FPGA实现图像几何变换:旋转

  旋转一般是指将图像围绕某一指定点旋转一定的角度。 旋转通常也会改变图像的大小,和图像平移的处理一样,可以把转出显示区域的图像截去,也可以改变输出图像的大小以扩展显示范围。

  本篇博客实现的旋转算法改编自上一篇博客的镜像,因此不说那么多理论,直接记录重点。

一、MATLAB实现

1、函数法

%--------------------------------------------------------------------------
%                       函数法旋转
%--------------------------------------------------------------------------
clc;
clear all;
RGB = imread('monkey.jpg'); %读取图像

L_rotate  = imrotate(RGB,90,'crop');
R_rotate  = imrotate(RGB,-90,'crop');
rotate    = imrotate(RGB,180,'crop');

subplot(2,2,1),imshow(RGB);      title('原图');
subplot(2,2,2),imshow(L_rotate); title('水平镜像');
subplot(2,2,3),imshow(R_rotate); title('垂直镜像');
subplot(2,2,4),imshow(rotate);title('水平垂直镜像');

运行结果:

2、公式法

%--------------------------------------------------------------------------
%                       公式法旋转
%--------------------------------------------------------------------------
clc;
clear all;
RGB = imread('monkey.jpg'); %读取图像
 
[ROW,COL,N] = size(RGB);

L_rotate  = uint8(zeros(ROW, COL,N)); %Left  rotate
R_rotate  = uint8(zeros(ROW, COL,N)); %Right rotate
rotate    = uint8(zeros(ROW, COL,N)); %180° rotate

%左转90度
for i =1:ROW
    for j=1:COL
        for k=1:N
            x = COL-j+1;
            y = i;
            z = k;
            L_rotate(x,y,z) =RGB(i,j,k);
        end
    end
end

%右转90度
for i =1:ROW
    for j=1:COL
        for k=1:N
            x = j;
            y = ROW-i+1;
            z = k;
            R_rotate(x,y,z) =RGB(i,j,k);
        end
    end
end

%旋转180度
for i =1:ROW
    for j=1:COL
        for k=1:N
            x = ROW-i+1;
            y = COL-j+1;
            z = k;
            rotate(x,y,z) =RGB(i,j,k);
        end
    end
end

subplot(2,2,1),imshow(RGB);      title('原图');
subplot(2,2,2),imshow(L_rotate); title('左转90度');
subplot(2,2,3),imshow(R_rotate); title('右转90度');
subplot(2,2,4),imshow(rotate);   title('旋转180度');

运行结果:

   两种方法得到的结果一致,表明公式正确。

 

二、FPGA实现

   本次实验基于镜像改编而来,其他模块一致,仅旋转算法部分不同。

//**************************************************************************
// *** 名称 : Rotate.v
// *** 作者 : xianyu_FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2019-06-23
// *** 描述 : 旋转操作
//**************************************************************************
module Rotate
//========================< 端口 >==========================================
(
//system --------------------------------------------
input   wire                rst_n                   , //复位,低电平有效
//uart ----------------------------------------------
input   wire                wr_clk                  , //50m
input   wire    [15:0]      din                     ,
input   wire                din_vld                 ,
//key -----------------------------------------------
input   wire                key_vld                 , //按键切换模式
//TFT_driver ----------------------------------------
input   wire                rd_clk                  , //9m
input   wire    [ 9:0]      TFT_x                   , //得到显示区域横坐标
input   wire    [ 9:0]      TFT_y                   , //得到显示区域纵坐标
output  wire    [15:0]      TFT_data                  //输出图像数据
);
//========================< 参数 >==========================================
parameter COL               = 10'd140               ; //图片长度
parameter ROW               = 10'd140               ; //图片高度
parameter IMG_x             = 10'd170               ; //图片起始横坐标
parameter IMG_y             = 10'd66                ; //图片起始纵坐标
//========================< 信号 >==========================================
reg     [15:0]              buffer[COL*ROW-1:0]     ; //类似RAM
reg     [14:0]              wr_addr                 ;
reg     [14:0]              rd_addr                 ;
//---------------------------------------------------
wire                        rd_en                   ;
reg							rd_en_r					;
reg     [ 9:0]              cnt_col                 ;
wire                        add_cnt_col             ;
wire                        end_cnt_col             ;
reg     [ 9:0]              cnt_row                 ;
wire                        add_cnt_row             ;
wire                        end_cnt_row             ;
//---------------------------------------------------
reg     [ 1:0]              mode                    ;
reg     [ 9:0]              rotate_x                ;
reg     [ 9:0]              rotate_y                ;
//==========================================================================
//==    缓存buffer,写操作
//==========================================================================
//写数据
//---------------------------------------------------
always @(posedge wr_clk) begin
    buffer[wr_addr] <= din;
