OV5640 摄像头的图像平滑处理

如图所示,这是整个视频采集系统的原理框图。

        上电初始,FPGA 需要通过 IIC 接口对 CMOS Sensor 进行寄存器初始化配置。这些初始化的基本参数,即初始化地址对应的初始化数据都存储在一个预先配置好的 FPGA 片内 ROM中。在初始化配置完成后,CMOS Sensor 就能够持续输出标准 RGB 的视频数据流,FPGA 通过对其同步信号,如时钟、行频和场频进行检测,从而从数据总线上实时的采集图像数据。
        在 FPGA 内部,采集到的视频数据先通过一个 FIFO,将原本 25MHz 频率下同步的数据流转换到 50MHz 的频率下。接着将这个数据再送入写 DDR3 缓存的异步 FIFO 中,这个 FIFO 中的数据一旦达到一定数量,就会通过 AXI HP0 总线写入 DDR3 中。与此同时,AXI HP0 总线也会读取 DDR3 中缓存的图像数据,缓存到 FIFO 中,并最终送往 LCD 驱动模块进行显示。LCD驱动模块不断的发出读图像数据的请求,并驱动液晶显示器显示视频图像。
        本实例除了前面提到对原始图像做 DDR3 缓存和显示,还会在原始图像缓存到 DDR3 之前,另外做图像的多行缓存和平滑处理运算,获得新的平滑后的图像流,这个图像流通过AXI HP1 总线写入到 DDR3 中。AXI HP1 总线也会根据 LCD 显示模块的请求,读取处理后的图像进行显示。最终在 VGA 液晶显示器上,可以看到左侧图像是原始的图像,右侧图像是经过平滑处理后的图像。

1、图像平滑与滤波

1.1 基本概念
        从统计学的观点来看,凡是统计特征不随时间变化的噪声称为平稳噪声,而统计特征随时间变化的噪声称为非平稳噪声。幅值基本相同,但是噪声出现的位置是随机的,称为椒盐噪声;如果噪声的幅值是随机的,根据幅值大小的分布,有高斯型和瑞利型两种,分别称为高斯噪声和瑞利噪声
        图像滤波,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作,其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。
        消除图像中的噪声成分叫作图像的平滑化或滤波操作。信号或图像的能量大部分集中在幅度谱的低频和中频段是很常见的,而在较高频段,感兴趣的信息经常被噪声淹没。因此一个能降低高频成分幅度的滤波器就能够减弱噪声的影响。
        图像滤波的目的有两个,一是抽出对象的特征作为图像识别的特征模式;另一个是为适应图像处理的要求,消除图像数字化时所混入的噪声。而对滤波处理的要求也有两条,一是不能损坏图像的轮廓及边缘等重要信息;二是使图像清晰视觉效果好。
        平滑滤波是低频增强的空间域滤波技术。它的目的有两类:一类是模糊;另一类是消除噪音。空间域的平滑滤波一般采用简单平均法进行,就是求邻近像元点的平均亮度值。邻域的大小与平滑的效果直接相关,邻域越大平滑的效果越好,但邻域过大,平滑会使边缘信息损失的越大,从而使输出的图像变得模糊,因此需合理选择邻域的大小。
        关于滤波器,一种形象的比喻法是:我们可以把滤波器想象成一个包含加权系数的窗口,当使用这个滤波器平滑处理图像时,就把这个窗口放到图像之上,透过这个窗口来看我们得到的图像。举一个滤波在我们生活中的应用:美颜的磨皮功能。如果将我们脸上坑坑洼洼比作是噪声的话,那么滤波算法就是来取出这些噪声,使我们自拍的皮肤看起来很光滑。

1.2滤波算法
各种不同的滤波算法如下:
• 限幅滤波法(又称程序判断滤波法)
• 中位值滤波法
• 算术平均滤波法
• 高斯滤波法
• 递推平均滤波法(又称滑动平均滤波法)
• 中位值平均滤波法(又称防脉冲干扰平均滤波法)
• 限幅平均滤波法
• 一阶滞后滤波法
• 加权递推平均滤波法
• 消抖滤波法
• 限幅消抖滤波法
• 卡尔曼滤波(非扩展卡尔曼)

1.3均值滤波
        均值滤波器是图像处理中一种常见的滤波器,它主要应用于平滑噪声。它的原理主要是利用某像素点周边像素的平均值来达到平滑噪声的效果。
        例如,1~8 像素是(x,y)点周围邻近的 8 个像素点。最简单的均值滤波,即对(x,y)以及周边 8 个像素点求平均替代原来的(x,y)点。

        这种滤波方式的优点很明显,算法简单,计算速度快。缺点是降低噪声的同时使图像产生模糊,特别是景物的边缘和细节部分。

1.4加权均值滤波器
        由于我们已经注意到了中心点和周边像素点的重要程度不同,因此可以将均值滤波进行改进,获得图像平滑滤波效果的同时,也在一定程度上尽量降低图像边缘和细节的损失。

基于 1/16 的加权均值滤波,我们的 Matlab 代码如下:

