OpenCV分水岭实现图像分割

OpenCV分水岭实现图像分割分水岭变换是一种流行的图像处理算法,用于快速将图像分割成多个同质区域。它基于这样的思想:如果把图像看作一个拓扑地貌,那么同类区域就相当于陡峭边缘内相对平坦的盆地。分水岭算法通过逐步增高水位,把地貌分割成多个部分。因为算法很简单,它的原始版本会过度分割图像,产生很多小的区域。因此OpenCV提出了该算法的改进版本,使用一系列预定义标记来引导图像分割的定义方式。

如何实现

使用分水岭分割法需要调用cv::watershed函数。该函数的输入对象是一个标记图像,图像的像素值为32位有符号整数,每个非零像素代表一个标签。它的原理是对图像中部分像素做标记,表明它们的所属区域是已知的。分水岭算法可根据这个初始标签确定其他像素所属的区域。本文将先建立一个标记图像作为灰度图像,然后将其转换成整型图像。我们把这个步骤封装进WatershedSegmenter类,它包括指定标记图像和计算分水岭的方法:

        class WatershedSegmenter {

          private:
          cv::Mat markers;

          public:
          void setMarkers(const cv::Mat& markerImage) {

          // 转换成整数型图像
          markerImage.convertTo(markers, CV_32S);
        }

        cv::Mat process(const cv::Mat &image) {

          // 应用分水岭
          cv::watershed(image, markers);
          return markers;
        }

不同应用程序获得标记的方式各不相同。例如,可在预处理过程中识别出一些属于某个感兴趣物体的像素。然后,根据初始检测结果,使用分水岭算法划出整个物体的边缘。识别出对应原始图像中的动物(原始图像见计算图像直方图)。因此,我们需要从二值图像中识别出属于前景(动物)的像素以及属于背景(主要是草地)的像素。这里把前景像素标记为255,把背景像素标记为128(该数字是随意选择的,任何不等于255的数字都可以)。其他像素的标签是未知的,标记为0。

现在,这个二值图像包含了属于图像不同部分的白色像素,因此要对图像做深度腐蚀运算,只保留明显属于前景物体的像素:

// 消除噪声和细小物体
cv::Mat fg;
cv::erode(binary, fg, cv::Mat(), cv::Point(-1, -1),4);

得到的图像如下所示。

OpenCV分水岭实现图像分割

注意,仍然有少量属于背景(森林)的像素保留了下来,不用管它们,可将它们看作感兴趣物体。与之类似,我们可以通过对原二值图像做一次大幅度的膨胀运算来选中一些背景像素:

// 标识不含物体的图像像素
cv::Mat bg;
cv::dilate(binary, bg, cv::Mat(), cv::Point(-1, -1),4);
cv::threshold(bg, bg,1,128, cv::THRESH_BINARY_INV);

得到的黑色像素对应背景像素。因此在膨胀后,要立即通过阈值化运算把它们赋值为128。得到的图像如下所示。

OpenCV分水岭实现图像分割

合并这两幅图像,得到标记图像,代码为:

// 创建标记图像
cv::Mat markers(binary.size(), CV_8U, cv::Scalar(0));
markers= fg+bg;

注意这里是如何用重载运算符+来合并图像的。下面的图像将被输入分水岭算法。

OpenCV分水岭实现图像分割

毫无疑问,在这个输入图像中,白色区域属于前景物体,灰色区域属于背景,而黑色区域带有未知标签。分水岭算法的作用就是明确地划分前景和背景,并对黑色区域的像素做出标记(属于前景还是背景)。可用下面的方法来分割图像:

// 创建分水岭分割类的对象
WatershedSegmenter segmenter;

// 设置标记图像,然后执行分割过程
segmenter.setMarkers(markers);
segmenter.process(image);

上面的代码会修改标记图像,每个值为0的像素都会被赋予一个输入标签,而边缘处的像素被赋值为-1,得到的标签图像如左图所示,边缘图像如右图所示。
OpenCV分水岭实现图像分割

酷客网相关文章:

赞(0)

评论 抢沙发

评论前必须登录!