[v2,09/10] ipa: libipa: agc: Calculate frame duration in helper
diff mbox series

Message ID 20251028-exposure-limits-v2-9-a8b5a318323e@ideasonboard.com
State New
Headers show
Series
  • libipa: agc: Calculate exposure limits
Related show

Commit Message

Jacopo Mondi Oct. 28, 2025, 9:31 a.m. UTC
The RkISP1 IPA is the only IPA that derives from AgcMeanLuminance
that populates the FrameDuration control.

The way frame duration is calculated is by using the shutter time
as returned by the Agc helpers and use that value to populate the
FrameDuration metadata and calculate the vblank.

This is however not correct as the exposure time shall be shorter than
the frame duration by a sensor-specific margin.

The AGC helpers are in charge of calculating the desired exposure
and split it between shutter time and gains. They already use the
exposure margin to limit the maximum shutter time to the maximum
frame duration. Let the helpers calculate the frame duration and
return it with shutter time and gains from calculateNewEv().

Also update the Mali C55 and IPU3 IPAs, however they do not currently
handle the frame duration and never change vblank so they don't report
the FrameDuration metadata (Mali C55) or return a fixed value (IPU3).

Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
---
 src/ipa/ipu3/algorithms/agc.cpp         | 12 +++++++-----
 src/ipa/libipa/agc_mean_luminance.cpp   |  6 +++---
 src/ipa/libipa/agc_mean_luminance.h     |  2 +-
 src/ipa/libipa/exposure_mode_helper.cpp | 33 +++++++++++++++++++++++----------
 src/ipa/libipa/exposure_mode_helper.h   |  5 ++++-
 src/ipa/mali-c55/algorithms/agc.cpp     |  3 ++-
 src/ipa/rkisp1/algorithms/agc.cpp       | 11 ++++++-----
 7 files changed, 46 insertions(+), 26 deletions(-)

Patch
diff mbox series

diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp
index 51aa4e51841ca8c6363c0211819a7d14cc3baec6..34cb31a3ef4ff8a4774351e1d1a8b5a46aca33cc 100644
--- a/src/ipa/ipu3/algorithms/agc.cpp
+++ b/src/ipa/ipu3/algorithms/agc.cpp
@@ -236,8 +236,9 @@  void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,
 	utils::Duration effectiveExposureValue = exposureTime * analogueGain;
 
 	utils::Duration newExposureTime;
+	utils::Duration frameDuration;
 	double aGain, qGain, dGain;
-	std::tie(newExposureTime, aGain, qGain, dGain) =
+	std::tie(newExposureTime, frameDuration, aGain, qGain, dGain) =
 		calculateNewEv(context.activeState.agc.constraintMode,
 			       context.activeState.agc.exposureMode, hist,
 			       effectiveExposureValue);
@@ -254,12 +255,13 @@  void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,
 	metadata.set(controls::AnalogueGain, frameContext.sensor.gain);
 	metadata.set(controls::ExposureTime, exposureTime.get<std::micro>());
 
-	/* \todo Use VBlank value calculated from each frame exposure. */
+	/* \todo Use frameDuration to calculate the right vblank. */
+
 	uint32_t vTotal = context.configuration.sensor.size.height
 			+ context.configuration.sensor.defVBlank;
-	utils::Duration frameDuration = context.configuration.sensor.lineDuration
-				      * vTotal;
-	metadata.set(controls::FrameDuration, frameDuration.get<std::micro>());
+	utils::Duration defFrameDuration = context.configuration.sensor.lineDuration
+					 * vTotal;
+	metadata.set(controls::FrameDuration, defFrameDuration.get<std::micro>());
 }
 
 REGISTER_IPA_ALGORITHM(Agc, "Agc")
diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp
index 36bdf76dca45c837d5ef4eb03244a43d1262c675..3722602921d6f1716f4cfedcae84023e4bea02a6 100644
--- a/src/ipa/libipa/agc_mean_luminance.cpp
+++ b/src/ipa/libipa/agc_mean_luminance.cpp
@@ -646,10 +646,10 @@  utils::Duration AgcMeanLuminance::filterExposure(utils::Duration exposureValue)
  * exposure value is filtered to prevent rapid changes from frame to frame, and
  * divided into exposure time, analogue, quantization and digital gain.
  *
- * \return Tuple of exposure time, analogue gain, quantization gain and digital
- * gain
+ * \return Tuple of exposure time, frame duration analogue gain, quantization
+ * gain and digital gain
  */
-std::tuple<utils::Duration, double, double, double>
+std::tuple<utils::Duration, utils::Duration, double, double, double>
 AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex,
 				 uint32_t exposureModeIndex,
 				 const Histogram &yHist,
diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h
index b00772760035a477152d638809ac71fcbc66cb68..71c4951080c728e87530caf189fbd52f92b3d033 100644
--- a/src/ipa/libipa/agc_mean_luminance.h
+++ b/src/ipa/libipa/agc_mean_luminance.h
@@ -80,7 +80,7 @@  public:
 		return controls_;
 	}
 
-	std::tuple<utils::Duration, double, double, double>
+	std::tuple<utils::Duration, utils::Duration, double, double, double>
 	calculateNewEv(uint32_t constraintModeIndex, uint32_t exposureModeIndex,
 		       const Histogram &yHist, utils::Duration effectiveExposureValue);
 
diff --git a/src/ipa/libipa/exposure_mode_helper.cpp b/src/ipa/libipa/exposure_mode_helper.cpp
index 46ba535c67d92bd384095b250b925cdc0da40b38..e9a37a3771cdfbb35ad4c8a373d3189f8a468c7b 100644
--- a/src/ipa/libipa/exposure_mode_helper.cpp
+++ b/src/ipa/libipa/exposure_mode_helper.cpp
@@ -100,9 +100,10 @@  void ExposureModeHelper::setMaxExposure(utils::Duration minExposureTime,
 			<< "Exposure margin not known. Default to 4";
 		margin = { 4 };
 	}
+	exposureMargin_ = margin.value() * lineDuration_;
 
 	maxExposureTime_ = minExposureTime != maxExposureTime
-			 ? maxFrameDuration - margin.value() * lineDuration_
+			 ? maxFrameDuration - exposureMargin_
 			 : minExposureTime;
 }
 
@@ -152,6 +153,7 @@  void ExposureModeHelper::configure(utils::Duration lineDuration,
 	setMaxExposure(minExposureTime, maxExposureTime, frameDuration);
 
 	maxFrameDuration_ = *maxFrameDuration = frameDuration;
+	minFrameDuration_ = minFrameDuration;
 }
 
 /**
@@ -207,6 +209,17 @@  double ExposureModeHelper::clampGain(double gain, double *quantizationGain) cons
 	return sensorHelper_->quantizeGain(clamped, quantizationGain);
 }
 
+utils::Duration ExposureModeHelper::frameDurationFromExposure(utils::Duration exposureTime) const
+{
+	/*
+	 * The maximum exposure value has already been clamped to the maximum
+	 * frame duration. Re-apply the exposure margin and make sure we don't
+	 * go below the minium frame duration.
+	 */
+	utils::Duration frameDuration = exposureTime + exposureMargin_;
+	return std::max(frameDuration, minFrameDuration_);
+}
+
 /**
  * \brief Split exposure into exposure time and gain
  * \param[in] exposure Exposure value
@@ -244,7 +257,7 @@  double ExposureModeHelper::clampGain(double gain, double *quantizationGain) cons
  * \return Tuple of exposure time, analogue gain, quantization gain and digital
  * gain
  */
-std::tuple<utils::Duration, double, double, double>
+std::tuple<utils::Duration, utils::Duration, double, double, double>
 ExposureModeHelper::splitExposure(utils::Duration exposure) const
 {
 	ASSERT(maxExposureTime_);
@@ -266,8 +279,8 @@  ExposureModeHelper::splitExposure(utils::Duration exposure) const
 		gain = clampGain(minGain_, &quantGain2);
 		quantGain *= quantGain2;
 
-		return { exposureTime, gain, quantGain,
-			 exposure / (exposureTime * gain * quantGain) };
+		return { exposureTime, frameDurationFromExposure(exposureTime),
+			 gain, quantGain, exposure / (exposureTime * gain * quantGain) };
 	}
 
 	double stageGain = clampGain(1.0);
@@ -292,8 +305,8 @@  ExposureModeHelper::splitExposure(utils::Duration exposure) const
 			gain = clampGain(exposure / exposureTime, &quantGain2);
 			quantGain *= quantGain2;
 
-			return { exposureTime, gain, quantGain,
-				 exposure / (exposureTime * gain * quantGain) };
+			return { exposureTime, frameDurationFromExposure(exposureTime),
+				 gain, quantGain, exposure / (exposureTime * gain * quantGain) };
 		}
 
 		/* Clamp the exposureTime to stageExposureTime and regulate gain. */
@@ -302,8 +315,8 @@  ExposureModeHelper::splitExposure(utils::Duration exposure) const
 			gain = clampGain(exposure / exposureTime, &quantGain2);
 			quantGain *= quantGain2;
 
-			return { exposureTime, gain, quantGain,
-				 exposure / (exposureTime * gain * quantGain) };
+			return { exposureTime, frameDurationFromExposure(exposureTime),
+				 gain, quantGain, exposure / (exposureTime * gain * quantGain) };
 		}
 
 		lastStageGain = stageGain;
