From patchwork Fri Jul 3 15:38:12 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= X-Patchwork-Id: 27190 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 8D0DEC330A for ; Fri, 3 Jul 2026 15:38:43 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1C4F56600C; Fri, 3 Jul 2026 17:38:39 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="AMTG1rjI"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E08E565FDC for ; Fri, 3 Jul 2026 17:38:24 +0200 (CEST) Received: from pb-laptop.local (185.221.140.128.nat.pool.zt.hu [185.221.140.128]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C81DD1121 for ; Fri, 3 Jul 2026 17:37:38 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1783093058; bh=5O8ZU2Gcw9FKr8ilWmixdJ36NgReYWWAbkJRbpl3tIs=; h=From:To:Subject:Date:In-Reply-To:References:From; b=AMTG1rjIQV1FseWcBe1KGcVLAJaBxO48tJqCkKcE8h5mg30R9/mzZGEo/Jc591kux paddmhH1YmOcJIxuwkFKDgthlSNVxe4+lacxt7l7mrSP2PtCW8xWODh+kePwEbyUGP IrRzDqks6LJaewo6L0VzpVgV5keRLoeh1RqxHFPQ= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Subject: [RFC PATCH v1 10/17] ipa: libipa: Add `AgcMeanLuminance` wrapper Date: Fri, 3 Jul 2026 17:38:12 +0200 Message-ID: <20260703153819.1088752-11-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260703153819.1088752-1-barnabas.pocze@ideasonboard.com> References: <20260703153819.1088752-1-barnabas.pocze@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 an `AgcMeanLuminance` wrapper that can be used to easily implement the `Algorithm` interface in an IPA module. This implementation is a copy of the rkisp1 agc algorithm with minor adjustments. Signed-off-by: Barnabás Pőcze --- src/ipa/libipa/agc_mean_luminance.cpp | 599 ++++++++++++++++++++++++++ src/ipa/libipa/agc_mean_luminance.h | 96 +++++ 2 files changed, 695 insertions(+) diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index 1529d55864..951a4b0e02 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -8,8 +8,11 @@ #include "agc_mean_luminance.h" #include +#include #include +#include + #include #include @@ -738,6 +741,602 @@ AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex, * their configure() function. */ +/** + * \class AgcMeanLuminanceAlgorithm + * \brief AgcMeanLuminance wrapper for implementing the Algorithm interface + * + * \todo DigitalGain, DigitalGainMode + */ + +/** + * \struct AgcMeanLuminanceAlgorithm::Session + * \brief Session configuration for AgcMeanLuminanceAlgorithm + * + * \var AgcMeanLuminanceAlgorithm::Session::minExposureTime + * \brief Minimum exposure time supported with the configured sensor + * + * \var AgcMeanLuminanceAlgorithm::Session::maxExposureTime + * \brief Maximum exposure time supported with the configured sensor + * + * \var AgcMeanLuminanceAlgorithm::Session::minAnalogueGain + * \brief Minimum analogue gain supported with the configured sensor + * + * \var AgcMeanLuminanceAlgorithm::Session::maxAnalogueGain + * \brief Maximum analogue gain supported with the configured sensor + * + * \var AgcMeanLuminanceAlgorithm::Session::minFrameDuration + * \brief Minimum frame duration supported with the configured sensor + * + * \var AgcMeanLuminanceAlgorithm::Session::maxFrameDuration + * \brief Maximum frame duration supported with the configured sensor + * + * \var AgcMeanLuminanceAlgorithm::Session::lineDuration + * \brief Line duration with the configured sensor and output size + * + * \var AgcMeanLuminanceAlgorithm::Session::sensor + * \brief Details of the sensor configuration + * + * \var AgcMeanLuminanceAlgorithm::Session::sensor.outputSize + * \brief Configured output size of the sensor + * + * \var AgcMeanLuminanceAlgorithm::Session::autoAllowed + * \brief Whether automatic controls are allowed + */ + +/** + * \struct AgcMeanLuminanceAlgorithm::ActiveState + * \brief Active state for AgcMeanLuminanceAlgorithm + * + * The \a automatic variables track the latest values computed by algorithm + * based on the latest processed statistics. All other variables track the + * consolidated controls requested in queued requests. + * + * \var AgcMeanLuminanceAlgorithm::ActiveState::manual + * \brief Manual exposure time and analog gain (set through requests) + * + * \var AgcMeanLuminanceAlgorithm::ActiveState::manual.exposure + * \brief Manual exposure time expressed as a number of lines as set by the + * ExposureTime control + * + * \var AgcMeanLuminanceAlgorithm::ActiveState::manual.gain + * \brief Manual analogue gain as set by the AnalogueGain control + * + * \var AgcMeanLuminanceAlgorithm::ActiveState::automatic + * \brief Automatic exposure time and analog gain (computed by the algorithm) + * + * \var AgcMeanLuminanceAlgorithm::ActiveState::automatic.exposure + * \brief Automatic exposure time expressed as a number of lines + * + * \var AgcMeanLuminanceAlgorithm::ActiveState::automatic.gain + * \brief Automatic analogue gain multiplier + * + * \var AgcMeanLuminanceAlgorithm::ActiveState::automatic.quantizationGain + * \brief Automatic quantization gain multiplier + * + * \var AgcMeanLuminanceAlgorithm::ActiveState::automatic.yTarget + * \brief Automatically determined luminance target + * + * \var AgcMeanLuminanceAlgorithm::ActiveState::autoExposureEnabled + * \brief Manual/automatic AGC state (exposure) as set by the ExposureTimeMode control + * + * \var AgcMeanLuminanceAlgorithm::ActiveState::autoGainEnabled + * \brief Manual/automatic AGC state (gain) as set by the AnalogueGainMode control + * + * \var AgcMeanLuminanceAlgorithm::ActiveState::exposureValue + * \brief Exposure value as set by the ExposureValue control + * + * \var AgcMeanLuminanceAlgorithm::ActiveState::constraintMode + * \brief Constraint mode as set by the AeConstraintMode control + * + * \var AgcMeanLuminanceAlgorithm::ActiveState::exposureMode + * \brief Exposure mode as set by the AeExposureMode control + * + * \var AgcMeanLuminanceAlgorithm::ActiveState::minFrameDuration + * \brief Minimum frame duration as set by the FrameDurationLimits control + * + * \var AgcMeanLuminanceAlgorithm::ActiveState::maxFrameDuration + * \brief Maximum frame duration as set by the FrameDurationLimits control + */ + +/** + * \struct AgcMeanLuminanceAlgorithm::FrameContext + * \brief Per-frame context for AgcMeanLuminanceAlgorithm + * + * \var AgcMeanLuminanceAlgorithm::FrameContext::exposure + * \brief Exposure time expressed as a number of lines computed by the algorithm + * + * \var AgcMeanLuminanceAlgorithm::FrameContext::gain + * \brief Analogue gain multiplier computed by the algorithm + * + * The gain should be adapted to the sensor specific gain code before applying. + * + * \var AgcMeanLuminanceAlgorithm::FrameContext::quantizationGain + * \brief Quantization gain multiplier computed by the algorithm + * + * \var AgcMeanLuminanceAlgorithm::FrameContext::exposureValue + * \brief Exposure value as set by the ExposureValue control + * + * \var AgcMeanLuminanceAlgorithm::FrameContext::yTarget + * \brief Luminance target computed by the algorithm + * + * \var AgcMeanLuminanceAlgorithm::FrameContext::vblank + * \brief Vertical blanking parameter computed by the algorithm + * + * \var AgcMeanLuminanceAlgorithm::FrameContext::autoExposureEnabled + * \brief Manual/automatic AGC state (exposure) as set by the ExposureTimeMode control + * + * \var AgcMeanLuminanceAlgorithm::FrameContext::autoGainEnabled + * \brief Manual/automatic AGC state (gain) as set by the AnalogueGainMode control + * + * \var AgcMeanLuminanceAlgorithm::FrameContext::constraintMode + * \brief Constraint mode as set by the AeConstraintMode control + * + * \var AgcMeanLuminanceAlgorithm::FrameContext::exposureMode + * \brief Exposure mode as set by the AeExposureMode control + * + * \var AgcMeanLuminanceAlgorithm::FrameContext::minFrameDuration + * \brief Minimum frame duration as set by the FrameDurationLimits control + * + * \var AgcMeanLuminanceAlgorithm::FrameContext::maxFrameDuration + * \brief Maximum frame duration as set by the FrameDurationLimits control + * + * \var AgcMeanLuminanceAlgorithm::FrameContext::frameDuration + * \brief The actual FrameDuration used by the algorithm for the frame + * + * \var AgcMeanLuminanceAlgorithm::FrameContext::autoExposureModeChange + * \brief Indicate if autoExposureEnabled has changed from true in the previous + * frame to false in the current frame, and no manual exposure value has been + * supplied in the current frame. + * + * \var AgcMeanLuminanceAlgorithm::FrameContext::autoGainModeChange + * \brief Indicate if autoGainEnabled has changed from true in the previous + * frame to false in the current frame, and no manual gain value has been + * supplied in the current frame. + */ + +/** + * \struct AgcMeanLuminanceAlgorithm::ConfigurationParams + * \brief Parameters for AgcMeanLuminanceAlgorithm::configure() + * + * \var AgcMeanLuminanceAlgorithm::ConfigurationParams::sensor + * \brief CameraSensorHelper for the sensor + * + * \var AgcMeanLuminanceAlgorithm::ConfigurationParams::sensorInfo + * \brief Details of the sensor + * + * \var AgcMeanLuminanceAlgorithm::ConfigurationParams::sensorControls + * \brief ControlInfoMap of the sensor + * + * \var AgcMeanLuminanceAlgorithm::ConfigurationParams::ctrlMap + * \brief ControlMap to update with controls + * + * \var AgcMeanLuminanceAlgorithm::ConfigurationParams::autoAllowed + * \brief Whether to enable auto controls + */ + +/** + * \struct AgcMeanLuminanceAlgorithm::ProcessParams + * \brief Parameters for AgcMeanLuminanceAlgorithm::process() + * + * \var AgcMeanLuminanceAlgorithm::ProcessParams::traits + * \brief Implementation of AgcMeanLuminance::Traits + * + * \var AgcMeanLuminanceAlgorithm::ProcessParams::hist + * \brief Luminance histogram of the frame + * + * \var AgcMeanLuminanceAlgorithm::ProcessParams::exposure + * \brief Effective exposure of the frame + * + * \var AgcMeanLuminanceAlgorithm::ProcessParams::gain + * \brief Effective gain of the frame + * + * \var AgcMeanLuminanceAlgorithm::ProcessParams::additionalConstraints + * \brief Addition AgcMeanLuminance::AgcConstraints to apply + * + * \var AgcMeanLuminanceAlgorithm::ProcessParams::lux + * \brief Lux value for the frame + */ + +/** + * \brief Load tuning data + */ +int AgcMeanLuminanceAlgorithm::init(const ValueNode &tuningData) +{ + int ret = impl_.parseTuningData(tuningData); + if (ret) + return ret; + + return 0; +} + +/** + * \brief Initialize the session configuration and active state + */ +int AgcMeanLuminanceAlgorithm::configure(Session &session, ActiveState &state, const ConfigurationParams &config) +{ + session = {}; + session.lineDuration = config.sensorInfo.minLineLength * 1.0s + / config.sensorInfo.pixelRate; + session.sensor.outputSize = config.sensorInfo.outputSize; + session.autoAllowed = config.autoAllowed; + + const double lineDurationUs = session.lineDuration.get(); + + /* + * Compute exposure time limits from the V4L2_CID_EXPOSURE control + * limits and the line duration. + */ + + const ControlInfo &v4l2Exposure = config.sensorControls.find(V4L2_CID_EXPOSURE)->second; + int32_t minExposure = v4l2Exposure.min().get(); + int32_t maxExposure = v4l2Exposure.max().get(); + int32_t defExposure = v4l2Exposure.def().get(); + config.ctrlMap[&controls::ExposureTime] = ControlInfo{ + static_cast(minExposure * lineDurationUs), + static_cast(maxExposure * lineDurationUs), + static_cast(defExposure * lineDurationUs), + }; + + /* Compute the analogue gain limits. */ + const ControlInfo &v4l2Gain = config.sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second; + float minGain = config.sensor.gain(v4l2Gain.min().get()); + float maxGain = config.sensor.gain(v4l2Gain.max().get()); + float defGain = config.sensor.gain(v4l2Gain.def().get()); + config.ctrlMap[&controls::AnalogueGain] = ControlInfo{ + minGain, + maxGain, + defGain, + }; + + LOG(AgcMeanLuminance, Debug) + << "Exposure: [" << minExposure << ", " << maxExposure + << "], gain: [" << minGain << ", " << maxGain << "]"; + + /* + * Compute the frame duration limits. + * + * The frame length is computed assuming a fixed line length combined + * with the vertical frame sizes. + */ + const ControlInfo &v4l2HBlank = config.sensorControls.find(V4L2_CID_HBLANK)->second; + uint32_t hblank = v4l2HBlank.def().get(); + uint32_t lineLength = config.sensorInfo.outputSize.width + hblank; + + const ControlInfo &v4l2VBlank = config.sensorControls.find(V4L2_CID_VBLANK)->second; + std::array frameHeights{ + v4l2VBlank.min().get() + config.sensorInfo.outputSize.height, + v4l2VBlank.max().get() + config.sensorInfo.outputSize.height, + v4l2VBlank.def().get() + config.sensorInfo.outputSize.height, + }; + + std::array frameDurations; + for (unsigned int i = 0; i < frameHeights.size(); ++i) { + uint64_t frameSize = lineLength * frameHeights[i]; + frameDurations[i] = frameSize / (config.sensorInfo.pixelRate / 1000000U); + } + + config.ctrlMap[&controls::FrameDurationLimits] = ControlInfo{ + frameDurations[0], + frameDurations[1], + Span{ { frameDurations[2], frameDurations[2] } }, + }; + + session.minFrameDuration = std::chrono::microseconds(frameDurations[0]); + session.maxFrameDuration = std::chrono::microseconds(frameDurations[1]); + + /* + * When the AGC computes the new exposure values for a frame, it needs + * to know the limits for exposure time and analogue gain. As it depends + * on the sensor, update it with the controls. + * + * \todo take VBLANK into account for maximum exposure time + */ + session.minExposureTime = minExposure * session.lineDuration; + session.maxExposureTime = maxExposure * session.lineDuration; + session.minAnalogueGain = minGain; + session.maxAnalogueGain = maxGain; + + impl_.configure(session.lineDuration, &config.sensor); + impl_.setLimits(session.minExposureTime, session.maxExposureTime, + session.minAnalogueGain, session.maxAnalogueGain, + {}); + impl_.resetFrameCount(); + + /* Configure the default exposure and gain. */ + state = {}; + state.automatic.gain = session.minAnalogueGain; + state.automatic.exposure = 10ms / session.lineDuration; + state.automatic.quantizationGain = 1; + state.automatic.yTarget = impl_.effectiveYTarget(); + state.manual.gain = state.automatic.gain; + state.manual.exposure = state.automatic.exposure; + state.autoExposureEnabled = session.autoAllowed; + state.autoGainEnabled = session.autoAllowed; + state.exposureValue = 0; + + state.constraintMode = + static_cast(impl_.constraintModes().begin()->first); + state.exposureMode = + static_cast(impl_.exposureModeHelpers().begin()->first); + + state.minFrameDuration = session.minFrameDuration; + state.maxFrameDuration = session.maxFrameDuration; + + const auto add = [&](const ControlId &cid, const auto &automatic, const auto &manual) { + std::array values; + size_t count = 0; + + if (session.autoAllowed) + values[count++] = ControlValue(automatic); + + values[count++] = ControlValue(manual); + + config.ctrlMap[&cid] = ControlInfo{ + { values.data(), count }, + ControlValue(session.autoAllowed ? automatic : manual), + }; + }; + + add(controls::ExposureTimeMode, controls::ExposureTimeModeAuto, controls::ExposureTimeModeManual); + add(controls::AnalogueGainMode, controls::AnalogueGainModeAuto, controls::AnalogueGainModeManual); + + /* \todo Move this to the `Camera` class. */ + config.ctrlMap[&controls::AeEnable] = ControlInfo{ + false, + session.autoAllowed, + session.autoAllowed, + }; + + // \todo Should these be added/removed based on `session.autoAllowed` ? + config.ctrlMap[&controls::ExposureValue] = ControlInfo(-8.0f, 8.0f, 0.0f); + + for (const auto &[id, info] : impl_.controls()) + config.ctrlMap[id] = info; + + return 0; +} + +/** + * \brief Handle a \a queueRequest operation + */ +void AgcMeanLuminanceAlgorithm::queueRequest(const Session &session, ActiveState &state, + FrameContext &frameContext, const ControlList &controls) +{ + if (session.autoAllowed) { + const auto &aeEnable = controls.get(controls::ExposureTimeMode); + if (aeEnable && + (*aeEnable == controls::ExposureTimeModeAuto) != state.autoExposureEnabled) { + state.autoExposureEnabled = (*aeEnable == controls::ExposureTimeModeAuto); + + LOG(AgcMeanLuminance, Debug) + << (state.autoExposureEnabled ? "Enabling" : "Disabling") + << " AGC (exposure)"; + + /* + * If we go from auto -> manual with no manual control + * set, use the last computed value, which we don't + * know until prepare() so save this information. + * + * \todo Check the previous frame at prepare() time + * instead of saving a flag here + */ + if (!state.autoExposureEnabled && !controls.get(controls::ExposureTime)) + frameContext.autoExposureModeChange = true; + } + + const auto &agEnable = controls.get(controls::AnalogueGainMode); + if (agEnable && + (*agEnable == controls::AnalogueGainModeAuto) != state.autoGainEnabled) { + state.autoGainEnabled = (*agEnable == controls::AnalogueGainModeAuto); + + LOG(AgcMeanLuminance, Debug) + << (state.autoGainEnabled ? "Enabling" : "Disabling") + << " AGC (gain)"; + /* + * If we go from auto -> manual with no manual control + * set, use the last computed value, which we don't + * know until prepare() so save this information. + */ + if (!state.autoGainEnabled && !controls.get(controls::AnalogueGain)) + frameContext.autoGainModeChange = true; + } + } + + const auto &exposure = controls.get(controls::ExposureTime); + if (exposure && !state.autoExposureEnabled) { + state.manual.exposure = *exposure * 1.0us / session.lineDuration; + + LOG(AgcMeanLuminance, Debug) + << "Set exposure to " << state.manual.exposure; + } + + const auto &gain = controls.get(controls::AnalogueGain); + if (gain && !state.autoGainEnabled) { + state.manual.gain = *gain; + + LOG(AgcMeanLuminance, Debug) << "Set gain to " << state.manual.gain; + } + + frameContext.autoExposureEnabled = state.autoExposureEnabled; + frameContext.autoGainEnabled = state.autoGainEnabled; + + if (!frameContext.autoExposureEnabled) + frameContext.exposure = state.manual.exposure; + if (!frameContext.autoGainEnabled) + frameContext.gain = state.manual.gain; + + if (!frameContext.autoExposureEnabled && + !frameContext.autoGainEnabled) + frameContext.quantizationGain = 1.0; + + const auto &exposureMode = controls.get(controls::AeExposureMode); + if (exposureMode) + state.exposureMode = + static_cast(*exposureMode); + frameContext.exposureMode = state.exposureMode; + + const auto &constraintMode = controls.get(controls::AeConstraintMode); + if (constraintMode) + state.constraintMode = + static_cast(*constraintMode); + frameContext.constraintMode = state.constraintMode; + + const auto &exposureValue = controls.get(controls::ExposureValue); + if (exposureValue) + state.exposureValue = *exposureValue; + frameContext.exposureValue = state.exposureValue; + + const auto &frameDurationLimits = controls.get(controls::FrameDurationLimits); + if (frameDurationLimits) { + /* Limit the control value to the limits in ControlInfo */ + state.minFrameDuration = std::clamp( + std::chrono::microseconds((*frameDurationLimits).front()), + session.minFrameDuration, + session.maxFrameDuration + ); + + state.maxFrameDuration = std::clamp( + std::chrono::microseconds((*frameDurationLimits).back()), + session.minFrameDuration, + session.maxFrameDuration + ); + } + frameContext.minFrameDuration = state.minFrameDuration; + frameContext.maxFrameDuration = state.maxFrameDuration; +} + +/** + * \brief Handle a \a prepare operation + */ +void AgcMeanLuminanceAlgorithm::prepare(ActiveState &state, FrameContext &frameContext) +{ + uint32_t activeAutoExposure = state.automatic.exposure; + double activeAutoGain = state.automatic.gain; + double activeAutoQGain = state.automatic.quantizationGain; + + /* Populate exposure and gain in auto mode */ + if (frameContext.autoExposureEnabled) { + frameContext.exposure = activeAutoExposure; + frameContext.quantizationGain = activeAutoQGain; + } + if (frameContext.autoGainEnabled) { + frameContext.gain = activeAutoGain; + frameContext.quantizationGain = activeAutoQGain; + } + + /* + * Populate manual exposure and gain from the active auto values when + * transitioning from auto to manual + */ + if (!frameContext.autoExposureEnabled && frameContext.autoExposureModeChange) { + state.manual.exposure = activeAutoExposure; + frameContext.exposure = activeAutoExposure; + } + if (!frameContext.autoGainEnabled && frameContext.autoGainModeChange) { + state.manual.gain = activeAutoGain; + frameContext.gain = activeAutoGain; + frameContext.quantizationGain = activeAutoQGain; + } + + frameContext.yTarget = state.automatic.yTarget; +} + +/** + * \brief Handle a \a process operation + */ +void AgcMeanLuminanceAlgorithm::process(const Session &session, ActiveState &state, + FrameContext &frameContext, std::optional &¶ms, + ControlList &metadata) +{ + const utils::Duration &lineDuration = session.lineDuration; + utils::Duration newExposureTime = {}; + + if (params) { + ASSERT(session.autoAllowed); + + /* + * Set the AGC limits using the fixed exposure time and/or gain in + * manual mode, or the sensor limits in auto mode. + */ + utils::Duration minExposureTime; + utils::Duration maxExposureTime; + double minAnalogueGain; + double maxAnalogueGain; + + if (frameContext.autoExposureEnabled) { + minExposureTime = session.minExposureTime; + maxExposureTime = std::clamp(frameContext.maxFrameDuration, session.minExposureTime, session.maxExposureTime); + } else { + minExposureTime = lineDuration * frameContext.exposure; + maxExposureTime = minExposureTime; + } + + if (frameContext.autoGainEnabled) { + minAnalogueGain = session.minAnalogueGain; + maxAnalogueGain = session.maxAnalogueGain; + } else { + minAnalogueGain = frameContext.gain; + maxAnalogueGain = frameContext.gain; + } + + /* + * The Agc algorithm needs to know the effective exposure value that was + * applied to the sensor when the statistics were collected. + */ + utils::Duration effectiveExposureValue = + lineDuration * params->exposure * params->gain; + + impl_.setLimits(minExposureTime, maxExposureTime, + minAnalogueGain, maxAnalogueGain, + std::move(params->additionalConstraints)); + + impl_.setExposureCompensation(pow(2.0, frameContext.exposureValue)); + impl_.setLux(params->lux); + + double aGain, qGain, dGain; + std::tie(newExposureTime, aGain, qGain, dGain) = + impl_.calculateNewEv(frameContext.constraintMode, frameContext.exposureMode, + params->hist, effectiveExposureValue, params->traits); + + LOG(AgcMeanLuminance, Debug) + << "Divided up exposure time, analogue gain, quantization gain" + << " and digital gain are " << newExposureTime << ", " << aGain + << ", " << qGain << " and " << dGain; + + /* Update the estimated exposure and gain. */ + state.automatic.exposure = newExposureTime / lineDuration; + state.automatic.gain = aGain; + state.automatic.quantizationGain = qGain; + state.automatic.yTarget = impl_.effectiveYTarget(); + } + + /* + * Expand the target frame duration so that we do not run faster than + * the minimum frame duration when we have short exposures. + */ + const auto frameDuration = std::max(frameContext.minFrameDuration, newExposureTime); + frameContext.vblank = (frameDuration / lineDuration) - session.sensor.outputSize.height; + + /* Update frame duration accounting for line length quantization. */ + frameContext.frameDuration = (session.sensor.outputSize.height + frameContext.vblank) * lineDuration; + + metadata.set(controls::AnalogueGain, frameContext.gain); + metadata.set(controls::ExposureTime, utils::Duration(lineDuration * frameContext.exposure).get()); + metadata.set(controls::FrameDuration, frameContext.frameDuration.get()); + metadata.set(controls::ExposureTimeMode, + frameContext.autoExposureEnabled + ? controls::ExposureTimeModeAuto + : controls::ExposureTimeModeManual); + metadata.set(controls::AnalogueGainMode, + frameContext.autoGainEnabled + ? controls::AnalogueGainModeAuto + : controls::AnalogueGainModeManual); + + metadata.set(controls::AeExposureMode, frameContext.exposureMode); + metadata.set(controls::AeConstraintMode, frameContext.constraintMode); + metadata.set(controls::ExposureValue, frameContext.exposureValue); +} + } /* namespace ipa */ } /* namespace libcamera */ diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index f4e1680ab5..3378ec7faa 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -9,13 +9,17 @@ #include #include +#include #include #include #include +#include #include +#include + #include "libcamera/internal/value_node.h" #include "exposure_mode_helper.h" @@ -115,6 +119,98 @@ private: ControlInfoMap::Map controls_; }; +class AgcMeanLuminanceAlgorithm +{ +public: + struct Session { + utils::Duration minExposureTime; + utils::Duration maxExposureTime; + double minAnalogueGain; + double maxAnalogueGain; + utils::Duration minFrameDuration; + utils::Duration maxFrameDuration; + + utils::Duration lineDuration; + + struct { + Size outputSize; + } sensor; + + bool autoAllowed; + }; + + struct ActiveState { + struct { + uint32_t exposure; + double gain; + } manual; + struct { + uint32_t exposure; + double gain; + double quantizationGain; + double yTarget; + } automatic; + + bool autoExposureEnabled; + bool autoGainEnabled; + double exposureValue; + controls::AeConstraintModeEnum constraintMode; + controls::AeExposureModeEnum exposureMode; + utils::Duration minFrameDuration; + utils::Duration maxFrameDuration; + }; + + struct FrameContext { + uint32_t exposure; + double gain; + double quantizationGain; + double exposureValue; + double yTarget; + uint32_t vblank; + bool autoExposureEnabled; + bool autoGainEnabled; + controls::AeConstraintModeEnum constraintMode; + controls::AeExposureModeEnum exposureMode; + utils::Duration minFrameDuration; + utils::Duration maxFrameDuration; + utils::Duration frameDuration; + bool autoExposureModeChange; + bool autoGainModeChange; + }; + + struct ConfigurationParams { + const CameraSensorHelper &sensor; + const IPACameraSensorInfo &sensorInfo; + const ControlInfoMap &sensorControls; + ControlInfoMap::Map &ctrlMap; + bool autoAllowed = true; + }; + + int init(const ValueNode &tuningData); + + int configure(Session &session, ActiveState &state, const ConfigurationParams &config); + + void queueRequest(const Session &session, ActiveState &state, + FrameContext &frameContext, const ControlList &controls); + + void prepare(ActiveState &state, FrameContext &frameContext); + + struct ProcessParams { + const AgcMeanLuminance::Traits &traits; + const Histogram &hist; + uint32_t exposure; + double gain; + std::vector &&additionalConstraints = {}; + unsigned int lux = 0; + }; + + void process(const Session &session, ActiveState &state, FrameContext &frameContext, + std::optional &¶ms, ControlList &metadata); + +private: + AgcMeanLuminance impl_; +}; + } /* namespace ipa */ } /* namespace libcamera */