天天看點

SLIC 超像素分割(C++)摘要:1.原理2. 實作3. 實測效果4. 完整源碼

摘要:

SLIC:simple linear iterative clustering,簡單的線性疊代聚類,它使用k-means聚類來生成超像素,實作起來簡單,運作效率還比較高,超像素對邊界的保持效果也比較好,具體的與其他方法的對比結果在下方論文中有詳細記錄。

文章目錄

  • 摘要:
  • 1.原理
  • 2. 實作
    • 2.1 初始化聚類中心
    • 2.2 聚類
    • 2.3 更新聚類中心
    • 2.4 顯示超像素分割結果
  • 3. 實測效果
  • 4. 完整源碼

論文位址:https://infoscience.epfl.ch/record/177415/files/Superpixel_PAMI2011-2.pdf

1.原理

SLIC本質上說隻需要一個聚類數k,這個聚類數k也就是超像素的個數,希望把圖檔分割成多少個超像素就設多少個聚類中心,在初始化時把聚類中心等間隔的放到圖像上,設圖像像素數為N,則在每個邊長為 N / k \sqrt{N/k} N/k

​的方格内放置一個初始聚類中心, 為了避免把噪聲像素和邊緣當作聚類中心,會把該中心移動到周圍3*3鄰域内梯度最小的地方。

在用k-means做聚類時,采用的特征是Lab顔色空間的三個值加上坐标x,y,一共5個次元, x i . f e a t u r e = [ L , a , b , x , y ] T x_{i.feature} = [L,a,b,x,y]^T xi.feature​=[L,a,b,x,y]T,另外與正常的k均值不同的地方是,這裡計算距離時隻考慮聚類中心周圍 2 N / k ∗ 2 N / k 2\sqrt{N/k} * 2\sqrt{N/k} 2N/k

​∗2N/k

​的鄰域,是以計算量和一般的k均值聚類相比要小很多。搜尋空間對比如下圖, S = N / k S = \sqrt{N/k} S=N/k

SLIC 超像素分割(C++)摘要:1.原理2. 實作3. 實測效果4. 完整源碼

聚類後計算每一類的平均特征,并把聚類中心的特征更新為平均特征。如果疊代超過10次或者前後兩次聚類的差小于門檻值則結束,否則繼續聚類,更新聚類中心…。算法流程如下:

SLIC 超像素分割(C++)摘要:1.原理2. 實作3. 實測效果4. 完整源碼

2. 實作

2.1 初始化聚類中心

原文中按聚類數k來等間隔地初始化聚類中心,假設樣本總數為N,聚類數為k,則每隔 N / k N/k N/k個樣本放置一個聚類中心。在圖檔上等間隔地放置k個初始聚類中心,也就是把圖檔等分成邊長為 N / k \sqrt{N/k} N/k

​的格子,在格子的固定位置放置初始聚類中心。另外為了避免初始的聚類中心落在物體邊緣上,還要對每一個聚類中心都進行一下微調,具體就是計算初始聚類中心點周圍鄰域的梯度,把中心點移到梯度最小的點上。

這裡實作的時候以初始超像素的邊長len作為參數會比較直覺,可以很簡單的控制生成的超像素的大小,k和len的關系是 l e n = N / k len=\sqrt{N/k} len=N/k

​。

注:圖檔坐标系以左上角為原點,水準向右為x軸正方向,水準向下為y軸正方向(與opencv一緻),在通路圖檔資料矩陣時,一般用行和列的方式來描述,也就是row對應y,colum對應x。

  • 等間隔放置聚類中心,
