ipa: libipa: Add ExposureModeHelper
Add a helper for managing exposure modes and splitting exposure times into shutter and gain values. Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
parent
6728160b11
commit
34c9ab6282
3 changed files with 301 additions and 0 deletions
246
src/ipa/libipa/exposure_mode_helper.cpp
Normal file
246
src/ipa/libipa/exposure_mode_helper.cpp
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
|
||||||
|
*
|
||||||
|
* exposure_mode_helper.cpp - 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 shutter time, analogue
|
||||||
|
* and digital gain. Multiple implementations do so based on paired stages of
|
||||||
|
* shutter 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 shutter time and total gain
|
||||||
|
*
|
||||||
|
* The ExposureModeHelper class provides a standard interface through which an
|
||||||
|
* AEGC algorithm can divide exposure between shutter time and gain. It is
|
||||||
|
* configured with a set of shutter time and gain pairs and works by initially
|
||||||
|
* fixing gain at 1.0 and increasing shutter time up to the shutter 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 shutter 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 shutter time to ramp up to
|
||||||
|
* the shutter 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 hardware's
|
||||||
|
* shutter 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 shutter 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 sensor's analogue gain limits if either
|
||||||
|
* it or the IPA is also capable of digital gain. The configure() function must
|
||||||
|
* be called with the hardware's limits to inform the helper of those
|
||||||
|
* constraints. Any gain that is needed will be applied as analogue gain first
|
||||||
|
* until the hardware's limit is reached, following which digital gain will be
|
||||||
|
* used.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Construct an ExposureModeHelper instance
|
||||||
|
* \param[in] stages The vector of paired shutter time and gain limits
|
||||||
|
*
|
||||||
|
* The input stages are shutter 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 setShutterGainLimits() instead.
|
||||||
|
*/
|
||||||
|
ExposureModeHelper::ExposureModeHelper(const Span<std::pair<utils::Duration, double>> stages)
|
||||||
|
{
|
||||||
|
minShutter_ = 0us;
|
||||||
|
maxShutter_ = 0us;
|
||||||
|
minGain_ = 0;
|
||||||
|
maxGain_ = 0;
|
||||||
|
|
||||||
|
for (const auto &[s, g] : stages) {
|
||||||
|
shutters_.push_back(s);
|
||||||
|
gains_.push_back(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Set the shutter time and gain limits
|
||||||
|
* \param[in] minShutter The minimum shutter time supported
|
||||||
|
* \param[in] maxShutter The maximum shutter time supported
|
||||||
|
* \param[in] minGain The minimum analogue gain supported
|
||||||
|
* \param[in] maxGain The maximum analogue gain supported
|
||||||
|
*
|
||||||
|
* This function configures the shutter 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 shutter 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 minShutter,
|
||||||
|
utils::Duration maxShutter,
|
||||||
|
double minGain, double maxGain)
|
||||||
|
{
|
||||||
|
minShutter_ = minShutter;
|
||||||
|
maxShutter_ = maxShutter;
|
||||||
|
minGain_ = minGain;
|
||||||
|
maxGain_ = maxGain;
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::Duration ExposureModeHelper::clampShutter(utils::Duration shutter) const
|
||||||
|
{
|
||||||
|
return std::clamp(shutter, minShutter_, maxShutter_);
|
||||||
|
}
|
||||||
|
|
||||||
|
double ExposureModeHelper::clampGain(double gain) const
|
||||||
|
{
|
||||||
|
return std::clamp(gain, minGain_, maxGain_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Split exposure time into shutter time and gain
|
||||||
|
* \param[in] exposure Exposure time
|
||||||
|
*
|
||||||
|
* This function divides a given exposure time into shutter time, analogue and
|
||||||
|
* digital gain by iterating through stages of shutter time and gain limits. At
|
||||||
|
* each stage the current stage's shutter time limit is multiplied by the
|
||||||
|
* previous stage's gain limit (or 1.0 initially) to see if the combination of
|
||||||
|
* the two can meet the required exposure time. If they cannot then the current
|
||||||
|
* stage's shutter time limit is multiplied by the same stage's 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 shutter time and gain _stage_ limits are found that are
|
||||||
|
* sufficient to meet the required exposure time, the function attempts to
|
||||||
|
* reduce shutter time as much as possible whilst fixing gain and still meeting
|
||||||
|
* the exposure time. If a _runtime_ limit prevents shutter time from being
|
||||||
|
* lowered enough to meet the exposure time with gain fixed at the stage limit,
|
||||||
|
* gain is also lowered to compensate.
|
||||||
|
*
|
||||||
|
* Once the shutter 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 shutter time and gain limits is found that meets the
|
||||||
|
* required exposure time, the helper falls-back to simply maximising the
|
||||||
|
* shutter time first, followed by analogue gain, followed by digital gain.
|
||||||
|
*
|
||||||
|
* \return Tuple of shutter time, analogue gain, and digital gain
|
||||||
|
*/
|
||||||
|
std::tuple<utils::Duration, double, double>
|
||||||
|
ExposureModeHelper::splitExposure(utils::Duration exposure) const
|
||||||
|
{
|
||||||
|
ASSERT(maxShutter_);
|
||||||
|
ASSERT(maxGain_);
|
||||||
|
|
||||||
|
bool gainFixed = minGain_ == maxGain_;
|
||||||
|
bool shutterFixed = minShutter_ == maxShutter_;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There's no point entering the loop if we cannot change either gain
|
||||||
|
* nor shutter anyway.
|
||||||
|
*/
|
||||||
|
if (shutterFixed && gainFixed)
|
||||||
|
return { minShutter_, minGain_, exposure / (minShutter_ * minGain_) };
|
||||||
|
|
||||||
|
utils::Duration shutter;
|
||||||
|
double stageGain;
|
||||||
|
double gain;
|
||||||
|
|
||||||
|
for (unsigned int stage = 0; stage < gains_.size(); stage++) {
|
||||||
|
double lastStageGain = stage == 0 ? 1.0 : clampGain(gains_[stage - 1]);
|
||||||
|
utils::Duration stageShutter = clampShutter(shutters_[stage]);
|
||||||
|
stageGain = clampGain(gains_[stage]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We perform the clamping on both shutter 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (stageShutter * lastStageGain >= exposure) {
|
||||||
|
shutter = clampShutter(exposure / clampGain(lastStageGain));
|
||||||
|
gain = clampGain(exposure / shutter);
|
||||||
|
|
||||||
|
return { shutter, gain, exposure / (shutter * gain) };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stageShutter * stageGain >= exposure) {
|
||||||
|
shutter = clampShutter(exposure / clampGain(stageGain));
|
||||||
|
gain = clampGain(exposure / shutter);
|
||||||
|
|
||||||
|
return { shutter, gain, exposure / (shutter * gain) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* From here on all we can do is max out the shutter time, followed by
|
||||||
|
* the analogue gain. If we still haven't achieved the target we send
|
||||||
|
* the rest of the exposure time to digital gain. If we were given no
|
||||||
|
* stages to use then set stageGain to 1.0 so that shutter time is maxed
|
||||||
|
* before gain touched at all.
|
||||||
|
*/
|
||||||
|
if (gains_.empty())
|
||||||
|
stageGain = 1.0;
|
||||||
|
|
||||||
|
shutter = clampShutter(exposure / clampGain(stageGain));
|
||||||
|
gain = clampGain(exposure / shutter);
|
||||||
|
|
||||||
|
return { shutter, gain, exposure / (shutter * gain) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn ExposureModeHelper::minShutter()
|
||||||
|
* \brief Retrieve the configured minimum shutter time limit set through
|
||||||
|
* setShutterGainLimits()
|
||||||
|
* \return The minShutter_ value
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn ExposureModeHelper::maxShutter()
|
||||||
|
* \brief Retrieve the configured maximum shutter time set through
|
||||||
|
* setShutterGainLimits()
|
||||||
|
* \return The maxShutter_ value
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn ExposureModeHelper::minGain()
|
||||||
|
* \brief Retrieve the configured minimum gain set through
|
||||||
|
* setShutterGainLimits()
|
||||||
|
* \return The minGain_ value
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn ExposureModeHelper::maxGain()
|
||||||
|
* \brief Retrieve the configured maximum gain set through
|
||||||
|
* setShutterGainLimits()
|
||||||
|
* \return The maxGain_ value
|
||||||
|
*/
|
||||||
|
|
||||||
|
} /* namespace ipa */
|
||||||
|
|
||||||
|
} /* namespace libcamera */
|
53
src/ipa/libipa/exposure_mode_helper.h
Normal file
53
src/ipa/libipa/exposure_mode_helper.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
|
||||||
|
*
|
||||||
|
* exposure_mode_helper.h - 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>
|
||||||
|
|
||||||
|
namespace libcamera {
|
||||||
|
|
||||||
|
namespace ipa {
|
||||||
|
|
||||||
|
class ExposureModeHelper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ExposureModeHelper(const Span<std::pair<utils::Duration, double>> stages);
|
||||||
|
~ExposureModeHelper() = default;
|
||||||
|
|
||||||
|
void setLimits(utils::Duration minShutter, utils::Duration maxShutter,
|
||||||
|
double minGain, double maxGain);
|
||||||
|
|
||||||
|
std::tuple<utils::Duration, double, double>
|
||||||
|
splitExposure(utils::Duration exposure) const;
|
||||||
|
|
||||||
|
utils::Duration minShutter() const { return minShutter_; }
|
||||||
|
utils::Duration maxShutter() const { return maxShutter_; }
|
||||||
|
double minGain() const { return minGain_; }
|
||||||
|
double maxGain() const { return maxGain_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
utils::Duration clampShutter(utils::Duration shutter) const;
|
||||||
|
double clampGain(double gain) const;
|
||||||
|
|
||||||
|
std::vector<utils::Duration> shutters_;
|
||||||
|
std::vector<double> gains_;
|
||||||
|
|
||||||
|
utils::Duration minShutter_;
|
||||||
|
utils::Duration maxShutter_;
|
||||||
|
double minGain_;
|
||||||
|
double maxGain_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace ipa */
|
||||||
|
|
||||||
|
} /* namespace libcamera */
|
|
@ -3,6 +3,7 @@
|
||||||
libipa_headers = files([
|
libipa_headers = files([
|
||||||
'algorithm.h',
|
'algorithm.h',
|
||||||
'camera_sensor_helper.h',
|
'camera_sensor_helper.h',
|
||||||
|
'exposure_mode_helper.h',
|
||||||
'fc_queue.h',
|
'fc_queue.h',
|
||||||
'histogram.h',
|
'histogram.h',
|
||||||
'module.h',
|
'module.h',
|
||||||
|
@ -11,6 +12,7 @@ libipa_headers = files([
|
||||||
libipa_sources = files([
|
libipa_sources = files([
|
||||||
'algorithm.cpp',
|
'algorithm.cpp',
|
||||||
'camera_sensor_helper.cpp',
|
'camera_sensor_helper.cpp',
|
||||||
|
'exposure_mode_helper.cpp',
|
||||||
'fc_queue.cpp',
|
'fc_queue.cpp',
|
||||||
'histogram.cpp',
|
'histogram.cpp',
|
||||||
'module.cpp',
|
'module.cpp',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue