@@ -24,7 +24,7 @@ struct DebayerParams {
RGB<float> blackLevel = RGB<float>({ 0.0, 0.0, 0.0 });
float gamma = 1.0;
float contrastExp = 1.0;
- RGB<float> gains = RGB<float>({ 1.0, 1.0, 1.0 });
+ RGB<double> gains = RGB<double>({ 1.0, 1.0, 1.0 });
};
} /* namespace libcamera */
@@ -15,7 +15,6 @@
#include <libcamera/control_ids.h>
#include "libipa/colours.h"
-#include "simple/ipa_context.h"
namespace libcamera {
@@ -23,41 +22,79 @@ LOG_DEFINE_CATEGORY(IPASoftAwb)
namespace ipa::soft::algorithms {
+/*
+ * \todo Replace it with a proper Lux algorithm
+ */
+static constexpr unsigned int kDefaultLux = 500;
+
+class SimpleAwbStats final : public AwbStats
+{
+public:
+ SimpleAwbStats() {}
+ SimpleAwbStats(const RGB<double> &rgbMeans)
+ : AwbStats(rgbMeans)
+ {
+ }
+
+ /* Minimum mean value below which AWB can't operate. */
+ double minColourValue() const override
+ {
+ return 0.2;
+ }
+};
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
+int Awb::init(IPAContext &context, const ValueNode &tuningData)
+{
+ return awbAlgo_.init(tuningData, context.ctrlMap);
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::configure
+ */
int Awb::configure(IPAContext &context,
[[maybe_unused]] const IPAConfigInfo &configInfo)
{
- auto &gains = context.activeState.awb.gains;
- gains = { { 1.0, 1.0, 1.0 } };
+ return awbAlgo_.configure(context.activeState.awb,
+ context.configuration.awb);
+}
- 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);
}
+/**
+ * \copydoc libcamera::ipa::Algorithm::prepare
+ */
void Awb::prepare(IPAContext &context,
[[maybe_unused]] const uint32_t frame,
IPAFrameContext &frameContext,
DebayerParams *params)
{
- auto &gains = context.activeState.awb.gains;
+ awbAlgo_.prepare(context.activeState.awb, frameContext.awb);
- frameContext.gains = gains;
- params->gains = gains;
+ params->gains = frameContext.awb.gains;
}
-void Awb::process(IPAContext &context,
- [[maybe_unused]] const uint32_t frame,
- IPAFrameContext &frameContext,
- const SwIspStats *stats,
- ControlList &metadata)
+SimpleAwbStats Awb::calculateRgbMeans(IPAContext &context,
+ const SwIspStats *stats) const
{
+ if (!stats->valid)
+ return {};
+
const SwIspStats::Histogram &histogram = stats->yHistogram;
const uint8_t blackLevel = context.activeState.blc.level;
- metadata.set(controls::ColourGains, { frameContext.gains.r(),
- frameContext.gains.b() });
-
- if (!stats->valid)
- return;
-
/*
* Black level must be subtracted to get the correct AWB ratios, they
* would be off if they were computed from the whole brightness range
@@ -67,30 +104,37 @@ void Awb::process(IPAContext &context,
histogram.begin(), histogram.end(), uint64_t(0));
const uint64_t offset = blackLevel * nPixels;
const uint64_t minValid = 1;
+
/*
* Make sure the sums are at least minValid, while preventing unsigned
* integer underflow.
*/
const RGB<uint64_t> sum = stats->sum_.max(offset + minValid) - offset;
+ RGB<double> rgbMeans = { { static_cast<double>(sum.r() / nPixels),
+ static_cast<double>(sum.g() / nPixels),
+ static_cast<double>(sum.b() / nPixels) } };
+
/*
- * Calculate red and blue gains for AWB.
- * Clamp max gain at 4.0, this also avoids 0 division.
+ * \todo Determine the minimum allowed thresholds from the mean
+ * but we currently have the sum - not the mean value!
+ *
+ * Currently set to SimpleAwbStats::minColourValue() = 0.2.
*/
- auto &gains = context.activeState.awb.gains;
- gains = { {
- sum.r() <= sum.g() / 4 ? 4.0f : static_cast<float>(sum.g()) / sum.r(),
- 1.0,
- sum.b() <= sum.g() / 4 ? 4.0f : static_cast<float>(sum.g()) / sum.b(),
- } };
-
- RGB<double> rgbGains{ { 1 / gains.r(), 1 / gains.g(), 1 / gains.b() } };
- context.activeState.awb.temperatureK = estimateCCT(rgbGains);
- metadata.set(controls::ColourTemperature, context.activeState.awb.temperatureK);
-
- LOG(IPASoftAwb, Debug)
- << "gain R/B: " << gains << "; temperature: "
- << context.activeState.awb.temperatureK;
+ return SimpleAwbStats(rgbMeans);
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::process
+ */
+void Awb::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext, const SwIspStats *stats,
+ ControlList &metadata)
+{
+ SimpleAwbStats awbStats = calculateRgbMeans(context, stats);
+
+ awbAlgo_.process(context.activeState.awb, frameContext.awb, awbStats,
+ kDefaultLux, metadata);
}
REGISTER_IPA_ALGORITHM(Awb, "Awb")
@@ -7,19 +7,37 @@
#pragma once
+#include <libcamera/controls.h>
+
+#include "libcamera/internal/software_isp/debayer_params.h"
+#include "libcamera/internal/value_node.h"
+
+#include "libipa/awb.h"
+#include "libipa/fixedpoint.h"
+#include "simple/ipa_context.h"
+
#include "algorithm.h"
namespace libcamera {
namespace ipa::soft::algorithms {
+class SimpleAwbStats;
+
class Awb : public Algorithm
{
public:
Awb() = default;
~Awb() = default;
+ int init(IPAContext &context,
+ const ValueNode &tuningData) override;
int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
+
+ void queueRequest(IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const ControlList &controls) override;
void prepare(IPAContext &context,
const uint32_t frame,
IPAFrameContext &frameContext,
@@ -29,6 +47,17 @@ public:
IPAFrameContext &frameContext,
const SwIspStats *stats,
ControlList &metadata) override;
+
+private:
+ SimpleAwbStats calculateRgbMeans(IPAContext &context,
+ const SwIspStats *stats) const;
+
+ /*
+ * There actually is no Q register format for SoftISP, but allow the
+ * colour gains to range in the [0.0f, 15.999f] interval, which seems
+ * reasonable.
+ */
+ AwbAlgorithm<UQ<4, 8>> awbAlgo_;
};
} /* namespace ipa::soft::algorithms */
@@ -44,7 +44,7 @@ int Ccm::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)
void Ccm::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)
{
- const unsigned int ct = context.activeState.awb.temperatureK;
+ const unsigned int ct = frameContext.awb.temperatureK;
/* Change CCM only on bigger temperature changes. */
if (!currentCcm_ ||
@@ -16,6 +16,7 @@
#include "libcamera/internal/matrix.h"
#include "libcamera/internal/vector.h"
+#include <libipa/awb.h>
#include <libipa/fc_queue.h>
#include "core_ipa_interface.h"
@@ -25,6 +26,8 @@ namespace libcamera {
namespace ipa::soft {
struct IPASessionConfiguration {
+ ipa::awb::Session awb;
+
struct {
int32_t exposureMin, exposureMax;
double againMin, againMax, again10, againMinStep;
@@ -36,6 +39,8 @@ struct IPASessionConfiguration {
};
struct IPAActiveState {
+ ipa::awb::ActiveState awb;
+
struct {
int32_t exposure;
double again;
@@ -48,11 +53,6 @@ struct IPAActiveState {
double lastGain;
} blc;
- struct {
- RGB<float> gains;
- unsigned int temperatureK;
- } awb;
-
Matrix<float, 3, 3> combinedMatrix;
struct {
@@ -64,6 +64,8 @@ struct IPAActiveState {
};
struct IPAFrameContext : public FrameContext {
+ ipa::awb::FrameContext awb;
+
Matrix<float, 3, 3> ccm;
struct {
@@ -71,8 +73,6 @@ struct IPAFrameContext : public FrameContext {
double gain;
} sensor;
- RGB<float> gains;
-
float gamma;
std::optional<float> contrast;
std::optional<float> saturation;
@@ -1051,7 +1051,7 @@ void DebayerCpu::updateLookupTables(const DebayerParams ¶ms)
auto &blue = swapRedBlueGains_ ? red_ : blue_;
for (unsigned int i = 0; i < kRGBLookupSize; i++) {
/* Apply gamma after gain! */
- const RGB<float> lutGains = (gains * i / div).min(gammaTableSize - 1);
+ const RGB<double> lutGains = (gains * i / div).min(gammaTableSize - 1);
red[i] = gammaTable_[static_cast<unsigned int>(lutGains.r())];
green[i] = gammaTable_[static_cast<unsigned int>(lutGains.g())];
blue[i] = gammaTable_[static_cast<unsigned int>(lutGains.b())];