使用 OpenCV(基于霍夫变换或其他特征)编写鲁棒的(颜色和大小不变)圆检测

Posted

技术标签:

【中文标题】使用 OpenCV(基于霍夫变换或其他特征)编写鲁棒的(颜色和大小不变)圆检测【英文标题】:Writing robust (color and size invariant) circle detection with OpenCV (based on Hough transform or other features) 【发布时间】:2012-04-09 06:43:30 【问题描述】:

我编写了以下非常简单的 python 代码来查找图像中的圆圈:

import cv
import numpy as np

WAITKEY_DELAY_MS = 10
STOP_KEY = 'q'

cv.NamedWindow("image - press 'q' to quit", cv.CV_WINDOW_AUTOSIZE);
cv.NamedWindow("post-process", cv.CV_WINDOW_AUTOSIZE);

key_pressed = False
while key_pressed != STOP_KEY:

    # grab image
    orig = cv.LoadImage('circles3.jpg')

    # create tmp images
    grey_scale = cv.CreateImage(cv.GetSize(orig), 8, 1)
    processed = cv.CreateImage(cv.GetSize(orig), 8, 1)


    cv.Smooth(orig, orig, cv.CV_GAUSSIAN, 3, 3)

    cv.CvtColor(orig, grey_scale, cv.CV_RGB2GRAY)

    # do some processing on the grey scale image
    cv.Erode(grey_scale, processed, None, 10)
    cv.Dilate(processed, processed, None, 10)
    cv.Canny(processed, processed, 5, 70, 3)
    cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 15, 15)

    storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)

    # these parameters need to be adjusted for every single image
    HIGH = 50
    LOW = 140

    try: 
        # extract circles
        cv.HoughCircles(processed, storage, cv.CV_HOUGH_GRADIENT, 2, 32.0, HIGH, LOW)

        for i in range(0, len(np.asarray(storage))):
            print "circle #%d" %i
            Radius = int(np.asarray(storage)[i][0][2])
            x = int(np.asarray(storage)[i][0][0])
            y = int(np.asarray(storage)[i][0][1])
            center = (x, y)

            # green dot on center and red circle around
            cv.Circle(orig, center, 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
            cv.Circle(orig, center, Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)

            cv.Circle(processed, center, 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
            cv.Circle(processed, center, Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)

    except:
        print "nothing found"
        pass

    # show images
    cv.ShowImage("image - press 'q' to quit", orig)
    cv.ShowImage("post-process", processed)

    cv_key = cv.WaitKey(WAITKEY_DELAY_MS)
    key_pressed = chr(cv_key & 255)

从以下两个例子可以看出,“找圈质量”变化很大:

案例1:

案例2:

Case1 和Case2 基本上是同一张图片,但是算法还是检测到了不同的圆圈。如果我向算法展示具有不同大小圆圈的图像,则圆圈检测甚至可能完全失败。这主要是由于HIGHLOW 参数需要为每张新图片单独调整。

因此我的问题是:使该算法更健壮的各种可能性是什么?它应该是大小和颜色不变的,以便检测具有不同颜色和不同大小的不同圆圈。也许使用霍夫变换不是最好的做事方式?有更好的方法吗?

【问题讨论】:

预处理后的图像是什么样的? IE。 processed 之前调用HoughCircles @Eric 我已经添加了输入HoughCircles 的后处理图像。希望对您有所帮助。 谢谢。您能否为您的两个示例描述您的预期输出是什么?所有的涂鸦还是特定的? @Eric 理想情况下,我想检测在那张纸上可见的所有圆圈。一旦可行,如果我可以定义“圆度”参数来确定椭圆体是否足够圆,那就太好了。 呵呵,问题依然存在:你的意思是检测纸上的所有东西,还是只检测未填充的东西? :-) 在任何一种情况下,在您的输入中添加一些您明确不想检测到的涂鸦(正方形、线条、笔迹、太扁的椭圆)。然后尝试降低LOW,直到您对误报的数量感到满意为止。最后,如果您只想要非实心圆,您可能需要在程序中添加一个额外的处理步骤,以区分实心圆和非实心圆,与霍夫变换分开。 【参考方案1】:

以下内容基于我作为视觉研究员的经验。从您的问题来看,您似乎对可能的算法和方法感兴趣,而只是对一段工作代码感兴趣。首先,我为您的示例图像提供了一个快速而肮脏的 Python 脚本,并显示了一些结果以证明它可能会解决您的问题。解决这些问题后,我将尝试回答您有关稳健检测算法的问题。

快速结果

带有检测到的圆圈的一些示例图像(除您之外的所有图像均已从 flickr.com 下载并获得 CC 许可)(无需更改/调整任何参数,使用以下代码提取所有图像中的圆圈):

代码(基于 MSER Blob Detector)

这里是代码:

import cv2
import math
import numpy as np

d_red = cv2.cv.RGB(150, 55, 65)
l_red = cv2.cv.RGB(250, 200, 200)

orig = cv2.imread("c.jpg")
img = orig.copy()
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

detector = cv2.FeatureDetector_create('MSER')
fs = detector.detect(img2)
fs.sort(key = lambda x: -x.size)

def supress(x):
        for f in fs:
                distx = f.pt[0] - x.pt[0]
                disty = f.pt[1] - x.pt[1]
                dist = math.sqrt(distx*distx + disty*disty)
                if (f.size > x.size) and (dist<f.size/2):
                        return True

sfs = [x for x in fs if not supress(x)]

for f in sfs:
        cv2.circle(img, (int(f.pt[0]), int(f.pt[1])), int(f.size/2), d_red, 2, cv2.CV_AA)
        cv2.circle(img, (int(f.pt[0]), int(f.pt[1])), int(f.size/2), l_red, 1, cv2.CV_AA)

h, w = orig.shape[:2]
vis = np.zeros((h, w*2+5), np.uint8)
vis = cv2.cvtColor(vis, cv2.COLOR_GRAY2BGR)
vis[:h, :w] = orig
vis[:h, w+5:w*2+5] = img

cv2.imshow("image", vis)
cv2.imwrite("c_o.jpg", vis)
cv2.waitKey()
cv2.destroyAllWindows()

如您所见,它基于MSER blob 检测器。除了简单的灰度映射之外,该代码不会对图像进行预处理。因此,预计会丢失图像中那些微弱的黄色斑点。

理论

简而言之:除了只提供两张没有描述的示例图像之外,您不会告诉我们您对问题的了解。在这里,我解释了为什么在我的拙见中,在询问解决问题的有效方法之前,了解有关该问题的更多信息很重要。

回到主要问题:解决这个问题的最佳方法是什么? 让我们将其视为一个搜索问题。为了简化讨论,假设我们正在寻找具有给定大小/半径的圆。因此,问题归结为找到中心。每个像素都是一个候选中心,因此,搜索空间包含所有像素。

P = p1, ..., pn 
P: search space
p1...pn: pixels

为了解决这个搜索问题,应该定义另外两个函数:

E(P) : enumerates the search space
V(p) : checks whether the item/pixel has the desirable properties, the items passing the check are added to the output list

假设算法的复杂性无关紧要,可以使用穷举或蛮力搜索,其中 E 获取每个像素并传递给 V。在实时应用中,重要的是减少搜索空间并优化计算V的效率。

我们正在接近主要问题。我们如何定义 V,更准确地说,候选者的哪些属性应该是衡量标准,以及应该如何解决将它们分为可取和不可取的二分法问题。最常见的方法是找到一些属性,这些属性可用于基于属性的测量来定义简单的决策规则。这就是你通过反复试验所做的。您正在通过从正面和负面示例中学习来编写分类器。这是因为您使用的方法不知道您想做什么。您必须调整/调整决策规则的参数和/或预处理数据,以减少用于二分法问题的方法(理想候选者的)属性的变化。您可以使用机器学习算法来找到给定示例集的最佳参数值。从决策树到遗传编程,您可以使用大量学习算法来解决这个问题。您还可以使用学习算法来找到几种圆检测算法的最佳参数值,并查看哪个提供了更好的准确度。这承担了您只需要收集样本图像的学习算法的主要负担。

另一个经常被忽视的提高稳健性的方法是利用更容易获得的信息。如果您以几乎零额外的努力知道圆圈的颜色,您可以显着提高检测器的准确性。如果您知道平面上圆的位置并且想要检测成像的圆,您应该记住这两组位置之间的转换是由二维单应性描述的。并且可以仅使用四个点来估计单应性。然后,您可以提高稳健性以拥有坚如磐石的方法。特定领域知识的价值常常被低估。这样看,在第一种方法中,我们尝试基于有限数量的样本来近似一些决策规则。在第二种方法中,我们知道决策规则,只需要找到一种在算法中有效利用它们的方法。

总结

总而言之,有两种方法可以提高解决方案的准确性/稳健性:

    基于工具:寻找更易于使用的算法/使用更少的参数/调整算法/使用机器学习算法自动执行此过程 基于信息的:您是否使用所有现成​​的信息?在问题中,您没有提及您对问题的了解。

对于您分享的这两个图像,我将使用斑点检测器而不是 HT 方法。对于背景减法,我建议尝试估计背景的颜色,因为在两个图像中它没有变化,而圆圈的颜色会有所不同。而且大部分区域都是光秃秃的。

【讨论】:

感谢这篇出色的文章。你能评论一下为什么你决定使用 blob 检测器而不是 HT 来搜索圆圈吗?如果圆是完美的圆形(即使用计算机打印在页面上),您会改用 HT,还是可以转换为完美的圆形(即使用 2D 单应性)? cv2.cv.RGB() 函数在新版本的opencv中不再可用,它的新名称是什么? @linuscl 在新版本中,您可以使用元组作为颜色,d_red = (56, 55, 150)。请注意,这是在图像的颜色空间中,BGR,而不是 RGB,因此交换了 R 和 B 值。【参考方案2】:

这是一个很好的建模问题。我有以下建议/想法:

    将图像拆分为 RGB,然后进行处理。 预处理。 动态参数搜索。 添加约束。 确定您要检测的内容。

更详细的:

1:如其他答案所述,直接转换为灰度会丢弃太多信息 - 任何与背景亮度相似的圆圈都会丢失。更好地考虑单独或不同颜色空间中的颜色通道。这里有两种方法:对每个预处理通道单独执行HoughCircles,然后组合结果,或者处理通道,然后组合它们,然后操作HoughCircles。在下面的尝试中,我尝试了第二种方法,拆分为 RGB 通道,处理,然后组合。组合时要小心图像过度饱和,我使用cv.And 来避免这个问题(在这个阶段我的圆圈总是白色背景上的黑色圆环/圆盘)。

2:预处理非常棘手,通常最好玩一些。我使用了AdaptiveThreshold,这是一种非常强大的卷积方法,可以通过基于像素的局部平均值对像素进行阈值化来增强图像中的边缘(类似的过程也发生在哺乳动物视觉系统的早期路径中)。这也很有用,因为它可以减少一些噪音。我只用过一次dilate/erode。而且我保留了其他参数的方式。似乎在HoughCircles 之前使用Canny 确实有助于找到“实心圆圈”,所以最好保留它。这种预处理非常繁重,可能会导致误报,并带有更多的“斑点圆圈”,但在我们的情况下,这也许是可取的?

3:正如您所指出的,需要针对每个图像调整 HoughCircles 参数 param2(您的参数 LOW)以获得最佳解决方案,实际上来自 docs:

它越小,可能检测到的错误圆圈越多。

问题是每张图片的最佳位置都会不同。我认为这里最好的方法是设置一个条件并通过不同的param2 值进行搜索,直到满足这个条件。您的图像显示不重叠的圆圈,当param2 太低时,我们通常会得到大量重叠的圆圈。所以我建议搜索:

非重叠和非包含圆的最大数量

所以我们一直用不同的param2 值调用 HoughCircles,直到满足。我在下面的示例中执行此操作,只需递增 param2 直到达到阈值假设。如果您执行二进制搜索以查找何时遇到这种情况,将会更快(并且相当容易做到),但是您需要小心异常处理,因为 opencv 经常会为看起来无害的 param2 值抛出错误(在至少在我的安装中)。另一个我们非常有用的匹配条件是圈数。

4:我们可以向模型添加更多约束吗?我们可以告诉我们的模型越多的东西,我们就越容易完成检测圆圈的任务。例如,我们是否知道:

圈数。 - 即使是上限或下限也很有帮助。 圆圈、背景或“非圆圈”的可能颜色。 它们的尺寸。 它们可以在图像中的什么位置。

5:图片中的一些斑点只能粗略地称为圆圈!考虑第二张图片中的两个“非圆形斑点”,我的代码找不到它们(很好!),但是......如果我“photoshop”它们使它们更圆,我的代码可以找到它们......也许如果你想检测不是圆圈的东西,另一种方法,比如Tim Lukins 可能会更好。

问题

通过对AdaptiveThresholding 和“Canny”进行大量预处理,图像中的特征可能会出现很多失真,这可能会导致错误的圆检测或错误的半径报告。例如一个大的实心圆盘经过处理后会出现一个环,因此 HughesCircles 可能会找到内环。此外,甚至文档也指出:

...通常该函数可以很好地检测圆的中心,但是它可能无法找到正确的半径。

如果您需要更准确的半径检测,我建议以下方法(未实现):

在原始图像上,来自报告的圆心的光线追踪,在一个扩大的十字形中(4 条光线:上/下/左/右) 在每个 RGB 通道中单独执行此操作 以合理的方式为每条光线的每个通道组合此信息(即,必要时翻转、偏移、缩放等) 取每条射线上前几个像素的平均值,用它来检测射线上发生显着偏差的位置。 这 4 个点是圆周上的点的估计值。 使用这四个估计值来确定更准确的半径和中心位置 (!)。 这可以通过使用膨胀环而不是四条射线来概括。

结果

最后的代码在很多时候都做得很好,这些例子都是用如下所示的代码完成的:

检测第一张图片中的所有圆圈:

预处理后的图像在应用canny滤镜之前的样子(不同颜色的圆圈非常明显):

在第二张图片中检测到除了两个(斑点)之外的所有东西:

改变的第二张图片(斑点是圆形的,大椭圆形更圆,从而提高了检测能力),全部检测到:

在这幅康定斯基绘画中检测中心方面做得很好(由于边界条件,我找不到同心环)。

代码:

import cv
import numpy as np

output = cv.LoadImage('case1.jpg')
orig = cv.LoadImage('case1.jpg')

# create tmp images
rrr=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
ggg=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
bbb=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
processed = cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)

def channel_processing(channel):
    pass
    cv.AdaptiveThreshold(channel, channel, 255, adaptive_method=cv.CV_ADAPTIVE_THRESH_MEAN_C, thresholdType=cv.CV_THRESH_BINARY, blockSize=55, param1=7)
    #mop up the dirt
    cv.Dilate(channel, channel, None, 1)
    cv.Erode(channel, channel, None, 1)

def inter_centre_distance(x1,y1,x2,y2):
    return ((x1-x2)**2 + (y1-y2)**2)**0.5

def colliding_circles(circles):
    for index1, circle1 in enumerate(circles):
        for circle2 in circles[index1+1:]:
            x1, y1, Radius1 = circle1[0]
            x2, y2, Radius2 = circle2[0]
            #collision or containment:
            if inter_centre_distance(x1,y1,x2,y2) < Radius1 + Radius2:
                return True

def find_circles(processed, storage, LOW):
    try:
        cv.HoughCircles(processed, storage, cv.CV_HOUGH_GRADIENT, 2, 32.0, 30, LOW)#, 0, 100) great to add circle constraint sizes.
    except:
        LOW += 1
        print 'try'
        find_circles(processed, storage, LOW)
    circles = np.asarray(storage)
    print 'number of circles:', len(circles)
    if colliding_circles(circles):
        LOW += 1
        storage = find_circles(processed, storage, LOW)
    print 'c', LOW
    return storage

