系列文章目录
双线性插值缩放算法原理以及matlab与verilog的实现(一)
class="toc">
双线性插值缩放算法原理以及matlab与verilog的实现(一)
开发平台:vivado2020.1
开发芯片:xc7k410tffv900-2
在上一篇文章,我们学会了双线性缩放插值算法的原理以及matlab的实现,以及VGA时序的简单实现。
本文实现目的:用verilog实现双线性插值算法,将一张112 ×103大小图片,放大两倍至224 ×206,然后通过VGA显示出来
双线性插值算法公式如下:
f ( x , y ) = f ( Q 11 ) ( 1 − u ) ( 1 − v ) + f ( Q 21 ) u ( 1 − v ) + f ( Q 12 ) ( 1 − u ) v + f ( Q 22 ) u v f(x,y)={f(Q_{11})}{(1-u)(1-v)}+{f(Q_{21})}{u(1-v)}+{f(Q_{12})}{(1-u)v}+{f(Q_{22})}{uv} f(x,y)=f(Q11)(1−u)(1−v)+f(Q21)u(1−v)+f(Q12)(1−u)v+f(Q22)uv
由于在图像处理中,一般将左上角的像素点定义为坐标原点,因此将上述的图片变换一下坐标,以及命名顺序,就变为了:
f ( x , y ) = f ( Q 11 ) ( 1 − u ) ( 1 − v ) + f ( Q 21 ) u ( 1 − v ) + f ( Q 12 ) ( 1 − u ) v + f ( Q 22 ) u v f(x,y)={f(Q_{11})}{(1-u)(1-v)}+{f(Q_{21})}{u(1-v)}+{f(Q_{12})}{(1-u)v}+{f(Q_{22})}{uv} f(x,y)=f(Q11)(1−u)(1−v)+f(Q21)u(1−v)+f(Q12)(1−u)v+f(Q22)uv
现在我们知道,我们用一张已知各点像素值的图片(源图像),求出一张放大两倍的图片(目标图像),但是目标各点像素值都是未知。双线性插值算法核心就是:用已知的四个像素点的值算出所求目标像素点的值。因此步骤如下:
根据目标像素点位置乘以缩放系数(
s
r
c
w
i
d
t
h
d
s
t
w
i
d
t
h
,
s
r
c
h
e
i
g
h
t
d
s
t
h
e
i
g
h
t
\frac{srcwidth}{dstwidth},\frac{srcheight}{dstheight}
dstwidthsrcwidth,dstheightsrcheight)得到一个浮点坐标
(
i
+
u
,
j
+
v
)
(i+u,j+v)
(i+u,j+v)(其中
i
i
i,
j
j
j为浮点坐标的整数部分;
u
u
u,
v
v
v为浮点坐标的小数部分),而这个浮点坐标
(
i
+
u
,
j
+
v
)
(i+u,j+v)
(i+u,j+v)的像素值
f
(
i
+
u
,
j
+
v
)
f(i+u,j+v)
f(i+u,j+v),就是上图中所要求的p点值,所以由公式可知,
f
(
p
)
f(p)
f(p)可由
f
(
Q
11
f(Q_{11}
f(Q11)、
f
(
Q
12
f(Q_{12}
f(Q12)、
f
(
Q
21
f(Q_{21}
f(Q21)、
f
(
Q
22
f(Q_{22}
f(Q22)求出。而
f
(
Q
11
f(Q_{11}
f(Q11)、
f
(
Q
12
f(Q_{12}
f(Q12)、
f
(
Q
21
f(Q_{21}
f(Q21)、
f
(
Q
22
f(Q_{22}
f(Q22)的坐标分别是
(
i
,
j
)
(i,j)
(i,j)、
(
i
+
1
,
j
)
(i+1,j)
(i+1,j)、
(
i
,
j
+
1
)
(i,j+1)
(i,j+1)、
(
i
+
1
,
j
+
1
)
(i+1,j+1)
(i+1,j+1)。
找源图像坐标公式如下:
s
r
c
X
=
d
s
t
X
×
s
r
c
w
i
d
t
h
d
s
t
w
i
d
t
h
srcX=dstX×\frac{srcwidth}{dstwidth}
srcX=dstX×dstwidthsrcwidth
s
r
c
Y
=
d
s
t
Y
×
s
r
c
h
e
i
g
h
t
d
s
t
h
e
i
g
h
t
srcY=dstY×\frac{srcheight}{dstheight}
srcY=dstY×dstheightsrcheight
例如:将图像放大2倍
在查阅一些资料中发现,将坐标公式优化一下,图像显示效果会更好,具体公式修改如下:
s
r
c
X
=
(
d
s
t
X
+
0.5
)
×
s
r
c
w
i
d
t
h
d
s
t
w
i
d
t
h
−
0.5
srcX=(dstX+0.5)×\frac{srcwidth}{dstwidth}-0.5
srcX=(dstX+0.5)×dstwidthsrcwidth−0.5
s
r
c
Y
=
(
d
s
t
Y
+
0.5
)
×
s
r
c
h
e
i
g
h
t
d
s
t
h
e
i
g
h
t
−
0.5
srcY=(dstY+0.5)×\frac{srcheight}{dstheight}-0.5
srcY=(dstY+0.5)×dstheightsrcheight−0.5
例如:还是上面同样例子将图像放大2倍
% 读取图像
img = imread('C:\Users\Administrator\Desktop\qq.jpg');
% 获取图像尺寸
[height, width, ~] = size(img);
% 打开 COE 文件以写入数据
fileID = fopen('C:\Users\Administrator\Desktop\save.coe', 'w');
% 写入 COE 文件头部信息
fprintf(fileID, 'memory_initialization_radix=16;\n');
fprintf(fileID, 'memory_initialization_vector=\n');
% 遍历图像像素并将像素值写入 COE 文件
for i = 1:height
for j = 1:width
% 将像素值写入 COE 文件
fprintf(fileID, '%02X%02X%02X,\n', img(i,j,1), img(i,j,2), img(i,j,3));
end
end
% 关闭 COE 文件
fclose(fileID);
disp('COE file generation complete.');
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
得到coe文件
打开ROM ip,生成一个位宽24位。深度112×103=11536的ROM,初始化文件为此coe文件
vga模块代码和上篇文章一样,只是加了读ROM的操作,代码如下:
//显示ROM缓存的图片
module vga_ctrl
(
input clk ,
input [23:0] data_in ,
output reg [13:0] rd_rom_addr,
output reg vs =1'b0 ,
output reg hs =1'b0 ,
output reg [23:0] data_out
);
parameter hcnt_max =1100;//行扫描是对列计数,最大值
parameter vcnt_max =2250;//列扫描是对行计数,最大值
parameter H_ACTIVE =960; //行数据有效时间
parameter H_FRONT_PORCH =44 ; //行前沿时间
parameter H_SYNC_TIME =22;//行同步信号时间
parameter H_BACK_PORCH =74;//行消隐后肩时间
parameter V_ACTIVE =2160;//场数据有效时间
parameter V_FRONT_PORCH =8 ; //场前沿时间
parameter V_SYNC_TIME =10 ; //场同步信号时间
parameter V_BACK_PORCH =72;//场后沿时间
reg [10:0] hcnt ='d0 ;
reg [11:0] vcnt ='d0 ;
reg [10:0] pix_x ='d0 ;//当前显示像素点x坐标
reg [11:0] pix_y ='d0 ; //当前显示像素点y坐标
//对行扫描进行计数
always @(posedge clk)begin
if(hcnt == hcnt_max - 1 )
hcnt <= 0;
else
hcnt <= hcnt +'d1;
end
//对列扫描进行计数
always @(posedge clk)
begin
if((vcnt == vcnt_max-1)&&(hcnt == hcnt_max -1))
vcnt <= 0;
else if(hcnt == hcnt_max -1)
vcnt <= vcnt +'d1;
else
vcnt <= vcnt;
end
always@(posedge clk)begin
if(hcnt < H_SYNC_TIME)
hs <= 1;
else
hs <= 0;
end
always@(posedge clk)begin
if(vcnt < V_SYNC_TIME)
vs <= 1;
else
vs <= 0;
end
//整个屏幕有效显示区域
always @(posedge clk) begin
if(((hcnt >= H_SYNC_TIME +H_BACK_PORCH)&&(hcnt < H_SYNC_TIME +H_BACK_PORCH +H_ACTIVE))&&((vcnt >=V_SYNC_TIME + V_BACK_PORCH)&&(vcnt<V_SYNC_TIME+V_BACK_PORCH+V_ACTIVE)))begin
de <= 1'b1;
end
else begin
de <= 1'b0;
end
end
//当前显示的像素点的位置
always@(posedge clk)begin
if(de == 1'b1)begin
pix_x <= hcnt - H_SYNC_TIME - H_BACK_PORCH;
pix_y <= vcnt - V_SYNC_TIME - V_BACK_PORCH;
end
else begin
pix_x <= 'd0;
pix_y <= 'd0;
end
end
//提前一拍读ROM地址
always @(posedge clk) begin
if((pix_x >= 278)&&(pix_x <390)&&((pix_y >=1080)&&(pix_y < 1183)))begin
if(rd_rom_addr <'d11535)
rd_rom_addr <= rd_rom_addr + 1;
else
rd_rom_addr <= 'd0;
end
else begin
rd_rom_addr <= rd_rom_addr;
end
end
always @(posedge clk) begin
if((pix_x >= 279)&&(pix_x <391)&&((pix_y >=1080)&&(pix_y < 1183)))
data_out <= data_in;
else
data_out <= 96'h800080_800080_800080_800080;
end
endmodule
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
屏幕显示效果:
VGA模块以及ROM数据皆正常,至于为什么图片偏绿,可能是因为显示器YUV和RGB色域转换的问题,后续文章会说明,现在暂时不管。
module bilinear_calculate(
input clk ,
input rst_n ,
input [7:0] cur_u_dec , //输入的u
input [7:0] cur_v_dec , //输出的v
input pix_en,
input [7:0] i_j_pix , //i_j_pix=f(i,j)
input [7:0] i_j_1_pix , //i_j_1_pix=f(i,j+1)
input [7:0] i_1_j_pix , //i_1_j_pix=f(i+1,j)
input [7:0] i_1_j_1_pix , //i_1_j_1_pix=f(i+1,j+1)
output reg [7:0] post_cal_data,
output reg post_cal_data_valid =1'b0
);
//f(x,y)= f(0,0)*(1-u)(1-v)+f(1,0)*u*(1-v)+f(0,1)*(1-u)*v+f(l,1)*u*v
reg [7:0] cur_u_dec_reg=8'd0 ;
reg [7:0] cur_v_dec_reg=8'd0 ;
reg [8:0] u_1 =9'd0; //1-u
reg [8:0] v_1 =9'd0; //1-v
reg [8:0] u =9'd0; //u
reg [8:0] v =9'd0; //v
reg [8:0] u_1_reg =9'd0;
reg [8:0] v_1_reg =9'd0;
reg [8:0] u_reg =9'd0;
reg [8:0] v_reg =9'd0;
reg [8:0] u_1_reg_copy =9'd0;
reg [8:0] v_1_reg_copy =9'd0;
reg [8:0] u_reg_copy =9'd0;
reg [8:0] v_reg_copy =9'd0;
wire [17:0] u_v ;
wire [17:0] u_1_v_1 ;
wire [17:0] u_v_1 ;
wire [17:0] u_1_v ;
reg [17:0] u_v_reg =18'd0;
reg [17:0] u_1_v_1_reg =18'd0;
reg [17:0] u_v_1_reg =18'd0;
reg [17:0] u_1_v_reg =18'd0;
reg [7:0] i_j_pix_reg =8'd0;
reg [7:0] i_j_1_pix_reg =8'd0;
reg [7:0] i_1_j_pix_reg =8'd0;
reg [7:0] i_1_j_1_pix_reg =8'd0;
wire [25:0] Fi_j_pix0 ;
wire [25:0] Fi_j_1_pix0 ;
wire [25:0] Fi_1_j_pix0 ;
wire [25:0] Fi_1_j_1_pix0 ;
reg [25:0] Fi_j_pix0_reg =26'd0;
reg [25:0] Fi_j_1_pix0_reg =26'd0;
reg [25:0] Fi_1_j_pix0_reg =26'd0;
reg [25:0] Fi_1_j_1_pix0_reg =26'd0;
reg [25:0] F_i_u_j_v1_pix0 =26'd0;
reg [25:0] F_i_u_j_v2_pix0 =26'd0;
reg [26:0] F_i_u_j_v_pix0 =27'd0;
reg [5:0] pix_en_reg ='d0 ;
always@(posedge clk )begin
cur_u_dec_reg <= cur_u_dec;
cur_v_dec_reg <= cur_v_dec;
end
always@(posedge clk )begin
u_1 <= {1'b0,~cur_u_dec_reg} + 1;
v_1 <= {1'b0,~cur_v_dec_reg} + 1;
u <= cur_u_dec_reg;
v <= cur_v_dec_reg;
end
always@(posedge clk )begin
u_1_reg <= u_1;
v_1_reg <= v_1;
u_reg <= u;
v_reg <= v;
end
always@(posedge clk )begin
u_1_reg_copy <= u_1;
v_1_reg_copy <= v_1;
u_reg_copy <= u;
v_reg_copy <= v;
end
mult_9_9 mult_u_v (
.CLK(clk),
.A(u_reg),
.B(v_reg),
.P(u_v)
);
mult_9_9 mult_u_1_v_1 (
.CLK(clk),
.A(u_1_reg),
.B(v_1_reg),
.P(u_1_v_1)
);
mult_9_9 mult_u_1_v (
.CLK(clk),
.A(u_1_reg_copy),
.B(v_reg_copy),
.P(u_1_v)
);
mult_9_9 mult_u_v_1 (
.CLK(clk),
.A(u_reg_copy),
.B(v_1_reg_copy),
.P(u_v_1)
);
always@(posedge clk)begin
u_v_reg <= u_v;
u_1_v_1_reg <= u_1_v_1;
u_v_1_reg <= u_v_1;
u_1_v_reg <= u_1_v;
end
// pix_cal
always@(posedge clk)begin
i_j_pix_reg <= i_j_pix;
i_j_1_pix_reg <= i_j_1_pix;
i_1_j_pix_reg <= i_1_j_pix;
i_1_j_1_pix_reg <= i_1_j_1_pix;
end
mult_18_8 mult1 (
.CLK(clk),
.A(u_1_v_1_reg),
.B(i_j_pix_reg),
.P(Fi_j_pix0)
);
mult_18_8 mult2 (
.CLK(clk),
.A(u_1_v_reg),
.B(i_j_1_pix_reg),
.P(Fi_j_1_pix0)
);
mult_18_8 mult3(
.CLK(clk),
.A(u_v_1_reg),
.B(i_1_j_pix_reg),
.P(Fi_1_j_pix0)
);
mult_18_8 mult4(
.CLK(clk),
.A(u_v_reg),
.B(i_1_j_1_pix_reg),
.P(Fi_1_j_1_pix0)
);
always@(posedge clk)begin
Fi_j_pix0_reg <= Fi_j_pix0;
Fi_j_1_pix0_reg <= Fi_j_1_pix0;
Fi_1_j_pix0_reg <= Fi_1_j_pix0;
Fi_1_j_1_pix0_reg <= Fi_1_j_1_pix0;
end
always@(posedge clk)begin
F_i_u_j_v1_pix0 <= Fi_j_pix0_reg + Fi_j_1_pix0_reg;
F_i_u_j_v2_pix0 <= Fi_1_j_pix0_reg + Fi_1_j_1_pix0_reg;
end
always@(posedge clk)begin
F_i_u_j_v_pix0 <= {1'b0,F_i_u_j_v1_pix0} + {1'b0,F_i_u_j_v2_pix0};
end
always@(posedge clk)begin
if(F_i_u_j_v_pix0[26 : 24] == 'd0)
post_cal_data <= F_i_u_j_v_pix0[23 : 16];
else
post_cal_data <= 8'hff;
end
always @(posedge clk) begin
pix_en_reg <= {pix_en_reg[4:0],pix_en};
if(pix_en_reg[5] == 1'b1)
post_cal_data_valid <= 1'b1;
else
post_cal_data_valid <= 1'b0;
end
endmodule
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
再修改一下vga模块,让它显示两部分,左边是原始图片,右边显示放大图片。效果如下:
至此,我们完成了双线性插值缩放算法的实现。
评论记录:
回复评论: