Skip to content

Commit

Permalink
add support for configuring min/max content boost
Browse files Browse the repository at this point in the history
The min/max content boost for different encoding presets are computed
internally basing on the input hdr and sdr intents. This change
introduces an API to configure the values from the app side. If the
library during its computations observes a small min-max range, then
client recommendation is ignored. Otherwise, the configured values are
selected.

fixes google#160

Test: ./ultrahdr_app <options> -k 0.01 -K 8.0
  • Loading branch information
ram-mohan committed Aug 27, 2024
1 parent bddf8da commit 6adb18e
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 19 deletions.
40 changes: 32 additions & 8 deletions examples/ultrahdr_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <string.h>

#include <algorithm>
#include <cfloat>
#include <cmath>
#include <cstdint>
#include <fstream>
Expand Down Expand Up @@ -257,7 +258,8 @@ class UltraHdrAppInput {
uhdr_img_fmt_t oFmt = UHDR_IMG_FMT_32bppRGBA1010102, bool isHdrCrFull = false,
int gainmapScaleFactor = 4, int gainmapQuality = 85,
bool enableMultiChannelGainMap = false, float gamma = 1.0f,
bool enableGLES = false, uhdr_enc_preset_t encPreset = UHDR_USAGE_REALTIME)
bool enableGLES = false, uhdr_enc_preset_t encPreset = UHDR_USAGE_REALTIME,
float minContentBoost = FLT_MIN, float maxContentBoost = FLT_MAX)
: mHdrIntentRawFile(hdrIntentRawFile),
mSdrIntentRawFile(sdrIntentRawFile),
mSdrIntentCompressedFile(sdrIntentCompressedFile),
Expand All @@ -283,6 +285,8 @@ class UltraHdrAppInput {
mGamma(gamma),
mEnableGLES(enableGLES),
mEncPreset(encPreset),
mMinContentBoost(minContentBoost),
mMaxContentBoost(maxContentBoost),
mMode(0){};

UltraHdrAppInput(const char* gainmapMetadataCfgFile, const char* uhdrFile, const char* outputFile,
Expand Down Expand Up @@ -313,6 +317,8 @@ class UltraHdrAppInput {
mGamma(1.0f),
mEnableGLES(enableGLES),
mEncPreset(UHDR_USAGE_REALTIME),
mMinContentBoost(FLT_MIN),
mMaxContentBoost(FLT_MAX),
mMode(1){};

~UltraHdrAppInput() {
Expand Down Expand Up @@ -393,6 +399,8 @@ class UltraHdrAppInput {
const float mGamma;
const bool mEnableGLES;
const uhdr_enc_preset_t mEncPreset;
const float mMinContentBoost;
const float mMaxContentBoost;
const int mMode;

uhdr_raw_image_t mRawP010Image{};
Expand Down Expand Up @@ -690,6 +698,9 @@ bool UltraHdrAppInput::encode() {
RET_IF_ERR(uhdr_enc_set_gainmap_scale_factor(handle, mMapDimensionScaleFactor))
RET_IF_ERR(uhdr_enc_set_gainmap_gamma(handle, mGamma))
RET_IF_ERR(uhdr_enc_set_preset(handle, mEncPreset))
if (mMinContentBoost != FLT_MIN || mMaxContentBoost != FLT_MAX) {
RET_IF_ERR(uhdr_enc_set_min_max_content_boost(handle, mMinContentBoost, mMaxContentBoost))
}
if (mEnableGLES) {
RET_IF_ERR(uhdr_enable_gpu_acceleration(handle, mEnableGLES))
}
Expand Down Expand Up @@ -1358,6 +1369,10 @@ static void usage(const char* name) {
fprintf(
stderr,
" -D select encoding preset, optional. [0:real time (default), 1:best quality]. \n");
fprintf(stderr,
" -k min content boost recommendation, must be in linear scale, optional \n");
fprintf(stderr,
" -k max content boost recommendation, must be in linear scale, optional \n");
fprintf(stderr, " -x binary input resource containing exif data to insert, optional. \n");
fprintf(stderr, "\n## decoder options : \n");
fprintf(stderr, " -j ultra hdr compressed input resource, required. \n");
Expand Down Expand Up @@ -1445,7 +1460,7 @@ static void usage(const char* name) {
}

int main(int argc, char* argv[]) {
char opt_string[] = "p:y:i:g:f:w:h:C:c:t:q:o:O:m:j:e:a:b:z:R:s:M:Q:G:x:u:D:";
char opt_string[] = "p:y:i:g:f:w:h:C:c:t:q:o:O:m:j:e:a:b:z:R:s:M:Q:G:x:u:D:k:K:";
char *hdr_intent_raw_file = nullptr, *sdr_intent_raw_file = nullptr, *uhdr_file = nullptr,
*sdr_intent_compressed_file = nullptr, *gainmap_compressed_file = nullptr,
*gainmap_metadata_cfg_file = nullptr, *output_file = nullptr, *exif_file = nullptr;
Expand All @@ -1467,6 +1482,8 @@ int main(int argc, char* argv[]) {
float gamma = 1.0f;
bool enable_gles = false;
uhdr_enc_preset_t enc_preset = UHDR_USAGE_REALTIME;
float min_content_boost = FLT_MIN;
float max_content_boost = FLT_MAX;
int ch;
while ((ch = getopt_s(argc, argv, opt_string)) != -1) {
switch (ch) {
Expand Down Expand Up @@ -1555,6 +1572,12 @@ int main(int argc, char* argv[]) {
case 'D':
enc_preset = static_cast<uhdr_enc_preset_t>(atoi(optarg_s));
break;
case 'k':
min_content_boost = atof(optarg_s);
break;
case 'K':
max_content_boost = atof(optarg_s);
break;
default:
usage(argv[0]);
return -1;
Expand All @@ -1577,12 +1600,13 @@ int main(int argc, char* argv[]) {
std::cerr << "did not receive raw resources for encoding." << std::endl;
return -1;
}
UltraHdrAppInput appInput(
hdr_intent_raw_file, sdr_intent_raw_file, sdr_intent_compressed_file,
gainmap_compressed_file, gainmap_metadata_cfg_file, exif_file,
output_file ? output_file : "out.jpeg", width, height, hdr_cf, sdr_cf, hdr_cg, sdr_cg,
hdr_tf, quality, out_tf, out_cf, use_full_range_color_hdr, gainmap_scale_factor,
gainmap_compression_quality, use_multi_channel_gainmap, gamma, enable_gles, enc_preset);
UltraHdrAppInput appInput(hdr_intent_raw_file, sdr_intent_raw_file, sdr_intent_compressed_file,
gainmap_compressed_file, gainmap_metadata_cfg_file, exif_file,
output_file ? output_file : "out.jpeg", width, height, hdr_cf, sdr_cf,
hdr_cg, sdr_cg, hdr_tf, quality, out_tf, out_cf,
use_full_range_color_hdr, gainmap_scale_factor,
gainmap_compression_quality, use_multi_channel_gainmap, gamma,
enable_gles, enc_preset, min_content_boost, max_content_boost);
if (!appInput.encode()) return -1;
if (compute_psnr == 1) {
if (!appInput.decode()) return -1;
Expand Down
39 changes: 30 additions & 9 deletions lib/include/ultrahdr/jpegr.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ class JpegR {
size_t mapDimensionScaleFactor = kMapDimensionScaleFactorDefault,
int mapCompressQuality = kMapCompressQualityDefault,
bool useMultiChannelGainMap = kUseMultiChannelGainMapDefault,
float gamma = kGainMapGammaDefault, uhdr_enc_preset_t preset = UHDR_USAGE_REALTIME);
float gamma = kGainMapGammaDefault, uhdr_enc_preset_t preset = UHDR_USAGE_REALTIME,
float minContentBoost = FLT_MIN, float maxContentBoost = FLT_MAX);

/*!\brief Encode API-0.
*
Expand Down Expand Up @@ -242,7 +243,6 @@ class JpegR {
jr_info_ptr uhdr_image_info);

/*!\brief set gain map dimension scale factor
*
* NOTE: Applicable only in encoding scenario
*
* \param[in] mapDimensionScaleFactor scale factor
Expand All @@ -254,15 +254,13 @@ class JpegR {
}

/*!\brief get gain map dimension scale factor
*
* NOTE: Applicable only in encoding scenario
*
* \return mapDimensionScaleFactor
*/
size_t getMapDimensionScaleFactor() { return this->mMapDimensionScaleFactor; }

/*!\brief set gain map compression quality factor
*
* NOTE: Applicable only in encoding scenario
*
* \param[in] mapCompressQuality quality factor for gain map image compression
Expand All @@ -274,15 +272,13 @@ class JpegR {
}

/*!\brief get gain map quality factor
*
* NOTE: Applicable only in encoding scenario
*
* \return quality factor
*/
int getMapCompressQuality() { return this->mMapCompressQuality; }

/*!\brief set gain map gamma
*
* NOTE: Applicable only in encoding scenario
*
* \param[in] gamma gamma parameter that is used for gain map calculation
Expand All @@ -292,15 +288,13 @@ class JpegR {
void setGainMapGamma(float gamma) { this->mGamma = gamma; }

/*!\brief get gain map gamma
*
* NOTE: Applicable only in encoding scenario
*
* \return gamma parameter
*/
float getGainMapGamma() { return this->mGamma; }

/*!\brief enable / disable multi channel gain map
*
* NOTE: Applicable only in encoding scenario
*
* \param[in] useMultiChannelGainMap enable / disable multi channel gain map
Expand All @@ -312,13 +306,38 @@ class JpegR {
}

/*!\brief check if multi channel gain map is enabled
*
* NOTE: Applicable only in encoding scenario
*
* \return true if multi channel gain map is enabled, false otherwise
*/
bool isUsingMultiChannelGainMap() { return this->mUseMultiChannelGainMap; }

/*!\brief set gain map min and max content boost
* NOTE: Applicable only in encoding scenario
*
* \param[in] minBoost gain map min content boost
* \param[in] maxBoost gain map max content boost
*
* \return none
*/
void setGainMapMinMaxContentBoost(float minBoost, float maxBoost) {
this->mMinContentBoost = minBoost;
this->mMaxContentBoost = maxBoost;
}

/*!\brief get gain map min max content boost
* NOTE: Applicable only in encoding scenario
*
* \param[out] minBoost gain map min content boost
* \param[out] maxBoost gain map max content boost
*
* \return none
*/
void getGainMapMinMaxContentBoost(float& minBoost, float& maxBoost) {
minBoost = this->mMinContentBoost;
maxBoost = this->mMaxContentBoost;
}

/* \brief Alias of Encode API-0.
*
* \deprecated This function is deprecated. Use its alias
Expand Down Expand Up @@ -572,6 +591,8 @@ class JpegR {
bool mUseMultiChannelGainMap; // enable multichannel gain map
float mGamma; // gain map gamma parameter
uhdr_enc_preset_t mEncPreset; // encoding speed preset
float mMinContentBoost; // min content boost recommendation
float mMaxContentBoost; // max content boost recommendation
};

struct GlobalTonemapOutputs {
Expand Down
2 changes: 2 additions & 0 deletions lib/include/ultrahdr/ultrahdrcommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@ struct uhdr_encoder_private : uhdr_codec_private {
bool m_use_multi_channel_gainmap;
float m_gamma;
uhdr_enc_preset_t m_enc_preset;
float m_min_content_boost;
float m_max_content_boost;

// internal data
std::unique_ptr<ultrahdr::uhdr_compressed_image_ext_t> m_compressed_output_buffer;
Expand Down
13 changes: 12 additions & 1 deletion lib/src/jpegr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,16 @@ int GetCPUCoreCount() {
}

JpegR::JpegR(void* uhdrGLESCtxt, size_t mapDimensionScaleFactor, int mapCompressQuality,
bool useMultiChannelGainMap, float gamma, uhdr_enc_preset_t preset) {
bool useMultiChannelGainMap, float gamma, uhdr_enc_preset_t preset,
float minContentBoost, float maxContentBoost) {
mUhdrGLESCtxt = uhdrGLESCtxt;
mMapDimensionScaleFactor = mapDimensionScaleFactor;
mMapCompressQuality = mapCompressQuality;
mUseMultiChannelGainMap = useMultiChannelGainMap;
mGamma = gamma;
mEncPreset = preset;
mMinContentBoost = minContentBoost;
mMaxContentBoost = maxContentBoost;
}

/*
Expand Down Expand Up @@ -808,6 +811,14 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_
// black from any sdr luminance. Allowing further excursion might not offer any benefit and on
// the downside can cause bigger error during affine map and inverse map.
min_content_boost_log2 = (std::max)(-13.0f, min_content_boost_log2);
if (this->mMaxContentBoost != FLT_MAX) {
float suggestion = log2(this->mMaxContentBoost);
max_content_boost_log2 = (std::min)(max_content_boost_log2, suggestion);
}
if (this->mMinContentBoost != FLT_MIN) {
float suggestion = log2(this->mMinContentBoost);
min_content_boost_log2 = (std::max)(min_content_boost_log2, suggestion);
}
if (fabs(max_content_boost_log2 - min_content_boost_log2) < FLT_EPSILON) {
max_content_boost_log2 += 0.1; // to avoid div by zero during affine transform
}
Expand Down
51 changes: 50 additions & 1 deletion lib/src/ultrahdr_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,52 @@ uhdr_error_info_t uhdr_enc_set_preset(uhdr_codec_private_t* enc, uhdr_enc_preset
return status;
}

uhdr_error_info_t uhdr_enc_set_min_max_content_boost(uhdr_codec_private_t* enc, float min_boost,
float max_boost) {
uhdr_error_info_t status = g_no_error;

if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
status.error_code = UHDR_CODEC_INVALID_PARAM;
status.has_detail = 1;
snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
return status;
}

if (max_boost < min_boost) {
status.error_code = UHDR_CODEC_INVALID_PARAM;
status.has_detail = 1;
snprintf(status.detail, sizeof status.detail,
"Invalid min boost / max boost configuration. configured max boost %f is less than "
"min boost %f",
max_boost, min_boost);
return status;
}

if (min_boost < 0) {
status.error_code = UHDR_CODEC_INVALID_PARAM;
status.has_detail = 1;
snprintf(status.detail, sizeof status.detail,
"Invalid min boost configuration. configured min boost %f is less than 0", min_boost);
return status;
}

uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);

if (handle->m_sailed) {
status.error_code = UHDR_CODEC_INVALID_OPERATION;
status.has_detail = 1;
snprintf(status.detail, sizeof status.detail,
"An earlier call to uhdr_encode() has switched the context from configurable state to "
"end state. The context is no longer configurable. To reuse, call reset()");
return status;
}

handle->m_min_content_boost = min_boost;
handle->m_max_content_boost = max_boost;

return status;
}

uhdr_error_info_t uhdr_enc_set_raw_image(uhdr_codec_private_t* enc, uhdr_raw_image_t* img,
uhdr_img_label_t intent) {
uhdr_error_info_t status = g_no_error;
Expand Down Expand Up @@ -1050,7 +1096,8 @@ uhdr_error_info_t uhdr_encode(uhdr_codec_private_t* enc) {

ultrahdr::JpegR jpegr(
nullptr, handle->m_gainmap_scale_factor, handle->m_quality.find(UHDR_GAIN_MAP_IMG)->second,
handle->m_use_multi_channel_gainmap, handle->m_gamma, handle->m_enc_preset);
handle->m_use_multi_channel_gainmap, handle->m_gamma, handle->m_enc_preset,
handle->m_min_content_boost, handle->m_max_content_boost);
if (handle->m_compressed_images.find(UHDR_BASE_IMG) != handle->m_compressed_images.end() &&
handle->m_compressed_images.find(UHDR_GAIN_MAP_IMG) != handle->m_compressed_images.end()) {
auto& base_entry = handle->m_compressed_images.find(UHDR_BASE_IMG)->second;
Expand Down Expand Up @@ -1151,6 +1198,8 @@ void uhdr_reset_encoder(uhdr_codec_private_t* enc) {
handle->m_use_multi_channel_gainmap = ultrahdr::kUseMultiChannelGainMapDefault;
handle->m_gamma = ultrahdr::kGainMapGammaDefault;
handle->m_enc_preset = UHDR_USAGE_REALTIME;
handle->m_min_content_boost = FLT_MIN;
handle->m_max_content_boost = FLT_MAX;

handle->m_compressed_output_buffer.reset();
handle->m_encode_call_status = g_no_error;
Expand Down
14 changes: 14 additions & 0 deletions ultrahdr_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,20 @@ UHDR_EXTERN uhdr_error_info_t uhdr_enc_set_gainmap_scale_factor(uhdr_codec_priva
*/
UHDR_EXTERN uhdr_error_info_t uhdr_enc_set_gainmap_gamma(uhdr_codec_private_t* enc, float gamma);

/*!\brief Set min max content boost. This configuration is treated as a recommendation by the
* library. It is entirely possible for the library to use a different set of values. Value MUST be
* in linear scale.
*
* \param[in] enc encoder instance.
* \param[in] min_boost min content boost. Any positive real number > 0.0f
* \param[in] max_boost max content boost. Any positive real number >= min_boost
*
* \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds,
* #UHDR_CODEC_INVALID_PARAM otherwise.
*/
UHDR_EXTERN uhdr_error_info_t uhdr_enc_set_min_max_content_boost(uhdr_codec_private_t* enc,
float min_boost, float max_boost);

/*!\brief Set encoding preset. Tunes the encoder configurations for performance or quality. Default
* configuration is #UHDR_USAGE_REALTIME.
*
Expand Down

0 comments on commit 6adb18e

Please sign in to comment.