Files
external_libcamera/src/ipa/simple/algorithms/awb.cpp
Milan Zamazal bb2e6d0833 libcamera: ipa: simple: Fix multiplication order in Awb
The matrix multiplication in Awb is swapped: the gains should be applied
after combinedMatrix, i.e. on the left side.  The mistake happened when
`ccm' was replaced with combinedMatrix and gainMatrix multiplication was
moved to Awb.  While CCM must be applied after gains, the gains must be
applied after the combined matrix, which no longer corresponds to CCM in
Awb.

Since there is currently no algorithm modifying combinedMatrix before
Awb, combinedMatrix is an identity matrix there and the wrong order
doesn't influence the output at the moment.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2026-02-06 00:39:33 +02:00

113 lines
2.9 KiB
C++

/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2024-2026 Red Hat Inc.
*
* Auto white balance
*/
#include "awb.h"
#include <numeric>
#include <stdint.h>
#include <libcamera/base/log.h>
#include <libcamera/control_ids.h>
#include "libipa/colours.h"
#include "simple/ipa_context.h"
#include "control_ids.h"
namespace libcamera {
LOG_DEFINE_CATEGORY(IPASoftAwb)
namespace ipa::soft::algorithms {
int Awb::configure(IPAContext &context,
[[maybe_unused]] const IPAConfigInfo &configInfo)
{
auto &gains = context.activeState.awb.gains;
gains = { { 1.0, 1.0, 1.0 } };
return 0;
}
void Awb::prepare(IPAContext &context,
[[maybe_unused]] const uint32_t frame,
IPAFrameContext &frameContext,
DebayerParams *params)
{
auto &gains = context.activeState.awb.gains;
Matrix<float, 3, 3> gainMatrix = { { gains.r(), 0, 0,
0, gains.g(), 0,
0, 0, gains.b() } };
context.activeState.combinedMatrix =
gainMatrix * context.activeState.combinedMatrix;
frameContext.gains.red = gains.r();
frameContext.gains.blue = gains.b();
params->gains = gains;
}
void Awb::process(IPAContext &context,
[[maybe_unused]] const uint32_t frame,
IPAFrameContext &frameContext,
const SwIspStats *stats,
ControlList &metadata)
{
const SwIspStats::Histogram &histogram = stats->yHistogram;
const uint8_t blackLevel = context.activeState.blc.level;
const float mdGains[] = {
static_cast<float>(frameContext.gains.red),
static_cast<float>(frameContext.gains.blue)
};
metadata.set(controls::ColourGains, mdGains);
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
* rather than from the sensor range.
*/
const uint64_t nPixels = std::accumulate(
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;
/*
* Calculate red and blue gains for AWB.
* Clamp max gain at 4.0, this also avoids 0 division.
*/
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;
}
REGISTER_IPA_ALGORITHM(Awb, "Awb")
} /* namespace ipa::soft::algorithms */
} /* namespace libcamera */