虽说回头再看的时候这东西很基础,但也是一开始花了些时间一点点啃下来的。勿忘初心。
一、引言
1.编写目的:
总结gamma矫正模块的总体设计、详细设计及仿真测试、后期优化。
2.项目背景:
编写gamma矫正模块。
3.定 义:
- gamma校正: 大多数CRT显示器的变换函数产生的亮度值正比于信号幅度的某种能量(称为gamma)。因此高亮度的范围被扩展了,而低亮度的范围被压缩了。在发射之前对视频信号进行gamma校准,显示器的亮度输出就大体上是线性的了,并且发射过程中产生的噪声也会受到抑制。
目的: 为图像进行gamma编码的目的是用来对人类视觉的特性进行补偿,从而根据人类对光线或黑白的感知,最大化地利用表示黑白的数据位或带宽。
原理: 归一化、预补偿、反归一化
算法: 假设图像中有一个像素A,值是150,那么对这个像素进行校正须执行如下步骤。
归一化: 将像素值转换为 0~1 之间的实数。 算法如下: ( i + 0. 5)/256 。对于像素 A 而言 ,其对应的归一化值为 0. 587891。
预补偿: 根据公式L_in=〖L_org〗^(1/gamma),求出像素归一化后的数据以 1/gamma 为指数的对应值。若 gamma 值为 2. 2,则 1/gamma 为 0. 45454,对归一化后的 A 值进行预补偿的结果就 是 0. 587891^0. 454545 = 0.785479。
反归一化: 将经过预补偿的实数值反变换为 0 ~ 255 之间的整数值。具体算法为: f*256 - 0. 5 。续前例,将 A 的预补偿结果0.785479 代入上式,得到 A 预补偿后对应的像素值为 201 ,这个 201 就是最后送 入显示器的数据。 - 图像数据 : 用数值表示的各像素的灰度值的集合。
- 灰 度 值 : 指黑白图像中点的颜色深度,范围一般从0到255,白色为255,黑色为0,故 黑白图片也称为灰度图像。灰度也可认为是亮度,简单的说就是色彩的深浅程 度,R、G、B值均相等。
- 参考资料: [1] PG058 Block Memory Generator v8.4 LogiCORE IP Product Guide.
[2] UG908 Vivado Design Suite User Guide Programming and Debugging.
[3] PG021 AXI DMA v7.1 LogiCORE IP Product Guide.
二、总体设计
1.需求概述:
从BRAM数据源读取图像数据,进行gamma矫正,然后存入RAM中。
2.模块结构:
后期将RAM2接口改为AXIS型,只取RAM2_gamma模块,将其放入整个工程中。修改后的模块结构应为:
(接口对应工程中的前后两个模块分别为:alinx_ov5640_0,System_i)
3.Schematic:
4.模块描述
-
Top:
功能:顶层模块,调用下面三个模块,加一个ila的IP核以实时仿真时探取信号
输入:clk,rst -
Module1:
功能:存放图像数据源,导入.coe文件
输入:clk,rst_b 输出:dout_b -
Module2:
功能:存放matlab计算结果,调取像素值相对应的gamma矫正结果
输入:clk,rst_g,din_g 输出:dout_g -
Module3:
功能:存放矫正后的数据
输入:clk,rst_g,dina_a 输出:doutb_a -
后期将Module2模块改为AXIS接口,则该模块应为:
RAM_gamma:
功能:存放matlab计算结果,调取像素值相对应的gamma矫正结果
输入:clk,resetn, 输出:
s_axis_gamma_rdata, m_axis_gamma_tdata,
s_axis_gamma_rvalid, m_axis_gamma_tvalid,
m_axis_gamma_rready, s_axis_gamma_tready,
s_axis_gamma_rlast, m_axis_gamma_tlast,
s_axis_gamma_ruser, m_axis_gamma_tuser,
s_axis_gamma_rkeep m_axis_gamma_tkeep
三、详细设计
1.代码语言:
verilogHDL,c语言
2.运行环境:
Vivado 2017.4,modelsim,ALINX黑金AX7020开发板,matlab 2014
3.各模块设计:
- RAM1:存放图像数据;
- RAM2:存放0~255之间整数gamma矫正的结果;
- RAM3:存放图像数据gamma矫正的结果
① 数据源位宽:RAM1输入输出数据位宽:8位; RAM2输入输出数据位宽:8位;RAM3输入输出数据位宽:8位;
② 数据源深度:RAM1深度:128128(图像数据源:128124);RAM2深度:256;RAM3深度:2^14
③ 时钟同步
④ 图像矩阵作为数据源读入顺序:从上到下
后期将RAM2模块改为AXIS接口直接加到工程里,端口为:data,keep,valid,ready,last,user。参考datasheet,如图:
由上图分析, - aclk为时钟线,所有信号都在aclk上升沿被采样;
- data为数据线,主机发送,从机接收。
- keep为主机数据有效指示,为高代表对应的字节为有效字节,否则表示发送的为空字节。
- valid为主机数据同步线,为高表示主机准备好发送数据;
- ready为从机数据同步线,为高表示从机准备好接收数据;这两根线完成了主机与从机的握手信号,一旦二者都变高有效,数据传输开始。
- last为主机最后一个字指示,下一clk数据将无效,TVALID将变低。
另,user为valid和ready同时为高时才为有效数据。
考虑到RAM2模块的输入输出为8位,模块alinx_ov5640_0和System_i输入输出为16位,将输入数据取高8位为0,低8位输入。输出时,将8位的输出数据放在最终输出数据的低8位,其高八位置0。这里的keep始终为2’b11。分析得具体接口连接如图:
4.实现方案:
- 在matlab中计算、对比:计算0~255之间所有整数的gamma矫正结果。
- 将仿真数的结果放入matlab,恢复成图像,输出处理后的图像。
- 在modelsim中仿真:ila中探针探取想要看的结果。
- 在FPGA中实现:将matlab中读出的图像数据存入RAM1作为数据源,其输出与RAM2中的gamma矫正结果相匹配,将匹配后的值存入RAM3。
5.步 骤:
① 从matlab中提取图像的一维数组元素存入.COE文档;
src_path = 'rice.jpg';
I = imread(src_path);
x = I(:,:,1);
my_write(x,'E:\practice\gamma\RAM1.COE','%0.2x\n');
② 将文档中的数据作为数据源,存入RAM1中,RAM1选择single port ROM,导入.COE文件;
③ 在matlab上计算0~255之间所有整数的gamma矫正结果,并存入RAM2;
i = [0:1:255]; //生成0~255的整数数组
A = (i+0.5)/256; //归一化
f = A.^(1/2.2); //预补偿
s = f*256 - 0.5; //反归一化
r=round(s); //四舍五入取整
my_write(r,'E:\gamma.dat','%0.2x\n') //数据读出到.dat文档中
④ 将每个时钟周期从RAM1输出的数据作为地址在RAM2中查找计算结果;
⑤ RAM2输出的值存入到RAM3中,RAM3可选选择Block Memory Generator中的Simple Dual Port RAM。
⑥ 在testbench中导出RAM3中的数据:
//宏定义
define stor_file "E:/practice/gamma/out.dat"
//变量声明
integer fid1;
initial begin
//打开输出数据文件
fid1 = $fopen(`stor_file,"w");
end
//-----------导出输出数据----------
always@(posedge clk) begin
$fdisplay(fid1,"%x",doutb_a[7:0]);
end
⑦ 在top文件中加入IP核ila,探取需要知道的信号波形。这里举例探取dout_b和doutb_a信号。
ila_0 ila_0(
.clk(clk),
.probe0(dout_b),
.probe1(doutb_a)
);
⑧ 将top模块例化,加入到整个工程中去,clk和resetn分别对应工程中的FCLK_CLK1和FCLK- CLK1RESETN。在综合布线后生成bit文件,将bit文件名改为fpga.bit,替换板子上sd卡里 的bit文件。在vivado中open target,连接开发板,进行时序仿真。
⑨ 在matlab里对比分析matlab矫正的结果和verilog矫正的结果是否一致。
matlab代码:
src_path = 'rice.jpg';
gamma = 2.2 ;
N = 256;
S = 0.5;
%读取数据
I = imread(src_path);
%gamma矫正
i = double(I);
A = (i + 0.5) / 256;
F = A.^(1/gamma);
B = F*N - S;
b = uint8(B);
%画图分析
figure;
subplot(1, 2, 1);
imshow(I, []);
title('原图');
subplot(1, 2, 2);
imshow(b, []);
title('matlab处理后');
将verilog矫正后的数据提取出来,输出图像:
fid = fopen('out.dat');
a = textscan(fid,'%s'); %读取矫正后的数据
b = a{1}; %textscan生成的是cell型的数组
c = cell2mat(b); %cell转换为矩阵
d = hex2dec(c); %转为十进制
s = cat(3,d,d,d);
e = reshape(s,124,128,3); %重组回原图像的像素值
f = uint8(e); %强制转换为uint8的格式
imshow(f); %显示图像
title('verilog处理后');
⑩ 后期将RAM2改为AXIS型接口,单独作为一个模块整合进工程代码中。
四、测试仿真
1.测试要点:
① 将RAM3存储的处理后的数据与matlab处理后的数据对比
② 将上两组数据转化为图像,进行对比
2.测试结果:
① matlab correction:
Verilog correction:
② matlab:
Verilog:
3.仿 真:
modelsim仿真,ila的探针探取的结果与之前仿真结果对比
4.仿真结果:
- ila分别采样三个模块的输出数据
- ila采样的module1和module3的数据,在随机抓取的样值中看到空出来的数据00和0f
- 这里的00分为两段,前一段是RAM深度为16384,但图像数据为128*124=15872,空余的深度值
- 后一段的00是延时
将RAM2改为AXIS型接口,单独作为一个模块整合进工程代码中之后的仿真结果。 - ila探取输入值和输出值
不足:仿真中波形为随机抓取,可以考虑将触发改为从地址0开始。
后期将RAM2接口改为AXIS型,只取RAM2_gamma模块,将其放入整个工程中。修改后的模块结构应为:
(接口对应工程中的前后两个模块分别为:alinx_ov5640_0,System_i)