【问题标题】:How to visualize descriptor matching using opencv module in python如何在python中使用opencv模块可视化描述符匹配
【发布时间】:2012-06-22 06:46:28
【问题描述】:

我正在尝试将 opencv 与 python 一起使用。我在 C++ 版本的 opencv 2.4 中编写了一个描述符(SIFT、SURF 或 ORB)匹配代码。我想用python将此代码转换为opencv。我找到了一些关于如何在 c++ 中使用 opencv 函数的文档,但是 python 中的许多 opencv 函数我找不到如何使用它们。这是我的python代码,我目前的问题是我不知道如何在python中使用opencv c++的“drawMatches”。我找到了 cv2.DRAW_MATCHES_FLAGS_DEFAULT 但我不知道如何使用它。这是我使用 ORB 描述符匹配的 python 代码:

im1 = cv2.imread(r'C:\boldt.jpg')
im2 = cv2.cvtColor(im1, cv2.COLOR_BGR2GRAY)
im3 = cv2.imread(r'C:\boldt_resize50.jpg')
im4 = cv2.cvtColor(im3, cv2.COLOR_BGR2GRAY)

orbDetector2 = cv2.FeatureDetector_create("ORB")
orbDescriptorExtractor2 = cv2.DescriptorExtractor_create("ORB")
orbDetector4 = cv2.FeatureDetector_create("ORB")
orbDescriptorExtractor4 = cv2.DescriptorExtractor_create("ORB")

keypoints2 = orbDetector2.detect(im2)
(keypoints2, descriptors2) = orbDescriptorExtractor2.compute(im2,keypoints2)
keypoints4 = orbDetector4.detect(im4)
(keypoints4, descriptors4) = orbDescriptorExtractor4.compute(im4,keypoints4)
matcher = cv2.DescriptorMatcher_create('BruteForce-Hamming')
raw_matches = matcher.match(descriptors2, descriptors4)
img_matches = cv2.DRAW_MATCHES_FLAGS_DEFAULT(im2, keypoints2, im4, keypoints4, raw_matches)
cv2.namedWindow("Match")
cv2.imshow( "Match", img_matches);

“img_matches = cv2.DRAW_MATCHES_FLAGS_DEFAULT(im2, keypoints2, im4, keypoints4, raw_matches)”行的错误信息

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'long' object is not callable

我花了很多时间搜索在 python 中使用 opencv 函数的文档和示例。但是,我很沮丧,因为在 python 中使用 opencv 函数的信息很少。如果有人能教我在哪里可以找到有关如何在 python 中使用 opencv 模块的每个功能的文档,那将非常有帮助。感谢您的宝贵时间和帮助。

