[{"id":36745,"web_url":"https://patchwork.libcamera.org/comment/36745/","msgid":"<176250925991.2116251.3492112143066097139@neptunite.rasen.tech>","date":"2025-11-07T09:54:19","subject":"Re: [PATCH v2 30/35] libcamera: dw100_vertexmap: Implement\n\tparametric dewarping","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"Quoting Stefan Klug (2025-10-23 23:48:31)\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> \n> ---\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 e72cb72bb9f1..b05ed8338a23 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> @@ -55,9 +58,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> @@ -71,6 +82,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 0e930479b6f7..f2be9a697ff3 100644\n> --- a/src/libcamera/converter/converter_dw100_vertexmap.cpp\n> +++ b/src/libcamera/converter/converter_dw100_vertexmap.cpp\n> @@ -184,8 +184,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> @@ -546,10 +544,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> @@ -563,4 +559,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> + */\n> +\n> +/**\n> + * \\fn Dw100VertexMap::setLensDewarpEnable()\n> + * \\brief Enables or disabled lens dewarping\n\ns/disabled/disables/\n\nReviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n\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.48.1\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 C5801BDE4C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  7 Nov 2025 09:54:30 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id D85E4609D8;\n\tFri,  7 Nov 2025 10:54:29 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A09EB606D5\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  7 Nov 2025 10:54:27 +0100 (CET)","from neptunite.rasen.tech (unknown\n\t[IPv6:2404:7a81:160:2100:8ce5:1b8c:5343:a04e])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 57F39593;\n\tFri,  7 Nov 2025 10:52:31 +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=\"a7SNR/Bc\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1762509151;\n\tbh=8JJkomSHqUF+Z17bgQQ3osdrdskpb/Isfrb16Eg8VeA=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=a7SNR/BcQOVP4wjoaRpitd4QCtGGJE+BDkRCD3zGCwXO0wIMMC+SFDwA1ACveZKKb\n\tWvwuTNdXEGnQWh5ef7ajR0zQFy9v7h+G0+3LKNYa0tyHw/r8eIhUYr8zhOKz3i4NbG\n\tRLG9obZBWAD1+5RPhEbLIWfTOyJwzYHRxoLVmSmg=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20251023144841.403689-31-stefan.klug@ideasonboard.com>","References":"<20251023144841.403689-1-stefan.klug@ideasonboard.com>\n\t<20251023144841.403689-31-stefan.klug@ideasonboard.com>","Subject":"Re: [PATCH v2 30/35] libcamera: dw100_vertexmap: Implement\n\tparametric dewarping","From":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"Stefan Klug <stefan.klug@ideasonboard.com>","To":"Stefan Klug <stefan.klug@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Fri, 07 Nov 2025 18:54:19 +0900","Message-ID":"<176250925991.2116251.3492112143066097139@neptunite.rasen.tech>","User-Agent":"alot/0.0.0","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>"}}]