scikit-image库中的图像分割方法概述

门槛


通过选择高于或低于某个阈值的像素,这是将对象与背景分离的最简单方法。 当我们要按对象背景分割对象时,这通常很有用。 您可以在此处阅读有关阈值的更多信息。

熟悉电影《终结者》的人可能会同意,这是该时代最伟大的科幻电影。 在影片中,詹姆斯·卡梅隆(James Cameron)提出了一个有趣的视觉效果概念,使观看者可以隐藏在名为终结者(Terminator)的电子人的眼睛后面。 这种效果已被称为“终结者视觉”(英语Terminator Vision)。 从某种意义上说,他将人物的轮廓与背景分离开来。 那时听起来可能完全不合适,但是图​​像分割是当今许多图像处理技术的重要组成部分。

图像分割


有许多用于图像分析的库。 在本文中,我们将详细讨论python图像处理库scikit-image。

Scikit图片


图片

Scikit-image是用于图像处理的Python库。

安装方式


scikit-image的安装如下:

pip install -U scikit-image(Linux and OSX) pip install scikit-image(Windows) # For Conda-based distributions conda install scikit-image 



Python映像概述


在深入了解图像分割的技术方面之前,一定要熟悉Scikit图像生态系统及其如何处理图像。

从skimage库导入GrayScale图像


skimage数据模块包含几个内置的数据集示例,这些示例通常以jpeg或png格式存储。

 from skimage import data import numpy as np import matplotlib.pyplot as plt image = data.binary_blobs() plt.imshow(image, cmap='gray') 

从skimage库导入彩色图像


 from skimage import data import numpy as np import matplotlib.pyplot as plt image = data.astronaut() plt.imshow(image) 



从外部来源导入图像


 # The I/O module is used for importing the image from skimage import data import numpy as np import matplotlib.pyplot as plt from skimage import io image = io.imread('skimage_logo.png') plt.imshow(image); 



下载多张图片


 images = io.ImageCollection('../images/*.png:../images/*.jpg') print('Type:', type(images)) images.files Out[]: Type: <class 'skimage.io.collection.ImageCollection'> 

保存图像


 #Saving file as 'logo.png' io.imsave('logo.png', logo) 

图像分割


现在我们有了scikit-image的概念,我们考虑图像分割的细节。 图像分割是将数字图像分为几个部分,以简化和/或将图像表示更改为更有意义且更易于分析的过程。

在本文中,我们将考虑在有老师(有监督)和没有老师(无监督)的情况下教授模型的算法。


scikit图像库中提供了一些分割算法。

与老师进行细分:一些初步的知识(可能来自人工输入)可用于指导算法。

在没有老师的情况下进行细分:不需要先验知识。 这些算法尝试将图像自动划分为重要区域。 用户仍然可以配置某些参数以获得所需的结果。

让我们在预定义的scikit-image数据集随附的教程图像上尝试一下。

定期进口


 import numpy as np import matplotlib.pyplot as plt import skimage.data as data import skimage.segmentation as seg import skimage.filters as filters import skimage.draw as draw import skimage.color as color 

简单的成像功能

 def image_show(image, nrows=1, ncols=1, cmap='gray'): fig, ax = plt.subplots(nrows=nrows, ncols=ncols, figsize=(14, 14)) ax.imshow(image, cmap='gray') ax.axis('off') return fig, ax 

图片


 text = data.page() image_show(text) 



该图像较暗,但也许我们仍然可以选择一个值,该值将为我们提供合理的分割,而无需任何复杂的算法。 现在,为了帮助我们选择此值,我们将使用直方图。

在这种情况下,直方图显示在此图像中发现的具有不同强度的图像中的像素数。 简而言之,直方图是一个图形,其中X轴显示图像中的所有值,Y轴显示这些值的频率。

 fig, ax = plt.subplots(1, 1) ax.hist(text.ravel(), bins=32, range=[0, 256]) ax.set_xlim(0, 256); 



我们的示例结果是一个8位图像,因此在X轴上有256个可能的值,直方图显示有相当亮的像素(0:黑色,255:白色)。 这很可能是我们漂亮的浅色文本背景,但其余部分有点模糊。 理想的分割直方图将是双峰的,因此我们可以在中间选择一个数字。 现在,让我们尝试基于简单的阈值创建一些分割的图像。

受控阈值


由于我们自己选择阈值,因此我们将其称为受控阈值。

 text_segmented = text > (value concluded from histogram ie 50,70,120 ) image_show(text_segmented); 


左:文字> 50 | 中:文字> 70 | 右:文字> 120

我们没有得到理想的结果,因为左侧的阴影会产生问题。 让我们尝试现在无人看管的阈值。

阈值不受控制


不受控制的阈值Scikit图像具有许多自动阈值确定方法,在选择最佳阈值时不需要输入。 以下是一些方法:otsu,li,local。

 text_threshold = filters.threshold_ # Hit tab with the cursor after the underscore to get all the methods. image_show(text < text_threshold); 


左大津|| 右:李

对于本地,我们还需要指定block_size。 偏移有助于调整图像以获得更好的效果。

 text_threshold = filters.threshold_local(text,block_size=51, offset=10) image_show(text > text_threshold); 



这种方法效果很好。 在很大程度上,可以摆脱嘈杂的区域。

带有老师的模型的算法分割


阈值化是一个非常简单的分割过程,在高对比度图像上无法正常使用,为此我们将需要更高级的工具。

在本节中,我们将使用免费提供的示例图像,并尝试使用与老师联系的方法对头部进行分割。

 # import the image from skimage import io image = io.imread('girl.jpg') plt.imshow(image); 



在进行任何图像分割之前,建议使用一些滤镜将其去除。

