[v2,3/8] libcamera: geometry: Add Rectangle::transformedBetween()
diff mbox series

Message ID 20241125151430.2437285-4-stefan.klug@ideasonboard.com
State New
Headers show
Series
  • rkisp1: Fix aspect ratio and ScalerCrop
Related show

Commit Message

Stefan Klug Nov. 25, 2024, 3:14 p.m. UTC
Handling cropping and scaling within a complicated pipeline involves
transformations of rectangles between different coordinate systems. For
example the full input of the dewarper (0,0)/1920x1080 might correspond
to the rectangle (0, 243)/2592x1458 in sensor coordinates (of a
2592x1944 sensor). Add a function that allows the transformation of a
rectangle defined in one reference frame (dewarper) into the coordinates
of a second reference frame (sensor).

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>

---
Changes in v2:
- Renamed mappedBetween() to transformedBetween()
- Improved documentation
- Collected tags
---
 include/libcamera/geometry.h |  3 +++
 src/libcamera/geometry.cpp   | 37 ++++++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+)

Comments

Jacopo Mondi Nov. 25, 2024, 6:20 p.m. UTC | #1
Hi Stefan

On Mon, Nov 25, 2024 at 04:14:12PM +0100, Stefan Klug wrote:
> Handling cropping and scaling within a complicated pipeline involves
> transformations of rectangles between different coordinate systems. For
> example the full input of the dewarper (0,0)/1920x1080 might correspond
> to the rectangle (0, 243)/2592x1458 in sensor coordinates (of a
> 2592x1944 sensor). Add a function that allows the transformation of a
> rectangle defined in one reference frame (dewarper) into the coordinates
> of a second reference frame (sensor).
>
> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
>
> ---
> Changes in v2:
> - Renamed mappedBetween() to transformedBetween()
> - Improved documentation
> - Collected tags
> ---
>  include/libcamera/geometry.h |  3 +++
>  src/libcamera/geometry.cpp   | 37 ++++++++++++++++++++++++++++++++++++
>  2 files changed, 40 insertions(+)
>
> diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
> index 9ca5865a3d0d..e5f0a843d314 100644
> --- a/include/libcamera/geometry.h
> +++ b/include/libcamera/geometry.h
> @@ -299,6 +299,9 @@ public:
>  	__nodiscard Rectangle scaledBy(const Size &numerator,
>  				       const Size &denominator) const;
>  	__nodiscard Rectangle translatedBy(const Point &point) const;
> +
> +	Rectangle transformedBetween(const Rectangle &source,
> +				     const Rectangle &target) const;
>  };
>
>  bool operator==(const Rectangle &lhs, const Rectangle &rhs);
> diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
> index 90ccf8c19f97..2c9308cb25ee 100644
> --- a/src/libcamera/geometry.cpp
> +++ b/src/libcamera/geometry.cpp
> @@ -837,6 +837,43 @@ Rectangle Rectangle::translatedBy(const Point &point) const
>  	return { x + point.x, y + point.y, width, height };
>  }
>
> +/**
> + * \brief Transforms a Rectangle from one reference frame to another

nit: documentation is usually in imperative form:
Transforms -> Transform

> + * \param[in] source The \a source reference frame
> + * \param[in] destination The \a destination reference frame

I'm not sure I 100% like 'frame' here. The geometry library is not
only about frames or images...


> + *
> + * The params source and destinations specify two reference frames for the same

Let's spell out terms in full in documentation.

"The \a source and \a destination parameters "

Also, I would find it cleaner if we talk about Rectangles here.

"The \a source and \a destination parameters describe two rectangles
defined in different reference systems. The Rectangle is translated
from the source reference system into the destination reference
system."


> + * data. The rectangle is translated from the source reference frame into the
> + * destination reference frame.
> + *

We can say

"The typical use case for this function is to translate a selection
rectangle specified in a reference system, in example the sensor's
pixel array, into the same rectangle re-scaled and translated into
a different reference system, in example the output frame on
which the selection rectangle is applied to."

> + * For example, consider a sensor with a resolution of 4040x2360 pixels and a
> + * assume a rectangle of (100, 100)/3840x2160 (sensorFrame) in sensor
> + * coordinates is mapped to a rectangle (0,0)/(1920,1080) (displayFrame) in
> + * display coordinates. This function can be used to transform an arbitrary
> + * rectangle from display coordinates to sensor coordinates or vice versa:

And then provide a concrete example ? What do you think ? is is too
much details ?

> + *
> + * \code{.cpp}
> + * // Bottom right quarter in sensor coordinates
> + * Rectangle sensorRect(2020, 100, 1920, 1080);
> + * displayRect = sensorRect.transformedBetween(sensorFrame, displayFrame);
> + * // displayRect is now (960, 540)/960x540
> + * sensorRect = displayRect.transformedBetween(displayFrame, sensorFrame);
> + * \endcode
> + */
> +Rectangle Rectangle::transformedBetween(const Rectangle &source,
> +					const Rectangle &destination) const
> +{
> +	Rectangle r;
> +	double sx = static_cast<double>(destination.width) / source.width;
> +	double sy = static_cast<double>(destination.height) / source.height;
> +
> +	r.x = static_cast<int>((x - source.x) * sx) + destination.x;
> +	r.y = static_cast<int>((y - source.y) * sy) + destination.y;
> +	r.width = static_cast<int>(width * sx);
> +	r.height = static_cast<int>(height * sy);

nit: I would add a blank line here

> +	return r;
> +}
> +

