形态学处理,除了最基本的膨胀、腐蚀、开/闭运算、黑/白帽处理外,还有一些更高级的运用,如凸包,连通区域标记,删除小块区域等。
1、凸包
凸包是指一个凸多边形,这个凸多边形将图片中所有的白色像素点都包含在内。
函数为:
1
|
skimage.morphology.convex_hull_image(image) |
输入为二值图像,输出一个逻辑二值图像。在凸包内的点为true, 否则为false
例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import matplotlib.pyplot as plt from skimage import data,color,morphology #生成二值测试图像 img = color.rgb2gray(data.horse()) img = (img< 0.5 ) * 1 chull = morphology.convex_hull_image(img) #绘制轮廓 fig, axes = plt.subplots( 1 , 2 ,figsize = ( 8 , 8 )) ax0, ax1 = axes.ravel() ax0.imshow(img,plt.cm.gray) ax0.set_title( 'original image' ) ax1.imshow(chull,plt.cm.gray) ax1.set_title( 'convex_hull image' ) |
convex_hull_image()是将图片中的所有目标看作一个整体,因此计算出来只有一个最小凸多边形。如果图中有多个目标物体,每一个物体需要计算一个最小凸多边形,则需要使用convex_hull_object()函数。
函数格式:skimage.morphology.convex_hull_object(image,neighbors=8)
输入参数image是一个二值图像,neighbors表示是采用4连通还是8连通,默认为8连通。
例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import matplotlib.pyplot as plt from skimage import data,color,morphology,feature #生成二值测试图像 img = color.rgb2gray(data.coins()) #检测canny边缘,得到二值图片 edgs = feature.canny(img, sigma = 3 , low_threshold = 10 , high_threshold = 50 ) chull = morphology.convex_hull_object(edgs) #绘制轮廓 fig, axes = plt.subplots( 1 , 2 ,figsize = ( 8 , 8 )) ax0, ax1 = axes.ravel() ax0.imshow(edgs,plt.cm.gray) ax0.set_title( 'many objects' ) ax1.imshow(chull,plt.cm.gray) ax1.set_title( 'convex_hull image' ) plt.show() |
2、连通区域标记
在二值图像中,如果两个像素点相邻且值相同(同为0或同为1),那么就认为这两个像素点在一个相互连通的区域内。而同一个连通区域的所有像素点,都用同一个数值来进行标记,这个过程就叫连通区域标记。在判断两个像素是否相邻时,我们通常采用4连通或8连通判断。在图像中,最小的单位是像素,每个像素周围有8个邻接像素,常见的邻接关系有2种:4邻接与8邻接。4邻接一共4个点,即上下左右,如下左图所示。8邻接的点一共有8个,包括了对角线位置的点,如下右图所示。
在skimage包中,我们采用measure子模块下的label()函数来实现连通区域标记。
函数格式:
1
|
skimage.measure.label(image,connectivity = none) |
参数中的image表示需要处理的二值图像,connectivity表示连接的模式,1代表4邻接,2代表8邻接。
输出一个标记数组(labels), 从0开始标记。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
import numpy as np import scipy.ndimage as ndi from skimage import measure,color import matplotlib.pyplot as plt #编写一个函数来生成原始二值图像 def microstructure(l = 256 ): n = 5 x, y = np.ogrid[ 0 :l, 0 :l] #生成网络 mask = np.zeros((l, l)) generator = np.random.randomstate( 1 ) #随机数种子 points = l * generator.rand( 2 , n * * 2 ) mask[(points[ 0 ]).astype(np. int ), (points[ 1 ]).astype(np. int )] = 1 mask = ndi.gaussian_filter(mask, sigma = l / ( 4. * n)) #高斯滤波 return mask > mask.mean() data = microstructure(l = 128 ) * 1 #生成测试图片 labels = measure.label(data,connectivity = 2 ) #8连通区域标记 dst = color.label2rgb(labels) #根据不同的标记显示不同的颜色 print ( 'regions number:' ,labels. max () + 1 ) #显示连通区域块数(从0开始标记) fig, (ax1, ax2) = plt.subplots( 1 , 2 , figsize = ( 8 , 4 )) ax1.imshow(data, plt.cm.gray, interpolation = 'nearest' ) ax1.axis( 'off' ) ax2.imshow(dst,interpolation = 'nearest' ) ax2.axis( 'off' ) fig.tight_layout() plt.show() |
在代码中,有些地方乘以1,则可以将bool数组快速地转换为int数组。
结果如图:有10个连通的区域,标记为0-9
如果想分别对每一个连通区域进行操作,比如计算面积、外接矩形、凸包面积等,则需要调用measure子模块的regionprops()函数。该函数格式为:
1
|
skimage.measure.regionprops(label_image) |
返回所有连通区块的属性列表,常用的属性列表如下表:
属性名称 | 类型 | 描述 |
area | int | 区域内像素点总数 |
bbox | tuple | 边界外接框(min_row, min_col, max_row, max_col) |
centroid | array | 质心坐标 |
convex_area | int | 凸包内像素点总数 |
convex_image | ndarray | 和边界外接框同大小的凸包 |
coords | ndarray | 区域内像素点坐标 |
eccentricity | float | 离心率 |
equivalent_diameter | float | 和区域面积相同的圆的直径 |
euler_number | int | 区域欧拉数 |
extent | float | 区域面积和边界外接框面积的比率 |
filled_area | int | 区域和外接框之间填充的像素点总数 |
perimeter | float | 区域周长 |
label | int | 区域标记 |
3、删除小块区域
有些时候,我们只需要一些大块区域,那些零散的、小块的区域,我们就需要删除掉,则可以使用morphology子模块的remove_small_objects()函数。
函数格式:skimage.morphology.remove_small_objects(ar,min_size=64,connectivity=1,in_place=false)
参数:
ar: 待操作的bool型数组。
min_size: 最小连通区域尺寸,小于该尺寸的都将被删除。默认为64.
connectivity: 邻接模式,1表示4邻接,2表示8邻接
in_place: bool型值,如果为true,表示直接在输入图像中删除小块区域,否则进行复制后再删除。默认为false.
返回删除了小块区域的二值图像。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
import numpy as np import scipy.ndimage as ndi from skimage import morphology import matplotlib.pyplot as plt #编写一个函数来生成原始二值图像 def microstructure(l = 256 ): n = 5 x, y = np.ogrid[ 0 :l, 0 :l] #生成网络 mask = np.zeros((l, l)) generator = np.random.randomstate( 1 ) #随机数种子 points = l * generator.rand( 2 , n * * 2 ) mask[(points[ 0 ]).astype(np. int ), (points[ 1 ]).astype(np. int )] = 1 mask = ndi.gaussian_filter(mask, sigma = l / ( 4. * n)) #高斯滤波 return mask > mask.mean() data = microstructure(l = 128 ) #生成测试图片 dst = morphology.remove_small_objects(data,min_size = 300 ,connectivity = 1 ) fig, (ax1, ax2) = plt.subplots( 1 , 2 , figsize = ( 8 , 4 )) ax1.imshow(data, plt.cm.gray, interpolation = 'nearest' ) ax2.imshow(dst,plt.cm.gray,interpolation = 'nearest' ) fig.tight_layout() plt.show() |
在此例中,我们将面积小于300的小块区域删除(由1变为0),结果如下图:
4、综合示例:阈值分割+闭运算+连通区域标记+删除小区块+分色显示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
import numpy as np import matplotlib.pyplot as plt import matplotlib.patches as mpatches from skimage import data, filter ,segmentation,measure,morphology,color #加载并裁剪硬币图片 image = data.coins()[ 50 : - 50 , 50 : - 50 ] thresh = filter .threshold_otsu(image) #阈值分割 bw = morphology.closing(image > thresh, morphology.square( 3 )) #闭运算 cleared = bw.copy() #复制 segmentation.clear_border(cleared) #清除与边界相连的目标物 label_image = measure.label(cleared) #连通区域标记 borders = np.logical_xor(bw, cleared) #异或 label_image[borders] = - 1 image_label_overlay = color.label2rgb(label_image, image = image) #不同标记用不同颜色显示 fig,(ax0,ax1) = plt.subplots( 1 , 2 , figsize = ( 8 , 6 )) ax0.imshow(cleared,plt.cm.gray) ax1.imshow(image_label_overlay) for region in measure.regionprops(label_image): #循环得到每一个连通区域属性集 #忽略小区域 if region.area < 100 : continue #绘制外包矩形 minr, minc, maxr, maxc = region.bbox rect = mpatches.rectangle((minc, minr), maxc - minc, maxr - minr, fill = false, edgecolor = 'red' , linewidth = 2 ) ax1.add_patch(rect) fig.tight_layout() plt.show() |
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:http://www.cnblogs.com/denny402/p/5166258.html