一、MD(移动侦测):
移动侦测是检测正在视频编码的图像是否发生亮度变化以及相应的运动向量。移动侦测通道就是视频编码通道,最大支持运动侦测路数与编码路数相同。
Hi3520/Hi3515 提供的移动侦测功能以宏块为最小单位,计算指定图像的宏块在指定图像间隔内的亮度变化和运动向量。如需要获取移动侦测的结果,则启用某一视频编码通道的移动侦测功能。移动侦测的结果包括宏块 SAD、宏块运动向量 MV、宏块报警信息、宏块报警像素的个数。
MD(Move Detect)模块支持 H.264编码和 MJPEG编码时进行移动侦测,针对同一 VI通道的主次码流,二者只能有一个作 MD。
实际工作中,主要获取宏块报警信息,宏块报警信息为侦测某个宏块(16*16以像素为单位)的结果:要么1,要么0,如果1的累加值超过阈值,就报警,当然这是相对简单的,如果想复杂点,比如同时可以通过相对静像的测试,判断出距离内报警信息等,是需要相对比较复杂的算法的,待续……
另一种方式:使用视频芯片CX25828实现移动侦测:http://blog.csdn.net/huangminqiang201209/article/details/8292060
也可以有其他方法,比如网上比较多的H.264帧差法,还有就是转化为YUV,然后灰度值的原始像素点比较,从而得到帧之间的差别,从而进行判定。
二、侦测方法:
时间差分(又称相邻帧差)方法(Temporal Difference )是在连续的图像序列中两个或三个相邻帧间采用基于像素的时间差分并且阈值化来提取出图像中的运动区域。时间差分运动检测方法对于动态环境具有较强的自适应性,但一般不能完全提取出所有相关的特征。
同时也还有背景减除法,即以一张图像为参考,是属于静态的,如果与参考不同,并符合报警阈值,则报警。还有光流法等
三、侦测的实现:
1.信号输入处理模块:标准模拟视频信号(CVBS彩色或黑白)是亮度信号和色度信号通过频普间置叠加在一起,需经过A/D芯片(如科胜讯CX25828)的解码,将模拟信号转成数字信号,产生标准的ITU 656 YUV格式的数字信号以帧为单位送到编码卡上的DSP和内存中。
2.CP(Image Coprocessor 图象协处理器)处理模块:YUV数据在DSP中加上OSD(字符时间叠加)和LOGO(位图)等,复合后通过PCI总线送到显存中,供视频实时预览用,还将复合后的数据送到编码卡的内存中,供编码使用。
3.ENCODER(编码)模块:将编码卡内存中的YUV数据送到MPEG4/H264编码器中,产生压缩好的码流,送到主机内存中,供录像或网络传输使用。
4.MOTIONDETECT处理模块:对编码卡内存中的以帧为单位YUV数据进行处理。(移动侦测)。
三、重要概念
MD图像与MD参考图像
比较 MD图像与 MD参考图像,来进行移动侦测。两者均可以随时间而不断更新。
MD间隔
MD图像与MD参考图像之间的间隔,单位为帧。通俗地讲,就是几帧做一次MD。
MD数据
也称 MD结果。
Hi3520/Hi3515 可以输出四种 MD数据:宏块 SAD,宏块 MV,宏块报警信息,宏块报警像素个数。
每一种 MD数据是否输出,均可以通过开关进行控制。
SDK仅提供 MD数据,而不进行 MD判决。客户可根据不同的应用选择合适的MD数据进行 MD判决,推荐使用宏块 SAD作为 MD判决依据。
宏块
将图像划分为 16*16(以像素为单位)大小的块,每一块称为一个宏块。
宏块 SAD
当前帧与参考帧相应宏块之间的亮度绝对差之和。
SAD(Sum of Absolute Difference):绝对差之和。
宏块 SAD越大,说明两帧对应宏块间的差别越大。
宏块 MV
参考帧至当前帧的各帧中相应宏块的运动向量的累加值,表示该宏块运动的方向。不推荐 MV作为移动侦测的判断依据。
MV(Motion Vector)运动向量。
宏块报警信息
宏块是否报警。
在去光照效应不打开的情况下,MD模块通过比较宏块 SAD与用户设置的 SAD报警阈值决定是否报警。
在去光照效应打开的情况下,MD模块通过宏块 SAD,用户设置的 SAD报警阈值,结合用户设置的像素报警阈值、像素报警个数阈值,通过一定去光照效应运算后得到的结果。
Hi3520/Hi3515 芯片暂不支持去光照效应。
只有设定宏块报警 SAD 阈值且启用宏块报警信息输出时,才会输出该信息。//故与stMdAttr.u16MBALSADTh = 1000;//宏块SAD的阈值为1000相关
宏块报警像素个数
宏块中报警的像素总数。
MD模块通过比较像素亮度差(当前帧和参考帧之间)与用户设置的像素报警阈值决定像素是否报警。
只有设定宏块报警像素阈值且启用宏块报警像素个数输出时,才会输出该数据。//故与stMdAttr.u8MBPelALTh = 20;//像素报警阈值为20相关
四、相关结构
1.定义运动侦测属性结构体:
typedef structhiMD_CHN_ATTR_S
{
MD_MB_MODE_S stMBMode; //宏块模式
MD_SADBITS_E enSADBits; //SAD输出精度
MD_DLIGHT_S stDlight; //去光照效应属性
HI_U8 u8MBPelALTh; //像素报警阈值。 取值范围:(0, 255)。
HI_U8 u8MBPerALNumTh; //像素报警个数阈值。 取值范围:(0, 255)。
HI_U16 u16MBALSADTh; //宏块报警阈值。 取值范围: 去光照效应打开时:(0, 255),去光照效应不打开时:(0, 65535)
HI_U32 u32MDInternal; //MD 侦测间隔。取值范围:[0, 256],以帧为单位。
HI_U32 u32MDBufNum; //MD缓存大小。 取值范围:[1, 16]。
} MD_CHN_ATTR_S;
2.定义参考图像属性结构体:
typedef structhiMD_REF_ATTR_S
{
MD_REF_MODE_E enRefFrameMode; //参考图像模式
MD_REF_STATUS_E enRefFrameStat; //参考图像更新状态
VIDEO_FRAME_S stUserRefFrame; //用户输入模式,参考图像的信息结构体
} MD_REF_ATTR_S;
【注意事项】
参考图像是否更新均是在参考图像模式为自动模式的情况下。参考图像为用户设置模式时,此参考图像更新状态无效,在此模式下用户还需配置参考图像。
3.定义运动侦测结果结构体:
typedef struct hiMD_DATA_S
{
HI_U32* pu32Addr; //MD结果地址信息,释放结果时需用到。不允许修改。
HI_U16 u16MBWidth; //图像的宽度。以宏块为单位。
HI_U16 u16MBHeight; //图像的高度。以宏块为单位。
HI_U64 u64Pts; //时间戳。
MD_MB_MODE_S stMBMode; //宏块模式
MD_MB_DATA_S stMBSAD; //宏块 SAD。
MD_MB_DATA_S stMBMV; //宏块 MV。
MD_MB_DATA_S stMBAlarm; //宏块报警信息。
MD_MB_DATA_S stMBPelAlarmNum; //宏块报警像素个数。
}MD_DATA_S;
4.定义宏块结果结构体:
typedef structhiMD_MB_DATA_S
{
HI_U32* pu32Addr; //宏块数据指针
HI_U32 u32Stride; //宏块数据以行为单位的内存宽度
} MD_MB_DATA_S;
5.定义宏块模式结构体:
typedef structhiMD_MB_MODE_S
{
HI_BOOL bMBSADMode; //宏块 SAD模式开关。取值范围:{HI_TRUE,HI_FALSE}
HI_BOOL bMBMVMode; //宏块 MV模式开关。
HI_BOOL bMBALARMMode; //宏块报警模式开关。
HI_BOOL bMBPelNumMode; //宏块报警像素个数模式开关。
} MD_MB_MODE_S;
6.定义去光照效应属性结构体:
typedef structhiMD_DLIGHT_S
{
HI_BOOL bEnable; //去光照效应开关。取值范围:{HI_TRUE, HI_FALSE}。
HI_U8 u8DlBeta; //去光照效应 Beta 值。 取值范围:[0, 7]。
HI_U8 u8DlAlpha; //去光照效应 Alpha 值。取值范围:[0, 7]。
HI_U16 Reserved; //保留。
} MD_DLIGHT_S;
7.定义运动侦测的 SAD的精度:
typedef enumhiMD_SADBITS_E
{
MD_SAD_8BIT = 0, //SAD精度为 8bit。
MD_SAD_16BIT, //SAD精度为 16bit。
MD_SAD_BUTT
} MD_SADBITS_E;
【注意事项】
不管精度值如何,在运动侦测结果中,每个宏块 SAD都要占用 2byte 内存。
8.定义参考图像模式:
typedef enumhiMD_REF_MODE_E
{
MD_REF_AUTO = 0, //自动设置参考图像模式。
MD_REF_USER, //用户输入参考图像模式。
MD_REF_MODE_BUTT
} MD_REF_MODE_E;
9.定义参考图像更新状态:
typedef enumhiMD_REF_STATUS_E
{
MD_REF_STATIC = 0, //参考图像不更新。
MD_REF_DYNAMIC, //参考图像更新。
MD_REF_STATUS_BUTT
} MD_REF_STATUS_E;
【注意事项】
参考图像是否更新均是在参考图像模式为自动模式的情况下。参考图像为用户设置模式时,此参考图像状态无效。
五、API参考
1.启用/禁用某一路视频编码通道的运动侦测功能
HI_S32 HI_MPI_MD_EnableChn(VENC_CHNVeChn);
HI_S32 HI_MPI_MD_DisableChn(VENC_CHNVeChn);
在启用该编码通道的运动侦测前,相对应的编码通道必须已经创建,且注册到编码通道组 GROUP 中,否则启用失败。运动侦测与编码同时实现,若对应的编码通道没有启动编码,无论运动侦测是否启用,都不会进行运动侦测。
在启用前,必须设置该视频编码通道的运动侦测属性。 如果需要获取宏块 SAD、宏块报警信息、宏块报警像素个数,还必须设置参考图像属性。
多次启用某一视频编码通道的运动侦测功能,和启用一次效果相同,都会返回成功。
目前支持 H.264 编码和 MJPEG编码时,进行运动侦测。对于大小码流编码通道,只支持其中一个码流编码通道进行运动侦测
2.设置/获取运动侦测的属性
HI_S32 HI_MPI_MD_SetChnAttr(VENC_CHNVeChn, const MD_CHN_ATTR_S *pstAttr);
HI_S32 HI_MPI_MD_GetChnAttr(VENC_CHNVeChn, MD_CHN_ATTR_S *pstAttr);
在启用运动侦测功能前,需要设置某一路视频编码通道的运动侦测属性。
在设置运动侦测的属性前,必须保证运动侦测处于禁用状态。
如果要获取宏块报警信息,在去光照效应不打开时,需设置宏块报警 SAD阈值;在去光照效应打开时,需要设置三个阈值:宏块的 SAD阈值、宏块内像素报警阈值和宏块内像素报警个数阈值 (Hi3520/Hi3515芯片暂不支持去光照效应) 。当发现宏块阈值的变化超过设定的阈值,就认为该宏块是运动的,然后给出宏块的报警信息。
如果要获取宏块报警信息,需设置宏块报警 SAD阈值。
如果要获取宏块像素报警个数时,需设置宏块报警的像素阈值。
3.设置/获取运动侦测的参考图像属性
HI_S32 HI_MPI_MD_SetRefFrame(VENC_CHNVeChn, const MD_REF_ATTR_S *pstAttr);
HI_S32 HI_MPI_MD_GetRefFrame(VENC_CHNVeChn, MD_REF_ATTR_S *pstAttr);
如果配置的某一视频编码通道运动侦测属性的运动侦测模式中包含宏块SAD、宏块报警信息、宏块像素报警个数模式中的其中一项或几项,
那么必须设置该视频编码通道运动侦测参考图像属性信息。
自动设置参考图像时,用户可以根据需要,设置参考图像是否更新。
− 如果不更新,就按照启用运动侦测后第一帧的编码图像作为参考。
− 如果更新,就按照运动侦测属性中的运动侦测间隔进行更新。
在设置运动侦测的参考图像属性之前必须保证运动侦测处于禁用状态,如果运动侦测为启用状态,则必须首先禁用运动侦测。
Hi3520/Hi3515 均不支持用户输入参考图象模式。
4.获取/释放运动侦测结果
HI_S32 HI_MPI_MD_GetData(VENC_CHN VeChn,MD_DATA_S *pstMdData, HI_U32 u32BlockFlag);
u32BlockFlag:阻塞标志。 HI_IO_BLOCK:阻塞, HI_IO_NOBLOCK:非阻塞。
如果运动侦测已经禁用,返回失败。
支持阻塞和非阻塞接口。
如果在获取过程中禁用 MD通道,则立即返回失败。
虽然 MD可以提供四种 MD数据,但只有用户打开该数据输出的开关,才会输出该数据。
HI_S32 HI_MPI_MD_ReleaseData(VENC_CHNVeChn, const MD_DATA_S *pstMdData);
此接口需与 HI_MPI_MD_GetData配对使用。
运动侦测结果需要在使用完之后立即释放,如果不及时释放会导致无运动侦测结果缓存而不能进行运动侦测。
释放的运动侦测结果必须是从该通道获取的运动侦测结果,不得对运动侦测结果结构体进行任何修改,也不允许释放从别的通道获取的运动侦测结果,否则会导
致运动侦测结果不能释放,使此运动侦测结果缓存丢失,甚至导致程序异常。
如果在释放运动侦测结果缓存过程中销毁通道,则立刻返回失败。
5.获取运动侦测通道对应的设备文件句柄
HI_S32 HI_MPI_MD_GetFd(VENC_CHN VeChn);
六、部分实现代码(软件实现,直接调用上面的函数)
/**************************************************************************************************************
**文件:MD.c
**编写者:huangminqiang
**编写日期:2012年4月27号
**简要描述:移动侦测
**修改者:
**修改日期:
**注:MD模块
**************************************************************************************************************/
/***** MD的初始化 ********************************************************************************************/
HI_S32 EnableMd(void)
{
HI_S32 s32Ret;
VENC_CHN VeChn = VENCCHNID;
MD_CHN_ATTR_S stMdAttr;
MD_REF_ATTR_S stRefAttr;
/*set MD attribute*/
//使能宏块SAD 模式,不使能 宏块MV 模式、宏块报警模式、宏块报警像素个数模式。
stMdAttr.stMBMode.bMBSADMode =HI_TRUE;
stMdAttr.stMBMode.bMBMVMode = HI_FALSE;
stMdAttr.stMBMode.bMBPelNumMode = HI_FALSE;
stMdAttr.stMBMode.bMBALARMMode = HI_FALSE;
stMdAttr.u16MBALSADTh = 1000;//宏块SAD 的阈值为1000
stMdAttr.u8MBPelALTh = 20;//像素报警阈值为20
stMdAttr.u8MBPerALNumTh = 20;//像素报警个数阈值为20
stMdAttr.enSADBits = MD_SAD_8BIT;//SAD 输出精度为8bit
stMdAttr.stDlight.bEnable = HI_FALSE;//去光照效应不使能 (不支持使能)
stMdAttr.u32MDInternal = 0;//MD监测间隔为0,即每帧都做MD
stMdAttr.u32MDBufNum = 16;//MD 缓存大小为16
/*set MD frame*/
stRefAttr.enRefFrameMode = MD_REF_AUTO;//参考图像模式为:自动设置
stRefAttr.enRefFrameStat = MD_REF_DYNAMIC;//参考图像更新状态为:更新
//设置移动侦测属性
s32Ret = HI_MPI_MD_SetChnAttr(VeChn, &stMdAttr);
if(s32Ret != HI_SUCCESS)
{
printf("HI_MPI_MD_SetChnAttr Err 0x%x\n", s32Ret);
return HI_FAILURE;
}
//设置移动侦测的参考图像属性
s32Ret = HI_MPI_MD_SetRefFrame(VeChn, &stRefAttr);
if(s32Ret != HI_SUCCESS)
{
printf("HI_MPI_MD_SetRefFrame Err 0x%x\n", s32Ret);
return HI_FAILURE;
}
//移动侦测通道使能
s32Ret = HI_MPI_MD_EnableChn(VeChn);
if(s32Ret != HI_SUCCESS)
{
printf("HI_MPI_MD_EnableChn Err 0x%x\n", s32Ret);
return HI_FAILURE;
}
return HI_SUCCESS;
}
/***** 线程:获取MD数据 ***************************************************************************************/
HI_VOID *GetMdData(HI_VOID *p)
{
HI_S32 s32Ret;
HI_S32 s32MdFd;
MD_DATA_S stMdData;
VENC_CHN VeChn = VENCCHNID;
fd_set read_fds;
struct timeval TimeoutVal;
FILE *pfd;
//创建MD结果文件
pfd = fopen("md_result.data", "wb");
if (!pfd)
{
return NULL;
}
//获取移动侦测通道对应的设备文件句柄
s32MdFd = HI_MPI_MD_GetFd(VeChn);
//get MD data
do{
FD_ZERO(&read_fds);
FD_SET(s32MdFd,&read_fds);
TimeoutVal.tv_sec = 2;
TimeoutVal.tv_usec = 0;
//监测设备是否有数据可读,超时是2s
s32Ret = select(s32MdFd+1, &read_fds, NULL, NULL, &TimeoutVal);
if (s32Ret < 0) //错误
{
printf("select err\n");
return NULL;
}
else if (0 == s32Ret) //超时
{
printf("time out\n");
return NULL;
}
else
{
//数据清零
memset(&stMdData, 0, sizeof(MD_DATA_S));
//有数据可读
if (FD_ISSET(s32MdFd, &read_fds))
{
//获取移动侦测数据
s32Ret = HI_MPI_MD_GetData(VeChn, &stMdData, HI_IO_NOBLOCK);
if(s32Ret != HI_SUCCESS)
{
printf("HI_MPI_MD_GetData err 0x%x\n",s32Ret);
return NULL;
}
}
//打印结果(略)
//对MD四种模式进行分析,读取数据。
//释放移动侦测数据
s32Ret = HI_MPI_MD_ReleaseData(VeChn, &stMdData);
if(s32Ret != HI_SUCCESS)
{
printf("Md Release Data Err 0x%x\n",s32Ret);
return NULL;
}
}
}while (HI_FALSE == g_bIsQuit);
if (pfd)
{
fclose(pfd);
}
return NULL;
}