FPGA多路视频叠加融合 HLS算法实现 提供2套工程源码和技术支持

1、前言

视频叠加和融合在FPGA图像处理领域有着广泛应用,但其复杂的内存访问机制和视频叠加透明度的融合,使得实现难度很大,让很多FPGA工程师望而却步,在目前的技术条件下,使用HLS实现视频叠加融合是最简单方便的实现方式,本设计也是基于此实现。

本设计提供2套vivado工程,一套是单路同源视频的缩放叠加,原视频作为底层视频,取原视频的中间部分缩小后作为叠加视频,叠加于底层视频的左上角后输出;另一套是两路非同源视频的缩放叠加,一路视频作为底层视频,取另一路视频的中间部分缩小后作为叠加视频,叠加于底层视频的左上角后输出;代码编译通过后上板调试验证,可直接项目移植,适用于在校学生做毕业设计、研究生项目开发,也适用于在职工程师做项目开发,可应用于医疗、军工等行业的数字成像和图像传输领域;
提供完整的、跑通的工程源码和技术支持;
工程源码和技术支持的获取方式以及上板调试的演示视频放在了文章末尾,请耐心看到最后;

2、视频叠加模块的功能和性能

1:支持多路视频的叠加,前提是FPGA读写内存的带宽得足够宽,本设计使用2路视频叠加;
2:支持叠加视频任意尺寸缩放,因为图像叠加必然是有一个底层视频和叠加层视频,一般情况下叠加层视频尺寸会小于底层视频,而本设计支持叠加视频任意尺寸缩放,通过SDK软件配置;
3:支持任意位置的视频叠加,即叠加层视频在底层视频的叠加位置的坐标是可任意配置的,本设计的叠加位置是在x=0,y=0的位置,即视频左上角,通过SDK软件配置;
4:支持叠加视频透明度的任意配置,即所谓的视频融合,叠加层可以完全覆盖底层视频,也可以完全融合于底层视频,这个是通过透明度参数自由配置的,通过SDK软件配置;
5:通用性较好,zynq系列FPGA均可使用,通过修改HLS工程中的FPGA型号,还可以适配于其他FPGA型号;
6:目前最高支持1920x1080@60Hz的分辨率视频叠加,可以修改HLS工程中视频参数后适应4K分辨率,前提是FPGA的内存读写带宽得足够宽;
7:视频输入输出接口都是AXI4-Sream;

3、HLS视频叠加融合设计

HLS视频叠加融合设计框图如下:
在这里插入图片描述
底层视频输入:
AXI4-Sream视频流输入,本设计由VDMA的输出灌入,作为底层视频;
叠加层视频输入:
AXI4-FULL数据流输入,这里的叠加层视频是HLS视频叠加融合模块主动发起的AXI4读操作,所以需要给出读取视频的首地址和分辨率信息,这些参数是通过AXI4-Lite配置的,即SDK配置;
叠加层视频缩放:
调用HLS库函数实现,可以任意尺寸缩放,转为视频叠加设计,高度贴近真实项目,缩放参数通过AXI4-Lite配置的,即SDK配置;
视频输出:
AXI4-FULL数据流输出,输出是叠加后的视频;
配置参数输入:
AXI4-Lite接口,对接SDK软件;

4、vivado工程1–单路同源视频的缩放叠加

详细设计方案

单路视频的缩放叠加设计框图如下:
在这里插入图片描述
这里只用到1路视频,采用OV5640摄像头,视频通过VDMA缓存3帧到DDR3,取第一帧作为叠加视频叠加到OV5640摄像头视频流,然后输出;
这个工程的目的是验证同源视频的视频叠加,因为底层视频和叠加层视频都来自于同一个视频源,因为输入只有一个OV5640摄像头;
本工程的叠加层视频截取底层视频中心位置的图像缩小,然后叠加到底层视频的左上角;

vivado工程详解

