이번시간에는 두 이미지 간의 템플릿을 비교(매칭)하는 방법과 그 과정에 대해서 설명드리겠습니다.
흔히 컴퓨터비전에서는 두 이미지를 제시하고 여기서 특징점을 파악하여 왼쪽이미지의 이 부분과 오른쪽 이미지의 이 부분이 같은 것을 어떻게 수학적으로 알아낼 수 있을지에 대한 것이 주요 관심사였는데, 이 주제를 보통 'Template Matching'이라고 부릅니다.
이 템플릿 매칭을 어떻게 효과적으로 해결할지에 대해서 얘기할때 대표적으로 거론되는 알고리즘 중 하나가 바로 SIFT-Descriptor입니다.
SIFT Descriptor란?
정의:
Scale-Invariant Feature Transform Descriptor의 약자로, 이미지 내에서 특징정(keypoint)을 찾아내고, 그 특징점을 나타내는 고유한 벡터를 생성후 비교하는 알고리즘입니다. 두 이미지간의 유사도를 비교하는데 사용됩니다.
SIFT Descriptor 생성과정:
일단 아래와 같이 sift 검출기를 생성하여 이미지를 그레이 스케일로 받은 후 이 검출기에 넣어 키포인트와 그에 대응되는 크기가 128인(원소가 128개인) 디스크립터를 추출해냅니다.
여기서 키포인트에 대해 알아보자면,
이미지 위에 윈도우(커널)을 correlation하여 움직이면서 피라미드 형태로 이미지를 재구성하는데, 이미지의 밝기에 따른 히스토그램을 떠올리면 될 것 같습니다. 이때 각 스케일에서 가우시안 미분을 이용하여 극값을 찾음으로써 keypoint를 찾고 그에 일대일대응되는 디스크립터를 찾습니다. 키포인트는 다음과 같이 추출하게 됩니다.
하나의 특징점을 찾으면 아래와 같이 클래스 하나를 얻게 되는 것입니다.
<class 'cv2.KeyPoint'>
그 다음으로 디스크립터에 대해 알아보자면,
각 스케일에서 미분을 통해 극값을 찾으면 keypoint를 추출할 수 있다고 했는데, 이때 각 키포인트 주변의 영역을 16x16 크기의 영역으로 나누고, 그 영역 내에 이미지의 기울기 방향 히스토그램을 계산합니다.(링크참고)
그리고 그 히스토그램 영역들을 하나로 이어붙여 하나의 거대한 히스토그램 집합을 만들어낼 수가 있습니다.
이 거대한 히스토그램을 128차원의 벡터로 표현하면 다음과 같이 크기가 128인 넘파이 배열이 생성됩니다.
이렇게 키포인트들을 두 이미지에서 추출해 냈다면 무작위로 비교하는 과정을 거쳐 유사도를 따져야 합니다. 여기서는 128차원의 모든 벡터들을 일일이 매칭하는 작업을 해주기 위해서 Brute-Force매처를 사용합니다.
아래 그림에서 왼쪽은 특징이 되는 점의 좌표와 주변정보를 고려하여 선정한 그 특징점의 좌표를 저장하고 있는 키포인트이고, 오른쪽은 각 키포인트를 128차원의 벡터로 표현했을때, 그 128개의 원소벡터로 이루어진 넘파이 배열 꼴의 디스크립터입니다.
sift-descriptor 알고리즘에서는 특징점에 대해서 일일이 키포인트와 디스크립터를 추출하고 이것들에 대해서 무작위 유사도를 따지는 작업을 거치게 됩니다.
그 결과로 두 이미지의 매칭되는 특징점의 인덱스와 Euclidean 거리를 추출해냅니다.
(물론 사진의 특성에 따라 Hamming 거리를 추출해 낼수도 있으나, 여기서는 일반적으로 쓰이는 유클리디안 거리를 추출해 냈습니다)
이를 통해 가능한 모든 경우의 수에 대해서 매칭을 진행하므로 정확도가 높을 수 있으나, 모든 경우의 수에 대해서 진행을 하는 만큼 연산량이 많아 컴퓨터 성능에 따라 결과가 나오는데 오랜 시간이 걸릴 수 있습니다. 특히 resolution이 높을 수록 그 연산량의 기하급수적으로 늘어나므로, 상대적으로 해상도가 낮은 이미지들에 대해서 sift-descriptor를 적용하기 좋습니다.
위 설명을 기반으로 직접 코드로 구현해보도록 하겠습니다.
코드와 그 결과
코드>
import cv2
import matplotlib.pyplot as plt
# 이미지를 불러줍니다.
gray1 = cv2.imread('./../../assets/images/donkeybro.jpg', cv2.COLOR_BGR2GRAY)
gray2 = cv2.imread('./../../assets/images/donkeybro2.jpg', cv2.COLOR_BGR2GRAY)
# SIFT 검출기 생성
sift = cv2.SIFT_create()
# 첫번째 이미지에서 SIFT 키포인트와 디스크립터를 검출합니다.
kp1, des1 = sift.detectAndCompute(gray1, None)
# 두번째 이미지에서 SIFT 키포인트와 디스크립터를 검출합니다.
kp2, des2 = sift.detectAndCompute(gray2 , None)
# 키포인트 매칭을 위한 Brute-Force 매처 생성
bf = cv2.BFMatcher(cv2.NORM_L2, crossCheck=True)
# 두 이미지 간의 디스크립터를 매칭합니다.
matches = bf.match(des1, des2)
# 매칭 결과를 매칭 거리(작을수록 더 좋은 매칭)로 정렬합니다.
matches = sorted(matches, key=lambda x: x.distance)
# 매칭된 결과를 이미지로 그립니다.
img_matches = cv2.drawMatches(gray1, kp1, gray2, kp2, matches[:50], None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
# 결과를 시각화합니다.
plt.figure(figsize=(12,6))
plt.imshow(img_matches)
plt.title('SIFT Keypoint Matching')
plt.axis('off')
plt.show()
결과>
참고링크:
링크(1): OpenCV - 27. 특징 디스크립터 검출기 (SIFT, SURF, ORB)
링크(2): Introduction to SIFT (Scale-Invariant Feature Transform)
이것으로 이번 포스팅을 마치겠습니다! 읽어주셔서 감사합니다!

'Computer VIsion > 입문(이론편)' 카테고리의 다른 글
특징점(KeyPoint) 검출에 효과적인 Harris-Corner Detector에 대해 알아보자! (0) | 2024.09.17 |
---|---|
Invariance(불변성) VS Discriminability(변별성) (0) | 2024.09.02 |
인스타의 핵심인 이미지 필터링에 대해 알아보자! (1) | 2024.08.27 |
이미지는 어떻게 디지털화되는가? (0) | 2024.08.23 |
댓글