基于 OpenCV 的图像亮度、对比度与锐度调节

    图像亮度、对比度和锐度是图像质量感知的重要参数,调节这些属性常用于图像增强、图像美化或图像分析的预处理阶段。本文将基于 OpenCV 实现这三项基础图像处理功能,并提供滑动条交互界面与直方图可视化分析,方便调试和理解效果。

亮度调整

    图像亮度调整使得图像整体变亮或变暗,其数学表达式为:dst(x,y)=src(x,y) + beta。图像亮度调整本质上改变了图像像素灰度分布。如亮度加30使得其灰度分布向正方向平移30,反之则向负方向平移30。这里的灰度分布我们称之为图像直方图。我们使用眼底图像作为示例来展示效果:

左:眼底原图                                                          右:图像亮度降低30
上:眼底原图直方图                   下:亮度降低30后直方图

    从直方图上我们可以清晰看出,亮度调整会导致直方图左右平移。我们需要注意的是,当亮度调整到饱和状态,如调整后亮度大于255或者小于0,可能会因为阶段而改变直方图形状。

    如何使用代码实现图像亮度调整,直观代码如下:

for (int row = 0; row < src.rows; ++row)
{uchar* data = src.ptr<uchar>(row);for (int col = 0; col < src.cols * src.channels(); ++col){// 遍历所有点,对每个点执行亮度调整adj_brightness操作int val = data[col] + adj_brightness;if (val < 0) val = 0;if (val > 255) val = 255;data[col] = val;}
}

    以上代码从原理上给出了一个实现逻辑,有利于我们理解具体实现。但该代码效率较低,主要原因包括:

    1)内层for循环的条件语句可能打断流水线优化,流水线优化利用CPU的指令流水线结构实现多个指令并行处理。

    2)可能没有有效利用SIMD指令,CPU通过SIMD指令可以实现多条数据同时处理的效果,也称作向量化。

    由于OpenCV经过了大量的优化,通过调用OpenCV接口,在绝大多数情况下可以获得极佳的性能,因此我们会尽量使用OpenCV提供的接口来构造功能。

   通过cv::Mat提供的接口convertTo可以实现图像亮度的调整,该函数的核心功能包括:

   1)数据类型转换,如从CV_8U转换为CV_32F。

   2)在转换过程中执行dst = src * alpha + beta操作,我们将alpha参数设置为1,beta参数设置为亮度调整参数即可实现亮度的调整。

    为什么要调用convertTo实现亮度调整,而不是直接使用cv::add或者直接mat=mat+offset呢?

    其实以上方法都可以实现亮度调整,这里使用convertTo的主要原因是该函数可以同步提高亮度调整与数据类型转换功能。通过一个函数实现两个功能,可以避免内存的多次拷贝,从而提升运行效率。

    那么,为什么我们需要做数据类型转换呢?主要有两点理由:

    1)我们一般将原始的8位无符号整数(CV_8U)图像转换为32位(CV_32F)浮点图像。在后续图像处理运算中,不可避免会产生浮点数值。如果仍旧使用整形表示则会产生阶段误差,从而降低调整后图像的信噪比。

    2)在某些运算中可能产生负数,CV_8U类型无法有效表示负数,从而产生运算错误。

   以下是亮度调整与类型转换的代码:

 cv::Mat img_src_f;img_src.convertTo(img_src_f, CV_32F, 1., adj_brightness);

对比度调整 

    对比度控制图像的明暗差异,通常用一个比例因子alpha控制像素的扩展或压缩,其数学表达式可写作:dst(x,y) = src(x,y) * alpha。当alpha>1时对比度增强,当alpha<1时对比度降低。

    通过以上方法,我们会发现调整后图像的对比度会显著影响图像亮度。如增强对比度时也同样增强了图像亮度。如下图:

左:眼底原图                                                        右:对比度增加1.5(亮度也同步增强)

 

