Bartrae
Bartrae

Reputation: 37

Houghcircles detection certainty

Recently I made my first vision application. My code can recognise marbles of a certain colour and gives me the X,Y,Z coordinates of this marble. To debug and setup my system I made some code with which I can easily tweak and try settings. This code tries to detect all marbles within an image and tells me where it thinks marbles are by marking it with a green dot.

Basically my code works like this: My camera takes a picture.

It looks for colours within a certain range and makes a mask out of this (within range is white, out of range is black) like this: image mask.

I then look for circles within this image using the houghcircles command.

I extract the centre point from each detected circle and put it over the original image, like this: (green dot = centre circle) output image

There are still some problem with my detection but for the moment I am quite satisfied.

Now, the thing I would like to know is this: Is it possible to put a percentage next to each marked centre point, telling me how sure the program is that it is a circle.

If you have any other suggestions or questions feel free to ask. I've put my code below:

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <librealsense2/rs.hpp>


using namespace std;
using namespace cv;


Mat image;
Mat imgHSV;
Mat OutputImage;
Mat testframe;


int iLowH = 104;
int iHighH = 111;
int iLowS = 109;
int iHighS = 155;
int iLowV = 120;
int iHighV = 255;


int acc = 1;
int rows = 10;
int para1 = 100;
int para2 = 7;
int minRad = 3;
int maxRad = 14;


static void HSVthreshold(int, int, int, int, int, int, void*)
{
    inRange(imgHSV, Scalar(iLowH, iLowS, iLowV), Scalar(iHighH, iHighS, iHighV), OutputImage);
}

static void Circle_detector(int, int, int, int, int, void*)
{
    vector<Vec3f> circles;
    HoughCircles(OutputImage, circles, HOUGH_GRADIENT, 1,
        OutputImage.rows / rows,      //change to detect circles that are closer to eachother
        para1, para2, minRad, maxRad);        //chang last to parameters to detect larger or smaller circles

    for (size_t i = 0; i < circles.size(); i++)
    {
        Vec3i c = circles[i];
        Point center = Point(c[0], c[1]);
        // circle center
        circle(testframe, center, 1, Scalar(0, 255, 0), 2, LINE_AA);
        // circle outline
        int radius = c[2];
        circle(imgHSV, center, radius, Scalar(255, 0, 0), 2, LINE_AA);
    }
}


int main()
{


    // Contructing piplines and other stuff to receive data from the realsense camera.

    //Contruct a pipeline which abstracts the device
    rs2::pipeline pipe;

    //Create a configuration for configuring the pipeline with a non default profile
    rs2::config cfg;

    //Add desired streams to configuration
    cfg.enable_stream(RS2_STREAM_COLOR, 640, 480, RS2_FORMAT_BGR8, 30);

    //Instruct pipeline to start streaming with the requested configuration
    pipe.start(cfg);

    // Camera warmup - dropping several first frames to let auto-exposure stabilize
    rs2::frameset frames;
    for (int i = 0; i < 30; i++)
    {
        //Wait for all configured streams to produce a frame
        frames = pipe.wait_for_frames();
    }
    while (waitKey(1) < 0)
    {
        frames = pipe.wait_for_frames();
        //Get each frame
        rs2::frame color_frame = frames.get_color_frame();






        // Creating OpenCV Matrix from a color image
        Mat color(Size(640, 480), CV_8UC3, (void*)color_frame.get_data(), Mat::AUTO_STEP);

        // Display in a GUI
        if (color.empty())
        {
            cerr << "image was not generated !" << endl;
            return 1;
        }

        testframe = color;

        namedWindow("Display Image", WINDOW_AUTOSIZE);
        imshow("Display Image", color);


        //convert RGB to HSV
        cvtColor(color, imgHSV, COLOR_BGR2HSV);


        //Create windows
        namedWindow("image", WINDOW_AUTOSIZE); //window for original image
        namedWindow("Control", WINDOW_AUTOSIZE); //window for HSV-control sliders
        namedWindow("Output", WINDOW_AUTOSIZE); //window for output mask
        namedWindow("Control HoughCircles", WINDOW_AUTOSIZE); //window for HoughCircle sliders

        namedWindow("Test-window", WINDOW_AUTOSIZE);

        //Create trackbars in "Control HSV" window
        createTrackbar("LowH", "Control", &iLowH, 179); //Hue (0 - 179)
        createTrackbar("HighH", "Control", &iHighH, 179);

        createTrackbar("LowS", "Control", &iLowS, 255); //Saturation (0 - 255)
        createTrackbar("HighS", "Control", &iHighS, 255);

        createTrackbar("LowV", "Control", &iLowV, 255); //Value (0 - 255)
        createTrackbar("HighV", "Control", &iHighV, 255);

        int key = 0;
        while (key != 27) { // 27 is escape
            HSVthreshold(iLowH, iHighH, iLowS, iHighS, iLowV, iHighV, 0);
            imshow("Output", OutputImage);
            imshow("image", imgHSV);
            key = waitKey(1); // wait at most 1 ms for input, if nothing was pressed result is -1
        }


        //Optional filter --> does not work properly at the moment <--

        //morphological opening (remove small objects from the foreground)
        erode(OutputImage, OutputImage, getStructuringElement(MORPH_ELLIPSE, Size(1, 1)));
        dilate(OutputImage, OutputImage, getStructuringElement(MORPH_ELLIPSE, Size(1, 1)));


        //morphological closing (fill small holes in the foreground)
        dilate(OutputImage, OutputImage, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
        erode(OutputImage, OutputImage, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));


        imshow("Output", OutputImage);
        waitKey();


        //Create trackbars in "Control HoughCircles" window
        createTrackbar("Distance between detections", "Control HoughCircles", &rows, 50); //detection distance (0 - 50)


        createTrackbar("Upper threshold for internal canny edge", "Control HoughCircles", &para1, 100); //upper threshold for internal canny edge detector (0 - 100)
        createTrackbar("threshold for internal canny edge", "Control HoughCircles", &para2, 50); //threshold for internal canny edge detector (0 - 50)

        createTrackbar("Min radius", "Control HoughCircles", &minRad, 200); //minimum circle radius (0 - 200)
        createTrackbar("Max radiu", "Control HoughCircles", &maxRad, 200); // maximum circle radius (0 - 200)


        int key2 = 0;
        while (key2 != 27) { // 27 is escape
            Circle_detector(rows, para1, para2, minRad, maxRad, 0);
            imshow("image", imgHSV);
            imshow("Test-window", testframe);
            key2 = waitKey(1); // wait at most 1 ms for input, if nothing was pressed result is -1
        }

        waitKey();
    }
        return 0;
    }

