IDL/Image Processing

Histogram Equalization 기법에 관하여

이상우_idl 2021. 1. 11. 17:14
728x90
반응형

오늘은 이미지 처리 기법들 중 하나인 히스토그램 균일화(Histogram Equalization) 기법을 IDL에서 적용하는 방법에 관하여 소개해보고자 합니다. 히스토그램 균일화라는 것은 이미지의 명암대비(Contrast)를 향상시키는 기법들 중 하나인데요. 사실 이미지의 명암대비를 향상시키는 방법들은 여러가지가 있으며, 흔히 자주 사용되는 Clipping이란 기법도 그 중 하나입니다. 그런데 히스토그램 균일화 기법은 또 그 나름대로의 장점이 있습니다. IDL에서는 HIST_EQUAL 함수를 사용하여 이러한 기법을 적용할 수 있습니다. 그러면 예제를 통하여 그 과정을 살펴보기로 하겠습니다. 예제 데이터는 IDL의 설치와 함께 딸려오는 convec.dat라는 바이너리 파일로부터 다음과 같이 읽어옵시다.

 

file = FILEPATH('convec.dat', SUBDIR=['examples', 'data'])

img = READ_BINARY(file, DATA_DIMS=[248, 248])

HELP, img

PRINT, MIN(img), MAX(img)

hist = HISTOGRAM(img)

 

이와 같이 img라는 배열로 읽어오게 되는데, HELP 및 PRINT로 출력된 내용을 통하여 이 배열이 0~255의 범위를 갖는 바이트형 값들로 구성되어 있으며 248x248의 구조를 갖는다는 것을 확인할 수 있습니다. 그리고 HISTOGRAM 함수를 사용하여 얻은 hist는 배열 img의 화소값들의 분포도를 표출하기 위한 것입니다. 일단 배열 img를 이미지의 형태로 표출하고 화소값 분포도를 그 옆에 나란히 표출해봅시다. 그 과정은 다음과 같습니다.

 

win = WINDOW(DIMENSIONS=[800, 400], /NO_TOOLBAR)

im = IMAGE(img, MARGIN=0.1, /CURRENT, LAYOUT=[2, 1, 1])

h = PLOT(hist, XRANGE=[0, 256], YRANGE=[0, 3000], $

  COLOR='crimson', THICK=2, /CURRENT, LAYOUT=[2, 1, 2])

 

그리고 표출된 그림은 다음과 같습니다.

 

 

여기서 왼쪽은 원본 데이터 배열인 img를 이미지 형태로 표출한 것이고, 오른쪽은 img의 화소값 분포를 나타낸 분포도입니다. 분포도를 잘 보면 이미지 상에서 검은색으로 보이는 배경 부분에 해당되는 화소들이 갯수가 많으며 그 화소값은 0입니다. 또한 흰색으로 보이는 가운데 부분에 해당되는 화소들 역시 갯수가 많으며 그 화소값은 255입니다. 갯수가 많다보니 분포도의 Y축 범위가 너무 넓어지기 때문에 일부러 Y축의 범위를 0~3000으로 좁혀놓았습니다. 어쨌든 화소값이 0 및 255인 경우는 논외로 하고 나머지 화소값들에 대한 분포를 보면 대략적으로 0~120 정도의 범위 내에 분포하는 것을 볼 수 있습니다. 그러면 여기서 이제 히스토그램 균일화(Histogram Equalization) 기법을 적용해봅시다. 그 과정은 다음과 같습니다.

 

img_he = HIST_EQUAL(img)

HELP, img_he

PRINT, MIN(img_he), MAX(img_he)

hist_he = HISTOGRAM(img_he)

 

이와 같이 원본 데이터인 img에 대하여 HIST_EQUAL 함수를 적용하면, 히스토그램 균일화 기법이 적용된 결과를 배열 img_he의 형태로 얻게 됩니다. 이 배열 역시 0~255의 범위를 갖는 바이트형 값들로 구성되며 그 구조는 248x248입니다. 원본 배열과 스펙은 동일합니다. 그리고 이 img_he에 대해서도 화소값 분포를 보기 위하여 HISTOGRAM 함수를 적용하였습니다. 그러면 이 결과들을 표출해봅시다. 그 과정은 다음과 같습니다.

 