nits and nitpicking apart
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>

Thanks
  j

>  /**
>   * \brief Compare rectangles for equality
>   * \return True if the two rectangles are equal, false otherwise
> --
> 2.43.0
>
Jacopo Mondi Nov. 25, 2024, 7:28 p.m. UTC | #2
Ups, I think you'll need a test for this though, even a simple one
that tests the example you have made in the documentation..

On Mon, Nov 25, 2024 at 07:20:46PM +0100, Jacopo Mondi wrote:
> Hi Stefan
>
> On Mon, Nov 25, 2024 at 04:14:12PM +0100, Stefan Klug wrote:
> > Handling cropping and scaling within a complicated pipeline involves
> > transformations of rectangles between different coordinate systems. For
> > example the full input of the dewarper (0,0)/1920x1080 might correspond
> > to the rectangle (0, 243)/2592x1458 in sensor coordinates (of a
> > 2592x1944 sensor). Add a function that allows the transformation of a
> > rectangle defined in one reference frame (dewarper) into the coordinates
> > of a second reference frame (sensor).
> >
> > Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
> > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
> >
> > ---
> > Changes in v2:
> > - Renamed mappedBetween() to transformedBetween()
> > - Improved documentation
> > - Collected tags
> > ---
> >  include/libcamera/geometry.h |  3 +++
> >  src/libcamera/geometry.cpp   | 37 ++++++++++++++++++++++++++++++++++++
> >  2 files changed, 40 insertions(+)
> >
> > diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
> > index 9ca5865a3d0d..e5f0a843d314 100644
> > --- a/include/libcamera/geometry.h
> > +++ b/include/libcamera/geometry.h
> > @@ -299,6 +299,9 @@ public:
> >  	__nodiscard Rectangle scaledBy(const Size &numerator,
> >  				       const Size &denominator) const;
> >  	__nodiscard Rectangle translatedBy(const Point &point) const;
> > +
> > +	Rectangle transformedBetween(const Rectangle &source,
> > +				     const Rectangle &target) const;
> >  };
> >
> >  bool operator==(const Rectangle &lhs, const Rectangle &rhs);
> > diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
> > index 90ccf8c19f97..2c9308cb25ee 100644
> > --- a/src/libcamera/geometry.cpp
> > +++ b/src/libcamera/geometry.cpp
> > @@ -837,6 +837,43 @@ Rectangle Rectangle::translatedBy(const Point &point) const
> >  	return { x + point.x, y + point.y, width, height };
> >  }
> >
> > +/**
> > + * \brief Transforms a Rectangle from one reference frame to another
>
> nit: documentation is usually in imperative form:
> Transforms -> Transform
>
> > + * \param[in] source The \a source reference frame
> > + * \param[in] destination The \a destination reference frame
>
> I'm not sure I 100% like 'frame' here. The geometry library is not
> only about frames or images...
>
>
> > + *
> > + * The params source and destinations specify two reference frames for the same
>
> Let's spell out terms in full in documentation.
>
> "The \a source and \a destination parameters "
>
> Also, I would find it cleaner if we talk about Rectangles here.
>
> "The \a source and \a destination parameters describe two rectangles
> defined in different reference systems. The Rectangle is translated
> from the source reference system into the destination reference
> system."
>
>
> > + * data. The rectangle is translated from the source reference frame into the
> > + * destination reference frame.
> > + *
>
> We can say
>
> "The typical use case for this function is to translate a selection
> rectangle specified in a reference system, in example the sensor's
> pixel array, into the same rectangle re-scaled and translated into
> a different reference system, in example the output frame on
> which the selection rectangle is applied to."
>
> > + * For example, consider a sensor with a resolution of 4040x2360 pixels and a
> > + * assume a rectangle of (100, 100)/3840x2160 (sensorFrame) in sensor
> > + * coordinates is mapped to a rectangle (0,0)/(1920,1080) (displayFrame) in
> > + * display coordinates. This function can be used to transform an arbitrary
> > + * rectangle from display coordinates to sensor coordinates or vice versa:
>
> And then provide a concrete example ? What do you think ? is is too
> much details ?
>
> > + *
> > + * \code{.cpp}
> > + * // Bottom right quarter in sensor coordinates
> > + * Rectangle sensorRect(2020, 100, 1920, 1080);
> > + * displayRect = sensorRect.transformedBetween(sensorFrame, displayFrame);
> > + * // displayRect is now (960, 540)/960x540
> > + * sensorRect = displayRect.transformedBetween(displayFrame, sensorFrame);
> > + * \endcode
> > + */
> > +Rectangle Rectangle::transformedBetween(const Rectangle &source,
> > +					const Rectangle &destination) const
> > +{
> > +	Rectangle r;
> > +	double sx = static_cast<double>(destination.width) / source.width;
> > +	double sy = static_cast<double>(destination.height) / source.height;
> > +
> > +	r.x = static_cast<int>((x - source.x) * sx) + destination.x;
> > +	r.y = static_cast<int>((y - source.y) * sy) + destination.y;
> > +	r.width = static_cast<int>(width * sx);
> > +	r.height = static_cast<int>(height * sy);
>
> nit: I would add a blank line here
>
> > +	return r;
> > +}
> > +
>
> nits and nitpicking apart
> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
>
> Thanks
>   j
>
> >  /**
> >   * \brief Compare rectangles for equality
> >   * \return True if the two rectangles are equal, false otherwise
> > --
> > 2.43.0
> >
Stefan Klug Nov. 28, 2024, 12:58 p.m. UTC | #3
Hi Jacopo,

On Mon, Nov 25, 2024 at 08:28:27PM +0100, Jacopo Mondi wrote:
> Ups, I think you'll need a test for this though, even a simple one
> that tests the example you have made in the documentation..

Right that was forgotten in v2, now added for v3.

> 
> On Mon, Nov 25, 2024 at 07:20:46PM +0100, Jacopo Mondi wrote:
> > Hi Stefan
> >
> > On Mon, Nov 25, 2024 at 04:14:12PM +0100, Stefan Klug wrote:
> > > Handling cropping and scaling within a complicated pipeline involves
> > > transformations of rectangles between different coordinate systems. For
> > > example the full input of the dewarper (0,0)/1920x1080 might correspond
> > > to the rectangle (0, 243)/2592x1458 in sensor coordinates (of a
> > > 2592x1944 sensor). Add a function that allows the transformation of a
> > > rectangle defined in one reference frame (dewarper) into the coordinates
> > > of a second reference frame (sensor).
> > >
> > > Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
> > > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
> > >
> > > ---
> > > Changes in v2:
> > > - Renamed mappedBetween() to transformedBetween()
> > > - Improved documentation
> > > - Collected tags
> > > ---
> > >  include/libcamera/geometry.h |  3 +++
> > >  src/libcamera/geometry.cpp   | 37 ++++++++++++++++++++++++++++++++++++
> > >  2 files changed, 40 insertions(+)
> > >
> > > diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
> > > index 9ca5865a3d0d..e5f0a843d314 100644
> > > --- a/include/libcamera/geometry.h
> > > +++ b/include/libcamera/geometry.h
> > > @@ -299,6 +299,9 @@ public:
> > >  	__nodiscard Rectangle scaledBy(const Size &numerator,
> > >  				       const Size &denominator) const;
> > >  	__nodiscard Rectangle translatedBy(const Point &point) const;
> > > +
> > > +	Rectangle transformedBetween(const Rectangle &source,
> > > +				     const Rectangle &target) const;
> > >  };
> > >
> > >  bool operator==(const Rectangle &lhs, const Rectangle &rhs);
> > > diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
> > > index 90ccf8c19f97..2c9308cb25ee 100644
> > > --- a/src/libcamera/geometry.cpp
> > > +++ b/src/libcamera/geometry.cpp
> > > @@ -837,6 +837,43 @@ Rectangle Rectangle::translatedBy(const Point &point) const
> > >  	return { x + point.x, y + point.y, width, height };
> > >  }
> > >
> > > +/**
> > > + * \brief Transforms a Rectangle from one reference frame to another
> >
> > nit: documentation is usually in imperative form:
> > Transforms -> Transform
> >
> > > + * \param[in] source The \a source reference frame
> > > + * \param[in] destination The \a destination reference frame
> >
> > I'm not sure I 100% like 'frame' here. The geometry library is not
> > only about frames or images...
> >
> >
> > > + *
> > > + * The params source and destinations specify two reference frames for the same
> >
> > Let's spell out terms in full in documentation.
> >
> > "The \a source and \a destination parameters "
> >
> > Also, I would find it cleaner if we talk about Rectangles here.
> >
> > "The \a source and \a destination parameters describe two rectangles
> > defined in different reference systems. The Rectangle is translated
> > from the source reference system into the destination reference
> > system."
> >
> >
> > > + * data. The rectangle is translated from the source reference frame into the
> > > + * destination reference frame.
> > > + *
> >
> > We can say
> >
> > "The typical use case for this function is to translate a selection
> > rectangle specified in a reference system, in example the sensor's
> > pixel array, into the same rectangle re-scaled and translated into
> > a different reference system, in example the output frame on
> > which the selection rectangle is applied to."
> >
> > > + * For example, consider a sensor with a resolution of 4040x2360 pixels and a
> > > + * assume a rectangle of (100, 100)/3840x2160 (sensorFrame) in sensor
> > > + * coordinates is mapped to a rectangle (0,0)/(1920,1080) (displayFrame) in
> > > + * display coordinates. This function can be used to transform an arbitrary
> > > + * rectangle from display coordinates to sensor coordinates or vice versa:
> >
> > And then provide a concrete example ? What do you think ? is is too
> > much details ?

Thanks for the suggestions. I took all of them. Let's see how you like
v3.

> >
> > > + *
> > > + * \code{.cpp}
> > > + * // Bottom right quarter in sensor coordinates
> > > + * Rectangle sensorRect(2020, 100, 1920, 1080);
> > > + * displayRect = sensorRect.transformedBetween(sensorFrame, displayFrame);
> > > + * // displayRect is now (960, 540)/960x540
> > > + * sensorRect = displayRect.transformedBetween(displayFrame, sensorFrame);
> > > + * \endcode
> > > + */
> > > +Rectangle Rectangle::transformedBetween(const Rectangle &source,
> > > +					const Rectangle &destination) const
> > > +{
> > > +	Rectangle r;
> > > +	double sx = static_cast<double>(destination.width) / source.width;
> > > +	double sy = static_cast<double>(destination.height) / source.height;
> > > +
> > > +	r.x = static_cast<int>((x - source.x) * sx) + destination.x;
> > > +	r.y = static_cast<int>((y - source.y) * sy) + destination.y;
> > > +	r.width = static_cast<int>(width * sx);
> > > +	r.height = static_cast<int>(height * sy);
> >
> > nit: I would add a blank line here
> >

Done.

Cheers,
Stefan

> > > +	return r;
> > > +}
> > > +
> >
> > nits and nitpicking apart
> > Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> >
> > Thanks
> >   j
> >
> > >  /**
> > >   * \brief Compare rectangles for equality
> > >   * \return True if the two rectangles are equal, false otherwise
> > > --
> > > 2.43.0
> > >

Patch
diff mbox series

diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
index 9ca5865a3d0d..e5f0a843d314 100644
--- a/include/libcamera/geometry.h
+++ b/include/libcamera/geometry.h
@@ -299,6 +299,9 @@  public:
 	__nodiscard Rectangle scaledBy(const Size &numerator,
 				       const Size &denominator) const;
 	__nodiscard Rectangle translatedBy(const Point &point) const;
+
+	Rectangle transformedBetween(const Rectangle &source,
+				     const Rectangle &target) const;
 };
 
 bool operator==(const Rectangle &lhs, const Rectangle &rhs);
diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
index 90ccf8c19f97..2c9308cb25ee 100644
--- a/src/libcamera/geometry.cpp
+++ b/src/libcamera/geometry.cpp
@@ -837,6 +837,43 @@  Rectangle Rectangle::translatedBy(const Point &point) const
 	return { x + point.x, y + point.y, width, height };
 }
 
+/**
+ * \brief Transforms a Rectangle from one reference frame to another
+ * \param[in] source The \a source reference frame
+ * \param[in] destination The \a destination reference frame
+ *
+ * The params source and destinations specify two reference frames for the same
+ * data. The rectangle is translated from the source reference frame into the
+ * destination reference frame.
+ *
+ * For example, consider a sensor with a resolution of 4040x2360 pixels and a
+ * assume a rectangle of (100, 100)/3840x2160 (sensorFrame) in sensor
+ * coordinates is mapped to a rectangle (0,0)/(1920,1080) (displayFrame) in
+ * display coordinates. This function can be used to transform an arbitrary
+ * rectangle from display coordinates to sensor coordinates or vice versa:
+ *
+ * \code{.cpp}
+ * // Bottom right quarter in sensor coordinates
+ * Rectangle sensorRect(2020, 100, 1920, 1080);
+ * displayRect = sensorRect.transformedBetween(sensorFrame, displayFrame);
+ * // displayRect is now (960, 540)/960x540
+ * sensorRect = displayRect.transformedBetween(displayFrame, sensorFrame);
+ * \endcode
+ */
+Rectangle Rectangle::transformedBetween(const Rectangle &source,
+					const Rectangle &destination) const
+{
+	Rectangle r;
+	double sx = static_cast<double>(destination.width) / source.width;
+	double sy = static_cast<double>(destination.height) / source.height;
+
+	r.x = static_cast<int>((x - source.x) * sx) + destination.x;
+	r.y = static_cast<int>((y - source.y) * sy) + destination.y;
+	r.width = static_cast<int>(width * sx);
+	r.height = static_cast<int>(height * sy);
+	return r;
+}
+
 /**
  * \brief Compare rectangles for equality
  * \return True if the two rectangles are equal, false otherwise