啥是直方图均衡化

  直方图均衡化,通过一种灰度映射使输入图像经过转换后,在每一灰度级上都有相近似的输出图像,输出的灰度值是均匀的。经过这样处理的图像具有较高的对比对和较大的动态范围。
  对于离散的灰度级,直方图均衡化有如下关系:

  其中Db是转换过后的像素的灰度值,Dmax是输入图像经过直方图统计后得到的最大的灰度值,A0是一幅图像面积,也就是所有的像素值H(i)是灰度级的各级的统计结果。Da是当前像素的灰度级。
  直方图均衡化就的作用就是,对于当前的输入像素,需要首先求出小于当前像素灰度值的所有像素统计值的和,然后与该图像中的最大灰度值进行相乘,最后除以图像的面积。

基于FPGA的直方图均衡化

  基于FPGA也是能够完成图像的直方图的处理的。具体的计算步骤如下:

  1. 首先需要统计出该图像的直方图分布H(i)
  2. 其次需要计算出各个灰度级下的直方图累加和也即
  3. 找到图像的灰度最大值Dmax,然后将Dmax与对应灰度级的累加和相乘
  4. 除以总的像素面积,完成图像直方图的转换

  在本人进行算法验证的时候,并没有对输入图像进行帧缓存,正常的操作应该是,将本帧的直方图统计完成后,在将缓存好的图像从内存中取出,并进行直方图均衡化。在进行算法验证时,未进行帧缓存,相当于将上一帧图像数据的统计结果作为下一帧图像的变换因子。这在变化速率不太快的情况下是可以采取的一种方式。

具体实现过程

1. 求累加和

  在上一篇博客中介绍了图像直方图的统计方法,并且最终得到的输出的直方图统计值和有效信号。因此上游模块输出直方图统计信息时,可以进行计算直方图对应灰度级下的累加和,并且确定灰度的最大值。
  采用一个BRAM来存储一幅图像的灰度累加和。于此同时确定当前图形中的灰度最大值。

2. 进行乘法计算

  在上一步求得灰度累加和之后,当前帧图像像素已经来临,因此可以将该像素对应的灰度级和最大灰度值相乘。由于FPGA并不擅长乘法运算,尤其不擅长除法运算。因此在进行乘除法时,会调用内部的专用的DSP资源,因此可以调用Xilinx的乘法器或者除法器IP来进行乘除法的运算。
在进行乘法运算时,需要根据仿真的结果来确定乘法的Latency。

3. 进行除法计算

  在上一步的乘法计算完成后,可以进行除法的运算,在除法IP的配置时,可以设置除法的运算的潜伏期。本人所设置的Latency为7。

代码设计

