8.5 分水岭算法(watershed algorithm)

1.基于拓扑理论的数学形态学的分割方法。
2.基本思想:把图像看作测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,分水岭变换得到的是输入图像的集水盆图像,集水盆边界形成分水岭(输入图像的极大值点)。
3.计算过程:一个迭代标注过程,包括排序过程和淹没过程,先对每个像素的灰度级进行从低到高的排序,然后在从低到高实现淹没的过程中,对每一个局部最小值在h阶高度的影响域采用先进先出(FIFO)结构进行判断及标注。

8.5.1 实现分水岭算法:watershed()函数

1.基本操作:
  基于标记的分割算法,在把图像传给函数之前,需要大致标记出图像中期望进行分割的区域,每个区域被标记为像素值1、2、3等,表示成为一个或多个连接组件,标记的值可使用findContours()函数和drawContours()函数由二进制的掩码检索出来,函数输出中,每一个标记中的像素被设置为标记的值,区域间的值被设置为-1。
2.函数原型:

void watershed(InputArray image,InputOutputArray markers)

3.参数说明:
  (1)输入图像,8位三通道彩色图像
  (2)函数调用后运算结果,输入/输出32位单通道图像的标记结果

8.5.2 综合示例

/*程序说明:鼠标大致标记出图像中期望进行分割的区域键盘按键【1】启动分水岭算法按键【2】恢复原始图重新标记
*/
#include<opencv2/opencv.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
using namespace cv;
using namespace std;
//定义辅助宏
#define WINDOW_NAME1 "【原始图窗口】"
#define WINDOW_NAME2 "【恢复原始图窗口】"
#define WINDOW_NAME3 "【分水岭变换窗口】"
//全局变量
Mat g_srcImage, g_maskImage;
Point prevPt(-1, -1);
//全局函数
static void on_Mouse(int event, int x, int y, int flags, void*);
static void ShowHelpText();int main()
{//【0】显示帮助信息ShowHelpText();//【1】载入原图并显示g_srcImage = imread("night.jpg");if (!g_srcImage.data){ printf("载入原图像失败~!\n");return false;}imshow(WINDOW_NAME1, g_srcImage);//【2】初始化掩模和灰度图Mat srcImage, grayImage;g_srcImage.copyTo(srcImage);cvtColor(srcImage, g_maskImage, COLOR_BGR2GRAY);cvtColor(g_maskImage, grayImage, COLOR_GRAY2BGR);g_maskImage = Scalar::all(0);//【3】设置鼠标回调函数setMouseCallback(WINDOW_NAME1, on_Mouse, 0);//【4】轮询按键,进行处理while (1){//获取键值int c = waitKey(0);//按键值为ESC时,退出程序if ((char)c == 27)break;//按键为2时,恢复原图,使g_maskImage和g_srcImage可重新标记if ((char)c == '2'){g_maskImage = Scalar::all(0);srcImage.copyTo(g_srcImage);imshow(WINDOW_NAME2, g_srcImage);}//按键值为1时,进行处理if ((char)c == '1'){//定义一些参数int i, j;int compCount = 0;  //记录轮廓数vector<vector<Point>>contours;vector<Vec4i> hierarchy;//寻找轮廓findContours(g_maskImage, contours, hierarchy, CV_RETR_CCOMP, CHAIN_APPROX_SIMPLE);//轮廓为空时的处理if (contours.empty())continue;//复制掩膜Mat maskImage(g_maskImage.size(), CV_32S);maskImage = Scalar::all(0);//循环绘制轮廓for (int index = 0; index >= 0; index = hierarchy[index][0], compCount++){drawContours(maskImage, contours, index, Scalar::all(compCount + 1), -1, 8, hierarchy, INT_MAX);}//为每一个轮廓生成一个随机颜色vector<Vec3b>colorTab;for (int i = 0; i < compCount; i++){int b = theRNG().uniform(0, 255);int g = theRNG().uniform(0, 255);int r = theRNG().uniform(0, 255);colorTab.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));}//进行分水岭算法并计算处理时间输出到窗口中double dTime = (double)getTickCount();watershed(srcImage, maskImage);dTime = (double)getTickCount() - dTime;printf("\t 处理时间 = %gms\n", dTime*1000. / getTickFrequency());//双层循环,将分水岭图像遍历存入watershedImage中Mat watershedImage(maskImage.size(), CV_8UC3);for (i = 0; i < maskImage.rows; i++)for (j = 0; j < maskImage.cols; j++){int index = maskImage.at<int>(i, j);if (index == -1)watershedImage.at<Vec3b>(i, j) = Vec3b(255, 255, 255);//maskImage中区域间值像素为-1,将对应watershed图像像素设置为白色(255,255,255)else if (index <= 0 || index > compCount)watershedImage.at<Vec3b>(i, j) = Vec3b(0, 0, 0);      //maskImage中非标记区域,将对应watershed图像像素设置为黑色(0,0,0)elsewatershedImage.at<Vec3b>(i, j) = colorTab[index - 1]; //maskImage中标记区域,将对应watershed图像像素设置为之前随机出的颜色colorTab}//混合灰度图和分水岭效果图并显示最终的窗口watershedImage = watershedImage * 0.5 + grayImage * 0.5;imshow(WINDOW_NAME3, watershedImage);}}return 0;
}//鼠标消息回调函数
void on_Mouse(int event, int x, int y, int flags, void*)
{//处理鼠标不在窗口中的情况if (x < 0 || x >= g_srcImage.cols || y < 0 || y >= g_srcImage.rows)return;//处理鼠标左键相关消息if (event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON)) //松开左键prevPt = Point(-1, -1);else if (event == EVENT_LBUTTONDOWN)//按下左键prevPt = Point(x, y);           //记录鼠标按下时的位置,作为白色线条的起始点//鼠标左键呈按下状态并移动,绘制出白色线条else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON)){Point pt(x, y);if (prevPt.x < 0)  //如果白线起始点范围溢出,则白线起始点为当前点ptprevPt = pt;line(g_maskImage, prevPt, pt, Scalar::all(255), 2, 8, 0);//画白线line(g_srcImage, prevPt, pt, Scalar::all(255), 2, 8, 0); //画白线prevPt = pt;imshow(WINDOW_NAME1, g_srcImage);}
}
static void ShowHelpText()
{printf("\n\n\t欢迎来到【分水岭算法】示例程序~\n");printf("\n\t请先用鼠标在图片窗口中标记出大致的区域\n");printf("\n\t然后再按键【1】启动分水岭算法\n");printf("\n\t按键操作说明:\n");printf("\t\t\t键盘按键【1】--运行分水岭分割算法\n");printf("\t\t\t键盘按键【2】--恢复原始图\n");printf("\t\t\t键盘按键【ESC】--退出程序\n");
}