上:原图直方图                      下:对比度增强后直方图

    从直方图上来看,图像平均亮度确实增加了50%,同时直方图的宽度也有所增加(即动态范围更大)。在现实应用中,我们在增加对比度时更加希望图像平均亮度基本保持不变,那么应该如何处理呢?

改进的对比度调整

    为了保持图像亮度基本不变,我们可以引入一些对称性操作。在上一节内容中,图像对比度的扩张是以0点为原点进行缩放,即dst(x,y)=[src(x,y) - 0] * alpha。如果我们以图像均值为原点进行对比度缩放,则可以确保对比度缩放后的图像平均亮度基本保持不变,其表达式为:dst(x,y)=(src(x,y) -mean)*alpha + mean,这里的mean为图像的统计均值。

    使用该改进后方案,我们的对比度调整有如下效果:

左:眼底原图                                                  右:对比度增加1.5(保持亮度不变)
上:原图直方图             下:对比度增强直方图(亮度保持)

    以上直方图表明,由于对比度增加使得直方图动态范围增加,但确保持了平均亮度。这正是我们在对比度调整时所期望的效果。

    另外,对比度调整参数的取值范围理论上为[0,+\infty ]。实际应用中,我们可能将其限制在[0.5,5]或者[1,5]

锐度调整

    我们之前讨论的图像亮度与图像对比度都是从全局角度来控制图像效果。因此,每一个有效的条件都会改变直方图的基本形态,如导致直方图平移或者缩放等。关于图像锐度的调整主要改变图像边缘清晰程度,这是一个邻域操作。也就是说,每一个像素变换后的值仅与其相邻的像素相关。

   如果我们可以获取图像上每一个点的边缘强度信息,那么我们就可以通过对该位置的强度进行叠加,从而获得边缘更强的图像。因此,一个核心点就是如何获得每个点的边缘强度信息。

    我们这里使用一个反锐化掩膜方式(Unsharp Masking)来获取每个点的边缘强度。公式如下:

 UnsharpMask=Image-Gaussian(Image),通过原图减去高斯模糊图像获取高频信息,作为每个点的边缘强度表征。

    然后再在原图上叠加边缘强度信息,并通过参数控制叠加强度,如:

Sharpness = Image + UnsharpMask * \lambda\lambda为增强系数,其范围为[0,+\infty]

左:眼底原图                                                  右:锐度增强图像
上:原图直方图                      下:锐度增强直方图

    从以上直方图来看,锐度增强基本没有改变直方图的动态范围与均值,这也进一步验证图像锐化操作是一个邻域操作而非全局变换。同时,我们的锐化效果似乎也比较理想。然而仔细观察会发现:图像边缘虽然得到了有效增强,但图像噪声似乎也被增强了。我们需要进一步接近该问题。

改进的锐度调整

    要想抑制锐化增强后的噪声,我们首先需要明白噪声的特点。噪声相对于信号来说比较弱,在一个完美信号上叠加随机噪声有如下效果:

    如上图所示,上半部分是一个理想的阶跃信息和叠加随机噪声的阶跃信号,下图是叠加随机噪声阶跃信号的梯度响应。我们在做图像增强时,期望增强的边缘显然是0坐标附近的阶跃边缘,而在非零附近区域的噪声响应是我们期望忽略掉的。 从梯度响应上来看(红色曲线),阶跃信号有一个很强的响应,噪声信号的响应较弱,这就带给给我们一个自适应边缘增强的启示。

    如果我们将每个点的梯度响应强度作为其增强系数因素,则可以通过选择合适的策略在增强信号同时抑制噪声。具体如下:

    1)将梯度响应归一化到(0,1)区间;

    2)对现有锐度增强方案添加适当的控制因子,以实现增强信号同时抑制噪声,公式如下:Sharpness=UnsharpMask * norm(gradint) * \lambda + Image

    以下为改进后锐化增强结果:  

左:锐化增强                                                  右:改进后的锐化增强(降低了噪声)

    很明显,通过引入梯度响应权值,我们能够获得更加理想的锐化增强图像。

