[libcamera-devel,v3] libcamera: properties: Re-define ScalerCropMaximum
diff mbox series

Message ID 20201201100654.33552-1-jacopo@jmondi.org
State RFC
Delegated to: Jacopo Mondi
Headers show
Series
  • [libcamera-devel,v3] libcamera: properties: Re-define ScalerCropMaximum
Related show

Commit Message

Jacopo Mondi Dec. 1, 2020, 10:06 a.m. UTC
Currently the properties::ScalerCropMaximum property reports the maximum
valid Rectangle for the controls::ScalerCrop control, which can be
combined with the sensor pixel array size in order to obtain the minimum
available zoom factor.

It is also useful to be able to calculate the maximum available zoom
factor, and in order to do so the minimum valid crop rectangle has to
be reported.

Transform the ScalerCropMaximum property in ScalerCropLimits, which
reports both the maximum and minimum crop limits and port the only user
of such a property (Raspberry Pi) to use it.

Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
---
v2 -> v3:
- Fix grammar issues pointed out by David
- Add David's tag

v1 -> v2:
- Re-based on master
- Specify that the top-left corner of the minimum rectangle is not relevant
- RPi: scale ispMinCropSize_ in the sensor's pixel array matrix coordinates
  Tested with 640x480 2x2 binned mode -> min = 128x128
  Tested with 3280x2464 non binned mode -> min = 64x64
---
 src/libcamera/control_ids.yaml                |  2 +-
 .../pipeline/raspberrypi/raspberrypi.cpp      | 17 +++++---
 src/libcamera/property_ids.yaml               | 42 ++++++++++++++-----
 3 files changed, 45 insertions(+), 16 deletions(-)

--
2.29.1

Comments

David Plowman Dec. 1, 2020, 10:12 a.m. UTC | #1
Hi Jacopo

Thanks for version 3 - just confirming that it all looks good to me now!

Best regards
David

