[{"id":37100,"web_url":"https://patchwork.libcamera.org/comment/37100/","msgid":"<m47vectyqyqdf4pnxvdtmayhyxpj7f5smwztjy6qfp4japefxk@qifh32jgh4ib>","date":"2025-11-28T15:26:48","subject":"Re: [PATCH v1 02/11] ipa: rkisp1: algorithms: dpf: init Dpf tuning\n\tconfig","submitter":{"id":143,"url":"https://patchwork.libcamera.org/api/people/143/","name":"Jacopo Mondi","email":"jacopo.mondi@ideasonboard.com"},"content":"Hi Rui\n\nOn Mon, Nov 24, 2025 at 07:08:39PM -0500, Rui Wang wrote:\n> Load tuning flags and gain levels on top of the single-config helper.\n\nwhat's \"the single-config helper\" ?\n\n> The parseConfig() function wraps parseSingleConfig() to load the base\n> configuration, then parses optional enable/devmode flags and any\n> exposure index -banded overrides.\n\n\"index-banded\" maybe ?\n\n>\n> Replace the inline parsing in init() with a call to parseConfig(). This\n> simplifies init() and allows the parsing logic to be reused. The refactor\n> also adds logging of the parsed base tuning and preserves the base config\n> for manual mode restoration.\n>\n> Rework domain filter, NLL, gain, and strength parsing into a\n> parseSingleConfig() helper. This extracts the parsing logic so it can be\n> reused for both the base tuning and per-exposure-gain-level configuration blocks.\n\nline length for commit message is 75 cols\n\n>\n> The init method now:\n> - Calls parseConfig() to load tuning data\n> - Logs the parsed base configuration\n> - Caches base config for manual mode restore\n> - exposure levels are sorted by exposure index threshold for efficient band selection\n> at runtime.\n\nditto, please reflow\n\n>\n> Signed-off-by: Rui Wang <rui.wang@ideasonboard.com>\n> ---\n>  src/ipa/rkisp1/algorithms/denoise.h |  11 +-\n>  src/ipa/rkisp1/algorithms/dpf.cpp   | 186 ++++++++++++++++++++++++----\n>  src/ipa/rkisp1/algorithms/dpf.h     |  24 +++-\n>  3 files changed, 195 insertions(+), 26 deletions(-)\n>\n> diff --git a/src/ipa/rkisp1/algorithms/denoise.h b/src/ipa/rkisp1/algorithms/denoise.h\n> index 8907dc4e..abd08cba 100644\n> --- a/src/ipa/rkisp1/algorithms/denoise.h\n> +++ b/src/ipa/rkisp1/algorithms/denoise.h\n> @@ -20,12 +20,21 @@ class DenoiseBaseAlgorithm : public ipa::rkisp1::Algorithm\n>  protected:\n>  \tDenoiseBaseAlgorithm() = default;\n>  \t~DenoiseBaseAlgorithm() = default;\n> -\n\nThere usually is an empty line between constructors/destructors and\nother methods (which might be grouped as well)\n\n> +\tvirtual void setDevMode(bool dev) { devMode_ = dev; }\n> +\tvirtual bool isDevMode() const { return devMode_; }\n\ngetters in libcamera usually have the name of the field\n\n\tvirtual bool devMode() const { return devMode_; }\n\n>  \tvirtual uint32_t computeExposureIndex(const IPAContext &context,\n>  \t\t\t\t\t      const IPAFrameContext &frameContext) const;\n>  \ttemplate<typename LevelContainer>\n>  \tuint32_t selectExposureIndexBand(unsigned exposureIndex,\n>  \t\t\t\t\t const LevelContainer &levels) const;\n> +\tvirtual bool parseConfig([[maybe_unused]] const YamlObject &tuningData)\n> +\t{\n> +\t\treturn true;\n> +\t}\n> +\n> +private:\n> +\t/**< Developer mode state for advanced controls */\n\nwe don't doxygen in headers, and do not doxygen private fields (I\ndon't think Doxygen collects documentation for those ?)\n\nA simple comment will do\n\n> +\tbool devMode_ = false;\n>  };\n>\n>  inline unsigned DenoiseBaseAlgorithm::computeExposureIndex(const IPAContext &context,\n> diff --git a/src/ipa/rkisp1/algorithms/dpf.cpp b/src/ipa/rkisp1/algorithms/dpf.cpp\n> index 39f3e461..dc0a361c 100644\n> --- a/src/ipa/rkisp1/algorithms/dpf.cpp\n> +++ b/src/ipa/rkisp1/algorithms/dpf.cpp\n> @@ -8,6 +8,7 @@\n>  #include \"dpf.h\"\n>\n>  #include <algorithm>\n> +#include <array>\n\nWhere are arrays used in this patch ?\n\n>  #include <string>\n>  #include <vector>\n>\n> @@ -37,7 +38,7 @@ namespace ipa::rkisp1::algorithms {\n>  LOG_DEFINE_CATEGORY(RkISP1Dpf)\n>\n>  Dpf::Dpf()\n> -\t: config_({}), strengthConfig_({})\n> +\t: config_({}), strengthConfig_({}), baseConfig_({}), baseStrengthConfig_({})\n>  {\n>  }\n>\n> @@ -47,14 +48,130 @@ Dpf::Dpf()\n>  int Dpf::init([[maybe_unused]] IPAContext &context,\n>  \t      const YamlObject &tuningData)\n>  {\n> -\tstd::vector<uint8_t> values;\n> +\t/* Parse tuning block */\n\nComments should end with '.'\n\n> +\tif (!parseConfig(tuningData))\n> +\t\treturn -EINVAL;\n> +\n> +\t/* Log parsed base tuning (counts are always full-sized for base). */\n> +\tLOG(RkISP1Dpf, Info)\n> +\t\t<< \"DPF init: base tuning parsed, G coeffs=\"\n> +\t\t<< RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS\n> +\t\t<< \", RB fltsize=\"\n> +\t\t<< (config_.rb_flt.fltsize == RKISP1_CIF_ISP_DPF_RB_FILTERSIZE_13x9 ? \"13x9\" : \"9x9\")\n> +\t\t<< \", NLL coeffs=\" << RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS\n> +\t\t<< \", NLL scale=\"\n> +\t\t<< (config_.nll.scale_mode == RKISP1_CIF_ISP_NLL_SCALE_LOGARITHMIC ? \"log\" : \"linear\")\n> +\t\t<< \", Strength (r,g,b)=\"\n> +\t\t<< (int)strengthConfig_.r << \",\" << (int)strengthConfig_.g\n> +\t\t<< \",\" << (int)strengthConfig_.b;\n> +\n> +\t/* Preserve base (non-exposure index) YAML configuration for restoration after manual mode. */\n\nwhen it's trivial to stay in 80 cols, please do so\n\n> +\tbaseConfig_ = config_;\n> +\tbaseStrengthConfig_ = strengthConfig_;\n> +\n> +\t/* Optional exposure index-banded tuning */\n> +\tif (useExposureIndexLevels_) {\n> +\t\tLOG(RkISP1Dpf, Info)\n> +\t\t\t<< \"DPF init: loaded \" << exposureIndexLevels_.size()\n> +\t\t\t<< \" exposureIndex level(s) from tuning\";\n> +\t}\n> +\n> +\t/* Optional mode tuning */\n> +\tif (!modes_.empty()) {\n> +\t\tLOG(RkISP1Dpf, Info)\n> +\t\t\t<< \"DPF init: loaded \" << modes_.size()\n> +\t\t\t<< \" mode(s) from tuning\";\n> +\t}\n\nno {} for single statements\n\n> +\n> +\treturn 0;\n> +}\n> +\n> +bool Dpf::parseConfig(const YamlObject &tuningData)\n> +{\n> +\t/* Parse base config */\n> +\tif (!parseSingleConfig(tuningData, config_, strengthConfig_))\n> +\t\treturn false;\n> +\n> +\tbaseConfig_ = config_;\n> +\tbaseStrengthConfig_ = strengthConfig_;\n> +\n> +\t/* Optional developer mode flag (default true). If false, only basic manual controls available. */\n\nWhat is \"developer mode\" ?\n\nDo we have anything like this in other algorithms ?\nIt's true by default, so applications can eventually disable it ?\n\n> +\tbool devMode = tuningData[\"devmode\"].get<bool>().value_or(true);\n> +\tsetDevMode(devMode);\n> +\n> +\t/* Parse exposure index levels */\n> +\tif (tuningData.contains(\"ExposureIndexLevels\")) {\n> +\t\tuseExposureIndexLevels_ = true;\n> +\t\texposureIndexLevels_.clear();\n> +\t\tfor (const auto &entry : tuningData[\"ExposureIndexLevels\"].asList()) {\n> +\t\t\tstd::optional<uint32_t> maxExposureIndexOpt =\n> +\t\t\t\tentry[\"maxExposureIndex\"].get<uint32_t>();\n> +\t\t\tif (!maxExposureIndexOpt) {\n> +\t\t\t\tLOG(RkISP1Dpf, Error) << \"ExposureIndexLevels entry missing maxExposureIndex\";\n> +\t\t\t\tcontinue;\n\nShouldn't this be mandatory if \"ExposureIndexLevels\" is specified ?\n\n> +\t\t\t}\n> +\t\t\tExposureIndexLevelConfig lvl{};\n> +\t\t\tlvl.maxExposureIndex = *maxExposureIndexOpt;\n> +\t\t\tif (!parseSingleConfig(entry, lvl.dpf, lvl.strength))\n> +\t\t\t\tcontinue;\n> +\t\t\texposureIndexLevels_.push_back(lvl);\n> +\t\t}\n\nSo each \"ExposureIndexLevels\" might contain the same exact\nconfiguration of the main outer block ?\n\nI've not yet understood how these are used, but do you think all\nparamters should be repeated in each ExposureIndexLevel ?\n\n> +\t\tstd::sort(exposureIndexLevels_.begin(), exposureIndexLevels_.end(),\n> +\t\t\t  [](const ExposureIndexLevelConfig &a, const ExposureIndexLevelConfig &b) {\n> +\t\t\t\t  return a.maxExposureIndex < b.maxExposureIndex;\n> +\t\t\t  });\n> +\t}\n> +\n> +\t/* Parse modes */\n> +\tif (tuningData.contains(\"NoiseReductionMode\")) {\n> +\t\tmodes_.clear();\n> +\t\tfor (const auto &entry : tuningData[\"NoiseReductionMode\"].asList()) {\n> +\t\t\tstd::optional<std::string> typeOpt =\n> +\t\t\t\tentry[\"type\"].get<std::string>();\n> +\t\t\tif (!typeOpt) {\n> +\t\t\t\tLOG(RkISP1Dpf, Error) << \"Modes entry missing type\";\n> +\t\t\t\tcontinue;\n\nI think this should be an error as well\n\n> +\t\t\t}\n> +\n> +\t\t\tint32_t modeValue;\n> +\t\t\tif (*typeOpt == \"minimal\") {\n> +\t\t\t\tmodeValue = controls::draft::NoiseReductionModeMinimal;\n> +\t\t\t} else if (*typeOpt == \"highquality\") {\n> +\t\t\t\tmodeValue = controls::draft::NoiseReductionModeHighQuality;\n> +\t\t\t} else if (*typeOpt == \"fast\") {\n> +\t\t\t\tmodeValue = controls::draft::NoiseReductionModeFast;\n> +\t\t\t} else if (*typeOpt == \"zsl\") {\n> +\t\t\t\tmodeValue = controls::draft::NoiseReductionModeZSL;\n> +\t\t\t} else {\n> +\t\t\t\tLOG(RkISP1Dpf, Error) << \"Unknown mode type: \" << *typeOpt;\n> +\t\t\t\tcontinue;\n\nSame here probably\n\n> +\t\t\t}\n>\n> +\t\t\tModeConfig mode{};\n> +\t\t\tmode.modeValue = modeValue;\n\nCan't you assign to mode.modeValue directly ?\n\n> +\t\t\tif (!parseSingleConfig(entry, mode.dpf, mode.strength))\n> +\t\t\t\tcontinue;\n\nSame question, for each mode should all the paramters be repeated ?\nDoes each mode require both the denoise pre-filter and the filter to\nbe fully configured or only some paramters will change per-mode ?\n\n> +\t\t\tmodes_.push_back(mode);\n> +\t\t}\n> +\t}\n> +\n> +\treturn true;\n> +}\n> +\n> +bool Dpf::parseSingleConfig(const YamlObject &tuningData,\n> +\t\t\t    rkisp1_cif_isp_dpf_config &config,\n> +\t\t\t    rkisp1_cif_isp_dpf_strength_config &strengthConfig)\n> +{\n> +\tstd::vector<uint8_t> values;\n\nEither an empty line here, or you can simply move the declaration\nbelow when it is used\n\n>  \t/*\n>  \t * The domain kernel is configured with a 9x9 kernel for the green\n>  \t * pixels, and a 13x9 or 9x9 kernel for red and blue pixels.\n>  \t */\n> +\tif (!tuningData.contains(\"DomainFilter\")) {\n> +\t\tLOG(RkISP1Dpf, Error) << \"DomainFilter section missing\";\n> +\t\treturn false;\n> +\t}\n>  \tconst YamlObject &dFObject = tuningData[\"DomainFilter\"];\n\nI think you can avoid a double lookup by simply\n\n\tconst YamlObject &dFObject = tuningData[\"DomainFilter\"];\n\tif (!dFObject) {\n\t\tLOG(RkISP1Dpf, Error) << \"DomainFilter section missing\";\n\t\treturn false;\n\t}\n\nhere and in all other places where this pattern is repeated in this\npatch series\n\n> -\n\nYou really don't like empty lines :)\n\n>  \t/*\n>  \t * For the green component, we have the 9x9 kernel specified\n>  \t * as 6 coefficients:\n> @@ -78,14 +195,14 @@ int Dpf::init([[maybe_unused]] IPAContext &context,\n>  \t\t\t<< \"Invalid 'DomainFilter:g': expected \"\n>  \t\t\t<< RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS\n>  \t\t\t<< \" elements, got \" << values.size();\n> -\t\treturn -EINVAL;\n> +\t\treturn false;\n>  \t}\n>\n>  \tstd::copy_n(values.begin(), values.size(),\n> -\t\t    std::begin(config_.g_flt.spatial_coeff));\n> +\t\t    std::begin(config.g_flt.spatial_coeff));\n>\n> -\tconfig_.g_flt.gr_enable = true;\n> -\tconfig_.g_flt.gb_enable = true;\n> +\tconfig.g_flt.gr_enable = true;\n> +\tconfig.g_flt.gb_enable = true;\n>\n>  \t/*\n>  \t * For the red and blue components, we have the 13x9 kernel specified\n> @@ -116,24 +233,28 @@ int Dpf::init([[maybe_unused]] IPAContext &context,\n>  \t\t\t<< RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS - 1\n>  \t\t\t<< \" or \" << RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS\n>  \t\t\t<< \" elements, got \" << values.size();\n> -\t\treturn -EINVAL;\n> +\t\treturn false;\n>  \t}\n>\n> -\tconfig_.rb_flt.fltsize = values.size() == RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS\n> -\t\t\t       ? RKISP1_CIF_ISP_DPF_RB_FILTERSIZE_13x9\n> -\t\t\t       : RKISP1_CIF_ISP_DPF_RB_FILTERSIZE_9x9;\n> +\tconfig.rb_flt.fltsize = values.size() == RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS\n> +\t\t\t\t\t? RKISP1_CIF_ISP_DPF_RB_FILTERSIZE_13x9\n> +\t\t\t\t\t: RKISP1_CIF_ISP_DPF_RB_FILTERSIZE_9x9;\n>\n>  \tstd::copy_n(values.begin(), values.size(),\n> -\t\t    std::begin(config_.rb_flt.spatial_coeff));\n> +\t\t    std::begin(config.rb_flt.spatial_coeff));\n>\n> -\tconfig_.rb_flt.r_enable = true;\n> -\tconfig_.rb_flt.b_enable = true;\n> +\tconfig.rb_flt.r_enable = true;\n> +\tconfig.rb_flt.b_enable = true;\n>\n>  \t/*\n>  \t * The range kernel is configured with a noise level lookup table (NLL)\n>  \t * which stores a piecewise linear function that characterizes the\n>  \t * sensor noise profile as a noise level function curve (NLF).\n>  \t */\n> +\tif (!tuningData.contains(\"NoiseLevelFunction\")) {\n> +\t\tLOG(RkISP1Dpf, Error) << \"NoiseLevelFunction section missing\";\n> +\t\treturn false;\n> +\t}\n>  \tconst YamlObject &rFObject = tuningData[\"NoiseLevelFunction\"];\n\nOr\n        if (!rFObject)\n\n>\n>  \tstd::vector<uint16_t> nllValues;\n> @@ -143,32 +264,49 @@ int Dpf::init([[maybe_unused]] IPAContext &context,\n>  \t\t\t<< \"Invalid 'RangeFilter:coeff': expected \"\n>  \t\t\t<< RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS\n>  \t\t\t<< \" elements, got \" << nllValues.size();\n> -\t\treturn -EINVAL;\n> +\t\treturn false;\n>  \t}\n>\n>  \tstd::copy_n(nllValues.begin(), nllValues.size(),\n> -\t\t    std::begin(config_.nll.coeff));\n> +\t\t    std::begin(config.nll.coeff));\n>\n>  \tstd::string scaleMode = rFObject[\"scale-mode\"].get<std::string>(\"\");\n>  \tif (scaleMode == \"linear\") {\n> -\t\tconfig_.nll.scale_mode = RKISP1_CIF_ISP_NLL_SCALE_LINEAR;\n> +\t\tconfig.nll.scale_mode = RKISP1_CIF_ISP_NLL_SCALE_LINEAR;\n>  \t} else if (scaleMode == \"logarithmic\") {\n> -\t\tconfig_.nll.scale_mode = RKISP1_CIF_ISP_NLL_SCALE_LOGARITHMIC;\n> +\t\tconfig.nll.scale_mode = RKISP1_CIF_ISP_NLL_SCALE_LOGARITHMIC;\n>  \t} else {\n>  \t\tLOG(RkISP1Dpf, Error)\n>  \t\t\t<< \"Invalid 'RangeFilter:scale-mode': expected \"\n>  \t\t\t<< \"'linear' or 'logarithmic' value, got \"\n>  \t\t\t<< scaleMode;\n> -\t\treturn -EINVAL;\n> +\t\treturn false;\n>  \t}\n>\n> +\tif (!tuningData.contains(\"Gain\")) {\n> +\t\tLOG(RkISP1Dpf, Error) << \"Gain section missing\";\n> +\t\treturn false;\n> +\t}\n> +\tconst YamlObject &gObject = tuningData[\"Gain\"];\n> +\n> +\tconfig.gain.mode =\n> +\t\tgObject[\"gain_mode\"].get<uint32_t>().value_or(\n> +\t\t\tRKISP1_CIF_ISP_DPF_GAIN_USAGE_AWB_LSC_GAINS);\n\nShould we validate that gain_mode is a valid value ? Do we want to\nhave an integer or should we rather have strings like:\n\"awb\", \"lsc\", \"awblsc\", \"nf\", \"nflsc\" ?\n\nI'm looking at rkisp1_dpf_config in\ndrivers/media/platform/rockchip/rkisp1/rkisp1-params.c\n\nDo you know why that function unconditionally enable\nRKISP1_CIF_ISP_DPF_MODE_AWB_GAIN_COMP\nwhenever using NF gains ?\n\n> +\tconfig.gain.nf_r_gain = gObject[\"nf_r_gain\"].get<uint16_t>().value_or(256);\n> +\tconfig.gain.nf_b_gain = gObject[\"nf_b_gain\"].get<uint16_t>().value_or(256);\n> +\tconfig.gain.nf_gr_gain = gObject[\"nf_gr_gain\"].get<uint16_t>().value_or(256);\n> +\tconfig.gain.nf_gb_gain = gObject[\"nf_gb_gain\"].get<uint16_t>().value_or(256);\n\nFor my education, how do the gains work ?\n\nWe have a single value for R, Gb, Gr, B. Are these gains applied to\nall pixels throughout the image ? It is not clear to me if they should\n\"replace\" the AWB gains or add to it (as the driver as noted above\nalways enable RKISP1_CIF_ISP_DPF_MODE_AWB_GAIN_COMP when using NF\ngains).\n\n> +\n> +\tif (!tuningData.contains(\"FilterStrength\")) {\n> +\t\tLOG(RkISP1Dpf, Error) << \"FilterStrength section missing\";\n> +\t\treturn false;\n> +\t}\n>  \tconst YamlObject &fSObject = tuningData[\"FilterStrength\"];\n>\n> -\tstrengthConfig_.r = fSObject[\"r\"].get<uint16_t>(64);\n> -\tstrengthConfig_.g = fSObject[\"g\"].get<uint16_t>(64);\n> -\tstrengthConfig_.b = fSObject[\"b\"].get<uint16_t>(64);\n> -\n> -\treturn 0;\n> +\tstrengthConfig.r = fSObject[\"r\"].get<uint8_t>().value_or(64);\n> +\tstrengthConfig.g = fSObject[\"g\"].get<uint8_t>().value_or(64);\n> +\tstrengthConfig.b = fSObject[\"b\"].get<uint8_t>().value_or(64);\n\nSo these are the filter strengths.\n\nIf my understanding is right, we have two main functions to configure:\nthe denoise pre-filter and the actual filter.\n\nThe tuning paramters now have the following structure (if I'm not\nmistaken)\n\n      DomainFilter:\n        g:\n        rb:\n      NoiseLevelFunction:\n        coeff: []\n        scale-mode:\n      Gain:\n        gain_mode:\n        nf_r_gain:\n        nf_b_gain:\n        nf_gr_gain:\n        nf_gb_gain:\n      FilterStrength:\n        r:\n        g:\n        b:\n\nCan we re-organize it in logical blocks ?\n\nI don't know if we have a policy of being backward-compatible with\ntuning files. I think it's too early to have it, but others might have\ndifferent opinions.\n\nIn case we can rework the paramters organization, wouldn't something\nlike:\n\n    Dpf:\n      Denoise:\n        scale-mode:\"\"\n        coeff: []\n        gains:\n          mode: \"\"\n          r:\n          gr:\n          gb:\n          b:\n\n      Filter:\n        weights:\n          g: []\n          rb: []\n        strength:\n          r:\n          b:\n          g:\n\nBe easier to maintain and understand ?\n\n> +\treturn true;\n>  }\n>\n>  /**\n> diff --git a/src/ipa/rkisp1/algorithms/dpf.h b/src/ipa/rkisp1/algorithms/dpf.h\n> index 2dd8cd36..8691932d 100644\n> --- a/src/ipa/rkisp1/algorithms/dpf.h\n> +++ b/src/ipa/rkisp1/algorithms/dpf.h\n> @@ -10,12 +10,13 @@\n>  #include <sys/types.h>\n>\n>  #include \"algorithm.h\"\n> +#include \"denoise.h\"\n>\n>  namespace libcamera {\n>\n>  namespace ipa::rkisp1::algorithms {\n>\n> -class Dpf : public Algorithm\n> +class Dpf : public DenoiseBaseAlgorithm\n>  {\n>  public:\n>  \tDpf();\n> @@ -32,6 +33,27 @@ public:\n>  private:\n>  \tstruct rkisp1_cif_isp_dpf_config config_;\n>  \tstruct rkisp1_cif_isp_dpf_strength_config strengthConfig_;\n> +\tstruct rkisp1_cif_isp_dpf_config baseConfig_;\n> +\tstruct rkisp1_cif_isp_dpf_strength_config baseStrengthConfig_;\n> +\tstruct ExposureIndexLevelConfig {\n> +\t\tuint32_t maxExposureIndex; /* inclusive upper bound */\n> +\t\tstruct rkisp1_cif_isp_dpf_config dpf;\n> +\t\tstruct rkisp1_cif_isp_dpf_strength_config strength;\n> +\t};\n> +\tstruct ModeConfig {\n> +\t\tint32_t modeValue;\n> +\t\tstruct rkisp1_cif_isp_dpf_config dpf;\n> +\t\tstruct rkisp1_cif_isp_dpf_strength_config strength;\n> +\t};\n> +\n> +\tstd::vector<ExposureIndexLevelConfig> exposureIndexLevels_;\n> +\tstd::vector<ModeConfig> modes_;\n> +\tbool useExposureIndexLevels_ = false;\n> +\n> +\tbool parseConfig(const YamlObject &tuningData) override;\n> +\tbool parseSingleConfig(const YamlObject &tuningData,\n> +\t\t\t       rkisp1_cif_isp_dpf_config &config,\n> +\t\t\t       rkisp1_cif_isp_dpf_strength_config &strengthConfig);\n\nPlease follow:\nhttps://google.github.io/styleguide/cppguide.html#Declaration_Order\n\nIn this case: types definition first, then functions, then data members\n\n>  };\n>\n>  } /* namespace ipa::rkisp1::algorithms */\n> --\n> 2.43.0\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id CEA86C3257\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 28 Nov 2025 15:26:54 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id B3E7360A85;\n\tFri, 28 Nov 2025 16:26:53 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1DFA06069A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 28 Nov 2025 16:26:52 +0100 (CET)","from ideasonboard.com (net-93-65-100-155.cust.vodafonedsl.it\n\t[93.65.100.155])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id AB11CC67;\n\tFri, 28 Nov 2025 16:24:40 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"OvnkYW+K\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1764343480;\n\tbh=fC7LGneGd6x43/ds2Nrss5U1HAQOp0xL0HCNNK0dWW0=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=OvnkYW+KfQunLrg6QqMwp0jY5wscd8g/9CHsE5bM/jWz2TUFiStQyiImNcJO75HH4\n\tASwMswLhB5BurRHoR7MGS3YPHc19K26nU5ZA/sPX6pRj0EEz0Ux0HcqrQG1dhXzcfk\n\t/4Ul/V2vGGqqP21+56D/JwuXFUa31YOdew9aS+oA=","Date":"Fri, 28 Nov 2025 16:26:48 +0100","From":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","To":"Rui Wang <rui.wang@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH v1 02/11] ipa: rkisp1: algorithms: dpf: init Dpf tuning\n\tconfig","Message-ID":"<m47vectyqyqdf4pnxvdtmayhyxpj7f5smwztjy6qfp4japefxk@qifh32jgh4ib>","References":"<20251125000848.4103786-1-rui.wang@ideasonboard.com>\n\t<20251125000848.4103786-3-rui.wang@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20251125000848.4103786-3-rui.wang@ideasonboard.com>","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]