서론
이미지는 때때로 대비(contrast)가 낮다.
위와같은 이미지의 경우 픽셀 값의 빈도수를 히스토그램으로 나타내면 다음과 같은데
0~50과 100~200 사이의 값들이 빈도가 많다는 것을 알 수 있다.
이와같은 이미지의 대비를 높이기 위해서 histogram stretching(특정 구간을 1차 함수를 통해 늘리는 것)을 통해 개선할 수 있다.
하지만 stretching 방법은 특정 구간을 매번 잡아줘야하는 귀찮음이 크다.
0~50을 0~200으로 늘려준 후, 다시 늘려야하는 구간이 보일 수밖에 없으므로 또 다시 늘려주고.. 이걸 알고리즘으로 만들어볼 수도 있겠지만 상당히 귀찮은 작업이 아닐 수 없다.
이제 이를 한번에 해결해줄 equalization 방법을 소개하고자 한다.
Histogram Equalization
histogram equalization을 사용하면 좁은 구간에 빈도수가 몰려있는 것을 최대한 넓은 구간으로 퍼뜨릴 수 있다.
그렇기 때문에 균등화(Equalization)라는 용어를 사용한다.
어떻게하면 만들 수 있는걸까?
유도
맨 왼쪽에 있는 히스토그램은 원본 이미지가 가진 픽셀값들의 빈도수를 나타낸 것이다.
잘 보면 중간에 빈도수가 몰려있는 것을 볼 수 있다.
위 이미지를 보면 $s = T(r)$을 볼 수 있는데, 이 T라는 함수를 통해 뭔가 과정을 거쳐서 맨 오른쪽 히스토그램과 같이 빈도수를 모든 픽셀값에 대해 균등하게 만들고자한다.
이 뭔 과정이라는게 이해하기가 상당히 까다로우니 졸지말고 화이팅.
우리가 가진 정보는 이미 대비가 처참한 이미지밖에 없다.
이 원본 이미지로 픽셀값이라는 입력값에 대해 빈도수가 나오는 함수를 만들 수 있다. 이걸 $f_R(r)$이라고 하자.
예를들어 $f_R(255) = 10$ 이런 식이다. 255라는 밝기를 가지는 픽셀이 10개있다는 뜻이다.
이제 균등화를 마친 이미지도 마찬가지로 함수를 만들 수 있는데, 이건 $f_S(s)$ 라고 하자.
이때 입력으로 주어지는 것은 원본의 픽셀값(0~255)이 아닌 T라는 함수를 통과한 새로운 픽셀 값 $s = T(r)$ 들이다.
뭔소리냐면, 원본에서의 픽셀값 0을 넣었을때 0이 아닌 10이나 219라는 이상한 값이 나올 수도 있다는 것이다.
여기서 중요한것은 픽셀값을 입력으로 넣고 픽셀값을 얻는다는 점이다.
즉, 새롭게 만들어지는 이미지는 0이라는 픽셀값들이 10이나 219로 바뀐다는 말이다.
더 자세히 말해보자.
위 이미지를 보면 이미지가 전체적으로 어두운 밝기값을 가지고 있다.
이때 0이라는 밝기값이 10이나 219으로 바뀐다는 것은 그림판에서 특정 색(0을)을 가지고 있는 영역을 다른 색(10 또는 219로)으로 교체한다는 것으로 이해할 수 있다.
따라서 위와같이 균등화 시킬 수 있다.
누구는 이게 어떻게 균등화야!!!!! 라고 화낼 수 있는데, 어떤 과정을 거쳐서 만들게 되는지를 바라보며 너그러운 마음으로 이해해보자.
과정
시작하기 전에 입력값이 0~255가 아닌 0~1로 정규화(normalization)이 되어있는데, 이건 어려운게 아니라 그냥 가진 픽셀값들을 255로 나눈거다.
우리는 오른쪽 함수와 같이 픽셀값의 빈도수를 평평하게 만들고싶다.
이에 대한 아이디어는 다음과 같다.
$f_R(r) \cdot dr = f_S(s) \cdot ds$
1. 변화된 이미지를 나타내는 함수($f_S(s)$)에서 ds만큼 조금 움직일 때의 넓이와
2. 원본 이미지를 나타내는 함수($f_R(r)$)에서 dr만큼 조금 움직일 때의 넓이를
일치시키겠다는 아이디어다.
이렇게 하면 어떤 결과가 나올까?
x가 0인 지점부터 dr을 점점 늘리면(0~dr까지의 구간, 즉 사각형의 너비), 그에 대한 높이인 $f_R(dr)$은 높이가 들쭉 날쭉 하면서 넓어진다.
그리고 그 넓이와 0~ds까지와 $f_S(ds)$에 대한 넓이가 같아야한다.
즉, $s = T(r)$은 특정 픽셀 값 r에 대해 적절히 높이(특정 픽셀값의 빈도수)를 조절하게 된다.
따라서 높이가 균등하도록 하는 식인 것임을 알 수 있다.
$f_s(s) = 1$ 이고, $s = T(r)$ 이라는 것은 위에서 정한 것들이고, 이를 통해 식을 변형하면 다음과 같다.
$f_R(r) \cdot dr = f_S(s) \cdot ds$
$f_R(r) \cdot dr = ds$
$f_R(r) = \frac{ds}{dr}$
$f_R(r) = \frac{d T(r)}{dr}$
$f_R(r) = T'(r)$
위 과정을 보며 눈여겨 보아야할 것은 $f_R(r) = \frac{ds}{dr}$ 이다.
ds에 대한 dr의 변화량이 $f_R(r)$이므로 위에서 넓이에 대해 접근했음이 맞았다!
마무리
$f_R(r) = T'(r)$에 대해 우리가 알고싶은것은 함수 T이다.
따라서 $f_R(r)$을 적분해야하는데, 컴퓨터에서 다루는 이미지는 절대 연속적일 수 없음을 여러 포스트에서 언급했다.
연속적인 함수에 대한 적분은 이산적인 함수에 대해서는 시그마 연산과 같다.
따라서 $T(r) = \sum_{t=0}^{r}f_R(t)$ 이다.
T를 구했다면, 만세를 불러야할까?
우리가 구한건 단지 특정 픽셀값(밝기값)이 균등하게 만들 이미지에서 어떤 픽셀로 변하는지를 구한것이다.
이미지를 만들어낸게 아니다!
import numpy as np
equalized_image = np.zeros_like(original_image)
for pixel_value, equlized_value in enumerate(equalized_pixels):
equalized_image[original_image == pixel_value] = equlized_value
다행히 파이썬에서 넘파이를 사용하면 몇줄 안되는 코드로 만들어낼 수 있다.
우리는 특정 픽셀값이 어떤 픽셀값으로 바뀌었는지를 T함수에 입력값으로 넣어 알아낼 수 있다.(equalized_pixels)
이때 equalized_pixels의 각 인덱스(pixel_value)는 바뀌기 전 픽셀값과 대응되고, 각 인덱스에 대한 값(eqalized_pixels)은 균등화가 진행된 후 어떤 픽셀값으로 바뀌어야 하는지를 의미한다.
따라서 원본 이미지에서 인덱스에 대응되는 위치(original_image == pixel_value)에 바뀐 픽셀값을 넣어주면 된다.(equalized_image[original_image == pixel_value] = equlized_value)
레나 할머니 이미지가 딱히 대비가 낮은건 아니었지만, 더욱 대비가 높아진것을 확인할 수 있다.
의문점
대비가 높아진건 인정.
하지만 Histogram Equalization이라면서 하나도 균등해보이지 않는 결과가 도출되었다.
너그러운 마음으로도 도저히 이해할 수가 없다.
이건 도대체 어떻게 설명해야하는것일까?
이에 대해서는 이산적인 이미지 데이터와 사각형으로 접근하여 넓이를 계산하는 과정에 있다.
이미지가 연속적이더라도 아마 결과는 생각보다 균등해보이지 않을 수 있다.
이유는 사각형으로 접근한 것 때문인데, 사각형으로 넓이를 계산하면 실제로 함수를 적분하여 얻은 실질적인 넓이와 오차가 있을 수 밖에 없기 때문이다.
더불어 이미지 데이터가 이산적이므로 이런 오차는 더욱 커지기 때문에 균등화를 진행한 이후의 히스토그램도 딱히 균등해졌다고 느낄 수 없다.
'컴퓨터 비전' 카테고리의 다른 글
3D keypoints 시각화 해보기 (0) | 2024.07.13 |
---|---|
Camera calibration (0) | 2024.07.06 |
[컴퓨터 비전] 이미지 변환(2) - Bilinear Interpolation (0) | 2023.01.21 |
[컴퓨터 비전] 이미지 변환(1) (0) | 2022.02.04 |