From patchwork Fri Oct 9 10:48:04 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 28426 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 BB12ABDCB6 for ; Fri, 9 Oct 2026 10:48:39 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8279268A3C; Fri, 9 Oct 2026 12:48:39 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="mMORCRbl"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 67B6368A1C for ; Fri, 9 Oct 2026 12:48:38 +0200 (CEST) Received: from ideasonboard.com (unknown [IPv6:2a01:599:814:de78:3e2b:9bce:b9f5:d608]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id CE592C4A; Fri, 9 Oct 2026 12:46:39 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1791542800; bh=7SSYyl+t5MjOL+/ZNsG+KtpTSf0uWG8TkNUthWX6iVY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mMORCRblyKDAPAZZ+Mf5ii3Yx3dPW+VPSjo5ljszSHQ7zS6YayXmva85KVWUPITdW R88IJW6OThU2KhxrcHOv9D4qI1MDMJTWbS7gR8wXrdIas849s9qR02XIWHZ0CFHsTL QBuABp7i4fZsxrPTCo0jzdWbSZs9+Awm7Q6EUl6k= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug , Kieran Bingham , Daniel Scally Subject: [PATCH v2 2/3] libcamera: pipeline: rkisp1: Fix dewarping with multiple cameras Date: Fri, 9 Oct 2026 12:48:04 +0200 Message-ID: <20261009104826.45426-3-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20261009104826.45426-1-stefan.klug@ideasonboard.com> References: <20261009104826.45426-1-stefan.klug@ideasonboard.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" The dewarper is only instantiated once per pipeline. One pipeline can support multiple cameras with different sets of dewarp parmeters. In the current implementation the dewarp parameters are stored in the dewarper when the tuning file is loaded at init() time. Therefore the tuning file loaded last will overwrite the dewarp params for all cameras. Fix that by storing the dewarp parameters in the camera data and passing them to the dewarper at configure() time. While at it, mention the cmNew parameter in the yaml code sample. Signed-off-by: Stefan Klug Reviewed-by: Kieran Bingham Reviewed-by: Daniel Scally Tested-by: Daniel Scally --- Changes in v2: - Collected tags - dwNew -> cmNew in commit message - Pass dewarpParams to configure by reference - Initialize hasDewarpParams_ - Initialize inputBufferCount_ as well - Reference std::nullopt instead of nullopt_t in documentation --- .../internal/converter/converter_dw100.h | 8 ++-- src/libcamera/converter/converter_dw100.cpp | 40 +++++++++++++------ src/libcamera/pipeline/rkisp1/rkisp1.cpp | 5 ++- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/include/libcamera/internal/converter/converter_dw100.h b/include/libcamera/internal/converter/converter_dw100.h index 003f5eb954e9..795e5a49ce81 100644 --- a/include/libcamera/internal/converter/converter_dw100.h +++ b/include/libcamera/internal/converter/converter_dw100.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include @@ -31,11 +32,12 @@ public: static std::unique_ptr createModule(DeviceEnumerator *enumerator); - int init(const ValueNode ¶ms); + int loadDewarpParams(const ValueNode ¶ms, std::optional &dewarpParams); int configure(const StreamConfiguration &inputCfg, const std::vector> - &outputCfg); + &outputCfg, + const std::optional &dewarpParams); bool isConfigured(const Stream *stream) const; Size adjustInputSize(const PixelFormat &pixFmt, const Size &size, @@ -75,7 +77,7 @@ private: }; std::map vertexMaps_; - std::optional dewarpParams_; + bool hasDewarpParams_; unsigned int inputBufferCount_; V4L2M2MConverter converter_; Rectangle sensorCrop_; diff --git a/src/libcamera/converter/converter_dw100.cpp b/src/libcamera/converter/converter_dw100.cpp index 300ea808ceff..e4ea719d0f06 100644 --- a/src/libcamera/converter/converter_dw100.cpp +++ b/src/libcamera/converter/converter_dw100.cpp @@ -35,7 +35,7 @@ LOG_DECLARE_CATEGORY(Converter) */ ConverterDW100Module::ConverterDW100Module(std::shared_ptr media) - : converter_(media), running_(false) + : hasDewarpParams_(false), inputBufferCount_(0), converter_(media), running_(false) { converter_.outputBufferReady.connect(&this->outputBufferReady, &Signal::emit); converter_.inputBufferReady.connect(&this->inputBufferReady, &Signal::emit); @@ -72,11 +72,14 @@ ConverterDW100Module::createModule(DeviceEnumerator *enumerator) } /** - * \brief Initialize the module with configuration data + * \brief Load dewarp parameters from configuration file * \param[in] params The config parameters + * \param[out] dewarpParams The dewarp parameters * - * This function shall be called from the pipeline handler to initialize the - * module with the provided parameters. + * This function shall be called from the pipeline handler to load dewarp + * parameters from a tuning file. The dewarpParams can then be passed to + * configure() to configure the dewarper. If the tuning file does not contain + * any property, 0 is returned and dewarpParams is set to std::nullopt. * * A typical tuning file entry for the dewarper looks like this: * \code{.unparsed} @@ -87,6 +90,7 @@ ConverterDW100Module::createModule(DeviceEnumerator *enumerator) * 0.0, 1.0, 0.0, * 0.0, 0.0, 1.0, * ] + * cmNew: * coefficients: [ * 0,0,0,0,0, * ] @@ -98,9 +102,11 @@ ConverterDW100Module::createModule(DeviceEnumerator *enumerator) * \sa Dw100VertexMap::setDewarpParams() * \return 0 if successful, an error code otherwise */ -int ConverterDW100Module::init(const ValueNode ¶ms) +int ConverterDW100Module::loadDewarpParams(const ValueNode ¶ms, + std::optional &dewarpParams) { Dw100VertexMap::DewarpParams dp; + dewarpParams.reset(); auto &cm = params["cm"]; auto &coefficients = params["coefficients"]; @@ -153,17 +159,26 @@ int ConverterDW100Module::init(const ValueNode ¶ms) dp.cmNew = dp.cm; } - dewarpParams_ = dp; + dewarpParams = dp; return 0; } /** - * \copydoc libcamera::V4L2M2MConverter::configure + * \brief Configure a the dw100 converter module + * \param[in] inputCfg Input stream configuration + * \param[in] outputCfgs A list of output stream configurations + * \param[in] dewarpParams The lens dewarp parameters to apply + * + * Configures the converter for the given input and output stream configurations + * and an optional set of dewarp parameters. + * + * \return 0 on success or a negative error code otherwise */ int ConverterDW100Module::configure(const StreamConfiguration &inputCfg, const std::vector> - &outputCfgs) + &outputCfgs, + const std::optional &dewarpParams) { int ret; @@ -173,6 +188,7 @@ int ConverterDW100Module::configure(const StreamConfiguration &inputCfg, return ret; inputBufferCount_ = inputCfg.bufferCount; + hasDewarpParams_ = dewarpParams.has_value(); for (auto &ref : outputCfgs) { const auto &outputCfg = ref.get(); @@ -182,8 +198,8 @@ int ConverterDW100Module::configure(const StreamConfiguration &inputCfg, vertexMap.setOutputSize(outputCfg.size); vertexMap.setSensorCrop(sensorCrop_); - if (dewarpParams_) - vertexMap.setDewarpParams(*dewarpParams_); + if (dewarpParams) + vertexMap.setDewarpParams(*dewarpParams); info.update = true; } @@ -363,7 +379,7 @@ void ConverterDW100Module::updateControlInfos(const Stream *stream, ControlInfoM controls[&controls::ScalerCrop] = ControlInfo(Rectangle(sensorCrop_.x, sensorCrop_.y, 1, 1), sensorCrop_, sensorCrop_); - if (dewarpParams_.has_value()) + if (hasDewarpParams_) controls[&controls::LensDewarpEnable] = ControlInfo(false, true, true); if (!converter_.supportsRequests()) @@ -424,7 +440,7 @@ void ConverterDW100Module::populateMetadata(const Stream *stream, ControlList &m meta.set(controls::ScalerCrop, vertexMap.effectiveScalerCrop()); - if (dewarpParams_.has_value()) + if (hasDewarpParams_) meta.set(controls::LensDewarpEnable, vertexMap.lensDewarpEnable()); } diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index 96382c93a427..39fe0f0964d7 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -128,6 +128,7 @@ public: bool canUseDewarper_; bool usesDewarper_; + std::optional dewarpParams_; private: void paramsComputed(unsigned int frame, unsigned int bytesused); @@ -462,7 +463,7 @@ int RkISP1CameraData::loadTuningFile(const std::string &path) if (!params) continue; - ret = pipe()->dewarper_->init(params); + ret = pipe()->dewarper_->loadDewarpParams(params, dewarpParams_); if (ret) return ret; @@ -1058,7 +1059,7 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c) PixelFormatInfo::info(ispCfg.pixelFormat) .stride(ispCfg.size.width, 0); - ret = dewarper_->configure(ispCfg, { cfg }); + ret = dewarper_->configure(ispCfg, { cfg }, data->dewarpParams_); if (ret) return ret;