Opencv-C++笔记 (19) : 分水岭图像分割

文章目录

  • 一、基于距离变换与分水岭的图像分割
    • 1、图像分割
    • 2、距离和变换与分水岭
      • 距离变换常见算法有两种
      • 分水岭变换常见的算法
    • 3、距离变换API函数接口
    • 4、watershed 分水岭函数API接口
      • 步骤
    • 5、代码

一、基于距离变换与分水岭的图像分割

1、图像分割

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

2、距离和变换与分水岭

距离变换常见算法有两种

1、不断膨胀/ 腐蚀得到
2、基于倒角距离

分水岭变换常见的算法

分水岭法(Meyer)是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。该算法的实现可以模拟成洪水淹没的过程,图像的最低点首先被淹没,然后水逐渐淹没整个山谷。当水位到达一定高度的时候将会溢出,这时在水溢出的地方修建堤坝,重复这个过程直到整个图像上的点全部被淹没,这时所建立的一系列堤坝就成为分开各个盆地的分水岭。分水岭算法对微弱的边缘有着良好的响应,但图像中的噪声会使分水岭算法产生过分割的现象。
————————————————

基于浸泡理论实现

3、距离变换API函数接口

距离变换用于计算图像中每一个非零点像素与其周围最近的零点像素之间的距离,返回的值保存了每一个非零点与最近零点的距离信息;在图像上的体现为图像上越亮的点,代表了离零点的距离越远。

void distanceTransform( 
InputArray src,  
OutputArray dst,
OutputArray labels,
int distanceType,
int maskSize,
int labelType=DIST_LABEL_CCOMP
)

(1)src是单通道的8bit的二值图像(只有0或1)
(2)dst表示的是计算距离的输出图像,可以使单通道32bit浮点数据
(3)distanceType表示的是选取距离的类型,可以设置为
DIST_USER User defined distance
DIST_L1=1 distance = |x1-x2| + |y1-y2
DIST_L2 the simple euclidean distance
DIST_C distance = max(|x1-x2|,|y1-y2|)
DIST_L12 L1-L2 metric: distance =2(sqrt(1+x*x/2) - 1))
DIST_FAIR distance = c^2(|x|/c-log(1+|x|/c)),c = 1.3998
DIST_WELSCH distance = c2/2(1-exp(-(x/c)2)), c= 2.9846
DIST_HUBER distance = |x|<c ? x^2/2 :c(|x|-c/2), c=1.345
(4)maskSize表示的是距离变换的掩膜模板,可以设置为3,5或CV_DIST_MASK_PRECISE,对 CV_DIST_L1 或CV_DIST_C 的情况,参数值被强制设定为 3, 因为3×3 mask 给出5×5 mask 一样的结果,而且速度还更快。
DIST_MASK_3 mask=3
DIST_MASK_5 mask=5
DIST_MASK-PRECISE
(5)labels表示可选输出2维数组;
(6)labelType表示的是输出二维数组的类型,8位或者32位浮点数,图像是单一通道,并且大小与输入图像一致

4、watershed 分水岭函数API接口

void watershed( InputArray image, InputOutputArray markers );

参数说明

(1)参数 image,必须是一个8bit3通道彩色图像矩阵序列。
(2) 输入或输出32位单通道的标记,和图像一样大小。(输入高峰轮廓标记);在执行分水岭函数watershed之前,必须对第二个参数markers进行处理,它应该包含不同区域的轮廓,每个轮廓有一个自己唯一的编号,轮廓的定位可以通过Opencv中findContours方法实现,这个是执行分水岭之前的要求。

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


步骤

1、将白色背景变成黑色-目的是为后面的变换做准备
2、使用filter2D与拉普拉斯算子实现图像对比度提高,sharp(锐化)
3、转为二值图像通过threshold
4、距离变换
5、对距离变换结果进行归一化到[0~1]之间
6、使用阈值,再次二值化,得到标记
7、腐蚀得到每个Peak - erode
8、发现轮廓 – findContours
9、绘制轮廓- drawContours
10、分水岭变换 watershed
11、对每个分割区域着色输出结果
————————————————

