Visual Servoing Platform version 3.6.0
Loading...
Searching...
No Matches
Tutorial: Contours extraction from a binary image

Introduction

This tutorial will show you how to extract the contours from a binary image. The contour extraction algorithm is based on [articleSuzuki] article and most of the implementation has been ported from [Hare:2011:OIJ:2072298.2072421] library.

The function to call is vp::findContours(const vpImage<unsigned char> &, vpContour &, std::vector<std::vector<vpImagePoint> > &, const vpContourRetrievalType&):

  • the first argument is the image where '0' pixel value means the background and '1' pixel value means the foreground. Other values are not allowed.
  • the second argument is a vp::vpContour structure that contains the list of contours in a tree
  • the third argument is the list of contours
  • the last argument is an option to choose the type of contour extraction, see vp::vpContourRetrievalType

The vp::vpContour structure is composed of:

The different contour extraction methods are:

  • vp::CONTOUR_RETR_TREE, all the contours are extracted and stored in a hierarchical tree.
  • vp::CONTOUR_RETR_LIST, all the contours are extracted and stored in a list. The top level contour contains in m_children the list of all the extracted contours.
  • vp::CONTOUR_RETR_EXTERNAL, only the external contours are extracted and stored in a list. The top level contour contains in m_children the list of the external extracted contours.

The next section will provide a concrete example for better understanding.

Example code

The following example also available in tutorial-contour.cpp will demonstrate on a sample image the result of each of these methods:

