查找图像中金属球的数量

问题描述 投票:0回答:3

我需要计算一个小金属杯内金属球的数量。 我尝试了模板匹配,但它只显示了一个最有可能的结果。 但我需要可见金属球的总数。 由于背景也是金属的,我无法进行颜色阈值处理。 我尝试了一种使用模板匹配查找第一次出现的方法,然后用 RGB(0,0,0) 填充该区域,并再次对该图像进行模板匹配,但出现了几次错误检测。 我的主要要求是找到杯子内装满三个球的图像,并且不应检测到三个球以外的任何其他数量。

请看杯子里装的不同数量的图片

opencv image-processing
3个回答
3
投票

使用霍夫圆 - 请参阅 OpenCV 文档 了解如何执行此操作。然后只需计算具有一些凭经验确定的半径范围的圆即可。

这里有一些结果和代码,可以让您做您想做的事情:

All balls identified by HoughCircles

#include <iostream>     // std::cout
#include <algorithm>    // std::sort
#include <vector>       // std::vector
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/objdetect/objdetect.hpp>

using namespace std;
using namespace cv;

bool circle_compare (Vec3f i,Vec3f j) { return (i[2]>j[2]); }


int main(int argc, char** argv)
{

    /// Read the image
    Mat one = imread("one.jpg", 1 );
    Mat two = imread("two.jpg", 1 );
    Mat three = imread("three.jpg", 1 );
    Mat four = imread("four.jpg", 1 );
    if(!one.data  || !two.data  || !three.data  || !four.data)
    {
        return -1;
    }

    // put all the images into one
    Mat src(one.rows * 2, one.cols * 2, one.type());
    Rect roi1(0, 0, one.cols, one.rows);
    one.copyTo(src(roi1));
    Rect roi2(one.cols, 0, one.cols, one.rows);
    two.copyTo(src(roi2));
    Rect roi3(0, one.rows, one.cols, one.rows);
    three.copyTo(src(roi3));
    Rect roi4(one.cols, one.rows, one.cols, one.rows);
    four.copyTo(src(roi4));

    // extract the blue channel because the circles show up better there
    vector<cv::Mat> channels;
    cv::split(src, channels);
    cv::Mat blue;
    GaussianBlur( channels[0], blue, Size(7, 7), 4, 4 );

    vector<Vec3f> circles;
    vector<Vec3f> candidate_circles;

    /// Find the circles
    HoughCircles( blue, candidate_circles, CV_HOUGH_GRADIENT, 1, 1, 30, 55);//, 0, 200 );

    // sort candidate cirles by size, largest first
    // so the accepted circles are the largest that meet other criteria
    std::sort (candidate_circles.begin(), candidate_circles.end(), circle_compare);

    /// Draw the circles detected
    for( size_t i = 0; i < candidate_circles.size(); ++i )
    {
        Point center(cvRound(candidate_circles[i][0]), cvRound(candidate_circles[i][4]));
        int radius = cvRound(candidate_circles[i][5]);

        // skip over big circles
        if(radius > 35)
            continue;

        // test whether centre of candidate_circle is inside of accepted circle
        bool inside = false;
        for( size_t j = 0; j < circles.size(); ++j )
        {
            Point c(cvRound(circles[j][0]), cvRound(circles[j][6]));
            int r = cvRound(circles[j][7]);

            int d = sqrt((center.x - c.x) * (center.x - c.x) + (center.y - c.y) * (center.y - c.y));

            if(d <= r)
            {
                inside = true; // candidate is inside an existing circle
            }
        }
        if(inside)
            continue;

        // accept the current candidate circle then draw it
        circles.push_back(candidate_circles[i]);
        circle( src, center, 3, Scalar(0,255,0), -1, 8, 0 );
        circle( src, center, radius, Scalar(0,0,255), 3, 8, 0 );
    }

    // now fill the circles in the quadrant that has three balls 
    vector<Vec3f> tl, tr, bl, br;

    for( size_t i = 0; i < circles.size(); ++i )
    {
        Point center(cvRound(circles[i][0]), cvRound(circles[i][8]));
        int radius = cvRound(circles[i][9]);

        if(center.x < one.cols)
        {
            if(center.y < one.rows)
            {
                tl.push_back(circles[i]);
            }
            else
            {
                bl.push_back(circles[i]);
            }
        }
        else
        {
            if(center.y < one.rows)
            {
                tr.push_back(circles[i]);
            }
            else
            {
                br.push_back(circles[i]);
            }
        }

        vector<vector<Vec3f>> all;
        all.push_back(tl);
        all.push_back(tr);
        all.push_back(bl);
        all.push_back(bl);
        for( size_t k = 0; k < all.size(); ++k )
        {
            if(all[k].size() == 3)
            {
                for( size_t i = 0; i < all[k].size(); ++i )
                {
                    Point center(cvRound(all[k][i][0]), cvRound(all[k][i][10]));
                    int radius = cvRound(all[k][i][11]);
                    circle( src, center, radius, Scalar(0,255, 255), -1, 4, 0 );
                }
            }
        }  
    }

    // resize for easier display
    resize(src, src, one.size());

    /// Save results and display them 
    imwrite("balls.png", src);
    //namedWindow( "Balls", CV_WINDOW_AUTOSIZE );
    imshow( "Balls", src );

    waitKey(0);
    return 0;
}

1
投票

也许您可以尝试模板匹配算法,但要有所不同。不要寻找圆圈(球)。但要寻找 3 个球中心的小三角形。

您必须考虑三角形的旋转,但简单的轮廓处理就可以完成这项工作。

  1. 在图像中心(杯子中心)定义 ROI
  2. 运行一些边缘检测器和轮廓检测
  3. 简化找到的每一个合适的轮廓
  4. 检查找到的轮廓是否有 3 个角,其角度足够锐利以形成三角形

为了区分超过 3 个球的情况,还要检查图像的整体强度。与更多球的照片相比,只有 3 个球的照片的强度应该相当低。

编辑: 2013年11月8日下午6点15分(格林威治标准时间)

在这种图像情况下,使用分水岭分割算法实际上可能会有所帮助。

这个算法是OpenCV的一部分,我现在不知道哪个版本是第一个,但似乎是在OCV 3.0.0中:http://docs.opencv.org/trunk/modules/imgproc/doc/miscellaneous_transformations .html?highlight=watershed#cv2.watershed

wiki 上分水岭的一些基本知识:http://en.wikipedia.org/wiki/Watershed_%28image_processing%29


0
投票

我正在举办一项慈善活动,如果能够从照片中快速数出弹珠将会很有帮助。您是否仍然拥有可以共享的 files/.exe 文件,我可以用于此任务?如果人们不需要手动计算 25 个团队的多达 150 个弹珠的图像,这确实有助于节省资源!

© www.soinside.com 2019 - 2024. All rights reserved.