findContours函数分析

findContours函数 —— 在二值图像(binary image)中寻找轮廓(contour)

函数声明
cv::findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())

cv::findContours(InputOutputArray image, OutputArrayOfArrays contours, int mode, int method, Point offset=Point())

其中
(1)hierarchy表示轮廓的层次关系,对于第 i i i 条轮廓,hierarchy[i][0] , hierarchy[i][1] , hierarchy[i][2] , hierarchy[i][3]分别表示后一条轮廓、前一条轮廓、(同层次的第一个)子轮廓、父轮廓的索引(如果没有对应的索引,则为负数)。

(2)mode参数表示” 轮廓检索模式(Contour retrieval mode)”,包含了RETR_LIST,RETR_EXTERNAL,RETR_CCOMP,RETR_TREE四种模式。

(3)method参数表示轮廓表示方法,一般采用CHAIN_APPROX_SIMPLE。对于矩形轮廓,只需要4个点来保存,如下图所示。

findContours函数分析

以下图为例:

findContours函数分析
\quad
  • LIST模式 —— 列出 所有轮廓
    findContours函数分析

\quad所有轮廓按照 0 ↦ 1 ↦ 2 ↦ 3 ↦ 4 ↦ 5 ↦ 6 ↦ 7 ↦ 8 ↦ 9 0\mapsto1\mapsto2\mapsto3\mapsto4\mapsto5\mapsto6\mapsto7\mapsto8\mapsto9 0 ↦1 ↦2 ↦3 ↦4 ↦5 ↦6 ↦7 ↦8 ↦9 的顺序排列(没有子轮廓、父轮廓之分,只有前后轮廓之分)

前后轮廓关系用 ↦ \mapsto ↦ 表示

  • EXTERNAL模式 —— 只列出所有轮廓中” 最外层的轮廓
    findContours函数分析

\quad三个最外层的轮廓按照 0 ↦ 1 ↦ 2 0\mapsto1\mapsto2 0 ↦1 ↦2 的顺序排列:

\quad 0:[1,-1,-1,-1] 表示该轮廓的下一条轮廓是”索引为1的轮廓”,没有前一条轮廓、子轮廓、父轮廓。
\quad 1:[2, 0,-1,-1] 表示该轮廓的下一条轮廓是”索引为2的轮廓”,前一条轮廓是”索引为0的轮廓”,没有子轮廓、父轮廓。
\quad 2:[-1,1,-1,-1] 表示该轮廓的前一条轮廓是”索引为1的轮廓”,没有下一条轮廓、子轮廓、父轮廓。

前后轮廓关系用 ↦ \mapsto ↦ 表示

  • CCOMP模式 —— 列出 所有轮廓,且hierarchy是以” 成对的方式“来进行组织
    findContours函数分析

轮廓对“按照 0 ↦ 1 ↦ { 4 ⇓ 5 } ↦ { 6 ⇓ 7 } ↦ 8 ↦ 9 ⇓ 2 → 3 ⏞ \begin{array}{l} 0\mapsto1\mapsto \left{\begin{array}{c}\ \4\ \Downarrow\5\end{array}\right} \mapsto \left{\begin{array}{c} \\6\ \Downarrow\7\end{array}\right} \mapsto8\mapsto9 \ \qquad\Downarrow\ \ \ \ \ \overbrace{\ \ \ \ \textcolor{red}{2}\to3\ \ \ \ }\end{array}0 ↦1 ↦⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧​4 ⇓5 ​⎭⎪⎪⎪⎪⎬⎪⎪⎪⎪⎫​↦⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧​6 ⇓7 ​⎭⎪⎪⎪⎪⎬⎪⎪⎪⎪⎫​↦8 ↦9 ⇓2 →3 ​ 的顺序排列:

前后轮廓关系用 ↦ \mapsto ↦ 表示,父子轮廓关系用 ⇒ \Rightarrow ⇒ 表示,同一级别的排列用 → \to → 表示

( 1 ) \quad(1)(1 ) 0:[1,-1,-1,-1] 和 1:[4, 0, 2,-1] 是”同一级并列的关系”,按 0 → 1 0\to1 0 →1 顺序

