1
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.

Miki
  • 40,887
  • 13
  • 123
  • 202

1 Answers1

5

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

drawContours accepts as input points a InputArrayOfArrays, i.e. 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, i.e. 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 Mats, 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, i.e. 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;
}
Community
  • 1
  • 1
Miki
  • 40,887
  • 13
  • 123
  • 202
  • Thank you very much for all the helpful tips! I appreciate your time to modify my code. However, the problem is not solved, as , I am getting compilation error in the following line: drawContours(contour, vector> {hull} , 0....... it is underlined in red and says, type name not allowed here. Please help me. – Sruti Das Choudhury Apr 18 '16 at 13:55
  • you probably don't have C++11 enabled. Just do: `vector> tmp; tmp.push_back(hull); drawContours(contour, tmp, 0, Scalar(128), 3);` – Miki Apr 18 '16 at 13:58
  • Hello, I made the suggested change. The compilation error is gone, but the convexhull is not displayed as before. Also, I would like to display the contour using white color with convexhull in red color, while the background is black. Please help me. I am stuck here, for the convexhull is not displayed. – Sruti Das Choudhury Apr 18 '16 at 14:09
  • I spotted a typo, not it should work ok and display a red hull. Note that I changed also the ROI, so you may want to use your predefined values – Miki Apr 18 '16 at 14:21
  • Thank you so much!! The convex hull is displaying perfectly. Now, I seek your advice: is there any way to use the purpose of WINDOW_NORMAL in imshow? the image of the plant (object) is out of window, so to visualise it, I needed to use namedWindow("contour", WINOW_NORMAL), which I would like to drop out according to your advice. – Sruti Das Choudhury Apr 18 '16 at 14:50
  • WINDOW_NORMAL is not the default, which it's WINDOW_AUTOSIZE. So if you need to be able resize your images, you need to use `namedWindow`. I must admit that I was sure that it was the other way around ;D. However, in general, it can soon become impracticable to use so many displayed images for debugging... That's way I never use them, and was confused on the names... I'll update that in the answer – Miki Apr 18 '16 at 14:56
  • I would be very grateful to you if you could please tell me if there is any method similar to cvResetImageROI in C++. Because, I would like to display the contour with convex hull using window with the same size as the original image and at the same position as it was in the original image. – Sruti Das Choudhury Apr 19 '16 at 04:52
  • Check now. Easiest thing is to directly get the contour points shifted by the `roi.tl()`, so that their coordinates are relative to the original image. Or you can shift later all the points in the contour by the required offset – Miki Apr 19 '16 at 10:44