clarkk
clarkk

Reputation: 27689

findContours and RETR_TREE iterate through hierarchy

How to iterate through the contours if I only want first (root) level and second level in the hierarchy?

When iterating through contours in the first code sample the desired contours are marked in the result image.. So far so good..

But in the second code sample I can't see how to iterate through first and second level via hierarchy.. The root contour has id 124 but can't see the logic in how to find it's children..

In the bottom the output from hierarchy array is posted

source image

enter image description here

code

std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(boxes, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point(0, 0));

cv::Scalar color    = cv::Scalar(0, 255, 0);
int thickness       = 2;
cv::Mat test;
cv::cvtColor(boxes, test, cv::COLOR_GRAY2BGR);

// filter contours
for(int idx = 0; idx < contours.size(); idx++){
    cv::Rect rect = cv::boundingRect(contours[idx]);

    if(rect.width >= horizontal && rect.height >= vertical){
        std::cout << idx << std::endl;

        cv::rectangle(test, rect, color, thickness);
    }
}

result image

enter image description here

hierarchy iteration

if(!hierarchy.empty()){
    for(int idx = 0; idx >= 0; idx = hierarchy[idx][0]){
        cv::Rect rect = cv::boundingRect(contours[idx]);

        if(rect.width >= horizontal && rect.height >= vertical){
            std::cout << idx << std::endl;

            cv::drawContours(mask, contours, idx, cv::Scalar(255, 255, 255));

            cv::rectangle(test, rect, color, thickness);
            std::cout << "x: " << rect.x << " y: " << rect.y << " w: " << rect.width << " h: " << rect.height << std::endl;

            std::cout << "\tnext: " << hierarchy[idx][1] << std::endl;
            std::cout << "\tprev: " << hierarchy[idx][2] << std::endl;
            std::cout << "\tnested: " << hierarchy[idx][3] << std::endl;

            for(int idy = 0; idy >= 0; idy = hierarchy[idy][0]){
                std::cout << "\t\tchild: " << hierarchy[idy] << std::endl;
            }
        }
    }
}

output

