From patchwork Mon Aug 24 09:13:58 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: 28071 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 4FC05C333C for ; Mon, 24 Aug 2026 09:15:11 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E9FF068455; Mon, 24 Aug 2026 11:15:10 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="pja5KmYM"; 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 8EF47683D5 for ; Mon, 24 Aug 2026 11:14:21 +0200 (CEST) Received: from pb-laptop.local (185.221.143.32.nat.pool.zt.hu [185.221.143.32]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 49118C3C; Mon, 24 Aug 2026 11:12:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1787562777; bh=gvzQtB26+5qhgERXlgFWe+w4KJk+BMSzAacIZgMO4jQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pja5KmYMhdwFpBE1djMc/gP3t0u2yH36g7vqkR7kJt4Zg3oFyOLhZYq7xZ+6ekChU DfCeZRb2kM9tK66D5e4Wi36EzkqXEM/Mscbb8C1jdCgxyx1SRiLMwE43qsxif5ynCa I/EvFj+sRnW0xeaSFsiOCt+A0acxK0mTJyjEzLWU= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal Subject: [PATCH v8 39/47] ipa: softisp: agc: Adjust histogram for black level Date: Mon, 24 Aug 2026 11:13:58 +0200 Message-ID: <20260824091407.502020-40-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260824091407.502020-1-barnabas.pocze@ideasonboard.com> References: <20260824091407.502020-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/softisp/algorithms/agc.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/ipa/softisp/algorithms/agc.cpp b/src/ipa/softisp/algorithms/agc.cpp index 0027365b33..b56d57cfeb 100644 --- a/src/ipa/softisp/algorithms/agc.cpp +++ b/src/ipa/softisp/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 std::nullopt; - 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 * histogram.size() / 256; + + for (unsigned int i = 0; i < blackLevelHistIdx; i++) + histogram[blackLevelHistIdx] += histogram[i]; + + auto exposureMSV = calculateMSV({ { histogram.begin() + blackLevelHistIdx, histogram.end() } }); if (!exposureMSV) { LOG(IPASoftIspExposure, Debug) << "Not adjusting exposure due to insufficient histogram data";