計思路解析)
目錄一、概述二、頭文件 exposure_mode_helper.h三、實現(xiàn)文件 exposure_mode_helper.cpp 逐段解析四、算法完整流程文字版五、代碼實現(xiàn)一、概述ExposureModeHelper屬于 libcamera IPA 模塊是給 AEGC自動曝光自動增益控制使用的曝光拆分工具類。核心職責(zé)給定目標總曝光量將其拆分為硬件曝光時間、硬件模擬增益、量化補償增益、ISP 數(shù)字增益四部分輸出。普通 AE 策略通常先把曝光時間拉到最大再增加增益。 該類支持多階段 (stage) 策略每個階段配置一對(階段最大曝光時間階段總增益上限)。 業(yè)務(wù)價值可以主動限制曝光時長避免運動場景拖影、保障幀率提前啟用增益當(dāng)傳入空 stages自動回退傳統(tǒng)「優(yōu)先曝光時間后增益」策略。重要定義stage 中的 gain 是邏輯總增益上限模擬增益 數(shù)字增益合計不是直接的模擬增益會被硬件模擬增益上限maxGain_鉗位超出部分落到數(shù)字增益。quantizationGain硬件寄存器量化帶來的補償系數(shù)不是硬件可配置參數(shù)是軟件算法補償因子和數(shù)字增益相互獨立要同時應(yīng)用兩者才能精準復(fù)現(xiàn)目標曝光。lineLength入?yún)㈩愋?DurationlineDuration_成員sensor 一行的時間行周期曝光時間必須是該值整數(shù)倍不是像素計數(shù)。二、頭文件 exposure_mode_helper.h#pragma once #include tuple #include utility #include vector #include libcamera/base/span.h #include libcamera/base/utils.h #include camera_sensor_helper.h namespace libcamera { namespace ipa { class ExposureModeHelper { public: /// \param stages 階段數(shù)組每一項(階段最大曝光時間階段總增益上限) ExposureModeHelper(const Spanstd::pairutils::Duration, double stages); ~ExposureModeHelper() default; /// 配置sensor行周期與sensor輔助對象用于硬件量化 /// \param lineLength sensor行周期時間維度 void configure(utils::Duration lineLength, const CameraSensorHelper *sensorHelper); /// 設(shè)置硬件運行時約束**每次硬件限制變化必須調(diào)用splitExposure調(diào)用前必須執(zhí)行** void setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime, double minGain, double maxGain); /// 核心接口輸入目標總曝光量返回四元組 /// 返回(實際下發(fā)sensor曝光時間模擬增益量化補償增益數(shù)字增益) std::tupleutils::Duration, double, double, double splitExposure(utils::Duration exposure) const; // 獲取已經(jīng)配置的硬件限制 utils::Duration minExposureTime() const { return minExposureTime_; } utils::Duration maxExposureTime() const { return maxExposureTime_; } double minGain() const { return minGain_; } double maxGain() const { return maxGain_; } private: /// 私有工具曝光時間鉗位 對齊行周期量化 utils::Duration clampExposureTime(utils::Duration exposureTime, double *quantizationGain nullptr) const; /// 私有工具增益鉗位 sensor增益檔位量化 double clampGain(double gain, double *quantizationGain nullptr) const; std::vectorutils::Duration exposureTimes_; /// 各階段的最大曝光時間 std::vectordouble gains_; /// 各階段邏輯總增益上限 utils::Duration lineDuration_; /// sensor行周期來自configure utils::Duration minExposureTime_; /// 硬件最小曝光 utils::Duration maxExposureTime_; /// 硬件最大曝光 double minGain_; /// 硬件最小模擬增益 double maxGain_; /// 硬件最大模擬增益 const CameraSensorHelper *sensorHelper_; /// sensor輔助對象外部保證生命周期有效 }; } /* namespace ipa */ } /* namespace libcamera */三、實現(xiàn)文件 exposure_mode_helper.cpp 逐段解析構(gòu)造函數(shù)ExposureModeHelper::ExposureModeHelper(const Spanstd::pairutils::Duration, double stages) : lineDuration_(1us), minExposureTime_(0us), maxExposureTime_(0us), minGain_(0), maxGain_(0), sensorHelper_(nullptr) { for (const auto [s, g] : stages) { exposureTimes_.push_back(s); gains_.push_back(g); } }將傳入的 stage 數(shù)組拆分為兩個并行 vector 保存。注意此時不做硬件鉗位鉗位、量化發(fā)生在splitExposure運行時。configure()void ExposureModeHelper::configure(utils::Duration lineDuration, const CameraSensorHelper *sensorHelper) { lineDuration_ lineDuration; sensorHelper_ sensorHelper; }設(shè)置 sensor 行周期、sensorHelper 裸指針。若不調(diào)用默認曝光單位為微秒增益不做硬件量化。??只保存指針不管理對象生命周期調(diào)用方必須保證 sensorHelper 在使用期間有效。setLimits()void ExposureModeHelper::setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime, double minGain, double maxGain) { minExposureTime_ minExposureTime; maxExposureTime_ maxExposureTime; minGain_ minGain; maxGain_ maxGain; }設(shè)置硬件運行邊界。固定曝光minExposureTime maxExposureTime固定模擬增益minGain maxGain未調(diào)用該函數(shù)時maxExposureTime_/maxGain_為 0調(diào)用splitExposure會觸發(fā)ASSERT崩潰。clampExposureTime 曝光時間鉗位與量化utils::Duration ExposureModeHelper::clampExposureTime(utils::Duration exposureTime, double *quantizationGain) const { utils::Duration clamped; utils::Duration exp; // 第一步鉗位到硬件最大最小曝光 clamped std::clamp(exposureTime, minExposureTime_, maxExposureTime_); // 第二步對齊行周期時長除以行周期取long向零截斷正數(shù)等價向下取整 exp static_castlong(clamped / lineDuration_) * lineDuration_; // 量化補償系數(shù) 理想值 / 硬件實際可設(shè)置值exp ≤ clamped → quantGain ≥ 1 if (quantizationGain) *quantizationGain clamped / exp; return exp; }示例理想曝光 105us行周期 10us → exp100usquantizationGain 105/100 1.05。clampGain 模擬增益鉗位與量化double ExposureModeHelper::clampGain(double gain, double *quantizationGain) const { // 鉗位到硬件模擬增益上下限 double clamped std::clamp(gain, minGain_, maxGain_); if (sensorHelper_) // sensorHelper完成離散增益檔位量化輸出量化補償 return sensorHelper_-quantizeGain(clamped, quantizationGain); if (quantizationGain) *quantizationGain 1.0; // 無量化損失補償系數(shù)為1 return clamped; }stage 傳入的總增益上限會經(jīng)過本函數(shù)被硬件模擬增益上限截斷超出部分無法由模擬增益實現(xiàn)最后落到 digitalGain。核心函數(shù) splitExposure輸入exposure目標總曝光量 返回 tuple(exposureTime, analogue_gain, quantization_gain, digital_gain)嚴格正確總曝光等式exposureTime_hw下發(fā) sensor 的曝光時間對齊行周期gain_ana_hw下發(fā) sensor 的模擬增益量化后檔位quantGain曝光 模擬增益帶來的總量化補償gain_digitalISP 配置的數(shù)字增益std::tupleutils::Duration, double, double, double ExposureModeHelper::splitExposure(utils::Duration exposure) const { ASSERT(maxExposureTime_); ASSERT(maxGain_); utils::Duration exposureTime; double gain; double quantGain; double quantGain2; bool gainFixed minGain_ maxGain_; bool exposureTimeFixed minExposureTime_ maxExposureTime_; // 分支1硬件層面曝光時間和增益全部鎖死不可調(diào)節(jié) if (exposureTimeFixed gainFixed) { exposureTime clampExposureTime(minExposureTime_, quantGain); gain clampGain(minGain_, quantGain2); quantGain * quantGain2; return { exposureTime, gain, quantGain, exposure / (exposureTime * gain * quantGain) }; } double stageGain clampGain(1.0); double lastStageGain stageGain; // 上一階段經(jīng)過鉗位后的總增益上限初始為1.0 // 分支2遍歷所有stage做分階段曝光拆分 for (unsigned int stage 0; stage gains_.size(); stage) { utils::Duration stageExposureTime clampExposureTime(exposureTimes_[stage], quantGain); stageGain clampGain(gains_[stage]); // a維持上一階段增益上限lastStageGain不提升曝光最多拉到本階段stageExposureTime // 條件成立僅靠【不提升增益 曝光最高到本階段上限】就可以滿足目標曝光 if (stageExposureTime * lastStageGain exposure) { exposureTime clampExposureTime(exposure / lastStageGain, quantGain); gain clampGain(exposure / exposureTime, quantGain2); quantGain * quantGain2; return { exposureTime, gain, quantGain, exposure / (exposureTime * gain * quantGain) }; } // ba條件不成立允許增益提升到本階段stageGain曝光釘死本階段最大曝光 // 條件成立本階段最大曝光 本階段最大增益可滿足目標曝光 if (stageExposureTime * stageGain exposure) { exposureTime stageExposureTime; gain clampGain(exposure / exposureTime, quantGain2); quantGain * quantGain2; return { exposureTime, gain, quantGain, exposure / (exposureTime * gain * quantGain) }; } // ca、b都不成立本stage即使曝光、增益全部拉滿依舊達不到目標曝光 // 更新上階段增益進入下一輪stage lastStageGain stageGain; } // 分支3全部stage遍歷完畢進入兜底邏輯 // stageGain為最后一個stage經(jīng)過鉗位后的增益上限stages為空時for不執(zhí)行stageGain1.0 exposureTime clampExposureTime(exposure / stageGain, quantGain); gain clampGain(exposure / exposureTime, quantGain2); quantGain * quantGain2; return { exposureTime, gain, quantGain, exposure / (exposureTime * gain * quantGain) }; }四、算法完整流程文字版前置斷言maxExposureTime_、maxGain_必須非 0如果硬件曝光、模擬增益同時被鎖死直接計算硬件可設(shè)置值缺口全部由數(shù)字增益補齊返回初始化lastStageGain clampGain(1.0)遍歷每一個 stage將當(dāng)前 stage 配置的最大曝光、最大總增益做硬件鉗位、量化得到stageExposureTime、stageGain判斷stageExposureTime * lastStageGain exposure?成立不提升增益維持上階段增益上限反求需要的曝光時間硬件鉗位量化微調(diào)增益直接返回結(jié)果。關(guān)鍵點最終曝光不需要等于 stageExposureTime反求得到更小的曝光時間。上一步不成立判斷stageExposureTime * stageGain exposure?成立曝光固定為本階段最大曝光stageExposureTime允許增益提升到本階段上限反求增益返回結(jié)果。兩個條件均不成立本 stage 能力不足以滿足目標曝光更新lastStageGain stageGain進入下一 stage所有 stage 執(zhí)行完畢執(zhí)行兜底使用最后 stage 的增益上限stageGain做基準計算曝光時間與增益特殊情況stages 為空for 循環(huán)不執(zhí)行stageGain1.0此時行為等價傳統(tǒng) AE優(yōu)先最大化曝光時間之后增加模擬增益剩余缺口交給數(shù)字增益。返回四元組。五、代碼實現(xiàn)exposure_mode_helper.h實現(xiàn)/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2024, Paul Elder paul.elderideasonboard.com * * Helper class that performs computations relating to exposure */ #pragma once #include tuple #include utility #include vector #include libcamera/base/span.h #include libcamera/base/utils.h #include camera_sensor_helper.h namespace libcamera { namespace ipa { class ExposureModeHelper { public: ExposureModeHelper(const Spanstd::pairutils::Duration, double stages); ~ExposureModeHelper() default; void configure(utils::Duration lineLength, const CameraSensorHelper *sensorHelper); void setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime, double minGain, double maxGain); std::tupleutils::Duration, double, double, double splitExposure(utils::Duration exposure) const; utils::Duration minExposureTime() const { return minExposureTime_; } utils::Duration maxExposureTime() const { return maxExposureTime_; } double minGain() const { return minGain_; } double maxGain() const { return maxGain_; } private: utils::Duration clampExposureTime(utils::Duration exposureTime, double *quantizationGain nullptr) const; double clampGain(double gain, double *quantizationGain nullptr) const; std::vectorutils::Duration exposureTimes_; std::vectordouble gains_; utils::Duration lineDuration_; utils::Duration minExposureTime_; utils::Duration maxExposureTime_; double minGain_; double maxGain_; const CameraSensorHelper *sensorHelper_; }; } /* namespace ipa */ } /* namespace libcamera */exposure_mode_helper.c實現(xiàn)/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2024, Paul Elder paul.elderideasonboard.com * * Helper class that performs computations relating to exposure */ #include exposure_mode_helper.h #include algorithm #include libcamera/base/log.h /** * \file exposure_mode_helper.h * \brief Helper class that performs computations relating to exposure * * AEGC algorithms have a need to split exposure between exposure time, analogue * and digital gain. Multiple implementations do so based on paired stages of * exposure time and gain limits; provide a helper to avoid duplicating the code. */ namespace libcamera { using namespace std::literals::chrono_literals; LOG_DEFINE_CATEGORY(ExposureModeHelper) namespace ipa { /** * \class ExposureModeHelper * \brief Class for splitting exposure into exposure time and total gain * * The ExposureModeHelper class provides a standard interface through which an * AEGC algorithm can divide exposure between exposure time and gain. It is * configured with a set of exposure time and gain pairs and works by initially * fixing gain at 1.0 and increasing exposure time up to the exposure time value * from the first pair in the set in an attempt to meet the required exposure * value. * * If the required exposure is not achievable by the first exposure time value * alone it ramps gain up to the value from the first pair in the set. If the * required exposure is still not met it then allows exposure time to ramp up to * the exposure time value from the second pair in the set, and continues in this * vein until either the required exposure time is met, or else the hardwares * exposure time or gain limits are reached. * * This method allows users to strike a balance between a well-exposed image and * an acceptable frame-rate, as opposed to simply maximising exposure time * followed by gain. The same helpers can be used to perform the latter * operation if needed by passing an empty set of pairs to the initialisation * function. * * The gain values may exceed a camera sensors analogue gain limits if either * it or the IPA is also capable of digital gain. The configure() function must * be called with the hardwares limits to inform the helper of those * constraints. Any gain that is needed will be applied as analogue gain first * until the hardwares limit is reached, following which digital gain will be * used. */ /** * \brief Construct an ExposureModeHelper instance * \param[in] stages The vector of paired exposure time and gain limits * * The input stages are exposure time and _total_ gain pairs; the gain * encompasses both analogue and digital gain. * * The vector of stages may be empty. In that case, the helper will simply use * the runtime limits set through setLimits() instead. */ ExposureModeHelper::ExposureModeHelper(const Spanstd::pairutils::Duration, double stages) : lineDuration_(1us), minExposureTime_(0us), maxExposureTime_(0us), minGain_(0), maxGain_(0), sensorHelper_(nullptr) { for (const auto [s, g] : stages) { exposureTimes_.push_back(s); gains_.push_back(g); } } /** * \brief Configure sensor details * \param[in] lineDuration The current line length of the sensor * \param[in] sensorHelper The sensor helper * * This function sets the line length and sensor helper. These are used in * splitExposure() to take the quantization of the exposure and gain into * account. * * When this has not been called, it is assumed that exposure is in micro second * granularity and gain has no quantization at all. * * ExposureModeHelper keeps a pointer to the CameraSensorHelper, so the caller * has to ensure that sensorHelper is valid until the next call to configure(). */ void ExposureModeHelper::configure(utils::Duration lineDuration, const CameraSensorHelper *sensorHelper) { lineDuration_ lineDuration; sensorHelper_ sensorHelper; } /** * \brief Set the exposure time and gain limits * \param[in] minExposureTime The minimum exposure time supported * \param[in] maxExposureTime The maximum exposure time supported * \param[in] minGain The minimum analogue gain supported * \param[in] maxGain The maximum analogue gain supported * * This function configures the exposure time and analogue gain limits that need * to be adhered to as the helper divides up exposure. Note that this function * *must* be called whenever those limits change and before splitExposure() is * used. * * If the algorithm using the helpers needs to indicate that either exposure time * or analogue gain or both should be fixed it can do so by setting both the * minima and maxima to the same value. */ void ExposureModeHelper::setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime, double minGain, double maxGain) { minExposureTime_ minExposureTime; maxExposureTime_ maxExposureTime; minGain_ minGain; maxGain_ maxGain; } utils::Duration ExposureModeHelper::clampExposureTime(utils::Duration exposureTime, double *quantizationGain) const { utils::Duration clamped; utils::Duration exp; clamped std::clamp(exposureTime, minExposureTime_, maxExposureTime_); exp static_castlong(clamped / lineDuration_) * lineDuration_; if (quantizationGain) *quantizationGain clamped / exp; return exp; } double ExposureModeHelper::clampGain(double gain, double *quantizationGain) const { double clamped std::clamp(gain, minGain_, maxGain_); if (sensorHelper_) return sensorHelper_-quantizeGain(clamped, quantizationGain); if (quantizationGain) *quantizationGain 1.0; return clamped; } /** * \brief Split exposure into exposure time and gain * \param[in] exposure Exposure value * * This function divides a given exposure into exposure time, analogue and * digital gain by iterating through stages of exposure time and gain limits. * At each stage the current stages exposure time limit is multiplied by the * previous stages gain limit (or 1.0 initially) to see if the combination of * the two can meet the required exposure. If they cannot then the current * stages exposure time limit is multiplied by the same stages gain limit to * see if that combination can meet the required exposure time. If they cannot * then the function moves to consider the next stage. * * When a combination of exposure time and gain _stage_ limits are found that * are sufficient to meet the required exposure, the function attempts to reduce * exposure time as much as possible whilst fixing gain and still meeting the * exposure. If a _runtime_ limit prevents exposure time from being lowered * enough to meet the exposure with gain fixed at the stage limit, gain is also * lowered to compensate. * * Once the exposure time and gain values are ascertained, gain is assigned as * analogue gain as much as possible, with digital gain only in use if the * maximum analogue gain runtime limit is unable to accommodate the exposure * value. * * If no combination of exposure time and gain limits is found that meets the * required exposure, the helper falls-back to simply maximising the exposure * time first, followed by analogue gain, followed by digital gain. * * During the calculations the gain missed due to quantization is recorded and * returned as quantization gain. The quantization gain is not included in the * digital gain. So to exactly apply the given exposure, both quantization gain * and digital gain must be applied. * * \return Tuple of exposure time, analogue gain, quantization gain and digital * gain */ std::tupleutils::Duration, double, double, double ExposureModeHelper::splitExposure(utils::Duration exposure) const { ASSERT(maxExposureTime_); ASSERT(maxGain_); utils::Duration exposureTime; double gain; double quantGain; double quantGain2; bool gainFixed minGain_ maxGain_; bool exposureTimeFixed minExposureTime_ maxExposureTime_; /* * Theres no point entering the loop if we cannot change either gain * nor exposure time anyway. */ if (exposureTimeFixed gainFixed) { exposureTime clampExposureTime(minExposureTime_, quantGain); gain clampGain(minGain_, quantGain2); quantGain * quantGain2; return { exposureTime, gain, quantGain, exposure / (exposureTime * gain * quantGain) }; } double stageGain clampGain(1.0); double lastStageGain stageGain; for (unsigned int stage 0; stage gains_.size(); stage) { utils::Duration stageExposureTime clampExposureTime(exposureTimes_[stage], quantGain); stageGain clampGain(gains_[stage]); /* * We perform the clamping on both exposure time and gain in * case the helper has had limits set that prevent those values * being lowered beyond a certain minimum...this can happen at * runtime for various reasons and so would not be known when * the stage limits are initialised. */ /* Clamp the gain to lastStageGain and regulate exposureTime. */ if (stageExposureTime * lastStageGain exposure) { exposureTime clampExposureTime(exposure / lastStageGain, quantGain); gain clampGain(exposure / exposureTime, quantGain2); quantGain * quantGain2; return { exposureTime, gain, quantGain, exposure / (exposureTime * gain * quantGain) }; } /* Clamp the exposureTime to stageExposureTime and regulate gain. */ if (stageExposureTime * stageGain exposure) { exposureTime stageExposureTime; gain clampGain(exposure / exposureTime, quantGain2); quantGain * quantGain2; return { exposureTime, gain, quantGain, exposure / (exposureTime * gain * quantGain) }; } lastStageGain stageGain; } /* * From here on all we can do is max out the exposure time, followed by * the analogue gain. If we still havent achieved the target we send * the rest of the exposure time to digital gain. If we were given no * stages to use then the default stageGain of 1.0 is used so that * exposure time is maxed before gain is touched at all. */ exposureTime clampExposureTime(exposure / stageGain, quantGain); gain clampGain(exposure / exposureTime, quantGain2); quantGain * quantGain2; return { exposureTime, gain, quantGain, exposure / (exposureTime * gain * quantGain) }; } /** * \fn ExposureModeHelper::minExposureTime() * \brief Retrieve the configured minimum exposure time limit set through * setLimits() * \return The minExposureTime_ value */ /** * \fn ExposureModeHelper::maxExposureTime() * \brief Retrieve the configured maximum exposure time set through setLimits() * \return The maxExposureTime_ value */ /** * \fn ExposureModeHelper::minGain() * \brief Retrieve the configured minimum gain set through setLimits() * \return The minGain_ value */ /** * \fn ExposureModeHelper::maxGain() * \brief Retrieve the configured maximum gain set through setLimits() * \return The maxGain_ value */ } /* namespace ipa */ } /* namespace libcamera */