Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d19e576f authored by Nick Deakin's avatar Nick Deakin
Browse files

jpegrecoverymap: Add min boost to metadata.

This change updates recovery map metadata from specifying only max boost
(with implied min boost of its inverse) to specifying both min and max
boost explicitly. Places where this matters are also updated to handle
calculating, encoding, extracting, and applying boost appropriately.
Updated tests and added new tests for relevant new edge cases.

Also add benchmark test. To make this nice, make generateRecoveryMap()
and applyRecoveryMap() functions protected instead of private in the
RecoveryMap class. Current results on Pixel 7 Pro for 12MP are:
 * generate map: ~150ms
 * apply map: ~95ms

This is a bit slower than before for generate (~110ms) because we now
also sample the SDR image when determining the metadata's boost values.

Also fix a bug in the applyRecoveryLUT test where we weren't actually
iterating over the LUT and instead only tested the first entry, and
another bug with LUTs in general where we were incrementing values by
1/number of entries when we should actuall increment by 1/(number-1).

Bug: 264715926
Test: tests pass
Change-Id: I687eb9c2f788e9220a14311497a129956b2077fd
parent 82a999b8
Loading
Loading
Loading
Loading
+17 −11
Original line number Original line Diff line number Diff line
@@ -88,6 +88,8 @@ struct jpegr_metadata {
  uint32_t version;
  uint32_t version;
  // Max Content Boost for the map
  // Max Content Boost for the map
  float maxContentBoost;
  float maxContentBoost;
  // Min Content Boost for the map
  float minContentBoost;
};
};


typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
@@ -219,16 +221,9 @@ public:
    */
    */
    status_t getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
    status_t getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
                          jr_info_ptr jpegr_info);
                          jr_info_ptr jpegr_info);
private:

    /*
protected:
     * This method is called in the encoding pipeline. It will encode the recovery map.
    // Following functions protected instead of private for testing.
     *
     * @param uncompressed_recovery_map uncompressed recovery map
     * @param dest encoded recover map
     * @return NO_ERROR if encoding succeeds, error code if error occurs.
     */
    status_t compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
                               jr_compressed_ptr dest);


    /*
    /*
     * This method is called in the encoding pipeline. It will take the uncompressed 8-bit and
     * This method is called in the encoding pipeline. It will take the uncompressed 8-bit and
@@ -239,7 +234,7 @@ private:
     * @param uncompressed_p010_image uncompressed HDR image in P010 color format
     * @param uncompressed_p010_image uncompressed HDR image in P010 color format
     * @param hdr_tf transfer function of the HDR image
     * @param hdr_tf transfer function of the HDR image
     * @param dest recovery map; caller responsible for memory of data
     * @param dest recovery map; caller responsible for memory of data
     * @param metadata max_content_boost is filled in
     * @param metadata minContentBoost and maxContentBoost are filled in
     * @return NO_ERROR if calculation succeeds, error code if error occurs.
     * @return NO_ERROR if calculation succeeds, error code if error occurs.
     */
     */
    status_t generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
    status_t generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
@@ -265,6 +260,17 @@ private:
                              jr_metadata_ptr metadata,
                              jr_metadata_ptr metadata,
                              jr_uncompressed_ptr dest);
                              jr_uncompressed_ptr dest);


