조금씩 꾸준히 완성을 향해

[OpenCV/Python] 코너 검출 - 해리스(Harris), FAST, preCornerDetect, preCornerEigenValsAndVecs 본문

Python/OpenCV

[OpenCV/Python] 코너 검출 - 해리스(Harris), FAST, preCornerDetect, preCornerEigenValsAndVecs

all_sound 2022. 11. 18. 17:35

코너 검출

  • 특징(feature)
    • 영상으로부터 추출한 유용한 정보
    • global feature & local feature
  • 코너 검출 방법
    • 엣지 방향이 급격히 변하는 부분
    • 꼭지점, 튀어나온 부분
    • 코너는 다른 지역 특징에 비해 분별력이 높고 영상 전체에 분포

# 코너점 검출

  • 미분 연산자에 의한 엣지 방향 이용
  • cv2.preCornerDetect(src, ksize)
    • dst의 local optima 값으로 검출
 
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import cv2
src = cv2.imread('./CornerTest.jpg') # 컬러로 이미지 가져오기
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY) # gray 컬러로 변경

res = cv2.preCornerDetect(gray, ksize=3)
ret, res2 = cv2.threshold(np.abs(res), 0.1, 0, cv2.THRESH_TOZERO) # 임계값 0.1 보다 낮으면 블랙(0)으로, 높으면 원래값
points = np.argwhere(res2 > 0) # 0이 아닌 값들의 위치 저장 => 코너일 가능성이 있는 점들
points.shape
# (24, 2)
# points 점들을 중심으로 작은 원 그리기
dst = src.copy()
for y, x in points:
    r = np.random.randint(0, 255)
    g = np.random.randint(0, 255)
    b = np.random.randint(0, 255)
    cv2.circle(dst, (x, y), radius=4, color=(r,g,b), thickness=2)  
cv2.imshow('src', dst) 
cv2.waitKey()
cv2.destroyAllWindows()
# 24개의 원 생성 -> 실제 코너점(8개) 보다 많음
 
 
  • 침식과 팽창을 이용한 corner 찾기 방법
kernel = cv2.getStructuringElement(shape=cv2.MORPH_RECT, ksize=(11,11))
dilate = cv2.dilate(src, kernel)
erode = cv2.erode(src, kernel)

plt.figure(figsize=(12, 6))
ax1 = plt.subplot(1, 3, 1)
ax2 = plt.subplot(1, 3, 2)
ax3 = plt.subplot(1, 3, 3)
ax1.imshow(src)
ax2.imshow(dilate)
ax3.imshow(erode)
 
def findLocalMaxima(src):
    kernel = cv2.getStructuringElement(shape=cv2.MORPH_RECT, ksize=(11,11))
    dilate = cv2.dilate(src, kernel) # dilate => 최대픽셀이 팽창
    localMax = (src == dilate) # 원본에서 최대픽셀값인 값 찾기
    erode = cv2.erode(src, kernel) # erode => 최소픽셀값 침식
    localMax2 = (src > erode) # 원본에서 최소픽셀값보다 큰 픽셀값 찾기
    localMax &= localMax2 # &= AND연산
    points = np.argwhere(localMax == True) # localMax 좌표 저장
    points[:, [0, 1]] = points[:, [1, 0]] # x, y축 위치 변경
    return points

points = findLocalMaxima(res2)
dst = src.copy()
for x, y in points:
    r = np.random.randint(0, 255)  # 랜덤 컬러설정
    g = np.random.randint(0, 255)
    b = np.random.randint(0, 255)
    cv2.circle(dst, (x, y), 4, (b, g, r), 2) # 코너점에 원 그리기
cv2.imshow('src', gray)
cv2.imshow('res2', res2)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

  • cv2.preCornerEigenValsAndVecs(srx, blockSize, ksize)
    • ksize = 이웃 윈도우 크기
    • ksize sobel mask
    • 영상 내 각 이웃의 covariance matrix M의 eigenvalue, eigenvector를 계산하여 코너 검출
 
src = cv2.imread('./CornerTest.jpg') # 컬러로 이미지 가져오기
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY) # gray 컬러로 변경

# 아이겐밸류 구해서 임계값보다 큰 값 반환(이 값이 코너)
eigen = cv2.cornerMinEigenVal(gray, blockSize=5)

#eigen 밝기값 분포 확인, eigen이 특정값을 초과하는 좌표 구하기
corners = np.argwhere(eigen > 0.2)
corners[:, [0, 1]] = corners[:, [1,0]]
corners.shape
# (8, 2)
#원본 영상 카피하여 위의 좌표를 빨간색 점으로 출력
dst = src.copy()
for x, y in corners:
    cv2.circle(dst, (x, y), 4, (0, 0, 255), 2)
cv2.imshow('gray', gray)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
 
