ToB企服应用市场:ToB评测及商务社交产业平台

标题: Opencv-C++笔记 (20) : 间隔变更与分水岭的图像分割 [打印本页]

作者: 鼠扑    时间: 2024-8-21 21:22
标题: Opencv-C++笔记 (20) : 间隔变更与分水岭的图像分割
一、图片分割

   图像分割(Image Segmentation)是图像处理惩罚最重要的处理惩罚手段之一
图像分割的目标是将图像中像素根据肯定的规则分为若干(N)个cluster集合,每个集合包罗一类像素。
根据算法分为监视学习方法和无监视学习方法,图像分割的算法多数都是无监视学习方法 - KMeans
  分水岭算法理解

分水岭(Watershed)是基于地理形态的分析的图像分割算法,模拟地理结构(比如山水、沟壑,盆地)来实现对差别物体的分类。分水岭算法中会用到一个重要的概念——测地线间隔
  1.     图像的灰度空间很像地球表面的整个地理结构,每个像素的灰度值代表高度。其中的灰度值较大的像素连成的线可以看做山脊,也就是分水岭。其中的水就是用于二值化的gray threshold level,二值化阈值可以理解为水平面,比水平面低的区域会被淹没,刚开始用水填充每个孤立的山谷(局部最小值)。
  2.     当水平面上升到一定高度时,水就会溢出当前山谷,可以通过在分水岭上修大坝,从而避免两个山谷的水汇集,这样图像就被分成2个像素集,一个是被水淹没的山谷像素集,一个是分水岭线像素集。最终这些大坝形成的线就对整个图像进行了分区,实现对图像的分割。
复制代码

在该算法中,空间上相邻而且灰度值相近的像素被划分为一个地区。
分水岭算法过程

此中的解决方法:
二、间隔变更与分水岭

间隔变更常见算法有两种

  1.    不断膨胀/ 腐蚀得到
  2.    基于倒角距离
复制代码
分水岭变更常见的算法

  1.   基于浸泡理论实现
复制代码
步骤

   将白色配景变成黑色-目的是为背面的变更做准备
使用filter2D与拉普拉斯算子实现图像对比度进步,sharp(锐化)
转为二值图像通过threshold
间隔变更
对间隔变更结果举行归一化到[0~1]之间 使用阈值,再次二值化,
得到标记 腐蚀得到每个Peak- erode
发现轮廓 – findContours
绘制轮廓- drawContours
分水岭变更 watershed
对每个分割地区着色输出结果
  配景:不感兴趣的地区,越阔别目标图像中央的地区就越是配景
前景:感兴趣的地区,越靠近目标图像中央就越是前景
未知地区:即不确定地区,边界所在的地区

改进:
在 O p e n C v OpenCvOpenCv 中算法不从最小值开始增长,可以将相对较高的灰度值像素作为起始点(必要用户手动标记),从标记处开始举行淹没,则很多小地区都会被合并为一个地区,这被称为基于图像标(mark)的分水岭算法。此中标记的每个点就相当于分水岭中的注水点,从这些点开始注水使得水平面上升。手动标记太麻烦,我们可是使用间隔转换(cv2.distanceTransform函数)的方法举行标记。cv2.distanceTransform计算的是图像内非零值像素点到最近的零值像素点的间隔,即计算二值图像中全部像素点间隔其最近的值为 0 的像素点的间隔。当然,如果像素点本身的值为 0,则这个间隔也为 0。
主要函数


   cv::watershed 函数实现了基于间隔变更的分水岭算法。该函数的原型如下:
  1.  void watershed(InputArray image,
  2.    InputOutputArray markers
  3.    );
复制代码
  image:输入的图像,必须为8位的3通道彩色图像。
markers:输出的标记图像,必须为单通道32位整型图像。
在使用cv::watershed函数举行分水岭算法分割时,必要先举行前期处理惩罚,包括图像的预处理惩罚和创建标记图像。
  c++代码

  1. #include <opencv2\opencv.hpp>
  2. #include <iostream>
  3. using namespace std;
  4. using namespace cv;
  5. int main()
  6. {
  7.         Mat img, imgGray, imgMask;
  8.         Mat maskWaterShed;  // watershed()函数的参数
  9.         img = imread("HoughLines.jpg");  //原图像
  10.         if (img.empty())
  11.         {
  12.                 cout << "请确认图像文件名称是否正确" << endl;
  13.                 return -1;
  14.         }
  15.         cvtColor(img, imgGray, COLOR_BGR2GRAY);
  16.         //提取边缘并进行闭运算
  17.         Canny(imgGray, imgMask, 150, 300);
  18.         Mat k = getStructuringElement(0, Size(3, 3));
  19.         morphologyEx(imgMask, imgMask, MORPH_CLOSE, k);
  20.         imshow("边缘图像", imgMask);
  21.         imshow("原图像", img);
  22.         //计算连通域数目
  23.         vector<vector<Point>> contours;
  24.         vector<Vec4i> hierarchy;
  25.         findContours(imgMask, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
  26.         //在maskWaterShed上绘制轮廓,用于输入分水岭算法
  27.         maskWaterShed = Mat::zeros(imgMask.size(), CV_32S);
  28.         for (int index = 0; index < contours.size(); index++)
  29.         {
  30.                 drawContours(maskWaterShed, contours, index, Scalar::all(index + 1),
  31.                         -1, 8, hierarchy, INT_MAX);
  32.         }
  33.         //分水岭算法   需要对原图像进行处理
  34.         watershed(img, maskWaterShed);
  35.         vector<Vec3b> colors;  // 随机生成几种颜色
  36.         for (int i = 0; i < contours.size(); i++)
  37.         {
  38.                 int b = theRNG().uniform(0, 255);
  39.                 int g = theRNG().uniform(0, 255);
  40.                 int r = theRNG().uniform(0, 255);
  41.                 colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
  42.         }
  43.         Mat resultImg = Mat(img.size(), CV_8UC3);  //显示图像
  44.         for (int i = 0; i < imgMask.rows; i++)
  45.         {
  46.                 for (int j = 0; j < imgMask.cols; j++)
  47.                 {
  48.                         // 绘制每个区域的颜色
  49.                         int index = maskWaterShed.at<int>(i, j);
  50.                         if (index == -1)  // 区域间的值被置为-1(边界)
  51.                         {
  52.                                 resultImg.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
  53.                         }
  54.                         else if (index <= 0 || index > contours.size())  // 没有标记清楚的区域被置为0
  55.                         {
  56.                                 resultImg.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
  57.                         }
  58.                         else  // 其他每个区域的值保持不变:1,2,…,contours.size()
  59.                         {
  60.                                 resultImg.at<Vec3b>(i, j) = colors[index - 1];  // 把些区域绘制成不同颜色
  61.                         }
  62.                 }
  63.         }
  64.         resultImg = resultImg * 0.6 + img * 0.4;
  65.         imshow("分水岭结果", resultImg);
  66.         waitKey(0);
  67.         return 0;
  68. }
复制代码
四、结果展示

1、原始图像

2、分割结果

五、参考链接
[1] 【OpenCv】图像分割——分水岭算法
[2] 【OpenCv】图像分割2——分水岭算法

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4