mirror of
https://git.libcamera.org/libcamera/libcamera.git
synced 2025-07-23 16:45:07 +03:00
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:
parent
95fa5c40ba
commit
9fcc0029ec
22 changed files with 106 additions and 494 deletions
|
@ -6,8 +6,6 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "pwl.h"
|
|
||||||
|
|
||||||
struct CacStatus {
|
struct CacStatus {
|
||||||
std::vector<double> lutRx;
|
std::vector<double> lutRx;
|
||||||
std::vector<double> lutRy;
|
std::vector<double> lutRy;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "pwl.h"
|
#include "libipa/pwl.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The "contrast" algorithm creates a gamma curve, optionally doing a little bit
|
* The "contrast" algorithm creates a gamma curve, optionally doing a little bit
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct ContrastStatus {
|
struct ContrastStatus {
|
||||||
RPiController::Pwl gammaCurve;
|
libcamera::ipa::Pwl gammaCurve;
|
||||||
double brightness;
|
double brightness;
|
||||||
double contrast;
|
double contrast;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,6 @@ rpi_ipa_controller_sources = files([
|
||||||
'controller.cpp',
|
'controller.cpp',
|
||||||
'device_status.cpp',
|
'device_status.cpp',
|
||||||
'histogram.cpp',
|
'histogram.cpp',
|
||||||
'pwl.cpp',
|
|
||||||
'rpi/af.cpp',
|
'rpi/af.cpp',
|
||||||
'rpi/agc.cpp',
|
'rpi/agc.cpp',
|
||||||
'rpi/agc_channel.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,
|
rpi_ipa_controller_lib = static_library('rpi_ipa_controller', rpi_ipa_controller_sources,
|
||||||
|
include_directories : libipa_includes,
|
||||||
dependencies : rpi_ipa_controller_deps)
|
dependencies : rpi_ipa_controller_deps)
|
||||||
|
|
|
@ -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 ¶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");
|
|
||||||
}
|
|
|
@ -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 ¶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 */
|
|
|
@ -139,7 +139,7 @@ int Af::CfgParams::read(const libcamera::YamlObject ¶ms)
|
||||||
readNumber<uint32_t>(skipFrames, params, "skip_frames");
|
readNumber<uint32_t>(skipFrames, params, "skip_frames");
|
||||||
|
|
||||||
if (params.contains("map"))
|
if (params.contains("map"))
|
||||||
map.read(params["map"]);
|
map.readYaml(params["map"]);
|
||||||
else
|
else
|
||||||
LOG(RPiAf, Warning) << "No map defined";
|
LOG(RPiAf, Warning) << "No map defined";
|
||||||
|
|
||||||
|
@ -721,7 +721,7 @@ bool Af::setLensPosition(double dioptres, int *hwpos)
|
||||||
|
|
||||||
if (mode_ == AfModeManual) {
|
if (mode_ == AfModeManual) {
|
||||||
LOG(RPiAf, Debug) << "setLensPosition: " << dioptres;
|
LOG(RPiAf, Debug) << "setLensPosition: " << dioptres;
|
||||||
ftarget_ = cfg_.map.domain().clip(dioptres);
|
ftarget_ = cfg_.map.domain().clamp(dioptres);
|
||||||
changed = !(initted_ && fsmooth_ == ftarget_);
|
changed = !(initted_ && fsmooth_ == ftarget_);
|
||||||
updateLensPosition();
|
updateLensPosition();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
#include "../af_algorithm.h"
|
#include "../af_algorithm.h"
|
||||||
#include "../af_status.h"
|
#include "../af_status.h"
|
||||||
#include "../pdaf_data.h"
|
#include "../pdaf_data.h"
|
||||||
#include "../pwl.h"
|
|
||||||
|
#include "libipa/pwl.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This algorithm implements a hybrid of CDAF and PDAF, favouring PDAF.
|
* 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 confThresh; /* PDAF confidence cell min (sensor-specific) */
|
||||||
uint32_t confClip; /* PDAF confidence cell max (sensor-specific) */
|
uint32_t confClip; /* PDAF confidence cell max (sensor-specific) */
|
||||||
uint32_t skipFrames; /* frames to skip at start or modeswitch */
|
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();
|
CfgParams();
|
||||||
int read(const libcamera::YamlObject ¶ms);
|
int read(const libcamera::YamlObject ¶ms);
|
||||||
|
|
|
@ -130,7 +130,7 @@ int AgcConstraint::read(const libcamera::YamlObject ¶ms)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
qHi = *value;
|
qHi = *value;
|
||||||
|
|
||||||
return yTarget.read(params["y_target"]);
|
return yTarget.readYaml(params["y_target"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::tuple<int, AgcConstraintMode>
|
static std::tuple<int, AgcConstraintMode>
|
||||||
|
@ -237,7 +237,7 @@ int AgcConfig::read(const libcamera::YamlObject ¶ms)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = yTarget.read(params["y_target"]);
|
ret = yTarget.readYaml(params["y_target"]);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -715,7 +715,7 @@ static constexpr double EvGainYTargetLimit = 0.9;
|
||||||
static double constraintComputeGain(AgcConstraint &c, const Histogram &h, double lux,
|
static double constraintComputeGain(AgcConstraint &c, const Histogram &h, double lux,
|
||||||
double evGain, double &targetY)
|
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);
|
targetY = std::min(EvGainYTargetLimit, targetY * evGain);
|
||||||
double iqm = h.interQuantileMean(c.qLo, c.qHi);
|
double iqm = h.interQuantileMean(c.qLo, c.qHi);
|
||||||
return (targetY * h.bins()) / iqm;
|
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
|
* The initial gain and target_Y come from some of the regions. After
|
||||||
* that we consider the histogram constraints.
|
* 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);
|
targetY = std::min(EvGainYTargetLimit, targetY * evGain);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -12,10 +12,11 @@
|
||||||
|
|
||||||
#include <libcamera/base/utils.h>
|
#include <libcamera/base/utils.h>
|
||||||
|
|
||||||
|
#include <libipa/pwl.h>
|
||||||
|
|
||||||
#include "../agc_status.h"
|
#include "../agc_status.h"
|
||||||
#include "../awb_status.h"
|
#include "../awb_status.h"
|
||||||
#include "../controller.h"
|
#include "../controller.h"
|
||||||
#include "../pwl.h"
|
|
||||||
|
|
||||||
/* This is our implementation of AGC. */
|
/* This is our implementation of AGC. */
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ struct AgcConstraint {
|
||||||
Bound bound;
|
Bound bound;
|
||||||
double qLo;
|
double qLo;
|
||||||
double qHi;
|
double qHi;
|
||||||
Pwl yTarget;
|
libcamera::ipa::Pwl yTarget;
|
||||||
int read(const libcamera::YamlObject ¶ms);
|
int read(const libcamera::YamlObject ¶ms);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@ struct AgcConfig {
|
||||||
std::map<std::string, AgcExposureMode> exposureModes;
|
std::map<std::string, AgcExposureMode> exposureModes;
|
||||||
std::map<std::string, AgcConstraintMode> constraintModes;
|
std::map<std::string, AgcConstraintMode> constraintModes;
|
||||||
std::vector<AgcChannelConstraint> channelConstraints;
|
std::vector<AgcChannelConstraint> channelConstraints;
|
||||||
Pwl yTarget;
|
libcamera::ipa::Pwl yTarget;
|
||||||
double speed;
|
double speed;
|
||||||
uint16_t startupFrames;
|
uint16_t startupFrames;
|
||||||
unsigned int convergenceFrames;
|
unsigned int convergenceFrames;
|
||||||
|
|
|
@ -49,10 +49,10 @@ int AwbPrior::read(const libcamera::YamlObject ¶ms)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
lux = *value;
|
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) {
|
if (params.size() % 3) {
|
||||||
LOG(RPiAwb, Error) << "AwbConfig: incomplete CT curve entry";
|
LOG(RPiAwb, Error) << "AwbConfig: incomplete CT curve entry";
|
||||||
|
@ -103,8 +103,8 @@ int AwbConfig::read(const libcamera::YamlObject ¶ms)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
/* We will want the inverse functions of these too. */
|
/* We will want the inverse functions of these too. */
|
||||||
ctRInverse = ctR.inverse();
|
ctRInverse = ctR.inverse().first;
|
||||||
ctBInverse = ctB.inverse();
|
ctBInverse = ctB.inverse().first;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.contains("priors")) {
|
if (params.contains("priors")) {
|
||||||
|
@ -207,7 +207,7 @@ void Awb::initialise()
|
||||||
* them.
|
* them.
|
||||||
*/
|
*/
|
||||||
if (!config_.ctR.empty() && !config_.ctB.empty()) {
|
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_.gainR = 1.0 / config_.ctR.eval(syncResults_.temperatureK);
|
||||||
syncResults_.gainG = 1.0;
|
syncResults_.gainG = 1.0;
|
||||||
syncResults_.gainB = 1.0 / config_.ctB.eval(syncResults_.temperatureK);
|
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_;
|
syncResults_.gainB = prevSyncResults_.gainB = manualB_;
|
||||||
if (config_.bayes) {
|
if (config_.bayes) {
|
||||||
/* Also estimate the best corresponding colour temperature from the curves. */
|
/* Also estimate the best corresponding colour temperature from the curves. */
|
||||||
double ctR = config_.ctRInverse.eval(config_.ctRInverse.domain().clip(1 / manualR_));
|
double ctR = config_.ctRInverse.eval(config_.ctRInverse.domain().clamp(1 / manualR_));
|
||||||
double ctB = config_.ctBInverse.eval(config_.ctBInverse.domain().clip(1 / manualB_));
|
double ctB = config_.ctBInverse.eval(config_.ctBInverse.domain().clamp(1 / manualB_));
|
||||||
prevSyncResults_.temperatureK = (ctR + ctB) / 2;
|
prevSyncResults_.temperatureK = (ctR + ctB) / 2;
|
||||||
syncResults_.temperatureK = prevSyncResults_.temperatureK;
|
syncResults_.temperatureK = prevSyncResults_.temperatureK;
|
||||||
}
|
}
|
||||||
|
@ -468,7 +468,7 @@ double Awb::computeDelta2Sum(double gainR, double gainB)
|
||||||
return delta2Sum;
|
return delta2Sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pwl Awb::interpolatePrior()
|
ipa::Pwl Awb::interpolatePrior()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Interpolate the prior log likelihood function for our current lux
|
* Interpolate the prior log likelihood function for our current lux
|
||||||
|
@ -485,7 +485,7 @@ Pwl Awb::interpolatePrior()
|
||||||
idx++;
|
idx++;
|
||||||
double lux0 = config_.priors[idx].lux,
|
double lux0 = config_.priors[idx].lux,
|
||||||
lux1 = config_.priors[idx + 1].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,
|
config_.priors[idx + 1].prior,
|
||||||
[&](double /*x*/, double y0, double y1) {
|
[&](double /*x*/, double y0, double y1) {
|
||||||
return y0 + (y1 - y0) *
|
return y0 + (y1 - y0) *
|
||||||
|
@ -494,26 +494,26 @@ Pwl Awb::interpolatePrior()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static double interpolateQuadatric(Pwl::Point const &a, Pwl::Point const &b,
|
static double interpolateQuadatric(ipa::Pwl::Point const &a, ipa::Pwl::Point const &b,
|
||||||
Pwl::Point const &c)
|
ipa::Pwl::Point const &c)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Given 3 points on a curve, find the extremum of the function in that
|
* Given 3 points on a curve, find the extremum of the function in that
|
||||||
* interval by fitting a quadratic.
|
* interval by fitting a quadratic.
|
||||||
*/
|
*/
|
||||||
const double eps = 1e-3;
|
const double eps = 1e-3;
|
||||||
Pwl::Point ca = c - a, ba = b - a;
|
ipa::Pwl::Point ca = c - a, ba = b - a;
|
||||||
double denominator = 2 * (ba.y * ca.x - ca.y * ba.x);
|
double denominator = 2 * (ba.y() * ca.x() - ca.y() * ba.x());
|
||||||
if (abs(denominator) > eps) {
|
if (abs(denominator) > eps) {
|
||||||
double numerator = ba.y * ca.x * ca.x - ca.y * ba.x * ba.x;
|
double numerator = ba.y() * ca.x() * ca.x() - ca.y() * ba.x() * ba.x();
|
||||||
double result = numerator / denominator + a.x;
|
double result = numerator / denominator + a.x();
|
||||||
return std::max(a.x, std::min(c.x, result));
|
return std::max(a.x(), std::min(c.x(), result));
|
||||||
}
|
}
|
||||||
/* has degenerated to straight line segment */
|
/* 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 */
|
points_.clear(); /* assume doesn't deallocate memory */
|
||||||
size_t bestPoint = 0;
|
size_t bestPoint = 0;
|
||||||
|
@ -525,22 +525,22 @@ double Awb::coarseSearch(Pwl const &prior)
|
||||||
double b = config_.ctB.eval(t, &spanB);
|
double b = config_.ctB.eval(t, &spanB);
|
||||||
double gainR = 1 / r, gainB = 1 / b;
|
double gainR = 1 / r, gainB = 1 / b;
|
||||||
double delta2Sum = computeDelta2Sum(gainR, gainB);
|
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;
|
double finalLogLikelihood = delta2Sum - priorLogLikelihood;
|
||||||
LOG(RPiAwb, Debug)
|
LOG(RPiAwb, Debug)
|
||||||
<< "t: " << t << " gain R " << gainR << " gain B "
|
<< "t: " << t << " gain R " << gainR << " gain B "
|
||||||
<< gainB << " delta2_sum " << delta2Sum
|
<< gainB << " delta2_sum " << delta2Sum
|
||||||
<< " prior " << priorLogLikelihood << " final "
|
<< " prior " << priorLogLikelihood << " final "
|
||||||
<< finalLogLikelihood;
|
<< finalLogLikelihood;
|
||||||
points_.push_back(Pwl::Point(t, finalLogLikelihood));
|
points_.push_back(ipa::Pwl::Point({ t, finalLogLikelihood }));
|
||||||
if (points_.back().y < points_[bestPoint].y)
|
if (points_.back().y() < points_[bestPoint].y())
|
||||||
bestPoint = points_.size() - 1;
|
bestPoint = points_.size() - 1;
|
||||||
if (t == mode_->ctHi)
|
if (t == mode_->ctHi)
|
||||||
break;
|
break;
|
||||||
/* for even steps along the r/b curve scale them by the current t */
|
/* 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 = 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;
|
LOG(RPiAwb, Debug) << "Coarse search found CT " << t;
|
||||||
/*
|
/*
|
||||||
* We have the best point of the search, but refine it with a quadratic
|
* 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;
|
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;
|
int spanR = -1, spanB = -1;
|
||||||
config_.ctR.eval(t, &spanR);
|
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);
|
config_.ctR.eval(t - nsteps * step, &spanR);
|
||||||
double bDiff = config_.ctB.eval(t + nsteps * step, &spanB) -
|
double bDiff = config_.ctB.eval(t + nsteps * step, &spanB) -
|
||||||
config_.ctB.eval(t - nsteps * step, &spanB);
|
config_.ctB.eval(t - nsteps * step, &spanB);
|
||||||
Pwl::Point transverse(bDiff, -rDiff);
|
ipa::Pwl::Point transverse({ bDiff, -rDiff });
|
||||||
if (transverse.len2() < 1e-6)
|
if (transverse.length2() < 1e-6)
|
||||||
return;
|
return;
|
||||||
/*
|
/*
|
||||||
* unit vector orthogonal to the b vs. r function (pointing outwards
|
* unit vector orthogonal to the b vs. r function (pointing outwards
|
||||||
* with r and b increasing)
|
* with r and b increasing)
|
||||||
*/
|
*/
|
||||||
transverse = transverse / transverse.len();
|
transverse = transverse / transverse.length();
|
||||||
double bestLogLikelihood = 0, bestT = 0, bestR = 0, bestB = 0;
|
double bestLogLikelihood = 0, bestT = 0, bestR = 0, bestB = 0;
|
||||||
double transverseRange = config_.transverseNeg + config_.transversePos;
|
double transverseRange = config_.transverseNeg + config_.transversePos;
|
||||||
const int maxNumDeltas = 12;
|
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++) {
|
for (int i = -nsteps; i <= nsteps; i++) {
|
||||||
double tTest = t + i * step;
|
double tTest = t + i * step;
|
||||||
double priorLogLikelihood =
|
double priorLogLikelihood =
|
||||||
prior.eval(prior.domain().clip(tTest));
|
prior.eval(prior.domain().clamp(tTest));
|
||||||
double rCurve = config_.ctR.eval(tTest, &spanR);
|
double rCurve = config_.ctR.eval(tTest, &spanR);
|
||||||
double bCurve = config_.ctB.eval(tTest, &spanB);
|
double bCurve = config_.ctB.eval(tTest, &spanB);
|
||||||
/* x will be distance off the curve, y the log likelihood there */
|
/* x will be distance off the curve, y the log likelihood there */
|
||||||
Pwl::Point points[maxNumDeltas];
|
ipa::Pwl::Point points[maxNumDeltas];
|
||||||
int bestPoint = 0;
|
int bestPoint = 0;
|
||||||
/* Take some measurements transversely *off* the CT curve. */
|
/* Take some measurements transversely *off* the CT curve. */
|
||||||
for (int j = 0; j < numDeltas; j++) {
|
for (int j = 0; j < numDeltas; j++) {
|
||||||
points[j].x = -config_.transverseNeg +
|
points[j][0] = -config_.transverseNeg +
|
||||||
(transverseRange * j) / (numDeltas - 1);
|
(transverseRange * j) / (numDeltas - 1);
|
||||||
Pwl::Point rbTest = Pwl::Point(rCurve, bCurve) +
|
ipa::Pwl::Point rbTest = ipa::Pwl::Point({ rCurve, bCurve }) +
|
||||||
transverse * points[j].x;
|
transverse * points[j].x();
|
||||||
double rTest = rbTest.x, bTest = rbTest.y;
|
double rTest = rbTest.x(), bTest = rbTest.y();
|
||||||
double gainR = 1 / rTest, gainB = 1 / bTest;
|
double gainR = 1 / rTest, gainB = 1 / bTest;
|
||||||
double delta2Sum = computeDelta2Sum(gainR, gainB);
|
double delta2Sum = computeDelta2Sum(gainR, gainB);
|
||||||
points[j].y = delta2Sum - priorLogLikelihood;
|
points[j][1] = delta2Sum - priorLogLikelihood;
|
||||||
LOG(RPiAwb, Debug)
|
LOG(RPiAwb, Debug)
|
||||||
<< "At t " << tTest << " r " << rTest << " b "
|
<< "At t " << tTest << " r " << rTest << " b "
|
||||||
<< bTest << ": " << points[j].y;
|
<< bTest << ": " << points[j].y();
|
||||||
if (points[j].y < points[bestPoint].y)
|
if (points[j].y() < points[bestPoint].y())
|
||||||
bestPoint = j;
|
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.
|
* now let's do a quadratic interpolation for the best result.
|
||||||
*/
|
*/
|
||||||
bestPoint = std::max(1, std::min(bestPoint, numDeltas - 2));
|
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],
|
transverse * interpolateQuadatric(points[bestPoint - 1],
|
||||||
points[bestPoint],
|
points[bestPoint],
|
||||||
points[bestPoint + 1]);
|
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 gainR = 1 / rTest, gainB = 1 / bTest;
|
||||||
double delta2Sum = computeDelta2Sum(gainR, gainB);
|
double delta2Sum = computeDelta2Sum(gainR, gainB);
|
||||||
double finalLogLikelihood = delta2Sum - priorLogLikelihood;
|
double finalLogLikelihood = delta2Sum - priorLogLikelihood;
|
||||||
|
@ -653,7 +653,7 @@ void Awb::awbBayes()
|
||||||
* Get the current prior, and scale according to how many zones are
|
* Get the current prior, and scale according to how many zones are
|
||||||
* valid... not entirely sure about this.
|
* valid... not entirely sure about this.
|
||||||
*/
|
*/
|
||||||
Pwl prior = interpolatePrior();
|
ipa::Pwl prior = interpolatePrior();
|
||||||
prior *= zones_.size() / (double)(statistics_->awbRegions.numRegions());
|
prior *= zones_.size() / (double)(statistics_->awbRegions.numRegions());
|
||||||
prior.map([](double x, double y) {
|
prior.map([](double x, double y) {
|
||||||
LOG(RPiAwb, Debug) << "(" << x << "," << y << ")";
|
LOG(RPiAwb, Debug) << "(" << x << "," << y << ")";
|
||||||
|
|
|
@ -10,11 +10,14 @@
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
#include <libcamera/geometry.h>
|
||||||
|
|
||||||
#include "../awb_algorithm.h"
|
#include "../awb_algorithm.h"
|
||||||
#include "../pwl.h"
|
|
||||||
#include "../awb_status.h"
|
#include "../awb_status.h"
|
||||||
#include "../statistics.h"
|
#include "../statistics.h"
|
||||||
|
|
||||||
|
#include "libipa/pwl.h"
|
||||||
|
|
||||||
namespace RPiController {
|
namespace RPiController {
|
||||||
|
|
||||||
/* Control algorithm to perform AWB calculations. */
|
/* Control algorithm to perform AWB calculations. */
|
||||||
|
@ -28,7 +31,7 @@ struct AwbMode {
|
||||||
struct AwbPrior {
|
struct AwbPrior {
|
||||||
int read(const libcamera::YamlObject ¶ms);
|
int read(const libcamera::YamlObject ¶ms);
|
||||||
double lux; /* lux level */
|
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 {
|
struct AwbConfig {
|
||||||
|
@ -41,10 +44,10 @@ struct AwbConfig {
|
||||||
unsigned int convergenceFrames; /* approx number of frames to converge */
|
unsigned int convergenceFrames; /* approx number of frames to converge */
|
||||||
double speed; /* IIR filter speed applied to algorithm results */
|
double speed; /* IIR filter speed applied to algorithm results */
|
||||||
bool fast; /* "fast" mode uses a 16x16 rather than 32x32 grid */
|
bool fast; /* "fast" mode uses a 16x16 rather than 32x32 grid */
|
||||||
Pwl ctR; /* function maps CT to r (= R/G) */
|
libcamera::ipa::Pwl ctR; /* function maps CT to r (= R/G) */
|
||||||
Pwl ctB; /* function maps CT to b (= B/G) */
|
libcamera::ipa::Pwl ctB; /* function maps CT to b (= B/G) */
|
||||||
Pwl ctRInverse; /* inverse of ctR */
|
libcamera::ipa::Pwl ctRInverse; /* inverse of ctR */
|
||||||
Pwl ctBInverse; /* inverse of ctB */
|
libcamera::ipa::Pwl ctBInverse; /* inverse of ctB */
|
||||||
/* table of illuminant priors at different lux levels */
|
/* table of illuminant priors at different lux levels */
|
||||||
std::vector<AwbPrior> priors;
|
std::vector<AwbPrior> priors;
|
||||||
/* AWB "modes" (determines the search range) */
|
/* AWB "modes" (determines the search range) */
|
||||||
|
@ -161,11 +164,11 @@ private:
|
||||||
void awbGrey();
|
void awbGrey();
|
||||||
void prepareStats();
|
void prepareStats();
|
||||||
double computeDelta2Sum(double gainR, double gainB);
|
double computeDelta2Sum(double gainR, double gainB);
|
||||||
Pwl interpolatePrior();
|
libcamera::ipa::Pwl interpolatePrior();
|
||||||
double coarseSearch(Pwl const &prior);
|
double coarseSearch(libcamera::ipa::Pwl const &prior);
|
||||||
void fineSearch(double &t, double &r, double &b, Pwl const &prior);
|
void fineSearch(double &t, double &r, double &b, libcamera::ipa::Pwl const &prior);
|
||||||
std::vector<RGB> zones_;
|
std::vector<RGB> zones_;
|
||||||
std::vector<Pwl::Point> points_;
|
std::vector<libcamera::ipa::Pwl::Point> points_;
|
||||||
/* manual r setting */
|
/* manual r setting */
|
||||||
double manualR_;
|
double manualR_;
|
||||||
/* manual b setting */
|
/* manual b setting */
|
||||||
|
|
|
@ -71,7 +71,7 @@ int Ccm::read(const libcamera::YamlObject ¶ms)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (params.contains("saturation")) {
|
if (params.contains("saturation")) {
|
||||||
ret = config_.saturation.read(params["saturation"]);
|
ret = config_.saturation.readYaml(params["saturation"]);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,7 @@ void Ccm::prepare(Metadata *imageMetadata)
|
||||||
ccmStatus.saturation = saturation;
|
ccmStatus.saturation = saturation;
|
||||||
if (!config_.saturation.empty())
|
if (!config_.saturation.empty())
|
||||||
saturation *= config_.saturation.eval(
|
saturation *= config_.saturation.eval(
|
||||||
config_.saturation.domain().clip(lux.lux));
|
config_.saturation.domain().clamp(lux.lux));
|
||||||
ccm = applySaturation(ccm, saturation);
|
ccm = applySaturation(ccm, saturation);
|
||||||
for (int j = 0; j < 3; j++)
|
for (int j = 0; j < 3; j++)
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
|
|
|
@ -8,8 +8,9 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <libipa/pwl.h>
|
||||||
|
|
||||||
#include "../ccm_algorithm.h"
|
#include "../ccm_algorithm.h"
|
||||||
#include "../pwl.h"
|
|
||||||
|
|
||||||
namespace RPiController {
|
namespace RPiController {
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ struct CtCcm {
|
||||||
|
|
||||||
struct CcmConfig {
|
struct CcmConfig {
|
||||||
std::vector<CtCcm> ccms;
|
std::vector<CtCcm> ccms;
|
||||||
Pwl saturation;
|
libcamera::ipa::Pwl saturation;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Ccm : public CcmAlgorithm
|
class Ccm : public CcmAlgorithm
|
||||||
|
|
|
@ -53,7 +53,7 @@ int Contrast::read(const libcamera::YamlObject ¶ms)
|
||||||
config_.hiHistogram = params["hi_histogram"].get<double>(0.95);
|
config_.hiHistogram = params["hi_histogram"].get<double>(0.95);
|
||||||
config_.hiLevel = params["hi_level"].get<double>(0.95);
|
config_.hiLevel = params["hi_level"].get<double>(0.95);
|
||||||
config_.hiMax = params["hi_max"].get<double>(2000);
|
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)
|
void Contrast::setBrightness(double brightness)
|
||||||
|
@ -92,10 +92,10 @@ void Contrast::prepare(Metadata *imageMetadata)
|
||||||
imageMetadata->set("contrast.status", status_);
|
imageMetadata->set("contrast.status", status_);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pwl computeStretchCurve(Histogram const &histogram,
|
ipa::Pwl computeStretchCurve(Histogram const &histogram,
|
||||||
ContrastConfig const &config)
|
ContrastConfig const &config)
|
||||||
{
|
{
|
||||||
Pwl enhance;
|
ipa::Pwl enhance;
|
||||||
enhance.append(0, 0);
|
enhance.append(0, 0);
|
||||||
/*
|
/*
|
||||||
* If the start of the histogram is rather empty, try to pull it down a
|
* 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;
|
return enhance;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pwl applyManualContrast(Pwl const &gammaCurve, double brightness,
|
ipa::Pwl applyManualContrast(ipa::Pwl const &gammaCurve, double brightness,
|
||||||
double contrast)
|
double contrast)
|
||||||
{
|
{
|
||||||
Pwl newGammaCurve;
|
ipa::Pwl newGammaCurve;
|
||||||
LOG(RPiContrast, Debug)
|
LOG(RPiContrast, Debug)
|
||||||
<< "Manual brightness " << brightness << " contrast " << contrast;
|
<< "Manual brightness " << brightness << " contrast " << contrast;
|
||||||
gammaCurve.map([&](double x, double y) {
|
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
|
* ways: 1. Adjust the gamma curve so as to pull the start of the
|
||||||
* histogram down, and possibly push the end up.
|
* histogram down, and possibly push the end up.
|
||||||
*/
|
*/
|
||||||
Pwl gammaCurve = config_.gammaCurve;
|
ipa::Pwl gammaCurve = config_.gammaCurve;
|
||||||
if (ceEnable_) {
|
if (ceEnable_) {
|
||||||
if (config_.loMax != 0 || config_.hiMax != 0)
|
if (config_.loMax != 0 || config_.hiMax != 0)
|
||||||
gammaCurve = computeStretchCurve(histogram, config_).compose(gammaCurve);
|
gammaCurve = computeStretchCurve(histogram, config_).compose(gammaCurve);
|
||||||
|
|
|
@ -8,8 +8,9 @@
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <libipa/pwl.h>
|
||||||
|
|
||||||
#include "../contrast_algorithm.h"
|
#include "../contrast_algorithm.h"
|
||||||
#include "../pwl.h"
|
|
||||||
|
|
||||||
namespace RPiController {
|
namespace RPiController {
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ struct ContrastConfig {
|
||||||
double hiHistogram;
|
double hiHistogram;
|
||||||
double hiLevel;
|
double hiLevel;
|
||||||
double hiMax;
|
double hiMax;
|
||||||
Pwl gammaCurve;
|
libcamera::ipa::Pwl gammaCurve;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Contrast : public ContrastAlgorithm
|
class Contrast : public ContrastAlgorithm
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
#include "../device_status.h"
|
#include "../device_status.h"
|
||||||
#include "../lux_status.h"
|
#include "../lux_status.h"
|
||||||
#include "../pwl.h"
|
|
||||||
|
|
||||||
#include "geq.h"
|
#include "geq.h"
|
||||||
|
|
||||||
|
@ -45,7 +44,7 @@ int Geq::read(const libcamera::YamlObject ¶ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.contains("strength")) {
|
if (params.contains("strength")) {
|
||||||
int ret = config_.strength.read(params["strength"]);
|
int ret = config_.strength.readYaml(params["strength"]);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -67,7 +66,7 @@ void Geq::prepare(Metadata *imageMetadata)
|
||||||
GeqStatus geqStatus = {};
|
GeqStatus geqStatus = {};
|
||||||
double strength = config_.strength.empty()
|
double strength = config_.strength.empty()
|
||||||
? 1.0
|
? 1.0
|
||||||
: config_.strength.eval(config_.strength.domain().clip(luxStatus.lux));
|
: config_.strength.eval(config_.strength.domain().clamp(luxStatus.lux));
|
||||||
strength *= deviceStatus.analogueGain;
|
strength *= deviceStatus.analogueGain;
|
||||||
double offset = config_.offset * strength;
|
double offset = config_.offset * strength;
|
||||||
double slope = config_.slope * strength;
|
double slope = config_.slope * strength;
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libipa/pwl.h>
|
||||||
|
|
||||||
#include "../algorithm.h"
|
#include "../algorithm.h"
|
||||||
#include "../geq_status.h"
|
#include "../geq_status.h"
|
||||||
|
|
||||||
|
@ -16,7 +18,7 @@ namespace RPiController {
|
||||||
struct GeqConfig {
|
struct GeqConfig {
|
||||||
uint16_t offset;
|
uint16_t offset;
|
||||||
double slope;
|
double slope;
|
||||||
Pwl strength; /* lux to strength factor */
|
libcamera::ipa::Pwl strength; /* lux to strength factor */
|
||||||
};
|
};
|
||||||
|
|
||||||
class Geq : public Algorithm
|
class Geq : public Algorithm
|
||||||
|
|
|
@ -42,7 +42,7 @@ void HdrConfig::read(const libcamera::YamlObject ¶ms, const std::string &mod
|
||||||
|
|
||||||
/* Lens shading related parameters. */
|
/* Lens shading related parameters. */
|
||||||
if (params.contains("spatial_gain_curve")) {
|
if (params.contains("spatial_gain_curve")) {
|
||||||
spatialGainCurve.read(params["spatial_gain_curve"]);
|
spatialGainCurve.readYaml(params["spatial_gain_curve"]);
|
||||||
} else if (params.contains("spatial_gain")) {
|
} else if (params.contains("spatial_gain")) {
|
||||||
double spatialGain = params["spatial_gain"].get<double>(2.0);
|
double spatialGain = params["spatial_gain"].get<double>(2.0);
|
||||||
spatialGainCurve.append(0.0, spatialGain);
|
spatialGainCurve.append(0.0, spatialGain);
|
||||||
|
@ -66,7 +66,7 @@ void HdrConfig::read(const libcamera::YamlObject ¶ms, const std::string &mod
|
||||||
iirStrength = params["iir_strength"].get<double>(8.0);
|
iirStrength = params["iir_strength"].get<double>(8.0);
|
||||||
strength = params["strength"].get<double>(1.5);
|
strength = params["strength"].get<double>(1.5);
|
||||||
if (tonemapEnable)
|
if (tonemapEnable)
|
||||||
tonemap.read(params["tonemap"]);
|
tonemap.readYaml(params["tonemap"]);
|
||||||
speed = params["speed"].get<double>(1.0);
|
speed = params["speed"].get<double>(1.0);
|
||||||
if (params.contains("hi_quantile_targets")) {
|
if (params.contains("hi_quantile_targets")) {
|
||||||
hiQuantileTargets = params["hi_quantile_targets"].getList<double>().value();
|
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. */
|
/* When there's a change of HDR mode we start over with a new tonemap curve. */
|
||||||
if (delayedStatus_.mode != previousMode_) {
|
if (delayedStatus_.mode != previousMode_) {
|
||||||
previousMode_ = delayedStatus_.mode;
|
previousMode_ = delayedStatus_.mode;
|
||||||
tonemap_ = Pwl();
|
tonemap_ = ipa::Pwl();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* No tonemapping. No need to output a tonemap.status. */
|
/* 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);
|
double power = std::clamp(min_power, config.powerMin, config.powerMax);
|
||||||
|
|
||||||
/* Generate the tonemap, including the contrast adjustment factors. */
|
/* Generate the tonemap, including the contrast adjustment factors. */
|
||||||
Pwl tonemap;
|
libcamera::ipa::Pwl tonemap;
|
||||||
tonemap.append(0, 0);
|
tonemap.append(0, 0);
|
||||||
for (unsigned int i = 0; i <= 6; i++) {
|
for (unsigned int i = 0; i <= 6; i++) {
|
||||||
double x = 1 << (i + 9); /* x loops from 512 to 32768 inclusive */
|
double x = 1 << (i + 9); /* x loops from 512 to 32768 inclusive */
|
||||||
|
|
|
@ -12,9 +12,10 @@
|
||||||
|
|
||||||
#include <libcamera/geometry.h>
|
#include <libcamera/geometry.h>
|
||||||
|
|
||||||
|
#include <libipa/pwl.h>
|
||||||
|
|
||||||
#include "../hdr_algorithm.h"
|
#include "../hdr_algorithm.h"
|
||||||
#include "../hdr_status.h"
|
#include "../hdr_status.h"
|
||||||
#include "../pwl.h"
|
|
||||||
|
|
||||||
/* This is our implementation of an HDR algorithm. */
|
/* This is our implementation of an HDR algorithm. */
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ struct HdrConfig {
|
||||||
std::map<unsigned int, std::string> channelMap;
|
std::map<unsigned int, std::string> channelMap;
|
||||||
|
|
||||||
/* Lens shading related parameters. */
|
/* 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. */
|
unsigned int diffusion; /* How much to diffuse the gain spatially. */
|
||||||
|
|
||||||
/* Tonemap related parameters. */
|
/* Tonemap related parameters. */
|
||||||
|
@ -35,7 +36,7 @@ struct HdrConfig {
|
||||||
double detailSlope;
|
double detailSlope;
|
||||||
double iirStrength;
|
double iirStrength;
|
||||||
double strength;
|
double strength;
|
||||||
Pwl tonemap;
|
libcamera::ipa::Pwl tonemap;
|
||||||
/* These relate to adaptive tonemap calculation. */
|
/* These relate to adaptive tonemap calculation. */
|
||||||
double speed;
|
double speed;
|
||||||
std::vector<double> hiQuantileTargets; /* quantiles to check for unsaturated images */
|
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 status_; /* track the current HDR mode and channel */
|
||||||
HdrStatus delayedStatus_; /* track the delayed HDR mode and channel */
|
HdrStatus delayedStatus_; /* track the delayed HDR mode and channel */
|
||||||
std::string previousMode_;
|
std::string previousMode_;
|
||||||
Pwl tonemap_;
|
libcamera::ipa::Pwl tonemap_;
|
||||||
libcamera::Size regions_; /* stats regions */
|
libcamera::Size regions_; /* stats regions */
|
||||||
unsigned int numRegions_; /* total number of stats regions */
|
unsigned int numRegions_; /* total number of stats regions */
|
||||||
std::vector<double> gains_[2];
|
std::vector<double> gains_[2];
|
||||||
|
|
|
@ -33,7 +33,7 @@ int Tonemap::read(const libcamera::YamlObject ¶ms)
|
||||||
config_.detailSlope = params["detail_slope"].get<double>(0.1);
|
config_.detailSlope = params["detail_slope"].get<double>(0.1);
|
||||||
config_.iirStrength = params["iir_strength"].get<double>(1.0);
|
config_.iirStrength = params["iir_strength"].get<double>(1.0);
|
||||||
config_.strength = params["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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,9 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libipa/pwl.h>
|
||||||
|
|
||||||
#include "algorithm.h"
|
#include "algorithm.h"
|
||||||
#include "pwl.h"
|
|
||||||
|
|
||||||
namespace RPiController {
|
namespace RPiController {
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ struct TonemapConfig {
|
||||||
double detailSlope;
|
double detailSlope;
|
||||||
double iirStrength;
|
double iirStrength;
|
||||||
double strength;
|
double strength;
|
||||||
Pwl tonemap;
|
libcamera::ipa::Pwl tonemap;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Tonemap : public Algorithm
|
class Tonemap : public Algorithm
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "pwl.h"
|
#include <libipa/pwl.h>
|
||||||
|
|
||||||
struct TonemapStatus {
|
struct TonemapStatus {
|
||||||
uint16_t detailConstant;
|
uint16_t detailConstant;
|
||||||
double detailSlope;
|
double detailSlope;
|
||||||
double iirStrength;
|
double iirStrength;
|
||||||
double strength;
|
double strength;
|
||||||
RPiController::Pwl tonemap;
|
libcamera::ipa::Pwl tonemap;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue