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

Commit d4ebf542 authored by Dichen Zhang's avatar Dichen Zhang Committed by Android (Google) Code Review
Browse files

Merge "A bunch of improvements to recoverymap calculations."

parents d1c36ee0 6bd90432
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ enum {
    ERROR_JPEGR_INVALID_NULL_PTR        = JPEGR_IO_ERROR_BASE - 2,
    ERROR_JPEGR_RESOLUTION_MISMATCH     = JPEGR_IO_ERROR_BASE - 3,
    ERROR_JPEGR_BUFFER_TOO_SMALL        = JPEGR_IO_ERROR_BASE - 4,
    ERROR_JPEGR_INVALID_COLORGAMUT      = JPEGR_IO_ERROR_BASE - 5,

    JPEGR_RUNTIME_ERROR_BASE            = -20000,
    ERROR_JPEGR_ENCODE_ERROR            = JPEGR_RUNTIME_ERROR_BASE - 1,
+93 −31
Original line number Diff line number Diff line
@@ -22,11 +22,17 @@
namespace android::recoverymap {

typedef enum {
  JPEGR_COLORSPACE_UNSPECIFIED,
  JPEGR_COLORSPACE_BT709,
  JPEGR_COLORSPACE_P3,
  JPEGR_COLORSPACE_BT2100,
} jpegr_color_space;
  JPEGR_COLORGAMUT_UNSPECIFIED,
  JPEGR_COLORGAMUT_BT709,
  JPEGR_COLORGAMUT_P3,
  JPEGR_COLORGAMUT_BT2100,
} jpegr_color_gamut;

// Transfer functions as defined for XMP metadata
typedef enum {
  JPEGR_TF_HLG = 0,
  JPEGR_TF_PQ = 1,
} jpegr_transfer_function;

/*
 * Holds information for uncompressed image or recovery map.
@@ -38,8 +44,8 @@ struct jpegr_uncompressed_struct {
    int width;
    // Height of the recovery map or image in pixels.
    int height;
    // Color space.
    jpegr_color_space colorSpace;
    // Color gamut.
    jpegr_color_gamut colorGamut;
};

/*
@@ -50,8 +56,8 @@ struct jpegr_compressed_struct {
    void* data;
    // Data length.
    int length;
    // Color space.
    jpegr_color_space colorSpace;
    // Color gamut.
    jpegr_color_gamut colorGamut;
};

/*
@@ -64,9 +70,51 @@ struct jpegr_exif_struct {
    int length;
};

struct chromaticity_coord {
  float x;
  float y;
};


struct st2086_metadata {
  // xy chromaticity coordinate of the red primary of the mastering display
  chromaticity_coord redPrimary;
  // xy chromaticity coordinate of the green primary of the mastering display
  chromaticity_coord greenPrimary;
  // xy chromaticity coordinate of the blue primary of the mastering display
  chromaticity_coord bluePrimary;
  // xy chromaticity coordinate of the white point of the mastering display
  chromaticity_coord whitePoint;
  // Maximum luminance in nits of the mastering display
  uint32_t maxLuminance;
  // Minimum luminance in nits of the mastering display
  float minLuminance;
};

struct hdr10_metadata {
  // Mastering display color volume
  st2086_metadata st2086Metadata;
  // Max frame average light level in nits
  float maxFALL;
  // Max content light level in nits
  float maxCLL;
};

struct jpegr_metadata {
  // JPEG/R version
  uint32_t version;
  // Range scaling factor for the map
  float rangeScalingFactor;
  // The transfer function for decoding the HDR representation of the image
  jpegr_transfer_function transferFunction;
  // HDR10 metadata, only applicable for transferFunction of JPEGR_TF_PQ
  hdr10_metadata hdr10Metadata;
};

typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
typedef struct jpegr_compressed_struct* jr_compressed_ptr;
typedef struct jpegr_exif_struct* jr_exif_ptr;
typedef struct jpegr_metadata* jr_metadata_ptr;

class RecoveryMap {
public:
@@ -75,9 +123,10 @@ public:
     *
     * Generate recovery map from the HDR and SDR inputs, compress SDR YUV to 8-bit JPEG and append
     * the recovery map to the end of the compressed JPEG. HDR and SDR inputs must be the same
     * resolution and color space.
     * resolution.
     * @param uncompressed_p010_image uncompressed HDR image in P010 color format
     * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
     * @param hdr_tf transfer function of the HDR image
     * @param dest destination of the compressed JPEGR image
     * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
     *                the highest quality
@@ -86,6 +135,7 @@ public:
     */
    status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                         jr_uncompressed_ptr uncompressed_yuv_420_image,
                         jpegr_transfer_function hdr_tf,
                         jr_compressed_ptr dest,
                         int quality,
                         jr_exif_ptr exif);