开发板FPGA型号:FPGA–xc7z100ffg900-2;
开发环境:vivado2019.1;
输入:1路OV5640摄像头,分辨率1280x720@60Hz;
输出:HDMI,分辨率1280x720@60Hz;
Bolck Design设计如下:
在这里插入图片描述
综合后的工程代码架构如下:
在这里插入图片描述
综合编译完成后的FPGA资源消耗和功耗预估如下:
在这里插入图片描述

SDK工程详解

SDK工程代码架构如下:
在这里插入图片描述
自定义视频叠加API及其入口参数如下:

//video_width-->原视频宽度
//video_height-->原视频高度
//zoom_x-->提取原视频的x轴起始位置,提取的原视频区域作为叠加的视频
//zoom_y-->提取原视频的y轴起始位置,提取的原视频区域作为叠加的视频
//zoom_w-->叠加的视频宽度
//zoom_h-->叠加的视频高度
//overly_x-->叠加的视频在底层视频中的起始x轴坐标
//overly_y-->叠加的视频在底层视频中的起始y轴坐标
//overly_w-->叠加的视频缩放后的宽度
//overly_h-->叠加的视频缩放后的高度
//overly_alpha-->叠加的视频与底层视频之间的透明度

void helai_mix(u32 video_width,u32 video_height,u32 zoom_x,u32 zoom_y,u32 zoom_w,u32 zoom_h,u32 overly_x,
		       u32 overly_y,u32 overly_w,u32 overly_h,u32 overly_alpha){
	//初始化
	XOverlaystream_Initialize(&overlaystreamInstance, XPAR_OVERLAYSTREAM_0_DEVICE_ID);
	//取视频的地址,这里取VDMA的第一帧地址
	XOverlaystream_Set_pMem(&overlaystreamInstance,VIDEO_BASEADDR0);
	//使能
	XOverlaystream_EnableAutoRestart(&overlaystreamInstance);
	//启动
	XOverlaystream_Start(&overlaystreamInstance);
	//配置参数
	XOverlaystream_Set_cols(&overlaystreamInstance, video_width);
	XOverlaystream_Set_rows(&overlaystreamInstance, video_height);
	XOverlaystream_Set_zoom_x(&overlaystreamInstance, zoom_x);
	XOverlaystream_Set_zoom_y(&overlaystreamInstance, zoom_y);
	XOverlaystream_Set_zoom_w(&overlaystreamInstance, zoom_w);
	XOverlaystream_Set_zoom_h(&overlaystreamInstance, zoom_h);
	XOverlaystream_Set_overly_x(&overlaystreamInstance, overly_x);
	XOverlaystream_Set_overly_y(&overlaystreamInstance, overly_y);
	XOverlaystream_Set_overly_w(&overlaystreamInstance, overly_w);
	XOverlaystream_Set_overly_h(&overlaystreamInstance, overly_h);
	XOverlaystream_Set_overly_alpha(&overlaystreamInstance, overly_alpha);
}

主函数如下:

int main(){
    init_platform();
	I2C_config_init();
	helai_vdma();
	helai_mix(1280,720,1280/2-200,720/2-200,400,400,0,0,1280/2,720/2,40);
	while(1);
    cleanup_platform();
    return 0;
}

5、vivado工程2–两路非同源视频的缩放叠加

详细设计方案

两路视频的缩放叠加设计框图如下:
在这里插入图片描述
这里用到了2路视频,采用OV5640摄像头,摄像头2视频通过VDMA缓存3帧到DDR3,作为底层视频;摄像头1视频通过VDMA缓存3帧到DDR3,作为叠加层视频;两路视频叠加,然后输出;
这个工程的目的是验证非同源视频的视频叠加,因为底层视频和叠加层视频都来自于不同的视频源,因为输入只有2个OV5640摄像头;
本工程的叠加层视频截摄像头1视频中心位置的图像缩小,然后叠加到摄像头2底层视频的左上角;

vivado工程详解