int initilizeCenters(cv::Mat &imageLAB, std::vector<center> &centers, int len)
{
	if (imageLAB.empty())
	{
		std::cout << "In itilizeCenters:     image is empty!\n";
		return -1;
	}

	uchar *ptr = NULL;
	center cent;
	int num = 0;
	for (int i = 0; i < imageLAB.rows; i += len)
	{
		cent.y = i + len / 2;
		if (cent.y >= imageLAB.rows) continue;
		ptr = imageLAB.ptr<uchar>(cent.y);
		for (int j = 0; j < imageLAB.cols; j += len)
		{
			cent.x = j + len / 2;
			if ((cent.x >= imageLAB.cols)) continue;
			cent.L = *(ptr + cent.x * 3);
			cent.A = *(ptr + cent.x * 3 + 1);
			cent.B = *(ptr + cent.x * 3 + 2);
			cent.label = ++num;
			centers.push_back(cent);
		}
	}
	return 0;
}
           
  • 把聚類中心移到到周圍8鄰域裡梯度最小的地方,梯度用Sobel計算(不希望聚類中心落在邊緣上是以才調整的)
int fituneCenter(cv::Mat &imageLAB, cv::Mat &sobelGradient, std::vector<center> &centers)
{
	if (sobelGradient.empty()) return -1;

	center cent;
	double *sobPtr = sobelGradient.ptr<double>(0);
	uchar *imgPtr = imageLAB.ptr<uchar>(0);
	int w = sobelGradient.cols;
	for (int ck = 0; ck < centers.size(); ck++)
	{
		cent = centers[ck];
		if (cent.x - 1 < 0 || cent.x + 1 >= sobelGradient.cols || cent.y - 1 < 0 || cent.y + 1 >= sobelGradient.rows)
		{
			continue;
		}//end if
		double minGradient = 9999999;
		int tempx = 0, tempy = 0;
		for (int m = -1; m < 2; m++)
		{
			sobPtr = sobelGradient.ptr<double>(cent.y + m);
			for (int n = -1; n < 2; n++)
			{
				double gradient = pow(*(sobPtr + (cent.x + n) * 3), 2)
					+ pow(*(sobPtr + (cent.x + n) * 3 + 1), 2)
					+ pow(*(sobPtr + (cent.x + n) * 3 + 2), 2);
				if (gradient < minGradient)
				{
					minGradient = gradient;
					tempy = m;//row
					tempx = n;//column
				}//end if
			}
		}
		cent.x += tempx;
		cent.y += tempy;
		imgPtr = imageLAB.ptr<uchar>(cent.y);
		centers[ck].x = cent.x;
		centers[ck].y = cent.y;
		centers[ck].L = *(imgPtr + cent.x * 3);
		centers[ck].A = *(imgPtr + cent.x * 3 + 1);
		centers[ck].B = *(imgPtr + cent.x * 3 + 2);

	}//end for
	return 0;
}
           

2.2 聚類

對每一個聚類中心center_k,計算它到周圍2len*2len區域的所有點的距離,如果新計算的距離比原來的距離更小,則把該點歸入center_k這一類。

注意聚類的本質就是“物以類聚”,判斷樣本點和聚類中心的“近似”程度,可以從兩個方面來考察,一種是距離測度:距離越近越相似,另一種是相似性測度,例如角度相似系數,相關系數,指數相似系數等。

