From patchwork Fri Sep 13 07:57:19 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 21250 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 2D68FC324C for ; Fri, 13 Sep 2024 07:58:01 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D000663500; Fri, 13 Sep 2024 09:58:00 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="WBcezrHQ"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id AB743634F5 for ; Fri, 13 Sep 2024 09:57:57 +0200 (CEST) Received: from ideasonboard.com (213-229-8-243.static.upcbusiness.at [213.229.8.243]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A8A865A5; Fri, 13 Sep 2024 09:56:38 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1726214198; bh=gTUECSt4IiUs23i7lbNpd7AmqaMSyIdz1wn+WlC+beM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WBcezrHQsJeK2zehqO6yu8WVYSWX3hHKCKN8FlhTqE1q4XEqUPMWK+aqqTYcjXwC5 PMmzkgfCDm4V6NNJBk2K8ZdORKyvfEQIu2UrA4Bb63HL4UkAMrTQv0iju81gktMmER A1rV8f9u1H5+gKYQm/Evf64SZ37FTMMdPZltIAvM= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH v2 1/9] ipa: libipa: Add generic Interpolator class Date: Fri, 13 Sep 2024 09:57:19 +0200 Message-ID: <20240913075750.35115-2-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240913075750.35115-1-stefan.klug@ideasonboard.com> References: <20240913075750.35115-1-stefan.klug@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 calss can be adapted to any other type using partial template specialization. Signed-off-by: Stefan Klug Reviewed-by: Kieran Bingham Reviewed-by: Paul Elder --- src/ipa/libipa/interpolator.cpp | 156 ++++++++++++++++++++++++++++++++ src/ipa/libipa/interpolator.h | 131 +++++++++++++++++++++++++++ src/ipa/libipa/meson.build | 2 + 3 files changed, 289 insertions(+) create mode 100644 src/ipa/libipa/interpolator.cpp create mode 100644 src/ipa/libipa/interpolator.h diff --git a/src/ipa/libipa/interpolator.cpp b/src/ipa/libipa/interpolator.cpp new file mode 100644 index 000000000000..05a4af984f9a --- /dev/null +++ b/src/ipa/libipa/interpolator.cpp @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Paul Elder + * + * Helper class for interpolating objects + */ +#include "interpolator.h" + +#include +#include + +#include + +#include "libcamera/internal/yaml_parser.h" + +#include "interpolator.h" + +/** + * \file interpolator.h + * \brief Helper class for linear interpolating a set of objects + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(Interpolator) + +namespace ipa { + +/** + * \class Interpolator + * \brief Class for storing, retrieving, and interpolating objects + * \tparam T Type of objects stored in the interpolator + * + * The main use case is to pass a map from color temperatures to corresponding + * objects (eg. matrices for color correction), and then requesting a + * interpolated object for a specific color temperature. This class will + * abstract away the interpolation portion. + */ + +/** + * \fn Interpolator::Interpolator() + * \brief Construct a empty interpolator + */ + +/** + * \fn Interpolator::Interpolator(const std::map &data) + * \brief Construct a interpolator from a map of objects + * \param data Map from which to construct the interpolator + */ + +/** + * \fn Interpolator::Interpolator(std::map &&data) + * \brief Construct a interpolator from a map of objects + * \param data Map from which to construct the interpolator + */ + +/** + * \fn int Interpolator::readYaml(const libcamera::YamlObject &yaml, + const std::string &key_name, + const std::string &value_name) + * \brief Initialize an Interpolator instance from yaml + * \tparam T Type of data stored in the interpolator + * \param[in] yaml The yaml object that contains the map of unsigned integers to objects + * \param[in] key_name The name of the key in the yaml object + * \param[in] value_name The name of the value in the yaml object + * + * The yaml object is expected to be a list of maps. Each map has two or more + * pairs: one of \a key_name to the key value (usually color temperature), and + * one or more of \a value_name to the object. This is a bit difficult to + * explain, so here is an example (in python, as it is easier to parse than + * yaml): + * [ + * { + * 'ct': 2860, + * 'ccm': [ 2.12089, -0.52461, -0.59629, + * -0.85342, 2.80445, -0.95103, + * -0.26897, -1.14788, 2.41685 ], + * 'offsets': [ 0, 0, 0 ] + * }, + * + * { + * 'ct': 2960, + * 'ccm': [ 2.26962, -0.54174, -0.72789, + * -0.77008, 2.60271, -0.83262, + * -0.26036, -1.51254, 2.77289 ], + * 'offsets': [ 0, 0, 0 ] + * }, + * + * { + * 'ct': 3603, + * 'ccm': [ 2.18644, -0.66148, -0.52496, + * -0.77828, 2.69474, -0.91645, + * -0.25239, -0.83059, 2.08298 ], + * 'offsets': [ 0, 0, 0 ] + * }, + * ] + * + * In this case, \a key_name would be 'ct', and \a value_name can be either + * 'ccm' or 'offsets'. This way multiple interpolators can be defined in + * one set of color temperature ranges in the tuning file, and they can be + * retrieved separately with the \a value_name parameter. + * + * \return Zero on success, negative error code otherwise + */ + +/** + * \fn void Interpolator::setQuantization(const unsigned int q) + * \brief Set the quantization value + * \param[in] q The quantization value + * + * Sets the quantization value. When this is set, 'key' gets quantized to this + * size, before doing the interpolation. This can help in reducing the number of + * updated pushed to the hardware. + * + * Note that normally a threshold needs to be combined with quantization. + * Otherwise a value that swings around the edge of the quantization step will + * lead to constant updates. + */ + +/** + * \fn void Interpolator::setData(std::map &&data) + * \brief Set the internal map + * + * Overwrites the internal map using move semantics. + */ + +/** + * \fn const T& Interpolator::getInterpolated(unsigned int key, unsigned int *quantizedKey = nullptr) + * \brief Retrieve a a interpolated value for the given key + * \param[in] key The unsigned integer key of the object to retrieve + * \param[out] quantizedKey If provided, the key value after quantization + * \return The object corresponding to the key. The object is cached internally, + * so on successive calls with the same key (after quantization) interpolation + * is not recalculated. + */ + +/** + * \fn void Interpolator::interpolate(const T &a, const T &b, T &dest, double + * lambda) + * \brief Interpolate between two instances of T + * \param a The first value to interpolate + * \param b The second value to interpolate + * \param dest The destination for the interpolated value + * \param lambda The interpolation factor (0..1) + * + * Interpolates between a and b according to lambda. It calculates dest = a * + * (1.0 - lambda) + b * lambda; + * + * If T supports multiplication with double and addition, this function can be + * used as is. For other types this function can be overwritten using partial + * template specialization. + */ + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/interpolator.h b/src/ipa/libipa/interpolator.h new file mode 100644 index 000000000000..fffce21465fe --- /dev/null +++ b/src/ipa/libipa/interpolator.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Paul Elder + * + * Helper class for interpolating maps of objects + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include "libcamera/internal/yaml_parser.h" + +namespace libcamera { + +LOG_DECLARE_CATEGORY(Interpolator) + +namespace ipa { + +template +class Interpolator +{ +public: + Interpolator() = default; + Interpolator(const std::map &data) + : data_(data) + { + } + Interpolator(std::map &&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::optional data = + value[value_name].get(); + 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 &&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(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(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 data_; + T lastInterpolatedValue_; + std::optional lastInterpolatedKey_; + unsigned int quantization_ = 0; +}; + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build index eff8ce2660f1..2c2712a7d252 100644 --- a/src/ipa/libipa/meson.build +++ b/src/ipa/libipa/meson.build @@ -7,6 +7,7 @@ libipa_headers = files([ 'exposure_mode_helper.h', 'fc_queue.h', 'histogram.h', + 'interpolator.h', 'matrix.h', 'matrix_interpolator.h', 'module.h', @@ -21,6 +22,7 @@ libipa_sources = files([ 'exposure_mode_helper.cpp', 'fc_queue.cpp', 'histogram.cpp', + 'interpolator.cpp', 'matrix.cpp', 'matrix_interpolator.cpp', 'module.cpp', From patchwork Fri Sep 13 07:57:20 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 21251 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id DF09AC324C for ; Fri, 13 Sep 2024 07:58:03 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 93165634F8; Fri, 13 Sep 2024 09:58:03 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="b82OH/Xj"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 279D4634F8 for ; Fri, 13 Sep 2024 09:58:00 +0200 (CEST) Received: from ideasonboard.com (213-229-8-243.static.upcbusiness.at [213.229.8.243]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 5B95E1011; Fri, 13 Sep 2024 09:56:41 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1726214201; bh=tjh4IHdZhV9Q/6JixLvDs4zZHFCRZQIr5+oEcwpDoeo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=b82OH/Xj0wKhef/cUWqcg8e+pe5t2RTkyI8Q2sO27EETgGmvy8UiofAv0RbofKpin nd1VvvUDtIsBhSqamtUrJj1J++flT0PVoIC4ZaPGcfgwHmMcpr6aj/7K18QNSg5xsA qpM7TzVGBgFo4JAMTYJuQHXPQUx8mdmrlcinaaXw= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH v2 2/9] test: ipa: libipa: Add tets for Interpolator Date: Fri, 13 Sep 2024 09:57:20 +0200 Message-ID: <20240913075750.35115-3-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240913075750.35115-1-stefan.klug@ideasonboard.com> References: <20240913075750.35115-1-stefan.klug@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add tests for the Interpolator class. Signed-off-by: Stefan Klug Reviewed-by: Kieran Bingham Reviewed-by: Paul Elder --- test/ipa/libipa/interpolator.cpp | 55 ++++++++++++++++++++++++++++++++ test/ipa/libipa/meson.build | 15 +++++++++ test/ipa/meson.build | 1 + 3 files changed, 71 insertions(+) create mode 100644 test/ipa/libipa/interpolator.cpp create mode 100644 test/ipa/libipa/meson.build diff --git a/test/ipa/libipa/interpolator.cpp b/test/ipa/libipa/interpolator.cpp new file mode 100644 index 000000000000..adb215386f0e --- /dev/null +++ b/test/ipa/libipa/interpolator.cpp @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024, Paul Elder + * + * Miscellaneous utility tests + */ + +#include +#include +#include +#include +#include + +#include "../src/ipa/libipa/interpolator.h" + +#include "test.h" + +using namespace std; +using namespace libcamera; +using namespace ipa; + +#define ASSERT_EQ(a, b) \ + if((a) != (b)) { printf(#a " != " #b "\n"); \ + return TestFail; \ + } + +class InterpolatorTest : public Test +{ +protected: + + int run() + { + Interpolator interpolator; + interpolator.setData({{10,100},{20,200},{30,300}}); + + ASSERT_EQ(interpolator.getInterpolated(0), 100); + ASSERT_EQ(interpolator.getInterpolated(10), 100); + ASSERT_EQ(interpolator.getInterpolated(20), 200); + ASSERT_EQ(interpolator.getInterpolated(25), 250); + ASSERT_EQ(interpolator.getInterpolated(30), 300); + ASSERT_EQ(interpolator.getInterpolated(40), 300); + + interpolator.setQuantization(10); + unsigned int q = 0; + ASSERT_EQ(interpolator.getInterpolated(25, &q), 300); + ASSERT_EQ(q, 30); + ASSERT_EQ(interpolator.getInterpolated(24, &q), 200); + ASSERT_EQ(q, 20); + + + return TestPass; + } +}; + +TEST_REGISTER(InterpolatorTest) diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build new file mode 100644 index 000000000000..4d2427dbd4e7 --- /dev/null +++ b/test/ipa/libipa/meson.build @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: CC0-1.0 + +libipa_test = [ + {'name': 'interpolator', 'sources': ['interpolator.cpp']}, +] + +foreach test : libipa_test + exe = executable(test['name'], test['sources'], + dependencies : [libcamera_private, libipa_dep], + link_with : [test_libraries], + include_directories : [test_includes_internal, + '../../../src/ipa/libipa/']) + + test(test['name'], exe, suite : 'ipa') +endforeach diff --git a/test/ipa/meson.build b/test/ipa/meson.build index e9871aba44ee..63820de54899 100644 --- a/test/ipa/meson.build +++ b/test/ipa/meson.build @@ -1,5 +1,6 @@ # SPDX-License-Identifier: CC0-1.0 +subdir('libipa') subdir('rkisp1') ipa_test = [ From patchwork Fri Sep 13 07:57:21 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 21252 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 7E191C324C for ; Fri, 13 Sep 2024 07:58:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 83E1363504; Fri, 13 Sep 2024 09:58:04 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="JS5K1uJx"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 916B8634F8 for ; Fri, 13 Sep 2024 09:58:02 +0200 (CEST) Received: from ideasonboard.com (213-229-8-243.static.upcbusiness.at [213.229.8.243]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C598A1011; Fri, 13 Sep 2024 09:56:43 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1726214203; bh=dsK4XmVPJVZ60yPUmPAKacXeXOOghrjApbZ3T/ZoJ08=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=JS5K1uJxO/iPNBSz4Z6XnUaAQ4B8lk+kw9m9KxnwF5+nLboLzt/xDeVxozxB8KBXE bhE8PzYRGvU4f/arUKCVohZ2wCf50b2kboyWnoZq5s5/CGqNOEjlE3lh/SOI95dU+c 3xC3vDsSqlhgvlueA7UgDX9a+7Uw1p4LCyCRin0g= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH v2 3/9] ipa: rkisp1: Use generic Interpolator class Date: Fri, 13 Sep 2024 09:57:21 +0200 Message-ID: <20240913075750.35115-4-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240913075750.35115-1-stefan.klug@ideasonboard.com> References: <20240913075750.35115-1-stefan.klug@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Replace all occurrences of the MatrixInterpolator with the generic one. Signed-off-by: Stefan Klug Reviewed-by: Kieran Bingham Reviewed-by: Paul Elder --- src/ipa/rkisp1/algorithms/ccm.cpp | 18 ++++++------------ src/ipa/rkisp1/algorithms/ccm.h | 6 +++--- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/ccm.cpp b/src/ipa/rkisp1/algorithms/ccm.cpp index 1ca0e73f81ad..6b7d2e2cd5b3 100644 --- a/src/ipa/rkisp1/algorithms/ccm.cpp +++ b/src/ipa/rkisp1/algorithms/ccm.cpp @@ -19,7 +19,7 @@ #include "libcamera/internal/yaml_parser.h" #include "../utils.h" -#include "libipa/matrix_interpolator.h" +#include "libipa/interpolator.h" /** * \file ccm.h @@ -46,7 +46,7 @@ int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData LOG(RkISP1Ccm, Warning) << "Failed to parse 'ccm' " << "parameter from tuning file; falling back to unit matrix"; - ccm_.reset(); + ccm_.setData({ { 0, Matrix::identity() } }); } ret = offsets_.readYaml(tuningData["ccms"], "ct", "offsets"); @@ -54,14 +54,8 @@ int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData LOG(RkISP1Ccm, Warning) << "Failed to parse 'offsets' " << "parameter from tuning file; falling back to zero offsets"; - /* - * MatrixInterpolator::reset() resets to identity matrices - * while here we need zero matrices so we need to construct it - * ourselves. - */ - Matrix m({ 0, 0, 0 }); - std::map> matrices = { { 0, m } }; - offsets_ = MatrixInterpolator(matrices); + + offsets_.setData({ { 0, Matrix({ 0, 0, 0 }) } }); } return 0; @@ -106,8 +100,8 @@ void Ccm::prepare(IPAContext &context, const uint32_t frame, } ct_ = ct; - Matrix ccm = ccm_.get(ct); - Matrix offsets = offsets_.get(ct); + Matrix ccm = ccm_.getInterpolated(ct); + Matrix offsets = offsets_.getInterpolated(ct); context.activeState.ccm.ccm = ccm; frameContext.ccm.ccm = ccm; diff --git a/src/ipa/rkisp1/algorithms/ccm.h b/src/ipa/rkisp1/algorithms/ccm.h index 9daadb6834b1..46a1416e6be5 100644 --- a/src/ipa/rkisp1/algorithms/ccm.h +++ b/src/ipa/rkisp1/algorithms/ccm.h @@ -9,8 +9,8 @@ #include +#include "libipa/interpolator.h" #include "libipa/matrix.h" -#include "libipa/matrix_interpolator.h" #include "algorithm.h" @@ -40,8 +40,8 @@ private: const Matrix &offsets); unsigned int ct_; - MatrixInterpolator ccm_; - MatrixInterpolator offsets_; + Interpolator> ccm_; + Interpolator> offsets_; }; } /* namespace ipa::rkisp1::algorithms */ From patchwork Fri Sep 13 07:57:22 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 21253 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 24765C324C for ; Fri, 13 Sep 2024 07:58:09 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DE6FB63504; Fri, 13 Sep 2024 09:58:08 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="I5koErmD"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2173E63501 for ; Fri, 13 Sep 2024 09:58:05 +0200 (CEST) Received: from ideasonboard.com (213-229-8-243.static.upcbusiness.at [213.229.8.243]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 2DEC71011; Fri, 13 Sep 2024 09:56:46 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1726214206; bh=SAcmxR2skxA+O+Pust+9yHZvGivrdXO3xoBIyIBY4Yw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=I5koErmDzTSGLVXWuaRPn2mbMHfE4CzuOsGvqxC0wmDLHqQWDorf0JhHg05aTW7vk Wh9gQrGjbfTCGm264YIpcBf+yBsqVvzPBG+mfM9cFgr7UL4nZTwnBF22aT/g7ArJKB r3dpTUsy/X/7lzGXGxqPEE7DhhxYCD6Pn9LibxbM= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH v2 4/9] ipa: rkisp1: Remove MatrixInterpolator Date: Fri, 13 Sep 2024 09:57:22 +0200 Message-ID: <20240913075750.35115-5-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240913075750.35115-1-stefan.klug@ideasonboard.com> References: <20240913075750.35115-1-stefan.klug@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The MatrixInterpolator is no longer used. Remove it. Signed-off-by: Stefan Klug Reviewed-by: Kieran Bingham Reviewed-by: Paul Elder --- src/ipa/libipa/matrix_interpolator.cpp | 103 --------------------- src/ipa/libipa/matrix_interpolator.h | 120 ------------------------- src/ipa/libipa/meson.build | 2 - 3 files changed, 225 deletions(-) delete mode 100644 src/ipa/libipa/matrix_interpolator.cpp delete mode 100644 src/ipa/libipa/matrix_interpolator.h diff --git a/src/ipa/libipa/matrix_interpolator.cpp b/src/ipa/libipa/matrix_interpolator.cpp deleted file mode 100644 index d5188f8aa4e9..000000000000 --- a/src/ipa/libipa/matrix_interpolator.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2024, Paul Elder - * - * Helper class for interpolating maps of matrices - */ -#include "matrix_interpolator.h" - -#include - -/** - * \file matrix_interpolator.h - * \brief Helper class for interpolating maps of matrices - */ - -namespace libcamera { - -LOG_DEFINE_CATEGORY(MatrixInterpolator) - -namespace ipa { - -/** - * \class MatrixInterpolator - * \brief Class for storing, retrieving, and interpolating matrices - * \tparam T Type of numerical values to be stored in the matrices - * \tparam R Number of rows in the matrices - * \tparam C Number of columns in the matrices - * - * The main use case is to pass a map from color temperatures to corresponding - * matrices (eg. color correction), and then requesting a matrix for a specific - * color temperature. This class will abstract away the interpolation portion. - */ - -/** - * \fn MatrixInterpolator::MatrixInterpolator(const std::map> &matrices) - * \brief Construct a matrix interpolator from a map of matrices - * \param matrices Map from which to construct the matrix interpolator - */ - -/** - * \fn MatrixInterpolator::reset() - * \brief Reset the matrix interpolator content to a single identity matrix - */ - -/** - * \fn int MatrixInterpolator::readYaml() - * \brief Initialize an MatrixInterpolator instance from yaml - * \tparam T Type of data stored in the matrices - * \tparam R Number of rows of the matrices - * \tparam C Number of columns of the matrices - * \param[in] yaml The yaml object that contains the map of unsigned integers to matrices - * \param[in] key_name The name of the key in the yaml object - * \param[in] matrix_name The name of the matrix in the yaml object - * - * The yaml object is expected to be a list of maps. Each map has two or more - * pairs: one of \a key_name to the key value (usually color temperature), and - * one or more of \a matrix_name to the matrix. This is a bit difficult to - * explain, so here is an example (in python, as it is easier to parse than - * yaml): - * [ - * { - * 'ct': 2860, - * 'ccm': [ 2.12089, -0.52461, -0.59629, - * -0.85342, 2.80445, -0.95103, - * -0.26897, -1.14788, 2.41685 ], - * 'offsets': [ 0, 0, 0 ] - * }, - * - * { - * 'ct': 2960, - * 'ccm': [ 2.26962, -0.54174, -0.72789, - * -0.77008, 2.60271, -0.83262, - * -0.26036, -1.51254, 2.77289 ], - * 'offsets': [ 0, 0, 0 ] - * }, - * - * { - * 'ct': 3603, - * 'ccm': [ 2.18644, -0.66148, -0.52496, - * -0.77828, 2.69474, -0.91645, - * -0.25239, -0.83059, 2.08298 ], - * 'offsets': [ 0, 0, 0 ] - * }, - * ] - * - * In this case, \a key_name would be 'ct', and \a matrix_name can be either - * 'ccm' or 'offsets'. This way multiple matrix interpolators can be defined in - * one set of color temperature ranges in the tuning file, and they can be - * retrieved separately with the \a matrix_name parameter. - * - * \return Zero on success, negative error code otherwise - */ - -/** - * \fn Matrix MatrixInterpolator::get(unsigned int key) - * \brief Retrieve a matrix from the list of matrices, interpolating if necessary - * \param[in] key The unsigned integer key of the matrix to retrieve - * \return The matrix corresponding to the color temperature - */ - -} /* namespace ipa */ - -} /* namespace libcamera */ diff --git a/src/ipa/libipa/matrix_interpolator.h b/src/ipa/libipa/matrix_interpolator.h deleted file mode 100644 index afbce5386b06..000000000000 --- a/src/ipa/libipa/matrix_interpolator.h +++ /dev/null @@ -1,120 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2024, Paul Elder - * - * Helper class for interpolating maps of matrices - */ - -#pragma once - -#include -#include - -#include - -#include "libcamera/internal/yaml_parser.h" - -#include "matrix.h" - -namespace libcamera { - -LOG_DECLARE_CATEGORY(MatrixInterpolator) - -namespace ipa { - -#ifndef __DOXYGEN__ -template> * = nullptr> -#else -template -#endif /* __DOXYGEN__ */ -class MatrixInterpolator -{ -public: - MatrixInterpolator() - { - reset(); - } - - MatrixInterpolator(const std::map> &matrices) - { - for (const auto &pair : matrices) - matrices_[pair.first] = pair.second; - } - - ~MatrixInterpolator() {} - - void reset() - { - matrices_.clear(); - matrices_[0] = Matrix::identity(); - } - - int readYaml(const libcamera::YamlObject &yaml, - const std::string &key_name, - const std::string &matrix_name) - { - matrices_.clear(); - - if (!yaml.isList()) { - LOG(MatrixInterpolator, 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::optional> matrix = - value[matrix_name].get>(); - if (!matrix) { - LOG(MatrixInterpolator, Error) << "Failed to read matrix"; - return -EINVAL; - } - - matrices_[ct] = *matrix; - - LOG(MatrixInterpolator, Debug) - << "Read matrix '" << matrix_name << "' for key '" - << key_name << "' " << ct << ": " - << matrices_[ct].toString(); - } - - if (matrices_.size() < 1) { - LOG(MatrixInterpolator, Error) << "Need at least one matrix"; - return -EINVAL; - } - - return 0; - } - - Matrix get(unsigned int ct) - { - ASSERT(matrices_.size() > 0); - - if (matrices_.size() == 1 || - ct <= matrices_.begin()->first) - return matrices_.begin()->second; - - if (ct >= matrices_.rbegin()->first) - return matrices_.rbegin()->second; - - if (matrices_.find(ct) != matrices_.end()) - return matrices_[ct]; - - /* The above four guarantee that this will succeed */ - auto iter = matrices_.upper_bound(ct); - unsigned int ctUpper = iter->first; - unsigned int ctLower = (--iter)->first; - - double lambda = (ct - ctLower) / static_cast(ctUpper - ctLower); - Matrix ret = - lambda * matrices_[ctUpper] + (1.0 - lambda) * matrices_[ctLower]; - return ret; - } - -private: - std::map> matrices_; -}; - -} /* namespace ipa */ - -} /* namespace libcamera */ diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build index 2c2712a7d252..3740178b2909 100644 --- a/src/ipa/libipa/meson.build +++ b/src/ipa/libipa/meson.build @@ -9,7 +9,6 @@ libipa_headers = files([ 'histogram.h', 'interpolator.h', 'matrix.h', - 'matrix_interpolator.h', 'module.h', 'pwl.h', 'vector.h', @@ -24,7 +23,6 @@ libipa_sources = files([ 'histogram.cpp', 'interpolator.cpp', 'matrix.cpp', - 'matrix_interpolator.cpp', 'module.cpp', 'pwl.cpp', 'vector.cpp', From patchwork Fri Sep 13 07:57:23 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 21254 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 3BC95C324C for ; Fri, 13 Sep 2024 07:58:12 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D8C3F63501; Fri, 13 Sep 2024 09:58:11 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="BoaLDwMB"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E5328634FE for ; Fri, 13 Sep 2024 09:58:07 +0200 (CEST) Received: from ideasonboard.com (213-229-8-243.static.upcbusiness.at [213.229.8.243]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 294195A5; Fri, 13 Sep 2024 09:56:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1726214209; bh=X6Xf67Eenm5NloMdJWj+67/2M8EYb3sGdSzEncwaQXI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BoaLDwMBin+DdBbJKMEjORGttnbq9Lj1Spfpuf81D6zAD65T3aQe4B5TLnrhZuAXC uyj9LObtlpDi4K8KrNilLstbXrvEjVYPPpasueUcaaWmXJTq6uBCxPGqHxrossiYlr ZSZhQfuoGfLu3XHP4FazrfzuV20Vo7KR6+bElTQs= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH v2 5/9] ipa: rkisp1: Use interpolator in lsc Date: Fri, 13 Sep 2024 09:57:23 +0200 Message-ID: <20240913075750.35115-6-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240913075750.35115-1-stefan.klug@ideasonboard.com> References: <20240913075750.35115-1-stefan.klug@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Now, that the generic interpolator is available, use it to do the interpolation of the lens shading tables. This makes the algorithm easier to read and remove some duplicate code. Signed-off-by: Stefan Klug Reviewed-by: Kieran Bingham Reviewed-by: Paul Elder --- src/ipa/rkisp1/algorithms/lsc.cpp | 166 +++++++++--------------------- src/ipa/rkisp1/algorithms/lsc.h | 13 +-- 2 files changed, 57 insertions(+), 122 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp index 5f3a0388075b..87a04ec048f8 100644 --- a/src/ipa/rkisp1/algorithms/lsc.cpp +++ b/src/ipa/rkisp1/algorithms/lsc.cpp @@ -24,6 +24,36 @@ namespace libcamera { +namespace ipa { + +constexpr int kColourTemperatureChangeThreshhold = 10; + +template +void interpolateVector(const std::vector &a, const std::vector &b, + std::vector &dest, double lambda) +{ + assert(a.size() == b.size()); + dest.resize(a.size()); + for (size_t i = 0; i < a.size(); i++) { + dest[i] = a[i] * (1.0 - lambda) + b[i] * lambda; + } +} + +template<> +void Interpolator:: + interpolate(const rkisp1::algorithms::LensShadingCorrection::Components &a, + const rkisp1::algorithms::LensShadingCorrection::Components &b, + rkisp1::algorithms::LensShadingCorrection::Components &dest, + double lambda) +{ + interpolateVector(a.r, b.r, dest.r, lambda); + interpolateVector(a.gr, b.gr, dest.gr, lambda); + interpolateVector(a.gb, b.gb, dest.gb, lambda); + interpolateVector(a.b, b.b, dest.b, lambda); +} + +} // namespace ipa + namespace ipa::rkisp1::algorithms { /** @@ -90,8 +120,9 @@ static std::vector parseTable(const YamlObject &tuningData, } LensShadingCorrection::LensShadingCorrection() - : lastCt_({ 0, 0 }) + : lastAppliedCt_(0), lastAppliedQuantizedCt_(0) { + sets_.setQuantization(kColourTemperatureChangeThreshhold); } /** @@ -115,17 +146,18 @@ int LensShadingCorrection::init([[maybe_unused]] IPAContext &context, } const auto &sets = yamlSets.asList(); + std::map lscData; for (const auto &yamlSet : sets) { uint32_t ct = yamlSet["ct"].get(0); - if (sets_.count(ct)) { + if (lscData.count(ct)) { LOG(RkISP1Lsc, Error) << "Multiple sets found for color temperature " << ct; return -EINVAL; } - Components &set = sets_[ct]; + Components &set = lscData[ct]; set.ct = ct; set.r = parseTable(yamlSet, "r"); @@ -142,11 +174,13 @@ int LensShadingCorrection::init([[maybe_unused]] IPAContext &context, } } - if (sets_.empty()) { + if (lscData.empty()) { LOG(RkISP1Lsc, Error) << "Failed to load any sets"; return -EINVAL; } + sets_.setData(std::move(lscData)); + return 0; } @@ -202,134 +236,34 @@ void LensShadingCorrection::copyTable(rkisp1_cif_isp_lsc_config &config, std::copy(set.b.begin(), set.b.end(), &config.b_data_tbl[0][0]); } -/* - * Interpolate LSC parameters based on color temperature value. - */ -void LensShadingCorrection::interpolateTable(rkisp1_cif_isp_lsc_config &config, - const Components &set0, - const Components &set1, - const uint32_t ct) -{ - double coeff0 = (set1.ct - ct) / static_cast(set1.ct - set0.ct); - double coeff1 = (ct - set0.ct) / static_cast(set1.ct - set0.ct); - - for (unsigned int i = 0; i < RKISP1_CIF_ISP_LSC_SAMPLES_MAX; ++i) { - for (unsigned int j = 0; j < RKISP1_CIF_ISP_LSC_SAMPLES_MAX; ++j) { - unsigned int sample = i * RKISP1_CIF_ISP_LSC_SAMPLES_MAX + j; - - config.r_data_tbl[i][j] = - set0.r[sample] * coeff0 + - set1.r[sample] * coeff1; - - config.gr_data_tbl[i][j] = - set0.gr[sample] * coeff0 + - set1.gr[sample] * coeff1; - - config.gb_data_tbl[i][j] = - set0.gb[sample] * coeff0 + - set1.gb[sample] * coeff1; - - config.b_data_tbl[i][j] = - set0.b[sample] * coeff0 + - set1.b[sample] * coeff1; - } - } -} - /** * \copydoc libcamera::ipa::Algorithm::prepare */ void LensShadingCorrection::prepare(IPAContext &context, - const uint32_t frame, + [[maybe_unused]] const uint32_t frame, [[maybe_unused]] IPAFrameContext &frameContext, RkISP1Params *params) { - /* - * If there is only one set, the configuration has already been done - * for first frame. - */ - if (sets_.size() == 1 && frame > 0) - return; - - /* - * If there is only one set, pick it. We can ignore lastCt_, as it will - * never be relevant. - */ - if (sets_.size() == 1) { - auto config = params->block(); - config.setEnabled(true); - - setParameters(*config); - copyTable(*config, sets_.cbegin()->second); - return; - } - uint32_t ct = context.activeState.awb.temperatureK; - ct = std::clamp(ct, sets_.cbegin()->first, sets_.crbegin()->first); - - /* - * If the original is the same, then it means the same adjustment would - * be made. If the adjusted is the same, then it means that it's the - * same as what was actually applied. Thus in these cases we can skip - * reprogramming the LSC. - * - * original == adjusted can only happen if an interpolation - * happened, or if original has an exact entry in sets_. This means - * that if original != adjusted, then original was adjusted to - * the nearest available entry in sets_, resulting in adjusted. - * Clearly, any ct value that is in between original and adjusted - * will be adjusted to the same adjusted value, so we can skip - * reprogramming the LSC table. - * - * We also skip updating the original value, as the last one had a - * larger bound and thus a larger range of ct values that will be - * adjusted to the same adjusted. - */ - if ((lastCt_.original <= ct && ct <= lastCt_.adjusted) || - (lastCt_.adjusted <= ct && ct <= lastCt_.original)) + if (std::abs(static_cast(ct) - static_cast(lastAppliedCt_)) < + kColourTemperatureChangeThreshhold) + return; + unsigned int quantizedCt; + const Components &set = sets_.getInterpolated(ct, &quantizedCt); + if (lastAppliedQuantizedCt_ == quantizedCt) return; auto config = params->block(); config.setEnabled(true); setParameters(*config); + copyTable(*config, set); - /* - * The color temperature matches exactly one of the available LSC tables. - */ - if (sets_.count(ct)) { - copyTable(*config, sets_[ct]); - lastCt_ = { ct, ct }; - return; - } + lastAppliedCt_ = ct; + lastAppliedQuantizedCt_ = quantizedCt; - /* No shortcuts left; we need to round or interpolate */ - auto iter = sets_.upper_bound(ct); - const Components &set1 = iter->second; - const Components &set0 = (--iter)->second; - uint32_t ct0 = set0.ct; - uint32_t ct1 = set1.ct; - uint32_t diff0 = ct - ct0; - uint32_t diff1 = ct1 - ct; - static constexpr double kThreshold = 0.1; - float threshold = kThreshold * (ct1 - ct0); - - if (diff0 < threshold || diff1 < threshold) { - const Components &set = diff0 < diff1 ? set0 : set1; - LOG(RkISP1Lsc, Debug) << "using LSC table for " << set.ct; - copyTable(*config, set); - lastCt_ = { ct, set.ct }; - return; - } - - /* - * ct is not within 10% of the difference between the neighbouring - * color temperatures, so we need to interpolate. - */ LOG(RkISP1Lsc, Debug) - << "ct is " << ct << ", interpolating between " - << ct0 << " and " << ct1; - interpolateTable(*config, set0, set1, ct); - lastCt_ = { ct, ct }; + << "ct is " << ct << ", quantized to " + << quantizedCt; } REGISTER_IPA_ALGORITHM(LensShadingCorrection, "LensShadingCorrection") diff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h index a9c7a230e0fc..5a0824e36dd5 100644 --- a/src/ipa/rkisp1/algorithms/lsc.h +++ b/src/ipa/rkisp1/algorithms/lsc.h @@ -9,6 +9,8 @@ #include +#include "libipa/interpolator.h" + #include "algorithm.h" namespace libcamera { @@ -27,7 +29,6 @@ public: IPAFrameContext &frameContext, RkISP1Params *params) override; -private: struct Components { uint32_t ct; std::vector r; @@ -36,23 +37,23 @@ private: std::vector b; }; +private: void setParameters(rkisp1_cif_isp_lsc_config &config); void copyTable(rkisp1_cif_isp_lsc_config &config, const Components &set0); void interpolateTable(rkisp1_cif_isp_lsc_config &config, const Components &set0, const Components &set1, const uint32_t ct); - std::map sets_; + ipa::Interpolator sets_; std::vector xSize_; std::vector ySize_; uint16_t xGrad_[RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE]; uint16_t yGrad_[RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE]; uint16_t xSizes_[RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE]; uint16_t ySizes_[RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE]; - struct { - uint32_t original; - uint32_t adjusted; - } lastCt_; + + unsigned int lastAppliedCt_; + unsigned int lastAppliedQuantizedCt_; }; } /* namespace ipa::rkisp1::algorithms */ From patchwork Fri Sep 13 07:57:24 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 21255 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id E8D11C3261 for ; Fri, 13 Sep 2024 07:58:13 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DC8406350A; Fri, 13 Sep 2024 09:58:12 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="AHQxQ3FL"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9C4D8634F5 for ; Fri, 13 Sep 2024 09:58:10 +0200 (CEST) Received: from ideasonboard.com (213-229-8-243.static.upcbusiness.at [213.229.8.243]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D79401011; Fri, 13 Sep 2024 09:56:51 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1726214211; bh=ARU+LcX5zZp3pLdbXRWNrfXM431hnj9muMp4KSA55NU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=AHQxQ3FLGob4QPNFsazO4X0hjl82kcpdNimOJuhGXWjjJczd5B4OKnnPBnmsUQtA4 +JzhGtfftVe64WTlDlbKEVwRJlSoWRBIuk1QPPikVacVPUdGtk5u2z7yEy3DLq66GU MNPsadRlZ3EPLxPj7DdkW4bman+PIDsDAh1fUm9Q= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH v2 6/9] ipa: rkisp1: Move loader functions into helper class Date: Fri, 13 Sep 2024 09:57:24 +0200 Message-ID: <20240913075750.35115-7-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240913075750.35115-1-stefan.klug@ideasonboard.com> References: <20240913075750.35115-1-stefan.klug@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" In preparation to the polynomial LSC data, move the current loader into it's own helper class. Signed-off-by: Stefan Klug Reviewed-by: Kieran Bingham Reviewed-by: Paul Elder --- src/ipa/rkisp1/algorithms/lsc.cpp | 125 ++++++++++++++++++------------ 1 file changed, 76 insertions(+), 49 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp index 87a04ec048f8..12867b8f7d0f 100644 --- a/src/ipa/rkisp1/algorithms/lsc.cpp +++ b/src/ipa/rkisp1/algorithms/lsc.cpp @@ -70,6 +70,70 @@ namespace ipa::rkisp1::algorithms { LOG_DEFINE_CATEGORY(RkISP1Lsc) +class LscClassicLoader +{ +public: + int parseLscData(const YamlObject &yamlSets, + std::map &lscData) + { + const auto &sets = yamlSets.asList(); + + for (const auto &yamlSet : sets) { + uint32_t ct = yamlSet["ct"].get(0); + + if (lscData.count(ct)) { + LOG(RkISP1Lsc, Error) + << "Multiple sets found for color temperature " + << ct; + return -EINVAL; + } + + LensShadingCorrection::Components &set = lscData[ct]; + + set.ct = ct; + set.r = parseTable(yamlSet, "r"); + set.gr = parseTable(yamlSet, "gr"); + set.gb = parseTable(yamlSet, "gb"); + set.b = parseTable(yamlSet, "b"); + + if (set.r.empty() || set.gr.empty() || + set.gb.empty() || set.b.empty()) { + LOG(RkISP1Lsc, Error) + << "Set for color temperature " << ct + << " is missing tables"; + return -EINVAL; + } + } + + if (lscData.empty()) { + LOG(RkISP1Lsc, Error) << "Failed to load any sets"; + return -EINVAL; + } + + return 0; + } + +private: + std::vector parseTable(const YamlObject &tuningData, + const char *prop) + { + static constexpr unsigned int kLscNumSamples = + RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX; + + std::vector table = + tuningData[prop].getList().value_or(std::vector{}); + if (table.size() != kLscNumSamples) { + LOG(RkISP1Lsc, Error) + << "Invalid '" << prop << "' values: expected " + << kLscNumSamples + << " elements, got " << table.size(); + return {}; + } + + return table; + } +}; + static std::vector parseSizes(const YamlObject &tuningData, const char *prop) { @@ -100,25 +164,6 @@ static std::vector parseSizes(const YamlObject &tuningData, return sizes; } -static std::vector parseTable(const YamlObject &tuningData, - const char *prop) -{ - static constexpr unsigned int kLscNumSamples = - RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX; - - std::vector table = - tuningData[prop].getList().value_or(std::vector{}); - if (table.size() != kLscNumSamples) { - LOG(RkISP1Lsc, Error) - << "Invalid '" << prop << "' values: expected " - << kLscNumSamples - << " elements, got " << table.size(); - return {}; - } - - return table; -} - LensShadingCorrection::LensShadingCorrection() : lastAppliedCt_(0), lastAppliedQuantizedCt_(0) { @@ -145,39 +190,21 @@ int LensShadingCorrection::init([[maybe_unused]] IPAContext &context, return -EINVAL; } - const auto &sets = yamlSets.asList(); std::map lscData; - for (const auto &yamlSet : sets) { - uint32_t ct = yamlSet["ct"].get(0); - - if (lscData.count(ct)) { - LOG(RkISP1Lsc, Error) - << "Multiple sets found for color temperature " - << ct; - return -EINVAL; - } - - Components &set = lscData[ct]; - - set.ct = ct; - set.r = parseTable(yamlSet, "r"); - set.gr = parseTable(yamlSet, "gr"); - set.gb = parseTable(yamlSet, "gb"); - set.b = parseTable(yamlSet, "b"); - - if (set.r.empty() || set.gr.empty() || - set.gb.empty() || set.b.empty()) { - LOG(RkISP1Lsc, Error) - << "Set for color temperature " << ct - << " is missing tables"; - return -EINVAL; - } + int res = 0; + std::optional type = tuningData["type"].get(); + if (!type.has_value()) { + LOG(RkISP1Lsc, Warning) << "LSC data is in classic format. " + << "This will be deprecated soon."; + auto loader = LscClassicLoader(); + res = loader.parseLscData(yamlSets, lscData); + } else { + LOG(RkISP1Lsc, Error) << "Unsupported LSC type '" << *type << "'"; + res = -EINVAL; } - if (lscData.empty()) { - LOG(RkISP1Lsc, Error) << "Failed to load any sets"; - return -EINVAL; - } + if (res) + return res; sets_.setData(std::move(lscData)); From patchwork Fri Sep 13 07:57:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 21256 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id D83E9C324C for ; Fri, 13 Sep 2024 07:58:16 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 88B3C6350A; Fri, 13 Sep 2024 09:58:16 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="IrFMZqik"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 58C216350C for ; Fri, 13 Sep 2024 09:58:13 +0200 (CEST) Received: from ideasonboard.com (213-229-8-243.static.upcbusiness.at [213.229.8.243]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 56A535A5; Fri, 13 Sep 2024 09:56:54 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1726214214; bh=iED+IJGiCgic69/fCg92a8nSNxF/IcTI1qcNqQjkSCk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IrFMZqikgQwG9uN7YBA+R33i0TuOx0bddlr+dRaHqgpk+5g6RSTHn7Kx2iIjo2NWM kDSpwqJBt/Yt/nlmUoliugONjrSKAZPQfqjPdAArMVGukMAMzePHuJq8WlYvFIHDaw Ox3/SkDSymb5h4xFCgbXYQvSD0ENP3g3PdJIqcEg= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH v2 7/9] ipa: libipa: Add lsc polynomial class Date: Fri, 13 Sep 2024 09:57:25 +0200 Message-ID: <20240913075750.35115-8-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240913075750.35115-1-stefan.klug@ideasonboard.com> References: <20240913075750.35115-1-stefan.klug@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a basic class to represent polynomials as specified in the DNG spec for vignetting correction. Signed-off-by: Stefan Klug Reviewed-by: Kieran Bingham --- src/ipa/libipa/lsc_polynomial.cpp | 81 +++++++++++++++++++++++ src/ipa/libipa/lsc_polynomial.h | 104 ++++++++++++++++++++++++++++++ src/ipa/libipa/meson.build | 2 + 3 files changed, 187 insertions(+) create mode 100644 src/ipa/libipa/lsc_polynomial.cpp create mode 100644 src/ipa/libipa/lsc_polynomial.h diff --git a/src/ipa/libipa/lsc_polynomial.cpp b/src/ipa/libipa/lsc_polynomial.cpp new file mode 100644 index 000000000000..abbbcb6a724c --- /dev/null +++ b/src/ipa/libipa/lsc_polynomial.cpp @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Ideas On Board + * + * Polynomal class to represent lens shading correction + */ + +#include "lsc_polynomial.h" + +#include + +/** + * \file lsc_polynomial.h + * \brief LscPolynomial class + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(LscPolynomial) + +namespace ipa { + +/** + * \class LscPolynomial + * \brief Class for handling even polynomial used in lens shading correction + * + * Shading artifacts of camera lenses can be modeled using even radial + * polynomials. This class implements a polynomial with 5 coefficients which + * follows the definition of the FixVignetteRadial opcode in the Adobe DNG + * specification. + */ + +/** + * \fn LscPolynomial::LscPolynomial(double cx = 0.0, double cy = 0.0, double k0 = 0.0, + double k1 = 0.0, double k2 = 0.0, double k3 = 0.0, + double k4 = 0.0) + * \brief Construct a polynomial using the given coefficients + * \param cx Center-x relative to the image in normalized coordinates (0..1) + * \param cy Center-y relative to the image in normalized coordinates (0..1) + * \param k0 Coefficient of the polynomial + * \param k1 Coefficient of the polynomial + * \param k2 Coefficient of the polynomial + * \param k3 Coefficient of the polynomial + * \param k4 Coefficient of the polynomial + */ + +/** + * \fn LscPolynomial::sampleAtNormalizedPixelPos(double x, double y) + * \brief Sample the polynomial at the given normalized pixel position + * + * This functions samples the polynomial at the given pixel position divided by + * the value returned by getM(). + * + * \param x x position in normalized coordinates + * \param y y position in normalized coordinates + * \return The sampled value + */ + +/** + * \fn LscPolynomial::getM() + * \brief Get the value m as described in the dng specification + * + * Returns m according to dng spec. m represents the Euclidean distance + * (in pixels) from the optical center to the farthest pixel in the + * image. + * + * \return The sampled value + */ + +/** + * \fn LscPolynomial::setReferenceImageSize(const Size &size) + * \brief Set the reference image size + * + * Set the reference image size that is used for subsequent calls to getM() and + * sampleAtNormalizedPixelPos() + * + * \param size THe size of the reference image + */ + +} // namespace ipa +} // namespace libcamera diff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h new file mode 100644 index 000000000000..890199017590 --- /dev/null +++ b/src/ipa/libipa/lsc_polynomial.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Ideas On Board + * + * Helper for radial polynomial used in lens shading correction. + */ +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "libcamera/internal/yaml_parser.h" + +namespace libcamera { + +LOG_DECLARE_CATEGORY(LscPolynomial) + +namespace ipa { + +class LscPolynomial +{ +public: + LscPolynomial(double cx = 0.0, double cy = 0.0, double k0 = 0.0, + double k1 = 0.0, double k2 = 0.0, double k3 = 0.0, + double k4 = 0.0) + : cx_(cx), cy_(cy), + coefficients_({ k0, k1, k2, k3, k4 }) + { + } + + double sampleAtNormalizedPixelPos(double x, double y) const + { + double dx = x - cnx_; + double dy = y - cny_; + double r = sqrt(dx * dx + dy * dy); + double res = 1.0; + for (unsigned int i = 0; i < coefficients_.size(); i++) { + res += coefficients_[i] * std::pow(r, (i + 1) * 2); + } + return res; + } + + double getM() const + { + double cpx = imageSize_.width * cx_; + double cpy = imageSize_.height * cy_; + double mx = std::max(cpx, std::fabs(imageSize_.width - cpx)); + double my = std::max(cpy, std::fabs(imageSize_.height - cpy)); + + return sqrt(mx * mx + my * my); + } + + void setReferenceImageSize(const Size &size) + { + imageSize_ = size; + + /* Calculate normalized centers */ + double m = getM(); + cnx_ = (size.width * cx_) / m; + cny_ = (size.height * cy_) / m; + } + +private: + double cx_; + double cy_; + double cnx_; + double cny_; + std::array coefficients_; + + Size imageSize_; +}; + +} /* namespace ipa */ + +#ifndef __DOXYGEN__ + +template<> +struct YamlObject::Getter { + std::optional get(const YamlObject &obj) const + { + std::optional cx = obj["cx"].get(); + std::optional cy = obj["cy"].get(); + std::optional k0 = obj["k0"].get(); + std::optional k1 = obj["k1"].get(); + std::optional k2 = obj["k2"].get(); + std::optional k3 = obj["k3"].get(); + std::optional k4 = obj["k4"].get(); + + if (!(cx && cy && k0 && k1 && k2 && k3 && k4)) + LOG(LscPolynomial, Error) + << "Polynomial is missing a parameter"; + + return ipa::LscPolynomial(*cx, *cy, *k0, *k1, *k2, *k3, *k4); + } +}; + +#endif + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build index 3740178b2909..e78cbcd63633 100644 --- a/src/ipa/libipa/meson.build +++ b/src/ipa/libipa/meson.build @@ -8,6 +8,7 @@ libipa_headers = files([ 'fc_queue.h', 'histogram.h', 'interpolator.h', + 'lsc_polynomial.h', 'matrix.h', 'module.h', 'pwl.h', @@ -22,6 +23,7 @@ libipa_sources = files([ 'fc_queue.cpp', 'histogram.cpp', 'interpolator.cpp', + 'lsc_polynomial.cpp', 'matrix.cpp', 'module.cpp', 'pwl.cpp', From patchwork Fri Sep 13 07:57:26 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 21257 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 5DCF4C324C for ; Fri, 13 Sep 2024 07:58:19 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DC78163509; Fri, 13 Sep 2024 09:58:18 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="YN9BHp4Q"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 62DE663504 for ; Fri, 13 Sep 2024 09:58:16 +0200 (CEST) Received: from ideasonboard.com (213-229-8-243.static.upcbusiness.at [213.229.8.243]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 9AE601011; Fri, 13 Sep 2024 09:56:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1726214217; bh=Ddnboubq04MBc79+AsxPZLaJr0dyGKhRBhZd8P5psVI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YN9BHp4QyD4069VI0oDaDZj61d20KJGO5ESV2+OpHq4bjtdH8GDivUW+p3IMiA/rZ uYeBhI5N0YP1TubWyo+q+tIvvB04aZQeqrQUr9Rz9MkJ0H7MWeg1+rgobmbzoKKx1K Vexy25/iDsPFdiInlT+clLtmKTGZ8BE0vnNpnJfg= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH v2 8/9] ipa: rkisp1: Add sensor info to context Date: Fri, 13 Sep 2024 09:57:26 +0200 Message-ID: <20240913075750.35115-9-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240913075750.35115-1-stefan.klug@ideasonboard.com> References: <20240913075750.35115-1-stefan.klug@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" For the LSC algorithm to dynamically calculate the LSC tables based on the sensor size and the crop rectangle it needs access to that data. Provide access to it by adding the sensorInfo object to the context. Signed-off-by: Stefan Klug Reviewed-by: Kieran Bingham Reviewed-by: Paul Elder --- src/ipa/rkisp1/ipa_context.h | 2 ++ src/ipa/rkisp1/rkisp1.cpp | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index 3af2774a7fc8..e274d9b01e1c 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -180,6 +181,7 @@ struct IPAFrameContext : public FrameContext { struct IPAContext { const IPAHwSettings *hw; + IPACameraSensorInfo sensorInfo; IPASessionConfiguration configuration; IPAActiveState activeState; diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp index 78d2c375d6dc..9e161cabdea4 100644 --- a/src/ipa/rkisp1/rkisp1.cpp +++ b/src/ipa/rkisp1/rkisp1.cpp @@ -124,7 +124,7 @@ const ControlInfoMap::Map rkisp1Controls{ } /* namespace */ IPARkISP1::IPARkISP1() - : context_({ {}, {}, {}, { kMaxFrameContexts }, {}, {} }) + : context_({ {}, {}, {}, {}, { kMaxFrameContexts }, {}, {} }) { } @@ -158,6 +158,8 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision, LOG(IPARkISP1, Debug) << "Hardware revision is " << hwRevision; + context_.sensorInfo = sensorInfo; + context_.camHelper = CameraSensorHelperFactoryBase::create(settings.sensorModel); if (!context_.camHelper) { LOG(IPARkISP1, Error) From patchwork Fri Sep 13 07:57:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 21258 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 7EEF2C324C for ; Fri, 13 Sep 2024 07:58:23 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 270E76350E; Fri, 13 Sep 2024 09:58:23 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="eYuIyF9f"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 599C663504 for ; Fri, 13 Sep 2024 09:58:19 +0200 (CEST) Received: from ideasonboard.com (213-229-8-243.static.upcbusiness.at [213.229.8.243]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 8C25A1011; Fri, 13 Sep 2024 09:57:00 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1726214220; bh=xJLxMRw47iu6x6DQ3E63NZqhqnC645GatEgAZzP+h7E=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eYuIyF9f4fkGuYl8BVwuRvGdmMZKAuzzf79QLGJUOb/sivgWwBk9/azzOBnaGm6Bw 94XYIBYDDsBLTdxIcAbRSV75eBsBtUxJ9TiDzFzS8NfWU9GNPJ6/M6MicFi0bqZyfZ gXDpGcOcOIopR6qZgdInIuF1Nc+P/8c3vTi9S9Ss= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH v2 9/9] ipa: rkisp1: Add polynomial LSC loader Date: Fri, 13 Sep 2024 09:57:27 +0200 Message-ID: <20240913075750.35115-10-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240913075750.35115-1-stefan.klug@ideasonboard.com> References: <20240913075750.35115-1-stefan.klug@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a loader that is capable to load polynomial coefficients from the tuning files. The polynoms are sampled at load time to reduce the computational overhead at runtime. Signed-off-by: Stefan Klug --- src/ipa/rkisp1/algorithms/lsc.cpp | 126 +++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp index 12867b8f7d0f..b0ad2234cd11 100644 --- a/src/ipa/rkisp1/algorithms/lsc.cpp +++ b/src/ipa/rkisp1/algorithms/lsc.cpp @@ -16,6 +16,7 @@ #include "libcamera/internal/yaml_parser.h" +#include "libipa/lsc_polynomial.h" #include "linux/rkisp1-config.h" /** @@ -70,6 +71,123 @@ namespace ipa::rkisp1::algorithms { LOG_DEFINE_CATEGORY(RkISP1Lsc) +class LscPolynomialLoader +{ +public: + LscPolynomialLoader(const Size &sensorSize, + const Rectangle &cropRectangle, + const std::vector &xSizes, + const std::vector &ySizes) + : sensorSize_(sensorSize), + cropRectangle_(cropRectangle), + xSizes_(xSizes), + ySizes_(ySizes) + { + } + + int parseLscData(const YamlObject &yamlSets, + std::map &lscData) + { + const auto &sets = yamlSets.asList(); + for (const auto &yamlSet : sets) { + std::optional pr, pgr, pgb, pb; + uint32_t ct = yamlSet["ct"].get(0); + + if (lscData.count(ct)) { + LOG(RkISP1Lsc, Error) + << "Multiple sets found for " + << "color temperature " << ct; + return -EINVAL; + } + + LensShadingCorrection::Components &set = lscData[ct]; + pr = yamlSet["r"].get(); + pgr = yamlSet["gr"].get(); + pgb = yamlSet["gb"].get(); + pb = yamlSet["b"].get(); + + if (!(pr || pgr || pgb || pb)) { + LOG(RkISP1Lsc, Error) + << "Failed to parse polynomial for " + << "colour temperature " << ct; + return -EINVAL; + } + + set.ct = ct; + pr->setReferenceImageSize(sensorSize_); + pgr->setReferenceImageSize(sensorSize_); + pgb->setReferenceImageSize(sensorSize_); + pb->setReferenceImageSize(sensorSize_); + set.r = samplePolynomial(*pr); + set.gr = samplePolynomial(*pgr); + set.gb = samplePolynomial(*pgb); + set.b = samplePolynomial(*pb); + } + + if (lscData.empty()) { + LOG(RkISP1Lsc, Error) << "Failed to load any sets"; + return -EINVAL; + } + + return 0; + } + +private: + std::vector sizesListToPositions(const std::vector &sizes) + { + const int half = sizes.size(); + std::vector res(half * 2 + 1); + double x = 0.0; + + res[half] = 0.5; + for (int i = 1; i <= half; i++) { + x += sizes[half - i]; + res[half - i] = 0.5 - x; + res[half + i] = 0.5 + x; + } + + return res; + } + + std::vector samplePolynomial(const LscPolynomial &poly) + { + constexpr int k = RKISP1_CIF_ISP_LSC_SAMPLES_MAX; + + double m = poly.getM(); + double x0 = cropRectangle_.x / m; + double y0 = cropRectangle_.y / m; + double w = cropRectangle_.width / m; + double h = cropRectangle_.height / m; + std::vector res; + + assert(xSizes_.size() * 2 + 1 == k); + + res.reserve(k * k); + + std::vector xPos(sizesListToPositions(xSizes_)); + std::vector yPos(sizesListToPositions(ySizes_)); + + for (int x = 0; x < k; x++) { + for (int y = 0; y < k; y++) { + double xp = x0 + xPos[x] * w; + double yp = y0 + yPos[y] * h; + int v = static_cast( + poly.sampleAtNormalizedPixelPos(xp, yp) * + 1024); + + v = std::min(std::max(v, 1024), 4095); + res.push_back(v); + } + } + return res; + } + + Size sensorSize_; + Rectangle cropRectangle_; + const std::vector &xSizes_; + const std::vector &ySizes_; +}; + class LscClassicLoader { public: @@ -193,11 +311,17 @@ int LensShadingCorrection::init([[maybe_unused]] IPAContext &context, std::map lscData; int res = 0; std::optional type = tuningData["type"].get(); - if (!type.has_value()) { + if (!tuningData.contains("type")) { LOG(RkISP1Lsc, Warning) << "LSC data is in classic format. " << "This will be deprecated soon."; auto loader = LscClassicLoader(); res = loader.parseLscData(yamlSets, lscData); + } else if (*type == "polynomial") { + auto loader = LscPolynomialLoader(context.sensorInfo.activeAreaSize, + context.sensorInfo.analogCrop, + xSize_, + ySize_); + res = loader.parseLscData(yamlSets, lscData); } else { LOG(RkISP1Lsc, Error) << "Unsupported LSC type '" << *type << "'"; res = -EINVAL;