566 lines
20 KiB
C++
566 lines
20 KiB
C++
#include <stdlib.h>
|
|
#include "gtest/gtest.h"
|
|
#include "utils/DataGenerator.h"
|
|
#include "md.h"
|
|
#include "sample.h"
|
|
#include "svc_motion_estimate.h"
|
|
#include "wels_func_ptr_def.h"
|
|
#include "cpu.h"
|
|
|
|
using namespace WelsEnc;
|
|
|
|
void CopyTargetBlock (uint8_t* pSrcBlock, const int32_t kiBlockSize, SMVUnitXY sTargetMv, const int32_t kiRefPicStride,
|
|
uint8_t* pRefPic) {
|
|
uint8_t* pTargetPos = pRefPic + sTargetMv.iMvY * kiRefPicStride + sTargetMv.iMvX;
|
|
uint8_t* pSourcePos = pSrcBlock;
|
|
|
|
for (int i = 0; i < kiBlockSize; i++) {
|
|
memcpy (pSourcePos, pTargetPos, kiBlockSize * sizeof (uint8_t));
|
|
pTargetPos += kiRefPicStride;
|
|
pSourcePos += kiBlockSize;
|
|
}
|
|
}
|
|
|
|
|
|
void InitMe (const uint8_t kuiQp, const uint32_t kuiMvdTableMiddle, const uint32_t kuiMvdTableStride,
|
|
uint16_t* pMvdCostTable, SWelsME* pMe) {
|
|
MvdCostInit (pMvdCostTable, kuiMvdTableStride);
|
|
pMe->pMvdCost = &pMvdCostTable[kuiQp * kuiMvdTableStride + kuiMvdTableMiddle];
|
|
pMe->sMvp.iMvX = pMe->sMvp.iMvY = 0;
|
|
pMe->sMvBase.iMvX = pMe->sMvBase.iMvY = 0;
|
|
pMe->sMv.iMvX = pMe->sMv.iMvY = 0;
|
|
}
|
|
|
|
class MotionEstimateTest : public ::testing::Test {
|
|
public:
|
|
virtual void SetUp() {
|
|
m_pRefData = NULL;
|
|
m_pSrcBlock = NULL;
|
|
m_pMvdCostTable = NULL;
|
|
|
|
m_iWidth = 64;//size of search window
|
|
m_iHeight = 64;//size of search window
|
|
m_iMaxSearchBlock = 16;
|
|
m_uiMvdTableSize = (1 + (648 << 1));
|
|
|
|
pMa = new CMemoryAlign (0);
|
|
m_pRefData = static_cast<uint8_t*>
|
|
(pMa->WelsMalloc (m_iWidth * m_iHeight, "RefPic"));
|
|
ASSERT_TRUE (NULL != m_pRefData);
|
|
m_pSrcBlock = static_cast<uint8_t*>
|
|
(pMa->WelsMalloc (m_iMaxSearchBlock * m_iMaxSearchBlock, "SrcBlock"));
|
|
ASSERT_TRUE (NULL != m_pSrcBlock);
|
|
m_pMvdCostTable = new uint16_t[52 * m_uiMvdTableSize];
|
|
ASSERT_TRUE (NULL != m_pMvdCostTable);
|
|
}
|
|
void DoLineTest (PLineFullSearchFunc func, bool horizontal);
|
|
virtual void TearDown() {
|
|
delete [] m_pMvdCostTable;
|
|
pMa->WelsFree (m_pRefData, "RefPic");
|
|
pMa->WelsFree (m_pSrcBlock, "SrcBlock");
|
|
delete pMa;
|
|
}
|
|
public:
|
|
uint8_t* m_pRefData;
|
|
uint8_t* m_pSrcBlock;
|
|
uint32_t m_uiMvdTableSize;
|
|
uint16_t* m_pMvdCostTable;
|
|
|
|
int32_t m_iWidth;
|
|
int32_t m_iHeight;
|
|
int32_t m_iMaxSearchBlock;
|
|
CMemoryAlign* pMa;
|
|
};
|
|
|
|
|
|
TEST_F (MotionEstimateTest, TestDiamondSearch) {
|
|
#define TEST_POS (5)
|
|
const int32_t kiPositionToCheck[TEST_POS][2] = {{0, 0}, {0, 1}, {1, 0}, {0, -1}, { -1, 0}};
|
|
const int32_t kiMaxBlock16Sad = 72000;//a rough number
|
|
SWelsFuncPtrList sFuncList;
|
|
SWelsME sMe;
|
|
SSlice sSlice;
|
|
memset (&sSlice, 0, sizeof (sSlice));
|
|
|
|
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_pRefData + (m_iHeight / 2) * m_iWidth + (m_iWidth / 2);
|
|
bool bDataGeneratorSucceed = false;
|
|
bool bFoundMatch = false;
|
|
int32_t i, iTryTimes;
|
|
for (i = 0; i < TEST_POS; i++) {
|
|
sTargetMv.iMvX = kiPositionToCheck[i][0];
|
|
sTargetMv.iMvY = kiPositionToCheck[i][1];
|
|
iTryTimes = 100;
|
|
bDataGeneratorSucceed = false;
|
|
bFoundMatch = false;
|
|
while (!bFoundMatch && (iTryTimes--) > 0) {
|
|
if (!YUVPixelDataGenerator (m_pRefData, 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.sMv.iMvX = sMe.sMv.iMvY = 0;
|
|
sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
|
|
WelsDiamondSearch (&sFuncList, &sMe, &sSlice, m_iMaxSearchBlock, m_iWidth);
|
|
|
|
//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)
|
|
bFoundMatch = ((sMe.sMv.iMvX == (sTargetMv.iMvX)) || (sMe.sMv.iMvX == 0)) && ((sMe.sMv.iMvY == (sTargetMv.iMvY))
|
|
|| (sMe.sMv.iMvY == 0));
|
|
}
|
|
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
|
|
}
|
|
}
|
|
}
|
|
|
|
class MotionEstimateRangeTest : public ::testing::Test {
|
|
public:
|
|
virtual void SetUp() {
|
|
|
|
m_iWidth = 320;
|
|
m_iHeight = 240;
|
|
m_iWidthExt = m_iWidth + 2 * PADDING_LENGTH;
|
|
m_iHeightExt = m_iHeight + 2 * PADDING_LENGTH;
|
|
m_iMbWidth = m_iWidth >> 4;
|
|
m_iMbHeight = m_iHeight >> 4;
|
|
|
|
m_iUsageType = 0;
|
|
m_iNumDependencyLayers = 1;
|
|
m_iMvRange = m_iUsageType ? EXPANDED_MV_RANGE : CAMERA_STARTMV_RANGE;
|
|
m_iMvdRange = (m_iUsageType ? EXPANDED_MVD_RANGE : ((m_iNumDependencyLayers == 1) ? CAMERA_MVD_RANGE :
|
|
CAMERA_HIGHLAYER_MVD_RANGE));
|
|
m_uiMvdInterTableSize = (m_iMvdRange << 2); //intepel*4=qpel
|
|
m_uiMvdInterTableStride = 1 + (m_uiMvdInterTableSize << 1);//qpel_mv_range*2=(+/-);
|
|
m_uiMvdCacheAlignedSize = m_uiMvdInterTableStride * sizeof (uint16_t);
|
|
|
|
m_pMa = new CMemoryAlign (16);
|
|
ASSERT_TRUE (NULL != m_pMa);
|
|
m_pMvdCostTable = (uint16_t*)m_pMa->WelsMallocz (52 * m_uiMvdCacheAlignedSize, "pMvdCostTable");
|
|
ASSERT_TRUE (NULL != m_pMvdCostTable);
|
|
m_pRefStart = (uint8_t*)m_pMa->WelsMallocz (m_iWidthExt * m_iHeightExt, "reference frame ");
|
|
ASSERT_TRUE (NULL != m_pRefStart);
|
|
m_pSrc = (uint8_t*)m_pMa->WelsMallocz (m_iWidth * m_iHeight, "source frame");
|
|
ASSERT_TRUE (NULL != m_pSrc);
|
|
|
|
}
|
|
virtual void TearDown() {
|
|
if (!m_pMa)
|
|
return;
|
|
if (m_pRefStart) {
|
|
m_pMa->WelsFree (m_pRefStart, "reference frame ");
|
|
m_pRefStart = NULL;
|
|
}
|
|
if (m_pSrc) {
|
|
m_pMa->WelsFree (m_pSrc, "source frame ");
|
|
m_pSrc = NULL;
|
|
}
|
|
if (m_pMvdCostTable) {
|
|
m_pMa->WelsFree (m_pMvdCostTable, "pMvdCostTable");
|
|
m_pMvdCostTable = NULL;
|
|
}
|
|
if (m_pMa) {
|
|
delete m_pMa;
|
|
m_pMa = NULL;
|
|
}
|
|
}
|
|
public:
|
|
uint8_t* m_pRefStart;
|
|
uint8_t* m_pSrc;
|
|
uint16_t* m_pMvdCostTable;
|
|
|
|
int32_t m_iWidth;
|
|
int32_t m_iHeight;
|
|
int32_t m_iWidthExt;
|
|
int32_t m_iHeightExt;
|
|
int32_t m_iMbWidth;
|
|
int32_t m_iMbHeight;
|
|
CMemoryAlign* m_pMa;
|
|
|
|
int32_t m_iMvRange;
|
|
int32_t m_iMvdRange;
|
|
uint32_t m_uiMvdInterTableSize;
|
|
uint32_t m_uiMvdInterTableStride;
|
|
uint32_t m_uiMvdCacheAlignedSize;
|
|
int32_t m_iUsageType;
|
|
int32_t m_iNumDependencyLayers;
|
|
};
|
|
|
|
TEST_F (MotionEstimateRangeTest, TestDiamondSearch) {
|
|
|
|
const int32_t kiMaxBlock16Sad = 72000;//a rough number
|
|
uint8_t* pRef = m_pRefStart + PADDING_LENGTH * m_iWidthExt + PADDING_LENGTH;
|
|
SWelsFuncPtrList sFuncList;
|
|
SWelsME sMe;
|
|
SSlice sSlice;
|
|
const uint8_t kuiQp = rand() % 52;
|
|
InitMe (kuiQp, m_uiMvdInterTableSize, m_uiMvdInterTableStride, m_pMvdCostTable, &sMe);
|
|
|
|
WelsInitSampleSadFunc (&sFuncList, 0); //test c functions
|
|
|
|
memset (&sSlice, 0, sizeof (sSlice));
|
|
memset (m_pSrc, 128, m_iWidth * m_iHeight);
|
|
memset (m_pRefStart, 0, m_iWidthExt * m_iHeightExt);
|
|
|
|
sMe.uiBlockSize = BLOCK_16x16; //
|
|
|
|
sMe.sMvp.iMvX = rand() % m_iMvRange;
|
|
sMe.sMvp.iMvY = rand() % m_iMvRange;
|
|
|
|
for (int h = 0; h < m_iHeight; h++)
|
|
memset (pRef + h * m_iWidthExt, h, m_iWidthExt);
|
|
|
|
sMe.pEncMb = m_pSrc;
|
|
|
|
sMe.sMv.iMvX = sMe.sMvp.iMvX;
|
|
sMe.sMv.iMvY = sMe.sMvp.iMvY;
|
|
|
|
sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
|
|
SetMvWithinIntegerMvRange (m_iMbWidth, m_iMbHeight, 0, 0, m_iMvRange,
|
|
& (sSlice.sMvStartMin), & (sSlice.sMvStartMax));
|
|
|
|
|
|
sMe.pRefMb = pRef + sMe.sMvp.iMvY * m_iWidthExt;
|
|
WelsDiamondSearch (&sFuncList, &sMe, &sSlice, m_iWidth, m_iWidthExt);
|
|
|
|
if ((WELS_ABS (sMe.sMv.iMvX) > m_iMvRange))
|
|
printf ("mvx = %d\n", sMe.sMv.iMvX);
|
|
ASSERT_TRUE (! (WELS_ABS (sMe.sMv.iMvX) > m_iMvRange));
|
|
if ((WELS_ABS (sMe.sMv.iMvY) > m_iMvRange))
|
|
printf ("mvy = %d\n", sMe.sMv.iMvY);
|
|
ASSERT_TRUE (! (WELS_ABS (sMe.sMv.iMvY) > m_iMvRange));
|
|
|
|
|
|
}
|
|
|
|
TEST_F (MotionEstimateRangeTest, TestWelsMotionCrossSearch) {
|
|
|
|
SWelsFuncPtrList sFuncList;
|
|
SWelsME sMe;
|
|
SSlice sSlice;
|
|
bool bUsageType = true;
|
|
uint8_t* pRef = m_pRefStart + PADDING_LENGTH * m_iWidthExt + PADDING_LENGTH;
|
|
const int32_t kiMaxBlock16Sad = 72000;//a rough number
|
|
|
|
memset (&sSlice, 0, sizeof (sSlice));
|
|
memset (&sMe, 0, sizeof (sMe));
|
|
WelsInitSampleSadFunc (&sFuncList, 0); //test c functions
|
|
WelsInitMeFunc (&sFuncList, 0, bUsageType);
|
|
|
|
RandomPixelDataGenerator (m_pSrc, m_iWidth, m_iHeight, m_iWidth);
|
|
RandomPixelDataGenerator (m_pRefStart, m_iWidthExt, m_iHeightExt, m_iWidthExt);
|
|
|
|
sMe.uiBlockSize = BLOCK_16x16; //
|
|
for (int32_t iMby = 0; iMby < m_iMbHeight; iMby++) {
|
|
for (int32_t iMbx = 0; iMbx < m_iMbWidth; iMbx++) {
|
|
|
|
const uint8_t kuiQp = rand() % 52;
|
|
|
|
InitMe (kuiQp, m_uiMvdInterTableSize, m_uiMvdInterTableStride, m_pMvdCostTable, &sMe);
|
|
SetMvWithinIntegerMvRange (m_iMbWidth, m_iMbHeight, iMbx , iMby, m_iMvRange,
|
|
& (sSlice.sMvStartMin), & (sSlice.sMvStartMax));
|
|
|
|
|
|
sMe.sMvp.iMvX = rand() % m_iMvRange;
|
|
sMe.sMvp.iMvY = rand() % m_iMvRange;
|
|
sMe.iCurMeBlockPixX = (iMbx << 4);
|
|
sMe.iCurMeBlockPixY = (iMby << 4);
|
|
sMe.pRefMb = pRef + sMe.iCurMeBlockPixX + sMe.iCurMeBlockPixY * m_iWidthExt;
|
|
sMe.pEncMb = m_pSrc + sMe.iCurMeBlockPixX + sMe.iCurMeBlockPixY * m_iWidth;
|
|
sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
|
|
sMe.pColoRefMb = sMe.pRefMb;
|
|
WelsMotionCrossSearch (&sFuncList, &sMe, &sSlice, m_iWidth, m_iWidthExt);
|
|
if ((WELS_ABS (sMe.sMv.iMvX) > m_iMvRange))
|
|
printf ("mvx = %d\n", sMe.sMv.iMvX);
|
|
ASSERT_TRUE (! (WELS_ABS (sMe.sMv.iMvX) > m_iMvRange));
|
|
if ((WELS_ABS (sMe.sMv.iMvY) > m_iMvRange))
|
|
printf ("mvy = %d\n", sMe.sMv.iMvY);
|
|
ASSERT_TRUE (! (WELS_ABS (sMe.sMv.iMvY) > m_iMvRange));
|
|
}
|
|
}
|
|
}
|
|
void MotionEstimateTest::DoLineTest (PLineFullSearchFunc func, bool vertical) {
|
|
const int32_t kiMaxBlock16Sad = 72000;//a rough number
|
|
SWelsFuncPtrList sFuncList;
|
|
SWelsME sMe;
|
|
|
|
const uint8_t kuiQp = rand() % 52;
|
|
InitMe (kuiQp, 648, m_uiMvdTableSize, m_pMvdCostTable, &sMe);
|
|
|
|
SMVUnitXY sTargetMv;
|
|
WelsInitSampleSadFunc (&sFuncList, 0); //test c functions
|
|
WelsInitMeFunc (&sFuncList, WelsCPUFeatureDetect (NULL), 1);
|
|
|
|
uint8_t* pRefPicCenter = m_pRefData + (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;
|
|
|
|
if (vertical) {
|
|
sTargetMv.iMvX = 0;
|
|
sTargetMv.iMvY = -sMe.iCurMeBlockPixY + INTPEL_NEEDED_MARGIN + rand() % (m_iHeight - 16 - 2 * INTPEL_NEEDED_MARGIN);
|
|
} else {
|
|
sTargetMv.iMvX = -sMe.iCurMeBlockPixX + INTPEL_NEEDED_MARGIN + rand() % (m_iWidth - 16 - 2 * INTPEL_NEEDED_MARGIN);
|
|
sTargetMv.iMvY = 0;
|
|
}
|
|
bDataGeneratorSucceed = false;
|
|
bFoundMatch = false;
|
|
while (!bFoundMatch && (iTryTimes--) > 0) {
|
|
if (!YUVPixelDataGenerator (m_pRefData, 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;
|
|
uint16_t* pMvdCost = vertical ? pMvdCostY : pMvdCostX;
|
|
int iSize = vertical ? m_iHeight : m_iWidth;
|
|
|
|
//the last selection may be affected by MVDcost, that is when smaller MvY will be better
|
|
if (vertical) {
|
|
func (&sFuncList, &sMe,
|
|
pMvdCost,
|
|
m_iMaxSearchBlock, m_iWidth,
|
|
INTPEL_NEEDED_MARGIN - sMe.iCurMeBlockPixY,
|
|
iSize - INTPEL_NEEDED_MARGIN - 16 - sMe.iCurMeBlockPixY, vertical);
|
|
bFoundMatch = (sMe.sMv.iMvX == 0
|
|
&& (sMe.sMv.iMvY == sTargetMv.iMvY || abs (sMe.sMv.iMvY) < abs (sTargetMv.iMvY)));
|
|
} else {
|
|
func (&sFuncList, &sMe,
|
|
pMvdCost,
|
|
m_iMaxSearchBlock, m_iWidth,
|
|
INTPEL_NEEDED_MARGIN - sMe.iCurMeBlockPixX,
|
|
iSize - INTPEL_NEEDED_MARGIN - 16 - sMe.iCurMeBlockPixX, vertical);
|
|
bFoundMatch = (sMe.sMv.iMvY == 0
|
|
&& (sMe.sMv.iMvX == sTargetMv.iMvX || abs (sMe.sMv.iMvX) < abs (sTargetMv.iMvX)));
|
|
}
|
|
//printf("DoLineTest 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, TestVerticalSearch) {
|
|
DoLineTest (LineFullSearch_c, true);
|
|
}
|
|
TEST_F (MotionEstimateTest, TestHorizontalSearch) {
|
|
DoLineTest (LineFullSearch_c, false);
|
|
}
|
|
|
|
#ifdef X86_ASM
|
|
TEST_F (MotionEstimateTest, TestVerticalSearch_SSE41) {
|
|
int32_t iTmp = 1;
|
|
uint32_t uiCPUFlags = WelsCPUFeatureDetect (&iTmp);
|
|
if ((uiCPUFlags & WELS_CPU_SSE41) == 0) return ;
|
|
|
|
DoLineTest (VerticalFullSearchUsingSSE41, true);
|
|
}
|
|
|
|
TEST_F (MotionEstimateTest, TestHorizontalSearch_SSE41) {
|
|
int32_t iTmp = 1;
|
|
uint32_t uiCPUFlags = WelsCPUFeatureDetect (&iTmp);
|
|
if ((uiCPUFlags & WELS_CPU_SSE41) == 0) return ;
|
|
|
|
DoLineTest (HorizontalFullSearchUsingSSE41, false);
|
|
}
|
|
#endif
|
|
|
|
class FeatureMotionEstimateTest : public ::testing::Test {
|
|
public:
|
|
virtual void SetUp() {
|
|
m_pRefData = NULL;
|
|
m_pSrcBlock = NULL;
|
|
m_pMvdCostTable = NULL;
|
|
|
|
m_iWidth = 64;//size of search window
|
|
m_iHeight = 64;//size of search window
|
|
m_iMaxSearchBlock = 8;
|
|
m_uiMvdTableSize = (1 + (648 << 1));
|
|
|
|
m_pMa = new CMemoryAlign (16);
|
|
ASSERT_TRUE (NULL != m_pMa);
|
|
m_pRefData = (uint8_t*)m_pMa->WelsMalloc (m_iWidth * m_iHeight * sizeof (uint8_t), "m_pRefData");
|
|
ASSERT_TRUE (NULL != m_pRefData);
|
|
m_pSrcBlock = (uint8_t*)m_pMa->WelsMalloc (m_iMaxSearchBlock * m_iMaxSearchBlock * sizeof (uint8_t), "m_pSrcBlock");
|
|
ASSERT_TRUE (NULL != m_pSrcBlock);
|
|
m_pMvdCostTable = (uint16_t*)m_pMa->WelsMalloc (52 * m_uiMvdTableSize * sizeof (uint16_t), "m_pMvdCostTable");
|
|
ASSERT_TRUE (NULL != m_pMvdCostTable);
|
|
m_pFeatureSearchPreparation = (SFeatureSearchPreparation*)m_pMa->WelsMalloc (sizeof (SFeatureSearchPreparation),
|
|
"m_pFeatureSearchPreparation");
|
|
ASSERT_TRUE (NULL != m_pFeatureSearchPreparation);
|
|
m_pScreenBlockFeatureStorage = (SScreenBlockFeatureStorage*)m_pMa->WelsMalloc (sizeof (SScreenBlockFeatureStorage),
|
|
"m_pScreenBlockFeatureStorage");
|
|
ASSERT_TRUE (NULL != m_pScreenBlockFeatureStorage);
|
|
}
|
|
virtual void TearDown() {
|
|
if (m_pMa) {
|
|
if (m_pRefData) {
|
|
m_pMa->WelsFree (m_pRefData, "m_pRefData");
|
|
m_pRefData = NULL;
|
|
}
|
|
if (m_pSrcBlock) {
|
|
m_pMa->WelsFree (m_pSrcBlock, "m_pSrcBlock");
|
|
m_pSrcBlock = NULL;
|
|
}
|
|
if (m_pMvdCostTable) {
|
|
m_pMa->WelsFree (m_pMvdCostTable, "m_pMvdCostTable");
|
|
m_pMvdCostTable = NULL;
|
|
}
|
|
|
|
if (m_pFeatureSearchPreparation) {
|
|
ReleaseFeatureSearchPreparation (m_pMa, m_pFeatureSearchPreparation->pFeatureOfBlock);
|
|
m_pMa->WelsFree (m_pFeatureSearchPreparation, "m_pFeatureSearchPreparation");
|
|
m_pFeatureSearchPreparation = NULL;
|
|
}
|
|
if (m_pScreenBlockFeatureStorage) {
|
|
ReleaseScreenBlockFeatureStorage (m_pMa, m_pScreenBlockFeatureStorage);
|
|
m_pMa->WelsFree (m_pScreenBlockFeatureStorage, "m_pScreenBlockFeatureStorage");
|
|
m_pScreenBlockFeatureStorage = NULL;
|
|
}
|
|
delete m_pMa;
|
|
m_pMa = NULL;
|
|
}
|
|
}
|
|
void InitRefPicForMeTest (SPicture* pRefPic) {
|
|
pRefPic->pData[0] = m_pRefData;
|
|
pRefPic->iLineSize[0] = m_iWidth;
|
|
pRefPic->iFrameAverageQp = rand() % 52;
|
|
pRefPic->iWidthInPixel = m_iWidth;
|
|
pRefPic->iHeightInPixel = m_iHeight;
|
|
}
|
|
public:
|
|
CMemoryAlign* m_pMa;
|
|
|
|
SFeatureSearchPreparation* m_pFeatureSearchPreparation;
|
|
SScreenBlockFeatureStorage* m_pScreenBlockFeatureStorage;
|
|
|
|
uint8_t* m_pRefData;
|
|
uint8_t* m_pSrcBlock;
|
|
uint16_t* m_pMvdCostTable;
|
|
uint32_t m_uiMvdTableSize;
|
|
|
|
int32_t m_iWidth;
|
|
int32_t m_iHeight;
|
|
int32_t m_iMaxSearchBlock;
|
|
};
|
|
|
|
TEST_F (FeatureMotionEstimateTest, TestFeatureSearch) {
|
|
const int32_t kiMaxBlock16Sad = 72000;//a rough number
|
|
SWelsFuncPtrList sFuncList;
|
|
WelsInitSampleSadFunc (&sFuncList, 0); //test c functions
|
|
WelsInitMeFunc (&sFuncList, 0, true);
|
|
|
|
SWelsME sMe;
|
|
const uint8_t kuiQp = rand() % 52;
|
|
InitMe (kuiQp, 648, m_uiMvdTableSize, m_pMvdCostTable, &sMe);
|
|
sMe.iCurMeBlockPixX = (m_iWidth / 2);
|
|
sMe.iCurMeBlockPixY = (m_iHeight / 2);
|
|
uint8_t* pRefPicCenter = m_pRefData + (m_iHeight / 2) * m_iWidth + (m_iWidth / 2);
|
|
|
|
SPicture sRef;
|
|
InitRefPicForMeTest (&sRef);
|
|
|
|
SSlice sSlice;
|
|
const int32_t kiSupposedPaddingLength = 16;
|
|
SetMvWithinIntegerMvRange (m_iWidth / 16 - kiSupposedPaddingLength, m_iHeight / 16 - kiSupposedPaddingLength,
|
|
m_iWidth / 2 / 16, m_iHeight / 2 / 16, 508,
|
|
& (sSlice.sMvStartMin), & (sSlice.sMvStartMax));
|
|
int32_t iReturn;
|
|
const int32_t kiNeedFeatureStorage = ME_DIA_CROSS_FME;
|
|
iReturn = RequestFeatureSearchPreparation (m_pMa, m_iWidth, m_iHeight, kiNeedFeatureStorage,
|
|
m_pFeatureSearchPreparation);
|
|
ASSERT_TRUE (ENC_RETURN_SUCCESS == iReturn);
|
|
iReturn = RequestScreenBlockFeatureStorage (m_pMa, m_iWidth, m_iHeight, kiNeedFeatureStorage,
|
|
m_pScreenBlockFeatureStorage);
|
|
ASSERT_TRUE (ENC_RETURN_SUCCESS == iReturn);
|
|
|
|
SMVUnitXY sTargetMv;
|
|
for (int i = sSlice.sMvStartMin.iMvX; i <= sSlice.sMvStartMax.iMvX; i++) {
|
|
for (int j = sSlice.sMvStartMin.iMvY; j <= sSlice.sMvStartMax.iMvY; j++) {
|
|
if (i == 0 || j == 0) continue; //exclude x=0 or y=0 since that will be skipped by FME
|
|
|
|
bool bDataGeneratorSucceed = false;
|
|
bool bFoundMatch = false;
|
|
|
|
if (!YUVPixelDataGenerator (m_pRefData, m_iWidth, m_iHeight, m_iWidth))
|
|
continue;
|
|
bDataGeneratorSucceed = true;
|
|
|
|
sTargetMv.iMvX = i;
|
|
sTargetMv.iMvY = j;
|
|
CopyTargetBlock (m_pSrcBlock, m_iMaxSearchBlock, sTargetMv, m_iWidth, pRefPicCenter);
|
|
|
|
//clean sMe status
|
|
sMe.uiBlockSize = BLOCK_8x8;
|
|
sMe.pEncMb = m_pSrcBlock;
|
|
sMe.pRefMb = pRefPicCenter;
|
|
sMe.pColoRefMb = pRefPicCenter;
|
|
sMe.sMv.iMvX = sMe.sMv.iMvY = 0;
|
|
sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
|
|
|
|
//begin FME process
|
|
PerformFMEPreprocess (&sFuncList, &sRef, m_pFeatureSearchPreparation->pFeatureOfBlock,
|
|
m_pScreenBlockFeatureStorage);
|
|
m_pScreenBlockFeatureStorage->uiSadCostThreshold[BLOCK_8x8] = UINT_MAX;//to avoid early skip
|
|
uint32_t uiMaxSearchPoint = INT_MAX;
|
|
SFeatureSearchIn sFeatureSearchIn = {0};
|
|
if (SetFeatureSearchIn (&sFuncList, sMe, &sSlice, m_pScreenBlockFeatureStorage,
|
|
m_iMaxSearchBlock, m_iWidth,
|
|
&sFeatureSearchIn)) {
|
|
MotionEstimateFeatureFullSearch (sFeatureSearchIn, uiMaxSearchPoint, &sMe);
|
|
}
|
|
|
|
bool bMvMatch = sMe.sMv.iMvX == sTargetMv.iMvX && sMe.sMv.iMvY == sTargetMv.iMvY;
|
|
bool bFeatureMatch =
|
|
(* (m_pScreenBlockFeatureStorage->pFeatureOfBlockPointer + (m_iHeight / 2 + sTargetMv.iMvY) * (m_iWidth - 8) +
|
|
(m_iWidth / 2 + sTargetMv.iMvX))
|
|
== * (m_pScreenBlockFeatureStorage->pFeatureOfBlockPointer + (m_iHeight / 2 + sMe.sMv.iMvY) * (m_iWidth - 8) +
|
|
(m_iWidth / 2 + sMe.sMv.iMvX)))
|
|
&& ((sMe.pMvdCost[sMe.sMv.iMvY << 2] + sMe.pMvdCost[sMe.sMv.iMvX << 2]) <= (sMe.pMvdCost[sTargetMv.iMvY << 2] +
|
|
sMe.pMvdCost[sTargetMv.iMvX << 2]));
|
|
|
|
//the last selection may be affected by MVDcost, that is when smaller Mv will be better
|
|
bFoundMatch = bMvMatch || bFeatureMatch;
|
|
|
|
if (bDataGeneratorSucceed) {
|
|
//if DataGenerator never succeed, there is no meaning to check iTryTimes
|
|
if (!bFoundMatch) {
|
|
printf ("TestFeatureSearch Target: %d,%d, Result: %d,%d\n", sTargetMv.iMvX, sTargetMv.iMvY, sMe.sMv.iMvX, sMe.sMv.iMvY);
|
|
}
|
|
EXPECT_TRUE (bFoundMatch);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|