【OpenCVSharp】多目标模板匹配MatchTemplate

建立测试程序

通过NuGet工具为工程添加OpenCvSharp3-AnyCPU:

  • 工具–NuGet包管理器—管理解决方案的NuGet程序包
    在这里插入图片描述

测试程序

打开位于\bin\Debug\netcoreapp2.1和dill文件同目录下的lenna.jpg。

先添加引用:using OpenCvSharp;

  		static void Main(string[] args)
        {
            Mat src = new Mat("lenna.jpg", ImreadModes.Grayscale);//小写的scale,不然报错
            Mat dst = new Mat();
            Mat dst1 = new Mat();

            Cv2.Canny(src, dst, 50, 200);
            Cv2.Add(src, dst, dst1);
            using (new Window("src image", src))
            using (new Window("dst image", dst))
            using (new Window("dst1 image", dst1))
            {
                Cv2.WaitKey();
            }
        }

测试成功之后进行函数理解和面向搜索引擎的编程
关于OpenCv的模板匹配函数matchTemplate,这些地方都讲得都比较清楚,但我第一天看,还没没看到在C#上实现的,所以只能一步步写,函数定义,模仿,解决报错。
友情链接:

C#模板匹配代码

static void Main(string[] args)
        {
            //模板图片
            Mat temp = new Mat("temp.jpg",ImreadModes.AnyColor);
            //被匹配图
            Mat wafer = new Mat("wafer.jpg", ImreadModes.AnyColor);
            //匹配结果
            Mat result=new Mat();
            //模板匹配
            Cv2.MatchTemplate(wafer,temp,result,TemplateMatchModes.CCoeffNormed);//最好匹配为1,值越小匹配越差
            //数组位置下x,y
            Point minLoc =new Point(0,0);
            Point maxLoc = new Point(0, 0);
            Point matchLoc = new Point(0, 0);
            Cv2.MinMaxLoc(result, out minLoc, out maxLoc);
            matchLoc = maxLoc;
            Mat mask = wafer.Clone();
            //画框显示
            Cv2.Rectangle(mask, matchLoc, new Point(matchLoc.X + temp.Cols, matchLoc.Y + temp.Rows),Scalar.Green, 2);
                
            //新建窗体显示图片
            using (new Window("temp image", temp))
            using (new Window("wafer image", wafer))
            using (new Window("mask image", mask))
            {
                Cv2.WaitKey();
            }
            

        }

结果

在这里插入图片描述

详解和补习

  • Point是个结构体,Point newPoint=new Point(int x,int y);,用于得到位置。
  • Cv2.Rectangle():对角线画框,起点和终点,都用Point,线宽
Cv2.Rectangle(img, (x1, y1), (x2, y2), (255,0,0), 2)//
x1,y1 ------
|          |
|          |
|          |
--------x2,y2

重载是怎么回事呢?overload是重载,一般是用于在一个类内实现若干重载的方法,这些方法的名称相同而参数形式不同。在这里插入图片描述

根据一定度量值画框标识出多个匹配结果

我们知道保存结果的矩阵是result,查找矩阵中在某一范围座标,就可以标识出多个结果。参数说明:
result –Map of comparison results. It must be single-channel 32-bit floating-point. If image is  and templ is  , then result is  .
把result打印出来如下图,CV_32FC1就代表数据是32位单通道浮点数
在这里插入图片描述
通过At函数可以获取某座标的值

    //
        // 摘要:
        //     Returns a value to the specified array element.
        //
        // 参数:
        //   i0:
        //     Index along the dimension 0
        //
        //   i1:
        //     Index along the dimension 1
        //
        // 类型参数:
        //   T:
        //
        // 返回结果:
        //     A value to the specified array element.
        public T At<T>(int i0, int i1) where T : struct;

通过Set函数可以设置某座标的值

//
        // 摘要:
        //     Set a value to the specified array element.
        //
        // 参数:
        //   i0:
        //     Index along the dimension 0
        //
        //   i1:
        //     Index along the dimension 1
        //
        //   value:
        //
        // 类型参数:
        //   T:
        public void Set<T>(int i0, int i1, T value) where T : struct;

