×

FPGA相机边缘检测开源分享

消耗积分:0 | 格式:zip | 大小:0.03 MB | 2023-07-10

李超

分享资料个

描述

该项目将展示如何实施完整的端到端图像处理管道,以实施能够以 30fps 的速度运行的边缘检测视觉算法,以检测道路车道。

 
 
poYBAGOlJs2AGtfUAAlW6-j4RJ8752.jpg
 
1 / 3
 

一、简介

要实现实时车道检测,我们的想法是拍摄图像并将其传递给边缘检测器 IP。最著名的一种是 Sobel 边缘检测器。然而,Canny 边缘检测器具有可编程阈值的优势。即时修改阈值可以适应不同的光照条件。

https://en.wikipedia.org/wiki/Canny_edge_detector

视觉处理流水线在逻辑上可以分为三个部分。图像源、图像处理管道和图像接收器。

 

 
pYYBAGOlJtCAbjB9AAD4wV-AWo4500.png
 

 

从高层次的角度来看,需要一个图像源(可以是相机传感器或图像模式生成器)和一个接收器(可以是显示器)。

2.硬件块

首先,必须将所有原型硬件部件放在一起。在这种情况下,系统硬件仅由五个组件组成。

a) MiniZed ZYNQ 开发板

b) OV7670 相机

c) Arduino 屏蔽穿孔板

d) 10pin 0.1inch 母头

e) VGA PMOD

OV7670 相机安装在一个 10 针 0.1 英寸母接头上,该接头本身焊接到原型板上。VGA PMOD 连接到 MiniZed 双 PMOD 接头连接器。最后一步是将 VGA 电缆连接到显示器。

 
poYBAGOlJwOADJ6lAA_Wa_4cbas252.jpg
 

一旦硬件系统组装在一起,下一步就是使用 Vivado 在 FPGA (PL) 上设计硬件。

3.FPGA硬件

1. 图片来源

有许多相机接口,但最简单的是使用并行总线的 CMOS 传感器,例如古老的 OV7670 相机传感器。然而,要使用这样的传感器,必须实现一个相机捕获模块,该模块将从传感器传输的字节流转换为适当的协议,在我们的例子中是 AXIS 总线。

此外,必须通过在 PL 中将配置模块实现为从 BRAM 读取配置的 I2C 模块或在 PS 中将配置模块实现为通用相机驱动程序来配置相机。PS 配置显然更灵活,所以我选择了这个选项。

 

 
pYYBAGOlJwWAV6N1AABtVQ3HR8I090.png
 

 

Vivado 设计中的图像源是与 OV7690 CMOS 相机接口的定制 IP。相机使用并行八位接口。此外,还有两个同步信号HSYNC和VSYNC,其选通模式分别表示行和帧。相机使用输出像素时钟 PCLK。像素速率与 PCLK 同步。OV7690 相机还需要一个 24 MHz (XCLK) 相机时钟作为输入。

OV7690 每个像素输出 2 个字节。根据配置代码,相机可以配置为使用不同的色彩空间。IP 配置为使用 RGB565 色彩空间。相机的控制是通过 AXI 接口完成的。摄像机 IP 在内存中分配了一个位置。要启动相机,必须设置位以启用相机输出数据。数据本身使用 AXIS (AXI Stream) 协议打包。

2. 跨时钟域

来自相机的 AXIS 输出被发送到异步 FIFO。这是需要跨越两个不同时钟域时使用的一种方法。

 

FIFO 的从端连接到 PCLK,而 FIFO 的主端连接到工作频率为 50MHz 的 AXI 主时钟域。主时钟必须始终高于从时钟,否则 FIFO 将溢出导致丢失像素。

 
poYBAGOlJwiADZy8AABqNm4NDQw336.png
 

 

在像素流穿过时钟域后,它通过子集转换器发送。这是一个 AXIS IP 块,可将 RGB565 数据重新映射为 24 位数据包。图像处理 IP 使用 24 位,因为每种颜色都分配了 8 位。

下一个元素是 AXIS 开关。该 IP 作为 AXIS 流的简单多路复用器运行。它可以通过 AXI lite 接口或利用 AXIS 频闪信号自动配置。在此特定应用中,启用了 AXIS 开关的 AXI Lite 接口。然而,这需要实施 SDK 驱动程序来配置开关多路复用器。

3.图像过滤

