본문 바로가기

컴퓨터비전/라이브러리

[OpenCV] 특징점 연결 BF Matcher

파노라마 사진을 만들기 위해선 image stitching이라고도 부르는 image registration (영상 정합)을 해야한다. 그리고 이를 위해서는 두 이미지에서 같은 지점을 가리키는 좌표들의 쌍들을 먼저 알아내야 한다. 그래야 변환 행렬을 계산해 registration을 할 수 있기 때문이다. image registration은 보통 아래와 같은 흐름으로 진행된다.

 

이번 글에서는 matching과정에서 쓰이는 cv2.BFMatcher()함수에 대해 정리하고자 한다.

 

 

* 목표

이 챕터에서는

1) 우리는 한 이미지에서 특징점을 추출한 것을 다른 것과 어떻게 연결시키는 지를 확인하고,

2) OpenCV에서 Brute-Force matcher와 FLANN Matcher을 사용할 것이다.

 

1. Brute - Force Matcher의 기본

cv2.BFMatcher( ) 에서 BF는 Brute Force의 약자이다. Brute Force는 원래부터 있던 개념으로 무식하게 전부 다 확인해본다는 의미를 갖는다. 다르게 말하면 가능한 모든 경우에 대해 다 계산해본 후 최적의 결과를 반환하는 알고리즘이다.

 

 cv2.BFMatcher( ) 함수는 image A의 des1과 가장 유사도가 높은 image B의 descriptor를 찾기 위해 M개의 descriptor를 대상으로 거리를 모두 구한 다음에 가장 짧은 거리를 갖는 image B의 descriptor를 찾는다. 만약 image A의 des1과 image B의 des15가 가장 유사도가 높은 descriptor라고 가정하면, cv2.BFMatcher( )는 매칭된 des들의 index들과 유사도를 반환한다. (리터값 = 1, 15, descriptor 사이의 거리 반환 )

 

 예제를 통해 확인해보자.


BFM은 간단하다. 그것은 거리 계산을 이용하여 첫 번째 세트의 한 특징점의 descriptor를 가져와서, 두번째 세트에서의 다른 특징점들과 연결된다. 그리고 가장 가까운 것이 반환되게 된다.

 

BF matcher에 대해서, 처음에 우리는 cv2.BFMatcher()를 사용하여 BFMatcher객체를 만들어야만 한다.

그것은 두 가지 부가적인 인자를 갖는다.

왼쪽 사진과 오른쪽 사진 사이의 유사도가 높은 특징점들을 매칭하는 예제를 살펴보겠다.

 

import numpy as np
import cv2
imageA = cv2.imread('./pano_1.jpeg') # 왼쪽 사진
imageB = cv2.imread('./pano_2.jpeg') # 오른쪽 사진
 
grayA = cv2.cvtColor(imageA,cv2.COLOR_BGR2GRAY)
grayB = cv2.cvtColor(imageB,cv2.COLOR_BGR2GRAY)

## keypoint 및 descriptor를 뽑아내기 위해 "SIFT" 사용
sift = cv2.xfeatures2d.SIFT_create()
kpA, desA = sift.detectAndCompute(grayA, None)
kpB, desB = sift.detectAndCompute(grayB, None)

## cv2.BFMatcher( )를 통해 유사도가 가장 높은 keypoint 쌍들을 찾은 결과들 matches에 저장
bf = cv2.BFMatcher()
matches = bf.match(desA,desB)
 
sorted_matches = sorted(matches, key = lambda x : x.distance)
res = cv2.drawMatches(imageA, kpA, imageB, kpB, sorted_matches[:30], None, flags = 2)
 
cv2.imshow('res', res)

cv2.BFMatcher( )를 통해 유사도가 가장 높은 keypoint 쌍들을 찾은 결과들이 matches에 저장되는 과정에서
함수의 내부 흐름은 다음과 같다.
  (1) desA[0]와 매칭되는 desB의 des에 대한 정보를 matches[0]에 저장
  (2) desA[1]와 매칭되는 desB의 des에 대한 정보를 matches[1]에 저장
  (3) desA의 마지막 des까지 반복

 

matches[0]에는 queryIdx, trainIdx, distance 정보가 포함되는데 각각의 의미는 아래와 같다.

 

(1) queryIdx
   : 기준이 되는 descriptor 및 keypoint의 index이다.
     matches[0]는 desA[0]를 기준으로 삼기 때문에
     matches[0].queryIdx = 0 이다.
(2) trainIdx
   : desA[0]과 매칭된 image B, des의 index에 해당한다.
(3) distance
   : desA[0]와 매칭된 desB의 des 사이의 거리( = 유사도 )값이다.

 

→ matches[0].distance와 np.linalg.norm( ) 결과가 같은 것을 확인할 수 있다.

※ distance를 유사도와 같은 의미로 사용하는 이유는 Norm2를 사용하기 때문이다. MSE에서도 거리를 에러로 사용한다는 걸 떠올릴 것 !

 

 

 

 

 

[참고사이트]

https://adrian0220.tistory.com/36

http://blog.naver.com/PostView.nhn?blogId=samsjang&logNo=220657424078&categoryNo=66&parentCategoryNo=0&viewDate=¤tPage=2&postListTopCurrentPage=1&from=postList&userTopListOpen=true&userTopListCount=10&userTopListManageOpen=false&userTopListCurrentPage=2

https://www.pyimagesearch.com/2016/01/11/opencv-panorama-stitching/

https://towardsdatascience.com/image-stitching-using-opencv-817779c86a83

https://swprog.tistory.com/entry/%ED%8C%8C%EB%85%B8%EB%9D%BC%EB%A7%88-panorama-by-OpenCV-32

https://ballentain.tistory.com/36