我正在尝试使用Kmeans通过以下代码分割肺部CT图像:
def process_mask(mask):
convex_mask = np.copy(mask)
for i_layer in range(convex_mask.shape[0]):
mask1 = np.ascontiguousarray(mask[i_layer])
if np.sum(mask1)>0:
mask2 = convex_hull_image(mask1)
if np.sum(mask2)>2*np.sum(mask1):
mask2 = mask1
else:
mask2 = mask1
convex_mask[i_layer] = mask2
struct = generate_binary_structure(3,1)
dilatedMask = binary_dilation(convex_mask,structure=struct,iterations=10)
return dilatedMask
def lumTrans(img):
lungwin = np.array([-1200.,600.])
newimg = (img-lungwin[0])/(lungwin[1]-lungwin[0])
newimg[newimg<0]=0
newimg[newimg>1]=1
newimg = (newimg*255).astype('uint8')
return newimg
def lungSeg(imgs_to_process,output,name):
if os.path.exists(output+'/'+name+'_clean.npy') : return
imgs_to_process = Image.open(imgs_to_process)
img_to_save = imgs_to_process.copy()
img_to_save = np.asarray(img_to_save).astype('uint8')
imgs_to_process = lumTrans(imgs_to_process)
imgs_to_process = np.expand_dims(imgs_to_process, axis=0)
x,y,z = imgs_to_process.shape
img_array = imgs_to_process.copy()
A1 = int(y/(512./100))
A2 = int(y/(512./400))
A3 = int(y/(512./475))
A4 = int(y/(512./40))
A5 = int(y/(512./470))
for i in range(len(imgs_to_process)):
img = imgs_to_process[i]
print(img.shape)
x,y = img.shape
#Standardize the pixel values
allmean = np.mean(img)
allstd = np.std(img)
img = img-allmean
img = img/allstd
# Find the average pixel value near the lungs
# to renormalize washed out images
middle = img[A1:A2,A1:A2]
mean = np.mean(middle)
max = np.max(img)
min = np.min(img)
kmeans = KMeans(n_clusters=2).fit(np.reshape(middle,[np.prod(middle.shape),1]))
centers = sorted(kmeans.cluster_centers_.flatten())
threshold = np.mean(centers)
thresh_img = np.where(img<threshold,1.0,0.0) # threshold the image
eroded = morphology.erosion(thresh_img,np.ones([4,4]))
dilation = morphology.dilation(eroded,np.ones([10,10]))
labels = measure.label(dilation)
label_vals = np.unique(labels)
regions = measure.regionprops(labels)
good_labels = []
for prop in regions:
B = prop.bbox
if B[2]-B[0]<A3 and B[3]-B[1]<A3 and B[0]>A4 and B[2]<A5:
good_labels.append(prop.label)
mask = np.ndarray([x,y],dtype=np.int8)
mask[:] = 0
for N in good_labels:
mask = mask + np.where(labels==N,1,0)
mask = morphology.dilation(mask,np.ones([10,10])) # one last dilation
imgs_to_process[i] = mask
m1 = imgs_to_process
convex_mask = m1
dm1 = process_mask(m1)
dilatedMask = dm1
Mask = m1
extramask = dilatedMask ^ Mask
bone_thresh = 180
pad_value = 0
img_array[np.isnan(img_array)]=-2000
sliceim = img_array
sliceim = sliceim*dilatedMask+pad_value*(1-dilatedMask).astype('uint8')
bones = sliceim*extramask>bone_thresh
sliceim[bones] = pad_value
x,y,z = sliceim.shape
if not os.path.exists(output):
os.makedirs(output)
img_to_save[sliceim.squeeze()==0] = 0
im = Image.fromarray(img_to_save)
im.save(output + name + '.png', 'PNG')
问题是分割的肺仍然包含如下白色边界:
分段肺(输出):
未分段肺(输入):
完整的代码可以在GoogleColab笔记本中找到code
数据集的样本是here
对于这个问题,我不建议使用Kmeans颜色量化,因为这种技术通常只适用于存在各种颜色的情况,并且您希望将它们分割为主色块。看看这个previous answer的典型用例。由于您的CT扫描图像是灰度的,Kmeans的性能不太好。下面是一个使用OpenCV的简单图像处理的潜在解决方案:
获取二进制图像。Load input image,转换为grayscale、Otsu's threshold和find contours
创建空白遮罩以提取所需对象。我们可以使用^{} 创建与输入图像大小相同的空掩码
使用轮廓面积和纵横比过滤轮廓。我们通过确保轮廓在指定的区域阈值内以及aspect ratio来搜索肺部对象。我们使用^{} 、^{} 和^{} 进行轮廓周长和轮廓形状近似。如果我们已经找到了我们的lung对象,我们将利用^{} 用白色填充遮罩,以表示我们要提取的对象
按位和原始图像掩码。最后,我们将掩码转换为灰度、按位和^{} 以获得结果
下面是我们逐步可视化的图像处理管道:
灰度
->
大津阈值检测到要提取的对象以绿色
->
填充遮罩突出显示按位and获得结果
->
可选结果,背景为白色代码
解决这个问题的一个更简单的方法是使用形态侵蚀。只是你需要调整阈值
相关问题 更多 >
编程相关推荐