다운로드
작성자: admin 작성일시: 2018-11-23 19:37:48 조회수: 256 다운로드: 44
카테고리: 머신 러닝 태그목록:

이미지 필터

임계처리

임계처리(thresholding)는 사용자가 지정한 기준값(Threshold)를 기준으로 이미지 픽셀의 값이 더 크면 최대값, 더 작으면 최소값을 적용하여 이진화 한다. OpenCV에서는 cv2.threshold()라는 함수로 구현되어 있다. 함수의 파라미터는 다음과 같다.

  • cv2.threshold(src, thresh, maxval, type)
    • src : 그레이 스케일 이미지
    • thresh : 기준값
    • maxval : 기준값을 넘었을 때 적용할 값
    • type : 임계처리 유형

마지막 type 임계처리 유형은 5가지가 구현되어 있다. 이 5가지의 유형을 간단한 수식으로 표현하면 다음의 표와 같다. 표에서 $I(x,y)$는 $x,y$ 위치에서의 픽셀값(Intensity)를 의미한다.

임계처리유형 방법
THRESH_BINARY $\begin{cases} \text{maxval} & I(x,y) > \text{thresh} \\ 0 & else \end{cases}$
THRESH_BINARY_INV $ \begin{cases} 0 & I(x,y) > \text{thresh} \\ \text{maxval} & else \end{cases}$
THRESH_TRUNC $\begin{cases} \text{thresh} & I(x,y) > \text{thresh} \\ I(x,y) & else \end{cases}$
THRESH_TOZERO $\begin{cases} I(x,y) & I(x,y) > \text{thresh} \\ 0 & else \end{cases}$
THRESH_TOZERO_INV $\begin{cases} 0 & I(x,y) > \text{thresh} \\ I(x,y) & else \end{cases}$
표 15.1.1 : 임계처리 유형

각 임계유형의 결과를 시각화 하면 다음과 같다. 다음 코드는 코인 이미지에 5가지 종류의 임계처리를 한다.

In [1]:
import cv2
from skimage.data import coins

img = coins()

_, thresh1 = cv2.threshold(img, 255/2, 255, cv2.THRESH_BINARY)
_, thresh2 = cv2.threshold(img, 255/2, 255, cv2.THRESH_BINARY_INV)
_, thresh3 = cv2.threshold(img, 255/2, 255, cv2.THRESH_TRUNC)
_, thresh4 = cv2.threshold(img, 255/2, 255, cv2.THRESH_TOZERO)
_, thresh5 = cv2.threshold(img, 255/2, 255, cv2.THRESH_TOZERO_INV)

titles = ['원본이미지', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]

plt.figure(figsize=(9, 5))
for i in range(6):
    plt.subplot(2, 3, i+1), plt.imshow(images[i], 'gray')
    plt.title(titles[i], fontdict={'fontsize': 10})
    plt.axis('off')

plt.tight_layout(pad=0.7)
plt.show()

적응임계처리

임계처리의 경우는 기준값(threshold)이 하나이기 때문에, 기준값을 설정하는데 고민을 해야하고 한 이미지 내에서 음영, 명도의 차이가 있을 때는 한 부분 전체가 흰색 혹은 검은색으로 보인다는 단점이 있다. 적응임계처리는 이런 단점을 보안하기 위해 한 픽셀의 임계처리를 할 때, 일정한 영역 내의 이웃한 픽셀들의 값들을 이용하여 기준값을 계산하고 그에 따라 임계처리를 한다. OpenCV에서는 cv2.adaptiveThreshold()라는 함수로 구현되어 있다. 파라미터는 다음과 같다.

  • cv2.adaptiveThreshold()
    • src : 그레이스케일 이미지
    • maxValue – 기준값을 넘었을 때 적용할 값
    • adaptiveMethod : 영역 내에서 기준값을 계산하는 방법
    • thresholdType : 임계처리 유형, 이전 임계처리 유형 중, THRESH_BINARY, THRESH_BINARY_INV 만 가능하다.
    • blockSize : 임계처리를 적용할 영역의 크기
    • C : 평균이나 가중평균에서 차감할 값

