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.mzph.cn/news/608602.shtml

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

相关文章

遥感影像-语义分割数据集:云数据集详细介绍及训练样本处理流程

原始数据集详情 简介&#xff1a;该云数据集包括150张RGB三通道的高分辨率图像&#xff0c;在全球不同区域的分辨率从0.5米到15米不等。这些图像采集自谷歌Earth的五种主要土地覆盖类型&#xff0c;即水、植被、湿地、城市、冰雪和贫瘠土地。 KeyValue卫星类型谷歌Earth覆盖区…

[足式机器人]Part2 Dr. CAN学习笔记-动态系统建模与分析 Ch02-8 Bode Plot伯德图

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-动态系统建模与分析 Ch02-8 Bode Plot伯德图 Bode Plot 手绘技巧与应用

第7章-第6节-Java中的Map集合

1、HashMap&#xff1a; 1&#xff09;、 引入 如果业务需要我们去用姓名的拼音手写字母匹配完整姓名&#xff0c;那么如果用单列数据&#xff0c;我们可能需要两个集合才能存储&#xff0c;而且两个集合之间没有关联不好操作&#xff0c;这种时候双列数据就会起很大作用 2&…

【前端素材】bootstrap4实现服装鞋饰电商平台Doron

一、需求分析 一个服装鞋饰电子商务页面是一个在线平台&#xff0c;用于展示和销售各种服装、鞋子和配饰产品。它通常具有以下功能&#xff1a; 产品展示&#xff1a;服装鞋饰电子商务页面会展示各种服装、鞋子和配饰产品的图片、描述和价格。这些产品可以按照不同的分类&#…

批量剪辑方法:掌握视频剪辑技巧,按指定时长轻松分割视频

在视频制作和编辑过程中&#xff0c;经常要批量处理和剪辑大量的视频片段。学会批量剪辑方法可以提高工作效率&#xff0c;还可以使视频编辑更加准确和高效。下面来看下云炫AI智剪如何按指定时长轻松分割视频的批量剪辑方法。 分割后的视频文件效果&#xff0c;已分割分段的视…

PHPStudy快速搭建网站并结合内网穿透远程访问本地站点

文章目录 [toc]使用工具1. 本地搭建web网站1.1 下载phpstudy后解压并安装1.2 打开默认站点&#xff0c;测试1.3 下载静态演示站点1.4 打开站点根目录1.5 复制演示站点到站网根目录1.6 在浏览器中&#xff0c;查看演示效果。 2. 将本地web网站发布到公网2.1 安装cpolar内网穿透2…

减少客户流失并留住长期客户:实用策略与实践分享

一、什么是客户流失&#xff1f; 客户流失是指客户停止付费并离开您的产品/服务。流失率是指在特定时间内离开产品/服务的用户除以用户总数的百分比。 比如&#xff1a; 假设某媒体平台是一个基于订阅的流媒体平台&#xff0c;为用户提供各种电影、电视剧、纪录片等。到二月…

Linux--防火墙,实验案例:基于区域、服务、端口的访问控制

实验环境 某公司的Web服务器&#xff0c;网关服务器均采用Linux CentOS 7.3操作系统&#xff0c;如图2.13所示。为了 加强网络访问的安全性&#xff0c;要求管理员熟悉firewalld防火墙规则的编写&#xff0c;以便制定有效、可行的主机防护策略。 需求描述 > 网关服务器ens3…

RT-Thread:ADC 框架应用,通过 STM32CubeMX 配置 STM32 ADC驱动

关键词&#xff1a;ADC,RT-Thread ADC,STM32 ADC应用 说明&#xff1a;本笔记是记录如何开启 RT-Thread 框架的ADC功能&#xff0c;使用系统自带的ADC函数&#xff0c;并通过 STM32CubeMX 配置 STM32 ADC驱动 。 1. 打开board.h 文件&#xff0c;找到ADC 使用配置的流程&…

猴子选大王

思路&#xff1a;首先举个例子&#xff1a;当N 5 时 1 2 3 4 5 3 3 3 3 输出4 请观看代码 …

