[libcamera-devel,RFC,0/4] libcamera: Replace CameraConfiguration::transform
mbox series

Message ID 20230620142904.72600-1-jacopo.mondi@ideasonboard.com
Headers show
Series
  • libcamera: Replace CameraConfiguration::transform
Related show

Message

Jacopo Mondi June 20, 2023, 2:29 p.m. UTC
Hello everyone

We have been discussing rotation/transformations for a long time, and
the currently implemented CameraConfiguration::transform behaviour has
proven to be confusing for several reasons: it was poorly documented,
translating it to something consumable for upper layer frameworks was
difficult and it behaved differently than any other field part of the
CameraConfiguration, as it was meant to tell libcamera "what to do" instead
of expressing what applications want.

For these reasons this series proposes to replace the usage to Transform
in the public API in favour of a new CameraConfiguration::Orientation type,
defined based on the EXIF specification, tag 274, 'orientation'. For reference
this is the same as implemented by gstreamer:
https://gstreamer.freedesktop.org/documentation/gstreamer/gsttaglist.html?gi-language=c#GST_TAG_IMAGE_ORIENTATION

The newly introduced CameraConfiguration::orientation replaces the
existing CameraConfiguration::tranform, and it is meant for application to
express how they would like the images to be oriented, not to tell libcamera
what to do. As an example, passing in 'rotation0' means that the application
expects the images to be rotated upright, and doesn't tell libcamera not to
apply any rotation like passing in "Transform::Identity" did.

The value CameraConfiguration::orientation is set to after a validate() also
differs in meaning, as instead of reporting "what applications have to do
to obtain what they originally asked for" it simply reports the actual
orientation of the stream: this means that if libcamera cannot fully satisfy the
user request it will set ::orientation to report the native images rotation
and the CameraConfiguration::status will be set to Adjusted.

Handling of 90 and 270 degrees rotation has also changed: as the camera sensor
cannot correct rotations that include a transposition, requests for a 90/270
degrees are ignored, and cameras with a mounting rotation of 90/270  degrees
are never re-oriented. This makes it clear and less confusing for applications
that they have to deal with correction fully by themselves. As an example, with
the current implementation if the application requires a Rot270 (HFlip +
Transpose) libcamera will do the HFlip and leave transposition to the upper
layers. There is no clear advantage in doing so, and display compositors are
better suited for handling transpositions and flipping in a single pass instead
of having the library try to handle part of that.

This series clearly breaks the application API as it removes a member from
CameraConfiguration, so it should be introduced probably only when a new release
is cut.

Tested on RPi imx219 with the newly introduced cam option, flip, mirror and
rotation work as expected.

Tested on Pinephone Pro, where sensors are 90/270 degrees rotated (libcamera
performs no correction)

Comments welcome of course.

Thanks
   j

Jacopo Mondi (4):
  libcamera: camera_sensor: Cache rotationTransform_
  libcamera: camera: Introduce CameraConfiguration::orientation
  libcamera: Move to use CameraConfiguration::orientation
  apsp: cam: Add option to set stream orientation

 Documentation/Doxyfile.in                     |   2 +
 Documentation/rotation/flip-rotate-0.eps      | 170 +++++++++++++++
 Documentation/rotation/flip-rotate-0.png      | Bin 0 -> 16488 bytes
 Documentation/rotation/flip-rotate-180.eps    | 191 +++++++++++++++++
 Documentation/rotation/flip-rotate-180.png    | Bin 0 -> 22198 bytes
 Documentation/rotation/flip-rotate-270.eps    | 197 ++++++++++++++++++
 Documentation/rotation/flip-rotate-270.png    | Bin 0 -> 21367 bytes
 Documentation/rotation/flip-rotate-90.eps     | 195 +++++++++++++++++
 Documentation/rotation/flip-rotate-90.png     | Bin 0 -> 21020 bytes
 Documentation/rotation/rotate-0.eps           | 169 +++++++++++++++
 Documentation/rotation/rotate-0.png           | Bin 0 -> 9086 bytes
 Documentation/rotation/rotate-180.eps         | 189 +++++++++++++++++
 Documentation/rotation/rotate-180.png         | Bin 0 -> 22182 bytes
 Documentation/rotation/rotate-270.eps         | 196 +++++++++++++++++
 Documentation/rotation/rotate-270.png         | Bin 0 -> 21016 bytes
 Documentation/rotation/rotate-90.eps          | 196 +++++++++++++++++
 Documentation/rotation/rotate-90.png          | Bin 0 -> 21528 bytes
 include/libcamera/camera.h                    |  14 +-
 include/libcamera/internal/camera_sensor.h    |   4 +-
 include/libcamera/transform.h                 |   4 +
 src/apps/cam/camera_session.cpp               |  17 ++
 src/apps/cam/main.cpp                         |   5 +
 src/apps/cam/main.h                           |   1 +
 src/libcamera/camera.cpp                      |  54 ++++-
 src/libcamera/camera_sensor.cpp               | 137 ++++++------
 src/libcamera/pipeline/ipu3/ipu3.cpp          |   6 +-
 src/libcamera/pipeline/rkisp1/rkisp1.cpp      |   8 +-
 .../pipeline/rpi/common/pipeline_base.cpp     |   9 +-
 src/libcamera/pipeline/simple/simple.cpp      |   6 +-
 src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |   4 +-
 src/libcamera/pipeline/vimc/vimc.cpp          |   4 +-
 src/libcamera/transform.cpp                   |  58 ++++++
 32 files changed, 1739 insertions(+), 97 deletions(-)
 create mode 100644 Documentation/rotation/flip-rotate-0.eps
 create mode 100644 Documentation/rotation/flip-rotate-0.png
 create mode 100644 Documentation/rotation/flip-rotate-180.eps
 create mode 100644 Documentation/rotation/flip-rotate-180.png
 create mode 100644 Documentation/rotation/flip-rotate-270.eps
 create mode 100644 Documentation/rotation/flip-rotate-270.png
 create mode 100644 Documentation/rotation/flip-rotate-90.eps
 create mode 100644 Documentation/rotation/flip-rotate-90.png
 create mode 100644 Documentation/rotation/rotate-0.eps
 create mode 100644 Documentation/rotation/rotate-0.png
 create mode 100644 Documentation/rotation/rotate-180.eps
 create mode 100644 Documentation/rotation/rotate-180.png
 create mode 100644 Documentation/rotation/rotate-270.eps
 create mode 100644 Documentation/rotation/rotate-270.png
 create mode 100644 Documentation/rotation/rotate-90.eps
 create mode 100644 Documentation/rotation/rotate-90.png

--
2.40.1

Comments

David Plowman June 21, 2023, 9:08 a.m. UTC | #1
Hi Jacopo

Thanks for sending this proposal. I wasn't sure I really understood
it, perhaps you can help me out!

On Tue, 20 Jun 2023 at 15:29, Jacopo Mondi
<jacopo.mondi@ideasonboard.com> wrote:
>
> Hello everyone
>
> We have been discussing rotation/transformations for a long time, and
> the currently implemented CameraConfiguration::transform behaviour has
> proven to be confusing for several reasons: it was poorly documented,
> translating it to something consumable for upper layer frameworks was
> difficult and it behaved differently than any other field part of the
> CameraConfiguration, as it was meant to tell libcamera "what to do" instead
> of expressing what applications want.

As far as I understand it, I don't believe this is correct, and the
transform is where you say what you want.

For example, on a Raspberry Pi I always set the transform to
Transform::Identity because I want the images "the right way up". (I
know that internally, it will calculate and apply a 180 rotation to
counteract the sensor mounting rotation.) Does that sound right?

If the problem is actually to do with documentation, I would always
agree that it can be improved, and am very happy to help!

>
> For these reasons this series proposes to replace the usage to Transform
> in the public API in favour of a new CameraConfiguration::Orientation type,
> defined based on the EXIF specification, tag 274, 'orientation'. For reference
> this is the same as implemented by gstreamer:
> https://gstreamer.freedesktop.org/documentation/gstreamer/gsttaglist.html?gi-language=c#GST_TAG_IMAGE_ORIENTATION

I must confess to not liking the term "orientation". There is such a
thing as a "2D plane transformation" and I'd expect to refer to them
as transformations or maybe transforms for short. I don't understand
what an "orientation" is without reading extra documentation. Are
"orientations" composable? Is there such a thing as an "inverse
orientation"?

>
> The newly introduced CameraConfiguration::orientation replaces the
> existing CameraConfiguration::tranform, and it is meant for application to
> express how they would like the images to be oriented, not to tell libcamera
> what to do. As an example, passing in 'rotation0' means that the application
> expects the images to be rotated upright, and doesn't tell libcamera not to
> apply any rotation like passing in "Transform::Identity" did.

