C# OpenCV实现形状匹配的方法详解

1. 多角度模板匹配测试效果如下图:

图1-1

图1-2

图1-3

正负角度均可正常识别,识别角度偏差<1°

2. 下面分享一下开发过程:

a). ROI区域的生成,基于GDI+完成图形绘制,如图

绘制模板设置区域,用来生成需要的模板特征。

ROI区域绘制代码如下:

        /// <summary>
        /// 区域绘制
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="regionEx"></param>
        /// <param name="sizeratio"></param>
       public  static void drawRegion(this Graphics graphics, RegionEx regionEx,float sizeratio=1)
        {
            if (regionEx?.Region is RectangleF)
            {
                RectangleF rect = (RectangleF)regionEx.Region;
                graphics.DrawRectangle(new Pen(regionEx.Color, regionEx.Size), rect.X / sizeratio, rect.Y / sizeratio,
                                                    rect.Width / sizeratio, rect.Height / sizeratio);
            }
            else if(regionEx?.Region is RotatedRectF)
            {
                RotatedRectF rrect = (RotatedRectF)regionEx.Region;

                using (var graph = new GraphicsPath())
                {
                    PointF Center = new PointF(rrect.cx / sizeratio, rrect.cy / sizeratio);

                    graph.AddRectangle(new RectangleF( rrect.getrectofangleEqualZero().X / sizeratio,
                        rrect.getrectofangleEqualZero().Y / sizeratio,
                        rrect.getrectofangleEqualZero().Width / sizeratio,
                        rrect.getrectofangleEqualZero().Height / sizeratio));
                    graph.AddLine(new PointF((rrect.cx - rrect.Width / 2) / sizeratio, rrect.cy / sizeratio),
                                 new PointF((rrect.cx + rrect.Width/2) / sizeratio, rrect.cy / sizeratio));
                    /
                    RotatedRectF rotatedRectF = new RotatedRectF((rrect.cx + rrect.Width / 2) / sizeratio,
                        rrect.cy / sizeratio,20 / sizeratio, 10 / sizeratio, 0);
                    PointF[] point2Fs = rotatedRectF.getPointF();
                    graph.AddLine(new PointF((rrect.cx + rrect.Width / 2) / sizeratio,
                        rrect.cy / sizeratio), new PointF(point2Fs[0].X, point2Fs[0].Y));
                    graph.AddLine(new PointF((rrect.cx + rrect.Width / 2) / sizeratio,
                       rrect.cy / sizeratio), new PointF(point2Fs[3].X, point2Fs[3].Y));
                    /
                    var a = rrect.angle * (Math.PI / 180);
                    var n1 = (float)Math.Cos(a);
                    var n2 = (float)Math.Sin(a);
                    var n3 = -(float)Math.Sin(a);
                    var n4 = (float)Math.Cos(a);
                    var n5 = (float)(Center.X * (1 - Math.Cos(a)) + Center.Y * Math.Sin(a));
                    var n6 = (float)(Center.Y * (1 - Math.Cos(a)) - Center.X * Math.Sin(a));
                    graph.Transform(new Matrix(n1, n2, n3, n4, n5, n6));
                    graphics.DrawPath(new Pen(regionEx.Color, regionEx.Size), graph);

                }
            }
            else if (regionEx?.Region is RotatedCaliperRectF)
            {
                RotatedCaliperRectF rrect = (RotatedCaliperRectF)regionEx.Region;

                using (var graph = new GraphicsPath())
                {
                    PointF Center = new PointF(rrect.cx / sizeratio, rrect.cy / sizeratio);

                    graph.AddRectangle(new RectangleF(rrect.getrectofangleEqualZero().X / sizeratio,
                        rrect.getrectofangleEqualZero().Y / sizeratio,
                        rrect.getrectofangleEqualZero().Width / sizeratio,
                        rrect.getrectofangleEqualZero().Height / sizeratio));
                    graph.AddLine(new PointF((rrect.cx - rrect.Width / 2) / sizeratio, rrect.cy / sizeratio),
                                 new PointF((rrect.cx + rrect.Width / 2) / sizeratio, rrect.cy / sizeratio));
                    /
                    RotatedCaliperRectF rotatedRectF = new RotatedCaliperRectF((rrect.cx + rrect.Width / 2) / sizeratio,
                        rrect.cy / sizeratio, 20 / sizeratio, 10 / sizeratio, 0);
                    PointF[] point2Fs = rotatedRectF.getPointF();
                    graph.AddLine(new PointF((rrect.cx + rrect.Width / 2) / sizeratio,
                        rrect.cy / sizeratio), new PointF(point2Fs[0].X, point2Fs[0].Y));
                    graph.AddLine(new PointF((rrect.cx + rrect.Width / 2) / sizeratio,
                       rrect.cy / sizeratio), new PointF(point2Fs[3].X, point2Fs[3].Y));
                    /
                    var a = rrect.angle * (Math.PI / 180);
                    var n1 = (float)Math.Cos(a);
                    var n2 = (float)Math.Sin(a);
                    var n3 = -(float)Math.Sin(a);
                    var n4 = (float)Math.Cos(a);
                    var n5 = (float)(Center.X * (1 - Math.Cos(a)) + Center.Y * Math.Sin(a));
                    var n6 = (float)(Center.Y * (1 - Math.Cos(a)) - Center.X * Math.Sin(a));
                    graph.Transform(new Matrix(n1, n2, n3, n4, n5, n6));
                    graphics.DrawPath(new Pen(regionEx.Color, regionEx.Size), graph);

                }
            }
            else if (regionEx?.Region is CircleF)
            {
                CircleF circle = (CircleF)regionEx.Region;
                graphics.DrawEllipse(new Pen(regionEx.Color, regionEx.Size), (circle.Centerx - circle.Radius) / sizeratio,
                      (circle.Centery - circle.Radius) / sizeratio, 2 * circle.Radius / sizeratio, 2 * circle.Radius / sizeratio);

            }
            else if (regionEx?.Region is PointF)
            {
                PointF point = (PointF)regionEx.Region;
                graphics.DrawPolygon(new Pen(regionEx.Color, regionEx.Size), new PointF[] { new PointF (
                    point.X/sizeratio,point.Y/sizeratio
                    )});
            }
            else if (regionEx?.Region is PolygonF)
            {
                PolygonF polygon = (PolygonF)regionEx.Region;
                List<PointF> temlist = new List<PointF>();
                foreach (var s in polygon.Points)
                    temlist.Add(new PointF(s.X / sizeratio, s.Y / sizeratio));
                graphics.DrawPolygon(new Pen(regionEx.Color, regionEx.Size), temlist.ToArray());
            }
            else if (regionEx?.Region is LineF)
            {
                LineF line = (LineF)regionEx.Region;
                graphics.DrawLine(new Pen(regionEx.Color, regionEx.Size), line.x1/ sizeratio, line.y1/ sizeratio,
                   line.x2/ sizeratio, line.y2/ sizeratio);
            }
            else if (regionEx?.Region is CrossF)
            {
                CrossF cross = (CrossF)regionEx.Region;
                graphics.DrawLine(new Pen(regionEx.Color, regionEx.Size), (cross.x1- cross.width/2) / sizeratio, cross.y1 / sizeratio,
                  (cross.x1 + cross.width / 2) / sizeratio, cross.y1 / sizeratio);
                graphics.DrawLine(new Pen(regionEx.Color, regionEx.Size), cross.x1 / sizeratio, (cross.y1- cross.height/2) / sizeratio,
                  cross.x1 / sizeratio, (cross.y1 + cross.height / 2) / sizeratio);
                graphics.DrawEllipse(new Pen(regionEx.Color, regionEx.Size), (cross.x1 - cross.radius) / sizeratio,
                       (cross.y1 - cross.radius) / sizeratio, 2 * cross.radius / sizeratio, 2 * cross.radius / sizeratio);
            }
            else if(regionEx?.Region is SectorF)
            {
                SectorF sectorF=(SectorF)regionEx.Region;

                //graphics.DrawEllipse(MyPens.assist, sectorF.x / sizeratio, sectorF.y / sizeratio,
                //  sectorF.width / sizeratio, sectorF.height / sizeratio);

                graphics.DrawPie(new Pen(regionEx.Color, regionEx.Size),
                    sectorF.x / sizeratio, sectorF.y / sizeratio,
                    sectorF.width / sizeratio, sectorF.height / sizeratio,
                    sectorF.startAngle, sectorF.sweepAngle);
            }
            else if (regionEx?.Region is Region)
            {
                Region unionRegion = (Region)regionEx?.Region;
                //RectangleF rectangleF = unionRegion.GetBounds(graphics);

                //Matrix matrix = new Matrix();
                //matrix.Scale(1/sizeratio, 1/sizeratio);
                //unionRegion.Transform(matrix);

                //RectangleF rectangleF2= unionRegion.GetBounds(graphics);

                graphics.FillRegion(Brushes.Orange, unionRegion);

            }
            else
                ;
        }

