/* 工业视觉_60:使用机器人美缝\切割\焊接
未来20年里,最火的行业之一:机器换人,智能使用机器人,传统工业改造成智慧工厂,智慧工业!
工业视觉,目标很明确:快,准,稳. 快:开发快,运行速度快;准:高精度;稳:稳健可靠
所以,目前"机器换人"项目大多采用工控电脑,搭建 Windows7+VS2019+EMGU(或AForge+Accord),这一方案见效最快.
Halcon,Emgu的视觉库都很强大,而AForge+Accord库更全面更丰富(如:数学,人工智能,机器学习).
直线识别是工业视觉的入门,它常常包括两直线的几何关系(平行,相交,重合),夹角.
当光线明暗比较均匀时,用统一的阈值可以过滤出直线.
当光线明暗问题较大时,应该分成小区块处理.
在使用机器人美缝\切割\焊接时,应该对相机时行纠正畸变,标定,光线均匀恒定等处理,
然后,合理的阈值与丰度是提高精度的关键!
--------- 编撰: 项道德(微信:daode1212),2021-07-10
*/
//载入照片:
pictureBox1.Image = Image.FromFile("2D161b_直线与交点.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.Invert().Apply(gray);//翻转成:背景黑色,线段为白色.若已经是背景黑色,线段为白色的图片不用再翻转
var ThresholdBinarization = new Accord.Imaging.Filters.Threshold(255-55).Apply(gray);//调节亮度:阈值二值化
pictureBox1.Image = ThresholdBinarization;
MSG("阈值二值化后有用信息查看.\r\n 接着,呈现处理结果");
//转为非托管数据:
Accord.Imaging.UnmanagedImage image = Accord.Imaging.UnmanagedImage.FromManagedImage(ThresholdBinarization);
//建立霍夫变换器:
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.25"上提取直线: 0.1:高敏感, 0.9:低敏感
HoughLine[] lines = lineTransform.GetLinesByRelativeIntensity(0.25);
//创建新24位位图的非托管数据:
// var bmpData = Accord.Imaging.UnmanagedImage.Create(bmp.Width, bmp.Height, PixelFormat.Format24bppRgb);
//复制原24位位图到非托管数据:
var bmpData = Accord.Imaging.UnmanagedImage.FromManagedImage(bmp);
string txt = "Radius\tTheta\r\n";//半径(直线到原点的距离),方位角
//绘制各直线:
foreach (HoughLine line in lines)
{
//读取每一直线的半径(直线到原点的距离),方位角(-180...+180)
int r = line.Radius;
double t = line.Theta;
// 翻转R(直线到原点的距离)是负数的线段:
if (r < 0)
{
t += 180;
r = -r;
}
//转换角度制为弧度制:
t = (t / 180) * Math.PI;
//计算图像的中心点:
int w2 = ww / 2;
int h2 = hh / 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.FromArgb(255, 255, 0));
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("", 10), bh, pp.Value.X - 24, pp.Value.Y + 4);
}
}
g.DrawString("机器人美缝,切割,焊接\r\n" + txt, new Font("", 10), bh, bmp2.Width / 3, 30);
textBox1.Text = txt;
//非托管转变为托管对象,并绘制到控件中:
pictureBox1.Image = bmp2;