国产FPGA安路SF1系列测评【使用 FPGA 进行图像处理】
[复制链接]
本帖最后由 Grayson__ 于 2023-4-14 21:11 编辑
本次测评我们将进行 FPGA 的图像处理,将图像数据先通过RGB转YCbCr模块转为灰度数据后进行中值滤波,测评主要分析 SF1 的资源使用量。
工程中各模块的层次如下图所示:
中值滤波算法就是取一个滤波窗口内的中间值进行计算的算法,选用中间值进行计算,理论上可以消除噪声。本测评使用 3x3 窗口进行中值滤波的实现方案。
PART 1: RGB图像格式转YCbCr图像格式
YCbCr由 Y、Cb、Cr 组成,Y代表颜色的明亮度和浓度,Cb代表颜色的蓝色浓度偏移,Cr代表颜色的红色浓度偏移。Y分量就是我们常说的灰度值,因为很多图像处理不关注色彩,只需在灰度域处理即可,所以使用 Y 分量可以对传输带宽进行一定程度的压缩。YCbCr分为两种格式:tv range和full range,其中full range是我们本次使用的转换格式,对应的R、G、B范围均是0-255,Y、Cb、Cr也都是0-255。公式如下:
FPGA对浮点数进行运算要使用大量资源,所以我们将公式做如下转换,可大大减少资源的使用量并提高运算速度,这种运算转换的思想也是FPGA在进行图像处理或数字信号处理中一些算法常用的思想:
在运算过程中使用三级流水线,第一级是各分量的值对应相乘,第二级进行相乘后的值得相加,第三级就是右移8bit。由于是三级流水线,最后输出的数据延迟了三个时钟周期,所以我们需要对输出的行场同步信号以及使能信号进行打三拍的操作,才能使输出的数据在时序上对齐。完整代码如下:
module rgb2ycbcr(
//module clock
input clk , // 模块驱动时钟
input rst_n , // 复位信号
//图像处理前的数据接口
input pre_frame_vsync , // vsync信号
input pre_frame_hsync , // hsync信号
input pre_frame_de , // data enable信号
input [4:0] img_red , // 输入图像数据R
input [5:0] img_green , // 输入图像数据G
input [4:0] img_blue , // 输入图像数据B
//图像处理后的数据接口
output post_frame_vsync, // vsync信号
output post_frame_hsync, // hsync信号
output post_frame_de , // data enable信号
output [7:0] img_y , // 输出图像Y数据
output [7:0] img_cb , // 输出图像Cb数据
output [7:0] img_cr // 输出图像Cr数据
);
//reg define
reg [4:0] img_red_r0;
reg [5:0] img_green_r0;
reg [4:0] img_blue_r0;
reg [4:0] img_red_r1;
reg [5:0] img_green_r1;
reg [4:0] img_blue_r1;
reg [15:0] rgb_r_m0, rgb_r_m1, rgb_r_m2;
reg [15:0] rgb_g_m0, rgb_g_m1, rgb_g_m2;
reg [15:0] rgb_b_m0, rgb_b_m1, rgb_b_m2;
reg [15:0] img_y0 ;
reg [15:0] img_cb0;
reg [15:0] img_cr0;
reg [ 7:0] img_y1 ;
reg [ 7:0] img_cb1;
reg [ 7:0] img_cr1;
reg [ 2:0] pre_frame_vsync_d;
reg [ 2:0] pre_frame_hsync_d;
reg [ 2:0] pre_frame_de_d;
//wire define
wire [ 7:0] rgb888_r;
wire [ 7:0] rgb888_g;
wire [ 7:0] rgb888_b;
//RGB565 to RGB 888,使用高位进行量化补偿
assign rgb888_r = {img_red , img_red[4:2] };
assign rgb888_g = {img_green, img_green[5:4]};
assign rgb888_b = {img_blue , img_blue[4:2] };
//同步输出数据接口信号
assign post_frame_vsync = pre_frame_vsync_d[2];
assign post_frame_hsync = pre_frame_hsync_d[2];
assign post_frame_de = pre_frame_de_d[2] ;
assign img_y = img_y1;
assign img_cb = img_cb1;
assign img_cr = img_cr1;
//--------------------------------------------
//RGB 888 to YCbCr
/********************************************************
RGB888 to YCbCr888
Y = 0.299R +0.587G + 0.114B
Cb = 0.568(B-Y) + 128 = -0.172R-0.339G + 0.511B + 128
CR = 0.713(R-Y) + 128 = 0.511R-0.428G -0.083B + 128
Y = (77 *R + 150*G + 29 *B)>>8
Cb = (-43*R - 85 *G + 128*B)>>8 + 128
Cr = (128*R - 107*G - 21 *B)>>8 + 128
Y = (77 *R + 150*G + 29 *B )>>8
Cb = (-43*R - 85 *G + 128*B + 32768)>>8
Cr = (128*R - 107*G - 21 *B + 32768)>>8
*********************************************************/
//step1 pipeline mult
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
rgb_r_m0 <= 16'd0;
rgb_r_m1 <= 16'd0;
rgb_r_m2 <= 16'd0;
rgb_g_m0 <= 16'd0;
rgb_g_m1 <= 16'd0;
rgb_g_m2 <= 16'd0;
rgb_b_m0 <= 16'd0;
rgb_b_m1 <= 16'd0;
rgb_b_m2 <= 16'd0;
end
else begin
rgb_r_m0 <= rgb888_r * 8'd77 ;
rgb_r_m1 <= rgb888_r * 8'd43 ;
rgb_r_m2 <= rgb888_r << 3'd7 ;
rgb_g_m0 <= rgb888_g * 8'd150;
rgb_g_m1 <= rgb888_g * 8'd85 ;
rgb_g_m2 <= rgb888_g * 8'd107;
rgb_b_m0 <= rgb888_b * 8'd29 ;
rgb_b_m1 <= rgb888_b << 3'd7 ;
rgb_b_m2 <= rgb888_b * 8'd21 ;
end
end
//step2 pipeline add
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
img_y0 <= 16'd0;
img_cb0 <= 16'd0;
img_cr0 <= 16'd0;
end
else begin
img_y0 <= rgb_r_m0 + rgb_g_m0 + rgb_b_m0;
img_cb0 <= rgb_b_m1 - rgb_r_m1 - rgb_g_m1 + 16'd32768;
img_cr0 <= rgb_r_m2 - rgb_g_m2 - rgb_b_m2 + 16'd32768;
end
end
//step3 pipeline div
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
img_y1 <= 8'd0;
img_cb1 <= 8'd0;
img_cr1 <= 8'd0;
end
else begin
img_y1 <= img_y0 [15:8];
img_cb1 <= img_cb0[15:8];
img_cr1 <= img_cr0[15:8];
end
end
//延时3拍以同步数据信号
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
pre_frame_vsync_d <= 3'd0;
pre_frame_hsync_d <= 3'd0;
pre_frame_de_d <= 3'd0;
img_red_r0 <= 5'd0;
img_green_r0 <= 6'd0;
img_blue_r0 <= 5'd0;
img_red_r1 <= 5'd0;
img_green_r1 <= 6'd0;
img_blue_r1 <= 5'd0;
end
else begin
pre_frame_vsync_d <= {pre_frame_vsync_d[1:0], pre_frame_vsync};
pre_frame_hsync_d <= {pre_frame_hsync_d[1:0], pre_frame_hsync};
pre_frame_de_d <= {pre_frame_de_d[1:0] , pre_frame_de };
img_red_r0 <= img_red;
img_green_r0 <= img_green;
img_blue_r0 <= img_blue;
img_red_r1 <= img_red_r0;
img_green_r1 <= img_green_r0;
img_blue_r1 <= img_blue_r0;
end
end
endmodule
PART 2 :中值滤波模块
1.生成 3x3 格式的矩阵。
由于本测评中使用的是 3x3 的滤波窗口,所以我们首先要生成一个 3x3 矩阵模块,这里我们使用 FPGA 中的 RAM IP核,使用简单双端口ram,数据位宽为8bit,深度为256,读写时钟,写使能,读写地址配置如下:
这样,在 line_shift_ram_8bit 模块中调用ram模块可以获得三行一列的数据:
module line_shift_ram_8bit(
input clock,
input clken,
input per_frame_href,
input [7:0] shiftin, //当前行(第三行)
output [7:0] taps0x, //第二行
output [7:0] taps1x //第一行
);
//reg define
reg [2:0] clken_dly;
reg [9:0] ram_rd_addr;
reg [9:0] ram_rd_addr_d0;
reg [9:0] ram_rd_addr_d1;
reg [7:0] shiftin_d0;
reg [7:0] shiftin_d1;
reg [7:0] shiftin_d2;
reg [7:0] taps0x_d0;
//在数据来到时,ram地址累加
always@(posedge clock)begin
if(per_frame_href)
if(clken)
ram_rd_addr <= ram_rd_addr + 1 ;
else
ram_rd_addr <= ram_rd_addr ;
else
ram_rd_addr <= 0 ;
end
//时钟使能信号延迟三拍,从RAM中读取,以及向RAM0 RAM1中更新数据各需要一个时钟
always@(posedge clock) begin
clken_dly <= { clken_dly[1:0] , clken };
end
//将ram地址延迟二拍
always@(posedge clock) begin
ram_rd_addr_d0 <= ram_rd_addr;
ram_rd_addr_d1 <= ram_rd_addr_d0;
end
//输入数据延迟三拍
always@(posedge clock)begin
shiftin_d0 <= shiftin;
shiftin_d1 <= shiftin_d0;
shiftin_d2 <= shiftin_d1;
end
//用于存储前一行图像的RAM
ram_8x256 u_ram_8x256_0(
.clka (clock),
.cea (clken_dly[2]), //在延迟的第三个时钟周期,将当前行的数据写入RAM0
.addra (ram_rd_addr_d1),
.dia (shiftin_d2),
.clkb (clock),
.addrb (ram_rd_addr),
.dob (taps0x) //延迟一个时钟周期,输出RAM0中前一行图像的数据
);
//寄存一次前一行图像的数据
always@(posedge clock) begin
taps0x_d0 <= taps0x;
end
//用于存储前前一行图像的RAM
ram_8x256 u_ram_8x256_1(
.clka (clock),
.cea (clken_dly[1]), //在延迟的第二个时钟周期,将前一行图像的数据写入RAM1
.addra (ram_rd_addr_d0),
.dia (taps0x_d0),
.clkb (clock),
.addrb (ram_rd_addr),
.dob (taps1x) //延迟一个时钟周期,输出RAM1中前前一行图像的数据
);
endmodule
之后生成 3x3 的矩阵:
module matrix_generate_3x3_8bit(
input clk,
input rst_n,
input per_frame_vsync,
input per_frame_href,
input per_frame_clken,
input [7:0] per_img_y,
output matrix_frame_vsync,
output matrix_frame_href,
output matrix_frame_clken,
output reg [7:0] matrix_p11,
output reg [7:0] matrix_p12,
output reg [7:0] matrix_p13,
output reg [7:0] matrix_p21,
output reg [7:0] matrix_p22,
output reg [7:0] matrix_p23,
output reg [7:0] matrix_p31,
output reg [7:0] matrix_p32,
output reg [7:0] matrix_p33
);
//wire define
wire [7:0] row1_data; //第一行
wire [7:0] row2_data; //第二行
wire read_frame_href;
wire read_frame_clken;
//reg define
reg [7:0] row3_data; //当前行
reg [1:0] per_frame_vsync_r;
reg [1:0] per_frame_href_r;
reg [1:0] per_frame_clken_r;
assign read_frame_href = per_frame_href_r[0] ;
assign read_frame_clken = per_frame_clken_r[0];
assign matrix_frame_vsync = per_frame_vsync_r[1];
assign matrix_frame_href = per_frame_href_r[1] ;
assign matrix_frame_clken = per_frame_clken_r[1];
//当前数据放在第3行
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
row3_data <= 0;
else begin
if(per_frame_clken)
row3_data <= per_img_y ;
else
row3_data <= row3_data ;
end
end
//用于存储列数据的RAM,消耗一个时钟周期
line_shift_ram_8bit u_line_shift_ram_8bit
(
.clock (clk),
.clken (per_frame_clken),
.per_frame_href (per_frame_href),
.shiftin (per_img_y),
.taps0x (row2_data),
.taps1x (row1_data)
);
//将同步信号延迟两拍,用于同步化处理
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
per_frame_vsync_r <= 0;
per_frame_href_r <= 0;
per_frame_clken_r <= 0;
end
else begin
per_frame_vsync_r <= { per_frame_vsync_r[0], per_frame_vsync };
per_frame_href_r <= { per_frame_href_r[0], per_frame_href };
per_frame_clken_r <= { per_frame_clken_r[0], per_frame_clken };
end
end
//在同步处理后的控制信号下,输出图像矩阵,消耗一个时钟周期
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
{matrix_p11, matrix_p12, matrix_p13} <= 24'h0;
{matrix_p21, matrix_p22, matrix_p23} <= 24'h0;
{matrix_p31, matrix_p32, matrix_p33} <= 24'h0;
end
else if(read_frame_href) begin
if(read_frame_clken) begin
{matrix_p11, matrix_p12, matrix_p13} <= {matrix_p12, matrix_p13, row1_data};
{matrix_p21, matrix_p22, matrix_p23} <= {matrix_p22, matrix_p23, row2_data};
{matrix_p31, matrix_p32, matrix_p33} <= {matrix_p32, matrix_p33, row3_data};
end
else begin
{matrix_p11, matrix_p12, matrix_p13} <= {matrix_p11, matrix_p12, matrix_p13};
{matrix_p21, matrix_p22, matrix_p23} <= {matrix_p21, matrix_p22, matrix_p23};
{matrix_p31, matrix_p32, matrix_p33} <= {matrix_p31, matrix_p32, matrix_p33};
end
end
else begin
{matrix_p11, matrix_p12, matrix_p13} <= 24'h0;
{matrix_p21, matrix_p22, matrix_p23} <= 24'h0;
{matrix_p31, matrix_p32, matrix_p33} <= 24'h0;
end
end
endmodule
2.下面就开始进行矩阵中值的寻找,流程如下:
先将每行3个数据进行两两比较,得到最大值Max,中间值Mid,最小值Min;
再求3个最大值的最小值Max_min,3个中间值的中间值Mid_mid,3个最小值的最大值Min_max;
最后求Max_min、Mid_mid、Min_max的中间值,即为最终结果。
求最大最小中间值需要对三个数进行排序才可得出,此处使用sort3模块,代码如下:
module sort3(
input clk,
input rst_n,
input [7:0] data1,
input [7:0] data2,
input [7:0] data3,
output reg [7:0] max_data,
output reg [7:0] mid_data,
output reg [7:0] min_data
);
//对三个数据进行排序
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
max_data <= 0;
mid_data <= 0;
min_data <= 0;
end
else begin
//取最大值
if(data1 >= data2 && data1 >= data3)
max_data <= data1;
else if(data2 >= data1 && data2 >= data3)
max_data <= data2;
else//(data3 >= data1 && data3 >= data2)
max_data <= data3;
//取中值
if((data1 >= data2 && data1 <= data3) || (data1 >= data3 && data1 <= data2))
mid_data <= data1;
else if((data2 >= data1 && data2 <= data3) || (data2 >= data3 && data2 <= data1))
mid_data <= data2;
else//((data3 >= data1 && data3 <= data2) || (data3 >= data2 && data3 <= data1))
mid_data <= data3;
//取最小值
if(data1 <= data2 && data1 <= data3)
min_data <= data1;
else if(data2 <= data1 && data2 <= data3)
min_data <= data2;
else//(data3 <= data1 && data3 <= data2)
min_data <= data3;
end
end
endmodule
之后在median_filter_3x3模块中进行上述三步骤的实现:
module median_filter_3x3(
input clk,
input rst_n,
input median_frame_vsync,
input median_frame_href,
input median_frame_clken,
input [7:0] data11,
input [7:0] data12,
input [7:0] data13,
input [7:0] data21,
input [7:0] data22,
input [7:0] data23,
input [7:0] data31,
input [7:0] data32,
input [7:0] data33,
output [7:0] target_data,
output pos_median_vsync,
output pos_median_href,
output pos_median_clken
);
//--------------------------------------------------------------------------------------
//FPGA Median Filter Sort order
// Pixel -- Sort1 -- Sort2 -- Sort3
// [ P1 P2 P3 ] --> [ Max1 Mid1 Min1 ]
// [ P4 P5 P6 ] --> [ Max2 Mid2 Min2 ] --> [Max_min, Mid_mid, Min_max] --> mid_valid
// [ P7 P8 P9 ] --> [ Max3 Mid3 Min3 ]
//reg define
reg [2:0] median_frame_vsync_r;
reg [2:0] median_frame_href_r;
reg [2:0] median_frame_clken_r;
//wire define
wire [7:0] max_data1;
wire [7:0] mid_data1;
wire [7:0] min_data1;
wire [7:0] max_data2;
wire [7:0] mid_data2;
wire [7:0] min_data2;
wire [7:0] max_data3;
wire [7:0] mid_data3;
wire [7:0] min_data3;
wire [7:0] max_min_data;
wire [7:0] mid_mid_data;
wire [7:0] min_max_data;
assign pos_median_vsync = median_frame_vsync_r[2];
assign pos_median_href = median_frame_href_r[2];
assign pos_median_clken = median_frame_clken_r[2];
//Step1 对stor3进行三次例化操作
sort3 u_sort3_1( //第一行数据排序
.clk (clk),
.rst_n (rst_n),
.data1 (data11),
.data2 (data12),
.data3 (data13),
.max_data (max_data1),
.mid_data (mid_data1),
.min_data (min_data1)
);
sort3 u_sort3_2( //第二行数据排序
.clk (clk),
.rst_n (rst_n),
.data1 (data21),
.data2 (data22),
.data3 (data23),
.max_data (max_data2),
.mid_data (mid_data2),
.min_data (min_data2)
);
sort3 u_sort3_3( //第三行数据排序
.clk (clk),
.rst_n (rst_n),
.data1 (data31),
.data2 (data32),
.data3 (data33),
.max_data (max_data3),
.mid_data (mid_data3),
.min_data (min_data3)
);
//Step2 对三行像素取得的排序进行处理
sort3 u_sort3_4( //取三行最大值的最小值
.clk (clk),
.rst_n (rst_n),
.data1 (max_data1),
.data2 (max_data2),
.data3 (max_data3),
.max_data (),
.mid_data (),
.min_data (max_min_data)
);
sort3 u_sort3_5( //取三行中值的最小值
.clk (clk),
.rst_n (rst_n),
.data1 (mid_data1),
.data2 (mid_data2),
.data3 (mid_data3),
.max_data (),
.mid_data (mid_mid_data),
.min_data ()
);
sort3 u_sort3_6( //取三行最小值的最大值
.clk (clk),
.rst_n (rst_n),
.data1 (min_data1),
.data2 (min_data2),
.data3 (min_data3),
.max_data (min_max_data),
.mid_data (),
.min_data ()
);
//step3 将step2 中得到的三个值,再次取中值
sort3 u_sort3_7(
.clk (clk),
.rst_n (rst_n),
.data1 (max_min_data),
.data2 (mid_mid_data),
.data3 (min_max_data),
.max_data (),
.mid_data (target_data),
.min_data ()
);
//延迟三个周期进行同步
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
median_frame_vsync_r <= 0;
median_frame_href_r <= 0;
median_frame_clken_r <= 0;
end
else begin
median_frame_vsync_r <= {median_frame_vsync_r[1:0],median_frame_vsync};
median_frame_href_r <= {median_frame_href_r [1:0], median_frame_href};
median_frame_clken_r <= {median_frame_clken_r[1:0],median_frame_clken};
end
end
endmodule
最后在gray_median_filter模块中例化matrix_generate_3x3_8bit与median_filter_3x3模块,需要注意的是3x3阵列的中值滤波,需要3个时钟:
module gray_median_filter(
//时钟
input clk, //50MHz
input rst_n,
//处理前图像数据
input pe_frame_vsync, //处理前图像数据场信号
input pe_frame_href, //处理前图像数据行信号
input pe_frame_clken, //处理前图像数据输入使能效信号
input [7:0] pe_img, //灰度数据
//处理后的图像数据
output pos_frame_vsync, //处理后的图像数据场信号
output pos_frame_href, //处理后的图像数据行信号
output pos_frame_clken, //处理后的图像数据输出使能效信号
output [7:0] pos_img //处理后的灰度数据
);
//wire define
wire matrix_frame_vsync;
wire matrix_frame_href;
wire matrix_frame_clken;
wire [7:0] matrix_p11; //3X3 阵列输出
wire [7:0] matrix_p12;
wire [7:0] matrix_p13;
wire [7:0] matrix_p21;
wire [7:0] matrix_p22;
wire [7:0] matrix_p23;
wire [7:0] matrix_p31;
wire [7:0] matrix_p32;
wire [7:0] matrix_p33;
wire [7:0] mid_value;
//在延迟后的行信号有效,将中值赋给灰度输出值
assign pos_img = pos_frame_href ? mid_value : 8'd0;
matrix_generate_3x3_8bit u_matrix_generate_3x3_8bit(
.clk (clk),
.rst_n (rst_n),
//处理前图像数据
.per_frame_vsync (pe_frame_vsync),
.per_frame_href (pe_frame_href),
.per_frame_clken (pe_frame_clken),
.per_img_y (pe_img),
//处理后的图像数据
.matrix_frame_vsync (matrix_frame_vsync),
.matrix_frame_href (matrix_frame_href),
.matrix_frame_clken (matrix_frame_clken),
.matrix_p11 (matrix_p11),
.matrix_p12 (matrix_p12),
.matrix_p13 (matrix_p13),
.matrix_p21 (matrix_p21),
.matrix_p22 (matrix_p22),
.matrix_p23 (matrix_p23),
.matrix_p31 (matrix_p31),
.matrix_p32 (matrix_p32),
.matrix_p33 (matrix_p33)
);
//3x3阵列的中值滤波,需要3个时钟
median_filter_3x3 u_median_filter_3x3(
.clk (clk),
.rst_n (rst_n),
.median_frame_vsync (matrix_frame_vsync),
.median_frame_href (matrix_frame_href),
.median_frame_clken (matrix_frame_clken),
//第一行
.data11 (matrix_p11),
.data12 (matrix_p12),
.data13 (matrix_p13),
//第二行
.data21 (matrix_p21),
.data22 (matrix_p22),
.data23 (matrix_p23),
//第三行
.data31 (matrix_p31),
.data32 (matrix_p32),
.data33 (matrix_p33),
.pos_median_vsync (pos_frame_vsync),
.pos_median_href (pos_frame_href),
.pos_median_clken (pos_frame_clken),
.target_data (mid_value)
);
endmodule
PART 3 :图像处理顶层
在图像处理顶层模块中,我们将例化各个处理算法,此次例化的是rgb转ycbcr的模块以及中值滤波模块:
module vip_top(
input clk ,
input rst_n ,
input pre_frame_vsync ,
input pre_frame_hsync ,
input pre_frame_de ,
input [4:0] img_red ,
input [5:0] img_green ,
input [4:0] img_blue ,
output pos_frame_vsync ,
output pos_frame_href ,
output pos_frame_clken ,
output [7:0] pos_img
);
//wire define
wire pe1_frame_vsync;
wire pe1_frame_hsync;
wire pe1_frame_de;
wire [7:0] pe1_img_y;
wire [7:0] pe1_img_Cb;
wire [7:0] pe1_img_Cr;
rgb2ycbcr u_rgb2ycbcr(
//module clock
.clk (clk), // 模块驱动时钟
.rst_n (rst_n), // 复位信号
//图像处理前的数据接口
.pre_frame_vsync (pre_frame_vsync), // vsync信号
.pre_frame_hsync (pre_frame_hsync), // hsync信号
.pre_frame_de (pre_frame_de), // data enable信号
.img_red (img_red), // 输入图像数据R
.img_green (img_green), // 输入图像数据G
.img_blue (img_blue), // 输入图像数据B
//图像处理后的数据接口
.post_frame_vsync(pe1_frame_vsync), // vsync信号
.post_frame_hsync(pe1_frame_hsync), // hsync信号
.post_frame_de (pe1_frame_de), // data enable信号
.img_y (pe1_img_y), // 输出图像Y数据
.img_cb (pe1_img_Cb), // 输出图像Cb数据
.img_cr (pe1_img_Cr) // 输出图像Cr数据
);
gray_median_filter u_gray_median_filter(
//时钟
.clk (clk), // 模块驱动时钟
.rst_n (rst_n), // 复位信号
//处理前图像数据
.pe_frame_vsync (pe1_frame_vsync), //处理前图像数据场信号
.pe_frame_href (pe1_frame_hsync), //处理前图像数据行信号
.pe_frame_clken (pe1_frame_de), //处理前图像数据输入使能效信号
.pe_img (pe1_img_y), //灰度数据
//处理后的图像数据
.pos_frame_vsync (pos_frame_vsync), //处理后的图像数据场信号
.pos_frame_href (pos_frame_href), //处理后的图像数据行信号
.pos_frame_clken (pos_frame_clken), //处理后的图像数据输出使能效信号
.pos_img (pos_img) //处理后的灰度数据
);
endmodule
至此,图像中值滤波已在 FPGA 上实现,下面是FPGA的资源占用率:
可见,使用 SF1 FPGA 部分进行图像的处理加速,其资源是绰绰有余的,感兴趣的读者可以继续研究并在FPGA上实现其他算法,例如均值滤波,其与中值滤波的区别只是在所求数据上(均值)不同,模块层次基本一致,当然还有腐蚀、膨胀、二值化、sobel边缘检测等图像处理常见的操作。安路的SF1同时具备FPGA 与 MCU两部分资源,FPGA在进行算法加速的方面优势突出,而MCU则适合进行一些控制操作,将二者结合起来使用会大大减少开发难度,当然前提是需要同时了解MCU与FPGA的相关知识与运用。
遗憾的是,SF1 DEMO板上使用的均是mipi接口的摄像头与屏幕,目前身边没有这样的摄像头与屏幕,所以上板测试只能缓缓,有机会一定测试一下。
|