#include <cstdlib>
#include <iostream>
#include <visp3/core/vpImage.h>
#include <visp3/gui/vpDisplayGDI.h>
#include <visp3/gui/vpDisplayOpenCV.h>
#include <visp3/gui/vpDisplayX.h>
#include <visp3/io/vpImageIo.h>
#if defined(VISP_HAVE_MODULE_IMGPROC) && (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV))
#include <visp3/imgproc/vpImgproc.h>
namespace
{
void displayContourInfo(const vp::vpContour &contour, int level)
{
std::cout << "\nContour:" << std::endl;
std::cout << "\tlevel: " << level << std::endl;
std::cout << "\tcontour type: " << (contour.m_contourType == vp::CONTOUR_OUTER ? "outer contour" : "hole contour")
<< std::endl;
std::cout << "\tcontour size: " << contour.m_points.size() << std::endl;
std::cout << "\tnb children: " << contour.m_children.size() << std::endl;
for (std::vector<vp::vpContour *>::const_iterator it = contour.m_children.begin(); it != contour.m_children.end();
++it) {
displayContourInfo(**it, level + 1);
}
}
void drawContoursTree(vpImage<vpRGBa> &I, const vp::vpContour &contour)
{
std::vector<std::vector<vpImagePoint> > contours;
contours.push_back(contour.m_points);
for (std::vector<vp::vpContour *>::const_iterator it = contour.m_children.begin(); it != contour.m_children.end();
++it) {
drawContoursTree(I, **it);
}
}
} // namespace
#endif
int main(int argc, const char **argv)
{
#if defined(VISP_HAVE_MODULE_IMGPROC) && (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV))
std::string input_filename = "grid36-03.pgm";
bool white_foreground = false;
for (int i = 1; i < argc; i++) {
if (std::string(argv[i]) == "--input" && i + 1 < argc) {
input_filename = std::string(argv[i + 1]);
} else if (std::string(argv[i]) == "--white_foreground") {
white_foreground = true;
} else if (std::string(argv[i]) == "--method" && i + 1 < argc) {
extraction_method = (vp::vpContourRetrievalType)atoi(argv[i + 1]);
} else if (std::string(argv[i]) == "--help" || std::string(argv[i]) == "-h") {
std::cout << "Usage: " << argv[0]
<< " [--input <input image>] [--method <0: "
"CONTOUR_RETR_TREE, 1: CONTOUR_RETR_LIST, 2: "
"CONTOUR_RETR_EXTERNAL>]"
" [--white_foreground] [--help]"
<< std::endl;
return EXIT_SUCCESS;
}
}
vpImageIo::read(I, input_filename);
vpImage<vpRGBa> I_draw_contours(I.getHeight(), I.getWidth());
#ifdef VISP_HAVE_X11
vpDisplayX d, d2;
#elif defined(VISP_HAVE_GDI)
vpDisplayGDI d, d2;
#elif defined(HAVE_OPENCV_HIGHGUI)
#endif
d.init(I_bin, 0, 0, "After binarisation");
d2.init(I_draw_contours, I_bin.getWidth(), 10, "Contours");
vp::autoThreshold(I, vp::AUTO_THRESHOLD_OTSU, white_foreground ? 0 : 1, white_foreground ? 1 : 0);
for (unsigned int i = 0; i < I_bin.getSize(); i++) {
I_bin.bitmap[i] = 255 * I.bitmap[i];
}
vp::vpContour vp_contours;
std::vector<std::vector<vpImagePoint> > contours;
vp::findContours(I, vp_contours, contours, extraction_method);
vp::drawContours(I_draw_contours, contours, vpColor::red);
vpDisplay::display(I_draw_contours);
vpDisplay::displayText(I_draw_contours, 20, 20, "Click to draw outer / hole contours.", vpColor::red);
vpDisplay::flush(I_draw_contours);
vpDisplay::getClick(I_draw_contours);
I_draw_contours = 0;
drawContoursTree(I_draw_contours, vp_contours);
displayContourInfo(vp_contours, 0);
vpDisplay::display(I_draw_contours);
vpDisplay::displayText(I_draw_contours, 20, 20, "Click to quit.", vpColor::red);
vpDisplay::displayText(I_draw_contours, 20, I_draw_contours.getWidth() - 200, "Outer contour", vpColor::red);
vpDisplay::displayText(I_draw_contours, 20, I_draw_contours.getWidth() - 100, "Hole contour", vpColor::green);
vpDisplay::flush(I_draw_contours);
vpDisplay::getClick(I_draw_contours);
#else
(void)argc;
(void)argv;
#endif
return EXIT_SUCCESS;
}
static const vpColor red
Definition vpColor.h:211
static const vpColor green
Definition vpColor.h:214
Display for windows using GDI (available on any windows 32 platform).
The vpDisplayOpenCV allows to display image using the OpenCV library. Thus to enable this class OpenC...
Use the X11 console to display images on unix-like OS. Thus to enable this class X11 should be instal...
Definition vpDisplayX.h:132
void init(vpImage< unsigned char > &I, int win_x=-1, int win_y=-1, const std::string &win_title="")
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
static void flush(const vpImage< unsigned char > &I)
static void displayText(const vpImage< unsigned char > &I, const vpImagePoint &ip, const std::string &s, const vpColor &color)
static void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition of the vpImage class member functions.
Definition vpImage.h:135
unsigned int getWidth() const
Definition vpImage.h:242
Type * bitmap
points toward the bitmap
Definition vpImage.h:139
unsigned int getHeight() const
Definition vpImage.h:184
VISP_EXPORT void findContours(const vpImage< unsigned char > &I_original, vpContour &contours, std::vector< std::vector< vpImagePoint > > &contourPts, const vpContourRetrievalType &retrievalMode=vp::CONTOUR_RETR_TREE)
VISP_EXPORT void drawContours(vpImage< unsigned char > &I, const std::vector< std::vector< vpImagePoint > > &contours, unsigned char grayValue=255)
VISP_EXPORT unsigned char autoThreshold(vpImage< unsigned char > &I, const vp::vpAutoThresholdMethod &method, const unsigned char backgroundValue=0, const unsigned char foregroundValue=255)
vpContourRetrievalType
Definition vpContours.h:197
@ CONTOUR_RETR_TREE
Definition vpContours.h:198
@ AUTO_THRESHOLD_OTSU
Definition vpImgproc.h:83
@ CONTOUR_OUTER
Definition vpContours.h:189
vpContourType m_contourType
Contour type.
Definition vpContours.h:212
std::vector< vpImagePoint > m_points
Vector of points belonging to the contour.
Definition vpContours.h:216
std::vector< vpContour * > m_children
Children contour.
Definition vpContours.h:210

These functions are provided in a vp:: namespace and accessible using this include:

#include <visp3/imgproc/vpImgproc.h>

The first steps are:

The result images for each step are:

