summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
Diffstat (limited to 'include/linux')
0 files changed, 0 insertions, 0 deletions
>41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.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 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 setLimits() 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 = 1.0;
	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 the default stageGain of 1.0 is used so that
	 * shutter time is maxed before gain is touched at all.
	 */
	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
 * setLimits()
 * \return The minShutter_ value
 */

/**
 * \fn ExposureModeHelper::maxShutter()
 * \brief Retrieve the configured maximum shutter time set through setLimits()
 * \return The maxShutter_ 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 */