From patchwork Fri Jul 3 12:25:14 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 27167 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 6CB45C328C for ; Fri, 3 Jul 2026 12:26:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1706F65FD9; Fri, 3 Jul 2026 14:26:31 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="fUVSbv7M"; 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 C4D9765FCA for ; Fri, 3 Jul 2026 14:26:28 +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 E45811121; Fri, 3 Jul 2026 14:25:39 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1783081542; bh=b9X1JcNno86vhftBspY6DOelKj8JrllY5rJUTzW1Ne0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=fUVSbv7Mipyu65I7+rrDc0qSmJQbsUBVpMA5a0BhNfN00UsJruiJ7b0P2sEwSrpEh p4w0S97SQgMKeMrHBmB6BHi2DHiR47Bf3sGHc26SfnfgwfGVamK8J6MmbskBsZe4iK jif/zsrH3eQ9MXOG9TtYGkkXTu88a8DGiAQGcTM8= 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 08/19] ipa: rkisp2: algo: bls: Implement black level subtraction Date: Fri, 3 Jul 2026 21:25:14 +0900 Message-ID: <20260703122543.1991189-9-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 a static black level subtraction algorithm for the rkisp2 IPA. Signed-off-by: Paul Elder --- src/ipa/rkisp2/algorithms/bls.cpp | 154 ++++++++++++++++++++++++++ src/ipa/rkisp2/algorithms/bls.h | 41 +++++++ src/ipa/rkisp2/algorithms/meson.build | 1 + 3 files changed, 196 insertions(+) create mode 100644 src/ipa/rkisp2/algorithms/bls.cpp create mode 100644 src/ipa/rkisp2/algorithms/bls.h diff --git a/src/ipa/rkisp2/algorithms/bls.cpp b/src/ipa/rkisp2/algorithms/bls.cpp new file mode 100644 index 000000000000..aba2b7a867be --- /dev/null +++ b/src/ipa/rkisp2/algorithms/bls.cpp @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas On Board + * + * RkISP2 Black Level Subtraction control + */ + +#include "bls.h" + +#include + +#include + +#include + +/** + * \file bls.h + */ + +namespace libcamera { + +namespace ipa::rkisp2::algorithms { + +/** + * \class BlackLevelSubtraction + * \brief RkISP2 Black Level Subtraction control + * + * The pixels output by the camera normally include a black level, because + * sensors do not always report a signal level of '0' for black. Pixels at or + * below this level should be considered black. To achieve that, the RkISP2 BLS + * algorithm subtracts a configurable offset from all pixels. + * + * The black level can be measured at runtime from an optical dark region of the + * camera sensor, or measured during the camera tuning process. The first option + * isn't currently supported. + * + * \todo Add support for black level in the metadata so that we can capture + * proper raw images for tuning + */ + +LOG_DEFINE_CATEGORY(RkISP2Bls) + +/** + * \copydoc libcamera::ipa::Algorithm::init + */ +int BlackLevelSubtraction::init(IPAContext &context, const ValueNode &tuningData) +{ + std::optional levelRed = tuningData["R"].get(); + std::optional levelGreenR = tuningData["Gr"].get(); + std::optional levelGreenB = tuningData["Gb"].get(); + std::optional levelBlue = tuningData["B"].get(); + bool tuningHasLevels = levelRed && levelGreenR && levelGreenB && levelBlue; + + auto blackLevel = context.camHelper->blackLevel(); + if (!blackLevel) { + /* + * Not all camera sensor helpers have been updated with black + * levels. Print a warning and fall back to the levels from the + * tuning data to preserve backward compatibility. This should + * be removed once all helpers provide the data. + */ + LOG(RkISP2Bls, Warning) + << "No black levels provided by camera sensor helper" + << ", please fix"; + + blackLevelRed_ = levelRed.value_or(4096); + blackLevelGreenR_ = levelGreenR.value_or(4096); + blackLevelGreenB_ = levelGreenB.value_or(4096); + blackLevelBlue_ = levelBlue.value_or(4096); + } else if (tuningHasLevels) { + /* + * If black levels are provided in the tuning file, use them to + * avoid breaking existing camera tuning. This is deprecated and + * will be removed. + */ + LOG(RkISP2Bls, Warning) + << "Deprecated: black levels overwritten by tuning file"; + + blackLevelRed_ = *levelRed; + blackLevelGreenR_ = *levelGreenR; + blackLevelGreenB_ = *levelGreenB; + blackLevelBlue_ = *levelBlue; + } else { + blackLevelRed_ = *blackLevel; + blackLevelGreenR_ = *blackLevel; + blackLevelGreenB_ = *blackLevel; + blackLevelBlue_ = *blackLevel; + } + + LOG(RkISP2Bls, Debug) + << "Black levels: red " << blackLevelRed_ + << ", green (red) " << blackLevelGreenR_ + << ", green (blue) " << blackLevelGreenB_ + << ", blue " << blackLevelBlue_; + + return 0; +} + +int BlackLevelSubtraction::configure([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const IPACameraSensorInfo &configInfo) +{ + /* \todo Save the cfa */ + + return 0; +} + +/** + * \copydoc libcamera::ipa::Algorithm::prepare + */ +void BlackLevelSubtraction::prepare(IPAContext &context, + const uint32_t frame, + [[maybe_unused]] IPAFrameContext &frameContext, + RkISP2Params *params) +{ + if (context.configuration.raw) + return; + + if (frame > 1) + return; + + auto config = params->block(); + config.setEnabled(true); + + config->enable_auto = 0; + + /* Scale down to the 12-bit black levels used by the BLS block. */ + /* \todo Handle cfa properly */ + config->bls_fixed_val.a = blackLevelRed_ >> 4; + config->bls_fixed_val.b = blackLevelGreenR_ >> 4; + config->bls_fixed_val.c = blackLevelGreenB_ >> 4; + config->bls_fixed_val.d = blackLevelBlue_ >> 4; +} + +/** + * \copydoc libcamera::ipa::Algorithm::process + */ +void BlackLevelSubtraction::process([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + [[maybe_unused]] IPAFrameContext &frameContext, + [[maybe_unused]] const rkisp2_stats_buffer *stats, + ControlList &metadata) +{ + metadata.set(controls::SensorBlackLevels, + { static_cast(blackLevelRed_), + static_cast(blackLevelGreenR_), + static_cast(blackLevelGreenB_), + static_cast(blackLevelBlue_) }); +} + +REGISTER_IPA_ALGORITHM(BlackLevelSubtraction, "BlackLevelSubtraction") + +} /* namespace ipa::rkisp2::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/rkisp2/algorithms/bls.h b/src/ipa/rkisp2/algorithms/bls.h new file mode 100644 index 000000000000..5377ae76a029 --- /dev/null +++ b/src/ipa/rkisp2/algorithms/bls.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Ideas On Board + * + * RkISP2 Black Level Subtraction control + */ + +#pragma once + +#include "algorithm.h" + +namespace libcamera { + +namespace ipa::rkisp2::algorithms { + +class BlackLevelSubtraction : public Algorithm +{ +public: + BlackLevelSubtraction() = default; + ~BlackLevelSubtraction() = default; + + int init(IPAContext &context, const ValueNode &tuningData) override; + int configure(IPAContext &context, + const IPACameraSensorInfo &configInfo) 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: + int16_t blackLevelRed_; + int16_t blackLevelGreenR_; + int16_t blackLevelGreenB_; + int16_t blackLevelBlue_; +}; + +} /* namespace ipa::rkisp2::algorithms */ +} /* namespace libcamera */ diff --git a/src/ipa/rkisp2/algorithms/meson.build b/src/ipa/rkisp2/algorithms/meson.build index 4bb81e48eb01..36996918b39e 100644 --- a/src/ipa/rkisp2/algorithms/meson.build +++ b/src/ipa/rkisp2/algorithms/meson.build @@ -1,5 +1,6 @@ # SPDX-License-Identifier: CC0-1.0 rkisp2_ipa_algorithms = files([ + 'bls.cpp', ])