【问题标题】:Image recognition using SURF with OpenCV in Android在 Android 中使用带有 OpenCV 的 SURF 进行图像识别
【发布时间】:2016-03-20 02:18:31
【问题描述】:

我正在尝试使用 Android 和 OpenCV 构建一个简单的叶子识别应用程序;我的数据库仅包含 3 个条目(3 种树叶的 3 张图片),我希望能够识别数据库中的一张图片是否在智能手机拍摄的另一张图片中。 我正在使用 SURF 方法从数据库图像中提取关键点,然后将它们与捕获的图像中提取的关键点进行比较以寻找匹配项。 我的问题是结果显示为“颜色匹配”,而不是“特征匹配”:当我比较数据库中的图片和捕获的图片时,匹配的数量与所有 3 个条目相等,因此我得到一个匹配错误。

这张图片来自数据库(注意是没有背景的)

这是我得到的结果:

上图是从智能手机拍摄的,下图是突出显示匹配的结果。

这是我实现的代码:

Mat orig = Highgui.imread(photoPathwithoutFile);
Mat origBW = new Mat();
Imgproc.cvtColor(orig, origBW, Imgproc.COLOR_RGB2GRAY);
MatOfKeyPoint kpOrigin = createSURFdetector(origBW);
Mat descOrig = extractDescription(kpOrigin, origBW);
Leaf result = findMatches(descOrig);
Mat imageOut = orig.clone();
Features2d.drawMatches(orig, kpOrigin, maple, keypointsMaple, resultMaple, imageOut);


public MatOfKeyPoint createSURFdetector (Mat origBW) {
    FeatureDetector surf = FeatureDetector.create(FeatureDetector.FAST);

    MatOfKeyPoint keypointsOrig = new MatOfKeyPoint();

    surf.detect(origBW, keypointsOrig);

    return keypointsOrig;
}

public Mat extractDescription (MatOfKeyPoint kpOrig, Mat origBW) {
    DescriptorExtractor surfExtractor = DescriptorExtractor.create(FeatureDetector.SURF);

    Mat origDesc = new Mat();

    surfExtractor.compute(origBW, kpOrig, origDesc);

    return origDesc;
}

public Leaf findMatches (Mat descriptors) {
    DescriptorMatcher m = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE);
    MatOfDMatch max = new MatOfDMatch();
    resultMaple = new MatOfDMatch();
    resultChestnut = new MatOfDMatch();
    resultSwedish = new MatOfDMatch();
    Leaf match = null;

    m.match(descriptors, mapleDescriptors, resultMaple);
    Log.d("Origin", resultMaple.toList().size()+" matches with Maples");
    if (resultMaple.toList().size() > max.toList().size()) { max = resultMaple; match = Leaf.MAPLE; }
    m.match(descriptors, chestnutDescriptors, resultChestnut);
    Log.d("Origin", resultChestnut.toList().size()+" matches with Chestnut");
    if (resultChestnut.toList().size() > max.toList().size()) { max = resultChestnut; match = Leaf.CHESTNUT; }
    m.match(descriptors, swedishDescriptors, resultSwedish);
    Log.d("Origin", resultSwedish.toList().size()+" matches with Swedish");
    if (resultSwedish.toList().size() > max.toList().size()) { max = resultSwedish; match = Leaf.SWEDISH; }

    //return the match object with more matches
    return match;
}

如何不根据颜色而是根据图片的实际奇点获得更准确的匹配?

