ref: b4be8f3f1a6a42e3edf51ddf59b9fb8f2ac55572
parent: 54f5f3472565c2ad5a38b80d7c3154622507723a
parent: bbe016543fd2d82f941318901af1d40231f36869
author: Licai Guo <licaguo@cisco.com>
date: Wed Mar 26 12:37:49 EDT 2014
Merge pull request #586 from sijchen/me_merge72 [Encoder ME] Add basic cross search functions and its unit tests
--- a/codec/encoder/core/inc/svc_motion_estimate.h
+++ b/codec/encoder/core/inc/svc_motion_estimate.h
@@ -67,7 +67,7 @@
uint32_t uiSadCostThreshold;
int32_t iCurMeBlockPixX;
int32_t iCurMeBlockPixY;
-uint8_t uiPixel; /* PIXEL_WxH */
+uint8_t uiBlockSize; /* BLOCK_WxH */
uint8_t uiReserved;
uint8_t* pEncMb;
@@ -147,7 +147,14 @@
const SMVUnitXY ksMinMv, const SMVUnitXY ksMaxMv, const int32_t kiEncStride, const int32_t kiRefStride,
int32_t& iBestSadCost);
-inline void SetMvWithinMvRange( const int32_t kiMbWidth, const int32_t kiMbHeight, const int32_t kiMbX, const int32_t kiMbY,
+void LineFullSearch_c( PSampleSadSatdCostFunc pSad, void *vpMe,
+ uint16_t* pMvdTable, const int32_t kiFixedMvd,
+ const int32_t kiEncStride, const int32_t kiRefStride,
+ const int32_t kiMinPos, const int32_t kiMaxPos,
+ const bool bVerticalSearch );
+void WelsMotionCrossSearch(SWelsFuncPtrList *pFuncList, SDqLayer* pCurLayer, SWelsME * pMe, const SSlice* pSlice);
+
+inline void SetMvWithinIntegerMvRange( const int32_t kiMbWidth, const int32_t kiMbHeight, const int32_t kiMbX, const int32_t kiMbY,
const int32_t kiMaxMvRange,
SMVUnitXY* pMvMin, SMVUnitXY* pMvMax)
{
--- a/codec/encoder/core/src/encoder_ext.cpp
+++ b/codec/encoder/core/src/encoder_ext.cpp
@@ -2499,7 +2499,7 @@
void PreprocessSliceCoding (sWelsEncCtx* pCtx) {
SDqLayer* pCurLayer = pCtx->pCurDqLayer;
- const bool kbBaseAvail = pCurLayer->bBaseLayerAvailableFlag;
+ //const bool kbBaseAvail = pCurLayer->bBaseLayerAvailableFlag;
const bool kbHighestSpatialLayer =
(pCtx->pSvcParam->iSpatialLayerNum == (pCurLayer->sLayerInfo.sNalHeaderExt.uiDependencyId + 1));
SWelsFuncPtrList* pFuncList = pCtx->pFuncList;
--- a/codec/encoder/core/src/md.cpp
+++ b/codec/encoder/core/src/md.cpp
@@ -536,7 +536,7 @@
int32_t iCurCost;
uint8_t* pEncMb = pMe->pEncMb;
uint8_t* pTmp = NULL;
- const uint8_t kuiPixel = pMe->uiPixel;
+ const uint8_t kuiPixel = pMe->uiBlockSize;
pSampleAvg[kiAvgIndex] (pMeRefine->pQuarPixTmp, ME_REFINE_BUF_STRIDE, pParams->pSrcA[0], ME_REFINE_BUF_STRIDE,
pParams->pSrcB[0], pParams->iStrideA, kiHeight);
@@ -603,7 +603,7 @@
&& (pFunc->sSampleDealingFuncs.pfMdCost == pFunc->sSampleDealingFuncs.pfSampleSatd)) {
iBestCost = pMe->uSadPredISatd.uiSatd + COST_MVD (pMe->pMvdCost, iMvx - pMe->sMvp.iMvX, iMvy - pMe->sMvp.iMvY);
} else {
- iBestCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiPixel] (pEncData, kiStrideEnc, pRef, kiStrideRef) +
+ iBestCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiBlockSize] (pEncData, kiStrideEnc, pRef, kiStrideRef) +
COST_MVD (pMe->pMvdCost, iMvx - pMe->sMvp.iMvX, iMvy - pMe->sMvp.iMvY);
}
@@ -614,7 +614,7 @@
//step 1: get [iWidth][iHeight+1] half pixel from vertical filter
//===========================(0, -2)==============================//
- iCurCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiPixel] (pEncData, kiStrideEnc, pMeRefine->pHalfPixV,
+ iCurCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiBlockSize] (pEncData, kiStrideEnc, pMeRefine->pHalfPixV,
ME_REFINE_BUF_STRIDE) +
COST_MVD (pMe->pMvdCost, iMvx - pMe->sMvp.iMvX, iMvy - 2 - pMe->sMvp.iMvY);
if (iCurCost < iBestCost) {
@@ -623,7 +623,7 @@
pBestPredInter = pMeRefine->pHalfPixV;
}
//===========================(0, 2)==============================//
- iCurCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiPixel] (pEncData, kiStrideEnc,
+ iCurCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiBlockSize] (pEncData, kiStrideEnc,
pMeRefine->pHalfPixV + ME_REFINE_BUF_STRIDE, ME_REFINE_BUF_STRIDE) +
COST_MVD (pMe->pMvdCost, iMvx - pMe->sMvp.iMvX, iMvy + 2 - pMe->sMvp.iMvY);
if (iCurCost < iBestCost) {
@@ -636,7 +636,7 @@
//step 2: get [iWidth][iHeight+1] half pixel from horizon filter
//===========================(-2, 0)==============================//
- iCurCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiPixel] (pEncData, kiStrideEnc, pMeRefine->pHalfPixH,
+ iCurCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiBlockSize] (pEncData, kiStrideEnc, pMeRefine->pHalfPixH,
ME_REFINE_BUF_STRIDE) +
COST_MVD (pMe->pMvdCost, iMvx - 2 - pMe->sMvp.iMvX, iMvy - pMe->sMvp.iMvY);
if (iCurCost < iBestCost) {
@@ -645,7 +645,7 @@
pBestPredInter = pMeRefine->pHalfPixH;
}
//===========================(2, 0)===============================//
- iCurCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiPixel] (pEncData, kiStrideEnc, pMeRefine->pHalfPixH + 1,
+ iCurCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiBlockSize] (pEncData, kiStrideEnc, pMeRefine->pHalfPixH + 1,
ME_REFINE_BUF_STRIDE) +
COST_MVD (pMe->pMvdCost, iMvx + 2 - pMe->sMvp.iMvX, iMvy - pMe->sMvp.iMvY);
if (iCurCost < iBestCost) {
--- a/codec/encoder/core/src/svc_base_layer_md.cpp
+++ b/codec/encoder/core/src/svc_base_layer_md.cpp
@@ -359,7 +359,7 @@
ST32 (&pCurMb->sP16x16Mv, 0);
ST32 (&pCurLayer->pDecPic->sMvList[kiMbXY], 0);
- SetMvWithinMvRange( kiMbWidth, kiMbHeight, kiMbX, kiMbY, CAMERA_STARTMV_RANGE, &(pSlice->sMvStartMin), &(pSlice->sMvStartMax));
+ SetMvWithinIntegerMvRange( kiMbWidth, kiMbHeight, kiMbX, kiMbY, CAMERA_STARTMV_RANGE, &(pSlice->sMvStartMin), &(pSlice->sMvStartMax));
}
int32_t WelsMdI16x16 (SWelsFuncPtrList* pFunc, SDqLayer* pCurDqLayer, SMbCache* pMbCache, int32_t iLambda) {
@@ -975,7 +975,7 @@
{
sWelsMe.iCurMeBlockPixX = sWelsMd.iMbPixX;
sWelsMe.iCurMeBlockPixY = sWelsMd.iMbPixY;
- sWelsMe.uiPixel = iBlockSize;
+ sWelsMe.uiBlockSize = iBlockSize;
sWelsMe.pMvdCost = sWelsMd.pMvdCost;
sWelsMe.pEncMb = pEnc;
--- a/codec/encoder/core/src/svc_motion_estimate.cpp
+++ b/codec/encoder/core/src/svc_motion_estimate.cpp
@@ -81,7 +81,7 @@
MeEndIntepelSearch(pMe);
}
- pFuncList->pfCalculateSatd( pFuncList->sSampleDealingFuncs.pfSampleSatd[pMe->uiPixel], pMe, iStrideEnc, iStrideRef );
+ pFuncList->pfCalculateSatd( pFuncList->sSampleDealingFuncs.pfSampleSatd[pMe->uiBlockSize], pMe, iStrideEnc, iStrideRef );
}
/*!
@@ -96,7 +96,7 @@
*/
bool WelsMotionEstimateInitialPoint (SWelsFuncPtrList* pFuncList, SWelsME* pMe, SSlice* pSlice, int32_t iStrideEnc,
int32_t iStrideRef) {
- PSampleSadSatdCostFunc pSad = pFuncList->sSampleDealingFuncs.pfSampleSad[pMe->uiPixel];
+ PSampleSadSatdCostFunc pSad = pFuncList->sSampleDealingFuncs.pfSampleSad[pMe->uiBlockSize];
const uint16_t* kpMvdCost = pMe->pMvdCost;
uint8_t* const kpEncMb = pMe->pEncMb;
int16_t iMvc0, iMvc1;
@@ -197,7 +197,7 @@
void WelsMotionEstimateIterativeSearch (SWelsFuncPtrList* pFuncList, SWelsME* pMe, const int32_t kiStrideEnc,
const int32_t kiStrideRef, uint8_t* pFref) {
- PSample4SadCostFunc pSad = pFuncList->sSampleDealingFuncs.pfSample4Sad[pMe->uiPixel];
+ PSample4SadCostFunc pSad = pFuncList->sSampleDealingFuncs.pfSample4Sad[pMe->uiBlockSize];
uint8_t* const kpEncMb = pMe->pEncMb;
const uint16_t* kpMvdCost = pMe->pMvdCost;
@@ -255,7 +255,7 @@
const int16_t kiMvY = pMe->sDirectionalMv.iMvY;
//Check MV from scrolling detection
- if ( (BLOCK_16x16!=pMe->uiPixel) //scrolled_MV with P16x16 is checked SKIP checking function
+ if ( (BLOCK_16x16!=pMe->uiBlockSize) //scrolled_MV with P16x16 is checked SKIP checking function
&& ( kiMvX | kiMvY ) //(0,0) checked in ordinary initial point checking
&& CheckMvInRange( pMe->sDirectionalMv, ksMinMv, ksMaxMv ) )
{
@@ -278,4 +278,70 @@
return false;
}
+void LineFullSearch_c( PSampleSadSatdCostFunc pSad, void *vpMe,
+ uint16_t* pMvdTable, const int32_t kiFixedMvd,
+ const int32_t kiEncStride, const int32_t kiRefStride,
+ const int32_t kiMinPos, const int32_t kiMaxPos,
+ const bool bVerticalSearch )
+{
+ SWelsME *pMe = static_cast<SWelsME *>(vpMe);
+ const int32_t kiCurMeBlockPix = bVerticalSearch?pMe->iCurMeBlockPixY:pMe->iCurMeBlockPixX;
+ const int32_t kiStride = bVerticalSearch?kiRefStride:1;
+ uint8_t* pRef = &pMe->pColoRefMb[(kiMinPos - kiCurMeBlockPix)*kiStride];
+ uint16_t* pMvdCost = &(pMvdTable[kiMinPos<<2]);
+ uint32_t uiBestCost = 0xFFFFFFFF;
+ int32_t iBestPos = 0;
+
+ for ( int32_t iTargetPos = kiMinPos; iTargetPos < kiMaxPos; ++ iTargetPos ) {
+ uint8_t* const kpEncMb = pMe->pEncMb;
+ uint32_t uiSadCost = pSad( kpEncMb, kiEncStride, pRef, kiRefStride ) + (kiFixedMvd + *pMvdCost);
+ if (uiSadCost < uiBestCost) {
+ uiBestCost = uiSadCost;
+ iBestPos = iTargetPos;
+ }
+ pRef += kiStride;
+ pMvdCost+=4;
+ }
+
+ if (uiBestCost < pMe->uiSadCost) {
+ SMVUnitXY sBestMv;
+ sBestMv.iMvX = bVerticalSearch?0:(iBestPos - kiCurMeBlockPix);
+ sBestMv.iMvY = bVerticalSearch?(iBestPos - kiCurMeBlockPix):0;
+ UpdateMeResults( sBestMv, uiBestCost, &pMe->pColoRefMb[sBestMv.iMvY*kiStride], pMe );
+ }
+}
+
+void WelsMotionCrossSearch(SWelsFuncPtrList *pFuncList, SDqLayer* pCurLayer, SWelsME * pMe,
+ const SSlice* pSlice)
+{
+ //TODO:
+ //PMOTION_VERFULL_SEARCH VerticalFullSearchFunc = pFuncList->VerticalFullSearch_c;
+ //PMOTION_HORFULL_SEARCH HorizontalFullSearchFunc = pFuncList->HorizontalFullSearch_c;
+ const int32_t kiEncStride = pCurLayer->iEncStride[0];
+ const int32_t kiRefStride = pCurLayer->pRefPic->iLineSize[0];
+
+ const int32_t iCurMeBlockPixX = pMe->iCurMeBlockPixX;
+ const int32_t iCurMeBlockQpelPixX = ((iCurMeBlockPixX)<<2);
+ const int32_t iCurMeBlockPixY = pMe->iCurMeBlockPixY;
+ const int32_t iCurMeBlockQpelPixY = ((iCurMeBlockPixY)<<2);
+ uint16_t* pMvdCostX = pMe->pMvdCost - iCurMeBlockQpelPixX - pMe->sMvp.iMvX;//do the offset here instead of in the search
+ uint16_t* pMvdCostY = pMe->pMvdCost - iCurMeBlockQpelPixY - pMe->sMvp.iMvY;//do the offset here instead of in the search
+
+ //vertical search
+ LineFullSearch_c( pFuncList->sSampleDealingFuncs.pfSampleSad[pMe->uiBlockSize], pMe,
+ pMvdCostY, pMvdCostX[ iCurMeBlockQpelPixX ],
+ kiEncStride, kiRefStride,
+ iCurMeBlockPixY + pSlice->sMvStartMin.iMvY,
+ iCurMeBlockPixY + pSlice->sMvStartMax.iMvY, true );
+
+ //horizontal search
+ if (pMe->uiSadCost >= pMe->uiSadCostThreshold) {
+ LineFullSearch_c( pFuncList->sSampleDealingFuncs.pfSampleSad[pMe->uiBlockSize], pMe,
+ pMvdCostX, pMvdCostY[ iCurMeBlockQpelPixY ],
+ kiEncStride, kiRefStride,
+ iCurMeBlockPixX + pSlice->sMvStartMin.iMvX,
+ iCurMeBlockPixX + pSlice->sMvStartMax.iMvX,
+ false );
+ }
+}
} // namespace WelsSVCEnc
--- a/test/encoder/EncUT_MotionEstimate.cpp
+++ b/test/encoder/EncUT_MotionEstimate.cpp
@@ -104,13 +104,13 @@
CopyTargetBlock( m_pSrcBlock, 16, sTargetMv, m_iWidth, pRefPicCenter);
//clean the sMe status
- sMe.uiPixel = rand()%5;
+ sMe.uiBlockSize = rand()%5;
sMe.pEncMb = m_pSrcBlock;
sMe.pRefMb = pRefPicCenter;
sMe.sMv.iMvX = sMe.sMv.iMvY = 0;
sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
WelsMotionEstimateIterativeSearch (&sFuncList, &sMe, m_iMaxSearchBlock,
- m_iWidth, pRefPicCenter);
+ m_iWidth, pRefPicCenter);
//the last selection may be affected by MVDcost, that is when (0,0) will be better
//when comparing (1,1) and (1,0), due to the difference between MVD cost, it is possible that (1,0) is selected while the best match is (1,1)
@@ -123,3 +123,129 @@
}
}
}
+
+
+TEST_F(MotionEstimateTest, TestVerticalSearch)
+{
+ const int32_t kiMaxBlock16Sad = 72000;//a rough number
+ SWelsFuncPtrList sFuncList;
+ SWelsME sMe;
+
+ srand((uint32_t)time(NULL));
+ const uint8_t kuiQp = rand()%52;
+ InitMe(kuiQp, 648, m_uiMvdTableSize, m_pMvdCostTable, &sMe);
+
+ SMVUnitXY sTargetMv;
+ WelsInitSampleSadFunc( &sFuncList, 0 );//test c functions
+
+ uint8_t *pRefPicCenter = m_pRefPic+(m_iHeight/2)*m_iWidth+(m_iWidth/2);
+ sMe.iCurMeBlockPixX = (m_iWidth/2);
+ sMe.iCurMeBlockPixY = (m_iHeight/2);
+
+ bool bDataGeneratorSucceed = false;
+ bool bFoundMatch = false;
+ int32_t iTryTimes=100;
+
+ sTargetMv.iMvX = 0;
+ sTargetMv.iMvY = WELS_MAX(INTPEL_NEEDED_MARGIN, rand()%m_iHeight-INTPEL_NEEDED_MARGIN);
+ bDataGeneratorSucceed = false;
+ bFoundMatch = false;
+ while (!bFoundMatch && (iTryTimes--)>0) {
+ if (!YUVPixelDataGenerator( m_pRefPic, m_iWidth, m_iHeight, m_iWidth ))
+ continue;
+
+ bDataGeneratorSucceed = true;
+ CopyTargetBlock( m_pSrcBlock, 16, sTargetMv, m_iWidth, pRefPicCenter);
+
+ //clean the sMe status
+ sMe.uiBlockSize = rand()%5;
+ sMe.pEncMb = m_pSrcBlock;
+ sMe.pRefMb = pRefPicCenter;
+ sMe.pColoRefMb = pRefPicCenter;
+ sMe.sMv.iMvX = sMe.sMv.iMvY = 0;
+ sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
+ const int32_t iCurMeBlockPixX = sMe.iCurMeBlockPixX;
+ const int32_t iCurMeBlockQpelPixX = ((iCurMeBlockPixX)<<2);
+ const int32_t iCurMeBlockPixY = sMe.iCurMeBlockPixY;
+ const int32_t iCurMeBlockQpelPixY = ((iCurMeBlockPixY)<<2);
+ uint16_t* pMvdCostX = sMe.pMvdCost - iCurMeBlockQpelPixX - sMe.sMvp.iMvX; //do the offset here
+ uint16_t* pMvdCostY = sMe.pMvdCost - iCurMeBlockQpelPixY - sMe.sMvp.iMvY;
+ LineFullSearch_c ( sFuncList.sSampleDealingFuncs.pfSampleSad[sMe.uiBlockSize], &sMe,
+ pMvdCostY, pMvdCostX[ iCurMeBlockQpelPixX ],
+ m_iMaxSearchBlock, m_iWidth,
+ INTPEL_NEEDED_MARGIN,
+ m_iHeight-INTPEL_NEEDED_MARGIN, true );
+
+ //the last selection may be affected by MVDcost, that is when smaller MvY will be better
+ bFoundMatch = (sMe.sMv.iMvX==0
+ &&(sMe.sMv.iMvY==sTargetMv.iMvY||abs(sMe.sMv.iMvY)<abs(sTargetMv.iMvY)));
+ //printf("TestVerticalSearch Target: %d,%d\n", sTargetMv.iMvX, sTargetMv.iMvY);
+ }
+ if (bDataGeneratorSucceed) {
+ //if DataGenerator never succeed, there is no meaning to check iTryTimes
+ ASSERT_TRUE(iTryTimes > 0);
+ //it is possible that ref at differnt position is identical, but that should be under a low probability
+ }
+}
+TEST_F(MotionEstimateTest, TestHorizontalSearch)
+{
+ const int32_t kiMaxBlock16Sad = 72000;//a rough number
+ SWelsFuncPtrList sFuncList;
+ SWelsME sMe;
+
+ srand((uint32_t)time(NULL));
+ const uint8_t kuiQp = rand()%52;
+ InitMe(kuiQp, 648, m_uiMvdTableSize, m_pMvdCostTable, &sMe);
+
+ SMVUnitXY sTargetMv;
+ WelsInitSampleSadFunc( &sFuncList, 0 );//test c functions
+
+ uint8_t *pRefPicCenter = m_pRefPic+(m_iHeight/2)*m_iWidth+(m_iWidth/2);
+ sMe.iCurMeBlockPixX = (m_iWidth/2);
+ sMe.iCurMeBlockPixY = (m_iHeight/2);
+
+ bool bDataGeneratorSucceed = false;
+ bool bFoundMatch = false;
+ int32_t iTryTimes=100;
+
+ sTargetMv.iMvX = WELS_MAX(INTPEL_NEEDED_MARGIN, rand()%m_iWidth-INTPEL_NEEDED_MARGIN);
+ sTargetMv.iMvY = 0;
+ bDataGeneratorSucceed = false;
+ bFoundMatch = false;
+ while (!bFoundMatch && (iTryTimes--)>0) {
+ if (!YUVPixelDataGenerator( m_pRefPic, m_iWidth, m_iHeight, m_iWidth ))
+ continue;
+
+ bDataGeneratorSucceed = true;
+ CopyTargetBlock( m_pSrcBlock, 16, sTargetMv, m_iWidth, pRefPicCenter);
+
+ //clean the sMe status
+ sMe.uiBlockSize = rand()%5;
+ sMe.pEncMb = m_pSrcBlock;
+ sMe.pRefMb = pRefPicCenter;
+ sMe.pColoRefMb = pRefPicCenter;
+ sMe.sMv.iMvX = sMe.sMv.iMvY = 0;
+ sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
+ const int32_t iCurMeBlockPixX = sMe.iCurMeBlockPixX;
+ const int32_t iCurMeBlockQpelPixX = ((iCurMeBlockPixX)<<2);
+ const int32_t iCurMeBlockPixY = sMe.iCurMeBlockPixY;
+ const int32_t iCurMeBlockQpelPixY = ((iCurMeBlockPixY)<<2);
+ uint16_t* pMvdCostX = sMe.pMvdCost - iCurMeBlockQpelPixX - sMe.sMvp.iMvX; //do the offset here
+ uint16_t* pMvdCostY = sMe.pMvdCost - iCurMeBlockQpelPixY - sMe.sMvp.iMvY;
+ LineFullSearch_c ( sFuncList.sSampleDealingFuncs.pfSampleSad[sMe.uiBlockSize], &sMe,
+ pMvdCostX, pMvdCostY[ iCurMeBlockQpelPixY ],
+ m_iMaxSearchBlock, m_iWidth,
+ INTPEL_NEEDED_MARGIN,
+ m_iWidth-INTPEL_NEEDED_MARGIN, false );
+
+ //the last selection may be affected by MVDcost, that is when smaller MvY will be better
+ bFoundMatch = (sMe.sMv.iMvY==0
+ &&(sMe.sMv.iMvX==sTargetMv.iMvX||abs(sMe.sMv.iMvX)<abs(sTargetMv.iMvX)));
+ //printf("TestHorizontalSearch Target: %d,%d\n", sTargetMv.iMvX, sTargetMv.iMvY);
+ }
+ if (bDataGeneratorSucceed) {
+ //if DataGenerator never succeed, there is no meaning to check iTryTimes
+ ASSERT_TRUE(iTryTimes > 0);
+ //it is possible that ref at differnt position is identical, but that should be under a low probability
+ }
+}
\ No newline at end of file