分享

【从零学习OpenCV 4】轮廓发现与绘制

 小白学视觉 2021-01-28
重磅干货,第一时间送达

经过几个月的努力,小白终于完成了市面上第一本OpenCV 4入门书籍《OpenCV 4开发详解》。为了更让小伙伴更早的了解最新版的OpenCV 4,小白与出版社沟通,提前在公众号上连载部分内容,请持续关注小白。

图像的轮廓不仅能够提供物体的边缘,而且还能提供物体边缘之间的层次关系以及拓扑关系。我们可以将图像轮廓发现简单理解为带有结构关系的边缘检测,这种结构关系可以表明图像中连通域或者某些区域之间的关系。图7-14为一个具有4个不连通边缘的二值化图像,由外到内依次为0号、1号、2号、3号条边缘。为了描述不同轮廓之间的结构关系,定义由外到内的轮廓级别越来越低,也就是高一层级的轮廓包围着较低层级的轮廓,被同一个轮廓包围的多个不互相包含的轮廓是同一层级轮廓。例如在图7-14中,0号轮廓层级比1号和第2号轮廓的层及都要高,2号轮廓包围着3号轮廓,因此2号轮廓的层级要高于3号轮廓。

图7-14 图像轮廓序号

为了更够更好的表明各个轮廓之间的层级关系,常用4个参数来描述不同层级之间的结构关系,这4个参数分别是:同层下一个轮廓索引、同层上一个轮廓索引、下一层第一个子轮廓索引和上层父轮廓索引。根据这种描述方式,图7-14中0号轮廓没有同级轮廓和父轮廓需要用-1表示,其第一个子轮廓为1号轮廓,因此可以用描述该轮廓的结构。1号轮廓的下一个同级轮廓为2号轮廓但是没有上一个同级轮廓用-1表示,父轮廓为0号轮廓,第一个子轮廓为3号轮廓,因此可以用描述该轮廓结构。2号轮廓和3号轮廓同样可以用这样的方式构建结构关系描述子。图7-14中不同轮廓之间的层级关系可以用图7-15表示。

图7-15 图7-14中不同轮廓之间的结构关系

OpenCV 4提供了可以在二值图像中检测图像中所有轮廓并生成不同轮廓结构关系描述子的findContours()函数,该函数的函数原型在代码清单7-11中给出。

代码清单7-11 findContours()函数原型11.void cv::findContours(InputArray image,2. OutputArrayOfArrays contours,3. OutputArray hierarchy,4. int mode,5. int method,6. Point offset = Point() 7. )
  • image:输入图像,数据类型为CV_8U的单通道灰度图像或者二值化图像。
  • contours:检测到的轮廓,每个轮廓中存放着像素的坐标。
  • hierarchy:轮廓结构关系描述向量。
  • mode:轮廓检测模式标志,可以选择的参数在表7-2给出。
  • method:轮廓逼近方法标志,可以选择的参数在表7-3给出。
  • offset:每个轮廓点移动的可选偏移量。这个参数主要用在从ROI图像中找出轮廓并基于整个图像分析轮廓的场景中。

该函数主要用于检测图像中的轮廓信息,并输出各个轮廓之间的结构信息。函数的第一个参数是待检测轮廓的输入图像,从理论上讲检测图像轮廓需要是二值化图像,但是该函数会对非0像素视为1,0像素保持不变,因此该参数能够接受非二值化的灰度图像。由于该函数默认二值化操作不能保持图像主要的内容,因此常需要对图像进行预处理,利用threshold()函数或者adaptiveThreshold()函数根据需求进行二值化。第二个参数用于存放检测到的轮廓,数据类型为vector<vector>,每个轮廓中存放着属于该轮廓的像素坐标。函数的第三个参数用于存放各个轮廓之间的结构信息,数据类型为vector,数据的尺寸与检测到的轮廓数目相同,每个轮廓结构信息中第1个数据表示同层下一个轮廓索引、第2个数据表示同层上一个轮廓索引、第3个数据表示下一层第一个子轮廓索引、第4个数据表示上层父轮廓索引。函数第四个参数是轮廓检测模式的标志,可以选择的参数及含义在表7-2给出。函数第五个参数是选择轮廓逼近方法的标志,可以选择的参数及含义在表7-3给出。函数最后一个参数是每个轮廓点移动的可选偏移量。这个函数主要用在从ROI图像中找出轮廓并基于整个图像分析轮廓的场景中。

表7-2 findContours()函数轮廓检测模式标志可选择参数
标志参数简记含义
RETR_EXTERNAL0只检测最外层轮廓,对所有轮廓设置hierarchy[i][2]= hierarchy[i][2]=-1
RETR_LIST1提取所有轮廓,并且放置在list中。检测的轮廓不建立等级关系。
RETR_CCOMP2提取所有轮廓,并且将其组织为双层结构。顶层为连通域的外围边界,次层为孔的内层边界。
RETR_TREE3提取所有轮廓,并重新建立网状的轮廓结构。
表7-3 findContours()函数轮廓逼近方法标志可选择参数
标志参数简记含义
CHAIN_APPROX_NONE1获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1。
CHAIN_APPROX_SIMPLE2压缩水平方向、垂直方向和对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保持轮廓信息。
CHAIN_APPROX_TC89_L13使用The-Chinl链逼近算法中的一个。
CHAIN_APPROX_TC89_KCOS4使用The-Chinl链逼近算法中的一个。