My understanding is that passing "Transform::Identity" has always
meant that the application wants its images "upright". (Are there
cases where this doesn't happen?)

>
> The value CameraConfiguration::orientation is set to after a validate() also
> differs in meaning, as instead of reporting "what applications have to do
> to obtain what they originally asked for" it simply reports the actual
> orientation of the stream: this means that if libcamera cannot fully satisfy the
> user request it will set ::orientation to report the native images rotation
> and the CameraConfiguration::status will be set to Adjusted.

I believe this to be the current behaviour of the transform field.

>
> Handling of 90 and 270 degrees rotation has also changed: as the camera sensor
> cannot correct rotations that include a transposition, requests for a 90/270
> degrees are ignored, and cameras with a mounting rotation of 90/270  degrees
> are never re-oriented. This makes it clear and less confusing for applications
> that they have to deal with correction fully by themselves. As an example, with
> the current implementation if the application requires a Rot270 (HFlip +
> Transpose) libcamera will do the HFlip and leave transposition to the upper
> layers. There is no clear advantage in doing so, and display compositors are
> better suited for handling transpositions and flipping in a single pass instead
> of having the library try to handle part of that.

Again, I'm not really understanding this.

The current behaviour is such that, if it can't give you the
transformation that you ask for (on account of not being able to do
transposition), then it guarantees to give you what you requested
_apart_ from the transposition. So the application only has one case
to deal with - applying a transpose. It doesn't have to worry about
the other 3 "transforms-that-I-can't-handle", thereby making life
easier for the application.

Because transforms are composable, it's easy to calculate what
transform to ask for to get a different behaviour (for example, do
nothing at all if there's a transpose involved). I could imagine a
slightly different API might be helpful, perhaps where you get to say
not only "what you want", but also what you want the "residual
transform" to be (being the one the application has to apply after) if
it can't deliver what you wanted. Would that be useful?

I also recall that there was some discussion a while back about the
precise orientation of the rotations which could affect some of the
details, but I don't think it was ever concluded. I don't know if
that's causing any problems?

>
> This series clearly breaks the application API as it removes a member from
> CameraConfiguration, so it should be introduced probably only when a new release
> is cut.

I can't comment on this as I haven't really understood the change
here. Obviously we have many end users for whom an API change would be
unwelcome, but I suppose the change could be hidden from them.

I apologise if there have been some discussions flying around that I
haven't followed sufficiently. I'm afraid I don't normally study
gstreamer related posts in detail, perhaps I need to try harder...

Thanks
David

>
> Tested on RPi imx219 with the newly introduced cam option, flip, mirror and
> rotation work as expected.
>
> Tested on Pinephone Pro, where sensors are 90/270 degrees rotated (libcamera
> performs no correction)
>
> Comments welcome of course.
>
> Thanks
>    j
>
> Jacopo Mondi (4):
>   libcamera: camera_sensor: Cache rotationTransform_
>   libcamera: camera: Introduce CameraConfiguration::orientation
>   libcamera: Move to use CameraConfiguration::orientation
>   apsp: cam: Add option to set stream orientation
>
>  Documentation/Doxyfile.in                     |   2 +
>  Documentation/rotation/flip-rotate-0.eps      | 170 +++++++++++++++
>  Documentation/rotation/flip-rotate-0.png      | Bin 0 -> 16488 bytes
>  Documentation/rotation/flip-rotate-180.eps    | 191 +++++++++++++++++
>  Documentation/rotation/flip-rotate-180.png    | Bin 0 -> 22198 bytes
>  Documentation/rotation/flip-rotate-270.eps    | 197 ++++++++++++++++++
>  Documentation/rotation/flip-rotate-270.png    | Bin 0 -> 21367 bytes
>  Documentation/rotation/flip-rotate-90.eps     | 195 +++++++++++++++++
>  Documentation/rotation/flip-rotate-90.png     | Bin 0 -> 21020 bytes
>  Documentation/rotation/rotate-0.eps           | 169 +++++++++++++++
>  Documentation/rotation/rotate-0.png           | Bin 0 -> 9086 bytes
>  Documentation/rotation/rotate-180.eps         | 189 +++++++++++++++++
>  Documentation/rotation/rotate-180.png         | Bin 0 -> 22182 bytes
>  Documentation/rotation/rotate-270.eps         | 196 +++++++++++++++++
>  Documentation/rotation/rotate-270.png         | Bin 0 -> 21016 bytes
>  Documentation/rotation/rotate-90.eps          | 196 +++++++++++++++++
>  Documentation/rotation/rotate-90.png          | Bin 0 -> 21528 bytes
>  include/libcamera/camera.h                    |  14 +-
>  include/libcamera/internal/camera_sensor.h    |   4 +-
>  include/libcamera/transform.h                 |   4 +
>  src/apps/cam/camera_session.cpp               |  17 ++
>  src/apps/cam/main.cpp                         |   5 +
>  src/apps/cam/main.h                           |   1 +
>  src/libcamera/camera.cpp                      |  54 ++++-
>  src/libcamera/camera_sensor.cpp               | 137 ++++++------
>  src/libcamera/pipeline/ipu3/ipu3.cpp          |   6 +-
>  src/libcamera/pipeline/rkisp1/rkisp1.cpp      |   8 +-
>  .../pipeline/rpi/common/pipeline_base.cpp     |   9 +-
>  src/libcamera/pipeline/simple/simple.cpp      |   6 +-
>  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |   4 +-
>  src/libcamera/pipeline/vimc/vimc.cpp          |   4 +-
>  src/libcamera/transform.cpp                   |  58 ++++++
>  32 files changed, 1739 insertions(+), 97 deletions(-)
>  create mode 100644 Documentation/rotation/flip-rotate-0.eps
>  create mode 100644 Documentation/rotation/flip-rotate-0.png
>  create mode 100644 Documentation/rotation/flip-rotate-180.eps
>  create mode 100644 Documentation/rotation/flip-rotate-180.png
>  create mode 100644 Documentation/rotation/flip-rotate-270.eps
>  create mode 100644 Documentation/rotation/flip-rotate-270.png
>  create mode 100644 Documentation/rotation/flip-rotate-90.eps
>  create mode 100644 Documentation/rotation/flip-rotate-90.png
>  create mode 100644 Documentation/rotation/rotate-0.eps
>  create mode 100644 Documentation/rotation/rotate-0.png
>  create mode 100644 Documentation/rotation/rotate-180.eps
>  create mode 100644 Documentation/rotation/rotate-180.png
>  create mode 100644 Documentation/rotation/rotate-270.eps
>  create mode 100644 Documentation/rotation/rotate-270.png
>  create mode 100644 Documentation/rotation/rotate-90.eps
>  create mode 100644 Documentation/rotation/rotate-90.png
>
> --
> 2.40.1
>
Jacopo Mondi June 21, 2023, 9:23 a.m. UTC | #2
Hi David

On Wed, Jun 21, 2023 at 10:08:25AM +0100, David Plowman wrote:
> Hi Jacopo
>
> Thanks for sending this proposal. I wasn't sure I really understood
> it, perhaps you can help me out!
>
> On Tue, 20 Jun 2023 at 15:29, Jacopo Mondi
> <jacopo.mondi@ideasonboard.com> wrote:
> >
> > Hello everyone
> >
> > We have been discussing rotation/transformations for a long time, and
> > the currently implemented CameraConfiguration::transform behaviour has
> > proven to be confusing for several reasons: it was poorly documented,
> > translating it to something consumable for upper layer frameworks was
> > difficult and it behaved differently than any other field part of the
> > CameraConfiguration, as it was meant to tell libcamera "what to do" instead
> > of expressing what applications want.
>
> As far as I understand it, I don't believe this is correct, and the
> transform is where you say what you want.
>
> For example, on a Raspberry Pi I always set the transform to
> Transform::Identity because I want the images "the right way up". (I
> know that internally, it will calculate and apply a 180 rotation to
> counteract the sensor mounting rotation.) Does that sound right?

Yes, now that we correct properties::Rotation to 0 as the library
compensate the mounting rotation behind your back, that's what you
get.

Before we had properties::Rotation = 180; transform = Identity and you
got an upright image. Confusing, isn't it ?

>
> If the problem is actually to do with documentation, I would always
> agree that it can be improved, and am very happy to help!
>
> >
> > For these reasons this series proposes to replace the usage to Transform
> > in the public API in favour of a new CameraConfiguration::Orientation type,
> > defined based on the EXIF specification, tag 274, 'orientation'. For reference
> > this is the same as implemented by gstreamer:
> > https://gstreamer.freedesktop.org/documentation/gstreamer/gsttaglist.html?gi-language=c#GST_TAG_IMAGE_ORIENTATION
>
> I must confess to not liking the term "orientation". There is such a
> thing as a "2D plane transformation" and I'd expect to refer to them
> as transformations or maybe transforms for short. I don't understand
> what an "orientation" is without reading extra documentation. Are
> "orientations" composable? Is there such a thing as an "inverse
> orientation"?
>

I'm fine bikeshedding on the name. What matters to me is that now we
conform to an existing specification adopeted by most frameworks
instead of defining our own Transform.

> >
> > The newly introduced CameraConfiguration::orientation replaces the
> > existing CameraConfiguration::tranform, and it is meant for application to
> > express how they would like the images to be oriented, not to tell libcamera
> > what to do. As an example, passing in 'rotation0' means that the application
> > expects the images to be rotated upright, and doesn't tell libcamera not to
> > apply any rotation like passing in "Transform::Identity" did.
>
> My understanding is that passing "Transform::Identity" has always
> meant that the application wants its images "upright". (Are there
> cases where this doesn't happen?)
>

As we have auto-correction in place, yes.

> >
> > The value CameraConfiguration::orientation is set to after a validate() also
> > differs in meaning, as instead of reporting "what applications have to do
> > to obtain what they originally asked for" it simply reports the actual
> > orientation of the stream: this means that if libcamera cannot fully satisfy the
> > user request it will set ::orientation to report the native images rotation
> > and the CameraConfiguration::status will be set to Adjusted.
>
> I believe this to be the current behaviour of the transform field.
>

Not really, see below

> >
> > Handling of 90 and 270 degrees rotation has also changed: as the camera sensor
> > cannot correct rotations that include a transposition, requests for a 90/270
> > degrees are ignored, and cameras with a mounting rotation of 90/270  degrees
> > are never re-oriented. This makes it clear and less confusing for applications
> > that they have to deal with correction fully by themselves. As an example, with
> > the current implementation if the application requires a Rot270 (HFlip +
> > Transpose) libcamera will do the HFlip and leave transposition to the upper
> > layers. There is no clear advantage in doing so, and display compositors are
> > better suited for handling transpositions and flipping in a single pass instead
> > of having the library try to handle part of that.
>
> Again, I'm not really understanding this.
>
> The current behaviour is such that, if it can't give you the
> transformation that you ask for (on account of not being able to do
> transposition), then it guarantees to give you what you requested
> _apart_ from the transposition. So the application only has one case
> to deal with - applying a transpose. It doesn't have to worry about
> the other 3 "transforms-that-I-can't-handle", thereby making life
> easier for the application.

The _apart_ is the key here.

If you currently ask for Rot270 the library will do an HFlip and set
transform to Transpose (ie what applications still have to do to
obtain Rot270). This is not how the other fields of
CameraConfiguration work, and mostly, applying a flip and letting apps
only deal with transpose is an "optimization" that might actually make
life harder for apps, as display compositors usually have means to
deal with known rotations, while in this case you only ask them to do
a Transpose, which might require them more work ?

In any case, I think it's saner if the library cannot do what it has
been asked for to set ::transform to what the stream orientation
actually is, instead of telling apps what "they still have to do".

>
> Because transforms are composable, it's easy to calculate what
> transform to ask for to get a different behaviour (for example, do
> nothing at all if there's a transpose involved). I could imagine a
> slightly different API might be helpful, perhaps where you get to say
> not only "what you want", but also what you want the "residual
> transform" to be (being the one the application has to apply after) if
> it can't deliver what you wanted. Would that be useful?
>

I think it's better to leave the stream orientation unmodified if it
cannot be corrected. This matches the behaviour of the other
CameraConfiguration fields, and seems easier to deal with from
applications.

> I also recall that there was some discussion a while back about the
> precise orientation of the rotations which could affect some of the
> details, but I don't think it was ever concluded. I don't know if
> that's causing any problems?

Sorry, I don't remember such discussion. Any link ?

>
> >
> > This series clearly breaks the application API as it removes a member from
> > CameraConfiguration, so it should be introduced probably only when a new release
> > is cut.
>
> I can't comment on this as I haven't really understood the change
> here. Obviously we have many end users for whom an API change would be
> unwelcome, but I suppose the change could be hidden from them.
>
> I apologise if there have been some discussions flying around that I
> haven't followed sufficiently. I'm afraid I don't normally study
> gstreamer related posts in detail, perhaps I need to try harder...
>
> Thanks
> David
>
> >
> > Tested on RPi imx219 with the newly introduced cam option, flip, mirror and
> > rotation work as expected.
> >
> > Tested on Pinephone Pro, where sensors are 90/270 degrees rotated (libcamera
> > performs no correction)
> >
> > Comments welcome of course.
> >
> > Thanks
> >    j
> >
> > Jacopo Mondi (4):
> >   libcamera: camera_sensor: Cache rotationTransform_
> >   libcamera: camera: Introduce CameraConfiguration::orientation
> >   libcamera: Move to use CameraConfiguration::orientation
> >   apsp: cam: Add option to set stream orientation
> >
> >  Documentation/Doxyfile.in                     |   2 +
> >  Documentation/rotation/flip-rotate-0.eps      | 170 +++++++++++++++
> >  Documentation/rotation/flip-rotate-0.png      | Bin 0 -> 16488 bytes
> >  Documentation/rotation/flip-rotate-180.eps    | 191 +++++++++++++++++
> >  Documentation/rotation/flip-rotate-180.png    | Bin 0 -> 22198 bytes
> >  Documentation/rotation/flip-rotate-270.eps    | 197 ++++++++++++++++++
> >  Documentation/rotation/flip-rotate-270.png    | Bin 0 -> 21367 bytes
> >  Documentation/rotation/flip-rotate-90.eps     | 195 +++++++++++++++++
> >  Documentation/rotation/flip-rotate-90.png     | Bin 0 -> 21020 bytes
> >  Documentation/rotation/rotate-0.eps           | 169 +++++++++++++++
> >  Documentation/rotation/rotate-0.png           | Bin 0 -> 9086 bytes
> >  Documentation/rotation/rotate-180.eps         | 189 +++++++++++++++++
> >  Documentation/rotation/rotate-180.png         | Bin 0 -> 22182 bytes
> >  Documentation/rotation/rotate-270.eps         | 196 +++++++++++++++++
> >  Documentation/rotation/rotate-270.png         | Bin 0 -> 21016 bytes
> >  Documentation/rotation/rotate-90.eps          | 196 +++++++++++++++++
> >  Documentation/rotation/rotate-90.png          | Bin 0 -> 21528 bytes
> >  include/libcamera/camera.h                    |  14 +-
> >  include/libcamera/internal/camera_sensor.h    |   4 +-
> >  include/libcamera/transform.h                 |   4 +
> >  src/apps/cam/camera_session.cpp               |  17 ++
> >  src/apps/cam/main.cpp                         |   5 +
> >  src/apps/cam/main.h                           |   1 +
> >  src/libcamera/camera.cpp                      |  54 ++++-
> >  src/libcamera/camera_sensor.cpp               | 137 ++++++------
> >  src/libcamera/pipeline/ipu3/ipu3.cpp          |   6 +-
> >  src/libcamera/pipeline/rkisp1/rkisp1.cpp      |   8 +-
> >  .../pipeline/rpi/common/pipeline_base.cpp     |   9 +-
> >  src/libcamera/pipeline/simple/simple.cpp      |   6 +-
> >  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |   4 +-
> >  src/libcamera/pipeline/vimc/vimc.cpp          |   4 +-
> >  src/libcamera/transform.cpp                   |  58 ++++++
> >  32 files changed, 1739 insertions(+), 97 deletions(-)
> >  create mode 100644 Documentation/rotation/flip-rotate-0.eps
> >  create mode 100644 Documentation/rotation/flip-rotate-0.png
> >  create mode 100644 Documentation/rotation/flip-rotate-180.eps
> >  create mode 100644 Documentation/rotation/flip-rotate-180.png
> >  create mode 100644 Documentation/rotation/flip-rotate-270.eps
> >  create mode 100644 Documentation/rotation/flip-rotate-270.png
> >  create mode 100644 Documentation/rotation/flip-rotate-90.eps
> >  create mode 100644 Documentation/rotation/flip-rotate-90.png
> >  create mode 100644 Documentation/rotation/rotate-0.eps
> >  create mode 100644 Documentation/rotation/rotate-0.png
> >  create mode 100644 Documentation/rotation/rotate-180.eps
> >  create mode 100644 Documentation/rotation/rotate-180.png
> >  create mode 100644 Documentation/rotation/rotate-270.eps
> >  create mode 100644 Documentation/rotation/rotate-270.png
> >  create mode 100644 Documentation/rotation/rotate-90.eps
> >  create mode 100644 Documentation/rotation/rotate-90.png
> >
> > --
> > 2.40.1
> >
David Plowman June 21, 2023, 12:37 p.m. UTC | #3
Hi Jacopo

Thanks for the reply.

On Wed, 21 Jun 2023 at 10:23, Jacopo Mondi
<jacopo.mondi@ideasonboard.com> wrote:
>
> Hi David
>
> On Wed, Jun 21, 2023 at 10:08:25AM +0100, David Plowman wrote:
> > Hi Jacopo
> >
> > Thanks for sending this proposal. I wasn't sure I really understood
> > it, perhaps you can help me out!
> >
> > On Tue, 20 Jun 2023 at 15:29, Jacopo Mondi
> > <jacopo.mondi@ideasonboard.com> wrote:
> > >
> > > Hello everyone
> > >
> > > We have been discussing rotation/transformations for a long time, and
> > > the currently implemented CameraConfiguration::transform behaviour has
> > > proven to be confusing for several reasons: it was poorly documented,
> > > translating it to something consumable for upper layer frameworks was
> > > difficult and it behaved differently than any other field part of the
> > > CameraConfiguration, as it was meant to tell libcamera "what to do" instead
> > > of expressing what applications want.
> >
> > As far as I understand it, I don't believe this is correct, and the
> > transform is where you say what you want.
> >
> > For example, on a Raspberry Pi I always set the transform to
> > Transform::Identity because I want the images "the right way up". (I
> > know that internally, it will calculate and apply a 180 rotation to
> > counteract the sensor mounting rotation.) Does that sound right?
>
> Yes, now that we correct properties::Rotation to 0 as the library
> compensate the mounting rotation behind your back, that's what you
> get.
>
> Before we had properties::Rotation = 180; transform = Identity and you
> got an upright image. Confusing, isn't it ?

I understood the previous arrangement, but the new one seems
counter-intuitive to me.

On a Raspberry Pi, I expect the properties::Rotation to report 180
degrees because the sensor is mounted upside down. Then I can ask for
Transform::Identity and internally a 180 degree rotation will be
applied to give me what I asked for. I understand this.

Are we saying that now the rotation will report 0 degrees? Will it now
always report 0 degrees, whatever the sensor and platform? That would
obviously break things...

>
> >
> > If the problem is actually to do with documentation, I would always
> > agree that it can be improved, and am very happy to help!
> >
> > >
> > > For these reasons this series proposes to replace the usage to Transform
> > > in the public API in favour of a new CameraConfiguration::Orientation type,
> > > defined based on the EXIF specification, tag 274, 'orientation'. For reference
> > > this is the same as implemented by gstreamer:
> > > https://gstreamer.freedesktop.org/documentation/gstreamer/gsttaglist.html?gi-language=c#GST_TAG_IMAGE_ORIENTATION
> >
> > I must confess to not liking the term "orientation". There is such a
> > thing as a "2D plane transformation" and I'd expect to refer to them
> > as transformations or maybe transforms for short. I don't understand
> > what an "orientation" is without reading extra documentation. Are
> > "orientations" composable? Is there such a thing as an "inverse
> > orientation"?
> >
>
> I'm fine bikeshedding on the name. What matters to me is that now we
> conform to an existing specification adopeted by most frameworks
> instead of defining our own Transform.

I'd like to find something that works for all libcamera users.

The Transform class allows composition, inverse operations and so on,
the idea being that users can calculate for themselves what to ask for
if the default behaviour isn't what they want. I did comment that
making it easier for folks to get a specific behaviour might be
something to look into.

>
> > >
> > > The newly introduced CameraConfiguration::orientation replaces the
> > > existing CameraConfiguration::tranform, and it is meant for application to
> > > express how they would like the images to be oriented, not to tell libcamera
> > > what to do. As an example, passing in 'rotation0' means that the application
> > > expects the images to be rotated upright, and doesn't tell libcamera not to
> > > apply any rotation like passing in "Transform::Identity" did.
> >
> > My understanding is that passing "Transform::Identity" has always
> > meant that the application wants its images "upright". (Are there
> > cases where this doesn't happen?)
> >
>
> As we have auto-correction in place, yes.

My understanding is that everything got "auto-corrected" before. I'm
still struggling to understand how it seems to be broken now.

>
> > >
> > > The value CameraConfiguration::orientation is set to after a validate() also
> > > differs in meaning, as instead of reporting "what applications have to do
> > > to obtain what they originally asked for" it simply reports the actual
> > > orientation of the stream: this means that if libcamera cannot fully satisfy the
> > > user request it will set ::orientation to report the native images rotation
> > > and the CameraConfiguration::status will be set to Adjusted.
> >
> > I believe this to be the current behaviour of the transform field.
> >
>
> Not really, see below
>
> > >
> > > Handling of 90 and 270 degrees rotation has also changed: as the camera sensor
> > > cannot correct rotations that include a transposition, requests for a 90/270
> > > degrees are ignored, and cameras with a mounting rotation of 90/270  degrees
> > > are never re-oriented. This makes it clear and less confusing for applications
> > > that they have to deal with correction fully by themselves. As an example, with
> > > the current implementation if the application requires a Rot270 (HFlip +
> > > Transpose) libcamera will do the HFlip and leave transposition to the upper
> > > layers. There is no clear advantage in doing so, and display compositors are
> > > better suited for handling transpositions and flipping in a single pass instead
> > > of having the library try to handle part of that.
> >
> > Again, I'm not really understanding this.
> >
> > The current behaviour is such that, if it can't give you the
> > transformation that you ask for (on account of not being able to do
> > transposition), then it guarantees to give you what you requested
> > _apart_ from the transposition. So the application only has one case
> > to deal with - applying a transpose. It doesn't have to worry about
> > the other 3 "transforms-that-I-can't-handle", thereby making life
> > easier for the application.
>
> The _apart_ is the key here.
>
> If you currently ask for Rot270 the library will do an HFlip and set
> transform to Transpose (ie what applications still have to do to
> obtain Rot270). This is not how the other fields of

What it _should_ do is set the transform field to the actual transform
that the user will perceive the images to have. I believe this to be
"proper" libcamera configuration behaviour. I'm not understanding why
things are different now.

Beyond this, the current behaviour is to leave the application to do
the transpose, but that's just a choice. The point of transforms is
that it's easy to use them to produce something different if you want.
I don't mind looking at that if we can make it easier for everyone to
get their preferred behaviour, which I suggested in my original reply.

> CameraConfiguration work, and mostly, applying a flip and letting apps
> only deal with transpose is an "optimization" that might actually make
> life harder for apps, as display compositors usually have means to
> deal with known rotations, while in this case you only ask them to do
> a Transpose, which might require them more work ?
>
> In any case, I think it's saner if the library cannot do what it has
> been asked for to set ::transform to what the stream orientation
> actually is, instead of telling apps what "they still have to do".

I don't understand the repeated statement that the transform is
telling apps "what they still have to do". That was never what it
meant, unless it got changed somewhere.

>
> >
> > Because transforms are composable, it's easy to calculate what
> > transform to ask for to get a different behaviour (for example, do
> > nothing at all if there's a transpose involved). I could imagine a
> > slightly different API might be helpful, perhaps where you get to say
> > not only "what you want", but also what you want the "residual
> > transform" to be (being the one the application has to apply after) if
> > it can't deliver what you wanted. Would that be useful?
> >
>
> I think it's better to leave the stream orientation unmodified if it
> cannot be corrected. This matches the behaviour of the other
> CameraConfiguration fields, and seems easier to deal with from
> applications.

I feel we ought to be able to come up with something that makes it
convenient for everyone to get what they want.

>
> > I also recall that there was some discussion a while back about the
> > precise orientation of the rotations which could affect some of the
> > details, but I don't think it was ever concluded. I don't know if
> > that's causing any problems?
>
> Sorry, I don't remember such discussion. Any link ?

I think the last email I sent on the subject was this one, after which
things went quiet:
https://lists.libcamera.org/pipermail/libcamera-devel/2023-January/036445.html
Actually it seems to echo quite a bit of what I say above.

Thanks
David

>
> >
> > >
> > > This series clearly breaks the application API as it removes a member from
> > > CameraConfiguration, so it should be introduced probably only when a new release
> > > is cut.
> >
> > I can't comment on this as I haven't really understood the change
> > here. Obviously we have many end users for whom an API change would be
> > unwelcome, but I suppose the change could be hidden from them.
> >
> > I apologise if there have been some discussions flying around that I
> > haven't followed sufficiently. I'm afraid I don't normally study
> > gstreamer related posts in detail, perhaps I need to try harder...
> >
> > Thanks
> > David
> >
> > >
> > > Tested on RPi imx219 with the newly introduced cam option, flip, mirror and
> > > rotation work as expected.
> > >
> > > Tested on Pinephone Pro, where sensors are 90/270 degrees rotated (libcamera
> > > performs no correction)
> > >
> > > Comments welcome of course.
> > >
> > > Thanks
> > >    j
> > >
> > > Jacopo Mondi (4):
> > >   libcamera: camera_sensor: Cache rotationTransform_
> > >   libcamera: camera: Introduce CameraConfiguration::orientation
> > >   libcamera: Move to use CameraConfiguration::orientation
> > >   apsp: cam: Add option to set stream orientation
> > >
> > >  Documentation/Doxyfile.in                     |   2 +
> > >  Documentation/rotation/flip-rotate-0.eps      | 170 +++++++++++++++
> > >  Documentation/rotation/flip-rotate-0.png      | Bin 0 -> 16488 bytes
> > >  Documentation/rotation/flip-rotate-180.eps    | 191 +++++++++++++++++
> > >  Documentation/rotation/flip-rotate-180.png    | Bin 0 -> 22198 bytes
> > >  Documentation/rotation/flip-rotate-270.eps    | 197 ++++++++++++++++++
> > >  Documentation/rotation/flip-rotate-270.png    | Bin 0 -> 21367 bytes
> > >  Documentation/rotation/flip-rotate-90.eps     | 195 +++++++++++++++++
> > >  Documentation/rotation/flip-rotate-90.png     | Bin 0 -> 21020 bytes
> > >  Documentation/rotation/rotate-0.eps           | 169 +++++++++++++++
> > >  Documentation/rotation/rotate-0.png           | Bin 0 -> 9086 bytes
> > >  Documentation/rotation/rotate-180.eps         | 189 +++++++++++++++++
> > >  Documentation/rotation/rotate-180.png         | Bin 0 -> 22182 bytes
> > >  Documentation/rotation/rotate-270.eps         | 196 +++++++++++++++++
> > >  Documentation/rotation/rotate-270.png         | Bin 0 -> 21016 bytes
> > >  Documentation/rotation/rotate-90.eps          | 196 +++++++++++++++++
> > >  Documentation/rotation/rotate-90.png          | Bin 0 -> 21528 bytes
> > >  include/libcamera/camera.h                    |  14 +-
> > >  include/libcamera/internal/camera_sensor.h    |   4 +-
> > >  include/libcamera/transform.h                 |   4 +
> > >  src/apps/cam/camera_session.cpp               |  17 ++
> > >  src/apps/cam/main.cpp                         |   5 +
> > >  src/apps/cam/main.h                           |   1 +
> > >  src/libcamera/camera.cpp                      |  54 ++++-
> > >  src/libcamera/camera_sensor.cpp               | 137 ++++++------
> > >  src/libcamera/pipeline/ipu3/ipu3.cpp          |   6 +-
> > >  src/libcamera/pipeline/rkisp1/rkisp1.cpp      |   8 +-
> > >  .../pipeline/rpi/common/pipeline_base.cpp     |   9 +-
> > >  src/libcamera/pipeline/simple/simple.cpp      |   6 +-
> > >  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |   4 +-
> > >  src/libcamera/pipeline/vimc/vimc.cpp          |   4 +-
> > >  src/libcamera/transform.cpp                   |  58 ++++++
> > >  32 files changed, 1739 insertions(+), 97 deletions(-)
> > >  create mode 100644 Documentation/rotation/flip-rotate-0.eps
> > >  create mode 100644 Documentation/rotation/flip-rotate-0.png
> > >  create mode 100644 Documentation/rotation/flip-rotate-180.eps
> > >  create mode 100644 Documentation/rotation/flip-rotate-180.png
> > >  create mode 100644 Documentation/rotation/flip-rotate-270.eps
> > >  create mode 100644 Documentation/rotation/flip-rotate-270.png
> > >  create mode 100644 Documentation/rotation/flip-rotate-90.eps
> > >  create mode 100644 Documentation/rotation/flip-rotate-90.png
> > >  create mode 100644 Documentation/rotation/rotate-0.eps
> > >  create mode 100644 Documentation/rotation/rotate-0.png
> > >  create mode 100644 Documentation/rotation/rotate-180.eps
> > >  create mode 100644 Documentation/rotation/rotate-180.png
> > >  create mode 100644 Documentation/rotation/rotate-270.eps
> > >  create mode 100644 Documentation/rotation/rotate-270.png
> > >  create mode 100644 Documentation/rotation/rotate-90.eps
> > >  create mode 100644 Documentation/rotation/rotate-90.png
> > >
> > > --
> > > 2.40.1
> > >
Jacopo Mondi June 21, 2023, 12:57 p.m. UTC | #4
Hi David

On Wed, Jun 21, 2023 at 01:37:11PM +0100, David Plowman wrote:
> Hi Jacopo
>
> Thanks for the reply.
>
> On Wed, 21 Jun 2023 at 10:23, Jacopo Mondi
> <jacopo.mondi@ideasonboard.com> wrote:
> >
> > Hi David
> >
> > On Wed, Jun 21, 2023 at 10:08:25AM +0100, David Plowman wrote:
> > > Hi Jacopo
> > >
> > > Thanks for sending this proposal. I wasn't sure I really understood
> > > it, perhaps you can help me out!
> > >
> > > On Tue, 20 Jun 2023 at 15:29, Jacopo Mondi
> > > <jacopo.mondi@ideasonboard.com> wrote:
> > > >
> > > > Hello everyone
> > > >
> > > > We have been discussing rotation/transformations for a long time, and
> > > > the currently implemented CameraConfiguration::transform behaviour has
> > > > proven to be confusing for several reasons: it was poorly documented,
> > > > translating it to something consumable for upper layer frameworks was
> > > > difficult and it behaved differently than any other field part of the
> > > > CameraConfiguration, as it was meant to tell libcamera "what to do" instead
> > > > of expressing what applications want.
> > >
> > > As far as I understand it, I don't believe this is correct, and the
> > > transform is where you say what you want.
> > >
> > > For example, on a Raspberry Pi I always set the transform to
> > > Transform::Identity because I want the images "the right way up". (I
> > > know that internally, it will calculate and apply a 180 rotation to
> > > counteract the sensor mounting rotation.) Does that sound right?
> >
> > Yes, now that we correct properties::Rotation to 0 as the library
> > compensate the mounting rotation behind your back, that's what you
> > get.
> >
> > Before we had properties::Rotation = 180; transform = Identity and you
> > got an upright image. Confusing, isn't it ?
>
> I understood the previous arrangement, but the new one seems
> counter-intuitive to me.
>
> On a Raspberry Pi, I expect the properties::Rotation to report 180
> degrees because the sensor is mounted upside down. Then I can ask for
> Transform::Identity and internally a 180 degree rotation will be
> applied to give me what I asked for. I understand this.
>
> Are we saying that now the rotation will report 0 degrees? Will it now
> always report 0 degrees, whatever the sensor and platform? That would
> obviously break things...
>

So you're an app, you read properties::Rotation and you get back 180.
Then you star streaming and you get a stream oriented "correctly".

Isn't this wrong ?

> >
> > >
> > > If the problem is actually to do with documentation, I would always
> > > agree that it can be improved, and am very happy to help!
> > >
> > > >
> > > > For these reasons this series proposes to replace the usage to Transform
> > > > in the public API in favour of a new CameraConfiguration::Orientation type,
> > > > defined based on the EXIF specification, tag 274, 'orientation'. For reference
> > > > this is the same as implemented by gstreamer:
> > > > https://gstreamer.freedesktop.org/documentation/gstreamer/gsttaglist.html?gi-language=c#GST_TAG_IMAGE_ORIENTATION
> > >
> > > I must confess to not liking the term "orientation". There is such a
> > > thing as a "2D plane transformation" and I'd expect to refer to them
> > > as transformations or maybe transforms for short. I don't understand
> > > what an "orientation" is without reading extra documentation. Are
> > > "orientations" composable? Is there such a thing as an "inverse
> > > orientation"?
> > >
> >
> > I'm fine bikeshedding on the name. What matters to me is that now we
> > conform to an existing specification adopeted by most frameworks
> > instead of defining our own Transform.
>
> I'd like to find something that works for all libcamera users.
>
> The Transform class allows composition, inverse operations and so on,
> the idea being that users can calculate for themselves what to ask for
> if the default behaviour isn't what they want. I did comment that
> making it easier for folks to get a specific behaviour might be
> something to look into.
>
> >
> > > >
> > > > The newly introduced CameraConfiguration::orientation replaces the
> > > > existing CameraConfiguration::tranform, and it is meant for application to
> > > > express how they would like the images to be oriented, not to tell libcamera
> > > > what to do. As an example, passing in 'rotation0' means that the application
> > > > expects the images to be rotated upright, and doesn't tell libcamera not to
> > > > apply any rotation like passing in "Transform::Identity" did.
> > >
> > > My understanding is that passing "Transform::Identity" has always
> > > meant that the application wants its images "upright". (Are there
> > > cases where this doesn't happen?)
> > >
> >
> > As we have auto-correction in place, yes.
>
> My understanding is that everything got "auto-corrected" before. I'm
> still struggling to understand how it seems to be broken now.
>

It is not 'broken'. Simply nobody got how thing worked in practice and
the existing behaviour (when it comes to adjusting ::transform) is not
what most users expects. See below

> >
> > > >
> > > > The value CameraConfiguration::orientation is set to after a validate() also
> > > > differs in meaning, as instead of reporting "what applications have to do
> > > > to obtain what they originally asked for" it simply reports the actual
> > > > orientation of the stream: this means that if libcamera cannot fully satisfy the
> > > > user request it will set ::orientation to report the native images rotation
> > > > and the CameraConfiguration::status will be set to Adjusted.
> > >
> > > I believe this to be the current behaviour of the transform field.
> > >
> >
> > Not really, see below
> >
> > > >
> > > > Handling of 90 and 270 degrees rotation has also changed: as the camera sensor
> > > > cannot correct rotations that include a transposition, requests for a 90/270
> > > > degrees are ignored, and cameras with a mounting rotation of 90/270  degrees
> > > > are never re-oriented. This makes it clear and less confusing for applications
> > > > that they have to deal with correction fully by themselves. As an example, with
> > > > the current implementation if the application requires a Rot270 (HFlip +
> > > > Transpose) libcamera will do the HFlip and leave transposition to the upper
> > > > layers. There is no clear advantage in doing so, and display compositors are
> > > > better suited for handling transpositions and flipping in a single pass instead
> > > > of having the library try to handle part of that.
> > >
> > > Again, I'm not really understanding this.
> > >
> > > The current behaviour is such that, if it can't give you the
> > > transformation that you ask for (on account of not being able to do
> > > transposition), then it guarantees to give you what you requested
> > > _apart_ from the transposition. So the application only has one case
> > > to deal with - applying a transpose. It doesn't have to worry about
> > > the other 3 "transforms-that-I-can't-handle", thereby making life
> > > easier for the application.
> >
> > The _apart_ is the key here.
> >
> > If you currently ask for Rot270 the library will do an HFlip and set
> > transform to Transpose (ie what applications still have to do to
> > obtain Rot270). This is not how the other fields of
>
> What it _should_ do is set the transform field to the actual transform
> that the user will perceive the images to have. I believe this to be
> "proper" libcamera configuration behaviour. I'm not understanding why
> things are different now.
>

Let's start with the fact that "transpose" is a concept that is
introduced in Transform and it's not part of the EXIF standard which
gstreamer/pipewire expect to use.

Again, you set transform = rot270, libcamera does HFLIP and you have
to do Transpose, correct ?

For compositor this is more work that just do "rot270"

> Beyond this, the current behaviour is to leave the application to do
> the transpose, but that's just a choice. The point of transforms is
> that it's easy to use them to produce something different if you want.
> I don't mind looking at that if we can make it easier for everyone to
> get their preferred behaviour, which I suggested in my original reply.
>

I don't get why we should be imposing other frameworks/apps to learn
Transpose when there is a standard to follow to be honest.

> > CameraConfiguration work, and mostly, applying a flip and letting apps
> > only deal with transpose is an "optimization" that might actually make
> > life harder for apps, as display compositors usually have means to
> > deal with known rotations, while in this case you only ask them to do
> > a Transpose, which might require them more work ?
> >
> > In any case, I think it's saner if the library cannot do what it has
> > been asked for to set ::transform to what the stream orientation
> > actually is, instead of telling apps what "they still have to do".
>
> I don't understand the repeated statement that the transform is
> telling apps "what they still have to do". That was never what it
> meant, unless it got changed somewhere.
>

Isn't it ? I'm not repeating the Rot270 example, but applications are
left with ::transform = Transpose. Isn't this what they have to do to
obtain Rot270 once HFlip has been done by libcamera ?

So they have to "I asked for Rot270, I got back Transpose. Ah!
libcamera did HFlip for me, so now I have to do Transpose."

Is it what happens ?

> >
> > >
> > > Because transforms are composable, it's easy to calculate what
> > > transform to ask for to get a different behaviour (for example, do
> > > nothing at all if there's a transpose involved). I could imagine a
> > > slightly different API might be helpful, perhaps where you get to say
> > > not only "what you want", but also what you want the "residual
> > > transform" to be (being the one the application has to apply after) if
> > > it can't deliver what you wanted. Would that be useful?
> > >
> >
> > I think it's better to leave the stream orientation unmodified if it
> > cannot be corrected. This matches the behaviour of the other
> > CameraConfiguration fields, and seems easier to deal with from
> > applications.
>
> I feel we ought to be able to come up with something that makes it
> convenient for everyone to get what they want.
>
> >
> > > I also recall that there was some discussion a while back about the
> > > precise orientation of the rotations which could affect some of the
> > > details, but I don't think it was ever concluded. I don't know if
> > > that's causing any problems?
> >
> > Sorry, I don't remember such discussion. Any link ?
>
> I think the last email I sent on the subject was this one, after which
> things went quiet:
> https://lists.libcamera.org/pipermail/libcamera-devel/2023-January/036445.html
> Actually it seems to echo quite a bit of what I say above.
>
> Thanks
> David
>
> >
> > >
> > > >
> > > > This series clearly breaks the application API as it removes a member from
> > > > CameraConfiguration, so it should be introduced probably only when a new release
> > > > is cut.
> > >
> > > I can't comment on this as I haven't really understood the change
> > > here. Obviously we have many end users for whom an API change would be
> > > unwelcome, but I suppose the change could be hidden from them.
> > >
> > > I apologise if there have been some discussions flying around that I
> > > haven't followed sufficiently. I'm afraid I don't normally study
> > > gstreamer related posts in detail, perhaps I need to try harder...
> > >
> > > Thanks
> > > David
> > >
> > > >
> > > > Tested on RPi imx219 with the newly introduced cam option, flip, mirror and
> > > > rotation work as expected.
> > > >
> > > > Tested on Pinephone Pro, where sensors are 90/270 degrees rotated (libcamera
> > > > performs no correction)
> > > >
> > > > Comments welcome of course.
> > > >
> > > > Thanks
> > > >    j
> > > >
> > > > Jacopo Mondi (4):
> > > >   libcamera: camera_sensor: Cache rotationTransform_
> > > >   libcamera: camera: Introduce CameraConfiguration::orientation
> > > >   libcamera: Move to use CameraConfiguration::orientation
> > > >   apsp: cam: Add option to set stream orientation
> > > >
> > > >  Documentation/Doxyfile.in                     |   2 +
> > > >  Documentation/rotation/flip-rotate-0.eps      | 170 +++++++++++++++
> > > >  Documentation/rotation/flip-rotate-0.png      | Bin 0 -> 16488 bytes
> > > >  Documentation/rotation/flip-rotate-180.eps    | 191 +++++++++++++++++
> > > >  Documentation/rotation/flip-rotate-180.png    | Bin 0 -> 22198 bytes
> > > >  Documentation/rotation/flip-rotate-270.eps    | 197 ++++++++++++++++++
> > > >  Documentation/rotation/flip-rotate-270.png    | Bin 0 -> 21367 bytes
> > > >  Documentation/rotation/flip-rotate-90.eps     | 195 +++++++++++++++++
> > > >  Documentation/rotation/flip-rotate-90.png     | Bin 0 -> 21020 bytes
> > > >  Documentation/rotation/rotate-0.eps           | 169 +++++++++++++++
> > > >  Documentation/rotation/rotate-0.png           | Bin 0 -> 9086 bytes
> > > >  Documentation/rotation/rotate-180.eps         | 189 +++++++++++++++++
> > > >  Documentation/rotation/rotate-180.png         | Bin 0 -> 22182 bytes
> > > >  Documentation/rotation/rotate-270.eps         | 196 +++++++++++++++++
> > > >  Documentation/rotation/rotate-270.png         | Bin 0 -> 21016 bytes
> > > >  Documentation/rotation/rotate-90.eps          | 196 +++++++++++++++++
> > > >  Documentation/rotation/rotate-90.png          | Bin 0 -> 21528 bytes
> > > >  include/libcamera/camera.h                    |  14 +-
> > > >  include/libcamera/internal/camera_sensor.h    |   4 +-
> > > >  include/libcamera/transform.h                 |   4 +
> > > >  src/apps/cam/camera_session.cpp               |  17 ++
> > > >  src/apps/cam/main.cpp                         |   5 +
> > > >  src/apps/cam/main.h                           |   1 +
> > > >  src/libcamera/camera.cpp                      |  54 ++++-
> > > >  src/libcamera/camera_sensor.cpp               | 137 ++++++------
> > > >  src/libcamera/pipeline/ipu3/ipu3.cpp          |   6 +-
> > > >  src/libcamera/pipeline/rkisp1/rkisp1.cpp      |   8 +-
> > > >  .../pipeline/rpi/common/pipeline_base.cpp     |   9 +-
> > > >  src/libcamera/pipeline/simple/simple.cpp      |   6 +-
> > > >  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |   4 +-
> > > >  src/libcamera/pipeline/vimc/vimc.cpp          |   4 +-
> > > >  src/libcamera/transform.cpp                   |  58 ++++++
> > > >  32 files changed, 1739 insertions(+), 97 deletions(-)
> > > >  create mode 100644 Documentation/rotation/flip-rotate-0.eps
> > > >  create mode 100644 Documentation/rotation/flip-rotate-0.png
> > > >  create mode 100644 Documentation/rotation/flip-rotate-180.eps
> > > >  create mode 100644 Documentation/rotation/flip-rotate-180.png
> > > >  create mode 100644 Documentation/rotation/flip-rotate-270.eps
> > > >  create mode 100644 Documentation/rotation/flip-rotate-270.png
> > > >  create mode 100644 Documentation/rotation/flip-rotate-90.eps
> > > >  create mode 100644 Documentation/rotation/flip-rotate-90.png
> > > >  create mode 100644 Documentation/rotation/rotate-0.eps
> > > >  create mode 100644 Documentation/rotation/rotate-0.png
> > > >  create mode 100644 Documentation/rotation/rotate-180.eps
> > > >  create mode 100644 Documentation/rotation/rotate-180.png
> > > >  create mode 100644 Documentation/rotation/rotate-270.eps
> > > >  create mode 100644 Documentation/rotation/rotate-270.png
> > > >  create mode 100644 Documentation/rotation/rotate-90.eps
> > > >  create mode 100644 Documentation/rotation/rotate-90.png
> > > >
> > > > --
> > > > 2.40.1
> > > >
David Plowman June 21, 2023, 2:14 p.m. UTC | #5
Hi again

On Wed, 21 Jun 2023 at 13:57, Jacopo Mondi
<jacopo.mondi@ideasonboard.com> wrote:
>
> Hi David
>
> On Wed, Jun 21, 2023 at 01:37:11PM +0100, David Plowman wrote:
> > Hi Jacopo
> >
> > Thanks for the reply.
> >
> > On Wed, 21 Jun 2023 at 10:23, Jacopo Mondi
> > <jacopo.mondi@ideasonboard.com> wrote:
> > >
> > > Hi David
> > >
> > > On Wed, Jun 21, 2023 at 10:08:25AM +0100, David Plowman wrote:
> > > > Hi Jacopo
> > > >
> > > > Thanks for sending this proposal. I wasn't sure I really understood
> > > > it, perhaps you can help me out!
> > > >
> > > > On Tue, 20 Jun 2023 at 15:29, Jacopo Mondi
> > > > <jacopo.mondi@ideasonboard.com> wrote:
> > > > >
> > > > > Hello everyone
> > > > >
> > > > > We have been discussing rotation/transformations for a long time, and
> > > > > the currently implemented CameraConfiguration::transform behaviour has
> > > > > proven to be confusing for several reasons: it was poorly documented,
> > > > > translating it to something consumable for upper layer frameworks was
> > > > > difficult and it behaved differently than any other field part of the
> > > > > CameraConfiguration, as it was meant to tell libcamera "what to do" instead
> > > > > of expressing what applications want.
> > > >
> > > > As far as I understand it, I don't believe this is correct, and the
> > > > transform is where you say what you want.
> > > >
> > > > For example, on a Raspberry Pi I always set the transform to
> > > > Transform::Identity because I want the images "the right way up". (I
> > > > know that internally, it will calculate and apply a 180 rotation to
> > > > counteract the sensor mounting rotation.) Does that sound right?
> > >
> > > Yes, now that we correct properties::Rotation to 0 as the library
> > > compensate the mounting rotation behind your back, that's what you
> > > get.
> > >
> > > Before we had properties::Rotation = 180; transform = Identity and you
> > > got an upright image. Confusing, isn't it ?
> >
> > I understood the previous arrangement, but the new one seems
> > counter-intuitive to me.
> >
> > On a Raspberry Pi, I expect the properties::Rotation to report 180
> > degrees because the sensor is mounted upside down. Then I can ask for
> > Transform::Identity and internally a 180 degree rotation will be
> > applied to give me what I asked for. I understand this.
> >
> > Are we saying that now the rotation will report 0 degrees? Will it now
> > always report 0 degrees, whatever the sensor and platform? That would
> > obviously break things...
> >
>
> So you're an app, you read properties::Rotation and you get back 180.
> Then you star streaming and you get a stream oriented "correctly".
>
> Isn't this wrong ?

I think a lot of this hinges on what the Rotation property is.

It _used_ to be the mounting rotation of the sensor (is that right?),
at which point everything was as I have described it. But I think it
is being changed now, which is indeed something that was under
discussion in that thread back in January. We're free to do that, but
perhaps there have been some unintended consequences?

>
> > >
> > > >
> > > > If the problem is actually to do with documentation, I would always
> > > > agree that it can be improved, and am very happy to help!
> > > >
> > > > >
> > > > > For these reasons this series proposes to replace the usage to Transform
> > > > > in the public API in favour of a new CameraConfiguration::Orientation type,
> > > > > defined based on the EXIF specification, tag 274, 'orientation'. For reference
> > > > > this is the same as implemented by gstreamer:
> > > > > https://gstreamer.freedesktop.org/documentation/gstreamer/gsttaglist.html?gi-language=c#GST_TAG_IMAGE_ORIENTATION
> > > >
> > > > I must confess to not liking the term "orientation". There is such a
> > > > thing as a "2D plane transformation" and I'd expect to refer to them
> > > > as transformations or maybe transforms for short. I don't understand
> > > > what an "orientation" is without reading extra documentation. Are
> > > > "orientations" composable? Is there such a thing as an "inverse
> > > > orientation"?
> > > >
> > >
> > > I'm fine bikeshedding on the name. What matters to me is that now we
> > > conform to an existing specification adopeted by most frameworks
> > > instead of defining our own Transform.
> >
> > I'd like to find something that works for all libcamera users.
> >
> > The Transform class allows composition, inverse operations and so on,
> > the idea being that users can calculate for themselves what to ask for
> > if the default behaviour isn't what they want. I did comment that
> > making it easier for folks to get a specific behaviour might be
> > something to look into.
> >
> > >
> > > > >
> > > > > The newly introduced CameraConfiguration::orientation replaces the
> > > > > existing CameraConfiguration::tranform, and it is meant for application to
> > > > > express how they would like the images to be oriented, not to tell libcamera
> > > > > what to do. As an example, passing in 'rotation0' means that the application
> > > > > expects the images to be rotated upright, and doesn't tell libcamera not to
> > > > > apply any rotation like passing in "Transform::Identity" did.
> > > >
> > > > My understanding is that passing "Transform::Identity" has always
> > > > meant that the application wants its images "upright". (Are there
> > > > cases where this doesn't happen?)
> > > >
> > >
> > > As we have auto-correction in place, yes.
> >
> > My understanding is that everything got "auto-corrected" before. I'm
> > still struggling to understand how it seems to be broken now.
> >
>
> It is not 'broken'. Simply nobody got how thing worked in practice and
> the existing behaviour (when it comes to adjusting ::transform) is not
> what most users expects. See below
>
> > >
> > > > >
> > > > > The value CameraConfiguration::orientation is set to after a validate() also
> > > > > differs in meaning, as instead of reporting "what applications have to do
> > > > > to obtain what they originally asked for" it simply reports the actual
> > > > > orientation of the stream: this means that if libcamera cannot fully satisfy the
> > > > > user request it will set ::orientation to report the native images rotation
> > > > > and the CameraConfiguration::status will be set to Adjusted.
> > > >
> > > > I believe this to be the current behaviour of the transform field.
> > > >
> > >
> > > Not really, see below
> > >
> > > > >
> > > > > Handling of 90 and 270 degrees rotation has also changed: as the camera sensor
> > > > > cannot correct rotations that include a transposition, requests for a 90/270
> > > > > degrees are ignored, and cameras with a mounting rotation of 90/270  degrees
> > > > > are never re-oriented. This makes it clear and less confusing for applications
> > > > > that they have to deal with correction fully by themselves. As an example, with
> > > > > the current implementation if the application requires a Rot270 (HFlip +
> > > > > Transpose) libcamera will do the HFlip and leave transposition to the upper
> > > > > layers. There is no clear advantage in doing so, and display compositors are
> > > > > better suited for handling transpositions and flipping in a single pass instead
> > > > > of having the library try to handle part of that.
> > > >
> > > > Again, I'm not really understanding this.
> > > >
> > > > The current behaviour is such that, if it can't give you the
> > > > transformation that you ask for (on account of not being able to do
> > > > transposition), then it guarantees to give you what you requested
> > > > _apart_ from the transposition. So the application only has one case
> > > > to deal with - applying a transpose. It doesn't have to worry about
> > > > the other 3 "transforms-that-I-can't-handle", thereby making life
> > > > easier for the application.
> > >
> > > The _apart_ is the key here.
> > >
> > > If you currently ask for Rot270 the library will do an HFlip and set
> > > transform to Transpose (ie what applications still have to do to
> > > obtain Rot270). This is not how the other fields of
> >
> > What it _should_ do is set the transform field to the actual transform
> > that the user will perceive the images to have. I believe this to be
> > "proper" libcamera configuration behaviour. I'm not understanding why
> > things are different now.
> >
>
> Let's start with the fact that "transpose" is a concept that is
> introduced in Transform and it's not part of the EXIF standard which
> gstreamer/pipewire expect to use.
>
> Again, you set transform = rot270, libcamera does HFLIP and you have
> to do Transpose, correct ?

That's one option. Or, if you want to do a "rot270" in the application
you just set the transform to "userTransform * -Transform::rot270".

As I've said, I'm really happy to try and make this easier for people.

>
> For compositor this is more work that just do "rot270"
>
> > Beyond this, the current behaviour is to leave the application to do
> > the transpose, but that's just a choice. The point of transforms is
> > that it's easy to use them to produce something different if you want.
> > I don't mind looking at that if we can make it easier for everyone to
> > get their preferred behaviour, which I suggested in my original reply.
> >
>
> I don't get why we should be imposing other frameworks/apps to learn
> Transpose when there is a standard to follow to be honest.
>
> > > CameraConfiguration work, and mostly, applying a flip and letting apps
> > > only deal with transpose is an "optimization" that might actually make
> > > life harder for apps, as display compositors usually have means to
> > > deal with known rotations, while in this case you only ask them to do
> > > a Transpose, which might require them more work ?
> > >
> > > In any case, I think it's saner if the library cannot do what it has
> > > been asked for to set ::transform to what the stream orientation
> > > actually is, instead of telling apps what "they still have to do".
> >
> > I don't understand the repeated statement that the transform is
> > telling apps "what they still have to do". That was never what it
> > meant, unless it got changed somewhere.
> >
>
> Isn't it ? I'm not repeating the Rot270 example, but applications are
> left with ::transform = Transpose. Isn't this what they have to do to
> obtain Rot270 once HFlip has been done by libcamera ?
>
> So they have to "I asked for Rot270, I got back Transpose. Ah!
> libcamera did HFlip for me, so now I have to do Transpose."
>
> Is it what happens ?

The code that I am currently running returns HFlip as the transform
when I ask for Rot270, which is indeed what the application will get.

In this case yes, I still have to do a transpose. But if I had, for
example, code that does a 90 degree rotation, I'd ask for "Rot270 *
-Rot90" instead. The transform field would come back as HVFlip telling
me that my images will be upside down, and all I have to do is call my
rotate 90 function.

The idea is that it should be simple to get whatever behaviour you
want, but I'm very open to trying to make this easier.

>
> > >
> > > >
> > > > Because transforms are composable, it's easy to calculate what
> > > > transform to ask for to get a different behaviour (for example, do
> > > > nothing at all if there's a transpose involved). I could imagine a
> > > > slightly different API might be helpful, perhaps where you get to say
> > > > not only "what you want", but also what you want the "residual
> > > > transform" to be (being the one the application has to apply after) if
> > > > it can't deliver what you wanted. Would that be useful?
> > > >
> > >
> > > I think it's better to leave the stream orientation unmodified if it
> > > cannot be corrected. This matches the behaviour of the other
> > > CameraConfiguration fields, and seems easier to deal with from
> > > applications.
> >
> > I feel we ought to be able to come up with something that makes it
> > convenient for everyone to get what they want.
> >
> > >
> > > > I also recall that there was some discussion a while back about the
> > > > precise orientation of the rotations which could affect some of the
> > > > details, but I don't think it was ever concluded. I don't know if
> > > > that's causing any problems?
> > >
> > > Sorry, I don't remember such discussion. Any link ?
> >
> > I think the last email I sent on the subject was this one, after which
> > things went quiet:
> > https://lists.libcamera.org/pipermail/libcamera-devel/2023-January/036445.html
> > Actually it seems to echo quite a bit of what I say above.

I think one of the sticking points for me is that I don't really
understand what the problems are. Perhaps we could revisit some of
this so that I can be more helpful?

Thanks
David

> >
> > Thanks
> > David
> >
> > >
> > > >
> > > > >
> > > > > This series clearly breaks the application API as it removes a member from
> > > > > CameraConfiguration, so it should be introduced probably only when a new release
> > > > > is cut.
> > > >
> > > > I can't comment on this as I haven't really understood the change
> > > > here. Obviously we have many end users for whom an API change would be
> > > > unwelcome, but I suppose the change could be hidden from them.
> > > >
> > > > I apologise if there have been some discussions flying around that I
> > > > haven't followed sufficiently. I'm afraid I don't normally study
> > > > gstreamer related posts in detail, perhaps I need to try harder...
> > > >
> > > > Thanks
> > > > David
> > > >
> > > > >
> > > > > Tested on RPi imx219 with the newly introduced cam option, flip, mirror and
> > > > > rotation work as expected.
> > > > >
> > > > > Tested on Pinephone Pro, where sensors are 90/270 degrees rotated (libcamera
> > > > > performs no correction)
> > > > >
> > > > > Comments welcome of course.
> > > > >
> > > > > Thanks
> > > > >    j
> > > > >
> > > > > Jacopo Mondi (4):
> > > > >   libcamera: camera_sensor: Cache rotationTransform_
> > > > >   libcamera: camera: Introduce CameraConfiguration::orientation
> > > > >   libcamera: Move to use CameraConfiguration::orientation
> > > > >   apsp: cam: Add option to set stream orientation
> > > > >
> > > > >  Documentation/Doxyfile.in                     |   2 +
> > > > >  Documentation/rotation/flip-rotate-0.eps      | 170 +++++++++++++++
> > > > >  Documentation/rotation/flip-rotate-0.png      | Bin 0 -> 16488 bytes
> > > > >  Documentation/rotation/flip-rotate-180.eps    | 191 +++++++++++++++++
> > > > >  Documentation/rotation/flip-rotate-180.png    | Bin 0 -> 22198 bytes
> > > > >  Documentation/rotation/flip-rotate-270.eps    | 197 ++++++++++++++++++
> > > > >  Documentation/rotation/flip-rotate-270.png    | Bin 0 -> 21367 bytes
> > > > >  Documentation/rotation/flip-rotate-90.eps     | 195 +++++++++++++++++
> > > > >  Documentation/rotation/flip-rotate-90.png     | Bin 0 -> 21020 bytes
> > > > >  Documentation/rotation/rotate-0.eps           | 169 +++++++++++++++
> > > > >  Documentation/rotation/rotate-0.png           | Bin 0 -> 9086 bytes
> > > > >  Documentation/rotation/rotate-180.eps         | 189 +++++++++++++++++
> > > > >  Documentation/rotation/rotate-180.png         | Bin 0 -> 22182 bytes
> > > > >  Documentation/rotation/rotate-270.eps         | 196 +++++++++++++++++
> > > > >  Documentation/rotation/rotate-270.png         | Bin 0 -> 21016 bytes
> > > > >  Documentation/rotation/rotate-90.eps          | 196 +++++++++++++++++
> > > > >  Documentation/rotation/rotate-90.png          | Bin 0 -> 21528 bytes
> > > > >  include/libcamera/camera.h                    |  14 +-
> > > > >  include/libcamera/internal/camera_sensor.h    |   4 +-
> > > > >  include/libcamera/transform.h                 |   4 +
> > > > >  src/apps/cam/camera_session.cpp               |  17 ++
> > > > >  src/apps/cam/main.cpp                         |   5 +
> > > > >  src/apps/cam/main.h                           |   1 +
> > > > >  src/libcamera/camera.cpp                      |  54 ++++-
> > > > >  src/libcamera/camera_sensor.cpp               | 137 ++++++------
> > > > >  src/libcamera/pipeline/ipu3/ipu3.cpp          |   6 +-
> > > > >  src/libcamera/pipeline/rkisp1/rkisp1.cpp      |   8 +-
> > > > >  .../pipeline/rpi/common/pipeline_base.cpp     |   9 +-
> > > > >  src/libcamera/pipeline/simple/simple.cpp      |   6 +-
> > > > >  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |   4 +-
> > > > >  src/libcamera/pipeline/vimc/vimc.cpp          |   4 +-
> > > > >  src/libcamera/transform.cpp                   |  58 ++++++
> > > > >  32 files changed, 1739 insertions(+), 97 deletions(-)
> > > > >  create mode 100644 Documentation/rotation/flip-rotate-0.eps
> > > > >  create mode 100644 Documentation/rotation/flip-rotate-0.png
> > > > >  create mode 100644 Documentation/rotation/flip-rotate-180.eps
> > > > >  create mode 100644 Documentation/rotation/flip-rotate-180.png
> > > > >  create mode 100644 Documentation/rotation/flip-rotate-270.eps
> > > > >  create mode 100644 Documentation/rotation/flip-rotate-270.png
> > > > >  create mode 100644 Documentation/rotation/flip-rotate-90.eps
> > > > >  create mode 100644 Documentation/rotation/flip-rotate-90.png
> > > > >  create mode 100644 Documentation/rotation/rotate-0.eps
> > > > >  create mode 100644 Documentation/rotation/rotate-0.png
> > > > >  create mode 100644 Documentation/rotation/rotate-180.eps
> > > > >  create mode 100644 Documentation/rotation/rotate-180.png
> > > > >  create mode 100644 Documentation/rotation/rotate-270.eps
> > > > >  create mode 100644 Documentation/rotation/rotate-270.png
> > > > >  create mode 100644 Documentation/rotation/rotate-90.eps
> > > > >  create mode 100644 Documentation/rotation/rotate-90.png
> > > > >
> > > > > --
> > > > > 2.40.1
> > > > >
Jacopo Mondi June 21, 2023, 2:30 p.m. UTC | #6
Hi David
   let me summarize, as usual the discussion become hard to follow.

On Wed, Jun 21, 2023 at 03:14:28PM +0100, David Plowman wrote:

[snip]

> > >
> > > I think the last email I sent on the subject was this one, after which
> > > things went quiet:
> > > https://lists.libcamera.org/pipermail/libcamera-devel/2023-January/036445.html
> > > Actually it seems to echo quite a bit of what I say above.
>
> I think one of the sticking points for me is that I don't really
> understand what the problems are. Perhaps we could revisit some of
> this so that I can be more helpful?
>

1) Transform is a libcamera concept, other frameworks have to learn
how to use it. A specification exists and it's wildely adopted, why
should we diverge from it ? Because there is code already using the
current implementation ?

2) The library applies partial transforms (like "I'll do HFlip if I'm
asked Rot270 and I can't do Transpose"). This is confusing as
applications have to compose transforms and do computations. This
patch simplifies it: either you can do it in full, or leave the stream
un-touched. There are no clear advantages that I can think of in
partially applying transforms.

3) properties::Rotation, the documentation does not report 'mounting
rotation' but I admit we (I myself for one) have often used that term.
That's because, originally, my understanding was that no
autocorrection should have been performed. Since it has been
introduced (by copying your PH implementation) and others liked it,
now properties::Rotation reports the stream rotation when no
::transform is specified. I think it's confusing to have
properties::Rotation=180 and a stream oriented correctly, applications
might wonder what's happening and if libcamera is doing something
behind their back.

I think these are the main discussion points, aren't they ?

> Thanks
> David
>
> > >
> > > Thanks
> > > David
> > >
> > > >
> > > > >
> > > > > >
> > > > > > This series clearly breaks the application API as it removes a member from
> > > > > > CameraConfiguration, so it should be introduced probably only when a new release
> > > > > > is cut.
> > > > >
> > > > > I can't comment on this as I haven't really understood the change
> > > > > here. Obviously we have many end users for whom an API change would be
> > > > > unwelcome, but I suppose the change could be hidden from them.
> > > > >
> > > > > I apologise if there have been some discussions flying around that I
> > > > > haven't followed sufficiently. I'm afraid I don't normally study
> > > > > gstreamer related posts in detail, perhaps I need to try harder...
> > > > >
> > > > > Thanks
> > > > > David
> > > > >
> > > > > >
> > > > > > Tested on RPi imx219 with the newly introduced cam option, flip, mirror and
> > > > > > rotation work as expected.
> > > > > >
> > > > > > Tested on Pinephone Pro, where sensors are 90/270 degrees rotated (libcamera
> > > > > > performs no correction)
> > > > > >
> > > > > > Comments welcome of course.
> > > > > >
> > > > > > Thanks
> > > > > >    j
> > > > > >
> > > > > > Jacopo Mondi (4):
> > > > > >   libcamera: camera_sensor: Cache rotationTransform_
> > > > > >   libcamera: camera: Introduce CameraConfiguration::orientation
> > > > > >   libcamera: Move to use CameraConfiguration::orientation
> > > > > >   apsp: cam: Add option to set stream orientation
> > > > > >
> > > > > >  Documentation/Doxyfile.in                     |   2 +
> > > > > >  Documentation/rotation/flip-rotate-0.eps      | 170 +++++++++++++++
> > > > > >  Documentation/rotation/flip-rotate-0.png      | Bin 0 -> 16488 bytes
> > > > > >  Documentation/rotation/flip-rotate-180.eps    | 191 +++++++++++++++++
> > > > > >  Documentation/rotation/flip-rotate-180.png    | Bin 0 -> 22198 bytes
> > > > > >  Documentation/rotation/flip-rotate-270.eps    | 197 ++++++++++++++++++
> > > > > >  Documentation/rotation/flip-rotate-270.png    | Bin 0 -> 21367 bytes
> > > > > >  Documentation/rotation/flip-rotate-90.eps     | 195 +++++++++++++++++
> > > > > >  Documentation/rotation/flip-rotate-90.png     | Bin 0 -> 21020 bytes
> > > > > >  Documentation/rotation/rotate-0.eps           | 169 +++++++++++++++
> > > > > >  Documentation/rotation/rotate-0.png           | Bin 0 -> 9086 bytes
> > > > > >  Documentation/rotation/rotate-180.eps         | 189 +++++++++++++++++
> > > > > >  Documentation/rotation/rotate-180.png         | Bin 0 -> 22182 bytes
> > > > > >  Documentation/rotation/rotate-270.eps         | 196 +++++++++++++++++
> > > > > >  Documentation/rotation/rotate-270.png         | Bin 0 -> 21016 bytes
> > > > > >  Documentation/rotation/rotate-90.eps          | 196 +++++++++++++++++
> > > > > >  Documentation/rotation/rotate-90.png          | Bin 0 -> 21528 bytes
> > > > > >  include/libcamera/camera.h                    |  14 +-
> > > > > >  include/libcamera/internal/camera_sensor.h    |   4 +-
> > > > > >  include/libcamera/transform.h                 |   4 +
> > > > > >  src/apps/cam/camera_session.cpp               |  17 ++
> > > > > >  src/apps/cam/main.cpp                         |   5 +
> > > > > >  src/apps/cam/main.h                           |   1 +
> > > > > >  src/libcamera/camera.cpp                      |  54 ++++-
> > > > > >  src/libcamera/camera_sensor.cpp               | 137 ++++++------
> > > > > >  src/libcamera/pipeline/ipu3/ipu3.cpp          |   6 +-
> > > > > >  src/libcamera/pipeline/rkisp1/rkisp1.cpp      |   8 +-
> > > > > >  .../pipeline/rpi/common/pipeline_base.cpp     |   9 +-
> > > > > >  src/libcamera/pipeline/simple/simple.cpp      |   6 +-
> > > > > >  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |   4 +-
> > > > > >  src/libcamera/pipeline/vimc/vimc.cpp          |   4 +-
> > > > > >  src/libcamera/transform.cpp                   |  58 ++++++
> > > > > >  32 files changed, 1739 insertions(+), 97 deletions(-)
> > > > > >  create mode 100644 Documentation/rotation/flip-rotate-0.eps
> > > > > >  create mode 100644 Documentation/rotation/flip-rotate-0.png
> > > > > >  create mode 100644 Documentation/rotation/flip-rotate-180.eps
> > > > > >  create mode 100644 Documentation/rotation/flip-rotate-180.png
> > > > > >  create mode 100644 Documentation/rotation/flip-rotate-270.eps
> > > > > >  create mode 100644 Documentation/rotation/flip-rotate-270.png
> > > > > >  create mode 100644 Documentation/rotation/flip-rotate-90.eps
> > > > > >  create mode 100644 Documentation/rotation/flip-rotate-90.png
> > > > > >  create mode 100644 Documentation/rotation/rotate-0.eps
> > > > > >  create mode 100644 Documentation/rotation/rotate-0.png
> > > > > >  create mode 100644 Documentation/rotation/rotate-180.eps
> > > > > >  create mode 100644 Documentation/rotation/rotate-180.png
> > > > > >  create mode 100644 Documentation/rotation/rotate-270.eps
> > > > > >  create mode 100644 Documentation/rotation/rotate-270.png
> > > > > >  create mode 100644 Documentation/rotation/rotate-90.eps
> > > > > >  create mode 100644 Documentation/rotation/rotate-90.png
> > > > > >
> > > > > > --
> > > > > > 2.40.1
> > > > > >
David Plowman June 21, 2023, 4:33 p.m. UTC | #7
Hi again

On Wed, 21 Jun 2023 at 15:30, Jacopo Mondi
<jacopo.mondi@ideasonboard.com> wrote:
>
> Hi David
>    let me summarize, as usual the discussion become hard to follow.
>
> On Wed, Jun 21, 2023 at 03:14:28PM +0100, David Plowman wrote:
>
> [snip]
>
> > > >
> > > > I think the last email I sent on the subject was this one, after which
> > > > things went quiet:
> > > > https://lists.libcamera.org/pipermail/libcamera-devel/2023-January/036445.html
> > > > Actually it seems to echo quite a bit of what I say above.
> >
> > I think one of the sticking points for me is that I don't really
> > understand what the problems are. Perhaps we could revisit some of
> > this so that I can be more helpful?
> >
>
> 1) Transform is a libcamera concept, other frameworks have to learn
> how to use it. A specification exists and it's wildely adopted, why
> should we diverge from it ? Because there is code already using the
> current implementation ?

The orientation flag is completely identical to the transform, just
with different names, so I don't feel this should be an area of
difficulty. We can change the names, or add synonyms, I don't really
mind. But the transform does have useful compose and inverse functions
which I would not want to lose.

This code is in quite wide use in the Pi community, so I do generally
want to inconvenience users as little as possible.

>
> 2) The library applies partial transforms (like "I'll do HFlip if I'm
> asked Rot270 and I can't do Transpose"). This is confusing as
> applications have to compose transforms and do computations. This
> patch simplifies it: either you can do it in full, or leave the stream
> un-touched. There are no clear advantages that I can think of in
> partially applying transforms.

I like the partial transforms because it means I have only one
"exception" case to deal with, and not four. In particular, I can
control what the "left over" transform will be, if I want to. Though
of course, I don't have to.

I think it would be OK to change the default behaviour to what you
have described (i.e. do nothing at all if it can't do the whole
transform), so long as I can still generate the transform that I want,
or my users are able to.

>
> 3) properties::Rotation, the documentation does not report 'mounting
> rotation' but I admit we (I myself for one) have often used that term.
> That's because, originally, my understanding was that no
> autocorrection should have been performed. Since it has been
> introduced (by copying your PH implementation) and others liked it,
> now properties::Rotation reports the stream rotation when no
> ::transform is specified. I think it's confusing to have
> properties::Rotation=180 and a stream oriented correctly, applications
> might wonder what's happening and if libcamera is doing something
> behind their back.

I don't mind changing the meaning of the Rotation property, but I
think we should also be fixing up the existing code so that its
behaviour doesn't change. It would also be nice if you could query the
mounting rotation from an application, though I would agree an
application should never need to use it.

>
> I think these are the main discussion points, aren't they ?

I'd like to make a proposal, if I may.

1. I'm happy to change the Rotation property, but at the same time we
need to fix the code so that the behaviour of transforms is not
changed. I would still like there to be a property so that an
application can query the mounting rotation if it wants to.
2. We change the default behaviour to apply no transform at all if it
can't apply the full transform. (Applications that don't want this can
easily sort themselves out now.)

At this point I think everyone has the functionality that they want.
Just to try and flesh out the application code a bit so that we are
forced to understand it, we have the following.

Applications that want the simple behaviour:

Transform requestedTransform; // the transform the user wants
Transform actualTransform; // what the user will get, from checking
the transform field after validate()

if (requestedTransform != actualTransform)
    /* The application will have to apply a transform equivalent to
-actualTransform * requestedTransform */;

Applications that want to control the transform that they apply (let's
call this the "leftoverTransform"):

if (requestedTransform != actualTransform)
    requestedTransform *= -leftoverTransform; // now just try again

But maybe double-check if that all feels right!!

3. We can consider swapping the transform in the configuration for an
"orientation" with a different set of names. We need to add
transform/orientation conversion functions so that I (and my users)
can still use it.

How does that sound?

David

>
> > Thanks
> > David
> >
> > > >
> > > > Thanks
> > > > David
> > > >
> > > > >
> > > > > >
> > > > > > >
> > > > > > > This series clearly breaks the application API as it removes a member from
> > > > > > > CameraConfiguration, so it should be introduced probably only when a new release
> > > > > > > is cut.
> > > > > >
> > > > > > I can't comment on this as I haven't really understood the change
> > > > > > here. Obviously we have many end users for whom an API change would be
> > > > > > unwelcome, but I suppose the change could be hidden from them.
> > > > > >
> > > > > > I apologise if there have been some discussions flying around that I
> > > > > > haven't followed sufficiently. I'm afraid I don't normally study
> > > > > > gstreamer related posts in detail, perhaps I need to try harder...
> > > > > >
> > > > > > Thanks
> > > > > > David
> > > > > >
> > > > > > >
> > > > > > > Tested on RPi imx219 with the newly introduced cam option, flip, mirror and
> > > > > > > rotation work as expected.
> > > > > > >
> > > > > > > Tested on Pinephone Pro, where sensors are 90/270 degrees rotated (libcamera
> > > > > > > performs no correction)
> > > > > > >
> > > > > > > Comments welcome of course.
> > > > > > >
> > > > > > > Thanks
> > > > > > >    j
> > > > > > >
> > > > > > > Jacopo Mondi (4):
> > > > > > >   libcamera: camera_sensor: Cache rotationTransform_
> > > > > > >   libcamera: camera: Introduce CameraConfiguration::orientation
> > > > > > >   libcamera: Move to use CameraConfiguration::orientation
> > > > > > >   apsp: cam: Add option to set stream orientation
> > > > > > >
> > > > > > >  Documentation/Doxyfile.in                     |   2 +
> > > > > > >  Documentation/rotation/flip-rotate-0.eps      | 170 +++++++++++++++
> > > > > > >  Documentation/rotation/flip-rotate-0.png      | Bin 0 -> 16488 bytes
> > > > > > >  Documentation/rotation/flip-rotate-180.eps    | 191 +++++++++++++++++
> > > > > > >  Documentation/rotation/flip-rotate-180.png    | Bin 0 -> 22198 bytes
> > > > > > >  Documentation/rotation/flip-rotate-270.eps    | 197 ++++++++++++++++++
> > > > > > >  Documentation/rotation/flip-rotate-270.png    | Bin 0 -> 21367 bytes
> > > > > > >  Documentation/rotation/flip-rotate-90.eps     | 195 +++++++++++++++++
> > > > > > >  Documentation/rotation/flip-rotate-90.png     | Bin 0 -> 21020 bytes
> > > > > > >  Documentation/rotation/rotate-0.eps           | 169 +++++++++++++++
> > > > > > >  Documentation/rotation/rotate-0.png           | Bin 0 -> 9086 bytes
> > > > > > >  Documentation/rotation/rotate-180.eps         | 189 +++++++++++++++++
> > > > > > >  Documentation/rotation/rotate-180.png         | Bin 0 -> 22182 bytes
> > > > > > >  Documentation/rotation/rotate-270.eps         | 196 +++++++++++++++++
> > > > > > >  Documentation/rotation/rotate-270.png         | Bin 0 -> 21016 bytes
> > > > > > >  Documentation/rotation/rotate-90.eps          | 196 +++++++++++++++++
> > > > > > >  Documentation/rotation/rotate-90.png          | Bin 0 -> 21528 bytes
> > > > > > >  include/libcamera/camera.h                    |  14 +-
> > > > > > >  include/libcamera/internal/camera_sensor.h    |   4 +-
> > > > > > >  include/libcamera/transform.h                 |   4 +
> > > > > > >  src/apps/cam/camera_session.cpp               |  17 ++
> > > > > > >  src/apps/cam/main.cpp                         |   5 +
> > > > > > >  src/apps/cam/main.h                           |   1 +
> > > > > > >  src/libcamera/camera.cpp                      |  54 ++++-
> > > > > > >  src/libcamera/camera_sensor.cpp               | 137 ++++++------
> > > > > > >  src/libcamera/pipeline/ipu3/ipu3.cpp          |   6 +-
> > > > > > >  src/libcamera/pipeline/rkisp1/rkisp1.cpp      |   8 +-
> > > > > > >  .../pipeline/rpi/common/pipeline_base.cpp     |   9 +-
> > > > > > >  src/libcamera/pipeline/simple/simple.cpp      |   6 +-
> > > > > > >  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |   4 +-
> > > > > > >  src/libcamera/pipeline/vimc/vimc.cpp          |   4 +-
> > > > > > >  src/libcamera/transform.cpp                   |  58 ++++++
> > > > > > >  32 files changed, 1739 insertions(+), 97 deletions(-)
> > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-0.eps
> > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-0.png
> > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-180.eps
> > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-180.png
> > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-270.eps
> > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-270.png
> > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-90.eps
> > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-90.png
> > > > > > >  create mode 100644 Documentation/rotation/rotate-0.eps
> > > > > > >  create mode 100644 Documentation/rotation/rotate-0.png
> > > > > > >  create mode 100644 Documentation/rotation/rotate-180.eps
> > > > > > >  create mode 100644 Documentation/rotation/rotate-180.png
> > > > > > >  create mode 100644 Documentation/rotation/rotate-270.eps
> > > > > > >  create mode 100644 Documentation/rotation/rotate-270.png
> > > > > > >  create mode 100644 Documentation/rotation/rotate-90.eps
> > > > > > >  create mode 100644 Documentation/rotation/rotate-90.png
> > > > > > >
> > > > > > > --
> > > > > > > 2.40.1
> > > > > > >
Jacopo Mondi June 22, 2023, 8:08 a.m. UTC | #8
Hi David

On Wed, Jun 21, 2023 at 05:33:45PM +0100, David Plowman wrote:
> Hi again
>
> On Wed, 21 Jun 2023 at 15:30, Jacopo Mondi
> <jacopo.mondi@ideasonboard.com> wrote:
> >
> > Hi David
> >    let me summarize, as usual the discussion become hard to follow.
> >
> > On Wed, Jun 21, 2023 at 03:14:28PM +0100, David Plowman wrote:
> >
> > [snip]
> >
> > > > >
> > > > > I think the last email I sent on the subject was this one, after which
> > > > > things went quiet:
> > > > > https://lists.libcamera.org/pipermail/libcamera-devel/2023-January/036445.html
> > > > > Actually it seems to echo quite a bit of what I say above.
> > >
> > > I think one of the sticking points for me is that I don't really
> > > understand what the problems are. Perhaps we could revisit some of
> > > this so that I can be more helpful?
> > >
> >
> > 1) Transform is a libcamera concept, other frameworks have to learn
> > how to use it. A specification exists and it's wildely adopted, why
> > should we diverge from it ? Because there is code already using the
> > current implementation ?
>
> The orientation flag is completely identical to the transform, just
> with different names, so I don't feel this should be an area of

Why do you say it is identical ?

The Orientation enumeration is defined based on the EXIF tag 274
'orientation' and describes 2D transforms in terms of clock-wise
rotation and horizontal mirroring, while Transform is a custom-defined
type which uses Transpose and V/HFlips. Why do you think they're
identical ?

The EXIF defined values are also used by

gstreamer/gtk
https://api.gtkd.org/gstreamer.c.types.TAG_IMAGE_ORIENTATION.html

pipewire
https://docs.pipewire.org/group__spa__buffer.html#gab4afeb1516aa33126757cfa275efd7a3

> difficulty. We can change the names, or add synonyms, I don't really
> mind. But the transform does have useful compose and inverse functions
> which I would not want to lose.
>
> This code is in quite wide use in the Pi community, so I do generally
> want to inconvenience users as little as possible.
>

I understand this argument, but it can't be used as an excuse to never
change the API, in particular if there is a standard already adopted
by other frameworks we don't ahere to.

> >
> > 2) The library applies partial transforms (like "I'll do HFlip if I'm
> > asked Rot270 and I can't do Transpose"). This is confusing as
> > applications have to compose transforms and do computations. This
> > patch simplifies it: either you can do it in full, or leave the stream
> > un-touched. There are no clear advantages that I can think of in
> > partially applying transforms.
>
> I like the partial transforms because it means I have only one
> "exception" case to deal with, and not four. In particular, I can
> control what the "left over" transform will be, if I want to. Though
> of course, I don't have to.
>

I think what makes a difference here is the behaviour of the consumers
of a "rotated" image.

What is the "next" component that in most cases have to compensate for
a non-fully corrected rotation ? I presume it generally is the display
compositor or a framework like gstreamer that has elements like
'videoflip'. We know gtk/gstreamer internally uses the EXIF tag, but
when it comes to compositor, why should for them be better to deal
with "Transpose" instead that on Rot270 ? I'm no expert there but I
presume rendering libraries have methods for quickly rotate images
before rendering but they might not have "transpose only" ones. Also
because "transpose along the diagonal axis from the top-left corner to
the right-bottom one" is a concept I haven't found documented anywhere
else if not in the Transform class (of course I might have missed it
as I'm certainly no expert on rendering).

> I think it would be OK to change the default behaviour to what you
> have described (i.e. do nothing at all if it can't do the whole
> transform), so long as I can still generate the transform that I want,
> or my users are able to.
>
> >
> > 3) properties::Rotation, the documentation does not report 'mounting
> > rotation' but I admit we (I myself for one) have often used that term.
> > That's because, originally, my understanding was that no
> > autocorrection should have been performed. Since it has been
> > introduced (by copying your PH implementation) and others liked it,
> > now properties::Rotation reports the stream rotation when no
> > ::transform is specified. I think it's confusing to have
> > properties::Rotation=180 and a stream oriented correctly, applications
> > might wonder what's happening and if libcamera is doing something
> > behind their back.
>
> I don't mind changing the meaning of the Rotation property, but I
> think we should also be fixing up the existing code so that its
> behaviour doesn't change. It would also be nice if you could query the

What existing code relies on Rotation being the mounting rotation ? In
libcamera ? In the apps ?

> mounting rotation from an application, though I would agree an
> application should never need to use it.
>
> >
> > I think these are the main discussion points, aren't they ?
>
> I'd like to make a proposal, if I may.
>
> 1. I'm happy to change the Rotation property, but at the same time we
> need to fix the code so that the behaviour of transforms is not
> changed. I would still like there to be a property so that an
> application can query the mounting rotation if it wants to.

I'm fine introducing one

> 2. We change the default behaviour to apply no transform at all if it
> can't apply the full transform. (Applications that don't want this can
> easily sort themselves out now.)
>
> At this point I think everyone has the functionality that they want.
> Just to try and flesh out the application code a bit so that we are
> forced to understand it, we have the following.
>
> Applications that want the simple behaviour:
>
> Transform requestedTransform; // the transform the user wants
> Transform actualTransform; // what the user will get, from checking
> the transform field after validate()
>
> if (requestedTransform != actualTransform)
>     /* The application will have to apply a transform equivalent to
> -actualTransform * requestedTransform */;
>
> Applications that want to control the transform that they apply (let's
> call this the "leftoverTransform"):
>
> if (requestedTransform != actualTransform)
>     requestedTransform *= -leftoverTransform; // now just try again

I still don't get why people should do any of this calculations.

I don't expect anyone to write their own rotation libraries, but
instead rely on the rendering libraries or on piping gstreamer
elements to the capture output. Why should they calculate the
"leftoverTransform" ? What is the use case for an application in doing
this ?

>
> But maybe double-check if that all feels right!!

I think it's relevant to know how gstreamer/pipewire expects to
deal with transforms, and more importantly what 'transform' display
compositors supports natively.

>
> 3. We can consider swapping the transform in the configuration for an
> "orientation" with a different set of names. We need to add
> transform/orientation conversion functions so that I (and my users)
> can still use it.

See the newly added functions in transform.cpp

>
> How does that sound?
>
> David
>
> >
> > > Thanks
> > > David
> > >
> > > > >
> > > > > Thanks
> > > > > David
> > > > >
> > > > > >
> > > > > > >
> > > > > > > >
> > > > > > > > This series clearly breaks the application API as it removes a member from
> > > > > > > > CameraConfiguration, so it should be introduced probably only when a new release
> > > > > > > > is cut.
> > > > > > >
> > > > > > > I can't comment on this as I haven't really understood the change
> > > > > > > here. Obviously we have many end users for whom an API change would be
> > > > > > > unwelcome, but I suppose the change could be hidden from them.
> > > > > > >
> > > > > > > I apologise if there have been some discussions flying around that I
> > > > > > > haven't followed sufficiently. I'm afraid I don't normally study
> > > > > > > gstreamer related posts in detail, perhaps I need to try harder...
> > > > > > >
> > > > > > > Thanks
> > > > > > > David
> > > > > > >
> > > > > > > >
> > > > > > > > Tested on RPi imx219 with the newly introduced cam option, flip, mirror and
> > > > > > > > rotation work as expected.
> > > > > > > >
> > > > > > > > Tested on Pinephone Pro, where sensors are 90/270 degrees rotated (libcamera
> > > > > > > > performs no correction)
> > > > > > > >
> > > > > > > > Comments welcome of course.
> > > > > > > >
> > > > > > > > Thanks
> > > > > > > >    j
> > > > > > > >
> > > > > > > > Jacopo Mondi (4):
> > > > > > > >   libcamera: camera_sensor: Cache rotationTransform_
> > > > > > > >   libcamera: camera: Introduce CameraConfiguration::orientation
> > > > > > > >   libcamera: Move to use CameraConfiguration::orientation
> > > > > > > >   apsp: cam: Add option to set stream orientation
> > > > > > > >
> > > > > > > >  Documentation/Doxyfile.in                     |   2 +
> > > > > > > >  Documentation/rotation/flip-rotate-0.eps      | 170 +++++++++++++++
> > > > > > > >  Documentation/rotation/flip-rotate-0.png      | Bin 0 -> 16488 bytes
> > > > > > > >  Documentation/rotation/flip-rotate-180.eps    | 191 +++++++++++++++++
> > > > > > > >  Documentation/rotation/flip-rotate-180.png    | Bin 0 -> 22198 bytes
> > > > > > > >  Documentation/rotation/flip-rotate-270.eps    | 197 ++++++++++++++++++
> > > > > > > >  Documentation/rotation/flip-rotate-270.png    | Bin 0 -> 21367 bytes
> > > > > > > >  Documentation/rotation/flip-rotate-90.eps     | 195 +++++++++++++++++
> > > > > > > >  Documentation/rotation/flip-rotate-90.png     | Bin 0 -> 21020 bytes
> > > > > > > >  Documentation/rotation/rotate-0.eps           | 169 +++++++++++++++
> > > > > > > >  Documentation/rotation/rotate-0.png           | Bin 0 -> 9086 bytes
> > > > > > > >  Documentation/rotation/rotate-180.eps         | 189 +++++++++++++++++
> > > > > > > >  Documentation/rotation/rotate-180.png         | Bin 0 -> 22182 bytes
> > > > > > > >  Documentation/rotation/rotate-270.eps         | 196 +++++++++++++++++
> > > > > > > >  Documentation/rotation/rotate-270.png         | Bin 0 -> 21016 bytes
> > > > > > > >  Documentation/rotation/rotate-90.eps          | 196 +++++++++++++++++
> > > > > > > >  Documentation/rotation/rotate-90.png          | Bin 0 -> 21528 bytes
> > > > > > > >  include/libcamera/camera.h                    |  14 +-
> > > > > > > >  include/libcamera/internal/camera_sensor.h    |   4 +-
> > > > > > > >  include/libcamera/transform.h                 |   4 +
> > > > > > > >  src/apps/cam/camera_session.cpp               |  17 ++
> > > > > > > >  src/apps/cam/main.cpp                         |   5 +
> > > > > > > >  src/apps/cam/main.h                           |   1 +
> > > > > > > >  src/libcamera/camera.cpp                      |  54 ++++-
> > > > > > > >  src/libcamera/camera_sensor.cpp               | 137 ++++++------
> > > > > > > >  src/libcamera/pipeline/ipu3/ipu3.cpp          |   6 +-
> > > > > > > >  src/libcamera/pipeline/rkisp1/rkisp1.cpp      |   8 +-
> > > > > > > >  .../pipeline/rpi/common/pipeline_base.cpp     |   9 +-
> > > > > > > >  src/libcamera/pipeline/simple/simple.cpp      |   6 +-
> > > > > > > >  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |   4 +-
> > > > > > > >  src/libcamera/pipeline/vimc/vimc.cpp          |   4 +-
> > > > > > > >  src/libcamera/transform.cpp                   |  58 ++++++
> > > > > > > >  32 files changed, 1739 insertions(+), 97 deletions(-)
> > > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-0.eps
> > > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-0.png
> > > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-180.eps
> > > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-180.png
> > > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-270.eps
> > > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-270.png
> > > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-90.eps
> > > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-90.png
> > > > > > > >  create mode 100644 Documentation/rotation/rotate-0.eps
> > > > > > > >  create mode 100644 Documentation/rotation/rotate-0.png
> > > > > > > >  create mode 100644 Documentation/rotation/rotate-180.eps
> > > > > > > >  create mode 100644 Documentation/rotation/rotate-180.png
> > > > > > > >  create mode 100644 Documentation/rotation/rotate-270.eps
> > > > > > > >  create mode 100644 Documentation/rotation/rotate-270.png
> > > > > > > >  create mode 100644 Documentation/rotation/rotate-90.eps
> > > > > > > >  create mode 100644 Documentation/rotation/rotate-90.png
> > > > > > > >
> > > > > > > > --
> > > > > > > > 2.40.1
> > > > > > > >
David Plowman June 22, 2023, 12:47 p.m. UTC | #9
HI Jacopo

On Thu, 22 Jun 2023 at 09:08, Jacopo Mondi
<jacopo.mondi@ideasonboard.com> wrote:
>
> Hi David
>
> On Wed, Jun 21, 2023 at 05:33:45PM +0100, David Plowman wrote:
> > Hi again
> >
> > On Wed, 21 Jun 2023 at 15:30, Jacopo Mondi
> > <jacopo.mondi@ideasonboard.com> wrote:
> > >
> > > Hi David
> > >    let me summarize, as usual the discussion become hard to follow.
> > >
> > > On Wed, Jun 21, 2023 at 03:14:28PM +0100, David Plowman wrote:
> > >
> > > [snip]
> > >
> > > > > >
> > > > > > I think the last email I sent on the subject was this one, after which
> > > > > > things went quiet:
> > > > > > https://lists.libcamera.org/pipermail/libcamera-devel/2023-January/036445.html
> > > > > > Actually it seems to echo quite a bit of what I say above.
> > > >
> > > > I think one of the sticking points for me is that I don't really
> > > > understand what the problems are. Perhaps we could revisit some of
> > > > this so that I can be more helpful?
> > > >
> > >
> > > 1) Transform is a libcamera concept, other frameworks have to learn
> > > how to use it. A specification exists and it's wildely adopted, why
> > > should we diverge from it ? Because there is code already using the
> > > current implementation ?
> >
> > The orientation flag is completely identical to the transform, just
> > with different names, so I don't feel this should be an area of
>
> Why do you say it is identical ?

Sorry, I could have been clearer. The orientation flag is describing
exactly the same 8 plane transformations as the transform, with
exactly the same meanings. I believe the only difference is in the
naming, the precise encoding in terms of numeric values (hope no one
relies on those!), and the availability of extra functions to
"manipulate" them (compose/invert etc.).

I think different categories of users (maybe those who care about
rendering images, and those who are interested in image analysis, for
example) may have slightly different expectations for naming these
things, so I definitely think there's some scope for re-examining
those.

>
> The Orientation enumeration is defined based on the EXIF tag 274
> 'orientation' and describes 2D transforms in terms of clock-wise
> rotation and horizontal mirroring, while Transform is a custom-defined
> type which uses Transpose and V/HFlips. Why do you think they're
> identical ?
>
> The EXIF defined values are also used by
>
> gstreamer/gtk
> https://api.gtkd.org/gstreamer.c.types.TAG_IMAGE_ORIENTATION.html
>
> pipewire
> https://docs.pipewire.org/group__spa__buffer.html#gab4afeb1516aa33126757cfa275efd7a3
>
> > difficulty. We can change the names, or add synonyms, I don't really
> > mind. But the transform does have useful compose and inverse functions
> > which I would not want to lose.
> >
> > This code is in quite wide use in the Pi community, so I do generally
> > want to inconvenience users as little as possible.
> >
>
> I understand this argument, but it can't be used as an excuse to never
> change the API, in particular if there is a standard already adopted
> by other frameworks we don't ahere to.

I agree. Generally speaking we try not to change things more than
necessary, though there are many occasions where you have to. There
are always different things to consider and a good balance needs to be
found.

>
> > >
> > > 2) The library applies partial transforms (like "I'll do HFlip if I'm
> > > asked Rot270 and I can't do Transpose"). This is confusing as
> > > applications have to compose transforms and do computations. This
> > > patch simplifies it: either you can do it in full, or leave the stream
> > > un-touched. There are no clear advantages that I can think of in
> > > partially applying transforms.
> >
> > I like the partial transforms because it means I have only one
> > "exception" case to deal with, and not four. In particular, I can
> > control what the "left over" transform will be, if I want to. Though
> > of course, I don't have to.
> >
>
> I think what makes a difference here is the behaviour of the consumers
> of a "rotated" image.
>
> What is the "next" component that in most cases have to compensate for
> a non-fully corrected rotation ? I presume it generally is the display
> compositor or a framework like gstreamer that has elements like
> 'videoflip'. We know gtk/gstreamer internally uses the EXIF tag, but
> when it comes to compositor, why should for them be better to deal
> with "Transpose" instead that on Rot270 ? I'm no expert there but I
> presume rendering libraries have methods for quickly rotate images
> before rendering but they might not have "transpose only" ones. Also
> because "transpose along the diagonal axis from the top-left corner to
> the right-bottom one" is a concept I haven't found documented anywhere
> else if not in the Transform class (of course I might have missed it
> as I'm certainly no expert on rendering).

My aim is to try and give everyone the functionality that they want.

I wouldn't want us to think that rendering is the only use for images.
We have many users who want to process images and analyse them in
different ways. They may be running neural networks, doing filtering
operations, combining images, all kinds of things. It's actually very
common that they aren't rendering at all, and it's all about the
downstream image processing. Rendering is very important, but there
are other uses too, and it would be great to give them some
flexibility as well.

Even within rendering, I wonder what would happen if (for example) you
had hardware that can do rot90 and rot270, but not transpose. What
would you do if an application happened to ask for an orientation that
left you needing to do a transpose? It would be nice if you could
easily calculate the transform you need to ask for so that you're left
with a rot90 instead.

>
> > I think it would be OK to change the default behaviour to what you
> > have described (i.e. do nothing at all if it can't do the whole
> > transform), so long as I can still generate the transform that I want,
> > or my users are able to.
> >
> > >
> > > 3) properties::Rotation, the documentation does not report 'mounting
> > > rotation' but I admit we (I myself for one) have often used that term.
> > > That's because, originally, my understanding was that no
> > > autocorrection should have been performed. Since it has been
> > > introduced (by copying your PH implementation) and others liked it,
> > > now properties::Rotation reports the stream rotation when no
> > > ::transform is specified. I think it's confusing to have
> > > properties::Rotation=180 and a stream oriented correctly, applications
> > > might wonder what's happening and if libcamera is doing something
> > > behind their back.
> >
> > I don't mind changing the meaning of the Rotation property, but I
> > think we should also be fixing up the existing code so that its
> > behaviour doesn't change. It would also be nice if you could query the
>
> What existing code relies on Rotation being the mounting rotation ? In
> libcamera ? In the apps ?

I would agree that no one "needs" the mounting rotation, like they
don't "need" the pixel cell size. But we normally like to be able to
report everything that we know about a sensor, and applications may
have a "list all the info about this sensor" feature. So it just seems
like a helpful thing to do.

>
> > mounting rotation from an application, though I would agree an
> > application should never need to use it.
> >
> > >
> > > I think these are the main discussion points, aren't they ?
> >
> > I'd like to make a proposal, if I may.
> >
> > 1. I'm happy to change the Rotation property, but at the same time we
> > need to fix the code so that the behaviour of transforms is not
> > changed. I would still like there to be a property so that an
> > application can query the mounting rotation if it wants to.
>
> I'm fine introducing one

Great!

>
> > 2. We change the default behaviour to apply no transform at all if it
> > can't apply the full transform. (Applications that don't want this can
> > easily sort themselves out now.)

I think this was the biggest concern, so I'm hoping this addresses it.

> >
> > At this point I think everyone has the functionality that they want.
> > Just to try and flesh out the application code a bit so that we are
> > forced to understand it, we have the following.
> >
> > Applications that want the simple behaviour:
> >
> > Transform requestedTransform; // the transform the user wants
> > Transform actualTransform; // what the user will get, from checking
> > the transform field after validate()
> >
> > if (requestedTransform != actualTransform)
> >     /* The application will have to apply a transform equivalent to
> > -actualTransform * requestedTransform */;
> >
> > Applications that want to control the transform that they apply (let's
> > call this the "leftoverTransform"):
> >
> > if (requestedTransform != actualTransform)
> >     requestedTransform *= -leftoverTransform; // now just try again
>
> I still don't get why people should do any of this calculations.

I hope I've addressed that above. There are other use cases, and it
would be great to give everyone the functionality they want.

>
> I don't expect anyone to write their own rotation libraries, but
> instead rely on the rendering libraries or on piping gstreamer
> elements to the capture output. Why should they calculate the
> "leftoverTransform" ? What is the use case for an application in doing
> this ?

I hope that's covered above. I'd like everyone to have access to
functionality they might want, whilst not impacting those who don't
wish to use it.

>
> >
> > But maybe double-check if that all feels right!!
>
> I think it's relevant to know how gstreamer/pipewire expects to
> deal with transforms, and more importantly what 'transform' display
> compositors supports natively.

Absolutely, and I think my proposal covers this, I believe everyone
gets to do exactly what they want. If there are still particular
things that don't work, or are difficult, then we should definitely
look into them.

>
> >
> > 3. We can consider swapping the transform in the configuration for an
> > "orientation" with a different set of names. We need to add
> > transform/orientation conversion functions so that I (and my users)
> > can still use it.
>
> See the newly added functions in transform.cpp

That seems fine. Also very happy to discuss all this at a later date!

Thanks!
David

>
> >
> > How does that sound?
> >
> > David
> >
> > >
> > > > Thanks
> > > > David
> > > >
> > > > > >
> > > > > > Thanks
> > > > > > David
> > > > > >
> > > > > > >
> > > > > > > >
> > > > > > > > >
> > > > > > > > > This series clearly breaks the application API as it removes a member from
> > > > > > > > > CameraConfiguration, so it should be introduced probably only when a new release
> > > > > > > > > is cut.
> > > > > > > >
> > > > > > > > I can't comment on this as I haven't really understood the change
> > > > > > > > here. Obviously we have many end users for whom an API change would be
> > > > > > > > unwelcome, but I suppose the change could be hidden from them.
> > > > > > > >
> > > > > > > > I apologise if there have been some discussions flying around that I
> > > > > > > > haven't followed sufficiently. I'm afraid I don't normally study
> > > > > > > > gstreamer related posts in detail, perhaps I need to try harder...
> > > > > > > >
> > > > > > > > Thanks
> > > > > > > > David
> > > > > > > >
> > > > > > > > >
> > > > > > > > > Tested on RPi imx219 with the newly introduced cam option, flip, mirror and
> > > > > > > > > rotation work as expected.
> > > > > > > > >
> > > > > > > > > Tested on Pinephone Pro, where sensors are 90/270 degrees rotated (libcamera
> > > > > > > > > performs no correction)
> > > > > > > > >
> > > > > > > > > Comments welcome of course.
> > > > > > > > >
> > > > > > > > > Thanks
> > > > > > > > >    j
> > > > > > > > >
> > > > > > > > > Jacopo Mondi (4):
> > > > > > > > >   libcamera: camera_sensor: Cache rotationTransform_
> > > > > > > > >   libcamera: camera: Introduce CameraConfiguration::orientation
> > > > > > > > >   libcamera: Move to use CameraConfiguration::orientation
> > > > > > > > >   apsp: cam: Add option to set stream orientation
> > > > > > > > >
> > > > > > > > >  Documentation/Doxyfile.in                     |   2 +
> > > > > > > > >  Documentation/rotation/flip-rotate-0.eps      | 170 +++++++++++++++
> > > > > > > > >  Documentation/rotation/flip-rotate-0.png      | Bin 0 -> 16488 bytes
> > > > > > > > >  Documentation/rotation/flip-rotate-180.eps    | 191 +++++++++++++++++
> > > > > > > > >  Documentation/rotation/flip-rotate-180.png    | Bin 0 -> 22198 bytes
> > > > > > > > >  Documentation/rotation/flip-rotate-270.eps    | 197 ++++++++++++++++++
> > > > > > > > >  Documentation/rotation/flip-rotate-270.png    | Bin 0 -> 21367 bytes
> > > > > > > > >  Documentation/rotation/flip-rotate-90.eps     | 195 +++++++++++++++++
> > > > > > > > >  Documentation/rotation/flip-rotate-90.png     | Bin 0 -> 21020 bytes
> > > > > > > > >  Documentation/rotation/rotate-0.eps           | 169 +++++++++++++++
> > > > > > > > >  Documentation/rotation/rotate-0.png           | Bin 0 -> 9086 bytes
> > > > > > > > >  Documentation/rotation/rotate-180.eps         | 189 +++++++++++++++++
> > > > > > > > >  Documentation/rotation/rotate-180.png         | Bin 0 -> 22182 bytes
> > > > > > > > >  Documentation/rotation/rotate-270.eps         | 196 +++++++++++++++++
> > > > > > > > >  Documentation/rotation/rotate-270.png         | Bin 0 -> 21016 bytes
> > > > > > > > >  Documentation/rotation/rotate-90.eps          | 196 +++++++++++++++++
> > > > > > > > >  Documentation/rotation/rotate-90.png          | Bin 0 -> 21528 bytes
> > > > > > > > >  include/libcamera/camera.h                    |  14 +-
> > > > > > > > >  include/libcamera/internal/camera_sensor.h    |   4 +-
> > > > > > > > >  include/libcamera/transform.h                 |   4 +
> > > > > > > > >  src/apps/cam/camera_session.cpp               |  17 ++
> > > > > > > > >  src/apps/cam/main.cpp                         |   5 +
> > > > > > > > >  src/apps/cam/main.h                           |   1 +
> > > > > > > > >  src/libcamera/camera.cpp                      |  54 ++++-
> > > > > > > > >  src/libcamera/camera_sensor.cpp               | 137 ++++++------
> > > > > > > > >  src/libcamera/pipeline/ipu3/ipu3.cpp          |   6 +-
> > > > > > > > >  src/libcamera/pipeline/rkisp1/rkisp1.cpp      |   8 +-
> > > > > > > > >  .../pipeline/rpi/common/pipeline_base.cpp     |   9 +-
> > > > > > > > >  src/libcamera/pipeline/simple/simple.cpp      |   6 +-
> > > > > > > > >  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |   4 +-
> > > > > > > > >  src/libcamera/pipeline/vimc/vimc.cpp          |   4 +-
> > > > > > > > >  src/libcamera/transform.cpp                   |  58 ++++++
> > > > > > > > >  32 files changed, 1739 insertions(+), 97 deletions(-)
> > > > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-0.eps
> > > > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-0.png
> > > > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-180.eps
> > > > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-180.png
> > > > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-270.eps
> > > > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-270.png
> > > > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-90.eps
> > > > > > > > >  create mode 100644 Documentation/rotation/flip-rotate-90.png
> > > > > > > > >  create mode 100644 Documentation/rotation/rotate-0.eps
> > > > > > > > >  create mode 100644 Documentation/rotation/rotate-0.png
> > > > > > > > >  create mode 100644 Documentation/rotation/rotate-180.eps
> > > > > > > > >  create mode 100644 Documentation/rotation/rotate-180.png
> > > > > > > > >  create mode 100644 Documentation/rotation/rotate-270.eps
> > > > > > > > >  create mode 100644 Documentation/rotation/rotate-270.png
> > > > > > > > >  create mode 100644 Documentation/rotation/rotate-90.eps
> > > > > > > > >  create mode 100644 Documentation/rotation/rotate-90.png
> > > > > > > > >
> > > > > > > > > --
> > > > > > > > > 2.40.1
> > > > > > > > >
Laurent Pinchart June 22, 2023, 3:14 p.m. UTC | #10
Hello,

I'm replying in the middle of the thread as the next e-mail trimmed part
of the conversation. I'll reply to the second part just after.

On Wed, Jun 21, 2023 at 03:14:28PM +0100, David Plowman via libcamera-devel wrote:
> On Wed, 21 Jun 2023 at 13:57, Jacopo Mondi wrote:
> > On Wed, Jun 21, 2023 at 01:37:11PM +0100, David Plowman wrote:
> > > On Wed, 21 Jun 2023 at 10:23, Jacopo Mondi wrote:
> > > > On Wed, Jun 21, 2023 at 10:08:25AM +0100, David Plowman wrote:
> > > > > Hi Jacopo
> > > > >
> > > > > Thanks for sending this proposal. I wasn't sure I really understood
> > > > > it, perhaps you can help me out!
> > > > >
> > > > > On Tue, 20 Jun 2023 at 15:29, Jacopo Mondi wrote:
> > > > > >
> > > > > > Hello everyone
> > > > > >
> > > > > > We have been discussing rotation/transformations for a long time, and
> > > > > > the currently implemented CameraConfiguration::transform behaviour has
> > > > > > proven to be confusing for several reasons: it was poorly documented,
> > > > > > translating it to something consumable for upper layer frameworks was
> > > > > > difficult and it behaved differently than any other field part of the
> > > > > > CameraConfiguration, as it was meant to tell libcamera "what to do" instead
> > > > > > of expressing what applications want.
> > > > >

The last point is slightly debatable. I can't tell if this is right, as
it depends how one interprets the confusing documentation :-) Maybe this
is the source of the disagreement. What is absolutely clear is that the
original intent, the documentation and the implementation don't align
properly. Whether it is the documentation or the code (or both) that we
need to fix is what we're discussing here.

