通过并查集进行图像的连通区域实现

通过"并查集"进行图像的连通区域实现

并查集是个什么概念也是通过刷题刷到的,然后就稍微了解了下
小白记录并查集自己实现图像连通区域问题

完整代码放在最后面

并查集可以自己谷歌,目前有点被搞得头晕

在图像里面经常性的需要用连通区域这个概念,尤其是对图像分割,跟踪以及一些需要用到图像基本处理的地方,不过使用OpenCV的话很简单,一个API解决一切问题findContours,不过既然遇到了,就更应该去了解下更底层的东西以及原理性的东西吧!

可能对于连通区域用DFS更好理解吧,DFS:简单点理解就是不撞南墙不回头的那种算法

搞了一下午,被弄得头有点晕了,先记录下来,等清醒了再来理一理思路

- 首先自己生成简单的连通图像

通过并查集进行图像的连通区域实现_第1张图片
此处有两个白色的连通区域,目的是将他们分割出来
生成图像的代码

Mat Image = Mat::zeros(500, 500, CV_8UC1);
Mat gray(200, 200, CV_8UC1);
gray.setTo(255);
gray.copyTo(Image(Rect(100, 100, 200, 200)));
Image(Rect(0, 0, 10, 20)).setTo(255);

- 然后需要生成的分割图为

通过并查集进行图像的连通区域实现_第2张图片
总共分为三个区域的连通区域,其实这个有点像是图像分割了,在图像中加个KMeans的话或许就可以了,不过Kmeans原理也不难,有写过这样的博客,转这里

- 如何才能生成上面的图像呢,就是并查集算法了

class Solution {
public:
	int director[4][2] = { {0,-1},{-1,0},{0,1},{1,0} };
	map<int, int> m;
	int find(int x)
	{
		auto it = m.find(x);
		if (it == m.end())
		{
			m[x] = x;
		}
		
		if (m[x] != x)
		{
			m[x] = find(m[x]);
		}

		return m[x];
	}

	void con_union(int x, int y)
	{
		m[find(y)] = find(x);
	}

	void solve(vector<vector<int>>& board)
	{
		int row = board.size();
		if (row == 0) return;
		int col = board[0].size();
		int total_num = row * col;

		for (int i = 0; i < row; ++i)
		{
			for (int j = 0; j < col; ++j)
			{
				if (board[i][j] == 255)
				{	
					for (int k = 0; k < 4; ++k)
					{
						int ROW = i + director[k][0];
						int COL = j + director[k][1];
						if (ROW >= 0 && ROW < row && COL >= 0 && COL < col && board[ROW][COL] == 255)
						{
							con_union(i*col + j, ROW*col + COL);
						}
					}
				}
			}
		}
	}
};

这里是主要的并查集算法,主要的原理,还是通过find函数进行操作的,对每个坐标位置进行他们之间连通邻点的根值做索引,通过根值进行连通区域的分割操作

先记录下来,完整代码在这里

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
using namespace cv;

class Solution {
public:
	int director[4][2] = { {0,-1},{-1,0},{0,1},{1,0} };
	map<int, int> m;
	int findx(int x)
	{
		auto it = m.find(x);
		if (it == m.end())
		{
			m[x] = x;
		}
		
		if (m[x] != x)
		{
			m[x] = findx(m[x]);
		}

		return m[x];
	}

	void con_union(int x, int y)
	{
		m[findx(y)] = findx(x);
	}

	void solve(vector<vector<int>>& board)
	{
		int row = board.size();
		if (row == 0) return;
		int col = board[0].size();
		int total_num = row * col;

		for (int i = 0; i < row; ++i)
		{
			for (int j = 0; j < col; ++j)
			{
				if (board[i][j] == 255)
				{	
					for (int k = 0; k < 4; ++k)
					{
						int ROW = i + director[k][0];
						int COL = j + director[k][1];
						if (ROW >= 0 && ROW < row && COL >= 0 && COL < col && board[ROW][COL] == 255)
						{
							con_union(i*col + j, ROW*col + COL);
						}
					}
				}
			}
		}

		//set s;  // 连通区域的数量
		//int index = 0;
		//for (map::iterator it = m.begin(); it != m.end(); it++)
		//{
		//	index++;
		//	s.insert(it->second);
		//}
		//cout << "index=" << index << endl;
		//cout << "s.size=" << s.size() << endl;
		//int s_size = s.size();
		//vector V(s_size, 0);
		//int v_begin = 0;

		//for (set::iterator s_it = s.begin(); s_it != s.end(); s_it++)
		//{
		//	V[v_begin++] = *s_it;
		//	cout << *s_it << endl;
		//}

		vector<int> V_1;
		for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
		{
			int temp = it->second;
			if (V_1.empty())
			{
				V_1.push_back(temp);
			}
			else
			{
				if (find(V_1.begin(), V_1.end(), temp) == V_1.end())
				{
					V_1.push_back(temp);
				}
			}
		}
		int s_size = V_1.size();

		map<int,int>::iterator it = m.begin();
		for (int i = 0; i < row; ++i)
		{
			for (int j = 0; j < col; ++j)
			{
				int flag_index = i * col + j;
				if (it->first == flag_index)
				{
					for (int l = 0; l < s_size; l++)
					{
						if (m[flag_index] == V_1[l])
						{
							board[i][j] = l + 1;
						}
					}
					it++;
				}
			}
		}
	}
};

int main(int argc, char* argv[])
{
	Mat Image = Mat::zeros(500, 500, CV_8UC1);
	Mat gray(200, 200, CV_8UC1);
	gray.setTo(255);
	imshow("gray", gray);
	gray.copyTo(Image(Rect(100, 100, 200, 200)));
	Image(Rect(0, 0, 10, 20)).setTo(255);
	imshow("Image", Image);
	vector<vector<int>> board(Image.rows, vector<int>(Image.cols));

	for (int row = 0; row < Image.rows; ++row)
	{
		for (int col = 0; col < Image.cols; ++col)
		{
			board[row][col] = (int)Image.at<uchar>(row, col);
		}
	}

	Solution s;
	s.solve(board);

	// 辅助颜色层
	Vec3b ColorTab[] =
	{
		Vec3b(0, 0, 255),
		Vec3b(0, 255, 0),
		Vec3b(255, 0, 0),
		Vec3b(0, 255, 255),
		Vec3b(255, 0, 255),
		Vec3b(255, 255, 0)
	};

	Mat result;
	cvtColor(Image, result, COLOR_GRAY2BGR);
	for (int row = 0; row < Image.rows; ++row)
	{
		for (int col = 0; col < Image.cols; ++col)
		{
			result.at<Vec3b>(row, col) = ColorTab[board[row][col]];
		}
	}
	imshow("....", result);

	waitKey(0);
	return 0;
}

不过有些代码肯定是多余的,自行做相应的删减操作吧!

最后有个问题,不知道是不是程序没有释放内存空间还是自己电脑的编译器有问题,前面跑几次还可以,后面总是跑不出来!我也不知道为什么,小白在这里让大佬们解释下呀,感激不尽!


2020-05-28:
重新审了一遍代码:终于知道问题出在了哪里,一个边界越界的问题,vector数组没有先判断下标再去索引相应的值

if (board[ROW][COL] == 255 && ROW >= 0 && ROW < row && COL >= 0 && COL < col)

就这个地方,现在重新改过来了,代码一点问题都没有
这也说明写代码要好好考虑下界限的问题了!!!

后面有时间更新下DFS的连通区域算法!!!

欢迎指正批评!!!
欢迎关注微信公众号–木木夕算法笔记,与博主交流!

你可能感兴趣的:(c++)