🔍 灰度图像连通域分析 (C++实现)

基于C++非递归DFS的4邻域连通性检测 - 高效且易于理解

📖 什么是连通域分析?

连通域分析是图像处理中的重要技术,用于识别图像中相互连接的像素区域。在灰度图像中,我们通过比较相邻像素的灰度值差异来判断它们是否属于同一个连通域。

核心概念:
4邻域:每个像素的上、下、左、右四个相邻像素
连通性判断:灰度值差异小于阈值(20)的像素视为连通
非递归DFS:使用std::stack避免递归调用栈溢出
C++实现:使用STL容器,代码结构清晰,性能优秀

🔧 算法实现原理

步骤1:初始化
创建访问标记数组和结果数组,初始化栈结构
步骤2:遍历每个像素
对未访问的像素进行连通域检测
步骤3:非递归DFS
使用栈存储待访问的像素坐标,避免递归调用
步骤4:4邻域检查
检查当前像素的上下左右四个邻居
步骤5:连通性判断
比较邻居像素与当前像素的灰度值差异

💻 核心代码实现 (C++)

#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++逻辑)

以下JavaScript演示实现了与上述C++代码相同的算法逻辑

原始灰度图像

连通域结果

� C++代码详细解释

1. 数据结构设计
// 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;                          // 连通域计数器
2. 核心算法:非递归DFS
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));
                }
            }
        }
    }
}
3. 连通性判断逻辑
// 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));
}
关键优势:
非递归实现:避免递归调用栈溢出,适合大图像处理
内存高效:使用STL容器,自动内存管理
代码清晰:结构化设计,易于理解和维护
扩展性好:可轻松修改邻域类型和连通性判断条件

� 项目结构与完整示例

文件结构
connected_components_project/
├── connected_components.cpp    # 主要实现文件
├── connected_components.h      # 头文件(可选)
├── test_data/                  # 测试数据目录
│   ├── test1.txt              # 测试图像数据
│   └── test2.txt              # 更多测试数据
├── Makefile                   # 构建文件
└── README.md                  # 项目说明
Makefile 示例
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 
编译要求:
C++11或更高版本:使用了现代C++特性
标准库支持:需要<vector>, <stack>, <iostream>
优化建议:使用-O2优化标志提高性能
调试模式:使用-g -Wall进行调试和警告检查

📊 算法复杂度分析

⚡ 优化技巧

1. 栈优化:使用数组模拟栈,避免频繁的push/pop操作
2. 内存优化:复用数组空间,减少内存分配
3. 并行处理:对于大图像,可以分块并行处理
4. 阈值调整:根据图像特性动态调整连通性阈值
🏠 回到首页