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

Ash*_*all 4 python opencv image-processing

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

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

以下是一个很好的例子: 良好的源图像: 良好的源图像 提取轮廓后: 提取轮廓后 检测到斑点: 检测到斑点

当它变得很糟糕时(你可以看到它错误地将椭圆覆盖在三个blob上,并检测到一个我不想要的): 错误的源图像: 糟糕的源图像 提取轮廓后: 提取轮廓后 检测到斑点: 检测到斑点

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

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)
Run Code Online (Sandbox Code Playgroud)

非常感谢你阅读这篇文章,我真诚地希望有人可以帮助我,或指出我正确的方向.谢谢!

alk*_*asm 7

斑点探测器

目前,您的实施是多余的.来自SimpleBlobDetector()文档:

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

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

所以你已经实现了部分步骤,这可能会带来一些意想不到的行为.您可以尝试使用参数来查看是否可以找出适合您的参数(尝试创建跟踪条以使用参数并使用不同的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)
Run Code Online (Sandbox Code Playgroud)

检测到质心

更多参与

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

gif是我的意思的动画.请注意,gif轮廓分离后停止:

轮廓区域缩小直至相似

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

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)
Run Code Online (Sandbox Code Playgroud)

发现了质心

请注意,这里我通过与所有可能的阈值循环range(0, 255)0, 1, ..., 254,但真正你可以开始提高,并通过在用,比如一个时间几个值跳跃,range(50, 200, 5)50, 55, ..., 195这当然会快得多.