图像处理之基于标记的分水岭算法(C++)

news2024/9/21 18:53:01

图像处理之基于标记的分水岭算法(C++)


文章目录

  • 图像处理之基于标记的分水岭算法(C++)
  • 前言
  • 一、基于标记点的分水岭算法应用
    • 1.实现步骤:
    • 2.代码实现
  • 总结


前言

传统分水岭算法存在过分割的不足,OpenCV提供了一种改进的分水岭算法,使用一系列预定义标记来引导图像分割的定义方式。使用OpenCV的分水岭算法cv::wathershed,需要输入一个标记图像,图像的像素值为32位有符号正数(CV_32S类型),每个非零像素代表一个标签。**它的原理是对图像中部分像素做标记,表明它的所属区域是已知的。分水岭算法可以根据这个初始标签确定其他像素所属的区域。**传统的基于梯度的分水岭算法和改进后基于标记的分水岭算法示意图如下图所示。
对比图
从上图可以看出,传统基于梯度的分水岭算法由于局部最小值过多造成分割后的分水岭较多。而基于标记的分水岭算法,水淹过程从预先定义好的标记图像(像素)开始,较好的克服了过度分割的不足。本质上讲,基于标记点的改进算法是利用先验知识来帮助分割的一种方法。因此,改进算法的关键在于如何获得准确的标记图像,即如何将前景物体与背景准确的标记出来。


一、基于标记点的分水岭算法应用

1.实现步骤:

  1. 封装分水岭算法
  2. 获取标记图像(在标记图中前景设置为255,背景像素设置为128,未知像素设置为0)
  3. 将原图和标记图输入分水岭算法
  4. 显示结果

2.代码实现

#include <iostream>
#include <opencv.hpp>

class WatershedSegmenter {

private:

	cv::Mat markers;		// 标记图

public:

	/*
	* @param const cv::Mat& markerImage 传入的标记图
	* @brief 将传入的标记图转换成CV_32S的标记图
	*/
	void setMarkers(const cv::Mat& markerImage) {

		// Convert to image of ints
		markerImage.convertTo(markers, CV_32S);
	}


	/*
	* @param const cv::Mat& image 原图
	* @brief 对原图和标记图执行基于标记的分水岭分割
	*/
	cv::Mat process(const cv::Mat& image) {

		// Apply watershed
		cv::watershed(image, markers);

		return markers;
	}

	// Return result in the form of an image
	/*
	* @brief 得到分割结果的标签
	*/
	cv::Mat getSegmentation() {

		cv::Mat tmp;
		// all segment with label higher than 255
		// will be assigned value 255
		markers.convertTo(tmp, CV_8U);

		return tmp;
	}

	// Return watershed in the form of an image以图像的形式返回分水岭
	cv::Mat getWatersheds() {

		cv::Mat tmp;
		//在变换前,把每个像素p转换为255p+255(在conertTo中实现)
		markers.convertTo(tmp, CV_8U, 255, 255);

		return tmp;
	}
};

