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

图像处理之基于标记的分水岭算法(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;

}

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


总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CTFHUB-密码口令-弱口令

目录 题干介绍 密码字典 找flag过程 尾声 题干介绍 通常认为容易被别人&#xff08;他们有可能对你很了解&#xff09;猜测到或被破解工具破解的口令均为弱口令。 密码字典 下载地址&#xff1a;GitHub - NepoloHebo/Commonly-used-weak-password-dictionary: 常用弱密码字…

川北医学院与爱尔眼科医院集团签署战略合作协议共谋医学发展新篇章

为深入贯彻落实党的二十大精神&#xff0c;统筹校、企、医、政多方资源&#xff0c;服务“健康中国”战略&#xff0c;推动眼健康产业发展&#xff0c;打造国家及区域级眼科医学中心&#xff0c;2024年5月31日&#xff0c;川北医学院与爱尔眼科医院集团在成都举行战略合作协议签…

腾讯云 TDMQ for Apache Pulsar 多地区高可用容灾实践

作者介绍 林宇强 腾讯云高级工程师 专注于消息队列、API网关、微服务、数据同步等 PaaS 领域。有多年的开发和维护经验&#xff0c;目前在腾讯云从事 TDMQ Pulsar 商业化产品方向的研发工作。 导语 本文将从四个维度&#xff0c;深入剖析 Pulsar 在多可用区高可用领域的容…

单实例11.2.0.4迁移到11.2.0.4RAC_使用rman异机恢复

保命法则&#xff1a;先备份再操作&#xff0c;磁盘空间紧张无法备份就让满足&#xff0c;给自己留退路。 场景说明&#xff1a; 1.本文档的环境为同平台、不同版本&#xff08;操作系统版本可以不同&#xff0c;数据库版本相同&#xff09;&#xff0c;源机器和目标机器部分…

QML信号连接到c++的槽函数(五)

文章目录 前言一、QML Signal and Handler Event System二、QML信号连接到c++的槽函数代码实例1. 创建一个QML 工程2. 用C++ 实现一个QML Types3. 代码实例4. 运行结果总结参考资料前言 本文主要介绍,如何将QML 中的信号连接到C++ 中的槽函数 软硬件环境: 硬件:PC 软件:wi…

MDK5.10 安装手册

1.MDK5.10 安装 打开开发板光盘&#xff1a; 6 &#xff0c;软件资料 \ 软件 \MDK5 &#xff0c;双击 mdk_510.exe &#xff0c;进行安装。这里我们 将其安装到 D 盘&#xff0c; MDK5.10 文件夹下&#xff0c;需要设置安装路径&#xff0c;如图 1.1 所示&#xff1a; …

上传图片并显示#Vue3#后端接口数据

上传图片并显示#Vue3#后端接口数据 效果&#xff1a; 上传并显示图片 代码&#xff1a; <!-- 上传图片并显示 --> <template><!-- 上传图片start --><div><el-form><el-form-item><el-uploadmultipleclass"avatar-uploader&quo…

独立游戏开发的 6 个步骤

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

计算机网络⑩ —— Linux系统如何收发网络包

转载于小林coding&#xff1a;https://www.xiaolincoding.com/network/1_base/how_os_deal_network_package.html 1. OSI七层模型 应用层&#xff0c;负责给应用程序提供统一的接口&#xff1b;表示层&#xff0c;负责把数据转换成兼容另一个系统能识别的格式&#xff1b;会话…

【Python】 如何将 datetime 转换为 date?

基本原理 在 Python 中&#xff0c;我们经常需要处理日期和时间。datetime 模块提供了丰富的功能来处理日期和时间。datetime 类型和 date 类型是 datetime 模块中的两个不同的类型。datetime 类型包含了日期和时间的信息&#xff0c;而 date 类型只包含日期信息。 当你需要将…

运筹学_7.博弈论(对策略)

文章目录 引言7.1 博弈论(对策论)的基本概念对策论有三个基本假设对策论的三个要素零和对策二人有限零和对策 7.2 矩阵对策矩阵对策数学模型 7.3 最优纯策略基本定理和性质最优纯策略基本定理最优纯策略基本性质 7.4 混合策略定义和性质混合策略的定义混合策略的性质 7.5 矩阵对…

德国RS SMA100A原装二手sma100a信号发生器6G

罗德与施瓦茨 SMA100A信号发生器&#xff0c;9 kHz 至 3 GHz 或 6 GHz R&S SMA100A 提供信号质量、速度和灵活性。R&S SMA100A 是一款高级模拟发生器&#xff0c;因其出色的特性而树立了标准。 它结合了卓越的信号质量和极高的设置速度。无论是在开发、生产、服务还是维…

GSEA的算法只考虑排序吗

其实这个问题很好回答&#xff0c;只需要运行如下代码&#xff0c;如下的基因列表是顺序是完全相同&#xff0c;并且我们只是做了最基础的变换 library(clusterProfiler) library(org.Hs.eg.db)data(geneList, package"DOSE")ego1 <- gseGO(geneList geneLi…

企业在现代市场中的战略:通过数据可视化提升财务决策

新时代&#xff0c;财务规划团队不仅仅是企业内部的一个部门&#xff0c;更是帮助企业做出明智决策和设定战略目标的中坚力量。在当今瞬息万变的商业环境中&#xff0c;财务专业人士需要具备应对挑战并引导企业走向成功的角色职能。企业领导者时常面临着数据压力&#xff0c;需…

如何快速部署上线项目

CSDN 的小伙伴们&#xff0c;大家好呀&#xff0c;我是苍何。 今天在群里面看到有小伙伴反馈说&#xff0c;面试的时候一被问到简历中的项目还没上线&#xff0c;就不继续问了&#xff0c;感觉挺奇葩的&#xff0c;要知道就校招来说&#xff0c;项目本身大部分都是练手的项目&…

基于 Potree.js 的 3D 点云展示

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 基于 Potree.js 的 3D 点云展示 应用场景 本代码主要应用于需要在 Web 浏览器中展示和交互式浏览 3D 点云数据的场景。点云数据广泛应用于建筑、测绘、地理信息等领域&#xff0c;通过可视化点云&#xff0c;…

Mac下载docker

先安装homebrew Mac下载Homebrew-CSDN博客 然后输入以下命令安装docker brew install --cask --appdir/Applications docker 期间需要输入密码。输入完等待即可

解决uni-app progress控件不显示问题

官方代码&#xff1a; <view class"progress-box"><progress :percent"80" show-info activeColor"red" stroke-width"10" /> </view> 进度条并不在页面中显示&#xff0c;那么我们需要给进度条加上宽高style"…

面试Tip--java创建对象的四种方式

java创建对象一共有四种方式&#xff0c;但是我们在写代码的时候用的new 关键字偏多&#xff0c;像一些接口对接则是序列化创建对象偏多&#xff0c;今天我们来简单介绍下使用场景以及各个方式 1. 使用 new 关键字 这是最常见的创建对象的方式。 public class Example {priva…

vue-cl-service不同环境运行/build配置

概述 在项目开发过程中&#xff0c;同一个项目在开发、测试、灰度、生产可能需要不同的配置信息&#xff0c;所以如果能根据环境的不同来设置参数很重要。 vue项目的vue-cl-service插件也支持不同环境的不同参数配置和打包。 实现 新建不同环境配置文件 vue项目中的配置文件以…