I also agree with Jacopo that consuming the API in upper layers is
currently problematic.

> > > > > As far as I understand it, I don't believe this is correct, and the
> > > > > transform is where you say what you want.
> > > > >
> > > > > For example, on a Raspberry Pi I always set the transform to
> > > > > Transform::Identity because I want the images "the right way up". (I
> > > > > know that internally, it will calculate and apply a 180 rotation to
> > > > > counteract the sensor mounting rotation.) Does that sound right?

The documentation states

 * The transform is a user-specified 2D plane transform that will be applied
 * to the camera images by the processing pipeline before being handed to
 * the application. This is subsequent to any transform that is already
 * required to fix up any platform-defined rotation.

I can't interpret the first sentence as anything else than a
transformation applied on the image, I don't see how it can be
construed as meaning the desired orientation of the image at the output
of the pipeline.

Of course, for 180° rotations, the two are equivalent. The fact that 90°
and 270° support has been implemented in
CameraSensor::validateTransform() but not tested (due to lack of
hardware support) only contributes to additional confusion as what the
original intent was.

The second sentence sounds very confusing to me.

> > > >
> > > > Yes, now that we correct properties::Rotation to 0 as the library
> > > > compensate the mounting rotation behind your back, that's what you
> > > > get.
> > > >
> > > > Before we had properties::Rotation = 180; transform = Identity and you
> > > > got an upright image. Confusing, isn't it ?
> > >
> > > I understood the previous arrangement, but the new one seems
> > > counter-intuitive to me.
> > >
> > > On a Raspberry Pi, I expect the properties::Rotation to report 180
> > > degrees because the sensor is mounted upside down. Then I can ask for
> > > Transform::Identity and internally a 180 degree rotation will be
> > > applied to give me what I asked for. I understand this.
> > >
> > > Are we saying that now the rotation will report 0 degrees? Will it now
> > > always report 0 degrees, whatever the sensor and platform? That would
> > > obviously break things...
> >
> > So you're an app, you read properties::Rotation and you get back 180.
> > Then you star streaming and you get a stream oriented "correctly".
> >
> > Isn't this wrong ?
> 
> I think a lot of this hinges on what the Rotation property is.
> 
> It _used_ to be the mounting rotation of the sensor (is that right?),
> at which point everything was as I have described it. But I think it
> is being changed now, which is indeed something that was under
> discussion in that thread back in January. We're free to do that, but
> perhaps there have been some unintended consequences?

