C#+OpenCV基础(十八)_模版匹配

  • MatchTemplate匹配图片:用于匹配光照角度未变化的图片。
  • SIFT特征点匹配图片:尺度不变特征变换(Scale-invariant feature transform,SIFT)。用于匹配光照角度未变化的图片,可在图像中检测出关键点,是一种局部特征描述子。
  • SURF特征点匹配图片:SURF算法速度是SIFT的3倍,SURF善于处理具有模糊和旋转的图像,但是不善于处理视角变化和光照变化,是一种局部特征描述子。其对匹配旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变换、噪声保持一定程度的匹配稳定性。

1、MatchTemplate匹配图片

/// <summary>
/// 匹配图片(Cv2.MatchTemplate)
/// </summary>
/// <param name="matSrc">图片</param>
/// <param name="matTo">特征图片</param>
/// <param name="outMat">结果图片</param>
/// <param name="threshold">相似度</param>
/// <returns></returns>
public static List<Rect> ImageDetector_MatchTemplate(Mat matSrc, Mat matTo, out Mat outMat, double threshold = 0.81)
{
    List<Rect> rects = new List<Rect>();
    outMat = new Mat();
    matSrc.CopyTo(outMat);

    Mat resultMat = new Mat();
    // 模版匹配
    Cv2.MatchTemplate(matSrc, matTo, resultMat, TemplateMatchModes.CCoeffNormed);

    while (true)
    {
        Cv2.MinMaxLoc(resultMat, out double minVal, out double maxVal, out Point minLoc, out Point maxLoc);

        if (maxVal < threshold)  // 如果最大值小于阈值则退出
            break;

        // 绘制图像
        Rect rect = new Rect(maxLoc.X, maxLoc.Y, matTo.Width, matTo.Height);
        Cv2.Rectangle(outMat, rect, Scalar.Red, 1);
        rects.Add(rect);

        // 将匹配位置重置为0,以便匹配下一个最大值
        Cv2.FloodFill(resultMat, maxLoc, Scalar.Black);
    }
    return rects;
}

2、SIFT特征点匹配图片

/// <summary>
/// 匹配图片(SIFT特征点)
/// </summary>
/// <param name="mat">源图片</param>
/// <param name="mat">目标图片</param>
/// <param name="resultMat">结果图片</param>
/// <returns>相似特征点集合</returns>
/// <exception cref="Exception"></exception>
public static List<DMatch> ImageDetector_SIFT(Mat matSrc, Mat matTo, out Mat outMat)
{
    using (Mat matSrcRet = new Mat())  // 结果图片 -源图片
    using (Mat matToRet = new Mat())   // 结果图片 -目标图片
    {
        KeyPoint[] keyPointsSrc, keyPointsTo;  // 源图片与目标图片的‘特征点’
        using (var sift = OpenCvSharp.Features2D.SIFT.Create())
        {
            sift.DetectAndCompute(matSrc, null, out keyPointsSrc, matSrcRet);  // 检测和计算‘源图片的特征点’
            sift.DetectAndCompute(matTo, null, out keyPointsTo, matToRet);     // 检测和计算‘目标图片的特征点’
        }
        using (var bfMatcher = new BFMatcher())
        {
            var matches = bfMatcher.KnnMatch(matSrcRet, matToRet, k: 2);  // 匹配相似点

            // 获取相似特征点
            var pointsSrc = new List<Point2f>();  // 源图片的相似特征点
            var pointsDst = new List<Point2f>();  // 目标图片的相似特征点
            var goodMatches = new List<DMatch>();  // 匹配到的相似特征点

            var matches1 = matches.Where(x => x.Length > 1);
            foreach (DMatch[] items in matches1)
            {
                if (items[0].Distance < 0.5 * items[1].Distance)
                {
                    pointsSrc.Add(keyPointsSrc[items[0].QueryIdx].Pt);
                    pointsDst.Add(keyPointsTo[items[0].TrainIdx].Pt);
                    goodMatches.Add(items[0]);
                    Console.WriteLine($"源图片的特征点:{keyPointsSrc[items[0].QueryIdx].Pt.X}, {keyPointsSrc[items[0].QueryIdx].Pt.Y}");
                }
            }

            outMat = new Mat();

            // 过滤相似特征点(使用RANSAC算法过滤匹配结果)
            var pSrc = pointsSrc.ConvertAll<Point2d>(Point2fToPoint2d);
            var pDst = pointsDst.ConvertAll<Point2d>(Point2fToPoint2d);
            var outMask = new Mat();
            // 如果原始的匹配结果为空, 则跳过过滤步骤
            if (pSrc.Count > 0 && pDst.Count > 0)
                Cv2.FindHomography(pSrc, pDst, HomographyMethods.Ransac, mask: outMask);
            // 如果通过RANSAC处理后的匹配点大于10个,才应用过滤. 否则使用原始的匹配点结果(因为匹配点过少的时候通过RANSAC处理后,可能会得到0个匹配点的结果).
            if (outMask.Rows > 10)
            {
                byte[] maskBytes = new byte[outMask.Rows * outMask.Cols];
                outMask.GetArray(out maskBytes);
                Cv2.DrawMatches(matSrc, keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, matchesMask: maskBytes, flags: DrawMatchesFlags.NotDrawSinglePoints);
            }
            else
                Cv2.DrawMatches(matSrc, keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, flags: DrawMatchesFlags.NotDrawSinglePoints);
            return goodMatches;
        }
    }
}

