From patchwork Wed Feb 23 10:48:23 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jean-Michel Hautbois X-Patchwork-Id: 15374 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 38D6CC3261 for ; Wed, 23 Feb 2022 10:48:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8C69E61183; Wed, 23 Feb 2022 11:48:31 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="oF18WPIb"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CA03261101 for ; Wed, 23 Feb 2022 11:48:28 +0100 (CET) Received: from tatooine.ideasonboard.com (unknown [IPv6:2a01:e0a:169:7140:7ebc:9e38:c4df:c57]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 8360A11BF; Wed, 23 Feb 2022 11:48:28 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1645613308; bh=jxU9hk1cYKxbO9o549ZcQQLvcDjxXbHlDRELGKmSvnc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=oF18WPIbM+mPX8EmtYXRhHzEPxjfQB63gQT890BiRW4FeOszW44dBmbjT9pN/qI7j JsB2/QW8fmseP12ldYA9X7ktVqAXlFx3AUB6Zr/8fpVB58wn4UcvxwRozKX6KvE/eh 46zA1AWDTOl/16y3AM+nfvBe7aarWg/u/Ovb2/So= From: Jean-Michel Hautbois To: libcamera-devel@lists.libcamera.org Date: Wed, 23 Feb 2022 11:48:23 +0100 Message-Id: <20220223104824.25723-4-jeanmichel.hautbois@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220223104824.25723-1-jeanmichel.hautbois@ideasonboard.com> References: <20220223104824.25723-1-jeanmichel.hautbois@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 3/4] ipa: rkisp1: agc: Introduce histogram calculation X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 Tested-by: Peter Griffin --- src/ipa/rkisp1/algorithms/agc.cpp | 97 ++++++++++++++++++++++++++----- src/ipa/rkisp1/algorithms/agc.h | 4 +- src/ipa/rkisp1/ipa_context.cpp | 3 + src/ipa/rkisp1/ipa_context.h | 1 + 4 files changed, 89 insertions(+), 16 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 50980835..0311ecf4 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -16,6 +16,8 @@ #include +#include "libipa/histogram.h" + /** * \file agc.h */ @@ -43,6 +45,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. * @@ -51,10 +56,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) { } @@ -65,8 +70,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); @@ -77,10 +81,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; } @@ -125,7 +138,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; @@ -134,6 +147,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); @@ -144,7 +160,7 @@ void Agc::computeExposure(IPAContext &context, double yGain) kMaxAnalogueGain); /* Consider within 1% of the target as correctly exposed. */ - if (utils::abs_diff(yGain, 1.0) < 0.01) + if (utils::abs_diff(evGain, 1.0) < 0.01) return; /* extracted from Rpi::Agc::computeTargetExposure. */ @@ -161,13 +177,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; @@ -238,6 +254,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 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(measHist)).interQuantileMean(0.98, 1.0); +} + /** * \brief Process RkISP1 statistics, and run AGC operations * \param[in] context The shared IPA context @@ -252,9 +285,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 = ¶ms->ae; + const rkisp1_cif_isp_hist_stat *hist = ¶ms->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 @@ -277,14 +314,44 @@ 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) { - params->module_ens |= RKISP1_CIF_ISP_MODULE_AEC; - params->module_en_update |= RKISP1_CIF_ISP_MODULE_AEC; + /* 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; + + if (context.frameContext.frameId == 0) { + 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; + + if (context.frameContext.frameId == 0) { + /* 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 942c9d7a..872776d0 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_; }; diff --git a/src/ipa/rkisp1/ipa_context.cpp b/src/ipa/rkisp1/ipa_context.cpp index 992c9225..39acef47 100644 --- a/src/ipa/rkisp1/ipa_context.cpp +++ b/src/ipa/rkisp1/ipa_context.cpp @@ -71,6 +71,9 @@ namespace libcamera::ipa::rkisp1 { * \var IPASessionConfiguration::agc.maxAnalogueGain * \brief Maximum analogue gain supported with the configured sensor * + * \var IPASessionConfiguration::agc.measureWindow + * \brief AGC measure window + * * \var IPASessionConfiguration::hw * \brief RkISP1-specific hardware information * diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index c447369f..9ac3b40c 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -22,6 +22,7 @@ struct IPASessionConfiguration { utils::Duration maxShutterSpeed; double minAnalogueGain; double maxAnalogueGain; + struct rkisp1_cif_isp_window measureWindow; } agc; struct {