[libcamera-devel] [PATCH 3/3] ipa: rkisp1: Add support of De-noise Pre-Filter control
Florian Sylvestre
fsylvestre at baylibre.com
Thu Aug 4 16:12:28 CEST 2022
The De-noise pre-filter algorithm is a bilateral filter which combines a range
filter and a domain filter. The De-noise pre-filter is applied before
demosaicing.
Signed-off-by: Florian Sylvestre <fsylvestre at baylibre.com>
---
src/ipa/rkisp1/algorithms/dpf.cpp | 279 ++++++++++++++++++++++++++
src/ipa/rkisp1/algorithms/dpf.h | 36 ++++
src/ipa/rkisp1/algorithms/meson.build | 1 +
src/ipa/rkisp1/data/ov5640.yaml | 11 +
src/ipa/rkisp1/ipa_context.cpp | 11 +
src/ipa/rkisp1/ipa_context.h | 5 +
6 files changed, 343 insertions(+)
create mode 100644 src/ipa/rkisp1/algorithms/dpf.cpp
create mode 100644 src/ipa/rkisp1/algorithms/dpf.h
diff --git a/src/ipa/rkisp1/algorithms/dpf.cpp b/src/ipa/rkisp1/algorithms/dpf.cpp
new file mode 100644
index 00000000..d99bb728
--- /dev/null
+++ b/src/ipa/rkisp1/algorithms/dpf.cpp
@@ -0,0 +1,279 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021-2022, Ideas On Board
+ *
+ * dpf.cpp - RkISP1 De-noise Pre-Filter control
+ */
+
+#include "dpf.h"
+
+#include <cmath>
+
+#include <libcamera/base/log.h>
+
+#include <libcamera/control_ids.h>
+#include "linux/rkisp1-config.h"
+
+/**
+ * \file dpf.h
+ */
+
+namespace libcamera {
+
+namespace ipa::rkisp1::algorithms {
+
+/**
+ * \class Dpf
+ * \brief RkISP1 De-noise Pre-Filter control
+ *
+ * The De-noise pre-filter algorithm is a bilateral filter which combines a
+ * range filter and a domain filter. The De-noise pre-filter is applied before
+ * demosaicing.
+ */
+
+LOG_DEFINE_CATEGORY(RkISP1Dpf)
+
+Dpf::Dpf()
+ : initialized_(false), config_({}), strength_config_({})
+{
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
+int Dpf::init([[maybe_unused]] IPAContext &context,
+ const YamlObject &tuningData)
+{
+ std::vector<double> 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.
+ */
+ const YamlObject &dFObject = tuningData["DomainFilter"];
+ if (!dFObject.isDictionary()) {
+ LOG(RkISP1Dpf, Error)
+ << "Issue while parsing 'DomainFilter' in tuning "
+ << "file: entry must be a dictionary";
+ return -EINVAL;
+ }
+
+ /*
+ * For the green component, we have the 9x9 kernel specified
+ * as 6 coefficients:
+ * Y
+ * ^
+ * 4 | 6 5 4 5 6
+ * 3 | 5 3 3 5
+ * 2 | 5 3 2 3 5
+ * 1 | 3 1 1 3
+ * 0 - 4 2 0 2 4
+ * -1 | 3 1 1 3
+ * -2 | 5 3 2 3 5
+ * -3 | 5 3 3 5
+ * -4 | 6 5 4 5 6
+ * +---------|--------> X
+ * -4....-1 0 1 2 3 4
+ */
+ values = dFObject["g"].getList<double>().value_or(utils::defopt);
+ if (values.size() != RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS) {
+ LOG(RkISP1Dpf, Error)
+ << "Invalid 'DomainFilter:g': expected "
+ << RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS
+ << " elements, got " << values.size();
+ return -EINVAL;
+ }
+
+ for (unsigned int i = 0; i < values.size(); ++i) {
+ config_.g_flt.spatial_coeff[i] = std::round(values[i] * 16);
+ }
+
+ config_.g_flt.gr_enable = true;
+ config_.g_flt.gb_enable = true;
+
+ /*
+ * For the red and blue components, we have the 13x9 kernel specified
+ * as 6 coefficients:
+ *
+ * Y
+ * ^
+ * 4 | 6 5 4 3 4 5 6
+ * |
+ * 2 | 5 4 2 1 2 4 5
+ * |
+ * 0 - 5 3 1 0 1 3 5
+ * |
+ * -2 | 5 4 2 1 2 4 5
+ * |
+ * -4 | 6 5 4 3 4 5 6
+ * +-------------|------------> X
+ * -6 -4 -2 0 2 4 6
+ *
+ * For a 9x9 kernel, columns -6 and -6 are dropped, so coefficient
+ * number 6 is not used.
+ */
+ values = dFObject["rb"].getList<double>().value_or(utils::defopt);
+ 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 "
+ << RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS - 1
+ << " or " << RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS
+ << " elements, got " << values.size();
+ return -EINVAL;
+ }
+
+ 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;
+
+ for (unsigned int i = 0; i < values.size(); ++i) {
+ config_.rb_flt.spatial_coeff[i] = std::round(values[i] * 16);
+ }
+
+ 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).
+ */
+ const YamlObject &rFObject = tuningData["RangeFilter"];
+ if (!rFObject.isDictionary()) {
+ LOG(RkISP1Dpf, Error)
+ << "Issue while parsing 'RangeFilter' in tuning "
+ << "file: entry must be a dictionary";
+ return -EINVAL;
+ }
+
+ values = rFObject["coeff"].getList<double>().value_or(utils::defopt);
+ if (values.size() != RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS) {
+ LOG(RkISP1Dpf, Error)
+ << "Invalid 'RangeFilter:coeff': expected "
+ << RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS
+ << " elements, got " << values.size();
+ return -EINVAL;
+ }
+
+ for (unsigned int i = 0; i < values.size(); ++i) {
+ config_.nll.coeff[i] = std::round(values[i] * 1024);
+ }
+
+ std::string scaleMode = rFObject["scale-mode"].get<std::string>("");
+ if (scaleMode != "linear" && scaleMode != "logarithmic") {
+ LOG(RkISP1Dpf, Error)
+ << "Invalid 'RangeFilter:scale-mode': expected "
+ << "'linear' or 'logarithmic' value, got "
+ << scaleMode;
+ return -EINVAL;
+ }
+
+ config_.nll.scale_mode = scaleMode == "linear"
+ ? RKISP1_CIF_ISP_NLL_SCALE_LINEAR
+ : RKISP1_CIF_ISP_NLL_SCALE_LOGARITHMIC;
+
+ const YamlObject &fSObject = tuningData["FilterStrength"];
+ if (!fSObject.isDictionary()) {
+ LOG(RkISP1Dpf, Error)
+ << "Issue while parsing 'FilterStrength' in tuning "
+ << "file: entry must be a dictionary";
+ return -EINVAL;
+ }
+
+ strength_config_.r = fSObject["r"].get<uint16_t>(64);
+ strength_config_.g = fSObject["g"].get<uint16_t>(64);
+ strength_config_.b = fSObject["b"].get<uint16_t>(64);
+
+ initialized_ = true;
+
+ return 0;
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::queueRequest
+ */
+void Dpf::queueRequest(IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ const ControlList &controls)
+{
+ auto &dpf = context.frameContext.dpf;
+
+ const auto &denoise = controls.get(controls::draft::NoiseReductionMode);
+ if (denoise) {
+ LOG(RkISP1Dpf, Debug) << "Set denoise to " << *denoise;
+
+ switch (*denoise) {
+ case controls::draft::NoiseReductionModeOff:
+ dpf.denoise = false;
+ dpf.updateParams = true;
+ break;
+ case controls::draft::NoiseReductionModeMinimal:
+ case controls::draft::NoiseReductionModeHighQuality:
+ case controls::draft::NoiseReductionModeFast:
+ dpf.denoise = true;
+ dpf.updateParams = true;
+ break;
+ default:
+ LOG(RkISP1Dpf, Error)
+ << "Unsupported denoise value "
+ << *denoise;
+ }
+ }
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::prepare
+ */
+void Dpf::prepare(IPAContext &context, rkisp1_params_cfg *params)
+{
+ auto &dpf = context.frameContext.dpf;
+ auto &awb = context.configuration.awb;
+ auto &lsc = context.configuration.lsc;
+ auto &dpf_config = params->others.dpf_config;
+ auto &dpf_strength_config = params->others.dpf_strength_config;
+
+ if ((context.frameContext.frameCount > 0) && (!dpf.updateParams))
+ return;
+
+ if (!initialized_)
+ return;
+
+ /*
+ * We must copy the static configuration each time we modify a
+ * parameter in the config because the strucure is zeroed between each
+ * call.
+ */
+ dpf_config = config_;
+ dpf_strength_config = strength_config_;
+
+ if (dpf.updateParams) {
+ if (!dpf.denoise || (!awb.enabled && !lsc.enabled)) {
+ dpf_config.gain.mode =
+ RKISP1_CIF_ISP_DPF_GAIN_USAGE_DISABLED;
+ } else if (awb.enabled && lsc.enabled) {
+ dpf_config.gain.mode =
+ RKISP1_CIF_ISP_DPF_GAIN_USAGE_AWB_LSC_GAINS;
+ } else if (awb.enabled) {
+ dpf_config.gain.mode =
+ RKISP1_CIF_ISP_DPF_GAIN_USAGE_AWB_GAINS;
+ } else { /* lsc.enabled only */
+ dpf_config.gain.mode =
+ RKISP1_CIF_ISP_DPF_GAIN_USAGE_LSC_GAINS;
+ }
+ }
+
+ dpf.updateParams = false;
+
+ uint32_t flags = RKISP1_CIF_ISP_MODULE_DPF |
+ RKISP1_CIF_ISP_MODULE_DPF_STRENGTH;
+ params->module_en_update |= flags;
+ params->module_ens |= flags;
+ params->module_cfg_update |= flags;
+}
+
+REGISTER_IPA_ALGORITHM(Dpf, "Dpf")
+
+} /* namespace ipa::rkisp1::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/rkisp1/algorithms/dpf.h b/src/ipa/rkisp1/algorithms/dpf.h
new file mode 100644
index 00000000..c8b8419a
--- /dev/null
+++ b/src/ipa/rkisp1/algorithms/dpf.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021-2022, Ideas On Board
+ *
+ * dpf.h - RkISP1 De-noise Pre-Filter control
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+namespace ipa::rkisp1::algorithms {
+
+class Dpf : public Algorithm
+{
+public:
+ Dpf();
+ ~Dpf() = default;
+
+ int init(IPAContext &context, const YamlObject &tuningData) override;
+ void queueRequest(IPAContext &context, const uint32_t frame,
+ const ControlList &controls) override;
+ void prepare(IPAContext &context, rkisp1_params_cfg *params) override;
+
+private:
+ bool initialized_;
+ struct rkisp1_cif_isp_dpf_config config_;
+ struct rkisp1_cif_isp_dpf_strength_config strength_config_;
+};
+
+} /* namespace ipa::rkisp1::algorithms */
+} /* namespace libcamera */
diff --git a/src/ipa/rkisp1/algorithms/meson.build b/src/ipa/rkisp1/algorithms/meson.build
index e48974b4..e1492dd5 100644
--- a/src/ipa/rkisp1/algorithms/meson.build
+++ b/src/ipa/rkisp1/algorithms/meson.build
@@ -5,6 +5,7 @@ rkisp1_ipa_algorithms = files([
'awb.cpp',
'blc.cpp',
'cproc.cpp',
+ 'dpf.cpp',
'dpcc.cpp',
'filter.cpp',
'gsl.cpp',
diff --git a/src/ipa/rkisp1/data/ov5640.yaml b/src/ipa/rkisp1/data/ov5640.yaml
index 93d7d1e7..69c6ad97 100644
--- a/src/ipa/rkisp1/data/ov5640.yaml
+++ b/src/ipa/rkisp1/data/ov5640.yaml
@@ -11,6 +11,17 @@ algorithms:
Gb: 256
B: 256
- ColorProcessing:
+ - Dpf:
+ DomainFilter:
+ g: [ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 ]
+ rb: [ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 ]
+ RangeFilter:
+ coeff: [ 0.999, 0.999, 0.999, 0.999, 0.999, 0.999, 0.999, 0.999, 0.999, 0.999, 0.999, 0.999, 0.999, 0.999, 0.999, 0.999, 0.999 ]
+ scale-mode: "linear"
+ FilterStrength:
+ r: 64
+ g: 64
+ b: 64
- GammaSensorLinearization:
x-intervals: [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ]
y:
diff --git a/src/ipa/rkisp1/ipa_context.cpp b/src/ipa/rkisp1/ipa_context.cpp
index c90bf992..3b92e95e 100644
--- a/src/ipa/rkisp1/ipa_context.cpp
+++ b/src/ipa/rkisp1/ipa_context.cpp
@@ -165,6 +165,17 @@ namespace libcamera::ipa::rkisp1 {
* \brief Indicates if ISP parameters need to be updated
*/
+/**
+ * \var IPAFrameContext::dpf
+ * \brief Context for the De-noise Pre-Filter algorithm
+ *
+ * \var IPAFrameContext::dpf.denoise
+ * \brief Indicates if de-noise is activated
+ *
+ * \var IPAFrameContext::dpf.updateParams
+ * \brief Indicates if ISP parameters need to be updated
+ */
+
/**
* \var IPAFrameContext::filter
* \brief Context for the Filter algorithm
diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h
index 0cd6aadb..3a743ac3 100644
--- a/src/ipa/rkisp1/ipa_context.h
+++ b/src/ipa/rkisp1/ipa_context.h
@@ -69,6 +69,11 @@ struct IPAFrameContext {
bool updateParams;
} cproc;
+ struct {
+ bool denoise;
+ bool updateParams;
+ } dpf;
+
struct {
uint8_t denoise;
uint8_t sharpness;
--
2.34.1
More information about the libcamera-devel
mailing list