距離的計算方式有很多種,比如:

  • 歐拉距離 d ( x ⃗ , y ⃗ ) = ∣ ∣ x ⃗ − y ⃗ ∣ ∣ 2 2 d(\vec{x},\vec{y}) = ||\vec{x}-\vec{y}||_2^2 d(x

    ,y

    ​)=∣∣x

    −y

    ​∣∣22​

  • 城區距離 d ( x ⃗ , y ⃗ ) = ∣ x ⃗ − y ⃗ ∣ d(\vec{x},\vec{y}) = |\vec{x}-\vec{y}| d(x

    ,y

    ​)=∣x

    −y

    ​∣

  • 切比雪夫距離 d ( x ⃗ , y ⃗ ) = m a x ∣ x i − y i ∣ , i 表 示 維 度 d(\vec{x},\vec{y}) = max|x_i-y_i|,i表示次元 d(x

    ,y

    ​)=max∣xi​−yi​∣,i表示次元

  • 馬氏距離(Mahalanobis): d ( x ⃗ , y ⃗ ) = ( x ⃗ − y ⃗ ) T V − 1 ( x ⃗ − y ⃗ ) , V 表 示 樣 本 總 體 的 協 方 差 矩 陣 , V = 1 n − 1 Σ i = 1 n ( x i − x ⃗ ˉ ⃗ ) ( x i − x ⃗ ˉ ⃗ ) T , n 為 樣 本 數 , x i ⃗ 為 第 i 個 樣 本 ( 列 向 量 ) , x ⃗ ˉ 為 樣 本 均 值 d(\vec{x},\vec{y}) = (\vec{x}-\vec{y})^TV^{-1}(\vec{x}-\vec{y}),V表示樣本總體的協方差矩陣,V= \frac{1}{n-1}\Sigma_{i=1}^{n}(\vec{x_i - \bar{\vec{x}}})(\vec{x_i - \bar{\vec{x}}})^T,n為樣本數, \vec{x_i}為第i個樣本(列向量), \bar{\vec{x}}為樣本均值 d(x

    ,y

    ​)=(x

    −y

    ​)TV−1(x

    −y

    ​),V表示樣本總體的協方差矩陣,V=n−11​Σi=1n​(xi​−x

    ˉ

    ​)(xi​−x

    ˉ

    ​)T,n為樣本數,xi​

    ​為第i個樣本(列向量),x

    ˉ為樣本均值,它有一點比較好的性質就是與量綱無關(另外它還對坐标尺度、旋轉、平移保持不變,從統計意義上去除了分量間的相關性),在這裡分割超像素時,Lab顔色空間的距離往往會比空間距離大很多,用歐式距離時需要加一個權重參數來調節顔色距離和空間距離的比例。

要是以後有時間的話可以考慮一下都試一下這些距離聚類的效果。這裡采用的是歐式距離,而且因為Lab顔色空間和圖像xy坐标空間量綱不同,需要調整顔色空間距離和xy坐标距離的權重,論文中用下面的方式來計算距離

d c = ( l j − l i ) 2 + ( a j − a i ) 2 + ( b j − b i ) 2 d s = ( x j − x i ) 2 + ( y j − y i ) 2 D = d c 2 + ( d s S ) 2 m 2 \begin{aligned} d_{c}&=\sqrt{\left(l_{j}-l_{i}\right)^{2}+\left(a_{j}-a_{i}\right)^{2}+\left(b_{j}-b_{i}\right)^{2}} \\ d_{s} &=\sqrt{\left(x_{j}-x_{i}\right)^{2}+\left(y_{j}-y_{i}\right)^{2}}\\D&=\sqrt{d_{c}^{2}+\left(\frac{d_{s}}{S}\right)^{2} m^{2}} \end{aligned} dc​ds​D​=(lj​−li​)2+(aj​−ai​)2+(bj​−bi​)2

​=(xj​−xi​)2+(yj​−yi​)2

​=dc2​+(Sds​​)2m2

​​

但是實際上在做超像素分割時我們更關心超像素的大小,而不是有多少個,雖然尺寸S和聚類數k有明确的對應關系,但是把k當輸入參數不如直接用尺寸S來得直接,另外 d s 的 權 重 用 m 2 S 2 ds的權重用\frac{m^2}{S^2} ds的權重用S2m2​實際用起來有點麻煩,因為單獨修改m或者s都會被另外一個參數調制,是以D的計算我改成了下面這樣 D = d c 2 + m d s 2 \begin{aligned}D=\sqrt{d_{c}^{2}+md_{s}^{2} }\end{aligned} D=dc2​+mds2​

​​

