简体   繁体   中英

drawing convex hull of the biggest contour using OpenCV C++

int main()
{

vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
int largest_area=0;
int largest_contour_index=0;


OriginalImage = imread("C:\\Data Drive\\opencv Projects\\testwithC++\\Plant001-9\\SideView90\\Day_021.png",CV_LOAD_IMAGE_GRAYSCALE);
BackgroundImage = imread("C:\\Data Drive\\opencv Projects\\testwithC++\\Plant001-9\\SideView90\\Day_001.png",CV_LOAD_IMAGE_GRAYSCALE);

absdiff(OriginalImage,BackgroundImage,GrayImage);
threshold(GrayImage,Binary,80,255,CV_THRESH_BINARY);


namedWindow( "OriginalImage", WINDOW_NORMAL);             
imshow("OriginalImage", OriginalImage);                   


namedWindow( "BackgroundImage", WINDOW_NORMAL);             
imshow("BackgroundImage", BackgroundImage);                 

namedWindow( "GrayImage", WINDOW_NORMAL);               
imshow("GrayImage", GrayImage);                         


namedWindow( "Binary", WINDOW_NORMAL);              
imshow("Binary", Binary);                 

ImageROI = Binary(Rect(300,0,Binary.size().width-600,Binary.size().height));

namedWindow( "ImageROI", WINDOW_NORMAL);                
imshow("ImageROI", ImageROI);                           

dilate(ImageROI,BinaryMorph,Mat(),Point(-1,-1),2);

namedWindow( "BinaryMorph", WINDOW_NORMAL);             
imshow("BinaryMorph", BinaryMorph);                 

findContours(BinaryMorph, contours, hierarchy, RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

for( int i = 0; i< contours.size(); i++ )
 {
    double a=contourArea(contours[i],false);
    if(a>largest_area)
    {
    largest_area=a;
    largest_contour_index=i; 
    }
 }
Contour = Mat(ImageROI.size().width,ImageROI.size().height,CV_8UC1,Scalar::all(0));


drawContours(Contour, contours,largest_contour_index,Scalar(255),CV_FILLED, 8,hierarchy);

vector<Point>hull;

convexHull(contours[largest_contour_index],hull,CV_CLOCKWISE,true);
drawContours(Contour, Mat(hull),largest_contour_index,Scalar(255),3, 8);

namedWindow( "Contour", WINDOW_NORMAL);             
imshow("Contour", Contour);                       

OriginalImage.release();
BackgroundImage.release();
GrayImage.release();
Binary.release();
BinaryMorph.release();
ImageROI.release();
Contour.release();

waitKey(0);                                              
return 0;}

I have written the above code to draw the convex hull of the biggest contour using OpenCV 2.4.9 using microsoft visual studio 2010 express. The code complies and executes without any error, draws the biggest contour successfully, but CANNOT DISPLAY THE CONTOUR.

Please be informed that I used C api so far, and now trying to convert to C++. So, I am new to use openCV with C++. Your advice to make the program work for drawing the convex hull would be greatly appreciated.

The main problem here is that you're calling drawContours for drawing the convex hull incorrectly.

drawContours accepts as input points a InputArrayOfArrays , ie a 2 dimensional structure, while hull is only 1 dimensional.

You can easily fix this creating on the fly a two dimensional vector, with only one element (the hull ), passing as index 0 , ie the first element of the 2D structure you just created:

vector<Point>hull;
convexHull(contours[largest_contour_index], hull, CV_CLOCKWISE, true);
drawContours(Contour, vector<vector<Point>> {hull}, 0, Scalar(128), 3, 8);

or, if C++11 is not available:

vector<Point>hull;
convexHull(contours[largest_contour_index], hull, CV_CLOCKWISE, true);
vector<vector<Point>> tmp;
tmp.push_back(hull);
drawContours(Contour, tmp, 0, Scalar(128), 3, 8);

Also, since you're from a C background, a few tips:

  • Declare variables just before they are needed, not at the beginning of the function.
  • You don't need to manually release Mat s, since they are automatically deallocated by their destructor when they go out of scope.

Also:

  • if you don't need to be able to resize the windows, you don't need to call namedWindow since imshow will just create that for you. (In the code below I dropped all calls to namedWindow , but I cannot resize them anymore)
  • Since you already know the type of your matrices ( CV_8UC1 ), you can use the Mat_<Tp> specialization, ie Mat1b (aka Mat_<uchar> ). This will produce less verbose code, and also allows you to access elements like mat(i,j) , instead of mat.at<uchar>(i,j) (this is not needed here, but just a general advice).
  • dilate with an empty kernel is useless. Please define a suitable kernel.
  • you don't need the hierarchy here, so just don't use it.
  • use updated constant names: IMREAD_GRAYSCALE instead of CV_LOAD_IMAGE_GRAYSCALE , CHAIN_APPROX_SIMPLE instead of CV_CHAIN_APPROX_SIMPLE , etc...
  • Better don't put magic values in the code (such as the ROI values 300, 600).
  • you don't need to specify the parameters if you're using the default value.
  • names with Capital letters are usually for classes and structs names, not variables (this is somehow subjective, but personally I think it makes code easier to read).
  • skip computation if you don't have any contours detected.
  • if you're on VisualStudio, instead of using all these imshow for debugging purposes you can use Image Watch .
  • Comment the code!

Here the working code with these tips applied:

#include <opencv2/opencv.hpp>
#include <vector>
#include <string>

using namespace std;
using namespace cv;

int main()
{
    // Load images
    Mat1b originalImage = imread("path_to_original_image", IMREAD_GRAYSCALE);
    Mat1b backgroundImage = imread("path_to_bkg_image", IMREAD_GRAYSCALE);

    // Create binary mask
    Mat1b grayImage;
    absdiff(originalImage, backgroundImage, grayImage);
    Mat1b binary;
    threshold(grayImage, binary, 80, 255, THRESH_BINARY);

    imshow("OriginalImage", originalImage);
    imshow("BackgroundImage", backgroundImage);
    imshow("GrayImage", grayImage);
    imshow("Binary", binary);

    // Take a ROI
    Rect roi(binary.cols / 3, 0, (binary.cols * 2) / 3, binary.rows);
    Mat1b imageROI = binary(roi);

    imshow("ImageROI", imageROI);

    // Apply morphological dilate, 2 times
    Mat1b binaryMorph;
    Mat1b kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
    dilate(imageROI, binaryMorph, kernel, Point(-1, -1), 2);

    imshow("BinaryMorph", binaryMorph);

    // Find blob contours
    vector<vector<Point>> contours;
    double largest_area = 0.0;
    int largest_contour_index = 0;
    findContours(binaryMorph.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, roi.tl());

    if (!contours.empty())
    {
        // Find largest contour
        for (size_t i = 0; i < contours.size(); i++)
        {
            double a = contourArea(contours[i], false);
            if (a > largest_area)
            {
                largest_area = a;
                largest_contour_index = i;
            }
        }

        // Draw largest contors
        Mat3b contour(binary.rows, binary.cols, Vec3b(0, 0, 0));
        drawContours(contour, contours, largest_contour_index, Scalar(255, 255, 255), CV_FILLED);

        // Find convex hull of largest contour
        vector<Point>hull;
        convexHull(contours[largest_contour_index], hull, CV_CLOCKWISE, true);

        // Draw the convex hull
        vector<vector<Point>> tmp;
        tmp.push_back(hull);
        drawContours(contour, tmp, 0, Scalar(0, 0, 255), 3);

        imshow("Contour", contour);
    }

    waitKey(0);
    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM