FPGA图像OTSU自适应阈值实现

目录

        在图像分割的过程中,通常需要进行二值化处理。所谓的二值化,就是把图像分成黑和白两部分,用于凸显图像的某些特征。在毕设的项目中,鉴于图像的背景是白色的,原定是想把图像转化为灰度图像在进行分离,但是实测过程中,白色背景也会出现阴影,或者是水果的颜色过浅色(比如黄色的香蕉),这些都会导致分割失败。因此为了针对这个现象,我使用HSV的S域来进行分离,因为无论是背景的白色还是灰色S域的大小都是很小,因此可以通过这个来将其进行分离操作,同时使用大律法算法进一步提高分离的效率。        


一、什么是OTSU

        最大类间方差法是由日本学者大津(Nobuyuki Otsu)于1979年提出的,是一种自适合于双峰情况的自动求取阈值的方法,又叫大津法,简称OTSU。它是按图像的灰度特性,将图像分成背景和目标2部分。背景和目标之间的类间方差越大,说明构成图像的两部分的差别越大,当部分目标错分为背景或部分背景错分为目标都会导致2部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。

二、OTSU的实现原理

        它按图像的灰度特性,将图像分成背景和前景两部分。因方差是灰度分布均匀性的一种度量,背景和前景之间的类间方差越大,说明构成图像的两部分的差别越大,因此分割的效果也就最好。

        假设ω(D0)为背景D0出现的概率,ω(D1)为目标出现的概率,u(D0)u(D1)分别为D0D1的灰度均值。ω(D0)ω(D1)u(D0)以及u(D1)计算公式如下所示:

        其中P(i)为灰度级i出现的概率,T为背景和目标的分界灰度级,L为最大灰度级。则整幅图像的背景与目标之间的类间方差g为:

        

三、OTSU的FPGA实现

        这里为了适应FPGA流水线的计算逻辑,先将公式进行转换,尽可能少的使用乘除法逻辑资源:

        其中,N为总的像素个数,G为整幅图像的平均灰度,因此我们只需要计算,而这两个可以通过流水线进行计算,依次计算T从1到256的所有数值。

        该模块由直方图统计模块和类间方差计算模块两部分构成,系统整体架构框图如下:

        OTSU全局阈值分割的实现首先需要对当前图像进行灰度直方图统计。具体通过双口RAM架构实现像素频次统计:当灰度图像输入时,像素值作为地址信号输入RAM地址总线,同时触发灰度计数器工作。该计数器用于统计各灰度值连续出现的频次,在灰度值切换时,将当前计数值与RAM对应地址的存储数据进行累加后回写,这也是使用双口RAM的原因。通过这种地址映射机制,RAM的0-255地址空间最终将精确存储对应灰度级的像素数量分布。代码如下:

module VIP_Histogram_Get(
    input 	wire 			clk 		,  //时钟信号
	input	wire 			rst_n 		,  //低电平复位信号
	input 	wire 			cam_href	,  //行同步信号
	input	wire 			cam_vsync	,  //场同步信号
	input	wire 			cam_valid	,  //像素数据有效信号
	input 	wire 	[7:0]	cam_gray 	,  //灰度数据

	output 	wire 			po_histo_vld,  //输出数据有效信号
	output 	wire 	[31:0]	po_histo_data  //输出直方图统计结果.虽然图像大小为320X240=76800个数据,理论上单个灰度级最多有76800个数据,用17位足以表达,但是一般设定为2的整数次幂
);

//==================================================================
//parameter define
//==================================================================
parameter   IMG_WIDTH   =   9'd320 ;   //图像宽度
parameter   IMG_HEIGHT  =   8'd240 ;   //图像高度
parameter   GRAY_LEVEL  =   9'd256 ;   //图像灰度级

parameter   IDLE 		 =   4'b0001;//空闲状态
parameter   CALCULATE    =   4'b0010;//统计图像直方图状态
parameter   GET_HISTO    =   4'b0100;//输出直方图

//内部信号
reg 	[3:0]	state 		     ;//状态寄存器

//统计直方图阶段
reg 	[7:0]	cnt_row 	;
wire 			add_cnt_row ;
wire 			end_cnt_row	;