基于OpenCV的图像调整实现 

    下面给出一个完整的基于OpenCV图像调整实现,通过引入Trackbar,我们可以做一个简单的实时调节交互。具体代码如下:

#include "opencv2/highgui/highgui_c.h"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"// 设置滑动条绑定参数变量,该变量必须为整数类型
// 同时分别设置滑块的取值范围,如下:
// 亮度[0,512], 对比度[0,500], 锐度[0,1000]
int bar_brightness = 256;
int bar_contrast = 0;
int bar_sharpness = 0;cv::Mat img_src, img_dst;void applyAdjustment(int, void*)
{// 根据对应滑块的取值范围换算出不同类型图像调整的参数值// 换算后的取值范围为:亮度[-256,256], 对比度[1.,6.],锐度[0.,10.]float adj_brightness = bar_brightness - 256;float adj_contrast = bar_contrast / 100. + 1.;float adj_sharpness = bar_sharpness / 100.;// 通过convertTo函数同步实现了浮点类型转换和亮度调整,使得计算效率更高cv::Mat img_src_f;img_src.convertTo(img_src_f, CV_32F, 1., adj_brightness);// 对比度增强// 注意:以下矩阵img_src_f, img_diff, img_con均公用数据区(浅拷贝)cv::Scalar m = cv::mean(img_src_f);cv::Mat img_diff = img_src_f - m;cv::Mat img_con = img_diff * adj_contrast + m;// 计算自适应增强系数cv::Mat img_green;cv::extractChannel(img_con, img_green, 1);cv::GaussianBlur(img_green, img_green, cv::Size(3, 3), 0);cv::Mat grad_dx, grad_dy, grad_mag, grad_magc3;cv::Sobel(img_green, grad_dx, CV_32F, 1, 0, 3);cv::Sobel(img_green, grad_dy, CV_32F, 0, 1, 3);cv::magnitude(grad_dx, grad_dx, grad_mag);cv::normalize(grad_mag, grad_mag, 0.0, 1.0, cv::NORM_MINMAX);cv::cvtColor(grad_mag, grad_magc3, CV_GRAY2BGR);// 锐化增强cv::Mat img_blur;cv::GaussianBlur(img_con, img_blur, cv::Size(15, 15), 0);cv::Mat img_diff2 = cv::Mat::zeros(img_con.size(), img_con.type());img_diff2 = img_con - img_blur;cv::Mat img_con2= cv::Mat::zeros(img_con.size(), img_con.type());img_con2 = img_con + img_diff2.mul(grad_magc3) * adj_sharpness;// 转换为8位深度图像,用于显示img_con2.convertTo(img_dst, CV_8U);cv::imshow("调整图像", img_dst);
}void briConSharpAdjust()
{img_src = cv::imread("fundus3.png", cv::IMREAD_COLOR);if (img_src.empty()) return;  // 检查是否成功读取图像// 打开图像调整窗口cv::namedWindow("调整图像", cv::WINDOW_NORMAL);// 创建Trackbarcv::createTrackbar("亮度", "调整图像", &bar_brightness, 512, applyAdjustment);cv::createTrackbar("对比度", "调整图像", &bar_contrast, 500, applyAdjustment);cv::createTrackbar("锐度", "调整图像", &bar_sharpness, 1000, applyAdjustment);applyAdjustment(0, 0);cv::waitKey(0);}int main()
{briConSharpAdjust();return 0;
}

结语

    本文深入浅出的讲解了图像调整的基本算法,通过启发式的方式引入一些深入问题研究。同时给出了一个完整的Demo,该Demo基于OpenCV进行UI交互,可以实现基本的图像调整演示。通过对Demo改进可以快速落地一些相关应用。

    另外,我们将所有的图像处理均放在一个函数中,包括图像亮度对比度,图像锐度处理。对于小图像来说,这样做当然没有太多问题。当我们在项目中要求更高的实时性,或者所处理图像的尺寸特别大(如4000*3000),那么我们可以拆分每项处理,从而达到更好的实时性。主要策略包括:

    1)拆分对比度与锐度处理,每次调整仅做一项调整,这也是用户交互的一个自然逻辑;

    2)保存中间图像,如第一次实现锐度增强后,第二次进行对比度调整时基于锐度增强图像即可;

    3)所保存的中间图像一定为浮点类型,尽可能规避整形图像的截断误差累积;

    4)在用户拉动滑块时(如锐度调整),一些公用数据仅需要计算一次即可,如自适应增强系数,差分图等。 

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

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

