您的位置:首页 > 编程语言

SLIC Superpixels 算法代码学习笔记

2015-01-14 14:13 253 查看
1.主程序入口

下面的程序就是超像素生成的函数入口:

slic.DoSuperpixelSegmentation_ForGivenNumberOfSuperpixels(img, width, height, labels,
numlabels, m_spcount, m_compactness);
这里有几个特别的参数需要说明:

slic:SLIC slic; 是一个SLIC类
labels:int* labels = new int[sz];一张标签图,和图像大小一致,用于标记每个像素的标签值;sz=width*height,即一张图像的像素总数。
numlabels:int numlabels(0);是图像最终分成的类数,即最终生成的超像素个数,在这里被初始化为0。
m_spcount: 是客户从界面输入的值,即初始化的种子个数,但是SLIC算法中不一定每个种子最终都能得一个超像素,由于某些因素可能被其他超像素合并。若种子数不符合规定,则通过(总像素值SZ)/(每个超像素默认大小200)获得种子数:if (m_spcount
< 20 || m_spcount > sz/4) m_spcount = sz/200;
m_compactness:if(m_compactness < 1.0 || m_compactness > 80.0) m_compactness = 20.0;这个值也是有用户设定的,是颜色特征和XY坐标特征之间的紧密度比例,20这个值效果往往不错。

该函数的定义:

void SLIC::DoSuperpixelSegmentation_ForGivenNumberOfSuperpixels(
unsigned int *
ubuff,//img
const int
width,
const int
height,
int*& klabels,//labels
int& numlabels, //
const int &
K, //初始化的种子数m_spcount
const double &
compactness) //m_compactness空间参数转换的权重值
{
const int superpixelsize
= 0.5+double(width*height)/ double(K);
DoSuperpixelSegmentation_ForGivenSuperpixelSize(ubuff,width,height,klabels,numlabels,superpixelsize,compactness);
}
superpixelsize:超像素的大小,即每个超像素中包含的像素值
DoSuperpixelSegmentation_ForGivenSuperpixelSize函数中完成了超像素生成的功能
const int STEP
= sqrt(double(superpixelsize))+0.5;这个变量很关键,是种子点的跨度。

2.子程序流程
在DoSuperpixelSegmentation_ForGivenSuperpixelSize函数中主要包含以下函数:

DoRGBtoLABConversion(ubuff, m_lvec, m_avec, m_bvec);

将RGB图像转换为LAB图像。

GetLABXYSeeds_ForGivenStepSize(kseedsl, kseedsa, kseedsb, kseedsx, kseedsy,
STEP, perturbseeds, edgemag);

均匀分布种子点,将种子点的5维特征值LABXY作为分类的中心点特征值存入kseeds向量中。

PerformSuperpixelSLIC(kseedsl, kseedsa, kseedsb, kseedsx, kseedsy, klabels,
STEP, edgemag,compactness);

对整张图像进行局部的K-Means聚类,生成超像素。这是超像素生成的关键步骤,也耗时最多。

EnforceLabelConnectivity(klabels, m_width, m_height, nlabels, numlabels, double(sz)/double(STEP*STEP));

对生成的初步超像素图像,进行合并孤立超像素,某些孤点像素与大小过小的超像素被合并到附近的超像素中。

3.关键程序解析:这里只讲PerformSuperpixelSLIC与EnforceLabelConnectivity

PerformSuperpixelSLIC:

(1)核心就是局部的K-Means聚类





局部顾名思义,就是只对种子点附近的像素进行聚类,这里种子是按照STEP=S的跨度分布的,稍微扩大一点聚类范围,选为边长为2S矩形。
(2)特征值计算







上面即像素到种子点的“距离”计算,距离中包括了LABXY5个特征值。方法就是,在局部区域内对每个像素点求其到中心的距离,若小于以前存放的距离,则将距离更新,且更新该像素点的类别标签。
(3)种子点特征值更新




上部分程序,将超像素中的特征值加在一起。




上部分程序将各特征值的平均值作为中心点的特征值。
整个KMeans聚类迭代次数为10,即上面的内容重复10次,每次的像素点所属的类都有可能变化。
EnforceLabelConnectivity

函数定义与说明

void SLIC::EnforceLabelConnectivity(

const int * labels, //input
labels that need to be corrected to remove stray labels

const int width,

const int height,

int*& nlabels, //new
labels

int& numlabels, //the
number of labels changes in the end if segments are removed

const int & K)

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
label
for( 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;
//在像素点4领域内找到被标记的标签,记为adjlabel (该点的邻域标签)
{ for( int n
= 0; n < 4; n++ )
{
int x
= xvec[0] + dx4
;
int y
= yvec[0] + dy4
;
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++ )
{
//以4邻域区域增长的方式找和原始像素点标签相同的像素点的个数
int x
= xvec[c] + dx4
;
int y
= yvec[c] + dy4
;
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++;
}
}
}
}
//若区域小于超像素预定值的1/4,则与相邻的类进行合并,adjlabel。
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: