1.
Hi35xx芯片自帶硬件的移動偵測功能,要實現需要找到對應接口及配置相關通道屬性。
2.
找到“..._SDK_V1.0.4.0\mpp\sample\common\sample_comm_vda.c",查看移動偵測的實現過程。
3.應用到嵌入式設備中
①.封裝接口,調用\sample_comm_vda.c文件裏的函數。
int vda_md(VI_CHN ViChn_Md)
{
int s32Ret;
////本例,ViChn_Md參數是設置綁定的輸入通道爲物理輸入,通道號爲24
VDA_CHN VdaChn_Md = 0;////設置對應的vda通道爲0.
SIZE_S stSize;
//vi通道實際是1080p的,此處輸入是720p。實際移動偵測是等比縮放檢測,不影響結果。
stSize.u32Width = 720;/////設置vda通道的輸入視頻寬高:
stSize.u32Height = 576;
s32Ret = SAMPLE_COMM_VDA_MdStart(VdaChn_Md, ViChn_Md, &stSize);
if (SUCCESS != s32Ret)
{
("VDA Md Start failed!\n");
return -1;
}
return 0;
}
②.修改\sample_comm_vda.c文件裏的函數,對應設備程序。
int SAMPLE_COMM_VDA_MdStart(VDA_CHN VdaChn, VI_CHN ViChn, SIZE_S *pstSize)
{
HI_S32 s32Ret = HI_SUCCESS;
VDA_CHN_ATTR_S stVdaChnAttr; //設置VDA通道的屬性
MPP_CHN_S stSrcChn, stDestChn;
if (VDA_MAX_WIDTH < pstSize->u32Width || VDA_MAX_HEIGHT < pstSize->u32Height)
{
("Picture size invaild!\n");
return -1;
}
/* step 1 create vda channel */
stVdaChnAttr.enWorkMode = VDA_WORK_MODE_MD;//設置工作模式爲md
stVdaChnAttr.u32Width = pstSize->u32Width; ////通道寬度,傳遞的參數值是720
stVdaChnAttr.u32Height = pstSize->u32Height; ////通道高度,傳遞的參數值是576
////設置移動偵測屬性結構體
stVdaChnAttr.unAttr.stMdAttr.enVdaAlg = VDA_ALG_REF; //VDA算法採樣幀差法
stVdaChnAttr.unAttr.stMdAttr.enMbSize = VDA_MB_16PIXEL;//宏塊大小,兩種16*16和8*8單位像素。
stVdaChnAttr.unAttr.stMdAttr.enMbSadBits = VDA_MB_SAD_8BIT;//SAD輸出精度
stVdaChnAttr.unAttr.stMdAttr.enRefMode = VDA_REF_MODE_DYNAMIC;
stVdaChnAttr.unAttr.stMdAttr.u32MdBufNum = 8;
stVdaChnAttr.unAttr.stMdAttr.u32VdaIntvl = 4; //偵測間隔以幀爲單位
stVdaChnAttr.unAttr.stMdAttr.u32BgUpSrcWgt = 128;
stVdaChnAttr.unAttr.stMdAttr.u32SadTh = 2000; //SAD報警閾值,最大4080
stVdaChnAttr.unAttr.stMdAttr.u32ObjNumMax = 128; //運動區域最大輸出個數
s32Ret = HI_MPI_VDA_CreateChn(VdaChn, &stVdaChnAttr);//創建視頻偵測通道,這裏主要設置好vda通道屬性。
if(s32Ret != HI_SUCCESS)
{
("err!\n");
return s32Ret;
}
/* step 2: vda chn bind vi chn */
stSrcChn.enModId = HI_ID_VIU;////設置輸入通道的類型,本次採用物理輸入hdmi或vga
stSrcChn.s32ChnId = ViChn; ///VI物理輸入通道號
stSrcChn.s32DevId = 0;////VI 和 VDEC 作爲數據源,是以通道爲發送者,向其他模塊發送數據,用戶將設備號置爲 0,SDK 不檢查輸入的設備號。詳細查看綁定接口說明。
("stSrcChn.s32ChnId=%d\n", stSrcChn.s32ChnId);
stDestChn.enModId = HI_ID_VDA;
stDestChn.s32ChnId = VdaChn;
stDestChn.s32DevId = 0;
s32Ret = HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);//將vda通道和要進行md的通道進行綁定
if(s32Ret != HI_SUCCESS)
{
("err!\n");
return s32Ret;
}
/* step 3: vda chn start recv picture */
s32Ret = HI_MPI_VDA_StartRecvPic(VdaChn);////開始接受圖像
if(s32Ret != HI_SUCCESS)
{
("err!\n");
return s32Ret;
}
/* step 4: create thread to get result */
gs_stMdParam.bThreadStart = HI_TRUE;
gs_stMdParam.VdaChn = VdaChn;
("gs_stMdParam.bThreadStart=%d\n", gs_stMdParam.bThreadStart);
("gs_stMdParam.VdaChn=%d\n", gs_stMdParam.VdaChn);
pthread_create(&gs_VdaPid[SAMPLE_VDA_MD_CHN], 0, SAMPLE_COMM_VDA_MdGetResult, (void *)&gs_stMdParam);////獲取結果並對結果處理。
return HI_SUCCESS;
}
③.獲取MD結果
void *SAMPLE_COMM_VDA_MdGetResult(void *pdata)
{
HI_S32 s32Ret;
VDA_CHN VdaChn;
VDA_DATA_S stVdaData;
VDA_MD_PARAM_S *pgs_stMdParam;
HI_S32 maxfd = 0;
FILE *fp = stdout;
HI_S32 VdaFd;
fd_set read_fds;
struct timeval TimeoutVal;
pgs_stMdParam = (VDA_MD_PARAM_S *)pdata;
VdaChn = pgs_stMdParam->VdaChn;
/* decide the stream file name, and open file to save stream */
/* Set Venc Fd. */
VdaFd = HI_MPI_VDA_GetFd(VdaChn);
if (VdaFd < 0)
{
printf("HI_MPI_VDA_GetFd failed with %#x!\n", VdaFd);
return NULL;
}
if (maxfd <= VdaFd)
{
maxfd = VdaFd;
}
while (HI_TRUE == pgs_stMdParam->bThreadStart)
{
FD_ZERO(&read_fds);
FD_SET(VdaFd, &read_fds);
TimeoutVal.tv_sec = 2;
TimeoutVal.tv_usec = 0;
s32Ret = select(maxfd + 1, &read_fds, NULL, NULL, &TimeoutVal);
if (s32Ret < 0)
{
printf("select failed!\n");
break;
}
else if (s32Ret == 0)
{
printf("get venc stream time out, exit thread\n");
//break;
continue;
}
else
{
if (FD_ISSET(VdaFd, &read_fds))
{
/*******************************************************
step 2.3 : call mpi to get one-frame stream
*******************************************************/
s32Ret = HI_MPI_VDA_GetData(VdaChn, &stVdaData, HI_TRUE);////獲取視頻偵測分析結果
if(s32Ret != HI_SUCCESS)
{
printf("HI_MPI_VDA_GetData failed with %#x!\n", s32Ret);
return NULL;
}
/*******************************************************
*step 2.4 : save frame to file
*******************************************************/
//printf("\033[0;0H");/*move cursor*/
SAMPLE_COMM_VDA_MdPrtObj(fp, &stVdaData);////對結果進行顯示處理
/*******************************************************
*step 2.5 : release stream
*******************************************************/
s32Ret = HI_MPI_VDA_ReleaseData(VdaChn,&stVdaData);////釋放視頻偵測分析結果。
if(s32Ret != HI_SUCCESS)
{
printf("HI_MPI_VDA_ReleaseData failed with %#x!\n", s32Ret);
return NULL;
}
}
}
usleep(1000);
}
return 0;
}
④.將檢測到的運動區域打印處理
int SAMPLE_COMM_VDA_MdPrtObj(FILE *fp, VDA_DATA_S *pstVdaData)
{
VDA_OBJ_S *pstVdaObj;
int i;
sint32 x0, y0, width, hight;
////bObjValid爲MD結果的有效性,是否有效是根據u32SadTh設置的值決定的。運動區域內宏塊以u32SadTh值爲界
if (HI_TRUE != pstVdaData->unData.stMdData.bObjValid)
{
printf("bMbObjValid = FALSE.\n");
return HI_SUCCESS;
}
////有效運動區域大於2個時,才進行打印
if(pstVdaData->unData.stMdData.stObjData.u32ObjNum > 2)
{
printf("ObjNum=%d, IndexOfMaxObj=%d, SizeOfMaxObj=%d, SizeOfTotalObj=%d\n", \
pstVdaData->unData.stMdData.stObjData.u32ObjNum, \
pstVdaData->unData.stMdData.stObjData.u32IndexOfMaxObj, \
pstVdaData->unData.stMdData.stObjData.u32SizeOfMaxObj,\
pstVdaData->unData.stMdData.stObjData.u32SizeOfTotalObj);
for (i=0; i<pstVdaData->unData.stMdData.stObjData.u32ObjNum; i++)
{
////檢測結果放在pstAddr 爲首地址的內存中
pstVdaObj = pstVdaData->unData.stMdData.stObjData.pstAddr + i;
printf("==>left=%d, top=%d, right=%d, bottom=%d\n", i, \
pstVdaObj->u16Left, pstVdaObj->u16Top, \
pstVdaObj->u16Right, pstVdaObj->u16Bottom);
x0 = pstVdaObj->u16Left;
y0 = pstVdaObj->u16Top;
width = pstVdaObj->u16Right - pstVdaObj->u16Left;
hight = pstVdaObj->u16Bottom - pstVdaObj->u16Top;
////運動區域是(x0,y0)爲左上角座標,width爲寬,hight爲高的矩形框裏,這些座標是對應輸入分辨率stSize.u32Width = 720;stSize.u32Height = 576;圖像的座標,圖像左上角座標爲(0,0)
}
}
return HI_SUCCESS;
}
⑤.關閉移動偵測的函數
void SAMPLE_COMM_VDA_MdStop(VDA_CHN VdaChn, VI_CHN ViChn)
{
HI_S32 s32Ret = HI_SUCCESS;
MPP_CHN_S stSrcChn, stDestChn;
printf("gs_stMdParam.bThreadStart=%d\n", gs_stMdParam.bThreadStart);
/* join thread */
if (TRUE == gs_stMdParam.bThreadStart)
{
gs_stMdParam.bThreadStart = HI_FALSE;
printf("gs_stMdParam.bThreadStart=%d\n", gs_stMdParam.bThreadStart);
pthread_join(gs_VdaPid[SAMPLE_VDA_MD_CHN], 0);
}
/* vda stop recv picture */
s32Ret = HI_MPI_VDA_StopRecvPic(VdaChn);
if(s32Ret != HI_SUCCESS)
{
printf("err(0x%x)!!!!\n",s32Ret);
}
/* unbind vda chn & vi chn */
stSrcChn.enModId = HI_ID_VIU;
stSrcChn.s32ChnId = ViChn;
stSrcChn.s32DevId = 0;
stDestChn.enModId = HI_ID_VDA;
stDestChn.s32ChnId = VdaChn;
stDestChn.s32DevId = 0;
s32Ret = HI_MPI_SYS_UnBind(&stSrcChn, &stDestChn);
if(s32Ret != HI_SUCCESS)
{
printf("err(0x%x)!!!!\n", s32Ret);
}
/* destroy vda chn */
s32Ret = HI_MPI_VDA_DestroyChn(VdaChn);
if(s32Ret != HI_SUCCESS)
{
printf("err(0x%x)!!!!\n", s32Ret);
}
return;
}
4.附錄注意
①.注意查看VI 的有效物理通道綁定的 VI 設備號,對應程序中的邏輯通道。通過cat /proc/umap/vi
②.MD原理,將輸入圖像,根據最小單位宏塊(16*16 或 8*8像素)分成相應個數的大小,然後檢測各宏塊的sad值(根據背景圖像和當前視頻差異判斷出兩幀圖像相應宏塊之間的亮度差的絕對值之和)。同時也可以輸出變化區域,變化區域的座標、每個宏塊的sad值。
例如:
以vi輸入通道2爲例,換算成物理輸入通道爲ToHisViChn(High , _s32Chn)=2*8=16,輸入圖像大小按CIF爲352*288,定義宏塊大小爲 16x16=stVdaChnAttr.unAttr.stMdAttr.enMbSize = VDA_MB_16PIXEL,則圖像可以分成22列18行。然後檢測獲得結果在定義爲VDA_DATA_S的結構體中。
③.因爲vda設置了最多通道數。在SAMPLE_COMM_VDA_MdStart(VDA_CHN VdaChn, VI_CHN ViChn, SIZE_S *pstSize)函數中,根據模塊號 stSrcChn.enModId不同,綁定目標數據的屬性中的通道號就不同。比如當模塊號爲HI_ID_VDEC解碼時,那麼stDestChn.s32ChnId則和HI_ID_VDEC解碼的通道ID=stSrcChn.s32ChnId一致;而當模塊號爲HI_ID_VIU輸入通道時,那麼stDestChn.s32ChnId則和輸入的物理通道號除8一致。
④.在HI_MPI_SYS_Bind函數中VI 和 VDEC 作爲數據源時,是以通道s32ChnId爲發送者,向其他模塊發送數據,用戶將設備號s32DevId置爲0,SDK 不檢查輸入的設備號。
⑤.如果檢測輸出的結果很多,不停打印,說明檢測太靈敏。需要設置 stVdaChnAttr.unAttr.stMdAttr.u32SadTh值大些,最大4080.可以理解成亮度的變化值,本例中調試設置值成2000.
具體實現代碼在以下鏈接:
海思35xx移動偵測-實現測試成功說明及代碼.rar
⑥.其他詳細說明可查看“HiMPP V3.0 媒體處理軟件開發參考.pdf”文檔中“7.視頻偵測分析”。
⑦.若用戶自己實現時,開始md只需要調用“vdamd.c”文件裏函數vda_md(VI_CHN ViChn_Md),注意ViChn_Md和stSrcChn.enModId及stSrcChn.s32ChnId需要對應,查看③。關閉md時,調用“vdamd.c”文件裏函數SAMPLE_COMM_VDA_MdStop,注意解綁時的 stSrcChn.enModId 和 stSrcChn.s32ChnId = ViChn。
⑧.如果“.c”文件中註釋爲亂碼,那麼可以用記事本或Notepad++打開文件查看。