다운로드
작성자: admin 작성일시: 2018-11-19 22:09:42 조회수: 338 다운로드: 32
카테고리: 기타 태그목록:

이미지 특징 추출

이번 절에서는 이미지에서 라인, 코너, 특징점 등과 같은 특징 추출(feature extraction) 방법을 공부한다.

이미지 미분

가로/세로 위치 변화에 따른 픽셀 값의 변화율을 이미지의 도함수(Image derivatives)라고 한다. 다음 식에서 $f(x)$는 $x$위치의 픽셀 명도이다.

$$ g_x = \dfrac{\partial f}{\partial x},\;\; g_y = \dfrac{\partial f}{\partial y} $$

$x,y$ 방향의 도함수의 크기를 구하는 것이 라플라스 연산이다.

$$ g = \sqrt{{g_x}^2 + {g_y}^2} $$

실제 이미지 프로세싱에서는 다음 수식에 따라 차분법을 사용한다.

$$G(x) = f(x+1) - f(x) \approx \begin{bmatrix}-1 & 0 & 1 \end{bmatrix} \;\; (\text{forward difference}) $$$$G(x) = f(x) - f(x-1) \approx \begin{bmatrix} 1 & 0 & -1 \end{bmatrix} \;\; (\text{backward difference}) $$$$G(x) = f(x+1) - f(x-1) \approx \begin{bmatrix}-1 & 0 & 1 \end{bmatrix} \;\; (\text{central difference}) $$

이 연산은 실제로는 다음과 같은 이미지 커널(필터, 윈도우)를 사용하여 필터링한 것과 같다.

  • forward difference: $[1, -1]$
  • backward difference: $[-1, 1]$
  • central difference: $[1, 0, -1]$

엣지 추출

경계선을 인지하는 것 엣지 추출(edge detection)이라고 한다. 엣지(경계선)는 이미지 안에서 픽셀의 값이 갑자기 변하는 곳이다. 따라서 엣지 추출을 하는 알고리즘은 이미지를 미분한 그레디언트(gradient) 벡터의 크기로 판단한다. 대표적인 엣지 추출 알고리즘으로 Sobel edge DetectionCanny edge Detection 등이 있다.

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

img = text()
sobelx = cv2.Sobel(img, cv2.CV_8U, 1, 0, ksize=3)
sobely = cv2.Sobel(img, cv2.CV_8U, 0, 1, ksize=3)
laplacian = cv2.Laplacian(img, cv2.CV_8U)

plt.subplot(2, 2, 1)
plt.imshow(img, cmap='gray')
plt.title('Original')
plt.xticks([])
plt.yticks([])
plt.subplot(2, 2, 2)
plt.imshow(laplacian, cmap='gray')
plt.title('Laplacian')
plt.xticks([])
plt.yticks([])
plt.subplot(2, 2, 3)
plt.imshow(sobelx, cmap='gray')
plt.title('Sobel X')
plt.xticks([])
plt.yticks([])
plt.subplot(2, 2, 4)
plt.imshow(sobely, cmap='gray')
plt.title('Sobel Y')
plt.xticks([]), plt.yticks([])
plt.show()

캐니 엣지 추출

  1. Smoothing - Gaussian Filter(5x5)를 이용하여 노이즈를 제거하는 작업
  2. 그레디언트 계산 - 그레디언트를 구하는 것도 중요하지만, 그 그레디언트의 방향을 아는 것도 중요하다. 그래서 Canny detector는 x축, y축의 미분 값을 따로 구하고, atan2 함수를 이용하여 그레디언트의 방향 또한 구한다. $$ G = \sqrt{G_x + G_y} \\ \theta = atan2(y, x) $$
  3. Nonmaximal suppression - 엣지가 아닌 픽셀은 지운다.
  4. Thresholding - 엣지라고 판단되는 픽셀이 실제로 엣지인가 검증하는 단계, 두 개의 기준값(threshold)을 사용해서 첨예하게 구분한다.

아래 코드는 이미지의 엣지를 추출하고 50과 100의 기준값으로 진짜 엣지를 구별하여 아래 그림과 같이 출력한다. cv2.canny라는 함수가 이 기능을 수행한다.

In [2]:
from skimage.data import text
import cv2

img = text()
edges = cv2.Canny(img, 50, 100)

