OpenCV中的分水岭算法 (C/C++)
OpenCV中的分水岭算法 (C/C++) 🏞️
分水岭算法 (Watershed Algorithm) 是一种在图像处理和计算机视觉中广泛应用的图像分割方法。它特别适用于分离图像中相互接触或重叠的对象。其基本思想是将灰度图像看作一个地形景观,其中灰度值代表海拔高度。算法模拟从用户定义的标记点(“种子点”)开始向“盆地”注水的过程,当不同盆地的水汇合时,便形成了“分水岭线”,这些线就是对象的边界。
1. 算法原理简介
想象一下,一个地形表面被水淹没,水从代表不同区域的标记(局部最小值)开始上升。当来自不同标记的水相遇时,就在它们之间建立一道屏障(分水岭)。最终,这些屏障就勾勒出分割区域的轮廓。
直接将分水岭算法应用于梯度图像通常会导致过度分割 (over-segmentation),因为噪声和微小的局部梯度变化会产生许多不必要的集水盆。为了解决这个问题,通常采用基于标记 (marker-based) 的分水岭算法。用户(或通过其他算法)预先定义一些标记,这些标记代表了:
- 确定是前景的区域 (Sure Foreground)
- 确定是背景的区域 (Sure Background)
- 其他区域则标记为未知。
算法将仅从这些标记开始淹没,从而大大减少过度分割。
2. OpenCV中的 cv::watershed
OpenCV 提供了 cv::watershed
函数来实现基于标记的分水岭算法。
函数原型:
void cv::watershed(cv::InputArray image, // 输入图像,必须是8位3通道彩色图像 (CV_8UC3)cv::InputOutputArray markers // 输入/输出标记图像。它应该是一个32位单通道整数图像 (CV_32SC1)。// 在输入时,它包含预定义的标记。// 在输出时,它被修改为分割结果,其中分水岭线被标记为 -1。
);
markers
图像详解:
- 在输入
markers
图像时,不同的区域需要用不同的正整数标记:0
:表示未知区域,这些区域将由算法决定其归属。1
:通常用于标记确定的背景区域。>1
(如2
,3
, …)`:用于标记不同的确定前景对象区域。每个对象一个唯一的正整数。
- 在函数执行后,
markers
图像会被修改:- 属于特定标记区域的像素会被赋予该标记的值。
- 分水岭线(边界)上的像素会被标记为
-1
。
3. 典型工作流程与C++实现步骤
使用分水岭算法进行图像分割通常遵循以下步骤:
- 加载图像:读取源图像。确保它是或可以转换为
CV_8UC3
格式。 - 预处理与标记生成:这是最关键的一步。
- 灰度化与二值化:将图像转换为灰度图,然后通常使用阈值法(如
cv::threshold
配合cv::THRESH_OTSU
)得到二值图像,以初步分离前景和背景。 - 去除噪声(确定背景):对二值图像进行形态学开运算 (
cv::morphologyEx
与cv::MORPH_OPEN
) 去除小的噪声点。然后进行形态学膨胀 (cv::dilate
),得到的区域可以认为是确定的背景 (Sure Background)。 - 寻找确定前景:
- 使用距离变换 (
cv::distanceTransform
) 计算二值图像中每个前景像素到最近背景像素的距离。 - 对距离变换的结果进行阈值处理,得到一些可以认为是确定的前景 (Sure Foreground) 的区域。这些区域通常是对象的“核心”。
- 使用距离变换 (
- 标记未知区域:将确定背景和确定前景之外的区域标记为未知。
- 创建
markers
图像:根据上述确定的前景、背景区域,初始化一个CV_32SC1
类型的markers
图像。可以使用cv::connectedComponents
对确定前景区域进行连通组件分析,为每个组件分配一个唯一的标签。
- 灰度化与二值化:将图像转换为灰度图,然后通常使用阈值法(如
- 应用分水岭算法:调用
cv::watershed(image, markers);
。 - 可视化结果:遍历修改后的
markers
图像,将分水岭线(值为-1
的像素)在原图上用醒目的颜色标出。
示例代码:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>int main() {// 1. 加载图像cv::Mat src = cv::imread("your_image_with_touching_objects.png");if (src.empty()) {std::cerr << "Error: Could not load image." << std::endl;return -1;}cv::imshow("Source Image", src);// --- 2. 预处理与标记生成 ---cv::Mat gray, binary;cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);// 使用Otsu二值化cv::threshold(gray, binary, 0, 255, cv::THRESH_BINARY_INV + cv::THRESH_OTSU);cv::imshow("Binary Image", binary);// 去除噪声 (形态学开运算)cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));cv::Mat opening;cv::morphologyEx(binary, opening, cv::MORPH_OPEN, kernel, cv::Point(-1, -1), 2);cv::imshow("Opening (Noise Removal)", opening);// 确定背景区域 (膨胀)cv::Mat sure_bg;cv::dilate(opening, sure_bg, kernel, cv::Point(-1, -1), 3);cv::imshow("Sure Background", sure_bg);// 确定前景区域 (距离变换 + 阈值)cv::Mat dist_transform;cv::distanceTransform(opening, dist_transform, cv::DIST_L2, 5);cv::normalize(dist_transform, dist_transform, 0, 1.0, cv::NORM_MINMAX); // 归一化到0-1cv::imshow("Distance Transform", dist_transform * 255); // 可视化时乘以255cv::Mat sure_fg;double minVal, maxVal;cv::minMaxLoc(dist_transform, &minVal, &maxVal);cv::threshold(dist_transform, sure_fg, maxVal * 0.6, 255, cv::THRESH_BINARY); // 阈值可调sure_fg.convertTo(sure_fg, CV_8U);cv::imshow("Sure Foreground", sure_fg);// 寻找未知区域 (背景 - 前景)cv::Mat unknown;cv::subtract(sure_bg, sure_fg, unknown);cv::imshow("Unknown Region", unknown);// 创建 markers 图像cv::Mat markers;cv::connectedComponents(sure_fg, markers); // 连通组件标记前景// 将背景标记为1 (OpenCV中通常将背景标记为1,前景从2开始)// connectedComponents 会将背景标记为0,所以我们需要调整for (int i = 0; i < markers.rows; i++) {for (int j = 0; j < markers.cols; j++) {markers.at<int>(i, j) = markers.at<int>(i, j) + 1; // 确保前景标记从2开始if (sure_bg.at<uchar>(i, j) == 0) { // 如果是原始二值图中的背景(黑色),则认为是未知区域markers.at<int>(i, j) = 0; // 标记为未知}if (sure_bg.at<uchar>(i, j) == 255 && sure_fg.at<uchar>(i, j) == 0) { // 膨胀后的背景,且不是前景markers.at<int>(i, j) = 1; // 标记为确定背景}}}// 对于之前通过 `subtract` 得到的 `unknown` 区域,如果其像素值为255(表示这部分在sure_bg中但不在sure_fg中),// 且我们想确保它被算法处理(而不是固定为背景1),可以将其在markers中标记为0。// 但通常,将 `sure_bg` 减去 `sure_fg` 得到的区域作为 `unknown` 并在 `markers` 中标记为0更直接。// 此处已通过上一步骤的条件判断(`sure_bg.at<uchar>(i, j) == 0`)来处理了部分未知区域。//更精细的 `markers` 创建:// 1. 背景标记为1// 2. 前景用 `connectedComponents` 标记为 2, 3, ...// 3. `sure_bg` 中是背景但 `sure_fg` 中不是前景的区域,标记为1 (已在循环中完成)// 4. `unknown` 区域 (即 `sure_bg` 和 `sure_fg` 之间的模糊地带) 标记为0 (已在循环中完成)// --- 3. 应用分水岭算法 ---cv::watershed(src, markers); // src必须是CV_8UC3// --- 4. 可视化结果 ---// 将分水岭线 (-1) 标记为红色for (int i = 0; i < markers.rows; i++) {for (int j = 0; j < markers.cols; j++) {if (markers.at<int>(i, j) == -1) {src.at<cv::Vec3b>(i, j) = cv::Vec3b(0, 0, 255); // 红色}}}cv::imshow("Segmented (Watershed lines)", src);cv::waitKey(0);return 0;
}
注意: 上述代码中的参数(如形态学操作的迭代次数、距离变换后的阈值等)需要根据具体图像进行仔细调整。
4. 关键考量与技巧 ⚙️
- 标记的质量至关重要:分水岭算法的成功在很大程度上取决于初始标记的准确性。不好的标记会导致分割不佳。
- 避免过度分割:基于标记的分水岭是避免过度分割的主要手段。如果直接对梯度图像应用,几乎肯定会得到大量无用的小区域。
- 输入图像类型:
cv::watershed
要求输入图像image
是CV_8UC3
(8位3通道彩色图),markers
图像是CV_32SC1
(32位单通道整数图)。 - 参数调整:预处理步骤中的参数(如形态学核的大小、阈值、膨胀/腐蚀次数)需要根据待分割对象的特性进行实验和调整。
- 连通组件分析:
cv::connectedComponents
是生成前景对象独立标记的有效工具。
5. 总结 ✨
OpenCV 中的分水岭算法是一种强大的图像分割工具,尤其擅长分离接触或重叠的对象。其核心在于通过精心设计的预处理步骤生成准确的前景和背景标记,然后利用这些标记引导分割过程。虽然参数调整可能需要一些经验,但一旦掌握,它就能在许多复杂的分割任务中发挥巨大作用。
相关文章:
OpenCV中的分水岭算法 (C/C++)
OpenCV中的分水岭算法 (C/C) 🏞️ 分水岭算法 (Watershed Algorithm) 是一种在图像处理和计算机视觉中广泛应用的图像分割方法。它特别适用于分离图像中相互接触或重叠的对象。其基本思想是将灰度图像看作一个地形景观,其中灰度值代表海拔高度。算法模拟…...
Kafka 客户端连接机制的一个典型陷阱
这是 Kafka 客户端连接机制的一个典型陷阱,你遇到的现象可以通过 Kafka bootstrap server 的连接策略和 broker 的 advertised.listeners 配置来精确解释。 🎯 问题描述简化 ✅ 使用顺序为: 192.168.22.22:9092,192.168.22.23:9092,172.21.16…...
相机--RGB相机
教程 RGB--深度相机--激光雷达 RGB相机 原理: 仅捕获红(R)、绿(G)、蓝(B)三通道的彩色图像,输出2D像素矩阵,无深度信息。 核心作用: 2D视觉任务࿱…...
足球数据全解析:实时数据到高阶数据
一、实时数据 1.基础: 比分 & 时间:最基础也最容易出错,优秀的实时数据源,比分更新时间小于0.5秒 射门/射正:狂射20脚进攻猛?可能是"无效狂轰" 射正3次进2球效率逆天 控球率:…...

[科研实践] VS Code (Copilot) + Overleaf (使用 Overleaf Workshop 插件)
科研圈写文档常用 Latex 环境,尤其是 Overleaf 它自带的 AI 润色工具 Writefull 太难用了。如果能用本地的 CoPilot / Cursor 结合 Overleaf,那肯定超高效! 于是我们找到了 VS Code 里的 Overleaf Workshop 插件。这里已经安装好了࿰…...
人工智能100问☞第36问:什么是BERT?
目录 一、通俗解释 二、专业解析 三、权威参考 BERT是基于Transformer Encoder的双向语言预训练模型,具备强大的语义理解能力,是现代自然语言处理的重要基石。它是一套让机器像人一样“前后一起看”的语言理解技术,它让AI不光“读得快”,还“读得懂”。现在很多搜索引擎…...

从0开始学习R语言--Day12--泊松分布
今天我们来看一个很经典的回归模型:泊松分布。 泊松分布 我们一般会把泊松分布用于预测问题,比如想知道成年人每天接到的骚扰电话次数,医院每天的急诊病人等。但在一些方面,跟我们想的会有出入。例如你不能将其应用在预测下周你的…...

工控机安装lubuntu系统
工控机安装lubuntu系统指南手册 1. 准备 1个8G左右的U盘 下载Rufus: Index of /downloads 下载lubuntu系统镜像: NJU Mirror Downloads – Lubuntu 下载Ventoy工具: Releases ventoy/Ventoy GitHub 下载后,解压&#…...

视频监控汇聚平台EasyCVR安防小知识:如何通过视频融合平台解决信息孤岛问题?
一、项目背景与需求分析 随着数字化技术发展与网络带宽升级,视频技术应用场景不断拓展,视频监控、记录仪等多样化产品构建起庞大体系。但这些独立系统彼此割裂,形成信息孤岛。 在系统集成项目中,视频系统深度融合已成必然趋势…...

在大型中实施访问控制 语言模型
大家读完觉得有帮助记得关注!!! 抽象 在企业设置中,组织数据是隔离的、孤立的 并受到精心设计的访问控制框架的精心保护。 如果 LLM 对 siloed data serve 请求进行微调,用于下游任务, 来自具有不同访问权限…...

Haption在危险、挑战性或受限环境中操作的情况提供了一种创新的遥操作解决方案
Haption Virtuose 6D TAO是一款拥有7个主动自由度的触觉设备,专为虚拟现实环境交互而设计。 它与Virtuose的一系列软件解决方案兼容,可让您直接在CAD软件中使用该设备进行装配仿真,并在3D游戏引擎中使用该设备,从而打造更加逼真的…...
html中使用nginx ssi插入html
1.使用方法 nginx配置: server {listen 80;server_name example.com;location / {root /var/www/html;index index.html;ssi on; # 开启 SSI 功能ssi_types text/html; # 指定哪些类型的文件启用 SSI,默认只有 text/html} }html内容: &l…...

行为型:状态模式
目录 1、核心思想 2、实现方式 2.1 模式结构 2.2 实现案例 3、优缺点分析 4、适用场景 5、注意事项 1、核心思想 目的:将状态相关逻辑封装到独立的类中,消除复杂的条件分支,状态的切换由具体状态类自身管理 举例: 1>…...

优雅草最新实战项目技术Discuz X3.5电子签约插件开发项目实施方案优雅草·卓伊凡
优雅草最新实战项目技术Discuz X3.5电子签约插件开发项目实施方案优雅草卓伊凡 一、项目概述 甲方需求:为现有Discuz X3.5系统集成电子签约功能,对接e签宝API,实现用户发起/签署合同、模板管理、签约记录查询等功能。 总预算:9,3…...
人工智能在智能金融中的创新应用与未来趋势
随着金融科技的快速发展,人工智能(AI)技术正在深刻改变金融服务的各个方面。从风险评估到投资决策,从客户服务到欺诈检测,AI的应用不仅提高了金融服务的效率和准确性,还为用户带来了更加个性化和便捷的体验…...
LeetCode:贪心算法
目录 一、分发饼干 二、摆动序列 三、最大子数组和 四、买卖股票的最佳时机II 五、跳跃游戏 六、跳跃游戏II 七、K次取反后最大化的数组和 八、加油站 九、分发糖果 十、柠檬水找零 十一、根据身高重建队列 十二、用最少数量的箭引爆气球 十三、无重叠区间 十四、…...

基于本地化大模型的智能编程助手全栈实践:从模型部署到IDE深度集成学习心得
近年来,随着ChatGPT、Copilot等AI编程工具的爆发式增长,开发者生产力获得了前所未有的提升。然而,云服务的延迟、隐私顾虑及API调用成本促使我探索一种更自主可控的方案:基于开源大模型构建本地化智能编程助手。本文将分享我构建本…...

实验设计与分析(第6版,Montgomery)第5章析因设计引导5.7节思考题5.8 R语言解题
本文是实验设计与分析(第6版,Montgomery著,傅珏生译) 第5章析因设计引导5.7节思考题5.8 R语言解题。主要涉及方差分析,正态假设检验,残差分析,交互作用图。 (a) dataframe<-data.frame( Lightc(580,568…...

引领机器人交互未来!MANUS数据手套解锁精准手部追踪
MANUS数据手套为机器人技术带来高精度手部追踪,助力实现人与机器的自然交互!近年,越来越多客户希望利用这项技术精准操控机械臂、灵巧手和人形机器人,不断提升设备的智能化水平和交互体验。 MANUS数据手套是高精度人机交互设备&am…...
HarmonyNext使用request.agent.download实现断点下载
filedownlaod(API12) 📚简介 filedownload 这是一款支持大文件断点下载的开源插件,退出应用程序进程杀掉以后或无网络情况下恢复网络后,可以在上次位置继续恢复下载等 版本更新—请查看更新日志!!! 修复已知bug,demo已经更新 Ὅ…...
《重塑认知:Django MVT架构的多维剖析与实践》
MVT,即Model - View - Template,是Django框架独特的架构模式。它看似简单的三个字母,实则蕴含着深刻的设计哲学,如同古老智慧的密码,解开了Web应用开发的复杂谜题。 模型,是MVT架构中的数据核心࿰…...
JS入门——三种输入方式
JS入门——三种输入方式 一、方式一:直接在警告框弹出(window可以省略) <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head><body><script><!-- 方式一直接在警告框弹…...

源的企业级网络安全检测工具Prism X(棱镜X)
Prism X(棱镜X)是由yqcs团队自主研发的开源网络安全检测解决方案,专注于企业级风险自动化识别与漏洞智能探测。该工具采用轻量化架构与跨平台设计,全面兼容Windows、Linux及macOS操作系统,集成资产发现、指纹鉴别、弱口…...

基于FPGA的二叉决策树cart算法verilog实现,训练环节采用MATLAB仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) MATLAB训练结果 上述决策树判决条件: 分类的决策树1 if x21<17191.5 then node 2 elseif x21>17191…...
mac电脑安装nvm
方案一、常规安装 下载安装脚本:在终端中执行以下命令来下载并运行 NVM 的安装脚本3: bash curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.39.5/install.sh | bash配置环境变量:安装完成后,需要配置环境变量。如…...

权限分配不合理如何影响企业运营?
“我们明明只给了她CRM的查看权限,怎么客户数据被删了?” “新员工入职三天了,HR系统权限还没开通,流程完全卡住!” “上个月刚给项目经理配了财务权限,怎么又出乱子了?” 这些对话是否在你的…...

ES分词搜索
ES的使用 前言作者使用的版本作者需求 简介ES简略介绍ik分词器简介 使用es的直接简单使用es的查询 es在java中使用备注说明 前言 作者使用的版本 es: 7.17.27spring-boot-starter-data-elasticsearch: 7.14.2 作者需求 作者接到一个业务需求,我们系统有份数据被…...
深入掌握Node.js HTTP模块:从开始到放弃
文章目录 一、HTTP模块入门:从零搭建第一个服务器1.1 基础概念解析1.2 手把手创建服务器 二、核心功能深入解析2.1 处理不同请求类型2.2 实现文件下载功能 三、常见问题解决方案3.1 跨域问题处理3.2 防止服务崩溃3.3 调试技巧 四、安全最佳实践4.1 请求头安全设置4.…...

【数据库】并发控制
并发控制 在数据库系统,经常需要多个用户同时使用。同一时间并发的事务可达数百个,这就是并发引入的必要性。 常见的并发系统有三种: 串行事务执行(X),每个时刻只有一个事务运行,不能充分利用…...

Ansys Zemax | 手机镜头设计 - 第 2 部分:光机械封装
本文该系列文章将讨论智能手机镜头模组设计的挑战,涵盖了从概念、设计到制造和结构变形的分析。本文是四部分系列的第二部分,介绍了在 Ansys Speos 环境中编辑光学元件以及在整合机械组件后分析系统。案例研究对象是一家全球运营制造商的智能手机镜头系统…...