分水岭算法实现(C++、opencv)

1.作用:

通常用于分割图像,主要实现以临近像素间的相似性作为重要的参考依据,从而将在空间位置上相近并且灰度值相近的像素点互相连接起来构成一个封闭的轮廓,封闭性是分水岭算法的一个重要特征。 相对于基于阈值的图像分割,边缘检测等都不会考虑像素在空间关系上的相似性和封闭性这一概念,彼此像素间互相独立,没有统一性。分水岭算法较其他分割方法更具有思想性,更符合人眼对图像的印象。

2.实现:

#include <cmath>
#include <iostream>
#include <memory>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/background_segm.hpp>
#include "opencv2/calib3d/calib3d.hpp"
#include <opencv2/opencv.hpp>
#include <time.h>
#include <thread>
#include <future>
#include <chrono>
#include <vector>
#include "time.h"
#include <string>
#include <fstream>using namespace std;
using namespace cv;Vec3b RandomColor(int value)    //生成随机颜色函数
{value = value % 255;  //生成0~255的随机数RNG rng;int aa = rng.uniform(0, value);int bb = rng.uniform(0, value);int cc = rng.uniform(0, value);return Vec3b(aa, bb, cc);
}int main(int argc, char** argv) {cv::Mat rgb_image = cv::imread("../example/timg.jpg", CV_LOAD_IMAGE_UNCHANGED);cv::Mat rgb_image_blur;GaussianBlur(rgb_image, rgb_image_blur, Size(5, 5), 0, 0);cv::Mat rgb_image_canny;Canny(rgb_image_blur, rgb_image_canny, 10, 120, 3, false);cv::imshow("rgb_roi_binary", rgb_image_canny);vector<vector<Point>>contours;vector<Vec4i>hierarchy;findContours(rgb_image_canny, contours, hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE, Point());Mat imageContours = Mat::zeros(rgb_image.size(), CV_8UC1);  //轮廓 Mat marks(rgb_image.size(), CV_32S);   //Opencv分水岭第二个矩阵参数marks = Scalar::all(0);int index = 0;int compCount = 0;for (; index >= 0; index = hierarchy[index][0], compCount++){//对marks进行标记,对不同区域的轮廓进行编号,相当于设置注水点,有多少轮廓,就有多少注水点drawContours(marks, contours, index, Scalar::all(compCount + 1), 1, 8, hierarchy);drawContours(imageContours, contours, index, Scalar(255), 1, 8, hierarchy);}//我们来看一下传入的矩阵marks里是什么东西Mat marksShows;convertScaleAbs(marks, marksShows);imshow("marksShow", marksShows);imshow("轮廓", imageContours);cv::watershed(rgb_image, marks);Mat afterWatershed;convertScaleAbs(marks, afterWatershed);imshow("After Watershed", afterWatershed);//对每一个区域进行颜色填充Mat PerspectiveImage = Mat::zeros(rgb_image.size(), CV_8UC3);for (int i = 0; i < marks.rows; i++){for (int j = 0; j < marks.cols; j++){int index = marks.at<int>(i, j);if (marks.at<int>(i, j) == -1){PerspectiveImage.at<Vec3b>(i, j) = Vec3b(255, 255, 255);}else{PerspectiveImage.at<Vec3b>(i, j) = RandomColor(index);}}}imshow("After ColorFill", PerspectiveImage);//分割并填充颜色的结果跟原始图像融合Mat wshed;addWeighted(rgb_image, 0.4, PerspectiveImage, 0.6, 0, wshed);imshow("AddWeighted Image", wshed);cv::waitKey(0);return 0;
}

3.效果

4.函数原型

void watershed( InputArray image, InputOutputArray markers );

第一个参数 image,必须是一个8bit 3通道彩色图像矩阵序列。第二个参数 markers,根据Opencv官方文档主要可分为以下几步:

step 1:图像灰度化、滤波、Canny边缘检测、二值化等一系列戏台学操作,保证下一步输入可以正常传入findContours即可。

参数markers它应该包含不同区域的轮廓,每个轮廓有一个自己唯一的编号,轮廓的定位可以cv::findContours(输入时二值化图像或者灰度图)方法,这个是执行分水岭之前的要求。 

step2:查找轮廓,并且把轮廓信息按照不同的编号绘制到watershed的第二个入参merkers上,相当于标记注水点。

算法会根据markers传入的轮廓作为种子(注水点),对图像上其他的像素点根据分水岭算法规则进行判断,并对每个像素点的区域归属进行划定,直到处理完图像上所有像素点。而区域与区域之间的分界处的值被置为“-1”,以做区分。

step3:watershed分水岭运算,绘制分割出来的区域,视觉控还可以使用随机颜色填充,或者跟原始图像融合以下,以得到更好的显示效果。

就是说第二个入参markers必须包含了种子点信息。Opencv官方例程中使用鼠标划线标记,其实就是在定义种子,只不过需要手动操作,而使用findContours可以自动标记种子点。而分水岭方法完成之后并不会直接生成分割后的图像,还需要进一步的显示处理。

5.原理

       将上述三个步骤对应到三维空间中,findContours发现的轮廓值就是分水岭的“ling”,在三维空间中,在该处滴上一滴水,这滴水会滑向最小值平面,轮廓中间就是最小值点,也即注水点。

实际上, 在真实图像中,由于噪声点或者其它干扰因素的存在,使用分水岭算法常常存在过度分割的现象,这是因为很多很小的局部极值点的存在。为了解决过度分割的问题,可以使用基于标记(mark)图像的分水岭算法,就是通过先验知识,来指导分水岭算法,以便获得更好的图像分段效果。通常的mark图像,都是在某个区域定义了一些灰度层级,在这个区域的洪水淹没过程中,水平面都是从定义的高度开始的,这样可以避免一些很小的噪声极值区域的分割。

