1. 卡尺区域的生成及绘制,基于GDI+完成图形绘制
实现代码:
else if(regionEx?.Region is RotatedRectF)
{
RotatedRectF rrect = (RotatedRectF)regionEx.Region;
using (var graph = new GraphicsPath())
{
PointF Center = new PointF(rrect.cx / sizeratio, rrect.cy / sizeratio);
graph.AddRectangle(new RectangleF( rrect.getrectofangleEqualZero().X / sizeratio,
rrect.getrectofangleEqualZero().Y / sizeratio,
rrect.getrectofangleEqualZero().Width / sizeratio,
rrect.getrectofangleEqualZero().Height / sizeratio));
graph.AddLine(new PointF((rrect.cx - rrect.Width / 2) / sizeratio, rrect.cy / sizeratio),
new PointF((rrect.cx + rrect.Width/2) / sizeratio, rrect.cy / sizeratio));
/
RotatedRectF rotatedRectF = new RotatedRectF((rrect.cx + rrect.Width / 2) / sizeratio,
rrect.cy / sizeratio,20 / sizeratio, 10 / sizeratio, 0);
PointF[] point2Fs = rotatedRectF.getPointF();
graph.AddLine(new PointF((rrect.cx + rrect.Width / 2) / sizeratio,
rrect.cy / sizeratio), new PointF(point2Fs[0].X, point2Fs[0].Y));
graph.AddLine(new PointF((rrect.cx + rrect.Width / 2) / sizeratio,
rrect.cy / sizeratio), new PointF(point2Fs[3].X, point2Fs[3].Y));
/
var a = rrect.angle * (Math.PI / 180);
var n1 = (float)Math.Cos(a);
var n2 = (float)Math.Sin(a);
var n3 = -(float)Math.Sin(a);
var n4 = (float)Math.Cos(a);
var n5 = (float)(Center.X * (1 - Math.Cos(a)) + Center.Y * Math.Sin(a));
var n6 = (float)(Center.Y * (1 - Math.Cos(a)) - Center.X * Math.Sin(a));
graph.Transform(new Matrix(n1, n2, n3, n4, n5, n6));
graphics.DrawPath(new Pen(regionEx.Color, regionEx.Size), graph);
}
}
2. 查找边缘点
a)参照ROI的角度,先将图像进行旋转
public static Mat RotateAffineOfSizeNoChange(this Mat img, double angle, ref List
{
// angle 0-360
while (angle < 0) angle += 360;
if (angle > 360) angle %= 360;
// 计算旋转后的图像尺寸
int w0 = img.Width, h0 = img.Height;
float w =img.Width, h = img.Height;
Point2f center = new Point2f(w0 / 2f, h0 / 2f);
Mat m = Cv2.GetRotationMatrix2D(center, angle, 1.0);
if (angle == 90 || angle == 270)
{
w = img.Height;
h = img.Width;
var mIndex = m.GetGenericIndexer
mIndex[0, 2] += (w - w0) / 2f;
mIndex[1, 2] += (h - h0) / 2f;
}
//坐标点位集合转换
if (point2Fs.Count>0)
{
Point2f[] point2Fs1 = new CVPointF[point2Fs.Count];
point2Fs.CopyTo(point2Fs1);
for (int i = 0; i < point2Fs.Count; i++)
{
var x = point2Fs1[i].X * m.At
var y = point2Fs1[i].X * m.At
point2Fs1[i].X = (int)Math.Round(x, 0);
point2Fs1[i].Y = (int)Math.Round(y, 0);
}
point2Fs = point2Fs1.ToList
}
Mat rotated = new Mat();
Cv2.WarpAffine(img, rotated, m, new CVSize(w, h));
return rotated;
}
b)图像平均灰度投影
static Mat VerticalLine(Mat srcImage)//垂直线条检测
{
List
for (int i = 0; i < srcImage.Cols; i++)
{
int sumvalue = 0;
for (int j = 0; j < srcImage.Rows; j++)
{
sumvalue += srcImage.Get
}
byte Average_strength =(byte)(sumvalue / srcImage.Rows);//每一列的平均强度
array.Add(Average_strength);
}
Mat lineImage = new Mat(1, array.Count, MatType.CV_8UC1,new Scalar(0,0,0));
int count = array.Count();
//恢复直线
for (int n = 0; n < lineImage.Rows; n++)
{
for (int w = 0; w < count; w++)
{
lineImage.Set
}
}
return lineImage;
}
c)二阶求导零点寻找边缘点
static public Mat FindEdges(Mat srcImage, byte edgeThreshold, ref List
EumProjection_direction eumProjection_Direction= EumProjection_direction.vertical )
{
Rect boundary = srcImage.BoundingRect();
Mat GaussMat = Filter.ImageFilter.GaussImage(srcImage, new Size(3, 3), 0);
if (eumProjection_Direction== EumProjection_direction.vertical)
{
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>3&&i< CannyMat.Cols-2)//去掉首位干扰点
zeroPList.Add(new Point2f(i, cenY));
}
return CannyMat;
}
else
{
float cenX = (boundary.X + boundary.Width) / 2;
Mat projectMat = HorizonLine(srcImage);//平均值灰度投影
//projectMat.GetArray
Mat thdMat = new Mat();
Cv2.Threshold(projectMat, thdMat, edgeThreshold, 255, ThresholdTypes.Binary);
//thdMat.GetArray
Mat CannyMat = on_Canny(thdMat, edgeThreshold, 255);//二阶求导零点
//Mat mat = CannyMat.FindNonZero();
zeroPList = new List
for (int i = 0; i < CannyMat.Rows; i++)
{
//byte ss = CannyMat.Get
if (CannyMat.Get
zeroPList.Add(new Point2f(cenX, i));
}
return CannyMat;
}
}
3. 直线拟合
#1:最小二乘法
static public M_LINE fitLine( Mat srcImage, List
{
if (fitPList.Count < 2) return default;
//Cv2.FitLine(fitPList, DistanceTypes.L2, 0, 0.01, 0.01);
var line = Cv2.FitLine(fitPList, DistanceTypes.L1, 0, 0, 0);
line.FitSize(srcImage.Width, srcImage.Height, out var p1, out var p2);
return new M_LINE { sp = p1, ep = p2 };
}
#2: 随机抽样一致性法
public static M_LINE FitLineRansac( Mat srcImage, Vector2[] fitPList,
int iterations=1000, double sigma=1.0)
{
int bestScore = -1;
Line2D result = null;
Random random = new Random();
for (int i = 0; i < iterations; i++)
{
if (bestScore > fitPList.Length * 0.5)
break;
var indexes = GetRandomIndexes(random, 0, fitPList.Length);
var p1 = fitPList[indexes.Item0];
var p2 = fitPList[indexes.Item1];
var dir = Vector2.Normalize(p2 - p1);
var line = new Line2D(dir.X, dir.Y, p2.X, p2.Y);
int score = 0;
for (int j = 0; j < fitPList.Length; j++)
{
if (line.Distance(fitPList[j].X, fitPList[j].Y) < sigma)
score += 1;
}
if (score > bestScore)
{
bestScore = score;
result = line;
}
}
result.FitSize(srcImage.Width, srcImage.Height, out var pa, out var pb);
return new M_LINE { sp = pa, ep = pb };
}
4. 亚像素处理
P1、P2、P3、P4为邻近四个像素,a、b、c、d为待插值坐标距离邻近像素中心位置距离,绿点所表示的像素值可由下面公式计算得出
5. 检测效果图
PS:因为在网上opencvsharp开发的用例和参考资料相对较少,所以把这些分享出来和大家一起学习和探讨,有不足之处可以一起沟通交流!