C#编程实现自定义控件结合OpenCVSharp库的图像圆心定位技术
·
C# 自定义控件 opencvsharp 找圆
最近在做个工业视觉检测项目时,发现WinForm自带的PictureBox控件完全不够用。客户要求实时显示摄像头画面还要标出圆形瑕疵,这逼得我不得不撸起袖子造轮子——用C#自定义控件整合OpenCvSharp实现找圆功能。
先搞个基础画布控件,继承UserControl后往里塞个双缓冲的PictureBox:
public class VisionCanvas : UserControl
{
private PictureBox picBox = new PictureBox();
private float zoomScale = 1.0f;
public VisionCanvas()
{
picBox.SizeMode = PictureBoxSizeMode.Zoom;
picBox.Dock = DockStyle.Fill;
this.Controls.Add(picBox);
this.Resize += (s, e) => {
if (picBox.Image != null)
{
// 保持图像比例缩放
zoomScale = Math.Min(
(float)this.Width / picBox.Image.Width,
(float)this.Height / picBox.Image.Height);
}
};
}
}
这里有个坑要注意:直接修改PictureBox的尺寸会导致图像变形,所以我在Resize事件里动态计算缩放比例。zoomScale这个参数后面画标记时会派上大用场。

重点来了!OpenCvSharp的找圆方法比传统Hough变换更高效。在控件类里加个FindCircles方法:
public CircleSegment[] FindCircles(Mat src, int minRadius=10, int maxRadius=100)
{
using (var gray = new Mat())
{
Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);
Cv2.MedianBlur(gray, gray, 5);
return Cv2.HoughCircles(gray, HoughMethods.Gradient,
dp: 1.2,
minDist: gray.Height / 8,
param1: 100,
param2: 30,
minRadius: minRadius,
maxRadius: maxRadius);
}
}
参数解释时间:
- dp=1.2:图像分辨率缩小比例,实测1.2能平衡精度和速度
- minDist:防止圆圈检测重叠,按图像高度动态调整更智能
- param2:这个值越小检测越敏感,但误检率会上升
检测到圆之后得在控件上画出来。重写控件的OnPaint方法:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (_circles != null)
{
using (var g = e.Graphics)
{
foreach (var circle in _circles)
{
// 坐标转换(原始图像坐标→缩放后控件坐标)
var center = new Point(
(int)(circle.Center.X * zoomScale),
(int)(circle.Center.Y * zoomScale));
var radius = (int)(circle.Radius * zoomScale);
// 画圆
g.DrawEllipse(Pens.Red,
center.X - radius, center.Y - radius,
radius * 2, radius * 2);
// 画圆心十字线
g.DrawLine(Pens.Blue, center.X-10, center.Y, center.X+10, center.Y);
g.DrawLine(Pens.Blue, center.X, center.Y-10, center.X, center.Y+10);
}
}
}
}
这里有个细节:直接拿原始图像的坐标画会错位,必须乘以之前算好的zoomScale系数。画十字线时用固定10像素长度,这样无论怎么缩放标记都清晰可见。
实际使用时只需要三行代码:
var canvas = new VisionCanvas { Dock = DockStyle.Fill };
var image = Cv2.ImRead("test.jpg");
canvas.SetImage(image.ToBitmap());
canvas.FindCircles(image);
调试时发现个有趣现象——当图像中有多个同心圆时,适当调高param2参数能过滤掉内层小圆。这比写复杂的逻辑判断省事多了,果然参数调优才是OpenCV的正确打开方式。
完整项目里还可以给控件添加右键菜单,保存当前参数配置到本地。毕竟产线上的工人大哥可没耐心每次都手动调参,咱们得把复杂度封装在底层。

更多推荐

所有评论(0)