相关文章

WAF(web应用防火墙)的简单了解

WAF称之为Web应用防火墙&#xff0c;是一种专门设计用于保护web应用程序免受恶意攻击的安全设备&#xff0c;能实时监控过滤和拦截可能对网站造成危害的网络流量&#xff0c;从而避免网络服务器被恶意入侵导致性能异常、数据泄露、服务中断这些问题 (WAF是通过执行一系列针对HT…

跟着AI学习C# Day28

&#x1f4c5; Day 28&#xff1a;C# 源生成器&#xff08;Source Generators&#xff09;与编译时元编程 ✅ 学习目标&#xff1a; 理解什么是 源生成器&#xff08;Source Generator&#xff09;&#xff1b;掌握如何在 编译阶段生成 C# 代码&#xff0c;而不是运行时动态处…

设计模式精讲 Day 4:建造者模式(Builder Pattern)

【设计模式精讲 Day 4】建造者模式&#xff08;Builder Pattern&#xff09; 文章简述&#xff1a; 在软件开发中&#xff0c;对象的构造过程往往复杂且容易出错&#xff0c;尤其是在对象包含多个可选参数或构建步骤时。建造者模式&#xff08;Builder Pattern&#xff09;正是…

如何轻松地将联系人从 iPhone 转移到 iPhone?

也许您升级到最新的 iPhone 型号&#xff0c;或者需要切换到另一部 iPhone 来工作。无论如何&#xff0c;您不能错过您的联系人&#xff0c;这对每个人来说都是最重要的数据。因此&#xff0c;今天我们将分享 5 种如何将联系人从 iPhone 转移到 iPhone 的方法&#xff0c;帮助您…

【51单片机简单的流水灯程序问题】2022-5-24

1.利用单片机的P2口接8个发光二极管。简单的流水灯程序问题-编程语言-CSDN问答 2&#xff0e;发光二极管自由闪烁&#xff08;自己设计两种模式&#xff09;。 3&#xff0e;可通过按键实现暂停、启动以及不用模式的切换。 4. 利用Proteus绘制电路原理图 5. 元件选型&#xff1…

第七节:Vben Admin 最新 v5.0 (vben5) 快速入门 - 用户管理(上)

Vben5 系列文章目录 💻 基础篇 ✅ 第一节:Vben Admin 最新 v5.0 (vben5) 快速入门 ✅ 第二节:Vben Admin 最新 v5.0 (vben5) 快速入门 - Python Flask 后端开发详解(附源码) ✅ 第三节:Vben Admin 最新 v5.0 (vben5) 快速入门 - 对接后端登录接口(上) ✅ 第四节:Vben Ad…

1572. 矩阵对角线元素的和

给你一个正方形矩阵 mat&#xff0c;请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 示例 1&#xff1a; 输入&#xff1a;mat [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;25 解释&#xff1a;对角线的和为&#xf…

供应链场景使用ClickHouse最佳实践

一、概述 ClickHouse是一款由俄罗斯公司Yandex开发的开源列式数据库管理系统&#xff0c;以其高性能的分析查询能力和高压缩比著称。供应链场景中&#xff0c;数据量大且数据类型复杂&#xff0c;需要高效的数据存储和快速的查询性能&#xff0c;ClickHouse在这些方面具有显著…

RA4M2开发IOT(0)----安装e² studio

RA4M2开发IOT.0--安装e studio 概述视频教学样品申请安装 概述 瑞萨电子灵活配置软件包 (FSP) 是用于嵌入式系统设计的高质量增强型软件包&#xff0c;支持瑞萨电子 RA 产品家族 Arm 微控制器&#xff0c;提供用户友好的界面且可灵活扩展&#xff0c;确保从入门级到高性能的整…

