๐ ๊ณต๋ถํ๋ ์ง์ง์ํ์นด๋ ์ฒ์์ด์ง?
[v0.31]์์์ฒ๋ฆฌ_์ฌ๋ฐ๋ฅธ ๋งค์นญ์ ์ฐพ๊ธฐ ๋ณธ๋ฌธ
[v0.31]์์์ฒ๋ฆฌ_์ฌ๋ฐ๋ฅธ ๋งค์นญ์ ์ฐพ๊ธฐ
์ง์ง์ํ์นด 2022. 1. 17. 23:56220117 ์์ฑ
<๋ณธ ๋ธ๋ก๊ทธ๋ ๊ทํ์ด ์์ฌ๋์ ๋ธ๋ก๊ทธ๋ฅผ ์ฐธ๊ณ ํด์ ๊ณต๋ถํ๋ฉฐ ์์ฑํ์์ต๋๋ค>
OpenCV - 29. ์ฌ๋ฐ๋ฅธ ๋งค์นญ์ ์ฐพ๊ธฐ
์ด๋ฒ ํฌ์คํ ์ ์ด์ ํฌ์คํ ์ ํ์ ํธ์ ๋๋ค. ์ด์ ํฌ์คํ ์์๋ ํน์ง ๋งค์นญ์ ๋ํด ์์๋ดค์ต๋๋ค. ๊ทธ๋ฌ๋ ์๋ชป๋ ํน์ง ๋งค์นญ์ด ๋๋ฌด ๋ง์์ต๋๋ค. ์๋ชป๋ ํน์ง ๋งค์นญ์ ์ ์ธํ๊ณ ์ฌ๋ฐ๋ฅธ ๋งค์นญ์ ์
bkshin.tistory.com
1. ์ฌ๋ฐ๋ฅธ ๋งค์นญ์ ์ฐพ๊ธฐ
match() ํจ์
: ๋ชจ๋ ๋์คํฌ๋ฆฝํฐ๋ฅผ ํ๋ํ๋ ๋น๊ตํ์ฌ ๋งค์นญ์ ์ ์ฐพ๋๋ค
: ๊ฐ์ฅ ์์ ๊ฑฐ๋ฆฌ ๊ฐ๊ณผ ํฐ ๊ฑฐ๋ฆฌ ๊ฐ์ ์์ ๋ช ํผ์ผํธ๋ง ๊ณจ๋ผ์ ์ฌ๋ฐ๋ฅธ ๋งค์นญ์ ์ ์ฐพ์ ์ ์์
# match ํจ์๋ก๋ถํฐ ์ฌ๋ฐ๋ฅธ ๋งค์นญ์ ์ฐพ๊ธฐ
import cv2, numpy as np
img1 = cv2.imread('img/taekwonv1.jpg')
img2 = cv2.imread('img/figures.jpg')
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
# ORB๋ก ์์ ์ ์ถ์ถ ---โ
detector = cv2.ORB_create()
kp1, desc1 = detector.detectAndCompute(gray1, None)
kp2, desc2 = detector.detectAndCompute(gray2, None)
# BF-Hamming์ผ๋ก ๋งค์นญ ---โก
matcher = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = matcher.match(desc1, desc2)
# ๋งค์นญ ๊ฒฐ๊ณผ๋ฅผ ๊ฑฐ๋ฆฌ๊ธฐ์ค ์ค๋ฆ์ฐจ์์ผ๋ก ์ ๋ ฌ ---โข
matches = sorted(matches, key=lambda x:x.distance)
# ์ต์ ๊ฑฐ๋ฆฌ ๊ฐ๊ณผ ์ต๋ ๊ฑฐ๋ฆฌ ๊ฐ ํ๋ณด ---โฃ
min_dist, max_dist = matches[0].distance, matches[-1].distance
# ์ต์ ๊ฑฐ๋ฆฌ์ 15% ์ง์ ์ ์๊ณ์ ์ผ๋ก ์ค์ ---โค
ratio = 0.2
good_thresh = (max_dist - min_dist) * ratio + min_dist
# ์๊ณ์ ๋ณด๋ค ์์ ๋งค์นญ์ ๋ง ์ข์ ๋งค์นญ์ ์ผ๋ก ๋ถ๋ฅ ---โฅ
good_matches = [m for m in matches if m.distance < good_thresh]
print('matches:%d/%d, min:%.2f, max:%.2f, thresh:%.2f' \
%(len(good_matches),len(matches), min_dist, max_dist, good_thresh))
# ์ข์ ๋งค์นญ์ ๋ง ๊ทธ๋ฆฌ๊ธฐ ---โฆ
res = cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None, \
flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
# ๊ฒฐ๊ณผ ์ถ๋ ฅ
cv2.imshow('Good Match', res)
cv2.waitKey()
cv2.destroyAllWindows()
knnMatch() ํจ์
: ๋์คํฌ๋ฆฝํฐ๋น k๊ฐ์ ์ต๊ทผ์ ์ด์ ๋งค์นญ์ ์ ๊ฐ๊น์ด ์์๋๋ก ๋ฐํ
: k๊ฐ์ ์ต๊ทผ์ ์ด์ ์ค ๊ฑฐ๋ฆฌ๊ฐ ๊ฐ๊น์ด ๊ฒ์ ์ข์ ๋งค์นญ์ ์ด๊ณ ,
๊ฑฐ๋ฆฌ๊ฐ ๋จผ ๊ฒ์ ์ข์ง ์์ ๋งค์นญ์ ์ผ ๊ฐ๋ฅ์ฑ์ด ๋์
: ์ต๊ทผ์ ์ด์ ์ค ๊ฑฐ๋ฆฌ๊ฐ ๊ฐ๊น์ด ๊ฒ ์์ฃผ๋ก ๊ณจ๋ผ๋ด๋ฉด ์ข์ ๋งค์นญ์ ์ ์ฐพ์ ์ ์์
# knnMatch ํจ์๋ก๋ถํฐ ์ฌ๋ฐ๋ฅธ ๋งค์นญ์ ์ฐพ๊ธฐ
import cv2, numpy as np
img1 = cv2.imread('img/taekwonv1.jpg')
img2 = cv2.imread('img/figures.jpg')
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
# ORB๋ก ์์ ์ ์ถ์ถ ---โ
detector = cv2.ORB_create()
kp1, desc1 = detector.detectAndCompute(gray1, None)
kp2, desc2 = detector.detectAndCompute(gray2, None)
# BF-Hamming ์์ฑ ---โก
matcher = cv2.BFMatcher(cv2.NORM_HAMMING2)
# knnMatch, k=2 ---โข
matches = matcher.knnMatch(desc1, desc2, 2)
# ์ฒซ๋ฒ์ฌ ์ด์์ ๊ฑฐ๋ฆฌ๊ฐ ๋ ๋ฒ์งธ ์ด์ ๊ฑฐ๋ฆฌ์ 75% ์ด๋ด์ธ ๊ฒ๋ง ์ถ์ถ---โค
ratio = 0.75
good_matches = [first for first,second in matches \
if first.distance < second.distance * ratio]
print('matches:%d/%d' %(len(good_matches),len(matches)))
# ์ข์ ๋งค์นญ๋ง ๊ทธ๋ฆฌ๊ธฐ
res = cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None, \
flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
# ๊ฒฐ๊ณผ ์ถ๋ ฅ
cv2.imshow('Matching', res)
cv2.waitKey()
cv2.destroyAllWindows()
- ์ต๊ทผ์ ์ด์ ์ค ๊ฑฐ๋ฆฌ๊ฐ ๊ฐ๊น์ด ๊ฒ ์ค 75%๋ง ๊ณ ๋ฆ
2. ๋งค์นญ ์์ญ ์๊ทผ ๋ณํ
: ์ฌ๋ฐ๋ฅด๊ฒ ๋งค์นญ๋ ์ขํ๋ค์ ์๊ทผ ๋ณํ ํ๋ ฌ์ ๊ตฌํ๋ฉด ๋งค์นญ ๋๋ ๋ฌผ์ฒด๊ฐ ์ด๋ ์๋์ง ํ์
: ๋น๊ตํ๋ ค๋ ๋ฌผ์ฒด๊ฐ ๋ ์ฌ์ง ์์์ ์ฝ๊ฐ ํ์ ํ์ ์๋ ์๊ณ ํฌ๊ธฐ๊ฐ ์กฐ๊ธ ๋ค๋ฅผ ์๋ ์์
: ์๊ทผ ๋ณํ ํ๋ ฌ์ ๊ตฌํ๋ฉด ์ฐพ๊ณ ์ ํ๋ ๋ฌผ์ฒด์ ์์น๋ฅผ ์ ์ฐพ์ ์ ์์
: ์๊ทผ ๋ณํ ํ๋ ฌ์ ๋ค์ด๋ง์ง ์๋ ๋งค์นญ์ ์ ๊ตฌ๋ถํ ์ ์์ด์ ๋์ ๋งค์นญ์ ์ ํ๋ฒ ๋ ์ ๊ฑฐ ๊ฐ๋ฅ
mtrx, mask = cv2.findHomography(srcPoints, dstPoints, method, ransacReprojThreshold, mask, maxIters, confidence)
: ์ฌ๋ฌ ๋งค์นญ์ ์ผ๋ก ์๊ทผ ๋ณํ ํ๋ ฌ์ ๊ตฌํ๋ ํจ์
- srcPoints : ์๋ณธ ์ขํ ๋ฐฐ์ด
- dstPoints : ๊ฒฐ๊ณผ ์ขํ ๋ฐฐ์ด
- method=0(optional) : ๊ทผ์ฌ ๊ณ์ฐ ์๊ณ ๋ฆฌ์ฆ ์ ํ
- 0 : ๋ชจ๋ ์ ์ผ๋ก ์ต์ ์ ๊ณฑ ์ค์ฐจ ๊ณ์ฐ
- cv2.RANSAC : ๋ชจ๋ ์ขํ๋ฅผ ์ฌ์ฉX, ์์์ ์ขํ๋ง ์ ์ ํด์ ๋ง์กฑ๋ ๊ตฌํจ, ์ ์์น, ์ด์์น๋ฅผ ๊ตฌ๋ถํ๋ mask ๋ฐํ
- cv2.LMEDS : ์ ๊ณฑ์ ์ต์ ์ค๊ฐ๊ฐ์ ์ฌ์ฉ, ์ ์์น๊ฐ 50% ์ด์์ธ ๊ฒฝ์ฐ์๋ง ์ ์์ ์ผ๋ก ์๋
- cv2.RHO : ์ด์์น๊ฐ ๋ง์ ๊ฒฝ์ฐ์ ๋ ๋น ๋ฆ
- ransacReprojThreshold=3(optional) : ์ ์์น ๊ฑฐ๋ฆฌ ์๊ณ ๊ฐ(RANSAC, RHO์ธ ๊ฒฝ์ฐ)
- maxIters=2000(optional) : ๊ทผ์ฌ ๊ณ์ฐ ๋ฐ๋ณต ํ์
- confidence=0.995(optional) : ์ ๋ขฐ๋(0~1์ ๊ฐ)
- mtrx : ๊ฒฐ๊ณผ ๋ณํ ํ๋ ฌ
- mask : ์ ์์น ํ๋ณ ๊ฒฐ๊ณผ, N x 1 ๋ฐฐ์ด (0: ๋น์ ์์น, 1: ์ ์์น)
dst = cv2.perspectiveTransform(src, m, dst)
: ์๋ ์ขํ๋ค์ ์๊ทผ ๋ณํ ํ๋ ฌ๋ก ๋ณํํ๋ ํจ์
- src : ์
๋ ฅ ์ขํ ๋ฐฐ์ด
- m : ๋ณํ ๋ฐฐ์ด
- dst(optional) : ์ถ๋ ฅ ์ขํ ๋ฐฐ์ด
: cv2.getPerspectiveTransform()์ 4๊ฐ์ ๊ผญ์ง์ ์ผ๋ก ์ ํํ ์๊ทผ ๋ณํ ํ๋ ฌ์ ๋ฐํํ์ง๋ง,
: cv2.findHomography()๋ ์ฌ๋ฌ ๊ฐ์ ์ ์ผ๋ก ๊ทผ์ฌ ๊ณ์ฐํ ์๊ทผ ๋ณํ ํ๋ ฌ์ ๋ฐํ
: cv2.perspectiveTransform() ํจ์๋ ์๊ทผ ๋ณํํ ์๋ก์ด ์ขํ ๋ฐฐ์ด์ ๋ฐํ
# ๋งค์นญ์ ์๊ทผ ๋ณํ์ผ๋ก ์์ญ ์ฐพ๊ธฐ
import cv2, numpy as np
img1 = cv2.imread('img/taekwonv1.jpg')
img2 = cv2.imread('img/figures.jpg')
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
# ORB, BF-Hamming ๋ก knnMatch ---โ
detector = cv2.ORB_create()
kp1, desc1 = detector.detectAndCompute(gray1, None)
kp2, desc2 = detector.detectAndCompute(gray2, None)
matcher = cv2.BFMatcher(cv2.NORM_HAMMING2)
matches = matcher.knnMatch(desc1, desc2, 2)
# ์ด์ ๊ฑฐ๋ฆฌ์ 75%๋ก ์ข์ ๋งค์นญ์ ์ถ์ถ---โก
ratio = 0.75
good_matches = [first for first,second in matches \
if first.distance < second.distance * ratio]
print('good matches:%d/%d' %(len(good_matches),len(matches)))
# ์ข์ ๋งค์นญ์ ์ queryIdx๋ก ์๋ณธ ์์์ ์ขํ ๊ตฌํ๊ธฐ ---โข
src_pts = np.float32([ kp1[m.queryIdx].pt for m in good_matches ])
# ์ข์ ๋งค์นญ์ ์ trainIdx๋ก ๋์ ์์์ ์ขํ ๊ตฌํ๊ธฐ ---โฃ
dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good_matches ])
# ์๊ทผ ๋ณํ ํ๋ ฌ ๊ตฌํ๊ธฐ ---โค
mtrx, mask = cv2.findHomography(src_pts, dst_pts)
# ์๋ณธ ์์ ํฌ๊ธฐ๋ก ๋ณํ ์์ญ ์ขํ ์์ฑ ---โฅ
h,w, = img1.shape[:2]
pts = np.float32([ [[0,0]],[[0,h-1]],[[w-1,h-1]],[[w-1,0]] ])
# ์๋ณธ ์์ ์ขํ๋ฅผ ์๊ทผ ๋ณํ ---โฆ
dst = cv2.perspectiveTransform(pts,mtrx)
# ๋ณํ ์ขํ ์์ญ์ ๋์ ์์์ ๊ทธ๋ฆฌ๊ธฐ ---โง
img2 = cv2.polylines(img2,[np.int32(dst)],True,255,3, cv2.LINE_AA)
# ์ข์ ๋งค์นญ ๊ทธ๋ ค์ ์ถ๋ ฅ ---โจ
res = cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None, \
flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
cv2.imshow('Matching Homography', res)
cv2.waitKey()
cv2.destroyAllWindows()
: ์ฌ๋ฐ๋ฅธ ๋งค์นญ์ ์ ํ์ฉํด ์๊ทผ ๋ณํ ํ๋ ฌ์ ๊ตฌํ๊ณ , ์๋ณธ ์ด๋ฏธ์ง ํฌ๊ธฐ๋งํผ์ ์ฌ๊ฐํ ๋ํ์ ์๊ทผ ๋ณํํ์ฌ ๊ฒฐ๊ณผ ์ด๋ฏธ์ง์ ํ์
+) good_matches๋ knnMatch() ํจ์์ ๋ฐํ ๊ฒฐ๊ณผ
: match(), knnMatch(), radiusMatch() ํจ์์ ๋ฐํ ๊ฒฐ๊ณผ๋ DMatch ๊ฐ์ฒด ๋ฆฌ์คํธ
DMatch
: ๋งค์นญ ๊ฒฐ๊ณผ๋ฅผ ํํํ๋ ๊ฐ์ฒด
- queryIdx : queryDescriptors์ ์ธ๋ฑ์ค
- trainIdx : trainDescriptors์ ์ธ๋ฑ์ค
- imgIdx : trainDescriptor์ ์ด๋ฏธ์ง ์ธ๋ฑ์ค
- distance : ์ ์ฌ๋ ๊ฑฐ๋ฆฌ
+) RANSAC ์๊ทผ ๋ณํ ๊ทผ์ฌ ๊ณ์ฐ์ผ๋ก ์๋ชป๋ ๋งค์นญ์ ์ถ๊ฐ๋ก ์ ๊ฑฐ
# RANSAC ์๊ทผ ๋ณํ ๊ทผ์ฌ ๊ณ์ฐ์ผ๋ก ๋์ ๋งค์นญ ์ ๊ฑฐ
import cv2, numpy as np
img1 = cv2.imread('img/taekwonv1.jpg')
img2 = cv2.imread('img/figures2.jpg')
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
# ORB, BF-Hamming ๋ก knnMatch ---โ
detector = cv2.ORB_create()
kp1, desc1 = detector.detectAndCompute(gray1, None)
kp2, desc2 = detector.detectAndCompute(gray2, None)
matcher = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = matcher.match(desc1, desc2)
# ๋งค์นญ ๊ฒฐ๊ณผ๋ฅผ ๊ฑฐ๋ฆฌ๊ธฐ์ค ์ค๋ฆ์ฐจ์์ผ๋ก ์ ๋ ฌ ---โข
matches = sorted(matches, key=lambda x:x.distance)
# ๋ชจ๋ ๋งค์นญ์ ๊ทธ๋ฆฌ๊ธฐ ---โฃ
res1 = cv2.drawMatches(img1, kp1, img2, kp2, matches, None, \
flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
# ๋งค์นญ์ ์ผ๋ก ์๊ทผ ๋ณํ ๋ฐ ์์ญ ํ์ ---โค
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์ผ๋ก ๋ณํ ํ๋ ฌ ๊ทผ์ฌ ๊ณ์ฐ ---โฅ
mtrx, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
h,w = img1.shape[:2]
pts = np.float32([ [[0,0]],[[0,h-1]],[[w-1,h-1]],[[w-1,0]] ])
dst = cv2.perspectiveTransform(pts,mtrx)
img2 = cv2.polylines(img2,[np.int32(dst)],True,255,3, cv2.LINE_AA)
# ์ ์์น ๋งค์นญ๋ง ๊ทธ๋ฆฌ๊ธฐ ---โฆ
matchesMask = mask.ravel().tolist()
res2 = cv2.drawMatches(img1, kp1, img2, kp2, matches, None, \
matchesMask = matchesMask,
flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
# ๋ชจ๋ ๋งค์นญ์ ๊ณผ ์ ์์น ๋น์จ ---โง
accuracy=float(mask.sum()) / mask.size
print("accuracy: %d/%d(%.2f%%)"% (mask.sum(), mask.size, accuracy))
# ๊ฒฐ๊ณผ ์ถ๋ ฅ
cv2.imshow('Matching-All', res1)
cv2.imshow('Matching-Inlier ', res2)
cv2.waitKey()
cv2.destroyAllWindows()
- ์๊ทผ ๋ณํ ํ๋ ฌ์ ๊ตฌํ ๋ RANSAC์ ์ฌ์ฉํ๊ณ , ๊ทธ ๊ฒฐ๊ณผ์ธ mask๋ฅผ ํ์ฉํ์ฌ ์๋ชป๋ ๋งค์นญ์ ์ ์ ๊ฑฐ
- mask์๋ ์ ๋ ฅ ์ขํ์ ๋์ผํ ์ธ๋ฑ์ค์ ์ ์์น์๋ 1, ์ด์์น์๋ 0์ด ํ์
์ด๋ ต๋น,,,
'๐ฉโ๐ป IoT (Embedded) > Image Processing' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[v0.33]์์์ฒ๋ฆฌ_๊ดํ ํ๋ฆ(Optical Flow) (0) | 2022.01.18 |
---|---|
[v0.32]์์์ฒ๋ฆฌ_๋ฐฐ๊ฒฝ ์ ๊ฑฐ (0) | 2022.01.18 |
[v0.30]์์์ฒ๋ฆฌ_ํน์ง ๋งค์นญ(Feature Matching) (0) | 2022.01.16 |
[v0.29]์์์ฒ๋ฆฌ_ํน์ง ๋์คํฌ๋ฆฝํฐ ๊ฒ์ถ๊ธฐ (0) | 2022.01.15 |
[v0.28]์์์ฒ๋ฆฌ_์ด๋ฏธ์ง์ ํน์ง์ , ํน์ง์ ๊ฒ์ถ๊ธฐ (0) | 2022.01.15 |