计算机视觉——OpenCV 使用分水岭算法进行图像分割

分水岭算法

在这里插入图片描述

分水岭算法:模拟地理形态的图像分割

分水岭算法通过模拟自然地形来实现图像中物体的分类。在这一过程中,每个像素的灰度值被视作其高度,灰度值较高的像素形成山脊,即分水岭,而二值化阈值则相当于水平面,低于这个水平面的区域会被“淹没”。

测地线距离:地形分析的核心

测地线距离是分水岭算法中的一个关键概念,它代表地球表面两点间的最短路径。这一概念在图论中同样适用,指的是图中两节点间的最短路径,与欧氏距离相比,测地线距离考虑的是实际路径。

分水岭算法的执行步骤

  1. 梯度图像分类:根据灰度值对梯度图像中的像素进行分类,并设定测地距离阈值。
  2. 起始点标记:选择灰度值最小的像素点作为起始点,这些点通常是局部最小值。
  3. 水平面上升:随着阈值的增长,测量周围邻域像素到起始点的测地距离。若小于阈值,则淹没这些像素;若大于阈值,则在这些像素上建立“大坝”。
  4. 大坝设置与区域分区:随着水平面的上升,建立更多的大坝,直到所有区域在分水岭线上相遇,完成图像的分区。

避免过度分割的策略

分水岭算法可能会因噪声或干扰导致图像过度分割,形成过多的小区域。解决这一问题的方法包括:

  • 高斯平滑:通过高斯平滑减少噪声,合并小分区。
  • 基于标记的分水岭算法:选择相对较高的灰度值像素作为起始点,手动标记或使用自动方法如距离变换来确定,从而合并小区域。

OpenCV 实现 Watershed 算法

函数原型:

void watershed( InputArray image, InputOutputArray markers );

参数说明:

  1. image:输入的图像,必须是8位的单通道灰度图像。这个图像的梯度信息将被用来模拟水流向低洼地区流动的过程。

  2. markers:输入输出参数,是一个与原图像大小相同的图像,用于存放分割标记。在函数调用前,这个图像应该被初始化,其中包含了用户定义的分割区域的标记。标记是通过正整数索引来表示的,表示用户已知的前景或背景区域。所有未知区域(即算法需要确定的区域)应该被标记为0。函数执行完成后,每个像素点的标记将被更新为“种子”组件的值,或者在区域边界处被设置为-1。

功能说明:

  • watershed 函数会分析 image 的梯度信息,并使用 markers 中定义的已知区域作为分割的起点(种子点)。
  • 算法将从这些种子点开始,逐步对图像中的其他像素点进行区域归属的判定,直到所有像素点都被标记。
  • 在分割过程中,如果两个相邻的已知区域(种子点)相遇,算法会在它们之间创建一个边界,以避免这些区域合并在一起,从而实现分割。

注意事项:

  • markers 中的标记非常重要,它们直接影响分割的结果。因此,用户需要仔细考虑如何标记已知的前景和背景区域。
  • 分水岭算法可能会导致过度分割,特别是当图像中存在大量噪声时。在实际应用中,可能需要对图像进行预处理,如使用高斯模糊去除小的局部最小值,以减少过度分割的问题。

C++ 代码实现

  1. 读取图像
if(argc < 2){std::cerr << "Errorn";std::cerr << "Provide Input Image:n<program> <inputimage>\n";return -1;
}
cv::Mat original_img = cv::imread(argv[1]);
if(original_img.empty()){std::cerr << "Errorn";std::cerr << "Cannot Read Imagen";return -1;
}

在这里插入图片描述

  1. 使用滤波器从图像中去除噪声
    Mean shift blur 是一种保留图像边缘的滤波算法,经常用于在图像 Watershed 分割之前消除噪声,这可以显著改善 Watershed 分割效果。
cv::Mat shifted;
cv::pyrMeanShiftFiltering(original_img, shifted, 21, 51);
showImg("图像滤波", shifted);

在这里插入图片描述

  1. 将原始图像转换为灰度和二进制图像
cv::Mat gray_img;
cv::cvtColor(original_img, gray_img, cv::COLOR_BGR2GRAY);
showImg("", gray_img);

