[libcamera-devel] [PATCH v5 02/12] libcamera: camera: Introduce Orientation
Jacopo Mondi
jacopo.mondi at ideasonboard.com
Thu Oct 19 10:19:11 CEST 2023
Hi Laurent
On Wed, Oct 18, 2023 at 09:37:10PM +0300, Laurent Pinchart via libcamera-devel wrote:
> Hi Jacopo,
>
> Thank you for the patch.
>
> On Fri, Sep 01, 2023 at 05:02:05PM +0200, Jacopo Mondi via libcamera-devel wrote:
> > Introduce the Orientation enumeration which describes the possible 2D
> > transformations that can be applied to an image using two basic plane
> > transformations.
> >
> > Add to the CameraConfiguration class a new member 'orientation' which is
> > used to specify the image orientation in the memory buffers delivered to
> > applications.
> >
> > The enumeration values follow the ones defined by the EXIF specification at
> > revision 2.32, Tag 274 'orientation'.
> >
> > The newly introduced field is meant to replace
> > CameraConfiguration::transform which is not removed yet not to break
> > compilation.
> >
> > Signed-off-by: Jacopo Mondi <jacopo.mondi at ideasonboard.com>
> > Reviewed-by: David Plowman <david.plowman at raspberrypi.com>
> > ---
> > include/libcamera/camera.h | 2 +
> > include/libcamera/meson.build | 1 +
> > include/libcamera/orientation.h | 28 ++++++++++++
> > src/libcamera/camera.cpp | 18 +++++++-
> > src/libcamera/meson.build | 1 +
> > src/libcamera/orientation.cpp | 78 +++++++++++++++++++++++++++++++++
> > 6 files changed, 127 insertions(+), 1 deletion(-)
> > create mode 100644 include/libcamera/orientation.h
> > create mode 100644 src/libcamera/orientation.cpp
> >
> > diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
> > index 004bc89455f5..6e9342773962 100644
> > --- a/include/libcamera/camera.h
> > +++ b/include/libcamera/camera.h
> > @@ -19,6 +19,7 @@
> > #include <libcamera/base/signal.h>
> >
> > #include <libcamera/controls.h>
> > +#include <libcamera/orientation.h>
> > #include <libcamera/request.h>
> > #include <libcamera/stream.h>
> > #include <libcamera/transform.h>
> > @@ -67,6 +68,7 @@ public:
> > std::size_t size() const;
> >
> > Transform transform;
> > + Orientation orientation;
> >
> > protected:
> > CameraConfiguration();
> > diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build
> > index 408b7acf152c..a24c50d66a82 100644
> > --- a/include/libcamera/meson.build
> > +++ b/include/libcamera/meson.build
> > @@ -12,6 +12,7 @@ libcamera_public_headers = files([
> > 'framebuffer_allocator.h',
> > 'geometry.h',
> > 'logging.h',
> > + 'orientation.h',
> > 'pixel_format.h',
> > 'request.h',
> > 'stream.h',
> > diff --git a/include/libcamera/orientation.h b/include/libcamera/orientation.h
> > new file mode 100644
> > index 000000000000..63ac4aba07ce
> > --- /dev/null
> > +++ b/include/libcamera/orientation.h
> > @@ -0,0 +1,28 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2023, Ideas On Board Oy
> > + *
> > + * orientation.h - Image orientation
> > + */
> > +
> > +#pragma once
> > +
> > +#include <iostream>
> > +
> > +namespace libcamera {
> > +
> > +enum class Orientation {
> > + /* EXIF tag 274 starts from '1' */
> > + rotate0 = 1,
>
> Enumerators should start with an uppercase letter.
>
> > + rotate0Flip,
> > + rotate180,
> > + rotate180Flip,
> > + rotate90Flip,
> > + rotate270,
>
> I think this should be rotate90.
>
Ah!
The EXIF tag 6 is described as
The 0th row is the visual right-hand side of the image, and the 0th
column is the visual top.
> > + rotate270Flip,
> > + rotate90,
>
> And here, rotate270.
EXIF tag 8
The 0th row is the visual left-hand side of the image, and the 0th
column is the visual bottom.
I'll correct both
>
> > +};
> > +
> > +std::ostream &operator<<(std::ostream &out, const Orientation &orientation);
> > +
> > +} /* namespace libcamera */
> > diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
> > index 0eecee766f00..d4ad4a553752 100644
> > --- a/src/libcamera/camera.cpp
> > +++ b/src/libcamera/camera.cpp
> > @@ -160,7 +160,8 @@ LOG_DECLARE_CATEGORY(Camera)
> > * \brief Create an empty camera configuration
> > */
> > CameraConfiguration::CameraConfiguration()
> > - : transform(Transform::Identity), config_({})
> > + : transform(Transform::Identity), orientation(Orientation::rotate0),
> > + config_({})
> > {
> > }
> >
> > @@ -404,6 +405,21 @@ CameraConfiguration::Status CameraConfiguration::validateColorSpaces(ColorSpaceF
> > * may adjust this field at its discretion if the selection is not supported.
> > */
> >
> > +/**
> > + * \var CameraConfiguration::orientation
> > + * \brief The desired orientation of the images produced by the camera
> > + *
> > + * The orientation field is a user-specified 2D plane transformation that
>
> s/field/member/
>
> The word "field" in the C++ specification is used for bit-field only.
> "fields" of a class or structure are named "members" (or "data member"
> if you want to differentiate them from member functions).
>
> > + * specifies how the application wants the camera images to be rotated in
> > + * the memory buffers.
>
> As the whole point of this series is to move from transform to
> orientation, can we try to avoid using "transformation" to define the
> orientation ? How about the following ?
>
> * The orientation specifies how the application wants images to be oriented in
> * memory buffers. The camera will rotate and flip images as appropriate to
> * obtain the desired orientation.
>
I think there's difference between the Transform we want to move away
from and a 2d plane transformation, which is a specific term, but if
just seeing "transformation" spelled out irritates you.. ok...
> > + *
> > + * If the application requested orientation cannot be obtained the validate()
>
> s/obtained/obtained,/
>
> > + * function will Adjust this field to the actual orientation of the images
>
> s/this field/the orientation member/
>
> > + * as produced by the camera pipeline.
>
> I recall we discussed that, in this case, the camera shouldn't try to
> pick a value it considers the closest to the requested orientation, but
> don't transform the image at all. Is that right ? If so, let's document
> it explicitly:
Yes, that's what the proposed documentation says
* If the application requested orientation cannot be obtained the validate()
* function will Adjust this field to the actual orientation of the images
* as produced by the camera pipeline.
>
> * If the orientation requested by the application cannot be obtained, the
> * camera will not rotate or flip the images, and the validate() function will
> * Adjust this value to the native image orientation produced by the camera.
>
The two says the same thing, but.. ok
> > + *
> > + * By default the orientation field is set to Orientation::rotate0.
>
> s/field/member/
>
> > + */
> > +
> > /**
> > * \var CameraConfiguration::config_
> > * \brief The vector of stream configurations
> > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> > index b24f82965764..d0e26f6b4141 100644
> > --- a/src/libcamera/meson.build
> > +++ b/src/libcamera/meson.build
> > @@ -34,6 +34,7 @@ libcamera_sources = files([
> > 'mapped_framebuffer.cpp',
> > 'media_device.cpp',
> > 'media_object.cpp',
> > + 'orientation.cpp',
> > 'pipeline_handler.cpp',
> > 'pixel_format.cpp',
> > 'process.cpp',
> > diff --git a/src/libcamera/orientation.cpp b/src/libcamera/orientation.cpp
> > new file mode 100644
> > index 000000000000..f2ee14dd4182
> > --- /dev/null
> > +++ b/src/libcamera/orientation.cpp
> > @@ -0,0 +1,78 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2023, Ideas On Board Oy
> > + *
> > + * orientation.cpp - Image orientation
> > + */
> > +
> > +#include <libcamera/orientation.h>
> > +
> > +#include <array>
> > +#include <string>
> > +
> > +/**
> > + * \file libcamera/orientation.h
> > + * \brief Image orientation definition
> > + */
> > +
> > +namespace libcamera {
> > +
> > +/**
> > + * \enum Orientation
> > + * \brief The image orientation in a memory buffer
> > + *
> > + * The Orientation enumeration describes the orientation of the images
> > + * produced by the camera pipeline as they get received by the application
> > + * inside memory buffers.
> > + *
>
> From here to ...
>
> > + * All the possible 2D plane transformations of an image are expressed as the
> > + * combination of two basic operations:
> > + *
> > + * 'a': rotate the image 90 degrees clockwise
> > + * 'b': flip the image horizontally (mirroring)
> > + *
> > + * plus an identity operator 'e'.
> > + *
> > + * The composition of operations 'a' and 'b' according to the canonical function
> > + * composition notion 'b * a' (where 'a' is applied first, then 'b' is applied
> > + * next) gives origin to a symmetric group of order 8, or dihedral group.
> > + *
> > + * See https://en.wikipedia.org/wiki/Dihedral_group#/media/File:Dih4_cycle_graph.svg
> > + * as an example of the 2D plane transformation and how they originate from
> > + * the composition of the 'a' and 'b' operations.
> > + *
> > + * The image orientation expressed using the Orientation enumeration can be
> > + * then inferred by applying a multiple of a 90 degrees rotation from the origin
> > + * and then applying any horizontal mirroring to a base image assume to be
> > + * naturally oriented (no rotation and no mirroring applied).
>
> ... here, is this a leftover of a previous implementation, or copied
> from somewhere else ? It doesn't seem to match the enumeration.
Why it doesn't match the enumeration ? The enumerated members are
named as "90 degrees rotation" + "optional horizontal mirroring".
What is wrong here ?
>
> > + *
> > + * The enumeration numerical values follow the ones defined by the EXIF
> > + * Specification version 2.32, Tag 274 "Orientation", while the names of the
> > + * enumerated values report the rotation and mirroring operations performed.
> > + *
> > + * In example Orientation::rotate90Flip describes the image transformation
>
> s/In example/For example,/
>
> > + * obtained by rotating 90 degrees clockwise first and then applying an
> > + * horizontal mirroring.
> > + */
> > +
> > +/**
> > + * \brief Prints human-friendly names for Orientation items
>
> Let's be consistent with the other operator<<() implementations:
>
> * \brief Insert a text representation of an Orientation into an output stream
>
> > + * \param[in] out The output stream
> > + * \param[in] orientation The Orientation item
>
> s/ item//
>
> > + * \return The output stream \a out
> > + */
> > +std::ostream &operator<<(std::ostream &out, const Orientation &orientation)
> > +{
> > + constexpr std::array<const char *, 9> orientationNames = {
> > + "", /* Orientation starts counting from 1. */
> > + "rotate0", "rotate0Flip",
> > + "rotate180", "rotate180Flip",
> > + "rotate90Flip", "rotate270",
> > + "rotate270Flip", "rotate90",
> > + };
> > +
> > + out << orientationNames[static_cast<unsigned int>(orientation)];
>
> You can write
>
> /* The Orientation enumeration starts counting from 1. */
> out << orientationNames[static_cast<unsigned int>(orientation) - 1];
>
> And drop the first entry from the array.
>
> > + return out;
> > +}
> > +
> > +} /* namespace libcamera */
>
> --
> Regards,
>
> Laurent Pinchart
More information about the libcamera-devel
mailing list