[1/2] libcamera: geometry: Add Rectangle::croppedBy
diff mbox series

Message ID 20250718144456.58625-1-mzamazal@redhat.com
State Superseded
Headers show
Series
  • [1/2] libcamera: geometry: Add Rectangle::croppedBy
Related show

Commit Message

Milan Zamazal July 18, 2025, 2:44 p.m. UTC
Let's add a new Rectangle method that returns the coordinates of the
central area of the original rectangle cropped by given
numerator/denominator fraction.  This is useful to specify a limited
area of the image to operate on, for example computing statistics only
from a reduced area for speedup.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
---
 include/libcamera/geometry.h |  2 ++
 src/libcamera/geometry.cpp   | 18 ++++++++++++++++++
 2 files changed, 20 insertions(+)

Comments

Kieran Bingham July 21, 2025, 5:16 p.m. UTC | #1
Quoting Milan Zamazal (2025-07-18 15:44:55)
> Let's add a new Rectangle method that returns the coordinates of the
> central area of the original rectangle cropped by given
> numerator/denominator fraction.  This is useful to specify a limited
> area of the image to operate on, for example computing statistics only
> from a reduced area for speedup.
> 
> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
> ---
>  include/libcamera/geometry.h |  2 ++
>  src/libcamera/geometry.cpp   | 18 ++++++++++++++++++
>  2 files changed, 20 insertions(+)
> 
> diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
> index f322e3d5b..603c9a117 100644
> --- a/include/libcamera/geometry.h
> +++ b/include/libcamera/geometry.h
> @@ -297,6 +297,8 @@ public:
>         [[nodiscard]] Rectangle scaledBy(const Size &numerator,
>                                          const Size &denominator) const;
>         [[nodiscard]] Rectangle translatedBy(const Point &point) const;
> +       [[nodiscard]] Rectangle croppedBy(const unsigned int numerator,
> +                                         const unsigned int denominator) const;
>  
>         Rectangle transformedBetween(const Rectangle &source,
>                                      const Rectangle &target) const;
> diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
> index 81cc8cd53..8c1b156c5 100644
> --- a/src/libcamera/geometry.cpp
> +++ b/src/libcamera/geometry.cpp
> @@ -837,6 +837,24 @@ Rectangle Rectangle::translatedBy(const Point &point) const
>         return { x + point.x, y + point.y, width, height };
>  }
>  
> +/**
> + * \brief Return a central area crop of the rectangle
> + * \param[in] numerator The numerator of the crop factor
> + * \param[in] denominator The denominator of the crop factor
> + *
> + * The rectangle of the central part of the original rectangle is computed, of
> + * numerator/denominator times the original width and height.
> + *
> + * \return The cropped Rectangle coordinates
> + */
> +Rectangle Rectangle::croppedBy(const unsigned int numerator,
> +                              const unsigned int denominator) const
> +{
> +       const unsigned int w = numerator * width / denominator;
> +       const unsigned int h = numerator * height / denominator;

In Rectangle::scaledBy the numbers are cast to 64 bits to ensure that
overflows don't happen. I suspect we should do the same here...

Or use that call as a helper ?


> +       return Rectangle((width - w) / 2, (height - h) / 2, w, h);

Also scaledBy accounts for the x, y coordinates too. We can't always
assume that a rectangle being cropped starts at 0,0!

For example the incoming Rectangle you're croppping might already have a
crop applied to represent the active pixels of a sensor array...

> +}

I wonder if implementing this as :

Rectangle Rectangle::croppedBy(const unsigned int numerator,
                               const unsigned int denominator) const
{
	Size cropped = this.size() * numerator / denominator);
	return cropped.centeredTo(this.center());
}


would solve the above concerns. Actually I don't think that would
protect against overflow ... but maybe it's not a big deal (yet?) So
maybe inlining the Size cropped still makes sense.

Which makes it more like:

