图像处理-填充空心圆圈

Image processing - fill in hollow circles

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

我想把那些白色圆圈填成实心的白色圆盘。我如何在 Python 中执行此操作,最好使用 skimage?

我不知道 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/

做一个morphological closing (explanation) to fill those tiny gaps, to complete the circles. Then 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')

结果:

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

编辑:对以下评论的长回复

磁盘 (4) 只是我用来生成图像中所见结果的示例。您将需要自己找到合适的值。太大的值会导致小物体被融合到附近的更大物体中,就像图像右侧的集群一样。它还会缩小对象之间的间隙,无论您是否需要。太小的值会导致算法无法完成圆圈,因此填充操作将失败。

形态侵蚀将从对象的边界擦除结构元素大小的区域。形态学开运算是闭运算的逆运算,所以不是扩张->侵蚀,而是侵蚀->扩张。打开的净效果是所有小于结构元素的对象和斗篷都将消失。如果你在填充后这样做,那么大的物体将保持相对不变。理想情况下,它应该消除很多由我在代码示例中使用的形态闭合引起的分割伪影,这可能与您相关,也可能不相关,具体取决于您的应用程序。

您可以使用 skimage 的方法 hough_circlehough_circle_peaks 检测圆圈,然后将它们绘制到 "fill" 它们。

在下面的示例中,大部分代码都在 "hierarchy" 计算最合适的圆,以避免绘制一个在另一个圆中的圆:

# 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()

结果: