IDL/Image Processing

이미지의 마스크(Mask) 영역에 대한 색상 처리

이상우_idl 2017. 3. 9. 09:30
728x90
반응형

오늘은 2차원 이미지 데이터에 대하여 화소값에 대한 일정 기준을 적용한 마스크(Mask) 영역을 찾아내고 이 영역을 원하는색상으로 처리하는 방법에 대하여 알아보고자 합니다. 사실 이 내용은 제가 예전에 올렸던 어떤 게시물의 내용과 연관성이 있습니다. 예제 이미지 자료도 이 당시의 것을 그대로 사용하고 있는데요. 링크는 바로 앞 문장의 "어떤 게시물"이라는 문구에 바로 걸어놓았으니 한번 참조하셔도 좋을 것 같습니다. 예제 이미지는 태양의 표면을 연속선(Continuum)으로 촬영한 데이터로서 태양 흑점이 바로 눈에 잘 띄는 특성을 갖습니다. 이미지 파일은 제가 이전 게시물에도 첨부했었지만 여기서도 다시 첨부해 놓습니다.


sunspots.png


이 파일을 읽고 원본 그대로 표출하는 과정은 다음과 같습니다. 이 부분 이전 게시물에서 했던 방식과 다소 유사합니다. 먼저 이미지 파일을 읽고, 이미지의 크기를 확인하고, 그 크기에 맞는 그래픽창을 띄우고, 이미지를 표출하는 순서입니다. 그 결과는 다음 그림과 같습니다.


file = 'sunspots.png'

img = READ_PNG(file)

HELP, img

PRINT, MIN(img), MAX(img)

sz = SIZE(img, /DIM)

win1 = WINDOW(DIMENSIONS=sz, /NO_TOOLBAR)

im1 = IMAGE(img, MARGIN=0, /CURRENT)



그리고 이 이미지 상에서 태양의 흑점과 그 주변을 잘 구분해주는 화소값을 알아본 결과 166이라는 값이 가장 적당하다는 언급도 이전 게시물에서 했던 적이 있습니다. 따라서 이 값을 기준으로 마스킹(Masking)을 수행하여 화소값이 166보다 작은 화소들을 탐지하고 그 결과를 별도의 그래픽창에 표출하는 과정을 다음과 같이 진행해 봅시다. 그 결과는 다음 그림과 같습니다.


mask = img LT 166

HELP, mask

PRINT, MIN(mask), MAX(mask)

win2 = WINDOW(DIMENSIONS=sz, /NO_TOOLBAR)

im2 = IMAGE(FIX(mask), MARGIN=0, /CURRENT)



여기서는 마스킹 기법을 수행하는데 있어서 화소값이 166보다 작은 화소들을 골라냅니다. 이렇게 얻어진 mask라는 배열은 오직 0과 1 두 종류의 값을 갖는데, 이 안에서 1인 화소들이 바로 마스킹에서 명시한 조건을 만족하는 화소들에 해당되고 0인 화소들은 그렇지 못한 화소들에 해당됩니다. 이 mask라는 배열은 출력된 내용에서 확인할 수 있듯이 바이트(Byte)형의 0과 1의 값들로 구성된 배열입니다. 따라서 IMAGE 함수로 표출할 때 바이트형 그대로 사용할 경우에는 컬러테이블에서 0과 1의 색상들만 사용하므로 검정색으로만 보이게 되는 문제가 발생합니다. 따라서 0~1 범위가 컬러테이블상의 0~255 색상으로 제대로 반영되도록 하기 위하여 FIX 함수를 사용하여 일시적으로 정수형으로 변환한 상태로 IMAGE 함수에 투입하였습니다. 따라서 위의 결과 이미지에서 흰색인 부분들은 mask 배열 내에서 값이 1인 부분으로 실제 대응된 색상값은 255입니다. 그리고 검은색인 부분들은 mask 배열 내에서 값이 0인 부분으로 실제 대응된 색상값은 0입니다. 기본 컬러테이블인 0번 컬러테이블상에서 0은 완전 검은색이고 255는 완전 흰색이라는 점을 참고하시면 됩니다.


여기까지는 원본 이미지를 표출하고 마스킹 결과도 표출하는 일련의 과정들입니다. 그러면 마스킹된 영역을 원본 이미지상에 중첩하여 나타내고 싶다면 어떻게 해야 할까요? 그리고 중첩을 하면서 이 마스킹 영역을 내가 원하는 색상으로 나타내려면 어떤 방법을 사용해야 할까요? 사실 이런 표출 방식에 대하여 저도 종종 질문을 받습니다. 제 기억으로는 질문하셨던 분들에게 나름의 답을 드렸던 기억은 나는데 뭔가 속시원한 해결책을 제대로 알려드리진 못했던 것으로 기억을 합니다. CONTOUR 함수를 사용하여 원본 이미지 상에 등위선을 중첩하여 표출하는 것도 하나의 방법일 수 있습니다. 그러나 오늘은 원본 이미지 상에 마스크 이미지를 중첩하면서도 원본과 마스크 영역이 함께 잘 표출될 수 있는 방법으로 소개해보고자 합니다. 이를 위해서는 약간의 중간 처리 과정이 필요한데요. 일단 그 내용을 먼저 제시하면 다음과 같습니다.


