오늘은 IMAGE_THRESHOLD라는 함수를 소개해보려고 합니다. 이 함수는 IDL 8.2.2 버전에서 새로 도입된 것인데, 어떤 이미지내에서 화소값들의 특성을 서로 양분하는 하나의 경계화소값을 찾아주는 역할을 합니다. 즉, 이미지의 전체적인 특성을 둘로 나누는 경계값을 찾는다는 의미인데요. 말로는 금방 다가오지 않을 수도 있는데, 오늘 예제 데이터로 사용할 이미지를 보면서 생각해 봅시다.예제 이미지는 다음과 같습니다. 참고로 이 이미지는 태양의 표면을 촬영한 위성 데이터로서 흑점 및 그 주변의 모습을 보여주고 있습니다.
이 이미지의 특성을 양분하는 경계값라고 한다면 대략적으로 보면, 흑점과 그 주변을 구분짓는 경계값에 해당된다고 보면 될 것 같습니다. 이러한 경계값을 찾는 작업은 물론 화소값들의 분포를 직접 눈으로 확인하거나 나름의 기법을 동원하여 계산하는 방법도 있겠지만, 지금 소개하려는 IMAGE_THRESHOLD 함수를 사용하면 꽤 간단하게 찾을 수도 있습니다. 먼저 예제로 사용될 이미지 파일을 다음과 같이 읽고 기본적인 정보들부터 확인해봅시다.
ifile = 'test_threshold.png'
img = READ_IMAGE(ifile)
HELP, img
PRINT, MIN(img), MAX(img)
여기서 출력된 결과를 보면 600X400의 크기를 갖고 바이트형 값들로 구성되어 있으며 최소값은 0이고 최대값은 239라는 것을 확인할 수 있습니다. 그렇다면 위에서 언급한 것과 같은 경계값은 당연히 0과 239 사이에 있는 어떤 값이 된다는 점은 당연하겠지요. 그 값을 찾는 것은 다음과 같이 IMAGE_THRESHOLD 함수를 사용하면 정말 너무나 간단하게 처리가 됩니다.
result = IMAGE_THRESHOLD(img, THRESHOLD=tf)
PRINT, tf
여기서는 이미지 배열인 img가 입력인자로서 사용되었습니다. 그리고 THRESHOLD라는 키워드가 보이는데, 이 키워드에 부여된 변수인 tf를 통해서 그 경계값을 받아올 수 있게 됩니다. 즉, THRESHOLD 키워드는 결과값을 돌려주는 역할을 한다는 점을 유의해야 합니다. 이 tf의 값을 출력해보면 우리가 궁금해하는 그 경계값을 확인할 수 있습니다. 실제로 출력을 해본 바로는 166.0이라는 값이 나왔습니다. 즉, 예제 이미지의 화소값들은 0부터 239의 범위를 갖는데, 그 특성을 양분할 수 있는 경계값이 166이라는 얘기입니다. 그리고 이 함수가 직접 돌려주는 result는 일종의 마스크(Mask) 이미지입니다. 즉, 경계값인 tf보다 큰 부분과 작은 부분을 1과 0으로 나타낸 binary image가 되므로, 이 result를 표출하면 경계값을 중심으로 양분되는 부분들이 어디인가를 가시적으로 확인하는 것이 가능합니다. 그러면 다음과 같이 그래픽 창 하나를 띄워서 원본 이미지 및 마스크 이미지를 함께 표출해봅시다. 그 결과는 다음 그림과 같습니다.
sz = SIZE(img, /DIM)
win1 = WINDOW(DIMENSIONS=[sz[0], sz[1]*2])
im1 = IMAGE(img, MARGIN=0, /CURRENT, LAYOUT=[1, 2, 1])
im2 = IMAGE(BYTSCL(result), MARGIN=0, /CURRENT, LAYOUT=[1, 2, 2])
여기서 하단의 그림을 보면 166.0이란 값으로 양분된 두 종류의 영역을 확인할 수 있습니다. 흑점이라고 할 만한 영역과 그렇지 않은영역과의 구분이 그럭저럭 잘 된 것으로 보입니다. 물론 이 판단은 보는 시각에 따라 다를 수 있습니다. 어쨌든 이렇게 얻은 경계값이 과연 전체 화소값 분포상에서 어느 위치에 해당되는가를 좀 더 확인해보려면 다음과 같이 화소값 분포도를 그려보는 것도 좋습니다. 그리고 추가적으로 이 경계값의 위치도 선으로 나타낼 수 있다면 더욱 좋겠지요. 다음과 같은 과정을 통하여 이러한 작업을 수행할 수있는데, 그 결과는 다음 그림과 같습니다.
win2 = WINDOW(DIMENSIONS=sz)
hist = HISTOGRAM(img, MIN=0, MAX=240)
hpl = PLOT(hist, COLOR='blue', THICK=2, /CURRENT, $
TITLE='Histogram of Pixel Values')
ln = POLYLINE([tf, tf], hpl.YRANGE, COLOR='crimson', /DATA)
이 그림을 보면 우리가 찾은 경계값이 전체 화소값 분포도상에서 어느 정도 위치에 해당되는가를 확인할 수 있습니다. 전체 분포도를 보면, 화소값들이 완벽하게 bi-modal하지는 않지만 어느 정도는 양분이 가능한 분포를 하고 있기는 합니다. 사실 이미지상의 화소값 분포가 너무 균일한 경우, 즉 모든 화소값에 대하여 빈도수가 엇비슷한 이미지에 대해서는 이 IMAGE_THRESHOLD 함수가 제 역할을 하기 힘듭니다. 이러한 특성은 감안이 되어야 할 것입니다.
그렇다면 IMAGE_THRESHOLD 함수가 경계값을 찾는 구체적인 알고리즘은 무엇일까요? IDL 도움말에서 이 함수에 관한 내용을찾아보면 그 설명들이 자세히 나와 있기 때문에 여기서 자세히 언급하지는 않겠습니다. 기본적으로는 Isodata thresholding이라는 알고리즘이 사용됩니다. 그 외에 키워드 설정을 통하여 다른 알고리즘이 사용되도록 설정할 수도 있는데, 가용한 알고리즘들의 목록은 다음과 같습니다.
Isodata Thresholding : /ISODATA 키워드 (디폴트)
Maximum Entropy Thresholding : /MAXENTROPY 키워드
Mean Thresholding : /MEAN 키워드
Minimum Error Thresholding : /MINERROR 키워드
Moments Thresholding : /MOMENTS 키워드
Otsu Thresholding : /OTSU 키워드
그래서 어떤 알고리즘을 사용하느냐에 따라 결과가 달라질 수도 있습니다. 참고로 지금의 예제 이미지에 대하여 다음과 같이 기본 알고리즘이 아닌 Maximum Entropy Thresholding 알고리즘을 사용했을 경우 결과가 다음 그림과 같이 나옵니다. 여기서는 경계값이 128.0으로 얻어졌는데, 앞선 경우에서 얻었던 166.0보다는 상당히 낮습니다. 따라서 앞선 경우와는 달리 흑점의 코어한 검은 부분만을 찾아내는 역할을 한 것으로 볼 수 있습니다.
result = IMAGE_THRESHOLD(img, THRESHOLD=tf, /MAXENTROPY)
PRINT, tf
이미지상에서 그 특성을 양분하는 화소값을 찾는 작업은 물론 다른 방법으로도 얼마든지 가능합니다. 다만, 데이터의 특성에 따라서는 오늘 소개한 IMAGE_THRESHOLD와 같은 함수를 사용해도 꽤 간단명료하게 작업이 가능하다는 점을 참조해두면 좋을 것 같습니다. 특히 여러 장의 이미지들에 대하여 대량으로 작업을 해야 할 경우 꽤 유용하지 않을까 생각이 됩니다. 오늘 예제로 사용된 이미지 파일은 아래에 첨부합니다.
'IDL > Image Processing' 카테고리의 다른 글
영상 처리에서 ROI의 활용법 [4] (0) | 2016.08.12 |
---|---|
COLORTABLE 함수를 이용한 컬러테이블의 생성 [1] (0) | 2016.04.29 |
2차원 이미지 자료에서 배열 인덱스의 해석에 관한 유의사항 (0) | 2015.12.09 |
iPhone Wallpapers powered by IDL (0) | 2015.10.30 |
POLAR_SURFACE 함수의 활용 (0) | 2015.08.06 |