124
x: 0 y: 799 w: 2191 h: 827
        next: 123
        prev: 125
        nested: -1
                child: [1, -1, -1, -1]
                child: [2, 0, -1, -1]
                child: [3, 1, -1, -1]
                child: [4, 2, -1, -1]
                child: [5, 3, -1, -1]
                child: [6, 4, -1, -1]
                child: [7, 5, -1, -1]
                child: [8, 6, -1, -1]
                child: [9, 7, -1, -1]
                child: [10, 8, -1, -1]
                child: [11, 9, -1, -1]
                child: [12, 10, -1, -1]
                child: [13, 11, -1, -1]
                child: [14, 12, -1, -1]
                child: [15, 13, -1, -1]
                child: [16, 14, -1, -1]
                child: [17, 15, -1, -1]
                child: [18, 16, -1, -1]
                child: [19, 17, -1, -1]
                child: [20, 18, -1, -1]
                child: [21, 19, -1, -1]
                child: [22, 20, -1, -1]
                child: [23, 21, -1, -1]
                child: [24, 22, -1, -1]
                child: [25, 23, -1, -1]
                child: [26, 24, -1, -1]
                child: [27, 25, -1, -1]
                child: [28, 26, -1, -1]
                child: [29, 27, -1, -1]
                child: [30, 28, -1, -1]
                child: [31, 29, -1, -1]
                child: [32, 30, -1, -1]
                child: [33, 31, -1, -1]
                child: [34, 32, -1, -1]
                child: [35, 33, -1, -1]
                child: [36, 34, -1, -1]
                child: [37, 35, -1, -1]
                child: [38, 36, -1, -1]
                child: [39, 37, -1, -1]
                child: [40, 38, -1, -1]
                child: [41, 39, -1, -1]
                child: [42, 40, -1, -1]
                child: [43, 41, -1, -1]
                child: [44, 42, -1, -1]
                child: [45, 43, -1, -1]
                child: [46, 44, -1, -1]
                child: [47, 45, -1, -1]
                child: [48, 46, -1, -1]
                child: [49, 47, -1, -1]
                child: [50, 48, -1, -1]
                child: [51, 49, -1, -1]
                child: [52, 50, -1, -1]
                child: [53, 51, -1, -1]
                child: [54, 52, -1, -1]
                child: [55, 53, -1, -1]
                child: [56, 54, -1, -1]
                child: [57, 55, -1, -1]
                child: [58, 56, -1, -1]
                child: [59, 57, -1, -1]
                child: [60, 58, -1, -1]
                child: [61, 59, -1, -1]
                child: [62, 60, -1, -1]
                child: [63, 61, -1, -1]
                child: [64, 62, -1, -1]
                child: [65, 63, -1, -1]
                child: [66, 64, -1, -1]
                child: [67, 65, -1, -1]
                child: [68, 66, -1, -1]
                child: [69, 67, -1, -1]
                child: [70, 68, -1, -1]
                child: [71, 69, -1, -1]
                child: [72, 70, -1, -1]
                child: [73, 71, -1, -1]
                child: [74, 72, -1, -1]
                child: [75, 73, -1, -1]
                child: [76, 74, -1, -1]
                child: [77, 75, -1, -1]
                child: [78, 76, -1, -1]
                child: [79, 77, -1, -1]
                child: [80, 78, -1, -1]
                child: [81, 79, -1, -1]
                child: [82, 80, -1, -1]
                child: [83, 81, -1, -1]
                child: [84, 82, -1, -1]
                child: [85, 83, -1, -1]
                child: [86, 84, -1, -1]
                child: [87, 85, -1, -1]
                child: [88, 86, -1, -1]
                child: [89, 87, -1, -1]
                child: [90, 88, -1, -1]
                child: [91, 89, -1, -1]
                child: [92, 90, -1, -1]
                child: [93, 91, -1, -1]
                child: [94, 92, -1, -1]
                child: [95, 93, -1, -1]
                child: [96, 94, -1, -1]
                child: [97, 95, -1, -1]
                child: [98, 96, -1, -1]
                child: [99, 97, -1, -1]
                child: [100, 98, -1, -1]
                child: [101, 99, -1, -1]
                child: [102, 100, -1, -1]
                child: [103, 101, -1, -1]
                child: [104, 102, -1, -1]
                child: [105, 103, -1, -1]
                child: [106, 104, -1, -1]
                child: [107, 105, -1, -1]
                child: [108, 106, -1, -1]
                child: [109, 107, -1, -1]
                child: [110, 108, -1, -1]
                child: [111, 109, -1, -1]
                child: [112, 110, -1, -1]
                child: [113, 111, -1, -1]
                child: [114, 112, -1, -1]
                child: [115, 113, -1, -1]
                child: [116, 114, -1, -1]
                child: [117, 115, -1, -1]
                child: [118, 116, -1, -1]
                child: [119, 117, -1, -1]
                child: [120, 118, -1, -1]
                child: [121, 119, -1, -1]
                child: [122, 120, -1, -1]
                child: [123, 121, -1, -1]
                child: [124, 122, -1, -1]
                child: [213, 123, 125, -1]
                child: [214, 124, -1, -1]
                child: [215, 213, -1, -1]
                child: [216, 214, -1, -1]
                child: [217, 215, -1, -1]
                child: [218, 216, -1, -1]
                child: [219, 217, -1, -1]
                child: [220, 218, -1, -1]
                child: [221, 219, -1, -1]
                child: [222, 220, -1, -1]
                child: [223, 221, -1, -1]
                child: [224, 222, -1, -1]
                child: [225, 223, -1, -1]
                child: [226, 224, -1, -1]
                child: [227, 225, -1, -1]
                child: [228, 226, -1, -1]
                child: [229, 227, -1, -1]
                child: [230, 228, -1, -1]
                child: [231, 229, -1, -1]
                child: [232, 230, -1, -1]
                child: [233, 231, -1, -1]
                child: [234, 232, -1, -1]
                child: [235, 233, -1, -1]
                child: [236, 234, -1, -1]
                child: [237, 235, -1, -1]
                child: [238, 236, -1, -1]
                child: [239, 237, -1, -1]
                child: [240, 238, -1, -1]
                child: [241, 239, -1, -1]
                child: [242, 240, -1, -1]
                child: [243, 241, -1, -1]
                child: [244, 242, -1, -1]
                child: [245, 243, -1, -1]
                child: [246, 244, -1, -1]
                child: [247, 245, -1, -1]
                child: [248, 246, -1, -1]
                child: [249, 247, -1, -1]
                child: [250, 248, -1, -1]
                child: [251, 249, -1, -1]
                child: [252, 250, -1, -1]
                child: [253, 251, -1, -1]
                child: [254, 252, -1, -1]
                child: [255, 253, -1, -1]
                child: [256, 254, -1, -1]
                child: [257, 255, -1, -1]
                child: [258, 256, -1, -1]
                child: [259, 257, -1, -1]
                child: [260, 258, -1, -1]
                child: [261, 259, -1, -1]
                child: [262, 260, -1, -1]
                child: [263, 261, -1, -1]
                child: [264, 262, -1, -1]
                child: [265, 263, -1, -1]
                child: [266, 264, -1, -1]
                child: [267, 265, -1, -1]
                child: [268, 266, -1, -1]
                child: [269, 267, -1, -1]
                child: [270, 268, -1, -1]
                child: [271, 269, -1, -1]
                child: [272, 270, -1, -1]
                child: [273, 271, -1, -1]
                child: [274, 272, -1, -1]
                child: [275, 273, -1, -1]
                child: [276, 274, -1, -1]
                child: [277, 275, -1, -1]
                child: [278, 276, -1, -1]
                child: [279, 277, -1, -1]
                child: [280, 278, -1, -1]
                child: [281, 279, -1, -1]
                child: [282, 280, -1, -1]
                child: [283, 281, -1, -1]
                child: [-1, 282, -1, -1]

