OV5640 摄像头的图像平滑处理

news2024/11/16 16:49:06

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

        上电初始,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-1
    for j=2:n-1
        A(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-1
    for j=2:n-1
        A(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-1
    for j=2:n-1
        A(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
end

B=A/16;

%output
 imwrite(B,'lena.tif','tif');
 imshow('.\lena.jpg');title('origin image');figure
 imshow('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 Sensor
			input 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 transform
			output 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 begin
		case(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;
		endcase
	end

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) begin
		if(average_num < IMAGE_WIDTH) average_num <= average_num+1'b1;
		else average_num <= 10'd0;
	end
	else 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) begin
	data_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];
end

always @(posedge clk) begin
	data_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];
end

always @(posedge clk) begin
	data_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) begin
	sum_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) begin
	sum_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) begin
	if((average_num == 10'd1) || (average_num == IMAGE_WIDTH)) average_result <= data_temp_line_2[2];	//第1列和最后1列使用原值
	else begin
		average_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 begin
		r_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 Sensor
			input 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 transform
			output 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 begin
		case(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;
		endcase
	end
	
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;
	
	//读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) begin
		if(average_num < IMAGE_WIDTH) average_num <= average_num+1'b1;
		else average_num <= 10'd0;
	end
	else 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) begin
	data_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];
end

always @(posedge clk) begin
	data_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];
end

