大图定位小图|裁剪小图与样本图对比相似度

/ C# / 0 条评论 / 7454浏览

需求简介

相机拍照截取某一区域作为样本图片。相机连续拍照,从新拍摄的图片中定位样本图片的位置,并比较样本图与新拍摄图片的相似度 对比图

如上图

bitmap.png 为新拍摄的图片,会持续拍摄。

sample.png 为样本图。

bitmap.png中定位sample.png的位置,截取与sample.png同等大小的区域并与sample.png做相似度对比,最终获取0-1之间的浮点数

开发环境

开发工具 Visual Studio 2015

图像对比工具 OpenCvSharp3-AnyCPU 3.4.4.20181118

安装OpenCvSharp

如下图 右键项目 → 管理NuGet程序包 → 浏览 → 输入opencvsharp3搜索opencv插件,点击安装,等待程序安装即可(大概耗时5分钟)

安装OpenCV插件

图片定位|对比

引入OpenCV

using OpenCvSharp;

定位样本位置

        public static System.Drawing.Point FindPicFromImage(Bitmap imgSrc, Bitmap imgSub, double threshold = 0.9)
        {
            OpenCvSharp.Mat srcMat = null;
            OpenCvSharp.Mat dstMat = null;
            OpenCvSharp.OutputArray outArray = null;
            try
            {
                srcMat = OpenCvSharp.Extensions.BitmapConverter.ToMat(imgSrc);
                dstMat = OpenCvSharp.Extensions.BitmapConverter.ToMat(imgSub);
                outArray = OpenCvSharp.OutputArray.Create(srcMat);
                OpenCvSharp.Cv2.MatchTemplate(srcMat, dstMat, outArray, TemplateMatchModes.CCoeff);
                double minValue, maxValue;
                OpenCvSharp.Point location, point;
                OpenCvSharp.Cv2.MinMaxLoc(OpenCvSharp.InputArray.Create(outArray.GetMat()), out minValue, out maxValue, out location, out point);
                Console.WriteLine(maxValue);
                if (maxValue >= threshold)
                    return new System.Drawing.Point(point.X, point.Y);
                return System.Drawing.Point.Empty;
            }
            catch (Exception ex)
            {
                return System.Drawing.Point.Empty;
            }
            finally
            {
                if (srcMat != null)
                    srcMat.Dispose();
                if (dstMat != null)
                    dstMat.Dispose();
                if (outArray != null)
                    outArray.Dispose();
            }
        }

SIFT对比相似度

        public static double MatchPicBySift(Bitmap imgSrc, Bitmap imgSub)
        {
            using (Mat matSrc = OpenCvSharp.Extensions.BitmapConverter.ToMat(imgSrc))
            using (Mat matTo = OpenCvSharp.Extensions.BitmapConverter.ToMat(imgSub))
            using (Mat matSrcRet = new Mat())
            using (Mat matToRet = new Mat())
            {
                KeyPoint[] keyPointsSrc, keyPointsTo;
                using (var sift = OpenCvSharp.XFeatures2D.SIFT.Create())
                {
                    sift.DetectAndCompute(matSrc, null, out keyPointsSrc, matSrcRet);
                    sift.DetectAndCompute(matTo, null, out keyPointsTo, matToRet);
                }
                using (var bfMatcher = new OpenCvSharp.BFMatcher())
                {
                    var matches = bfMatcher.KnnMatch(matSrcRet, matToRet, k: 1);
                    double maxDistance = 0;
                    foreach (DMatch[] items in matches)
                    {
                        maxDistance = Math.Max(maxDistance, items[0].Distance);
                    }
                    var goodMatches = new List<DMatch>();
                    foreach (DMatch[] items in matches)
                    {
                        if (items[0].Distance < 0.5 * maxDistance)
                        {
                            goodMatches.Add(items[0]);
                        }
                    }
                    if (matches.Count() == 0)
                    {
                        return 0.0;
                    }
                    return Math.Round(Math.Pow((double)goodMatches.Count / matches.Count(), 1.0 / 4), 2);
                }
            }
        }

实操代码

效果图

程序效果图

全局变量

Bitmap imageCut = null;

定位

    Bitmap bitmap = new Bitmap(Image.FromFile("C:\\xxx\\" + textBox1.Text));
    Bitmap sourceBitmap = new Bitmap(Image.FromFile("C:\\xxx\\" + textBox2.Text));
    System.Drawing.Point result = OpencvUtil.FindPicFromImage(sourceBitmap, bitmap, 0.95);
    Console.WriteLine("result=" + result.X + " , " + result.Y);

    //截图样图对应图片
    int x = result.X, y = result.Y;
    if (x < 0 || y < 0)
    {
        x = Convert.ToInt32(textLX.Text);
        y = Convert.ToInt32(textLY.Text);
    }
    Rectangle rect = new Rectangle(x, y, bitmap.Width, bitmap.Height);
    Bitmap b = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb);
    Graphics g = Graphics.FromImage(b);
    g.DrawImage(sourceBitmap, 0, 0, rect, GraphicsUnit.Pixel);
    g.Dispose();
    imageCut = b;
    bitmap.Dispose();
    sourceBitmap.Dispose();

对比

    String sFile = "C:\\xxx\\" + textBox1.Text;
    Bitmap image1 = new Bitmap(Image.FromFile(sFile));
    pictureBox1.Image = image1;

    Bitmap image2 = imageCut;
   

    pictureBox2.Image = image2;
    double rate = OpencvUtil.MatchPicBySift(image1, image2);

    Console.WriteLine(rate);
    label1.Text = "相似度:" + rate;

注意事项

  1. Bitmap 必须是 PixelFormat.Format32bppArgb 格式,否则报错

转换方式

private Bitmap to32Argb(Bitmap bitmap)
{
    Rectangle bitmapRect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
    Bitmap newBitmap = new Bitmap(bitmapRect.Width, bitmapRect.Height, PixelFormat.Format32bppArgb);
    Graphics g = Graphics.FromImage(newBitmap);
    g.DrawImage(bitmap, 0, 0, bitmapRect, GraphicsUnit.Pixel);
    g.Dispose();
    bitmap.Dispose();
    bitmap = null;
    return newBitmap;
}

参考文章

在C#中使用OpenCV(使用OpenCVSharp)

OpenCvSharp 通过特征点匹配图片(SIFT & SURF 算法)