注意行和列的区分,matchLoc是和习惯相反的。Y是行座标,X是列座标

在这里插入图片描述
在这里插入图片描述

那么我们就可以通过遍历一遍result里面每个座标点的值,如果大于阈值,那么就进行画框显示。一般还要先经过归一化。还有看到一个人是每次都把最大值设为0,再用一次MinMaxLoc查找。

我们还可以画出座标

void cv::putText(
		cv::Mat& img, // 待绘制的图像
		const string& text, // 待绘制的文字
		cv::Point origin, // 文本框的左下角
		int fontFace, // 字体 (如cv::FONT_HERSHEY_PLAIN)
		double fontScale, // 尺寸因子,值越大文字越大
		cv::Scalar color, // 线条的颜色(RGB)
		int thickness = 1, // 线条宽度
		int lineType = 8, // 线型(4邻域或8邻域,默认8邻域)
		bool bottomLeftOrigin = false // true='origin at lower left'
	);

        static void Main(string[] args)
        {
            //读取图片
            Mat temp = new Mat("temp.jpg",ImreadModes.AnyColor);//模板图片
            //Cv2.ImWrite("temp_write.jpg",temp);//写入图片
            Mat wafer = new Mat("wafer.jpg", ImreadModes.AnyColor);//被匹配图        
            Mat result=new Mat(); //匹配结果
            //模板匹配
            Cv2.MatchTemplate(wafer,temp,result,TemplateMatchModes.CCoeffNormed);//最好匹配为1,值越小匹配越差
            Double minVul;
            Double maxVul;
            Point minLoc =new Point(0,0);
            Point maxLoc = new Point(0, 0);
            Point matchLoc = new Point(0, 0);
            Cv2.Normalize(result, result,0, 1,NormTypes.MinMax,-1);//归一化
            Cv2.MinMaxLoc(result, out minVul, out maxVul, out minLoc, out maxLoc);//查找极值
            matchLoc = maxLoc;//最大值座标
            //result.Set(matchLoc.Y, matchLoc.X, 0);//改变最大值为最小值  
            Mat mask = wafer.Clone();//复制整个矩阵
            //画框显示
            Cv2.Rectangle(mask, matchLoc, new Point(matchLoc.X + temp.Cols, matchLoc.Y + temp.Rows),Scalar.Green, 2);

            Console.WriteLine("最大值:{0},X:{1},Y:{2}", maxVul, matchLoc.Y, matchLoc.X);
            Console.WriteLine("At获取最大值(Y,X):{0}", result.At<float>(matchLoc.Y, matchLoc.X));
            Console.WriteLine("result的类型:{0}", result.GetType());
            
            //循环查找画框显示
            Double threshold = 0.91;
            Mat maskMulti = wafer.Clone();//复制整个矩阵
            for (int i=1; i<result.Rows; i++)//行遍历
            {

                for (int j = 1; j < result.Cols; j++)//列遍历
                {
                    // Console.WriteLine("({0},{1}):{2}",i,j, result.At<float>(j, i));
                    if (result.At<float>(i, j) > threshold)
                    {
                        Cv2.Rectangle(maskMulti, new Point(j, i), new Point(j + temp.Cols, i + temp.Rows), Scalar.Green, 2);
                        //画出座标
                        string axis = '(' + Convert.ToString(i) + ',' + Convert.ToString(j) + ')';
                        Cv2.PutText(maskMulti, axis, new Point(j, i), HersheyFonts.HersheyPlain, 1, Scalar.Red, 1, LineTypes.Link4);

                    }

                }
            }


            //新建窗体显示图片
            using (new Window("temp image", temp))
            using (new Window("wafer image", wafer))
            using (new Window("mask image", mask))
            using (new Window("maskMulti image", maskMulti))
            {
                Cv2.WaitKey();
            }
            

        }

在这里插入图片描述

模板匹配可分为两类

  • 基于图像灰度相关的模板匹配
  • 基于几何特征的模板匹配

