From patchwork Tue Nov 25 00:08:39 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rui Wang X-Patchwork-Id: 25169 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 01580C3257 for ; Tue, 25 Nov 2025 00:09:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 99B5A60A80; Tue, 25 Nov 2025 01:09:06 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="dsPn0jdT"; 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 38D2D608CF for ; Tue, 25 Nov 2025 01:09:05 +0100 (CET) Received: from rui-Precision-7560.local (unknown [IPv6:2607:fea8:935b:7220:cb34:a7b8:53d:5466]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3482D1648; Tue, 25 Nov 2025 01:06:56 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1764029216; bh=IMB0LPoQqYANqR/0AvPU4uc9w6SsnqDnZqhk1APmWQk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dsPn0jdTT3Q3FVwWQXYJArNtQkwpI4ZxiBE9KdUyOTbTh/yq3tGVRf0SVmDQIXgVE jLWtzj33SIA1MPmDaFVNIBrm1lGqxqAXnwTtJUueaZ2B0AnDC55lmUa9LMwxjfK8az bSFab+k55gj7yFi1Gf0ecqtga88XjmtEdzShfrZ0= From: Rui Wang To: libcamera-devel@lists.libcamera.org Cc: Rui Wang Subject: [PATCH v1 02/11] ipa: rkisp1: algorithms: dpf: init Dpf tuning config Date: Mon, 24 Nov 2025 19:08:39 -0500 Message-ID: <20251125000848.4103786-3-rui.wang@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251125000848.4103786-1-rui.wang@ideasonboard.com> References: <20251125000848.4103786-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" Load tuning flags and gain levels on top of the single-config helper. The parseConfig() function wraps parseSingleConfig() to load the base configuration, then parses optional enable/devmode flags and any exposure index -banded overrides. Replace the inline parsing in init() with a call to parseConfig(). This simplifies init() and allows the parsing logic to be reused. The refactor also adds logging of the parsed base tuning and preserves the base config for manual mode restoration. Rework domain filter, NLL, gain, and strength parsing into a parseSingleConfig() helper. This extracts the parsing logic so it can be reused for both the base tuning and per-exposure-gain-level configuration blocks. The init method now: - Calls parseConfig() to load tuning data - Logs the parsed base configuration - Caches base config for manual mode restore - exposure levels are sorted by exposure index threshold for efficient band selection at runtime. Signed-off-by: Rui Wang --- src/ipa/rkisp1/algorithms/denoise.h | 11 +- src/ipa/rkisp1/algorithms/dpf.cpp | 186 ++++++++++++++++++++++++---- src/ipa/rkisp1/algorithms/dpf.h | 24 +++- 3 files changed, 195 insertions(+), 26 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/denoise.h b/src/ipa/rkisp1/algorithms/denoise.h index 8907dc4e..abd08cba 100644 --- a/src/ipa/rkisp1/algorithms/denoise.h +++ b/src/ipa/rkisp1/algorithms/denoise.h @@ -20,12 +20,21 @@ class DenoiseBaseAlgorithm : public ipa::rkisp1::Algorithm protected: DenoiseBaseAlgorithm() = default; ~DenoiseBaseAlgorithm() = default; - + virtual void setDevMode(bool dev) { devMode_ = dev; } + virtual bool isDevMode() const { return devMode_; } virtual uint32_t computeExposureIndex(const IPAContext &context, const IPAFrameContext &frameContext) const; template uint32_t selectExposureIndexBand(unsigned exposureIndex, const LevelContainer &levels) const; + virtual bool parseConfig([[maybe_unused]] const YamlObject &tuningData) + { + return true; + } + +private: + /**< Developer mode state for advanced controls */ + bool devMode_ = false; }; inline unsigned DenoiseBaseAlgorithm::computeExposureIndex(const IPAContext &context, diff --git a/src/ipa/rkisp1/algorithms/dpf.cpp b/src/ipa/rkisp1/algorithms/dpf.cpp index 39f3e461..dc0a361c 100644 --- a/src/ipa/rkisp1/algorithms/dpf.cpp +++ b/src/ipa/rkisp1/algorithms/dpf.cpp @@ -8,6 +8,7 @@ #include "dpf.h" #include +#include #include #include @@ -37,7 +38,7 @@ namespace ipa::rkisp1::algorithms { LOG_DEFINE_CATEGORY(RkISP1Dpf) Dpf::Dpf() - : config_({}), strengthConfig_({}) + : config_({}), strengthConfig_({}), baseConfig_({}), baseStrengthConfig_({}) { } @@ -47,14 +48,130 @@ Dpf::Dpf() int Dpf::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData) { - std::vector values; + /* Parse tuning block */ + if (!parseConfig(tuningData)) + return -EINVAL; + + /* Log parsed base tuning (counts are always full-sized for base). */ + LOG(RkISP1Dpf, Info) + << "DPF init: base tuning parsed, G coeffs=" + << RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS + << ", RB fltsize=" + << (config_.rb_flt.fltsize == RKISP1_CIF_ISP_DPF_RB_FILTERSIZE_13x9 ? "13x9" : "9x9") + << ", NLL coeffs=" << RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS + << ", NLL scale=" + << (config_.nll.scale_mode == RKISP1_CIF_ISP_NLL_SCALE_LOGARITHMIC ? "log" : "linear") + << ", Strength (r,g,b)=" + << (int)strengthConfig_.r << "," << (int)strengthConfig_.g + << "," << (int)strengthConfig_.b; + + /* Preserve base (non-exposure index) YAML configuration for restoration after manual mode. */ + baseConfig_ = config_; + baseStrengthConfig_ = strengthConfig_; + + /* Optional exposure index-banded tuning */ + if (useExposureIndexLevels_) { + LOG(RkISP1Dpf, Info) + << "DPF init: loaded " << exposureIndexLevels_.size() + << " exposureIndex level(s) from tuning"; + } + + /* Optional mode tuning */ + if (!modes_.empty()) { + LOG(RkISP1Dpf, Info) + << "DPF init: loaded " << modes_.size() + << " mode(s) from tuning"; + } + + return 0; +} + +bool Dpf::parseConfig(const YamlObject &tuningData) +{ + /* Parse base config */ + if (!parseSingleConfig(tuningData, config_, strengthConfig_)) + return false; + + baseConfig_ = config_; + baseStrengthConfig_ = strengthConfig_; + + /* Optional developer mode flag (default true). If false, only basic manual controls available. */ + bool devMode = tuningData["devmode"].get().value_or(true); + setDevMode(devMode); + + /* Parse exposure index levels */ + if (tuningData.contains("ExposureIndexLevels")) { + useExposureIndexLevels_ = true; + exposureIndexLevels_.clear(); + for (const auto &entry : tuningData["ExposureIndexLevels"].asList()) { + std::optional maxExposureIndexOpt = + entry["maxExposureIndex"].get(); + if (!maxExposureIndexOpt) { + LOG(RkISP1Dpf, Error) << "ExposureIndexLevels entry missing maxExposureIndex"; + continue; + } + ExposureIndexLevelConfig lvl{}; + lvl.maxExposureIndex = *maxExposureIndexOpt; + if (!parseSingleConfig(entry, lvl.dpf, lvl.strength)) + continue; + exposureIndexLevels_.push_back(lvl); + } + std::sort(exposureIndexLevels_.begin(), exposureIndexLevels_.end(), + [](const ExposureIndexLevelConfig &a, const ExposureIndexLevelConfig &b) { + return a.maxExposureIndex < b.maxExposureIndex; + }); + } + + /* Parse modes */ + if (tuningData.contains("NoiseReductionMode")) { + modes_.clear(); + for (const auto &entry : tuningData["NoiseReductionMode"].asList()) { + std::optional typeOpt = + entry["type"].get(); + if (!typeOpt) { + LOG(RkISP1Dpf, Error) << "Modes entry missing type"; + continue; + } + + int32_t modeValue; + if (*typeOpt == "minimal") { + modeValue = controls::draft::NoiseReductionModeMinimal; + } else if (*typeOpt == "highquality") { + modeValue = controls::draft::NoiseReductionModeHighQuality; + } else if (*typeOpt == "fast") { + modeValue = controls::draft::NoiseReductionModeFast; + } else if (*typeOpt == "zsl") { + modeValue = controls::draft::NoiseReductionModeZSL; + } else { + LOG(RkISP1Dpf, Error) << "Unknown mode type: " << *typeOpt; + continue; + } + ModeConfig mode{}; + mode.modeValue = modeValue; + if (!parseSingleConfig(entry, mode.dpf, mode.strength)) + continue; + modes_.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; /* * The domain kernel is configured with a 9x9 kernel for the green * pixels, and a 13x9 or 9x9 kernel for red and blue pixels. */ + if (!tuningData.contains("DomainFilter")) { + LOG(RkISP1Dpf, Error) << "DomainFilter section missing"; + return false; + } const YamlObject &dFObject = tuningData["DomainFilter"]; - /* * For the green component, we have the 9x9 kernel specified * as 6 coefficients: @@ -78,14 +195,14 @@ int Dpf::init([[maybe_unused]] IPAContext &context, << "Invalid 'DomainFilter:g': expected " << RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS << " elements, got " << values.size(); - return -EINVAL; + return false; } std::copy_n(values.begin(), values.size(), - std::begin(config_.g_flt.spatial_coeff)); + std::begin(config.g_flt.spatial_coeff)); - config_.g_flt.gr_enable = true; - config_.g_flt.gb_enable = true; + config.g_flt.gr_enable = true; + config.g_flt.gb_enable = true; /* * For the red and blue components, we have the 13x9 kernel specified @@ -116,24 +233,28 @@ int Dpf::init([[maybe_unused]] IPAContext &context, << 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; + 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; std::copy_n(values.begin(), values.size(), - std::begin(config_.rb_flt.spatial_coeff)); + std::begin(config.rb_flt.spatial_coeff)); - config_.rb_flt.r_enable = true; - config_.rb_flt.b_enable = true; + config.rb_flt.r_enable = true; + config.rb_flt.b_enable = true; /* * The range kernel is configured with a noise level lookup table (NLL) * which stores a piecewise linear function that characterizes the * sensor noise profile as a noise level function curve (NLF). */ + if (!tuningData.contains("NoiseLevelFunction")) { + LOG(RkISP1Dpf, Error) << "NoiseLevelFunction section missing"; + return false; + } const YamlObject &rFObject = tuningData["NoiseLevelFunction"]; std::vector nllValues; @@ -143,32 +264,49 @@ int Dpf::init([[maybe_unused]] IPAContext &context, << "Invalid 'RangeFilter: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::begin(config.nll.coeff)); std::string scaleMode = rFObject["scale-mode"].get(""); if (scaleMode == "linear") { - config_.nll.scale_mode = RKISP1_CIF_ISP_NLL_SCALE_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; + config.nll.scale_mode = RKISP1_CIF_ISP_NLL_SCALE_LOGARITHMIC; } else { LOG(RkISP1Dpf, Error) << "Invalid 'RangeFilter:scale-mode': expected " << "'linear' or 'logarithmic' value, got " << scaleMode; - return -EINVAL; + return false; } + if (!tuningData.contains("Gain")) { + LOG(RkISP1Dpf, Error) << "Gain section missing"; + return false; + } + const YamlObject &gObject = tuningData["Gain"]; + + config.gain.mode = + gObject["gain_mode"].get().value_or( + RKISP1_CIF_ISP_DPF_GAIN_USAGE_AWB_LSC_GAINS); + config.gain.nf_r_gain = gObject["nf_r_gain"].get().value_or(256); + config.gain.nf_b_gain = gObject["nf_b_gain"].get().value_or(256); + config.gain.nf_gr_gain = gObject["nf_gr_gain"].get().value_or(256); + config.gain.nf_gb_gain = gObject["nf_gb_gain"].get().value_or(256); + + if (!tuningData.contains("FilterStrength")) { + LOG(RkISP1Dpf, Error) << "FilterStrength section missing"; + return false; + } const YamlObject &fSObject = tuningData["FilterStrength"]; - strengthConfig_.r = fSObject["r"].get(64); - strengthConfig_.g = fSObject["g"].get(64); - strengthConfig_.b = fSObject["b"].get(64); - - 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; } /** diff --git a/src/ipa/rkisp1/algorithms/dpf.h b/src/ipa/rkisp1/algorithms/dpf.h index 2dd8cd36..8691932d 100644 --- a/src/ipa/rkisp1/algorithms/dpf.h +++ b/src/ipa/rkisp1/algorithms/dpf.h @@ -10,12 +10,13 @@ #include #include "algorithm.h" +#include "denoise.h" namespace libcamera { namespace ipa::rkisp1::algorithms { -class Dpf : public Algorithm +class Dpf : public DenoiseBaseAlgorithm { public: Dpf(); @@ -32,6 +33,27 @@ public: private: struct rkisp1_cif_isp_dpf_config config_; struct rkisp1_cif_isp_dpf_strength_config strengthConfig_; + struct rkisp1_cif_isp_dpf_config baseConfig_; + struct rkisp1_cif_isp_dpf_strength_config baseStrengthConfig_; + struct ExposureIndexLevelConfig { + uint32_t maxExposureIndex; /* inclusive upper bound */ + struct rkisp1_cif_isp_dpf_config dpf; + struct rkisp1_cif_isp_dpf_strength_config strength; + }; + struct ModeConfig { + int32_t modeValue; + struct rkisp1_cif_isp_dpf_config dpf; + struct rkisp1_cif_isp_dpf_strength_config strength; + }; + + std::vector exposureIndexLevels_; + std::vector modes_; + bool useExposureIndexLevels_ = false; + + bool parseConfig(const YamlObject &tuningData) override; + bool parseSingleConfig(const YamlObject &tuningData, + rkisp1_cif_isp_dpf_config &config, + rkisp1_cif_isp_dpf_strength_config &strengthConfig); }; } /* namespace ipa::rkisp1::algorithms */