FPGA实现图像的直方图拉伸

  在视频处理中,为了能够实时调节图像的对比度,通常需要对直方图进行拉伸处理。直方图拉伸是指将图像灰度直方图较窄的灰度级区间向两端拉伸,增强整幅图像像素的灰度级对比度,达到增强图像的效果。

  常用的直方图拉伸方法有线性拉伸、3段式分段线性拉伸和非线性拉伸等。这里我们介绍FPGA中常见的线性拉伸。

  线性拉伸也即灰度拉伸,属于线性点运算的一种。它扩展图像的直方图,使其充满整个灰度级范围内。设f(x,y)为输入图像,它的最小灰度级A和最大灰度级B的定义如下:

  将A和B分别映射到0和255,则最终得到输出图像g(x,y)为

 

一、MATLAB实现

%--------------------------------------------------------------------------
%--                     灰度直方图拉伸
%--------------------------------------------------------------------------
close all
clear all;
clc;
 
I = rgb2gray(imread('car.bmp')); %读图转灰度
 
Imin = min(min(I));
Imax = max(max(I));

[M,N] = size(I);
C =255/(Imax-Imin);
Inew = zeros(size(I));
for i=1:M
   for j=1:N
       if(I(i,j)==Imin) 
           Inew(i,j) = 0;
       elseif(I(i,j)==Imax) 
           Inew(i,j) = 255;
       else
           Inew(i,j)=(C.*(I(i,j)-Imin));
       end
   end
end

Inew = uint8(Inew);

subplot(221),imshow(I);   title('灰度图');
subplot(223),imshow(Inew);title('灰度拉伸图');
subplot(222),imhist(I);   title('灰度直方图统计');
subplot(224),imhist(Inew);title('灰度拉伸后的直方图统计');

  点击运行,得到如下结果:

  由此可以看出:线性拉伸达到了类似直方图均衡化的功能,将比较集中的直方图拉伸到整个灰度分布区域。

 

二、FPGA实现

1、理论分析

  拉伸处理的公式如下,其中 A 为最大灰度级,B为最小灰度级,f(x,y)为原图像,g(x,y)为拉伸后的图像。

  FPGA实现灰度图像的拉伸可分为真拉伸和伪拉伸,真拉伸是指公式所有值都是当前帧,处理后的结果也是当前帧。这需要对图像进行一帧的缓存,在实际应用中,处理帧缓存是费时费力费资源的一-件事情,在许多情况下,图像的变换比较慢,在这种情况下的一-个近似是当建立当前帧的直方图统计结果时使用从前一帧得到的映射。结果是直方图均衡化可能不是非常准确,但是消耗的资源和处理的延时都有显著地减少。我们称之为伪拉伸,其实就是在前一帧计算出公式所有值,用来对本帧图像进行拉伸处理,这样设计难度大大减小。

2、实现步骤

  为得到A、B的值需要进行统计工作,这至少要等到前一帧图像“流过”之后才能完成。此限制决定了我们难以在同一帧既统计又输出最终拉伸结果。必须对前期的统计结果进行缓存。这点是毋庸置疑的。在下一次统计前需要将缓存结果清零。我们可以按一下步骤来实现:

  (1)前一帧:统计一帧图像的最大值和最小值,即A和B

  (2)前一帧到当前帧的空隙:保存前一帧的最大值和最小值。

  (3)当前帧:进行直方图拉伸公式的计算:

    ①计算 255 * (f(x,y) - A 和 B - A。

    ②计算①中,二者的商

    ③计算大括号,对f(x,y)进行判断,输出最终直方图拉伸的结果。

3、Verilog设计

//==========================================================================
//==    计算一帧图像的最大值最小值
//==========================================================================
//vsync下降沿
//---------------------------------------------------
assign neg_Y_vsync = Y_vsync_r[0] && ~Y_vsync;

//实时计算大小
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        max <= 8'd0;
        min <= 8'd255;
    end
    else if(Y_vsync && Y_de) begin     //像素有效时
        max <= (max > Y_data) ? max : Y_data;
        min <= (min < Y_data) ? min : Y_data;
    end
    else if(neg_Y_vsync) begin         //一帧图像结束时
        max <= 8'd0;
        min <= 8'd255;
    end
end

//保存上一帧最大值、最小值
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        Y_max <= 'd0;
        Y_min <= 'd0;
    end
    else if(neg_Y_vsync) begin         //一帧图像结束时
        Y_max <= max;
        Y_min <= min;
    end
end
//==========================================================================
//==    直方图拉伸,耗费3clk
//==========================================================================
//分子和分母,1clk
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        mole <= 'd0;
        deno <= 'd0;
    end
    else begin
        mole <= (Y_data - Y_min) * 255;
        deno <=  Y_max  - Y_min;
    end
end

//g(x,y),1clk
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        quot <= 'd0;
    end
    else begin
        quot <= mole / deno;
    end
end

//获得拉伸结果,1clk
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        hist_data <= 8'd0;
    end
    else if(Y_data < Y_min) begin
        hist_data <= 8'd0;
    end
    else if(Y_data > Y_max) begin
        hist_data <= 8'd255;
    end
    else begin
        hist_data <= quot[7:0];
    end
end
//==========================================================================
//==    信号同步
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        Y_de_r    <= 3'b0;
        Y_hsync_r <= 3'b0;
        Y_vsync_r <= 3'b0;
    end
    else begin  
        Y_de_r    <= {Y_de_r[1:0],    Y_de};
        Y_hsync_r <= {Y_hsync_r[1:0], Y_hsync};
        Y_vsync_r <= {Y_vsync_r[1:0], Y_vsync};
    end
end

assign hist_de    = Y_de_r[2];
assign hist_hsync = Y_hsync_r[2];
assign hist_vsync = Y_vsync_r[2];

 

三、上板验证

  原图:

  直方图拉伸处理后:

  拉伸后,对比度明显增强,实验成功。

 

后记

  同样的,也可以用彩色图像的RGB三通道来这样处理,最后再合并,也能起到不错的效果。

  此外本博客设计的直方图拉伸是有缺陷的,当选取的图片有白色噪声时,直方图拉伸会失败。不过面对噪声我们可以在直方图拉伸前用中值滤波先处理一下,后续的博客会整理中值滤波的实现方法。此外,本博客的实现方法基于书本《基于FPGA的数字图像处理原理及应用》,该书提供了一种改良的方法,可以应对白色噪声,但是设计难度增加很多,感兴趣的朋友可以翻阅该书学学看。

 

参考资料:

[1] OpenS Lee:FPGA开源工作室(公众号)

[2] 牟新刚、周晓、郑晓亮.基于FPGA的数字图像处理原理及应用[M]. 电子工业出版社,2017.

 

posted @ 2020-03-16 20:14  咸鱼IC  阅读(3363)  评论(0编辑  收藏  举报