SLIC代码实现

该代码实现了一个基于SLIC算法的超像素分割过程,包括图像预处理、色彩空间转换、像素初始化、超像素更新和绘制结果。主要函数有init_clusters、update_pixel、updaye_clusters,用于超像素的生成和优化。VSCode中遇到的问题是色彩空间转换标识符错误,通过修改为COLOR_BGR2Lab解决了问题。
摘要由CSDN通过智能技术生成

代码需实现步骤:

1.完成框架:包含头文件、定义变量、配置opencv、配置vscode
2.函数实现:mread读取图片——》resize图片并滤波——》cvtColor完成RGB到LAB空间的转换——》定义计算距离的参数N、K、S——》像素初始化init_clusters——》在定义的超像素框框内循环完成数据初始化update_pixel+更新超像素位置updaye_clusters+标识超像素draw_clusters+绘制超像素的图final_draw

#include <opencv2/opencv.hpp>
#include <iostream>  

using namespace cv;
using namespace std;

#define sqrtK 128		// 超像素个数128*128 (zx:自定义,也可以是32
#define sqrtN 512		// 图像格式512*512 (zx:根据具体图片来

int label[sqrtN][sqrtN];		// 图像各像素点归属
int dis[sqrtN][sqrtN];			// 图像各像素点距离

struct cluster{
	int row, col, l, a, b;
};
cluster clusters[sqrtK*sqrtK];		// 存储超像素的像素坐标


int main(){
	// 注意修改文件位置
	Mat src = imread("E:\\TruthCocapop\\SLIC\\1.jpg"), lab;
	
	// resize图片尺寸并滤波(zx:选择合适的滤波,比如高斯滤波啊之类的
	resize(src, src, Size(sqrtN, sqrtN));
	// GaussianBlur(src, src, Size(3, 3), 1, 1);
	
	// 得到Lab色彩空间,需要注意的是:
	// 1.opencv里面默认为BGR排列方式
	// 2.LAB通道范围取决于转换前的通道范围,这样其实也方便处理
	// 	例如:开始是0-255,转换后也是0-255,而不是LAB规定的[127,-128]
	cvtColor(src, lab, CV_BGR2Lab);   //zx:在SLIC中对于颜色的空间转换(RGB->LAB)会消耗不少时间,这里直接使用opencv现有的转化算法进行颜色空间的转换

	int N = sqrtN * sqrtN;			// N:像素点总数 512*512
	int K = sqrtK * sqrtK;			// K:超像素个数 128*128
	int S = sqrt(N / K);			// S:相邻种子点距离(相邻两超像素间距)4,即超像素边长

	// 1.初始化像素
	init_clusters(lab,S);
	cout << "1-初始化像素-完成\n";

	// 2.微调种子的位置 (没改进太多,可用可不用
	 move_clusters(lab);
	 cout << "2-微调种子的位置-完成\n";

	for (int i = 0; i < 5; i++) { 	//s=4
		// 3.初始化数据
		update_pixel(lab, 2*S);
		cout << "3-初始化数据-完成\n";

		// 4.让超像素位于正中间
		updaye_clusters(lab);
		cout << "4-让超像素位于正中间-完成\n";

		// -------------------这两个函数主要是帮助显示结果的
		// 5.标识超像素
		draw_clusters(src.clone());
		cout << "5-标识超像素-完成\n";

		// 6.绘制超像素结果图
		final_draw(lab, lab.clone());
		cout << "6-绘制超像素结果图-完成\n";

		// opencv的函数,每1000ms更新一下,动态显示图片
		waitKey(1000);
		// -----------------------------------------------
	}
	imshow("原图", src);
	waitKey(0);
}

函数实现:

一、init_clusters

1.像素初始化函数init_clusters,传入的参数为lab彩色空间和S超像素间距
注意:opencv中Mat类的赋值不是拷贝赋值,而是像C++中的引用赋值。(就是把原来的值也给改了,就是这个意思。比如:Mat a,b; b=a; 改变a也会改变b)
· 所以如果想得到一个全新的图像矩阵,可以使用b=a.clone(); 这样改变a,b的值不会被改
· fill函数用于对一段空间赋值,这里即将矩阵dis赋-1。(在非opencv程序也可以使用)

