使用插值算法實現圖像縮放是數字圖像處理算法中經常遇到的問題。我們經常會將某種尺寸的圖像轉換為其他尺寸的圖像,如放大或者縮小圖像。由于在縮放的過程中會遇到浮點數,如何在FPGA中正確的處理浮點數運算是在FPGA中實現圖像縮放的關鍵。
一、插值算法原理
在圖像的縮放處理過程中,經常會用到插值算法,常見的插值算法包括最鄰近插值,雙線性插值,雙三次線性插值,蘭索斯插值等方法。其中,雙線性插值由于折中的插值效果和實現復雜度,運用較為廣泛。本文中僅介紹最臨近插值,重點討論如何在FPGA平臺上使用雙線性插值實現圖像的縮放。
1.1 最臨近插值---------最臨近插值介紹
講理論不如舉例子來的快,所以為了更好更快的理解最臨近插值,我們通過舉個簡單的例子來解釋最臨近插值是個什么神奇的東西。假如有一個3*3矩陣(一幅圖像其實就是矩陣),如下,我們把這個圖像叫做原圖(source image):
66 28 128
25 88 200
36 68 120
在矩陣中,坐標(x,y)是這樣確定的,矩陣的左上角的頂點為原點,從左到右為x軸,從上到下為y軸,如下所示:
圖1 圖像中坐標確定方式
假設我們想把這個3*3的原圖擴大成4*4(我們把這個4*4的圖像叫做目的圖像destination image)我們該如何做呢?首先當然是先把4*4的矩陣畫出來,如下所示:
????
? ???
????
????
矩陣畫出來后,接下來就要像未知數里填充像素點了。要填入的值如何計算呢,通過如下公式進行計算:
首先我們先來填充目的圖像 (0,0),套用上述公式可得到對應原圖像的坐標點,srcX =0,srcY= 0;找到原圖像中對應的坐標點的像素值,將該像素填充到目的圖像中,如下
66 ???
????
????
接下來填充目的圖像(1,0),仍然套用公式,srcX = 3/4,srcY = 0,結果發現得到的結果居然有小數,由于計算機中的像素點已經是最小單位,像素的坐標都是整數,沒有小數。這是只需要按照四舍五入的思想將小數坐標轉換為整數坐標即可.所以(3/4,0) ≈ (1,0),把原圖像中(1,0)點的像素值填入目的圖像(1,0)坐標處,得到如下結果:
66 28 ??
????
????
接下來重復上述過程,就可得到放大后的目的圖像,如下:
66 28 128 128
25 88 200 200
36 68 120 120
36 68 120 120
這種放大圖像的方法叫做最臨近插值算法,這是一種最基本最簡單的圖像縮放算法,該方法縮放后的圖像會出現失真現象。
1.2 雙線性插值算法
雙線性插值算法是一種比較好的縮放算法,它充分利用源圖中虛擬點四周的四個像素點來共同決定目標圖形中的一個像素值,因此縮放效果要比最臨近插值算法好的多。
雙線性插值算法的描述如下:
對于目的圖像中的某點坐標,通過乘以縮放倍數(srcwidth/dstwidth、srcheight/dstheight)得到一個浮點坐標(i+u,j+v)(其中i,j均為浮點坐標的整數部分;u,v為浮點坐標的小數部分),則這個浮點坐標(i+u,j+v)處的像素值f(i+u,j+v)可以由原圖像中的坐標點(i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所對應的四個像素值來共同決定,即
如1.1計算的目的像素坐標(1,0)對應源圖像的坐標為(3/4,0),即 i = 0,u = 0.75;j = 0,v = 0;即目的圖像(1,0)處的像素值由源圖像中(0,0)、(1,0)、(0,1)(1,1)四個坐標點對應的像素值來確定,代入上述公式即可計算出(3/4,0)處的像素值。
看了上述內容應該對最臨近插值算法和雙線性插值算法有一定的了解,其基本原理應該已經掌握。
在網上刷博客發現有好多大佬講解了雙線性插值算法的優化(雙線性插值算法總結),發現優化以后縮放效果更好一些,優化的具體細節就不再講解,具體優化過程就是使用
寫到這里雙線性插值算法的基本原理及優化方式已經基本講述完畢,那么,如何使用FPGA來實現雙線性插值呢?接下來我們進行分析如何使用FPGA來實現雙線性插值。
二、雙線性插值的FPGA實現遇到的問題及解決方案
通過以上分析,我們會發現使用FPGA實現雙線性插值有以下難點:
如何處理算法中出現的小數;
如何同時求出相鄰的四個點的坐標以及系數;
如何使用這四個坐標同時輸出相鄰的四個像素值;
接下來我們依次解決上述問題
2.1 如何處理算法中出現的小數
FPGA處理浮點數一般都是將浮點數轉化為定點數進行處理,我們在求srcX、srcY也按照此思想。由于FPGA中沒法進行小數表示,因此我們將srcX、srcY放大一定倍數變成整數,這個整數的高n位表示坐標點的整數部分(i或j),低m位表示小數部分(u或者v)。如我們要將30*30的圖像放大成64*64即srcwidth = srcheight = 30,dstwidth = dstheight =64(接下來工程實現也按照此例子來實現)。
位寬為16位。由于放大了128倍,所以srcX[6:0]、srcY[6:0]的低7位表示小數部分,即u = srcX[6:0],v=srcY[6:0];
其余高位部分表示整數部分,即i = srcX[15:7],j = srcY[15:7]。這樣就可以解決算法中出現的小數問題。
2.2如何同時求出相鄰的四個點的坐標以及系數
由1.1可知,我們可以求出第一個坐標點(i,j) = (srcX[15:7],srcY[15:7]),那么如何求出相鄰其它三個點的坐標呢?接下來需要求出其余三點(i+1,j)、(i,j+1)、(i+1,j+1)的坐標,
(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);
通過以上方式便可以很容易的求出四個點的坐標。接下來就要考慮如何使用這四個坐標同時讀取四個對應的像素值。
同時我們定義四個位寬為8位的系數變量coefficient1[7:0]、coefficient2[7:0]、coefficient3[7:0]、coefficient4[7:0],通過
coefficient1[7:0] = ‘d128 - coefficient2;(由于系數放大了128倍,所以是128-系數2,對應1-u)
coefficient2[7:0] = {1'b0,u} = {1'b0,srcX[6:0]};
coefficient3[7:0] = ‘d128 - coefficient4;(由于系數放大了128倍,所以是128-系數4,對應1-v)
coefficient4[7:0] = {1'b0,v} = {1'b0,srcY[6:0]};
為什么定義的系數變量為8位而不是7位,這是因為系數變量的最大值為’d128,8位位寬才能表示‘d128。
接下來使用上述求出的四個坐標求出對應的坐標的像素值,再套用公式
即可求出目的圖像對應位置放大后的像素值。
通過以上分析,可以將公式變形為如下形式,求出對應點的像素值后直接使用以下公式即可:
求得該像素值之后還需要將該像素值除以128*128才是所得的實際結果。
2.3 如何使用這四個坐標同時輸出相鄰的四個像素值
通過2.2分析可以同時求出四個像素點的坐標,但是如何通過這四個坐標同時求出對應的像素值呢?由于待縮放的數據是先緩存進RAM的,如果待縮放的圖像數據僅僅緩存在一個RAM里,是不可能通過四個像素點的坐標同時訪問這個RAM,即不可能同時求出對應的四個像素點的值。所以,可以通過犧牲面積換取速度的方式,即將RAM復制4次,每個RAM都緩存整個待縮放的圖像數據,這樣四個像素點的坐標就可以通過訪問不同的RAM來同時訪問對應的四個像素值了。雖然犧牲了FPGA內部的存儲資源,但是提升了雙線性插值算法的速度。
三 、雙線性插值的FPGA實現
通過第二節內容的分析可知,使用FPGA實現雙線性插值主要包含以下三個模塊:生成目的圖像對應原圖像坐標和系數模塊、待處理圖像緩存模塊、雙線性插值運算單元模塊。其數據流向圖如下圖所示:
圖2 FPGA實現雙線性插值數據流向圖
圖2中虛框部分是圖像裁剪模塊,這里不討論圖像裁剪功能的實現,僅僅討論圖像縮放功能的實現。
整個實現的過程描述如下:
上位機將待縮放的圖像數據送進待處理圖像緩存模塊,該模塊將待處理的數據復制四份
待1中數據緩存完畢,開始生成目的圖像對應原圖像坐標和系數;
將生成的坐標送給待處理圖像緩存模塊進行數據的訪問,將讀取的數據送給雙線性插值運算單元;
將生成的系數送給雙線性插值運算單元,與相應的像素值進行數學運算,實現雙線性插值功能。
實現雙線性插值功能的代碼如下所示:
頂層模塊:
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
生成對應的坐標和系數模塊:
//該模塊是用來計算源圖像的虛擬坐標,由于源圖像和目的圖像都是正方形,所以只考慮一個縮放倍數即可
//
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,///數據緩存滿了以后才可以進行計算
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)高電平復位
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;//目的坐標計數器使能
always @(*)
case(current_state)
IDLE:begin
en = 'd0;
end
START:begin
en = 'd1;
end
default:en = 'd0;
endcase
///對目的圖像坐標進行計數
reg[5:0] pos_x = 'd0;/列計數
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;行計數
always @(posedge clk)
if(pos_x == 'd63)
pos_y <= pos_y + 'd1;?
else
pos_y <= pos_y;
//結束標志
always@(posedge clk)
if((pos_x == 'd62)&&(pos_y == 'd63))///是pos_x==62而不是63
finish <= 'd1;
else
finish <= 'd0;
//通過pos_x、pos_y可以計算對應源圖像位置的虛擬坐標
reg [15:0]src_x = 'd0;///高8位表示整數,低8位表示小數
reg [15:0]src_y = 'd0;///高8位表示整數,低8位表示小數
//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
//生成對應坐標
//wire [6:0]coordinate_x;
//wire [6:0]coordinate_y;
assign coordinate_x = src_x[14:7];
assign coordinate_y = src_y[14:7];
//生成對應系數
//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
模擬上位機產生待縮放的數據模塊:
//模擬的輸入圖像指標是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;/有效數據共30個
///
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;
//把數據復制四份,通過犧牲面積換取速度
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;
如果數據緩存完畢,產生start標志信號,表示可以進行數據處理
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
雙線性插值運算單元模塊
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
對運算后的數據進行緩存模塊:
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
仿真結果如下:該仿真結果處理的是30*30大小圖像縮放成64*64圖像,輸入圖像的每一行數據都是1,2,3···30,仿真結果如下所示:
經與matlab運算結果做對比,結果完全一致。 ?
原文標題:FPGA學習-基于FPGA的圖像實時縮放
文章出處:【微信公眾號:FPGA設計論壇】歡迎添加關注!文章轉載請注明出處。
-
FPGA
+關注
關注
1629文章
21729瀏覽量
602993 -
計算機
+關注
關注
19文章
7488瀏覽量
87849 -
數字圖像處理
+關注
關注
7文章
103瀏覽量
18917
原文標題:FPGA學習-基于FPGA的圖像實時縮放
文章出處:【微信號:gh_9d70b445f494,微信公眾號:FPGA設計論壇】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論