EmguCV(OpenCV)ORBetector仅找到不匹配的内容

时间:2018-10-31 08:29:45

标签: c# opencv feature-detection

问题

因此,我对计算机视觉的总体了解还不错。我目前正在尝试通过分析2张图像来计算单应性。我想用单应性校正1个图像的透视图以匹配另一个图像。但是我得到的比赛是错误的,也是错误的。所以我做的单应性扭曲完全消失了。

当前状态

我正在使用EmguCV在C#中包装opencv。 我知道我的代码似乎“正确”地工作。

我加载了两个图像,并声明了一些变量来存储计算结果。

(Image<Bgr, byte> Image, VectorOfKeyPoint Keypoints, Mat Descriptors) imgModel = (new Image<Bgr, byte>(imageFolder + "image0.jpg").Resize(0.2, Emgu.CV.CvEnum.Inter.Area), new VectorOfKeyPoint(), new Mat());
(Image<Bgr, byte> Image, VectorOfKeyPoint Keypoints, Mat Descriptors) imgTest = (new Image<Bgr, byte>(imageFolder + "image1.jpg").Resize(0.2, Emgu.CV.CvEnum.Inter.Area), new VectorOfKeyPoint(), new Mat());
Mat imgKeypointsModel = new Mat();
Mat imgKeypointsTest = new Mat();
Mat imgMatches = new Mat();
Mat imgWarped = new Mat();
VectorOfVectorOfDMatch matches = new VectorOfVectorOfDMatch();
VectorOfVectorOfDMatch filteredMatches = new VectorOfVectorOfDMatch();
List<MDMatch[]> filteredMatchesList = new List<MDMatch[]>();

请注意,我使用ValueTuple<Image,VectorOfKeyPoint,Mat>直接将图像及其各自的关键点和描述符存储起来。

在使用ORB检测器和BruteForce匹配器检测,描述和匹配关键点之后:

ORBDetector detector = new ORBDetector();
BFMatcher matcher = new BFMatcher(DistanceType.Hamming2);

detector.DetectAndCompute(imgModel.Image, null, imgModel.Keypoints, imgModel.Descriptors, false);
detector.DetectAndCompute(imgTest.Image, null, imgTest.Keypoints, imgTest.Descriptors, false);

matcher.Add(imgTest.Descriptors);
matcher.KnnMatch(imgModel.Descriptors, matches, k: 2, mask: null);

此后,我应用ratio test并使用匹配距离阈值进行进一步过滤。

MDMatch[][] matchesArray = matches.ToArrayOfArray();

//Apply ratio test
for (int i = 0; i < matchesArray.Length; i++)
{
  MDMatch first = matchesArray[i][0];
  float dist1 = matchesArray[i][0].Distance;
  float dist2 = matchesArray[i][1].Distance;

  if (dist1 < ms_MIN_RATIO * dist2)
  {
    filteredMatchesList.Add(matchesArray[i]);
  }
}

//Filter by threshold
MDMatch[][] defCopy = new MDMatch[filteredMatchesList.Count][];
filteredMatchesList.CopyTo(defCopy);
filteredMatchesList = new List<MDMatch[]>();

foreach (var item in defCopy)
{
  if (item[0].Distance < ms_MAX_DIST)
  {
    filteredMatchesList.Add(item);
  }
}

filteredMatches = new VectorOfVectorOfDMatch(filteredMatchesList.ToArray());

禁用所有这些过滤器方法并不能使我的结果变好或变差(仅保留所有匹配项),但是它们似乎很有意义,因此我保留了它们。

最后,我从找到并过滤的匹配项中计算出我的单应性,然后使用该单应性扭曲图像并绘制一些调试图像:

Mat homography = Features2DToolbox.GetHomographyMatrixFromMatchedFeatures(imgModel.Keypoints, imgTest.Keypoints, filteredMatches, null, 10);
CvInvoke.WarpPerspective(imgTest.Image, imgWarped, homography, imgTest.Image.Size);

Features2DToolbox.DrawKeypoints(imgModel.Image, imgModel.Keypoints, imgKeypointsModel, new Bgr(0, 0, 255));
Features2DToolbox.DrawKeypoints(imgTest.Image, imgTest.Keypoints, imgKeypointsTest, new Bgr(0, 0, 255));
Features2DToolbox.DrawMatches(imgModel.Image, imgModel.Keypoints, imgTest.Image, imgTest.Keypoints, filteredMatches, imgMatches, new MCvScalar(0, 255, 0), new MCvScalar(0, 0, 255));

//Task.Factory.StartNew(() => ImageViewer.Show(imgKeypointsModel, "Keypoints Model"));
//Task.Factory.StartNew(() => ImageViewer.Show(imgKeypointsTest, "Keypoints Test"));
Task.Factory.StartNew(() => ImageViewer.Show(imgMatches, "Matches"));
Task.Factory.StartNew(() => ImageViewer.Show(imgWarped, "Warp"));

tl; dr :ORBDetector-> BFMatcher-> FilterMatches-> GetHomography-> WarpPerspective

输出

Example for the algorithm 算法示例

Test whether projection is going wrong 测试投影是否出错

Using crosscheck when matching 匹配时使用交叉检查

每个原始图像均为2448x3264,并在对其进行任何计算之前按的比例缩放0.2。

问题

基本上,它很简单但很复杂:我在做什么错? 从上面的示例中可以看到,我检测特征并匹配特征的方法似乎效果极差。所以我问是否有人可以在我的代码中发现错误。或给出建议,说明为什么当互联网上有数百个示例显示出其工作原理和“简易”效果时,我的结果如此糟糕。

我到目前为止所做的尝试:

  • 缩放输入图像。如果将它们缩小很多,通常会得到更好的结果。
  • 检测更多或更少的功能。默认值为500,当前使用。增加或减少这个数字并不能真正使我的结果更好。
  • k的数目众多,但除k = 2之外的其他任何东西对我来说都没有意义,因为我不知道如何修改k> 2的比率检验。
  • 改变过滤器参数,例如在定量测试中使用0.6-0.9的比率。
  • 使用不同的图片:QR码,恐龙的轮廓,我躺在办公桌周围的其他随机物体。
  • 将重新投影阈值从1-10更改为结果的任何变化
  • 验证投影本身没有故障。为算法提供相同的图像以进行模型和测试,可以生成单应性并将其与单应性扭曲。图像不应改变。 按预期工作(请参见示例图片2)。
  • 图片3:匹配时使用交叉检查。看起来更有希望,但仍然不是我所期望的。
  • 使用其他距离方法:Hamming,Hamming2,L2Sqr(不支持其他距离)

我使用的示例

原始图片: 原始图像可以从这里下载: https://drive.google.com/open?id=1Nlqv_0sH8t1wiH5PG-ndMxoYhsUbFfkC

问了以后的进一步实验

所以我问了之后做了一些进一步的研究。上面已经包含了大多数更改,但是我想为此做一个单独的部分。 因此,在遇到许多问题并且似乎无处可去之后,我决定用Google搜索original paper on ORB。此后,我决定尝试并复制他们的一些结果。尝试此操作后,我意识到,即使我尝试将匹配图像旋转一定程度,匹配看起来也不错,但转换完全失败了。 Rotation

我尝试复制对象透视图的方法是否有可能是错误的?

MCVE

https://drive.google.com/open?id=17DwFoSmco9UezHkON5prk8OsPalmp2MX (没有软件包,但是nuget restore足以使其编译)

2 个答案:

答案 0 :(得分:2)

解决方案

问题1

最大的问题实际上是一个很简单的问题。匹配时我不小心翻转了模型和测试描述符:

matcher.Add(imgTest.Descriptors);
matcher.KnnMatch(imgModel.Descriptors, matches, 1, null);

但是,如果您查看这些功能的文档,则会发现必须添加模型并与测试图像进​​行匹配。

matcher.Add(imgModel.Descriptors);
matcher.KnnMatch(imgTest.Descriptors, matches, 1, null);

问题2

我现在不知道为什么,但是Features2DToolbox.GetHomographyMatrixFromMatchedFeatures似乎坏了,我的单应性总是错误的,以一种奇怪的方式扭曲了图像(类似于上面的示例)。

要解决此问题,我继续进行操作,直接将包装器调用用于OpenCV FindHomography(srcPoints, destPoints, method)。为了做到这一点,我必须编写一个小助手来以正确的格式获取数据结构:

public static Mat GetHomography(VectorOfKeyPoint keypointsModel, VectorOfKeyPoint keypointsTest, List<MDMatch[]> matches)
{
  MKeyPoint[] kptsModel = keypointsModel.ToArray();
  MKeyPoint[] kptsTest = keypointsTest.ToArray();

  PointF[] srcPoints = new PointF[matches.Count];
  PointF[] destPoints = new PointF[matches.Count];

  for (int i = 0; i < matches.Count; i++)
  {
    srcPoints[i] = kptsModel[matches[i][0].TrainIdx].Point;
    destPoints[i] = kptsTest[matches[i][0].QueryIdx].Point;
  }

  Mat homography = CvInvoke.FindHomography(srcPoints, destPoints, Emgu.CV.CvEnum.HomographyMethod.Ransac);

  //PrintMatrix(homography);

  return homography;
}

结果

现在一切正常,并按预期进行: Result

答案 1 :(得分:1)

我遇到了同样的问题,并找到了一个合适的解决方案:github Emgu.CV.Example DrawMatches.cs,一切正常。

我修改了代码和方法FindMatch 看起来像这样:

public static void FindMatch(Mat modelImage, Mat observedImage, out VectorOfKeyPoint modelKeyPoints, out VectorOfKeyPoint observedKeyPoints, VectorOfVectorOfDMatch matches, out Mat mask, out Mat homography)
{
    int k = 2;
    double uniquenessThreshold = 0.80;
    homography = null;
    modelKeyPoints = new VectorOfKeyPoint();
    observedKeyPoints = new VectorOfKeyPoint();
    using (UMat uModelImage = modelImage.GetUMat(AccessType.Read))
    using (UMat uObservedImage = observedImage.GetUMat(AccessType.Read))
    {
        var featureDetector = new ORBDetector(9000);
        Mat modelDescriptors = new Mat();
        featureDetector.DetectAndCompute(uModelImage, null, modelKeyPoints, modelDescriptors, false);
        Mat observedDescriptors = new Mat();
        featureDetector.DetectAndCompute(uObservedImage, null, observedKeyPoints, observedDescriptors, false);
        using (var matcher = new BFMatcher(DistanceType.Hamming, false))
        {
            matcher.Add(modelDescriptors);

            matcher.KnnMatch(observedDescriptors, matches, k, null);
            mask = new Mat(matches.Size, 1, DepthType.Cv8U, 1);
            mask.SetTo(new MCvScalar(255));
            Features2DToolbox.VoteForUniqueness(matches, uniquenessThreshold, mask);

            int nonZeroCount = CvInvoke.CountNonZero(mask);
            if (nonZeroCount >= 4)
            {
                nonZeroCount = Features2DToolbox.VoteForSizeAndOrientation(modelKeyPoints, observedKeyPoints,
                    matches, mask, 1.5, 20);
                if (nonZeroCount >= 4)
                    homography = Features2DToolbox.GetHomographyMatrixFromMatchedFeatures(modelKeyPoints,
                        observedKeyPoints, matches, mask, 2);
            }
        }
    }
}

使用:

var model = new Mat(@"image0.jpg");
var scene = new Mat(@"image1.jpg");
Mat result = new Mat();
VectorOfKeyPoint modelKeyPoints;
VectorOfKeyPoint observedKeyPoints;
var matches = new VectorOfVectorOfDMatch();
Mat mask;
Mat homography;
FindMatch(model, scene, out modelKeyPoints, out observedKeyPoints, matches, out mask, out homography);
CvInvoke.WarpPerspective(scene, result, homography, model.Size, Inter.Linear, Warp.InverseMap);

结果:

enter image description here

如果您想观看此过程,请使用下一个代码:

public static Mat Draw(Mat modelImage, Mat observedImage)
{
    Mat homography;
    VectorOfKeyPoint modelKeyPoints;
    VectorOfKeyPoint observedKeyPoints;
    using (VectorOfVectorOfDMatch matches = new VectorOfVectorOfDMatch())
    {
        Mat mask;
        FindMatch(modelImage, observedImage, out modelKeyPoints, out observedKeyPoints, matches, out mask, out homography);
        Mat result = new Mat();
        Features2DToolbox.DrawMatches(modelImage, modelKeyPoints, observedImage, observedKeyPoints,
            matches, result, new MCvScalar(255, 0, 0), new MCvScalar(0, 0, 255), mask);

        if (homography != null)
        {
            var imgWarped = new Mat();
            CvInvoke.WarpPerspective(observedImage, imgWarped, homography, modelImage.Size, Inter.Linear, Warp.InverseMap);
            Rectangle rect = new Rectangle(Point.Empty, modelImage.Size);
            var pts = new PointF[]
            {
                  new PointF(rect.Left, rect.Bottom),
                  new PointF(rect.Right, rect.Bottom),
                  new PointF(rect.Right, rect.Top),
                  new PointF(rect.Left, rect.Top)
            };

            pts = CvInvoke.PerspectiveTransform(pts, homography);
            var points = new Point[pts.Length];
            for (int i = 0; i < points.Length; i++)
                points[i] = Point.Round(pts[i]);

            using (var vp = new VectorOfPoint(points))
            {
                CvInvoke.Polylines(result, vp, true, new MCvScalar(255, 0, 0, 255), 5);
            }
        }
        return result;
    }
}

使用:

var model = new Mat(@"image0.jpg");
var scene = new Mat(@"image1.jpg");
var result = Draw(model, scene);

结果:

enter image description here