int clustering(const cv::Mat &imageLAB, cv::Mat &DisMask, cv::Mat &labelMask,
	std::vector<center> &centers, int len, int m)
{
	if (imageLAB.empty())
	{
		std::cout << "clustering :the input image is empty!\n";
		return -1;
	}

	double *disPtr = NULL;//disMask type: 64FC1
	double *labelPtr = NULL;//labelMask type: 64FC1
	const uchar *imgPtr = NULL;//imageLAB type: 8UC3

	//disc = std::sqrt(pow(L - cL, 2)+pow(A - cA, 2)+pow(B - cB,2))
	//diss = std::sqrt(pow(x-cx,2) + pow(y-cy,2));
	//dis = sqrt(disc^2 + (diss/len)^2 * m^2)
	double dis = 0, disc = 0, diss = 0;
	//cluster center's cx, cy,cL,cA,cB;
	int cx, cy, cL, cA, cB, clabel;
	//imageLAB's  x, y, L,A,B
	int x, y, L, A, B;

	//注:這裡的圖像坐标以左上角為原點,水準向右為x正方向,水準向下為y正方向,與opencv保持一緻
	//      從矩陣行列角度看,i表示行,j表示列,即(i,j) = (y,x)
	for (int ck = 0; ck < centers.size(); ++ck)
	{
		cx = centers[ck].x;
		cy = centers[ck].y;
		cL = centers[ck].L;
		cA = centers[ck].A;
		cB = centers[ck].B;
		clabel = centers[ck].label;

		for (int i = cy - len; i < cy + len; i++)
		{
			if (i < 0 | i >= imageLAB.rows) continue;
			//pointer point to the ith row
			imgPtr = imageLAB.ptr<uchar>(i);
			disPtr = DisMask.ptr<double>(i);
			labelPtr = labelMask.ptr<double>(i);
			for (int j = cx - len; j < cx + len; j++)
			{
				if (j < 0 | j >= imageLAB.cols) continue;
				L = *(imgPtr + j * 3);
				A = *(imgPtr + j * 3 + 1);
				B = *(imgPtr + j * 3 + 2);

				disc = std::sqrt(pow(L - cL, 2) + pow(A - cA, 2) + pow(B - cB, 2));
				diss = std::sqrt(pow(j - cx, 2) + pow(i - cy, 2));
				dis = sqrt(pow(disc, 2) + m * pow(diss, 2));

				if (dis < *(disPtr + j))
				{
					*(disPtr + j) = dis;
					*(labelPtr + j) = clabel;
				}//end if
			}//end for
		}
	}//end for (int ck = 0; ck < centers.size(); ++ck)


	return 0;
}
           

2.3 更新聚類中心

對每一個聚類中心center_k,把所有屬于這一類的點的特征加起來求平均,把這個平均特征賦給center_k。

int updateCenter(cv::Mat &imageLAB, cv::Mat &labelMask, std::vector<center> &centers, int len)
{
	double *labelPtr = NULL;//labelMask type: 64FC1
	const uchar *imgPtr = NULL;//imageLAB type: 8UC3
	int cx, cy;

	for (int ck = 0; ck < centers.size(); ++ck)
	{
		double sumx = 0, sumy = 0, sumL = 0, sumA = 0, sumB = 0, sumNum = 0;
		cx = centers[ck].x;
		cy = centers[ck].y;
		for (int i = cy - len; i < cy + len; i++)
		{
			if (i < 0 | i >= imageLAB.rows) continue;
			//pointer point to the ith row
			imgPtr = imageLAB.ptr<uchar>(i);
			labelPtr = labelMask.ptr<double>(i);
			for (int j = cx - len; j < cx + len; j++)
			{
				if (j < 0 | j >= imageLAB.cols) continue;

				if (*(labelPtr + j) == centers[ck].label)
				{
					sumL += *(imgPtr + j * 3);
					sumA += *(imgPtr + j * 3 + 1);
					sumB += *(imgPtr + j * 3 + 2);
					sumx += j;
					sumy += i;
					sumNum += 1;
				}//end if
			}
		}
		//update center
		if (sumNum == 0) sumNum = 0.000000001;
		centers[ck].x = sumx / sumNum;
		centers[ck].y = sumy / sumNum;
		centers[ck].L = sumL / sumNum;
		centers[ck].A = sumA / sumNum;
		centers[ck].B = sumB / sumNum;

	}//end for

	return 0;
}
           