@@ -100,12 +150,14 @@ public:
     * @param uncompressed_p010_image uncompressed HDR image in P010 color format
     * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
     * @param compressed_jpeg_image compressed 8-bit JPEG image
     * @param hdr_tf transfer function of the HDR image
     * @param dest destination of the compressed JPEGR image
     * @return NO_ERROR if encoding succeeds, error code if error occurs.
     */
    status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                         jr_uncompressed_ptr uncompressed_yuv_420_image,
                         jr_compressed_ptr compressed_jpeg_image,
                         jpegr_transfer_function hdr_tf,
                         jr_compressed_ptr dest);

    /*
@@ -115,27 +167,28 @@ public:
     *
     * Decode the compressed 8-bit JPEG image to YUV SDR, generate recovery map from the HDR input
     * and the decoded SDR result, append the recovery map to the end of the compressed JPEG. HDR
     * and SDR inputs must be the same resolution and color space.
     * and SDR inputs must be the same resolution.
     * @param uncompressed_p010_image uncompressed HDR image in P010 color format
     * @param compressed_jpeg_image compressed 8-bit JPEG image
     * @param hdr_tf transfer function of the HDR image
     * @param dest destination of the compressed JPEGR image
     * @return NO_ERROR if encoding succeeds, error code if error occurs.
     */
    status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                         jr_compressed_ptr compressed_jpeg_image,
                         jpegr_transfer_function hdr_tf,
                         jr_compressed_ptr dest);

    /*
     * Decompress JPEGR image.
     *
     * The output JPEGR image is in RGBA_1010102 data format if decoding to HDR.
     * @param compressed_jpegr_image compressed JPEGR image
     * @param dest destination of the uncompressed JPEGR image
     * @param exif destination of the decoded EXIF metadata. Default value is nullptr where EXIF
     *             metadata will not be decoded.
     * @param request_sdr flag that request SDR output, default to false (request HDR output). If
     *                    set to true, decoder will only decode the primary image which is SDR.
     *                    Setting of request_sdr and input source (HDR or SDR) can be found in
     *                    the table below:
     * @param exif destination of the decoded EXIF metadata.
     * @param request_sdr flag that request SDR output. If set to true, decoder will only decode
     *                    the primary image which is SDR. Setting of request_sdr and input source
     *                    (HDR or SDR) can be found in the table below:
     *                    |  input source  |  request_sdr  |  output of decoding  |
     *                    |       HDR      |     true      |          SDR         |
     *                    |       HDR      |     false     |          HDR         |
@@ -145,8 +198,8 @@ public:
     */
    status_t decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
                         jr_uncompressed_ptr dest,
                         jr_exif_ptr exif = nullptr,
                         bool request_sdr = false);
                         jr_exif_ptr exif,
                         bool request_sdr);
