ISP中gamma矫正模块的FPGA设计和仿真
pangpang最近耗费很久的时间写了一个ISP中的gamma矫正模块,写下本文记录一下。
目录
1、gamma矫正介绍
2、本文gamma矫正设计要求
3、设计过程
4、仿真验证
5、总结
1、gamma矫正介绍
- 在电视和图形监视器中,显像管发生的电子束及其生成的图像亮度并不是随显像管的输入电压线性变化,电子流与输入电压相比是按照指数曲线变化的,输入电压的指数要大于电子束的指数。这说明暗区的信号要比实际情况更暗,而亮区要比实际情况更高。所以,要重现摄像机拍摄的画面,电视和监视器必须进行伽玛补偿。这种伽玛校正也可以由摄像机完成。我们对整个电视系统进行伽玛补偿的目的,是使摄像机根据入射光亮度与显像管的亮度对称而产生的输出信号,所以应对图像信号引入一个相反的非线性失真,即与电视系统的伽玛曲线对应的摄像机伽玛曲线,它的值应为1/γ,我们称为摄像机的伽玛值。电视系统的伽玛值约为2.2,所以电视系统的摄像机非线性补偿伽玛值为0.45。彩色显像管的伽玛值为2.8,它的图像信号校正指数应为1/2.8=0.35,但由于显像管内外杂散光的影响,重现图像的对比度和饱和度均有所降低,所以彩色摄像机的伽玛值仍多采用0.45。在实际应用中,我们可以根据实际情况在一定范围内调整伽玛值,以获得最佳效果。
以传统CRT(Cathode Ray Tube)屏幕的特性而言,该曲线通常是一个乘幂函数,Y=(X+e)γ,其中,Y为亮度、X为输出电压、e为补偿系数、乘幂值(γ)为伽玛值,改变乘幂 值(γ)的大小,就能改变CRT的伽玛曲线。典型的Gamma值是0.45,它会使CRT的影像亮度呈现线性。使用CRT的电视机等显示器屏幕,由于对于 输入信号的发光灰度,不是线性函数,而是指数函数,因此必需校正。
所谓伽玛校正就是对图像的伽玛曲线进行编辑,以对图像进行非线性色调编辑的方法,检出图像信号中的深色部分和浅色部分,并使两者比例增大,从而提高图像对比度效果。计算机绘图领域惯以此屏幕输出电压与对应亮度的转换关系曲线,称为伽玛曲线(Gamma Curve)。
这应该说是一个历史遗留问题,以前的CRT显示器是使用电子显像管,通过控制电流大小来控制显示屏幕上的亮度。然而亮度和电流之间的关系并非是线性的,也就是说电流强度变为2倍,显示的亮度并非是两倍,而是由公式1决定:
其中 gamma为CRT显示器的伽马值。
然而对于现实中的大部分摄像机或成像设备来讲,输入能量和记录在图片文件中的颜色亮度之间的关系却是线性的。这就导致显示器显示的图像与摄像设备捕捉的实际图像不一致,为了校正这个差异,摄像机在保存图像时会自动对数据进行一个伽马校正,如公式2
gamma依旧为显示器的伽马值。这样,当显示器显示图像时,公式2的输出作为公式1的输入,最后抵消了显示器的 gammagamma值造成的误差。
gamma的实现如下图所示
红线表示显示器的伽马值,蓝线表示摄像机保存图片时进行的校正,紫线表示二者合成之后的结果。可以看出,显示器的gammagamma值越高,图像越偏暗。SRGB标准中,默认显示设备的gamma值为2.2。
2、本文gamma矫正设计要求
由于在FPGA中实现,并且需要PS和PL协同处理,因此本文的设计要求如下:
- PS侧乒乓输入gamma表
- PL侧乒乓读取gamma表,并把输入的像素数值作为索引地址,检索对应地址的gamma值并输出
3、设计过程
设计过程需要考虑的因素比较多,如PS并不是一直乒乓写入gamma值,而是隔一段时间向乒或者乓写gamma值,这需要确定何时在写数据,并且数据是写在哪一个区域,极限情况下可以认为每一帧图都需要更新一张gamma表。对于PL一侧,也并不是一直进行乒乓操作,如当乒中数据写好,并一直没有更新乓中数据时,PL一侧需要一直读取乒的数据。具体的内部模块框图如下。
图3-1 Gammacorrection内部模块结构设计图
采用真双端口RAM IP核,由于RGB三个通道的数据,因此为了节省FPGA资源,使用了三块BRAM,即例化了三个IP核,由于R、G、B数据最大支持12位,故位宽采用12位,地址深度采用8192,0-4095作为乒地址,4096-8195作为乓地址。
- 对于PS一侧的乒乓写,采用如下的时序图
图3-2 PS端乒乓写入gamma表时序图
PS的写使能拉高的情况下,当写乒地址写满,此时会有一个full信号拉高一个时钟,此时证明可以读取乒地址,因此会将ping_rdy信号拉高,此时告诉PL测,可以读取乒的地址中的gamma表了,同理,乓地址的写数据方法一样。
PS乒乓写代码
//------------------------ps_ping_full-------------------------------------------
always @(posedge clk) beginif(rst) beginps_ping_full <= 1'b0;endelse if(ps_wren && (ps_waddr == 13'd4095)) beginps_ping_full <= 1'b1;endelse ps_ping_full <= 1'b0;
end//------------------------ps_ping_full-------------------------------------------
always @(posedge clk) beginif(rst) beginps_pang_full <= 1'b0;endelse if(ps_wren && (ps_waddr == 13'd8191)) beginps_pang_full <= 1'b1;endelse ps_pang_full <= 1'b0;
end//------------------------ping ready---------------------------------------------
always @(posedge clk) beginif(rst) beginping_rdy <= 1'b0;endelse if(ps_ping_full || (ps_wren && ps_waddr[12] == 1)) beginping_rdy <= 1'b1;end else if((ps_wren && ps_waddr == 13'd0) || ps_pang_full) beginping_rdy <= 1'b0;endelse ping_rdy <= ping_rdy;
end
//------------------------pang ready---------------------------------------------
always @(posedge clk) beginif(rst) beginpang_rdy <= 1'b0;endelse if((ps_wren && ps_waddr[12] == 0) || ps_pang_full) beginpang_rdy <= 1'b1;endelse if((ps_wren && (ps_waddr == 13'd4096))||ps_ping_full) beginpang_rdy <= 1'b0;endelse pang_rdy <= pang_rdy;
end
2.对于PL一侧的乒乓读,需要一个状态机来进行状态切换,模块内部主要由如下状态机负责控制相应的逻辑跳转:
图3-2 PL端乒乓读取gamma表状态机
跳转分支和对应的跳转条件
跳转分支 | 跳转条件 |
---|---|
IDLE→READ_PING | 检测到ping ready信号 |
READ_PING→IDLE | 检测到图像第一帧数据读取完成并且检测到pang ready信号 |
IDLE→READ_PANG | 检测到pang ready信号 |
READ_PANG→IDLE | 检测到图像第一帧数据读取完成并且检测到ping ready信号 |
READ_PING→READ_PING | 检测到图像第一帧数据读取完成并且仍然是ping ready信号 |
READ_PANG→READ_PANG | 检测到图像第一帧数据读取完成并且仍然是pang ready信号 |
三段式状态机代码
always @(posedge clk) beginif(rst) begincstate <= IDLE;endelse begincstate <= nstate;end
endalways @(*) begincase (cstate)IDLE:beginif(ping_rdy) beginnstate = READ_PING;endelse if(pang_rdy) beginnstate = READ_PANG; endend READ_PING:beginif(image_done && ping_rdy) beginnstate = READ_PING;endelse if(image_done && pang_rdy) beginnstate = IDLE;endendREAD_PANG:beginif(image_done && pang_rdy) beginnstate = READ_PANG;endelse if(image_done && ping_rdy) beginnstate = IDLE;endenddefault: nstate = IDLE;endcase
endalways @(posedge clk) beginif(rst) beginaddrb_R <= 12'b0;addrb_G <= 12'b0;addrb_B <= 12'b0; endelse begincase (nstate)IDLE: beginaddrb_R <= 12'b0;addrb_G <= 12'b0;addrb_B <= 12'b0; ps_ping_busy <= 1'b0;ps_pang_busy <= 1'b0; endREAD_PING:beginaddrb_R <= addrin_R;addrb_G <= addrin_G;addrb_B <= addrin_B;ps_ping_busy <= 1'b1;ps_pang_busy <= 1'b0; endREAD_PANG:beginaddrb_R <= addrin_R + 13'd4096;addrb_G <= addrin_G + 13'd4096;addrb_B <= addrin_B + 13'd4096; ps_ping_busy <= 1'b0;ps_pang_busy <= 1'b1; enddefault: beginaddrb_R <= 12'b0;addrb_G <= 12'b0;addrb_B <= 12'b0; ps_ping_busy <= 1'b0;ps_pang_busy <= 1'b0; endendcaseend
end
4、仿真验证
编写tb文件,并提前做好几个文本文档用来对比,如input.txt,gamma0.txt,gamma1.txt,output0.txt,output1.txt,通过tb测试之后仿真出来两个读取出来的gamma矫正的文本,并和output0.txt,output1.txt进行比较,查看是否一致。编写task代码模拟利用AXI协议输入image的过程,代码如下
//task image_stimulus
task image_stimulus;
input [12:0] image_row ;
input [12:0] image_col ;begin@(posedge clk);s_tvalid <= 1'b0; s_tdata <= 'd0 ; s_tuser <= 1'b0;s_tlast <= 1'b0;m_tready <= 1'b0;@(posedge clk);s_tvalid <= 1'b0; s_tdata <= 'd0 ; s_tuser <= 1'b0;s_tlast <= 1'b0;m_tready <= 1'b0;for(integer pix_row = 0 ; pix_row < image_row ; pix_row=pix_row+1)beginfor(integer pix_col = 0 ; pix_col < image_col; pix_col=pix_col+1)begin@(posedge clk);if(pix_row ==0 && pix_col ==0)begins_tvalid <= 1'b1 ;s_tdata <= din[pix_row*image_col + pix_col];s_tuser <= 1'b1 ;s_tlast <= 1'b0 ;m_tready <= 1'b1;endelse if(pix_col == (image_col -1))begins_tvalid <= 1'b1 ;s_tdata <= din[pix_row*image_col + pix_col];s_tuser <= 1'b0 ;s_tlast <= 1'b1 ; m_tready <= 1'b1; $display(":::::::::image row = %d ::::::::::::::: \n",pix_row) ; endelse begins_tvalid <= 1'b1 ;s_tdata <= din[pix_row*image_col + pix_col];s_tuser <= 1'b0 ;s_tlast <= 1'b0 ; m_tready <= 1'b1;endendendend
endtask
编写模拟乒乓初始化的task
ping初始化
task gamma_lut_init_ping;begin@(posedge clk);ps_wren <= 1'b0;ps_rden <= 1'b0;ps_waddr <= 13'b0;ps_wdata <= 12'b0;ps_raddr <= 13'b0;for(integer y=0;y<4096;y=y+1) begin //4096*6@(posedge clk);ps_wren <= 1'b1;ps_rden <= 1'b0;ps_waddr = y;ps_wdata <= lut[y];ps_raddr <= 13'b0;$display("==========address %d, gamma 1 mum = %d=========== \n",y,lut[y]);end@(posedge clk);ps_wren <= 1'b0;ps_rden <= 1'b0;ps_waddr <= 13'b0;ps_wdata <= 12'b0;ps_raddr <= 13'b0;end
endtask
pang初始化
task gamma_lut_init_pang;begin@(posedge clk);ps_wren <= 1'b0;ps_rden <= 1'b0;ps_waddr <= 13'b0;ps_wdata <= 12'b0;ps_raddr <= 13'b0;for(integer y=4096;y<8192;y=y+1) begin @(posedge clk);ps_wren <= 1'b1;ps_rden <= 1'b0;ps_waddr = y;ps_wdata <= lut_2[y-4096];ps_raddr <= 13'b0;$display("==========address %d, gamma 2 mum = %d=========== \n",y,lut_2[y-4096]);end@(posedge clk);ps_wren <= 1'b0;ps_rden <= 1'b0;ps_waddr <= 13'b0;ps_wdata <= 12'b0;ps_raddr <= 13'b0;end
endtask
通过VCS+Verdi联合仿真,得到如下仿真波形
图中,PS初始化乒地址,接着写入三帧image,接着初始化pang地址,写入三帧image,从仿真结果可以看出,由于第一帧读取乓地址,此时乓地址并没有被写入数据,因此读出数据为空,符合预期。
5、总结
本次设计经过了大概半个月的时间,从无到有,学会了很多新的设计方法和设计思路,对自己的成长有很大的帮助,感受到了自己的确有着一定的进步。
参考:伽马校正(gamma correction)学习笔记https://blog.csdn.net/w450468524/article/details/51649651?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link
图像处理之gamma矫正https://blog.csdn.net/u013625961/article/details/54375010?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163800009416780264053445%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163800009416780264053445&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-54375010.first_rank_v2_pc_rank_v29&utm_term=gamma%E7%9F%AB%E6%AD%A3&spm=1018.2226.3001.4187
ISP中gamma矫正模块的FPGA设计和仿真相关推荐
- 电子商务(电销)平台中用户模块(User)数据库设计明细
原文:电子商务(电销)平台中用户模块(User)数据库设计明细 以下是自己在电子商务系统设计中的订单模块的数据库设计经验总结,而今发表出来一起分享,如有不当,欢迎跟帖讨论~ 用户基础表(user_ba ...
- 电子商务(电销)平台中订单模块(Order)数据库设计明细
电子商务(电销)平台中订单模块(Order)数据库设计明细 以下是自己在电子商务系统设计中的订单模块的数据库设计经验总结,而今发表出来一起分享,如有不当,欢迎跟帖讨论~ 订单表 (order) |-- ...
- 电子商务(电销)平台中订单模块(Order)数据库设计明细(转载)
电子商务(电销)平台中订单模块(Order)数据库设计明细 以下是自己在电子商务系统设计中的订单模块的数据库设计经验总结,而今发表出来一起分享,如有不当,欢迎跟帖讨论~ 订单表 (order) |-- ...
- FPGA设计思想之“逻辑复制”
1.逻辑复制是一种通过增加面积来改善时序条件的优化手段,它最主要的应用时调整信号的扇出.如果某个信号需要驱动的后级逻辑信号较多,也就是其扇出非常大,那么为了增加这个信号的驱动能力,就必须插入很多级的B ...
- Camera ISP - Gamma 矫正
这是第一篇 Camera ISP 的文章,主要从自己收集到的资料来试着描述下关于 Gamma 的理解,需要注意的是目前为止我了解到的有关于 Gamma 的说法还是众说纷纭,各处都不甚一致,在尽可能收集 ...
- 防止FPGA设计中综合后的信号被优化
这不是一个新话题了,写这个也是当作自己的一个小小的笔记吧!觉得挺有用的. 一般在做前仿真(即功能仿真)时,不会考虑信号被优化的问题.最近做一个关于运算的小程序,前仿真的数据没有问题,但是实际出来的数据 ...
- FPGA设计中,Vivado 调用IP核详细操作步骤
FPGA设计中,Vivado 调用IP核详细操作步骤 今天给大侠带来了FPGA设计中,Vivado 调用IP核详细操作步骤,话不多说,手把手教学,请往下看. 首先咱们来了解一下vivado的IP核,I ...
- 关于FPGA设计中的线性序列机和状态机
在FPGA设计中,线性序列机和状态机思想是十分重要的思想方法,用于实现各种时序要求.这次的内容是实现用FPGA,时钟周期为20ns,每间隔5000ns发送一个字节的数据,数据不从外部接入,而是内部给定 ...
- 在FPGA设计中怎么应用ChatGPT?
在FPGA设计中怎么应用ChatGPT? 科技即生产力,最近,OpenAI 发布了 ChatGPT,在各大论坛和许多网站上受到了广泛关注,ChatGPT是由 OpenAI 提出的大型预训练语言模型,使 ...
最新文章
- HTML超文本描述语言,HTML超文本标记语言的介绍
- mybatis 配置
- 浅谈C++设计模式之工厂方法(Factory Method)
- Swiper学习之三---swiper的配置选项 ②:Free模式和Effects切换效果
- 小游戏一键跳转小程序任意页面
- tensorflow之saver
- 《Perl语言入门》学习笔记
- 公司打算使用内网安全管理的软件,看了一下感觉IP-guard和Ping32两个好像不错,有需要的朋友可以参考一下
- 微信支付商户平台可以绑定多个不同主体的小程序或微信公众号
- 微信小程序后端用python_Python开发微信小程序后端开发小坑
- vmware反复触发numlock问题
- php 对接 hotmail邮箱,php 模拟GMAIL,HOTMAIL(MSN),YAHOO,163,126邮箱登录的详细介绍
- 微信原生组件|基于小程序实现音视频通话
- NAT转换技术(SNAT、MASQUERADE、DNAT策略)及代理服务(squid服务)
- Java 求a+b的值
- 程序员英语 - Word Group
- 题解 P3588 [POI2015]PUS
- 支付宝开通海外退税 阿里腾讯暗战跨境O2O_21世纪网
- 随着信息产业的飞速发展,项目管理对于应用开发为主的软件企业是一个行之有效的管理办法,在软件开发中项目...
- 队列详解及java实现