OpenCvSharp函数:FindContours轮廓发现、DrawContours绘制轮廓

文章详细介绍了OpenCV中的FindContours函数,用于在二值图像中寻找轮廓。函数支持不同的检索模式(如External,List,CComp,Tree,FloodFill)和轮廓坐标近似方法。通过实例展示了不同参数组合下的轮廓检测结果,并提供了DrawContours函数用于绘制轮廓。此外,代码示例演示了如何使用这些函数处理图像并显示轮廓。
摘要由CSDN通过智能技术生成

FindContours轮廓发现

//函数原型1
void FindContours(InputArray image,
    out Mat[] contours,
    OutputArray hierarchy,
    RetrievalModes mode,
    ContourApproximationModes method,
    Point? offset = null)

//函数原型2
void FindContours(InputArray image,
    out Point[][] contours,
    out HierarchyIndex[] hierarchy,
    RetrievalModes mode,
    ContourApproximationModes method,
    Point? offset = null)

FindContours轮廓发现,在二值图中查找轮廓

参数

说明

InputArray image

输入图像:8位单通道,非0像素当1处理

可处理Compare,InRange,Threshold,AdaptiveThreshold,Canny等生成的二值图。如果mode为CComp或FloodFill,也可以为CV_32SC1

out Mat[] contours

检测到的轮廓数组:返回N个轮廓mat(各轮廓坐标个数*1*CV_32SC2)

out Point[][] contours

检测到的轮廓坐标数组:返回N个轮廓坐标数组(与上面的表示形式不同而已)

OutputArray hierarchy

轮廓的层次关系:返回1*N*CV_32SC4的mat,对应每个轮廓的,

hierarchy[i][0],hierarchy[i][1],hierarchy[i][2],hierarchy[i][3]分别对应该轮廓的Next同层下一个轮廓索引,Previous同层上一个轮廓索引,Child下层第一个孩子轮廓索引,Parent父层轮廓索引,负数表示没有相应元素。

out HierarchyIndex[] hierarchy

轮廓的层次关系:HierarchyIndex有Next,Previous,Child,Parent属性可直接访问

RetrievalModes mode

轮廓检索模式:影响轮廓个数、层次关系

ContourApproximationModes method

轮廓坐标近似模式:影响各轮廓坐标个数

Point? offset

轮廓整体偏移量:如在ROI区域进行轮廓识别后,加上ROI左上角的偏移量后,最终结果即为整图对应的轮廓。

RetrievalModes mode轮廓检索模式

External

仅检索最外层轮廓,hierarchy[i][2]=hierarchy[i][3]=-1

List

检索所有轮廓,但不包含层级关系,hierarchy[i][2]=hierarchy[i][3]=-1

CComp

检索所有轮廓,包含两层层级关系。顶层为最外轮廓,第二层为孔洞轮廓。如果有轮廓在孔洞内,它也设为顶层(Parent=-1)

Tree

检索所有轮廓,包含完整的层次关系。与CComp不同的是,孔内的轮廓的Parent为对应的孔

FloodFill

检索所有轮廓,使用FloodFill算法,其它与CComp相同,输入图像需为CV_32SC1类型

ContourApproximationModes method 轮廓坐标近似模式

参数

说明

ApproxNone

记录轮廓上的所有点

ApproxSimple

压缩记录,只记录垂直、水平、对角线段的结束点

ApproxTC89L1

应用Teh-Chin链近似算法中的一种

ApproxTC89KCOS

应用Teh-Chin链近似算法中的一种

原灰度图

各参数图例对比

下面图例对比RetrievalModes和ContourApproximationModes不同时的不同结果

窗口标题是对应的RetrievalModes、ContourApproximationModes取值及总轮廓数

轮廓左上角的文字格式说明

第一个数字:表示当前轮廓序号

Next:下一个轮廓序号

Pre:Previous前一个轮廓序号

Child:子轮廓序号

Pa:Parent:父轮廓序号

Count:该轮廓包含的坐标个数

外轮廓为坐标为逆针,内轮廓坐标为顺时针

RetrievalModes为External时

只有外轮廓,没有层次关系(Child,Parent为-1)

RetrievalModes为List时

内、外轮廓都有,无层次关系

RetrievalModes为CComp时

内、外轮廓都有,也有层次关系,注意孔内轮廓3的Parent为轮廓2,轮廓2的Parent为-1(即也认为是顶层)

RetrievalModes为Tree时

内、外轮廓都有,完整的树形层次关系。注意轮廓9(对应上图的轮廓3)的Parent为轮廓8(对应上图的轮廓2),轮廓8的Parent为轮廓7。

RetrievalModes为FloodFill时

加了offset=(5,15),

DrawContours绘制轮廓

//函数原型1
void DrawContours(InputOutputArray image,
    IEnumerable<IEnumerable<Point>> contours,
    int contourIdx,
    Scalar color,
    int thickness = 1,
    LineTypes lineType = LineTypes.Link8,
    IEnumerable<HierarchyIndex>? hierarchy = null,
    int maxLevel = int.MaxValue,
    Point? offset = null)

//函数原型2
void DrawContours(InputOutputArray image,
    IEnumerable<Mat> contours,
    int contourIdx,
    Scalar color,
    int thickness = 1,
    LineTypes lineType = LineTypes.Link8,
    Mat? hierarchy = null,
    int maxLevel = int.MaxValue,
    Point? offset = null)

DrawContours绘制轮廓

InputOutputArray image

待绘制轮廓的图像

