[v1,28/33] libcamera: dw100_vertexmap: Implement parametric dewarping
diff mbox series

Message ID 20250930122726.1837524-29-stefan.klug@ideasonboard.com
State New
Headers show
Series
  • Full dewarper support on imx8mp
Related show

Commit Message

Stefan Klug Sept. 30, 2025, 12:26 p.m. UTC
Implement functions to allow lens dewarping based on the common lens
dewarp model used e.g. by OpenCV

See https://docs.opencv.org/3.4/da/d54/group__imgproc__transform.html#ga7dfb72c9cf9780a347fbe3d1c47e5d5a

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
---
 .../converter/converter_dw100_vertexmap.h     | 17 ++++
 .../converter/converter_dw100_vertexmap.cpp   | 87 +++++++++++++++++++
 2 files changed, 104 insertions(+)

Comments

Kieran Bingham Oct. 2, 2025, 9:57 p.m. UTC | #1
Quoting Stefan Klug (2025-09-30 13:26:49)
> Implement functions to allow lens dewarping based on the common lens
> dewarp model used e.g. by OpenCV
> 
> See https://docs.opencv.org/3.4/da/d54/group__imgproc__transform.html#ga7dfb72c9cf9780a347fbe3d1c47e5d5a
> 
> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
> ---
>  .../converter/converter_dw100_vertexmap.h     | 17 ++++
>  .../converter/converter_dw100_vertexmap.cpp   | 87 +++++++++++++++++++
>  2 files changed, 104 insertions(+)
> 
> diff --git a/include/libcamera/internal/converter/converter_dw100_vertexmap.h b/include/libcamera/internal/converter/converter_dw100_vertexmap.h
> index 9c6e0ffa4513..7a12c4cb7a50 100644
> --- a/include/libcamera/internal/converter/converter_dw100_vertexmap.h
> +++ b/include/libcamera/internal/converter/converter_dw100_vertexmap.h
> @@ -12,6 +12,9 @@
>  #include <libcamera/geometry.h>
>  #include <libcamera/transform.h>
>  
> +#include "libcamera/internal/matrix.h"
> +#include "libcamera/internal/vector.h"
> +
>  namespace libcamera {
>  
>  class Dw100VertexMap
> @@ -57,11 +60,20 @@ public:
>         void setMode(const ScaleMode mode) { mode_ = mode; }
>         ScaleMode mode() const { return mode_; }
>  
> +       int loadDewarpParams(const YamlObject &dict);
> +       int setDewarpParams(const Matrix<double, 3, 3> &cm, const Span<const double> &coeffs);
> +       bool dewarpParamsValid() { return dewarpParamsValid_; }
> +
> +       void setLensDewarpEnable(bool enable) { lensDewarpEnable_ = enable; }
> +       bool lensDewarpEnable() { return lensDewarpEnable_; }
> +
>         std::vector<uint32_t> getVertexMap();
>  
>  private:
>         int getVerticesForLength(const int length);
>  
> +       Vector<double, 2> dewarpPoint(const Vector<double, 2> &p);
> +
>         Rectangle scalerCrop_;
>         Rectangle sensorCrop_;
>         Transform transform_ = Transform::Identity;
> @@ -75,6 +87,11 @@ private:
>         double effectiveScaleY_;
>         Point effectiveOffset_;
>         Rectangle effectiveScalerCrop_;
> +
> +       Matrix<double, 3, 3> dewarpM_ = Matrix<double, 3, 3>::identity();
> +       std::array<double, 12> dewarpCoeffs_;
> +       bool lensDewarpEnable_ = true;
> +       bool dewarpParamsValid_ = false;
>  };
>  
>  } /* namespace libcamera */
> diff --git a/src/libcamera/converter/converter_dw100_vertexmap.cpp b/src/libcamera/converter/converter_dw100_vertexmap.cpp
> index 10d9e34d98c5..5581d53e57c7 100644
> --- a/src/libcamera/converter/converter_dw100_vertexmap.cpp
> +++ b/src/libcamera/converter/converter_dw100_vertexmap.cpp
> @@ -66,6 +66,14 @@ Vector2d point2Vec2d(const Point &p)
>         return { { static_cast<double>(p.x), static_cast<double>(p.y) } };
>  }
>  
> +void transformPoint(const Rectangle &from, const Rectangle &to, Vector2d &p)