def draw_circles(storage, output):
    circles = np.asarray(storage)
    print len(circles), 'circles found'
    for circle in circles:
        Radius, x, y = int(circle[0][2]), int(circle[0][0]), int(circle[0][1])
        cv.Circle(output, (x, y), 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
        cv.Circle(output, (x, y), Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)

#split image into RGB components
cv.Split(orig,rrr,ggg,bbb,None)
#process each component
channel_processing(rrr)
channel_processing(ggg)
channel_processing(bbb)
#combine images using logical 'And' to avoid saturation
cv.And(rrr, ggg, rrr)
cv.And(rrr, bbb, processed)
cv.ShowImage('before canny', processed)
# cv.SaveImage('case3_processed.jpg',processed)
#use canny, as HoughCircles seems to prefer ring like circles to filled ones.
cv.Canny(processed, processed, 5, 70, 3)
#smooth to reduce noise a bit more
cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 7, 7)
cv.ShowImage('processed', processed)
#find circles, with parameter search
storage = find_circles(processed, storage, 100)
draw_circles(storage, output)
# show images
cv.ShowImage("original with circles", output)
cv.SaveImage('case1.jpg',output)

cv.WaitKey(0)

【讨论】:

【参考方案3】:

啊,是的……圆问题的旧颜色/大小不变量(AKA 霍夫变换太具体且不稳健)……

