Message ID | 20240405080259.1806453-5-paul.elder@ideasonboard.com |
---|---|
State | Superseded |
Headers | show |
Series |
|
Related | show |
Hi Paul, thanks for the patch. On Fri, Apr 05, 2024 at 05:02:59PM +0900, Paul Elder wrote: > 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 <paul.elder@ideasonboard.com> Looks good to me. Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> Cheers, Stefan > --- > 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 | 6 +- > 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, 83 insertions(+), 471 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 475d4c5c..adffce41 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<double> lutRx; > std::vector<double> lutRy; > diff --git a/src/ipa/rpi/controller/contrast_status.h b/src/ipa/rpi/controller/contrast_status.h > index fb9fe4ba..c9fbc3f6 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 32a4d31c..74b74888 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 70c2e24b..00000000 > --- 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 > - * > - * pwl.cpp - piecewise linear functions > - */ > - > -#include <cassert> > -#include <cmath> > -#include <stdexcept> > - > -#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<double>(); > - if (!x) > - return -EINVAL; > - if (it != list.begin() && *x <= points_.back().x) > - return -EINVAL; > - > - auto y = (++it)->get<double>(); > - 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<void(double x, double y)> f) const > -{ > - for (auto &pt : points_) > - f(pt.x, pt.y); > -} > - > -void Pwl::map2(Pwl const &pwl0, Pwl const &pwl1, > - std::function<void(double x, double y0, double y1)> 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<double(double x, double y0, double y1)> 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 aacf6039..00000000 > --- 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 > - * > - * pwl.h - piecewise linear functions interface > - */ > -#pragma once > - > -#include <functional> > -#include <math.h> > -#include <vector> > - > -#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<Point> 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<void(double x, double y)> 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<void(double x, double y0, double y1)> 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<double(double x, double y0, double y1)> 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<Point> points_; > -}; > - > -} /* namespace RPiController */ > diff --git a/src/ipa/rpi/controller/rpi/af.cpp b/src/ipa/rpi/controller/rpi/af.cpp > index ed0c8a94..d54cee69 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<uint32_t>(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 6d2bae67..a6b2ad07 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 8116c6c1..e6d292f9 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<int, AgcConstraintMode> > @@ -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 4cf7233e..157b3baf 100644 > --- a/src/ipa/rpi/controller/rpi/agc_channel.h > +++ b/src/ipa/rpi/controller/rpi/agc_channel.h > @@ -12,10 +12,11 @@ > > #include <libcamera/base/utils.h> > > +#include <libipa/pwl.h> > + > #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<std::string, AgcExposureMode> exposureModes; > std::map<std::string, AgcConstraintMode> constraintModes; > std::vector<AgcChannelConstraint> 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 dde5785a..f4093910 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(FPoint const &a, FPoint const &b, > + FPoint 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; > + FPoint 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(FPoint(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); > + FPoint 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]; > + FPoint 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) + > + FPoint rbTest = FPoint(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) + > + FPoint rbTest = FPoint(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 cde6a62f..681353fe 100644 > --- a/src/ipa/rpi/controller/rpi/awb.h > +++ b/src/ipa/rpi/controller/rpi/awb.h > @@ -10,11 +10,14 @@ > #include <condition_variable> > #include <thread> > > +#include <libcamera/geometry.h> > + > #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<AwbPrior> 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<RGB> zones_; > - std::vector<Pwl::Point> points_; > + std::vector<libcamera::FPoint> 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 2e2e6664..68f9cff6 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 286d0b33..e5042176 100644 > --- a/src/ipa/rpi/controller/rpi/ccm.h > +++ b/src/ipa/rpi/controller/rpi/ccm.h > @@ -8,8 +8,9 @@ > > #include <vector> > > +#include <libipa/pwl.h> > + > #include "../ccm_algorithm.h" > -#include "../pwl.h" > > namespace RPiController { > > @@ -54,7 +55,7 @@ struct CtCcm { > > struct CcmConfig { > std::vector<CtCcm> 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 4e038a02..7ba6cef0 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<double>(0.95); > config_.hiLevel = params["hi_level"].get<double>(0.95); > config_.hiMax = params["hi_max"].get<double>(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 59aa70dc..c61bfb4d 100644 > --- a/src/ipa/rpi/controller/rpi/contrast.h > +++ b/src/ipa/rpi/controller/rpi/contrast.h > @@ -8,8 +8,9 @@ > > #include <mutex> > > +#include <libipa/pwl.h> > + > #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 510870e9..a0c350ec 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 ee3a52ff..5ae29edc 100644 > --- a/src/ipa/rpi/controller/rpi/geq.h > +++ b/src/ipa/rpi/controller/rpi/geq.h > @@ -6,6 +6,8 @@ > */ > #pragma once > > +#include <libipa/pwl.h> > + > #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 fb580548..b3ff2e25 100644 > --- a/src/ipa/rpi/controller/rpi/hdr.cpp > +++ b/src/ipa/rpi/controller/rpi/hdr.cpp > @@ -40,7 +40,7 @@ void HdrConfig::read(const libcamera::YamlObject ¶ms, const std::string &mod > > /* Lens shading related parameters. */ > if (params.contains("spatial_gain")) { > - spatialGain.read(params["spatial_gain"]); > + spatialGain.readYaml(params["spatial_gain"]); > diffusion = params["diffusion"].get<unsigned int>(3); > /* Clip to an arbitrary limit just to stop typos from killing the system! */ > const unsigned int MAX_DIFFUSION = 15; > @@ -57,7 +57,7 @@ void HdrConfig::read(const libcamera::YamlObject ¶ms, const std::string &mod > iirStrength = params["iir_strength"].get<double>(8.0); > strength = params["strength"].get<double>(1.5); > if (tonemapEnable) > - tonemap.read(params["tonemap"]); > + tonemap.readYaml(params["tonemap"]); > > /* Read any stitch parameters. */ > stitchEnable = params["stitch_enable"].get<int>(0); > @@ -183,7 +183,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. */ > diff --git a/src/ipa/rpi/controller/rpi/hdr.h b/src/ipa/rpi/controller/rpi/hdr.h > index 980aa3d1..991fa690 100644 > --- a/src/ipa/rpi/controller/rpi/hdr.h > +++ b/src/ipa/rpi/controller/rpi/hdr.h > @@ -12,9 +12,10 @@ > > #include <libcamera/geometry.h> > > +#include <libipa/pwl.h> > + > #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<unsigned int, std::string> channelMap; > > /* Lens shading related parameters. */ > - Pwl spatialGain; /* Brightness to gain curve for different image regions. */ > + libcamera::ipa::Pwl spatialGain; /* 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; > > /* Stitch related parameters. */ > bool stitchEnable; > @@ -67,7 +68,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<double> gains_[2]; > diff --git a/src/ipa/rpi/controller/rpi/tonemap.cpp b/src/ipa/rpi/controller/rpi/tonemap.cpp > index 5f8b2bf2..f7a47fe0 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<double>(0.1); > config_.iirStrength = params["iir_strength"].get<double>(1.0); > config_.strength = params["strength"].get<double>(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 f25aa47f..ba0cf5c4 100644 > --- a/src/ipa/rpi/controller/rpi/tonemap.h > +++ b/src/ipa/rpi/controller/rpi/tonemap.h > @@ -6,8 +6,9 @@ > */ > #pragma once > > +#include <libipa/pwl.h> > + > #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 0e639946..e51a2b6c 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 <libipa/pwl.h> > > struct TonemapStatus { > uint16_t detailConstant; > double detailSlope; > double iirStrength; > double strength; > - RPiController::Pwl tonemap; > + libcamera::ipa::Pwl tonemap; > }; > -- > 2.39.2 >
diff --git a/src/ipa/rpi/controller/cac_status.h b/src/ipa/rpi/controller/cac_status.h index 475d4c5c..adffce41 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<double> lutRx; std::vector<double> lutRy; diff --git a/src/ipa/rpi/controller/contrast_status.h b/src/ipa/rpi/controller/contrast_status.h index fb9fe4ba..c9fbc3f6 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 32a4d31c..74b74888 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 70c2e24b..00000000 --- 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 - * - * pwl.cpp - piecewise linear functions - */ - -#include <cassert> -#include <cmath> -#include <stdexcept> - -#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<double>(); - if (!x) - return -EINVAL; - if (it != list.begin() && *x <= points_.back().x) - return -EINVAL; - - auto y = (++it)->get<double>(); - 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<void(double x, double y)> f) const -{ - for (auto &pt : points_) - f(pt.x, pt.y); -} - -void Pwl::map2(Pwl const &pwl0, Pwl const &pwl1, - std::function<void(double x, double y0, double y1)> 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<double(double x, double y0, double y1)> 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 aacf6039..00000000 --- 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 - * - * pwl.h - piecewise linear functions interface - */ -#pragma once - -#include <functional> -#include <math.h> -#include <vector> - -#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<Point> 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<void(double x, double y)> 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<void(double x, double y0, double y1)> 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<double(double x, double y0, double y1)> 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<Point> points_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/rpi/af.cpp b/src/ipa/rpi/controller/rpi/af.cpp index ed0c8a94..d54cee69 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<uint32_t>(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 6d2bae67..a6b2ad07 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 8116c6c1..e6d292f9 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<int, AgcConstraintMode> @@ -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 4cf7233e..157b3baf 100644 --- a/src/ipa/rpi/controller/rpi/agc_channel.h +++ b/src/ipa/rpi/controller/rpi/agc_channel.h @@ -12,10 +12,11 @@ #include <libcamera/base/utils.h> +#include <libipa/pwl.h> + #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<std::string, AgcExposureMode> exposureModes; std::map<std::string, AgcConstraintMode> constraintModes; std::vector<AgcChannelConstraint> 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 dde5785a..f4093910 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(FPoint const &a, FPoint const &b, + FPoint 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; + FPoint 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(FPoint(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); + FPoint 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]; + FPoint 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) + + FPoint rbTest = FPoint(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) + + FPoint rbTest = FPoint(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 cde6a62f..681353fe 100644 --- a/src/ipa/rpi/controller/rpi/awb.h +++ b/src/ipa/rpi/controller/rpi/awb.h @@ -10,11 +10,14 @@ #include <condition_variable> #include <thread> +#include <libcamera/geometry.h> + #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<AwbPrior> 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<RGB> zones_; - std::vector<Pwl::Point> points_; + std::vector<libcamera::FPoint> 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 2e2e6664..68f9cff6 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 286d0b33..e5042176 100644 --- a/src/ipa/rpi/controller/rpi/ccm.h +++ b/src/ipa/rpi/controller/rpi/ccm.h @@ -8,8 +8,9 @@ #include <vector> +#include <libipa/pwl.h> + #include "../ccm_algorithm.h" -#include "../pwl.h" namespace RPiController { @@ -54,7 +55,7 @@ struct CtCcm { struct CcmConfig { std::vector<CtCcm> 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 4e038a02..7ba6cef0 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<double>(0.95); config_.hiLevel = params["hi_level"].get<double>(0.95); config_.hiMax = params["hi_max"].get<double>(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 59aa70dc..c61bfb4d 100644 --- a/src/ipa/rpi/controller/rpi/contrast.h +++ b/src/ipa/rpi/controller/rpi/contrast.h @@ -8,8 +8,9 @@ #include <mutex> +#include <libipa/pwl.h> + #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 510870e9..a0c350ec 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 ee3a52ff..5ae29edc 100644 --- a/src/ipa/rpi/controller/rpi/geq.h +++ b/src/ipa/rpi/controller/rpi/geq.h @@ -6,6 +6,8 @@ */ #pragma once +#include <libipa/pwl.h> + #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 fb580548..b3ff2e25 100644 --- a/src/ipa/rpi/controller/rpi/hdr.cpp +++ b/src/ipa/rpi/controller/rpi/hdr.cpp @@ -40,7 +40,7 @@ void HdrConfig::read(const libcamera::YamlObject ¶ms, const std::string &mod /* Lens shading related parameters. */ if (params.contains("spatial_gain")) { - spatialGain.read(params["spatial_gain"]); + spatialGain.readYaml(params["spatial_gain"]); diffusion = params["diffusion"].get<unsigned int>(3); /* Clip to an arbitrary limit just to stop typos from killing the system! */ const unsigned int MAX_DIFFUSION = 15; @@ -57,7 +57,7 @@ void HdrConfig::read(const libcamera::YamlObject ¶ms, const std::string &mod iirStrength = params["iir_strength"].get<double>(8.0); strength = params["strength"].get<double>(1.5); if (tonemapEnable) - tonemap.read(params["tonemap"]); + tonemap.readYaml(params["tonemap"]); /* Read any stitch parameters. */ stitchEnable = params["stitch_enable"].get<int>(0); @@ -183,7 +183,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. */ diff --git a/src/ipa/rpi/controller/rpi/hdr.h b/src/ipa/rpi/controller/rpi/hdr.h index 980aa3d1..991fa690 100644 --- a/src/ipa/rpi/controller/rpi/hdr.h +++ b/src/ipa/rpi/controller/rpi/hdr.h @@ -12,9 +12,10 @@ #include <libcamera/geometry.h> +#include <libipa/pwl.h> + #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<unsigned int, std::string> channelMap; /* Lens shading related parameters. */ - Pwl spatialGain; /* Brightness to gain curve for different image regions. */ + libcamera::ipa::Pwl spatialGain; /* 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; /* Stitch related parameters. */ bool stitchEnable; @@ -67,7 +68,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<double> gains_[2]; diff --git a/src/ipa/rpi/controller/rpi/tonemap.cpp b/src/ipa/rpi/controller/rpi/tonemap.cpp index 5f8b2bf2..f7a47fe0 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<double>(0.1); config_.iirStrength = params["iir_strength"].get<double>(1.0); config_.strength = params["strength"].get<double>(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 f25aa47f..ba0cf5c4 100644 --- a/src/ipa/rpi/controller/rpi/tonemap.h +++ b/src/ipa/rpi/controller/rpi/tonemap.h @@ -6,8 +6,9 @@ */ #pragma once +#include <libipa/pwl.h> + #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 0e639946..e51a2b6c 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 <libipa/pwl.h> struct TonemapStatus { uint16_t detailConstant; double detailSlope; double iirStrength; double strength; - RPiController::Pwl tonemap; + libcamera::ipa::Pwl tonemap; };
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 <paul.elder@ideasonboard.com> --- 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 | 6 +- 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, 83 insertions(+), 471 deletions(-) delete mode 100644 src/ipa/rpi/controller/pwl.cpp delete mode 100644 src/ipa/rpi/controller/pwl.h