Python/파이썬 OpenCV 공부

[파이썬 OpenCV] 호모그래피와 영상 매칭 - cv2.findHomgraphy

AI 꿈나무 2020. 10. 17. 21:12
반응형

호모그래피와 영상 매칭

 호모그래피(Homography)는 두 평면 사이의 투시 변환(Perspective transform)을 의미합니다.

 

출처 : opencv 튜토리얼

 위 그림에서 v1 지점에서 바닥에 있는 사진을 카메라로 찰영한다고 가정하겠습니다.

 찰영한 사진은 I1 입니다.

 왼쪽 위에서 찰영을 했기 때문에 영상도 좀 기울어지고 비스듬해집니다.

 원래 사진과 기울어진 사진과의 관계를 호모그래피 H1이라고 표현합니다.

 

 마찬가지로 v2 지점에서 바닥 사진을 찰영한 사진은 I2입니다.

 I2와 바닥 사진 관계를 호모그래피 H2로 표현합니다.

 

 또한 I1과 I2도 호모그래피 H12 관계가 생깁니다. 

 

 호모그래피는 투시변환과 거의 유사합니다.

 투시변환 행렬로 표현할 수 있습니다.

 

 

 투시변환 행렬에서 h33은 상수이므로 8개의 미지수로 구성됩니다.

 점 4개의 좌표를 알고 이동관계를 알면 8개의 식이 도출되어 8개의 미지수를 구해 투시변환 행렬을 구할 수 있습니다.

 

출처 : OpenCV 튜토리얼

 

 이처럼 비스듬하게 찍은 사진을 정면에서 찍은 사진처럼 변환할 수 있다는 것을 호모그래피라고 합니다.

 

1. 호모그래피 계산 함수 - cv2.findHomgraphy

 호모그래피 계산 함수에서는 잘못된 매칭이 있을 수 있다는 가정이 꼭 들어가야 합니다.

 미지수는 8개이지만 도출할 수 있는 수식이 8개보다 훨씬 많고 잘못된 정보가 들어가 있을 때

 투시변환을 하는 용도로 사용하는 함수가 cv2.findHomography 입니다.

 

 인자를 잘 지정해줘야 합니다.

 

cv2.findHomography(srcPoints, dstPoints, method=None, ransacReprojThreshold=None, mask=None, maxIters=None, confidence=None) -> retval, mask

• srcPoints: 1번 이미지 특징점 좌표. numpy.ndarray. shape=(N, 1, 2). dtype=numpy.float32.

• dstPoints: 2번 이미지 특징점 점 좌표. numpy.ndarray. shape=(N, 1, 2). dtype=numpy.float32.

• method: 호모그래피 행렬 계산 방법. 0, LMEDS, RANSAC, RHO 중 선택. 기본값은 0이며, 이상치가 있을 경우 RANSAC, RHO 방법 권장.

• ransacReprojThreshold: RANSAC 재투영 에러 허용치. 기본값은 3.

• maxIters: RANSAC 최대 반복 횟수. 기본값은 2000.

• retval: 호모그래피 행렬. numpy.ndarray. shape=(3, 3). dtype=numpy.float32.

• mask: 출력 마스크 행렬. RANSAC, RHO 방법 사용 시 Inlier로 사용된 점들을 1로 표시한 행렬. numpy.ndarray. shape=(N, 1), dtype=uint8

 주의할 점은 srcPoints, dstPoints 값을 (N, 1, 2)로 변환하여 입력해야 합니다.

 

 method 의 기본값은 좋은 결과가 나오지 않습니다.

 RANSAC는 이상점이 많을 때 잘 작동하는 알고리즘인데 무조건 RANSAC를 사용하는 것이 좋습니다.

 

 출력값은 2개 입니다.

 이 두 값을 잘 기억해둬야 합니다.

 MASK 행렬은 (N, 1) 형태로 반환하는데 기존의 알고있는 0또는 255로 구성되어 있는 마스크가 아닙니다.

 여기서의 MASK는 RANSAC로 계산하는 마스크입니다.

 80개의 점을 입력했을 때 이상점이 아닌 것으로 판단되는 것과 이상점으로 판단되는 것들에 대해서만 행렬을 만들어 줍니다.

 그 행렬 요소가 1로 채워져있다면 인라이어로 사용되었다는 의미입니다.

 마찬가지로 행렬 요소가 0이라면 아웃라이어를 의미합니다.

 