This stems from the fact that you wanted (if I recall correctly) the
platform to auto-correct the mounting rotation when possible. Exposing
to the user the fact that the sensor is mounted upside-down, *and*
auto-correcting this when possible, isn't a behaviour I like. There are
too many implicit assumptions, it's just confusing. If you want
auto-correction, it needs to be completely transparent, libcamera needs
to hide the fact that the sensor is mounted upside-down from the
application in that case.

I want the API to be clear and simple when no auto-correction is
applied. I'm OK with auto-correction being added on top if it's totally
transparent for applications. This means that auto-correction shouldn't
even need to be mentioned in the API specification, as applications
shouldn't have to care.

> > > > > If the problem is actually to do with documentation, I would always
> > > > > agree that it can be improved, and am very happy to help!

Documentation surely needs to be improved, regardless of what we decide
to do with the code.

> > > > > > For these reasons this series proposes to replace the usage to Transform
> > > > > > in the public API in favour of a new CameraConfiguration::Orientation type,
> > > > > > defined based on the EXIF specification, tag 274, 'orientation'. For reference
> > > > > > this is the same as implemented by gstreamer:
> > > > > > https://gstreamer.freedesktop.org/documentation/gstreamer/gsttaglist.html?gi-language=c#GST_TAG_IMAGE_ORIENTATION
> > > > >
> > > > > I must confess to not liking the term "orientation". There is such a
> > > > > thing as a "2D plane transformation" and I'd expect to refer to them
> > > > > as transformations or maybe transforms for short. I don't understand
> > > > > what an "orientation" is without reading extra documentation. Are
> > > > > "orientations" composable? Is there such a thing as an "inverse
> > > > > orientation"?