EDIT: I've added some new pictures of my testing material, sadly this is not completely equal to the situation above due to light conditions.

Original image: Original image

Black and white image mask: Image mask

Detection window:Detection window

Upvotes: 1

Views: 167

Answers (1)

Yunus Temurlenk
Yunus Temurlenk

Reputation: 4352

I tried the picture you added. I didn't use something differently comparing to your code. I focused to check the pixels inside of each circle. Here are my steps:

  • Convert image to HSV
  • Find appropriate HSV values for marbles and apply it
  • Use medianBlur to decrease noise
  • Apply HoughCircles to binary image
  • According to output of HoughCircles crop the each circle by a rectangle
  • Check that small cropped image's each pixel and decide it is inside the circle or not( I used radius and center here. If the length of each pixel to center is longer than radius, the pixel is out of circle else inside the circle)
  • Lastly, check the pixel and count desired hsv values

Here is my code and results(Results are no different with yours, the important part is checking inside the circle):

Code:

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <librealsense2/rs.hpp>

using namespace std;
using namespace cv;

int main()
{

    Mat color = imread("/ur/image/directory/marble.png",1);
    Mat hsv;
    cvtColor(color, hsv, COLOR_BGR2HSV);

    Mat board = Mat::zeros(hsv.rows,hsv.cols,CV_8UC1);

    for(int i=0; i<hsv.rows; i++)
    {
        for(int j=0; j<hsv.cols; j++)
        {
            if(hsv.at<Vec3b>(Point(j,i))[2]<60 && hsv.at<Vec3b>(Point(j,i))[2]>20 && hsv.at<Vec3b>(Point(j,i))[1]<120 && hsv.at<Vec3b>(Point(j,i))[1]>50
                    && hsv.at<Vec3b>(Point(j,i))[0]<105 && hsv.at<Vec3b>(Point(j,i))[0]>85)
                board.at<uchar>(Point(j,i)) = 254;
        }
    }

    medianBlur(board,board,3);            
    vector<Vec3f> circles;
    HoughCircles(board, circles, HOUGH_GRADIENT, 1,
                 board.rows / 10,      //change to detect circles that are closer to eachother
                 100, 7, 3, 14);        //chang last to parameters to detect larger or smaller circles

    for (size_t i = 0; i < circles.size(); i++)
    {
        Vec3i cc = circles[i];
        Point center = Point(cc[0], cc[1]);
        // circle center
        circle(color, center, 1, Scalar(0, 255, 0), 2, LINE_AA);
        // circle outline
        int radius = cc[2];
        circle(color, center, radius, Scalar(255, 0, 0), 2, LINE_AA);

        // Firstly, Crop that region
        Rect crop(center.x-radius-5, center.y-radius-5,2*radius+10,2*radius+10);
        Mat crop_for_test = hsv(crop);

        //Secondly, check each pixel inside the circle or not
        for(int r=0; r<crop_for_test.rows; r++)
        {
            for(int c=0; c<crop_for_test.cols; c++)
            {
                double length_to_center = norm(Point(cc[0]-(center.x-radius-5), cc[1]-(center.y-radius-5))-Point(r,c));

                if(length_to_center<radius)
                {
                    // Here all points inside the circle
                    cout<<"H value:  "<<to_string(crop_for_test.at<Vec3b>(Point(r,c))[2])<<"   "<<"S value:  "
                        <<to_string(crop_for_test.at<Vec3b>(Point(r,c))[1])<<"   "<<"V value:  "
                        <<to_string(crop_for_test.at<Vec3b>(Point(r,c))[0])<<endl;




                }
            }
        }
    }


    imshow("board",board);

    imshow("hsv",hsv);
    imshow("rgb",color);

    waitKey(0);

    return 0;
}

HSV Input:

enter image description here

Binary after filter:

enter image description here

Output:

enter image description here

Upvotes: 1

Related Questions