当前位置: 首页 > article >正文

FPGA的直方图均衡

文章目录

  • 一、直方图均衡
  • 二、代码实现
  • 三、仿真


一、直方图均衡

  直方图均衡(Histogram Equalization)是一种用于增强图像对比度的图像处理技术。它通过重新分配图像像素的灰度值,使得图像的灰度直方图在整个灰度范围内均匀分布,从而增强图像的细节和视觉效果。
  直方图均衡也称为直方图拉伸,是一种简单有效的图像增强技术,通过改变图像的直方图分布,来改变图像中各像素的灰度,可用于增强动态范围偏小的图像的对比度。原始图像由于其灰度分布可能集中在较窄的区间,结果呈现出曝光不足/过高,使图像灰度集中在低/高亮度范围内,对比度很低。
  直方图均衡的基本原理,就是对在图像中像素个数多的灰度值(即对画面起主要作用的灰度值)进行拉伸,而对像素个数少的灰度值(即对画面不起主要作用的灰度值)进行合并,从而提高对比度,使图像清晰,达到增强的目的。如下图所示:
在这里插入图片描述

二、代码实现

  直方图均衡代码参考的这篇博客:FPGA图像处理仿真实验——直方图均衡化,该代码不需要缓存一张图像,但是需要连续的两帧图像,前一帧用于像素灰度级数统计和灰度级数累积统计,后一帧从 BRAM 中读取对应的灰度级数累积统计结果,并与 136957 相乘得到 mult_result,其中mult_result[34:27为整数部分, mult_result[26:0]为小数部分。完整代码如下:

`timescale 1ns / 1ps
 module histogram_equ(
    input clk,
    input rst_n,
    
    input per_frame_vsync,
    input per_frame_href,
    input per_frame_clken,
    input [7:0] per_img_Y,
    
    output post_frame_vsync,
    output post_frame_href,
    output post_frame_clken,
    output [23:0] post_img_Y
    );
    
localparam IMG_TOTAL_PIXEL=307200;   //640*480
localparam IMG_MAX_GRAY=256;      //0-255
 
//reg web;
wire [18:0] cnt,cntplus;   //同一灰度级像素个数计数器
wire [18:0] ram_his_rd_data;  //从累计直方图中读出的数据
reg vsync_delay;
wire vsync_negedge;    //场同步信号下降沿
wire vsync_posedge;    //场同步信号上升沿
reg web2;   //双端口RAM u2的写使能信号
reg tag;    //标志位
reg [7:0] dizhi;
reg [7:0] dizhi1,dizhi2;   //双端口RAM u2的写数据地址
wire [7:0] addra;    //双端口RAM  u1的a端口地址
wire [18:0] dout;    //双端口RAM  u1的输出数据
wire [18:0] din;    //双端口RAM u1的输入数据
reg [7:0] addrb;   //双端口RAM  u1的b端口地址
wire web;   //双端口RAM的写使能 
reg [18:0] sum;  //为了计算累加直方图,用于存放直方图数据的和
wire [18:0] ram_accu_rddata;   //从累加直方图中读出的数据
 
 
//统计像素个数时的写使能信号
//always @(posedge clk)
//begin
//    web<=per_frame_clken;
//    //addrb<=per_img_Y;
//end
assign cntplus=cnt+1;
 
 
//将场同步信号延时一拍
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        vsync_delay<=1'b0;
    else
        vsync_delay<=per_frame_vsync;
end
assign vsync_negedge=vsync_delay&(!per_frame_vsync);
assign vsync_posedge=(!vsync_delay)&per_frame_vsync;
always @(posedge clk)
begin
    dizhi1<=dizhi;
    dizhi2<=dizhi1;
end
//在一帧图像输入完成后需要对RAM进行数据读出和数据清0,此时的读写数据地址为变量dizhi
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        dizhi<=8'd0;
    else
        begin
            if(tag==0)
                 begin
                    if(dizhi==255)
                        dizhi<=dizhi;
                    else
                        dizhi<=dizhi+1;
                 end
        end
end
//在对累计直方图进行写操作时,只有在标志位tag=0时才进行,并且只遍历一次0-255地址
always @(posedge clk)
begin
   if(tag==0)
        begin
            if(dizhi2==255)
                web2<=0;
            else
                web2<=1;
        end
end
 
//当场有效信号为上升沿时,标志位置1,下降沿时,标志位置0。tag=1代表正在输入图像数据流,tag=0代表两帧图像输入数据流之间的间隙
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        tag<=1;
    else
        begin
            if(vsync_posedge)
                tag<=1;
            if(vsync_negedge)
                tag<=0;
        end
end
 
always @(posedge clk)
begin
    addrb<=addra;
end
 
assign addra=tag?per_img_Y:dizhi;  //当输入图像数据流时,地址为每个像素点图像灰度值对应的地址,当处于两帧图像输入数据流之间的间隙时,地址为0-255遍历过程的地址
assign cnt=tag?dout:cnt;  //当输入图像数据流时,cnt与u1 的输出数据相连,否则保持
assign ram_his_rd_data=web2?dout:ram_his_rd_data;  //当web2有效时,从累计直方图中读出的数据与双端口RAM u2的输出相连,否则保持
assign din=tag?cntplus:(19'd0);  //当tag=1时,输入为+1后的数据,当tag=0时,需要对RAN进行清0操作,所以输入为0
assign web=tag?per_frame_clken:1;  //tag=1时,使能信号为帧时钟使能信号,tag=0时,要对RAM进行清零操作,所以使能信号一直为1
//计算累加直方图要输入的数据
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
            sum<=19'd0;
        end
    else
        begin
            if(web2==1)
                begin
                    sum<=sum+ram_his_rd_data;
                end
        end
end
 
//双端口RAM u1,用于存放直方图的数据,当输入图片数据流有效时,将每个像素点灰度值对应的地址存放的数据读出,再加1,然后再写回去
//在帧与帧之间的间隙,从地址0-255,将累计直方图数据读出,再将RAM清零
blk_mem_gen_0 u1_blk_mem_gen_0(
    .clka(clk),
    .wea(1'b0),
    .addra(addra),  // input wire [7 : 0] addra
    .dina(0),    // input wire [18 : 0] dina
    .douta(dout),  // output wire [18 : 0] douta
    .clkb(clk),    // input wire clkb
    .web(web),      // input wire [0 : 0] web
    .addrb(addrb),  // input wire [7 : 0] addrb
    .dinb(din),    // input wire [18 : 0] dinb
    .doutb()  // output wire [18 : 0] doutb
);
//双端口RAM u2,用于存放累加直方图数据,将累加直方图的数据从a端口写入,b端口读出
blk_mem_gen_0 u2_blk_mem_gen_0(
    .clka(clk),
    .wea(web2),
    .addra(dizhi2),  // input wire [7 : 0] addra
    .dina(sum),    // input wire [18 : 0] dina
    .douta(),  // output wire [18 : 0] douta
    .clkb(per_frame_clken),    // input wire clkb
    .web(1'b0),      // input wire [0 : 0] web
    .addrb(per_img_Y),  // input wire [7 : 0] addrb
    .dinb(),    // input wire [18 : 0] dinb
    .doutb(ram_accu_rddata)  // output wire [18 : 0] doutb
);
 
//直方图均衡化
reg [34:0] data_mult;       //乘法运算结果
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        data_mult<=35'd0;
    else
        data_mult<=ram_accu_rddata* 18'd136957;
end
reg [7:0] data_div;    //除法运算结果
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        data_div<=8'd0;
    else
        //data_div<=data_mult/IMG_TOTAL_PIXEL;
        data_div = data_mult[34:27] + data_mult[26];
end
//------------------------------------------
//lag 3 clocks signal sync  
reg	[2:0]	per_frame_vsync_r;
reg	[2:0]	per_frame_href_r;	
reg	[2:0]	per_frame_clken_r;
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		begin
		per_frame_vsync_r <= 0;
		per_frame_href_r <= 0;
		per_frame_clken_r <= 0;
		end
	else
		begin
		per_frame_vsync_r 	<= 	{per_frame_vsync_r[1:0], 	per_frame_vsync};
		per_frame_href_r 	<= 	{per_frame_href_r[1:0], 	per_frame_href};
		per_frame_clken_r 	<= 	{per_frame_clken_r[1:0], 	per_frame_clken};
		end
end
assign	post_frame_vsync 	= 	per_frame_vsync_r[2];
assign	post_frame_href 	= 	per_frame_href_r[2];
assign	post_frame_clken 	= 	per_frame_clken_r[2];
assign  post_img_Y          =   post_frame_href?{data_div,data_div,data_div}:24'd0;
endmodule

代码中例化了两个双端RAM,具体如下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、仿真

  前面提到,由于不需要缓存整张图片用于直方图均衡的计数,所以属于连续的两帧图片,而仿真代码正好是将同一张图片连续读取两次,所以不需要修改仿真代码。仿真代码完整的可以去找之前的博客,这里只放仿真顶层文件,代码如下:

`timescale 1ns / 1ps



module img_sim_tb();
localparam	PIC_INPUT_PATH  	= 	"gsls_test1.bmp"			;
localparam	PIC_OUTPUT_PATH 	= 	"output.bmp"  	;
localparam	PIC_WIDTH  			=	640		;
localparam	PIC_HEIGHT 			=	480	;
reg         cmos_clk   = 0;
reg         cmos_rst_n = 0;

wire        cmos_vsync              ;
wire        cmos_href               ;
wire        cmos_clken              ;
wire [23:0] cmos_data               ;

wire        pos_vsync      ;
wire        pos_hsync      ;
wire        pos_de         ;
wire [23:0] pos_data       ;
parameter cmos0_period = 6;
always#(cmos0_period/2) cmos_clk = ~cmos_clk;
initial #(20*cmos0_period) cmos_rst_n = 1;
//--------------------------------------------------
//Camera Simulation
sim_cmos #(
		.PIC_PATH			(PIC_INPUT_PATH			)
	,	.IMG_HDISP 			(PIC_WIDTH 				)
	,	.IMG_VDISP 			(PIC_HEIGHT				)
)u_sim_cmos0(
        .clk            	(cmos_clk	    		)
    ,   .rst_n          	(cmos_rst_n     		)
	,   .CMOS_VSYNC     	(cmos_vsync     		)
	,   .CMOS_HREF      	(cmos_href      		)
	,   .CMOS_CLKEN     	(cmos_clken     		)
	,   .CMOS_DATA      	(cmos_data      		)
	,   .X_POS          	()
	,   .Y_POS          	()
);
histogram_equ_top u_histogram_equ_top(
.clk         (cmos_clk) ,                
.rst_n       (cmos_rst_n),                
                             
.pre_image_vsync    (cmos_vsync),  // vsync信号    
.pre_image_clken    (cmos_clken),  // 时钟使能信号     
.pre_image_hsync    (cmos_href),  // 数据有效信号     
.pre_image_data     (cmos_data),  // 输入图像数据RGB  
                              
.pos_image_vsync  (pos_vsync),  // vsync信号  
.pos_image_clken  (pos_de),  // 时钟使能信号    
.pos_image_hsync  (pos_hsync),  // 数据有效信号   
.pos_image_data   (pos_data)  // 输出图像Y数据 
);
//--------------------------------------------------
//Video saving 
video_to_pic #(
		.PIC_PATH       	(PIC_OUTPUT_PATH		)
	,	.START_FRAME    	(2                      )
	,	.IMG_HDISP      	(PIC_WIDTH 				)
	,	.IMG_VDISP      	(PIC_HEIGHT				)
)u_video_to_pic0(
	 	.clk            	(cmos_clk	            )
	,	.rst_n          	(cmos_rst_n             )
	,	.video_vsync    	(pos_vsync		)
	,	.video_hsync    	(pos_hsync		)
	,	.video_de       	(pos_de   		)
	,	.video_data     	(pos_data      )
);
endmodule


