【图像处理OpenCV(C++版)】——5.5 图像平滑之双边滤波

news2024/11/16 12:00:58

前言

😊😊😊欢迎来到本博客😊😊😊

🌟🌟🌟 本专栏主要结合OpenCV和C++来实现一些基本的图像处理算法并详细解释各参数含义,适用于平时学习、工作快速查询等,随时更新。

😊😊😊 具体食用方式:可以点击本专栏【OpenCV快速查找(更新中)】–>搜索你要查询的算子名称或相关知识点,或者通过这篇博客👉通俗易懂OpenCV(C++版)详细教程——OpenCV函数快速查找(不断更新中)]查阅你想知道的知识,即可食用。

🎁🎁🎁支持:如果觉得博主的文章还不错或者您用得到的话,可以悄悄关注一下博主哈,如果三连收藏支持就更好啦!这就是给予我最大的支持!😙😙😙


文章目录

    • 学习目标
    • 一、双边滤波原理
      • 1.1 原理
      • 1.2 作用
    • 二、C++实现
      • 2.1 原理实现
      • 2.2 OpenCV函数
    • 三、 总结

学习目标

  • 了解双边滤波含义及原理
  • C++实现双边滤波案例

  每一张图像都可能包含某种程度的噪声,噪声可以理解为由一种或者多种原因造成的灰度值的随机变化。
  在大多数情况下,通过平滑技术(也常称为滤波技术)进行抑制或者去除,其中具备保持边缘(Edge Preserving)作用的平滑技术得到了更多的关注。
  常用的平滑处理算法包括基于二维离散卷积高斯平滑、均值平滑,基于统计学方法的中值平滑,具备保持边缘作用的平滑算法的双边滤波、导向滤波等。

  下面几节将详细关于具备保持边缘作用得相关方法,首先介绍双边滤波技术原理、常见应用及实现。


一、双边滤波原理

1.1 原理

  均值平滑和高斯平滑本质上是计算每个位置的邻域加权和作为该位置的输出,只是这种运算可以用卷积实现,加权系数模板是通过卷积核逆时针翻转180°得到的。

  双边滤波(Bilateral filter)是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折中滤波方法,根据每个位置的邻域,对该位置构建不同的权重模板,详细过程如下:

  (1) 首先构建空间距离权重模板,与构建高斯卷积核的过程类似。

  其中wd表示邻域内某点q(k,l)与中心点p(i, j)的欧氏距离。σd为高斯函数的标准差。使用该公式生成的滤波器模板和高斯滤波器使用的模板是没有区别的,每个位置的空间距离权重模板是相同的

  (2) 然后,构建相似性权重模板,是通过(r,c)处的值与其邻域值的差值的指数衡量的。

  其中,f(i,j) 表示图像在点(i,j)处的像素值;f(k,l) 为模板窗口中心坐标点的像素值。σr为高斯函数䣌标准差,显然每个位置的相似性权重模板是不一样的

  (3) 最后,将上述两个模板相乘,然后进行归一化,便可得到该位置的权重模板。

  滤波后的图像的像素值为:

  将所得到的权重模板和该位置邻域的对应位置相乘,然后求和就得到该位置的输出值,和卷积运算的第二步操作类似

1.2 作用

  中值滤波、高斯滤波、维纳滤波等滤波方法容易模糊图片的边缘细节,对高频细节的保护效果并不明显。相较而言,双边滤波器可以很好的在降噪的同时保护边缘。但是,双边滤波的卷积核是非线性的,因此计算复杂度高。


二、C++实现

2.1 原理实现

  首先,通过定义函数getClosenessWeight实现空间距离权重模板,代码如下:

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include <cmath>
#include <opencv2/imgproc.hpp>
using namespace std;
using namespace cv;

cv::Mat getClosenessWeight(double Sigma_g,Size size) {
	//获取模板大小
	int H = size.height;
	int W = size.width;
	
	//获取模板中心点
	int H_center = (H - 1) / 2;
	int W_center = (W - 1) / 2;

	//设置 空间距离权重模板
	Mat ClosenessWeight = Mat::zeros(size, CV_64FC1);
	for (int r = 0; r < H; r++){
		for (int c = 0; c <W; c++){
			double norm2 = pow(double(r- HH_center),2.0)+ pow(double(c - W_center), 2.0);
			double Sigma_g2 =2 * pow(Sigma_g, 2.0);
			
			//模板赋值
			ClosenessWeight.at<double>(r, c) = exp(-norm2 / Sigma_g2);
	}
		}
			return ClosenessWeight;
}

  通过定义函数bfltGray实现图像的双边滤波:
    其中参数image代表图像矩阵且灰度值,范围是[0,1];
    参HW分别代表权重模板的高和宽且均为奇数
    sigma_g代表空间距离,权重模板的标准差;
    sigma_d代表相似性权重模板的标准差,1效果会比较好,返回值是浮点型矩阵。

  具体代码如下:

