PotatoPie 4.0 实验教程(23) —— FPGA实现摄像头图像伽马(Gamma)变换

为什么要进行Gamma校正

图像的 gamma 校正是一种图像处理技术,用于调整图像的亮度和对比度,让显示设备显示的亮度和对比度更符合人眼的感知。Gamma 校正主要用于修正显示设备的非线性响应,以及在图像处理中进行色彩校正和图像增强。

以前,大多数监视器是阴极射线管显示器(CRT)。这些显示器有一个物理特性就是两倍的输入电压产生的不是两倍的亮度。输入电压产生约为输入电压的2.2次幂的亮度,这叫做显示器Gamma。

Gamma也叫灰度系数,每种显示设备都有自己的Gamma值,都不相同,有一个公式:设备输出亮度 = 电压的Gamma次幂,任何设备Gamma基本上都不会等于1,等于1是一种理想的线性状态,这种理想状态是:如果电压和亮度都是在0到1的区间,那么多少电压就等于多少亮度。对于CRT,Gamma通常为2.2,因而,输出亮度 = 输入电压的2.2次幂,你可以从本节第二张图中看到Gamma2.2实际显示出来的总会比预期暗,相反Gamma0.45就会比理想预期亮,如果你讲Gamma0.45叠加到Gamma2.2的显示设备上,便会对偏暗的显示效果做到校正,这个简单的思路就是本节的核心

人类所感知的亮度恰好和CRT所显示出来相似的指数关系非常匹配(我猜并不是巧合,可能是物理原因)。为了更好的理解所有含义,请看下面的图片:

第一行是人眼所感知到的正常的灰阶,亮度要增加一倍(比如从0.1到0.2)你才会感觉比原来变亮了一倍(这里的意思是说比如一个东西的亮度0.3,让人感觉它比原来变亮一倍,那么现在这个亮度应该成为0.6,而不是0.4,也就是说人眼感知到的亮度的变化并非线性均匀分布的。问题的关键在于这样的一倍相当于一个亮度级,例如假设0.1、0.2、0.4、0.8是我们定义的四个亮度级别,在0.1和0.2之间人眼只能识别出0.15这个中间级,而虽然0.4到0.8之间的差距更大,这个区间人眼也只能识别出一个颜色)。然而,当我们谈论光的物理亮度,比如光源发射光子的数量的时候,底部(第二行)的灰阶显示出的才是物理世界真实的亮度。如底部的灰阶显示,亮度加倍时返回的也是真实的物理亮度(译注:这里亮度是指光子数量和正相关的亮度,即物理亮度,前面讨论的是人的感知亮度;物理亮度和感知亮度的区别在于,物理亮度基于光子数量,感知亮度基于人的感觉,比如第二个灰阶里亮度0.1的光子数量是0.2的二分之一),但是由于这与我们的眼睛感知亮度不完全一致(对比较暗的颜色变化更敏感),所以它看起来有差异。

因为人眼看到颜色的亮度更倾向于顶部的灰阶,显示器使用的也是一种指数关系(电压的2.2次幂),所以物理亮度通过监视器能够被映射到顶部的非线性亮度;因此看起来效果不错(译注:CRT亮度是是电压的2.2次幂而人眼相当于2次幂,因此CRT这个缺陷正好能满足人的需要)。

显示器的这个非线性映射的确可以让亮度在我们眼中看起来更好,但当渲染图像时,会产生一个问题:我们在应用中配置的亮度和颜色是基于显示器所看到的,这样所有的配置实际上是非线性的亮度/颜色配置。请看下图:

点线代表线性颜色/亮度值(译注:这表示的是理想状态,Gamma为1),实线代表显示器显示的颜色。如果我们把一个点线线性的颜色翻一倍,结果就是这个值的两倍。比如,光的颜色向量L¯=(0.5,0.0,0.0)代表的是暗红色。如果我们在线性空间中把它翻倍,就会变成(1.0,0.0,0.0),就像你在图中看到的那样。然而,由于我们定义的颜色仍然需要输出的显示器上,显示器上显示的实际颜色就会是(0.218,0.0,0.0)(0.218,0.0,0.0)。在这儿问题就出现了:当我们将理想中直线上的那个暗红色翻一倍时,在显示器上实际上亮度翻了4.5倍以上!

