这一篇文章主要介绍了SLIC算法的大致分析和理解,提供了源码,供大家复制下载。
目录
部分代码分析
1.RGB -> XYZ
大致分析:
?详细代码:
2.RGB ->?LAB?
大致分析:
具体代码:
3.将一个图像中的全部点进行二次转化
大致分析:
具体代码:
4.计算图像中每个像素点的梯度
大致分析:
具体代码:
?5.在 3*3 的领域内选择梯度最小的点作为中心节点
大致思路:
具体代码:
6.移动聚类中心到 3*3 领域中梯度最小的位置
大致分析:
具体代码:
7.计算像素点与聚类中心的距离+分类+重新分类聚类中心
大致分析:
?具体代码:
8.提取图片中每个像素点的R、G、B的值
具体代码:
9.给定K个超像素的SLIC算法
具体代码:
完整代码
SLIC.cpp
SLIC.h
运行结果:
部分代码分析
1.RGB -> XYZ
大致分析:
?详细代码:
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;
}
2.RGB ->?LAB?
大致分析:
具体代码:
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 standard
double kappa = 903.3; //actual CIE standard
double Xr = 0.950456; //reference white
double Yr = 1.0; //reference white
double Zr = 1.088754; //reference white
double 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);
}
3.将一个图像中的全部点进行二次转化
大致分析:
去一个数组的24位的 23~16位, 15~8位,7~0位作为 R、G、B?的值。采用第四步中的方法将其转化为 L、 A、 B的值。
具体代码:
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; //取高16位的八位
int g = (ubuff[j] >> 8) & 0xFF; //取高8位的八位
int b = (ubuff[j] ) & 0xFF; //取低位的八位
RGB2LAB( r, g, b, lvec[j], avec[j], bvec[j] );
}
}
4.计算图像中每个像素点的梯度
大致分析:
具体代码:
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);
}
}
}
?5.在 3*3 的领域内选择梯度最小的点作为中心节点
大致思路:
用for循环来遍历图像中的每一个像素点,利用数组来锁定 3*3 的领域。在调用第6步的方法进行对比,如果梯度小的话,就更换中心节点,否则就不变。
具体代码:
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 x
int oy = kseedsy[n];//original y
int oind = oy*m_width + ox;
int storeind = oind;
for( int i = 0; i < 8; i++ )
{
int nx = ox+dx8[i];//new x
int ny = oy+dy8[i];//new y
if( 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];
}
}
}
6.移动聚类中心到 3*3 领域中梯度最小的位置
大致分析:
利用Sqr(N/K)来遍历整个像素点,然后调用第7步中方法来实现聚类中心的移动。
具体代码:
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 grid
int X = x*step + (xoff<<(r&0x1));//hex grid
if(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);
}
}
7.计算像素点与聚类中心的距离+分类+重新分类聚类中心
大致分析:
距离使用的是欧式距离,总距离?D? 由?dc 颜色距离与?ds 空间距离两部分组成。公式如下:
标记每个像素点的类别为距离其最小的聚类中心的类别。
计算属于同一个聚类的所有像素点的平均向量值,重新得到聚类中心 。
?具体代码:
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] values
vector<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 10
double invxywt = 1.0/(STEP*STEP);//NOTE: this is different from how usual SLIC/LKM works
while( numitr < NUMITR )
{
//------
//cumerr = 0;
numitr++;
//------
distvec.assign(sz, DBL_MAX);
for( int n = 0; n < numk; n++ )
{
int y1 = max(0, (int)(kseedsy[n]-offset));
int y2 = min(m_height, (int)(kseedsy[n]+offset));
int x1 = max(0, (int)(kseedsx[n]-offset));
int x2 = min(m_width, (int)(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];
}}
}
}
8.提取图片中每个像素点的R、G、B的值
具体代码:
void SLIC::SaveSuperpixelLabels2PPM(
char* filename,
int * labels,
const int width,
const int height)
{
FILE* fp;
char header[20];
fp = fopen(filename, "wb");
// write the PPM header info, such as type, width, height and maximum
fprintf(fp,"P6\n%d %d\n255\n", width, height);
// write the RGB data
unsigned char *rgb = new unsigned char [ (width)*(height)*3 ];
int k = 0;
unsigned char c = 0;
for ( int i = 0; i < (height); i++ ) {
for ( int j = 0; j < (width); j++ ) {
c = (unsigned char)(labels[k]);
rgb[i*(width)*3 + j*3 + 2] = labels[k] >> 16 & 0xff; // r
rgb[i*(width)*3 + j*3 + 1] = labels[k] >> 8 & 0xff; // g
rgb[i*(width)*3 + j*3 + 0] = labels[k] & 0xff; // b
// rgb[i*(width) + j + 0] = c;
k++;
}
}
fwrite(rgb, width*height*3, 1, fp);
delete [] rgb;
fclose(fp);
}
9.给定K个超像素的SLIC算法
具体代码:
void SLIC::PerformSLICO_ForGivenK(
const unsigned int* ubuff,
const int width,
const int height,
int* klabels,
int& numlabels,
const int& K,//required number of superpixels
const 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); //RGB->LAB
}
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);//移动聚类中心到 3*3 领域中梯度最小的位置
int STEP = sqrt(double(sz)/double(K)) + 2.0;//adding a small value in the even the STEP size is too small.
PerformSuperpixelSegmentation_VariableSandM(kseedsl,kseedsa,kseedsb,kseedsx,kseedsy,klabels,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;
}
完整代码
SLIC.cpp
// 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.
//
// https://www.epfl.ch/labs/ivrl/research/slic-superpixels/
//===========================================================================
// Copyright (c) 2013 Radhakrishna Achanta.
//
// For commercial use please contact the author:
//
// Email: firstname.lastname@epfl.ch
//===========================================================================
#include <stdio.h>
#include <cfloat>
#include <cmath>
#include <iostream>
#include <fstream>
#include "SLIC.h"
#include <chrono>
#include <windows.h>
typedef chrono::high_resolution_clock Clock;
// For superpixels
const 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 supervoxels
const 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 standard
double kappa = 903.3; //actual CIE standard
double Xr = 0.950456; //reference white
double Yr = 1.0; //reference white
double Zr = 1.088754; //reference white
double 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; //取高16位的八位
int g = (ubuff[j] >> 8) & 0xFF; //取高8位的八位
int b = (ubuff[j] ) & 0xFF; //取低位的八位
RGB2LAB( r, g, b, lvec[j], avec[j], bvec[j] );
}
}
//==============================================================================
/// 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 x
int oy = kseedsy[n];//original y
int oind = oy*m_width + ox;
int storeind = oind;
for( int i = 0; i < 8; i++ )
{
int nx = ox+dx8[i];//new x
int ny = oy+dy8[i];//new y
if( 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_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 grid
int X = x*step + (xoff<<(r&0x1));//hex grid
if(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] values
vector<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 10
double invxywt = 1.0/(STEP*STEP);//NOTE: this is different from how usual SLIC/LKM works
while( numitr < NUMITR )
{
//------
//cumerr = 0;
numitr++;
//------
distvec.assign(sz, DBL_MAX);
for( int n = 0; n < numk; n++ )
{
int y1 = max(0, (int)(kseedsy[n]-offset));
int y2 = min(m_height, (int)(kseedsy[n]+offset));
int x1 = max(0, (int)(kseedsx[n]-offset));
int x2 = min(m_width, (int)(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];
}}
}
}
//===========================================================================
/// SaveSuperpixelLabels2PGM
///
/// Save labels to PGM in raster scan order.
//===========================================================================
void SLIC::SaveSuperpixelLabels2PPM(
char* filename,
int * labels,
const int width,
const int height)
{
FILE* fp;
char header[20];
fp = fopen(filename, "wb");
// write the PPM header info, such as type, width, height and maximum
fprintf(fp,"P6\n%d %d\n255\n", width, height);
// write the RGB data
unsigned char *rgb = new unsigned char [ (width)*(height)*3 ];
int k = 0;
unsigned char c = 0;
for ( int i = 0; i < (height); i++ ) {
for ( int j = 0; j < (width); j++ ) {
c = (unsigned char)(labels[k]);
rgb[i*(width)*3 + j*3 + 2] = labels[k] >> 16 & 0xff; // r
rgb[i*(width)*3 + j*3 + 1] = labels[k] >> 8 & 0xff; // g
rgb[i*(width)*3 + j*3 + 0] = labels[k] & 0xff; // b
// rgb[i*(width) + j + 0] = c;
k++;
}
}
fwrite(rgb, width*height*3, 1, fp);
delete [] rgb;
fclose(fp);
}
//===========================================================================
/// 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 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) //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 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;
//-------------------------------------------------------
// 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_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 superpixels
const 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); //RGB->LAB
}
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);//移动聚类中心到 3*3 领域中梯度最小的位置
int STEP = sqrt(double(sz)/double(K)) + 2.0;//adding a small value in the even the STEP size is too small.
PerformSuperpixelSegmentation_VariableSandM(kseedsl,kseedsa,kseedsb,kseedsx,kseedsy,klabels,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;
}
//===========================================================================
/// Load PPM file
///
///
//===========================================================================
void LoadPPM(char* filename, unsigned int** data, int* width, int* height)
{
char header[1024];
FILE* fp = NULL;
int line = 0;
fp = fopen(filename, "rb");
// read the image type, such as: P6
// skip the comment lines
while (line < 2) {
fgets(header, 1024, fp);
if (header[0] != '#') {
++line;
}
}
// read width and height
sscanf(header,"%d %d\n", width, height);
// read the maximum of pixels
fgets(header, 20, fp);
// get rgb data
unsigned char *rgb = new unsigned char [ (*width)*(*height)*3 ];
fread(rgb, (*width)*(*height)*3, 1, fp);
*data = new unsigned int [ (*width)*(*height)*4 ];
int k = 0;
for ( int i = 0; i < (*height); i++ ) {
for ( int j = 0; j < (*width); j++ ) {
unsigned char *p = rgb + i*(*width)*3 + j*3;
// a ( skipped )
(*data)[k] = p[2] << 16; // r
(*data)[k] |= p[1] << 8; // g
(*data)[k] |= p[0]; // b
k++;
}
}
// ofc, later, you'll have to cleanup
delete [] rgb;
fclose(fp);
}
//===========================================================================
/// Load PPM file
///
///
//===========================================================================
int CheckLabelswithPPM(char* filename, int* labels, int width, int height)
{
char header[1024];
FILE* fp = NULL;
int line = 0, ground = 0;
fp = fopen(filename, "rb");
// read the image type, such as: P6
// skip the comment lines
while (line < 2) {
fgets(header, 1024, fp);
if (header[0] != '#') {
++line;
}
}
// read width and height
int w(0);
int h(0);
sscanf(header,"%d %d\n", &w, &h);
if (w != width || h != height) return -1;
// read the maximum of pixels
fgets(header, 20, fp);
// get rgb data
unsigned char *rgb = new unsigned char [ (w)*(h)*3 ];
fread(rgb, (w)*(h)*3, 1, fp);
int num = 0, k = 0;
for ( int i = 0; i < (h); i++ ) {
for ( int j = 0; j < (w); j++ ) {
unsigned char *p = rgb + i*(w)*3 + j*3;
// a ( skipped )
ground = p[2] << 16; // r
ground |= p[1] << 8; // g
ground |= p[0]; // b
if (ground != labels[k])
num++;
k++;
}
}
// ofc, later, you'll have to cleanup
delete [] rgb;
fclose(fp);
return num;
}
//===========================================================================
/// The main function
///
//===========================================================================
int main (int argc, char **argv)
{
unsigned int* img = NULL;
int width(0);
int height(0);
LoadPPM((char *)"../input_image.ppm", &img, &width, &height);
if (width == 0 || height == 0) return -1;
// cout << "sss" << endl;
int sz = width*height;
int* labels = new int[sz];
int numlabels(0);
SLIC slic;
int m_spcount;
double m_compactness;
m_spcount = 200;
m_compactness = 10.0;
auto startTime = Clock::now();
slic.PerformSLICO_ForGivenK(img, width, height, labels, numlabels, m_spcount, m_compactness);//for a given number K of superpixels
auto endTime = Clock::now();
auto compTime = chrono::duration_cast<chrono::microseconds>(endTime - startTime);
cout << "Computing time=" << compTime.count()/1000 << " ms" << endl;
int num = CheckLabelswithPPM((char *)"../check.ppm", labels, width, height);
if (num < 0) {
cout << "The result for labels is different from output_labels.ppm." << endl;
} else {
cout << "There are " << num << " points' labels are different from original file." << endl;
}
slic.SaveSuperpixelLabels2PPM((char *)"../output_labels.ppm", labels, width, height);
if(labels) delete [] labels;
if(img) delete [] img;
return 0;
}
SLIC.h
// SLIC.h: interface for 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: firstname.lastname@epfl.ch
//===========================================================================
#if !defined(_SLIC_H_INCLUDED_)
#define _SLIC_H_INCLUDED_
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
class SLIC
{
public:
SLIC();
virtual ~SLIC();
//============================================================================
// Superpixel segmentation for a given number of superpixels
//============================================================================
void PerformSLICO_ForGivenK(
const unsigned int* ubuff,//Each 32 bit unsigned int contains ARGB pixel values.
const int width,
const int height,
int* klabels,
int& numlabels,
const int& K,
const double& m);
//============================================================================
// Save superpixel labels to pgm in raster scan order
//============================================================================
void SaveSuperpixelLabels2PPM(
char* filename,
int * labels,
const int width,
const int height);
private:
//============================================================================
// Magic SLIC. No need to set M (compactness factor) and S (step size).
// SLICO (SLIC Zero) varies only M dynamicaly, not S.
//============================================================================
void 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);
//============================================================================
// Pick seeds for superpixels when number of superpixels is input.
//============================================================================
void GetLABXYSeeds_ForGivenK(
vector<double>& kseedsl,
vector<double>& kseedsa,
vector<double>& kseedsb,
vector<double>& kseedsx,
vector<double>& kseedsy,
const int& STEP,
const bool& perturbseeds,
const vector<double>& edges);
//============================================================================
// Move the seeds to low gradient positions to avoid putting seeds at region boundaries.
//============================================================================
void PerturbSeeds(
vector<double>& kseedsl,
vector<double>& kseedsa,
vector<double>& kseedsb,
vector<double>& kseedsx,
vector<double>& kseedsy,
const vector<double>& edges);
//============================================================================
// Detect color edges, to help PerturbSeeds()
//============================================================================
void DetectLabEdges(
const double* lvec,
const double* avec,
const double* bvec,
const int& width,
const int& height,
vector<double>& edges);
//============================================================================
// xRGB to XYZ conversion; helper for RGB2LAB()
//============================================================================
void RGB2XYZ(
const int& sR,
const int& sG,
const int& sB,
double& X,
double& Y,
double& Z);
//============================================================================
// sRGB to CIELAB conversion
//============================================================================
void RGB2LAB(
const int& sR,
const int& sG,
const int& sB,
double& lval,
double& aval,
double& bval);
//============================================================================
// sRGB to CIELAB conversion for 2-D images
//============================================================================
void DoRGBtoLABConversion(
const unsigned int*& ubuff,
double*& lvec,
double*& avec,
double*& bvec);
//============================================================================
// Post-processing of SLIC segmentation, to avoid stray labels.
//============================================================================
void EnforceLabelConnectivity(
const int* labels,
const int& width,
const int& height,
int* nlabels,//input labels that need to be corrected to remove stray labels
int& numlabels,//the number of labels changes in the end if segments are removed
const int& K); //the number of superpixels desired by the user
private:
int m_width;
int m_height;
int m_depth;
double* m_lvec;
double* m_avec;
double* m_bvec;
double** m_lvecvec;
double** m_avecvec;
double** m_bvecvec;
};
#endif // !defined(_SLIC_H_INCLUDED_)
运行结果:
|