文件位置
openh264/codec/processing/scenechangedetection/SceneChangeDetection.cpp
openh264/codec/processing/scenechangedetection/SceneChangeDetection.h
代码流程
说明 : 通过代码流程分析,当METHOD_SCENE_CHANGE_DETECTION_SCREEN场景类型为时候,创建CSceneChangeDetectorScreen子类进行场景变化检测;当METHOD_SCENE_CHANGE_DETECTION_VIDEO场景类型为时,创建CSceneChangeDetectorVideo子类进行场景变化检测。
原理
开关控制参数 :(bool)bEnableSceneChangeDetect
CSceneChangeDetectorVideo 类
功能 :通过CSceneChangeDetection类模板调用CSceneChangeDetectorVideo类中方法实现摄像视频场景变化检测功能类过程 :
第一步:CSceneChangeDetection类中process 函数; 初始化变量、参数设置; 场景变化阈值iSceneChangeThresholdLarge、iSceneChangeThresholdMedium计算; 设置eSceneChangeIdc类型为SIMILAR_SCENE; 调用一个名为m_cDetector的对象的重载运算符(),传入m_sLocalParam作为参数,调用指向CSceneChangeDetectorVideo类中场景变化检测算法; 场景变化判断; 如果,iMotionBlockNum 大于等于iSceneChangeThresholdLarge,则设置eSceneChangeIdc类型为LARGE_CHANGED_SCENE; 否则,iMotionBlockNum 大于等于iSceneChangeThresholdMedium,则设置eSceneChangeIdc类型为MEDIUM_CHANGED_SCENE; 第二步:CSceneChangeDetectorVideo类中operator重载; 双层嵌套 for 循环处理以 8x8 为单位的图像块,即处理每个 8x8 块; 调用m_pfSad
函数(指向具体的WelsSampleSad8x8_c
函数)计算 iSad 值; 比较 iSad 与HIGH_MOTION_BLOCK_THRESHOLD的大小,将结果累加到iMotionBlockNum中; 指针更新,下一个 8x8 块以及下一行图像;
原理图 : 相关源码 :
template < typename T >
class CSceneChangeDetection : public IStrategy { public : CSceneChangeDetection ( EMethods eMethod, int32_t iCpuFlag) : m_cDetector ( m_sSceneChangeParam, iCpuFlag) { m_eMethod = eMethod; WelsMemset ( & m_sSceneChangeParam, 0 , sizeof ( m_sSceneChangeParam) ) ; } ~ CSceneChangeDetection ( ) { } EResult Process ( int32_t iType, SPixMap* pSrcPixMap, SPixMap* pRefPixMap) { EResult eReturn = RET_INVALIDPARAM; m_sLocalParam. iWidth = pSrcPixMap-> sRect. iRectWidth; m_sLocalParam. iHeight = pSrcPixMap-> sRect. iRectHeight; m_sLocalParam. iBlock8x8Width = m_sLocalParam. iWidth >> 3 ; m_sLocalParam. iBlock8x8Height = m_sLocalParam. iHeight >> 3 ; m_sLocalParam. pRefY = ( uint8_t * ) pRefPixMap-> pPixel[ 0 ] ; m_sLocalParam. pCurY = ( uint8_t * ) pSrcPixMap-> pPixel[ 0 ] ; m_sLocalParam. iRefStride = pRefPixMap-> iStride[ 0 ] ; m_sLocalParam. iCurStride = pSrcPixMap-> iStride[ 0 ] ; m_sLocalParam. pStaticBlockIdc = m_sSceneChangeParam. pStaticBlockIdc; int32_t iBlock8x8Num = m_sLocalParam. iBlock8x8Width * m_sLocalParam. iBlock8x8Height; int32_t iSceneChangeThresholdLarge = WelsStaticCast ( int32_t , m_cDetector. GetSceneChangeMotionRatioLarge ( ) * iBlock8x8Num + 0.5f + PESN) ; int32_t iSceneChangeThresholdMedium = WelsStaticCast ( int32_t , m_cDetector. GetSceneChangeMotionRatioMedium ( ) * iBlock8x8Num + 0.5f + PESN) ; m_sSceneChangeParam. iMotionBlockNum = 0 ; m_sSceneChangeParam. iFrameComplexity = 0 ; m_sSceneChangeParam. eSceneChangeIdc = SIMILAR_SCENE; m_cDetector ( m_sLocalParam) ; if ( m_sSceneChangeParam. iMotionBlockNum >= iSceneChangeThresholdLarge) { m_sSceneChangeParam. eSceneChangeIdc = LARGE_CHANGED_SCENE; } else if ( m_sSceneChangeParam. iMotionBlockNum >= iSceneChangeThresholdMedium) { m_sSceneChangeParam. eSceneChangeIdc = MEDIUM_CHANGED_SCENE; } eReturn = RET_SUCCESS; return eReturn; } EResult Get ( int32_t iType, void * pParam) { if ( pParam == NULL ) { return RET_INVALIDPARAM; } * ( SSceneChangeResult* ) pParam = m_sSceneChangeParam; return RET_SUCCESS; } EResult Set ( int32_t iType, void * pParam) { if ( pParam == NULL ) { return RET_INVALIDPARAM; } m_sSceneChangeParam = * ( SSceneChangeResult* ) pParam; return RET_SUCCESS; } private : SSceneChangeResult m_sSceneChangeParam; SLocalParam m_sLocalParam; T m_cDetector;
} ;
CSceneChangeDetectorVideo
类
class CSceneChangeDetectorVideo { public : CSceneChangeDetectorVideo ( SSceneChangeResult& sParam, int32_t iCpuFlag) : m_sParam ( sParam) { m_pfSad = WelsSampleSad8x8_c;
# ifdef X86_ASM if ( iCpuFlag & WELS_CPU_SSE2) { m_pfSad = WelsSampleSad8x8_sse21; }
# endif
# ifdef HAVE_NEON if ( iCpuFlag & WELS_CPU_NEON) { m_pfSad = WelsProcessingSampleSad8x8_neon; }
# endif # ifdef HAVE_NEON_AARCH64 if ( iCpuFlag & WELS_CPU_NEON) { m_pfSad = WelsProcessingSampleSad8x8_AArch64_neon; }
# endif # ifdef HAVE_MMI if ( iCpuFlag & WELS_CPU_MMI) { m_pfSad = WelsSampleSad8x8_mmi; }
# endif m_fSceneChangeMotionRatioLarge = SCENE_CHANGE_MOTION_RATIO_LARGE_VIDEO; m_fSceneChangeMotionRatioMedium = SCENE_CHANGE_MOTION_RATIO_MEDIUM; } virtual ~ CSceneChangeDetectorVideo ( ) { } void operator ( ) ( SLocalParam& sLocalParam) { int32_t iRefRowStride = 0 , iCurRowStride = 0 ; uint8_t * pRefY = sLocalParam. pRefY; uint8_t * pCurY = sLocalParam. pCurY; uint8_t * pRefTmp = NULL , * pCurTmp = NULL ; iRefRowStride = sLocalParam. iRefStride << 3 ; iCurRowStride = sLocalParam. iCurStride << 3 ; for ( int32_t j = 0 ; j < sLocalParam. iBlock8x8Height; j++ ) { pRefTmp = pRefY; pCurTmp = pCurY; for ( int32_t i = 0 ; i < sLocalParam. iBlock8x8Width; i++ ) { int32_t iSad = m_pfSad ( pCurTmp, sLocalParam. iCurStride, pRefTmp, sLocalParam. iRefStride) ; m_sParam. iMotionBlockNum += iSad > HIGH_MOTION_BLOCK_THRESHOLD; pRefTmp += 8 ; pCurTmp += 8 ; } pRefY += iRefRowStride; pCurY += iCurRowStride; } } float GetSceneChangeMotionRatioLarge ( ) const { return m_fSceneChangeMotionRatioLarge; } float GetSceneChangeMotionRatioMedium ( ) const { return m_fSceneChangeMotionRatioMedium; } protected : SadFuncPtr m_pfSad; SSceneChangeResult& m_sParam; float m_fSceneChangeMotionRatioLarge; float m_fSceneChangeMotionRatioMedium;
} ;
int32_t WelsSampleSad8x8_c ( uint8_t * pSample1, int32_t iStride1, uint8_t * pSample2, int32_t iStride2) { int32_t iSadSum = 0 ; int32_t i = 0 ; uint8_t * pSrc1 = pSample1; uint8_t * pSrc2 = pSample2; for ( i = 0 ; i < 8 ; i++ ) { iSadSum += WELS_ABS ( ( pSrc1[ 0 ] - pSrc2[ 0 ] ) ) ; iSadSum += WELS_ABS ( ( pSrc1[ 1 ] - pSrc2[ 1 ] ) ) ; iSadSum += WELS_ABS ( ( pSrc1[ 2 ] - pSrc2[ 2 ] ) ) ; iSadSum += WELS_ABS ( ( pSrc1[ 3 ] - pSrc2[ 3 ] ) ) ; iSadSum += WELS_ABS ( ( pSrc1[ 4 ] - pSrc2[ 4 ] ) ) ; iSadSum += WELS_ABS ( ( pSrc1[ 5 ] - pSrc2[ 5 ] ) ) ; iSadSum += WELS_ABS ( ( pSrc1[ 6 ] - pSrc2[ 6 ] ) ) ; iSadSum += WELS_ABS ( ( pSrc1[ 7 ] - pSrc2[ 7 ] ) ) ; pSrc1 += iStride1; pSrc2 += iStride2; } return iSadSum;
}
CSceneChangeDetectorScreen类
功能 :通过CSceneChangeDetection类模板调用CSceneChangeDetectorScreen类中方法实现桌面视频场景变化检测功能类过程 :
第一步:同CSceneChangeDetectorVideo类中检测过程; 第二步:CSceneChangeDetectorScreen类中operator重载; 获取滚动检测标志bScrollDetectFlag和 mv 坐标iScrollMvX、iScrollMvY; 初始化工作; 双层 for 嵌套循环处理每个 8x8 图像块; 调用m_pfSad
函数计算 iSad; 如果 iSad 为 0,则标记当前块为COLLOCATED_STATIC,表明是静止的; 如果滚动检测标志为真,并且滚动向量不为空,当前块的坐标加上滚动向量后仍在图像范围内; 使用滚动向量调整参考块的位置pRefTmpScroll; 再次调用m_pfSad
函数,得到iSadScroll值; 如果 iSadScroll 为 0,则标记当前块为COLLOCATED_STATIC,表明是静止的; 否则, 将 iSad 累加到iFrameComplexity; 判断 iSad 与HIGH_MOTION_BLOCK_THRESHOLD的大小关系,将结果累加到iMotionBlockNum; 否则, 将 iSad 累加到iFrameComplexity; 判断 iSad 与HIGH_MOTION_BLOCK_THRESHOLD的大小关系,将结果累加到iMotionBlockNum; 更新图像块位置。
相关源码 :
template < typename T >
class CSceneChangeDetection : public IStrategy { public : CSceneChangeDetection ( EMethods eMethod, int32_t iCpuFlag) : m_cDetector ( m_sSceneChangeParam, iCpuFlag) { m_eMethod = eMethod; WelsMemset ( & m_sSceneChangeParam, 0 , sizeof ( m_sSceneChangeParam) ) ; } ~ CSceneChangeDetection ( ) { } EResult Process ( int32_t iType, SPixMap* pSrcPixMap, SPixMap* pRefPixMap) { EResult eReturn = RET_INVALIDPARAM; m_sLocalParam. iWidth = pSrcPixMap-> sRect. iRectWidth; m_sLocalParam. iHeight = pSrcPixMap-> sRect. iRectHeight; m_sLocalParam. iBlock8x8Width = m_sLocalParam. iWidth >> 3 ; m_sLocalParam. iBlock8x8Height = m_sLocalParam. iHeight >> 3 ; m_sLocalParam. pRefY = ( uint8_t * ) pRefPixMap-> pPixel[ 0 ] ; m_sLocalParam. pCurY = ( uint8_t * ) pSrcPixMap-> pPixel[ 0 ] ; m_sLocalParam. iRefStride = pRefPixMap-> iStride[ 0 ] ; m_sLocalParam. iCurStride = pSrcPixMap-> iStride[ 0 ] ; m_sLocalParam. pStaticBlockIdc = m_sSceneChangeParam. pStaticBlockIdc; int32_t iBlock8x8Num = m_sLocalParam. iBlock8x8Width * m_sLocalParam. iBlock8x8Height; int32_t iSceneChangeThresholdLarge = WelsStaticCast ( int32_t , m_cDetector. GetSceneChangeMotionRatioLarge ( ) * iBlock8x8Num + 0.5f + PESN) ; int32_t iSceneChangeThresholdMedium = WelsStaticCast ( int32_t , m_cDetector. GetSceneChangeMotionRatioMedium ( ) * iBlock8x8Num + 0.5f + PESN) ; m_sSceneChangeParam. iMotionBlockNum = 0 ; m_sSceneChangeParam. iFrameComplexity = 0 ; m_sSceneChangeParam. eSceneChangeIdc = SIMILAR_SCENE; m_cDetector ( m_sLocalParam) ; if ( m_sSceneChangeParam. iMotionBlockNum >= iSceneChangeThresholdLarge) { m_sSceneChangeParam. eSceneChangeIdc = LARGE_CHANGED_SCENE; } else if ( m_sSceneChangeParam. iMotionBlockNum >= iSceneChangeThresholdMedium) { m_sSceneChangeParam. eSceneChangeIdc = MEDIUM_CHANGED_SCENE; } eReturn = RET_SUCCESS; return eReturn; } EResult Get ( int32_t iType, void * pParam) { if ( pParam == NULL ) { return RET_INVALIDPARAM; } * ( SSceneChangeResult* ) pParam = m_sSceneChangeParam; return RET_SUCCESS; } EResult Set ( int32_t iType, void * pParam) { if ( pParam == NULL ) { return RET_INVALIDPARAM; } m_sSceneChangeParam = * ( SSceneChangeResult* ) pParam; return RET_SUCCESS; } private : SSceneChangeResult m_sSceneChangeParam; SLocalParam m_sLocalParam; T m_cDetector;
} ;
CSceneChangeDetectorScreen
类
class CSceneChangeDetectorScreen : public CSceneChangeDetectorVideo { public : CSceneChangeDetectorScreen ( SSceneChangeResult& sParam, int32_t iCpuFlag) : CSceneChangeDetectorVideo ( sParam, iCpuFlag) { m_fSceneChangeMotionRatioLarge = SCENE_CHANGE_MOTION_RATIO_LARGE_SCREEN; m_fSceneChangeMotionRatioMedium = SCENE_CHANGE_MOTION_RATIO_MEDIUM; } virtual ~ CSceneChangeDetectorScreen ( ) { } void operator ( ) ( SLocalParam& sLocalParam) { bool bScrollDetectFlag = m_sParam. sScrollResult. bScrollDetectFlag; int32_t iScrollMvX = m_sParam. sScrollResult. iScrollMvX; int32_t iScrollMvY = m_sParam. sScrollResult. iScrollMvY; int32_t iRefRowStride = 0 , iCurRowStride = 0 ; uint8_t * pRefY = sLocalParam. pRefY; uint8_t * pCurY = sLocalParam. pCurY; uint8_t * pRefTmp = NULL , * pCurTmp = NULL ; int32_t iWidth = sLocalParam. iWidth; int32_t iHeight = sLocalParam. iHeight; iRefRowStride = sLocalParam. iRefStride << 3 ; iCurRowStride = sLocalParam. iCurStride << 3 ; for ( int32_t j = 0 ; j < sLocalParam. iBlock8x8Height; j++ ) { pRefTmp = pRefY; pCurTmp = pCurY; for ( int32_t i = 0 ; i < sLocalParam. iBlock8x8Width; i++ ) { int32_t iBlockPointX = i << 3 ; int32_t iBlockPointY = j << 3 ; uint8_t uiBlockIdcTmp = NO_STATIC; int32_t iSad = m_pfSad ( pCurTmp, sLocalParam. iCurStride, pRefTmp, sLocalParam. iRefStride) ; if ( iSad == 0 ) { uiBlockIdcTmp = COLLOCATED_STATIC; } else if ( bScrollDetectFlag && ( ! iScrollMvX || ! iScrollMvY) && ( iBlockPointX + iScrollMvX >= 0 ) && ( iBlockPointX + iScrollMvX <= iWidth - 8 ) && ( iBlockPointY + iScrollMvY >= 0 ) && ( iBlockPointY + iScrollMvY <= iHeight - 8 ) ) { uint8_t * pRefTmpScroll = pRefTmp + iScrollMvY * sLocalParam. iRefStride + iScrollMvX; int32_t iSadScroll = m_pfSad ( pCurTmp, sLocalParam. iCurStride, pRefTmpScroll, sLocalParam. iRefStride) ; if ( iSadScroll == 0 ) { uiBlockIdcTmp = SCROLLED_STATIC; } else { m_sParam. iFrameComplexity += iSad; m_sParam. iMotionBlockNum += iSad > HIGH_MOTION_BLOCK_THRESHOLD; } } else { m_sParam. iFrameComplexity += iSad; m_sParam. iMotionBlockNum += iSad > HIGH_MOTION_BLOCK_THRESHOLD; } * ( sLocalParam. pStaticBlockIdc) ++ = uiBlockIdcTmp; pRefTmp += 8 ; pCurTmp += 8 ; } pRefY += iRefRowStride; pCurY += iCurRowStride; } }
} ;
int32_t WelsSampleSad8x8_c ( uint8_t * pSample1, int32_t iStride1, uint8_t * pSample2, int32_t iStride2) { int32_t iSadSum = 0 ; int32_t i = 0 ; uint8_t * pSrc1 = pSample1; uint8_t * pSrc2 = pSample2; for ( i = 0 ; i < 8 ; i++ ) { iSadSum += WELS_ABS ( ( pSrc1[ 0 ] - pSrc2[ 0 ] ) ) ; iSadSum += WELS_ABS ( ( pSrc1[ 1 ] - pSrc2[ 1 ] ) ) ; iSadSum += WELS_ABS ( ( pSrc1[ 2 ] - pSrc2[ 2 ] ) ) ; iSadSum += WELS_ABS ( ( pSrc1[ 3 ] - pSrc2[ 3 ] ) ) ; iSadSum += WELS_ABS ( ( pSrc1[ 4 ] - pSrc2[ 4 ] ) ) ; iSadSum += WELS_ABS ( ( pSrc1[ 5 ] - pSrc2[ 5 ] ) ) ; iSadSum += WELS_ABS ( ( pSrc1[ 6 ] - pSrc2[ 6 ] ) ) ; iSadSum += WELS_ABS ( ( pSrc1[ 7 ] - pSrc2[ 7 ] ) ) ; pSrc1 += iStride1; pSrc2 += iStride2; } return iSadSum;
}