在这里插入图片描述

cv::Mat bin_img;
cv::threshold(gray_img, bin_img, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
showImg("二值图像", bin_img);

在这里插入图片描述

  1. 查找图像的确定背景

在这一步中,要找到图像中我们确定是背景的区域。

void getBackground(const cv::Mat& source, cv::Mat& dst) {cv::dilate(source, dst, cv::Mat::ones(3, 3, CV_8U)); // 3x3 核
}

在这里插入图片描述

  1. 查找图像的确定前景

为了找到图像的前景,使用距离变换算法

void getForeground(const cv::Mat& source, cv::Mat& dst) {cv::distanceTransform(source, dst, cv::DIST_L2, 3, CV_32F);cv::normalize(dst, dst, 0, 1, cv::NORM_MINMAX);
}

在这里插入图片描述

  1. 查找标记

在应用 Watershed 算法之前,需要标记。为此,我们将使用 OpenCV 提供的 findContour() 函数来在图像中找到标记。

void findMarker(const cv::Mat& sureBg, cv::Mat& markers, std::vector<std::vector<cv::Point>>& contours) {cv::findContours(sureBg, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);// 绘制前景标记for (size_t i = 0, size = contours.size(); i < size; i++)drawContours(markers, contours, static_cast<int>(i), cv::Scalar(static_cast<int>(i)+1), -1);
}

在这里插入图片描述

  1. 应用 Watershed 算法
cv::watershed(original_img, markers);
cv::Mat mark;
markers.convertTo(mark, CV_8U);
cv::bitwise_not(mark, mark); // 将白色转换为黑色,黑色转换为白色
showImg("MARKER", mark);

在这里插入图片描述

完整代码

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>void showImg(const std::string& windowName, const cv::Mat& img){cv::imshow(windowName, img);
}void getBackground(const cv::Mat& source, cv::Mat& dst) {cv::dilate(source, dst, cv::Mat::ones(3, 3, CV_8U)); // 3x3 核
}void getForeground(const cv::Mat& source, cv::Mat& dst) {cv::distanceTransform(source, dst, cv::DIST_L2, 3, CV_32F);cv::normalize(dst, dst, 0, 1, cv::NORM_MINMAX);
}void findMarker(const cv::Mat& sureBg, cv::Mat& markers, std::vector<std::vector<cv::Point>>& contours) {cv::findContours(sureBg, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);// 绘制前景标记for (size_t i = 0, size = contours.size(); i < size; i++)drawContours(markers, contours, static_cast<int>(i), cv::Scalar(static_cast<int>(i)+1), -1);
}void getRandomColor(std::vector<cv::Vec3b>& colors, size_t size) {for (int i = 0; i < size ; ++i) {int b = cv::theRNG().uniform(0, 256);int g = cv::theRNG().uniform(0, 256);int r = cv::theRNG().uniform(0, 256);colors.emplace_back(cv::Vec3b((uchar)b, (uchar)g, (uchar)r));}
}int main(int argc, char** argv) {if(argc < 2){std::cerr << "Errorn";std::cerr << "Provide Input Image:n n";return -1;}cv::Mat original_img = cv::imread(argv[1]);if(original_img.empty()){std::cerr << "Errorn";std::cerr << "Cannot Read Imagen";return -1;}cv::Mat shifted;cv::pyrMeanShiftFiltering(original_img, shifted, 21, 51);showImg("Mean Shifted", shifted);cv::Mat gray_img;cv::cvtColor(original_img, gray_img, cv::COLOR_BGR2GRAY);showImg("GrayIMg", gray_img);cv::Mat bin_img;cv::threshold(gray_img, bin_img, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);showImg("thres img", bin_img);cv::Mat sure_bg;getBackground(bin_img, sure_bg);showImg("Sure Background", sure_bg);cv::Mat sure_fg;getForeground(bin_img, sure_fg);showImg("Sure ForeGround", sure_fg);cv::Mat markers = cv::Mat::zeros(sure_bg.size(), CV_32S);std::vector<std::vector<cv::Point>> contours;findMarker(sure_bg, markers, contours);cv::circle(markers, cv::Point(5, 5), 3, cv::Scalar(255), -1); // 在标记周围绘制圆圈cv::watershed(original_img, markers);cv::Mat mark;markers.convertTo(mark, CV_8U);cv::bitwise_not(mark, mark); // 将白色转换为黑色,黑色转换为白色showImg("MARKER", mark);// 在图像中突出显示标记 /std::vector<cv::Vec3b> colors;getRandomColor(colors, contours.size()); // 创建结果图像cv::Mat dst = cv::Mat::zeros(markers.size(), CV_8UC3);// 用随机颜色填充标记的对象for (int i = 0; i < markers.rows; i++){for (int j = 0; j < markers.cols; j++){int index = markers.at(i,j);if (index > 0 && index <= static_cast<int>(contours.size()))dst.at<cv::Vec3b>(i,j) = colors[index-1];}}showImg("Final Result", dst);cv::waitKey(0);return 0;
}

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

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

相关文章

Windows如何通过wsl2迅速启动Docker desktop的PHP的Hyperf项目容器?

一、安装WSL 什么是WSL&#xff1f; 官网&#xff1a;什么是WSL&#xff1f; Windows Subsystem for Linux (WSL) 是一个在Windows 10和Windows 11上运行原生Linux二进制可执行文件的兼容性层。 换句话说&#xff0c;WSL让你可以在Windows系统上运行Linux环境&#xff0c;而无需…

【Web】2024XYCTF题解(全)

目录 ezhttp ezmd5 warm up ezMake ez?Make εZ?мKε? 我是一个复读机 牢牢记住&#xff0c;逝者为大 ezRCE ezPOP ezSerialize ezClass pharme 连连看到底是连连什么看 ezLFI login give me flag baby_unserialize ezhttp 访问./robots.txt 继…

linux高性能服务器--Ngix内存池简单实现

文章目录 内存模型&#xff1a;流程图内存对齐code 内存模型&#xff1a; 流程图 内存对齐 对齐计算 要分配一个以指定大小对齐的内存&#xff0c;可以使用如下公式&#xff1a; 假设要分配大小为n&#xff0c;对齐方式为x&#xff0c;那么 size(n(x-1)) & (~(x-1))。 举个…

【分布式通信】NPKit,NCCL的Profiling工具

NPKit介绍 NPKit (Networking Profiling Kit) is a profiling framework designed for popular collective communication libraries (CCLs), including Microsoft MSCCL, NVIDIA NCCL and AMD RCCL. It enables users to insert customized profiling events into different C…

26.统一网关Gateway

网关的功能 1.身份认证&#xff0c;权限的校验。 2.服务的路由&#xff0c;负载均衡。用户请求被分配到哪一个微服务。一个微服务可以有多个实例&#xff0c;所以使用负载均衡。 3.请求限流。 springcloud网关实现有两种&#xff1a;gateway, zuul zuul是基于servlet实现的…

随笔Ubuntu上的的一些使用

Ubuntu简易使用 常用指令 cdlsmkdirrf -rm 路径 换源 备份镜像 sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak编辑文件设置 sudo gedit /etc/apt/sources.list清华源 # 阿里源 deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe mul…

数据仓库Data Warehouse

数据仓库Data Warehouse 数仓是一种思想,数仓是一种规范,数仓是一种解决方案 1. 数据处理方式 数据处理大致可以分成两大类: 联机事务处理OLTP(on-line transaction processing)联机分析处理OLAP(On-Line Analytical Processing)1.1. OLTP OLTP的全称是On-line Transa…

YOLO系列改进,自研模块助力涨点

目录 一、原理 二、代码 三、添加到YOLOv5中 一、原理 论文地址:

手机空号过滤,提高工作效率

手机空号过滤在多个方面都具有重要的作用。 首先&#xff0c;它对于短信群发商和电话营销商来说至关重要。通过空号过滤&#xff0c;他们可以确保手机号码数据库的准确性和有效性。由于每天都有大量人群因各种原因更换手机号码&#xff0c;导致每个号段中的空号率和手机状态都…

蓝桥杯如何准备国赛?

目录 一、赛前准备 1、如何刷题&#xff0c;刷哪些题&#xff1f; 2、记录&#xff08;主要看个人习惯&#xff09; CSDN博客 写注释 3、暴力骗分 4、从出题人的角度出发&#xff0c;应该如何骗分 二、赛中注意事项 一、赛前准备 1、如何刷题&#xff0c;刷哪些题&…

【算法刷题 | 贪心算法05】4.27(K次取反后最大化的数组和、加油站)

文章目录 8.K次取反后最大化的数组和8.1题目8.2解法&#xff1a;贪心8.2.1贪心思路8.2.2代码实现 9.加油站9.1题目9.2解法&#xff1a;贪心9.2.1贪心思路9.2.2代码实现 8.K次取反后最大化的数组和 8.1题目 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数…

制作github.io学术个人主页

制作如图的学术个人主页。About me - Xianwen Ling’s Blog 学术个人主页是一个学者展示个人学术成果和研究方向的重要工具。个人主页可以集中展示学者的研究论文、出版物、演讲和发布的项目等学术成果&#xff0c;这样其他人可以更方便地了解和评估学者的研究贡献。个人主页可…

Python | Leetcode Python题解之第60题排列序列

题目&#xff1a; 题解&#xff1a; class Solution:def getPermutation(self, n: int, k: int) -> str:factorial [1]for i in range(1, n):factorial.append(factorial[-1] * i)k - 1ans list()valid [1] * (n 1)for i in range(1, n 1):order k // factorial[n - …

c#数据库: 4.修改学生成绩

将4年级的学生成绩全部修改为100分,。修改前的学生信息表如图所示: using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Linq; using System.Text; using System.Threading.Tasks;namespace StudentUpdate {internal class Program{s…

Web后端开发中对三层架构解耦之控制反转与依赖注入

内聚与耦合 内聚 比如说我们刚刚书写的员工的实现类 在这里我们仅仅书写的是和员工相关的代码 而与员工无关的代码都没有放到这里 说明内聚程度较高 耦合 以后软件开发要高内聚 低耦合 提高程序灵活性 扩拓展性 分析代码 如何解耦 创建容器 提供一个容器 存储东西 存储E…

【图论】图论基础

图论不同地方讲的不太一样&#xff0c;本文仅限作者的理解 定义 图一般由点集 V V V 和边集 E E E 组成。 对于 v ∈ V v\in V v∈V&#xff0c;称 v v v 为该图的一个节点。 对于 e ∈ E e\in E e∈E&#xff0c;一般用二元组 ( u , v ) (u,v) (u,v) 表示 e e e&…

VS2022 .Net6.0 无法打开窗体设计器

拿Vs2022 建了个Demo&#xff0c;运行环境是net6.0-windows&#xff0c;无论双击或是右键都打不开窗体设计器 打开项目目录下的*.csproj.user <?xml version"1.0" encoding"utf-8"?> <Project ToolsVersion"Current" xmlns"htt…

2024年第二十一届 五一杯 (B题)大学生数学建模挑战赛 | 最大流问题,深度学习分析 | 数学建模完整代码解析

DeepVisionary 每日深度学习前沿科技推送&顶会论文&数学建模与科技信息前沿资讯分享&#xff0c;与你一起了解前沿科技知识&#xff01; 本次DeepVisionary带来的是五一杯的详细解读&#xff1a; 完整内容可以在文章末尾全文免费领取&阅读&#xff01; 第一个问题…

[高质量]2024五一数学建模A题保奖思路+代码(后续会更新)

你的点赞收藏是我继续更新的最大动力&#xff0c;可点击文末卡片获取更多资料 你是否在寻找数学建模比赛的突破点&#xff1f; 作为经验丰富的数学建模团队&#xff0c;我们将为你带来2024 年华东杯&#xff08;A题&#xff09;的全面解析包。这个解决方案包不仅包括完整的代…

实习面试算法准备之图论

这里写目录标题 1 基础内容1.1 图的表示1.2图的遍历 2 例题2.1 所有可能的路径2.2 课程表&#xff08;环检测算法&#xff09;2.2.1 环检测算法 DFS版2.2.2 环检测算法 BFS版 2.3 课程表 II &#xff08;拓扑排序算法&#xff09;2.3.1 拓扑排序 DFS版 1 基础内容 图没啥高深的…