libcamera: stabilize AWB with a white point locus clamp + damping (r8)
The softISP AWB is pure gray world, so panning swings ColourGains with the frame average (white wall renders green when the frame is warm-dominated, dark desk renders red when cool-dominated) and the CCM, interpolated over a CT estimated from those gains, amplifies and quantizes the swings. Patch 0015 (ours, original work) adds optional per-sensor Awb tuning to the simple IPA: a piecewise-linear locus of calibrated illuminant white points in gain space that the gray-world estimate is clamped to (clampMargin), a per-stats-frame EMA on the applied gains (damping) with a fast-converge path for persistent illuminant changes, and CT interpolated from the applied gains' position along the locus - on the same scale as the tuning file's ccms table - instead of the generic estimateCCT(). Tuning files without whitePoints keep the previous behaviour exactly, so every other sensor is unaffected. imx896.yaml gains the locus from the stock tuning blob's 10-point per-illuminant AWB calibration (white points and their exactly reciprocal gain triplets, cross-validated): the 7 on-locus points as nodes, the 3 fluorescent points admitted via clampMargin 0.35. The 5000 K label is the blob's own proven anchor; 2856 K (A) is locked by the fluorescent trio projecting onto the 2856-5000 chord at ~3960 K (TL84); the rest are geometry-derived approximations. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
a5e3d1626c
commit
48cedac3c3
3 changed files with 437 additions and 2 deletions
|
|
@ -0,0 +1,408 @@
|
||||||
|
From 62fc9d05460aa0ad5e1e4c8f81ae966796c066c9 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
|
||||||
|
Date: Mon, 17 Aug 2026 03:23:30 +0200
|
||||||
|
Subject: [PATCH] ipa: simple: awb: Add optional white point locus clamp and
|
||||||
|
damping
|
||||||
|
|
||||||
|
The simple IPA's AWB is pure gray world: every stats frame the applied
|
||||||
|
gains become the frame-average G/R and G/B ratios of that frame. Two
|
||||||
|
artifacts follow on real hardware. Panning swings the gains with the
|
||||||
|
scene composition - a warm-dominated frame renders a white wall green,
|
||||||
|
a cool-dominated one renders a dark desk red - and the CCM, which is
|
||||||
|
interpolated over a colour temperature estimated from those same gains,
|
||||||
|
amplifies and quantizes each swing.
|
||||||
|
|
||||||
|
Add optional per-sensor tuning for the Awb algorithm:
|
||||||
|
|
||||||
|
- Awb:
|
||||||
|
whitePoints: # ordered by strictly ascending ct
|
||||||
|
- { ct: 2856, gains: [ 1.378, 2.769 ] } # [ R gain, B gain ]
|
||||||
|
- { ct: 5000, gains: [ 2.037, 1.809 ] }
|
||||||
|
clampMargin: 0.35 # max distance from the locus in gain space
|
||||||
|
damping: 0.8 # per-stats-frame EMA weight of the old gains
|
||||||
|
|
||||||
|
whitePoints defines a piecewise-linear locus of calibrated illuminant
|
||||||
|
white points in (R gain, B gain) space. The gray-world estimate is
|
||||||
|
computed as before, then clamped to within clampMargin of the locus:
|
||||||
|
scene colours can no longer pull the white balance arbitrarily far from
|
||||||
|
colours a real illuminant would produce, while off-locus (greenish,
|
||||||
|
e.g. fluorescent) illuminants within the margin still get corrected.
|
||||||
|
The applied gains follow the clamped estimate through a per-stats-frame
|
||||||
|
exponential moving average so composition changes fade in smoothly; a
|
||||||
|
large difference persisting for several stats frames is a real
|
||||||
|
illuminant change and halves the damping so scene cuts converge quickly
|
||||||
|
instead of crawling.
|
||||||
|
|
||||||
|
The colour temperature is no longer estimated with the generic
|
||||||
|
estimateCCT() matrix but interpolated from the applied gains' position
|
||||||
|
along the locus, so the Ccm algorithm always picks a matrix consistent
|
||||||
|
with the applied white balance, on the same ct scale the tuning file's
|
||||||
|
ccms table uses.
|
||||||
|
|
||||||
|
A tuning file without whitePoints keeps the previous behaviour exactly.
|
||||||
|
|
||||||
|
Signed-off-by: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
|
||||||
|
---
|
||||||
|
src/ipa/simple/algorithms/awb.cpp | 240 ++++++++++++++++++++++++++++--
|
||||||
|
src/ipa/simple/algorithms/awb.h | 30 ++++
|
||||||
|
2 files changed, 257 insertions(+), 13 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp
|
||||||
|
index 05155c8..83dcece 100644
|
||||||
|
--- a/src/ipa/simple/algorithms/awb.cpp
|
||||||
|
+++ b/src/ipa/simple/algorithms/awb.cpp
|
||||||
|
@@ -1,34 +1,160 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024-2026 Red Hat Inc.
|
||||||
|
+ * Copyright (C) 2026 Jorijn van der Graaf
|
||||||
|
*
|
||||||
|
* Auto white balance
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "awb.h"
|
||||||
|
|
||||||
|
+#include <algorithm>
|
||||||
|
+#include <cmath>
|
||||||
|
+#include <limits>
|
||||||
|
#include <numeric>
|
||||||
|
+#include <optional>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <libcamera/base/log.h>
|
||||||
|
|
||||||
|
#include <libcamera/control_ids.h>
|
||||||
|
|
||||||
|
+#include "libcamera/internal/value_node.h"
|
||||||
|
+#include "libcamera/internal/vector.h"
|
||||||
|
+
|
||||||
|
#include "libipa/colours.h"
|
||||||
|
#include "simple/ipa_context.h"
|
||||||
|
|
||||||
|
+namespace {
|
||||||
|
+
|
||||||
|
+constexpr double kDefaultClampMargin = 0.35;
|
||||||
|
+constexpr double kDefaultDamping = 0.8;
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * When the damped gains stay farther than kFastConvergeDistance (in gain
|
||||||
|
+ * space) from the clamped gray-world target for more than
|
||||||
|
+ * kFastConvergeFrames consecutive stats frames, the difference is a real
|
||||||
|
+ * illuminant change rather than scene-composition noise: halve the damping
|
||||||
|
+ * so the change converges quickly instead of crawling.
|
||||||
|
+ */
|
||||||
|
+constexpr double kFastConvergeDistance = 0.15;
|
||||||
|
+constexpr unsigned int kFastConvergeFrames = 3;
|
||||||
|
+
|
||||||
|
+/* Start from the white point closest to daylight. */
|
||||||
|
+constexpr double kInitialCT = 5000.0;
|
||||||
|
+
|
||||||
|
+} /* namespace */
|
||||||
|
+
|
||||||
|
namespace libcamera {
|
||||||
|
|
||||||
|
LOG_DEFINE_CATEGORY(IPASoftAwb)
|
||||||
|
|
||||||
|
namespace ipa::soft::algorithms {
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Optional per-sensor tuning:
|
||||||
|
+ *
|
||||||
|
+ * - Awb:
|
||||||
|
+ * whitePoints: # ordered by strictly ascending ct
|
||||||
|
+ * - { ct: 2856, gains: [ 1.378, 2.769 ] } # [ R gain, B gain ]
|
||||||
|
+ * - { ct: 5000, gains: [ 2.037, 1.809 ] }
|
||||||
|
+ * clampMargin: 0.35 # max distance from the locus in gain space
|
||||||
|
+ * damping: 0.8 # per-stats-frame EMA weight of the old gains
|
||||||
|
+ *
|
||||||
|
+ * whitePoints defines a piecewise-linear locus of calibrated illuminant
|
||||||
|
+ * white points in (R gain, B gain) space. The gray-world estimate is
|
||||||
|
+ * clamped to within clampMargin of the locus, bounding how far scene
|
||||||
|
+ * colours can pull the white balance while still allowing off-locus
|
||||||
|
+ * (greenish, e.g. fluorescent) illuminants. The applied gains follow the
|
||||||
|
+ * clamped estimate through an exponential moving average, and the colour
|
||||||
|
+ * temperature fed to the Ccm algorithm is interpolated from the applied
|
||||||
|
+ * gains' position along the locus. Without whitePoints the plain
|
||||||
|
+ * gray-world algorithm runs unchanged.
|
||||||
|
+ */
|
||||||
|
+int Awb::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)
|
||||||
|
+{
|
||||||
|
+ const ValueNode &wps = tuningData["whitePoints"];
|
||||||
|
+ if (wps.isEmpty())
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ if (!wps.isList() || wps.size() < 2) {
|
||||||
|
+ LOG(IPASoftAwb, Error)
|
||||||
|
+ << "whitePoints must be a list of at least two points";
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (const ValueNode &node : wps.asList()) {
|
||||||
|
+ std::optional<double> ct = node["ct"].get<double>();
|
||||||
|
+ std::optional<Vector<double, 2>> gains =
|
||||||
|
+ node["gains"].get<Vector<double, 2>>();
|
||||||
|
+ if (!ct || !gains || (*gains)[0] <= 0.0 || (*gains)[1] <= 0.0) {
|
||||||
|
+ LOG(IPASoftAwb, Error)
|
||||||
|
+ << "whitePoints entries need a ct and positive gains [ R, B ]";
|
||||||
|
+ whitePoints_.clear();
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+ if (!whitePoints_.empty()) {
|
||||||
|
+ const WhitePoint &prev = whitePoints_.back();
|
||||||
|
+ if (*ct <= prev.ct) {
|
||||||
|
+ LOG(IPASoftAwb, Error)
|
||||||
|
+ << "whitePoints must be ordered by strictly ascending ct";
|
||||||
|
+ whitePoints_.clear();
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+ if ((*gains)[0] == prev.rGain && (*gains)[1] == prev.bGain) {
|
||||||
|
+ LOG(IPASoftAwb, Error)
|
||||||
|
+ << "consecutive whitePoints must differ in gains";
|
||||||
|
+ whitePoints_.clear();
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ whitePoints_.push_back({ *ct, (*gains)[0], (*gains)[1] });
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ clampMargin_ = tuningData["clampMargin"].get<double>(kDefaultClampMargin);
|
||||||
|
+ damping_ = tuningData["damping"].get<double>(kDefaultDamping);
|
||||||
|
+ if (clampMargin_ <= 0.0 || damping_ < 0.0 || damping_ >= 1.0) {
|
||||||
|
+ LOG(IPASoftAwb, Error)
|
||||||
|
+ << "clampMargin must be positive and damping within [0, 1)";
|
||||||
|
+ whitePoints_.clear();
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ LOG(IPASoftAwb, Info)
|
||||||
|
+ << "White point locus: " << whitePoints_.size() << " points, "
|
||||||
|
+ << whitePoints_.front().ct << "-" << whitePoints_.back().ct
|
||||||
|
+ << " K, clamp margin " << clampMargin_
|
||||||
|
+ << ", damping " << damping_;
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
int Awb::configure(IPAContext &context,
|
||||||
|
[[maybe_unused]] const IPAConfigInfo &configInfo)
|
||||||
|
{
|
||||||
|
auto &gains = context.activeState.awb.gains;
|
||||||
|
gains = { { 1.0, 1.0, 1.0 } };
|
||||||
|
|
||||||
|
+ fastFrames_ = 0;
|
||||||
|
+
|
||||||
|
+ if (whitePoints_.empty())
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Start from the calibrated white point closest to daylight rather
|
||||||
|
+ * than from unity gains, so the first frames are roughly plausible
|
||||||
|
+ * while the loop converges.
|
||||||
|
+ */
|
||||||
|
+ const WhitePoint *initial = &whitePoints_.front();
|
||||||
|
+ for (const WhitePoint &wp : whitePoints_) {
|
||||||
|
+ if (std::abs(wp.ct - kInitialCT) < std::abs(initial->ct - kInitialCT))
|
||||||
|
+ initial = ℘
|
||||||
|
+ }
|
||||||
|
+ gains = { { static_cast<float>(initial->rGain), 1.0,
|
||||||
|
+ static_cast<float>(initial->bGain) } };
|
||||||
|
+ context.activeState.awb.temperatureK =
|
||||||
|
+ static_cast<unsigned int>(std::lround(initial->ct));
|
||||||
|
+
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -43,6 +169,39 @@ void Awb::prepare(IPAContext &context,
|
||||||
|
params->gains = gains;
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Find the closest point on the piecewise-linear white point locus, and
|
||||||
|
+ * the colour temperature interpolated along the containing segment.
|
||||||
|
+ */
|
||||||
|
+Awb::LocusPosition Awb::closestOnLocus(double rGain, double bGain) const
|
||||||
|
+{
|
||||||
|
+ LocusPosition best{};
|
||||||
|
+ double bestDist2 = std::numeric_limits<double>::max();
|
||||||
|
+
|
||||||
|
+ for (unsigned int i = 0; i + 1 < whitePoints_.size(); i++) {
|
||||||
|
+ const WhitePoint &p0 = whitePoints_[i];
|
||||||
|
+ const WhitePoint &p1 = whitePoints_[i + 1];
|
||||||
|
+ const double dr = p1.rGain - p0.rGain;
|
||||||
|
+ const double db = p1.bGain - p0.bGain;
|
||||||
|
+ const double len2 = dr * dr + db * db;
|
||||||
|
+ double t = ((rGain - p0.rGain) * dr + (bGain - p0.bGain) * db) / len2;
|
||||||
|
+ t = std::clamp(t, 0.0, 1.0);
|
||||||
|
+ const double qr = p0.rGain + t * dr;
|
||||||
|
+ const double qb = p0.bGain + t * db;
|
||||||
|
+ const double dist2 = (rGain - qr) * (rGain - qr) +
|
||||||
|
+ (bGain - qb) * (bGain - qb);
|
||||||
|
+ if (dist2 < bestDist2) {
|
||||||
|
+ bestDist2 = dist2;
|
||||||
|
+ best.rGain = qr;
|
||||||
|
+ best.bGain = qb;
|
||||||
|
+ best.ct = p0.ct + t * (p1.ct - p0.ct);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ best.distance = std::sqrt(bestDist2);
|
||||||
|
+ return best;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
void Awb::process(IPAContext &context,
|
||||||
|
[[maybe_unused]] const uint32_t frame,
|
||||||
|
IPAFrameContext &frameContext,
|
||||||
|
@@ -73,24 +232,79 @@ void Awb::process(IPAContext &context,
|
||||||
|
*/
|
||||||
|
const RGB<uint64_t> sum = stats->sum_.max(offset + minValid) - offset;
|
||||||
|
|
||||||
|
+ auto &gains = context.activeState.awb.gains;
|
||||||
|
+
|
||||||
|
+ if (whitePoints_.empty()) {
|
||||||
|
+ /*
|
||||||
|
+ * Calculate red and blue gains for AWB.
|
||||||
|
+ * Clamp max gain at 4.0, this also avoids 0 division.
|
||||||
|
+ */
|
||||||
|
+ 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;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
- * Calculate red and blue gains for AWB.
|
||||||
|
- * Clamp max gain at 4.0, this also avoids 0 division.
|
||||||
|
+ * Gray-world estimate. The sums are at least minValid, so the
|
||||||
|
+ * divisions are safe; the locus clamp below bounds the result.
|
||||||
|
*/
|
||||||
|
- 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(),
|
||||||
|
- } };
|
||||||
|
+ double rTarget = static_cast<double>(sum.g()) / sum.r();
|
||||||
|
+ double bTarget = static_cast<double>(sum.g()) / sum.b();
|
||||||
|
+
|
||||||
|
+ /* Clamp the estimate to within clampMargin_ of the locus. */
|
||||||
|
+ const LocusPosition projection = closestOnLocus(rTarget, bTarget);
|
||||||
|
+ if (projection.distance > clampMargin_) {
|
||||||
|
+ const double f = clampMargin_ / projection.distance;
|
||||||
|
+ rTarget = projection.rGain + (rTarget - projection.rGain) * f;
|
||||||
|
+ bTarget = projection.bGain + (bTarget - projection.bGain) * f;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- 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);
|
||||||
|
+ /*
|
||||||
|
+ * Damp the applied gains toward the clamped target so that changes
|
||||||
|
+ * of the frame composition (e.g. panning) do not swing the colours,
|
||||||
|
+ * converging faster when the difference is large and persistent.
|
||||||
|
+ */
|
||||||
|
+ const double err = std::hypot(rTarget - gains.r(), bTarget - gains.b());
|
||||||
|
+ if (err > kFastConvergeDistance)
|
||||||
|
+ fastFrames_++;
|
||||||
|
+ else
|
||||||
|
+ fastFrames_ = 0;
|
||||||
|
+ double damping = damping_;
|
||||||
|
+ if (fastFrames_ > kFastConvergeFrames)
|
||||||
|
+ damping /= 2.0;
|
||||||
|
+
|
||||||
|
+ const double rNew = damping * gains.r() + (1.0 - damping) * rTarget;
|
||||||
|
+ const double bNew = damping * gains.b() + (1.0 - damping) * bTarget;
|
||||||
|
+ gains = { { static_cast<float>(rNew), 1.0, static_cast<float>(bNew) } };
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * The colour temperature the Ccm algorithm consumes follows from
|
||||||
|
+ * where the applied gains sit along the locus, so the matrix always
|
||||||
|
+ * matches the applied white balance.
|
||||||
|
+ */
|
||||||
|
+ const LocusPosition applied = closestOnLocus(rNew, bNew);
|
||||||
|
+ context.activeState.awb.temperatureK =
|
||||||
|
+ static_cast<unsigned int>(std::lround(applied.ct));
|
||||||
|
+ metadata.set(controls::ColourTemperature,
|
||||||
|
+ context.activeState.awb.temperatureK);
|
||||||
|
|
||||||
|
LOG(IPASoftAwb, Debug)
|
||||||
|
- << "gain R/B: " << gains << "; temperature: "
|
||||||
|
- << context.activeState.awb.temperatureK;
|
||||||
|
+ << "gain R/B: " << gains
|
||||||
|
+ << "; locus distance: " << projection.distance
|
||||||
|
+ << "; temperature: " << context.activeState.awb.temperatureK;
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_IPA_ALGORITHM(Awb, "Awb")
|
||||||
|
diff --git a/src/ipa/simple/algorithms/awb.h b/src/ipa/simple/algorithms/awb.h
|
||||||
|
index ad993f3..ef4e4f3 100644
|
||||||
|
--- a/src/ipa/simple/algorithms/awb.h
|
||||||
|
+++ b/src/ipa/simple/algorithms/awb.h
|
||||||
|
@@ -1,12 +1,15 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024-2025 Red Hat Inc.
|
||||||
|
+ * Copyright (C) 2026 Jorijn van der Graaf
|
||||||
|
*
|
||||||
|
* Auto white balance
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
+#include <vector>
|
||||||
|
+
|
||||||
|
#include "algorithm.h"
|
||||||
|
|
||||||
|
namespace libcamera {
|
||||||
|
@@ -19,6 +22,7 @@ public:
|
||||||
|
Awb() = default;
|
||||||
|
~Awb() = default;
|
||||||
|
|
||||||
|
+ int init(IPAContext &context, const ValueNode &tuningData) override;
|
||||||
|
int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
|
||||||
|
void prepare(IPAContext &context,
|
||||||
|
const uint32_t frame,
|
||||||
|
@@ -29,6 +33,32 @@ public:
|
||||||
|
IPAFrameContext &frameContext,
|
||||||
|
const SwIspStats *stats,
|
||||||
|
ControlList &metadata) override;
|
||||||
|
+
|
||||||
|
+private:
|
||||||
|
+ struct WhitePoint {
|
||||||
|
+ double ct;
|
||||||
|
+ double rGain;
|
||||||
|
+ double bGain;
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ struct LocusPosition {
|
||||||
|
+ double rGain;
|
||||||
|
+ double bGain;
|
||||||
|
+ double ct;
|
||||||
|
+ double distance;
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ LocusPosition closestOnLocus(double rGain, double bGain) const;
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Calibrated white point locus in (R gain, B gain) space, ordered by
|
||||||
|
+ * strictly ascending colour temperature. Empty when the tuning file
|
||||||
|
+ * provides no white points; the plain gray-world algorithm runs then.
|
||||||
|
+ */
|
||||||
|
+ std::vector<WhitePoint> whitePoints_;
|
||||||
|
+ double clampMargin_ = 0.0;
|
||||||
|
+ double damping_ = 0.0;
|
||||||
|
+ unsigned int fastFrames_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace ipa::soft::algorithms */
|
||||||
|
|
@ -3,7 +3,7 @@ maintainer="Robert Mader <robert.mader@collabora.com>"
|
||||||
pkgname=libcamera
|
pkgname=libcamera
|
||||||
_pkgver=0.7.2
|
_pkgver=0.7.2
|
||||||
pkgver=9999$_pkgver
|
pkgver=9999$_pkgver
|
||||||
pkgrel=7
|
pkgrel=8
|
||||||
pkgdesc="Linux camera framework"
|
pkgdesc="Linux camera framework"
|
||||||
url="https://libcamera.org/"
|
url="https://libcamera.org/"
|
||||||
arch="all"
|
arch="all"
|
||||||
|
|
@ -72,6 +72,7 @@ source="https://gitlab.freedesktop.org/camera/libcamera/-/archive/v$_pkgver/libc
|
||||||
0012-libcamera-software_isp-af-Harden-the-lens-control-ha.patch
|
0012-libcamera-software_isp-af-Harden-the-lens-control-ha.patch
|
||||||
0013-libcamera-software_isp-af-Add-AfMode-and-focus-witho.patch
|
0013-libcamera-software_isp-af-Add-AfMode-and-focus-witho.patch
|
||||||
0014-libcamera-software_isp-af-Re-baseline-the-scene-refe.patch
|
0014-libcamera-software_isp-af-Re-baseline-the-scene-refe.patch
|
||||||
|
0015-ipa-simple-awb-Add-optional-white-point-locus-clamp-.patch
|
||||||
qcam.desktop
|
qcam.desktop
|
||||||
$_tuning_files
|
$_tuning_files
|
||||||
"
|
"
|
||||||
|
|
@ -199,6 +200,7 @@ c8ddc64ab943d9b215f4c997f2629e4403744c0ddb5de88cd42d384506a27c0762191eddd4d13559
|
||||||
8734e27966bd1bc5a4b0324a3d8a3ee4ab371f6d1536500b2bb44d49bde874c3fd6f244312332226c352e4716ba12fb72bf51e64b9e97cf6fa646aa6dfef3277 0012-libcamera-software_isp-af-Harden-the-lens-control-ha.patch
|
8734e27966bd1bc5a4b0324a3d8a3ee4ab371f6d1536500b2bb44d49bde874c3fd6f244312332226c352e4716ba12fb72bf51e64b9e97cf6fa646aa6dfef3277 0012-libcamera-software_isp-af-Harden-the-lens-control-ha.patch
|
||||||
dce81c2863ff6b9374115325f4af14ac5d396a6e2aee12f87d55a47d1d0bda860ae2af5a9d6885b126e20a12b7f3f40e49a2b9d738f94c04177bff6149da64e1 0013-libcamera-software_isp-af-Add-AfMode-and-focus-witho.patch
|
dce81c2863ff6b9374115325f4af14ac5d396a6e2aee12f87d55a47d1d0bda860ae2af5a9d6885b126e20a12b7f3f40e49a2b9d738f94c04177bff6149da64e1 0013-libcamera-software_isp-af-Add-AfMode-and-focus-witho.patch
|
||||||
18ed6a03fbe3bcd750fac280831d1e62f2a2e35f76880535f2dc16ff196308512535e077ab0d8a923ad241090713edb53d1777e0dd6e5dcc10de495db1277052 0014-libcamera-software_isp-af-Re-baseline-the-scene-refe.patch
|
18ed6a03fbe3bcd750fac280831d1e62f2a2e35f76880535f2dc16ff196308512535e077ab0d8a923ad241090713edb53d1777e0dd6e5dcc10de495db1277052 0014-libcamera-software_isp-af-Re-baseline-the-scene-refe.patch
|
||||||
|
b01deba75c093ad4312ffa829ddbe5d745f2dfb199a3f00cdd52a8cb972fbe58bfb7cd94fa825b30d65047ac0c371ecf3ec0d0e356f090119d11086552f514be 0015-ipa-simple-awb-Add-optional-white-point-locus-clamp-.patch
|
||||||
22167a4eceb6d1b40b0b7c45fdf116c71684f5340de7f767535cb8e160ad9d2ae0f00cb3d461f73a344520a48a4641cf46226841d78bee06bfbfd2a91337f754 qcam.desktop
|
22167a4eceb6d1b40b0b7c45fdf116c71684f5340de7f767535cb8e160ad9d2ae0f00cb3d461f73a344520a48a4641cf46226841d78bee06bfbfd2a91337f754 qcam.desktop
|
||||||
2ee566653b17d565d2c0de67cbe6ebad25df5aac6051cad78e6bae52016fa2ca0247e964d735ac776b3e0bd447ead9c4fcb26cd7629228cdba3ceb91b46f378f hi846.yaml
|
2ee566653b17d565d2c0de67cbe6ebad25df5aac6051cad78e6bae52016fa2ca0247e964d735ac776b3e0bd447ead9c4fcb26cd7629228cdba3ceb91b46f378f hi846.yaml
|
||||||
55dab9dcb9b1982143b9d63e4e51138c5bffd7c8f4a8e4e122750b3ad9d475446956bc297d9f564819ade4be75a575d06633a064e14f8ba3ef77986783c5f067 imx355.yaml
|
55dab9dcb9b1982143b9d63e4e51138c5bffd7c8f4a8e4e122750b3ad9d475446956bc297d9f564819ade4be75a575d06633a064e14f8ba3ef77986783c5f067 imx355.yaml
|
||||||
|
|
@ -207,7 +209,7 @@ dce81c2863ff6b9374115325f4af14ac5d396a6e2aee12f87d55a47d1d0bda860ae2af5a9d6885b1
|
||||||
55dab9dcb9b1982143b9d63e4e51138c5bffd7c8f4a8e4e122750b3ad9d475446956bc297d9f564819ade4be75a575d06633a064e14f8ba3ef77986783c5f067 imx376.yaml
|
55dab9dcb9b1982143b9d63e4e51138c5bffd7c8f4a8e4e122750b3ad9d475446956bc297d9f564819ade4be75a575d06633a064e14f8ba3ef77986783c5f067 imx376.yaml
|
||||||
2ee566653b17d565d2c0de67cbe6ebad25df5aac6051cad78e6bae52016fa2ca0247e964d735ac776b3e0bd447ead9c4fcb26cd7629228cdba3ceb91b46f378f imx519.yaml
|
2ee566653b17d565d2c0de67cbe6ebad25df5aac6051cad78e6bae52016fa2ca0247e964d735ac776b3e0bd447ead9c4fcb26cd7629228cdba3ceb91b46f378f imx519.yaml
|
||||||
55dab9dcb9b1982143b9d63e4e51138c5bffd7c8f4a8e4e122750b3ad9d475446956bc297d9f564819ade4be75a575d06633a064e14f8ba3ef77986783c5f067 imx858.yaml
|
55dab9dcb9b1982143b9d63e4e51138c5bffd7c8f4a8e4e122750b3ad9d475446956bc297d9f564819ade4be75a575d06633a064e14f8ba3ef77986783c5f067 imx858.yaml
|
||||||
cbb6204657fdb4bc35e9ee686200900291c8bb74678d3f19f8ed30120e5d939836534f8724726c58e8b7aa2eb99e99edace96350b11472fbbf4878cc1025c3f5 imx896.yaml
|
918bc5fc2035ba32af617a9f00a98dda1b8272cab04d7cea12bdf26b7e0d9811a8c8c2061191e00174d01a1ad991a3327bf418ce89dfb3610d06787f14389fa0 imx896.yaml
|
||||||
2ee566653b17d565d2c0de67cbe6ebad25df5aac6051cad78e6bae52016fa2ca0247e964d735ac776b3e0bd447ead9c4fcb26cd7629228cdba3ceb91b46f378f s5k3l6xx.yaml
|
2ee566653b17d565d2c0de67cbe6ebad25df5aac6051cad78e6bae52016fa2ca0247e964d735ac776b3e0bd447ead9c4fcb26cd7629228cdba3ceb91b46f378f s5k3l6xx.yaml
|
||||||
d59020e62520ee622282c85ce7bcdfe453c99becf48cc1005c5f8a4bb8b2511b19c89f38fb3c994e7c6a79d1f84e2ed5f5a09b4c5bdc1dc4a44b071dbccf0d44 s5kjn1.yaml
|
d59020e62520ee622282c85ce7bcdfe453c99becf48cc1005c5f8a4bb8b2511b19c89f38fb3c994e7c6a79d1f84e2ed5f5a09b4c5bdc1dc4a44b071dbccf0d44 s5kjn1.yaml
|
||||||
"
|
"
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,21 @@
|
||||||
# dynamic estimate ran at 48, leaving a 16-code residual the CCM amplified
|
# dynamic estimate ran at 48, leaving a 16-code residual the CCM amplified
|
||||||
# into colour casts in dark regions.
|
# into colour casts in dark regions.
|
||||||
#
|
#
|
||||||
|
# Awb whitePoints: the 10-point per-illuminant AWB calibration from the
|
||||||
|
# same blob (white points @0x1c69ed, gain triplets @0x1c2dba — exact
|
||||||
|
# reciprocals of each other, cross-validated to float precision). The
|
||||||
|
# locus below carries the 7 on-locus points as (R gain, B gain), warm to
|
||||||
|
# cool. The 3 fluorescent points (idx 4-6) are deliberately NOT locus
|
||||||
|
# nodes: they sit ~0.33 gain units off the A-D50 chord (green side) and
|
||||||
|
# would zigzag the locus; clampMargin 0.35 admits them instead. CT
|
||||||
|
# labels: 5000 K is PROVEN (the blob's default gain + CCT anchor at
|
||||||
|
# index 3); 2856 K (illuminant A) is locked by two independent checks —
|
||||||
|
# sensor-typical A ratios AND the fluorescent trio projecting onto the
|
||||||
|
# 2856-5000 chord at ~3960 K, i.e. exactly TL84; the remaining labels
|
||||||
|
# are geometry-derived approximations (the blob stores no CCT list).
|
||||||
|
# Same ct scale as the ccms table below. damping = per-stats-frame EMA
|
||||||
|
# (stats run every 4th frame), ~1 s settling at 30 fps.
|
||||||
|
#
|
||||||
# PROVENANCE / LICENSE: the matrices are numeric calibration data read out
|
# PROVENANCE / LICENSE: the matrices are numeric calibration data read out
|
||||||
# of the proprietary vendor blob (measured spectral response of this
|
# of the proprietary vendor blob (measured spectral response of this
|
||||||
# sensor, not authored expression). Shipping decided 2026-08-17 by the
|
# sensor, not authored expression). Shipping decided 2026-08-17 by the
|
||||||
|
|
@ -32,6 +47,16 @@ algorithms:
|
||||||
- BlackLevel:
|
- BlackLevel:
|
||||||
blackLevel: 4096
|
blackLevel: 4096
|
||||||
- Awb:
|
- Awb:
|
||||||
|
whitePoints:
|
||||||
|
- { ct: 1900, gains: [ 0.878997, 4.771635 ] }
|
||||||
|
- { ct: 2300, gains: [ 1.119141, 3.340533 ] }
|
||||||
|
- { ct: 2856, gains: [ 1.377957, 2.769483 ] }
|
||||||
|
- { ct: 5000, gains: [ 2.037341, 1.809127 ] }
|
||||||
|
- { ct: 6504, gains: [ 2.367133, 1.574040 ] }
|
||||||
|
- { ct: 7504, gains: [ 2.741927, 1.357219 ] }
|
||||||
|
- { ct: 9500, gains: [ 4.508486, 1.012164 ] }
|
||||||
|
clampMargin: 0.35
|
||||||
|
damping: 0.8
|
||||||
- Ccm:
|
- Ccm:
|
||||||
ccms:
|
ccms:
|
||||||
- ct: 2800
|
- ct: 2800
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue