Feature Detection & Matching
์ปดํจํฐ๋น์ ๋ถ์ผ์์์ feature matching์ ๋ ๊ฐ ์ด์์ ์ด๋ฏธ์ง์์ ๊ณตํต์ ์ผ๋ก ๋ํ๋๋ ํน์ง์ ์ฐพ์๋ด๋ ๊ธฐ์ ์ด๋ค. ์ด๋ฅผ ํตํด ์ด๋ฏธ์ง ๊ฐ์ ๋์์ ์ ์ฐพ๊ฑฐ๋, ์ด๋ฏธ์ง ๊ฐ์ ๋ณํ๋ฅผ ์ถ์ ํ๋ ๋ฑ ๋ค์ํ ๋ถ์ผ์ ํ์ฉํ ์ ์๋ค.
์ด๋ฌํ Feature matching์ ๊ณผ์ ์ ํฌ๊ฒ ๋ ๊ฐ์ง ๋จ๊ณ๋ก ๋๋๋ค. ๋จผ์ ๊ฐ ์ด๋ฏธ์ง์์ ํน์ง์ ๊ฒ์ถํ๊ณ , ์ด๋ฅผ ์ด์ฉํ์ฌ ๊ฐ ํน์ง๋ค์ด ์ด๋ป๊ฒ ๋งค์นญ๋๋์ง ์ฐพ์๋ด๋ ๊ฒ์ด๋ค.
1. Feature Detection (ํน์ง ๊ฒ์ถ)
์ด๋ฏธ์ง์์ ํน์ง์ ๊ฒ์ถํ๊ธฐ ์ํด์๋ ์ด๋ฏธ์ง ๋ด์ ํน์ดํ ํจํด์ด๋ ๊ตฌ์กฐ๋ฅผ ์ฐพ์๋ด๋ ๊ฒ์ด ์ค์ํ๋ค. ์ด๋ฅผ ์ํด SIFT, SURF, ORB ๋ฑ์ ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ๋๋ฐ ์ด ์๊ณ ๋ฆฌ์ฆ๋ค์ ์ด๋ฏธ์ง์์ ํน์ง์ ์ ๊ฒ์ถํ๊ณ , ์ด๋ค ์ฃผ๋ณ์์ ์ง์ญ์ ์ธ ํจํด์ ์ถ์ถํ์ฌ ํน์ง ๋์คํฌ๋ฆฝํฐ๋ฅผ ์์ฑํ๋ค.
2. Feature Matching (ํน์ง ๋งค์นญ)
๋ ์ด๋ฏธ์ง์์ ์ถ์ถํ ํน์ง ๋์คํฌ๋ฆฝํฐ (Feature Descriptor)๋ฅผ ๋น๊ตํ์ฌ ๋์๋๋ ํน์ง์ ์์ ์ฐพ์๋ด๋ ๊ฒ์ด ํน์ง ๋งค์นญ์ ํต์ฌ์ด๋ค. ๋งค์นญ ๋ฐฉ๋ฒ์๋ ์ฃผ๋ก ์ต๊ทผ์ ์ด์(Nearest Neighbor) ๋งค์นญ๊ณผ RANSAC(Random Sample Consensus)์ ์ด์ฉํ ๋งค์นญ์ด ์ฌ์ฉ๋๋ค.
- Nearest Neighbor Matching (์ต๊ทผ์ ์ด์ ๋งค์นญ): ํน์ง ๋์คํฌ๋ฆฝํฐ ๊ฐ์ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ์ฐํ์ฌ ๊ฐ์ฅ ๊ฐ๊น์ด ํน์ง ๋์คํฌ๋ฆฝํฐ๋ฅผ ๋งค์นญ์ํค๋ ๋ฐฉ๋ฒ์ด๋ค. ์ด๋, ์ด์๊ณผ์ ๊ฑฐ๋ฆฌ๊ฐ ์ผ์ ์ด์ ์ฐจ์ด๋๋ฉด ๋งค์นญ์ ๊ฑฐ๋ถํ๋ ๋ฐฉ์์ผ๋ก ๊ฑฐ๋ฆฌ ์๊ณ๊ฐ์ ์ค์ ํ ์ ์๋ค.
- RANSAC : ์ผ๋ถ ์๋ชป๋ ๋งค์นญ์ ๊ฑธ๋ฌ๋ด๊ธฐ ์ํ ๋ฐฉ๋ฒ์ผ๋ก, ์ผ์ ๊ฐ์์ ๋งค์นญ์ ๋ฌด์์๋ก ์ ํํ์ฌ ์ด๋ฅผ ์ด์ฉํ์ฌ ๋ชจ๋ธ์ ์์ฑํ๊ณ , ์ด ๋ชจ๋ธ์ ์ด์ฉํ์ฌ ๋๋จธ์ง ๋งค์นญ๋ค์ ๊ฒ์ฆํ๊ณ ์ ํ๋๊ฐ ๋์ ๋งค์นญ๋ค๋ง์ ์ ํํ๋ค.
Feature matching์ ์ด๋ฏธ์ง ๊ฒ์, ๊ฐ์ฒด ์ธ์, ์์ ๋ถ๋ฅ ๋ฑ์ ๋ถ์ผ์์ ํ์ฉ๋์ด ์๊ณ , ํนํ multi-view stereo ์ฒ๋ผ ์ฌ๋ฌ ์ฅ์ ์๋ก ๋ค๋ฅธ ์์ ์ ์ด๋ฏธ์ง๋ค ์์์ ๋์์ ์ ์ฐพ๊ธฐ ์ํด ์์ฃผ ์ฌ์ฉ๋๋ค.
OpenCV Feature Detection
OpenCV์์ ์ ๊ณตํ๋ ๋ํ์ ์ธ feature detection ์๊ณ ๋ฆฌ์ฆ์ผ๋ก๋ SIFT, SURF, ORB, FAST, BRISK, AKAZE ๋ฑ์ด ์๋ค. ๊ฐ์ฅ ์ฑ๋ฅ์ด ์ข์ ์๊ณ ๋ฆฌ์ฆ์ ์ด๋ฏธ์ง์ ์ฌ๋ฌ ํน์ฑ (ํฌ๊ธฐ, ๋ฐฉํฅ์ฑ, ๋
ธ์ด์ฆ, ํจํด์ ๋ณต์ก์ฑ,...)์ ๋ฐ๋ผ ๋ค๋ฅผ ์ ์๋ค. ์ผ๋ฐ์ ์ผ๋ก๋ ํน์ง์ด ๋จ์ํ ๊ฒฝ์ฐ์๋ FAST๋ BRISK ๊ฐ์ ์๊ณ ๋ฆฌ์ฆ์ด ์ ํฉํ๊ณ ์ด๋ฏธ์ง๊ฐ ํฌ๊ฑฐ๋ ํน์ง์ด ๋ณต์กํ ๊ฒฝ์ฐ SIFT, SURF, AKAZE์ ๊ฐ์ ์๊ณ ๋ฆฌ์ฆ์ด ์ ํฉํ๋ค.
- SIFT(Scale-Invariant Feature Transform)
- ์ด๋ฏธ์ง์ ํฌ๊ธฐ์ ํ์ ์ ๋ํ ๋ถ๋ณ์ฑ์ ๊ฐ์ง๋ ํน์ง์ ๊ฒ์ถ ์๊ณ ๋ฆฌ์ฆ
- ์ด๋ฏธ์ง์ ํฌ๊ธฐ์ ํ์ ์ ๊ฐํ ํน์ง์ ๊ฐ์ง๊ณ ์์ด ๋๋ถ๋ถ์ ์ํฉ์์ ์์ ์ ์ธ ์ฑ๋ฅ์ ๋ณด์ฌ์ค
- ํ์ง๋ง SIFT๋ ์ปดํจํ ๋ฆฌ์์ค๊ฐ ๋ง์ด ์์
- SURF(Speeded-Up Robust Features)
- SIFT์์ ํน์ง์ ๊ฒ์ถ ๋ฐ ๊ธฐ์ ์ ๊ณ์ฐ ์๋๋ฅผ ๊ฐ์ ํ ์๊ณ ๋ฆฌ์ฆ
- SIFT์ ๋น์ทํ ์ฑ๋ฅ์ ๊ฐ์ง๋ฉด์๋ ๋ ๋น ๋ฅธ ์๋๋ฅผ ์ ๊ณต
- ORB(Oriented FAST and Rotated BRIEF)
- FAST์ BRIEF ์๊ณ ๋ฆฌ์ฆ์ ๊ฒฐํฉํ ๋ฐฉ์์ผ๋ก, SIFT์ SURF ๋๋น ์๋๊ฐ ๋งค์ฐ ๋น ๋ฅด๋ฉด์๋ ์ข์ ์ฑ๋ฅ์ ๊ฐ์ง ์๊ณ ๋ฆฌ์ฆ
- ์ด๋ฏธ์ง๋ฅผ ํ์ ํ๊ฑฐ๋ ํฌ๊ธฐ๋ฅผ ๋ฐ๊ฟจ์ ๋ ํน์ง์ ๊ฒ์ถ ๊ฒฐ๊ณผ๊ฐ ๋ฐ๋์ง ์๋ ํน์ง
- ํนํ, ์๋ฒ ๋๋ ๊ธฐ๊ธฐ๋ ๋ชจ๋ฐ์ผ ์ฑ ๋ฑ์์์ ์ค์๊ฐ ์ฐ์ฐ์ ์ ํฉํ ์๊ณ ๋ฆฌ์ฆ์ด๋ผ ํญ๋๊ฒ ์ฌ์ฉ
- FAST(Features from Accelerated Segment Test)
- ์ด๋ฏธ์ง์์ ๋น ๋ฅด๊ฒ ํน์ง์ ์ ๊ฒ์ถํ๋ ์๊ณ ๋ฆฌ์ฆ
- ๋จ์ํ๊ณ ๋น ๋ฅธ ์๊ณ ๋ฆฌ์ฆ์ด๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ์๊ณ ๋ฆฌ์ฆ๋ค๊ณผ ํจ๊ป ์ฌ์ฉ๋์ด, ์ด๊ธฐ ํน์ง์ ๊ฒ์ถ ํ, ๋ณด๋ค ์ ํํ ๊ฒ์ถ ์๊ณ ๋ฆฌ์ฆ(์: BRIEF, SIFT, SURF)์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์
- BRISK( Binary Robust Invariant Scalable Keypoints)
- 2011๋ ์ ๋ ผ๋ฌธ์ผ๋ก ๋ฐํ๋ ์๊ณ ๋ฆฌ์ฆ์ผ๋ก, ์ด๋ฏธ์ง์์ ํน์ง์ ์ ๊ฒ์ถํ๊ธฐ ์ํด ์ค์ผ์ผ ๊ณต๊ฐ(scale space)์์ ํน์ ํ ํฌ๊ธฐ์ ํจ์น(patch)๋ฅผ ์์ฑํ๊ณ , ํจ์น ๋ด์ intensity difference๋ฅผ ์ด์ฉํ์ฌ FAST ๋ฐฉ๋ฒ์ผ๋ก ํน์ง์ ์ ๊ฒ์ถ
- ํน์ง์ ์ ๋ฌ์ฌํ๊ธฐ ์ํด intensity difference ๊ฐ๋ค์ ๋ถํฌ๋ฅผ ์ด์ฉํ์ฌ ํน์ง์ ์ ๊ธฐ์ ํ๋ binary descriptor๋ฅผ ์ฌ์ฉ
- AKAZE(Accelerated-KAZE)
- 2013๋ ์ ๋ฐํ๋ ์๊ณ ๋ฆฌ์ฆ์ผ๋ก, ์ด๋ฏธ์ง์์ ํน์ง์ ์ ๊ฒ์ถํ๊ธฐ ์ํด KAZE ๋ฐฉ๋ฒ์ ์ฌ์ฉ
- KAZE๋ ์ด๋ฏธ์ง์ ์ค์ผ์ผ ๊ณต๊ฐ์์ multiscale ๋ฐฉ์์ผ๋ก ์ด๋ฏธ์ง๋ฅผ ๋ธ๋ฌ๋งํ๊ณ , Difference of Gaussian(DoG)๋ฅผ ์ฌ์ฉํ์ฌ scale-space ๋ด์์ ๊ทนํ ๊ฐ(extrema)์ ์ฐพ์. ์ดํ, extrema์์ Hessian matrix๋ฅผ ์ด์ฉํ์ฌ ํน์ง์ ์ ๊ฒ์ถ
- AKAZE๋ KAZE์ ๋ณํ์ผ๋ก, ์ด๋ฅผ ๋ ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌํ ์ ์๋๋ก ์ค๊ณ
- AKAZE์์๋ scale-space ๋ด์์ ํน์ง์ ์ ๊ฒ์ถํ ์ดํ, ๊ทธ ํน์ง์ ์ ๋ํ ๋ฐฉํฅ(direction)์ ์ถ์ถํ์ฌ, ๊ทธ ๋ฐฉํฅ์ ๋ฐ๋ผ ํน์ง์ ์ ๋ฌ์ฌ(descriptor)๋ฅผ ์์ฑ
OpenCV Feature Matching
OpenCV์์ feature matching์ ํ๊ธฐ ์ํด์๋ ๋ํ์ ์ผ๋ก SIFT, SURF, ORB ๋ฑ์ feature detection ๋ฐ detection ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ๊ณ , ์ดํ feature matching ์๊ณ ๋ฆฌ์ฆ์ผ๋ก๋ ๋ํ์ ์ผ๋ก brute-force matching, FLANN matching ๋ฑ์ ์ฌ์ฉํ ์ ์๋ค.
- cv2.BFMatcher()
- ๋ธ๋ฃจํธ ํฌ์ค ๋งค์นญ(brute-force matching) ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ์ฌ ํน์ง ๋งค์นญ์ ์ํ
- ํนํ ์์ ๋ฐ์ดํฐ ์ ์์ ๋์ ์ ํ๋๋ฅผ ๋ณด์ด๋ฉฐ, ๋๋ถ๋ถ์ ๊ฒฝ์ฐ์๋ cv2.FlannBasedMatcher() ๋ณด๋ค ๋๋ฆฌ์ง๋ง ๋ ๋์ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์
- cv2.FlannBasedMatcher()
- ๋น ๋ฅธ Nearest Neighbor ๊ฒ์์ ์ํ ํน์ง ๋งค์นญ ์๊ณ ๋ฆฌ์ฆ ์ค ํ๋
- FLANN (Fast Library for Approximate Nearest Neighbors) ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ์ฌ ํน์ง ๋์คํฌ๋ฆฝํฐ ์ฌ์ด์ ๊ฐ์ฅ ๊ฐ๊น์ด ์ด์(Nearest Neighbor)์ ์ฐพ์
- ํน์ง ๊ธฐ์ ์๋ค์ ์งํฉ์์ k-NN(k-Nearest Neighbor)์ ๊ฒ์ํ์ฌ ๋ ์ด๋ฏธ์ง์์ ์ ์ฌํ ํน์ง์ ๋งค์นญ. ๊ธฐ๋ณธ์ ์ผ๋ก, cv2.FlannBasedMatcher๋ k=2๋ก ์ค์
- index_params: Flann ๊ธฐ๋ฐ ๋งค์นญ์์ ์ธ๋ฑ์ค๋ฅผ ์ค์ ํ๋ ํ๋ผ๋ฏธํฐ. ๋์
๋๋ฆฌ ํํ๋ก ์ ๋ฌ๋๋ฉฐ, algorithm๊ณผ trees ๋ ๊ฐ์ง ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ์ง
- algorithm : ๋งค์นญ์ ์ฌ์ฉํ ์๊ณ ๋ฆฌ์ฆ์ ๋ํ๋ด๋ฉฐ, ๋ณดํต์ cv2.flann.IndexParams ํด๋์ค์ ์์ ์ค ํ๋๋ฅผ ์ฌ์ฉ
- trees : ๋งค์นญ์ ์ฌ์ฉ๋ ํธ๋ฆฌ์ ๊ฐ์
- search_params: ๋งค์นญ์ ์ํํ ๋ ๊ฒ์ํ ์ด์์ ๊ฐ์์ ๊ฒ์ํ ์ต๋ ๊ฑฐ๋ฆฌ๋ฅผ ์ค์ ํ๋ ํ๋ผ๋ฏธํฐ. ๋์
๋๋ฆฌ ํํ๋ก ์ ๋ฌ๋๋ฉฐ, checks์ eps ๋ ๊ฐ์ง ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ์ง
- checks : ๊ฒ์ํ ์ด์์ ๊ฐ์
- eps : ๊ฒ์ํ ์ต๋ ๊ฑฐ๋ฆฌ
- index_params: Flann ๊ธฐ๋ฐ ๋งค์นญ์์ ์ธ๋ฑ์ค๋ฅผ ์ค์ ํ๋ ํ๋ผ๋ฏธํฐ. ๋์
๋๋ฆฌ ํํ๋ก ์ ๋ฌ๋๋ฉฐ, algorithm๊ณผ trees ๋ ๊ฐ์ง ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ์ง
Feature Detection & Matching ์์
- ๊ธ๋ฆฌ์ฝ์ ์ด๋ฏธ์ง์ ๋ํค๋ณด๋ฆฌ ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง ๊ฐ feature matching
- Feature Detection ์๊ณ ๋ฆฌ์ฆ์ SIFT, ORB, BRISK, AKAZE ์ฌ์ฉ
- Matching ์๊ณ ๋ฆฌ์ฆ์ brute-force์ flann-based ๋ฐฉ๋ฒ ์ค ์ ํ
- ๋งค์นญ ๊ฒฐ๊ณผ ์๊ฐํ
- ๋งค์นญ ํฌ์ธํธ (ํฝ์ ์์น) ์ถ๋ ฅ
import cv2
# ์ด๋ฏธ์ง ์ฝ๊ธฐ
img1 = cv2.imread('๊ธ๋ฆฌ์ฝ์.png')
img2 = cv2.imread('๊ธ๋ฆฌ์ฝ์1.jpg')
using_FAST = False
feature_detector = dict(SIFT = cv2.SIFT_create(),
ORB = cv2.ORB_create(),
brisk = cv2.BRISK_create(),
akaze = cv2.AKAZE_create())
matching_type = 'flann' # 'bruteforce' or 'flann'
for type in list(feature_detector.keys()):
# SIFT ์์ฑ
detector = feature_detector[type]
# ๊ฐ ์ด๋ฏธ์ง์์ ํคํฌ์ธํธ ๋ฐ ๋์คํฌ๋ฆฝํฐ ๊ณ์ฐ
if using_FAST == True :
fast = cv2.FastFeatureDetector_create()
kp1 = fast.detect(img1, None)
kp2 = fast.detect(img2, None)
kp1, des1 = detector.compute(img1, kp1)
kp2, des2 = detector.compute(img2, kp2)
else:
kp1, des1 = detector.detectAndCompute(img1, None)
kp2, des2 = detector.detectAndCompute(img2, None)
# ๋งค์นญ ์๊ณ ๋ฆฌ์ฆ ์ ์
if matching_type == 'bruteforce':
matcher = cv2.BFMatcher()
elif matching_type == 'flann':
if type == 'SIFT' or type == 'FAST':
# SIFT ํ๋ผ๋ฏธํฐ
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
elif type == 'ORB' :
# ORB ํ๋ผ๋ฏธํฐ
FLANN_INDEX_LSH = 6
index_params= dict(algorithm = FLANN_INDEX_LSH,
table_number = 6,
key_size = 12,
multi_probe_level = 1)
search_params=dict(checks=32)
matcher = cv2.FlannBasedMatcher(index_params, search_params)
# ๋งค์นญ ๋ฐ ๋งค์นญ๋ ํฌ์ธํธ ๊ฑฐ๋ฆฌ ์ ๋ ฌ
matches = matcher.match(des1, des2)
matches = sorted(matches, key=lambda x:x.distance)
good_matches = matches[:30]
# ๋งค์นญ ๊ฒฐ๊ณผ ์๊ฐํ
img3 = cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None, flags=2)
cv2.imshow("Matching result", img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
# ๋งค์นญ๋ ์ง์ ์ ํฝ์
์์น ์ถ์ถ
matched_points1 = []
matched_points2 = []
for match in good_matches:
# queryIdx: ์ฒซ ๋ฒ์งธ ์ด๋ฏธ์ง์์์ ํคํฌ์ธํธ ์ธ๋ฑ์ค
# trainIdx: ๋ ๋ฒ์งธ ์ด๋ฏธ์ง์์์ ํคํฌ์ธํธ ์ธ๋ฑ์ค
pt1 = kp1[match.queryIdx].pt
pt2 = kp2[match.trainIdx].pt
matched_points1.append((int(pt1[0]), int(pt1[1])))
matched_points2.append((int(pt2[0]), int(pt2[1])))
# ํฝ์
์์น ์ถ๋ ฅ
print("Matched Points in Image1:", matched_points1)
print("Matched Points in Image2:", matched_points2)
๊ฒฐ๊ณผ
# cv2.drawMatches ํจ์๋ฅผ ์ด์ฉํ ๋งค์นญํฌ์ธํธ ์๊ฐํ
- ์ฒซ ๋ฒ์งธ ์ด๋ฏธ์ง์ ๋ ๋ฒ์งธ ์ด๋ฏธ์ง์์ ์ถ์ถ๋ ํน์ง์ ๋ค ์ค์์ ๋งค์นญ๋ ํฌ์ธํธ๋ค์ ์ด์ด์ ์๊ฐํ
- ๊ฐ์ฒด์ ์๊ฐ์ ํน์ง์ ๋ฐ๋ผ ํน์ง์ ์ด ์ ์ถ์ถ๋๋ feature detection ๋ฐฉ๋ฒ์ด ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ์๊ฐํ ํ ์คํธ๊ฐ ์ค์
# ๋งค์นญ ํฌ์ธํธ ์ถ๋ ฅ
- ๋งค์นญ ํฌ์ธํธ์ ํฝ์ ์์น๋ฅผ ์๊ณ ์ถ์ ๊ฒฝ์ฐ ์ถ์ถ ๊ฐ๋ฅ
- ์ ์์์ ๊ฒฝ์ฐ ๋ ๋ฒ์งธ ์ด๋ฏธ์ง์์ ๊ธ๋ฆฌ์ฝ์์ด ์กด์ฌํ๋ ์์น ํ์ ๊ฐ๋ฅ
- ์ ํ๋ ํ๊ฒฝ์์ ๊ฐ์ฒด ๊ฒ์ถ์ด ๊ฐ๋ฅ
Matched Points in Image1: [(111, 131), (211, 128), (114, 130), (159, 266), (156, 271), (218, 128), (220, 128), (216, 114), (119, 131), (122, 143), (119, 131), (129, 209), (212, 131), (143, 120), (125, 207), (212, 127), (215, 114), (179, 322), (114, 114), (161, 267), (120, 130), (121, 141), (173, 229), (71, 93), (130, 209), (156, 79), (199, 211), (118, 150), (171, 229), (183, 75)]
Matched Points in Image2: [(73, 234), (115, 247), (73, 234), (92, 315), (92, 315), (119, 248), (119, 249), (268, 385), (76, 234), (78, 243), (76, 234), (81, 280), (115, 247), (87, 232), (79, 279), (91, 297), (269, 387), (104, 372), (73, 234), (93, 316), (77, 234), (78, 242), (97, 298), (92, 296), (81, 280), (94, 214), (109, 290), (734, 548), (98, 297), (248, 176)]