文件位置
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;
}