Rectangle Rectangle::croppedBy(const unsigned int numerator,
                               const unsigned int denominator) const
{
	unsigned int w = static_cast<uint64_t>(width) * numerator / denominator;
	unsigned int h = static_cast<uint64_t>(height) * numerator / denominator;

	return Size(w, h).centeredTo(center());

}

?

Perhaps we should also add both:

  croppedBy() and cropBy()

though to have the version that modifies in place? 

Finally, any additions to geometry.cpp likely need associated tests in
test/geometry.cpp

--
Kieran



> +
>  /**
>   * \brief Transform a Rectangle from one reference rectangle to another
>   * \param[in] source The \a source reference rectangle
> -- 
> 2.50.1
>
Milan Zamazal July 21, 2025, 6:58 p.m. UTC | #2
Hi Kieran,

thank you for review.

Kieran Bingham <kieran.bingham@ideasonboard.com> writes:

> Quoting Milan Zamazal (2025-07-18 15:44:55)
>> Let's add a new Rectangle method that returns the coordinates of the
>> central area of the original rectangle cropped by given
>
>> numerator/denominator fraction.  This is useful to specify a limited
>> area of the image to operate on, for example computing statistics only
>> from a reduced area for speedup.
>> 
>> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
>> ---
>>  include/libcamera/geometry.h |  2 ++
>>  src/libcamera/geometry.cpp   | 18 ++++++++++++++++++
>>  2 files changed, 20 insertions(+)
>> 
>> diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
>> index f322e3d5b..603c9a117 100644
>> --- a/include/libcamera/geometry.h
>> +++ b/include/libcamera/geometry.h
>> @@ -297,6 +297,8 @@ public:
>>         [[nodiscard]] Rectangle scaledBy(const Size &numerator,
>>                                          const Size &denominator) const;
>>         [[nodiscard]] Rectangle translatedBy(const Point &point) const;
>> +       [[nodiscard]] Rectangle croppedBy(const unsigned int numerator,
>> +                                         const unsigned int denominator) const;
>>  
>>         Rectangle transformedBetween(const Rectangle &source,
>>                                      const Rectangle &target) const;
>> diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
>> index 81cc8cd53..8c1b156c5 100644
>> --- a/src/libcamera/geometry.cpp
>> +++ b/src/libcamera/geometry.cpp
>> @@ -837,6 +837,24 @@ Rectangle Rectangle::translatedBy(const Point &point) const
>>         return { x + point.x, y + point.y, width, height };
>>  }
>>  
>> +/**
>> + * \brief Return a central area crop of the rectangle
>> + * \param[in] numerator The numerator of the crop factor
>> + * \param[in] denominator The denominator of the crop factor
>> + *
>> + * The rectangle of the central part of the original rectangle is computed, of
>> + * numerator/denominator times the original width and height.
>> + *
>> + * \return The cropped Rectangle coordinates
>> + */
>> +Rectangle Rectangle::croppedBy(const unsigned int numerator,
>> +                              const unsigned int denominator) const
>> +{
>> +       const unsigned int w = numerator * width / denominator;
>> +       const unsigned int h = numerator * height / denominator;
>
> In Rectangle::scaledBy the numbers are cast to 64 bits to ensure that
> overflows don't happen. I suspect we should do the same here...
>
> Or use that call as a helper ?
>
>
>> +       return Rectangle((width - w) / 2, (height - h) / 2, w, h);
>
> Also scaledBy accounts for the x, y coordinates too. We can't always
> assume that a rectangle being cropped starts at 0,0!
>
> For example the incoming Rectangle you're croppping might already have a
> crop applied to represent the active pixels of a sensor array...
>
>> +}
>
> I wonder if implementing this as :
>
> Rectangle Rectangle::croppedBy(const unsigned int numerator,
>                                const unsigned int denominator) const
> {
> 	Size cropped = this.size() * numerator / denominator);
> 	return cropped.centeredTo(this.center());
> }
>
>
> would solve the above concerns. Actually I don't think that would
> protect against overflow ... but maybe it's not a big deal (yet?) So
> maybe inlining the Size cropped still makes sense.
>
> Which makes it more like:
>
> Rectangle Rectangle::croppedBy(const unsigned int numerator,
>                                const unsigned int denominator) const
> {
> 	unsigned int w = static_cast<uint64_t>(width) * numerator / denominator;
> 	unsigned int h = static_cast<uint64_t>(height) * numerator / denominator;
>
> 	return Size(w, h).centeredTo(center());
>
> }
>
> ?

