OpenCvSharp函数:ContourArea、ArcLength、BoundingRect、MinAreaRect、BoxPoints

ContourArea计算轮廓面积

//函数原型1
double ContourArea(InputArray contour,
    bool oriented = false)

//函数原型2
double ContourArea(IEnumerable<Point> contour,
    bool oriented = false)

//函数原型3
double ContourArea(IEnumerable<Point2f> contour,
    bool oriented = false)

ContourArea计算轮廓面积:通过格林公式(Green formula)计算轮廓面积,返回的面积与非零像素与DrawContours或FillPoly可能不同。另外,如果轮廓自相互,计算的面积是有误(见图例中的3)

参数

说明

InputArray contour

IEnumerable<Point> contour

IEnumerable<Point2f> contour

待计算轮廓坐标组:若oriented为true,坐标的顺序会影响返回结果值的正负号

bool oriented

是否忽略方向:为false时,返回面积绝对值。

为true时,若轮廓坐标为逆时针时,值为负,若轮廓坐标为顺时针时,值为正。

FindContours的外轮廓为逆时针,内轮廓为顺时针。

ArcLength计算弧长

//函数原型1
double ArcLength(InputArray curve,
    bool closed)

//函数原型2
double ArcLength(IEnumerable<Point> curve,
    bool closed)

//函数原型3
double ArcLength(IEnumerable<Point2f> curve,
    bool closed)

ArcLength计算轮廓周长或曲线长度(两点的L2距离累加)

参数

说明

InputArray curve

IEnumerable<Point> curve

IEnumerable<Point2f> curve

轮廓/曲线坐标数组

bool closed

是否封闭

BoundingRect轮廓外接矩形

//函数原型1
Rect BoundingRect(InputArray curve)

//函数原型2
Rect BoundingRect(IEnumerable<Point> curve)

//函数原型3
Rect BoundingRect(IEnumerable<Point2f> curve)

BoundingRect返回坐标点集的外接矩形

X=minX,Y=minY

Width=maxX-minX,Height=maxY-minY

参数

说明

InputArray curve

IEnumerable<Point> curveIEnumerable<Point2f> curve

待计算外接矩形的坐标点集:必须为CV_32SC2或 CV_32FC2

MinAreaRect查找包含轮廓的最小外接矩形

//函数原型1
RotatedRect MinAreaRect(InputArray points)

//函数原型2
RotatedRect MinAreaRect(IEnumerable<Point> points)

//函数原型3
RotatedRect MinAreaRect(IEnumerable<Point2f> points)

MinAreaRect查找包含轮廓的最小面积旋转矩形

参数

说明

InputArray points

IEnumerable<Point> points

IEnumerable<Point2f> points

待检测的轮廓(坐标点集)

返回值

包含了轮廓(坐标点集)的最小旋转矩形

RotatedRect旋转矩形结构体

属性、方法

说明

Point2f Center

矩形质心

Size2f Size

矩形的宽、高

float Angle

矩形旋转角度:旋转角度θ是水平轴(x轴)顺时针旋转,直到碰到矩形的第一条边停住,此时该边与水平轴的夹角。并且这个边的边长是width,另一条边边长是height。也就是说,在这里,width与height不是按照长短来定义的。

Point2f[] Points()

旋转矩形的四个顶点

Rect BoundingRect()

包含旋转矩形的最小右上矩形

BoxPoints查找旋转矩形的四个顶点

//函数原型1
void BoxPoints(RotatedRect box,
    OutputArray points)

//函数原型2
Point2f[] BoxPoints(RotatedRect box)

BoxPoints查找旋转矩形的四个顶点(用RotatedRect对象的Points属性即可)

参数

说明

RotatedRect box

输入的旋转矩形

OutputArray points

返回值

旋转矩形的四个顶点坐标数组

原图顺时针旋转20度

源码示例

