From patchwork Fri Apr 5 14:47:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 19851 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 6D6C6C327C for ; Fri, 5 Apr 2024 14:47:49 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B5E5F61C31; Fri, 5 Apr 2024 16:47:47 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="tB4/bgeY"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BDD1E61C2F for ; Fri, 5 Apr 2024 16:47:46 +0200 (CEST) Received: from pyrite.hamster-moth.ts.net (h175-177-049-156.catv02.itscom.jp [175.177.49.156]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 0092C63B; Fri, 5 Apr 2024 16:47:06 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1712328428; bh=uEiZ9XUT64hX3kGNRz2krIRJMyJCutWL38psNEuR+Yo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=tB4/bgeYp3fMkA5kxax9Haqv7hTZym2K6VqSNtvajcOORI5aUJeBraze+NLI/lutz 8J+m1hZUvBY8bLKsTgXD36tfpN7q+ZtnZrHaS6cMv+wOpm2vpSfwb6zHmqzCSbuJsX gMNdU7Fpd6uJcjAHoWcGgqRuJZVFSy7MZxaLaswU= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder Subject: [PATCH 1/5] ipa: rkisp1: agc: Read histogram weights from tuning file Date: Fri, 5 Apr 2024 23:47:25 +0900 Message-Id: <20240405144729.2992219-2-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240405144729.2992219-1-paul.elder@ideasonboard.com> References: <20240405144729.2992219-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 --- src/ipa/rkisp1/algorithms/agc.cpp | 80 +++++++++++++++++++++++++++++-- src/ipa/rkisp1/algorithms/agc.h | 6 +++ 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 1cd977a0..fd47ba4e 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" /** @@ -42,6 +44,62 @@ static constexpr double kMinAnalogueGain = 1.0; /* \todo Honour the FrameDurationLimits control instead of hardcoding a limit */ static constexpr utils::Duration kMaxShutterSpeed = 60ms; +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; + } + + std::vector weights = + value.getList().value_or(std::vector{}); + if (weights.size() != context.hw->numHistogramWeights) { + LOG(RkISP1Agc, Error) + << "Invalid '" << key << "' values: expected " + << context.hw->numHistogramWeights + << " elements, got " << weights.size(); + return -ENODATA; + } + + meteringModes_[controls::AeMeteringModeNameValueMap.at(key)] = weights; + } + + return 0; +} + +uint8_t Agc::predivider(Size &size) +{ + static const std::vector> + pixelCountThresholds = { + { 640 * 480, 3 }, + { 800 * 600, 4 }, + { 1024 * 768, 5 }, + { 1280 * 960, 6 }, + { 1600 * 1200, 8 }, + { 2048 * 1536, 10 }, + { 2600 * 1950, 12 }, + { 4096 * 3072, 16 }, + }; + + unsigned long pixels = size.width * size.height; + for (auto &pair : pixelCountThresholds) + if (pixels < pair.first) + return pair.second; + + return 24; +} + Agc::Agc() { supportsRaw_ = true; @@ -72,6 +130,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; @@ -177,6 +239,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; @@ -195,14 +258,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 311d4e94..43e3d5b2 100644 --- a/src/ipa/rkisp1/algorithms/agc.h +++ b/src/ipa/rkisp1/algorithms/agc.h @@ -44,6 +44,10 @@ 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); void parseStatistics(const rkisp1_stat_buffer *stats, @@ -52,6 +56,8 @@ private: Histogram hist_; Span expMeans_; + + std::map> meteringModes_; }; } /* namespace ipa::rkisp1::algorithms */ From patchwork Fri Apr 5 14:47:26 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 19852 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 DA8A5C32C9 for ; Fri, 5 Apr 2024 14:47:52 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4780063352; Fri, 5 Apr 2024 16:47:52 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="mutSa9rX"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 98D8261C2F for ; Fri, 5 Apr 2024 16:47:48 +0200 (CEST) Received: from pyrite.hamster-moth.ts.net (h175-177-049-156.catv02.itscom.jp [175.177.49.156]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id DA05D63B; Fri, 5 Apr 2024 16:47:08 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1712328430; bh=VWM+ow7U8AHZqhwKyG/s1eLJfjCpgQ/SKy+k21i1blU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mutSa9rX38yBc8Jkh7UVV5w7ltA2cwL0R5Ys6CPMu+1PfM+2rSSYpmozqh/pspjeg ewWv2Mo5Ec0VlwmBlvYH6EffzSgSJINbNMqCGMDuatEVuHDAmXJ20O4TnH1dG/9RIX G9a/9EvzYAcRtjvmWjueRX4rAqJuwfH4w3mWitNU= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder Subject: [PATCH 2/5] ipa: rkisp1: agc: Add digital gain Date: Fri, 5 Apr 2024 23:47:26 +0900 Message-Id: <20240405144729.2992219-3-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240405144729.2992219-1-paul.elder@ideasonboard.com> References: <20240405144729.2992219-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 set digital gain. Signed-off-by: Paul Elder Reviewed-by: Umang Jain --- src/ipa/rkisp1/algorithms/agc.cpp | 5 +++++ src/ipa/rkisp1/ipa_context.h | 3 +++ src/ipa/rkisp1/rkisp1.cpp | 2 ++ 3 files changed, 10 insertions(+) diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index fd47ba4e..7220f00a 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -152,8 +153,10 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) context.activeState.agc.automatic.gain = context.configuration.sensor.minAnalogueGain; context.activeState.agc.automatic.exposure = 10ms / context.configuration.sensor.lineDuration; + context.activeState.agc.automatic.dgain = 1; context.activeState.agc.manual.gain = context.activeState.agc.automatic.gain; context.activeState.agc.manual.exposure = context.activeState.agc.automatic.exposure; + context.activeState.agc.manual.dgain = 1; context.activeState.agc.autoEnabled = !context.configuration.raw; /* @@ -237,6 +240,7 @@ void Agc::prepare(IPAContext &context, const uint32_t frame, if (frameContext.agc.autoEnabled) { frameContext.agc.exposure = context.activeState.agc.automatic.exposure; frameContext.agc.gain = context.activeState.agc.automatic.gain; + frameContext.agc.dgain = context.activeState.agc.automatic.dgain; } /* \todo Remove this when we can set the below with controls */ @@ -380,6 +384,7 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, /* Update the estimated exposure and gain. */ activeState.agc.automatic.exposure = shutterTime / context.configuration.sensor.lineDuration; activeState.agc.automatic.gain = aGain; + activeState.agc.automatic.dgain = dGain; fillMetadata(context, frameContext, metadata); } diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index 256b75eb..a70c7eb3 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -61,10 +61,12 @@ struct IPAActiveState { struct { uint32_t exposure; double gain; + double dgain; } manual; struct { uint32_t exposure; double gain; + double dgain; } automatic; bool autoEnabled; @@ -110,6 +112,7 @@ struct IPAFrameContext : public FrameContext { struct { uint32_t exposure; double gain; + double dgain; bool autoEnabled; } agc; diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp index b0bbcd8c..d66dfdd7 100644 --- a/src/ipa/rkisp1/rkisp1.cpp +++ b/src/ipa/rkisp1/rkisp1.cpp @@ -446,10 +446,12 @@ void IPARkISP1::setControls(unsigned int frame) IPAFrameContext &frameContext = context_.frameContexts.get(frame); uint32_t exposure = frameContext.agc.exposure; uint32_t gain = camHelper_->gainCode(frameContext.agc.gain); + uint32_t dgain = camHelper_->gainCode(frameContext.agc.dgain); ControlList ctrls(sensorControls_); ctrls.set(V4L2_CID_EXPOSURE, static_cast(exposure)); ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast(gain)); + ctrls.set(V4L2_CID_DIGITAL_GAIN, static_cast(dgain)); setSensorControls.emit(frame, ctrls); } From patchwork Fri Apr 5 14:47:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 19853 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 9BE92C32CA for ; Fri, 5 Apr 2024 14:47:53 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DA34263340; Fri, 5 Apr 2024 16:47:52 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="o4ueVyPc"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A816761C2F for ; Fri, 5 Apr 2024 16:47:50 +0200 (CEST) Received: from pyrite.hamster-moth.ts.net (h175-177-049-156.catv02.itscom.jp [175.177.49.156]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id CC44BC8A; Fri, 5 Apr 2024 16:47:10 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1712328432; bh=pTkClS0JmEycEN34D5sgrJilJ2D7GpzSxDz1Nu0sgIA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=o4ueVyPcQVxGipSfsfCE7zHnpf2Fr0YxitqfpJ5MAZ43GkYaYMXXEDQQfWt2pDToU TAxa69JOBHQB5O/+Or5cH6UugjwOLOy4HMcfxlPF0xzCJaJ5IYYVjBP5auCDTVIm1S xu65AWJ2ImNfjJrtWAthV/nsWHdRpLrgkVFCrdBg= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder Subject: [PATCH 3/5] fixup: ipa: rkisp1: Remove bespoke Agc functions Date: Fri, 5 Apr 2024 23:47:27 +0900 Message-Id: <20240405144729.2992219-4-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240405144729.2992219-1-paul.elder@ideasonboard.com> References: <20240405144729.2992219-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" Remove some constants that should have been removed, and restore a fairly informative comment that got removed. Although the removal of one of the constants also removes a todo comment, this will be addressed imminently so it should be fine. Signed-off-by: Paul Elder --- src/ipa/rkisp1/algorithms/agc.cpp | 32 +++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 7220f00a..1dfc4aaa 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -39,12 +39,6 @@ namespace ipa::rkisp1::algorithms { LOG_DEFINE_CATEGORY(RkISP1Agc) -/* Minimum limit for analogue gain value */ -static constexpr double kMinAnalogueGain = 1.0; - -/* \todo Honour the FrameDurationLimits control instead of hardcoding a limit */ -static constexpr utils::Duration kMaxShutterSpeed = 60ms; - int Agc::parseMeteringModes(IPAContext &context, const YamlObject &tuningData, const char *prop) { @@ -315,6 +309,32 @@ void Agc::parseStatistics(const rkisp1_stat_buffer *stats, context.hw->numHistogramBins), 4); } +/** + * \brief Estimate the relative luminance of the frame with a given gain + * \param[in] expMeans The mean luminance values, from the RkISP1 statistics + * \param[in] gain The gain to apply to the frame + * + * This function estimates the average relative luminance of the frame that + * would be output by the sensor if an additional \a gain was applied. + * + * The estimation is based on the AE statistics for the current frame. Y + * averages for all cells are first multiplied by the gain, and then saturated + * to approximate the sensor behaviour at high brightness values. The + * approximation is quite rough, as it doesn't take into account non-linearities + * when approaching saturation. In this case, saturating after the conversion to + * YUV doesn't take into account the fact that the R, G and B components + * contribute differently to the relative luminance. + * + * \todo Have a dedicated YUV algorithm ? + * + * The values are normalized to the [0.0, 1.0] range, where 1.0 corresponds to a + * theoretical perfect reflector of 100% reference white. + * + * More detailed information can be found in: + * https://en.wikipedia.org/wiki/Relative_luminance + * + * \return The relative luminance + */ double Agc::estimateLuminance(double gain) { double ySum = 0.0; From patchwork Fri Apr 5 14:47:28 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 19854 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 1240CC32CB for ; Fri, 5 Apr 2024 14:47:56 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 82CEF63361; Fri, 5 Apr 2024 16:47:55 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="oIEVdEaW"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id AAD4E63361 for ; Fri, 5 Apr 2024 16:47:52 +0200 (CEST) Received: from pyrite.hamster-moth.ts.net (h175-177-049-156.catv02.itscom.jp [175.177.49.156]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id BEA6363B; Fri, 5 Apr 2024 16:47:12 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1712328434; bh=d6IIQmH1l7yyHbcCWvhNL7wu4MUBA+yNtu0ss2BZ+4E=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=oIEVdEaWn0zSJaucA+swLHoEyNjT9kb9trQeuwzyuk8fqZ9+cb6iMRJmTuiyb9KJ/ IzOeo1D2TW9pLaLbwDKqCalArvZ5JBFL3LpbdTELqhObXY5tjXnLrA4GbZFsKCjwvf 0XZGEgPBuFGJi0CtdG2jvwC0nh5N+hTVDCmvbKZc= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder Subject: [PATCH 4/5] ipa: libipa: agc: Change luminance target to piecewise linear function Date: Fri, 5 Apr 2024 23:47:28 +0900 Message-Id: <20240405144729.2992219-5-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240405144729.2992219-1-paul.elder@ideasonboard.com> References: <20240405144729.2992219-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 valur to a piecewise linear function that needs to be sampled by the estimate lux value. Also chagne the rkisp1 and ipu3 IPAs according, 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 --- src/ipa/ipu3/algorithms/agc.cpp | 5 ++++- src/ipa/libipa/agc.cpp | 22 +++++++++++++++------- src/ipa/libipa/agc.h | 7 ++++--- src/ipa/rkisp1/algorithms/agc.cpp | 5 ++++- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp index 08deff0c..8e07c89e 100644 --- a/src/ipa/ipu3/algorithms/agc.cpp +++ b/src/ipa/ipu3/algorithms/agc.cpp @@ -228,12 +228,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.cpp b/src/ipa/libipa/agc.cpp index af57a571..bcb036e6 100644 --- a/src/ipa/libipa/agc.cpp +++ b/src/ipa/libipa/agc.cpp @@ -110,7 +110,7 @@ static constexpr double kDefaultRelativeLuminanceTarget = 0.16; */ MeanLuminanceAgc::MeanLuminanceAgc() - : frameCount_(0), filteredExposure_(0s), relativeLuminanceTarget_(0) + : frameCount_(0), filteredExposure_(0s) { } @@ -120,8 +120,12 @@ MeanLuminanceAgc::MeanLuminanceAgc() */ void MeanLuminanceAgc::parseRelativeLuminanceTarget(const YamlObject &tuningData) { - relativeLuminanceTarget_ = - tuningData["relativeLuminanceTarget"].get(kDefaultRelativeLuminanceTarget); + int ret = relativeLuminanceTarget_.readYaml(tuningData["relativeLuminanceTarget"]); + if (ret == 0) + return; + + std::vector points = { { 0, kDefaultRelativeLuminanceTarget } }; + relativeLuminanceTarget_ = Pwl(points); } /** @@ -378,6 +382,7 @@ int MeanLuminanceAgc::parseExposureModes(const YamlObject &tuningData) /** * \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 @@ -385,9 +390,10 @@ int MeanLuminanceAgc::parseExposureModes(const YamlObject &tuningData) * * \return The calculated initial gain */ -double MeanLuminanceAgc::estimateInitialGain() +double MeanLuminanceAgc::estimateInitialGain(double lux) { - double yTarget = relativeLuminanceTarget_; + double yTarget = + relativeLuminanceTarget_.eval(relativeLuminanceTarget_.domain().clamp(lux)); double yGain = 1.0; for (unsigned int i = 0; i < 8; i++) { @@ -476,6 +482,7 @@ utils::Duration MeanLuminanceAgc::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 @@ -487,7 +494,8 @@ std::tuple MeanLuminanceAgc::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 @@ -496,7 +504,7 @@ MeanLuminanceAgc::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.h b/src/ipa/libipa/agc.h index 902a359a..f187dbc8 100644 --- a/src/ipa/libipa/agc.h +++ b/src/ipa/libipa/agc.h @@ -16,6 +16,7 @@ #include "exposure_mode_helper.h" #include "histogram.h" +#include "pwl.h" namespace libcamera { @@ -59,18 +60,18 @@ public: } virtual double estimateLuminance(const double gain) = 0; - double estimateInitialGain(); + double estimateInitialGain(double lux); double constraintClampGain(uint32_t constraintModeIndex, const Histogram &hist, double gain); utils::Duration filterExposure(utils::Duration exposureValue); std::tuple calculateNewEv(uint32_t constraintModeIndex, uint32_t exposureModeIndex, - const Histogram &yHist, utils::Duration effectiveExposureValue); + const Histogram &yHist, utils::Duration effectiveExposureValue, double lux); 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 1dfc4aaa..a1b6eb39 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -389,12 +389,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(RkISP1Agc, Debug) << "Divided up shutter, analogue gain and digital gain are " From patchwork Fri Apr 5 14:47:29 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 19855 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 BA31DC32CC for ; Fri, 5 Apr 2024 14:47:56 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 455D963365; Fri, 5 Apr 2024 16:47:56 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ICWwGSSC"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 579126335E for ; Fri, 5 Apr 2024 16:47:54 +0200 (CEST) Received: from pyrite.hamster-moth.ts.net (h175-177-049-156.catv02.itscom.jp [175.177.49.156]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 9878F8E1; Fri, 5 Apr 2024 16:47:14 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1712328435; bh=UkIMdnPquCi1Z1Jv8wJhrkji5gwobMzv8tKEA1PGHdM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ICWwGSSCIWHY9LMOVC4IkDb8+dWjhSplx2KNk1BUrR/dmqy436Ezge97LnCbSMeQK gI/ZN39xKIu/a5XyH4AeKJF6E1zy0nSV/OgH/0CYwYjnj+FU+ygRU4ZT+C/u4JpEil rhDf1okULx6+6Y2WfLveDwWF9Es7BC/ILLeh7H4M= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder Subject: [PATCH 5/5] ipa: rkisp1: agc: Plumb mode-selection and frame duration controls Date: Fri, 5 Apr 2024 23:47:29 +0900 Message-Id: <20240405144729.2992219-6-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240405144729.2992219-1-paul.elder@ideasonboard.com> References: <20240405144729.2992219-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. Signed-off-by: Paul Elder --- src/ipa/rkisp1/algorithms/agc.cpp | 67 ++++++++++++++++++++------- src/ipa/rkisp1/algorithms/agc.h | 5 ++ src/ipa/rkisp1/algorithms/algorithm.h | 2 + src/ipa/rkisp1/ipa_context.h | 4 ++ src/ipa/rkisp1/rkisp1.cpp | 10 ++++ 5 files changed, 72 insertions(+), 16 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index a1b6eb39..ed4d6330 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -49,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()) { @@ -67,9 +69,14 @@ int Agc::parseMeteringModes(IPAContext &context, const YamlObject &tuningData, return -ENODATA; } - meteringModes_[controls::AeMeteringModeNameValueMap.at(key)] = weights; + int32_t control = controls::AeMeteringModeNameValueMap.at(key); + meteringModes_[control] = weights; + availableMeteringModes.push_back(control); } + Algorithm::controls_[&controls::AeMeteringMode] = + ControlInfo(availableMeteringModes); + return 0; } @@ -131,9 +138,25 @@ int Agc::init(IPAContext &context, const YamlObject &tuningData) context.ctrlMap.merge(controls()); + defaultConstraintMode_ = constraintModes().begin()->first; + defaultExposureMode_ = exposureModeHelpers().begin()->first; + defaultMeteringMode_ = meteringModes_.begin()->first; + + Algorithm::controls_.merge(ControlInfoMap::Map(controls())); + return 0; } +void Agc::configureExposureModeHelpers(IPAContext &context, utils::Duration maxShutterSpeed) +{ + for (auto &[id, helper] : exposureModeHelpers()) { + helper->configure(context.configuration.sensor.minShutterSpeed, + maxShutterSpeed, + context.configuration.sensor.minAnalogueGain, + context.configuration.sensor.maxAnalogueGain); + } +} + /** * \brief Configure the AGC given a configInfo * \param[in] context The shared IPA context @@ -153,12 +176,8 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) context.activeState.agc.manual.dgain = 1; context.activeState.agc.autoEnabled = !context.configuration.raw; - /* - * \todo We should use the first available mode rather than assume that - * the "Normal" modes are present in tuning data. - */ - context.activeState.agc.constraintMode = controls::ConstraintNormal; - context.activeState.agc.exposureMode = controls::ExposureNormal; + context.activeState.agc.constraintMode = defaultConstraintMode_; + context.activeState.agc.exposureMode = defaultExposureMode_; /* * Define the measurement window for AGC as a centered rectangle @@ -169,13 +188,7 @@ 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; - for (auto &[id, helper] : exposureModeHelpers()) { - /* \todo Run this again when FrameDurationLimits is passed in */ - helper->configure(context.configuration.sensor.minShutterSpeed, - context.configuration.sensor.maxShutterSpeed, - context.configuration.sensor.minAnalogueGain, - context.configuration.sensor.maxAnalogueGain); - } + configureExposureModeHelpers(context, context.configuration.sensor.maxShutterSpeed); return 0; } @@ -223,6 +236,20 @@ void Agc::queueRequest(IPAContext &context, frameContext.agc.exposure = agc.manual.exposure; frameContext.agc.gain = agc.manual.gain; } + + const auto &meteringMode = controls.get(controls::AeMeteringMode); + frameContext.agc.meteringMode = meteringMode.value_or(defaultMeteringMode_); + + const auto &exposureMode = controls.get(controls::AeExposureMode); + frameContext.agc.exposureMode = exposureMode.value_or(defaultExposureMode_); + + const auto &constraintMode = controls.get(controls::AeConstraintMode); + frameContext.agc.constraintMode = constraintMode.value_or(defaultConstraintMode_); + + const auto &frameDurationLimits = controls.get(controls::FrameDurationLimits); + frameContext.agc.maxShutterSpeed = frameDurationLimits + ? std::chrono::milliseconds((*frameDurationLimits).back()) + : 60ms; } /** @@ -262,8 +289,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; @@ -290,6 +316,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 @@ -297,6 +324,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); } void Agc::parseStatistics(const rkisp1_stat_buffer *stats, @@ -378,6 +409,10 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, ASSERT(stats->meas_type & RKISP1_CIF_ISP_STAT_AUTOEXP); + utils::Duration maxShutterSpeed = std::min(context.configuration.sensor.maxShutterSpeed, + frameContext.agc.maxShutterSpeed); + configureExposureModeHelpers(context, maxShutterSpeed); + parseStatistics(stats, context); /* diff --git a/src/ipa/rkisp1/algorithms/agc.h b/src/ipa/rkisp1/algorithms/agc.h index 43e3d5b2..c05ba4be 100644 --- a/src/ipa/rkisp1/algorithms/agc.h +++ b/src/ipa/rkisp1/algorithms/agc.h @@ -47,6 +47,7 @@ private: int parseMeteringModes(IPAContext &context, const YamlObject &tuningData, const char *prop); uint8_t predivider(Size &size); + void configureExposureModeHelpers(IPAContext &context, utils::Duration maxShutterSpeed); void fillMetadata(IPAContext &context, IPAFrameContext &frameContext, ControlList &metadata); @@ -58,6 +59,10 @@ private: Span expMeans_; std::map> meteringModes_; + + int32_t defaultConstraintMode_; + int32_t defaultExposureMode_; + int32_t defaultMeteringMode_; }; } /* namespace ipa::rkisp1::algorithms */ diff --git a/src/ipa/rkisp1/algorithms/algorithm.h b/src/ipa/rkisp1/algorithms/algorithm.h index 9454c9a1..c3a002b8 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 a70c7eb3..dc876da0 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -114,6 +114,10 @@ struct IPAFrameContext : public FrameContext { double gain; double dgain; bool autoEnabled; + int32_t meteringMode; + int32_t exposureMode; + int32_t constraintMode; + utils::Duration maxShutterSpeed; } agc; struct { diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp index d66dfdd7..3654b5a6 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