У меня есть двоичные черно-белые изображения, которые выглядят так
Я хочу заполнить эти белые круги сплошными белыми дисками. Как я могу сделать это в Python
, предпочтительно используя skimage
?
У меня есть двоичные черно-белые изображения, которые выглядят так
Я хочу заполнить эти белые круги сплошными белыми дисками. Как я могу сделать это в Python
, предпочтительно используя skimage
?
Выполните морфологическое закрытие (explanation), чтобы заполнить эти крошечные пробелы, чтобы завершить круги. Затем заполните результирующее бинарное изображение.
Код :
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_circle
и hough_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()
Результат:
Я не знаю 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/