Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenCV, how to use arrays of points for smoothing and sampling contours?

I have a problem to get my head around smoothing and sampling contours in OpenCV (C++ API). Lets say I have got sequence of points retrieved from cv::findContours (for instance applied on this this image:

enter image description here

Ultimately, I want

  1. To smooth a sequence of points using different kernels.
  2. To resize the sequence using different types of interpolations.

After smoothing, I hope to have a result like :

enter image description here

I also considered drawing my contour in a cv::Mat, filtering the Mat (using blur or morphological operations) and re-finding the contours, but is slow and suboptimal. So, ideally, I could do the job using exclusively the point sequence.

I read a few posts on it and naively thought that I could simply convert a std::vector(of cv::Point) to a cv::Mat and then OpenCV functions like blur/resize would do the job for me... but they did not.

Here is what I tried:

int main( int argc, char** argv ){

    cv::Mat conv,ori;
    ori=cv::imread(argv[1]);
    ori.copyTo(conv);
    cv::cvtColor(ori,ori,CV_BGR2GRAY);

    std::vector<std::vector<cv::Point> > contours;
    std::vector<cv::Vec4i > hierarchy;

    cv::findContours(ori, contours,hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);

    for(int k=0;k<100;k += 2){
        cv::Mat smoothCont;

        smoothCont = cv::Mat(contours[0]);
        std::cout<<smoothCont.rows<<"\t"<<smoothCont.cols<<std::endl;
        /* Try smoothing: no modification of the array*/
//        cv::GaussianBlur(smoothCont, smoothCont, cv::Size(k+1,1),k);
        /* Try sampling: "Assertion failed (func != 0) in resize"*/
//        cv::resize(smoothCont,smoothCont,cv::Size(0,0),1,1);
        std::vector<std::vector<cv::Point> > v(1);
        smoothCont.copyTo(v[0]);
        cv::drawContours(conv,v,0,cv::Scalar(255,0,0),2,CV_AA);
        std::cout<<k<<std::endl;
        cv::imshow("conv", conv);
        cv::waitKey();
    }
    return 1;
}

Could anyone explain how to do this ?

In addition, since I am likely to work with much smaller contours, I was wondering how this approach would deal with border effect (e.g. when smoothing, since contours are circular, the last elements of a sequence must be used to calculate the new value of the first elements...)

Thank you very much for your advices,

Edit:

I also tried cv::approxPolyDP() but, as you can see, it tends to preserve extremal points (which I want to remove):

Epsilon=0

enter image description here

Epsilon=6

enter image description here

Epsilon=12

enter image description here

Epsilon=24

enter image description here

Edit 2: As suggested by Ben, it seems that cv::GaussianBlur() is not supported but cv::blur() is. It looks very much closer to my expectation. Here are my results using it:

k=13

enter image description here

k=53

enter image description here

k=103

enter image description here

To get around the border effect, I did:

    cv::copyMakeBorder(smoothCont,smoothCont, (k-1)/2,(k-1)/2 ,0, 0, cv::BORDER_WRAP);
    cv::blur(smoothCont, result, cv::Size(1,k),cv::Point(-1,-1));
    result.rowRange(cv::Range((k-1)/2,1+result.rows-(k-1)/2)).copyTo(v[0]);

I am still looking for solutions to interpolate/sample my contour.

like image 875
Quentin Geissmann Avatar asked Aug 12 '12 20:08

Quentin Geissmann


2 Answers

How about approxPolyDP()?

It uses this algorithm to 'smooth' a contour (basically gettig rid of most of the contour's points and leave the ones that represent a good approximation of your contour)

like image 75
Ben Avatar answered Oct 20 '22 21:10

Ben


Another possibility is to use the algorithm openFrameworks uses:

https://github.com/openframeworks/openFrameworks/blob/master/libs/openFrameworks/graphics/ofPolyline.cpp#L416-459

It traverses the contour and essentially applies a low-pass filter using the points around it. Should do exactly what you want with low overhead and (there's no reason to do a big filter on an image that's essentially just a contour).

like image 37
Aaron Krajeski Avatar answered Oct 20 '22 21:10

Aaron Krajeski