2.4 顯示超像素分割結果

方式一:把屬于同一類的點的特征都替換成平均特征;

方式二:畫出聚類邊界;

3. 實測效果

  • 左側為原圖,中間為在原圖上畫出超像素邊界效果圖,右側為超像素圖像效果
    SLIC 超像素分割(C++)摘要:1.原理2. 實作3. 實測效果4. 完整源碼
    SLIC 超像素分割(C++)摘要:1.原理2. 實作3. 實測效果4. 完整源碼
SLIC 超像素分割(C++)摘要:1.原理2. 實作3. 實測效果4. 完整源碼
SLIC 超像素分割(C++)摘要:1.原理2. 實作3. 實測效果4. 完整源碼
SLIC 超像素分割(C++)摘要:1.原理2. 實作3. 實測效果4. 完整源碼
SLIC 超像素分割(C++)摘要:1.原理2. 實作3. 實測效果4. 完整源碼
SLIC 超像素分割(C++)摘要:1.原理2. 實作3. 實測效果4. 完整源碼
SLIC 超像素分割(C++)摘要:1.原理2. 實作3. 實測效果4. 完整源碼

4. 完整源碼

//
//created by Mr. Peng. 2021\08\31
//

#include "opencv.hpp"

struct center
{
	int x;//column
	int y;//row
	int L;
	int A;
	int B;
	int label;
};


/
//input parameters:
//imageLAB:    the source image in Lab color space
//DisMask:       it save the shortest distance to the nearest center
//labelMask:   it save every pixel's label  
//centers:       clustering center
//len:         the super pixls will be initialize to len*len
//m:           a parameter witch adjust the weights of the spacial and color space distance 
//
//output:

int clustering(const cv::Mat &imageLAB, cv::Mat &DisMask, cv::Mat &labelMask,
	std::vector<center> &centers, int len, int m)
{
	if (imageLAB.empty())
	{
		std::cout << "clustering :the input image is empty!\n";
		return -1;
	}

	double *disPtr = NULL;//disMask type: 64FC1
	double *labelPtr = NULL;//labelMask type: 64FC1
	const uchar *imgPtr = NULL;//imageLAB type: 8UC3

	//disc = std::sqrt(pow(L - cL, 2)+pow(A - cA, 2)+pow(B - cB,2))
	//diss = std::sqrt(pow(x-cx,2) + pow(y-cy,2));
	//dis = sqrt(disc^2 + (diss/len)^2 * m^2)
	double dis = 0, disc = 0, diss = 0;
	//cluster center's cx, cy,cL,cA,cB;
	int cx, cy, cL, cA, cB, clabel;
	//imageLAB's  x, y, L,A,B
	int x, y, L, A, B;

	//注:這裡的圖像坐标以左上角為原點,水準向右為x正方向,水準向下為y正方向,與opencv保持一緻
	//      從矩陣行列角度看,i表示行,j表示列,即(i,j) = (y,x)
	for (int ck = 0; ck < centers.size(); ++ck)
	{
		cx = centers[ck].x;
		cy = centers[ck].y;
		cL = centers[ck].L;
		cA = centers[ck].A;
		cB = centers[ck].B;
		clabel = centers[ck].label;

		for (int i = cy - len; i < cy + len; i++)
		{
			if (i < 0 | i >= imageLAB.rows) continue;
			//pointer point to the ith row
			imgPtr = imageLAB.ptr<uchar>(i);
			disPtr = DisMask.ptr<double>(i);
			labelPtr = labelMask.ptr<double>(i);
			for (int j = cx - len; j < cx + len; j++)
			{
				if (j < 0 | j >= imageLAB.cols) continue;
				L = *(imgPtr + j * 3);
				A = *(imgPtr + j * 3 + 1);
				B = *(imgPtr + j * 3 + 2);

				disc = std::sqrt(pow(L - cL, 2) + pow(A - cA, 2) + pow(B - cB, 2));
				diss = std::sqrt(pow(j - cx, 2) + pow(i - cy, 2));
				dis = sqrt(pow(disc, 2) + m * pow(diss, 2));

				if (dis < *(disPtr + j))
				{
					*(disPtr + j) = dis;
					*(labelPtr + j) = clabel;
				}//end if
			}//end for
		}
	}//end for (int ck = 0; ck < centers.size(); ++ck)


	return 0;
}


