Python OpenCV 图像匹配(Brute-Force与FLANN)的原理与代码实现

先导文章:SIFT特征提取

Python OpenCV SIFT特征提取的原理与代码实现_乔卿的博客-CSDN博客如果对图像扩大规模,如缩放,如下图所示,那么原本的角点在变换后的某些窗口中可能就不是角点,因此,HarrisDetectors不具有尺度不变性。例如,在上图中,低σ的高斯核可以为小角点提供高值,而高σ的高斯核则适合于大角点。因此,我们可以在尺度空间中找到局部极大值。...https://qiaoxs.blog.csdn.net/article/details/125849051?spm=1001.2014.3001.5502

图像匹配

获得两张图像的关键点之后,下一步就是找到它们之间的对应关系,找到那些相匹配的点,从而基于这些点,实现图像拼接。在OpenCV中,我们可以用于Feature Matching的方法有两种:Brute-Force Matcher 和 FLANN Matcher。

Brute-Force Matcher

这一方法即暴力搜索法,它选择第一个集合中一个特征的描述符,计算与第二个集合中的所有其他特征描述符之间的距离,返回最接近的一个。

cv.BFMathcer()

对于BF Matcher,我们首先使用cv.BFMatcher()创建BFMatcher对象,该方法需要两个可选参数:

  • normType:指定距离测量方法,默认为cv.NORM_L2,适合SIFT、SURF。cv.NORM_HAMMING则是更适合ORB(WTA_K == 3 or 4)、BRIEF、BRISK等二进制串描述符。下面列举一些距离方法,参考链接为:OpenCV: Operations on arrays
    • cv.NORM_INF
    • cv.NORM_L1
    • cv.NORM_L2
    • cv.NORM_L2SQR
    • cv.NORM_HAMMING
    • cv.NORM_HAMMING2
  • crossCheck:这是一个布尔型的参数,默认为False。当crossCheck为True时,该方法仅返回那些互为最佳匹配的描述符下标(i,j)。

BFMatcher.match()

创建BFMatcher对象之后,使用BFMatcher.match()方法获得最佳匹配。更进一步地,可以使用BFMatcher.knnMatch()方法返回 k 个最佳匹配,其中 k 由用户指定。

该方法返回的结果是DMatch对象的列表。一个DMatch对象具有如下属性:

  • DMatch.distance - 描述符之间的距离。越低越好。
  • DMatch.trainIdx - 训练描述符描述符的索引。
  • DMatch.queryIdx - 查询描述符中描述符的索引。
  • DMatch.imgIdx - 训练图像的索引。

cv.drawMatches()

该方法被用于绘制关键点的匹配情况。我们看到的许多匹配结果都是使用这一方法绘制的——一左一右两张图像,匹配的关键点之间用线条链接。

函数原型:

cv.drawMatches(   img1, keypoints1, img2, keypoints2, matches1to2, outImg[, matchColor[, singlePointColor[, matchesMask[, flags]]]]) -> outImg


cv.drawMatches(   img1, keypoints1, img2, keypoints2, matches1to2, outImg, matchesThickness[, matchColor[, singlePointColor[, matchesMask[, flags]]]]   ) -> outImg


cv.drawMatchesKnn(    img1, keypoints1, img2, keypoints2, matches1to2, outImg[, matchColor[, singlePointColor[, matchesMask[, flags]]]]) -> outImg