运行效果:

《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(五)分水岭算法(watershed algorithm)相关推荐

  1. 《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(一)查找并绘制轮廓

    第8章 图像轮廓与图像分割修复 8.1 查找并绘制轮廓 8.1.1 寻找轮廓:findContours()函数 1.作用:在二值图像中寻找轮廓 2.函数原型: void findcontours(In ...

  2. 《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(四)图像的矩

    8.4 图像的矩 从一幅数字图形中计算出来的矩集,通常描述了该图像形状的全局特征,并提供了大量关于该图像不同类型的几何特性信息,如大小.位置.方向.形状等 (1)一阶矩与形状有关 (2)二阶矩显示曲线 ...

  3. 《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(三)使用多边形将轮廓包围

    8.3 使用多边形将轮廓包围 8.3.1 将轮廓包围的多边形函数 1.返回外部边界:boundingRect()函数 (1)作用:返回指定点集最外面的边界矩形(四个顶点) (2)函数原型:Rect b ...

  4. 《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(二)寻找物体的凸包

    8.2 寻找物体的凸包 8.2.1 概念 1.给定二维平面上的点集,将最外层点连接起来构成的凸多边形. 2.理解物体形状或轮廓的一种比较有用的方法是计算一个物体的凸包,然后计算其凸缺陷(convexi ...

  5. 《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(六)图像修补

    8.6 图像修补 基本思想:   利用已经被破坏区域的边缘,即边缘的颜色和结构,繁殖和混合到损坏的图像中,达到图像修补的目的. 8.6.1 实现图像修补:inpaint()函数 1.作用:   用来从 ...

  6. 原创 OpenCV3编程入门 学习笔记(总)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/qq_36163358/article/ ...

  7. OpenCV3编程入门 学习笔记(总)

    OpenCV3编程入门 学习笔记 2018.12.12-2018.12.29 此博客为在看过毛星云版<OpenCV3编程入门>后所总结的一本笔记,可供复习使用. 文章目录 OpenCV3编 ...

  8. Opencv3编程入门学习笔记(五)之通道分离(split)与合并(merge)

    若要对Opencv中(BGR)颜色通道进行单一处理,那必然会涉及到通道分离(split)与合并(merge).那么本篇博客笔者记录了两个方法的使用方法和案例.案例来源于<Opencv3编程入门学 ...

  9. Opencv3编程入门学习笔记(三)之访问图像像素的三种方法

    访问图像像素的三种方法:指针访问,迭代器访问,动态地址访问.访问最快的为指针访问,以下算法在几毫秒,但指针访问容易造成内存泄漏:其次为迭代器访问:最后为动态地址访问. 以下程序是根据<OpenC ...

最新文章

  1. linux 环境配置 安装jdk
  2. python【力扣LeetCode算法题库】16- 最接近的三数之和
  3. python【蓝桥杯vip练习题库】ADV-69质因数(数论)
  4. 去除字符串标点 + 泛型算法使用
  5. C# 调用SQL的存储过程的接口及实现
  6. VTK:vtkChartMatrix用法实战
  7. miui怎么用第三方图标包_空气能热水器怎么用?控制面板的图标详解来了
  8. Scanner读取记事本文件内容为空的解决办法
  9. php 命令行方式运行时 几种传入参数的方式
  10. 教你从零开始搭建一款前端脚手架工具
  11. 运用基础班知识做一个网页
  12. python中self做前缀_python 创建类和为什么类方法中self形参必不可少?
  13. eclipse 快捷键收藏
  14. 由pthread_create引起的段错误
  15. html5中的h1,HTML5语义 - 产品列表中的h1用法
  16. 用python输出200以内的素数_Python练习题 008:打印101-200之间的所有素数
  17. 运动目标跟踪(十五)--WMIL跟踪
  18. getBoundingClientRect()来获取页面元素的位置”
  19. 海南省月降水量分布数据
  20. Java实现UDP功能

热门文章

  1. 2022-2028年中国数字电视产业投资分析及前景预测报告(全卷)
  2. 2022-2028年中国香精香料行业投资分析及前景预测报告
  3. 【牛腩新闻发布系统】整和后台05
  4. Android Studio中RecycerView依赖库加载问题
  5. LeetCode简单题之两数之和 IV - 输入 BST
  6. LeetCode简单题之至少是其他数字两倍的最大数
  7. k8s核心组件详细介绍教程(配超详细实例演示)
  8. HBM2E Flashbolt--提升人工智能的算力
  9. AlexeyAB DarkNet YOLOv3框架解析与应用实践(三)
  10. [JS] [C] [编程题] 用户喜好