I wonder if this ends up going to geometry in the future. No need now
without another user.

> +{
> +       double sx = to.width / static_cast<double>(from.width);
> +       double sy = to.height / static_cast<double>(from.height);
> +       p.x() = (p.x() - from.x) * sx + to.x;
> +       p.y() = (p.y() - from.y) * sy + to.y;
> +}
> +
>  } /* namespace */
>  
>  /**
> @@ -344,6 +352,17 @@ std::vector<uint32_t> Dw100VertexMap::getVertexMap()
>                         p.x() += localScalerCrop.x;
>                         p.y() += localScalerCrop.y;
>  
> +                       if (dewarpParamsValid_ && lensDewarpEnable_) {
> +                               /*
> +                                * We are in localScalerCrop coordinates.
> +                                * Tranform to sensor coordinates.

Transform

> +                                */
> +                               transformPoint(localScalerCrop, effectiveScalerCrop_, p);
> +                               p = dewarpPoint(p);
> +                               /* tarnsform back to localScalerCrop */

Transform

> +                               transformPoint(effectiveScalerCrop_, localScalerCrop, p);
> +                       }
> +
>                         /* Convert to fixed point */
>                         uint32_t v = static_cast<uint32_t>(p.y() * 16) << 16 |
>                                      (static_cast<uint32_t>(p.x() * 16) & 0xffff);
> @@ -354,6 +373,74 @@ std::vector<uint32_t> Dw100VertexMap::getVertexMap()
>         return res;
>  }
>  
> +int Dw100VertexMap::loadDewarpParams(const YamlObject &dict)
> +{
> +       Matrix<double, 3, 3> m;
> +       const auto &cm = dict["cm"].get<Matrix<double, 3, 3>>();
> +       if (!cm) {
> +               LOG(Converter, Error) << "Dewarp parameters are missing 'cm' value";
> +               return -EINVAL;
> +       }
> +
> +       m = *cm;
> +
> +       const auto &coeffs = dict["coefficients"].getList<double>();
> +       if (!coeffs) {
> +               LOG(Converter, Error) << "Dewarp parameters 'coefficients' value is not a list";
> +               return -EINVAL;
> +       }
> +
> +       const Span<const double> span{ *coeffs };
> +       return setDewarpParams(m, span);
> +}
> +
> +int Dw100VertexMap::setDewarpParams(const Matrix<double, 3, 3> &cm, const Span<const double> &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;
> +}
> +
> +Vector2d Dw100VertexMap::dewarpPoint(const Vector2d &p)
> +{
> +       double x, y;
> +       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];

I'm scared to ask - but how could we document the coefficients. Perhaps
we need a top level dewarp doxygen page sometime.

> +
> +       y = (p.y() - dewarpM_[1][2]) / dewarpM_[1][1];
> +       x = (p.x() - dewarpM_[0][2] - y * dewarpM_[0][1]) / dewarpM_[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);
> +       x = x * d + 2 * p1 * x * y + p2 * (r2 + 2 * x * x) + s1 * r2 + s2 * r2 * r2;
> +       y = y * d + 2 * p2 * x * y + p1 * (r2 + 2 * y * y) + s3 * r2 + s4 * r2 * r2;
> +
> +       Vector2d ret{ { x * dewarpM_[0][0] + y * dewarpM_[0][1] + dewarpM_[0][2],
> +                       y * dewarpM_[1][1] + dewarpM_[1][2] } };
> +       return ret;

