python图像分割算法_SLIC算法分割超像素原理及Python实现

超像素(SuperPixel),就是把原本多个像素点,组合成一个大的像素。比如,原本的图片有二十多万个像素,用超像素处理之后,就只有几千个像素了。后面做直方图等处理就会方便许多。经常作为图像处理的预处理步骤。

SuperPixel.png

在超像素算法方面,SLIC Superpixels Compared to State-of-the-art Superpixel Methods这篇论文非常经典。论文中从算法效率,内存使用以及直观性比较了现有的几种超像素处理方法,并提出了一种更加实用,速度更快的算法——SLIC(simple linear iterative clustering),名字叫做简单的线性迭代聚类。其实是从k-means算法演化的,算法复杂度是O(n),只与图像的像素点数有关。

这个算法突破性的地方有二:

限制聚类时搜索的区域(2Sx2S),这样将k-means算法的复杂度降为常数。整个算法的复杂度为线性。

计算距离时考虑LAB颜色和XY距离,5维。这样就把颜色和距离都考虑进去了。通过M可以调整颜色和距离的比重,灵活性强,超像素更加规则。

SLIC算法原理

整个算法的输入只有一个,即超像素的个数K。

图片原有N个像素,要分割成K个像素,那么每个像素的大小是N/K。超像素之间的距离(即规则情况下超像素的边长)就是S=√N/K。

我们的目标是使代价函数(cost function)最小。具体到本算法中,就是每个像素到所属的中心点的距离之和最小。

首先,将K个超像素种子(也叫做聚类,即超像素的中心),均匀撒到图像的像素点上。

一次迭代的第一步,对每个超像素的中心,2S范围内的所有像素点,判断他们是否属于这个超像素。这样之后,就缩短了像素点到超像素中心的距离。

一次迭代的第二步,对每个超像素,将它的超像素中心移动到这个超像素的中点上。这样也缩短了像素点到超像素中心的距离。

一般来说,迭代10是聚类效果和计算成本折中的次数。

SLIC算法步骤

撒种子。将K个超像素中心分布到图像的像素点上。

微调种子的位置。以K为中心的3×3范围内,移动超像素中心到这9个点中梯度最小的点上。这样是为了避免超像素点落到噪点或者边界上。

初始化数据。取一个数组label保存每一个像素点属于哪个超像素。dis数组保存像素点到它属于的那个超像素中心的距离。

对每一个超像素中心x,它2S范围内的点:如果点到超像素中心x的距离(5维)小于这个点到它原来属于的超像素中心的距离,那么说明这个点属于超像素x。更新dis,更新label。

对每一个超像素中心,重新计算它的位置。

重复4 5 两步。

伪代码(来自论文)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

/∗Initialization∗/

InitializeclustercentersCk=[lk,ak,bk,xk,yk]TbysamplingpixelsatregulargridstepsS.

Moveclustercenterstothelowestgradientpositionina3×3neighborhood.

Setlabell(i)=−1foreachpixeli.Setdistanced(i)=∞foreachpixeli.

repeat

/∗Assignment∗/

foreachclustercenterCkdo

foreachpixeliina2S×2SregionaroundCkdo

ComputethedistanceDbetweenCkandi.

ifD

setd(i)=D

setl(i)=k

endif

endfor

endfor

/∗Update∗/

Computenewclustercenters.ComputeresidualerrorE.

untilE≤threshold

Python实现SLIC

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

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

importmath

fromskimageimportio,color

importnumpyasnp

fromtqdmimporttrange

classCluster(object):

cluster_index=1

def__init__(self,h,w,l=0,a=0,b=0):

self.update(h,w,l,a,b)

self.pixels=[]

self.no=self.cluster_index

self.cluster_index+=1

defupdate(self,h,w,l,a,b):

self.h=h

self.w=w

self.l=l

self.a=a

self.b=b

def__str__(self):

return"{},{}:{} {} {} ".format(self.h,self.w,self.l,self.a,self.b)

def__repr__(self):

returnself.__str__()

classSLICProcessor(object):

@staticmethod

defopen_image(path):

"""

Return:

3D array, row col [LAB]

"""

rgb=io.imread(path)

lab_arr=color.rgb2lab(rgb)

returnlab_arr

@staticmethod

defsave_lab_image(path,lab_arr):