clear
clc
I1=imread('.\lena.jpg');
I=im2double(I1);
[m,n,c]=size(I);
A=zeros(m,n,c);%           1   2   1
%   1/16 *  2   4   2
%           1   2   1%for R
for i=2:m-1for j=2:n-1A(i,j,1)=I(i-1,j-1,1)+I(i+1,j-1,1)+I(i-1,j+1,1)+I(i+1,j+1,1)+2*I(i+1,j,1)+2*I(i-1,j,1)+2*I(i,j+1,1)+2*I(i,j-1,1)+4*I(i,j,1);end
end%for G
for i=2:m-1for j=2:n-1A(i,j,2)=I(i-1,j-1,2)+I(i+1,j-1,2)+I(i-1,j+1,2)+I(i+1,j+1,2)+2*I(i+1,j,2)+2*I(i-1,j,2)+2*I(i,j+1,2)+2*I(i,j-1,2)+4*I(i,j,2);end
end%for B
for i=2:m-1for j=2:n-1A(i,j,3)=I(i-1,j-1,3)+I(i+1,j-1,3)+I(i-1,j+1,3)+I(i+1,j+1,3)+2*I(i+1,j,3)+2*I(i-1,j,3)+2*I(i,j+1,3)+2*I(i,j-1,3)+4*I(i,j,3);end
endB=A/16;%outputimwrite(B,'lena.tif','tif');imshow('.\lena.jpg');title('origin image');figureimshow('lena.tif');title('image after average filter')

滤波效果如下。

2、基于FPGA 的图像平滑处理
        工程中average_filter.v 模块实现了1/16 的图像加权均值滤波处理。该模块功能框图如下,使用 2 个 FIFO,分别缓存前后行,即进入图像处理的 3 组数据流分别是第 n-1 行、第 n 行和第 n+1 行的图像,控制输入数据流和 2 个 FIFO 缓存的图像在同一个位置、寄存器对前后 2 个像素的图像值进行缓存,这样便可实现中心像素点以及前后列、上下行之间数据的同步处理了。

        average_filter.v 模块的接口定义如下。时钟信号 clk 和复位信号 rst_n;i_image_ddr3_* 信号为输入图像数据流接口,复位信号 i_image_ddr3_clr 拉高表示复位,当 i_image_ddr3_clr 为低电平时,图像输入使能信号 i_image_ddr3_wren 拉高表示图像数据 i_image_ddr3_wrdb 有效,而每行最好一个有效数据输入时,图像行尾指示信号 i_image_ddr3_line_end 拉高;o_image_ddr3_*信号为输出图像数据流接口,当图像输出使能信号 o_image_ddr3_wren 拉高时,图像数据输出总线 o_image_ddr3_wrdb 上的数据有效。

