(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) beginif(!init_rst_n) beginlast_data_rd <= 16'd0;buf_wr_addr <= 11'd0;reading <= 1'b0;line_buffer <= 24'd0;endelse beginif(lie >= Y_OFFSET) beginif(!reading) begin // 第一次读取last_data_rd <= data_rd;reading <= 1'b1;endelse begin // 第二次读取line_buffer <= {last_data_rd, data_rd[15:8]};buf_wr_addr <= buf_wr_addr + 11'd1;reading <= 1'b0;endendelse beginbuf_wr_addr <= 11'd0;reading <= 1'd0;endend end// 行缓存读取控制,在这里实现偏移 always @(posedge clk_vga or negedge init_rst_n) beginif(!init_rst_n) buf_rd_addr <= 11'd0;else beginif(display_valid)buf_rd_addr <= buf_rd_addr + 1'd1;else buf_rd_addr <= 11'd0;end endassign display_valid = (hang >= X_OFFSET)&&(lie >= Y_OFFSET);
-
最终现象如下: