Extracting Contours with OpenCV

Yesterday I was asked how to extract a contour from a given image in OpenCV. Here is an example. Imagine we got this tasty apple and we want to put it in another image (with a green background):

apple.jpg

One solution is to first detect the edges of the apple with a Canny filter, then find the contours with OpenCV's findContours and create a mask with drawContours:

apple_mask.jpg

Finally copy the masked original image to the new image, which means only the areas of the contours will be copied... and you are done. You end up with a tasty apple on a green background:

apple_cropped.jpg

In code this translates to:

#include "cv.h"
#include "highgui.h"

using namespace cv;
using namespace std;

int main() {

    // read in the apple (change path to the file)
    Mat img0 = imread("/home/philipp/img/apple.jpg", 1);

    Mat img1;
    cvtColor(img0, img1, CV_RGB2GRAY);

    // apply your filter
    Canny(img1, img1, 100, 200);

    // find the contours
    vector< vector<Point> > contours;
    findContours(img1, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    // you could also reuse img1 here
    Mat mask = Mat::zeros(img1.rows, img1.cols, CV_8UC1);

    // CV_FILLED fills the connected components found
    drawContours(mask, contours, -1, Scalar(255), CV_FILLED);

    /*
     Before drawing all contours you could also decide
     to only draw the contour of the largest connected component
     found. Here's some commented out code how to do that:
    */

//    vector<double> areas(contours.size());
//    for(int i = 0; i < contours.size(); i++)
//        areas[i] = contourArea(Mat(contours[i]));
//    double max;
//    Point maxPosition;
//    minMaxLoc(Mat(areas),0,&max,0,&maxPosition);
//    drawContours(mask, contours, maxPosition.y, Scalar(1), CV_FILLED);

    // let's create a new image now
    Mat crop(img0.rows, img0.cols, CV_8UC3);

    // set background to green
    crop.setTo(Scalar(0,255,0));

    // and copy the magic apple
    img0.copyTo(crop, mask);

    // normalize so imwrite(...)/imshow(...) shows the mask correctly!
    normalize(mask.clone(), mask, 0.0, 255.0, CV_MINMAX, CV_8UC1);

    // show the images
    imshow("original", img0);
    imshow("mask", mask);
    imshow("canny", img1);
    imshow("cropped", crop);

    imwrite("/home/philipp/img/apple_canny.jpg", img1);
    imwrite("/home/philipp/img/apple_mask.jpg", mask);
    imwrite("/home/philipp/img/apple_cropped.jpg", crop);

    waitKey();
    return 0;
}

How to contribute

One of the easiest ways to contribute is to participate in discussions. You can also contribute by submitting pull requests.

General feedback and discussions?

Do you have questions or feedback on this article? Please create an issue on the GitHub issue tracker.

Something is wrong or missing?

There may be something wrong or missing in this article. If you want to help fixing it, then please make a Pull Request to this file on GitHub.