[CV] 联通状态估计
文章目录
简介
联通状态估计可以通过Two-Pass 算法实现,基于并查集。
假设对于一张图片,背景标记为-1,前景标记为0,统计图片中的联通区域。
下面动图可以展示:

算法流程
以8邻域联通为例。
第一次扫描
逐行扫描输入图片。初始化 label = 1。
对于每个像素位置,若为前景,扫描当前位置正左、左上、正上、右上四个位置的标签。如下表:*是当前位置,1是需要统计的八邻域中标签的位置。
1 1 1
1 * 0
0 0 0
- 若邻域中有标签,则
*处标签为邻域标签的最小值,同时通过并查集,合并邻域中所有标签 - 若邻域中没有标签,则
*处标签为label,label作为新的节点加入并查集,同时更新label += 1
第二次扫描
更新并查集,每个节点都指向其根结点。
逐行扫描标签,对每个标签,都更新为其根结点的标签,同时对标签计数。
代码
程序员面试金典有道题类似,
你有一个用于表示一片土地的整数矩阵 land,该矩阵中每个点的值代表对应地点的海拔高度。若值为 0 则表示水域。由垂直、水平或对角连接的水域为池塘。池塘的大小是指相连接的水域的个数。编写一个方法来计算矩阵中所有池塘的大小,返回值需要从小到大排序。
示例:
输入:
[
[0,2,1,0],
[0,1,0,1],
[1,1,0,1],
[0,1,0,1]
]
输出: [1,2,4]
代码如下
int DX[] = { 0, -1, -1, -1};
int DY[] = {-1, -1, 0, 1};
class Solution {
public:
vector<int> pondSizes(vector<vector<int>>& land) {
if (land.empty()) return {};
int row = land.size(), col = land[0].size();
for (auto &l: land)
for(auto &n: l) if (n) n = -1;
int label = 1;
vector<int> dsu{0};
for(int i = 0; i < row; ++i){
for(int j = 0; j < col; ++j){
if(land[i][j] == -1) continue;
int min_label = -1;
// 寻找当前位置 * 为中心,标记为 1 的位置的标签的最小值
// 1 1 1
// 1 * 0
// 0 0 0
for(int dir = 0; dir < 4; ++dir){
int dx = DX[dir], dy = DY[dir];
int x = i + dx, y = j + dy;
if (x > -1 && x < row && y > -1 && y < col){
if (land[x][y] != -1){
min_label = (min_label == -1? land[x][y]: min(land[x][y], min_label));
}
}
}
// 如果周围没有标签,新建一个标签赋给当前位置
if (min_label == -1){
land[i][j] = label;
dsu.push_back(label);
++label;
}
else{
// 通过dsu,找到最小值的根结点,赋给当前位置
land[i][j] = min_label;
int org = min_label;
while(dsu[min_label] != min_label){
min_label = dsu[min_label];
}
//
dsu[org] = min_label;
for(int dir = 0; dir < 4; ++dir){
int dx = DX[dir], dy = DY[dir];
int x = i + dx, y = j + dy;
if (x > -1 && x < row && y > -1 && y < col){
// 遍历周围的标签,通过dsu合并标签
if (land[x][y] != -1) {
int o = land[x][y];
while (dsu[o] != o) o = dsu[o];
dsu[land[x][y]] = o;
dsu[o] = min_label;
}
}
}
}
}
}
int dsu_size = dsu.size();
// 将 dsu中每个子结点都直接指向根结点
for (int i = 1; i < dsu_size; ++i){
int m = i;
while (dsu[m] != m) m = dsu[m];
dsu[i] = m;
}
unordered_map<int, int> counter;
// 通过标签计数
for(auto &l: land)
for(auto &n: l) if(n != -1) ++counter[dsu[n]];
vector<int> ret;
for(auto p: counter){
ret.push_back(p.second);
}
sort(ret.begin(), ret.end());
return ret;
}
};
本文介绍了如何利用并查集实现联通状态估计,特别是在图像处理中统计联通区域的应用。通过两次扫描图片,首次将相同联通背景的像素归为同一组,第二次扫描统一各组标签并计数。算法流程包括8邻域联通判断,并提供了相关代码示例。

3563

被折叠的 条评论
为什么被折叠?