第一种即为现在所用到的,其缺点

  • 由于其算法特性,无法匹配超过一定角度的
  • 对于筛选阈值获取多处匹配时,降低阈值时,在一处周围会出现重复匹配的效果

但是我们也可以通过建立感兴区,在里面找最大,再判断是否大于阈值,再移动感兴趣,可解决重复匹配问题。


        static void Main(string[] args)
        {
            //读取图片
            Mat temp = new Mat("temp.jpg",ImreadModes.AnyColor);//模板图片
            //Cv2.ImWrite("temp_write.jpg",temp);//写入图片
            Mat wafer = new Mat("wafer.jpg", ImreadModes.AnyColor);//被匹配图        
            Mat result=new Mat(); //匹配结果
            //模板匹配
            Cv2.MatchTemplate(wafer,temp,result,TemplateMatchModes.CCoeffNormed);//最好匹配为1,值越小匹配越差
            Double minVul;
            Double maxVul;
            Point minLoc =new Point(0,0);
            Point maxLoc = new Point(0, 0);
            Point matchLoc = new Point(0, 0);
            Cv2.Normalize(result, result,0, 1,NormTypes.MinMax,-1);//归一化
            Cv2.MinMaxLoc(result, out minVul, out maxVul, out minLoc, out maxLoc);//查找极值
            matchLoc = maxLoc;//最大值座标
            //result.Set(matchLoc.Y, matchLoc.X, 0);//改变最大值为最小值  
            Mat mask = wafer.Clone();//复制整个矩阵
            //画框显示
            Cv2.Rectangle(mask, matchLoc, new Point(matchLoc.X + temp.Cols, matchLoc.Y + temp.Rows),Scalar.Green, 2);

            Console.WriteLine("最大值:{0},X:{1},Y:{2}", maxVul, matchLoc.Y, matchLoc.X);
            Console.WriteLine("At获取最大值(Y,X):{0}", result.At<float>(matchLoc.Y, matchLoc.X));
            Console.WriteLine("result的类型:{0}", result.GetType());
            
            //循环查找画框显示
            Double threshold = 0.91;
            Mat maskMulti = wafer.Clone();//复制整个矩阵
    
            for (int i=1; i<result.Rows- temp.Rows; i+= temp.Rows)//行遍历
            {

                for (int j = 1; j < result.Cols- temp.Cols; j+= temp.Cols)//列遍历
                {
                    Rect roi = new Rect(j,i, temp.Cols, temp.Rows);        //建立感兴趣
                    Mat RoiResult = new Mat(result,roi);
                    Cv2.MinMaxLoc(RoiResult, out minVul, out maxVul, out minLoc, out maxLoc);//查找极值
                    matchLoc = maxLoc;//最大值座标
                    if (maxVul > threshold)
                    {
                      
                        //画框显示
                        Cv2.Rectangle(maskMulti, new Point(j+maxLoc.X,i+maxLoc.Y), new Point(j + maxLoc.X + temp.Cols, i + maxLoc.Y + temp.Rows), Scalar.Green, 2);
                        string axis = '(' + Convert.ToString(i + maxLoc.Y) + ',' + Convert.ToString(j + maxLoc.X) + ')';
                        Cv2.PutText(maskMulti, axis, new Point(j + maxLoc.X, i + maxLoc.Y), HersheyFonts.HersheyPlain, 1, Scalar.Red, 1, LineTypes.Link4);
                        
                    }

                }
            }


            //新建窗体显示图片
            using (new Window("temp image", temp))
            using (new Window("wafer image", wafer))
            using (new Window("mask image", mask))
            using (new Window("maskMulti image", maskMulti))
            {
                Cv2.WaitKey();
            }
            

        }

在这里插入图片描述
在这种芯片定位任务中或简单的缺陷识别任务中最好不要使用这种方法,鲁棒性太差。

参考文献:
[1]徐亚坤. 基于机器视觉的LED芯片识别与产品检测系统[D].南昌大学,2018.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章