private:
    /*
     * This method is called in the decoding pipeline. It will decode the recovery map.
@@ -176,26 +229,32 @@ private:
     * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
     * @param uncompressed_p010_image uncompressed HDR image in P010 color format
     * @param dest recovery map; caller responsible for memory of data
     * @param hdr_ratio HDR ratio will be updated in this method
     * @param metadata metadata provides the transfer function for the HDR
     *                 image; range_scaling_factor and hdr10 FALL and CLL will
     *                 be updated.
     * @return NO_ERROR if calculation succeeds, error code if error occurs.
     */
    status_t generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
                                 jr_uncompressed_ptr uncompressed_p010_image,
                                 jr_uncompressed_ptr dest,
                                 float &hdr_ratio);
                                 jr_metadata_ptr metadata,
                                 jr_uncompressed_ptr dest);

    /*
     * This method is called in the decoding pipeline. It will take the uncompressed (decoded)
     * 8-bit yuv image and the uncompressed (decoded) recovery map as input, and calculate the
     * 10-bit recovered image (in p010 color format).
     * 8-bit yuv image, the uncompressed (decoded) recovery map, and extracted JPEG/R metadata as
     * input, and calculate the 10-bit recovered image. The recovered output image is the same
     * color gamut as the SDR image, with the transfer function specified in the JPEG/R metadata,
     * and is in RGBA1010102 data format.
     *
     * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
     * @param uncompressed_recovery_map uncompressed recovery map
     * @param metadata JPEG/R metadata extracted from XMP.
     * @param dest reconstructed HDR image
     * @return NO_ERROR if calculation succeeds, error code if error occurs.
     */
    status_t applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
                              jr_uncompressed_ptr uncompressed_recovery_map,
                              jr_metadata_ptr metadata,
                              jr_uncompressed_ptr dest);

    /*
@@ -204,9 +263,12 @@ private:
     *
     * @param compressed_jpegr_image compressed JPEGR image
     * @param dest destination of compressed recovery map
     * @param metadata destination of the recovery map metadata
     * @return NO_ERROR if calculation succeeds, error code if error occurs.
     */
    status_t extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image, jr_compressed_ptr dest);
    status_t extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
                                jr_compressed_ptr dest,
                                jr_metadata_ptr metadata);

    /*
     * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image
@@ -215,13 +277,13 @@ private:
     *
     * @param compressed_jpeg_image compressed 8-bit JPEG image
     * @param compress_recovery_map compressed recover map
     * @param hdr_ratio HDR ratio
     * @param metadata JPEG/R metadata to encode in XMP of the jpeg
     * @param dest compressed JPEGR image
     * @return NO_ERROR if calculation succeeds, error code if error occurs.
     */
    status_t appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
                               jr_compressed_ptr compressed_recovery_map,
                               float hdr_ratio,
                               jr_metadata_ptr metadata,
                               jr_compressed_ptr dest);

    /*
@@ -229,7 +291,7 @@ private:
     *
     * below is an example of the XMP metadata that this function generates where
     * secondary_image_length = 1000
     * hdr_ratio = 1.25
     * range_scaling_factor = 1.25
     *
     * <x:xmpmeta
     *   xmlns:x="adobe:ns:meta/"
@@ -239,7 +301,7 @@ private:
     *     <rdf:Description
     *       xmlns:GContainer="http://ns.google.com/photos/1.0/container/">
     *       <GContainer:Version>1</GContainer:Version>
     *       <GContainer:HdrRatio>1.25</GContainer:HdrRatio>
     *       <GContainer:RangeScalingFactor>1.25</GContainer:RangeScalingFactor>
     *       <GContainer:Directory>
     *         <rdf:Seq>
     *           <rdf:li>
@@ -260,10 +322,10 @@ private:
     * </x:xmpmeta>
     *
     * @param secondary_image_length length of secondary image
     * @param hdr_ratio hdr ratio
     * @param metadata JPEG/R metadata to encode as XMP
     * @return XMP metadata in type of string
     */
    std::string generateXmp(int secondary_image_length, float hdr_ratio);
    std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);
};

} // namespace android::recoverymap
+47 −1
Original line number Diff line number Diff line
@@ -43,6 +43,9 @@ struct Color {
  };
};

typedef Color (*ColorTransformFn)(Color);
typedef float (*ColorCalculationFn)(Color);

