From patchwork Fri May 31 14:42:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 20177 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id F2EF1BDE6B for ; Fri, 31 May 2024 14:43:18 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 95C93634BA; Fri, 31 May 2024 16:43:18 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="hudna2uv"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B6CDF634AF for ; Fri, 31 May 2024 16:43:14 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (h175-177-049-156.catv02.itscom.jp [175.177.49.156]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 5954C66B; Fri, 31 May 2024 16:43:08 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1717166589; bh=NmvvQHugKpoVRJPvz4qjDW1LKaBuuIVLKQa9a7uzwSs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hudna2uvI7u0qxEo8d4SFw89cI9dmmmJ/OGYstLKLDfT5DttmgXKpaHRSRzS9nNPp R2b8NQOjAzpcvK9FVSpqPlXA2paJRa6UVC/XNxnbM8znWjWj1LWmzVFN1sVGOfU+Uh I6szALcg0Gf/mzftGr2qCbAipIejim/wYGYgmQXk= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , Stefan Klug , David Plowman Subject: [PATCH v4 1/4] libcamera: geometry: Add floating-point version of Point class Date: Fri, 31 May 2024 23:42:58 +0900 Message-Id: <20240531144301.3950115-2-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240531144301.3950115-1-paul.elder@ideasonboard.com> References: <20240531144301.3950115-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The piecewise linear function (Pwl) class from the Raspberry Pi IPA has its own Point class while one already exists in libcamera's geometry.h header. The reason is because libcamera's Point is on the plane of integers, while Raspberry Pi's is on the plane of reals. While making this a template class might be cleaner, it was deemed to be too intrusive of a change, and it might not feel nice to need to specify the type from a public API point of view. Hence we introduce a PointF class to designate a Point class on the plane of reals. This is in preparation for copying/moving the Pwl class from the Raspberry Pi IPA to libipa. Signed-off-by: Paul Elder Reviewed-by: Stefan Klug Acked-by: David Plowman --- No change in v4 Changes in v3: - fix typoes in commit message Changes in v2: - renamed FPoint to PointF - add documentation - add tests --- include/libcamera/geometry.h | 65 +++++++ src/libcamera/geometry.cpp | 123 +++++++++++- test/geometry.cpp | 355 +++++++++++++++++++++++++++++++++++ 3 files changed, 542 insertions(+), 1 deletion(-) diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h index 3e6f0f5d7..81a697398 100644 --- a/include/libcamera/geometry.h +++ b/include/libcamera/geometry.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include @@ -49,6 +50,70 @@ static inline bool operator!=(const Point &lhs, const Point &rhs) std::ostream &operator<<(std::ostream &out, const Point &p); +struct PointF { + constexpr PointF() + : x(0), y(0) + { + } + + constexpr PointF(double _x, double _y) + : x(_x), y(_y) + { + } + + constexpr PointF operator-() const + { + return PointF{ -x, -y }; + } + + constexpr PointF operator-(const PointF &p) const + { + return PointF(x - p.x, y - p.y); + } + + constexpr PointF operator+(const PointF &p) const + { + return PointF(x + p.x, y + p.y); + } + + constexpr double operator%(const PointF &p) const + { + return x * p.x + y * p.y; + } + + constexpr PointF operator*(double f) const + { + return PointF(x * f, y * f); + } + + constexpr PointF operator/(double f) const + { + return PointF(x / f, y / f); + } + + constexpr double len2() const + { + return x * x + y * y; + } + + constexpr double len() const + { + return std::sqrt(len2()); + } + + const std::string toString() const; + + double x, y; +}; + +bool operator==(const PointF &lhs, const PointF &rhs); +static inline bool operator!=(const PointF &lhs, const PointF &rhs) +{ + return !(lhs == rhs); +} + +std::ostream &operator<<(std::ostream &out, const PointF &p); + class Size { public: diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp index 000151364..2ab4c60f4 100644 --- a/src/libcamera/geometry.cpp +++ b/src/libcamera/geometry.cpp @@ -21,7 +21,7 @@ namespace libcamera { /** * \class Point - * \brief Describe a point in two-dimensional space + * \brief Describe a point in two-dimensional integer space * * The Point structure defines a point in two-dimensional space with integer * precision. The coordinates of a Point may be negative as well as positive. @@ -94,6 +94,127 @@ std::ostream &operator<<(std::ostream &out, const Point &p) return out; } +/** + * \class PointF + * \brief Describe a point in two-dimensional real space + * + * The Point structure defines a point in two-dimensional space with double + * precision. The coordinates of a Point may be negative as well as positive. + * + * This class exists separately from Point to not require all users of the + * Point class to have to use template parameters to specify a type. + */ + +/** + * \fn PointF::PointF() + * \copydoc libcamera::Point::Point + */ + +/** + * \fn PointF::PointF(double _x, double _y) + * \brief Construct a PointF at given \a _x and \a _y values + * \param[in] _x The x-coordinate + * \param[in] _y The y-coordinate + */ + +/** + * \var PointF::x + * \copydoc libcamera::Point::x + */ + +/** + * \var PointF::y + * \copydoc libcamera::Point::y + */ + +/** + * \fn constexpr PointF PointF::operator-() const + * \copydoc libcamera::Point::operator- + */ + +/** + * \fn constexpr PointF PointF::operator-(PointF const &p) const + * \brief Subtract one point from another, as if they were vectors + * \param[in] p The other point + * \return The difference of p from this point + */ + +/** + * \fn PointF::operator+() + * \brief Add two points together, as if they were vectors + * \param[in] p The other point + * \return The sum of the two points + */ + +/** + * \fn PointF::operator%() + * \brief Compute the dot product, treating the points as vectors + * \param[in] p The other point + * \return The dot product of the two points + */ + +/** + * \fn PointF::operator*() + * \brief Scale up the point, as if it were a vector + * \param[in] f The factor + * \return The scaled point + */ + +/** + * \fn PointF::operator/() + * \brief Scale down the point, as if it were a vector + * \param[in] f The factor + * \return The scaled point + */ + +/** + * \fn PointF::len2() + * \brief Get the squared length of the point, as if it were a vector + * \return The squared length of the point + */ + +/** + * \fn PointF::len() + * \brief Get the length of the point, as if it were a vector + * \return The length of the point + */ + +/** + * \copydoc Point::toString() + */ +const std::string PointF::toString() const +{ + std::stringstream ss; + ss << *this; + + return ss.str(); +} + +/** + * \copydoc operator==(const Point &lhs, const Point &rhs) + */ +bool operator==(const PointF &lhs, const PointF &rhs) +{ + return lhs.x == rhs.x && lhs.y == rhs.y; +} + +/** + * \fn bool operator!=(const Point &lhs, const Point &rhs) + * \copydoc libcamera::Point::operator!= + */ + +/** + * \brief Insert a text representation of a PointF into an output stream + * \param[in] out The output stream + * \param[in] p The point + * \return The output stream \a out + */ +std::ostream &operator<<(std::ostream &out, const PointF &p) +{ + out << "(" << p.x << ", " << p.y << ")"; + return out; +} + /** * \class Size * \brief Describe a two-dimensional size diff --git a/test/geometry.cpp b/test/geometry.cpp index 64169206a..54f1ce4ab 100644 --- a/test/geometry.cpp +++ b/test/geometry.cpp @@ -33,6 +33,53 @@ protected: return true; } + template + bool compareF(const T &lhs, const U &rhs, + V (PointF::*op)(const U &) const, + const char *opName, V expect) + { + V result = (lhs.*op)(rhs); + + if (result != expect) { + cout << lhs << opName << " " << rhs + << "test failed" << std::endl; + return false; + } + + return true; + } + + template + bool compareFScale(const T &lhs, const U &rhs, + V (PointF::*op)(U) const, + const char *opName, V expect) + { + V result = (lhs.*op)(rhs); + + if (result != expect) { + cout << lhs << opName << " " << rhs + << "test failed" << std::endl; + return false; + } + + return true; + } + + template + bool compareFLen(const T &lhs, double (PointF::*op)() const, + const char *opName, double expect) + { + double result = (lhs.*op)(); + + if (result != expect) { + cout << lhs << opName + << "test failed" << std::endl; + return false; + } + + return true; + } + int run() { /* @@ -88,6 +135,314 @@ protected: return TestFail; } + /* + * PointF tests + */ + + /* Equality */ + if (!compare(PointF(50.1, 100.1), PointF(50.1, 100.1), &operator==, "==", true)) + return TestFail; + + if (!compare(PointF(-50.1, 100.1), PointF(-50.1, 100.1), &operator==, "==", true)) + return TestFail; + + if (!compare(PointF(50.1, -100.1), PointF(50.1, -100.1), &operator==, "==", true)) + return TestFail; + + if (!compare(PointF(-50.1, -100.1), PointF(-50.1, -100.1), &operator==, "==", true)) + return TestFail; + + if (!compare(PointF(-50.1, -100.0), PointF(-50.1, -100), &operator==, "==", true)) + return TestFail; + + /* Inequality */ + if (!compare(PointF(50.1, 100.1), PointF(50.1, 100.2), &operator!=, "!=", true)) + return TestFail; + + if (!compare(PointF(-50.1, 100.1), PointF(-50.1, 100.01), &operator!=, "!=", true)) + return TestFail; + + if (!compare(PointF(-50.1, 100.1), PointF(-50.1, 100.1), &operator!=, "!=", false)) + return TestFail; + + if (!compare(PointF(50.1, -100.1), PointF(50.2, -100.1), &operator!=, "!=", true)) + return TestFail; + + if (!compare(PointF(-50.1, -100.1), PointF(-50.01, -100.0), &operator!=, "!=", true)) + return TestFail; + + if (!compare(PointF(-50.1, 100.1), PointF(50.1, 100.1), &operator!=, "!=", true)) + return TestFail; + + if (!compare(PointF(50.1, -100.1), PointF(50.1, 100.1), &operator!=, "!=", true)) + return TestFail; + + if (!compare(PointF(-50.1, -100.1), PointF(-50.1, -100.1), &operator!=, "!=", false)) + return TestFail; + + /* Negation */ + if (PointF(50.1, 100.1) != -PointF(-50.1, -100.1) || + PointF(50.1, 100.1) == -PointF(50.1, -100.1) || + PointF(50.1, 100.1) == -PointF(-50.1, 100.1)) { + cout << "PointF negation test failed" << endl; + return TestFail; + } + + typedef PointF (PointF::*pointfOp)(const PointF &) const; + typedef double (PointF::*pointfDotProd)(const PointF &) const; + typedef PointF (PointF::*pointfScale)(double) const; + typedef double (PointF::*pointfLen)() const; + + /* Subtraction */ + if (!compareF(PointF(50.1, 100.1), PointF(50.1, 100.1), + static_cast(&PointF::operator-), "-", + PointF(0, 0))) + return TestFail; + + if (!compareF(PointF(-50.1, 100.1), PointF(-50.1, 100.1), + static_cast(&PointF::operator-), "-", + PointF(0, 0))) + return TestFail; + + if (!compareF(PointF(50.1, -100.1), PointF(50.1, -100.1), + static_cast(&PointF::operator-), "-", + PointF(0, 0))) + return TestFail; + + if (!compareF(PointF(-50.1, -100.1), PointF(-50.1, -100.1), + static_cast(&PointF::operator-), "-", + PointF(0, 0))) + return TestFail; + + if (!compareF(PointF(50.1, 100.1), PointF(-50.1, 100.1), + static_cast(&PointF::operator-), "-", + PointF(100.2, 0))) + return TestFail; + + if (!compareF(PointF(50.1, 100.1), PointF(50.1, -100.1), + static_cast(&PointF::operator-), "-", + PointF(0, 200.2))) + return TestFail; + + if (!compareF(PointF(50.1, 100.1), PointF(-50.1, -100.1), + static_cast(&PointF::operator-), "-", + PointF(100.2, 200.2))) + return TestFail; + + if (!compareF(PointF(-50.1, 100.1), PointF(50.1, 100.1), + static_cast(&PointF::operator-), "-", + PointF(-100.2, 0))) + return TestFail; + + /* Addition */ + if (!compareF(PointF(50.1, 100.1), PointF(50.1, 100.1), + static_cast(&PointF::operator+), "+", + PointF(100.2, 200.2))) + return TestFail; + + if (!compareF(PointF(-50.1, 100.1), PointF(-50.1, 100.1), + static_cast(&PointF::operator+), "+", + PointF(-100.2, 200.2))) + return TestFail; + + if (!compareF(PointF(50.1, -100.1), PointF(50.1, -100.1), + static_cast(&PointF::operator+), "+", + PointF(100.2, -200.2))) + return TestFail; + + if (!compareF(PointF(-50.1, -100), PointF(-50.1, -100.1), + static_cast(&PointF::operator+), "+", + PointF(-100.2, -200.1))) + return TestFail; + + if (!compareF(PointF(50.1, 100.1), PointF(-50.1, 100.1), + static_cast(&PointF::operator+), "+", + PointF(0, 200.2))) + return TestFail; + + if (!compareF(PointF(50.1, 100.0), PointF(50.1, -100.1), + static_cast(&PointF::operator+), "+", + PointF(100.2, -0.09999999999999432))) + return TestFail; + + if (!compareF(PointF(50.1, 100.1), PointF(-50.1, -100.1), + static_cast(&PointF::operator+), "+", + PointF(0, 0))) + return TestFail; + + if (!compareF(PointF(-50.1, 100.1), PointF(50.1, 100.1), + static_cast(&PointF::operator+), "+", + PointF(0, 200.2))) + return TestFail; + + /* Dot product */ + if (!compareF(PointF(50.1, 100.1), PointF(50.1, 100.1), + static_cast(&PointF::operator%), "%", + 12530.019999999999)) + return TestFail; + + if (!compareF(PointF(50.1, 100.1), PointF(50.1, -100.1), + static_cast(&PointF::operator%), "%", + -7509.999999999998)) + return TestFail; + + if (!compareF(PointF(50.1, 100.1), PointF(-50.1, 100.1), + static_cast(&PointF::operator%), "%", + 7509.999999999998)) + return TestFail; + + if (!compareF(PointF(50.1, 100.1), PointF(-50.1, -100.1), + static_cast(&PointF::operator%), "%", + -12530.019999999999)) + return TestFail; + + if (!compareF(PointF(-50.1, 100.1), PointF(-50.1, 100.1), + static_cast(&PointF::operator%), "%", + 12530.019999999999)) + return TestFail; + + if (!compareF(PointF(50.1, -100.1), PointF(50.1, -100.1), + static_cast(&PointF::operator%), "%", + 12530.019999999999)) + return TestFail; + + if (!compareF(PointF(-50.1, -100.1), PointF(-50.1, -100.1), + static_cast(&PointF::operator%), "%", + 12530.019999999999)) + return TestFail; + + if (!compareF(PointF(0, 0), PointF(0, 0), + static_cast(&PointF::operator%), "%", + 0)) + return TestFail; + + + /* Scaling up */ + if (!compareFScale(PointF(10.5, -100.1), 0, + static_cast(&PointF::operator*), "*", + PointF(0, 0))) + return TestFail; + + if (!compareFScale(PointF(10.5, -100.1), 1, + static_cast(&PointF::operator*), "*", + PointF(10.5, -100.1))) + return TestFail; + + if (!compareFScale(PointF(10.5, -100.1), 1.0, + static_cast(&PointF::operator*), "*", + PointF(10.5, -100.1))) + return TestFail; + + if (!compareFScale(PointF(10.5, -100.1), -4.2, + static_cast(&PointF::operator*), "*", + PointF(-44.1, 420.42))) + return TestFail; + + if (!compareFScale(PointF(10.5, -100.1), 4.2, + static_cast(&PointF::operator*), "*", + PointF(44.1, -420.42))) + return TestFail; + + if (!compareFScale(PointF(0, -100.1), 4.2, + static_cast(&PointF::operator*), "*", + PointF(0, -420.42))) + return TestFail; + + if (!compareFScale(PointF(-50.1, -100.1), 4.2, + static_cast(&PointF::operator*), "*", + PointF(-210.42000000000002, -420.42))) + return TestFail; + + /* Scaling down */ + if (!compareFScale(PointF(10.5, -100.1), 1, + static_cast(&PointF::operator/), "/", + PointF(10.5, -100.1))) + return TestFail; + + if (!compareFScale(PointF(10.5, -100.1), 1.0, + static_cast(&PointF::operator/), "/", + PointF(10.5, -100.1))) + return TestFail; + + if (!compareFScale(PointF(10.5, -100.1), -4.2, + static_cast(&PointF::operator/), "/", + PointF(-2.5, 23.833333333333332))) + return TestFail; + + if (!compareFScale(PointF(10.5, -100.1), 4.2, + static_cast(&PointF::operator/), "/", + PointF(2.5, -23.833333333333332))) + return TestFail; + + if (!compareFScale(PointF(0, -100.1), 4.2, + static_cast(&PointF::operator/), "/", + PointF(0, -23.833333333333332))) + return TestFail; + + if (!compareFScale(PointF(-50.1, -100.1), 4.2, + static_cast(&PointF::operator/), "/", + PointF(-11.928571428571429, -23.833333333333332))) + return TestFail; + + + /* Squared length */ + if (!compareFLen(PointF(0, 0), + static_cast(&PointF::len2), "len2", + 0)) + return TestFail; + + if (!compareFLen(PointF(10.4, 0), + static_cast(&PointF::len2), "len2", + 108.16000000000001)) + return TestFail; + + if (!compareFLen(PointF(10.4, 50.1), + static_cast(&PointF::len2), "len2", + 2618.17)) + return TestFail; + + if (!compareFLen(PointF(-10.4, 50.1), + static_cast(&PointF::len2), "len2", + 2618.17)) + return TestFail; + + if (!compareFLen(PointF(-10.4, -50.1), + static_cast(&PointF::len2), "len2", + 2618.17)) + return TestFail; + + /* Length */ + if (!compareFLen(PointF(0, 0), + static_cast(&PointF::len), "len", + 0)) + return TestFail; + + if (!compareFLen(PointF(10.4, 0), + static_cast(&PointF::len), "len", + 10.4)) + return TestFail; + + if (!compareFLen(PointF(10.4, 50.1), + static_cast(&PointF::len), "len", + 51.16805644149483)) + return TestFail; + + if (!compareFLen(PointF(-10.4, 50.1), + static_cast(&PointF::len), "len", + 51.16805644149483)) + return TestFail; + + if (!compareFLen(PointF(-10.4, -50.1), + static_cast(&PointF::len), "len", + 51.16805644149483)) + return TestFail; + + /* Default constructor */ + if (PointF() != PointF(0, 0)) { + cout << "Default constructor test failed" << endl; + return TestFail; + } + /* * Size tests */ From patchwork Fri May 31 14:42:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 20178 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id A9F94BDE6B for ; Fri, 31 May 2024 14:43:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 56889634B9; Fri, 31 May 2024 16:43:21 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="kVTFue6O"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CE4B6634AF for ; Fri, 31 May 2024 16:43:16 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (h175-177-049-156.catv02.itscom.jp [175.177.49.156]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7794829F; Fri, 31 May 2024 16:43:10 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1717166592; bh=1If8lJxm2MD9+2qBMimJJ3xZAdnvchA46OpoUN6v0lM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kVTFue6OSW82z1BjxyorpXrIsB4lwIzHQFSbmog0qPnT5R8mldevMkp9cq/GYV7d6 mz/Fkyj4NchvYxh/DVi1tuwxTeoI5Aw62Bi90sGdG6jdPt4n3I6hkX0jpZ5ydi3XXt 6yoHqUpFeRlSoQMzqAirevekDyw6Kals6vdIcFyY= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , Stefan Klug , David Plowman Subject: [PATCH v4 2/4] ipa: libipa: Copy pwl from rpi Date: Fri, 31 May 2024 23:42:59 +0900 Message-Id: <20240531144301.3950115-3-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240531144301.3950115-1-paul.elder@ideasonboard.com> References: <20240531144301.3950115-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Copy the piecewise linear function code from Raspberry Pi. Signed-off-by: Paul Elder Reviewed-by: Stefan Klug Acked-by: David Plowman Reviewed-by: Kieran Bingham --- Changes in v4: - update the copy No change in v3 No change in v2 --- src/ipa/libipa/meson.build | 2 + src/ipa/libipa/pwl.cpp | 269 +++++++++++++++++++++++++++++++++++++ src/ipa/libipa/pwl.h | 127 +++++++++++++++++ 3 files changed, 398 insertions(+) create mode 100644 src/ipa/libipa/pwl.cpp create mode 100644 src/ipa/libipa/pwl.h diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build index 7ce885da7..8ec9c7847 100644 --- a/src/ipa/libipa/meson.build +++ b/src/ipa/libipa/meson.build @@ -8,6 +8,7 @@ libipa_headers = files([ 'fc_queue.h', 'histogram.h', 'module.h', + 'pwl.h', ]) libipa_sources = files([ @@ -18,6 +19,7 @@ libipa_sources = files([ 'fc_queue.cpp', 'histogram.cpp', 'module.cpp', + 'pwl.cpp', ]) libipa_includes = include_directories('..') diff --git a/src/ipa/libipa/pwl.cpp b/src/ipa/libipa/pwl.cpp new file mode 100644 index 000000000..e39123767 --- /dev/null +++ b/src/ipa/libipa/pwl.cpp @@ -0,0 +1,269 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * piecewise linear functions + */ + +#include +#include +#include + +#include "pwl.h" + +using namespace RPiController; + +int Pwl::read(const libcamera::YamlObject ¶ms) +{ + if (!params.size() || params.size() % 2) + return -EINVAL; + + const auto &list = params.asList(); + + for (auto it = list.begin(); it != list.end(); it++) { + auto x = it->get(); + if (!x) + return -EINVAL; + if (it != list.begin() && *x <= points_.back().x) + return -EINVAL; + + auto y = (++it)->get(); + if (!y) + return -EINVAL; + + points_.push_back(Point(*x, *y)); + } + + return 0; +} + +void Pwl::append(double x, double y, const double eps) +{ + if (points_.empty() || points_.back().x + eps < x) + points_.push_back(Point(x, y)); +} + +void Pwl::prepend(double x, double y, const double eps) +{ + if (points_.empty() || points_.front().x - eps > x) + points_.insert(points_.begin(), Point(x, y)); +} + +Pwl::Interval Pwl::domain() const +{ + return Interval(points_[0].x, points_[points_.size() - 1].x); +} + +Pwl::Interval Pwl::range() const +{ + double lo = points_[0].y, hi = lo; + for (auto &p : points_) + lo = std::min(lo, p.y), hi = std::max(hi, p.y); + return Interval(lo, hi); +} + +bool Pwl::empty() const +{ + return points_.empty(); +} + +double Pwl::eval(double x, int *spanPtr, bool updateSpan) const +{ + int span = findSpan(x, spanPtr && *spanPtr != -1 ? *spanPtr : points_.size() / 2 - 1); + if (spanPtr && updateSpan) + *spanPtr = span; + return points_[span].y + + (x - points_[span].x) * (points_[span + 1].y - points_[span].y) / + (points_[span + 1].x - points_[span].x); +} + +int Pwl::findSpan(double x, int span) const +{ + /* + * Pwls are generally small, so linear search may well be faster than + * binary, though could review this if large PWls start turning up. + */ + int lastSpan = points_.size() - 2; + /* + * some algorithms may call us with span pointing directly at the last + * control point + */ + span = std::max(0, std::min(lastSpan, span)); + while (span < lastSpan && x >= points_[span + 1].x) + span++; + while (span && x < points_[span].x) + span--; + return span; +} + +Pwl::PerpType Pwl::invert(Point const &xy, Point &perp, int &span, + const double eps) const +{ + assert(span >= -1); + bool prevOffEnd = false; + for (span = span + 1; span < (int)points_.size() - 1; span++) { + Point spanVec = points_[span + 1] - points_[span]; + double t = ((xy - points_[span]) % spanVec) / spanVec.len2(); + if (t < -eps) /* off the start of this span */ + { + if (span == 0) { + perp = points_[span]; + return PerpType::Start; + } else if (prevOffEnd) { + perp = points_[span]; + return PerpType::Vertex; + } + } else if (t > 1 + eps) /* off the end of this span */ + { + if (span == (int)points_.size() - 2) { + perp = points_[span + 1]; + return PerpType::End; + } + prevOffEnd = true; + } else /* a true perpendicular */ + { + perp = points_[span] + spanVec * t; + return PerpType::Perpendicular; + } + } + return PerpType::None; +} + +Pwl Pwl::inverse(bool *trueInverse, const double eps) const +{ + bool appended = false, prepended = false, neither = false; + Pwl inverse; + + for (Point const &p : points_) { + if (inverse.empty()) + inverse.append(p.y, p.x, eps); + else if (std::abs(inverse.points_.back().x - p.y) <= eps || + std::abs(inverse.points_.front().x - p.y) <= eps) + /* do nothing */; + else if (p.y > inverse.points_.back().x) { + inverse.append(p.y, p.x, eps); + appended = true; + } else if (p.y < inverse.points_.front().x) { + inverse.prepend(p.y, p.x, eps); + prepended = true; + } else + neither = true; + } + + /* + * This is not a proper inverse if we found ourselves putting points + * onto both ends of the inverse, or if there were points that couldn't + * go on either. + */ + if (trueInverse) + *trueInverse = !(neither || (appended && prepended)); + + return inverse; +} + +Pwl Pwl::compose(Pwl const &other, const double eps) const +{ + double thisX = points_[0].x, thisY = points_[0].y; + int thisSpan = 0, otherSpan = other.findSpan(thisY, 0); + Pwl result({ { thisX, other.eval(thisY, &otherSpan, false) } }); + while (thisSpan != (int)points_.size() - 1) { + double dx = points_[thisSpan + 1].x - points_[thisSpan].x, + dy = points_[thisSpan + 1].y - points_[thisSpan].y; + if (std::abs(dy) > eps && + otherSpan + 1 < (int)other.points_.size() && + points_[thisSpan + 1].y >= + other.points_[otherSpan + 1].x + eps) { + /* + * next control point in result will be where this + * function's y reaches the next span in other + */ + thisX = points_[thisSpan].x + + (other.points_[otherSpan + 1].x - + points_[thisSpan].y) * + dx / dy; + thisY = other.points_[++otherSpan].x; + } else if (std::abs(dy) > eps && otherSpan > 0 && + points_[thisSpan + 1].y <= + other.points_[otherSpan - 1].x - eps) { + /* + * next control point in result will be where this + * function's y reaches the previous span in other + */ + thisX = points_[thisSpan].x + + (other.points_[otherSpan + 1].x - + points_[thisSpan].y) * + dx / dy; + thisY = other.points_[--otherSpan].x; + } else { + /* we stay in the same span in other */ + thisSpan++; + thisX = points_[thisSpan].x, + thisY = points_[thisSpan].y; + } + result.append(thisX, other.eval(thisY, &otherSpan, false), + eps); + } + return result; +} + +void Pwl::map(std::function f) const +{ + for (auto &pt : points_) + f(pt.x, pt.y); +} + +void Pwl::map2(Pwl const &pwl0, Pwl const &pwl1, + std::function f) +{ + int span0 = 0, span1 = 0; + double x = std::min(pwl0.points_[0].x, pwl1.points_[0].x); + f(x, pwl0.eval(x, &span0, false), pwl1.eval(x, &span1, false)); + while (span0 < (int)pwl0.points_.size() - 1 || + span1 < (int)pwl1.points_.size() - 1) { + if (span0 == (int)pwl0.points_.size() - 1) + x = pwl1.points_[++span1].x; + else if (span1 == (int)pwl1.points_.size() - 1) + x = pwl0.points_[++span0].x; + else if (pwl0.points_[span0 + 1].x > pwl1.points_[span1 + 1].x) + x = pwl1.points_[++span1].x; + else + x = pwl0.points_[++span0].x; + f(x, pwl0.eval(x, &span0, false), pwl1.eval(x, &span1, false)); + } +} + +Pwl Pwl::combine(Pwl const &pwl0, Pwl const &pwl1, + std::function f, + const double eps) +{ + Pwl result; + map2(pwl0, pwl1, [&](double x, double y0, double y1) { + result.append(x, f(x, y0, y1), eps); + }); + return result; +} + +void Pwl::matchDomain(Interval const &domain, bool clip, const double eps) +{ + int span = 0; + prepend(domain.start, eval(clip ? points_[0].x : domain.start, &span), + eps); + span = points_.size() - 2; + append(domain.end, eval(clip ? points_.back().x : domain.end, &span), + eps); +} + +Pwl &Pwl::operator*=(double d) +{ + for (auto &pt : points_) + pt.y *= d; + return *this; +} + +void Pwl::debug(FILE *fp) const +{ + fprintf(fp, "Pwl {\n"); + for (auto &p : points_) + fprintf(fp, "\t(%g, %g)\n", p.x, p.y); + fprintf(fp, "}\n"); +} diff --git a/src/ipa/libipa/pwl.h b/src/ipa/libipa/pwl.h new file mode 100644 index 000000000..7d5e7e4d3 --- /dev/null +++ b/src/ipa/libipa/pwl.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * piecewise linear functions interface + */ +#pragma once + +#include +#include +#include + +#include "libcamera/internal/yaml_parser.h" + +namespace RPiController { + +class Pwl +{ +public: + struct Interval { + Interval(double _start, double _end) + : start(_start), end(_end) + { + } + double start, end; + bool contains(double value) + { + return value >= start && value <= end; + } + double clip(double value) + { + return value < start ? start + : (value > end ? end : value); + } + double len() const { return end - start; } + }; + struct Point { + Point() : x(0), y(0) {} + Point(double _x, double _y) + : x(_x), y(_y) {} + double x, y; + Point operator-(Point const &p) const + { + return Point(x - p.x, y - p.y); + } + Point operator+(Point const &p) const + { + return Point(x + p.x, y + p.y); + } + double operator%(Point const &p) const + { + return x * p.x + y * p.y; + } + Point operator*(double f) const { return Point(x * f, y * f); } + Point operator/(double f) const { return Point(x / f, y / f); } + double len2() const { return x * x + y * y; } + double len() const { return sqrt(len2()); } + }; + Pwl() {} + Pwl(std::vector const &points) : points_(points) {} + int read(const libcamera::YamlObject ¶ms); + void append(double x, double y, const double eps = 1e-6); + void prepend(double x, double y, const double eps = 1e-6); + Interval domain() const; + Interval range() const; + bool empty() const; + /* + * Evaluate Pwl, optionally supplying an initial guess for the + * "span". The "span" may be optionally be updated. If you want to know + * the "span" value but don't have an initial guess you can set it to + * -1. + */ + double eval(double x, int *spanPtr = nullptr, + bool updateSpan = true) const; + /* + * Find perpendicular closest to xy, starting from span+1 so you can + * call it repeatedly to check for multiple closest points (set span to + * -1 on the first call). Also returns "pseudo" perpendiculars; see + * PerpType enum. + */ + enum class PerpType { + None, /* no perpendicular found */ + Start, /* start of Pwl is closest point */ + End, /* end of Pwl is closest point */ + Vertex, /* vertex of Pwl is closest point */ + Perpendicular /* true perpendicular found */ + }; + PerpType invert(Point const &xy, Point &perp, int &span, + const double eps = 1e-6) const; + /* + * Compute the inverse function. Indicate if it is a proper (true) + * inverse, or only a best effort (e.g. input was non-monotonic). + */ + Pwl inverse(bool *trueInverse = nullptr, const double eps = 1e-6) const; + /* Compose two Pwls together, doing "this" first and "other" after. */ + Pwl compose(Pwl const &other, const double eps = 1e-6) const; + /* Apply function to (x,y) values at every control point. */ + void map(std::function f) const; + /* + * Apply function to (x, y0, y1) values wherever either Pwl has a + * control point. + */ + static void map2(Pwl const &pwl0, Pwl const &pwl1, + std::function f); + /* + * Combine two Pwls, meaning we create a new Pwl where the y values are + * given by running f wherever either has a knot. + */ + static Pwl + combine(Pwl const &pwl0, Pwl const &pwl1, + std::function f, + const double eps = 1e-6); + /* + * Make "this" match (at least) the given domain. Any extension my be + * clipped or linear. + */ + void matchDomain(Interval const &domain, bool clip = true, + const double eps = 1e-6); + Pwl &operator*=(double d); + void debug(FILE *fp = stdout) const; + +private: + int findSpan(double x, int span) const; + std::vector points_; +}; + +} /* namespace RPiController */ From patchwork Fri May 31 14:43:00 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 20179 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id EEB93BDE6B for ; Fri, 31 May 2024 14:43:23 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5828D634BE; Fri, 31 May 2024 16:43:22 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="hmDscTzy"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3DD59634AF for ; Fri, 31 May 2024 16:43:19 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (h175-177-049-156.catv02.itscom.jp [175.177.49.156]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 9025CB53; Fri, 31 May 2024 16:43:12 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1717166594; bh=zBWWx+Dv6fHsZGLWq0ie4leJFMWUexmpc/2qBkLUKmc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hmDscTzygivHABrevvKDpdqnDbWnb745W6XN4tAI4pP/BEfeokdWeRD71iNWCKYPx L8MG0olChf8xp1Er8R2b3s6pPSQQcMG6F6yrWPG9aqHGSTAZOGBq4gIOaoLPjcGjN7 7BgKEsuE0Nn69cMFOK2EsiiSzJAm1sz05WwTPe+o= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , Stefan Klug , David Plowman Subject: [PATCH v4 3/4] ipa: libipa: pwl: Clean up Pwl class to match libcamera Date: Fri, 31 May 2024 23:43:00 +0900 Message-Id: <20240531144301.3950115-4-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240531144301.3950115-1-paul.elder@ideasonboard.com> References: <20240531144301.3950115-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Clean up the Pwl class copied from the Raspberry Pi IPA to align it more with the libcamera style. Signed-off-by: Paul Elder Reviewed-by: Stefan Klug Acked-by: David Plowman --- Changes in v4: - update to apply to new copy of pwl - add documentation - fix doxygen No change in v3 Changes in v2: - s/FPoint/PointF/g - improve documentation - s/matchDomain/extendDomain/ --- src/ipa/libipa/pwl.cpp | 268 ++++++++++++++++++++++++++++++++++++----- src/ipa/libipa/pwl.h | 119 +++++++----------- 2 files changed, 285 insertions(+), 102 deletions(-) diff --git a/src/ipa/libipa/pwl.cpp b/src/ipa/libipa/pwl.cpp index e39123767..945914347 100644 --- a/src/ipa/libipa/pwl.cpp +++ b/src/ipa/libipa/pwl.cpp @@ -1,19 +1,113 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (C) 2019, Raspberry Pi Ltd + * Copyright (C) 2024, Ideas on Board Oy * - * piecewise linear functions + * Piecewise linear functions */ +#include "pwl.h" + #include #include +#include #include -#include "pwl.h" +#include + +/** + * \file pwl.h + * \brief Piecewise linear functions + */ + +namespace libcamera { + +namespace ipa { + +/** + * \class Pwl + * \brief Describe a univariate piecewise linear function in real space + */ -using namespace RPiController; +/** + * \class Pwl::Interval + * \brief Describe an interval in one-dimensional real space + */ + +/** + * \fn Pwl::Interval::Interval(double _start, double _end) + * \brief Construct an interval + * \param _start Start of the interval + * \param _end End of the interval + */ + +/** + * \fn Pwl::Interval::contains + * \brief Check if a given values falls within the interval + * \param value Value to check + */ + +/** + * \fn Pwl::Interval::clamp + * \brief Clamp a value such that it is within the interval + * \param value Value to clamp + */ + +/** + * \fn Pwl::Interval::len + * \brief Compute the length of the interval + */ -int Pwl::read(const libcamera::YamlObject ¶ms) +/** + * \var Pwl::Interval::start + * \brief Start of the interval + */ + +/** + * \var Pwl::Interval::end + * \brief End of the interval + */ + +/** + * \enum Pwl::PerpType + * \brief Type of perpendicular found when inverting a piecewise linear function + * + * \var Pwl::PerpType::None + * \brief No perpendicular found + * + * \var Pwl::PerpType::Start + * \brief Start of Pwl is closest point + * + * \var Pwl::PerpType::End + * \brief End of Pwl is closest point + * + * \var Pwl::PerpType::Vertex + * \brief Vertex of Pwl is closest point + * + * \var Pwl::PerpType::Perpendicular + * \brief True perpendicular found + */ + +/** + * \fn Pwl::Pwl(std::vector const &points) + * \brief Construct a piecewise linear function from a list of 2D points + * \param points Vector of points from which to construct the piecewise linear function + */ + +/** + * \brief Populate the piecewise linear function from yaml data + * \param params Yaml data to populate the piecewise linear function with + * + * Any existing points in the piecewise linear function will *not* be + * overwritten. + * + * The yaml data is expected to be a list with an even number of numerical + * elements. These will be parsed in pairs into x and y points in the piecewise + * linear function. x must be monotonically increasing. + * + * \return 0 on success, negative error code otherwise + */ +int Pwl::readYaml(const libcamera::YamlObject ¶ms) { if (!params.size() || params.size() % 2) return -EINVAL; @@ -31,29 +125,55 @@ int Pwl::read(const libcamera::YamlObject ¶ms) if (!y) return -EINVAL; - points_.push_back(Point(*x, *y)); + points_.push_back(PointF(*x, *y)); } return 0; } +/** + * \brief Append a point to the end of the piecewise linear function + * \param x x-coordinate of the point to add to the piecewise linear function + * \param y y-coordinate of the point to add to the piecewise linear function + * \param eps Epsilon for the minimum x distance between points (optional) + * + * The point's x-coordinate must be greater than the x-coordinate of the last + * (= greatest) point already in the piecewise linear function. + */ void Pwl::append(double x, double y, const double eps) { if (points_.empty() || points_.back().x + eps < x) - points_.push_back(Point(x, y)); + points_.push_back(PointF(x, y)); } +/** + * \brief Prepend a point to the beginning of the piecewise linear function + * \param x x-coordinate of the point to add to the piecewise linear function + * \param y y-coordinate of the point to add to the piecewise linear function + * \param eps Epsilon for the minimum x distance between points (optional) + * + * The point's x-coordinate must be less than the x-coordinate of the first + * (= smallest) point already in the piecewise linear function. + */ void Pwl::prepend(double x, double y, const double eps) { if (points_.empty() || points_.front().x - eps > x) - points_.insert(points_.begin(), Point(x, y)); + points_.insert(points_.begin(), PointF(x, y)); } +/** + * \brief Get the domain of the piecewise linear function + * \return An interval representing the domain + */ Pwl::Interval Pwl::domain() const { return Interval(points_[0].x, points_[points_.size() - 1].x); } +/** + * \brief Get the range of the piecewise linear function + * \return An interval representing the range + */ Pwl::Interval Pwl::range() const { double lo = points_[0].y, hi = lo; @@ -62,11 +182,28 @@ Pwl::Interval Pwl::range() const return Interval(lo, hi); } +/** + * \brief Check if the piecewise linear function is empty + * \return True if there are no points in the function, false otherwise + */ bool Pwl::empty() const { return points_.empty(); } +/** + * \brief Evaluate the piecewise linear function + * \param[in] x The x value to input into the function + * \param[inout] spanPtr Initial guess for span + * \param[in] updateSpan Set to true to update spanPtr + * + * Evaluate Pwl, optionally supplying an initial guess for the + * "span". The "span" may be optionally be updated. If you want to know + * the "span" value but don't have an initial guess you can set it to + * -1. + * + * \return The result of evaluating the piecewise linear function at position \a x + */ double Pwl::eval(double x, int *spanPtr, bool updateSpan) const { int span = findSpan(x, spanPtr && *spanPtr != -1 ? *spanPtr : points_.size() / 2 - 1); @@ -96,16 +233,29 @@ int Pwl::findSpan(double x, int span) const return span; } -Pwl::PerpType Pwl::invert(Point const &xy, Point &perp, int &span, +/** + * \brief Find perpendicular closest to a given point + * \param[in] xy Point to find the perpendicular to + * \param[out] perp The found perpendicular + * \param[inout] span The span+1 to start searching from + * \param[in] eps Epsilon for the minimum x distance between points (optional) + * + * Find perpendicular closest to \a xy, starting from \a span+1 so you can call + * it repeatedly to check for multiple closest points (set span to -1 on the + * first call). Also returns "pseudo" perpendiculars; see PerpType enum. + * + * \return Type of perpendicular found + */ +Pwl::PerpType Pwl::invert(PointF const &xy, PointF &perp, int &span, const double eps) const { assert(span >= -1); bool prevOffEnd = false; for (span = span + 1; span < (int)points_.size() - 1; span++) { - Point spanVec = points_[span + 1] - points_[span]; + PointF spanVec = points_[span + 1] - points_[span]; double t = ((xy - points_[span]) % spanVec) / spanVec.len2(); - if (t < -eps) /* off the start of this span */ - { + if (t < -eps) { + /* off the start of this span */ if (span == 0) { perp = points_[span]; return PerpType::Start; @@ -113,15 +263,15 @@ Pwl::PerpType Pwl::invert(Point const &xy, Point &perp, int &span, perp = points_[span]; return PerpType::Vertex; } - } else if (t > 1 + eps) /* off the end of this span */ - { + } else if (t > 1 + eps) { + /* off the end of this span */ if (span == (int)points_.size() - 2) { perp = points_[span + 1]; return PerpType::End; } prevOffEnd = true; - } else /* a true perpendicular */ - { + } else { + /* a true perpendicular */ perp = points_[span] + spanVec * t; return PerpType::Perpendicular; } @@ -129,25 +279,36 @@ Pwl::PerpType Pwl::invert(Point const &xy, Point &perp, int &span, return PerpType::None; } +/** + * \brief Compute the inverse function + * \param[out] trueInverse True if the result is a proper/true inverse + * \param[in] eps Epsilon for the minimum x distance between points (optional) + * + * Indicate if it is a proper (true) inverse, or only a best effort (e.g. + * input was non-monotonic). + * + * \return The inverse piecewise linear function + */ Pwl Pwl::inverse(bool *trueInverse, const double eps) const { bool appended = false, prepended = false, neither = false; Pwl inverse; - for (Point const &p : points_) { - if (inverse.empty()) + for (PointF const &p : points_) { + if (inverse.empty()) { inverse.append(p.y, p.x, eps); - else if (std::abs(inverse.points_.back().x - p.y) <= eps || - std::abs(inverse.points_.front().x - p.y) <= eps) + } else if (std::abs(inverse.points_.back().x - p.y) <= eps || + std::abs(inverse.points_.front().x - p.y) <= eps) { /* do nothing */; - else if (p.y > inverse.points_.back().x) { + } else if (p.y > inverse.points_.back().x) { inverse.append(p.y, p.x, eps); appended = true; } else if (p.y < inverse.points_.front().x) { inverse.prepend(p.y, p.x, eps); prepended = true; - } else + } else { neither = true; + } } /* @@ -161,18 +322,27 @@ Pwl Pwl::inverse(bool *trueInverse, const double eps) const return inverse; } +/** + * \brief Compose two piecewise linear functions together + * \param[in] other The "other" piecewise linear function + * \param[in] eps Epsilon for the minimum x distance between points (optional) + * + * The "this" function is done first, and "other" after. + * + * \return The composed piecewise linear function + */ Pwl Pwl::compose(Pwl const &other, const double eps) const { double thisX = points_[0].x, thisY = points_[0].y; int thisSpan = 0, otherSpan = other.findSpan(thisY, 0); Pwl result({ { thisX, other.eval(thisY, &otherSpan, false) } }); + while (thisSpan != (int)points_.size() - 1) { double dx = points_[thisSpan + 1].x - points_[thisSpan].x, dy = points_[thisSpan + 1].y - points_[thisSpan].y; if (std::abs(dy) > eps && otherSpan + 1 < (int)other.points_.size() && - points_[thisSpan + 1].y >= - other.points_[otherSpan + 1].x + eps) { + points_[thisSpan + 1].y >= other.points_[otherSpan + 1].x + eps) { /* * next control point in result will be where this * function's y reaches the next span in other @@ -206,18 +376,27 @@ Pwl Pwl::compose(Pwl const &other, const double eps) const return result; } +/** + * \brief Apply function to (x,y) values at every control point + * \param f Function to be applied + */ void Pwl::map(std::function f) const { for (auto &pt : points_) f(pt.x, pt.y); } +/** + * \brief Apply function to (x, y0, y1) values wherever either Pwl has a + * control point. + */ void Pwl::map2(Pwl const &pwl0, Pwl const &pwl1, std::function f) { int span0 = 0, span1 = 0; double x = std::min(pwl0.points_[0].x, pwl1.points_[0].x); f(x, pwl0.eval(x, &span0, false), pwl1.eval(x, &span1, false)); + while (span0 < (int)pwl0.points_.size() - 1 || span1 < (int)pwl1.points_.size() - 1) { if (span0 == (int)pwl0.points_.size() - 1) @@ -232,6 +411,12 @@ void Pwl::map2(Pwl const &pwl0, Pwl const &pwl1, } } +/** + * \brief Combine two Pwls + * + * Create a new Pwl where the y values are given by running f wherever either + * has a knot. + */ Pwl Pwl::combine(Pwl const &pwl0, Pwl const &pwl1, std::function f, const double eps) @@ -243,7 +428,19 @@ Pwl Pwl::combine(Pwl const &pwl0, Pwl const &pwl1, return result; } -void Pwl::matchDomain(Interval const &domain, bool clip, const double eps) +/** + * \brief Extend the domain of the piecewise linear function + * \param[in] domain The domain to extend to + * \param[in] clip True to keep the existing edge y values, false to extrapolate + * \param[in] eps Epsilon for the minimum x distance between points (optional) + * + * Extend the domain of the piecewise linear function to match \a domain. If \a + * clip is set to true then the y values of the new edges will be the same as + * the existing y values of the edge points of the pwl. If false, then the y + * values will be extrapolated linearly from the existing edge points of the + * pwl. + */ +void Pwl::extendDomain(Interval const &domain, bool clip, const double eps) { int span = 0; prepend(domain.start, eval(clip ? points_[0].x : domain.start, &span), @@ -253,6 +450,11 @@ void Pwl::matchDomain(Interval const &domain, bool clip, const double eps) eps); } +/** + * \brief Multiply the piecewise linear function + * \param d Scalar multiplier to multiply the function by + * \return This function, after it has been multiplied by \a d + */ Pwl &Pwl::operator*=(double d) { for (auto &pt : points_) @@ -260,10 +462,20 @@ Pwl &Pwl::operator*=(double d) return *this; } -void Pwl::debug(FILE *fp) const +/** + * \brief Assemble and return a string describing the piecewise linear function + * \return A string describing the piecewise linear function + */ +std::string Pwl::toString() const { - fprintf(fp, "Pwl {\n"); + std::stringstream ss; + ss << "Pwl { "; for (auto &p : points_) - fprintf(fp, "\t(%g, %g)\n", p.x, p.y); - fprintf(fp, "}\n"); + ss << "(" << p.x << ", " << p.y << ") "; + ss << "}"; + return ss.str(); } + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/pwl.h b/src/ipa/libipa/pwl.h index 7d5e7e4d3..9b716c788 100644 --- a/src/ipa/libipa/pwl.h +++ b/src/ipa/libipa/pwl.h @@ -2,126 +2,97 @@ /* * Copyright (C) 2019, Raspberry Pi Ltd * - * piecewise linear functions interface + * Piecewise linear functions interface */ #pragma once #include #include +#include #include +#include + #include "libcamera/internal/yaml_parser.h" -namespace RPiController { +namespace libcamera { + +namespace ipa { class Pwl { public: + enum class PerpType { + None, + Start, + End, + Vertex, + Perpendicular, + }; + struct Interval { Interval(double _start, double _end) - : start(_start), end(_end) - { - } - double start, end; + : start(_start), end(_end) {} + bool contains(double value) { return value >= start && value <= end; } - double clip(double value) + + double clamp(double value) { return value < start ? start : (value > end ? end : value); } + double len() const { return end - start; } + + double start, end; }; - struct Point { - Point() : x(0), y(0) {} - Point(double _x, double _y) - : x(_x), y(_y) {} - double x, y; - Point operator-(Point const &p) const - { - return Point(x - p.x, y - p.y); - } - Point operator+(Point const &p) const - { - return Point(x + p.x, y + p.y); - } - double operator%(Point const &p) const - { - return x * p.x + y * p.y; - } - Point operator*(double f) const { return Point(x * f, y * f); } - Point operator/(double f) const { return Point(x / f, y / f); } - double len2() const { return x * x + y * y; } - double len() const { return sqrt(len2()); } - }; + Pwl() {} - Pwl(std::vector const &points) : points_(points) {} - int read(const libcamera::YamlObject ¶ms); + Pwl(std::vector const &points) + : points_(points) {} + int readYaml(const libcamera::YamlObject ¶ms); + void append(double x, double y, const double eps = 1e-6); void prepend(double x, double y, const double eps = 1e-6); + Interval domain() const; Interval range() const; + bool empty() const; - /* - * Evaluate Pwl, optionally supplying an initial guess for the - * "span". The "span" may be optionally be updated. If you want to know - * the "span" value but don't have an initial guess you can set it to - * -1. - */ + double eval(double x, int *spanPtr = nullptr, bool updateSpan = true) const; - /* - * Find perpendicular closest to xy, starting from span+1 so you can - * call it repeatedly to check for multiple closest points (set span to - * -1 on the first call). Also returns "pseudo" perpendiculars; see - * PerpType enum. - */ - enum class PerpType { - None, /* no perpendicular found */ - Start, /* start of Pwl is closest point */ - End, /* end of Pwl is closest point */ - Vertex, /* vertex of Pwl is closest point */ - Perpendicular /* true perpendicular found */ - }; - PerpType invert(Point const &xy, Point &perp, int &span, + + PerpType invert(PointF const &xy, PointF &perp, int &span, const double eps = 1e-6) const; - /* - * Compute the inverse function. Indicate if it is a proper (true) - * inverse, or only a best effort (e.g. input was non-monotonic). - */ Pwl inverse(bool *trueInverse = nullptr, const double eps = 1e-6) const; - /* Compose two Pwls together, doing "this" first and "other" after. */ Pwl compose(Pwl const &other, const double eps = 1e-6) const; - /* Apply function to (x,y) values at every control point. */ + void map(std::function f) const; - /* - * Apply function to (x, y0, y1) values wherever either Pwl has a - * control point. - */ + static void map2(Pwl const &pwl0, Pwl const &pwl1, std::function f); - /* - * Combine two Pwls, meaning we create a new Pwl where the y values are - * given by running f wherever either has a knot. - */ + static Pwl combine(Pwl const &pwl0, Pwl const &pwl1, std::function f, const double eps = 1e-6); - /* - * Make "this" match (at least) the given domain. Any extension my be - * clipped or linear. - */ - void matchDomain(Interval const &domain, bool clip = true, - const double eps = 1e-6); + + void extendDomain(Interval const &domain, bool clip = true, + const double eps = 1e-6); + Pwl &operator*=(double d); - void debug(FILE *fp = stdout) const; + + std::string toString() const; private: int findSpan(double x, int span) const; - std::vector points_; + std::vector points_; }; -} /* namespace RPiController */ +} /* namespace ipa */ + +} /* namespace libcamera */ From patchwork Fri May 31 14:43:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 20180 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id D1749BDE6B for ; Fri, 31 May 2024 14:43:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6414C634BB; Fri, 31 May 2024 16:43:27 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="KL0ipYE0"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5C412634BF for ; Fri, 31 May 2024 16:43:21 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (h175-177-049-156.catv02.itscom.jp [175.177.49.156]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A8F4629F; Fri, 31 May 2024 16:43:14 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1717166596; bh=kN0fMTg9Pgvk2rbdpRuzQ6VhSoZOrNBCEQfjun3CtY0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=KL0ipYE0nJcVIxNxQQx/EF2i6FYNZCjBUnEZY73QCERtP/eBc2IyxelRT8lbfRJzf DXvsl1dLQGn0TUDPWeudSXcR6qsWoHk//QXQB+cROq9hcH1eoRaYv3uKkdRW5a9pqT HhA0+Xfw5/3u5SbanpO+MZFgD/neit7I8IUJ+ANs= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , Stefan Klug , David Plowman Subject: [PATCH v4 4/4] ipa: rpi: controller: Use libipa's Pwl class Date: Fri, 31 May 2024 23:43:01 +0900 Message-Id: <20240531144301.3950115-5-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240531144301.3950115-1-paul.elder@ideasonboard.com> References: <20240531144301.3950115-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" To reduce code duplication, use the Pwl class from libipa. This also removes the Pwl class from the Raspberry Pi IPA. Signed-off-by: Paul Elder Reviewed-by: Stefan Klug Acked-by: David Plowman Reviewed-by: Kieran Bingham --- No change in v4 No change in v3 Changes in v2: - s/FPoint/PointF/g --- src/ipa/rpi/controller/cac_status.h | 2 - src/ipa/rpi/controller/contrast_status.h | 4 +- src/ipa/rpi/controller/meson.build | 2 +- src/ipa/rpi/controller/pwl.cpp | 269 --------------------- src/ipa/rpi/controller/pwl.h | 127 ---------- src/ipa/rpi/controller/rpi/af.cpp | 4 +- src/ipa/rpi/controller/rpi/af.h | 5 +- src/ipa/rpi/controller/rpi/agc_channel.cpp | 8 +- src/ipa/rpi/controller/rpi/agc_channel.h | 7 +- src/ipa/rpi/controller/rpi/awb.cpp | 40 +-- src/ipa/rpi/controller/rpi/awb.h | 23 +- src/ipa/rpi/controller/rpi/ccm.cpp | 4 +- src/ipa/rpi/controller/rpi/ccm.h | 5 +- src/ipa/rpi/controller/rpi/contrast.cpp | 14 +- src/ipa/rpi/controller/rpi/contrast.h | 5 +- src/ipa/rpi/controller/rpi/geq.cpp | 5 +- src/ipa/rpi/controller/rpi/geq.h | 4 +- src/ipa/rpi/controller/rpi/hdr.cpp | 8 +- src/ipa/rpi/controller/rpi/hdr.h | 9 +- src/ipa/rpi/controller/rpi/tonemap.cpp | 2 +- src/ipa/rpi/controller/rpi/tonemap.h | 5 +- src/ipa/rpi/controller/tonemap_status.h | 4 +- 22 files changed, 84 insertions(+), 472 deletions(-) delete mode 100644 src/ipa/rpi/controller/pwl.cpp delete mode 100644 src/ipa/rpi/controller/pwl.h diff --git a/src/ipa/rpi/controller/cac_status.h b/src/ipa/rpi/controller/cac_status.h index 475d4c5cc..adffce411 100644 --- a/src/ipa/rpi/controller/cac_status.h +++ b/src/ipa/rpi/controller/cac_status.h @@ -6,8 +6,6 @@ */ #pragma once -#include "pwl.h" - struct CacStatus { std::vector lutRx; std::vector lutRy; diff --git a/src/ipa/rpi/controller/contrast_status.h b/src/ipa/rpi/controller/contrast_status.h index 7c67f0547..1f175872a 100644 --- a/src/ipa/rpi/controller/contrast_status.h +++ b/src/ipa/rpi/controller/contrast_status.h @@ -6,7 +6,7 @@ */ #pragma once -#include "pwl.h" +#include "libipa/pwl.h" /* * The "contrast" algorithm creates a gamma curve, optionally doing a little bit @@ -14,7 +14,7 @@ */ struct ContrastStatus { - RPiController::Pwl gammaCurve; + libcamera::ipa::Pwl gammaCurve; double brightness; double contrast; }; diff --git a/src/ipa/rpi/controller/meson.build b/src/ipa/rpi/controller/meson.build index 32a4d31cf..74b74888b 100644 --- a/src/ipa/rpi/controller/meson.build +++ b/src/ipa/rpi/controller/meson.build @@ -5,7 +5,6 @@ rpi_ipa_controller_sources = files([ 'controller.cpp', 'device_status.cpp', 'histogram.cpp', - 'pwl.cpp', 'rpi/af.cpp', 'rpi/agc.cpp', 'rpi/agc_channel.cpp', @@ -32,4 +31,5 @@ rpi_ipa_controller_deps = [ ] rpi_ipa_controller_lib = static_library('rpi_ipa_controller', rpi_ipa_controller_sources, + include_directories : libipa_includes, dependencies : rpi_ipa_controller_deps) diff --git a/src/ipa/rpi/controller/pwl.cpp b/src/ipa/rpi/controller/pwl.cpp deleted file mode 100644 index e39123767..000000000 --- a/src/ipa/rpi/controller/pwl.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * piecewise linear functions - */ - -#include -#include -#include - -#include "pwl.h" - -using namespace RPiController; - -int Pwl::read(const libcamera::YamlObject ¶ms) -{ - if (!params.size() || params.size() % 2) - return -EINVAL; - - const auto &list = params.asList(); - - for (auto it = list.begin(); it != list.end(); it++) { - auto x = it->get(); - if (!x) - return -EINVAL; - if (it != list.begin() && *x <= points_.back().x) - return -EINVAL; - - auto y = (++it)->get(); - if (!y) - return -EINVAL; - - points_.push_back(Point(*x, *y)); - } - - return 0; -} - -void Pwl::append(double x, double y, const double eps) -{ - if (points_.empty() || points_.back().x + eps < x) - points_.push_back(Point(x, y)); -} - -void Pwl::prepend(double x, double y, const double eps) -{ - if (points_.empty() || points_.front().x - eps > x) - points_.insert(points_.begin(), Point(x, y)); -} - -Pwl::Interval Pwl::domain() const -{ - return Interval(points_[0].x, points_[points_.size() - 1].x); -} - -Pwl::Interval Pwl::range() const -{ - double lo = points_[0].y, hi = lo; - for (auto &p : points_) - lo = std::min(lo, p.y), hi = std::max(hi, p.y); - return Interval(lo, hi); -} - -bool Pwl::empty() const -{ - return points_.empty(); -} - -double Pwl::eval(double x, int *spanPtr, bool updateSpan) const -{ - int span = findSpan(x, spanPtr && *spanPtr != -1 ? *spanPtr : points_.size() / 2 - 1); - if (spanPtr && updateSpan) - *spanPtr = span; - return points_[span].y + - (x - points_[span].x) * (points_[span + 1].y - points_[span].y) / - (points_[span + 1].x - points_[span].x); -} - -int Pwl::findSpan(double x, int span) const -{ - /* - * Pwls are generally small, so linear search may well be faster than - * binary, though could review this if large PWls start turning up. - */ - int lastSpan = points_.size() - 2; - /* - * some algorithms may call us with span pointing directly at the last - * control point - */ - span = std::max(0, std::min(lastSpan, span)); - while (span < lastSpan && x >= points_[span + 1].x) - span++; - while (span && x < points_[span].x) - span--; - return span; -} - -Pwl::PerpType Pwl::invert(Point const &xy, Point &perp, int &span, - const double eps) const -{ - assert(span >= -1); - bool prevOffEnd = false; - for (span = span + 1; span < (int)points_.size() - 1; span++) { - Point spanVec = points_[span + 1] - points_[span]; - double t = ((xy - points_[span]) % spanVec) / spanVec.len2(); - if (t < -eps) /* off the start of this span */ - { - if (span == 0) { - perp = points_[span]; - return PerpType::Start; - } else if (prevOffEnd) { - perp = points_[span]; - return PerpType::Vertex; - } - } else if (t > 1 + eps) /* off the end of this span */ - { - if (span == (int)points_.size() - 2) { - perp = points_[span + 1]; - return PerpType::End; - } - prevOffEnd = true; - } else /* a true perpendicular */ - { - perp = points_[span] + spanVec * t; - return PerpType::Perpendicular; - } - } - return PerpType::None; -} - -Pwl Pwl::inverse(bool *trueInverse, const double eps) const -{ - bool appended = false, prepended = false, neither = false; - Pwl inverse; - - for (Point const &p : points_) { - if (inverse.empty()) - inverse.append(p.y, p.x, eps); - else if (std::abs(inverse.points_.back().x - p.y) <= eps || - std::abs(inverse.points_.front().x - p.y) <= eps) - /* do nothing */; - else if (p.y > inverse.points_.back().x) { - inverse.append(p.y, p.x, eps); - appended = true; - } else if (p.y < inverse.points_.front().x) { - inverse.prepend(p.y, p.x, eps); - prepended = true; - } else - neither = true; - } - - /* - * This is not a proper inverse if we found ourselves putting points - * onto both ends of the inverse, or if there were points that couldn't - * go on either. - */ - if (trueInverse) - *trueInverse = !(neither || (appended && prepended)); - - return inverse; -} - -Pwl Pwl::compose(Pwl const &other, const double eps) const -{ - double thisX = points_[0].x, thisY = points_[0].y; - int thisSpan = 0, otherSpan = other.findSpan(thisY, 0); - Pwl result({ { thisX, other.eval(thisY, &otherSpan, false) } }); - while (thisSpan != (int)points_.size() - 1) { - double dx = points_[thisSpan + 1].x - points_[thisSpan].x, - dy = points_[thisSpan + 1].y - points_[thisSpan].y; - if (std::abs(dy) > eps && - otherSpan + 1 < (int)other.points_.size() && - points_[thisSpan + 1].y >= - other.points_[otherSpan + 1].x + eps) { - /* - * next control point in result will be where this - * function's y reaches the next span in other - */ - thisX = points_[thisSpan].x + - (other.points_[otherSpan + 1].x - - points_[thisSpan].y) * - dx / dy; - thisY = other.points_[++otherSpan].x; - } else if (std::abs(dy) > eps && otherSpan > 0 && - points_[thisSpan + 1].y <= - other.points_[otherSpan - 1].x - eps) { - /* - * next control point in result will be where this - * function's y reaches the previous span in other - */ - thisX = points_[thisSpan].x + - (other.points_[otherSpan + 1].x - - points_[thisSpan].y) * - dx / dy; - thisY = other.points_[--otherSpan].x; - } else { - /* we stay in the same span in other */ - thisSpan++; - thisX = points_[thisSpan].x, - thisY = points_[thisSpan].y; - } - result.append(thisX, other.eval(thisY, &otherSpan, false), - eps); - } - return result; -} - -void Pwl::map(std::function f) const -{ - for (auto &pt : points_) - f(pt.x, pt.y); -} - -void Pwl::map2(Pwl const &pwl0, Pwl const &pwl1, - std::function f) -{ - int span0 = 0, span1 = 0; - double x = std::min(pwl0.points_[0].x, pwl1.points_[0].x); - f(x, pwl0.eval(x, &span0, false), pwl1.eval(x, &span1, false)); - while (span0 < (int)pwl0.points_.size() - 1 || - span1 < (int)pwl1.points_.size() - 1) { - if (span0 == (int)pwl0.points_.size() - 1) - x = pwl1.points_[++span1].x; - else if (span1 == (int)pwl1.points_.size() - 1) - x = pwl0.points_[++span0].x; - else if (pwl0.points_[span0 + 1].x > pwl1.points_[span1 + 1].x) - x = pwl1.points_[++span1].x; - else - x = pwl0.points_[++span0].x; - f(x, pwl0.eval(x, &span0, false), pwl1.eval(x, &span1, false)); - } -} - -Pwl Pwl::combine(Pwl const &pwl0, Pwl const &pwl1, - std::function f, - const double eps) -{ - Pwl result; - map2(pwl0, pwl1, [&](double x, double y0, double y1) { - result.append(x, f(x, y0, y1), eps); - }); - return result; -} - -void Pwl::matchDomain(Interval const &domain, bool clip, const double eps) -{ - int span = 0; - prepend(domain.start, eval(clip ? points_[0].x : domain.start, &span), - eps); - span = points_.size() - 2; - append(domain.end, eval(clip ? points_.back().x : domain.end, &span), - eps); -} - -Pwl &Pwl::operator*=(double d) -{ - for (auto &pt : points_) - pt.y *= d; - return *this; -} - -void Pwl::debug(FILE *fp) const -{ - fprintf(fp, "Pwl {\n"); - for (auto &p : points_) - fprintf(fp, "\t(%g, %g)\n", p.x, p.y); - fprintf(fp, "}\n"); -} diff --git a/src/ipa/rpi/controller/pwl.h b/src/ipa/rpi/controller/pwl.h deleted file mode 100644 index 7d5e7e4d3..000000000 --- a/src/ipa/rpi/controller/pwl.h +++ /dev/null @@ -1,127 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * piecewise linear functions interface - */ -#pragma once - -#include -#include -#include - -#include "libcamera/internal/yaml_parser.h" - -namespace RPiController { - -class Pwl -{ -public: - struct Interval { - Interval(double _start, double _end) - : start(_start), end(_end) - { - } - double start, end; - bool contains(double value) - { - return value >= start && value <= end; - } - double clip(double value) - { - return value < start ? start - : (value > end ? end : value); - } - double len() const { return end - start; } - }; - struct Point { - Point() : x(0), y(0) {} - Point(double _x, double _y) - : x(_x), y(_y) {} - double x, y; - Point operator-(Point const &p) const - { - return Point(x - p.x, y - p.y); - } - Point operator+(Point const &p) const - { - return Point(x + p.x, y + p.y); - } - double operator%(Point const &p) const - { - return x * p.x + y * p.y; - } - Point operator*(double f) const { return Point(x * f, y * f); } - Point operator/(double f) const { return Point(x / f, y / f); } - double len2() const { return x * x + y * y; } - double len() const { return sqrt(len2()); } - }; - Pwl() {} - Pwl(std::vector const &points) : points_(points) {} - int read(const libcamera::YamlObject ¶ms); - void append(double x, double y, const double eps = 1e-6); - void prepend(double x, double y, const double eps = 1e-6); - Interval domain() const; - Interval range() const; - bool empty() const; - /* - * Evaluate Pwl, optionally supplying an initial guess for the - * "span". The "span" may be optionally be updated. If you want to know - * the "span" value but don't have an initial guess you can set it to - * -1. - */ - double eval(double x, int *spanPtr = nullptr, - bool updateSpan = true) const; - /* - * Find perpendicular closest to xy, starting from span+1 so you can - * call it repeatedly to check for multiple closest points (set span to - * -1 on the first call). Also returns "pseudo" perpendiculars; see - * PerpType enum. - */ - enum class PerpType { - None, /* no perpendicular found */ - Start, /* start of Pwl is closest point */ - End, /* end of Pwl is closest point */ - Vertex, /* vertex of Pwl is closest point */ - Perpendicular /* true perpendicular found */ - }; - PerpType invert(Point const &xy, Point &perp, int &span, - const double eps = 1e-6) const; - /* - * Compute the inverse function. Indicate if it is a proper (true) - * inverse, or only a best effort (e.g. input was non-monotonic). - */ - Pwl inverse(bool *trueInverse = nullptr, const double eps = 1e-6) const; - /* Compose two Pwls together, doing "this" first and "other" after. */ - Pwl compose(Pwl const &other, const double eps = 1e-6) const; - /* Apply function to (x,y) values at every control point. */ - void map(std::function f) const; - /* - * Apply function to (x, y0, y1) values wherever either Pwl has a - * control point. - */ - static void map2(Pwl const &pwl0, Pwl const &pwl1, - std::function f); - /* - * Combine two Pwls, meaning we create a new Pwl where the y values are - * given by running f wherever either has a knot. - */ - static Pwl - combine(Pwl const &pwl0, Pwl const &pwl1, - std::function f, - const double eps = 1e-6); - /* - * Make "this" match (at least) the given domain. Any extension my be - * clipped or linear. - */ - void matchDomain(Interval const &domain, bool clip = true, - const double eps = 1e-6); - Pwl &operator*=(double d); - void debug(FILE *fp = stdout) const; - -private: - int findSpan(double x, int span) const; - std::vector points_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/rpi/af.cpp b/src/ipa/rpi/controller/rpi/af.cpp index c5fd84826..304629d6d 100644 --- a/src/ipa/rpi/controller/rpi/af.cpp +++ b/src/ipa/rpi/controller/rpi/af.cpp @@ -139,7 +139,7 @@ int Af::CfgParams::read(const libcamera::YamlObject ¶ms) readNumber(skipFrames, params, "skip_frames"); if (params.contains("map")) - map.read(params["map"]); + map.readYaml(params["map"]); else LOG(RPiAf, Warning) << "No map defined"; @@ -721,7 +721,7 @@ bool Af::setLensPosition(double dioptres, int *hwpos) if (mode_ == AfModeManual) { LOG(RPiAf, Debug) << "setLensPosition: " << dioptres; - ftarget_ = cfg_.map.domain().clip(dioptres); + ftarget_ = cfg_.map.domain().clamp(dioptres); changed = !(initted_ && fsmooth_ == ftarget_); updateLensPosition(); } diff --git a/src/ipa/rpi/controller/rpi/af.h b/src/ipa/rpi/controller/rpi/af.h index 2617e2ace..317a51f3e 100644 --- a/src/ipa/rpi/controller/rpi/af.h +++ b/src/ipa/rpi/controller/rpi/af.h @@ -9,7 +9,8 @@ #include "../af_algorithm.h" #include "../af_status.h" #include "../pdaf_data.h" -#include "../pwl.h" + +#include "libipa/pwl.h" /* * This algorithm implements a hybrid of CDAF and PDAF, favouring PDAF. @@ -100,7 +101,7 @@ private: uint32_t confThresh; /* PDAF confidence cell min (sensor-specific) */ uint32_t confClip; /* PDAF confidence cell max (sensor-specific) */ uint32_t skipFrames; /* frames to skip at start or modeswitch */ - Pwl map; /* converts dioptres -> lens driver position */ + libcamera::ipa::Pwl map; /* converts dioptres -> lens driver position */ CfgParams(); int read(const libcamera::YamlObject ¶ms); diff --git a/src/ipa/rpi/controller/rpi/agc_channel.cpp b/src/ipa/rpi/controller/rpi/agc_channel.cpp index a77ccec36..a381dd972 100644 --- a/src/ipa/rpi/controller/rpi/agc_channel.cpp +++ b/src/ipa/rpi/controller/rpi/agc_channel.cpp @@ -130,7 +130,7 @@ int AgcConstraint::read(const libcamera::YamlObject ¶ms) return -EINVAL; qHi = *value; - return yTarget.read(params["y_target"]); + return yTarget.readYaml(params["y_target"]); } static std::tuple @@ -237,7 +237,7 @@ int AgcConfig::read(const libcamera::YamlObject ¶ms) return ret; } - ret = yTarget.read(params["y_target"]); + ret = yTarget.readYaml(params["y_target"]); if (ret) return ret; @@ -715,7 +715,7 @@ static constexpr double EvGainYTargetLimit = 0.9; static double constraintComputeGain(AgcConstraint &c, const Histogram &h, double lux, double evGain, double &targetY) { - targetY = c.yTarget.eval(c.yTarget.domain().clip(lux)); + targetY = c.yTarget.eval(c.yTarget.domain().clamp(lux)); targetY = std::min(EvGainYTargetLimit, targetY * evGain); double iqm = h.interQuantileMean(c.qLo, c.qHi); return (targetY * h.bins()) / iqm; @@ -734,7 +734,7 @@ void AgcChannel::computeGain(StatisticsPtr &statistics, Metadata *imageMetadata, * The initial gain and target_Y come from some of the regions. After * that we consider the histogram constraints. */ - targetY = config_.yTarget.eval(config_.yTarget.domain().clip(lux.lux)); + targetY = config_.yTarget.eval(config_.yTarget.domain().clamp(lux.lux)); targetY = std::min(EvGainYTargetLimit, targetY * evGain); /* diff --git a/src/ipa/rpi/controller/rpi/agc_channel.h b/src/ipa/rpi/controller/rpi/agc_channel.h index 99033e23e..58368889e 100644 --- a/src/ipa/rpi/controller/rpi/agc_channel.h +++ b/src/ipa/rpi/controller/rpi/agc_channel.h @@ -12,10 +12,11 @@ #include +#include + #include "../agc_status.h" #include "../awb_status.h" #include "../controller.h" -#include "../pwl.h" /* This is our implementation of AGC. */ @@ -40,7 +41,7 @@ struct AgcConstraint { Bound bound; double qLo; double qHi; - Pwl yTarget; + libcamera::ipa::Pwl yTarget; int read(const libcamera::YamlObject ¶ms); }; @@ -61,7 +62,7 @@ struct AgcConfig { std::map exposureModes; std::map constraintModes; std::vector channelConstraints; - Pwl yTarget; + libcamera::ipa::Pwl yTarget; double speed; uint16_t startupFrames; unsigned int convergenceFrames; diff --git a/src/ipa/rpi/controller/rpi/awb.cpp b/src/ipa/rpi/controller/rpi/awb.cpp index abe5906e9..db9937cd4 100644 --- a/src/ipa/rpi/controller/rpi/awb.cpp +++ b/src/ipa/rpi/controller/rpi/awb.cpp @@ -49,10 +49,10 @@ int AwbPrior::read(const libcamera::YamlObject ¶ms) return -EINVAL; lux = *value; - return prior.read(params["prior"]); + return prior.readYaml(params["prior"]); } -static int readCtCurve(Pwl &ctR, Pwl &ctB, const libcamera::YamlObject ¶ms) +static int readCtCurve(ipa::Pwl &ctR, ipa::Pwl &ctB, const libcamera::YamlObject ¶ms) { if (params.size() % 3) { LOG(RPiAwb, Error) << "AwbConfig: incomplete CT curve entry"; @@ -207,7 +207,7 @@ void Awb::initialise() * them. */ if (!config_.ctR.empty() && !config_.ctB.empty()) { - syncResults_.temperatureK = config_.ctR.domain().clip(4000); + syncResults_.temperatureK = config_.ctR.domain().clamp(4000); syncResults_.gainR = 1.0 / config_.ctR.eval(syncResults_.temperatureK); syncResults_.gainG = 1.0; syncResults_.gainB = 1.0 / config_.ctB.eval(syncResults_.temperatureK); @@ -273,8 +273,8 @@ void Awb::setManualGains(double manualR, double manualB) syncResults_.gainB = prevSyncResults_.gainB = manualB_; if (config_.bayes) { /* Also estimate the best corresponding colour temperature from the curves. */ - double ctR = config_.ctRInverse.eval(config_.ctRInverse.domain().clip(1 / manualR_)); - double ctB = config_.ctBInverse.eval(config_.ctBInverse.domain().clip(1 / manualB_)); + double ctR = config_.ctRInverse.eval(config_.ctRInverse.domain().clamp(1 / manualR_)); + double ctB = config_.ctBInverse.eval(config_.ctBInverse.domain().clamp(1 / manualB_)); prevSyncResults_.temperatureK = (ctR + ctB) / 2; syncResults_.temperatureK = prevSyncResults_.temperatureK; } @@ -468,7 +468,7 @@ double Awb::computeDelta2Sum(double gainR, double gainB) return delta2Sum; } -Pwl Awb::interpolatePrior() +ipa::Pwl Awb::interpolatePrior() { /* * Interpolate the prior log likelihood function for our current lux @@ -485,7 +485,7 @@ Pwl Awb::interpolatePrior() idx++; double lux0 = config_.priors[idx].lux, lux1 = config_.priors[idx + 1].lux; - return Pwl::combine(config_.priors[idx].prior, + return ipa::Pwl::combine(config_.priors[idx].prior, config_.priors[idx + 1].prior, [&](double /*x*/, double y0, double y1) { return y0 + (y1 - y0) * @@ -494,15 +494,15 @@ Pwl Awb::interpolatePrior() } } -static double interpolateQuadatric(Pwl::Point const &a, Pwl::Point const &b, - Pwl::Point const &c) +static double interpolateQuadatric(PointF const &a, PointF const &b, + PointF const &c) { /* * Given 3 points on a curve, find the extremum of the function in that * interval by fitting a quadratic. */ const double eps = 1e-3; - Pwl::Point ca = c - a, ba = b - a; + PointF ca = c - a, ba = b - a; double denominator = 2 * (ba.y * ca.x - ca.y * ba.x); if (abs(denominator) > eps) { double numerator = ba.y * ca.x * ca.x - ca.y * ba.x * ba.x; @@ -513,7 +513,7 @@ static double interpolateQuadatric(Pwl::Point const &a, Pwl::Point const &b, return a.y < c.y - eps ? a.x : (c.y < a.y - eps ? c.x : b.x); } -double Awb::coarseSearch(Pwl const &prior) +double Awb::coarseSearch(ipa::Pwl const &prior) { points_.clear(); /* assume doesn't deallocate memory */ size_t bestPoint = 0; @@ -525,14 +525,14 @@ double Awb::coarseSearch(Pwl const &prior) double b = config_.ctB.eval(t, &spanB); double gainR = 1 / r, gainB = 1 / b; double delta2Sum = computeDelta2Sum(gainR, gainB); - double priorLogLikelihood = prior.eval(prior.domain().clip(t)); + double priorLogLikelihood = prior.eval(prior.domain().clamp(t)); double finalLogLikelihood = delta2Sum - priorLogLikelihood; LOG(RPiAwb, Debug) << "t: " << t << " gain R " << gainR << " gain B " << gainB << " delta2_sum " << delta2Sum << " prior " << priorLogLikelihood << " final " << finalLogLikelihood; - points_.push_back(Pwl::Point(t, finalLogLikelihood)); + points_.push_back(PointF(t, finalLogLikelihood)); if (points_.back().y < points_[bestPoint].y) bestPoint = points_.size() - 1; if (t == mode_->ctHi) @@ -559,7 +559,7 @@ double Awb::coarseSearch(Pwl const &prior) return t; } -void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior) +void Awb::fineSearch(double &t, double &r, double &b, ipa::Pwl const &prior) { int spanR = -1, spanB = -1; config_.ctR.eval(t, &spanR); @@ -570,7 +570,7 @@ void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior) config_.ctR.eval(t - nsteps * step, &spanR); double bDiff = config_.ctB.eval(t + nsteps * step, &spanB) - config_.ctB.eval(t - nsteps * step, &spanB); - Pwl::Point transverse(bDiff, -rDiff); + PointF transverse(bDiff, -rDiff); if (transverse.len2() < 1e-6) return; /* @@ -592,17 +592,17 @@ void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior) for (int i = -nsteps; i <= nsteps; i++) { double tTest = t + i * step; double priorLogLikelihood = - prior.eval(prior.domain().clip(tTest)); + prior.eval(prior.domain().clamp(tTest)); double rCurve = config_.ctR.eval(tTest, &spanR); double bCurve = config_.ctB.eval(tTest, &spanB); /* x will be distance off the curve, y the log likelihood there */ - Pwl::Point points[maxNumDeltas]; + PointF points[maxNumDeltas]; int bestPoint = 0; /* Take some measurements transversely *off* the CT curve. */ for (int j = 0; j < numDeltas; j++) { points[j].x = -config_.transverseNeg + (transverseRange * j) / (numDeltas - 1); - Pwl::Point rbTest = Pwl::Point(rCurve, bCurve) + + PointF rbTest = PointF(rCurve, bCurve) + transverse * points[j].x; double rTest = rbTest.x, bTest = rbTest.y; double gainR = 1 / rTest, gainB = 1 / bTest; @@ -619,7 +619,7 @@ void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior) * now let's do a quadratic interpolation for the best result. */ bestPoint = std::max(1, std::min(bestPoint, numDeltas - 2)); - Pwl::Point rbTest = Pwl::Point(rCurve, bCurve) + + PointF rbTest = PointF(rCurve, bCurve) + transverse * interpolateQuadatric(points[bestPoint - 1], points[bestPoint], points[bestPoint + 1]); @@ -653,7 +653,7 @@ void Awb::awbBayes() * Get the current prior, and scale according to how many zones are * valid... not entirely sure about this. */ - Pwl prior = interpolatePrior(); + ipa::Pwl prior = interpolatePrior(); prior *= zones_.size() / (double)(statistics_->awbRegions.numRegions()); prior.map([](double x, double y) { LOG(RPiAwb, Debug) << "(" << x << "," << y << ")"; diff --git a/src/ipa/rpi/controller/rpi/awb.h b/src/ipa/rpi/controller/rpi/awb.h index 499b4519c..7869d630a 100644 --- a/src/ipa/rpi/controller/rpi/awb.h +++ b/src/ipa/rpi/controller/rpi/awb.h @@ -10,11 +10,14 @@ #include #include +#include + #include "../awb_algorithm.h" -#include "../pwl.h" #include "../awb_status.h" #include "../statistics.h" +#include "libipa/pwl.h" + namespace RPiController { /* Control algorithm to perform AWB calculations. */ @@ -28,7 +31,7 @@ struct AwbMode { struct AwbPrior { int read(const libcamera::YamlObject ¶ms); double lux; /* lux level */ - Pwl prior; /* maps CT to prior log likelihood for this lux level */ + libcamera::ipa::Pwl prior; /* maps CT to prior log likelihood for this lux level */ }; struct AwbConfig { @@ -41,10 +44,10 @@ struct AwbConfig { unsigned int convergenceFrames; /* approx number of frames to converge */ double speed; /* IIR filter speed applied to algorithm results */ bool fast; /* "fast" mode uses a 16x16 rather than 32x32 grid */ - Pwl ctR; /* function maps CT to r (= R/G) */ - Pwl ctB; /* function maps CT to b (= B/G) */ - Pwl ctRInverse; /* inverse of ctR */ - Pwl ctBInverse; /* inverse of ctB */ + libcamera::ipa::Pwl ctR; /* function maps CT to r (= R/G) */ + libcamera::ipa::Pwl ctB; /* function maps CT to b (= B/G) */ + libcamera::ipa::Pwl ctRInverse; /* inverse of ctR */ + libcamera::ipa::Pwl ctBInverse; /* inverse of ctB */ /* table of illuminant priors at different lux levels */ std::vector priors; /* AWB "modes" (determines the search range) */ @@ -161,11 +164,11 @@ private: void awbGrey(); void prepareStats(); double computeDelta2Sum(double gainR, double gainB); - Pwl interpolatePrior(); - double coarseSearch(Pwl const &prior); - void fineSearch(double &t, double &r, double &b, Pwl const &prior); + libcamera::ipa::Pwl interpolatePrior(); + double coarseSearch(libcamera::ipa::Pwl const &prior); + void fineSearch(double &t, double &r, double &b, libcamera::ipa::Pwl const &prior); std::vector zones_; - std::vector points_; + std::vector points_; /* manual r setting */ double manualR_; /* manual b setting */ diff --git a/src/ipa/rpi/controller/rpi/ccm.cpp b/src/ipa/rpi/controller/rpi/ccm.cpp index c55880296..3272a1416 100644 --- a/src/ipa/rpi/controller/rpi/ccm.cpp +++ b/src/ipa/rpi/controller/rpi/ccm.cpp @@ -71,7 +71,7 @@ int Ccm::read(const libcamera::YamlObject ¶ms) int ret; if (params.contains("saturation")) { - ret = config_.saturation.read(params["saturation"]); + ret = config_.saturation.readYaml(params["saturation"]); if (ret) return ret; } @@ -172,7 +172,7 @@ void Ccm::prepare(Metadata *imageMetadata) ccmStatus.saturation = saturation; if (!config_.saturation.empty()) saturation *= config_.saturation.eval( - config_.saturation.domain().clip(lux.lux)); + config_.saturation.domain().clamp(lux.lux)); ccm = applySaturation(ccm, saturation); for (int j = 0; j < 3; j++) for (int i = 0; i < 3; i++) diff --git a/src/ipa/rpi/controller/rpi/ccm.h b/src/ipa/rpi/controller/rpi/ccm.h index b3abeddf6..4e5b33fef 100644 --- a/src/ipa/rpi/controller/rpi/ccm.h +++ b/src/ipa/rpi/controller/rpi/ccm.h @@ -8,8 +8,9 @@ #include +#include + #include "../ccm_algorithm.h" -#include "../pwl.h" namespace RPiController { @@ -54,7 +55,7 @@ struct CtCcm { struct CcmConfig { std::vector ccms; - Pwl saturation; + libcamera::ipa::Pwl saturation; }; class Ccm : public CcmAlgorithm diff --git a/src/ipa/rpi/controller/rpi/contrast.cpp b/src/ipa/rpi/controller/rpi/contrast.cpp index 9eef792d3..66871a61e 100644 --- a/src/ipa/rpi/controller/rpi/contrast.cpp +++ b/src/ipa/rpi/controller/rpi/contrast.cpp @@ -53,7 +53,7 @@ int Contrast::read(const libcamera::YamlObject ¶ms) config_.hiHistogram = params["hi_histogram"].get(0.95); config_.hiLevel = params["hi_level"].get(0.95); config_.hiMax = params["hi_max"].get(2000); - return config_.gammaCurve.read(params["gamma_curve"]); + return config_.gammaCurve.readYaml(params["gamma_curve"]); } void Contrast::setBrightness(double brightness) @@ -92,10 +92,10 @@ void Contrast::prepare(Metadata *imageMetadata) imageMetadata->set("contrast.status", status_); } -Pwl computeStretchCurve(Histogram const &histogram, +ipa::Pwl computeStretchCurve(Histogram const &histogram, ContrastConfig const &config) { - Pwl enhance; + ipa::Pwl enhance; enhance.append(0, 0); /* * If the start of the histogram is rather empty, try to pull it down a @@ -136,10 +136,10 @@ Pwl computeStretchCurve(Histogram const &histogram, return enhance; } -Pwl applyManualContrast(Pwl const &gammaCurve, double brightness, - double contrast) +ipa::Pwl applyManualContrast(ipa::Pwl const &gammaCurve, double brightness, + double contrast) { - Pwl newGammaCurve; + ipa::Pwl newGammaCurve; LOG(RPiContrast, Debug) << "Manual brightness " << brightness << " contrast " << contrast; gammaCurve.map([&](double x, double y) { @@ -160,7 +160,7 @@ void Contrast::process(StatisticsPtr &stats, * ways: 1. Adjust the gamma curve so as to pull the start of the * histogram down, and possibly push the end up. */ - Pwl gammaCurve = config_.gammaCurve; + ipa::Pwl gammaCurve = config_.gammaCurve; if (ceEnable_) { if (config_.loMax != 0 || config_.hiMax != 0) gammaCurve = computeStretchCurve(histogram, config_).compose(gammaCurve); diff --git a/src/ipa/rpi/controller/rpi/contrast.h b/src/ipa/rpi/controller/rpi/contrast.h index a9d9bbc99..c0f7db981 100644 --- a/src/ipa/rpi/controller/rpi/contrast.h +++ b/src/ipa/rpi/controller/rpi/contrast.h @@ -8,8 +8,9 @@ #include +#include + #include "../contrast_algorithm.h" -#include "../pwl.h" namespace RPiController { @@ -26,7 +27,7 @@ struct ContrastConfig { double hiHistogram; double hiLevel; double hiMax; - Pwl gammaCurve; + libcamera::ipa::Pwl gammaCurve; }; class Contrast : public ContrastAlgorithm diff --git a/src/ipa/rpi/controller/rpi/geq.cpp b/src/ipa/rpi/controller/rpi/geq.cpp index fb539d1f2..c9c38ebff 100644 --- a/src/ipa/rpi/controller/rpi/geq.cpp +++ b/src/ipa/rpi/controller/rpi/geq.cpp @@ -9,7 +9,6 @@ #include "../device_status.h" #include "../lux_status.h" -#include "../pwl.h" #include "geq.h" @@ -45,7 +44,7 @@ int Geq::read(const libcamera::YamlObject ¶ms) } if (params.contains("strength")) { - int ret = config_.strength.read(params["strength"]); + int ret = config_.strength.readYaml(params["strength"]); if (ret) return ret; } @@ -67,7 +66,7 @@ void Geq::prepare(Metadata *imageMetadata) GeqStatus geqStatus = {}; double strength = config_.strength.empty() ? 1.0 - : config_.strength.eval(config_.strength.domain().clip(luxStatus.lux)); + : config_.strength.eval(config_.strength.domain().clamp(luxStatus.lux)); strength *= deviceStatus.analogueGain; double offset = config_.offset * strength; double slope = config_.slope * strength; diff --git a/src/ipa/rpi/controller/rpi/geq.h b/src/ipa/rpi/controller/rpi/geq.h index 2c8400c2f..e8b9f4270 100644 --- a/src/ipa/rpi/controller/rpi/geq.h +++ b/src/ipa/rpi/controller/rpi/geq.h @@ -6,6 +6,8 @@ */ #pragma once +#include + #include "../algorithm.h" #include "../geq_status.h" @@ -16,7 +18,7 @@ namespace RPiController { struct GeqConfig { uint16_t offset; double slope; - Pwl strength; /* lux to strength factor */ + libcamera::ipa::Pwl strength; /* lux to strength factor */ }; class Geq : public Algorithm diff --git a/src/ipa/rpi/controller/rpi/hdr.cpp b/src/ipa/rpi/controller/rpi/hdr.cpp index 34cf360e9..d533a4ea4 100644 --- a/src/ipa/rpi/controller/rpi/hdr.cpp +++ b/src/ipa/rpi/controller/rpi/hdr.cpp @@ -42,7 +42,7 @@ void HdrConfig::read(const libcamera::YamlObject ¶ms, const std::string &mod /* Lens shading related parameters. */ if (params.contains("spatial_gain_curve")) { - spatialGainCurve.read(params["spatial_gain_curve"]); + spatialGainCurve.readYaml(params["spatial_gain_curve"]); } else if (params.contains("spatial_gain")) { double spatialGain = params["spatial_gain"].get(2.0); spatialGainCurve.append(0.0, spatialGain); @@ -66,7 +66,7 @@ void HdrConfig::read(const libcamera::YamlObject ¶ms, const std::string &mod iirStrength = params["iir_strength"].get(8.0); strength = params["strength"].get(1.5); if (tonemapEnable) - tonemap.read(params["tonemap"]); + tonemap.readYaml(params["tonemap"]); speed = params["speed"].get(1.0); if (params.contains("hi_quantile_targets")) { hiQuantileTargets = params["hi_quantile_targets"].getList().value(); @@ -212,7 +212,7 @@ bool Hdr::updateTonemap([[maybe_unused]] StatisticsPtr &stats, HdrConfig &config /* When there's a change of HDR mode we start over with a new tonemap curve. */ if (delayedStatus_.mode != previousMode_) { previousMode_ = delayedStatus_.mode; - tonemap_ = Pwl(); + tonemap_ = ipa::Pwl(); } /* No tonemapping. No need to output a tonemap.status. */ @@ -275,7 +275,7 @@ bool Hdr::updateTonemap([[maybe_unused]] StatisticsPtr &stats, HdrConfig &config double power = std::clamp(min_power, config.powerMin, config.powerMax); /* Generate the tonemap, including the contrast adjustment factors. */ - Pwl tonemap; + libcamera::ipa::Pwl tonemap; tonemap.append(0, 0); for (unsigned int i = 0; i <= 6; i++) { double x = 1 << (i + 9); /* x loops from 512 to 32768 inclusive */ diff --git a/src/ipa/rpi/controller/rpi/hdr.h b/src/ipa/rpi/controller/rpi/hdr.h index 9b7327f8b..5c2f3988d 100644 --- a/src/ipa/rpi/controller/rpi/hdr.h +++ b/src/ipa/rpi/controller/rpi/hdr.h @@ -12,9 +12,10 @@ #include +#include + #include "../hdr_algorithm.h" #include "../hdr_status.h" -#include "../pwl.h" /* This is our implementation of an HDR algorithm. */ @@ -26,7 +27,7 @@ struct HdrConfig { std::map channelMap; /* Lens shading related parameters. */ - Pwl spatialGainCurve; /* Brightness to gain curve for different image regions. */ + libcamera::ipa::Pwl spatialGainCurve; /* Brightness to gain curve for different image regions. */ unsigned int diffusion; /* How much to diffuse the gain spatially. */ /* Tonemap related parameters. */ @@ -35,7 +36,7 @@ struct HdrConfig { double detailSlope; double iirStrength; double strength; - Pwl tonemap; + libcamera::ipa::Pwl tonemap; /* These relate to adaptive tonemap calculation. */ double speed; std::vector hiQuantileTargets; /* quantiles to check for unsaturated images */ @@ -75,7 +76,7 @@ private: HdrStatus status_; /* track the current HDR mode and channel */ HdrStatus delayedStatus_; /* track the delayed HDR mode and channel */ std::string previousMode_; - Pwl tonemap_; + libcamera::ipa::Pwl tonemap_; libcamera::Size regions_; /* stats regions */ unsigned int numRegions_; /* total number of stats regions */ std::vector gains_[2]; diff --git a/src/ipa/rpi/controller/rpi/tonemap.cpp b/src/ipa/rpi/controller/rpi/tonemap.cpp index 0426e9722..2dc50dfc8 100644 --- a/src/ipa/rpi/controller/rpi/tonemap.cpp +++ b/src/ipa/rpi/controller/rpi/tonemap.cpp @@ -33,7 +33,7 @@ int Tonemap::read(const libcamera::YamlObject ¶ms) config_.detailSlope = params["detail_slope"].get(0.1); config_.iirStrength = params["iir_strength"].get(1.0); config_.strength = params["strength"].get(1.0); - config_.tonemap.read(params["tone_curve"]); + config_.tonemap.readYaml(params["tone_curve"]); return 0; } diff --git a/src/ipa/rpi/controller/rpi/tonemap.h b/src/ipa/rpi/controller/rpi/tonemap.h index f25aa47f8..ba0cf5c40 100644 --- a/src/ipa/rpi/controller/rpi/tonemap.h +++ b/src/ipa/rpi/controller/rpi/tonemap.h @@ -6,8 +6,9 @@ */ #pragma once +#include + #include "algorithm.h" -#include "pwl.h" namespace RPiController { @@ -16,7 +17,7 @@ struct TonemapConfig { double detailSlope; double iirStrength; double strength; - Pwl tonemap; + libcamera::ipa::Pwl tonemap; }; class Tonemap : public Algorithm diff --git a/src/ipa/rpi/controller/tonemap_status.h b/src/ipa/rpi/controller/tonemap_status.h index 41a7bf2ff..0364ff66f 100644 --- a/src/ipa/rpi/controller/tonemap_status.h +++ b/src/ipa/rpi/controller/tonemap_status.h @@ -6,12 +6,12 @@ */ #pragma once -#include "pwl.h" +#include struct TonemapStatus { uint16_t detailConstant; double detailSlope; double iirStrength; double strength; - RPiController::Pwl tonemap; + libcamera::ipa::Pwl tonemap; };