伪红外图像处理
副标题:优秀的ic/fpga开源项目(七)-伪红外图像处理
《优秀的ic/fpga开源项目》是新开的系列,旨在介绍单一项目,会比《优秀的 verilog/fpga开源项目》内容介绍更加详细,包括但不限于综合、上板测试等。两者相辅相成,互补互充~
演示伪红外图像处理。
介绍
红外摄像机因为对可见光不敏感,所以在一些特殊行业应用越来越广泛。
红外摄像机甚至可以透过太阳镜看到人眼,并且摄像机图像不受白天或夜晚的影响,并且几乎没有环境光。
因为真正的红外sensor价格比较昂贵,所以这次选用一种伪红外sensor,即利用相机自己的光源,即安装在镜头旁边的 led,反射红外光后进项图像采集,这是一种利用近红外成像,和我们熟知的红外摄像头还是有区别的。
该项目展示了一些红外图像处理算法,这些算法可以提高图像质量。
所选fpga是 zynq-020 soc,摄像头是便宜的 raspberry pi 摄像头,带有两个红外 led,最大分辨率为 1080p@60hz。
该项目中呈现的体系结构是可扩展的,可以轻松添加更多算法。
理论
我选择了五种基于 3x3 内核的图像处理算法:
坏点校正
这是所有这类传感器的普遍问题,是一种常见的预处理算法。
中值滤波器
常见的噪声平滑预处理算法。
低通滤波器(平滑滤波器)
噪声平滑,这个算法使图像平滑,不会像中值滤波器那样使图像模糊。
图像锐化
通过“边缘锐化”提高图像质量,即强调边缘。
边缘检测
应用其中一种算法后,对图像边缘处理后,图像尺寸会减小(可选)。
架构
所有算法都基于 3x3 内核,这就是为什么所有算法内核 (pe) 都必须与 fifo 通信,每个 pe 都有一行的延迟。只有当第二行数据到达时才开始应用算法内核,考虑到图像处理时候会对边界有影响,但是我们需要输出端输出相同的图像大小。
架构
选择模块是一个可扩展的 mux 网络,在上图情况下,具有五个图像处理算法,由六个级联的 mux-es 组成,一个用于滤波器输出,一个用于输入信号。数据流可以配置,在这种情况下,视频流从输入到输出,它通过的图像处理元素的顺序和数量是可配置的。
算法内核的结构如下所示,基本上在这种情况下是一个延迟线,它以视频流作为输入并输出一个 3x3 矩阵,输出是处理后的帧。
设计
在该架构中,我在 vdma 和 gamma correction 模块之间插入了我的模块。
我为每个行缓冲区添加了一个 fifo。
接口
所有模块都使用规定好的帧接口 (fi),它与参考设计中使用的 axi stream 接口非常相似(https://reference.digilentinc.com/learn/programmable-logic/tutorials/zybo-z7-pcam-5c-demo/start),可以在两者之间进行转换。从 axi stream 到 frame 不需要转换,反之则必须生成一些额外的信号。axi stream 接口只有帧开始和行结束控制信号。
module axi_stream2frame#( parameter data_width = 24)( input clk , // syste clock input rst_n , // asynchronous reset active low//------------------------- configuration interface ---------------------------------- input [11:0] cfg_img_w , // image width input [11:0] cfg_img_h , // image width//------------------------- axi-stream interface ------------------------------------- input m_axi_stream_tuser , // start of frame input m_axi_stream_tvalid , // slave has valid data input m_axi_stream_tlast , // end of frame input [data_width-1:0] m_axi_stream_tdata , // data transferred output m_axi_stream_tready , // master is ready to receive// ------------------------------ frame interface ----------------------------------- output reg s_frm_val , // master has valid data input s_frm_rdy , // slave is ready to receive output reg [data_width-1:0] s_frm_data , // data transferred output reg s_frm_sof , // start of frame output reg s_frm_eof , // end of frame output reg s_frm_sol , // start of line output reg s_frm_eol // end of line);reg [11:0] pix_cnt ;reg [11:0] line_cnt;wire invalrdy;wire outvalrdy;wire set_eof;assign invalrdy = m_axi_stream_tvalid & m_axi_stream_tready;assign outvalrdy = s_frm_rdy & s_frm_val;assign m_axi_stream_tready = s_frm_rdy;assign set_eof = (line_cnt == (cfg_img_h - 1'd1)) & m_axi_stream_tlast & invalrdy; always@(posedge clk or negedge rst_n)if(~rst_n ) pix_cnt <= 11'd0 ; elseif(m_axi_stream_tuser & invalrdy ) pix_cnt <= 11'd0 ; else // reset at start of frameif(m_axi_stream_tlast & invalrdy ) pix_cnt <= 11'd0 ; else // reset at end of frameif(invalrdy ) pix_cnt <= pix_cnt + 1'd1; // increment at each pixelalways@(posedge clk or negedge rst_n)if(~rst_n ) line_cnt <= 11'd0 ; elseif(m_axi_stream_tuser & invalrdy) line_cnt <= 11'd0 ; else // reset at start of frameif(m_axi_stream_tlast & invalrdy) line_cnt <= line_cnt + 1'd1; // increment at each pixelalways@(posedge clk or negedge rst_n)if(~rst_n ) s_frm_sol <= 1'b0; elseif(outvalrdy & s_frm_sol ) s_frm_sol <= 1'b0; else // reset sol is transmittedif(m_axi_stream_tuser & invalrdy ) s_frm_sol <= 1'b1; else // set start of line after last pixel of line is transmittedif(outvalrdy & s_frm_eol & (~s_frm_eof)) s_frm_sol <= 1'b1; // set at start of framealways@(posedge clk or negedge rst_n)if(~rst_n ) s_frm_eof <= 1'b0; elseif(outvalrdy & s_frm_eof) s_frm_eof <= 1'b0; else // reset after eof is transmittedif(set_eof ) s_frm_eof <= 1'b1; // set when last pixel is receivedalways@(posedge clk or negedge rst_n)if(~rst_n ) s_frm_val <= 1'b0; elseif(s_frm_rdy & (~m_axi_stream_tvalid)) s_frm_val <= 1'b0; else // reset when ready and no valid data at the inputif(invalrdy ) s_frm_val <= 1'b1; // set if data is receivedalways@(posedge clk or negedge rst_n)if(~rst_n ) s_frm_eol <= 1'b0; elseif(outvalrdy & s_frm_eol ) s_frm_eol <= 1'b0; else // reset after eol is transmittedif(m_axi_stream_tlast & invalrdy) s_frm_eol <= 1'b1; // set when last pixel in a row is receivedalways@(posedge clk or negedge rst_n)if(~rst_n ) s_frm_sof <= 1'b0; elseif(outvalrdy & s_frm_sof ) s_frm_sof <= 1'b0; else // reset after sof is transmittedif(m_axi_stream_tuser & invalrdy) s_frm_sof <= 1'b1; // set when first pixel is receivedalways@(posedge clk or negedge rst_n)if(~rst_n ) s_frm_data <= {(data_width){1'b0}}; elseif(invalrdy) s_frm_data <= m_axi_stream_tdata ;endmodule //axi_stream2frame
配置sensor
这个摄像头是搭配树莓派使用的,所有驱动都是闭源的,所以没有配置示例。我在 scl 和 sda 引脚上的 i2c 引脚上焊接了两根电线。将相机连接到 raspeberry pi 并将逻辑分析仪连接到焊线,我按照相机接口指南
https://projects.raspberrypi.org/en/projects/getting-started-with-picamera
逻辑分析仪解码了i2c,抓取的值将在最后附上excel。
该配置已添加到 c++ 代码中。
摄像头是 rgb 摄像头,只有在房间黑暗时才会启动红外摄像头。为了解决这个问题,我在sensor前面粘上了一块塑料,这是红外 led 前面的过滤器。这不是一个很好的解决方案,但可以。
配置模块
使用 apb 接口进行配置。
void filter_cfg(){ xil_out32(apb_base_addr + cfg_img_width_addr, img_w); xil_out32(apb_base_addr + cfg_img_height_addr, img_h); xil_out32(apb_base_addr + cfg_pix_corr_sel_addr, 0); xil_out32(apb_base_addr + cfg_sharp_sel_addr, 0); xil_out32(apb_base_addr + cfg_smooth_sel_addr, 0); xil_out32(apb_base_addr + cfg_median_sel_addr, 0); xil_out32(apb_base_addr + cfg_laplace_sel_addr, 0); xil_out32(apb_base_addr + cfg_output_sel_addr, 0); xil_out32(apb_base_addr + cfg_pix_corr_thr_addr, 0); xil_out32(apb_base_addr + cfg_sharp_coef_addr, 0); xil_out32(apb_base_addr + cfg_test_mode_en_addr, 0);}
上面给出的配置是每个选择器模块的选择。现在它被配置为输入流不进行任何处理的情况下转到输出。
void filter_cfg(){ xil_out32(apb_base_addr + cfg_img_width_addr, img_w); xil_out32(apb_base_addr + cfg_img_height_addr, img_h); xil_out32(apb_base_addr + cfg_pix_corr_sel_addr, 0); xil_out32(apb_base_addr + cfg_sharp_sel_addr, 0); xil_out32(apb_base_addr + cfg_smooth_sel_addr, smooth_in_code); xil_out32(apb_base_addr + cfg_median_sel_addr, 0); xil_out32(apb_base_addr + cfg_laplace_sel_addr, smooth_in_code); xil_out32(apb_base_addr + cfg_output_sel_addr, laplace_in_code); xil_out32(apb_base_addr + cfg_pix_corr_thr_addr, 0); xil_out32(apb_base_addr + cfg_sharp_coef_addr, 0); xil_out32(apb_base_addr + cfg_test_mode_en_addr, 0);} xil_out32(apb_base_addr + cfg_smooth_sel_addr, smooth_in_code);
将输入视频流放入算法核心。
演示
我展示了带平滑和不带平滑的拉普拉斯滤波器,我们可以观察到图像有噪声,应用平滑滤波器后图像有所变化。
为了比较原始图像和处理后的两个图像,在 gamma 校正之后添加了第二个 vdma,,现在校正后的图像和原始图像都在 ddr 中,因此可以复制裁剪处理后的图像并将裁剪区域替换为原始图像。
工程链接
i2c 解码的 csv
https://github.com/hszilard13/infa-red-based-image-processing-zybo/blob/master/config_1080p_rgb.csv
整体工程
https://github.com/hszilard13/infared-based-image-processing-zybo
捷杰传感边缘计算网关完美解决信息汇集和传输的4大痛点
周立功:如何兼顾学习ARM与FPGA
Android上的Dark Sky应用程序将启动并运行到8月1日
催醒器的制作方法
USB Android HOST接口控制芯片CH9343概述及特点
优秀的IC/FPGA开源项目:伪红外图像处理
控制器的组成部件
音调可调的胆机电路,Adjustable tone vacuum tube amplifier
digilentchipKIT uC32带有UnoR3接头控制器介绍
车间车间多个CNC设备如何实现网段隔离和跨网段访问
智慧城市系统开发打造全智能化智慧园区综合指挥管控集成平台
单片机中断响应需要具备哪些条件
华为mate40pro参数配置详细评测 从拆解整机到分析器件
一文了解工业EC传感器
知名游戏开发商:VR目前不赚钱,要自己找资金
中国最大的动力镍氢电池制造商春兰集团已恢复上市
英特尔Tiger Lake新处理器的性能跑分已曝光
万众瞩目的三星折叠式手机有新消息,听说加强了
嵌入式主板可在恶劣的工业环境中安全运行
惠普暗影精灵2代Pro, 一款即将发售的主流游戏本