IEnumerable<IEnumerable<Point>> contours

IEnumerable<Mat> contours

各轮廓坐标数据

int contourIdx

待绘制轮廓的序号,负数表示绘制所有轮廓

Scalar color

待绘制轮廓线的颜色

int thickness = 1

待绘制轮廓线的宽度,负数表示填充轮廓内部

LineTypes lineType = LineTypes.Link8

轮廓线类型

IEnumerable<HierarchyIndex>? hierarchy = null

Mat? hierarchy = null

轮廓层次关系,绘制指定轮廓时可能要用到,影响maxLevel

int maxLevel = int.MaxValue

最大绘制轮廓级数:为0时,只绘制指定序号的轮廓;为1时指定序号及子轮廓;为2时指定序号及子轮廓和子轮廓的子轮廓。依次类推。

Point? offset = null

轮廓偏移量:轮廓坐标+偏移量为实际绘制坐标

contourIdx和maxLevel参数图例

测试图像

contourIdx和maxLevel分别为0,1,3,6时的示例(轮廓检索方式为Tree)

源码示例

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

    //需要去除噪声的话,可应用相关的模糊函数
    //Cv2.GaussianBlur(srcGray, srcGray, new Size(3, 3), 0);
    //用大律法二值化
    Cv2.Threshold(srcGray, srcGray, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.BinaryInv);

    //轮廓偏移量
    var offset = new Point(5, 15);

    //测试各种RetrievalModes
    foreach (RetrievalModes mode in Enum.GetValues(typeof(RetrievalModes)))
    {
        //记录窗口时,为了自动排列窗口用
        List<string> winNames = new List<string>();
        //测试各种ContourApproximationModes
        foreach (ContourApproximationModes caMode in Enum.GetValues(typeof(ContourApproximationModes)))
        {
            try
            {
                Point[][] contoursP;
                HierarchyIndex[] hierarchyI;
                if (mode== RetrievalModes.FloodFill)
                {
                    using var floodFileMat = new Mat();
                    //用FloodFill方式时,需转为CV_32SC1
                    srcGray.ConvertTo(floodFileMat, MatType.CV_32SC1);
                    Cv2.FindContours(floodFileMat, out contoursP, out hierarchyI, mode, caMode, offset);
                }
                else
                {
                    //检索轮廓
                    Cv2.FindContours(srcGray, out contoursP, out hierarchyI, mode, caMode);              
                }                        
                //另一种调用方式
                //FindContours(InputArray image, out Mat[] contours, OutputArray hierarchy, RetrievalModes mode,
                //      ContourApproximationModes method, Point? offset = null)

                using var result = srcGray.CvtColor(ColorConversionCodes.GRAY2BGR);
                //绘制所有轮廓
                Cv2.DrawContours(result, contoursP, -1, Scalar.Red, 2);
                /*
                int index = 0;
                foreach (Point[] points in contoursP)
                {
                    var h = hierarchyI[index];
                    //在轮廓左上角加白底黑字提示文字
                    var textPoint = new Point(points[0].X - 30, points[0].Y - 12);
                    Helper.PutTextWithBack(result, $"{index},Next:{h.Next},Pre:{h.Previous},Child:{h.Child},Pa:{h.Parent},Count:{points.Count()}",
                        textPoint, fontScale: 0.3);
                    //文字与轮廓连一条线
                    Cv2.Line(result, points[0], textPoint, Scalar.Green, 1);
                    index++;
                }*/
                var winName = $"RetrievalModes:{mode},ContourApproximationModes:{caMode},contoursCount:{contoursP.Count()}";
                if (mode == RetrievalModes.FloodFill)
                {//测试参数offset
                    result.PutText($"offset=({offset.X},{offset.Y})", offset, HersheyFonts.HersheySimplex, 0.3, Scalar.Red);
                }
                Cv2.ImShow(winName, result);
                winNames.Add(winName);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }                    
        }
        //窗口对齐
        //Helper.ArrangeWins(winNames, 2);
        Cv2.WaitKey();
        Cv2.DestroyAllWindows();
    }

    //用于测试多层次轮廓图像
    using var nContours = Cv2.ImRead(ImagePath.mContours, ImreadModes.Grayscale);
    Cv2.Threshold(nContours, nContours, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.BinaryInv);
    //检索轮廓
    Cv2.FindContours(nContours, out Point[][] contoursPs, out HierarchyIndex[] hierarchyIs, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple);

    for (int contourIdx = 0; contourIdx < contoursPs.Length; contourIdx++)
    {
        using var nContoursMat = nContours.CvtColor(ColorConversionCodes.GRAY2BGR);
        //绘制轮廓,测试contourIdx和maxLevel
        Cv2.DrawContours(nContoursMat, contoursPs, contourIdx, Scalar.Red, 2, hierarchy: hierarchyIs, maxLevel: contourIdx);
        //var textPoint = contoursPs[contourIdx][0];
        //var h = hierarchyIs[contourIdx];
        //Helper.PutTextWithBack(nContoursMat, $"{contourIdx},Next:{h.Next},Pre:{h.Previous},Child:{h.Child},Pa:{h.Parent}",
        //                textPoint, fontScale: 0.3);
        Cv2.ImShow($"contourIdx={contourIdx},maxLevel={contourIdx}", nContoursMat);
    }
    Cv2.WaitKey();
    Cv2.DestroyAllWindows();
}

OpenCvSharp函数示例(目录)

参考

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

https://docs.opencv.org/4.7.0/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图南科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值