【问题讨论】:

    标签: android opencv image-recognition surf


    【解决方案1】:

    嗯,SURF 不是这项任务的最佳人选。 SURF 描述符基本上是在角落的一个小邻域中编码一些梯度统计信息。这使您对许多转换保持不变,但是这样做时您会失去“大局”。该描述符用于缩小要匹配的点之间的对应范围,然后一些几何约束开始发挥作用。

    在您的情况下,描述符似乎在匹配点上做得不好,并且由于它们中有很多,每个点最终都会得到匹配(尽管奇怪的是几何测试并没有阻止这种情况)。

    我可以建议您尝试不同的匹配方法,可能是使用经过训练以检测叶子类型的描述符的 HOG,甚至是基于轮廓的方法,因为形状是您的图像之间真正不同的地方。例如,您可以检测叶子的轮廓,标准化它的长度,找到它的中心,然后以相等的间隔计算从每个点到中心的距离 - 这将是您的描述符。然后找到最大长度并循环移动这个描述符以从极值开始并除以这个值 - 这将为您提供一些基本不变的轮廓起点、旋转和比例的选择。但这很可能在透视和仿射变换下失败。

    如果您想进一步试验特征点 - 尝试检测较少但更具代表性的特征点(按梯度强度、角点得分或其他方式过滤)。也许使用 SIFT 而不是 SURF - 它应该更精确一些。匹配后检查内点数量 - 最佳匹配应具有更高的比率。

    但老实说,这似乎更像是一项机器学习任务,而不是计算机视觉。

    编辑:我检查了你的代码,发现你没有对匹配进行几何检查,因此你得到不正确的匹配。尝试在匹配后执行 findHomography,然后仅考虑在 mask 输出参数中设置为 1 的点。这将使您只考虑可以使用单应性相互扭曲的点,并且可能会大大改善匹配。

    Edit2:添加了代码sn-p(抱歉,我暂时无法测试Java,所以用Python)

    import cv2
    import numpy as np
    
    # read input
    a = cv2.imread(r'C:\Temp\leaf1.jpg')
    b = cv2.imread(r'C:\Temp\leaf2.jpg')
    
    # convert to gray
    agray = cv2.cvtColor(a, cv2.COLOR_BGR2GRAY)
    bgray = cv2.cvtColor(b, cv2.COLOR_BGR2GRAY)
    
    # detect features and compute descriptors
    surf = cv2.SURF() # better use SIFT instead
    kp1, d1 = surf.detectAndCompute(agray,None)
    kp2, d2 = surf.detectAndCompute(bgray,None)
    print 'numFeatures1 =', len(kp1)
    print 'numFeatures2 =', len(kp2)
    
    # use KNN matcher
    bf = cv2.BFMatcher()
    matches = bf.knnMatch(d1,d2, k=2)
    
    # Apply Lowe ratio test
    good = []
    for m,n in matches:
        if m.distance < 0.75*n.distance:
            good.append(m)
    
    print 'numMatches =', len(matches)
    print 'numGoodMatches =', len(good)
    
    # if have enough matches - try to calculare homography to discard matches 
    # that don't fit perspective transformation model
    if len(good)>10:
        # convert matches into correct format (python-specific)
        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 = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0)
        print 'numMatches =', sum(mask.ravel().tolist()) # calc number of 1s in mask
    
    else:
        print "not enough good matches are found"
    

    它使用 SURF 为我提供了不同叶子的以下输出

    numFeatures1 = 685
    numFeatures2 = 1566
    numMatches = 685
    numGoodMatches = 52
    numMatches = 11
    

    您可以看到“真实”匹配的数量非常少。但不幸的是,当我们匹配相同叶子类型的不同图像时,numMatches 是相似的。也许你可以通过调整参数来改善结果,但我认为在这里使用关键点并不是一个很好的方法。可能是同一类中的叶子变化所致。

    【讨论】:

    • 有趣!你能附上一个sn-p的代码吗?因为我没有完全理解您的意思:我尝试在匹配后执行findHomography,但我认为我做错了什么......谢谢
    • 已编辑答案以包含代码 sn-p。我已使用您的叶子图像作为输入,高度缩放为 512 像素。
    猜你喜欢
    • 2013-03-13
    • 1970-01-01
    • 2016-03-21
    • 1970-01-01
    • 2013-08-13
    • 1970-01-01
    • 2021-03-01
    • 2012-06-15
    • 2018-01-01
    相关资源
    最近更新 更多