Python 和 OpenCV。如何检测图像中的所有(实心)圆形/圆形物体?

发布于 2024-10-25 06:08:02 字数 1202 浏览 1 评论 0原文

我正在尝试编写一个程序来打开图像,扫描它的圆形/圆形形状并返回坐标,以便我可以使用 cv.Circle 函数在检测到的圆上绘制圆。

我的问题是:如何使用 cv.HoughCircles() 获取图像中检测到的圆的坐标/半径?

使用页面,我找到了如何检测圆圈(这花了我很多时间因为我不理解阈值之类的术语,而且 Python 的 OpenCV 文档非常差,几乎没有)。不幸的是,该页面上没有显示如何提取从创建的 CvMat 中检测到的每个圆的信息。如何提取该信息/是否有其他方法(例如使用 MemoryStorage() )?

这是我到目前为止的代码:

import cv, opencv

def main():


    im = cv.LoadImageM("Proba.jpg")

    gray = cv.CreateImage(cv.GetSize(im), 8, 1)
    edges = cv.CreateImage(cv.GetSize(im), 8, 1)

    cv.CvtColor(im, gray, cv.CV_BGR2GRAY)
    cv.Canny(gray, edges, 50, 200, 3)
    cv.Smooth(gray, gray, cv.CV_GAUSSIAN, 9, 9)

    storage = cv.CreateMat(im.rows, 1, cv.CV_32FC3)

    cv.HoughCircles(edges, storage, cv.CV_HOUGH_GRADIENT, 2, gray.height/4, 200, 100)
    # Now, supposing it found circles, how do I extract the information?
    print storage.r



if __name__ == '__main__':
    main()

此外,HoughCircles 的最后两个参数需要具有什么值才能让我检测到非常小的圆圈(例如屏幕上的 3mm)?

感谢大家花时间和精力来帮助我!

我正在使用的图像是这样的:在此处输入图像描述

I am trying to make a program which opens an image, scans it for circles/round shapes and returns the coordinates so that I can use the cv.Circle function to draw circles over the circle detected.

My question is: How do I get the coordinates/radii of the circles detected in an image using cv.HoughCircles() ?

