From b7f5b753dd978a80344e9d5387858a39f5f94979 Mon Sep 17 00:00:00 2001 From: Awawa <69086569+awawa-dev@users.noreply.github.com> Date: Sat, 30 Nov 2024 01:58:53 +0100 Subject: [PATCH] Automatic tone mapping control: add timeout option for turning tone mapping off --- include/base/AutomaticToneMapping.h | 5 +- include/base/Grabber.h | 2 +- sources/base/AutomaticToneMapping.cpp | 57 ++++++++++++------- sources/base/Grabber.cpp | 4 +- sources/base/GrabberWrapper.cpp | 5 +- .../schema/schema-automaticToneMapping.json | 20 ++++++- sources/lut-calibrator/LutCalibrator.cpp | 2 +- www/i18n/en.json | 6 +- 8 files changed, 70 insertions(+), 31 deletions(-) diff --git a/include/base/AutomaticToneMapping.h b/include/base/AutomaticToneMapping.h index 5b17fa83..f435ca3d 100644 --- a/include/base/AutomaticToneMapping.h +++ b/include/base/AutomaticToneMapping.h @@ -41,7 +41,7 @@ class AutomaticToneMapping AutomaticToneMapping* prepare(); void finilize(); - void setConfig(bool enabled, const ToneMappingThresholds& newConfig, int timeInSec); + void setConfig(bool enabled, const ToneMappingThresholds& newConfig, int timeInSec, int timeToDisableInMSec); void setToneMapping(bool enabled); constexpr uint8_t checkY(uint8_t y) @@ -71,12 +71,13 @@ class AutomaticToneMapping private: bool _enabled; int _timeInSec; + int _timeToDisableInMSec; ToneMappingThresholds _config, _running; bool _modeSDR; long _startedTime; - int _gracefulTimeout; + long _endingTime; Logger* _log; }; diff --git a/include/base/Grabber.h b/include/base/Grabber.h index 6fe99485..0a69e8ec 100644 --- a/include/base/Grabber.h +++ b/include/base/Grabber.h @@ -126,7 +126,7 @@ class Grabber : public DetectionAutomatic, public DetectionManual, protected Lut QString getConfigurationPath(); - void setAutomaticToneMappingConfig(bool enabled, const AutomaticToneMapping::ToneMappingThresholds& newConfig, int timeInSec); + void setAutomaticToneMappingConfig(bool enabled, const AutomaticToneMapping::ToneMappingThresholds& newConfig, int timeInSec, int timeToDisableInMSec); void setAutoToneMappingCurrentStateEnabled(bool enabled); struct DevicePropertiesItem diff --git a/sources/base/AutomaticToneMapping.cpp b/sources/base/AutomaticToneMapping.cpp index c7b51e77..7152233e 100644 --- a/sources/base/AutomaticToneMapping.cpp +++ b/sources/base/AutomaticToneMapping.cpp @@ -37,11 +37,12 @@ AutomaticToneMapping::AutomaticToneMapping() : _enabled(false), _timeInSec(30), + _timeToDisableInMSec(500), _config{}, _running{}, _modeSDR(true), _startedTime(0), - _gracefulTimeout(DEFAULT_GRACEFUL_TIMEOUT), + _endingTime(0), _log(Logger::getInstance(QString("AUTOTONEMAPPING"))) { } @@ -64,20 +65,28 @@ void AutomaticToneMapping::finilize() { bool triggered = (_config.y != _running.y || _config.u != _running.u || _config.v != _running.v); - if (triggered && !_modeSDR && _gracefulTimeout-- <= 0) + if (triggered && !_modeSDR) { - QString message = "Tone mapping OFF triggered by: "; - if (_config.y != _running.y) - message += QString(" Y threshold (%1), ").arg(_running.y); - if (_config.u != _running.u) - message += QString(" U threshold (%1), ").arg(_running.u); - if (_config.v != _running.v) - message += QString(" V threshold (%1), ").arg(_running.v); - Info(_log, "%s", QSTRING_CSTR(message)); - - _modeSDR = true; - _startedTime = 0; - GlobalSignals::getInstance()->SignalRequestComponent(hyperhdr::Components::COMP_HDR, -1, false); + auto now = InternalClock::now(); + if (_endingTime == 0 || _endingTime > now) + { + _endingTime = now; + } + else if (_endingTime + _timeToDisableInMSec <= now) + { + QString message = "Tone mapping OFF triggered by: "; + if (_config.y != _running.y) + message += QString(" Y threshold (%1), ").arg(_running.y); + if (_config.u != _running.u) + message += QString(" U threshold (%1), ").arg(_running.u); + if (_config.v != _running.v) + message += QString(" V threshold (%1), ").arg(_running.v); + Info(_log, "%s after %i ms", QSTRING_CSTR(message), now - _endingTime); + + + _modeSDR = true; + GlobalSignals::getInstance()->SignalRequestComponent(hyperhdr::Components::COMP_HDR, -1, false); + } } else if (!triggered && _modeSDR) { @@ -86,35 +95,43 @@ void AutomaticToneMapping::finilize() { _startedTime = now; } - else if (_startedTime + _timeInSec < now) + else if (_startedTime + _timeInSec <= now) { _modeSDR = false; - Info(_log, "Tone mapping ON triggered by configured time"); + Info(_log, "Tone mapping ON triggered after %i sec", now - _startedTime); GlobalSignals::getInstance()->SignalRequestComponent(hyperhdr::Components::COMP_HDR, -1, true); } } if (!triggered) - _gracefulTimeout = DEFAULT_GRACEFUL_TIMEOUT; + { + _endingTime = 0; + } + else + { + _startedTime = 0; + } _running = _config; } } -void AutomaticToneMapping::setConfig(bool enabled, const ToneMappingThresholds& newConfig, int timeInSec) +void AutomaticToneMapping::setConfig(bool enabled, const ToneMappingThresholds& newConfig, int timeInSec, int timeToDisableInMSec) { _enabled = enabled; _config = newConfig; _timeInSec = timeInSec; + _timeToDisableInMSec = timeToDisableInMSec; _running = _config; - Info(_log, "Enabled: %s, Time: %i, Thresholds: %i, %i, %i", (enabled) ? "yes" : "no", timeInSec, _config.y, _config.u, _config.v); + Info(_log, "Enabled: %s, time to enable: %is, time to disable: %ims, thresholds: { %i, %i, %i}", (enabled) ? "yes" : "no", _timeInSec, _timeToDisableInMSec, _config.y, _config.u, _config.v); } void AutomaticToneMapping::setToneMapping(bool enabled) { + Info(_log, "Tone mapping is currently: %s", (enabled) ? "enabled" : "disabled"); _modeSDR = !enabled; _startedTime = 0; - _gracefulTimeout = DEFAULT_GRACEFUL_TIMEOUT; + _endingTime = 0; } diff --git a/sources/base/Grabber.cpp b/sources/base/Grabber.cpp index 9974b26b..d25dfa0b 100644 --- a/sources/base/Grabber.cpp +++ b/sources/base/Grabber.cpp @@ -967,9 +967,9 @@ void Grabber::signalSetLutHandler(MemoryBuffer* lut) Error(_log, "Could not set LUT: current size = %i, incoming size = %i", _lut.size(), (lut != nullptr) ? lut->size() : 0); } -void Grabber::setAutomaticToneMappingConfig(bool enabled, const AutomaticToneMapping::ToneMappingThresholds& newConfig, int timeInSec) +void Grabber::setAutomaticToneMappingConfig(bool enabled, const AutomaticToneMapping::ToneMappingThresholds& newConfig, int timeInSec, int timeToDisableInMSec) { - _automaticToneMapping.setConfig(enabled, newConfig, timeInSec); + _automaticToneMapping.setConfig(enabled, newConfig, timeInSec, timeToDisableInMSec); if (_automaticToneMapping.prepare() && !_qframe) Error(_log, "Automatic tone mapping requires 'Quarter of frame' mode enabled"); if (_automaticToneMapping.prepare() && (_enc != PixelFormat::YUYV && _enc != PixelFormat::NV12 && _enc != PixelFormat::P010 )) diff --git a/sources/base/GrabberWrapper.cpp b/sources/base/GrabberWrapper.cpp index 2f9db2c5..29bbcf96 100644 --- a/sources/base/GrabberWrapper.cpp +++ b/sources/base/GrabberWrapper.cpp @@ -589,8 +589,9 @@ void GrabberWrapper::handleSettingsUpdate(settings::type type, const QJsonDocume t.y = obj["tone_mapping_y_threshold"].toInt(155); t.u = obj["tone_mapping_u_threshold"].toInt(175); t.v = obj["tone_mapping_v_threshold"].toInt(160); - auto time = obj["time_to_tone_mapping"].toInt(30); - _grabber->setAutomaticToneMappingConfig(enabled, t, time); + auto timeToEnableInSec = obj["time_to_tone_mapping"].toInt(30); + auto timeToDisableInMSec = obj["time_to_disable_tone_mapping"].toInt(500); + _grabber->setAutomaticToneMappingConfig(enabled, t, timeToEnableInSec, timeToDisableInMSec); } } diff --git a/sources/base/schema/schema-automaticToneMapping.json b/sources/base/schema/schema-automaticToneMapping.json index 94cf81b0..7cf2780d 100644 --- a/sources/base/schema/schema-automaticToneMapping.json +++ b/sources/base/schema/schema-automaticToneMapping.json @@ -80,7 +80,25 @@ } }, "propertyOrder" : 5 - } + }, + "time_to_disable_tone_mapping" : + { + "type" : "integer", + "format": "stepper", + "step" : 100, + "title" : "edt_automatic_tone_mapping_disable_time_title", + "minimum" : 0, + "maximum": 5000, + "default" : 500, + "append" : "edt_append_ms", + "required" : true, + "options": { + "dependencies": { + "enable": true + } + }, + "propertyOrder" : 6 + } }, "additionalProperties" : false } diff --git a/sources/lut-calibrator/LutCalibrator.cpp b/sources/lut-calibrator/LutCalibrator.cpp index 2d6b3c5b..6e80516b 100644 --- a/sources/lut-calibrator/LutCalibrator.cpp +++ b/sources/lut-calibrator/LutCalibrator.cpp @@ -1852,7 +1852,7 @@ void LutCalibrator::CreateDefaultLut(QString filepath) bestResult.signal.downYLimit = 0.062745; bestResult.signal.yShift = 0.062745; bestResult.signal.isSourceP010 = 0; - bestResult.minError = 212.883333; + bestResult.minError = 212; auto worker = new DefaultLutCreatorWorker(bestResult, filepath); QThreadPool::globalInstance()->start(worker); diff --git a/www/i18n/en.json b/www/i18n/en.json index 6af8b4fc..39ab28e2 100644 --- a/www/i18n/en.json +++ b/www/i18n/en.json @@ -1253,7 +1253,7 @@ "chk_lchCorrection": "LCH color correction", "grabber_calibration_expl": "This tool allows you to create a new calibrated HDR LUT for your grabber (or external flatbuffers source) as close to the actual input colors as possible.
You need an HDR10 video source that can display this web page, for example: Windows 10 with HDR enabled in the properties of the graphics driver.
The screen may flicker during calibration. The process typically takes about few minutes on a Intel 7 Windows PC (depending on the host CPU resources and the video capturing framerate).
The calculations are very intensive and put a strain on your equipment You can disable LCH color correction to reduce the load a bit
You can monitor the progress in HyperHDR logs using the browser from other device.


