SLIC超像素分割算法分析
1:导入原始照片,初始化聚类中心,按照设定的超像素个数,在图像内均匀的分配聚类中心。假设图片总共有 N 个像素点,预分割为 s 个相同尺寸的超像素,那么每个超像素的大小为N/ s ,则相邻种子点(聚类中心)的距离近似为S=sqrt(N/s)。
2:在种子点的n*n邻域内重新选择聚类中心。计算该邻域内所有像素点的梯度值,将种子点移到该邻域内梯度最小的地方,可以避免种子点落在梯度较大的轮廓边界上,以免影响后续聚类效果。
3:在每个种子点周围的邻域内为每个像素点进行分类,和标准的k-means在整张图中搜索不同,SLIC的期望的超像素尺寸为SS,但是搜索的范围是2S*2S。
4:距离度量。包括颜色距离和空间距离。对于每个搜索到的像素点,分别计算它和该种子点的距离,本质还是采用欧氏距离公式,计算方法如下:
其中,dc代表颜色距离,ds代表空间距离,Ns是类内最大空间距离。最大的颜色距离Nc既随图片不同而不同,也随聚类不同而不同,所以我们取一个固定常数m(1<m<40),一般取10代替。最终的距离度量D’如下:
由于每个像素点都会被多个聚类中心搜索到并计算其距离,最后选取最小的距离值作为该像素点的聚类中心。
5:迭代优化,一般达到迭代次数或者达到误差收敛(即每个像素点聚类中心不再发生变化为止)。
6:增强连通性。经过迭代优化可能出现多连通情况、超像素尺寸过小,单个超像素被切割成多个不连续超像素等,这些情况可以通过进行增强连通性解决。
7:基于SLIC超像素分割完毕,显示分割图像。
运行软件版本:MATLAB2014a(大于此版本即可)
运行方法:点击main.m文件,然后选择自己要超像素分割的照片即可。
MATLAB代码如下:
main.m文件
%选择要进行超像素分割图像的路径
close all;clc;
[filename, pathname, filterindex] = uigetfile(‘C:\Users\Hasee\Desktop\毕业设计\测试图库.jpg’, ‘选择图片’);
file = fullfile(pathname, filename);
%设置读取的照片为全局变量
global I;
%读取要选择的照片
I= imread(file);
figure,imshow(I);
title(‘原始图片’)
%利用SLIC函数处理待分割图像:
%超像素尺寸s=15,errTh为控制迭代结束的联合向量残差上限为10-2即0.01,控制色域与空域权重比例的系数wDs为0.52即0.25
s=15;
errTh=10^-2;
wDs=0.5^2;
Label=SLIC(I,s,errTh,wDs);
%% 显示轮廓
%矩阵初始化为矩阵
marker=zeros(size(Label));
%记录大小为m*n
[m,n]=size(Label);
for i=1:m
for j=1:n
top=Label(max(1,i-1),j);
bottom=Label(min(m,i+1),j);
left=Label(i,max(1,j-1));
right=Label(i,min(n,j+1));
if ~(topbottom && bottomleft && left==right)
marker(i,j)=1;
end
end
end
I2=I;
for i=1:m
for j=1:n
if marker(i,j)==1
I2(i,j,:)=0;
end
end
end
figure,imshow(I2);
title(‘分割结果’)
SLIC.m文件
function Label=SLIC(img,s,errTh,wDs)
% 基于KMeans的超像素分割
% img为输入图像,维度不限,最大值为255
% s x s为超像素尺寸
% errTh为控制迭代结束的联合向量残差上限
m=size(img,1);
n=size(img,2);
%% 计算栅格顶点与中心的坐标
%图像长度与超像素尺寸比值向下取整
h=floor(m/s);
%图像宽度与超像素尺寸比值向下取整
w=floor(n/s);
rowR=floor((m-hs)/2); %多余部分首尾均分
colR=floor((n-ws)/2);
rowStart=(rowR+1)😒:(m-s+1);
rowStart(1)=1;
rowEnd=rowStart+s;
rowEnd(1)=rowR+s;
rowEnd(end)=m;
colStart=(colR+1)😒:(n-s+1);
colStart(1)=1;
colEnd=colStart+s;
colEnd(1)=colR+s;
colEnd(end)=n;
rowC=floor((rowStart+rowEnd-1)/2);
colC=floor((colStart+colEnd-1)/2);
% 显示划分栅格结果
%先初始化temp为m*n的零矩阵
temp=zeros(m,n);
temp(rowStart,:)=1;
temp(:,colStart)=1;
for i=1:h
for j=1:w
temp(rowC(i),colC(j))=1;
end
end
figure,imshow(temp);
title(‘栅格划分结果’)
%保存栅格划分结果为栅格.jpg文件
imwrite(temp,‘栅格.jpg’);
%% 利用sobel算子和欧式距离计算梯度图像
img=double(img)/255;
%分别提取图像的三通道信息
r=img(:,:,1);
g=img(:,:,2);
b=img(:,:,3);
%采用加权法进行灰度化处理,权值分别为0.299 , 0.587,0.114
Y=0.299 * r + 0.587 * g + 0.114 * b;
f1=fspecial(‘sobel’);
f2=f1’;
gx=imfilter(Y,f1);
gy=imfilter(Y,f2);
G=sqrt(gx.2+gy.2);
%% 选择栅格中心点33邻域中梯度最小点作为起始点
rowC_std=repmat(rowC’,[1,w]);
colC_std=repmat(colC,[h,1]);
rowC=rowC_std;
colC=colC_std;
for i=1:h
for j=1:w
block=G(rowC(i,j)-1:rowC(i,j)+1,colC(i,j)-1:colC(i,j)+1);
[minVal,idxArr]=min(block(😃);
jOffset=floor((idxArr(1)+2)/3);
iOffset=idxArr(1)-3(jOffset-1);
rowC(i,j)=rowC(i,j)+iOffset;
colC(i,j)=colC(i,j)+jOffset;
end
end
%% KMeans超像素分割
Label=zeros(m,n)-1;
dis=Infones(m,n);
M=reshape(img,mn,size(img,3)); %像素值重排
% 联合色域值和空域值
colorC=zeros(h,w,size(img,3));
for i=1:h
for j=1:w
colorC(i,j,:)=img(rowC(i),colC(j)😅;
end
end
uniMat=cat(3,colorC,rowC,colC);
uniMat=reshape(uniMat,hw,size(img,3)+2);
iter=1;
while(1)
uniMat_old=uniMat;
% rowC_old=rowC;
% colC_old=colC;
for k=1:hw
c=floor((k-1)/h)+1;
r=k-h*(c-1);
rowCidx=rowC(r,c);
colCidx=colC(r,c); %聚类中心坐标
%聚类限定的栅格(中心点始终是原s x s栅格的中心点)
rowStart=max(1,rowC_std(r,c)-s);
rowEnd=min(m,rowC_std(r,c)+s-1);
colStart=max(1,colC_std(r,c)-s);
colEnd=min(n,colC_std(r,c)+s);
% colorC=uniMat(k,1:size(img,3));
colorC=M((colCidx-1)*m+rowCidx,:);
for i=rowStart:rowEnd
for j=colStart:colEnd
colorCur=M((j-1)*m+i,:);
dc=norm(colorC-colorCur);
ds=norm([i-rowCidx,j-colCidx]);
d=dc2+wDs*(ds/s)2;
if d<dis(i,j)
dis(i,j)=d;
Label(i,j)=k;
end
end
end
end
%显示聚类结果
temp=mod(Label,20)+1;
figure;
imagesc(label2rgb(temp-1,'jet','w','shuffle')) ;
title('聚类结果')
axis image ; axis off ;
F=getframe(gcf);
I=frame2im(F);
[I,map]=rgb2ind(I,256);
if iter == 1
imwrite(I,map,'test.gif','gif','Loopcount',inf,'DelayTime',0.2);
else
imwrite(I,map,'test.gif','gif','WriteMode','append','DelayTime',0.2);
end
iter=iter+1;
% 开始更新聚类中心
colorC=zeros(h,w,size(img,3));
for k=1:h*w
num=0;
sumColor=zeros(1,size(img,3));
sumR=0;
sumC=0;
c=floor((k-1)/h)+1;
r=k-h*(c-1);
rowCidx=rowC_std(r,c);
colCidx=colC_std(r,c);
rowStart=max(1,rowCidx-s);
rowEnd=min(m,rowCidx+s-1);
colStart=max(1,colCidx-s);
colEnd=min(n,colCidx+s);
for row=rowStart:rowEnd
for col=colStart:colEnd
if Label(row,col)==k
num=num+1;
sumR=sumR+row;
sumC=sumC+col;
color=reshape(img(row,col,:),1,size(img,3));
sumColor=sumColor+color;
end
end
end
colorC(r,c,:)=sumColor/num;
rowC(r,c)=round(sumR/num);
colC(r,c)=round(sumC/num);
end
uniMat=cat(3,colorC,rowC,colC);
uniMat=reshape(uniMat,h*w,size(img,3)+2);
diff=uniMat-uniMat_old;
diff(:,1:2)=sqrt(wDs)*diff(:,1:2)/s;
err=norm(diff)/sqrt(h*w);
if err<errTh %errTh是给定的阈值,残差低于阈值,结束迭代
break;
end
end
%% 按照边界接触点数最多原则分配小连通域的标签
for k=1:hw
c=floor((k-1)/h)+1;
r=k-h(c-1);
rowCidx=rowC_std(r,c);
colCidx=colC_std(r,c);
rowStart=max(1,rowCidx-s);
rowEnd=min(m,rowCidx+s-1);
colStart=max(1,colCidx-s);
colEnd=min(n,colCidx+s);
block=Label(rowStart:rowEnd,colStart:colEnd);
block(block~=k)=0;
block(block==k)=1;
label=bwlabel(block);
%标签个数最大值
szlabel=max(label(😃);
%block的高高
bh=rowEnd-rowStart+1;
%block的宽高
bw=colEnd-colStart+1;
if szlabel<2 %无伴生连通域,继续执行
continue;
end
labelC=label(rowCidx-rowStart+1,colCidx-colStart+1); %主连通域的标记值
top=max(1,rowStart-1);
bottom=min(m,rowEnd+1);
left=max(1,colStart-1);
right=min(n,colEnd+1);
for i=1:szlabel %遍历连通域
if i==labelC %主连通域不处理
continue;
end
marker=zeros(bottom-top+1,right-left+1); %生成一个外扩一圈的marker,标记哪些点已经被统计过接触情况
bw=label;
bw(bw~=i)=0;
bw(bw==i)=1; %当前连通域标记图
contourBW=bwperim(bw); %求取外轮廓
idxArr=find(double(contourBW)==1);
labelArr=zeros(4*length(idxArr),1); %记录轮廓点的4邻域点标记值的向量
num=0;
for idx=1:size(idxArr) %遍历轮廓点,统计其4邻域点的标记值
bc=floor((idxArr(idx)-1)/bh)+1;
br=idxArr(idx)-bh*(bc-1); %轮廓点在block中的行列信息
row=br+rowStart-1;
col=bc+colStart-1; %轮廓点在大图中的行列信息
rc=[row-1,col;...
row+1,col;...
row,col-1;...
row,col+1];
for p=1:4
row=rc(p,1);
col=rc(p,2);
if ~(row>=1 && row<=m && col>=1 && col<=n && Label(row,col)~=k)
continue;
end
if marker(row-top+1,col-left+1)==0 %未被统计过
marker(row-top+1,col-left+1)=1;
num=num+1;
labelArr(num)=Label(row,col);
end
end
end
labelArr(find(labelArr==0))=[]; %去除零元素
uniqueLabel=unique(labelArr);
numArr=zeros(length(uniqueLabel),1);
for p=1:length(uniqueLabel)
idx=find(labelArr==uniqueLabel(p));
numArr(p)=length(idx);
end
idx=find(numArr==max(numArr));
maxnumLabel=uniqueLabel(idx(1)); %接触最多的标签
for row=rowStart:rowEnd
for col=colStart:colEnd
if bw(row-rowStart+1,col-colStart+1)==0
continue;
end
Label(row,col)=maxnumLabel;
end
end
end
end
% 显示连通域处理后聚类结果
temp=mod(Label,20)+1;
figure;
imagesc(label2rgb(temp-1,‘jet’,‘w’,‘shuffle’)) ;
title(‘连通域处理后的聚类结果’)
axis image ; axis off ;
原始图像:
栅格划分结果:
聚类结果:
连通域处理后的聚类结果:
超像素分割结果: