소스 뷰어

헤리스 코너 검출

  • 영상에서 객체를 추척할 때나 영상과 영상을 매칭할 때 일반적으로 사용하는 방법이 중요한 특징 정보를 서로 비교하는 것이다.영상 내에는 에지나 직선과 같은 다양한 특정 정보들이 있다. 그러나 직선 정보는 영상 구조 파악 및 객체 검출에는 도움이 되지만, 영상 매칭에는 큰 도움이 되지 않는다. 도한, 에지는 강도와 방향 정보만 가지므로 영상 매칭을 하기에는 정보가 부족하다.
  • 에지나 직선처럼 영상처리에는 중요한 특정 정보로 사용되는 꼭지점 혹은 코너(corner)라 부르는 특징점이 있다
  • 이 코너 정보 중에서 영상의 왜곡에도 불편하는 특징을 가진 지점들이 영상 매칭에 유용하게 사용될 수 있다. 대표적인 검출기 중 하나인 헤리스 코너 검출기의 원리에 대해서 설명한다.
import numpy as np, cv2, time
# 문자열 출력 함수 - 그림자 효과
def put_string(frame, text, pt, value=None, color=(120, 200, 90)) :
    text = str(text) + str(value)
    shade = (pt[0] + 2, pt[1] + 2)
    font = cv2.FONT_HERSHEY_SIMPLEX
    cv2.putText(frame, text, shade, font, 0.7, (0, 0, 0), 2) # 그림자 효과
    cv2.putText(frame, text, pt   , font, 0.7, color, 2) # 작성 문자
# 수행시간 체크 함수
stime = 0
def ck_time(mode = 0 , msg = ""):
    global stime

    if (mode ==0 ):
        stime = time.perf_counter()

    elif (mode==1):
       etime = time.perf_counter()
       elapsed = (etime - stime)
       print("수행시간 = %.5f sec" % elapsed)  # 초 단위 경과 시간

    elif (mode == 2):
        etime = time.perf_counter()
        return (etime - stime)

    elif (mode== 3 ):
        etime = time.perf_counter()
        elapsed = (etime - stime)
        print("%s = %.5f sec" %(msg, elapsed))  # 초 단위 경과 시간
def cornerHarris(image, ksize, k):
    dx = cv2.Sobel(image, cv2.CV_32F, 1, 0, ksize)   # 미분 행렬 - 수평 소벨 마스크
    dy = cv2.Sobel(image, cv2.CV_32F, 0, 1, ksize)   # 미분 행렬 - 수직 소벨 마스크

    a = cv2.GaussianBlur(dx * dx, (5, 5), 0)                     # 가우시안 블러링 수행
    b = cv2.GaussianBlur(dy * dy, (5, 5), 0)
    c = cv2.GaussianBlur(dx * dy, (5, 5), 0)
    
    corner = (a * b - c * c) - k * (a + b) ** 2        # 코너 응답 함수 계산 -행렬 연산 적용
    return corner
def drawCorner(corner, image, thresh):
    cnt = 0
    corner = cv2.normalize(corner, 0, 100, cv2.NORM_MINMAX)
    corners = []
    for i in range(1, corner.shape[0]-1 ):
        for j in range(1, corner.shape[1]-1 ):
            neighbor = corner[i-1:i+2, j-1:j+2].flatten()
            max = np.max(neighbor[1::2])
            if thresh < corner[i, j] > max : corners.append((j,i))

    for pt in corners:
        cv2.circle(image, pt, 3, (0, 230, 0), -1)    # 좌표 표시
    print("임계값: %2d , 코너 개수: %2d" %(thresh, len(corners)) )
    return image
def onCornerHarris(thresh):
    img1 = drawCorner(corner1, np.copy(image), thresh)
    img2 = drawCorner(corner2, np.copy(image), thresh)

    put_string(img1, "USER", (10, 30), "" )
    put_string(img2, "OpenCV", (10, 30), "")
    dst = cv2.repeat(img1, 1, 2)                            # 두 개 영상을 하나의 윈도우에 표시
    dst[:, img1.shape[1]:, :] = img2
    cv2.imshow("harris detect", dst)
image = cv2.imread('img/harris.jpg', cv2.IMREAD_COLOR)
if image is None: raise Exception("영상 파일 읽기 에러")
blockSize = 4                                                # 이웃 화소 범위
apertureSize = 3                                             # 소벨 마스크 크기
k = 0.04
thresh = 2                                                   # 코너 응답 임계값
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
corner1 = cornerHarris(gray, apertureSize, k)                # 사용자 정의 함수
corner2 = cv2.cornerHarris(gray, blockSize, apertureSize, k) # OpenCV 제공 함수
onCornerHarris(thresh)
cv2.createTrackbar("Threshold", "harris detect", thresh, 20, onCornerHarris)
cv2.waitKey(0)
임계값:  2 , 코너 개수: 98
임계값:  2 , 코너 개수: 103
임계값:  3 , 코너 개수: 35
임계값:  3 , 코너 개수: 37
임계값: 10 , 코너 개수:  0
임계값: 10 , 코너 개수:  0
임계값:  6 , 코너 개수:  1
임계값:  6 , 코너 개수:  0
임계값:  2 , 코너 개수: 98
임계값:  2 , 코너 개수: 103
임계값:  6 , 코너 개수:  1
임계값:  6 , 코너 개수:  0
임계값:  2 , 코너 개수: 98
임계값:  2 , 코너 개수: 103
임계값:  1 , 코너 개수: 361
임계값:  1 , 코너 개수: 401
-1

result