/
//input parameters:
//imageLAB:    the source image in Lab color space
//labelMask:    it save every pixel's label
//centers:       clustering center
//len:         the super pixls will be initialize to len*len
//
//output:

int updateCenter(cv::Mat &imageLAB, cv::Mat &labelMask, std::vector<center> &centers, int len)
{
	double *labelPtr = NULL;//labelMask type: 64FC1
	const uchar *imgPtr = NULL;//imageLAB type: 8UC3
	int cx, cy;

	for (int ck = 0; ck < centers.size(); ++ck)
	{
		double sumx = 0, sumy = 0, sumL = 0, sumA = 0, sumB = 0, sumNum = 0;
		cx = centers[ck].x;
		cy = centers[ck].y;
		for (int i = cy - len; i < cy + len; i++)
		{
			if (i < 0 | i >= imageLAB.rows) continue;
			//pointer point to the ith row
			imgPtr = imageLAB.ptr<uchar>(i);
			labelPtr = labelMask.ptr<double>(i);
			for (int j = cx - len; j < cx + len; j++)
			{
				if (j < 0 | j >= imageLAB.cols) continue;

				if (*(labelPtr + j) == centers[ck].label)
				{
					sumL += *(imgPtr + j * 3);
					sumA += *(imgPtr + j * 3 + 1);
					sumB += *(imgPtr + j * 3 + 2);
					sumx += j;
					sumy += i;
					sumNum += 1;
				}//end if
			}
		}
		//update center
		if (sumNum == 0) sumNum = 0.000000001;
		centers[ck].x = sumx / sumNum;
		centers[ck].y = sumy / sumNum;
		centers[ck].L = sumL / sumNum;
		centers[ck].A = sumA / sumNum;
		centers[ck].B = sumB / sumNum;

	}//end for

	return 0;
}


int showSLICResult(const cv::Mat &image, cv::Mat &labelMask, std::vector<center> &centers, int len)
{
	cv::Mat dst = image.clone();
	cv::cvtColor(dst, dst, cv::COLOR_BGR2Lab);
	double *labelPtr = NULL;//labelMask type: 32FC1
	uchar *imgPtr = NULL;//image type: 8UC3

	int cx, cy;
	double sumx = 0, sumy = 0, sumL = 0, sumA = 0, sumB = 0, sumNum = 0.00000001;
	for (int ck = 0; ck < centers.size(); ++ck)
	{
		cx = centers[ck].x;
		cy = centers[ck].y;

		for (int i = cy - len; i < cy + len; i++)
		{
			if (i < 0 | i >= image.rows) continue;
			//pointer point to the ith row
			imgPtr = dst.ptr<uchar>(i);
			labelPtr = labelMask.ptr<double>(i);
			for (int j = cx - len; j < cx + len; j++)
			{
				if (j < 0 | j >= image.cols) continue;

				if (*(labelPtr + j) == centers[ck].label)
				{
					*(imgPtr + j * 3) = centers[ck].L;
					*(imgPtr + j * 3 + 1) = centers[ck].A;
					*(imgPtr + j * 3 + 2) = centers[ck].B;
				}//end if
			}
		}
	}//end for

	cv::cvtColor(dst, dst, cv::COLOR_Lab2BGR);
	cv::namedWindow("showSLIC", 0);
	cv::imshow("showSLIC", dst);
	cv::waitKey(1);

	return 0;
}


