使用关键点检测时,如何确定成功匹配的关键点数量?
我有三张待进行目标检测的图像,分别包含1个、2个或3个相同的五边形,缩放比例仅相差几个百分点。
最初我尝试在循环中使用屏幕不同区域(目标始终出现在同一位置)执行matchTemplate匹配,比如匹配第一个完整五边形和第二个五边形的中心区域以适配正确缩放比例,但该方法仅偶尔有效,且实现方式较为粗糙。
因此我转而尝试使用关键点检测,我认为检测到2个五边形时匹配关键点数量约为1个时的两倍,3个时约为三倍,以此判断检测到的目标数量。
我已编写好关键点检测代码且可运行,但无法确定应使用哪个变量来统计成功匹配的关键点数量。
当我打印good变量时,得到结果:[<DMatch 0000018FDE162B90>, <DMatch 0000018FDE162ED0>, <DMatch 0000018FDE180150>, <DMatch 0000018FDE180210>, <DMatch 0000018FDE1807D0>, <DMatch 0000018FDE180810>, <DMatch 0000018FDE1820D0>],我原本认为这代表检测到7个关键点,但cv.imshow显示的匹配点数量远多于此。
请问应使用哪个变量来统计成功匹配的关键点数量?
可运行的MVP代码
import cv2 as cv import win32gui, win32con, win32ui import numpy as np import glob crewstars = glob.glob(r"C:\Users\images\crewstars\*.png") def get_haystack_image(): w, h = 1920, 1080 hwnd = None wDC = win32gui.GetWindowDC(hwnd) dcObj = win32ui.CreateDCFromHandle(wDC) cDC = dcObj.CreateCompatibleDC() dataBitMap = win32ui.CreateBitmap() dataBitMap.CreateCompatibleBitmap(dcObj, w, h) cDC.SelectObject(dataBitMap) cDC.BitBlt((0, 0), (w, h), dcObj, (0, 0), win32con.SRCCOPY) signedIntsArray = dataBitMap.GetBitmapBits(True) img = np.frombuffer(signedIntsArray, dtype='uint8') img.shape = (h, w, 4) dcObj.DeleteDC() cDC.DeleteDC() win32gui.ReleaseDC(hwnd, wDC) win32gui.DeleteObject(dataBitMap.GetHandle()) img = img[...,:3] img = np.ascontiguousarray(img) return img def loadImages(directory): # Intialise empty array image_list = [] # Add images to array for i in directory: img = cv.imread(i, cv.IMREAD_UNCHANGED) image_list.append((img, i)) return image_list def preProcessNeedle(image_list): needle_kp1_desc = [] for i in image_list: img = i[0] orb = cv.ORB_create(edgeThreshold=0, patchSize=32) keypoint_needle, descriptors_needle = orb.detectAndCompute(img, None) needle_kp1_desc.append((keypoint_needle, descriptors_needle, img)) return needle_kp1_desc def match_keypoints(descriptors_needle, keypoint_haystack, min_match_count): orbHaystack = cv.ORB_create(edgeThreshold=0, patchSize=32, nfeatures=3000) keypoints_haystack, descriptors_haystack = orbHaystack.detectAndCompute(keypoint_haystack, None) 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=50) try: flann = cv.FlannBasedMatcher(index_params, search_params) matches = flann.knnMatch(descriptors_needle, descriptors_haystack, k=2) except cv.error: return None, None, [], [] good = [] points = [] for pair in matches: if len(pair) == 2: if pair[0].distance < 0.7*pair[1].distance: good.append(pair[0]) if len(good) > min_match_count: for match in good: points.append(keypoints_haystack[match.trainIdx].pt) return keypoints_haystack, good, points def keypointDetection(needle_kp1_desc): res = False # Object Detection for i, img in enumerate(needle_kp1_desc): kp1 = img[0] descriptors_needle = img[1] needle_img = img[2] # get an updated image of the screen & crop it keypoint_haystack = get_haystack_image() keypoint_haystack = keypoint_haystack[170:230, 800:1200] kp2, matches, match_points = match_keypoints(descriptors_needle, keypoint_haystack, min_match_count=40) # display the matches match_image = cv.drawMatches(needle_img, kp1, keypoint_haystack, kp2, matches, None) cv.imshow('Keypoint Search', match_image) cv.moveWindow("Keypoint Search",1940,30) cv.waitKey(0) if match_points: cv.imshow('Keypoint Search', match_image) cv.waitKey(0) res = True break return res while True: crewstarsdir = loadImages(crewstars) needle_kp1_desc = preProcessNeedle(crewstarsdir) if keypointDetection(needle_kp1_desc): pass
统计成功匹配关键点数量的正确变量
你代码中的good列表就是经过Lowe比例筛选后的有效匹配对集合,每个元素对应一对匹配的关键点(一个来自模板图,一个来自目标图)。len(good)就是有效匹配的关键点对数量,这正是你需要统计的数值。
关于cv.imshow显示匹配点更多的原因
你调用cv.drawMatches时传入的是matches而非good,matches是knnMatch返回的原始匹配结果(每个模板关键点会返回2个候选匹配),所以显示的匹配点数量远多于good列表中的数量。修改绘制代码如下:
match_image = cv.drawMatches(needle_img, kp1, keypoint_haystack, kp2, good, None)
这样显示的匹配点数量就会和good列表的长度一致。
额外建议
- 当前
min_match_count=40的阈值设置过高,单个五边形可能无法达到这么多匹配,建议降低至10-20,避免漏检目标。 - 用匹配数量判断目标个数的思路可行,但要注意缩放差异带来的匹配数量波动。建议先测试单张五边形的平均匹配数,再以此为基准设置倍数阈值(比如1.5倍单张数量判断为2个,2.5倍判断为3个)。
- 可以对
match_points进行聚类(如使用cv.kmeans),直接统计聚类中心的数量,这是更准确判断目标个数的方法,能避免单个目标匹配数波动导致的误判。
内容的提问来源于stack exchange,提问作者mak47

