From patchwork Mon Aug 17 11:43:40 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: 27815 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 02B59C3263 for ; Mon, 17 Aug 2026 11:44:59 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5D40668301; Mon, 17 Aug 2026 13:44:59 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="rYnkrymQ"; 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 15E07682B2 for ; Mon, 17 Aug 2026 13:44:06 +0200 (CEST) Received: from pb-laptop.local (catv-89-132-78-151.catv.fixed.one.hu [89.132.78.151]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id CB6691B41; Mon, 17 Aug 2026 13:42:46 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1786966967; bh=kMOM0V+0/wpMA8xwQrsTELD01dj+v7NBgHh8h+nD0s8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rYnkrymQc/tk8oUfQfGKvVCAcGYdVWY/1ZtyA48qe47OkUl6doLeJM6SE5rrhSuFu vyAqw4owK51ETXqckrAVI4PQLfq59NBMui6deqzDwSZ62HetH+NKqfHjONcWa+lEjW kOGBYn4WtC3sEVtDU6EQzqZ/BSUt91bwrIPAn1Dk= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal Subject: [PATCH v5 39/47] ipa: simple: agc: Adjust histogram for black level Date: Mon, 17 Aug 2026 13:43:40 +0200 Message-ID: <20260817114349.994123-40-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260817114349.994123-1-barnabas.pocze@ideasonboard.com> References: <20260817114349.994123-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" Instead of providing the black separately, adjust the luminance histogram according to the current black level. Everything under the black level is summed into the bin corresponding to the current black level, and the MSV calculation is carried out on the partial histogram that starts at the bin of the black level. This changes the behaviour slightly as previously everything under the black level was ignored, and only the remaining part was split into the 5 bins, but now the ignored bins are instead summed into the "first" bin of the "new" histogram. This can result in the same image with the same statistics being perceived as darker by the algorithm. Signed-off-by: Barnabás Pőcze Reviewed-by: Milan Zamazal --- src/ipa/simple/algorithms/agc.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp index e24eacde72..a3e0ebbc3d 100644 --- a/src/ipa/simple/algorithms/agc.cpp +++ b/src/ipa/simple/algorithms/agc.cpp @@ -66,19 +66,15 @@ static constexpr float kExpMaxStep = 0.15; namespace { -std::optional calculateMSV(const Histogram &histogram, uint8_t blackLevel) +std::optional calculateMSV(const Histogram &histogram) { /* * Calculate Mean Sample Value (MSV) according to formula from: * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf */ - const unsigned int blackLevelHistIdx = - blackLevel * histogram.bins() / 256; - const unsigned int histogramSize = - histogram.bins() - blackLevelHistIdx; - const unsigned int yHistValsPerBin = histogramSize / kExposureBinsCount; + const unsigned int yHistValsPerBin = histogram.bins() / kExposureBinsCount; const unsigned int yHistValsPerBinMod = - histogramSize / (histogramSize % kExposureBinsCount + 1); + histogram.bins() / (histogram.bins() % kExposureBinsCount + 1); int exposureBins[kExposureBinsCount] = {}; unsigned int denom = 0; unsigned int num = 0; @@ -86,9 +82,9 @@ std::optional calculateMSV(const Histogram &histogram, uint8_t blackLevel if (yHistValsPerBin == 0) return {}; - for (unsigned int i = 0; i < histogramSize; i++) { + for (unsigned int i = 0; i < histogram.bins(); i++) { unsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin; - exposureBins[idx] += histogram[blackLevelHistIdx + i]; + exposureBins[idx] += histogram[i]; } for (unsigned int i = 0; i < kExposureBinsCount; i++) { @@ -193,7 +189,14 @@ void Agc::process(IPAContext &context, return; } - auto exposureMSV = calculateMSV({ stats->yHistogram }, context.activeState.blc.level); + auto histogram = stats->yHistogram; + const unsigned int blackLevelHistIdx = + context.activeState.blc.level * std::size(histogram) / 256; + + for (unsigned int i = 0; i < blackLevelHistIdx; i++) + histogram[blackLevelHistIdx] += histogram[i]; + + auto exposureMSV = calculateMSV({ { histogram.begin() + blackLevelHistIdx, histogram.end() } }); if (!exposureMSV) { LOG(IPASoftExposure, Debug) << "Not adjusting exposure due to insufficient histogram data";