From patchwork Wed Jun 17 09:58:49 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 26910 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 68754C328C for ; Wed, 17 Jun 2026 09:59:07 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 07A5862720; Wed, 17 Jun 2026 11:59:07 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="WlSWG2J8"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E8F1162720 for ; Wed, 17 Jun 2026 11:59:05 +0200 (CEST) Received: from ideasonboard.com (unknown [IPv6:2a00:6020:448c:6c00:bba7:89ad:1437:cbea]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id CFD86664; Wed, 17 Jun 2026 11:58:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1781690311; bh=pCPqMhxPQo+B5cLq5cRPNqCviSDU2cvzi9RCZt7llV4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WlSWG2J8fI8tsnz02SWIPGpcLgDx5quyeATYmCBeCJ2x2I35gC98R6yKdrEquHJt5 I0dMnHVUyP0T8eaJZ5QDrM1zYojIHdgpLEPorsbx5Oz1Slyoz9hFP4oV/Cpwsx/SZJ SE7QSnJp5yCh9MNzFRo4xGGdrVQbwYWFAuskFJF4= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH 1/2] libcamera: converter: converter_dw100: Refactor dewarp param handling Date: Wed, 17 Jun 2026 11:58:49 +0200 Message-ID: <20260617095857.667583-2-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260617095857.667583-1-stefan.klug@ideasonboard.com> References: <20260617095857.667583-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" There is already a ConverterDW100Module::DewarpParams class while in Dw100VertexMap the parameters are handled separately for no good reason. Create a Dw100VertexMap::DewarpParams struct for that purpose and adjust the code accordingly. This has the added benefit that the size checking for the coefficient list can now be done in the tuning file loading code where it belongs. Signed-off-by: Stefan Klug --- .../internal/converter/converter_dw100.h | 7 +- .../converter/converter_dw100_vertexmap.h | 35 ++++++-- src/libcamera/converter/converter_dw100.cpp | 15 +++- .../converter/converter_dw100_vertexmap.cpp | 82 ++++++++----------- 4 files changed, 75 insertions(+), 64 deletions(-) diff --git a/include/libcamera/internal/converter/converter_dw100.h b/include/libcamera/internal/converter/converter_dw100.h index 1db1aadcb06b..003f5eb954e9 100644 --- a/include/libcamera/internal/converter/converter_dw100.h +++ b/include/libcamera/internal/converter/converter_dw100.h @@ -69,18 +69,13 @@ private: int applyControls(const Stream *stream, const V4L2Request *request); void reinitRequest(V4L2Request *request); - struct DewarpParms { - Matrix cm; - std::vector coeffs; - }; - struct VertexMapInfo { Dw100VertexMap map; bool update; }; std::map vertexMaps_; - std::optional dewarpParams_; + std::optional dewarpParams_; unsigned int inputBufferCount_; V4L2M2MConverter converter_; Rectangle sensorCrop_; diff --git a/include/libcamera/internal/converter/converter_dw100_vertexmap.h b/include/libcamera/internal/converter/converter_dw100_vertexmap.h index 3bf80ea66dc7..787498c7fdb5 100644 --- a/include/libcamera/internal/converter/converter_dw100_vertexmap.h +++ b/include/libcamera/internal/converter/converter_dw100_vertexmap.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -30,6 +31,33 @@ public: Crop = 1, }; + struct DewarpParams { + DewarpParams() : cm(Matrix::identity()), + coefficients({}) + { + } + + Matrix cm; + + union { + struct { + double k1; + double k2; + double p1; + double p2; + double k3; + double k4; + double k5; + double k6; + double s1; + double s2; + double s3; + double s4; + } coefficient; + std::array coefficients; + }; + }; + void applyLimits(); void setInputSize(const Size &size) { @@ -60,8 +88,7 @@ public: void setMode(const ScaleMode mode) { mode_ = mode; } ScaleMode mode() const { return mode_; } - int setDewarpParams(const Matrix &cm, const Span &coeffs); - bool dewarpParamsValid() { return dewarpParamsValid_; } + void setDewarpParams(const DewarpParams ¶ms) { dewarpParams_ = params; } void setLensDewarpEnable(bool enable) { lensDewarpEnable_ = enable; } bool lensDewarpEnable() { return lensDewarpEnable_; } @@ -85,10 +112,8 @@ private: Point effectiveOffset_; Rectangle effectiveScalerCrop_; - Matrix dewarpM_ = Matrix::identity(); - std::array dewarpCoeffs_; + std::optional dewarpParams_; bool lensDewarpEnable_ = true; - bool dewarpParamsValid_ = false; }; } /* namespace libcamera */ diff --git a/src/libcamera/converter/converter_dw100.cpp b/src/libcamera/converter/converter_dw100.cpp index 44f7ec035e62..34f81c6b9fab 100644 --- a/src/libcamera/converter/converter_dw100.cpp +++ b/src/libcamera/converter/converter_dw100.cpp @@ -15,6 +15,7 @@ #include #include "libcamera/internal/converter.h" +#include "libcamera/internal/converter/converter_dw100_vertexmap.h" #include "libcamera/internal/converter/converter_v4l2_m2m.h" #include "libcamera/internal/media_device.h" #include "libcamera/internal/v4l2_videodevice.h" @@ -99,7 +100,7 @@ ConverterDW100Module::createModule(DeviceEnumerator *enumerator) */ int ConverterDW100Module::init(const ValueNode ¶ms) { - DewarpParms dp; + Dw100VertexMap::DewarpParams dp; auto &cm = params["cm"]; auto &coefficients = params["coefficients"]; @@ -131,7 +132,15 @@ int ConverterDW100Module::init(const ValueNode ¶ms) LOG(Converter, Error) << "Dewarp parameters 'coefficients' value is not a list"; return -EINVAL; } - dp.coeffs = std::move(*coeffs); + + if (coeffs->size() != 4 && coeffs->size() != 5 && + coeffs->size() != 8 && coeffs->size() != 12) { + LOG(Converter, Error) + << "Dewarp 'coefficients' must have 4, 5, 8 or 12 values"; + return -EINVAL; + } + + std::copy(coeffs->begin(), coeffs->end(), &dp.coefficients[0]); dewarpParams_ = dp; @@ -163,7 +172,7 @@ int ConverterDW100Module::configure(const StreamConfiguration &inputCfg, vertexMap.setSensorCrop(sensorCrop_); if (dewarpParams_) - vertexMap.setDewarpParams(dewarpParams_->cm, dewarpParams_->coeffs); + vertexMap.setDewarpParams(*dewarpParams_); info.update = true; } diff --git a/src/libcamera/converter/converter_dw100_vertexmap.cpp b/src/libcamera/converter/converter_dw100_vertexmap.cpp index 6e238736c435..31d992546857 100644 --- a/src/libcamera/converter/converter_dw100_vertexmap.cpp +++ b/src/libcamera/converter/converter_dw100_vertexmap.cpp @@ -227,6 +227,20 @@ int dw100VerticesForLength(const int length) * into account within the possible limits. */ +/** + * \struct Dw100VertexMap::DewarpParams + * \brief Structure combining all dewarp parameters + * + * \var Dw100VertexMap::DewarpParams::cm + * \brief The camera matrix + * + * \var Dw100VertexMap::DewarpParams::coefficient + * \brief Structure containing the lens dewarp coefficients + + * See https://docs.opencv.org/4.12.0/d9/d0c/group__calib3d.html for further + * details on the model. + */ + /** * \brief Apply limits on scale and offset * @@ -549,7 +563,7 @@ std::vector Dw100VertexMap::getVertexMap() p = transformPoint(outputToSensor, p); - if (dewarpParamsValid_ && lensDewarpEnable_) + if (lensDewarpEnable_) p = dewarpPoint(p); p = transformPoint(sensorToInput, p); @@ -566,41 +580,13 @@ std::vector Dw100VertexMap::getVertexMap() /** * \brief Set the dewarp parameters - * \param cm The camera matrix - * \param coeffs The dewarp coefficients + * \param params The dewarp parameters * * Sets the dewarp parameters according to the commonly used dewarp model. See * https://docs.opencv.org/4.12.0/d9/d0c/group__calib3d.html for further details * on the model. The parameter \a coeffs must either hold 4,5,8 or 12 values. * They represent the parameters k1,k2,p1,p2[,k3[,k4,k5,k6[,s1,s2,s3,s4]]] in * the model. - * - * \return A negative number on error, 0 otherwise - */ -int Dw100VertexMap::setDewarpParams(const Matrix &cm, - const Span &coeffs) -{ - dewarpM_ = cm; - dewarpCoeffs_.fill(0.0); - - if (coeffs.size() != 4 && coeffs.size() != 5 && - coeffs.size() != 8 && coeffs.size() != 12) { - LOG(Converter, Error) - << "Dewarp 'coefficients' must have 4, 5, 8 or 12 values"; - dewarpParamsValid_ = false; - return -EINVAL; - } - std::copy(coeffs.begin(), coeffs.end(), dewarpCoeffs_.begin()); - - dewarpParamsValid_ = true; - return 0; -} - -/** - * \fn Dw100VertexMap::dewarpParamsValid() - * \brief Returns if the dewarp parameters are valid - * - * \return True if the dewarp parameters are valid, false otherwise */ /** @@ -627,32 +613,28 @@ int Dw100VertexMap::setDewarpParams(const Matrix &cm, */ Vector2d Dw100VertexMap::dewarpPoint(const Vector2d &p) { + if (!dewarpParams_.has_value()) + return p; + double x, y; double xout, yout; - double k1 = dewarpCoeffs_[0]; - double k2 = dewarpCoeffs_[1]; - double p1 = dewarpCoeffs_[2]; - double p2 = dewarpCoeffs_[3]; - double k3 = dewarpCoeffs_[4]; - double k4 = dewarpCoeffs_[5]; - double k5 = dewarpCoeffs_[6]; - double k6 = dewarpCoeffs_[7]; - double s1 = dewarpCoeffs_[8]; - double s2 = dewarpCoeffs_[9]; - double s3 = dewarpCoeffs_[10]; - double s4 = dewarpCoeffs_[11]; + auto &cm = dewarpParams_->cm; + auto &c = dewarpParams_->coefficient; - y = (p.y() - dewarpM_[1][2]) / dewarpM_[1][1]; - x = (p.x() - dewarpM_[0][2] - y * dewarpM_[0][1]) / dewarpM_[0][0]; + y = (p.y() - cm[1][2]) / cm[1][1]; + x = (p.x() - cm[0][2] - y * cm[0][1]) / cm[0][0]; double r2 = x * x + y * y; - double d = (1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2) / - (1 + k4 * r2 + k5 * r2 * r2 + k6 * r2 * r2 * r2); - xout = x * d + 2 * p1 * x * y + p2 * (r2 + 2 * x * x) + s1 * r2 + s2 * r2 * r2; - yout = y * d + 2 * p2 * x * y + p1 * (r2 + 2 * y * y) + s3 * r2 + s4 * r2 * r2; + double r4 = r2 * r2; + double r6 = r2 * r2 * r2; + double d = (1 + c.k1 * r2 + c.k2 * r4 + c.k3 * r6) / + (1 + c.k4 * r2 + c.k5 * r4 + c.k6 * r6); - return { { xout * dewarpM_[0][0] + yout * dewarpM_[0][1] + dewarpM_[0][2], - yout * dewarpM_[1][1] + dewarpM_[1][2] } }; + xout = x * d + 2 * c.p1 * x * y + c.p2 * (r2 + 2 * x * x) + c.s1 * r2 + c.s2 * r4; + yout = y * d + 2 * c.p2 * x * y + c.p1 * (r2 + 2 * y * y) + c.s3 * r2 + c.s4 * r4; + + return { { xout * cm[0][0] + yout * cm[0][1] + cm[0][2], + yout * cm[1][1] + cm[1][2] } }; } } /* namespace libcamera */ From patchwork Wed Jun 17 09:58:50 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 26911 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 E13F3BF415 for ; Wed, 17 Jun 2026 09:59:09 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8CA866289A; Wed, 17 Jun 2026 11:59:09 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ULl+NJod"; 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 1583562886 for ; Wed, 17 Jun 2026 11:59:08 +0200 (CEST) Received: from ideasonboard.com (unknown [IPv6:2a00:6020:448c:6c00:bba7:89ad:1437:cbea]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id EEED397F; Wed, 17 Jun 2026 11:58:33 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1781690314; bh=/7yCmiMb9P+Gw1Nx0CCveA5uU/i8ed4pbSKg6BYXfao=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ULl+NJodxWECuY8dGd3gD0SX3ZyBBBarg31u79EELq3vIUPw+mckhLeBbUWFpIIly 1qpmH2xR/isIGwdxf5IV8LFkDoevgi2ICPJZL/1J2ymn9joZLNTS0HLzduf4W0KHl2 +NBmEorh7arxjwvhddvk3xkXGEaO/9Dul8pzVdc8= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH 2/2] libcamera: converter: converter_dw100: Allow to transform between different camera matrices Date: Wed, 17 Jun 2026 11:58:50 +0200 Message-ID: <20260617095857.667583-3-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260617095857.667583-1-stefan.klug@ideasonboard.com> References: <20260617095857.667583-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" For some use cases (like removing a skew introduced by the camera geometry) it is necessary to define the output camera matrix. Add that functionality by adding an optional tuning parameter "cmNew" to the Dewarp configuration. If that parameter is not provided, cm is used instead. Signed-off-by: Stefan Klug --- Changes in v0.2: - Swapped cm and cmNew in the output calculation --- .../converter/converter_dw100_vertexmap.h | 2 ++ src/libcamera/converter/converter_dw100.cpp | 15 ++++++++++++++- .../converter/converter_dw100_vertexmap.cpp | 8 ++++++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/include/libcamera/internal/converter/converter_dw100_vertexmap.h b/include/libcamera/internal/converter/converter_dw100_vertexmap.h index 787498c7fdb5..d60997f56ad5 100644 --- a/include/libcamera/internal/converter/converter_dw100_vertexmap.h +++ b/include/libcamera/internal/converter/converter_dw100_vertexmap.h @@ -33,11 +33,13 @@ public: struct DewarpParams { DewarpParams() : cm(Matrix::identity()), + cmNew(Matrix::identity()), coefficients({}) { } Matrix cm; + Matrix cmNew; union { struct { diff --git a/src/libcamera/converter/converter_dw100.cpp b/src/libcamera/converter/converter_dw100.cpp index 34f81c6b9fab..97a025b7f961 100644 --- a/src/libcamera/converter/converter_dw100.cpp +++ b/src/libcamera/converter/converter_dw100.cpp @@ -104,9 +104,10 @@ int ConverterDW100Module::init(const ValueNode ¶ms) auto &cm = params["cm"]; auto &coefficients = params["coefficients"]; + auto &cmNew = params["cmNew"]; /* If nothing is provided, the dewarper is still functional */ - if (!cm && !coefficients) + if (!cm && !coefficients && !cmNew) return 0; if (!cm) { @@ -142,6 +143,18 @@ int ConverterDW100Module::init(const ValueNode ¶ms) std::copy(coeffs->begin(), coeffs->end(), &dp.coefficients[0]); + if (cmNew) { + matrix = cmNew.get>(); + if (!matrix) { + LOG(Converter, Error) << "Failed to load 'cmNew' value"; + return -EINVAL; + } + + dp.cmNew = *matrix; + } else { + dp.cmNew = dp.cm; + } + dewarpParams_ = dp; return 0; diff --git a/src/libcamera/converter/converter_dw100_vertexmap.cpp b/src/libcamera/converter/converter_dw100_vertexmap.cpp index 31d992546857..22e08656d36d 100644 --- a/src/libcamera/converter/converter_dw100_vertexmap.cpp +++ b/src/libcamera/converter/converter_dw100_vertexmap.cpp @@ -234,6 +234,9 @@ int dw100VerticesForLength(const int length) * \var Dw100VertexMap::DewarpParams::cm * \brief The camera matrix * + * \var Dw100VertexMap::DewarpParams::cmNew + * \brief The new camera matrix after dewarping + * * \var Dw100VertexMap::DewarpParams::coefficient * \brief Structure containing the lens dewarp coefficients @@ -619,10 +622,11 @@ Vector2d Dw100VertexMap::dewarpPoint(const Vector2d &p) double x, y; double xout, yout; auto &cm = dewarpParams_->cm; + auto &cmNew = dewarpParams_->cmNew; auto &c = dewarpParams_->coefficient; - y = (p.y() - cm[1][2]) / cm[1][1]; - x = (p.x() - cm[0][2] - y * cm[0][1]) / cm[0][0]; + y = (p.y() - cmNew[1][2]) / cmNew[1][1]; + x = (p.x() - cmNew[0][2] - y * cmNew[0][1]) / cmNew[0][0]; double r2 = x * x + y * y; double r4 = r2 * r2;