[libcamera-devel,v1,6/6] ipa: rkisp1: agc: Introduce histogram calculation
diff mbox series

Message ID 20211202180410.518232-7-jeanmichel.hautbois@ideasonboard.com
State Changes Requested
Headers show
Series
  • IPA RkISP1 awb and misc improvements
Related show

Commit Message

Jean-Michel Hautbois Dec. 2, 2021, 6:04 p.m. UTC
As for the IPU3, we can estimate the histogram of the luminance. The
RkISP1 can estimate multiple ones, the R, G and B ones, the Y only one
and a combination of RGB. The one we are interested by in AGC is the Y
histogram.

Use the hardware revision to determine the number of bins of the
produced histogram, and use it to populate a vector passed down to the
libipa::Histogram class.

As the sensor deGamma and AWB are applied, we also need to get back to
the relative luminance value of 0.16, as for the IPU3.

Signed-off-by: Jean-Michel Hautbois <jeanmichel.hautbois@ideasonboard.com>
---
 src/ipa/rkisp1/algorithms/agc.cpp | 87 ++++++++++++++++++++++++++-----
 src/ipa/rkisp1/algorithms/agc.h   |  4 +-
 2 files changed, 77 insertions(+), 14 deletions(-)

Patch
diff mbox series

diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
index f95ecfde5..dd7b26ae7 100644
--- a/src/ipa/rkisp1/algorithms/agc.cpp
+++ b/src/ipa/rkisp1/algorithms/agc.cpp
@@ -15,6 +15,8 @@ 
 
 #include <libcamera/ipa/core_ipa_interface.h>
 
+#include "libipa/histogram.h"
+
 /**
  * \file agc.h
  */
@@ -42,6 +44,9 @@  static constexpr utils::Duration kMaxShutterSpeed = 60ms;
 /* Number of frames to wait before calculating stats on minimum exposure */
 static constexpr uint32_t kNumStartupFrames = 10;
 
+/* Target value to reach for the top 2% of the histogram */
+static constexpr double kEvGainTarget = 0.5;
+
 /*
  * Relative luminance target.
  *
@@ -50,10 +55,10 @@  static constexpr uint32_t kNumStartupFrames = 10;
  *
  * \todo Why is the value different between IPU3 and RkISP1 ?
  */
-static constexpr double kRelativeLuminanceTarget = 0.4;
+static constexpr double kRelativeLuminanceTarget = 0.16;
 
 Agc::Agc()
-	: frameCount_(0), numCells_(0), filteredExposure_(0s)
+	: frameCount_(0), numCells_(0), numHistBins_(0), filteredExposure_(0s)
 {
 }
 
@@ -64,8 +69,7 @@  Agc::Agc()
  *
  * \return 0
  */