过去,我更多地依赖 OpenCV 的 structural and shape analysis 函数。您可以从“样本”文件夹中很好地了解可能的情况 - 特别是 fitellipse.pysquares.py

为了您的说明,我提供了这些示例的混合版本,并基于您的原始来源。检测到的轮廓为绿色,拟合的椭圆为红色。

还没有:

预处理步骤需要稍作调整才能检测出更微弱的圆圈。 您可以进一步测试轮廓以确定它是否是圆形...

祝你好运!

import cv
import numpy as np

# grab image
orig = cv.LoadImage('circles3.jpg')

# create tmp images
grey_scale = cv.CreateImage(cv.GetSize(orig), 8, 1)
processed = cv.CreateImage(cv.GetSize(orig), 8, 1)

cv.Smooth(orig, orig, cv.CV_GAUSSIAN, 3, 3)

cv.CvtColor(orig, grey_scale, cv.CV_RGB2GRAY)

# do some processing on the grey scale image
cv.Erode(grey_scale, processed, None, 10)
cv.Dilate(processed, processed, None, 10)
cv.Canny(processed, processed, 5, 70, 3)
cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 15, 15)

#storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)
storage = cv.CreateMemStorage(0)

contours = cv.FindContours(processed, storage, cv.CV_RETR_EXTERNAL)
# N.B. 'processed' image is modified by this!

