diff --git a/include/libcamera/internal/matrix.h b/include/libcamera/internal/matrix.h
index f74cda103c72..45ce0000a4cf 100644
--- a/include/libcamera/internal/matrix.h
+++ b/include/libcamera/internal/matrix.h
@@ -76,6 +76,8 @@ public:
 
 	constexpr std::span<const T, Rows * Cols> data() const { return data_; }
 
+	constexpr std::span<T, Rows * Cols> data() { return data_; }
+
 	constexpr std::span<const T, Cols> operator[](size_t i) const
 	{
 		return std::span<const T, Cols>{ &data_.data()[i * Cols], Cols };
@@ -115,6 +117,23 @@ public:
 		return inverse;
 	}
 
+	template<typename U = T>
+	[[nodiscard]]
+	constexpr Matrix<U, Cols, Rows> transpose() const
+	{
+		static_assert(std::is_convertible_v<T, U>);
+
+		Matrix<U, Cols, Rows> transposed;
+		std::span<U, Rows * Cols> data = transposed.data();
+
+		for (unsigned int r = 0; r < Rows; ++r) {
+			for (unsigned int c = 0; c < Cols; ++c)
+				data[c * Rows + r] = data_[r * Cols + c];
+		}
+
+		return transposed;
+	}
+
 private:
 	/*
 	 * \todo The initializer is only necessary for the constructor to be
diff --git a/src/libcamera/matrix.cpp b/src/libcamera/matrix.cpp
index 9cb0885b3b54..cbaaaa9634e8 100644
--- a/src/libcamera/matrix.cpp
+++ b/src/libcamera/matrix.cpp
@@ -69,7 +69,7 @@ LOG_DEFINE_CATEGORY(Matrix)
  */
 
 /**
- * \fn Matrix::data()
+ * \fn Matrix::data() const
  * \brief Access the matrix data as a linear array
  *
  * Access the contents of the matrix as a one-dimensional linear array of
@@ -79,6 +79,11 @@ LOG_DEFINE_CATEGORY(Matrix)
  * \return A span referencing the matrix data as a linear array
  */
 
+/**
+ * \fn Matrix::data()
+ * \copydoc Matrix::data() const
+ */
+
 /**
  * \fn std::span<const T, Cols> Matrix::operator[](size_t i) const
  * \brief Index to a row in the matrix
@@ -107,6 +112,19 @@ LOG_DEFINE_CATEGORY(Matrix)
  * \return The inverse of the matrix
  */
 
+/**
+ * \fn Matrix::transpose() const
+ * \brief Compute the transpose of the matrix
+ * \tparam U Type of the numerical values in the tranposed matrix
+ *
+ * This function computes the transpose of the matrix. The optional template
+ * parameter \a U specifies the type of the numerical values in the result. It
+ * defaults to \a T, and can be specified manually to convert to a different
+ * data type while transposing.
+ *
+ * \return The transpose of the matrix
+ */
+
 /**
  * \fn Matrix::operator[](size_t i)
  * \copydoc Matrix::operator[](size_t i) const
diff --git a/test/matrix.cpp b/test/matrix.cpp
index 4afae2da7866..9c69554bd1ee 100644
--- a/test/matrix.cpp
+++ b/test/matrix.cpp
@@ -46,6 +46,15 @@ protected:
 		ASSERT_EQ(m5[1][0], 0.0);
 		ASSERT_EQ(m5[1][1], 1.0);
 
+		Matrix<int, 2, 3> m6({ 1, 2, 3, 4, 5, 6 });
+		Matrix<unsigned int, 3, 2> m7 = m6.transpose<unsigned int>();
+		ASSERT_EQ(m7[0][0], 1);
+		ASSERT_EQ(m7[0][1], 4);
+		ASSERT_EQ(m7[1][0], 2);
+		ASSERT_EQ(m7[1][1], 5);
+		ASSERT_EQ(m7[2][0], 3);
+		ASSERT_EQ(m7[2][1], 6);
+
 		return TestPass;
 	}
 };
