1:首先找到原NNIE画框函数的C文件:sam_comm_nnie.c文件,HI_S32 SAMPLE_COMM_SVP_NNIE_FillRect(VIDEO_FRAME_INFO_S *pstFrmInfo, SAMPLE_SVP_NNIE_RECT_ARRAY_S* pstRect, HI_U32 u32Color)该函数就是我们检测到目标后执行的画框函数。我们分析他发现,该函数使用的是VGS接口实现画框,仿照这个函数我们可自己实现画框架。
自定义一个画款函数DrawRectOnImage
HI_S32 DrawRectOnImage(VIDEO_FRAME_INFO_S* pstFrmInfo, RECT_S* pstRect, HI_U32 u32Color, HI_U32 rectWidth, HI_U32 rectHeight, HI_U32 rectOffsetX, HI_U32 rectOffsetY)
{
VGS_HANDLE VgsHandle = -1;
HI_S32 s32Ret = HI_SUCCESS;
VGS_TASK_ATTR_S stVgsTask;
VGS_ADD_COVER_S stVgsAddCover;
s32Ret = HI_MPI_VGS_BeginJob(&VgsHandle);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_PRT("Vgs begin job fail, Error(%#x)\n", s32Ret);
return s32Ret;
}
stVgsTask.stImgOut = *pstFrmInfo;
//memcpy(&stVgsTask.stImgIn, pstFrmInfo, sizeof(VIDEO_FRAME_INFO_S));
//memcpy(&stVgsTask.stImgOut, pstFrmInfo, sizeof(VIDEO_FRAME_INFO_S));
stVgsAddCover.enCoverType = COVER_QUAD_RANGLE;
stVgsAddCover.u32Color = u32Color;
stVgsAddCover.stDstRect.s32X = pstRect->s32X + rectOffsetX;
stVgsAddCover.stDstRect.s32Y = pstRect->s32Y + rectOffsetY;
stVgsAddCover.stDstRect.u32Width = rectWidth;
stVgsAddCover.stDstRect.u32Height = rectHeight;
stVgsAddCover.stQuadRangle.bSolid = HI_FALSE;
stVgsAddCover.stQuadRangle.u32Thick = 2;
stVgsTask.stImgIn = *pstFrmInfo;
//memcpy(stVgsAddCover.stQuadRangle.stPoint, pstRect->astRect[i][j].astPoint, sizeof(pstRect->astRect[i][j].astPoint));
s32Ret = HI_MPI_VGS_AddCoverTask(VgsHandle, &stVgsTask, &stVgsAddCover);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_PRT("HI_MPI_VGS_AddCoverTask fail, Error(%#x)\n", s32Ret);
HI_MPI_VGS_CancelJob(VgsHandle);
return s32Ret;
}
s32Ret = HI_MPI_VGS_EndJob(VgsHandle);
if (s32Ret != HI_SUCCESS)
{
SAMPLE_PRT("HI_MPI_VGS_EndJob fail, Error(%#x)\n", s32Ret);
HI_MPI_VGS_CancelJob(VgsHandle);
return s32Ret;
}
return s32Ret;
}
添加在SAMPLE_COMM_SVP_NNIE_FillRect官方给的代码下面,在这里我们区别与NNIE画框函数就在于不让此函数进行帧图像判断。
解释一下这段代码的大制作用:
函数名称修改为DrawRectOnImage
,传入参数pstFrmInfo
为一个图像帧的指针,pstRect
为矩形框的位置信息,u32Color
为矩形框的颜色。
函数内部进行了以下操作:
- 检查矩形框的总数,如果为0,则直接返回。
- 开始VGS任务。
- 复制输入的图像帧信息到VGS任务的输入和输出图像信息中。
- 设置覆盖物属性:
- 将覆盖物类型设置为矩形覆盖(
COVER_RECT
)。 - 将矩形框的颜色设置为输入的
u32Color
。 - 将矩形框的线型设置为非实心(
HI_FALSE
)。 - 将矩形框的线宽设置为2。
- 将覆盖物类型设置为矩形覆盖(
- 遍历矩形框数组,每次循环设置一个矩形框的位置和大小,并调用
HI_MPI_VGS_AddCoverTask
函数添加覆盖物任务。 - 结束VGS任务。
- 返回执行结果。
该函数通过使用VGS提供的API来创建VGS任务,并根据输入的矩形框位置信息和颜色,通过添加覆盖物任务,实现在图像帧上绘制矩形框的功能。函数返回执行结果,如果成功则返回HI_SUCCESS
,否则返回相应的错误码。
接着在相应的头文件上添加函数声明
2:接着在调用yolov3视频检测的画框函数内使用
代码如下,起作用部分都已注释
static HI_VOID* SAMPLE_SVP_NNIE_Yolov3_ViToVo_thread(HI_VOID* pArgs)
{
HI_S32 s32Ret;
SAMPLE_SVP_NNIE_PARAM_S *pstParam;
SAMPLE_SVP_NNIE_YOLOV3_SOFTWARE_PARAM_S *pstSwParam;
VIDEO_FRAME_INFO_S stBaseFrmInfo;
VIDEO_FRAME_INFO_S stExtFrmInfo;
HI_S32 s32MilliSec = 20000;
VO_LAYER voLayer = 0;
VO_CHN voChn = 0;
HI_S32 s32VpssGrp = 0;
HI_S32 as32VpssChn[] = {VPSS_CHN0, VPSS_CHN1};
pstParam = &s_stYolov3NnieParam;
pstSwParam = &s_stYolov3SoftwareParam;
struct timespec start_time,end_time;
float use_time =0;
// 假设有一个图像帧和矩形框的位置信息
//VIDEO_FRAME_INFO_S frameInfo;
RECT_S rect;
// 设置图像帧的属性
// ...
// 设置矩形框的位置信息
rect.s32X = 5000; // 矩形框的左上角X坐标
rect.s32Y = 5000; // 矩形框的左上角Y坐标
rect.u32Width = 5000000; // 矩形框的宽度
rect.u32Height = 8000000; // 矩形框的高度
// 设置其他参数
HI_U32 color = 0xFF0000; // 矩形框的颜色(红色0xFF0000,绿色0x0000FF00)
HI_U32 rectWidth = 200; // 矩形框的线宽
HI_U32 rectHeight = 1000;
HI_U32 rectOffsetX = 100000; // 矩形框相对于原始位置的X偏移量
HI_U32 rectOffsetY = 100000; // 矩形框相对于原始位置的Y偏移量
while (HI_FALSE == s_bNnieStopSignal)
{
clock_gettime(CLOCK_REALTIME, &start_time);
s32Ret = HI_MPI_VPSS_GetChnFrame(s32VpssGrp, as32VpssChn[1], &stExtFrmInfo, s32MilliSec);
if(HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("Error(%#x),HI_MPI_VPSS_GetChnFrame failed, VPSS_GRP(%d), VPSS_CHN(%d)!\n",
s32Ret,s32VpssGrp, as32VpssChn[1]);
continue;
}
s32Ret = HI_MPI_VPSS_GetChnFrame(s32VpssGrp, as32VpssChn[0], &stBaseFrmInfo, s32MilliSec);
SAMPLE_CHECK_EXPR_GOTO(HI_SUCCESS!=s32Ret, EXT_RELEASE,
"Error(%#x),HI_MPI_VPSS_GetChnFrame failed, VPSS_GRP(%d), VPSS_CHN(%d)!\n",
s32Ret,s32VpssGrp, as32VpssChn[0]);
s32Ret = SAMPLE_SVP_NNIE_Yolov3_Proc_ViToVo(pstParam,pstSwParam, &stExtFrmInfo,
stBaseFrmInfo.stVFrame.u32Width,stBaseFrmInfo.stVFrame.u32Height);
SAMPLE_CHECK_EXPR_GOTO(HI_SUCCESS!=s32Ret, BASE_RELEASE,
"Error(%#x),SAMPLE_SVP_NNIE_YOLOV3_Proc failed!\n", s32Ret);
// 开始绘制
HI_S32 ret = DrawRectOnImage(&stBaseFrmInfo, &rect, color, rectWidth, rectHeight, rectOffsetX, rectOffsetY);
// 检查绘制结果
if (ret != HI_SUCCESS)
{
printf("Failed to draw rectangle on image, ret = %d\n", ret);
}
else
{
printf("Rectangle is successfully drawn on image.\n");
}
//Draw rect
s32Ret = SAMPLE_COMM_SVP_NNIE_FillRect(&stBaseFrmInfo, &(pstSwParam->stRect), 0x0000FF00);
SAMPLE_CHECK_EXPR_GOTO(HI_SUCCESS!=s32Ret, BASE_RELEASE,
"SAMPLE_COMM_SVP_NNIE_FillRect failed, Error(%#x)!\n", s32Ret);
s32Ret = HI_MPI_VO_SendFrame(voLayer, voChn, &stBaseFrmInfo, s32MilliSec);
SAMPLE_CHECK_EXPR_GOTO(HI_SUCCESS!=s32Ret, BASE_RELEASE,
"HI_MPI_VO_SendFrame failed, Error(%#x)!\n", s32Ret);
clock_gettime(CLOCK_REALTIME, &end_time);
use_time = (float)(end_time.tv_sec - start_time.tv_sec)*1000 + (end_time.tv_nsec - start_time.tv_nsec) / 1000000.0;
SAMPLE_SVP_TRACE_INFO("The use time is %f \n", use_time);
BASE_RELEASE:
s32Ret = HI_MPI_VPSS_ReleaseChnFrame(s32VpssGrp,as32VpssChn[0], &stBaseFrmInfo);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("Error(%#x),HI_MPI_VPSS_ReleaseChnFrame failed,Grp(%d) chn(%d)!\n",
s32Ret,s32VpssGrp,as32VpssChn[0]);
}
EXT_RELEASE:
s32Ret = HI_MPI_VPSS_ReleaseChnFrame(s32VpssGrp,as32VpssChn[1], &stExtFrmInfo);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("Error(%#x),HI_MPI_VPSS_ReleaseChnFrame failed,Grp(%d) chn(%d)!\n",
s32Ret,s32VpssGrp,as32VpssChn[1]);
}
}
return HI_NULL;
}
(由于找坐标调试我一直没有调试好,目前只实现了画线功能,下一步实现画框,然后进行区域检测)