b). 模板创建

模板如图:

选择稳定唯一的形状特征,设置合适的参数,用来生成模板,此基础版生成的特征为闭合的轮廓,后期版本会推出非闭合的多轮廓形状匹配算法。

模板创建代码如下:

         //创建模板
        private void btncreateModel_Click(object sender, EventArgs e)
        {
            if (GrabImg == null || GrabImg.Width <= 0)
            {
                MessageBox.Show("未获取图像");
                return;
            }

            List<RectangleF> roiList = currvisiontool.getRoiList<RectangleF>();
            if (roiList.Count <= 0)
            {
                MessageBox.Show("请设置模板创建区域{矩形}");
                return;

            }
            if (MessageBox.Show("确认创建新模板?", "Info", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
                                  == DialogResult.Yes)
            {
                CVRect cVRect = new CVRect((int)roiList[0].X, (int)roiList[0].Y, (int)roiList[0].Width, (int)roiList[0].Height);
                Mat tp = MatExtension.Crop_Mask_Mat(GrabImg, cVRect);

                templateContour = null;
                coutourLen = 100;
                NumMincoutourLen.Value=100;
                contourArea = 100;
                NumMinContourArea.Value=100;
                double modelx = 0, modely = 0;

                runTool = new ShapeMatchTool();
                parmaData = new ShapeMatchData();
                (parmaData as ShapeMatchData).Segthreshold = (double)NumSegthreshold.Value;

                modeltp = (runTool as ShapeMatchTool).CreateTemplateContours(tp,
                     (parmaData as ShapeMatchData).Segthreshold, cVRect,
                    ref templateContour,
                    ref coutourLen, ref contourArea, ref modelx, ref modely, ref modelangle);

                picTemplate.Image = BitmapConverter.ToBitmap(modeltp);
                if (templateContour == null)
                {
                    MessageBox.Show("模板创建失败!");
                    return;
                }
                modelx += cVRect.X;
                modely += cVRect.Y;
                lIstModelInfo.Items.Clear();
                lIstModelInfo.Items.Add(new ListViewItem(
                    new string[] { "BaseX", modelx.ToString("f3") }));
                lIstModelInfo.Items.Add(new ListViewItem(
                  new string[] { "BaseY", modely.ToString("f3") }));
                lIstModelInfo.Items.Add(new ListViewItem(
                  new string[] { "BaseAngle", modelangle.ToString("f3") }));
                lIstModelInfo.Items.Add(new ListViewItem(
                 new string[] { "ContourLength", coutourLen.ToString("f3") }));
                lIstModelInfo.Items.Add(new ListViewItem(
                 new string[] { "ContourArea", contourArea.ToString("f3") }));

                modelOrigion = string.Format("{0},{1},{2}",
                      modelx.ToString("f3"),
                          modely.ToString("f3"),
                              modelangle.ToString("f3"));

              if(coutourLen * 0.8> (double)NumMincoutourLen.Maximum||
                    contourArea * 0.8> (double)NumMinContourArea.Maximum)
                {
                    MessageBox.Show("模板创建完成失败,模板区域过大!");
                    return;
                }
                NumMincoutourLen.Value = (decimal)(coutourLen *0.8);
                NumMaxcoutourLen.Value = (decimal)(coutourLen *1.2);

                NumMinContourArea.Value = (decimal)(contourArea * 0.8);
                NumMaxContourArea.Value = (decimal)(contourArea * 1.2);

                NumMatchValue.Value = (decimal)0.5;
                MessageBox.Show("模板创建完成!");
            }

        }

c). 模板匹配

