From patchwork Thu Oct 29 13:26:24 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ray Strode X-Patchwork-Id: 28694 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 292DDC3261 for ; Thu, 29 Oct 2026 13:27:04 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D5657690AA; Thu, 29 Oct 2026 14:27:03 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="wxvAE0kI"; dkim-atps=neutral Received: from mail-vs1-xe2d.google.com (mail-vs1-xe2d.google.com [IPv6:2607:f8b0:4864:20::e2d]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0CD126909B for ; Thu, 29 Oct 2026 14:27:01 +0100 (CET) Received: by mail-vs1-xe2d.google.com with SMTP id ada2fe7eead31-7cc2bb73cd9so611007137.3 for ; Thu, 29 Oct 2026 06:27:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793280420; x=1793885220; 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=wxvAE0kI8LFPE0MLlJzlWK1Z+sKJdBk4Yi7VsxAcm5/UAJbvA9IVmaeDd/rpV3xH4U 0YMKI60o65gsN4wGxDwaILicMJ1DO4jQ0trLl8JCIrwxULneYlGdJrb3s5B6A8NEbvZt l/d04B7FfMn3EQIpxvgavWafs7F4bBit7spDyrp/FaYXUUBPeZsHdTCzVhtvVOvCAW9k KNYjFJhxcfZFpZltxis4kOfA9caHffR0ozEFslGrF3ZLt9HqrRObKCk4/hgCikdOW2rs M3F2iEuniyZKYMshJpkiPrcM7bukUGi2pBOHweteH8ls8WcIMvuYPLYSdhtXmRT7NUsD Ersw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793280420; x=1793885220; 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=VvyIZ3avs9dc9J99nlqC/YRtaEZG382LJRrDxM/84lEild6mGhwmoCTzr0V3I+Tjb/ GytC1IiOK1Z/QmSZHlSDf4flSsbivsFW3npnx8X6h6RoZ+L1c6ufVWyfXImlqbuATb8j PSTG8BuSPkDSonbYCvFE2SGNPszB4c/OmhTPxzcBVairDA/OrS5Ah4HstKehhPbyoTt9 gLmlLQdZKdx7ZmufqU5VHOiTyqYO3dX+P+q0nuxiJS3HzlzPJ2VHdblb48Gkd10GMuHO +fTkBhQBrAW3e6S4sbEngHspGjtGNYMH2YoIUjrjMyt6pgWUkEVQUgqNBneB1jkWyT3H hjgg== X-Gm-Message-State: AFq9FYKge/q45BGlm9DPDM254msm6RESEIgdBgkJnc5B0vKYzXHl+CHT DOV4eM7pcpdj7zMMLA5O+VT2MdZeBXUOBrCbiJYQNJ5YDWScYIGvxzYWNWm4vvKdmRc= X-Gm-Gg: AYBFou1MeFUw/fVGo3Gd/k8gtRa09zzQul1CQAXE/Z5Ogg+Hlppg0G/6QwYRSmmpZx1 9XfLcxRGhY/SASzvmPYHELfmqvsSmTK1rpV628ImtPDemplka8s5wIROqR/asVEQuzwP3UCTfjd E1X5Rr03kes3z2YO5igQQHqKMhTJ90EWrl2zb57ntl+1KhCW7+30/rierPaXcSuK4U62Aosf4XX WEK+gf/RCSxnsxZ1dAyNC3pwPw3jhAwgi58CVT2ZOwDo/YkzA5DHpJ5ehvs9WoU4QzgQi/xDXQK IleIvYeKEyJOzxSKp/I6Dopnl/OuBWnCFRjJnFgo/7NIFSgb4zEvoXFlwOZMtw82tzNchqRUnWw ohHlOptGIegSGv/ytlth3IePw3PI89sWfVOr4sk3DnHSe2CvUU2F7+vTKw00ZNYxD5tEiUsI8yq CJ7KEgCeJhVlD7cXWx7DbxnFIM/JPR16VZfp5xYQjsOnCSEQgEYjiUeVD2JOnu7cPbmoSR+k3AA mauiQmg3Q/94Vo7W21g0Xja/IgFBah3YzMuCemECiGoG/fJR6J9d211pVcDFtPuvtTjKY09NCwd DzL0FLy168fT27r6VRzEEa1xXcLlvlRf/8w88OCF4QNCTboA1uyEbY6C X-Received: by 2002:a05:6102:1499:b0:7b2:508e:3d57 with SMTP id ada2fe7eead31-7d03a289d03mr2358760137.4.1793280419679; Thu, 29 Oct 2026 06:26:59 -0700 (PDT) Received: from localhost ([144.121.52.163]) by smtp.gmail.com with ESMTPSA id a1e0cc1a2514c-99125931983sm9476046241.2.2026.10.29.06.26.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 29 Oct 2026 06:26:58 -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 v4 2/9] software-isp: Align statistics with the visible sensor crop Date: Thu, 29 Oct 2026 09:26:24 -0400 Message-ID: <20261029132631.370507-3-halfline@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261029132631.370507-1-halfline@gmail.com> References: <20261016202818.1405645-1-halfline@gmail.com> <20261029132631.370507-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 */