SLIC超像素分割代码
- 格式:doc
- 大小:286.50 KB
- 文档页数:23
opencv 超像素分割每个像素块同一颜色超像素分割是一种用于图像处理和计算机视觉领域的技术,它将图像划分为更小且具有相似特征的区域,这些区域被称为超像素。
每个超像素块具有相同的颜色或纹理等特性,可以用来进行图像分割、目标检测和图像分析等任务。
超像素分割算法的目标是将图像划分为若干个紧凑且连通的超像素块,使得每个超像素内的像素具有一致的属性。
相比传统的像素级分割方法,超像素分割可以减少图像中的冗余信息,并提高后续图像处理算法的效率和准确性。
常用的超像素分割算法有SLIC(Simple Linear Iterative Clustering)、SEEDS(Superpixels Extracted via Energy-Driven Sampling)和LSC(Linear Spectral Clustering)等。
这些算法的原理大致相同,首先根据图像的颜色或者纹理信息将图像划分为超像素种子,然后通过迭代合并或分离的方式不断优化超像素块的边界,直到满足停止准则。
在SLIC算法中,图像被划分为一个个紧凑的正方形块,每个块内像素的颜色和空间位置信息被用来度量相似性。
算法首先根据指定的超像素数目将图像划分为若干个初始网格,然后通过迭代优化每个超像素的中心位置和边界。
这样得到的超像素块具有较好的紧凑性和连通性。
SEEDS算法采用自底向上的策略进行超像素分割,首先将图像划分为若干个子区域,然后通过能量最小化的方式不断合并或分离子区域,直到达到预定的超像素数目。
SEEDS算法在计算效率和分割质量上都取得了较好的平衡。
LSC算法将超像素分割问题转化为了一个基于图论的最小切割问题,通过图论的方法对图像进行分割。
该算法在处理大规模图像时具有较好的效果,但对参数的敏感性较高。
超像素分割在图像处理领域有着广泛的应用。
首先,超像素分割可以用来进行图像分割,将图像中的前景和背景分离出来,有助于目标检测、图像识别和图像分析等任务的实现。
超像素分割 python超像素分割(Superpixel Segmentation)是一种图像分割的方法,旨在将图像分割为若干均匀、紧凑且具有语义的区域。
超像素分割是计算机视觉和图像处理领域的热门研究方向,常用于目标识别、图像分割、物体跟踪等应用。
超像素分割的基本思想是将图像划分为若干个具有一定连续性的区域,并且使得区域内的像素具有相似的颜色、纹理、边缘等特征。
相比于传统的像素级分割方法,超像素分割能够减少计算复杂度,并且更好地保留了图像中的结构信息。
下面介绍几种常用的超像素分割方法。
1. SLIC超像素分割算法SLIC(Simple Linear Iterative Clustering)是一种流行的超像素分割算法。
它将图像划分为一系列紧凑的超像素,其中每个超像素由相似的像素组成。
SLIC算法首先将图像均匀地划分为固定数量的网格,然后在每个网格内寻找代表性的像素,最后通过优化迭代过程来调整超像素的形状和位置。
2. SEEDS超像素分割算法SEEDS(Superpixels Extracted via Energy-Driven Sampling)是一种基于分水岭算法的超像素分割方法。
它通过在图像中随机选择一些种子像素,然后通过种子像素之间的相似性来迭代地合并像素,最终得到超像素分割结果。
3. LSC超像素分割算法LSC(Linear Spectral Clustering)是一种基于谱聚类的超像素分割算法。
它通过将图像像素表示为高维特征空间中的点,并利用谱聚类算法将像素分成若干个紧凑的子集。
LSC算法具有较好的鲁棒性和可扩展性,并且能够处理不规则形状的超像素。
4. ERS超像素分割算法ERS(Efficient Graph-Based Image Segmentation)是一种基于图论的超像素分割算法。
它通过建立一个图,其中图的节点表示图像的像素,边表示像素之间的关系。
ERS算法通过最小生成树算法和最大子图算法来合并相似的像素,从而得到紧凑且连通的超像素。
图像超像素(superpixels)分割算法——简单线性迭代聚类(SLIC)原理 SILC(simple linear iterative clustering)是⼀种图像分割算法。
默认情况下,该算法的唯⼀参数是k,约等于超像素尺⼨的期望数量。
对于CIELAB彩⾊空间的图像,在相隔S像素上采样得到初始聚类中⼼。
为了产⽣⼤致相同尺⼨的超像素,格点的距离是S=√N/k。
中⼼需要被移到3x3领域内的最低梯度处,这样做是为了避免超像素中⼼在边缘和噪声点上。
接下来为每⼀个像素i设置最近的聚类中⼼,该聚类中⼼的搜索区域要覆盖该像素的位置。
这是本算法加速的关键,因为通过限制搜索区域的⼤⼩减⼩了距离计算的数量,并且相对于传统的k-means聚类算法有显著的速度优势,因为后者的每个像素都必须和所有的聚类中⼼进⾏⽐较。
⼀个超像素的预期空间范围是约为SxS的区域,这⾥对于相似像素的搜索是在超像素中⼼的2Sx2S区域完成。
⼀旦每个像素被关联到最近的聚类中⼼后,就通过求聚类中⼼所有像素的均值来执⾏聚类中⼼的更新。
使⽤L2范数计算前⼀个聚类中⼼和当前聚类中⼼的残差。
assignment和update步骤被重复迭代直到误差收敛,但是我们发现对于⼤多数图像10次迭代就够了。
算法步骤1)通过在常规⽹格步长S处采样像素来初始化聚类中⼼2)在3x3的领域内移动聚类中⼼到最低的梯度位置3)为每⼀个像素i设置标签l(i)4)为每⼀个像素设置距离d(i)=∞5)对于每⼀个聚类中⼼遍历2Sx2S区域内的每⼀个像素点,计算距离决定是否更新像素的标签和距离6)更新聚类中⼼7)重复步骤5)6)直到收敛Processing math: 100%。
SLIC算法是simple linear iterative cluster的简称,该算法用来生成超像素(superpixel)。
SLIC的思想是将彩色图像转化为CIELAB颜色空间和XY坐标下的5维特征向量(Lab模式也是由三个通道组成,第一个通道是明度,即“L”。
a通道的颜色是从红色到深绿;b通道则是从蓝色到黄色。
),然后对5维特征向量构造度量标准,对图像像素进行局部聚类的过程。
该算法速度较快,能生成紧凑、近似均匀的超像素。
1 、SLIC超像素分割的步骤[java] view plain copyfunction [sp_img,disp_img] = DemoSLICSuperpixel (img,K,M)% img: original rgb image% K: number of superpixel 超像素的个数% M: compactness of superpixel 紧凑性tic;[X,Y,L,A,B,Lab_img,STEP] = GetLABXYSeeds(img,K); %初始化种子点并提取种子的XYLab 特征EdgeMap = DetectLabEdges(Lab_img); %为了避免边缘位置的干扰,还要考虑边缘剩余[X,Y,L,A,B] = PeturbSeeds (EdgeMap,Lab_img,X,Y,L,A,B); %产生新的XYLab种子labels = PerformSLICSuperpixel(X,Y,L,A,B,Lab_img,STEP,M); %<span style="font-family: Arial, Helvetica, sans-serif;">根据元素与种子点的相似度关系进行聚类,并添加标签</span>sp_img = EnforceLabelConnectivity(labels,K); %合并较小的聚类disp_img = DrawContour(img,sp_img); %超像素边界2 、初始化种子点并提取XYLab特征输入m*n的图像,将图像分成k'个超像素。
SILC超像素分割算法详解(附Python代码)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区域内的像素点,计算该区域内每个像素点距离哪⼀个超像素块的中⼼点最近,并将其划分到其中;完成⼀次迭代后,重新计算每个超像素块的中⼼点坐标,并重新进⾏迭⼆、代码实现1import math2from skimage import io, color3import numpy as np45class Cluster(object):67 cluster_index = 189def__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_index13 Cluster.cluster_index += 11415def update(self, row, col, l, a, b):16 self.row = row17 self.col = col18 self.l = l19 self.a = a20 self.b = b212223class SLICProcessor(object):24 @staticmethod25def open_image(path):26 rgb = io.imread(path)27 lab_arr = color.rgb2lab(rgb)28return lab_arr2930 @staticmethod31def save_lab_image(path, lab_arr):32 rgb_arr = b2rgb(lab_arr)33 io.imsave(path, rgb_arr)3435def make_cluster(self, row, col):36 row=int(row)37 col=int(col)38return Cluster(row, col,39 self.data[row][col][0],40 self.data[row][col][1],41 self.data[row][col][2])4243def__init__(self, filename, K, M):44 self.K = K45 self.M = M4647 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.cols51 self.S = int(math.sqrt(self.N / self.K))5253 self.clusters = []54 bel = {}55 self.dis = np.full((self.rows, self.cols), np.inf)5657def init_clusters(self):58 row = self.S / 259 col = self.S / 260while row < self.rows:61while col < self.cols:62 self.clusters.append(self.make_cluster(row, col))63 col+= self.S64 col = self.S / 265 row += self.S6667def get_gradient(self, row, col):68if col + 1 >= self.cols:69 col = self.cols - 270if row + 1 >= self.rows:71 row = self.rows - 27273 gradient = (self.data[row + 1][col][0] +self.data[row][col+1][0]-2*self.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])7677return gradient7879def move_clusters(self):80for cluster in self.clusters:81 cluster_gradient = self.get_gradient(cluster.row, cluster.col)82for dh in range(-1, 2):83for dw in range(-1, 2):84 _row = cluster.row + dh85 _col = cluster.col + dw86 new_gradient = self.get_gradient(_row, _col)87if 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_gradient9091def assignment(self):92for cluster in self.clusters:93for h in range(cluster.row - 2 * self.S, cluster.row + 2 * self.S):94if h < 0 or h >= self.rows: continue95for w in range(cluster.col - 2 * self.S, cluster.col + 2 * self.S):96if w < 0 or w >= self.cols: continue97 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))106if D < self.dis[h][w]:107if (h, w) not in bel:108 bel[(h, w)] = cluster109 cluster.pixels.append((h, w))110else:111 bel[(h, w)].pixels.remove((h, w))112 bel[(h, w)] = cluster113 cluster.pixels.append((h, w))114 self.dis[h][w] = D115116def update_cluster(self):117for cluster in self.clusters:118 sum_h = sum_w = number = 0119for p in cluster.pixels:120 sum_h += p[0]121 sum_w += p[1]122 number += 1123 _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])126127def save_current_image(self, name):128 image_arr = np.copy(self.data)129for cluster in self.clusters:130for p in cluster.pixels:131 image_arr[p[0]][p[1]][0] = cluster.l132 image_arr[p[0]][p[1]][1] = cluster.a133 image_arr[p[0]][p[1]][2] = cluster.b134 image_arr[cluster.row][cluster.col][0] = 0135 image_arr[cluster.row][cluster.col][1] = 0136 image_arr[cluster.row][cluster.col][2] = 0137 self.save_lab_image(name, image_arr)138139def iterates(self):140 self.init_clusters()141 self.move_clusters()142#考虑到效率和效果,折中选择迭代10次143for i in range(10):144 self.assignment()145 self.update_cluster()146 self.save_current_image("output.jpg")147148149if__name__ == '__main__':150 p = SLICProcessor('beauty.jpg', 200, 40)151 p.iterates()三、运⾏效果截图(原图)(效果图)代码参考了https:///laixintao/slic-python-implementation,且做了改进作为⼀枚技术⼩⽩,写这篇笔记的时候参考了很多博客论⽂,在这⾥表⽰感谢,转载请注明出处......。
科技与创新|Science and Technology & Innovation2024年第06期DOI:10.15913/ki.kjycx.2024.06.057超像素算法在医疗诊断领域中的应用张永博1,张苗2(1.巨鲨显示科技有限公司,江苏南京211106;2.南京航空航天大学,江苏南京211106)摘要:近年来,医学图像处理已被广泛应用于临床实践,在医疗诊断领域体现出重要的临床意义与价值。
SLIC(Simple Linear Iterative Clustering,简单线性迭代聚类)超像素分割算法是通过聚类的思想将图像中具有相似特征的邻近像素点合并为分布均匀、边界清晰的超像素块,其数量比原像素数少很多,能够极大地提升图像处理效率。
在此基础上提出std_SLIC超像素分割算法,该算法可以在一定程度上提高算法的准确率。
最后选取多组医学图像进行测试,验证了所提出算法可以为医疗诊断提供可靠依据。
关键词:医学图像;超像素;SLIC;辅助诊断中图分类号:TP391.41 文献标志码:A 文章编号:2095-6835(2024)06-0194-03随着医学成像技术的迅速发展,医学图像所蕴含的信息也更加丰富。
将像素作为图像分割的最小单元往往会带来数据量大、迭代次数多、参数估计收敛慢等问题,难以满足临床实践中的时效性需求。
REN&MALIK(2003)[1]提出超像素的概念,利用图像冗余信息将具有相似特征的邻近像素点归为一类,形成像素聚合块,即超像素。
超像素模型能够很好地保留图像中的边界特征,更好地表达图像的局部特征以及部分结构特征[2]。
此外,一幅图像中超像素的数目远小于其像素数,可以极大地减少计算量,有利于提高图像处理效率。
针对现有技术所存在的问题,提出了一种基于SLIC超像素图像的医学图像匹配方法,能够对选取关键帧图像分割后的像素块进行相似度判别,以解决现有技术中实用性、精度不高的问题。
SLIC超像素分割算法和目前超像素算法的比较Radhakrishna Achanta, IEEE专业会员,Appu Shaji, Kevin Smith, IEEE专业会员,Aurelien Lucchi,Pascal Fua, IEEE会士,and Sabine Susstrunk,IEEE高级会员摘要近年来计算机视觉应用已经越来越依赖于超像素处理,但它并不总是很清楚什么是一个好的超像素的算法。
为了了解目前算法的优点和缺点,我们验证比较了5种目前使用的超像素算法与图像边缘吻合的能力,速度,内存使用率和它们对于分割效果的影响。
我们引入了一种基于应用k-means 聚类算法的简单线性迭代聚类(SLIC)的新的超像素算法以有效生成超像素。
尽管它很简单,SLIC对于边界的吻合度与之前的算法相比不分上下甚至更好。
同时,它速度更快,占用内存更小,分割性能更优,并直接扩展了超体素生成。
索引词汇超像素,分割,聚类,k-means1 简介超像素算法组像素在感知上有意义的原子区域中可以取代像素网格的刚性结构(图1)。
他们捕捉图像冗余,提供了一种便捷的计算图像特征,并大幅降低后续图像处理任务的复杂度的原始方法。
他们已经成为很多计算机视觉算法的关键构建模块,比如在PASCAL VOC挑战赛中得分最高的多类对象分割[ 9 ],[ 29 ],[ 11 ],深度估计[ 30 ],分割[ 16 ],人体模型估计[ 22 ],以及目标定位[ 9 ]。
有许多方法来生成超像素,每一个都有自己的优点和缺点,可能更适合特定的应用程序。
例如,如果图像边界吻合度是非常重要的,那么基于图的方法会是一个理想的选择。
然而,如果是用超像素来构建一幅图像,那么如[ 23 ]这种产生一个更为常规的晶格的方法可能是更好的选择。
虽然很难界定什么是对所有应用都理想的方法,我们相信以下性能通常是可取的:1. 超像素应该有好的图像边界吻合度。
2. 当用于减少计算的复杂性时,作为一个预处理步骤,超像素应该可被快速计算,占据较小的内存,和简单的使用。
第 22卷第 3期2023年 3月Vol.22 No.3Mar.2023软件导刊Software Guide基于凸包的SLIC超像素分割算法杨昊宇1,张春富1,杨佳武1,王鹏1,谈格2,彭华伟2,许杰2(1.盐城工学院电气工程学院,江苏盐城 224007;2.盐城市计量测试所,江苏盐城 224008)摘要:在SLIC算法聚类分割过程中,因像素标记冗余更新导致算法耗时较长。
为解决上述问题,提出基于凸包的SLIC超像素分割算法。
该算法首先根据图像网格的灰度特征,利用阈值标记筛选出目标区域;其次,利用凸包算法构建图像凸包,并以其结构特征确定初始聚类中心;最后,基于初始聚类中心对目标区域使用SLIC算法完成图像分割。
在MATLAB环境下分别对该算法与3种相关算法进行对比测试,实验结果表明,该算法能够有效提高算法运行效率,并具有较好的分割质量。
关键词:SLIC算法;超像素;凸包;聚类分割DOI:10.11907/rjdk.222011开放科学(资源服务)标识码(OSID):中图分类号:TP301 文献标识码:A文章编号:1672-7800(2023)003-0153-04SLIC Superpixel Segmentation Algorithm Based on Convex HullYANG Hao-yu1, ZHANG Chun-fu1, YANG Jia-wu1, WANG Peng1, TAN Ge2, PENG Hua-wei2, XU Jie2(1.School of Electrical Engineering, Yancheng Institute of Technology, Yancheng 224007, China;2.Yancheng Measurement and Testing Institute, Yancheng 224008, China)Abstract:In the process of SLIC clustering and segmentation, the redundant updating of pixel marks causes the algorithm to take a long time. In order to solve the above problems, a convex hull based SLIC super-pixel segmentation algorithm is proposed. Firstly, according to the gray characteristics of the image grid, the target region is filtered out by using the threshold mark. Secondly, the convex hull algorithm is used to construct the convex hull of the image and determine the initial clustering center based on its structural characteristics. Finally, based on the initial clustering center, SLIC algorithm is used to complete the image segmentation. In the MATLAB environment, the proposed algorithm and three related algorithms are tested. The experimental results show that the proposed algorithm can effectively improve the efficiency of the algorithm and has better segmentation quality.Key Words:SLIC algorithm; superpixel; convex hull; clustering segmentation0 引言在数据爆发式增长的信息时代,图像处理的工作量与日俱增,图像分割技术面临巨大挑战[1]。
基于改进SLIC算法的超像素图像分割及参数优化
王静;余顺园
【期刊名称】《自动化技术与应用》
【年(卷),期】2024(43)5
【摘要】为了提高超像素图像分割效率,根据颜色参数设计得到SLIC图像分割算法。
研究结果表明:应确保K值达到尽量小的状态下,设置更高精度的边界分割效果,从而确保精度提升的基础上尽量减少计算量。
逐渐提高超像素数后,UE呈现持续降低的趋势,最终达到饱和状态;在初期处于较小超像素数量的情况下,ASA发生了快速增长,此时分割精度也获得了快速提升;BR表现为较平稳的增长趋势,形成了稳定的BR参数。
优化SLIC算法以自动方式设置的K值为323,实现了算法计算过程的明显简化。
所设计的优化SLIC方法可针对各类图像分别设置超像素数量,不必进行多次尝试来选择超像素,使运行时间大幅缩短。
【总页数】4页(P67-69)
【作者】王静;余顺园
【作者单位】安康学院电子与信息工程学院
【正文语种】中文
【中图分类】TP391
【相关文献】
1.改进的SLIC超像素图像分割与合并算法
2.改进的SLIC超像素图像分割与合并算法
3.基于SLIC超像素粒化的粗糙熵图像分割算法
4.基于改进的SLIC超像素分割算法
5.基于区域再分割的改进型SLIC超像素生成算法
因版权原因,仅展示原文概要,查看原文内容请购买。
超像素分割算法(SLIC算法)
SLIC算法的核心思想是将图像空间和颜色空间相结合,通过将像素点聚类为超像素,实现图像的分割。
算法的流程如下:
1.初始化:选择超像素数量K,并进行初始位置的选择。
一种常用的初始化方法是均匀地将图像分成K个网格,并选取每个网格的中心点作为初始位置。
2. 迭代优化:对每个超像素中心点,使用k-means算法将其周围的像素分类到该超像素。
这里的距离度量不仅包括欧氏距离,还考虑了颜色相似性和空间距离的权重。
同时,还计算了每个像素点到最近超像素中心点的距离,用于后续的超像素合并操作。
3.超像素合并:根据像素点到最近超像素中心点的距离和相邻超像素之间的相似性,进行超像素的合并操作。
这样可以将尺寸较小的超像素合并为更大的超像素,使得图像分割更加连贯。
4.迭代优化:重复步骤2和步骤3,直到达到预设的迭代次数或者收敛为止。
SLIC算法有以下特点:
1. 快速有效:SLIC算法通过使用k-means算法进行迭代聚类,使得算法具有较高的效率。
同时,由于使用了颜色和空间信息,也能够获得更好的分割效果。
2.参数少:SLIC算法只需要设置一个参数,即超像素数量K,此外,还可以根据需要设置聚类的迭代次数。
3.保持图像边界:由于考虑了颜色相似性和空间距离的权重,在进行超像素合并操作时能够较好地保持图像的边界。
4.可扩展性:SLIC算法可以很容易地扩展到多通道的图像,同时也可以用于视频超像素分割。
总的来说,SLIC算法是一种快速有效的超像素分割算法,具有较好的分割效果。
通过合适的初始化和迭代次数,可以在保持图像细节的同时实现图像的快速分割。
// SLIC.cpp: implementation of the SLIC class.//=========================================================================== // This code implements the zero parameter superpixel segmentation technique // described in://////// "SLIC Superpixels Compared to State-of-the-art Superpixel Methods"//// Radhakrishna Achanta, Appu Shaji, Kevin Smith, Aurelien Lucchi, Pascal Fua, // and Sabine Susstrunk,//// IEEE TPAMI, Volume 34, Issue 11, Pages 2274-2282, November 2012.//////=========================================================================== // Copyright (c) 2013 Radhakrishna Achanta.//// For commercial use please contact the author:////Email:**************************//===========================================================================#include"stdafx.h"#include<cfloat>#include<cmath>#include<iostream>#include<fstream>#include"SLIC.h"// For superpixelsconst int dx4[4] = {-1, 0, 1, 0};const int dy4[4] = { 0, -1, 0, 1};//const int dx8[8] = {-1, -1, 0, 1, 1, 1, 0, -1};//const int dy8[8] = { 0, -1, -1, -1, 0, 1, 1, 1};// For supervoxelsconst int dx10[10] = {-1, 0, 1, 0, -1, 1, 1, -1, 0, 0};const int dy10[10] = { 0, -1, 0, 1, -1, -1, 1, 1, 0, 0};const int dz10[10] = { 0, 0, 0, 0, 0, 0, 0, 0, -1, 1};//////////////////////////////////////////////////////////////////////// Construction/Destruction//////////////////////////////////////////////////////////////////////SLIC::SLIC(){m_lvec = NULL;m_avec = NULL;m_bvec = NULL;m_lvecvec = NULL;m_avecvec = NULL;m_bvecvec = NULL;}SLIC::~SLIC(){if(m_lvec) delete [] m_lvec;if(m_avec) delete [] m_avec;if(m_bvec) delete [] m_bvec;if(m_lvecvec){for( int d = 0; d < m_depth; d++ ) delete [] m_lvecvec[d];delete [] m_lvecvec;}if(m_avecvec){for( int d = 0; d < m_depth; d++ ) delete [] m_avecvec[d];delete [] m_avecvec;}if(m_bvecvec){for( int d = 0; d < m_depth; d++ ) delete [] m_bvecvec[d];delete [] m_bvecvec;}}//============================================================================== /// RGB2XYZ////// sRGB (D65 illuninant assumption) to XYZ conversion//==============================================================================void SLIC::RGB2XYZ(const int& sR,const int& sG,const int& sB,double& X,double& Y,double& Z){double R = sR/255.0;double G = sG/255.0;double B = sB/255.0;double r, g, b;if(R <= 0.04045) r = R/12.92;else r = pow((R+0.055)/1.055,2.4);if(G <= 0.04045) g = G/12.92;else g = pow((G+0.055)/1.055,2.4);if(B <= 0.04045) b = B/12.92;else b = pow((B+0.055)/1.055,2.4);X = r*0.4124564 + g*0.3575761 + b*0.1804375;Y = r*0.2126729 + g*0.7151522 + b*0.0721750;Z = r*0.0193339 + g*0.1191920 + b*0.9503041;}//===========================================================================/// RGB2LAB//===========================================================================void SLIC::RGB2LAB(const int& sR, const int& sG, const int& sB, double& lval, double& aval, double& bval){//------------------------// sRGB to XYZ conversion//------------------------double X, Y, Z;RGB2XYZ(sR, sG, sB, X, Y, Z);//------------------------// XYZ to LAB conversion//------------------------double epsilon = 0.008856; //actual CIE standarddouble kappa = 903.3; //actual CIE standarddouble Xr = 0.950456; //reference whitedouble Yr = 1.0; //reference whitedouble Zr = 1.088754; //reference whitedouble xr = X/Xr;double yr = Y/Yr;double zr = Z/Zr;double fx, fy, fz;if(xr > epsilon) fx = pow(xr, 1.0/3.0);else fx = (kappa*xr + 16.0)/116.0;if(yr > epsilon) fy = pow(yr, 1.0/3.0);else fy = (kappa*yr + 16.0)/116.0;if(zr > epsilon) fz = pow(zr, 1.0/3.0);else fz = (kappa*zr + 16.0)/116.0;lval = 116.0*fy-16.0;aval = 500.0*(fx-fy);bval = 200.0*(fy-fz);}//=========================================================================== /// DoRGBtoLABConversion////// For whole image: overlaoded floating point version//=========================================================================== void SLIC::DoRGBtoLABConversion(const unsigned int*& ubuff,double*& lvec,double*& avec,double*& bvec){int sz = m_width*m_height;lvec = new double[sz];avec = new double[sz];bvec = new double[sz];for( int j = 0; j < sz; j++ ){int r = (ubuff[j] >> 16) & 0xFF;int g = (ubuff[j] >> 8) & 0xFF;int b = (ubuff[j] ) & 0xFF;RGB2LAB( r, g, b, lvec[j], avec[j], bvec[j] );}}//===========================================================================/// DoRGBtoLABConversion////// For whole volume//===========================================================================void SLIC::DoRGBtoLABConversion(const unsigned int**& ubuff,double**& lvec,double**& avec,double**& bvec){int sz = m_width*m_height;for( int d = 0; d < m_depth; d++ ){for( int j = 0; j < sz; j++ ){int r = (ubuff[d][j] >> 16) & 0xFF;int g = (ubuff[d][j] >> 8) & 0xFF;int b = (ubuff[d][j] ) & 0xFF;RGB2LAB( r, g, b, lvec[d][j], avec[d][j], bvec[d][j] );}}}//================================================================================= /// DrawContoursAroundSegments////// Internal contour drawing option exists. One only needs to comment the if/// statement inside the loop that looks at neighbourhood.//=================================================================================void SLIC::DrawContoursAroundSegments(unsigned int* ubuff,const int* labels,const int& width,const int& height,const unsigned int& color ){const int dx8[8] = {-1, -1, 0, 1, 1, 1, 0, -1};const int dy8[8] = { 0, -1, -1, -1, 0, 1, 1, 1};int sz = width*height;vector<bool> istaken(sz, false);int mainindex(0);for( int j = 0; j < height; j++ ){for( int k = 0; k < width; k++ ){int np(0);for( int i = 0; i < 8; i++ ){int x = k + dx8[i];int y = j + dy8[i];if( (x >= 0 && x < width) && (y >= 0 && y < height) ){int index = y*width + x;if( false== istaken[index] )//comment this to obtain internal contours{if( labels[mainindex] != labels[index] ) np++;}}}if( np > 1 )//change to 2 or 3 for thinner lines{ubuff[mainindex] = color;istaken[mainindex] = true;}mainindex++;}}}//================================================================================= /// DrawContoursAroundSegmentsTwoColors////// Internal contour drawing option exists. One only needs to comment the if/// statement inside the loop that looks at neighbourhood.//================================================================================= void SLIC::DrawContoursAroundSegmentsTwoColors(unsigned int* img,const int* labels,const int& width,const int& height){const int dx[8] = {-1, -1, 0, 1, 1, 1, 0, -1};const int dy[8] = { 0, -1, -1, -1, 0, 1, 1, 1};int sz = width*height;vector<bool> istaken(sz, false);vector<int> contourx(sz);vector<int> contoury(sz);int mainindex(0);int cind(0);for( int j = 0; j < height; j++ ){for( int k = 0; k < width; k++ ){int np(0);for( int i = 0; i < 8; i++ ){int x = k + dx[i];int y = j + dy[i];if( (x >= 0 && x < width) && (y >= 0 && y < height) ){int index = y*width + x;//if( false == istaken[index] )//comment this to obtain internal contours{if( labels[mainindex] != labels[index] ) np++;}}}if( np > 1 ){contourx[cind] = k;contoury[cind] = j;istaken[mainindex] = true;//img[mainindex] = color;cind++;}mainindex++;}}int numboundpix = cind;//int(contourx.size());for( int j = 0; j < numboundpix; j++ ){int ii = contoury[j]*width + contourx[j];img[ii] = 0xffffff;//----------------------------------// Uncomment this for thicker lines//----------------------------------for( int n = 0; n < 8; n++ ){int x = contourx[j] + dx[n];int y = contoury[j] + dy[n];if( (x >= 0 && x < width) && (y >= 0 && y < height) ){int ind = y*width + x;if(!istaken[ind]) img[ind] = 0;}}}}//==============================================================================/// DetectLabEdges//==============================================================================void SLIC::DetectLabEdges(const double* lvec,const double* avec,const double* bvec,const int& width,const int& height,vector<double>& edges){int sz = width*height;edges.resize(sz,0);for( int j = 1; j < height-1; j++ ){for( int k = 1; k < width-1; k++ ){int i = j*width+k;double dx = (lvec[i-1]-lvec[i+1])*(lvec[i-1]-lvec[i+1]) +(avec[i-1]-avec[i+1])*(avec[i-1]-avec[i+1]) +(bvec[i-1]-bvec[i+1])*(bvec[i-1]-bvec[i+1]);double dy = (lvec[i-width]-lvec[i+width])*(lvec[i-width]-lvec[i+width]) +(avec[i-width]-avec[i+width])*(avec[i-width]-avec[i+width]) +(bvec[i-width]-bvec[i+width])*(bvec[i-width]-bvec[i+width]);//edges[i] = (sqrt(dx) + sqrt(dy));edges[i] = (dx + dy);}}}//===========================================================================/// PerturbSeeds//===========================================================================void SLIC::PerturbSeeds(vector<double>& kseedsl,vector<double>& kseedsa,vector<double>& kseedsb,vector<double>& kseedsx,vector<double>& kseedsy,const vector<double>& edges){const int dx8[8] = {-1, -1, 0, 1, 1, 1, 0, -1};const int dy8[8] = { 0, -1, -1, -1, 0, 1, 1, 1};int numseeds = kseedsl.size();for( int n = 0; n < numseeds; n++ ){int ox = kseedsx[n];//original xint oy = kseedsy[n];//original yint oind = oy*m_width + ox;int storeind = oind;for( int i = 0; i < 8; i++ ){int nx = ox+dx8[i];//new xint ny = oy+dy8[i];//new yif( nx >= 0 && nx < m_width && ny >= 0 && ny < m_height){int nind = ny*m_width + nx;if( edges[nind] < edges[storeind]){storeind = nind;}}}if(storeind != oind){kseedsx[n] = storeind%m_width;kseedsy[n] = storeind/m_width;kseedsl[n] = m_lvec[storeind];kseedsa[n] = m_avec[storeind];kseedsb[n] = m_bvec[storeind];}}}//=========================================================================== /// GetLABXYSeeds_ForGivenStepSize////// The k seed values are taken as uniform spatial pixel samples.//=========================================================================== void SLIC::GetLABXYSeeds_ForGivenStepSize(vector<double>& kseedsl,vector<double>& kseedsa,vector<double>& kseedsb,vector<double>& kseedsx,vector<double>& kseedsy,const int& STEP,const bool& perturbseeds,const vector<double>& edgemag){int numseeds(0);int n(0);//int xstrips = m_width/STEP;//int ystrips = m_height/STEP;int xstrips = (0.5+double(m_width)/double(STEP));int ystrips = (0.5+double(m_height)/double(STEP));int xerr = m_width - STEP*xstrips;int yerr = m_height - STEP*ystrips;double xerrperstrip = double(xerr)/double(xstrips);double yerrperstrip = double(yerr)/double(ystrips);int xoff = STEP/2;int yoff = STEP/2;//-------------------------numseeds = xstrips*ystrips;//-------------------------kseedsl.resize(numseeds);kseedsa.resize(numseeds);kseedsb.resize(numseeds);kseedsx.resize(numseeds);kseedsy.resize(numseeds);for( int y = 0; y < ystrips; y++ ){int ye = y*yerrperstrip;for( int x = 0; x < xstrips; x++ ){int xe = x*xerrperstrip;int i = (y*STEP+yoff+ye)*m_width + (x*STEP+xoff+xe);kseedsl[n] = m_lvec[i];kseedsa[n] = m_avec[i];kseedsb[n] = m_bvec[i];kseedsx[n] = (x*STEP+xoff+xe);kseedsy[n] = (y*STEP+yoff+ye);n++;}}if(perturbseeds){PerturbSeeds(kseedsl, kseedsa, kseedsb, kseedsx, kseedsy, edgemag);}}//=========================================================================== /// GetLABXYSeeds_ForGivenK////// The k seed values are taken as uniform spatial pixel samples.//=========================================================================== void SLIC::GetLABXYSeeds_ForGivenK(vector<double>& kseedsl,vector<double>& kseedsa,vector<double>& kseedsb,vector<double>& kseedsx,vector<double>& kseedsy,const int& K,const bool& perturbseeds,const vector<double>& edgemag){int sz = m_width*m_height;double step = sqrt(double(sz)/double(K));int T = step;int xoff = step/2;int yoff = step/2;int n(0);int r(0);for( int y = 0; y < m_height; y++ ){int Y = y*step + yoff;if( Y > m_height-1 ) break;for( int x = 0; x < m_width; x++ ){//int X = x*step + xoff;//square gridint X = x*step + (xoff<<(r&0x1));//hex gridif(X > m_width-1) break;int i = Y*m_width + X;//_ASSERT(n < K);//kseedsl[n] = m_lvec[i];//kseedsa[n] = m_avec[i];//kseedsb[n] = m_bvec[i];//kseedsx[n] = X;//kseedsy[n] = Y;kseedsl.push_back(m_lvec[i]);kseedsa.push_back(m_avec[i]);kseedsb.push_back(m_bvec[i]);kseedsx.push_back(X);kseedsy.push_back(Y);n++;}r++;}if(perturbseeds){PerturbSeeds(kseedsl, kseedsa, kseedsb, kseedsx, kseedsy, edgemag); }}//===========================================================================/// PerformSuperpixelSegmentation_VariableSandM////// Magic SLIC - no parameters////// Performs k mean segmentation. It is fast because it looks locally, not/// over the entire image./// This function picks the maximum value of color distance as compact factor/// M and maximum pixel distance as grid step size S from each cluster (13 April 2011). /// So no need to input a constant value of M and S. There are two clear/// advantages:////// [1] The algorithm now better handles both textured and non-textured regions/// [2] There is not need to set any parameters////// SLICO (or SLIC Zero) dynamically varies only the compactness factor S,/// not the step size S.//===========================================================================void SLIC::PerformSuperpixelSegmentation_VariableSandM(vector<double>& kseedsl,vector<double>& kseedsa,vector<double>& kseedsb,vector<double>& kseedsx,vector<double>& kseedsy,int* klabels,const int& STEP,const int& NUMITR){int sz = m_width*m_height;const int numk = kseedsl.size();//double cumerr(99999.9);int numitr(0);//----------------int offset = STEP;if(STEP < 10) offset = STEP*1.5;//----------------vector<double> sigmal(numk, 0);vector<double> sigmaa(numk, 0);vector<double> sigmab(numk, 0);vector<double> sigmax(numk, 0);vector<double> sigmay(numk, 0);vector<int> clustersize(numk, 0);vector<double> inv(numk, 0);//to store 1/clustersize[k] valuesvector<double> distxy(sz, DBL_MAX);vector<double> distlab(sz, DBL_MAX);vector<double> distvec(sz, DBL_MAX);vector<double> maxlab(numk, 10*10);//THIS IS THE VARIABLE VALUE OF M, just start with 10 vector<double> maxxy(numk, STEP*STEP);//THIS IS THE VARIABLE VALUE OF M, just start with 10double invxywt = 1.0/(STEP*STEP);//NOTE: this is different from how usual SLIC/LKM workswhile( numitr < NUMITR ){//------//cumerr = 0;numitr++;//------distvec.assign(sz, DBL_MAX);for( int n = 0; n < numk; n++ ){int y1 = max(0, kseedsy[n]-offset);int y2 = min(m_height, k seedsy[n]+offset);int x1 = max(0, kseedsx[n]-offset);int x2 = min(m_width, kseedsx[n]+offset);for( int y = y1; y < y2; y++ ){for( int x = x1; x < x2; x++ ){int i = y*m_width + x;_ASSERT( y < m_height && x < m_width && y >= 0 && x >= 0 );double l = m_lvec[i];double a = m_avec[i];double b = m_bvec[i];distlab[i] = (l - kseedsl[n])*(l - kseedsl[n]) +(a - kseedsa[n])*(a - kseedsa[n]) +(b - kseedsb[n])*(b - kseedsb[n]);distxy[i] = (x - kseedsx[n])*(x - kseedsx[n]) +(y - kseedsy[n])*(y - kseedsy[n]);//------------------------------------------------------------------------double dist = distlab[i]/maxlab[n] + distxy[i]*invxywt;//only varying m, prettier superpixels//double dist = distlab[i]/maxlab[n] + distxy[i]/maxxy[n];//varying both m and S//------------------------------------------------------------------------if( dist < distvec[i] ){distvec[i] = dist;klabels[i] = n;}}}}//-----------------------------------------------------------------// Assign the max color distance for a cluster//-----------------------------------------------------------------if(0 == numitr){maxlab.assign(numk,1);maxxy.assign(numk,1);}{for( int i = 0; i < sz; i++ ){if(maxlab[klabels[i]] < distlab[i]) maxlab[klabels[i]] = distlab[i];if(maxxy[klabels[i]] < distxy[i]) maxxy[klabels[i]] = distxy[i];}}//-----------------------------------------------------------------// Recalculate the centroid and store in the seed values//-----------------------------------------------------------------sigmal.assign(numk, 0);sigmaa.assign(numk, 0);sigmab.assign(numk, 0);sigmax.assign(numk, 0);sigmay.assign(numk, 0);clustersize.assign(numk, 0);for( int j = 0; j < sz; j++ ){int temp = klabels[j];_ASSERT(klabels[j] >= 0);sigmal[klabels[j]] += m_lvec[j];sigmaa[klabels[j]] += m_avec[j];sigmab[klabels[j]] += m_bvec[j];sigmax[klabels[j]] += (j%m_width);sigmay[klabels[j]] += (j/m_width);clustersize[klabels[j]]++;}{for( int k = 0; k < numk; k++ ){//_ASSERT(clustersize[k] > 0);if( clustersize[k] <= 0 ) clustersize[k] = 1;inv[k] = 1.0/double(clustersize[k]);//computing inverse now to multiply, than divide later}}{for( int k = 0; k < numk; k++ ){kseedsl[k] = sigmal[k]*inv[k];kseedsa[k] = sigmaa[k]*inv[k];kseedsb[k] = sigmab[k]*inv[k];kseedsx[k] = sigmax[k]*inv[k];kseedsy[k] = sigmay[k]*inv[k];}}}}//===========================================================================/// SaveSuperpixelLabels////// Save labels in raster scan order.//===========================================================================void SLIC::SaveSuperpixelLabels(const int* labels,const int& width,const int& height,const string& filename,const string& path){int sz = width*height;char fname[_MAX_FNAME];char extn[_MAX_FNAME];_splitpath(filename.c_str(), NULL, NULL, fname, extn);string temp = fname;ofstream outfile;string finalpath = path + temp + string(".dat");outfile.open(finalpath.c_str(), ios::binary);for( int i = 0; i < sz; i++ ){outfile.write((const char*)&labels[i], sizeof(int));}outfile.close();}//===========================================================================/// EnforceLabelConnectivity////// 1. finding an adjacent label for each new component at the start/// 2. if a certain component is too small, assigning the previously found/// adjacent label to this component, and not incrementing the label.//===========================================================================void SLIC::EnforceLabelConnectivity(const int* labels,//input labels that need to be corrected to remove stray labelsconst int& width,const int& height,int* nlabels,//new labelsint& numlabels,//the number of labels changes in the end if segments are removedconst int& K) //the number of superpixels desired by the user{// const int dx8[8] = {-1, -1, 0, 1, 1, 1, 0, -1};// const int dy8[8] = { 0, -1, -1, -1, 0, 1, 1, 1};const int dx4[4] = {-1, 0, 1, 0};const int dy4[4] = { 0, -1, 0, 1};const int sz = width*height;const int SUPSZ = sz/K;//nlabels.resize(sz, -1);for( int i = 0; i < sz; i++ ) nlabels[i] = -1;int label(0);int* xvec = new int[sz];int* yvec = new int[sz];int oindex(0);int adjlabel(0);//adjacent labelfor( int j = 0; j < height; j++ ){for( int k = 0; k < width; k++ ){if( 0 > nlabels[oindex] ){nlabels[oindex] = label;//--------------------// Start a new segment//--------------------xvec[0] = k;yvec[0] = j;//-------------------------------------------------------// Quickly find an adjacent label for use later if needed//-------------------------------------------------------{for( int n = 0; n < 4; n++ ){int x = xvec[0] + dx4[n];int y = yvec[0] + dy4[n];if( (x >= 0 && x < width) && (y >= 0 && y < height) ){int nindex = y*width + x;if(nlabels[nindex] >= 0) adjlabel = nlabels[nindex];}}}int count(1);for( int c = 0; c < count; c++ ){for( int n = 0; n < 4; n++ ){int x = xvec[c] + dx4[n];int y = yvec[c] + dy4[n];if( (x >= 0 && x < width) && (y >= 0 && y < height) ){int nindex = y*width + x;if( 0 > nlabels[nindex] && labels[oindex] == labels[nindex] ){xvec[count] = x;yvec[count] = y;nlabels[nindex] = label;count++;}}}}//-------------------------------------------------------// If segment size is less then a limit, assign an// adjacent label found before, and decrement label count.//-------------------------------------------------------if(count <= SUPSZ >> 2){for( int c = 0; c < count; c++ ){int ind = yvec[c]*width+xvec[c];nlabels[ind] = adjlabel;}label--;}label++;}oindex++;}}numlabels = label;if(xvec) delete [] xvec;if(yvec) delete [] yvec;}//=========================================================================== /// PerformSLICO_ForGivenStepSize////// There is option to save the labels if needed.//=========================================================================== void SLIC::PerformSLICO_ForGivenStepSize(const unsigned int* ubuff,const int width,const int height,int* klabels,int& numlabels,const int& STEP,const double& m){vector<double> kseedsl(0);vector<double> kseedsa(0);vector<double> kseedsb(0);vector<double> kseedsx(0);vector<double> kseedsy(0);//--------------------------------------------------m_width = width;m_height = height;int sz = m_width*m_height;//klabels.resize( sz, -1 );//--------------------------------------------------//klabels = new int[sz];for( int s = 0; s < sz; s++ ) klabels[s] = -1;//--------------------------------------------------DoRGBtoLABConversion(ubuff, m_lvec, m_avec, m_bvec);//--------------------------------------------------bool perturbseeds(true);vector<double> edgemag(0);if(perturbseeds) DetectLabEdges(m_lvec, m_avec, m_bvec, m_width, m_height, edgemag);GetLABXYSeeds_ForGivenStepSize(kseedsl, kseedsa, kseedsb, kseedsx, kseedsy, STEP, perturbseeds, edgemag);PerformSuperpixelSegmentation_VariableSandM(kseedsl,kseedsa,kseedsb,kseedsx,kseedsy,kl abels,STEP,10);numlabels = kseedsl.size();int* nlabels = new int[sz];EnforceLabelConnectivity(klabels, m_width, m_height, nlabels, numlabels,double(sz)/double(STEP*STEP));{for(int i = 0; i < sz; i++ ) klabels[i] = nlabels[i];}if(nlabels) delete [] nlabels;}//===========================================================================/// PerformSLICO_ForGivenK////// Zero parameter SLIC algorithm for a given number K of superpixels.//===========================================================================void SLIC::PerformSLICO_ForGivenK(const unsigned int* ubuff,const int width,const int height,int* klabels,int& numlabels,const int& K,//required number of superpixelsconst double& m)//weight given to spatial distance{vector<double> kseedsl(0);vector<double> kseedsa(0);vector<double> kseedsb(0);vector<double> kseedsx(0);vector<double> kseedsy(0);//--------------------------------------------------m_width = width;m_height = height;int sz = m_width*m_height;//--------------------------------------------------//if(0 == klabels) klabels = new int[sz];for( int s = 0; s < sz; s++ ) klabels[s] = -1;//--------------------------------------------------if(1)//LAB{DoRGBtoLABConversion(ubuff, m_lvec, m_avec, m_bvec);}else//RGB{m_lvec = new double[sz]; m_avec = new double[sz]; m_bvec = new double[sz];for( int i = 0; i < sz; i++ ){m_lvec[i] = ubuff[i] >> 16 & 0xff;m_avec[i] = ubuff[i] >> 8 & 0xff;m_bvec[i] = ubuff[i] & 0xff;}}//--------------------------------------------------bool perturbseeds(true);vector<double> edgemag(0);if(perturbseeds) DetectLabEdges(m_lvec, m_avec, m_bvec, m_width, m_height, edgemag);GetLABXYSeeds_ForGivenK(kseedsl, kseedsa, kseedsb, kseedsx, kseedsy, K, perturbseeds, edgemag);int STEP = sqrt(double(sz)/double(K)) + 2.0;//adding a small value in the even the STEP size is too small.//PerformSuperpixelSLIC(kseedsl, kseedsa, kseedsb, kseedsx, kseedsy, klabels, STEP, edgemag, m);PerformSuperpixelSegmentation_VariableSandM(kseedsl,kseedsa,kseedsb,kseedsx,kseedsy,kl abels,STEP,10);numlabels = kseedsl.size();int* nlabels = new int[sz];EnforceLabelConnectivity(klabels, m_width, m_height, nlabels, numlabels, K);{for(int i = 0; i < sz; i++ ) klabels[i] = nlabels[i];}if(nlabels) delete [] nlabels;}。