Can this be directly returned?

	return { { x * dewarpM_[0][0] + y * dewarpM_[0][1] + dewarpM_[0][2],
		   y * dewarpM_[1][1] + dewarpM_[1][2] } };

Or does the construction get more difficult ?


But actually for this patch -  I think I'll already add with the typos
fixed:

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

> +}
> +
>  int Dw100VertexMap::getVerticesForLength(const int length)
>  {
>         return (length + kDw100BlockSize - 1) / kDw100BlockSize + 1;
> -- 
> 2.48.1
>
Barnabás Pőcze Oct. 3, 2025, 8:09 a.m. UTC | #2
Hi


2025. 10. 02. 23:57 keltezéssel, Kieran Bingham írta:
> Quoting Stefan Klug (2025-09-30 13:26:49)
>> Implement functions to allow lens dewarping based on the common lens
>> dewarp model used e.g. by OpenCV
>>
>> See https://docs.opencv.org/3.4/da/d54/group__imgproc__transform.html#ga7dfb72c9cf9780a347fbe3d1c47e5d5a
>>
>> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
>> ---
>>   .../converter/converter_dw100_vertexmap.h     | 17 ++++
>>   .../converter/converter_dw100_vertexmap.cpp   | 87 +++++++++++++++++++
>>   2 files changed, 104 insertions(+)
>>
>> diff --git a/include/libcamera/internal/converter/converter_dw100_vertexmap.h b/include/libcamera/internal/converter/converter_dw100_vertexmap.h
>> index 9c6e0ffa4513..7a12c4cb7a50 100644
>> --- a/include/libcamera/internal/converter/converter_dw100_vertexmap.h
>> +++ b/include/libcamera/internal/converter/converter_dw100_vertexmap.h
>> @@ -12,6 +12,9 @@
>>   #include <libcamera/geometry.h>
>>   #include <libcamera/transform.h>
>>
>> +#include "libcamera/internal/matrix.h"
>> +#include "libcamera/internal/vector.h"
>> +
>>   namespace libcamera {
>>
>>   class Dw100VertexMap
>> @@ -57,11 +60,20 @@ public:
>>          void setMode(const ScaleMode mode) { mode_ = mode; }
>>          ScaleMode mode() const { return mode_; }
>>
>> +       int loadDewarpParams(const YamlObject &dict);
>> +       int setDewarpParams(const Matrix<double, 3, 3> &cm, const Span<const double> &coeffs);
>> +       bool dewarpParamsValid() { return dewarpParamsValid_; }
>> +
>> +       void setLensDewarpEnable(bool enable) { lensDewarpEnable_ = enable; }
>> +       bool lensDewarpEnable() { return lensDewarpEnable_; }
>> +
>>          std::vector<uint32_t> getVertexMap();
>>
>>   private:
>>          int getVerticesForLength(const int length);
>>
>> +       Vector<double, 2> dewarpPoint(const Vector<double, 2> &p);
>> +
>>          Rectangle scalerCrop_;
>>          Rectangle sensorCrop_;
>>          Transform transform_ = Transform::Identity;
>> @@ -75,6 +87,11 @@ private:
>>          double effectiveScaleY_;
>>          Point effectiveOffset_;
>>          Rectangle effectiveScalerCrop_;
>> +
>> +       Matrix<double, 3, 3> dewarpM_ = Matrix<double, 3, 3>::identity();
>> +       std::array<double, 12> dewarpCoeffs_;
>> +       bool lensDewarpEnable_ = true;
>> +       bool dewarpParamsValid_ = false;
>>   };
>>
>>   } /* namespace libcamera */
>> diff --git a/src/libcamera/converter/converter_dw100_vertexmap.cpp b/src/libcamera/converter/converter_dw100_vertexmap.cpp
>> index 10d9e34d98c5..5581d53e57c7 100644
>> --- a/src/libcamera/converter/converter_dw100_vertexmap.cpp
>> +++ b/src/libcamera/converter/converter_dw100_vertexmap.cpp
>> @@ -66,6 +66,14 @@ Vector2d point2Vec2d(const Point &p)
>>          return { { static_cast<double>(p.x), static_cast<double>(p.y) } };
>>   }
>>
>> +void transformPoint(const Rectangle &from, const Rectangle &to, Vector2d &p)
> 
> I wonder if this ends up going to geometry in the future. No need now
> without another user.

