From patchwork Fri Jul 3 12:25:15 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 27168 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 090AAC328C for ; Fri, 3 Jul 2026 12:26:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A5F4165FD8; Fri, 3 Jul 2026 14:26:33 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="rMV/2muO"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 53E2965FD8 for ; Fri, 3 Jul 2026 14:26:32 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (unknown [IPv6:2404:7a81:160:2100:a2cc:2f45:3bd7:2589]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 739198E0; Fri, 3 Jul 2026 14:25:43 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1783081546; bh=CzSviiRCwi9H5hJ2YKvMHc9OL7RDud3wqlrh3cartjY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rMV/2muOy6BeQmPaWFkT487SQkngQpBUoF55BQVEdINGtmJtSRbagljD91LC/M4P2 Gg6GoT8mCslGi+DWAaR4TEE8FFjdfgAC7pfRsJq4Bs4QVMwdTgAndSsz/gMeKrjTch ZA/XUm3fjFi/W8uUX6jF0TttijM+ivGIKoR/59dU= From: Paul Elder To: laurent.pinchart@ideasonboard.com Cc: Paul Elder , michael.riesch@collabora.com, xuhf@rock-chips.com, stefan.klug@ideasonboard.com, kieran.bingham@ideasonboard.com, dan.scally@ideasonboard.com, jacopo.mondi@ideasonboard.com, nicolas.dufresne@collabora.com, libcamera-devel@lists.libcamera.org Subject: [RFC PATCH 09/19] ipa: rkisp2: algo: awb: Implement automatic white balance control Date: Fri, 3 Jul 2026 21:25:15 +0900 Message-ID: <20260703122543.1991189-10-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20260703122543.1991189-1-paul.elder@ideasonboard.com> References: <20260703122543.1991189-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Implement auto white balance control algorithm for the rkisp2 IPA. This leverages the libipa awb, so it supports baysian awb, grey world awb, as well as manual white balance. Signed-off-by: Paul Elder --- src/ipa/rkisp2/algorithms/awb.cpp | 174 ++++++++++++++++++++++++++ src/ipa/rkisp2/algorithms/awb.h | 56 +++++++++ src/ipa/rkisp2/algorithms/meson.build | 1 + 3 files changed, 231 insertions(+) create mode 100644 src/ipa/rkisp2/algorithms/awb.cpp create mode 100644 src/ipa/rkisp2/algorithms/awb.h diff --git a/src/ipa/rkisp2/algorithms/awb.cpp b/src/ipa/rkisp2/algorithms/awb.cpp new file mode 100644 index 000000000000..4928ab13d220 --- /dev/null +++ b/src/ipa/rkisp2/algorithms/awb.cpp @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas On Board + * + * AWB control algorithm + */ + +#include "awb.h" + +#include + +#include + +#include + +#include "libcamera/internal/vector.h" + +/** + * \file awb.h + */ + +namespace libcamera { + +namespace ipa::rkisp2::algorithms { + +LOG_DEFINE_CATEGORY(RkISP2Awb) + +class RkISP2AwbStats final : public AwbStats +{ +public: + RkISP2AwbStats() = default; + RkISP2AwbStats(const RGB means) + : AwbStats(means) + { + } + + /* Minimum mean value below which AWB can't operate. */ + double minColourValue() const override + { + return 2.0; + } +}; + +namespace { + +} /* namespace */ + +/** + * \class Awb + * \brief Manage the white balance with automatic and manual controls + */ + +Awb::Awb() +{ +} + +/** + * \copydoc libcamera::ipa::Algorithm::init + */ +int Awb::init(IPAContext &context, const ValueNode &tuningData) +{ + return awbAlgo_.init(tuningData, context.ctrlMap); +} + +int Awb::configure(IPAContext &context, + const IPACameraSensorInfo &configInfo) +{ + awbAlgo_.configure(context.activeState.awb); + + context.configuration.awb.measureWindow.h_offs = 0; + context.configuration.awb.measureWindow.v_offs = 0; + /* + * Unlike ae lite, this seems to still work when height == full window + * height + */ + context.configuration.awb.measureWindow.h_size = configInfo.outputSize.width / 15; + context.configuration.awb.measureWindow.v_size = configInfo.outputSize.height / 15; + + return 0; +} + +/** + * \copydoc libcamera::ipa::Algorithm::queueRequest + */ +void Awb::queueRequest(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const ControlList &controls) +{ + awbAlgo_.queueRequest(context.activeState.awb, frame, frameContext.awb, + controls); +} + +/** + * \copydoc libcamera::ipa::Algorithm::prepare + */ +void Awb::prepare([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, RkISP2Params *params) +{ + awbAlgo_.prepare(context.activeState.awb, frameContext.awb); + + auto gainConfig = params->block(); + gainConfig.setEnabled(true); + + RGB gains = frameContext.awb.gains; + + /* \todo Use quantized class */ + gainConfig->gains[0].gb = std::clamp(256 * 1.0, 0, 0x3fff); + gainConfig->gains[0].b = std::clamp(256 * gains.b(), 0, 0x3fff); + gainConfig->gains[0].r = std::clamp(256 * gains.r(), 0, 0x3fff); + gainConfig->gains[0].gr = std::clamp(256 * 1.0, 0, 0x3fff); + + auto measConfig = params->block(); + measConfig.setEnabled(true); + + measConfig->meas_window = context.configuration.awb.measureWindow; + struct rkisp2_isp_awb_color_quad minLimits = { 0, 0, 0, 0 }; + struct rkisp2_isp_awb_color_quad maxLimits = { 255, 255, 255, 255 }; + measConfig->limits[0] = minLimits; + measConfig->limits[1] = maxLimits; + + for (unsigned int i = 0; i < RKISP2_ISP_AWB_COUNTS_SIZE; i++) + measConfig->weights[i] = 0x20; +} + +/** + * \copydoc libcamera::ipa::Algorithm::process + */ +void Awb::process([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + const rkisp2_stats_buffer *stats, + ControlList &metadata) +{ + RkISP2AwbStats awbStats = calculateRgbMeans(frameContext, stats); + + awbAlgo_.process(context.activeState.awb, frameContext.awb, awbStats, + frameContext.lux.lux, metadata); +} + +RkISP2AwbStats Awb::calculateRgbMeans([[maybe_unused]] const IPAFrameContext &frameContext, + const rkisp2_stats_buffer *stats) const +{ + if (!stats->awb.done) { + LOG(RkISP2Awb, Error) << "No awb stats"; + return {}; + } + + std::array counts; + RGB means; + + for (size_t i = 0; i < RKISP2_ISP_AWB_COUNTS_SIZE; i++) + counts[i] = static_cast(stats->awb.counts_r[i]); + means.r() = std::accumulate(counts.begin(), counts.end(), 0) / counts.size(); + + for (size_t i = 0; i < RKISP2_ISP_AWB_COUNTS_SIZE; i++) + counts[i] = static_cast(stats->awb.counts_g[i]); + means.g() = std::accumulate(counts.begin(), counts.end(), 0) / counts.size(); + + for (size_t i = 0; i < RKISP2_ISP_AWB_COUNTS_SIZE; i++) + counts[i] = static_cast(stats->awb.counts_b[i]); + means.b() = std::accumulate(counts.begin(), counts.end(), 0) / counts.size(); + + for (size_t i = 0; i < RKISP2_ISP_AWB_COUNTS_SIZE; i++) + counts[i] = static_cast(stats->awb.counts_w[i]); + + return RkISP2AwbStats(means); +} + +REGISTER_IPA_ALGORITHM(Awb, "Awb") + +} /* namespace ipa::rkisp2::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/rkisp2/algorithms/awb.h b/src/ipa/rkisp2/algorithms/awb.h new file mode 100644 index 000000000000..5f25ae1f1f05 --- /dev/null +++ b/src/ipa/rkisp2/algorithms/awb.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas On Board + * + * AWB control algorithm + */ + +#pragma once + +#include + +#include + +#include "libcamera/internal/value_node.h" + +#include "libipa/awb.h" +#include "libipa/fixedpoint.h" + +#include "algorithm.h" +#include "ipa_context.h" +#include "params.h" + +namespace libcamera { + +namespace ipa::rkisp2::algorithms { + +class RkISP2AwbStats; + +class Awb : public Algorithm +{ +public: + Awb(); + ~Awb() = default; + + int init(IPAContext &context, const ValueNode &tuningData) override; + int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override; + void queueRequest(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const ControlList &controls) override; + void prepare(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + RkISP2Params *params) override; + void process(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const rkisp2_stats_buffer *stats, + ControlList &metadata) override; + +private: + RkISP2AwbStats calculateRgbMeans(const IPAFrameContext &frameContext, + const rkisp2_stats_buffer *stats) const; + + AwbAlgorithm> awbAlgo_; +}; + +} /* namespace ipa::rkisp2::algorithms */ +} /* namespace libcamera */ diff --git a/src/ipa/rkisp2/algorithms/meson.build b/src/ipa/rkisp2/algorithms/meson.build index 36996918b39e..30133ac4fa68 100644 --- a/src/ipa/rkisp2/algorithms/meson.build +++ b/src/ipa/rkisp2/algorithms/meson.build @@ -1,6 +1,7 @@ # SPDX-License-Identifier: CC0-1.0 rkisp2_ipa_algorithms = files([ + 'awb.cpp', 'bls.cpp', ])