FPGA实现图像的直方图均衡化

  直方图均衡化又称为灰度均衡化,是指通过某种灰度映射使输入图像转换为在每一灰度级上都有近似相同的输出图像(即输出的直方图是均匀的)。在经过均衡化处理后的图像中,像素将占有尽可能多的灰度级并且分布均匀。因此,这样的图像将具有较高的对比度和较大的动态范围。直方图均衡可以很好地解决相机过曝光或曝光不足的问题。

一、MATLAB实现

%--------------------------------------------------------------------------
%                         直方图均衡化
%--------------------------------------------------------------------------
close all
clear all;
clc;

I = rgb2gray(imread('car.bmp'));
Ieq=histeq(I);

subplot(221),imshow(I);title('原图');
subplot(222),imhist(I);
subplot(223),imshow(Ieq);title('直方图均衡化');
subplot(224),imhist(Ieq);

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

  从结果可以看出:图片对比度显著提高,直方图变得更均匀。

二、FPGA实现

1、理论分析

  直方图均衡化的公式如下所示,H(i)为第 i 级灰度的像素个数,A0为图像的面积(即分辨率),Dmax为灰度最大值,即255。

2、实现步骤

  和直方图拉伸的情况一样,直方图均衡化也分为真均衡化和伪均衡化。本次设计采用伪均衡化,即采用前一帧的图像进行统计、累计和、归一化,当前帧做均衡化的数据输出。

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

  (1)前一帧:计算图像的直方图 H(i)

  (2)前一帧:计算直方图累计和

  (3)前一帧到当前帧的空隙:归一化,即

  (4)当前帧:均衡化后的数据输出

3、Verilog设计

  先说归一化,归一化就是 *255 / 分辨率,我此次的屏幕分辨率是 480x272,经过计算,归一化的参数为 255 / 480x272 = 2 ^(-9),即计算出累计和后,结果右移9位即可。

  书本《基于FPGA的数字图像处理原理及应用》中采用了 RAM 的方式来实现上述设计,该设计代码较复杂。OpenS Lee 前辈给出了一种新的实现方式:数组。使得整个设计变得异常的简洁,关键代码如下所示:

  1 //**************************************************************************
  2 // *** 名称 : Hist_equalization.v
  3 // *** 作者 : xianyu_FPGA
  4 // *** 博客 : https://www.cnblogs.com/xianyufpga/
  5 // *** 日期 : 2020年3月
  6 // *** 描述 : 直方图均衡化(伪),刚好右移9位即可
  7 //--------------------------------------------------------------------------
  8 //            Dmax        = 255                     255
  9 //            A0          = 480000(600x800)         130560(480x272)
 10 //            Dmax/A0     = 0.00053125              0.001953125 = 2^(-9)
 11 //            *2^20       = 557
 12 //**************************************************************************
 13 module Hist_equalization
 14 //========================< 端口 >==========================================
 15 (
 16 input   wire                clk                     ,
 17 input   wire                rst_n                   ,
 18 //---------------------------------------------------
 19 input   wire                Y_de                    ,
 20 input   wire                Y_hsync                 ,
 21 input   wire                Y_vsync                 ,
 22 input   wire    [ 7:0]      Y_data                  ,
 23 //---------------------------------------------------
 24 output  reg                 hist_de                 ,
 25 output  reg                 hist_hsync              ,
 26 output  reg                 hist_vsync              ,
 27 output  reg     [ 7:0]      hist_data                
 28 );
 29 //========================< 参数 >==========================================
 30 parameter IDLE              = 4'b0001               ; //空闲
 31 parameter STATISTICS        = 4'b0010               ; //统计
 32 parameter ACCUMULATION      = 4'b0100               ; //累计和
 33 parameter NORMALIZATION     = 4'b1000               ; //归一化
 34 //========================< 信号 >==========================================
 35 reg     [ 3:0]              state                   ;
 36 wire                        pos_vsync               ;
 37 wire                        neg_vsync               ;
 38 reg     [16:0]              statistics[0:255]       ; //480x272 = 17位
 39 reg     [16:0]              accumulation[0:255]     ;
 40 reg     [ 7:0]              normalization[0:255]    ;
 41 reg     [ 7:0]              i,j,k                   ;
 42 //==========================================================================
 43 //==    帧开始和结束标志
 44 //==========================================================================
 45 assign pos_vsync =  Y_vsync && ~hist_vsync;
 46 assign neg_vsync = ~Y_vsync &&  hist_vsync;
 47 //==========================================================================
 48 //==    前一帧:直方图统计、累计和、归一化
 49 //==========================================================================
 50 always @(posedge clk or negedge rst_n) begin
 51     if(!rst_n) begin
 52         i <= 0;
 53         j <= 0;
 54         k <= 0;
 55         state <= IDLE;
 56     end
 57     else begin
 58         case(state)
 59             IDLE:       begin
 60                             if(pos_vsync) begin
 61                                 i <= 0;
 62                                 j <= 0;
 63                                 k <= 0;
 64                                 state <= STATISTICS;
 65                             end
 66                             else begin
 67                                 i <= i + 1;
 68                                 statistics[i] <= 0;
 69                                 accumulation[i] <= 0;
 70                             end
 71                         end
 72             STATISTICS: begin
 73                             if(Y_de) begin
 74                                 statistics[Y_data] <= statistics[Y_data] + 1;
 75                             end
 76                             else if(neg_vsync)
 77                                 state <= ACCUMULATION;
 78                         end
 79             ACCUMULATION:begin
 80                             if(j == 0) begin 
 81                                 accumulation[j] <= statistics[j];
 82                                 j <= j + 1;
 83                             end
 84                             else if(j > 0 && j < 255) begin
 85                                 accumulation[j] <= statistics[j] + accumulation[j-1];
 86                                 j <= j + 1;
 87                             end
 88                             else if(j == 255) begin
 89                                 accumulation[j] <= statistics[j] + accumulation[j-1];
 90                                 state <= NORMALIZATION;
 91                             end
 92                         end
 93             NORMALIZATION:begin
 94                             if(k < 255) begin
 95                                 normalization[k] <= accumulation[k][16:9] + {7'b0,accumulation[k][8]};
 96                                 k <= k + 1;
 97                             end
 98                             else if(k == 255) begin
 99                                 normalization[k] <= accumulation[k][16:9] + {7'b0,accumulation[k][8]};
100                                 state <= IDLE;
101                             end
102                         end
103             default:;
104         endcase
105     end
106 end
107 //==========================================================================
108 //==    当前帧:输出直方图均衡后的数据
109 //==========================================================================
110 always @(posedge clk or negedge rst_n) begin
111     if(!rst_n) begin
112         hist_data <= 8'd0;
113     end
114     else if(Y_de) begin
115         hist_data <= normalization[Y_data];
116     end
117 end
118 //==========================================================================
119 //==    信号同步
120 //==========================================================================
121 always @(posedge clk) begin 
122     hist_de <= Y_de;
123     hist_hsync <= Y_hsync;
124     hist_vsync <= Y_vsync;
125 end
126 
127    
128    
129 endmodule

三、上板验证

  失败......因为数组消耗资源过大,我的小梅哥AC620支持不了,不过理论上应该没问题,原作者 OpenS Lee 在 Xilinx 板卡中上板成功,感兴趣的朋友可以试试。

参考资料:

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

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

原文地址:https://www.cnblogs.com/xianyufpga/p/12531577.html