This version looks safe and right, I'll use it in v2, thank you for the
suggestion.

> Perhaps we should also add both:
>
>   croppedBy() and cropBy()
>
> though to have the version that modifies in place? 

We don't have currently use for the in-place-modification version,
should it be added anyway?

> Finally, any additions to geometry.cpp likely need associated tests in
> test/geometry.cpp

I'll add them.

> --
> Kieran
>
>
>
>> +
>>  /**
>>   * \brief Transform a Rectangle from one reference rectangle to another
>>   * \param[in] source The \a source reference rectangle
>> -- 
>> 2.50.1
>>
Kieran Bingham July 21, 2025, 7:22 p.m. UTC | #3
Quoting Milan Zamazal (2025-07-21 19:58:55)
> Hi Kieran,
> 
> thank you for review.
> 
> Kieran Bingham <kieran.bingham@ideasonboard.com> writes:
> 
> > Quoting Milan Zamazal (2025-07-18 15:44:55)
> >> Let's add a new Rectangle method that returns the coordinates of the
> >> central area of the original rectangle cropped by given
> >
> >> numerator/denominator fraction.  This is useful to specify a limited
> >> area of the image to operate on, for example computing statistics only
> >> from a reduced area for speedup.
> >> 
> >> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
> >> ---
> >>  include/libcamera/geometry.h |  2 ++
> >>  src/libcamera/geometry.cpp   | 18 ++++++++++++++++++
> >>  2 files changed, 20 insertions(+)
> >> 
> >> diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
> >> index f322e3d5b..603c9a117 100644
> >> --- a/include/libcamera/geometry.h
> >> +++ b/include/libcamera/geometry.h
> >> @@ -297,6 +297,8 @@ public:
> >>         [[nodiscard]] Rectangle scaledBy(const Size &numerator,
> >>                                          const Size &denominator) const;
> >>         [[nodiscard]] Rectangle translatedBy(const Point &point) const;
> >> +       [[nodiscard]] Rectangle croppedBy(const unsigned int numerator,
> >> +                                         const unsigned int denominator) const;
> >>  
> >>         Rectangle transformedBetween(const Rectangle &source,
> >>                                      const Rectangle &target) const;
> >> diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
> >> index 81cc8cd53..8c1b156c5 100644
> >> --- a/src/libcamera/geometry.cpp
> >> +++ b/src/libcamera/geometry.cpp
> >> @@ -837,6 +837,24 @@ Rectangle Rectangle::translatedBy(const Point &point) const
> >>         return { x + point.x, y + point.y, width, height };
> >>  }
> >>  
> >> +/**
> >> + * \brief Return a central area crop of the rectangle
> >> + * \param[in] numerator The numerator of the crop factor
> >> + * \param[in] denominator The denominator of the crop factor
> >> + *
> >> + * The rectangle of the central part of the original rectangle is computed, of
> >> + * numerator/denominator times the original width and height.
> >> + *
> >> + * \return The cropped Rectangle coordinates
> >> + */
> >> +Rectangle Rectangle::croppedBy(const unsigned int numerator,
> >> +                              const unsigned int denominator) const
> >> +{
> >> +       const unsigned int w = numerator * width / denominator;
> >> +       const unsigned int h = numerator * height / denominator;
> >
> > In Rectangle::scaledBy the numbers are cast to 64 bits to ensure that
> > overflows don't happen. I suspect we should do the same here...
> >
> > Or use that call as a helper ?
> >
> >
> >> +       return Rectangle((width - w) / 2, (height - h) / 2, w, h);
> >
> > Also scaledBy accounts for the x, y coordinates too. We can't always
> > assume that a rectangle being cropped starts at 0,0!
> >
> > For example the incoming Rectangle you're croppping might already have a
> > crop applied to represent the active pixels of a sensor array...
> >
> >> +}
> >
> > I wonder if implementing this as :
> >
> > Rectangle Rectangle::croppedBy(const unsigned int numerator,
> >                                const unsigned int denominator) const
> > {
> >       Size cropped = this.size() * numerator / denominator);
> >       return cropped.centeredTo(this.center());
> > }
> >
> >
> > would solve the above concerns. Actually I don't think that would
> > protect against overflow ... but maybe it's not a big deal (yet?) So
> > maybe inlining the Size cropped still makes sense.
> >
> > Which makes it more like:
> >
> > Rectangle Rectangle::croppedBy(const unsigned int numerator,
> >                                const unsigned int denominator) const
> > {
> >       unsigned int w = static_cast<uint64_t>(width) * numerator / denominator;
> >       unsigned int h = static_cast<uint64_t>(height) * numerator / denominator;
> >
> >       return Size(w, h).centeredTo(center());
> >
> > }
> >
> > ?
> 
> This version looks safe and right, I'll use it in v2, thank you for the
> suggestion.
> 
> > Perhaps we should also add both:
> >
> >   croppedBy() and cropBy()
> >
> > though to have the version that modifies in place? 
> 
> We don't have currently use for the in-place-modification version,
> should it be added anyway?

