(1)图像平移的基本原理:计算每个像素点的移动向量,并将这些像素按照指定的方向和距离进行移动。
(2)平移向量包括水平和垂直分量,可以表示为(dx,dy),其中dx表示水平方向上的移动距离,dy表示垂直方向上的移动距离。
(3)经过平移后,新图像中的每个像素点在原图像中都有对应的像素点。图像平移使用软件开发语言实现很容易,但在FGPA中实现需要考虑缓存。
(4)matlab实现代码:
% 读取图像
% imread函数用于读取图像文件,支持多种格式如BMP、PNG、JPG等
img = imread('1_1920x1080.bmp');
% 获取图像尺寸信息
% size函数返回矩阵的维度,对于彩色图像返回[高度 宽度 通道数]
[rows, cols, channels] = size(img);
% 创建仿射变换矩阵
% 这里创建的是一个2x3的变换矩阵,用于定义图像的变换方式
% [1 0 300; - 第一行表示x方向的变换:x'= 1*x + 0*y + 300
% 0 1 200] - 第二行表示y方向的变换:y'= 0*x + 1*y + 200
% 这个矩阵表示将图像向右平移300像素,向下平移200像素
M = single([1, 0, 300; 0, 1, 200]);
% 执行仿射变换
% affine2d函数用于创建二维仿射变换对象
tform = affine2d(M'); % 注意MATLAB中需要转置变换矩阵
% imwarp函数执行图像变换
% OutputView选项指定输出图像的大小,这里保持与原图相同
res = imwarp(img, tform, 'OutputView', imref2d([rows cols]));
% 保存变换后的图像
% imwrite函数将图像保存到文件
% 第一个参数是图像数据,第二个参数是文件名
imwrite(res, 'result.bmp');
% 显示结果图像
% figure创建新的图形窗口
figure;
% subplot用于创建子图,这里创建1x2的子图布局
subplot(1,2,1);
imshow(img); % 显示原图
title('原始图像');
subplot(1,2,2);
imshow(res); % 显示变换后的图像
title('变换后的图像');
(5)FPGA仿真实现:
module move
(
input wire clk ,
input wire reset_n ,
input wire [10:0] img_width ,
input wire [10:0] img_height ,
input wire [10:0] img_x_start ,
input wire [10:0] img_y_start ,
input wire [23:0] img_data_i ,
output wire wr_ready ,
output reg valid_o ,
output reg [23:0] img_data_o
);
reg [11:0] h_cnt,v_cnt;
always@(posedge clk or negedge reset_n)
if(!reset_n)
h_cnt <= 12'd0;
else if(h_cnt == img_x_start + img_width - 1)
h_cnt <= 12'd0;
else
h_cnt <= h_cnt + 12'd1;
always@(posedge clk or negedge reset_n)
if(!reset_n)
v_cnt <= 12'd0;
else if((v_cnt == img_y_start + img_height - 1) && (h_cnt == img_x_start + img_width - 1))
v_cnt <= 12'd0;
else if(h_cnt == img_x_start + img_width - 1)
v_cnt <= v_cnt + 12'd1;
else
v_cnt <= v_cnt;
assign wr_ready = (h_cnt >= img_x_start) && (v_cnt >= img_y_start);
always@(posedge clk or negedge reset_n)
if(!reset_n)
valid_o <= 1'd0;
else if((h_cnt < img_width) && (v_cnt < img_height))
valid_o <= 1'd1;
else
valid_o <= 1'd0;
always@(posedge clk or negedge reset_n)
if(!reset_n)
img_data_o <= 24'd0;
else if((h_cnt < img_width) && (v_cnt < img_height) && (wr_ready))
img_data_o <= img_data_i;
else
img_data_o <= 24'd0;
endmodule
微调读写测试文件后,仿真出来的图像(与matlab仿真结果一致):
(6)FPGA实现
- 查看配置进程:report_property -all [get_runs impl_1]
-
写入DDR3部分不需要修改,可以沿用,但是读取部分需要修改,首先是结束地址,需要适配新的y轴偏移量
axi_ddr3_top axi_ddr3_top_inst ( .ddr3_clk (clk_320M ), .reset_n (rst_n ), .pingpang (1'd0 ), .ui_clk (ui_clk ), .ui_rst (ui_rst ), .wr_b_addr (32'd0 ), .wr_e_addr (IMG_LENGTH*IMG_WIDE*4 ), .wr_clk (clk ), .data_wren (data_wren ), .data_wr (data_wr ), .wr_rst (1'd0 ), .rd_b_addr (32'd0 ), .rd_e_addr (IMG_LENGTH*(IMG_WIDE-Y_OFFSET+1)*4 ), .rd_clk (clk_vga_2 ), .data_rden (lie >= Y_OFFSET ), .data_rd (data_rd ), .rd_rst (1'd0 ), .read_enable (1'd1 ), .rd_data_valid (), .ddr3_addr (ddr3_addr ), .ddr3_ba (ddr3_ba ), .ddr3_cas_n (ddr3_cas_n ), .ddr3_ck_n (ddr3_ck_n ), .ddr3_ck_p (ddr3_ck_p ), .ddr3_cke (ddr3_cke ), .ddr3_ras_n (ddr3_ras_n ), .ddr3_reset_n (ddr3_reset_n ), .ddr3_we_n (ddr3_we_n ), .ddr3_dq (ddr3_dq ), .ddr3_dqs_n (ddr3_dqs_n ), .ddr3_dqs_p (ddr3_dqs_p ), .init_calib_complete (init_calib_complete ), .ddr3_cs_n (ddr3_cs_n ), .ddr3_dm (ddr3_dm ), .ddr3_odt (ddr3_odt ) );
-
缓存行数据,使用一个24位,深度位2048的双口RAM去存储从DDR3中读出来的数据,然后在VGA模块扫描到对应位置时输出,即可。
hang_ram_2048 hang_ram_2048_inst ( .clka (clk_vga_2 ), .ena (1'd1 ), .wea (lie >= Y_OFFSET && reading ), .addra (buf_wr_addr ), .dina (line_buffer ), .clkb (clk_vga ), .enb (hang >= X_OFFSET ), .addrb (buf_rd_addr ), .doutb (ram_dout ) ); always @(posedge clk_vga_2 or negedge init_rst_n) begin if(!init_rst_n) begin last_data_rd <= 16'd0; buf_wr_addr <= 11'd0; reading <= 1'b0; line_buffer <= 24'd0; end else begin if(lie >= Y_OFFSET) begin if(!reading) begin // 第一次读取 last_data_rd <= data_rd; reading <= 1'b1; end else begin // 第二次读取 line_buffer <= {last_data_rd, data_rd[15:8]}; buf_wr_addr <= buf_wr_addr + 11'd1; reading <= 1'b0; end end else begin buf_wr_addr <= 11'd0; reading <= 1'd0; end end end // 行缓存读取控制,在这里实现偏移 always @(posedge clk_vga or negedge init_rst_n) begin if(!init_rst_n) buf_rd_addr <= 11'd0; else begin if(display_valid) buf_rd_addr <= buf_rd_addr + 1'd1; else buf_rd_addr <= 11'd0; end end assign display_valid = (hang >= X_OFFSET)&&(lie >= Y_OFFSET);
-
最终现象如下: