[PATCH v2 4/4] ipa: rpi: controller: Use libipa's Pwl class
Paul Elder
paul.elder at ideasonboard.com
Fri Apr 26 09:36:12 CEST 2024
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 at ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug at ideasonboard.com>
---
Changes in v2:
- s/FPoint/PointF/g
---
src/ipa/rpi/controller/cac_status.h | 2 -
src/ipa/rpi/controller/contrast_status.h | 4 +-
src/ipa/rpi/controller/meson.build | 2 +-
src/ipa/rpi/controller/pwl.cpp | 269 ---------------------
src/ipa/rpi/controller/pwl.h | 127 ----------
src/ipa/rpi/controller/rpi/af.cpp | 4 +-
src/ipa/rpi/controller/rpi/af.h | 5 +-
src/ipa/rpi/controller/rpi/agc_channel.cpp | 8 +-
src/ipa/rpi/controller/rpi/agc_channel.h | 7 +-
src/ipa/rpi/controller/rpi/awb.cpp | 40 +--
src/ipa/rpi/controller/rpi/awb.h | 23 +-
src/ipa/rpi/controller/rpi/ccm.cpp | 4 +-
src/ipa/rpi/controller/rpi/ccm.h | 5 +-
src/ipa/rpi/controller/rpi/contrast.cpp | 14 +-
src/ipa/rpi/controller/rpi/contrast.h | 5 +-
src/ipa/rpi/controller/rpi/geq.cpp | 5 +-
src/ipa/rpi/controller/rpi/geq.h | 4 +-
src/ipa/rpi/controller/rpi/hdr.cpp | 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..9474fcef 100644
--- a/src/ipa/rpi/controller/rpi/awb.cpp
+++ b/src/ipa/rpi/controller/rpi/awb.cpp
@@ -49,10 +49,10 @@ int AwbPrior::read(const libcamera::YamlObject ¶ms)
return -EINVAL;
lux = *value;
- return prior.read(params["prior"]);
+ return prior.readYaml(params["prior"]);
}
-static int readCtCurve(Pwl &ctR, Pwl &ctB, const libcamera::YamlObject ¶ms)
+static int readCtCurve(ipa::Pwl &ctR, ipa::Pwl &ctB, const libcamera::YamlObject ¶ms)
{
if (params.size() % 3) {
LOG(RPiAwb, Error) << "AwbConfig: incomplete CT curve entry";
@@ -207,7 +207,7 @@ void Awb::initialise()
* them.
*/
if (!config_.ctR.empty() && !config_.ctB.empty()) {
- syncResults_.temperatureK = config_.ctR.domain().clip(4000);
+ syncResults_.temperatureK = config_.ctR.domain().clamp(4000);
syncResults_.gainR = 1.0 / config_.ctR.eval(syncResults_.temperatureK);
syncResults_.gainG = 1.0;
syncResults_.gainB = 1.0 / config_.ctB.eval(syncResults_.temperatureK);
@@ -273,8 +273,8 @@ void Awb::setManualGains(double manualR, double manualB)
syncResults_.gainB = prevSyncResults_.gainB = manualB_;
if (config_.bayes) {
/* Also estimate the best corresponding colour temperature from the curves. */
- double ctR = config_.ctRInverse.eval(config_.ctRInverse.domain().clip(1 / manualR_));
- double ctB = config_.ctBInverse.eval(config_.ctBInverse.domain().clip(1 / manualB_));
+ double ctR = config_.ctRInverse.eval(config_.ctRInverse.domain().clamp(1 / manualR_));
+ double ctB = config_.ctBInverse.eval(config_.ctBInverse.domain().clamp(1 / manualB_));
prevSyncResults_.temperatureK = (ctR + ctB) / 2;
syncResults_.temperatureK = prevSyncResults_.temperatureK;
}
@@ -468,7 +468,7 @@ double Awb::computeDelta2Sum(double gainR, double gainB)
return delta2Sum;
}
-Pwl Awb::interpolatePrior()
+ipa::Pwl Awb::interpolatePrior()
{
/*
* Interpolate the prior log likelihood function for our current lux
@@ -485,7 +485,7 @@ Pwl Awb::interpolatePrior()
idx++;
double lux0 = config_.priors[idx].lux,
lux1 = config_.priors[idx + 1].lux;
- return Pwl::combine(config_.priors[idx].prior,
+ return ipa::Pwl::combine(config_.priors[idx].prior,
config_.priors[idx + 1].prior,
[&](double /*x*/, double y0, double y1) {
return y0 + (y1 - y0) *
@@ -494,15 +494,15 @@ Pwl Awb::interpolatePrior()
}
}
-static double interpolateQuadatric(Pwl::Point const &a, Pwl::Point const &b,
- Pwl::Point const &c)
+static double interpolateQuadatric(PointF const &a, PointF const &b,
+ PointF const &c)
{
/*
* Given 3 points on a curve, find the extremum of the function in that
* interval by fitting a quadratic.
*/
const double eps = 1e-3;
- Pwl::Point ca = c - a, ba = b - a;
+ PointF ca = c - a, ba = b - a;
double denominator = 2 * (ba.y * ca.x - ca.y * ba.x);
if (abs(denominator) > eps) {
double numerator = ba.y * ca.x * ca.x - ca.y * ba.x * ba.x;
@@ -513,7 +513,7 @@ static double interpolateQuadatric(Pwl::Point const &a, Pwl::Point const &b,
return a.y < c.y - eps ? a.x : (c.y < a.y - eps ? c.x : b.x);
}
-double Awb::coarseSearch(Pwl const &prior)
+double Awb::coarseSearch(ipa::Pwl const &prior)
{
points_.clear(); /* assume doesn't deallocate memory */
size_t bestPoint = 0;
@@ -525,14 +525,14 @@ double Awb::coarseSearch(Pwl const &prior)
double b = config_.ctB.eval(t, &spanB);
double gainR = 1 / r, gainB = 1 / b;
double delta2Sum = computeDelta2Sum(gainR, gainB);
- double priorLogLikelihood = prior.eval(prior.domain().clip(t));
+ double priorLogLikelihood = prior.eval(prior.domain().clamp(t));
double finalLogLikelihood = delta2Sum - priorLogLikelihood;
LOG(RPiAwb, Debug)
<< "t: " << t << " gain R " << gainR << " gain B "
<< gainB << " delta2_sum " << delta2Sum
<< " prior " << priorLogLikelihood << " final "
<< finalLogLikelihood;
- points_.push_back(Pwl::Point(t, finalLogLikelihood));
+ points_.push_back(PointF(t, finalLogLikelihood));
if (points_.back().y < points_[bestPoint].y)
bestPoint = points_.size() - 1;
if (t == mode_->ctHi)
@@ -559,7 +559,7 @@ double Awb::coarseSearch(Pwl const &prior)
return t;
}
-void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior)
+void Awb::fineSearch(double &t, double &r, double &b, ipa::Pwl const &prior)
{
int spanR = -1, spanB = -1;
config_.ctR.eval(t, &spanR);
@@ -570,7 +570,7 @@ void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior)
config_.ctR.eval(t - nsteps * step, &spanR);
double bDiff = config_.ctB.eval(t + nsteps * step, &spanB) -
config_.ctB.eval(t - nsteps * step, &spanB);
- Pwl::Point transverse(bDiff, -rDiff);
+ PointF transverse(bDiff, -rDiff);
if (transverse.len2() < 1e-6)
return;
/*
@@ -592,17 +592,17 @@ void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior)
for (int i = -nsteps; i <= nsteps; i++) {
double tTest = t + i * step;
double priorLogLikelihood =
- prior.eval(prior.domain().clip(tTest));
+ prior.eval(prior.domain().clamp(tTest));
double rCurve = config_.ctR.eval(tTest, &spanR);
double bCurve = config_.ctB.eval(tTest, &spanB);
/* x will be distance off the curve, y the log likelihood there */
- Pwl::Point points[maxNumDeltas];
+ PointF points[maxNumDeltas];
int bestPoint = 0;
/* Take some measurements transversely *off* the CT curve. */
for (int j = 0; j < numDeltas; j++) {
points[j].x = -config_.transverseNeg +
(transverseRange * j) / (numDeltas - 1);
- Pwl::Point rbTest = Pwl::Point(rCurve, bCurve) +
+ PointF rbTest = PointF(rCurve, bCurve) +
transverse * points[j].x;
double rTest = rbTest.x, bTest = rbTest.y;
double gainR = 1 / rTest, gainB = 1 / bTest;
@@ -619,7 +619,7 @@ void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior)
* now let's do a quadratic interpolation for the best result.
*/
bestPoint = std::max(1, std::min(bestPoint, numDeltas - 2));
- Pwl::Point rbTest = Pwl::Point(rCurve, bCurve) +
+ PointF rbTest = PointF(rCurve, bCurve) +
transverse * interpolateQuadatric(points[bestPoint - 1],
points[bestPoint],
points[bestPoint + 1]);
@@ -653,7 +653,7 @@ void Awb::awbBayes()
* Get the current prior, and scale according to how many zones are
* valid... not entirely sure about this.
*/
- Pwl prior = interpolatePrior();
+ ipa::Pwl prior = interpolatePrior();
prior *= zones_.size() / (double)(statistics_->awbRegions.numRegions());
prior.map([](double x, double y) {
LOG(RPiAwb, Debug) << "(" << x << "," << y << ")";
diff --git a/src/ipa/rpi/controller/rpi/awb.h b/src/ipa/rpi/controller/rpi/awb.h
index cde6a62f..a4445c1b 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::PointF> 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
More information about the libcamera-devel
mailing list