From patchwork Fri Jun 7 08: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: 20239 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 2FC0FBD87C for ; Fri, 7 Jun 2024 08:43:15 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A2A1065458; Fri, 7 Jun 2024 10:43:12 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Zl6seWdz"; 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 ED8F6633DD for ; Fri, 7 Jun 2024 10:43:10 +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 720BF3D5; Fri, 7 Jun 2024 10:43:00 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1717749781; bh=xbGyVrq4ThND01xnyrmCx2lFoN5dgwImwoVyJWGJTNg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Zl6seWdz00vNtKNSTgY3Z6W8Sem3NaAadrXfSq7q4md8/y1wm2EvrE2Nl8WNtzyFr iP/9NRm7INbOaAZgrtjZsgowgUNqTsVIm+S0phGp6oNaBkqnsWVbJuRopGeOEsFM3U GwE4A16oyp+HHrStXpEDFYsRlfMVcz0TMbExfhR8= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder Subject: [PATCH v6 1/4] ipa: libipa: Add Vector class Date: Fri, 7 Jun 2024 17:42:58 +0900 Message-Id: <20240607084301.2791258-2-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240607084301.2791258-1-paul.elder@ideasonboard.com> References: <20240607084301.2791258-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" Add a vector class to libipa. The original purpose of this is to replace the floating-point Point class that Raspberry Pi used in their Pwl, as that implementation of Point seemed more akin to a Vector than a Point. This is added to libipa instead of to geometry.h to avoid public API issues, plus this is not expected to be needed by applications. Signed-off-by: Paul Elder Reviewed-by: Stefan Klug --- No change in v6 New in v5 --- src/ipa/libipa/vector.cpp | 162 ++++++++++++++++++++++++++++++ src/ipa/libipa/vector.h | 206 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 368 insertions(+) create mode 100644 src/ipa/libipa/vector.cpp create mode 100644 src/ipa/libipa/vector.h diff --git a/src/ipa/libipa/vector.cpp b/src/ipa/libipa/vector.cpp new file mode 100644 index 000000000..bdc3c447c --- /dev/null +++ b/src/ipa/libipa/vector.cpp @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * Copyright (C) 2024, Paul Elder + * + * Vector and related operations + */ + +#include "vector.h" + +#include + +/** + * \file vector.h + * \brief Vector class + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(Vector) + +namespace ipa { + +/** + * \class Vector + * \brief Vector class + * \tparam T Type of numerical values to be stored in the vector + * \tparam R Number of rows in the vector + */ + +/** + * \fn Vector::Vector() + * \brief Construct an identity vector + */ + +/** + * \fn Vector::Vector(const std::array &data) + * \brief Construct vector from supplied data + * \param data Data from which to construct a vector + * + * \a data is a one-dimensional vector and will be turned into a vector in + * row-major order. The size of \a data must be equal to the product of the + * number of rows and columns of the vector (RxC). + */ + +/** + * \fn Vector::readYaml + * \brief Populate the vector with yaml data + * \param yaml Yaml data to populate the vector with + * + * Any existing data in the vector will be overwritten. The size of the data + * read from \a yaml must be equal to the product of the number of rows and + * columns of the vector (RxC). + * + * The yaml data is expected to be a list with elements of type T. + * + * \return 0 on success, negative error code otherwise + */ + +/** + * \fn Vector::toString + * \brief Assemble and return a string describing the vector + * \return A string describing the vector + */ + +/** + * \fn T Vector::operator[](size_t i) const + * \brief Index to a row in the vector + * \param i Index of row to retrieve + * + * This operator[] returns a Span, which can then be indexed into again with + * another operator[], allowing a convenient m[i][j] to access elements of the + * vector. Note that the lifetime of the Span returned by this first-level + * operator[] is bound to that of the Vector itself, so it is not recommended + * to save the Span that is the result of this operator[]. + * + * \return Row \a i from the vector, as a Span + */ + +/** + * \fn T &Vector::operator[](size_t i) + * \copydoc Vector::operator[](size_t i) const + */ + +/** + * \fn Vector::x() + * \brief Convenience function to access the first element of the vector + */ + +/** + * \fn Vector::y() + * \brief Convenience function to access the second element of the vector + */ + +/** + * \fn Vector::operator-() const + * \brief Negate a Vector by negating both all of its coordinates + * \return The negated vector + */ + +/** + * \fn Vector::operator-(Vector const &other) const + * \brief Subtract one vector from another + * \param[in] other The other vector + * \return The difference of \a other from this vector + */ + +/** + * \fn Vector::operator+() + * \brief Add two vectors together + * \param[in] other The other vector + * \return The sum of the two vectors + */ + +/** + * \fn Vector::operator*(const Vector &other) const + * \brief Compute the dot product + * \param[in] other The other vector + * \return The dot product of the two vectors + */ + +/** + * \fn Vector::operator*(T factor) const + * \brief Scale up the vector + * \param[in] factor The factor + * \return The vector scaled up by \a factor + */ + +/** + * \fn Vector::operator/() + * \brief Scale down the vector + * \param[in] factor The factor + * \return The vector scaled down by \a factor + */ + +/** + * \fn Vector::len2() + * \brief Get the squared length of the vector + * \return The squared length of the vector + */ + +/** + * \fn Vector::len() + * \brief Get the length of the vector + * \return The length of the vector + */ + +/** + * \fn bool operator==(const Vector &lhs, const Vector &rhs) + * \brief Compare vectors for equality + * \return True if the two vectors are equal, false otherwise + */ + +/** + * \fn bool operator!=(const Vector &lhs, const Vector &rhs) + * \brief Compare vectors for inequality + * \return True if the two vectors are not equal, false otherwise + */ + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/vector.h b/src/ipa/libipa/vector.h new file mode 100644 index 000000000..87aad28b5 --- /dev/null +++ b/src/ipa/libipa/vector.h @@ -0,0 +1,206 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * Copyright (C) 2024, Paul Elder + * + * Vector and related operations + */ +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "libcamera/internal/yaml_parser.h" + +namespace libcamera { + +LOG_DECLARE_CATEGORY(Vector) + +namespace ipa { + +#ifndef __DOXYGEN__ +template && R >= 2> * = nullptr> +#else +template +#endif /* __DOXYGEN__ */ +class Vector +{ +public: + Vector() = default; + + Vector(const std::array &data) + { + ASSERT(data.size() == R); + + for (unsigned int i = 0; i < R; i++) + data_[i] = data[i]; + } + + ~Vector() = default; + + int readYaml(const libcamera::YamlObject &yaml) + { + if (yaml.size() != R) { + LOG(Vector, Error) + << "Wrong number of values in vector: expected " + << R << ", got " << yaml.size(); + return -EINVAL; + } + + unsigned int i = 0; + for (const auto &x : yaml.asList()) { + auto value = x.get(); + if (!value) { + LOG(Vector, Error) << "Failed to read vector value"; + return -EINVAL; + } + + data_[i++] = *value; + } + + return 0; + } + + const std::string toString() const + { + std::stringstream out; + + out << "Vector { "; + for (unsigned int i = 0; i < R; i++) { + out << (*this)[i]; + out << ((i + 1 < R) ? ", " : " "); + } + out << " }"; + + return out.str(); + } + + const T operator[](size_t i) const + { + return data_[i]; + } + + T &operator[](size_t i) + { + return data_[i]; + } + + const T x() const + { + return data_[0]; + } + + const T y() const + { + return data_[1]; + } + + constexpr Vector operator-() const + { + Vector ret; + for (unsigned int i = 0; i < R; i++) + ret[i] = -data_[i]; + return ret; + } + + constexpr Vector operator-(const Vector &other) const + { + Vector ret; + for (unsigned int i = 0; i < R; i++) + ret[i] = data_[i] - other[i]; + return ret; + } + + constexpr Vector operator+(const Vector &other) const + { + Vector ret; + for (unsigned int i = 0; i < R; i++) + ret[i] = data_[i] + other[i]; + return ret; + } + + constexpr T operator*(const Vector &other) const + { + T ret = 0; + for (unsigned int i = 0; i < R; i++) + ret += data_[i] * other[i]; + return ret; + } + + constexpr Vector operator*(T factor) const + { + Vector ret; + for (unsigned int i = 0; i < R; i++) + ret[i] = data_[i] * factor; + return ret; + } + + constexpr Vector operator/(T factor) const + { + Vector ret; + for (unsigned int i = 0; i < R; i++) + ret[i] = data_[i] / factor; + return ret; + } + + constexpr T len2() const + { + T ret = 0; + for (unsigned int i = 0; i < R; i++) + ret += data_[i] * data_[i]; + return ret; + } + + constexpr double len() const + { + return std::sqrt(len2()); + } + +private: + std::array data_; +}; + +#ifndef __DOXYGEN__ +template> * = nullptr> +#endif /* __DOXYGEN__ */ +bool operator==(const Vector &lhs, const Vector &rhs) +{ + for (unsigned int i = 0; i < R; i++) + if (lhs[i] != rhs[i]) + return false; + + return true; +} + +#ifndef __DOXYGEN__ +template> * = nullptr> +#endif /* __DOXYGEN__ */ +bool operator!=(const Vector &lhs, const Vector &rhs) +{ + for (unsigned int i = 0; i < R; i++) + if (lhs[i] != rhs[i]) + return true; + + return false; +} + +} /* namespace ipa */ + +#ifndef __DOXYGEN__ +template +std::ostream &operator<<(std::ostream &out, const ipa::Vector &v) +{ + out << v.toString(); + return out; +} +#endif /* __DOXYGEN__ */ + +} /* namespace libcamera */ From patchwork Fri Jun 7 08: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: 20240 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 1CF42BD87C for ; Fri, 7 Jun 2024 08:43:17 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id AE8CA65464; Fri, 7 Jun 2024 10:43:15 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="mWKXBZN+"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 873AB6545E for ; Fri, 7 Jun 2024 10:43:13 +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 ED0DA23F4; Fri, 7 Jun 2024 10:43:01 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1717749783; bh=KgC1MPZki9ISEpnqxcjpyYt6FNPC/LKWJieMtNcTcXA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mWKXBZN+yXRA6oWFY4VQVw2ye/y9m2zCI6CMMvNtBtV4KEGqTD2Cuv1ig3I5do9Ca CollQWrYs/4qwn5boI7z4C8g95i4kFRLw+xTR40pC10xoa0BFcCsFV9iMcAJwmK+67 NKomn+l41RFtGBQlzT694MFa4HzUkAq5km8hQqgk= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , Stefan Klug , David Plowman , Kieran Bingham Subject: [PATCH v6 2/4] ipa: libipa: Copy pwl from rpi Date: Fri, 7 Jun 2024 17:42:59 +0900 Message-Id: <20240607084301.2791258-3-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240607084301.2791258-1-paul.elder@ideasonboard.com> References: <20240607084301.2791258-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 --- No change in v6 Changes in v5: - remove meson.build to prevent compilation this early in the merge Changes in v4: - update the copy No change in v3 No change in v2 --- src/ipa/libipa/pwl.cpp | 269 +++++++++++++++++++++++++++++++++++++++++ src/ipa/libipa/pwl.h | 127 +++++++++++++++++++ 2 files changed, 396 insertions(+) create mode 100644 src/ipa/libipa/pwl.cpp create mode 100644 src/ipa/libipa/pwl.h 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 Jun 7 08: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: 20241 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 1D392BD87C for ; Fri, 7 Jun 2024 08:43:20 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A40E16546B; Fri, 7 Jun 2024 10:43:19 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="b74huaFy"; 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 60DC36545E for ; Fri, 7 Jun 2024 10:43:15 +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 2BD702CD9; Fri, 7 Jun 2024 10:43:03 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1717749785; bh=iY3GEorHP7b2YofjOdszWfsH4kp7phXLSDf9VQulOeI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=b74huaFyl3lvWyPNii2mQYcgwVAxxX/NCX/M4QO+lLWJmwtRP7ktvFhxu6MgVY78x WD2OsGCOT8NwfHiSNsyJeRowoAPvncL8q6mq2mrJVojP+8gBItTnGJXX238EWz5dhc LJTgkFDStTL9pDb7sNhm9zJ8RoPuAhCCTXK6kngc= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , Stefan Klug , David Plowman Subject: [PATCH v6 3/4] ipa: libipa: pwl: Clean up Pwl class to match libcamera Date: Fri, 7 Jun 2024 17:43:00 +0900 Message-Id: <20240607084301.2791258-4-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240607084301.2791258-1-paul.elder@ideasonboard.com> References: <20240607084301.2791258-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 Reviewed-by: Kieran Bingham --- Changes in v6: - move adding pwl to meson here Changes in v5: - fix documentation order - fix some typos - add the Vector-based PointF 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/meson.build | 2 + src/ipa/libipa/pwl.cpp | 365 +++++++++++++++++++++++++++++-------- src/ipa/libipa/pwl.h | 123 +++++-------- 3 files changed, 344 insertions(+), 146 deletions(-) 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 index e39123767..8f8ee17e8 100644 --- a/src/ipa/libipa/pwl.cpp +++ b/src/ipa/libipa/pwl.cpp @@ -1,19 +1,120 @@ /* 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 + */ + +/** + * \typedef Pwl::PointF + * \brief Describe a point in two-dimensional real space + */ + +/** + * \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 + */ -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 value 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 + */ + +/** + * \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 + * + * \a points must be in ascending order of x-value. + */ + +/** + * \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, and added in order. 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; @@ -24,64 +125,109 @@ int Pwl::read(const libcamera::YamlObject ¶ms) auto x = it->get(); if (!x) return -EINVAL; - if (it != list.begin() && *x <= points_.back().x) + if (it != list.begin() && *x <= points_.back().x()) return -EINVAL; auto y = (++it)->get(); 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)); + if (points_.empty() || points_.back().x() + eps < x) + 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)); + if (points_.empty() || points_.front().x() - eps > x) + 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); + 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; + double lo = points_[0].y(), hi = lo; for (auto &p : points_) - lo = std::min(lo, p.y), hi = std::max(hi, p.y); + lo = std::min(lo, p.y()), hi = std::max(hi, p.y()); 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); + 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); + 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. + * binary, though could review this if large Pwls start turning up. */ int lastSpan = points_.size() - 2; /* @@ -89,23 +235,36 @@ int Pwl::findSpan(double x, int span) const * control point */ span = std::max(0, std::min(lastSpan, span)); - while (span < lastSpan && x >= points_[span + 1].x) + while (span < lastSpan && x >= points_[span + 1].x()) span++; - while (span && x < points_[span].x) + while (span && x < points_[span].x()) span--; 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 left of the point 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]; - double t = ((xy - points_[span]) % spanVec) / spanVec.len2(); - if (t < -eps) /* off the start of this 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 (span == 0) { perp = points_[span]; return PerpType::Start; @@ -113,15 +272,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 +288,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()) - 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) + 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) { /* do nothing */; - else if (p.y > inverse.points_.back().x) { - inverse.append(p.y, p.x, eps); + } 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); + } else if (p.y() < inverse.points_.front().x()) { + inverse.prepend(p.y(), p.x(), eps); prepended = true; - } else + } else { neither = true; + } } /* @@ -161,44 +331,53 @@ 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; + 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) } }); + Pwl result({ PointF({ 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; + 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 */ - thisX = points_[thisSpan].x + - (other.points_[otherSpan + 1].x - - points_[thisSpan].y) * + thisX = points_[thisSpan].x() + + (other.points_[otherSpan + 1].x() - + points_[thisSpan].y()) * dx / dy; - thisY = other.points_[++otherSpan].x; + thisY = other.points_[++otherSpan].x(); } else if (std::abs(dy) > eps && otherSpan > 0 && - 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 previous span in other */ - thisX = points_[thisSpan].x + - (other.points_[otherSpan + 1].x - - points_[thisSpan].y) * + thisX = points_[thisSpan].x() + + (other.points_[otherSpan + 1].x() - + points_[thisSpan].y()) * dx / dy; - thisY = other.points_[--otherSpan].x; + thisY = other.points_[--otherSpan].x(); } else { /* we stay in the same span in other */ thisSpan++; - thisX = points_[thisSpan].x, - thisY = points_[thisSpan].y; + thisX = points_[thisSpan].x(), + thisY = points_[thisSpan].y(); } result.append(thisX, other.eval(thisY, &otherSpan, false), eps); @@ -206,32 +385,47 @@ 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); + 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); + 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; + 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; + 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; + x = pwl0.points_[++span0].x(); f(x, pwl0.eval(x, &span0, false), pwl1.eval(x, &span1, false)); } } +/** + * \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,27 +437,54 @@ 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), + 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), + append(domain.end, eval(clip ? points_.back().x() : domain.end, &span), 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_) - pt.y *= d; + pt[1] *= 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..c73693341 100644 --- a/src/ipa/libipa/pwl.h +++ b/src/ipa/libipa/pwl.h @@ -2,126 +2,101 @@ /* * 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 { +#include "vector.h" + +namespace libcamera { + +namespace ipa { class Pwl { public: + using PointF = Vector; + + 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 Jun 7 08: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: 20242 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 89EB9C3292 for ; Fri, 7 Jun 2024 08:43:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0A83965463; Fri, 7 Jun 2024 10: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="C6eThp86"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9A87B65463 for ; Fri, 7 Jun 2024 10:43:17 +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 5CFE13D5; Fri, 7 Jun 2024 10:43:06 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1717749788; bh=E+gmdHM31Y2kh6SU975ejV+D81/+I3//RSYvl4hILho=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=C6eThp86oF8tmWRD7utnr0VAn3cDdfel+3TszsEU0cJMQ7/TrlMWfU85J+DOBQuSF jdfl2WaJFjke6sUlVFLgWTlbaJ+AOFa2qnIVb8UuR8JRLBl3lU9VMPdxa//ELQPWj5 8M2RENnAZFzmrCf9yb7oLYLVA+u9p8y4/L6iGIAg= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , Stefan Klug , David Plowman , Kieran Bingham Subject: [PATCH v6 4/4] ipa: rpi: controller: Use libipa's Pwl class Date: Fri, 7 Jun 2024 17:43:01 +0900 Message-Id: <20240607084301.2791258-5-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240607084301.2791258-1-paul.elder@ideasonboard.com> References: <20240607084301.2791258-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 --- Changes in v6: - move adding Pwl to meson to the Pwl cleanup instead of here, as it is already usable at that stage Changes in v5: - add PointF as a typedef of Vector, and use it - add Pwl to meson.build at this stage instead of earlier 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 | 76 +++--- 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, 102 insertions(+), 490 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..65daecca2 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,26 +494,26 @@ Pwl Awb::interpolatePrior() } } -static double interpolateQuadatric(Pwl::Point const &a, Pwl::Point const &b, - Pwl::Point const &c) +static double interpolateQuadatric(ipa::Pwl::PointF const &a, ipa::Pwl::PointF const &b, + ipa::Pwl::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; - double denominator = 2 * (ba.y * ca.x - ca.y * ba.x); + ipa::Pwl::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; - double result = numerator / denominator + a.x; - return std::max(a.x, std::min(c.x, result)); + double numerator = ba.y() * ca.x() * ca.x() - ca.y() * ba.x() * ba.x(); + double result = numerator / denominator + a.x(); + return std::max(a.x(), std::min(c.x(), result)); } /* has degenerated to straight line segment */ - return a.y < c.y - eps ? a.x : (c.y < a.y - eps ? c.x : b.x); + 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,22 +525,22 @@ 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)); - if (points_.back().y < points_[bestPoint].y) + points_.push_back(ipa::Pwl::PointF({t, finalLogLikelihood})); + if (points_.back().y() < points_[bestPoint].y()) bestPoint = points_.size() - 1; if (t == mode_->ctHi) break; /* for even steps along the r/b curve scale them by the current t */ t = std::min(t + t / 10 * config_.coarseStep, mode_->ctHi); } - t = points_[bestPoint].x; + t = points_[bestPoint].x(); LOG(RPiAwb, Debug) << "Coarse search found CT " << t; /* * We have the best point of the search, but refine it with a quadratic @@ -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); + ipa::Pwl::PointF transverse({bDiff, -rDiff}); if (transverse.len2() < 1e-6) return; /* @@ -592,26 +592,26 @@ 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]; + ipa::Pwl::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) + - transverse * points[j].x; - double rTest = rbTest.x, bTest = rbTest.y; + points[j][0] = -config_.transverseNeg + + (transverseRange * j) / (numDeltas - 1); + ipa::Pwl::PointF rbTest = ipa::Pwl::PointF({rCurve, bCurve}) + + transverse * points[j].x(); + double rTest = rbTest.x(), bTest = rbTest.y(); double gainR = 1 / rTest, gainB = 1 / bTest; double delta2Sum = computeDelta2Sum(gainR, gainB); - points[j].y = delta2Sum - priorLogLikelihood; + points[j][1] = delta2Sum - priorLogLikelihood; LOG(RPiAwb, Debug) << "At t " << tTest << " r " << rTest << " b " - << bTest << ": " << points[j].y; - if (points[j].y < points[bestPoint].y) + << bTest << ": " << points[j].y(); + if (points[j].y() < points[bestPoint].y()) bestPoint = j; } /* @@ -619,11 +619,11 @@ 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) + - transverse * interpolateQuadatric(points[bestPoint - 1], - points[bestPoint], - points[bestPoint + 1]); - double rTest = rbTest.x, bTest = rbTest.y; + ipa::Pwl::PointF rbTest = ipa::Pwl::PointF({rCurve, bCurve}) + + transverse * interpolateQuadatric(points[bestPoint - 1], + points[bestPoint], + points[bestPoint + 1]); + double rTest = rbTest.x(), bTest = rbTest.y(); double gainR = 1 / rTest, gainB = 1 / bTest; double delta2Sum = computeDelta2Sum(gainR, gainB); double finalLogLikelihood = delta2Sum - priorLogLikelihood; @@ -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..e85ca8938 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; };