private:
    /*
     * This method is called in the encoding pipeline. It will encode the recovery map.
     *
     * @param uncompressed_recovery_map uncompressed recovery map
     * @param dest encoded recover map
     * @return NO_ERROR if encoding succeeds, error code if error occurs.
     */
    status_t compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
                               jr_compressed_ptr dest);

    /*
    /*
     * This methoud is called to separate primary image and recovery map image from JPEGR
     * This methoud is called to separate primary image and recovery map image from JPEGR
     *
     *
+26 −10
Original line number Original line Diff line number Diff line
@@ -118,11 +118,12 @@ inline Color operator/(const Color& lhs, const float rhs) {
constexpr size_t kRecoveryFactorPrecision = 10;
constexpr size_t kRecoveryFactorPrecision = 10;
constexpr size_t kRecoveryFactorNumEntries = 1 << kRecoveryFactorPrecision;
constexpr size_t kRecoveryFactorNumEntries = 1 << kRecoveryFactorPrecision;
struct RecoveryLUT {
struct RecoveryLUT {
  RecoveryLUT(float hdrRatio) {
  RecoveryLUT(jr_metadata_ptr metadata) {
    float increment = 2.0 / kRecoveryFactorNumEntries;
    for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
    float value = -1.0f;
      float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1);
    for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++, value += increment) {
      float logBoost = log2(metadata->minContentBoost) * (1.0f - value)
      mRecoveryTable[idx] = pow(hdrRatio, value);
                     + log2(metadata->maxContentBoost) * value;
      mRecoveryTable[idx] = exp2(logBoost);
    }
    }
  }
  }


@@ -130,10 +131,10 @@ struct RecoveryLUT {
  }
  }


  float getRecoveryFactor(float recovery) {
  float getRecoveryFactor(float recovery) {
    uint32_t value = static_cast<uint32_t>(((recovery + 1.0f) / 2.0f) * kRecoveryFactorNumEntries);
    uint32_t idx = static_cast<uint32_t>(recovery * (kRecoveryFactorNumEntries - 1));
    //TODO() : Remove once conversion modules have appropriate clamping in place
    //TODO() : Remove once conversion modules have appropriate clamping in place
    value = CLIP3(value, 0, kRecoveryFactorNumEntries - 1);
    idx = CLIP3(idx, 0, kRecoveryFactorNumEntries - 1);
    return mRecoveryTable[value];
    return mRecoveryTable[idx];
  }
  }


private:
private:
@@ -219,6 +220,9 @@ Color srgbInvOetf(Color e_gamma);
float srgbInvOetfLUT(float e_gamma);
float srgbInvOetfLUT(float e_gamma);
Color srgbInvOetfLUT(Color e_gamma);
Color srgbInvOetfLUT(Color e_gamma);


constexpr size_t kSrgbInvOETFPrecision = 10;
constexpr size_t kSrgbInvOETFNumEntries = 1 << kSrgbInvOETFPrecision;

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Display-P3 transformations
// Display-P3 transformations


@@ -260,6 +264,9 @@ Color hlgOetf(Color e);
float hlgOetfLUT(float e);
float hlgOetfLUT(float e);
Color hlgOetfLUT(Color e);
Color hlgOetfLUT(Color e);


constexpr size_t kHlgOETFPrecision = 10;
constexpr size_t kHlgOETFNumEntries = 1 << kHlgOETFPrecision;

/*
/*
 * Convert from HLG to scene luminance.
 * Convert from HLG to scene luminance.
 *
 *
@@ -270,6 +277,9 @@ Color hlgInvOetf(Color e_gamma);
float hlgInvOetfLUT(float e_gamma);
float hlgInvOetfLUT(float e_gamma);
Color hlgInvOetfLUT(Color e_gamma);
Color hlgInvOetfLUT(Color e_gamma);


constexpr size_t kHlgInvOETFPrecision = 10;
constexpr size_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision;

/*
/*
 * Convert from scene luminance to PQ.
 * Convert from scene luminance to PQ.
 *
 *
@@ -280,6 +290,9 @@ Color pqOetf(Color e);
float pqOetfLUT(float e);
float pqOetfLUT(float e);
Color pqOetfLUT(Color e);
Color pqOetfLUT(Color e);


constexpr size_t kPqOETFPrecision = 10;
constexpr size_t kPqOETFNumEntries = 1 << kPqOETFPrecision;

/*
/*
 * Convert from PQ to scene luminance in nits.
 * Convert from PQ to scene luminance in nits.
 *
 *
@@ -290,6 +303,9 @@ Color pqInvOetf(Color e_gamma);
float pqInvOetfLUT(float e_gamma);
float pqInvOetfLUT(float e_gamma);
Color pqInvOetfLUT(Color e_gamma);
Color pqInvOetfLUT(Color e_gamma);


constexpr size_t kPqInvOETFPrecision = 10;
constexpr size_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision;



////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Color space conversions
// Color space conversions
@@ -326,13 +342,13 @@ ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gam
 * Calculate the 8-bit unsigned integer recovery value for the given SDR and HDR
 * Calculate the 8-bit unsigned integer recovery value for the given SDR and HDR
 * luminances in linear space, and the hdr ratio to encode against.
 * luminances in linear space, and the hdr ratio to encode against.
 */
 */
