[v5,04/11] software-isp: Align statistics with the visible sensor crop
diff mbox series

Message ID 20261029152058.896209-5-halfline@gmail.com
State Rejected
Headers show
Series
  • software-isp: Align metering and report the configured crop
Related show

Commit Message

Ray Strode Oct. 29, 2026, 3:20 p.m. UTC
From: Ray Strode <rstrode@redhat.com>

This commit changes statistics gathering to follow the sensor area visible
in the GPU output.

The GPU debayer path fills the requested output with a centered crop of the
sensor image. CPU statistics read the original sensor buffer, but their
window uses the output dimensions and starts at the buffer's origin. A
centered origin is calculated but never passed to the statistics engine.
Metering therefore covers a different sensor region when the image is
cropped or scaled.

The window is now derived in sensor coordinates, accounting for Bayer
border removal and cropping to the output aspect ratio, and centered in the
sensor buffer. Changing resolution without changing aspect ratio leaves the
rectangle unchanged.

Signed-off-by: Ray Strode <rstrode@redhat.com>
---
 .../internal/software_isp/debayer_geometry.h  | 23 +++++++++++++++++++
 .../internal/software_isp/meson.build         |  1 +
 src/libcamera/software_isp/debayer_egl.cpp    | 14 ++++-------
 src/libcamera/software_isp/debayer_egl.h      |  1 -
 4 files changed, 28 insertions(+), 11 deletions(-)
 create mode 100644 include/libcamera/internal/software_isp/debayer_geometry.h

Patch
diff mbox series

diff --git a/include/libcamera/internal/software_isp/debayer_geometry.h b/include/libcamera/internal/software_isp/debayer_geometry.h
new file mode 100644
index 00000000..8812a3cf
--- /dev/null
+++ b/include/libcamera/internal/software_isp/debayer_geometry.h
@@ -0,0 +1,23 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026, Red Hat Inc.
+ *
+ * Geometry for scaling and cropping debayered images
+ */
+
+#pragma once
+
+
+#include <libcamera/geometry.h>
+
+namespace libcamera {
+
+/* Return the visible sensor rectangle, before statistics Bayer alignment. */
+inline Rectangle debayerCrop(const Size &inputSize, const Size &nativeSize,
+			     const Size &outputSize)
+{
+	const Size visibleSize = nativeSize.boundedToAspectRatio(outputSize);
+	return visibleSize.centeredTo(Rectangle(inputSize).center());
+}
+
+} /* namespace libcamera */
diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
index df7c3b97..e18597ed 100644
--- a/include/libcamera/internal/software_isp/meson.build
+++ b/include/libcamera/internal/software_isp/meson.build
@@ -1,9 +1,10 @@ 
 # SPDX-License-Identifier: CC0-1.0
 
 libcamera_internal_headers += files([
     'benchmark.h',
+    'debayer_geometry.h',
     'debayer_params.h',
     'software_isp.h',
     'swisp_stats.h',
     'swstats_cpu.h',
 ])
diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
index 3356070c..5a27a38d 100644
--- a/src/libcamera/software_isp/debayer_egl.cpp
+++ b/src/libcamera/software_isp/debayer_egl.cpp
@@ -1,56 +1,57 @@ 
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
  * Copyright (C) 2024, Linaro Ltd.
  *
  * Authors:
  * Bryan O'Donoghue <bryan.odonoghue@linaro.org>
  *
  */
 
 #include "debayer_egl.h"
 
 #include <algorithm>
 #include <assert.h>
 #include <memory>
 #include <span>
 #include <stdlib.h>
 #include <string>
 #include <tuple>
 #include <vector>
 
 #include <libcamera/base/utils.h>
 
 #include <libcamera/formats.h>
 
 #include "libcamera/internal/formats.h"
 #include "libcamera/internal/framebuffer.h"