【Ambari3.0.0 部署】Step2—免密登陆认证-适用于el8

如果有其他系统部署需求可以参考原文 戳我->所有组件编译教程 戳我->获取部署源代码 一、免密登录认证 &#x1f510; 在多台服务器协同工作的环境中&#xff0c;免密登录&#xff08;SSH 免密认证&#xff09;是一种常见的优化手段&#xff0c;能够极大地提升运维效率&…

网站自助广告投放系统源码 附安装教程(源码下载)

网站自助广告投放系统源码 全自动无人化出售网站广告位 站长必备 源码测试可用&#xff0c;部分加密。感兴趣自行下载 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/91093092 更多资源下载&#xff1a;关注我 图片&#xff1a;

日常运维问题汇总-15

42.SD开票计划产生的预收款在正式开票时未自动清账 统驭科目&#xff08;应收、预收&#xff09;对应的字段状态组中附加科目设置销售订单字段设置为了隐藏导致&#xff0c;更改为“可选输入项” 43.MIGO取消凭证时&#xff0c;用户反馈发现除一行外&#xff0c;其它都不能取消…

【设计模式】6.原型模式

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 原型模式 1. 基础 import copyclass Resume:def __init__(self, name):self.name nameself.sex Noneself.age Noneself.time_area Noneself.compan…

【算法 day08】LeetCode 151.翻转字符串里的单词 |卡码网:55.右旋转字符串

151.翻转字符串里的单词 题目链接 | 文档讲解 |视频讲解 : 链接 1.思路&#xff1a; 1.去除字符串头尾的空格 &#xff0c;使用库函数 trim&#xff08;&#xff09; 2.对字符串进行分割&#xff0c;使用库函数split&#xff08;&#xff09; 3.创建StringBuilder sb&#x…

【WordPress优化插件】WPOPT v2.4.7

WPOPT插件,是由本站开发的一款WordPress优化插件,能对WordPress底层功能进行优化,支持功能开关,系统加速等功能。 2.0版本全新发布,采用vite打包,界面采用Vue3+element-plus制作。无论是外观,还是框架功能,都是空前的强大。 功能更多,更强,是所有WordPress网站都值得…

如何使用 mkimage 工具生成 uImage 文件(RISC-V 环境)

一、mkimage 命令参数详解 在 RISC-V Linux 环境下&#xff0c;使用 U-Boot 的 mkimage 工具生成 uImage 的基本命令格式如下&#xff1a; mkimage -A riscv -O linux -T kernel -C compression -a load_addr -e entry_addr -n "描述信息" -d Image uImage核心参数…

React Native 搭建iOS与Android开发环境

目录 第一步 第二步 一、必须安装的工具 二、具体安装步骤 1. 安装 Homebrew 切换国内源和其他配置&#xff1a; 2. 安装 node 3.下载watchman 4. Ruby 5.CocoaPods 配置环境 6. jdk 7. 配置git 开发环境 第三步——启动项目&#xff08;可以忽略&#xff09; 1…

Vue 简写形式全解析:清晰记忆指南

Vue 简写形式全解析:清晰记忆指南 Vue 中的各种简写形式确实容易混淆,我将它们系统化整理,并提供了多种记忆方法,帮助你轻松掌握! 一、核心简写形式汇总表 完整形式简写形式适用场景记忆技巧v-bind:attribute:attribute动态绑定属性: 像链条,表示"绑定"v-on:…

车载电子电器架构 --- 电子电气架构设计方案

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…

MVCC中read_view的核心参数解析与读操作流程实战

在数据库并发控制领域&#xff0c;MVCC&#xff08;多版本并发控制&#xff09;是实现高性能读写并发的关键技术。其中&#xff0c;read_view作为MVCC判断数据可见性的核心组件&#xff0c;其内部参数的设计直接影响着并发访问的行为。本文将深入解析read_view的三个核心参数&a…