If they're not needed at the moment it's fine not to add them. And
probably simpler for now. It's just the symmetry with the
scaledBy/scaleBy I was comparing against.

> 
> > Finally, any additions to geometry.cpp likely need associated tests in
> > test/geometry.cpp
> 
> I'll add them.

Thanks.

Kieran

> 
> > --
> > Kieran
> >
> >
> >
> >> +
> >>  /**
> >>   * \brief Transform a Rectangle from one reference rectangle to another
> >>   * \param[in] source The \a source reference rectangle
> >> -- 
> >> 2.50.1
> >>
>

Patch
diff mbox series

diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
index f322e3d5b..603c9a117 100644
--- a/include/libcamera/geometry.h
+++ b/include/libcamera/geometry.h
@@ -297,6 +297,8 @@  public:
 	[[nodiscard]] Rectangle scaledBy(const Size &numerator,
 					 const Size &denominator) const;
 	[[nodiscard]] Rectangle translatedBy(const Point &point) const;
+	[[nodiscard]] Rectangle croppedBy(const unsigned int numerator,
+					  const unsigned int denominator) const;
 
 	Rectangle transformedBetween(const Rectangle &source,
 				     const Rectangle &target) const;
diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
index 81cc8cd53..8c1b156c5 100644
--- a/src/libcamera/geometry.cpp
+++ b/src/libcamera/geometry.cpp
@@ -837,6 +837,24 @@  Rectangle Rectangle::translatedBy(const Point &point) const
 	return { x + point.x, y + point.y, width, height };
 }
 
+/**
+ * \brief Return a central area crop of the rectangle
+ * \param[in] numerator The numerator of the crop factor
+ * \param[in] denominator The denominator of the crop factor
+ *
+ * The rectangle of the central part of the original rectangle is computed, of
+ * numerator/denominator times the original width and height.
+ *
+ * \return The cropped Rectangle coordinates
+ */
+Rectangle Rectangle::croppedBy(const unsigned int numerator,
+			       const unsigned int denominator) const
+{
+	const unsigned int w = numerator * width / denominator;
+	const unsigned int h = numerator * height / denominator;
+	return Rectangle((width - w) / 2, (height - h) / 2, w, h);
+}
+
 /**
  * \brief Transform a Rectangle from one reference rectangle to another
  * \param[in] source The \a source reference rectangle