图像的轮廓不仅能够提供物体的边缘,而且还能提供物体边缘之间的层次关系以及拓扑关系。我们可以将图像轮廓发现简单理解为带有结构关系的边缘检测,这种结构关系可以表明图像中连通域或者某些区域之间的关系。图7-14为一个具有4个不连通边缘的二值化图像,由外到内依次为0号、1号、2号、3号条边缘。为了描述不同轮廓之间的结构关系,定义由外到内的轮廓级别越来越低,也就是高一层级的轮廓包围着较低层级的轮廓,被同一个轮廓包围的多个不互相包含的轮廓是同一层级轮廓。例如在图7-14中,0号轮廓层级比1号和第2号轮廓的层及都要高,2号轮廓包围着3号轮廓,因此2号轮廓的层级要高于3号轮廓。 为了更够更好的表明各个轮廓之间的层级关系,常用4个参数来描述不同层级之间的结构关系,这4个参数分别是:同层下一个轮廓索引、同层上一个轮廓索引、下一层第一个子轮廓索引和上层父轮廓索引。根据这种描述方式,图7-14中0号轮廓没有同级轮廓和父轮廓需要用-1表示,其第一个子轮廓为1号轮廓,因此可以用描述该轮廓的结构。1号轮廓的下一个同级轮廓为2号轮廓但是没有上一个同级轮廓用-1表示,父轮廓为0号轮廓,第一个子轮廓为3号轮廓,因此可以用描述该轮廓结构。2号轮廓和3号轮廓同样可以用这样的方式构建结构关系描述子。图7-14中不同轮廓之间的层级关系可以用图7-15表示。 OpenCV 4提供了可以在二值图像中检测图像中所有轮廓并生成不同轮廓结构关系描述子的findContours()函数,该函数的函数原型在代码清单7-11中给出。 代码清单7-11 findContours()函数原型1 1.void cv::findContours(InputArray image, 2. OutputArrayOfArrays contours, 3. OutputArray hierarchy, 4. int mode, 5. int method, 6. Point offset = Point() 7. )
该函数主要用于检测图像中的轮廓信息,并输出各个轮廓之间的结构信息。函数的第一个参数是待检测轮廓的输入图像,从理论上讲检测图像轮廓需要是二值化图像,但是该函数会对非0像素视为1,0像素保持不变,因此该参数能够接受非二值化的灰度图像。由于该函数默认二值化操作不能保持图像主要的内容,因此常需要对图像进行预处理,利用threshold()函数或者adaptiveThreshold()函数根据需求进行二值化。第二个参数用于存放检测到的轮廓,数据类型为vector<vector
有时我们只需要检测图像的轮廓,并不关心轮廓之间的结构关系信息,此时轮廓之间的结构关系变量会造成内存资源的浪费,因此OpenCV 4提供了findContours()函数的另一种函数原型,可以不输出轮廓之间的结构关系信息,该种函数原型在代码清单7-12中给出。 代码清单7-12 findContours()函数原型2 1.void cv::findContours(InputArray image, 2. OutputArrayOfArrays contours, 3. int mode, 4. int method, 5. Point offset = Point() 6. )
提取了图像轮廓后,为了能够直观的查看轮廓检测的结果,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() )
该函数用于绘制findContours()函数检测到的图像轮廓。函数的第一个参数为绘制轮廓的图像,根据需求该参数可以是单通道的灰度图像或者三通道的彩色图像。第二个参数是所有将要绘制的轮廓,数据类型为vector<vector
为了了解图像轮廓检测和绘制相关函数的使用,在代码清单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(9, 9), 2, 2); //平滑滤波 threshold(gray, binary, 170, 255, 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(0, 0, 255), 2, 8); } //输出轮廓结构描述子 for (int i = 0; i < hierarchy.size(); i++) { cout << hierarchy[i] << endl; } //显示结果 imshow("轮廓检测结果", img); waitKey(0); return 0; }
|
|