FPGA实现图像几何变换:平移
图像平移就是将图像中所有的点按照指定的平移量水平或者垂直移动。
设(x0,y0)为图像上的一点,图像水平平移量为Tx,垂直平移量为Ty,如图所示:

则平移之后的点坐标(x1,y1)变为:

用矩阵表示为:

对变换矩阵求逆,可以得到逆变换:

即:

这样,平移后的目标图像中的每一点都可以在原图像中找到对应的点。例如,将新图中的(i,j)像素,代入上面的方程组,可以求出对应原图中的像素(i-Tx, j-Ty). 而此时如果Tx大于 i 或大于 j,则点 (i-Tx, j-Ty)超出了原图的范围,可以直接将它的像素值统一设置为0或者255。
对于原图中被移出图像显示区域的点通常也有两种处理方法:直接丢弃或者通过适当增加目标图像的尺寸(将新生成的图像宽度增加Tx,高度增加Ty)的方法使新图像中能够包含这些点。在稍后给出的程序实现中,我们采用了第-一种处理方法。
一、MATLAB实现
MATLAB中没有直接用于图像平移的函数,这里给出一个基于灰度形态学的图像平移实现。
%-------------------------------------------------------------------------- % 图像平移 %-------------------------------------------------------------------------- clc; clear all; RGB = imread('monkey.jpg'); %读取图像 se = translate(strel(1),[-30,20]); %strel创建形态学结构元素,然后偏移 trans = imdilate(RGB,se); subplot(1,2,1),imshow(RGB); title('原图'); subplot(1,2,2),imshow(trans);title('平移');
点击运行,得到如下结果:

二、FPGA实现
平移的实现和前面相比,多了两个偏移参数,由于偏移量有正有负,因此定义成有符号数,如下所示:
parameter COL = 10'd140 ; //图片长度 parameter ROW = 10'd140 ; //图片高度 parameter IMG_x = 10'd170 ; //图片起始横坐标 parameter IMG_y = 10'd66 ; //图片起始纵坐标 //--------------------------------------------------- parameter signed x_add = 20 ; //x轴偏移 parameter signed y_add = -30 ; //y轴偏移
关于平移的算法总的来说比较简单,核心思想即构建新的坐标数值对应关系,坐标改为原先坐标基础加上偏移量。此外需要注意的是,新坐标是旧坐标加上偏移量,偏移量可能是负数,因此新坐标的结果也可能是负数,也要和上面的 x_add 和 y_add 一样定义成有符号数。同时需要进行判定,如果新的坐标值超过原有的图像范围,则选择丢弃。
//**************************************************************************
// *** 名称 : Translation.v
// *** 作者 : xianyu_FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2019-06-23
// *** 描述 : 平移操作
//**************************************************************************
module Translation
//========================< 端口 >==========================================
(
//system --------------------------------------------
input wire rst_n , //复位,低电平有效
//uart ----------------------------------------------
input wire wr_clk , //50m
input wire [15:0] din ,
input wire din_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 ; //图片起始纵坐标
//---------------------------------------------------
parameter signed x_add = 20 ; //x轴偏移
parameter signed y_add = -30 ; //y轴偏移
//---------------------------------------------------
wire signed [9:0] new_cnt_col ; //新横坐标
wire signed [9:0] new_cnt_row ; //新纵坐标
//========================< 信号 >==========================================
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 [ 9:0] trans_cnt_col ;
reg [ 9:0] trans_cnt_row ;
wire [15:0] wr_data ;
wire wr_en ;
wire rd_en ;
reg rd_en_r ;
wire [14:0] wr_addr ;
reg [14:0] rd_addr ;
reg [15:0] rd_data ;
reg [15:0] buffer[COL*ROW-1:0] ; //类似RAM
//==========================================================================
//== 行列划分
//==========================================================================
always @(posedge wr_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 = din_vld;
assign end_cnt_col = add_cnt_col && cnt_col== COL-10'd1;
always @(posedge wr_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;
//==========================================================================
//== 平移
//==========================================================================
assign new_cnt_col = cnt_col + x_add; //新横坐标
assign new_cnt_row = cnt_row + y_add; //新纵坐标
always @(*) begin
if(new_cnt_col > COL-1 || new_cnt_row > ROW-1 || new_cnt_col < 0 || new_cnt_row < 0) begin
trans_cnt_col <= 'd0;
trans_cnt_row <= 'd0;
end
else begin
trans_cnt_col <= new_cnt_col;
trans_cnt_row <= new_cnt_row;
end
end
//==========================================================================
//== 缓存buffer,作用类似RAM
//==========================================================================
//写数据
//---------------------------------------------------
always @(posedge wr_clk) begin
buffer[wr_addr] <= din;
end
//写地址
//---------------------------------------------------
assign wr_addr = trans_cnt_row * COL + trans_cnt_col;
//读使能
//---------------------------------------------------
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)
rd_addr <= 'd0;
else if(rd_addr==COL*ROW-1)
rd_addr <= 'd0;
else if(rd_en)
rd_addr <= rd_addr + 1'b1;
end
//读数据
//---------------------------------------------------
always @(posedge rd_clk or negedge rst_n) begin
if(!rst_n)
rd_data <= 'd0;
else if(rd_en)
rd_data <= buffer[rd_addr];
end
//==========================================================================
//== 数据输出,打拍对齐
//==========================================================================
always @(posedge rd_clk) begin
rd_en_r <= rd_en;
end
assign TFT_data = rd_en_r ? rd_data : 16'hffff;
endmodule
三、上板验证

上板结果和 MATLAB 结果一致,此次平移实验设计成功。
参考资料:
[1] OpenS Lee:FPGA开源工作室(公众号)
[2] 张铮, 王艳平, 薛桂香. 数字图像处理与机器视觉[M]. 人民邮电出版社, 2010.

浙公网安备 33010602011771号