summaryrefslogtreecommitdiff
path: root/src/ipa/rkisp1/algorithms/dpf.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ipa/rkisp1/algorithms/dpf.cpp')
-rw-r--r--src/ipa/rkisp1/algorithms/dpf.cpp258
1 files changed, 258 insertions, 0 deletions
diff --git a/src/ipa/rkisp1/algorithms/dpf.cpp b/src/ipa/rkisp1/algorithms/dpf.cpp
new file mode 100644
index 00000000..c93c8361
--- /dev/null
+++ b/src/ipa/rkisp1/algorithms/dpf.cpp
@@ -0,0 +1,258 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021-2022, Ideas On Board
+ *
+ * dpf.cpp - RkISP1 Denoise 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 Denoise Pre-Filter control
+ *
+ * The denoise pre-filter algorithm is a bilateral filter which combines a
+ * range filter and a domain filter. The denoise pre-filter is applied before
+ * demosaicing.
+ */
+
+LOG_DEFINE_CATEGORY(RkISP1Dpf)
+
+Dpf::Dpf()
+ : initialized_(false), config_({}), strengthConfig_({})
+{
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
+int Dpf::init([[maybe_unused]] IPAContext &context,
+ const YamlObject &tuningData)
+{
+ std::vector<uint8_t> 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"];
+
+ /*
+ * 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<uint8_t>().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;
+ }
+
+ std::copy_n(values.begin(), values.size(),
+ std::begin(config_.g_flt.spatial_coeff));
+
+ 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<uint8_t>().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;
+
+ std::copy_n(values.begin(), values.size(),
+ std::begin(config_.rb_flt.spatial_coeff));
+
+ 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["NoiseLevelFunction"];
+
+ std::vector<uint16_t> nllValues;
+ nllValues = rFObject["coeff"].getList<uint16_t>().value_or(utils::defopt);
+ if (nllValues.size() != RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS) {
+ LOG(RkISP1Dpf, Error)
+ << "Invalid 'RangeFilter:coeff': expected "
+ << RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS
+ << " elements, got " << nllValues.size();
+ return -EINVAL;
+ }
+
+ std::copy_n(nllValues.begin(), nllValues.size(),
+ std::begin(config_.nll.coeff));
+
+ std::string scaleMode = rFObject["scale-mode"].get<std::string>("");
+ 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 "
+ << "'linear' or 'logarithmic' value, got "
+ << scaleMode;
+ return -EINVAL;
+ }
+
+ const YamlObject &fSObject = tuningData["FilterStrength"];
+
+ strengthConfig_.r = fSObject["r"].get<uint16_t>(64);
+ strengthConfig_.g = fSObject["g"].get<uint16_t>(64);
+ strengthConfig_.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)
+{
+ if (!initialized_)
+ return;
+
+ auto &dpf = context.frameContext.dpf;
+
+ if (context.frameContext.frameCount == 0) {
+ params->others.dpf_config = config_;
+ params->others.dpf_strength_config = strengthConfig_;
+
+ const auto &awb = context.configuration.awb;
+ const auto &lsc = context.configuration.lsc;
+ auto &mode = params->others.dpf_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;
+
+ params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_DPF |
+ RKISP1_CIF_ISP_MODULE_DPF_STRENGTH;
+ }
+
+ if (dpf.updateParams) {
+ params->module_en_update |= RKISP1_CIF_ISP_MODULE_DPF;
+ if (dpf.denoise)
+ params->module_ens |= RKISP1_CIF_ISP_MODULE_DPF;
+
+ dpf.updateParams = false;
+ }
+}
+
+REGISTER_IPA_ALGORITHM(Dpf, "Dpf")
+
+} /* namespace ipa::rkisp1::algorithms */
+
+} /* namespace libcamera */