int showSLICResult2(const cv::Mat &image, cv::Mat &labelMask, std::vector<center> &centers, int len)
{
	cv::Mat dst = image.clone();
	//cv::cvtColor(dst, dst, cv::COLOR_Lab2BGR);
	double *labelPtr = NULL;//labelMask type: 32FC1
	double *labelPtr_nextRow = NULL;//labelMask type: 32FC1
	uchar *imgPtr = NULL;//image type: 8UC3

	for (int i = 0; i < labelMask.rows - 1; i++)
	{
		labelPtr = labelMask.ptr<double>(i);
		imgPtr = dst.ptr<uchar>(i);
		for (int j = 0; j < labelMask.cols - 1; j++)
		{
			//if left pixel's label is different from the right's 
			if (*(labelPtr + j) != *(labelPtr + j + 1))
			{
				*(imgPtr + 3 * j) = 0;
				*(imgPtr + 3 * j + 1) = 0;
				*(imgPtr + 3 * j + 2) = 0;
			}

			//if the upper pixel's label is different from the bottom's 
			labelPtr_nextRow = labelMask.ptr<double>(i + 1);
			if (*(labelPtr_nextRow + j) != *(labelPtr + j))
			{
				*(imgPtr + 3 * j) = 0;
				*(imgPtr + 3 * j + 1) = 0;
				*(imgPtr + 3 * j + 2) = 0;
			}
		}
	}

	//show center
	for (int ck = 0; ck < centers.size(); ck++)
	{
		imgPtr = dst.ptr<uchar>(centers[ck].y);
		*(imgPtr + centers[ck].x * 3) = 100;
		*(imgPtr + centers[ck].x * 3 + 1) = 100;
		*(imgPtr + centers[ck].x * 3 + 1) = 10;
	}

	cv::namedWindow("showSLIC2", 0);
	cv::imshow("showSLIC2", dst);
	cv::waitKey(1);
	return 0;
}


int initilizeCenters(cv::Mat &imageLAB, std::vector<center> &centers, int len)
{
	if (imageLAB.empty())
	{
		std::cout << "In itilizeCenters:     image is empty!\n";
		return -1;
	}

	uchar *ptr = NULL;
	center cent;
	int num = 0;
	for (int i = 0; i < imageLAB.rows; i += len)
	{
		cent.y = i + len / 2;
		if (cent.y >= imageLAB.rows) continue;
		ptr = imageLAB.ptr<uchar>(cent.y);
		for (int j = 0; j < imageLAB.cols; j += len)
		{
			cent.x = j + len / 2;
			if ((cent.x >= imageLAB.cols)) continue;
			cent.L = *(ptr + cent.x * 3);
			cent.A = *(ptr + cent.x * 3 + 1);
			cent.B = *(ptr + cent.x * 3 + 2);
			cent.label = ++num;
			centers.push_back(cent);
		}
	}
	return 0;
}