"""

Convert the array to RBG, then save the image

"""

rgb_arr=color.lab2rgb(lab_arr)

io.imsave(path,rgb_arr)

defmake_cluster(self,h,w):

returnCluster(h,w,

self.data[h][w][0],

self.data[h][w][1],

self.data[h][w][2])

def__init__(self,filename,K,M):

self.K=K

self.M=M

self.data=self.open_image(filename)

self.image_height=self.data.shape[0]

self.image_width=self.data.shape[1]

self.N=self.image_height*self.image_width

self.S=int(math.sqrt(self.N/self.K))

self.clusters=[]

self.label={}

self.dis=np.full((self.image_height,self.image_width),np.inf)

definit_clusters(self):

h=self.S/2

w=self.S/2

whileh

whilew

self.clusters.append(self.make_cluster(h,w))

w+=self.S

w=self.S/2

h+=self.S

defget_gradient(self,h,w):

ifw+1>=self.image_width:

w=self.image_width-2

ifh+1>=self.image_height:

h=self.image_height-2

gradient=self.data[w+1][h+1][0]-self.data[w][h][0]+\

self.data[w+1][h+1][1]-self.data[w][h][1]+\

self.data[w+1][h+1][2]-self.data[w][h][2]

returngradient

defmove_clusters(self):

forclusterinself.clusters:

cluster_gradient=self.get_gradient(cluster.h,cluster.w)

fordhinrange(-1,2):

fordwinrange(-1,2):

_h=cluster.h+dh

_w=cluster.w+dw

new_gradient=self.get_gradient(_h,_w)

ifnew_gradient

cluster.update(_h,_w,self.data[_h][_w][0],self.data[_h][_w][1],self.data[_h][_w][2])

cluster_gradient=new_gradient

defassignment(self):

forclusterinself.clusters:

forhinrange(cluster.h-2*self.S,cluster.h+2*self.S):

ifh<0orh>=self.image_height:continue

forwinrange(cluster.w-2*self.S,cluster.w+2*self.S):

ifw<0orw>=self.image_width:continue

L,A,B=self.data[h][w]

Dc=math.sqrt(

math.pow(L-cluster.l,2)+

math.pow(A-cluster.a,2)+

math.pow(B-cluster.b,2))

Ds=math.sqrt(

math.pow(h-cluster.h,2)+

math.pow(w-cluster.w,2))

D=math.sqrt(math.pow(Dc/self.M,2)+math.pow(Ds/self.S,2))

ifD

if(h,w)notinself.label:

self.label[(h,w)]=cluster

cluster.pixels.append((h,w))

else:

self.label[(h,w)].pixels.remove((h,w))

self.label[(h,w)]=cluster

cluster.pixels.append((h,w))

self.dis[h][w]=D

defupdate_cluster(self):

forclusterinself.clusters:

sum_h=sum_w=number=0

forpincluster.pixels:

sum_h+=p[0]

sum_w+=p[1]

number+=1

_h=sum_h/number

_w=sum_w/number

cluster.update(_h,_w,self.data[_h][_w][0],self.data[_h][_w][1],self.data[_h][_w][2])

defsave_current_image(self,name):

image_arr=np.copy(self.data)

forclusterinself.clusters:

forpincluster.pixels:

image_arr[p[0]][p[1]][0]=cluster.l

image_arr[p[0]][p[1]][1]=cluster.a

image_arr[p[0]][p[1]][2]=cluster.b

image_arr[cluster.h][cluster.w][0]=0

image_arr[cluster.h][cluster.w][1]=0

image_arr[cluster.h][cluster.w][2]=0

self.save_lab_image(name,image_arr)

defiterate_10times(self):

self.init_clusters()

self.move_clusters()

foriintrange(10):

self.assignment()

self.update_cluster()

name='lenna_M{m}_K{k}_loop{loop}.png'.format(loop=i,m=self.M,k=self.K)

self.save_current_image(name)

if__name__=='__main__':

p=SLICProcessor('Lenna.png',500,30)

p.iterate_10times()

效果如下:

lenna_M30_K500_loop0.png

Lenna图像在M=30,K=500时第一次迭代产生的超像素图。

lenna_M30_K500_loop9.png

Lenna图像在M=30,K=500时第10次迭代产生的超像素图。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值