Files
external_libcamera/src/ipa/libipa/interpolator.h
T
Stefan Klug 2e936455ae ipa: libipa: Add generic Interpolator class
The MatrixInterpolator is great for interpolation of matrices for
different color temperatures. It has however one limitation - it can
only handle matrices. For LSC it would be great to interpolate the LSC
tables (or even polynomials) using the same approach. Add a generic
Interpolator class based on the existing MatrixInterpolator. This class
can be adapted to any other type using partial template specialization.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2024-09-23 16:42:45 +02:00

132 lines
2.6 KiB
C++

/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
*
* Helper class for interpolating maps of objects
*/
#pragma once
#include <algorithm>
#include <cmath>
#include <map>
#include <string>
#include <tuple>
#include <libcamera/base/log.h>
#include "libcamera/internal/yaml_parser.h"
namespace libcamera {
LOG_DECLARE_CATEGORY(Interpolator)
namespace ipa {
template<typename T>
class Interpolator
{
public:
Interpolator() = default;
Interpolator(const std::map<unsigned int, T> &data)
: data_(data)
{
}
Interpolator(std::map<unsigned int, T> &&data)
: data_(std::move(data))
{
}
~Interpolator() = default;
int readYaml(const libcamera::YamlObject &yaml,
const std::string &key_name,
const std::string &value_name)
{
data_.clear();
lastInterpolatedKey_.reset();
if (!yaml.isList()) {
LOG(Interpolator, Error) << "yaml object must be a list";
return -EINVAL;
}
for (const auto &value : yaml.asList()) {
unsigned int ct = std::stoul(value[key_name].get<std::string>(""));
std::optional<T> data =
value[value_name].get<T>();
if (!data) {
return -EINVAL;
}
data_[ct] = *data;
}
if (data_.size() < 1) {
LOG(Interpolator, Error) << "Need at least one element";
return -EINVAL;
}
return 0;
}
void setQuantization(const unsigned int q)
{
quantization_ = q;
}
void setData(std::map<unsigned int, T> &&data)
{
data_ = std::move(data);
lastInterpolatedKey_.reset();
}
const T &getInterpolated(unsigned int key, unsigned int *quantizedKey = nullptr)
{
ASSERT(data_.size() > 0);
if (quantization_ > 0)
key = std::lround(key / static_cast<double>(quantization_)) * quantization_;
if (quantizedKey)
*quantizedKey = key;
if (lastInterpolatedKey_.has_value() &&
*lastInterpolatedKey_ == key)
return lastInterpolatedValue_;
auto it = data_.lower_bound(key);
if (it == data_.begin())
return it->second;
if (it == data_.end())
return std::prev(it)->second;
if (it->first == key)
return it->second;
auto it2 = std::prev(it);
double lambda = (key - it2->first) / static_cast<double>(it->first - it2->first);
interpolate(it2->second, it->second, lastInterpolatedValue_, lambda);
lastInterpolatedKey_ = key;
return lastInterpolatedValue_;
}
void interpolate(const T &a, const T &b, T &dest, double lambda)
{
dest = a * (1.0 - lambda) + b * lambda;
}
private:
std::map<unsigned int, T> data_;
T lastInterpolatedValue_;
std::optional<unsigned int> lastInterpolatedKey_;
unsigned int quantization_ = 0;
};
} /* namespace ipa */
} /* namespace libcamera */