Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

You are mixing contours and circles from HoughCircles in a obscure way. Detected circles OR contours and select your favourite.

  • If you want to filter circles by area you can get it from radius or not ?
  • If you want to filter contours by circularity calculate it as circularity = 4 * PI * area / perimeter^2
  • use findContours with CV_RETR_EXTERNAL and forget forget about hierarchy
  • you are looking for largest area. Maybe it isn't the ball. Sure your control var objectFound id bad managed because it can become false after it has been true.

here is a ready to use function for contour circularity

/**
* @brief Calculates circularity measure of a contour using common standard method
* The circularity is calculated with common method as relation between area and perimeter as:
*
* Circularity = (4 * PI * area) / (perimeter^2)
*
* @param [in]contour a vector of cv::Point
* @returns ranges over [0, 1] where 1 means that the contour is a perfect circle
* if area or perimeter is 0 than circularity will be 0
*
* @note this returns 0.785 for squares
*/
double CircularityStandard(double area,double perimeter)
{
    if((area*perimeter)==0) return 0;
    return (4 * CV_PI*area) / (perimeter*perimeter);
}
double CircularityStandard(const vector<cv::Point> &contour)
{
    double area = cv::contourArea(contour);
    if (area == 0) return 0.0;
    double perimeter = cv::arcLength(contour, true);
    if (perimeter == 0) return 0.0;
    return (4 * CV_PI*area) / (perimeter*perimeter);
}

here is a code I could write