Using this page, I found out how to detect the circles (which took me a lot of time to find out since I don't understand terms like threshold and the OpenCV documentation for Python is really poor, almost none). Unfortunately, on that page it didn't show how to extract the information of each circle detected from the CvMat created. How do I extract that information/is there some other way(eg. with MemoryStorage() ) ?

This is my code so far:

import cv, opencv

def main():


    im = cv.LoadImageM("Proba.jpg")

    gray = cv.CreateImage(cv.GetSize(im), 8, 1)
    edges = cv.CreateImage(cv.GetSize(im), 8, 1)

    cv.CvtColor(im, gray, cv.CV_BGR2GRAY)
    cv.Canny(gray, edges, 50, 200, 3)
    cv.Smooth(gray, gray, cv.CV_GAUSSIAN, 9, 9)

    storage = cv.CreateMat(im.rows, 1, cv.CV_32FC3)

    cv.HoughCircles(edges, storage, cv.CV_HOUGH_GRADIENT, 2, gray.height/4, 200, 100)
    # Now, supposing it found circles, how do I extract the information?
    print storage.r



if __name__ == '__main__':
    main()

Also, what value do the last two parameters of HoughCircles need to have in order for me to detect really small circles (like 3mm on the screen) ?

Thank you all for your time and effort trying to help me!

The image I'm working with is this:enter image description here

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

维持三分热 2024-11-01 06:08:03

最后两个参数似乎是传递给 cv.Canny() 的,这意味着 cv.Canny() 是从内部调用的cv.HoughCircles()。我对此不太确定。

至于尺寸,似乎接下来的两个参数(200, 100 之后) 默认为 0,这可能意味着所有尺寸都是检测到。

从 C++ 示例的源代码中,我还可以猜测您不需要进行 Canny 边缘检测:

#include <cv.h>
#include <highgui.h>
#include <math.h>

using namespace cv;

int main(int argc, char** argv)
{
    Mat img, gray;
    if( argc != 2 && !(img=imread(argv[1], 1)).data)
        return -1;
    cvtColor(img, gray, CV_BGR2GRAY);
    // smooth it, otherwise a lot of false circles may be detected
    GaussianBlur( gray, gray, Size(9, 9), 2, 2 );
    vector<Vec3f> circles;
    HoughCircles(gray, circles, CV_HOUGH_GRADIENT,
                 2, gray->rows/4, 200, 100 );
    for( size_t i = 0; i < circles.size(); i++ )
    {
         Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
         int radius = cvRound(circles[i][2]);
         // draw the circle center
         circle( img, center, 3, Scalar(0,255,0), -1, 8, 0 );
         // draw the circle outline
         circle( img, center, radius, Scalar(0,0,255), 3, 8, 0 );
    }
    namedWindow( "circles", 1 );
    imshow( "circles", img );
    return 0;
}

我认为您正在尝试将此 C++ 代码转换为 Python?

for( size_t i = 0; i < circles.size(); i++ )
{
     Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
     int radius = cvRound(circles[i][2]);
     // draw the circle center
     circle( img, center, 3, Scalar(0,255,0), -1, 8, 0 );
     // draw the circle outline
     circle( img, center, radius, Scalar(0,0,255), 3, 8, 0 );
}

据我所知,CvMat 对象是可迭代的,就像列表一样:

for circle in storage:
  radius = circle[2]
  center = (circle[0], circle[1])

  cv.Circle(im, center, radius, (0, 0, 255), 3, 8, 0)

我没有任何测试图像,所以不要相信我说这有效。您的完整代码可能是:

import cv

def main():
  im = cv.LoadImage('Proba.jpg')
  gray = cv.CreateImage(cv.GetSize(im), 8, 1)
  edges = cv.CreateImage(cv.GetSize(im), 8, 1)

  cv.CvtColor(im, gray, cv.CV_BGR2GRAY)
  #cv.Canny(gray, edges, 20, 55, 3)

  storage = cv.CreateMat(im.width, 1, cv.CV_32FC3)
  cv.HoughCircles(edges, storage, cv.CV_HOUGH_GRADIENT, 5, 25, 200, 10)

  for i in xrange(storage.width - 1):
    radius = storage[i, 2]
    center = (storage[i, 0], storage[i, 1])

    print (radius, center)

    cv.Circle(im, center, radius, (0, 0, 255), 3, 8, 0)

  cv.NamedWindow('Circles')
  cv.ShowImage('Circles', im)
  cv.WaitKey(0)

if __name__ == '__main__':
  main()

The last two parameters are what seem to be passed to cv.Canny(), which implies that cv.Canny() is called from within cv.HoughCircles(). I'm not too sure about that.

As for the sizes, it seems like the next two parameters (after 200, 100) default to 0, which might mean that all sizes are detected.

From the C++ example's source, I can also guess that you don't need to do a Canny edge detection:

#include <cv.h>
#include <highgui.h>
#include <math.h>

using namespace cv;

int main(int argc, char** argv)
{
    Mat img, gray;
    if( argc != 2 && !(img=imread(argv[1], 1)).data)
        return -1;
    cvtColor(img, gray, CV_BGR2GRAY);
    // smooth it, otherwise a lot of false circles may be detected
    GaussianBlur( gray, gray, Size(9, 9), 2, 2 );
    vector<Vec3f> circles;
    HoughCircles(gray, circles, CV_HOUGH_GRADIENT,
                 2, gray->rows/4, 200, 100 );
    for( size_t i = 0; i < circles.size(); i++ )
    {
         Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
         int radius = cvRound(circles[i][2]);
         // draw the circle center
         circle( img, center, 3, Scalar(0,255,0), -1, 8, 0 );
         // draw the circle outline
         circle( img, center, radius, Scalar(0,0,255), 3, 8, 0 );
    }
    namedWindow( "circles", 1 );
    imshow( "circles", img );
    return 0;
}

You're trying to convert this C++ code into Python, I assume?

for( size_t i = 0; i < circles.size(); i++ )
{
     Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
     int radius = cvRound(circles[i][2]);
     // draw the circle center
     circle( img, center, 3, Scalar(0,255,0), -1, 8, 0 );
     // draw the circle outline
     circle( img, center, radius, Scalar(0,0,255), 3, 8, 0 );
}

As far as I can tell, CvMat objects are iterable, just like a list:

for circle in storage:
  radius = circle[2]
  center = (circle[0], circle[1])

  cv.Circle(im, center, radius, (0, 0, 255), 3, 8, 0)

I don't have any test images, so don't take my word that this works. Your complete code would might be:

import cv

def main():
  im = cv.LoadImage('Proba.jpg')
  gray = cv.CreateImage(cv.GetSize(im), 8, 1)
  edges = cv.CreateImage(cv.GetSize(im), 8, 1)

  cv.CvtColor(im, gray, cv.CV_BGR2GRAY)
  #cv.Canny(gray, edges, 20, 55, 3)

  storage = cv.CreateMat(im.width, 1, cv.CV_32FC3)
  cv.HoughCircles(edges, storage, cv.CV_HOUGH_GRADIENT, 5, 25, 200, 10)

  for i in xrange(storage.width - 1):
    radius = storage[i, 2]
    center = (storage[i, 0], storage[i, 1])

    print (radius, center)

    cv.Circle(im, center, radius, (0, 0, 255), 3, 8, 0)

  cv.NamedWindow('Circles')
  cv.ShowImage('Circles', im)
  cv.WaitKey(0)

if __name__ == '__main__':
  main()
如此安好 2024-11-01 06:08:03

看看我的回答对这个问题的一些工作源代码(它是C,但我使用了C++编译器,因为它更宽松)。

首先,我裁剪了您的图像(以方便使用)并对图像应用阈值以将前景与背景分开:

在此处输入图像描述

然后我直接将源代码应用于阈值图像。这是文本输出:

center x: 330 y: 507 A: 13 B: 4
center x: 78 y: 507 A: 22 B: 4
center x: 270 y: 503 A: 8 B: 8
center x: 222 y: 493 A: 21 B: 17
center x: 140 y: 484 A: 17 B: 18
center x: 394 y: 478 A: 17 B: 15
center x: 311 y: 468 A: 8 B: 8
center x: 107 y: 472 A: 12 B: 12
center x: 7 y: 472 A: 6 B: 19
center x: 337 y: 442 A: 10 B: 9
center x: 98 y: 432 A: 10 B: 10
center x: 357 y: 421 A: 7 B: 7
center x: 488 y: 429 A: 22 B: 23
center x: 411 y: 400 A: 13 B: 12
center x: 42 y: 400 A: 11 B: 12
center x: 365 y: 391 A: 14 B: 13
center x: 141 y: 396 A: 19 B: 19
center x: 9 y: 379 A: 8 B: 18
center x: 192 y: 365 A: 10 B: 9
center x: 347 y: 340 A: 20 B: 20
center x: 8 y: 305 A: 7 B: 13
center x: 95 y: 308 A: 23 B: 24
center x: 318 y: 297 A: 15 B: 15
center x: 159 y: 285 A: 10 B: 10
center x: 412 y: 291 A: 26 B: 27
center x: 504 y: 278 A: 6 B: 16
center x: 233 y: 277 A: 20 B: 20
center x: 459 y: 256 A: 15 B: 15
center x: 7 y: 239 A: 6 B: 9
center x: 377 y: 239 A: 14 B: 14
center x: 197 y: 228 A: 12 B: 12
center x: 302 y: 237 A: 12 B: 22
center x: 98 y: 224 A: 24 B: 23
center x: 265 y: 203 A: 18 B: 18
center x: 359 y: 202 A: 22 B: 22
center x: 149 y: 201 A: 20 B: 21
center x: 219 y: 169 A: 7 B: 9
center x: 458 y: 172 A: 20 B: 20
center x: 497 y: 157 A: 13 B: 21
center x: 151 y: 125 A: 18 B: 17
center x: 39 y: 109 A: 9 B: 10
center x: 81 y: 116 A: 20 B: 19
center x: 249 y: 104 A: 14 B: 13
center x: 429 y: 76 A: 23 B: 24
center x: 493 y: 33 A: 11 B: 10
center x: 334 y: 26 A: 12 B: 14

这是输出图像:

在此处输入图像描述

主要问题是已合并的圆圈在一起根本没有被发现。该代码最初是为了检测填充的省略号而编写的,因此您可以通过调整代码来解决此问题。

Have a look at my answer to this question for some working source code (it's C but I used a C++ compiler cause it's more lenient).

First, I cropped your image (to get something convenient to work with) and applied a threshold to your image to separate the foreground from the background:

enter image description here

Then I directly applied the source code to the thresholded image. Here is the text output:

center x: 330 y: 507 A: 13 B: 4
center x: 78 y: 507 A: 22 B: 4
center x: 270 y: 503 A: 8 B: 8
center x: 222 y: 493 A: 21 B: 17
center x: 140 y: 484 A: 17 B: 18
center x: 394 y: 478 A: 17 B: 15
center x: 311 y: 468 A: 8 B: 8
center x: 107 y: 472 A: 12 B: 12
center x: 7 y: 472 A: 6 B: 19
center x: 337 y: 442 A: 10 B: 9
center x: 98 y: 432 A: 10 B: 10
center x: 357 y: 421 A: 7 B: 7
center x: 488 y: 429 A: 22 B: 23
center x: 411 y: 400 A: 13 B: 12
center x: 42 y: 400 A: 11 B: 12
center x: 365 y: 391 A: 14 B: 13
center x: 141 y: 396 A: 19 B: 19
center x: 9 y: 379 A: 8 B: 18
center x: 192 y: 365 A: 10 B: 9
center x: 347 y: 340 A: 20 B: 20
center x: 8 y: 305 A: 7 B: 13
center x: 95 y: 308 A: 23 B: 24
center x: 318 y: 297 A: 15 B: 15
center x: 159 y: 285 A: 10 B: 10
center x: 412 y: 291 A: 26 B: 27
center x: 504 y: 278 A: 6 B: 16
center x: 233 y: 277 A: 20 B: 20
center x: 459 y: 256 A: 15 B: 15
center x: 7 y: 239 A: 6 B: 9
center x: 377 y: 239 A: 14 B: 14
center x: 197 y: 228 A: 12 B: 12
center x: 302 y: 237 A: 12 B: 22
center x: 98 y: 224 A: 24 B: 23
center x: 265 y: 203 A: 18 B: 18
center x: 359 y: 202 A: 22 B: 22
center x: 149 y: 201 A: 20 B: 21
center x: 219 y: 169 A: 7 B: 9
center x: 458 y: 172 A: 20 B: 20
center x: 497 y: 157 A: 13 B: 21
center x: 151 y: 125 A: 18 B: 17
center x: 39 y: 109 A: 9 B: 10
center x: 81 y: 116 A: 20 B: 19
center x: 249 y: 104 A: 14 B: 13
center x: 429 y: 76 A: 23 B: 24
center x: 493 y: 33 A: 11 B: 10
center x: 334 y: 26 A: 12 B: 14

And here is the output image:

enter image description here

The main problem is that circles that have merged together have not been detected at all. The code was originally written for detecting filled ellipses only, so you can probably deal with this issue by tweaking the code.

执笔绘流年 2024-11-01 06:08:03

python 中的类似解决方案。最初,我尝试运行 此处 描述的轮廓检测,但效果不佳。所以首先需要一些阈值。阈值的代码在这里:

    fimg = misc.imread("boubles.jpg")
    gimg = color.colorconv.rgb2grey(fimg)
    vimg = []
    for l in gimg:
        l2 = sign(l - 0.50) / 2 + 0.5
        vimg.append(l2)

    img = array(vimg)
    imshow(img)

这样我得到一个像这样的图像:

thresholded image

在链接中描述的边缘检测之后上面我得到了这个:

findcontours

如果你检查代码,你会发现计算对象非常容易。唯一的问题是,有些气泡被计数了两次。我想阈值功能也可以改进。但我建议使用 skimage,它很容易使用,并且在其网页上有很好的示例。

A similar solution in python. Originally I tried to run a contour detection described here, but it doesn't worked well. So first some thresholding was necessary. The code for threshold is here:

    fimg = misc.imread("boubles.jpg")
    gimg = color.colorconv.rgb2grey(fimg)
    vimg = []
    for l in gimg:
        l2 = sign(l - 0.50) / 2 + 0.5
        vimg.append(l2)

    img = array(vimg)
    imshow(img)

With this I get an image like this:

thresholded image

And after edge detection described in the link above I got this:

find contours

If you check the code, you will find that's really easy to count the objects. The only problem is, that some of the bubbles are counted twice. And I guess the thresholding function can be improved as well. But I suggest to use skimage it's easy to use and has good samples on their web page.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文