This is already implemented in `Rectangle::transformedBetween()`.
That works on rectangles, but to transform a point, one can use
the top left corner and ignore the sizes.


Regards,
Barnabás Pőcze


> 
>> +{
>> +       double sx = to.width / static_cast<double>(from.width);
>> +       double sy = to.height / static_cast<double>(from.height);
>> +       p.x() = (p.x() - from.x) * sx + to.x;
>> +       p.y() = (p.y() - from.y) * sy + to.y;
>> +}
>> +
>>   } /* namespace */
>>
>>   /**
>> @@ -344,6 +352,17 @@ std::vector<uint32_t> Dw100VertexMap::getVertexMap()
>>                          p.x() += localScalerCrop.x;
>>                          p.y() += localScalerCrop.y;
>>
>> +                       if (dewarpParamsValid_ && lensDewarpEnable_) {
>> +                               /*
>> +                                * We are in localScalerCrop coordinates.
>> +                                * Tranform to sensor coordinates.
> 
> Transform
> 
>> +                                */
>> +                               transformPoint(localScalerCrop, effectiveScalerCrop_, p);
>> +                               p = dewarpPoint(p);
>> +                               /* tarnsform back to localScalerCrop */
> 
> Transform
> 
>> +                               transformPoint(effectiveScalerCrop_, localScalerCrop, p);
>> +                       }
>> +
>>                          /* Convert to fixed point */
>>                          uint32_t v = static_cast<uint32_t>(p.y() * 16) << 16 |
>>                                       (static_cast<uint32_t>(p.x() * 16) & 0xffff);
>> @@ -354,6 +373,74 @@ std::vector<uint32_t> Dw100VertexMap::getVertexMap()
>>          return res;
>>   }
>>
>> +int Dw100VertexMap::loadDewarpParams(const YamlObject &dict)
>> +{
>> +       Matrix<double, 3, 3> m;
>> +       const auto &cm = dict["cm"].get<Matrix<double, 3, 3>>();
>> +       if (!cm) {
>> +               LOG(Converter, Error) << "Dewarp parameters are missing 'cm' value";
>> +               return -EINVAL;
>> +       }
>> +
>> +       m = *cm;
>> +
>> +       const auto &coeffs = dict["coefficients"].getList<double>();
>> +       if (!coeffs) {
>> +               LOG(Converter, Error) << "Dewarp parameters 'coefficients' value is not a list";
>> +               return -EINVAL;
>> +       }
>> +
>> +       const Span<const double> span{ *coeffs };
>> +       return setDewarpParams(m, span);
>> +}
>> +
>> +int Dw100VertexMap::setDewarpParams(const Matrix<double, 3, 3> &cm, const Span<const double> &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;
>> +}
>> +
>> +Vector2d Dw100VertexMap::dewarpPoint(const Vector2d &p)
>> +{
>> +       double x, y;
>> +       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];
> 
> I'm scared to ask - but how could we document the coefficients. Perhaps
> we need a top level dewarp doxygen page sometime.
> 
>> +
>> +       y = (p.y() - dewarpM_[1][2]) / dewarpM_[1][1];
>> +       x = (p.x() - dewarpM_[0][2] - y * dewarpM_[0][1]) / dewarpM_[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);
>> +       x = x * d + 2 * p1 * x * y + p2 * (r2 + 2 * x * x) + s1 * r2 + s2 * r2 * r2;
>> +       y = y * d + 2 * p2 * x * y + p1 * (r2 + 2 * y * y) + s3 * r2 + s4 * r2 * r2;
>> +
>> +       Vector2d ret{ { x * dewarpM_[0][0] + y * dewarpM_[0][1] + dewarpM_[0][2],
>> +                       y * dewarpM_[1][1] + dewarpM_[1][2] } };
>> +       return ret;
> 
> Can this be directly returned?
> 
> 	return { { x * dewarpM_[0][0] + y * dewarpM_[0][1] + dewarpM_[0][2],
> 		   y * dewarpM_[1][1] + dewarpM_[1][2] } };
> 
> Or does the construction get more difficult ?
> 
> 
> But actually for this patch -  I think I'll already add with the typos
> fixed:
> 
> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 
>> +}
>> +
>>   int Dw100VertexMap::getVerticesForLength(const int length)
>>   {
>>          return (length + kDw100BlockSize - 1) / kDw100BlockSize + 1;
>> --
>> 2.48.1
>>
Stefan Klug Oct. 6, 2025, 9:12 a.m. UTC | #3
Quoting Barnabás Pőcze (2025-10-03 10:09:32)
> Hi
> 
> 
> 2025. 10. 02. 23:57 keltezéssel, Kieran Bingham írta:
> > Quoting Stefan Klug (2025-09-30 13:26:49)
> >> Implement functions to allow lens dewarping based on the common lens
> >> dewarp model used e.g. by OpenCV
> >>
> >> See https://docs.opencv.org/3.4/da/d54/group__imgproc__transform.html#ga7dfb72c9cf9780a347fbe3d1c47e5d5a
> >>
> >> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
> >> ---
> >>   .../converter/converter_dw100_vertexmap.h     | 17 ++++
> >>   .../converter/converter_dw100_vertexmap.cpp   | 87 +++++++++++++++++++
> >>   2 files changed, 104 insertions(+)
> >>
> >> diff --git a/include/libcamera/internal/converter/converter_dw100_vertexmap.h b/include/libcamera/internal/converter/converter_dw100_vertexmap.h
> >> index 9c6e0ffa4513..7a12c4cb7a50 100644
> >> --- a/include/libcamera/internal/converter/converter_dw100_vertexmap.h
> >> +++ b/include/libcamera/internal/converter/converter_dw100_vertexmap.h
> >> @@ -12,6 +12,9 @@
> >>   #include <libcamera/geometry.h>
> >>   #include <libcamera/transform.h>
> >>
> >> +#include "libcamera/internal/matrix.h"
> >> +#include "libcamera/internal/vector.h"
> >> +
> >>   namespace libcamera {
> >>
> >>   class Dw100VertexMap
> >> @@ -57,11 +60,20 @@ public:
> >>          void setMode(const ScaleMode mode) { mode_ = mode; }
> >>          ScaleMode mode() const { return mode_; }
> >>
> >> +       int loadDewarpParams(const YamlObject &dict);
> >> +       int setDewarpParams(const Matrix<double, 3, 3> &cm, const Span<const double> &coeffs);
> >> +       bool dewarpParamsValid() { return dewarpParamsValid_; }
> >> +
> >> +       void setLensDewarpEnable(bool enable) { lensDewarpEnable_ = enable; }
> >> +       bool lensDewarpEnable() { return lensDewarpEnable_; }
> >> +
> >>          std::vector<uint32_t> getVertexMap();
> >>
> >>   private:
> >>          int getVerticesForLength(const int length);
> >>
> >> +       Vector<double, 2> dewarpPoint(const Vector<double, 2> &p);
> >> +
> >>          Rectangle scalerCrop_;
> >>          Rectangle sensorCrop_;
> >>          Transform transform_ = Transform::Identity;
> >> @@ -75,6 +87,11 @@ private:
> >>          double effectiveScaleY_;
> >>          Point effectiveOffset_;
> >>          Rectangle effectiveScalerCrop_;
> >> +
> >> +       Matrix<double, 3, 3> dewarpM_ = Matrix<double, 3, 3>::identity();
> >> +       std::array<double, 12> dewarpCoeffs_;
> >> +       bool lensDewarpEnable_ = true;
> >> +       bool dewarpParamsValid_ = false;
> >>   };
> >>
> >>   } /* namespace libcamera */
> >> diff --git a/src/libcamera/converter/converter_dw100_vertexmap.cpp b/src/libcamera/converter/converter_dw100_vertexmap.cpp
> >> index 10d9e34d98c5..5581d53e57c7 100644
> >> --- a/src/libcamera/converter/converter_dw100_vertexmap.cpp
> >> +++ b/src/libcamera/converter/converter_dw100_vertexmap.cpp
> >> @@ -66,6 +66,14 @@ Vector2d point2Vec2d(const Point &p)
> >>          return { { static_cast<double>(p.x), static_cast<double>(p.y) } };
> >>   }
> >>
> >> +void transformPoint(const Rectangle &from, const Rectangle &to, Vector2d &p)
> > 
> > I wonder if this ends up going to geometry in the future. No need now
> > without another user.
> 
> This is already implemented in `Rectangle::transformedBetween()`.
> That works on rectangles, but to transform a point, one can use
> the top left corner and ignore the sizes.

