问题描述
假设一条直线相交成直角。
并且您想对其进行骨架化以获得(您希望)一个十字形状。相反,使用 sklearn.morphology.skeletonize
获得以下图像:
我们称之为“有洞的十字架”。
另一方面,您有 OpenCV 和 OpenCV 骨架化功能,这些功能在互联网上的几个博客和答案中随处可见:
def skeletonize(bin: numpy.ndarray,erosion_shape=cv2.MORPH_RECT,kernel_sz: Union[int,Tuple[int,int]] = 3):
kernel_sz = fix_kernel(kernel_sz)
kernel = cv2.getStructuringElement(erosion_shape,kernel_sz)
thresh = bin.copy()
skeleton = numpy.zeros_like(bin)
eroded = numpy.zeros_like(bin)
carry = numpy.zeros_like(bin)
while (True):
cv2.erode(thresh,kernel,dst=eroded)
cv2.dilate(eroded,dst=carry)
cv2.subtract(thresh,carry,dst=carry)
cv2.bitwise_or(skeleton,dst=skeleton)
thresh,eroded = eroded,thresh
if cv2.countNonZero(thresh) == 0:
return skeleton
这会产生以下结果:
因此,浮动的基本 OpenCV 骨架化功能存在错误或错误,并且无法使用结构化形状修改 Skimage 骨架化。
有没有办法在python中获得骨架化的十字/加号形状?
解决方法
正如我在评论中指出的,您可以通过拟合霍夫线来清理骨架图像中的交叉点:
#!/usr/bin/env python
"""
https://stackoverflow.com/q/66995948/2912349
"""
import numpy as np
import matplotlib.pyplot as plt
from skimage.morphology import skeletonize
from skimage.transform import probabilistic_hough_line
from skimage.draw import line as get_line_pixels
img = np.zeros((20,20))
img[4:16,6:14] = 1
img[:,10] = 1
img[10,:] = 1
skel = skeletonize(img)
lines = probabilistic_hough_line(skel,line_length=10)
# hough_line() returns the start and endpoint of the fitted lines;
# we need all pixels covered by that line;
cleaned = np.zeros_like(img)
for ((r0,c0),(r1,c1)) in lines:
rr,cc = get_line_pixels(r0,c0,r1,c1)
cleaned[rr,cc] = 1
fig,axes = plt.subplots(1,3,sharex=True,sharey=True)
axes[0].imshow(img,cmap='gray')
axes[0].set_title('Raw')
axes[1].imshow(skel,cmap='gray')
axes[1].set_title('Skeleton')
axes[2].imshow(cleaned,cmap='gray')
axes[2].set_title('Hough lines')
plt.show()
如果您想强制水平或垂直适合,可以简单地过滤 lines
以排除非水平和非垂直线:
for ((r0,c1)) in lines:
if (r0 == r1) or (c0 == c1):
...