+#include "libcamera/internal/software_isp/debayer_geometry.h"
 
 #include "../shaders/glsl_shaders.h"
 
 namespace libcamera {
 
 /**
  * \class DebayerEGL
  * \brief Class for debayering using an EGL Shader
  *
  * Implements an EGL shader based debayering solution.
  */
 
 /**
  * \brief Construct a DebayerEGL object
  * \param[in] stats Statistics processing object
  * \param[in] cm The camera manager
  * \param[in] display The EGL display to use
  */
 DebayerEGL::DebayerEGL(std::unique_ptr<SwStatsCpu> stats, const CameraManager &cm, EGLDisplay display)
 	: Debayer(cm), stats_(std::move(stats)), egl_(display)
 {
 }
 
 DebayerEGL::~DebayerEGL()
 {
 }
 
 int DebayerEGL::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config)
 {
 	BayerFormat bayerFormat =
@@ -304,72 +305,65 @@  int DebayerEGL::configure(const StreamConfiguration &inputCfg,
 
 	inputConfig_.stride = inputCfg.stride;
 	inputPixelFormat_ = inputCfg.pixelFormat;
 	width_ = inputCfg.size.width;
 	height_ = inputCfg.size.height;
 
 	if (outputCfgs.size() != 1) {
 		LOG(Debayer, Error)
 			<< "Unsupported number of output streams: "
 			<< outputCfgs.size();
 		return -EINVAL;
 	}
 
 	const StreamConfiguration &outputCfg = outputCfgs[0];
 	SizeRange outSizeRange = sizes(inputCfg.pixelFormat, inputCfg.size);
 	std::tie(outputConfig_.stride, outputConfig_.frameSize) =
 		strideAndFrameSize(outputCfg.pixelFormat, outputCfg.size);
 
 	if (!outSizeRange.contains(outputCfg.size) || outputConfig_.stride != outputCfg.stride) {
 		LOG(Debayer, Error)
 			<< "Invalid output size/stride: "
 			<< "\n  " << outputCfg.size << " (" << outSizeRange << ")"
 			<< "\n  " << outputCfg.stride << " (" << outputConfig_.stride << ")";
 		return -EINVAL;
 	}
 
 	outputPixelFormat_ = outputCfg.pixelFormat;
 	outputSize_ = outputCfg.size;
 	nativeOutputSize_ = outSizeRange.max;
 
-	window_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) &
-		    ~(inputConfig_.patternSize.width - 1);
-	window_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) &
-		    ~(inputConfig_.patternSize.height - 1);
-	window_.width = outputCfg.size.width;
-	window_.height = outputCfg.size.height;
-
 	/*
-	 * Don't pass x,y from window_ since process() already adjusts for it.
-	 * But crop the window to 2/3 of its width and height for speedup.
+	 * The GPU scales the visible sensor crop to outputSize_. Statistics
+	 * consume the original input buffer, so use sensor coordinates here.
 	 */
-	stats_->setWindow(Rectangle(window_.size()));
+	stats_->setWindow(debayerCrop(inputCfg.size, nativeOutputSize_, outputSize_));
 
 	inputBufferCount_ = inputCfg.bufferCount;
 	outputBufferCount_ = outputCfg.bufferCount;
 
 	return 0;
 }
 
 Size DebayerEGL::patternSize(PixelFormat inputFormat)
 {
 	DebayerEGL::DebayerInputConfig config;
 
 	if (getInputConfig(inputFormat, config) != 0)
 		return {};
 
 	return config.patternSize;
 }
 
 std::vector<PixelFormat> DebayerEGL::formats(PixelFormat inputFormat)
 {
 	DebayerEGL::DebayerInputConfig config;
 
 	if (getInputConfig(inputFormat, config) != 0)
 		return std::vector<PixelFormat>();
 
 	return config.outputFormats;
 }
 
 std::tuple<unsigned int, unsigned int>
 DebayerEGL::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
 {
diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h
index cbbc6a45..6910fa49 100644
--- a/src/libcamera/software_isp/debayer_egl.h
+++ b/src/libcamera/software_isp/debayer_egl.h
@@ -93,41 +93,40 @@  private:
 	float firstRed_y_;
 	GLint attributeVertex_;
 	GLint attributeTexture_;
 	GLint textureUniformStep_;
 	GLint textureUniformSize_;
 	GLint textureUniformStrideFactor_;
 	GLint textureUniformBayerFirstRed_;
 	GLint textureUniformProjMatrix_;
 
 	GLint textureUniformBayerDataIn_;
 
 	/* Per-frame AWB gains */
 	GLint awbUniformDataIn_;
 
 	/* Lens shading correction */
 	GLint textureUniformLsc_;
 
 	/* Represent per-frame CCM as a uniform vector of floats 3 x 3 */
 	GLint ccmUniformDataIn_;
 
 	/* Black Level compensation */
 	GLint blackLevelUniformDataIn_;
 
 	/* Gamma */
 	GLint gammaUniformDataIn_;
 
 	/* Contrast */
 	GLint contrastExpUniformDataIn_;
 
 	Size nativeOutputSize_;
-	Rectangle window_;
 	std::unique_ptr<SwStatsCpu> stats_;
 	eGL egl_;
 	uint32_t width_;
 	uint32_t height_;
 	GLint glFormat_;
 	unsigned int bytesPerPixel_;
 	uint32_t shaderStridePixels_;
 };
 
 } /* namespace libcamera */