From patchwork Thu Dec 4 20:24:59 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rui Wang X-Patchwork-Id: 25365 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id BB122BD80A for ; Thu, 4 Dec 2025 20:25:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6D50461145; Thu, 4 Dec 2025 21:25:27 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Gi+T8hKe"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8A4E461136 for ; Thu, 4 Dec 2025 21:25:25 +0100 (CET) Received: from localhost.localdomain (unknown [209.216.103.65]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7AECFEB7; Thu, 4 Dec 2025 21:23:09 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1764879790; bh=1ehvjQdVxodgJZnewrT50IJCpgntDXllaau3hYSJ3+Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Gi+T8hKeE5wydwJHqLe7bVuUYOIfw4Qr0ckx/4xKL4G8Iq3lQ20yEBXsZojl1El9K 8LgFtT003CRa69uODNFYh+4cA/k1PwEwUnvBH5VmSnJVuJqbliKetYALkZh8g0K+iM 7jycEQ6nzPER7iQeKSTJqrQaH6r9eFjkFlibUaJ8= From: Rui Wang To: libcamera-devel@lists.libcamera.org Cc: Rui Wang Subject: [PATCH v2 1/2] ipa/rkisp1: refactory DPF parsing and initialization Date: Thu, 4 Dec 2025 15:24:59 -0500 Message-ID: <20251204202500.1075575-2-rui.wang@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251204202500.1075575-1-rui.wang@ideasonboard.com> References: <20251204202500.1075575-1-rui.wang@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Split DPF configuration parsing and initialization into clearer, self-contained helpers and modernized initialization patterns. Add parseConfig, parseModes and loadReductionConfig to factor parsing logic and mode lookups. Introduce ModeConfig and store mode entries in noiseReductionModes_ to decouple parsing from runtime selection. Move sentinel member initializers into the constructor initializer list (runningMode_) and declare vectors without in-class initializers for default construction. Replace ad-hoc string handling with value_or and const auto where appropriate for clearer and safer parsing. Replace domain/range/strength YAML keys mapping and error returns (use filter, nll, strength keys and return false from parse helpers instead of -EINVAL). Add loadReductionConfig to centralize loading of DPF configs for reduction modes and preserve logging and failure behavior. Adjust queueRequest, prepare, and helpers to use the new parsing/initialization flow while preserving existing behavior. This refactor improves separation of concerns, makes parsing easier to maintain, and reduces duplicated logic. No functional behaviour is intended to be changed. Signed-off-by: Rui Wang --- src/ipa/rkisp1/algorithms/dpf.cpp | 290 ++++++++++++++++++++++-------- src/ipa/rkisp1/algorithms/dpf.h | 19 ++ 2 files changed, 233 insertions(+), 76 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/dpf.cpp b/src/ipa/rkisp1/algorithms/dpf.cpp index 39f3e461..cf1f9199 100644 --- a/src/ipa/rkisp1/algorithms/dpf.cpp +++ b/src/ipa/rkisp1/algorithms/dpf.cpp @@ -37,7 +37,8 @@ namespace ipa::rkisp1::algorithms { LOG_DEFINE_CATEGORY(RkISP1Dpf) Dpf::Dpf() - : config_({}), strengthConfig_({}) + : config_({}), strengthConfig_({}), + runningMode_(controls::draft::NoiseReductionModeOff) { } @@ -46,6 +47,69 @@ Dpf::Dpf() */ int Dpf::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData) +{ + /* Parse tuning block. */ + if (!parseConfig(tuningData)) { + return -EINVAL; + } + + return 0; +} +bool Dpf::parseConfig(const YamlObject &tuningData) +{ + /* Parse base config. */ + if (!parseSingleConfig(tuningData, config_, strengthConfig_)) { + return false; + } + if (!parseModes(tuningData)) { + return false; + } + return true; +} + +bool Dpf::parseModes(const YamlObject &tuningData) +{ + /* Parse noise reduction modes. */ + if (!tuningData.contains("modes")) { + return true; + } + + noiseReductionModes_.clear(); + for (const auto &entry : tuningData["modes"].asList()) { + std::optional typeOpt = + entry["type"].get(); + if (!typeOpt) { + LOG(RkISP1Dpf, Error) << "Modes entry missing type"; + return false; + } + + int32_t modeValue = controls::draft::NoiseReductionModeOff; + if (*typeOpt == "minimal") { + modeValue = controls::draft::NoiseReductionModeMinimal; + } else if (*typeOpt == "fast") { + modeValue = controls::draft::NoiseReductionModeFast; + } else if (*typeOpt == "highquality") { + modeValue = controls::draft::NoiseReductionModeHighQuality; + } else if (*typeOpt == "zsl") { + modeValue = controls::draft::NoiseReductionModeZSL; + } else { + LOG(RkISP1Dpf, Error) << "Unknown mode type: " << *typeOpt; + return false; + } + + ModeConfig mode{}; + mode.modeValue = modeValue; + if (!parseSingleConfig(entry, mode.dpf, mode.strength)) { + return false; + } + noiseReductionModes_.push_back(mode); + } + + return true; +} +bool Dpf::parseSingleConfig(const YamlObject &tuningData, + rkisp1_cif_isp_dpf_config &config, + rkisp1_cif_isp_dpf_strength_config &strengthConfig) { std::vector values; @@ -53,7 +117,11 @@ int Dpf::init([[maybe_unused]] IPAContext &context, * The domain kernel is configured with a 9x9 kernel for the green * pixels, and a 13x9 or 9x9 kernel for red and blue pixels. */ - const YamlObject &dFObject = tuningData["DomainFilter"]; + const YamlObject &dFObject = tuningData["filter"]; + if (!dFObject) { + LOG(RkISP1Dpf, Error) << "filter section missing"; + return false; + } /* * For the green component, we have the 9x9 kernel specified @@ -75,10 +143,10 @@ int Dpf::init([[maybe_unused]] IPAContext &context, values = dFObject["g"].getList().value_or(std::vector{}); if (values.size() != RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS) { LOG(RkISP1Dpf, Error) - << "Invalid 'DomainFilter:g': expected " + << "Invalid 'filter:g': expected " << RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS << " elements, got " << values.size(); - return -EINVAL; + return false; } std::copy_n(values.begin(), values.size(), @@ -112,16 +180,16 @@ int Dpf::init([[maybe_unused]] IPAContext &context, if (values.size() != RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS && values.size() != RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS - 1) { LOG(RkISP1Dpf, Error) - << "Invalid 'DomainFilter:rb': expected " + << "Invalid 'filter:rb': expected " << RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS - 1 << " or " << RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS << " elements, got " << values.size(); - return -EINVAL; + return false; } config_.rb_flt.fltsize = values.size() == RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS - ? RKISP1_CIF_ISP_DPF_RB_FILTERSIZE_13x9 - : RKISP1_CIF_ISP_DPF_RB_FILTERSIZE_9x9; + ? RKISP1_CIF_ISP_DPF_RB_FILTERSIZE_13x9 + : RKISP1_CIF_ISP_DPF_RB_FILTERSIZE_9x9; std::copy_n(values.begin(), values.size(), std::begin(config_.rb_flt.spatial_coeff)); @@ -134,43 +202,86 @@ int Dpf::init([[maybe_unused]] IPAContext &context, * which stores a piecewise linear function that characterizes the * sensor noise profile as a noise level function curve (NLF). */ - const YamlObject &rFObject = tuningData["NoiseLevelFunction"]; + const YamlObject &rFObject = tuningData["nll"]; + if (!rFObject) { + LOG(RkISP1Dpf, Error) << "nll section missing"; + return false; + } - std::vector nllValues; - nllValues = rFObject["coeff"].getList().value_or(std::vector{}); + const auto nllValues = + rFObject["coeff"].getList().value_or(std::vector{}); if (nllValues.size() != RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS) { LOG(RkISP1Dpf, Error) - << "Invalid 'RangeFilter:coeff': expected " + << "Invalid 'nll:coeff': expected " << RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS << " elements, got " << nllValues.size(); - return -EINVAL; + return false; } std::copy_n(nllValues.begin(), nllValues.size(), std::begin(config_.nll.coeff)); - std::string scaleMode = rFObject["scale-mode"].get(""); + const auto scaleMode = rFObject["scale-mode"].get(""); if (scaleMode == "linear") { config_.nll.scale_mode = RKISP1_CIF_ISP_NLL_SCALE_LINEAR; } else if (scaleMode == "logarithmic") { config_.nll.scale_mode = RKISP1_CIF_ISP_NLL_SCALE_LOGARITHMIC; } else { LOG(RkISP1Dpf, Error) - << "Invalid 'RangeFilter:scale-mode': expected " + << "Invalid 'nll:scale-mode': expected " << "'linear' or 'logarithmic' value, got " << scaleMode; - return -EINVAL; + return false; } - const YamlObject &fSObject = tuningData["FilterStrength"]; + const YamlObject &gObject = tuningData["gain"]; + if (!gObject) { + LOG(RkISP1Dpf, Error) << "gain section missing"; + return false; + } - strengthConfig_.r = fSObject["r"].get(64); - strengthConfig_.g = fSObject["g"].get(64); - strengthConfig_.b = fSObject["b"].get(64); + config.gain.mode = + gObject["gain_mode"].get().value_or( + RKISP1_CIF_ISP_DPF_GAIN_USAGE_AWB_LSC_GAINS); + config.gain.nf_r_gain = gObject["r"].get().value_or(256); + config.gain.nf_b_gain = gObject["b"].get().value_or(256); + config.gain.nf_gr_gain = gObject["gr"].get().value_or(256); + config.gain.nf_gb_gain = gObject["gb"].get().value_or(256); + + const YamlObject &fSObject = tuningData["strength"]; + if (!fSObject) { + LOG(RkISP1Dpf, Error) << "strength section missing"; + return false; + } - return 0; + strengthConfig.r = fSObject["r"].get().value_or(64); + strengthConfig.g = fSObject["g"].get().value_or(64); + strengthConfig.b = fSObject["b"].get().value_or(64); + return true; } +bool Dpf::loadReductionConfig(int32_t mode) +{ + auto it = std::find_if(noiseReductionModes_.begin(), noiseReductionModes_.end(), + [mode](const ModeConfig &m) { + return m.modeValue == mode; + }); + if (it == noiseReductionModes_.end()) { + LOG(RkISP1Dpf, Warning) + << "No DPF config for reduction mode " + << static_cast(mode); + return false; + } + + config_ = it->dpf; + strengthConfig_ = it->strength; + + LOG(RkISP1Dpf, Info) + << "DPF mode=Reduction (config loaded)" + << " mode=" << static_cast(mode); + + return true; +} /** * \copydoc libcamera::ipa::Algorithm::queueRequest */ @@ -183,30 +294,34 @@ void Dpf::queueRequest(IPAContext &context, bool update = false; const auto &denoise = controls.get(controls::draft::NoiseReductionMode); - if (denoise) { - LOG(RkISP1Dpf, Debug) << "Set denoise to " << *denoise; - - switch (*denoise) { - case controls::draft::NoiseReductionModeOff: - if (dpf.denoise) { - dpf.denoise = false; - update = true; - } - break; - case controls::draft::NoiseReductionModeMinimal: - case controls::draft::NoiseReductionModeHighQuality: - case controls::draft::NoiseReductionModeFast: - if (!dpf.denoise) { - dpf.denoise = true; - update = true; - } - break; - default: - LOG(RkISP1Dpf, Error) - << "Unsupported denoise value " - << *denoise; - break; + if (!denoise) { + return; + } + runningMode_ = *denoise; + switch (runningMode_) { + case controls::draft::NoiseReductionModeOff: + if (dpf.denoise) { + dpf.denoise = false; + update = true; + } + break; + case controls::draft::NoiseReductionModeMinimal: + case controls::draft::NoiseReductionModeHighQuality: + case controls::draft::NoiseReductionModeFast: + case controls::draft::NoiseReductionModeZSL: + if (loadReductionConfig(runningMode_)) { + update = true; + dpf.denoise = true; + } else { + dpf.denoise = false; + update = true; } + break; + default: + LOG(RkISP1Dpf, Error) + << "Unsupported denoise value " + << *denoise; + break; } frameContext.dpf.denoise = dpf.denoise; @@ -219,41 +334,64 @@ void Dpf::queueRequest(IPAContext &context, void Dpf::prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, RkISP1Params *params) { - if (!frameContext.dpf.update && frame > 0) + if (!frameContext.dpf.update && frame > 0) { + return; + } + + if (!frameContext.dpf.denoise) { + prepareDisabledMode(context, frame, frameContext, params); return; + } + + prepareEnabledMode(context, frame, frameContext, params); +} + +void Dpf::prepareDisabledMode([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + [[maybe_unused]] IPAFrameContext &frameContext, + RkISP1Params *params) +{ + frameContext.dpf.denoise = false; + auto dpfConfig = params->block(); + dpfConfig.setEnabled(false); + auto dpfStrength = params->block(); + dpfStrength.setEnabled(false); +} - auto config = params->block(); - config.setEnabled(frameContext.dpf.denoise); - - auto strengthConfig = params->block(); - strengthConfig.setEnabled(frameContext.dpf.denoise); - - if (frameContext.dpf.denoise) { - *config = config_; - *strengthConfig = strengthConfig_; - - const auto &awb = context.configuration.awb; - const auto &lsc = context.configuration.lsc; - - auto &mode = config->gain.mode; - - /* - * The DPF needs to take into account the total amount of - * digital gain, which comes from the AWB and LSC modules. The - * DPF hardware can be programmed with a digital gain value - * manually, but can also use the gains supplied by the AWB and - * LSC modules automatically when they are enabled. Use that - * mode of operation as it simplifies control of the DPF. - */ - if (awb.enabled && lsc.enabled) - mode = RKISP1_CIF_ISP_DPF_GAIN_USAGE_AWB_LSC_GAINS; - else if (awb.enabled) - mode = RKISP1_CIF_ISP_DPF_GAIN_USAGE_AWB_GAINS; - else if (lsc.enabled) - mode = RKISP1_CIF_ISP_DPF_GAIN_USAGE_LSC_GAINS; - else - mode = RKISP1_CIF_ISP_DPF_GAIN_USAGE_DISABLED; +void Dpf::prepareEnabledMode(IPAContext &context, [[maybe_unused]] const uint32_t frame, + [[maybe_unused]] IPAFrameContext &frameContext, RkISP1Params *params) +{ + auto dpfConfig = params->block(); + dpfConfig.setEnabled(true); + *dpfConfig = config_; + + /* + * The DPF needs to take into account the total amount of + * digital gain, which comes from the AWB and LSC modules. The + * DPF hardware can be programmed with a digital gain value + * manually, but can also use the gains supplied by the AWB and + * LSC modules automatically when they are enabled. Use that + * mode of operation as it simplifies control of the DPF. + */ + const auto &awb = context.configuration.awb; + const auto &lsc = context.configuration.lsc; + auto &mode = dpfConfig->gain.mode; + + if (awb.enabled && lsc.enabled) { + mode = RKISP1_CIF_ISP_DPF_GAIN_USAGE_AWB_LSC_GAINS; + } else if (awb.enabled) { + mode = RKISP1_CIF_ISP_DPF_GAIN_USAGE_AWB_GAINS; + } else if (lsc.enabled) { + mode = RKISP1_CIF_ISP_DPF_GAIN_USAGE_LSC_GAINS; + } else { + mode = RKISP1_CIF_ISP_DPF_GAIN_USAGE_DISABLED; } + config_.gain.mode = mode; + + auto dpfStrength = params->block(); + dpfStrength.setEnabled(true); + + *dpfStrength = strengthConfig_; } REGISTER_IPA_ALGORITHM(Dpf, "Dpf") diff --git a/src/ipa/rkisp1/algorithms/dpf.h b/src/ipa/rkisp1/algorithms/dpf.h index 2dd8cd36..163a5b1f 100644 --- a/src/ipa/rkisp1/algorithms/dpf.h +++ b/src/ipa/rkisp1/algorithms/dpf.h @@ -30,8 +30,27 @@ public: RkISP1Params *params) override; private: + struct ModeConfig { + int32_t modeValue; + rkisp1_cif_isp_dpf_config dpf; + rkisp1_cif_isp_dpf_strength_config strength; + }; + bool parseConfig(const YamlObject &tuningData); + bool parseModes(const YamlObject &tuningData); + bool parseSingleConfig(const YamlObject &tuningData, + rkisp1_cif_isp_dpf_config &config, + rkisp1_cif_isp_dpf_strength_config &strengthConfig); + bool loadReductionConfig(int32_t mode); + void logConfigIfChanged(const IPAFrameContext &frameContext); + void prepareDisabledMode(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + RkISP1Params *params); + void prepareEnabledMode(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, RkISP1Params *params); struct rkisp1_cif_isp_dpf_config config_; struct rkisp1_cif_isp_dpf_strength_config strengthConfig_; + std::vector noiseReductionModes_; + int32_t runningMode_; }; } /* namespace ipa::rkisp1::algorithms */ From patchwork Thu Dec 4 20:25:00 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rui Wang X-Patchwork-Id: 25366 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 4E88DBD80A for ; Thu, 4 Dec 2025 20:25:59 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 16BCD6115B; Thu, 4 Dec 2025 21:25:59 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="kvl1fdT/"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id EDA3E61136 for ; Thu, 4 Dec 2025 21:25:56 +0100 (CET) Received: from localhost.localdomain (unknown [209.216.103.65]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1C1ADE91; Thu, 4 Dec 2025 21:23:41 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1764879821; bh=rZ0qMrC4Rjs2hrokDqG2Mp93Ahv3kwm8DiQNIEzqrM0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kvl1fdT/GFm6vBVBKzIokM663926L7PL4mvO0zxCeOZeu6x8sLzOzdD5sfTU+oOU9 3ssWCasBSZt3q7+pk/I0CZ39Ou0iKZpM26t2kMxME4dflLTOB6r8SiQCYcnEGKT6cG ANinIeXoEV39jMhpXwoDuWFI4KWpUFn1NN+L0AEg= From: Rui Wang To: libcamera-devel@lists.libcamera.org Cc: Rui Wang Subject: [PATCH v2 2/2] imx219: enable DPF tuning for IMX219 sensor Date: Thu, 4 Dec 2025 15:25:00 -0500 Message-ID: <20251204202500.1075575-3-rui.wang@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251204202500.1075575-1-rui.wang@ideasonboard.com> References: <20251204202500.1075575-1-rui.wang@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Enable the RkISP1 denoise pre-filter (DPF) for the IMX219 sensor by adding the required DPF tuning block to imx219.yaml. Signed-off-by: Rui Wang --- src/ipa/rkisp1/data/imx219.yaml | 90 +++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/ipa/rkisp1/data/imx219.yaml b/src/ipa/rkisp1/data/imx219.yaml index 0d99cb52..5b0b2771 100644 --- a/src/ipa/rkisp1/data/imx219.yaml +++ b/src/ipa/rkisp1/data/imx219.yaml @@ -111,4 +111,94 @@ algorithms: 1438, 1226, 1059, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1025, 1054, 1185, 1326, 1334, 1334, ] + - Dpf: + filter: + g: [ 18, 13, 9, 5, 3, 1 ] + rb: [ 18, 15, 12, 8, 5, 2 ] + nll: + coeff: [ + 0, 14, 28, 42, 58, 76, 96, 120, + 148, 180, 216, 256, 300, 348, 400, 456, + 520 + ] + scale-mode: "linear" + strength: + r: 80 + g: 80 + b: 80 + gain: + gain_mode: 5 + r: 256 + b: 256 + gr: 256 + gb: 256 + modes: + - type: "minimal" + filter: + g: [ 14, 10, 7, 4, 2, 1 ] + rb: [ 14, 11, 8, 4, 2, 1 ] + nll: + coeff: [ 0, 10, 20, 30, 42, 56, 72, 90, 112, 138, 168, 200, 236, 276, 320, 368, 420 ] + scale-mode: "linear" + strength: + r: 60 + g: 60 + b: 60 + gain: + gain_mode: 5 + r: 256 + b: 256 + gr: 256 + gb: 256 + - type: "highquality" + filter: + g: [ 22, 18, 13, 8, 5, 2 ] + rb: [ 20, 18, 16, 11, 7, 3 ] + nll: + coeff: [ 0, 26, 52, 78, 106, 138, 172, 208, 248, 292, 340, 392, 448, 508, 572, 640, 712 ] + scale-mode: "linear" + strength: + r: 130 + g: 130 + b: 130 + gain: + gain_mode: 5 + r: 256 + b: 256 + gr: 256 + gb: 256 + - type: "fast" + filter: + g: [ 16, 12, 9, 5, 3, 1 ] + rb: [ 16, 13, 10, 6, 4, 2 ] + nll: + coeff: [ 0, 16, 32, 48, 66, 86, 108, 132, 160, 192, 228, 268, 312, 360, 412, 468, 528 ] + scale-mode: "linear" + strength: + r: 90 + g: 90 + b: 90 + gain: + gain_mode: 5 + r: 256 + b: 256 + gr: 256 + gb: 256 + - type: "zsl" + filter: + g: [ 18, 14, 10, 6, 3, 1 ] + rb: [ 18, 16, 13, 9, 5, 3 ] + nll: + coeff: [ 0, 20, 40, 60, 82, 106, 132, 160, 192, 228, 268, 312, 360, 412, 468, 528, 592 ] + scale-mode: "linear" + strength: + r: 110 + g: 110 + b: 110 + gain: + gain_mode: 5 + r: 256 + b: 256 + gr: 256 + gb: 256 ...