• OpenCV4【22】模板匹配与特征点匹配


    模板匹配

    业务描述:从 一张图 中找到 和 模板图片 “非常相似” 的区域,获取该区域坐标;

    原理简介:用 模板图像 在 原图上 滑动,然后计算 滑到的区域 和 模板 的相似程度,如像素差,把该值 记录在 对应位置,过程类似卷积;

    滑完后,找到 相似程度 最大的 坐标,还原到 原图的坐标,加上 模板的宽高,就得到了 原图上 和模板相似的 区域;

    最大的缺点是 如果 图片有旋转或者缩放,是无法进行正确匹配的

    单目标匹配

    一张图上只找 一个 和 模板 最相似的区域;

    注意:即使 这个区域 和 模板 完全不一样,也会找到这个 极值点,比较死板吧;最直观的优化是 设定一个阈值,大于阈值 才算 匹配

    Opencv 提供了 多种 相似度 度量方式,示例如下

    ################ 单目标匹配
    ### 用 模板 在 原图上 滑动,然后计算像素差(匹配程度),把像素差放到对应的位置上,(类似于卷积过程)
    # 全部滑动完毕后,找到像素差最小的坐标,还原到原图得到原图坐标,加上模板宽高就是 目标在原图上的位置
    
    ## 注意,只是取最小差,即使 原图中没有和模板相似的区域,也有个最小差,这时仍然会取最小差,比较死板,
    # 比较直观的优化思路是设定一个阈值,至少保证大于阈值才算匹配
    
    img0 = cv.imread('imgs/bus.jpg', 0)
    print('img0 shape', img0.shape)
    template = cv.imread('imgs/template2.jpg', 0)   # 模板可以和原图上的匹配区域 像素 不完全一致,取最小差即可,但对应位置必须一致
    print('template shape', template.shape)
    w, h = template.shape[: : -1]
    
    # 列表中所有的6种比较方法
    methods = [cv.TM_CCOEFF, cv.TM_CCOEFF_NORMED, cv.TM_CCORR,
                cv.TM_CCORR_NORMED, cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]
    for method in methods:
        print(method)
        img = img0.copy()
        # 应用模板匹配
        res = cv.matchTemplate(img, template, method)
        print('res shape', res.shape)
        min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
        # 如果方法是TM_SQDIFF或TM_SQDIFF_NORMED,则取最小值
        if method in [cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]:
            top_left = min_loc  # (x, y)
        else:
            top_left = max_loc
        bottom_right = (top_left[0] + w, top_left[1] + h)
        cv.rectangle(img, top_left, bottom_right, 255, 2)
        plt.subplot(121), plt.imshow(res, cmap='gray')
        plt.title('Matching Result'), plt.xticks([]), plt.yticks([])
    
        plt.subplot(122), plt.imshow(img, cmap='gray')
        plt.title('Detected Point'), plt.xticks([]), plt.yticks([])
        plt.suptitle(method)
        plt.show()

    输出效果图

    模板

     

    1. 模板是直接从原图上 抠下来的

    2. 我故意给 模板 加了一些噪声,也匹配正确了 

    平滑过程分析

    通过 输入尺寸来 说明 平滑过程

    img0 shape (903, 1204)      # 原图
    template shape (55, 61)     # 模板
    res shape (849, 1144)       # 匹配后的矩阵 903-55+1=849,1204-61+1=1144

    多目标匹配

    一张图中 有 多个 和 模板相似的 区域,原理没变化,只是 加了个 阈值,大于阈值的 都算匹配

    注意:由于旋转和缩放会导致算法失效,故匹配效果不佳

    ################ 多目标匹配
    # 一张图中存在多个与模板相似的地方,原理同单目标检测,
    img_rgb = cv.imread('imgs/balls.jpg')
    img_gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY)
    template = cv.imread('imgs/ballst.jpg', 0)
    w, h = template.shape[::-1]
    res = cv.matchTemplate(img_gray, template, cv.TM_CCOEFF_NORMED)
    threshold = 0.6
    loc = np.where(res >= threshold)
    print(loc)
    for pt in zip(*loc[::-1]):
        cv.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
    cv.imwrite('res3.png', img_rgb)

    输出效果图

    模板

     

    特征点匹配

    也是一种模板匹配,只是 对 旋转和缩放 依然有效

    暴力匹配 - brute force - BFMatch

    暴力匹配很简单,首先在 模板特征点描述符中找一个特征点,去匹配目标图中所有特征点描述符,匹配使用 距离 来衡量,返回 距离最近的特征点

    cv.BFMatcher 创建匹配器,两个参数
    def create(self, normType=None, crossCheck=None):
    normType:计算距离的方式,缺省条件下为 cv2.NORM_L2
    crossCheck:

    normType 默认值为cv2.Norm_L2, 适用于SIFT,SURF算法,还有一个参数为cv2.Norm_L1;

    如果是ORB,BRIEF,BRISK算法等,要是用cv2.NORM_HAMMING,如果ORB算法的参数设置为VTA_K==3或4,normType就应该设置为cv2.NORM_HAMMING2

    crossCheck,默认值是False。如果设置为True,匹配条件会更加严格。举例来说,如果A图像中的i点和B图像中的j点距离最近,并且B中的j点到A中的i点距离也最近,相互匹配,这个匹配结果才会返回。 

    示例代码

    # 读取需要特征匹配的两张照片,格式为灰度图
    template = cv.imread("imgs/ballt.jpg", 0)
    target = cv.imread("imgs/balln.jpg", 0)
    
    orb = cv.ORB_create()     # 建立orb特征检测器
    kp1, des1 = orb.detectAndCompute(template, None)    # 计算template中的特征点和描述符
    kp2, des2 = orb.detectAndCompute(target, None)      # 计算target中的特征点和描述符
    bf = cv.BFMatcher(cv.NORM_HAMMING, crossCheck=True) # 建立匹配关系
    mathces = bf.match(des1, des2)                      # 匹配描述符
    mathces = sorted(mathces, key=lambda x: x.distance) # 据距离来排序
    print([i.distance for i in mathces])
    # [3.0, 3.0, 4.0, 9.0, 10.0, 13.0, 13.0, 20.0, 21.0, 23.0, 23.0, 25.0, 25.0, 29.0, 30.0, 32.0,
    # 51.0, 56.0, 70.0, 70.0, 74.0]
    mask = np.empty((30, 30))
    result = cv.drawMatches(template, kp1, target, kp2, mathces[: 6], None, flags=2) # 画出匹配关系
    plt.imshow(result), plt.show()                      # matplotlib描绘出来

    输出效果图

    FLANN匹配

    示例代码

    '''
    1.FLANN代表近似最近邻居的快速库。它代表一组经过优化的算法,用于大数据集中的快速最近邻搜索以及高维特征。
    2.对于大型数据集,它的工作速度比BFMatcher快。
    3.需要传递两个字典来指定要使用的算法及其相关参数等
    对于SIFT或SURF等算法,可以用以下方法:
    index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
    对于ORB,可以使用以下参数:
    index_params= dict(algorithm = FLANN_INDEX_LSH,
                       table_number = 6, # 12   这个参数是searchParam,指定了索引中的树应该递归遍历的次数。值越高精度越高
                       key_size = 12,     # 20
                       multi_probe_level = 1) #2
    '''
    
    queryImage = cv.imread("imgs/ballt.jpg", 0)
    trainingImage = cv.imread("imgs/balln.jpg", 0)
    
    sift = cv.SIFT_create()     # 创建sift检测器
    kp1, des1 = sift.detectAndCompute(queryImage, None)
    kp2, des2 = sift.detectAndCompute(trainingImage, None)
    # 设置Flannde参数
    FLANN_INDEX_KDTREE = 0
    indexParams = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
    searchParams = dict(checks=50)
    flann = cv.FlannBasedMatcher(indexParams, searchParams)     # 建立匹配关系
    matches = flann.knnMatch(des1, des2, k=2)                   # 开始匹配
    # 设置好初始匹配值
    matchesMask = [[0, 0] for i in range(len(matches))]
    for i, (m, n) in enumerate(matches):
        if m.distance < 0.5 * n.distance: # 舍弃小于0.5的匹配结果
            matchesMask[i] = [1, 0]
    print(matchesMask)
    
    drawParams = dict(matchColor=(0, 0, 255), singlePointColor=(255, 0, 0),
                      matchesMask=matchesMask, flags=0)         # 给特征点和匹配的线定义颜色
    resultimage = cv.drawMatchesKnn(queryImage, kp1, trainingImage, kp2,
                                    matches, None, **drawParams) # 画出匹配的结果
    plt.imshow(resultimage,), plt.show()

    输出效果图

    对缩放好像效果一般,可能球 确实不同吧 

    FLANN单应性匹配

    处理不在同一 平面角度 的照片

    基于FLANN的匹配器(FLANN based Matcher)定位图片

    示例代码

    MIN_MATCH_COUNT = 10    # 设置最低特征点匹配数量为10
    template = cv.imread("imgs/ballt.jpg", 0)
    target = cv.imread("imgs/balln.jpg", 0)
    
    # Initiate SIFT detector创建sift检测器
    sift = cv.SIFT_create()
    # find the keypoints and descriptors with SIFT
    kp1, des1 = sift.detectAndCompute(template, None)
    kp2, des2 = sift.detectAndCompute(target, None)
    # 创建设置FLANN匹配
    FLANN_INDEX_KDTREE = 0
    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: # 舍弃大于0.7的匹配
            good.append(m)
    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)
        #计算变换矩阵和MASK
        M, mask = cv.findHomography(src_pts, dst_pts, cv.RANSAC, 5.0)
        matchesMask = mask.ravel().tolist()
        h, w = template.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)
        cv.polylines(target, [np.int32(dst)], True, 0, 2, cv.LINE_AA)
    else:
        print( "Not enough matches are found - %d/%d" % (len(good),MIN_MATCH_COUNT))
        matchesMask = None
    draw_params = dict(matchColor=(0, 255, 0),
                       singlePointColor=None,
                       matchesMask=matchesMask,
                       flags=2)
    result = cv.drawMatches(template, kp1, target, kp2, good, None, **draw_params)
    plt.imshow(result, 'gray')
    plt.show()

    输出效果图

    小结

    应用扩展

    1. 图像拼接,在图像拼接前,找到各个图像的特征点很重要 

    参考资料:

    https://blog.csdn.net/zhuisui_woxin/article/details/84400439  opencv+python实现图像匹配----模板匹配、特征点匹配

    https://zhuanlan.zhihu.com/p/35226009    Opencv for python(2)--图像匹配

    https://baijiahao.baidu.com/s?id=1659458483678227764&wfr=spider&for=pc

    https://www.jb51.net/article/173182.htm  Python使用Opencv实现图像特征检测与匹配的方法

    https://www.jianshu.com/p/ed57ee1056ab  OpenCV-Python教程:41.特征匹配

    https://blog.csdn.net/weixin_39614546/article/details/110884130    参数解释很详细

  • 相关阅读:
    20192415 202120222 《网络与系统攻防技术》实验七实验报告
    C#字符串压缩
    linux服务器登录mysql报错bash: mysql: command not found
    java读取含有合并单元格的Excel
    修改序列号:当序列号小于主键ID的时候如何处理?
    1130 is not allowed to connect to this MySql server
    报错: java.lang.IllegalStateException: Cannot get a STRING value from a NUMERIC cell
    报错:There is no getter for property named 'distinct' in
    报错:违反检查约束条件 (GYPTS.SYS_C0016857)
    vue代码如何实现平方米m2
  • 原文地址:https://www.cnblogs.com/yanshw/p/15603757.html
Copyright © 2020-2023  润新知