#contours = cv.ApproxPoly (contours, storage, cv.CV_POLY_APPROX_DP, 3, 1) 
# If you wanted to reduce the number of points...

cv.DrawContours (orig, contours, cv.RGB(0,255,0), cv.RGB(255,0,0), 2, 3, cv.CV_AA, (0, 0)) 

def contour_iterator(contour):
  while contour:
    yield contour
    contour = contour.h_next()

for c in contour_iterator(contours):
  # Number of points must be more than or equal to 6 for cv.FitEllipse2
  if len(c) >= 6:
    # Copy the contour into an array of (x,y)s
    PointArray2D32f = cv.CreateMat(1, len(c), cv.CV_32FC2)

    for (i, (x, y)) in enumerate(c):
      PointArray2D32f[0, i] = (x, y)

    # Fits ellipse to current contour.
    (center, size, angle) = cv.FitEllipse2(PointArray2D32f)

    # Convert ellipse data from float to integer representation.
    center = (cv.Round(center[0]), cv.Round(center[1]))
    size = (cv.Round(size[0] * 0.5), cv.Round(size[1] * 0.5))

    # Draw ellipse
    cv.Ellipse(orig, center, size, angle, 0, 360, cv.RGB(255,0,0), 2,cv.CV_AA, 0)

# show images
cv.ShowImage("image - press 'q' to quit", orig)
#cv.ShowImage("post-process", processed)
cv.WaitKey(-1)

编辑:

只是一个更新,我相信所有这些答案的一个主要主题是,有许多进一步的假设和限制可以应用于您试图识别为循环的内容。我自己的回答对此毫不掩饰——无论是在低级预处理还是高级几何拟合中。事实上,由于绘制方式或图像的非仿射/投影变换,以及它们如何渲染/捕获的其他属性(颜色、噪声、照明、边缘厚度)——所有这些都会在一张图像中产生任意数量的可能候选圆圈。

还有更复杂的技术。但它们会让你付出代价。我个人喜欢@fraxel 使用附加阈值的想法。这是快速、可靠且相当稳健的。然后,您可以通过椭圆轴的简单比率测试进一步测试最终轮廓(例如使用 Hu 矩)或配件 - 例如if ((min(size)/max(size))>0.7).

与以往的计算机视觉一样,实用主义、原则和吝啬之间存在紧张关系。正如我喜欢告诉那些认为 CV 很简单的人一样,事实并非如此——它实际上是一个著名的AI complete 问题。在此之外,您通常可以期望的最好的东西是大多数时间都有效的东西。

【讨论】:

【参考方案4】:

查看您的代码,我注意到以下内容:

灰度转换。我明白你为什么这样做,但意识到你在扔 远离那里的信息。正如您在“后期处理”图像中看到的那样,您的黄色圆圈是 与背景的强度相同,只是颜色不同。

去噪后的边缘检测(erae/dilate)。这不应该是必要的; Canny 应该解决这个问题。

Canny 边缘检测。你的“开放”圈子有两个边缘,一个内边缘和一个外边缘。由于它们相当接近,Canny 高斯滤波器可能会将它们加在一起。如果没有,您将有两条边靠近在一起。 IE。在 Canny 之前,您有空心圆和实心圆。之后,您分别拥有 0/2 和 1 个边。由于 Hough 再次调用 Canny,在第一种情况下,两条边可能会一起平滑(取决于初始宽度),这就是核心 Hough 算法可以同样处理空心圆和实心圆的原因。