( 2 ) \quad(2)(2 ) 1:[4, 0, 2,-1] 是 23 的父轮廓,1 → { 2 3 1\to\left{\begin{array}{c} 2\3\end{array}\right.1 →{2 3 ​ 组成一个” 轮廓对“。其中,第一子轮廓 2 和 子轮廓 3 是”同一级并列的关系”,按 2 → 3 2\to3 2 →3 顺序

( 3 ) \quad(3)(3 ) “1-2-3 轮廓对“的下一个是 “4-5 轮廓对“,因此 1 的下一条轮廓是 4:[6, 1, 5,-1] 轮廓。由于 { 4 5 \left{\begin{array}{c} 4\5\end{array}\right.{4 5 ​ 组成一个” 轮廓对“,因此轮廓 4:[6, 1, 5,-1] 是 5:[-1,-1,-1, 4] 的父轮廓。其中,4 和 5 是”同一级并列的关系”,按 4 → 5 4\to5 4 →5 顺序

( 4 ) \quad(4)(4 ) “4-5 轮廓对“的下一个是 “6-7 轮廓对“,因此 4 的下一条轮廓是 6:[8, 4, 7,-1] 轮廓。由于 { 6 7 \left{\begin{array}{c} 6\7\end{array}\right.{6 7 ​ 组成一个” 轮廓对“,因此轮廓 6:[8, 4, 7,-1] 是 7:[-1,-1,-1, 6] 的父轮廓。其中,6 和 7 是”同一级并列的关系”,按 6 → 7 6\to7 6 →7 顺序

( 5 ) \quad(5)(5 ) 轮廓 6:[8, 4, 7,-1] 的下一条轮廓是 8:[9, 6,-1,-1]

( 6 ) \quad(6)(6 ) 轮廓 8:[9, 6,-1,-1] 的下一条轮廓是 9:[-1, 8,-1,-1]
\quad

  • TREE模式 —— 提取 所有轮廓,且hierarchy是以” 树状结构“来进行组织
    findContours函数分析
    ( 1 ) \quad(1)(1 ) 树根: 按照0 ↦ 8 ↦ 9 0\mapsto8\mapsto9 0 ↦8 ↦9 的顺序

前后轮廓关系用 ↦ \mapsto ↦ 表示

\quad\qquad 0:[8,-1,1,-1] , 8:[9, 0,-1,-1], 9:[-1,8,-1,-1] 是 树根,是同一级别的并列关系,其中,轮廓 89没有后代,只有轮廓 0有后代。

( 2 ) \quad(2)(2 )轮廓 0分支
\qquad\qquad\quad按照 0 ⇒ 1 ⇒ 2 ⇒ 3 ⇒ { 4 ↓ 5 ⇒ { 6 ↓ 7 0\Rightarrow1\Rightarrow2\Rightarrow3\Rightarrow\left{\begin{array}{l}\\\\\\\\ \textcolor{red}{4}\\\downarrow \5\Rightarrow\left{\begin{array}{c}\ \\textcolor{red}{6}\\downarrow\ 7\end{array}\right.\end{array}\right.0 ⇒1 ⇒2 ⇒3 ⇒⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧​4 ↓5 ⇒⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧​6 ↓7 ​​ 的顺序

父子轮廓关系用 ⇒ \Rightarrow ⇒ 表示,同一级别的排列用 ↓ \downarrow ↓ 表示

1 ) \quad\quad1)1 ) 0:[8,-1,1,-1] 的后代是轮廓 1:[-1,-1,2,0]
2 ) \quad\quad2)2 ) 1:[-1,-1,2,0] 的后代是轮廓 2:[-1,-1,3,1]
3 ) \quad\quad3)3 ) 2:[-1,-1,3,1] 的后代是轮廓 3:[-1,-1,4,2]
4 ) \quad\quad4)4 ) 3:[-1,-1,4,2] 的后代是轮廓 4和轮廓 5,第一子轮廓 4:[5,-1,-1,3] 和子轮廓 5:[-1, 4, 6, 3]是同一级别的并列关系。其中,第一子轮廓 4 没有后代,只有子轮廓 5有后代。
5 ) \quad\quad5)5 ) 5:[-1, 4, 6, 3] 的后代是轮廓 6和轮廓 7,第一子轮廓 6:[7,-1,-1,5]和子轮廓 7:[7,-1,-1,5]是同一级别的并列关系

\quad
本文代码

#include
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include
#include
using namespace cv;
using namespace std;