win_he = WINDOW(DIMENSIONS=[800, 400], /NO_TOOLBAR)

im_he = IMAGE(img_he, MARGIN=0.1, /CURRENT, LAYOUT=[2, 1, 1])

h_he = PLOT(hist_he, XRANGE=[0, 256], YRANGE=[0, 3000], $

  COLOR='crimson', THICK=2, /CURRENT, LAYOUT=[2, 1, 2])

 

그리고 표출된 그림을 보면 다음과 같습니다.

 

 

우리가 앞서 원본 데이터인 img에 대하여 얻었던 결과와 비교해봅시다. 왼쪽의 이미지 형태의 표출 결과부터 보면, 히스토그램 균일화가 적용된 결과 이미지의 명암대비가 원본 이미지에 비해서는 전반적으로 높아진 것을 볼 수 있습니다. 이러한 현상은 화소값 범위 전체에 걸쳐 두드러지게 나타납니다. 오른쪽의 분포도 표출 결과를 비교해보면 좀 더 명확히 확인할 수 있는데요. 원본 이미지의 화소값 분포의 경우 앞서 언급했듯이 유의미한 화소값들의 범위가 대략 0~120 정도로 한정적이었던 반면, 결과 이미지의 화소값 분포에서는 거의 0~200 정도까지 확대되어 있습니다. 그리고 원본 데이터의 경우는 0~120의 범위 내에서도 빈도수가 들쑥날쑥한 편인데 비하여, 결과 데이터의 경우는 0~200의 더 넓어진 범위 내에서도 빈도수가 상대적으로 좀 더 고르게 퍼져있는 것을 볼 수 있습니다. 즉 히스토그램 균일화라는 것은 데이터의 값 분포가 가용 범위 전체에 걸쳐서 전반적으로 균일하게 퍼지도록 인위적으로 조정하는 작업이라고 보면 됩니다. 따라서 원본 데이터의 값 분포가 고르지 않고 편중이 심할 경우, 이러한 처리를 해주면 값 분포가 어느 정도 균일해지도록 조정하는 것이 가능합니다. 이러한 효과는 이미지 데이터의 경우에는 명암대비가 전반적으로 높아지는 모습으로 나타납니다.

 

히스토그램 균일화의 효과를 좀 더 극명하게 보기 위하여, 이번에는 화소값 분포의 편중이 좀 더 심한 예제 데이터를 사용해보도록 하겠습니다. 예제 파일은 제가 준비한 JPG 파일인데, 아래에 첨부하였으므로 받아서 사용하시면 됩니다.

 

my_photo1.jpg
0.15MB

 

그러면 먼저 이 JPG 파일을 다음과 같이 읽어옵시다.

 

file = 'my_photo1.jpg'

READ_JPEG, file, img, /GRAY

HELP, img

PRINT, MIN(img), MAX(img)

hist = HISTOGRAM(img)

 

이와 같이 img라는 배열로 읽어오게 되는데, HELP 및 PRINT로 출력된 내용을 통하여 이 배열이 0~255의 범위를 갖는 바이트형 값들로 구성되어 있으며 734x979의 구조를 갖는다는 것을 확인할 수 있습니다. 그리고 우리가 앞서 했던 것과 마찬가지로 가로로 길쭉한 그래픽창을 띄워서 좌측에는 배열 img를 이미지의 형태로 표출하고 우측에는 HISTOGRAM 함수를 사용하여 얻은 배열 img의 화소값들의 분포를 그림으로 표출해봅시다. 그 과정은 다음과 같습니다.

 

win = WINDOW(DIMENSIONS=[800, 400], /NO_TOOLBAR)

im = IMAGE(img, MARGIN=0.1, /CURRENT, LAYOUT=[2, 1, 1])

h = PLOT(hist, XRANGE=[0, 256], YRANGE=[0, 50000], $

  XTITLE='Pixel Value', YTITLE='Count', $

  COLOR='crimson', THICK=2, /CURRENT, LAYOUT=[2, 1, 2])

 

그리고 표출된 그림은 다음과 같습니다.

 

 

왼쪽은 원본 데이터 배열인 img를 이미지 형태로 표출한 것이고, 오른쪽은 img의 화소값 분포를 나타낸 분포도입니다. 이 이미지는 제가 직접 촬영한 것인데 일부러 역광 효과를 연출한 것입니다. 즉 전면에 보이는 책의 커버가 뒷배경의 전등빛으로 인하여 잘 안보이는 상태입니다. 우리가 밝은 대낮에 실외에서 햇빛을 등진 피사체를 촬영할 때 자주 겪게 되는 현상과 비슷합니다. 이렇게 역광으로 인하여 잘 안보이는 형체가 있을 때, 그 형체가 잘 보이도록 처리하는 방법은 사실 여러가지가 있습니다. 일단 가장 단순한 방법은 클리핑(Clipping)이라는 기법인데요. 화소값 범위를 극도로 제한하여 특정 범위 내의 화소값 범위만 강조하는 기법입니다. 역광으로 인하여 어둡게 보이는 책 커버 부분의 화소값들은 위의 화소값 분포도 상에서는 대략 0~50 정도의 범위 내에 속합니다. 따라서 다음과 같이 배열 img에 대하여 클리핑 기법을 적용함으로써(<50) 화소값이 50보다 작은 화소들에 대해서만 화소값을 그대로 살리고, 화소값이 50보다 큰 화소들에 대해서는 그 값을 50으로 퉁쳐버립시다.

 

img_cl = img<50

HELP, img_cl

PRINT, MIN(img_cl), MAX(img_cl)

hist_cl = HISTOGRAM(img_cl)

 

이러한 처리를 거쳐 얻은 결과 배열인 img_cl은 734x979라는 배열 구조 자체는 원본과 동일하지만 화소값 범위는 0~50으로 제한되어버립니다. 그리고 HISTOGRAM 함수를 적용하여 img_cl에 대한 화소값 분포 결과도 얻었습니다. 이제 다음과 같이 그래픽창을 띄워서 왼쪽에는 배열 img_cl의 이미지 형태 그리고 오른쪽에는 배열 img_cl의 화소값 분포도를 표출해봅시다.

 

win_cl = WINDOW(DIMENSIONS=[800, 400], /NO_TOOLBAR)

im_cl = IMAGE(BYTSCL(img_cl), MARGIN=0.1, /CURRENT, LAYOUT=[2, 1, 1])

h_cl = PLOT(hist_cl, XRANGE=[0, 256], YRANGE=[0, 50000], $

  XTITLE='Pixel Value', YTITLE='Count', $

  COLOR='crimson', THICK=2, /CURRENT, LAYOUT=[2, 1, 2])

 

여기서 한가지 유의할 부분은 IMAGE 함수로 배열 img_cl을 표출할 때 BYTSCL 함수를 사용한 것입니다. 이렇게 하면 원래의 0~50 범위인 img_cl을 표출할 때 바이트 스케일링(Byte Scaling)을 적용함으로써, 컬러테이블의 모든 색상들(0~255)을 사용되도록 해줍니다. 그러면 결국 이미지의 명암대비가 강조되는 효과를 얻게 됩니다. 표출된 모습은 다음 그림과 같습니다.

 

 

