Ask Your Question
2

Detect different shaped contours?

asked Nov 13 '15

215 gravatar image

updated Dec 30 '15

How do i use detect certain shaped contours found using findContours. I want to bound contours which are thin and long, leave everything else away.. but cannot seem to adjust the parameter correctly, or would it be better to use simple blob detector??.. I tried it, but it doesn't seem to work that well..

here is a sample image image description

I find conturs using findContours, and store them as vector<vector <points>> contour.

I then check the area of each contour, those below 1000 are filtered out, and not drawn in the output image.

So the one i am interested in is the one the red rect, and everything else is noise.

the contour has a distinct shape which is always long. and thin as seen in contour down below.

Preview: (hide)

Comments

please share your code and a sample image

sturkmen gravatar imagesturkmen (Nov 13 '15)edit

then from the bounding box that you are applying just filter the blobs that you want by filtering the height and width of the the bounding box.

theodore gravatar imagetheodore (Nov 13 '15)edit

1 answer

Sort by » oldest newest most voted
3

answered Nov 13 '15

updated Nov 14 '15

EDIT 2 : you can change width&height ratio if(dist0 > dist1 *4) and angle if( fabs(angle) > 35 & fabs(angle) < 150 )

input & output Image:

image description image description

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

using namespace cv;
using namespace std;

//! Compute the distance between two points
/*! Compute the Euclidean distance between two points
*
* @Param a Point a
* @Param b Point b
*/
static double distanceBtwPoints(const cv::Point2f &a, const cv::Point2f &b)
{
    double xDiff = a.x - b.x;
    double yDiff = a.y - b.y;

    return std::sqrt((xDiff * xDiff) + (yDiff * yDiff));
}

int main( int argc, char** argv )
{
    Mat src,gray;
    src = imread(argv[1]);
    if(src.empty())
        return -1;

    cvtColor( src, gray, COLOR_BGR2GRAY );
    gray = gray < 200;

    vector<vector<Point> > contours;

    findContours(gray.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    RotatedRect _minAreaRect;

    for (size_t i = 0; i < contours.size(); ++i)
    {
        _minAreaRect = minAreaRect( Mat(contours[i]) );
        Point2f pts[4];
        _minAreaRect.points(pts);

        double dist0 = distanceBtwPoints(pts[0], pts[1]);
        double dist1 = distanceBtwPoints(pts[1], pts[2]);

        double angle = 0;
        if(dist0 > dist1 *4)
            angle =atan2(pts[0].y - pts[1].y,pts[0].x - pts[1].x) * 180.0 / CV_PI;
        if(dist1 > dist0 *4)
            angle =atan2(pts[1].y - pts[2].y,pts[1].x - pts[2].x) * 180.0 / CV_PI;

        if( fabs(angle) > 35 & fabs(angle) < 150 )
            for( int j = 0; j < 4; j++ )
                line(src, pts[j], pts[(j+1)%4], Scalar(0, 0, 255), 1, LINE_AA);
    }
    imshow("result", src);
    waitKey(0);
    return 0;
}

EDIT 1: you can improve the code below by changing

 if( _minAreaRect.angle < -30 & (dist0 > dist1 *4 | dist1 > dist0 *4) )

here you can change width&height ratio and angle of RotatedRect ( need your care )

input & output Image:

image description image description

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

using namespace cv;
using namespace std;

//! Compute the distance between two points
/*! Compute the Euclidean distance between two points
*
* @Param a Point a
* @Param b Point b
*/
static double distanceBtwPoints(const cv::Point2f &a, const cv::Point2f &b)
{
    double xDiff = a.x - b.x;
    double yDiff = a.y - b.y;

    return std::sqrt((xDiff * xDiff) + (yDiff * yDiff));
}

int main( int argc, char** argv )
{
    Mat src,gray;
    src = imread(argv[1]);
    if(src.empty())
        return -1;

    cvtColor( src, gray, COLOR_BGR2GRAY );
    gray = gray < 200;

    vector<vector<Point> > contours;

    findContours(gray.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    RotatedRect _minAreaRect;

    for (size_t i = 0; i < contours.size(); ++i)
    {
        _minAreaRect = minAreaRect( Mat(contours[i]) );
        Point2f pts[4];
        _minAreaRect.points(pts);

        double dist0 = distanceBtwPoints(pts[0], pts[1]);
        double dist1 = distanceBtwPoints(pts[1], pts[2]);

        if( _minAreaRect.angle < -30 & (dist0 > dist1 *4 | dist1 > dist0 *4) )
            for( int j = 0; j < 4; j++ )
                line(src, pts[j], pts[(j+1)%4], Scalar(0, 0, 255), 1, LINE_AA);
    }
    imshow("result", src);
    waitKey(0);
    return 0;
}

you can filter contours by using height and width of bounding rectangels .

like if ( minRect.height > minRect.width*4 ) as shown with the code below:

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

using namespace cv;
using namespace std;

int main( int argc, char** argv )
{
    Mat src,gray;
    src = imread(argv ...
(more)
Preview: (hide)

Comments

I know that i've only provided one case, but this shape wouldn't be detected if it was rotated 45 degrees as the bounding box just would be a square.. and thereby not a solid solution..

215 gravatar image215 (Nov 14 '15)edit

You can use moment and contour.length()

LBerger gravatar imageLBerger (Nov 14 '15)edit

as an extra check, or as a substitute for the one provided?

215 gravatar image215 (Nov 14 '15)edit

After findContour you can calculate moment and contour length. After you can calculate ratio surface/length or major axis/ minor axis

LBerger gravatar imageLBerger (Nov 14 '15)edit

Does the function know that the shape exist?.. I mean wouldn't be able to change it to an boolean function..

215 gravatar image215 (Nov 15 '15)edit

you can modify as you need

sturkmen gravatar imagesturkmen (Nov 15 '15)edit

Question Tools

2 followers

Stats

Asked: Nov 13 '15

Seen: 2,850 times

Last updated: Nov 14 '15