Ask Your Question
0

regionprops vs. findContours

asked 2015-03-26 20:37:41 -0600

Gilad Darmon gravatar image

updated 2015-03-27 15:55:03 -0600

theodore gravatar image

Is there a way to get the same results for cDist=regionprops(bwImg, 'Area'); and openCV's findContours?

Here is what I have tried so far:

dst.convertTo(dst,CV_8U);
cv::vector<cv::vector<cv::Point> > contours_1;
cv::vector<cv::Vec4i> hierarchy_1;
cv::findContours(dst,contours_1,hierarchy_1,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);

double maxLabelSize = (dst.rows/4.0) * (dst.cols/6.0);
double minLabelSize = ((dst.rows/40.0) * (dst.cols/60.0));

cv::vector<cv::vector<cv::Point> > goodContours;
for (int i = 0; i < contours_1.size(); i++)
{
    double size = cv::contourArea(contours_1[i]);
    if (size < maxLabelSize && size > minLabelSize)
    {
        goodContours.push_back(contours_1[i]);
    }
}

cv::Mat filterContours = cv::Mat::zeros(dst.size(),CV_8UC3);    
for (int i = 0; i < goodContours.size(); i++)
{
    cv::RNG rng(12345);
    cv::Scalar color = cv::Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
    drawContours( filterContours, goodContours, i, color, 2, 8, hierarchy_1, 0, cv::Point() );
}

cv::imshow( "Contours", filterContours );
cv::waitKey(0);

the source image was created using average smoothing and

cv::Mat utils::im2bw(cv::Mat src, double grayThresh)
{
    cv::Mat dst;
    cv::threshold(src, dst, grayThresh, 1, CV_THRESH_BINARY);
    return dst; 
}

original image image description

OpenCV's result

Matlab's Version:

% Calculate each separated object area
    cDist=regionprops(bwImg, 'Area');
    cDist=[cDist.Area];

    % Label each object
    [bwImgLabeled, ~]=bwlabel(bwImg);

    % Calculate min and max object size based on assumptions
    maxLabelSize = prod(size(imageData)./[4 6]);
    minLabelSize = prod(size(imageData)./[4 6]./10);

    % Find label indices for objects that are too large or too small
    remInd = find(cDist > maxLabelSize);
    remInd = [remInd find(cDist < minLabelSize)];

    % Remove over/undersized objects
    for n=1:length(remInd)
        ri = bwImgLabeled == remInd(n);
        bwImgLabeled(ri) = 0;
    end

image description

Matlab's result

Please note the left bottom square is missing from the openCV image. should I do Canny edge detection before findContours?

edit retag flag offensive close merge delete

Comments

your issue seems not to be the findContours() functionality but the pre-processing that you are applying before. If you could provide the source image and some more info/code about the way that you obtain the dst image that you pass into the findContours() function I am sure that we could provide some help ;-)

theodore gravatar imagetheodore ( 2015-03-26 20:45:44 -0600 )edit

well the image is a little too big to put it in here. but i can share the code.

Gilad Darmon gravatar imageGilad Darmon ( 2015-03-26 20:52:32 -0600 )edit

you can resize it and just upload a smaller version of it ;-), also by source I was meaning the original input image before any thresholding/binarization. Moreover, it would help to tell us what you want to extract. By the images I suppose the squares on the image, is that right please correct if I am wrong.

theodore gravatar imagetheodore ( 2015-03-26 20:55:41 -0600 )edit

I have uploaded the source images, which are both black and white images, and the function which creates the black and white image. I did some some smoothing filtering, but as you can see the images are identical.

Gilad Darmon gravatar imageGilad Darmon ( 2015-03-26 20:58:36 -0600 )edit

Can it be that because i'm doing dst.convertTo(dst,CV_8U); i'm loosing some information ? what do i need to do ?

Gilad Darmon gravatar imageGilad Darmon ( 2015-03-26 21:08:22 -0600 )edit

@Gilad Darmon please read again my previous comment.

