공돌이 공룡의 서재

[python openCV] 이미지 처리 - 임계 처리 (4): adaptive threshold 본문

코딩/opencv

[python openCV] 이미지 처리 - 임계 처리 (4): adaptive threshold

구름위의공룡 2020. 9. 22. 21:29

<들어가기 전>

지금까지 배운 임계처리 방식에서는 단점이 하나 있다.

 

바로 조명에 약할 수 있다는 것. 사진을 찍을 때 조명의 위치에 따라서 어떤 사물은 그늘이 져있어 더 어둡게 보일 수 있고, 어떤 사물은 빛이 강해서 원래 색보다 더 밝게 보일 수 있다. 상황에 따라서는 글자의 일부분은 읽을 수 있는데 다른 부분은 어두워서 읽지 못하는 경우도 생길 것이다. 

 

이런 그림이 있다고 생각해보자. 3등분했을 때 조명 차이가 뚜렷히 나는 사진이라고 생각해보자. 여기서 사람, 사과, 집 (객체)를 배경과 구분하여 얻고 싶은 상황이다. 

 

 

 

 

하나의 임계값 (Tau 0)로 전체 이미지에 대해 임계처리를 한다 생각하자. 임의로 왼쪽부터 영역의 픽셀값이 60, 120, 180이라 하고, 임계값을 그 가운데 값인 120이라고 하자. 그럼 집이 그려져 있는 오른쪽 영역이 전부 검정색으로 처리가 되고 말 것이다. Otsu를 하든 Triangle을 쓰든 오른쪽 영역은 검정색이 되어버리고 말 것이다. 

 

이처럼 지금까지 배운 방식은 global thresholding에 해당하므로, 구분하고자 하는 대상들이 조명때문에 파악이 안 될 가능성이 있다. 하나의 임계값으로 이미지 전체에 대해 처리하기 때문이다. 그렇다면 어떻게 이 문제를 해결할 수 있을까.

 

 

 


<원리>

우선 문제 상황을 다시 살펴보자

 

 

 

위 그림처럼 밝기가 다른 각 영역에 대해 각각 임계값을 다르게 설정하면 어떨까? 객체의 검정선과 배경색을 구분할 수 있을 것이다. 이것이 adaptive threshold의 원리다.

 

즉, 이미지의 세부 영역에서의 히스토그램이 더 분석하기 좋게 분포되어 있다는 가정하에 사용하게 된다. 따라서 픽셀 히스토그램에서 뚜렷한 피크 영역이 없는 이미지(사진에서 빛이 불균일하게 있다는 것을 의미)에 대한 처리에 좋다. 

 

 

 


<함수>

cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C, dst=None)

 

src: 처리할 이미지

maxValue: 임계값

adapativeMethod: thresholding value를 계산하는 방법

thresholdType: 임계처리를 어떻게 할 것인지. 이전에 보았던 cv2.BINARY flag를 쓴다.

blocksize: thresholding을 적용할 영역 크기. 3, 5, 7, ... 의 양수를 쓴다. 3이면 3X3 크기로 자신 포함 이웃 8개의 픽셀과 같이 생각한다. 

C: 평균이나 가중 평균에서 차감할 값

 

CNN을 공부해본 사람이라면 이해가 좀 더 수월할 것 같다. 위의 원리에서 든 그림을 보면서 인수의 의미를 살펴보자.

 

adaptiveMethod는 우선 다음의 2가지 방식이 있다.

 

  • cv2.ADAPTIVE_THRESH_MEAN_C : 이웃 영역의 평균값 - C(상수) 임계값 결정
  • cv2.ADAPTIVE_THRESH_GAUSSIAN_C : 가우시안 필터 사용. 가중치로 가우시안 윈도우를 써서 이웃 영역 값들에 이 가중치까지 고려한 값들의 합 - C(상수)으로 결정

Gaussian 방식이 생소한 개념일 수 있는데 간단히 설명하면 다음과 같다.

 

우선 Gaussain window는 일종의 저주파 필터로 생각하면 된다. 

 

참고:

en.wikipedia.org/wiki/Window_function#:~:text=Gaussian%20window,-Gaussian%20window%2C%20%CF%83&text=The%20Fourier%20transform%20of%20a,with%20another%20zero%2Dended%20window.

 

stackoverflow.com/questions/54052076/how-does-an-adaptive-gaussian-threshold-filter-work

 

작은 이미지 영역에 대해 이 필터를 사용해서 임계값을 구하는 것이 Gaussian 방법이다. 이미지 내에서 픽셀 값의 변화가 빈번히 일어나는 영역은 이미지의 edge(경계선) 때문이므로 이를 제외시키고, 남은 영역은 조명에 따른 변화로 생각하는 것이 기본 아이디어다. 

 

C는 아무렇게나 잡으면 된다.

 

 

 


<코드>

이번에 쓸 이미지

커피를 기준으로 반반 밝은 곳과 어두운 곳이 나뉜다. 이 이미지를 다뤄보자.

 

import cv2

cafe = cv2.imread('./cafe.jpg')
cafe = cv2.resize(cafe, dsize=(0, 0), fx=0.5, fy=0.5)
gray = cv2.cvtColor(cafe, cv2.COLOR_RGB2GRAY)
mean_c = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 9, 10)
gaussian_c = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 9, 10)


cv2.imshow('original', gray)
cv2.imshow('mean', mean_c)
cv2.imshow('gaussian', gaussian_c)
cv2.waitKey()

 

결과

 

 

 

왼쪽과 가운데를 비교해보면 하나 차이점이 보일 것이다. 바로 나무무늬에 해당하는 벽면의 검정색 점들이 가운데에서 더 적다. 이는 가우시안이 필터역할을 하다보니 노이즈를 제거하는 기능도 있기 때문이다. 나무무늬의 픽셀이 불규칙적이므로 주파수로 관련지어 생각해봤을 때 필터 입장에선 노이즈로 간주하기 때문이라 볼 수 있다.

 

이 adaptive threshold의 장점은 윤곽선(edge)를 잡는데에 효과적이다. 따라서 가려진 글씨에 대한 처리에도 유용하다. 사물에 대해 쓸 경우에는 형체를 뚜렷히 알 수 있는 장점이 있다. 

 

윤곽선에 대한 처리와 필터에 대한 개념도 살펴봤으니 다음에는 canny edge detector를 살펴보자

 

Comments