From patchwork Fri Oct 11 18:27:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 21600 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 71ADAC3260 for ; Fri, 11 Oct 2024 18:28:20 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 008E965394; Fri, 11 Oct 2024 20:28:19 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="MVjmQ1W3"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6F5C465392 for ; Fri, 11 Oct 2024 20:28:16 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1728671295; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=usHtf8u5MuPnmymKcXXyfopIWLsnb/w1BVM1pLW3SZM=; b=MVjmQ1W3tuOpMXRvVE+W/TL5g6iCRVpEDWOr4XS+jN8sjWPY9tkjUns+SftSpeMghuKLTq HIdRpQWY0P6JjKDS4lsMYcvZM2oXmRD4Pq1uBWXZKnfTtRFGK+NPM6JJxGt52MQRTuP8Nb GbSeDkSTEMHfcyQ0BThOyygK/Qftx58= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-94-fgvTkO6uNBiXfl3fudIVtg-1; Fri, 11 Oct 2024 14:28:12 -0400 X-MC-Unique: fgvTkO6uNBiXfl3fudIVtg-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 4609D1956096; Fri, 11 Oct 2024 18:28:11 +0000 (UTC) Received: from nuthatch.redhat.com (unknown [10.45.224.99]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id ECA0430001A3; Fri, 11 Oct 2024 18:28:09 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal , Kieran Bingham Subject: [PATCH v3 3/4] libcamera: software_isp: Add support for contrast control Date: Fri, 11 Oct 2024 20:27:58 +0200 Message-ID: <20241011182800.1750589-4-mzamazal@redhat.com> In-Reply-To: <20241011182800.1750589-1-mzamazal@redhat.com> References: <20241011182800.1750589-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com 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" Software ISP is currently fully automatic and doesn't allow image modifications by explicitly set control values. The user has no means to make the image looking better. This patch introduces support for contrast control, which can improve e.g. a flat looking image. Based on the provided contrast value with a range 0..infinity and 1.0 being the normal value, it applies a simple S-curve modification to the image. The contrast algorithm just handles the provided values, while the S-curve is applied in the gamma algorithm on the computed gamma curve whenever the contrast value changes. Since the algorithm is applied only on the lookup table already present, its overhead is negligible. This is a preparation patch without actually providing the control itself, which is done in the following patch. Signed-off-by: Milan Zamazal --- src/ipa/simple/algorithms/lut.cpp | 38 +++++++++++++++++++++++++++---- src/ipa/simple/algorithms/lut.h | 5 ++++ src/ipa/simple/ipa_context.h | 8 +++++++ 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp index 9744e773a..ffded0594 100644 --- a/src/ipa/simple/algorithms/lut.cpp +++ b/src/ipa/simple/algorithms/lut.cpp @@ -9,14 +9,19 @@ #include #include +#include #include #include #include "simple/ipa_context.h" +#include "control_ids.h" + namespace libcamera { +LOG_DEFINE_CATEGORY(IPASoftLut) + namespace ipa::soft::algorithms { int Lut::configure(IPAContext &context, @@ -24,24 +29,46 @@ int Lut::configure(IPAContext &context, { /* Gamma value is fixed */ context.configuration.gamma = 0.5; + context.activeState.knobs.contrast = std::optional(); updateGammaTable(context); return 0; } +void Lut::queueRequest(typename Module::Context &context, + [[maybe_unused]] const uint32_t frame, + [[maybe_unused]] typename Module::FrameContext &frameContext, + const ControlList &controls) +{ + const auto &contrast = controls.get(controls::Contrast); + if (contrast.has_value()) { + context.activeState.knobs.contrast = contrast; + LOG(IPASoftLut, Debug) << "Setting contrast to " << contrast.value(); + } +} + void Lut::updateGammaTable(IPAContext &context) { auto &gammaTable = context.activeState.gamma.gammaTable; - auto blackLevel = context.activeState.blc.level; + const auto blackLevel = context.activeState.blc.level; const unsigned int blackIndex = blackLevel * gammaTable.size() / 256; + const auto contrast = context.activeState.knobs.contrast.value_or(1.0); std::fill(gammaTable.begin(), gammaTable.begin() + blackIndex, 0); const float divisor = gammaTable.size() - blackIndex - 1.0; - for (unsigned int i = blackIndex; i < gammaTable.size(); i++) - gammaTable[i] = UINT8_MAX * std::pow((i - blackIndex) / divisor, - context.configuration.gamma); + for (unsigned int i = blackIndex; i < gammaTable.size(); i++) { + double normalized = (i - blackIndex) / divisor; + /* Apply simple S-curve */ + if (normalized < 0.5) + normalized = 0.5 * std::pow(normalized / 0.5, contrast); + else + normalized = 1.0 - 0.5 * std::pow((1.0 - normalized) / 0.5, contrast); + gammaTable[i] = UINT8_MAX * + std::pow(normalized, context.configuration.gamma); + } context.activeState.gamma.blackLevel = blackLevel; + context.activeState.gamma.contrast = contrast; } void Lut::prepare(IPAContext &context, @@ -55,7 +82,8 @@ void Lut::prepare(IPAContext &context, * observed, it's not permanently prone to minor fluctuations or * rounding errors. */ - if (context.activeState.gamma.blackLevel != context.activeState.blc.level) + if (context.activeState.gamma.blackLevel != context.activeState.blc.level || + context.activeState.gamma.contrast != context.activeState.knobs.contrast) updateGammaTable(context); auto &gains = context.activeState.gains; diff --git a/src/ipa/simple/algorithms/lut.h b/src/ipa/simple/algorithms/lut.h index b635987d0..ef2df147c 100644 --- a/src/ipa/simple/algorithms/lut.h +++ b/src/ipa/simple/algorithms/lut.h @@ -20,6 +20,11 @@ public: ~Lut() = default; int configure(IPAContext &context, const IPAConfigInfo &configInfo) override; + void queueRequest(typename Module::Context &context, + const uint32_t frame, + typename Module::FrameContext &frameContext, + const ControlList &controls) + override; void prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index 491448ece..3a8d2d59c 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -8,8 +8,11 @@ #pragma once #include +#include #include +#include + #include namespace libcamera { @@ -44,7 +47,12 @@ struct IPAActiveState { struct { std::array gammaTable; uint8_t blackLevel; + double contrast; } gamma; + struct { + /* 0..inf range, 1.0 = normal */ + std::optional contrast; + } knobs; }; struct IPAFrameContext : public FrameContext {