直到现在,我们还一直假设我们所有的工作都是在线性空间中进行的(Gamma为1),但最终还是要把所哟的颜色输出到显示器上,所以我们配置的所有颜色和光照变量从物理角度来看都是不正确的,在我们的显示器上很少能够正确地显示。出于这个原因,我们(以及艺术家)通常将光照值设置得比本来更亮一些(由于显示器会将其亮度显示的更暗一些),如果不是这样,在线性空间里计算出来的光照就会不正确。同时,还要记住,监视器所显示出来的图像和线性图像的最小亮度是相同的,它们最大的亮度也是相同的;只是中间亮度部分会被压暗。

因为所有中间亮度都是线性空间计算出来的(计算的时候假设Gamma为1),显示器显以后,实际上都会不正确。当使用更高级的光照算法时,这个问题会变得越来越明显,你可以看看下图

Gamma校正

Gamma校正(Gamma Correction)的思路是在最终的颜色输出上应用显示器Gamma的倒数。回头看前面的Gamma曲线图,会有一个短划线,它是显示器Gamma曲线的翻转曲线。我们在颜色显示到显示器的时候把每个颜色输出都加上这个翻转的Gamma曲线,这样应用了显示器Gamma以后最终的颜色将会变为线性的。我们所得到的中间色调就会更亮,所以虽然监视器使它们变暗,但是我们又将其平衡回来了。

我们来看另一个例子。还是那个暗红色(0.5,0.0,0.0)。在将颜色显示到显示器之前,我们先对颜色应用Gamma校正曲线。线性的颜色显示在显示器上相当于降低了2.22.2次幂的亮度,所以倒数就是1/2.2次幂。Gamma校正后的暗红色就会成为

(0.5,0.0,0.0)^1/2.2=(0.5,0.0,0.0)^0.45=(0.73,0.0,0.0)(0.5,0.0,0.0)^1/2.2=(0.5,0.0,0.0)^0.45=(0.73,0.0,0.0)。

校正后的颜色接着被发送给监视器,最终显示出来的颜色是

(0.73,0.0,0.0)^2.2=(0.5,0.0,0.0)。

你会发现使用了Gamma校正,监视器最终会显示出我们在应用中设置的那种线性的颜色。

2.2通常是是大多数显示设备的大概平均gamma值。基于gamma2.2的颜色空间叫做sRGB颜色空间。每个监视器的gamma曲线都有所不同,但是gamma2.2在大多数监视器上表现都不错。出于这个原因,游戏经常都会为玩家提供改变游戏gamma设置的选项,以适应每个监视器(译注:现在Gamma2.2相当于一个标准,后文中你会看到。但现在你可能会问,前面不是说Gamma2.2看起来不是正好适合人眼么,为何还需要校正。这是因为你在程序中设置的颜色,比如光照都是基于线性Gamma,即Gamma1,所以你理想中的亮度和实际表达出的不一样,如果要表达出你理想中的亮度就要对这个光照进行校正)。

在 gamma 校正中,通过应用一个指数函数来调整图像的像素值,这个指数函数通常被称为 gamma 曲线。Gamma 曲线的形状决定了图像的亮度和对比度的变化程度。

一般而言,gamma 校正可以分为两种情况:

  1. 增加 gamma 值(大于1):这会使得图像中较暗的部分变得更暗,而较亮的部分变得更亮。这样做可以增强图像的对比度,使得细节更加清晰。这种调整通常称为对比度增强。

  2. 减小 gamma 值(小于1):这会使得图像中较亮的部分变得更暗,而较暗的部分变得更亮。这样做可以降低图像的对比度,使得图像更柔和。这种调整通常称为图像的色彩校正。

Gamma校正的算法

Gamma 校正是通过应用一个指数函数来调整图像的像素值,即将图像中的每个像素值 映射到一个新值

这里的 I 是原始的像素值, γ 是所谓的 gamma 参数,决定了调整的程度。

