From patchwork Mon Aug 3 12:07:53 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 27547 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 1D6A9C328C for ; Mon, 3 Aug 2026 12:30:30 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 88DB468071; Mon, 3 Aug 2026 14:30:30 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="cIln9mOB"; dkim-atps=neutral Received: from mail-wm1-x332.google.com (mail-wm1-x332.google.com [IPv6:2a00:1450:4864:20::332]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8F37768069 for ; Mon, 3 Aug 2026 14:30:29 +0200 (CEST) Received: by mail-wm1-x332.google.com with SMTP id 5b1f17b1804b1-495590dde14so20389995e9.0 for ; Mon, 03 Aug 2026 05:30:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1785760229; x=1786365029; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=6sCb2Flj7Tg1+PVP4S5OmXEko1eJ2hZmIe43/SIZYRw=; b=cIln9mOBSJxKOkoCH/al95hQmSn0S0yMTPowU7NediFzQ5Hu144LyiQyRiFZaS3eFh WYB+WimGe/pbEN5j5quv3MonQC5IoABdohheR+GGM40z8rLhJi6Dddhb51uO6kvSdVgK jGm43hRUK1wUPWvenBMk3rSxPLsxmvGlgLf++0OoU4KbnKEwBGhxYYRhXJQIEr8WFHV9 /IZFyAgja1/D1mfHQy5blnSKAoJAwI6jQyTrfDbceoc371QlWTXVhOWj25A24l+xNE+V 13eZwOXx4MfpXm29kshSK3F5MkpsgBbeBZyc+fUB6yR3UBcoVXnkzePhDUSLWJgG+YNK UaQg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785760229; x=1786365029; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=6sCb2Flj7Tg1+PVP4S5OmXEko1eJ2hZmIe43/SIZYRw=; b=iA3wSMLW2fE3cubGHuXmExA7BuhKP2soHbHcD5snJL3mf2bgpqdJgfeE04OLoOWdud oB4XypVwnv1fxAhMUrBrwnCkWRef9aiewlDhWVWO6yDAY8zuk/uBBu7e8pXBLYsob8vd kMy2AOSMJ2hDyJZozgMfwOpGFSGTEtzcgz+iFrb0pk64NfV4HrIszQ0T/4zn7OHzfnSG MTRIEJzhMyv2yIjdY3kF5BDSyBU1Tg/xMOsUvpggunGRg4KyP2GwakLvlEOHE1kxhPTo 29hL666yzWRI1gSd+IUUmUFK/9H3AB0MACtGn4OalSeytmHdGmM1r8gutoILBlMQx8Z0 uRZw== X-Gm-Message-State: AOJu0YxrwYdoSa0Abid/KcFxNlCm43PDinaU4ktiFNlLEoh5GFMAK3zV QqYkw/ApSoZ28PEIfd6CsJYKUY/4Tb0ofoPy0wisqlWd2oFTiRTPljopuuAyMEv9NbRy3KjzuRW zr/e+ X-Gm-Gg: AR+sD12JUMLr/Z4+yAZLlX/CroDNH3K4zZkqBLNGHv7mFik/Vq2DXIeEr4JpXbCyJ4c ddIZwknHNZBaEeU5qK74l4S04L9mcKjCMs4wnW7LxPkANXdVzc1Yxr6tasmSm1yvP837Du0/KUQ 4KYShmX+5Dm0NMR1OU4nvztGpmavRO4iGfjJtzg5si+ssgxKvxhM4Dt3RmjaKYNLxY8UfiLaexG dDGd1YxjuP05G+NYIJ8c3NqGwK0gq6TdV2kCti7s5aBUlwryqhBf9LoXmS9sv2NWRkcNMxf//s5 0IX9Z80/uchiBZxYAjevrEgEtlUq2ekeUvVXxM/f8Mres7WcqmpOq2/j7S4zBZBuO5FZufHlzjA xi2Yd9TT4gvaC9g2/bHPn9CcU49EwH+jbPAeVilxntQa5jrtovqK88huIZ7YDV140RGd/tfnuQx 7omHHqnquJO7/l25+GeTMBA0k8iMtLp4O6rsAfahR0rJDGoZhHpEpgaQX1tFHVsFvKRlZqUMZ67 zq0S6ZIXhb+OVXyi7wFN1OFroOPi74rQzx860OLMUM2I+h3kZTh7XaJA6Fo28+psk8sbKwIkIsp oMCB X-Received: by 2002:a05:600c:4f4a:b0:495:6478:2dbc with SMTP id 5b1f17b1804b1-4980eea02e5mr205696185e9.6.1785760219094; Mon, 03 Aug 2026 05:30:19 -0700 (PDT) Received: from davidp-pi5.pitowers.org ([2a00:1098:3142:1f:4706:89ee:c47e:1087]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49808690ffbsm256452635e9.10.2026.08.03.05.30.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 03 Aug 2026 05:30:18 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Cc: David Plowman Subject: [PATCH 2/4] ipa: rpi: Allow black levels to vary with analogue gain Date: Mon, 3 Aug 2026 13:07:53 +0100 Message-ID: <20260803123012.10175-3-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260803123012.10175-1-david.plowman@raspberrypi.com> References: <20260803123012.10175-1-david.plowman@raspberrypi.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" Optionally, a PWL (piecewise linear) function can be specified as the black level instead of a constant value. This PWL is evaluated at the current analogue gain for each frame. Tuning files will also accept "black_level_func" as a shorthand for all three channels. Signed-off-by: David Plowman --- src/ipa/rpi/controller/rpi/black_level.cpp | 33 +++++++++++++++++++--- src/ipa/rpi/controller/rpi/black_level.h | 7 +++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/ipa/rpi/controller/rpi/black_level.cpp b/src/ipa/rpi/controller/rpi/black_level.cpp index 42ea15050..876bc661d 100644 --- a/src/ipa/rpi/controller/rpi/black_level.cpp +++ b/src/ipa/rpi/controller/rpi/black_level.cpp @@ -41,12 +41,29 @@ int BlackLevel::read(const libcamera::ValueNode ¶ms) << " Read black levels red " << blackLevelR_ << " green " << blackLevelG_ << " blue " << blackLevelB_; + + /* Allow "black_level_func" as a shorthand for all 3 colours. */ + libcamera::ipa::Pwl blackLevelFunc; + blackLevelFunc = params["black_level_func"].get(ipa::Pwl{}); + blackLevelFuncR_ = params["black_level_func_r"].get(blackLevelFunc); + blackLevelFuncG_ = params["black_level_func_g"].get(blackLevelFunc); + blackLevelFuncB_ = params["black_level_func_b"].get(blackLevelFunc); + return 0; } void BlackLevel::initialValues(uint16_t &blackLevelR, uint16_t &blackLevelG, uint16_t &blackLevelB) { + if (!blackLevelFuncR_.empty()) + blackLevelR_ = blackLevelFuncR_.eval(1.0); + + if (!blackLevelFuncG_.empty()) + blackLevelG_ = blackLevelFuncG_.eval(1.0); + + if (!blackLevelFuncB_.empty()) + blackLevelB_ = blackLevelFuncB_.eval(1.0); + blackLevelR = blackLevelR_; blackLevelG = blackLevelG_; blackLevelB = blackLevelB_; @@ -54,10 +71,18 @@ void BlackLevel::initialValues(uint16_t &blackLevelR, uint16_t &blackLevelG, void BlackLevel::prepare(Metadata *imageMetadata) { - /* - * Possibly we should think about doing this in a switchMode or - * something? - */ + DeviceStatus deviceStatus; + if (!imageMetadata->get("device.status", deviceStatus)) { + if (!blackLevelFuncR_.empty()) + blackLevelR_ = blackLevelFuncR_.eval(deviceStatus.analogueGain); + + if (!blackLevelFuncG_.empty()) + blackLevelG_ = blackLevelFuncG_.eval(deviceStatus.analogueGain); + + if (!blackLevelFuncB_.empty()) + blackLevelB_ = blackLevelFuncB_.eval(deviceStatus.analogueGain); + } + struct BlackLevelStatus status; status.blackLevelR = blackLevelR_; status.blackLevelG = blackLevelG_; diff --git a/src/ipa/rpi/controller/rpi/black_level.h b/src/ipa/rpi/controller/rpi/black_level.h index dbf29b282..34bb81548 100644 --- a/src/ipa/rpi/controller/rpi/black_level.h +++ b/src/ipa/rpi/controller/rpi/black_level.h @@ -6,6 +6,8 @@ */ #pragma once +#include + #include "../black_level_algorithm.h" #include "../black_level_status.h" @@ -27,6 +29,11 @@ private: double blackLevelR_; double blackLevelG_; double blackLevelB_; + + /* Black levels can vary with analogue gain instead of being constant. */ + libcamera::ipa::Pwl blackLevelFuncR_; + libcamera::ipa::Pwl blackLevelFuncG_; + libcamera::ipa::Pwl blackLevelFuncB_; }; } /* namespace RPiController */