首页 > 编程笔记 > C++笔记 阅读:4

C++ cv::findContours()函数的用法(附带实例)

在 OpenCV 中通过使用 findContours() 函数,只需几个简单步骤就可以检测出物体的轮廓,十分方便。

findContours() 函数声明如下:
void cv::findContours (InputArray  image, OutputArrayOfArrays  contours, OutputArray  hierarchy, int  mode, int  method, Point  offset = Point() );
参数说明如下:
另外,绘制轮廓函数 drawContours 经常和查找轮廓函数 findContours 联合使用。这个好理解,查找出轮廓后,通常需要把轮廓绘制出来。函数 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 函数中各参数取不同值时,向量 contours 和 hierarchy 的内容如何变化,以及有何异同。

【实例】对比 findContours 函数的不同效果。
1) 新建一个控制台工程,工程名是 test。

2) 打开 main.cpp,输入如下代码:
#include <opencv2/opencv.hpp>
using namespace cv;
#include<iostream>
using namespace std;

#include <opencv2/highgui/highgui_c.h>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
#include <stdio.h>

int main(int argc, char *argv[])
{
    int op;
    printf("enter 1--4:");
    scanf_s("%d", &op);
    Mat imageSource = imread("test.jpg", 0);
    imshow("Source Image", imageSource);
    Mat image;
    GaussianBlur(imageSource, image, Size(3, 3), 0);
    Canny(image, image, 100, 250);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    if(op==1)
        findContours(image, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
    else if(op==2)
        findContours(image, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point());
    else if(op==3)
        findContours(image, contours, hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE, Point());
    else if(op==4)
        findContours(image, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, Point());

    Mat imageContours = Mat::zeros(image.size(), CV_8UC1); // 绘制
    Mat Contours = Mat::zeros(image.size(), CV_8UC1); // 绘制
    for (int i = 0; i < contours.size(); i++) {
        // contours[i]代表的是第 i 个轮廓, contours[i].size()代表的是第 i 个轮廓上所有的像素点数
        for (int j = 0; j < contours[i].size(); j++) {
            // 绘制出 contours 向量内所有的像素点
            Point P = Point(contours[i][j].x, contours[i][j].y);
            Contours.at<uchar>(P) = 255;
        }
        // 输出 hierarchy 向量内容
        char sz[256];
        sprintf(sz, "The %d-th element of Vector hierarchy:", i);
        string str = sz;
        cout << str << endl << hierarchy[i] << endl << endl;
    }
    // 绘制轮廓
    drawContours(imageContours, contours, i, Scalar(255), 1, 8, hierarchy);
    imshow("Contours Image", imageContours); // 轮廓
    imshow("Point of Contours", Contours); // 向量 contours 内保存的所有轮廓点集
    waitKey(0);
    return 0;
}
在上述代码中,我们分别设置了 4 种不同参数的 findContours 调用,根据用户输入的 1~4 来决定执行哪个 findContours。findContours 调用后轮廓就查找出来了。

我们先绘制出 contours 向量内所有的像素点,然后调用 drawContours 绘制出轮廓。通过调整第 4 个参数 mode(轮廓的检索模式)、第 5 个参数 method(轮廓的近似方式)以及不同的偏移量 Point(),就可以得到不同的效果。

3) 保存工程并运行,结果说明如下:
① 当输入 2 时,mode 取值 RETR_EXTERNAL,method 取值 CHAIN_APPROX_NONE,即只检测最外层轮廓,内层的轮廓被忽略,并且保存轮廓上的所有点。轮廓如下图所示。


contours 向量内所有点集如下图所示。Contours向量中保存了所有轮廓上的所有点,图像表现得跟轮廓一致。


hierarchy 向量如下图所示:


重温一下 hierarchy 向量,向量中每个元素的 4 个整型变量(hierarchy[i][0]、hiearchy[i][1]、hiearchy[i][2] 和 hiearchy[i][3])分别表示与当前轮廓平级的后一个轮廓的索引编号、与当前轮廓平级的前一个轮廓的索引编号、当前轮廓的子轮廓的索引编号和当前轮廓的父轮廓的索引编号。如果没有对应项,则 hierarchy[i][x] = -1。该参数配置下,hierarchy 向量内有 3 个元素,分别对应 3 个轮廓。以第 2 个轮廓(对应向量内第 1 个元素)为例,内容为 [2,0,-1,-1],2 表示当前轮廓的后一个轮廓的编号为 2,0 表示当前轮廓的前一个轮廓编号为 0,其后两个 -1 表示为空,因为只有最外层轮廓这一个等级,所以不存在父轮廓和内嵌轮廓。

② 当输入 3 时,mode 取值 RETR_LIST,method 取值 CHAIN_APPROX_SIMPLE,即检测所有轮廓,但各轮廓之间彼此独立,不建立等级关系,并且仅保存轮廓上的拐点信息。轮廓如下图所示。


contours 向量内所有点集如下图所示。contours 向量中所有的拐点信息得到了保留,但是拐点与拐点之间直线段的部分被省略了。


hierarchy 向量(截取一部分)如下图所示:


该参数配置下,检测出了较多轮廓。第 1 个、第 2 个整型值分别指向上一个和下一个轮廓编号。由于本次配置 mode 取值 RETR_LIST,各轮廓间各自独立,不建立等级关系,因此第 3 个、第 4 个整型参数为空,值设为 -1。

③ 当输入 4 时,mode 取值 RETR_TREE,method 取值 CHAIN_APPROX_NONE,即检测所有轮廓,轮廓间建立外层、内层的等级关系,并且保存轮廓上的所有点。轮廓如下图所示。


contours 向量内所有点集如下图所示。所有内外层轮廓都被检测到,contours 点集组成的图形跟轮廓表现一致。


hierarchy 向量(截取一部分)如下图所示:


该参数配置要求检测所有轮廓,每个轮廓都被划分等级,包括最外围、第一内围、第二内围等,所以除第 1 和个最后一个轮廓外,其他轮廓都具有不为 -1 的第 3 个、第 4 个整型参数,分别指向当前轮廓的父轮廓、内嵌轮廓的索引编号。

相关文章