You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 13:20:35