C#自学32—OpenCvSharp模板匹配,检测圆并获得圆心坐标

it2026-08-17  5

using OpenCvSharp; using OpenCvSharp.Extensions; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using static System.Windows.Forms.VisualStyles.VisualStyleElement; using Point = OpenCvSharp.Point; using Size = OpenCvSharp.Size; namespace DrawROI { public partial class Form1 : Form { private System.Drawing.Point RectStartPoint, tempEndPoint; bool blnDraw; Mat ImageROI; Mat OrgMat; private string FilePath; public Form1() { InitializeComponent(); } private void pictureBox1_MouseDown(object sender, MouseEventArgs e) { RectStartPoint = e.Location; //获得鼠标按下的pictureBox上坐标 Invalidate(); blnDraw = true;//判断标志 } private void pictureBox1_MouseMove(object sender, MouseEventArgs e) { if (blnDraw) { if (e.Button != MouseButtons.Left)//判断是否按下左键 { return; } tempEndPoint = e.Location; //记录框的位置和大小 //pictureBox上开始点坐标 //Rect.Location = new System.Drawing.Point( //Math.Min(RectStartPoint.X, tempEndPoint.X), //Math.Min(RectStartPoint.Y, tempEndPoint.Y)); pictureBox上矩形大小 //Rect.Size = new System.Drawing.Size( //Math.Abs(RectStartPoint.X - tempEndPoint.X), //Math.Abs(RectStartPoint.Y - tempEndPoint.Y)); pictureBox1.Invalidate(); // 最后点位置 int X0, Y0; Utilities.ConvertCoordinates(pictureBox1, out X0, out Y0, e.X, e.Y); //在控件中 //textBox1.Text = Convert.ToString("pictureBox最后点坐标" + e.X + " ," + e.Y); //pictureBox 上终点坐标 //textBox2.Text = Convert.ToString("pictureBox开始点坐标" + Rect.X + " ," + Rect.Y); //开始点坐标 //textBox3.Text = Convert.ToString("pictureBox的Width" + Rect.Width + " ," + Rect.Height);//大小 //Create ROI 感兴趣区域 Utilities.ConvertCoordinates(pictureBox1, out X0, out Y0, RectStartPoint.X, RectStartPoint.Y); int X1, Y1; Utilities.ConvertCoordinates(pictureBox1, out X1, out Y1, tempEndPoint.X, tempEndPoint.Y); //感兴趣区域 左上点坐标-宽-高 //RealImageRect.Location = new System.Drawing.Point( // Math.Min(X0, X1), // Math.Min(Y0, Y1)); //RealImageRect.Size = new System.Drawing.Size( // Math.Abs(X0 - X1), // Math.Abs(Y0 - Y1)); //textBox4.Text = "原图像上最后点坐标: X:" + X0 + " Y:" + Y0; //textBox5.Text = "原图像上RealImageRect: X:" + RealImageRect.X + " Y:" + RealImageRect.Y; // 原图像-左上点坐标 //textBox6.Text = "原图像上RealImageRectSize: X:" + RealImageRect.Width + " Y:" + RealImageRect.Height; // 原图像-大小 Rect tmp_Rect = new Rect(Math.Min(X0, X1), Math.Min(Y0, Y1), Math.Abs(X0 - X1), Math.Abs(Y0 - Y1)); ImageROI = new Mat(OrgMat, tmp_Rect);//新建一个mat,把roi内的图像加载到里面去。 //Cv2.ImWrite("4.jpg",ImageROI); //保存 } } private void pictureBox1_MouseUp(object sender, MouseEventArgs e) { // mouseUp 结束以后 将图像显示在pictureBox2控件中 pictureBox2.Image = ImageROI.ToBitmap(); //***************************************// blnDraw = false; //结束绘制 } private void button1_Click(object sender, EventArgs e) { OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.ShowDialog(); //openFileDialog.Multiselect=true; FilePath = openFileDialog.FileName; OrgMat = new Mat(FilePath, ImreadModes.Grayscale); OrgMat.MedianBlur(3); pictureBox1.Image = BitmapConverter.ToBitmap(OrgMat); } private void button3_Click(object sender, EventArgs e) { if (ImageROI == null) { MessageBox.Show("请先绘制模板"); return ; } Mat RoiClone = ImageROI.Clone(); Mat mat3 = new Mat(); //创建result的模板,就是MatchTemplate里的第三个参数 //mat3.Create(mat1.Cols - mat2.Cols + 1, mat1.Rows - mat2.Rows + 1, MatType.CV_32FC1); //进行匹配(1母图,2模版子图,3返回的result,4匹配模式) Cv2.MatchTemplate(OrgMat, RoiClone, mat3, TemplateMatchModes.SqDiff); //对结果进行归一化(这里我测试的时候没有发现有什么用,但在opencv的书里有这个操作,应该有什么神秘加成,这里也加上) Cv2.Normalize(mat3, mat3, 1, 0, NormTypes.MinMax, -1); //double minValue, maxValue; Point minLocation, maxLocation; /// 通过函数 minMaxLoc 定位最匹配的位置 /// (这个方法在opencv里有5个参数,这里我写的时候发现在有3个重载,看了下可以直接写成拿到起始坐标就不取最大值和最小值了) /// minLocation和maxLocation根据匹配调用的模式取不同的点 Cv2.MinMaxLoc(mat3, out minLocation, out maxLocation); Mat OrgMatClone = OrgMat.Clone(); //画出匹配的矩, //Cv2.Rectangle(mask, maxLocation, new Point(maxLocation.X + mat2.Cols, maxLocation.Y + mat2.Rows), Scalar.Red, 2); //Cv2.Rectangle(OrgMatClone, minLocation, new Point(minLocation.X + RoiClone.Cols, minLocation.Y + RoiClone.Rows), Scalar.Red, 2); //Cv2.ImShow("mat1", mat1); //Cv2.ImShow("mat2", mat2); //霍夫圆检测:使用霍夫变换查找灰度图像中的圆。 /* * 参数: * 1:输入参数: 8位、单通道、灰度输入图像 * 2:实现方法:目前,唯一的实现方法是HoughCirclesMethod.Gradient * 3: dp :累加器分辨率与图像分辨率的反比。默认=1 * 4:minDist: 检测到的圆的中心之间的最小距离。(最短距离-可以分辨是两个圆的,否则认为是同心圆- src_gray.rows/8) * 5:param1: 第一个方法特定的参数。[默认值是100] canny边缘检测阈值低 * 6:param2: 第二个方法特定于参数。[默认值是100] 中心点累加器阈值 – 候选圆心 * 7:minRadius: 最小半径 * 8:maxRadius: 最大半径 * */ CircleSegment[] cs = Cv2.HoughCircles(RoiClone, HoughMethods.Gradient, 1, 80, 70, 100, 100, 200); for (int i = 0; i < cs.Count(); i++) { //画圆 Cv2.Circle(OrgMatClone, (int)(cs[i].Center.X + minLocation.X), (int)(cs[i].Center.Y + minLocation.Y), (int)cs[i].Radius, new Scalar(0, 0, 255), 2, LineTypes.AntiAlias); //加强圆心显示 Cv2.Circle(OrgMatClone, (int)cs[i].Center.X, (int)cs[i].Center.Y, 3, new Scalar(0, 0, 255), 2, LineTypes.AntiAlias); textBox1.Text = cs[i].Center.X.ToString(); textBox2.Text = cs[i].Center.Y.ToString(); } //pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage; pictureBox1.Image = BitmapConverter.ToBitmap(OrgMatClone); } //private void pictureBox1_Paint(object sender, PaintEventArgs e) //{ // if (blnDraw) // { // if (pictureBox1.Image != null) // { // if (Rect != null && Rect.Width > 0 && Rect.Height > 0) // { // e.Graphics.DrawRectangle(new Pen(Color.Red, 1), Rect);//重新绘制颜色为红色 // } // } // } //} public class Utilities { //坐标转换 //************************************** //* 图片左边转换, //* Input输入: pictureBox 坐标X,Y //* Output输出: Image 图像上对应的坐标 //**************************************// public static void ConvertCoordinates(PictureBox pic, out int X0, out int Y0, int x, int y) { int pic_hgt = pic.ClientSize.Height; int pic_wid = pic.ClientSize.Width; int img_hgt = pic.Image.Height; int img_wid = pic.Image.Width; X0 = x; Y0 = y; switch (pic.SizeMode) { case PictureBoxSizeMode.AutoSize: case PictureBoxSizeMode.StretchImage: X0 = (int)(img_wid * x / (float)pic_wid); Y0 = (int)(img_hgt * y / (float)pic_hgt); break; } } } //******************************************************************// /// /// 多角度模板匹配方法 /// /// 待匹配图像 /// 模板图像 /// 起始角度 /// 角度范围 /// 角度步长 /// 金字塔层级 /// 得分阈值 /// //Mat ImageRotate; //private ResultPoint CircleMatchNcc(Mat srcImage, Mat modelImage, double angleStart, double angleRange, double angleStep, int numLevels, double thresScore, int nccMethod) //{ // double step = angleRange / ((angleRange / angleStep) / 100); // double start = angleStart; // double range = angleRange; // //定义图片匹配所需要的参数 // int resultCols = srcImage.Cols - modelImage.Cols + 1; // int resultRows = srcImage.Rows - modelImage.Cols + 1; // Mat result = new Mat(resultCols, resultRows, MatType.CV_8U); // Mat src = new Mat(); // Mat model = new Mat(); // srcImage.CopyTo(src); // modelImage.CopyTo(model); // //对模板图像和待检测图像分别进行图像金字塔下采样 // for (int i = 0; i < numLevels; i++) // { // Cv2.PyrDown(src, src, new Size(src.Cols / 2, src.Rows / 2)); // Cv2.PyrDown(model, model, new Size(model.Cols / 2, model.Rows / 2)); // } // TemplateMatchModes matchMode = TemplateMatchModes.CCoeffNormed; // switch (nccMethod) // { // case 0: // matchMode = TemplateMatchModes.SqDiff; // break; // case 1: // matchMode = TemplateMatchModes.SqDiffNormed; // break; // case 2: // matchMode = TemplateMatchModes.CCorr; // break; // case 3: // matchMode = TemplateMatchModes.CCorrNormed; // break; // case 4: // matchMode = TemplateMatchModes.CCoeff; // break; // case 5: // matchMode = TemplateMatchModes.CCoeffNormed; // break; // } // //在没有旋转的情况下进行第一次匹配 // Cv2.MatchTemplate(src, model, result, matchMode); // Cv2.MinMaxLoc(result, out double minVal, out double maxVal, out Point minLoc, out Point maxLoc, new Mat()); // Point location = maxLoc; // double temp = maxVal; // double angle = 0; // Mat newImg; // //以最佳匹配点左右十倍角度步长进行循环匹配,直到角度步长小于参数角度步长 // if (nccMethod == 0 || nccMethod == 1) // { // do // { // for (int i = 0; i <= (int)range / step; i++) // { // newImg = ImageRotate(model, start + step * i); // Cv2.MatchTemplate(src, newImg, result, matchMode); // Cv2.MinMaxLoc(result, out double minval, out double maxval, out Point minloc, out Point maxloc, new Mat()); // if (maxval < temp) // { // location = maxloc; // temp = maxval; // angle = start + step * i; // } // } // range = step * 2; // start = angle - step; // step = step / 10; // } while (step > angleStep); // return new ResultPoint(location.X * Math.Pow(2, numLevels) + modelImage.Width / 2, location.Y * Math.Pow(2, numLevels) + modelImage.Height / 2, -angle, temp); // } // else // { // do // { // for (int i = 0; i <= (int)range / step; i++) // { // newImg = ImageRotate(model, start + step * i); // Cv2.MatchTemplate(src, newImg, result, matchMode); // Cv2.MinMaxLoc(result, out double minval, out double maxval, out Point minloc, out Point maxloc, new Mat()); // if (maxval > temp) // { // location = maxloc; // temp = maxval; // angle = start + step * i; // } // } // range = step * 2; // start = angle - step; // step = step / 10; // } while (step > angleStep); // if (temp > thresScore) // { // return new ResultPoint(location.X * Math.Pow(2, numLevels), location.Y * Math.Pow(2, numLevels), -angle, temp); // } // } // return new ResultPoint(); //} 旋转模板 //private Mat ImageRotate(Mat src, double angle) //{ // Mat dst = new Mat(); // Point2f center = new Point2f(src.Cols / 2, src.Rows / 2); // Mat rot = Cv2.GetRotationMatrix2D(center, angle, 1); // Size2f s2f = new Size2f(src.Size().Width, src.Size().Height); // Rect box = new RotatedRect(new Point2f(0, 0), s2f, (float)angle).BoundingRect(); // double xx = rot.At<double>(0, 2) + box.Width / 2 - src.Cols / 2; // double zz = rot.At<double>(1, 2) + box.Height / 2 - src.Rows / 2; // rot.Set(0, 2, xx); // rot.Set(1, 2, zz); // Cv2.WarpAffine(src, dst, rot, box.Size); // return dst; } }
最新回复(0)