OpenCV卡尺工具

1. 卡尺区域的生成及绘制,基于GDI+完成图形绘制

OpenCV卡尺工具_第1张图片

 实现代码:

 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 point2Fs)
        {
                  
            // 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(0, 0) + point2Fs1[i].Y * m.At(0, 1) + m.At(0, 2);
                    var y = point2Fs1[i].X * m.At(1, 0) + point2Fs1[i].Y * m.At(1, 1) + m.At(1, 2);
                    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 array=new List ();
                 
            for (int i = 0; i < srcImage.Cols; i++)
            {
                int sumvalue = 0;
                for (int j = 0; j < srcImage.Rows; j++)
                {
                    sumvalue += srcImage.Get(j, i);//每一列的像素和
                }
                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(n, w, array[w]);

                }
            }

            return lineImage;
        }

c)二阶求导零点寻找边缘点

     static public Mat FindEdges(Mat srcImage, byte edgeThreshold, ref List zeroPList,
             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(out byte[] bytearray);
                zeroPList = new List();//极值点坐标集合
                for (int i = 0; i < CannyMat.Cols; i++)
                {            
                    if (CannyMat.Get(0, i) == 255
                        &&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(out byte[] values);
                Mat thdMat = new Mat();
                Cv2.Threshold(projectMat, thdMat, edgeThreshold, 255, ThresholdTypes.Binary);
                //thdMat.GetArray(out byte[] values);
                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(0, i);
                    if (CannyMat.Get(i, 0) == 255)
                        zeroPList.Add(new Point2f(cenX, i));
                }
                return CannyMat;
            }
        }

3. 直线拟合

#1:最小二乘法

 static public M_LINE fitLine( Mat srcImage,   List fitPList)
        {
            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. 亚像素处理

OpenCV卡尺工具_第2张图片

P1、P2、P3、P4为邻近四个像素,a、b、c、d为待插值坐标距离邻近像素中心位置距离,绿点所表示的像素值可由下面公式计算得出

(1-a)(p2\times (1-b)+p1\times b)+a\times (p3\times (1-b)+p4\times b)

 5. 检测效果图

OpenCV卡尺工具_第3张图片 

OpenCV卡尺工具_第4张图片 

 PS:因为在网上opencvsharp开发的用例和参考资料相对较少,所以把这些分享出来和大家一起学习和探讨,有不足之处可以一起沟通交流!

你可能感兴趣的:(opencv,c#)