Input image
Image after binarization using the Otsu method
Contours extracted and displayed on a new image

To understand how the hierarchical contours extraction works, let's switch on another example. In a terminal, run:

$ ./tutorial-contour --input Contours_tree.pgm --white_foreground

The image after binarisation:

Image after binarization using the Otsu method

Instead of drawing all the contours with the same color, we can assign a first color for vp::CONTOUR_OUTER contour and a second color for vp::CONTOUR_HOLE contour.

The function to navigate in the contour tree is the following:

void drawContoursTree(vpImage<vpRGBa> &I, const vp::vpContour &contour)
{
std::vector<std::vector<vpImagePoint> > contours;
contours.push_back(contour.m_points);
for (std::vector<vp::vpContour *>::const_iterator it = contour.m_children.begin(); it != contour.m_children.end();
++it) {
drawContoursTree(I, **it);
}
}

The call to draw the hierarchical contours:

drawContoursTree(I_draw_contours, vp_contours);

The result image is:

Contours extracted and displayed on a new image, in red outer contours, in green hole contours

To display the hierarchy, we can use this function:

void displayContourInfo(const vp::vpContour &contour, int level)
{
std::cout << "\nContour:" << std::endl;
std::cout << "\tlevel: " << level << std::endl;
std::cout << "\tcontour type: " << (contour.m_contourType == vp::CONTOUR_OUTER ? "outer contour" : "hole contour")
<< std::endl;
std::cout << "\tcontour size: " << contour.m_points.size() << std::endl;
std::cout << "\tnb children: " << contour.m_children.size() << std::endl;
for (std::vector<vp::vpContour *>::const_iterator it = contour.m_children.begin(); it != contour.m_children.end();
++it) {
displayContourInfo(**it, level + 1);
}
}

For the vp::CONTOUR_RETR_TREE method, the output is:

Contour:
level: 0
contour type: hole contour
contour size: 0
nb children: 3

Contour:
level: 1
contour type: outer contour
contour size: 438
nb children: 0

Contour:
level: 1
contour type: outer contour
contour size: 748
nb children: 0

Contour:
level: 1
contour type: outer contour
contour size: 2012
nb children: 1

Contour:
level: 2
contour type: hole contour
contour size: 1906
nb children: 1

Contour:
level: 3
contour type: outer contour
contour size: 1610
nb children: 1

Contour:
level: 4
contour type: hole contour
contour size: 1494
nb children: 1

Contour:
level: 5
contour type: outer contour
contour size: 792
nb children: 2

Contour:
level: 6
contour type: hole contour
contour size: 372
nb children: 0

Contour:
level: 6
contour type: hole contour
contour size: 392
nb children: 0

The top level contour is always the root contour with zero contour point and which contains the list of contours.

For the vp::CONTOUR_RETR_EXTERNAL method, the output is:

Contour:
level: 0
contour type: hole contour
contour size: 0
nb children: 3
Contour:
level: 1
contour type: outer contour
contour size: 438
nb children: 0
Contour:
level: 1
contour type: outer contour
contour size: 748
nb children: 0
Contour:
level: 1
contour type: outer contour
contour size: 2012
nb children: 0

The result image is:

External contours extracted and displayed on a new image

For the vp::CONTOUR_RETR_LIST method, the output is:

Contour:
level: 0
contour type: hole contour
contour size: 0
nb children: 9
Contour:
level: 1
contour type: outer contour
contour size: 438
nb children: 0
Contour:
level: 1
contour type: outer contour
contour size: 748
nb children: 0
Contour:
level: 1
contour type: outer contour
contour size: 2012
nb children: 0
Contour:
level: 1
contour type: hole contour
contour size: 1906
nb children: 0
Contour:
level: 1
contour type: outer contour
contour size: 1610
nb children: 0
Contour:
level: 1
contour type: hole contour
contour size: 1494
nb children: 0
Contour:
level: 1
contour type: outer contour
contour size: 792
nb children: 0
Contour:
level: 1
contour type: hole contour
contour size: 372
nb children: 0
Contour:
level: 1
contour type: hole contour
contour size: 392
nb children: 0

Next tutorial

You can now read the Tutorial: Connected-components labeling, for a similar method to extract the connected-components in a grayscale image.