【问题标题】:Image processing - fill in hollow circles图像处理 - 填充空心圆圈
【发布时间】:2019-01-24 02:22:59
【问题描述】:

我有一个看起来像这样的二进制黑白图像

我想将那些白色圆圈填充为纯白色圆盘。如何在Python 中执行此操作,最好使用skimage

【问题讨论】:

    标签: python image-processing computer-vision scikit-image


    【解决方案1】:

    您可以使用 skimage 的方法 hough_circlehough_circle_peaks 检测圆圈,然后在它们上绘制以“填充”它们。

    在下面的示例中,大部分代码都在为最合适的圆进行“层次”计算,以避免绘制一个在另一个内部的圆:

    # skimage version 0.14.0
    
    import math
    import numpy as np
    import matplotlib.pyplot as plt
    
    from skimage import color
    from skimage.io import imread
    from skimage.transform import hough_circle, hough_circle_peaks
    from skimage.feature import canny
    from skimage.draw import circle
    from skimage.util import img_as_ubyte
    
    INPUT_IMAGE = 'circles.png' # input image name
    BEST_COUNT = 6              # how many circles to draw
    MIN_RADIUS = 20             # min radius should be bigger than noise
    MAX_RADIUS = 60             # max radius of circles to be detected (in pixels)
    LARGER_THRESH = 1.2         # circle is considered significantly larger than another one if its radius is at least so much bigger
    OVERLAP_THRESH = 0.1        # circles are considered overlapping if this part of the smaller circle is overlapping
    
    def circle_overlap_percent(centers_distance, radius1, radius2):
        '''
        Calculating the percentage area overlap between circles
        See Gist for comments:
            https://gist.github.com/amakukha/5019bfd4694304d85c617df0ca123854
        '''
        R, r = max(radius1, radius2), min(radius1, radius2)
        if centers_distance >= R + r:
            return 0.0
        elif R >= centers_distance + r:
            return 1.0
        R2, r2 = R**2, r**2
        x1 = (centers_distance**2 - R2 + r2 )/(2*centers_distance)
        x2 = abs(centers_distance - x1)
        y = math.sqrt(R2 - x1**2)
        a1 = R2 * math.atan2(y, x1) - x1*y
        if x1 <= centers_distance:
            a2 = r2 * math.atan2(y, x2) - x2*y
        else:
            a2 = math.pi * r2 - a2
        overlap_area = a1 + a2
        return overlap_area / (math.pi * r2)
    
    def circle_overlap(c1, c2):
        d = math.sqrt((c1[0]-c2[0])**2 + (c1[1]-c2[1])**2)
        return circle_overlap_percent(d, c1[2], c2[2])
    
    def inner_circle(cs, c, thresh):
        '''Is circle `c` is "inside" one of the `cs` circles?'''
        for dc in cs:
            # if new circle is larger than existing -> it's not inside
            if c[2] > dc[2]*LARGER_THRESH: continue
            # if new circle is smaller than existing one...
            if circle_overlap(dc, c)>thresh:
                # ...and there is a significant overlap -> it's inner circle
                return True
        return False
    
    # Load picture and detect edges
    image = imread(INPUT_IMAGE, 1)
    image = img_as_ubyte(image)
    edges = canny(image, sigma=3, low_threshold=10, high_threshold=50)
    
    # Detect circles of specific radii
    hough_radii = np.arange(MIN_RADIUS, MAX_RADIUS, 2)
    hough_res = hough_circle(edges, hough_radii)
    
    # Select the most prominent circles (in order from best to worst)
    accums, cx, cy, radii = hough_circle_peaks(hough_res, hough_radii)
    
    # Determine BEST_COUNT circles to be drawn
    drawn_circles = []
    for crcl in zip(cy, cx, radii):
        # Do not draw circles if they are mostly inside better fitting ones
        if not inner_circle(drawn_circles, crcl, OVERLAP_THRESH):
            # A good circle found: exclude smaller circles it covers
            i = 0
            while i<len(drawn_circles):
                if circle_overlap(crcl, drawn_circles[i]) > OVERLAP_THRESH:
                    t = drawn_circles.pop(i)
                else:
                    i += 1
            # Remember the new circle
            drawn_circles.append(crcl)
        # Stop after have found more circles than needed
        if len(drawn_circles)>BEST_COUNT:
            break
    
    drawn_circles = drawn_circles[:BEST_COUNT]
    
    # Actually draw circles
    colors  = [(250, 0, 0), (0, 250, 0), (0, 0, 250)]
    colors += [(200, 200, 0), (0, 200, 200), (200, 0, 200)]
    fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(10, 4))
    image = color.gray2rgb(image)
    for center_y, center_x, radius in drawn_circles:
        circy, circx = circle(center_y, center_x, radius, image.shape)
        color = colors.pop(0)
        image[circy, circx] = color
        colors.append(color)
    
    ax.imshow(image, cmap=plt.cm.gray)
    plt.show()
    

    结果:

    【讨论】:

      【解决方案2】:

      做一个morphological closing (explanation) 来填补那些微小的空白,完成圆圈。然后fill得到二值图像。

      代码:

      from skimage import io
      from skimage.morphology import binary_closing, disk
      import scipy.ndimage as nd
      import matplotlib.pyplot as plt
      
      # Read image, binarize
      I = io.imread("FillHoles.png")
      bwI =I[:,:,1] > 0
      
      fig=plt.figure(figsize=(24, 8))
      
      # Original image
      fig.add_subplot(1,3,1)
      plt.imshow(bwI, cmap='gray')
      
      # Dilate -> Erode. You might not want to use a disk in this case,
      # more asymmetric structuring elements might work better
      strel = disk(4)
      I_closed = binary_closing(bwI, strel)
      
      # Closed image
      fig.add_subplot(1,3,2)
      plt.imshow(I_closed, cmap='gray')
      
      I_closed_filled = nd.morphology.binary_fill_holes(I_closed)
      
      # Filled image
      fig.add_subplot(1,3,3)
      plt.imshow(I_closed_filled, cmap='gray')
      

      结果:

      请注意分割垃圾如何融合到右下角的对象中,并且中间对象下部的小斗篷已关闭。您可能希望在此之后继续进行形态侵蚀或开放。

      编辑:对以下 cmets 的长响应

      disk(4) 只是我用来生成图像中结果的示例。您需要自己找到合适的值。太大的值会导致小对象融合到它们附近的较大对象中,例如图像中的右侧簇。无论您是否愿意,它还将缩小对象之间的间隙。值太小会导致算法无法完成圆圈,因此填充操作会失败。

      形态侵蚀将从对象的边界擦除结构元素大小的区域。形态开是闭的逆运算,所以不是扩张->侵蚀,而是侵蚀->扩张。打开的最终效果是所有小于结构元素的对象和斗篷都将消失。如果您在填充后执行此操作,则大对象将保持相对不变。理想情况下,它应该消除很多由我在代码示例中使用的形态关闭引起的分割伪影,根据您的应用程序,这可能与您相关,也可能不相关。

      【讨论】:

      • 您能否详细说明一下形态侵蚀和开放,它们是做什么的,我将它们用于什么?另外,不知道你指的是什么右下角的物体,能否请你在原图中用红色或其他东西标出它,以便我看到它?
      • 另外,您使用的是硬编码磁盘(4),一般情况下可以吗?
      • 我只是在一般图像上尝试过,但它没有检测到所有圆圈。我该如何解决这个问题,也许我把它放在一个循环中并在磁盘(r)中为 r 尝试不同的值?
      【解决方案3】:

      我不知道skimage,但如果你使用OpenCv,我会对圆圈进行霍夫变换,然后将它们画出来。

      霍夫变换是健壮的,如果圆圈上有一些小洞也没问题。

      类似:

      circles = cv2.HoughCircles(gray, cv2.cv.CV_HOUGH_GRADIENT, 1.2, 100)
      
      # ensure at least some circles were found
      if circles is not None:
          # convert the (x, y) coordinates and radius of the circles to integers
          circles = np.round(circles[0, :]).astype("int")
      
          # loop over the (x, y) coordinates and radius of the circles
          # you can check size etc here.
          for (x, y, r) in circles:
              # draw the circle in the output image
              # you can fill here.
              cv2.circle(output, (x, y), r, (0, 255, 0), 4)
      
          # show the output image
          cv2.imshow("output", np.hstack([image, output]))
          cv2.waitKey(0)
      

      在此处查看更多信息:https://www.pyimagesearch.com/2014/07/21/detecting-circles-images-using-opencv-hough-circles/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-05-15
        • 2019-01-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-03
        • 1970-01-01
        相关资源
        最近更新 更多