【问题讨论】:

    标签: python opencv image-processing descriptor


    【解决方案1】:

    我自己也写了一些只使用 OpenCV Python 接口的东西,我没有使用scipydrawMatches 是 OpenCV 3.0.0 的一部分,而不是我目前使用的 OpenCV 2 的一部分。即使我迟到了,这是我自己的实现,尽我所能模仿drawMatches

    我提供了我自己的图像,其中一张是摄影师,另一张是相同的图像但逆时针旋转了 55 度。

    我写的基本前提是我分配了一个输出 RGB 图像,其中行数是两个图像中的最大值,以适应将两个图像放在输出图像中,而列只是简单的总和两列在一起。我将每个图像放在它们对应的位置,然后遍历所有匹配的关键点的循环。我提取两个图像之间匹配的关键点,然后提取它们的(x,y) 坐标。然后我在每个检测到的位置画圆圈,然后画一条线将这些圆圈连接在一起。

    请记住,在第二张图像中检测到的关键点是相对于它自己的坐标系而言的。如果要将其放置在最终输出图像中,则需要将列坐标偏移第一个图像的列数,以便列坐标相对于输出图像的坐标系.

    事不宜迟:

    import numpy as np
    import cv2
    
    def drawMatches(img1, kp1, img2, kp2, matches):
        """
        My own implementation of cv2.drawMatches as OpenCV 2.4.9
        does not have this function available but it's supported in
        OpenCV 3.0.0
    
        This function takes in two images with their associated 
        keypoints, as well as a list of DMatch data structure (matches) 
        that contains which keypoints matched in which images.
    
        An image will be produced where a montage is shown with
        the first image followed by the second image beside it.
    
        Keypoints are delineated with circles, while lines are connected
        between matching keypoints.
    
        img1,img2 - Grayscale images
        kp1,kp2 - Detected list of keypoints through any of the OpenCV keypoint 
                  detection algorithms
        matches - A list of matches of corresponding keypoints through any
                  OpenCV keypoint matching algorithm
        """
    
        # Create a new output image that concatenates the two images together
        # (a.k.a) a montage
        rows1 = img1.shape[0]
        cols1 = img1.shape[1]
        rows2 = img2.shape[0]
        cols2 = img2.shape[1]
    
        out = np.zeros((max([rows1,rows2]),cols1+cols2,3), dtype='uint8')
    
        # Place the first image to the left
        out[:rows1,:cols1,:] = np.dstack([img1, img1, img1])
    
        # Place the next image to the right of it
        out[:rows2,cols1:cols1+cols2,:] = np.dstack([img2, img2, img2])
    
        # For each pair of points we have between both images
        # draw circles, then connect a line between them
        for mat in matches:
    
            # Get the matching keypoints for each of the images
            img1_idx = mat.queryIdx
            img2_idx = mat.trainIdx
    
            # x - columns
            # y - rows
            (x1,y1) = kp1[img1_idx].pt
            (x2,y2) = kp2[img2_idx].pt
    
            # Draw a small circle at both co-ordinates
            # radius 4
            # colour blue
            # thickness = 1
            cv2.circle(out, (int(x1),int(y1)), 4, (255, 0, 0), 1)   
            cv2.circle(out, (int(x2)+cols1,int(y2)), 4, (255, 0, 0), 1)
    
            # Draw a line in between the two points
            # thickness = 1
            # colour blue
            cv2.line(out, (int(x1),int(y1)), (int(x2)+cols1,int(y2)), (255, 0, 0), 1)
    
    
        # Show the image
        cv2.imshow('Matched Features', out)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    

    为了说明这一点,这是我使用的两张图片:

    我使用 OpenCV 的 ORB 检测器来检测关键点,并使用归一化的汉明距离作为相似度的距离度量,因为这是一个二进制描述符。因此:

    import numpy as np
    import cv2
    
    img1 = cv2.imread('cameraman.png') # Original image
    img2 = cv2.imread('cameraman_rot55.png') # Rotated image
    
    # Create ORB detector with 1000 keypoints with a scaling pyramid factor
    # of 1.2
    orb = cv2.ORB(1000, 1.2)
    
    # Detect keypoints of original image
    (kp1,des1) = orb.detectAndCompute(img1, None)
    
    # Detect keypoints of rotated image
    (kp2,des2) = orb.detectAndCompute(img2, None)
    
    # Create matcher
    bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
    
    # Do matching
    matches = bf.match(des1,des2)
    
    # Sort the matches based on distance.  Least distance
    # is better
    matches = sorted(matches, key=lambda val: val.distance)
    
    # Show only the top 10 matches
    drawMatches(img1, kp1, img2, kp2, matches[:10])
    

    这是我得到的图像:

    【讨论】:

    • Hii @rayryeng 当我尝试运行上面的代码时,我得到了 Traceback(最近一次调用最后):文件“orb1.py”,第 33 行,在 out[:rows1, :cols1,:] = np.dstack([img1, img1, img1]) ValueError: could not broadcast input array from shape (900,1440,9) into shape (900,1440,3)
    • @BhushanPatil - 仔细阅读函数的文档字符串。它需要灰度图像。您正在使用 RGB 图像。在使用该功能之前,您需要将图像转换为灰度。一个简单的电话cv2.cvtColor 就足够了:img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 可以工作。下次使用前请实际阅读该函数的文档。这是所有开发人员在使用其他人的代码时都需要学习的标准技能。
    【解决方案2】:

    您可以在 Python 中将特征匹配可视化如下。注意 scipy 库的使用。

    # matching features of two images
    import cv2
    import sys
    import scipy as sp
    
    if len(sys.argv) < 3:
        print 'usage: %s img1 img2' % sys.argv[0]
        sys.exit(1)
    
    img1_path = sys.argv[1]
    img2_path = sys.argv[2]
    
    img1 = cv2.imread(img1_path, cv2.CV_LOAD_IMAGE_GRAYSCALE)
    img2 = cv2.imread(img2_path, cv2.CV_LOAD_IMAGE_GRAYSCALE)
    
    detector = cv2.FeatureDetector_create("SURF")
    descriptor = cv2.DescriptorExtractor_create("BRIEF")
    matcher = cv2.DescriptorMatcher_create("BruteForce-Hamming")
    
    # detect keypoints
    kp1 = detector.detect(img1)
    kp2 = detector.detect(img2)
    
    print '#keypoints in image1: %d, image2: %d' % (len(kp1), len(kp2))
    
    # descriptors
    k1, d1 = descriptor.compute(img1, kp1)
    k2, d2 = descriptor.compute(img2, kp2)
    
    print '#keypoints in image1: %d, image2: %d' % (len(d1), len(d2))
    
    # match the keypoints
    matches = matcher.match(d1, d2)
    
    # visualize the matches
    print '#matches:', len(matches)
    dist = [m.distance for m in matches]
    
    print 'distance: min: %.3f' % min(dist)
    print 'distance: mean: %.3f' % (sum(dist) / len(dist))
    print 'distance: max: %.3f' % max(dist)
    
    # threshold: half the mean
    thres_dist = (sum(dist) / len(dist)) * 0.5
    
    # keep only the reasonable matches
    sel_matches = [m for m in matches if m.distance < thres_dist]
    
    print '#selected matches:', len(sel_matches)
    
    # #####################################
    # visualization of the matches
    h1, w1 = img1.shape[:2]
    h2, w2 = img2.shape[:2]
    view = sp.zeros((max(h1, h2), w1 + w2, 3), sp.uint8)
    view[:h1, :w1, :] = img1  
    view[:h2, w1:, :] = img2
    view[:, :, 1] = view[:, :, 0]  
    view[:, :, 2] = view[:, :, 0]
    
    for m in sel_matches:
        # draw the keypoints
        # print m.queryIdx, m.trainIdx, m.distance
        color = tuple([sp.random.randint(0, 255) for _ in xrange(3)])
        cv2.line(view, (int(k1[m.queryIdx].pt[0]), int(k1[m.queryIdx].pt[1])) , (int(k2[m.trainIdx].pt[0] + w1), int(k2[m.trainIdx].pt[1])), color)
    
    
    cv2.imshow("view", view)
    cv2.waitKey()
    

    【讨论】:

    • 运行您的代码时,我在第 66 行遇到错误,TypeError: integer argument expected, got float
    • @wall-e 一个匿名用户刚刚编辑了您的帖子,可能想检查一下他们没有破坏它
    • view[:h1, :w1, :] = img1 ValueError: 无法将输入数组从形状 (322,518) 广播到形状 (322,518,3)
    • 好的,问题是 img 并不总是 3 通道,正如@brainless 所指出的那样。这取决于 OpenCV 版本。所以在我的情况下(我使用的是 2.7.13)我不得不回滚到 wall-e 发布的原始版本
    【解决方案3】:

    正如错误消息所述,DRAW_MATCHES_FLAGS_DEFAULT 的类型为“long”。它是 cv2 模块定义的常量,而不是函数。不幸的是,你想要的函数“drawMatches”只存在于 OpenCV 的 C++ 接口中。

    【讨论】:

    • 不再正确。它现在存在于 OpenCV 3.x 和 4.x 中。然而,上述答案是 OpenCV 2.x 的解决方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-14
    • 2018-04-21
    • 2017-01-14
    • 2011-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多