IDL/Image Processing

이미지를 원하는 형태로 추출해내는 방법에 관하여

이상우_idl 2017. 5. 3. 23:56
728x90
반응형

오늘은 2차원 이미지를 내가 원하는 특정한 형태로 추출해내는 방법이라는 주제를 다뤄보고자 합니다. 여기서 말하는 "특정한 형태"라는 것은 내가 원하는 특정한 기하학적 형태를 갖는 다각형(Polygon)을 의미합니다. 우리가 이미지라고 하면 항상 사각형의 프레임안에 가로/세로 격자들로 구성된 형태를 생각하는 것이 당연한데, 사각형이 아닌 임의의 형태를 띄는 이미지 데이터를 추출해내는 방법을 의미합니다. 아무래도 말로는 표현하기가 조금 애매한 부분이 있어서 그림으로 먼저 보여드리는 것이 좋을 것 같습니다. 거두절미하고 일단 아래의 예제코드의 내용을 먼저 봅시다.


img = HANNING(600, 600)

sz = SIZE(img, /DIM)

win1 = WINDOW(DIMENSIONS=sz, /NO_TOOLBAR)

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

vx = [260, 460, 260, 60]

vy = [100, 300, 500, 300]

plg = POLYGON(vx, vy, COLOR='black', THICK=2, FILL_BACKGROUND=0, /DATA)


여기서는 제가 항상 애용하는 HANNING 함수를 사용하여 600x600의 크기를 갖고 값의 범위가 0~1인 img라는 배열을 먼저 만든 다음, WINDOW 및 IMAGE 함수를 사용하여 표출하였습니다. IMAGE 함수에서는 컬러테이블 34번을 사용해서 좀 더 컬러풀한 표출이 되도록 하였습니다. 첫 네 줄이 이러한 작업에 해당되는데, 사실 여기까지는 NG 체계에서 이미지를 표출하는 기본적인 방법을 따른 것으로 별로 새로울 것은 없는 내용이긴 합니다. 하지만 그 뒤의 세 줄에서는 POLYGON 함수를 사용하여 이미지 상에 마름모 형태의 다각형(Polygon)을 덧그렸습니다. 여기서 사용한 vx, vy는 이 마름모형 다각형을 구성하는 4개의 꼭지점들의 X 및 Y 좌표값들로 구성된 배열이며, 이 좌표값들은 픽셀(Pixel) 단위입니다. 위 예제코드를 실행한 결과는 다음 그림과 같습니다.



그래서 오늘 제가 해보고자 하는 것은 위의 그림에서 마름모 다각형 안쪽에 해당되는 픽셀들만 살리고 그 외곽의 픽셀들은 제거하는 것입니다. 얻고자 하는 최종 결과를 미리 한번 보여드리면 다음 그림과 같습니다.



사실 이런 작업이 필요할 경우가 종종 있습니다만, 이러한 결과를 얻기 위한 작업 과정은 아주 간단하지는 않은 것이 사실입니다. 이러한기법을 굳이 한마디로 표현하자면 ROI Masking 정도의 용어가 적당하지 않을까 싶습니다. 즉 이미지상에서 내가 지정하는 ROI(관심영역 : Region of Interest) 안쪽 부분만 추출해내는 기법이라 할 수 있습니다. 따라서 아무래도 ROI 및 Masking의 개념에 대한 이해가 어느 정도는 필요한 것이 사실이지만, 가급적 너무 복잡하지 않게 그 과정을 설명해보겠습니다. 먼저 내가 원하는 다각형의 형태로 ROI를 정의하기 위하여 IDLanROI라는 클래스의 객체를 사용해야 합니다. "객체"라고 너무 겁먹진 마시고 그냥 다음과 같은 방식으로 정의하시면 됩니다.


oROI = OBJ_NEW('IDLanROI', vx, vy)

mask = oROI -> ComputeMask(DIMENSIONS=sz)

IMAGE_STATISTICS, img, MASK=mask, COUNT=count

PRINT, 'area of mask =  ', count,' pixels'


