我想识别用于构建乐高分拣机的乐高积木(我将 c++ 与 opencv 结合使用)。 这意味着我必须区分看起来非常相似的对象。
砖 block 通过平面传送带单独送到我的相机。但它们可能以任何可能的方式放置:倒置、侧放或“正常”放置。
我的方法是通过用相机在许多不同的位置和旋转方向拍打砖 block 来教分拣机识别砖 block 。每个 View 的特征都是通过冲浪算法计算的。
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);
}
如果有一 block 未知的砖 block (我要排序的砖 block ),它的特征也会被计算并与已知的特征匹配。 为了找到错误匹配的特征,我按照 OpenCV 2 Cookbook 一书中的描述进行操作:
使用匹配器 (=cv::BFMatcher(cv::NORM_L2)) 搜索两个方向上的两个最近邻
matcher.knnMatch(descriptorsImage1, descriptorsImage2, matches1, 2); matcher.knnMatch(descriptorsImage2, descriptorsImage1, matches2, 2);
我检查找到的最近邻居之间的距离比率。如果两个距离非常相似,则可能使用了错误值。
// loop for matches1 and matches2 for(iterator matchIterator over all matches) if( ((*matchIterator)[0].distance / (*matchIterator)[1].distance) > 0.65 ) throw away
最后只接受对称匹配对。在这些匹配中,不仅 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
现在只剩下相当不错的比赛了。为了过滤掉一些更糟糕的匹配,我使用基本矩阵检查哪些匹配符合 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
结果还不错。但是在极度相似的情况下还是会出现错误。
上图中可以看到类似的砖 block 识别的很好。
然而,在第二张图片中,一 block 错误的砖 block 也被识别出来了。
现在的问题是如何改进匹配。
我有两个不同的想法:
第二张图片中的匹配可以追溯到真正适合的特征,但前提是视野发生剧烈变化。为了识别一 block 砖,我无论如何都必须在许多不同的位置进行比较(至少如图三所示)。这意味着我知道我只能最小限度地改变视野。视野改变的强度信息应该隐藏在基本矩阵中。我如何从这个矩阵中读出房间中的位置变化了多远?尤其是旋转和强缩放应该引起兴趣;如果砖 block 曾经用胶带粘在左侧更远的地方,这应该无关紧要。
第二个想法:
我从 2 张图片中计算出基本矩阵并过滤掉不适合投影的特征 - 难道没有办法使用三张或更多图片来做同样的事情吗? (关键字三焦点张量)。这样匹配应该会变得更加稳定。但我既不知道如何使用 OpenCV 执行此操作,也无法在谷歌上找到任何相关信息。
最佳答案
我没有一个完整的答案,但我有一些建议。
在图像分析方面:
- 看起来你的相机设置非常稳定。很容易将砖 block 与背景分开。我还看到您的系统在后台查找功能。这是不必要的。将所有非砖 block 像素设置为黑色以将其从分析中移除。
- 当您只找到砖 block 时,您的第一步应该是根据砖 block 的大小(即像素数)过滤可能的候选对象。这样,您显示的错误匹配示例就不太可能了。
- 您可以考虑其他特征,例如积木边界框的纵横比、major and minor axes (中心矩的协方差矩阵的特征向量)砖等。
这些更简单的功能将为您提供一个合理的第一个过滤器来限制您的搜索空间。
在机械方面:
- 如果砖 block 实际上是从传送带上下来的,您应该能够使用杆之类的东西沿着直边“拉直”砖 block ,该杆与传送带穿过传送带的方向成一定角度,这样砖 block 就可以到达更多统一对着你的相机like so .
- 与上一点类似,您可以使用类似非常松散的刷子这样的东西悬卡在传送带上,以在砖 block 经过时将它们推倒。
同样,这两点都会限制您的搜索空间。
关于algorithm - 用opencv区分对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18975400/