代码拉取完成,页面将自动刷新
/******************************************************************************************
版权所有 2021-2021, Turix实验中心。保留所有权利。
文件名:main.c
作者:燕卫博 版本:V1.0 日期:2021.8.11
文件描述:
程序包含两个分支:BGR图片检测程序分支和即时检测程序分支。
【即时检测程序分支】初始化NNIE模块和MPP的VI(IMX307/GC2053)-VPSS(2Chn)~VO(HDMI)通路,
VPSS使能2路通道,高分辨率图像用于显示,低分辨率图像用于送NNIE预测。
依照预测结果在高分辨率图像上绘制检测框。
若编译时使能了H264_RTP_ENABLE宏开关,检测结果将同时被以H264-RTP方
式转发到远程端口。
【BGR图片检测程序分支】初始化NNIE模块,仅检测单张320x320分辨率的BGR图像并将结果通过串
口终端打印到屏幕。
其它说明:
9Hv2,个人。
仅考虑适配YOLO-Fastest1.0-XL。
历史修改记录:
1. 2021-5-27:V1.0 燕卫博
创建。
2. 2021-8-11:V1.0 燕卫博
从9Hv1拷贝,仅考虑适配Hi3516DV300,单路检测,H264-RTP推流和HDMI送显示例,用作实验室公开。
******************************************************************************************/
// ↓
//+------------------------------------------------------------------------------------------+
//| 工程自述
//+------------------------------------------------------------------------------------------+
//| “9Hv2”是由当前工程首位开发者分配的标识代号,代指当前工程主体。
//| 9Hv2工程的目标任务为:基于9Hv1个人工程,仅适配Hi3516DV300,使用YOLO-Fastest网络实现
//| 实时检测和实时编码,并分离检测和编码逻辑。工程归属于代号为“WIT”的更高任务范围的衍生。
//| 本工程的性质为个人学习工程,因此仅实现基本功能,不考虑可扩展性和灵活配置的需求。实际
//| 使用时,不建议直接基于本工程做修改。
//|
//| 最简单的检测思路可参考SVP-NNIE例程中的RFCN示例流程。MPP将初始化单路VI并经由VPSS模块
//| 生成两条不同分辨率的图像处理通路,其中一路为用于显示的大分辨率图像,另一路为用于送往NNIE
//| 进行预测的小分辨率图像。考虑到图像拉抻可能对检测效果造成负面影响,工程中保持图像比例进行
//| 缩放(2M -> 320*180),而后在装载图像到NNIE时使用标准灰色RGB(128,128,128)对源Blob空间进
//| 行对称填充。获取到网络输出结果后,再对坐标进行变换,使用VGS模块对大分辨率图像打Cover,最
//| 后送VO模块显示输出。
//| 上述程序结构便于理解,但因显示与预测处于同一过程,预测性能将影响显示帧数。因此为了实
//| 现满帧显示或编码,9Hv2选择抽帧送检的方式重新设计主程序,抽帧检测结果通过全局变量共享给主
//| 线程,以供显示、编码或后续处理所需。
//| 程序同时包含了送帧编码和RTP发送的示例,由于此功能是被追加的,仅用于功能实现示意,因
//| 此将相关参数直接写死在程序中而不考虑实现灵活配置——毕竟这个程序本来就是为了示例而生。
//| 工程中使用的wk文件从YOLO-Fastest1.0-XL网络在COCO数据集上的预训练权重转换而来,网络转
//| 换(Darknet2Caffe)的步骤、过程和资料参见WIT文档或其它网络资料。
//|
//| 本自述作为C&I Lab.实验室内部代码公开的许可凭证。
//+------------------------------------------------------------------------------------------+
//| 宏开关
//+------------------------------------------------------------------------------------------+
//| ----------------------------------------
//| NOTE:若使能H264_RTP_ENABLE宏定义,检测结
//| 果将被同时转发到远程端口。
//| ----------------------------------------
//| #define H264_RTP_ENABLE
//+------------------------------------------------------------------------------------------+
//| 头文件包含
//+------------------------------------------------------------------------------------------+
/*|*/ #include <stdio.h>
/*|*/ #include <stdlib.h>
/*|*/ #include <stdint.h>
/*|*/ #include <stdbool.h>
/*|*/ #include <signal.h>
/*|*/ #include <unistd.h>
/*|*/ #include <pthread.h>
/*|*/ #include <sys/time.h>
/*|*/ #include <arpa/inet.h>
//|
/*|*/ #include "hi_comm_ive.h"
/*|*/ #include "hi_comm_vpss.h"
/*|*/ #include "hi_comm_venc.h"
/*|*/ #include "mpi_vpss.h"
/*|*/ #include "mpi_venc.h"
/*|*/ #include "mpi_vo.h"
//|
/*|*/ #include "L_MPP40_sys.h"
/*|*/ #include "L_MPP40_vi.h"
/*|*/ #include "L_MPP40_vpss.h"
/*|*/ #include "L_MPP40_vo.h"
/*|*/ #include "L_MPP40_vgs.h"
/*|*/ #include "L_SVP_NNIE_model.h"
/*|*/ #include "L_SVP_IVE_csc.h"
//|
/*|*/ #ifdef H264_RTP_ENABLE
/*|*/ #include "L_MPP40_venc.h"
/*|*/ #include "L_RTP.h"
/*|*/ #endif
//+------------------------------------------------------------------------------------------+
//| 全局变量
//+------------------------------------------------------------------------------------------+
//| 主程序循环标志位
/*|*/ static bool flag_main_loop = true;
//|
//| ----------------------------------------
//| 显示/检测线程共享与同步相关
//| ----------------------------------------
/*|*/ static bool flag_rect_ready = false;
/*|*/ static pthread_mutex_t bbox_mutex = PTHREAD_MUTEX_INITIALIZER;
/*|*/ static pthread_t detect_tid;
/*|*/ static IVE_IMAGE_S stOutputRGB888Image = {0};
/*|*/ static SVP_NNIE_YOLO_OUTPUT_BBOX_S *pBboxBuf = NULL;
/*|*/ static int BboxNum = 0;
/*|*/ static VGS_RECT rect[MAX_DETECTION_NUM] = {0};
//|
/*|*/ #ifdef H264_RTP_ENABLE
//| ----------------------------------------
//| 编码线程相关
//| ----------------------------------------
/*|*/ static pthread_t encode_tid;
//|
/*|*/ static VENC_PARA_S stVENCpara=
/*|*/ {
/*|*/ .VencChn=0,
/*|*/ .enPayLoad=PT_H264,
/*|*/ .enSize=PIC_1080P,
/*|*/ .enGopMode=VENC_GOPMODE_NORMALP,
/*|*/ .u32Gop=30,
/*|*/ .enRcMode=RC_CBR,
/*|*/ .u32FrameRate=30,
/*|*/ .u32Profile=0,
/*|*/ .s32FrameNumber=-1,
/*|*/ };
//|
//| ----------------------------------------
//| 推流设置相关
//| 为方便增删,将推流信息作为全局变量
//| ----------------------------------------
/*|*/ static char rtp_remote_ip[] = "169.254.134.37";
/*|*/ static char rtp_remote_port[] = "1118";
/*|*/ static L_STRUCT_RTPSESSION RTPSession=
/*|*/ {
/*|*/ .flag_inited = 0,
/*|*/ };
/*|*/ #endif
//+------------------------------------------------------------------------------------------+
#ifdef H264_RTP_ENABLE
//+------------------------------------------------------------------------------------------+
//| 函数名称:stream_process
//| 功能描述:编码码流处理
//| 参数说明:VENC通道号,码流包结构体
//| 返回值说明:无
//| 备注:
//+------------------------------------------------------------------------------------------+
static void stream_process(int VencChn, VENC_STREAM_S *pstStream)
{
int packagelength;
static float len_per_second = 0;
static int framecount = 0;
if(VencChn == stVENCpara.VencChn)
{
for(int i = 0; i < pstStream->u32PackCount; i++)
{
packagelength = pstStream->pstPack[i].u32Len - pstStream->pstPack[i].u32Offset;
len_per_second += packagelength;
RTPSession.pData = pstStream->pstPack[i].pu8Addr + pstStream->pstPack[i].u32Offset;
RTPSession.DataLength = packagelength;
RTPSession.TimeStamp = pstStream->pstPack[i].u64PTS;
RTPSession.DataType = RTP_H264;
L_RTP_Send(&RTPSession);
}
framecount++;
if(framecount % 30 == 0)
{
printf("[ENCODE -INFO]RC:%f Kbps\n", len_per_second/128);
len_per_second = 0;
framecount = 0;
}
}
}
//+------------------------------------------------------------------------------------------+
//| 函数名称:encode_process
//| 功能描述:编码处理线程
//| 参数说明:无
//| 返回值说明:无
//| 备注:
//+------------------------------------------------------------------------------------------+
static void *encode_process(void *p)
{
L_VENC_GetStream(&flag_main_loop, stream_process);
pthread_exit(NULL);
}
#endif
//+------------------------------------------------------------------------------------------+
//| 函数名称:detect_process
//| 功能描述:检测处理线程
//| 参数说明:无
//| 返回值说明:无
//| 备注:
//+------------------------------------------------------------------------------------------+
static void *detect_process(void *p)
{
HI_S32 s32Ret = HI_SUCCESS;
VIDEO_FRAME_INFO_S stLittleFrameInfo = {0};
struct timeval tvstart, tvend;
int detect_fps = 0;
long runningtime = 0;
gettimeofday(&tvstart, NULL);
while(flag_main_loop)
{
//------------------------------------------------------------
// 获取小分辨率图像
// NOTE:VPSS两通道的初始化见其封装文件。通道2为待画框和送显的
// 原图;通道1为待检测的缩略图。
// ATTENTION:本工程中,缩略图大小需定为320 * 180。
//------------------------------------------------------------
s32Ret = HI_MPI_VPSS_GetChnFrame(0, 1, &stLittleFrameInfo, 1000);
if(s32Ret != HI_SUCCESS){printf("[MAIN-ERROR]HI_MPI_VPSS_GetChnFrame[1] failed with %#x!\n", s32Ret);break;}
//------------------------------------------------------------
// 图像像素格式转换
// NOTE:网络模型需要的图像像素格式为U8C3,但VPSS输出的图像像素
// 仅支持YVU-SP420/YVU-SP422/YUV-SP420/YUV-SP422/YUV400。
// 因此需进一步将YVU/YUV图像转换为RGB格式。
// NOTE:IVE仅能将图像转为RGB而非BGR。
//------------------------------------------------------------
s32Ret = L_SVP_IVE_CSC(&stLittleFrameInfo, &stOutputRGB888Image);
if(s32Ret != 0){printf("[MAIN-ERROR]L_SVP_IVE_CSC failed with %#x!\n", s32Ret);break;}
//------------------------------------------------------------
// 装载待检测图像
// NOTE:对待检测图像的预处理操作由装载函数完成。
// ATTENTION:这里装载的图像大小需为320 * 180,否则需修改装载函
// 数中的padding操作。
//------------------------------------------------------------
s32Ret = L_SVP_NNIE_LoadData(NULL, &stOutputRGB888Image);
if(s32Ret != 0){printf("[MAIN-ERROR]L_SVP_NNIE_LoadData failed with %#x!\n", s32Ret);break;}
//------------------------------------------------------------
// NNIE前向计算
//------------------------------------------------------------
s32Ret = L_SVP_NNIE_Forward();
if(s32Ret != 0){printf("[MAIN-ERROR]L_SVP_NNIE_Forward failed with %#x!\n", s32Ret);break;}
//------------------------------------------------------------
// 获取检测结果
//------------------------------------------------------------
pthread_mutex_lock(&bbox_mutex);
L_SVP_NNIE_GetResult(&pBboxBuf, &BboxNum);
if(BboxNum != 0)
{
/* 统计框信息并填充rect结构 */
for(int i = 0; i < BboxNum; i++)
{
/* Bbox坐标以图像左上为原点,6为宽高缩放倍数(1920/320) */
int32_t trans_Xmin = 6 * SVP_NNIE_MAX(pBboxBuf[i].u32Xmin, 0);
int32_t trans_Ymin = 6 * SVP_NNIE_MAX(pBboxBuf[i].u32Ymin - 70, 0);
int32_t trans_Xmax = 6 * SVP_NNIE_MIN(pBboxBuf[i].u32Xmax, 320);
int32_t trans_Ymax = 6 * SVP_NNIE_MIN(pBboxBuf[i].u32Ymax - 70, 180);
/* 左上 */
rect[i].astPoint[0].s32X = trans_Xmin;
rect[i].astPoint[0].s32Y = trans_Ymin;
/* 左下 */
rect[i].astPoint[1].s32X = trans_Xmin;
rect[i].astPoint[1].s32Y = trans_Ymax;
/* 右下 */
rect[i].astPoint[2].s32X = trans_Xmax;
rect[i].astPoint[2].s32Y = trans_Ymax;
/* 右上 */
rect[i].astPoint[3].s32X = trans_Xmax;
rect[i].astPoint[3].s32Y = trans_Ymin;
}
}
pthread_mutex_unlock(&bbox_mutex);
flag_rect_ready = true;
//------------------------------------------------------------
// 释放图像
//------------------------------------------------------------
HI_MPI_VPSS_ReleaseChnFrame(0, 1, &stLittleFrameInfo);
//------------------------------------------------------------
// 处理帧率统计
//------------------------------------------------------------
detect_fps++;
gettimeofday(&tvend, NULL);
runningtime = (1000000 * tvend.tv_sec + tvend.tv_usec) - (1000000 * tvstart.tv_sec + tvstart.tv_usec);
if(runningtime > 1000000)
{
printf("[DETECT -INFO]Detect FPS:%d\n", detect_fps);
detect_fps = 0;
gettimeofday(&tvstart, NULL);
}
}
pthread_exit(NULL);
}
//+------------------------------------------------------------------------------------------+
//| 函数名称:Usage
//| 功能描述:使用说明
//| 参数说明:无
//| 返回值说明:无
//| 备注:
//+------------------------------------------------------------------------------------------+
static void Usage(void)
{
printf("Usage:\n");
printf("Adaptation:Hi3516DV300\n");
printf("You can call program like this:\n");
printf(" 1) ./program\n");
printf(" Only show this Usage.\n");
printf(" 2) ./program wk-filename\n");
printf(" The program will get the data from VI, after predicting by YOLO-Fastest, the result will be displayed on HDMI.\n");
printf(" If RTP is enabled, the detection result will be forwarded to the remote port at the same time.\n");
printf(" 3) ./program wk-filename bgr-filename\n");
printf(" The program will only predict the BGR image and print the result on the console.\n");
printf("Author:Linyar Version:9Hv2 E-mail:WindForest@yeah.net\n");
printf("Infomation:9Hv2 belongs to WIT-Extend project.\n");
printf("[NOTICE]Input Ctrl+C to stop program.\n\n");
}
//+------------------------------------------------------------------------------------------+
//| 函数名称:sig_handle
//| 功能描述:信号处理
//| 参数说明:信号值
//| 返回值说明:无
//| 备注:
//+------------------------------------------------------------------------------------------+
static void sig_handle(int signo)
{
if(SIGINT == signo || SIGTERM == signo)
{
flag_main_loop = false;
}
}
//+------------------------------------------------------------------------------------------+
//| 函数名称:main
//| 功能描述:主函数
//| 参数说明:当无传参时,程序仅打印使用说明;
//| 当传递1个参数时,该参数应为wk文件路径;
//| 当传递2个或以上参数时,第2个参数为待预测的bgr图像,程序仅打印该图像的预测结果而
//| 不调用MPP和摄像头。
//| 返回值说明:略
//| 备注:
//+------------------------------------------------------------------------------------------+
int main(int argc, const char *argv[])
{
HI_S32 s32Ret = HI_SUCCESS;
VIDEO_FRAME_INFO_S stBigFrameInfo = {0};
struct timeval tvstart, tvend;
int display_fps = 0;
long runningtime = 0;
signal(SIGINT, sig_handle);
signal(SIGTERM, sig_handle);
Usage();
if(argc == 1)exit(0);
//------------------------------------------------------------
// MPP系统初始化
//------------------------------------------------------------
L_MPP_SYS_Init();
//------------------------------------------------------------
// SVP初始化
//------------------------------------------------------------
L_SVP_NNIE_Init(argv[1]);
L_SVP_IVE_CSC_Init(&stOutputRGB888Image);
//------------------------------------------------------------
// 根据传入参数决定应用程序运行路径
//------------------------------------------------------------
if(argc > 2)
{
printf("[MAIN-INFO]NNIE will predict the image from the file.\n");
L_SVP_NNIE_LoadData(argv[2], NULL);
gettimeofday(&tvstart, NULL);
L_SVP_NNIE_Forward();
gettimeofday(&tvend, NULL);
runningtime = (1000000 * tvend.tv_sec + tvend.tv_usec) - (1000000 * tvstart.tv_sec + tvstart.tv_usec);
printf("[MAIN-TEST]L_SVP_NNIE_Forward takes %ldus\n", runningtime);
gettimeofday(&tvstart, NULL);
L_SVP_NNIE_GetResult(&pBboxBuf, &BboxNum);
gettimeofday(&tvend, NULL);
runningtime = (1000000 * tvend.tv_sec + tvend.tv_usec) - (1000000 * tvstart.tv_sec + tvstart.tv_usec);
printf("[MAIN-TEST]L_SVP_NNIE_GetResult takes %ldus\n", runningtime);
gettimeofday(&tvstart, NULL);
printf("[MAIN-TEST]Get Bbox(es):\n");
if(BboxNum != 0)
{
for(int i = 0; i < BboxNum; i++)
{
printf("\tXmin:%d Ymin:%d Xmax:%d Ymax:%d Prob:%f ClassIndex:%d\n",
pBboxBuf[i].u32Xmin,
pBboxBuf[i].u32Ymin,
pBboxBuf[i].u32Xmax,
pBboxBuf[i].u32Ymax,
pBboxBuf[i].f32ObjectProb,
pBboxBuf[i].u32ClassIndex);
}
}
gettimeofday(&tvend, NULL);
runningtime = (1000000 * tvend.tv_sec + tvend.tv_usec) - (1000000 * tvstart.tv_sec + tvstart.tv_usec);
printf("[MAIN-TEST]GetResults takes %ldus\n", runningtime);
goto END_NNIE;
}
//------------------------------------------------------------
// MPP业务初始化
// NOTE:VI(IMX307/GC2053)-VPSS(2Chn)~VO(HDMI)
//------------------------------------------------------------
if(L_VI_Init(NULL) < 0)goto END_VI;
if(L_VPSS_Init() < 0)goto END_VPSS;
#ifdef H264_RTP_ENABLE
if(L_VENC_CreateChnForVideo(&stVENCpara) < 0)goto END_VENC;
#endif
if(L_VO_Init() < 0)goto END_VO;
printf("[MAIN-INFO]NNIE will predict the image from the sensor.\n");
//------------------------------------------------------------
// 创建检测线程
//------------------------------------------------------------
if(pthread_create(&detect_tid, NULL, detect_process, NULL) != 0)
{
printf("[MAIN-ERROR]Create detect thread failed!\n");
flag_main_loop = false;
goto END_VO;
}
#ifdef H264_RTP_ENABLE
//------------------------------------------------------------
// 创建编码线程
//------------------------------------------------------------
if(pthread_create(&encode_tid, NULL, encode_process, NULL) != 0)
{
printf("[MAIN-ERROR]Create encode process thread failed!\n");
flag_main_loop = false;
goto END_DETECT;
}
//------------------------------------------------------------
// 创建RTP会话
//------------------------------------------------------------
inet_aton(rtp_remote_ip, (struct in_addr *)&(RTPSession.u32DestIP));
RTPSession.DestPort = atoi(rtp_remote_port);
if(L_RTP_CreateSession(&RTPSession) != 0)
{
printf("[MAIN-ERROR]Create RTP session failed!\n");
flag_main_loop = false;
goto END_ENCODE;
}
printf("[MAIN-INFO]RTP[%s:%s] startup.", rtp_remote_ip, rtp_remote_port);
#endif
gettimeofday(&tvstart, NULL);
while(flag_main_loop)
{
//------------------------------------------------------------
// 获取原分辨率图像
// NOTE:VPSS两通道的初始化见其封装文件。通道2为待画框和送显的
// 原图;通道1为待检测的缩略图。
// ATTENTION:本工程中,缩略图大小需定为320 * 180。
//------------------------------------------------------------
s32Ret = HI_MPI_VPSS_GetChnFrame(0, 2, &stBigFrameInfo, 1000);
if(s32Ret != HI_SUCCESS){printf("[MAIN-ERROR]HI_MPI_VPSS_GetChnFrame[2] failed with %#x!\n", s32Ret);break;}
//------------------------------------------------------------
// 在原图上按比例绘制检测框
// NOTE:绘制检测框前,需对检测结果进行变换,主要包括对坐标的变
// 换,因为数据装载时的padding操作会将原图填充为320 * 320
// 的图像。
//------------------------------------------------------------
pthread_mutex_lock(&bbox_mutex);
if(BboxNum != 0 && flag_rect_ready)
{
/* 将框信息送VGS打Cover */
s32Ret = L_VGS_AddCover(&stBigFrameInfo, rect, BboxNum, 0x000000FF);
if(s32Ret != HI_SUCCESS)printf("[MAIN-ERROR]L_VGS_AddCover failed with %#x!\n", s32Ret);
}
pthread_mutex_unlock(&bbox_mutex);
//------------------------------------------------------------
// 视频帧送显
// NOTE:送显示到视频层0-通道0,非阻塞,该视频层送往HDMI。
//------------------------------------------------------------
s32Ret = HI_MPI_VO_SendFrame(0, 0, &stBigFrameInfo, 0);
if(s32Ret != HI_SUCCESS){printf("[MAIN-ERROR]HI_MPI_VO_SendFrame[Layer0] failed with %#x!\n", s32Ret);continue;}
#ifdef H264_RTP_ENABLE
s32Ret = HI_MPI_VENC_SendFrame(stVENCpara.VencChn, &stBigFrameInfo, 0);
if(s32Ret != HI_SUCCESS){printf("[MAIN-ERROR]HI_MPI_VENC_SendFrame failed with %#x!\n", s32Ret);continue;}
#endif
//------------------------------------------------------------
// 释放图像
//------------------------------------------------------------
HI_MPI_VPSS_ReleaseChnFrame(0, 2, &stBigFrameInfo);
//------------------------------------------------------------
// 显示帧率统计
//------------------------------------------------------------
display_fps++;
gettimeofday(&tvend, NULL);
runningtime = (1000000 * tvend.tv_sec + tvend.tv_usec) - (1000000 * tvstart.tv_sec + tvstart.tv_usec);
if(runningtime > 1000000)
{
printf("[DISPLAY-INFO]Display FPS:%d\n", display_fps);
display_fps = 0;
gettimeofday(&tvstart, NULL);
}
}
//------------------------------------------------------------
// MPP业务去初始化
//------------------------------------------------------------
#ifdef H264_RTP_ENABLE
L_RTP_DestorySession(&RTPSession);
END_ENCODE:
pthread_join(encode_tid, NULL);
END_DETECT:
#endif
pthread_join(detect_tid, NULL);
END_VO:
L_VO_DeInit();
#ifdef H264_RTP_ENABLE
END_VENC:
L_VENC_DeInit(stVENCpara.VencChn);
#endif
END_VPSS:
L_VPSS_DeInit();
END_VI:
L_VI_DeInit();
//------------------------------------------------------------
// SVP去初始化
//------------------------------------------------------------
END_NNIE:
L_SVP_NNIE_DeInit();
L_SVP_IVE_CSC_DeInit(&stOutputRGB888Image);
//------------------------------------------------------------
// MPP系统退出
//------------------------------------------------------------
L_MPP_SYS_DeInit();
//------------------------------------------------------------
// 其它退出操作
//------------------------------------------------------------
return 0;
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。