[2/4] libcamera: Enable and use matrix implementation from libcamera/internal
diff mbox series

Message ID 20241118150528.1856797-3-stefan.klug@ideasonboard.com
State New
Headers show
Series
  • Move Matrix class from libipa to libcamera
Related show

Commit Message

Stefan Klug Nov. 18, 2024, 3:05 p.m. UTC
The matrix code copied from libipa needs to be adapted to compile and
work from the new location. To keep the project buildable at all times,
these changes are not split into multiple commits but kept as a single
one.

The changes are:
- Add matrix class to meson.build
- Move matrix class from libipa namespace into libcamera
- Add std::initializer_list based constructor to be able to replace the
  Matrix implementation contained in the rpi pipeline
- Replace Matrix class in rpi pipeline with the new one to prevent name
  clashes

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
---
 include/libcamera/internal/matrix.h    | 20 ++++-----
 include/libcamera/internal/meson.build |  1 +
 src/ipa/rpi/controller/rpi/ccm.cpp     | 56 +++++++-------------------
 src/ipa/rpi/controller/rpi/ccm.h       | 35 +---------------
 src/libcamera/matrix.cpp               |  8 +---
 src/libcamera/meson.build              |  1 +
 6 files changed, 32 insertions(+), 89 deletions(-)

Comments

Laurent Pinchart Nov. 18, 2024, 3:59 p.m. UTC | #1
Hi Stefan,

Thank you for the patch.

On Mon, Nov 18, 2024 at 04:05:05PM +0100, Stefan Klug wrote:
> The matrix code copied from libipa needs to be adapted to compile and
> work from the new location. To keep the project buildable at all times,
> these changes are not split into multiple commits but kept as a single
> one.

If you first rename the Matrix class in the RPi IPA to Matrix3x3 then
you will be able to split this patch better, with the rework of the
Matrix class (and wiring up to the build system) in separate patches..

