<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
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形狀匹配內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45