EmguCV学习笔记 C# 6.S 特别示例
版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。
EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。
教程VB.net版本请访问:EmguCV学习笔记 VB.Net 目录-CSDN博客
教程C#版本请访问:EmguCV学习笔记 C# 目录-CSDN博客
笔者的博客网址:https://blog.csdn.net/uruseibest
教程配套文件及相关说明以及如何获得pdf教程和代码,请移步:EmguCV学习笔记
学习VB.Net知识,请移步: vb.net 教程 目录_vb中如何用datagridview-CSDN博客
学习C#知识,请移步:C# 教程 目录_c#教程目录-CSDN博客
6.S 特别示例
6.S.1图像中的圆半径
本示例来自一名网友咨询如何获得图像中圆形的半径,其中有两个十字作为标记,十字之间距离为100mm。如下图:
图6-28 根据给出的定位标记求圆半径
【代码位置:frmChapter6_S1】Button1_Click、PointFToPoint
private void Button1_Click(object sender, EventArgs e)
{
Mat msrc = new Mat("C:\\learnEmgucv\\celiang.jpg", ImreadModes.Color);
Mat mgray = new Mat();
CvInvoke.CvtColor(msrc, mgray, ColorConversion.Bgr2Gray);
Mat kernel = new Mat();
kernel = CvInvoke.GetStructuringElement(ElementShape.Cross, new Size(3, 3), new Point(-1, -1));
Mat merode = new Mat();
//这里使用了2次迭代
CvInvoke.Dilate(mgray, merode, kernel, new Point(-1, -1), 1, BorderType.Constant, new MCvScalar());
CvInvoke.Threshold(merode, merode, 200, 255, ThresholdType.BinaryInv);
ImageBox1.Image = merode;
//获得所有轮廓
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
VectorOfRect hierarchy = new VectorOfRect();
CvInvoke.FindContours(merode, contours, hierarchy, RetrType.List, ChainApproxMethod.ChainApproxSimple);
Mat m2 = new Mat(merode.Size, DepthType.Cv8U, 1);
m2.SetTo(new MCvScalar(0));
//圆轮廓
VectorOfPoint contourCircle =new VectorOfPoint();
//圆轮廓的周长
Double perimeter =0;
//绘制轮廓
for (int i = 0; i < contours.Size; i++)
{
VectorOfPoint carea = contours[i];
//获得轮廓面积
Double area = CvInvoke.ContourArea(carea, false);
//符合条件时,绘制轮廓,排除圆形,只保留十字线
//本图中圆形面积为2449,直线面积为8,需要根据实际情况调整
if (area < 200)
{
CvInvoke.DrawContours(m2, contours, i, new MCvScalar(255),1);
}
else
{
//得到圆形,图像中只有三个轮廓:2个交叉十字线段、1个圆形
//这里简化操作,否则在多个轮廓情况下,应获取最大面积的轮廓判断为圆形
contourCircle = contours[i];
//获取轮廓周长
perimeter = CvInvoke.ArcLength(contourCircle, true);
}
}
ImageBox1.Image = m2;
//使用HoughLinesP方法检测图像中的直线,并将其绘制到图像
//因为本图中十字线上的线段较短,所以这里阈值设置很小
LineSegment2D[] lines = CvInvoke.HoughLinesP(m2, 1, Math.PI / 180, 5, 5, 80);
Mat m3 = new Mat(merode.Size, DepthType.Cv8U, 3);
m3.SetTo(new MCvScalar(0, 0, 0));
foreach (LineSegment2D line in lines)
CvInvoke.Line(m3, line.P1, line.P2, new MCvScalar(0, 255, 0), 2);
ImageBox2.Image = m3;
//对直线进行分类,将其分为垂直和水平两类:
List<LineSegment2D> verticalLines = new List<LineSegment2D>();
List<LineSegment2D> horizontalLines = new List<LineSegment2D>();
//计算每条直线的倾斜角度来进行分类,
//将倾斜角度在60 - 120度之间的直线划分为垂直类,
//将倾斜角度在30 - 150度之间的直线划分为水平类。
foreach (LineSegment2D line in lines)
{
Double angle = Math.Atan2(line.P2.Y - line.P1.Y, line.P2.X - line.P1.X) * 180 / Math.PI;
if (angle < 0)
angle += 180;
if (angle > 60 && angle < 120)
verticalLines.Add(line);
else if ( angle > 150 || angle< 30 )
horizontalLines.Add(line);
}
//对垂直和水平直线进行匹配,并计算十字中心点的位置:
List<PointF> intersections = new List<PointF>();
//得到两个相交点
foreach (LineSegment2D verticalLine in verticalLines)
{
foreach (LineSegment2D horizontalLine in horizontalLines)
{
//基于图像中两条直线真实相交,
//如果垂直线的中点X坐标在水平线两端点X坐标之间
//那么,这条垂直线段和这条水平线段相交
Single centerX = (verticalLine.P1.X + verticalLine.P2.X) / 2;
if (horizontalLine.P1.X < horizontalLine.P2.X)
{
if ((centerX > horizontalLine.P1.X) && (centerX < horizontalLine.P2.X))
{
PointF intersectionPoint = new PointF(
(horizontalLine.P1.X + horizontalLine.P2.X + verticalLine.P1.X + verticalLine.P2.X) / 4,
(horizontalLine.P1.Y + horizontalLine.P2.Y + verticalLine.P1.Y + verticalLine.P2.Y) / 4
);
intersections.Add(intersectionPoint);
}
}
else
{
if (centerX > horizontalLine.P2.X && centerX < horizontalLine.P1.X)
{
PointF intersectionPoint = new PointF(
(horizontalLine.P1.X + horizontalLine.P2.X + verticalLine.P1.X + verticalLine.P2.X) / 4,
(horizontalLine.P1.Y + horizontalLine.P2.Y + verticalLine.P1.Y + verticalLine.P2.Y) / 4
);
intersections.Add(intersectionPoint);
}
}
}
}
if (intersections.Count != 2)
{
MessageBox.Show("未能获得两个十字线的交叉点");
return;
}
CvInvoke.Line(msrc, PointFToPoint(intersections[0]), PointFToPoint(intersections[1]), new MCvScalar(0, 255, 0), 2);
CvInvoke.Imshow("m3", msrc);
//计算两个交点的距离
Double distance = Math.Sqrt(
Math.Pow((intersections[0].X - intersections[1].X),2) +Math.Pow((intersections[0].Y - intersections[1].Y) ,2)
);
//实际中两交点距离为100毫米,计算相应比例
Double proportion = 100 / distance;
//以下是基于最小外接圆来计算实际圆半径
CircleF cf = CvInvoke.MinEnclosingCircle(contourCircle);
//获得外接圆形
CvInvoke.Circle(msrc, new Point((int)cf.Center.X, (int)cf.Center.Y), (int)cf.Radius, new MCvScalar(0, 0, 255), 2);
CvInvoke.Imshow("m4", msrc);
//实际圆半径
Double realradius1 = proportion * cf.Radius;
//以下是基于轮廓周长来计算实际圆半径
//实际圆周长
Double realperimeter = perimeter * proportion;
//图像中的圆半径
Double radius = (perimeter / Math.PI) / 2;
//实际圆半径
Double realradius2 = proportion * radius;
MessageBox.Show("最小外接圆来计算实际圆半径:" + realradius1 + "\r\n" +
"基于轮廓周长来计算实际圆半径:" + realradius2);
}
输出结果如下图所示:
图6-29 求出的圆半径