《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(五)分水岭算法(watershed algorithm)
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)相关推荐
- 《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(一)查找并绘制轮廓
第8章 图像轮廓与图像分割修复 8.1 查找并绘制轮廓 8.1.1 寻找轮廓:findContours()函数 1.作用:在二值图像中寻找轮廓 2.函数原型: void findcontours(In ...
- 《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(四)图像的矩
8.4 图像的矩 从一幅数字图形中计算出来的矩集,通常描述了该图像形状的全局特征,并提供了大量关于该图像不同类型的几何特性信息,如大小.位置.方向.形状等 (1)一阶矩与形状有关 (2)二阶矩显示曲线 ...
- 《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(三)使用多边形将轮廓包围
8.3 使用多边形将轮廓包围 8.3.1 将轮廓包围的多边形函数 1.返回外部边界:boundingRect()函数 (1)作用:返回指定点集最外面的边界矩形(四个顶点) (2)函数原型:Rect b ...
- 《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(二)寻找物体的凸包
8.2 寻找物体的凸包 8.2.1 概念 1.给定二维平面上的点集,将最外层点连接起来构成的凸多边形. 2.理解物体形状或轮廓的一种比较有用的方法是计算一个物体的凸包,然后计算其凸缺陷(convexi ...
- 《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(六)图像修补
8.6 图像修补 基本思想: 利用已经被破坏区域的边缘,即边缘的颜色和结构,繁殖和混合到损坏的图像中,达到图像修补的目的. 8.6.1 实现图像修补:inpaint()函数 1.作用: 用来从 ...
- 原创 OpenCV3编程入门 学习笔记(总)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/qq_36163358/article/ ...
- OpenCV3编程入门 学习笔记(总)
OpenCV3编程入门 学习笔记 2018.12.12-2018.12.29 此博客为在看过毛星云版<OpenCV3编程入门>后所总结的一本笔记,可供复习使用. 文章目录 OpenCV3编 ...
- Opencv3编程入门学习笔记(五)之通道分离(split)与合并(merge)
若要对Opencv中(BGR)颜色通道进行单一处理,那必然会涉及到通道分离(split)与合并(merge).那么本篇博客笔者记录了两个方法的使用方法和案例.案例来源于<Opencv3编程入门学 ...
- Opencv3编程入门学习笔记(三)之访问图像像素的三种方法
访问图像像素的三种方法:指针访问,迭代器访问,动态地址访问.访问最快的为指针访问,以下算法在几毫秒,但指针访问容易造成内存泄漏:其次为迭代器访问:最后为动态地址访问. 以下程序是根据<OpenC ...
最新文章
- linux 环境配置 安装jdk
- python【力扣LeetCode算法题库】16- 最接近的三数之和
- python【蓝桥杯vip练习题库】ADV-69质因数(数论)
- 去除字符串标点 + 泛型算法使用
- C# 调用SQL的存储过程的接口及实现
- VTK:vtkChartMatrix用法实战
- miui怎么用第三方图标包_空气能热水器怎么用?控制面板的图标详解来了
- Scanner读取记事本文件内容为空的解决办法
- php 命令行方式运行时 几种传入参数的方式
- 教你从零开始搭建一款前端脚手架工具
- 运用基础班知识做一个网页
- python中self做前缀_python 创建类和为什么类方法中self形参必不可少?
- eclipse 快捷键收藏
- 由pthread_create引起的段错误
- html5中的h1,HTML5语义 - 产品列表中的h1用法
- 用python输出200以内的素数_Python练习题 008:打印101-200之间的所有素数
- 运动目标跟踪(十五)--WMIL跟踪
- getBoundingClientRect()来获取页面元素的位置”
- 海南省月降水量分布数据
- Java实现UDP功能
热门文章
- 2022-2028年中国数字电视产业投资分析及前景预测报告(全卷)
- 2022-2028年中国香精香料行业投资分析及前景预测报告
- 【牛腩新闻发布系统】整和后台05
- Android Studio中RecycerView依赖库加载问题
- LeetCode简单题之两数之和 IV - 输入 BST
- LeetCode简单题之至少是其他数字两倍的最大数
- k8s核心组件详细介绍教程(配超详细实例演示)
- HBM2E Flashbolt--提升人工智能的算力
- AlexeyAB DarkNet YOLOv3框架解析与应用实践(三)
- [JS] [C] [编程题] 用户喜好