libjxl

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

image_test_utils.h (8600B)


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