多角度轮廓匹配算法,同时通过钜来获取中心,和角度

  //模板匹配
        void TestModelMatch()
        {
            if (GrabImg == null || GrabImg.Width <= 0)
            {
                stuModelMatchData.runFlag = false;
                MessageBox.Show("未获取图像");
                return;
            }

            if (templateContour == null)
            {
                stuModelMatchData.runFlag = false;
                MessageBox.Show("模板不存在,请先创建模板!");
                return;
            }
            runTool = new ShapeMatchTool();
            parmaData = new ShapeMatchData();
            (parmaData as ShapeMatchData).tpContour = templateContour;
            (parmaData as ShapeMatchData).Segthreshold = (double)NumSegthreshold.Value;
            (parmaData as ShapeMatchData).MatchValue = (double)NumMatchValue.Value;
            (parmaData as ShapeMatchData).MincoutourLen = (int)NumMincoutourLen.Value;
            (parmaData as ShapeMatchData).MaxcoutourLen = (int)NumMaxcoutourLen.Value;
            (parmaData as ShapeMatchData).MinContourArea = (int)NumMinContourArea.Value;
            (parmaData as ShapeMatchData).MaxContourArea = (int)NumMaxContourArea.Value;
            (parmaData as ShapeMatchData).baseAngle = modelangle;

            ResultOfToolRun = runTool.Run<ShapeMatchData>(GrabImg, parmaData as ShapeMatchData);

            currvisiontool.clearAll();
            currvisiontool.dispImage(ResultOfToolRun.resultToShow);

            ShapeMatchResult shapeMatchResult = ResultOfToolRun as ShapeMatchResult;

            if (shapeMatchResult.scores.Count <= 0)
            {
                currvisiontool.DrawText(new TextEx("模板匹配失败!") {x=1000,y=10, brush = new SolidBrush(Color.Red) });

                currvisiontool.AddTextBuffer(new TextEx("模板匹配失败!") { x = 1000, y = 10, brush = new SolidBrush(Color.Red) });

                stuModelMatchData.runFlag = false;
                return;
            }
            currvisiontool.DrawText(new TextEx("得分:" + shapeMatchResult.scores[0].ToString("f3")));
            currvisiontool.AddTextBuffer(new TextEx("得分:" + shapeMatchResult.scores[0].ToString("f3")));

            currvisiontool.DrawText(new TextEx("偏转角度:" + shapeMatchResult.rotations[0].ToString("f3")) { x = 10, y = 100 });
            currvisiontool.AddTextBuffer(new TextEx("偏转角度:" + shapeMatchResult.rotations[0].ToString("f3")) { x = 10, y = 100 });

            currvisiontool.DrawText(new TextEx(string.Format("匹配点位X:{0},Y:{1}", shapeMatchResult.positions[0].X.ToString("f3"),
                shapeMatchResult.positions[0].Y.ToString("f3")))
            { x = 10, y = 200 });
            currvisiontool.AddTextBuffer(new TextEx(string.Format("匹配点位X:{0},Y:{1}", shapeMatchResult.positions[0].X.ToString("f3"),
                shapeMatchResult.positions[0].Y.ToString("f3")))
            { x = 10, y = 200 });

            stuModelMatchData.matchPoint = shapeMatchResult.positions[0];
            stuModelMatchData.matchOffsetAngle = shapeMatchResult.rotations[0];
            stuModelMatchData.matchScore = shapeMatchResult.scores[0];
            stuModelMatchData.runFlag = true;

        }