uint8_t encodeRecovery(float y_sdr, float y_hdr, float hdr_ratio);
uint8_t encodeRecovery(float y_sdr, float y_hdr, jr_metadata_ptr metadata);


/*
/*
 * Calculates the linear luminance in nits after applying the given recovery
 * Calculates the linear luminance in nits after applying the given recovery
 * value, with the given hdr ratio, to the given sdr input in the range [0, 1].
 * value, with the given hdr ratio, to the given sdr input in the range [0, 1].
 */
 */
Color applyRecovery(Color e, float recovery, float hdr_ratio);
Color applyRecovery(Color e, float recovery, jr_metadata_ptr metadata);
Color applyRecoveryLUT(Color e, float recovery, RecoveryLUT& recoveryLUT);
Color applyRecoveryLUT(Color e, float recovery, RecoveryLUT& recoveryLUT);


/*
/*
+30 −21
Original line number Original line Diff line number Diff line
@@ -573,19 +573,20 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4
  }
  }


  std::mutex mutex;
  std::mutex mutex;
  float hdr_y_nits_max = 0.0f;
  float max_gain = 0.0f;
  double hdr_y_nits_avg = 0.0f;
  float min_gain = 1.0f;
  const int threads = std::clamp(GetCPUCoreCount(), 1, 4);
  const int threads = std::clamp(GetCPUCoreCount(), 1, 4);
  size_t rowStep = threads == 1 ? image_height : kJobSzInRows;
  size_t rowStep = threads == 1 ? image_height : kJobSzInRows;
  JobQueue jobQueue;
  JobQueue jobQueue;


  std::function<void()> computeMetadata = [uncompressed_p010_image, hdrInvOetf,
  std::function<void()> computeMetadata = [uncompressed_p010_image, uncompressed_yuv_420_image,
                                           hdrGamutConversionFn, luminanceFn, hdr_white_nits,
                                           hdrInvOetf, hdrGamutConversionFn, luminanceFn,
                                           threads, &mutex, &hdr_y_nits_avg,
                                           hdr_white_nits, threads, &mutex, &max_gain, &min_gain,
                                           &hdr_y_nits_max, &jobQueue]() -> void {
                                           &jobQueue]() -> void {
    size_t rowStart, rowEnd;
    size_t rowStart, rowEnd;
    float hdr_y_nits_max_th = 0.0f;
    float max_gain_th = 0.0f;
    double hdr_y_nits_avg_th = 0.0f;
    float min_gain_th = 1.0f;

    while (jobQueue.dequeueJob(rowStart, rowEnd)) {
    while (jobQueue.dequeueJob(rowStart, rowEnd)) {
      for (size_t y = rowStart; y < rowEnd; ++y) {
      for (size_t y = rowStart; y < rowEnd; ++y) {
        for (size_t x = 0; x < uncompressed_p010_image->width; ++x) {
        for (size_t x = 0; x < uncompressed_p010_image->width; ++x) {
@@ -595,16 +596,25 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4
          hdr_rgb = hdrGamutConversionFn(hdr_rgb);
          hdr_rgb = hdrGamutConversionFn(hdr_rgb);
          float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
          float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;


          hdr_y_nits_avg_th += hdr_y_nits;
          Color sdr_yuv_gamma =
          if (hdr_y_nits > hdr_y_nits_max_th) {
              getYuv420Pixel(uncompressed_yuv_420_image, x, y);
            hdr_y_nits_max_th = hdr_y_nits;
          Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma);
          }
#if USE_SRGB_INVOETF_LUT
          Color sdr_rgb = srgbInvOetfLUT(sdr_rgb_gamma);
#else
          Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma);
#endif
          float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits;

          float gain = hdr_y_nits / sdr_y_nits;
          max_gain_th = std::max(max_gain_th, gain);
          min_gain_th = std::min(min_gain_th, gain);
        }
        }
      }
      }
    }
    }
    std::unique_lock<std::mutex> lock{mutex};
    std::unique_lock<std::mutex> lock{mutex};
    hdr_y_nits_avg += hdr_y_nits_avg_th;
    max_gain = std::max(max_gain, max_gain_th);
    hdr_y_nits_max = std::max(hdr_y_nits_max, hdr_y_nits_max_th);
    min_gain = std::min(min_gain, min_gain_th);
  };
  };


  std::function<void()> generateMap = [uncompressed_yuv_420_image, uncompressed_p010_image,
  std::function<void()> generateMap = [uncompressed_yuv_420_image, uncompressed_p010_image,
@@ -634,7 +644,7 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4


          size_t pixel_idx = x + y * dest_map_stride;
          size_t pixel_idx = x + y * dest_map_stride;
          reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
          reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
              encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->maxContentBoost);
              encodeRecovery(sdr_y_nits, hdr_y_nits, metadata);
        }
        }
      }
      }
    }
    }
@@ -655,9 +665,9 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4
  computeMetadata();
  computeMetadata();
  std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); });
  std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); });
  workers.clear();
  workers.clear();
  hdr_y_nits_avg /= image_width * image_height;


  metadata->maxContentBoost = hdr_y_nits_max / kSdrWhiteNits;
  metadata->maxContentBoost = max_gain;
  metadata->minContentBoost = min_gain;


  // generate map
  // generate map
  jobQueue.reset();
  jobQueue.reset();
@@ -693,7 +703,7 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_
  dest->width = uncompressed_yuv_420_image->width;
  dest->width = uncompressed_yuv_420_image->width;
  dest->height = uncompressed_yuv_420_image->height;
  dest->height = uncompressed_yuv_420_image->height;
  ShepardsIDW idwTable(kMapDimensionScaleFactor);
  ShepardsIDW idwTable(kMapDimensionScaleFactor);
  RecoveryLUT recoveryLUT(metadata->maxContentBoost);
  RecoveryLUT recoveryLUT(metadata);


  JobQueue jobQueue;
  JobQueue jobQueue;
  std::function<void()> applyRecMap = [uncompressed_yuv_420_image, uncompressed_recovery_map,
  std::function<void()> applyRecMap = [uncompressed_yuv_420_image, uncompressed_recovery_map,
@@ -729,13 +739,12 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_
          if (map_scale_factor != floorf(map_scale_factor)) {
          if (map_scale_factor != floorf(map_scale_factor)) {
            recovery = sampleMap(uncompressed_recovery_map, map_scale_factor, x, y);
            recovery = sampleMap(uncompressed_recovery_map, map_scale_factor, x, y);
          } else {
          } else {
            recovery = sampleMap(uncompressed_recovery_map, map_scale_factor, x, y,
            recovery = sampleMap(uncompressed_recovery_map, map_scale_factor, x, y, idwTable);
                                idwTable);
          }
          }
#if USE_APPLY_RECOVERY_LUT
#if USE_APPLY_RECOVERY_LUT
          Color rgb_hdr = applyRecoveryLUT(rgb_sdr, recovery, recoveryLUT);
          Color rgb_hdr = applyRecoveryLUT(rgb_sdr, recovery, recoveryLUT);
#else
#else
          Color rgb_hdr = applyRecovery(rgb_sdr, recovery, hdr_ratio);
          Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata);
#endif
#endif
          Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->maxContentBoost);
          Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->maxContentBoost);
          uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
          uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
+28 −44
Original line number Original line Diff line number Diff line
@@ -20,65 +20,46 @@


namespace android::recoverymap {
namespace android::recoverymap {


constexpr size_t kPqOETFPrecision = 10;
constexpr size_t kPqOETFNumEntries = 1 << kPqOETFPrecision;

static const std::vector<float> kPqOETF = [] {
static const std::vector<float> kPqOETF = [] {
    std::vector<float> result;
    std::vector<float> result;
    float increment = 1.0 / kPqOETFNumEntries;
    for (int idx = 0; idx < kPqOETFNumEntries; idx++) {
    float value = 0.0f;
      float value = static_cast<float>(idx) / static_cast<float>(kPqOETFNumEntries - 1);
    for (int idx = 0; idx < kPqOETFNumEntries; idx++, value += increment) {
      result.push_back(pqOetf(value));
      result.push_back(pqOetf(value));
    }
    }
    return result;
    return result;
}();
}();


constexpr size_t kPqInvOETFPrecision = 10;
constexpr size_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision;

static const std::vector<float> kPqInvOETF = [] {
static const std::vector<float> kPqInvOETF = [] {
    std::vector<float> result;
    std::vector<float> result;
    float increment = 1.0 / kPqInvOETFNumEntries;
    for (int idx = 0; idx < kPqInvOETFNumEntries; idx++) {
    float value = 0.0f;
      float value = static_cast<float>(idx) / static_cast<float>(kPqInvOETFNumEntries - 1);
    for (int idx = 0; idx < kPqInvOETFNumEntries; idx++, value += increment) {
      result.push_back(pqInvOetf(value));
      result.push_back(pqInvOetf(value));
    }
    }
    return result;
    return result;
}();
}();


constexpr size_t kHlgOETFPrecision = 10;
constexpr size_t kHlgOETFNumEntries = 1 << kHlgOETFPrecision;

static const std::vector<float> kHlgOETF = [] {
static const std::vector<float> kHlgOETF = [] {
    std::vector<float> result;
    std::vector<float> result;
    float increment = 1.0 / kHlgOETFNumEntries;
    for (int idx = 0; idx < kHlgOETFNumEntries; idx++) {
    float value = 0.0f;
      float value = static_cast<float>(idx) / static_cast<float>(kHlgOETFNumEntries - 1);
    for (int idx = 0; idx < kHlgOETFNumEntries; idx++, value += increment) {
      result.push_back(hlgOetf(value));
      result.push_back(hlgOetf(value));
    }
    }
    return result;
    return result;
}();
}();


constexpr size_t kHlgInvOETFPrecision = 10;
constexpr size_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision;

static const std::vector<float> kHlgInvOETF = [] {
static const std::vector<float> kHlgInvOETF = [] {
    std::vector<float> result;
    std::vector<float> result;
    float increment = 1.0 / kHlgInvOETFNumEntries;
    for (int idx = 0; idx < kHlgInvOETFNumEntries; idx++) {
    float value = 0.0f;
      float value = static_cast<float>(idx) / static_cast<float>(kHlgInvOETFNumEntries - 1);
    for (int idx = 0; idx < kHlgInvOETFNumEntries; idx++, value += increment) {
      result.push_back(hlgInvOetf(value));
      result.push_back(hlgInvOetf(value));
    }
    }
    return result;
    return result;
}();
}();


constexpr size_t kSRGBInvOETFPrecision = 10;
static const std::vector<float> kSrgbInvOETF = [] {
constexpr size_t kSRGBInvOETFNumEntries = 1 << kSRGBInvOETFPrecision;
static const std::vector<float> kSRGBInvOETF = [] {
    std::vector<float> result;
    std::vector<float> result;
    float increment = 1.0 / kSRGBInvOETFNumEntries;
    for (int idx = 0; idx < kSrgbInvOETFNumEntries; idx++) {
    float value = 0.0f;
      float value = static_cast<float>(idx) / static_cast<float>(kSrgbInvOETFNumEntries - 1);
    for (int idx = 0; idx < kSRGBInvOETFNumEntries; idx++, value += increment) {
      result.push_back(srgbInvOetf(value));
      result.push_back(srgbInvOetf(value));
    }
    }
    return result;
    return result;
@@ -182,10 +163,10 @@ Color srgbInvOetf(Color e_gamma) {


// See IEC 61966-2-1, Equations F.5 and F.6.
// See IEC 61966-2-1, Equations F.5 and F.6.
float srgbInvOetfLUT(float e_gamma) {
float srgbInvOetfLUT(float e_gamma) {
  uint32_t value = static_cast<uint32_t>(e_gamma * kSRGBInvOETFNumEntries);
  uint32_t value = static_cast<uint32_t>(e_gamma * kSrgbInvOETFNumEntries);
  //TODO() : Remove once conversion modules have appropriate clamping in place
  //TODO() : Remove once conversion modules have appropriate clamping in place
  value = CLIP3(value, 0, kSRGBInvOETFNumEntries - 1);
  value = CLIP3(value, 0, kSrgbInvOETFNumEntries - 1);
  return kSRGBInvOETF[value];
  return kSrgbInvOETF[value];
}
}


Color srgbInvOetfLUT(Color e_gamma) {
Color srgbInvOetfLUT(Color e_gamma) {
@@ -461,21 +442,24 @@ ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gam


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Recovery map calculations
// Recovery map calculations

uint8_t encodeRecovery(float y_sdr, float y_hdr, jr_metadata_ptr metadata) {
uint8_t encodeRecovery(float y_sdr, float y_hdr, float hdr_ratio) {
  float gain = 1.0f;
  float gain = 1.0f;
  if (y_sdr > 0.0f) {
  if (y_sdr > 0.0f) {
    gain = y_hdr / y_sdr;
    gain = y_hdr / y_sdr;
  }
  }


  if (gain < (1.0f / hdr_ratio)) gain = 1.0f / hdr_ratio;
  if (gain < metadata->minContentBoost) gain = metadata->minContentBoost;
  if (gain > hdr_ratio) gain = hdr_ratio;
  if (gain > metadata->maxContentBoost) gain = metadata->maxContentBoost;


  return static_cast<uint8_t>(log2(gain) / log2(hdr_ratio) * 127.5f  + 127.5f);
  return static_cast<uint8_t>((log2(gain) - log2(metadata->minContentBoost))
                            / (log2(metadata->maxContentBoost) - log2(metadata->minContentBoost))
                            * 255.0f);
}
}


Color applyRecovery(Color e, float recovery, float hdr_ratio) {
Color applyRecovery(Color e, float recovery, jr_metadata_ptr metadata) {
  float recoveryFactor = pow(hdr_ratio, recovery);
  float logBoost = log2(metadata->minContentBoost) * (1.0f - recovery)
                 + log2(metadata->maxContentBoost) * recovery;
  float recoveryFactor = exp2(logBoost);
  return e * recoveryFactor;
  return e * recoveryFactor;
}
}


@@ -550,7 +534,7 @@ static size_t clamp(const size_t& val, const size_t& low, const size_t& high) {
}
}


static float mapUintToFloat(uint8_t map_uint) {
static float mapUintToFloat(uint8_t map_uint) {
  return (static_cast<float>(map_uint) - 127.5f) / 127.5f;
  return static_cast<float>(map_uint) / 255.0f;
}
}


static float pythDistance(float x_diff, float y_diff) {
static float pythDistance(float x_diff, float y_diff) {
@@ -558,9 +542,9 @@ static float pythDistance(float x_diff, float y_diff) {
}
}


// TODO: If map_scale_factor is guaranteed to be an integer, then remove the following.
// TODO: If map_scale_factor is guaranteed to be an integer, then remove the following.
float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y) {
float sampleMap(jr_uncompressed_ptr map, float map_scale_factor, size_t x, size_t y) {
  float x_map = static_cast<float>(x) / static_cast<float>(map_scale_factor);
  float x_map = static_cast<float>(x) / map_scale_factor;
  float y_map = static_cast<float>(y) / static_cast<float>(map_scale_factor);
  float y_map = static_cast<float>(y) / map_scale_factor;


  size_t x_lower = static_cast<size_t>(floor(x_map));
  size_t x_lower = static_cast<size_t>(floor(x_map));
  size_t x_upper = x_lower + 1;
  size_t x_upper = x_lower + 1;
+132 −18
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@
#include <fcntl.h>
#include <fcntl.h>
#include <fstream>
#include <fstream>
#include <gtest/gtest.h>
#include <gtest/gtest.h>
#include <sys/time.h>
#include <utils/Log.h>
#include <utils/Log.h>


#define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010"
#define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010"
@@ -35,27 +36,24 @@


namespace android::recoverymap {
namespace android::recoverymap {


class RecoveryMapTest : public testing::Test {
struct Timer {
public:
  struct timeval StartingTime;
  RecoveryMapTest();
  struct timeval EndingTime;
  ~RecoveryMapTest();
  struct timeval ElapsedMicroseconds;
protected:
  virtual void SetUp();
  virtual void TearDown();

  struct jpegr_uncompressed_struct mRawP010Image;
  struct jpegr_uncompressed_struct mRawYuv420Image;
  struct jpegr_compressed_struct mJpegImage;
};
};


RecoveryMapTest::RecoveryMapTest() {}
void timerStart(Timer *t) {
RecoveryMapTest::~RecoveryMapTest() {}
  gettimeofday(&t->StartingTime, nullptr);
}


void RecoveryMapTest::SetUp() {}
void timerStop(Timer *t) {
void RecoveryMapTest::TearDown() {
  gettimeofday(&t->EndingTime, nullptr);
  free(mRawP010Image.data);
}
  free(mRawYuv420Image.data);

  free(mJpegImage.data);
int64_t elapsedTime(Timer *t) {
  t->ElapsedMicroseconds.tv_sec = t->EndingTime.tv_sec - t->StartingTime.tv_sec;
  t->ElapsedMicroseconds.tv_usec = t->EndingTime.tv_usec - t->StartingTime.tv_usec;
  return t->ElapsedMicroseconds.tv_sec * 1000000 + t->ElapsedMicroseconds.tv_usec;
}
}


static size_t getFileSize(int fd) {
static size_t getFileSize(int fd) {
@@ -89,6 +87,80 @@ static bool loadFile(const char filename[], void*& result, int* fileLength) {
  return true;
  return true;
}
}


class RecoveryMapTest : public testing::Test {
public:
  RecoveryMapTest();
  ~RecoveryMapTest();

protected:
  virtual void SetUp();
  virtual void TearDown();

  struct jpegr_uncompressed_struct mRawP010Image;
  struct jpegr_uncompressed_struct mRawYuv420Image;
  struct jpegr_compressed_struct mJpegImage;
};

RecoveryMapTest::RecoveryMapTest() {}
RecoveryMapTest::~RecoveryMapTest() {}

void RecoveryMapTest::SetUp() {}
void RecoveryMapTest::TearDown() {
  free(mRawP010Image.data);
  free(mRawYuv420Image.data);
  free(mJpegImage.data);
}

class RecoveryMapBenchmark : public RecoveryMap {
public:
 void BenchmarkGenerateRecoveryMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr p010Image,
                                   jr_metadata_ptr metadata, jr_uncompressed_ptr map);
 void BenchmarkApplyRecoveryMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map,
                                jr_metadata_ptr metadata, jr_uncompressed_ptr dest);
private:
 const int kProfileCount = 10;
};

void RecoveryMapBenchmark::BenchmarkGenerateRecoveryMap(jr_uncompressed_ptr yuv420Image,
                                                        jr_uncompressed_ptr p010Image,
                                                        jr_metadata_ptr metadata,
                                                        jr_uncompressed_ptr map) {
  ASSERT_EQ(yuv420Image->width, p010Image->width);
  ASSERT_EQ(yuv420Image->height, p010Image->height);

  Timer genRecMapTime;

  timerStart(&genRecMapTime);
  for (auto i = 0; i < kProfileCount; i++) {
      ASSERT_EQ(OK, generateRecoveryMap(
          yuv420Image, p010Image, jpegr_transfer_function::JPEGR_TF_HLG, metadata, map));
      if (i != kProfileCount - 1) delete[] static_cast<uint8_t *>(map->data);
  }
  timerStop(&genRecMapTime);

  ALOGE("Generate Recovery Map:- Res = %i x %i, time = %f ms",
        yuv420Image->width, yuv420Image->height,
        elapsedTime(&genRecMapTime) / (kProfileCount * 1000.f));

}

void RecoveryMapBenchmark::BenchmarkApplyRecoveryMap(jr_uncompressed_ptr yuv420Image,
                                                     jr_uncompressed_ptr map,
                                                     jr_metadata_ptr metadata,
                                                     jr_uncompressed_ptr dest) {
  Timer applyRecMapTime;

  timerStart(&applyRecMapTime);
  for (auto i = 0; i < kProfileCount; i++) {
      ASSERT_EQ(OK, applyRecoveryMap(yuv420Image, map, metadata, dest));
  }
  timerStop(&applyRecMapTime);

  ALOGE("Apply Recovery Map:- Res = %i x %i, time = %f ms",
        yuv420Image->width, yuv420Image->height,
        elapsedTime(&applyRecMapTime) / (kProfileCount * 1000.f));
}

TEST_F(RecoveryMapTest, build) {
TEST_F(RecoveryMapTest, build) {
  // Force all of the recovery map lib to be linked by calling all public functions.
  // Force all of the recovery map lib to be linked by calling all public functions.
  RecoveryMap recovery_map;
  RecoveryMap recovery_map;
@@ -382,4 +454,46 @@ TEST_F(RecoveryMapTest, encodeFromJpegThenDecode) {
  free(decodedJpegR.data);
  free(decodedJpegR.data);
}
}


TEST_F(RecoveryMapTest, ProfileRecoveryMapFuncs) {
  const size_t kWidth = TEST_IMAGE_WIDTH;
  const size_t kHeight = TEST_IMAGE_HEIGHT;

  // Load input files.
  if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
    FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
  }
  mRawP010Image.width = kWidth;
  mRawP010Image.height = kHeight;
  mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;

  if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
    FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
  }
  mRawYuv420Image.width = kWidth;
  mRawYuv420Image.height = kHeight;
  mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;

  RecoveryMapBenchmark benchmark;

  jpegr_metadata metadata = { .version = 1,
                              .maxContentBoost = 8.0f,
                              .minContentBoost = 1.0f / 8.0f };

  jpegr_uncompressed_struct map = { .data = NULL,
                                    .width = 0,
                                    .height = 0,
                                    .colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED };

  benchmark.BenchmarkGenerateRecoveryMap(&mRawYuv420Image, &mRawP010Image, &metadata, &map);

  const int dstSize = mRawYuv420Image.width * mRawYuv420Image.height * 4;
  auto bufferDst = std::make_unique<uint8_t[]>(dstSize);
  jpegr_uncompressed_struct dest = { .data = bufferDst.get(),
                                     .width = 0,
                                     .height = 0,
                                     .colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED };

  benchmark.BenchmarkApplyRecoveryMap(&mRawYuv420Image, &map, &metadata, &dest);
}

} // namespace android::recoverymap
} // namespace android::recoverymap
Loading