ipa: rpi: controller: Use libipa's Pwl class

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>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
Acked-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
Paul Elder 2024-03-29 21:44:29 +09:00
parent 95fa5c40ba
commit 9fcc0029ec
22 changed files with 106 additions and 494 deletions

View file

@ -6,8 +6,6 @@
*/
#pragma once
#include "pwl.h"
struct CacStatus {
std::vector<double> lutRx;
std::vector<double> lutRy;

View file

@ -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;
};

View file

@ -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)

View file

@ -1,269 +0,0 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
* piecewise linear functions
*/
#include <cassert>
#include <cmath>
#include <stdexcept>
#include "pwl.h"
using namespace RPiController;
int Pwl::read(const libcamera::YamlObject &params)
{
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");
}

View file

@ -1,127 +0,0 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
* 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 &params);
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 */

View file

@ -139,7 +139,7 @@ int Af::CfgParams::read(const libcamera::YamlObject &params)
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();
}

View file

@ -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 &params);

View file

@ -130,7 +130,7 @@ int AgcConstraint::read(const libcamera::YamlObject &params)
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 &params)
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);
/*

View file

@ -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 &params);
};
@ -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;

View file

@ -49,10 +49,10 @@ int AwbPrior::read(const libcamera::YamlObject &params)
return -EINVAL;
lux = *value;
return prior.read(params["prior"]);
return prior.readYaml(params["prior"]);
}
static int readCtCurve(Pwl &ctR, Pwl &ctB, const libcamera::YamlObject &params)
static int readCtCurve(ipa::Pwl &ctR, ipa::Pwl &ctB, const libcamera::YamlObject &params)
{
if (params.size() % 3) {
LOG(RPiAwb, Error) << "AwbConfig: incomplete CT curve entry";
@ -103,8 +103,8 @@ int AwbConfig::read(const libcamera::YamlObject &params)
if (ret)
return ret;
/* We will want the inverse functions of these too. */
ctRInverse = ctR.inverse();
ctBInverse = ctB.inverse();
ctRInverse = ctR.inverse().first;
ctBInverse = ctB.inverse().first;
}
if (params.contains("priors")) {
@ -207,7 +207,7 @@ void Awb::initialise()
* them.
*/
if (!config_.ctR.empty() && !config_.ctB.empty()) {
syncResults_.temperatureK = config_.ctR.domain().clip(4000);
syncResults_.temperatureK = config_.ctR.domain().clamp(4000);
syncResults_.gainR = 1.0 / config_.ctR.eval(syncResults_.temperatureK);
syncResults_.gainG = 1.0;
syncResults_.gainB = 1.0 / config_.ctB.eval(syncResults_.temperatureK);
@ -273,8 +273,8 @@ void Awb::setManualGains(double manualR, double manualB)
syncResults_.gainB = prevSyncResults_.gainB = manualB_;
if (config_.bayes) {
/* Also estimate the best corresponding colour temperature from the curves. */
double ctR = config_.ctRInverse.eval(config_.ctRInverse.domain().clip(1 / manualR_));
double ctB = config_.ctBInverse.eval(config_.ctBInverse.domain().clip(1 / manualB_));
double ctR = config_.ctRInverse.eval(config_.ctRInverse.domain().clamp(1 / manualR_));
double ctB = config_.ctBInverse.eval(config_.ctBInverse.domain().clamp(1 / manualB_));
prevSyncResults_.temperatureK = (ctR + ctB) / 2;
syncResults_.temperatureK = prevSyncResults_.temperatureK;
}
@ -468,7 +468,7 @@ double Awb::computeDelta2Sum(double gainR, double gainB)
return delta2Sum;
}
Pwl Awb::interpolatePrior()
ipa::Pwl Awb::interpolatePrior()
{
/*
* Interpolate the prior log likelihood function for our current lux
@ -485,7 +485,7 @@ Pwl Awb::interpolatePrior()
idx++;
double lux0 = config_.priors[idx].lux,
lux1 = config_.priors[idx + 1].lux;
return Pwl::combine(config_.priors[idx].prior,
return ipa::Pwl::combine(config_.priors[idx].prior,
config_.priors[idx + 1].prior,
[&](double /*x*/, double y0, double y1) {
return y0 + (y1 - y0) *
@ -494,26 +494,26 @@ Pwl Awb::interpolatePrior()
}
}
static double interpolateQuadatric(Pwl::Point const &a, Pwl::Point const &b,
Pwl::Point const &c)
static double interpolateQuadatric(ipa::Pwl::Point const &a, ipa::Pwl::Point const &b,
ipa::Pwl::Point const &c)
{
/*
* Given 3 points on a curve, find the extremum of the function in that
* interval by fitting a quadratic.
*/
const double eps = 1e-3;
Pwl::Point ca = c - a, ba = b - a;
double denominator = 2 * (ba.y * ca.x - ca.y * ba.x);
ipa::Pwl::Point ca = c - a, ba = b - a;
double denominator = 2 * (ba.y() * ca.x() - ca.y() * ba.x());
if (abs(denominator) > eps) {
double numerator = ba.y * ca.x * ca.x - ca.y * ba.x * ba.x;
double result = numerator / denominator + a.x;
return std::max(a.x, std::min(c.x, result));
double numerator = ba.y() * ca.x() * ca.x() - ca.y() * ba.x() * ba.x();
double result = numerator / denominator + a.x();
return std::max(a.x(), std::min(c.x(), result));
}
/* has degenerated to straight line segment */
return a.y < c.y - eps ? a.x : (c.y < a.y - eps ? c.x : b.x);
return a.y() < c.y() - eps ? a.x() : (c.y() < a.y() - eps ? c.x() : b.x());
}
double Awb::coarseSearch(Pwl const &prior)
double Awb::coarseSearch(ipa::Pwl const &prior)
{
points_.clear(); /* assume doesn't deallocate memory */
size_t bestPoint = 0;
@ -525,22 +525,22 @@ double Awb::coarseSearch(Pwl const &prior)
double b = config_.ctB.eval(t, &spanB);
double gainR = 1 / r, gainB = 1 / b;
double delta2Sum = computeDelta2Sum(gainR, gainB);
double priorLogLikelihood = prior.eval(prior.domain().clip(t));
double priorLogLikelihood = prior.eval(prior.domain().clamp(t));
double finalLogLikelihood = delta2Sum - priorLogLikelihood;
LOG(RPiAwb, Debug)
<< "t: " << t << " gain R " << gainR << " gain B "
<< gainB << " delta2_sum " << delta2Sum
<< " prior " << priorLogLikelihood << " final "
<< finalLogLikelihood;
points_.push_back(Pwl::Point(t, finalLogLikelihood));
if (points_.back().y < points_[bestPoint].y)
points_.push_back(ipa::Pwl::Point({ t, finalLogLikelihood }));
if (points_.back().y() < points_[bestPoint].y())
bestPoint = points_.size() - 1;
if (t == mode_->ctHi)
break;
/* for even steps along the r/b curve scale them by the current t */
t = std::min(t + t / 10 * config_.coarseStep, mode_->ctHi);
}
t = points_[bestPoint].x;
t = points_[bestPoint].x();
LOG(RPiAwb, Debug) << "Coarse search found CT " << t;
/*
* We have the best point of the search, but refine it with a quadratic
@ -559,7 +559,7 @@ double Awb::coarseSearch(Pwl const &prior)
return t;
}
void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior)
void Awb::fineSearch(double &t, double &r, double &b, ipa::Pwl const &prior)
{
int spanR = -1, spanB = -1;
config_.ctR.eval(t, &spanR);
@ -570,14 +570,14 @@ 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);
if (transverse.len2() < 1e-6)
ipa::Pwl::Point transverse({ bDiff, -rDiff });
if (transverse.length2() < 1e-6)
return;
/*
* unit vector orthogonal to the b vs. r function (pointing outwards
* with r and b increasing)
*/
transverse = transverse / transverse.len();
transverse = transverse / transverse.length();
double bestLogLikelihood = 0, bestT = 0, bestR = 0, bestB = 0;
double transverseRange = config_.transverseNeg + config_.transversePos;
const int maxNumDeltas = 12;
@ -592,26 +592,26 @@ void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior)
for (int i = -nsteps; i <= nsteps; i++) {
double tTest = t + i * step;
double priorLogLikelihood =
prior.eval(prior.domain().clip(tTest));
prior.eval(prior.domain().clamp(tTest));
double rCurve = config_.ctR.eval(tTest, &spanR);
double bCurve = config_.ctB.eval(tTest, &spanB);
/* x will be distance off the curve, y the log likelihood there */
Pwl::Point points[maxNumDeltas];
ipa::Pwl::Point 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 +
points[j][0] = -config_.transverseNeg +
(transverseRange * j) / (numDeltas - 1);
Pwl::Point rbTest = Pwl::Point(rCurve, bCurve) +
transverse * points[j].x;
double rTest = rbTest.x, bTest = rbTest.y;
ipa::Pwl::Point rbTest = ipa::Pwl::Point({ rCurve, bCurve }) +
transverse * points[j].x();
double rTest = rbTest.x(), bTest = rbTest.y();
double gainR = 1 / rTest, gainB = 1 / bTest;
double delta2Sum = computeDelta2Sum(gainR, gainB);
points[j].y = delta2Sum - priorLogLikelihood;
points[j][1] = delta2Sum - priorLogLikelihood;
LOG(RPiAwb, Debug)
<< "At t " << tTest << " r " << rTest << " b "
<< bTest << ": " << points[j].y;
if (points[j].y < points[bestPoint].y)
<< bTest << ": " << points[j].y();
if (points[j].y() < points[bestPoint].y())
bestPoint = j;
}
/*
@ -619,11 +619,11 @@ void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior)
* now let's do a quadratic interpolation for the best result.
*/
bestPoint = std::max(1, std::min(bestPoint, numDeltas - 2));
Pwl::Point rbTest = Pwl::Point(rCurve, bCurve) +
ipa::Pwl::Point rbTest = ipa::Pwl::Point({ rCurve, bCurve }) +
transverse * interpolateQuadatric(points[bestPoint - 1],
points[bestPoint],
points[bestPoint + 1]);
double rTest = rbTest.x, bTest = rbTest.y;
double rTest = rbTest.x(), bTest = rbTest.y();
double gainR = 1 / rTest, gainB = 1 / bTest;
double delta2Sum = computeDelta2Sum(gainR, gainB);
double finalLogLikelihood = delta2Sum - priorLogLikelihood;
@ -653,7 +653,7 @@ void Awb::awbBayes()
* Get the current prior, and scale according to how many zones are
* valid... not entirely sure about this.
*/
Pwl prior = interpolatePrior();
ipa::Pwl prior = interpolatePrior();
prior *= zones_.size() / (double)(statistics_->awbRegions.numRegions());
prior.map([](double x, double y) {
LOG(RPiAwb, Debug) << "(" << x << "," << y << ")";

View file

@ -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 &params);
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::ipa::Pwl::Point> points_;
/* manual r setting */
double manualR_;
/* manual b setting */

View file

@ -71,7 +71,7 @@ int Ccm::read(const libcamera::YamlObject &params)
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++)