伽马变换对图像的修正作用其实就是通过增强低灰度或高灰度的细节实现的, 从下面的伽马曲线可以直观理解:

(生成这个交互式曲线图的python代码在文章的后面有提供)

Gamma 校正的算法步骤如下:

  1. 确定 gamma 值:首先需要确定用于 gamma 校正的 gamma 参数值。通常,gamma 参数取值在 0 到 1 之间会使图像变亮,而取值大于 1 会使图像变暗。

  2. 应用指数变换:对于图像中的每个像素,将原始像素值 应用指数变换得到校正后的像素值 。

  3. 归一化处理:根据需要,对校正后的像素值进行归一化处理,使得调整后的像素值范围在合适的范围内,通常是 0 到 255。

  4. 输出校正后的图像:将处理后的像素值重新组合成一个图像,并输出校正后的图像。

python实现图像的Gamma校正

PotatoPie 4.0 实验教程(23) —— FPGA实现摄像头图像伽马(Gamma)变换-Anlogic-安路论坛-FPGA CPLD-ChipDebug

这段代码使用 OpenCV 读取和处理图像,并使用 Matplotlib 显示图像。它实现了对图像的两种不同的 Gamma 变换(sqrt 和 square),并显示了原始图像以及两种 Gamma 变换后的图像对比。

Matlab实现图像的Gamma校正

PotatoPie 4.0 实验教程(23) —— FPGA实现摄像头图像伽马(Gamma)变换-Anlogic-安路论坛-FPGA CPLD-ChipDebug

这段 MATLAB 代码实现了对图像进行 Gamma 变换的功能,并提供了两种不同类型的 Gamma 变换:sqrt 和 square

  • gamma_correction 函数接受三个参数:
    • image:输入的彩色图像。
    • gamma_type:指定 Gamma 变换的类型,支持 ‘sqrt’ 和 ‘square’ 两种。
    • c:常数系数,用于调节变换后的亮度范围。

该函数首先将输入图像分割成三个通道(蓝色、绿色和红色)。然后根据指定的 Gamma 类型进行 Gamma 变换,计算每个通道的 Gamma 变换后的像素值。最后,将各通道的像素值进行缩放以控制亮度范围,并合并三个通道,返回进行 Gamma 变换后的图像。

  • img_gamma 函数负责读取图像并调用 gamma_correction 函数进行 Gamma 变换。它显示原始图像和两种 Gamma 变换后的彩色图像对比,以直观展示 Gamma 变换的效果。

这段代码的主要功能是通过 Gamma 变换来调整图像的亮度和对比度,从而改善图像的视觉效果。

工程解析

工程层次图

demo18相比,只是多了一个img_gamma的模块,也就是下面这一段代码,在从SDRAM读出来之后,经它处理后再输出hdmi_tx模块。

代码解析

对于FPGA做log运算最好的办法是我们先用matlab或者python把gamma运算的结果计算出来,然后存到FPGA的ROM中,运行时FPGA从ROM中查表获取c*f(r)运算的结果即可。

这个工程的算法流程就是一个查表操作,img_gamma.v代码中的注释也很详细,请不一一解释了。

代码中我们提供了GAMMA_SQUARE和GAMMA_SQRT两个gamm计算方法,它们分别表示什么含义呢?

GAMMA_SQUARE 和 GAMMA_SQRT 是两种不同的 Gamma 校正算法的表示。

  • GAMMA_SQUARE 表示使用 Gamma 平方校正算法。在这种算法中,对输入信号的每个分量进行平方操作,以实现 Gamma 校正。
  • GAMMA_SQRT 表示使用 Gamma 平方根校正算法。在这种算法中,对输入信号的每个分量进行平方根操作,以实现 Gamma 校正。

这两种算法的选择会影响最终的图像效果,因为它们在处理输入信号时采用了不同的数学操作。

这两种算法的rom.v如何生成呢?我们提供了matlab代码和python代码。

python版gamma rom生成代码

PotatoPie 4.0 实验教程(23) —— FPGA实现摄像头图像伽马(Gamma)变换-Anlogic-安路论坛-FPGA CPLD-ChipDebug

