使用OpenCV区分物体

6
我希望能够识别乐高积木,以便用于制作乐高分拣机(我使用C++和OpenCV)。 这意味着我必须区分外观非常相似的对象。
积木逐个经过平面传送带到达我的相机。但它们可能倒置、侧放或"正常"放置。
我的方法是通过在许多不同位置和旋转中使用相机捕获图像,从而让分拣机学习积木。每个视图的特征由SURF算法计算出来。
void calculateFeatures(const cv::Mat& image,
        std::vector<cv::KeyPoint>& keypoints,
        cv::Mat& descriptors)
{
  // detector == cv::SurfFeatureDetector(10)
  detector->detect(image,keypoints);
  // extractor == cv::SurfDescriptorExtractor()
  extractor->compute(image,keypoints,descriptors);
}

如果有一个未知的砖块(我想要分类的砖块),它的特征也会被计算并与已知的进行匹配。 为了找到错误匹配的特征,我按照《OpenCV 2 Cookbook》中的描述进行操作:

  1. with the matcher (=cv::BFMatcher(cv::NORM_L2)) the two nearest neighbours in both directions are searched

    matcher.knnMatch(descriptorsImage1, descriptorsImage2,
      matches1,
          2);
    matcher.knnMatch(descriptorsImage2, descriptorsImage1,
      matches2,
      2);
    
  2. I check the ratio between the distances of the found nearest neighbours. If the two distances are very similar it's likely that a false value is used.

    // loop for matches1 and matches2
    for(iterator matchIterator over all matches)
      if( ((*matchIterator)[0].distance / (*matchIterator)[1].distance) > 0.65 )
        throw away
    
  3. Finally only symmatrical match-pairs are accepted. These are matches in which not only n1 is the nearest neighbour to feature f1, but also f1 is the nearest neighbour to n1.

    for(iterator matchIterator1 over all matches)
      for(iterator matchIterator2 over all matches)
        if ((*matchIterator1)[0].queryIdx == (*matchIterator2)[0].trainIdx  &&
        (*matchIterator2)[0].queryIdx == (*matchIterator1)[0].trainIdx)
          // good Match
    

现在只剩下相当不错的匹配项。为了过滤掉更多的错误匹配,我使用基本矩阵检查哪些匹配项适合将img1投影到img2上。

std::vector<uchar> inliers(points1.size(),0);
cv::findFundamentalMat(
    cv::Mat(points1),cv::Mat(points2), // matching points
    inliers,
    CV_FM_RANSAC,
    3,
    0.99);

std::vector<cv::DMatch> goodMatches
// extract the surviving (inliers) matches
std::vector<uchar>::const_iterator itIn= inliers.begin();
std::vector<cv::DMatch>::const_iterator itM= allMatches.begin();
// for all matches
for ( ;itIn!= inliers.end(); ++itIn, ++itM)

  if (*itIn)
    // it is a valid match

好的匹配 结果非常不错。但在极端相似的情况下仍会出现错误。
在上面的图片中,您可以看到一个相似的砖块被很好地识别了。

坏的匹配 然而,在第二张图片中,一个错误的砖块同样被识别了。

现在的问题是如何改进匹配。

我有两个不同的想法:

所有可能的砖块视图

  • 第二张图片中的匹配跟实际匹配的特征相关,但只有在视野强烈变化时才能匹配得很好。无论如何,为了识别砖块,我必须在许多不同的位置进行比较(至少如图三所示)。这意味着我知道我只允许最小程度地改变视野。视野变化的强度信息应该隐藏在基本矩阵中。如何从这个矩阵中读取房间位置的变化程度?特别是旋转和强缩放应该是有趣的;如果砖块一次被贴在左侧更远的位置上,这不应该有影响。

  • 第二个想法:
    我从2张图片中计算出基本矩阵,并过滤掉不适合投影的特征 - 难道没有一种方法可以使用3张或更多张图片做到同样的事情吗?(关键词三线性张量)。这样匹配应该会变得更加稳定。但我既不知道如何使用OpenCV实现这一点,也无法在Google上找到任何相关信息。

1个回答

2

我没有一个完整的答案,但我有一些建议。

在图像分析方面:

  • 看起来你的相机设置是比较固定的。很容易将砖块与背景分离。我还看到你的系统在背景中找到了一些特征。这是不必要的。将所有非砖块像素设为黑色以将它们从分析中删除。
  • 当你只定位到砖块时,你的第一步应该是基于砖块中的大小(即像素数)过滤可能的候选项。这样,你展示的错误匹配示例就已经不太可能发生了。
  • 你还可以考虑其他特征,例如砖块的边界框的长宽比、主轴和次轴(中心矩的协方差矩阵的特征向量)等。

这些更简单的特征将为你提供一个合理的第一级过滤器,以限制你的搜索空间。

在机械方面:

  • 如果砖块实际上是从传送带上下来的,你应该能够使用类似于一根棒子的东西将砖块沿着与传送带方向呈角度的直边“拉直”,以便砖块更加均匀地到达你的相机,就像这样
  • 与上一个观点类似,你可以使用类似于悬挂在传送带上的非常松散的刷子,使竖立的砖块倒下。

同样,这两点将限制你的搜索空间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接