Python和OpenCV。如何在图像中检测所有(填充的)圆形/圆形对象?

18
我正在尝试编写一个程序,打开图像并扫描其中的圆形/圆形轮廓,并返回其坐标,以便我可以使用 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()

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

谢谢大家花时间和努力帮助我!

我正在处理的图像是这样的:enter image description here


你能否发布一张样例图片,这样我可以测试我的代码吗? - Blender
1
使用Python从图片中找到有色形状的数量 - jfs
3个回答

4
最后两个参数似乎被传递给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()

你能发布一下你的测试图片吗?如果我能真正运行它,我就可以更快地调试代码 ;) 由于你没有图像共享权限,所以如果愿意,请将其上传到图像共享网站并发布链接。我会编辑你的问题,使其显示为图像。 - Blender
我认为正确的属性是cols,但它也不能正常工作。 - Calin
我现在正在调试代码。不知何故,cv.HoughCircles() 没有检测到任何东西。它只是运行而没有错误,但对原始图像没有进行任何修改... - Blender
嗯,这很有趣,也许是因为传递的参数。你不知道我有多感激你尝试帮助我。我从未想过有人会如此详细地提供答案。我真的很感激! :) - Calin
我尝试调整参数。并不是很多人使用OpenCV和Python(我使用!),所以我找不到太多使用cv.HoughCircles()的例子... - Blender
显示剩余2条评论

2
请看我的回答,其中有一些可行的源代码(它是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

以下是输出图像:

enter image description here

主要问题在于合并在一起的圆没有被检测到。原始代码仅用于检测填充椭圆,因此您可以通过调整代码来解决此问题。


0
一个在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

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

find contours

如果你检查代码,你会发现计算对象是非常容易的。唯一的问题是有些气泡会被重复计数。我想阈值函数也可以改进。但我建议使用skimage,它易于使用,并且在他们的网页上有良好的样例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接