본문 바로가기
OpenCV/OpenCV-Chapter

CH09 OpenCV-Python 특징점 매칭(feature point matching)

by kiimy 2021. 12. 25.
728x90

특징점 매칭 (feature point matching)

• 두 영상에서 추출한 특징점 기술자를 비교하여 서로 유사한 기술자를 찾는 작업

 

특징 벡터 유사도 측정 방법

실수 특징 벡터: L2 노름(L2 norm) 사용

이진 특징 벡터: 해밍 거리(hamming distance) 사용

 

OpenCV 특징점 매칭 클래스

BF: Brute-force (전수 조사)

== 각 특징점 하나씩 다 매칭해보면서 가장 distance가 작은 것을 골라낸다

 

Flann: Fast Library for Approximate Nearest Neighbor (K-D Tree 사용)

== 무 특징점이 많을 때 사용

==> 완전히 최소값 매칭이 어려워(속도는 빠름) but 특징점이 몇천개 정도면 별차이 없음
==> 영상 사이즈가 너무 커져서 너무느리다 싶으면 사용

좋은 매칭 선별 방법 1

• 가장 좋은 매칭 결과에서 distance 값이 작은 것 N개를 사용

• cv2.DMatch.distance 값을 기준으로 정렬 후 상위 N개 선택

# 영상 불러오기
src1 = cv2.imread('ch09\\images\\graf1.png', cv2.IMREAD_GRAYSCALE)
src2 = cv2.imread('ch09\\images\\graf3.png', cv2.IMREAD_GRAYSCALE)

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

feature = cv2.KAZE_create()
# feature = cv2.AKAZE_create()
# feature = cv2.ORB_create()

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

# 특징점 매칭
matcher = cv2.BFMatcher_create()
'''
+ BFMatcher_create :  Brute-force (전수 조사)
+ cv2.FlannBasedMatcher : 특징점이 너무 많을 시 사용(근사화)

# 실수 기술자 KAZE(default = cv2.NORM_L2)
# 이진 기술자 AKAZE, ORB(=cv2.NORM_HAMMING)
'''
matches = matcher.match(desc1, desc2)
'''
cv2.DescriptorMatcher.match(queryDescriptors, trainDescriptors, mask=None)
                            -> matches

• queryDescriptors: (기준 영상 특징점) 질의 기술자 행렬
• trainDescriptors: (대상 영상 특징점) 학습 기술자 행렬
• mask: 매칭 진행 여부를 지정하는 행렬 마스크.
• matches: 매칭 결과. cv2.DMatch 객체의 리스트.

*멤버함수*
+ queryIdx : 기준 영상 특징점(src1)
+ trainIdx : 대상 영상 특징점(src2)
+ imgIdx : 매칭할 영상(src2)를 여러장 줄 수 있는데 그중 가장 비슷한 것을 찾는 형태로 동작
+ distance
'''
# 좋은 매칭 결과 선별
# 값이 작은것이 먼저오고 큰 값이 뒤로
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)) # matching 개수는 kp1
print('# of good_matches:', len(good_matches)) # 80개

# 특징점 매칭 결과 영상 생성
dst = cv2.drawMatches(src1, kp1, src2, kp2, matches, None)
dst1 = cv2.drawMatches(src1, kp1, src2, kp2, good_matches, None)

cv2.namedWindow('dst', cv2.WINDOW_NORMAL)
cv2.imshow('dst', dst)
cv2.imshow('dst1', dst1)
cv2.waitKey()
cv2.drawMatches(img1, keypoints1, img2, keypoints2, matches1to2, outImg,
                matchColor=None, singlePointColor=None, matchesMask=None, 
                flags=None) -> outImg

• img1, keypoints1: 기준 영상과 기준 영상에서 추출한 특징점 정보
• img2, keypoints2: 대상 영상과 대상 영상에서 추출한 특징점 정보
• matches1to2: 매칭 정보. cv2.DMatch의 리스트.
• outImg: 출력 영상
• matchColor: 매칭된 특징점과 직선 색상
• singlePointColor: 매칭되지 않은 특징점 색상
• matchesMask: 매칭 정보를 선택하여 그릴 때 사용할 마스크
• flags: 매칭 정보 그리기 방법. 
기본값은 cv2.DRAW_MATCHES_FLAGS_DEFAULT.

거리가 작은 값 80개만 선택

좋은 매칭 선별 방법 2

• 가장 좋은 매칭 결과의 distance 값과 두 번째로 좋은 매칭 결과의 distance 값의 비율을 계산

# 영상 불러오기
src1 = cv2.imread('ch09\\images\\graf1.png', cv2.IMREAD_GRAYSCALE)
src2 = cv2.imread('ch09\\images\\graf3.png', cv2.IMREAD_GRAYSCALE)

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

feature = cv2.KAZE_create()
# feature = cv2.AKAZE_create()
# feature = cv2.ORB_create()

kp1, desc1 = feature.detectAndCompute(src1, None)
kp2, desc2 = feature.detectAndCompute(src2, None)

matcher = cv2.BFMatcher_create()
# 이진 기술자의 경우
# matcher = cv2.BFMatcher_create(cv2.NORM_HAMMING)
matches = matcher.knnMatch(desc1, desc2, 2) # k: 매칭할 근접 이웃 개수
# [[DMatch], [DMatch]] 형태

'''
가장 좋은 매칭 결과의 distance 값과 두 번째로 좋은 매칭 결과의 
distance 값의 비율을 계산
'''
good_matches = []
for m in matches: # len(matches) = 3159
    # [0] 값이 작고 / [1] 값이 큼
    # (<DMatch 0000025608092BF0>, <DMatch 0000025608092C10>) ....
    if m[0].distance / m[1].distance < 0.7: # 비율이 임계값(e.g. 0.7)보다 작으면 선택
        good_matches.append(m[0])

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

# 잘못된 매칭이 간혹 보이는데 호모그래피 사용하여 보완
dst = cv2.drawMatches(src1, kp1, src2, kp2, good_matches, None)

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

len(good_matches) = 384

 

728x90

댓글