2
votes

I'm new to OpenCV, and I want to work on object detection to help my FRC robotics team. I'm trying to use an HSV filter and HoughCircles to find a tennis ball in a webcam image and draw a circle around it. Here is my code:

Mat currentFrame = new Mat();
Mat hsv = new Mat();
Mat threshImage = new Mat();
Mat circles = new Mat();

while (true) {
    camera.read(currentFrame);

    Imgproc.resize(currentFrame, currentFrame, new Size(WIDTH, HEIGHT));
    Imgproc.cvtColor(currentFrame, hsv, Imgproc.COLOR_RGB2HSV);

    hsvWindow.showImage(hsv);

    Core.inRange(hsv, new Scalar(50, 100, 0), new Scalar(95, 255, 255), threshImage);

    threshWindow.showImage(threshImage);

    Imgproc.HoughCircles(threshImage, circles, Imgproc.CV_HOUGH_GRADIENT, 2, 100, 100, 100, 0, 500);
    for (int i = 0; i < circles.cols(); i++) {
        double[] vCircle = circles.get(0, i);

        Point pt = new Point(Math.round(vCircle[0]), Math.round(vCircle[1]));
        int radius = (int)Math.round(vCircle[2]);

        Core.circle(currentFrame, pt, radius, new Scalar(255, 0, 0), 2);
    }

    drawWindow.showImage(currentFrame);
}

The original image, the hsv image, and the filtered image are in this album: http://imgur.com/a/hO8vs

When I run HoughCircles with the parameters here, it finds circles on the piano bench and toy rabbit, but not the tennis ball, which appears as a big white circle.

1
Best of luck with your FRC competitions. But when you come across Icarus 2081, be nice :-D - Mark Miller

1 Answers

7
votes

I fixed it! After some fiddling with the parameters of HoughCircles and blurring and thresholding the binary image it was finding it reliably, but the circle was jittery and inconsistent. So, I replaced HoughCircles with findContours, cycled through the contours looking for the largest one, and used minEnclosingCircle. Here is the code now:

Mat currentFrame = new Mat(), hsv = new Mat(), threshImage = new Mat();
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();

while (true) {
    camera.read(currentFrame);

    Imgproc.resize(currentFrame, currentFrame, new Size(WIDTH, HEIGHT));
    Imgproc.cvtColor(currentFrame, hsv, Imgproc.COLOR_RGB2HSV);

    hsvWindow.showImage(hsv);

    Core.inRange(hsv, new Scalar(50, 100, 50), new Scalar(95, 255, 255), threshImage);
    Imgproc.blur(threshImage, threshImage, new Size(10, 10));
    Imgproc.threshold(threshImage, threshImage, 150, 255, Imgproc.THRESH_BINARY);

    threshWindow.showImage(threshImage);

    Imgproc.findContours(threshImage, contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
    double maxArea = 0;
    float[] radius = new float[1];
    Point center = new Point();
    for (int i = 0; i < contours.size(); i++) {
        MatOfPoint c = contours.get(i);
        if (Imgproc.contourArea(c) > maxArea) {
            MatOfPoint2f c2f = new MatOfPoint2f(c.toArray());
            Imgproc.minEnclosingCircle(c2f, center, radius);
        }
    }
    Core.circle(currentFrame, center, (int)radius[0], new Scalar(255, 0, 0), 2);

    drawWindow.showImage(currentFrame);
}

I know this might not be particularly helpful for people looking to use HoughCircles specifically, but it's a testament to the power of blurring binary images. If you're looking for a circle among many things, you look for contours and compare the contour area to the area of its enclosing circle.