C# 图像模板匹配并标注

目录

需求

功能演示

核心代码

说明


​​​​​​​

需求

这个是粉丝在我的技术群提的一个需求

1、 模板匹配 :

功能:

  (1)在一张大图像中,选取一小块区域作为模板

  (2)可在大图像中匹配到模板图像和位置。

模板匹配是图像处理中最基本、最常用的匹配方法。目前我司用hacon去做的,还进行了二次封装,可以设置图片的旋转角度等信息,这个设计公司机密,这里我就用opencv(NET封装版叫emgucv)去实现这个功能。

功能演示

核心代码​​​​​​​

using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using PropertyChanged;
using System;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Caliburn.Micro.Hello
{
    [AddINotifyPropertyChangedInterface]
    public class MatchTemplateViewModel: IViewModel
    {
        public ImageSource TemplateImage { get; set; }
        public string TemplateImagePath { get; set; }
        public ImageSource MarkImage { get; set; }
        public string  MarkImagePath { get; set; }
        public string ResultString { get; set; }
        public  void  MatchTemplate()
        {
            Mat src = CvInvoke.Imread(TemplateImagePath, LoadImageType.AnyColor);//从本地读取图片
            Mat result = src.Clone();

            Mat tempImg = CvInvoke.Imread(MarkImagePath, LoadImageType.AnyColor);
            int matchImg_rows = src.Rows - tempImg.Rows + 1;
            int matchImg_cols = src.Cols - tempImg.Cols + 1;
            Mat matchImg = new Mat(matchImg_rows, matchImg_rows, DepthType.Cv32F, 1); //存储匹配结果
             #region 模板匹配参数说明
            采用系数匹配法,匹配值越大越接近准确图像。
            IInputArray image:输入待搜索的图像。图像类型为8位或32位浮点类型。设图像的大小为[W, H]。
            IInputArray templ:输入模板图像,类型与待搜索图像类型一致,并且大小不能大于待搜索图像。设图像大小为[w, h]。
            IOutputArray result:输出匹配的结果,单通道,32位浮点类型且大小为[W - w + 1, H - h + 1]。
            TemplateMatchingType method:枚举类型标识符,表示匹配算法类型。
            Sqdiff = 0 平方差匹配,最好的匹配为 0。
            SqdiffNormed = 1 归一化平方差匹配,最好效果为 0。
            Ccorr = 2 相关匹配法,数值越大效果越好。
            CcorrNormed = 3 归一化相关匹配法,数值越大效果越好。
            Ccoeff = 4 系数匹配法,数值越大效果越好。
            CcoeffNormed = 5 归一化系数匹配法,数值越大效果越好。
            #endregion
            CvInvoke.MatchTemplate(src, tempImg, matchImg, TemplateMatchingType.CcoeffNormed);
            #region 归一化函数参数说明
            IInputArray src:输入数据。
            IOutputArray dst:进行归一化后输出数据。
            double alpha = 1; 归一化后的最大值,默认为 1。
            double beta = 0:归一化后的最小值,默认为 0。
            #endregion
            CvInvoke.Normalize(matchImg, matchImg, 0, 1, NormType.MinMax, matchImg.Depth); //归一化
            double minValue = 0.0, maxValue = 0.0;
            Point minLoc = new Point();
            Point maxLoc = new Point();
            #region 极值函数参数说明
            IInputArray arr:输入数组。
            ref double minVal:输出数组中的最小值。
            ref double maxVal; 输出数组中的最大值。
            ref Point minLoc:输出最小值的坐标。
            ref Point maxLoc; 输出最大值的坐标。
            IInputArray mask = null:蒙版。
            #endregion
            CvInvoke.MinMaxLoc(matchImg, ref minValue, ref maxValue, ref minLoc, ref maxLoc);

            StringBuilder tb_result = new StringBuilder();
            tb_result.Append("min=" + minValue + ",max=" + maxValue);
            tb_result.Append(Environment.NewLine);
            tb_result.Append("最小值坐标:\n" + minLoc.ToString());
            tb_result.Append(Environment.NewLine);
            tb_result.Append("最大值坐标:\n" + maxLoc.ToString());
            ResultString = tb_result.ToString();
            //Console.WriteLine(tb_result);
            CvInvoke.Rectangle(src, new Rectangle(maxLoc, tempImg.Size), new MCvScalar(0, 0, 255), 3);//绘制矩形,匹配得到的效果。

            CvInvoke.Imshow("result", src);
            CvInvoke.WaitKey(0);
        }

        /// <summary>
        /// 加载模板图片
        /// </summary>
        public void LoadTemplateImage()
        {
            TemplateImage = LoadImage(ImageLoadType.TemplateImage);
        }

        /// <summary>
        /// 加载标记图片
        /// </summary>
        public void LoadMarkImage()
        {
            MarkImage = LoadImage(ImageLoadType.MarkImage);
        }
        public ImageSource LoadImage(ImageLoadType imageType )
        {
            OpenFileDialog openFileDialog1 = new OpenFileDialog();
            openFileDialog1.Filter = "图片|*.jpg;*.jpeg;*.bmp;*.png;*.gif";
            openFileDialog1.FilterIndex = 1;//当前使用第二个过滤字符串
            openFileDialog1.RestoreDirectory = true;//对话框关闭时恢复原目录
            openFileDialog1.Multiselect = false;
            openFileDialog1.Title = "选择文件";
            ImageSource iSouce = null;
            try
            {
                if (openFileDialog1.ShowDialog() == DialogResult.OK)
                {
                    iSouce = LoadImageFreeze(openFileDialog1.FileName);//加载显示完成需要释放
                    switch(imageType)
                    {
                        case ImageLoadType.MarkImage:
                            MarkImagePath = openFileDialog1.FileName;break;
                        case ImageLoadType.TemplateImage:
                            TemplateImagePath = openFileDialog1.FileName; break;
                        default: break;
                    }
                    return iSouce;
                }
                return null;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[MatchTemplateViewModel]:Load() execute error:{ex}");
                return null;
            }
        }

        /// <summary>
        /// 图片加载显示完成后释放
        /// </summary>
        /// <param name="imagePath"></param>
        /// <returns></returns>
        public static BitmapImage LoadImageFreeze(string imagePath)
        {
            try
            {
                BitmapImage bitmap = new BitmapImage();
                if (File.Exists(imagePath))
                {
                    bitmap.BeginInit();
                    bitmap.CacheOption = BitmapCacheOption.OnLoad;

                    using (Stream ms = new MemoryStream(File.ReadAllBytes(imagePath)))
                    {
                        bitmap.StreamSource = ms;
                        bitmap.EndInit();
                        bitmap.Freeze();
                    }
                }
                return bitmap;
            }
            catch (Exception)
            {
                return null;
            }
        }

    }
}

