Emgu C#实现视频窗口绘制十字并获取中心坐标
问题:在Emgu.CV视频窗口绘制十字线并获取中心坐标
- 作为开发新手,我目前无法在视频窗口中绘制十字形线条,同时获取该十字的中心坐标。已在论坛、YouTube等平台查找资料,但未找到可用的简单示例。
- 当前效果:视频窗口仅显示检测到的圆形标记,无十字定位线
- 目标效果:在检测到的圆形中心绘制十字形定位线
以下是现有代码:
using System; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using Emgu.CV; using Emgu.CV.Structure; using System.Runtime.InteropServices; using System.Drawing.Imaging; namespace Farmer_v._0._1._0 { public partial class Vision_artificial : Form { //COLOR PICKER [DllImport("user32.dll")] static extern bool GetCursorPos(ref Point lpPoint); [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)] public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop); //COLOR PICKER Emgu.CV.Capture capWebcam = null; bool blnCapturingInProcess = false; Image<Bgr, Byte> imgOriginal; Image<Gray, Byte> imgProcesada; Mat frame = new Mat(); //canny double umbral11 = 300; double umbral22= 300; //circulos double min_distancia = 4; double umbralCirculos1 = 1; double umbralCirculos2 = 1; int min_diametro = 10; int max_diametro = 400; int n_circulos = 0; //BGR int B1 = 0; int G1 = 0; int R1 = 175; int B2 = 100; int G2 = 100; int R2 = 256; public Vision_artificial() { InitializeComponent(); } public Color GetColorAt(Point location) { using (Bitmap screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppRgb)) { using (Graphics gdest = Graphics.FromImage(screenPixel)) { using (Graphics gsrc = Graphics.FromHwnd(ibOriginal.Handle)) { IntPtr hSrcDC = gsrc.GetHdc(); IntPtr hDC = gdest.GetHdc(); int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy); gdest.ReleaseHdc(); gsrc.ReleaseHdc(); } } return screenPixel.GetPixel(0, 0); } } public void Draw(Cross2DF cross, IColor color, int thickness) { } private void Vision_artificial_Load(object sender, EventArgs e) { try { capWebcam = new Emgu.CV.Capture(1); } catch (NullReferenceException except) { txtXYRadius.Text = except.Message; return; } Application.Idle += processFrameAndUpdateGUI; blnCapturingInProcess = true; } private void Vision_artificial_Closed(object sender, EventArgs e) { if (capWebcam != null) { capWebcam.Dispose(); } } void processFrameAndUpdateGUI(object sender, EventArgs e) { imgOriginal = capWebcam.QueryFrame().ToImage<Bgr, Byte>(); if (imgOriginal == null) return; imgProcesada = imgOriginal.InRange(new Bgr(B1, G1, R1), new Bgr(B2, G2, R2)); imgProcesada = imgProcesada.SmoothGaussian(9); CircleF[] circles = imgProcesada.HoughCircles(new Gray(100), new Gray(50), 2, imgProcesada.Height / min_distancia, min_diametro, max_diametro)[0]; foreach (CircleF circle in circles) { if (txtXYRadius.Text != "") txtXYRadius.AppendText(Environment.NewLine); txtXYRadius.AppendText("Objeto en posicion x=" + circle.Center.X.ToString().PadLeft(4) + ", y =" + circle.Center.Y.ToString().PadLeft(4) + ", radius =" + circle.Radius.ToString("###.000").PadLeft(7)); txtXYRadius.ScrollToCaret(); n_circulos = circles.Length; geoX.Text = circle.Center.X.ToString(); geoY.Text = circle.Center.Y.ToString(); CvInvoke.Circle(imgOriginal, new Point((int)circle.Center.X, (int)circle.Center.Y), 3, new MCvScalar(0, 255, 0), -1, Emgu.CV.CvEnum.LineType.FourConnected, 0); imgOriginal.Draw(circle, new Bgr(Color.Red), 3); } ibOriginal.Image = imgOriginal.ToBitmap(); ibProcesada.Image = imgProcesada.ToBitmap(); } private void Arduino(object sender, EventArgs e) { } public Bitmap CannyEdge(Bitmap bmp) { Image<Gray, Byte> Cannybmp; Image<Gray, Byte> GrayBmp; Image<Bgr, Byte> orig = new Image<Bgr, Byte>(bmp); Image<Bgr, Byte> imgSmooth; Bitmap output; imgSmooth = orig.PyrUp(); imgSmooth._SmoothGaussian(3); GrayBmp = imgSmooth.Convert<Gray, byte>(); Gray grayCannyThreshold = new Gray(umbral11); Gray grayThreshLinking = new Gray(umbral22); Cannybmp = GrayBmp.Canny(grayCannyThreshold.Intensity, grayThreshLinking.Intensity); output = Cannybmp.ToBitmap(); return output; } private void pictureBox1_LoadCompleted(object sender, AsyncCompletedEventArgs e) { } private void pictureBox2_Click(object sender, EventArgs e) { } private void btnPausa_Click(object sender, EventArgs e) { if (blnCapturingInProcess == true) { Application.Idle -= processFrameAndUpdateGUI; blnCapturingInProcess = false; btnPausa.Text = "Capturar"; } else { Application.Idle += processFrameAndUpdateGUI; blnCapturingInProcess = true; btnPausa.Text = "Pausa"; } } private void ibCanny_Click(object sender, EventArgs e) { } private void pictureBox1_Click(object sender, EventArgs e) { } private void numericUpDown1_ValueChanged(object sender, EventArgs e) { umbral11 = (double)numericUpDown1.Value; } private void numericUpDown2_ValueChanged(object sender, EventArgs e) { umbral22 = (double)numericUpDown2.Value; } private void timer1_Tick(object sender, EventArgs e) { lbln_circulos.Text = n_circulos.ToString(); } private void numericUpDown5_ValueChanged(object sender, EventArgs e) { umbralCirculos1 = (double)num_umbral_1.Value; } private void num_distanicia_minima_ValueChanged(object sender, EventArgs e) { min_distancia = (double)num_distancia_minima.Value; } private void num_umbral_2_ValueChanged(object sender, EventArgs e) { umbralCirculos2 = (double)num_umbral_2.Value; } private void num_diametro_minimo_ValueChanged(object sender, EventArgs e) { min_diametro = (int)num_diametro_minimo.Value; } private void num_diametro_maximo_ValueChanged(object sender, EventArgs e) { max_diametro = (int)num_diametro_maximo.Value; } private void boxB1_ValueChanged(object sender, EventArgs e) { B1 = (int)boxB1.Value; } private void boxG1_ValueChanged(object sender, EventArgs e) { G1 = (int)boxG1.Value; } private void boxR1_ValueChanged(object sender, EventArgs e) { R1 = (int)boxG1.Value; } private void boxB2_ValueChanged(object sender, EventArgs e) { B2 = (int)boxB2.Value; } private void boxG2_ValueChanged(object sender, EventArgs e) { G2 = (int)boxG2.Value; } private void boxR2_ValueChanged(object sender, EventArgs e) { R2 = (int)boxR2.Value; } private void button1_Click(object sender, EventArgs e) { } private void ibOriginal_MouseDown(object sender, MouseEventArgs e) { } private void ibOriginal_MouseMove(object sender, MouseEventArgs e) { panelColor.BackColor = GetColorAt(e.Location); } } }
解决方案
1. 实现十字线绘制逻辑
直接在检测到圆形的循环中添加画线代码即可,以下是具体修改:
在processFrameAndUpdateGUI方法的foreach (CircleF circle in circles)循环内,添加十字线绘制代码:
// 定义十字线长度,可根据需求调整 int crossLength = 20; Point center = new Point((int)circle.Center.X, (int)circle.Center.Y); // 绘制水平横线:从中心向左、向右延伸crossLength长度 CvInvoke.Line(imgOriginal, new Point(center.X - crossLength, center.Y), new Point(center.X + crossLength, center.Y), new MCvScalar(0, 0, 255), // 红色,Emgu.CV使用BGR颜色格式 2); // 线条粗细 // 绘制垂直竖线:从中心向上、向下延伸crossLength长度 CvInvoke.Line(imgOriginal, new Point(center.X, center.Y - crossLength), new Point(center.X, center.Y + crossLength), new MCvScalar(0, 0, 255), 2);
2. 获取十字线中心坐标
十字线的中心坐标就是你检测到的圆形中心坐标,直接使用circle.Center即可。你已经在代码中通过geoX.Text = circle.Center.X.ToString();和geoY.Text = circle.Center.Y.ToString();获取并显示了该坐标,无需额外修改。
修改后的完整循环代码示例
foreach (CircleF circle in circles) { if (txtXYRadius.Text != "") txtXYRadius.AppendText(Environment.NewLine); txtXYRadius.AppendText("Objeto en posicion x=" + circle.Center.X.ToString().PadLeft(4) + ", y =" + circle.Center.Y.ToString().PadLeft(4) + ", radius =" + circle.Radius.ToString("###.000").PadLeft(7)); txtXYRadius.ScrollToCaret(); n_circulos = circles.Length; geoX.Text = circle.Center.X.ToString(); geoY.Text = circle.Center.Y.ToString(); CvInvoke.Circle(imgOriginal, new Point((int)circle.Center.X, (int)circle.Center.Y), 3, new MCvScalar(0, 255, 0), -1, Emgu.CV.CvEnum.LineType.FourConnected, 0); imgOriginal.Draw(circle, new Bgr(Color.Red), 3); // 添加十字线绘制代码 int crossLength = 20; Point center = new Point((int)circle.Center.X, (int)circle.Center.Y); CvInvoke.Line(imgOriginal, new Point(center.X - crossLength, center.Y), new Point(center.X + crossLength, center.Y), new MCvScalar(0, 0, 255), 2); CvInvoke.Line(imgOriginal, new Point(center.X, center.Y - crossLength), new Point(center.X, center.Y + crossLength), new MCvScalar(0, 0, 255), 2); }
注意事项
- 可根据需求调整
crossLength(十字线长度)、线条颜色(修改MCvScalar的参数,格式为BGR)和粗细参数 - 确保绘制操作在
imgOriginal上完成后,最终将其赋值给ibOriginal.Image显示
内容的提问来源于stack exchange,提问作者ECORON
相关产品推荐
相关产品推荐