开发板FPGA型号:FPGA–xc7z100ffg900-2;
开发环境:vivado2019.1;
输入:2路OV5640摄像头,分辨率1280x720@60Hz;
输出:HDMI,分辨率1280x720@60Hz;
Bolck Design设计如下:
在这里插入图片描述
底层视频的VDMA配置为读写模式,如下:
在这里插入图片描述
叠加层视频的VDMA配置为只写模式,如下:
在这里插入图片描述
综合后的工程代码架构如下:
在这里插入图片描述
综合编译完成后的FPGA资源消耗和功耗预估如下:
在这里插入图片描述

SDK工程详解

SDK工程代码架构如下:
在这里插入图片描述
自定义视频叠加API及其入口参数如下:

//video_width-->原视频宽度
//video_height-->原视频高度
//zoom_x-->提取原视频的x轴起始位置,提取的原视频区域作为叠加的视频
//zoom_y-->提取原视频的y轴起始位置,提取的原视频区域作为叠加的视频
//zoom_w-->叠加的视频宽度
//zoom_h-->叠加的视频高度
//overly_x-->叠加的视频在底层视频中的起始x轴坐标
//overly_y-->叠加的视频在底层视频中的起始y轴坐标
//overly_w-->叠加的视频缩放后的宽度
//overly_h-->叠加的视频缩放后的高度
//overly_alpha-->叠加的视频与底层视频之间的透明度

void helai_mix(u32 video_width,u32 video_height,u32 zoom_x,u32 zoom_y,u32 zoom_w,u32 zoom_h,u32 overly_x,
		       u32 overly_y,u32 overly_w,u32 overly_h,u32 overly_alpha){
	//初始化
	XOverlaystream_Initialize(&overlaystreamInstance, XPAR_OVERLAYSTREAM_0_DEVICE_ID);
	//取视频的地址,这里取VDMA的第一帧地址
	XOverlaystream_Set_pMem(&overlaystreamInstance,VIDEO1_BASEADDR1);
	//使能
	XOverlaystream_EnableAutoRestart(&overlaystreamInstance);
	//启动
	XOverlaystream_Start(&overlaystreamInstance);
	//配置参数
	XOverlaystream_Set_cols(&overlaystreamInstance, video_width);
	XOverlaystream_Set_rows(&overlaystreamInstance, video_height);
	XOverlaystream_Set_zoom_x(&overlaystreamInstance, zoom_x);
	XOverlaystream_Set_zoom_y(&overlaystreamInstance, zoom_y);
	XOverlaystream_Set_zoom_w(&overlaystreamInstance, zoom_w);
	XOverlaystream_Set_zoom_h(&overlaystreamInstance, zoom_h);
	XOverlaystream_Set_overly_x(&overlaystreamInstance, overly_x);
	XOverlaystream_Set_overly_y(&overlaystreamInstance, overly_y);
	XOverlaystream_Set_overly_w(&overlaystreamInstance, overly_w);
	XOverlaystream_Set_overly_h(&overlaystreamInstance, overly_h);
	XOverlaystream_Set_overly_alpha(&overlaystreamInstance, overly_alpha);
}

主函数如下:

int main()
{
    init_platform();
    I2C_config_init(DeviceId_0);
    I2C_config_init(DeviceId_1);
	helai_vdma();
	helai_mix(1280,720,1280/2-200,720/2-200,400,400,0,0,1280/2,720/2,40);
	while(1);
    cleanup_platform();
    return 0;
}

6、上板调试验证

静态演示如下:
在这里插入图片描述
我用到的开发板FPGA型号为xc7z100ffg900-2;板载一路HDMI输出接口,没有HDMI编码芯片,由FPGA逻辑资源驱动;
工程1,即单路同源视频的缩放叠加的输出视频如下:

1路同源视频叠加


工程2,即2路非同源视频的缩放叠加的输出视频如下:

2路非同源视频叠加

7、福利:工程代码的获取

