用opencv区分对象

Ulr*_*kop 6 algorithm opencv image-processing object-detection computer-vision

我想识别用于构建乐高分拣机的乐高积木(我使用带有opencv的c ++).这意味着我必须区分看起来非常相似的对象.

砖块在平板输送机上单独进入我的相机.但它们可能以任何可能的方式存在:颠倒,侧面或"正常".

我的方法是通过用相机拍摄许多不同的位置和旋转来教给分拣机砖.每个视图的特征都由surf-algorythm计算.

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);
}
Run Code Online (Sandbox Code Playgroud)

如果有一个未知的砖块(我想要排序的砖块),它的特征也会被计算出来并与已知的砖块相匹配.为了找到错误匹配的功能,我按照OpenCV 2 Cookbook一书中的描述进行操作:

  1. 使用匹配器(= cv :: BFMatcher(cv :: NORM_L2))搜索两个方向上的两个最近邻居

    matcher.knnMatch(descriptorsImage1, descriptorsImage2,
      matches1,
          2);
    matcher.knnMatch(descriptorsImage2, descriptorsImage1,
      matches2,
      2);
    
    Run Code Online (Sandbox Code Playgroud)
  2. 我检查找到的最近邻居的距离之间的比率.如果两个距离非常相似,则可能使用假值.

    // loop for matches1 and matches2
    for(iterator matchIterator over all matches)
      if( ((*matchIterator)[0].distance / (*matchIterator)[1].distance) > 0.65 )
        throw away
    
    Run Code Online (Sandbox Code Playgroud)
  3. 最后,只接受对称匹配对.这些匹配不仅n1是特征f1的最近邻居,而且f1是与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
    
    Run Code Online (Sandbox Code Playgroud)

现在只剩下相当不错的比赛.为了过滤掉一些更糟糕的匹配,我使用基本矩阵检查哪些匹配符合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
Run Code Online (Sandbox Code Playgroud)

很好的比赛 结果非常好.但是在极端相似的情况下,仍然会发生故障.
在上图中,您可以看到类似的砖被很好地识别.

不好的比赛 然而,在第二张图片中,也识别出错误的砖块.

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

我有两个不同的想法:

所有可能的砖景

  • 第二张照片中的匹配追溯到非常合适的特征,但前提是视野强烈变化.要识别砖块,我必须在许多不同的位置比较它(至少如图3所示).这意味着我知道我只允许最低限度地改变视野.视野变化的强度应该隐藏在基本矩阵中.如何从这个矩阵中读出房间中的位置变化了多远?特别是旋转和强缩放应该是有意义的; 如果砖块一旦在左侧贴上,这应该没关系.

  • 第二个想法:
    我从2张图片中计算出基本矩阵并过滤掉了不适合投影的特征 - 不应该有办法用三张或更多图片做同样的事情吗?(关键词Trifocal tensor).这样匹配应该变得更加稳定.但我既不知道如何使用OpenCV这样做,也不能在谷歌上找到任何相关的信息.

jil*_*wit 2

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

在图像分析方面:

  • 看起来你的相机设置非常稳定。很容易将砖块与背景分开。我还看到您的系统在后台寻找功能。这是不必要的。将所有非砖块像素设置为黑色以将其从分析中删除。
  • 当您仅找到砖块时,第一步应该是根据砖块的大小(即像素数)过滤可能的候选者。这样,您显示的示例错误匹配的可能性就已经很小了。
  • 您可以考虑其他特征,例如砖块边界框的纵横比、砖块的长轴和短轴(中心矩协方差矩阵的特征向量)等。

这些更简单的功能将为您提供合理的第一个过滤器来限制您的搜索空间。

在机械方面:

  • 如果砖块实际上从传送带上下来,您应该能够使用类似杆之类的东西沿着直边“拉直”砖块,该杆与传送带上的传送带方向成一定角度,以便砖块更均匀地到达您的位置。相机就像这样
  • 与上一点类似,您可以使用悬挂在传送带上的非常松的刷子之类的东西来推翻直立的砖块。

同样,这两点都会限制您的搜索空间。