5、代码

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>using namespace std;
using namespace cv;int main(int argc, char** argv) {char input_win[] = "input image";char watershed_win[] = "watershed segmentation demo";Mat src = imread("D:/vcprojects/images/cards.png");// Mat src = imread("D:/kuaidi.jpg");if (src.empty()) {printf("could not load image...\n");return -1;}namedWindow(input_win, CV_WINDOW_AUTOSIZE);imshow(input_win, src);// 1. change backgroundfor (int row = 0; row < src.rows; row++) {for (int col = 0; col < src.cols; col++) {if (src.at<Vec3b>(row, col) == Vec3b(255, 255, 255)) {src.at<Vec3b>(row, col)[0] = 0;src.at<Vec3b>(row, col)[1] = 0;src.at<Vec3b>(row, col)[2] = 0;}}}namedWindow("black background", CV_WINDOW_AUTOSIZE);imshow("black background", src);// sharpenMat kernel = (Mat_<float>(3, 3) << 1, 1, 1, 1, -8, 1, 1, 1, 1);Mat imgLaplance;Mat sharpenImg = src;filter2D(src, imgLaplance, CV_32F, kernel, Point(-1, -1), 0, BORDER_DEFAULT);src.convertTo(sharpenImg, CV_32F);Mat resultImg = sharpenImg - imgLaplance;resultImg.convertTo(resultImg, CV_8UC3);imgLaplance.convertTo(imgLaplance, CV_8UC3);imshow("sharpen image", resultImg);// src = resultImg; // copy back// convert to binaryMat binaryImg;cvtColor(src, resultImg, CV_BGR2GRAY);threshold(resultImg, binaryImg, 40, 255, THRESH_BINARY | THRESH_OTSU);imshow("binary image", binaryImg);Mat distImg;distanceTransform(binaryImg, distImg, DIST_L1, 3, 5);normalize(distImg, distImg, 0, 1, NORM_MINMAX);imshow("distance result", distImg);// binary againthreshold(distImg, distImg, .4, 1, THRESH_BINARY);Mat k1 = Mat::ones(13, 13, CV_8UC1);erode(distImg, distImg, k1, Point(-1, -1));imshow("distance binary image", distImg);// markers Mat dist_8u;distImg.convertTo(dist_8u, CV_8U);vector<vector<Point>> contours;findContours(dist_8u, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));// create makersMat markers = Mat::zeros(src.size(), CV_32SC1);for (size_t i = 0; i < contours.size(); i++) {drawContours(markers, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i) + 1), -1);}circle(markers, Point(5, 5), 3, Scalar(255, 255, 255), -1);imshow("my markers", markers*1000);// perform watershedwatershed(src, markers);Mat mark = Mat::zeros(markers.size(), CV_8UC1);markers.convertTo(mark, CV_8UC1);bitwise_not(mark, mark, Mat());imshow("watershed image", mark);// generate random colorvector<Vec3b> colors;for (size_t i = 0; i < contours.size(); i++) {int r = theRNG().uniform(0, 255);int g = theRNG().uniform(0, 255);int b = theRNG().uniform(0, 255);colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));}// fill with color and display final resultMat dst = Mat::zeros(markers.size(), CV_8UC3);for (int row = 0; row < markers.rows; row++) {for (int col = 0; col < markers.cols; col++) {int index = markers.at<int>(row, col);if (index > 0 && index <= static_cast<int>(contours.size())) {dst.at<Vec3b>(row, col) = colors[index - 1];}else {dst.at<Vec3b>(row, col) = Vec3b(0, 0, 0);}}}imshow("Final Result", dst);waitKey(0);return 0;
}

输入原图像和锐化图像
在这里插入图片描述

原图和黑背景图(背景应为黑色)
在这里插入图片描述

