@@ -20,10 +20,12 @@ class DenoiseBaseAlgorithm : public ipa::rkisp1::Algorithm
protected:
DenoiseBaseAlgorithm() = default;
~DenoiseBaseAlgorithm() = default;
+
struct EnableState {
bool enabled = true; /**< Current enable state */
bool lastEnabled = true; /**< Previous enable state for change detection */
};
+
bool processEnableToggle(bool value, EnableState &state);
void setManualMode(bool manual) { manualMode_ = manual; }
@@ -31,15 +33,27 @@ protected:
void setDevMode(bool dev) { devMode_ = dev; }
bool isManualMode() const { return manualMode_; }
+
bool isDevMode() const { return devMode_; }
+
unsigned computeIso(const IPAContext &context,
const IPAFrameContext &frameContext) const;
+
template<typename LevelContainer>
int selectIsoBand(unsigned iso, const LevelContainer &levels) const;
+
virtual bool parseConfig(const YamlObject &tuningData) = 0;
+
virtual void handleEnableControl(const ControlList &controls, IPAFrameContext &frameContext, IPAContext &context) = 0;
+
virtual void collectManualOverrides(const ControlList &controls) = 0;
+ virtual bool processModeChange(const ControlList &controls, uint32_t currentFrame) = 0;
+
+ virtual void snapshotCurrentToOverrides() = 0;
+
+ virtual void restoreAutoConfig(IPAContext &context, IPAFrameContext &frameContext) = 0;
+
private:
bool manualMode_ = false; /**< Current manual/auto mode state */
bool devMode_ = false; /**< Developer mode state for advanced controls */
@@ -338,6 +338,71 @@ bool Dpf::checkDevModeOverridesChanged()
return changed;
}
+void Dpf::snapshotCurrentToOverrides()
+{
+ overrides_.clear();
+ overrides_.strength = DpfStrengthSettings{ strengthConfig_.r, strengthConfig_.g, strengthConfig_.b };
+ if (isDevMode()) {
+ DpfSpatialGreenSettings green;
+ std::copy_n(std::begin(config_.g_flt.spatial_coeff), RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS, green.coeffs.begin());
+ overrides_.spatialGreen = green;
+ DpfSpatialRbSettings rb;
+ std::copy_n(std::begin(config_.rb_flt.spatial_coeff), RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS, rb.coeffs.begin());
+ rb.size = (config_.rb_flt.fltsize == RKISP1_CIF_ISP_DPF_RB_FILTERSIZE_13x9) ? 1 : 0;
+ overrides_.spatialRb = rb;
+ overrides_.rbSize = rb.size;
+ DpfNllSettings nll;
+ std::copy_n(std::begin(config_.nll.coeff), RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS, nll.coeffs.begin());
+ nll.scaleMode = (config_.nll.scale_mode == RKISP1_CIF_ISP_NLL_SCALE_LOGARITHMIC) ? 1 : 0;
+ overrides_.nll = nll;
+ }
+}
+
+void Dpf::restoreAutoConfig(IPAContext &context, IPAFrameContext &frameContext)
+{
+ overrides_.clear();
+ if (useIsoLevels_) {
+ unsigned iso = computeIso(context, frameContext);
+ int idx = DenoiseBaseAlgorithm::selectIsoBand(iso, isoLevels_);
+ if (idx >= 0) {
+ config_ = isoLevels_[idx].dpf;
+ strengthConfig_ = isoLevels_[idx].strength;
+ lastIsoIndex_ = idx;
+ }
+ } else {
+ config_ = baseConfig_;
+ strengthConfig_ = baseStrengthConfig_;
+ lastIsoIndex_ = -1;
+ }
+ frameContext.dpf.update = true;
+}
+
+bool Dpf::processModeChange(const ControlList &controls, uint32_t currentFrame)
+{
+ const auto &cMode = controls.get(controls::rkisp1::DpfMode);
+ if (!cMode)
+ return false;
+
+ bool requested = (*cMode == controls::rkisp1::DpfModeManual);
+ if (requested == isManualMode())
+ return false;
+
+ // Prevent rapid mode changes (hysteresis to avoid application bugs)
+ uint32_t framesSinceLastChange = currentFrame - lastModeChangeFrame_;
+ if (framesSinceLastChange < kMinModeChangeInterval && lastModeChangeFrame_ != 0) {
+ LOG(RkISP1Dpf, Debug) << "Ignoring rapid mode change (hysteresis): requested="
+ << (requested ? "manual" : "auto")
+ << ", current=" << (isManualMode() ? "manual" : "auto")
+ << ", framesSinceLast=" << framesSinceLastChange;
+ return false;
+ }
+
+ setManualMode(requested);
+ // Reset overrides if switching to auto mode , make sure the config will apply to next frame
+ lastModeChangeFrame_ = currentFrame;
+ return true;
+}
+
/**
* \copydoc libcamera::ipa::Algorithm::queueRequest
*/
@@ -348,6 +413,17 @@ void Dpf::queueRequest(IPAContext &context,
{
frameContext.dpf.update = false;
handleEnableControl(controls, frameContext, context);
+ bool modeChanged = processModeChange(controls, frame);
+ if (modeChanged) {
+ if (isManualMode()) {
+ snapshotCurrentToOverrides();
+ LOG(RkISP1Dpf, Info) << "DPF mode=Manual (snapshot captured)";
+ } else {
+ restoreAutoConfig(context, frameContext);
+ LOG(RkISP1Dpf, Info) << "DPF mode=Auto (restored auto config)";
+ }
+ frameContext.dpf.update = true;
+ }
if (isManualMode()) {
collectManualOverrides(controls);
@@ -78,6 +78,11 @@ private:
std::vector<IsoLevelConfig> isoLevels_;
bool useIsoLevels_ = false;
bool enableDpf_ = true; /* YAML master enable */
+ int lastIsoIndex_ = -1;
+
+ /* Mode change protection */
+ uint32_t lastModeChangeFrame_ = 0;
+ static constexpr uint32_t kMinModeChangeInterval = 2; /* frames */
void handleEnableControl(const ControlList &controls, IPAFrameContext &frameContext, IPAContext &context) override;
@@ -90,6 +95,12 @@ private:
bool parseSingleConfig(const YamlObject &config,
rkisp1_cif_isp_dpf_config &cfg,
rkisp1_cif_isp_dpf_strength_config &strength);
+
+ bool processModeChange(const ControlList &controls, uint32_t currentFrame) override;
+
+ void snapshotCurrentToOverrides() override;
+
+ void restoreAutoConfig(IPAContext &context, IPAFrameContext &frameContext) override;
};
} /* namespace ipa::rkisp1::algorithms */
@@ -82,5 +82,44 @@ controls:
- name: DpfNoiseLevelLookupScaleLogarithmic
value: 1
description: Logarithmic scale.
+ - DpfMode:
+ type: int32_t
+ direction: inout
+ description: |
+ Controls the operating mode of the Denoise Pre-Filter (DPF) algorithm.
+
+ In Auto mode the algorithm selects parameters automatically from the
+ sensor tuning data and (if provided) the ISO level table. Changes in
+ scene brightness (analogue gain / ISO) may cause the algorithm to
+ re-evaluate and reprogram the hardware when an ISO band boundary is
+ crossed.
+
+ In Manual mode the automatically selected parameters are frozen at
+ the moment the mode switch occurs (a snapshot is taken). Subsequent
+ per‑frame automatic ISO/tuning updates are disabled until the mode is
+ set back to Auto. While in Manual mode the application may modify any
+ of the override controls (e.g. DpfChannelStrengths, DpfGreenSpatialCoefficients,
+ DpfRedBlueSpatialCoefficients, DpfRbFilterSize, DpfNoiseLevelLookupCoefficients,
+ DpfNoiseLevelLookupScaleMode). Changes are applied immediately to hardware.
+
+ Transition Rules:
+ * Auto -> Manual: The current effective (auto-selected) parameters
+ are captured as the initial manual override values.
+ * Manual -> Auto: All manual override state is discarded and the
+ algorithm immediately reverts to automatic tuning selection,
+ potentially reprogramming hardware that same frame.
+
+ If the application switches to Manual but supplies no overrides,
+ the previously auto-derived parameters continue to be used unchanged.
+
+ \\sa DpfEnable
+ \\sa DpfChannelStrengths
+ enum:
+ - name: DpfModeAuto
+ value: 0
+ description: Automatic mode - algorithm selects parameters based on tuning data.
+ - name: DpfModeManual
+ value: 1
+ description: Manual mode - parameters are frozen and can be overridden.
...
Implement hysteresis-aware mode switching via processModeChange(), snapshot the current configuration when entering manual mode, and restore base tuning when returning to auto mode. The implementation includes: - processModeChange(): Detects DpfMode control changes with hysteresis to prevent rapid mode toggling (minimum 5-frame interval) - snapshotCurrentToOverrides(): Captures current config when entering manual mode so users can continue from current state - restoreAutoConfig(): Restores base or ISO-banded config when returning to auto mode, clearing all manual overrides Updates queueRequest() to handle mode transitions with appropriate logging and config restoration. Signed-off-by: Rui Wang <rui.wang@ideasonboard.com> --- src/ipa/rkisp1/algorithms/denoise.h | 14 +++++ src/ipa/rkisp1/algorithms/dpf.cpp | 76 +++++++++++++++++++++++++++ src/ipa/rkisp1/algorithms/dpf.h | 11 ++++ src/libcamera/control_ids_rkisp1.yaml | 39 ++++++++++++++ 4 files changed, 140 insertions(+)