Yes, but then you'd loose the subpixel accuracy because Rectangle is int
only. That would lead to visible artifacts.

Best regards,
Stefan

> 
> 
> Regards,
> Barnabás Pőcze
> 
> 
> > 
> >> +{
> >> +       double sx = to.width / static_cast<double>(from.width);
> >> +       double sy = to.height / static_cast<double>(from.height);
> >> +       p.x() = (p.x() - from.x) * sx + to.x;
> >> +       p.y() = (p.y() - from.y) * sy + to.y;
> >> +}
> >> +
> >>   } /* namespace */
> >>
> >>   /**
> >> @@ -344,6 +352,17 @@ std::vector<uint32_t> Dw100VertexMap::getVertexMap()
> >>                          p.x() += localScalerCrop.x;
> >>                          p.y() += localScalerCrop.y;
> >>
> >> +                       if (dewarpParamsValid_ && lensDewarpEnable_) {
> >> +                               /*
> >> +                                * We are in localScalerCrop coordinates.
> >> +                                * Tranform to sensor coordinates.
> > 
> > Transform
> > 
> >> +                                */
> >> +                               transformPoint(localScalerCrop, effectiveScalerCrop_, p);
> >> +                               p = dewarpPoint(p);
> >> +                               /* tarnsform back to localScalerCrop */
> > 
> > Transform
> > 
> >> +                               transformPoint(effectiveScalerCrop_, localScalerCrop, p);
> >> +                       }
> >> +
> >>                          /* Convert to fixed point */
> >>                          uint32_t v = static_cast<uint32_t>(p.y() * 16) << 16 |
> >>                                       (static_cast<uint32_t>(p.x() * 16) & 0xffff);
> >> @@ -354,6 +373,74 @@ std::vector<uint32_t> Dw100VertexMap::getVertexMap()
> >>          return res;
> >>   }
> >>
> >> +int Dw100VertexMap::loadDewarpParams(const YamlObject &dict)
> >> +{
> >> +       Matrix<double, 3, 3> m;
> >> +       const auto &cm = dict["cm"].get<Matrix<double, 3, 3>>();
> >> +       if (!cm) {
> >> +               LOG(Converter, Error) << "Dewarp parameters are missing 'cm' value";
> >> +               return -EINVAL;
> >> +       }
> >> +
> >> +       m = *cm;
> >> +
> >> +       const auto &coeffs = dict["coefficients"].getList<double>();
> >> +       if (!coeffs) {
> >> +               LOG(Converter, Error) << "Dewarp parameters 'coefficients' value is not a list";
> >> +               return -EINVAL;
> >> +       }
> >> +
> >> +       const Span<const double> span{ *coeffs };
> >> +       return setDewarpParams(m, span);
> >> +}
> >> +
> >> +int Dw100VertexMap::setDewarpParams(const Matrix<double, 3, 3> &cm, const Span<const double> &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;
> >> +}
> >> +
> >> +Vector2d Dw100VertexMap::dewarpPoint(const Vector2d &p)
> >> +{
> >> +       double x, y;
> >> +       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];
> > 
> > I'm scared to ask - but how could we document the coefficients. Perhaps
> > we need a top level dewarp doxygen page sometime.
> > 
> >> +
> >> +       y = (p.y() - dewarpM_[1][2]) / dewarpM_[1][1];
> >> +       x = (p.x() - dewarpM_[0][2] - y * dewarpM_[0][1]) / dewarpM_[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);
> >> +       x = x * d + 2 * p1 * x * y + p2 * (r2 + 2 * x * x) + s1 * r2 + s2 * r2 * r2;
> >> +       y = y * d + 2 * p2 * x * y + p1 * (r2 + 2 * y * y) + s3 * r2 + s4 * r2 * r2;
> >> +
> >> +       Vector2d ret{ { x * dewarpM_[0][0] + y * dewarpM_[0][1] + dewarpM_[0][2],
> >> +                       y * dewarpM_[1][1] + dewarpM_[1][2] } };
> >> +       return ret;
> > 
> > Can this be directly returned?
> > 
> >       return { { x * dewarpM_[0][0] + y * dewarpM_[0][1] + dewarpM_[0][2],
> >                  y * dewarpM_[1][1] + dewarpM_[1][2] } };
> > 
> > Or does the construction get more difficult ?
> > 
> > 
> > But actually for this patch -  I think I'll already add with the typos
> > fixed:
> > 
> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > 
> >> +}
> >> +
> >>   int Dw100VertexMap::getVerticesForLength(const int length)
> >>   {
> >>          return (length + kDw100BlockSize - 1) / kDw100BlockSize + 1;
> >> --
> >> 2.48.1
> >>
>

Patch
diff mbox series

diff --git a/include/libcamera/internal/converter/converter_dw100_vertexmap.h b/include/libcamera/internal/converter/converter_dw100_vertexmap.h
index 9c6e0ffa4513..7a12c4cb7a50 100644
--- a/include/libcamera/internal/converter/converter_dw100_vertexmap.h
+++ b/include/libcamera/internal/converter/converter_dw100_vertexmap.h
@@ -12,6 +12,9 @@ 
 #include <libcamera/geometry.h>
 #include <libcamera/transform.h>
 
+#include "libcamera/internal/matrix.h"
+#include "libcamera/internal/vector.h"
+
 namespace libcamera {
 
 class Dw100VertexMap
@@ -57,11 +60,20 @@  public:
 	void setMode(const ScaleMode mode) { mode_ = mode; }
 	ScaleMode mode() const { return mode_; }
 
+	int loadDewarpParams(const YamlObject &dict);
+	int setDewarpParams(const Matrix<double, 3, 3> &cm, const Span<const double> &coeffs);
+	bool dewarpParamsValid() { return dewarpParamsValid_; }
+
+	void setLensDewarpEnable(bool enable) { lensDewarpEnable_ = enable; }
+	bool lensDewarpEnable() { return lensDewarpEnable_; }
+
 	std::vector<uint32_t> getVertexMap();
 
 private:
 	int getVerticesForLength(const int length);
 
+	Vector<double, 2> dewarpPoint(const Vector<double, 2> &p);
+
 	Rectangle scalerCrop_;
 	Rectangle sensorCrop_;
 	Transform transform_ = Transform::Identity;
@@ -75,6 +87,11 @@  private:
 	double effectiveScaleY_;
 	Point effectiveOffset_;
 	Rectangle effectiveScalerCrop_;
+
+	Matrix<double, 3, 3> dewarpM_ = Matrix<double, 3, 3>::identity();
+	std::array<double, 12> dewarpCoeffs_;
+	bool lensDewarpEnable_ = true;
+	bool dewarpParamsValid_ = false;
 };
 
 } /* namespace libcamera */
