[{"id":21460,"web_url":"https://patchwork.libcamera.org/comment/21460/","msgid":"<20211130200535.dtglfobo7i2zw53g@uno.localdomain>","date":"2021-11-30T20:05:35","subject":"Re: [libcamera-devel] [PATCH v7 1/7] libcamera: Add ColorSpace class","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi David,\n\nOn Fri, Nov 26, 2021 at 10:40:39AM +0000, David Plowman wrote:\n> This class represents a color space by defining its color primaries,\n> YCbCr encoding, the transfer (gamma) function it uses, and whether the\n> output is full or limited range.\n>\n> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>\n> Reviewed-by: Naushir Patuck <naush@raspberrypi.com>\n> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>\n> ---\n>  include/libcamera/color_space.h |  72 ++++++++\n>  include/libcamera/meson.build   |   1 +\n>  src/libcamera/color_space.cpp   | 305 ++++++++++++++++++++++++++++++++\n>  src/libcamera/meson.build       |   1 +\n>  4 files changed, 379 insertions(+)\n>  create mode 100644 include/libcamera/color_space.h\n>  create mode 100644 src/libcamera/color_space.cpp\n>\n> diff --git a/include/libcamera/color_space.h b/include/libcamera/color_space.h\n> new file mode 100644\n> index 00000000..a780d41e\n> --- /dev/null\n> +++ b/include/libcamera/color_space.h\n> @@ -0,0 +1,72 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2021, Raspberry Pi (Trading) Limited\n> + *\n> + * color_space.h - color space definitions\n> + */\n> +\n> +#ifndef __LIBCAMERA_COLOR_SPACE_H__\n> +#define __LIBCAMERA_COLOR_SPACE_H__\n\nYou can now use\n#pragma once\n\n> +\n> +#include <optional>\n> +#include <string>\n> +\n> +namespace libcamera {\n> +\n> +class ColorSpace\n> +{\n> +public:\n> +\tenum class Primaries {\n> +\t\tRaw,\n> +\t\tSmpte170m,\n> +\t\tRec709,\n> +\t\tRec2020,\n> +\t};\n> +\n> +\tenum class YcbcrEncoding {\n> +\t\tRec601,\n> +\t\tRec709,\n> +\t\tRec2020,\n> +\t};\n> +\n> +\tenum class TransferFunction {\n> +\t\tLinear,\n> +\t\tSrgb,\n> +\t\tRec709,\n> +\t};\n> +\n> +\tenum class Range {\n> +\t\tFull,\n> +\t\tLimited,\n> +\t};\n> +\n> +\tconstexpr ColorSpace(Primaries p, YcbcrEncoding e, TransferFunction t, Range r)\n> +\t\t: primaries(p), ycbcrEncoding(e), transferFunction(t), range(r)\n> +\t{\n> +\t}\n> +\n> +\tstatic const ColorSpace Raw;\n> +\tstatic const ColorSpace Jpeg;\n> +\tstatic const ColorSpace Srgb;\n> +\tstatic const ColorSpace Smpte170m;\n> +\tstatic const ColorSpace Rec709;\n> +\tstatic const ColorSpace Rec2020;\n> +\n> +\tPrimaries primaries;\n> +\tYcbcrEncoding ycbcrEncoding;\n> +\tTransferFunction transferFunction;\n> +\tRange range;\n> +\n> +\tconst std::string toString() const;\n> +\tstatic const std::string toString(const std::optional<ColorSpace> &colorSpace);\n> +};\n> +\n> +bool operator==(const ColorSpace &lhs, const ColorSpace &rhs);\n> +static inline bool operator!=(const ColorSpace &lhs, const ColorSpace &rhs)\n> +{\n> +\treturn !(lhs == rhs);\n> +}\n> +\n> +} /* namespace libcamera */\n> +\n> +#endif /* __LIBCAMERA_COLOR_SPACE_H__ */\n> diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build\n> index 7155ff20..131e1740 100644\n> --- a/include/libcamera/meson.build\n> +++ b/include/libcamera/meson.build\n> @@ -5,6 +5,7 @@ libcamera_include_dir = 'libcamera' / 'libcamera'\n>  libcamera_public_headers = files([\n>      'camera.h',\n>      'camera_manager.h',\n> +    'color_space.h',\n>      'compiler.h',\n>      'controls.h',\n>      'file_descriptor.h',\n> diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp\n> new file mode 100644\n> index 00000000..9882424d\n> --- /dev/null\n> +++ b/src/libcamera/color_space.cpp\n> @@ -0,0 +1,305 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2021, Raspberry Pi (Trading) Limited\n> + *\n> + * color_space.cpp - color spaces.\n> + */\n> +\n> +#include <libcamera/color_space.h>\n> +\n> +#include <algorithm>\n> +#include <map>\n> +#include <sstream>\n> +#include <utility>\n> +#include <vector>\n> +\n> +/**\n> + * \\file color_space.h\n> + * \\brief Class and enums to represent color spaces\n> + */\n> +\n> +namespace libcamera {\n> +\n> +/**\n> + * \\class ColorSpace\n> + * \\brief Class to describe a color space\n> + *\n> + * The ColorSpace class defines the color primaries, the Y'CbCr encoding,\n> + * the transfer function associated with the color space, and the range\n> + * (sometimes also referred to as the quantisation) of the color space.\n> + *\n> + * Certain combinations of these fields form well-known standard color\n> + * spaces such as \"JPEG\" or \"REC709\".\n> + *\n> + * In the strictest sense a \"color space\" formally only refers to the\n> + * color primaries and white point. Here, however, the ColorSpace class\n> + * adopts the common broader usage that includes the transfer function,\n> + * Y'CbCr encoding method and quantisation.\n> + *\n> + * For more information on the specific color spaces described here, please\n> + * see:\n> + *\n> + * <a href=\"https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-srgb\">sRGB</a>\n> + * <a href=\"https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-jpeg\">JPEG</a>\n> + * <a href=\"https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-smpte-170m\">SMPTE 170M</a>\n> + * <a href=\"https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-rec709\">Rec.709</a>\n> + * <a href=\"https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/colorspaces-details.html#col-bt2020\">Rec.2020</a>\n> + */\n> +\n> +/**\n> + * \\enum ColorSpace::Primaries\n> + * \\brief The color primaries for this color space\n> + *\n> + * \\var ColorSpace::Primaries::Raw\n> + * \\brief These are raw colors directly from a sensor\n> + *\n> + * \\var ColorSpace::Primaries::Smpte170m\n> + * \\brief SMPTE 170M color primaries\n> + *\n> + * \\var ColorSpace::Primaries::Rec709\n> + * \\brief Rec.709 color primaries\n> + *\n> + * \\var ColorSpace::Primaries::Rec2020\n> + * \\brief Rec.2020 color primaries\n> + */\n> +\n> +/**\n> + * \\enum ColorSpace::YcbcrEncoding\n> + * \\brief The Y'CbCr encoding\n> + *\n> + * \\var ColorSpace::YcbcrEncoding::Rec601\n> + * \\brief Rec.601 Y'CbCr encoding\n> + *\n> + * \\var ColorSpace::YcbcrEncoding::Rec709\n> + * \\brief Rec.709 Y'CbCr encoding\n> + *\n> + * \\var ColorSpace::YcbcrEncoding::Rec2020\n> + * \\brief Rec.2020 Y'CbCr encoding\n> + */\n> +\n> +/**\n> + * \\enum ColorSpace::TransferFunction\n> + * \\brief The transfer function used for this color space\n> + *\n> + * \\var ColorSpace::TransferFunction::Linear\n> + * \\brief This color space uses a linear (identity) transfer function\n> + *\n> + * \\var ColorSpace::TransferFunction::Srgb\n> + * \\brief sRGB transfer function\n> + *\n> + * \\var ColorSpace::TransferFunction::Rec709\n> + * \\brief Rec.709 transfer function\n> + */\n> +\n> +/**\n> + * \\enum ColorSpace::Range\n> + * \\brief The range (sometimes \"quantisation\") for this color space\n> + *\n> + * \\var ColorSpace::Range::Full\n> + * \\brief This color space uses full range pixel values\n> + *\n> + * \\var ColorSpace::Range::Limited\n> + * \\brief This color space uses limited range pixel values, being\n> + * 16 to 235 for Y' and 16 to 240 for Cb and Cr (8 bits per sample)\n> + * or 64 to 940 for Y' and 16 to 960 for Cb and Cr (10 bits)\n> + */\n> +\n> +/**\n> + * \\fn ColorSpace::ColorSpace(Primaries p, Encoding e, TransferFunction t, Range r)\n> + * \\brief Construct a ColorSpace from explicit values\n> + * \\param[in] p The color primaries\n> + * \\param[in] e The Y'CbCr encoding\n> + * \\param[in] t The transfer function for the color space\n> + * \\param[in] r The range of the pixel values in this color space\n> + */\n> +\n> +/**\n> + * \\brief Assemble and return a readable string representation of the\n> + * ColorSpace\n> + *\n> + * If the color space matches a standard ColorSpace (such as ColorSpace::Jpeg)\n> + * then the short name of the color space (\"Jpeg\") is returned. Otherwise\n> + * the four constituent parts of the ColorSpace are assembled into a longer\n> + * string.\n> + *\n> + * \\return A string describing the ColorSpace\n> + */\n> +const std::string ColorSpace::toString() const\n> +{\n> +\t/* Print out a brief name only for standard color spaces. */\n> +\n> +\tstatic const std::vector<std::pair<ColorSpace, const char *>> colorSpaceNames = {\n> +\t\t{ ColorSpace::Raw, \"Raw\" },\n> +\t\t{ ColorSpace::Jpeg, \"Jpeg\" },\n> +\t\t{ ColorSpace::Srgb, \"Srgb\" },\n> +\t\t{ ColorSpace::Smpte170m, \"Smpte170m\" },\n> +\t\t{ ColorSpace::Rec709, \"Rec709\" },\n> +\t\t{ ColorSpace::Rec2020, \"Rec2020\" },\n> +\t};\n> +\tauto it = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),\n> +\t\t\t       [this](const auto &item) {\n> +\t\t\t\t       return *this == item.first;\n> +\t\t\t       });\n> +\tif (it != colorSpaceNames.end())\n> +\t\treturn std::string(it->second);\n> +\n> +\t/* Assemble a name made of the constituent fields. */\n> +\n> +\tstatic const std::map<Primaries, std::string> primariesNames = {\n> +\t\t{ Primaries::Raw, \"Raw\" },\n> +\t\t{ Primaries::Smpte170m, \"Smpte170m\" },\n> +\t\t{ Primaries::Rec709, \"Rec709\" },\n> +\t\t{ Primaries::Rec2020, \"Rec2020\" },\n> +\t};\n> +\tstatic const std::map<YcbcrEncoding, std::string> encodingNames = {\n> +\t\t{ YcbcrEncoding::Rec601, \"Rec601\" },\n> +\t\t{ YcbcrEncoding::Rec709, \"Rec709\" },\n> +\t\t{ YcbcrEncoding::Rec2020, \"Rec2020\" },\n> +\t};\n> +\tstatic const std::map<TransferFunction, std::string> transferNames = {\n> +\t\t{ TransferFunction::Linear, \"Linear\" },\n> +\t\t{ TransferFunction::Srgb, \"Srgb\" },\n> +\t\t{ TransferFunction::Rec709, \"Rec709\" },\n> +\t};\n> +\tstatic const std::map<Range, std::string> rangeNames = {\n> +\t\t{ Range::Full, \"Full\" },\n> +\t\t{ Range::Limited, \"Limited\" },\n> +\t};\n> +\n> +\tauto itPrimaries = primariesNames.find(primaries);\n> +\tstd::string primariesName =\n> +\t\titPrimaries == primariesNames.end() ? \"Invalid\" : itPrimaries->second;\n> +\n> +\tauto itEncoding = encodingNames.find(ycbcrEncoding);\n> +\tstd::string encodingName =\n> +\t\titEncoding == encodingNames.end() ? \"Invalid\" : itEncoding->second;\n> +\n> +\tauto itTransfer = transferNames.find(transferFunction);\n> +\tstd::string transferName =\n> +\t\titTransfer == transferNames.end() ? \"Invalid\" : itTransfer->second;\n> +\n> +\tauto itRange = rangeNames.find(range);\n> +\tstd::string rangeName =\n> +\t\titRange == rangeNames.end() ? \"Invalid\" : itRange->second;\n> +\n> +\tstd::stringstream ss;\n> +\tss << primariesName << \"/\" << encodingName << \"/\" << transferName << \"/\" << rangeName;\n> +\n> +\treturn ss.str();\n> +}\n> +\n> +/**\n> + * \\brief Assemble and return a readable string representation of an\n> + * optional ColorSpace\n> + * \\return A string describing the optional ColorSpace\n> + */\n> +const std::string ColorSpace::toString(const std::optional<ColorSpace> &colorSpace)\n> +{\n> +\tif (!colorSpace)\n> +\t\treturn \"Unknown\";\n> +\n> +\treturn colorSpace->toString();\n> +}\n> +\n> +/**\n> + * \\var ColorSpace::primaries\n> + * \\brief The color primaries\n> + */\n> +\n> +/**\n> + * \\var ColorSpace::ycbcrEncoding\n> + * \\brief The Y'CbCr encoding\n> + */\n> +\n> +/**\n> + * \\var ColorSpace::transferFunction\n> + * \\brief The transfer function for this color space\n> + */\n> +\n> +/**\n> + * \\var ColorSpace::range\n> + * \\brief The pixel range used by this color space\n> + */\n\nA very minor nit which can be fixed when applying: remove \"for this\ncolor space\" or add to all the 4 variables\n\nThis apart, and with #pragma which can be equally chenged when\napplying:\nReviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n\nThanks\n   j\n> +\n> +/**\n> + * \\brief A constant representing a raw color space (from a sensor)\n> + */\n> +const ColorSpace ColorSpace::Raw = {\n> +\tPrimaries::Raw,\n> +\tYcbcrEncoding::Rec601,\n> +\tTransferFunction::Linear,\n> +\tRange::Full\n> +};\n> +\n> +/**\n> + * \\brief A constant representing the JPEG color space used for\n> + * encoding JPEG images.\n> + */\n> +const ColorSpace ColorSpace::Jpeg = {\n> +\tPrimaries::Rec709,\n> +\tYcbcrEncoding::Rec601,\n> +\tTransferFunction::Srgb,\n> +\tRange::Full\n> +};\n> +\n> +/**\n> + * \\brief A constant representing the sRGB color space. This is\n> + * identical to the JPEG color space except that the range Y'CbCr\n> + * range is limited rather than full.\n> + */\n> +const ColorSpace ColorSpace::Srgb = {\n> +\tPrimaries::Rec709,\n> +\tYcbcrEncoding::Rec601,\n> +\tTransferFunction::Srgb,\n> +\tRange::Limited\n> +};\n> +\n> +/**\n> + * \\brief A constant representing the SMPTE170M color space\n> + */\n> +const ColorSpace ColorSpace::Smpte170m = {\n> +\tPrimaries::Smpte170m,\n> +\tYcbcrEncoding::Rec601,\n> +\tTransferFunction::Rec709,\n> +\tRange::Limited\n> +};\n> +\n> +/**\n> + * \\brief A constant representing the Rec.709 color space\n> + */\n> +const ColorSpace ColorSpace::Rec709 = {\n> +\tPrimaries::Rec709,\n> +\tYcbcrEncoding::Rec709,\n> +\tTransferFunction::Rec709,\n> +\tRange::Limited\n> +};\n> +\n> +/**\n> + * \\brief A constant representing the Rec.2020 color space\n> + */\n> +const ColorSpace ColorSpace::Rec2020 = {\n> +\tPrimaries::Rec2020,\n> +\tYcbcrEncoding::Rec2020,\n> +\tTransferFunction::Rec709,\n> +\tRange::Limited\n> +};\n> +\n> +/**\n> + * \\brief Compare color spaces for equality\n> + * \\return True if the two color spaces are identical, false otherwise\n> + */\n> +bool operator==(const ColorSpace &lhs, const ColorSpace &rhs)\n> +{\n> +\treturn lhs.primaries == rhs.primaries &&\n> +\t       lhs.ycbcrEncoding == rhs.ycbcrEncoding &&\n> +\t       lhs.transferFunction == rhs.transferFunction &&\n> +\t       lhs.range == rhs.range;\n> +}\n> +\n> +/**\n> + * \\fn bool operator!=(const ColorSpace &lhs, const ColorSpace &rhs)\n> + * \\brief Compare color spaces for inequality\n> + * \\return True if the two color spaces are not identical, false otherwise\n> + */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index 6727a777..e7371d20 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -8,6 +8,7 @@ libcamera_sources = files([\n>      'camera_manager.cpp',\n>      'camera_sensor.cpp',\n>      'camera_sensor_properties.cpp',\n> +    'color_space.cpp',\n>      'controls.cpp',\n>      'control_serializer.cpp',\n>      'control_validator.cpp',\n> --\n> 2.30.2\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id E400CBDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 30 Nov 2021 20:04:48 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 29E2860723;\n\tTue, 30 Nov 2021 21:04:48 +0100 (CET)","from relay1-d.mail.gandi.net (relay1-d.mail.gandi.net\n\t[217.70.183.193])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 33767605C4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 30 Nov 2021 21:04:46 +0100 (CET)","(Authenticated sender: jacopo@jmondi.org)\n\tby relay1-d.mail.gandi.net (Postfix) with ESMTPSA id 3782A240002;\n\tTue, 30 Nov 2021 20:04:42 +0000 (UTC)"],"Date":"Tue, 30 Nov 2021 21:05:35 +0100","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"David Plowman <david.plowman@raspberrypi.com>","Message-ID":"<20211130200535.dtglfobo7i2zw53g@uno.localdomain>","References":"<20211126104045.4756-1-david.plowman@raspberrypi.com>\n\t<20211126104045.4756-2-david.plowman@raspberrypi.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20211126104045.4756-2-david.plowman@raspberrypi.com>","Subject":"Re: [libcamera-devel] [PATCH v7 1/7] libcamera: Add ColorSpace class","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"Tomasz Figa <tfiga@google.com>, libcamera-devel@lists.libcamera.org,\n\tHans Verkuil <hverkuil-cisco@xs4all.nl>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]