1 #include <stdlib.h>
2 #include "gtest/gtest.h"
3 #include "utils/DataGenerator.h"
4 #include "md.h"
5 #include "sample.h"
6 #include "svc_motion_estimate.h"
7 #include "wels_func_ptr_def.h"
8 #include "cpu.h"
9
10 using namespace WelsEnc;
11
CopyTargetBlock(uint8_t * pSrcBlock,const int32_t kiBlockSize,SMVUnitXY sTargetMv,const int32_t kiRefPicStride,uint8_t * pRefPic)12 void CopyTargetBlock (uint8_t* pSrcBlock, const int32_t kiBlockSize, SMVUnitXY sTargetMv, const int32_t kiRefPicStride,
13 uint8_t* pRefPic) {
14 uint8_t* pTargetPos = pRefPic + sTargetMv.iMvY * kiRefPicStride + sTargetMv.iMvX;
15 uint8_t* pSourcePos = pSrcBlock;
16
17 for (int i = 0; i < kiBlockSize; i++) {
18 memcpy (pSourcePos, pTargetPos, kiBlockSize * sizeof (uint8_t));
19 pTargetPos += kiRefPicStride;
20 pSourcePos += kiBlockSize;
21 }
22 }
23
24
InitMe(const uint8_t kuiQp,const uint32_t kuiMvdTableMiddle,const uint32_t kuiMvdTableStride,uint16_t * pMvdCostTable,SWelsME * pMe)25 void InitMe (const uint8_t kuiQp, const uint32_t kuiMvdTableMiddle, const uint32_t kuiMvdTableStride,
26 uint16_t* pMvdCostTable, SWelsME* pMe) {
27 MvdCostInit (pMvdCostTable, kuiMvdTableStride);
28 pMe->pMvdCost = &pMvdCostTable[kuiQp * kuiMvdTableStride + kuiMvdTableMiddle];
29 pMe->sMvp.iMvX = pMe->sMvp.iMvY = 0;
30 pMe->sMvBase.iMvX = pMe->sMvBase.iMvY = 0;
31 pMe->sMv.iMvX = pMe->sMv.iMvY = 0;
32 }
33
34 class MotionEstimateTest : public ::testing::Test {
35 public:
SetUp()36 virtual void SetUp() {
37 m_pRefData = NULL;
38 m_pSrcBlock = NULL;
39 m_pMvdCostTable = NULL;
40
41 m_iWidth = 64;//size of search window
42 m_iHeight = 64;//size of search window
43 m_iMaxSearchBlock = 16;
44 m_uiMvdTableSize = (1 + (648 << 1));
45
46 pMa = new CMemoryAlign (0);
47 m_pRefData = static_cast<uint8_t*>
48 (pMa->WelsMalloc (m_iWidth * m_iHeight, "RefPic"));
49 ASSERT_TRUE (NULL != m_pRefData);
50 m_pSrcBlock = static_cast<uint8_t*>
51 (pMa->WelsMalloc (m_iMaxSearchBlock * m_iMaxSearchBlock, "SrcBlock"));
52 ASSERT_TRUE (NULL != m_pSrcBlock);
53 m_pMvdCostTable = new uint16_t[52 * m_uiMvdTableSize];
54 ASSERT_TRUE (NULL != m_pMvdCostTable);
55 }
56 void DoLineTest (PLineFullSearchFunc func, bool horizontal);
TearDown()57 virtual void TearDown() {
58 delete [] m_pMvdCostTable;
59 pMa->WelsFree (m_pRefData, "RefPic");
60 pMa->WelsFree (m_pSrcBlock, "SrcBlock");
61 delete pMa;
62 }
63 public:
64 uint8_t* m_pRefData;
65 uint8_t* m_pSrcBlock;
66 uint32_t m_uiMvdTableSize;
67 uint16_t* m_pMvdCostTable;
68
69 int32_t m_iWidth;
70 int32_t m_iHeight;
71 int32_t m_iMaxSearchBlock;
72 CMemoryAlign* pMa;
73 };
74
75
TEST_F(MotionEstimateTest,TestDiamondSearch)76 TEST_F (MotionEstimateTest, TestDiamondSearch) {
77 #define TEST_POS (5)
78 const int32_t kiPositionToCheck[TEST_POS][2] = {{0, 0}, {0, 1}, {1, 0}, {0, -1}, { -1, 0}};
79 const int32_t kiMaxBlock16Sad = 72000;//a rough number
80 SWelsFuncPtrList sFuncList;
81 SWelsME sMe;
82 SSlice sSlice;
83 memset (&sSlice, 0, sizeof (sSlice));
84
85 const uint8_t kuiQp = rand() % 52;
86 InitMe (kuiQp, 648, m_uiMvdTableSize, m_pMvdCostTable, &sMe);
87
88 SMVUnitXY sTargetMv;
89 WelsInitSampleSadFunc (&sFuncList, 0); //test c functions
90
91 uint8_t* pRefPicCenter = m_pRefData + (m_iHeight / 2) * m_iWidth + (m_iWidth / 2);
92 bool bDataGeneratorSucceed = false;
93 bool bFoundMatch = false;
94 int32_t i, iTryTimes;
95 for (i = 0; i < TEST_POS; i++) {
96 sTargetMv.iMvX = kiPositionToCheck[i][0];
97 sTargetMv.iMvY = kiPositionToCheck[i][1];
98 iTryTimes = 100;
99 bDataGeneratorSucceed = false;
100 bFoundMatch = false;
101 while (!bFoundMatch && (iTryTimes--) > 0) {
102 if (!YUVPixelDataGenerator (m_pRefData, m_iWidth, m_iHeight, m_iWidth))
103 continue;
104
105 bDataGeneratorSucceed = true;
106 CopyTargetBlock (m_pSrcBlock, 16, sTargetMv, m_iWidth, pRefPicCenter);
107
108 //clean the sMe status
109 sMe.uiBlockSize = rand() % 5;
110 sMe.pEncMb = m_pSrcBlock;
111 sMe.pRefMb = pRefPicCenter;
112 sMe.sMv.iMvX = sMe.sMv.iMvY = 0;
113 sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
114 WelsDiamondSearch (&sFuncList, &sMe, &sSlice, m_iMaxSearchBlock, m_iWidth);
115
116 //the last selection may be affected by MVDcost, that is when (0,0) will be better
117 //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)
118 bFoundMatch = ((sMe.sMv.iMvX == (sTargetMv.iMvX)) || (sMe.sMv.iMvX == 0)) && ((sMe.sMv.iMvY == (sTargetMv.iMvY))
119 || (sMe.sMv.iMvY == 0));
120 }
121 if (bDataGeneratorSucceed) {
122 //if DataGenerator never succeed, there is no meaning to check iTryTimes
123 ASSERT_TRUE (iTryTimes > 0);
124 //it is possible that ref at differnt position is identical, but that should be under a low probability
125 }
126 }
127 }
128
129 class MotionEstimateRangeTest : public ::testing::Test {
130 public:
SetUp()131 virtual void SetUp() {
132
133 m_iWidth = 320;
134 m_iHeight = 240;
135 m_iWidthExt = m_iWidth + 2 * PADDING_LENGTH;
136 m_iHeightExt = m_iHeight + 2 * PADDING_LENGTH;
137 m_iMbWidth = m_iWidth >> 4;
138 m_iMbHeight = m_iHeight >> 4;
139
140 m_iUsageType = 0;
141 m_iNumDependencyLayers = 1;
142 m_iMvRange = m_iUsageType ? EXPANDED_MV_RANGE : CAMERA_STARTMV_RANGE;
143 m_iMvdRange = (m_iUsageType ? EXPANDED_MVD_RANGE : ((m_iNumDependencyLayers == 1) ? CAMERA_MVD_RANGE :
144 CAMERA_HIGHLAYER_MVD_RANGE));
145 m_uiMvdInterTableSize = (m_iMvdRange << 2); //intepel*4=qpel
146 m_uiMvdInterTableStride = 1 + (m_uiMvdInterTableSize << 1);//qpel_mv_range*2=(+/-);
147 m_uiMvdCacheAlignedSize = m_uiMvdInterTableStride * sizeof (uint16_t);
148
149 m_pMa = new CMemoryAlign (16);
150 ASSERT_TRUE (NULL != m_pMa);
151 m_pMvdCostTable = (uint16_t*)m_pMa->WelsMallocz (52 * m_uiMvdCacheAlignedSize, "pMvdCostTable");
152 ASSERT_TRUE (NULL != m_pMvdCostTable);
153 m_pRefStart = (uint8_t*)m_pMa->WelsMallocz (m_iWidthExt * m_iHeightExt, "reference frame ");
154 ASSERT_TRUE (NULL != m_pRefStart);
155 m_pSrc = (uint8_t*)m_pMa->WelsMallocz (m_iWidth * m_iHeight, "source frame");
156 ASSERT_TRUE (NULL != m_pSrc);
157
158 }
TearDown()159 virtual void TearDown() {
160 if (!m_pMa)
161 return;
162 if (m_pRefStart) {
163 m_pMa->WelsFree (m_pRefStart, "reference frame ");
164 m_pRefStart = NULL;
165 }
166 if (m_pSrc) {
167 m_pMa->WelsFree (m_pSrc, "source frame ");
168 m_pSrc = NULL;
169 }
170 if (m_pMvdCostTable) {
171 m_pMa->WelsFree (m_pMvdCostTable, "pMvdCostTable");
172 m_pMvdCostTable = NULL;
173 }
174 if (m_pMa) {
175 delete m_pMa;
176 m_pMa = NULL;
177 }
178 }
179 public:
180 uint8_t* m_pRefStart;
181 uint8_t* m_pSrc;
182 uint16_t* m_pMvdCostTable;
183
184 int32_t m_iWidth;
185 int32_t m_iHeight;
186 int32_t m_iWidthExt;
187 int32_t m_iHeightExt;
188 int32_t m_iMbWidth;
189 int32_t m_iMbHeight;
190 CMemoryAlign* m_pMa;
191
192 int32_t m_iMvRange;
193 int32_t m_iMvdRange;
194 uint32_t m_uiMvdInterTableSize;
195 uint32_t m_uiMvdInterTableStride;
196 uint32_t m_uiMvdCacheAlignedSize;
197 int32_t m_iUsageType;
198 int32_t m_iNumDependencyLayers;
199 };
200
TEST_F(MotionEstimateRangeTest,TestDiamondSearch)201 TEST_F (MotionEstimateRangeTest, TestDiamondSearch) {
202
203 const int32_t kiMaxBlock16Sad = 72000;//a rough number
204 uint8_t* pRef = m_pRefStart + PADDING_LENGTH * m_iWidthExt + PADDING_LENGTH;
205 SWelsFuncPtrList sFuncList;
206 SWelsME sMe;
207 SSlice sSlice;
208 const uint8_t kuiQp = rand() % 52;
209 InitMe (kuiQp, m_uiMvdInterTableSize, m_uiMvdInterTableStride, m_pMvdCostTable, &sMe);
210
211 WelsInitSampleSadFunc (&sFuncList, 0); //test c functions
212
213 memset (&sSlice, 0, sizeof (sSlice));
214 memset (m_pSrc, 128, m_iWidth * m_iHeight);
215 memset (m_pRefStart, 0, m_iWidthExt * m_iHeightExt);
216
217 sMe.uiBlockSize = BLOCK_16x16; //
218
219 sMe.sMvp.iMvX = rand() % m_iMvRange;
220 sMe.sMvp.iMvY = rand() % m_iMvRange;
221
222 for (int h = 0; h < m_iHeight; h++)
223 memset (pRef + h * m_iWidthExt, h, m_iWidthExt);
224
225 sMe.pEncMb = m_pSrc;
226
227 sMe.sMv.iMvX = sMe.sMvp.iMvX;
228 sMe.sMv.iMvY = sMe.sMvp.iMvY;
229
230 sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
231 SetMvWithinIntegerMvRange (m_iMbWidth, m_iMbHeight, 0, 0, m_iMvRange,
232 & (sSlice.sMvStartMin), & (sSlice.sMvStartMax));
233
234
235 sMe.pRefMb = pRef + sMe.sMvp.iMvY * m_iWidthExt;
236 WelsDiamondSearch (&sFuncList, &sMe, &sSlice, m_iWidth, m_iWidthExt);
237
238 if ((WELS_ABS (sMe.sMv.iMvX) > m_iMvRange))
239 printf ("mvx = %d\n", sMe.sMv.iMvX);
240 ASSERT_TRUE (! (WELS_ABS (sMe.sMv.iMvX) > m_iMvRange));
241 if ((WELS_ABS (sMe.sMv.iMvY) > m_iMvRange))
242 printf ("mvy = %d\n", sMe.sMv.iMvY);
243 ASSERT_TRUE (! (WELS_ABS (sMe.sMv.iMvY) > m_iMvRange));
244
245
246 }
247
TEST_F(MotionEstimateRangeTest,TestWelsMotionCrossSearch)248 TEST_F (MotionEstimateRangeTest, TestWelsMotionCrossSearch) {
249
250 SWelsFuncPtrList sFuncList;
251 SWelsME sMe;
252 SSlice sSlice;
253 bool bUsageType = true;
254 uint8_t* pRef = m_pRefStart + PADDING_LENGTH * m_iWidthExt + PADDING_LENGTH;
255 const int32_t kiMaxBlock16Sad = 72000;//a rough number
256
257 memset (&sSlice, 0, sizeof (sSlice));
258 memset (&sMe, 0, sizeof (sMe));
259 WelsInitSampleSadFunc (&sFuncList, 0); //test c functions
260 WelsInitMeFunc (&sFuncList, 0, bUsageType);
261
262 RandomPixelDataGenerator (m_pSrc, m_iWidth, m_iHeight, m_iWidth);
263 RandomPixelDataGenerator (m_pRefStart, m_iWidthExt, m_iHeightExt, m_iWidthExt);
264
265 sMe.uiBlockSize = BLOCK_16x16; //
266 for (int32_t iMby = 0; iMby < m_iMbHeight; iMby++) {
267 for (int32_t iMbx = 0; iMbx < m_iMbWidth; iMbx++) {
268
269 const uint8_t kuiQp = rand() % 52;
270
271 InitMe (kuiQp, m_uiMvdInterTableSize, m_uiMvdInterTableStride, m_pMvdCostTable, &sMe);
272 SetMvWithinIntegerMvRange (m_iMbWidth, m_iMbHeight, iMbx , iMby, m_iMvRange,
273 & (sSlice.sMvStartMin), & (sSlice.sMvStartMax));
274
275
276 sMe.sMvp.iMvX = rand() % m_iMvRange;
277 sMe.sMvp.iMvY = rand() % m_iMvRange;
278 sMe.iCurMeBlockPixX = (iMbx << 4);
279 sMe.iCurMeBlockPixY = (iMby << 4);
280 sMe.pRefMb = pRef + sMe.iCurMeBlockPixX + sMe.iCurMeBlockPixY * m_iWidthExt;
281 sMe.pEncMb = m_pSrc + sMe.iCurMeBlockPixX + sMe.iCurMeBlockPixY * m_iWidth;
282 sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
283 sMe.pColoRefMb = sMe.pRefMb;
284 WelsMotionCrossSearch (&sFuncList, &sMe, &sSlice, m_iWidth, m_iWidthExt);
285 if ((WELS_ABS (sMe.sMv.iMvX) > m_iMvRange))
286 printf ("mvx = %d\n", sMe.sMv.iMvX);
287 ASSERT_TRUE (! (WELS_ABS (sMe.sMv.iMvX) > m_iMvRange));
288 if ((WELS_ABS (sMe.sMv.iMvY) > m_iMvRange))
289 printf ("mvy = %d\n", sMe.sMv.iMvY);
290 ASSERT_TRUE (! (WELS_ABS (sMe.sMv.iMvY) > m_iMvRange));
291 }
292 }
293 }
DoLineTest(PLineFullSearchFunc func,bool vertical)294 void MotionEstimateTest::DoLineTest (PLineFullSearchFunc func, bool vertical) {
295 const int32_t kiMaxBlock16Sad = 72000;//a rough number
296 SWelsFuncPtrList sFuncList;
297 SWelsME sMe;
298
299 const uint8_t kuiQp = rand() % 52;
300 InitMe (kuiQp, 648, m_uiMvdTableSize, m_pMvdCostTable, &sMe);
301
302 SMVUnitXY sTargetMv;
303 WelsInitSampleSadFunc (&sFuncList, 0); //test c functions
304 WelsInitMeFunc (&sFuncList, WelsCPUFeatureDetect (NULL), 1);
305
306 uint8_t* pRefPicCenter = m_pRefData + (m_iHeight / 2) * m_iWidth + (m_iWidth / 2);
307 sMe.iCurMeBlockPixX = (m_iWidth / 2);
308 sMe.iCurMeBlockPixY = (m_iHeight / 2);
309
310 bool bDataGeneratorSucceed = false;
311 bool bFoundMatch = false;
312 int32_t iTryTimes = 100;
313
314 if (vertical) {
315 sTargetMv.iMvX = 0;
316 sTargetMv.iMvY = -sMe.iCurMeBlockPixY + INTPEL_NEEDED_MARGIN + rand() % (m_iHeight - 16 - 2 * INTPEL_NEEDED_MARGIN);
317 } else {
318 sTargetMv.iMvX = -sMe.iCurMeBlockPixX + INTPEL_NEEDED_MARGIN + rand() % (m_iWidth - 16 - 2 * INTPEL_NEEDED_MARGIN);
319 sTargetMv.iMvY = 0;
320 }
321 bDataGeneratorSucceed = false;
322 bFoundMatch = false;
323 while (!bFoundMatch && (iTryTimes--) > 0) {
324 if (!YUVPixelDataGenerator (m_pRefData, m_iWidth, m_iHeight, m_iWidth))
325 continue;
326
327 bDataGeneratorSucceed = true;
328 CopyTargetBlock (m_pSrcBlock, 16, sTargetMv, m_iWidth, pRefPicCenter);
329
330 //clean the sMe status
331 sMe.uiBlockSize = rand() % 5;
332 sMe.pEncMb = m_pSrcBlock;
333 sMe.pRefMb = pRefPicCenter;
334 sMe.pColoRefMb = pRefPicCenter;
335 sMe.sMv.iMvX = sMe.sMv.iMvY = 0;
336 sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
337 const int32_t iCurMeBlockPixX = sMe.iCurMeBlockPixX;
338 const int32_t iCurMeBlockQpelPixX = ((iCurMeBlockPixX) << 2);
339 const int32_t iCurMeBlockPixY = sMe.iCurMeBlockPixY;
340 const int32_t iCurMeBlockQpelPixY = ((iCurMeBlockPixY) << 2);
341 uint16_t* pMvdCostX = sMe.pMvdCost - iCurMeBlockQpelPixX - sMe.sMvp.iMvX; //do the offset here
342 uint16_t* pMvdCostY = sMe.pMvdCost - iCurMeBlockQpelPixY - sMe.sMvp.iMvY;
343 uint16_t* pMvdCost = vertical ? pMvdCostY : pMvdCostX;
344 int iSize = vertical ? m_iHeight : m_iWidth;
345
346 //the last selection may be affected by MVDcost, that is when smaller MvY will be better
347 if (vertical) {
348 func (&sFuncList, &sMe,
349 pMvdCost,
350 m_iMaxSearchBlock, m_iWidth,
351 INTPEL_NEEDED_MARGIN - sMe.iCurMeBlockPixY,
352 iSize - INTPEL_NEEDED_MARGIN - 16 - sMe.iCurMeBlockPixY, vertical);
353 bFoundMatch = (sMe.sMv.iMvX == 0
354 && (sMe.sMv.iMvY == sTargetMv.iMvY || abs (sMe.sMv.iMvY) < abs (sTargetMv.iMvY)));
355 } else {
356 func (&sFuncList, &sMe,
357 pMvdCost,
358 m_iMaxSearchBlock, m_iWidth,
359 INTPEL_NEEDED_MARGIN - sMe.iCurMeBlockPixX,
360 iSize - INTPEL_NEEDED_MARGIN - 16 - sMe.iCurMeBlockPixX, vertical);
361 bFoundMatch = (sMe.sMv.iMvY == 0
362 && (sMe.sMv.iMvX == sTargetMv.iMvX || abs (sMe.sMv.iMvX) < abs (sTargetMv.iMvX)));
363 }
364 //printf("DoLineTest Target: %d,%d\n", sTargetMv.iMvX, sTargetMv.iMvY);
365 }
366 if (bDataGeneratorSucceed) {
367 //if DataGenerator never succeed, there is no meaning to check iTryTimes
368 ASSERT_TRUE (iTryTimes > 0);
369 //it is possible that ref at differnt position is identical, but that should be under a low probability
370 }
371 }
372
TEST_F(MotionEstimateTest,TestVerticalSearch)373 TEST_F (MotionEstimateTest, TestVerticalSearch) {
374 DoLineTest (LineFullSearch_c, true);
375 }
TEST_F(MotionEstimateTest,TestHorizontalSearch)376 TEST_F (MotionEstimateTest, TestHorizontalSearch) {
377 DoLineTest (LineFullSearch_c, false);
378 }
379
380 #ifdef X86_ASM
TEST_F(MotionEstimateTest,TestVerticalSearch_SSE41)381 TEST_F (MotionEstimateTest, TestVerticalSearch_SSE41) {
382 int32_t iTmp = 1;
383 uint32_t uiCPUFlags = WelsCPUFeatureDetect (&iTmp);
384 if ((uiCPUFlags & WELS_CPU_SSE41) == 0) return ;
385
386 DoLineTest (VerticalFullSearchUsingSSE41, true);
387 }
388
TEST_F(MotionEstimateTest,TestHorizontalSearch_SSE41)389 TEST_F (MotionEstimateTest, TestHorizontalSearch_SSE41) {
390 int32_t iTmp = 1;
391 uint32_t uiCPUFlags = WelsCPUFeatureDetect (&iTmp);
392 if ((uiCPUFlags & WELS_CPU_SSE41) == 0) return ;
393
394 DoLineTest (HorizontalFullSearchUsingSSE41, false);
395 }
396 #endif
397
398 class FeatureMotionEstimateTest : public ::testing::Test {
399 public:
SetUp()400 virtual void SetUp() {
401 m_pRefData = NULL;
402 m_pSrcBlock = NULL;
403 m_pMvdCostTable = NULL;
404
405 m_iWidth = 64;//size of search window
406 m_iHeight = 64;//size of search window
407 m_iMaxSearchBlock = 8;
408 m_uiMvdTableSize = (1 + (648 << 1));
409
410 m_pMa = new CMemoryAlign (16);
411 ASSERT_TRUE (NULL != m_pMa);
412 m_pRefData = (uint8_t*)m_pMa->WelsMalloc (m_iWidth * m_iHeight * sizeof (uint8_t), "m_pRefData");
413 ASSERT_TRUE (NULL != m_pRefData);
414 m_pSrcBlock = (uint8_t*)m_pMa->WelsMalloc (m_iMaxSearchBlock * m_iMaxSearchBlock * sizeof (uint8_t), "m_pSrcBlock");
415 ASSERT_TRUE (NULL != m_pSrcBlock);
416 m_pMvdCostTable = (uint16_t*)m_pMa->WelsMalloc (52 * m_uiMvdTableSize * sizeof (uint16_t), "m_pMvdCostTable");
417 ASSERT_TRUE (NULL != m_pMvdCostTable);
418 m_pFeatureSearchPreparation = (SFeatureSearchPreparation*)m_pMa->WelsMalloc (sizeof (SFeatureSearchPreparation),
419 "m_pFeatureSearchPreparation");
420 ASSERT_TRUE (NULL != m_pFeatureSearchPreparation);
421 m_pScreenBlockFeatureStorage = (SScreenBlockFeatureStorage*)m_pMa->WelsMalloc (sizeof (SScreenBlockFeatureStorage),
422 "m_pScreenBlockFeatureStorage");
423 ASSERT_TRUE (NULL != m_pScreenBlockFeatureStorage);
424 }
TearDown()425 virtual void TearDown() {
426 if (m_pMa) {
427 if (m_pRefData) {
428 m_pMa->WelsFree (m_pRefData, "m_pRefData");
429 m_pRefData = NULL;
430 }
431 if (m_pSrcBlock) {
432 m_pMa->WelsFree (m_pSrcBlock, "m_pSrcBlock");
433 m_pSrcBlock = NULL;
434 }
435 if (m_pMvdCostTable) {
436 m_pMa->WelsFree (m_pMvdCostTable, "m_pMvdCostTable");
437 m_pMvdCostTable = NULL;
438 }
439
440 if (m_pFeatureSearchPreparation) {
441 ReleaseFeatureSearchPreparation (m_pMa, m_pFeatureSearchPreparation->pFeatureOfBlock);
442 m_pMa->WelsFree (m_pFeatureSearchPreparation, "m_pFeatureSearchPreparation");
443 m_pFeatureSearchPreparation = NULL;
444 }
445 if (m_pScreenBlockFeatureStorage) {
446 ReleaseScreenBlockFeatureStorage (m_pMa, m_pScreenBlockFeatureStorage);
447 m_pMa->WelsFree (m_pScreenBlockFeatureStorage, "m_pScreenBlockFeatureStorage");
448 m_pScreenBlockFeatureStorage = NULL;
449 }
450 delete m_pMa;
451 m_pMa = NULL;
452 }
453 }
InitRefPicForMeTest(SPicture * pRefPic)454 void InitRefPicForMeTest (SPicture* pRefPic) {
455 pRefPic->pData[0] = m_pRefData;
456 pRefPic->iLineSize[0] = m_iWidth;
457 pRefPic->iFrameAverageQp = rand() % 52;
458 pRefPic->iWidthInPixel = m_iWidth;
459 pRefPic->iHeightInPixel = m_iHeight;
460 }
461 public:
462 CMemoryAlign* m_pMa;
463
464 SFeatureSearchPreparation* m_pFeatureSearchPreparation;
465 SScreenBlockFeatureStorage* m_pScreenBlockFeatureStorage;
466
467 uint8_t* m_pRefData;
468 uint8_t* m_pSrcBlock;
469 uint16_t* m_pMvdCostTable;
470 uint32_t m_uiMvdTableSize;
471
472 int32_t m_iWidth;
473 int32_t m_iHeight;
474 int32_t m_iMaxSearchBlock;
475 };
476
TEST_F(FeatureMotionEstimateTest,TestFeatureSearch)477 TEST_F (FeatureMotionEstimateTest, TestFeatureSearch) {
478 const int32_t kiMaxBlock16Sad = 72000;//a rough number
479 SWelsFuncPtrList sFuncList;
480 WelsInitSampleSadFunc (&sFuncList, 0); //test c functions
481 WelsInitMeFunc (&sFuncList, 0, true);
482
483 SWelsME sMe;
484 const uint8_t kuiQp = rand() % 52;
485 InitMe (kuiQp, 648, m_uiMvdTableSize, m_pMvdCostTable, &sMe);
486 sMe.iCurMeBlockPixX = (m_iWidth / 2);
487 sMe.iCurMeBlockPixY = (m_iHeight / 2);
488 uint8_t* pRefPicCenter = m_pRefData + (m_iHeight / 2) * m_iWidth + (m_iWidth / 2);
489
490 SPicture sRef;
491 InitRefPicForMeTest (&sRef);
492
493 SSlice sSlice;
494 const int32_t kiSupposedPaddingLength = 16;
495 SetMvWithinIntegerMvRange (m_iWidth / 16 - kiSupposedPaddingLength, m_iHeight / 16 - kiSupposedPaddingLength,
496 m_iWidth / 2 / 16, m_iHeight / 2 / 16, 508,
497 & (sSlice.sMvStartMin), & (sSlice.sMvStartMax));
498 int32_t iReturn;
499 const int32_t kiNeedFeatureStorage = ME_DIA_CROSS_FME;
500 iReturn = RequestFeatureSearchPreparation (m_pMa, m_iWidth, m_iHeight, kiNeedFeatureStorage,
501 m_pFeatureSearchPreparation);
502 ASSERT_TRUE (ENC_RETURN_SUCCESS == iReturn);
503 iReturn = RequestScreenBlockFeatureStorage (m_pMa, m_iWidth, m_iHeight, kiNeedFeatureStorage,
504 m_pScreenBlockFeatureStorage);
505 ASSERT_TRUE (ENC_RETURN_SUCCESS == iReturn);
506
507 SMVUnitXY sTargetMv;
508 for (int i = sSlice.sMvStartMin.iMvX; i <= sSlice.sMvStartMax.iMvX; i++) {
509 for (int j = sSlice.sMvStartMin.iMvY; j <= sSlice.sMvStartMax.iMvY; j++) {
510 if (i == 0 || j == 0) continue; //exclude x=0 or y=0 since that will be skipped by FME
511
512 bool bDataGeneratorSucceed = false;
513 bool bFoundMatch = false;
514
515 if (!YUVPixelDataGenerator (m_pRefData, m_iWidth, m_iHeight, m_iWidth))
516 continue;
517 bDataGeneratorSucceed = true;
518
519 sTargetMv.iMvX = i;
520 sTargetMv.iMvY = j;
521 CopyTargetBlock (m_pSrcBlock, m_iMaxSearchBlock, sTargetMv, m_iWidth, pRefPicCenter);
522
523 //clean sMe status
524 sMe.uiBlockSize = BLOCK_8x8;
525 sMe.pEncMb = m_pSrcBlock;
526 sMe.pRefMb = pRefPicCenter;
527 sMe.pColoRefMb = pRefPicCenter;
528 sMe.sMv.iMvX = sMe.sMv.iMvY = 0;
529 sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
530
531 //begin FME process
532 PerformFMEPreprocess (&sFuncList, &sRef, m_pFeatureSearchPreparation->pFeatureOfBlock,
533 m_pScreenBlockFeatureStorage);
534 m_pScreenBlockFeatureStorage->uiSadCostThreshold[BLOCK_8x8] = UINT_MAX;//to avoid early skip
535 uint32_t uiMaxSearchPoint = INT_MAX;
536 SFeatureSearchIn sFeatureSearchIn = {0};
537 if (SetFeatureSearchIn (&sFuncList, sMe, &sSlice, m_pScreenBlockFeatureStorage,
538 m_iMaxSearchBlock, m_iWidth,
539 &sFeatureSearchIn)) {
540 MotionEstimateFeatureFullSearch (sFeatureSearchIn, uiMaxSearchPoint, &sMe);
541 }
542
543 bool bMvMatch = sMe.sMv.iMvX == sTargetMv.iMvX && sMe.sMv.iMvY == sTargetMv.iMvY;
544 bool bFeatureMatch =
545 (* (m_pScreenBlockFeatureStorage->pFeatureOfBlockPointer + (m_iHeight / 2 + sTargetMv.iMvY) * (m_iWidth - 8) +
546 (m_iWidth / 2 + sTargetMv.iMvX))
547 == * (m_pScreenBlockFeatureStorage->pFeatureOfBlockPointer + (m_iHeight / 2 + sMe.sMv.iMvY) * (m_iWidth - 8) +
548 (m_iWidth / 2 + sMe.sMv.iMvX)))
549 && ((sMe.pMvdCost[sMe.sMv.iMvY << 2] + sMe.pMvdCost[sMe.sMv.iMvX << 2]) <= (sMe.pMvdCost[sTargetMv.iMvY << 2] +
550 sMe.pMvdCost[sTargetMv.iMvX << 2]));
551
552 //the last selection may be affected by MVDcost, that is when smaller Mv will be better
553 bFoundMatch = bMvMatch || bFeatureMatch;
554
555 if (bDataGeneratorSucceed) {
556 //if DataGenerator never succeed, there is no meaning to check iTryTimes
557 if (!bFoundMatch) {
558 printf ("TestFeatureSearch Target: %d,%d, Result: %d,%d\n", sTargetMv.iMvX, sTargetMv.iMvY, sMe.sMv.iMvX, sMe.sMv.iMvY);
559 }
560 EXPECT_TRUE (bFoundMatch);
561 }
562 }
563 }
564 }
565
566