On Tue, 1 Dec 2020 at 10:06, Jacopo Mondi <jacopo@jmondi.org> wrote:
>
> Currently the properties::ScalerCropMaximum property reports the maximum
> valid Rectangle for the controls::ScalerCrop control, which can be
> combined with the sensor pixel array size in order to obtain the minimum
> available zoom factor.
>
> It is also useful to be able to calculate the maximum available zoom
> factor, and in order to do so the minimum valid crop rectangle has to
> be reported.
>
> Transform the ScalerCropMaximum property in ScalerCropLimits, which
> reports both the maximum and minimum crop limits and port the only user
> of such a property (Raspberry Pi) to use it.
>
> Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> ---
> v2 -> v3:
> - Fix grammar issues pointed out by David
> - Add David's tag
>
> v1 -> v2:
> - Re-based on master
> - Specify that the top-left corner of the minimum rectangle is not relevant
> - RPi: scale ispMinCropSize_ in the sensor's pixel array matrix coordinates
>   Tested with 640x480 2x2 binned mode -> min = 128x128
>   Tested with 3280x2464 non binned mode -> min = 64x64
> ---
>  src/libcamera/control_ids.yaml                |  2 +-
>  .../pipeline/raspberrypi/raspberrypi.cpp      | 17 +++++---
>  src/libcamera/property_ids.yaml               | 42 ++++++++++++++-----
>  3 files changed, 45 insertions(+), 16 deletions(-)
>
> diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml
> index a883e27e22e9..44c4bb8e86b6 100644
> --- a/src/libcamera/control_ids.yaml
> +++ b/src/libcamera/control_ids.yaml
> @@ -283,7 +283,7 @@ controls:
>          a binning or skipping mode.
>
>          This control is only present when the pipeline supports scaling. Its
> -        maximum valid value is given by the properties::ScalerCropMaximum
> +        valid value limits are given by the properties::ScalerCropLimits
>          property, and the two can be used to implement digital zoom.
>
>    # ----------------------------------------------------------------------------
> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> index 6fcdf557afcf..22d37e7f1ea1 100644
> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> @@ -708,13 +708,19 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
>                 LOG(RPI, Error) << "Failed to configure the IPA: " << ret;
>
>         /*
> -        * Update the ScalerCropMaximum to the correct value for this camera mode.
> -        * For us, it's the same as the "analogue crop".
> +        * Update the ScalerCropLimits to the correct values for this camera
> +        * mode. For us, it's the same as the "analogue crop" and pixel array
> +        * portion used to produce the ispMinCropSize_;
>          *
>          * \todo Make this property the ScalerCrop maximum value when dynamic
>          * controls are available and set it at validate() time
>          */
> -       data->properties_.set(properties::ScalerCropMaximum, data->sensorInfo_.analogCrop);
> +       Rectangle sensorMinCrop =
> +               Rectangle(data->ispMinCropSize_).scaledBy(
> +                               data->sensorInfo_.analogCrop.size(),
> +                               data->sensorInfo_.outputSize);
> +       data->properties_.set(properties::ScalerCropLimits,
> +                             { data->sensorInfo_.analogCrop, sensorMinCrop });
>
>         return ret;
>  }
> @@ -946,11 +952,12 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator)
>         data->properties_ = data->sensor_->properties();
>
>         /*
> -        * Set a default value for the ScalerCropMaximum property to show
> +        * Set default values for the ScalerCropLimits property to show
>          * that we support its use, however, initialise it to zero because
>          * it's not meaningful until a camera mode has been chosen.
>          */
> -       data->properties_.set(properties::ScalerCropMaximum, Rectangle{});
> +       data->properties_.set(properties::ScalerCropLimits,
> +                             { Rectangle{}, Rectangle{} });
>
>         /*
>          * We cache three things about the sensor in relation to transforms
> diff --git a/src/libcamera/property_ids.yaml b/src/libcamera/property_ids.yaml
> index 64e88f0361d6..76474bfc5c31 100644
> --- a/src/libcamera/property_ids.yaml
> +++ b/src/libcamera/property_ids.yaml
> @@ -663,19 +663,41 @@ controls:
>          \todo Rename this property to ActiveAreas once we will have property
>                categories (i.e. Properties::PixelArray::ActiveAreas)
>
> -  - ScalerCropMaximum:
> +  - ScalerCropLimits:
>        type: Rectangle
> +      size: [2]
>        description: |
> -        The maximum valid rectangle for the controls::ScalerCrop control. This
> -        reflects the minimum mandatory cropping applied in the camera sensor and
> -        the rest of the pipeline. Just as the ScalerCrop control, it defines a
> -        rectangle taken from the sensor's active pixel array.
> -
> -        This property is valid only after the camera has been successfully
> -        configured and its value may change whenever a new configuration is
> +        The maximum and minimum valid rectangles for the controls::ScalerCrop
> +        control, specified in this order.
> +
> +        This two rectangles respectively reflect the minimum and maximum
> +        mandatory cropping applied in the camera sensor and the rest of the
> +        pipeline. Both Rectangles are defined relatively to the sensor's active
> +        pixel array (properties::PixelArrayActiveAreas).
> +
> +        Implementation details for the maximum valid crop rectangle.
> +        The maximum valid crop rectangle depends on the camera configuration as
> +        pipelines are free to adjust the frame size requested from the sensor
> +        and used to produce the final image streams. The largest possible crop
> +        rectangle is then limited by the size of the sensor's active pixel area
> +        portion used to produce the sensor output frame.
> +
> +        Implementation details for the minimum valid crop rectangle. If a
> +        pipeline cannot up-scale, the minimum valid crop rectangle is equal to
> +        the size of sensor pixel array portion used to produce the smallest
> +        available stream resolution, accounting for any applied pixel
> +        sub-sampling technique in use and including any border pixels required
> +        by the ISP for image processing. If a pipeline can up-scale, the minimum
> +        valid crop rectangle is equal to the pixel array portion that produces
> +        the smallest frame size accepted by the ISP input. The rectangle
> +        position, defined by its top-left corner, is not relevant and should be
> +        set to (0, 0) by pipelines.
> +
> +        The two rectangles are valid only after the camera has been successfully
> +        configured and their value may change whenever a new configuration is
>          applied.
>
> -        \todo Turn this property into a "maximum control value" for the
> -        ScalerCrop control once "dynamic" controls have been implemented.
> +        \todo Express this property as the limits for the ScalerCrop control
> +        once "dynamic" controls have been implemented.
>
>  ...
> --
> 2.29.1
>
Niklas Söderlund Dec. 1, 2020, 12:10 p.m. UTC | #2
Hi Jacopo,

Thanks for your patch.

On 2020-12-01 11:06:54 +0100, Jacopo Mondi wrote:
> Currently the properties::ScalerCropMaximum property reports the maximum
> valid Rectangle for the controls::ScalerCrop control, which can be
> combined with the sensor pixel array size in order to obtain the minimum
> available zoom factor.
> 
> It is also useful to be able to calculate the maximum available zoom
> factor, and in order to do so the minimum valid crop rectangle has to
> be reported.
> 
> Transform the ScalerCropMaximum property in ScalerCropLimits, which
> reports both the maximum and minimum crop limits and port the only user
> of such a property (Raspberry Pi) to use it.
> 
> Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>

I think this is a step in the right direction.

Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>

> ---
> v2 -> v3:
> - Fix grammar issues pointed out by David
> - Add David's tag
> 
> v1 -> v2:
> - Re-based on master
> - Specify that the top-left corner of the minimum rectangle is not relevant
> - RPi: scale ispMinCropSize_ in the sensor's pixel array matrix coordinates
>   Tested with 640x480 2x2 binned mode -> min = 128x128
>   Tested with 3280x2464 non binned mode -> min = 64x64
> ---
>  src/libcamera/control_ids.yaml                |  2 +-
>  .../pipeline/raspberrypi/raspberrypi.cpp      | 17 +++++---
>  src/libcamera/property_ids.yaml               | 42 ++++++++++++++-----
>  3 files changed, 45 insertions(+), 16 deletions(-)
> 
> diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml
> index a883e27e22e9..44c4bb8e86b6 100644
> --- a/src/libcamera/control_ids.yaml
> +++ b/src/libcamera/control_ids.yaml
> @@ -283,7 +283,7 @@ controls:
>          a binning or skipping mode.
> 
>          This control is only present when the pipeline supports scaling. Its
> -        maximum valid value is given by the properties::ScalerCropMaximum
> +        valid value limits are given by the properties::ScalerCropLimits
>          property, and the two can be used to implement digital zoom.
> 
>    # ----------------------------------------------------------------------------
> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> index 6fcdf557afcf..22d37e7f1ea1 100644
> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> @@ -708,13 +708,19 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
>  		LOG(RPI, Error) << "Failed to configure the IPA: " << ret;
> 
>  	/*
> -	 * Update the ScalerCropMaximum to the correct value for this camera mode.
> -	 * For us, it's the same as the "analogue crop".
> +	 * Update the ScalerCropLimits to the correct values for this camera
> +	 * mode. For us, it's the same as the "analogue crop" and pixel array
> +	 * portion used to produce the ispMinCropSize_;
>  	 *
>  	 * \todo Make this property the ScalerCrop maximum value when dynamic
>  	 * controls are available and set it at validate() time
>  	 */
> -	data->properties_.set(properties::ScalerCropMaximum, data->sensorInfo_.analogCrop);
> +	Rectangle sensorMinCrop =
> +		Rectangle(data->ispMinCropSize_).scaledBy(
> +				data->sensorInfo_.analogCrop.size(),
> +				data->sensorInfo_.outputSize);
> +	data->properties_.set(properties::ScalerCropLimits,
> +			      { data->sensorInfo_.analogCrop, sensorMinCrop });
> 
>  	return ret;
>  }
> @@ -946,11 +952,12 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator)
>  	data->properties_ = data->sensor_->properties();
> 
>  	/*
> -	 * Set a default value for the ScalerCropMaximum property to show
> +	 * Set default values for the ScalerCropLimits property to show
>  	 * that we support its use, however, initialise it to zero because
>  	 * it's not meaningful until a camera mode has been chosen.
>  	 */
> -	data->properties_.set(properties::ScalerCropMaximum, Rectangle{});
> +	data->properties_.set(properties::ScalerCropLimits,
> +			      { Rectangle{}, Rectangle{} });
> 
>  	/*
>  	 * We cache three things about the sensor in relation to transforms
> diff --git a/src/libcamera/property_ids.yaml b/src/libcamera/property_ids.yaml
> index 64e88f0361d6..76474bfc5c31 100644
> --- a/src/libcamera/property_ids.yaml
> +++ b/src/libcamera/property_ids.yaml
> @@ -663,19 +663,41 @@ controls:
>          \todo Rename this property to ActiveAreas once we will have property
>                categories (i.e. Properties::PixelArray::ActiveAreas)
> 
> -  - ScalerCropMaximum:
> +  - ScalerCropLimits:
>        type: Rectangle
> +      size: [2]
>        description: |
> -        The maximum valid rectangle for the controls::ScalerCrop control. This
> -        reflects the minimum mandatory cropping applied in the camera sensor and
> -        the rest of the pipeline. Just as the ScalerCrop control, it defines a
> -        rectangle taken from the sensor's active pixel array.
> -
> -        This property is valid only after the camera has been successfully
> -        configured and its value may change whenever a new configuration is
> +        The maximum and minimum valid rectangles for the controls::ScalerCrop
> +        control, specified in this order.
> +
> +        This two rectangles respectively reflect the minimum and maximum
> +        mandatory cropping applied in the camera sensor and the rest of the
> +        pipeline. Both Rectangles are defined relatively to the sensor's active
> +        pixel array (properties::PixelArrayActiveAreas).
> +
> +        Implementation details for the maximum valid crop rectangle.
> +        The maximum valid crop rectangle depends on the camera configuration as
> +        pipelines are free to adjust the frame size requested from the sensor
> +        and used to produce the final image streams. The largest possible crop
> +        rectangle is then limited by the size of the sensor's active pixel area
> +        portion used to produce the sensor output frame.
> +
> +        Implementation details for the minimum valid crop rectangle. If a
> +        pipeline cannot up-scale, the minimum valid crop rectangle is equal to
> +        the size of sensor pixel array portion used to produce the smallest
> +        available stream resolution, accounting for any applied pixel
> +        sub-sampling technique in use and including any border pixels required
> +        by the ISP for image processing. If a pipeline can up-scale, the minimum
> +        valid crop rectangle is equal to the pixel array portion that produces
> +        the smallest frame size accepted by the ISP input. The rectangle
> +        position, defined by its top-left corner, is not relevant and should be
> +        set to (0, 0) by pipelines.
> +
> +        The two rectangles are valid only after the camera has been successfully
> +        configured and their value may change whenever a new configuration is
>          applied.
> 
> -        \todo Turn this property into a "maximum control value" for the
> -        ScalerCrop control once "dynamic" controls have been implemented.
> +        \todo Express this property as the limits for the ScalerCrop control
> +        once "dynamic" controls have been implemented.
> 
>  ...
> --
> 2.29.1
> 
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
Laurent Pinchart Dec. 1, 2020, 1:50 p.m. UTC | #3
Hi Jacopo,

Thank you for the patch.

On Tue, Dec 01, 2020 at 11:06:54AM +0100, Jacopo Mondi wrote:
> Currently the properties::ScalerCropMaximum property reports the maximum
> valid Rectangle for the controls::ScalerCrop control, which can be
> combined with the sensor pixel array size in order to obtain the minimum
> available zoom factor.
> 
> It is also useful to be able to calculate the maximum available zoom
> factor, and in order to do so the minimum valid crop rectangle has to
> be reported.
> 
> Transform the ScalerCropMaximum property in ScalerCropLimits, which
> reports both the maximum and minimum crop limits and port the only user
> of such a property (Raspberry Pi) to use it.
> 
> Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> ---
> v2 -> v3:
> - Fix grammar issues pointed out by David
> - Add David's tag
> 
> v1 -> v2:
> - Re-based on master
> - Specify that the top-left corner of the minimum rectangle is not relevant
> - RPi: scale ispMinCropSize_ in the sensor's pixel array matrix coordinates
>   Tested with 640x480 2x2 binned mode -> min = 128x128
>   Tested with 3280x2464 non binned mode -> min = 64x64
> ---
>  src/libcamera/control_ids.yaml                |  2 +-
>  .../pipeline/raspberrypi/raspberrypi.cpp      | 17 +++++---
>  src/libcamera/property_ids.yaml               | 42 ++++++++++++++-----
>  3 files changed, 45 insertions(+), 16 deletions(-)
> 
> diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml
> index a883e27e22e9..44c4bb8e86b6 100644
> --- a/src/libcamera/control_ids.yaml
> +++ b/src/libcamera/control_ids.yaml
> @@ -283,7 +283,7 @@ controls:
>          a binning or skipping mode.
> 
>          This control is only present when the pipeline supports scaling. Its
> -        maximum valid value is given by the properties::ScalerCropMaximum
> +        valid value limits are given by the properties::ScalerCropLimits
>          property, and the two can be used to implement digital zoom.
> 
>    # ----------------------------------------------------------------------------
> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> index 6fcdf557afcf..22d37e7f1ea1 100644
> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> @@ -708,13 +708,19 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
>  		LOG(RPI, Error) << "Failed to configure the IPA: " << ret;
> 
>  	/*
> -	 * Update the ScalerCropMaximum to the correct value for this camera mode.
> -	 * For us, it's the same as the "analogue crop".
> +	 * Update the ScalerCropLimits to the correct values for this camera
> +	 * mode. For us, it's the same as the "analogue crop" and pixel array
> +	 * portion used to produce the ispMinCropSize_;
>  	 *
>  	 * \todo Make this property the ScalerCrop maximum value when dynamic
>  	 * controls are available and set it at validate() time
>  	 */
> -	data->properties_.set(properties::ScalerCropMaximum, data->sensorInfo_.analogCrop);
> +	Rectangle sensorMinCrop =
> +		Rectangle(data->ispMinCropSize_).scaledBy(
> +				data->sensorInfo_.analogCrop.size(),
> +				data->sensorInfo_.outputSize);
> +	data->properties_.set(properties::ScalerCropLimits,
> +			      { data->sensorInfo_.analogCrop, sensorMinCrop });
> 
>  	return ret;
>  }
> @@ -946,11 +952,12 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator)
>  	data->properties_ = data->sensor_->properties();
> 
>  	/*
> -	 * Set a default value for the ScalerCropMaximum property to show
> +	 * Set default values for the ScalerCropLimits property to show
>  	 * that we support its use, however, initialise it to zero because
>  	 * it's not meaningful until a camera mode has been chosen.
>  	 */
> -	data->properties_.set(properties::ScalerCropMaximum, Rectangle{});
> +	data->properties_.set(properties::ScalerCropLimits,
> +			      { Rectangle{}, Rectangle{} });
> 
>  	/*
>  	 * We cache three things about the sensor in relation to transforms
> diff --git a/src/libcamera/property_ids.yaml b/src/libcamera/property_ids.yaml
> index 64e88f0361d6..76474bfc5c31 100644
> --- a/src/libcamera/property_ids.yaml
> +++ b/src/libcamera/property_ids.yaml
> @@ -663,19 +663,41 @@ controls:
>          \todo Rename this property to ActiveAreas once we will have property
>                categories (i.e. Properties::PixelArray::ActiveAreas)
> 
> -  - ScalerCropMaximum:
> +  - ScalerCropLimits:
>        type: Rectangle
> +      size: [2]
>        description: |
> -        The maximum valid rectangle for the controls::ScalerCrop control. This
> -        reflects the minimum mandatory cropping applied in the camera sensor and
> -        the rest of the pipeline. Just as the ScalerCrop control, it defines a
> -        rectangle taken from the sensor's active pixel array.
> -
> -        This property is valid only after the camera has been successfully
> -        configured and its value may change whenever a new configuration is
> +        The maximum and minimum valid rectangles for the controls::ScalerCrop
> +        control, specified in this order.

It's quite customary to report limits in the minimum, maximum order. Is
there a reason to do it the other way around here ?

> +
> +        This two rectangles respectively reflect the minimum and maximum

s/This/These/

> +        mandatory cropping applied in the camera sensor and the rest of the

That's not quite right I think. It's true for the maximum value
(reporting the minimum mandatory cropping), but for the minimum value,
it's more about reporting the scaler limits (maximum scaling factor or
minimum input size) than the "maximum mandatory cropping". How about the
following ?

        The minimum value reflects the upscaling limit, resulting from the
        maximum scaling factor or the minimum scaler input size. The maximum
        value reflects the minimum mandatory cropping applied in the camera
        sensor and the rest of the pipeline. Both rectangles are defined
        relatively to the sensor's active pixel array
        (properties::PixelArrayActiveAreas)/

> +        pipeline. Both Rectangles are defined relatively to the sensor's active
> +        pixel array (properties::PixelArrayActiveAreas).
> +
> +        Implementation details for the maximum valid crop rectangle.

How about

        \par Implementation details

with a white line just after to create a header ? Otherwise it looks a
bit weird to have one single paragraph whose first sentence is
"Implementation details for ...".

> +        The maximum valid crop rectangle depends on the camera configuration as
> +        pipelines are free to adjust the frame size requested from the sensor
> +        and used to produce the final image streams. The largest possible crop

"frame size" makes me think about binning/skipping. Maybe "... pipelines
may select different portions of the sensor's pixel array depending on
the requested streams" ? Up to you.

> +        rectangle is then limited by the size of the sensor's active pixel area

s/then/thus/ ?

I would drop "the size of", as the position of the maximum rectangle
depends on the sensor crop configuration, it's not just the size.

> +        portion used to produce the sensor output frame.
> +
> +        Implementation details for the minimum valid crop rectangle. If a
> +        pipeline cannot up-scale, the minimum valid crop rectangle is equal to
> +        the size of sensor pixel array portion used to produce the smallest
> +        available stream resolution, accounting for any applied pixel
> +        sub-sampling technique in use and including any border pixels required
> +        by the ISP for image processing.

I'm having trouble parsing this. By "smallest available stream
resolution", are you talking about the smallest resolution a camera can
produce, or the smallest stream in the current configuration ? In the
latter case, shouldn't it be the largest stream, as upscaling is not
possible ?

> If a pipeline can up-scale, the minimum
> +        valid crop rectangle is equal to the pixel array portion that produces
> +        the smallest frame size accepted by the ISP input. The rectangle
> +        position, defined by its top-left corner, is not relevant and should be
> +        set to (0, 0) by pipelines.

Is the last sentence valid for both the non-upscaling and upscaling
cases ? It sounds like the latter, but I suppose it should be the
former. I don't think it's an implementation detail, I'd move it to the
first paragraph that could then be written

        The maximum value reflects the minimum mandatory cropping applied in the
        camera sensor and the rest of the pipeline. It specifies the boundary
        rectangle within which the ScalerCrop rectangle can be positioned. The
        value is defined relatively to the sensor's active pixel array
        (properties::PixelArrayActiveAreas)/

        The minimum value reflects the upscaling limit. It results from the
        maximum scaling factor or the minimum scaler input size, and specifies
        the minimum size for the ScalerCrop rectangle. The position of the
        minmum rectangle isn't relevant and shall be set to (0, 0).

> +
> +        The two rectangles are valid only after the camera has been successfully
> +        configured and their value may change whenever a new configuration is
>          applied.
> 
> -        \todo Turn this property into a "maximum control value" for the
> -        ScalerCrop control once "dynamic" controls have been implemented.
> +        \todo Express this property as the limits for the ScalerCrop control
> +        once "dynamic" controls have been implemented.
> 
>  ...
Jacopo Mondi Dec. 1, 2020, 5:01 p.m. UTC | #4
Hi Laurent,

On Tue, Dec 01, 2020 at 03:50:39PM +0200, Laurent Pinchart wrote:
> Hi Jacopo,
>
> Thank you for the patch.
>
> On Tue, Dec 01, 2020 at 11:06:54AM +0100, Jacopo Mondi wrote:
> > Currently the properties::ScalerCropMaximum property reports the maximum
> > valid Rectangle for the controls::ScalerCrop control, which can be
> > combined with the sensor pixel array size in order to obtain the minimum
> > available zoom factor.
> >
> > It is also useful to be able to calculate the maximum available zoom
> > factor, and in order to do so the minimum valid crop rectangle has to
> > be reported.
> >
> > Transform the ScalerCropMaximum property in ScalerCropLimits, which
> > reports both the maximum and minimum crop limits and port the only user
> > of such a property (Raspberry Pi) to use it.
> >
> > Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
> > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > ---
> > v2 -> v3:
> > - Fix grammar issues pointed out by David
> > - Add David's tag
> >
> > v1 -> v2:
> > - Re-based on master
> > - Specify that the top-left corner of the minimum rectangle is not relevant
> > - RPi: scale ispMinCropSize_ in the sensor's pixel array matrix coordinates
> >   Tested with 640x480 2x2 binned mode -> min = 128x128
> >   Tested with 3280x2464 non binned mode -> min = 64x64
> > ---
> >  src/libcamera/control_ids.yaml                |  2 +-
> >  .../pipeline/raspberrypi/raspberrypi.cpp      | 17 +++++---
> >  src/libcamera/property_ids.yaml               | 42 ++++++++++++++-----
> >  3 files changed, 45 insertions(+), 16 deletions(-)
> >
> > diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml
> > index a883e27e22e9..44c4bb8e86b6 100644
> > --- a/src/libcamera/control_ids.yaml
> > +++ b/src/libcamera/control_ids.yaml
> > @@ -283,7 +283,7 @@ controls:
> >          a binning or skipping mode.
> >
> >          This control is only present when the pipeline supports scaling. Its
> > -        maximum valid value is given by the properties::ScalerCropMaximum
> > +        valid value limits are given by the properties::ScalerCropLimits
> >          property, and the two can be used to implement digital zoom.
> >
> >    # ----------------------------------------------------------------------------
> > diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > index 6fcdf557afcf..22d37e7f1ea1 100644
> > --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > @@ -708,13 +708,19 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
> >  		LOG(RPI, Error) << "Failed to configure the IPA: " << ret;
> >
> >  	/*
> > -	 * Update the ScalerCropMaximum to the correct value for this camera mode.
> > -	 * For us, it's the same as the "analogue crop".
> > +	 * Update the ScalerCropLimits to the correct values for this camera
> > +	 * mode. For us, it's the same as the "analogue crop" and pixel array
> > +	 * portion used to produce the ispMinCropSize_;
> >  	 *
> >  	 * \todo Make this property the ScalerCrop maximum value when dynamic
> >  	 * controls are available and set it at validate() time
> >  	 */
> > -	data->properties_.set(properties::ScalerCropMaximum, data->sensorInfo_.analogCrop);
> > +	Rectangle sensorMinCrop =
> > +		Rectangle(data->ispMinCropSize_).scaledBy(
> > +				data->sensorInfo_.analogCrop.size(),
> > +				data->sensorInfo_.outputSize);
> > +	data->properties_.set(properties::ScalerCropLimits,
> > +			      { data->sensorInfo_.analogCrop, sensorMinCrop });
> >
> >  	return ret;
> >  }
> > @@ -946,11 +952,12 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator)
> >  	data->properties_ = data->sensor_->properties();
> >
> >  	/*
> > -	 * Set a default value for the ScalerCropMaximum property to show
> > +	 * Set default values for the ScalerCropLimits property to show
> >  	 * that we support its use, however, initialise it to zero because
> >  	 * it's not meaningful until a camera mode has been chosen.
> >  	 */
> > -	data->properties_.set(properties::ScalerCropMaximum, Rectangle{});
> > +	data->properties_.set(properties::ScalerCropLimits,
> > +			      { Rectangle{}, Rectangle{} });
> >
> >  	/*
> >  	 * We cache three things about the sensor in relation to transforms
> > diff --git a/src/libcamera/property_ids.yaml b/src/libcamera/property_ids.yaml
> > index 64e88f0361d6..76474bfc5c31 100644
> > --- a/src/libcamera/property_ids.yaml
> > +++ b/src/libcamera/property_ids.yaml
> > @@ -663,19 +663,41 @@ controls:
> >          \todo Rename this property to ActiveAreas once we will have property
> >                categories (i.e. Properties::PixelArray::ActiveAreas)
> >
> > -  - ScalerCropMaximum:
> > +  - ScalerCropLimits:
> >        type: Rectangle
> > +      size: [2]
> >        description: |
> > -        The maximum valid rectangle for the controls::ScalerCrop control. This
> > -        reflects the minimum mandatory cropping applied in the camera sensor and
> > -        the rest of the pipeline. Just as the ScalerCrop control, it defines a
> > -        rectangle taken from the sensor's active pixel array.
> > -
> > -        This property is valid only after the camera has been successfully
> > -        configured and its value may change whenever a new configuration is
> > +        The maximum and minimum valid rectangles for the controls::ScalerCrop
> > +        control, specified in this order.
>
> It's quite customary to report limits in the minimum, maximum order. Is
> there a reason to do it the other way around here ?
>

No specific reasons, I can swap them

> > +
> > +        This two rectangles respectively reflect the minimum and maximum
>
> s/This/These/
>
> > +        mandatory cropping applied in the camera sensor and the rest of the
>
> That's not quite right I think. It's true for the maximum value
> (reporting the minimum mandatory cropping), but for the minimum value,
> it's more about reporting the scaler limits (maximum scaling factor or
> minimum input size) than the "maximum mandatory cropping". How about the
> following ?

Yes, "max mandatory cropping doesn't sound right".

>
>         The minimum value reflects the upscaling limit, resulting from the
>         maximum scaling factor or the minimum scaler input size. The maximum
>         value reflects the minimum mandatory cropping applied in the camera
>         sensor and the rest of the pipeline. Both rectangles are defined
>         relatively to the sensor's active pixel array
>         (properties::PixelArrayActiveAreas)/
>

Ack

> > +        pipeline. Both Rectangles are defined relatively to the sensor's active
> > +        pixel array (properties::PixelArrayActiveAreas).
> > +
> > +        Implementation details for the maximum valid crop rectangle.
>
> How about
>
>         \par Implementation details
>
> with a white line just after to create a header ? Otherwise it looks a
> bit weird to have one single paragraph whose first sentence is
> "Implementation details for ...".
>

Ack

> > +        The maximum valid crop rectangle depends on the camera configuration as
> > +        pipelines are free to adjust the frame size requested from the sensor
> > +        and used to produce the final image streams. The largest possible crop
>
> "frame size" makes me think about binning/skipping. Maybe "... pipelines
> may select different portions of the sensor's pixel array depending on
> the requested streams" ? Up to you.
>

Ack

> > +        rectangle is then limited by the size of the sensor's active pixel area
>
> s/then/thus/ ?
>
> I would drop "the size of", as the position of the maximum rectangle
> depends on the sensor crop configuration, it's not just the size.
>

Ack

> > +        portion used to produce the sensor output frame.
> > +
> > +        Implementation details for the minimum valid crop rectangle. If a
> > +        pipeline cannot up-scale, the minimum valid crop rectangle is equal to
> > +        the size of sensor pixel array portion used to produce the smallest
> > +        available stream resolution, accounting for any applied pixel
> > +        sub-sampling technique in use and including any border pixels required
> > +        by the ISP for image processing.
>
> I'm having trouble parsing this. By "smallest available stream
> resolution", are you talking about the smallest resolution a camera can
> produce, or the smallest stream in the current configuration ? In the
> latter case, shouldn't it be the largest stream, as upscaling is not
> possible ?

I meant the "smallest resolution in the current configuration"
and I think "the smallest" is right, but I get what you mean.

It's not easy to express this with words but let me try.

  What I meant is that the min crop rectangle should be equal to the
  analog crop rectangle of the smaller sensor mode the pipeline can
  select, when considered in isolation.

  I thought at this a static property mostly, that will give you the
  possibility to calculate the largest possible zoom factor, to be
  populated at camera initialization time (to make it possible to
  report ANDROID_SCALER_MAX_DIGITAL_ZOOM). When a mode is applied, the
  pipeline will of course select a sensor mode large enough to
  accommodate the largest stream configuration (assuming no
  up-scaling) and update this. To me it was implied, but I agree it's
  not clear.

So, I guess the thing boils down to the fact I want to use this also
to report the max digital zoom factor to android, and this is an
absolute value, so I envisioned pipelines to initialize this at the
smallest selectable sensor mode.

How should we reword this to make it fit for both usages ?

>
> > If a pipeline can up-scale, the minimum
> > +        valid crop rectangle is equal to the pixel array portion that produces
> > +        the smallest frame size accepted by the ISP input. The rectangle
> > +        position, defined by its top-left corner, is not relevant and should be
> > +        set to (0, 0) by pipelines.
>
> Is the last sentence valid for both the non-upscaling and upscaling
> cases ? It sounds like the latter, but I suppose it should be the
> former. I don't think it's an implementation detail, I'd move it to the
> first paragraph that could then be written
>
>         The maximum value reflects the minimum mandatory cropping applied in the
>         camera sensor and the rest of the pipeline. It specifies the boundary
>         rectangle within which the ScalerCrop rectangle can be positioned. The
>         value is defined relatively to the sensor's active pixel array
>         (properties::PixelArrayActiveAreas)/
>
>         The minimum value reflects the upscaling limit. It results from the
>         maximum scaling factor or the minimum scaler input size, and specifies
>         the minimum size for the ScalerCrop rectangle. The position of the
>         minmum rectangle isn't relevant and shall be set to (0, 0).
>

ok

> > +
> > +        The two rectangles are valid only after the camera has been successfully
> > +        configured and their value may change whenever a new configuration is
> >          applied.
> >
> > -        \todo Turn this property into a "maximum control value" for the
> > -        ScalerCrop control once "dynamic" controls have been implemented.
> > +        \todo Express this property as the limits for the ScalerCrop control
> > +        once "dynamic" controls have been implemented.
> >
> >  ...
>
> --
> Regards,
>
> Laurent Pinchart
Jacopo Mondi Dec. 7, 2020, 11:24 a.m. UTC | #5
Hi Laurent,
  gentle ping

On Tue, Dec 01, 2020 at 06:01:57PM +0100, Jacopo Mondi wrote:
> Hi Laurent,
>
> On Tue, Dec 01, 2020 at 03:50:39PM +0200, Laurent Pinchart wrote:
> > Hi Jacopo,
> >
> > Thank you for the patch.
> >

[snip]

> > > +        Implementation details for the minimum valid crop rectangle. If a
> > > +        pipeline cannot up-scale, the minimum valid crop rectangle is equal to
> > > +        the size of sensor pixel array portion used to produce the smallest
> > > +        available stream resolution, accounting for any applied pixel
> > > +        sub-sampling technique in use and including any border pixels required
> > > +        by the ISP for image processing.
> >
> > I'm having trouble parsing this. By "smallest available stream
> > resolution", are you talking about the smallest resolution a camera can
> > produce, or the smallest stream in the current configuration ? In the
> > latter case, shouldn't it be the largest stream, as upscaling is not
> > possible ?
>
> I meant the "smallest resolution in the current configuration"
> and I think "the smallest" is right, but I get what you mean.
>
> It's not easy to express this with words but let me try.
>
>   What I meant is that the min crop rectangle should be equal to the
>   analog crop rectangle of the smaller sensor mode the pipeline can
>   select, when considered in isolation.
>
>   I thought at this a static property mostly, that will give you the
>   possibility to calculate the largest possible zoom factor, to be
>   populated at camera initialization time (to make it possible to
>   report ANDROID_SCALER_MAX_DIGITAL_ZOOM). When a mode is applied, the
>   pipeline will of course select a sensor mode large enough to
>   accommodate the largest stream configuration (assuming no
>   up-scaling) and update this. To me it was implied, but I agree it's
>   not clear.
>
> So, I guess the thing boils down to the fact I want to use this also
> to report the max digital zoom factor to android, and this is an
> absolute value, so I envisioned pipelines to initialize this at the
> smallest selectable sensor mode.
>
> How should we reword this to make it fit for both usages ?
>
Laurent Pinchart Dec. 15, 2020, 4:38 a.m. UTC | #6
Hi Jacopo,

On Tue, Dec 01, 2020 at 06:01:57PM +0100, Jacopo Mondi wrote:
> On Tue, Dec 01, 2020 at 03:50:39PM +0200, Laurent Pinchart wrote:
> > On Tue, Dec 01, 2020 at 11:06:54AM +0100, Jacopo Mondi wrote:
> > > Currently the properties::ScalerCropMaximum property reports the maximum
> > > valid Rectangle for the controls::ScalerCrop control, which can be
> > > combined with the sensor pixel array size in order to obtain the minimum
> > > available zoom factor.
> > >
> > > It is also useful to be able to calculate the maximum available zoom
> > > factor, and in order to do so the minimum valid crop rectangle has to
> > > be reported.
> > >
> > > Transform the ScalerCropMaximum property in ScalerCropLimits, which
> > > reports both the maximum and minimum crop limits and port the only user
> > > of such a property (Raspberry Pi) to use it.
> > >
> > > Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
> > > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > > ---
> > > v2 -> v3:
> > > - Fix grammar issues pointed out by David
> > > - Add David's tag
> > >
> > > v1 -> v2:
> > > - Re-based on master
> > > - Specify that the top-left corner of the minimum rectangle is not relevant
> > > - RPi: scale ispMinCropSize_ in the sensor's pixel array matrix coordinates
> > >   Tested with 640x480 2x2 binned mode -> min = 128x128
> > >   Tested with 3280x2464 non binned mode -> min = 64x64
> > > ---
> > >  src/libcamera/control_ids.yaml                |  2 +-
> > >  .../pipeline/raspberrypi/raspberrypi.cpp      | 17 +++++---
> > >  src/libcamera/property_ids.yaml               | 42 ++++++++++++++-----
> > >  3 files changed, 45 insertions(+), 16 deletions(-)
> > >
> > > diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml
> > > index a883e27e22e9..44c4bb8e86b6 100644
> > > --- a/src/libcamera/control_ids.yaml
> > > +++ b/src/libcamera/control_ids.yaml
> > > @@ -283,7 +283,7 @@ controls:
> > >          a binning or skipping mode.
> > >
> > >          This control is only present when the pipeline supports scaling. Its
> > > -        maximum valid value is given by the properties::ScalerCropMaximum
> > > +        valid value limits are given by the properties::ScalerCropLimits
> > >          property, and the two can be used to implement digital zoom.
> > >
> > >    # ----------------------------------------------------------------------------
> > > diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > > index 6fcdf557afcf..22d37e7f1ea1 100644
> > > --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > > +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > > @@ -708,13 +708,19 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
> > >  		LOG(RPI, Error) << "Failed to configure the IPA: " << ret;
> > >
> > >  	/*
> > > -	 * Update the ScalerCropMaximum to the correct value for this camera mode.
> > > -	 * For us, it's the same as the "analogue crop".
> > > +	 * Update the ScalerCropLimits to the correct values for this camera
> > > +	 * mode. For us, it's the same as the "analogue crop" and pixel array
> > > +	 * portion used to produce the ispMinCropSize_;
> > >  	 *
> > >  	 * \todo Make this property the ScalerCrop maximum value when dynamic
> > >  	 * controls are available and set it at validate() time
> > >  	 */
> > > -	data->properties_.set(properties::ScalerCropMaximum, data->sensorInfo_.analogCrop);
> > > +	Rectangle sensorMinCrop =
> > > +		Rectangle(data->ispMinCropSize_).scaledBy(
> > > +				data->sensorInfo_.analogCrop.size(),
> > > +				data->sensorInfo_.outputSize);
> > > +	data->properties_.set(properties::ScalerCropLimits,
> > > +			      { data->sensorInfo_.analogCrop, sensorMinCrop });
> > >
> > >  	return ret;
> > >  }
> > > @@ -946,11 +952,12 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator)
> > >  	data->properties_ = data->sensor_->properties();
> > >
> > >  	/*
> > > -	 * Set a default value for the ScalerCropMaximum property to show
> > > +	 * Set default values for the ScalerCropLimits property to show
> > >  	 * that we support its use, however, initialise it to zero because
> > >  	 * it's not meaningful until a camera mode has been chosen.
> > >  	 */
> > > -	data->properties_.set(properties::ScalerCropMaximum, Rectangle{});
> > > +	data->properties_.set(properties::ScalerCropLimits,
> > > +			      { Rectangle{}, Rectangle{} });
> > >
> > >  	/*
> > >  	 * We cache three things about the sensor in relation to transforms
> > > diff --git a/src/libcamera/property_ids.yaml b/src/libcamera/property_ids.yaml
> > > index 64e88f0361d6..76474bfc5c31 100644
> > > --- a/src/libcamera/property_ids.yaml
> > > +++ b/src/libcamera/property_ids.yaml
> > > @@ -663,19 +663,41 @@ controls:
> > >          \todo Rename this property to ActiveAreas once we will have property
> > >                categories (i.e. Properties::PixelArray::ActiveAreas)
> > >
> > > -  - ScalerCropMaximum:
> > > +  - ScalerCropLimits:
> > >        type: Rectangle
> > > +      size: [2]
> > >        description: |
> > > -        The maximum valid rectangle for the controls::ScalerCrop control. This
> > > -        reflects the minimum mandatory cropping applied in the camera sensor and
> > > -        the rest of the pipeline. Just as the ScalerCrop control, it defines a
> > > -        rectangle taken from the sensor's active pixel array.
> > > -
> > > -        This property is valid only after the camera has been successfully
> > > -        configured and its value may change whenever a new configuration is
> > > +        The maximum and minimum valid rectangles for the controls::ScalerCrop
> > > +        control, specified in this order.
> >
> > It's quite customary to report limits in the minimum, maximum order. Is
> > there a reason to do it the other way around here ?
> 
> No specific reasons, I can swap them
> 
> > > +
> > > +        This two rectangles respectively reflect the minimum and maximum
> >
> > s/This/These/
> >
> > > +        mandatory cropping applied in the camera sensor and the rest of the
> >
> > That's not quite right I think. It's true for the maximum value
> > (reporting the minimum mandatory cropping), but for the minimum value,
> > it's more about reporting the scaler limits (maximum scaling factor or
> > minimum input size) than the "maximum mandatory cropping". How about the
> > following ?
> 
> Yes, "max mandatory cropping doesn't sound right".
> 
> >         The minimum value reflects the upscaling limit, resulting from the
> >         maximum scaling factor or the minimum scaler input size. The maximum
> >         value reflects the minimum mandatory cropping applied in the camera
> >         sensor and the rest of the pipeline. Both rectangles are defined
> >         relatively to the sensor's active pixel array
> >         (properties::PixelArrayActiveAreas)/
> >
> 
> Ack
> 
> > > +        pipeline. Both Rectangles are defined relatively to the sensor's active
> > > +        pixel array (properties::PixelArrayActiveAreas).
> > > +
> > > +        Implementation details for the maximum valid crop rectangle.
> >
> > How about
> >
> >         \par Implementation details
> >
> > with a white line just after to create a header ? Otherwise it looks a
> > bit weird to have one single paragraph whose first sentence is
> > "Implementation details for ...".
> >
> 
> Ack
> 
> > > +        The maximum valid crop rectangle depends on the camera configuration as
> > > +        pipelines are free to adjust the frame size requested from the sensor
> > > +        and used to produce the final image streams. The largest possible crop
> >
> > "frame size" makes me think about binning/skipping. Maybe "... pipelines
> > may select different portions of the sensor's pixel array depending on
> > the requested streams" ? Up to you.
> >
> 
> Ack
> 
> > > +        rectangle is then limited by the size of the sensor's active pixel area
> >
> > s/then/thus/ ?
> >
> > I would drop "the size of", as the position of the maximum rectangle
> > depends on the sensor crop configuration, it's not just the size.
> >
> 
> Ack
> 
> > > +        portion used to produce the sensor output frame.
> > > +
> > > +        Implementation details for the minimum valid crop rectangle. If a
> > > +        pipeline cannot up-scale, the minimum valid crop rectangle is equal to
> > > +        the size of sensor pixel array portion used to produce the smallest
> > > +        available stream resolution, accounting for any applied pixel
> > > +        sub-sampling technique in use and including any border pixels required
> > > +        by the ISP for image processing.
> >
> > I'm having trouble parsing this. By "smallest available stream
> > resolution", are you talking about the smallest resolution a camera can
> > produce, or the smallest stream in the current configuration ? In the
> > latter case, shouldn't it be the largest stream, as upscaling is not
> > possible ?
> 
> I meant the "smallest resolution in the current configuration"
> and I think "the smallest" is right, but I get what you mean.
> 
> It's not easy to express this with words but let me try.
> 
>   What I meant is that the min crop rectangle should be equal to the
>   analog crop rectangle of the smaller sensor mode the pipeline can
>   select, when considered in isolation.

I don't think that's correct. The minimum crop rectangle is indeed
defined in pixel array coordinates, but it corresponds to the smallest
input size for the scaler, when considering the scaler output size and
its maximum upscaling factor, not the smallest analog crop rectangle the
sensor can produce (otherwise, for non mode-based sensors, that would
usually be a very very small value).

This can be a bit tricky, and easily misleading. Let's assume a sensor
that can bin, and a scaler at the very last stage of the ISP that can at
most downscale by 1/4 and upscale by x4. Let's further assume that
nothing in the pipeline applies additional cropping. This is likely
incorrect, but simplifies the example and doesn't affect the conclusion.
Finally, let's consider we capture a 1920x1080 stream.

The minimum crop rectangle size at the scaler input is 480x270 (1/4 of
1920x1080). Translated to sensor coordinates, this becomes 960x540 if
the sensor is configured with binning, or stays 480x270 otherwise. The
maximum digital zoom that the system can apply is thus the sensor
resolution (note how we haven't defined it yet) divided by 960x540 or by
480x270, depending on the sensor configuration selected by the pipeline
handler. We thus have a different maximum zoom depending on the camera
configuration.

Note that the maximium zoom factor is usually *not* x4 in this case,
even if that's the maximum upscaling factor of the sensor. Let's assume
a sensor resolution larger than 1920x1080, for instance 4056x3042. What
will a pipeline handler typically do ? There are two options.

- It can configure the sensor to output the full resolution. The aspect
  ratios don't match, so the image will be cropped to 4056x2282, and
  then *downscaled* to 1920x1080. When zooming in with the scaler, we
  can go down to a crop rectangle of 480x270. The maximum zoom factor is
  4056/480 = 2282/270 = 8.45. Note that if the application had decided
  to capture 1280x720, the minimum scaler crop would have been 320x180,
  and the maximum zoom factor 4056/320 = 2282/180 = 12.675.

- It can also configure the sensor to bin the image, for instance
  because the pipeline's bandwidth at full resolution would restrict the
  frame rate. The input to the scaler thus become 2028x1521, which would
  be cropped to 2028x1140 to product the correct aspect ratio. The image
  will again be downscaled to 1920x1080. When zomming in with the
  scaler, we can go down to a crop rectangle of 480x270. The maximum
  zoom factor is hus 2028/480 = 1140/270 = 4.225. If we translate the
  crop rectangle to sensor coordinates (as done by the scaler crop limit
  property), we get a rectangle of 960x540 pixels, but the maximum zoom
  factor is then calculated as 4056/960 = 2282/540 = 4.225, yielding the
  same value.

We assume there that the binning configuration of the sensor can't be
changed while streaming. Otherwise we would have two scalers that can be
combined, and the results would be different.

We also assume a sensor resolution larger than the stream size. This is
required by Android (upscaling isn't allowed when no digital zoom is
applied), but we don't have to set such a limitation in libcamera
itself. If the sensor resolution was smaller than the stream size, we
would already scale up when no digital zoom is applied, and the maximum
digital zoom factor would then be smaller than x4.

Finally, note that deciding whether to bin or not isn't always
completely up to the pipeline handler. When the scaler has minimum
downscaling ratio, binning may need to be applied to achieve small
output resolutions. With the above example, if the application wants to
capture 640x480, binning will be required as it would otherwise exceed
the limits of the scaler (it would require downscaling by 6.3375). This
is needed to fulfil Android's requirements (no digital zoom by default),
but is also not a limit that we need to enforce in libcamera (an
application may be interested in applying digital zoom between 1.584 and
25.35, instead of between 1.0 and 12.675).

>   I thought at this a static property mostly, that will give you the
>   possibility to calculate the largest possible zoom factor, to be
>   populated at camera initialization time (to make it possible to
>   report ANDROID_SCALER_MAX_DIGITAL_ZOOM). When a mode is applied, the
>   pipeline will of course select a sensor mode large enough to
>   accommodate the largest stream configuration (assuming no
>   up-scaling) and update this. To me it was implied, but I agree it's
>   not clear.
> 
> So, I guess the thing boils down to the fact I want to use this also
> to report the max digital zoom factor to android, and this is an
> absolute value, so I envisioned pipelines to initialize this at the
> smallest selectable sensor mode.

There are a few challenges in doing so, given the above, as the maximum
zoom factor depends on the camera configuration (it also gets more
complicated when we consider multiple streams). This requires a bit more
thinking. I'd be happy to brainstorm this further with you if that can
help.

> How should we reword this to make it fit for both usages ?

Let's agree on the above first :-)

> > > If a pipeline can up-scale, the minimum
> > > +        valid crop rectangle is equal to the pixel array portion that produces
> > > +        the smallest frame size accepted by the ISP input. The rectangle
> > > +        position, defined by its top-left corner, is not relevant and should be
> > > +        set to (0, 0) by pipelines.
> >
> > Is the last sentence valid for both the non-upscaling and upscaling
> > cases ? It sounds like the latter, but I suppose it should be the
> > former. I don't think it's an implementation detail, I'd move it to the
> > first paragraph that could then be written
> >
> >         The maximum value reflects the minimum mandatory cropping applied in the
> >         camera sensor and the rest of the pipeline. It specifies the boundary
> >         rectangle within which the ScalerCrop rectangle can be positioned. The
> >         value is defined relatively to the sensor's active pixel array
> >         (properties::PixelArrayActiveAreas)/
> >
> >         The minimum value reflects the upscaling limit. It results from the
> >         maximum scaling factor or the minimum scaler input size, and specifies
> >         the minimum size for the ScalerCrop rectangle. The position of the
> >         minmum rectangle isn't relevant and shall be set to (0, 0).
> >
> 
> ok
> 
> > > +
> > > +        The two rectangles are valid only after the camera has been successfully
> > > +        configured and their value may change whenever a new configuration is
> > >          applied.
> > >
> > > -        \todo Turn this property into a "maximum control value" for the
> > > -        ScalerCrop control once "dynamic" controls have been implemented.
> > > +        \todo Express this property as the limits for the ScalerCrop control
> > > +        once "dynamic" controls have been implemented.
> > >
> > >  ...
Jacopo Mondi Dec. 17, 2020, 12:36 p.m. UTC | #7
Hi Laurent,

On Tue, Dec 15, 2020 at 06:38:33AM +0200, Laurent Pinchart wrote:
> Hi Jacopo,
>

[snip]

> > > > +
> > > > +        Implementation details for the minimum valid crop rectangle. If a
> > > > +        pipeline cannot up-scale, the minimum valid crop rectangle is equal to
> > > > +        the size of sensor pixel array portion used to produce the smallest
> > > > +        available stream resolution, accounting for any applied pixel
> > > > +        sub-sampling technique in use and including any border pixels required
> > > > +        by the ISP for image processing.
> > >
> > > I'm having trouble parsing this. By "smallest available stream
> > > resolution", are you talking about the smallest resolution a camera can
> > > produce, or the smallest stream in the current configuration ? In the
> > > latter case, shouldn't it be the largest stream, as upscaling is not
> > > possible ?
> >
> > I meant the "smallest resolution in the current configuration"
> > and I think "the smallest" is right, but I get what you mean.
> >
> > It's not easy to express this with words but let me try.
> >
> >   What I meant is that the min crop rectangle should be equal to the
> >   analog crop rectangle of the smaller sensor mode the pipeline can
> >   select, when considered in isolation.
>
> I don't think that's correct. The minimum crop rectangle is indeed
> defined in pixel array coordinates, but it corresponds to the smallest
> input size for the scaler, when considering the scaler output size and
> its maximum upscaling factor, not the smallest analog crop rectangle the
> sensor can produce (otherwise, for non mode-based sensors, that would
> usually be a very very small value).

Maybe I don't fully get this but the text does not mention "the
smallest analog crop the sensor can produce" but rather
"the size of sensor pixel array portion used to produce the smallest
available stream resolution". All of this prefixed by "if a pipeline
cannot up-scale"

Probably it's also questionable if the "pipeline cannot up-scale" case
is really relevant when talking about digital zoom with one stream.

>
> This can be a bit tricky, and easily misleading. Let's assume a sensor
> that can bin, and a scaler at the very last stage of the ISP that can at
> most downscale by 1/4 and upscale by x4. Let's further assume that
> nothing in the pipeline applies additional cropping. This is likely
> incorrect, but simplifies the example and doesn't affect the conclusion.
> Finally, let's consider we capture a 1920x1080 stream.
>
> The minimum crop rectangle size at the scaler input is 480x270 (1/4 of
> 1920x1080). Translated to sensor coordinates, this becomes 960x540 if
> the sensor is configured with binning, or stays 480x270 otherwise. The
> maximum digital zoom that the system can apply is thus the sensor
> resolution (note how we haven't defined it yet) divided by 960x540 or by
> 480x270, depending on the sensor configuration selected by the pipeline
> handler. We thus have a different maximum zoom depending on the camera
> configuration.

The fact that it depends on the camera configuration to me was
implied, the current property definition is already dependent on the
configuration and reports it with:

        \todo Turn this property into a "maximum control value" for the
        ScalerCrop control once "dynamic" controls have been implemented.

Which I've replaced with

        \todo Express this property as the limits for the ScalerCrop control
        once "dynamic" controls have been implemented.

do you think the new definition makes the situation worse ?
I don't think it does for libcamera, but is problematic for android.

>
> Note that the maximium zoom factor is usually *not* x4 in this case,
> even if that's the maximum upscaling factor of the sensor. Let's assume

s/of the sensor/of the scaler/ right ? Or did I missed something ?

> a sensor resolution larger than 1920x1080, for instance 4056x3042. What
> will a pipeline handler typically do ? There are two options.
>
> - It can configure the sensor to output the full resolution. The aspect
>   ratios don't match, so the image will be cropped to 4056x2282, and
>   then *downscaled* to 1920x1080. When zooming in with the scaler, we
>   can go down to a crop rectangle of 480x270. The maximum zoom factor is
>   4056/480 = 2282/270 = 8.45. Note that if the application had decided
>   to capture 1280x720, the minimum scaler crop would have been 320x180,
>   and the maximum zoom factor 4056/320 = 2282/180 = 12.675.
>

Indeed.
Doesn't this example match the "size of the pixel array portion used
to produce the smallest available stream resolution" definition I gave
for the minimum crop rectangle ?

I think replacing 'smallest available stream resolution' with 'the smallest
of the stream resolutions part of a configuration' would make it more
clear ?

If I resume your examples here:
1) output=1920x1080, minScalerInput=480x270
        if 480x720 is 2x2 binned -> analogCrop = 960x540
                zoomFactor = (active pixel array / analog crop) = 4.225
2) output=1920x1080, minScalerInput=480x270
        if 480x720 is not binned -> analogCrop = 480x720
                zoomFactor = (active pixel array / analog crop) = 8.45
3) output=1280x720, minScalerInput=320x180
        if 320x180 is not binned -> analogCrop = 320x180
                zoomFactor = (active pixel array / analog crop) = 12.675

> - It can also configure the sensor to bin the image, for instance
>   because the pipeline's bandwidth at full resolution would restrict the
>   frame rate. The input to the scaler thus become 2028x1521, which would
>   be cropped to 2028x1140 to product the correct aspect ratio. The image
>   will again be downscaled to 1920x1080. When zomming in with the
>   scaler, we can go down to a crop rectangle of 480x270. The maximum
>   zoom factor is hus 2028/480 = 1140/270 = 4.225. If we translate the
>   crop rectangle to sensor coordinates (as done by the scaler crop limit
>   property), we get a rectangle of 960x540 pixels, but the maximum zoom
>   factor is then calculated as 4056/960 = 2282/540 = 4.225, yielding the
>   same value.

Isn't this actually correct ? If we can only obtain 480x270 by binning
and we translate it back to the sensor coordinates to 960x540 and we
do the same for the scaler input size (which is not relevant as we reason
in terms of pixel array size 2028x1521 2x2 binned = 4056x2282)

Again, this changes with the camera configuration, and depends on a
combination of the sensor capabilities (both the device capabilities
and the driver limitations) and the pipeline.

It doesn't seem contraditory to me, if not that establishing an
absolute zoom factor as requested by android is not possible or
easy. This can be solved in the HAL by testing the list of supported
configurations and inspecting the property to collect the maximum and
report it.

>
> We assume there that the binning configuration of the sensor can't be
> changed while streaming. Otherwise we would have two scalers that can be
> combined, and the results would be different.
>
> We also assume a sensor resolution larger than the stream size. This is
> required by Android (upscaling isn't allowed when no digital zoom is
> applied), but we don't have to set such a limitation in libcamera
> itself. If the sensor resolution was smaller than the stream size, we
> would already scale up when no digital zoom is applied, and the maximum
> digital zoom factor would then be smaller than x4.

Can we mark the "sensor resolution smaller than the largest stream"
use case as a corner case for devices that support android ?
It seems very unlikely to me that an Android device relies on
up-scaling to produce its largest stream. Am I overlooking something ?

>
> Finally, note that deciding whether to bin or not isn't always
> completely up to the pipeline handler. When the scaler has minimum
> downscaling ratio, binning may need to be applied to achieve small
> output resolutions. With the above example, if the application wants to
> capture 640x480, binning will be required as it would otherwise exceed
> the limits of the scaler (it would require downscaling by 6.3375). This
> is needed to fulfil Android's requirements (no digital zoom by default),
> but is also not a limit that we need to enforce in libcamera (an
> application may be interested in applying digital zoom between 1.584 and
> 25.35, instead of between 1.0 and 12.675).
>
> >   I thought at this a static property mostly, that will give you the
> >   possibility to calculate the largest possible zoom factor, to be
> >   populated at camera initialization time (to make it possible to
> >   report ANDROID_SCALER_MAX_DIGITAL_ZOOM). When a mode is applied, the
> >   pipeline will of course select a sensor mode large enough to
> >   accommodate the largest stream configuration (assuming no
> >   up-scaling) and update this. To me it was implied, but I agree it's
> >   not clear.
> >
> > So, I guess the thing boils down to the fact I want to use this also
> > to report the max digital zoom factor to android, and this is an
> > absolute value, so I envisioned pipelines to initialize this at the
> > smallest selectable sensor mode.
>
> There are a few challenges in doing so, given the above, as the maximum
> zoom factor depends on the camera configuration (it also gets more
> complicated when we consider multiple streams). This requires a bit more
> thinking. I'd be happy to brainstorm this further with you if that can
> help.
>

I think the definition for libcamera only is not worse than what we
had. It depends on the current configurtion indeed, but I don't see it
too much controversial.

For the HAL side, it can't probably be used as it is. It can be worked
around in the HAL (not easily, as static metadata should be reported
even before the camera is acquired) or exposed through a draft
property. Or we can decide to make regular property to allow pipeline
handlers to report their maximum absolute zoom factor if they have any
known and easily computable limits (unfortunately I doubt so).

This apart, I extending this property to make it useful to calculate
the maximum possible zoom factor -per camera configuration- still has
merits.

> > How should we reword this to make it fit for both usages ?
>
> Let's agree on the above first :-)
>
> > > > If a pipeline can up-scale, the minimum
> > > > +        valid crop rectangle is equal to the pixel array portion that produces
> > > > +        the smallest frame size accepted by the ISP input. The rectangle
> > > > +        position, defined by its top-left corner, is not relevant and should be
> > > > +        set to (0, 0) by pipelines.
> > >
> > > Is the last sentence valid for both the non-upscaling and upscaling
> > > cases ? It sounds like the latter, but I suppose it should be the
> > > former. I don't think it's an implementation detail, I'd move it to the
> > > first paragraph that could then be written
> > >
> > >         The maximum value reflects the minimum mandatory cropping applied in the
> > >         camera sensor and the rest of the pipeline. It specifies the boundary
> > >         rectangle within which the ScalerCrop rectangle can be positioned. The
> > >         value is defined relatively to the sensor's active pixel array
> > >         (properties::PixelArrayActiveAreas)/
> > >
> > >         The minimum value reflects the upscaling limit. It results from the
> > >         maximum scaling factor or the minimum scaler input size, and specifies
> > >         the minimum size for the ScalerCrop rectangle. The position of the
> > >         minmum rectangle isn't relevant and shall be set to (0, 0).
> > >
> >
> > ok
> >
> > > > +
> > > > +        The two rectangles are valid only after the camera has been successfully
> > > > +        configured and their value may change whenever a new configuration is
> > > >          applied.
> > > >
> > > > -        \todo Turn this property into a "maximum control value" for the
> > > > -        ScalerCrop control once "dynamic" controls have been implemented.
> > > > +        \todo Express this property as the limits for the ScalerCrop control
> > > > +        once "dynamic" controls have been implemented.
> > > >
> > > >  ...
>
> --
> Regards,
>
> Laurent Pinchart

Patch
diff mbox series

diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml
index a883e27e22e9..44c4bb8e86b6 100644
--- a/src/libcamera/control_ids.yaml
+++ b/src/libcamera/control_ids.yaml
@@ -283,7 +283,7 @@  controls:
         a binning or skipping mode.

         This control is only present when the pipeline supports scaling. Its
-        maximum valid value is given by the properties::ScalerCropMaximum
+        valid value limits are given by the properties::ScalerCropLimits
         property, and the two can be used to implement digital zoom.

   # ----------------------------------------------------------------------------
diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
index 6fcdf557afcf..22d37e7f1ea1 100644
--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
@@ -708,13 +708,19 @@  int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
 		LOG(RPI, Error) << "Failed to configure the IPA: " << ret;

 	/*
-	 * Update the ScalerCropMaximum to the correct value for this camera mode.
-	 * For us, it's the same as the "analogue crop".
+	 * Update the ScalerCropLimits to the correct values for this camera
+	 * mode. For us, it's the same as the "analogue crop" and pixel array
+	 * portion used to produce the ispMinCropSize_;
 	 *
 	 * \todo Make this property the ScalerCrop maximum value when dynamic
 	 * controls are available and set it at validate() time
 	 */
