@@ -208,9 +208,15 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,
ControlList &metadata)
{
Histogram hist = parseStatistics(stats, context.configuration.grid.bdsGrid);
- rGain_ = context.activeState.awb.gains.red;
- gGain_ = context.activeState.awb.gains.blue;
- bGain_ = context.activeState.awb.gains.green;
+ rGain_ = context.activeState.awb.autoEnabled ?
+ context.activeState.awb.automatic.gains[0] :
+ context.activeState.awb.manual.gains[0];
+ gGain_ = context.activeState.awb.autoEnabled ?
+ context.activeState.awb.automatic.gains[1] :
+ context.activeState.awb.manual.gains[1];
+ bGain_ = context.activeState.awb.autoEnabled ?
+ context.activeState.awb.automatic.gains[2] :
+ context.activeState.awb.manual.gains[2];
/*
* The Agc algorithm needs to know the effective exposure value that was
@@ -26,11 +26,40 @@ namespace ipa::ipu3::algorithms {
LOG_DEFINE_CATEGORY(IPU3Awb)
/*
- * When zones are used for the grey world algorithm, they are only considered if
- * their average green value is at least 16/255 (after black level subtraction)
- * to exclude zones that are too dark and don't provide relevant colour
- * information (on the opposite side of the spectrum, saturated regions are
- * excluded by the ImgU statistics engine).
+ * \todo IPU3 doesn't support the Lux algorithm.
+ */
+static constexpr unsigned int kDefaultLux = 500;
+
+/**
+ * \brief The IPU3 implementation of AwbStats
+ */
+class Ipu3AwbStats final : public AwbStats
+{
+public:
+ Ipu3AwbStats(){};
+ Ipu3AwbStats(const RGB<double> means)
+ : AwbStats(means)
+ {
+ }
+
+ double minColourValue() const override
+ {
+ return 0.0;
+ }
+};
+
+/**
+ * \fn Ipu3AwbStats::Ipu3AwbStats(const RGB<double> means)
+ * \brief Construct an instance of the class with RGB means
+ * \param[in] means The mean R, G and B values from the statistics
+ */
+
+/*
+ * Zones are only considered if their average green value is at least
+ * kMinGreenLevelInZone/255 (after black level subtraction) to exclude zones
+ * that are too dark and don't provide relevant colour information (on the
+ * opposite side of the spectrum, saturated regions are excluded by the ImgU
+ * statistics engine).
*/
static constexpr uint32_t kMinGreenLevelInZone = 16;
@@ -74,26 +103,6 @@ static constexpr uint32_t kMinCellsPerZoneRatio = 255 * 90 / 100;
* \brief Sum of the average blue values of each unsaturated cell in the zone
*/
-/**
- * \struct Awb::AwbStatus
- * \brief AWB parameters calculated
- *
- * The AwbStatus structure is intended to store the AWB
- * parameters calculated by the algorithm
- *
- * \var AwbStatus::temperatureK
- * \brief Color temperature calculated
- *
- * \var AwbStatus::redGain
- * \brief Gain calculated for the red channel
- *
- * \var AwbStatus::greenGain
- * \brief Gain calculated for the green channel
- *
- * \var AwbStatus::blueGain
- * \brief Gain calculated for the blue channel
- */
-
/* Default settings for Bayer noise reduction replicated from the Kernel */
static const struct ipu3_uapi_bnr_static_config imguCssBnrDefaults = {
.wb_gains = { 16, 16, 16, 16 },
@@ -114,11 +123,7 @@ static const struct ipu3_uapi_bnr_static_config imguCssBnrDefaults = {
/**
* \class Awb
- * \brief A Grey world white balance correction algorithm
- *
- * The Grey World algorithm assumes that the scene, in average, is neutral grey.
- * Reference: Lam, Edmund & Fung, George. (2008). Automatic White Balancing in
- * Digital Photography. 10.1201/9781420054538.ch10.
+ * \brief The IPU3 white balance correction algorithm implementation
*
* The IPU3 generates statistics from the Bayer Down Scaler output into a grid
* defined in the ipu3_uapi_awb_config_s structure.
@@ -168,26 +173,26 @@ static const struct ipu3_uapi_bnr_static_config imguCssBnrDefaults = {
* cells are ignored. The grid configuration is computed by
* IPAIPU3::calculateBdsGrid().
*
- * Before calculating the gains, the algorithm aggregates the cell averages for
- * each zone in generateAwbStats(). Cells that have a too high ratio of
- * saturated pixels are ignored, and only zones that contain enough
- * non-saturated cells are then used by the algorithm.
- *
- * The Grey World algorithm will then estimate the red and blue gains to apply, and
- * store the results in the metadata. The green gain is always set to 1.
+ * Before running the AWB algorithm, we aggregate the cell averages for each
+ * zone in generateAwbStats(). Cells that have a too high ratio of saturated
+ * pixels are ignored, and only zones that contain enough non-saturated cells
+ * are then used by the algorithm.
*/
Awb::Awb()
: Algorithm()
{
- asyncResults_.blueGain = 1.0;
- asyncResults_.greenGain = 1.0;
- asyncResults_.redGain = 1.0;
- asyncResults_.temperatureK = 4500;
-
zones_.reserve(kAwbStatsSizeX * kAwbStatsSizeY);
}
+/**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
+int Awb::init(IPAContext &context, const ValueNode &tuningData)
+{
+ return awbAlgo_.init(tuningData, context.ctrlMap);
+}
+
/**
* \copydoc libcamera::ipa::Algorithm::configure
*/
@@ -197,12 +202,14 @@ int Awb::configure(IPAContext &context,
const ipu3_uapi_grid_config &grid = context.configuration.grid.bdsGrid;
stride_ = context.configuration.grid.stride;
+ awbAlgo_.configure(context.activeState.awb, context.configuration.awb);
+
cellsPerZoneX_ = std::round(grid.width / static_cast<double>(kAwbStatsSizeX));
cellsPerZoneY_ = std::round(grid.height / static_cast<double>(kAwbStatsSizeY));
/*
* Configure the minimum proportion of cells counted within a zone
- * for it to be relevant for the grey world algorithm.
+ * for it to be used.
* \todo This proportion could be configured.
*/
cellsPerZoneThreshold_ = cellsPerZoneX_ * cellsPerZoneY_ * kMaxCellSaturationRatio;
@@ -211,6 +218,17 @@ int Awb::configure(IPAContext &context,
return 0;
}
+/**
+ * \copydoc libcamera::ipa::Algorithm::queueRequest
+ */
+void Awb::queueRequest(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const ControlList &controls)
+{
+ awbAlgo_.queueRequest(context.activeState.awb, frame, frameContext.awb,
+ controls);
+}
+
constexpr uint16_t Awb::threshold(float value)
{
/* AWB thresholds are in the range [0, 8191] */
@@ -237,11 +255,12 @@ constexpr uint16_t Awb::gainValue(double gain)
/**
* \copydoc libcamera::ipa::Algorithm::prepare
*/
-void Awb::prepare(IPAContext &context,
- [[maybe_unused]] const uint32_t frame,
- [[maybe_unused]] IPAFrameContext &frameContext,
- ipu3_uapi_params *params)
+void Awb::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ [[maybe_unused]] ipu3_uapi_params *params)
{
+ awbAlgo_.prepare(context.activeState.awb, frameContext.awb);
+
/*
* Green saturation thresholds are reduced because we are using the
* green channel only in the exposure computation.
@@ -279,13 +298,11 @@ void Awb::prepare(IPAContext &context,
params->acc_param.bnr.opt_center_sqr.y_sqr_reset = params->acc_param.bnr.opt_center.y_reset
* params->acc_param.bnr.opt_center.y_reset;
- params->acc_param.bnr.wb_gains.gr = gainValue(context.activeState.awb.gains.green);
- params->acc_param.bnr.wb_gains.r = gainValue(context.activeState.awb.gains.red);
- params->acc_param.bnr.wb_gains.b = gainValue(context.activeState.awb.gains.blue);
- params->acc_param.bnr.wb_gains.gb = gainValue(context.activeState.awb.gains.green);
-
- LOG(IPU3Awb, Debug) << "Color temperature estimated: " << asyncResults_.temperatureK;
+ params->acc_param.bnr.wb_gains.gr = gainValue(frameContext.awb.gains.g());
+ params->acc_param.bnr.wb_gains.r = gainValue(frameContext.awb.gains.r());
+ params->acc_param.bnr.wb_gains.b = gainValue(frameContext.awb.gains.b());
+ params->acc_param.bnr.wb_gains.gb = gainValue(frameContext.awb.gains.g());
params->use.acc_awb = 1;
params->use.acc_bnr = 1;
@@ -366,9 +383,17 @@ void Awb::clearAwbStats()
}
}
-void Awb::awbGreyWorld()
+Ipu3AwbStats Awb::calculateRgbMeans(const ipu3_uapi_stats_3a *stats)
{
- LOG(IPU3Awb, Debug) << "Grey world AWB";
+ ASSERT(stats->stats_3a_status.awb_en);
+
+ clearAwbStats();
+ generateAwbStats(stats);
+ generateZones();
+
+ if (zones_.size() <= 10)
+ return {};
+
/*
* Make a separate list of the derivatives for each of red and blue, so
* that we can sort them to exclude the extreme gains. We could
@@ -399,66 +424,21 @@ void Awb::awbGreyWorld()
double redGain = sumRed.g() / (sumRed.r() + 1),
blueGain = sumBlue.g() / (sumBlue.b() + 1);
- /* Color temperature is not relevant in Grey world but still useful to estimate it :-) */
- asyncResults_.temperatureK = estimateCCT({{ sumRed.r(), sumRed.g(), sumBlue.b() }});
-
- /*
- * Gain values are unsigned integer value ranging [0, 8) with 13 bit
- * fractional part.
- */
- redGain = std::clamp(redGain, 0.0, 65535.0 / 8192);
- blueGain = std::clamp(blueGain, 0.0, 65535.0 / 8192);
-
- asyncResults_.redGain = redGain;
- /* Hardcode the green gain to 1.0. */
- asyncResults_.greenGain = 1.0;
- asyncResults_.blueGain = blueGain;
-}
-
-void Awb::calculateWBGains(const ipu3_uapi_stats_3a *stats)
-{
- ASSERT(stats->stats_3a_status.awb_en);
-
- clearAwbStats();
- generateAwbStats(stats);
- generateZones();
-
- LOG(IPU3Awb, Debug) << "Valid zones: " << zones_.size();
-
- if (zones_.size() > 10) {
- awbGreyWorld();
- LOG(IPU3Awb, Debug) << "Gain found for red: " << asyncResults_.redGain
- << " and for blue: " << asyncResults_.blueGain;
- }
+ return Ipu3AwbStats({ { 1.0 / redGain, 1.0, 1.0 / blueGain } });
}
/**
* \copydoc libcamera::ipa::Algorithm::process
*/
void Awb::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,
- [[maybe_unused]] IPAFrameContext &frameContext,
- const ipu3_uapi_stats_3a *stats,
- [[maybe_unused]] ControlList &metadata)
+ IPAFrameContext &frameContext,
+ [[maybe_unused]] const ipu3_uapi_stats_3a *stats,
+ ControlList &metadata)
{
- calculateWBGains(stats);
+ Ipu3AwbStats awbStats = calculateRgbMeans(stats);
- /*
- * Gains are only recalculated if enough zones were detected.
- * The results are cached, so if no results were calculated, we set the
- * cached values from asyncResults_ here.
- */
- context.activeState.awb.gains.blue = asyncResults_.blueGain;
- context.activeState.awb.gains.green = asyncResults_.greenGain;
- context.activeState.awb.gains.red = asyncResults_.redGain;
- context.activeState.awb.temperatureK = asyncResults_.temperatureK;
-
- metadata.set(controls::AwbEnable, true);
- metadata.set(controls::ColourGains, {
- static_cast<float>(context.activeState.awb.gains.red),
- static_cast<float>(context.activeState.awb.gains.blue)
- });
- metadata.set(controls::ColourTemperature,
- context.activeState.awb.temperatureK);
+ awbAlgo_.process(context.activeState.awb, frameContext.awb, awbStats,
+ kDefaultLux, metadata);
}
REGISTER_IPA_ALGORITHM(Awb, "Awb")
@@ -13,6 +13,9 @@
#include <libcamera/geometry.h>
+#include "libipa/awb.h"
+#include "libipa/fixedpoint.h"
+
#include "libcamera/internal/vector.h"
#include "algorithm.h"
@@ -21,6 +24,8 @@ namespace libcamera {
namespace ipa::ipu3::algorithms {
+class Ipu3AwbStats;
+
/* Region size for the statistics generation algorithm */
static constexpr uint32_t kAwbStatsSizeX = 16;
static constexpr uint32_t kAwbStatsSizeY = 12;
@@ -39,7 +44,11 @@ class Awb : public Algorithm
public:
Awb();
+ int init(IPAContext &context, const ValueNode &tuningData) override;
int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
+ void queueRequest(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const ControlList &controls) override;
void prepare(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
ipu3_uapi_params *params) override;
@@ -49,15 +58,7 @@ public:
ControlList &metadata) override;
private:
- struct AwbStatus {
- double temperatureK;
- double redGain;
- double greenGain;
- double blueGain;
- };
-
-private:
- void calculateWBGains(const ipu3_uapi_stats_3a *stats);
+ Ipu3AwbStats calculateRgbMeans(const ipu3_uapi_stats_3a *stats);
void generateZones();
void generateAwbStats(const ipu3_uapi_stats_3a *stats);
void clearAwbStats();
@@ -67,7 +68,7 @@ private:
std::vector<RGB<double>> zones_;
Accumulator awbStats_[kAwbStatsSizeX * kAwbStatsSizeY];
- AwbStatus asyncResults_;
+ AwbAlgorithm<UQ<3, 13>> awbAlgo_;
uint32_t stride_;
uint32_t cellsPerZoneX_;
@@ -109,6 +109,11 @@ namespace libcamera::ipa::ipu3 {
* \brief Maximum analogue gain supported with the configured sensor
*/
+/**
+ * \var IPAActiveState::awb
+ * \brief Active auto-white balance parameters for the IPA
+ */
+
/**
* \var IPASessionConfiguration::sensor
* \brief Sensor-specific configuration of the IPA
@@ -123,6 +128,11 @@ namespace libcamera::ipa::ipu3 {
* \brief Sensor output resolution
*/
+/**
+ * \var IPASessionConfiguration::awb
+ * \brief Auto-white balance specific session configuration data
+ */
+
/**
* \var IPAActiveState::agc
* \brief Context for the Automatic Gain Control algorithm
@@ -139,26 +149,6 @@ namespace libcamera::ipa::ipu3 {
* The gain should be adapted to the sensor specific gain code before applying.
*/
-/**
- * \var IPAActiveState::awb
- * \brief Context for the Automatic White Balance algorithm
- *
- * \var IPAActiveState::awb.gains
- * \brief White balance gains
- *
- * \var IPAActiveState::awb.gains.red
- * \brief White balance gain for R channel
- *
- * \var IPAActiveState::awb.gains.green
- * \brief White balance gain for G channel
- *
- * \var IPAActiveState::awb.gains.blue
- * \brief White balance gain for B channel
- *
- * \var IPAActiveState::awb.temperatureK
- * \brief Estimated color temperature
- */
-
/**
* \var IPAActiveState::toneMapping
* \brief Context for ToneMapping and Gamma control
@@ -187,4 +177,9 @@ namespace libcamera::ipa::ipu3 {
* \brief Analogue gain multiplier
*/
+/**
+ * \var IPAFrameContext::awb
+ * \brief Per-frame auto-white balance parameters for the IPA
+ */
+
} /* namespace libcamera::ipa::ipu3 */
@@ -15,6 +15,7 @@
#include <libcamera/controls.h>
#include <libcamera/geometry.h>
+#include <libipa/awb.h>
#include <libipa/fc_queue.h>
namespace libcamera {
@@ -44,6 +45,8 @@ struct IPASessionConfiguration {
utils::Duration lineDuration;
Size size;
} sensor;
+
+ ipa::awb::Session awb;
};
struct IPAActiveState {
@@ -60,15 +63,7 @@ struct IPAActiveState {
uint32_t exposureMode;
} agc;
- struct {
- struct {
- double red;
- double green;
- double blue;
- } gains;
-
- double temperatureK;
- } awb;
+ ipa::awb::ActiveState awb;
struct {
double gamma;
@@ -81,6 +76,8 @@ struct IPAFrameContext : public FrameContext {
uint32_t exposure;
double gain;
} sensor;
+
+ ipa::awb::FrameContext awb;
};
struct IPAContext {
Port the IPU3 Awb algorithm to use the new libipa implementation of AwbAlgorithm. In this implementation the awbAlgo_ class member is initialised as UQ<3, 13> following the IPU3 params format documentation. Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com> --- src/ipa/ipu3/algorithms/agc.cpp | 12 ++- src/ipa/ipu3/algorithms/awb.cpp | 194 ++++++++++++++++++---------------------- src/ipa/ipu3/algorithms/awb.h | 21 ++--- src/ipa/ipu3/ipa_context.cpp | 35 ++++---- src/ipa/ipu3/ipa_context.h | 15 ++-- 5 files changed, 128 insertions(+), 149 deletions(-)