3. 关键部位代码如下,包含模板创建,模板多角度匹配等

a)创建形状轮廓模板核心代码如下:

        /// <summary>
        /// 创建形状轮廓模板
        /// </summary>
        /// <param name="img_template">模板图像</param>
        ///  <param name="Segthreshold">分割阈值</param>
        /// <param name="templateContour">模板轮廓</param>
        /// <param name="coutourLen">模板轮廓长度</param>
        /// <param name="contourArea">模板轮廓面积</param>
        ///  <param name="modelx">模板轮廓X</param>
        ///   <param name="modely">模板轮廓Y</param>
        ///    <param name="modelangle">模板轮廓角度</param>
        /// <returns>返回绘制图</returns>
        public Mat CreateTemplateContours(Mat img_template,double Segthreshold, CVRect boundingRect,
            ref CVPoint[] templateContour, ref double coutourLen, ref double contourArea,
            ref double modelx,ref double modely,ref double modelangle)
        {
            //灰度化
            //Mat gray_img_template = new Mat();
            //Cv2.CvtColor(img_template, gray_img_template, ColorConversionCodes.BGR2GRAY);

            //阈值分割
            Mat thresh_img_template = new Mat();
            Cv2.Threshold(img_template, thresh_img_template, Segthreshold, 255, ThresholdTypes.Binary);
            //开运算处理,提出白色噪点
            Mat ellipse = Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(3, 3));
            Cv2.MorphologyEx(thresh_img_template, thresh_img_template, MorphTypes.Open, ellipse);

            //Mat cannyMat = new Mat();
            //Cv2.Canny(thresh_img_template, cannyMat, Segthreshold, 255);

            //寻找边界
            CVPoint[][] contours_template;
            //Vector<Vector<CVPoint>> contours_template=new Vector<Vector<CVPoint>>();
            //Vector<Vec4i> hierarchy=new Vector<Vec4i>();
        //    HierarchyIndex[] hierarchy;
            Cv2.FindContours(thresh_img_template, out contours_template, out _, RetrievalModes.Tree,
                ContourApproximationModes.ApproxNone);

            CVPoint[][] ExceptContours = ContourOperate.ExceptBoundPoints(img_template.BoundingRect(), contours_template);

            int count = ExceptContours.ToList<CVPoint[]>().Count;
            List<CVPoint[]> ModelContours=new List<CVPoint[]>();

            for (int i=0;i< count; i++)
            {
                if(Cv2.ContourArea(ExceptContours[i])>= contourArea&&
                    Cv2.ArcLength(ExceptContours[i],false)>= coutourLen)
                //if (ExceptContours[i].Length > 30)//至少30点有效
                    ModelContours.Add(ExceptContours[i]);
            }
            ModelContours = ModelContours.OrderByDescending(s => s.Length).ToList();
            //绘制边界
            Mat dst = new Mat();
            Cv2.CvtColor(img_template, dst, ColorConversionCodes.GRAY2BGR);
            if(ModelContours.Count>0)
            {
                Cv2.DrawContours(dst, ModelContours, 0, new Scalar(0, 0, 255));
                //获取重心点
                Moments M;
                M = Cv2.Moments(ModelContours[0]);
                double cX = (M.M10 / M.M00);
                double cY = (M.M01 / M.M00);

                float a = (float)(M.M20 / M.M00 - cX * cX);
                float b = (float)(M.M11 / M.M00 - cX * cY);
                float c = (float)(M.M02 / M.M00 - cY * cY);
                //计算角度(0~180)
              //  double tanAngle = Cv2.FastAtan2(2 * b, (a - c)) / 2;

                //计算角度2(-90~90)
             //   double ang = (Math.Atan2(2 * b, (a - c)) * 180 / Math.PI) / 2;

                //double ang2=  Cv2.MinAreaRect(ModelContours[0]).Angle;

                //if (tanAngle > 90)
                //    tanAngle -= 180;
                //当前轮廓旋转矩
                RotatedRect currrect = Cv2.MinAreaRect(ModelContours[0]);
                //绘制旋转矩形
                   dst.DrawRotatedRect(currrect, Scalar.Lime);

                //绘制目标边界
                Cv2.DrawContours(dst, ModelContours, 0, new Scalar(0, 0, 255));
                //显示目标中心
                dst.drawCross(new CVPoint((int)cX, (int)cY),
                       new Scalar(0, 255, 0), 10, 2);
                //

                //CVPoint[] HullP = Cv2.ConvexHull(ModelContours[0], true);//顺时针方向

                //List<CVPoint[]> HullPList = new List<CVPoint[]>();

                //HullPList.Add(HullP);

                Cv2.Polylines(dst, HullPList, true, Scalar.Red);

                //Point2f cVPoint = CalBestDisP(new Point2d(cX, cY), HullP);

                //double ang3 = ang;

                //if(!(cVPoint.X==0&& cVPoint.Y == 0))
    //            {
                //    //计算角度2(-180~180)
                //    ang3 = calAngleOfLx(cX, cY, cVPoint.X, cVPoint.Y);
                //    Cv2.Line(dst, (int)cX, (int)cY, (int)cVPoint.X, (int)cVPoint.Y, Scalar.DarkOrange);
                //}

                //轮廓点位
                modelx = cX;
                modely = cY;
                modelangle = currrect.Angle;

                //轮廓长度
                coutourLen = Cv2.ArcLength(ModelContours[0],false);
                contourArea = Cv2.ContourArea(ModelContours[0]);
                templateContour = ModelContours[0];
            }
            else
            {
                //轮廓点位
                modelx = 0;
                modely = 0;
                modelangle = 0;

                //轮廓长度
                coutourLen = 0;
                contourArea = 0;
                templateContour =null;
            }
            return dst;
        }