//if the center locates in the edges, fitune it's location.
int fituneCenter(cv::Mat &imageLAB, cv::Mat &sobelGradient, std::vector<center> &centers)
{
	if (sobelGradient.empty()) return -1;

	center cent;
	double *sobPtr = sobelGradient.ptr<double>(0);
	uchar *imgPtr = imageLAB.ptr<uchar>(0);
	int w = sobelGradient.cols;
	for (int ck = 0; ck < centers.size(); ck++)
	{
		cent = centers[ck];
		if (cent.x - 1 < 0 || cent.x + 1 >= sobelGradient.cols || cent.y - 1 < 0 || cent.y + 1 >= sobelGradient.rows)
		{
			continue;
		}//end if
		double minGradient = 9999999;
		int tempx = 0, tempy = 0;
		for (int m = -1; m < 2; m++)
		{
			sobPtr = sobelGradient.ptr<double>(cent.y + m);
			for (int n = -1; n < 2; n++)
			{
				double gradient = pow(*(sobPtr + (cent.x + n) * 3), 2)
					+ pow(*(sobPtr + (cent.x + n) * 3 + 1), 2)
					+ pow(*(sobPtr + (cent.x + n) * 3 + 2), 2);
				if (gradient < minGradient)
				{
					minGradient = gradient;
					tempy = m;//row
					tempx = n;//column
				}//end if
			}
		}
		cent.x += tempx;
		cent.y += tempy;
		imgPtr = imageLAB.ptr<uchar>(cent.y);
		centers[ck].x = cent.x;
		centers[ck].y = cent.y;
		centers[ck].L = *(imgPtr + cent.x * 3);
		centers[ck].A = *(imgPtr + cent.x * 3 + 1);
		centers[ck].B = *(imgPtr + cent.x * 3 + 2);

	}//end for
	return 0;
}


/
//input parameters:
//image:    the source image in RGB color space
//resultLabel:     it save every pixel's label
//len:         the super pixls will be initialize to len*len
//m:           a parameter witch adjust the weights of diss 
//output:

int SLIC(cv::Mat &image, cv::Mat &resultLabel, std::vector<center> &centers, int len, int m)
{
	if (image.empty())
	{
		std::cout << "in SLIC the input image is empty!\n";
		return -1;

	}

	int MAXDIS = 999999;
	int height, width;
	height = image.rows;
	width = image.cols;

	//convert color
	cv::Mat imageLAB;
	cv::cvtColor(image, imageLAB, cv::COLOR_BGR2Lab);

	//get sobel gradient map
	cv::Mat sobelImagex, sobelImagey, sobelGradient;
	cv::Sobel(imageLAB, sobelImagex, CV_64F, 0, 1, 3);
	cv::Sobel(imageLAB, sobelImagey, CV_64F, 1, 0, 3);
	cv::addWeighted(sobelImagex, 0.5, sobelImagey, 0.5, 0, sobelGradient);//sobel output image type is CV_64F

	//initiate
	//std::vector<center> centers;
	//disMask save the distance of the pixels to center;
	cv::Mat disMask ;
	//labelMask save the label of the pixels
	cv::Mat labelMask = cv::Mat::zeros(cv::Size(width, height), CV_64FC1);

	//initialize centers,  get centers
	initilizeCenters(imageLAB, centers, len);
	//if the center locates in the edges, fitune it's location
	fituneCenter(imageLAB, sobelGradient, centers);

	//update cluster 10 times 
	for (int time = 0; time < 10; time++)
	{
		//clustering
		disMask = cv::Mat(height, width, CV_64FC1, cv::Scalar(MAXDIS));
		clustering(imageLAB, disMask, labelMask, centers, len, m);
		//update
		updateCenter(imageLAB, labelMask, centers, len);
		//fituneCenter(imageLAB, sobelGradient, centers);
	}

	resultLabel = labelMask;

	return 0;
}


int SLIC_Demo()
{
	
	std::string imagePath = "K:\\deepImage\\plane.jpg";
	cv::Mat image = cv::imread(imagePath);
	cv::Mat labelMask;//save every pixel's label
	cv::Mat dst;//save the shortest distance to the nearest centers
	std::vector<center> centers;//clustering centers

	int len = 25;//the scale of the superpixel ,len*len
	int m = 10;//a parameter witch adjust the weights of spacial distance and the color space distance
	SLIC(image, labelMask, centers, len, m);

	cv::namedWindow("image", 1);
	cv::imshow("image", image);
	showSLICResult(image, labelMask, centers, len);
	showSLICResult2(image, labelMask, centers, len);
	
	cv::waitKey(0);
	return 0;
}


int main()
{
	SLIC_Demo();
	return 0;
}
           

繼續閱讀