From patchwork Thu Feb 19 15:05:02 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26198 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 94B2AC0DA4 for ; Thu, 19 Feb 2026 15:05:17 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id AB2C562274; Thu, 19 Feb 2026 16:05:12 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="MwSoJGWV"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 14B4762251 for ; Thu, 19 Feb 2026 16:05:09 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 73D8855C; Thu, 19 Feb 2026 16:04:15 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771513455; bh=dBHuLUuIc6+R/8QxjRevUxGQdZjVWnIPBGkj+L2BXAI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=MwSoJGWVGWA1q0kRWWBOhuaBoNYS4PIVrmLyal9E3HgCGYWp/g6Axkr3dScfEf9Xg wJMcnvgtWejCfRxSBqUXXH3cUsTSdyvuKO5uH2IVtZZZGCCf5/OzXGTSY4923frgzN Gz1p3xf3ktqkBZaKpJ8Lj1LxLBrqk2/5+S1vOZ7g= From: Kieran Bingham Date: Thu, 19 Feb 2026 15:05:02 +0000 Subject: [PATCH v8 01/15] ipa: libipa: Provide a Quantized data type support MIME-Version: 1.0 Message-Id: <20260219-kbingham-quantizers-v8-1-2b6ff68ead26@ideasonboard.com> References: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> In-Reply-To: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , =?utf-8?q?Barnab?= =?utf-8?b?w6FzIFDFkWN6ZQ==?= , Isaac Scott , Paul Elder X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771513507; l=8394; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=dBHuLUuIc6+R/8QxjRevUxGQdZjVWnIPBGkj+L2BXAI=; b=bE25xL25qBG+owLZzMU78RY9GnL4aO6sXsVIKksP9C+cTgWMxldDagwXLKurn9gc8WGNxSLc3 D8En2am2wXMBd+vPyh0GNUe7FJGHYz95QvhCpNSo48jI1pwds7TDf4x X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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" Frequently when handling data in IPA components we must convert and store user interface values which may be floating point values, and perform a specific operation or conversion to quantize this to a hardware value. This value may be to a fixed point type, or more custom code mappings, but in either case it is important to contain both the required hardware value, with its effective quantized value. Provide a new storage type 'Quantized' which can be defined based on a set of type specific Traits to perform the conversions between floats and the underlying hardware type. Reviewed-by: Barnabás Pőcze Reviewed-by: Isaac Scott Reviewed-by: Paul Elder Signed-off-by: Kieran Bingham --- v8: - Remove noexcept --- src/ipa/libipa/meson.build | 2 + src/ipa/libipa/quantized.cpp | 135 +++++++++++++++++++++++++++++++++++++++++++ src/ipa/libipa/quantized.h | 75 ++++++++++++++++++++++++ 3 files changed, 212 insertions(+) diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build index 7202df869c2f..963c5ee73063 100644 --- a/src/ipa/libipa/meson.build +++ b/src/ipa/libipa/meson.build @@ -17,6 +17,7 @@ libipa_headers = files([ 'lux.h', 'module.h', 'pwl.h', + 'quantized.h', 'v4l2_params.h', ]) @@ -37,6 +38,7 @@ libipa_sources = files([ 'lux.cpp', 'module.cpp', 'pwl.cpp', + 'quantized.cpp', 'v4l2_params.cpp', ]) diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp new file mode 100644 index 000000000000..06143a97ab3e --- /dev/null +++ b/src/ipa/libipa/quantized.cpp @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2025, Ideas On Board Oy + * + * Helper class to manage conversions between floating point types and quantized + * storage and representation of those values. + */ + +#include "quantized.h" + +/** + * \file quantized.h + * \brief Quantized storage and Quantizer representations + */ + +namespace libcamera { + +namespace ipa { + +/** + * \struct libcamera::ipa::Quantized + * \brief Wrapper that stores a value in both quantized and floating-point form + * \tparam Traits The traits class defining the quantization behaviour + * + * The Quantized struct template provides a thin wrapper around a quantized + * representation of a floating-point value. It uses a traits type \a Traits + * to define the conversion policy between the floating-point domain and the + * quantized integer domain. + * + * Each Quantized instance maintains two synchronized members: + * - the quantized integer representation, and + * - the corresponding floating-point value. + * + * The traits type defines: + * - the integer storage type used for quantization, + * - the static conversion functions \c fromFloat() and \c toFloat(), and + * - optional metadata such as value ranges. + * + * Quantized provides convenient constructors and assignment operators from + * either representation, as well as comparison and string formatting utilities. + */ + +/** + * \typedef Quantized::TraitsType + * \brief The traits policy type defining the quantization behaviour + * + * Exposes the associated traits type used by this Quantized instance. + * This allows external code to refer to constants or metadata defined in + * the traits, such as \c TraitsType::min or \c TraitsType::max. + */ + +/** + * \typedef Quantized::QuantizedType + * \brief The integer type used for the quantized representation + * + * This alias corresponds to \c TraitsType::QuantizedType, as defined by + * the traits class. + */ + +/** + * \fn Quantized::Quantized(float x) + * \brief Construct a Quantized value from a floating-point number + * \param[in] x The floating-point value to be quantized + * + * Converts the floating-point input \a x to its quantized integer + * representation using the associated traits policy, and initializes + * both the quantized and floating-point members. + */ + +/** + * \fn Quantized::Quantized(QuantizedType x) + * \brief Construct a Quantized value from an existing quantized integer + * \param[in] x The quantized integer value + * + * Converts the quantized integer \a x to its corresponding floating-point + * value using the traits policy, and initializes both internal members. + */ + +/** + * \fn Quantized::operator=(float x) + * \brief Assign a floating-point value to the Quantized object + * \param[in] x The floating-point value to assign + * \return A reference to the updated Quantized object + * + * Converts the floating-point value \a x to its quantized integer + * representation using the traits policy and updates both members. + */ + +/** + * \fn Quantized::operator=(QuantizedType x) + * \brief Assign a quantized integer value to the Quantized object + * \param[in] x The quantized integer value to assign + * \return A reference to the updated Quantized object + * + * Converts the quantized integer \a x to its corresponding floating-point + * value using the traits policy and updates both members. + */ + +/** + * \fn Quantized::value() const noexcept + * \brief Retrieve the floating-point representation + * \return The floating-point value corresponding to the quantized value + */ + +/** + * \fn Quantized::quantized() const noexcept + * \brief Retrieve the quantized integer representation + * \return The quantized integer value + */ + +/** + * \fn Quantized::operator==(const Quantized &other) const noexcept + * \brief Compare two Quantized objects for equality + * \param[in] other The other Quantized object to compare against + * \return True if both objects have the same quantized integer value + */ + +/** + * \fn Quantized::operator!=(const Quantized &other) const noexcept + * \brief Compare two Quantized objects for inequality + * \param[in] other The other Quantized object to compare against + * \return True if the quantized integer values differ + */ + +/** + * \fn std::ostream &Quantized::operator<<(std::ostream &out, const Quantized &q) + * \brief Insert a text representation of a Quantized into an output stream + * \param[in] out The output stream + * \param[in] q The Quantized + * \return The output stream \a out + */ + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h new file mode 100644 index 000000000000..6bd196c7d9f2 --- /dev/null +++ b/src/ipa/libipa/quantized.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2025, Ideas On Board Oy + * + * Helper class to manage conversions between floating point types and quantized + * storage and representation of those values. + */ + +#pragma once + +#include +#include + +#include + +namespace libcamera { + +namespace ipa { + +template +struct Quantized { + using TraitsType = Traits; + using QuantizedType = typename Traits::QuantizedType; + static_assert(std::is_arithmetic_v, + "Quantized: QuantizedType must be arithmetic"); + + Quantized() + : Quantized(0.0f) {} + Quantized(float x) { *this = x; } + Quantized(QuantizedType x) { *this = x; } + + Quantized &operator=(float x) + { + quantized_ = Traits::fromFloat(x); + value_ = Traits::toFloat(quantized_); + return *this; + } + + Quantized &operator=(QuantizedType x) + { + value_ = Traits::toFloat(x); + quantized_ = x; + return *this; + } + + float value() const { return value_; } + QuantizedType quantized() const { return quantized_; } + + bool operator==(const Quantized &other) const + { + return quantized_ == other.quantized_; + } + + bool operator!=(const Quantized &other) const + { + return !(*this == other); + } + + friend std::ostream &operator<<(std::ostream &out, + const Quantized &q) + { + out << "[" << utils::hex(q.quantized()) + << ":" << q.value() << "]"; + + return out; + } + +private: + QuantizedType quantized_; + float value_; +}; + +} /* namespace ipa */ + +} /* namespace libcamera */ From patchwork Thu Feb 19 15:05:03 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26200 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 8F079C0DA4 for ; Thu, 19 Feb 2026 15:05:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 419D36226A; Thu, 19 Feb 2026 16:05:15 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="uq386DdE"; 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 6482D62254 for ; Thu, 19 Feb 2026 16:05:09 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id B6043F52; Thu, 19 Feb 2026 16:04:15 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771513455; bh=f1zkzThiM9e+EqzOBESZJHTkIUbFBF32VPJtpPWPfvc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=uq386DdEyXRwa4t/57GM01ZEfLmrutyB/zZHyjqYEMtA9h7keVehd0LTDFkspOer/ NDDq7gPQYCGzPN1bEkij7MrlqOVUiEfsMvXBQCz8R+JkQN6p3LGYyCGiQLsTLUsIsh L2G2f7B8oXkRPN5Y5MEDZMm2YKnEgPSjiuIeXviw= From: Kieran Bingham Date: Thu, 19 Feb 2026 15:05:03 +0000 Subject: [PATCH v8 02/15] test: libipa: Add tests for Quantized types MIME-Version: 1.0 Message-Id: <20260219-kbingham-quantizers-v8-2-2b6ff68ead26@ideasonboard.com> References: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> In-Reply-To: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , Isaac Scott , Paul Elder , Laurent Pinchart , Stefan Klug X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771513507; l=4765; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=f1zkzThiM9e+EqzOBESZJHTkIUbFBF32VPJtpPWPfvc=; b=9oh/u9y+TbIaWTFeWHauEPkyZNwXU5kXbkeBMFtJhg5kjh3Fdv3I/0BVLszuyWY/sH9PmoeOF MurqUDqJILMAKXxx3CIjoSy9OHS5wm2uRwwrxiBN1HNQ7MSWx9pHwBj X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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" Provide use case tests for the Quantized types to ensure construction and usages are consistent and work as expected. Reviewed-by: Isaac Scott Reviewed-by: Paul Elder Reviewed-by: Laurent Pinchart Reviewed-by: Stefan Klug Signed-off-by: Kieran Bingham --- v8: - Only use uint8_t - Don't output on success --- test/ipa/libipa/meson.build | 1 + test/ipa/libipa/quantized.cpp | 145 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build index 2070bed70222..c3e255871f4f 100644 --- a/test/ipa/libipa/meson.build +++ b/test/ipa/libipa/meson.build @@ -5,6 +5,7 @@ libipa_test = [ {'name': 'histogram', 'sources': ['histogram.cpp']}, {'name': 'interpolator', 'sources': ['interpolator.cpp']}, {'name': 'pwl', 'sources': ['pwl.cpp'] }, + {'name': 'quantized', 'sources': ['quantized.cpp']}, ] foreach test : libipa_test diff --git a/test/ipa/libipa/quantized.cpp b/test/ipa/libipa/quantized.cpp new file mode 100644 index 000000000000..f138597f273d --- /dev/null +++ b/test/ipa/libipa/quantized.cpp @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2026, Ideas on Board + * + * Dual Type and Quantizer tests + */ + +#include "../src/ipa/libipa/quantized.h" + +#include +#include +#include +#include + +#include "test.h" + +using namespace std; +using namespace libcamera; +using namespace ipa; + +struct BrightnessHueTraits { + using QuantizedType = uint8_t; + static QuantizedType fromFloat(float v) + { + int quantized = std::lround(v * 128.0f); + return std::clamp(quantized, -128, 127); + } + static float toFloat(QuantizedType v) + { + return static_cast(v) / 128.0f; + } +}; + +using BrightnessHueQuantizer = Quantized; + +struct ContrastSaturationTraits { + using QuantizedType = uint8_t; + static QuantizedType fromFloat(float v) + { + int quantized = std::lround(v * 128.0f); + return std::clamp(quantized, 0, 255); + } + static float toFloat(QuantizedType v) + { + return static_cast(v) / 128.0f; + } +}; + +using ContrastSaturationQuantizer = Quantized; + +using BrightnessQ = BrightnessHueQuantizer; +using HueQ = BrightnessHueQuantizer; +using ContrastQ = ContrastSaturationQuantizer; +using SaturationQ = ContrastSaturationQuantizer; + +class QuantizedTest : public Test +{ +protected: + int run() + { + /* Test construction from float */ + { + BrightnessQ b(0.5f); + if (b.quantized() != 64 || std::abs(b.value() - 0.5f) > 0.01f) + return TestFail; + } + + /* Test construction from T */ + { + ContrastQ c(uint8_t(128)); + if (c.quantized() != 128 || std::abs(c.value() - 1.0f) > 0.01f) + return TestFail; + } + + /* + * Only construction from the exact storage type or a float + * is permitted. + */ + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + + /* Test equality */ + { + BrightnessQ b1(0.5f), b2(uint8_t(64)); + if (!(b1 == b2)) + return TestFail; + } + + /* Test inequality */ + { + BrightnessQ b1(0.5f), b2(-0.5f); + if (!(b1 != b2)) + return TestFail; + } + + /* Test copying */ + { + BrightnessQ b1(0.25f); + BrightnessQ b2 = b1; + if (!(b1 == b2)) + return TestFail; + } + + /* Test assignment */ + { + ContrastQ c1(1.5f); + ContrastQ c2(0.0f); + c2 = c1; + if (!(c1 == c2)) + return TestFail; + } + + /* + * Test construction from different floats mapping to same + * quantized value + */ + { + /* Two floats that have the same quantized value. */ + const float f1 = 1.007f; + const float f2 = 1.008f; + + ContrastQ c1(f1); + ContrastQ c2(f2); + + /* Quantized values must match */ + if (!(c1.quantized() == c2.quantized())) + return TestFail; + + /* Float values must now match */ + if (!(c1.value() == c2.value())) + return TestFail; + + if (!(c1 == c2)) + return TestFail; + } + + return TestPass; + } +}; + +TEST_REGISTER(QuantizedTest) From patchwork Thu Feb 19 15:05:04 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26199 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 D62FAC0DA4 for ; Thu, 19 Feb 2026 15:05:19 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0E1FD62277; Thu, 19 Feb 2026 16:05:14 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="XlIzQ0zs"; 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 5359C62253 for ; Thu, 19 Feb 2026 16:05:09 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 0A55DFCC; Thu, 19 Feb 2026 16:04:16 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771513456; bh=0w7k+nsaptyC4B22GJj31eSBRHS0E9X8RAxn0D4HPjg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=XlIzQ0zsILsdyVrlY1Tpz+L4Ul3e2TMChCs+mcjmyMCtEpkFCokUeeBg2gd4xVoeg MKNHjdPvBueSJg7qNnfEwsrhTd42SM8PHayPaflzNiIGuAt8/Dq7Pm9oRGCYHM6PGE 6HVldg0a+lppjktBNTgH8OIEKi9zfn5lsdflxonw= From: Kieran Bingham Date: Thu, 19 Feb 2026 15:05:04 +0000 Subject: [PATCH v8 03/15] ipa: libipa: fixedpoint: Fix unsigned usage MIME-Version: 1.0 Message-Id: <20260219-kbingham-quantizers-v8-3-2b6ff68ead26@ideasonboard.com> References: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> In-Reply-To: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , Isaac Scott , Paul Elder , Laurent Pinchart X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771513507; l=2457; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=0w7k+nsaptyC4B22GJj31eSBRHS0E9X8RAxn0D4HPjg=; b=vfIN6Mwmybo7FoTLJ6FwlWO40OhB/O6U4EiPFYj9tpAcjc4U2hwZ++SMh9yr4qIg6/Q4YeW3m nnxxHqQVblqDf+xPNZ94vaAUYB1x4KR0fVeE/UBDh/kP5t4fBTaa+Gp X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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 fixedToFloatingPoint does not support unsigned Q types, and incorrectly sign-extends all values which have the top most bit set in the quantized values. Fix this by ensuring that only signed types perform sign extension, and simplify the calculation for unsigned types. Convert the storage of the test cases to signed types to correctly represent their intended purpose, to prevent test failures. Reviewed-by: Isaac Scott Reviewed-by: Paul Elder Reviewed-by: Laurent Pinchart Signed-off-by: Kieran Bingham --- src/ipa/libipa/fixedpoint.h | 3 +++ test/ipa/libipa/fixedpoint.cpp | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ipa/libipa/fixedpoint.h b/src/ipa/libipa/fixedpoint.h index 709cf50f0fcd..aeb9bce3269b 100644 --- a/src/ipa/libipa/fixedpoint.h +++ b/src/ipa/libipa/fixedpoint.h @@ -49,6 +49,9 @@ constexpr R fixedToFloatingPoint(T number) static_assert(sizeof(int) >= sizeof(T)); static_assert(I + F <= sizeof(T) * 8); + if constexpr (std::is_unsigned_v) + return static_cast(number) / static_cast(T{ 1 } << F); + /* * Recreate the upper bits in case of a negative number by shifting the sign * bit from the fixed point to the first bit of the unsigned and then right shifting diff --git a/test/ipa/libipa/fixedpoint.cpp b/test/ipa/libipa/fixedpoint.cpp index 99eb662ddf4e..4b017e86a74f 100644 --- a/test/ipa/libipa/fixedpoint.cpp +++ b/test/ipa/libipa/fixedpoint.cpp @@ -68,7 +68,7 @@ protected: * The second 7.992 test is to test that unused bits don't * affect the result. */ - std::map testCases = { + std::map testCases = { { 7.992, 0x3ff }, { 0.2, 0x01a }, { -0.2, 0x7e6 }, @@ -81,14 +81,14 @@ protected: int ret; for (const auto &testCase : testCases) { - ret = testSingleFixedPoint<4, 7, uint16_t>(testCase.first, + ret = testSingleFixedPoint<4, 7, int16_t>(testCase.first, testCase.second); if (ret != TestPass) return ret; } /* Special case with a superfluous one in the unused bits */ - ret = testFixedToFloat<4, 7, uint16_t, double>(0xbff, 7.992); + ret = testFixedToFloat<4, 7, int16_t, double>(0xbff, 7.992); if (ret != TestPass) return ret; From patchwork Thu Feb 19 15:05:05 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26201 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 AB882C0DA4 for ; Thu, 19 Feb 2026 15:05:22 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 26DF662268; Thu, 19 Feb 2026 16:05:16 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="syVmGh7b"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 84C8262255 for ; Thu, 19 Feb 2026 16:05:09 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 549F11023; Thu, 19 Feb 2026 16:04:16 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771513456; bh=OLNJoiHLmmmIIuIvF7pWFX26DPsb+CGOkVU0InYRLUE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=syVmGh7bAO4VBh2bm9/ogXKXIymshZLCHYQ1KhzyJmeupy/zK3U+BCKFRZ8Uewxxr Jiv1J8VIMJTVsKsce/L/T3AdnThyhzCzG5H5Es3T/k7pSw3fr0cVPywOo1q39V32uA BHjOj751vh4b3XFYZuVjBEdAGKNjOoABF3o+3wuk= From: Kieran Bingham Date: Thu, 19 Feb 2026 15:05:05 +0000 Subject: [PATCH v8 04/15] ipa: libipa: Provide fixed point quantized traits MIME-Version: 1.0 Message-Id: <20260219-kbingham-quantizers-v8-4-2b6ff68ead26@ideasonboard.com> References: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> In-Reply-To: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , Isaac Scott , Paul Elder , Stefan Klug X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771513507; l=7570; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=OLNJoiHLmmmIIuIvF7pWFX26DPsb+CGOkVU0InYRLUE=; b=a4oNA3kjlrp/bvUyt+hSgxgRYBddct6pV9AXOi37QMxp9Df6cHFC8QIJVNxy7RGM28XCbYkeS Bq1fOO3ViBxB/Hk3HfUOkkwrkZRcoiBnbbcOvd8m7jlL4ymZfIfGnfI X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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" Extend the new Quantized type infrastructure by providing a FixedPointQTraits template. This allows construction of fixed point types with a Quantized storage that allows easy reading of both the underlying quantized type value and a floating point representation of that same value. Reviewed-by: Isaac Scott Reviewed-by: Paul Elder Reviewed-by: Stefan Klug Signed-off-by: Kieran Bingham --- src/ipa/libipa/fixedpoint.cpp | 98 +++++++++++++++++++++++++++++++++++++++++++ src/ipa/libipa/fixedpoint.h | 74 ++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp index 6b698fc5d680..b581ca8453ef 100644 --- a/src/ipa/libipa/fixedpoint.cpp +++ b/src/ipa/libipa/fixedpoint.cpp @@ -37,6 +37,104 @@ namespace ipa { * \return The converted value */ +/** + * \struct libcamera::ipa::FixedPointQTraits + * \brief Traits type implementing fixed-point quantization conversions + * + * The FixedPointQTraits structure defines a policy for mapping floating-point + * values to and from fixed-point 2's complement integer representations. It is + * parameterised by the number of integer bits \a I, fractional bits \a F, and + * the integral storage type \a T. The traits are used with Quantized to + * create a quantized type that stores both the fixed-point representation and + * the corresponding floating-point value. + * + * The signedness of the type is determined by the signedness of \a T. For + * signed types, the number of integer bits in \a I includes the sign bit. + * + * Storage is determined by the total number of bits \a (I + F) and is + * automatically selected, but the internal storage type is always an unsigned + * integer to guarantee against sign extension when storing quantized values + * in registers. + * + * The trait exposes compile-time constants describing the bit layout, limits, + * and scaling factors used in the fixed-point representation. + * + * \tparam I Number of integer bits + * \tparam F Number of fractional bits + * \tparam T Integral type used to store the quantized value + */ + +/** + * \typedef FixedPointQTraits::QuantizedType + * \brief The integral storage type used for the fixed-point representation + */ + +/** + * \var FixedPointQTraits::qMin + * \brief Minimum representable quantized integer value + * + * This corresponds to the most negative value for signed formats or zero for + * unsigned formats. + */ + +/** + * \var FixedPointQTraits::qMax + * \brief Maximum representable quantized integer value + */ + +/** + * \var FixedPointQTraits::min + * \brief Minimum representable floating-point value corresponding to qMin + */ + +/** + * \var FixedPointQTraits::max + * \brief Maximum representable floating-point value corresponding to qMax + */ + +/** + * \fn FixedPointQTraits::fromFloat(float v) + * \brief Convert a floating-point value to a fixed-point integer + * \param[in] v The floating-point value to be converted + * \return The quantized fixed-point integer representation + * + * The conversion first clamps the floating-point input \a v to the range [min, + * max] and then rounds it to the nearest fixed-point value according to the + * scaling factor defined by the number of fractional bits F. + */ + +/** + * \fn FixedPointQTraits::toFloat(QuantizedType q) + * \brief Convert a fixed-point integer to a floating-point value + * \param[in] q The fixed-point integer value to be converted + * \return The corresponding floating-point value + * + * The conversion sign-extends the integer value if required and divides by the + * scaling factor defined by the number of fractional bits F. + */ + +/** + * \typedef Q + * \brief Define a signed fixed-point quantized type with automatic storage width + * \tparam I The number of integer bits + * \tparam F The number of fractional bits + * + * This alias defines a signed fixed-point quantized type using the + * \ref FixedPointQTraits trait and a suitable signed integer storage type + * automatically selected based on the total number of bits \a (I + F). + */ + +/** + * \typedef UQ + * \brief Define an unsigned fixed-point quantized type with automatic storage width + * \tparam I The number of integer bits + * \tparam F The number of fractional bits + * + * This alias defines an unsigned fixed-point quantized type using the + * \ref FixedPointQTraits trait and a suitable unsigned integer storage type + * automatically selected based on the total number of bits \a (I + F). + */ + } /* namespace ipa */ } /* namespace libcamera */ diff --git a/src/ipa/libipa/fixedpoint.h b/src/ipa/libipa/fixedpoint.h index aeb9bce3269b..df685e852bff 100644 --- a/src/ipa/libipa/fixedpoint.h +++ b/src/ipa/libipa/fixedpoint.h @@ -10,6 +10,8 @@ #include #include +#include "quantized.h" + namespace libcamera { namespace ipa { @@ -63,6 +65,78 @@ constexpr R fixedToFloatingPoint(T number) return static_cast(t) / static_cast(1 << F); } +template +struct FixedPointQTraits { +private: + static_assert(std::is_integral_v, "FixedPointQTraits: T must be integral"); + using UT = std::make_unsigned_t; + + static constexpr unsigned int bits = I + F; + static_assert(bits <= sizeof(UT) * 8, "FixedPointQTraits: too many bits for type UT"); + + /* + * If fixed point storage is required with more than 24 bits, consider + * updating this implementation to use double-precision floating point. + */ + static_assert(bits <= 24, "Floating point precision may be insufficient for more than 24 bits"); + + static constexpr UT bitMask = bits < sizeof(UT) * 8 + ? (UT{ 1 } << bits) - 1 + : ~UT{ 0 }; + +public: + using QuantizedType = UT; + + static constexpr UT qMin = std::is_signed_v + ? -(UT{ 1 } << (bits - 1)) + : 0; + + static constexpr UT qMax = std::is_signed_v + ? (UT{ 1 } << (bits - 1)) - 1 + : bitMask; + + static constexpr float toFloat(QuantizedType q) + { + return fixedToFloatingPoint(q); + } + + static constexpr float min = fixedToFloatingPoint(static_cast(qMin)); + static constexpr float max = fixedToFloatingPoint(static_cast(qMax)); + + static_assert(min < max, "FixedPointQTraits: Minimum must be less than maximum"); + + /* Conversion functions required by Quantized */ + static QuantizedType fromFloat(float v) + { + v = std::clamp(v, min, max); + return floatingToFixedPoint(v); + } +}; + +namespace details { + +template +constexpr auto qtype() +{ + static_assert(Bits <= 32, + "Unsupported number of bits for quantized type"); + + if constexpr (Bits <= 8) + return int8_t(); + else if constexpr (Bits <= 16) + return int16_t(); + else if constexpr (Bits <= 32) + return int32_t(); +} + +} /* namespace details */ + +template +using Q = Quantized())>>; + +template +using UQ = Quantized())>>>; + } /* namespace ipa */ } /* namespace libcamera */ From patchwork Thu Feb 19 15:05:06 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26202 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 AA12CC3240 for ; Thu, 19 Feb 2026 15:05:23 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 32B9162271; Thu, 19 Feb 2026 16:05:17 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="BSeexMje"; 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 C11D562250 for ; Thu, 19 Feb 2026 16:05:09 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 9CCA4195E; Thu, 19 Feb 2026 16:04:16 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771513456; bh=nwSRBBpIh9fZCssqHXUziQx+gihbPtauF8x+GSnBBGQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=BSeexMjeBKKAmY+2iTjvlHUmilG9/jZQ+hrgzZlOyfJWHs1rLuUoIKMNU2uT35q2r ghddgqQuavoNZQbvftofMtsaeQpFWIBws8KllRfR4P86wBBK3/AN835k1+lhlfu4M3 E6nmTk6BDx95tahk9SsuLj6fa3zR3YxHmleUtPGY= From: Kieran Bingham Date: Thu, 19 Feb 2026 15:05:06 +0000 Subject: [PATCH v8 05/15] test: libipa: Provide FixedPoint Quantized tests MIME-Version: 1.0 Message-Id: <20260219-kbingham-quantizers-v8-5-2b6ff68ead26@ideasonboard.com> References: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> In-Reply-To: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , Paul Elder , Stefan Klug , Isaac Scott X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771513507; l=9386; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=nwSRBBpIh9fZCssqHXUziQx+gihbPtauF8x+GSnBBGQ=; b=//qnpi9NcvIB/dii+ObF42XIH9ROLu2bt7KtCgSWr1BdcXIXZx+PKSIeC+U2EFrOpZkj3Sm88 z1l5YqdAQ4nDK2Ay1YD/iWGjUlz3TUmNdmLthdYcsWKHOY6TG2XYHCG X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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" Provide tests to validate the conditions of FixedPoint types used within libcamera explicitly. Reviewed-by: Paul Elder Reviewed-by: Stefan Klug Reviewed-by: Isaac Scott Signed-off-by: Kieran Bingham --- test/ipa/libipa/fixedpoint.cpp | 209 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 205 insertions(+), 4 deletions(-) diff --git a/test/ipa/libipa/fixedpoint.cpp b/test/ipa/libipa/fixedpoint.cpp index 4b017e86a74f..2907426b7a69 100644 --- a/test/ipa/libipa/fixedpoint.cpp +++ b/test/ipa/libipa/fixedpoint.cpp @@ -5,12 +5,14 @@ * Fixed / Floating point utility tests */ +#include "../src/ipa/libipa/fixedpoint.h" + #include #include #include #include -#include "../src/ipa/libipa/fixedpoint.h" +#include #include "test.h" @@ -95,14 +97,213 @@ protected: return TestPass; } - int run() + template + int quantizedCheck(float input, typename Q::QuantizedType expected, float value) { - /* fixed point conversion test */ - if (testFixedPoint() != TestPass) + Q q(input); + using T = typename Q::QuantizedType; + + std::cout << " Checking " << input << " == " << q << std::endl; + + T quantized = q.quantized(); + if (quantized != expected) { + std::cout << " ** Q Expected " << input + << " to quantize to " << utils::hex(expected) + << ", got " << utils::hex(quantized) + << " - (" << q << ")" + << std::endl; + return 1; + } + + if ((std::abs(q.value() - value)) > 0.0001f) { + std::cout << " ** V Expected " << input + << " to quantize to " << value + << ", got " << q.value() + << " - (" << q << ")" + << std::endl; + return 1; + } + + return 0; + } + + template + int signExtendCheck() + { + Q minValue(Q::TraitsType::min); + using T = typename Q::QuantizedType; + T minQuantized = minValue.quantized(); + + /* Take the value and expand it into a larger register */ + RegisterType reg = static_cast(minValue.quantized()); + + std::cout << " Checking " << minValue << " == " << utils::hex(reg) << std::endl; + + /* + * Ensure that the minimum negative value is not sign + * extended when casting up to a larger register storage + * type. + */ + if (reg != minQuantized) { + std::cout << " ** Sign extension check failed for min value. Expected " + << utils::hex(minQuantized) << ", got " + << utils::hex(reg) << std::endl; + return 1; + } + + /* And we should be consistent when we convert back to Q */ + Q q2(static_cast(reg)); + if (!(q2 == minValue)) { + std::cout << " ** Sign extension check failed for min value. Expected " + << minValue << ", got " << q2 << std::endl; + return 1; + } + + return 0; + } + + template + void introduce(std::string_view type) + { + using T = typename Q::QuantizedType; + + std::cout << std::endl; + + std::cout << type + << "(" << Q::TraitsType::min << " .. " << Q::TraitsType::max << ") " + << " Min: " << Q(Q::TraitsType::min) + << " -- Max: " << Q(Q::TraitsType::max) + << " Step:" << Q(T(1)).value() + << std::endl; + } + + int testFixedPointQuantizers() + { + unsigned int fails = 0; + + /* + * These aim to specifically test all the corner cases of the + * quantization and de-quantization process. Including clamping + * to min/max, zero points and making sure that steps are + * correct. + * + * In particular test signed and unsigned types and a mix of the + * highest bit width of a storage type and smaller widths that + * require bit masking and sign extension. + * + * Note we must hard code these. Any calculation of expected + * values risks replicating bugs in the implementation. + * + * As the underlying types are integer and float the limit of + * precision is around 24 bits so we do not test wider types. + */ + + /* clang-format off */ + + /* Q1.7(-1 .. 0.992188) Min: [0x80:-1] -- Max: [0x7f:0.992188] Step:0.0078125*/ + introduce>("Q1.7"); + fails += quantizedCheck>(-2.000f, 0b1'0000000, -1.0f); /* Clamped to Min */ + fails += quantizedCheck>(-1.000f, 0b1'0000000, -1.0f); /* Min */ + fails += quantizedCheck>(-0.992f, 0b1'0000001, -0.992188f); /* Min + 1 step */ + fails += quantizedCheck>(-0.006f, 0b1'1111111, -0.0078125f); /* -1 step */ + fails += quantizedCheck>( 0.000f, 0b0'0000000, 0.0f); /* Zero */ + fails += quantizedCheck>( 0.008f, 0b0'0000001, 0.0078125f); /* +1 step */ + fails += quantizedCheck>( 0.992f, 0b0'1111111, 0.992188f); /* Max */ + fails += quantizedCheck>( 2.000f, 0b0'1111111, 0.992188f); /* Clamped to Max */ + + /* UQ1.7(0 .. 1.99219) Min: [0x00:0] -- Max: [0xff:1.99219] Step:0.0078125 */ + introduce>("UQ1.7"); + fails += quantizedCheck>(-1.0f, 0b0'0000000, 0.0f); /* Clamped to Min */ + fails += quantizedCheck>( 0.0f, 0b0'0000000, 0.0f); /* Min / Zero */ + fails += quantizedCheck>( 1.0f, 0b1'0000000, 1.0f); /* Mid */ + fails += quantizedCheck>( 1.992f, 0b1'1111111, 1.99219f); /* Max */ + fails += quantizedCheck>( 2.000f, 0b1'1111111, 1.99219f); /* Clamped to Max */ + + /* UQ4.8(0 .. 15.9961) Min: [0x0000:0] -- Max: [0x0fff:15.9961] Step:0.00390625 */ + introduce>("UQ4.8"); + fails += quantizedCheck>( 0.0f, 0b0000'00000000, 0.00f); + fails += quantizedCheck>(16.0f, 0b1111'11111111, 15.9961f); + + /* Q5.4(-16 .. 15.9375) Min: [0x0100:-16] -- Max: [0x00ff:15.9375] Step:0.0625 */ + introduce>("Q5.4"); + fails += quantizedCheck>(-16.00f, 0b10000'0000, -16.00f); + fails += quantizedCheck>( 15.94f, 0b01111'1111, 15.9375f); + + /* UQ5.8(0 .. 31.9961) Min: [0x0000:0] -- Max: [0x1fff:31.9961] Step:0.00390625 */ + introduce>("UQ5.8"); + fails += quantizedCheck>( 0.00f, 0b00000'00000000, 0.00f); + fails += quantizedCheck>(32.00f, 0b11111'11111111, 31.9961f); + + /* Q12.4(-2048 .. 2047.94) Min: [0x8000:-2048] -- Max: [0x7fff:2047.94] Step:0.0625 */ + introduce>("Q12.4"); + fails += quantizedCheck>( 0.0f, 0b000000000000'0000, 0.0f); + fails += quantizedCheck>( 7.5f, 0b000000000111'1000, 7.5f); + + /* UQ12.4(0 .. 4095.94) Min: [0x0000:0] -- Max: [0xffff:4095.94] Step:0.0625 */ + introduce>("UQ12.4"); + fails += quantizedCheck>(0.0f, 0b000000000000'0000, 0.0f); + fails += quantizedCheck>(7.5f, 0b000000000111'1000, 7.5f); + + /* Q4.20 */ + introduce>("Q4.20"); + fails += quantizedCheck>( -9.0f, 0b1000'00000000000000000000, -8.0f); + fails += quantizedCheck>( -8.0f, 0b1000'00000000000000000000, -8.0f); + fails += quantizedCheck>( 8.0f, 0b0111'11111111111111111111, 8.0f); + fails += quantizedCheck>( 9.0f, 0b0111'11111111111111111111, 8.0f); + + /* UQ4.20 */ + introduce>("UQ4.20"); + fails += quantizedCheck>(-1.0f, 0b0000'00000000000000000000, 0.0f); + fails += quantizedCheck>( 0.0f, 0b0000'00000000000000000000, 0.0f); + fails += quantizedCheck>(16.0f, 0b1111'11111111111111111111, 16.0f); + fails += quantizedCheck>(20.0f, 0b1111'11111111111111111111, 16.0f); + + /* Storage selection tests */ + static_assert(std::is_same_v::TraitsType::QuantizedType, uint8_t>); + static_assert(std::is_same_v::TraitsType::QuantizedType, uint8_t>); + static_assert(std::is_same_v::TraitsType::QuantizedType, uint16_t>); + static_assert(std::is_same_v::TraitsType::QuantizedType, uint16_t>); + static_assert(std::is_same_v::TraitsType::QuantizedType, uint32_t>); + static_assert(std::is_same_v::TraitsType::QuantizedType, uint32_t>); + + /* + * Test and validate that sign extension can not modify a + * quantized value when stored or cast to a larger register. + */ + std::cout << std::endl; + std::cout << "Testing sign extension of quantized values when cast to larger registers" << std::endl; + fails += signExtendCheck, uint8_t>(); + fails += signExtendCheck, uint8_t>(); + fails += signExtendCheck, uint16_t>(); + fails += signExtendCheck, uint16_t>(); + fails += signExtendCheck, uint32_t>(); + + /* clang-format on */ + + std::cout << std::endl; + + if (fails > 0) { + cout << "Fixed point quantizer tests failed: " + << std::dec << fails << " failures." << std::endl; return TestFail; + } return TestPass; } + + int run() + { + unsigned int fails = 0; + + /* fixed point conversion test */ + if (testFixedPoint() != TestPass) + fails++; + + if (testFixedPointQuantizers() != TestPass) + fails++; + + return fails ? TestFail : TestPass; + } }; TEST_REGISTER(FixedPointUtilsTest) From patchwork Thu Feb 19 15:05:07 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26203 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 973F3C0DA4 for ; Thu, 19 Feb 2026 15:05:24 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E028462267; Thu, 19 Feb 2026 16:05:17 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="u6a51fek"; 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 23BCA62256 for ; Thu, 19 Feb 2026 16:05:10 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id EB7ED19A8; Thu, 19 Feb 2026 16:04:16 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771513457; bh=CRMX1wVhgL2atyvM+zrnGPqIHbnsdykYtN2tLS62HGk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=u6a51fekZF+7CxMceebuU3UtNjbHDH0Q6y4aRZ08YfQUI25zvKs4V7H9k7DUXqG/p V444attwYSSeKjhuxYfPpSsKhWHs5teCPo0iagi1NdE9yLl7LjFEyi+1VoIIyUFqqR JzILJ5FmObZbtXGXgFvRHa8zHcWT9QMqgxcOS0d8= From: Kieran Bingham Date: Thu, 19 Feb 2026 15:05:07 +0000 Subject: [PATCH v8 06/15] ipa: rkisp1: cproc: Convert to use Quantized types MIME-Version: 1.0 Message-Id: <20260219-kbingham-quantizers-v8-6-2b6ff68ead26@ideasonboard.com> References: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> In-Reply-To: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , Isaac Scott , Laurent Pinchart , Paul Elder X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771513507; l=4735; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=CRMX1wVhgL2atyvM+zrnGPqIHbnsdykYtN2tLS62HGk=; b=CQHZqNSNahlA7gKqSg2jsUVzMqsRcinKndS5uBuZDtH4dGy2IM+VaXn19/GtiEgM29/9i0Vay pAQEVKpEYCBCACvIKsNRCfWKlPAgv8i8ptFN8z5nU+XHz8EcvySMduT X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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" Convert the Brightness, Contrast and Saturation helper functions to a Quantizer type to support maintaining the data. While modifying the include blocks of the ipa_context.h, also fix the include style for libipa components. Reviewed-by: Isaac Scott Reviewed-by: Laurent Pinchart Reviewed-by: Paul Elder Signed-off-by: Kieran Bingham --- src/ipa/rkisp1/algorithms/cproc.cpp | 28 +++++++++------------------- src/ipa/rkisp1/ipa_context.h | 23 +++++++++++++++-------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp index d1fff6990d37..4374c08c2a4f 100644 --- a/src/ipa/rkisp1/algorithms/cproc.cpp +++ b/src/ipa/rkisp1/algorithms/cproc.cpp @@ -39,16 +39,6 @@ constexpr float kDefaultBrightness = 0.0f; constexpr float kDefaultContrast = 1.0f; constexpr float kDefaultSaturation = 1.0f; -int convertBrightness(const float v) -{ - return std::clamp(std::lround(v * 128), -128, 127); -} - -int convertContrastOrSaturation(const float v) -{ - return std::clamp(std::lround(v * 128), 0, 255); -} - } /* namespace */ /** @@ -74,9 +64,9 @@ int ColorProcessing::configure(IPAContext &context, { auto &cproc = context.activeState.cproc; - cproc.brightness = convertBrightness(kDefaultBrightness); - cproc.contrast = convertContrastOrSaturation(kDefaultContrast); - cproc.saturation = convertContrastOrSaturation(kDefaultSaturation); + cproc.brightness = BrightnessQ(kDefaultBrightness); + cproc.contrast = ContrastQ(kDefaultContrast); + cproc.saturation = SaturationQ(kDefaultSaturation); return 0; } @@ -97,7 +87,7 @@ void ColorProcessing::queueRequest(IPAContext &context, const auto &brightness = controls.get(controls::Brightness); if (brightness) { - int value = convertBrightness(*brightness); + BrightnessQ value = *brightness; if (cproc.brightness != value) { cproc.brightness = value; update = true; @@ -108,7 +98,7 @@ void ColorProcessing::queueRequest(IPAContext &context, const auto &contrast = controls.get(controls::Contrast); if (contrast) { - int value = convertContrastOrSaturation(*contrast); + ContrastQ value = *contrast; if (cproc.contrast != value) { cproc.contrast = value; update = true; @@ -119,7 +109,7 @@ void ColorProcessing::queueRequest(IPAContext &context, const auto saturation = controls.get(controls::Saturation); if (saturation) { - int value = convertContrastOrSaturation(*saturation); + SaturationQ value = *saturation; if (cproc.saturation != value) { cproc.saturation = value; update = true; @@ -148,9 +138,9 @@ void ColorProcessing::prepare([[maybe_unused]] IPAContext &context, auto config = params->block(); config.setEnabled(true); - config->brightness = frameContext.cproc.brightness; - config->contrast = frameContext.cproc.contrast; - config->sat = frameContext.cproc.saturation; + config->brightness = frameContext.cproc.brightness.quantized(); + config->contrast = frameContext.cproc.contrast.quantized(); + config->sat = frameContext.cproc.saturation.quantized(); } REGISTER_IPA_ALGORITHM(ColorProcessing, "ColorProcessing") diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index fa748811be74..cca5e281c732 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -24,14 +24,20 @@ #include "libcamera/internal/matrix.h" #include "libcamera/internal/vector.h" -#include -#include #include "libipa/agc_mean_luminance.h" +#include "libipa/camera_sensor_helper.h" +#include "libipa/fc_queue.h" +#include "libipa/fixedpoint.h" namespace libcamera { namespace ipa::rkisp1 { +/* Fixed point types used by CPROC */ +using BrightnessQ = Q<1, 7>; +using ContrastQ = UQ<1, 7>; +using SaturationQ = UQ<1, 7>; + struct IPAHwSettings { unsigned int numAeCells; unsigned int numHistogramBins; @@ -111,9 +117,9 @@ struct IPAActiveState { } ccm; struct { - int8_t brightness; - uint8_t contrast; - uint8_t saturation; + BrightnessQ brightness; + ContrastQ contrast; + SaturationQ saturation; } cproc; struct { @@ -173,9 +179,10 @@ struct IPAFrameContext : public FrameContext { } awb; struct { - int8_t brightness; - uint8_t contrast; - uint8_t saturation; + BrightnessQ brightness; + ContrastQ contrast; + SaturationQ saturation; + bool update; } cproc; From patchwork Thu Feb 19 15:05:08 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26204 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 9B0D1C3240 for ; Thu, 19 Feb 2026 15:05:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B1AEC6226F; Thu, 19 Feb 2026 16:05:18 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="DFFyAHGq"; 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 6E2466225C for ; Thu, 19 Feb 2026 16:05:10 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 4765619D6; Thu, 19 Feb 2026 16:04:17 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771513457; bh=od9HXa0RvmhehZR62vGgqnqMUNop+bUsUvJ0D8wvKB0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=DFFyAHGq8jGu1f+1DGFSzR59FGDW208cJNjGqOAVpDKLPN76pW5874u6yuFa4dCvC 4vhypIC/38nEslsQDvfzsEuzHshVrc/GFnBkSXpbGmQ7dqkXMe309MqM+a2SauzYOU d+PhZYIDeTv1JSx5aAfaPR2LCLuLdidVNdJdfa84= From: Kieran Bingham Date: Thu, 19 Feb 2026 15:05:08 +0000 Subject: [PATCH v8 07/15] ipa: rkisp1: cproc: Report metadata MIME-Version: 1.0 Message-Id: <20260219-kbingham-quantizers-v8-7-2b6ff68ead26@ideasonboard.com> References: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> In-Reply-To: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , Isaac Scott , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= , Laurent Pinchart , Paul Elder X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771513507; l=2534; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=od9HXa0RvmhehZR62vGgqnqMUNop+bUsUvJ0D8wvKB0=; b=RZ5M1B8pCVG1iYAjg4w/fup7fTjSn0jwltOlOroj8hSiXIMe7Lq99jXSrFbMRD3jZMntkkSld XDLDTlzipzYB6VUScCiuK7Yea+DPW/tNzS6faHT3me/H+utZset4Gww X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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" Presently the colour processing component exposes controls for brightness, saturation, and contrast to the applications which are handled and processed accordingly on the ISP. The implementation lacks reporting the values that are set back to the application. Utilise the new Quantised types to provide the values that were applied to the hardware and report them in the completed request metadata. Reviewed-by: Isaac Scott Reviewed-by: Barnabás Pőcze Reviewed-by: Laurent Pinchart Reviewed-by: Paul Elder Signed-off-by: Kieran Bingham --- src/ipa/rkisp1/algorithms/cproc.cpp | 14 ++++++++++++++ src/ipa/rkisp1/algorithms/cproc.h | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp index 4374c08c2a4f..e9e2b5444bc9 100644 --- a/src/ipa/rkisp1/algorithms/cproc.cpp +++ b/src/ipa/rkisp1/algorithms/cproc.cpp @@ -143,6 +143,20 @@ void ColorProcessing::prepare([[maybe_unused]] IPAContext &context, config->sat = frameContext.cproc.saturation.quantized(); } +/** + * \copydoc libcamera::ipa::Algorithm::process + */ +void ColorProcessing::process([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + [[maybe_unused]] const rkisp1_stat_buffer *stats, + ControlList &metadata) +{ + metadata.set(controls::Brightness, frameContext.cproc.brightness.value()); + metadata.set(controls::Contrast, frameContext.cproc.contrast.value()); + metadata.set(controls::Saturation, frameContext.cproc.saturation.value()); +} + REGISTER_IPA_ALGORITHM(ColorProcessing, "ColorProcessing") } /* namespace ipa::rkisp1::algorithms */ diff --git a/src/ipa/rkisp1/algorithms/cproc.h b/src/ipa/rkisp1/algorithms/cproc.h index fd38fd17e8bb..9b589ebd4ad7 100644 --- a/src/ipa/rkisp1/algorithms/cproc.h +++ b/src/ipa/rkisp1/algorithms/cproc.h @@ -30,6 +30,10 @@ public: void prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, RkISP1Params *params) override; + void process(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const rkisp1_stat_buffer *stats, + ControlList &metadata) override; }; } /* namespace ipa::rkisp1::algorithms */ From patchwork Thu Feb 19 15:05:09 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26205 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 64909C32EA for ; Thu, 19 Feb 2026 15:05:26 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5C58C62294; Thu, 19 Feb 2026 16:05:19 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="N0hd7bdN"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CB8FE62266 for ; Thu, 19 Feb 2026 16:05:10 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 946B519DB; Thu, 19 Feb 2026 16:04:17 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771513457; bh=IQN5mfMyGVYat62QadSljdKhCwzL3IKFYLwXG9YzV8o=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=N0hd7bdNd3EDuwxx30S4roWEyIYKQeFSMhtdtupk7WYLA9/S80M9/HCUwzUsDL5eO WNur3kxXlg9wfpBa5ZvPhRikvA4TDhHWzlQ/R+LJRQdEGu2FESeAvlekkzBlL98DPx h595ug28VI0pR58f18I/yGMSgyYmwltEDMMZTInE= From: Kieran Bingham Date: Thu, 19 Feb 2026 15:05:09 +0000 Subject: [PATCH v8 08/15] libcamera: controls: Define a new core Hue control MIME-Version: 1.0 Message-Id: <20260219-kbingham-quantizers-v8-8-2b6ff68ead26@ideasonboard.com> References: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> In-Reply-To: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , "van Veen, Stephan" , Isaac Scott , Paul Elder , Laurent Pinchart , Stefan Klug X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771513507; l=1747; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=KKbPES795OS8Z0E+MjbsA9rZOX36xmjEzpI1sO/XrwI=; b=xgyZhu4o2LniG15nhm6b9jK3q0LwErvwG5kpB4lgZeCrnMZVMfwPgON6uHJzvNeJgzrqoxNcz OebcqLuIAcbBcrsygs//3mCErcl1k0SKKMhTODYfMqT+6CAdzbc2kxl X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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" From: "van Veen, Stephan" Define a new control to support configuration of Hue adjustments when supported by the available platform. Reviewed-by: Isaac Scott Signed-off-by: van Veen, Stephan [Kieran: Rework to define as a rotation in degrees] Reviewed-by: Paul Elder Reviewed-by: Laurent Pinchart Reviewed-by: Stefan Klug Signed-off-by: Kieran Bingham --- v8: - Use [-180, 180] to allow metadata expectations to be met (without changing sign on an input control) --- src/libcamera/control_ids_core.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/libcamera/control_ids_core.yaml b/src/libcamera/control_ids_core.yaml index f26a1b1b08bd..89991d03d79a 100644 --- a/src/libcamera/control_ids_core.yaml +++ b/src/libcamera/control_ids_core.yaml @@ -1361,4 +1361,19 @@ controls: description: | Enable or disable the lens shading correction. + - Hue: + type: float + direction: inout + description: | + Adjusts the image hue (colour rotation) in degrees, as defined in + the HSL/HSV colour model. + + The value represents a rotation around the hue circle in HSL/HSV space: + positive values rotate hues clockwise (for example a +60° turns + Red hues to Yellow hues), and negative values rotate counter-clockwise + (a -60° turns Red hues to Magenta hues). + + The nominal range is [-180, 180], where 0° leaves hues unchanged and the + range wraps around continuously, with 180° == -180°. + ... From patchwork Thu Feb 19 15:05:10 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26207 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 ECAB4C32F0 for ; Thu, 19 Feb 2026 15:05:26 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1F93A62292; Thu, 19 Feb 2026 16:05:22 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="WYYNHbP8"; 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 22D7062267 for ; Thu, 19 Feb 2026 16:05:11 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id ECF611AA6; Thu, 19 Feb 2026 16:04:17 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771513458; bh=vukayUivZ4mcZveseUVE914X29E6CUv5FHAfht1MWhg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=WYYNHbP8Pk3HEbvrXzz0XNLaecy65AVrt1s8fOoy4Pqg2jBHt9OvXqeDnPOINCpfX XkzFmmO+mzpCCIfjEGRMNWgJnK5Y940/EqQ2qJTmPA4cgFj0n6XPMrnjffF2VE3OKq BGh7I28yxQ5qAcFh/3s2vYYBThUu2Gp7abtVlF2o= From: Kieran Bingham Date: Thu, 19 Feb 2026 15:05:10 +0000 Subject: [PATCH v8 09/15] ipa: rkisp1: cproc: Provide a Hue control MIME-Version: 1.0 Message-Id: <20260219-kbingham-quantizers-v8-9-2b6ff68ead26@ideasonboard.com> References: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> In-Reply-To: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , Isaac Scott , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= , Paul Elder , Laurent Pinchart X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771513507; l=5006; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=vukayUivZ4mcZveseUVE914X29E6CUv5FHAfht1MWhg=; b=899ShL2M1wdkbBPsTqX2gXPY9iWGIYfO4/2fWI5WijEUOD3lad6P+qy1INlmf6w64SjS2e+nF xUWrN7FPzBrBzsnZXkHgKAZLJwTHp66DaQBemw3UO2fITary7YjSHpF X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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 RKISP1 supports a configurable Hue as part of the colour processing unit (cproc). Implement the new control converting to the hardware scale accordingly and report the applied control in the completed request metadata. This is implemented as a phase shift of the chrominance values between -90 and +87.188 degrees according to the datasheet however the type itself would imply that this is a range between -90 and 89.2969. Moreover, the hardware applies the inverse phase shift to the operation expected and documented by libcamera, so we apply a negative scale when converting the libcamera control to the Q<1,7> type, resulting in a range of -89.2969 to +90.0 degrees control. Reviewed-by: Isaac Scott Tested-by: Barnabás Pőcze Reviewed-by: Paul Elder Reviewed-by: Laurent Pinchart Signed-off-by: Kieran Bingham --- src/ipa/rkisp1/algorithms/cproc.cpp | 28 ++++++++++++++++++++++++++++ src/ipa/rkisp1/ipa_context.h | 3 +++ 2 files changed, 31 insertions(+) diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp index e9e2b5444bc9..7484e4780094 100644 --- a/src/ipa/rkisp1/algorithms/cproc.cpp +++ b/src/ipa/rkisp1/algorithms/cproc.cpp @@ -37,8 +37,15 @@ namespace { constexpr float kDefaultBrightness = 0.0f; constexpr float kDefaultContrast = 1.0f; +constexpr float kDefaultHue = 0.0f; constexpr float kDefaultSaturation = 1.0f; +/* + * The Hue scale is negated as the hardware performs the opposite phase shift + * to what is expected and defined from the libcamera Hue control value. + */ +constexpr float kHueScale = -90.0f; + } /* namespace */ /** @@ -53,6 +60,11 @@ int ColorProcessing::init(IPAContext &context, cmap[&controls::Contrast] = ControlInfo(0.0f, 1.993f, kDefaultContrast); cmap[&controls::Saturation] = ControlInfo(0.0f, 1.993f, kDefaultSaturation); + /* Hue adjustment is negated by kHueScale, min/max are swapped */ + cmap[&controls::Hue] = ControlInfo(HueQ::TraitsType::max * kHueScale, + HueQ::TraitsType::min * kHueScale, + kDefaultHue); + return 0; } @@ -66,6 +78,7 @@ int ColorProcessing::configure(IPAContext &context, cproc.brightness = BrightnessQ(kDefaultBrightness); cproc.contrast = ContrastQ(kDefaultContrast); + cproc.hue = HueQ(kDefaultHue); cproc.saturation = SaturationQ(kDefaultSaturation); return 0; @@ -107,6 +120,18 @@ void ColorProcessing::queueRequest(IPAContext &context, LOG(RkISP1CProc, Debug) << "Set contrast to " << value; } + const auto &hue = controls.get(controls::Hue); + if (hue) { + /* Scale the Hue from ]-90, +90] */ + HueQ value = *hue / kHueScale; + if (cproc.hue != value) { + cproc.hue = value; + update = true; + } + + LOG(RkISP1CProc, Debug) << "Set hue to " << value; + } + const auto saturation = controls.get(controls::Saturation); if (saturation) { SaturationQ value = *saturation; @@ -120,6 +145,7 @@ void ColorProcessing::queueRequest(IPAContext &context, frameContext.cproc.brightness = cproc.brightness; frameContext.cproc.contrast = cproc.contrast; + frameContext.cproc.hue = cproc.hue; frameContext.cproc.saturation = cproc.saturation; frameContext.cproc.update = update; } @@ -140,6 +166,7 @@ void ColorProcessing::prepare([[maybe_unused]] IPAContext &context, config.setEnabled(true); config->brightness = frameContext.cproc.brightness.quantized(); config->contrast = frameContext.cproc.contrast.quantized(); + config->hue = frameContext.cproc.hue.quantized(); config->sat = frameContext.cproc.saturation.quantized(); } @@ -154,6 +181,7 @@ void ColorProcessing::process([[maybe_unused]] IPAContext &context, { metadata.set(controls::Brightness, frameContext.cproc.brightness.value()); metadata.set(controls::Contrast, frameContext.cproc.contrast.value()); + metadata.set(controls::Hue, frameContext.cproc.hue.value() * kHueScale); metadata.set(controls::Saturation, frameContext.cproc.saturation.value()); } diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index cca5e281c732..e61391bb1510 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -36,6 +36,7 @@ namespace ipa::rkisp1 { /* Fixed point types used by CPROC */ using BrightnessQ = Q<1, 7>; using ContrastQ = UQ<1, 7>; +using HueQ = Q<1, 7>; using SaturationQ = UQ<1, 7>; struct IPAHwSettings { @@ -119,6 +120,7 @@ struct IPAActiveState { struct { BrightnessQ brightness; ContrastQ contrast; + HueQ hue; SaturationQ saturation; } cproc; @@ -181,6 +183,7 @@ struct IPAFrameContext : public FrameContext { struct { BrightnessQ brightness; ContrastQ contrast; + HueQ hue; SaturationQ saturation; bool update; From patchwork Thu Feb 19 15:05:11 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26206 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 90035C32EF for ; Thu, 19 Feb 2026 15:05:26 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C7CD662297; Thu, 19 Feb 2026 16:05:20 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="u8Wpp8QJ"; 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 6D6CB62262 for ; Thu, 19 Feb 2026 16:05:11 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 4858E1F17; Thu, 19 Feb 2026 16:04:18 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771513458; bh=4pCBfQSee7I0pfwKO4in0LLcCKgDO/USt9/vbY6gvNs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=u8Wpp8QJyNKJnoSpUm5sb2jqdTxhbaZO5BZvjiaD81Fbd7uC5UFMCS6FZH95167PO Ho7sIC0vAp2FWDWmLlaYUBXjZJngN15ABG/bHw+AtO8JSLtQpvJXNZ8jLHfT4JaC8A 3JsamraweeK2YKx98p1bPCCZSMy5uzhUVSq/gNxY= From: Kieran Bingham Date: Thu, 19 Feb 2026 15:05:11 +0000 Subject: [PATCH v8 10/15] ipa: rkisp1: ccm: Use Q<4, 7> format directly MIME-Version: 1.0 Message-Id: <20260219-kbingham-quantizers-v8-10-2b6ff68ead26@ideasonboard.com> References: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> In-Reply-To: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , Isaac Scott , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= , Paul Elder , Laurent Pinchart X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771513507; l=1173; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=4pCBfQSee7I0pfwKO4in0LLcCKgDO/USt9/vbY6gvNs=; b=l8Pvi9CTn6qDFUhy5gmbOmEo9YiEoNxMZ6CgherZrz3ZIsE4C5YXcqEt5KY6P+wFuH+dAcGkJ DuvMSyjQenqC4XsgZs6dWtFE+VRkCY0ZEuS58/z6ZpGRnfbhmgg97Wa X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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 the legacy call to floatingToFixedPoint with the new FixedPoint quantizer to explicitly describe the type used by the RKISP1 Colour Correction Matrix. Reviewed-by: Isaac Scott Reviewed-by: Barnabás Pőcze Reviewed-by: Paul Elder Reviewed-by: Laurent Pinchart Signed-off-by: Kieran Bingham --- src/ipa/rkisp1/algorithms/ccm.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/ccm.cpp b/src/ipa/rkisp1/algorithms/ccm.cpp index de2b6fe775aa..466d7a116eea 100644 --- a/src/ipa/rkisp1/algorithms/ccm.cpp +++ b/src/ipa/rkisp1/algorithms/ccm.cpp @@ -117,8 +117,7 @@ void Ccm::setParameters(struct rkisp1_cif_isp_ctk_config &config, */ for (unsigned int i = 0; i < 3; i++) { for (unsigned int j = 0; j < 3; j++) - config.coeff[i][j] = - floatingToFixedPoint<4, 7, uint16_t, double>(matrix[i][j]); + config.coeff[i][j] = Q<4, 7>(matrix[i][j]).quantized(); } for (unsigned int i = 0; i < 3; i++) From patchwork Thu Feb 19 15:05:12 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26208 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 599B8C32F1 for ; Thu, 19 Feb 2026 15:05:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C24FB6227B; Thu, 19 Feb 2026 16:05:22 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="PBzn9lkR"; 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 AE85162254 for ; Thu, 19 Feb 2026 16:05:11 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 9065D259E; Thu, 19 Feb 2026 16:04:18 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771513458; bh=4AD4w6srHMQImIIhm/+qGTzISyhaPIWnXBj7164B2CE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=PBzn9lkRj0ZZ+iAo8YUGf88AUqrsQXdOTogwWk6ef//I1cuj4omXyZUDCRMeFVXrF b9++5XpZinvBrcSF0lW+ENU1CHUyNOy8MoUMCJySloRcg7fGVRnPfhWTaIcsTh1X+r 4qESwMxLxEPfvoFam3kDafeENYoHRff9zOJpTu9A= From: Kieran Bingham Date: Thu, 19 Feb 2026 15:05:12 +0000 Subject: [PATCH v8 11/15] ipa: mali-c55: Reduce AWB calculations to float precision MIME-Version: 1.0 Message-Id: <20260219-kbingham-quantizers-v8-11-2b6ff68ead26@ideasonboard.com> References: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> In-Reply-To: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , =?utf-8?q?Barnab?= =?utf-8?b?w6FzIFDFkWN6ZQ==?= , Stefan Klug , Laurent Pinchart X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771513507; l=2428; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=4AD4w6srHMQImIIhm/+qGTzISyhaPIWnXBj7164B2CE=; b=9w1XHVy9K1BytXqjF7UgzwozKqe2XzaZMu8dNBoPjossu6QEgk5IGn+IcAPudvQ02bOZ1JoDi cBUii5cjVApBOeJxGUBzke/zlZFklssyAZ89iJOmYRjEGKGPfxtYmj7 X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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 AWB calculations are determined using double precision, and then will be soon stored in a quantized float. Use float types for the intermediate types after the sums have been converted to an average to remove static cast assignments. Reviewed-by: Barnabás Pőcze Reviewed-by: Stefan Klug Reviewed-by: Laurent Pinchart Signed-off-by: Kieran Bingham --- v8: - Use 1.0f and 0.2f to prevent double usage --- src/ipa/mali-c55/algorithms/awb.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ipa/mali-c55/algorithms/awb.cpp b/src/ipa/mali-c55/algorithms/awb.cpp index 964e810882a9..fab73294a88f 100644 --- a/src/ipa/mali-c55/algorithms/awb.cpp +++ b/src/ipa/mali-c55/algorithms/awb.cpp @@ -159,7 +159,7 @@ void Awb::process(IPAContext &context, const uint32_t frame, * Sometimes the first frame's statistics have no valid pixels, in which * case we'll just assume a grey world until they say otherwise. */ - double rgAvg, bgAvg; + float rgAvg, bgAvg; if (!counted_zones) { rgAvg = 1.0; bgAvg = 1.0; @@ -174,15 +174,15 @@ void Awb::process(IPAContext &context, const uint32_t frame, * figure by the gains that were applied when the statistics for this * frame were generated. */ - double rRatio = rgAvg / frameContext.awb.rGain; - double bRatio = bgAvg / frameContext.awb.bGain; + float rRatio = rgAvg / frameContext.awb.rGain; + float bRatio = bgAvg / frameContext.awb.bGain; /* * And then we can simply invert the ratio to find the gain we should * apply. */ - double rGain = 1 / rRatio; - double bGain = 1 / bRatio; + float rGain = 1 / rRatio; + float bGain = 1 / bRatio; /* * Running at full speed, this algorithm results in oscillations in the @@ -190,7 +190,7 @@ void Awb::process(IPAContext &context, const uint32_t frame, * changes in gain, unless we're in the startup phase in which case we * want to fix the miscolouring as quickly as possible. */ - double speed = frame < kNumStartupFrames ? 1.0 : 0.2; + float speed = frame < kNumStartupFrames ? 1.0f : 0.2f; rGain = speed * rGain + context.activeState.awb.rGain * (1.0 - speed); bGain = speed * bGain + context.activeState.awb.bGain * (1.0 - speed); From patchwork Thu Feb 19 15:05:13 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26209 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 C2318C32F2 for ; Thu, 19 Feb 2026 15:05:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DC1F56228A; Thu, 19 Feb 2026 16:05:23 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="iASSGRM0"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 03AA16225D for ; Thu, 19 Feb 2026 16:05:12 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D497A2683; Thu, 19 Feb 2026 16:04:18 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771513459; bh=E714MbmoTzlkEknfkQGU2teK+G38gzHM0hn9fxjmOaE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=iASSGRM0NGpM325NWDjBfqyitQDyPDUoofDTx3ydYoC2WGXFWvkUfruRszgb/Icjj 7GFX84g57Y4y98lAQkDC5q5yRZUjf1PoXtWn0Oq0vWneNfPWV8WL3USAO/S6WNgAtR RPlmzbIPWRjRdZ/2W76R0Cgha4NMQAnsObKxxaus= From: Kieran Bingham Date: Thu, 19 Feb 2026 15:05:13 +0000 Subject: [PATCH v8 12/15] ipa: mali-c55: Convert AWB to UQ<4, 8> usage MIME-Version: 1.0 Message-Id: <20260219-kbingham-quantizers-v8-12-2b6ff68ead26@ideasonboard.com> References: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> In-Reply-To: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , Isaac Scott , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= , Laurent Pinchart X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771513507; l=5293; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=E714MbmoTzlkEknfkQGU2teK+G38gzHM0hn9fxjmOaE=; b=5dVSoiCVCumRyn/ReqxMKIOcjPvIBNrf23H6dRCeXCmHxh6arBkOkTpgJrgsnWwNZ4TkoYJl1 frLonkoNshqAM4ZCBJxycuJqFBBZLk/rEvrPm4vx7s+Ir48bxdPvAP/ X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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" Utilise the new FixedPoint type to explicitly calculate gains for AWB in Q4.8 format. This ensures that reporting of gains in metadata reflect the true AWB gains applied. Reviewed-by: Isaac Scott Reviewed-by: Barnabás Pőcze Reviewed-by: Laurent Pinchart Signed-off-by: Kieran Bingham --- v8: - Use 1.0f to prevent double usage --- src/ipa/mali-c55/algorithms/awb.cpp | 36 ++++++++++++++++++------------------ src/ipa/mali-c55/ipa_context.h | 10 ++++++---- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/ipa/mali-c55/algorithms/awb.cpp b/src/ipa/mali-c55/algorithms/awb.cpp index fab73294a88f..8a671b52be59 100644 --- a/src/ipa/mali-c55/algorithms/awb.cpp +++ b/src/ipa/mali-c55/algorithms/awb.cpp @@ -37,8 +37,8 @@ int Awb::configure([[maybe_unused]] IPAContext &context, * for the first frame we will make no assumptions and leave the R/B * channels unmodified. */ - context.activeState.awb.rGain = 1.0; - context.activeState.awb.bGain = 1.0; + context.activeState.awb.rGain = 1.0f; + context.activeState.awb.bGain = 1.0f; return 0; } @@ -46,8 +46,8 @@ int Awb::configure([[maybe_unused]] IPAContext &context, void Awb::fillGainsParamBlock(MaliC55Params *params, IPAContext &context, IPAFrameContext &frameContext) { - double rGain = context.activeState.awb.rGain; - double bGain = context.activeState.awb.bGain; + UQ<4, 8> rGain = context.activeState.awb.rGain; + UQ<4, 8> bGain = context.activeState.awb.bGain; /* * The gains here map as follows: @@ -61,10 +61,10 @@ void Awb::fillGainsParamBlock(MaliC55Params *params, IPAContext &context, */ auto block = params->block(); - block->gain00 = floatingToFixedPoint<4, 8, uint16_t, double>(rGain); - block->gain01 = floatingToFixedPoint<4, 8, uint16_t, double>(1.0); - block->gain10 = floatingToFixedPoint<4, 8, uint16_t, double>(1.0); - block->gain11 = floatingToFixedPoint<4, 8, uint16_t, double>(bGain); + block->gain00 = rGain.quantized(); + block->gain01 = UQ<4, 8>(1.0f).quantized(); + block->gain10 = UQ<4, 8>(1.0f).quantized(); + block->gain11 = bGain.quantized(); frameContext.awb.rGain = rGain; frameContext.awb.bGain = bGain; @@ -140,18 +140,18 @@ void Awb::process(IPAContext &context, const uint32_t frame, * gain figures that we can apply to approximate a grey world. */ unsigned int counted_zones = 0; - double rgSum = 0, bgSum = 0; + float rgSum = 0, bgSum = 0; for (unsigned int i = 0; i < 225; i++) { if (!awb_ratios[i].num_pixels) continue; /* - * The statistics are in Q4.8 format, so we convert to double + * The statistics are in Q4.8 format, so we convert to float * here. */ - rgSum += fixedToFloatingPoint<4, 8, double, uint16_t>(awb_ratios[i].avg_rg_gr); - bgSum += fixedToFloatingPoint<4, 8, double, uint16_t>(awb_ratios[i].avg_bg_br); + rgSum += UQ<4, 8>(awb_ratios[i].avg_rg_gr).value(); + bgSum += UQ<4, 8>(awb_ratios[i].avg_bg_br).value(); counted_zones++; } @@ -174,8 +174,8 @@ void Awb::process(IPAContext &context, const uint32_t frame, * figure by the gains that were applied when the statistics for this * frame were generated. */ - float rRatio = rgAvg / frameContext.awb.rGain; - float bRatio = bgAvg / frameContext.awb.bGain; + float rRatio = rgAvg / frameContext.awb.rGain.value(); + float bRatio = bgAvg / frameContext.awb.bGain.value(); /* * And then we can simply invert the ratio to find the gain we should @@ -191,15 +191,15 @@ void Awb::process(IPAContext &context, const uint32_t frame, * want to fix the miscolouring as quickly as possible. */ float speed = frame < kNumStartupFrames ? 1.0f : 0.2f; - rGain = speed * rGain + context.activeState.awb.rGain * (1.0 - speed); - bGain = speed * bGain + context.activeState.awb.bGain * (1.0 - speed); + rGain = speed * rGain + context.activeState.awb.rGain.value() * (1.0f - speed); + bGain = speed * bGain + context.activeState.awb.bGain.value() * (1.0f - speed); context.activeState.awb.rGain = rGain; context.activeState.awb.bGain = bGain; metadata.set(controls::ColourGains, { - static_cast(frameContext.awb.rGain), - static_cast(frameContext.awb.bGain), + frameContext.awb.rGain.value(), + frameContext.awb.bGain.value(), }); LOG(MaliC55Awb, Debug) << "For frame number " << frame << ": " diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h index 13885eb83b5c..08f78e4f74ce 100644 --- a/src/ipa/mali-c55/ipa_context.h +++ b/src/ipa/mali-c55/ipa_context.h @@ -14,6 +14,8 @@ #include +#include "libipa/fixedpoint.h" + namespace libcamera { namespace ipa::mali_c55 { @@ -53,8 +55,8 @@ struct IPAActiveState { } agc; struct { - double rGain; - double bGain; + UQ<4, 8> rGain; + UQ<4, 8> bGain; } awb; }; @@ -66,8 +68,8 @@ struct IPAFrameContext : public FrameContext { } agc; struct { - double rGain; - double bGain; + UQ<4, 8> rGain; + UQ<4, 8> bGain; } awb; }; From patchwork Thu Feb 19 15:05:14 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26211 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 8F27CC32F3 for ; Thu, 19 Feb 2026 15:05:28 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3CE196229B; Thu, 19 Feb 2026 16:05:26 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ZE4HGxcM"; 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 4CD1862270 for ; Thu, 19 Feb 2026 16:05:12 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 27E6727F7; Thu, 19 Feb 2026 16:04:19 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771513459; bh=8mR7Mod1Kgrw0Q8pG6KfQsCxjvvIGVE6ohbpnqr2JS4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=ZE4HGxcM5n+M78FT3H0Mx7+E+3dvgapTyizu202gJ1PMe1dmLMTYKSWbnvyWKDmw4 a97R5uy/HYaFFvRbnUb1VcTu9V2dkdtttKJVc1Gm1hdcJYcAxIL6VVo63v8WrurKmP j04lKvxAd7DdFtGEd+4bSGqtQ1+NllLmxc4UujuU= From: Kieran Bingham Date: Thu, 19 Feb 2026 15:05:14 +0000 Subject: [PATCH v8 13/15] ipa: mali-c55: agc: Quantise the ISP Digital Gain MIME-Version: 1.0 Message-Id: <20260219-kbingham-quantizers-v8-13-2b6ff68ead26@ideasonboard.com> References: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> In-Reply-To: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , Isaac Scott , Stefan Klug X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771513507; l=4981; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=8mR7Mod1Kgrw0Q8pG6KfQsCxjvvIGVE6ohbpnqr2JS4=; b=6xOklpOspfW3Nkx7kpqKymQ9Ig+/8Pim+p6Yyl/kYpXiL4hQajCO0pm+lh1zj44IvAITWfFED 7oOy9qUcTIQCGceJNm+L1lf9yMOYJLUYhR2lKnyrUKBSt9jUTPI0g9D X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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 Mali-C55 ISP has a digital gain block which allows setting gain in Q5.8 format, a range of 0.0 to (very nearly) 32.0. Convert usage to the new UQ<5, 8> FixedPoint Quantised type which will support the conversion, clamping and quantisation so that the metadata and debug prints can now report the effective gain applied instead of the potentially inaccurate float. Reviewed-by: Isaac Scott Reviewed-by: Stefan Klug Signed-off-by: Kieran Bingham --- v8: - Keep the gain clamps --- src/ipa/mali-c55/algorithms/agc.cpp | 25 ++++++++++++------------- src/ipa/mali-c55/ipa_context.h | 6 +++--- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp index 014fd12452ac..34d676677efa 100644 --- a/src/ipa/mali-c55/algorithms/agc.cpp +++ b/src/ipa/mali-c55/algorithms/agc.cpp @@ -38,8 +38,8 @@ static constexpr unsigned int kNumHistogramBins = 256; * format, a range of 0.0 to (very nearly) 32.0. We clamp from 1.0 to the actual * max value which is 8191 * 2^-8. */ -static constexpr double kMinDigitalGain = 1.0; -static constexpr double kMaxDigitalGain = 31.99609375; +static constexpr float kMinDigitalGain = 1.0; +static constexpr float kMaxDigitalGain = UQ<5, 8>::TraitsType::max; uint32_t AgcStatistics::decodeBinValue(uint16_t binVal) { @@ -139,10 +139,7 @@ int Agc::init(IPAContext &context, const YamlObject &tuningData) context.ctrlMap[&controls::AeEnable] = ControlInfo(false, true); context.ctrlMap[&controls::DigitalGain] = ControlInfo( - static_cast(kMinDigitalGain), - static_cast(kMaxDigitalGain), - static_cast(kMinDigitalGain) - ); + kMinDigitalGain, kMaxDigitalGain, kMinDigitalGain); context.ctrlMap.merge(controls()); return 0; @@ -245,7 +242,7 @@ void Agc::fillGainParamBlock(IPAContext &context, IPAFrameContext &frameContext, MaliC55Params *params) { IPAActiveState &activeState = context.activeState; - double gain; + UQ<5, 8> gain; if (activeState.agc.autoEnabled) gain = activeState.agc.automatic.ispGain; @@ -253,7 +250,7 @@ void Agc::fillGainParamBlock(IPAContext &context, IPAFrameContext &frameContext, gain = activeState.agc.manual.ispGain; auto block = params->block(); - block->gain = floatingToFixedPoint<5, 8, uint16_t, double>(gain); + block->gain = gain.quantized(); frameContext.agc.ispGain = gain; } @@ -358,7 +355,7 @@ void Agc::process(IPAContext &context, */ uint32_t exposure = frameContext.agc.exposure; double analogueGain = frameContext.agc.sensorGain; - double digitalGain = frameContext.agc.ispGain; + double digitalGain = frameContext.agc.ispGain.value(); double totalGain = analogueGain * digitalGain; utils::Duration currentShutter = exposure * configuration.sensor.lineDuration; utils::Duration effectiveExposureValue = currentShutter * totalGain; @@ -370,19 +367,21 @@ void Agc::process(IPAContext &context, activeState.agc.exposureMode, statistics_.yHist, effectiveExposureValue); - dGain = std::clamp(dGain, kMinDigitalGain, kMaxDigitalGain); + UQ<5, 8> dGainQ = std::clamp(static_cast(dGain), + kMinDigitalGain, + kMaxDigitalGain); LOG(MaliC55Agc, Debug) << "Divided up shutter, analogue gain and digital gain are " - << shutterTime << ", " << aGain << " and " << dGain; + << shutterTime << ", " << aGain << " and " << dGainQ; activeState.agc.automatic.exposure = shutterTime / configuration.sensor.lineDuration; activeState.agc.automatic.sensorGain = aGain; - activeState.agc.automatic.ispGain = dGain; + activeState.agc.automatic.ispGain = dGainQ; metadata.set(controls::ExposureTime, currentShutter.get()); metadata.set(controls::AnalogueGain, frameContext.agc.sensorGain); - metadata.set(controls::DigitalGain, frameContext.agc.ispGain); + metadata.set(controls::DigitalGain, frameContext.agc.ispGain.value()); metadata.set(controls::ColourTemperature, context.activeState.agc.temperatureK); } diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h index 08f78e4f74ce..ac4b83773803 100644 --- a/src/ipa/mali-c55/ipa_context.h +++ b/src/ipa/mali-c55/ipa_context.h @@ -41,12 +41,12 @@ struct IPAActiveState { struct { uint32_t exposure; double sensorGain; - double ispGain; + UQ<5, 8> ispGain; } automatic; struct { uint32_t exposure; double sensorGain; - double ispGain; + UQ<5, 8> ispGain; } manual; bool autoEnabled; uint32_t constraintMode; @@ -64,7 +64,7 @@ struct IPAFrameContext : public FrameContext { struct { uint32_t exposure; double sensorGain; - double ispGain; + UQ<5, 8> ispGain; } agc; struct { From patchwork Thu Feb 19 15:05:15 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26210 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 314CBC0DA4 for ; Thu, 19 Feb 2026 15:05:28 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9226B6226E; Thu, 19 Feb 2026 16:05:25 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="RlBmCUin"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 97DBB62273 for ; Thu, 19 Feb 2026 16:05:12 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7370527F9; Thu, 19 Feb 2026 16:04:19 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771513459; bh=gvIg/krgeAKVXjFC9J/b5MWuAj3gObmd6jfe4R6IQlg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=RlBmCUinmw6i1cGj9u9YD940SDZZpxgPF85Sr7DNwY63nI4hWhD3OPTWLSVuaa0rI 8fiPm/Myw141VaCiKyXXxNgIp+Fo0Hk2icVnGbAtFbpKcQ9i6LeJ+2GAwl/HVTtWaX hoPpBATZRQoCYmQz5z6rpJc6RwMPqYdbBEhE+7UY= From: Kieran Bingham Date: Thu, 19 Feb 2026 15:05:15 +0000 Subject: [PATCH v8 14/15] test: libipa: Remove legacy fixed point conversion test MIME-Version: 1.0 Message-Id: <20260219-kbingham-quantizers-v8-14-2b6ff68ead26@ideasonboard.com> References: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> In-Reply-To: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , Isaac Scott , Laurent Pinchart , Paul Elder X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771513507; l=4866; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=gvIg/krgeAKVXjFC9J/b5MWuAj3gObmd6jfe4R6IQlg=; b=tMktmhb5PMirbXXnx2fzI45bJhe6HkwGuaU1pDfMlP30uGLb6fwdJRH8XZiNNrNYzvS5u0gls LGQTLR8WVH7BzduQL4kV1rFwc3URttjmJrKVVeg4hHCTI1jmewJ9sSD X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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 fixed point conversions are equally covered by the new Q types, the legacy tests for fixedToFloatingPoint and floatingToFixedPoint are redundant. Remove them, and replace the existing test cases with equivalant tests using the new Q4.7 type directly to maintain identical test coverage. Reviewed-by: Isaac Scott Reviewed-by: Laurent Pinchart Reviewed-by: Paul Elder Signed-off-by: Kieran Bingham --- test/ipa/libipa/fixedpoint.cpp | 93 +++++++----------------------------------- 1 file changed, 15 insertions(+), 78 deletions(-) diff --git a/test/ipa/libipa/fixedpoint.cpp b/test/ipa/libipa/fixedpoint.cpp index 2907426b7a69..316d9f8f1b7d 100644 --- a/test/ipa/libipa/fixedpoint.cpp +++ b/test/ipa/libipa/fixedpoint.cpp @@ -23,80 +23,6 @@ using namespace ipa; class FixedPointUtilsTest : public Test { protected: - /* R for real, I for integer */ - template - int testFixedToFloat(I input, R expected) - { - R out = fixedToFloatingPoint(input); - R prec = 1.0 / (1 << FracPrec); - if (std::abs(out - expected) > prec) { - cerr << "Reverse conversion expected " << input - << " to convert to " << expected - << ", got " << out << std::endl; - return TestFail; - } - - return TestPass; - } - - template - int testSingleFixedPoint(double input, T expected) - { - T ret = floatingToFixedPoint(input); - if (ret != expected) { - cerr << "Expected " << input << " to convert to " - << expected << ", got " << ret << std::endl; - return TestFail; - } - - /* - * The precision check is fairly arbitrary but is based on what - * the rkisp1 is capable of in the crosstalk module. - */ - double f = fixedToFloatingPoint(ret); - if (std::abs(f - input) > 0.005) { - cerr << "Reverse conversion expected " << ret - << " to convert to " << input - << ", got " << f << std::endl; - return TestFail; - } - - return TestPass; - } - - int testFixedPoint() - { - /* - * The second 7.992 test is to test that unused bits don't - * affect the result. - */ - std::map testCases = { - { 7.992, 0x3ff }, - { 0.2, 0x01a }, - { -0.2, 0x7e6 }, - { -0.8, 0x79a }, - { -0.4, 0x7cd }, - { -1.4, 0x74d }, - { -8, 0x400 }, - { 0, 0 }, - }; - - int ret; - for (const auto &testCase : testCases) { - ret = testSingleFixedPoint<4, 7, int16_t>(testCase.first, - testCase.second); - if (ret != TestPass) - return ret; - } - - /* Special case with a superfluous one in the unused bits */ - ret = testFixedToFloat<4, 7, int16_t, double>(0xbff, 7.992); - if (ret != TestPass) - return ret; - - return TestPass; - } - template int quantizedCheck(float input, typename Q::QuantizedType expected, float value) { @@ -219,6 +145,21 @@ protected: fails += quantizedCheck>( 1.992f, 0b1'1111111, 1.99219f); /* Max */ fails += quantizedCheck>( 2.000f, 0b1'1111111, 1.99219f); /* Clamped to Max */ + /* Q4.7(-8 .. 7.99219) Min: [0x0400:-8] -- Max: [0x03ff:7.99219] Step:0.0078125 */ + introduce>("Q4.7"); + fails += quantizedCheck>(-8.0f, 0b1000'0000000, -8.0f); /* Min */ + fails += quantizedCheck>(-0.008f, 0b1111'1111111, -0.0078125); /* -1 step */ + fails += quantizedCheck>( 0.0f, 0b0000'0000000, 0.0f); /* Zero */ + fails += quantizedCheck>( 0.008f, 0b0000'0000001, 0.0078125f); /* +1 step */ + fails += quantizedCheck>( 7.992f, 0b0111'1111111, 7.99219f); /* Max */ + + /* Retain additional tests from original testFixedPoint() */ + fails += quantizedCheck>( 0.2f, 0b0000'0011010, 0.203125f); /* 0x01a */ + fails += quantizedCheck>(-0.2f, 0b1111'1100110, -0.203125f); /* 0x7e6 */ + fails += quantizedCheck>(-0.8f, 0b1111'0011010, -0.796875f); /* 0x79a */ + fails += quantizedCheck>(-0.4f, 0b1111'1001101, -0.398438f); /* 0x7cd */ + fails += quantizedCheck>(-1.4f, 0b1110'1001101, -1.39844f); /* 0x74d */ + /* UQ4.8(0 .. 15.9961) Min: [0x0000:0] -- Max: [0x0fff:15.9961] Step:0.00390625 */ introduce>("UQ4.8"); fails += quantizedCheck>( 0.0f, 0b0000'00000000, 0.00f); @@ -295,10 +236,6 @@ protected: { unsigned int fails = 0; - /* fixed point conversion test */ - if (testFixedPoint() != TestPass) - fails++; - if (testFixedPointQuantizers() != TestPass) fails++; From patchwork Thu Feb 19 15:05:16 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26212 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 11207C32F4 for ; Thu, 19 Feb 2026 15:05:29 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id EED00622A2; Thu, 19 Feb 2026 16:05:26 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="LY55XWJa"; 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 EB3DD62275 for ; Thu, 19 Feb 2026 16:05:12 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id B9E5B27FE; Thu, 19 Feb 2026 16:04:19 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771513459; bh=ZS4mVLpItJiKWId3mQLO8b9HUpOybF6GBGG9uLiV+uw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=LY55XWJawpY/k0jkdMpi4fX3QTd9BM3UT5m5ziu4AIG9dodSYkUBPrUF3z/bKSdMa ATKUjP41hlz6EzbFRmbHa4YZ4mPFz9gYb7812NpjxxFYheZpZu4gIHqlvpZzOn1gCE AxDwXFaSWqrbNTLXzqNwWV8Z83JdEdCgT4kZ/dqY= From: Kieran Bingham Date: Thu, 19 Feb 2026 15:05:16 +0000 Subject: [PATCH v8 15/15] ipa: libipa: fixedpoint: Move float conversion inline MIME-Version: 1.0 Message-Id: <20260219-kbingham-quantizers-v8-15-2b6ff68ead26@ideasonboard.com> References: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> In-Reply-To: <20260219-kbingham-quantizers-v8-0-2b6ff68ead26@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , Isaac Scott , Laurent Pinchart , Paul Elder X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771513507; l=5788; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=ZS4mVLpItJiKWId3mQLO8b9HUpOybF6GBGG9uLiV+uw=; b=yYz+JGj+J4LPtbtHlUpILFVPkU7ywdFWBuUlsOfqnRq4E29F0K3xHDHYA3yMm8WwOfXK5/GRt /JV6cVnrEdoAfya7/h9DRBq1eOdBGeQa5fJGD1iEfoV4+k0qeWaadY+ X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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" With all users of the floatingToFixedPoint and fixedToFloatingPoint calls converted to use the FixedPoint Quantized types, remove the calls and documentation and move the implementation inline in the FixedPointTraits implementation. Reviewed-by: Isaac Scott Reviewed-by: Laurent Pinchart Reviewed-by: Paul Elder Signed-off-by: Kieran Bingham --- src/ipa/libipa/fixedpoint.cpp | 22 ------------- src/ipa/libipa/fixedpoint.h | 75 +++++++++++++------------------------------ 2 files changed, 22 insertions(+), 75 deletions(-) diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp index b581ca8453ef..b06ad8d26719 100644 --- a/src/ipa/libipa/fixedpoint.cpp +++ b/src/ipa/libipa/fixedpoint.cpp @@ -15,28 +15,6 @@ namespace libcamera { namespace ipa { -/** - * \fn R floatingToFixedPoint(T number) - * \brief Convert a floating point number to a fixed-point representation - * \tparam I Bit width of the integer part of the fixed-point - * \tparam F Bit width of the fractional part of the fixed-point - * \tparam R Return type of the fixed-point representation - * \tparam T Input type of the floating point representation - * \param number The floating point number to convert to fixed point - * \return The converted value - */ - -/** - * \fn R fixedToFloatingPoint(T number) - * \brief Convert a fixed-point number to a floating point representation - * \tparam I Bit width of the integer part of the fixed-point - * \tparam F Bit width of the fractional part of the fixed-point - * \tparam R Return type of the floating point representation - * \tparam T Input type of the fixed-point representation - * \param number The fixed point number to convert to floating point - * \return The converted value - */ - /** * \struct libcamera::ipa::FixedPointQTraits * \brief Traits type implementing fixed-point quantization conversions diff --git a/src/ipa/libipa/fixedpoint.h b/src/ipa/libipa/fixedpoint.h index df685e852bff..ed8aca9f150f 100644 --- a/src/ipa/libipa/fixedpoint.h +++ b/src/ipa/libipa/fixedpoint.h @@ -16,55 +16,6 @@ namespace libcamera { namespace ipa { -#ifndef __DOXYGEN__ -template && - std::is_floating_point_v> * = nullptr> -#else -template -#endif -constexpr R floatingToFixedPoint(T number) -{ - static_assert(sizeof(int) >= sizeof(R)); - static_assert(I + F <= sizeof(R) * 8); - - /* - * The intermediate cast to int is needed on arm platforms to properly - * cast negative values. See - * https://embeddeduse.com/2013/08/25/casting-a-negative-float-to-an-unsigned-int/ - */ - R mask = (1 << (F + I)) - 1; - R frac = static_cast(static_cast(std::round(number * (1 << F)))) & mask; - - return frac; -} - -#ifndef __DOXYGEN__ -template && - std::is_integral_v> * = nullptr> -#else -template -#endif -constexpr R fixedToFloatingPoint(T number) -{ - static_assert(sizeof(int) >= sizeof(T)); - static_assert(I + F <= sizeof(T) * 8); - - if constexpr (std::is_unsigned_v) - return static_cast(number) / static_cast(T{ 1 } << F); - - /* - * Recreate the upper bits in case of a negative number by shifting the sign - * bit from the fixed point to the first bit of the unsigned and then right shifting - * by the same amount which keeps the sign bit in place. - * This can be optimized by the compiler quite well. - */ - int remaining_bits = sizeof(int) * 8 - (I + F); - int t = static_cast(static_cast(number) << remaining_bits) >> remaining_bits; - return static_cast(t) / static_cast(1 << F); -} - template struct FixedPointQTraits { private: @@ -97,11 +48,23 @@ public: static constexpr float toFloat(QuantizedType q) { - return fixedToFloatingPoint(q); + if constexpr (std::is_unsigned_v) + return static_cast(q) / static_cast(UT{ 1 } << F); + + /* + * Recreate the upper bits in case of a negative number by + * shifting the sign bit from the fixed point to the first bit + * of the unsigned and then right shifting by the same amount + * which keeps the sign bit in place. This can be optimized by + * the compiler quite well. + */ + unsigned int remaining_bits = sizeof(UT) * 8 - (I + F); + T t = static_cast(static_cast(q) << remaining_bits) >> remaining_bits; + return static_cast(t) / static_cast(UT{ 1 } << F); } - static constexpr float min = fixedToFloatingPoint(static_cast(qMin)); - static constexpr float max = fixedToFloatingPoint(static_cast(qMax)); + static constexpr float min = toFloat(qMin); + static constexpr float max = toFloat(qMax); static_assert(min < max, "FixedPointQTraits: Minimum must be less than maximum"); @@ -109,7 +72,13 @@ public: static QuantizedType fromFloat(float v) { v = std::clamp(v, min, max); - return floatingToFixedPoint(v); + + /* + * The intermediate cast to int is needed on arm platforms to + * properly cast negative values. See + * https://embeddeduse.com/2013/08/25/casting-a-negative-float-to-an-unsigned-int/ + */ + return static_cast(static_cast(std::round(v * (1 << F)))) & bitMask; } };