16.直方图均衡化

发布于:2024-05-20 ⋅ 阅读:(163) ⋅ 点赞:(0)

数字图像处理(17): 直方图均衡化处理

简介

  直方图均衡化是一种简单有效的图像增强技术,通过改变图像的直方图来改变图像中各像素的灰度,主要用于增强动态范围偏小的图像的对比度。当原始图像的灰度分布较为集中的时候,可能造成图像不够清晰,图像会过曝或者过暗。采用直方图均衡化,可以将原本较为集中的灰度分布变换成较为均匀的形式,增加了图像的灰度之间的动态范围,从而增加了图像的对比度。直方图均衡化的主要思想就是对图像灰度较为集中的地方进行拉伸展宽,对图像中像素较少的灰度值进行归并。

直方图均衡化理论基础

  严格来说,图像的灰度直方图是一个一维的离散函数,可以写成:
h ( k ) = n k           k = 0 , 1 , 2 , 3 , . . . , L − 1 (1) h(k)=nk \,\,\,\,\,\,\,\,\,k=0,1,2,3,...,L-1\tag{1} h(k)=nkk=0,1,2,3,...,L1(1)
  公式中 n k nk nk是图像 f ( x , y ) f(x,y) f(x,y)中灰度级为 k k k的像素的个数。直方图的每一列的高度对应 n k nk nk。直方图提供了原图中各种灰度值分布的情况,在直方图的基础上,进一步定义归一化的直方图为灰度级出现的相对频率 P r ( k ) P_r(k) Pr(k),即:
P r ( k ) = n k N (2) P_r(k)=\frac{nk}{N}\tag{2} Pr(k)=Nnk(2)
  式中, N N N表示图像 f ( x , y ) 的像素的总数 f(x,y)的像素的总数 f(x,y)的像素的总数 n k nk nk是图像 f ( x , y ) f(x,y) f(x,y)中灰度级为 k k k的像素的个数。
  得到直方图的相对频率后就可以按照需求对直方图进行拉伸和映射。
  为了方便讨论,以 r r r s s s分别表示归一化了的原图像灰度和经直方图均衡化后的灰度,因为归一化了,所以r和s的取值范围都在0到1之间。所谓的直方图均衡化,就是根据直方图对像素点的灰度值进行变换,属于点操作范围。换而言之,即:已知r,求相应的s。
  在[0:1]区间内任意一个r,经变换函数 T ( r ) T(r) T(r)都可以产生一个对应的s,且
s = T ( r ) (3) s=T(r)\tag{3} s=T(r)(3)
  式中, T ( r ) T(r) T(r)应当满足以下两个条件:
  (1)在 0 ≤ r ≤ 1 0\leq r \leq 1 0r1内, T ( r ) T(r) T(r)为单调递增函数;
  (1)在 0 ≤ r ≤ 1 0\leq r \leq 1 0r1内有 0 ≤ T ( r ) ≤ 1 0\leq T(r) \leq 1 0T(r)1

直方图均衡化的实际运算过程

  (1)首先遍历图像,统计出每个灰度值各有多少个像素。
  (2)统计每个灰度值的像素各占一整幅图的多少百分比。以及累计直方图。
  (3)将累计直方图百分比进行区间映射(也就是乘以像素级255)得到像素级的映射关系。
  (4)根据映射关系对像素进行重新分配。

MATLAB实现

clear;
clc;
close all;

a = imread('../img/pre_hist_rq.bmp');

imshow(a);
[row,col] = size(a);

hist_lut = zeros(1,256);
lut_add = zeros(1,256);

result = zeros(row,col);

%Statistical gray value   计算一副图像的灰度值分布数量
for i = 1:row
    for j = 1:col
        hist_lut(a(i,j)+1) = hist_lut(a(i,j)+1)+1;
    end
end

%Cumulative histogram     统计累计直方图
for i=1:256
    if(i==1)
        lut_add(i) = hist_lut(i);
    else 
        lut_add(i) = lut_add(i - 1) + hist_lut(i);
    end
end 

%累计直方图归一化与重映射
for i = 1:row
    for j=1:col
        result(i,j) = lut_add(a(i,j)+1) * 255/(row*col);
    end
end

matlab_hist_eq = uint8(floor(result));

hist_lut1 = zeros(1,256);
%图像灰度重映射
for i = 1:row
    for j = 1:col
        hist_lut1(matlab_hist_eq(i,j)+1) = hist_lut1(matlab_hist_eq(i,j)+1) + 1;
    end 
end

subplot(2,2,1);
imshow(uint8(a));

subplot(2,2,2);
bar(hist_lut);

subplot(2,2,3);
imshow(uint8(result));

subplot(2,2,4);
bar(hist_lut1);

FPGA实现

module hist_eq#(
	parameter 	ROW 	= 	1920 		,
	parameter 	COL 	= 	1080 
)(
	input 	wire  			clk  		,

	input 	wire  			pre_vs 		,
	input 	wire  			pre_de 		,
	input 	wire  	[7:0] 	pre_data	,

	output 	wire   			post_vs 	,
	output 	wire  			post_de 	,
	output 	wire  	[7:0] 	post_data 
);

reg  	[31:0] 	hist_lut 	[255:0] 	; 	 	//存放图像的灰度分布情况

integer 	i;

initial begin
	for(i=0;i<256;i++)begin
		hist_lut[i] = 32'd0;
	end
end

reg  			pre_vs_r 	;

