From patchwork Thu Oct 29 15:20:51 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ray Strode X-Patchwork-Id: 28707 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id E6F39C324C for ; Thu, 29 Oct 2026 15:21:47 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9C300690D3; Thu, 29 Oct 2026 16:21:47 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="TjHoEutW"; dkim-atps=neutral Received: from mail-ua1-x92d.google.com (mail-ua1-x92d.google.com [IPv6:2607:f8b0:4864:20::92d]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1DA64690A0 for ; Thu, 29 Oct 2026 16:21:46 +0100 (CET) Received: by mail-ua1-x92d.google.com with SMTP id a1e0cc1a2514c-9912c47367cso654715241.1 for ; Thu, 29 Oct 2026 08:21:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793287305; x=1793892105; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=eiwkemerKEObigwDJiP0dfbVwgplTC6iMDJv0APNU3c=; b=TjHoEutWbYsuhuvHu5vUc1O+uuVOFaTpvv8esTdCcMoMKTy/EloutBTE2k1w1KNzzi +5ZEo8ot9PXcXIdWyYEO7rQmCjt1c7jySh1nodcYjKfSKUy5c03+1LuqMJqhRetJFt7z ns8P/6LtO9PonmED3c5qViwrwy9whQyMglzmnkJ1j3F8Ecnf94pCvylqo14G3y0stHaZ 3T+hM4AGDaJDypvdPJyFKSEkQdkIrPSUgS/CgEcXJCg77iAz3Xv90e/uA8DpX0FnywRN ZJJUcz9mzaVqN8mZaeRbD5JvbB0sqe2ig3FYWrsUt6/qkqiesdMBmTh0Mb5Db6fGrQyQ QbQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793287305; x=1793892105; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=eiwkemerKEObigwDJiP0dfbVwgplTC6iMDJv0APNU3c=; b=mCjUTe48hnHU+Xb7DbINFEOvIclwcrExgY1yLxecLCqceJZSquA7inpqVpGRiL/dgn FpH6wTkjbd0QvyqueebXzNWR7cJoScHkvGusKqRCC8LcVcKtIPuZ/RDr5QQa/cdfUzjo kqIzSuMF6odzZtcgXBBPBuRvC7i7WIPvA/t5UkUXH5Ce7Bi6qkLemlDE6P5jO4cs8s0A CgNSKkSIqUwMgwJNxtZhzaLqM6QgbhIr4phj4sPGjz7GRmza6d38GlUwMqNmKwOB0tzg Dggx2Fba+cou/1fjj25bTuNnm1mhK2OM1Dda1i7bw0sOGZEz2SsZl/GJDmdKn55QYZhy 2qCA== X-Gm-Message-State: AFq9FYJGlxEU4sEc4ZBm6+PorYETKYAmh6IAEeDb50tNrqI50QcMT8Uj Slg0/iZfdqbl0f3/oCv83d0Uu33hRDaH5J5IlHZc+LZN8wKfVxysFRNKskG2k7ZuyXU= X-Gm-Gg: AYBFou3Gu9WH+28np5VhCV5Ul1LYVaQv5jTHAjYD1bxVHcMMfwYUgaAbRA+dTBFgW8k 4EtkvDidyT0oNWDhGuk51Vw+QLuSsTHUNARoQrZEN2VGIrJ8ej8LRkP2HtdyHy5N7GGTDEsg9tz F8XN3SU7gkKHsRYSYAG9uj21Ww16Yxx8tFVTL2bSsqKKdTPhe72bwtrxC9ojsMd4LHKR+BPSmqc nA5JDMMrY49YWNASirxMyUelFEs8j1yxh9cCBoU+Hh36V3DzFALDLD2CTGbK8OsQANDL/Jt3Ikw HwS4zlh11I4wzonTRXNXNd0nk8KipDOc1pSfb9+KwMrY42SQSaKCNYg4/CL96/QLNxolTmEvRVN GHTfg7C/QLha6FP+hgmlkCam5MkmXePSFOrKrmIpTg/t+G4ST9RuZ5vTQPHCyaXaqKKPdpNWzXd ijiqM4Utsj1IxWjnDJ4eCEuEFOq2RHJgoU+ahsZLj3ZV+hft7lNJPBLCTMYEuNI2YyX6EzqcR+t vT65l9UbH1iSSp5zPyVdGRaN0wKgOIm2/2qTevc4NdVJSlnGPlRTdiDJjmgbontuyUAx22FvzA/ RqByaMgqYQ/i3EXc4ASGk8KvJ7/nC3TWYijABbN9N0kaSSsjNP4BwXv1 X-Received: by 2002:a05:6102:f0f:b0:7cc:ace:24ad with SMTP id ada2fe7eead31-7d053b5df5dmr994055137.0.1793287303816; Thu, 29 Oct 2026 08:21:43 -0700 (PDT) Received: from localhost ([144.121.52.163]) by smtp.gmail.com with ESMTPSA id a1e0cc1a2514c-991f74e5e22sm206563241.4.2026.10.29.08.21.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 29 Oct 2026 08:21:38 -0700 (PDT) From: Ray Strode To: libcamera-devel@lists.libcamera.org Cc: kieran.bingham@ideasonboard.com, bod.linux@nxsw.ie, laurent.pinchart@ideasonboard.com, stefan.klug@ideasonboard.com, martincarvalho@gmail.com Subject: [PATCH v5 04/11] software-isp: Align statistics with the visible sensor crop Date: Thu, 29 Oct 2026 11:20:51 -0400 Message-ID: <20261029152058.896209-5-halfline@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261029152058.896209-1-halfline@gmail.com> References: <20261029152058.896209-1-halfline@gmail.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" From: Ray Strode 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 --- .../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 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 + +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 * */ #include "debayer_egl.h" #include #include #include #include #include #include #include #include #include #include #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 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 DebayerEGL::formats(PixelFormat inputFormat) { DebayerEGL::DebayerInputConfig config; if (getInputConfig(inputFormat, config) != 0) return std::vector(); return config.outputFormats; } std::tuple 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 stats_; eGL egl_; uint32_t width_; uint32_t height_; GLint glFormat_; unsigned int bytesPerPixel_; uint32_t shaderStridePixels_; }; } /* namespace libcamera */