淘先锋技术网

首页 1 2 3 4 5 6 7

1:需求:在原图中,有多个特征点和模板图像一模一样,因此,寻找原图中中心位置最近的特征点位(模板匹配详解);原图如下所示:

模板图像:

                                                                        

2:现要求匹配找到距离原图中心位置最近的特征位置;例如,在下图中找到红色十字标记处。

 3:模板匹配,全局搜索相似特征位置,并且标记出,结果如下:

4:想要的结果位置,下图用红色方框标记:

 

 5:代码如下:

private Point MatchTemplate(Mat tempalte, Mat srcPic, double matchValue)
        {
            using (Mat result = new Mat()) //匹配结果
            {                             //模板匹配
                double minVul;
                double maxVul;
                Point tempPoint = new Point();
                OpenCvSharp.Point minLoc = new OpenCvSharp.Point(0, 0);
                OpenCvSharp.Point maxLoc = new OpenCvSharp.Point(0, 0);
                //OpenCvSharp.Point matchLoc = new OpenCvSharp.Point(0, 0);
                Cv2.MatchTemplate(srcPic, tempalte, result, TemplateMatchModes.CCoeffNormed);//CCoeffNormed  最好匹配为1,值越小匹配越差 xxxNormed的算法
                //Cv2.Normalize(result, result, 0, 1, NormTypes.MinMax, -1);//归一化
                Cv2.MinMaxLoc(result, out minVul, out maxVul, out minLoc, out maxLoc);//查找极值
                //matchLoc = maxLoc;//图像中左上角的坐标位置(应该叫min)
                //result.Set(matchLoc.Y, matchLoc.X, 0);//改变最大值为最小值  
                //Mat mask = srcPic.Clone();//复制整个矩阵
                //画框显示 :对角线画框,起点和终点,都用Point,线宽
                //if (maxVul > matchValue)
                //{
                //    Cv2.Rectangle(mask, matchLoc, new OpenCvSharp.Point(matchLoc.X + tempalte.Cols, matchLoc.Y + tempalte.Rows), Scalar.Green, 2);//2代表画的线条的宽细程度
                //}
                //循环查找 画框显示
                double minDistance = int.MaxValue;
                //double threshold = 0.71;
                int minLocX = int.MaxValue;
                int minLocY = int.MaxValue;
                Mat maskMulti = srcPic.Clone();//复制整个矩阵
                for (int i = 1; i < result.Rows - tempalte.Rows; i += tempalte.Rows)//行遍历
                {
                    for (int j = 1; j < result.Cols - tempalte.Cols; j += tempalte.Cols)//列遍历
                    {
                        Rect roi = new Rect(j, i, tempalte.Cols, tempalte.Rows);        //建立感兴趣
                        Mat RoiResult = new Mat(result, roi);
                        Cv2.MinMaxLoc(RoiResult, out minVul, out maxVul, out minLoc, out maxLoc);//查找极值
                        //matchLoc = maxLoc;//最大值坐标
                        if (maxVul > matchValue)
                        {
                            double dis = CalculateDistance((j + maxLoc.X), (i + maxLoc.Y), (result.Width / 2), (result.Height / 2));
                            if (dis < minDistance)
                            {
                                minDistance = dis;
                                minLocY = j + maxLoc.X;
                                minLocX = i + maxLoc.Y;
                                //    File.AppendAllText(System.AppDomain.CurrentDomain.BaseDirectory + @"Log\LogError\log" + DateTime.Now.ToString("yyyyMMdd")
                                //+ "imgInfo.txt", "minDistance:" + minDistance + "  maxLocX:" + minLocX + "  maxLocY" + minLocY + "\r\n");
                            }
                            //画框显示
                            Cv2.Rectangle(maskMulti, new OpenCvSharp.Point(j + maxLoc.X, i + maxLoc.Y), new OpenCvSharp.Point(j + maxLoc.X + tempalte.Cols, i + maxLoc.Y + tempalte.Rows), Scalar.Green, 2);
                            //string axis = '(' + Convert.ToString(i + maxLoc.Y) + ',' + Convert.ToString(j + maxLoc.X) + ')';
                            //Cv2.PutText(maskMulti, axis, new OpenCvSharp.Point(j + maxLoc.X, i + maxLoc.Y), HersheyFonts.HersheyPlain, 1, Scalar.Red, 1, LineTypes.Link4);
                        }
                    }
                }
                if (minDistance < int.MaxValue)
                {
                    tempPoint.X = minLocY;
                    tempPoint.Y = minLocX;
                }
                Cv2.ImWrite(@"save\" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".png", maskMulti);
                return tempPoint;//OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mask);
            }
        }

double CalculateDistance(double x1, double y1, double x2, double y2)
        {
            double distance = Math.Sqrt(Math.Pow(x2 - x1, 2) + Math.Pow(y2 - y1, 2));
            return distance;
        }

        如有不正确地方,欢迎指正。

附1:Cv2.MatchTemplate函数参数详解:

cv2.matchTemplate() 是模板匹配的函数之一,其参数如下:

image待匹配的图像,应该是一个灰度图像。
template模板图像,应该是一个灰度图像。
method匹配方法,有6种不同的方法可以选择,分别是:
cv2.TM_CCOEFF相关系数匹配法。
cv2.TM_CCOEFF_NORMED归一化的相关系数匹配法。
cv2.TM_CCORR相关匹配法。
cv2.TM_CCORR_NORMED归一化的相关匹配法。
cv2.TM_SQDIFF平方差匹配法。
cv2.TM_SQDIFF_NORMED归一化的平方差匹配法。
result匹配结果数组,必须是单通道32位浮点数类型的数组。
mask需要匹配的区域,可以不指定。
use_mask是否要使用掩膜,可以不指定。

其中,匹配方法是最重要的参数,不同的方法适用于不同的情况。下面介绍各种匹配方法的具体作用:

cv2.TM_CCOEFF相关系数匹配法。计算模板图像与待匹配图像的线性相关系数,该值越大则表示匹配度越高。适用于模板图像和待匹配图像的亮度差异较小的情况。
cv2.TM_CCOEFF_NORMED归一化的相关系数匹配法。计算模板图像与待匹配图像的归一化线性相关系数,该值越大则表示匹配度越高。适用于模板图像和待匹配图像的亮度差异较大的情况。
cv2.TM_CCORR相关匹配法。计算模板图像与待匹配图像的互相关系数,该值越大则表示匹配度越高。适用于模板图像和待匹配图像的亮度变化较小、平移变化较大的情况。
cv2.TM_CCORR_NORMED归一化的相关匹配法。计算模板图像与待匹配图像的归一化互相关系数,该值越大则表示匹配度越高。适用于模板图像和待匹配图像的亮度变化较大、平移变化较大的情况。
cv2.TM_SQDIFF平方差匹配法。计算模板图像与待匹配图像的平方差,该值越小则表示匹配度越高。适用于模板图像的亮度与待匹配图像有很大差异的情况。
cv2.TM_SQDIFF_NORMED归一化的平方差匹配法。计算模板图像与待匹配图像的归一化平方差,该值越小则表示匹配度越高。适用于模板图像的亮度与待匹配图像有较大差异的情况。

使用不同的匹配方法,可以适应不同的场景,提高匹配的准确性。需要根据具体情况选择合适的匹配方法。

附2:Cv2.MinMaxLoc函数参数详解:

cv2.minMaxLoc()是OpenCV中用于查找图像中最小值和最大值位置的函数,其参数如下:

src输入的单通道数组图像。
mask可选的掩膜。如果传入了掩膜,则只会查找掩膜图像中非零像素处的最值。
minVal返回最小值。
maxVal返回最大值。
minLoc返回最小值所在的位置。
maxLoc返回最大值所在的位置。

该函数能够在图像中查找最大值和最小值,以及它们的位置。其中,最大值和最小值的位置分别被保存在maxLocminLoc中,可以结合这些位置进行后续的图像处理工作。

问:Cv2.MatchTemplate方法之后使用到Cv2.MinMaxLoc函数,为什么?

答:在使用cv2.matchTemplate()方法进行模板匹配时,可能会存在多个匹配位置或存在误匹配的情况。因此,在匹配结果中需要用cv2.minMaxLoc()函数寻找最大值/最小值和它们的位置,以保证匹配结果的准确性。

具体来说,在使用cv2.matchTemplate()方法匹配模板图像和待匹配图像后,会生成一个匹配矩阵,其中每个数值表示这个位置的匹配程度。最大或最小值出现在匹配矩阵中,需要通过cv2.minMaxLoc()函数获取这些值对应的坐标。

例如,我们可以通过将匹配矩阵进行归一化(即获取矩阵中数值最大/最小值处的位置),来获取匹配度最高/最低的位置。这样可以排除掉一些误匹配的位置,提高匹配的准确性。