Upvotes: 2

Views: 1634

Answers (1)

Dan Mašek
Dan Mašek

Reputation: 19041

Just for posterity, let's define some symbolic names for the indices of the Vec4i that represents the hierarchy data:

  • NEXT_SIBLING = 0
  • PREV_SIBLING = 1
  • CHILD_CONTOUR = 2
  • PARENT_CONTOUR = 3

Let's also recall that each of the 4 elements represents the index of the contours, and that -1 is used when there is no such object.

Let entry be an Vec4i representing a hierarchy entry. Then we can make following observations:

  • entry[PARENT_CONTOUR] == -1 → This contour has no parent -- i.e. it's a top-level contour.
  • entry[CHILD_CONTOUR] == -1 → This contours has no children.
  • entry[NEXT_SIBLING] == -1 → There are no more contours at this level of hierarchy for this particular parent.

At this point the strategy is simple.

Start with the first contour in the list (and verify that the assumption that it's a top-level contour holds). If this contour has a child contour, process that child contour and all its siblings. Repeat for all the siblings of the first contour.


Sample Code

#include <opencv2/opencv.hpp>

#define NEXT_SIBLING 0
#define PREV_SIBLING 1
#define CHILD_CONTOUR 2
#define PARENT_CONTOUR 3

int main()
{
    cv::Mat boxes(cv::imread("WqP29.png", cv::IMREAD_GRAYSCALE));

    cv::Mat output;
    cv::cvtColor(boxes, output, cv::COLOR_GRAY2BGR);

    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;
    cv::findContours(boxes, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point(0, 0));

    if (hierarchy.empty()) {
        return -1;
    }

    // Iterate over top-level contours
    for (int i(0); i >= 0; i = hierarchy[i][NEXT_SIBLING]) {
        // This must be a top-level contour
        CV_Assert(hierarchy[i][PARENT_CONTOUR] == -1);

        if (hierarchy[i][CHILD_CONTOUR] == -1) {
            // This contour has no children, draw it red for demonstration purposes
            cv::drawContours(output, contours, i, cv::Scalar(0, 0, 255), -1);
            continue;
        }

        std::cout << "Contour with children: #" << i
            << " r=" << cv::boundingRect(contours[i])
            << " h=" << hierarchy[i]
            << "\n";

        // This contour has children, draw it blue for demonstration purposes
        cv::drawContours(output, contours, i, cv::Scalar(255, 0, 0), -1);

        // Iterate over all direct children
        for (int j(hierarchy[i][CHILD_CONTOUR]); j >= 0; j = hierarchy[j][NEXT_SIBLING]) {
            std::cout << " * Child contour: #" << j
                << " r=" << cv::boundingRect(contours[j])
                << " h=" << hierarchy[j]
                << "\n";

            cv::drawContours(output, contours, j, cv::Scalar(0, 255, 0), -1);
        }
    }

    cv::imwrite("WqP29_out.png", output);

    return 0;
}

Console Output

Contour with children: #124 r=[2190 x 827 from (1, 799)] h=[213, 123, 125, -1]
 * Child contour: #125 r=[377 x 99 from (1808, 1521)] h=[127, -1, 126, 124]
 * Child contour: #127 r=[377 x 98 from (1808, 1419)] h=[131, 125, 128, 124]
 * Child contour: #131 r=[377 x 98 from (1808, 1317)] h=[133, 127, 132, 124]
 * Child contour: #133 r=[377 x 98 from (1808, 1214)] h=[135, 131, 134, 124]
 * Child contour: #135 r=[607 x 98 from (1197, 1214)] h=[137, 133, 136, 124]
 * Child contour: #137 r=[943 x 98 from (250, 1214)] h=[144, 135, 138, 124]
 * Child contour: #144 r=[243 x 99 from (3, 1214)] h=[146, 137, 145, 124]
 * Child contour: #146 r=[377 x 98 from (1808, 1112)] h=[148, 144, 147, 124]
 * Child contour: #148 r=[607 x 98 from (1197, 1112)] h=[150, 146, 149, 124]
 * Child contour: #150 r=[943 x 98 from (250, 1112)] h=[156, 148, 151, 124]
 * Child contour: #156 r=[243 x 98 from (3, 1112)] h=[158, 150, 157, 124]
 * Child contour: #158 r=[377 x 99 from (1808, 1009)] h=[160, 156, 159, 124]
 * Child contour: #160 r=[607 x 99 from (1197, 1009)] h=[162, 158, 161, 124]
 * Child contour: #162 r=[943 x 99 from (250, 1009)] h=[171, 160, 163, 124]
 * Child contour: #171 r=[243 x 99 from (3, 1009)] h=[173, 162, 172, 124]
 * Child contour: #173 r=[377 x 98 from (1808, 907)] h=[174, 171, -1, 124]
 * Child contour: #174 r=[607 x 98 from (1197, 907)] h=[175, 173, -1, 124]
 * Child contour: #175 r=[943 x 98 from (250, 907)] h=[182, 174, 176, 124]
 * Child contour: #182 r=[243 x 100 from (3, 907)] h=[183, 175, -1, 124]
 * Child contour: #183 r=[377 x 98 from (1808, 805)] h=[187, 182, 184, 124]
 * Child contour: #187 r=[607 x 98 from (1197, 805)] h=[199, 183, 188, 124]
 * Child contour: #199 r=[943 x 98 from (250, 805)] h=[208, 187, 200, 124]
 * Child contour: #208 r=[243 x 98 from (3, 805)] h=[-1, 199, 209, 124]

Output image

Top-level contours without children are filled red. Top-level contours with children are filled blue. Second-level contours are filled green.

Sample output

Upvotes: 3

Related Questions