Canny 边缘检测器 IP 是使用 Vivado HLS 构建的。它是用 C 语言编写的。HLS 的主要优点是允许快速部署 IP。缺点是生成的逻辑是模糊的,通常不如手工制作的 Verilog 代码高效,尽管它可以接近它。在任何情况下,生成 Canny IP 都涉及使用 Vivado HLS 2013 和 2018.3 版本的 xfopencv。这是一组 C++ 库,复制了用于 FPGA 逻辑的众所周知的 OpenCV 库。

Xilinx 最近发布了 Vivado 2019.1 以及 HLS 2019.1。这需要使用带有非免费修订框架的 SDSOC。

已实施的修订代码的主要更改是修改功能以支持 AXIS 协议的输入和输出。此外,AXI Lite 总线用于将所有可编程变量捆绑在一个集合中,并通过连接到 GP0 ZYNQ 总线的 AXI 互连将它们暴露给 PS 内存映射。

 

 
pYYBAGOlJwyAL8qtAACR9fV-h2E900.png
 

 

最后,为了在两个不同的 IP 之间切换,使用了 AXI 开关。

 

 
poYBAGOlJw-ABlnqAAEZq1YHXhE666.png
VDMA 配置为写入模式。
 

 

VDMA 配置为三重缓冲模式。VDMA 需要在运行前通过 AXI lite 接口进行配置,该接口将其连接到主 AXI 互连。

下面的代码显示了针对 VGA 分辨率的 VDMA 配置。

	Xil_DCacheFlush();    
	Xil_ICacheInvalidate();    
	/* Start of VDMA Configuration */    
   Xil_Out32(XPAR_VDMA_S2MM_BASEADDR + 0x30, 0x8B);    
   Xil_Out32(XPAR_VDMA_S2MM_BASEADDR + 0xAC, 0x10000000);    
   Xil_Out32(XPAR_VDMA_S2MM_BASEADDR + 0xB0, 0x100F0000);    
   Xil_Out32(XPAR_VDMA_S2MM_BASEADDR + 0xB4, 0x101E0000);    
   Xil_Out32(XPAR_VDMA_S2MM_BASEADDR + 0xA8, WIDTH*2);    
   Xil_Out32(XPAR_VDMA_S2MM_BASEADDR + 0xA4, WIDTH*2);    
   Xil_Out32(XPAR_VDMA_S2MM_BASEADDR + 0xA0, HEIGHT);    
   Xil_Out32(XPAR_VDMA_S2MM_BASEADDR + 0x00, 0x8B);    
   Xil_Out32(XPAR_VDMA_S2MM_BASEADDR + 0x5C, 0x10000000);    
   Xil_Out32(XPAR_VDMA_S2MM_BASEADDR + 0x60, 0x100F0000);    
   Xil_Out32(XPAR_VDMA_S2MM_BASEADDR + 0x64, 0x101E0000);    
   Xil_Out32(XPAR_VDMA_S2MM_BASEADDR + 0x58, WIDTH*2);    
   Xil_Out32(XPAR_VDMA_S2MM_BASEADDR + 0x54, WIDTH*2);    
   Xil_Out32(XPAR_VDMA_S2MM_BASEADDR + 0x50, HEIGHT);     

。。

VTC 本身配置为 VGA 分辨率。

 

 
poYBAGOlJxGAfEoiAAEA6uPNZ2Q084.png
 

 

这基本上是一个 R2RDAC,它采用数字数据总线以及行和帧同步信号 HSYNCS 和 VSYNC,并将数据发送到监视器。

正如您在下面看到的,应用程序在正常 VGA 模式、灰色模式(看起来更像蓝色)和边缘检测模式之间不断切换。

 

 
 
 
 
pYYBAGOlJz6AVIIzAAo3uh_mjPk252.jpg
 
1 / 3
 

概括

该项目展示了如何在 ZYNQ FPGA SoC 上实现完整的端到端视觉处理流水线。硬件能够以 30 fps 的速度进行实时边缘检测。

其他想法包括:

  • 添加更多滤波器类型,如中值、高斯、索贝尔、普维特等。
  • 为 MiniZed 构建一个 PYNQ 发行版以从 Linux 访问 IP,以构建一个无线 WiFi 摄像头。
  • 升级到更高清晰度的相机传感器。

 

 


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

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.lene-v.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'FPGA相机边缘检测开源分享',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.lene-v.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);