maskf = FLOAT(mask)

ww = WHERE(maskf EQ 0, count)

maskf[ww] = !values.f_nan

HELP, mask


이 과정에서 가장 핵심은, 마스크 이미지 내에서 화소값이 1인 부분들에 대해서는 화소값을 그대로 살려두고 0인 부분들은 NaN값으로 대체하는 부분입니다. 다만 이 과정이 제대로 효과를 발휘하려면 화소값들이 실수형이어야 한다는 단서가 필요합니다. 따라서 바이트형 값들로만 구성되어 있는 배열 mask를 FLOAT 함수로 실수형으로 변환하고 그 결과를 maskf라는 별도의 배열로 만들었습니다. 그리고 이 maskf 배열 내에서 값이 0인 부분들을 추출하여 NaN값으로 대체하는 과정이라고 보면 됩니다. 이러한 처리 과정을 거친 maskf를 원본 이미지상에 중첩하여 표출하면 됩니다. 그 과정은 다음과 같습니다.


win3 = WINDOW(DIMENSIONS=sz, /NO_TOOLBAR)

im3A = IMAGE(img, MARGIN=0, /CURRENT)

ct = COLORTABLE(['red'])

im3B = IMAGE(maskf, RGB_TABLE=ct, MARGIN=0, /CURRENT)


여기서는 그래픽창을 새로 띄운 다음 먼저 원본 이미지를 표출합니다(im3A). 그리고 앞서 얻은 maskf를 원본 이미지 바로 위에 중첩하여 표출하는 방식입니다(im3B). 그런데 여기서 하나 특이한 부분은 컬러테이블의 적용 부분인데, COLORTABLE이라는 함수를 사용하여 나름의 컬러테이블을 직접 만들어 사용한 것이 보입니다. 여기서 사용된 COLORTABLE 함수에 대해서는 역시 제가 예전에 소개했던게시물이 있습니다. 여기서는 약간 독특한 방식으로 사용을 했는데, COLORTABLE 함수에 투입된 색상이 red 단 하나 뿐입니다. 이렇게얻어진 컬러테이블은 0~255의 색상값들 모두가 오직 빨간색에 해당되는, 즉 단 하나의 색상만을 갖는 형태가 됩니다. 그런데 굳이 이렇게 단순한 컬러테이블을 사용하게 되면, 이 컬러테이블이 적용될 데이터인 maskf 배열 내에 존재하는 값들(1 아니면 NaN) 중 1은 빨간색에 대응되고 NaN값은 아예 색상 표현이 되지 않습니다. 즉 NaN값인 화소들은 아예 투명 처리된다고 보면 됩니다.


이와 같이 2차원 배열을 이미지로 표출할 때 화소값이 NaN인 부분들은 실제로는 투명하게 마치 구멍이 난 것처럼 표출됩니다. 이와 같은 내용은 제가 예전에 올렸던 또 다른 게시물에서도 확인해보실 수 있습니다. 따라서 이미 표출된 원본 이미지 위에 중첩되는 이미지는 NaN값인 화소들은 사실상 뚫려 있는 것과 같은 효과를 보입니다. 그리고 유효한 값인 1에 해당되는 화소들은 모두 red 색상으로 처리되는 효과를 볼 수 있습니다. 따라서 다음 그림과 같은 결과를 얻을 수 있습니다.



그리고 맨 마지막 줄에서 IMAGE 함수에 다음과 같이 TRANSPARENCY 속성을 추가로 사용하면 투명도 효과를 줄 수 있습니다. 이렇게 하면 다음 그림과 같이 마스크로 걸러진 부분의 원본 이미지 상의 모습도 살짝 비쳐 보이도록 처리하는 것이 가능합니다.


im3B = IMAGE(maskf, RGB_TABLE=ct, TRANSPARENCY=50, MARGIN=0, /CURRENT)



오늘 소개한 방법을 잘 활용하면 이미지 상에서 내가 원하는 조건을 만족하는 화소들만을 다양한 방식으로 표출하는 것이 가능합니다. 잘 알아두시면 나름 여러모로 도움이 될 것 같습니다.

sunspots.png
0.13MB
반응형