여기서 왼쪽의 이미지를 보면 앞서 처리 이전의 원본 이미지에서는 잘 보이지 않던 책 커버 부분의 디테일이 이제 잘 보이게 됩니다. 물론 클리핑 처리가 된 이미지이기 때문에 화소값의 범위가 0~50으로 제한되어버렸다는 것을 우측의 img<50에 대한 분포도를 통해서도 확인할 수 있습니다. 이와 같이 클리핑 및 바이트스케일링 기법을 사용하면 잘 보이지 않던 부분에 대하여 명암대비를 의도적으로 강조함으로써 그 디테일의 식별이 좀 더 쉬워지는 효과를 얻을 수 있습니다. 그런데 클리핑 기법에는 장점과 단점이 공존합니다. 특정한 화소값 범위에 속하는 형체들만 살려두고 나머지 화소값 범위에 속하는 형체들은 완전히 없애버린다는 것입니다. 위의 이미지를 보면, 어두워서 식별이 잘 안되었던 책 커버 부분이 잘 보이도록 하는 것은 성공했지만, 그 대신 그 주변부의 원래 좀 밝았던 부분의 디테일은 완전히 죽어버려서 형체를 식별할 수 없게 되었음을 볼 수 있습니다. 이것은 클리핑 기법 자체의 특성상 어쩔 수 없습니다. 이 기법에서는 어차피 특정한 화소값 범위만 살리고 나머지는 포기하기 때문입니다.

 

그런데 클리핑 기법 대신 히스토그램 균일화 기법을 사용하면 이러한 단점을 상당부분 극복할 수 있습니다. 즉 클리핑 기법을 적용했을 경우에는 명암대비가 강조되는 효과가 특정한 화소값 범위로만 한정되는 반면, 히스토그램 균일화 기법을 적용했을 경우에는 그 효과가 더 넓은 화소값 범위에 걸쳐서 고르게 적용됩니다. 다음과 같이 HIST_EQUAL 함수를 사용하면 됩니다.

 

img_he = HIST_EQUAL(img)

HELP, img_he

PRINT, MIN(img_he), MAX(img_he)

hist_he = HISTOGRAM(img_he)

 

이러한 처리를 거쳐 얻은 결과 배열인 img_he는 원본 데이터 배열과 동일한 734x979의 구조를 갖습니다. 그리고 HISTOGRAM 함수를 적용하여 img_he에 대한 화소값 분포 결과도 얻었습니다. 이제 다음과 같이 그래픽창을 띄워서 왼쪽에는 배열 img_he의 이미지 형태 그리고 오른쪽에는 배열 img_he의 화소값 분포도를 표출해봅시다.

 

win_he = WINDOW(DIMENSIONS=[800, 400], /NO_TOOLBAR)

im_he = IMAGE(img_he, MARGIN=0.1, /CURRENT, LAYOUT=[2, 1, 1])

h_he = PLOT(hist_he, XRANGE=[0, 256], YRANGE=[0, 50000], $

  XTITLE='Pixel Value', YTITLE='Count', $

  COLOR='crimson', THICK=2, /CURRENT, LAYOUT=[2, 1, 2])

 

표출된 모습은 다음 그림과 같습니다.

 

 

여기서 왼쪽의 이미지를 보면, 어두웠던 책 커버 부분의 명암대비가 강조되어 형체가 잘 식별됩니다. 여기까지는 앞서 클리핑 기법을 적용했을 경우와 거의 유사합니다. 그런데 책 커버 주변의 좀 밝았던 부분들도 명암대비가 강조되었을 뿐 아니라 잘 보이기까지 합니다. 클리핑 기법을 적용했을 경우에는 이 부분들은 형체가 사라졌던 반면, 지금은 특별히 형체가 유실된 부분은 거의 보이지 않습니다. 따라서 히스토그램 균일화 기법을 적용하면 특정한 화소값 범위에 대한 형체 정보를 잃지 않으면서도 전반적으로 명암대비가 높아지는 효과를 얻을 수 있게 됩니다.

 

오늘은 IDL의 HIST_EQUAL 함수를 이용하여 히스토그램 균일화 기법을 사용하는 방법을 소개해보았습니다. IDL에서는 HIST_EQUAL 함수 외에도 선택적 히스토그램 균일화(Adaptive Histogram Equalization) 기법에 해당되는 ADAPT_HIST_EQUAL 함수도 함께 지원됩니다. 이와 관련된 자세한 내용은 IDL 도움말에서 관련 섹션을 찾아서 보시면 됩니다.

반응형