libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

ac_strategy_test.cc (9640B)


      1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style
      4 // license that can be found in the LICENSE file.
      5 
      6 #include "lib/jxl/ac_strategy.h"
      7 
      8 #include <string.h>
      9 
     10 #include <cmath>
     11 #include <hwy/aligned_allocator.h>
     12 #include <hwy/base.h>  // HWY_ALIGN_MAX
     13 #include <hwy/tests/hwy_gtest.h>
     14 #include <utility>
     15 
     16 #include "lib/jxl/base/random.h"
     17 #include "lib/jxl/dct_scales.h"
     18 #include "lib/jxl/dec_transforms_testonly.h"
     19 #include "lib/jxl/enc_transforms.h"
     20 #include "lib/jxl/simd_util.h"
     21 #include "lib/jxl/testing.h"
     22 
     23 namespace jxl {
     24 namespace {
     25 
     26 // Test that DCT -> IDCT is a noop.
     27 class AcStrategyRoundtrip : public ::hwy::TestWithParamTargetAndT<int> {
     28  protected:
     29   void Run() {
     30     const AcStrategy::Type type = static_cast<AcStrategy::Type>(GetParam());
     31     const AcStrategy acs = AcStrategy::FromRawStrategy(type);
     32     const size_t dct_scratch_size =
     33         3 * (MaxVectorSize() / sizeof(float)) * AcStrategy::kMaxBlockDim;
     34 
     35     auto mem = hwy::AllocateAligned<float>(4 * AcStrategy::kMaxCoeffArea +
     36                                            dct_scratch_size);
     37     float* coeffs = mem.get();
     38     float* idct = coeffs + AcStrategy::kMaxCoeffArea;
     39     float* input = idct + AcStrategy::kMaxCoeffArea;
     40     float* scratch_space = input + AcStrategy::kMaxCoeffArea;
     41 
     42     Rng rng(type * 65537 + 13);
     43 
     44     for (size_t j = 0; j < 64; j++) {
     45       size_t i = (acs.log2_covered_blocks()
     46                       ? rng.UniformU(0, 64u << acs.log2_covered_blocks())
     47                       : j);
     48       std::fill_n(input, AcStrategy::kMaxCoeffArea, 0);
     49       input[i] = 0.2f;
     50       TransformFromPixels(type, input, acs.covered_blocks_x() * 8, coeffs,
     51                           scratch_space);
     52       ASSERT_NEAR(coeffs[0], 0.2 / (64 << acs.log2_covered_blocks()), 1e-6)
     53           << " i = " << i;
     54       TransformToPixels(type, coeffs, idct, acs.covered_blocks_x() * 8,
     55                         scratch_space);
     56       for (size_t j = 0; j < 64u << acs.log2_covered_blocks(); j++) {
     57         ASSERT_NEAR(idct[j], j == i ? 0.2f : 0, 2e-6)
     58             << "j = " << j << " i = " << i << " acs " << type;
     59       }
     60     }
     61     // Test DC.
     62     std::fill_n(idct, AcStrategy::kMaxCoeffArea, 0);
     63     for (size_t y = 0; y < acs.covered_blocks_y(); y++) {
     64       for (size_t x = 0; x < acs.covered_blocks_x(); x++) {
     65         float* dc = idct + AcStrategy::kMaxCoeffArea;
     66         std::fill_n(dc, AcStrategy::kMaxCoeffArea, 0);
     67         dc[y * acs.covered_blocks_x() * 8 + x] = 0.2;
     68         LowestFrequenciesFromDC(type, dc, acs.covered_blocks_x() * 8, coeffs,
     69                                 scratch_space);
     70         DCFromLowestFrequencies(type, coeffs, idct, acs.covered_blocks_x() * 8);
     71         std::fill_n(dc, AcStrategy::kMaxCoeffArea, 0);
     72         dc[y * acs.covered_blocks_x() * 8 + x] = 0.2;
     73         for (size_t j = 0; j < 64u << acs.log2_covered_blocks(); j++) {
     74           ASSERT_NEAR(idct[j], dc[j], 1e-6)
     75               << "j = " << j << " x = " << x << " y = " << y << " acs " << type;
     76         }
     77       }
     78     }
     79   }
     80 };
     81 
     82 HWY_TARGET_INSTANTIATE_TEST_SUITE_P_T(
     83     AcStrategyRoundtrip,
     84     ::testing::Range(0,
     85                      static_cast<int>(AcStrategy::Type::kNumValidStrategies)));
     86 
     87 TEST_P(AcStrategyRoundtrip, Test) { Run(); }
     88 
     89 // Test that DC(2x2) -> DCT coefficients -> IDCT -> downsampled IDCT is a noop.
     90 class AcStrategyRoundtripDownsample
     91     : public ::hwy::TestWithParamTargetAndT<int> {
     92  protected:
     93   void Run() {
     94     const AcStrategy::Type type = static_cast<AcStrategy::Type>(GetParam());
     95     const AcStrategy acs = AcStrategy::FromRawStrategy(type);
     96     const size_t dct_scratch_size =
     97         3 * (MaxVectorSize() / sizeof(float)) * AcStrategy::kMaxBlockDim;
     98 
     99     auto mem = hwy::AllocateAligned<float>(4 * AcStrategy::kMaxCoeffArea +
    100                                            dct_scratch_size);
    101     float* coeffs = mem.get();
    102     float* idct = coeffs + AcStrategy::kMaxCoeffArea;
    103     float* dc = idct + AcStrategy::kMaxCoeffArea;
    104     float* scratch_space = dc + AcStrategy::kMaxCoeffArea;
    105 
    106     std::fill_n(coeffs, AcStrategy::kMaxCoeffArea, 0.0f);
    107     Rng rng(type * 65537 + 13);
    108 
    109     for (size_t y = 0; y < acs.covered_blocks_y(); y++) {
    110       for (size_t x = 0; x < acs.covered_blocks_x(); x++) {
    111         if (x > 4 || y > 4) {
    112           if (rng.Bernoulli(0.9f)) continue;
    113         }
    114         std::fill_n(dc, AcStrategy::kMaxCoeffArea, 0);
    115         dc[y * acs.covered_blocks_x() * 8 + x] = 0.2f;
    116         LowestFrequenciesFromDC(type, dc, acs.covered_blocks_x() * 8, coeffs,
    117                                 scratch_space);
    118         TransformToPixels(type, coeffs, idct, acs.covered_blocks_x() * 8,
    119                           scratch_space);
    120         std::fill_n(coeffs, AcStrategy::kMaxCoeffArea, 0.0f);
    121         std::fill_n(dc, AcStrategy::kMaxCoeffArea, 0);
    122         dc[y * acs.covered_blocks_x() * 8 + x] = 0.2f;
    123         // Downsample
    124         for (size_t dy = 0; dy < acs.covered_blocks_y(); dy++) {
    125           for (size_t dx = 0; dx < acs.covered_blocks_x(); dx++) {
    126             float sum = 0;
    127             for (size_t iy = 0; iy < 8; iy++) {
    128               for (size_t ix = 0; ix < 8; ix++) {
    129                 sum += idct[(dy * 8 + iy) * 8 * acs.covered_blocks_x() +
    130                             dx * 8 + ix];
    131               }
    132             }
    133             sum /= 64.0f;
    134             ASSERT_NEAR(sum, dc[dy * 8 * acs.covered_blocks_x() + dx], 1e-6)
    135                 << "acs " << type;
    136           }
    137         }
    138       }
    139     }
    140   }
    141 };
    142 
    143 HWY_TARGET_INSTANTIATE_TEST_SUITE_P_T(
    144     AcStrategyRoundtripDownsample,
    145     ::testing::Range(0,
    146                      static_cast<int>(AcStrategy::Type::kNumValidStrategies)));
    147 
    148 TEST_P(AcStrategyRoundtripDownsample, Test) { Run(); }
    149 
    150 // Test that IDCT(block with zeros in the non-topleft corner) -> downsampled
    151 // IDCT is the same as IDCT -> DC(2x2) of the same block.
    152 class AcStrategyDownsample : public ::hwy::TestWithParamTargetAndT<int> {
    153  protected:
    154   void Run() {
    155     const AcStrategy::Type type = static_cast<AcStrategy::Type>(GetParam());
    156     const AcStrategy acs = AcStrategy::FromRawStrategy(type);
    157     const size_t dct_scratch_size =
    158         3 * (MaxVectorSize() / sizeof(float)) * AcStrategy::kMaxBlockDim;
    159     size_t cx = acs.covered_blocks_y();
    160     size_t cy = acs.covered_blocks_x();
    161     CoefficientLayout(&cy, &cx);
    162 
    163     auto mem = hwy::AllocateAligned<float>(4 * AcStrategy::kMaxCoeffArea +
    164                                            dct_scratch_size);
    165     float* idct = mem.get();
    166     float* idct_acs_downsampled = idct + AcStrategy::kMaxCoeffArea;
    167     float* coeffs = idct + AcStrategy::kMaxCoeffArea;
    168     float* scratch_space = coeffs + AcStrategy::kMaxCoeffArea;
    169 
    170     Rng rng(type * 65537 + 13);
    171 
    172     for (size_t y = 0; y < cy; y++) {
    173       for (size_t x = 0; x < cx; x++) {
    174         if (x > 4 || y > 4) {
    175           if (rng.Bernoulli(0.9f)) continue;
    176         }
    177         float* coeffs = idct + AcStrategy::kMaxCoeffArea;
    178         std::fill_n(coeffs, AcStrategy::kMaxCoeffArea, 0);
    179         coeffs[y * cx * 8 + x] = 0.2f;
    180         TransformToPixels(type, coeffs, idct, acs.covered_blocks_x() * 8,
    181                           scratch_space);
    182         std::fill_n(coeffs, AcStrategy::kMaxCoeffArea, 0);
    183         coeffs[y * cx * 8 + x] = 0.2f;
    184         DCFromLowestFrequencies(type, coeffs, idct_acs_downsampled,
    185                                 acs.covered_blocks_x() * 8);
    186         // Downsample
    187         for (size_t dy = 0; dy < acs.covered_blocks_y(); dy++) {
    188           for (size_t dx = 0; dx < acs.covered_blocks_x(); dx++) {
    189             float sum = 0;
    190             for (size_t iy = 0; iy < 8; iy++) {
    191               for (size_t ix = 0; ix < 8; ix++) {
    192                 sum += idct[(dy * 8 + iy) * 8 * acs.covered_blocks_x() +
    193                             dx * 8 + ix];
    194               }
    195             }
    196             sum /= 64;
    197             ASSERT_NEAR(
    198                 sum, idct_acs_downsampled[dy * 8 * acs.covered_blocks_x() + dx],
    199                 1e-6)
    200                 << " acs " << type;
    201           }
    202         }
    203       }
    204     }
    205   }
    206 };
    207 
    208 HWY_TARGET_INSTANTIATE_TEST_SUITE_P_T(
    209     AcStrategyDownsample,
    210     ::testing::Range(0,
    211                      static_cast<int>(AcStrategy::Type::kNumValidStrategies)));
    212 
    213 TEST_P(AcStrategyDownsample, Test) { Run(); }
    214 
    215 class AcStrategyTargetTest : public ::hwy::TestWithParamTarget {};
    216 HWY_TARGET_INSTANTIATE_TEST_SUITE_P(AcStrategyTargetTest);
    217 
    218 TEST_P(AcStrategyTargetTest, RoundtripAFVDCT) {
    219   HWY_ALIGN_MAX float idct[16];
    220   for (size_t i = 0; i < 16; i++) {
    221     HWY_ALIGN_MAX float pixels[16] = {};
    222     pixels[i] = 1;
    223     HWY_ALIGN_MAX float coeffs[16] = {};
    224 
    225     AFVDCT4x4(pixels, coeffs);
    226     AFVIDCT4x4(coeffs, idct);
    227     for (size_t j = 0; j < 16; j++) {
    228       EXPECT_NEAR(idct[j], pixels[j], 1e-6);
    229     }
    230   }
    231 }
    232 
    233 TEST_P(AcStrategyTargetTest, BenchmarkAFV) {
    234   const AcStrategy::Type type = AcStrategy::Type::AFV0;
    235   HWY_ALIGN_MAX float pixels[64] = {1};
    236   HWY_ALIGN_MAX float coeffs[64] = {};
    237   const size_t dct_scratch_size =
    238       3 * (MaxVectorSize() / sizeof(float)) * AcStrategy::kMaxBlockDim;
    239   auto mem = hwy::AllocateAligned<float>(64 + dct_scratch_size);
    240   float* scratch_space = mem.get();
    241   for (size_t i = 0; i < 1 << 14; i++) {
    242     TransformToPixels(type, coeffs, pixels, 8, scratch_space);
    243     TransformFromPixels(type, pixels, 8, coeffs, scratch_space);
    244   }
    245   EXPECT_NEAR(pixels[0], 0.0, 1E-6);
    246 }
    247 
    248 TEST_P(AcStrategyTargetTest, BenchmarkAFVDCT) {
    249   HWY_ALIGN_MAX float pixels[64] = {1};
    250   HWY_ALIGN_MAX float coeffs[64] = {};
    251   for (size_t i = 0; i < 1 << 14; i++) {
    252     AFVDCT4x4(pixels, coeffs);
    253     AFVIDCT4x4(coeffs, pixels);
    254   }
    255   EXPECT_NEAR(pixels[0], 1.0, 1E-6);
    256 }
    257 
    258 }  // namespace
    259 }  // namespace jxl