b)形状多角度匹配核心算法如下:

    /// <summary>
        /// 形状匹配
        /// </summary>
        /// <param name="image">输入图像</param>
        /// <param name="imgTemplatecontours">模板轮廓</param>
        ///  <param name="Segthreshold">分割阈值</param>
        /// <param name="MatchValue">匹配值</param>
        /// <param name="MincoutourLen">轮廓最小长度</param>
        /// <param name="MaxcoutourLen">轮廓最大长度</param>
        /// <param name="MinContourArea">轮廓最小面积</param>
        /// <param name="MaxContourArea">轮廓最大面积</param>
        /// <param name="shapeMatchResult">匹配结果</param>
        /// <param name="isMultipleTemplates">是否使用多模板</param>
        /// <returns>返回绘制图</returns>
        bool ShapeTemplateMatch(Mat image, CVPoint[] imgTemplatecontours, double Segthreshold,
            double MatchValue, int MincoutourLen, int MaxcoutourLen,
             double MinContourArea, double MaxContourArea,  double baseAngle,
             ref ShapeMatchResult shapeMatchResult,
             bool isMultipleTemplates=false)
        {

            //List<Point2d> image_coordinates = new List<Point2d>();
            //灰度化
            //Mat gray_img=new Mat();
            //Cv2.CvtColor(image, gray_img, ColorConversionCodes.BGR2GRAY);
            Mat dst = new Mat();
            Cv2.CvtColor(image, dst, ColorConversionCodes.GRAY2BGR);
            //阈值分割
            Mat thresh_img = new Mat();
            Cv2.Threshold(image, thresh_img, Segthreshold, 255, ThresholdTypes.Binary);

            #region------此处增加与模板创建时候同样的图像处理--------
            //开运算处理,提出白色噪点
            Mat ellipse = Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(3, 3));

            Cv2.MorphologyEx(thresh_img, thresh_img, MorphTypes.Open, ellipse);
            #endregion

            //Mat cannyMat = new Mat();
            //Cv2.Canny(thresh_img, cannyMat, Segthreshold, 255);

            //寻找边界
            CVPoint[][] contours_img;
            //HierarchyIndex[] hierarchy;
            Cv2.FindContours(thresh_img, out contours_img, out _, RetrievalModes.Tree,
                 ContourApproximationModes.ApproxNone);
            //根据形状模板进行匹配
            int min_pos = -1;
            double min_value = MatchValue;//匹配分值
            List<CVPoint[]> points = contours_img.ToList<CVPoint[]>();

            //List<double> angleList = new List<double>();
            for (int i = 0; i < points.Count; i++)
            {
                //计算轮廓面积,筛选掉一些没必要的小轮廓
                if (Cv2.ContourArea(contours_img[i]) < MinContourArea ||
                              Cv2.ContourArea(contours_img[i]) > MaxContourArea)
                    continue;
                //轮廓长度不达标
                if (Cv2.ArcLength(contours_img[i], false) < MincoutourLen ||
                              Cv2.ArcLength(contours_img[i], false) > MaxcoutourLen)
                    continue;

                //得到匹配分值 ,值越小相似度越高
                double value = Cv2.MatchShapes(contours_img[i], imgTemplatecontours,
                                                           ShapeMatchModes.I3, 0.0);
                value = 1 - value;

                //将匹配分值与设定分值进行比较
                if (value >= min_value)
                {
                    min_pos = i;

                    //将目标的得分都存在数组中
                    shapeMatchResult.scores.Add(value);
                    //匹配到的轮廓
                    shapeMatchResult.contours.Add(contours_img[min_pos]);
                    /*----------------*/
                }

            }
            /*----------------*/
            int count = shapeMatchResult.scores.Count;
            if(count<=0)
            {
                shapeMatchResult.resultToShow = dst;
                shapeMatchResult.exceptionInfo = "模板匹配失败!";
                return false;
            }

            if (isMultipleTemplates)
            {
                for (int j = 0; j < count; j++)
                {
                    //绘制目标边界
                    Cv2.DrawContours(dst, shapeMatchResult.contours, j, new Scalar(0, 0, 255));
                    //得分绘制
                    Cv2.PutText(dst,
                        string.Format("Score:{0};Angle:{1}", shapeMatchResult.scores[j].ToString("F3"),
                        shapeMatchResult.rotations[j].ToString("F3")),
                             //anglebuf[j].ToString("F3")),
                             new CVPoint(shapeMatchResult.contours[j][0].X + 10, shapeMatchResult.contours[j][0].Y - 10),
                                        HersheyFonts.HersheyDuplex, 1, Scalar.Yellow);
                    //显示目标中心并提取坐标点
                    dst.drawCross(new CVPoint((int)shapeMatchResult.positions[j].X, (int)shapeMatchResult.positions[j].Y),
                           new Scalar(0, 255, 0), 10, 2);
                    //当前轮廓旋转矩
                    RotatedRect currrect = Cv2.MinAreaRect(shapeMatchResult.contours[j]);

                    dst.DrawRotatedRect(currrect, Scalar.Lime);
                }
            }
            else
            {
                double bestScore=  shapeMatchResult.scores.Max();    //最佳得分
                int index = shapeMatchResult.scores.FindIndex(s=>s== bestScore);
              //  double bestangle = shapeMatchResult.rotations[index]; //最佳角度
            //    Point2d bestpos = shapeMatchResult.positions[index]; //最佳点位
                CVPoint[] bestcontour= shapeMatchResult.contours[index]; //最佳轮廓            

                //绘制目标边界
                Cv2.DrawContours(dst, shapeMatchResult.contours, index, new Scalar(0, 0, 255));

                //获取重心点
                Moments M = Cv2.Moments(bestcontour);
                double cX = (M.M10 / M.M00);
                double cY = (M.M01 / M.M00);

                float a = (float)(M.M20 / M.M00 - cX * cX);
                float b = (float)(M.M11 / M.M00 - cX * cY);
                float c = (float)(M.M02 / M.M00 - cY * cY);
                //计算角度(0~180)
               // double tanAngle = Cv2.FastAtan2(2 * b, (a - c)) / 2;
                //angleList.Add(tanAngle);

                //计算角度2(-90~90)
                //double ang = (Math.Atan2(2 * b, (a - c)) * 180 / Math.PI) / 2;

                #region----角度计算方式2---
                //-90~90度
                //由于先验目标最小包围矩形是长方形
                //因此最小包围矩形的中心和重心的向量夹角为旋转
                RotatedRect rect_template = Cv2.MinAreaRect(imgTemplatecontours);
                RotatedRect rect_search = Cv2.MinAreaRect(bestcontour);
                //两个旋转矩阵是否同向
                float sign = (rect_template.Size.Width - rect_template.Size.Height) *
                                  (rect_search.Size.Width - rect_search.Size.Height);
                float angle=0;
                if (sign > 0)
                    // 可以直接相减
                    angle = rect_search.Angle - rect_template.Angle;
                else
                    angle = (90 + rect_search.Angle) - rect_template.Angle;

                if (angle > 90)
                    angle -= 180;
                #endregion

                    //显示目标中心并提取坐标点
                dst.drawCross(new CVPoint((int)cX, (int)cY),
                            new Scalar(0, 255, 0), 10, 2);
                //当前轮廓旋转矩
                RotatedRect currrect = Cv2.MinAreaRect(bestcontour);
                //绘制旋转矩形
                dst.DrawRotatedRect(currrect, Scalar.Lime);

                //CVPoint[] HullP = Cv2.ConvexHull(bestcontour, true);//顺时针方向

                //List<CVPoint[]> HullPList = new List<CVPoint[]>();

                //HullPList.Add(HullP);

                //Cv2.Polylines(dst, HullPList, true, Scalar.Red);

                //Point2f cVPoint = CalBestDisP(new Point2d(cX, cY), HullP);

                //double ang3 = ang;

                //if (!(cVPoint.X == 0 && cVPoint.Y == 0))
                //{
                //    //计算角度2(-180~180)
                //    ang3 = calAngleOfLx(cX, cY, cVPoint.X, cVPoint.Y);
                //    Cv2.Line(dst, (int)cX, (int)cY, (int)cVPoint.X, (int)cVPoint.Y, Scalar.DarkOrange);
                //}

                //double offsetA = ang3 - baseAngle;//偏转角
                //if (offsetA < -180)
                //    offsetA += 360;
                //else if (offsetA > 180)
                //    offsetA -= 360;

                    //得分绘制
                //Cv2.PutText(dst,
                //    string.Format("Score:{0};Angle:{1}", bestScore.ToString("F3"),
                //              ang3.ToString("F3")),
                //         new CVPoint(shapeMatchResult.contours[index][0].X + 10, shapeMatchResult.contours[index][0].Y - 10),
                //                    HersheyFonts.HersheyDuplex, 1, Scalar.Yellow);

                shapeMatchResult.positions.Clear();
                shapeMatchResult.rotations.Clear();
                shapeMatchResult.scores.Clear();
                shapeMatchResult.contours.Clear();
                //将目标的重心坐标都存在数组中
                shapeMatchResult.positions.Add(new Point2d(cX, cY));//向数组中存放点的坐标

                shapeMatchResult.rotations.Add(angle);//将偏转角度都存在数组中 

                shapeMatchResult.scores.Add(bestScore);//将目标的得分都存在数组中 

                shapeMatchResult.contours.Add(bestcontour); //匹配到的轮廓
                /*----------------*/
            }

            shapeMatchResult.resultToShow = dst;
            return true;
        }