int main()
{
	// 读取图片
	std::string filepath = "F://work_study//algorithm_demo//watershedSeg.jpg";
	cv::Mat src = cv::imread(filepath);
	if (src.empty())
	{
		return -1;
	}
	imshow("src", src);

	// 高斯滤波平滑图像
	cv::Mat gaussImg;
	// 彩色图转换为灰度图
	cv::cvtColor(src, gaussImg, cv::COLOR_BGR2GRAY);
	cv::GaussianBlur(gaussImg, gaussImg,cv::Size(7,7),1.0);
	imshow("gaussImg", gaussImg);

	// 形态学梯度
	cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
	cv::morphologyEx(gaussImg, gaussImg, cv::MORPH_GRADIENT,kernel);
	imshow("morphologyEx", gaussImg);

	// OTSU大津法阈值
	cv::threshold(gaussImg, gaussImg, 0, 255, cv::THRESH_OTSU | cv::THRESH_BINARY);
	imshow("threshold", gaussImg);

	// 形态学操作,生成确定的背景区域
	cv::Mat kernel_bg = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(7, 7));
	 开运算(先腐蚀后膨胀)消除噪声点
	//cv::morphologyEx(gaussImg, gaussImg, cv::MORPH_OPEN, kernel1,cv::Point(-1,-1),2);
	// 生成确定的背景区域
	cv::Mat backgroundImg;
	cv::dilate(gaussImg, backgroundImg, kernel_bg);
	imshow("backgroundImg", backgroundImg);


	// 距离变换,生成确定的前景区域
	cv::Mat distanceImg,foregroundImg;
	cv::distanceTransform(gaussImg, distanceImg, cv::DIST_L1,5);
	double min, max;
	cv::minMaxLoc(distanceImg, &min, &max);
	cv::threshold(distanceImg, distanceImg, 0.1 * max, 255, cv::THRESH_BINARY);
	cv::normalize(distanceImg, foregroundImg, 0, 255, cv::NORM_MINMAX, CV_8U);
	imshow("foregroundImg", foregroundImg);

	// 去除连通域中的背景部分
	cv::Mat unknownImg;
	cv::subtract(backgroundImg, foregroundImg, unknownImg);	//待定区域,减去前景与背景的重合区域
	imshow("unknownImg", unknownImg);

	cv::Mat makers(gaussImg.size(),CV_8U,cv::Scalar(128));
	for (int i = 0; i < makers.rows; i++)
	{
		for (int j = 0; j < makers.cols; j++)
		{
			if (foregroundImg.at<uchar>(i, j) == 255)
			{
				makers.at<uchar>(i, j) = 255;
			}
			else if (unknownImg.at<uchar>(i, j) == 255)
			{
				makers.at<uchar>(i, j) = 0;
			}
		}
	}
	imshow("makers", makers);

	// 分水岭算法分割图像
	WatershedSegmenter seg;					// 实例化一个分水岭分割对象
	seg.setMarkers(makers);					// 设置算法的标记图像,使得水淹过程从这组预定义好的标记像素开始
	seg.process(src);						// 传入待分割的原图(要求为CV_8UC3)
	cv::Mat resultImg;
	resultImg = seg.getSegmentation();		// 将修改后的标记图makers转换为可显示的8位灰度图并返回分割结果(白色为前景,灰色为背景,0为边缘)

	cv::threshold(resultImg, resultImg, 250, 1, cv::THRESH_BINARY);
	cv::cvtColor(resultImg, resultImg, cv::COLOR_GRAY2BGR);
	resultImg = src.mul(resultImg);
	cv::cvtColor(resultImg, resultImg, cv::COLOR_BGR2GRAY);
	std::cout << resultImg.type() << std::endl;
	for(int i=0;i<resultImg.rows;i++)
		for (int j = 0; j < resultImg.cols; j++)
		{
			if (resultImg.at<uchar>(i, j) == 0)
			{
				resultImg.at<uchar>(i, j) = 255;
			}
		}
	imshow("resultImg", resultImg);
	cv::waitKey(0);
	return 0;

}

过程图
在这里插入图片描述


总结

本文主要介绍了一种基于标记的分水岭算法的应用,关键在于如何巧妙地设计“标记图”,欢迎讨论交流。

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

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

相关文章

图片提取表格要怎么做?7个软件教你快速进行图片识别

图片提取表格要怎么做&#xff1f;7个软件教你快速进行图片识别 要从图片中提取表格&#xff0c;您可以使用以下七款软件来快速进行图片识别和表格提取&#xff1a; 1.一键识别王&#xff1a;这是一款专业的OCR&#xff08;光学字符识别&#xff09;软件&#xff0c;可以帮助…

在通过跨网文件交换时,如何保障科研结构核心研究数据?

当今科研领域&#xff0c;数据如同生命线&#xff0c;支撑着每一个突破性发现的诞生。随着国际合作的加深&#xff0c;跨网文件交换成了常态&#xff0c;但这也为科研机构的核心研究数据安全带来了一系列挑战。想象一下&#xff0c;那些精心搜集和分析的宝贵数据&#xff0c;在…

【Typescript】通过变量的值即可获取变量的类型【typeof 变量】

注意&#xff1a;只要变量的类型准确,则typeof获取变量的类型就不会错 enum Test {a "a0",b "b0" }// 这里的a是一个变量的值 let a: Test.a "a0" as Test.a// 这里的typeof a是一个类型【Test.a】 let x: typeof a Test.a

【C++】开源:RabbitMQ安装与配置使用(SimpleAmqpClient)

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下次更新不迷路&#x1…

Jlink卡死 JFlash keil 盗版JLINK

现象&#xff1a;用Keil打开Jlink配置页&#xff0c;会卡死。 解决方法&#xff1a;用旧版本的Jlink软件&#xff0c;因为淘宝买的很多JLINK下载器是盗版的&#xff0c;不支持新版本的JLINK软件。到https://www.segger.com/downloads/jlink下载旧版本的软件。 如果必须要用新版…

重量and体积,不要在傻傻的花冤枉钱寄快递了!

寄快递时有没有遇到过明明不重却被按体积收费的情况&#xff1f;别急&#xff0c;今天就来给大家揭秘快递收费的奥秘&#xff01; 实际重量和体积重量&#xff01; 首先&#xff0c;我们要明白两个概念&#xff1a;实际重量和体积重量。实际重量就是你看到的物品重量&#xf…

安装vllm的时候卡主:Collecting vllm-nccl-cu12<2.19,>=2.18 (from vllm)

