@@ -101,8 +101,21 @@ private:
/* Bayer 12 bpp packed */
void statsBGGR12PLine0(const uint8_t *src[], SwIspStats &stats);
void statsGBRG12PLine0(const uint8_t *src[], SwIspStats &stats);
+ /* Mono 8 bpp unpacked */
+ void statsMono8Line0(const uint8_t *src[], SwIspStats &stats);
+ /* Mono 10 bpp unpacked */
+ void statsMono10Line0(const uint8_t *src[], SwIspStats &stats);
+ /* Mono 10 bpp packed */
+ void statsMono10PLine0(const uint8_t *src[], SwIspStats &stats);
+ /* Mono 12 bpp unpacked */
+ void statsMono12Line0(const uint8_t *src[], SwIspStats &stats);
+ /* Mono 12 bpp packed */
+ void statsMono12PLine0(const uint8_t *src[], SwIspStats &stats);
+ /* Mono 16 bpp unpacked */
+ void statsMono16Line0(const uint8_t *src[], SwIspStats &stats);
void processBayerFrame2(MappedFrameBuffer &in);
+ void processMonoFrame2(MappedFrameBuffer &in);
processFrameFn processFrame_;
@@ -375,6 +375,124 @@ void SwStatsCpu::statsGBRG12PLine0(const uint8_t *src[], SwIspStats &stats)
SWSTATS_FINISH_LINE_STATS()
}
+/*
+ * Horizontal sub-sampling optimization: evaluates 1 pixel out of 4 (x += 4)
+ * to reduce CPU overhead. Each sample gets a weight of +4 to preserve global
+ * frame statistical density required by the Auto Exposure (AE) algorithm.
+ */
+inline void storeMonoAccumulation(SwIspStats &stats, uint64_t value)
+{
+ stats.sum_.r() += value;
+ stats.sum_.g() += value;
+ stats.sum_.b() += value;
+}
+
+void SwStatsCpu::statsMono8Line0(const uint8_t *src[], SwIspStats &stats)
+{
+ const uint8_t *cur = src[1] + window_.x;
+ uint64_t sum = 0;
+
+ for (unsigned int x = 0; x < window_.width; x += 4) {
+ sum += *cur;
+ stats.yHistogram[*cur >> 2] += 4;
+ cur += 4;
+ }
+ /* Process 4 pixels per iteration : x << 2 */
+ storeMonoAccumulation(stats,sum << 2);
+}
+
+void SwStatsCpu::statsMono10Line0(const uint8_t *src[], SwIspStats &stats)
+{
+ /*
+ * Little-Endian optimization: Unpacked 10-bit format stores data
+ * right-aligned in a 2-byte container. Reading via a 16-bit pointer
+ * allows clean data access before applying the bit-shift.
+ */
+ const uint16_t *cur = reinterpret_cast<const uint16_t *>(src[1] + window_.x * 2);
+ uint64_t sum = 0;
+
+ for (unsigned int x = 0; x < window_.width; x += 4) {
+ uint16_t val = *cur;
+ sum += val;
+ stats.yHistogram[(val >> 4) & 0x3F] += 4;
+ cur += 4;
+ }
+ /* Process 4 pixels per iteration : x 10 >> 2 to 8bits and x << 2 */
+ storeMonoAccumulation(stats,sum);
+}
+
+void SwStatsCpu::statsMono12Line0(const uint8_t *src[], SwIspStats &stats)
+{
+ /*
+ * 12-bit unpacked processing. Data is read via a native 16-bit pointer
+ * and right-shifted to fit the histogram range.
+ */
+ const uint16_t *cur = reinterpret_cast<const uint16_t *>(src[1] + window_.x * 2);
+ uint64_t sum = 0;
+
+ for (unsigned int x = 0; x < window_.width; x += 4) {
+ uint16_t val = *cur;
+ sum += val;
+ stats.yHistogram[(val >> 6) & 0x3F] += 4;
+ cur += 4;
+ }
+ /* Process 4 pixels per iteration : 12 >> 4 to 8bits and x << 2 */
+ storeMonoAccumulation(stats,sum >> 2);
+}
+
+void SwStatsCpu::statsMono10PLine0(const uint8_t *src[], SwIspStats &stats)
+{
+ /*
+ * MIPI CSI-2 10-bit packed: 4 pixels in 5 bytes. Stepping 5 bytes
+ * evaluates the head MSB pixel of each block as a native 8-bit value.
+ */
+ const uint8_t *cur = src[1] + window_.x * 5 / 4;
+ uint64_t sum = 0;
+
+ for (unsigned int x = 0; x < window_.width; x += 4) {
+ uint8_t val = *cur;
+ sum += val;
+ stats.yHistogram[val >> 2] += 4;
+ cur += 5;
+ }
+ /* Process 4 pixels per iteration : first byte x << 2 */
+ storeMonoAccumulation(stats,sum << 2);
+}
+
+void SwStatsCpu::statsMono12PLine0(const uint8_t *src[], SwIspStats &stats)
+{
+ /*
+ * MIPI CSI-2 12-bit packed: 2 pixels in 3 bytes. Stepping 6 bytes
+ * advances by two full blocks for optimal data alignment.
+ */
+ const uint8_t *cur = src[1] + window_.x * 3 / 2;
+ uint64_t sum = 0;
+
+ for (unsigned int x = 0; x < window_.width; x += 4) {
+ uint8_t val = *cur;
+ sum += val;
+ stats.yHistogram[val >> 2] += 4;
+ cur += 6;
+ }
+ /* Process 4 pixels per iteration : first byte x << 2 */
+ storeMonoAccumulation(stats,sum << 2);
+}
+
+void SwStatsCpu::statsMono16Line0(const uint8_t *src[], SwIspStats &stats)
+{
+ const uint16_t *cur = reinterpret_cast<const uint16_t *>(src[1] + window_.x * 2);
+ uint64_t sum = 0;
+
+ for (unsigned int x = 0; x < window_.width; x += 4) {
+ uint16_t val = *cur;
+ sum += val;
+ stats.yHistogram[val >> 10] += 4;
+ cur += 4;
+ }
+ /* Process 4 pixels per iteration : 16 >> 8 to 8bits and x << 2 */
+ storeMonoAccumulation(stats,sum >> 6);
+}
+
/**
* \brief Reset state to start statistics gathering for a new frame
* \param[in] frame The frame number
@@ -494,6 +612,49 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg, unsigned int stat
uint8_t bitDepth = bayerFormat.bitDepth;
+ if (bayerFormat.order == BayerFormat::Order::MONO) {
+ /* No skip line for mono */
+ ySkipMask_ = 0;
+ xShift_ = 0;
+ sumShift_ = 0;
+ processFrame_ = &SwStatsCpu::processMonoFrame2;
+
+ if (bayerFormat.packing == BayerFormat::Packing::None) {
+ patternSize_.width = 2;
+ patternSize_.height = 2;
+
+ switch (bitDepth) {
+ case 8:
+ stats0_ = &SwStatsCpu::statsMono8Line0;
+ return 0;
+ case 10:
+ stats0_ = &SwStatsCpu::statsMono10Line0;
+ return 0;
+ case 12:
+ stats0_ = &SwStatsCpu::statsMono12Line0;
+ return 0;
+ case 16:
+ stats0_ = &SwStatsCpu::statsMono16Line0;
+ return 0;
+ }
+ }
+ if (bayerFormat.packing == BayerFormat::Packing::CSI2) {
+ patternSize_.height = 2;
+
+ switch (bitDepth) {
+ case 10:
+ patternSize_.width = 4;
+ stats0_ = &SwStatsCpu::statsMono10PLine0;
+ return 0;
+ case 12:
+ patternSize_.width = 2;
+ stats0_ = &SwStatsCpu::statsMono12PLine0;
+ return 0;
+ }
+ }
+ return -EINVAL;
+ }
+
if ((bitDepth == 10 || bitDepth == 12) &&
bayerFormat.packing == BayerFormat::Packing::CSI2) {
if (bitDepth == 10)
@@ -594,6 +755,25 @@ void SwStatsCpu::processBayerFrame2(MappedFrameBuffer &in)
}
}
+void SwStatsCpu::processMonoFrame2(MappedFrameBuffer &in)
+{
+ const uint8_t *src = in.planes()[0].data();
+ const uint8_t *linePointers[3];
+
+ src += window_.y * stride_;
+
+ for (unsigned int y = 0; y < window_.height; y++) {
+ if (y & ySkipMask_) {
+ src += stride_;
+ continue;
+ }
+
+ linePointers[1] = src;
+ (this->*stats0_)(linePointers, stats_[0]);
+ src += stride_;
+ }
+}
+
/**
* \brief Calculate statistics for a frame in one go
* \param[in] frame The frame number
Signed-off-by: Alain CousiniƩ <alain.cousinie@laposte.net> --- .../internal/software_isp/swstats_cpu.h | 13 ++ src/libcamera/software_isp/swstats_cpu.cpp | 180 ++++++++++++++++++ 2 files changed, 193 insertions(+) -- 2.55.0