inline Color operator+=(Color& lhs, const Color& rhs) {
  lhs.r += rhs.r;
  lhs.g += rhs.g;
@@ -138,7 +141,10 @@ Color srgbInvOetf(Color e_gamma);
////////////////////////////////////////////////////////////////////////////////
// Display-P3 transformations


/*
 * Calculated the luminance of a linear RGB P3 pixel, according to EG 432-1.
 */
float p3Luminance(Color e);


////////////////////////////////////////////////////////////////////////////////
@@ -169,10 +175,43 @@ Color hlgOetf(Color e);
 */
Color hlgInvOetf(Color e_gamma);

/*
 * Convert from scene luminance in nits to PQ.
 */
Color pqOetf(Color e);

/*
 * Convert from PQ to scene luminance in nits.
 */
Color pqInvOetf(Color e_gamma);


////////////////////////////////////////////////////////////////////////////////
// Color space conversions

/*
 * Convert between color spaces with linear RGB data, according to ITU-R BT.2407 and EG 432-1.
 *
 * All conversions are derived from multiplying the matrix for XYZ to output RGB color gamut by the
 * matrix for input RGB color gamut to XYZ. The matrix for converting from XYZ to an RGB gamut is
 * always the inverse of the RGB gamut to XYZ matrix.
 */
Color bt709ToP3(Color e);
Color bt709ToBt2100(Color e);
Color p3ToBt709(Color e);
Color p3ToBt2100(Color e);
Color bt2100ToBt709(Color e);
Color bt2100ToP3(Color e);

/*
 * Identity conversion.
 */
inline Color identityConversion(Color e) { return e; }

/*
 * Get the conversion to apply to the HDR image for recovery map generation
 */
ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gamut hdr_gamut);


////////////////////////////////////////////////////////////////////////////////
@@ -220,6 +259,13 @@ Color sampleYuv420(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, s
 */
Color sampleP010(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);

/*
 * Convert from Color to RGBA1010102.
 *
 * Alpha always set to 1.0.
 */
uint32_t colorToRgba1010102(Color e_gamma);

} // namespace android::recoverymap

#endif // ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H
+136 −53

File changed.

Preview size limit exceeded, changes collapsed.