public void Run(ParamBase paramBase)
{

    #region 测试ContourArea的参数oriented=true时,顺、逆时针方向的正负值
    Point[][] testContourAreaOriented = new Point[][] { new Point[] { new Point(50,50),new Point(150,50),new Point(150,150),new Point(50,150)},
                                                        new Point[] { new Point(50,200), new Point(50, 300),new Point(150,300),new Point(150, 200) },
                                                        new Point[] { new Point(50,350), new Point(150, 450),new Point(150,350),new Point(50,450)}};

    using (Mat canvas = Mat.Zeros(new Size(400, 482), MatType.CV_8UC3))
    {
        foreach (var Points in testContourAreaOriented)
        {
            var color = Scalar.RandomColor();
            for (int index = 1; index <= Points.Length; index++)
            {
                canvas.Line(Points[index - 1], Points[index % 4], color);
                //在起点标上数字,方便判断顺时针还是逆时针
                canvas.PutText($"{index - 1}", Points[index - 1], HersheyFonts.HersheySimplex, 1, color, 2);
                ImShowAndWait("Verify ContourArea return value", canvas);
            }
            //计算轮廓面积
            var area = Cv2.ContourArea(Points, true);
            //顺时针,还是逆时針
            var direction = Points[1].X > Points[0].X ? "clockwise" : "counter-clockwise";
            canvas.PutText($"{direction},area={area}", Points[0], HersheyFonts.HersheySimplex, 0.5, color, 1);

            //计算轮廓弧长
            var arc = Cv2.ArcLength(Points, true);
            canvas.PutText($"arc={arc}", GetOffsetPoint(Points[0]), HersheyFonts.HersheySimplex, 0.5, color, 1);

            ImShowAndWait("Verify ContourArea return value", canvas);
        }//顺时针为正,逆时针为负
    }
    #endregion


    #region ContourArea
    using (var srcGray = Cv2.ImRead(ImagePath.Contours, ImreadModes.Grayscale))
    {
        if (srcGray.Empty()) throw new Exception("图像读取有误");
        Cv2.ImShow("srcGray", srcGray);

        //用大律法二值化
        Cv2.Threshold(srcGray, srcGray, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.BinaryInv);

        //检索轮廓
        Cv2.FindContours(srcGray, out Point[][] contours, out _, RetrievalModes.CComp, ContourApproximationModes.ApproxSimple);

        using (var srcColor = srcGray.CvtColor(ColorConversionCodes.GRAY2BGR))
        {
            foreach (Point[] Points in contours)
            {
                //外轮廓为负,内轮廓为正
                var area = Cv2.ContourArea(Points, true);
                srcColor.PutText($"area={area}", Points[0], HersheyFonts.HersheySimplex, 0.5, Scalar.Red);

                var arc = Cv2.ArcLength(Points, true);
                srcColor.PutText($"arc={arc}", GetOffsetPoint(Points[0]), HersheyFonts.HersheySimplex, 0.5, Scalar.Red, 1);
            }
            ImShowAndWait("ContourArea", srcColor);
        }

    }
    #endregion

    using (var srcGray = Cv2.ImRead(ImagePath.Contours, ImreadModes.Grayscale))
    {
        if (srcGray.Empty()) throw new Exception("图像读取有误");
        Cv2.ImShow("srcGray", srcGray);
        //用大律法二值化
        Cv2.Threshold(srcGray, srcGray, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.BinaryInv);

        //正逆时针,负顺时针
        var angle = -20;
        var rotMat = Cv2.GetRotationMatrix2D(new Point2f(srcGray.Width / 2f, srcGray.Height / 2f), angle, 1);

        var sqrt2 = Math.Sqrt(2);
        //中点偏移
        var offsetMat = new Mat<double>(2, 3, new double[] { 0,0, (srcGray.Width /2d),
                                                             0,0, (srcGray.Height /2d)});
        //顺时针旋转,加中心偏移
        Cv2.WarpAffine(srcGray, srcGray, rotMat + offsetMat, new Size(srcGray.Width * 2, srcGray.Height * 2));


        //检索轮廓
        Cv2.FindContours(srcGray, out Point[][] contours, out _, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
        using (var srcColor = srcGray.CvtColor(ColorConversionCodes.GRAY2BGR))
        {
            foreach (Point[] Points in contours)
            {

                //最小外接面积旋转旋转矩形
                var minRect = Cv2.MinAreaRect(Points);

                //查找最小面积旋转矩形的四个顶点
                var boxPointfs = minRect.Points();
                //var boxPointfs2 = Cv2.BoxPoints(minRect);与minRect.Points()等价

                var boxPoints = Array.ConvertAll<Point2f, Point>(boxPointfs, z => new Point(z.X, z.Y));

                //绘制最小外接旋转矩形
                Cv2.DrawContours(srcColor, new Point[][] { boxPoints }, -1, Scalar.OrangeRed);
                
                Cv2.PutText(srcColor,
                            $"MinAreaRect,Width={minRect.Size.Width.ToString("0")},Height={minRect.Size.Height.ToString("0")},Angle={minRect.Angle.ToString("0.0")}",
                            boxPointfs[0].ToPoint(),
                            HersheyFonts.HersheySimplex,
                            0.5,
                            Scalar.OrangeRed);

                //最小外接矩形质点
                Cv2.Circle(srcColor, (int)minRect.Center.X, (int)minRect.Center.Y, 2, Scalar.OrangeRed, -1);

                //包含最小外接旋转矩形的右上矩形
                var minBoundingRect = minRect.BoundingRect();
                Cv2.Rectangle(srcColor, minBoundingRect, Scalar.LightYellow);

                srcColor.PutText($"minRect.BoundingRect,Size=({(int)(minBoundingRect.Size.Width)},{(int)(minBoundingRect.Size.Height)})", minBoundingRect.Location, HersheyFonts.HersheySimplex, 0.5, Scalar.LightYellow);

                //外接矩形
                var rect = Cv2.BoundingRect(Points);
                Cv2.Rectangle(srcColor, rect, Scalar.Green);
                Cv2.PutText(srcColor, $"BoundingRect,Width={rect.Width},Height={rect.Height}", rect.Location, HersheyFonts.HersheySimplex, 0.5, Scalar.Green);
            }
            ImShowAndWait("Rect", srcColor);
        }
    }

    Cv2.DestroyAllWindows();
}

/// <summary>
/// 返回一个偏移坐标
/// </summary>
/// <param name="point"></param>
/// <returns></returns>
private Point GetOffsetPoint(Point point)
{
    return new Point(point.X, point.Y - 15);
}

/// <summary>
/// 显示窗口并按任意键继续
/// </summary>
/// <param name="winName"></param>
/// <param name="canvas"></param>
private void ImShowAndWait(string winName, Mat canvas)
{
    Cv2.ImShow(winName, canvas);
    Cv2.WaitKey();
}

OpenCvSharp函数示例(目录)

参考

https://edu.csdn.net/learn/38286/608280

https://docs.opencv.org/

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值