threshold转化的二值化图片和距离变换结果图
在这里插入图片描述

距离变换结果图和二值化图像
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/179424.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android问题记录 - Unable to make field private final java.lang.String java.io.File.path accessible(持续更新)

文章目录 前言开发环境问题描述问题分析解决方案补充内容最后 前言 最近一个Flutter项目有新需求&#xff0c;开发时一直是在iOS设备上运行&#xff0c;花了几天做完后运行到Android设备测试&#xff0c;结果项目构建失败了。 开发环境 Flutter: 3.7.11Android Studio: 2022…

MySQL进阶知识:二

目录 视图 基本语法 视图的更新 视图的作用 存储过程 介绍 存储过程基本语法 存储过程的变量 系统变量 用户自定义变量 局部变量 存储过程的判断逻辑 存储过程的参数 存储过程中的流程控制 存储过程中的循环 while的基本语法 repeat的基本语法 loop的基本语法…

金山办公前端二面

1. react 和 vue的区别 还有jquery&#xff1f; &#xff08;1&#xff09; jquery 和 vue、react 的区别&#xff1a; vue 和 react : 数据和视图分离 以数据驱动视图&#xff0c;只关心数据变化 dom 操作被封装&#xff08;数据驱动&#xff09; jquery&#xff1a;依靠 do…

【问题解决!】OSError: [WinError 1455] 页面文件太小,无法完成操作。Error loading “c:\Anaconda3\lib

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 问题描述问题原因二、解决方法 问题描述 在使用pytorch跑深度学习的时候报错OSError: [WinError 1455] 页面文件太小&#xff0c;无法完成操作。Error loading “c…

系列七、事务

一、事务 1.1、概述 事务是数据库操作的基本单元&#xff0c;它是指逻辑上的一组操作&#xff0c;要么都成功&#xff0c;要么都失败。典型场景&#xff1a;转账&#xff0c;例如Jack给Rose转账1000元&#xff0c;转账成功&#xff1a;Jack账户的余额少1000元&#xff0c;Rose…

人脸识别经典网络-MTCNN(含Python源码实现)

人脸检测-mtcnn 本文参加新星计划人工智能赛道&#xff1a;https://bbs.csdn.net/topics/613989052 文章目录 人脸检测-mtcnn1. 人脸检测1.1 人脸检测概述1.2 人脸检测的难点1.3 人脸检测的应用场景 2. mtcnn2.1 mtcnn概述2.2 mtcnn的网络结构2.3 图像金字塔2.4 P-Net2.5 R-Ne…

戴尔科技推出全新96核Precision 7875塔式工作站

工作站行业一直是快节奏且充满惊喜的。在过去25年中,戴尔Precision一直处于行业前沿,帮助创作者、工程师、建筑师、研究人员等将想法变为现实,并对整个世界产生影响。工作站所发挥的作用至关重要,被视为化不可能为可能的必要工具。如今,人工智能(AI)和生成式AI(GenAI)的浪潮正在…

西南科技大学C++程序设计实验二(类与对象一)

C++最大的特点就是面向对象,掌握它的几种基本性质还是好理解的,可以看我C++专栏的期末速成,希望对你们学习C++有帮助。 一、实验目的 1.理解简单类的定义、说明与使用 2.理解类中不同属性数据成员的访问特点 3.理解构造函数、析构函数的作用 重点:掌握类的定义与实现,…

MPPT工作流程及算法和硬件的选择

MPPT算法选择 目前&#xff0c;MPPT算法有开路电压比率(离线)、短路电流比率(离线)、观察调节(在线)、极限追踪控制法(在线)。 在光伏控制系统中&#xff0c;因为日照、温度等条件的变化&#xff0c;光伏电池的输出功率也是在不断变化的&#xff0c;为保证使得光伏电池的输出功…

vue3中的customRef创建一个自定义的 ref对象

