[v1,12/16] ipa: rkisp1: algorithms: dpf: manage DPF mode transitions
diff mbox series

Message ID 20251028170847.2673396-12-rui.wang@ideasonboard.com
State New
Headers show
Series
  • [v1,01/16] ipa: rkisp1: algorithms: add Denoise base class shell
Related show

Commit Message

Rui Wang Oct. 28, 2025, 5:08 p.m. UTC
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(+)

Patch
diff mbox series

diff --git a/src/ipa/rkisp1/algorithms/denoise.h b/src/ipa/rkisp1/algorithms/denoise.h
index 1a874def..c4b5b0af 100644
--- a/src/ipa/rkisp1/algorithms/denoise.h
+++ b/src/ipa/rkisp1/algorithms/denoise.h
@@ -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 */
diff --git a/src/ipa/rkisp1/algorithms/dpf.cpp b/src/ipa/rkisp1/algorithms/dpf.cpp
index cefa5da5..f6bbe3e4 100644
--- a/src/ipa/rkisp1/algorithms/dpf.cpp
+++ b/src/ipa/rkisp1/algorithms/dpf.cpp
@@ -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);
diff --git a/src/ipa/rkisp1/algorithms/dpf.h b/src/ipa/rkisp1/algorithms/dpf.h
index b971619b..b6676203 100644
--- a/src/ipa/rkisp1/algorithms/dpf.h
+++ b/src/ipa/rkisp1/algorithms/dpf.h
@@ -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 */
diff --git a/src/libcamera/control_ids_rkisp1.yaml b/src/libcamera/control_ids_rkisp1.yaml
index bf7875e9..78b95447 100644
--- a/src/libcamera/control_ids_rkisp1.yaml
+++ b/src/libcamera/control_ids_rkisp1.yaml
@@ -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.
 
 ...