+121 −1
Original line number Diff line number Diff line
@@ -64,6 +64,11 @@ Color srgbInvOetf(Color e_gamma) {
////////////////////////////////////////////////////////////////////////////////
// Display-P3 transformations

static const float kP3R = 0.22897f, kP3G = 0.69174f, kP3B = 0.07929f;

float p3Luminance(Color e) {
  return kP3R * e.r + kP3G * e.g + kP3B * e.b;
}


////////////////////////////////////////////////////////////////////////////////
@@ -128,7 +133,7 @@ Color hlgOetf(Color e) {
  return {{{ hlgOetf(e.r), hlgOetf(e.g), hlgOetf(e.b) }}};
}

float hlgInvOetf(float e_gamma) {
static float hlgInvOetf(float e_gamma) {
  if (e_gamma <= 0.5f) {
    return pow(e_gamma, 2.0f) / 3.0f;
  } else {
@@ -142,10 +147,118 @@ Color hlgInvOetf(Color e_gamma) {
             hlgInvOetf(e_gamma.b) }}};
}

static const float kPqM1 = 2610.0f / 16384.0f, kPqM2 = 2523.0f / 4096.0f * 128.0f;
static const float kPqC1 = 3424.0f / 4096.0f, kPqC2 = 2413.0f / 4096.0f * 32.0f,
                   kPqC3 = 2392.0f / 4096.0f * 32.0f;

static float pqOetf(float e) {
  if (e < 0.0f) e = 0.0f;
  return pow((kPqC1 + kPqC2 * pow(e / 10000.0f, kPqM1)) / (1 + kPqC3 * pow(e / 10000.0f, kPqM1)),
             kPqM2);
}

Color pqOetf(Color e) {
  return {{{ pqOetf(e.r), pqOetf(e.g), pqOetf(e.b) }}};
}

static float pqInvOetf(float e_gamma) {
  static const float kPqInvOetfCoef = log2(-(pow(kPqM1, 1.0f / kPqM2) - kPqC1)
                                         / (kPqC3 * pow(kPqM1, 1.0f / kPqM2) - kPqC2));
  return kPqInvOetfCoef / log2(e_gamma * 10000.0f);
}

Color pqInvOetf(Color e_gamma) {
  return {{{ pqInvOetf(e_gamma.r),
             pqInvOetf(e_gamma.g),
             pqInvOetf(e_gamma.b) }}};
}


////////////////////////////////////////////////////////////////////////////////
// Color conversions

Color bt709ToP3(Color e) {
 return {{{ 0.82254f * e.r + 0.17755f * e.g + 0.00006f * e.b,
            0.03312f * e.r + 0.96684f * e.g + -0.00001f * e.b,
            0.01706f * e.r + 0.07240f * e.g + 0.91049f * e.b }}};
}

Color bt709ToBt2100(Color e) {
 return {{{ 0.62740f * e.r + 0.32930f * e.g + 0.04332f * e.b,
            0.06904f * e.r + 0.91958f * e.g + 0.01138f * e.b,
            0.01636f * e.r + 0.08799f * e.g + 0.89555f * e.b }}};
}

Color p3ToBt709(Color e) {
 return {{{ 1.22482f * e.r + -0.22490f * e.g + -0.00007f * e.b,
            -0.04196f * e.r + 1.04199f * e.g + 0.00001f * e.b,
            -0.01961f * e.r + -0.07865f * e.g + 1.09831f * e.b }}};
}

Color p3ToBt2100(Color e) {
 return {{{ 0.75378f * e.r + 0.19862f * e.g + 0.04754f * e.b,
            0.04576f * e.r + 0.94177f * e.g + 0.01250f * e.b,
            -0.00121f * e.r + 0.01757f * e.g + 0.98359f * e.b }}};
}

Color bt2100ToBt709(Color e) {
 return {{{ 1.66045f * e.r + -0.58764f * e.g + -0.07286f * e.b,
            -0.12445f * e.r + 1.13282f * e.g + -0.00837f * e.b,
            -0.01811f * e.r + -0.10057f * e.g + 1.11878f * e.b }}};
}

Color bt2100ToP3(Color e) {
 return {{{ 1.34369f * e.r + -0.28223f * e.g + -0.06135f * e.b,
            -0.06533f * e.r + 1.07580f * e.g + -0.01051f * e.b,
            0.00283f * e.r + -0.01957f * e.g + 1.01679f * e.b
 }}};
}

// TODO: confirm we always want to convert like this before calculating
// luminance.
ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gamut hdr_gamut) {
    switch (sdr_gamut) {
    case JPEGR_COLORGAMUT_BT709:
      switch (hdr_gamut) {
        case JPEGR_COLORGAMUT_BT709:
          return identityConversion;
        case JPEGR_COLORGAMUT_P3:
          return p3ToBt709;
        case JPEGR_COLORGAMUT_BT2100:
          return bt2100ToBt709;
        case JPEGR_COLORGAMUT_UNSPECIFIED:
          return nullptr;
      }
      break;
    case JPEGR_COLORGAMUT_P3:
      switch (hdr_gamut) {
        case JPEGR_COLORGAMUT_BT709:
          return bt709ToP3;
        case JPEGR_COLORGAMUT_P3:
          return identityConversion;
        case JPEGR_COLORGAMUT_BT2100:
          return bt2100ToP3;
        case JPEGR_COLORGAMUT_UNSPECIFIED:
          return nullptr;
      }
      break;
    case JPEGR_COLORGAMUT_BT2100:
      switch (hdr_gamut) {
        case JPEGR_COLORGAMUT_BT709:
          return bt709ToBt2100;
        case JPEGR_COLORGAMUT_P3:
          return p3ToBt2100;
        case JPEGR_COLORGAMUT_BT2100:
          return identityConversion;
        case JPEGR_COLORGAMUT_UNSPECIFIED:
          return nullptr;
      }
      break;
    case JPEGR_COLORGAMUT_UNSPECIFIED:
      return nullptr;
  }
}


////////////////////////////////////////////////////////////////////////////////
// Recovery map calculations
@@ -257,4 +370,11 @@ Color sampleP010(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, s
  return samplePixels(image, map_scale_factor, x, y, getP010Pixel);
}

uint32_t colorToRgba1010102(Color e_gamma) {
  return (0x3ff & static_cast<uint32_t>(e_gamma.r * 1023.0f))
       | ((0x3ff & static_cast<uint32_t>(e_gamma.g * 1023.0f)) << 10)
       | ((0x3ff & static_cast<uint32_t>(e_gamma.b * 1023.0f)) << 20)
       | (0x3 << 30);  // Set alpha to 1.0
}

} // namespace android::recoverymap
Loading