From 19441409c937b8883a09dd6741d97e5c6f61353f Mon Sep 17 00:00:00 2001 From: erazortt Date: Mon, 12 Feb 2024 12:09:47 +0100 Subject: [PATCH] add support for limited range inputs --- DoViBaker/AvisynthEntry.cpp | 11 ++++++---- DoViBaker/DoViBaker.vcxproj.user | 4 ++++ DoViBaker/DoViEetf.cpp | 37 +++++++++++++++++++++++++++++--- DoViBaker/DoViTonemap.cpp | 23 ++++++++++++-------- README.md | 6 +++++- include/DoViEetf.h | 3 ++- include/DoViTonemap.h | 1 + 7 files changed, 67 insertions(+), 18 deletions(-) diff --git a/DoViBaker/AvisynthEntry.cpp b/DoViBaker/AvisynthEntry.cpp index 7beb9b1..8823035 100644 --- a/DoViBaker/AvisynthEntry.cpp +++ b/DoViBaker/AvisynthEntry.cpp @@ -422,12 +422,15 @@ int main(int argc, char* argv[]) uint16_t targetMinPq = DoViProcessor::nits2pq(2); uint16_t masterMaxPq = DoViProcessor::nits2pq(2000); uint16_t masterMinPq = DoViProcessor::nits2pq(1); - DoViEetf<12> tonemap(0.75, false); - tonemap.generateEETF(targetMaxPq, targetMinPq, masterMaxPq, masterMinPq, 1.0); + const int bitDepth = 12; + DoViEetf tonemap(0.75, false); + tonemap.generateEETF(targetMaxPq, targetMinPq, masterMaxPq, masterMinPq, 1.0, false); for (int i = 0; i <= 255; i++) { - uint16_t signal = i * 4095.0 / 255 + 0.5; + uint16_t signal = i * ((1<-showTonemap WindowsLocalDebugger + + -showTonemap + WindowsLocalDebugger + \ No newline at end of file diff --git a/DoViBaker/DoViEetf.cpp b/DoViBaker/DoViEetf.cpp index 8ea0fc7..b6b42cc 100644 --- a/DoViBaker/DoViEetf.cpp +++ b/DoViBaker/DoViEetf.cpp @@ -2,6 +2,7 @@ #include "DoViProcessor.h" // explicitly instantiate the template for the linker +template class DoViEetf<8>; template class DoViEetf<10>; template class DoViEetf<12>; template class DoViEetf<14>; @@ -18,7 +19,8 @@ void DoViEetf::generateEETF( uint16_t targetMinPq, uint16_t masterMaxPq, uint16_t masterMinPq, - float lumScale) + float lumScale, + bool limitedInput) { // based on report ITU-R BT.2408-7 Annex 5 (was in ITU-R BT.2390 until revision 7) float masterMaxEp = DoViProcessor::EOTFinv(DoViProcessor::EOTF(masterMaxPq / 4095.0f) * lumScale); @@ -39,8 +41,33 @@ void DoViEetf::generateEETF( // we don't need unnecessarily big values of the taper power, thus b is limited const float taperPwr = 1 / std::max(b, 0.01f); - for (int inSignal = 0; inSignal < LUT_SIZE; inSignal++) { - float ep = inSignal / float(LUT_SIZE - 1); + int inBlack = 0; + int inWhite = LUT_SIZE - 1; + if (limitedInput) { + inBlack = 16 << (signalBitDepth - 8); + inWhite = (235 << (signalBitDepth - 8)); + } + int inMinSignal = masterMinEp * (inWhite - inBlack) + inBlack + 0.5; + int inMaxSignal = masterMaxEp * (inWhite - inBlack) + inBlack + 0.5; + + // limiting is needed since masterMaxEp can be above 1 due to lumScale + inMinSignal = std::min(inMinSignal, LUT_SIZE); + inMaxSignal = std::min(inMaxSignal, LUT_SIZE); + + uint16_t outBlack = 0; + uint16_t outWhite = LUT_SIZE - 1; + if (!normalizeOutput) { + outBlack = targetMinEp * (LUT_SIZE - 1) + 0.5f; + outWhite = targetMaxEp * (LUT_SIZE - 1) + 0.5f; + } + + int inSignal = 0; + for (; inSignal < inMinSignal; inSignal++) { + // skip unnecessary processing where result is known + lut[inSignal] = outBlack; + } + for (; inSignal < inMaxSignal; inSignal++) { + float ep = (inSignal - inBlack) / float(inWhite - inBlack); float e = DoViProcessor::EOTF(ep) * lumScale; ep = DoViProcessor::EOTFinv(e); float e1 = (ep - masterMinEp) / (masterMaxEp - masterMinEp); @@ -69,5 +96,9 @@ void DoViEetf::generateEETF( uint16_t outSignal = e4 * (LUT_SIZE - 1) + 0.5f; lut[inSignal] = outSignal; } + for (; inSignal < LUT_SIZE; inSignal++) { + // skip unnecessary processing where result is known + lut[inSignal] = outWhite; + } } diff --git a/DoViBaker/DoViTonemap.cpp b/DoViBaker/DoViTonemap.cpp index f370154..63ef7e1 100644 --- a/DoViBaker/DoViTonemap.cpp +++ b/DoViBaker/DoViTonemap.cpp @@ -2,6 +2,7 @@ #include "DoViProcessor.h" // explicitly instantiate the template for the linker +// 8 bit inputs are not supported and don't make much sense anyhow template class DoViTonemap<10>; template class DoViTonemap<12>; template class DoViTonemap<14>; @@ -24,6 +25,7 @@ DoViTonemap::DoViTonemap( , masterMaxPq(DoViProcessor::nits2pq(masterMaxNits < 0 ? 10000 : masterMaxNits)) , masterMinPq(DoViProcessor::nits2pq(masterMinNits < 0 ? 0 : masterMinNits)) , lumScale(lumScale_ < 0 ? 1 : lumScale_) + , limitedInput(false) , dynamicMasterMaxPq(masterMaxNits < 0) , dynamicMasterMinPq(masterMinNits < 0) , dynamicLumScale(lumScale < 0) @@ -32,10 +34,10 @@ DoViTonemap::DoViTonemap( // prevent EETF tapering to fail from a too low value of the taper power env->ThrowError("DoViTonemap: Value for 'targetMinNits' is too large to process"); } - if (targetMaxPq < targetMinPq) { + if (targetMaxPq <= targetMinPq) { env->ThrowError("DoViTonemap: target capabilities given are invalid"); } - if (masterMaxPq < masterMinPq) { + if (masterMaxPq <= masterMinPq) { env->ThrowError("DoViTonemap: master capabilities given are invalid"); } @@ -45,7 +47,8 @@ DoViTonemap::DoViTonemap( targetMinPq, masterMaxPq, masterMinPq, - lumScale); + lumScale, + limitedInput); } template @@ -64,13 +67,13 @@ PVideoFrame DoViTonemap::GetFrame(int n, IScriptEnvironment* env uint16_t maxPq = masterMaxPq; uint16_t minPq = masterMinPq; float scale = lumScale; + bool limited = limitedInput; if (env->propNumElements(env->getFramePropsRO(src), "_ColorRange") > -1) { - bool limitedRange = env->propGetInt(env->getFramePropsRO(dst), "_ColorRange", 0, 0); - if (limitedRange) { - env->ThrowError("DoViTonemap: Only full range inputs supported"); - } + limited = env->propGetInt(env->getFramePropsRO(dst), "_ColorRange", 0, 0); } + // output is always full range independently of the input + env->propSetInt(env->getFramePropsRW(dst), "_ColorRange", 0, 0); if (dynamicMasterMaxPq) { if (env->propNumElements(env->getFramePropsRO(src), "_dovi_dynamic_max_pq") > -1) { maxPq = env->propGetInt(env->getFramePropsRO(src), "_dovi_dynamic_max_pq", 0, 0); @@ -87,16 +90,18 @@ PVideoFrame DoViTonemap::GetFrame(int n, IScriptEnvironment* env } else env->ThrowError("DoViTonemap: Expected frame property not available. Set 'lumScale' explicitly."); } - if (maxPq != masterMaxPq || minPq != masterMinPq || std::abs(scale-lumScale)>0.001f) { + if (maxPq != masterMaxPq || minPq != masterMinPq || std::abs(scale-lumScale)>0.001f || limited != limitedInput) { masterMaxPq = maxPq; masterMinPq = minPq; lumScale = scale; + limitedInput = limited; doviEetf->generateEETF( targetMaxPq, targetMinPq, masterMaxPq, masterMinPq, - lumScale); + lumScale, + limitedInput); } applyTonemapRGB(dst, src); diff --git a/README.md b/README.md index bfbff51..470d986 100644 --- a/README.md +++ b/README.md @@ -94,10 +94,14 @@ If your source is just PQ and doesn't have a DolbyVision substream, there are tw Shown below is the functional form of the tonemapping curve with the following parameters: masterMaxNits=10000, targetMaxNits=1000, masterMinNits=0, targetMinNits=0.1, lumscale=1. ![Tonemapping function](EETF.png "Tonemapping function") ## Frame Properties -The following frame properties will be consumed, if the related arguments `masterMaxNits`, `masterMinNits` and `lumScale` are set to `-1`: +The following frame properties will be consumed (if the related arguments `masterMaxNits`, `masterMinNits` and `lumScale` are set to `-1`): - `_dovi_dynamic_max_pq` the max_pq value of the current scene - `_dovi_dynamic_min_pq` the min_pq value of the current scene - `_dovi_dynamic_luminosity_scale` the luminosity scaling factor of the current scene +- `_ColorRange` both limited and full range RGB inputs are supported + +The following frame properties will be set: +- `_ColorRange` set to 0, since the output is always full range RGB independently of the input # DoViCubes This plugin provides LUT processing capabilites based on the frame property `_dovi_dynamic_max_content_light_level` set by either `DoViBaker` or `DoViStatsFileReader`. Different LUTs are applied based adjustable thresholds. This is done by providing a collection of LUTs and limits of validity measured in nits of max-content-light-level. (The LUT processing implentation is based on: https://github.com/sekrit-twc/timecube). diff --git a/include/DoViEetf.h b/include/DoViEetf.h index b859e57..fd6aadc 100644 --- a/include/DoViEetf.h +++ b/include/DoViEetf.h @@ -13,7 +13,8 @@ class DoViEetf uint16_t targetMinPq, uint16_t masterMaxPq, uint16_t masterMinPq, - float lumScale); + float lumScale, + bool limitedInput); private: static inline constexpr float eetfSpline(float e1, float kS, float maxLum); diff --git a/include/DoViTonemap.h b/include/DoViTonemap.h index 4a1fdd9..dc9c014 100644 --- a/include/DoViTonemap.h +++ b/include/DoViTonemap.h @@ -30,6 +30,7 @@ class DoViTonemap : public GenericVideoFilter uint16_t masterMaxPq; uint16_t masterMinPq; float lumScale; + bool limitedInput; const bool dynamicMasterMaxPq; const bool dynamicMasterMinPq;