[{"id":39161,"web_url":"https://patchwork.libcamera.org/comment/39161/","msgid":"<7ace050d-22f5-40c6-8e61-7f7610747410@ideasonboard.com>","date":"2026-06-17T12:39:16","subject":"Re: [PATCH 1/2] libcamera: converter: converter_dw100: Refactor\n\tdewarp param handling","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"2026. 06. 17. 11:58 keltezéssel, Stefan Klug írta:\n> There is already a ConverterDW100Module::DewarpParams class while in\n> Dw100VertexMap the parameters are handled separately for no good reason.\n> Create a Dw100VertexMap::DewarpParams struct for that purpose and adjust\n> the code accordingly. This has the added benefit that the size checking\n> for the coefficient list can now be done in the tuning file loading code\n> where it belongs.\n> \n> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> ---\n>   .../internal/converter/converter_dw100.h      |  7 +-\n>   .../converter/converter_dw100_vertexmap.h     | 35 ++++++--\n>   src/libcamera/converter/converter_dw100.cpp   | 15 +++-\n>   .../converter/converter_dw100_vertexmap.cpp   | 82 ++++++++-----------\n>   4 files changed, 75 insertions(+), 64 deletions(-)\n> \n> diff --git a/include/libcamera/internal/converter/converter_dw100.h b/include/libcamera/internal/converter/converter_dw100.h\n> index 1db1aadcb06b..003f5eb954e9 100644\n> --- a/include/libcamera/internal/converter/converter_dw100.h\n> +++ b/include/libcamera/internal/converter/converter_dw100.h\n> @@ -69,18 +69,13 @@ private:\n>   \tint applyControls(const Stream *stream, const V4L2Request *request);\n>   \tvoid reinitRequest(V4L2Request *request);\n> \n> -\tstruct DewarpParms {\n> -\t\tMatrix<double, 3, 3> cm;\n> -\t\tstd::vector<double> coeffs;\n> -\t};\n> -\n>   \tstruct VertexMapInfo {\n>   \t\tDw100VertexMap map;\n>   \t\tbool update;\n>   \t};\n> \n>   \tstd::map<const Stream *, VertexMapInfo> vertexMaps_;\n> -\tstd::optional<DewarpParms> dewarpParams_;\n> +\tstd::optional<Dw100VertexMap::DewarpParams> dewarpParams_;\n>   \tunsigned int inputBufferCount_;\n>   \tV4L2M2MConverter converter_;\n>   \tRectangle sensorCrop_;\n> diff --git a/include/libcamera/internal/converter/converter_dw100_vertexmap.h b/include/libcamera/internal/converter/converter_dw100_vertexmap.h\n> index 3bf80ea66dc7..787498c7fdb5 100644\n> --- a/include/libcamera/internal/converter/converter_dw100_vertexmap.h\n> +++ b/include/libcamera/internal/converter/converter_dw100_vertexmap.h\n> @@ -9,6 +9,7 @@\n> \n>   #include <assert.h>\n>   #include <cmath>\n> +#include <optional>\n>   #include <stdint.h>\n>   #include <vector>\n> \n> @@ -30,6 +31,33 @@ public:\n>   \t\tCrop = 1,\n>   \t};\n> \n> +\tstruct DewarpParams {\n> +\t\tDewarpParams() : cm(Matrix<double, 3, 3>::identity()),\n> +\t\t\t\t coefficients({})\n> +\t\t{\n> +\t\t}\n> +\n> +\t\tMatrix<double, 3, 3> cm;\n> +\n> +\t\tunion {\n> +\t\t\tstruct {\n> +\t\t\t\tdouble k1;\n> +\t\t\t\tdouble k2;\n> +\t\t\t\tdouble p1;\n> +\t\t\t\tdouble p2;\n> +\t\t\t\tdouble k3;\n> +\t\t\t\tdouble k4;\n> +\t\t\t\tdouble k5;\n> +\t\t\t\tdouble k6;\n> +\t\t\t\tdouble s1;\n> +\t\t\t\tdouble s2;\n> +\t\t\t\tdouble s3;\n> +\t\t\t\tdouble s4;\n> +\t\t\t} coefficient;\n> +\t\t\tstd::array<double, 12> coefficients;\n> +\t\t};\n\nI'm a bit worried about this part. As far as I am aware only the active member of\na union can technically be read (and there is the common initial sequence rule,\nbut I don't think it applies here, between a struct and and array). So I guess one\nway to avoid this is to define a function for each. Or am I missing something that\nmakes this well-defined?\n\n\n> +\t};\n> +\n>   \tvoid applyLimits();\n>   \tvoid setInputSize(const Size &size)\n>   \t{\n> @@ -60,8 +88,7 @@ public:\n>   \tvoid setMode(const ScaleMode mode) { mode_ = mode; }\n>   \tScaleMode mode() const { return mode_; }\n> \n> -\tint setDewarpParams(const Matrix<double, 3, 3> &cm, const Span<const double> &coeffs);\n> -\tbool dewarpParamsValid() { return dewarpParamsValid_; }\n> +\tvoid setDewarpParams(const DewarpParams &params) { dewarpParams_ = params; }\n> \n>   \tvoid setLensDewarpEnable(bool enable) { lensDewarpEnable_ = enable; }\n>   \tbool lensDewarpEnable() { return lensDewarpEnable_; }\n> @@ -85,10 +112,8 @@ private:\n>   \tPoint effectiveOffset_;\n>   \tRectangle effectiveScalerCrop_;\n> \n> -\tMatrix<double, 3, 3> dewarpM_ = Matrix<double, 3, 3>::identity();\n> -\tstd::array<double, 12> dewarpCoeffs_;\n> +\tstd::optional<DewarpParams> dewarpParams_;\n>   \tbool lensDewarpEnable_ = true;\n> -\tbool dewarpParamsValid_ = false;\n>   };\n> \n>   } /* namespace libcamera */\n> diff --git a/src/libcamera/converter/converter_dw100.cpp b/src/libcamera/converter/converter_dw100.cpp\n> index 44f7ec035e62..34f81c6b9fab 100644\n> --- a/src/libcamera/converter/converter_dw100.cpp\n> +++ b/src/libcamera/converter/converter_dw100.cpp\n> @@ -15,6 +15,7 @@\n>   #include <libcamera/stream.h>\n> \n>   #include \"libcamera/internal/converter.h\"\n> +#include \"libcamera/internal/converter/converter_dw100_vertexmap.h\"\n>   #include \"libcamera/internal/converter/converter_v4l2_m2m.h\"\n>   #include \"libcamera/internal/media_device.h\"\n>   #include \"libcamera/internal/v4l2_videodevice.h\"\n> @@ -99,7 +100,7 @@ ConverterDW100Module::createModule(DeviceEnumerator *enumerator)\n>    */\n>   int ConverterDW100Module::init(const ValueNode &params)\n>   {\n> -\tDewarpParms dp;\n> +\tDw100VertexMap::DewarpParams dp;\n> \n>   \tauto &cm = params[\"cm\"];\n>   \tauto &coefficients = params[\"coefficients\"];\n> @@ -131,7 +132,15 @@ int ConverterDW100Module::init(const ValueNode &params)\n>   \t\tLOG(Converter, Error) << \"Dewarp parameters 'coefficients' value is not a list\";\n>   \t\treturn -EINVAL;\n>   \t}\n> -\tdp.coeffs = std::move(*coeffs);\n> +\n> +\tif (coeffs->size() != 4 && coeffs->size() != 5 &&\n> +\t    coeffs->size() != 8 && coeffs->size() != 12) {\n> +\t\tLOG(Converter, Error)\n> +\t\t\t<< \"Dewarp 'coefficients' must have 4, 5, 8 or 12 values\";\n> +\t\treturn -EINVAL;\n> +\t}\n> +\n> +\tstd::copy(coeffs->begin(), coeffs->end(), &dp.coefficients[0]);\n> \n>   \tdewarpParams_ = dp;\n> \n> @@ -163,7 +172,7 @@ int ConverterDW100Module::configure(const StreamConfiguration &inputCfg,\n>   \t\tvertexMap.setSensorCrop(sensorCrop_);\n> \n>   \t\tif (dewarpParams_)\n> -\t\t\tvertexMap.setDewarpParams(dewarpParams_->cm, dewarpParams_->coeffs);\n> +\t\t\tvertexMap.setDewarpParams(*dewarpParams_);\n>   \t\tinfo.update = true;\n>   \t}\n> \n> diff --git a/src/libcamera/converter/converter_dw100_vertexmap.cpp b/src/libcamera/converter/converter_dw100_vertexmap.cpp\n> index 6e238736c435..31d992546857 100644\n> --- a/src/libcamera/converter/converter_dw100_vertexmap.cpp\n> +++ b/src/libcamera/converter/converter_dw100_vertexmap.cpp\n> @@ -227,6 +227,20 @@ int dw100VerticesForLength(const int length)\n>    * into account within the possible limits.\n>    */\n> \n> +/**\n> + * \\struct Dw100VertexMap::DewarpParams\n> + * \\brief Structure combining all dewarp parameters\n> + *\n> + * \\var Dw100VertexMap::DewarpParams::cm\n> + * \\brief The camera matrix\n> + *\n> + * \\var Dw100VertexMap::DewarpParams::coefficient\n> + * \\brief Structure containing the lens dewarp coefficients\n> +\n> + * See https://docs.opencv.org/4.12.0/d9/d0c/group__calib3d.html for further\n> + * details on the model.\n> + */\n> +\n>   /**\n>    * \\brief Apply limits on scale and offset\n>    *\n> @@ -549,7 +563,7 @@ std::vector<uint32_t> Dw100VertexMap::getVertexMap()\n> \n>   \t\t\tp = transformPoint(outputToSensor, p);\n> \n> -\t\t\tif (dewarpParamsValid_ && lensDewarpEnable_)\n> +\t\t\tif (lensDewarpEnable_)\n>   \t\t\t\tp = dewarpPoint(p);\n> \n>   \t\t\tp = transformPoint(sensorToInput, p);\n> @@ -566,41 +580,13 @@ std::vector<uint32_t> Dw100VertexMap::getVertexMap()\n> \n>   /**\n>    * \\brief Set the dewarp parameters\n> - * \\param cm The camera matrix\n> - * \\param coeffs The dewarp coefficients\n> + * \\param params The dewarp parameters\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> -\t\t\t\t    const Span<const double> &coeffs)\n> -{\n> -\tdewarpM_ = cm;\n> -\tdewarpCoeffs_.fill(0.0);\n> -\n> -\tif (coeffs.size() != 4 && coeffs.size() != 5 &&\n> -\t    coeffs.size() != 8 && coeffs.size() != 12) {\n> -\t\tLOG(Converter, Error)\n> -\t\t\t<< \"Dewarp 'coefficients' must have 4, 5, 8 or 12 values\";\n> -\t\tdewarpParamsValid_ = false;\n> -\t\treturn -EINVAL;\n> -\t}\n> -\tstd::copy(coeffs.begin(), coeffs.end(), dewarpCoeffs_.begin());\n> -\n> -\tdewarpParamsValid_ = true;\n> -\treturn 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, false otherwise\n>    */\n> \n>   /**\n> @@ -627,32 +613,28 @@ int Dw100VertexMap::setDewarpParams(const Matrix<double, 3, 3> &cm,\n>    */\n>   Vector2d Dw100VertexMap::dewarpPoint(const Vector2d &p)\n>   {\n> +\tif (!dewarpParams_.has_value())\n> +\t\treturn p;\n> +\n>   \tdouble x, y;\n>   \tdouble xout, yout;\n> -\tdouble k1 = dewarpCoeffs_[0];\n> -\tdouble k2 = dewarpCoeffs_[1];\n> -\tdouble p1 = dewarpCoeffs_[2];\n> -\tdouble p2 = dewarpCoeffs_[3];\n> -\tdouble k3 = dewarpCoeffs_[4];\n> -\tdouble k4 = dewarpCoeffs_[5];\n> -\tdouble k5 = dewarpCoeffs_[6];\n> -\tdouble k6 = dewarpCoeffs_[7];\n> -\tdouble s1 = dewarpCoeffs_[8];\n> -\tdouble s2 = dewarpCoeffs_[9];\n> -\tdouble s3 = dewarpCoeffs_[10];\n> -\tdouble s4 = dewarpCoeffs_[11];\n> +\tauto &cm = dewarpParams_->cm;\n> +\tauto &c = dewarpParams_->coefficient;\n> \n> -\ty = (p.y() - dewarpM_[1][2]) / dewarpM_[1][1];\n> -\tx = (p.x() - dewarpM_[0][2] - y * dewarpM_[0][1]) / dewarpM_[0][0];\n> +\ty = (p.y() - cm[1][2]) / cm[1][1];\n> +\tx = (p.x() - cm[0][2] - y * cm[0][1]) / cm[0][0];\n> \n>   \tdouble r2 = x * x + y * y;\n> -\tdouble d = (1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2) /\n> -\t\t   (1 + k4 * r2 + k5 * r2 * r2 + k6 * r2 * r2 * r2);\n> -\txout = x * d + 2 * p1 * x * y + p2 * (r2 + 2 * x * x) + s1 * r2 + s2 * r2 * r2;\n> -\tyout = y * d + 2 * p2 * x * y + p1 * (r2 + 2 * y * y) + s3 * r2 + s4 * r2 * r2;\n> +\tdouble r4 = r2 * r2;\n> +\tdouble r6 = r2 * r2 * r2;\n> +\tdouble d = (1 + c.k1 * r2 + c.k2 * r4 + c.k3 * r6) /\n> +\t\t   (1 + c.k4 * r2 + c.k5 * r4 + c.k6 * r6);\n> \n> -\treturn { { xout * dewarpM_[0][0] + yout * dewarpM_[0][1] + dewarpM_[0][2],\n> -\t\t   yout * dewarpM_[1][1] + dewarpM_[1][2] } };\n> +\txout = x * d + 2 * c.p1 * x * y + c.p2 * (r2 + 2 * x * x) + c.s1 * r2 + c.s2 * r4;\n> +\tyout = y * d + 2 * c.p2 * x * y + c.p1 * (r2 + 2 * y * y) + c.s3 * r2 + c.s4 * r4;\n> +\n> +\treturn { { xout * cm[0][0] + yout * cm[0][1] + cm[0][2],\n> +\t\t   yout * cm[1][1] + cm[1][2] } };\n>   }\n> \n>   } /* namespace libcamera */\n> --\n> 2.53.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 D6866BF415\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 17 Jun 2026 12:39:22 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E94F2628AD;\n\tWed, 17 Jun 2026 14:39:21 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id DFFDC6287E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 17 Jun 2026 14:39:19 +0200 (CEST)","from [192.168.33.29] (185.221.142.169.nat.pool.zt.hu\n\t[185.221.142.169])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 354A097F;\n\tWed, 17 Jun 2026 14:38:45 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"b4XJL9a8\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1781699925;\n\tbh=uIFIw3YF+/FU8+1vAub7C6VJU9rXCEnyFhqDlHK5YNk=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=b4XJL9a8SB49Z1dPdytY2UHjCrzdD+rz0zAVkH/PuSOXU/UfGwVOtQ0o7mR/7P6ok\n\tJorJ4vE+g2jLbbg1p1dWI5TGZCXC2+XrQGtmlEMzl9jbrsjQEVqXR+LaZyxXBBulkV\n\tmPL+A9lRUmd1fBP6yVPr5B1HCWYxYoal9mFv6odc=","Message-ID":"<7ace050d-22f5-40c6-8e61-7f7610747410@ideasonboard.com>","Date":"Wed, 17 Jun 2026 14:39:16 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH 1/2] libcamera: converter: converter_dw100: Refactor\n\tdewarp param handling","To":"Stefan Klug <stefan.klug@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20260617095857.667583-1-stefan.klug@ideasonboard.com>\n\t<4ncuHGbKHJPWpI-bclkt46v_MoLRwBYG_HfCh3tcpJgsfvhWTRn_ldMmGXVOrPfvxIG-kLer_uM0F71W7ryRWQ==@protonmail.internalid>\n\t<20260617095857.667583-2-stefan.klug@ideasonboard.com>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20260617095857.667583-2-stefan.klug@ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"8bit","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>"}},{"id":39162,"web_url":"https://patchwork.libcamera.org/comment/39162/","msgid":"<178170469300.706072.9682935407927794729@localhost>","date":"2026-06-17T13:58:13","subject":"Re: [PATCH 1/2] libcamera: converter: converter_dw100: Refactor\n\tdewarp param handling","submitter":{"id":184,"url":"https://patchwork.libcamera.org/api/people/184/","name":"Stefan Klug","email":"stefan.klug@ideasonboard.com"},"content":"Hi Barnabás,\n\nThanks for the review.\n\nQuoting Barnabás Pőcze (2026-06-17 14:39:16)\n> 2026. 06. 17. 11:58 keltezéssel, Stefan Klug írta:\n> > There is already a ConverterDW100Module::DewarpParams class while in\n> > Dw100VertexMap the parameters are handled separately for no good reason.\n> > Create a Dw100VertexMap::DewarpParams struct for that purpose and adjust\n> > the code accordingly. This has the added benefit that the size checking\n> > for the coefficient list can now be done in the tuning file loading code\n> > where it belongs.\n> > \n> > Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> > ---\n> >   .../internal/converter/converter_dw100.h      |  7 +-\n> >   .../converter/converter_dw100_vertexmap.h     | 35 ++++++--\n> >   src/libcamera/converter/converter_dw100.cpp   | 15 +++-\n> >   .../converter/converter_dw100_vertexmap.cpp   | 82 ++++++++-----------\n> >   4 files changed, 75 insertions(+), 64 deletions(-)\n> > \n> > diff --git a/include/libcamera/internal/converter/converter_dw100.h b/include/libcamera/internal/converter/converter_dw100.h\n> > index 1db1aadcb06b..003f5eb954e9 100644\n> > --- a/include/libcamera/internal/converter/converter_dw100.h\n> > +++ b/include/libcamera/internal/converter/converter_dw100.h\n> > @@ -69,18 +69,13 @@ private:\n> >       int applyControls(const Stream *stream, const V4L2Request *request);\n> >       void reinitRequest(V4L2Request *request);\n> > \n> > -     struct DewarpParms {\n> > -             Matrix<double, 3, 3> cm;\n> > -             std::vector<double> coeffs;\n> > -     };\n> > -\n> >       struct VertexMapInfo {\n> >               Dw100VertexMap map;\n> >               bool update;\n> >       };\n> > \n> >       std::map<const Stream *, VertexMapInfo> vertexMaps_;\n> > -     std::optional<DewarpParms> dewarpParams_;\n> > +     std::optional<Dw100VertexMap::DewarpParams> dewarpParams_;\n> >       unsigned int inputBufferCount_;\n> >       V4L2M2MConverter converter_;\n> >       Rectangle sensorCrop_;\n> > diff --git a/include/libcamera/internal/converter/converter_dw100_vertexmap.h b/include/libcamera/internal/converter/converter_dw100_vertexmap.h\n> > index 3bf80ea66dc7..787498c7fdb5 100644\n> > --- a/include/libcamera/internal/converter/converter_dw100_vertexmap.h\n> > +++ b/include/libcamera/internal/converter/converter_dw100_vertexmap.h\n> > @@ -9,6 +9,7 @@\n> > \n> >   #include <assert.h>\n> >   #include <cmath>\n> > +#include <optional>\n> >   #include <stdint.h>\n> >   #include <vector>\n> > \n> > @@ -30,6 +31,33 @@ public:\n> >               Crop = 1,\n> >       };\n> > \n> > +     struct DewarpParams {\n> > +             DewarpParams() : cm(Matrix<double, 3, 3>::identity()),\n> > +                              coefficients({})\n> > +             {\n> > +             }\n> > +\n> > +             Matrix<double, 3, 3> cm;\n> > +\n> > +             union {\n> > +                     struct {\n> > +                             double k1;\n> > +                             double k2;\n> > +                             double p1;\n> > +                             double p2;\n> > +                             double k3;\n> > +                             double k4;\n> > +                             double k5;\n> > +                             double k6;\n> > +                             double s1;\n> > +                             double s2;\n> > +                             double s3;\n> > +                             double s4;\n> > +                     } coefficient;\n> > +                     std::array<double, 12> coefficients;\n> > +             };\n> \n> I'm a bit worried about this part. As far as I am aware only the active member of\n> a union can technically be read (and there is the common initial sequence rule,\n> but I don't think it applies here, between a struct and and array). So I guess one\n> way to avoid this is to define a function for each. Or am I missing something that\n> makes this well-defined?\n\nThanks for pointing that out. I thought that is a common pattern.\nAccording to https://stackoverflow.com/a/21763025 it actually seems to\nbe legal and the common initial sequence rule applies?\n\nBut nevertheless I changed the code to drop the union. I like that\nversion better. So I'll post a v2 soon.\n\nBest regards,\nStefan\n\n> \n> \n> > +     };\n> > +\n> >       void applyLimits();\n> >       void setInputSize(const Size &size)\n> >       {\n> > @@ -60,8 +88,7 @@ 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> > +     void setDewarpParams(const DewarpParams &params) { dewarpParams_ = params; }\n> > \n> >       void setLensDewarpEnable(bool enable) { lensDewarpEnable_ = enable; }\n> >       bool lensDewarpEnable() { return lensDewarpEnable_; }\n> > @@ -85,10 +112,8 @@ private:\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> > +     std::optional<DewarpParams> dewarpParams_;\n> >       bool lensDewarpEnable_ = true;\n> > -     bool dewarpParamsValid_ = false;\n> >   };\n> > \n> >   } /* namespace libcamera */\n> > diff --git a/src/libcamera/converter/converter_dw100.cpp b/src/libcamera/converter/converter_dw100.cpp\n> > index 44f7ec035e62..34f81c6b9fab 100644\n> > --- a/src/libcamera/converter/converter_dw100.cpp\n> > +++ b/src/libcamera/converter/converter_dw100.cpp\n> > @@ -15,6 +15,7 @@\n> >   #include <libcamera/stream.h>\n> > \n> >   #include \"libcamera/internal/converter.h\"\n> > +#include \"libcamera/internal/converter/converter_dw100_vertexmap.h\"\n> >   #include \"libcamera/internal/converter/converter_v4l2_m2m.h\"\n> >   #include \"libcamera/internal/media_device.h\"\n> >   #include \"libcamera/internal/v4l2_videodevice.h\"\n> > @@ -99,7 +100,7 @@ ConverterDW100Module::createModule(DeviceEnumerator *enumerator)\n> >    */\n> >   int ConverterDW100Module::init(const ValueNode &params)\n> >   {\n> > -     DewarpParms dp;\n> > +     Dw100VertexMap::DewarpParams dp;\n> > \n> >       auto &cm = params[\"cm\"];\n> >       auto &coefficients = params[\"coefficients\"];\n> > @@ -131,7 +132,15 @@ int ConverterDW100Module::init(const ValueNode &params)\n> >               LOG(Converter, Error) << \"Dewarp parameters 'coefficients' value is not a list\";\n> >               return -EINVAL;\n> >       }\n> > -     dp.coeffs = std::move(*coeffs);\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> > +             return -EINVAL;\n> > +     }\n> > +\n> > +     std::copy(coeffs->begin(), coeffs->end(), &dp.coefficients[0]);\n> > \n> >       dewarpParams_ = dp;\n> > \n> > @@ -163,7 +172,7 @@ int ConverterDW100Module::configure(const StreamConfiguration &inputCfg,\n> >               vertexMap.setSensorCrop(sensorCrop_);\n> > \n> >               if (dewarpParams_)\n> > -                     vertexMap.setDewarpParams(dewarpParams_->cm, dewarpParams_->coeffs);\n> > +                     vertexMap.setDewarpParams(*dewarpParams_);\n> >               info.update = true;\n> >       }\n> > \n> > diff --git a/src/libcamera/converter/converter_dw100_vertexmap.cpp b/src/libcamera/converter/converter_dw100_vertexmap.cpp\n> > index 6e238736c435..31d992546857 100644\n> > --- a/src/libcamera/converter/converter_dw100_vertexmap.cpp\n> > +++ b/src/libcamera/converter/converter_dw100_vertexmap.cpp\n> > @@ -227,6 +227,20 @@ int dw100VerticesForLength(const int length)\n> >    * into account within the possible limits.\n> >    */\n> > \n> > +/**\n> > + * \\struct Dw100VertexMap::DewarpParams\n> > + * \\brief Structure combining all dewarp parameters\n> > + *\n> > + * \\var Dw100VertexMap::DewarpParams::cm\n> > + * \\brief The camera matrix\n> > + *\n> > + * \\var Dw100VertexMap::DewarpParams::coefficient\n> > + * \\brief Structure containing the lens dewarp coefficients\n> > +\n> > + * See https://docs.opencv.org/4.12.0/d9/d0c/group__calib3d.html for further\n> > + * details on the model.\n> > + */\n> > +\n> >   /**\n> >    * \\brief Apply limits on scale and offset\n> >    *\n> > @@ -549,7 +563,7 @@ std::vector<uint32_t> Dw100VertexMap::getVertexMap()\n> > \n> >                       p = transformPoint(outputToSensor, p);\n> > \n> > -                     if (dewarpParamsValid_ && lensDewarpEnable_)\n> > +                     if (lensDewarpEnable_)\n> >                               p = dewarpPoint(p);\n> > \n> >                       p = transformPoint(sensorToInput, p);\n> > @@ -566,41 +580,13 @@ std::vector<uint32_t> Dw100VertexMap::getVertexMap()\n> > \n> >   /**\n> >    * \\brief Set the dewarp parameters\n> > - * \\param cm The camera matrix\n> > - * \\param coeffs The dewarp coefficients\n> > + * \\param params The dewarp parameters\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, false otherwise\n> >    */\n> > \n> >   /**\n> > @@ -627,32 +613,28 @@ int Dw100VertexMap::setDewarpParams(const Matrix<double, 3, 3> &cm,\n> >    */\n> >   Vector2d Dw100VertexMap::dewarpPoint(const Vector2d &p)\n> >   {\n> > +     if (!dewarpParams_.has_value())\n> > +             return p;\n> > +\n> >       double x, y;\n> >       double xout, yout;\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> > +     auto &cm = dewarpParams_->cm;\n> > +     auto &c = dewarpParams_->coefficient;\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> > +     y = (p.y() - cm[1][2]) / cm[1][1];\n> > +     x = (p.x() - cm[0][2] - y * cm[0][1]) / cm[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> > -     xout = x * d + 2 * p1 * x * y + p2 * (r2 + 2 * x * x) + s1 * r2 + s2 * r2 * r2;\n> > -     yout = y * d + 2 * p2 * x * y + p1 * (r2 + 2 * y * y) + s3 * r2 + s4 * r2 * r2;\n> > +     double r4 = r2 * r2;\n> > +     double r6 = r2 * r2 * r2;\n> > +     double d = (1 + c.k1 * r2 + c.k2 * r4 + c.k3 * r6) /\n> > +                (1 + c.k4 * r2 + c.k5 * r4 + c.k6 * r6);\n> > \n> > -     return { { xout * dewarpM_[0][0] + yout * dewarpM_[0][1] + dewarpM_[0][2],\n> > -                yout * dewarpM_[1][1] + dewarpM_[1][2] } };\n> > +     xout = x * d + 2 * c.p1 * x * y + c.p2 * (r2 + 2 * x * x) + c.s1 * r2 + c.s2 * r4;\n> > +     yout = y * d + 2 * c.p2 * x * y + c.p1 * (r2 + 2 * y * y) + c.s3 * r2 + c.s4 * r4;\n> > +\n> > +     return { { xout * cm[0][0] + yout * cm[0][1] + cm[0][2],\n> > +                yout * cm[1][1] + cm[1][2] } };\n> >   }\n> > \n> >   } /* namespace libcamera */\n> > --\n> > 2.53.0\n> > \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 D91EDC3261\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 17 Jun 2026 13:58:17 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E18AD62886;\n\tWed, 17 Jun 2026 15:58:16 +0200 (CEST)","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 5629A60579\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 17 Jun 2026 15:58:15 +0200 (CEST)","from ideasonboard.com (unknown\n\t[IPv6:2a00:6020:448c:6c00:b0bc:b9b5:573f:6fae])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id DD2B218E9;\n\tWed, 17 Jun 2026 15:57:40 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"pP0AQ+g6\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1781704661;\n\tbh=ChTBMx5px8oDgWgJYq1Nhbtg1aSM5napU/gR7ONU1yU=;\n\th=In-Reply-To:References:Subject:From:To:Date:From;\n\tb=pP0AQ+g634NCgLzPPTseB8tHjf6oSc9ZqzBj34mgdgtWfMDs80WeCVtquSAZjpZb6\n\tCx1hgIUxmWd6rvmPPl9yNHjbtQV/6LXtMiy4vYeKKSYHgW7lQ4XLcPh18FBpgqN/N6\n\tFF+nW9FOJEJAMEJTzPLrW8AUJRwk0eVZDDWp6z5o=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<7ace050d-22f5-40c6-8e61-7f7610747410@ideasonboard.com>","References":"<20260617095857.667583-1-stefan.klug@ideasonboard.com>\n\t<4ncuHGbKHJPWpI-bclkt46v_MoLRwBYG_HfCh3tcpJgsfvhWTRn_ldMmGXVOrPfvxIG-kLer_uM0F71W7ryRWQ==@protonmail.internalid>\n\t<20260617095857.667583-2-stefan.klug@ideasonboard.com>\n\t<7ace050d-22f5-40c6-8e61-7f7610747410@ideasonboard.com>","Subject":"Re: [PATCH 1/2] libcamera: converter: converter_dw100: Refactor\n\tdewarp param handling","From":"Stefan Klug <stefan.klug@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Wed, 17 Jun 2026 15:58:13 +0200","Message-ID":"<178170469300.706072.9682935407927794729@localhost>","User-Agent":"alot/0.12.dev43+g2cacc0d03","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>"}},{"id":39164,"web_url":"https://patchwork.libcamera.org/comment/39164/","msgid":"<a4731525-7f05-418d-9ccc-56c6e78a3165@ideasonboard.com>","date":"2026-06-17T14:26:30","subject":"Re: [PATCH 1/2] libcamera: converter: converter_dw100: Refactor\n\tdewarp param handling","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"2026. 06. 17. 15:58 keltezéssel, Stefan Klug írta:\n> Hi Barnabás,\n> \n> Thanks for the review.\n> \n> Quoting Barnabás Pőcze (2026-06-17 14:39:16)\n>> 2026. 06. 17. 11:58 keltezéssel, Stefan Klug írta:\n>>> There is already a ConverterDW100Module::DewarpParams class while in\n>>> Dw100VertexMap the parameters are handled separately for no good reason.\n>>> Create a Dw100VertexMap::DewarpParams struct for that purpose and adjust\n>>> the code accordingly. This has the added benefit that the size checking\n>>> for the coefficient list can now be done in the tuning file loading code\n>>> where it belongs.\n>>>\n>>> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n>>> ---\n>>>    .../internal/converter/converter_dw100.h      |  7 +-\n>>>    .../converter/converter_dw100_vertexmap.h     | 35 ++++++--\n>>>    src/libcamera/converter/converter_dw100.cpp   | 15 +++-\n>>>    .../converter/converter_dw100_vertexmap.cpp   | 82 ++++++++-----------\n>>>    4 files changed, 75 insertions(+), 64 deletions(-)\n>>>\n>>> diff --git a/include/libcamera/internal/converter/converter_dw100.h b/include/libcamera/internal/converter/converter_dw100.h\n>>> index 1db1aadcb06b..003f5eb954e9 100644\n>>> --- a/include/libcamera/internal/converter/converter_dw100.h\n>>> +++ b/include/libcamera/internal/converter/converter_dw100.h\n>>> @@ -69,18 +69,13 @@ private:\n>>>        int applyControls(const Stream *stream, const V4L2Request *request);\n>>>        void reinitRequest(V4L2Request *request);\n>>>\n>>> -     struct DewarpParms {\n>>> -             Matrix<double, 3, 3> cm;\n>>> -             std::vector<double> coeffs;\n>>> -     };\n>>> -\n>>>        struct VertexMapInfo {\n>>>                Dw100VertexMap map;\n>>>                bool update;\n>>>        };\n>>>\n>>>        std::map<const Stream *, VertexMapInfo> vertexMaps_;\n>>> -     std::optional<DewarpParms> dewarpParams_;\n>>> +     std::optional<Dw100VertexMap::DewarpParams> dewarpParams_;\n>>>        unsigned int inputBufferCount_;\n>>>        V4L2M2MConverter converter_;\n>>>        Rectangle sensorCrop_;\n>>> diff --git a/include/libcamera/internal/converter/converter_dw100_vertexmap.h b/include/libcamera/internal/converter/converter_dw100_vertexmap.h\n>>> index 3bf80ea66dc7..787498c7fdb5 100644\n>>> --- a/include/libcamera/internal/converter/converter_dw100_vertexmap.h\n>>> +++ b/include/libcamera/internal/converter/converter_dw100_vertexmap.h\n>>> @@ -9,6 +9,7 @@\n>>>\n>>>    #include <assert.h>\n>>>    #include <cmath>\n>>> +#include <optional>\n>>>    #include <stdint.h>\n>>>    #include <vector>\n>>>\n>>> @@ -30,6 +31,33 @@ public:\n>>>                Crop = 1,\n>>>        };\n>>>\n>>> +     struct DewarpParams {\n>>> +             DewarpParams() : cm(Matrix<double, 3, 3>::identity()),\n>>> +                              coefficients({})\n>>> +             {\n>>> +             }\n>>> +\n>>> +             Matrix<double, 3, 3> cm;\n>>> +\n>>> +             union {\n>>> +                     struct {\n>>> +                             double k1;\n>>> +                             double k2;\n>>> +                             double p1;\n>>> +                             double p2;\n>>> +                             double k3;\n>>> +                             double k4;\n>>> +                             double k5;\n>>> +                             double k6;\n>>> +                             double s1;\n>>> +                             double s2;\n>>> +                             double s3;\n>>> +                             double s4;\n>>> +                     } coefficient;\n>>> +                     std::array<double, 12> coefficients;\n>>> +             };\n>>\n>> I'm a bit worried about this part. As far as I am aware only the active member of\n>> a union can technically be read (and there is the common initial sequence rule,\n>> but I don't think it applies here, between a struct and and array). So I guess one\n>> way to avoid this is to define a function for each. Or am I missing something that\n>> makes this well-defined?\n> \n> Thanks for pointing that out. I thought that is a common pattern.\n> According to https://stackoverflow.com/a/21763025 it actually seems to\n> be legal and the common initial sequence rule applies?\n\nFunny, because I have come across https://stackoverflow.com/a/36080743\nwhich seems to come to a different conclusion. And I'm inclined to agree\nbased on my reading of the common initial sequence rules, but entirely sure.\n\n\n> \n> But nevertheless I changed the code to drop the union. I like that\n> version better. So I'll post a v2 soon.\n> \n> Best regards,\n> Stefan\n> \n>>\n>>\n>>> +     };\n>>> +\n>>>        void applyLimits();\n>>>        void setInputSize(const Size &size)\n>>>        {\n>>> @@ -60,8 +88,7 @@ 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>>> +     void setDewarpParams(const DewarpParams &params) { dewarpParams_ = params; }\n>>>\n>>>        void setLensDewarpEnable(bool enable) { lensDewarpEnable_ = enable; }\n>>>        bool lensDewarpEnable() { return lensDewarpEnable_; }\n>>> @@ -85,10 +112,8 @@ private:\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>>> +     std::optional<DewarpParams> dewarpParams_;\n>>>        bool lensDewarpEnable_ = true;\n>>> -     bool dewarpParamsValid_ = false;\n>>>    };\n>>>\n>>>    } /* namespace libcamera */\n>>> diff --git a/src/libcamera/converter/converter_dw100.cpp b/src/libcamera/converter/converter_dw100.cpp\n>>> index 44f7ec035e62..34f81c6b9fab 100644\n>>> --- a/src/libcamera/converter/converter_dw100.cpp\n>>> +++ b/src/libcamera/converter/converter_dw100.cpp\n>>> @@ -15,6 +15,7 @@\n>>>    #include <libcamera/stream.h>\n>>>\n>>>    #include \"libcamera/internal/converter.h\"\n>>> +#include \"libcamera/internal/converter/converter_dw100_vertexmap.h\"\n>>>    #include \"libcamera/internal/converter/converter_v4l2_m2m.h\"\n>>>    #include \"libcamera/internal/media_device.h\"\n>>>    #include \"libcamera/internal/v4l2_videodevice.h\"\n>>> @@ -99,7 +100,7 @@ ConverterDW100Module::createModule(DeviceEnumerator *enumerator)\n>>>     */\n>>>    int ConverterDW100Module::init(const ValueNode &params)\n>>>    {\n>>> -     DewarpParms dp;\n>>> +     Dw100VertexMap::DewarpParams dp;\n>>>\n>>>        auto &cm = params[\"cm\"];\n>>>        auto &coefficients = params[\"coefficients\"];\n>>> @@ -131,7 +132,15 @@ int ConverterDW100Module::init(const ValueNode &params)\n>>>                LOG(Converter, Error) << \"Dewarp parameters 'coefficients' value is not a list\";\n>>>                return -EINVAL;\n>>>        }\n>>> -     dp.coeffs = std::move(*coeffs);\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>>> +             return -EINVAL;\n>>> +     }\n>>> +\n>>> +     std::copy(coeffs->begin(), coeffs->end(), &dp.coefficients[0]);\n>>>\n>>>        dewarpParams_ = dp;\n>>>\n>>> @@ -163,7 +172,7 @@ int ConverterDW100Module::configure(const StreamConfiguration &inputCfg,\n>>>                vertexMap.setSensorCrop(sensorCrop_);\n>>>\n>>>                if (dewarpParams_)\n>>> -                     vertexMap.setDewarpParams(dewarpParams_->cm, dewarpParams_->coeffs);\n>>> +                     vertexMap.setDewarpParams(*dewarpParams_);\n>>>                info.update = true;\n>>>        }\n>>>\n>>> diff --git a/src/libcamera/converter/converter_dw100_vertexmap.cpp b/src/libcamera/converter/converter_dw100_vertexmap.cpp\n>>> index 6e238736c435..31d992546857 100644\n>>> --- a/src/libcamera/converter/converter_dw100_vertexmap.cpp\n>>> +++ b/src/libcamera/converter/converter_dw100_vertexmap.cpp\n>>> @@ -227,6 +227,20 @@ int dw100VerticesForLength(const int length)\n>>>     * into account within the possible limits.\n>>>     */\n>>>\n>>> +/**\n>>> + * \\struct Dw100VertexMap::DewarpParams\n>>> + * \\brief Structure combining all dewarp parameters\n>>> + *\n>>> + * \\var Dw100VertexMap::DewarpParams::cm\n>>> + * \\brief The camera matrix\n>>> + *\n>>> + * \\var Dw100VertexMap::DewarpParams::coefficient\n>>> + * \\brief Structure containing the lens dewarp coefficients\n>>> +\n>>> + * See https://docs.opencv.org/4.12.0/d9/d0c/group__calib3d.html for further\n>>> + * details on the model.\n>>> + */\n>>> +\n>>>    /**\n>>>     * \\brief Apply limits on scale and offset\n>>>     *\n>>> @@ -549,7 +563,7 @@ std::vector<uint32_t> Dw100VertexMap::getVertexMap()\n>>>\n>>>                        p = transformPoint(outputToSensor, p);\n>>>\n>>> -                     if (dewarpParamsValid_ && lensDewarpEnable_)\n>>> +                     if (lensDewarpEnable_)\n>>>                                p = dewarpPoint(p);\n>>>\n>>>                        p = transformPoint(sensorToInput, p);\n>>> @@ -566,41 +580,13 @@ std::vector<uint32_t> Dw100VertexMap::getVertexMap()\n>>>\n>>>    /**\n>>>     * \\brief Set the dewarp parameters\n>>> - * \\param cm The camera matrix\n>>> - * \\param coeffs The dewarp coefficients\n>>> + * \\param params The dewarp parameters\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, false otherwise\n>>>     */\n>>>\n>>>    /**\n>>> @@ -627,32 +613,28 @@ int Dw100VertexMap::setDewarpParams(const Matrix<double, 3, 3> &cm,\n>>>     */\n>>>    Vector2d Dw100VertexMap::dewarpPoint(const Vector2d &p)\n>>>    {\n>>> +     if (!dewarpParams_.has_value())\n>>> +             return p;\n>>> +\n>>>        double x, y;\n>>>        double xout, yout;\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>>> +     auto &cm = dewarpParams_->cm;\n>>> +     auto &c = dewarpParams_->coefficient;\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>>> +     y = (p.y() - cm[1][2]) / cm[1][1];\n>>> +     x = (p.x() - cm[0][2] - y * cm[0][1]) / cm[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>>> -     xout = x * d + 2 * p1 * x * y + p2 * (r2 + 2 * x * x) + s1 * r2 + s2 * r2 * r2;\n>>> -     yout = y * d + 2 * p2 * x * y + p1 * (r2 + 2 * y * y) + s3 * r2 + s4 * r2 * r2;\n>>> +     double r4 = r2 * r2;\n>>> +     double r6 = r2 * r2 * r2;\n>>> +     double d = (1 + c.k1 * r2 + c.k2 * r4 + c.k3 * r6) /\n>>> +                (1 + c.k4 * r2 + c.k5 * r4 + c.k6 * r6);\n>>>\n>>> -     return { { xout * dewarpM_[0][0] + yout * dewarpM_[0][1] + dewarpM_[0][2],\n>>> -                yout * dewarpM_[1][1] + dewarpM_[1][2] } };\n>>> +     xout = x * d + 2 * c.p1 * x * y + c.p2 * (r2 + 2 * x * x) + c.s1 * r2 + c.s2 * r4;\n>>> +     yout = y * d + 2 * c.p2 * x * y + c.p1 * (r2 + 2 * y * y) + c.s3 * r2 + c.s4 * r4;\n>>> +\n>>> +     return { { xout * cm[0][0] + yout * cm[0][1] + cm[0][2],\n>>> +                yout * cm[1][1] + cm[1][2] } };\n>>>    }\n>>>\n>>>    } /* namespace libcamera */\n>>> --\n>>> 2.53.0\n>>>\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 A252AC3261\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 17 Jun 2026 14:26:36 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id ACC8F62893;\n\tWed, 17 Jun 2026 16:26:35 +0200 (CEST)","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 8B14860579\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 17 Jun 2026 16:26:33 +0200 (CEST)","from [192.168.33.29] (185.221.142.169.nat.pool.zt.hu\n\t[185.221.142.169])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 1FE532EC;\n\tWed, 17 Jun 2026 16:25:59 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"SnemS24F\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1781706359;\n\tbh=Bwj34+mwYZKJHZflRUTqtQUIz3ZGnv7BBg7uzBpi25g=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=SnemS24FY6+PgoV7D/c6rxc9UEPDjy0A9bu6Ab6ehGit962ij/+BGyTUmFVSQYDCO\n\tUreR2y8JexPHZwyRIrzd7/9mGkNmbauffdFq8ij8GAIo69ypKdsPY6BtGUPxUPvxnN\n\tgGlQyA5GOMVmx0Th04UV/9AcFhZoTLy80jDXSVr4=","Message-ID":"<a4731525-7f05-418d-9ccc-56c6e78a3165@ideasonboard.com>","Date":"Wed, 17 Jun 2026 16:26:30 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH 1/2] libcamera: converter: converter_dw100: Refactor\n\tdewarp param handling","To":"Stefan Klug <stefan.klug@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20260617095857.667583-1-stefan.klug@ideasonboard.com>\n\t<4ncuHGbKHJPWpI-bclkt46v_MoLRwBYG_HfCh3tcpJgsfvhWTRn_ldMmGXVOrPfvxIG-kLer_uM0F71W7ryRWQ==@protonmail.internalid>\n\t<20260617095857.667583-2-stefan.klug@ideasonboard.com>\n\t<7ace050d-22f5-40c6-8e61-7f7610747410@ideasonboard.com>\n\t<178170469300.706072.9682935407927794729@localhost>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<178170469300.706072.9682935407927794729@localhost>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"8bit","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>"}}]