[OpenCV] 05-2. Harris Corner Detection
🐍Python/OpenCV

[OpenCV] 05-2. Harris Corner Detection

728x90
반응형

< Harris Corner Detection >

이번 장에서는

  • Harris Corner Detection의 개념의 이해
  • cv2.cornerHarris(), cv2.cornerSubPix()

에 대해 볼 것 이다.

Theory

마지막 장에서, 모든 방향으로 채도의 변화가 큰 이미지에서 모서리는 영역이라는 것을 봤다. 초창기에 이 모서리를 찾기 위한 시도가 바로 지금에 와서 Harris Corner Detection라고 불리는 것이다. 그는 이 간단한 아이디어를 수학적 형태로 나타냈다. 이는 모든 방향에서 (u,v)의 이동에 의한 채도 차이를 찾는 것이다. 아래 처럼 나타내진다.


E(u,v)=x,yw(x,y)[I(x+u,y+v)I(x,y)]2

  • w(x,y) : window function
  • I(x+u,y+v) : shifted intensity
  • I(x,y) : intensity

Window function은 픽셀의 표면에 가중치를 주는 직사각형 창이나 가우시나 창을 말한다.

모서리를 탐지하기 위해서 우리는 E(u,v)를 최대화해야한다. 즉, 두 번째 항을 최대화해야 한다. 위의 식에 Talyor Expansion을 적용시키고, 수학적 조치를 취하면 최종식을 얻을 수 있다 :

E(u,v)[u v]M[uv]

여기서 M은 다음과 같다.

M=x,yw(x,y)[IxIxIxIyIxIyIyIy]

여기서 Ix Iy는 각각 x와 y 방향의 편미분 값이다.(cv2.sobel()로 더 쉽게 찾을 수 있다.)

다시 메인 부분으로 돌아오자. 그 이후에, 점수 방정식을 만들었는데, 이는 창이 모서리인지 아닌지를 판단할 수 있다.


R=det(M)k(trace(M))2

  • det(M)=λ1λ2
  • trace(M)=λ1+λ2
  • λ1 λ2 M의 고유값(eigen value)이다.

그래서 이 고유값들의 값이 영역이 모서리인지, 가장자리인지 평면인지를 결정한다.

  • λ1,λ2이 작아서 |R|이 작을 때, 영역은 “평면”이다.
  • λ1>>λ2또는 반대여서 R<0이면, 영역은 “가장자리”이다.
  • λ1 λ2가 크고 λ1λ2여서 R이 클 때, 영역은 “모서리”이다.

아래의 그림을 보면 이해하기 쉽다!


그래서 Harris Corner Detection의 결과는 이 점수와 함께 흑백 이미지이다. 적합한 임계값을 지정하면 이미지의 모서리를 얻을 수 있다. 다음의 이미지로 진행해보자!

Harris Corner Detector in OpenCV

OpenCV는 이를 위한 cv2.cornerHarris() 함수가 있다. 인자들을 보자:

  • img - 입력 이미지이고, 흑백스케일이고 float32 형태이어야한다.
  • blockSize - 모서리 탐지를 위한 고려될 이웃 픽셀의 크기를 말한다.
  • ksize - Sobel 미분에 사용될 인자
  • k - 위의 방정식에서 나온 검출식에서 k

아래 예를 보자.

import cv2
import numpy as np

img = cv2.imread('./images/container.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

gray = np.float32(gray)
dst = cv2.cornerHarris(gray,2,3,0.04)

# 결과는 모서리에 마킹을 하기 위해 dilated(팽창)되어있을 것이다. 중요하진 않다!
dst = cv2.dilate(dst,None)

# 최적의 값을 위해 임계값을 정한다. 이미지에 따라 달라질 수 있다. 
img[dst>0.01*dst.max()] = [0,0,255]

cv2.imshow('dst',img)
if cv2.waitKey(0) & 0xFF == 27:
    cv2.destroyAllWindows()

결과는 아래와 같다.


결과가 정확하진 않지만 이미지의 특성 파악에 도움이 된다.

임계값을 0.05으로 높였더니 조금 더 정확하게 탐지를 하긴 했다!


Corner with Subpixel Accuracy

때때로, 최대의 정확도로 모서리를 찾아야 할 필요가 있다. OpenCV는 cv2.cornerSubPix()라는 함수로 sub-pixel 정확도와 함께 모서리 탐지를 정제한다. 아래는 예시이다. 보통, 우리는 먼저 Harris 모서리를 먼저 찾는다. 그리고 모서리들의 중심을 넘겨서(모서리에 다량의 픽셀이 있을 것이기에, 그들의 중심을 가져온다) 그것들을 수정한다. Harris 모서리는 빨간 픽셀로 표시되고 수정된 모서리들은 초록색 픽셀로 표시된다. 이 함수에 대해서는, 언제 반복을 중지할 것 인가에 대한 기준을 규정해야 한다. 우리는 특정한 횟수의 반복 또는 일정한 정확도가 달성된 후 어느 것이든 먼저 발생하는 것을 멈춘다. 우리는 모서리를 찾을 이웃 픽셀의 크기를 규정할 필요가 있다.

img = cv2.imread('./images/container.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# Harris Corner 찾기
gray = np.float32(gray)
dst = cv2.cornerHarris(gray,2,3,0.04)
dst = cv2.dilate(dst,None)
ret, dst = cv2.threshold(dst,0.05*dst.max(),255,0)
dst = np.uint8(dst)

# 중심을 찾기
ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst)

# 멈추기 위한 기준을 정하고 모서리를 정제하자
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100 ,0.001)
corners = cv2.cornerSubPix(gray,np.float32(centroids),(5,5),(-1,-1),criteria)

# 그리자!
res = np.hstack((centroids,corners))
res = np.int0(res)
img[res[:,1],res[:,0]] = [0,0,255]  # 각 열만 추출
img[res[:,3],res[:,2]] = [0,255,0]

cv2.imwrite('subpixel5.png',img)

임계값은 아까 결과가 더 좋았던 0.05로 사용했다.


자세히 보면 빨간 점에서 수정된 점들을 초록색으로 표시한 것을 볼 수 있다. 조금 더 정확해진 모서리들도 있지만, 벗어난 모서리도 있다!

728x90
반응형