end

//写地址
//---------------------------------------------------
always @(posedge wr_clk or negedge rst_n) begin
    if(!rst_n) begin
        wr_addr <= 'd0;
    end
    else if(din_vld) begin
        wr_addr <= wr_addr + 1'b1;
    end
end
//==========================================================================
//==    行列规划
//==========================================================================
//读使能,确定显示位置
//---------------------------------------------------
assign rd_en = (TFT_x >= IMG_x) && (TFT_x < IMG_x + COL) &&
               (TFT_y >= IMG_y) && (TFT_y < IMG_y + ROW)
               ? 1'b1 : 1'b0;
               
//行计数
//---------------------------------------------------
always @(posedge rd_clk or negedge rst_n) begin
    if(!rst_n)
        cnt_col <= 10'd0;
    else if(add_cnt_col) begin
        if(end_cnt_col)
            cnt_col <= 10'd0;
        else
            cnt_col <= cnt_col + 10'd1;
    end
end

assign add_cnt_col = rd_en;
assign end_cnt_col = add_cnt_col && cnt_col== COL-10'd1;

//列计数
//---------------------------------------------------
always @(posedge rd_clk or negedge rst_n) begin
    if(!rst_n)
        cnt_row <= 10'd0;
    else if(add_cnt_row) begin
        if(end_cnt_row)
            cnt_row <= 10'd0;
        else
            cnt_row <= cnt_row + 10'd1;
    end
end

assign add_cnt_row = end_cnt_col;
assign end_cnt_row = add_cnt_row && cnt_row== ROW-10'd1;
//==========================================================================
//==    镜像操作,读地址重规划
//==========================================================================
always @(posedge rd_clk or negedge rst_n) begin
    if(!rst_n) begin
        mode <= 2'b00;
    end
    else if(key_vld) begin
        mode <= mode + 1'b1;
    end
end

always @(*) begin
    case(mode)
        2'b00   : begin                                     //原图
                    rotate_x = cnt_col;
                    rotate_y = cnt_row;
                  end
        2'b01   : begin                                     //右转90度
                    rotate_x = cnt_row;
                    rotate_y = (COL-1) - cnt_col;
                  end
        2'b10   : begin                                     //旋转180度
                    rotate_x = (COL-1) - cnt_col;
                    rotate_y = (ROW-1) - cnt_row;
                  end
        2'b11   : begin                                     //左转90度(右转270度)
                    rotate_x = (ROW-1) - cnt_row;
                    rotate_y = cnt_col;
                  end
        default : begin
                    rotate_x = cnt_col;
                    rotate_y = cnt_row;
                  end
    endcase
end
//==========================================================================
//==    缓存buffer,读操作
//==========================================================================
always @(posedge rd_clk or negedge rst_n) begin
    if(!rst_n)
        rd_addr <= 'd0;
    else
        rd_addr <= rotate_y * COL + rotate_x;
end

always @(posedge rd_clk) begin
		rd_en_r <= rd_en;
end

assign TFT_data = rd_en_r ? buffer[rd_addr] : 16'hffff;



endmodule

三、上板验证

  总共4种模式,模式0、1、2、3 分别得到如下结果:

 

  把这4副图也按MATLAB的样子拼到一块看看吧:

  和上面的 MATLAB 实验结果对比,可以看到此次图像旋转实验成功。

  视频演示如下所示:

 

 

后记

  本博客整理的旋转算法仅支持90度周期的旋转,任意角度的旋转算法还待进一步研究。此外本次所选图片为正方形,如果是长方形图片,则必须在设计旋转算法的模块好好思考如何设计坐标和数值的对应关系。

 

参考资料:[1] OpenS Lee:FPGA开源工作室(公众号)

posted @ 2020-03-10 21:51  咸鱼IC  阅读(3777)  评论(3)    收藏  举报