تشخیص و توصیف ویژگی
تطبیق ویژگی + هموگرافی برای یافتن اشیاء (Feature Matching + Homography to find Objects)
هدف#
در این فصل،
- تطبیق ویژگی و findHomography از ماژول calib3d را با یکدیگر ترکیب خواهیم کرد تا اشیاء شناختهشده را در یک تصویر پیچیده پیدا کنیم.
مبانی#
پس در جلسه قبل چه کردیم؟ یک queryImage داشتیم، برخی نقاط ویژگی را در آن پیدا کردیم، یک trainImage دیگر گرفتیم، ویژگیهای آن تصویر را هم پیدا کردیم و بهترین تطبیقها در میان آنها را یافتیم. به طور خلاصه، مکانهایی از بخشهایی از یک شیء را در تصویر دیگری که شلوغ است پیدا کردیم. این اطلاعات برای پیدا کردن دقیق شیء در trainImage کافی است.
برای این کار، میتوانیم از یک تابع از ماژول calib3d یعنی cv.findHomography() استفاده کنیم. اگر مجموعه نقاط از هر دو تصویر را ارسال کنیم، تبدیل پرسپکتیو (perspective transformation) آن شیء را پیدا خواهد کرد. سپس میتوانیم از cv.perspectiveTransform() برای یافتن شیء استفاده کنیم. این تابع به حداقل چهار نقطه صحیح برای یافتن تبدیل نیاز دارد.
دیدهایم که ممکن است برخی خطاهای احتمالی در تطبیق وجود داشته باشد که بر نتیجه تأثیر بگذارد. برای حل این مشکل، الگوریتم از RANSAC یا LEAST_MEDIAN استفاده میکند (که توسط پرچمها قابل تعیین است). بنابراین تطبیقهای خوبی که برآورد صحیح ارائه میدهند inlier و بقیه outlier نامیده میشوند. cv.findHomography() یک ماسک را برمیگرداند که نقاط inlier و outlier را مشخص میکند.
پس بیایید این کار را انجام دهیم!!!
کد#
ابتدا، مثل همیشه، ویژگیهای SIFT را در تصاویر پیدا میکنیم و آزمون نسبت را برای یافتن بهترین تطبیقها اعمال میکنیم.
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
MIN_MATCH_COUNT = 10
img1 = cv.imread('box.png', cv.IMREAD_GRAYSCALE) # queryImage
img2 = cv.imread('box_in_scene.png', cv.IMREAD_GRAYSCALE) # trainImage
# Initiate SIFT detector
sift = cv.SIFT_create()
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks = 50)
flann = cv.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1,des2,k=2)
# store all the good matches as per Lowe's ratio test.
good = []
for m,n in matches:
if m.distance < 0.7*n.distance:
good.append(m)
اکنون یک شرط تعیین میکنیم که حداقل ۱۰ تطبیق (تعریفشده توسط MIN_MATCH_COUNT) باید وجود داشته باشد تا شیء پیدا شود. در غیر این صورت، پیامی نشان داده میشود که تعداد تطبیقها کافی نیست.
اگر تطبیقهای کافی پیدا شد، مکان نقاط کلیدی تطبیقیافته را در هر دو تصویر استخراج میکنیم. آنها برای یافتن تبدیل پرسپکتیو ارسال میشوند. پس از به دست آوردن این ماتریس تبدیل ۳x۳، از آن برای تبدیل گوشههای queryImage به نقاط متناظر در trainImage استفاده میکنیم. سپس آن را رسم میکنیم.
if len(good)>MIN_MATCH_COUNT:
src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2)
dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2)
M, mask = cv.findHomography(src_pts, dst_pts, cv.RANSAC,5.0)
matchesMask = mask.ravel().tolist()
h,w = img1.shape
pts = np.float32([ [0,0],[0,h-1],[w-1,h-1],[w-1,0] ]).reshape(-1,1,2)
dst = cv.perspectiveTransform(pts,M)
img2 = cv.polylines(img2,[np.int32(dst)],True,255,3, cv.LINE_AA)
else:
print( "Not enough matches are found - {}/{}".format(len(good), MIN_MATCH_COUNT) )
matchesMask = None
در نهایت، inlierهای خود را رسم میکنیم (در صورت پیدا شدن موفق شیء) یا نقاط کلیدی تطبیقیافته را (در صورت شکست).
draw_params = dict(matchColor = (0,255,0), # draw matches in green color
singlePointColor = None,
matchesMask = matchesMask, # draw only inliers
flags = 2)
img3 = cv.drawMatches(img1,kp1,img2,kp2,good,None,**draw_params)
plt.imshow(img3, 'gray'),plt.show()
نتیجه را در زیر ببینید. شیء با رنگ سفید در تصویر شلوغ مشخص شده است:
