如何找到这些有时重叠的圆圈的中心

时间:2017-08-14 23:44:10

标签: python opencv image-processing

作为我正在研究的项目的一部分,我需要使用OpenCV和Python在图像中找到一些“blob”的中心点。 我遇到了一些麻烦,真的很感激任何帮助或见解:)

我目前的方法是:获取图像的轮廓,在这些图像上叠加椭圆,使用斑点检测器找到每个图像的中心。 这种效果相当不错,但偶尔我还需要忽略无关的斑点,有时斑点会相互碰触。

这是一个很好的例子: 良好的源图片 Good source image 提取轮廓后 After extracting contours 检测到blob: With the blobs detected

当它变得很糟糕时(你可以看到它在三个blob上错误地叠加了一个椭圆,并检测到一个我不想要的): 错误的来源图片 Bad source image 提取轮廓后 After extracting contours 检测到blob: With the blobs detected

这是我目前使用的代码。我不确定任何其他选择。

def process_and_detect(img_path):
    img = cv2.imread(path)
    imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    ret, thresh = cv2.threshold(imgray, 50, 150, 0)
    im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    drawn_img = np.zeros(img.shape, np.uint8)
    min_area = 50
    min_ellipses = []
    for cnt in contours:
        if cv2.contourArea(cnt) >= min_area:
            ellipse = cv2.fitEllipse(cnt)
            cv2.ellipse(drawn_img,ellipse,(0,255,0),-1)
    plot_img(drawn_img, size=12)

    # Change thresholds
    params = cv2.SimpleBlobDetector_Params()
    params.filterByColor = True
    params.blobColor = 255
    params.filterByCircularity = True
    params.minCircularity = 0.75
    params.filterByArea = True
    params.minArea = 150
    # Set up the detector
    detector = cv2.SimpleBlobDetector_create(params)

    # Detect blobs.
    keypoints = detector.detect(drawn_img)
    for k in keypoints:
        x = round(k.pt[0])
        y = round(k.pt[1])
        line_length = 20
        cv2.line(img, (x-line_length, y), (x+line_length, y), (255, 0, 0), 2)
        cv2.line(img, (x, y-line_length), (x, y+line_length), (255, 0, 0), 2)
    plot_img(img, size=12)

非常感谢您阅读这篇文章,我衷心希望有人能帮助我,或指出我正确的方向。谢谢!

2 个答案:

答案 0 :(得分:6)

Blob探测器

目前,您的实施是多余的。来自SimpleBlobDetector() docs

  

该类实现了一种从图像中提取blob的简单算法:

     
      
  1. 将源图像转换为二进制图像,方法是应用阈值从minThreshold(包括)到maxThreshold(不包括)的阈值,其中相邻阈值之间的距离为thresholdStep。
  2.   
  3. 通过findContours()从每个二进制图像中提取连接的组件并计算它们的中心。
  4.   
  5. 根据坐标从多个二进制图像中心分组。关闭中心形成一个对应于一个blob的组,该组由minDistBetweenBlobs参数控制。
  6.   
  7. 从小组中,估计斑点及其半径的最终中心,并返回关键点的位置和大小。
  8.   

因此,您已经实施了部分步骤,这可能会产生一些意想不到的行为。您可以尝试使用参数来查看是否可以找出适合您的参数(尝试创建trackbars以使用参数并使用不同的blob检测器参数获取算法的实时结果)。

修改管道

但是,您已经编写了大部分自己的管道,因此您可以轻松删除blob检测器并实现自己的算法。如果您只是略微降低阈值,您可以轻松获得清晰标记的圆圈,然后斑点检测就像轮廓检测一样简单。如果每个斑点都有一个单独的轮廓,则可以使用moments()计算轮廓的质心。例如:

def process_and_detect(img_path):

    img = cv2.imread(img_path)
    imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    ret, thresh = cv2.threshold(imgray, 100, 255, cv2.THRESH_BINARY)

    contours = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[1]
    line_length = 20
    for c in contours:
        if cv2.contourArea(c) >= min_area:
            M = cv2.moments(c)
            x = int(M['m10']/M['m00'])
            y = int(M['m01']/M['m00']) 
            cv2.line(img, (x-line_length, y), (x+line_length, y), (255, 0, 0), 2)
            cv2.line(img, (x, y-line_length), (x, y+line_length), (255, 0, 0), 2)

Detected centroids

更多参与

此管道可用于自动循环阈值,因此您无需猜测和硬编码这些值。由于斑点看起来大致大致相同,因此您可以循环直到所有轮廓具有大致相同的区域。你可以这样做,例如通过查找中位轮廓大小,定义您允许的上下限度的某个百分比,并检查检测到的所有轮廓是否适合这些边界。

这是我的意思的动画gif。请注意,一旦轮廓分离,gif就会停止:

Contour areas shrinking until similar

然后你可以简单地找到那些分开的轮廓的质心。这是代码:

def process_and_detect(img_path):

    img = cv2.imread(img_path)
    imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    for thresh_val in range(0, 255):

        # threshold and detect contours
        thresh = cv2.threshold(imgray, thresh_val, 255, cv2.THRESH_BINARY)[1]
        contours = cv2.findContours(thresh,
                                    cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[1]

        # filter contours by area
        min_area = 50
        filtered_contours = [c for c in contours
                             if cv2.contourArea(c) >= min_area]
        area_contours = [cv2.contourArea(c) for c in filtered_contours]

        # acceptable deviation from median contour area
        median_area = np.median(area_contours)
        dev = 0.3
        lowerb = median_area - dev*median_area
        upperb = median_area + dev*median_area

        # break when all contours are within deviation from median area
        if ((area_contours > lowerb) & (area_contours < upperb)).all():
            break

    # draw center location of blobs
    line_length = 8
    cross_color = (255, 0, 0)
    for c in filtered_contours:
        M = cv2.moments(c)
        x = int(M['m10']/M['m00'])
        y = int(M['m01']/M['m00'])
        cv2.line(img, (x-line_length, y), (x+line_length, y), cross_color, 2)
        cv2.line(img, (x, y-line_length), (x, y+line_length), cross_color, 2)

Centroids found

请注意,我在这里使用range(0, 255)循环显示所有可能的阈值,以提供0, 1, ..., 254,但实际上您可以开始更高并一次跳过几个值,例如range(50, 200, 5)得到50, 55, ..., 195当然要快得多。

答案 1 :(得分:2)

&#34;标准&#34;这种斑点分裂问题的方法是通过分水岭变换。它可以应用于二进制图像,使用变换距离,或直接应用于灰度图像。

过度分割问题可能会让它变得棘手,但似乎你的情况不会受到影响。

为了找到中心,我通常建议使用像素坐标的加权平均值来获得降噪效果,但在这种情况下,我可能会选择最大强度的位置,这不会受到影响通过形状的变形。

以下是灰度分水岭的结果(区域强度是平均值)。与我最初的想法相反,由于斑点的不规则性存在一些碎片

enter image description here

在分割之前,您可以通过一些低通滤波来改进。

enter image description here

相关问题