效果:将二值化图像的连通区域划分出来

python opencv连通域骨架提取 opencv连通区域_算法

后:

python opencv连通域骨架提取 opencv连通区域_堆栈_02

优点:

1.图像尺寸大的也能用

2.提供一个思路

原理:

1.逐行遍历原图像,将当前第n行连接的部分放到一起并分组,然后在n-1行寻找连通的部分,如果找到就归入该组,如果没找到就列为新组

2.在向上寻找的时候可能遇到有:当前行n的某一连通区域在n-1行找到2个或2个以上对应的区域、当前行n有多个区域在n-1行找到同一个区域的问题,这些解决起来也不算难,就不提了

被我pass的方案:

逐个点遍历图像,通过递归的方式寻找连通区域(用这个方法记得copy一个图像出来,并且在遍历的时候记得修改像素,不然会死循环)。

pass原因:我要处理的图像比较大,会出现堆栈空间不够的问题,我试过增加堆栈空间,确实可行,但是这不灵活。

代码:

//连通区域提取
std::vector<std::vector<cv::Point>> getArea(Mat mat) {

	std::vector<int> last_row_countour_indexs;//上一行白色块所在的contours的索引,提高效率
	std::vector<std::vector<cv::Point>> contours;
	for (int i = 0; i < mat.rows; i++) {
		std::vector<int> row_countour_indexs;//这一行白色块所在的contours的索引,提高效率
											 //本行连续区块
		std::vector<std::vector<int>> continue_pos;
		bool newflag = true;
		std::vector<int> temp_block;
		for (int j = 0; j < mat.cols; j++) {
			uchar *data = mat.ptr<uchar>(i, j);
			if (newflag) {
				temp_block.clear();
			}
			if (data[0] == 255 && data[1] == 255 && data[2] == 255) {
				temp_block.push_back(j);
				newflag = false;
			}
			else {
				newflag = true;
				if (temp_block.size() > 0) {
					continue_pos.push_back(temp_block);
				}
			}
		}
		if (!newflag) {
			if (temp_block.size() > 0) {
				continue_pos.push_back(temp_block);
			}
		}
		//用上一行的信息判断这一行每个区块是属于哪个contours的,如果同一个区块属于不同contours,那就把那不同的contours合并
		for (int j = 0; j < mat.cols; j++) {
			uchar *data = mat.ptr<uchar>(i, j);
			if (data[0] == 255 && data[1] == 255 && data[2] == 255) {
				//白色像素,遍历已有的区域,判断是否建立新区域
				bool find = false;
				std::vector<int> find_indexs;
				//在上一行里找
				int temp = 0;
				int temp2 = 0;
				if (last_row_countour_indexs.size() > 0) {
					int index = -1;
					if (last_row_countour_indexs[j] >= 0) {
						//找到就在当前行对应的白块上标记contours的索引
						index = last_row_countour_indexs[j];
						temp++;
					}
					if (j + 1<mat.cols&&last_row_countour_indexs[j + 1] >= 0) {
						index = last_row_countour_indexs[j + 1];
						temp++;
						temp2++;
					}
					if (j - 1 >= 0 && last_row_countour_indexs[j - 1] >= 0) {
						index = last_row_countour_indexs[j - 1];
						temp++;
						temp2++;
					}
					if (temp2 == 2&&(last_row_countour_indexs[j + 1]!= last_row_countour_indexs[j - 1])) {
						//一点连接两个区域
						index = -2;
					}
					row_countour_indexs.push_back(index);
				}
				else {
					row_countour_indexs.push_back(-1);
				}
			}
			else {
				row_countour_indexs.push_back(-1);
			}
		}

		//要合并的contours索引
		std::vector<std::vector<int>> join_indexs;
		for (int m = 0; m < continue_pos.size(); m++) {
			std::vector<int> temp;
			for (int n = 0; n < continue_pos[m].size(); n++) {
				bool find = false;
				if (row_countour_indexs[continue_pos[m][n]] >= 0) {
					for (int z = 0; z < temp.size(); z++) {
						if (row_countour_indexs[continue_pos[m][n]] == temp[z]) {
							find = true;
							break;
						}
					}
					if (!find) {
						temp.push_back(row_countour_indexs[continue_pos[m][n]]);
					}
				}
				//一个点连接多个
				if (row_countour_indexs[continue_pos[m][n]] == -2) {
					row_countour_indexs[continue_pos[m][n]] = last_row_countour_indexs[continue_pos[m][n] - 1];
					temp.push_back(last_row_countour_indexs[continue_pos[m][n] + 1]);
					temp.push_back(last_row_countour_indexs[continue_pos[m][n] - 1]);
				}
			}
			join_indexs.push_back(temp);
		}

		/*
		if (i>=253&&i<=256) {
			cout << endl;
			for (int m = 0; m < row_countour_indexs.size(); m++) {
				cout << m << ":" << row_countour_indexs[m] << "||";
			}
			cout << endl;
			for (int m = 0; m < join_indexs.size(); m++) {
				cout << m << ":" << join_indexs[m].size() << "||";
			}
			cout << endl;
		}
		*/
		//合并contours或者新建
		for (int m = 0; m < join_indexs.size(); m++) {
			if (join_indexs[m].size() == 0) {
				//新建
				std::vector<cv::Point> newBlock;
				for (int n = 0; n < continue_pos[m].size(); n++) {
					//将当前行新区块添加到contours里
					newBlock.push_back(cv::Point(continue_pos[m][n], i));
					row_countour_indexs[continue_pos[m][n]] = contours.size();
				}
				contours.push_back(newBlock);
			}
			if (join_indexs[m].size() > 1) {
				//合并
				int index = join_indexs[m][0];
				//将要合并的合并到第一个
				for (int n = 1; n < join_indexs[m].size(); n++) {
					if (join_indexs[m][n] == index) {
						continue;
					}
					contours[index].insert(contours[index].begin(), contours[join_indexs[m][n]].begin(), contours[join_indexs[m][n]].end());
					/*for (int x = 0; x < contours[join_indexs[m][n]].size(); x++) {
						contours[index].push_back(contours[join_indexs[m][n]][x]);
					}*/
					contours[join_indexs[m][n]].clear();
					//修改所有和这个contours有关的新点
					for (int x = m+1; x < join_indexs.size(); x++) {
						for (int y = 0; y < join_indexs[x].size(); y++) {
							if (join_indexs[x][y] == join_indexs[m][n]) {
								join_indexs[x][y] = index;
							}
						}
					}
					//修改所有已记录的row_countour_indexs和join_indexs[m][n]相关的都要换成index
					for (int x = 0; x < row_countour_indexs.size(); x++) {
						if (row_countour_indexs[x] == join_indexs[m][n]) {
							row_countour_indexs[x] = index;
						}
					}
				}
				//将这一行的内容添加到新的集合里,并修改对应索引
				for (int n = 0; n < continue_pos[m].size(); n++) {
					contours[index].push_back(cv::Point(continue_pos[m][n], i));
					row_countour_indexs[continue_pos[m][n]] = index;
				}
			}
			if (join_indexs[m].size() == 1) {
				//将这一行的内容添加到新的集合里,并修改对应索引
				for (int n = 0; n < continue_pos[m].size(); n++) {
					contours[join_indexs[m][0]].push_back(cv::Point(continue_pos[m][n], i));
					row_countour_indexs[continue_pos[m][n]] = join_indexs[m][0];
				}
			}
		}
		/*
		for (int x = 0; x < join_indexs.size(); x++) {
		for (int y = 0; y < join_indexs[y].size(); y++) {
		cout << join_indexs[x][y] << endl;
		}
		}

		for (int x = 0; x < row_countour_indexs.size(); x++) {
		cout << row_countour_indexs[x] << endl;
		}
		*/
		last_row_countour_indexs = row_countour_indexs;
	}

	return contours;
}