0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于FPGA实现图像直方图设计

FPGA设计论坛 来源:FPGA设计论坛 2024-12-24 10:24 次阅读

直方图统计的原理

直方图统计从数学上来说,是对图像中的像素点进行统计。图像直方图统计常用于统计灰度图像,表示图像中各个灰度级出现的次数或者概率。统计直方图的实现采用C/C++或者其他高级语言实现十分简单,单采用FPGA来实现直方图的统计就稍显麻烦。若使用Xilinx和Altera的FPGA芯片,可以使用HLS来进行图像的加速处理。但这暂时不是我的重点。

C语言实现直方图统计:unsigned int histoBuffer[256];

for(int idxCol = 0; idxCol < imageWidth; idxCol ++)

{

for(int idxRow = 0; idxRow < imageHeight; idxRow ++)

{

histoBuffer[image[idxRow * imageWidth + idxCol]] ++;

}

}

基于FPGA实现图像直方图

在前面可以看到基于C/C++或者其他高级语言实现直方图统计十分简单。但是在FPGA中,需要设计具体的时序和电路才能正确地将直方图进行统计。

直方图统计的注意点

使用FPGA来完成直方图的统计需要注意以下几点:

对一幅图像进行统计,必须等到当前的图像“流过”后才能完成。这使得采用FPGA相较于其他方式并没有太大的优势。

在统计的过程中,需要对已经统计的像素的统计值进行缓存。

在下一帧图像来临的时候,需要将RAM中的数据清空。

设计统计模块

根据上面的要点,在设计直方图统计电路的时候可以按照如下思路来进行:

新的一帧图像来临是,需要将上一帧图像的直方图统计结果清零。

在新一帧图像数据有效时,进行统计

一帧图像数据统计完成后,将统计结果读出,并将统计结果输出到外部。

状态机设计:

在复位或空闲状态下,系统处于IDLE状态,当检测到新一帧图像(vsync信号的上升沿)时,状态跳转到CLEAR状态,清空RAM中保存的上一帧图像的数据。当RAM中的图像数据清空完成后,进入到直方图统计状态CALCULATE,在该状态下进行直方图的统计当一帧图像统计完成后,将本帧图像的统计结果输出,也即GET_HISTO状态。

4b988b6e-c01b-11ef-9310-92fbcf53809c.png

CLEAR状态:

下图是CLEAR状态下的时序设计图,拉高一个clear_flag信号,向RAM中写入0,将上一帧图像的统计结果清零。

4ba85c74-c01b-11ef-9310-92fbcf53809c.png

CALCULATE状态:

统计状态下完成的任务是最复杂的,由于在图像数据流来领的时候,常常会遇到相邻几个像素点的灰度值是相同的,因此可以将这些点进行统计,然后在将统计值写入到RAM中,将小对RAM的读写操作。

下面的时序图是一个典型的统计时序设计图,基本包括了图像流入时的像素状态。

