/* 工业视觉_59:霍夫(Hough)两个圆及其圆心距检测
* 确定半径R的圆的检测,在工业生产中会用得更多.
* 工业视觉,目标很明确:快,准,稳. 快:开发快,运行速度快;准:高精度;稳:稳健
* 所以,目前"机器换人"项目大多采用工控电脑,搭建 Windows7+VS2019+EMGU(或AForge+Accord),这一方案见效最快.
* Hacon,Emgu的视觉库都很强大,而AForge+Accord库更全面更丰富(如:数学,人工智能,机器学习).
* 圆识别是工业视觉的入门,它常常包括两圆的几何关系:位置关系(外切,内切,外离,内离,相交).交相时的交点坐标等.
* 圆的方程有直角坐标系与极坐标系两种.
* Hough算法的核心思想是,在所有白色的点都画低灰度的圆,再进行大量的投票统计,圆心处一般丰度很高.
* 另一思路:每取不在同一直线上的三点,找出二线段中垂线的交点,再计算圆心与半径,进行投票也是可行的,关键是速度与效益.
* 在自动化行业的机器视觉,常常与机器人或车床协作,如钻孔,激光切割,计算各类数据都是必需的.
* 霍夫(Hough)圆检测的官方参考(C#语言)在:
http://accord-framework.net/docs/html/T_Accord_Imaging_HoughCircle.htm
--------- 编撰: 项道德(微信:daode1212),2021-07-07
*/
pictureBox1.Image = Image.FromFile("2D106_直线与圆.jpg"); //
Bitmap bmp = (Bitmap)(pictureBox1.Image);
int ww = bmp.Width;
int hh = bmp.Height;
//自定义函数MSG("")为内置函数MessageBox.Show("")的简写方式;
MSG("图片宽度与高度:\r\n" + string.Format("w={0},h={1}", ww, hh) + "\r\n 接着,呈现处理结果");
var gray = new Accord.Imaging.Filters.Grayscale(0.3, 0.6, 0.1).Apply(bmp); //转为灰度位图
//gray = new Accord.Imaging.Filters.Threshold(100).Apply(gray);//调节亮度阈值
//gray = new Accord.Imaging.Filters.Invert().Apply(gray);//翻转成:背景黑色,线段为白色. 若已经是背景黑色,线段为白色的图片不用再翻转
//转为非托管数据:
Accord.Imaging.UnmanagedImage image = Accord.Imaging.UnmanagedImage.FromManagedImage(gray);
//建立霍夫变换器(图片中一个圆的半径是93,另一圆的半径是157):
List<HoughCircle[]> LH = new List<HoughCircle[]>();
for (int r = 93; r <= 157; r+=(157-93))
{
HoughCircleTransformation circleTransform = new HoughCircleTransformation(r);
//应用霍夫变换:
circleTransform.ProcessImage(image);
Bitmap houghCirlceImage = circleTransform.ToBitmap();
pictureBox1.Image = houghCirlceImage;
MSG("投票数据影像,确定后继续");
//根据相对丰度获取圆:
HoughCircle[] circles = circleTransform.GetCirclesByRelativeIntensity(0.9);
LH.Add(circles);
}
//创建非托管24位数据:
var bmpData = Accord.Imaging.UnmanagedImage.Create(bmp.Width, bmp.Height, PixelFormat.Format24bppRgb);
//转到托管图像后,添加一些标注与注释:
Bitmap bmp2 = bmpData.ToManagedImage();
Graphics g = Graphics.FromImage(bmp2);
Pen pen0 = new Pen(Color.FromArgb(255, 0, 0), 2);
Pen pen1 = new Pen(Color.FromArgb(220, 220, 220), 2);
Pen pen2 = new Pen(Color.FromArgb(0, 0, 220), 4);
SolidBrush bh = new SolidBrush(Color.Bisque);
string txt = "Radius\tCenter(x,y)";
List<PointF> PP = new List<PointF>();//记录圆心
foreach (var circles in LH)
{
foreach (HoughCircle circle in circles)
{
txt += "\r\n" + circle.Radius + "\t(" + circle.X + "," + circle.Y + ")";
g.DrawEllipse(pen0, circle.X - circle.Radius, circle.Y - circle.Radius, 2 * circle.Radius, 2 * circle.Radius);
g.DrawEllipse(pen1, circle.X - 4, circle.Y - 4, 8, 8);//圆心
//标出半径:
g.DrawLine(pen2, circle.X, circle.Y,circle.X+ circle.Radius, circle.Y);
g.DrawString(string.Format("{0:#.##}", circle.Radius), new Font("", 14), bh, circle.X + circle.Radius/3, circle.Y-14);
PP.Add(new PointF(circle.X , circle.Y));
}
}
g.DrawLine(pen1, PP.ElementAt(0), PP.ElementAt(1));
double dis = Math.Sqrt((PP.ElementAt(1).X- PP.ElementAt(0).X) *(PP.ElementAt(1).X - PP.ElementAt(0).X) +(PP.ElementAt(1).Y - PP.ElementAt(0).Y) *(PP.ElementAt(1).Y - PP.ElementAt(0).Y));
txt += "\r\n圆心距:" + dis;
//计算连线的中点,打印文本:
float xt = (PP.ElementAt(1).X + PP.ElementAt(0).X) / 2;
float yt = (PP.ElementAt(1).Y + PP.ElementAt(0).Y) / 2;
g.DrawString(string.Format("{0:#.##}",dis), new Font("", 14), bh, xt, yt-14);//标注圆心距
g.DrawString("霍夫(Hough)两个圆及其圆心距检测", new Font("", 12), bh, bmp2.Width/3,bmp2.Height-50);
textBox1.Text = txt;
pictureBox1.Image = bmp2;