-	data->properties_.set(properties::ScalerCropMaximum, data->sensorInfo_.analogCrop);
+	Rectangle sensorMinCrop =
+		Rectangle(data->ispMinCropSize_).scaledBy(
+				data->sensorInfo_.analogCrop.size(),
+				data->sensorInfo_.outputSize);
+	data->properties_.set(properties::ScalerCropLimits,
+			      { data->sensorInfo_.analogCrop, sensorMinCrop });

 	return ret;
 }
@@ -946,11 +952,12 @@  bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator)
 	data->properties_ = data->sensor_->properties();

 	/*
-	 * Set a default value for the ScalerCropMaximum property to show
+	 * Set default values for the ScalerCropLimits property to show
 	 * that we support its use, however, initialise it to zero because
 	 * it's not meaningful until a camera mode has been chosen.
 	 */
-	data->properties_.set(properties::ScalerCropMaximum, Rectangle{});
+	data->properties_.set(properties::ScalerCropLimits,
+			      { Rectangle{}, Rectangle{} });

 	/*
 	 * We cache three things about the sensor in relation to transforms
diff --git a/src/libcamera/property_ids.yaml b/src/libcamera/property_ids.yaml
index 64e88f0361d6..76474bfc5c31 100644
--- a/src/libcamera/property_ids.yaml
+++ b/src/libcamera/property_ids.yaml
@@ -663,19 +663,41 @@  controls:
         \todo Rename this property to ActiveAreas once we will have property
               categories (i.e. Properties::PixelArray::ActiveAreas)

-  - ScalerCropMaximum:
+  - ScalerCropLimits:
       type: Rectangle
+      size: [2]
       description: |
-        The maximum valid rectangle for the controls::ScalerCrop control. This
-        reflects the minimum mandatory cropping applied in the camera sensor and
-        the rest of the pipeline. Just as the ScalerCrop control, it defines a
-        rectangle taken from the sensor's active pixel array.
-
-        This property is valid only after the camera has been successfully
-        configured and its value may change whenever a new configuration is
+        The maximum and minimum valid rectangles for the controls::ScalerCrop
+        control, specified in this order.
+
+        This two rectangles respectively reflect the minimum and maximum
+        mandatory cropping applied in the camera sensor and the rest of the
+        pipeline. Both Rectangles are defined relatively to the sensor's active
+        pixel array (properties::PixelArrayActiveAreas).
+
+        Implementation details for the maximum valid crop rectangle.
+        The maximum valid crop rectangle depends on the camera configuration as
+        pipelines are free to adjust the frame size requested from the sensor
+        and used to produce the final image streams. The largest possible crop
+        rectangle is then limited by the size of the sensor's active pixel area
+        portion used to produce the sensor output frame.
+
+        Implementation details for the minimum valid crop rectangle. If a
+        pipeline cannot up-scale, the minimum valid crop rectangle is equal to
+        the size of sensor pixel array portion used to produce the smallest
+        available stream resolution, accounting for any applied pixel
+        sub-sampling technique in use and including any border pixels required
+        by the ISP for image processing. If a pipeline can up-scale, the minimum
+        valid crop rectangle is equal to the pixel array portion that produces
+        the smallest frame size accepted by the ISP input. The rectangle
+        position, defined by its top-left corner, is not relevant and should be
+        set to (0, 0) by pipelines.
+
+        The two rectangles are valid only after the camera has been successfully
+        configured and their value may change whenever a new configuration is
         applied.

-        \todo Turn this property into a "maximum control value" for the
-        ScalerCrop control once "dynamic" controls have been implemented.
+        \todo Express this property as the limits for the ScalerCrop control
+        once "dynamic" controls have been implemented.

 ...