always @(posedge clk) begin
	data_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) begin
	sum_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) begin
	sum_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) begin
	if((average_num == 10'd1) || (average_num == IMAGE_WIDTH)) average_result <= data_temp_line_2[2];	//第1列和最后1列使用原值
	else begin
		average_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 begin
		r_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.coloradmin.cn/o/1369048.html

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

相关文章

视频监控系统EasyCVR如何通过调用API接口查询和下载设备录像?

智慧安防平台EasyCVR是基于各种IP流媒体协议传输的视频汇聚和融合管理平台。视频流媒体服务器EasyCVR采用了开放式的网络结构&#xff0c;支持高清视频的接入和传输、分发&#xff0c;平台提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联…

Python词云wordcloud库不显示中文

博主之前在项目中发现Python的词云库wordcloud显示的都是方框&#xff0c;别担心&#xff0c;我有一个妙招让你的中文词云变得美观又清晰&#xff01; 问题 wordcloud是一个基于python的词云生成库&#xff0c;它可以让你用简单的代码创建出各种形状和颜色的词云图像。 word…

qt初入门2:qt选择一个文件或者目录,获取当前目录,操作文件目录等整理

最近用qt操作文件或者目录的动作比较多&#xff0c;简单整理一下常用的接口&#xff0c;方便回顾。 总的来说&#xff0c;其实就是用文件选择对话框QFileDialog类&#xff0c;以及操作文件信息的QFileInfo类&#xff0c;以及相关QCoreApplication中静态成员函数获取一些信息&a…

js(JavaScript)数据结构之数组(Array)

什么是数据结构&#xff1f; 下面是维基百科的解释&#xff1a; 数据结构是计算机存储、组织数据的方式。数据结构意味着接口或封装&#xff1a;一个数据结构可被视为两个函数之间的接口&#xff0c;或者是由数据类型联合组成的存储内容的访问方法封装。 我们每天的编码中都会…

有关“修改地址”的回复话术大全

类型一:不能改地址 1.亲非常抱歉 这边发货后客服就没法帮您操作修改地址了 2.非常遗憾&#xff0c;订单一旦下单完成 地址是无法进行修改的。如果您这边需要修改地址的话也是可以尝试去和这个物流方进行协商的哦&#xff0c;这边没有修改的按钮没法操作的 3.抱歉呢亲亲。修改…

CentOS服务器之间免密登录和传输文件

使用过 Jenkins 的同学都知道&#xff0c;Jenkins 会在远程服务器上执行一些命令&#xff0c;如&#xff1a;cd /home/wwwroot/ && git pull&#xff0c;这时候就需要在 Jenkins 服务器上配置免密登录&#xff0c;以及在远程服务器上配置免密登录&#xff0c;这样才能实…

vue 自定义网页图标 favicon.ico 和 网页标题

效果预览 1. 添加配置 vue.config.js 在 module.exports { 内添加 // 自定义网页图标pwa: {iconPaths: {favicon32: "./favicon.ico",favicon16: "./favicon.ico",appleTouchIcon: "./favicon.ico",maskIcon: "./favicon.ico",msTil…

为什么选择嬴图?

图数据库、图计算、图中台都是用图论的方式去构造实体间的关联关系&#xff0c;实体用顶点来表达&#xff0c;而实体间的关系用边来表达。图数据库的这种简洁、自由、高维但100%还原世界的数据建模的方式让实体间的关联关系的计算比SQL类的数据库高效成千上万倍。 图&#xff1…

互联网外包公司干了2个月,技术退步明显了.......

先说一下自己的情况&#xff0c;本科毕业&#xff0c;18年通过校招进入南京某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能…

cosmos及特定应用程序的区块链

特定应用程序的区块链,简单来说&#xff0c;一个区块链就是一个专门的应用程序。为了实现某一特定的去中心化应用而专门实现一个区块链。 传统的用智能合约构建去中心化应用不行吗&#xff1f; 灵活性不足&#xff1a;智能合约本质上受到虚拟机本身的限制。例如&#xff0c;以…

详解Oracle数据库的启动

Oracle数据库的启动&#xff0c;其概念可参考Overview of Instance and Database Startup。 其过程可参见下图&#xff1a; 当数据库从关闭状态进入打开数据库状态时&#xff0c;它会经历以下阶段。 阶段Mount状态描述1实例在没有挂载数据库的情况下启动实例已启动&#xff…

【STM32F103】RCC复位和时钟控制

前言 之前介绍外设的时候总是没有提到RCC&#xff0c;但其实我们使用STM32的外设之前都需要做的一步就是打开外设时钟。原本想着没什么可说的&#xff0c;就是用什么外设的时候就在开头加一行代码打开外设时钟就好了。直到最近写到了TIM定时器&#xff0c;我才开始觉得应该说一…

scipy-interpolate整理

文章目录 scipy-interpolate整理Univariate interpolation 单变量插值Multivariate interpolation 多元插值Unstructured data 非结构化数据&#xff1a;:For data on a grid 对于网格上的数据&#xff1a;:Tensor product polynomials 张量积多项式&#xff1a;: 1-D Splines …

Python(33):数据断言(查询数据库数据和插入数据对比)

Python(33):数据断言(查询数据库数据和插入数据对比) 前言&#xff1a; 需求&#xff1a;需要针对查询数据库数据和插入的数据进行对比&#xff0c;用Python语言进行编写 数据库查询的结果可参考&#xff1a;https://blog.csdn.net/fen_fen/article/details/135462484 1、查…

java解析json复杂数据的两种思路

文章目录 一、原始需求二、简单分析三、具体实现一1. api接口2. 接口返回3. json 数据解析1.&#xff09;引入Jackson库2.&#xff09;定义实体3.&#xff09;解析json字符串4.&#xff09;运行结果 4. 过程分析 四、具体实现二1. 核心代码2.运行结果 五、方案比较六、源码传送…

Python 自学(七) 之面向对象

目录 1. 类的初始化函数 __init__ P186 2. 动态的为类和对象添加属性 P190 3. 类的访问限制 __xxx P192 4. 类的继承及方法重写 P197 1. 类的初始化函数 __init__ P186 每当创建一个类的实例时&#xff0c;__init__都会被执…

C#高级 10 Linq操作

1.Linq操作介绍 Linq操作是C#集成的类似于数据库语言的操作&#xff0c;是通过将数据库的表名映射为类&#xff0c;把数据库的列名映射为属性。 Linq查询主要分为3类&#xff1a;Linq to object(数组、list集合) --内存里面的数据 Linq to sql(查询数据库用的) --在数据库数据…

告别冗余空白,批量删除空白行

你是否遇到过这样的尴尬情况&#xff1a;花费了大量时间整理的文档&#xff0c;却在最后发现其中充斥着无用的空白行&#xff0c;这些多余的空行不仅影响美观&#xff0c;还让整个文档显得杂乱无章。今天&#xff0c;我要给大家介绍一款强大且实用的工具——首助编辑高手&#…

Go采集1688网站数据对比商品价格

最近看了下多多和1688的一些商品价格&#xff0c;发现好多店铺都是无货源拿货一件发货&#xff0c;这就导致层层叠加价格翻了不知道几倍&#xff0c;真所谓多花钱办的事还是一样&#xff0c;因此&#xff0c;今天我就通过一个爬虫程序监控对应商品价格&#xff0c;了解行业龙头…

Java 最小优先队列API设计与实现

Java 学习面试指南&#xff1a;https://javaxiaobear.cn 最小的元素放在数组的索引1处。每个结点的数据总是小于等于它的两个子结点的数据。 1、API设计 类名MinPriorityQueue构造方法MinPriorityQueue(int capacity)&#xff1a;创建容量为capacity的MinPriorityQueue对象成员…