Mat bfltGray(const Mat& image, Size winSize, float sigma_g, float sigma_d){
	int winH = winSize.height;
	int winW = winSize.width;

	//平滑窗口得高、宽为奇数
	CV_Assert(winH > 0 && winW > 0);
	CV_Assert(winH%2==1&& winW%2==1);

	if (winH==1 && winW==1){
		return image;
	}
	
	//中心点
	int half_winW = (winW - 1) / 2;
	int half_winH = (winH - 1) / 2;
	
	//空间距离得权重因子
	Mat closenessWeight = getClosenessWeight(sigma_g, winSize);
	
	//图像得宽高
	int rows = image.rows;
	int cols = image.cols;

	//双边滤波后得输出图
	Mat blfImage =Mat::zeros(image.size(), CV_32FC1);
	
	//对每个像素得领域进行核卷积
	for (int r = 0; r < rows; r++){
		for (int c = 0; c < cols; c++){
			double pixel = image.at<double>(r, c);
			//判断边界
			int rTop= (r - half_winH) < 0 ? 0 : r - half_winH;
			int rBottom = (r + half_winH) > rows - 1 ? rows - 1 : r + half_winH;
			int cLeft = (c - half_winW) <0 ? 0 : c - half_winW;
			int cRight = (c + half_winW) > cols - 1 ? cols - 1 :  c + half_winW;
			
			//核作用区域
			Mat region = image(Rect(Point(cLeft, rTop),Point(cRight+1, rBottom+1))).clone();
			
			//相似性权重模板
			Mat similaritWeight;
			pow(region-pixel,2.0,similaritWeight);
			exp(-0.5 * similaritWeight / pow(sigma_d, 2), similaritWeight);
			similaritWeight /= pow(sigma_d, 2);

			//空间距离权重
			Rect regionRect = Rect(Point(cLeft - c + half_winW,rTop - r + half_winH),Point(cRight - c + half_winW,rBottom - r + half_winH + 1));

			Mat closenessWeightTemp = closenessWeight(regionRect).clone();

			//两个权重模板点乘并归一化
			Mat weightTemp =closenessWeightTemp.mul(similaritWeight);

			weightTemp = weightTemp /sum(weightTemp)[0];

			//权重模板与当前领域对应位置相乘,求和
			Mat result = weightTemp.mul(region);
			blfImage.at<double>(r, c)= sum(result)[0];
	}
		}
	return blfImage;
}

  使用bfltGray实现图像的双滤滤波,需要注意bfltGray返回的是灰度值在范围[0,1]之间的浮点型图像矩阵,如果使用函数imwrite直接保存的话,则显示为一张黑色的图片,所以要先乘以255并转换为8位图进行保存。主函数如下:

int main() {
	//输入图像
	Mat I = imread("D:/VSCodeFile/OpenCV_CSDN/image/logo_gray.jpeg");

	if (!I.data)
	{
		return -1;
	}

	//灰度值归一化
	Mat FI;
	I.convertTo(FI, CV_64FC1, 1.0 / 255, 0);

	//双边滤波
	Mat blfI = bflfGray(FI,Size(7,7),19,0.5);

	//显示原图与结果图
	imshow("原图",I);
	imshow("双边滤波", blfI);

	//若保存为8位图,则需要乘255,并转换为CV_8U
	blfI.convertTo(blfI, CV_8U,  255, 0);
	imshow("blf", blfI);
	waitKey(0);
	return 0;
}

2.2 OpenCV函数

  在OpenCV中通过定义函数bilateralFilter实现了双边滤波的功能。