void init_clusters(const Mat lab,int S) {
	// 初始化每一个超像素的坐标
	for (int i = 0; i < sqrtK; i++) { //超像素个数sqrtK=128
		int temp_row = S / 2 + i * S;
		for (int j = 0; j < sqrtK; j++) {
			clusters[i * sqrtK + j].row = temp_row;
			clusters[i * sqrtK + j].col = S / 2 + j * S;
			// cout << clusters[i * sqrtK + j].row << "\t" << clusters[i * sqrtK + j].col 
			// << "\t" << clusters[i * sqrtK + j].h << endl;
		}
	}

	// 初始化每一个像素的label(即属于哪一个超像素)
	for (int i = 0; i < sqrtN; i++) { //图片像素sqrtN=512
		int cluster_row = i / S;
		for (int j = 0; j < sqrtN; j++) {
			label[i][j] = cluster_row * sqrtK + j / S;
			// cout << cluster_row * sqrtK + j / S << endl;
		}
	}

	// 像素与超像素的距离先假设为-1
	fill(dis[0], dis[0] + (sqrtN * sqrtN), -1);
}

二、update_pixel函数

· 首先定义距离计算函数get_distance
传入的参数为lab、超像素索引clusters_index、像素的横纵坐标i和j
·lab.at(row,col)属于opencv里面的写法,用于访问矩阵lab在坐标(row,col)的值。Vec3b表示3通道,每个通道为uchar类型(0-255)。

inline int get_distance(const Mat lab,int clusters_index,int i,int j) {
	int dl = clusters[clusters_index].l - lab.at<Vec3b>(i, j)[0];
	int da = clusters[clusters_index].a - lab.at<Vec3b>(i, j)[1];
	int db = clusters[clusters_index].b - lab.at<Vec3b>(i, j)[2];
	int dx = clusters[clusters_index].row - i;
	int dy = clusters[clusters_index].col - j;

	int h_distance = dl * dl + da * da + db * db;
	int xy_distance = dx * dx + dy * dy;
	//cout << h_distance << "\t" << xy_distance * 100 << endl;
	return h_distance + xy_distance * 100;
}

完成update_pixel函数:

void update_pixel(const Mat lab,int s) {
	for (int i = 0; i < sqrtK * sqrtK; i++) {	// 对于每一个超像素
		int clusters_x = clusters[i].row;
		int clusters_y = clusters[i].col;
		for (int x = -s; x <= s; x++) {			// 在它周围-s到s的范围内
			for (int y = -s; y <= s; y++) {
				int now_x = clusters_x + x;
				int now_y = clusters_y + y;
				if (now_x < 0 || now_x >= sqrtN || now_y < 0 || now_y >= sqrtN)
					continue;
				int new_dis = get_distance(lab, i, now_x, now_y);
				// 如果为-1(还没有更新过)或者新的距离更小,就更换当前像素属于的超像素
				if (dis[now_x][now_y] > new_dis || dis[now_x][now_y] == -1) {
					dis[now_x][now_y] = new_dis;
					label[now_x][now_y] = i;
				}
			}
		}
	}
}

三、updaye_clusters函数

根据当前超像素的所有归属像素来更新位置。需要注意的是C++用new申请空间时后面加上()会自动初始化申请的空间。且new出的空间需要程序员手动delete释放。

