[{"id":37062,"web_url":"https://patchwork.libcamera.org/comment/37062/","msgid":"<176409280821.567526.12504756477774187335@ping.linuxembedded.co.uk>","date":"2025-11-25T17:46:48","subject":"Re: [PATCH v3 24/29] libcamera: dw100_vertexmap: Implement\n\tparametric dewarping","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Stefan Klug (2025-11-25 16:28:36)\n> Implement functions to allow lens dewarping based on the common lens\n> dewarp model used e.g. by OpenCV.\n> \n> See https://docs.opencv.org/4.12.0/d9/d0c/group__calib3d.html for an\n> in depth explanation of the parameters.\n> \n> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> \n> ---\n> \n> Changes in v3:\n> - Fixed typo in docs\n> - Collected tag\n> \n> Changes in v2:\n> - Adapt to vertex map implementation based on affine transforms\n> - Improved documentation\n> - dropped loadDewarpParams function as it was never used\n> ---\n>  .../converter/converter_dw100_vertexmap.h     | 16 +++\n>  .../converter/converter_dw100_vertexmap.cpp   | 98 +++++++++++++++++--\n>  2 files changed, 108 insertions(+), 6 deletions(-)\n> \n> diff --git a/include/libcamera/internal/converter/converter_dw100_vertexmap.h b/include/libcamera/internal/converter/converter_dw100_vertexmap.h\n> index 428b3d74d4d2..49ecc81678a0 100644\n> --- a/include/libcamera/internal/converter/converter_dw100_vertexmap.h\n> +++ b/include/libcamera/internal/converter/converter_dw100_vertexmap.h\n> @@ -10,6 +10,9 @@\n>  #include <libcamera/geometry.h>\n>  #include <libcamera/transform.h>\n>  \n> +#include \"libcamera/internal/matrix.h\"\n> +#include \"libcamera/internal/vector.h\"\n> +\n>  namespace libcamera {\n>  \n>  class Dw100VertexMap\n> @@ -50,9 +53,17 @@ public:\n>         void setMode(const ScaleMode mode) { mode_ = mode; }\n>         ScaleMode mode() const { return mode_; }\n>  \n> +       int setDewarpParams(const Matrix<double, 3, 3> &cm, const Span<const double> &coeffs);\n> +       bool dewarpParamsValid() { return dewarpParamsValid_; }\n> +\n> +       void setLensDewarpEnable(bool enable) { lensDewarpEnable_ = enable; }\n> +       bool lensDewarpEnable() { return lensDewarpEnable_; }\n> +\n>         std::vector<uint32_t> getVertexMap();\n>  \n>  private:\n> +       Vector<double, 2> dewarpPoint(const Vector<double, 2> &p);\n> +\n>         Rectangle scalerCrop_;\n>         Rectangle sensorCrop_;\n>         Transform transform_ = Transform::Identity;\n> @@ -66,6 +77,11 @@ private:\n>         double effectiveScaleY_;\n>         Point effectiveOffset_;\n>         Rectangle effectiveScalerCrop_;\n> +\n> +       Matrix<double, 3, 3> dewarpM_ = Matrix<double, 3, 3>::identity();\n> +       std::array<double, 12> dewarpCoeffs_;\n> +       bool lensDewarpEnable_ = true;\n> +       bool dewarpParamsValid_ = false;\n>  };\n>  \n>  } /* namespace libcamera */\n> diff --git a/src/libcamera/converter/converter_dw100_vertexmap.cpp b/src/libcamera/converter/converter_dw100_vertexmap.cpp\n> index 427d710743b8..b6880ba40a20 100644\n> --- a/src/libcamera/converter/converter_dw100_vertexmap.cpp\n> +++ b/src/libcamera/converter/converter_dw100_vertexmap.cpp\n> @@ -183,8 +183,6 @@ int dw100VerticesForLength(const int length)\n>   * |             |    |             |    | Transpose) |    | Rotate)         |\n>   * +-------------+    +-------------+    +------------+    +-----------------+\n>   *\n> - * \\todo Lens dewarp is not yet implemented. An identity map is used instead.\n> - *\n>   * All parameters are clamped to valid values before creating the vertex map.\n>   *\n>   * The constrains process works as follows:\n> @@ -544,10 +542,8 @@ std::vector<uint32_t> Dw100VertexMap::getVertexMap()\n>  \n>                         p = transformPoint(outputToSensor, p);\n>  \n> -                       /*\n> -                        * \\todo: Transformations in sensor space to be added\n> -                        * here.\n> -                        */\n> +                       if (dewarpParamsValid_ && lensDewarpEnable_)\n> +                               p = dewarpPoint(p);\n>  \n>                         p = transformPoint(sensorToInput, p);\n>  \n> @@ -561,4 +557,94 @@ std::vector<uint32_t> Dw100VertexMap::getVertexMap()\n>         return res;\n>  }\n>  \n> +/**\n> + * \\brief Set the dewarp parameters\n> + * \\param cm The camera matrix\n> + * \\param coeffs The dewarp coefficients\n> + *\n> + * Sets the dewarp parameters according to the commonly used dewarp model. See\n> + * https://docs.opencv.org/4.12.0/d9/d0c/group__calib3d.html for further details\n> + * on the model. The parameter \\a coeffs must either hold 4,5,8 or 12 values.\n> + * They represent the parameters k1,k2,p1,p2[,k3[,k4,k5,k6[,s1,s2,s3,s4]]] in\n> + * the model.\n> + *\n> + * \\return A negative number on error, 0 otherwise\n> + */\n> +int Dw100VertexMap::setDewarpParams(const Matrix<double, 3, 3> &cm,\n> +                                   const Span<const double> &coeffs)\n> +{\n> +       dewarpM_ = cm;\n> +       dewarpCoeffs_.fill(0.0);\n> +\n> +       if (coeffs.size() != 4 && coeffs.size() != 5 &&\n> +           coeffs.size() != 8 && coeffs.size() != 12) {\n> +               LOG(Converter, Error)\n> +                       << \"Dewarp 'coefficients' must have 4, 5, 8 or 12 values\";\n> +               dewarpParamsValid_ = false;\n> +               return -EINVAL;\n> +       }\n> +       std::copy(coeffs.begin(), coeffs.end(), dewarpCoeffs_.begin());\n> +\n> +       dewarpParamsValid_ = true;\n> +       return 0;\n> +}\n> +\n> +/**\n> + * \\fn Dw100VertexMap::dewarpParamsValid()\n> + * \\brief Returns if the dewarp parameters are valid\n> + *\n> + * \\return True if the dewarp parameters are valid, fals otherwise\n\ns/fals/false/\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n\n> + */\n> +\n> +/**\n> + * \\fn Dw100VertexMap::setLensDewarpEnable()\n> + * \\brief Enables or disables lens dewarping\n> + * \\param[in] enable Enable or disable lens dewarping\n> + */\n> +\n> +/**\n> + * \\fn Dw100VertexMap::lensDewarpEnable()\n> + * \\brief Returns if lens dewarping is enabled\n> + */\n> +\n> +/**\n> + * \\brief Apply dewarp calculation to a point\n> + * \\param p The point to dewarp\n> + *\n> + * Applies the dewarp transformation to point \\a p according to the commonly\n> + * used dewarp model. See\n> + * https://docs.opencv.org/4.12.0/d9/d0c/group__calib3d.html for further details\n> + * on the model.\n> + *\n> + * \\return The dewarped point\n> + */\n> +Vector2d Dw100VertexMap::dewarpPoint(const Vector2d &p)\n> +{\n> +       double x, y;\n> +       double k1 = dewarpCoeffs_[0];\n> +       double k2 = dewarpCoeffs_[1];\n> +       double p1 = dewarpCoeffs_[2];\n> +       double p2 = dewarpCoeffs_[3];\n> +       double k3 = dewarpCoeffs_[4];\n> +       double k4 = dewarpCoeffs_[5];\n> +       double k5 = dewarpCoeffs_[6];\n> +       double k6 = dewarpCoeffs_[7];\n> +       double s1 = dewarpCoeffs_[8];\n> +       double s2 = dewarpCoeffs_[9];\n> +       double s3 = dewarpCoeffs_[10];\n> +       double s4 = dewarpCoeffs_[11];\n> +\n> +       y = (p.y() - dewarpM_[1][2]) / dewarpM_[1][1];\n> +       x = (p.x() - dewarpM_[0][2] - y * dewarpM_[0][1]) / dewarpM_[0][0];\n> +\n> +       double r2 = x * x + y * y;\n> +       double d = (1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2) /\n> +                  (1 + k4 * r2 + k5 * r2 * r2 + k6 * r2 * r2 * r2);\n> +       x = x * d + 2 * p1 * x * y + p2 * (r2 + 2 * x * x) + s1 * r2 + s2 * r2 * r2;\n> +       y = y * d + 2 * p2 * x * y + p1 * (r2 + 2 * y * y) + s3 * r2 + s4 * r2 * r2;\n> +\n> +       return { { x * dewarpM_[0][0] + y * dewarpM_[0][1] + dewarpM_[0][2],\n> +                  y * dewarpM_[1][1] + dewarpM_[1][2] } };\n> +}\n> +\n>  } /* namespace libcamera */\n> -- \n> 2.51.0\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 5CED6C0F2A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 25 Nov 2025 17:46:53 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 9B2AF60A9E;\n\tTue, 25 Nov 2025 18:46:52 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1A9B4609D8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 25 Nov 2025 18:46:51 +0100 (CET)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id CBB8443;\n\tTue, 25 Nov 2025 18:44:41 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"APIhVSXM\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1764092681;\n\tbh=aZINnGX8g4jgmlPkXDOAINfGihHSZ4GIyMnR11QCGYk=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=APIhVSXM1zwpn1Wn1u9OG2ATDANn/RFvdwYUFYgM2H5DzgqpxeAw3Qtzvi2xauKGj\n\t4UJPVtmUGwv9gOxCPZ9cRyjzyhy0xK0LHCKrJt1CdQDRpjuPcY6Pu3AGwKTw7e2eFd\n\tjyR4iTQBGegnTKdxPyDeVDn3g0LYCcCohZFpGmp8=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20251125162851.2301793-25-stefan.klug@ideasonboard.com>","References":"<20251125162851.2301793-1-stefan.klug@ideasonboard.com>\n\t<20251125162851.2301793-25-stefan.klug@ideasonboard.com>","Subject":"Re: [PATCH v3 24/29] libcamera: dw100_vertexmap: Implement\n\tparametric dewarping","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"Stefan Klug <stefan.klug@ideasonboard.com>,\n\tPaul Elder <paul.elder@ideasonboard.com>","To":"Stefan Klug <stefan.klug@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Tue, 25 Nov 2025 17:46:48 +0000","Message-ID":"<176409280821.567526.12504756477774187335@ping.linuxembedded.co.uk>","User-Agent":"alot/0.9.1","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]