diff --git a/src/libcamera/converter/converter_dw100_vertexmap.cpp b/src/libcamera/converter/converter_dw100_vertexmap.cpp
index 10d9e34d98c5..5581d53e57c7 100644
--- a/src/libcamera/converter/converter_dw100_vertexmap.cpp
+++ b/src/libcamera/converter/converter_dw100_vertexmap.cpp
@@ -66,6 +66,14 @@  Vector2d point2Vec2d(const Point &p)
 	return { { static_cast<double>(p.x), static_cast<double>(p.y) } };
 }
 
+void transformPoint(const Rectangle &from, const Rectangle &to, Vector2d &p)
+{
+	double sx = to.width / static_cast<double>(from.width);
+	double sy = to.height / static_cast<double>(from.height);
+	p.x() = (p.x() - from.x) * sx + to.x;
+	p.y() = (p.y() - from.y) * sy + to.y;
+}
+
 } /* namespace */
 
 /**
@@ -344,6 +352,17 @@  std::vector<uint32_t> Dw100VertexMap::getVertexMap()
 			p.x() += localScalerCrop.x;
 			p.y() += localScalerCrop.y;
 
+			if (dewarpParamsValid_ && lensDewarpEnable_) {
+				/*
+				 * We are in localScalerCrop coordinates.
+				 * Tranform to sensor coordinates.
+				 */
+				transformPoint(localScalerCrop, effectiveScalerCrop_, p);
+				p = dewarpPoint(p);
+				/* tarnsform back to localScalerCrop */
+				transformPoint(effectiveScalerCrop_, localScalerCrop, p);
+			}
+
 			/* Convert to fixed point */
 			uint32_t v = static_cast<uint32_t>(p.y() * 16) << 16 |
 				     (static_cast<uint32_t>(p.x() * 16) & 0xffff);
