From patchwork Fri May 17 08:07:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 20062 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 223ACBD78E for ; Fri, 17 May 2024 08:08:15 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 956E863484; Fri, 17 May 2024 10:08:14 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Vu/pQGOw"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C895263481 for ; Fri, 17 May 2024 10:08:11 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (h175-177-049-156.catv02.itscom.jp [175.177.49.156]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 9CC3BD49; Fri, 17 May 2024 10:08:01 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1715933282; bh=XCdkKLFzRUMlHE/avLE5hZrjtk7oS0AAPDwdh9sHNos=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Vu/pQGOwBnTioovJjz1d3d427TWLhY+V8U5JzrO6KVbEpfV8OPg1aSx/tTNm1xiZd +pKjJJg/+xcF3dtNPu98YTIlpmjxER9vzIKhBLdlKFg4m6jav86Qu8ISwLuyZANcMr +M+ifN0raTnJdpsQl9H2oJHHMXsIE48dgL2+q0bw= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder Subject: [PATCH v3 1/3] ipa: rkisp1: agc: Read histogram weights from tuning file Date: Fri, 17 May 2024 17:07:59 +0900 Message-Id: <20240517080802.3896531-2-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240517080802.3896531-1-paul.elder@ideasonboard.com> References: <20240517080802.3896531-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 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" Add support to the rkisp1 AGC to read histogram weights from the tuning file. As controls for selecting the metering mode are not yet supported, for now hardcode the matrix metering mode, which is the same as what the AGC previously hardcoded. Signed-off-by: Paul Elder Reviewed-by: Stefan Klug --- Changes in v3: - support the new tuning file layout for interpolated matrices - support both v10 and v12 Changes in v2: - add default metering mode if none are read successfully from the tuning file - compute the predivider instead of using a table --- src/ipa/rkisp1/algorithms/agc.cpp | 94 ++++++++++++++++++++++++++++++- src/ipa/rkisp1/algorithms/agc.h | 6 ++ 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 5aad5c7c2..4af397bdc 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -17,6 +17,8 @@ #include #include +#include "libcamera/internal/yaml_parser.h" + #include "libipa/histogram.h" /** @@ -36,6 +38,76 @@ namespace ipa::rkisp1::algorithms { LOG_DEFINE_CATEGORY(RkISP1Agc) +int Agc::parseMeteringModes(IPAContext &context, const YamlObject &tuningData, + const char *prop) +{ + const YamlObject &yamlMeteringModes = tuningData[prop]; + if (!yamlMeteringModes.isDictionary()) { + LOG(RkISP1Agc, Error) + << "'" << prop << "' parameter not found in tuning file"; + return -EINVAL; + } + + for (const auto &[key, value] : yamlMeteringModes.asDict()) { + if (controls::AeMeteringModeNameValueMap.find(key) == + controls::AeMeteringModeNameValueMap.end()) { + LOG(RkISP1Agc, Warning) + << "Skipping unknown metering mode '" << key << "'"; + continue; + } + + for (const auto &[version, matrix] : value.asDict()) { + std::vector weights = + matrix.getList().value_or(std::vector{}); + if (weights.size() != context.hw->numHistogramWeights) + continue; + + LOG(RkISP1Agc, Debug) + << "Matched metering matrix mode " + << key << ", version " << version; + + meteringModes_[controls::AeMeteringModeNameValueMap.at(key)] = weights; + } + } + + if (meteringModes_.empty()) { + int32_t meteringModeId = controls::AeMeteringModeNameValueMap.at("MeteringMatrix"); + std::vector weights(context.hw->numHistogramWeights, 1); + + meteringModes_[meteringModeId] = weights; + } + + return 0; +} + +uint8_t Agc::predivider(Size &size) +{ + /* + * The maximum number of pixels that could potentially be in one bin is + * if all the pixels of the image are in it, multiplied by 3 for the + * three color channels. The counter for each bin is 16 bits wide, so + * `factor` thus contains the number of times we'd wrap around. This is + * obviously the number of pixels that we need to skip to make sure + * that we don't wrap around, but we compute the square root of it + * instead, as the skip that we need to program is for both the x and y + * directions. + * + * There's a bit of extra rounding math to make sure the rounding goes + * the correct direction so that the square of the step is big enough + * to encompass the `factor` number of pixels that we need to skip. + */ + double factor = size.width * size.height * 3 / 65535.0; + double root = std::sqrt(factor); + uint8_t ret; + + if (std::pow(std::floor(root), 2) + 0.01 < factor) + ret = static_cast(std::ceil(root)); + else + ret = static_cast(std::floor(root)); + + return std::clamp(ret, 3, 127); +} + Agc::Agc() { supportsRaw_ = true; @@ -59,6 +131,10 @@ int Agc::init(IPAContext &context, const YamlObject &tuningData) if (ret) return ret; + ret = parseMeteringModes(context, tuningData, "AeMeteringMode"); + if (ret) + return ret; + context.ctrlMap.merge(controls()); return 0; @@ -160,6 +236,7 @@ void Agc::prepare(IPAContext &context, const uint32_t frame, frameContext.agc.gain = context.activeState.agc.automatic.gain; } + /* \todo Remove this when we can set the below with controls */ if (frame > 0) return; @@ -178,14 +255,25 @@ void Agc::prepare(IPAContext &context, const uint32_t frame, 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. */ Span weights{ params->meas.hst_config.hist_weight, context.hw->numHistogramWeights }; - std::fill(weights.begin(), weights.end(), 1); - /* Step size can't be less than 3. */ - params->meas.hst_config.histogram_predivider = 4; + /* \todo Get this from control */ + std::vector &modeWeights = meteringModes_.at(controls::MeteringMatrix); + std::copy(modeWeights.begin(), modeWeights.end(), weights.begin()); + + std::stringstream str; + str << "Histogram weights : "; + for (size_t i = 0; i < context.hw->numHistogramWeights; i++) + str << (int)params->meas.hst_config.hist_weight[i] << " "; + LOG(RkISP1Agc, Debug) << str.str(); + + /* \todo Add a control for this? */ + params->meas.hst_config.histogram_predivider = + predivider(context.configuration.sensor.size); /* Update the configuration for histogram. */ params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_HST; diff --git a/src/ipa/rkisp1/algorithms/agc.h b/src/ipa/rkisp1/algorithms/agc.h index f2f5b59d0..77d944237 100644 --- a/src/ipa/rkisp1/algorithms/agc.h +++ b/src/ipa/rkisp1/algorithms/agc.h @@ -44,11 +44,17 @@ public: ControlList &metadata) override; private: + int parseMeteringModes(IPAContext &context, const YamlObject &tuningData, + const char *prop); + uint8_t predivider(Size &size); + void fillMetadata(IPAContext &context, IPAFrameContext &frameContext, ControlList &metadata); double estimateLuminance(double gain) const override; Span expMeans_; + + std::map> meteringModes_; }; } /* namespace ipa::rkisp1::algorithms */ From patchwork Fri May 17 08:08:00 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 20063 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 24A5DBD78E for ; Fri, 17 May 2024 08:08:18 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C91526347C; Fri, 17 May 2024 10:08:17 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="EI7D0eRH"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 716F66347C for ; Fri, 17 May 2024 10:08:13 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (h175-177-049-156.catv02.itscom.jp [175.177.49.156]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 40EFC512; Fri, 17 May 2024 10:08:03 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1715933284; bh=a9FaRafw2h5nCtyietIF5enmJb+LshWt0xILvrq4fA0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EI7D0eRH+oOsA2UijF5jRCXjXdJ7JgRQS95T0fPEgCqi0Hz3YJlBDj95bUas6DAuv D9alnCHYyBFH0NyCKrjI8vFxtSeIRRMtOa88RYvKcWbMDxPOghmCtKE4nvzBkwDeX/ nndERcsmVzMf0lI6te3bhrnmKExaPxJ3gW628yf0= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , Stefan Klug Subject: [PATCH v3 2/3] ipa: libipa: agc: Change luminance target to piecewise linear function Date: Fri, 17 May 2024 17:08:00 +0900 Message-Id: <20240517080802.3896531-3-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240517080802.3896531-1-paul.elder@ideasonboard.com> References: <20240517080802.3896531-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 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" Change the relative luminance target from a scalar value to a piecewise linear function that needs to be sampled by the estimate lux value. Also change the rkisp1 and ipu3 IPAs accordingly, as they use the libipa agc. As they both don't yet have lux modules, hardcode them to a single lux value for now. This affects the format of the tuning files, but as there aren't yet any this shouldn't be an issue. Signed-off-by: Paul Elder Reviewed-by: Stefan Klug Reviewed-by: Daniel Scally --- No change in v3 Changes in v2: - s/FPoint/PointF/ - add warning when using default relative luminance target when loading from the tuning file fails --- src/ipa/ipu3/algorithms/agc.cpp | 5 ++++- src/ipa/libipa/agc_mean_luminance.cpp | 31 +++++++++++++++++++++------ src/ipa/libipa/agc_mean_luminance.h | 7 +++--- src/ipa/rkisp1/algorithms/agc.cpp | 5 ++++- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp index c9b5548c4..984ed0874 100644 --- a/src/ipa/ipu3/algorithms/agc.cpp +++ b/src/ipa/ipu3/algorithms/agc.cpp @@ -222,12 +222,15 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, double analogueGain = frameContext.sensor.gain; utils::Duration effectiveExposureValue = exposureTime * analogueGain; + /* \todo Plumb in the lux value. Requires a lux algo + tuning */ + double lux = 400; + utils::Duration shutterTime; double aGain, dGain; std::tie(shutterTime, aGain, dGain) = calculateNewEv(context.activeState.agc.constraintMode, context.activeState.agc.exposureMode, hist, - effectiveExposureValue); + effectiveExposureValue, lux); LOG(IPU3Agc, Debug) << "Divided up shutter, analogue gain and digital gain are " diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index 2bf84d05b..fe07777dc 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -134,7 +134,7 @@ static constexpr double kDefaultRelativeLuminanceTarget = 0.16; */ AgcMeanLuminance::AgcMeanLuminance() - : frameCount_(0), filteredExposure_(0s), relativeLuminanceTarget_(0) + : frameCount_(0), filteredExposure_(0s) { } @@ -142,8 +142,16 @@ AgcMeanLuminance::~AgcMeanLuminance() = default; void AgcMeanLuminance::parseRelativeLuminanceTarget(const YamlObject &tuningData) { - relativeLuminanceTarget_ = - tuningData["relativeLuminanceTarget"].get(kDefaultRelativeLuminanceTarget); + int ret = relativeLuminanceTarget_.readYaml(tuningData["relativeLuminanceTarget"]); + if (ret == 0) + return; + + LOG(AgcMeanLuminance, Warning) + << "Failed to load tuning parameter 'relativeLuminanceTarget', " + << "using default [0, " << kDefaultRelativeLuminanceTarget << "]"; + + std::vector points = { { 0, kDefaultRelativeLuminanceTarget } }; + relativeLuminanceTarget_ = Pwl(points); } void AgcMeanLuminance::parseConstraint(const YamlObject &modeDict, int32_t id) @@ -421,11 +429,18 @@ void AgcMeanLuminance::setLimits(utils::Duration minShutter, /** * \brief Estimate the initial gain needed to achieve a relative luminance * target + * \param[in] lux The lux value at which to sample the luminance target pwl + * + * To account for non-linearity caused by saturation, the value needs to be + * estimated in an iterative process, as multiplying by a gain will not increase + * the relative luminance by the same factor if some image regions are saturated + * * \return The calculated initial gain */ -double AgcMeanLuminance::estimateInitialGain() const +double AgcMeanLuminance::estimateInitialGain(double lux) const { - double yTarget = relativeLuminanceTarget_; + double yTarget = + relativeLuminanceTarget_.eval(relativeLuminanceTarget_.domain().clamp(lux)); double yGain = 1.0; /* @@ -520,6 +535,7 @@ utils::Duration AgcMeanLuminance::filterExposure(utils::Duration exposureValue) * the calculated gain * \param[in] effectiveExposureValue The EV applied to the frame from which the * statistics in use derive + * \param[in] lux The lux value at which to sample the luminance target pwl * * Calculate a new exposure value to try to obtain the target. The calculated * exposure value is filtered to prevent rapid changes from frame to frame, and @@ -531,7 +547,8 @@ std::tuple AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex, uint32_t exposureModeIndex, const Histogram &yHist, - utils::Duration effectiveExposureValue) + utils::Duration effectiveExposureValue, + double lux) { /* * The pipeline handler should validate that we have received an allowed @@ -540,7 +557,7 @@ AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex, std::shared_ptr exposureModeHelper = exposureModeHelpers_.at(exposureModeIndex); - double gain = estimateInitialGain(); + double gain = estimateInitialGain(lux); gain = constraintClampGain(constraintModeIndex, yHist, gain); /* diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index 0a81c6d28..6ec2a0dc9 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -18,6 +18,7 @@ #include "exposure_mode_helper.h" #include "histogram.h" +#include "pwl.h" namespace libcamera { @@ -62,7 +63,7 @@ public: std::tuple calculateNewEv(uint32_t constraintModeIndex, uint32_t exposureModeIndex, - const Histogram &yHist, utils::Duration effectiveExposureValue); + const Histogram &yHist, utils::Duration effectiveExposureValue, double lux); void resetFrameCount() { @@ -76,7 +77,7 @@ private: void parseConstraint(const YamlObject &modeDict, int32_t id); int parseConstraintModes(const YamlObject &tuningData); int parseExposureModes(const YamlObject &tuningData); - double estimateInitialGain() const; + double estimateInitialGain(double lux) const; double constraintClampGain(uint32_t constraintModeIndex, const Histogram &hist, double gain); @@ -84,7 +85,7 @@ private: uint64_t frameCount_; utils::Duration filteredExposure_; - double relativeLuminanceTarget_; + Pwl relativeLuminanceTarget_; std::map> constraintModes_; std::map> exposureModeHelpers_; diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 4af397bdc..1c9872d02 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -379,12 +379,15 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, double analogueGain = frameContext.sensor.gain; utils::Duration effectiveExposureValue = exposureTime * analogueGain; + /* \todo Plumb in the lux value. Requires a lux algo + tuning */ + double lux = 400; + utils::Duration shutterTime; double aGain, dGain; std::tie(shutterTime, aGain, dGain) = calculateNewEv(context.activeState.agc.constraintMode, context.activeState.agc.exposureMode, - hist, effectiveExposureValue); + hist, effectiveExposureValue, lux); LOG(RkISP1Agc, Debug) << "Divided up shutter, analogue gain and digital gain are " From patchwork Fri May 17 08:08:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 20064 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 D8267C322E for ; Fri, 17 May 2024 08:08:19 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B3F2063488; Fri, 17 May 2024 10:08:18 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="jBVBkbjK"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 13AE66347E for ; Fri, 17 May 2024 10:08:15 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (h175-177-049-156.catv02.itscom.jp [175.177.49.156]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 26A24D49; Fri, 17 May 2024 10:08:04 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1715933286; bh=RIQ+qy+Qr25XQScAPj/r2YHRmAXqWBjvsfq644P1l34=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jBVBkbjK+6r2E78ZwuFXWKjyxWbtjW14autq0BEUrn8fqDyMkg/nIcSjjS0SDdw2b PU5B75IUk3j4PeH9hHlW7X7tXL+CNU5B+xZXz+UArpFyCDzECLNYYaVCBC3KAUb06U KS8KEy4MXKzz1Ws32HJHp+Nwe9x1FMprNaOHknjk= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder Subject: [PATCH v3 3/3] ipa: rkisp1: agc: Plumb mode-selection and frame duration controls Date: Fri, 17 May 2024 17:08:01 +0900 Message-Id: <20240517080802.3896531-4-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240517080802.3896531-1-paul.elder@ideasonboard.com> References: <20240517080802.3896531-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 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" Plumb controls for setting metering mode, exposure mode, constraint mode, and frame duration limits. Also report them as available controls, as well as in metadata. While at it, add the missing #include for tuple, as a std::tie is used. Signed-off-by: Paul Elder Reviewed-by: Stefan Klug --- Changes in v3: - prevent maxShutterSpeed (for setLimits) from choosing 0 shutter speed if one of the inputs std::min() is not set (this fixes an assertion failure in the exposure mode helper) Changes in v2: - add #include - don't overwrite metering/exposure/constraint mode controls to default if no control is supplied, and retain the old value instead (same for frame duration limits) --- src/ipa/rkisp1/algorithms/agc.cpp | 55 ++++++++++++++++++++++++--- src/ipa/rkisp1/algorithms/algorithm.h | 2 + src/ipa/rkisp1/ipa_context.h | 10 ++++- src/ipa/rkisp1/rkisp1.cpp | 10 +++++ 4 files changed, 70 insertions(+), 7 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 1c9872d02..ce8beae24 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include @@ -48,6 +50,7 @@ int Agc::parseMeteringModes(IPAContext &context, const YamlObject &tuningData, return -EINVAL; } + std::vector availableMeteringModes; for (const auto &[key, value] : yamlMeteringModes.asDict()) { if (controls::AeMeteringModeNameValueMap.find(key) == controls::AeMeteringModeNameValueMap.end()) { @@ -66,17 +69,23 @@ int Agc::parseMeteringModes(IPAContext &context, const YamlObject &tuningData, << "Matched metering matrix mode " << key << ", version " << version; - meteringModes_[controls::AeMeteringModeNameValueMap.at(key)] = weights; + int32_t control = controls::AeMeteringModeNameValueMap.at(key); + meteringModes_[control] = weights; + availableMeteringModes.push_back(control); } } - if (meteringModes_.empty()) { + if (availableMeteringModes.empty()) { int32_t meteringModeId = controls::AeMeteringModeNameValueMap.at("MeteringMatrix"); std::vector weights(context.hw->numHistogramWeights, 1); meteringModes_[meteringModeId] = weights; + availableMeteringModes.push_back(meteringModeId); } + Algorithm::controls_[&controls::AeMeteringMode] = + ControlInfo(availableMeteringModes); + return 0; } @@ -137,6 +146,8 @@ int Agc::init(IPAContext &context, const YamlObject &tuningData) context.ctrlMap.merge(controls()); + Algorithm::controls_.merge(ControlInfoMap::Map(controls())); + return 0; } @@ -159,6 +170,7 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) context.activeState.agc.constraintMode = constraintModes().begin()->first; context.activeState.agc.exposureMode = exposureModeHelpers().begin()->first; + context.activeState.agc.meteringMode = meteringModes_.begin()->first; /* * Define the measurement window for AGC as a centered rectangle @@ -169,7 +181,6 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) context.configuration.agc.measureWindow.h_size = 3 * configInfo.outputSize.width / 4; context.configuration.agc.measureWindow.v_size = 3 * configInfo.outputSize.height / 4; - /* \todo Run this again when FrameDurationLimits is passed in */ setLimits(context.configuration.sensor.minShutterSpeed, context.configuration.sensor.maxShutterSpeed, context.configuration.sensor.minAnalogueGain, @@ -223,6 +234,26 @@ void Agc::queueRequest(IPAContext &context, frameContext.agc.exposure = agc.manual.exposure; frameContext.agc.gain = agc.manual.gain; } + + const auto &meteringMode = controls.get(controls::AeMeteringMode); + if (meteringMode) + agc.meteringMode = *meteringMode; + frameContext.agc.meteringMode = agc.meteringMode; + + const auto &exposureMode = controls.get(controls::AeExposureMode); + if (exposureMode) + agc.exposureMode = *exposureMode; + frameContext.agc.exposureMode = agc.exposureMode; + + const auto &constraintMode = controls.get(controls::AeConstraintMode); + if (constraintMode) + agc.constraintMode = *constraintMode; + frameContext.agc.constraintMode = agc.constraintMode; + + const auto &frameDurationLimits = controls.get(controls::FrameDurationLimits); + if (frameDurationLimits) + agc.maxShutterSpeed = std::chrono::milliseconds((*frameDurationLimits).back()); + frameContext.agc.maxShutterSpeed = agc.maxShutterSpeed; } /** @@ -261,8 +292,7 @@ void Agc::prepare(IPAContext &context, const uint32_t frame, params->meas.hst_config.hist_weight, context.hw->numHistogramWeights }; - /* \todo Get this from control */ - std::vector &modeWeights = meteringModes_.at(controls::MeteringMatrix); + std::vector &modeWeights = meteringModes_.at(frameContext.agc.meteringMode); std::copy(modeWeights.begin(), modeWeights.end(), weights.begin()); std::stringstream str; @@ -289,6 +319,7 @@ void Agc::fillMetadata(IPAContext &context, IPAFrameContext &frameContext, * frameContext.sensor.exposure; metadata.set(controls::AnalogueGain, frameContext.sensor.gain); metadata.set(controls::ExposureTime, exposureTime.get()); + metadata.set(controls::AeEnable, frameContext.agc.autoEnabled); /* \todo Use VBlank value calculated from each frame exposure. */ uint32_t vTotal = context.configuration.sensor.size.height @@ -296,6 +327,10 @@ void Agc::fillMetadata(IPAContext &context, IPAFrameContext &frameContext, utils::Duration frameDuration = context.configuration.sensor.lineDuration * vTotal; metadata.set(controls::FrameDuration, frameDuration.get()); + + metadata.set(controls::AeMeteringMode, frameContext.agc.meteringMode); + metadata.set(controls::AeExposureMode, frameContext.agc.exposureMode); + metadata.set(controls::AeConstraintMode, frameContext.agc.constraintMode); } /** @@ -370,6 +405,16 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, [](uint32_t x) { return x >> 4; }); expMeans_ = { params->ae.exp_mean, context.hw->numAeCells }; + utils::Duration maxShutterSpeed = std::min(context.configuration.sensor.maxShutterSpeed, + frameContext.agc.maxShutterSpeed); + if (!maxShutterSpeed) + maxShutterSpeed = std::max(context.configuration.sensor.maxShutterSpeed, + frameContext.agc.maxShutterSpeed); + setLimits(context.configuration.sensor.minShutterSpeed, + maxShutterSpeed, + context.configuration.sensor.minAnalogueGain, + context.configuration.sensor.maxAnalogueGain); + /* * The Agc algorithm needs to know the effective exposure value that was * applied to the sensor when the statistics were collected. diff --git a/src/ipa/rkisp1/algorithms/algorithm.h b/src/ipa/rkisp1/algorithms/algorithm.h index 9454c9a1f..c3a002b85 100644 --- a/src/ipa/rkisp1/algorithms/algorithm.h +++ b/src/ipa/rkisp1/algorithms/algorithm.h @@ -25,6 +25,8 @@ public: bool disabled_; bool supportsRaw_; + + ControlInfoMap::Map controls_; }; } /* namespace ipa::rkisp1 */ diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index 256b75ebc..26c395704 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -68,8 +68,10 @@ struct IPAActiveState { } automatic; bool autoEnabled; - uint32_t constraintMode; - uint32_t exposureMode; + int32_t constraintMode; + int32_t exposureMode; + int32_t meteringMode; + utils::Duration maxShutterSpeed; } agc; struct { @@ -111,6 +113,10 @@ struct IPAFrameContext : public FrameContext { uint32_t exposure; double gain; bool autoEnabled; + int32_t exposureMode; + int32_t constraintMode; + int32_t meteringMode; + utils::Duration maxShutterSpeed; } agc; struct { diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp index 963cdbab4..7716c11bb 100644 --- a/src/ipa/rkisp1/rkisp1.cpp +++ b/src/ipa/rkisp1/rkisp1.cpp @@ -80,6 +80,7 @@ private: std::map mappedBuffers_; ControlInfoMap sensorControls_; + ControlInfoMap::Map algoControls_; /* Interface to the Camera Helper */ std::unique_ptr camHelper_; @@ -193,6 +194,14 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision, if (ret) return ret; + for (auto const &a : algorithms()) { + Algorithm *algo = static_cast(a.get()); + + /* \todo Avoid merging duplicate controls */ + if (!algo->controls_.empty()) + algoControls_.merge(ControlInfoMap::Map(algo->controls_)); + } + /* Initialize controls. */ updateControls(sensorInfo, sensorControls, ipaControls); @@ -377,6 +386,7 @@ void IPARkISP1::updateControls(const IPACameraSensorInfo &sensorInfo, ControlInfoMap *ipaControls) { ControlInfoMap::Map ctrlMap = rkisp1Controls; + ctrlMap.merge(algoControls_); /* * Compute exposure time limits from the V4L2_CID_EXPOSURE control