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表。

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

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

相关文章

unity学习(91)——云服务器调试——补充catch和if判断

本机局域网没问题,服务器放入云服务器后,会出现异常。 想要找到上面的问题,最简单的方法就是在云服务器上下载一个vs2022! 应该不是大小端的问题! 修改一下readMessage的内容,可以直接粘贴到云服务器的。 …

AIGC技术的伦理与风险探讨:现状与未来

如何看待AIGC技术? 简介:探讨AIGC技术的发展现状和未来趋势。 随着人工智能的迅速发展,AIGC(Artificial Intelligence in General Computing)技术作为智能计算的一种形式,正逐渐渗透到我们的生活和工作中…

firebase:一款功能强大的Firebase数据库安全漏洞与错误配置检测工具

关于firebase firebase是一款针对Firebase数据库的安全工具,该工具基于Python 3开发,可以帮助广大研究人员针对目标Firebase数据库执行安全漏洞扫描、漏洞测试和错误配置检测等任务。 该工具专为红队研究人员设计,请在获得授权许可后再进行安…

OpenCV添加文字和水印------c++

添加文字 bool opencvTool::addText(cv::Mat& image, const std::string text, const cv::Point& position, double fontScale, cv::Scalar color, int thickness, int fontFace) {cv::putText(image, text, position, fontFace, fontScale, color, thickness);return…

HTML5+CSS3小实例:飞行滑块

实例:飞行滑块 技术栈:HTML+CSS 效果: 源码: 【HTML】 <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0&qu…

利用STM32实现语音识别功能

引言 随着物联网和智能设备的普及&#xff0c;语音识别技术正逐渐成为用户交互的主流方式之一。 STM32微控制器具备处理高效率语音识别算法的能力&#xff0c;使其成为实现低成本、低功耗语音交互系统的理想选择。 本教程将介绍如何在STM32平台上开发和部署一个基础的语音识…

CentOS系统服务器装机后常用的操作命令大全

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

3分钟入门Java多线程

如何在程序中创建出多条线程&#xff1f; 继承Thread类 public class MyThread extends Thread {Overridepublic void run() {for (int i 0; i < 10; i) {System.out.println("MyThread运行了" i);}} }实现Runnable接口 public class MyRunnable implements …

js的算法-插入排序(折半插入排序)

直接插入排序的步骤 1. 从前面的有序子表中查找出待插入元素应该被插入的位置 2. 给插入位置腾空间 3. 将待插入元素复制到表中的插入位置。 直接插入排序&#xff1a;边比较边移动&#xff1b; 折半插入排序 先折半查找出元素的待插入位置&#xff0c;然后统一地移动待插…

Windows系统中下Oracle 19C数据库超级详细安装、设置教程(自己电脑上安装Oracle学习,保姆级教学,亲测有效)

Oracle 官方提供了一个基于 Java 技术的图形界面安装工具&#xff1a;Oracle Universal Installer&#xff08;Oracle 通用安装器&#xff09;简称 OUI&#xff0c;利用它可以完成在不同操作系统平台上&#xff08;Windows、Linux、UNIX&#xff09;的、不同类型的、不同版本的…

kotlin 编写一个简单的天气预报app (七)使用material design

一、优化思路 对之前的天气预报的app进行了优化&#xff0c;原先的天气预报程序逻辑是这样的。 使用text和button组合了一个输入城市&#xff0c;并请求openweathermap对应数据&#xff0c;并显示的功能。 但是搜索城市的时候&#xff0c;可能会有错误&#xff0c;比如大小写…

Java设计模式 _结构型模式_过滤器模式

一、过滤器模式 1、过滤器模式 过滤器模式&#xff08;Filter Pattern&#xff09;是这一种结构型设计模式。过滤器&#xff0c;顾名思义&#xff0c;就是对一组数据进行过滤&#xff0c;从而最终获取到我们预期的数据。 2、实现思路 &#xff08;1&#xff09;、定义过滤器的…

解决问题:Canal客户端覆盖服务端Subscribe,只有TRANSACTIONBEGIN和TRANSACTIONEND日志,没有ROWDATA日志的问题

一&#xff0c;背景 在整合canal和Spring时&#xff0c;本地使用canal的subscribe方法订阅了需要监听的表&#xff0c;但是获得只有transactionbegin和transactionend两种eventType的日志&#xff0c; 没有rowdata类型的日志&#xff0c;导致无法完成监听数据库数据更新的需求…

提示词优化的自动化探索:Automated Prompt Engineering

编者按&#xff1a; 作者在尝试教授母亲使用 LLM 完成工作任务时&#xff0c;意识到提示词的优化并不像想象中简单。提示词的自动优化对于经验并不丰富的提示词撰写者很有价值&#xff0c;他们没有足够的经验去调整和改进提供给模型的提示词&#xff0c;这引发了对自动化提示词…

C++—DAY2

定义一个矩形类Rec&#xff0c;包含私有属性length&#xff0c;width&#xff0c;有以下成员函数: void set length(int l);//设置长度 void set width(int w); //设置宽度 int get length(); //获取长度 int get_width(); //获取宽度 void show(); //输出…

可见水印去除算法简介

去水印技术简介 进入二十一世纪以来&#xff0c;随着互联网技术和电子技术的飞速发展和进步&#xff0c;电子设备比如智能手机、iPad、个人计算机和智能穿戴设备等的大规模普及使用&#xff0c;各种文字、图像、视频及音频等数据信息借助于互联网实现了人们之间远距离的信息传…

kernel32.dll文件丢失的原因以及相对应的解决办法分享

kernel32.dll丢失是电脑中一个重要的文件&#xff0c;其实想要修复kernel32.dll文件的方法比较简单&#xff0c;今天就和大家说说如何去修复kernel32.dll文件。导致kernel32.dll文件丢失的原因又是什么&#xff1f;一起开看看吧。 kernel32.dll的作用 kernel32.dll是一个重要的…

IntelliJ IDEA 如何启用 JDK 预览特性

IntelliJ IDEA 也可以启用 JDK 的预览特性。 针对项目&#xff0c;选择项目结构。 配置是在语言结构上。 单击语言结构上的 SDK 默认&#xff0c;往下拉&#xff0c;就可以看到针对新版本的选项。 同时还可以看到那些版本是支持新特性预览的&#xff0c;那些版本是不支持新特…

Oracle 19c OCM考试难度如何?

许多人对 Oracle 19c OCM 的考试规则并不熟悉&#xff0c;本文将详细介绍考证所需条件以及具体要求&#xff0c;以帮助大家更顺利地完成考试流程。 首先&#xff0c;考生需具备相匹配的同级别 OCP 证书&#xff0c;如已获得 10g/11g/12c 证书者&#xff0c;则须先完成 083 升级…

UE5 GAS开发P41-43 永久效果,去除永久效果,伤害区域,EnumClass,开始重叠与结束重叠事件

这一部分学习了怎么创建一个伤害性的地形(火焰地形,毒沼泽等都可以用这个方式创建) AuraEffectActor.h // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h" #include "GameplayEffect.h&q…