1 If everything is properly connected, this page should be displayed on the TV screen (as HDR content) and live preview in HyperHDR (captured by the grabber).
2 Absolute minimum capturing resolution is 1280x720 (we will verify this). Recommended is 1920x1080 YUV/NV12. Aspect 1920/1080 must be preserved.
3 You must disable 'Quarter of frame mode' in your grabber properties if it's enabled.
4 You must set the grabber's video format to MJPEG/YUV/NV12/P010.
5 Before you run the process please put your WWW browser in the full-screen mode (F11 key, we will verify this).
6 If you are calibrating using Windows 11, turn off features such as 'Night light', 'Automatic manage color for apps' and 'Auto-HDR'. Do not change the color balance in the graphics driver. The GFX output should support e.g. 10 or 12 bit RGB in full PC range.

After completing the calibration, your new LUT table file (lut_lin_tables.3d) will be created in the user's HyperHDR home directory and is immediately ready to use when you just enable HDR tone mapping. Please verify HyperHDR logs for details.", "edt_automatic_tone_mapping_title" : "Automatic tone mapping", - "edt_automatic_tone_mapping_enable_explain" : "Automatic tone mapping control is only available for 'YUV/NV12/P010' video formats with 'Quarter of frame' mode enabled. Please configure them in the USB grabber settings.
The component will automatically enable tone mapping if the signal does not exceed the configured threshold levels for a certain period of time, and will disable it immediately after exceeding them.", + "edt_automatic_tone_mapping_enable_explain" : "Automatic tone mapping control is only available for 'YUV/NV12/P010' video formats with 'Quarter of frame' mode enabled. Please configure them in the USB grabber settings.
The component will automatically enable tone mapping if the signal does not exceed the configured threshold levels for a certain period of time, and will disable it immediately after exceeding them.
Then later you can check what was the value that exceeded any of the thresholds, which actually caused tone mapping to be disabled in HyperHDR logs. And then possibly adjust the configuration if it generates false events of tone mapping disabling (threshold too low) or generates unnecessary tone mapping enabling in dark SDR scenes (threshold too high).", "edt_automatic_tone_mapping_y_threshold_title" : "Brightness threshold (Y)", "edt_automatic_tone_mapping_y_threshold_expl" : "The brightness level that separates raw dark HDR material from the SDR signal", "edt_automatic_tone_mapping_u_threshold_title" : "Blue chroma threshold (U)", @@ -1261,5 +1261,7 @@ "edt_automatic_tone_mapping_v_threshold_title" : "Red chroma threshold (V)", "edt_automatic_tone_mapping_v_threshold_expl" : "Red chroma level that separates raw pale HDR material from the SDR signal", "edt_automatic_tone_mapping_time_title" : "Time to turn on tone mapping", - "edt_automatic_tone_mapping_time_expl" : "Time to turn on tone mapping if the signal does not exceed the configured threshold levels" + "edt_automatic_tone_mapping_time_expl" : "Time to turn on tone mapping if the signal does not exceed the configured threshold levels", + "edt_automatic_tone_mapping_disable_time_title" : "Time to turn off tone mapping", + "edt_automatic_tone_mapping_disable_time_expl" : "In an ideal world this value should be zero, because tone mapping should be disabled immediately after crossing one of the thresholds. But it happens, for example: when starting or switching resolution, the grabber can generate junk frames, which can disable tone mapping unnecessarily." }