logo

이미지 매칭

이미지 매칭 Image Matching

  • 서로 다른 두 이미지를 비교해서 짝이 맞는 같은 형태의 객체가 있는지 찾아내는 기술
  • 키포인트 (Keypoint): 이미지 내에서 특징적인 위치를 나타내는 점
  • 디스크립터 (Descriptor): 키포인트 주변의 지역적인 이미지 정보를 숫자 벡터로 표현한 것.
  • 이 벡터는 해당 키포인트의 "지문" 역할
  • 다른 이미지에서 유사한 특징점을 찾을 때 비교에 사용
  • ORB (Oriented FAST and Rotated BRIEF) 특징점 검출기:
    • FAST 알고리즘을 이용해 키포인트 검출
    • BRIEF 알고리즘을 이용해 디스크립터 생성
    • Oriented & Rotated: 키포인트의 방향성과 디스크립터의 회전 개념을 추가

FAST

  • Features from Accelerated Segment Test
  • 후보점 p 주변의 원을 이루는 픽셀 16개의 밝기를 봄
  • 만약 연속된 9개의 픽셀이 모두 중심보다 충분히 밝거나, 모두 중심보다 충분히 어두우면 코너로 판정
  • Oriented FAST: FAST + 코너 방향

FAST 이미지 1

BRIEF

  • Binary Robust Independent Elementary Features
  • 특징점 주변의 작은 패치를 가져옴
  • 미리 정해진 픽셀 쌍들을 패치 안에서 뽑아 각 쌍에 대해 밝기 비교
  • 비교 결과를 0/1 비트열로 저장
  • 두 개의 특징점을 비교할 때는 비트열에서 차이를 구함

특징 디스크립터 Feature Descriptor

import cv2 as cv
import numpy as np

img1 = cv.imread('stop_sign1.jpg') # 비교할 이미지 로드
img1 = cv.resize(img1, (250, 250)) # 이미지 크기 조정
img2 = cv.imread('stop_sign2.jpg')
gray1 = cv.cvtColor(img1, cv.COLOR_BGR2GRAY) # 특징점 검출을 위해 흑백으로
gray2 = cv.cvtColor(img2, cv.COLOR_BGR2GRAY)

# ORB(Oriented FAST and Rotated BRIEF) 특징점 검출기
detector = cv.ORB_create()  # 검출기 생성
kp1, desc1 = detector.detectAndCompute(gray1, None)  # 각 이미지의 키포인트(kp)와
kp2, desc2 = detector.detectAndCompute(gray2, None)  # 디스크립터(desc)를 추출

특징 매칭 Feature Matching

# 2개의 그림에서 특징들이 서로 일치하는 부분을 매칭
matcher = cv.BFMatcher(cv.NORM_HAMMING, crossCheck=True)
matches = matcher.match(desc1, desc2)

# 매칭된 결과를 거리의 오름차순으로 정렬(가까울수록 좋은 매칭)
matches = sorted(matches, key=lambda x: x.distance)

# 결과 시각화
res1 = cv.drawMatches(img1, kp1, img2, kp2, matches, None)
show(res1)

특징 매칭 Feature Matching 이미지 1

호모그래피 Homography

  • 두 장의 사진이 다른 방향과 각도에서 촬영한 것이므로 완전히 일치하지는 않음
  • 투시 변환을 통해서 방향과 각도를 맞출 수 있음(호모그래피 ≒ 투시 변환)
# 두 이미지에서 매칭된 점의 좌표들을 추출
src_pts = np.float32([ kp1[m.queryIdx].pt for m in matches ])
dst_pts = np.float32([ kp2[m.trainIdx].pt for m in matches ])

# RANSAC 알고리즘으로 두 좌표들을 최대한 맞출 수 있는 변환 행렬을 계산
# (임계값 5.0 이하의 점들만 포함)
mtrx, mask = cv.findHomography(src_pts, dst_pts, cv.RANSAC, 5.0)

시각화

h, w = img1.shape[:2] # 원본 이미지 크기
pts = np.float32([[[0, 0]], [[0, h - 1]], [[w - 1, h - 1]], [[w - 1, 0]]]) # 원본 이미지의 네 모서리 좌표
dst = cv.perspectiveTransform(pts, mtrx)  # img1의 네 모서리가 img2에서 어디로 변환되는지

# 시각화
polyline = img2.copy()
cv.polylines(polyline, [np.int32(dst)], True, (0, 255, 0), 3, cv.LINE_AA)
show(polyline)

시각화 이미지 1

겹쳐서 보기

warped = cv.warpPerspective(img1, mtrx, (img2.shape[1], img2.shape[0]))
alpha = 0.5
overlay = cv.addWeighted(img2, 1 - alpha, warped, alpha, 0)
show(overlay)

겹쳐서 보기 이미지 1

좋은 매칭점만 보기

matchesMask = mask.ravel().tolist() # RANSAC 알고리즘으로 걸러진 좋은 매칭점은 1
good_matches = [match for match, keep in zip(matches, matchesMask) if keep == 1] # mask가 1인 점
res2 = cv.drawMatches(
    img1, kp1, img2, kp2, good_matches, None,
    flags=cv.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
show(res2)

# 좋은 매칭점의 개수(적으면 물체가 없다고 판단할 수 있음)
len(good_matches)

좋은 매칭점만 보기 이미지 1

Previous
도형 검출