因此,我的第一个建议是更改灰度映射。不要使用强度,而是使用色调/饱和度/值。此外,使用差分方法 - 您正在寻找边缘。因此,计算HSV 变换,平滑副本,然后获取原始副本和平滑副本之间的差异。这将为您提供每个点的dH, dS, dV 值(色相、饱和度、值的局部变化)。平方和相加得到一维图像,在所有边缘(内部和外部)附近都有峰值。

我的第二个建议是局部标准化,但我不确定这是否有必要。这个想法是你并不特别关心你得到的边缘信号的确切值,它应该真的是二进制的(边缘与否)。因此,您可以通过除以局部平均值(其中局部是边缘大小的数量级)来标准化每个值。

【讨论】:

【参考方案5】:

如您所知,霍夫变换使用“模型”来查找(通常)边缘检测图像中的某些特征。在HoughCircles 的情况下,该模型是一个完美的圆。这意味着可能不存在可以在不增加误报数量的情况下检测图片中更不规则和椭圆形的圆圈的参数组合。另一方面,由于潜在的投票机制,一个非封闭的完美圆或一个带有“凹痕”的完美圆可能会持续出现。所以取决于你的预期输出你可能想也可能不想使用这个方法。

也就是说,我看到的一些事情可能会帮助您使用此功能:

    HoughCircles 在内部调用 Canny,所以我想你可以不去调用。 param1(你称之为HIGH)通常被初始化为200。它用作对Canny:cv.Canny(processed, cannied, HIGH, HIGH/2) 的内部调用的参数。像这样自己运行 Canny 可能会有所帮助,以了解设置 HIGH 如何影响 Hough 变换处理的图像。 param2(您称之为LOW)通常围绕值100 进行初始化。它是霍夫变换累加器的投票阈值。将其设置得更高意味着更多的误报,更低的误报。我相信这是您要开始摆弄的第一个。

参考:http://docs.opencv.org/3.0-beta/modules/imgproc/doc/feature_detection.html#houghcircles

更新 re:实心圆:在您使用 Hough 变换找到圆形形状后,您可以通过对边界颜色进行采样并将其与圆内的一个或多个点进行比较来测试它们是否被填充所谓的圈子。或者,您可以将假定圆圈内的一个或多个点与给定的背景颜色进行比较。如果前一个比较成功,则圆圈被填充,如果替代比较失败,则圆圈被填充。

【讨论】:

如果我将Canny 排除在外,那么结果会发生巨大变化。【参考方案6】:

好的,看看图片。我建议使用**Active Contours**

Active Contours 主动轮廓的好处是它们几乎完全适合任何给定的形状。无论是正方形还是三角形,在您的情况下它们都是完美的候选者。 如果您能够提取圆心,那就太好了。活动轮廓总是需要一个点开始,它们可以增长或缩小以适应。中心不必总是与中心对齐。稍微偏移一下还是可以的。 在您的情况下,如果您让轮廓从中心向外增长,它们将停留在圆形边界上。 请注意,增大或缩小的活动轮廓使用balloon energy,这意味着您可以设置轮廓的方向,向内或向外。 您可能需要使用灰度渐变图像。但你仍然可以尝试颜色。如果有效! 如果您不提供中心,请添加大量活动轮廓,然后进行增长/收缩。稳定下来的轮廓被保留,未稳定的轮廓被丢弃。这是一种蛮力方法。会占用CPU。但需要更仔细的工作,以确保留下正确的轮廓并丢弃坏的轮廓。

我希望这样你可以解决问题。

【讨论】:

由于某种原因 cvSnakes 已从 OpenCV 中删除。不过,您仍然可以链接到旧版库。 你说的是哪个版本的 OpenCV? 2.something。在其他关于 cvSnakes 的问题中提到过。

以上是关于使用 OpenCV(基于霍夫变换或其他特征)编写鲁棒的(颜色和大小不变)圆检测的主要内容,如果未能解决你的问题,请参考以下文章

『OpenCV3』霍夫变换

20opencv入门霍夫变换:霍夫线变换,霍夫圆变换合辑

霍夫变换和openCV

Python+opencv 机器视觉 - 基于霍夫圈变换算法检测图像中的圆形实例演示

Python+opencv 机器视觉 - 基于霍夫圈变换算法检测图像中的圆形实例演示

opencv-霍夫直线变换与圆变换