简介
直方图均衡化是一种简单有效的图像增强技术,通过改变图像的直方图来改变图像中各像素的灰度,主要用于增强动态范围偏小的图像的对比度。当原始图像的灰度分布较为集中的时候,可能造成图像不够清晰,图像会过曝或者过暗。采用直方图均衡化,可以将原本较为集中的灰度分布变换成较为均匀的形式,增加了图像的灰度之间的动态范围,从而增加了图像的对比度。直方图均衡化的主要思想就是对图像灰度较为集中的地方进行拉伸展宽,对图像中像素较少的灰度值进行归并。
直方图均衡化理论基础
严格来说,图像的灰度直方图是一个一维的离散函数,可以写成:
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,...,L−1(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 0≤r≤1内, T ( r ) T(r) T(r)为单调递增函数;
(1)在 0 ≤ r ≤ 1 0\leq r \leq 1 0≤r≤1内有 0 ≤ T ( r ) ≤ 1 0\leq T(r) \leq 1 0≤T(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;