在统计时,主要是来比较当前的像素点和上一个像素点的值是否相同,若相同则像素统计值cal_pixel就会加一,直到相邻两像素值不同或者一行图像结束时,停止加一,并且将当前统计结果cal_pixel和RAM中已经缓存的统计值进行累加,重新写入到RAM中(也即wr_ram_data),同时需要使能RAM的写操作。wr_ram_en。写入RAM的地址,其实就是当前的灰度值。在这之中,需要注意从RAM中读出数据具有1或者2个时钟周期的Latency(根据IP核设置有关。

在FPGA的直方图统计中,该部分是最重要的。完成了该时序图,也就基本上完成了统计电路。

4bbb6a58-c01b-11ef-9310-92fbcf53809c.png

GET_HISTO状态:

4bd43d30-c01b-11ef-9310-92fbcf53809c.png

该状态下,就是完成对直方图的统计结果的读出。

程序设计

FPGA完成直方图的设计,其实就是上面的三个时序图的设计,完成了上述三个时序图后,就能够直方图统计模块。下面的这个模块完成的是一个256*256大小的灰度图的直方图统计,若需要对其他大小的图像进行直方图统计,只需修改其中的参数即可。其实对于我的设计,每一行的像素个数是由上游模块确定的,在本模块中,只需指定图像的高度即可,指定高度,也仅仅是为了将直方图从RAM中读出。

parameter IMG_WIDTH=256;

parameter IMG_HEIGHT=256;

`timescale 1ns / 1ps

module calculate_histogram(

input wire clk ,

inputwire rst ,

input wire pi_hsync,

inputwire pi_vsync,

inputwire pi_data_vld,

input wire [7:0]pi_data ,

output wire po_histo_vld,

output wire [31:0]po_histo_data

);

//==========================================

//parameter define

//==========================================

parameter IMG_WIDTH = 256 ;

parameter IMG_HEIGHT =256 ;

parameter GRAY_LEVEL= 256;//灰度级

parameter IDLE = 4'b0001;//空闲状态

parameter CLEAR= 4'b0010;//清空RAM中数据状态

parameter CALCULATE = 4'b0100;//统计图像直方图状态

parameter GET_HISTO = 4'b1000;//输出直方图

//==========================================

//internal siganls

//==========================================

reg [3:0]state ;//状态寄存器

reg [1:0]vsync_dd;//场同步信号寄存

//==========================================

//清空RAM阶段

//==========================================

reg [8:0]cnt_clear ;

wireadd_cnt_clear;

wire end_cnt_clear;

reg clear_flag;//清空RAM指示信号

//==========================================

//统计直方图阶段

//==========================================

reg [12:0]cnt_row ;

wire add_cnt_row ;

wire end_cnt_row;

reg data_vld_dd0;//数据有效延时信号

reg data_vld_dd1;//数据有效延时信号

reg [7:0]pi_data_dd0;//有效数据延时

reg [7:0]pi_data_dd1;//有效数据延时

reg [31:0]cal_pixle;//相同的像素统计值

reg [31:0]cal_value ;//写入RAM的统计值

reg cal_value_vld;//写入RAM数据有效信号

reg cal_one_row_done;//统计一行图像数据结束

wire [7:0]cal_wr_ram_addr;//统计状态下写RAM的地址

wire [7:0]cal_rd_ram_addr;//统计状态下读RAM的地址

//==========================================

//读出数据阶段

//==========================================

reg get_data_flag ;

reg [8:0]cnt_get ;

wire add_cnt_get ;

wire end_cnt_get ;

reg histo_data_vld ;

wire [31:0]histo_data ;

//==========================================

//Block RAM Related Signals

//==========================================

reg wr_ram_en ;//写RAM使能信号

reg [7:0]wr_ram_addr;//写RAM地址

reg [31:0]wr_ram_data ;//写入RAM的数据

reg [7:0]rd_ram_addr ;//读RAM的地址

wire[31:0]rd_ram_data;//从RAM中读出的数据

assign po_histo_data = (histo_data_vld) ? histo_data : 32'd0;

assign po_histo_vld = histo_data_vld;

//----------------state machine describe------------------

always @(posedge clk) begin

if (rst==1'b1) begin

state <= IDLE ;

end

else begin

case(state)

IDLE : begin

//检测到新的一帧图像

if (vsync_dd[0] == 1'b1 && vsync_dd[1] == 1'b0) begin

state <= CLEAR;

end

else begin

state <= IDLE;

end

end

CLEAR : begin

//当前RAM中的数据已经清空

if (end_cnt_clear == 1'b1) begin

state <= CALCULATE;

end

else begin

state <= CLEAR;

end

end

CALCULATE : begin

//当前一幅图像数据的灰度直方图已经统计完成

if (end_cnt_row == 1'b1) begin

state <= GET_HISTO;

end

else begin

state <= CALCULATE;

end

end

GET_HISTO : begin

//将RAM中的直方图数据全部读出

if (end_cnt_get == 1'b1) begin

state <= IDLE;

end

else begin

state <= GET_HISTO;

end

end

default : begin

state <= IDLE;

end

endcase

end

end

//----------------vsync_dd------------------

//检测一帧图像

always @(posedge clk) begin

if (rst==1'b1) begin

vsync_dd <= 'd0;

end

else begin

vsync_dd <= {vsync_dd[0], pi_vsync};

end

end

//==========================================

//during the clear state

//==========================================

//----------------cnt_clear------------------

//用于清空RAM的计数器

always @(posedge clk) begin

if (rst == 1'b1) begin

cnt_clear <= 'd0;

end

else if (add_cnt_clear) begin

if(end_cnt_clear)

cnt_clear <= 'd0;

else

cnt_clear <= cnt_clear + 1'b1;

end

else begin

cnt_clear <= 'd0;

end

end

assign add_cnt_clear = state == CLEAR && wr_ram_en == 1'b1;

assign end_cnt_clear = add_cnt_clear &&cnt_clear == GRAY_LEVEL - 1;

//----------------clear_flag------------------

always @(posedge clk) begin

if (rst==1'b1) begin

clear_flag <= 1'b0;

end

else if (state == CLEAR ) begin

if (end_cnt_clear == 1'b1) begin

clear_flag <= 1'b0;

end

else begin

clear_flag <= 1'b1;

end

end

else begin

clear_flag <= 1'b0;

end

end

//==========================================

//during the calculate state

//==========================================

//----------------delay------------------

always @(posedge clk) begin

if (rst==1'b1) begin

data_vld_dd0 <= 'd0;

data_vld_dd1 <= 'd0;

pi_data_dd0 <= 'd0;

pi_data_dd1 <= 'd0;

end

else begin

data_vld_dd0 <= pi_data_vld;

data_vld_dd1 <= data_vld_dd0;

pi_data_dd0 <= pi_data;

pi_data_dd1 <= pi_data_dd0;

end

end

//----------------cal_pixle------------------

always @(posedge clk) begin

if (rst==1'b1) begin

cal_pixle <= 'd1;

end

else if (state == CALCULATE && data_vld_dd0 == 1'b1 ) begin

//相邻两个像素点的值不同,统计值回到1

if (pi_data != pi_data_dd0 ) begin

cal_pixle <= 'd1;

end

//一行图形数据统计结束

else if (pi_data_vld == 1'b0 ) begin

cal_pixle <= 'd1;

end

//相邻两个像素点的值相同

else if (pi_data == pi_data_dd0) begin

cal_pixle <= cal_pixle + 1'b1;

end

end

else begin

cal_pixle <= 'd1;

end

end

//----------------cal_value------------------

//写入RAM的数据

always @(posedge clk) begin

if (rst==1'b1) begin

cal_value <= 'd0;

cal_value_vld <= 1'b0;

end

else if (state == CALCULATE ) begin

//相邻两个像素值不同,将当前统计结果写入

if (pi_data != pi_data_dd0 && data_vld_dd0 == 1'b1) begin

//从RAM中读出的数据,有一拍的延时,这里保证了数据对齐

cal_value <= rd_ram_data + cal_pixle;

cal_value_vld <= 1'b1;

end

//一行图像统计结束,将当前结果写入

else if(pi_data_vld == 1'b0 && data_vld_dd0 == 1'b1)begin

cal_value <= rd_ram_data + cal_pixle;

cal_value_vld <= 1'b1;

end

else begin

cal_value <= 'd0;

cal_value_vld <= 1'b0;

end

end

else begin

cal_value <= 'd0;

cal_value_vld <= 1'b0;

end

end

//----------------cal_wr_ram_addr/cal_rd_ram_addr------------------

assign cal_wr_ram_addr = pi_data_dd1; //写入数据RAM的地址

assign cal_rd_ram_addr = pi_data;//读出数据RAM的地址

//----------------cal_one_row_done------------------

always @(posedge clk) begin

if (rst==1'b1) begin

cal_one_row_done <= 1'b0;

end

//一行图像统计完成

else if (state == CALCULATE && pi_data_vld == 1'b0 && data_vld_dd0 == 1'b1) begin

cal_one_row_done <= 1'b1;

end

else begin

cal_one_row_done <= 1'b0;

end

end

//----------------cnt_row------------------

always @(posedge clk) begin

if (rst == 1'b1) begin

cnt_row <= 'd0;

end

else if (add_cnt_row) begin

if(end_cnt_row)

cnt_row <= 'd0;

else

cnt_row <= cnt_row + 1'b1;

end

end

assign add_cnt_row = cal_one_row_done == 1'b1;

assign end_cnt_row = add_cnt_row &&cnt_row == IMG_HEIGHT - 1;

//==========================================

//during get histogram data state

//==========================================

//----------------get_data_flag------------------

always @(posedge clk) begin

if (rst==1'b1) begin

get_data_flag <= 1'b0;

end

else if (state == GET_HISTO) begin

if (end_cnt_get == 1'b1) begin

get_data_flag <= 1'b0;

end

else begin

get_data_flag <= 1'b1;

end

end

else begin

get_data_flag <= 1'b0;

end

end

//----------------cnt_get------------------

always @(posedge clk) begin

if (rst == 1'b1) begin

cnt_get <= 'd0;

end

else if (add_cnt_get) begin

if(end_cnt_get)

cnt_get <= 'd0;

else

cnt_get <= cnt_get + 1'b1;

end

else begin

cnt_get <= 'd0;

end

end

assign add_cnt_get = get_data_flag == 1'b1;

assign end_cnt_get = add_cnt_get &&cnt_get == GRAY_LEVEL - 1;

//----------------histo_data_vld------------------

always @(posedge clk) begin

if (rst==1'b1) begin

histo_data_vld <= 1'b0;

end

else begin

histo_data_vld <= get_data_flag;

end

end

assign histo_data = (histo_data_vld) ? rd_ram_data : 'd0 ;

//==========================================

//signals that related to Block RAM

//==========================================

histogram_ram inst_bram_histo (

.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

);

//----------------wr_ram_addr,wr_ram_data,wr_ram_en------------------

always @(*) begin

if (state == CLEAR) begin

wr_ram_addr = cnt_clear;

wr_ram_en = clear_flag;

wr_ram_data = 'd0;

end

else if (state == CALCULATE) begin

wr_ram_addr = cal_wr_ram_addr;

wr_ram_en = cal_value_vld;

wr_ram_data = cal_value;

end

else begin

wr_ram_addr = 'd0;

wr_ram_en = 1'b0;

wr_ram_data = 'd0;

end

end

//----------------rd_ram_addr------------------

always @(*) begin

if (state == CALCULATE) begin

rd_ram_addr = cal_rd_ram_addr;

end

else if (state == GET_HISTO) begin

rd_ram_addr = cnt_get;

end

else begin

rd_ram_addr = 'd0;

end

end

endmodule

仿真验证

由于只是算法的一个验证,我并不想使用太多的外部资源,使用片上的存储资源即可。一个2562568bit大小的图像并不会占用多少资源。图像大小是256*256的灰度图,在matlab中完成直方图的统计,是十分简单的,只需使用imhist这个函数即可。

4be67144-c01b-11ef-9310-92fbcf53809c.png

直方图部分统计结果如下:

4bf7e51e-c01b-11ef-9310-92fbcf53809c.png

在modelsim中,对前面所设计的模块进行仿真。仿真的结果如下:

4c084080-c01b-11ef-9310-92fbcf53809c.png

可以看到仿真的直方图统计结果与matlab中的仿真结果相比一致。在Modelsim的Memory List中,也可以看到一帧图像统计完成后,RAM中的结果,从结果中可以看到统计结果和matlab一致。

4c1d3f6c-c01b-11ef-9310-92fbcf53809c.png

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FPGA
    +关注

    关注

    1629

    文章

    21734

    浏览量

    603111
  • 直方图
    +关注

    关注

    0

    文章

    21

    浏览量

    7883

原文标题:FPGA图像处理基础----直方图统计

文章出处:【微信号:gh_9d70b445f494,微信公众号:FPGA设计论坛】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    基于直方图算法进行FPGA架构设计

    引言 直方图统计在图像增强和目标检测领域有重要应用,比如直方图均衡,梯度直方图直方图的不同种类和统计方法请见之前的文章。本章就是用
    的头像 发表于 12-10 16:37 2790次阅读

    基于FPGA实现分离用软件的图像处理系统设计

    灰度直方图统计直方图图像的灰度分布统计的一种表示方法,统计目标图像中各个灰度点的像素个数,很多对于图像的调整算法都是基于此进行的;如何基于
    的头像 发表于 04-04 10:32 1433次阅读

    FPGA直方图处理方法

    图像直方图用作数字图像中色调分布的图形表示。它绘制了每个色调值的像素数。通过查看特定图像直方图,观看者将能够一目了然地判断整个色调分布。
    的头像 发表于 12-15 09:20 1064次阅读
    <b class='flag-5'>FPGA</b><b class='flag-5'>直方图</b>处理方法

    一文带你了解FPGA直方图操作

    后面慢慢介绍,先重点介绍直方图均衡。公众号:OpenFPGA 直方图统计及FPGA实现 从数学上来说,
    发表于 01-10 15:07

    基于FPGA的HDTV视频图像灰度直方图统计算法设计

    本文介绍了如何在FPGA 中利用Block RAM 的特殊结构实现HDTV 视频增强算法中灰度直方图统计。灰度直方图统计灰度直方图统计是
    发表于 05-14 12:37

    有谁用FPGA做过数字图像直方图统计

    刚开始用FPGA做数字图像处理,看了一些有关直方图方面的资料但是感觉还是不太清晰,请问有谁做过求带顺便求推荐FPGA做数字图像处理方面入门级
    发表于 08-21 09:48

    有谁用FPGA做过数字图像直方图统计

    刚开始用FPGA做数字图像处理,看了一些有关直方图方面的资料但是感觉还是不太清晰,请问有谁做过求带顺便求推荐FPGA做数字图像处理方面入门级
    发表于 08-23 11:01

    基于FPGA图像直方图实时显示

    ``基于FPGA图像直方图实时显示 AT7_Xilinx开发板(USB3.0+LVDS)资料共享 腾讯链接:https://share.weiyun.com/5GQyKKc 百度网盘链接
    发表于 07-12 17:33

    基于FPGA图像直方图均衡处理

    放在projectzstar_ex66matlab文件夹下。3基于FPGA直方图均衡处理工程文件夹at7_img_ex04at7.srcssources_1 ew下的laplace_transform.v模块实现
    发表于 07-14 17:26

    【干货】基于FPGA图像处理(图像增强)之直方图均衡

    最近一直在用FPGA图像处理相关的算法,主要是集中在图像增强和增晰方面。现在来介绍一个复杂度不高,但确实也还比较好用的图像增强算法-直方图
    发表于 12-08 09:40

    Labview图像处理——直方图介绍

    一、Labview直方图程序介绍1、前面板2、程序框图二、直方图基本概念介绍图像直方图图像的基本属性之一,也是
    发表于 11-24 17:59

    基于FPGA的视频图像灰度信号直方图均衡算法实现设计

    直方图均衡作为一种基础的图像处理方法在很多领域得到应用,但大多是通过DSP或者CPU编程实现,其优点是灵活性比较高,调试方便,最大的缺点是很难做到实时或者准实时处理,这在某些领域是不可接受的。而使
    发表于 07-20 18:06 2688次阅读
    基于<b class='flag-5'>FPGA</b>的视频<b class='flag-5'>图像</b>灰度信号<b class='flag-5'>直方图</b>均衡算法<b class='flag-5'>实现</b>设计

    MATLAB如何实现图像增强灰度变换直方图均衡匹配

    在MATLAB数字图像处理领域,如何实现空间域图像增强的灰度变换,以及图像直方图的均衡和匹配(配准)?本文通过大量的图片增强案例,从
    发表于 01-13 21:56 1.1w次阅读
    MATLAB如何<b class='flag-5'>实现</b><b class='flag-5'>图像</b>增强灰度变换<b class='flag-5'>直方图</b>均衡匹配

    基于直方图算法的FPGA设计架构

    直方图统计在图像增强和目标检测领域有重要应用,比如直方图均衡,梯度直方图直方图的不同种类和统计方法请见之前的文章。本章就是用
    发表于 04-20 10:47 1195次阅读
    基于<b class='flag-5'>直方图</b>算法的<b class='flag-5'>FPGA</b>设计架构

    如何使用FPGA实现实时图像增强算法

    FPGA,通过并行处理结构及流水线技术,可实时处理每秒50帧780×582×12bits的可见光图像。在处理视频的过程中,由前一帧图像直方图信息,来增强后一帧
    发表于 02-03 15:21 10次下载
    如何使用<b class='flag-5'>FPGA</b><b class='flag-5'>实现</b>实时<b class='flag-5'>图像</b>增强算法