부트캠프/컴퓨터 비전

[OpenCV] 호모그래피와 매칭

례지 2022. 11. 18. 14:04
728x90
호모그래피 행렬 계산
  • 호모그래피는 투시변환이기 때문에 3X3 행렬로 표현
  • 네 개의 대응되는 점의 좌표 이동 정보가 있으면 행렬 계산 가능
  • 특징점 매칭 정보로부터 호모그래프를 구하는 경우 서로 대응되는 점 개수보다 4개보다 많기 때문에 투시 변환 시 에러가 최소가 되는 형태의 호모그래피 행렬을 구해야 합니다.
호모그래피 행렬 계산을 위해 findHomography()에서 method 설정
  • feature point가 최소 4개 이상은 되어야 함
  • cv2.findHomography(src1_pts, src2_pts, cv2.LMEDS, 3.0)
  • cv2.findHomography(src1_pts, src2_pts, cv2.RANSAC, 3.0)
  • cv2.findHomography(src1_pts, src2_pts, cv2.RHO, 3.0)
호모그래피 실습
import cv2
import numpy as np
import matplotlib.pyplot as plt
path = './'
src1_bgr = cv2.imread(path + 'book1.jpg')
src2_bgr = cv2.imread(path + 'book2.jpg')
# src1_bgr = cv2.imread(path + ‘cup1.jpg’)
# src2_bgr = cv2.imread(path + ‘cup2.jpg’)
img1 = cv2.cvtColor(src1_bgr, cv2.COLOR_BGR2GRAY) # template image
img2 = cv2.cvtColor(src2_bgr, cv2.COLOR_BGR2GRAY) # input image
# 두 영상 읽고 매칭 실습, 실제 매칭 시 그레이 영상 사용
src1_rgb = cv2.cvtColor(src1_bgr, cv2.COLOR_BGR2RGB)
src2_rgb = cv2.cvtColor(src2_bgr, cv2.COLOR_BGR2RGB)
# 1. descriptor
orb = cv2.ORB_create(nfeatures=1000) # 최대 1000개 까지만 찾아라
brisk = cv2.BRISK_create()
detector = [orb, brisk]
didx = 1 # 0:orb, 1:brisk, 숫자 바꾸며 테스트
# create > detectandcompute하면 디스크립터 생성 끝
kp1, des1 = detector[didx].detectAndCompute(img1, None)
# detect 로 키포인트 찾고, compute로 descriptor 계산
kp2, des2 = detector[didx].detectAndCompute(img2, None)
# kp: keypoint, des:descriptor
# 2. matcher
bf = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck=True) 
# Hamming distance: 두개의 서로 다른 비트가 몇 개인지? 5개 비트가 다르면 해밍 디스턴스 거리는 5
flann = cv2.FlannBasedMatcher_create() 
# crossCheck, 변환, 역변환 한 게 같은 것을 찾는 것, 보통 이렇게 사용
matcher = [bf, flann] 
# bf 모두 다 계산, 정확, 시간 오래, flann 근사 계산, 정확도 감소, 빨라
midx = 1 # 0:bf, 1:flann
try:
    matches = matcher[midx].match(des1, des2)
except: # try 문 에러나면 except 문 실행
    matches = matcher[midx].match(np.float32(des1), np.float32(des2))
# 매칭 끝
# result display
# matches 세 개 값 반환, sort, 키포인트 디스턴스 오름차순 기준 소팅
matches = sorted(matches, key = lambda x:x.distance)
print('len(matches)=', len(matches)) # 몇 개나 매칭이 되었는지?
for i, j in enumerate(matches[:10]): # 상위 10개만 데이터를 보자, 디스턴스 확인
    print(‘matches[{}]=(queryIdx:{}, trainIdx:{}, distance:{})’
          .format(i, matches[i].queryIdx,matches[i].trainIdx,matches[i].distance))
# post processing
MIN_MATCH_COUNT = 4 # 추가
# draw only good matching
minDist = matches[0].distance
good_matches = list(filter(lambda m:m.distance <= 2 * minDist, matches))
print('len(good_matches)=', len(good_matches))
if len(good_matches) < MIN_MATCH_COUNT: # 변경
    print('sorry, too small good matches')
    exit()
print(len(good_matches))
src1_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches]) 
      # 키포인트에 정보를 넣어주면 좌표를 받을 수 있다
src2_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches])
H, mask = cv2.findHomography(src1_pts, src2_pts, cv2.RANSAC, 3.0) 
      # H: 호모그래피 행렬, 임계치 3
mask_matches = mask.flatten().tolist()
# 마스크는 이미지 형태이므로 2차원을 1차원 변경. 리스트 형태, 
      # 밑에 draw_match 파라미터로 들어간다.
h, w = img1.shape # 이미지 1의 높이, 너비
pts = np.float32([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]).reshape(-1, 1, 2)
# 템플릿의 네개의 모서리. (4,1,2) 행렬곱을 위한 자리맞춤
pts2 = cv2.perspectiveTransform(pts, H)
# 입력영상에 대응되는 네 점
src2_rgb = cv2.polylines(src2_rgb, [np.int32(pts2)], True, (255,0,0), 2)
draw_params = dict(matchColor=(0,255,0), singlePointColor=None,
                   matchesMask = mask_matches, flags = 2)
# 매칭 안되었으면 그리지 말라
img5 = cv2.drawMatches(src1_rgb, kp1, src2_rgb, kp2, good_matches, None, 
                       **draw_params)
plt.figure(figsize=(120,120))
plt.imshow(img5)
plt.show()
728x90

'부트캠프 > 컴퓨터 비전' 카테고리의 다른 글

[OpenCV] 2D Convolution, MNIST, Pooling  (0) 2022.11.29
[OpenCV] HOG  (0) 2022.11.18
[OpenCV] Feature Extraction, Descriptor  (0) 2022.11.16
[OpenCV] 코너 검출 (2)  (0) 2022.11.15
[OpenCV] 코너 검출  (0) 2022.11.15