plt.figure(figsize=(7, 6))
plt.subplot(2, 1, 1)
plt.imshow(img, cmap="gray")
plt.title("원본 이미지")
plt.axis('off')
plt.subplot(2, 1, 2)
plt.imshow(edges, cmap="Greys", alpha=0.3)
plt.axis('off')
plt.title("Canny Detection 결과")
plt.tight_layout()
plt.show()

코너 추출

코너란 엣지가 교차되는 점이라고 정의한다. 코너(Corner Detection))를 감지하는 가장 간단한 방법은 모든 엣지를 먼저 찾고 그것들이 교차되는 지점을 찾는 것이다. 다만 이 방법은 매우 비효율적이다. 아래 소개할 Harris corner detection은 모든 방향으로 변화가 큰 지점을 코너라고 정의하고 이를 기준으로 코너를 찾음으로써 비효율성 문제를 개선했다.

해리스 코너 추출

해리스 코너 추출 함수 Harris corner detector는 이미지 위에 마스크 행렬을 이동시키면서, 점 $(x, y)$ 주변에 대한 변화량을 구한다. $(x, y)$을 중심으로 일정 범위 내의 변화량을 $E(x,y)$라고 하고, 다음 수식과 같이 변화량을 계산한다. 다음 수식에서 $u$, $v$는 각각 x, y축으로의 이동량이고, $I(x,y)$는 $(x,y)$에서의 픽셀값이다.

$$ E(x, y) = \sum [I(x + u, y+v) - I(x, y)]^2$$

다음 그림을 보면 어떤 지점이 코너가 되는지 감이 올 것이다. 다음 그림의 (1) 위치에서는 어떤 방향으로 움직여도, 픽셀 간의 변화가 없다. 이를 'flat' 하다고 한다. 반면 (2)번 위치에서는 하나의 방향에 대해서는 변화가 크게 나타난다. Canny Edge Detection을 사용한다면, 이 위치에서 엣지가 검출되고, 엣지의 방향이 y축 방향이라는 것도 알 수 있다. 그렇지만, 이 엣지의 방향(y축 방향)으로는 변화가 없다. (3) 번 위치에서는 모든 방향으로 픽셀 값이 변화한다. Harris Corner Detection에서는 이러한 지점을 코너로 판단한다. 이렇게 찾은 코너는 이미지를 회전 시키더라도 찾을 수 있다.

In [3]:
import matplotlib.patches as patches

x = [1, 1, 7]
y = [0, 7, 4]

fig, ax = plt.subplots(1, 1)
ax.set_title("위치 별 변화량")
ax.set_axis_off()
ax.plot(x, y, "k-", lw=4)
ax.set_xlim(-3, 9)
ax.set_ylim(0, 8)

ax.add_patch(patches.Rectangle((2.3, 1.5), 1, 1, color='g', alpha=0.7))
ax.add_patch(patches.Rectangle((2.2, 1.4), 1, 1, color='g', alpha=0.7))
ax.add_patch(patches.Rectangle((2.4, 1.6), 1, 1, color='g', alpha=0.7))
ax.add_patch(patches.Rectangle((.5, 2.9), 1, 1, color='b', alpha=0.7))
ax.add_patch(patches.Rectangle((.4, 2.8), 1, 1, color='b', alpha=0.7))
ax.add_patch(patches.Rectangle((.6, 3), 1, 1, color='b', alpha=0.7))
ax.add_patch(patches.Rectangle((.5, 6.5), 1, 1, color='r', alpha=0.7))
ax.add_patch(patches.Rectangle((.4, 6.4), 1, 1, color='r', alpha=0.7))
ax.add_patch(patches.Rectangle((.6, 6.6), 1, 1, color='r', alpha=0.7))
ax.text(0, 7, "(3)")
ax.text(0, 3.3, "(2)")
ax.text(1.8, 2, "(1)")

plt.tight_layout()
plt.show()

이 알고리즘은 OpenCV에서 cv2.cornerHarris() 함수로 구현되어 있다. 아래의 코드는 Harris Corner Dectection으로 체크보드 이미지에서 코너를 찾는 기능을 수행한다.

In [4]:
dst = cv2.cornerHarris(img, 2, 3, 0.24)
ret, dst = cv2.threshold(dst, 0.001 * dst.max(), 1, 0)
x, y = np.nonzero(dst)
plt.figure(figsize=(7,7))
plt.title("Harris Corner Dectection 결과")
plt.axis("off")
plt.imshow(img, cmap="gray")
plt.scatter(y, x, s=30, c="w", alpha=0.7)
plt.show()