目录
- 一、sobel边沿检测原理
- 二、HLS方案实现sobel边沿检测
- 三、HLS在线仿真并导出IP
- 四、Kintex7开发板vivado工程验证
- 五、zynq7100开发板vivado工程验证
- 六、板级调试验证
- 七、福利:工程源码获取
一、sobel边沿检测原理
所谓边缘是指其周围像素灰度急剧变化的那些象素的集合,它是图像最基本的特征。边缘存在于目标、背景和区域之间,所以,它是图像分割所依赖的最重要的依据。由于边缘是位置的标志,对灰度的变化不敏感,因此,边缘也是图像匹配的重要的特征。
边缘检测和区域划分是图像分割的两种不同的方法,二者具有相互补充的特点。在边缘检测中,是提取图像中不连续部分的特征,根据闭合的边缘确定区域。而在区域划分中,是把图像分割成特征相同的区域,区域之间的边界就是边缘。由于边缘检测方法不需要将图像逐个像素地分割,因此更适合大图像的分割。边缘大致可以分为两种,一种是阶跃状边缘,边缘两边像素的灰度值明显不同;另一种为屋顶状边缘,边缘处于灰度值由小到大再到小的变化转折点处。边缘检测的主要工具是边缘检测模板。边缘检测的有很多,典型的有索贝尔算子、普里维特算子、罗伯茨交叉边缘检测等边缘检测技术,在设计中采用的是索贝尔算子。
索贝尔算子(Sobel operator)主要用作边缘检测,在技术上,它是一离散性差分算子,用来运算图像亮度函数的灰度之近似值。在图像的任何一点使用此算子,将会产生对应的灰度矢量或是其法矢量。Sobel 卷积因子为:
该算子包含两组 3x3 的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向
的亮度差分近似值。如果以 A 代表原始图像,Gx 及 Gy 分别代表经横向及纵向边缘检测的图像灰度值,
其公式如下:
图像的每一个像素的横向及纵向灰度值通过以下公式结合,来计算该点灰度的大小:
通常,为了提高效率 使用不开平方的近似值,但这样做会损失精度,迫不得已的时候可以如下这样子:
如果梯度 G 大于某一阀值,则认为该点(x,y)为边缘点。
二、HLS方案实现sobel边沿检测
在前面的sobel边沿检测原理中详细介绍了sobel边沿检测的算法公式,看起来很复杂很NB对吧?
然并卵!!!!!!!
然并卵!!!!!!!
然并卵!!!!!!!
因为对于HLS来说,干这活儿只需要一句话一行代码即可实现;
因为Xilinx早就帮你做好了sobel边沿检测的库,并且可以综合,既然如此,我还需要去管他怎么实现的,算法公式是怎样的吗?这就是HLS的NB之处。。。
HLS工程如下:
综合后的延时、资源占用等性能参数如下:
头文件如下:
#ifndef _HELAI_HLS_SOBEL_H
#define _HELAI_HLS_SOBEL_H
#include "hls_video.h"
#define MAX_HEIGHT 1080 //图像最大高度
#define MAX_WIDTH 1920 //图像最大宽度
#define INPUT_IMAGE "luoli.jpg"
#define OUTPUT_IMAGE "luoli_hls.jpg"
typedef hls::stream<ap_axiu<24,1,1,1> > AXI_STREAM;
typedef hls::Mat<MAX_HEIGHT,MAX_WIDTH,HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<MAX_HEIGHT,MAX_WIDTH,HLS_8UC1> GRAY_IMAGE;
void helai_hls_sobel(AXI_STREAM&INPUT_STREAM,AXI_STREAM&OUTPUT_STREAM,int rows,int cols);
#endif
源文件的核心代码如下:
hls::Sobel<1,0,3>(img_1,img_2); //将灰度数据与Sobel算子卷积
核心代码就一句话,BN吧?呵呵。。。。。。
三、HLS在线仿真并导出IP
仿真源文件如下:
#include "helai_hls_sobel.h"
#include "hls_opencv.h"
int main(void)
{
//获取图像数据
IplImage* src = cvLoadImage(INPUT_IMAGE);
IplImage* dst = cvCreateImage(cvGetSize(src),src->depth,src->nChannels);
//使用HLS库进行处理
AXI_STREAM src_axi,dst_axi;
IplImage2AXIvideo(src,src_axi);
helai_hls_sobel(src_axi,dst_axi,src->height,src->width);
AXIvideo2IplImage(dst_axi,dst);
//保存图像
cvSaveImage(OUTPUT_IMAGE,dst);
//显示图像
cvShowImage(INPUT_IMAGE,src);
cvShowImage(OUTPUT_IMAGE,dst);
//等待用户按下键盘上的任一按键
cv::waitKey(0);
}
话不多说直接看HLS仿真结果:
仿真完整成功后即可综合再导出IP:
四、Kintex7开发板vivado工程验证
开发板:Xilinx Kintex7开发板;
开发环境:HLS2019.1;vivado2019.1;
输入:OV5640摄像头,输入分辨率1280x720;
输出:HDMI,输出分辨率1920x1080;
工程BD如下:
生成顶层RTL如下:
SDK主函数源码如下:
#include <stdio.h>
#include "xgpio.h"
#include "oak_iic.h"
#include "unistd.h"
#include "helai_vdma.h"
#include "helai_color_back.h"
#include "helai_hls_sobel.h"
XGpio_Config *XGpioCfg;
XGpio led_gpio;
#define AXI_GPIO_DEVICE_ID XPAR_GPIO_0_DEVICE_ID
int main(){
XGpioCfg = XGpio_LookupConfig(AXI_GPIO_DEVICE_ID);
XGpio_CfgInitialize(&led_gpio, XGpioCfg, XGpioCfg->BaseAddress);
XGpio_SetDataDirection(&led_gpio, 1, 0); //output
XGpio_DiscreteWrite(&led_gpio, 1, 0);
oak_i2c_init(OV5640_IIC_BASEADDR, 1000000, 0x78>>1, IIC_REG_LEN16, IIC_DATA_LEN8);
OV5640_Init(OV5640_IIC_BASEADDR,1280,720);
helai_hls_sobel(720,1280);
helai_vdma();
while(1){
usleep(500000);
XGpio_DiscreteWrite(&led_gpio, 1, 1);
usleep(500000);
XGpio_DiscreteWrite(&led_gpio, 1, 0);
}
}
五、zynq7100开发板vivado工程验证
开发板:Xilinx zynq7100开发板;
开发环境:HLS2019.1;vivado2019.1;
输入:OV5640摄像头,输入分辨率1280x720;
输出:HDMI,输出分辨率1920x1080;
工程BD如下:
生成顶层RTL如下:
SDK主函数源码如下:
#include "I2C_16bit.h"
#include "xiicps.h"
#include "xil_io.h"
#include "xparameters.h"
#include "helai_vdma.h"
#include "helai_hls_sobel.h"
void main()
{
// Initialize OV5640 regesiter
I2C_config_init();
helai_hls_sobel(720,1280);
helai_vdma();
while (1) ;
}
六、板级调试验证
K7开发板和zynq开发板实物连接如下:图中K7为连接状态
运行结果静态展示:
下载程序后运行结果如下:以K7开发板为例,持续运行48小时无问题
HLS图像处理sobel
七、福利:工程源码获取
福利:工程代码的获取
代码太大,无法邮箱发送,以某度网盘链接方式发送,
资料如下:获取方式:私。
K7开发板网盘资料如下:
zynq开发板网盘资料如下: