原理:
1.从左到右,从上到下遍历每一个像素点,然后判断该像素的值是否在集合V中(这里是一张二值图,我们假设黑色的值为0)。
2.如果遇到一个值为0的点,说明在该点附近可能存在与该点相连通的像素点,即可能存在连通域。那么我们就暂时停止之前的遍历,开始以该点为种子点,查找该点附近的邻域中是否存在与其连通的像素点,若与该点连通,则将其存到一个堆栈中,并将该点的标签赋值为与种子点相同的标签,并对访问过的像素点置一个表示已访问的标志,以避免后面对其重复访问。
3.接下来就从堆栈中取出点,查看该点的四邻域,依次把和该点连通的点压入到堆栈中。
4.继续从堆栈中取出点,并遍历该点的四邻域,如此循环,直到堆栈变为空,则说明已经遍历完了所有的与初始点相连通的连通成分。标签加1,以便对后面的连通成分标签和该连通成分标签加以区分。
5.接下来就可以继续之前的从左到右、从上到下的遍历工作了。访问过的点不再进行对其访问,直接跳过。
6.当访问到一个新的在集合V内的像素点时,则又以该点为种子点进行四邻域的遍历循环,并标记上相应的标签和访问标志,直到标记完整个连通成分,则标签加1,继续后面的遍历。如此循环往复。直到访问完最后一个像素点,我们的连通成分也标记完了。
实现:
递归方法(容易栈溢出)
/**
* @description: 深度优先搜索
* @param src 输入图像
* @param dst 输出图像
* @param i 行号
* @param j 列号
* @param num 区域序号
*/
void dfs(cv::Mat& src, cv::Mat& dst, int i, int j, int num)
{
if (i >= 0 && i < src.rows && j >= 0 && j < src.cols && src.at<uchar>(i, j) && dst.at<cv::Vec3b>(i, j) == cv::Vec3b(0, 0, 0))
{
dst.at<cv::Vec3b>(i, j) = Vec3b(num * 80, 255 - num * 80, 0);
dfs(src, dst, i - 1, j, num);
dfs(src, dst, i + 1, j, num);
dfs(src, dst, i, j - 1, num);
dfs(src, dst, i, j + 1, num);
}
}
/**
* @description: 连通域标记
* @param src 输入图像
* @param dst 输出图像
*/
void getConnectedDomain(cv::Mat& src, cv::Mat &dst)
{
dst = cv::Mat::zeros(src.size(), cv::CV_8UC3);
int num = 0;
for (int i = 0; i < src.rows; ++i)
{
for (int j = 0; j < src.cols; ++j)
{
if (src.at<uchar>(i, j)!=0 && dst.at<Vec3b>(i, j) == Vec3b(0, 0, 0))
{
dfs(src, dst, i, j, num);
++num;
}
}
}
}
用栈实现
/**
* @description: 连通域标记
* @param src 输入图像
* @param dst 输出图像
*/
void getConnectedDomain(cv::Mat& src, cv::Mat &dst)
{
dst = cv::Mat::zeros(src.size(), cv::CV_8UC3);
int num = 0;
for (int i = 0; i < src.rows; ++i)
{
for (int j = 0; j < src.cols; ++j)
{
int dir[4][2] = { { -1, 0 },{ 1, 0 },{ 0, -1 },{ 0, 1 }};
std::vector<cv::Point> pts;
cv::Point pt,p;
if (src.at<uchar>(i, j)!=0 && dst.at<cv::Vec3b>(i, j) == cv::Vec3b(0, 0, 0))
{
pts.push_back(cv::Point(j, i));
dst.at<cv::Vec3b>(i, j) = cv::Vec3b(num * 80, 255 - num * 80, 0);
while (!pts.empty())
{
pt = pts.back();
pts.pop_back();
for (int k = 0; k < 4; ++k)
{
p.x = pt.x + dir[k][0];
p.y = pt.y + dir[k][1];
if (p.x < 0 || p.y < 0 || p.x >= src.cols || p.y >= src.rows) continue;
if (src.at<uchar>(p.y,p.x)!=0 && dst.at<cv::Vec3b>(p.y, p.x) == cv::Vec3b(0, 0, 0))
{
dst.at<cv::Vec3b>(p.y, p.x) = cv::Vec3b(num * 80, 255 - num * 80, 0);
pts.push_back(p);
}
}
}
++num;
}
}
}
}