theodore gravatar imagetheodore ( 2015-03-26 21:16:14 -0600 )edit

Yes correct i want to extract the squares, uploaded the original image

Gilad Darmon gravatar imageGilad Darmon ( 2015-03-27 03:00:44 -0600 )edit

1 answer

Sort by ยป oldest newest most voted
3

answered 2015-03-27 15:49:59 -0600

theodore gravatar image

@Gilad Darmon since you want to detect the squares you can use some shape segmentation technique as it is described in the examples here and here. You can also combine these algorithms since they differ a bit in specific parts of the source code. Anyway, by just trying some modifications and some basic steps I got the following result:

image description

image description

I guess this is what you want. For the code check below. By the way I guess you are aware that your original image is distorted and as a consequence that affects the result. If you undistort your image most likely you will obtain better shapes on your detected squares. Plus there is this dirt on the window causing some problems and destroying the shape of one of the squares which as you can notice is not detected.

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

// helper function:
// finds a cosine of angle between vectors
// from pt0->pt1 and from pt0->pt2
static double angle( Point pt1, Point pt2, Point pt0 )
{
    double dx1 = pt1.x - pt0.x;
    double dy1 = pt1.y - pt0.y;
    double dx2 = pt2.x - pt0.x;
    double dy2 = pt2.y - pt0.y;
    return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}

// returns sequence of squares detected on the image.
// the sequence is stored in the specified memory storage
static void findSquares( const Mat& image, vector<vector<Point> >& squares )
{
    squares.clear();

    Mat pyr, timg, gray0(image.size(), CV_8U), gray;

    // down-scale and upscale the image to filter out the noise
    pyrDown(image, pyr, Size(image.cols/2, image.rows/2));
    pyrUp(pyr, timg, image.size());
    vector<vector<Point> > contours;

    // find squares in every color plane of the image
    for( int c = 0; c < 3; c++ )
    {
        int ch[] = {c, 0};
        mixChannels(&timg, 1, &gray0, 1, ch, 1);

        // try several threshold levels
        int N = 5;
        for( int l = 0; l < N; l++ )
        {
            // hack: use Canny instead of zero threshold level.
            // Canny helps to catch squares with gradient shading
            if( l == 0 )
            {
                // apply Canny. Take the upper threshold from slider
                // and set the lower to 0 (which forces edges merging)
                Canny(gray0, gray, 0, 50, 5);
                // dilate canny output to remove potential
                // holes between edge segments
                dilate(gray, gray, Mat(), Point(-1,-1));
            }
            else
            {
                // apply threshold if l!=0:
                //     tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
                gray = gray0 >= (l+1)*255/N;
            }

            // find contours and store them all as a list
            findContours(gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);

            vector<Point> approx;

            // test each contour
            for( size_t i = 0; i < contours.size(); i++ )
            {
                // approximate contour with accuracy proportional
                // to the contour perimeter
                approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);

                // square contours should have 4 vertices after approximation
                // relatively large area (to filter out noisy contours)
                // and be convex.
                // Note: absolute value of an area is used because
                // area may be positive or negative - in accordance with the
                // contour orientation
                if( approx.size() == 4 &&
                    fabs(contourArea ...
(more)
edit flag offensive delete link more

Comments

Cool, looks like a great solution for my problem. I will try it with the rest of my algorithm. Thanks, Is there a way no to create all of the rectangles? just the best fitted ones to the square? i mean the green ones you added?

Gilad Darmon gravatar imageGilad Darmon ( 2015-03-27 16:29:47 -0600 )edit
1

if you read the links that I have added probably you will find a way to achieve that. My guess would be to play with the maxCosine variable and increase/decrease the acceptance threshold.

theodore gravatar imagetheodore ( 2015-03-27 16:51:02 -0600 )edit

Question Tools

2 followers

Stats

Asked: 2015-03-26 20:37:41 -0600

Seen: 10,425 times

Last updated: Mar 27 '15