adaptiveMethod에는 ADAPTIVE_THRESH_MEAN_C, ADAPTIVE_THRESH_GAUSSIAN_C 2가지 방법이 있다.

  • cv2.ADAPTIVE_THRESH_MEAN_C : 영역 내의 평균값에 C를 뺀 값을 기준값으로 사용
  • cv2.ADAPTIVE_THRESH_GAUSSIAN_C : 영역의 크기와 같은 가중치 행렬 곱하여 가중합 한 것에서 C를 뺀 값을 기준값으로 사용한다. 이 가중치 행렬은 다음과 같이 계산한다.
$$G_i= \alpha \exp \left({-\dfrac{\left(i-(\dfrac{\text{blockSize}^2-1}{2}\right)^2}{2 \sigma^2 }} \right)$$
  • $G$는 $\text{blockSize}^2$개의 원소를 가진 가중치 벡터이고, $G_i$는 그 가중치 벡터의 $i$번째 원소를 의미한다.
  • $\alpha$는 $\sum G_i = 1$이 되도록하는 상수이다.
  • $\sigma = 0.3 \times ((\text{blockSize}^2 - 1) \times0.5 - 1) + 0.8$
In [2]:
from skimage.data import page

img = page()
ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
th2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
                            cv2.THRESH_BINARY, 15, 2)
th3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                            cv2.THRESH_BINARY, 15, 2)
titles = ['원본이미지', '임계처리', '평균-적응임계처리', 'Gaussian-적응임계처리']

images = [img, th1, th2, th3]
plt.figure(figsize=(7, 5))
for i in range(4):
    plt.subplot(2, 2, i+1)
    plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.axis('off')

plt.tight_layout(pad=0.7)
plt.show()

이미지 필터링

이미지 필터링(image filtering)은 필터(filter) 또는 커널(kernel) 또는 윈도우(window)라고 하는 정방행렬을 정의하고 이 커널을 이동시키면서 같은 이미지 영역과 곱하여 그 결과값을 이미지의 해당 위치의 값으로 하는 새로운 이미지를 만드는 연산이다.

원본 이미지의 $(x,y)$ 위치의 명도를 $f(x,y)$, 필터 이미지를 $h(x,y)$, 필터링된 결과를 $g(x,y)$라고 하면 수식으로 다음과 같다.

$$ g(x, y) = \sum_{u=-K/2}^{K} \sum_{v=-K/2}^{K} f(x + u, y + v) h(u, v) $$

이 식에서 $K$는 필터 크기의 절반을 뜻한다. 예를 들어 $3\times 3$ 크기의 필터에서는 $K=1$이다. 위 식을 줄여서 다음처럼 나타내기도 한다. $W$S는 윈도우 영역을 뜻한다.

$$ g(x, y) = \sum_{W} f(x + u, y + v) h(u, v) $$

위 식에서 필터를 좌우, 상하로 뒤집은 필터를 사용하면 콘볼루션(convolution)이라고 한다.

$$ \tilde{h}(u, v) = -h(-u, -v) $$$$ g(x, y) = \sum_{W} f(x - u, y - v) \tilde{h}(u, v) $$

openCV에서는 cv2.filter2D() 함수를 사용하면 사용자가 정의한 커널을 적용하여 이미지를 필터링 할 수 있다. 다음 코드는 평균을 구하는 커널을 이용해 이미지를 보정한다. 더 자세히 말하자면, 한 픽셀을 중심으로 하여 이미지 위에 커널을 올리고, 그 영역의 평균값으로 해당 픽셀 값을 대체한다. 이 때, 커널의 크기를 5, 10, 15, 20으로 점점 크게 바꾸었다. 커널이 커지면서 이미지가 점점 더 흐려지는 것을 확인 할 수 있다. cv2.filter2D() 함수의 파라미터로는 이미지, 이미지의 채널 수, 커널의 크기를 입력하면 된다.

In [3]:
from skimage.data import astronaut

img = astronaut()

for k in range(5, 16, 5):
    kernel = np.ones((k, k)) / k**2
    filtering = cv2.filter2D(img, -1, kernel)
    plt.subplot(1, 3, k/5)
    plt.imshow(filtering)
    plt.title("커널 사이즈 {}".format(k))
    plt.axis("off")

plt.show()