"Orientation" is a term widely used in other frameworks and
specifications, including EXIF, and GStreamer. While I don't rule out
finding a better name, it doesn't seem bad to me.

"Transform" has a strong implication that it specifies what
transformation is applied to the image, not what orientation the image
has at the output of the pipeline. If you prefer the "transform" name,
then the meaning of the parameter has to match the name.

> > > > I'm fine bikeshedding on the name. What matters to me is that now we
> > > > conform to an existing specification adopeted by most frameworks
> > > > instead of defining our own Transform.
> > >
> > > I'd like to find something that works for all libcamera users.
> > >
> > > The Transform class allows composition, inverse operations and so on,
> > > the idea being that users can calculate for themselves what to ask for
> > > if the default behaviour isn't what they want. I did comment that
> > > making it easier for folks to get a specific behaviour might be
> > > something to look into.

Helpers exposed by the libcamera core API need to have a purpose for
libcamera. If the only purpose of a Transform class, as used by
applications, is to compose transformations to calculate how to
configure a compositor based on the orientation of the image produced by
libcamera, then that helper doesn't belong to the libcamera core API. I
don't mind having it in a conveniency utility library though.

I don't want to digress too much, but some context will be useful to
understand my point here. With the need to support more languages in the
future, as well as the ABI stability requirements, I expect we will
strip down the core libcamera API from helpers. The core API will likely
be specified using some form of IDL, and the ABI will be plain C. The
language bindings, including C++ bindings, will be implemented (likely
auto-generated from the API IDL) based on the C ABI. Utilities that are
not part of the core API will be implemented directly in the language
bindings. For instance, we will retain geometry structures to represent
points, sizes and rectangles, but the core API will not include the
geometry helpers. The existing geometry helpers will become internal to
libcamera (as I expect the libcamera internal implementation to remain
in C++, and the helpers have shown to be quite useful). A separate
version of the helpers will also likely be provided by the C++ bindings
and other language bindings. If any particular language has native
classes to represent the geometry primitives, then we will likely use
those.