说明

界面分别加载模板图片和标记图片,然后点击匹配按钮进行匹配,匹配结果在模板图片上用矩形标注,并把位置信息显示在界面上

①在NUGET上安装emgucv库:我这里适应的是3.1。0.1,注意emgucv每个版本不兼容

②模板匹配接口MatchTemplate说明,详细注释代码里面都有​​​​​​​

 #region 模板匹配参数说明
            采用系数匹配法,匹配值越大越接近准确图像。
            IInputArray image:输入待搜索的图像。图像类型为8位或32位浮点类型。设图像的大小为[W, H]。
            IInputArray templ:输入模板图像,类型与待搜索图像类型一致,并且大小不能大于待搜索图像。设图像大小为[w, h]。
            IOutputArray result:输出匹配的结果,单通道,32位浮点类型且大小为[W - w + 1, H - h + 1]。
            TemplateMatchingType method:枚举类型标识符,表示匹配算法类型。
            Sqdiff = 0 平方差匹配,最好的匹配为 0。
            SqdiffNormed = 1 归一化平方差匹配,最好效果为 0。
            Ccorr = 2 相关匹配法,数值越大效果越好。
            CcorrNormed = 3 归一化相关匹配法,数值越大效果越好。
            Ccoeff = 4 系数匹配法,数值越大效果越好。
            CcoeffNormed = 5 归一化系数匹配法,数值越大效果越好。
            #endregion
            CvInvoke.MatchTemplate(src, tempImg, matchImg, TemplateMatchingType.CcoeffNormed);

③外部加载 图片,加载显示完成后释放,返回BitmapImage 可以直接赋值给wpf控件的ImageSource​​​​​​​

public static BitmapImage LoadImageFreeze(string imagePath)
        {
            try
            {
                BitmapImage bitmap = new BitmapImage();
                if (File.Exists(imagePath))
                {
                    bitmap.BeginInit();
                    bitmap.CacheOption = BitmapCacheOption.OnLoad;

                    using (Stream ms = new MemoryStream(File.ReadAllBytes(imagePath)))
                    {
                        bitmap.StreamSource = ms;
                        bitmap.EndInit();
                        bitmap.Freeze();
                    }
                }
                return bitmap;
            }
            catch (Exception)
            {
                return null;
            }
        }

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值