이 예제코드에서는 앞서 POLYGON 함수를 사용하여 마름모 다각형을 그릴 때 사용했던 vx, vy 배열을 그대로 사용하여 ROI를 oROI라는 이름의 객체로 먼저 정의합니다. 그 다음에는 이 객체에 대하여 ComputeMask라는 메서드(명령)을 사용하였는데, 이 때 인자로서 투입한 sz는 앞서 img에 대하여 SIZE 함수를 적용하여 얻은 정보로서, 배열 img의 가로 및 세로 방향 픽셀 크기값들(600, 600)에 해당됩니다. 여기서 결과로 얻게 되는 mask라는 배열은 크기는 원본 이미지 배열인 img의 크기와 같습니다. 다만 내부를 구성하는 값들은 오로지 0 아니면 1만 존재합니다. 그렇다면 mask 내에서 어느 부분이 1이고 어느 부분이 0일까요? 여기서 잠시 그 모습을 보기 위하여 다음 내용을 실행하여 mask의 모습을 표출해보면 그 모습은 다음 그림과 같습니다.


win2 = WINDOW(DIMENSIONS=sz, /NO_TOOLBAR)

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



이 그림에서 흰 부분이 값이 1인 픽셀들이고 검은 부분은 값이 0인 픽셀들입니다. 따라서 이와 같은 방식으로 이미지 내에서 다각형의 내부 및 외부에 해당되는 픽셀들을 구분할 수 있습니다. 이러한 기법을 이미지 처리에서는 마스킹(Masking)이라고 하는데, 오늘 우리가 하고자 하는 작업에 있어서 사실상 이 부분이 가장 핵심적인 부분이라 할 수 있겠습니다.


여기서 잠시 이전의 예제코드 내용으로 돌아가보면 IMAGE_STATISTICS라는 명령을 사용한 부분이 나오는데, 이 내용은 img 배열 내에서 mask의 값이 1인 픽셀들의 갯수를 세어서 count 변수에 담아서 전달해달라는 의미입니다. 바로 이어지는 PRINT문의 출력 결과에 의하면 다음과 같이 총 80401개의 픽셀들이 여기에 해당되는 것으로 나타납니다.


area of mask =         80401 pixels


자 이제는 mask를 활용하여 좀 더 처리를 진행해 봅시다. 다음과 같이 원본 이미지 배열인 img에 mask를 곱해서 img_masked라는 배열로 정의합니다. 이렇게 하여 얻어진 img_masked 배열의 값들을 보면, img의 원본 픽셀값과 mask의 1이 곱해지는 부분은 원본 img의픽셀값이 그대로 살아남게 되지만, mask의 0이 곱해진 부분은 그냥 0이 되어 버립니다. 그러면 원본 이미지에서 마름모 다각형 안쪽의 픽셀들만 살아남고 그 외곽은 0이 되어 버리는 효과를 얻게 됩니다.


mask = mask GT 0

img_masked = img*mask


그러면 이 img_masked를 한번 표출해 보기 위하여 다음 내용을 실행해 봅시다. 그 결과는 다음 그림과 같습니다.


win3 = WINDOW(DIMENSIONS=sz, /NO_TOOLBAR)

im3 = IMAGE(img_masked, RGB_TABLE=34, MIN_VALUE=0., MAX_VALUE=1., $

  MARGIN=0, /CURRENT)



어떻습니까? 사실 이 정도만 되어도 제법 나쁘지 않은 결과라는 생각은 듭니다. 하지만 조금 마음에 걸리는 부분이 있는데요. 마름모 외곽 부분의 색상을 보면 보라색으로 처리가 되어 있습니다. 그 이유는 img_masked에서 마름모 외곽 부분은 픽셀값이 0인데, 지금 사용중인 34번 컬러테이블에서는 이 값이 보라색에 대응되기 때문에 이러한 색상이 반영된 것입니다. 그런데 엄밀히 생각해보면, 외곽 부분의 픽셀값이 0이 되는 것보다는 그냥 값 자체가 존재하지 않도록 하는 것이 더 필요할 수도 있습니다. 하지만 그렇다고 외곽 부분의 픽셀들을 아예 제거하는 것은 불가능합니다. 따라서 이 외곽 픽셀들에 대해서는 그 값이 NaN(Not A Number)이 되도록 처리하는 것이 어떨까 합니다. 이를 위하여 다음과 같은 내용을 먼저 실행한 후 위 예제코드의 표출 과정(win3, im3)을 다시 실행하면 그 결과는 다음 그림과 같습니다.