-int Agc::configure(IPAContext &context,
-		   [[maybe_unused]] const IPACameraSensorInfo &configInfo)
+int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
 {
 	/* Configure the default exposure and gain. */
 	context.frameContext.agc.gain = std::max(context.configuration.agc.minAnalogueGain, kMinAnalogueGain);
@@ -76,10 +80,19 @@  int Agc::configure(IPAContext &context,
 	 * - versions < V12 have RKISP1_CIF_ISP_AE_MEAN_MAX_V10 entries,
 	 * - versions >= V12 have RKISP1_CIF_ISP_AE_MEAN_MAX_V12 entries.
 	 */
-	if (context.configuration.hw.revision < RKISP1_V12)
+	if (context.configuration.hw.revision < RKISP1_V12) {
 		numCells_ = RKISP1_CIF_ISP_AE_MEAN_MAX_V10;
-	else
+		numHistBins_ = RKISP1_CIF_ISP_HIST_BIN_N_MAX_V10;
+	} else {
 		numCells_ = RKISP1_CIF_ISP_AE_MEAN_MAX_V12;
+		numHistBins_ = RKISP1_CIF_ISP_HIST_BIN_N_MAX_V12;
+	}
+
+	/* Define the measurement window for AGC. */
+	context.configuration.agc.measureWindow.h_offs = configInfo.outputSize.width / 8;
+	context.configuration.agc.measureWindow.v_offs = configInfo.outputSize.height / 8;
+	context.configuration.agc.measureWindow.h_size = 3 * configInfo.outputSize.width / 4;
+	context.configuration.agc.measureWindow.v_size = 3 * configInfo.outputSize.height / 4;
 
 	return 0;
 }
@@ -124,7 +137,7 @@  utils::Duration Agc::filterExposure(utils::Duration exposureValue)
  * \param[inout] frameContext The shared IPA frame Context
  * \param[in] yGain The gain calculated on the current brightness level
  */
-void Agc::computeExposure(IPAContext &context, double yGain)
+void Agc::computeExposure(IPAContext &context, double yGain, double iqMeanGain)
 {
 	IPASessionConfiguration &configuration = context.configuration;
 	IPAFrameContext &frameContext = context.frameContext;
@@ -133,6 +146,9 @@  void Agc::computeExposure(IPAContext &context, double yGain)
 	uint32_t exposure = frameContext.sensor.exposure;
 	double analogueGain = frameContext.sensor.gain;
 
+	/* Use the highest of the two gain estimates. */
+	double evGain = std::max(yGain, iqMeanGain);
+
 	utils::Duration minShutterSpeed = configuration.agc.minShutterSpeed;
 	utils::Duration maxShutterSpeed = std::min(configuration.agc.maxShutterSpeed,
 						   kMaxShutterSpeed);
@@ -143,7 +159,7 @@  void Agc::computeExposure(IPAContext &context, double yGain)
 					  kMaxAnalogueGain);
 
 	/* Consider within 1% of the target as correctly exposed. */
-	if (std::abs(yGain - 1.0) < 0.01)
+	if (std::abs(evGain - 1.0) < 0.01)
 		return;
 
 	/* extracted from Rpi::Agc::computeTargetExposure. */
@@ -160,13 +176,13 @@  void Agc::computeExposure(IPAContext &context, double yGain)
 	LOG(RkISP1Agc, Debug) << "Actual total exposure " << currentShutter * analogueGain
 			      << " Shutter speed " << currentShutter
 			      << " Gain " << analogueGain
-			      << " Needed ev gain " << yGain;
+			      << " Needed ev gain " << evGain;
 
 	/*
 	 * Calculate the current exposure value for the scene as the latest
 	 * exposure value applied multiplied by the new estimated gain.
 	 */
-	utils::Duration exposureValue = effectiveExposureValue * yGain;
+	utils::Duration exposureValue = effectiveExposureValue * evGain;
 
 	/* Clamp the exposure value to the min and max authorized. */
 	utils::Duration maxTotalExposure = maxShutterSpeed * maxAnalogueGain;
@@ -237,6 +253,23 @@  double Agc::estimateLuminance(const rkisp1_cif_isp_ae_stat *ae,
 	return ySum / numCells_ / 255;
 }
 
+/**
+ * \brief Estimate the mean value of the top 2% of the histogram
+ * \param[in] hist The histogram statistics computed by the ImgU
+ * \return The mean value of the top 2% of the histogram
+ */
+double Agc::measureBrightness(const rkisp1_cif_isp_hist_stat *hist) const
+{
+	std::vector<uint32_t> measHist(numHistBins_);
+
+	/* Initialise the histogram array using the maximum available size */
+	for (unsigned int histBin = 0; histBin < numHistBins_; histBin++)
+		measHist.push_back(hist->hist_bins[histBin]);
+
+	/* Estimate the quantile mean of the top 2% of the histogram. */
+	return Histogram(Span<uint32_t>(measHist)).interQuantileMean(0.98, 1.0);
+}
+
 /**
  * \brief Process RkISP1 statistics, and run AGC operations
  * \param[in] context The shared IPA context
@@ -251,9 +284,13 @@  void Agc::process(IPAContext &context, const rkisp1_stat_buffer *stats)
 	ASSERT(stats->meas_type & RKISP1_CIF_ISP_STAT_AUTOEXP);
 
 	const rkisp1_cif_isp_ae_stat *ae = &params->ae;
+	const rkisp1_cif_isp_hist_stat *hist = &params->hist;
 
 	frameCount_ = context.frameContext.frameId;
 
+	double iqMean = measureBrightness(hist);
+	double iqMeanGain = kEvGainTarget * numHistBins_ / iqMean;
+
 	/*
 	 * Estimate the gain needed to achieve a relative luminance target. To
 	 * account for non-linearity caused by saturation, the value needs to be
@@ -276,14 +313,38 @@  void Agc::process(IPAContext &context, const rkisp1_stat_buffer *stats)
 			break;
 	}
 
-	computeExposure(context, yGain);
+	computeExposure(context, yGain, iqMeanGain);
 }
 
-void Agc::prepare([[maybe_unused]] IPAContext &context,
-		  rkisp1_params_cfg *params)
+/**
+ * \copydoc libcamera::ipa::Algorithm::prepare
+ */
+void Agc::prepare(IPAContext &context, rkisp1_params_cfg *params)
 {
+	/* Configure the measurement window. */
+	params->meas.aec_config.meas_window = context.configuration.agc.measureWindow;
+	/* Use a continuous methode for measure. */
+	params->meas.aec_config.autostop = RKISP1_CIF_ISP_EXP_CTRL_AUTOSTOP_0;
+	/* Estimate Y as (R + G + B) x (85/256). */
+	params->meas.aec_config.mode = RKISP1_CIF_ISP_EXP_MEASURING_MODE_1;
+	params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_AEC;
 	params->module_ens |= RKISP1_CIF_ISP_MODULE_AEC;
 	params->module_en_update |= RKISP1_CIF_ISP_MODULE_AEC;
+
+	/* Configure histogram. */
+	params->meas.hst_config.meas_window = context.configuration.agc.measureWindow;
+	/* Produce the luminance histogram. */
+	params->meas.hst_config.mode = RKISP1_CIF_ISP_HISTOGRAM_MODE_Y_HISTOGRAM;
+	/* Set an average weighted histogram. */
+	for (unsigned int histBin = 0; histBin < numHistBins_; histBin++)
+		params->meas.hst_config.hist_weight[histBin] = 1;
+	/* Step size can't be less than 3. */
+	params->meas.hst_config.histogram_predivider = 4;
+	/* Update the configuration for histogram. */
+	params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_HST;
+	/* Enable the histogram measure unit. */
+	params->module_ens |= RKISP1_CIF_ISP_MODULE_HST;
+	params->module_en_update |= RKISP1_CIF_ISP_MODULE_HST;
 }
 
 } /* namespace ipa::rkisp1::algorithms */
diff --git a/src/ipa/rkisp1/algorithms/agc.h b/src/ipa/rkisp1/algorithms/agc.h
index 942c9d7a6..872776d0e 100644
--- a/src/ipa/rkisp1/algorithms/agc.h
+++ b/src/ipa/rkisp1/algorithms/agc.h
@@ -32,13 +32,15 @@  public:
 	void process(IPAContext &context, const rkisp1_stat_buffer *stats) override;
 
 private:
-	void computeExposure(IPAContext &Context, double yGain);
+	void computeExposure(IPAContext &Context, double yGain, double iqMeanGain);
 	utils::Duration filterExposure(utils::Duration exposureValue);
 	double estimateLuminance(const rkisp1_cif_isp_ae_stat *ae, double gain);
+	double measureBrightness(const rkisp1_cif_isp_hist_stat *hist) const;
 
 	uint64_t frameCount_;
 
 	uint32_t numCells_;
+	uint32_t numHistBins_;
 
 	utils::Duration filteredExposure_;
 };