{"id":22696,"url":"https://patchwork.libcamera.org/api/patches/22696/?format=json","web_url":"https://patchwork.libcamera.org/patch/22696/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20250130173254.112895-7-mzamazal@redhat.com>","date":"2025-01-30T17:32:53","name":"[v4,6/6] ipa: simple: Report exposure in metadata","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"18f780a424d951789127c72b03e37e7d52115e3c","submitter":{"id":177,"url":"https://patchwork.libcamera.org/api/people/177/?format=json","name":"Milan Zamazal","email":"mzamazal@redhat.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/22696/mbox/","series":[{"id":4979,"url":"https://patchwork.libcamera.org/api/series/4979/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=4979","date":"2025-01-30T17:32:47","name":"ipa: simple: Introduce metadata reporting","version":4,"mbox":"https://patchwork.libcamera.org/series/4979/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/22696/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/22696/checks/","tags":{},"headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id CA463C324C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 30 Jan 2025 17:33:23 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 824B368576;\n\tThu, 30 Jan 2025 18:33:23 +0100 (CET)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.129.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id DD4EF68573\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 30 Jan 2025 18:33:20 +0100 (CET)","from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com\n\t(ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63])\n\tby relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n\tcipher=TLS_AES_256_GCM_SHA384) id us-mta-393-Df8Hga56OOGHewkhB3QSvQ-1;\n\tThu, 30 Jan 2025 12:33:16 -0500","from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com\n\t(mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com\n\t[10.30.177.15])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\tkey-exchange X25519 server-signature RSA-PSS (2048 bits)\n\tserver-digest SHA256) (No client certificate requested)\n\tby mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTPS id 49B11195608B; Thu, 30 Jan 2025 17:33:15 +0000 (UTC)","from mzamazal-thinkpadp1gen3.tpbc.com (unknown [10.45.224.122])\n\tby mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTP id 7EA651956094; Thu, 30 Jan 2025 17:33:13 +0000 (UTC)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=redhat.com header.i=@redhat.com\n\theader.b=\"QeBZHDoX\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1738258400;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tcontent-transfer-encoding:content-transfer-encoding:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=CgmeRhJchBQ5+4nWoUKvpVKQA9dA1++jOuG4yjWjJz4=;\n\tb=QeBZHDoX8iy6oP1q8xUCcXTSu/4YX3TomPm/1K7ch5YCBhXI18Bv3bvpIky/zI8APhzcUb\n\t2ahJD9tEM4kwmZ6wANbBYwSU5OjyalASFoGMPaR/yDd+kdSw5cBrVmoD6UCSRe9aivlmcV\n\t87o782gh8+Q6t8SFwsh/0HY+3IAqVJU=","X-MC-Unique":"Df8Hga56OOGHewkhB3QSvQ-1","X-Mimecast-MFC-AGG-ID":"Df8Hga56OOGHewkhB3QSvQ","From":"Milan Zamazal <mzamazal@redhat.com>","To":"libcamera-devel@lists.libcamera.org","Cc":"Milan Zamazal <mzamazal@redhat.com>, Laurent Pinchart\n\t<laurent.pinchart@ideasonboard.com>, Kieran Bingham\n\t<kieran.bingham@ideasonboard.com>, =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?=\n\t<pobrn@protonmail.com>","Subject":"[PATCH v4 6/6] ipa: simple: Report exposure in metadata","Date":"Thu, 30 Jan 2025 18:32:53 +0100","Message-ID":"<20250130173254.112895-7-mzamazal@redhat.com>","In-Reply-To":"<20250130173254.112895-1-mzamazal@redhat.com>","References":"<20250130173254.112895-1-mzamazal@redhat.com>","MIME-Version":"1.0","X-Scanned-By":"MIMEDefang 3.0 on 10.30.177.15","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"EAqC_h0myaDwxVyYQ0_RDgPpT4FF68-0lrNhO9wuMck_1738258395","X-Mimecast-Originator":"redhat.com","Content-Transfer-Encoding":"8bit","content-type":"text/plain; charset=\"US-ASCII\"; x-default=true","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"Report exposure and gain in metadata.\n\nThis is more complicated than it could be expected because the exposure\nvalue should be in microseconds but it's handled using V4L2_CID_EXPOSURE\ncontrol, which doesn't specify the unit, see\nhttps://www.kernel.org/doc/html/latest/userspace-api/media/v4l/control.html.\nSo the unit conversion is done in the way rkisp1 IPA uses.\n\nThis requires getting and passing IPACameraSensorInfo around.  To avoid\nnaming confusion and to improve consistency with rkisp1 IPA,\nsensorCtrlInfoMap parameter is renamed to sensorControls.\n\nSigned-off-by: Milan Zamazal <mzamazal@redhat.com>\n---\n include/libcamera/ipa/soft.mojom            |  3 ++-\n src/ipa/simple/algorithms/agc.cpp           | 11 +++++++++--\n src/ipa/simple/ipa_context.h                |  6 +++++-\n src/ipa/simple/soft_simple.cpp              | 17 +++++++++++++----\n src/libcamera/software_isp/software_isp.cpp | 18 +++++++++++++-----\n 5 files changed, 42 insertions(+), 13 deletions(-)","diff":"diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom\nindex 934a6fd1..16870a4d 100644\n--- a/include/libcamera/ipa/soft.mojom\n+++ b/include/libcamera/ipa/soft.mojom\n@@ -16,7 +16,8 @@ interface IPASoftInterface {\n \tinit(libcamera.IPASettings settings,\n \t     libcamera.SharedFD fdStats,\n \t     libcamera.SharedFD fdParams,\n-\t     libcamera.ControlInfoMap sensorCtrlInfoMap)\n+\t     libcamera.IPACameraSensorInfo sensorInfo,\n+\t     libcamera.ControlInfoMap sensorControls)\n \t\t=> (int32 ret, libcamera.ControlInfoMap ipaControls);\n \tstart() => (int32 ret);\n \tstop();\ndiff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp\nindex 72aade14..c46bb0eb 100644\n--- a/src/ipa/simple/algorithms/agc.cpp\n+++ b/src/ipa/simple/algorithms/agc.cpp\n@@ -11,6 +11,8 @@\n \n #include <libcamera/base/log.h>\n \n+#include \"control_ids.h\"\n+\n namespace libcamera {\n \n LOG_DEFINE_CATEGORY(IPASoftExposure)\n@@ -97,10 +99,15 @@ void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, dou\n \n void Agc::process(IPAContext &context,\n \t\t  [[maybe_unused]] const uint32_t frame,\n-\t\t  [[maybe_unused]] IPAFrameContext &frameContext,\n+\t\t  IPAFrameContext &frameContext,\n \t\t  const SwIspStats *stats,\n-\t\t  [[maybe_unused]] ControlList &metadata)\n+\t\t  ControlList &metadata)\n {\n+\tutils::Duration exposureTime =\n+\t\tcontext.configuration.agc.lineDuration * frameContext.sensor.exposure;\n+\tmetadata.set(controls::ExposureTime, exposureTime.get<std::micro>());\n+\tmetadata.set(controls::AnalogueGain, frameContext.sensor.gain);\n+\n \t/*\n \t * Calculate Mean Sample Value (MSV) according to formula from:\n \t * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf\ndiff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h\nindex 07fd641a..22b80281 100644\n--- a/src/ipa/simple/ipa_context.h\n+++ b/src/ipa/simple/ipa_context.h\n@@ -1,6 +1,6 @@\n /* SPDX-License-Identifier: LGPL-2.1-or-later */\n /*\n- * Copyright (C) 2024 Red Hat, Inc.\n+ * Copyright (C) 2024-2025 Red Hat, Inc.\n  *\n  * Simple pipeline IPA Context\n  */\n@@ -15,6 +15,8 @@\n \n #include <libipa/fc_queue.h>\n \n+#include \"core_ipa_interface.h\"\n+\n namespace libcamera {\n \n namespace ipa::soft {\n@@ -24,6 +26,7 @@ struct IPASessionConfiguration {\n \tstruct {\n \t\tint32_t exposureMin, exposureMax;\n \t\tdouble againMin, againMax, againMinStep;\n+\t\tutils::Duration lineDuration;\n \t} agc;\n \tstruct {\n \t\tstd::optional<uint8_t> level;\n@@ -71,6 +74,7 @@ struct IPAContext {\n \t{\n \t}\n \n+\tIPACameraSensorInfo sensorInfo;\n \tIPASessionConfiguration configuration;\n \tIPAActiveState activeState;\n \tFCQueue<IPAFrameContext> frameContexts;\ndiff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp\nindex 944c644e..c6e82e12 100644\n--- a/src/ipa/simple/soft_simple.cpp\n+++ b/src/ipa/simple/soft_simple.cpp\n@@ -5,6 +5,7 @@\n  * Simple Software Image Processing Algorithm module\n  */\n \n+#include <chrono>\n #include <stdint.h>\n #include <sys/mman.h>\n \n@@ -32,6 +33,8 @@\n namespace libcamera {\n LOG_DEFINE_CATEGORY(IPASoft)\n \n+using namespace std::literals::chrono_literals;\n+\n namespace ipa::soft {\n \n /* Maximum number of frame contexts to be held */\n@@ -50,7 +53,8 @@ public:\n \tint init(const IPASettings &settings,\n \t\t const SharedFD &fdStats,\n \t\t const SharedFD &fdParams,\n-\t\t const ControlInfoMap &sensorInfoMap,\n+\t\t const IPACameraSensorInfo &sensorInfo,\n+\t\t const ControlInfoMap &sensorControls,\n \t\t ControlInfoMap *ipaControls) override;\n \tint configure(const IPAConfigInfo &configInfo) override;\n \n@@ -88,7 +92,8 @@ IPASoftSimple::~IPASoftSimple()\n int IPASoftSimple::init(const IPASettings &settings,\n \t\t\tconst SharedFD &fdStats,\n \t\t\tconst SharedFD &fdParams,\n-\t\t\tconst ControlInfoMap &sensorInfoMap,\n+\t\t\tconst IPACameraSensorInfo &sensorInfo,\n+\t\t\tconst ControlInfoMap &sensorControls,\n \t\t\tControlInfoMap *ipaControls)\n {\n \tcamHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);\n@@ -98,6 +103,8 @@ int IPASoftSimple::init(const IPASettings &settings,\n \t\t\t<< settings.sensorModel;\n \t}\n \n+\tcontext_.sensorInfo = sensorInfo;\n+\n \t/* Load the tuning data file */\n \tFile file(settings.configurationFile);\n \tif (!file.open(File::OpenModeFlag::ReadOnly)) {\n@@ -169,12 +176,12 @@ int IPASoftSimple::init(const IPASettings &settings,\n \t * Don't save the min and max control values yet, as e.g. the limits\n \t * for V4L2_CID_EXPOSURE depend on the configured sensor resolution.\n \t */\n-\tif (sensorInfoMap.find(V4L2_CID_EXPOSURE) == sensorInfoMap.end()) {\n+\tif (sensorControls.find(V4L2_CID_EXPOSURE) == sensorControls.end()) {\n \t\tLOG(IPASoft, Error) << \"Don't have exposure control\";\n \t\treturn -EINVAL;\n \t}\n \n-\tif (sensorInfoMap.find(V4L2_CID_ANALOGUE_GAIN) == sensorInfoMap.end()) {\n+\tif (sensorControls.find(V4L2_CID_ANALOGUE_GAIN) == sensorControls.end()) {\n \t\tLOG(IPASoft, Error) << \"Don't have gain control\";\n \t\treturn -EINVAL;\n \t}\n@@ -194,6 +201,8 @@ int IPASoftSimple::configure(const IPAConfigInfo &configInfo)\n \tcontext_.activeState = {};\n \tcontext_.frameContexts.clear();\n \n+\tcontext_.configuration.agc.lineDuration =\n+\t\tcontext_.sensorInfo.minLineLength * 1.0s / context_.sensorInfo.pixelRate;\n \tcontext_.configuration.agc.exposureMin = exposureInfo.min().get<int32_t>();\n \tcontext_.configuration.agc.exposureMax = exposureInfo.max().get<int32_t>();\n \tif (!context_.configuration.agc.exposureMin) {\ndiff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp\nindex 92f87acc..92fd97e3 100644\n--- a/src/libcamera/software_isp/software_isp.cpp\n+++ b/src/libcamera/software_isp/software_isp.cpp\n@@ -129,11 +129,19 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,\n \tstd::string ipaTuningFile =\n \t\tipa_->configurationFile(sensor->model() + \".yaml\", \"uncalibrated.yaml\");\n \n-\tint ret = ipa_->init(IPASettings{ ipaTuningFile, sensor->model() },\n-\t\t\t     debayer_->getStatsFD(),\n-\t\t\t     sharedParams_.fd(),\n-\t\t\t     sensor->controls(),\n-\t\t\t     ipaControls);\n+\tIPACameraSensorInfo sensorInfo{};\n+\tint ret = sensor->sensorInfo(&sensorInfo);\n+\tif (ret) {\n+\t\tLOG(SoftwareIsp, Error) << \"Camera sensor information not available\";\n+\t\treturn;\n+\t}\n+\n+\tret = ipa_->init(IPASettings{ ipaTuningFile, sensor->model() },\n+\t\t\t debayer_->getStatsFD(),\n+\t\t\t sharedParams_.fd(),\n+\t\t\t sensorInfo,\n+\t\t\t sensor->controls(),\n+\t\t\t ipaControls);\n \tif (ret) {\n \t\tLOG(SoftwareIsp, Error) << \"IPA init failed\";\n \t\tdebayer_.reset();\n","prefixes":["v4","6/6"]}