有时我们只需要检测图像的轮廓,并不关心轮廓之间的结构关系信息,此时轮廓之间的结构关系变量会造成内存资源的浪费,因此OpenCV 4提供了findContours()函数的另一种函数原型,可以不输出轮廓之间的结构关系信息,该种函数原型在代码清单7-12中给出。

代码清单7-12 findContours()函数原型21.void cv::findContours(InputArray image,2. OutputArrayOfArrays contours,3. int mode,4. int method,5. Point offset = Point() 6. )
  • image:输入图像,数据类型为CV_8U的单通道灰度图像或者二值化图像。
  • contours:检测到的轮廓,每个轮廓中存放着像素的坐标。
  • mode:轮廓检测模式标志,可以选择的参数在表7-2给出。
  • method:轮廓逼近方法标志,可以选择的参数在表7-3给出。
  • offset:每个轮廓点移动的可选偏移量。这个函数主要用在从ROI图像中找出轮廓并基于整个图像分析轮廓的场景中。

提取了图像轮廓后,为了能够直观的查看轮廓检测的结果,OpenCV 4提供了显示轮廓的drawContours()函数,该函数的函数原型在代码清单7-13中给出。

代码清单7-13 drawContours()函数原型  void cv::drawContours(InputOutputArray  image,                            InputArrayOfArrays  contours,                            int   contourIdx,                            const Scalar &  color,                            int  thickness = 1,                            int  lineType = LINE_8,                            InputArray  hierarchy = noArray(),                            int  maxLevel = INT_MAX,                            Point  offset = Point()                             )
  • image:绘制轮廓的目标图像。
  • contours:所有将要绘制的轮廓
  • contourIdx:要绘制的轮廓的数目,如果是负数,则绘制所有的轮廓。
  • color:绘制轮廓的颜色。
  • thickness:绘制轮廓的线条粗细,如果参数为负数,则绘制轮廓的内部,默认参数值为1.
  • lineType:边界线连接的类型,可以选择参数在表7-4给出,默认参数值为LINE_8。
  • hierarchy:可选的结构关系信息,默认值为noArray()。
  • maxLevel:表示用于绘制轮廓的最大等级,默认值为INT_MAX。
  • offset:可选的轮廓偏移参数,按指定的移动距离绘制所有的轮廓。

该函数用于绘制findContours()函数检测到的图像轮廓。函数的第一个参数为绘制轮廓的图像,根据需求该参数可以是单通道的灰度图像或者三通道的彩色图像。第二个参数是所有将要绘制的轮廓,数据类型为vector<vector>。第三个参数是要绘制的轮廓数目,该参数的数值与第二个参数相对应,应小于所有轮廓的数目,如果该参数值为负数,则绘制所有的轮廓。第四个参数是绘制轮廓的颜色,对于单通道的灰度图像用Scalar(x)赋值,对于三通道的彩色图像用Scalar(x,y,z)赋值。第五个参数是边界线的连接类型,可以选择的参数在表7-4给出,默认参数值为LINE_8。第六个参数是可选的结构关系信息,默认值为noArray()。第七个参数表示绘制轮廓的最大等级,参数值如果为0,则仅绘制指定的轮廓;如果为1,则该函数绘制轮廓和所有嵌套轮廓;如果为2,则该函数绘制轮廓以及所有嵌套轮廓和所有嵌套到嵌套轮廓的轮廓,以此类推,默认值为INT_MAX。函数最后一个参数是可选的轮廓偏移参数,按指定的移动距离绘制所有的轮廓。

表7-4 drawContours()函数中可选线型
标志参数简记含义
LINE_414连通线型
LINE_838连通线型
LINE_AA4抗锯齿线型

为了了解图像轮廓检测和绘制相关函数的使用,在代码清单7-14中给出了检测图像中的轮廓和绘制轮廓的示例程序。程序中不仅绘制了物体的轮廓,还输出了图像所有轮廓的结构关系信息。程序绘制的轮廓信息在图7-16给出,所有轮廓结构关系信息在图7-17给出,同时根据结果绘制了直观的结构关系。

代码清单7-14 myContours.cpp轮廓检测与绘制  #include <opencv2\opencv.hpp>  #include <iostream>  #include <vector>    using namespace cv;  using namespace std;    int main()  {    system("color F0");  //更改输出界面颜色    Mat img = imread("coins.jpg");    if (img.empty())    {      cout << "请确认图像文件名称是否正确" << endl;      return -1;    }    imshow("原图", img);    Mat gray, binary;    cvtColor(img, gray, COLOR_BGR2GRAY);  //转化成灰度图    GaussianBlur(gray, gray, Size(99), 22);  //平滑滤波    threshold(gray, binary, 170255, THRESH_BINARY | THRESH_OTSU);  //自适应二值化      // 轮廓发现与绘制    vector<vector<Point>> contours;  //轮廓    vector<Vec4i> hierarchy;  //存放轮廓结构变量    findContours(binary, contours, hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE, Point());    //绘制轮廓    for (int t = 0; t < contours.size(); t++)    {      drawContours(img, contours, t, Scalar(00255), 28);    }    //输出轮廓结构描述子    for (int i = 0; i < hierarchy.size(); i++)    {      cout << hierarchy[i] << endl;    }      //显示结果    imshow("轮廓检测结果", img);    waitKey(0);    return 0;  }
图7-16 myContours.cpp程序轮廓检测结果
图7-17 myContours.cpp程序检测轮廓层次结构
经过几个月的努力,市面上第一本OpenCV 4入门书籍《OpenCV 4开发详解》将春节后由人民邮电出版社发行。如果小伙伴觉得内容有帮助,希望到时候多多支持!

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多