View file

@ -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

View file

@ -53,7 +53,7 @@ int Contrast::read(const libcamera::YamlObject &params)
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,
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);

View file

@ -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

View file

@ -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 &params)
}
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;

View file

@ -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

View file

@ -42,7 +42,7 @@ void HdrConfig::read(const libcamera::YamlObject &params, const std::string &mod
/* Lens shading related parameters. */
if (params.contains("spatial_gain_curve")) {
spatialGainCurve.read(params["spatial_gain_curve"]);
spatialGainCurve.readYaml(params["spatial_gain_curve"]);
} else if (params.contains("spatial_gain")) {
double spatialGain = params["spatial_gain"].get<double>(2.0);
spatialGainCurve.append(0.0, spatialGain);
@ -66,7 +66,7 @@ void HdrConfig::read(const libcamera::YamlObject &params, 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"]);
speed = params["speed"].get<double>(1.0);
if (params.contains("hi_quantile_targets")) {
hiQuantileTargets = params["hi_quantile_targets"].getList<double>().value();
@ -212,7 +212,7 @@ bool Hdr::updateTonemap([[maybe_unused]] StatisticsPtr &stats, HdrConfig &config
/* When there's a change of HDR mode we start over with a new tonemap curve. */
if (delayedStatus_.mode != previousMode_) {
previousMode_ = delayedStatus_.mode;
tonemap_ = Pwl();
tonemap_ = ipa::Pwl();
}
/* No tonemapping. No need to output a tonemap.status. */
@ -275,7 +275,7 @@ bool Hdr::updateTonemap([[maybe_unused]] StatisticsPtr &stats, HdrConfig &config
double power = std::clamp(min_power, config.powerMin, config.powerMax);
/* Generate the tonemap, including the contrast adjustment factors. */
Pwl tonemap;
libcamera::ipa::Pwl tonemap;
tonemap.append(0, 0);
for (unsigned int i = 0; i <= 6; i++) {
double x = 1 << (i + 9); /* x loops from 512 to 32768 inclusive */

View file

@ -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 spatialGainCurve; /* Brightness to gain curve for different image regions. */
libcamera::ipa::Pwl spatialGainCurve; /* Brightness to gain curve for different image regions. */
unsigned int diffusion; /* How much to diffuse the gain spatially. */
/* Tonemap related parameters. */
@ -35,7 +36,7 @@ struct HdrConfig {
double detailSlope;
double iirStrength;
double strength;
Pwl tonemap;
libcamera::ipa::Pwl tonemap;
/* These relate to adaptive tonemap calculation. */
double speed;
std::vector<double> hiQuantileTargets; /* quantiles to check for unsaturated images */
@ -75,7 +76,7 @@ private:
HdrStatus status_; /* track the current HDR mode and channel */
HdrStatus delayedStatus_; /* track the delayed HDR mode and channel */
std::string previousMode_;
Pwl tonemap_;
libcamera::ipa::Pwl tonemap_;
libcamera::Size regions_; /* stats regions */
unsigned int numRegions_; /* total number of stats regions */
std::vector<double> gains_[2];

View file

@ -33,7 +33,7 @@ int Tonemap::read(const libcamera::YamlObject &params)
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;
}

View file

@ -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

View file

@ -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;
};