/*============================================
#
# Author: Wcc - 1530604142@qq.com
#
# QQ : 1530604142
#
# Last modified: 2020-07-08 20:02
#
# Filename: histogram_equalization.v
#
# Description:
#
============================================*/
`timescale 1ns / 1ps
module histogram_equalization(input wire            clk             ,input  wire            rst             ,input  wire            pi_hsync        ,//输入的视频流信号input    wire            pi_vsync        ,input  wire            pi_de           ,input  wire            pi_data_vld     ,input  wire    [7:0]   pi_data         ,input  wire            pi_histo_vld    ,//输入的直方图统计信息input  wire    [31:0]  pi_histo_data   ,output     wire            po_hsync        ,//输出的视频流信号output   wire            po_vsync        ,output wire            po_de           ,output wire            po_data_vld     ,output     wire    [7:0]   po_data             );//==========================================
//parameter define
//==========================================
parameter IMG_WIDTH     =  128     ;
parameter IMG_HEIGHT    =  128     ;
localparam TOTAL_PIXEL  = IMG_WIDTH * IMG_HEIGHT;//==========================================
//internal signal
//==========================================
reg     [1:0]       vsync_dd    ;//场同步信号延时
reg     [7:0]       pi_data_dd  ;//输入数据延时
reg                 data_vld_dd ;//输入数据有效延时
reg     [2:0]       stream_vld_dd   ;//==========================================
//求累加和与找寻灰度最大最小值
//==========================================
reg     [31:0]      gray_sum    ;//灰度累加和
reg     [7:0]       gray_max    ;//灰度值最大值
reg     [7:0]       gray_min    ;//灰度值最小值
reg     [7:0]       gray_idx    ;//灰度值索引//==========================================
//确定当前像素在图像中的位置
//==========================================
reg     [12:0]      cnt_col     ;
wire                add_cnt_col ;
wire                end_cnt_col ;reg    [12:0]      cnt_row     ;
wire                end_cnt_row ;
wire                add_cnt_row ;//==========================================
//ram 相关
//==========================================
reg                 wr_ram_en   ;//存储灰度累加和的RAM
wire    [31:0]      wr_ram_data ;//写入RAM的数据
reg     [7:0]       wr_ram_addr ;//写RAM时的地址
wire    [7:0]       rd_ram_addr ;
wire    [31:0]      rd_ram_data ;//==========================================
//multiplier
//==========================================
wire    [39:0]      mult_value  ;//乘积
wire                mult_vld    ;//乘积有效信号//==========================================
//divider
//==========================================
wire            div_tvalid  ;
wire [63 : 0]   div_tdata   ;//----------------vsync_dd------------------
always @(posedge clk) beginif (rst==1'b1) beginvsync_dd <= 'd0;endelse beginvsync_dd <= {vsync_dd[0], pi_vsync};end
end//==========================================
//将累加和写入到RAM中
//==========================================//----------------gray_sum------------------
always @(posedge clk) beginif (rst==1'b1) begingray_sum <= 'd0;endelse if (pi_histo_vld == 1'b1) begingray_sum <= gray_sum + pi_histo_data;    endelse begingray_sum <= 'd0;end
end
//----------------wr_ram_data------------------
assign wr_ram_data = gray_sum;//----------------wr_ram_en------------------
always @(posedge clk) beginif (rst==1'b1) beginwr_ram_en <= 1'b0;endelse beginwr_ram_en <= pi_histo_vld;end
end//----------------wr_ram_addr------------------
always @(posedge clk) beginif (rst==1'b1) beginwr_ram_addr <= 'd0;endelse if (wr_ram_en == 1'b1) beginwr_ram_addr <= wr_ram_addr + 1'b1;endelse beginwr_ram_addr <= 'd0;end
end//==========================================
//找寻最大最小值
//==========================================
//----------------gray_indx------------------
always @(posedge clk) beginif (rst==1'b1) begingray_idx <= 'd0;endelse if (pi_histo_vld == 1'b1) begingray_idx <= gray_idx + 1'b1;endelse begingray_idx <= 'd0;end
end//----------------gray_max------------------
always @(posedge clk) beginif (rst==1'b1) begingray_max <= 'd0;gray_min <= 'd255;end//检测到一帧图像结束else if (end_cnt_row == 1'b1) begingray_max <= 'd0;gray_min <= 'd255; endelse if (pi_histo_data != 0 && pi_histo_vld == 1'b1 ) beginif (gray_max <= gray_idx) begingray_max <= gray_idx;endif (gray_min >= gray_idx) begingray_min <= gray_idx;endend
end//----------------pi_data_dd, data_vld_dd------------------
always @(posedge clk) beginif (rst==1'b1) beginpi_data_dd <= 'd0;data_vld_dd <= 'd0;endelse beginpi_data_dd <= pi_data;data_vld_dd <= pi_data_vld;    end
end
//----------------stream_vld_dd------------------
always @(posedge clk) beginif (rst==1'b1) beginstream_vld_dd <= 'd0;endelse beginstream_vld_dd <= {stream_vld_dd[1:0], data_vld_dd};end
endassign mult_vld = stream_vld_dd[2];//----------------rd_ram_addr------------------
assign rd_ram_addr = (pi_data_vld) ? pi_data : 'd0;sum_ram inst_sum_ram (.clka(clk),              // input wire clka.wea(wr_ram_en),          // input wire [0 : 0] wea.addra(wr_ram_addr),   // input wire [7 : 0] addra.dina(wr_ram_data),      // input wire [31 : 0] dina.clkb(clk),              // input wire clkb.addrb(rd_ram_addr),      // input wire [7 : 0] addrb.doutb(rd_ram_data)      // output wire [31 : 0] doutb
);
//==========================================
//乘法器 3个时钟周期的潜伏期
//==========================================
mul_graylevel int_multiplier (.CLK(clk),        // input wire CLK.A(gray_max),     // input wire [7 : 0] A.B(rd_ram_data),  // input wire [31 : 0] B.P(mult_value)    // output wire [39 : 0] P
);//==========================================
//除法器 7个时钟周期的Latency
//==========================================
// m_axis_dout_tdata[63 : 0 ]
//[63:24] 商
//[21:0] 余数
div_gray inst_divider (.aclk(clk),                                          // input wire aclk.s_axis_divisor_tvalid(1'b1),                        // input wire s_axis_divisor_tvalid.s_axis_divisor_tdata(TOTAL_PIXEL[23:0]),        // input wire [23 : 0] s_axis_divisor_tdata.s_axis_dividend_tvalid(mult_vld),               // input wire s_axis_dividend_tvalid.s_axis_dividend_tdata(mult_value),             // input wire [39 : 0] s_axis_dividend_tdata.m_axis_dout_tvalid(div_tvalid),                // output wire m_axis_dout_tvalid.m_axis_dout_tdata(div_tdata)                      // output wire [63 : 0] m_axis_dout_tdata
);
assign po_data_vld = div_tvalid;   //==========================================
//确定当前像素在图像中的位置
//==========================================//----------------cnt_col------------------
always @(posedge clk) beginif (rst == 1'b1) begincnt_col <= 'd0;endelse if (add_cnt_col) beginif(end_cnt_col)cnt_col <= 'd0;elsecnt_col <= cnt_col + 1'b1;endelse begincnt_col <= 'd0;end
endassign add_cnt_col = div_tvalid == 1'b1;
assign end_cnt_col = add_cnt_col &&    cnt_col == IMG_WIDTH - 1;//----------------cnt_row------------------
always @(posedge clk) beginif (rst == 1'b1) begincnt_row <= 'd0;endelse if (add_cnt_row) beginif(end_cnt_row)cnt_row <= 'd0;elsecnt_row <= cnt_row + 1'b1;end
endassign add_cnt_row = end_cnt_col;
assign end_cnt_row = add_cnt_row &&    cnt_row == IMG_HEIGHT - 1;//----------------pi_hsync_dd/pi_vsync_dd,pi_de_dd------------------
reg     [10:0]  pi_hsync_dd;
reg     [10:0]  pi_vsync_dd;
reg     [10:0]  pi_de_dd;
reg     [87:0]  data_dd ;
//从数据输出到输出共有11个时钟周期的latency
always @(posedge clk) beginif (rst==1'b1) beginpi_hsync_dd <= 'd0;pi_vsync_dd <= 'd0;pi_de_dd     <= 'd0;    data_dd     <= 'd0;endelse beginpi_hsync_dd <= {pi_hsync_dd[9:0], pi_hsync};pi_vsync_dd <= {pi_vsync_dd[9:0], pi_vsync};pi_de_dd   <= {pi_de_dd[9:0], pi_de};  data_dd     <= {data_dd[79:0], pi_data};end
endassign po_hsync = pi_hsync_dd[10];
assign po_vsync = pi_vsync_dd[10];
assign po_de = pi_de_dd[10];
assign po_data = (po_data_vld) ? div_tdata[31:24] : data_dd[87:80];endmodule

测试结果

  在片内存储中,存储有一幅灰度图像的信息,下面的两幅图像中,第一幅图像是经过直方图均衡化过后的结果,第一幅图像的对比度相较于第二幅要高。


参考:《基于FPGA的数字图像处理》牟新刚

FPGA图像处理基础----直方图均衡化相关推荐

  1. OpenCV-数字图像处理之直方图均衡化

    OpenCV-数字图像处理之直方图均衡化 从这篇博文开始,小生正式从一个毫不相干专业转投数字图像处理.废话不多说了,talk is cheap. show me the code. 直方图均衡化目的 ...

  2. 【图像处理】直方图均衡化(附带Matlab及OpenCV3自编程实现代码)

    [fishing-pan:https://blog.csdn.net/u013921430转载请注明出处] 前言 直方图均衡化是最基础的图像处理方法之一,也是本人接触图像处理时最先接触到的算法.算法很 ...

  3. 【图像处理】直方图均衡化

    直方图均衡化是图像处理领域中利用图像直方图增强图像对比度的一种方法. 如果一副图像的像素占有很多的灰度级而且分布均匀,那么这样的图像往往有高对比度和多变的灰度色调."直方图均衡化" ...

  4. 【数字图像处理】直方图均衡化详解及编程实现

    直方图均衡化的英文名称是Histogram Equalization. 图像对比度增强的方法可以分成两类:一类是直接对比度增强方法;另一类是间接对比度增强方法.直方图拉伸和直方图均衡化是两种最常见的间 ...

  5. 【matlab图像处理】直方图均衡化操作

    中国史之[百家争鸣]: 春秋战国时期,知识分子中不同学派及各家族流派之间争芳斗艳的学术局面.这是中国思想.学术发展的黄金时期,史称"百家争鸣". --来源:全历史APP [路漫漫其 ...

  6. python图像处理:直方图均衡化

    理论参考链接:[图像处理算法]直方图均衡化 算法实现 # 对单通道的处理函数 def channel_equalize(img):od = img.reshape(-1)# 对像素计数tmp = {} ...

  7. 【数字图像处理】直方图均衡化

    全局直方图均衡化 直方图均衡化通过调整图像的直方图来增强图像的对比度,经常使用在医学图像分析中. 例如一幅8*8图像像素值如下: 对各个像素值进行计数: 得到累计概率分布: 其中均衡化后的像素值计算公 ...

  8. opencv基础---直方图均衡化(原理equalizeHist)

    直方图均衡化的作用是图像增强. 有两个问题比较难懂,一是为什么要选用累积分布函数,二是为什么使用累积分布函数处理后像素值会均匀分布. 第一个问题.均衡化过程中,必须要保证两个条件:①像素无论怎么映射, ...

  9. 数组图像处理:直方图均衡化

    证明: 设r是灰度级,s是均衡化后的灰度级,范围是0-L(L一般是255), 对图像进行统计,设均衡化前的灰度级的随机变量为X,概率密度函数为,均衡化后的随机变量为Y,概率密度函数为.我们通过统计已知 ...

  10. 【数字图像处理】直方图均衡化与规定化

    目录 直方图处理技术概述 直方图均衡化 公式推导 Matlab代码实现 图像的规定化 数学推导 单映射 Matlab代码 效果展示​编辑 组映射 Matlab代码 直方图处理技术概述 灰度级范围[0, ...

最新文章

  1. 基建狂魔:硬核技术之隔绝厌氧菌的涂料
  2. 测试hadoop安装是否成功
  3. 32位python-64位Python调用32位DLL方法(一)
  4. cli2弃用了吗 vue_vue-cli 3 和 vue-cli 2的区别
  5. node中的Stream-Readable和Writeable解读
  6. java 反射和泛型-反射来获取泛型信息
  7. 【codevs1378】选课——树形动规
  8. 前后端接口文档规范模板
  9. 微软王码五笔86版 for winnt/2000/xp v7.3 免费
  10. 饿了么高级设计师:界面视觉设计 5 要素
  11. 2017年信息学奥赛NOIP普及组试题
  12. 微信小程序自定义组件中对properties的修改
  13. matlab如何z变换,MATLAB第3章Z变换详解.ppt
  14. oracle18c静默安装教程,CentOS7无图形化界面静默安装oracle18c
  15. 第七届泰迪杯挑战赛C题
  16. Wrapper中ge,gt,lt,le的含义
  17. [附源码]Python计算机毕业设计SSM基于的影评系统(程序+LW)
  18. # Java 并发编程的艺术(二)
  19. unity万能的提示窗口
  20. linux教程 课件,《Linux实用教程》PPT课件

热门文章

  1. TBSchedule源码学习笔记-启动过程
  2. java saxreader 生成xml_SAXReader解析xml文件demo
  3. 游程编码详解(C语言)
  4. java打印某年日历_java打印指定年月份的日历
  5. kotlin教程(一)
  6. Spring学习04:事务控制(TransactionManager)
  7. SQL server 数据库 向表中添加字段
  8. 苹果笔记本装win7_小白“复活记”,十多年前的MacBook,曲折安装Win7
  9. 语音识别开源项目汇总
  10. 通讯接口应用笔记1:RS485通讯上下拉电阻的选择