参数:

  • img1:第一张原始图像。
  • keypoints1:第一张原始图像的关键点。
  • img2:第二张原始图像。
  • keypoints2:第二张原始图像的关键点。
  • matches1to2:从第一个图像到第二个图像的匹配,这意味着keypoints1[i]在keypoints2[Matches[i]中有一个对应的点。
  • outImg:绘制结果图像。
  • matchColor:匹配连线与关键点点的颜色,当matchColor==Scalar::all(-1) 时,代表取随机颜色。
  • singlePointColor:没有匹配项的关键点的颜色,当singlePointColor==Scalar::all(-1) 时,代表取随机颜色。
  • matchesMask:确定绘制哪些匹配项的掩码。如果掩码为空,则绘制所有匹配项。
  • flags:绘图功能的一些标志。具体有:
    • cv.DRAW_MATCHES_FLAGS_DEFAULT
    • cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
    • cv.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG
    • cv.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS

使用SIFT描述符的具体实现

# Brute-Force Matching

def bf_match(img_path1, img_path2):

    # 读取两张图像

    img1 = cv2.imread(img_path1, cv2.IMREAD_GRAYSCALE)

    img2 = cv2.imread(img_path2, cv2.IMREAD_GRAYSCALE)


    # 计算两张图像的SIFT描述符

    kp1, des1, _ = sift_algorithm(img_path1)

    kp2, des2, _ = sift_algorithm(img_path2)


    # 创建BFMatcher实例

    bf = cv2.BFMatcher()


    # 获得最佳匹配

    # matches = bf.match(des1, des2)

    matches = bf.knnMatch(des1, des2, k=2)


    # 使用比率检测,筛选出好的匹配

    good = []

    for m, n in matches:

        if m.distance < 0.75 * n.distance:

            good.append([m])


    # 绘制匹配结果

    # matches = sorted(matches, key = lambda x:x.distance)

    # match_result = cv2.drawMatches(img1, kp1, img2, kp2, matches[:10], None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)

    match_result = cv2.drawMatchesKnn(img1, kp1, img2, kp2, good, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)


    # 显示绘制结果

    plt.imshow(match_result)

    plt.show()


    return match_result



bf_match(images_list[0], images_list[1])

匹配结果

FLANN based Matcher

FLANN,即近似最近邻的快速库(Fast Library for Approximate Nearest Neighbors)。

它包含一系列算法,这些算法针对大型数据集中的快速最近邻搜索(KD树)和高维特征进行了优化。对于大型数据集,它比BFMatcher工作得更快。

cv2.FlannBasedMatcher()

对于基于 FLANN 的匹配器,我们使用cv2.FlannBasedMatcher()创建FLANN匹配器对象,该方法需要传递两个字典来指定要使用的算法及其相关参数等。

  • 第一个是 IndexParams,它指定最近邻搜索中的KD树算法。
    • 对于SIFT,SURF,该信息可以是:

FLANN_INDEX_KDTREE = 1

index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)

    • 对于ORB,该信息可以是:

FLANN_INDEX_LSH = 6

index_params= dict(algorithm = FLANN_INDEX_LSH,

                   table_number = 6,

                   key_size = 12,

                   multi_probe_level = 1)

  • 第二个是 SearchParams,它指定递归遍历索引中的树的次数。值越高,精度越高,但也需要更多时间。

search_params = dict(checks=100)

使用SIFT描述符的具体实现

# FLANN Matching

def flann_match(img_path1, img_path2):

    # 读取两张图像

    img1 = cv2.imread(img_path1, cv2.IMREAD_GRAYSCALE)  # queryImage

    img2 = cv2.imread(img_path2, cv2.IMREAD_GRAYSCALE)  # trainImage


    # 计算两张图像的SIFT描述符

    kp1, des1, _ = sift_algorithm(img_path1)

    kp2, des2, _ = sift_algorithm(img_path2)


    # 设置FLANN所需要的两个字典

    FLANN_INDEX_KDTREE = 1

    index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)

    search_params = dict(checks=50)


    # 创建FlannBasedMatcher对象

    flann = cv2.FlannBasedMatcher(index_params, search_params)


    # 获得匹配

    matches = flann.knnMatch(des1, des2, k=2)


    # 为了只绘制好的匹配,这里使用掩码

    matches_mask = [[0,0] for i in range(len(matches))]


    # 使用比率检测,筛选出好的匹配

    for i, (m, n) in enumerate(matches):

        if m.distance < 0.7 * n.distance:

            matches_mask[i] = [1, 0]


    # 绘制匹配结果,这里使用一个字典传递参数

    draw_params = dict(matchColor=(0, 255, 0),

                       singlePointColor=(255, 0, 0),

                       matchesMask=matches_mask,

                       flags=cv2.DrawMatchesFlags_DEFAULT)

    match_result = cv2.drawMatchesKnn(img1, kp1, img2, kp2, matches, None, **draw_params)


    # 显示绘制结果

    plt.imshow(match_result)

    plt.show()


    return match_result



flann_match(images_list[0], images_list[1])

匹配结果

猜你喜欢

转载自blog.csdn.net/qq_41112170/article/details/125849423