原图
PCB原图文件百度云链接:
https://pan.baidu.com/s/1Q4JYpmY6epPoO6dP45GcRw
提取码:zzxd
代码
//计算直线倾斜角度
public double ComputeAngle(CircleF referencePoint , CircleF rightPoint)
{
float left_rightXValue = referencePoint.Center.X - rightPoint.Center.X;
float left_rightYValue = referencePoint.Center.Y - rightPoint.Center.Y;
float kValue = left_rightYValue / left_rightXValue;
float slopeValue = 0;
float angle = 0;
double Result = 0;
if (left_rightXValue == 0.0)
slopeValue = (float)(Math.PI / 2.0);
else
slopeValue = (float)Math.Atan(Math.Abs(kValue));
if (left_rightYValue >= 0.0)
angle = (float)slopeValue * -1;
else if (left_rightYValue < 0.0)
angle = slopeValue;
else if ((left_rightXValue >= 0.0) && (left_rightYValue < 0.0))
angle = slopeValue;
//Angle
Result = -(180 / Math.PI) * angle;
return Result;
}
//主函数代码
private void FindAngle_Click(object sender, EventArgs e) // return angle
{
//不用旋转
Bitmap bmp = (Bitmap)pb1.Image;
Image<Gray, byte> grayImage = bmp.ToImage<Gray, byte>();
ResultImage = grayImage.Clone().Convert<Bgr, byte>();
//threshold
Image<Gray, byte> thresholdImage = new Image<Gray, byte>(grayImage.Width, grayImage.Height);
CvInvoke.Threshold(grayImage, thresholdImage, 80, 255, ThresholdType.Binary);
thresholdImage.Save(@"D:\1test\pcb\1thresholdImage.bmp");
//------------------find contours && fill up--------------------------------------------------------------------------------------------
VectorOfVectorOfPoint thresholdCountours = new VectorOfVectorOfPoint();
VectorOfVectorOfPoint totalCircleCoutours = new VectorOfVectorOfPoint();
Mat h_mat_1 = new Mat();
CvInvoke.FindContours(thresholdImage, thresholdCountours, h_mat_1, Emgu.CV.CvEnum.RetrType.External, ChainApproxMethod.ChainApproxSimple);
Image<Gray, byte> mask_1 = new Image<Gray, byte>(grayImage.Width, grayImage.Height);
List<double> Perimeters = new List<double>();
List<double> Areas = new List<double>();
informations.Text = "";
//---------------------Retain circles
for (int i = 0; i < thresholdCountours.Size; i++)
{
var area = CvInvoke.ContourArea(thresholdCountours[i], false);
double perimeter = CvInvoke.ArcLength(thresholdCountours[i], true); //length
if (area > 7500)
{
Areas.Add(area);
if (perimeter < 500 && perimeter > 320)
{
Perimeters.Add(perimeter);
totalCircleCoutours.Push(thresholdCountours[i]);
informations.Text += "area-" + area.ToString() + "ArcLength:" + perimeter.ToString(); //121779
informations.Text += Environment.NewLine;
//CvInvoke.FillPoly(mask_1, contours_1[i], new MCvScalar(255, 255, 255));
CvInvoke.DrawContours(mask_1, thresholdCountours, i, new MCvScalar(255, 255, 255), 2);
}
}
}
mask_1.Save(@"D:\1test\pcb\2mask_1.bmp");
#region
//all circle
//mask_1.Save(@"D:\1test\pcb\2fillup.bmp");
//------------------fit smallest circle
Image<Gray, byte> MinEnclosingCircleImage = new Image<Gray, byte>(grayImage.Width, grayImage.Height);
List<double> yPositions = new List<double>();
List<double> xPositions = new List<double>();
List<PointF> totalPoints = new List<PointF>();
List<CircleF> circleFs = new List<CircleF>();
int pointIndex = 0;
for (int i = 0; i < totalCircleCoutours.Size; i++)
{
CvInvoke.DrawContours(mask_1, totalCircleCoutours, i, new MCvScalar(255, 255, 255), 2);
CircleF circleF = CvInvoke.MinEnclosingCircle(totalCircleCoutours[i]);
MinEnclosingCircleImage.Draw(circleF, new Gray(255), 1);
xPositions.Add(circleF.Center.X); //point y position
yPositions.Add(circleF.Center.Y); //point y position
totalPoints.Add(new PointF(circleF.Center.X, circleF.Center.Y)); //point position
circleFs.Add(circleF);
pointIndex++;
}
#endregion
#region//------------------------------------小的PCB------------------------------------
if (pointIndex <= 6) //the point count == 6 small
{
thresholdImage.Save(@"D:\1test\pcb\temp\1-1thresholdImage.bmp");
//------------------find biggest circle contours && fill up--------------------------------------------------------------------------------------------
VectorOfVectorOfPoint contours_2 = new VectorOfVectorOfPoint();
Mat h_mat_2 = new Mat();
CvInvoke.FindContours(thresholdImage, contours_2, h_mat_2, Emgu.CV.CvEnum.RetrType.External, ChainApproxMethod.ChainApproxSimple);
Image<Bgr, byte> TheBiggestCircleCountours = new Image<Bgr, byte>(grayImage.Width, grayImage.Height);
List<double> perimeters = new List<double>();
List<double> Areas2 = new List<double>();
VectorOfVectorOfPoint BiggestCircleContours = new VectorOfVectorOfPoint();
informations.Text = "";
//find big circle
for (int i = 0; i < contours_2.Size; i++)
{
var area = CvInvoke.ContourArea(contours_2[i], false);
double perimeter = CvInvoke.ArcLength(contours_2[i], false); //length
//if (area > 7500 && area < 9000)
if (area > 20000)
{
//Areas2.Add(area);
//perimeters.Add(perimeter);
//if (perimeter > 300 && perimeter < 450)
if (perimeter > 300)
{
informations.Text += "i-" + "area-" + area.ToString() + "ArcLength:" + perimeter.ToString(); //121779
informations.Text += Environment.NewLine;
//CvInvoke.FillPoly(mask_1, contours_1[i], new MCvScalar(255, 255, 255));
CvInvoke.DrawContours(TheBiggestCircleCountours, contours_2, i, new MCvScalar(255, 255, 255), 2);
BiggestCircleContours.Push(contours_2[i]);
}
}
}
TheBiggestCircleCountours.Save(@"D:\1test\pcb\temp\1-2TheBiggestCircleCountours.bmp");
//follow circle countours to fit big circle
//Image<Gray, byte> circle_gray2 = TheBiggestCircleCountours.Clone().Convert<Gray, byte>();
Image<Gray, byte> MinEnclosingCircleImage_2 = new Image<Gray, byte>(grayImage.Width, grayImage.Height); //display
List<double> yPositions_2 = new List<double>();
List<double> xPositions_2 = new List<double>();
List<PointF> BiggestCircleTotal = new List<PointF>();
int pointIndex2 = 0;
for (int i = 0; i < BiggestCircleContours.Size; i++)
{
CircleF circleF = CvInvoke.MinEnclosingCircle(BiggestCircleContours[i]);
MinEnclosingCircleImage.Draw(circleF, new Gray(255), 1);
xPositions_2.Add(circleF.Center.X); //point y position
yPositions_2.Add(circleF.Center.Y); //point y position
BiggestCircleTotal.Add(new PointF(circleF.Center.X, circleF.Center.Y)); //point position
pointIndex2++;
CvInvoke.DrawContours(MinEnclosingCircleImage_2, BiggestCircleContours, i, new MCvScalar(255, 255, 255), 2);
}
MinEnclosingCircleImage_2.Save(@"D:\1test\pcb\temp\1-3MinEnclosingCircleImage_2.bmp");
//except top and bottom
double minY_2 = yPositions_2.ToArray().Min();
double maxY_2 = yPositions_2.ToArray().Max();
List<PointF> theTwoPoints_2 = new List<PointF>();
double y_length_2 = (maxY_2 - minY_2) / 4;
for (int i = 0; i < BiggestCircleTotal.Count; i++)
{
//compare point position
if (Convert.ToDouble(BiggestCircleTotal[i].Y) > (minY_2 + y_length_2) &&
Convert.ToDouble(BiggestCircleTotal[i].Y) < (maxY_2 - y_length_2)) //except top and bottom
{
theTwoPoints_2.Add(BiggestCircleTotal[i]);
}
}
//float angle_temp_2 = 0;
//float xx_2, yy_2;
List<float> xTwo_2 = new List<float>();
List<float> yTwo_2 = new List<float>();
CircleF centerPointF2 = new CircleF();//small pcb centerPoint
leftCircleF = new CircleF(); //左点
rightCircleF = new CircleF(); //右点
for (int i = 0; i < theTwoPoints_2.Count; i++)
{
xTwo_2.Add(theTwoPoints_2[i].X);
yTwo_2.Add(theTwoPoints_2[i].Y);
}
// conference leftCircleF and rightCircleF
for (int i = 0; i < theTwoPoints_2.Count; i++)
{
if (theTwoPoints_2[i].X == xTwo_2.Min())
{
leftCircleF.Center = theTwoPoints_2[i];
}
else
{
rightCircleF.Center = theTwoPoints_2[i];
}
}
Image<Bgr, byte> smallPcbResultImage = grayImage.ToBitmap().ToImage<Bgr, byte>();
x = leftCircleF.Center.X;
y = leftCircleF.Center.Y;
// conference ReferencePoint
ReferencePointF = leftCircleF;
//----compute Angle----
//Angle = ComputeAngle(ReferencePointF, rightCircleF);
//----log----
//informations.Text += Angle.ToString();
informations.Text += "圆左点坐标:" + x.ToString() + "," + y.ToString();
informations.Text += Environment.NewLine;
centerPointF2.Center = new PointF(float.Parse(x.ToString()), float.Parse(y.ToString()));
centerPointF2.Radius = 5;
//----draw----
smallPcbResultImage.Draw(centerPointF2, new Bgr(255, 0, 0), 5);
CvInvoke.Line(smallPcbResultImage, new Point((int)rightCircleF.Center.X, (int)rightCircleF.Center.Y), new Point((int)ReferencePointF.Center.X, (int)ReferencePointF.Center.Y), new MCvScalar(255, 255, 255), 2, LineType.EightConnected, 0);
//--save--
smallPcbResultImage.Save(@"D:\1test\pcb\temp\1-4smallPcbResultImage.bmp");
//pb2.Image = smallPcbResultImage.ToBitmap();
}
#endregion/* ------------------------------------小的PCB End------------------------------------*/
else
{
//------------------------------- using y position to order the point position save center point-------------------------------
double minY = yPositions.ToArray().Min();
double maxY = yPositions.ToArray().Max();
double minX = xPositions.ToArray().Min();
double maxX = xPositions.ToArray().Max();
PointF centerPoint = new PointF();
CircleF centerCircleF = new CircleF();
List<PointF> theTwoPoints = new List<PointF>();
double y_length = (maxY - minY) / 4;
double x_length = (maxX - minX) / 4;
//x top line
CvInvoke.Line(ResultImage, new Point((int)(minX + x_length), (int)(minY + y_length)), new Point((int)(maxX - x_length), (int)(minY + y_length)), new MCvScalar(0, 255, 255), 20, LineType.EightConnected, 0);
//y left line
CvInvoke.Line(ResultImage, new Point((int)(minX + x_length), (int)(minY + y_length)), new Point((int)(minX + x_length), (int)(maxY - y_length)), new MCvScalar(255, 0, 255), 20, LineType.EightConnected, 0);
//x bottom line
CvInvoke.Line(ResultImage, new Point((int)(minX + x_length), (int)(maxY - y_length)), new Point((int)(maxX - x_length), (int)(maxY - y_length)), new MCvScalar(0, 255, 255), 20, LineType.EightConnected, 0);
//y right line
CvInvoke.Line(ResultImage, new Point((int)(maxX - x_length), (int)(minY + y_length)), new Point((int)(maxX - x_length), (int)(maxY - y_length)), new MCvScalar(255, 0, 255), 20, LineType.EightConnected, 0);
for (int i = 0; i < totalPoints.Count; i++)
{
//save center point
if (Convert.ToDouble(totalPoints[i].Y) > (minY + y_length) &&
Convert.ToDouble(totalPoints[i].Y) < (maxY - y_length) && // except top and botton
Convert.ToDouble(totalPoints[i].X) > (minX + x_length) &&
Convert.ToDouble(totalPoints[i].X) < (maxX - x_length)) //except left and right
{
centerPoint = totalPoints[i];
centerCircleF.Center = centerPoint;
centerCircleF.Radius = 5;
MinEnclosingCircleImage.Draw(centerCircleF, new Gray(255), 1);
}
else if (Convert.ToDouble(totalPoints[i].Y) > (minY + y_length) && //find left and right points
Convert.ToDouble(totalPoints[i].Y) < (maxY - y_length) && // except top and botton
((Convert.ToDouble(totalPoints[i].X) > (minX - x_length) && Convert.ToDouble(totalPoints[i].X) < (minX + x_length)) ||
(Convert.ToDouble(totalPoints[i].X) < (maxX + x_length) && Convert.ToDouble(totalPoints[i].X) > (maxX - x_length)))) //except left and right
{
theTwoPoints.Add(totalPoints[i]);
}
}
x = centerPoint.X;
y = centerPoint.Y;
ReferencePointF.Center = centerPoint;
informations.Text += "圆中心点坐标:" + x.ToString() + "," + y.ToString();
informations.Text += Environment.NewLine;
//leftPoint and rightPoint
if (theTwoPoints[0].X - theTwoPoints[1].X > theTwoPoints[1].X - theTwoPoints[0].X)
{
rightCircleF.Center = theTwoPoints[0];
leftCircleF.Center = theTwoPoints[1];
}
else
{
rightCircleF.Center = theTwoPoints[1];
leftCircleF.Center = theTwoPoints[0];
}
pb2.Image = MinEnclosingCircleImage.ToBitmap();
}
//Follow center point to resure left point and right point
//double roiTopX = grayImage.Width;
//double roiTopY = grayImage.Height;
//draw left and right points
leftCircleF.Radius = 10;
rightCircleF.Radius = 10;
ReferencePointF.Radius = 3;
ResultImage.Draw(leftCircleF, new Bgr(0, 255, 0), 20);
ResultImage.Draw(rightCircleF, new Bgr(0, 255, 0), 20);
ResultImage.Draw(ReferencePointF, new Bgr(0, 0, 255), 10);
//dra line
CvInvoke.Line(ResultImage, new Point((int)rightCircleF.Center.X, (int)rightCircleF.Center.Y), new Point((int)ReferencePointF.Center.X, (int)ReferencePointF.Center.Y), new MCvScalar(255, 255, 255), 2, LineType.EightConnected, 0);
//compute angle
Angle = ComputeAngle(ReferencePointF, rightCircleF);
double Angle2 = ComputeAngle(leftCircleF, rightCircleF);
//--log--
informations.Text += "Angle:" + Angle.ToString() ;
informations.Text += "Angle2:" + Angle2.ToString();
ResultImage.Save(@"D:\1test\pcb\4smallPcbResultImage.bmp");
pb2.Image = ResultImage.ToBitmap();
}
步骤描述:
- 1、二值化
- 2、寻找二值化后图像的轮廓(设置轮廓周长以及面积范围,保留圆轮廓)
- 3、使用保留的轮廓进行MinEnclosingCircle(拟合圆)
- 4、根据圆心计算参考点以及直线的倾斜角度
- 注意:因为有三种不同的PCB所以对于其中一种使用了小的PCB检测方法
结果图:
描述:考虑到畸变影响因素,大的PCB以中间圆(红色点)为参考点,小的PCB以中间左边圆为参考点,白色线为旋转角度参考线