使用插值算法實(shí)現(xiàn)圖像縮放是數(shù)字圖像處理算法中經(jīng)常遇到的問題。我們經(jīng)常會將某種尺寸的圖像轉(zhuǎn)換為其他尺寸的圖像,如放大或者縮小圖像。由于在縮放的過程中會遇到浮點(diǎn)數(shù),如何在FPGA中正確的處理浮點(diǎn)數(shù)運(yùn)算是在FPGA中實(shí)現(xiàn)圖像縮放的關(guān)鍵。
一、插值算法原理
在圖像的縮放處理過程中,經(jīng)常會用到插值算法,常見的插值算法包括最鄰近插值,雙線性插值,雙三次線性插值,蘭索斯插值等方法。其中,雙線性插值由于折中的插值效果和實(shí)現(xiàn)復(fù)雜度,運(yùn)用較為廣泛。本文中僅介紹最臨近插值,重點(diǎn)討論如何在FPGA平臺上使用雙線性插值實(shí)現(xiàn)圖像的縮放。
1.1 最臨近插值---------最臨近插值介紹
講理論不如舉例子來的快,所以為了更好更快的理解最臨近插值,我們通過舉個(gè)簡單的例子來解釋最臨近插值是個(gè)什么神奇的東西。假如有一個(gè)3*3矩陣(一幅圖像其實(shí)就是矩陣),如下,我們把這個(gè)圖像叫做原圖(source image):
66 28 128
25 88 200
36 68 120
在矩陣中,坐標(biāo)(x,y)是這樣確定的,矩陣的左上角的頂點(diǎn)為原點(diǎn),從左到右為x軸,從上到下為y軸,如下所示:
圖1 圖像中坐標(biāo)確定方式
假設(shè)我們想把這個(gè)3*3的原圖擴(kuò)大成4*4(我們把這個(gè)4*4的圖像叫做目的圖像destination image)我們該如何做呢?首先當(dāng)然是先把4*4的矩陣畫出來,如下所示:
????
? ???
????
????
矩陣畫出來后,接下來就要像未知數(shù)里填充像素點(diǎn)了。要填入的值如何計(jì)算呢,通過如下公式進(jìn)行計(jì)算:
首先我們先來填充目的圖像 (0,0),套用上述公式可得到對應(yīng)原圖像的坐標(biāo)點(diǎn),srcX =0,srcY= 0;找到原圖像中對應(yīng)的坐標(biāo)點(diǎn)的像素值,將該像素填充到目的圖像中,如下
66 ???
????
????
接下來填充目的圖像(1,0),仍然套用公式,srcX = 3/4,srcY = 0,結(jié)果發(fā)現(xiàn)得到的結(jié)果居然有小數(shù),由于計(jì)算機(jī)中的像素點(diǎn)已經(jīng)是最小單位,像素的坐標(biāo)都是整數(shù),沒有小數(shù)。這是只需要按照四舍五入的思想將小數(shù)坐標(biāo)轉(zhuǎn)換為整數(shù)坐標(biāo)即可.所以(3/4,0) ≈ (1,0),把原圖像中(1,0)點(diǎn)的像素值填入目的圖像(1,0)坐標(biāo)處,得到如下結(jié)果:
66 28 ??
????
????
接下來重復(fù)上述過程,就可得到放大后的目的圖像,如下:
66 28 128 128
25 88 200 200
36 68 120 120
36 68 120 120
這種放大圖像的方法叫做最臨近插值算法,這是一種最基本最簡單的圖像縮放算法,該方法縮放后的圖像會出現(xiàn)失真現(xiàn)象。
1.2 雙線性插值算法
雙線性插值算法是一種比較好的縮放算法,它充分利用源圖中虛擬點(diǎn)四周的四個(gè)像素點(diǎn)來共同決定目標(biāo)圖形中的一個(gè)像素值,因此縮放效果要比最臨近插值算法好的多。
雙線性插值算法的描述如下:
對于目的圖像中的某點(diǎn)坐標(biāo),通過乘以縮放倍數(shù)(srcwidth/dstwidth、srcheight/dstheight)得到一個(gè)浮點(diǎn)坐標(biāo)(i+u,j+v)(其中i,j均為浮點(diǎn)坐標(biāo)的整數(shù)部分;u,v為浮點(diǎn)坐標(biāo)的小數(shù)部分),則這個(gè)浮點(diǎn)坐標(biāo)(i+u,j+v)處的像素值f(i+u,j+v)可以由原圖像中的坐標(biāo)點(diǎn)(i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所對應(yīng)的四個(gè)像素值來共同決定,即
如1.1計(jì)算的目的像素坐標(biāo)(1,0)對應(yīng)源圖像的坐標(biāo)為(3/4,0),即 i = 0,u = 0.75;j = 0,v = 0;即目的圖像(1,0)處的像素值由源圖像中(0,0)、(1,0)、(0,1)(1,1)四個(gè)坐標(biāo)點(diǎn)對應(yīng)的像素值來確定,代入上述公式即可計(jì)算出(3/4,0)處的像素值。
看了上述內(nèi)容應(yīng)該對最臨近插值算法和雙線性插值算法有一定的了解,其基本原理應(yīng)該已經(jīng)掌握。
在網(wǎng)上刷博客發(fā)現(xiàn)有好多大佬講解了雙線性插值算法的優(yōu)化(雙線性插值算法總結(jié)),發(fā)現(xiàn)優(yōu)化以后縮放效果更好一些,優(yōu)化的具體細(xì)節(jié)就不再講解,具體優(yōu)化過程就是使用
寫到這里雙線性插值算法的基本原理及優(yōu)化方式已經(jīng)基本講述完畢,那么,如何使用FPGA來實(shí)現(xiàn)雙線性插值呢?接下來我們進(jìn)行分析如何使用FPGA來實(shí)現(xiàn)雙線性插值。
二、雙線性插值的FPGA實(shí)現(xiàn)遇到的問題及解決方案
通過以上分析,我們會發(fā)現(xiàn)使用FPGA實(shí)現(xiàn)雙線性插值有以下難點(diǎn):
如何處理算法中出現(xiàn)的小數(shù);
如何同時(shí)求出相鄰的四個(gè)點(diǎn)的坐標(biāo)以及系數(shù);
如何使用這四個(gè)坐標(biāo)同時(shí)輸出相鄰的四個(gè)像素值;
接下來我們依次解決上述問題
2.1 如何處理算法中出現(xiàn)的小數(shù)
FPGA處理浮點(diǎn)數(shù)一般都是將浮點(diǎn)數(shù)轉(zhuǎn)化為定點(diǎn)數(shù)進(jìn)行處理,我們在求srcX、srcY也按照此思想。由于FPGA中沒法進(jìn)行小數(shù)表示,因此我們將srcX、srcY放大一定倍數(shù)變成整數(shù),這個(gè)整數(shù)的高n位表示坐標(biāo)點(diǎn)的整數(shù)部分(i或j),低m位表示小數(shù)部分(u或者v)。如我們要將30*30的圖像放大成64*64即srcwidth = srcheight = 30,dstwidth = dstheight =64(接下來工程實(shí)現(xiàn)也按照此例子來實(shí)現(xiàn))。
位寬為16位。由于放大了128倍,所以srcX[6:0]、srcY[6:0]的低7位表示小數(shù)部分,即u = srcX[6:0],v=srcY[6:0];
其余高位部分表示整數(shù)部分,即i = srcX[15:7],j = srcY[15:7]。這樣就可以解決算法中出現(xiàn)的小數(shù)問題。
2.2如何同時(shí)求出相鄰的四個(gè)點(diǎn)的坐標(biāo)以及系數(shù)
由1.1可知,我們可以求出第一個(gè)坐標(biāo)點(diǎn)(i,j) = (srcX[15:7],srcY[15:7]),那么如何求出相鄰其它三個(gè)點(diǎn)的坐標(biāo)呢?接下來需要求出其余三點(diǎn)(i+1,j)、(i,j+1)、(i+1,j+1)的坐標(biāo),
(i+1,j) = (srcX[15:7] + 'd1,srcY[15:7]);
(i,j+1) = (srcX[15:7] ,srcY[15:7] * srcwidth);
(i+1,j+1) = (srcX[15:7] + 'd1 ,srcY[15:7] * srcwidth);
通過以上方式便可以很容易的求出四個(gè)點(diǎn)的坐標(biāo)。接下來就要考慮如何使用這四個(gè)坐標(biāo)同時(shí)讀取四個(gè)對應(yīng)的像素值。
同時(shí)我們定義四個(gè)位寬為8位的系數(shù)變量coefficient1[7:0]、coefficient2[7:0]、coefficient3[7:0]、coefficient4[7:0],通過
coefficient1[7:0] = ‘d128 - coefficient2;(由于系數(shù)放大了128倍,所以是128-系數(shù)2,對應(yīng)1-u)
coefficient2[7:0] = {1'b0,u} = {1'b0,srcX[6:0]};
coefficient3[7:0] = ‘d128 - coefficient4;(由于系數(shù)放大了128倍,所以是128-系數(shù)4,對應(yīng)1-v)
coefficient4[7:0] = {1'b0,v} = {1'b0,srcY[6:0]};
為什么定義的系數(shù)變量為8位而不是7位,這是因?yàn)橄禂?shù)變量的最大值為’d128,8位位寬才能表示‘d128。
接下來使用上述求出的四個(gè)坐標(biāo)求出對應(yīng)的坐標(biāo)的像素值,再套用公式
即可求出目的圖像對應(yīng)位置放大后的像素值。
通過以上分析,可以將公式變形為如下形式,求出對應(yīng)點(diǎn)的像素值后直接使用以下公式即可:
求得該像素值之后還需要將該像素值除以128*128才是所得的實(shí)際結(jié)果。
2.3 如何使用這四個(gè)坐標(biāo)同時(shí)輸出相鄰的四個(gè)像素值
通過2.2分析可以同時(shí)求出四個(gè)像素點(diǎn)的坐標(biāo),但是如何通過這四個(gè)坐標(biāo)同時(shí)求出對應(yīng)的像素值呢?由于待縮放的數(shù)據(jù)是先緩存進(jìn)RAM的,如果待縮放的圖像數(shù)據(jù)僅僅緩存在一個(gè)RAM里,是不可能通過四個(gè)像素點(diǎn)的坐標(biāo)同時(shí)訪問這個(gè)RAM,即不可能同時(shí)求出對應(yīng)的四個(gè)像素點(diǎn)的值。所以,可以通過犧牲面積換取速度的方式,即將RAM復(fù)制4次,每個(gè)RAM都緩存整個(gè)待縮放的圖像數(shù)據(jù),這樣四個(gè)像素點(diǎn)的坐標(biāo)就可以通過訪問不同的RAM來同時(shí)訪問對應(yīng)的四個(gè)像素值了。雖然犧牲了FPGA內(nèi)部的存儲資源,但是提升了雙線性插值算法的速度。
三 、雙線性插值的FPGA實(shí)現(xiàn)
通過第二節(jié)內(nèi)容的分析可知,使用FPGA實(shí)現(xiàn)雙線性插值主要包含以下三個(gè)模塊:生成目的圖像對應(yīng)原圖像坐標(biāo)和系數(shù)模塊、待處理圖像緩存模塊、雙線性插值運(yùn)算單元模塊。其數(shù)據(jù)流向圖如下圖所示:
圖2 FPGA實(shí)現(xiàn)雙線性插值數(shù)據(jù)流向圖
圖2中虛框部分是圖像裁剪模塊,這里不討論圖像裁剪功能的實(shí)現(xiàn),僅僅討論圖像縮放功能的實(shí)現(xiàn)。
整個(gè)實(shí)現(xiàn)的過程描述如下:
上位機(jī)將待縮放的圖像數(shù)據(jù)送進(jìn)待處理圖像緩存模塊,該模塊將待處理的數(shù)據(jù)復(fù)制四份
待1中數(shù)據(jù)緩存完畢,開始生成目的圖像對應(yīng)原圖像坐標(biāo)和系數(shù);
將生成的坐標(biāo)送給待處理圖像緩存模塊進(jìn)行數(shù)據(jù)的訪問,將讀取的數(shù)據(jù)送給雙線性插值運(yùn)算單元;
將生成的系數(shù)送給雙線性插值運(yùn)算單元,與相應(yīng)的像素值進(jìn)行數(shù)學(xué)運(yùn)算,實(shí)現(xiàn)雙線性插值功能。
實(shí)現(xiàn)雙線性插值功能的代碼如下所示:
頂層模塊:
module top(input clk,
output [7:0]doutb,
output de_o,
output v_sync,
output h_sync
);
parameter [7:0]src_width = 'd30;
wire [7:0]coordinate_y;
wire start;
wire en_b;
//
wire [7:0]coefficient1;
wire [7:0]coefficient2;
wire [7:0]coefficient3;
wire [7:0]coefficient4;
wire [7:0]doutbx;
wire [7:0]doutbx1;
wire [7:0]doutby;
wire [7:0]doutby1;
// Instantiate the module
sourceimage_virtualcoordinate coordinate (
.clk(clk),
.src_width(src_width),
.start(start),
.coordinate_x(coordinate_x),
.coordinate_y(coordinate_y),
.coefficient1(coefficient1),
.coefficient2(coefficient2),
.coefficient3(coefficient3),
.coefficient4(coefficient4),
.en(en_b)
);
//
wire [7:0]dina;
wire valid_zsc;
// Instantiate the module
self_generate self_generate (
.clk(clk),
.data_o(dina),
.valid_zsc(valid_zsc)
);
// Instantiate the module
mem_control mem_control (
.clk_wr(clk),
.clk_rd(clk),
.coordinate_x(coordinate_x), /
.coordinate_y(coordinate_y),
.din_a(dina), /
.en_a(valid_zsc), ///
.src_width(src_width), //
.en_b(en_b), ///
.doutbx(doutbx),
.doutbx1(doutbx1),
.doutby(doutby),
.doutby1(doutby1),
.start(start)
);
///
wire [7:0] data_o;
wire en_o;
// Instantiate the module
arithmetic_unit arithmetic_unit (
.clk(clk),
.coefficient1(coefficient1),
.coefficient2(coefficient2),
.coefficient3(coefficient3),
.coefficient4(coefficient4),
.en_b(en_b),
.doutbx(doutbx),
.doutbx1(doutbx1),
.doutby(doutby),
.doutby1(doutby1),
.data_o(data_o),
.en_o(en_o)
);
wire de;
wire start_en;
// Instantiate the module
mem_64multi_64 mem_64multi_64 (
.clk(clk),
.dina(data_o),
.ena(en_o),
.enb(enb),
.start_en(start_en),
.doutb(doutb)
);
// Instantiate the module
vesa_out vesa_out (
.clk(clk),
.start_en(start_en),
.v_sync(v_sync),
.h_sync(h_sync),
.de(enb),
.de_o(de_o)
);
endmodule
生成對應(yīng)的坐標(biāo)和系數(shù)模塊:
//該模塊是用來計(jì)算源圖像的虛擬坐標(biāo),由于源圖像和目的圖像都是正方形,所以只考慮一個(gè)縮放倍數(shù)即可
//
module sourceimage_virtualcoordinate(input clk,
input [7:0]src_width,/src_width = src_height
input [5:0]dest_width,/dest_width = dest_height = 'd64
input start,///數(shù)據(jù)緩存滿了以后才可以進(jìn)行計(jì)算
output [7:0]coordinate_x,
output [7:0]coordinate_y,
output [7:0]coefficient1,
output [7:0]coefficient2,
output [7:0]coefficient3,
output [7:0]coefficient4,
output reg en = 'd0
);
/高電平有效rst
reg [1:0]cnt = 'd0;
always @(posedge clk)
if(cnt == 'd3)
cnt <= 'd3;
else
cnt <= cnt + 'd1;
reg rst = 'd1;
always @(posedge clk)
if(cnt == 'd3)
rst <= 'd0;
else
rst <= 'd1;
//
localparam [1:0]IDLE = 2'b01;
localparam [1:0]START = 2'b10;
/
reg[1:0]next_state = 'd0;
reg[1:0]current_state = 'd0;
always @(posedge clk)
if(rst)高電平復(fù)位
current_state <= IDLE;
else
current_state <= next_state;
//
reg finish = 'd0;
always @(*)
case(current_state)
IDLE:begin
if(start)
next_state = START;
else
next_state = IDLE;
end
START:begin
if(finish)
next_state = IDLE;
else
next_state = START;
end
default:next_state = IDLE;
endcase
//
//reg en = 'd0;//目的坐標(biāo)計(jì)數(shù)器使能
always @(*)
case(current_state)
IDLE:begin
en = 'd0;
end
START:begin
en = 'd1;
end
default:en = 'd0;
endcase
///對目的圖像坐標(biāo)進(jìn)行計(jì)數(shù)
reg[5:0] pos_x = 'd0;/列計(jì)數(shù)
always@(posedge clk)
if(en)begin
if(pos_x == 'd63)
pos_x <= 'd0;
else
pos_x <= pos_x + 'd1;
end
else
pos_x <= pos_x;
reg[5:0] pos_y = 'd0;行計(jì)數(shù)
always @(posedge clk)
if(pos_x == 'd63)
pos_y <= pos_y + 'd1;?
else
pos_y <= pos_y;
//結(jié)束標(biāo)志
always@(posedge clk)
if((pos_x == 'd62)&&(pos_y == 'd63))///是pos_x==62而不是63
finish <= 'd1;
else
finish <= 'd0;
//通過pos_x、pos_y可以計(jì)算對應(yīng)源圖像位置的虛擬坐標(biāo)
reg [15:0]src_x = 'd0;///高8位表示整數(shù),低8位表示小數(shù)
reg [15:0]src_y = 'd0;///高8位表示整數(shù),低8位表示小數(shù)
//assign src_x = ((pos_x<<1 + 'd1)*src_width - 'd64 > 'd0)?(pos_x<<1 + 'd1)*src_width - 'd64:'d64-(pos_x<<1 + 'd1)*src_width;
//assign src_y = ((pos_y<<1 + 'd1)*src_width - 'd64 > 'd0)?(pos_y<<1 + 'd1)*src_width - 'd64:'d64-(pos_y<<1 + 'd1)*src_width;
wire [7:0]pos_xq;
wire [7:0]pos_yq;
assign pos_xq = pos_x<<1;
assign pos_yq = pos_y<<1;
///
always @(posedge clk)
if(pos_x == 'd0)begin
if(src_width > 'd64)
src_x <= src_width - 'd64;
else
src_x <= 'd64 - src_width;
end
else begin
if((pos_xq + 'd1)*src_width > 'd64)
src_x <= (pos_xq + 'd1)*src_width - 'd64;
else
src_x <= 'd64 - (pos_xq + 'd1)*src_width;
end
always @(posedge clk)
if(pos_y == 'd0)begin
if(src_width > 'd64)
src_y <= src_width - 'd64;
else
src_y <= 'd64 - src_width;
end
else begin
if((pos_yq + 'd1)*src_width > 'd64)
src_y <= (pos_yq + 'd1)*src_width - 'd64;
else
src_y <= 'd64 - (pos_yq + 'd1)*src_width;
end
//生成對應(yīng)坐標(biāo)
//wire [6:0]coordinate_x;
//wire [6:0]coordinate_y;
assign coordinate_x = src_x[14:7];
assign coordinate_y = src_y[14:7];
//生成對應(yīng)系數(shù)
//wire [7:0]coefficient1;
//wire [7:0]coefficient2;
//wire [7:0]coefficient3;
//wire [7:0]coefficient4;
assign coefficient2 = {1'b0,src_x[6:0]};
assign coefficient1 = 'd128 - coefficient2;
assign coefficient4 = {1'b0,src_y[6:0]};
assign coefficient3 = 'd128 - coefficient4;
endmodule
模擬上位機(jī)產(chǎn)生待縮放的數(shù)據(jù)模塊:
//模擬的輸入圖像指標(biāo)是30*30@56Hz
//
module self_generate(input clk,
output reg[7:0]data_o = 'd0,
output reg valid_zsc = 'd0
);
reg [10:0] cnt_h = 'd0;
reg [10:0] cnt_v = 'd0;
always @(posedge clk)
if(cnt_h == 'd799)
cnt_h <= 'd0;
else
cnt_h <= cnt_h + 'd1;
always @(posedge clk)
if(cnt_h == 'd799)begin
if(cnt_v == 'd524)
cnt_v <= 'd0;
else
cnt_v <= cnt_v + 'd1;
end
else
cnt_v <= cnt_v;
//reg dInValid = 'd0;
wire valid_1;
assign valid_1 = (((cnt_h >='d144)&&(cnt_h <='d173))&&((cnt_v >='d35)&&(cnt_v <='d64)))?'d1:'d0;/有效數(shù)據(jù)共30個(gè)
///
always @(posedge clk)
if(valid_1)
//if((cnt_h >='d144)&&(cnt_h <='d176))
//data_o <= 'hff;
//else if((cnt_h >='d177)&&(cnt_h <='d209))
//data_o <= 'h0f;
//else if((cnt_h >='d210)&&(cnt_h <='d242))
//data_o <= 'hff;
//else if((cnt_h >='d243)&&(cnt_h <='d275))
//data_o <= 'h0f;
//else if((cnt_h >='d276)&&(cnt_h <='d308))
//data_o <= 'hff;
//else if((cnt_h >='d309)&&(cnt_h <='d341))
//data_o <= 'h0f;
//else if((cnt_h >='d342)&&(cnt_h <='d374))
//data_o <= 'hff;
//else
//data_o <= 'h0f;
data_o <= data_o + 'd1;
else
data_o <= 'd0;
always @(posedge clk)
valid_zsc <= valid_1;
endmodule
待處理圖像緩存模塊:
module mem_control(input clk_wr,
input clk_rd,
input [7:0]coordinate_x,
input [7:0]coordinate_y,
input [7:0]din_a,
input en_a,
input [7:0]src_width,
input en_b,
output [7:0]doutbx,
output [7:0]doutbx1,
output [7:0]doutby,
output [7:0]doutby1,
output reg start = 'd0
);
wire [15:0]size;
assign size = src_width*src_width - 'd1;
wire [7:0]width;
assign width = src_width - 'd1;
//把數(shù)據(jù)復(fù)制四份,通過犧牲面積換取速度
reg [15:0]address_a = 'd0;
always @(posedge clk_wr)
if(en_a)begin
if(address_a == size)
address_a <= 'd0;
else
address_a <= address_a + 'd1;
end
else
address_a <= address_a;
如果數(shù)據(jù)緩存完畢,產(chǎn)生start標(biāo)志信號,表示可以進(jìn)行數(shù)據(jù)處理
always @(posedge clk_wr)
if(address_a == size)
start <= 'd1;
else
start <= 'd0;
wire [15:0]address_bx;
wire [15:0]address_bx1;
wire [15:0]address_by;
wire [15:0]address_by1;
assign address_bx = (coordinate_x == width)?coordinate_x + coordinate_y*src_width - 'd1:coordinate_x + coordinate_y*src_width;
assign address_bx1 = (coordinate_x == width)?address_bx:address_bx + 'd1;
assign address_by = (coordinate_y==width)?address_bx:coordinate_x + (coordinate_y+'d1)*src_width;
assign address_by1 = (coordinate_x == width)?address_by:address_by + 'd1;
// Instantiate the module
ram_module ram_1 (
.clk_wr(clk_wr),
.address_a(address_a),
.en_a(en_a),
.din_a(din_a),
.clk_rd(clk_rd),
.address_b(address_bx),
.en_b(en_b),
.doutb(doutbx)
);
// Instantiate the module
ram_module ram_2 (
.clk_wr(clk_wr),
.address_a(address_a),
.en_a(en_a),
.din_a(din_a),
.clk_rd(clk_rd),
.address_b(address_bx1),
.en_b(en_b),
.doutb(doutbx1)
);
// Instantiate the module
ram_module ram_3 (
.clk_wr(clk_wr),
.address_a(address_a),
.en_a(en_a),
.din_a(din_a),
.clk_rd(clk_rd),
.address_b(address_by),
.en_b(en_b),
.doutb(doutby)
);
// Instantiate the module
ram_module ram_4 (
.clk_wr(clk_wr),
.address_a(address_a),
.en_a(en_a),
.din_a(din_a),
.clk_rd(clk_rd),
.address_b(address_by1),
.en_b(en_b),
.doutb(doutby1)
);
endmodule
雙線性插值運(yùn)算單元模塊
module arithmetic_unit(input clk,
input [7:0]coefficient1,
input [7:0]coefficient2,
input [7:0]coefficient3,
input [7:0]coefficient4,
input en_b,
input [7:0]doutbx,
input [7:0]doutbx1,
input [7:0]doutby,
input [7:0]doutby1,
output reg[7:0]data_o = 'd0,
output reg en_o = 'd0
);
wire [23:0]data_1;
wire [23:0]data_2;
wire [23:0]data_3;
wire [23:0]data_4;
assign data_1 = coefficient1*coefficient3*doutbx;
/
assign data_2 = coefficient2*coefficient3*doutbx1;
/
assign data_3 = coefficient1*coefficient4*doutby;
assign data_4 = coefficient2*coefficient4*doutby1;
wire [23:0]data_a;
assign data_a = data_1 + data_2;
reg [23:0]data_aq = 'd0;
always @(posedge clk)
data_aq <= data_a;
wire [23:0]data_b;
assign data_b = data_3 + data_4;
reg [23:0]data_bq = 'd0;
always @(posedge clk)
data_bq <= data_b;
wire [23:0]data_oq;
assign data_oq = data_aq + data_bq;
always @(posedge clk)
data_o <= data_oq[21:14];
//en_b
reg en_b_q;
reg en_b_q1;
always@(posedge clk)begin
en_b_q <= en_b;
en_b_q1 <= en_b_q;
en_o <= en_b_q1;
end
endmodule
對運(yùn)算后的數(shù)據(jù)進(jìn)行緩存模塊:
module mem_64multi_64(input clk,
input [7:0]dina,
input ena,
input enb,
output reg start_en = 'd0,
output [7:0]doutb
);
/ram write
reg ena_q = 'd0;
always @(posedge clk)
ena_q <= ena;
reg [11:0]addra = 'd0;
always @(posedge clk)
if(ena_q)begin
if(addra == 'd4095)
addra <= 'd0;
else
addra <= addra + 'd1;
end
else
addra <= addra;
//ram read
reg [11:0]addrb = 'd0;
always @(posedge clk)
if(enb)begin
if(addrb == 'd4095)
addrb <= 'd0;
else
addrb <= addrb + 'd1;
end
else
addrb <= addrb;
//
always @(posedge clk)
if(addra == 'd4095)
start_en <= 'd1;
else
start_en <= start_en;
//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
ram64multi64 ram64multi64 (
.clka(clk), // input clka
.ena(ena_q), // input ena
.wea(1'b1), // input [0 : 0] wea
.addra(addra), // input [11 : 0] addra
.dina(dina), // input [7 : 0] dina
.clkb(clk), // input clkb
.enb(enb), // input enb
.addrb(addrb), // input [11 : 0] addrb
.doutb(doutb) // output [7 : 0] doutb
);
endmodule
輸出顯示模塊:
module vesa_out( input clk,
input start_en,
output v_sync,
output h_sync,
output de,
output reg de_o = 'd0
);
reg [10:0] cnt_h = 'd0;
reg [10:0] cnt_v = 'd0;
always @(posedge clk)
if(start_en)begin
if(cnt_h == 'd799)
cnt_h <= 'd0;
else
cnt_h <= cnt_h + 'd1;
end
else
cnt_h <= cnt_h;
always @(posedge clk)
if(cnt_h == 'd799)begin
if(cnt_v == 'd524)
cnt_v <= 'd0;
else
cnt_v <= cnt_v + 'd1;
end
else
cnt_v <= cnt_v;
assign de = (((cnt_h >='d200)&&(cnt_h <='d263))&&((cnt_v >='d135)&&(cnt_v <='d198)))?'d1:'d0;
always @(posedge clk)
de_o <= de;
assign h_sync = (cnt_h >= 'd97)?'d1:'d0;
assign v_sync = (cnt_v >= 'd3)?'d1:'d0;
endmodule
仿真模塊:
module top_tb;
// Inputs
reg clk;
// Outputs
wire [7:0] doutb;
wire de_o;
wire v_sync;
wire h_sync;
// Instantiate the Unit Under Test (UUT)
top uut (
.clk(clk),
.doutb(doutb),
.de_o(de_o),
.v_sync(v_sync),
.h_sync(h_sync)
);
initial begin
// Initialize Inputs
clk = 0;
// Wait 100 ns for global reset to finish
#100;
// Add stimulus here
end
always #5 clk = !clk;
endmodule
仿真結(jié)果如下:該仿真結(jié)果處理的是30*30大小圖像縮放成64*64圖像,輸入圖像的每一行數(shù)據(jù)都是1,2,3···30,仿真結(jié)果如下所示:
經(jīng)與matlab運(yùn)算結(jié)果做對比,結(jié)果完全一致。 ?
原文標(biāo)題:FPGA學(xué)習(xí)-基于FPGA的圖像實(shí)時(shí)縮放
文章出處:【微信公眾號:FPGA設(shè)計(jì)論壇】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
-
FPGA
+關(guān)注
關(guān)注
1629文章
21729瀏覽量
602986 -
計(jì)算機(jī)
+關(guān)注
關(guān)注
19文章
7488瀏覽量
87849 -
數(shù)字圖像處理
+關(guān)注
關(guān)注
7文章
103瀏覽量
18917
原文標(biāo)題:FPGA學(xué)習(xí)-基于FPGA的圖像實(shí)時(shí)縮放
文章出處:【微信號:gh_9d70b445f494,微信公眾號:FPGA設(shè)計(jì)論壇】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論