自学内容网 自学内容网

数字图像处理(15):图像平移

        (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]
  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              )  
    );
  2. 缓存行数据,使用一个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);  
  3. 最终现象如下:


原文地址:https://blog.csdn.net/2301_80417284/article/details/144217552

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!