1.知识介绍
Opencv进行图片搜索需要的知识有:特征点匹配+单应性矩阵知识,特征点匹配作者前面文章有记录。
单应性矩阵:两个不同视角上的点所对应的单应性矩阵可以用同一个射影变换来表述可以简单理解为变换矩阵H,x1 = h*x2
2.实现流程
2.1 计算特征点与描述子
分别计算查询图片和训练图片的特征点和特征点的描述子,为后面进行特征点的匹配作准备,使用ORB算法来实现。
# 读取两个图片 img1 = cv2.imread('./image/opencv_search.png') img2 = cv2.imread('./image/opencv_orig.png') # 转换为灰度图 g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) g2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) # 创建ORB对象 orb = cv2.ORB_create() # 对orb特征点检测与描述子计算 ,特征点为KeyPoint kp1, dst1 = orb.detectAndCompute(g1, None) kp2, dst2 = orb.detectAndCompute(g1, None)
2.2 描述子的匹配
创建FLANN的匹配器进行匹配,然后根据比例筛选所有符合条件的匹配项。
# 创建FLANN所需要的参数 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=100) # 创建FLANN匹配器 flann = cv2.FlannBasedMatcher(index_params, search_params) # 进行匹配子计算, matches是一个元组 matches = flann.knnMatch(dst1, dst2, k=2) # print(matches) # 根据比率测试所有符合符合条件的匹配项 good = [] for m, n in matches: if m.distance < 0.7*n.distance: good.append(m)
2.3 求出单应性矩阵并画出轮廓
根据匹配的描述子来求单应性矩阵(3*3),然后根据透视变换来求出在训练集上的轮廓,并画出来
if len(good) > 10: # 寻找单应性矩阵 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即为单应性矩阵(可以理解就是变换矩阵) x1 = M*x2 M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0) # 将mask拉成一维数组,并转换成列表的形式 matchesMask = mask.ravel().tolist() h, w, d = img1.shape # 绘制矩形的四个坐标点 pts = np.float32([ [0, 0], [0, h-1], [w-1, h-1], [w-1, 0] ]).reshape(-1, 1, 2) # 求透视变换的矩阵,求训练图中的部分,求出轮廓边缘 dst = cv2.perspectiveTransform(pts, M) # 在图2上画矩形 img2 = cv2.polylines(img2, [np.int32(dst)], True, 255, 3, cv2.LINE_AA) else: print(f"Not enough matches are found - {len(good)} / {10}") matchesMask = None
2.4 将特征点标出
draw_params = dict(matchColor=(0, 255, 0), singlePointColor=(255, 0, 0), matchesMask=matchesMask, flags=cv2.DrawMatchesFlags_DEFAULT) img3 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params) cv2.imshow('img3', img3) cv2.waitKey(0)