module average_filter(input clk,		//50MHz时钟input rst_n,	//复位信号,低电平有效//Image Data flow from Image Sensorinput i_image_ddr3_wren,input i_image_ddr3_line_end,input i_image_ddr3_clr,input[15:0] i_image_ddr3_wrdb,			//Image Data flow after average transformoutput reg o_image_ddr3_wren,output[15:0] o_image_ddr3_wrdb			);

IMAGE_WIDTH 和 IMAGE_HIGHT 两个参数分别定义图像分辨率的宽度和高度像素数。

parameter IMAGE_WIDTH = 10’d640;
parameter IMAGE_HIGHT = 10’d480;

以图像行尾指示信号 i_image_ddr3_line_end 拉高进行计数的数据行计数器 r_line_cnt 逻辑如下。

//数据行计数器
reg[9:0] r_line_cnt;always @(posedge clk or negedge rst_n)if(!rst_n) r_line_cnt <= 10'd0;else if(i_image_ddr3_clr) r_line_cnt <= 10'd0;else if(i_image_ddr3_line_end) r_line_cnt <= r_line_cnt+1'b1;else ;

下面例化的两个 FIFO 分别缓存前后两行的图像数据。

//FIFO for cache 1 line image data
reg r_fifo1_rd_en;
wire[15:0] w_fifo1_dout;
wire[9:0] w_fifo1_data_count;fifo_generator_3 	uut1_fifo_generator_3 (.clk(clk),                // input wire clk.srst(!rst_n || i_image_ddr3_clr),              // input wire srst.din(i_image_ddr3_wrdb),                // input wire [15 : 0] din.wr_en(i_image_ddr3_wren),            // input wire wr_en.rd_en(r_fifo1_rd_en),            // input wire rd_en.dout(w_fifo1_dout),              // output wire [15 : 0] dout.full(),              // output wire full.empty(),            // output wire empty.data_count(w_fifo1_data_count)  // output wire [9 : 0] data_count
);reg r_fifo2_wr_en;
reg r_fifo2_rd_en;
wire[15:0] w_fifo2_dout;
wire[9:0] w_fifo2_data_count;fifo_generator_3 	uut2_fifo_generator_3 (.clk(clk),                // input wire clk.srst(!rst_n || i_image_ddr3_clr),              // input wire srst.din(w_fifo1_dout),                // input wire [15 : 0] din.wr_en(r_fifo2_wr_en),            // input wire wr_en.rd_en(r_fifo2_rd_en),            // input wire rd_en.dout(w_fifo2_dout),              // output wire [15 : 0] dout.full(),              // output wire full.empty(),            // output wire empty.data_count(w_fifo2_data_count)  // output wire [9 : 0] data_count
);

        下面的状态机主要用于FIFO的读写控制信号产生。上电初始处于复位状态RFIFO_RESET;
若 FIFO1 中的数据量满 1 行(w_fifo1_data_count >= IMAGE_WIDTH),则进入 FIFO1 读取状态RFIFO_RDDB1;RFIFO_RDDB1 状态下将会读取 FIFO1 中 1 行的数据,并写入到 FIFO2 中,完成操作后进入空闲状态 RFIFO_IDLE;RFIFO_IDLE 状态下等待 FIFO2 中的数据量满 1 行(w_fifo2_data_count >= IMAGE_WIDTH),则进入读 FIFO1/FIFO2 状态 RFIFO_RDDB2;RFIFO_RDDB2 状态下读取 1 行 FIFO1 数据并写入 FIFO2 中,同时也会读取 1 行 FIFO2 数据,FIFO2 中读出第 n 行数据、FIFO1 中读出第 n+1 行数据和即将写入 FIFO1 中的第 n+2 行数据,会进行均值滤波的处理后输出数据,RFIFO_RDDB2 状态处理完成后,返回空闲状态RFIFO_IDLE 继续等待下一行的处理。
        在 RFIFO_IDLE 状态、RFIFO_RDDB2 状态下,会同时判断若所有数据行已经操作完成(r_line_cnt >= IMAGE_HIGHT),则进入 RFIFO_WAIT 状态;RFIFO_WAIT 状态稍作延时后,进入 RFIFO_RDDB3 从FIFO1/FIFO2中读出最后1行的数据,最后返回复位状态RFIFO_RESET。

//连续读出640个数据计数控制状态机
parameter RFIFO_RESET	= 3'd0;
parameter RFIFO_IDLE	= 3'd1;
parameter RFIFO_RDDB1	= 3'd2;
parameter RFIFO_RDDB2	= 3'd3;
parameter RFIFO_WAIT	= 3'd4;
parameter RFIFO_RDDB3	= 3'd5;
reg[2:0] rfifo_state;
reg[9:0] dcnt;	//读FIFO数据个数计数器
reg[9:0] average_num;
reg[3:0] dly_cnt;always @(posedge clk or negedge rst_n)if(!rst_n) rfifo_state <= RFIFO_RESET;else if(i_image_ddr3_clr) rfifo_state <= RFIFO_RESET;else begincase(rfifo_state)RFIFO_RESET: if(w_fifo1_data_count >= IMAGE_WIDTH) rfifo_state <= RFIFO_RDDB1;else rfifo_state <= RFIFO_RESET;RFIFO_RDDB1: if(dcnt >= (IMAGE_WIDTH-1)) rfifo_state <= RFIFO_IDLE;else rfifo_state <= RFIFO_RDDB1;RFIFO_IDLE:  if(r_line_cnt >= IMAGE_HIGHT) rfifo_state <= RFIFO_WAIT;else if(w_fifo2_data_count >= IMAGE_WIDTH) rfifo_state <= RFIFO_RDDB2;else rfifo_state <= RFIFO_IDLE;			RFIFO_RDDB2: if(r_line_cnt >= IMAGE_HIGHT) rfifo_state <= RFIFO_WAIT;else if(dcnt >= (IMAGE_WIDTH-1)) rfifo_state <= RFIFO_IDLE;else rfifo_state <= RFIFO_RDDB2;RFIFO_WAIT:	 if(dly_cnt == 4'hf) rfifo_state <= RFIFO_RDDB3;	else rfifo_state <= RFIFO_WAIT;RFIFO_RDDB3: if(dcnt >= (IMAGE_WIDTH-1)) rfifo_state <= RFIFO_RESET;else rfifo_state <= RFIFO_RDDB3;						default: rfifo_state <= RFIFO_IDLE;endcaseend

RFIFO_WAIT 状态的延时计数逻辑如下。

always @(posedge clk or negedge rst_n)if(!rst_n) dly_cnt <= 4'd0;else if(rfifo_state == RFIFO_WAIT) dly_cnt <= dly_cnt+1'b1;else dly_cnt <= 4'd0;

RFIFO_RDDB1 状态、RFIFO_RDDB2 状态和 RFIFO_RDDB3 状态下,读数据的计数逻辑如
下。

	//读FIFO数据个数计数器
always @(posedge clk or negedge rst_n)if(!rst_n) dcnt <= 10'd0;else if((rfifo_state == RFIFO_IDLE) || (rfifo_state == RFIFO_RESET)) dcnt <= 10'd0;else if(((rfifo_state == RFIFO_RDDB1) || (rfifo_state == RFIFO_RDDB2)) && i_image_ddr3_wren) dcnt <= dcnt+1'b1;else if(rfifo_state == RFIFO_RDDB3) dcnt <= dcnt+1'b1;	else dcnt <= 10'd0;

均值滤波运算的数据计数器 average_num 逻辑如下。

//average transform数据计数器
always @(posedge clk or negedge rst_n)if(!rst_n) average_num <= 10'd0;else if(dcnt == 10'd4) average_num <= 10'd1;else if(average_num != 10'd0) beginif(average_num < IMAGE_WIDTH) average_num <= average_num+1'b1;else average_num <= 10'd0;endelse average_num <= 10'd0;

读 FIFO1 使能信号产生逻辑如下。