> The changes are:
> - Add matrix class to meson.build
> - Move matrix class from libipa namespace into libcamera
> - Add std::initializer_list based constructor to be able to replace the
>   Matrix implementation contained in the rpi pipeline
> - Replace Matrix class in rpi pipeline with the new one to prevent name
>   clashes
> 
> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
> ---
>  include/libcamera/internal/matrix.h    | 20 ++++-----
>  include/libcamera/internal/meson.build |  1 +
>  src/ipa/rpi/controller/rpi/ccm.cpp     | 56 +++++++-------------------
>  src/ipa/rpi/controller/rpi/ccm.h       | 35 +---------------
>  src/libcamera/matrix.cpp               |  8 +---
>  src/libcamera/meson.build              |  1 +
>  6 files changed, 32 insertions(+), 89 deletions(-)
> 
> diff --git a/include/libcamera/internal/matrix.h b/include/libcamera/internal/matrix.h
> index 5471e6975b74..b82d33f98658 100644
> --- a/include/libcamera/internal/matrix.h
> +++ b/include/libcamera/internal/matrix.h
> @@ -19,8 +19,6 @@ namespace libcamera {
>  
>  LOG_DECLARE_CATEGORY(Matrix)
>  
> -namespace ipa {
> -
>  #ifndef __DOXYGEN__
>  template<typename T, unsigned int Rows, unsigned int Cols,
>  	 std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>
> @@ -35,6 +33,12 @@ public:
>  		data_.fill(static_cast<T>(0));
>  	}
>  
> +	Matrix(std::initializer_list<T> data)
> +	{
> +		ASSERT(data.size() == Rows * Cols);

I considered adding a similar constructor to Vector in my latest series,
but it's problematic for two reasons. One is that the ASSERT() will only
trigger at runtime, while this should be a compile-time check. We
coulduse static_assert() instead (and mark the constructor as
constexpr):

constexpr Matrix(std::initializer_list<T> data)
{
	static_assert(data.size() == Rows * Cols);
	std::copy(data.begin(), data.end(), data_.begin());
}

But this will only work if the initializer list is a compile-time
constant. The following will compile fine:

void foo()
{
	Matrix<double, 2, 2> m{ 1.0, 2.0, 3.0, 4.0 };
	...
}

But this won't:

void foo(double v)
{
	Matrix<double, 2, 2> m{ v, v, v, v };
	...
}

as the initializer list isn't a compile-time constant.

This is why I didn't add a constructor taking an initializer_list, but
instead rely on the constructor taking an std::array. For the Matrix
class, this would be

constexpr Matrix(const std::array<T, Rows, Cols> &data)
{
	std::copy(data.begin(), data.end(), data_.begin());
}

The user needs to be slightly adapted (note the double curly braces):

void foo(double v)
{
	Matrix<double, 2, 2> m{{ v, v, v, v }};
	...
}

The addition of the new constructor could go to a separate patch, before
this one.

> +		std::copy(data.begin(), data.end(), data_.begin());
> +	}
> +
>  	Matrix(const std::vector<T> &data)
>  	{
>  		std::copy(data.begin(), data.end(), data_.begin());

This seems very unsafe :-S Let's see if the constructor taking an
std::array could replace this one.

> @@ -166,24 +170,22 @@ Matrix<T, Rows, Cols> operator+(const Matrix<T, Rows, Cols> &m1, const Matrix<T,
>  bool matrixValidateYaml(const YamlObject &obj, unsigned int size);
>  #endif /* __DOXYGEN__ */
>  
> -} /* namespace ipa */
> -
>  #ifndef __DOXYGEN__
>  template<typename T, unsigned int Rows, unsigned int Cols>
> -std::ostream &operator<<(std::ostream &out, const ipa::Matrix<T, Rows, Cols> &m)
> +std::ostream &operator<<(std::ostream &out, const Matrix<T, Rows, Cols> &m)
>  {
>  	out << m.toString();
>  	return out;
>  }
>  
>  template<typename T, unsigned int Rows, unsigned int Cols>
> -struct YamlObject::Getter<ipa::Matrix<T, Rows, Cols>> {
> -	std::optional<ipa::Matrix<T, Rows, Cols>> get(const YamlObject &obj) const
> +struct YamlObject::Getter<Matrix<T, Rows, Cols>> {
> +	std::optional<Matrix<T, Rows, Cols>> get(const YamlObject &obj) const
>  	{
> -		if (!ipa::matrixValidateYaml(obj, Rows * Cols))
> +		if (!matrixValidateYaml(obj, Rows * Cols))
>  			return std::nullopt;
>  
> -		ipa::Matrix<T, Rows, Cols> matrix;
> +		Matrix<T, Rows, Cols> matrix;
>  		T *data = &matrix[0][0];
>  
>  		unsigned int i = 0;
> diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
> index 1dddcd50c90b..7d6aa8b72bd7 100644
> --- a/include/libcamera/internal/meson.build
> +++ b/include/libcamera/internal/meson.build
> @@ -29,6 +29,7 @@ libcamera_internal_headers = files([
>      'ipc_pipe.h',
>      'ipc_unixsocket.h',
>      'mapped_framebuffer.h',
> +    'matrix.h',
>      'media_device.h',
>      'media_object.h',
>      'pipeline_handler.h',
> diff --git a/src/ipa/rpi/controller/rpi/ccm.cpp b/src/ipa/rpi/controller/rpi/ccm.cpp
> index aefa580c9a4b..418bead56ecd 100644
> --- a/src/ipa/rpi/controller/rpi/ccm.cpp
> +++ b/src/ipa/rpi/controller/rpi/ccm.cpp
> @@ -29,34 +29,7 @@ LOG_DEFINE_CATEGORY(RPiCcm)
>  
>  #define NAME "rpi.ccm"
>  
> -Matrix::Matrix()
> -{
> -	memset(m, 0, sizeof(m));
> -}
> -Matrix::Matrix(double m0, double m1, double m2, double m3, double m4, double m5,
> -	       double m6, double m7, double m8)
> -{
> -	m[0][0] = m0, m[0][1] = m1, m[0][2] = m2, m[1][0] = m3, m[1][1] = m4,
> -	m[1][2] = m5, m[2][0] = m6, m[2][1] = m7, m[2][2] = m8;
> -}
> -int Matrix::read(const libcamera::YamlObject &params)
> -{
> -	double *ptr = (double *)m;
> -
> -	if (params.size() != 9) {
> -		LOG(RPiCcm, Error) << "Wrong number of values in CCM";
> -		return -EINVAL;
> -	}
> -
> -	for (const auto &param : params.asList()) {
> -		auto value = param.get<double>();
> -		if (!value)
> -			return -EINVAL;
> -		*ptr++ = *value;
> -	}
> -
> -	return 0;
> -}
> +using Matrix3x3 = Matrix<double, 3, 3>;
>  
>  Ccm::Ccm(Controller *controller)
>  	: CcmAlgorithm(controller), saturation_(1.0) {}
> @@ -68,8 +41,6 @@ char const *Ccm::name() const
>  
>  int Ccm::read(const libcamera::YamlObject &params)
>  {
> -	int ret;
> -
>  	if (params.contains("saturation")) {
>  		config_.saturation = params["saturation"].get<ipa::Pwl>(ipa::Pwl{});
>  		if (config_.saturation.empty())
> @@ -83,9 +54,12 @@ int Ccm::read(const libcamera::YamlObject &params)
>  
>  		CtCcm ctCcm;
>  		ctCcm.ct = *value;
> -		ret = ctCcm.ccm.read(p["ccm"]);
> -		if (ret)
> -			return ret;
> +
> +		auto ccm = p["ccm"].get<Matrix3x3>();
> +		if (!ccm)
> +			return -EINVAL;
> +
> +		ctCcm.ccm = *ccm;
>  
>  		if (!config_.ccms.empty() && ctCcm.ct <= config_.ccms.back().ct) {
>  			LOG(RPiCcm, Error)
> @@ -125,7 +99,7 @@ bool getLocked(Metadata *metadata, std::string const &tag, T &value)
>  	return true;
>  }
>  
> -Matrix calculateCcm(std::vector<CtCcm> const &ccms, double ct)
> +Matrix3x3 calculateCcm(std::vector<CtCcm> const &ccms, double ct)
>  {
>  	if (ct <= ccms.front().ct)
>  		return ccms.front().ccm;
> @@ -141,13 +115,13 @@ Matrix calculateCcm(std::vector<CtCcm> const &ccms, double ct)
>  	}
>  }
>  
> -Matrix applySaturation(Matrix const &ccm, double saturation)
> +Matrix3x3 applySaturation(Matrix3x3 const &ccm, double saturation)
>  {
> -	Matrix RGB2Y(0.299, 0.587, 0.114, -0.169, -0.331, 0.500, 0.500, -0.419,
> -		     -0.081);
> -	Matrix Y2RGB(1.000, 0.000, 1.402, 1.000, -0.345, -0.714, 1.000, 1.771,
> -		     0.000);
> -	Matrix S(1, 0, 0, 0, saturation, 0, 0, 0, saturation);
> +	Matrix3x3 RGB2Y({ 0.299, 0.587, 0.114, -0.169, -0.331, 0.500, 0.500,
> +			  -0.419, -0.081 });
> +	Matrix3x3 Y2RGB({ 1.000, 0.000, 1.402, 1.000, -0.345, -0.714, 1.000,
> +			  1.771, 0.000 });

While at it, these two should be static const.

> +	Matrix3x3 S({ 1, 0, 0, 0, saturation, 0, 0, 0, saturation });
>  	return Y2RGB * S * RGB2Y * ccm;
>  }
>  
> @@ -181,7 +155,7 @@ void Ccm::prepare(Metadata *imageMetadata)
>  	for (int j = 0; j < 3; j++)
>  		for (int i = 0; i < 3; i++)
>  			ccmStatus.matrix[j * 3 + i] =
> -				std::max(-8.0, std::min(7.9999, ccm.m[j][i]));
> +				std::max(-8.0, std::min(7.9999, ccm[j][i]));
>  	LOG(RPiCcm, Debug)
>  		<< "colour temperature " << awb.temperatureK << "K";
>  	LOG(RPiCcm, Debug)
> diff --git a/src/ipa/rpi/controller/rpi/ccm.h b/src/ipa/rpi/controller/rpi/ccm.h
> index 4e5b33fefa4e..c05dbb17a264 100644
> --- a/src/ipa/rpi/controller/rpi/ccm.h
> +++ b/src/ipa/rpi/controller/rpi/ccm.h
> @@ -8,6 +8,7 @@
>  
>  #include <vector>
>  
> +#include "libcamera/internal/matrix.h"

Missing blank line.

>  #include <libipa/pwl.h>
>  
>  #include "../ccm_algorithm.h"
> @@ -16,41 +17,9 @@ namespace RPiController {
>  
>  /* Algorithm to calculate colour matrix. Should be placed after AWB. */
>  
> -struct Matrix {
> -	Matrix(double m0, double m1, double m2, double m3, double m4, double m5,
> -	       double m6, double m7, double m8);
> -	Matrix();
> -	double m[3][3];
> -	int read(const libcamera::YamlObject &params);
> -};
> -static inline Matrix operator*(double d, Matrix const &m)
> -{
> -	return Matrix(m.m[0][0] * d, m.m[0][1] * d, m.m[0][2] * d,
> -		      m.m[1][0] * d, m.m[1][1] * d, m.m[1][2] * d,
> -		      m.m[2][0] * d, m.m[2][1] * d, m.m[2][2] * d);
> -}
> -static inline Matrix operator*(Matrix const &m1, Matrix const &m2)
> -{
> -	Matrix m;
> -	for (int i = 0; i < 3; i++)
> -		for (int j = 0; j < 3; j++)
> -			m.m[i][j] = m1.m[i][0] * m2.m[0][j] +
> -				    m1.m[i][1] * m2.m[1][j] +
> -				    m1.m[i][2] * m2.m[2][j];
> -	return m;
> -}
> -static inline Matrix operator+(Matrix const &m1, Matrix const &m2)
> -{
> -	Matrix m;
> -	for (int i = 0; i < 3; i++)
> -		for (int j = 0; j < 3; j++)
> -			m.m[i][j] = m1.m[i][j] + m2.m[i][j];
> -	return m;
> -}
> -
>  struct CtCcm {
>  	double ct;
> -	Matrix ccm;
> +	libcamera::Matrix<double, 3, 3> ccm;
>  };
>  
>  struct CcmConfig {
> diff --git a/src/libcamera/matrix.cpp b/src/libcamera/matrix.cpp
> index 8346f0d34160..c3ed54895b22 100644
> --- a/src/libcamera/matrix.cpp
> +++ b/src/libcamera/matrix.cpp
> @@ -5,7 +5,7 @@
>   * Matrix and related operations
>   */
>  
> -#include "matrix.h"
> +#include "libcamera/internal/matrix.h"
>  
>  #include <libcamera/base/log.h>
>  
> @@ -18,8 +18,6 @@ namespace libcamera {
>  
>  LOG_DEFINE_CATEGORY(Matrix)
>  
> -namespace ipa {
> -
>  /**
>   * \class Matrix
>   * \brief Matrix class
> @@ -34,7 +32,7 @@ namespace ipa {
>   */
>  
>  /**
> - * \fn Matrix::Matrix(const std::vector<T> &data)
> + * \fn Matrix::Matrix(const Span<T> &data)
>   * \brief Construct a matrix from supplied data
>   * \param[in] data Data from which to construct a matrix
>   *
> @@ -144,6 +142,4 @@ bool matrixValidateYaml(const YamlObject &obj, unsigned int size)
>  }
>  #endif /* __DOXYGEN__ */
>  
> -} /* namespace ipa */
> -
>  } /* namespace libcamera */
> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> index 21cae11756d6..57fde8a8fab0 100644
> --- a/src/libcamera/meson.build
> +++ b/src/libcamera/meson.build
> @@ -40,6 +40,7 @@ libcamera_internal_sources = files([
>      'ipc_pipe_unixsocket.cpp',
>      'ipc_unixsocket.cpp',
>      'mapped_framebuffer.cpp',
> +    'matrix.cpp',
>      'media_device.cpp',
>      'media_object.cpp',
>      'pipeline_handler.cpp',
Stefan Klug Nov. 18, 2024, 4:52 p.m. UTC | #2
Hi Laurent,

Thank you for the review. 

On Mon, Nov 18, 2024 at 05:59:13PM +0200, Laurent Pinchart wrote:
> Hi Stefan,
> 
> Thank you for the patch.
> 
> On Mon, Nov 18, 2024 at 04:05:05PM +0100, Stefan Klug wrote:
> > The matrix code copied from libipa needs to be adapted to compile and
> > work from the new location. To keep the project buildable at all times,
> > these changes are not split into multiple commits but kept as a single
> > one.
> 
> If you first rename the Matrix class in the RPi IPA to Matrix3x3 then
> you will be able to split this patch better, with the rework of the
> Matrix class (and wiring up to the build system) in separate patches..

Is that really worth the effort? But yes, I can do it.

> 
> > The changes are:
> > - Add matrix class to meson.build
> > - Move matrix class from libipa namespace into libcamera
> > - Add std::initializer_list based constructor to be able to replace the
> >   Matrix implementation contained in the rpi pipeline
> > - Replace Matrix class in rpi pipeline with the new one to prevent name
> >   clashes
> > 
> > Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
> > ---
> >  include/libcamera/internal/matrix.h    | 20 ++++-----
> >  include/libcamera/internal/meson.build |  1 +
> >  src/ipa/rpi/controller/rpi/ccm.cpp     | 56 +++++++-------------------
> >  src/ipa/rpi/controller/rpi/ccm.h       | 35 +---------------
> >  src/libcamera/matrix.cpp               |  8 +---
> >  src/libcamera/meson.build              |  1 +
> >  6 files changed, 32 insertions(+), 89 deletions(-)
> > 
> > diff --git a/include/libcamera/internal/matrix.h b/include/libcamera/internal/matrix.h
> > index 5471e6975b74..b82d33f98658 100644
> > --- a/include/libcamera/internal/matrix.h
> > +++ b/include/libcamera/internal/matrix.h
> > @@ -19,8 +19,6 @@ namespace libcamera {
> >  
> >  LOG_DECLARE_CATEGORY(Matrix)
> >  
> > -namespace ipa {
> > -
> >  #ifndef __DOXYGEN__
> >  template<typename T, unsigned int Rows, unsigned int Cols,
> >  	 std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>
> > @@ -35,6 +33,12 @@ public:
> >  		data_.fill(static_cast<T>(0));
> >  	}
> >  
> > +	Matrix(std::initializer_list<T> data)
> > +	{
> > +		ASSERT(data.size() == Rows * Cols);
> 
> I considered adding a similar constructor to Vector in my latest series,
> but it's problematic for two reasons. One is that the ASSERT() will only
> trigger at runtime, while this should be a compile-time check. We
> coulduse static_assert() instead (and mark the constructor as
> constexpr):
> 
> constexpr Matrix(std::initializer_list<T> data)
> {
> 	static_assert(data.size() == Rows * Cols);
> 	std::copy(data.begin(), data.end(), data_.begin());
> }
> 
> But this will only work if the initializer list is a compile-time
> constant. The following will compile fine:
> 
> void foo()
> {
> 	Matrix<double, 2, 2> m{ 1.0, 2.0, 3.0, 4.0 };
> 	...
> }
> 
> But this won't:
> 
> void foo(double v)
> {
> 	Matrix<double, 2, 2> m{ v, v, v, v };
> 	...
> }
> 
> as the initializer list isn't a compile-time constant.
> 
> This is why I didn't add a constructor taking an initializer_list, but
> instead rely on the constructor taking an std::array. For the Matrix
> class, this would be
> 
> constexpr Matrix(const std::array<T, Rows, Cols> &data)
> {
> 	std::copy(data.begin(), data.end(), data_.begin());
> }
> 
> The user needs to be slightly adapted (note the double curly braces):
> 
> void foo(double v)
> {
> 	Matrix<double, 2, 2> m{{ v, v, v, v }};

I had exactly the same process (static_assert etc.). Just my conclusion
was a bit different. I would rate the syntax (the double braces are
unexpected and require too much knowledge on the internals) over the
missing compile time check (as the assert is on a pretty likely path and
will happen during testing). So I chose the initializer_list. Now I
tried the array and funny enough, there is no need to switch to double
braces - even with our old compilers. They manage to fold it.

> 	...
> }
> 
> The addition of the new constructor could go to a separate patch, before
> this one.
> 
> > +		std::copy(data.begin(), data.end(), data_.begin());
> > +	}
> > +
> >  	Matrix(const std::vector<T> &data)
> >  	{
> >  		std::copy(data.begin(), data.end(), data_.begin());
> 
> This seems very unsafe :-S Let's see if the constructor taking an
> std::array could replace this one.

I already read your comment on the other patch, so I'll remove it.

Cheers,
Stefan

> 
> > @@ -166,24 +170,22 @@ Matrix<T, Rows, Cols> operator+(const Matrix<T, Rows, Cols> &m1, const Matrix<T,
> >  bool matrixValidateYaml(const YamlObject &obj, unsigned int size);
> >  #endif /* __DOXYGEN__ */
> >  
> > -} /* namespace ipa */
> > -
> >  #ifndef __DOXYGEN__
> >  template<typename T, unsigned int Rows, unsigned int Cols>
> > -std::ostream &operator<<(std::ostream &out, const ipa::Matrix<T, Rows, Cols> &m)
> > +std::ostream &operator<<(std::ostream &out, const Matrix<T, Rows, Cols> &m)
> >  {
> >  	out << m.toString();
> >  	return out;
> >  }
> >  
> >  template<typename T, unsigned int Rows, unsigned int Cols>
> > -struct YamlObject::Getter<ipa::Matrix<T, Rows, Cols>> {
> > -	std::optional<ipa::Matrix<T, Rows, Cols>> get(const YamlObject &obj) const
> > +struct YamlObject::Getter<Matrix<T, Rows, Cols>> {
> > +	std::optional<Matrix<T, Rows, Cols>> get(const YamlObject &obj) const
> >  	{
> > -		if (!ipa::matrixValidateYaml(obj, Rows * Cols))
> > +		if (!matrixValidateYaml(obj, Rows * Cols))
> >  			return std::nullopt;
> >  
> > -		ipa::Matrix<T, Rows, Cols> matrix;
> > +		Matrix<T, Rows, Cols> matrix;
> >  		T *data = &matrix[0][0];
> >  
> >  		unsigned int i = 0;
> > diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
> > index 1dddcd50c90b..7d6aa8b72bd7 100644
> > --- a/include/libcamera/internal/meson.build
> > +++ b/include/libcamera/internal/meson.build
> > @@ -29,6 +29,7 @@ libcamera_internal_headers = files([
> >      'ipc_pipe.h',
> >      'ipc_unixsocket.h',
> >      'mapped_framebuffer.h',
> > +    'matrix.h',
> >      'media_device.h',
> >      'media_object.h',
> >      'pipeline_handler.h',
> > diff --git a/src/ipa/rpi/controller/rpi/ccm.cpp b/src/ipa/rpi/controller/rpi/ccm.cpp
> > index aefa580c9a4b..418bead56ecd 100644
> > --- a/src/ipa/rpi/controller/rpi/ccm.cpp
> > +++ b/src/ipa/rpi/controller/rpi/ccm.cpp
> > @@ -29,34 +29,7 @@ LOG_DEFINE_CATEGORY(RPiCcm)
> >  
> >  #define NAME "rpi.ccm"
> >  
> > -Matrix::Matrix()
> > -{
> > -	memset(m, 0, sizeof(m));
> > -}
> > -Matrix::Matrix(double m0, double m1, double m2, double m3, double m4, double m5,
> > -	       double m6, double m7, double m8)
> > -{
> > -	m[0][0] = m0, m[0][1] = m1, m[0][2] = m2, m[1][0] = m3, m[1][1] = m4,
> > -	m[1][2] = m5, m[2][0] = m6, m[2][1] = m7, m[2][2] = m8;
> > -}
> > -int Matrix::read(const libcamera::YamlObject &params)
> > -{
> > -	double *ptr = (double *)m;
> > -
> > -	if (params.size() != 9) {
> > -		LOG(RPiCcm, Error) << "Wrong number of values in CCM";
> > -		return -EINVAL;
> > -	}
> > -
> > -	for (const auto &param : params.asList()) {
> > -		auto value = param.get<double>();
> > -		if (!value)
> > -			return -EINVAL;
> > -		*ptr++ = *value;
> > -	}
> > -
> > -	return 0;
> > -}
> > +using Matrix3x3 = Matrix<double, 3, 3>;
> >  
> >  Ccm::Ccm(Controller *controller)
> >  	: CcmAlgorithm(controller), saturation_(1.0) {}
> > @@ -68,8 +41,6 @@ char const *Ccm::name() const
> >  
> >  int Ccm::read(const libcamera::YamlObject &params)
> >  {
> > -	int ret;
> > -
> >  	if (params.contains("saturation")) {
> >  		config_.saturation = params["saturation"].get<ipa::Pwl>(ipa::Pwl{});
> >  		if (config_.saturation.empty())
> > @@ -83,9 +54,12 @@ int Ccm::read(const libcamera::YamlObject &params)
> >  
> >  		CtCcm ctCcm;
> >  		ctCcm.ct = *value;
> > -		ret = ctCcm.ccm.read(p["ccm"]);
> > -		if (ret)
> > -			return ret;
> > +
> > +		auto ccm = p["ccm"].get<Matrix3x3>();
> > +		if (!ccm)
> > +			return -EINVAL;
> > +
> > +		ctCcm.ccm = *ccm;
> >  
> >  		if (!config_.ccms.empty() && ctCcm.ct <= config_.ccms.back().ct) {
> >  			LOG(RPiCcm, Error)
> > @@ -125,7 +99,7 @@ bool getLocked(Metadata *metadata, std::string const &tag, T &value)
> >  	return true;
> >  }
> >  
> > -Matrix calculateCcm(std::vector<CtCcm> const &ccms, double ct)
> > +Matrix3x3 calculateCcm(std::vector<CtCcm> const &ccms, double ct)
> >  {
> >  	if (ct <= ccms.front().ct)
> >  		return ccms.front().ccm;
> > @@ -141,13 +115,13 @@ Matrix calculateCcm(std::vector<CtCcm> const &ccms, double ct)
> >  	}
> >  }
> >  
> > -Matrix applySaturation(Matrix const &ccm, double saturation)
> > +Matrix3x3 applySaturation(Matrix3x3 const &ccm, double saturation)
> >  {
> > -	Matrix RGB2Y(0.299, 0.587, 0.114, -0.169, -0.331, 0.500, 0.500, -0.419,
> > -		     -0.081);
> > -	Matrix Y2RGB(1.000, 0.000, 1.402, 1.000, -0.345, -0.714, 1.000, 1.771,
> > -		     0.000);
> > -	Matrix S(1, 0, 0, 0, saturation, 0, 0, 0, saturation);
> > +	Matrix3x3 RGB2Y({ 0.299, 0.587, 0.114, -0.169, -0.331, 0.500, 0.500,
> > +			  -0.419, -0.081 });
> > +	Matrix3x3 Y2RGB({ 1.000, 0.000, 1.402, 1.000, -0.345, -0.714, 1.000,
> > +			  1.771, 0.000 });
> 
> While at it, these two should be static const.
> 
> > +	Matrix3x3 S({ 1, 0, 0, 0, saturation, 0, 0, 0, saturation });
> >  	return Y2RGB * S * RGB2Y * ccm;
> >  }
> >  
> > @@ -181,7 +155,7 @@ void Ccm::prepare(Metadata *imageMetadata)
> >  	for (int j = 0; j < 3; j++)
> >  		for (int i = 0; i < 3; i++)
> >  			ccmStatus.matrix[j * 3 + i] =
> > -				std::max(-8.0, std::min(7.9999, ccm.m[j][i]));
> > +				std::max(-8.0, std::min(7.9999, ccm[j][i]));
> >  	LOG(RPiCcm, Debug)
> >  		<< "colour temperature " << awb.temperatureK << "K";
> >  	LOG(RPiCcm, Debug)
> > diff --git a/src/ipa/rpi/controller/rpi/ccm.h b/src/ipa/rpi/controller/rpi/ccm.h
> > index 4e5b33fefa4e..c05dbb17a264 100644
> > --- a/src/ipa/rpi/controller/rpi/ccm.h
> > +++ b/src/ipa/rpi/controller/rpi/ccm.h
> > @@ -8,6 +8,7 @@
> >  
> >  #include <vector>
> >  
> > +#include "libcamera/internal/matrix.h"
> 
> Missing blank line.
> 
> >  #include <libipa/pwl.h>
> >  
> >  #include "../ccm_algorithm.h"
> > @@ -16,41 +17,9 @@ namespace RPiController {
> >  
> >  /* Algorithm to calculate colour matrix. Should be placed after AWB. */
> >  
> > -struct Matrix {
> > -	Matrix(double m0, double m1, double m2, double m3, double m4, double m5,
> > -	       double m6, double m7, double m8);
> > -	Matrix();
> > -	double m[3][3];
> > -	int read(const libcamera::YamlObject &params);
> > -};
> > -static inline Matrix operator*(double d, Matrix const &m)
> > -{
> > -	return Matrix(m.m[0][0] * d, m.m[0][1] * d, m.m[0][2] * d,
> > -		      m.m[1][0] * d, m.m[1][1] * d, m.m[1][2] * d,
> > -		      m.m[2][0] * d, m.m[2][1] * d, m.m[2][2] * d);
> > -}
> > -static inline Matrix operator*(Matrix const &m1, Matrix const &m2)
> > -{
> > -	Matrix m;
> > -	for (int i = 0; i < 3; i++)
> > -		for (int j = 0; j < 3; j++)
> > -			m.m[i][j] = m1.m[i][0] * m2.m[0][j] +
> > -				    m1.m[i][1] * m2.m[1][j] +
> > -				    m1.m[i][2] * m2.m[2][j];
> > -	return m;
> > -}
> > -static inline Matrix operator+(Matrix const &m1, Matrix const &m2)
> > -{
> > -	Matrix m;
> > -	for (int i = 0; i < 3; i++)
> > -		for (int j = 0; j < 3; j++)
> > -			m.m[i][j] = m1.m[i][j] + m2.m[i][j];
> > -	return m;
> > -}
> > -
> >  struct CtCcm {
> >  	double ct;
> > -	Matrix ccm;
> > +	libcamera::Matrix<double, 3, 3> ccm;
> >  };
> >  
> >  struct CcmConfig {
> > diff --git a/src/libcamera/matrix.cpp b/src/libcamera/matrix.cpp
> > index 8346f0d34160..c3ed54895b22 100644
> > --- a/src/libcamera/matrix.cpp
> > +++ b/src/libcamera/matrix.cpp
> > @@ -5,7 +5,7 @@
> >   * Matrix and related operations
> >   */
> >  
> > -#include "matrix.h"
> > +#include "libcamera/internal/matrix.h"
> >  
> >  #include <libcamera/base/log.h>
> >  
> > @@ -18,8 +18,6 @@ namespace libcamera {
> >  
> >  LOG_DEFINE_CATEGORY(Matrix)
> >  
> > -namespace ipa {
> > -
> >  /**
> >   * \class Matrix
> >   * \brief Matrix class
> > @@ -34,7 +32,7 @@ namespace ipa {
> >   */
> >  
> >  /**
> > - * \fn Matrix::Matrix(const std::vector<T> &data)
> > + * \fn Matrix::Matrix(const Span<T> &data)
> >   * \brief Construct a matrix from supplied data
> >   * \param[in] data Data from which to construct a matrix
> >   *
> > @@ -144,6 +142,4 @@ bool matrixValidateYaml(const YamlObject &obj, unsigned int size)
> >  }
> >  #endif /* __DOXYGEN__ */
> >  
> > -} /* namespace ipa */
> > -
> >  } /* namespace libcamera */
> > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> > index 21cae11756d6..57fde8a8fab0 100644
> > --- a/src/libcamera/meson.build
> > +++ b/src/libcamera/meson.build
> > @@ -40,6 +40,7 @@ libcamera_internal_sources = files([
> >      'ipc_pipe_unixsocket.cpp',
> >      'ipc_unixsocket.cpp',
> >      'mapped_framebuffer.cpp',
> > +    'matrix.cpp',
> >      'media_device.cpp',
> >      'media_object.cpp',
> >      'pipeline_handler.cpp',
> 
> -- 
> Regards,
> 
> Laurent Pinchart

Patch
diff mbox series

diff --git a/include/libcamera/internal/matrix.h b/include/libcamera/internal/matrix.h
index 5471e6975b74..b82d33f98658 100644
--- a/include/libcamera/internal/matrix.h
+++ b/include/libcamera/internal/matrix.h
@@ -19,8 +19,6 @@  namespace libcamera {
 
 LOG_DECLARE_CATEGORY(Matrix)
 
-namespace ipa {
-
 #ifndef __DOXYGEN__
 template<typename T, unsigned int Rows, unsigned int Cols,
 	 std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>
@@ -35,6 +33,12 @@  public:
 		data_.fill(static_cast<T>(0));
 	}
 
+	Matrix(std::initializer_list<T> data)
+	{
+		ASSERT(data.size() == Rows * Cols);
+		std::copy(data.begin(), data.end(), data_.begin());
+	}
+
 	Matrix(const std::vector<T> &data)
 	{
 		std::copy(data.begin(), data.end(), data_.begin());
@@ -166,24 +170,22 @@  Matrix<T, Rows, Cols> operator+(const Matrix<T, Rows, Cols> &m1, const Matrix<T,
 bool matrixValidateYaml(const YamlObject &obj, unsigned int size);
 #endif /* __DOXYGEN__ */
 
-} /* namespace ipa */
-
 #ifndef __DOXYGEN__
 template<typename T, unsigned int Rows, unsigned int Cols>
-std::ostream &operator<<(std::ostream &out, const ipa::Matrix<T, Rows, Cols> &m)
+std::ostream &operator<<(std::ostream &out, const Matrix<T, Rows, Cols> &m)
 {
 	out << m.toString();
 	return out;
 }
 
 template<typename T, unsigned int Rows, unsigned int Cols>
-struct YamlObject::Getter<ipa::Matrix<T, Rows, Cols>> {
-	std::optional<ipa::Matrix<T, Rows, Cols>> get(const YamlObject &obj) const
+struct YamlObject::Getter<Matrix<T, Rows, Cols>> {
+	std::optional<Matrix<T, Rows, Cols>> get(const YamlObject &obj) const
 	{
-		if (!ipa::matrixValidateYaml(obj, Rows * Cols))
+		if (!matrixValidateYaml(obj, Rows * Cols))
 			return std::nullopt;
 
-		ipa::Matrix<T, Rows, Cols> matrix;
+		Matrix<T, Rows, Cols> matrix;
 		T *data = &matrix[0][0];
 
 		unsigned int i = 0;
diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
index 1dddcd50c90b..7d6aa8b72bd7 100644
--- a/include/libcamera/internal/meson.build
+++ b/include/libcamera/internal/meson.build
@@ -29,6 +29,7 @@  libcamera_internal_headers = files([
     'ipc_pipe.h',
     'ipc_unixsocket.h',
     'mapped_framebuffer.h',
+    'matrix.h',
     'media_device.h',
     'media_object.h',
     'pipeline_handler.h',
diff --git a/src/ipa/rpi/controller/rpi/ccm.cpp b/src/ipa/rpi/controller/rpi/ccm.cpp
index aefa580c9a4b..418bead56ecd 100644
--- a/src/ipa/rpi/controller/rpi/ccm.cpp
+++ b/src/ipa/rpi/controller/rpi/ccm.cpp
@@ -29,34 +29,7 @@  LOG_DEFINE_CATEGORY(RPiCcm)
 
 #define NAME "rpi.ccm"
 
-Matrix::Matrix()
-{
-	memset(m, 0, sizeof(m));
-}
-Matrix::Matrix(double m0, double m1, double m2, double m3, double m4, double m5,
-	       double m6, double m7, double m8)
-{
-	m[0][0] = m0, m[0][1] = m1, m[0][2] = m2, m[1][0] = m3, m[1][1] = m4,
-	m[1][2] = m5, m[2][0] = m6, m[2][1] = m7, m[2][2] = m8;
-}
-int Matrix::read(const libcamera::YamlObject &params)
-{
-	double *ptr = (double *)m;
-
-	if (params.size() != 9) {
-		LOG(RPiCcm, Error) << "Wrong number of values in CCM";
-		return -EINVAL;
-	}
-
-	for (const auto &param : params.asList()) {
-		auto value = param.get<double>();
-		if (!value)
-			return -EINVAL;
-		*ptr++ = *value;
-	}
-
-	return 0;
-}
+using Matrix3x3 = Matrix<double, 3, 3>;
 
 Ccm::Ccm(Controller *controller)
 	: CcmAlgorithm(controller), saturation_(1.0) {}
@@ -68,8 +41,6 @@  char const *Ccm::name() const
 
 int Ccm::read(const libcamera::YamlObject &params)
 {
-	int ret;
-
 	if (params.contains("saturation")) {
 		config_.saturation = params["saturation"].get<ipa::Pwl>(ipa::Pwl{});
 		if (config_.saturation.empty())
@@ -83,9 +54,12 @@  int Ccm::read(const libcamera::YamlObject &params)
 
 		CtCcm ctCcm;
 		ctCcm.ct = *value;
-		ret = ctCcm.ccm.read(p["ccm"]);
-		if (ret)
-			return ret;
+
+		auto ccm = p["ccm"].get<Matrix3x3>();
+		if (!ccm)
+			return -EINVAL;
+
+		ctCcm.ccm = *ccm;
 
 		if (!config_.ccms.empty() && ctCcm.ct <= config_.ccms.back().ct) {
 			LOG(RPiCcm, Error)
@@ -125,7 +99,7 @@  bool getLocked(Metadata *metadata, std::string const &tag, T &value)
 	return true;
 }
 
-Matrix calculateCcm(std::vector<CtCcm> const &ccms, double ct)
+Matrix3x3 calculateCcm(std::vector<CtCcm> const &ccms, double ct)
 {
 	if (ct <= ccms.front().ct)
 		return ccms.front().ccm;
@@ -141,13 +115,13 @@  Matrix calculateCcm(std::vector<CtCcm> const &ccms, double ct)
 	}
 }
 
-Matrix applySaturation(Matrix const &ccm, double saturation)
+Matrix3x3 applySaturation(Matrix3x3 const &ccm, double saturation)
 {
-	Matrix RGB2Y(0.299, 0.587, 0.114, -0.169, -0.331, 0.500, 0.500, -0.419,
-		     -0.081);
-	Matrix Y2RGB(1.000, 0.000, 1.402, 1.000, -0.345, -0.714, 1.000, 1.771,
-		     0.000);
-	Matrix S(1, 0, 0, 0, saturation, 0, 0, 0, saturation);
+	Matrix3x3 RGB2Y({ 0.299, 0.587, 0.114, -0.169, -0.331, 0.500, 0.500,
+			  -0.419, -0.081 });
+	Matrix3x3 Y2RGB({ 1.000, 0.000, 1.402, 1.000, -0.345, -0.714, 1.000,
+			  1.771, 0.000 });
+	Matrix3x3 S({ 1, 0, 0, 0, saturation, 0, 0, 0, saturation });
 	return Y2RGB * S * RGB2Y * ccm;
 }
 
@@ -181,7 +155,7 @@  void Ccm::prepare(Metadata *imageMetadata)
 	for (int j = 0; j < 3; j++)
 		for (int i = 0; i < 3; i++)
 			ccmStatus.matrix[j * 3 + i] =
-				std::max(-8.0, std::min(7.9999, ccm.m[j][i]));
+				std::max(-8.0, std::min(7.9999, ccm[j][i]));
 	LOG(RPiCcm, Debug)
 		<< "colour temperature " << awb.temperatureK << "K";
 	LOG(RPiCcm, Debug)
diff --git a/src/ipa/rpi/controller/rpi/ccm.h b/src/ipa/rpi/controller/rpi/ccm.h
index 4e5b33fefa4e..c05dbb17a264 100644
--- a/src/ipa/rpi/controller/rpi/ccm.h
+++ b/src/ipa/rpi/controller/rpi/ccm.h
@@ -8,6 +8,7 @@ 
 
 #include <vector>
 
+#include "libcamera/internal/matrix.h"
 #include <libipa/pwl.h>
 
 #include "../ccm_algorithm.h"
@@ -16,41 +17,9 @@  namespace RPiController {
 
 /* Algorithm to calculate colour matrix. Should be placed after AWB. */
 
-struct Matrix {
-	Matrix(double m0, double m1, double m2, double m3, double m4, double m5,
-	       double m6, double m7, double m8);
-	Matrix();
-	double m[3][3];
-	int read(const libcamera::YamlObject &params);
-};
-static inline Matrix operator*(double d, Matrix const &m)
-{
-	return Matrix(m.m[0][0] * d, m.m[0][1] * d, m.m[0][2] * d,
-		      m.m[1][0] * d, m.m[1][1] * d, m.m[1][2] * d,
-		      m.m[2][0] * d, m.m[2][1] * d, m.m[2][2] * d);
-}
-static inline Matrix operator*(Matrix const &m1, Matrix const &m2)
-{
-	Matrix m;
-	for (int i = 0; i < 3; i++)
-		for (int j = 0; j < 3; j++)
-			m.m[i][j] = m1.m[i][0] * m2.m[0][j] +
-				    m1.m[i][1] * m2.m[1][j] +
-				    m1.m[i][2] * m2.m[2][j];
-	return m;
-}
-static inline Matrix operator+(Matrix const &m1, Matrix const &m2)
-{
-	Matrix m;
-	for (int i = 0; i < 3; i++)
-		for (int j = 0; j < 3; j++)
-			m.m[i][j] = m1.m[i][j] + m2.m[i][j];
-	return m;
-}
-
 struct CtCcm {
 	double ct;
-	Matrix ccm;
+	libcamera::Matrix<double, 3, 3> ccm;
 };
 
 struct CcmConfig {
diff --git a/src/libcamera/matrix.cpp b/src/libcamera/matrix.cpp
index 8346f0d34160..c3ed54895b22 100644
--- a/src/libcamera/matrix.cpp
+++ b/src/libcamera/matrix.cpp
@@ -5,7 +5,7 @@ 
  * Matrix and related operations
  */
 
-#include "matrix.h"
+#include "libcamera/internal/matrix.h"
 
 #include <libcamera/base/log.h>
 
@@ -18,8 +18,6 @@  namespace libcamera {
 
 LOG_DEFINE_CATEGORY(Matrix)
 
-namespace ipa {
-
 /**
  * \class Matrix
  * \brief Matrix class
@@ -34,7 +32,7 @@  namespace ipa {
  */
 
 /**
- * \fn Matrix::Matrix(const std::vector<T> &data)
+ * \fn Matrix::Matrix(const Span<T> &data)
  * \brief Construct a matrix from supplied data
  * \param[in] data Data from which to construct a matrix
  *
@@ -144,6 +142,4 @@  bool matrixValidateYaml(const YamlObject &obj, unsigned int size)
 }
 #endif /* __DOXYGEN__ */
 
-} /* namespace ipa */
-
 } /* namespace libcamera */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index 21cae11756d6..57fde8a8fab0 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -40,6 +40,7 @@  libcamera_internal_sources = files([
     'ipc_pipe_unixsocket.cpp',
     'ipc_unixsocket.cpp',
     'mapped_framebuffer.cpp',
+    'matrix.cpp',
     'media_device.cpp',
     'media_object.cpp',
     'pipeline_handler.cpp',