@@ -15,11 +15,20 @@
namespace libcamera {
/**
- * \brief Struct that holds the statistics for the Software ISP
+ * \brief Statistics for the Software ISP
*
- * The struct value types are large enough to not overflow.
- * Should they still overflow for some reason, no check is performed and they
- * wrap around.
+ * The software ISP computes two types of statistics:
+ *
+ * - The accumulator counts the number of non-saturated pixels and sums their
+ * R, G and B channels individually. Pixels that have any saturated channel
+ * are skipped as they don't provide accurate colour information due to
+ * clipping.
+ *
+ * - The histogram groups pixels in bins based on their luminance and counts
+ * the number of pixels in each bin. Saturated pixels are included.
+ *
+ * The value types are large enough to not overflow. Should they still overflow
+ * for some reason, no check is performed and they wrap around.
*/
struct SwIspStats {
/**
@@ -28,9 +37,13 @@ struct SwIspStats {
*/
bool valid;
/**
- * \brief Sums of colour channels of all the sampled pixels
+ * \brief Sums of colour channels of all the unsaturated pixels
*/
RGB<uint64_t> sum_;
+ /**
+ * \brief Number of unsaturated pixels accumulated in \a sum_
+ */
+ uint64_t numPixels_;
/**
* \brief Number of bins in the yHistogram
*/
@@ -7,7 +7,6 @@
#include "awb.h"
-#include <numeric>
#include <stdint.h>
#include <libcamera/base/log.h>
@@ -118,16 +117,23 @@ SoftIspAwbStats Awb::calculateRgbMeans(IPAContext &context,
if (!stats->valid)
return {};
- const SwIspStats::Histogram &histogram = stats->yHistogram;
const uint8_t blackLevel = context.activeState.blc.level;
+ const uint64_t nPixels = stats->numPixels_;
+
+ /*
+ * In the unlikely case where all pixels are saturated, return invalid
+ * statistics. This shouldn't happen in normal conditions but can be
+ * triggered by severe over-exposure.
+ */
+ if (!nPixels)
+ 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;
@@ -167,6 +167,14 @@ SwStatsCpu::SwStatsCpu(const CameraManager &cm)
<< "Failed to create shared memory for statistics";
}
+/*
+ * Hardcode the AWB stats saturation threshold to 95% of the maximum value.
+ *
+ * \todo Investigate if this should be made dynamic, based on sensor
+ * (non-)linearity and noise level.
+ */
+static constexpr unsigned int kSaturationThreshold = 0.95 * 256;
+
static constexpr unsigned int kRedYMul = 77; /* 0.299 * 256 */
static constexpr unsigned int kGreenYMul = 150; /* 0.587 * 256 */
static constexpr unsigned int kBlueYMul = 29; /* 0.114 * 256 */
@@ -177,22 +185,29 @@ static constexpr unsigned int kBlueYMul = 29; /* 0.114 * 256 */
\
uint64_t sumR = 0; \
uint64_t sumG = 0; \
- uint64_t sumB = 0;
+ uint64_t sumB = 0; \
+ uint64_t nSamples = 0;
-#define SWSTATS_ACCUMULATE_LINE_STATS(div) \
- sumR += r; \
- sumG += g; \
- sumB += b; \
- \
- yVal = r * kRedYMul; \
- yVal += g * kGreenYMul; \
- yVal += b * kBlueYMul; \
+#define SWSTATS_ACCUMULATE_LINE_STATS(div) \
+ if (r < kSaturationThreshold * (div) && \
+ g < kSaturationThreshold * (div) && \
+ b < kSaturationThreshold * (div)) { \
+ sumR += r; \
+ sumG += g; \
+ sumB += b; \
+ nSamples++; \
+ } \
+ \
+ yVal = r * kRedYMul; \
+ yVal += g * kGreenYMul; \
+ yVal += b * kBlueYMul; \
stats.yHistogram[yVal * SwIspStats::kYHistogramSize / (256 * 256 * (div))]++;
#define SWSTATS_FINISH_LINE_STATS() \
stats.sum_.r() += sumR; \
stats.sum_.g() += sumG; \
- stats.sum_.b() += sumB;
+ stats.sum_.b() += sumB; \
+ stats.numPixels_ += nSamples;
void SwStatsCpu::statsBGGR8Line0(const uint8_t *src[], SwIspStats &stats)
{
@@ -391,6 +406,7 @@ void SwStatsCpu::startFrame(uint32_t frame)
for (auto &s : stats_) {
s.sum_ = RGB<uint64_t>({ 0, 0, 0 });
+ s.numPixels_ = 0;
s.yHistogram.fill(0);
}
}
@@ -408,9 +424,11 @@ void SwStatsCpu::finishFrame(uint32_t frame, uint32_t bufferId)
if (valid) {
sharedStats_->sum_ = RGB<uint64_t>({ 0, 0, 0 });
+ sharedStats_->numPixels_ = 0;
sharedStats_->yHistogram.fill(0);
for (const auto &s : stats_) {
sharedStats_->sum_ += s.sum_;
+ sharedStats_->numPixels_ += s.numPixels_;
for (unsigned int j = 0; j < SwIspStats::kYHistogramSize; j++)
sharedStats_->yHistogram[j] += s.yHistogram[j];
}