雖說回頭再看的時候這東西很基礎,但也是一開始花了些時間一點點啃下來的。勿忘初心。
一、引言
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)