Message ID | 20211206105032.13876-2-david.plowman@raspberrypi.com |
---|---|
State | Superseded |
Headers | show |
Series |
|
Related | show |
Hi David, Thank you for the patch. On Mon, Dec 06, 2021 at 10:50:24AM +0000, David Plowman wrote: > This class represents a color space by defining its color primaries, > YCbCr encoding, the transfer (gamma) function it uses, and whether the > output is full or limited range. > > Signed-off-by: David Plowman <david.plowman@raspberrypi.com> > Reviewed-by: Naushir Patuck <naush@raspberrypi.com> > Reviewed-by: Umang Jain <umang.jain@ideasonboard.com> > Reviewed-by: Jacopo Mondi <jacopo@jmondi.org> > --- > include/libcamera/color_space.h | 69 ++++++++ > include/libcamera/meson.build | 1 + > src/libcamera/color_space.cpp | 305 ++++++++++++++++++++++++++++++++ > src/libcamera/meson.build | 1 + > 4 files changed, 376 insertions(+) > create mode 100644 include/libcamera/color_space.h > create mode 100644 src/libcamera/color_space.cpp > > diff --git a/include/libcamera/color_space.h b/include/libcamera/color_space.h > new file mode 100644 > index 00000000..0fe6b580 > --- /dev/null > +++ b/include/libcamera/color_space.h > @@ -0,0 +1,69 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2021, Raspberry Pi (Trading) Limited > + * > + * color_space.h - color space definitions > + */ > + > +#pragma once > + > +#include <optional> > +#include <string> > + > +namespace libcamera { > + > +class ColorSpace > +{ > +public: > + enum class Primaries { > + Raw, > + Smpte170m, It's always annoying to find the proper way to express abbrevitations in camelCase. We'll figure out any change later, if needed. > + Rec709, > + Rec2020, > + }; > + > + enum class YcbcrEncoding { > + Rec601, > + Rec709, > + Rec2020, Sorry if this has been discussed before, but do we need a None (or similar) entry for non-YUV formats ? > + }; > + > + enum class TransferFunction { > + Linear, > + Srgb, > + Rec709, > + }; Nitpicking (and can be handled during merging without a v10 if this is the only comment that needs addressing), could you move the TransferFunction before the YcbcrEncoding, to match the order in which a colour space "applies" ? Same for the function arguments, member functions and documentation below (and possibly in the rest of the series, if applicable) > + > + enum class Range { > + Full, > + Limited, > + }; > + > + constexpr ColorSpace(Primaries p, YcbcrEncoding e, TransferFunction t, Range r) > + : primaries(p), ycbcrEncoding(e), transferFunction(t), range(r) > + { > + } > + > + static const ColorSpace Raw; > + static const ColorSpace Jpeg; > + static const ColorSpace Srgb; > + static const ColorSpace Smpte170m; > + static const ColorSpace Rec709; > + static const ColorSpace Rec2020; > + > + Primaries primaries; > + YcbcrEncoding ycbcrEncoding; > + TransferFunction transferFunction; > + Range range; > + > + const std::string toString() const; > + static const std::string toString(const std::optional<ColorSpace> &colorSpace); You return a std::string by value from both functions, you can drop the const. > +}; > + > +bool operator==(const ColorSpace &lhs, const ColorSpace &rhs); > +static inline bool operator!=(const ColorSpace &lhs, const ColorSpace &rhs) > +{ > + return !(lhs == rhs); > +} > + > +} /* namespace libcamera */ Looking good so far, let's continue :-) > diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build > index 5f42977c..fd767b11 100644 > --- a/include/libcamera/meson.build > +++ b/include/libcamera/meson.build > @@ -5,6 +5,7 @@ libcamera_include_dir = 'libcamera' / 'libcamera' > libcamera_public_headers = files([ > 'camera.h', > 'camera_manager.h', > + 'color_space.h', > 'controls.h', > 'framebuffer.h', > 'framebuffer_allocator.h', > diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp > new file mode 100644 > index 00000000..5fe4fcc2 > --- /dev/null > +++ b/src/libcamera/color_space.cpp > @@ -0,0 +1,305 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2021, Raspberry Pi (Trading) Limited > + * > + * color_space.cpp - color spaces. > + */ > + > +#include <libcamera/color_space.h> > + > +#include <algorithm> > +#include <map> > +#include <sstream> > +#include <utility> > +#include <vector> > + > +/** > + * \file color_space.h > + * \brief Class and enums to represent color spaces > + */ > + > +namespace libcamera { > + > +/** > + * \class ColorSpace > + * \brief Class to describe a color space > + * > + * The ColorSpace class defines the color primaries, the Y'CbCr encoding, > + * the transfer function associated with the color space, and the range > + * (sometimes also referred to as the quantisation) of the color space. > + * > + * Certain combinations of these fields form well-known standard color > + * spaces such as "JPEG" or "REC709". > + * > + * In the strictest sense a "color space" formally only refers to the > + * color primaries and white point. Here, however, the ColorSpace class > + * adopts the common broader usage that includes the transfer function, > + * Y'CbCr encoding method and quantisation. > + * > + * For more information on the specific color spaces described here, please > + * see: > + * > + * <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-srgb">sRGB</a> > + * <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-jpeg">JPEG</a> > + * <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-smpte-170m">SMPTE 170M</a> > + * <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-rec709">Rec.709</a> > + * <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-bt2020">Rec.2020</a> Let's make this a list: * - <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-srgb">sRGB</a> * - <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-jpeg">JPEG</a> * - <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-smpte-170m">SMPTE 170M</a> * - <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-rec709">Rec.709</a> * - <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-bt2020">Rec.2020</a> > + */ > + > +/** > + * \enum ColorSpace::Primaries > + * \brief The color primaries for this color space > + * > + * \var ColorSpace::Primaries::Raw > + * \brief These are raw colors directly from a sensor I'd expand this a bit: * \brief These are raw colors directly from a sensor, the primaries are * unspecified > + * > + * \var ColorSpace::Primaries::Smpte170m > + * \brief SMPTE 170M color primaries > + * > + * \var ColorSpace::Primaries::Rec709 > + * \brief Rec.709 color primaries > + * > + * \var ColorSpace::Primaries::Rec2020 > + * \brief Rec.2020 color primaries > + */ > + > +/** > + * \enum ColorSpace::YcbcrEncoding > + * \brief The Y'CbCr encoding > + * > + * \var ColorSpace::YcbcrEncoding::Rec601 > + * \brief Rec.601 Y'CbCr encoding > + * > + * \var ColorSpace::YcbcrEncoding::Rec709 > + * \brief Rec.709 Y'CbCr encoding > + * > + * \var ColorSpace::YcbcrEncoding::Rec2020 > + * \brief Rec.2020 Y'CbCr encoding > + */ > + > +/** > + * \enum ColorSpace::TransferFunction > + * \brief The transfer function used for this color space > + * > + * \var ColorSpace::TransferFunction::Linear > + * \brief This color space uses a linear (identity) transfer function > + * > + * \var ColorSpace::TransferFunction::Srgb > + * \brief sRGB transfer function > + * > + * \var ColorSpace::TransferFunction::Rec709 > + * \brief Rec.709 transfer function > + */ > + > +/** > + * \enum ColorSpace::Range > + * \brief The range (sometimes "quantisation") for this color space > + * > + * \var ColorSpace::Range::Full > + * \brief This color space uses full range pixel values > + * > + * \var ColorSpace::Range::Limited > + * \brief This color space uses limited range pixel values, being > + * 16 to 235 for Y' and 16 to 240 for Cb and Cr (8 bits per sample) > + * or 64 to 940 for Y' and 16 to 960 for Cb and Cr (10 bits) > + */ > + > +/** > + * \fn ColorSpace::ColorSpace(Primaries p, Encoding e, TransferFunction t, Range r) > + * \brief Construct a ColorSpace from explicit values > + * \param[in] p The color primaries > + * \param[in] e The Y'CbCr encoding > + * \param[in] t The transfer function for the color space > + * \param[in] r The range of the pixel values in this color space > + */ > + > +/** > + * \brief Assemble and return a readable string representation of the > + * ColorSpace > + * > + * If the color space matches a standard ColorSpace (such as ColorSpace::Jpeg) > + * then the short name of the color space ("Jpeg") is returned. Otherwise > + * the four constituent parts of the ColorSpace are assembled into a longer > + * string. > + * > + * \return A string describing the ColorSpace > + */ > +const std::string ColorSpace::toString() const > +{ > + /* Print out a brief name only for standard color spaces. */ > + > + static const std::vector<std::pair<ColorSpace, const char *>> colorSpaceNames = { If you make this a std::array I think it can become a constexpr (std::vector got a constexpr constructor in C++20 only). > + { ColorSpace::Raw, "Raw" }, > + { ColorSpace::Jpeg, "Jpeg" }, > + { ColorSpace::Srgb, "Srgb" }, > + { ColorSpace::Smpte170m, "Smpte170m" }, > + { ColorSpace::Rec709, "Rec709" }, > + { ColorSpace::Rec2020, "Rec2020" }, Maybe "JPEG", "sRGB", "SMPTE170M" ? That's how it's usually printed. Same below. > + }; > + auto it = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(), > + [this](const auto &item) { > + return *this == item.first; > + }); > + if (it != colorSpaceNames.end()) > + return std::string(it->second); > + > + /* Assemble a name made of the constituent fields. */ > + > + static const std::map<Primaries, std::string> primariesNames = { > + { Primaries::Raw, "Raw" }, > + { Primaries::Smpte170m, "Smpte170m" }, > + { Primaries::Rec709, "Rec709" }, > + { Primaries::Rec2020, "Rec2020" }, > + }; > + static const std::map<YcbcrEncoding, std::string> encodingNames = { > + { YcbcrEncoding::Rec601, "Rec601" }, > + { YcbcrEncoding::Rec709, "Rec709" }, > + { YcbcrEncoding::Rec2020, "Rec2020" }, > + }; > + static const std::map<TransferFunction, std::string> transferNames = { > + { TransferFunction::Linear, "Linear" }, > + { TransferFunction::Srgb, "Srgb" }, > + { TransferFunction::Rec709, "Rec709" }, > + }; > + static const std::map<Range, std::string> rangeNames = { > + { Range::Full, "Full" }, > + { Range::Limited, "Limited" }, > + }; > + > + auto itPrimaries = primariesNames.find(primaries); > + std::string primariesName = > + itPrimaries == primariesNames.end() ? "Invalid" : itPrimaries->second; > + > + auto itEncoding = encodingNames.find(ycbcrEncoding); > + std::string encodingName = > + itEncoding == encodingNames.end() ? "Invalid" : itEncoding->second; > + > + auto itTransfer = transferNames.find(transferFunction); > + std::string transferName = > + itTransfer == transferNames.end() ? "Invalid" : itTransfer->second; > + > + auto itRange = rangeNames.find(range); > + std::string rangeName = > + itRange == rangeNames.end() ? "Invalid" : itRange->second; > + > + std::stringstream ss; > + ss << primariesName << "/" << encodingName << "/" << transferName << "/" << rangeName; > + > + return ss.str(); > +} > + > +/** > + * \brief Assemble and return a readable string representation of an > + * optional ColorSpace > + * \return A string describing the optional ColorSpace > + */ > +const std::string ColorSpace::toString(const std::optional<ColorSpace> &colorSpace) I'm not sure yet what this would be used for, I suppose I'll see in further patches. > +{ > + if (!colorSpace) > + return "Unknown"; > + > + return colorSpace->toString(); > +} > + > +/** > + * \var ColorSpace::primaries > + * \brief The color primaries of this color space > + */ > + > +/** > + * \var ColorSpace::ycbcrEncoding > + * \brief The Y'CbCr encoding used with this color space s/with/by/ ? Same below. > + */ > + > +/** > + * \var ColorSpace::transferFunction > + * \brief The transfer function used with this color space > + */ > + > +/** > + * \var ColorSpace::range > + * \brief The pixel range used with this color space > + */ > + > +/** > + * \brief A constant representing a raw color space (from a sensor) > + */ > +const ColorSpace ColorSpace::Raw = { > + Primaries::Raw, > + YcbcrEncoding::Rec601, > + TransferFunction::Linear, > + Range::Full > +}; > + > +/** > + * \brief A constant representing the JPEG color space used for > + * encoding JPEG images. s/.$// > + */ > +const ColorSpace ColorSpace::Jpeg = { > + Primaries::Rec709, > + YcbcrEncoding::Rec601, > + TransferFunction::Srgb, > + Range::Full > +}; > + > +/** > + * \brief A constant representing the sRGB color space. This is > + * identical to the JPEG color space except that the range Y'CbCr > + * range is limited rather than full. I'd split the second line out of the brief: * \brief A constant representing the sRGB color space * * This is identical to the JPEG color space except that the range Y'CbCr range * is limited rather than full. Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > + */ > +const ColorSpace ColorSpace::Srgb = { > + Primaries::Rec709, > + YcbcrEncoding::Rec601, > + TransferFunction::Srgb, > + Range::Limited > +}; > + > +/** > + * \brief A constant representing the SMPTE170M color space > + */ > +const ColorSpace ColorSpace::Smpte170m = { > + Primaries::Smpte170m, > + YcbcrEncoding::Rec601, > + TransferFunction::Rec709, > + Range::Limited > +}; > + > +/** > + * \brief A constant representing the Rec.709 color space > + */ > +const ColorSpace ColorSpace::Rec709 = { > + Primaries::Rec709, > + YcbcrEncoding::Rec709, > + TransferFunction::Rec709, > + Range::Limited > +}; > + > +/** > + * \brief A constant representing the Rec.2020 color space > + */ > +const ColorSpace ColorSpace::Rec2020 = { > + Primaries::Rec2020, > + YcbcrEncoding::Rec2020, > + TransferFunction::Rec709, > + Range::Limited > +}; > + > +/** > + * \brief Compare color spaces for equality > + * \return True if the two color spaces are identical, false otherwise > + */ > +bool operator==(const ColorSpace &lhs, const ColorSpace &rhs) > +{ > + return lhs.primaries == rhs.primaries && > + lhs.ycbcrEncoding == rhs.ycbcrEncoding && > + lhs.transferFunction == rhs.transferFunction && > + lhs.range == rhs.range; > +} > + > +/** > + * \fn bool operator!=(const ColorSpace &lhs, const ColorSpace &rhs) > + * \brief Compare color spaces for inequality > + * \return True if the two color spaces are not identical, false otherwise > + */ > + > +} /* namespace libcamera */ > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build > index 2e54cc04..4045e24e 100644 > --- a/src/libcamera/meson.build > +++ b/src/libcamera/meson.build > @@ -9,6 +9,7 @@ libcamera_sources = files([ > 'camera_manager.cpp', > 'camera_sensor.cpp', > 'camera_sensor_properties.cpp', > + 'color_space.cpp', > 'controls.cpp', > 'control_serializer.cpp', > 'control_validator.cpp',
On Tue, Dec 07, 2021 at 03:12:44PM +0200, Laurent Pinchart wrote: > Hi David, > > Thank you for the patch. > > On Mon, Dec 06, 2021 at 10:50:24AM +0000, David Plowman wrote: > > This class represents a color space by defining its color primaries, > > YCbCr encoding, the transfer (gamma) function it uses, and whether the > > output is full or limited range. > > > > Signed-off-by: David Plowman <david.plowman@raspberrypi.com> > > Reviewed-by: Naushir Patuck <naush@raspberrypi.com> > > Reviewed-by: Umang Jain <umang.jain@ideasonboard.com> > > Reviewed-by: Jacopo Mondi <jacopo@jmondi.org> > > --- > > include/libcamera/color_space.h | 69 ++++++++ > > include/libcamera/meson.build | 1 + > > src/libcamera/color_space.cpp | 305 ++++++++++++++++++++++++++++++++ > > src/libcamera/meson.build | 1 + > > 4 files changed, 376 insertions(+) > > create mode 100644 include/libcamera/color_space.h > > create mode 100644 src/libcamera/color_space.cpp > > > > diff --git a/include/libcamera/color_space.h b/include/libcamera/color_space.h > > new file mode 100644 > > index 00000000..0fe6b580 > > --- /dev/null > > +++ b/include/libcamera/color_space.h > > @@ -0,0 +1,69 @@ > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > > +/* > > + * Copyright (C) 2021, Raspberry Pi (Trading) Limited > > + * > > + * color_space.h - color space definitions > > + */ > > + > > +#pragma once > > + > > +#include <optional> > > +#include <string> > > + > > +namespace libcamera { > > + > > +class ColorSpace > > +{ > > +public: > > + enum class Primaries { > > + Raw, > > + Smpte170m, > > It's always annoying to find the proper way to express abbrevitations in > camelCase. We'll figure out any change later, if needed. > > > + Rec709, > > + Rec2020, > > + }; > > + > > + enum class YcbcrEncoding { > > + Rec601, > > + Rec709, > > + Rec2020, > > Sorry if this has been discussed before, but do we need a None (or > similar) entry for non-YUV formats ? > > > + }; > > + > > + enum class TransferFunction { > > + Linear, > > + Srgb, > > + Rec709, > > + }; > > Nitpicking (and can be handled during merging without a v10 if this is > the only comment that needs addressing), could you move the > TransferFunction before the YcbcrEncoding, to match the order in which a > colour space "applies" ? Same for the function arguments, member > functions and documentation below (and possibly in the rest of the > series, if applicable) > > > + > > + enum class Range { > > + Full, > > + Limited, > > + }; > > + > > + constexpr ColorSpace(Primaries p, YcbcrEncoding e, TransferFunction t, Range r) > > + : primaries(p), ycbcrEncoding(e), transferFunction(t), range(r) > > + { > > + } > > + > > + static const ColorSpace Raw; > > + static const ColorSpace Jpeg; > > + static const ColorSpace Srgb; > > + static const ColorSpace Smpte170m; > > + static const ColorSpace Rec709; > > + static const ColorSpace Rec2020; > > + > > + Primaries primaries; > > + YcbcrEncoding ycbcrEncoding; > > + TransferFunction transferFunction; > > + Range range; > > + > > + const std::string toString() const; > > + static const std::string toString(const std::optional<ColorSpace> &colorSpace); > > You return a std::string by value from both functions, you can drop the > const. > > > +}; > > + > > +bool operator==(const ColorSpace &lhs, const ColorSpace &rhs); > > +static inline bool operator!=(const ColorSpace &lhs, const ColorSpace &rhs) > > +{ > > + return !(lhs == rhs); > > +} > > + > > +} /* namespace libcamera */ > > Looking good so far, let's continue :-) > > > diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build > > index 5f42977c..fd767b11 100644 > > --- a/include/libcamera/meson.build > > +++ b/include/libcamera/meson.build > > @@ -5,6 +5,7 @@ libcamera_include_dir = 'libcamera' / 'libcamera' > > libcamera_public_headers = files([ > > 'camera.h', > > 'camera_manager.h', > > + 'color_space.h', > > 'controls.h', > > 'framebuffer.h', > > 'framebuffer_allocator.h', > > diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp > > new file mode 100644 > > index 00000000..5fe4fcc2 > > --- /dev/null > > +++ b/src/libcamera/color_space.cpp > > @@ -0,0 +1,305 @@ > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > > +/* > > + * Copyright (C) 2021, Raspberry Pi (Trading) Limited > > + * > > + * color_space.cpp - color spaces. > > + */ > > + > > +#include <libcamera/color_space.h> > > + > > +#include <algorithm> > > +#include <map> > > +#include <sstream> > > +#include <utility> > > +#include <vector> > > + > > +/** > > + * \file color_space.h > > + * \brief Class and enums to represent color spaces > > + */ > > + > > +namespace libcamera { > > + > > +/** > > + * \class ColorSpace > > + * \brief Class to describe a color space > > + * > > + * The ColorSpace class defines the color primaries, the Y'CbCr encoding, > > + * the transfer function associated with the color space, and the range > > + * (sometimes also referred to as the quantisation) of the color space. > > + * > > + * Certain combinations of these fields form well-known standard color > > + * spaces such as "JPEG" or "REC709". > > + * > > + * In the strictest sense a "color space" formally only refers to the > > + * color primaries and white point. Here, however, the ColorSpace class > > + * adopts the common broader usage that includes the transfer function, > > + * Y'CbCr encoding method and quantisation. > > + * > > + * For more information on the specific color spaces described here, please > > + * see: > > + * > > + * <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-srgb">sRGB</a> > > + * <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-jpeg">JPEG</a> > > + * <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-smpte-170m">SMPTE 170M</a> > > + * <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-rec709">Rec.709</a> > > + * <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-bt2020">Rec.2020</a> > > Let's make this a list: > > * - <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-srgb">sRGB</a> > * - <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-jpeg">JPEG</a> > * - <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-smpte-170m">SMPTE 170M</a> > * - <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-rec709">Rec.709</a> > * - <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-bt2020">Rec.2020</a> > > > + */ > > + > > +/** > > + * \enum ColorSpace::Primaries > > + * \brief The color primaries for this color space > > + * > > + * \var ColorSpace::Primaries::Raw > > + * \brief These are raw colors directly from a sensor > > I'd expand this a bit: > > * \brief These are raw colors directly from a sensor, the primaries are > * unspecified > > > + * > > + * \var ColorSpace::Primaries::Smpte170m > > + * \brief SMPTE 170M color primaries > > + * > > + * \var ColorSpace::Primaries::Rec709 > > + * \brief Rec.709 color primaries > > + * > > + * \var ColorSpace::Primaries::Rec2020 > > + * \brief Rec.2020 color primaries > > + */ > > + > > +/** > > + * \enum ColorSpace::YcbcrEncoding > > + * \brief The Y'CbCr encoding > > + * > > + * \var ColorSpace::YcbcrEncoding::Rec601 > > + * \brief Rec.601 Y'CbCr encoding > > + * > > + * \var ColorSpace::YcbcrEncoding::Rec709 > > + * \brief Rec.709 Y'CbCr encoding > > + * > > + * \var ColorSpace::YcbcrEncoding::Rec2020 > > + * \brief Rec.2020 Y'CbCr encoding > > + */ > > + > > +/** > > + * \enum ColorSpace::TransferFunction > > + * \brief The transfer function used for this color space > > + * > > + * \var ColorSpace::TransferFunction::Linear > > + * \brief This color space uses a linear (identity) transfer function > > + * > > + * \var ColorSpace::TransferFunction::Srgb > > + * \brief sRGB transfer function > > + * > > + * \var ColorSpace::TransferFunction::Rec709 > > + * \brief Rec.709 transfer function > > + */ > > + > > +/** > > + * \enum ColorSpace::Range > > + * \brief The range (sometimes "quantisation") for this color space > > + * > > + * \var ColorSpace::Range::Full > > + * \brief This color space uses full range pixel values > > + * > > + * \var ColorSpace::Range::Limited > > + * \brief This color space uses limited range pixel values, being > > + * 16 to 235 for Y' and 16 to 240 for Cb and Cr (8 bits per sample) > > + * or 64 to 940 for Y' and 16 to 960 for Cb and Cr (10 bits) > > + */ > > + > > +/** > > + * \fn ColorSpace::ColorSpace(Primaries p, Encoding e, TransferFunction t, Range r) > > + * \brief Construct a ColorSpace from explicit values > > + * \param[in] p The color primaries > > + * \param[in] e The Y'CbCr encoding > > + * \param[in] t The transfer function for the color space > > + * \param[in] r The range of the pixel values in this color space > > + */ > > + > > +/** > > + * \brief Assemble and return a readable string representation of the > > + * ColorSpace > > + * > > + * If the color space matches a standard ColorSpace (such as ColorSpace::Jpeg) > > + * then the short name of the color space ("Jpeg") is returned. Otherwise > > + * the four constituent parts of the ColorSpace are assembled into a longer > > + * string. > > + * > > + * \return A string describing the ColorSpace > > + */ > > +const std::string ColorSpace::toString() const > > +{ > > + /* Print out a brief name only for standard color spaces. */ > > + > > + static const std::vector<std::pair<ColorSpace, const char *>> colorSpaceNames = { > > If you make this a std::array I think it can become a constexpr > (std::vector got a constexpr constructor in C++20 only). > > > + { ColorSpace::Raw, "Raw" }, > > + { ColorSpace::Jpeg, "Jpeg" }, > > + { ColorSpace::Srgb, "Srgb" }, > > + { ColorSpace::Smpte170m, "Smpte170m" }, > > + { ColorSpace::Rec709, "Rec709" }, > > + { ColorSpace::Rec2020, "Rec2020" }, > > Maybe "JPEG", "sRGB", "SMPTE170M" ? That's how it's usually printed. > Same below. > > > + }; > > + auto it = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(), > > + [this](const auto &item) { > > + return *this == item.first; > > + }); > > + if (it != colorSpaceNames.end()) > > + return std::string(it->second); > > + > > + /* Assemble a name made of the constituent fields. */ > > + > > + static const std::map<Primaries, std::string> primariesNames = { > > + { Primaries::Raw, "Raw" }, > > + { Primaries::Smpte170m, "Smpte170m" }, > > + { Primaries::Rec709, "Rec709" }, > > + { Primaries::Rec2020, "Rec2020" }, > > + }; > > + static const std::map<YcbcrEncoding, std::string> encodingNames = { > > + { YcbcrEncoding::Rec601, "Rec601" }, > > + { YcbcrEncoding::Rec709, "Rec709" }, > > + { YcbcrEncoding::Rec2020, "Rec2020" }, > > + }; > > + static const std::map<TransferFunction, std::string> transferNames = { > > + { TransferFunction::Linear, "Linear" }, > > + { TransferFunction::Srgb, "Srgb" }, > > + { TransferFunction::Rec709, "Rec709" }, > > + }; > > + static const std::map<Range, std::string> rangeNames = { > > + { Range::Full, "Full" }, > > + { Range::Limited, "Limited" }, > > + }; > > + > > + auto itPrimaries = primariesNames.find(primaries); > > + std::string primariesName = > > + itPrimaries == primariesNames.end() ? "Invalid" : itPrimaries->second; > > + > > + auto itEncoding = encodingNames.find(ycbcrEncoding); > > + std::string encodingName = > > + itEncoding == encodingNames.end() ? "Invalid" : itEncoding->second; > > + > > + auto itTransfer = transferNames.find(transferFunction); > > + std::string transferName = > > + itTransfer == transferNames.end() ? "Invalid" : itTransfer->second; > > + > > + auto itRange = rangeNames.find(range); > > + std::string rangeName = > > + itRange == rangeNames.end() ? "Invalid" : itRange->second; > > + > > + std::stringstream ss; > > + ss << primariesName << "/" << encodingName << "/" << transferName << "/" << rangeName; > > + > > + return ss.str(); > > +} > > + > > +/** > > + * \brief Assemble and return a readable string representation of an > > + * optional ColorSpace > > + * \return A string describing the optional ColorSpace > > + */ > > +const std::string ColorSpace::toString(const std::optional<ColorSpace> &colorSpace) > > I'm not sure yet what this would be used for, I suppose I'll see in > further patches. Now that I see how this is being used, I think the documentation should be expanded a bit. * \brief Assemble and return a readable string representation of an * optional ColorSpace * * This is a convenience helper to easily obtain a string representation for a * ColorSpace in the parts of the libcamera API where it is stored in a * std::optional<>. If the ColorSpace is set, this function returns * \a colorSpace.toString(), otherwise it returns "Unknown". * * \return A string describing the optional ColorSpace And I wonder if we should return "Unset" instead of "Unknown". What do you think ? > > +{ > > + if (!colorSpace) > > + return "Unknown"; > > + > > + return colorSpace->toString(); > > +} > > + > > +/** > > + * \var ColorSpace::primaries > > + * \brief The color primaries of this color space > > + */ > > + > > +/** > > + * \var ColorSpace::ycbcrEncoding > > + * \brief The Y'CbCr encoding used with this color space > > s/with/by/ ? Same below. > > > + */ > > + > > +/** > > + * \var ColorSpace::transferFunction > > + * \brief The transfer function used with this color space > > + */ > > + > > +/** > > + * \var ColorSpace::range > > + * \brief The pixel range used with this color space > > + */ > > + > > +/** > > + * \brief A constant representing a raw color space (from a sensor) > > + */ > > +const ColorSpace ColorSpace::Raw = { > > + Primaries::Raw, > > + YcbcrEncoding::Rec601, > > + TransferFunction::Linear, > > + Range::Full > > +}; > > + > > +/** > > + * \brief A constant representing the JPEG color space used for > > + * encoding JPEG images. > > s/.$// > > > + */ > > +const ColorSpace ColorSpace::Jpeg = { > > + Primaries::Rec709, > > + YcbcrEncoding::Rec601, > > + TransferFunction::Srgb, > > + Range::Full > > +}; > > + > > +/** > > + * \brief A constant representing the sRGB color space. This is > > + * identical to the JPEG color space except that the range Y'CbCr > > + * range is limited rather than full. > > I'd split the second line out of the brief: > > * \brief A constant representing the sRGB color space > * > * This is identical to the JPEG color space except that the range Y'CbCr range > * is limited rather than full. > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > > > + */ > > +const ColorSpace ColorSpace::Srgb = { > > + Primaries::Rec709, > > + YcbcrEncoding::Rec601, > > + TransferFunction::Srgb, > > + Range::Limited > > +}; > > + > > +/** > > + * \brief A constant representing the SMPTE170M color space > > + */ > > +const ColorSpace ColorSpace::Smpte170m = { > > + Primaries::Smpte170m, > > + YcbcrEncoding::Rec601, > > + TransferFunction::Rec709, > > + Range::Limited > > +}; > > + > > +/** > > + * \brief A constant representing the Rec.709 color space > > + */ > > +const ColorSpace ColorSpace::Rec709 = { > > + Primaries::Rec709, > > + YcbcrEncoding::Rec709, > > + TransferFunction::Rec709, > > + Range::Limited > > +}; > > + > > +/** > > + * \brief A constant representing the Rec.2020 color space > > + */ > > +const ColorSpace ColorSpace::Rec2020 = { > > + Primaries::Rec2020, > > + YcbcrEncoding::Rec2020, > > + TransferFunction::Rec709, > > + Range::Limited > > +}; > > + > > +/** > > + * \brief Compare color spaces for equality > > + * \return True if the two color spaces are identical, false otherwise > > + */ > > +bool operator==(const ColorSpace &lhs, const ColorSpace &rhs) > > +{ > > + return lhs.primaries == rhs.primaries && > > + lhs.ycbcrEncoding == rhs.ycbcrEncoding && > > + lhs.transferFunction == rhs.transferFunction && > > + lhs.range == rhs.range; > > +} > > + > > +/** > > + * \fn bool operator!=(const ColorSpace &lhs, const ColorSpace &rhs) > > + * \brief Compare color spaces for inequality > > + * \return True if the two color spaces are not identical, false otherwise > > + */ > > + > > +} /* namespace libcamera */ > > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build > > index 2e54cc04..4045e24e 100644 > > --- a/src/libcamera/meson.build > > +++ b/src/libcamera/meson.build > > @@ -9,6 +9,7 @@ libcamera_sources = files([ > > 'camera_manager.cpp', > > 'camera_sensor.cpp', > > 'camera_sensor_properties.cpp', > > + 'color_space.cpp', > > 'controls.cpp', > > 'control_serializer.cpp', > > 'control_validator.cpp',
diff --git a/include/libcamera/color_space.h b/include/libcamera/color_space.h new file mode 100644 index 00000000..0fe6b580 --- /dev/null +++ b/include/libcamera/color_space.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Raspberry Pi (Trading) Limited + * + * color_space.h - color space definitions + */ + +#pragma once + +#include <optional> +#include <string> + +namespace libcamera { + +class ColorSpace +{ +public: + enum class Primaries { + Raw, + Smpte170m, + Rec709, + Rec2020, + }; + + enum class YcbcrEncoding { + Rec601, + Rec709, + Rec2020, + }; + + enum class TransferFunction { + Linear, + Srgb, + Rec709, + }; + + enum class Range { + Full, + Limited, + }; + + constexpr ColorSpace(Primaries p, YcbcrEncoding e, TransferFunction t, Range r) + : primaries(p), ycbcrEncoding(e), transferFunction(t), range(r) + { + } + + static const ColorSpace Raw; + static const ColorSpace Jpeg; + static const ColorSpace Srgb; + static const ColorSpace Smpte170m; + static const ColorSpace Rec709; + static const ColorSpace Rec2020; + + Primaries primaries; + YcbcrEncoding ycbcrEncoding; + TransferFunction transferFunction; + Range range; + + const std::string toString() const; + static const std::string toString(const std::optional<ColorSpace> &colorSpace); +}; + +bool operator==(const ColorSpace &lhs, const ColorSpace &rhs); +static inline bool operator!=(const ColorSpace &lhs, const ColorSpace &rhs) +{ + return !(lhs == rhs); +} + +} /* namespace libcamera */ diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 5f42977c..fd767b11 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -5,6 +5,7 @@ libcamera_include_dir = 'libcamera' / 'libcamera' libcamera_public_headers = files([ 'camera.h', 'camera_manager.h', + 'color_space.h', 'controls.h', 'framebuffer.h', 'framebuffer_allocator.h', diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp new file mode 100644 index 00000000..5fe4fcc2 --- /dev/null +++ b/src/libcamera/color_space.cpp @@ -0,0 +1,305 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Raspberry Pi (Trading) Limited + * + * color_space.cpp - color spaces. + */ + +#include <libcamera/color_space.h> + +#include <algorithm> +#include <map> +#include <sstream> +#include <utility> +#include <vector> + +/** + * \file color_space.h + * \brief Class and enums to represent color spaces + */ + +namespace libcamera { + +/** + * \class ColorSpace + * \brief Class to describe a color space + * + * The ColorSpace class defines the color primaries, the Y'CbCr encoding, + * the transfer function associated with the color space, and the range + * (sometimes also referred to as the quantisation) of the color space. + * + * Certain combinations of these fields form well-known standard color + * spaces such as "JPEG" or "REC709". + * + * In the strictest sense a "color space" formally only refers to the + * color primaries and white point. Here, however, the ColorSpace class + * adopts the common broader usage that includes the transfer function, + * Y'CbCr encoding method and quantisation. + * + * For more information on the specific color spaces described here, please + * see: + * + * <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-srgb">sRGB</a> + * <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-jpeg">JPEG</a> + * <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-smpte-170m">SMPTE 170M</a> + * <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-rec709">Rec.709</a> + * <a href="https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-bt2020">Rec.2020</a> + */ + +/** + * \enum ColorSpace::Primaries + * \brief The color primaries for this color space + * + * \var ColorSpace::Primaries::Raw + * \brief These are raw colors directly from a sensor + * + * \var ColorSpace::Primaries::Smpte170m + * \brief SMPTE 170M color primaries + * + * \var ColorSpace::Primaries::Rec709 + * \brief Rec.709 color primaries + * + * \var ColorSpace::Primaries::Rec2020 + * \brief Rec.2020 color primaries + */ + +/** + * \enum ColorSpace::YcbcrEncoding + * \brief The Y'CbCr encoding + * + * \var ColorSpace::YcbcrEncoding::Rec601 + * \brief Rec.601 Y'CbCr encoding + * + * \var ColorSpace::YcbcrEncoding::Rec709 + * \brief Rec.709 Y'CbCr encoding + * + * \var ColorSpace::YcbcrEncoding::Rec2020 + * \brief Rec.2020 Y'CbCr encoding + */ + +/** + * \enum ColorSpace::TransferFunction + * \brief The transfer function used for this color space + * + * \var ColorSpace::TransferFunction::Linear + * \brief This color space uses a linear (identity) transfer function + * + * \var ColorSpace::TransferFunction::Srgb + * \brief sRGB transfer function + * + * \var ColorSpace::TransferFunction::Rec709 + * \brief Rec.709 transfer function + */ + +/** + * \enum ColorSpace::Range + * \brief The range (sometimes "quantisation") for this color space + * + * \var ColorSpace::Range::Full + * \brief This color space uses full range pixel values + * + * \var ColorSpace::Range::Limited + * \brief This color space uses limited range pixel values, being + * 16 to 235 for Y' and 16 to 240 for Cb and Cr (8 bits per sample) + * or 64 to 940 for Y' and 16 to 960 for Cb and Cr (10 bits) + */ + +/** + * \fn ColorSpace::ColorSpace(Primaries p, Encoding e, TransferFunction t, Range r) + * \brief Construct a ColorSpace from explicit values + * \param[in] p The color primaries + * \param[in] e The Y'CbCr encoding + * \param[in] t The transfer function for the color space + * \param[in] r The range of the pixel values in this color space + */ + +/** + * \brief Assemble and return a readable string representation of the + * ColorSpace + * + * If the color space matches a standard ColorSpace (such as ColorSpace::Jpeg) + * then the short name of the color space ("Jpeg") is returned. Otherwise + * the four constituent parts of the ColorSpace are assembled into a longer + * string. + * + * \return A string describing the ColorSpace + */ +const std::string ColorSpace::toString() const +{ + /* Print out a brief name only for standard color spaces. */ + + static const std::vector<std::pair<ColorSpace, const char *>> colorSpaceNames = { + { ColorSpace::Raw, "Raw" }, + { ColorSpace::Jpeg, "Jpeg" }, + { ColorSpace::Srgb, "Srgb" }, + { ColorSpace::Smpte170m, "Smpte170m" }, + { ColorSpace::Rec709, "Rec709" }, + { ColorSpace::Rec2020, "Rec2020" }, + }; + auto it = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(), + [this](const auto &item) { + return *this == item.first; + }); + if (it != colorSpaceNames.end()) + return std::string(it->second); + + /* Assemble a name made of the constituent fields. */ + + static const std::map<Primaries, std::string> primariesNames = { + { Primaries::Raw, "Raw" }, + { Primaries::Smpte170m, "Smpte170m" }, + { Primaries::Rec709, "Rec709" }, + { Primaries::Rec2020, "Rec2020" }, + }; + static const std::map<YcbcrEncoding, std::string> encodingNames = { + { YcbcrEncoding::Rec601, "Rec601" }, + { YcbcrEncoding::Rec709, "Rec709" }, + { YcbcrEncoding::Rec2020, "Rec2020" }, + }; + static const std::map<TransferFunction, std::string> transferNames = { + { TransferFunction::Linear, "Linear" }, + { TransferFunction::Srgb, "Srgb" }, + { TransferFunction::Rec709, "Rec709" }, + }; + static const std::map<Range, std::string> rangeNames = { + { Range::Full, "Full" }, + { Range::Limited, "Limited" }, + }; + + auto itPrimaries = primariesNames.find(primaries); + std::string primariesName = + itPrimaries == primariesNames.end() ? "Invalid" : itPrimaries->second; + + auto itEncoding = encodingNames.find(ycbcrEncoding); + std::string encodingName = + itEncoding == encodingNames.end() ? "Invalid" : itEncoding->second; + + auto itTransfer = transferNames.find(transferFunction); + std::string transferName = + itTransfer == transferNames.end() ? "Invalid" : itTransfer->second; + + auto itRange = rangeNames.find(range); + std::string rangeName = + itRange == rangeNames.end() ? "Invalid" : itRange->second; + + std::stringstream ss; + ss << primariesName << "/" << encodingName << "/" << transferName << "/" << rangeName; + + return ss.str(); +} + +/** + * \brief Assemble and return a readable string representation of an + * optional ColorSpace + * \return A string describing the optional ColorSpace + */ +const std::string ColorSpace::toString(const std::optional<ColorSpace> &colorSpace) +{ + if (!colorSpace) + return "Unknown"; + + return colorSpace->toString(); +} + +/** + * \var ColorSpace::primaries + * \brief The color primaries of this color space + */ + +/** + * \var ColorSpace::ycbcrEncoding + * \brief The Y'CbCr encoding used with this color space + */ + +/** + * \var ColorSpace::transferFunction + * \brief The transfer function used with this color space + */ + +/** + * \var ColorSpace::range + * \brief The pixel range used with this color space + */ + +/** + * \brief A constant representing a raw color space (from a sensor) + */ +const ColorSpace ColorSpace::Raw = { + Primaries::Raw, + YcbcrEncoding::Rec601, + TransferFunction::Linear, + Range::Full +}; + +/** + * \brief A constant representing the JPEG color space used for + * encoding JPEG images. + */ +const ColorSpace ColorSpace::Jpeg = { + Primaries::Rec709, + YcbcrEncoding::Rec601, + TransferFunction::Srgb, + Range::Full +}; + +/** + * \brief A constant representing the sRGB color space. This is + * identical to the JPEG color space except that the range Y'CbCr + * range is limited rather than full. + */ +const ColorSpace ColorSpace::Srgb = { + Primaries::Rec709, + YcbcrEncoding::Rec601, + TransferFunction::Srgb, + Range::Limited +}; + +/** + * \brief A constant representing the SMPTE170M color space + */ +const ColorSpace ColorSpace::Smpte170m = { + Primaries::Smpte170m, + YcbcrEncoding::Rec601, + TransferFunction::Rec709, + Range::Limited +}; + +/** + * \brief A constant representing the Rec.709 color space + */ +const ColorSpace ColorSpace::Rec709 = { + Primaries::Rec709, + YcbcrEncoding::Rec709, + TransferFunction::Rec709, + Range::Limited +}; + +/** + * \brief A constant representing the Rec.2020 color space + */ +const ColorSpace ColorSpace::Rec2020 = { + Primaries::Rec2020, + YcbcrEncoding::Rec2020, + TransferFunction::Rec709, + Range::Limited +}; + +/** + * \brief Compare color spaces for equality + * \return True if the two color spaces are identical, false otherwise + */ +bool operator==(const ColorSpace &lhs, const ColorSpace &rhs) +{ + return lhs.primaries == rhs.primaries && + lhs.ycbcrEncoding == rhs.ycbcrEncoding && + lhs.transferFunction == rhs.transferFunction && + lhs.range == rhs.range; +} + +/** + * \fn bool operator!=(const ColorSpace &lhs, const ColorSpace &rhs) + * \brief Compare color spaces for inequality + * \return True if the two color spaces are not identical, false otherwise + */ + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 2e54cc04..4045e24e 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -9,6 +9,7 @@ libcamera_sources = files([ 'camera_manager.cpp', 'camera_sensor.cpp', 'camera_sensor_properties.cpp', + 'color_space.cpp', 'controls.cpp', 'control_serializer.cpp', 'control_validator.cpp',