3、SURF特征点匹配图片

/// <summary>
/// 匹配图片(SURF特征点)
/// </summary>
/// <param name="mat">图片</param>
/// <param name="resultMat">结果图片</param>
/// <returns>车牌号</returns>
/// <exception cref="Exception"></exception>
public static List<DMatch> ImageDetector_SURF(Mat matSrc, Mat matTo, out Mat outMat, double threshold = 400)
{
    using (Mat matSrcRet = new Mat())  // 结果图片 -源图片
    using (Mat matToRet = new Mat())   // 结果图片 -目标图片
    {
        KeyPoint[] keyPointsSrc, keyPointsTo;  // 源图片与目标图片的‘特征点’
        using (var surf = OpenCvSharp.XFeatures2D.SURF.Create(threshold, 4, 3, true, true))
        {
            surf.DetectAndCompute(matSrc, null, out keyPointsSrc, matSrcRet);  // 检测和计算‘源图片的特征点’
            surf.DetectAndCompute(matTo, null, out keyPointsTo, matToRet);     // 检测和计算‘目标图片的特征点’
        }

        using (var flnMatcher = new OpenCvSharp.FlannBasedMatcher())
        {
            var matches = flnMatcher.Match(matSrcRet, matToRet);  // 匹配相似点
            // 求最小最大距离
            double minDistance = 1000;//反向逼近
            double maxDistance = 0;
            for (int i = 0; i < matSrcRet.Rows; i++)
            {
                double distance = matches[i].Distance;
                if (distance > maxDistance)
                {
                    maxDistance = distance;
                }
                if (distance < minDistance)
                {
                    minDistance = distance;
                }
            }
            Console.WriteLine($"最大相似度: {maxDistance}");
            Console.WriteLine($"最小相似度: {minDistance}");

            // 获取相似特征点
            var pointsSrc = new List<Point2f>();  // 源图片的相似特征点
            var pointsDst = new List<Point2f>();  // 目标图片的相似特征点
            var goodMatches = new List<DMatch>();  // 匹配到的相似特征点
            for (int i = 0; i < matSrcRet.Rows; i++)
            {
                double distance = matches[i].Distance;
                if (distance < Math.Max(minDistance * 2, 0.02))
                {
                    pointsSrc.Add(keyPointsSrc[matches[i].QueryIdx].Pt);
                    pointsDst.Add(keyPointsTo[matches[i].TrainIdx].Pt);
                    //距离小于范围的压入新的DMatch
                    goodMatches.Add(matches[i]);
                }
            }

            outMat = new Mat();

            // 算法RANSAC对匹配的结果做过滤
            var pSrc = pointsSrc.ConvertAll(Point2fToPoint2d);
            var pDst = pointsDst.ConvertAll(Point2fToPoint2d);
            var outMask = new Mat();
            // 如果原始的匹配结果为空, 则跳过过滤步骤
            if (pSrc.Count > 0 && pDst.Count > 0)
                Cv2.FindHomography(pSrc, pDst, HomographyMethods.Ransac, mask: outMask);
            // 如果通过RANSAC处理后的匹配点大于10个,才应用过滤. 否则使用原始的匹配点结果(匹配点过少的时候通过RANSAC处理后,可能会得到0个匹配点的结果).
            if (outMask.Rows > 10)
            {
                byte[] maskBytes = new byte[outMask.Rows * outMask.Cols];
                outMask.GetArray(out maskBytes);
                Cv2.DrawMatches(matSrc, keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, matchesMask: maskBytes, flags: DrawMatchesFlags.NotDrawSinglePoints);
            }
            else
                Cv2.DrawMatches(matSrc, keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, flags: DrawMatchesFlags.NotDrawSinglePoints);

            return goodMatches;
        }
    }
}
posted @ 2024-07-11 11:05  ꧁执笔小白꧂  阅读(66)  评论(0编辑  收藏  举报