/* 工业视觉_57:霍夫(Hough)直线识别,交点与夹角
* 工业视觉,目标很明确:快,准,稳. 快:开发快,运行速度快;准:高精度;稳:稳健
* 所以,目前"机器换人"项目大多采用工控电脑,搭建 Windows7+VS2019+EMGU(或AForge+Accord),这一方案见效最快.
* Hacon,Emgu的视觉库都很强大,而AForge+Accord库更全面更丰富(如:数学,人工智能,机器学习).
* 直线识别是工业视觉的入门,它常常包括两直线的几何关系(平行,相交,重合),夹角.
* 就单一直线而言,中学数学有多种直线方程,如两点式,点斜式,截距式,一般式.
* 霍夫(Hough)所提出的直线的表述方法是: 1,直线到原点的距离R; 2,直线的方位角Theta(返回四象限的反正切:Atan2(y,x))
* Hough算法的核心思想是进行大量的投票统计.
* 在自动化行业,常常与机器人协作,如钻孔,激光切割,那些交点坐标,夹角大小都是必需的.
* 霍夫(Hough)直线检测的官方例程(C#语言)在:
http://accord-framework.net/docs/html/T_Accord_Imaging_HoughLine.htm
--------- 编撰: 项道德(微信:daode1212),2021-07-07
*/
pictureBox1.Image = Image.FromFile("工业视觉_5(直线拟合).png"); // 2D161_直线与交点mini.jpg,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);
//建立霍夫变换器:
HoughLineTransformation lineTransform = new HoughLineTransformation();
//霍夫变换器处理数据:
lineTransform.ProcessImage(image);
Bitmap houghLineImage = lineTransform.ToBitmap();
中间处理数据真像:
pictureBox1.Image = houghLineImage;
MSG("中间投票统计处理数据真像,\r\n接着,继续呈现处理结果");
//为几何处理,创建线段列表:
List<Accord.Math.Geometry.Line> LL = new List<Accord.Math.Geometry.Line>();
//在"相对密集度=0.5"上提取直线: 0.15:高敏感, 0.75:低敏感
HoughLine[] lines = lineTransform.GetLinesByRelativeIntensity(0.5);
//创建非托管24位数据:
var bmpData =Accord.Imaging.UnmanagedImage.Create(bmp.Width,bmp.Height,PixelFormat.Format24bppRgb);
string txt = "Radius\tTheta\r\n";
//绘制各直线:
foreach (HoughLine line in lines)
{
//读取每一直线的半径(直线到原点的距离),方位角(-180...+180)
int r = line.Radius;
double t = line.Theta;
// 翻转方位角是负数的线段:
if (r < 0)
{
t += 180;
r = -r;
}
//转换角度制为弧度制:
t = (t / 180) * Math.PI;
//计算图像的中心点:
int w2 = bmp.Width / 2;
int h2 = bmp.Height / 2;
double x0 = 0, x1 = 0, y0 = 0, y1 = 0;
if (line.Theta != 0)
{
//非垂直方向的线:
x0 = -w2; // 最左点,most left point
x1 = w2; // 最右点,most right point
// 计算Y坐标:
y0 = (-Math.Cos(t) * x0 + r) / Math.Sin(t);
y1 = (-Math.Cos(t) * x1 + r) / Math.Sin(t);
}
else
{
// 若正好是垂直方向的线:
x0 = line.Radius;
x1 = line.Radius;
y0 = h2;
y1 = -h2;
}
txt += line.Radius+"\t"+ line.Theta + "\r\n";
//绘制直线到bmpData中:
Drawing.Line(bmpData,
new Accord.IntPoint((int)x0 + w2, h2 - (int)y0),
new Accord.IntPoint((int)x1 + w2, h2 - (int)y1),
Color.Red);
//每一线段加入到列表中:
var Li = Accord.Math.Geometry.Line.FromPoints(
new Accord.IntPoint((int)x0 + w2, h2 - (int)y0),
new Accord.IntPoint((int)x1 + w2, h2 - (int)y1));
LL.Add(Li);
}
//转到托管图像后,添加一些注释:
Bitmap bmp2= bmpData.ToManagedImage();
Graphics g = Graphics.FromImage(bmp2);
Pen pen0 = new Pen(Color.FromArgb(100, 0, 0), 2);
SolidBrush bh = new SolidBrush(Color.AliceBlue);
LL.Add(LL.ElementAt(0));//为形成封闭
for (int i=1;i<LL.Count;i++)
{
var l1=LL.ElementAt(i-1);
var l2=LL.ElementAt(i);
var pp = l1.GetIntersectionWith(l2);//获取两直线交点,null:无交点,两直线重合时抛出异常"System.InvalidOperationException": Thrown if the specified line is the same line as this line.
var aa = l1.GetAngleBetweenLines(l2);//输出相交直线的最小角(0...90)
if (pp != null)
{
g.FillEllipse(bh, pp.Value.X - 4, pp.Value.Y - 4, 8, 8);
g.DrawString(string.Format("夹角:{0:#.##}度\r\nP({1:#.##},{2:#.##})",aa, pp.Value.X, pp.Value.Y), new Font("",14), bh, pp.Value.X - 24, pp.Value.Y + 4);
}
}
g.DrawString("霍夫(Hough)直线识别,交点与夹角", new Font("", 14), bh,bmp2.Width/3, bmp2.Height-30);
textBox1.Text = txt;
//非托管转变为托管对象,并绘制到控件中:
pictureBox1.Image = bmp2;
工业视觉_5(直线拟合).png
效果图