按照vllm的时候卡主&#xff1a; ... Requirement already satisfied: typing-extensions in /home/wangguisen/miniconda3/lib/python3.10/site-packages (from vllm) (4.9.0) Requirement already satisfied: filelock>3.10.4 in /home/wangguisen/miniconda3/lib/python…

推荐二轮电动车仪表盘蓝牙主芯片方案-HS6621CGC

随着国内二轮电动车的火热开启&#xff0c;电动车的智能化程度越来越高&#xff1b;电动车的智能操控需求也越来越高&#xff0c;现在介绍蓝牙控制面板的一些功能&#xff1b;例如&#xff1a;定位&#xff08;GNSS&#xff09;&#xff0c;设防&#xff0c;实时上报数据&#…

ctfshow web入门 web306--web310源码审计

web306 这和之前的完全不一样了 <?php #error_reporting(0); session_start(); require service.php;$username$_POST[userid]; $userpwd$_POST[userpwd]; $servicenew service();$user$service->login($username,$userpwd); if($user){setcookie(user,base64_encode(…

JAVA中的代理:代理的作用+静态代理的实现+动态代理的实现

JAVA中的代理&#xff1a;代理的作用静态代理的实现动态代理的实现 一、代理的作用二、静态代理实现方式2.1 实现原理2.2 示例 三、动态代理 一、代理的作用 代理是一种设计模式 主要目的&#xff1a;提供了对目标对象另外的访问方式 代理的好处&#xff1a; 目标对象可以间…

告别低效率||智能BI财务分析软件

在当今信息爆炸的时代&#xff0c;财务数据作为企业运营的核心&#xff0c;其处理和分析的效率直接关系到企业的决策速度和市场竞争力。奥威BI软件凭借其卓越的性能和智能化的分析功能&#xff0c;为企业提供了一套高效、准确的财务分析解决方案。 奥威BI软件在财务分析中的优…

从0开始回顾ElasticSearch

1 elasticsearch概述 1.1 elasticsearch简介 官网: https://www.elastic.co/ ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎&#xff0c;基于RESTful web接口。Elasticsearch是用Java开发的&#xff0c;并作为Apache许可条款下的…

angr使用学习(持续更新)

首先我是直接在kali中安装的&#xff0c;也是边练边学的。 嗯&#xff0c;要在纯净python环境&#xff0c;所以是在 virtualenv 虚拟环境里&#xff0c;也不是特别会用这个&#xff0c;按照教程一步步做的 source venv/bin/activate 进入了对应环境 退出是 deactivate en,ipy…

重生之 SpringBoot3 入门保姆级学习(06、属性绑定)

重生之 SpringBoot3 入门保姆级学习&#xff08;06、属性绑定&#xff09; 2.3.1 使用 ConfigurationProperties2.3.2 使用 EnableConfigurationProperties 2.3.1 使用 ConfigurationProperties application.properties 文件书写相关配置 pig.id1 pig.name王萍 pig.age21方法一…

Sping源码(九)—— Bean的初始化(非懒加载)— ConversionService

序言 经过前面一系列的加载、解析等准备工作&#xff0c;此刻refresh方法的执行已经来到了尾声&#xff0c;接下来我们用几篇文章着重的介绍一下Bean的初始化 代码 着重看refresh()主流程中的finishBeanFactoryInitialization()方法。 finishBeanFactoryInitialization 方法…

python深入探索斐波那契数列:代码示例与不满足的外围条件

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、斐波那契数列的初步实现 二、外围条件的不满足情况 总结 一、斐波那契数列的初步实现 …

二叉树——堆的实现

一.前言 前面我们讲解了二叉树的概念以及二叉树的存储结构&#xff1a;https://blog.csdn.net/yiqingaa/article/details/139224974?spm1001.2014.3001.5502 今天我们主要讲讲二叉树的存储结构&#xff0c;以及堆的实现。 二.正文 1.二叉树的顺序结构及实现 1.1二叉树的顺序…

五款屏幕监控软件测评,哪一款屏幕监控软件适合企业

市场上有多款屏幕监控软件&#xff0c;其中一些因其实用的功能、良好的用户体验和广泛的用户基础而获得了较高的市场占有率和口碑。以下几款是较为受欢迎且功能全面的屏幕监控软件&#xff1a; 1、安企神软件&#xff1a; 领取7天试用版&#xff1a;https://work.weixin.qq.co…

【NumPy】关于numpy.sort()函数,看这一篇文章就够了

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

一致 VS 正确

“代码命名时&#xff0c;一致大于正确” 本文适合以下小伙伴阅读&#xff1a; 经常会有疑惑&#xff1a;这谁的单词拼错了&#xff0c;我之后要将错就错吗&#xff1f;时常感觉到&#xff1a;项目中的命名好乱啊&#xff0c;明明是一个东西怎么一堆不同的名字 好了&#xf…