reg 			cam_valid_dly0;//数据有效延时信号
reg 			cam_valid_dly1;//数据有效延时信号
reg 	[7:0]	cam_gray_dly0;//有效数据延时
reg 	[7:0]	cam_gray_dly1;//有效数据延时
reg 	[31:0]	cal_pixle;//相同的像素统计值
reg 	[31:0]	cal_value;//写入RAM的统计值
reg 			cal_value_vld;//写入RAM数据有效信号
reg 			cal_one_row_done;//统计一行图像数据结束
wire 	[7:0]	cal_wr_ram_addr;//统计状态下写RAM的地址
wire 	[7:0]	cal_rd_ram_addr;//统计状态下读RAM的地址

//读出数据阶段
reg 			get_data_flag 	;
reg 	[7:0]	cnt_get 		;
wire 			add_cnt_get 	;
wire 			end_cnt_get 	;
reg 			histo_data_vld 	;
reg 			histo_data_vld_d;
wire 	[31:0]	histo_data 		;

//Block RAM 相关信号
reg             ram_choose      ;
wire	[31:0]  rd_ram_data     ;
reg 			wr_0_ram_en 	;//写RAM使能信号
reg 	[7:0]	wr_0_ram_addr	;//写RAM地址
reg 	[31:0]	wr_0_ram_data ;//写入RAM的数据
reg  	[7:0]	rd_0_ram_addr ;//读RAM的地址
wire	[31:0]	rd_0_ram_data	;//从RAM中读出的数据

reg 			wr_1_ram_en 	;//写RAM使能信号
reg 	[7:0]	wr_1_ram_addr	;//写RAM地址
reg 	[31:0]	wr_1_ram_data ;//写入RAM的数据
reg  	[7:0]	rd_1_ram_addr ;//读RAM的地址
wire	[31:0]	rd_1_ram_data	;//从RAM中读出的数据

reg 	[1:0]	cam_vsync_dly    ;

/*************************打拍操作***************************/
//对输入的灰度数据和数据有效信号延时2拍,主要是为了比较相邻的像素灰度值是否相同
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cam_vsync_dly   <=  2'd0            ;
        cam_valid_dly0  <=  1'b0            ;
        cam_valid_dly1  <=  1'b0            ;
        cam_gray_dly0   <=  8'd0            ;
        cam_gray_dly1   <=  8'd0            ;  
        histo_data_vld_d<=  1'b0            ;
    end
        cam_valid_dly0  <=  cam_valid       ;
        cam_valid_dly1  <=  cam_valid_dly0  ;
        cam_gray_dly0   <=  cam_gray        ;
        cam_gray_dly1   <=  cam_gray_dly0   ;
        histo_data_vld_d<=  histo_data_vld  ;
        cam_vsync_dly   <=  {cam_vsync_dly[0],cam_vsync};
end

/*************************统计阶段***************************/
assign cal_wr_ram_addr  =   cam_gray_dly1;  //写入数据RAM的地址,写入数据落后于cam_valid信号2个时钟周期
assign cal_rd_ram_addr  =   cam_gray     ;  //读出数据RAM的地址

//-------------cal_pixle(计算相邻且相等的像素灰度值个数)
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n)
        cal_pixle   <=  32'd1;  //初始值设置为1
    else if((state == CALCULATE) && (cam_valid_dly0 == 1'b1)) begin
        if(cam_gray != cam_gray_dly0) //相邻两个像素点的灰度值不同,统计值回到1
            cal_pixle   <=  32'd1;
        else if(cam_valid == 1'b0)
            cal_pixle   <=  32'd1;
        else if(cam_gray == cam_gray_dly0)
            cal_pixle   <=  cal_pixle + 1'b1;
    end else 
        cal_pixle   <=  32'd1;
end


//-------------cal_value(将cal_pixle写入RAM地址)
assign rd_ram_data = ram_choose ? rd_1_ram_data : rd_0_ram_data;
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cal_value       <=  32'd0;
        cal_value_vld   <=  1'b0;
    end else if(state == CALCULATE) begin
        if((cam_gray != cam_gray_dly0) && (cam_valid_dly0 == 1'b1)) begin //相邻两个像素灰度值不同写入
            //从RAM中读出的数据,有一拍的延时,这里保证了数据对齐
            cal_value       <=  rd_ram_data + cal_pixle;
            cal_value_vld   <=  1'b1;
        end else if((cam_valid == 1'b0) && (cam_valid_dly0 == 1'b1)) begin //当检测到cam_valid信号下降沿写入
            cal_value       <=  rd_r
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值