matlab版代码

PotatoPie 4.0 实验教程(23) —— FPGA实现摄像头图像伽马(Gamma)变换-Anlogic-安路论坛-FPGA CPLD-ChipDebug

这段 MATLAB 代码完成了以下功能:

  1. 定义了灰度级数目和灰度级数组。
  2. 计算了使用平方根算法和平方算法的伽马变换后的灰度值。
  3. 将伽马变换后的灰度值写入了 COE 文件中,用于 Verilog 仿真。
  4. 生成了伽马变换的 Verilog 源文件,其中包含模块定义、端口声明和组合逻辑。
  5. 绘制了原始灰度级和两种伽马变换方式的输出灰度级的图像。

管脚约束

与PotatoPie 4.0 实验教程(18) —— FPGA实现OV5640摄像头采集以SDRAM作为显存进行HDMI输出显示相同,不作赘述。

时序约束

与PotatoPie 4.0 实验教程(18) —— FPGA实现OV5640摄像头采集以SDRAM作为显存进行HDMI输出显示相同,不作赘述。

实验结果

由于rgb用的同一个gamma,导致画面偏紫,大家可以自行调整参数生成不同的gamma表。

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

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

相关文章

《从零开始的Java世界》11网络编程

《从零开始的Java世界》系列主要讲解Javase部分,从最简单的程序设计到面向对象编程,再到异常处理、常用API的使用,最后到注解、反射,涵盖Java基础所需的所有知识点。学习者应该从学会如何使用,到知道其实现原理全方位式…

LangChain入门:24.通过Baby AGI实现自动生成和执行任务

随着 ChatGPT 的崭露头角,我们迎来了一种新型的代理——Autonomous Agents(自治代理或自主代理)。 这些代理的设计初衷就是能够独立地执行任务,并持续地追求长期目标。 在 LangChain 的代理、工具和记忆这些组件的支持下,它们能够在无需外部干预的情况下自主运行,这在真…

冯唐成事心法笔记 —— 知己

系列文章目录 冯唐成事心法笔记 —— 知己 冯唐成事心法笔记 —— 知人 冯唐成事心法笔记 —— 知世 冯唐成事心法笔记 —— 知智慧 文章目录 系列文章目录卷首语 管理是一生的日常,成事是一生的修行PART 1 知己 用好自己的天赋如何管理自我第一,如何…

C++学习随笔(12)—— list

本章我们来了解一下list 目录 1. list的介绍及使用 1.1 list的介绍 1.2 list的使用 1.2.1 list的构造 1.2.2 list iterator的使用 1.2.3 list capacity 1.2.4 list element access 1.2.5 list modifiers 1.2.6 list的迭代器失效 1. list的介绍及使用 1.1 list的介绍…

【Unity常用插件】Dotween插件API详解【一】

👨‍💻个人主页:元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏:UI_…

LabVIEW飞机机电系统综合测试平台

LabVIEW飞机机电系统综合测试平台 在现代航空领域,机电系统的准确性与可靠性对飞行安全至关重要。针对飞机机电管理计算机(UMC)复杂度增加、测试覆盖率低、效率不高等问题,开发了一套基于LabVIEW的机电系统综合测试平台。平台通过…

内网穿透及公网解析说明

内网穿透释义: 自己在本地搭建服务器时,本地网络有多种环境,如没有公网IP、没有路由映射权限、网络被NAT转发等情况。在需要外网访问内网服务器资源时,就需要用到内网穿透。内网穿透,即内网映射,内网IP地址…

PotatoPie 4.0 实验教程(21) —— FPGA实现摄像头图像二值化(RGB2Gray2Bin)

PotatoPie 4.0开发板教程目录(2024/04/21) 为什么要进行图像的二值化? 当我们处理图像时,常常需要将其转换为二值图像。这是因为在很多应用中,我们只对图像中的某些特定部分感兴趣,而不需要考虑所有像素的…

如何进行域名解析?如何清理DNS缓存?(附源码)

目录 1、什么是域名? 2、为什么使用域名? 3、域名解析的完整流程 4、调用gethostbyname系统接口将域名解析成IP地址 5、为什么需要清理系统DNS缓存? 6、使用cmd命令清理DNS缓存 7、通过代码去清除系统DNS缓存 C软件异常排查从入门到精…

PeLK: 大卷积核强势回归,高达101 × 101,提出了外围卷积

paper:https://arxiv.org/pdf/2403.07589 code:暂无 目录 0. 摘要 1. 引言 2. 相关工作 2.1. Large Kernel Convolutional Networks 2.2. Peripheral Vision for Machine Learning 3. 密集卷积优于条纹卷积 4. 参数高效的大核卷积神经网络 4.1. …

粒子群算法与优化储能策略python实践

粒子群优化算法(Particle Swarm Optimization,简称PSO), 是1995年J. Kennedy博士和R. C. Eberhart博士一起提出的,它是源于对鸟群捕食行为的研究。粒子群优化算法的基本核心是利用群体中的个体对信息的共享从而使得整个群体的运动…

基于 Redis 发布订阅实现服务注册与发现

写在前面 其实很少有公司会使用 Redis 来实现服务注册与发现,通常是ETCD、NACOS、ZOOKEEPER等等,但是也不妨碍我们了解。本文会先介绍 Redis 的发布/订阅模式,接着基于这个模式实现服务注册与发现。 Redis发布订阅流程图: Red…

云备份项目->配置环境

升级gcc到7.3版本 sudo yum install centos-release-scl-rh centos-release-scl sudo yum install devtoolset-7-gcc devtoolset-7-gcc-c source /opt/rh/devtoolset-7/enable echo "source /opt/rh/devtoolset-7/enable" >> ~/.bashrc 安装Jsoncpp库 sud…

MyBatis面试题总结,详细(2024最新)

面试必须要看看 1、MyBatis 中的一级缓存和二级缓存是什么?它们的区别是什么? MyBatis 中的一级缓存是指 SqlSession 对象内部的缓存,它是默认开启的。一级缓存的生命周期是与 SqlSession 对象绑定的,当 SqlSession 关闭时&#…

Linux--进程控制(2)--进程的程序替换(夺舍)

目录 进程的程序替换 0.相关函数 1.先看现象 2.解释原理 3.将代码改成多进程版 4.使用其它的替换函数,并且认识函数参数的含义 5.其它 进程的程序替换 0.相关函数 关于进程替换我们需要了解的6个函数: 函数解释: 这些函数如果调用成功则…

通过filebeat实现对docker服务的通用日志收集

平台 依赖 linux docker docker-compose 或者 docker compose 镜像 docker.elastic.co/beats/filebeat:8.12.2 docker.elastic.co/beats/kibana:8.12.2 docker.elastic.co/beats/elasticsearch:8.12.2 正文 背景 对于有自建机房的公司来说,如果公司的运维技术…

Stable Diffusion使用ControlNet:IP-Adapter实现图片风格迁移

IP-Adapter 全称是 Text Compatible Image Prompt Adapter for Text-to-Image Diffusion Models(文本到图像扩散模型的文本兼容图像提示适配器),是腾讯研究院出品的一个新的ControlNet模型,旨在使预训练的文本到图像扩散模型能够生…

【06】JAVASE-数组讲解【从零开始学JAVA】

Java零基础系列课程-JavaSE基础篇 Lecture:波哥 Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机,Java 仍是企业和开发人员的首选开发平台。…

最全GPTs使用教程+Prompt预设词教程

使用指南 直接复制使用 可以前往已经添加好Prompt预设的AI系统测试使用(可自定义添加使用) https://ai.sparkaigf.com 现已支持GPTs 雅思写作考官 我希望你假定自己是雅思写作考官,根据雅思评判标准,按我给你的雅思考题和对应…

从零入门区块链和比特币(第三期)

欢迎来到我的区块链与比特币入门指南!如果你对区块链和比特币感兴趣,但不知道从何开始,那么你来对地方了。本博客将为你提供一个简明扼要的介绍,帮助你了解这个领域的基础知识,并引导你进一步探索这个激动人心的领域。…