# lena로 실습
src = cv2.imread('./lena.jpg')
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY) 
eigen = cv2.cornerMinEigenVal(gray, blockSize=5)
eigen.max()
0.056870822
T = 0.008
corners = np.argwhere(eigen > T)
corners[:, [0, 1]] = corners[:, [1,0]]
corners.shape
# (2489, 2)
dst = src.copy()
for x, y in corners:
    cv2.circle(dst, (x, y), 4, (0, 0, 255), 1)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

# 해리스 코너 검출 방법

- 영상 내부 작은 영역이 모든 방향에 대해 변화가 큰 경우 코너로 규정

- 코너 응답 함수 R을 반환 -> R(x,y)가 충분히 크면 코너로 구분

 

cv2.cornerHarris(src, blockSize, ksize, k, dst=None, borderType=None) 

  • src: 입력 단일채널 8비트 또는 실수형 영상
  • blockSize: 코너 응답 함수 계산에서 고려할 이웃 픽셀 크기. 보통 2~5.
  • ksize: (미분을 위한) 소벨 연산자를 위한 커널 크기. 보통 3.
  • k: 해리스 코너 검출 상수 (보통 0.04~0.06)
  • dst: 해리스 코너 응답 계수. src와 같은 크기의 행렬(numpy.ndarray). dtype=numpy.float32.
  • borderType: 가장자리 픽셀 확장 방식. 기본값은 cv2.BORDER_DEFAULT.
# 사각형
src = cv2.imread('./CornerTest.jpg.')
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)

# cornerHarris 함수 
res = cv2.cornerHarris(gray, blockSize=5, ksize=3, k=0.01)
_, res2 = cv2.threshold(np.abs(res), 0.06, 0, cv2.THRESH_TOZERO) #임계값 0.06보다 낮으면 블랙(0), 높으면 원래값
corners= np.argwhere(res2 > 0) # res2가 0이 아닌 좌표 출력 
corners.shape # 좌표 개수 확인
# (8, 2)
for y, x in corners: #원을 그려 좌표 표시
    cv2.circle(src, (x, y), radius=4, color=(0,0,255), thickness=3)  
cv2.imshow('src', src) 
cv2.waitKey()
cv2.destroyAllWindows()
# findLocalMaxima 함수 사용

src = cv2.imread('./CornerTest.jpg.')
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)

# cornerHarris 함수 
res = cv2.cornerHarris(gray, blockSize=5, ksize=3, k=0.01) # 해리스에서 지정하는 k 상수
ret, res2 = cv2.threshold(np.abs(res), 0.06, 0, cv2.THRESH_TOZERO) # res 절대값이 임계값 0.02 보다 낮으면 0(블랙), 높으면 원래값
corners = findLocalMaxima(res2) # 위 함수에 적용, 로컬 맥시마 위치 저장
print('corners =', corners) # 코너 좌표 출력
dst = src.copy()
for x, y in corners:
    cv2.circle(dst, (x, y), 3, (0, 0, 255), 2)
cv2.imshow('cornerHarris', dst)
cv2.waitKey()
cv2.destroyAllWindows()
# dasol 이미지
src = cv2.imread('./dasol.jpg')
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
res = cv2.cornerHarris(gray, blockSize=5, ksize=3, k=0.01)
_, res2 = cv2.threshold(np.abs(res), 0.0009, 0, cv2.THRESH_TOZERO)
corners= np.argwhere(res2 > 0)

for y, x in corners:
    cv2.circle(src, (x, y), radius=5, color=(0,0,255), thickness=1)  
cv2.imshow('src', src) 
cv2.waitKey()
cv2.destroyAllWindows()
 

# FAST 코너 검출 방법

- 주변 16개 픽셀 값 크기를 분석

- 기준 픽셀(p)보다 충분히 밝거나 또는 충분히 어두운 픽셀이 n개 연속으로 나타나면 코너로 인식(n은 보통 9)

 

▶ cv2.FastFeatureDetector_create(, threshold=None, nonmaxSuppression=None, type=None) 

  • threshold: 중심 픽셀 값과 주변 픽셀 값과의 차이 임계값. 기본값은 10. 30~60 적절
  • nonmaxSuppression: 비최대 억제 수행 여부. 기본값은 True.
  • type: 코너 검출 방법. 기본값은 cv2.FAST_FEATURE_DETECTOR_TYPE_9_16.
  • retval: FastFeatureDetector 객체
src = cv2.imread('./CornerTest.jpg.')
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)

fast = cv2.FastFeatureDetector_create(30)
fast.setNonmaxSuppression(0)  # off
kp = fast.detect(gray, None)
dst = src.copy()
cv2.drawKeypoints(src, kp, dst, (0, 0, 255))
cv2.imshow('fast', dst)
cv2.waitKey()
cv2.destroyAllWindows()
# lena
src = cv2.imread('./lena.jpg')
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
# fast.setNonmaxSuppression(0)  # on
fast = cv2.FastFeatureDetector_create(40)
kp = fast.detect(gray, None)
dst = src.copy()
cv2.drawKeypoints(src, kp, dst, (0, 0, 255))
cv2.imshow('fast', dst)
cv2.waitKey()
cv2.destroyAllWindows()