6.参考

【1】 https://docs.opencv.org/3.4.9/d7/d1b/group__imgproc__misc.html#ga3267243e4d3f95165d55a618c65ac6e1

【2】https://blog.csdn.net/dcrmg/article/details/52498440

【3】https://www.cnblogs.com/mikewolf2002/p/3304118.html

OpenCV 【四】————Watershed Algorithm(图像分割)——分水岭算法的原理及实现相关推荐

  1. 【OpenCV入门学习--python】Image Segmentation with Distance Transform and Watershed Algorithm图像分割

    例子源于OpenCV官网–基于距离变换和分水岭算法的图像分割 (https://docs.opencv.org/4.x/d2/dbd/tutorial_distance_transform.html) ...

  2. OpenCV学习(二十) :分水岭算法:watershed()

    OpenCV学习(二十) :分水岭算法:watershed() 参考博客: OpenCV-分水岭算法 图像处理--分水岭算法 OpenCV学习(7) 分水岭算法(1) Opencv分水岭算法--wat ...

  3. 传统图像分割——分水岭算法(watershed)

    传统图像分割--分水岭算法(watershed) 文章目录 传统图像分割--分水岭算法(watershed) 前言 一.什么是分水岭算法? 二.经典的分水岭求解算法 1.定义 2.算法流程 总结 前言 ...

  4. 【OpenCv】图像分割——分水岭算法

    文章目录 1 原理 2 算法改进 3 API 4 实例 1 原理   分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示 ...

  5. OpenCV与图像处理学习十一——分水岭算法(含代码)

    OpenCV与图像处理学习十一--分水岭算法(含代码) 一.分水岭算法概要 二.分水岭算法步骤 三.代码应用 一.分水岭算法概要 任意的灰度图像可以被看做是地质学表面,高亮度的地方是山峰,低亮度的地方 ...

  6. 图像分割 - 分水岭算法

    目录 1. 介绍 2.  分水岭算法的实现 距离变换 连接连通分量 3. 代码 1. 介绍 图像是由x,y表示的,如果将灰度值也考虑进去的话,那么一幅图像需要一个三维的空间去表示. 这样就可以把x,y ...

  7. 基于边缘的图像分割——分水岭算法(watershed)算法分析(附opencv源码分析)

    最近需要做一个图像分割的程序,查了opencv的源代码,发现opencv里实现的图像分割一共有两个方法,watershed和mean-shift算法.这两个算法的具体实现都在segmentation. ...

  8. 使用OpenCV和C++实现的分水岭算法(Watershed)

    分水岭算法(watershed)是一种比较基本的数学形态学分割算法,其基本思想是将灰度图像转换为梯度图像,将梯度值看作高低起伏的山岭,将局部极小值及其邻域看作一个"集水盆".设想一 ...

  9. OpenCV库中watershed函数(分水岭算法)的详细使用例程

    # 声明:如果有写的不对的地方欢迎指正! 一.分水岭算法 关于分水岭算法的具体原理我就不说了,网上搜一下很多.OpenCV中的watershed函数实现的分水岭算法是基于"标记"的 ...

最新文章

  1. Error in bl_make_text_box(token, drawing_context$gp, drawing_context$yoff_pt) : function ‘Rcpp_preci
  2. PCB第二节知识整理
  3. BZOJ 1444: [Jsoi2009]有趣的游戏 [AC自动机 高斯消元]
  4. 前端学习(799):根据位置返回字符
  5. nginxlua文件服务器权限,通过lua进行nginx的权限控制
  6. #39;boost/iterator/iterator_adaptor.hpp#39; file not found之xcode生成时报错的解决方案
  7. php 备份数据库 Backup Your MySQL Database Using PHP
  8. python、java大作战,python测试dubbo接口
  9. springboot 主键重复导致数据重复_程序员:MySQL处理插入过程中主键或唯一键重复值的解决办法
  10. [置顶] 提高生产力:开源Java工具包Jodd(Java的”瑞士军刀”)
  11. npm命令启动前端项目
  12. 区块链架构1.0、2.0与3.0梳理
  13. 3dmax和VRay灯光
  14. 计算机视觉(ComputerVision, CV)相关领域的网站链接
  15. java:AXIS调用webService接口,返回String类型xml,并用dom4j简单解析xml
  16. 学习缓冲区溢出攻击的前提知识
  17. Java,Scala:JDBCUtil,MySqlUtil,PhoenixJDBC
  18. 标称型数据和数值型数据_2017-12-9 机器学习(4)-标称型和数值型
  19. R | 可视化 | 关联图(Correlogram)
  20. PSIM与matlab联合仿真实战

热门文章

  1. qt工程在linux系统里颜色显示错误_【飞凌嵌入式RK3399开发板试用体验】+QT开发环境搭建测试(二)...
  2. 云米冰箱能控制扫地机器人_用冰箱就能掌控全屋家电?云米21Face 428L确实可以...
  3. 机器学习中的一些概念
  4. python升级知识整理 第五节:文件整理
  5. 多重继承_多重继承和菱形问题
  6. hbase 数据插入指定rowkey_「HBase大爆炸」HBase之常用Shell命令
  7. 疑问:undistortPoints()与remap()畸变校正后,结果相差很大
  8. webservice 暴漏接口_webService接口是什么?
  9. Open3d学习计划—高级篇 3(点云全局配准)
  10. 在CentOS 6.6 x86_64上安装SystemTap/Perf+FlameGraph玩转火焰图实录