tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

image_test_utils.h (8735B)


      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 #ifndef LIB_JXL_IMAGE_TEST_UTILS_H_
      7 #define LIB_JXL_IMAGE_TEST_UTILS_H_
      8 
      9 #include <cmath>
     10 #include <cstddef>
     11 #include <cstdint>
     12 #include <limits>
     13 #include <sstream>
     14 
     15 #include "lib/jxl/base/compiler_specific.h"
     16 #include "lib/jxl/base/random.h"
     17 #include "lib/jxl/base/rect.h"
     18 #include "lib/jxl/image.h"
     19 
     20 namespace jxl {
     21 
     22 template <typename T>
     23 bool SamePixels(const Plane<T>& image1, const Plane<T>& image2,
     24                std::stringstream& failures) {
     25  const Rect rect(image1);
     26  if (!SameSize(image1, image2)) {
     27    failures << "size mismatch\n";
     28    return false;
     29  }
     30  size_t mismatches = 0;
     31  for (size_t y = rect.y0(); y < rect.ysize(); ++y) {
     32    const T* const JXL_RESTRICT row1 = image1.Row(y);
     33    const T* const JXL_RESTRICT row2 = image2.Row(y);
     34    for (size_t x = rect.x0(); x < rect.xsize(); ++x) {
     35      if (row1[x] != row2[x]) {
     36        failures << "pixel mismatch" << x << ", " << y << ": "
     37                 << static_cast<double>(row1[x])
     38                 << " != " << static_cast<double>(row2[x]) << "\n";
     39        if (++mismatches > 4) {
     40          return false;
     41        }
     42      }
     43    }
     44  }
     45  return mismatches == 0;
     46 }
     47 
     48 template <typename T>
     49 bool SamePixels(const Image3<T>& image1, const Image3<T>& image2,
     50                std::stringstream& failures) {
     51  if (!SameSize(image1, image2)) {
     52    failures << "size mismatch\n";
     53    return false;
     54  }
     55  for (size_t c = 0; c < 3; ++c) {
     56    if (!SamePixels(image1.Plane(c), image2.Plane(c), failures)) {
     57      return false;
     58    }
     59  }
     60  return true;
     61 }
     62 
     63 // Use for floating-point images with fairly large numbers; tolerates small
     64 // absolute errors and/or small relative errors.
     65 template <typename T>
     66 bool VerifyRelativeError(const Plane<T>& expected, const Plane<T>& actual,
     67                         const double threshold_l1,
     68                         const double threshold_relative,
     69                         std::stringstream& failures, const intptr_t border = 0,
     70                         const int c = 0) {
     71  if (!SameSize(expected, actual)) {
     72    failures << "size mismatch\n";
     73    return false;
     74  }
     75  const intptr_t xsize = expected.xsize();
     76  const intptr_t ysize = expected.ysize();
     77 
     78  // Max over current scanline to give a better idea whether there are
     79  // systematic errors or just one outlier. Invalid if negative.
     80  double max_l1 = -1;
     81  double max_relative = -1;
     82  bool any_bad = false;
     83  for (intptr_t y = border; y < ysize - border; ++y) {
     84    const T* const JXL_RESTRICT row_expected = expected.Row(y);
     85    const T* const JXL_RESTRICT row_actual = actual.Row(y);
     86    for (intptr_t x = border; x < xsize - border; ++x) {
     87      const double l1 = std::abs(row_expected[x] - row_actual[x]);
     88 
     89      // Cannot compute relative, only check/update L1.
     90      if (std::abs(row_expected[x]) < 1E-10) {
     91        if (l1 > threshold_l1) {
     92          any_bad = true;
     93          max_l1 = std::max(max_l1, l1);
     94        }
     95      } else {
     96        const double relative =
     97            l1 / std::abs(static_cast<double>(row_expected[x]));
     98        if (l1 > threshold_l1 && relative > threshold_relative) {
     99          // Fails both tolerances => will exit below, update max_*.
    100          any_bad = true;
    101          max_l1 = std::max(max_l1, l1);
    102          max_relative = std::max(max_relative, relative);
    103        }
    104      }
    105    }
    106  }
    107  if (!any_bad) {
    108    return true;
    109  }
    110  // Never had a valid relative value, don't print it.
    111  if (max_relative < 0) {
    112    fprintf(stderr, "c=%d: max +/- %E exceeds +/- %.2E\n", c, max_l1,
    113            threshold_l1);
    114  } else {
    115    fprintf(stderr, "c=%d: max +/- %E, x %E exceeds +/- %.2E, x %.2E\n", c,
    116            max_l1, max_relative, threshold_l1, threshold_relative);
    117  }
    118  // Dump the expected image and actual image if the region is small enough.
    119  const intptr_t kMaxTestDumpSize = 16;
    120  if (xsize <= kMaxTestDumpSize + 2 * border &&
    121      ysize <= kMaxTestDumpSize + 2 * border) {
    122    fprintf(stderr, "Expected image:\n");
    123    for (intptr_t y = border; y < ysize - border; ++y) {
    124      const T* const JXL_RESTRICT row_expected = expected.Row(y);
    125      for (intptr_t x = border; x < xsize - border; ++x) {
    126        fprintf(stderr, "%10lf ", static_cast<double>(row_expected[x]));
    127      }
    128      fprintf(stderr, "\n");
    129    }
    130 
    131    fprintf(stderr, "Actual image:\n");
    132    for (intptr_t y = border; y < ysize - border; ++y) {
    133      const T* const JXL_RESTRICT row_expected = expected.Row(y);
    134      const T* const JXL_RESTRICT row_actual = actual.Row(y);
    135      for (intptr_t x = border; x < xsize - border; ++x) {
    136        const double l1 = std::abs(row_expected[x] - row_actual[x]);
    137 
    138        bool bad = l1 > threshold_l1;
    139        if (row_expected[x] > 1E-10) {
    140          const double relative =
    141              l1 / std::abs(static_cast<double>(row_expected[x]));
    142          bad &= relative > threshold_relative;
    143        }
    144        if (bad) {
    145          fprintf(stderr, "%10lf ", static_cast<double>(row_actual[x]));
    146        } else {
    147          fprintf(stderr, "%10s ", "==");
    148        }
    149      }
    150      fprintf(stderr, "\n");
    151    }
    152  }
    153 
    154  // Find first failing x for further debugging.
    155  for (intptr_t y = border; y < ysize - border; ++y) {
    156    const T* const JXL_RESTRICT row_expected = expected.Row(y);
    157    const T* const JXL_RESTRICT row_actual = actual.Row(y);
    158 
    159    for (intptr_t x = border; x < xsize - border; ++x) {
    160      const double l1 = std::abs(row_expected[x] - row_actual[x]);
    161 
    162      bool bad = l1 > threshold_l1;
    163      if (row_expected[x] > 1E-10) {
    164        const double relative =
    165            l1 / std::abs(static_cast<double>(row_expected[x]));
    166        bad &= relative > threshold_relative;
    167      }
    168      if (bad) {
    169        failures << x << ", " << y << " (" << expected.xsize() << " x "
    170                 << expected.ysize() << ") expected "
    171                 << static_cast<double>(row_expected[x]) << " actual "
    172                 << static_cast<double>(row_actual[x]);
    173        return false;
    174      }
    175    }
    176  }
    177  return false;
    178 }
    179 
    180 template <typename T>
    181 bool VerifyRelativeError(const Image3<T>& expected, const Image3<T>& actual,
    182                         const float threshold_l1,
    183                         const float threshold_relative,
    184                         std::stringstream& failures,
    185                         const intptr_t border = 0) {
    186  for (size_t c = 0; c < 3; ++c) {
    187    bool ok = VerifyRelativeError(expected.Plane(c), actual.Plane(c),
    188                                  threshold_l1, threshold_relative, failures,
    189                                  border, static_cast<int>(c));
    190    if (!ok) {
    191      return false;
    192    }
    193  }
    194  return true;
    195 }
    196 
    197 template <typename T, typename U = T>
    198 void GenerateImage(Rng& rng, Plane<T>* image, U begin, U end) {
    199  for (size_t y = 0; y < image->ysize(); ++y) {
    200    T* const JXL_RESTRICT row = image->Row(y);
    201    for (size_t x = 0; x < image->xsize(); ++x) {
    202      if (std::is_same<T, float>::value || std::is_same<T, double>::value) {
    203        row[x] = rng.UniformF(begin, end);
    204      } else if (std::is_signed<T>::value) {
    205        row[x] = rng.UniformI(begin, end);
    206      } else {
    207        row[x] = rng.UniformU(begin, end);
    208      }
    209    }
    210  }
    211 }
    212 
    213 template <typename T>
    214 void RandomFillImage(Plane<T>* image, const T begin, const T end,
    215                     const uint64_t seed = 129) {
    216  Rng rng(seed);
    217  GenerateImage(rng, image, begin, end);
    218 }
    219 
    220 template <typename T>
    221 typename std::enable_if<std::is_integral<T>::value>::type RandomFillImage(
    222    Plane<T>* image) {
    223  Rng rng(129);
    224  GenerateImage(rng, image, static_cast<int64_t>(0),
    225                static_cast<int64_t>(std::numeric_limits<T>::max()) + 1);
    226 }
    227 
    228 JXL_INLINE void RandomFillImage(Plane<float>* image) {
    229  Rng rng(129);
    230  GenerateImage(rng, image, 0.0f, std::numeric_limits<float>::max());
    231 }
    232 
    233 template <typename T, typename U>
    234 void GenerateImage(Rng& rng, Image3<T>* image, U begin, U end) {
    235  for (size_t c = 0; c < 3; ++c) {
    236    GenerateImage(rng, &image->Plane(c), begin, end);
    237  }
    238 }
    239 
    240 template <typename T>
    241 typename std::enable_if<std::is_integral<T>::value>::type RandomFillImage(
    242    Image3<T>* image) {
    243  Rng rng(129);
    244  GenerateImage(rng, image, static_cast<int64_t>(0),
    245                static_cast<int64_t>(std::numeric_limits<T>::max()) + 1);
    246 }
    247 
    248 JXL_INLINE void RandomFillImage(Image3F* image) {
    249  Rng rng(129);
    250  GenerateImage(rng, image, 0.0f, std::numeric_limits<float>::max());
    251 }
    252 
    253 template <typename T, typename U>
    254 void RandomFillImage(Image3<T>* image, const U begin, const U end,
    255                     const uint64_t seed = 129) {
    256  Rng rng(seed);
    257  GenerateImage(rng, image, begin, end);
    258 }
    259 
    260 }  // namespace jxl
    261 
    262 #endif  // LIB_JXL_IMAGE_TEST_UTILS_H_