From patchwork Thu Nov 5 09:01:55 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= X-Patchwork-Id: 28770 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 54E6EC3307 for ; Thu, 5 Nov 2026 09:02:02 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 23B226914A; Thu, 5 Nov 2026 10:02:01 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="D32V5Ba6"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C429769113 for ; Thu, 5 Nov 2026 10:01:59 +0100 (CET) Received: from pb-laptop.local (185.221.143.99.nat.pool.zt.hu [185.221.143.99]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 272E933D for ; Thu, 5 Nov 2026 09:59:41 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1793869181; bh=V56scLtE/GASWtxm7CFA6FYFrhIPKMC+3d8oeJPoz6o=; h=From:To:Subject:Date:From; b=D32V5Ba619/Xc7dNI6LIHm0l41tMAMn/StjPYWm3glycUs2CbvLSTmLfwibVTw/EX TtamL/lRw2b1WkRVNEsD7rcV+nKnjMlkS0bZh6FuhqsXuTVI882qeT1CrCV5ZbAjVq KntLwqkMDHb/vT74P9JG4ja9q9sI7SipyhRVQ1qI= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Subject: [RFC PATCH v1 1/2] ipa: libipa: histogram: Add `empty()` Date: Thu, 5 Nov 2026 10:01:55 +0100 Message-ID: <20261105090156.527585-1-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.56.0 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" Empty histograms are usually of little use, so add a function that checks if the histogram contains any data points. Signed-off-by: Barnabás Pőcze Reviewed-by: Kieran Bingham --- src/ipa/libipa/histogram.cpp | 10 ++++++++++ src/ipa/libipa/histogram.h | 1 + 2 files changed, 11 insertions(+) diff --git a/src/ipa/libipa/histogram.cpp b/src/ipa/libipa/histogram.cpp index 9e46a05a90..c516ace7da 100644 --- a/src/ipa/libipa/histogram.cpp +++ b/src/ipa/libipa/histogram.cpp @@ -74,6 +74,16 @@ Histogram::Histogram(std::span data) * \return Number of values */ +/** + * \fn Histogram::empty() + * \brief Determine if the histogram is empty + * + * An empty histogram is one which has only empty bins, + * that is, bins with a count of 0. + * + * \return True if the histogram is empty + */ + /** * \fn Histogram::operator[](size_t bin) const * \brief Retrieve the value for \a bin diff --git a/src/ipa/libipa/histogram.h b/src/ipa/libipa/histogram.h index b50868ab4d..cbc34e8c70 100644 --- a/src/ipa/libipa/histogram.h +++ b/src/ipa/libipa/histogram.h @@ -41,6 +41,7 @@ public: uint64_t cumulativeFrequency(double bin) const; double quantile(double q, uint32_t first = 0, uint32_t last = UINT_MAX) const; double interQuantileMean(double lowQuantile, double hiQuantile) const; + bool empty() const { return total() == 0; } uint32_t operator[](size_t bin) const { From patchwork Thu Nov 5 09:01:56 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= X-Patchwork-Id: 28771 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 E6B0DC335C for ; Thu, 5 Nov 2026 09:02:03 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id ADD4B69123; Thu, 5 Nov 2026 10:02:01 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="RbOWJo+w"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E0C786911A for ; Thu, 5 Nov 2026 10:01:59 +0100 (CET) Received: from pb-laptop.local (185.221.143.99.nat.pool.zt.hu [185.221.143.99]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 5E14A1494 for ; Thu, 5 Nov 2026 09:59:41 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1793869181; bh=fqTDoKEmSe+rCjCOF4WhOGhoZKJkqtcQPse4Ui1Im0U=; h=From:To:Subject:Date:In-Reply-To:References:From; b=RbOWJo+wouq1UfFN/TUeonaITJR3JjT3UnLrs1C6rhWZdBmfYpjYfRoMI125AXlSF HpE2dCy3ZFngzn4/oDpYbqSItQnlHsLJsEe0cqXaPpHOpu8jvys40jHXBdrYXK5rxT gdtOuRQRI0aw/Nh3vQiLByWiqvtpBnh7T7E9Lla4= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Subject: [RFC PATCH v1 2/2] ipa: mali-c55: agc: Ignore invalid statistics Date: Thu, 5 Nov 2026 10:01:56 +0100 Message-ID: <20261105090156.527585-2-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261105090156.527585-1-barnabas.pocze@ideasonboard.com> References: <20261105090156.527585-1-barnabas.pocze@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" It is possible that, for example, the first frame has no valid statistics yet. In that case the histograms are empty. This is problematic because e.g. `interQuantileMean()` may return NaN, which causes issues in the calculations (this affects both the color temperature calculation and `AgcTraits::estimanteLuminance()` as well). Previously this has not manifested itself because implementation details in `AgcMeanLuminance` prevented the NaNs from propagating, but 25cb6cd274d3 ("libipa: agc_mean_luminance: Improve inital gain estimation") has changed that. So ignore statistics if any of the color histograms are empty. Signed-off-by: Barnabás Pőcze Reviewed-by: Kieran Bingham --- It is possible that maybe something else should be adjusted in the kernel or libcamera so that even the first image is processed with the right parameters. Because it appears to me the first parameter buffer only takes effect after the first frame. --- src/ipa/mali-c55/algorithms/agc.cpp | 26 ++++++++++++++++---------- src/ipa/mali-c55/algorithms/agc.h | 5 +++++ 2 files changed, 21 insertions(+), 10 deletions(-) -- 2.56.0 diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp index 2c747da891..d665ba5244 100644 --- a/src/ipa/mali-c55/algorithms/agc.cpp +++ b/src/ipa/mali-c55/algorithms/agc.cpp @@ -257,16 +257,22 @@ void Agc::process(IPAContext &context, } statistics_.parseStatistics(stats); - context.activeState.agc.temperatureK = estimateCCT({ { statistics_.rHist.interQuantileMean(0, 1), - statistics_.gHist.interQuantileMean(0, 1), - statistics_.bHist.interQuantileMean(0, 1) } }); - - agc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {{ - .traits = AgcTraits(statistics_), - .yHist = statistics_.yHist, - .exposure = frameContext.sensor.exposure, - .gain = frameContext.sensor.gain, - }}, metadata); + if (statistics_.isValid()) { + context.activeState.agc.temperatureK = estimateCCT( + { { statistics_.rHist.interQuantileMean(0, 1), + statistics_.gHist.interQuantileMean(0, 1), + statistics_.bHist.interQuantileMean(0, 1) } }); + + agc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {{ + .traits = AgcTraits(statistics_), + .yHist = statistics_.yHist, + .exposure = frameContext.sensor.exposure, + .gain = frameContext.sensor.gain, + }}, metadata); + } else { + LOG(MaliC55Agc, Debug) << "ignoring invalid statistics"; + agc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {}, metadata); + } metadata.set(controls::ColourTemperature, context.activeState.agc.temperatureK); } diff --git a/src/ipa/mali-c55/algorithms/agc.h b/src/ipa/mali-c55/algorithms/agc.h index 809a57c53a..998ef57210 100644 --- a/src/ipa/mali-c55/algorithms/agc.h +++ b/src/ipa/mali-c55/algorithms/agc.h @@ -32,6 +32,11 @@ public: uint32_t decodeBinValue(uint16_t binVal); void parseStatistics(const mali_c55_stats_buffer *stats); + bool isValid() const + { + return !rHist.empty() && !gHist.empty() && !bHist.empty(); + } + Histogram rHist; Histogram gHist; Histogram bHist;