cv::bilateralFilter(InputArray src,
					OutputArray dst,
					int d,
					double sigmaColor,
					double sigmaSpace,
					int borderType = BORDER_DEFAULT 
)
参数解释
src输入矩阵,Mat类型,8位或者浮点型单通道、三通道的图像
dst输出矩阵,其大小与数据类型和src一致
d表示在过滤过程中每个像素邻域的直径。如果这个值设其为非正数,那么会从第五个参数sigmaSpace来计算出它来,在使用过程中类似于模糊力度。
sigmaColor颜色空间滤波器的sigma值。这个参数的值越大,就表明该像素邻域内有更宽广的颜色会被混合到一起,产生较大的半相等颜色区域。类似模糊范围的意思,范围越大看着越模糊
sigmaSpace坐标空间中滤波器的sigma值,坐标空间的标注方差。数值越大,意味着越远的像素会相互影响,从而使更大的区域足够相似的颜色获取相同的颜色。当d>0,d指定了邻域大小且与sigmaSpace无关。否则,d正比于sigmaSpace。值越大,图像的过渡效果越好
borderType推断图像边缘像素的边界模式,默认

三、 总结

  最后,长话短说,大家看完就好好动手实践一下,切记不能三分钟热度、三天打鱼,两天晒网。OpenCV是学习图像处理理论知识比较好的一个途径,大家也可以自己尝试写写博客,来记录大家平时学习的进度,可以和网上众多学者一起交流、探讨,有什么问题希望大家可以积极评论交流,我也会及时更新,来督促自己学习进度。希望大家觉得不错的可以点赞、关注、收藏。


🚶🚶🚶 今天的文章就到这里啦~
喜欢的话,点赞👍、收藏⭐️、关注💟哦 ~

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

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

相关文章

第三十六天 Java基础学习(三十)

一、Spring MVC 组件是将处理某类问题的代码进行封装的整体模块。一个大的问题可以拆分为不同的小问题&#xff0c;解决每个小问题的代码封装可以称之为组件&#xff0c;但是组件又是无法独立运行的&#xff0c;必须结合其他组件一起才能最终解决问题。就好比汽车&#xff0c;…

同步任务和异步任务的执行过程

同步任务和异步任务的执行过程 1、执行过程描述2、EventLoop的概念 1、执行过程描述 同步任务 是由JS主线程按次序执行异步任务委托给宿主环境执行已完成的异步任务对应的回调函数&#xff0c;会被加入到任务队列中等待执行JS的主线程的执行栈被清空后&#xff0c;会读取任务队…

Java026——System 类和Scanner 类

一、System 类 1.1、System 类提供的常用方法 方法 功能描述 ----------------------------------------------------------------------------------------------------------------------- currentTimeMillis() 返回当前计算机时间 和 格林威治时间&#xff…

mysql查询练习

1.创建表 CREATE TABLE worker( 部门号 int(11) NOT NULL, 职工号 int(11) NOT NULL, 工作时间 date NOT NULL,工资 float(8,2) NOT NULL,政治面貌 varchar(10) NOT NULL DEFAULT 群众 , 姓名 varchar(20) NOT NULL,出生日期 date NOT NULL,PRIMARY KEY (职工号))ENGINE…

4.3Java EE——一对多查询

用户与订单关联关系图​​​​​​​ 与一对一的关联关系相比&#xff0c;接触更多的关联关系是一对多&#xff08;或多对一&#xff09;。例如一个用户可以有多个订单&#xff0c;多个订单也可以归一个用户所有。用户和订单的关联关系如图。 一、<collection>元素 在MyB…

前端技术学习第九讲:VUE基础语法---VUE常用指令

VUE常用指令 在VUE学习中&#xff0c;通常使用相关指令使VUE对象中的内容与网页进行挂载绑定&#xff0c;是我们的数据与视图之间产生关联&#xff0c;完成渐进式动态效果。VUE指令都会以“v-”开头。 指令名描述v-text将文本内容挂载到页面元素中v-html将html代码展示到页面…

更开放、更高性能、更具规模,闪马智能布局AGI时代

7月6日&#xff0c;2023世界人工智能大会&#xff08;WAIC 2023&#xff09;在上海盛大开幕。本届大会以“智联世界 生成未来”为主题&#xff0c;聚焦通用人工智能发展&#xff0c;共话产业新未来。 8日上午&#xff0c;由上海闪马智能科技有限公司&#xff08;下称“闪马智能…

ZGC垃圾收集器(-XX:+UseZGC)

ZGC在jdk11只支持Linux版本&#xff0c;4TB的内存&#xff0c;STW时间控制在10ms内&#xff1b;jdk16已经支持16TB的内存&#xff0c;STW时间不超过1ms&#xff0c;下面主要针对jdk11版本的详解 一、堆内存结构 ZGC堆内存分为三种类型的页面即小页面&#xff08;空间大小2M&a…

CYCLO (L-ALA-L-ALA),5845-61-4,环(丙氨酰-丙氨酰),氨基酸中间体

