12
votes

As some kind of "holiday project" I'm playing around with OpenCV and want to detect and measure stuff.

Current workflow (early stage - detection):

  1. Convert to grayscale (cv::cvtColor)
  2. Apply Adaptive threshold (cv::adaptiveThreshold)
  3. Apply canny edge detection (cv::Canny)
  4. Finding contours (cv::findContours)

My outcome is kinda crappy and I'm not sure what's the right direction to go. I already got cvBlob working under my current setup (OSX 10.7.2, Xcode 4.2.1) is it a better way to go? If so, how can I implement it the right way?

Or do I need background subtraction first? I tried that but wasn't able to find contours afterwards

Here's my image: image to measure

And that's my output, when I draw my contours back into the first image: output

UPDATE

I got it working in my programm and my output looks a bit different …

- (IBAction)processImage:(id)sender
{
    cv::Mat forground = [[_inputView image] CVMat];
    cv::Mat result = [self isolateBackground:forground];
    [_outputView setImage:[NSImage imageWithCVMat:result]];
}

- (cv::Mat)isolateBackground:(cv::Mat &)_image
{
    int rh = 255, rl = 100, gh = 255, gl = 0, bh = 70, bl = 0;
    cv::cvtColor(_image, _image, CV_RGB2HSV_FULL);
    cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
    cv::Mat bgIsolation;
    cv::inRange(_image, cv::Scalar(bl, gl, rl), cv::Scalar(bh, gh, rh), bgIsolation);
    bitwise_not(bgIsolation, bgIsolation);
    erode(bgIsolation, bgIsolation, cv::Mat());
    dilate(bgIsolation, bgIsolation, element);
    return bgIsolation;
}
1

1 Answers

10
votes

This might be kind of a hack, but since it's a "holiday project", I'll post it anyway :)

Have you tried isolating the background and then inverting the mask (this would assume anything not background is an object, but it might work for what you want).

Below is the result I got using the OpenCV inRange function: enter image description here

You might want to smooth the image (pre-process) with GuassianBlur to get rid of some of the jagginess. I used a bigger dilation kernel than erosion kernel (5x5 vs. 3x3) to get rid of some noisy pixels. The smoothing might help this also tweaking the thresholds could make the erosion unnecessary. But, that should get you started.

Finally, here is my quick little code snippet I used to find this range:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <vector>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
    Mat src = imread("test.jpg");


    int rh = 255, rl = 100, gh = 255, gl = 0, bh = 70, bl = 0;

    string windowName = "background";
    namedWindow(windowName);

    createTrackbar("rh", windowName, &rh, 255);
    createTrackbar("rl", windowName, &rl, 255);
    createTrackbar("gh", windowName, &gh, 255);
    createTrackbar("gl", windowName, &gl, 255);
    createTrackbar("bh", windowName, &bh, 255);
    createTrackbar("bl", windowName, &bl, 255);

    // for dilation
    Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));

    Mat bgIsolation;
    int key = 0;
    do
    {
        inRange(src, Scalar(bl, gl, rl), Scalar(bh, gh, rh), bgIsolation);

        bitwise_not(bgIsolation, bgIsolation);

        erode(bgIsolation, bgIsolation, Mat());
        dilate(bgIsolation, bgIsolation, element);

        imshow(windowName, bgIsolation);
        key = waitKey(33);
    } while((char)key != 27);

    waitKey();

    return 0;
}

Enjoy the holiday project! Looks fun :)