37-数据类型,一元运算符typeof,字符串string,布尔Boolean,未定义undefined,空null,数组Array

<body><script>// 0.1加0.2不等于0.3&#xff0c;正确的运算方法如下console.log(0.10.2);var x 0.1;var y 0.2;console.log((x*10y*10)/10);</script> </body> 简单数据类型&#xff08;5种&#xff09;&#xff1a;数字number&#xff0c;字符串s…

【漏洞挖掘】sourcemap、webpck源码泄露漏洞

目录 1.使用npm安装reverse-souecemap1.1 安装npm1.2 npm配置优化1.2.1 全局模块存放路径和cache路径1.2.2 更改镜像源1.2.3 升级更新npm&#xff0c;并添加环境变量 1.3 安装 reverse-sourcemap 2. 使用reverse-sourcemap逆向获取源码3. 漏洞修复 1.使用npm安装reverse-souece…

基于 Python+Neo4j+医药数据,构建了一个知识图谱的自动问答系统

知识图谱是目前自然语言处理的一个热门方向。目前知识图谱在各个领域全面开花&#xff0c;如教育、医疗、司法、金融等。 本项目立足医药领域&#xff0c;以垂直型医药网站为数据来源&#xff0c;以疾病为核心&#xff0c;构建起一个包含7类规模为4.4万的知识实体&#xff0c;…

llama.cpp模型推理之界面篇

目录 前言 一、llama.cpp 目录结构 二、llama.cpp 之 server 学习 1. 介绍 2. 编译部署 3. 启动服务 4、扩展或构建其他的 Web 前端 5、其他 前言 在《基于llama.cpp学习开源LLM本地部署》这篇中介绍了基于llama.cpp学习开源LLM本地部署。在最后简单介绍了API 的调用方…

智汇云舟受邀出席《城市轨道交通公共安全防范安全评价标准》专家评审会

1月3日&#xff0c;由中国城市公共交通协会归口的《城市轨道交通公共安全防范安全评价标准》&#xff08;以下简称“《标准》”&#xff09;送审稿审查会顺利召开。该标准由同方威视技术股份有限公司、上海新海信通信息技术有限公司和中安保实业集团有限公司主编&#xff0c;北…

python进阶 -- 日志装饰器详解

日志 日志&#xff1a;记录程序运行的时候&#xff0c;出现的问题&#xff0c;或者说验证流程是否正常 在实际工作中&#xff0c;python的脚本命令一般是放在服务器执行的linux系统 日志其实就是记录程序运行时出现的问题、或者正常的打印&#xff0c;协助出现问题的时解决排查…

AMEYA360 | 热敏电阻的工作原理及作用 热敏电阻厂商有哪些

摘要&#xff1a;热敏电阻是一种传感器电阻&#xff0c;其电阻值随着温度的变化而改变。热敏电阻的工作原理是使用传感器来帮助调节温度高低&#xff0c;作用包括电压调节&#xff0c;音量控制&#xff0c;时间延迟和电路保护。热敏电阻具有测温、温度补偿、过热保护、液面测量…

ArrayList 与 LinkedList 区别?

如果你现在需要准备面试&#xff0c;可以关注我的公众号&#xff1a;”Tom聊架构“&#xff0c;回复暗号&#xff1a;”578“&#xff0c;领取一份我整理的50W字面试宝典&#xff0c;可以帮助你提高80%的面试通过率&#xff0c;价值很高&#xff01;&#xff01; 是否保证线程安…

竞赛保研 基于深度学习的人脸识别系统

前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的人脸识别系统 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/…

如何脱离keil在vscode上实现STM32单片机编程

【VScode Embedded IDE】Keil工程导入VScode&#xff0c;与Keil协同开发MCU_vscode编辑keil工程-CSDN博客 从零开始的51单片机——VsCodeEIDE环境搭建_vscodeeidesdcc-CSDN博客 结合一下这两个大佬的博客就是可以实现STM32的编程了 主要要点&#xff1a; &#xff08;1&#…