Skip to content

Commit

Permalink
add support for limited range inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
erazortt committed Feb 12, 2024
1 parent 103f8aa commit 1944140
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 18 deletions.
11 changes: 7 additions & 4 deletions DoViBaker/AvisynthEntry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<bitDepth> 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<<bitDepth)-1) / 255.0 + 0.5;
uint16_t signalPq = i * 4095 / 255.0 + 0.5;
uint16_t mapped = tonemap.applyEETF(signal);
printf("%i %f %f %i\n",signal, DoViProcessor::pq2nits(signal), DoViProcessor::pq2nits(mapped), mapped);
uint16_t mappedPq = mapped * 4095.0 / ((1 << bitDepth) - 1) + 0.5;
printf("%i %f %f %i\n",signal, DoViProcessor::pq2nits(signalPq), DoViProcessor::pq2nits(mappedPq), mapped);
}
}

Expand Down
4 changes: 4 additions & 0 deletions DoViBaker/DoViBaker.vcxproj.user
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@
<LocalDebuggerCommandArguments>-showTonemap</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerCommandArguments>-showTonemap</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>
37 changes: 34 additions & 3 deletions DoViBaker/DoViEetf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand All @@ -18,7 +19,8 @@ void DoViEetf<signalBitDepth>::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);
Expand All @@ -39,8 +41,33 @@ void DoViEetf<signalBitDepth>::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);
Expand Down Expand Up @@ -69,5 +96,9 @@ void DoViEetf<signalBitDepth>::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;
}
}

23 changes: 14 additions & 9 deletions DoViBaker/DoViTonemap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand All @@ -24,6 +25,7 @@ DoViTonemap<signalBitDepth>::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)
Expand All @@ -32,10 +34,10 @@ DoViTonemap<signalBitDepth>::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");
}

Expand All @@ -45,7 +47,8 @@ DoViTonemap<signalBitDepth>::DoViTonemap(
targetMinPq,
masterMaxPq,
masterMinPq,
lumScale);
lumScale,
limitedInput);
}

template<int signalBitDepth>
Expand All @@ -64,13 +67,13 @@ PVideoFrame DoViTonemap<signalBitDepth>::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);
Expand All @@ -87,16 +90,18 @@ PVideoFrame DoViTonemap<signalBitDepth>::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);
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
3 changes: 2 additions & 1 deletion include/DoViEetf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions include/DoViTonemap.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class DoViTonemap : public GenericVideoFilter
uint16_t masterMaxPq;
uint16_t masterMinPq;
float lumScale;
bool limitedInput;

const bool dynamicMasterMaxPq;
const bool dynamicMasterMinPq;
Expand Down

0 comments on commit 1944140

Please sign in to comment.