日常·唠嗑
有2个多月没写文章了,又是老借口:“最近实在是很忙”🤣,不过说真,确实是比较忙,搞了半个月serdes,做不完的仿真😫,好几个小伙伴问东西,催更,也没时间回复。恰逢毕业季,把之前跟小马哥合作的项目更一下,供大家使用。
系统架构图:
一、视频效果
话不多说,直接看效果
本案例采用的DDE(数字细节增强)算法,通过高斯滤波(部分人会跟双边滤波结合,本人也是有这个案例的)分离原图的高频信息和低频信息,利用原图和高斯滤波的差值提取细节,再将细节叠加到原始图像上,实现细节增强,增强后图像清晰度明显提高。
FPGA:图像数字细节增强算法(工程+仿真+实物,可用毕设)
二、硬件及功能
1、硬件选择
开发板Altera:EP4CE10F17C8
摄像头:OV5640
缓存数据:SDRAM
板子是自己制作的,可以提供原理图、PCB截图供大家插到论文中。
PCB图可私信获取:
2、功能
电脑上位机打开测试图像并通过串口发送给FPGA,FPGA将接收到的图像缓存在SDRAM中并从SDRAM中读出数据以产生视频源,测试视频源经过RGB转灰度、数字细节增强算法处理后实时显示在TFTLCD液晶屏上。
1.上位机通过串口发送图片给FPGA;
2.FPGA将接收到的图像缓存在SDRAM中;
3.FPGA将SDRAM中缓存的数据循环读出以产生视频流;
4.RGB转灰度算法处理;
5.图像细节增强算法处理;
6.TFTLCD液晶屏实时显示;
7.细节增强算法效果可以通过按键控制开关;
注:
本次设计,主要是验证理论,FPGA直接读取图片进行细节增强。图像数字细节增强算法通常可以用于实战中,比如红外图像成像中或者OV5640的图像采集处理中(关于红外热成像系统,在24年7月,本人会推出新文章,新套件,有需求的朋友可以关注留意一下本账号)。
3、特点
1、全套实物(含工程资料)
2、上电即出功能效果
3、DDE数字细节增强算法(目前网上信息少,较为先进)
4、支持定制设计,将串口图片,改为摄像头实时图像
5、提供源代码工程(Verilog)
6、提供Modelsim仿真
7、提供PCB截图
8、提供QT上位机软件及其源码,可自行修改上位机界面图案或者文字功能
9、提供文档说明&visio框图&提供售后答疑
三、算法理论
摘要:DDE算法通常运用在红外热成像系统中。
背景:
热成像系统普遍存在输出图像目标与场景细节不清晰现象,是继非均匀校正之后制约热成像系统性能的一大不足,已成为国内外研究人员关注的重要问题。对此,国外已进行了一些深入的研究,并在其产品中获得成功应用,这也是国内热像仪与国外产品在显示图像质量上始终存在较大的差距的重要原因。近年来,国内研究已逐步关注并试图解决这一问题。FLIR 公可提出的DDE技术是当前针对该问题的一个很好的解决方案,但出于自身商业秘密的保护,鲜有关于核心算法的论文和技术报告公之于众。
红外图像的一大特点是有着很高的动态范围(如地面与天空),而某些目标与其背景或者目标局部的温差却相对较小。因此为了能以足够高的精度来量化大动态红外场景,高性能热成像系统通常都采用14 bits或更高精度的 AD对探测器输出信号进行采样和量化。而通常的显示设备或快速处理只要求8bits数据宽度,所以 14bits的高精度数据需要经过压缩处理。于是如果压缩方法处理不当,使得本来探测到大动态图像的信息得不到显示,即大动态图像压缩可能会造成原有信息丢失,在显示图像中表现为图像细节的损失。对于目前常用的线性映射(如AGC,Automatic GainControl)或者非线性映射(如直方图均衡、y变换)等压缩方法2,由于小目标或物体局部的图像在温差或者像素数目上都不占优势,使得这些方法在大动态图像的压缩中普遍存在这种缺陷。
FLIR Systems 研发出一种强大的算法帮助用户解决在高动态范围场景中定位低对比度目标的难题。此算法称为数字图像细节增强(DDE)。DDE 是一种高级非线性图像处理算法,可以保留高动态范围图像中的细节。图像细节得到增强,从而与原始图像背景的总动态范围相匹配。这样即使在温度变化十分显著的场景中,操作员也能够看清细节。
为什么高动态范围会成为问题?
答案在于人体视觉系统和典型视频接口的局限性。人类只能识别图像中约128128 级灰阶(7位)。对于红外热像仪而言,挑战在于将隐藏在 14 位信号(>15000 级灰阶)中的信息映射为人类可以识别的7位信号。此外,许多模拟和数字视频接口都要求采用8位,因此即使最终用户并非人类(例如,目标跟踪器),也需要将动态范围有效地限制为 256 级灰阶。
DDE 不就是直方图均衡化吗?
不是。直方图均衡化(HE) 及其许多变体的工作模式都是“较高动态范围(对比度)摄取主体温度区域的图像,较低动态范围摄取非主体温度区域的图像”而 DDE 均衡地增强所有细节,无论目标处于怎样的温度范围。也就是说,假设一个小的热物体处于冷背景当中,小物体拥有的细节也和代表该主体温度范围的背景一样清晰。
理论例证:
通过以下三幅图像中的五个理论靶标(△T≈200mk)图像,对线性AGC、HE 和 DDE 进行比较,起初五个靶标均未显现。每个靶标温度都比背景温度高约 200mK。
标准 AGC 算法没有提高图像质量(图1)。图2 所示图像使用直方图均衡化进行了增强。但不出所料,只能观测到中心靶标,因为它恰好处于此场景的主体动态范围内。
使用 FLIR Systems DDE 算法(图3),就可以同时观测到全部五个靶标。而且,五个靶标的对比度均匀,而与特定动态范围内的像素数量无关。正是这一点使得 DDE 无论面对怎样变化的场景,效果依然可期。
传统 AGC 算法会去掉极值,并将动态范围线性地映射到 8位域上,这对高动态范围的视频效果几乎不起作用。而直方图均衡化提高了主体温度/辐照度范围中的对比度。但如果靶标不在主体范围内呢?DDE 增强了预先设定可用对比度的细节。因此,即使是低对比度对象,也可以获得探测率 恒定的图像效果。
实例验证:
我们对大多数安全和探测应用程序的要求是快速探测到目标,无需操作员手动调整。减少探测目标的时间意味着同时显示所有可能的目标 - 甚至是低对比度目标,而无需手动调整增益级别。
图8所示的视频序列是采用 640x480像素的高分辨率探测器捕获到的,显示的是高对比度场景。图 9 中的增益*和级别**经过手动调整后,才找到对比度特别低的十字准星。摄像机能够解析到目标,但是除非用户准确知道目标处于什么信号区间,否则也无法看到。这就大大增加了探测所需的时间。图 10 显示应用了 DDE 技术的图像。
DDE 和许多其它细节增强方法不同它对于环境变化的适应能力极强。在实践当中,通过 DDE 技术,摄像机可在各种情况下,呈现出近乎完美的视频,用户可以更专注于图像,而不是如何控制。
四、工程设计
1、顶层模块
module dmk_top(
input clk,
input rst_n, //KEY4(o) 即OK按键作为复位
//uart
input uart_rxd, //uart 串行数据接收
output uart_txd, //uart 串行数据发送
//key
input key_left, //预留的算法开关,默认关闭,按一下开、按一下关...
input key_right, //预留的算法开关,默认关闭,按一下开、按一下关...
input key_up, //预留的算法开关,默认关闭,按一下开、按一下关...
input key_down, //预留的算法开关,默认打开,按一下关、按一下开...
//LCD ILI9488 Port
output WR,
output RD,
output CS,
output RS,
output BL_cnt,
output [15:0] data,
output RESET,
//LED
output [3:0] led,
//SDRAM Port
output sdram_clk, //sdram clock
output sdram_cke, //sdram clock enable
output sdram_cs_n, //sdram chip select
output sdram_we_n, //sdram write enable
output sdram_cas_n, //sdram column address strobe
output sdram_ras_n, //sdram row address strobe
output[1:0] sdram_dqm, //sdram data enable
output[1:0] sdram_ba, //sdram bank address
output[12:0] sdram_addr, //sdram address
inout[15:0] sdram_dq //sdram data
);
//reset
wire sys_rst_n ;
//uart
wire rx_fifo_empty_w;
wire rx_fifo_rd_w ;
wire [7:0] rx_fifo_q_w ;
//byte_stream_decode
wire data_valid_w ;
wire [15:0] data_w ;//RGB565格式
//PLL
wire pll_lock_w ;
wire clk_ref ;
wire clk_refout ;
//SDRAM
wire [15:0] data_w0 ;
wire valid_w0,ready_w0,sop_w0,eop_w0;
wire sdram_init_done;
//sdram read & write
wire [15:0] wr1_data ;
wire wr1_wrreq ;
wire wr_full_1 /*synthesis keep*/;
wire wr_full_2 ;
wire rd_empty_1 ;
wire rd_empty_2/*synthesis keep*/;
wire rd1_rdreq ;
wire [15:0] rd1_data ;
//TFTLCD
wire lcd_init_done ;
wire clk_lcd_w ;
assign sdram_dqm = 2'b00 ;
assign wr1_data = data_w ;
assign wr1_wrreq = data_valid_w;
//PLL模块
sys_pll u_sys_pll_0(
.areset (!rst_n ),
.inclk0 (clk ),
.c0 (clk_ref ),//sdram控制器100M 工作时钟
.c1 (clk_refout),//SDRAM芯片 100M 时钟
//.c2 (cmos_xclk ),//CMOS XCLK 24M 时钟
.c3 (clk_lcd_w ),//LCD驱动模块工作时钟,12.5MHz
.locked (pll_lock_w)
);
//延迟复位模块
delay_reset u_delay_reset_0(
.clk (clk ),
.rst_n (rst_n && pll_lock_w && lcd_init_done),
.reset_n (sys_rst_n )
);
//按键控制模块
wire [3:0] pulse;
wire [3:0] sw;
key_pulse u_key_pulse(
.clk(clk),
.rst_n(sys_rst_n),
.key({key_down,key_right,key_up,key_left}),
.pulse(pulse)
);
pulse_cnt u_pulse_cnt0(
.clk(clk),
.rst_n(sys_rst_n),
.pulse(pulse[0]),
.ocnt(sw[0])
);
pulse_cnt u_pulse_cnt1(
.clk(clk),
.rst_n(sys_rst_n),
.pulse(pulse[1]),
.ocnt(sw[1])
);
pulse_cnt u_pulse_cnt2(
.clk(clk),
.rst_n(sys_rst_n),
.pulse(pulse[2]),
.ocnt(sw[2])
);
pulse_cnt u_pulse_cnt3(
.clk(clk),
.rst_n(sys_rst_n),
.pulse(pulse[3]),
.ocnt(sw[3])
);
//FIFO接口封装的串口uart发送/接收模块,只用接收功能
fifo_port_uart u_fifo_port_uart_0(
.clk50M (clk ),
.rst_n (sys_rst_n ),
.data (8'd0 ),
.write (1'b0 ),
.wr_full ( ),
.rd_empty(rx_fifo_empty_w),
.read (rx_fifo_rd_w ),
.q (rx_fifo_q_w ),
.uart_rxd(uart_rxd ),
.uart_txd(uart_txd )
);
//字节数据流解码模块,为节省SDRAM存储带宽,从RGB888转到RGB565
byte_stream_decode u_byte_stream_decode_0(
.clk (clk ),
.rst_n (sys_rst_n ),
.fifo_empty(rx_fifo_empty_w),
.fifo_q (rx_fifo_q_w ),
.fifo_rd (rx_fifo_rd_w ),
.data_valid(data_valid_w ),
.data (data_w )
);
//4端口SDRAM控制器模块
Sdram_Control_4Port Sdram_Control_4Port(
.REF_CLK (clk_ref ),
.OUT_CLK (clk_refout),
.RESET_N (sys_rst_n ), //复位输入,低电平复位
.WR1_DATA (wr1_data ), //写入端口1的数据输入端,16bit
.WR1 (wr1_wrreq ), //写入端口1的写使能端,高电平写入
.WR1_ADDR (0 ), //写入端口1的写起始地址
.WR1_MAX_ADDR (480*320 ), //写入端口1的写入最大地址
.WR1_LENGTH (256 ), //一次性写入数据长度
.WR1_LOAD (~sys_rst_n), //写入端口1清零请求,高电平清零写入地址和fifo
.WR1_CLK (clk ), //写入端口1 fifo写入时钟
.WR1_FULL (wr_full_1 ), //写入端口1 fifo写满信号
.WR1_USE ( ), //写入端口1 fifo已经写入的数据长度
.WR2_DATA ('d0 ), //写入端口2的数据输入端,16bit
.WR2 (1'b0 ), //写入端口2的写使能端,高电平写入
.WR2_ADDR (480*320 ), //写入端口2的写起始地址
.WR2_MAX_ADDR (480*320*2 ), //写入端口2的写入最大地址
.WR2_LENGTH (256 ), //一次性写入数据长度
.WR2_LOAD (~sys_rst_n ), //写入端口2清零请求,高电平清零写入地址和fifo
.WR2_CLK (clk ), //写入端口2 fifo写入时钟
.WR2_FULL (wr_full_2 ), //写入端口2 fifo写满信号
.WR2_USE ( ), //写入端口2 fifo已经写入的数据长度
.RD1_DATA (rd1_data ), //读出端口1的数据输出端,16bit
.RD1 (rd1_rdreq ), //读出端口1的读使能端,高电平读出
.RD1_ADDR (0 ), //读出端口1的读起始地址
.RD1_MAX_ADDR (480*320 ), //读出端口1的读出最大地址
.RD1_LENGTH (256 ), //一次性读出数据长度
.RD1_LOAD (~sys_rst_n ), //读出端口1 清零请求,高电平清零读出地址和fifo
.RD1_CLK (clk ), //读出端口1 fifo读取时钟
.RD1_EMPTY (rd_empty_1 ), //读出端口1 fifo读空信号
.RD1_USE ( ), //读出端口1 fifo已经还可以读取的数据长度
.RD2_DATA ( ), //读出端口2的数据输出端,16bit
.RD2 (0 ), //读出端口2的读使能端,高电平读出
.RD2_ADDR (480*320 ), //读出端口2的读起始地址
.RD2_MAX_ADDR (480*320*2 ), //读出端口2的读出最大地址
.RD2_LENGTH (256 ), //一次性读出数据长度
.RD2_LOAD (~sys_rst_n ), //读出端口2清零请求,高电平清零读出地址和fifo
.RD2_CLK (clk ), //读出端口2 fifo读取时钟
.RD2_EMPTY (rd_empty_2 ), //读出端口2 fifo读空信号
.RD2_USE ( ), //读出端口2 fifo已经还可以读取的数据长度
.SA (sdram_addr ), //SDRAM 地址线,
.BA (sdram_ba ), //SDRAM bank地址线
.CS_N (sdram_cs_n ), //SDRAM 片选信号
.CKE (sdram_cke ), //SDRAM 时钟使能
.RAS_N (sdram_ras_n), //SDRAM 行选中信号
.CAS_N (sdram_cas_n), //SDRAM 列选中信号
.WE_N (sdram_we_n ), //SDRAM 写请求信号
.DQ (sdram_dq ), //SDRAM 双向数据总线
.SDR_CLK (sdram_clk ),
.DQM ( ), //SDRAM 数据总线高低字节屏蔽信号
.Sdram_Init_Done(sdram_init_done)
);
//FIFO接口转Avalo-ST 裸流
fifo2st#(
.DWIDTH (16 ),
.TOTAL_PIXELS(480*320)
)u_fifo2st_0
( .clk (clk ), //本模块将FIFO数据转化成非标Avalon-ST视频流,即不带控制包头,并且无视频数据包头,sop对应一帧的第一个有效数据,eop对应一帧的最后一个有效数据
.rst_n (sys_rst_n ),
.fifo_aclr (~sys_rst_n), //外部模块需要根据视频流特征生成扇出FIFO的异步aclr信号
.fifo_empty (rd_empty_1), //FIFO空标志信号
.fifo_q (rd1_data ),
.fifo_rd (rd1_rdreq ),
.one_frame_done(),
.source_sop (sop_w0 ),
.source_valid (valid_w0 ),
.source_data (data_w0 ),
.source_eop (eop_w0 ),
.source_ready (ready_w0 )
);
//灰度化算法模块
wire [7:0] data_w1;
wire valid_w1,ready_w1,sop_w1,eop_w1;
vip_rgb2gray_top u_vip_rgb2gray_top_0
(
.clk (clk ),
.rst_n (sys_rst_n),
//sink
.sink_sop (sop_w0 ),
.sink_eop (eop_w0 ),
.sink_valid (valid_w0 ),
.sink_data ({data_w0[15:11],3'b111,data_w0[10:5],2'b11,data_w0[4:0],3'b111}),
.sink_ready (ready_w0 ),
//source
.source_sop (sop_w1 ),
.source_eop (eop_w1 ),
.source_valid(valid_w1 ),
.source_data (data_w1 ),
.source_ready(ready_w1 )
);
//5x5高斯滤波细节增强算法模块
wire [7:0] data_w2;
wire valid_w2,ready_w2,sop_w2,eop_w2;
vip_2d_fir_proc_gauss_enhance#(
.DWIDTH(8) ,
.WIDTH(480) ,
.HEIGHT(320)
)u_vip_2d_fir_proc_gauss_enhance_0
(
.clk(clk),
.rst_n(sys_rst_n),
.proc_en(sw[2]),
//sink
.sink_sop(sop_w1),
.sink_valid(valid_w1),
.sink_data(data_w1),//原始数据
.sink_eop(eop_w1),
.sink_ready(ready_w1),
//source
.source_sop(sop_w2),
.source_valid(valid_w2),
.source_data(data_w2),//细节增强的结果
.source_eop(eop_w2),
.source_ready(ready_w2)
);
//TFTLCD模块
vip_ILI9488 u_vip_ILI9488_0(
.clk (clk ),
.clk12p5M (clk_lcd_w ),
.rst_n (rst_n ),
//Avalon-ST Sink
.sink_sop (sop_w2 ),
.sink_valid (valid_w2 ),
.sink_data ({data_w2[7:3],data_w2[7:2],data_w2[7:3]}),
.sink_eop (eop_w2 ),
.sink_ready (ready_w2 ),
.lcd_intdone (lcd_init_done),
//TFTLCD interface
.WR (WR ),
.RD (RD ),
.CS (CS ),
.RS (RS ),
.BL_cnt (BL_cnt ),
.data (data ),
.RESET (RESET )
);
endmodule
2、高斯滤波细节增强算法模块
//功能:高斯滤波细节增强算法顶层模块
//Avalon-ST裸流协议,数据传输通过ready流控,ready潜伏期为1
`include "vip_2d_fir_define.h"
module vip_2d_fir_proc_gauss_enhance#(
parameter DWIDTH = 8,
parameter WIDTH = 480,
parameter HEIGHT = 272
)
(
input clk,
input rst_n,
input proc_en,
input sink_sop,
input sink_valid,
input [DWIDTH-1:0] sink_data,
input sink_eop,
output sink_ready,
output source_sop,
output source_valid,
output [DWIDTH:0] source_data,
output source_eop,
input source_ready
);
wire sop_w0,eop_w0,valid_w0,ready_w0;
wire [DWIDTH-1:0] data_w0;
wire sop_w1,eop_w1,valid_w1,ready_w1;
wire [DWIDTH:0] data_w1;
assign ready_w0 = ready_w1;
vip_border_expand#(
.DWIDTH(DWIDTH),
.WIDTH(WIDTH),
`ifdef WIN_3x3
.HEIGHT(HEIGHT),
.TOP_OFFSET(2),
.BOTTOM_OFFSET(2),
.LEFT_OFFSET(1),
.RIGHT_OFFSET(1)
`endif
`ifdef WIN_5x5
.HEIGHT(HEIGHT),
.TOP_OFFSET(3),
.BOTTOM_OFFSET(3),
.LEFT_OFFSET(2),
.RIGHT_OFFSET(2)
`endif
)u_vip_border_expand_0
(
.clk(clk),
.rst_n(rst_n),
.sink_sop(sink_sop),
.sink_valid(sink_valid),
.sink_data(sink_data),
.sink_eop(sink_eop),
.sink_ready(sink_ready),
.source_sop(sop_w0),
.source_valid(valid_w0),
.source_data(data_w0),
.source_eop(eop_w0),
.source_ready(ready_w0)
);
vip_gauss_proc_enhance#(
`ifdef WIN_3x3
.WIDTH(WIDTH+2),
.HEIGHT(HEIGHT+4),
`endif
`ifdef WIN_5x5
.WIDTH(WIDTH+4),
.HEIGHT(HEIGHT+6),
`endif
.DWIDTH(DWIDTH)
)
u_vip_gauss_proc_enhance_0
(
.clk(clk),
.rst_n(rst_n),
.proc_en(proc_en),
.sink_sop(sop_w0),
.sink_valid(valid_w0),
.sink_data(data_w0),
.sink_eop(eop_w0),
.source_sop(sop_w1),
.source_valid(valid_w1),
.source_data(data_w1),//对于系数为负数的情况,可能存在负值结果,因此source_data扩展1位符号位(二进制补码形式的有符号数)
.source_eop(eop_w1)
);
vip_2d_fir_clipper_gauss
#(
`ifdef WIN_3x3
.INPUT_WIDTH (WIDTH+2),
.INPUT_HEIGHT (HEIGHT+4) ,
.TOP_OFFSET (4),
.BOTTOM_OFFSET(0),
.LEFT_OFFSET (1),
.RIGHT_OFFSET (1),
`endif
`ifdef WIN_5x5
.INPUT_WIDTH (WIDTH+4),
.INPUT_HEIGHT (HEIGHT+6) ,
.TOP_OFFSET (6),
.BOTTOM_OFFSET(0),
.LEFT_OFFSET (3),
.RIGHT_OFFSET (1),
`endif
.DWIDTH (DWIDTH+1)
)u_vip_2d_fir_clipper_gauss_0
(
.clk(clk),
.rst_n(rst_n),
.sink_sop(sop_w1),
.sink_eop(eop_w1),
.sink_valid(valid_w1),
.sink_data(data_w1),
.sink_ready(ready_w1),
.source_sop(source_sop),
.source_eop(source_eop),
.source_valid(source_valid),
.source_data(source_data),
.source_ready(source_ready)
);
endmodule
五、板级验证
原图:
加载进串口:
FPGA加载到屏幕:
DDE图像细节增强:
六、参考文献
TN_0003_CN号文档《数字图像细节增强 (DDE)》;
七、工程获取
1、直接点击链接下载:
FPGA:DDE图像数字细节增强系统(工程+仿真+实物方案,可用毕设)
https://download.csdn.net/download/weixin_46423500/89274412
2、私信半价获取
请标明来意(需要套件或者需要工程资料)。
邮箱:bumianzhe@126.com