always @(posedge clk or negedge rst_n)if(!rst_n) r_fifo1_rd_en <= 1'b0;else if(((rfifo_state == RFIFO_RDDB1) || (rfifo_state == RFIFO_RDDB2)) && i_image_ddr3_wren) r_fifo1_rd_en <= 1'b1;else if((rfifo_state == RFIFO_RDDB3) && (dcnt < IMAGE_WIDTH)) r_fifo1_rd_en <= 1'b1;else r_fifo1_rd_en <= 1'b0;

写 FIFO2 使能信号产生逻辑如下。

//写FIFO2是能信号产生逻辑
always @(posedge clk or negedge rst_n)if(!rst_n) r_fifo2_wr_en <= 1'b0;else r_fifo2_wr_en <= r_fifo1_rd_en;

读 FIFO2 使能信号产生逻辑如下。

//读FIFO2使能信号产生逻辑	
always @(posedge clk or negedge rst_n)if(!rst_n) r_fifo2_rd_en <= 1'b0;else if(((rfifo_state == RFIFO_RDDB2) || (rfifo_state == RFIFO_RDDB3)) && i_image_ddr3_wren) r_fifo2_rd_en <= 1'b1;else r_fifo2_rd_en <= 1'b0;	

以下逻辑对多个不同列临近数据进行缓存,便于均值滤波处理时使用。 

/*
对图像进行均值滤波平滑处理
第1行、最后1行、第1列、最后1列不做处理,使用原始图像像素值输出;
对其余图像像素点,对周围像素点做如下运算:
%           	1    2    1
%   1/16 *  	2    4    2
%           	1    2    1
*///图像缓存3拍
reg[15:0] data_temp_line_1[2:0];	
reg[15:0] data_temp_line_2[2:0];
reg[15:0] data_temp_line_3[4:0];always @(posedge clk) begindata_temp_line_1[0] <= w_fifo2_dout;data_temp_line_1[1] <= data_temp_line_1[0];data_temp_line_1[2] <= data_temp_line_1[1];
endalways @(posedge clk) begindata_temp_line_2[0] <= w_fifo1_dout;data_temp_line_2[1] <= data_temp_line_2[0];data_temp_line_2[2] <= data_temp_line_2[1];
endalways @(posedge clk) begindata_temp_line_3[0] <= i_image_ddr3_wrdb;data_temp_line_3[1] <= data_temp_line_3[0];data_temp_line_3[2] <= data_temp_line_3[1];data_temp_line_3[3] <= data_temp_line_3[2];data_temp_line_3[4] <= data_temp_line_3[3];
end	

以下逻辑为图像的均值滤波运算。

