基于区域生长的图像分割算法!

图像分割的目的是将图像划分为多个不同的区域,所以我们可以直接从寻找图像中的区域来设计分割算法。区域生长正是一种基于区域寻找的传统图像分割算法。

区域生长基本原理

区域生长(Region Growth)算法是一种基于区域的传统图像分割算法。区域生长可以根据预先定义的生长规则将像素或者小区域不断组合为更大区域的过程。具体地,区域生长是从一组初始种子点出发,通过预先定义的区域生长规则,将与种子点性质相似的领域像素不断添加到每个种子点上,并且满足区域生长的终止条件时形成最终生长区域的过程。假设为待分割的输入图像阵列,为一组种子点阵列,其中种子点处位置为1,其他位置为0,并且假设和具有相同的尺寸。表示在每个像素点的相关属性。基于8连接的区域生长算法的流程描述如下:

  • 在种子阵列中找到所有的连通分量,将每个连通分量腐蚀为一个像素,并将腐蚀成功的像素标记为1,其他像素标记为0.

  • 在坐标形成图像,如果输入图像在该点坐标处满足给定的属性,则令,否则令。

  • 将中为8连通种子点的所有为1的点添加到s中的每个种子点中直至满足生长结束条件。

  • 最后在不同区域标记出每个连通分量形成最终的分割图像。

  • 下面我们通过一个简单的计算例子来更加直观地理解区域生长算法。

    假设有如图所示的5×5图像阵列,选定阵列中最大值9作为初始种子点(图中橙色像素块),区域生长的像素阈值为2,现按照区域生长算法对该图像阵列进行分割。

    241b356b1b3e2a532589eee079483fd7.png

    按照区域生长阈值2,取值范围为[7,11],生长后的区域像素均值为7.8。第一次生长效果如图2所示,蓝色区域即第一次生长后的区域。

    5a1f7fc5c53e01faadca6abe51bba02d.png

    第一次生长后的区域均值为7.8,按照生长阈值为2,第二次生长的像素取值范围应为[5.8,9.8],如图3所示,绿色部分为第二次生长的区域结果。

    c1c2a05239d3187c9e6f0b1037ffcd01.png

    第二次生长后的区域均值为7.1,按照生长阈值为2,第三次生长的像素取值范围应为[5.1,9.1],如图4所示,黄色部分为第三次生长的区域结果。

    376d79c1e97a3b8173f093a20f0ad208.png

    第三次生长后的区域均值为6.9,按照生长阈值为2,第三次生长的像素取值范围应为[4.9,8.9],周边已没有满足继续生长的像素点,到此我们停止生长。将生长后的区域标记为1,其他区域标记为0,最终的分割区域如图5所示。

    c24d869db0fd874de44db59142860b73.png

    区域生长分割示例

    给定一张NBA球星科比的图像(如图6所示),我们尝试实现一个区域生长图像分割算法来对该图像进行前景与背景的分割。

    3bf9b6cf2aa36d729ee00bda1cd01aba.png

    现尝试基于C++代码来实现一个区域生长分割算法。按照前述基于8连接的区域生长算法的流程描述,实现过程如下代码所示。

    #include <iostream>
    #include <opencv2/opencv.hpp>
    
    
    using namespace std;
    using namespace cv;
    // 定义区域生长分割算法
    bool RegionGrowing(Mat img, Mat& result, Point2i seed, int threshold) {
      // 将图像全部设置为黑
        result = Mat::zeros(img.size(), CV_8UC1); 
        // 设置种子点范围条件
        if (seed.x < 0 || seed.y < 0 || seed.y > img.rows - 1 || seed.x > img.cols - 1) {
            return false;
        }
        // 种子点集
        vector<Point2i> seeds; 
        // 压入初始种子点
        seeds.push_back(seed); 
        // 种子点设置为白
        result.ptr<uchar>(seed.y)[seed.x] = 255; 
        // 8连接生长方向
        int growDirections[8][2] = { {-1,-1}, {0,-1}, {1,-1}, {-1,0}, {1,0}, {-1,1}, {0,1}, {1,1} }; 
    
    
        // 开始生长
        while (!seeds.empty()) {
            // 取出一个种子点作为现在循环的初始种子点
            Point2i seed_current = seeds.back();
            seeds.pop_back();
    
    
            // 遍历各生长方向的邻点
            for (int i = 0; i < 8; i++) {
                Point2i neighborPoint = { seed_current.x + growDirections[i][0], seed_current.y + growDirections[i][1] }; // 邻点
                if (neighborPoint.x < 0 || neighborPoint.y < 0 || neighborPoint.x > img.cols - 1 || neighborPoint.y > img.rows - 1) { // 邻点超出范围
                    continue;
                }
                if ((result.ptr<uchar>(neighborPoint.y)[neighborPoint.x] == 0) && abs(img.ptr<uchar>(neighborPoint.y)[neighborPoint.x] - img.ptr<uchar>(seed.y)[seed.x]) < threshold) {
                    // 设置为种子点
                    result.ptr<uchar>(neighborPoint.y)[neighborPoint.x] = 255; // 设置为白色
                    seeds.push_back(neighborPoint); // 压入种子集
                }
            }
        }
    
    
        return true;
    }

    基于上述代码分割后的图像如图7所示。可以看到,区域生长算法基本上能分割出目标区域,但因为图像本身复杂性,在分割效果上仍有待提升的地方。

    95ad8be5391d0e21b588d63b37897a8b.png

    参考资料:

    冈萨雷斯 数字图像处理

    https://gy23333.github.io/2020/01/18/%E5%9F%BA%E4%BA%8E%E5%8C%BA%E5%9F%9F%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E5%89%B2%E2%80%94%E2%80%94%E5%8C%BA%E5%9F%9F%E7%94%9F%E9%95%BF/

    往期精彩:

    《机器学习:公式推导与代码实现》1-7章PPT下载

    《机器学习 公式推导与代码实现》随书PPT示例

     时隔一年!深度学习语义分割理论与代码实践指南.pdf第二版来了!

     新书首发 | 《机器学习 公式推导与代码实现》正式出版!

    《机器学习公式推导与代码实现》将会配套PPT和视频讲解!

    物联沃分享整理
    物联沃-IOTWORD物联网 » 基于区域生长的图像分割算法!

    发表评论