@@ -320,8 +333,8 @@  ExposureModeHelper::splitExposure(utils::Duration exposure) const
 	gain = clampGain(exposure / exposureTime, &quantGain2);
 	quantGain *= quantGain2;
 
-	return { exposureTime, gain, quantGain,
-		 exposure / (exposureTime * gain * quantGain) };
+	return { exposureTime, frameDurationFromExposure(exposureTime),
+		 gain, quantGain, exposure / (exposureTime * gain * quantGain) };
 }
 
 /**
diff --git a/src/ipa/libipa/exposure_mode_helper.h b/src/ipa/libipa/exposure_mode_helper.h
index 492d2787f75945fb487b351a60f13756caec66e0..8dc94cf808945e0ff3cea080046b1cafd453a014 100644
--- a/src/ipa/libipa/exposure_mode_helper.h
+++ b/src/ipa/libipa/exposure_mode_helper.h
@@ -34,7 +34,7 @@  public:
 		       utils::Duration maxFrameDuration,
 		       double minGain, double maxGain);
 
-	std::tuple<utils::Duration, double, double, double>
+	std::tuple<utils::Duration, utils::Duration, double, double, double>
 	splitExposure(utils::Duration exposure) const;
 
 	utils::Duration minExposureTime() const { return minExposureTime_; }
@@ -50,6 +50,7 @@  private:
 	utils::Duration clampExposureTime(utils::Duration exposureTime,
 					  double *quantizationGain = nullptr) const;
 	double clampGain(double gain, double *quantizationGain = nullptr) const;
+	utils::Duration frameDurationFromExposure(utils::Duration exposureTime) const;
 
 	std::vector<utils::Duration> exposureTimes_;
 	std::vector<double> gains_;
@@ -58,6 +59,8 @@  private:
 	utils::Duration minExposureTime_;
 	utils::Duration maxExposureTime_;
 	utils::Duration maxFrameDuration_;
+	utils::Duration minFrameDuration_;
+	utils::Duration exposureMargin_;
 	double minGain_;
 	double maxGain_;
 	const CameraSensorHelper *sensorHelper_;
diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp
index 4e2fa4386be621c63d992b75d3d9efc525c3d69c..6ecb18d959c7f978966d619beb43e1c2cbfeec5f 100644
--- a/src/ipa/mali-c55/algorithms/agc.cpp
+++ b/src/ipa/mali-c55/algorithms/agc.cpp
@@ -391,9 +391,10 @@  void Agc::process(IPAContext &context,
 	utils::Duration currentShutter = exposure * configuration.sensor.lineDuration;
 	utils::Duration effectiveExposureValue = currentShutter * totalGain;
 
+	utils::Duration frameDuration;
 	utils::Duration shutterTime;
 	double aGain, qGain, dGain;
-	std::tie(shutterTime, aGain, qGain, dGain) =
+	std::tie(shutterTime, frameDuration, aGain, qGain, dGain) =
 		calculateNewEv(activeState.agc.constraintMode,
 			       activeState.agc.exposureMode, statistics_.yHist,
 			       effectiveExposureValue);
diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
index a983efbecbd84fa9e4af8b9642fbdda124ba0b9f..c8a34c3d1bf2ed71b9cf203e48c4b42910eea797 100644
--- a/src/ipa/rkisp1/algorithms/agc.cpp
+++ b/src/ipa/rkisp1/algorithms/agc.cpp
@@ -625,15 +625,17 @@  void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,
 	setExposureCompensation(pow(2.0, frameContext.agc.exposureValue));
 
 	utils::Duration newExposureTime;
+	utils::Duration frameDuration;
 	double aGain, qGain, dGain;
-	std::tie(newExposureTime, aGain, qGain, dGain) =
+	std::tie(newExposureTime, frameDuration, aGain, qGain, dGain) =
 		calculateNewEv(frameContext.agc.constraintMode,
 			       frameContext.agc.exposureMode,
 			       hist, effectiveExposureValue);
 
 	LOG(RkISP1Agc, Debug)
-		<< "Divided up exposure time, analogue gain, quantization gain"
-		<< " and digital gain are " << newExposureTime << ", " << aGain
+		<< "Divided up exposure time, frame duration, analogue gain,"
+		<< "quantization gain and digital gain are "
+		<< newExposureTime << ", " << frameDuration << ", " << aGain
 		<< ", " << qGain << " and " << dGain;
 
 	IPAActiveState &activeState = context.activeState;
@@ -646,8 +648,7 @@  void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,
 	 * Expand the target frame duration so that we do not run faster than
 	 * the minimum frame duration when we have short exposures.
 	 */
-	processFrameDuration(context, frameContext,
-			     std::max(frameContext.agc.minFrameDuration, newExposureTime));
+	processFrameDuration(context, frameContext, frameDuration);
 
 	fillMetadata(context, frameContext, metadata);
 	expMeans_ = {};