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-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

  • 11
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
r40_tinav2.1_最终验证通过_使用CB-S来验证OV5640有横条纹fpscamera+SPI2.0成功_20171114_1443没有外层目录.7z 开发板:CB-S 1、(可选修改/调试技巧:) 除了ov5640.c之外,其它的驱动都不编译,节省编译时间! W:\ov5640_spi20_r40t\lichee\linux-3.10\drivers\media\platform\sunxi-vfe\device\Makefile obj-m += ov5640.o #obj-m += ov2640.o #obj-m += ov7736.o #obj-m += s5k4ec.o #obj-m += s5k4ec_mipi.o #obj-m += gc2035.o #obj-m += gt2005.o #obj-m += gc0307.o #obj-m += gc0308.o #obj-m += gc0328.o #obj-m += gc0328c.o #obj-m += gc2145.o #obj-m += gc0329.o #obj-m += gc0311.o #obj-m += hi253.o #obj-m += sp2518.o #obj-m += sp2519.o #obj-m += sp0718.o #obj-m += sp0838.o #obj-m += ov16825.o #obj-m += ov5650.o #obj-m += ov5647.o #obj-m += ov5647_mipi.o #obj-m += t8et5.o #obj-m += s5k4e1.o #obj-m += s5k4e1_mipi.o #obj-m += sp2518.o #obj-m += sp0718.o #obj-m += gc5004.o #obj-m += gc5004_mipi.o #obj-m += ov5648.o #obj-m += ar0330.o #obj-m += ov5648.o #obj-m += sp5408.o #obj-m += ov12830.o #obj-m += ov8825.o #obj-m += ov8850.o #obj-m += gc2155.o #obj-m += ov8858.o #obj-m += ov13850.o #obj-m += imx214.o #obj-m += ov8858_4lane.o #obj-m += sp5409.o #obj-m += s5k5e2yx.o #obj-m += ov2710_mipi.o #obj-m += ov2686.o (这里是看ov5640的驱动的probe执行是否正确?设备ID是否读取成功!) W:\ov5640_spi20_r40t\lichee\linux-3.10\drivers\media\platform\sunxi-vfe\device\ov5640.c static int sensor_detect(struct v4l2_subdev *sd) { data_type rdval; printk("****wyb %s:%d/%s()! \n", __FILE__, __LINE__, __func__); LOG_ERR_RET(sensor_read(sd, 0x300a, &rdval;)) printk("****wyb %s:%d/%s()! 0x300a rdval=0xx\n", __FILE__, __LINE__, __func__, rdval); if(rdval != 0x56) return -ENODEV; LOG_ERR_RET(sensor_read(sd, 0x300b, &rdval;)) printk("****wyb %s:%d/%s()! 0x300b rdval=0xx\n", __FILE__, __LINE__, __func__, rdval); if(rdval != 0x40) return -ENODEV; return 0; } (在全志R16平台改过这个文件,让摄像头不要休眠,但是全志R40平台的tina v2.1系统下不需要修改!) W:\ov5640_spi20_r40t\lichee\linux-3.10\drivers\media\platform\sunxi-vfe\vfe.c 2、(可
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值