基于C++非递归DFS的4邻域连通性检测 - 高效且易于理解
连通域分析是图像处理中的重要技术,用于识别图像中相互连接的像素区域。在灰度图像中,我们通过比较相邻像素的灰度值差异来判断它们是否属于同一个连通域。
#include <iostream>
#include <vector>
#include <stack>
#include <cmath>
struct Point {
int row, col;
Point(int r, int c) : row(r), col(c) {}
};
class ConnectedComponentAnalysis {
private:
std::vector<std::vector<int>> grayImage;
std::vector<std::vector<bool>> visited;
std::vector<std::vector<int>> result;
int height, width;
int componentCount;
int threshold;
// 4邻域方向:上、右、下、左
int directions[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
// 检查坐标是否有效
bool isValid(int row, int col) {
return row >= 0 && row < height && col >= 0 && col < width;
}
// 非递归DFS实现
void dfsNonRecursive(int startRow, int startCol) {
std::stack<Point> stack;
stack.push(Point(startRow, startCol));
int currentComponent = ++componentCount;
while (!stack.empty()) {
Point current = stack.top();
stack.pop();
int row = current.row;
int col = current.col;
// 如果已经访问过,跳过
if (visited[row][col]) continue;
// 标记为已访问并设置连通域ID
visited[row][col] = true;
result[row][col] = currentComponent;
// 检查4邻域
for (int i = 0; i < 4; i++) {
int newRow = row + directions[i][0];
int newCol = col + directions[i][1];
// 边界检查和访问状态检查
if (isValid(newRow, newCol) && !visited[newRow][newCol]) {
// 灰度值差异检查
int grayDiff = abs(grayImage[row][col] - grayImage[newRow][newCol]);
if (grayDiff < threshold) {
stack.push(Point(newRow, newCol));
}
}
}
}
}
public:
ConnectedComponentAnalysis(const std::vector<std::vector<int>>& image, int thresh = 20)
: grayImage(image), threshold(thresh) {
height = image.size();
width = image[0].size();
componentCount = 0;
// 初始化访问标记数组和结果数组
visited.assign(height, std::vector<bool>(width, false));
result.assign(height, std::vector<int>(width, 0));
}
// 执行连通域分析
void analyze() {
componentCount = 0;
// 遍历所有像素
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
// 对未访问的像素执行连通域检测
if (!visited[i][j]) {
dfsNonRecursive(i, j);
}
}
}
}
// 获取结果
const std::vector<std::vector<int>>& getResult() const {
return result;
}
// 获取连通域数量
int getComponentCount() const {
return componentCount;
}
// 打印结果
void printResult() {
std::cout << "连通域分析结果:" << std::endl;
std::cout << "图像尺寸: " << height << "x" << width << std::endl;
std::cout << "连通域数量: " << componentCount << std::endl;
std::cout << "阈值: " << threshold << std::endl;
std::cout << "\n连通域标记矩阵:" << std::endl;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
std::cout << result[i][j] << " ";
}
std::cout << std::endl;
}
}
};
// 使用示例
int main() {
// 创建测试图像 (8x8)
std::vector<std::vector<int>> testImage = {
{50, 52, 48, 120, 125, 200, 198, 202},
{49, 51, 47, 118, 122, 201, 199, 203},
{53, 50, 49, 119, 121, 197, 200, 201},
{120, 118, 122, 125, 123, 120, 118, 122},
{125, 123, 121, 124, 126, 119, 121, 120},
{200, 198, 202, 120, 118, 50, 52, 48},
{199, 201, 200, 122, 121, 49, 51, 47},
{203, 197, 199, 124, 123, 53, 50, 49}
};
// 创建连通域分析对象
ConnectedComponentAnalysis cca(testImage, 20);
// 执行分析
cca.analyze();
// 输出结果
cca.printResult();
return 0;
}
以下JavaScript演示实现了与上述C++代码相同的算法逻辑
// Point结构体:存储像素坐标
struct Point {
int row, col;
Point(int r, int c) : row(r), col(c) {}
};
// 核心成员变量
std::vector<std::vector<int>> grayImage; // 原始灰度图像
std::vector<std::vector<bool>> visited; // 访问标记数组
std::vector<std::vector<int>> result; // 连通域标记结果
int componentCount; // 连通域计数器
void dfsNonRecursive(int startRow, int startCol) {
std::stack<Point> stack; // 使用STL栈
stack.push(Point(startRow, startCol)); // 压入起始点
int currentComponent = ++componentCount; // 分配新的连通域ID
while (!stack.empty()) {
Point current = stack.top();
stack.pop();
int row = current.row;
int col = current.col;
if (visited[row][col]) continue; // 跳过已访问的像素
// 标记当前像素
visited[row][col] = true;
result[row][col] = currentComponent;
// 检查4个邻域
for (int i = 0; i < 4; i++) {
int newRow = row + directions[i][0];
int newCol = col + directions[i][1];
// 边界检查 + 访问状态检查
if (isValid(newRow, newCol) && !visited[newRow][newCol]) {
// 灰度值差异小于阈值则认为连通
int grayDiff = abs(grayImage[row][col] - grayImage[newRow][newCol]);
if (grayDiff < threshold) {
stack.push(Point(newRow, newCol));
}
}
}
}
}
// 4邻域方向向量:上、右、下、左
int directions[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
// 边界检查函数
bool isValid(int row, int col) {
return row >= 0 && row < height && col >= 0 && col < width;
}
// 连通性判断:灰度值差异小于阈值
int grayDiff = abs(grayImage[row][col] - grayImage[newRow][newCol]);
if (grayDiff < threshold) {
// 两个像素连通,加入栈中待处理
stack.push(Point(newRow, newCol));
}
connected_components_project/
├── connected_components.cpp # 主要实现文件
├── connected_components.h # 头文件(可选)
├── test_data/ # 测试数据目录
│ ├── test1.txt # 测试图像数据
│ └── test2.txt # 更多测试数据
├── Makefile # 构建文件
└── README.md # 项目说明
CXX = g++
CXXFLAGS = -std=c++11 -Wall -O2
TARGET = connected_components
SOURCE = connected_components.cpp
$(TARGET): $(SOURCE)
$(CXX) $(CXXFLAGS) -o $(TARGET) $(SOURCE)
clean:
rm -f $(TARGET)
test: $(TARGET)
./$(TARGET)
.PHONY: clean test
// 添加8邻域支持
class ConnectedComponentAnalysis8 : public ConnectedComponentAnalysis {
private:
// 8邻域方向:上、右上、右、右下、下、左下、左、左上
int directions[8][2] = {
{-1, 0}, {-1, 1}, {0, 1}, {1, 1},
{1, 0}, {1, -1}, {0, -1}, {-1, -1}
};
// 重写DFS函数以支持8邻域
void dfsNonRecursive(int startRow, int startCol) override {
// 使用8邻域的DFS实现
// ...
}
};
// 添加图像预处理功能
class ImagePreprocessor {
public:
static std::vector<std::vector<int>> gaussianBlur(
const std::vector<std::vector<int>>& image,
int kernelSize = 3
) {
// 高斯模糊实现
// ...
}
static std::vector<std::vector<int>> threshold(
const std::vector<std::vector<int>>& image,
int thresh = 128
) {
// 阈值化处理
// ...
}
};
# 编译命令
g++ -o connected_components connected_components.cpp -std=c++11
# 运行程序
./connected_components
# 预期输出示例
连通域分析结果:
图像尺寸: 8x8
连通域数量: 4
阈值: 20
连通域标记矩阵:
1 1 1 2 2 3 3 3
1 1 1 2 2 3 3 3
1 1 1 2 2 3 3 3
2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2
3 3 3 2 2 4 4 4
3 3 3 2 2 4 4 4
3 3 3 2 2 4 4 4
# 编译命令
g++ -o connected_components connected_components.cpp -std=c++11
# 运行程序
./connected_components
# 预期输出示例
连通域分析结果:
图像尺寸: 8x8
连通域数量: 4
阈值: 20
连通域标记矩阵:
1 1 1 2 2 3 3 3
1 1 1 2 2 3 3 3
1 1 1 2 2 3 3 3
2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2
3 3 3 2 2 4 4 4
3 3 3 2 2 4 4 4
3 3 3 2 2 4 4 4