[RANSAC란?]

 RANSAC는 이상점이 많은 경우에도 가장 주축이 될 것 같은 직선을 찾아낼 수 있습니다.

 임의의 두 점을 선별해 직선을 그립니다.

 이 직선에 어느 정도 offset을 두어 범위 안에 있는 점들이 몇 개 있는지 파악합니다.

 이 방법을 반복하고, offset 범위 안에 가장 많은 점을 포함한 두 점을 이용해서 직선의 방정식을 계산하는 방법입니다.

 

 여기서는 80개 중 4개의 점으로 투시변환을 해보고 나머지 76개 점들로 검증을 하게 됩니다.

 이상점이 포함된 상태로 투시변환하게 된다면 offset 범위안에 들어오는 매칭 결과가 적게됩니다.

 그러면 그 4개의 점을 버립니다.

 

2. 호모그래피 계산 예제 코드

예제 코드 출처 :  황선규 박사님 github홈페이지 sunkyoo.github.io/opencv4cvml/

 

# 영상 불러오기
src1 = cv2.imread('box.png', cv2.IMREAD_GRAYSCALE)
src2 = cv2.imread('box_in_scene.png', cv2.IMREAD_GRAYSCALE)

if src1 is None or src2 is None:
    print('Image load failed!')
    sys.exit()

# 특징점 알고리즘 객체 생성 (KAZE, AKAZE, ORB 등)
feature = cv2.KAZE_create() # 기본값인 L2놈 이용
#feature = cv2.AKAZE_create()
#feature = cv2.ORB_create()

# 특징점 검출 및 기술자 계산
kp1, desc1 = feature.detectAndCompute(src1, None)
kp2, desc2 = feature.detectAndCompute(src2, None)

# 특징점 매칭
matcher = cv2.BFMatcher_create()
matches = matcher.match(desc1, desc2_

# 좋은 매칭 결과 선별
matches = sorted(matches, key=lambda x: x.distance)
good_matches = matches[:80]

print('# of kp1:', len(kp1))
print('# of kp2:', len(kp2))
print('# of matches:', len(matches))
print('# of good_matches:', len(good_matches))

# 호모그래피 계산
# DMatch 객체에서 queryIdx와 trainIdx를 받아와서 크기와 타입 변환하기
pts1 = np.array([kp1[m.queryIdx].pt for m in good_matches]
				).reshape(-1, 1, 2).astype(np.float32)
pts2 = np.array([kp1[m.trainIdx].pt for m in good_matches]
				).reshape(-1, 1, 2).astype(np.float32)
                
H, _ = cv2.findHomography(pts1, pts2, cv2.RANSAC) # pts1과 pts2의 행렬 주의 (N,1,2)

# 호모그래피를 이용하여 기준 영상 영역 표시
dst = cv2.drawMatches(src1, kp1, src2, kp2, good_matches, None,
                      flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)

(h, w) = src1.shape[:2]

# 입력 영상의 모서리 4점 좌표
corners1 = np.array([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]
                    ).reshape(-1, 1, 2).astype(np.float32)

# 입력 영상에 호모그래피 H 행렬로 투시 변환
corners2 = cv2.perspectiveTransform(corners1, H)

# corners2는 입력 영상에 좌표가 표현되있으므로 입력영상의 넓이 만큼 쉬프트
corners2 = corners2 + np.float32([w, 0])

# 다각형 그리기
cv2.polylines(dst, [np.int32(corners2)], True, (0, 255, 0), 2, cv2.LINE_AA)

cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

 

 단순한 이미지를 찾을 때는 템플릿 매칭이 적절합니다.

 호모그래피는 사진처럼 복잡한 것을 매칭할때 유용합니다.

 

반응형