福利:工程代码的获取
代码太大,无法邮箱发送,以某度网盘链接方式发送,
资料获取方式1:私,或者文章末尾的V名片。
网盘资料如下:
在这里插入图片描述

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Vivado HLS是一个高级综合工具,可以使用C/C++等高级语言编写代码,并将其转换为可综合的硬件描述语言。下面是一个简单的车牌识别算法的示例: 1. 读入图片并进行预处理 ```c++ void plate_recognition(unsigned char *input_image, unsigned char *output_image) { // 读入图片并进行预处理 cv::Mat in_img = cv::imread(input_image); cv::Mat gray_img; cv::cvtColor(in_img, gray_img, CV_BGR2GRAY); cv::Mat blur_img; cv::GaussianBlur(gray_img, blur_img, cv::Size(5, 5), 0); cv::Mat canny_img; cv::Canny(blur_img, canny_img, 50, 150, 3); cv::Mat dilate_img; cv::dilate(canny_img, dilate_img, cv::Mat(), cv::Point(-1, -1), 1, 1, 1); cv::Mat erode_img; cv::erode(dilate_img, erode_img, cv::Mat(), cv::Point(-1, -1), 1, 1, 1); // 将处理后的图片转换为灰度图像 int width = erode_img.cols; int height = erode_img.rows; unsigned char *gray_data = (unsigned char *)malloc(width * height); for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { gray_data[row * width + col] = erode_img.at<unsigned char>(row, col); } } ``` 2. 进行车牌定位 ```c++ // 进行车牌定位 int plate_x = 0; int plate_y = 0; int plate_width = 0; int plate_height = 0; bool found_plate = false; for (int row = 0; row < height - 100; row++) { for (int col = 0; col < width - 200; col++) { int sum = 0; for (int i = 0; i < 100; i++) { for (int j = 0; j < 200; j++) { sum += gray_data[(row + i) * width + col + j]; } } if (sum > 100000 && sum < 150000) { plate_x = col; plate_y = row; plate_width = 200; plate_height = 100; found_plate = true; break; } } if (found_plate) { break; } } ``` 3. 对车牌进行字符分割和识别 ```c++ // 对车牌进行字符分割和识别 if (found_plate) { cv::Mat plate_img = in_img(cv::Rect(plate_x, plate_y, plate_width, plate_height)); cv::Mat gray_plate; cv::cvtColor(plate_img, gray_plate, CV_BGR2GRAY); cv::Mat thresh_plate; cv::adaptiveThreshold(gray_plate, thresh_plate, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, 11, 2); std::vector<std::vector<cv::Point>> contours; cv::findContours(thresh_plate, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); std::vector<cv::Rect> rects; for (int i = 0; i < contours.size(); i++) { cv::Rect rect = cv::boundingRect(contours[i]); if (rect.width > 10 && rect.height > 10 && rect.width < plate_width / 2 && rect.height < plate_height) { rects.push_back(rect); } } std::sort(rects.begin(), rects.end(), [](cv::Rect a, cv::Rect b) { return a.x < b.x; }); for (int i = 0; i < rects.size(); i++) { cv::Mat char_img = gray_plate(rects[i]); cv::resize(char_img, char_img, cv::Size(32, 32)); // 将字符图像转换为一维数组,供后面的神经网络使用 float *char_data = (float *)malloc(32 * 32 * sizeof(float)); for (int row = 0; row < 32; row++) { for (int col = 0; col < 32; col++) { char_data[row * 32 + col] = char_img.at<unsigned char>(row, col) / 255.0; } } // 调用神经网络进行字符识别 int char_label = neural_network(char_data); printf("%c", char_label); free(char_data); } } ``` 4. 将识别结果输出到控制台 ```c++ // 将识别结果输出到控制台 printf("\n"); free(gray_data); } ``` 以上是一个简单的车牌识别算法的示例,可以通过Vivado HLS将其转换为可综合的硬件描述语言,并在FPGA上进行加速。需要注意的是,在实现过程中需要考虑硬件资源的限制和优化算法以提高性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

9527华安

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值