但是,在我们的情况下,图像没有明显的噪点,因此我们将按原样接受它。 下一步是使用rgb2gray将图像转换为灰度。

 image_gray = color.rgb2gray(image) image_show(image_gray); 



我们将使用两种基于完全不同原理的细分方法。

主动轮廓分割


活动路径的分段也称为蛇形,使用用户定义的感兴趣区域周围的路径或直线进行初始化,然后缓慢压缩该路径,并用光和边缘吸引或排斥该路径。

对于我们的示例图像,让我们在人头周围画一个圆以初始化蛇。

 def circle_points(resolution, center, radius): """ Generate points which define a circle on an image.Centre refers to the centre of the circle """ radians = np.linspace(0, 2*np.pi, resolution) c = center[1] + radius*np.cos(radians)#polar co-ordinates r = center[0] + radius*np.sin(radians) return np.array([c, r]).T # Exclude last point because a closed path should not have duplicate points points = circle_points(200, [80, 250], 80)[:-1] 

上面的计算将计算圆的圆周上的点的x和y坐标。 由于我们给出的分辨率为200,因此它将计算200个这样的点。

 fig, ax = image_show(image) ax.plot(points[:, 0], points[:, 1], '--r', lw=3) 



然后,该算法从图像的其余部分中分割出人的脸部,将闭合曲线拟合到脸部边缘。



我们可以配置称为alpha和beta的参数。 较高的alpha值会使曲线收缩更快,而beta会使曲线更平滑。

 snake = seg.active_contour(image_gray, points,alpha=0.06,beta=0.3) fig, ax = image_show(image) ax.plot(points[:, 0], points[:, 1], '--r', lw=3) ax.plot(snake[:, 0], snake[:, 1], '-b', lw=3); 



随机沃克分割


在这种方法中,使用交互式标签(称为标签)进行分段。 通过将每个像素绘制到计算出最高概率的标签上,可以获得高质量的图像分割。 在工作中可以找到有关此方法的更多详细信息。

接下来,我们将再次使用示例中的先前值。 我们可以进行不同的初始化,但是为简单起见,我们坚持使用圆的原理。

 image_labels = np.zeros(image_gray.shape, dtype=np.uint8) 

随机通过算法接受标签作为输入。 因此,我们将有一个大圆圈覆盖整个人脸,而另一个小圆圈则位于人脸中间。

 indices = draw.circle_perimeter(80, 250,20)#from here image_labels[indices] = 1 image_labels[points[:, 1].astype(np.int), points[:, 0].astype(np.int)] = 2 image_show(image_labels); 


现在,让我们使用Random Walker看看会发生什么。

 image_segmented = seg.random_walker(image_gray, image_labels) # Check our results fig, ax = image_show(image_gray) ax.imshow(image_segmented == 1, alpha=0.3); 



结果不是最好的,脸部边缘仍未触及。 为了纠正这种情况,我们可以调整通道参数,直到获得所需的结果。 经过几次尝试,我们将该值设置为3000,效果很好。

 image_segmented = seg.random_walker(image_gray, image_labels, beta = 3000) # Check our results fig, ax = image_show(image_gray) ax.imshow(image_segmented == 1, alpha=0.3); 



这一切都是为了与老师进行细分,我们必须在其中提供某些输入数据并配置一些参数。 但是,一个人并不总是能够看到图像然后决定要做出什么贡献以及从哪里开始。 幸运的是,对于这种情况,我们拥有不受控制的分割方法。

没有老师的细分


在没有老师的情况下进行细分不需要先验知识。 考虑一个太大的图像,以至于不可能一次看到所有像素。 因此,在这种情况下,没有老师的分割可以将图像分成几个子区域,因此,您可以拥有数十个或数百个区域,而不是数百万个像素。 让我们看两个这样的算法:

简单线性迭代聚类


该方法(英文简单线性迭代聚类或SLIC)使用一种称为K-Means的机器学习算法。 它获取图像的所有像素值,然后尝试将它们划分为给定数量的子域。 阅读作品以获取详细信息。

SLIC使用不同的颜色,因此我们将使用原始图像。

 image_slic = seg.slic(image,n_segments=155) 

我们要做的只是为找到的每个片段设置一个平均值,这使其看起来更像图像。

 # label2rgb replaces each discrete label with the average interior color image_show(color.label2rgb(image_slic, image, kind='avg')); 



我们将此图片从512 * 512 = 262,000像素减少到155个细分。

费尔岑施瓦布


此方法还使用称为最小生成树聚类的机器学习算法。 Felzenszwaib没有告诉我们将图像划分为的确切簇数。 他将为此生成尽可能多的簇。

 image_felzenszwalb = seg.felzenszwalb(image) image_show(image_felzenszwalb); 



图片中的区域过多。 让我们计算唯一细分的数量。

 np.unique(image_felzenszwalb).size 3368 

现在,像在SLIC算法中一样,使用段上的平均值为它们重新着色。

 image_felzenszwalb_colored = color.label2rgb(image_felzenszwalb, image, kind='avg') image_show(image_felzenszwalb_colored); 

现在我们得到的细分更少了。 如果我们想要更少的细分,则可以更改scale参数。 这种方法有时称为过度细分。



这更像是海报图像,本质上只是减少了颜色数量。 重新组合它们(RAG)。

结论


图像分割是图像处理中非常重要的一步。 这是一个活跃的研究领域,具有各种应用程序,从计算机视觉到医学图像,交通和视频监控。 Python提供了一个强大的scikit-image库,其中包含大量的图像处理算法。 它是免费提供的,不受任何限制,并由活跃的社区提供支持。 我建议您阅读他们的文档。 原始文章可以在这里找到。

Source: https://habr.com/ru/post/zh-CN441006/


All Articles