int main( int argc, char** argv )
{
    Mat src = imread(argv[1], IMREAD_GRAYSCALE);
    imshow("Source Image", src);

    Mat dst0 = Mat::zeros(src.size(), CV_8UC3);
    Mat dst1 = dst0.clone();
    Mat dst2 = dst0.clone();
    Mat dst3 = dst0.clone();
    threshold(src, src, 128, 255, CV_8U);

    stringstream sstream;
    string str;

    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;

    findContours(src, contours, hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE);
    cout << endl;
    for(int i = 0; i < contours.size(); i++)
    {
        cout << "   #" << i << ": " << hierarchy[i] << endl;
        drawContours(dst0, contours, i, Scalar(255,255,255), 1, 8, hierarchy, 0);
        str = format("%d: [%d,%d,%d,%d]", i,hierarchy[i][0],hierarchy[i][1],hierarchy[i][2],hierarchy[i][3]);
        putText(dst0, str , Point2i(contours[i][0])-Point2i(4,6),1,1.2,Scalar(0,255,0),1,8,false);
    }
    imshow("RETR_LIST", dst0);
    imwrite("retr_list.jpg",dst0);

    findContours(src, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    cout << endl;
    for(int i = 0; i < contours.size(); i++)
    {
        cout << "   #" << i << ": " << hierarchy[i] << endl;
        drawContours(dst1, contours, i, Scalar(255,255,255), 1, 8, hierarchy, 0);
        str = format("%d: [%d,%d,%d,%d]", i,hierarchy[i][0],hierarchy[i][1],hierarchy[i][2],hierarchy[i][3]);
        putText(dst1, str , Point2i(contours[i][0])-Point2i(4,6),1,1.2,Scalar(0,255,0),1,8,false);
    }
    imshow("RETR_EXTERNAL", dst1);
    imwrite("retr_external.jpg",dst1);

    findContours(src, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_NONE);
    cout << endl;
    for(int i = 0; i < contours.size(); i++)
    {
        cout << "   #" << i << ": " << hierarchy[i] << endl;
        drawContours(dst2, contours, i, Scalar(255,255,255), 1, 8, hierarchy, 0);
        str = format("%d: [%d,%d,%d,%d]", i,hierarchy[i][0],hierarchy[i][1],hierarchy[i][2],hierarchy[i][3]);
        putText(dst2, str , Point2i(contours[i][0])-Point2i(4,6),1,1.2,Scalar(0,255,0),1,8,false);
    }
    imshow("RETR_CCOMP", dst2);
    imwrite("retr_ccomp.jpg",dst2);

    findContours(src, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
    cout << endl;
    for(int i = 0; i < contours.size(); i++)
    {
        cout << "   #" << i << ": " << hierarchy[i] << endl;
        drawContours(dst3, contours, i, Scalar(255,255,255), 1, 8, hierarchy, 0);
        str = format("%d: [%d,%d,%d,%d]", i,hierarchy[i][0],hierarchy[i][1],hierarchy[i][2],hierarchy[i][3]);
        putText(dst3, str , Point2i(contours[i][0])-Point2i(4,6),1,1.2,Scalar(0,255,0),1,8,false);
    }
    imshow("RETR_TREE", dst3);
    imwrite("retr_tree.jpg",dst3);

    waitKey(0);
    return 0;
}

运行结果:

   #0: [1, -1, -1, -1]
   #1: [2, 0, -1, -1]
   #2: [3, 1, -1, -1]
   #3: [4, 2, -1, -1]
   #4: [5, 3, -1, -1]
   #5: [6, 4, -1, -1]
   #6: [7, 5, -1, -1]
   #7: [8, 6, -1, -1]
   #8: [9, 7, -1, -1]
   #9: [-1, 8, -1, -1]

   #0: [1, -1, -1, -1]
   #1: [2, 0, -1, -1]
   #2: [-1, 1, -1, -1]

   #0: [1, -1, -1, -1]
   #1: [4, 0, 2, -1]
   #2: [3, -1, -1, 1]
   #3: [-1, 2, -1, 1]
   #4: [6, 1, 5, -1]
   #5: [-1, -1, -1, 4]
   #6: [8, 4, 7, -1]
   #7: [-1, -1, -1, 6]
   #8: [9, 6, -1, -1]
   #9: [-1, 8, -1, -1]

   #0: [8, -1, 1, -1]
   #1: [-1, -1, 2, 0]
   #2: [-1, -1, 3, 1]
   #3: [-1, -1, 4, 2]
   #4: [5, -1, -1, 3]
   #5: [-1, 4, 6, 3]
   #6: [7, -1, -1, 5]
   #7: [-1, 6, -1, 5]
   #8: [9, 0, -1, -1]
   #9: [-1, 8, -1, -1]

Original: https://blog.csdn.net/xfijun/article/details/117694917
Author: zfoox
Title: findContours函数分析

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/705217/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球