//图像输出average filter运算
reg[9:0] sum_a_r,sum_b_r1,sum_b_r2;
reg[9:0] sum_a_g,sum_b_g1,sum_b_g2;
reg[9:0] sum_a_b,sum_b_b1,sum_b_b2;reg[15:0] average_result;	always @(posedge clk) beginsum_a_r <= {3'b000,data_temp_line_2[1][15:11],2'b00};sum_a_g <= {2'b0,data_temp_line_2[1][10:5],2'b00};sum_a_b <= {3'b000,data_temp_line_2[1][4:0],2'b00};
end	always @(posedge clk) beginsum_b_r1 <= {4'd0,data_temp_line_2[0][15:11],1'b0}	+ {4'd0,data_temp_line_2[2][15:11],1'b0}+ {4'd0,data_temp_line_1[1][15:11],1'b0}+ {4'd0,data_temp_line_3[3][15:11],1'b0};sum_b_g1 <= {3'd0,data_temp_line_2[0][10:5],1'b0}	+ {3'd0,data_temp_line_2[2][10:5],1'b0}	+ {3'd0,data_temp_line_1[1][10:5],1'b0}	+ {3'd0,data_temp_line_3[3][10:5],1'b0};sum_b_b1 <= {4'd0,data_temp_line_2[0][4:0],1'b0}	+ {4'd0,data_temp_line_2[2][4:0],1'b0}	+ {4'd0,data_temp_line_1[1][4:0],1'b0}	+ {4'd0,data_temp_line_3[3][4:0],1'b0};sum_b_r2 <= {5'd0,data_temp_line_1[0][15:11]}	+ {5'd0,data_temp_line_1[2][15:11]}	+ {5'd0,data_temp_line_3[2][15:11]}	+ {5'd0,data_temp_line_3[4][15:11]};sum_b_g2 <= {4'd0,data_temp_line_1[0][10:5]}	+ {4'd0,data_temp_line_1[2][10:5]}	+ {4'd0,data_temp_line_3[2][10:5]}	+ {4'd0,data_temp_line_3[4][10:5]};sum_b_b2 <= {5'd0,data_temp_line_1[0][4:0]}		+ {5'd0,data_temp_line_1[2][4:0]}	+ {5'd0,data_temp_line_3[2][4:0]}	+ {5'd0,data_temp_line_3[4][4:0]};	
end	wire[9:0] temp_r = sum_a_r+(sum_b_r1+sum_b_r2);
wire[9:0] temp_g = sum_a_g+(sum_b_g1+sum_b_g2);
wire[9:0] temp_b = sum_a_b+(sum_b_b1+sum_b_b2);always @(posedge clk) beginif((average_num == 10'd1) || (average_num == IMAGE_WIDTH)) average_result <= data_temp_line_2[2];	//第1列和最后1列使用原值else beginaverage_result[15:11] = temp_r[8:4];		average_result[10:5] = temp_g[9:4];average_result[4:0] = temp_b[8:4];	end
end	

最终输出图像的是能信号产生如下。

//图像输出有效信号产生
reg r_image_ddr3_wren;
reg[3:0] r_average_line_wren;
reg r_last_line_wren;always @(posedge clk or negedge rst_n)if(!rst_n) r_last_line_wren <= 1'b0;else if((rfifo_state == RFIFO_RDDB3) && (dcnt < IMAGE_WIDTH)) r_last_line_wren <= 1'b1;else r_last_line_wren <= 1'b0;always @(posedge clk or negedge rst_n)if(!rst_n) r_average_line_wren <= 4'd0;else beginr_average_line_wren[3:1] <= r_average_line_wren[2:0];if((rfifo_state == RFIFO_RDDB2) && (r_line_cnt > 10'd1) && i_image_ddr3_wren) r_average_line_wren[0] <= 1'b1;else r_average_line_wren[0] <= 1'b0;end	always @(posedge clk or negedge rst_n)if(!rst_n) r_image_ddr3_wren <= 1'b0;else if(r_average_line_wren[3]) r_image_ddr3_wren <= 1'b1;else if(r_last_line_wren) r_image_ddr3_wren <= 1'b1;else r_image_ddr3_wren <= 1'b0;always @(posedge clk or negedge rst_n)if(!rst_n) o_image_ddr3_wren <= 1'b0;else if((r_line_cnt == 10'd0) && i_image_ddr3_wren) o_image_ddr3_wren <= 1'b1;else if(r_image_ddr3_wren) o_image_ddr3_wren <= 1'b1;else o_image_ddr3_wren <= 1'b0;

最终输出图像数据的产生如下。

//图像数据输出
reg[15:0] r_image_ddr3_wrdb;		always @(posedge clk or negedge rst_n)if(!rst_n) r_image_ddr3_wrdb <= 16'd0;else if(r_line_cnt == 10'd0) r_image_ddr3_wrdb <= i_image_ddr3_wrdb;else if(r_image_ddr3_wren && (r_line_cnt >= IMAGE_HIGHT)) r_image_ddr3_wrdb <= w_fifo1_dout;else ;reg output_link;	always @(posedge clk or negedge rst_n)if(!rst_n) output_link <= 1'b0;else if(r_line_cnt == 10'd0) output_link <= 1'b1;else if(r_image_ddr3_wren && (r_line_cnt >= IMAGE_HIGHT)) output_link <= 1'b1;else output_link <= 1'b0;	assign o_image_ddr3_wrdb = output_link ? r_image_ddr3_wrdb:average_result;

 工程架构:

average_filter.v

`timescale 1ns / 1ps
/*
对图像进行均值滤波平滑处理
第1行、最后1行、第1列、最后1列不做处理,使用原始图像像素值输出;
对其余图像像素点,对周围像素点做如下运算:
%           	1    2    1
%   1/16 *  	2    4    2
%           	1    2    1
*/
module average_filter(input clk,		//50MHz时钟input rst_n,	//复位信号,低电平有效//Image Data flow from Image Sensorinput i_image_ddr3_wren,input i_image_ddr3_line_end,input i_image_ddr3_clr,input[15:0] i_image_ddr3_wrdb,			//Image Data flow after average transformoutput reg o_image_ddr3_wren,output[15:0] o_image_ddr3_wrdb			);parameter IMAGE_WIDTH	= 10'd640;	
parameter IMAGE_HIGHT	= 10'd480;	//数据行计数器
reg[9:0] r_line_cnt;always @(posedge clk or negedge rst_n)if(!rst_n) r_line_cnt <= 10'd0;else if(i_image_ddr3_clr) r_line_cnt <= 10'd0;else if(i_image_ddr3_line_end) r_line_cnt <= r_line_cnt+1'b1;else ;//FIFO for cache 1 line image data
reg r_fifo1_rd_en;
wire[15:0] w_fifo1_dout;
wire[9:0] w_fifo1_data_count;fifo_generator_3 	uut1_fifo_generator_3 (.clk(clk),                // input wire clk.srst(!rst_n || i_image_ddr3_clr),              // input wire srst.din(i_image_ddr3_wrdb),                // input wire [15 : 0] din.wr_en(i_image_ddr3_wren),            // input wire wr_en.rd_en(r_fifo1_rd_en),            // input wire rd_en.dout(w_fifo1_dout),              // output wire [15 : 0] dout.full(),              // output wire full.empty(),            // output wire empty.data_count(w_fifo1_data_count)  // output wire [9 : 0] data_count
);reg r_fifo2_wr_en;
reg r_fifo2_rd_en;
wire[15:0] w_fifo2_dout;
wire[9:0] w_fifo2_data_count;fifo_generator_3 	uut2_fifo_generator_3 (.clk(clk),                // input wire clk.srst(!rst_n || i_image_ddr3_clr),              // input wire srst.din(w_fifo1_dout),                // input wire [15 : 0] din.wr_en(r_fifo2_wr_en),            // input wire wr_en.rd_en(r_fifo2_rd_en),            // input wire rd_en.dout(w_fifo2_dout),              // output wire [15 : 0] dout.full(),              // output wire full.empty(),            // output wire empty.data_count(w_fifo2_data_count)  // output wire [9 : 0] data_count
);//连续读出640个数据计数控制状态机
parameter RFIFO_RESET	= 3'd0;
parameter RFIFO_IDLE	= 3'd1;
parameter RFIFO_RDDB1	= 3'd2;
parameter RFIFO_RDDB2	= 3'd3;
parameter RFIFO_WAIT	= 3'd4;
parameter RFIFO_RDDB3	= 3'd5;
reg[2:0] rfifo_state;
reg[9:0] dcnt;	//读FIFO数据个数计数器
reg[9:0] average_num;
reg[3:0] dly_cnt;always @(posedge clk or negedge rst_n)if(!rst_n) rfifo_state <= RFIFO_RESET;else if(i_image_ddr3_clr) rfifo_state <= RFIFO_RESET;else begincase(rfifo_state)RFIFO_RESET: if(w_fifo1_data_count >= IMAGE_WIDTH) rfifo_state <= RFIFO_RDDB1;else rfifo_state <= RFIFO_RESET;RFIFO_RDDB1: if(dcnt >= (IMAGE_WIDTH-1)) rfifo_state <= RFIFO_IDLE;else rfifo_state <= RFIFO_RDDB1;RFIFO_IDLE:  if(r_line_cnt >= IMAGE_HIGHT) rfifo_state <= RFIFO_WAIT;else if(w_fifo2_data_count >= IMAGE_WIDTH) rfifo_state <= RFIFO_RDDB2;else rfifo_state <= RFIFO_IDLE;			RFIFO_RDDB2: if(r_line_cnt >= IMAGE_HIGHT) rfifo_state <= RFIFO_WAIT;else if(dcnt >= (IMAGE_WIDTH-1)) rfifo_state <= RFIFO_IDLE;else rfifo_state <= RFIFO_RDDB2;RFIFO_WAIT:	 if(dly_cnt == 4'hf) rfifo_state <= RFIFO_RDDB3;	else rfifo_state <= RFIFO_WAIT;RFIFO_RDDB3: if(dcnt >= (IMAGE_WIDTH-1)) rfifo_state <= RFIFO_RESET;else rfifo_state <= RFIFO_RDDB3;						default: rfifo_state <= RFIFO_IDLE;endcaseendalways @(posedge clk or negedge rst_n)if(!rst_n) dly_cnt <= 4'd0;else if(rfifo_state == RFIFO_WAIT) dly_cnt <= dly_cnt+1'b1;else dly_cnt <= 4'd0;//读FIFO数据个数计数器
always @(posedge clk or negedge rst_n)if(!rst_n) dcnt <= 10'd0;else if((rfifo_state == RFIFO_IDLE) || (rfifo_state == RFIFO_RESET)) dcnt <= 10'd0;else if(((rfifo_state == RFIFO_RDDB1) || (rfifo_state == RFIFO_RDDB2)) && i_image_ddr3_wren) dcnt <= dcnt+1'b1;else if(rfifo_state == RFIFO_RDDB3) dcnt <= dcnt+1'b1;	else dcnt <= 10'd0;//average transform数据计数器
always @(posedge clk or negedge rst_n)if(!rst_n) average_num <= 10'd0;else if(dcnt == 10'd4) average_num <= 10'd1;else if(average_num != 10'd0) beginif(average_num < IMAGE_WIDTH) average_num <= average_num+1'b1;else average_num <= 10'd0;endelse average_num <= 10'd0;//读FIFO1使能信号产生逻辑
always @(posedge clk or negedge rst_n)if(!rst_n) r_fifo1_rd_en <= 1'b0;else if(((rfifo_state == RFIFO_RDDB1) || (rfifo_state == RFIFO_RDDB2)) && i_image_ddr3_wren) r_fifo1_rd_en <= 1'b1;else if((rfifo_state == RFIFO_RDDB3) && (dcnt < IMAGE_WIDTH)) r_fifo1_rd_en <= 1'b1;else r_fifo1_rd_en <= 1'b0;//写FIFO2是能信号产生逻辑
always @(posedge clk or negedge rst_n)if(!rst_n) r_fifo2_wr_en <= 1'b0;else r_fifo2_wr_en <= r_fifo1_rd_en;	//读FIFO2使能信号产生逻辑	
always @(posedge clk or negedge rst_n)if(!rst_n) r_fifo2_rd_en <= 1'b0;else if(((rfifo_state == RFIFO_RDDB2) || (rfifo_state == RFIFO_RDDB3)) && i_image_ddr3_wren) r_fifo2_rd_en <= 1'b1;else r_fifo2_rd_en <= 1'b0;	//图像缓存3拍
reg[15:0] data_temp_line_1[2:0];	
reg[15:0] data_temp_line_2[2:0];
reg[15:0] data_temp_line_3[4:0];always @(posedge clk) begindata_temp_line_1[0] <= w_fifo2_dout;data_temp_line_1[1] <= data_temp_line_1[0];data_temp_line_1[2] <= data_temp_line_1[1];
endalways @(posedge clk) begindata_temp_line_2[0] <= w_fifo1_dout;data_temp_line_2[1] <= data_temp_line_2[0];data_temp_line_2[2] <= data_temp_line_2[1];
endalways @(posedge clk) begindata_temp_line_3[0] <= i_image_ddr3_wrdb;data_temp_line_3[1] <= data_temp_line_3[0];data_temp_line_3[2] <= data_temp_line_3[1];data_temp_line_3[3] <= data_temp_line_3[2];data_temp_line_3[4] <= data_temp_line_3[3];
end	//图像输出average filter运算
reg[9:0] sum_a_r,sum_b_r1,sum_b_r2;
reg[9:0] sum_a_g,sum_b_g1,sum_b_g2;
reg[9:0] sum_a_b,sum_b_b1,sum_b_b2;reg[15:0] average_result;	always @(posedge clk) beginsum_a_r <= {3'b000,data_temp_line_2[1][15:11],2'b00};sum_a_g <= {2'b0,data_temp_line_2[1][10:5],2'b00};sum_a_b <= {3'b000,data_temp_line_2[1][4:0],2'b00};
end	always @(posedge clk) beginsum_b_r1 <= {4'd0,data_temp_line_2[0][15:11],1'b0}	+ {4'd0,data_temp_line_2[2][15:11],1'b0}+ {4'd0,data_temp_line_1[1][15:11],1'b0}+ {4'd0,data_temp_line_3[3][15:11],1'b0};sum_b_g1 <= {3'd0,data_temp_line_2[0][10:5],1'b0}	+ {3'd0,data_temp_line_2[2][10:5],1'b0}	+ {3'd0,data_temp_line_1[1][10:5],1'b0}	+ {3'd0,data_temp_line_3[3][10:5],1'b0};sum_b_b1 <= {4'd0,data_temp_line_2[0][4:0],1'b0}	+ {4'd0,data_temp_line_2[2][4:0],1'b0}	+ {4'd0,data_temp_line_1[1][4:0],1'b0}	+ {4'd0,data_temp_line_3[3][4:0],1'b0};sum_b_r2 <= {5'd0,data_temp_line_1[0][15:11]}	+ {5'd0,data_temp_line_1[2][15:11]}	+ {5'd0,data_temp_line_3[2][15:11]}	+ {5'd0,data_temp_line_3[4][15:11]};sum_b_g2 <= {4'd0,data_temp_line_1[0][10:5]}	+ {4'd0,data_temp_line_1[2][10:5]}	+ {4'd0,data_temp_line_3[2][10:5]}	+ {4'd0,data_temp_line_3[4][10:5]};sum_b_b2 <= {5'd0,data_temp_line_1[0][4:0]}		+ {5'd0,data_temp_line_1[2][4:0]}	+ {5'd0,data_temp_line_3[2][4:0]}	+ {5'd0,data_temp_line_3[4][4:0]};	
end	wire[9:0] temp_r = sum_a_r+(sum_b_r1+sum_b_r2);
wire[9:0] temp_g = sum_a_g+(sum_b_g1+sum_b_g2);
wire[9:0] temp_b = sum_a_b+(sum_b_b1+sum_b_b2);always @(posedge clk) beginif((average_num == 10'd1) || (average_num == IMAGE_WIDTH)) average_result <= data_temp_line_2[2];	//第1列和最后1列使用原值else beginaverage_result[15:11] = temp_r[8:4];		average_result[10:5] = temp_g[9:4];average_result[4:0] = temp_b[8:4];	end
end	//图像输出有效信号产生
reg r_image_ddr3_wren;
reg[3:0] r_average_line_wren;
reg r_last_line_wren;always @(posedge clk or negedge rst_n)if(!rst_n) r_last_line_wren <= 1'b0;else if((rfifo_state == RFIFO_RDDB3) && (dcnt < IMAGE_WIDTH)) r_last_line_wren <= 1'b1;else r_last_line_wren <= 1'b0;always @(posedge clk or negedge rst_n)if(!rst_n) r_average_line_wren <= 4'd0;else beginr_average_line_wren[3:1] <= r_average_line_wren[2:0];if((rfifo_state == RFIFO_RDDB2) && (r_line_cnt > 10'd1) && i_image_ddr3_wren) r_average_line_wren[0] <= 1'b1;else r_average_line_wren[0] <= 1'b0;end	always @(posedge clk or negedge rst_n)if(!rst_n) r_image_ddr3_wren <= 1'b0;else if(r_average_line_wren[3]) r_image_ddr3_wren <= 1'b1;else if(r_last_line_wren) r_image_ddr3_wren <= 1'b1;else r_image_ddr3_wren <= 1'b0;always @(posedge clk or negedge rst_n)if(!rst_n) o_image_ddr3_wren <= 1'b0;else if((r_line_cnt == 10'd0) && i_image_ddr3_wren) o_image_ddr3_wren <= 1'b1;else if(r_image_ddr3_wren) o_image_ddr3_wren <= 1'b1;else o_image_ddr3_wren <= 1'b0;//图像数据输出
reg[15:0] r_image_ddr3_wrdb;		always @(posedge clk or negedge rst_n)if(!rst_n) r_image_ddr3_wrdb <= 16'd0;else if(r_line_cnt == 10'd0) r_image_ddr3_wrdb <= i_image_ddr3_wrdb;else if(r_image_ddr3_wren && (r_line_cnt >= IMAGE_HIGHT)) r_image_ddr3_wrdb <= w_fifo1_dout;else ;reg output_link;	always @(posedge clk or negedge rst_n)if(!rst_n) output_link <= 1'b0;else if(r_line_cnt == 10'd0) output_link <= 1'b1;else if(r_image_ddr3_wren && (r_line_cnt >= IMAGE_HIGHT)) output_link <= 1'b1;else output_link <= 1'b0;	assign o_image_ddr3_wrdb = output_link ? r_image_ddr3_wrdb:average_result;endmodule

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/333731.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

过去半年信竞基础学习总结

当Richard同学从去年7月份&#xff0c;开始从YY班学习C和信息学竞赛时&#xff0c;他对C还一无所知&#xff0c;但对计算机和编程的兴趣让他一直都乐在其中。在过去的半年中&#xff0c;通过参加各个平台的月赛&#xff0c;让他更加热爱编程&#xff0c;也让Richard更加了解自己…

中国建设银行 关于解决微软升级导致插入网银盾无法自动打开企业网银的通知

关于解决微软升级导致插入网银盾无法自动打开企业网银的通知 发布时间&#xff1a;2023-10-18 尊敬的客户&#xff1a; 近期Windows操作系统升级会禁止使用IE浏览器&#xff0c;可能会导致您在插入网银盾后无法自动弹出企业网银登录页面&#xff0c;您可以通过以下方式解决&…

MODBUS转PROFINET网关与全数字交流伺服配置案例

MODBUS转PROFINET网关连接与全数字交流伺服驱动系统的配置案例&#xff0c;这一通信方式极大地简化了工业自动化系统中的数据传输和控制过程。变频器和伺服电机可以实现数据交流和控制指令的实时传输&#xff0c;从而实现更精确更高效的生产过程。 案例简介&#xff1a;本案例是…

NODE笔记 0

一些简单的node学习笔记记录&#xff0c;是Vue等前端框架的基础 入门学习备忘录 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 node.js 内置网络服务器&#xff0c;是前端框架学习的基础&#xff1a; 概念&#xff1a;…

【python爬虫开发实战 情感分析】利用爬虫爬取城市评论并对其进行情感分析

&#x1f680;个人主页&#xff1a;为梦而生~ 关注我一起学习吧&#xff01; &#x1f4a1;专栏&#xff1a; python网络爬虫从基础到实战 带你学习爬虫从基础到实战 深度学习带你感受AI的魅力 &#x1f4a1;往期推荐&#xff1a; ⭐️前面比较重要的基础内容&#xff1a; 【Py…

安全远控如何设置?揭秘ToDesk、TeamViewer 、向日葵安全远程防御大招

写在前面一、远程控制&#xff1a;安全性不可忽略二、远控软件安全设置实测 ◉ ToDesk◉ TeamViewer◉ 向日葵 三、远控安全的亮点功能四、个人总结与建议 写在前面 说到远程办公&#xff0c;相信大家都不陌生。远程工作是员工在家中或者其他非办公场所上班的一种工作模式&am…

「PyMuPDF 专栏 」PyMuPDF创建PDF、拆分PDF

文章目录 一、本章前言二、使用PyMuPDF创建PDF文档1、实例代码2、过程详解①. 安装PyMuPDF②. 导入PyMuPDF模块③. 创建一个新的PDF文档④. 添加页面和内容⑤. 保存文档 三、使用PyMuPDF拆分PDF文档1、实例代码2、过程解析①. 导入模块②. 定义函数③. 打开源PDF文件④. 遍历页…

大模型笔记【2】 LLM in Flash

Apple最近发表了一篇文章&#xff0c;可以在iphone, MAC 上运行大模型&#xff1a;【LLM in a flash: Efficient Large Language Model Inference with Limited Memory】。 主要解决的问题是在DRAM中无法存放完整的模型和计算&#xff0c;但是Flash Memory可以存放完整的模型。…

大模型训练算力利用率达60%,蚂蚁开源分布式训练扩展库ATorch

近日&#xff0c;蚂蚁集团宣布开源大模型分布式训练加速扩展库ATorch。ATorch可针对不同模型和硬件资源&#xff0c;实现深度学习自动资源动态优化和分布式训练稳定性提升&#xff0c;帮助提升深度学习的智能性。据了解&#xff0c;在大模型训练中&#xff0c;ATorch千亿模型千…

第7章 DOM(下)

学习目标 熟悉节点的概念&#xff0c;能够说出节点的属性和层级 掌握节点操作&#xff0c;能够完成节点的获取、创建、添加、移除和复制操作 掌握事件的进阶操作&#xff0c;能够实现事件的监听和移除 熟悉DOM事件流&#xff0c;能够说出事件捕获和事件冒泡两种方式的区别 …

JavaScript版数据结构与算法(一)栈、队列、链表、集合、树

一、前言 为什么要学习数据结构与算法&#xff1f;最重要的就是面试要考算法&#xff0c;另外就是如果在实际工作当中&#xff0c;能够使用算法优化代码&#xff0c;会提升代码质量和运行效率&#xff0c;作为一名前端人员可能在实际中用的并不是特别多。数据结构与算法是分不…

4.6 BOUNDARY CHECKS

我们现在扩展了tile矩阵乘法内核&#xff0c;以处理具有任意宽度的矩阵。扩展必须允许内核正确处理宽度不是tile宽度倍数的矩阵。通过更改图4.14中的示例至33 M、N和P矩阵&#xff0c;图4.18创建了矩阵的宽度为3&#xff0c;不是tile宽度&#xff08;2&#xff09;的倍数。图4.…