到此这篇关于C# OpenCV实现形状匹配的方法详解的文章就介绍到这了,更多相关C# OpenCV形状匹配内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C#中OpenCvSharp 通过特征点匹配图片的方法

    现在的手游基本都是重复操作,一个动作要等好久,结束之后继续另一个动作.很麻烦,所以动起了自己写一个游戏辅助的心思. 这个辅助本身没什么难度,就是通过不断的截图,然后从这个截图中找出预先截好的能代表相应动作的按钮或者触发条件的小图. 找到之后获取该子区域的左上角坐标,然后通过windows API调用鼠标或者键盘做操作就行了. 这里面最难的也就是找图了,因为要精准找图,而且最好能适应不同的分辨率下找图,所以在模板匹配的基础上,就有了SIFT和SURF的特征点找图方式. 在写的过程中查找资料,大都是

  • C#使用OpenCV剪切图片中的人物头像的实现方法

    前言 本文主要介绍如何使用OpenCV剪切图形中的人物头像. 准备工作 首先创建一个Wpf项目--OpenCV_Face_Wpf,这里版本使用Framework4.7.2. 然后使用Nuget搜索[Emgu.CV],如下图. 这里的Emgu.CV选择4.3.0.3890版本,然后安装Emgu.CV和Emgu.CV.runtime.windows. 然后下载所需文件haarcascade_frontalface_default.xml. 可以去OpenCV的开源代码中下载,下载地址:https:/

  • 如何在c#中使用opencv函数库

    这个demo用c#实现图片裁剪和半透明融合的功能演示程序.功能挺简单的,就是把一张固定大小的图片先做边缘羽化,然后贴到一个圆形泡泡形状的底图上,最后把结果半透明融合到一张背景图上. C#实现图像的羽化.将图片裁剪复制到一个圆形图片这些都挺简单的,最后一步融合到背景图上需要用到opencv的seamlessClone方法.网上搜索c#使用opencv的方法有很多,一种是直接使用opencv的C#版本,一种是先把opencv的方法封装到一个dll然后用c#调用这个dll导出的方法.对于我这个需求,后

  • C#中OpenCVSharp实现轮廓检测

    OpenCv提供了函数 findContours()用于对物体轮廓进行检测,该函数实现算法是由S.suzuki K.Abe于1985年发表的.OpenCVSharp封装了这个函数,有2个参数(contours,hierarchy)要做特别的说明. public static void FindContours(InputOutputArray image, out Point[][] contours, out HierarchyIndex[] hierarchy, RetrievalModes

  • c# 使用OpenCV识别硬币

    在本系列文章中,我们将使用深度神经网络(DNN)来执行硬币识别.具体来说,我们将训练一个DNN识别图像中的硬币. 在本文中,我们将描述一个OpenCV应用程序,它将检测图像中的硬币.硬币检测是硬币完整识别之前的一个常见阶段.它包括从给定图像中检测和提取硬币. 本系列附带的代码将使用Keras在C#中实现.在本系列的最后一篇文章中,我们将简要地使用ML.NET.在众多选择中,为什么要使用Keras.NET呢?Keras.NET 非常容易学习,因为它基本上是从Python编写的经典TensorFlo

  • C# OpenCV实现形状匹配的方法详解

    1. 多角度模板匹配测试效果如下图: 图1-1 图1-2 图1-3 正负角度均可正常识别,识别角度偏差<1° 2. 下面分享一下开发过程: a). ROI区域的生成,基于GDI+完成图形绘制,如图 绘制模板设置区域,用来生成需要的模板特征. ROI区域绘制代码如下: /// <summary> /// 区域绘制 /// </summary> /// <param name="graphics"></param> /// <pa

  • 利用OpenCV实现YOLO对象检测方法详解

    目录 前言 什么是YOLO物体检测器? 项目结构 检测图像 检测视频 前言 本文将教你如何使用YOLOV3对象检测器.OpenCV和Python实现对图像和视频流的检测.用到的文件有yolov3.weights.yolov3.cfg.coco.names,这三个文件的github链接如下: GitHub - pjreddie/darknet: Convolutional Neural Networks https://pjreddie.com/media/files/yolov3.weights

  • Python+OpenCV实现阈值分割的方法详解

    目录 一.全局阈值 1.效果图 2.源码 二.滑动改变阈值(滑动条) 1.效果图 2.源码 三.自适应阈值分割 1.效果图 2.源码 3.GaussianBlur()函数去噪 四.参数解释 一.全局阈值 原图: 整幅图采用一个阈值,与图片的每一个像素灰度进行比较,重新赋值: 1.效果图 2.源码 import cv2 import matplotlib.pyplot as plt #设定阈值 thresh=130 #载入原图,并转化为灰度图像 img_original=cv2.imread(r'

  • Python OpenCV实现图片预处理的方法详解

    目录 一.图片预处理 1.1 边界填充(padding) 1.2 融合图片(mixup) 1.3 图像阈值 二.滤波器 2.1 均值滤波器 2.2 方框滤波器 2.3 高斯滤波器 2.4 中值滤波 2.5 所有滤波器按照上述顺序输出 一.图片预处理 1.1 边界填充(padding) 方法 : cv2.copyMakeBorder BORDER_REPLICATE:复制法,也就是复制最边缘像素. BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制例如:fedcba|abc

  • OpenCV形状检测的示例详解

    目录 1.基于OpenCV的形状检测Python版本 1.1.定义我们的形状检测器类ShapeDetector 1.2.基于OpenCV的形状检测器 2.基于OpenCV的形状检测C++版本 2.1代码实现 2.2主要函数解析 2.3结果展示 1.基于OpenCV的形状检测Python版本 目录结构 1.1.定义我们的形状检测器类ShapeDetector 开始定义我们的 ShapeDetector 类.我们将跳过这里的 init 构造函数,因为不需要初始化任何东西. # 导入必要的包 impo

  • Python+Opencv实现物体尺寸测量的方法详解

    目录 1.效果展示 2.项目介绍 3.项目搭建 4.utils.py文件代码展示与讲解 5.项目代码展示与讲解 6.项目资源 7.项目总结 1.效果展示 我们将以两种方式来展示我们这个项目的效果. 下面这是视频的实时检测,我分别用了盒子和盖子来检测,按理来说效果不应该怎么差的,但我实在没有找到合适的背景与物体.且我的摄像头使用的是外设,我不得不手持,所以存在一点点的抖动,但我可以保证,它是缺少了适合检测物体与背景. 我使用手机拍了一张照片并经过了ps修改了背景,效果不错. 2.项目介绍 本项目中

  • Python+OpenCV实现信用卡数字识别的方法详解

    目录 一.模板图像处理 二.信用卡图片预处理 一.模板图像处理 (1)灰度图.二值图转化 template = cv2.imread('C:/Users/bwy/Desktop/number.png') template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY) cv_show('template_gray', template_gray) # 形成二值图像,因为要做轮廓检测 ret, template_thresh = cv2.thre

  • 对python 匹配字符串开头和结尾的方法详解

    1.你需要通过指定的文本模式去检查字符串的开头或者结尾,比如文件名后缀,URL Scheme 等等.检 查 字 符 串 开 头 或 结 尾 的 一 个 简 单 方 法 是 使 用str.startswith() 或 者 是str.endswith()方法.比如: >>> filename = 'spam.txt' >>> filename.endswith('.txt') True >>> filename.startswith('file:') Fa

  • 对Python正则匹配IP、Url、Mail的方法详解

    如下所示: """ Created on Thu Nov 10 14:07:36 2016 @author: qianzhewoniuqusanbu """ import re def RegularMatchIP(ip):     '''进行正则匹配ip,加re.IGNORECASE是让结果返回bool型'''     pattern=re.match(r'\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?

  • Python实现括号匹配方法详解

    这篇文章主要介绍了python实现括号匹配方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.用一个栈[python中可以用List]就可以解决,时间和空间复杂度都是O(n) # -*- coding: utf8 -*- # 符号表 SYMBOLS = {'}': '{', ']': '[', ')': '(', '>': '<'} SYMBOLS_L, SYMBOLS_R = SYMBOLS.values(), SYMBOLS.ke

随机推荐