/***************************************************************************** * This file is part of uvg266 VVC encoder. * * Copyright (c) 2021, Tampere University, ITU/ISO/IEC, project contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * * * Neither the name of the Tampere University or ITU/ISO/IEC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS ****************************************************************************/ #include "greatest/greatest.h" #include "test_strategies.h" #include "src/image.h" #include ////////////////////////////////////////////////////////////////////////// // EXTERNAL FUNCTIONS ////////////////////////////////////////////////////////////////////////// // DEFINES #define TEST_SAD(X, Y) uvg_image_calc_sad(g_pic, g_ref, 0, 0, (X), (Y), 8, 8, NULL) ////////////////////////////////////////////////////////////////////////// // GLOBALS static const uvg_pixel ref_data[64] = { 1,2,2,2,2,2,2,3, 4,5,5,5,5,5,5,6, 4,5,5,5,5,5,5,6, 4,5,5,5,5,5,5,6, 4,5,5,5,5,5,5,6, 4,5,5,5,5,5,5,6, 4,5,5,5,5,5,5,6, 7,8,8,8,8,8,8,9 }; static const uvg_pixel pic_data[64] = { 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1 }; static uvg_picture *g_pic = 0; static uvg_picture *g_ref = 0; static uvg_picture *g_big_pic = 0; static uvg_picture *g_big_ref = 0; static uvg_picture *g_64x64_zero = 0; static uvg_picture *g_64x64_max = 0; static struct sad_test_env_t { int width; int height; void * tested_func; const strategy_t * strategy; char msg[255]; } sad_test_env; ////////////////////////////////////////////////////////////////////////// // SETUP, TEARDOWN AND HELPER FUNCTIONS static void setup_tests() { g_pic = uvg_image_alloc(UVG_CSP_420, 8, 8); for (int y = 0; y < 8; ++y) { for (int x = 0; x < 8; ++x) { g_pic->y[y*g_pic->stride + x] = pic_data[8*y + x] + 48; } } g_ref = uvg_image_alloc(UVG_CSP_420, 8, 8); for (int y = 0; y < 8; ++y) { for (int x = 0; x < 8; ++x) { g_ref->y[y*g_ref->stride + x] = ref_data[8*y + x] + 48; } } int i = 0; g_big_pic = uvg_image_alloc(UVG_CSP_420, 64, 64); for (int y = 0; y < 64; ++y) { for (int x = 0; x < 64; ++x) { i = ((64 * y) + x); g_big_pic->y[y*g_big_pic->stride + x] = (i*i / 32 + i) % 255; } } i = 0; g_big_ref = uvg_image_alloc(UVG_CSP_420, 64, 64); for (int y = 0; y < 64; ++y) { for (int x = 0; x < 64; ++x) { i = ((64 * y) + x); g_big_ref->y[y*g_big_ref->stride + x] = (i*i / 16 + i) % 255; } } g_64x64_zero = uvg_image_alloc(UVG_CSP_420, 64, 64); memset(g_64x64_zero->y, 0, 64 * 64 * sizeof(uvg_pixel)); g_64x64_max = uvg_image_alloc(UVG_CSP_420, 64, 64); memset(g_64x64_max->y, PIXEL_MAX, 64 * 64 * sizeof(uvg_pixel)); } static void tear_down_tests() { uvg_image_free(g_pic); uvg_image_free(g_ref); uvg_image_free(g_big_pic); uvg_image_free(g_big_ref); uvg_image_free(g_64x64_zero); uvg_image_free(g_64x64_max); } ////////////////////////////////////////////////////////////////////////// // OVERLAPPING BOUNDARY TESTS TEST test_topleft(void) { ASSERT_EQ( 1*(4*4) + (2+4)*(4*4) + 5*(4*4) - 64, TEST_SAD(-3, -3)); PASS(); } TEST test_top(void) { ASSERT_EQ( (1+3)*4 + 2*(6*4) + (4+6)*4 + 5*(6*4) - 64, TEST_SAD(0, -3)); PASS(); } TEST test_topright(void) { ASSERT_EQ( 3*(4*4) + (2+6)*(4*4) + 5*(4*4) - 64, TEST_SAD(3, -3)); PASS(); } TEST test_left(void) { ASSERT_EQ( (1+7)*4 + 4*(6*4) + (2+8)*4 + 5*(6*4) - 64, TEST_SAD(-3, 0)); PASS(); } TEST test_no_offset(void) { ASSERT_EQ( (1+3+7+9) + (2+4+6+8)*6 + 5*(6*6) - 64, TEST_SAD(0, 0)); PASS(); } TEST test_right(void) { ASSERT_EQ( (3+9)*4 + 6*(4*6) + (2+8)*4 + 5*(6*4) - 64, TEST_SAD(3, 0)); PASS(); } TEST test_bottomleft(void) { ASSERT_EQ( 7*(4*4) + (4+8)*(4*4) + 5*(4*4) - 64, TEST_SAD(-3, 3)); PASS(); } TEST test_bottom(void) { ASSERT_EQ( (7+9)*4 + 8*(6*4) + (4+6)*4 + 5*(6*4) - 64, TEST_SAD(0, 3)); PASS(); } TEST test_bottomright(void) { ASSERT_EQ( 9*(4*4) + (6+8)*(4*4) + 5*(4*4) - 64, TEST_SAD(3, 3)); PASS(); } ////////////////////////////////////////////////////////////////////////// // OUT OF FRAME TESTS #define DIST 10 TEST test_topleft_out(void) { ASSERT_EQ( 1*(8*8) - 64, TEST_SAD(-DIST, -DIST)); PASS(); } TEST test_top_out(void) { ASSERT_EQ( (1+3)*8 + 2*(6*8) - 64, TEST_SAD(0, -DIST)); PASS(); } TEST test_topright_out(void) { ASSERT_EQ( 3*(8*8) - 64, TEST_SAD(DIST, -DIST)); PASS(); } TEST test_left_out(void) { ASSERT_EQ( (1+7)*8 + 4*(6*8) - 64, TEST_SAD(-DIST, 0)); PASS(); } TEST test_right_out(void) { ASSERT_EQ( (3+9)*8 + 6*(6*8) - 64, TEST_SAD(DIST, 0)); PASS(); } TEST test_bottomleft_out(void) { ASSERT_EQ( 7*(8*8) - 64, TEST_SAD(-DIST, DIST)); PASS(); } TEST test_bottom_out(void) { ASSERT_EQ( (7+9)*8 + 8*(6*8) - 64, TEST_SAD(0, DIST)); PASS(); } TEST test_bottomright_out(void) { ASSERT_EQ( 9*(8*8) - 64, TEST_SAD(DIST, DIST)); PASS(); } static unsigned simple_sad(const uvg_pixel* buf1, const uvg_pixel* buf2, unsigned stride, unsigned width, unsigned height) { unsigned sum = 0; for (unsigned y = 0; y < height; ++y) { for (unsigned x = 0; x < width; ++x) { sum += abs((int)buf1[y * stride + x] - (int)buf2[y * stride + x]); } } return sum; } TEST test_reg_sad(void) { unsigned width = sad_test_env.width; unsigned height = sad_test_env.height; unsigned stride = 64; unsigned correct_result = simple_sad(g_big_pic->y, g_big_ref->y, stride, width, height); unsigned(*tested_func)(const uvg_pixel *, const uvg_pixel *, int, int, unsigned, unsigned) = sad_test_env.tested_func; unsigned result = tested_func(g_big_pic->y, g_big_ref->y, width, height, stride, stride); sprintf(sad_test_env.msg, "%s(%ux%u):%s", sad_test_env.strategy->type, width, height, sad_test_env.strategy->strategy_name); if (result != correct_result) { FAILm(sad_test_env.msg); } PASSm(sad_test_env.msg); } TEST test_reg_sad_overflow(void) { unsigned width = sad_test_env.width; unsigned height = sad_test_env.height; unsigned stride = 64; unsigned correct_result = simple_sad(g_64x64_zero->y, g_64x64_max->y, stride, width, height); unsigned(*tested_func)(const uvg_pixel *, const uvg_pixel *, int, int, unsigned, unsigned) = sad_test_env.tested_func; unsigned result = tested_func(g_64x64_zero->y, g_64x64_max->y, width, height, stride, stride); sprintf(sad_test_env.msg, "overflow %s(%ux%u):%s", sad_test_env.strategy->type, width, height, sad_test_env.strategy->strategy_name); if (result != correct_result) { FAILm(sad_test_env.msg); } PASSm(sad_test_env.msg); } ////////////////////////////////////////////////////////////////////////// // TEST FIXTURES SUITE(sad_tests) { //SET_SETUP(sad_setup); //SET_TEARDOWN(sad_teardown); setup_tests(); for (volatile unsigned i = 0; i < strategies.count; ++i) { if (strcmp(strategies.strategies[i].type, "reg_sad") != 0) { continue; } // Change the global reg_sad function pointer. uvg_reg_sad = strategies.strategies[i].fptr; // Tests for movement vectors that overlap frame. RUN_TEST(test_topleft); RUN_TEST(test_top); RUN_TEST(test_topright); RUN_TEST(test_left); RUN_TEST(test_no_offset); RUN_TEST(test_right); RUN_TEST(test_bottomleft); RUN_TEST(test_bottom); RUN_TEST(test_bottomright); // Tests for movement vectors that are outside the frame. RUN_TEST(test_topleft_out); RUN_TEST(test_top_out); RUN_TEST(test_topright_out); RUN_TEST(test_left_out); RUN_TEST(test_right_out); RUN_TEST(test_bottomleft_out); RUN_TEST(test_bottom_out); RUN_TEST(test_bottomright_out); struct dimension { int width; int height; }; static const struct dimension tested_dims[] = { // Square motion partitions {64, 64}, {32, 32}, {16, 16}, {8, 8}, // Symmetric motion partitions {64, 32}, {32, 64}, {32, 16}, {16, 32}, {16, 8}, {8, 16}, {8, 4}, {4, 8}, // Asymmetric motion partitions {48, 16}, {16, 48}, {24, 16}, {16, 24}, {12, 4}, {4, 12} }; sad_test_env.tested_func = strategies.strategies[i].fptr; sad_test_env.strategy = &strategies.strategies[i]; int num_dim_tests = sizeof(tested_dims) / sizeof(tested_dims[0]); for (volatile int dim_test = 0; dim_test < num_dim_tests; ++dim_test) { sad_test_env.width = tested_dims[dim_test].width; sad_test_env.height = tested_dims[dim_test].height; RUN_TEST(test_reg_sad); RUN_TEST(test_reg_sad_overflow); } } tear_down_tests(); }