ww = WHERE(mask EQ 0)

img_masked[ww] = !values.f_nan



이 그림에서 흰 부분은 픽셀값이 NaN이기 때문에 색상이 전혀 반영되지 않은 것입니다. 따라서 그래픽창의 배경색인 흰색이 그냥 그래도 드러난 것이라고 보면 됩니다. 하여간 이 정도면 우리가 원하는 결과에 가장 근접한 것이 아닐까 생각이 듭니다. 이와 같은 방법을 사용한다면, 우리가 원하는 어떤 형태의 다각형이 주어진다고 해도 그 내부의 픽셀들만 살려서 표출하는 것이 얼마든지 가능합니다. 여기서 하나만 좀 더 짚어보고 오늘 얘기를 마무리하고자 합니다. 앞서 최종 결과 이미지를 표출하기 위하여 IMAGE 함수가 사용된 부분을 다시 한번 보겠습니다.


im3 = IMAGE(img_masked, RGB_TABLE=34, MIN_VALUE=0., MAX_VALUE=1., $

  MARGIN=0, /CURRENT)


사실 여기서 MIN_VALUE, MAX_VALUE 속성들이 사용된 것을 주목할 필요가 있습니다. 굳이 이러한 속성을 사용한 이유는, 원본 이미지의 화소값 범위와 추출된 이미지의 화소값 범위가 서로 다를 수 있기 때문입니다. 실제로 img의 경우 0~1의 범위를 갖지만, 마름모 안쪽만 추출한 img_masked의 경우는 NaN값을 제외한 유효값들의 범위가 대략 0.1~1로 나옵니다. 이것은 다음과 같이 MIN, MAX 함수를 사용해보면 금방 알 수 있습니다.


PRINT, MIN(img_masked, /NAN), MAX(img_masked, /NAN)


그런데 IMAGE 함수는 그 특성상 화소값들이 실수형일 경우에는 색상 표출에 있어서 컬러테이블의 모든 색상들(0~255)을 사용하게 됩니다. 따라서 img를 표출할 때에는 0~1 범위의 값들을 0~255의 색상값들로 대응시켜 표출하지만, img_masked의 경우에는 0.1~1 범위의 값들을 0~255의 색상값들로 대응시키게 됩니다. 그러면 값에 대응되는 색상이 서로 달라지게 되고 이에 따라 눈으로 보이는 모습도 당연히 달라지게 됩니다. 그러나 IMAGE 함수에서 MIN_VALUE, MAX_VALUE 속성을 사용하면 표출에 있어서 어떤 범위를 0~255의 색상값들로 대응시킬 것인가를 직접 결정할 수 있습니다. 이를 위하여 MIN_VALUE를 0으로 그리고 MAX_VALUE를 1로 설정한 것입니다. 만약 이러한 추가 설정을 하지 않고 img_masked를 표출하면 어떻게 될까요? 다음과 같이 별도의 그래픽창에 이러한 결과를 표출한 다음 앞선 그림과 비교해보면 됩니다.


win4 = WINDOW(DIMENSIONS=sz, /NO_TOOLBAR)

im4 = IMAGE(img_masked, RGB_TABLE=34, MARGIN=0, /CURRENT)



이 그림과 앞선 그림을 잘 비교해보시면 그 차이가 바로 눈에 띌 것이라 생각합니다. 사실 이런 부분을 고려하는 것이 다소 귀찮게 여겨질 수도 있겠지만, IMAGE 함수의 특성이기도 하고 데이터를 좀 더 신중하게 표출하기 위한 고민의 산물이다 정도로 생각하시면 될 것 같습니다.


* 참고로 만약 마름모 다각형 외곽 부분 픽셀값들을 NaN으로 대체하지 않고 그냥 0으로 둘 경우에는 방금처럼 복잡한 처리를 신경쓰지 않아도 되긴 합니다. 그 경우에는 img 및 img_masked 모두 픽셀값 범위가 0~1로 동일하기 때문입니다.

반응형