@@ -354,6 +373,74 @@  std::vector<uint32_t> Dw100VertexMap::getVertexMap()
 	return res;
 }
 
+int Dw100VertexMap::loadDewarpParams(const YamlObject &dict)
+{
+	Matrix<double, 3, 3> m;
+	const auto &cm = dict["cm"].get<Matrix<double, 3, 3>>();
+	if (!cm) {
+		LOG(Converter, Error) << "Dewarp parameters are missing 'cm' value";
+		return -EINVAL;
+	}
+
+	m = *cm;
+
+	const auto &coeffs = dict["coefficients"].getList<double>();
+	if (!coeffs) {
+		LOG(Converter, Error) << "Dewarp parameters 'coefficients' value is not a list";
+		return -EINVAL;
+	}
+
+	const Span<const double> span{ *coeffs };
+	return setDewarpParams(m, span);
+}
+
+int Dw100VertexMap::setDewarpParams(const Matrix<double, 3, 3> &cm, const Span<const double> &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;
+}
+
+Vector2d Dw100VertexMap::dewarpPoint(const Vector2d &p)
+{
+	double x, y;
+	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];
+
+	y = (p.y() - dewarpM_[1][2]) / dewarpM_[1][1];
+	x = (p.x() - dewarpM_[0][2] - y * dewarpM_[0][1]) / dewarpM_[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);
+	x = x * d + 2 * p1 * x * y + p2 * (r2 + 2 * x * x) + s1 * r2 + s2 * r2 * r2;
+	y = y * d + 2 * p2 * x * y + p1 * (r2 + 2 * y * y) + s3 * r2 + s4 * r2 * r2;
+
+	Vector2d ret{ { x * dewarpM_[0][0] + y * dewarpM_[0][1] + dewarpM_[0][2],
+			y * dewarpM_[1][1] + dewarpM_[1][2] } };
+	return ret;
+}
+
 int Dw100VertexMap::getVerticesForLength(const int length)
 {
 	return (length + kDw100BlockSize - 1) / kDw100BlockSize + 1;