OpenCV
Open Source Computer Vision
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
Creating Bounding boxes and circles for contours

Table of Contents

Prev Tutorial: Convex Hull
Next Tutorial: Creating Bounding rotated boxes and ellipses for contours

Original author Ana Huamán
Compatibility OpenCV >= 3.0

Goal

In this tutorial you will learn how to:

Theory

Code

This tutorial code's is shown lines below. You can also download it from here

#include <iostream>
using namespace cv;
using namespace std;
Mat src_gray;
int thresh = 100;
RNG rng(12345);
void thresh_callback(int, void* );
int main( int argc, char** argv )
{
CommandLineParser parser( argc, argv, "{@input | stuff.jpg | input image}" );
Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
if( src.empty() )
{
cout << "Could not open or find the image!\n" << endl;
cout << "usage: " << argv[0] << " <Input image>" << endl;
return -1;
}
cvtColor( src, src_gray, COLOR_BGR2GRAY );
blur( src_gray, src_gray, Size(3,3) );
const char* source_window = "Source";
namedWindow( source_window );
imshow( source_window, src );
const int max_thresh = 255;
createTrackbar( "Canny thresh:", source_window, &thresh, max_thresh, thresh_callback );
thresh_callback( 0, 0 );
waitKey();
return 0;
}
void thresh_callback(int, void* )
{
Mat canny_output;
Canny( src_gray, canny_output, thresh, thresh*2 );
vector<vector<Point> > contours;
findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );
vector<vector<Point> > contours_poly( contours.size() );
vector<Rect> boundRect( contours.size() );
vector<Point2f>centers( contours.size() );
vector<float>radius( contours.size() );
for( size_t i = 0; i < contours.size(); i++ )
{
approxPolyDP( contours[i], contours_poly[i], 3, true );
boundRect[i] = boundingRect( contours_poly[i] );
minEnclosingCircle( contours_poly[i], centers[i], radius[i] );
}
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
for( size_t i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) );
drawContours( drawing, contours_poly, (int)i, color );
rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2 );
circle( drawing, centers[i], (int)radius[i], color, 2 );
}
imshow( "Contours", drawing );
}
Designed for command line parsing.
Definition utility.hpp:890
n-dimensional dense array class
Definition mat.hpp:829
MatSize size
Definition mat.hpp:2177
bool empty() const
Returns true if the array has no elements.
Random Number Generator.
Definition core.hpp:2874
Template class for specifying the size of an image or rectangle.
Definition types.hpp:335
std::string String
Definition cvstd.hpp:151
#define CV_8UC3
Definition interface.h:90
void imshow(const String &winname, InputArray mat)
Displays an image in the specified window.
int main(int argc, char *argv[])
Definition highgui_qt.cpp:3
Definition core.hpp:107
STL namespace.

Explanation

The main function is rather simple, as follows from the comments we do the following:

  • Open the image, convert it into grayscale and blur it to get rid of the noise.
CommandLineParser parser( argc, argv, "{@input | stuff.jpg | input image}" );
Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
if( src.empty() )
{
cout << "Could not open or find the image!\n" << endl;
cout << "usage: " << argv[0] << " <Input image>" << endl;
return -1;
}
cvtColor( src, src_gray, COLOR_BGR2GRAY );
blur( src_gray, src_gray, Size(3,3) );
  • Create a window with header "Source" and display the source file in it.
const char* source_window = "Source";
namedWindow( source_window );
imshow( source_window, src );
  • Create a trackbar on the source_window and assign a callback function to it. In general callback functions are used to react to some kind of signal, in our case it's trackbar's state change. Explicit one-time call of thresh_callback is necessary to display the "Contours" window simultaneously with the "Source" window.
const int max_thresh = 255;
createTrackbar( "Canny thresh:", source_window, &thresh, max_thresh, thresh_callback );
thresh_callback( 0, 0 );

The callback function does all the interesting job.

  • Use cv::Canny to detect edges in the images.
Mat canny_output;
Canny( src_gray, canny_output, thresh, thresh*2 );
  • Finds contours and saves them to the vectors contour and hierarchy.
vector<vector<Point> > contours;
findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );
  • For every found contour we now apply approximation to polygons with accuracy +-3 and stating that the curve must be closed. After that we find a bounding rect for every polygon and save it to boundRect. At last we find a minimum enclosing circle for every polygon and save it to center and radius vectors.
vector<vector<Point> > contours_poly( contours.size() );
vector<Rect> boundRect( contours.size() );
vector<Point2f>centers( contours.size() );
vector<float>radius( contours.size() );
for( size_t i = 0; i < contours.size(); i++ )
{
approxPolyDP( contours[i], contours_poly[i], 3, true );
boundRect[i] = boundingRect( contours_poly[i] );
minEnclosingCircle( contours_poly[i], centers[i], radius[i] );
}

We found everything we need, all we have to do is to draw.

  • Create new Mat of unsigned 8-bit chars, filled with zeros. It will contain all the drawings we are going to make (rects and circles).
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
  • For every contour: pick a random color, draw the contour, the bounding rectangle and the minimal enclosing circle with it.
for( size_t i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) );
drawContours( drawing, contours_poly, (int)i, color );
rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2 );
circle( drawing, centers[i], (int)radius[i], color, 2 );
}
  • Display the results: create a new window "Contours" and show everything we added to drawings on it.
imshow( "Contours", drawing );

Result

Here it is: