python 良好的单应性特征匹配产生的转换不充分

qyzbxkaa  于 2022-12-21  发布在  Python
关注(0)|答案(1)|浏览(115)

我正在尝试确定一种方法来旋转和平移扫描的2D图像,以匹配它几乎相同但质量较低的数字模板。运行代码后,我希望图像在叠加时非常接近地对齐,以便可以根据模板布局的知识将后处理效果应用于扫描。
我尝试了许多不同的方法,这些方法基于识别扫描图像上可以一致Map到模板的某些特征(基于findContour的方法运气不佳),但最终确定最有效的方法是使用openCV执行单应性匹配,然后应用变换(使用perspectiveTransform或warpPerspective)。
我得到的匹配非常好,即使我把匹配的距离阈值设置得非常严格,我也得到了几十个点匹配,我已经改变了阈值和findHomography RANSAC,但最终,我从findHomography得到的变换不足以满足我的需要;我不确定是否有我没有充分探索的旋钮,或者如果图像质量的差异刚刚好,这是不可行的。
下面是我使用的代码:

from matplotlib import pyplot as plt
import numpy as np
import cv2 as cv

def feature_match(scanned_image, template_image, MIN_MATCH_COUNT=10, dist_thresh=0.2, RANSAC=10.0):
    # Initiate SIFT detector
    sift = cv.SIFT_create()

    # find the keypoints and descriptors with SIFT
    kp1, des1 = sift.detectAndCompute(scanned_image, None)
    kp2, des2 = sift.detectAndCompute(template_image, 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 < dist_thresh * n.distance:
            good.append(m)

    # Do we have enough?
    if len(good) > MIN_MATCH_COUNT:
        print("%s good matches using distance threshold of %s" % (len(good), dist_thresh))
        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, RANSAC)
        matchesMask = mask.ravel().tolist()

        # Apply warp perspective based on homography matrix
        warped_image = cv.warpPerspective(scanned_image, M, (scanned_image.shape[1], scanned_image.shape[0]))
        plt.imshow(warped_image, 'gray'), plt.show()

    else:
        print("Not enough matches are found - {}/{}".format(len(good), MIN_MATCH_COUNT))
        matchesMask = None

    # Show quality of matches
    draw_params = dict(matchColor=(0, 255, 0),  # draw matches in green color
                       singlePointColor=None,
                       matchesMask=matchesMask,  # draw only inliers
                       flags=2)
    match_quality = cv.drawMatches(scanned_image, kp1, template_image, kp2, good, None, **draw_params)
    plt.imshow(match_quality, 'gray'), plt.show()

    cv.imwrite(r"img1.png", cv.cvtColor(img1, cv.COLOR_GRAY2RGB))
    cv.imwrite(r"img2.png", cv.cvtColor(img2, cv.COLOR_GRAY2RGB))
    cv.imwrite(r"warped_image.png", cv.cvtColor(warped_image, cv.COLOR_GRAY2RGB))

# Load images
img1_path = r"scanned_image.png"
img2_path = r"template_image.png"

img1 = cv.imread(img1_path)
img1 = cv.cvtColor(img1, cv.COLOR_BGR2GRAY)

img2 = cv.imread(img2_path)
img2 = cv.cvtColor(img2, cv.COLOR_BGR2GRAY)
# upscaling img2 to the final scale I'm ultimately after; saves an upscale
img2 = cv.resize(img2, (img2.shape[1] * 2, img2.shape[0] * 2), cv.IMREAD_UNCHANGED)

feature_match(scanned_image=img1, template_image=img2, MIN_MATCH_COUNT=10, dist_thresh=0.2)

这是我用的比喻:

扫描图像x1c 0d1x
模板图像

注意:既有较低质量的图像,也有最初较低的分辨率。两者之间有微小的差异,但不足以降低匹配(* 我认为?*)

扫描图像与模板图像匹配

使用0.2的距离阈值,我得到了100个匹配项。Setting it around 0.8 and I get over 2400

使用返回的单应矩阵将扫描图像扭曲到模板中

扭曲扫描覆盖在模板上

考虑到匹配点的数量,我期待着比这更好的结果。转换后的图像乍一看看很好(当然比开始时更好),但缺乏使用模板布局知识然后修改扫描的能力。
这里有没有我应该采用的替代方法?我应该利用的参数?或者这只是在给定模板质量的情况下可以实现的--或者是利用的方法论?

gmxoilav

gmxoilav1#

为了回答我自己的问题,以防将来有人神秘地遇到类似的问题:重要的是要确保当你应用你的单应矩阵时,你的目标大小与你试图匹配的模板相对应,如果你想得到与所述模板的“精确”匹配的话。
在我原来的我有:

warped_image = cv.warpPerspective(scanned_image, M, (scanned_image.shape[1], scanned_image.shape[0]))

本来应该是这样的:

warped_image = cv.warpPerspective(scanned_image, M, (template_image.shape[1], template_image.shape[0]))

scanned_image和template_image的大小之间存在微小的比例差异;虽然它们很接近,但这些微小的差异足以使它们在直接比较时投影到错误的大小将扭曲对齐。

这个更新的版本并不完美,但它可能足够接近我的需要。我怀疑ECC匹配方法/第二阶段处理方法@fmw42描述的将是一个很好的第二次通过。我会研究它,并编辑这个答案,如果他们足够重要,值得探索,如果有人在未来类似地处理这类事情。

相关问题