@@ -376,6 +376,7 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,
},
.exposure = frameContext.sensor.exposure,
.gain = frameContext.sensor.gain,
+ .lux = frameContext.lux.lux,
}}, metadata);
} else {
agc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {}, metadata);
@@ -209,7 +209,7 @@ void Awb::process(IPAContext &context,
RppX1AwbStats awbStats = calculateRgbMeans(frameContext, *awb);
awbAlgo_.process(context.activeState.awb, frameContext.awb, awbStats,
- 0, metadata);
+ frameContext.lux.lux, metadata);
}
RppX1AwbStats Awb::calculateRgbMeans(const IPAFrameContext &frameContext,
new file mode 100644
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026, Ideas On Board
+ *
+ * RPP-X1 Lux estimation
+ */
+
+#include "lux.h"
+
+#include <libcamera/base/utils.h>
+
+#include <libcamera/control_ids.h>
+
+#include "libipa/histogram.h"
+#include "libipa/lux.h"
+
+/**
+ * \file lux.h
+ */
+
+namespace libcamera {
+
+namespace ipa::rppx1::algorithms {
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
+int Lux::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)
+{
+ return lux_.parseTuningData(tuningData);
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::prepare
+ */
+void Lux::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ [[maybe_unused]] RppX1Params *params)
+{
+ frameContext.lux.lux = context.activeState.lux.lux;
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::process
+ */
+void Lux::process(IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const RppX1Stats *stats,
+ ControlList &metadata)
+{
+ /*
+ * Report the lux level used by algorithms to prepare this frame
+ * not the lux level *of* this frame.
+ */
+ metadata.set(controls::Lux, frameContext.lux.lux);
+
+ utils::Duration exposureTime = context.configuration.agc.lineDuration *
+ frameContext.sensor.exposure;
+ double gain = frameContext.sensor.gain;
+
+ /* \todo Deduplicate the histogram calculation from AGC */
+ const auto histPost = stats->block<StatsType::HistPost>();
+ if (!histPost)
+ return;
+
+ Histogram yHist(histPost->hist_bins, [](uint32_t x) { return x >> 4; });
+
+ context.activeState.lux.lux = lux_.estimateLux(exposureTime, gain, 1.0, yHist);
+}
+
+REGISTER_IPA_ALGORITHM(Lux, "Lux")
+
+} /* namespace ipa::rppx1::algorithms */
+
+} /* namespace libcamera */
new file mode 100644
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026, Ideas On Board
+ *
+ * RPP-X1 Lux estimation
+ */
+
+#pragma once
+
+#include "libipa/lux.h"
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+namespace ipa::rppx1::algorithms {
+
+class Lux : public Algorithm
+{
+public:
+ int init(IPAContext &context, const ValueNode &tuningData) override;
+ void prepare(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ RppX1Params *params) override;
+ void process(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const RppX1Stats *stats,
+ ControlList &metadata) override;
+
+private:
+ ipa::Lux lux_;
+};
+
+} /* namespace ipa::rppx1::algorithms */
+
+} /* namespace libcamera */
@@ -4,4 +4,5 @@ rppx1_ipa_algorithms = files([
'agc.cpp',
'awb.cpp',
'blc.cpp',
+ 'lux.cpp',
])
@@ -42,6 +42,10 @@ struct IPAActiveState {
} agc;
ipa::awb::ActiveState awb;
+
+ struct {
+ double lux;
+ } lux;
};
struct IPAFrameContext : public FrameContext {
@@ -56,6 +60,10 @@ struct IPAFrameContext : public FrameContext {
} agc;
ipa::awb::FrameContext awb;
+
+ struct {
+ double lux;
+ } lux;
};
struct IPAContext {