[libcamera-devel] [PATCH v8 4/8] libcamera: Add BayerFormat type

Laurent Pinchart laurent.pinchart at ideasonboard.com
Mon Sep 28 12:57:14 CEST 2020


Hi David,

Thank you for the patch.

On Mon, Sep 07, 2020 at 08:15:59AM +0100, David Plowman wrote:
> This type encodes BayerFormats in an explicit way, that makes them
> easier to use than some of the other more opaque type formats. This
> makes the BayerFormat useful for editing or manipulating Bayer types
> more easily.
> 
> Signed-off-by: David Plowman <david.plowman at raspberrypi.com>
> Reviewed-by: Kieran Bingham <kieran.bingham at ideasonboard.com>
> ---
>  include/libcamera/internal/bayer_format.h |  61 +++++
>  src/libcamera/bayer_format.cpp            | 268 ++++++++++++++++++++++
>  src/libcamera/meson.build                 |   1 +
>  3 files changed, 330 insertions(+)
>  create mode 100644 include/libcamera/internal/bayer_format.h
>  create mode 100644 src/libcamera/bayer_format.cpp
> 
> diff --git a/include/libcamera/internal/bayer_format.h b/include/libcamera/internal/bayer_format.h
> new file mode 100644
> index 0000000..4280b76
> --- /dev/null
> +++ b/include/libcamera/internal/bayer_format.h
> @@ -0,0 +1,61 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2020, Raspberry Pi (Trading) Ltd.
> + *
> + * bayer_format.h - Bayer Pixel Format
> + */
> +#ifndef __LIBCAMERA_INTERNAL_BAYER_FORMAT_H__
> +#define __LIBCAMERA_INTERNAL_BAYER_FORMAT_H__
> +
> +#include <stdint.h>
> +#include <string>
> +
> +#include "libcamera/internal/v4l2_pixelformat.h"
> +
> +namespace libcamera {
> +
> +enum class Transform;
> +
> +class BayerFormat
> +{
> +public:
> +	enum Order : uint8_t {
> +		BGGR = 0,
> +		GBRG = 1,
> +		GRBG = 2,
> +		RGGB = 3
> +	};
> +
> +	enum Packing : uint16_t {
> +		None = 0,
> +		CSI2Packed = 1,
> +		IPU3Packed = 2,
> +	};
> +
> +	constexpr BayerFormat()
> +		: order(Order::BGGR), bitDepth(0), packing(Packing::None)
> +	{
> +	}
> +
> +	constexpr BayerFormat(Order o, uint8_t b, Packing p)
> +		: order(o), bitDepth(b), packing(p)
> +	{
> +	}
> +
> +	explicit BayerFormat(V4L2PixelFormat v4l2Format);
> +	bool isValid() const { return bitDepth != 0; }
> +
> +	std::string toString() const;
> +
> +	V4L2PixelFormat toV4L2PixelFormat() const;
> +	BayerFormat transform(Transform t) const;
> +
> +	Order order;
> +	uint8_t bitDepth;
> +
> +	Packing packing;
> +};
> +
> +} /* namespace libcamera */
> +
> +#endif /* __LIBCAMERA_INTERNAL_BAYER_FORMAT_H__ */
> diff --git a/src/libcamera/bayer_format.cpp b/src/libcamera/bayer_format.cpp
> new file mode 100644
> index 0000000..c7e4f22
> --- /dev/null
> +++ b/src/libcamera/bayer_format.cpp
> @@ -0,0 +1,268 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2020, Raspberry Pi (Trading) Limited
> + *
> + * bayer_format.cpp - Class to represent Bayer formats
> + */
> +
> +#include "libcamera/internal/bayer_format.h"
> +
> +#include <map>
> +
> +#include <libcamera/transform.h>
> +
> +/**
> + * \file bayer_format.h
> + * \brief Class to represent Bayer formats and manipulate them
> + */
> +
> +namespace libcamera {
> +
> +/**
> + * \class BayerFormat
> + * \brief Class to represent a raw image Bayer format
> + *
> + * This class encodes the different Bayer formats in such a way that they can
> + * be easily manipulated. For example, the bit depth or Bayer order can be
> + * easily altered - the Bayer order can even be "transformed" in the same
> + * manner as happens in many sensors when their horizontal or vertical "flip"
> + * controls are set.
> + */
> +
> +/**
> + * \enum BayerFormat::Order
> + * \brief The order of the colour channels in the Bayer pattern
> + *
> + * \var BayerFormat::BGGR
> + * \brief B then G on the first row, G then R on the second row.
> + * \var BayerFormat::GBRG
> + * \brief G then B on the first row, R then G on the second row.
> + * \var BayerFormat::GRBG
> + * \brief G then R on the first row, B then G on the second row.
> + * \var BayerFormat::RGGB
> + * \brief R then G on the first row, G then B on the second row.
> + */
> +
> +/**
> + * \enum BayerFormat::Packing
> + * \brief Different types of packing that can be applied to a BayerFormat
> + *
> + * \var BayerFormat::None
> + * \brief No packing
> + * \var BayerFormat::CSI2Packed
> + * \brief Format uses MIPI CSI-2 style packing
> + * \var BayerFormat::IPU3Packed
> + * \brief Format uses IPU3 style packing
> + */
> +
> +namespace {
> +
> +const std::map<V4L2PixelFormat, BayerFormat> v4l2ToBayer{
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8), { BayerFormat::BGGR, 8, BayerFormat::None } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8), { BayerFormat::GBRG, 8, BayerFormat::None } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8), { BayerFormat::GRBG, 8, BayerFormat::None } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8), { BayerFormat::RGGB, 8, BayerFormat::None } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10), { BayerFormat::BGGR, 10, BayerFormat::None } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10), { BayerFormat::GBRG, 10, BayerFormat::None } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10), { BayerFormat::GRBG, 10, BayerFormat::None } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10), { BayerFormat::RGGB, 10, BayerFormat::None } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P), { BayerFormat::BGGR, 10, BayerFormat::CSI2Packed } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P), { BayerFormat::GBRG, 10, BayerFormat::CSI2Packed } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P), { BayerFormat::GRBG, 10, BayerFormat::CSI2Packed } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P), { BayerFormat::RGGB, 10, BayerFormat::CSI2Packed } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10), { BayerFormat::BGGR, 10, BayerFormat::IPU3Packed } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGBRG10), { BayerFormat::GBRG, 10, BayerFormat::IPU3Packed } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGRBG10), { BayerFormat::GRBG, 10, BayerFormat::IPU3Packed } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SRGGB10), { BayerFormat::RGGB, 10, BayerFormat::IPU3Packed } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12), { BayerFormat::BGGR, 12, BayerFormat::None } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12), { BayerFormat::GBRG, 12, BayerFormat::None } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12), { BayerFormat::GRBG, 12, BayerFormat::None } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12), { BayerFormat::RGGB, 12, BayerFormat::None } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P), { BayerFormat::BGGR, 12, BayerFormat::CSI2Packed } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P), { BayerFormat::GBRG, 12, BayerFormat::CSI2Packed } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P), { BayerFormat::GRBG, 12, BayerFormat::CSI2Packed } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P), { BayerFormat::RGGB, 12, BayerFormat::CSI2Packed } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16), { BayerFormat::BGGR, 16, BayerFormat::None } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16), { BayerFormat::GBRG, 16, BayerFormat::None } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16), { BayerFormat::GRBG, 16, BayerFormat::None } },
> +	{ V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16), { BayerFormat::RGGB, 16, BayerFormat::None } },
> +};
> +
> +/* Define a slightly arbitrary ordering so that we can use a std::map. */
> +struct BayerFormatComparator {
> +	constexpr bool operator()(const BayerFormat &lhs, const BayerFormat &rhs) const
> +	{
> +		if (lhs.bitDepth < rhs.bitDepth)
> +			return true;
> +		else if (lhs.bitDepth > rhs.bitDepth)
> +			return false;
> +
> +		if (lhs.order < rhs.order)
> +			return true;
> +		else if (lhs.order > rhs.order)
> +			return false;
> +
> +		if (lhs.packing < rhs.packing)
> +			return true;
> +		else
> +			return false;
> +	}
> +};
> +
> +const std::map<BayerFormat, V4L2PixelFormat, BayerFormatComparator> bayerToV4l2{
> +	{ { BayerFormat::BGGR, 8, BayerFormat::None }, V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8) },
> +	{ { BayerFormat::GBRG, 8, BayerFormat::None }, V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8) },
> +	{ { BayerFormat::GRBG, 8, BayerFormat::None }, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8) },
> +	{ { BayerFormat::RGGB, 8, BayerFormat::None }, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8) },
> +	{ { BayerFormat::BGGR, 10, BayerFormat::None }, V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10) },
> +	{ { BayerFormat::GBRG, 10, BayerFormat::None }, V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10) },
> +	{ { BayerFormat::GRBG, 10, BayerFormat::None }, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10) },
> +	{ { BayerFormat::RGGB, 10, BayerFormat::None }, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10) },
> +	{ { BayerFormat::BGGR, 10, BayerFormat::CSI2Packed }, V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P) },
> +	{ { BayerFormat::GBRG, 10, BayerFormat::CSI2Packed }, V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P) },
> +	{ { BayerFormat::GRBG, 10, BayerFormat::CSI2Packed }, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P) },
> +	{ { BayerFormat::RGGB, 10, BayerFormat::CSI2Packed }, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P) },
> +	{ { BayerFormat::BGGR, 10, BayerFormat::IPU3Packed }, V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10) },
> +	{ { BayerFormat::GBRG, 10, BayerFormat::IPU3Packed }, V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGBRG10) },
> +	{ { BayerFormat::GRBG, 10, BayerFormat::IPU3Packed }, V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGRBG10) },
> +	{ { BayerFormat::RGGB, 10, BayerFormat::IPU3Packed }, V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SRGGB10) },
> +	{ { BayerFormat::BGGR, 12, BayerFormat::None }, V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12) },
> +	{ { BayerFormat::GBRG, 12, BayerFormat::None }, V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12) },
> +	{ { BayerFormat::GRBG, 12, BayerFormat::None }, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12) },
> +	{ { BayerFormat::RGGB, 12, BayerFormat::None }, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12) },
> +	{ { BayerFormat::BGGR, 12, BayerFormat::CSI2Packed }, V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P) },
> +	{ { BayerFormat::GBRG, 12, BayerFormat::CSI2Packed }, V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P) },
> +	{ { BayerFormat::GRBG, 12, BayerFormat::CSI2Packed }, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P) },
> +	{ { BayerFormat::RGGB, 12, BayerFormat::CSI2Packed }, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P) },
> +	{ { BayerFormat::BGGR, 16, BayerFormat::None }, V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16) },
> +	{ { BayerFormat::GBRG, 16, BayerFormat::None }, V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16) },
> +	{ { BayerFormat::GRBG, 16, BayerFormat::None }, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16) },
> +	{ { BayerFormat::RGGB, 16, BayerFormat::None }, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16) },
> +};
> +
> +} // namespace

} /* namespace */

With this small issue fixed,

Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>

> +
> +/**
> + * \fn BayerFormat::BayerFormat()
> + * \brief Construct an empty (and invalid) BayerFormat
> + */
> +
> +/**
> + * \fn BayerFormat::BayerFormat(Order o, uint8_t b, Packing p)
> + * \brief Construct a BayerFormat from explicit values
> + * \param[in] o The order of the Bayer pattern
> + * \param[in] b The bit depth of the Bayer samples
> + * \param[in] p The type of packing applied to the pixel values
> + */
> +
> +/**
> + * \brief Construct a BayerFormat from a V4L2PixelFormat
> + * \param[in] v4l2Format The raw format to convert into a BayerFormat
> + */
> +BayerFormat::BayerFormat(V4L2PixelFormat v4l2Format)
> +	: order(BGGR), packing(None)
> +{
> +	const auto it = v4l2ToBayer.find(v4l2Format);
> +	if (it == v4l2ToBayer.end())
> +		bitDepth = 0;
> +	else
> +		*this = it->second;
> +}
> +
> +/**
> + * \fn BayerFormat::isValid()
> + * \brief Return whether a BayerFormat is valid
> + */
> +
> +/**
> + * \brief Assemble and return a readable string representation of the
> + * BayerFormat
> + * \return A string describing the BayerFormat
> + */
> +std::string BayerFormat::toString() const
> +{
> +	std::string result;
> +
> +	static const char *orderStrings[] = {
> +		"BGGR",
> +		"GBRG",
> +		"GRBG",
> +		"RGGB"
> +	};
> +	if (isValid() && order <= RGGB)
> +		result = orderStrings[order];
> +	else
> +		return "INVALID";
> +
> +	result += "-" + std::to_string(bitDepth);
> +
> +	if (packing == CSI2Packed)
> +		result += "-CSI2P";
> +	else if (packing == IPU3Packed)
> +		result += "-IPU3P";
> +
> +	return result;
> +}
> +
> +/**
> + * \brief Convert a BayerFormat into the corresponding V4L2PixelFormat
> + * \return The V4L2PixelFormat corresponding to this BayerFormat
> + */
> +V4L2PixelFormat BayerFormat::toV4L2PixelFormat() const
> +{
> +	const auto it = bayerToV4l2.find(*this);
> +	if (it != bayerToV4l2.end())
> +		return it->second;
> +
> +	return V4L2PixelFormat();
> +}
> +
> +/**
> + * \brief Apply a transform to this BayerFormat
> + * \param[in] t The transform to apply
> + *
> + * Appplying a transform to an image stored in a Bayer format affects the Bayer
> + * order. For example, performing a horizontal flip on the Bayer pattern
> + * RGGB causes the RG rows of pixels to become GR, and the GB rows to become BG.
> + * The transformed image would have a GRBG order. The bit depth and modifiers
> + * are not affected.
> + *
> + * Note that transpositions are ignored as the order of a transpose with
> + * respect to the flips would have to be defined, and sensors are not expected
> + * to support transposition.
> + *
> + * \return The transformed Bayer format
> + */
> +BayerFormat BayerFormat::transform(Transform t) const
> +{
> +	BayerFormat result = *this;
> +
> +	/*
> +	 * Observe that flipping bit 0 of the Order enum performs a horizontal
> +	 * mirror on the Bayer pattern (e.g. RGGB goes to GRBG). Similarly,
> +	 * flipping bit 1 performs a vertical mirror operation on it. Hence:
> +	 */
> +	if (!!(t & Transform::HFlip))
> +		result.order = static_cast<Order>(result.order ^ 1);
> +	if (!!(t & Transform::VFlip))
> +		result.order = static_cast<Order>(result.order ^ 2);
> +
> +	return result;
> +}
> +
> +/**
> + * \var BayerFormat::order
> + * \brief The order of the colour channels in the Bayer pattern
> + */
> +
> +/**
> + * \var BayerFormat::bitDepth
> + * \brief The bit depth of the samples in the Bayer pattern
> + */
> +
> +/**
> + * \var BayerFormat::packing
> + * \brief Any packing scheme applied to this BayerFormat
> + */
> +
> +} /* namespace libcamera */
> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> index edec55e..86c225f 100644
> --- a/src/libcamera/meson.build
> +++ b/src/libcamera/meson.build
> @@ -1,6 +1,7 @@
>  # SPDX-License-Identifier: CC0-1.0
>  
>  libcamera_sources = files([
> +    'bayer_format.cpp',
>      'bound_method.cpp',
>      'buffer.cpp',
>      'byte_stream_buffer.cpp',

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list