资料编辑|陕西新研博美生物科技有限公司小编MISSwu​ 【产品描述】 cyclo(Ala-Ala)氨基酸中间体&#xff0c;主要由丙氨酰组成 【中文名称】环(丙氨酰-丙氨酰) 【英文名称】 cyclo(Ala-Ala)&#xff0c;CYCLO (L-ALA-L-ALA) 【结 构 式】 【CAS】5845-61-4 【分子式】C6H10N2O…

C#制作打包安装程序,安装程序类使用

这里写目录标题 安装Microsoft Visual Studio Installer Projects创建安装项目设置安装程序文件设置程序桌面图标给程序设置安装程序名称安装程序类怎么使用Installer1.cs自定义安装步骤自定义设置安装程序路径&#xff0c;让用户安装时不能选择安装路径生成安装包 安装Microso…

实例011 在状态栏中显示检查框

实例说明 在设计程序界面时&#xff0c;为了规范界面&#xff0c;可以将一些控件放置在状态栏中&#xff0c;这样既能起到控制程序的作用&#xff0c;又能使界面和谐、美观。运行程序&#xff0c;在窗体的状态栏中加入了显示时间检查框。效果如图1.11所示。 技术要点 在状态…

对Linux系统对Spark开发环境配置

单机版本 上传对应文件&#xff0c;解压文件&#xff0c;并查看 unzip scala-2.12.12.zip tar -xzf spark-3.2.0-bin-hadoop2.7.tgz 移动scala及spark安装包到指定目录下 在opt目录下移动该文件到/usr目录下 mv scala-2.12.12 /usr/scala/ mv spark-3.2.0-bin-hadoop2.7 /…

Django_使用redis缓存数据

目录 一、配置redis 二、缓存Django的默认session 三、使用django的缓存机制缓存数据 四、自定义缓存数据 源码等资料获取方法 一、配置redis 在settings中添加配置参数 # Django的缓存配置 CACHES {"default": {"BACKEND": "django_redis.ca…

【企业微信多选的星期数据生成如“周一、周三至周六“】

目标效果如下图 实现这个过程首先是要找到逻辑&#xff0c;这个看似简单的操作却属实让我想了很久。 首先要了解需求 根据拿到的数据得到生成符合要求的字符串。中间有连续的星期大于三天的&#xff0c;开始和结束星期中间要有"至"。 分析逻辑 一、判断开始日期和…

RabbitMQ系列(19)--实现在RabbitMQ宕机的情况下对消息进行处理

前言&#xff1a;在生产环境中由于一些不明原因&#xff0c;导致RabbitMQ重启的情况下&#xff0c;在RabbitMQ重启期间生产者投递消息失败&#xff0c;生产者发送的消息会丢失&#xff0c;那这时候就需要去想在极端的情况下&#xff0c;RabbitMQ集群不可用的时候&#xff0c;如…

创造一款安卓自定义控件_裁剪原理介绍

1、新增功能&#xff0c;旋转&#xff1a; 效果如图&#xff0c;点击旋转&#xff0c;可以将控件画面本身进行90度倍数的旋转&#xff0c;并进行宽高比例适配&#xff0c;旋转之后裁剪依然正常。 功能实现原理&#xff1a; 1、通过调用view的setRotation功能进行以View为中心…

Stable Diffusion - 超分辨率插件 StableSR v2 (768x768) 配置与使用

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/131582734 论文&#xff1a;Exploiting Diffusion Prior for Real-World Image Super-Resolution StableSR 算法提出了一种新颖的方法&#xff0…

【JAVA】JAVA与C++的区别与联系

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️初识JAVA】 文章目录 前言两方介绍CJAVA 不同|Java不支持指针、模板、指针重载、联合等||支持析构函数||条件编译和包含||螺纹支架||默认参数||转到语句||多重继承||异常处理||方法重载和操作符重载|…

对表中的数据操作

目录标题 创建一个工作者的表 &#xff0c;对表中数据进行相关操作worker表要求表中的数据内容 对数据的操作1.显示所有职工的基本信息2.查询所有职工所属部门的部门号&#xff0c;不显示重复的部门号3.求出所有职工的人数4.列出最高工和最低工资5.列出职工的平均工资和总工资6…

卷积神经网络CNN进阶与搭建

目录 Pooling&#xff08;池化&#xff09;ReluResNetGradient VanishingFeature scalingImage NormalizationBatch Normalization Pooling&#xff08;池化&#xff09; 在降采样(Subsampling)中起作用&#xff0c;在不改变feature map的基础上&#xff0c;在卷积出来的基础上…