仿真结果如下,左边的为原始图像,中间是使用python代码处理的直方图均衡化,右边是Verilog仿真的直方图均衡化,可以看到有明显的图像失真,问题出在那暂时没有找留个坑。

在这里插入图片描述


http://www.kler.cn/a/600955.html

相关文章:

  • Python的线程、进程与协程
  • DrissionPage打造全自动音乐推荐系统——从爬虫到机器学习
  • 团体协作项目总结Git
  • Windows环境下使用OpenSSL查看pfx证书的有效期
  • 文章内容生成大语言模型训练的qa语料集
  • 使用vector构造杨辉三角形
  • vcd波形转仿真激励
  • 银行分布式新核心的部署架构(两地三中心)
  • 桑福德·韦尔策划美国捷运公司收购南美银行案例分析
  • 光学像差的类型与消除方法
  • DeepSeek-V3 模型更新,加量不加价
  • 【WebGIS教程2】Web服务与地理空间服务解析
  • 基于 PHP 内置类及函数的免杀 WebShell
  • 期权交易投资怎么操作?新手期权操作指南
  • 多模态大模型的基础模块
  • 稳定运行的以Neo4j图数据库为数据源和目标的ETL性能变差时提高性能方法和步骤
  • Web1.0、Web2.0、Web3.0:互联网进化之旅
  • Rviz 同时显示多个独立 URDF!解决双机械臂+底盘等场景(球体+方块实例演示)
  • 短期趋势动量策略思路
  • Git 命令大全,详解