The Matrix constructor that takes a std::vector is meant and only used to initialize a Matrix from an initializer list. Using a std::vector is problematic for two reasons. First, it requires constructing a vector, copying the data from the initializer list, which is an expensive operation. Then, the vector size can't be verified at compile time, making the constructor unsafe. The first issue could be solved by replacing the vector with a std::initializer_list or a Span. The second issue would require checking the initializer list size with a static assertion, or restricting usage of the constructor to fixed-extent spans. Unfortunately, even if the size of initializer lists is always known at compile time, the std::initializer_list::size() function is a compile-time constant only for constant initializer lists. Using a span would work better, but construction of a fixed extent span from an initializer list must be explicit, making the API cumbersome. We can solve all those issues by passing an std::array to the constructor. Construction of an array from an initializer list can be implicit and doesn't involve a copy, and the array size is a template parameter and therefore guaranteed to be a compile-time constant. Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
200 lines
4.4 KiB
C++
200 lines
4.4 KiB
C++
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
/*
|
|
* Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
|
|
*
|
|
* Matrix and related operations
|
|
*/
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <sstream>
|
|
#include <vector>
|
|
|
|
#include <libcamera/base/log.h>
|
|
#include <libcamera/base/span.h>
|
|
|
|
#include "libcamera/internal/yaml_parser.h"
|
|
|
|
namespace libcamera {
|
|
|
|
LOG_DECLARE_CATEGORY(Matrix)
|
|
|
|
#ifndef __DOXYGEN__
|
|
template<typename T, unsigned int Rows, unsigned int Cols,
|
|
std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>
|
|
#else
|
|
template<typename T, unsigned int Rows, unsigned int Cols>
|
|
#endif /* __DOXYGEN__ */
|
|
class Matrix
|
|
{
|
|
public:
|
|
Matrix()
|
|
{
|
|
data_.fill(static_cast<T>(0));
|
|
}
|
|
|
|
Matrix(const std::array<T, Rows * Cols> &data)
|
|
{
|
|
std::copy(data.begin(), data.end(), data_.begin());
|
|
}
|
|
|
|
static Matrix identity()
|
|
{
|
|
Matrix ret;
|
|
for (size_t i = 0; i < std::min(Rows, Cols); i++)
|
|
ret[i][i] = static_cast<T>(1);
|
|
return ret;
|
|
}
|
|
|
|
~Matrix() = default;
|
|
|
|
const std::string toString() const
|
|
{
|
|
std::stringstream out;
|
|
|
|
out << "Matrix { ";
|
|
for (unsigned int i = 0; i < Rows; i++) {
|
|
out << "[ ";
|
|
for (unsigned int j = 0; j < Cols; j++) {
|
|
out << (*this)[i][j];
|
|
out << ((j + 1 < Cols) ? ", " : " ");
|
|
}
|
|
out << ((i + 1 < Rows) ? "], " : "]");
|
|
}
|
|
out << " }";
|
|
|
|
return out.str();
|
|
}
|
|
|
|
Span<const T, Cols> operator[](size_t i) const
|
|
{
|
|
return Span<const T, Cols>{ &data_.data()[i * Cols], Cols };
|
|
}
|
|
|
|
Span<T, Cols> operator[](size_t i)
|
|
{
|
|
return Span<T, Cols>{ &data_.data()[i * Cols], Cols };
|
|
}
|
|
|
|
#ifndef __DOXYGEN__
|
|
template<typename U, std::enable_if_t<std::is_arithmetic_v<U>>>
|
|
#else
|
|
template<typename U>
|
|
#endif /* __DOXYGEN__ */
|
|
Matrix<T, Rows, Cols> &operator*=(U d)
|
|
{
|
|
for (unsigned int i = 0; i < Rows * Cols; i++)
|
|
data_[i] *= d;
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
std::array<T, Rows * Cols> data_;
|
|
};
|
|
|
|
#ifndef __DOXYGEN__
|
|
template<typename T, typename U, unsigned int Rows, unsigned int Cols,
|
|
std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>
|
|
#else
|
|
template<typename T, typename U, unsigned int Rows, unsigned int Cols>
|
|
#endif /* __DOXYGEN__ */
|
|
Matrix<U, Rows, Cols> operator*(T d, const Matrix<U, Rows, Cols> &m)
|
|
{
|
|
Matrix<U, Rows, Cols> result;
|
|
|
|
for (unsigned int i = 0; i < Rows; i++) {
|
|
for (unsigned int j = 0; j < Cols; j++)
|
|
result[i][j] = d * m[i][j];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#ifndef __DOXYGEN__
|
|
template<typename T, typename U, unsigned int Rows, unsigned int Cols,
|
|
std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>
|
|
#else
|
|
template<typename T, typename U, unsigned int Rows, unsigned int Cols>
|
|
#endif /* __DOXYGEN__ */
|
|
Matrix<U, Rows, Cols> operator*(const Matrix<U, Rows, Cols> &m, T d)
|
|
{
|
|
return d * m;
|
|
}
|
|
|
|
#ifndef __DOXYGEN__
|
|
template<typename T,
|
|
unsigned int R1, unsigned int C1,
|
|
unsigned int R2, unsigned int C2,
|
|
std::enable_if_t<C1 == R2> * = nullptr>
|
|
#else
|
|
template<typename T, unsigned int R1, unsigned int C1, unsigned int R2, unsigned in C2>
|
|
#endif /* __DOXYGEN__ */
|
|
Matrix<T, R1, C2> operator*(const Matrix<T, R1, C1> &m1, const Matrix<T, R2, C2> &m2)
|
|
{
|
|
Matrix<T, R1, C2> result;
|
|
|
|
for (unsigned int i = 0; i < R1; i++) {
|
|
for (unsigned int j = 0; j < C2; j++) {
|
|
T sum = 0;
|
|
|
|
for (unsigned int k = 0; k < C1; k++)
|
|
sum += m1[i][k] * m2[k][j];
|
|
|
|
result[i][j] = sum;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template<typename T, unsigned int Rows, unsigned int Cols>
|
|
Matrix<T, Rows, Cols> operator+(const Matrix<T, Rows, Cols> &m1, const Matrix<T, Rows, Cols> &m2)
|
|
{
|
|
Matrix<T, Rows, Cols> result;
|
|
|
|
for (unsigned int i = 0; i < Rows; i++) {
|
|
for (unsigned int j = 0; j < Cols; j++)
|
|
result[i][j] = m1[i][j] + m2[i][j];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#ifndef __DOXYGEN__
|
|
bool matrixValidateYaml(const YamlObject &obj, unsigned int size);
|
|
#endif /* __DOXYGEN__ */
|
|
|
|
#ifndef __DOXYGEN__
|
|
template<typename T, unsigned int Rows, unsigned int Cols>
|
|
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<Matrix<T, Rows, Cols>> {
|
|
std::optional<Matrix<T, Rows, Cols>> get(const YamlObject &obj) const
|
|
{
|
|
if (!matrixValidateYaml(obj, Rows * Cols))
|
|
return std::nullopt;
|
|
|
|
Matrix<T, Rows, Cols> matrix;
|
|
T *data = &matrix[0][0];
|
|
|
|
unsigned int i = 0;
|
|
for (const YamlObject &entry : obj.asList()) {
|
|
const auto value = entry.get<T>();
|
|
if (!value)
|
|
return std::nullopt;
|
|
|
|
data[i++] = *value;
|
|
}
|
|
|
|
return matrix;
|
|
}
|
|
};
|
|
#endif /* __DOXYGEN__ */
|
|
|
|
} /* namespace libcamera */
|