customRef 创建一个自定义的 ref&#xff0c;并对其依赖项跟踪和更新触发进行显式控制 小案例: 自定义 ref 实现 debounce <template><div style"font-size: 14px;"><input v-model"text" placeholder"搜索关键字"/><…

渗透测试考核--两层内网 cs windows socks5

这里考核为渗透 这里是网络拓扑图 这里记录一下 两台外网 两台内网 首先拿到C段 nmap进行扫描 外网1 nmap -p 80 172.16.17.2/24 主机存活 一般都是web服务入手 所以我们指定80端口 然后去查找开放的 最后获取到2个ip Nmap scan report for 172.16.17.177 Host is u…

如何高效批量生成条形码?

条形码作为商品、库存和信息管理的基础工具&#xff0c;扮演着至关重要的角色。为了满足用户对于高效、专业、多样化的条形码生成需求&#xff0c;我们推出了一款专业高效的在线条形码生成工具。 网址&#xff1a;https://www.1txm.com/ 多样化条形码支持 易条形支持多种常见…

评价体系如何构建?

本文将针对权重计算的一些常见问题进行说明&#xff1a;如组合赋权法的综合权重值如何计算&#xff1f;多层级权重如何计算&#xff1f;用多种方法计算得到的权重如何合并为综合权重用于之后的分析&#xff1f;常见的不同权重计算方法的搭配方式&#xff1f; 一、九种权重计算…

nodejs之express学习(1)

安装 npm i express使用 // 导入 const express require(express) // 创建应用 const app express() // 创建路由 app.get(/home,(req,res)>{res.end("hello express") }) app.listen(3000,()>{console.log("服务已启动~") })路由的介绍 什么是…

5.27每日一题(判断函数在那个区间上有界:充分条件不是必要条件)

若f(x)在(a , b)上连续&#xff0c;且f(a0)&#xff0c;f&#xff08;b-0&#xff09;存在&#xff08;及函数的左右极限存在&#xff09;>f(x)在(a,b)上有界

神经网络:脑科学中功能MRI成像的应用及其一些相关概念

文章目录 一、MRI成像简介核磁共振成像&#xff08;MRI&#xff09;侵入式成像功能磁共振成像&#xff08;fMRI&#xff09;血氧水平依赖&#xff08;BOLD&#xff09;效应对比基线状态代理指标 二、fMRI具有延迟性及其解决方案原因解决方法 三、fMRI 数据处理1. 数据预处理2. …

【栈和队列(1)(逆波兰表达式)】

文章目录 前言什么是栈(Stack)栈方法栈的模拟实现链表也可以实现栈逆波兰表达式逆波兰表达式在栈中怎么使用 前言 什么是栈(Stack) 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0…

详解Tomcat下载安装以及IDEA配置Tomcat(2023最新)

目录 步骤一&#xff1a;首先确认自己是否已经安装JDK步骤二&#xff1a;下载安装Tomcat步骤三&#xff1a;Tomcat配置环境变量步骤四&#xff1a;验证Tomcat配置是否成功步骤五&#xff1a;为IDEA配置Tomcat 步骤一&#xff1a;首先确认自己是否已经安装JDK jdk各版本通用安装…

6、信息收集(1)

文章目录 一、DNS信息查询1、利用dig工具查询各类DNS的解析。2、使用DNS子域名爆破工具&#xff0c;针对子域名进行爆破&#xff0c;同时解析出对应的IP地址。3、利用多地Ping工具&#xff0c;查看域名真实IP。4、针对部分IP进行信息收集 二、DNS域传输实验原理方法一方法二 三…

javaEE -15( 13000字 JavaScript入门 - 2)

一&#xff1a;JavaScript(WebAPI) JS 分成三个大的部分 ECMAScript: 基础语法部分DOM API: 操作页面结构BOM API: 操作浏览器 WebAPI 就包含了 DOM BOM&#xff0c;这个是 W3C 组织规定的. (和制定 ECMAScript 标准的大佬们不是一伙人). 前面学的 JS 基础语法主要学的是 …