I don't want to discuss this plan in details as part of this mail
thread, but let's keep it in mind for the libcamera core API design.

> > > > > > The newly introduced CameraConfiguration::orientation replaces the
> > > > > > existing CameraConfiguration::tranform, and it is meant for application to
> > > > > > express how they would like the images to be oriented, not to tell libcamera
> > > > > > what to do. As an example, passing in 'rotation0' means that the application
> > > > > > expects the images to be rotated upright, and doesn't tell libcamera not to
> > > > > > apply any rotation like passing in "Transform::Identity" did.
> > > > >
> > > > > My understanding is that passing "Transform::Identity" has always
> > > > > meant that the application wants its images "upright". (Are there
> > > > > cases where this doesn't happen?)

My understanding, as noted above, is that passing "Transform::Identity"
has always meant that the application wants the images in the
orientation produced by the sensor (or, to be precise, the orientation
of the pixel array, as the h/v flip are applied in the sensor too). If
we want CameraConfiguration::transform  to indicate what orientation the
output image should have (and it seems we all agree on that), then it
should be named differently.

> > > > As we have auto-correction in place, yes.
> > >
> > > My understanding is that everything got "auto-corrected" before. I'm
> > > still struggling to understand how it seems to be broken now.
> >
> > It is not 'broken'. Simply nobody got how thing worked in practice and
> > the existing behaviour (when it comes to adjusting ::transform) is not
> > what most users expects. See below
> >
> > > > > > The value CameraConfiguration::orientation is set to after a validate() also
> > > > > > differs in meaning, as instead of reporting "what applications have to do
> > > > > > to obtain what they originally asked for" it simply reports the actual
> > > > > > orientation of the stream: this means that if libcamera cannot fully satisfy the
> > > > > > user request it will set ::orientation to report the native images rotation
> > > > > > and the CameraConfiguration::status will be set to Adjusted.
> > > > >
> > > > > I believe this to be the current behaviour of the transform field.
> > > >
> > > > Not really, see below
> > > >
> > > > > > Handling of 90 and 270 degrees rotation has also changed: as the camera sensor
> > > > > > cannot correct rotations that include a transposition, requests for a 90/270
> > > > > > degrees are ignored, and cameras with a mounting rotation of 90/270  degrees
> > > > > > are never re-oriented. This makes it clear and less confusing for applications
> > > > > > that they have to deal with correction fully by themselves. As an example, with
> > > > > > the current implementation if the application requires a Rot270 (HFlip +
> > > > > > Transpose) libcamera will do the HFlip and leave transposition to the upper
> > > > > > layers. There is no clear advantage in doing so, and display compositors are
> > > > > > better suited for handling transpositions and flipping in a single pass instead
> > > > > > of having the library try to handle part of that.
> > > > >
> > > > > Again, I'm not really understanding this.
> > > > >
> > > > > The current behaviour is such that, if it can't give you the
> > > > > transformation that you ask for (on account of not being able to do
> > > > > transposition), then it guarantees to give you what you requested
> > > > > _apart_ from the transposition. So the application only has one case
> > > > > to deal with - applying a transpose. It doesn't have to worry about
> > > > > the other 3 "transforms-that-I-can't-handle",

That only holds true if the camera can apply h/v flip. If it can't, the
application will need to handle the 90° and 270° rotations, so generic
applications will always have to support those cases in addition to
transposition. It can thus be argued that having to support 90°
rotation, 270° rotation and transposition is more complicated for the
application than having to support rotations only.

> > > > > thereby making life easier for the application.
> > > >
> > > > The _apart_ is the key here.
> > > >
> > > > If you currently ask for Rot270 the library will do an HFlip and set
> > > > transform to Transpose (ie what applications still have to do to
> > > > obtain Rot270). This is not how the other fields of
> > >
> > > What it _should_ do is set the transform field to the actual transform
> > > that the user will perceive the images to have. I believe this to be
> > > "proper" libcamera configuration behaviour. I'm not understanding why
> > > things are different now.
> >
> > Let's start with the fact that "transpose" is a concept that is
> > introduced in Transform and it's not part of the EXIF standard which
> > gstreamer/pipewire expect to use.

Note that "rotation" isn't either. EXIF supports the 8 combinations of
h/v flip and tranpose, assigning them clear values, but doesn't use the
"rotation", "transpose" or "flip" names to document them. I'm fine using
those names in the libcamera API.

> > Again, you set transform = rot270, libcamera does HFLIP and you have
> > to do Transpose, correct ?
> 
> That's one option. Or, if you want to do a "rot270" in the application
> you just set the transform to "userTransform * -Transform::rot270".
> 
> As I've said, I'm really happy to try and make this easier for people.
> 
> > For compositor this is more work that just do "rot270"

Do all display engines that support rotation at the hardware
level be able to apply transposition without also flipping (that is,
pure transposition that isn't a 90° or 270° rotation) ? Do all
compositors that support rotation expose a pure transpose transformation
in their API ? And the other way around, are there hardware devices or
compositor APIs that can transpose but can't flip ?

Unless I'm mistaken, there's no clear and universal answer to these
questions. I thus don't want to assume what transformations the
consumers of the images will be able to apply easily and efficiently.
I'm missing the reason why libcamera should consider that producing an
image that requires rotation by 90° in the application is better (or
worse) than producing an image that requires transposing. If there's no
option that is intrinsically better, then I prefer aiming for the
simpler and easier to understand behaviour, which is to let the
application deal with it.

> > > Beyond this, the current behaviour is to leave the application to do
> > > the transpose, but that's just a choice. The point of transforms is
> > > that it's easy to use them to produce something different if you want.
> > > I don't mind looking at that if we can make it easier for everyone to
> > > get their preferred behaviour, which I suggested in my original reply.
> >
> > I don't get why we should be imposing other frameworks/apps to learn
> > Transpose when there is a standard to follow to be honest.
> >
> > > > CameraConfiguration work, and mostly, applying a flip and letting apps
> > > > only deal with transpose is an "optimization" that might actually make
> > > > life harder for apps, as display compositors usually have means to
> > > > deal with known rotations, while in this case you only ask them to do
> > > > a Transpose, which might require them more work ?
> > > >
> > > > In any case, I think it's saner if the library cannot do what it has
> > > > been asked for to set ::transform to what the stream orientation
> > > > actually is, instead of telling apps what "they still have to do".
> > >
> > > I don't understand the repeated statement that the transform is
> > > telling apps "what they still have to do". That was never what it
> > > meant, unless it got changed somewhere.
> >
> > Isn't it ? I'm not repeating the Rot270 example, but applications are
> > left with ::transform = Transpose. Isn't this what they have to do to
> > obtain Rot270 once HFlip has been done by libcamera ?
> >
> > So they have to "I asked for Rot270, I got back Transpose. Ah!
> > libcamera did HFlip for me, so now I have to do Transpose."
> >
> > Is it what happens ?
> 
> The code that I am currently running returns HFlip as the transform
> when I ask for Rot270, which is indeed what the application will get.
> 
> In this case yes, I still have to do a transpose. But if I had, for
> example, code that does a 90 degree rotation, I'd ask for "Rot270 *
> -Rot90" instead. The transform field would come back as HVFlip telling
> me that my images will be upside down, and all I have to do is call my
> rotate 90 function.
> 
> The idea is that it should be simple to get whatever behaviour you
> want, but I'm very open to trying to make this easier.
> 
> > > > > Because transforms are composable, it's easy to calculate what
> > > > > transform to ask for to get a different behaviour (for example, do
> > > > > nothing at all if there's a transpose involved). I could imagine a
> > > > > slightly different API might be helpful, perhaps where you get to say
> > > > > not only "what you want", but also what you want the "residual
> > > > > transform" to be (being the one the application has to apply after) if
> > > > > it can't deliver what you wanted. Would that be useful?

That's too complex I think. If we want to extend the API (not merely
change it), we could report the transformation capabilities of the
camera as a property to let applications discover what can be done. It
will then be the responsibility of the application to ask for something
that is supported, and we won't have to design complex heuristics to
find the "best" option when the applications asks for an unsupported
transformation. The API will be simpler to document, and simpler to
understand.

> > > > I think it's better to leave the stream orientation unmodified if it
> > > > cannot be corrected. This matches the behaviour of the other
> > > > CameraConfiguration fields, and seems easier to deal with from
> > > > applications.
> > >
> > > I feel we ought to be able to come up with something that makes it
> > > convenient for everyone to get what they want.
> > >
> > > > > I also recall that there was some discussion a while back about the
> > > > > precise orientation of the rotations which could affect some of the
> > > > > details, but I don't think it was ever concluded. I don't know if
> > > > > that's causing any problems?
> > > >
> > > > Sorry, I don't remember such discussion. Any link ?
> > >
> > > I think the last email I sent on the subject was this one, after which
> > > things went quiet:
> > > https://lists.libcamera.org/pipermail/libcamera-devel/2023-January/036445.html
> > > Actually it seems to echo quite a bit of what I say above.
> 
> I think one of the sticking points for me is that I don't really
> understand what the problems are. Perhaps we could revisit some of
> this so that I can be more helpful?
> 
> > > > > > This series clearly breaks the application API as it removes a member from
> > > > > > CameraConfiguration, so it should be introduced probably only when a new release
> > > > > > is cut.

The series can be merged when ready, whenever that will be. There's no
need to tie it to a "release".

> > > > > I can't comment on this as I haven't really understood the change
> > > > > here. Obviously we have many end users for whom an API change would be
> > > > > unwelcome, but I suppose the change could be hidden from them.

I'm afraid we will have many API changes, much more invasive than this
one, in the future before we stabilize the API.

> > > > > I apologise if there have been some discussions flying around that I
> > > > > haven't followed sufficiently. I'm afraid I don't normally study
> > > > > gstreamer related posts in detail, perhaps I need to try harder...
> > > > >
> > > > > > Tested on RPi imx219 with the newly introduced cam option, flip, mirror and
> > > > > > rotation work as expected.
> > > > > >
> > > > > > Tested on Pinephone Pro, where sensors are 90/270 degrees rotated (libcamera
> > > > > > performs no correction)
> > > > > >
> > > > > > Comments welcome of course.
> > > > > >
> > > > > > Thanks
> > > > > >    j
> > > > > >
> > > > > > Jacopo Mondi (4):
> > > > > >   libcamera: camera_sensor: Cache rotationTransform_
> > > > > >   libcamera: camera: Introduce CameraConfiguration::orientation
> > > > > >   libcamera: Move to use CameraConfiguration::orientation
> > > > > >   apsp: cam: Add option to set stream orientation
> > > > > >
> > > > > >  Documentation/Doxyfile.in                     |   2 +
> > > > > >  Documentation/rotation/flip-rotate-0.eps      | 170 +++++++++++++++
> > > > > >  Documentation/rotation/flip-rotate-0.png      | Bin 0 -> 16488 bytes
> > > > > >  Documentation/rotation/flip-rotate-180.eps    | 191 +++++++++++++++++
> > > > > >  Documentation/rotation/flip-rotate-180.png    | Bin 0 -> 22198 bytes
> > > > > >  Documentation/rotation/flip-rotate-270.eps    | 197 ++++++++++++++++++
> > > > > >  Documentation/rotation/flip-rotate-270.png    | Bin 0 -> 21367 bytes
> > > > > >  Documentation/rotation/flip-rotate-90.eps     | 195 +++++++++++++++++
> > > > > >  Documentation/rotation/flip-rotate-90.png     | Bin 0 -> 21020 bytes
> > > > > >  Documentation/rotation/rotate-0.eps           | 169 +++++++++++++++
> > > > > >  Documentation/rotation/rotate-0.png           | Bin 0 -> 9086 bytes
> > > > > >  Documentation/rotation/rotate-180.eps         | 189 +++++++++++++++++
> > > > > >  Documentation/rotation/rotate-180.png         | Bin 0 -> 22182 bytes
> > > > > >  Documentation/rotation/rotate-270.eps         | 196 +++++++++++++++++
> > > > > >  Documentation/rotation/rotate-270.png         | Bin 0 -> 21016 bytes
> > > > > >  Documentation/rotation/rotate-90.eps          | 196 +++++++++++++++++
> > > > > >  Documentation/rotation/rotate-90.png          | Bin 0 -> 21528 bytes
> > > > > >  include/libcamera/camera.h                    |  14 +-
> > > > > >  include/libcamera/internal/camera_sensor.h    |   4 +-
> > > > > >  include/libcamera/transform.h                 |   4 +
> > > > > >  src/apps/cam/camera_session.cpp               |  17 ++
> > > > > >  src/apps/cam/main.cpp                         |   5 +
> > > > > >  src/apps/cam/main.h                           |   1 +
> > > > > >  src/libcamera/camera.cpp                      |  54 ++++-
> > > > > >  src/libcamera/camera_sensor.cpp               | 137 ++++++------
> > > > > >  src/libcamera/pipeline/ipu3/ipu3.cpp          |   6 +-
> > > > > >  src/libcamera/pipeline/rkisp1/rkisp1.cpp      |   8 +-
> > > > > >  .../pipeline/rpi/common/pipeline_base.cpp     |   9 +-
> > > > > >  src/libcamera/pipeline/simple/simple.cpp      |   6 +-
> > > > > >  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |   4 +-
> > > > > >  src/libcamera/pipeline/vimc/vimc.cpp          |   4 +-
> > > > > >  src/libcamera/transform.cpp                   |  58 ++++++
> > > > > >  32 files changed, 1739 insertions(+), 97 deletions(-)
> > > > > >  create mode 100644 Documentation/rotation/flip-rotate-0.eps
> > > > > >  create mode 100644 Documentation/rotation/flip-rotate-0.png
> > > > > >  create mode 100644 Documentation/rotation/flip-rotate-180.eps
> > > > > >  create mode 100644 Documentation/rotation/flip-rotate-180.png
> > > > > >  create mode 100644 Documentation/rotation/flip-rotate-270.eps
> > > > > >  create mode 100644 Documentation/rotation/flip-rotate-270.png
> > > > > >  create mode 100644 Documentation/rotation/flip-rotate-90.eps
> > > > > >  create mode 100644 Documentation/rotation/flip-rotate-90.png
> > > > > >  create mode 100644 Documentation/rotation/rotate-0.eps
> > > > > >  create mode 100644 Documentation/rotation/rotate-0.png
> > > > > >  create mode 100644 Documentation/rotation/rotate-180.eps
> > > > > >  create mode 100644 Documentation/rotation/rotate-180.png
> > > > > >  create mode 100644 Documentation/rotation/rotate-270.eps
> > > > > >  create mode 100644 Documentation/rotation/rotate-270.png
> > > > > >  create mode 100644 Documentation/rotation/rotate-90.eps
> > > > > >  create mode 100644 Documentation/rotation/rotate-90.png
David Plowman June 23, 2023, 8:26 a.m. UTC | #11
Hi everyone

Thanks for that message. I think it's very helpful to quote the source
documentation:

 * The transform is a user-specified 2D plane transform that will be applied
 * to the camera images by the processing pipeline before being handed to
 * the application. This is subsequent to any transform that is already
 * required to fix up any platform-defined rotation.

What this is trying to say, badly, is that the transform you request
is applied after ("subsequent to") fixing up the platform rotation. So
you do indeed get auto-correction. This is also the actual behaviour
of the code on the Pi, has been for, I don't know, nearly 2 years? At
this point, the earth does indeed get my official permission to
swallow me up for not writing something that was less prone to
misinterpretation,

But I think this does leave us in a position where large amounts of
this thread are a mixture of not very helpful or even plain confusing,
and with people's permission, I'd honestly like to start again, with
everyone on the same page.

You might have noticed that "orientations" were really bugging me as
to what they were, and how one could use them. I've managed to resolve
these problems in my own mind and think that there's a very simple
connection between "orientations" and "transforms" that I would be OK
to implement and use. I think our next attempt at this discussion
should start by being clear on our requirements, and then we can look
at how "orientations" and "transforms" can get us there.

(Rest of thread purposefully deleted... hope that's ok!)

Thanks!
David
Robert Mader June 23, 2023, 11:04 a.m. UTC | #12
Hi David, thanks for initiative to bring us all on the same page.

 From what I understand, this series aims to do two things:


1.: Make the public API follow the EXIF specification

I personally, having implemented the existing API in Pipewire and, still 
pending, the Gstreamer element, am rather neutral on this, as the new 
and old values have a bijective relationship. All I care about that it's 
easy for apps to transition and support both APIs for at least a short 
while.


2.: Clarifying the expected behavior and fixing existing inconsistencies.

The main issue with the existing implementation I personally have been 
running into, that is fixed by this series, was the returned Transform 
value from `validate()`.

To give an example, consider the following two cases:

  * A camera mounted 90 deg rotated, a config requesting
    `Transform::Identity`
  * No camera rotation, a config requesting `Transform::rot90`

In the first case (typical for Linux phones like the Pinephone (Pro) and 
Librem5) the returned value will be `Transform::rot270` (IIRC) and the 
user can use that to adapt the output downstream.

In the second case, the returned value will be `Transform::Identity` and 
the user has to remember that it asked for something different.

Combining a non-identity mounting rotation and a non-identity requested 
transform thus essentially leads to random results and makes a clean 
implementation almost impossible.

I personally like the proposed behavior - either you get the requested 
result or you have to apply the combined transform yourself - a lot.

 From what I understand there are only two open questions here:

 1. Should the sensor do partial transforms?
 2. Do we really want/need the new `Orientation` enum or could/should we
    instead with `Transform` and just update/clarify the expected results?


Hope you agree that these are the issues to agree on.

Thanks everyone and cheers,

Robert

On 23.06.23 10:26, David Plowman via libcamera-devel wrote:
> Hi everyone
>
> Thanks for that message. I think it's very helpful to quote the source
> documentation:
>
>   * The transform is a user-specified 2D plane transform that will be applied
>   * to the camera images by the processing pipeline before being handed to
>   * the application. This is subsequent to any transform that is already
>   * required to fix up any platform-defined rotation.
>
> What this is trying to say, badly, is that the transform you request
> is applied after ("subsequent to") fixing up the platform rotation. So
> you do indeed get auto-correction. This is also the actual behaviour
> of the code on the Pi, has been for, I don't know, nearly 2 years? At
> this point, the earth does indeed get my official permission to
> swallow me up for not writing something that was less prone to
> misinterpretation,
>
> But I think this does leave us in a position where large amounts of
> this thread are a mixture of not very helpful or even plain confusing,
> and with people's permission, I'd honestly like to start again, with
> everyone on the same page.
>
> You might have noticed that "orientations" were really bugging me as
> to what they were, and how one could use them. I've managed to resolve
> these problems in my own mind and think that there's a very simple
> connection between "orientations" and "transforms" that I would be OK
> to implement and use. I think our next attempt at this discussion
> should start by being clear on our requirements, and then we can look
> at how "orientations" and "transforms" can get us there.
>
> (Rest of thread purposefully deleted... hope that's ok!)
>
> Thanks!
> David
David Plowman June 26, 2023, 8:59 a.m. UTC | #13
Hi Robert, everyone

Thanks for the message. I think that's helpful and makes sense to me.
It seems to me that a large part of this is to clarify what's meant to
happen, to resolve inconsistencies (and bugs!), and to make sure that
everyone gets what they need. To try and summarise my own
requirements:

1. I want any transform to be auto-corrected, that is, for any
mounting rotation to be corrected transparently for me. However, I
still think we should report what the mounting rotation is.

2. When it's possible to produce the requested transform, then
obviously I want it to do that!

3. When it's not possible to deliver the requested transform, then I
don't really mind what we choose to do, contingent on having
requirement 4. For example, I would be fine with libcamera doing
nothing at all, so that the "actual" transform returned would be the
sensor mounting rotation (does everyone agree that this is what would
happen?).

4. I want to be able to use the libcamera classes to compare what I
requested versus what I got, so that I can figure out what I need to
do. This should have absolutely no impact on the libcamera API (which
is principally a question of what happens to the transform during
validate()). Using the libcamera classes to work out what to do is
entirely optional.

A few final observations:

* I'm fine with changing the meaning of the "rotation" property.
Though we need to ensure this doesn't change the existing behaviour
(on the Pi, can't speak for certain about anything else!), and as
noted above, I think we should report the mounting rotation in another
way.

* I'm fine with introducing an Orientation if that's helpful to
people. If we put the orientation into the camera configuration
(replacing the transform) then I want the orientation class to
interact correctly with the transform class so that we still have
requirement 4. In this case the orientation class also needs to be
fully implemented in the Python bindings, as transforms are.

* I still have this niggle in my mind that the sense of the rotations
was not settled in our previous discussions. We need to resolve this.

That's everything I can think of at the minute. Does it seem reasonable?

Thanks!
David

On Fri, 23 Jun 2023 at 12:05, Robert Mader via libcamera-devel
<libcamera-devel@lists.libcamera.org> wrote:
>
> Hi David, thanks for initiative to bring us all on the same page.
>
> From what I understand, this series aims to do two things:
>
>
> 1.: Make the public API follow the EXIF specification
>
> I personally, having implemented the existing API in Pipewire and, still pending, the Gstreamer element, am rather neutral on this, as the new and old values have a bijective relationship. All I care about that it's easy for apps to transition and support both APIs for at least a short while.
>
>
> 2.: Clarifying the expected behavior and fixing existing inconsistencies.
>
> The main issue with the existing implementation I personally have been running into, that is fixed by this series, was the returned Transform value from `validate()`.
>
> To give an example, consider the following two cases:
>
> A camera mounted 90 deg rotated, a config requesting `Transform::Identity`
> No camera rotation, a config requesting `Transform::rot90`
>
> In the first case (typical for Linux phones like the Pinephone (Pro) and Librem5) the returned value will be `Transform::rot270` (IIRC) and the user can use that to adapt the output downstream.
>
> In the second case, the returned value will be `Transform::Identity` and the user has to remember that it asked for something different.
>
> Combining a non-identity mounting rotation and a non-identity requested transform thus essentially leads to random results and makes a clean implementation almost impossible.
>
> I personally like the proposed behavior - either you get the requested result or you have to apply the combined transform yourself - a lot.
>
> From what I understand there are only two open questions here:
>
> Should the sensor do partial transforms?
> Do we really want/need the new `Orientation` enum or could/should we instead with `Transform` and just update/clarify the expected results?
>
>
> Hope you agree that these are the issues to agree on.
>
> Thanks everyone and cheers,
>
> Robert
>
> On 23.06.23 10:26, David Plowman via libcamera-devel wrote:
>
> Hi everyone
>
> Thanks for that message. I think it's very helpful to quote the source
> documentation:
>
>  * The transform is a user-specified 2D plane transform that will be applied
>  * to the camera images by the processing pipeline before being handed to
>  * the application. This is subsequent to any transform that is already
>  * required to fix up any platform-defined rotation.
>
> What this is trying to say, badly, is that the transform you request
> is applied after ("subsequent to") fixing up the platform rotation. So
> you do indeed get auto-correction. This is also the actual behaviour
> of the code on the Pi, has been for, I don't know, nearly 2 years? At
> this point, the earth does indeed get my official permission to
> swallow me up for not writing something that was less prone to
> misinterpretation,
>
> But I think this does leave us in a position where large amounts of
> this thread are a mixture of not very helpful or even plain confusing,
> and with people's permission, I'd honestly like to start again, with
> everyone on the same page.
>
> You might have noticed that "orientations" were really bugging me as
> to what they were, and how one could use them. I've managed to resolve
> these problems in my own mind and think that there's a very simple
> connection between "orientations" and "transforms" that I would be OK
> to implement and use. I think our next attempt at this discussion
> should start by being clear on our requirements, and then we can look
> at how "orientations" and "transforms" can get us there.
>
> (Rest of thread purposefully deleted... hope that's ok!)
>
> Thanks!
> David