SILC超像素分割算法详解(附Python代码)

Stella981
• 阅读 770

SILC算法详解

一、原理介绍

SLIC算法是simple linear iterative cluster的简称,该算法用来生成超像素(superpixel)

算法步骤:

  •  已知一副图像大小M*N,可以从RGB空间转换为LAB空间,LAB颜色空间表现的颜色更全面

  • 假如预定义参数K,K为预生成的超像素数量,即预计将M*N大小的图像(像素数目即为M*N)分隔为K个超像素块,每个超像素块范围大小包含[(M*N)/K]个像素

  • 假设每个超像素区域长和宽都均匀分布的话,那么每个超像素块的长和宽均可定义为S,S=sqrt(M*N/K)

  • 遍历操作,将每个像素块的中心点的坐标(x,y)及其lab的值保存起来,加入到事先定义好的集合中

  • 每个像素块的中心点默认是(S/2,S/2)进行获取的,有可能落在噪音点或者像素边缘(所谓像素边缘,即指像素突变处,比如从黑色过渡到白色的交界处),这里,利用差分方式进行梯度计算,调整中心点:

算法中,使用中心点的8领域像素点,计算获得最小梯度值的像素点,并将其作为新的中心点,差分计算梯度的公式:

Gradient(x,y)=dx(i,j) + dy(i,j);

dx(i,j) = I(i+1,j) - I(i,j); 

dy(i,j) = I(i,j+1) - I(i,j);    遍历现中心点的8领域像素点,将其中计算得到最小Gradient值的像素点作为新的中心点
  • 调整完中心点后即需要进行像素点的聚类操作

    通过聚类的方式迭代计算新的聚类中心;
    
    首先,需要借助K-means聚类算法,将像素点进行归类,通过变换的欧氏聚距离公式进行,公式如下(同时参考像素值和坐标值提取相似度):        
    
     通过两个参数m和S来协调两种距离的比例分配。参数S即是上面第③步计算得出的每个像素块的长度值,而参数M为LAB空间的距离可能最大值,其可取的范围建议为[1,40]
    
     为了节省时间,只遍历每个超像素块中心点周边的2S*2S区域内的像素点,计算该区域内每个像素点距离哪一个超像素块的中心点最近,并将其划分到其中;完成一次迭代后,重新计算每个超像素块的中心点坐标,并重新进行迭代(注:衡量效率和效果后一般选择迭代10次)
    

    二、代码实现

    1 import math 2 from skimage import io, color 3 import numpy as np 4 5 class Cluster(object): 6 7 cluster_index = 1 8 9 def init(self, row, col, l=0, a=0, b=0): 10 self.update(row, col, l, a, b) 11 self.pixels = [] 12 self.no = self.cluster_index 13 Cluster.cluster_index += 1 14 15 def update(self, row, col, l, a, b): 16 self.row = row 17 self.col = col 18 self.l = l 19 self.a = a 20 self.b = b 21 22 23 class SLICProcessor(object): 24 @staticmethod 25 def open_image(path): 26 rgb = io.imread(path) 27 lab_arr = color.rgb2lab(rgb) 28 return lab_arr 29 30 @staticmethod 31 def save_lab_image(path, lab_arr): 32 rgb_arr = color.lab2rgb(lab_arr) 33 io.imsave(path, rgb_arr) 34 35 def make_cluster(self, row, col): 36 row=int(row) 37 col=int(col) 38 return Cluster(row, col, 39 self.data[row][col][0], 40 self.data[row][col][1], 41 self.data[row][col][2]) 42 43 def init(self, filename, K, M): 44 self.K = K 45 self.M = M 46 47 self.data = self.open_image(filename) 48 self.rows = self.data.shape[0] 49 self.cols = self.data.shape[1] 50 self.N = self.rows * self.cols 51 self.S = int(math.sqrt(self.N / self.K)) 52 53 self.clusters = [] 54 self.label = {} 55 self.dis = np.full((self.rows, self.cols), np.inf) 56 57 def init_clusters(self): 58 row = self.S / 2 59 col = self.S / 2 60 while row < self.rows: 61 while col < self.cols: 62 self.clusters.append(self.make_cluster(row, col)) 63 col+= self.S 64 col = self.S / 2 65 row += self.S 66 67 def get_gradient(self, row, col): 68 if col + 1 >= self.cols: 69 col = self.cols - 2 70 if row + 1 >= self.rows: 71 row = self.rows - 2 72 73 gradient = (self.data[row + 1][col][0] +self.data[row][col+1][0]-2self.data[row][col][0])+
    74 (self.data[row + 1][col][1] +self.data[row][col+1][1]-2
    self.data[row][col][1]) +
    75 (self.data[row + 1][col][2] +self.data[row][col+1][2]-2*self.data[row][col][2]) 76 77 return gradient 78 79 def move_clusters(self): 80 for cluster in self.clusters: 81 cluster_gradient = self.get_gradient(cluster.row, cluster.col) 82 for dh in range(-1, 2): 83 for dw in range(-1, 2): 84 _row = cluster.row + dh 85 _col = cluster.col + dw 86 new_gradient = self.get_gradient(_row, _col) 87 if new_gradient < cluster_gradient: 88 cluster.update(_row, _col, self.data[_row][_col][0], self.data[_row][_col][1], self.data[_row][_col][2]) 89 cluster_gradient = new_gradient 90 91 def assignment(self): 92 for cluster in self.clusters: 93 for h in range(cluster.row - 2 * self.S, cluster.row + 2 * self.S): 94 if h < 0 or h >= self.rows: continue 95 for w in range(cluster.col - 2 * self.S, cluster.col + 2 * self.S): 96 if w < 0 or w >= self.cols: continue 97 L, A, B = self.data[h][w] 98 Dc = math.sqrt( 99 math.pow(L - cluster.l, 2) + 100 math.pow(A - cluster.a, 2) + 101 math.pow(B - cluster.b, 2)) 102 Ds = math.sqrt( 103 math.pow(h - cluster.row, 2) + 104 math.pow(w - cluster.col, 2)) 105 D = math.sqrt(math.pow(Dc / self.M, 2) + math.pow(Ds / self.S, 2)) 106 if D < self.dis[h][w]: 107 if (h, w) not in self.label: 108 self.label[(h, w)] = cluster 109 cluster.pixels.append((h, w)) 110 else: 111 self.label[(h, w)].pixels.remove((h, w)) 112 self.label[(h, w)] = cluster 113 cluster.pixels.append((h, w)) 114 self.dis[h][w] = D 115 116 def update_cluster(self): 117 for cluster in self.clusters: 118 sum_h = sum_w = number = 0 119 for p in cluster.pixels: 120 sum_h += p[0] 121 sum_w += p[1] 122 number += 1 123 _h =int( sum_h / number) 124 _w =int( sum_w / number) 125 cluster.update(_h, _w, self.data[_h][_w][0], self.data[_h][_w][1], self.data[_h][_w][2]) 126 127 def save_current_image(self, name): 128 image_arr = np.copy(self.data) 129 for cluster in self.clusters: 130 for p in cluster.pixels: 131 image_arr[p[0]][p[1]][0] = cluster.l 132 image_arr[p[0]][p[1]][1] = cluster.a 133 image_arr[p[0]][p[1]][2] = cluster.b 134 image_arr[cluster.row][cluster.col][0] = 0 135 image_arr[cluster.row][cluster.col][1] = 0 136 image_arr[cluster.row][cluster.col][2] = 0 137 self.save_lab_image(name, image_arr) 138 139 def iterates(self): 140 self.init_clusters() 141 self.move_clusters() 142 #考虑到效率和效果,折中选择迭代10次 143 for i in range(10): 144 self.assignment() 145 self.update_cluster() 146 self.save_current_image("output.jpg") 147 148 149 if name == 'main': 150 p = SLICProcessor('beauty.jpg', 200, 40) 151 p.iterates()

    三、运行效果截图 (原图) (效果图)

代码参考了https://github.com/laixintao/slic-python-implementation,且做了改进

作为一枚技术小白,写这篇笔记的时候参考了很多博客论文,在这里表示感谢,转载请注明出处......

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
2年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
京东云开发者 京东云开发者
7个月前
分布式系统的主键生成方案对比 | 京东云技术团队
UUID​UUID(通用唯一识别码)是由32个十六进制数组成的无序字符串,通过一定的算法计算出来。为了保证其唯一性,UUID规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这