wire  			vs_pose 	;
wire  			ext_vs_pose ;

reg  			rst_n 		;

reg  	[15:0] 	hcnt 		;
reg  	[15:0] 	vcnt 		;

reg  			inter_de 	;
reg  	[7:0] 	inter_cnt 	;

reg  			clc_de 		;
reg  	[7:0] 	clc_cnt 	;

wire  	[31:0] 	hist_sum 	;

always @(posedge clk)
	pre_vs_r 	<= 	pre_vs 	;

assign 	vs_pose 	<= 	~pre_vs_r && pre_vs ;

data_sync_ext   u1_data_sync_ext(
    .clka           (clk        ),
    .rst_n          (1'b1       ),
    .pulse_a        (vs_pose    ),
    .ext_pulse_a    (ext_vs_pose) 
);

/***********************行列计数*************************************/
always @(posedge clk)
    rst_n <=    ~ext_vs_pose ;

always @(posedge clk)
    if(rst_n == 0)
        hcnt    <=  0;
    else if(hcnt == COL - 1 && pre_de == 1'b1)
        hcnt    <=  0;
    else if(pre_de == 1'b1)
        hcnt    <=  hcnt+1;
    else 
        hcnt    <=  hcnt;

always @(posedge clk)
    if(rst_n == 0)
        vcnt    <=  0;
    else if(hcnt == COL - 1 && vcnt == ROW - 1 && pre_de == 1'b1)
        vcnt    <=  0;
    else if(hcnt == COL - 1)
        vcnt    <=  vcnt + 1;
    else 
        vcnt    <=  vcnt    ; 

//当数据到来时相应的灰度值数量+1  当数据结束时依次清除数据
always @(posedge clk)
	if(clc_de)
		hist_lut[clc_cnt] <= 32'd0;
	else if(pre_de==1)
		hist_lut[pre_data] <= hist_lut[pre_data] + 1;


//当一帧数据结束后,开始统计直方图使能
always @(posedge clk)begin
	if(!rst_n)begin
		inter_de 	<= 	0 ;
	end
	else if(inter_cnt == 8'd255)begin
		inter_de 	<= 	0;
	end
	else if(hcnt == COL -1 && vcnt == ROW - 1)begin
		inter_de 	<= 	1'b1;
	end
	else begin
		inter_de 	<= 	inter_de ;
	end
end

//依次统计直方图的灰度
always @(posedge clk)begin
	if(!rst_n)begin
		inter_cnt 	<= 0;
	end
	else if(inter_cnt == 8'd255)begin
		inter_cnt 	<= 0;
	end
	else if(inter_de == 1)begin
		inter_cnt 	<= inter_cnt + 1'b1;
	end
	else begin
		inter_cnt 	<= inter_cnt;
	end
end

//依次统计直方图灰度
always @(posedge clk)
	if(rst_n == 0)
		lut_add 	<= 	0;
	else if(inter_de)
		lut_add 	<= 	lut_add 	+ 	hist_lut[inter_cnt];
	else 
		lut_add 	<= 	'd0;

//ram写使能拉高
always @(posedge clk )
	if(rst_n == 0)
		wea 	<= 	1'b0;
	else 
		wea 	<= 	inter_de;

//ram写地址
always @(posedge clk)
	if(rst_n == 0)
		addra 	<= 'd0 ;
	else if(addra == 8'd255)
		addra 	<= 	'd0;
	else if(wea == 1'd1)
		addra 	<= 	addra + 1'b1;
	else 
		addra 	<= 	addra ;

//统计完成后清除灰度直方图数据
always @(posedge clk)
	if(rst_n == 0)
		clc_de 	<= 	1'b0;
	else 
		clc_de 	<= 	inter_de ;

always @(posedge clk)
	if(rst_n == 0)
		clc_cnt 	<= 0;
	else 
		clc_cnt 	<= 	inter_cnt ;

//存储灰度直方图的ram
hist_ram u1_hist_ram (
	.clka		(clk			),  	
	.wea		(wea			),   	
	.addra		(addra			), 	
	.dina		(lut_add		),  	
	.douta		(				), 	

	.clkb		(clk			),  	
	.web		(1'b0			),   	
	.addrb		(pre_data		), 	
	.dinb		(32'd0			),  	
	.doutb		(hist_sum		)  	
);

reg  [0:1]	pre_de_r 	;

reg  	[63:0] 	mult 	;

localparam 	[63:0]  	coe 	= 	(2**25)*255/(COL*ROW) ; //累计直方图归一化以及重映射需要乘以的数据   乘数是累计直方图的灰度值

always @(posedge clk)
	if(rst_n == 0)
		pre_de_r 	<= 	0;
	else 
		pre_de_r 	<= 	{pre_de_r[0],pre_de};

//图像的数据进行重映射
always @(posedge clk)
	if(rst_n == 0)
		mult 	<= 	0;
	else if(pre_de_r[0] == 1'b1)
		mult 	<= 	hist_sum * coe;
	else 
		mult 	<= 	'd0;

always @(posedge clk)
	if(rst_n == 0)
		post_de 	<= 	1'b0;
	else 
		post_de 	<= 	pre_de_r[1];


//图像数据的位截取
always @(posedge clk)
	if(rst_n == 0)
		post_data 	<= 	1'b0;
	else if(pre_de_r[1] == 1'b1)
		post_data 	<= 	mult[25+7:25];
	else 
		post_data 	<= 	'd0;

网站公告

今日签到

点亮在社区的每一天
去签到