void trackFilteredObject(int &x, int &y, const Mat &threshold, Mat &cameraFeed)
{
    vector< vector<Point> > contours;
    vector<Vec4i> hierarchy;

    // find external contours ignores holes 
    cv::findContours(threshold, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    //if number of objects greater than MAX_NUM_OBJECTS we have a noisy filter
    if (contours.size() > MAX_NUM_OBJECTS)
    {
        putText(cameraFeed, "TOO MUCH NOISE! ADJUST FILTER", Point(0, 50), 1, 2, Scalar(0, 0, 255), 2);
        return;
    }

    double refArea = 0;
    bool objectFound = false;
    double circularity;
    int largest = -1;
    double minCircularity = 0.9; //use something > 0.85
    for (int i = 0; i < contours.size(); i++)
    {
        // draw all contours in red
        cv::drawContours(dst, contours, i, cv::Scalar(0, 0, 255), 1);

        cv::Moments moment = moments(contours[i]);
        double area = moment.m00;
        double perimeter = cv::arcLength(contour[i], true);
        //circularity = CircularityStandard(contours[i]); //alternative overload
        circularity = CircularityStandard(area,perimeter);
        if (circularity < minCircularity) //use something > 0.85
            continue;

        // draw all circles in blue
        int cx = cvRound(moment.m10 / area);
        int cy = cvRound(moment.m01 / area);
        cv::Point center(x, y);
        int radius = cvRound(sqrt(refArea / CV_PI));
        // circle center
        circle(cameraFeed, center, 3, Scalar(0, 255, 0), -1, 8, 0);
        // circle outline
        circle(cameraFeed, center, radius, Scalar(255, 0, 0), 3, 8, 0);

        //if the area is less than 20 px by 20px then it is probably just noise
        //if the area is the same as the 3/2 of the image size, probably just a bad filter
        //object wanted with the largest area so a reference area is stored each
        //iteration and compare it to the area in the next iteration.        
        if (area > refArea && area > MIN_OBJECT_AREA && area < MAX_OBJECT_AREA)
        {
            objectFound = true;
            x = cx;
            y = cy;
            refArea = area;
        }
    }
    //let user know you found an object
    if (objectFound == true)
    {
        putText(cameraFeed, "Tracking Object", Point(0, 50), 2, 1, Scalar(0, 255, 0), 2);
        //draw object location on screen
        drawObject(x, y, cameraFeed);
    }
}

Or you could use BlobDetector (as answerd here)

You are mixing contours and circles from HoughCircles in a obscure way. Detected circles OR contours and select your favourite.

  • If you want to filter circles by area you can get it from radius or not ?
  • If you want to filter contours by circularity calculate it as circularity = 4 * PI * area / perimeter^2
  • use findContours with CV_RETR_EXTERNAL and forget forget about hierarchy
  • you are looking for largest area. Maybe it isn't the ball. Sure your control var objectFound id bad managed because it can become false after it has been true.

here is a ready to use function for contour circularity

/**
* @brief Calculates circularity measure of a contour using common standard method
* The circularity is calculated with common method as relation between area and perimeter as:
*
* Circularity = (4 * PI * area) / (perimeter^2)
*
* @param [in]contour a vector of cv::Point
* @returns ranges over [0, 1] where 1 means that the contour is a perfect circle
* if area or perimeter is 0 <=0 than circularity will be 0
*
* @note this returns 0.785 for squares
*/
double CircularityStandard(double area,double perimeter)
{
    if((area*perimeter)==0) if( (area<=0) || (perimeter<=0)) return 0;
    return (4 * CV_PI*area) / (perimeter*perimeter);
}
double CircularityStandard(const vector<cv::Point> &contour)
{
    double area = cv::contourArea(contour);
    if (area == 0) return 0.0;
    double perimeter = cv::arcLength(contour, true);
    if (perimeter == 0) return 0.0;
    return (4 * CV_PI*area) / (perimeter*perimeter);
}

here is a code I could write

void trackFilteredObject(int &x, int &y, const Mat &threshold, Mat &cameraFeed)
{
    vector< vector<Point> > contours;
    vector<Vec4i> hierarchy;

    // find external contours ignores holes 
    cv::findContours(threshold, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    //if number of objects greater than MAX_NUM_OBJECTS we have a noisy filter
    if (contours.size() > MAX_NUM_OBJECTS)
    {
        putText(cameraFeed, "TOO MUCH NOISE! ADJUST FILTER", Point(0, 50), 1, 2, Scalar(0, 0, 255), 2);
        return;
    }

    double refArea = 0;
    bool objectFound = false;
    double circularity;
    int largest = -1;
    double minCircularity = 0.9; //use something > 0.85
    for (int i = 0; i < contours.size(); i++)
    {
        // draw all contours in red
        cv::drawContours(dst, contours, i, cv::Scalar(0, 0, 255), 1);

        cv::Moments moment = moments(contours[i]);
        double area = moment.m00;
        double perimeter = cv::arcLength(contour[i], true);
        //circularity = CircularityStandard(contours[i]); //alternative overload
        circularity = CircularityStandard(area,perimeter);
        if (circularity < minCircularity) //use something > 0.85
            continue;

        // draw all circles in blue
        int cx = cvRound(moment.m10 / area);
        int cy = cvRound(moment.m01 / area);
        cv::Point center(x, y);
        int radius = cvRound(sqrt(refArea / CV_PI));
        // circle center
        circle(cameraFeed, center, 3, Scalar(0, 255, 0), -1, 8, 0);
        // circle outline
        circle(cameraFeed, center, radius, Scalar(255, 0, 0), 3, 8, 0);

        //if the area is less than 20 px by 20px then it is probably just noise
        //if the area is the same as the 3/2 of the image size, probably just a bad filter
        //object wanted with the largest area so a reference area is stored each
        //iteration and compare it to the area in the next iteration.        
        if (area > refArea && area > MIN_OBJECT_AREA && area < MAX_OBJECT_AREA)
        {
            objectFound = true;
            x = cx;
            y = cy;
            refArea = area;
        }
    }
    //let user know you found an object
    if (objectFound == true)
    {
        putText(cameraFeed, "Tracking Object", Point(0, 50), 2, 1, Scalar(0, 255, 0), 2);
        //draw object location on screen
        drawObject(x, y, cameraFeed);
    }
}

Or you could use BlobDetector (as answerd here)

You are mixing contours and circles from HoughCircles in a obscure way. Detected circles OR contours and select your favourite.

  • If you want to filter circles by area you can get it from radius or not ?
  • If you want to filter contours by circularity calculate it as circularity = 4 * PI * area / perimeter^2
  • use findContours with CV_RETR_EXTERNAL and forget forget about hierarchy
  • you are looking for largest area. Maybe it isn't the ball. Sure your control var objectFound id bad managed because it can become false after it has been true.

here is a ready to use function for contour circularity

/**
* @brief Calculates circularity measure of a contour using common standard method
* The circularity is calculated with common method as relation between area and perimeter as:
*
* Circularity = (4 * PI * area) / (perimeter^2)
*
* @returns ranges over [0, 1] where 1 means that the contour is a perfect circle
* if area or perimeter is <=0 than circularity will be 0
*
* @note this returns 0.785 for squares
*/
double CircularityStandard(double area,double perimeter)
{
    if( (area<=0) || (perimeter<=0)) return 0;
    return (4 * CV_PI*area) / (perimeter*perimeter);
}
double CircularityStandard(const vector<cv::Point> &contour)
{
    double area = cv::contourArea(contour);
    if (area == 0) return 0.0;
    double perimeter = cv::arcLength(contour, true);
    if (perimeter == 0) return 0.0;
    return (4 * CV_PI*area) / (perimeter*perimeter);
}

here is a code I could writewrite (EDIT: small bug removed)

void trackFilteredObject(int &x, int &y, const Mat &threshold, Mat &cameraFeed)
{
    vector< vector<Point> > contours;
    vector<Vec4i> hierarchy;

    // find external contours ignores holes 
    cv::findContours(threshold, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    //if number of objects greater than MAX_NUM_OBJECTS we have a noisy filter
    if (contours.size() > MAX_NUM_OBJECTS)
    {
        putText(cameraFeed, "TOO MUCH NOISE! ADJUST FILTER", Point(0, 50), 1, 2, Scalar(0, 0, 255), 2);
        return;
    }

    double refArea = 0;
    bool objectFound = false;
    double circularity;
    int largest = -1;
    double minCircularity = 0.9; 0.85; //use something > 0.85
0.80
    for (int i = 0; i < contours.size(); i++)
    {
        // draw all contours in red
        cv::drawContours(dst, contours, i, cv::Scalar(0, 0, 255), 1);

        cv::Moments moment = moments(contours[i]);
        double area = moment.m00;
        double perimeter = cv::arcLength(contour[i], true);
        //circularity = CircularityStandard(contours[i]); //alternative overload
        circularity = CircularityStandard(area,perimeter);
        if (circularity < minCircularity) //use something > 0.85
0.80
            continue;

        // draw all circles in blue
        int cx = cvRound(moment.m10 / area);
        int cy = cvRound(moment.m01 / area);
        cv::Point center(x, y);
center(cx, cy);
        int radius = cvRound(sqrt(refArea cvRound(sqrt(area / CV_PI));
        // circle center
        circle(cameraFeed, center, 3, Scalar(0, 255, 0), -1, 8, 0);
        // circle outline
        circle(cameraFeed, center, radius, Scalar(255, 0, 0), 3, 8, 0);

        //if the area is less than 20 px by 20px then it is probably just noise
        //if the area is the same as the 3/2 of the image size, probably just a bad filter
        //object wanted with the largest area so a reference area is stored each
        //iteration and compare it to the area in the next iteration.        
        if (area > refArea && area > MIN_OBJECT_AREA && area < MAX_OBJECT_AREA)
        {
            objectFound = true;
            x = cx;
            y = cy;
            refArea = area;
        }
    }
    //let user know you found an object
    if (objectFound == true)
    {
        putText(cameraFeed, "Tracking Object", Point(0, 50), 2, 1, Scalar(0, 255, 0), 2);
        //draw object location on screen
        drawObject(x, y, cameraFeed);
    }
}

Or you could use BlobDetector (as answerd here)

You are mixing contours and circles from HoughCircles in a obscure way. Detected circles OR contours and select your favourite.

  • If you want to filter circles by area you can get it from radius or not ?
  • If you want to filter contours by circularity calculate it as circularity = 4 * PI * area / perimeter^2
  • use findContours with CV_RETR_EXTERNAL and forget forget about hierarchy
  • you are looking for largest area. Maybe it isn't the ball. Sure your control var objectFound id bad managed because it can become false after it has been true.

here is a ready to use function for contour circularity

/**
* @brief Calculates circularity measure of a contour using common standard method
* The circularity is calculated with common method as relation between area and perimeter as:
*
* Circularity = (4 * PI * area) / (perimeter^2)
*
* @returns ranges over [0, 1] where 1 means that the contour is a perfect circle
* if area or perimeter is <=0 than circularity will be 0
*
* @note this returns 0.785 for squares
*/
double CircularityStandard(double area,double perimeter)
{
    if( (area<=0) || (perimeter<=0)) return 0;
    return (4 * CV_PI*area) / (perimeter*perimeter);
}
double CircularityStandard(const vector<cv::Point> &contour)
{
    double area = cv::contourArea(contour);
    if (area == 0) return 0.0;
    double perimeter = cv::arcLength(contour, true);
    if (perimeter == 0) return 0.0;
    return (4 * CV_PI*area) / (perimeter*perimeter);
}

here is a code I could write (EDIT: small bug removed)

result image description

void trackFilteredObject(int &x, int &y, const Mat &threshold, Mat &cameraFeed)
{
    vector< vector<Point> > contours;
    vector<Vec4i> hierarchy;

    // find external contours ignores holes 
holes
    cv::findContours(threshold, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    //if number of objects greater than MAX_NUM_OBJECTS we have a noisy filter
    if (contours.size() > MAX_NUM_OBJECTS)
    {
        putText(cameraFeed, "TOO MUCH NOISE! ADJUST FILTER", Point(0, 50), 1, 2, Scalar(0, 0, 255), 2);
        return;
    }

     double refArea = 0;
    bool objectFound = false;
    double circularity;
    int //int largest = -1;
    double minCircularity = 0.85; //use something > 0.80
0.85
    for (int (size_t i = 0; i < contours.size(); i++)
    {
        // Mat src;
        // Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC3);
        // draw all contours in red
        cv::drawContours(dst, cv::drawContours(cameraFeed, contours, i, cv::Scalar(0, 0, 255), 1);
cv::Scalar(255, 255, 0), 2);

        cv::Moments moment = moments(contours[i]);
        double area = moment.m00;
        double perimeter = cv::arcLength(contour[i], cv::arcLength(contours[i], true);
        //circularity = CircularityStandard(contours[i]); //alternative overload
        circularity = CircularityStandard(area,perimeter);
CircularityStandard(area, perimeter);
        putText(cameraFeed, "Circularity="+to_string(circularity), 
            contours[i][0], 2, 1, Scalar(255, 255, 0), 1);
        if (circularity < minCircularity) //use something > 0.80
0.85
            continue;

        // draw all circles in blue
        int cx = cvRound(moment.m10 / area);
        int cy = cvRound(moment.m01 / area);
        cv::Point center(cx, cy);
        int radius = cvRound(sqrt(area / CV_PI));
        // circle center
        circle(cameraFeed, center, 3, Scalar(0, 255, 0), -1, 8, 0);
        // circle outline
        circle(cameraFeed, center, radius, Scalar(255, 0, 0), 3, 8, 0);

        //if the area is less than 20 px by 20px then it is probably just noise
        //if the area is the same as the 3/2 of the image size, probably just a bad filter
        //object wanted with the largest area so a reference area is stored each
        //iteration and compare it to the area in the next iteration.        
iteration.
        if (area > refArea && area > MIN_OBJECT_AREA && area < MAX_OBJECT_AREA)
        {
            objectFound = true;
            x = cx;
            y = cy;
            refArea = area;
        }
    }
    //let user know you found an object
    if (objectFound == true)
    {
        putText(cameraFeed, "Tracking Object", Point(0, 50), 2, 1, Scalar(0, 255, 0), 2);
        //draw object location on screen
        drawObject(x, y, cameraFeed);
    }
    imwrite("cameraFeed.jpg", cameraFeed);
}

Or you could use BlobDetector (as answerd here)