1. 卡尺区域的生成及绘制,基于GDI+完成图形绘制
实现代码:
else if(regionEx?.Region is SectorF)
{
SectorF sectorF=(SectorF)regionEx.Region;
graphics.DrawEllipse(MyPens.assist, sectorF.x / sizeratio, sectorF.y / sizeratio,
sectorF.width / sizeratio, sectorF.height / sizeratio);
graphics.DrawPie(new Pen(regionEx.Color, regionEx.Size),
sectorF.x / sizeratio, sectorF.y / sizeratio,
sectorF.width / sizeratio, sectorF.height / sizeratio,
sectorF.startAngle, sectorF.sweepAngle);
}
2. 生成扇形卡尺区域:
///
/// 生成扇形卡尺区域
///
///
///
///
///
static private List
{
List
float cenPx = sectorF.centreP.X;
float cenPy = sectorF.centreP.Y;
float radius = (float)sectorF.getRadius;
SectorF inner = sectorF.getInnerSector();
SectorF outer = sectorF.getOuterSector();
double height = outer.getRadius - inner.getRadius;
float startA= sectorF.startAngle;
float endA = sectorF.getEndAngle;
float offsetA = 0;
if ( Math.Abs(Math.Abs(sectorF.sweepAngle) - 360)<=1)//起点和终点重合,重合度允许1度的误差
offsetA = 360 / calipersNum;
else
offsetA = sectorF.sweepAngle / (calipersNum - 1);//可正可负,依据起点与终点的位置来定
//获取圆周上的左边点作为卡尺工具的中心
//x = cx + r * cos(a)
//y = cy + r * sin(a)
for(int i=0;i< calipersNum;i++)
{
float angle = startA + offsetA * i;
double rad = angle / 180 * Math.PI;
float cx = (float)(cenPx + radius * Math.Cos(-rad));
float cy = (float)(cenPy - radius * Math.Sin(-rad));
RotatedRectF rotatedRectF1 = new RotatedRectF(cx, cy, (float)height,
calipersWidth, angle);
rotatedRectFs.Add(rotatedRectF1);
}
return rotatedRectFs;
}
3. 图像非裁切旋转:
///
/// 图像不裁剪方式旋转
///
///
///
///
///
///
///
public static Mat RotateAffine(this Mat img, double angle, ref Point2f point,ref float Tx,ref float Ty)
{
// angle 0-360
while (angle < 0) angle += 360;
if (angle > 360) angle %= 360;
// 计算旋转后的图像尺寸
int w0 = img.Width, h0 = img.Height;
double arc = -Math.PI * angle / 180f;
double c = Math.Cos(arc);
double s = Math.Sin(arc);
double minx = Math.Min(Math.Min(w0 * c - h0 * s, 0), Math.Min(w0 * c, -h0 * s));
double maxx = Math.Max(Math.Max(w0 * c - h0 * s, 0), Math.Max(w0 * c, -h0 * s));
double miny = Math.Min(Math.Min(w0 * s + h0 * c, 0), Math.Min(w0 * s, h0 * c));
double maxy = Math.Max(Math.Max(w0 * s + h0 * c, 0), Math.Max(w0 * s, h0 * c));
float w = (float)(maxx - minx);
float h = (float)(maxy - miny);
Point2f center = new Point2f(w0 / 2f, h0 / 2f);
Mat m = Cv2.GetRotationMatrix2D(center, angle, 1.0);
// 将原图像中心平移到旋转后的图像中心
var mIndex = m.GetGenericIndexer
Tx= (w - w0) / 2f;
Ty= (h - h0) / 2f;
mIndex[0, 2] += (w - w0) / 2f;
mIndex[1, 2] += (h - h0) / 2f;
var x = point.X * m.At
var y = point.X * m.At
point.X = (float)Math.Round(x, 3);
point.Y = (float)Math.Round(y, 3);
Mat rotated = new Mat();
Cv2.WarpAffine(img, rotated, m, new CVSize(w, h));
return rotated;
}
4. 查找边缘点:
///
/// 边缘点查找
///
/// 输入图
/// 投影方向
/// 边缘阈值
/// 边缘点坐标集合
///
static public Mat FindEdges(Mat srcImage, byte edgeThreshold, ref List
{
CVRect boundary = srcImage.BoundingRect();
Mat GaussMat = Filter.ImageFilter.GaussImage(srcImage, new Size(3, 3), 0);
float cenY = (boundary.Y + boundary.Height) / 2;
Mat projectMat = VerticalLine(GaussMat);//平均值灰度投影
Mat thdMat = new Mat();
Cv2.Threshold(projectMat, thdMat, edgeThreshold, 255, ThresholdTypes.Binary);
Mat CannyMat = on_Canny(thdMat, edgeThreshold, 255);//二阶求导零点
//Mat mat = CannyMat.FindNonZero();
//CannyMat.GetArray
zeroPList = new List
for (int i = 0; i < CannyMat.Cols; i++)
{
if (CannyMat.Get
&& i > 5 && i < CannyMat.Cols - 5)//去掉首位干扰点
zeroPList.Add(new Point2f(i, cenY));
}
return CannyMat;
}
5. 圆拟合:
#A:最小二乘法:
///
/// 圆拟合
///
///
///
static public M_CIRCLE fitCircle( List
{
if (fitPList.Count < 3) return default;
float Radius = 0.0f;
Point2f center = new Point2f();
double sum_x = 0.0f, sum_y = 0.0f;
double sum_x2 = 0.0f, sum_y2 = 0.0f;
double sum_x3 = 0.0f, sum_y3 = 0.0f;
double sum_xy = 0.0f, sum_x1y2 = 0.0f, sum_x2y1 = 0.0f;
int N = fitPList.Count();
for (int i = 0; i < N; i++)
{
double x = fitPList.ElementAt(i).X;
double y = fitPList.ElementAt(i).Y;
double x2 = x * x;
double y2 = y * y;
sum_x += x;
sum_y += y;
sum_x2 += x2;
sum_y2 += y2;
sum_x3 += x2 * x;
sum_y3 += y2 * y;
sum_xy += x * y;
sum_x1y2 += x * y2;
sum_x2y1 += x2 * y;
}
double C, D, E, G, H;
double a, b, c;
C = N * sum_x2 - sum_x * sum_x;
D = N * sum_xy - sum_x * sum_y;
E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x;
G = N * sum_y2 - sum_y * sum_y;
H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y;
a = (H * D - E * G) / (C * G - D * D);
b = (H * C - E * D) / (D * D - G * C);
c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N;
center.X = (float)a / (-2);
center.Y = (float)b / (-2);
Radius = (float)Math.Sqrt(a * a + b * b - 4 * c) / 2;
if (double.IsNaN(Radius))
return default;
return new M_CIRCLE { centreP= center,radius= Radius };
//得分验证
//double sum = 0;
//for (int i = 0; i < N; i++)
//{
// Point2f pti = fitPList.ElementAt(i);
// double ri = Math.Sqrt(Math.Pow(pti.X - center.X, 2) + Math.Pow(pti.Y - center.Y, 2));
// sum += ri / radius > 1 ? ri / radius - 1 : 1 - ri / radius;
//}
//double sorce = 1 - sum / N;
//return (float)sorce;
}
#B:随机采样一致性:
///
/// Ransac随机一致性圆拟合
///
///
///
///
///
///
static public M_CIRCLE RansacCircleFiler(List
int iterations = 1000, double sigma = 1.0)
{
int n = points.Count;
vpdExceptPoints = new List
if (n < 3)
{
return default;
}
Random random = new Random();
double bestScore = -1.0;
//List
//int iterations = (int)(Math.Log(1 - 0.99) / (Math.Log(1 - (1.00 / n))) * 10);
M_CIRCLE m_CIRCLE1=new M_CIRCLE () ;
for (int k = 0; k < iterations; k++)
{
int i1 = 0, i2 = 0, i3 = 0;
Point2f p1 = new Point2f(0, 0), p2 = new Point2f(0, 0), p3 = new Point2f(0, 0);
while (true)
{
i1 = random.Next(n);
i2 = random.Next(n);
i3 = random.Next(n);
if ((i1 != i2 && i1 != i3 && i2 != i3))
{
if ((points[i1].Y != points[i2].Y) && (points[i1].Y != points[i3].Y))
{
break;
}
}
}
p1 = points[i1];
p2 = points[i2];
p3 = points[i3];
//use three points to caculate a circle
Point2f pdP12 = GetPPCenter(p1, p2);
double dK1 = -1 / GetLineSlope(p1, p2);
double dB1 = pdP12.Y - dK1 * pdP12.X;
Point2f pdP13 = GetPPCenter(p1, p3);
double dK2 = -1 / GetLineSlope(p1, p3);
double dB2 = pdP13.Y - dK2 * pdP13.X;
Point2f pdCenter = new Point2f(0, 0);
pdCenter.X = (float)((dB2 - dB1) / (dK1 - dK2));
pdCenter.Y = (float)(dK1 * pdCenter.X + dB1);
double dR = GetPPDistance(pdCenter, p1);
double score = 0;
//vpdTemp.Clear();
for (int i = 0; i < n; i++)
{
double d = dR - GetPPDistance(points[i], pdCenter);
if (Math.Abs(d) < sigma)
{
score += 1;
}
//else
//{
// vpdTemp.Add(points[i]);
//}
}
if (score > bestScore)
{
bestScore = score;
//vpdExceptPoints = vpdTemp;
m_CIRCLE1 = new M_CIRCLE {centreP= pdCenter ,radius= (float)dR };
}
}
return m_CIRCLE1;
}
6. 检测效果图
PS:好东西要拿出来大家一起分享,共同进步!