欢迎!请选择语言 / Please pick a language:
CLAHE(Contrast Limited Adaptive Histogram Equalization)是一种经典的图像增强算法,通过将图像分割成多个子区域(tiles),对每个子区域独立进行直方图均衡化处理,并使用插值技术消除边界块效应,从而实现局部对比度增强。本项目实现了该算法的 FPGA 硬件加速版本,支持实时视频处理。
Important
🚀 目前优化方案实现 720P @ 176.4MHz 时钟频率
- ✅ 实时视频处理:支持 1280×720@60fps 实时处理
- ✅ 双 tile 规模:提供 16 tile(4×4)和 64 tile(8×8)两个版本
- ✅ 流水线架构:5 级流水线设计,实现 1 像素/周期的处理吞吐率
- ✅ 双线性插值:消除 tile 边界块效应,提供平滑的图像增强效果
- ✅ Ping-Pong 架构:帧间乒乓操作,实现统计与映射并行处理
- ✅ YUV 格式支持:支持 YUV 三路数据输入输出,确保数据同步
- ✅ 完整仿真环境:支持 ModelSim/Questa、VCS、Icarus Verilog 等多种仿真器
已实现 8x8 (64 tiles) 的深度优化版本。 核心方案: 采用 VLSI DSP 硬件折叠与存储器交织 (Hardware Folding & Memory Interleaving) 技术,将 64 个逻辑 Tile 映射到 4 个物理 RAM Bank 中。
- 技术亮点:
- 4-Bank Interleaved: 通过映射策略,实现任意 2x2 邻域的无冲突并行读取。
- Port Folding: 利用分时复用技术,将逻辑上的 64 个读写端口折叠到物理上的 4 个 RAM (各 2 端口) 上。
- 资源骤降: 相比原始 64-tile 版本,RAM 实例数从 64 个减少到 4 个,BRAM 资源节省 93.75%。
- 时序收敛: 保持了 1 像素/周期 的全流水吞吐率,逻辑层级更精简,更易于综合。
Note
仓库已提供8×8和4×4两个版本参考 🔒 资源时序优化版本及其他参数配置,如需获取,请联系作者:📧 [email protected],欢迎讨论交流!
虽然当前设计满足 720p@60fps (74.25MHz) 甚至 1080p@60fps (148.5MHz) 的需求,但对于更高性能场景(如 4K 处理),仍有优化空间。
- Retiming / Register Balancing: 在 Crossbar 的 Mux 树中插入更细粒度的流水线寄存器,打破长组合逻辑链。
- Critical Path Analysis: 针对综合报告中的关键路径(通常在 Bank 选择逻辑)进行逻辑重组。
本仓库按功能与用途分为若干目录:源码(RTL)、测试平台、仿真流程、文档与综合/实现产物等,便于学习、仿真与上板验证。
CLAHE/
├── fpga报告.md # 项目报告(中文,可选)
├── LICENSE # 许可证
├── README*.md # 多语言入口文档(中文/英文)
├── artifacts/ # 综合/实现与工具产生的输出文件(例如 QDB、QPG、实现报告)
├── work/ # 仿真器(ModelSim/Questa)编译生成的工作目录(可忽略/清理)
├── vivado_project/ # Xilinx Vivado 工程文件(用于综合、仿真参考)
├── docs/ # 项目设计与 RTL 模块的详细文档与流程说明
├── pictures/ # 文档和 README 中使用的图片资源
├── projects/ # 各版本实现目录(16tile / 64tile)
│ ├── 16tile/ # 16 tile(4×4)参考实现(教学/轻量)
│ │ ├── rtl/ # RTL 源代码与子模块
│ │ ├── tb/ # Testbench(模块/顶层仿真)
│ │ ├── sim/ # 仿真脚本(ModelSim/VCS/Icarus 等)
│ │ ├── scripts/ # 辅助脚本(图片处理、仿真驱动等)
│ │ └── assets/ # 仿真/测试资源(图片、参考数据)
│ └── 64tile/ # 64 tile(8×8)主线实现(更高质量的局部增强)
│ ├── rtl/
│ ├── tb/
│ ├── sim/
│ ├── scripts/
│ └── assets/
├── optimized/ # 🔒 优化版本 (submodule,联系作者获取权限)
├── flows/ # 一些跨工具/跨版本的仿真与处理流程
│ └── full_sim/ # 整帧 BMP 端到端仿真流程(包含输入/输出文件与脚本)
└── projects/*/README/ # 各版本的快速说明(参见各子目录)
CLAHE 算法的处理流程包括以下几个步骤:
- 图像分块:将输入图像分割成多个不重叠的子区域(tiles)
- 直方图统计:对每个 tile 内的像素进行直方图统计
- 对比度裁剪:对直方图进行裁剪,限制对比度增强的幅度
- CDF 计算:计算裁剪后直方图的累积分布函数(CDF)
- 像素映射:使用 CDF 将像素值映射到新的亮度值
- 双线性插值:对相邻 tiles 的映射结果进行插值,消除边界效应
源代码:projects/16tile/rtl/clahe_top.v | projects/64tile/rtl/clahe_top.v
顶层模块负责整个CLAHE系统的集成和协调,管理各子模块间的数据流和控制流,实现乒乓控制逻辑。 在CDF计算完成时切换ping_pong_flag,充分利用帧间隙时间,确保下一帧vsync上升沿来临前,乒乓切换已完成。
// 优化的乒乓切换逻辑 (在CDF完成时切换)
always @(posedge pclk or negedge rst_n) begin
if (!rst_n)
ping_pong_flag <= 1'b0;
else if (cdf_done_posedge)
ping_pong_flag <= !ping_pong_flag;
end源代码:projects/16tile/rtl/clahe_coord_counter.v | projects/64tile/rtl/clahe_coord_counter.v
实时计算输入像素的全局坐标、所属tile索引和tile内相对坐标,为直方图统计和像素映射提供位置信息。
在href有效期间递增横向坐标x_cnt,行结束时递增纵向坐标y_cnt,帧无效期间复位所有计数器。对于块tile的索引计算,使用比较器链代替除法器(节省资源)。
横向索引:tile_x = f(x_cnt / 320);纵向索引:tile_y = f(y_cnt / 180);
总索引:tile_idx = {tile_y, tile_x}(4bit)。
其中块内坐标的计算,使用移位加法计算,减少资源使用。
local_x = x_cnt - tile_x * 320,local_y = y_cnt - tile_y * 180,
乘法用移位替代:
tile_x * 320 = (tile_x << 8) + (tile_x << 6),
tile_y * 180 = (tile_y << 7) + (tile_y << 5) + (tile_y << 4) + (tile_y << 2)。
// 横向tile索引计算 (避免除法器)
always @(*) begin
if (x_cnt < 320)
tile_x = 2'd0;
else if (x_cnt < 640)
tile_x = 2'd1;
else if (x_cnt < 960)
tile_x = 2'd2;
else
tile_x = 2'd3;
end源代码:projects/16tile/rtl/clahe_histogram_stat.v | projects/16tile/rtl/clahe_histogram_stat_v2.v | projects/64tile/rtl/clahe_histogram_stat.v
对每个tile的256个灰度级进行实时统计,使用3级流水线实现读-增-写操作。
对于这种流水读写ram情况,极易出现读写冲突情况(读写同一地址),经分析存在以下冲突情况:
1:连续相同像素值
例如像素序列:100, 100, 50,对于第二个100像素,读取统计旧值时,第一个100的累加值暂未写入,导致第二个像素累加值会发生错误。
2:间隔相同像素值(流水线深度冲突)
例如像素序列:100, 50, 100...(间隔2周期,< 流水线深度3)
第二个100像素值读入所在周期,其读地址产生,同时第一个100在该周期写入累加值,故发生读写冲突。
对于问题1,检测连续输入的相同像素值,合并为一次RAM写入,确保统计值正确的同时大幅减少RAM访问次数。
// 比较当前输入与上一周期输入
if ((in_href && in_vsync && clear_done) && valid_s1 &&
(in_y == pixel_s1) &&
(tile_idx == tile_s1)) begin
same_as_prev <= 1'b1; // 检测到相邻相同
end
else begin
same_as_prev <= 1'b0;
end如果出现相邻相等的情况,那么此时统计值自增二,来补偿读到的旧值带来的误差
// 相邻相同时增量为2,否则为1
if (same_as_prev) begin
increment_s2 <= 2'd2;
end
else begin
increment_s2 <= 2'd1;
end对于问题2,其实可以使用真双端口RAM并配置为写优先模式来解决读写冲突问题,但是为了减少资源占用,部署更多算法,这里仍采用伪双端口ram,通过旁路逻辑解决读写冲突。若当前周期发生写地址与读地址相同,那么寄存当前写数据,作为本周期正在读的像素的读取值,不再使用从ram读取到的未更新的旧数据。下附关键代码,具体流程可参考流程图。
// 1. 冲突检测: Stage1读地址 == Stage3写地址
wire conflict = (pixel_s1 == pixel_s3) &&
(tile_s1 == tile_s3) &&
valid_s3;
// 2. 旁路数据保存 (在冲突时锁存Stage3的写数据)
always @(posedge pclk or negedge rst_n) begin
if (!rst_n) begin
bypass_valid <= 1'b0;
bypass_data <= 16'd0;
end
else begin
if (conflict) begin
bypass_valid <= 1'b1; // 有冲突: 置1
bypass_data <= ram_wr_data_s3; // // 保存Stage3写入值
end
else begin
bypass_valid <= 1'b0; // 无冲突: 清零(不过写是不写入才对)
end
end
end
// 3. 数据选择: 旁路优先 (Stage 2使用)
wire [15:0] selected_data = bypass_valid ? bypass_data : ram_rd_data_b;
// 旁路有效? 使用旁路值 使用RAM读值
// 4. 如果 bypass_valid = 1, 使用保存的写入数据自增;
always @(posedge pclk or negedge rst_n) begin
// ... 复位逻辑 ...
// ...
else begin
// ...
// 关键计算: 写入值 = 选择的数据 + 增量
ram_wr_data_s3 <= selected_data + increment_s2;
end
end源代码:projects/16tile/rtl/clahe_clipper_cdf.v | projects/64tile/rtl/clahe_clipper_cdf.v
该模块在histogram结束后,对每帧图像16个tile的直方图数据进行Clip阈值限制裁剪和CDF计算,最后归一化生成像素映射查找表。使用的有限状态机流程如下。
表1 clahe_clipper_cdf模块不同状态消耗周期统计表
| 状态 | 周期数 | 说明 |
| READ_HIST_CLIP | 257 | 读取+裁剪 |
| CLIP_REDIST | 257 | 仅在有溢出时执行 |
| CALC_CDF | 257 | 累积分布函数计算 |
| WRITE_LUT | 259 | 3级流水线归一化写入 |
| NEXT_TILE | 1 | tile切换 |
| DONE | 1 | 产生cdf_done脉冲 |
分析该流程在每帧中的耗时,由上表可知,每块tile总周期数约257+257+257+259+1+1=1032周期,16块耗时约16*1032=16512周期,在96MHz时钟频率下耗时约172μs, 1280×720@30fps帧间隙约33ms,所以cdf模块的处理时间充足。同时,cdf模块在histogram统计结束后立即开始,充分利用帧间隙时间,提升性能。
源代码:projects/16tile/rtl/clahe_ram_16tiles_parallel.v | projects/64tile/rtl/clahe_ram_64tiles_parallel.v
clahe_ram_16tiles_parallel模块负责管理32块伪双端口RAM,实现乒乓操作、四块并行读取和多端口仲裁。上图仅展示A组16块的主要架构,B组架构相同,通过乒乓操作进行切换。
cdf计算模块和histogram模块同一时刻只需一组读写端口,所以为了尽可能减少资源利用,使用伪双端口ram。同时由于mapping模块进行双线性插值时需要并行读取四个块(tile)的计算结果,所以每一分块都需要分配一块ram,共需16块ram。为了避免统计写入和映射模块读出冲突,我们设计使用乒乓双组RAM架构,架构如下:
帧N(ping_pong_flag=0):
-
RAM_A组:用于统计(Port A写,Port B读)
-
RAM_B组:用于映射(Port B四块并行只读)
帧N+1(ping_pong_flag=1):
-
RAM_B组:用于统计(Port A写,Port B读)
-
RAM_A组:用于映射(Port B四块并行只读)
模块接口如下图所示,cdf模块与histogram模块同一时刻只需读取单一地址数据,故与cdf计算模块、hist直方图统计模块的连接分别引出一组读写端口,内部进行仲裁复用。由于mapping模块中的双线性插值需要读取当前像素最近的四个块(tile)的输出LUT,为了实现mapping模块的全流水,实现了四个块的并行读取,引出mapping模块需要使用的四个读数据端口和读地址端口,由mapping模块计算得到四个块的坐标输入,进行地址译码,同时输出四个块的读取数据。
鉴于图像区域每个分块都需要分配一块BRAM,为减少资源占用,故本工程采用4×4=16分块设计,虽然实际输出效果远不如8×8tile,但效果优于传统的HE算法。
modelsim仿真波形及图片结果如下:
详细算法说明请参考 docs/RTL_OVERVIEW.md。
| 模块 | 功能描述 | 16tile 文件 | 64tile 文件 |
|---|---|---|---|
clahe_coord_counter.v |
像素坐标计数与 tile 定位 | 16tile | 64tile |
clahe_histogram_stat*.v |
3 级流水线直方图统计 | 16tile v2 / 16tile | 64tile |
clahe_clipper_cdf.v |
对比度裁剪与 CDF 生成 | 16tile | 64tile |
clahe_mapping_parallel.v |
并行像素映射与双线性插值 | 16tile | 64tile |
clahe_ram_*tiles_parallel.v |
伪双端口 RAM 框架 | 16tile | 64tile |
clahe_top.v |
顶层模块,集成所有功能 | 16tile | 64tile |
- Stage 1: 坐标计数与 tile 索引计算
- Stage 2: 直方图统计(3 级子流水线)
- Stage 3: 对比度裁剪与 CDF 计算(帧间处理)
- Stage 4: CDF 查找与双线性插值(5 级子流水线)
- Stage 5: 数据对齐与输出
详细架构说明请参考 docs/RTL_OVERVIEW.md。
- Tile 配置: 4×4 = 16 tiles
- Tile 尺寸: 320×180 像素
- 处理能力: 1280×720@30fps
- 吞吐率: 1 像素/周期
- 流水线级数: 5 级
- 清零时间: 256 周期(@100MHz 约 2.56μs)
- Tile 配置: 8×8 = 64 tiles
- Tile 尺寸: 160×90 像素
- 处理能力: 1280×720@30fps
- 吞吐率: 1 像素/周期
- 流水线级数: 5 级
- 清零时间: 256 周期(@100MHz 约 2.56μs)
- 性能提升: 相比 16 tile 版本,64 tile 版本在局部对比度增强方面表现更好
基于 Vivado 2018.3 Implementation 在目标器件 Xilinx 7VX690T 上的真实数据对比。
| 资源 (Resource) | Baseline (64t) | Optimized (64t_opt) | 变化 (Change) |
|---|---|---|---|
| LUTs | 8,014 | 3,929 | ⬇️ 51.0% |
| Registers (Flip-Flops) | 637 | 3,104 | ⬆️ 387% (流水线代价) |
| Block RAM (Tiles) | 66 | 18 | ⬇️ 72.7% |
| DSP48E1 | 3 | 3 | → 持平 |
| F7 Muxes | 768 | 46 | ⬇️ 94.0% |
| F8 Muxes | 256 | 6 | ⬇️ 97.7% |
| 指标 | Baseline @ 74MHz | Optimized @ 100MHz |
|---|---|---|
| WNS | -22.347 ns ❌ | +3.223 ns ✅ |
| 关键路径逻辑级数 | 185 级 | 5 级 |
| 理论最高频率 | ~28 MHz | ~148 MHz |
xychart-beta
title "资源变化百分比 (负值=节省, 正值=增加)"
x-axis ["LUTs", "BRAM", "F7 Mux", "F8 Mux", "Registers"]
y-axis "变化%" -100 --> 400
bar [-51, -73, -94, -98, 387]
| 优化项 | 理论基础 | 效果 |
|---|---|---|
| 4-Bank 存储交织 | Parhi VLSI DSP Folding | BRAM -73% |
| 33级流水线除法器 | Cut-Set Pipelining | 逻辑级数 185→5 |
| Retiming 延迟对齐 | Retiming 定理 | 时序收敛 @ 100MHz |
| Crossbar 复用 | Resource Sharing | LUT -51% |
📌 寄存器增加是有意的设计权衡:流水线化需要额外寄存器,但换来了 5.3× 的频率提升。
- 仿真工具(任选其一):
- Mentor Graphics ModelSim/QuestaSim
- Synopsys VCS
- Icarus Verilog
- Python 3: 用于验证脚本和图像处理
- 操作系统: Windows/Linux(本项目在 Windows 下开发测试)
-
克隆仓库
git clone <repository-url> cd CLAHE
-
选择版本
- 入门学习:使用
projects/16tile/ - 生产应用:使用
projects/64tile/
- 入门学习:使用
-
运行仿真
提供了针对不同版本的仿真脚本,请根据需要选择:
项目版本 脚本位置 脚本文件名 功能描述 64tile_optimized 🔒 获取权限 run_top_opt.do64tile 优化版顶层仿真(需联系作者获取) 64tile projects/64tile/sim/run_clahe_top.do64tile 原版顶层仿真。 16tile projects/16tile/sim/run_tb_clahe_top_bmp_multi.do16tile 版本的多帧 BMP 输入仿真。 运行命令示例:
64tile 优化版 (Optimized): 🔒 需联系作者获取访问权限 (📧 [email protected])
64tile 原版 (Legacy):
cd projects/64tile/sim vsim -do run_clahe_top.do16tile 版本:
cd projects/16tile/sim vsim -do run_tb_clahe_top_bmp_multi.do
clahe_coord_counter.v: 坐标计数与 tile 定位clahe_histogram_stat*.v: 直方图统计与冲突处理clahe_clipper_cdf.v: 对比度裁剪与 CDF 生成clahe_mapping_parallel.v: 并行映射与双线性插值clahe_ram_*tiles_parallel.v: RAM 架构与 Ping-Pong 设计
- 模块级测试:
projects/*/tb/tb_clahe_*.v- 各模块独立测试 - 顶层测试:
projects/*/tb/tb_clahe_top_bmp.v- 整帧 BMP 输入测试 - 冲突测试:
projects/*/tb/tb_histogram_conflict_test.v- 直方图统计冲突场景
verify_output.py: 输出图像与黄金模型对比verify_cdf_golden.py: CDF 计算结果校验compare_results.py: 批量结果对比
- 输入图像:
assets/images/或flows/full_sim/bmp_in/ - 输出结果:
bmp_test_results/output/ - 参考数据:
assets/data/
parameter IMG_WIDTH = 1280, // 图像宽度
parameter IMG_HEIGHT = 720, // 图像高度16 tile 版本:
parameter TILE_H_NUM = 4, // 水平 tile 数量
parameter TILE_V_NUM = 4, // 垂直 tile 数量
parameter TILE_NUM = 16, // 总 tile 数64 tile 版本:
parameter TILE_H_NUM = 8, // 水平 tile 数量
parameter TILE_V_NUM = 8, // 垂直 tile 数量
parameter TILE_NUM = 64, // 总 tile 数- 插值模式 (
enable_clahe=1, enable_interp=1): 使用双线性插值,效果最佳 - 单 tile 模式 (
enable_clahe=1, enable_interp=0): 单 tile 映射,处理速度快 - Bypass 模式 (
enable_clahe=0): 直接输出原始数据
- Ping-Pong 架构: 使用双缓冲实现统计与映射的并行处理,提高吞吐率
- 冲突处理机制: 针对相邻重复(AA)和间隔重复(ABA)场景的优化处理
- 并行 RAM 读取: 单周期读取四个相邻 tile 的 CDF 数据,减少流水线级数
- 双线性插值: 8bit 输入扩展到 16bit 计算,确保平滑过渡
- 参数化设计: 通过参数化支持不同 tile 规模和图像分辨率
- 16tile 版本: 早期实现,适合教学和算法验证,资源占用较小
- 64tile 版本: 基础 8x8 实现,逻辑直接但资源消耗大。
- 64tile Optimized 版本: 🔒 采用 VLSI DSP 优化架构,极低资源消耗。需联系作者获取(📧 [email protected])
- RTL 代码遵循 Verilog-2001 标准
- 模块命名采用
clahe_前缀 - 参数化设计,便于配置和扩展
- 新功能开发建议在
projects/64tile/版本上进行 - 修改 RTL 后请更新
docs/RTL_OVERVIEW.md - 提交前运行完整仿真验证:
sim/run_all.do+flows/full_sim
本项目为开源项目,具体许可证信息请查看 LICENSE 文件。
更多技术细节请参考
docs/目录下的详细文档。