void updaye_clusters(const Mat lab) {
	int *sum_count = new int[sqrtK * sqrtK]();
	int *sum_i = new int[sqrtK * sqrtK]();
	int *sum_j = new int[sqrtK * sqrtK](); 
	int* sum_l = new int[sqrtK * sqrtK]();
	int* sum_a = new int[sqrtK * sqrtK]();
	int* sum_b = new int[sqrtK * sqrtK]();
	for (int i = 0; i < sqrtN; i++) {
		for (int j = 0; j < sqrtN; j++) {
			sum_count[label[i][j]]++;
			sum_i[label[i][j]] += i;
			sum_j[label[i][j]] += j; 
			sum_l[label[i][j]] += lab.at<Vec3b>(i, j)[0];
			sum_a[label[i][j]] += lab.at<Vec3b>(i, j)[1];
			sum_b[label[i][j]] += lab.at<Vec3b>(i, j)[2];
		}
	}
	for (int i = 0; i < sqrtK * sqrtK; i++) {
		if (sum_count[i] == 0) {
			continue;
		}
		clusters[i].row = round(sum_i[i] / sum_count[i]);
		clusters[i].col = round(sum_j[i] / sum_count[i]); 
		clusters[i].l = round(sum_l[i] / sum_count[i]);
		clusters[i].a = round(sum_a[i] / sum_count[i]);
		clusters[i].b = round(sum_b[i] / sum_count[i]);
	}
	delete[] sum_count;
	delete[] sum_i;
	delete[] sum_j;
	delete[] sum_l;
	delete[] sum_a;
	delete[] sum_b;
}

四、draw_clusters函数——画出每一个朝像素点

五、final_draw函数——绘制一张超像素分割图

void draw_clusters(const Mat copy) {
	for (int index = 0; index < sqrtK * sqrtK; index++) {
		Point p(clusters[index].row, clusters[index].col);
		circle(copy, p, 1, Scalar(0, 0, 255), 1);  // 画半径为1的圆(画点)
	}
	imshow("超像素示意图", copy);
}
	
void final_draw(const Mat lab,Mat copy) {
	for (int i = 0; i < sqrtN; i++) {
		for (int j = 0; j < sqrtN; j++) {
			int index = label[i][j];
			copy.at<Vec3b>(i, j)[0] = lab.at<Vec3b>(clusters[index].row, clusters[index].col)[0];
			copy.at<Vec3b>(i, j)[1] = lab.at<Vec3b>(clusters[index].row, clusters[index].col)[1];
			copy.at<Vec3b>(i, j)[2] = lab.at<Vec3b>(clusters[index].row, clusters[index].col)[2];
		}
	}
	cvtColor(copy, copy, CV_Lab2BGR);
	imshow("分割图", copy);
}

六、draw_edge

void draw_edge(const Mat lab, Mat copy) {
	// 这里的代码和上面的函数几乎一样,都是同标签的绘制相应的超像素颜色,因为方便用户自己选用绘制函数所以没有调用上面的函数
	for (int i = 0; i < sqrtN; i++) {
		for (int j = 0; j < sqrtN; j++) {
			int index = label[i][j];
			copy.at<Vec3b>(i, j)[0] = lab.at<Vec3b>(clusters[index].row, clusters[index].col)[0];
			copy.at<Vec3b>(i, j)[1] = lab.at<Vec3b>(clusters[index].row, clusters[index].col)[1];
			copy.at<Vec3b>(i, j)[2] = lab.at<Vec3b>(clusters[index].row, clusters[index].col)[2];
		}
	}

	// 这里的思路是4个方向,一旦有标签不同,就设置为黑色(超像素边界为黑色
	static int X[] = { 0,0,-1,1 };
	static int Y[] = { 1,-1,0,0 };
	cvtColor(copy, copy, CV_Lab2BGR); // 改成BGR,方便后面设置边框的颜色。
	for (int i = 0; i < sqrtN; i++) {
		for (int j = 0; j < sqrtN; j++) {
			int index = label[i][j];
			for (int k = 0; k < 4; k++) {
				if (index != label[i + X[k]][j + X[k]])
					copy.at<Vec3b>(i, j)[0] = copy.at<Vec3b>(i, j)[1] = copy.at<Vec3b>(i, j)[2] = 0;
			}
		}
	}
	imshow("超像素边界", copy);
}

vscode显示未定义标识符CV_Lab2BGR
问题原因:新版本中色彩空间转换的标识符已经改了,但是文档似乎还没更新。
解决方法:把 CV_BGR2Lab 改成 COLOR_BGR2Lab 即可。
结果:不报错。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值