From patchwork Wed May 6 23:07:13 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: devve X-Patchwork-Id: 26658 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 1F094C32FC for ; Wed, 6 May 2026 23:07:29 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id EE23063038; Thu, 7 May 2026 01:07:28 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="CqCVXTab"; dkim-atps=neutral Received: from mail-qk1-x72d.google.com (mail-qk1-x72d.google.com [IPv6:2607:f8b0:4864:20::72d]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2020063024 for ; Thu, 7 May 2026 01:07:28 +0200 (CEST) Received: by mail-qk1-x72d.google.com with SMTP id af79cd13be357-8e8c0c2d2bcso34885285a.1 for ; Wed, 06 May 2026 16:07:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778108847; x=1778713647; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=LaTrKnaEFSG6R1dixgnlxJZEG75nGFBB6KtmVy+2B9E=; b=CqCVXTabuBU0AfBEH9rYt1dEb/pYBsatGCr+sk7WgxKDYss6Pe4hBMYaqgySo822lQ ZQ0E1WRz5v9MabIqgUYkDycrRdyWZXOJU5UPyRPPJRilceEa9ku5lQlvlWjfN1vfLvem i4Z2N0PmfGxCcfm2jJMgvnAlEsrIGFkyNiPS1Ov1tFhuyHnFHFsd1UHwHxH1RCxRAwBT PKgnDtApX/dp10Prd3NjuhHSL1GeTVY+i5FDnNGxx0ciT5BTM4Ip2WEYnDV6MOVm/eJj 5hxyMev/7HUjc3aDBpunfje7xalnXIw0RhAKPV/jUFUWrGFbhqSAI9DDlvTXDO95Js3B hTpQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778108847; x=1778713647; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=LaTrKnaEFSG6R1dixgnlxJZEG75nGFBB6KtmVy+2B9E=; b=GBl4VeNsmpaD8ZpEblhbVtuDxB1I1W1VyVT860czt91DaVpFT1+ueWK0k9yQ5NVrIB RtzYb6Xh8UfDwbhLtLBN+noD7ZQy7QgHpmrZFgdGbIIZa0VwjyQxa7mm9aKFZT3eIum6 itEiH1XM09wFJAIuJ3kJonJupAcys3PTXpe9xizOufbwbBf89bLn61mpNEQXF+mXsM2v +iJdbUsSpBm3vm0NHCMyWt8LLUY8fTc9WIrTxMLrLVK/WRKDCXCP63GUus+IuCMOB4Qa wZSGtK1nmoGROR6qJx6h65txiZEIqmVijQmt0L9yeSCnMRhAG5wnBTS8Zf8cEQK8WDlZ nYlA== X-Gm-Message-State: AOJu0YxbSb6O4DMt0YNakxBVGbaKl2UZ0sDdHfKDnGtqzmofUFyGORwB Ed1t8yKpaN9emWrQNMtH0eX3yNxsdPFGclfyZo9R15aRbCIDcTbMOH9XLM3jtw== X-Gm-Gg: AeBDievxyfJaJOO3o2N41/N9HTpZo82RsUNCMDC9JaXZjkSSlabpUQE0B1gQeNC7FAD xrbbRCNGDrIaeeaQzeuxn5QgbwoyOA4dIrSfdsCkchwTaQxiFW0AnbqQcqpx3KU/KzojCW7jZdO mocaX4DJU69GelW9YCFG+I4x4g53JhQiSnp8gZo+N2R8b822W3zeK5lzT7am33GTfvb5OQT4PZ+ nqRFbKlt3jLogrX3OgGk40sGa9h2icOqXhoqLOZg9QP1RGYBbZz+BHnLbzizFHS3wC2RebyemTC N0t7C9xrfzC8yxhKGi6Rz2aWi9BlzvGyIqE3zBxSTqyRgMF1N8tX7okylstkvg5MQE1mzN/S3PO O8diLsar5CM+n3aoScud4BXz1H98v0G1V28TIEdCmERc8JFzwup6MIu32PK/hh7SRwDUwZpvas2 PzjQAmIY5N8gR+K0bMJ/s1aF4h2s44+HxL2rf1wQeazEKYkKhulho+NYDne1YPhjrZPWcNq98oI ejzi6MoZTJo8Ez/1ThfW8JMtdDoZt+s X-Received: by 2002:a05:620a:4505:b0:8cf:d9ce:378b with SMTP id af79cd13be357-904d3fa449cmr785076785a.2.1778108846655; Wed, 06 May 2026 16:07:26 -0700 (PDT) Received: from dexps.speedport.ip (p200300eda74453cf3cf3f7929e513b94.dip0.t-ipconnect.de. [2003:ed:a744:53cf:3cf3:f792:9e51:3b94]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8fc2c25324esm2035266385a.23.2026.05.06.16.07.24 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 May 2026 16:07:26 -0700 (PDT) From: d3vv3 To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 01/10] ipa: libipa: camera_sensor_helper: Add OV01A10 Date: Thu, 7 May 2026 01:07:13 +0200 Message-ID: <20260506230722.1041596-2-devve.3@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260506230722.1041596-1-devve.3@gmail.com> References: <20260506230722.1041596-1-devve.3@gmail.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" From: Stuart J Mackintosh Add a CameraSensorHelper for the OmniVision OV01A10 image sensor, used in Dell XPS 13 and other laptops with the Intel IPU6 camera subsystem. The analogue gain register (0x3508) uses a Q6.8 fixed-point format, with the minimum value OV01A10_ANAL_GAIN_MIN = 0x100 representing unity gain. This gives the linear model: gain = code / 256 Hans de Goede confirmed linear behaviour by monitoring the 18% grey patch of a Macbeth chart under controlled lighting while stepping the gain control. The black level of 0x40 at 10 bits (4096 scaled to 16 bits) was confirmed by dark frame measurement with the lens covered. Without this helper, libcamera's AGC algorithm cannot convert between gain codes and real gain values, causing auto-exposure oscillation and the following warning: IPASoft: Failed to create camera sensor helper for ov01a10 Signed-off-by: Stuart J Mackintosh --- src/ipa/libipa/camera_sensor_helper.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ipa/libipa/camera_sensor_helper.cpp b/src/ipa/libipa/camera_sensor_helper.cpp index e3e3e535..72466867 100644 --- a/src/ipa/libipa/camera_sensor_helper.cpp +++ b/src/ipa/libipa/camera_sensor_helper.cpp @@ -653,6 +653,18 @@ public: }; REGISTER_CAMERA_SENSOR_HELPER("imx708", CameraSensorHelperImx708) +class CameraSensorHelperOv01a10 : public CameraSensorHelper +{ +public: + CameraSensorHelperOv01a10() + { + /* From dark frame measurement: 0x40 at 10bits. */ + blackLevel_ = 4096; + gain_ = AnalogueGainLinear{ 1, 0, 0, 256 }; + } +}; +REGISTER_CAMERA_SENSOR_HELPER("ov01a10", CameraSensorHelperOv01a10) + class CameraSensorHelperOv2685 : public CameraSensorHelper { public: From patchwork Wed May 6 23:07:14 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: devve X-Patchwork-Id: 26659 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 D5CCCC32FE for ; Wed, 6 May 2026 23:07:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F0DCC63040; Thu, 7 May 2026 01:07:30 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="mW8fESsE"; dkim-atps=neutral Received: from mail-qk1-x72f.google.com (mail-qk1-x72f.google.com [IPv6:2607:f8b0:4864:20::72f]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1ED076303C for ; Thu, 7 May 2026 01:07:29 +0200 (CEST) Received: by mail-qk1-x72f.google.com with SMTP id af79cd13be357-8ec9f099fc6so28039985a.0 for ; Wed, 06 May 2026 16:07:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778108848; x=1778713648; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=vpT2omGvrrBC/UWz9mRqAkO01quAcHHbvl8N34ZIhJU=; b=mW8fESsE77c75Pp5SGeBgfUJam1cwdkUVOpyts8V2/qpnLAIZkti5BerdlzDgYJC8p /pGnBtml4Cdr1fYcQVHX2giRRJaZ9sZLcm+xKgQkBwyJ56QMNuTRTropBbkZ4MEMq7hK 8kxbF1Wwl65esQKw/T2/+RcHbt5+CuXCZarOy3oHSD+ZgJwXoyXy+cZ8Y2zN5DYPKakn wiEEL6xW6ZSUI2mjU2qIG1zPd8cZgXe28eDbwfLWsmx4OffvrgNavipZq6pkfqgdq4WM XDq0QQU7Arq/I5BB6l+1nUH6IVYR5SU+DO2e/S52RnnNxkNCJjEBeDBOp2ngjzPh4o9X X6Gw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778108848; x=1778713648; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=vpT2omGvrrBC/UWz9mRqAkO01quAcHHbvl8N34ZIhJU=; b=mmCN4s/Zv/jiXNg7Dh6bmulDVN1lW8BEnxHZ4l8EOrAh/A9bY3P41j5snKQb/EiMRE Ox60yPNLtZ0guyFVFbdsj6kX4OWXFtWIT8wlAgCXoTdiO6TqpuNf0K8cB0Q+5NEIuvaT 58In7D4R6X/JRUGy4wznSy+prcXw5rjQEo71Cie7K34JI3ktvBXu3iq4z2FpBcQifiUZ Zj+rP13+Npuzp9FNiBlmaSs/KEqQSYv3lPEihoueOihzI4LbRRHBqEzz3ZW+SD0CVcUy e1eO+tBvD1Hib4WzBWTKY7EQ+L2a4L+GG2xNHQ+4fI0u+1mq5UlCwhxy9v+rzjUVaFtD etHQ== X-Gm-Message-State: AOJu0YwYZXJjr7+rcyoplNG+3zbZshpYM1MNwnjKZHkCDNjGkTqrqJ0J iWl2oECIjWjYIwBiz2LCviJ5dk/K7c98dsmNC5j0SOXVxqmVZDieVL/c4xVsGA== X-Gm-Gg: AeBDietHxba0+bJaP1zRS9rgLd/eJqijknt3V8iUvcFRv03Mt4rGsOGyToZsNamN2v0 F/h6aNt9SRPGYiQY9El1afriHiGwc73n7ShvvDd9akckklK3Rl63xNOTrEhrVF0uEF/C6t2jJw7 eYkignrfY1oEI40AkimFfwOO3tV7odT0Y7sz9PWLN0XjUnxSlT9ngwZDsG0DuGnL00omqHn1FhH lQHe/48ep+w82q08rIv76Sa/aITO6erwcP7P+1caPFX3sIEdLI9gom8Myqmc/uO8CIxEpDM+pmP kx50yZODT3UC5F/ZJYd6N4BLCvmecGoEdYnQWIcDLxvZOyBFR5/u/oDDkGpixvt0N5Nw11TRJD7 o9b5ZgZsBWFAcbtJzosvVARCwOWDzym7gVzaJJf9ijOiXBDZKQPXQLgAFc29UDadUrbAwm+FHEJ b/6xKcQlvqQXuklwmhBA/hMypPxpLjohp8T8HuUFfAPULia2PNg0QL3IgPrQ5mPKKA2ex4ySKj3 5R1nB2aPCxbCyUjaooI+CeSKkRsGEog X-Received: by 2002:a05:620a:4591:b0:8eb:d38d:2013 with SMTP id af79cd13be357-904d496d4b6mr849920385a.17.1778108847994; Wed, 06 May 2026 16:07:27 -0700 (PDT) Received: from dexps.speedport.ip (p200300eda74453cf3cf3f7929e513b94.dip0.t-ipconnect.de. [2003:ed:a744:53cf:3cf3:f792:9e51:3b94]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8fc2c25324esm2035266385a.23.2026.05.06.16.07.27 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 May 2026 16:07:27 -0700 (PDT) From: d3vv3 To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 02/10] ipa: simple: awb: Add temporal smoothing and per-channel gain limits Date: Thu, 7 May 2026 01:07:14 +0200 Message-ID: <20260506230722.1041596-3-devve.3@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260506230722.1041596-1-devve.3@gmail.com> References: <20260506230722.1041596-1-devve.3@gmail.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" Add configurable YAML parameters maxGainR, maxGainB, and speed to AWB. Replace the single hardcoded max gain (4.0x) with per-channel limits, and apply exponential moving average smoothing to reduce colour temperature oscillation between frames. Signed-off-by: d3vv3 --- src/ipa/simple/algorithms/awb.cpp | 34 ++++++++++++++++++++++++++----- src/ipa/simple/algorithms/awb.h | 6 ++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp index f5c88ea6..937aabc8 100644 --- a/src/ipa/simple/algorithms/awb.cpp +++ b/src/ipa/simple/algorithms/awb.cpp @@ -14,6 +14,8 @@ #include +#include "libcamera/internal/yaml_parser.h" + #include "libipa/colours.h" #include "simple/ipa_context.h" @@ -23,6 +25,21 @@ LOG_DEFINE_CATEGORY(IPASoftAwb) namespace ipa::soft::algorithms { +int Awb::init([[maybe_unused]] IPAContext &context, + const ValueNode &tuningData) +{ + maxGainR_ = tuningData["maxGainR"].get().value_or(4.0f); + maxGainB_ = tuningData["maxGainB"].get().value_or(4.0f); + speed_ = tuningData["speed"].get().value_or(1.0f); + + LOG(IPASoftAwb, Debug) + << "AWB: maxGainR " << maxGainR_ + << ", maxGainB " << maxGainB_ + << ", speed " << speed_; + + return 0; +} + int Awb::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { @@ -84,14 +101,21 @@ void Awb::process(IPAContext &context, const RGB sum = stats->sum_.max(offset + minValid) - offset; /* - * Calculate red and blue gains for AWB. - * Clamp max gain at 4.0, this also avoids 0 division. + * Calculate red and blue gains for AWB. Clamp max gain to avoid + * division by zero and extreme color casts. */ auto &gains = context.activeState.awb.gains; + float rawRGain = sum.r() <= sum.g() / maxGainR_ ? maxGainR_ : + static_cast(sum.g()) / sum.r(); + float rawBGain = sum.b() <= sum.g() / maxGainB_ ? maxGainB_ : + static_cast(sum.g()) / sum.b(); + + /* Apply temporal smoothing to avoid rapid white balance changes. */ + float alpha = std::clamp(speed_, 0.01f, 1.0f); gains = { { - sum.r() <= sum.g() / 4 ? 4.0f : static_cast(sum.g()) / sum.r(), - 1.0, - sum.b() <= sum.g() / 4 ? 4.0f : static_cast(sum.g()) / sum.b(), + gains.r() * (1.0f - alpha) + rawRGain * alpha, + 1.0f, + gains.b() * (1.0f - alpha) + rawBGain * alpha, } }; RGB rgbGains{ { 1 / gains.r(), 1 / gains.g(), 1 / gains.b() } }; diff --git a/src/ipa/simple/algorithms/awb.h b/src/ipa/simple/algorithms/awb.h index ad993f39..0aedc1d1 100644 --- a/src/ipa/simple/algorithms/awb.h +++ b/src/ipa/simple/algorithms/awb.h @@ -19,6 +19,7 @@ public: Awb() = default; ~Awb() = default; + int init(IPAContext &context, const ValueNode &tuningData) override; int configure(IPAContext &context, const IPAConfigInfo &configInfo) override; void prepare(IPAContext &context, const uint32_t frame, @@ -29,6 +30,11 @@ public: IPAFrameContext &frameContext, const SwIspStats *stats, ControlList &metadata) override; + +private: + float maxGainR_; + float maxGainB_; + float speed_; }; } /* namespace ipa::soft::algorithms */ From patchwork Wed May 6 23:07:15 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: devve X-Patchwork-Id: 26660 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 4CDCCC3300 for ; Wed, 6 May 2026 23:07:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8A0AC6302D; Thu, 7 May 2026 01:07:33 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="FzsouHG/"; dkim-atps=neutral Received: from mail-qk1-x72b.google.com (mail-qk1-x72b.google.com [IPv6:2607:f8b0:4864:20::72b]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4984963024 for ; Thu, 7 May 2026 01:07:30 +0200 (CEST) Received: by mail-qk1-x72b.google.com with SMTP id af79cd13be357-8cbc593a67aso20497185a.2 for ; Wed, 06 May 2026 16:07:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778108849; x=1778713649; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=TdmC3s5OWUslsg0CaqrHDC/ToejoU6twUaRb3QtsRVI=; b=FzsouHG/eg8YUKdALa8MY/IeHUIZ9FQESUDeGOiwl4ii4WSwAJGjnSmTsd9Zc9RDmr YkBVlfFHi26yjwx9S1UOsiyHMnAz+Bwo158KggbNU/qgTbjXmRyUpzacdGdn3G78xoJo b4Ce09sjwVOZ0LyLkULaW7CL8GdJR+BydQsS0XQdvkwsMqQBjiQhOdYg0COU0Yq0PVL0 +gpmXfbdah46XzcB+RlDrEdwM/qQ1tBQxdBR3dfdpm9S8IyzJicil/Yd4+Y4voE7ugpr mWFwjCn7FjThiYDW6ksbKCBclA48L2BtjMgqFXXEDhqTc23Zo6mHVymqhxVlkMCdoxCw PPtA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778108849; x=1778713649; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=TdmC3s5OWUslsg0CaqrHDC/ToejoU6twUaRb3QtsRVI=; b=irfBMbjM1UYA3Nr+wnIRSjENACIJLD0wNQzTBM+eme243kR/12rIOA/0Zb9NKgYrTw zCjmpZ/hqJstoBHZJy1NK+lyI32cakAxu5uhDPqSLJcOsGjErv6ofgafYuqC9ganKWTY FX6DYgE806Xj08HsmvT/MDY+CwTlBt9DF5T2cogveKSZK+OEf0YMyRzpwljVQ0tLVVXH aO24Eur1ZZ/1fVXmcY4UEwt0xI1gX/C3guD6UyHLpQDfmBBGAPYFevT6KRnqiSwOrQxT lqoKuvysiMpEUICIs6rH3WpOlJa2+rR9oELHXUe55PwGd4GJvmHbd0Zhz+imYP7KOYGu YnyA== X-Gm-Message-State: AOJu0Yx+her1lhivfetdSZKA7BS4CDogimiM2yD80TDbdG4H8mRx/8Gg CratFoydcc+Vr8vsEQrPSfqjFjOQiAqAkc38kvDxDtbxh83c7RqiYVdt5Gg+XA== X-Gm-Gg: AeBDieugnjyqV9VRQCsTcueNHvDBHVAicJuFN2OCcMRBeYGZNj4hACTh/aKIWJAGXDh K68RcfL6hXJEhTrPfIiW5O2PrafhzcUpBopFDNAj5ACJ2fhAZrwxlQA8soNQS5V/80BKQjC7ZRk tBPaN++z74YE/XHX9AMyvOzvExB6VTcakP6VHcRI2NbdkrZ7FQkKoi2j8j9hLOSc1qUgR3d5T3W dlr09yAvfPDIrZLDerH/85h9DS9ajmcvR7YKR9AzynRj3Bh7wGmVjmV2+EK60QRl7XPDPjqHqGA P1nNGZyw7rfYpP0jG+LhE0Cy5on4mjSqwzY6I3fV2O80pPog+Mbogt7+X3+5FCdzpqYAF4lXyut M5LaC8U7mZZr4DhIU8ghwMKOy5aWQIqGMf+O4zKmrd9Cv12T5rNGffTsQCGohILdSuX6mDDec/r dDi5VCVike7a8OiSlOG/ZrwYAihFGhS9o3Qu7SfwiaIn3PKrTyYW/yE/5zo7/IqzJKJSLCDPhZr q5yesxgx6U0uPKnokVHove/J0XOUX0m X-Received: by 2002:a05:620a:2890:b0:8f4:3895:25d7 with SMTP id af79cd13be357-904d486edb2mr858797885a.23.1778108849148; Wed, 06 May 2026 16:07:29 -0700 (PDT) Received: from dexps.speedport.ip (p200300eda74453cf3cf3f7929e513b94.dip0.t-ipconnect.de. [2003:ed:a744:53cf:3cf3:f792:9e51:3b94]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8fc2c25324esm2035266385a.23.2026.05.06.16.07.28 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 May 2026 16:07:28 -0700 (PDT) From: d3vv3 To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 03/10] ipa: simple: adjust: Support gamma, contrast, saturation defaults from YAML Date: Thu, 7 May 2026 01:07:15 +0200 Message-ID: <20260506230722.1041596-4-devve.3@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260506230722.1041596-1-devve.3@gmail.com> References: <20260506230722.1041596-1-devve.3@gmail.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" Read default values for gamma, contrast, and saturation from the tuning file so sensors can specify different image processing defaults without code changes. Falls back to prior defaults (gamma 2.2, contrast 1.0, saturation 1.0) when not specified in YAML. Signed-off-by: d3vv3 --- src/ipa/simple/algorithms/adjust.cpp | 48 +++++++++++++--------------- src/ipa/simple/algorithms/adjust.h | 4 +++ src/ipa/simple/ipa_context.h | 8 ++--- 3 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index 8bf39c4c..a03a6f1f 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -14,34 +14,37 @@ #include #include "libcamera/internal/matrix.h" +#include "libcamera/internal/yaml_parser.h" namespace libcamera { namespace ipa::soft::algorithms { -constexpr float kDefaultContrast = 1.0f; -constexpr float kDefaultSaturation = 1.0f; - LOG_DEFINE_CATEGORY(IPASoftAdjust) -int Adjust::init(IPAContext &context, [[maybe_unused]] const ValueNode &tuningData) +int Adjust::init(IPAContext &context, const ValueNode &tuningData) { + defaultGamma_ = tuningData["gamma"].get().value_or(kDefaultGamma); + defaultContrast_ = tuningData["contrast"].get().value_or(1.0f); + defaultSaturation_ = tuningData["saturation"].get().value_or(1.0f); + context.ctrlMap[&controls::Gamma] = - ControlInfo(0.1f, 10.0f, kDefaultGamma); + ControlInfo(0.1f, 10.0f, defaultGamma_); context.ctrlMap[&controls::Contrast] = - ControlInfo(0.0f, 2.0f, kDefaultContrast); + ControlInfo(0.0f, 2.0f, defaultContrast_); if (context.ccmEnabled) context.ctrlMap[&controls::Saturation] = - ControlInfo(0.0f, 2.0f, kDefaultSaturation); + ControlInfo(0.0f, 2.0f, defaultSaturation_); + return 0; } int Adjust::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { - context.activeState.knobs.gamma = kDefaultGamma; - context.activeState.knobs.contrast = std::optional(); - context.activeState.knobs.saturation = std::optional(); + context.activeState.knobs.gamma = defaultGamma_; + context.activeState.knobs.contrast = defaultContrast_; + context.activeState.knobs.saturation = defaultSaturation_; return 0; } @@ -59,13 +62,13 @@ void Adjust::queueRequest(typename Module::Context &context, const auto &contrast = controls.get(controls::Contrast); if (contrast.has_value()) { - context.activeState.knobs.contrast = contrast; + context.activeState.knobs.contrast = contrast.value(); LOG(IPASoftAdjust, Debug) << "Setting contrast to " << contrast.value(); } const auto &saturation = controls.get(controls::Saturation); if (saturation.has_value()) { - context.activeState.knobs.saturation = saturation; + context.activeState.knobs.saturation = saturation.value(); LOG(IPASoftAdjust, Debug) << "Setting saturation to " << saturation.value(); } } @@ -100,15 +103,15 @@ void Adjust::prepare(IPAContext &context, frameContext.gamma = context.activeState.knobs.gamma; frameContext.contrast = context.activeState.knobs.contrast; - auto &saturation = context.activeState.knobs.saturation; - if (context.ccmEnabled && saturation) { - applySaturation(context.activeState.combinedMatrix, saturation.value()); + const float saturation = context.activeState.knobs.saturation; + if (context.ccmEnabled) { + applySaturation(context.activeState.combinedMatrix, saturation); frameContext.saturation = saturation; } params->gamma = 1.0 / context.activeState.knobs.gamma; - const float contrast = context.activeState.knobs.contrast.value_or(kDefaultContrast); - params->contrastExp = tan(std::clamp(contrast * M_PI_4, 0.0, M_PI_2 - 0.00001)); + params->contrastExp = tan(std::clamp(context.activeState.knobs.contrast * M_PI_4, + 0.0, M_PI_2 - 0.00001)); } void Adjust::process([[maybe_unused]] IPAContext &context, @@ -117,14 +120,9 @@ void Adjust::process([[maybe_unused]] IPAContext &context, [[maybe_unused]] const SwIspStats *stats, ControlList &metadata) { - const auto &gamma = frameContext.gamma; - metadata.set(controls::Gamma, gamma); - - const auto &contrast = frameContext.contrast; - metadata.set(controls::Contrast, contrast.value_or(kDefaultContrast)); - - const auto &saturation = frameContext.saturation; - metadata.set(controls::Saturation, saturation.value_or(kDefaultSaturation)); + metadata.set(controls::Gamma, frameContext.gamma); + metadata.set(controls::Contrast, frameContext.contrast); + metadata.set(controls::Saturation, frameContext.saturation); } REGISTER_IPA_ALGORITHM(Adjust, "Adjust") diff --git a/src/ipa/simple/algorithms/adjust.h b/src/ipa/simple/algorithms/adjust.h index 49c1f26c..a836b51b 100644 --- a/src/ipa/simple/algorithms/adjust.h +++ b/src/ipa/simple/algorithms/adjust.h @@ -43,6 +43,10 @@ public: private: void applySaturation(Matrix &ccm, float saturation); + + float defaultGamma_; + float defaultContrast_; + float defaultSaturation_; }; } /* namespace ipa::soft::algorithms */ diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index 34f7403a..cd9a8eda 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -58,8 +58,8 @@ struct IPAActiveState { struct { float gamma; /* 0..2 range, 1.0 = normal */ - std::optional contrast; - std::optional saturation; + float contrast; + float saturation; } knobs; }; @@ -77,8 +77,8 @@ struct IPAFrameContext : public FrameContext { } gains; float gamma; - std::optional contrast; - std::optional saturation; + float contrast; + float saturation; }; struct IPAContext { From patchwork Wed May 6 23:07:16 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: devve X-Patchwork-Id: 26661 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 EF672C3301 for ; Wed, 6 May 2026 23:07:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 2687363043; Thu, 7 May 2026 01:07:34 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="JjIddgXj"; dkim-atps=neutral Received: from mail-qk1-x736.google.com (mail-qk1-x736.google.com [IPv6:2607:f8b0:4864:20::736]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6EA6D63024 for ; Thu, 7 May 2026 01:07:31 +0200 (CEST) Received: by mail-qk1-x736.google.com with SMTP id af79cd13be357-8ee62a19730so26339585a.3 for ; Wed, 06 May 2026 16:07:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778108850; x=1778713650; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=JUKQ09Nx7NLeaiPTW9IaTACA3hv2E+jWsDiYD//GlaA=; b=JjIddgXjROogv6SFGXdaGtalR8qzL6Id1I67ZhT8Nv+yEdx6mKtEwBaSCzSJWcVMMm dvMYYp0gimNQkQILLsXaxmNvsQJ2JtmDtu8pF+QqjMpxmja88aZIycmanNh/CO2I309T Ece7id5N8XSjfNBwZe28VJL4f5wY3+kOmATVN2ymDoElYa8UeJWxmbSWGV/39WC0TehN uhwEF+KprGv0g0UvtGF3uRRCEkd0n22TOi4UhwCG0Cc4D1d1Lj0Raa0uXYVkg8RuOIzi mEwuHnzO7YmAjpPstUjNkHhz6QfGqB9wZAzPEhzHCK+CVDBlQ4Ie+9OeZCT1Ix9OZadE u4tw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778108850; x=1778713650; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=JUKQ09Nx7NLeaiPTW9IaTACA3hv2E+jWsDiYD//GlaA=; b=TBrA7F1MWfsZ+wu8lcChexE4oZOQt2QO27dx8Tzyt9Mk8n2PSFaPm+qC/8zMxI9/iO CkJiabQ9VYYh7OS+ni986RXBhL4de3IEy36UzH5sQyDohK+k0Imv5KrGK1CKdQkAaTeR 2y94xBfSEC5+ovb9GO+81NeVAuk/AskPrFYG2hu8+gU8/Ek2yNNjSQyeN6qwRGH3pdt9 /zJbx9bMWquqh6MLNFLWUF5yugVMR2zP9PmcAkrauqUPRViOiwGo7Th12B/xxFNPtAAc OVzL0snYaAKGwHncpbgvhwooPBscvr6sf7auYX6qLCzSiKkSRLgcoDbGg+zrUeaMhMoG r7Uw== X-Gm-Message-State: AOJu0YxCwc0jSbH5MT/8dFBGpWaPT4n5FKPS2X/eYG1E+HgSxX0ISlt/ Gw9M+pJzc//K5c1G1Y+umx5AUOFhUFGX6HjNJ3KWBqbm8nAYR3x9rkMdmwQaGA== X-Gm-Gg: AeBDieuGtDCZvZsUGTSlLk9hbqH78/rM4crE5tUD4jesTKk4+aVAgnNhBEpdojSLAlN pY+7eBPwSO6Quf83HX1FW4RjPNL4zSq4LcX4QJ7Yts95msX9DPyiNfdnlJTKSvgrBF1RySz4bjX wFB831u01iic0M2/157RjM947hNW3tJWA6lwCYmCMEXeFPuUkFm/wnbIaTgp5pQp4fKJE+RJXr6 xwmizNJfqUv4PViWFcO+VetVLkeI8z4um1qTFWc0IJ4wV6sYrd+NomXzrNM3bdvW3/aQMSpX2RK lwAsYB4Iaz4a2W1ymKyUt9tHiSKB/LzpxGRIJzMtSMRwcUfappyb53jW7ivPY4sDXxWiAy6HY/5 jkgXavNfPWnQw2HKE91lZMugdxylxmZvRxUqTUpiGABUAwqSIvz0eV8+1ohtHQKZXG2TNHJL47Y HXVWdGwE6FcxAcwVQ8mlo0uUzF/NrYYRZyufca/7Iyet4N55mUA8kowS7CFr3v/qIwGVXbrsChV Szw/aUkR1q/x0MPdmwKvmrBYUrgDlfsH6sQoVZJFY4NxzbXoy9TuQ== X-Received: by 2002:a05:620a:2955:b0:8ed:11b9:1eca with SMTP id af79cd13be357-904d4d530b3mr832423385a.23.1778108850326; Wed, 06 May 2026 16:07:30 -0700 (PDT) Received: from dexps.speedport.ip (p200300eda74453cf3cf3f7929e513b94.dip0.t-ipconnect.de. [2003:ed:a744:53cf:3cf3:f792:9e51:3b94]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8fc2c25324esm2035266385a.23.2026.05.06.16.07.29 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 May 2026 16:07:29 -0700 (PDT) From: d3vv3 To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 04/10] ipa: simple: data: Add OV01A10 tuning file Date: Thu, 7 May 2026 01:07:16 +0200 Message-ID: <20260506230722.1041596-5-devve.3@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260506230722.1041596-1-devve.3@gmail.com> References: <20260506230722.1041596-1-devve.3@gmail.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" Add tuning data for the OmniVision OV01A10 sensor, calibrated from the factory-supplied Intel IPU6 AIQB binary (ov01a_1BG101N3_MTL.aiqb) shipped with the Dell XPS 9320 (Meteor Lake). The AIQB binary was parsed with some custom scripts to extract: - 8 colour correction matrices spanning 2856K-7500K (Incandescent to D75), all with rows summing to 1.0 (luminance-preserving) - AWB neutral locus: 9 achromatic R/G, B/G points defining the sensor's white balance response across illuminants - Sensor properties: 1280x800, 10-bit, GBRG colour order, base ISO 43 - Noise model: read noise variance 7.5e-5, shot noise slope 0.187 The AWB gain limits (maxGainR: 2.5, maxGainB: 3.2) are derived from the maximum gains required to neutralise the warmest and coolest illuminants in the locus, with 10% headroom. Signed-off-by: d3vv3 --- src/ipa/simple/data/meson.build | 1 + src/ipa/simple/data/ov01a10.yaml | 55 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 src/ipa/simple/data/ov01a10.yaml diff --git a/src/ipa/simple/data/meson.build b/src/ipa/simple/data/meson.build index 92795ee4..e6110320 100644 --- a/src/ipa/simple/data/meson.build +++ b/src/ipa/simple/data/meson.build @@ -1,6 +1,7 @@ # SPDX-License-Identifier: CC0-1.0 conf_files = files([ + 'ov01a10.yaml', 'uncalibrated.yaml', ]) diff --git a/src/ipa/simple/data/ov01a10.yaml b/src/ipa/simple/data/ov01a10.yaml new file mode 100644 index 00000000..d1f613c5 --- /dev/null +++ b/src/ipa/simple/data/ov01a10.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: CC0-1.0 +%YAML 1.1 +--- +version: 1 +algorithms: + # Black level is not specified in the AIQB calibration binary; auto-detected + # from the histogram dark end at runtime. + - BlackLevel: + - Awb: + maxGainR: 2.5 + maxGainB: 3.2 + speed: 0.25 + - Ccm: + ccms: + - ct: 2856 + ccm: [ 1.1248, 0.2210, -0.3458, + -0.4616, 1.7736, -0.3120, + -0.4342, -0.9348, 2.3690 ] + - ct: 3000 + ccm: [ 1.5839, -0.4188, -0.1650, + -0.3670, 1.6565, -0.2895, + -0.1213, -1.0442, 2.1655 ] + - ct: 3450 + ccm: [ 1.6411, -0.5127, -0.1284, + -0.3680, 1.6337, -0.2657, + -0.1384, -1.0869, 2.2253 ] + - ct: 4000 + ccm: [ 1.5414, -0.4024, -0.1390, + -0.3304, 1.6352, -0.3048, + -0.1237, -0.6699, 1.7936 ] + - ct: 4150 + ccm: [ 1.7334, -0.6629, -0.0706, + -0.3121, 1.6267, -0.3146, + -0.0920, -0.9183, 2.0103 ] + - ct: 5000 + ccm: [ 1.5015, -0.3165, -0.1850, + -0.2277, 1.6190, -0.3913, + -0.0699, -0.7285, 1.7984 ] + - ct: 6500 + ccm: [ 1.8163, -0.7062, -0.1100, + -0.1640, 1.5736, -0.4096, + -0.0084, -0.8294, 1.8378 ] + - ct: 7500 + ccm: [ 1.8953, -0.7980, -0.0973, + -0.1539, 1.6001, -0.4462, + -0.0101, -0.7800, 1.7902 ] + - Adjust: + gamma: 2.2 + contrast: 1.0 + saturation: 1.0 + - Agc: + exposureTarget: 2.5 + hysteresis: 0.2 + stepDenominator: 20 +... From patchwork Wed May 6 23:07:17 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: devve X-Patchwork-Id: 26662 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 BEC95C3302 for ; Wed, 6 May 2026 23:07:36 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0DC83630A2; Thu, 7 May 2026 01:07:35 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="Bss7U651"; dkim-atps=neutral Received: from mail-qk1-x732.google.com (mail-qk1-x732.google.com [IPv6:2607:f8b0:4864:20::732]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 81AA663024 for ; Thu, 7 May 2026 01:07:32 +0200 (CEST) Received: by mail-qk1-x732.google.com with SMTP id af79cd13be357-8cb5c9ba82bso35301085a.2 for ; Wed, 06 May 2026 16:07:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778108851; x=1778713651; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=dZKZKTCGEv+ghBeM3aBja4SAmlWxPm+UsMadpqZ8y88=; b=Bss7U651ES8BR2TAsK9IFD3L63Hi+kNbOknapBlmcSRxojqI0nVpgjuGxYMUckclI/ k8+7zH2Y6nUiBORJQclnoey7zcYBs2ky0a9ygfhPuCIVXBsxORPya0q6JZqg3/4+M9mj wjAUm+PxtzsOGaJusAuDLylLxD5k4Tu9qKicuu5MYd+Mk7ofaOKCgc8pXgpQ7qc2nBhm L4KVrnTGEP7QDqq/aZJjfbc5xY1FFXeq0POxk6cR6Df6Dq01i7XiwVmi8rVZtQ5KakIy ACptZd4TnzpOgr5/h016f8k3ONUHBTGvCtWhIFfMNilXe7HK4V9oDH2slVIQuOosx/0+ 5DyA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778108851; x=1778713651; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=dZKZKTCGEv+ghBeM3aBja4SAmlWxPm+UsMadpqZ8y88=; b=HhxVHqzDpGJgqzwq5RsRvMZCx8Khcvfl0E3HjJNpLId4JD0qLwHQ7TNQe4KNZi5cy0 4R5h22LgQkrS6DFV731+zlFVzdbVH6or3DybakKA+R4eQ9U+ONbtq8JZn5KrdxqtGPBu hh2IKinDh6OhVT2GG7rgCgZaFauy+ns0uzw6eWMhg7OCQGpP6Z6qjezSgBnoCqJvPU48 GSVD7C2WTb4gz0VA4mZTkIFJcNAGB382TqEGOkHHdwbbmMwSnGha3zUCAP0VmKv6PFIC xjb4EfzXxtt/BaDF37lAR0SD/R/9XKnEu8rU3KIJ/s+B1E7dg+oJzKzFTbtF+uRb4CZb FBbw== X-Gm-Message-State: AOJu0YwyoOBy8gLRVYDR3vuzSGg1zFHzPftypWNlncunNpbNxWqFA048 iJxzjpYhjhKEFMVyq96cc41+kP3yGn1Av4WVNgB0iIUZz/f8txH8+42HTQdM/w== X-Gm-Gg: AeBDietTPOvH2i0b8MMGIqp2NDH+Uxpnzh3VMr8VOZ4cuP0t4b+pHrVrApvE05vC5Gb DbxXfCtWFi+biayHkMkXYaSKPbTQ9jhJQRaWJRVQApd3UdRc3tk25pbTXVc/YtsHXU0ophdQ77m CsIc1CEBYgCoABi2/quU7Mk0MXV4k2YB7MWglLDkKvT2ppA6JYVIpodFnEswcc4UdPyqSTNfBy2 /EYb3gUnvhNAMpGaebL6Ej0R9o+zDECzueD7CXfVzV16tZ+JlnD/TJO3NKxcFOpTzu++bIcoFN+ UmiPhc50MjnVrOXxwjaoMGxxbPCFjhpOQqdT9LvJtRIGGLn2XVPWUXEtRkdNlv+6FNYzyCniIgp yu1t8BW2loZziF7E0hRZrsXqz9qee01syM9SYvxBuqHifhF53KDUSmAxwChpn3OsBJEdAJi0TUy Ug6Thx6cNH6fCPMy1AboBkY8+1c6LgTxcyS8EoXwm8c2RTugwYCyFOu4HBqCroMgmRSIlJIznMR rSR/h309T9ovCW7O3jdudOU/M4fkFaJ X-Received: by 2002:a05:620a:4451:b0:8fb:e57e:9e0b with SMTP id af79cd13be357-904d69d8df5mr819540385a.46.1778108851410; Wed, 06 May 2026 16:07:31 -0700 (PDT) Received: from dexps.speedport.ip (p200300eda74453cf3cf3f7929e513b94.dip0.t-ipconnect.de. [2003:ed:a744:53cf:3cf3:f792:9e51:3b94]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8fc2c25324esm2035266385a.23.2026.05.06.16.07.30 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 May 2026 16:07:31 -0700 (PDT) From: d3vv3 To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 05/10] ipa: simple: agc: Replace bang-bang controller with proportional Date: Thu, 7 May 2026 01:07:17 +0200 Message-ID: <20260506230722.1041596-6-devve.3@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260506230722.1041596-1-devve.3@gmail.com> References: <20260506230722.1041596-1-devve.3@gmail.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" From: Javier Tia The AGC's updateExposure() uses a fixed ~10% step per frame regardless of how far the current exposure is from optimal. With a hysteresis dead band of only +/-4%, the controller overshoots when the correct value falls within one step, causing visible brightness oscillation (flicker). Replace the fixed-step bang-bang controller with a proportional one where the correction factor scales linearly with the MSV error: factor = 1.0 + error * 0.04 At maximum error (~2.5), this gives the same ~10% step as before. Near the target, steps shrink to <1%, eliminating overshoot. The existing hysteresis (kExposureSatisfactory) still prevents hunting on noise. Tested on OV2740 behind Intel IPU6 ISYS (ThinkPad X1 Carbon Gen 10) where the old controller produced continuous brightness flicker. The proportional controller converges in ~3 seconds from cold start with no visible oscillation. Signed-off-by: Javier Tia Reviewed-by: Milan Zamazal Tested-by: Barnabás Pőcze Signed-off-by: d3vv3 --- src/ipa/simple/algorithms/agc.cpp | 65 +++++++++++++++++++------------ 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp index 2f7e040c..ac977d5f 100644 --- a/src/ipa/simple/algorithms/agc.cpp +++ b/src/ipa/simple/algorithms/agc.cpp @@ -7,6 +7,8 @@ #include "agc.h" +#include +#include #include #include @@ -37,52 +39,66 @@ static constexpr float kExposureOptimal = kExposureBinsCount / 2.0; */ static constexpr float kExposureSatisfactory = 0.2; +/* + * Proportional gain for exposure/gain adjustment. Maps the MSV error to a + * multiplicative correction factor: + * + * factor = 1.0 + kExpProportionalGain * error + * + * With kExpProportionalGain = 0.04: + * - max error ~2.5 -> factor 1.10 (~10% step, same as before) + * - error 1.0 -> factor 1.04 (~4% step) + * - error 0.3 -> factor 1.012 (~1.2% step) + * + * This replaces the fixed 10% bang-bang step with a proportional correction + * that converges smoothly and avoids overshooting near the target. + */ +static constexpr float kExpProportionalGain = 0.04; + Agc::Agc() { } void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV) { - /* - * kExpDenominator of 10 gives ~10% increment/decrement; - * kExpDenominator of 5 - about ~20% - */ - static constexpr uint8_t kExpDenominator = 10; - static constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1; - static constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1; - int32_t &exposure = frameContext.sensor.exposure; double &again = frameContext.sensor.gain; - if (exposureMSV < kExposureOptimal - kExposureSatisfactory) { + double error = kExposureOptimal - exposureMSV; + + if (std::abs(error) <= kExposureSatisfactory) + return; + + /* + * Compute a proportional correction factor. The sign of the error + * determines the direction: positive error means too dark (increase), + * negative means too bright (decrease). + */ + float factor = 1.0f + static_cast(error) * kExpProportionalGain; + + if (factor > 1.0f) { + /* Scene too dark: increase exposure first, then gain. */ if (exposure < context.configuration.agc.exposureMax) { - int32_t next = exposure * kExpNumeratorUp / kExpDenominator; - if (next - exposure < 1) - exposure += 1; - else - exposure = next; + int32_t next = static_cast(exposure * factor); + exposure = std::max(next, exposure + 1); } else { - double next = again * kExpNumeratorUp / kExpDenominator; + double next = again * factor; if (next - again < context.configuration.agc.againMinStep) again += context.configuration.agc.againMinStep; else again = next; } - } - - if (exposureMSV > kExposureOptimal + kExposureSatisfactory) { + } else { + /* Scene too bright: decrease gain first, then exposure. */ if (again > context.configuration.agc.again10) { - double next = again * kExpNumeratorDown / kExpDenominator; + double next = again * factor; if (again - next < context.configuration.agc.againMinStep) again -= context.configuration.agc.againMinStep; else again = next; } else { - int32_t next = exposure * kExpNumeratorDown / kExpDenominator; - if (exposure - next < 1) - exposure -= 1; - else - exposure = next; + int32_t next = static_cast(exposure * factor); + exposure = std::min(next, exposure - 1); } } @@ -96,6 +112,7 @@ void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, dou LOG(IPASoftExposure, Debug) << "exposureMSV " << exposureMSV + << " error " << error << " factor " << factor << " exp " << exposure << " again " << again; } From patchwork Wed May 6 23:07:18 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: devve X-Patchwork-Id: 26663 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 16D69C3303 for ; Wed, 6 May 2026 23:07:38 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id CAF1A630BB; Thu, 7 May 2026 01:07:37 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="LCoBA+YH"; dkim-atps=neutral Received: from mail-qk1-x72f.google.com (mail-qk1-x72f.google.com [IPv6:2607:f8b0:4864:20::72f]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id AC7D16303C for ; Thu, 7 May 2026 01:07:33 +0200 (CEST) Received: by mail-qk1-x72f.google.com with SMTP id af79cd13be357-8d6d5e45c43so26538485a.3 for ; Wed, 06 May 2026 16:07:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778108853; x=1778713653; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=ANVPg7VR/jgoiN08fPKUsvHJnWWyqQ90OtjRAf9XFxo=; b=LCoBA+YH38KGgrhMeYE0Gv0ax5e0GYrslyQTqphthDwtB922cUGqJtC5LlM6T3Kq3E 4Cw895OhdaxO3Gm5J6ABJKXcdvKBl2UDN/rP/GKFzIgYBpPmE7ENwChpaugA34LsDPkn os+NVxM7E31b55t101sOZ2jeb68yiDSY07D3sFChpvXkIcrvsRzrAcv+YAk50IZ65DT3 0o64sZqcrSpUwQyb5jGj0UvOBZZfJPqbJFXTSN7IHxl5ChAAR/GQXn4RPQmf9ORsBm4X MqEgT0D6dx3h3askibqpOcKKsJTi5XP1jYXiXPtGXEW0FCZxodpyg8R7naQ7odTUH1w8 X9hQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778108853; x=1778713653; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=ANVPg7VR/jgoiN08fPKUsvHJnWWyqQ90OtjRAf9XFxo=; b=FPCTe/0ovqki5IvHE0oin0v9bd1Glrp4p5IFTARZYHNEQr7yKMJMHi09AuwO6hrdt8 hT40wGUV/At57HI01YkCcxJYxDXZ2GfXjoufJLdnCgKPeCjAL20dnGAQy6ODMaZU1aeV nZ/EbeuUSSoq5VussOBM3YnDwavKU/fXCtsCsFfhHAa7/J0th7xOQkl+zdB0EIPEjYck XTFFFU9wNyK9Gcer/gsBAXaG86D50Lv+uLMihH36BjAIIakixjXflRkedX4ohU/ytrsm iR4Cy1R7YzjLW0js0iESHAeO/P3NvSkUuFnU00dZjVLuZ7P9zYPFmu3RgbtmF/4U9IeM pQnw== X-Gm-Message-State: AOJu0Yz+UCriO52Z5K5V248Z1UOoM5sVa1UbR5TA6XL/F6UjquFPXykh 8G01b6A5PgS0qyS2QUjNrbZ0Ifu9hiJbpwTmDWU2yV1MY+DqLYjMjFoSmWWeEw== X-Gm-Gg: AeBDieuFcP3iaA5bC9d97ydgFjytMmL3HhHnI9mntWdMnadrsrW/esIjAjRT/bY6jVw iJHys/Lf/LVEtlBdPAstpMBnTJZAee4Le4dywb24In1daWH7wBwjrTBXPBDaYKrpUoY2DxC1VgM lYx8S328kP1p5AtehRkOkoIUu7DTM0vwIdScc2VS/44hKojqQt96eu6o68KfXq3R7+RCL8Teguw N13JUSNSF8VTlQuXq/YW+FEbHI1dOBRb7TktyWc0ONmgSSYoecqXwsv5iETewkPZtw/qSmNF7T5 aPO/PFcdYEXfj8dW30KNgp1d/7WTyKMIr88mrr/Gi+kqi5Ys2SHB50GEOyrFP6bTp1i9l8jI/8m AJSExotYiuaa8X3m+6jBsaDdaaEIu+kQU56rlhpNTUXPnPPSwb1co6Pr9hePWhut3q4OhrYev1/ eP2Kvua3Rmj7zWoxS1ZE4SPNJp7wsyyG/+Fd8z++Dkkv8teJca9RhyLEtmJyiLQWZgPXd/95XUo pYgEV7WuDM3adtwTaO8Pp5c/eNHo0my X-Received: by 2002:a05:620a:408e:b0:8ee:30e:cb97 with SMTP id af79cd13be357-904d4a61c65mr785228685a.21.1778108852492; Wed, 06 May 2026 16:07:32 -0700 (PDT) Received: from dexps.speedport.ip (p200300eda74453cf3cf3f7929e513b94.dip0.t-ipconnect.de. [2003:ed:a744:53cf:3cf3:f792:9e51:3b94]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8fc2c25324esm2035266385a.23.2026.05.06.16.07.31 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 May 2026 16:07:32 -0700 (PDT) From: d3vv3 To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 06/10] libcamera: software_isp: Normalize statistics sums to 8-bit Date: Thu, 7 May 2026 01:07:18 +0200 Message-ID: <20260506230722.1041596-7-devve.3@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260506230722.1041596-1-devve.3@gmail.com> References: <20260506230722.1041596-1-devve.3@gmail.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" From: Javier Tia The SWSTATS_ACCUMULATE_LINE_STATS() macro divides the luminance value for the histogram to normalize it to 8-bit range, but does not apply the same normalization to the RGB sums. For 10-bit and 12-bit unpacked Bayer formats this means the sums are accumulated at native bit depth (0-1023 or 0-4095 per pixel) while the AWB algorithm subtracts an 8-bit black level from them, under-correcting by 4x or 16x respectively. Fix this by right-shifting the RGB sums in finishFrame() to normalize them to 8-bit scale, matching the histogram and the 8-bit black level used by AWB. A per-format sumShift_ value is set in configure(): 0 for 8-bit and CSI-2 packed formats (already 8-bit), 2 for 10-bit, and 4 for 12-bit unpacked formats. Signed-off-by: Javier Tia Reviewed-by: Milan Zamazal Tested-by: Milan Zamazal Reviewed-by: Barnabás Pőcze Tested-by: Barnabás Pőcze Signed-off-by: d3vv3 --- include/libcamera/internal/software_isp/swstats_cpu.h | 1 + src/libcamera/software_isp/swstats_cpu.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/include/libcamera/internal/software_isp/swstats_cpu.h b/include/libcamera/internal/software_isp/swstats_cpu.h index 802370bd..2dac6945 100644 --- a/include/libcamera/internal/software_isp/swstats_cpu.h +++ b/include/libcamera/internal/software_isp/swstats_cpu.h @@ -116,6 +116,7 @@ private: unsigned int xShift_; unsigned int stride_; + unsigned int sumShift_; std::vector stats_; SharedMemObject sharedStats_; diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp index 5366e019..2ed906e1 100644 --- a/src/libcamera/software_isp/swstats_cpu.cpp +++ b/src/libcamera/software_isp/swstats_cpu.cpp @@ -362,6 +362,11 @@ void SwStatsCpu::finishFrame(uint32_t frame, uint32_t bufferId) for (unsigned int j = 0; j < SwIspStats::kYHistogramSize; j++) sharedStats_->yHistogram[j] += s.yHistogram[j]; } + if (sumShift_) { + sharedStats_->sum_.r() >>= sumShift_; + sharedStats_->sum_.g() >>= sumShift_; + sharedStats_->sum_.b() >>= sumShift_; + } } sharedStats_->valid = valid; @@ -422,6 +427,7 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg, unsigned int stat if (bayerFormat.packing == BayerFormat::Packing::None && setupStandardBayerOrder(bayerFormat.order) == 0) { processFrame_ = &SwStatsCpu::processBayerFrame2; + sumShift_ = bayerFormat.bitDepth - 8; switch (bayerFormat.bitDepth) { case 8: stats0_ = &SwStatsCpu::statsBGGR8Line0; @@ -442,6 +448,7 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg, unsigned int stat /* Skip every 3th and 4th line, sample every other 2x2 block */ ySkipMask_ = 0x02; xShift_ = 0; + sumShift_ = 0; processFrame_ = &SwStatsCpu::processBayerFrame2; switch (bayerFormat.order) { From patchwork Wed May 6 23:07:19 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: devve X-Patchwork-Id: 26664 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 D4D9CC3304 for ; Wed, 6 May 2026 23:07:40 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8C10A6303C; Thu, 7 May 2026 01:07:38 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="fxaxeS4g"; dkim-atps=neutral Received: from mail-qk1-x736.google.com (mail-qk1-x736.google.com [IPv6:2607:f8b0:4864:20::736]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CBF0963024 for ; Thu, 7 May 2026 01:07:34 +0200 (CEST) Received: by mail-qk1-x736.google.com with SMTP id af79cd13be357-8f0a87e23daso21937785a.1 for ; Wed, 06 May 2026 16:07:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778108854; x=1778713654; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=hUmrEbZjTFn8jTOOzmHe5n0FPgz7UNB05AkPrRPSWF8=; b=fxaxeS4gQWIFAmA/cKbbh5gEZUQ/TZrb/pfjCPQx2UvvxWp+5OkjiVBFW+r5UmwYp9 0gVBgk410p4Aneq76fz97VkK2aJRNsswRd1rBu0DM5h/5YT4Q//Jcm/D9P+PdtzZ97Lc 1ltLXG50YXQXkAXu28ZyjaIOdGt6hpcsyl65ghtFp4y8vTnjFHmOXxKDBSjg9fGIvKrl HODqorsHHSlMcf49WzEmNa+mQIDFzgALdTcrBnNHroX1O6gxIBtr0XAbCV0XUWmkCeyN dF8RInoriZt21Xa3ScbLQErJa/EUyrOG+CbpkR3n5ml+I+ZKymaEQLXpeJB7mlEuuyy4 WKaQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778108854; x=1778713654; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=hUmrEbZjTFn8jTOOzmHe5n0FPgz7UNB05AkPrRPSWF8=; b=nruXAQpphwKceXdGTYbU0xJlhWH/w+dSlKi+6V8HIEHu6JIGSsehtrPCrT5sSvCKIs EEmqLaefhg5+jUV5Fq3OV7A5Ace4tWlR1ra9a4p5/eMBak/cnYPWAaoqLYJUIoT1lgRc Kc5EIWYuVG8JSpCsJ1Cstsfpo3L4+Gcc1Xptje6dNl/l/msoKCp8JTQWUq+/15oZcW9N 1pKDttIShv6xK31kE3qgWLOLW6EJPeqcntWslHDhzJLMaj54StmEPhToxiJVBRRRirTr BaF7ik+zj2erVQUNnlxmal6ONHcwv0W9RpCmK1mxzpCSKC6voqv2tjuLc67894ObvfbW Bdzg== X-Gm-Message-State: AOJu0YwVpPKya4dXeSTZlwwepePJnLhL73OTOXCa7xEpP9Br/n3jn2iC cg6Ucqkt/j/YK/ipUhuNAqSzb2wbNnUpCqvJgGSjRJ9zDGjKDoptloC3JF2U7A== X-Gm-Gg: AeBDietG2I5aRCDQPPsP3O1uxiwtQDgWDgV83icKZA65FuAYN6kLe+fJCb7x1XXMxua xMvy1F2Z6fBzn7fWgmIAt1I2YxF7jyPKuNM6DqcRWGgSAO25Ox5gTIeQlTaFZCZg1s6gQYfvEWZ 9DbnO7nS9VnVPLJr6PPWi05rEJ4kH2vUWZjObGDTgjHD6EC0lvBpGg01h6o0vfgdTUl6V4fHA2Q L1W5BlqdoPcWNmO/wQa1Gmuf3GYaDkA+PHA5wRn3Dihx8ft9ovU9bYHRDPG9rE83lCXWV9ZTImv 8PusrMxtiCAD7MZdagYskzjtUbMHMfp14nYxSJtvyHtHNuw5bqPR7FXWXmee6OIo88eWcdKvwH2 Dub5HKRkMdeBggz4yp0DOOKtunuLzCnz5HwOK6sBr7LRpwA9Dw/ZVTbZHmPGc6oZ1liLVJ/tiAx pupra+5mgC5cN7qAmlK4KsNey+YJurZ7f6gz9lO8USNcHr5Ve5cWJ2dkOaKaAQYABZStkkmG+o/ 2MOF6RBumC2EzkzX/Rm5UqmvYNfDheO20TZ3Wtarkg= X-Received: by 2002:a05:620a:29c1:b0:8eb:f3c7:2246 with SMTP id af79cd13be357-904d60f60e3mr886020185a.35.1778108853719; Wed, 06 May 2026 16:07:33 -0700 (PDT) Received: from dexps.speedport.ip (p200300eda74453cf3cf3f7929e513b94.dip0.t-ipconnect.de. [2003:ed:a744:53cf:3cf3:f792:9e51:3b94]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8fc2c25324esm2035266385a.23.2026.05.06.16.07.32 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 May 2026 16:07:33 -0700 (PDT) From: d3vv3 To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 07/10] ipa: libipa: camera_sensor_helper: Add OV2740 black level Date: Thu, 7 May 2026 01:07:19 +0200 Message-ID: <20260506230722.1041596-8-devve.3@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260506230722.1041596-1-devve.3@gmail.com> References: <20260506230722.1041596-1-devve.3@gmail.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" From: Javier Tia Set blackLevel_ = 4096 (0x40 at 10-bit) in CameraSensorHelperOv2740. The OV2740 kernel driver programs BLC target register 0x4003 with 0x40 for the 180 MHz link frequency mode. This matches the same pattern used by OV5675 and other OmniVision sensors with a 10-bit black level of 64. Without this, the Simple pipeline falls back to auto-guessing the black level, which happens to arrive at the same value but isn't documented. More importantly, the CameraSensorHelper is the canonical location for sensor calibration data and is used across all pipeline handlers, not just Simple. Suggested-by: Robert Mader Signed-off-by: Javier Tia Reviewed-by: Milan Zamazal Reviewed-by: Kieran Bingham Signed-off-by: d3vv3 --- src/ipa/libipa/camera_sensor_helper.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ipa/libipa/camera_sensor_helper.cpp b/src/ipa/libipa/camera_sensor_helper.cpp index 72466867..f3e8d7c8 100644 --- a/src/ipa/libipa/camera_sensor_helper.cpp +++ b/src/ipa/libipa/camera_sensor_helper.cpp @@ -684,6 +684,8 @@ class CameraSensorHelperOv2740 : public CameraSensorHelper public: CameraSensorHelperOv2740() { + /* From Linux kernel driver: 0x40 at 10bits. */ + blackLevel_ = 4096; gain_ = AnalogueGainLinear{ 1, 0, 0, 128 }; } }; From patchwork Wed May 6 23:07:20 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: devve X-Patchwork-Id: 26665 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 E1BB0C3305 for ; Wed, 6 May 2026 23:07:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1CC5E6302D; Thu, 7 May 2026 01:07:40 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="hRk4KXZp"; dkim-atps=neutral Received: from mail-qk1-x72e.google.com (mail-qk1-x72e.google.com [IPv6:2607:f8b0:4864:20::72e]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 20D23630BB for ; Thu, 7 May 2026 01:07:36 +0200 (CEST) Received: by mail-qk1-x72e.google.com with SMTP id af79cd13be357-8e8c0c2d2bcso34900385a.1 for ; Wed, 06 May 2026 16:07:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778108855; x=1778713655; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=RzzlD4NGXBC8Ir7lyUZbzdtSSu1wXbM8QJB9JRJvLYY=; b=hRk4KXZpZVnPg1SVmCl0mjO/SP+PUPGA7FVBqDUgL5lLmVuakqmdST74wUk8zEhtNl EOyUkF5LaCTsaFukGR584fPHUAmhZtwm7Gux5LcFouhpRjhWFs9ebDCGHPgom/7cvweb 9l7BvgyLQOFXEHLSrEeVOCmHPOniwWY89wNgbLXSTQX6H+FX+9bEjUJlQHjyWHzQaSdn cwRc2d3NP+hx91lxLgU/jn9DHMtjhk6WrhaTvMEHQzqKTknrWLlZjshgleYXApYHSvlB KplsHKiY8IBt/zWQKDINQ4F5mOv7t4nfuoiIe/vwA2GNkg+Q4Z6oAG4X0E0hNQAQiJ99 m1ZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778108855; x=1778713655; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=RzzlD4NGXBC8Ir7lyUZbzdtSSu1wXbM8QJB9JRJvLYY=; b=PsfbwYhv6UqfIoRNyH2+MGfVIBVGxJARFE0s5PNQDsVZnGRlhduhwbg/1EYxNcIYxz +mI61Q98iyckyL6Ocf10HsH8i3ynwTD8ekQN2jsMNFyHQWUynE1uWJxNDK15aK/kBpnR k9/mVF48jLM4inFQ7DlAj/yi5FpcC/4MVt+WU8+dK4geZxA2DGQUdH22vW1pcTrqDEhr dbfj/x6CJNFlkqQEqhlcV7vMqLGqR1cqRgwklldHMULL6s3tvyulEMqjbEFKAy3f4afs bNWKWhELWWnTA5UrQvigJSmdj4ZLz2NgcDYgBN+wF1mZvzNDWMorTzRdIKFqiQfPAT1r uQfQ== X-Gm-Message-State: AOJu0YxUhcs1Uo9POhgtQNInQQqEpoEzvR+w9QNXE3aCQgnI5g2IXSPy yHVTn3Z704CEDGEOWQV9jYEhepGjZyunFkuoXNAWTVBl9MYSryDpn2340qLUlg== X-Gm-Gg: AeBDievbLRQwxWKr1U+YGyw1nnDJduogzCnUZQ/ujTPOF9/4X9NQ7dhw7OHnjSciNbY zj6eaLDxwqtzd2/AELzHc8CUKaXFGGLndhDMOj7HN3TNuoRlcifmZuptdu75mmtD/l4tn4Nc9Jq +JK6v94evdjl3XK8FPXuk66F5Qq3g7d7njzee5Q0Jc3wiNIa6lgkqy//fG47X5eP+An6ycLhwKg pR/flOzw4b3PSXKG8/6wEiH0AfH04a3IqSkLBI8xiHiTs/HFXHJ52ZdxLKwibt+eu/hNt0+VAQo iwGlvYd2jWUzXtPp+pRWMn+UCj6N3pAvwBgLot7ETg/JiWW+m8pl4htyGOYhBUlJ6Gea+I2Gxxt XQoHeV5g/bXvPxwnE1m2zbV7XjgT++FajSzJKuerE1I+ltrAXwc6GwNM7q7QCEfAHZegSTawI+Q xnb6uiVzzgRKzoQcq5XVlwa0jhSD1wz/C9UTDZLnYsrAvyaEcyJJwSHCZSJGPBlQdLbxTdsP7SP ZwyD4hYQSzsXJ77UkDogPJycjthzKuY X-Received: by 2002:a05:620a:29cc:b0:8ed:d906:a8da with SMTP id af79cd13be357-904d4a61b92mr784088785a.18.1778108855000; Wed, 06 May 2026 16:07:35 -0700 (PDT) Received: from dexps.speedport.ip (p200300eda74453cf3cf3f7929e513b94.dip0.t-ipconnect.de. [2003:ed:a744:53cf:3cf3:f792:9e51:3b94]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8fc2c25324esm2035266385a.23.2026.05.06.16.07.33 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 May 2026 16:07:34 -0700 (PDT) From: d3vv3 To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 08/10] ipa: simple: agc: Read exposure target and gain from YAML Date: Thu, 7 May 2026 01:07:20 +0200 Message-ID: <20260506230722.1041596-9-devve.3@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260506230722.1041596-1-devve.3@gmail.com> References: <20260506230722.1041596-1-devve.3@gmail.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" Replace the hardcoded kExposureOptimal, kExposureSatisfactory, and kExpProportionalGain constants with member variables read from the tuning file as exposureTarget, hysteresis, and proportionalGain. Defaults match the previous values (2.5, 0.2, 0.04). The constants are renamed to match their YAML keys and to use standard control-theory terminology: - kExposureOptimal -> exposureTarget: "optimal" implies a single universally correct value; "target" is the conventional ISP/AGC term for the setpoint the controller drives towards and is sensor-dependent. - kExposureSatisfactory -> hysteresis: the old name described the effect (exposure is satisfactory within this band) rather than the mechanism. "hysteresis" is the standard term for a deadband that prevents oscillation around a setpoint. - kExpProportionalGain -> proportionalGain: drops the redundant kExp prefix and matches the YAML key name directly. Signed-off-by: d3vv3 --- src/ipa/simple/algorithms/agc.cpp | 33 ++++++++++++++++++++----------- src/ipa/simple/algorithms/agc.h | 8 ++++++++ 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp index ac977d5f..66618d0f 100644 --- a/src/ipa/simple/algorithms/agc.cpp +++ b/src/ipa/simple/algorithms/agc.cpp @@ -28,45 +28,56 @@ static constexpr unsigned int kExposureBinsCount = 5; /* * The exposure is optimal when the mean sample value of the histogram is - * in the middle of the range. + * in the middle of the range. Overridable via YAML exposureTarget. */ -static constexpr float kExposureOptimal = kExposureBinsCount / 2.0; +static constexpr float kExposureTargetDefault = kExposureBinsCount / 2.0; /* * This implements the hysteresis for the exposure adjustment. * It is small enough to have the exposure close to the optimal, and is big * enough to prevent the exposure from wobbling around the optimal value. */ -static constexpr float kExposureSatisfactory = 0.2; +static constexpr float kHysteresisDefault = 0.2; /* * Proportional gain for exposure/gain adjustment. Maps the MSV error to a * multiplicative correction factor: * - * factor = 1.0 + kExpProportionalGain * error + * factor = 1.0 + proportionalGain_ * error * - * With kExpProportionalGain = 0.04: + * With proportionalGain_ = 0.04: * - max error ~2.5 -> factor 1.10 (~10% step, same as before) * - error 1.0 -> factor 1.04 (~4% step) * - error 0.3 -> factor 1.012 (~1.2% step) * - * This replaces the fixed 10% bang-bang step with a proportional correction - * that converges smoothly and avoids overshooting near the target. + * Overridable via YAML proportionalGain. */ -static constexpr float kExpProportionalGain = 0.04; +static constexpr float kProportionalGainDefault = 0.04; Agc::Agc() { } +int Agc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData) +{ + exposureTarget_ = tuningData["exposureTarget"].get() + .value_or(kExposureTargetDefault); + hysteresis_ = tuningData["hysteresis"].get() + .value_or(kHysteresisDefault); + proportionalGain_ = tuningData["proportionalGain"].get() + .value_or(kProportionalGainDefault); + + return 0; +} + void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV) { int32_t &exposure = frameContext.sensor.exposure; double &again = frameContext.sensor.gain; - double error = kExposureOptimal - exposureMSV; + double error = exposureTarget_ - exposureMSV; - if (std::abs(error) <= kExposureSatisfactory) + if (std::abs(error) <= hysteresis_) return; /* @@ -74,7 +85,7 @@ void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, dou * determines the direction: positive error means too dark (increase), * negative means too bright (decrease). */ - float factor = 1.0f + static_cast(error) * kExpProportionalGain; + float factor = 1.0f + static_cast(error) * proportionalGain_; if (factor > 1.0f) { /* Scene too dark: increase exposure first, then gain. */ diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h index 112d9f5a..b8ed542b 100644 --- a/src/ipa/simple/algorithms/agc.h +++ b/src/ipa/simple/algorithms/agc.h @@ -7,6 +7,8 @@ #pragma once +#include + #include "algorithm.h" namespace libcamera { @@ -19,6 +21,8 @@ public: Agc(); ~Agc() = default; + int init(IPAContext &context, const ValueNode &tuningData) override; + void process(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, const SwIspStats *stats, @@ -26,6 +30,10 @@ public: private: void updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV); + + float exposureTarget_; + float hysteresis_; + float proportionalGain_; }; } /* namespace ipa::soft::algorithms */ From patchwork Wed May 6 23:07:21 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: devve X-Patchwork-Id: 26666 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 BA291C3306 for ; Wed, 6 May 2026 23:07:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A8E3063137; Thu, 7 May 2026 01:07:40 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="QL6bvlBN"; dkim-atps=neutral Received: from mail-qk1-x730.google.com (mail-qk1-x730.google.com [IPv6:2607:f8b0:4864:20::730]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3785563022 for ; Thu, 7 May 2026 01:07:38 +0200 (CEST) Received: by mail-qk1-x730.google.com with SMTP id af79cd13be357-8d67a483d3eso24658785a.1 for ; Wed, 06 May 2026 16:07:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778108857; x=1778713657; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=hgD18DJi2B+HypgvtPdzMQmUh5U5RDMAHApeCfDjE1c=; b=QL6bvlBNn3hTuhfl6kk6OsHpws0bJZJh0zmcP4BMLEYuFGhYQFgiQAu1q1dZfSjWn5 ixwZiyFFfJQvKy283vxNvaGUZsj61XoBlg8HFH9XQckyBQP4TKYj1fS71QPQDdcX4ET3 0v6YdWm8TepSv8PoHFhsKBu2AUthCkDIwsONDZfq6VlNvm+CMJJST8iaDPB/+inY2aIs hbaChOsb2LWJrkPSWlrBNPUlxgnKwydmWnd8tX7vlASM+bw9IxqmRtB/92d5wE9qdTWX d0ypdeks3yX6KUkIpXywcUiHcOrh9vntCTQDurhkxnv9qDjgl7SD9a6mfSgIj07Y+Rs1 SPDA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778108857; x=1778713657; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=hgD18DJi2B+HypgvtPdzMQmUh5U5RDMAHApeCfDjE1c=; b=VD3sdRjJAxJawW9YSXoB+ig46YymkTQgdRFygehoQLfnXoNkCwYpNEOLJMCiURnbql mr6yWWAzvqgi/V59j3REm/2GnFcPaWbQP3iui7gj4IZjyJb3/qxTkpZetR/SapHmZPxx 5QUO1JgutVgE40ZQgIaw+4wB/x8NTyIbn5b0uT0lP+0HNFxdtFvePgvYwRZeCctjV3L6 QglGoSd7k4OtiEQM4cS1uthzIGSL7kbYgGBvOSlnvQuZO6K5dU9QTD6cWBFrNTCQ0o42 Nbxt2WNJloDGBauXWWwl7jvchd2QlYsNMQQjWqwXm1tRBaeHnYxwNemFL7X+fWrm3d0G 8byA== X-Gm-Message-State: AOJu0YwdJAxLUty1tQ5i2ezoanlD4GmKUIOkdshIdRAq9n7Ixk9Tvxz6 VNlZ7ePm/qBYSem566CZDtQMy86reXaTKAkl0THqrouU2VRPnE/Rbd8DcUJZQg== X-Gm-Gg: AeBDieuCc7fNjpuV5UMFnA/w/3hI+dFpGGatvkGP6q5/sMZIF2ALOt32lejy7Fy3Of2 IM5LqEQXpWUChmF26/hhP63E+zvN9o1HdzG26mZ98BENqGcUdeLOi70DyZ8s86mulIyISXseAy+ 8QADPPhJl0lLZW0TtzhneqLELlStUK3dS43iDgbI50DGHw0Db+QsWjNu0dw/M3GXoYDUsotS5xh rBHRSNn868/ivRV9uqBwy1AueSOM32wDuYsUzZPXFfaiOhABZnPYTRdFvcMJlKOAAkYNK9Effml ld4v6apyN4dooZf0VjagbXyQPxmHFwe4O/SvFztKooy2RB3wUqnHoKa1lajo07r1MOSaf0Zk0K/ ZqtMLawVoaHvCMC1dPiynRYtVrsqle6dMvUc32q898HEReRvesXfCvEw2z9GFGo4dp+fGnyULx+ iTvdnksfkFjPe/HduyCKb1XVlL8Ewtu0bq3amBK7d4r2h0VMUkL5wtQEooYNmeKFOlC1MWcMD46 q+PpYVloquUzcgsNZcZFRVdsqDkwtZi X-Received: by 2002:a05:620a:1a19:b0:8f4:3895:25d3 with SMTP id af79cd13be357-904d3eab6dbmr829234885a.9.1778108857037; Wed, 06 May 2026 16:07:37 -0700 (PDT) Received: from dexps.speedport.ip (p200300eda74453cf3cf3f7929e513b94.dip0.t-ipconnect.de. [2003:ed:a744:53cf:3cf3:f792:9e51:3b94]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8fc2c25324esm2035266385a.23.2026.05.06.16.07.35 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 May 2026 16:07:36 -0700 (PDT) From: d3vv3 To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 09/10] ipa: simple: data: Document tuning parameters in uncalibrated.yaml Date: Thu, 7 May 2026 01:07:21 +0200 Message-ID: <20260506230722.1041596-10-devve.3@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260506230722.1041596-1-devve.3@gmail.com> References: <20260506230722.1041596-1-devve.3@gmail.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" Add comments documenting all algorithm YAML keys (BlackLevel, Awb, Ccm, Adjust, Agc) to the uncalibrated template so sensor calibration authors can discover parameters without reading source code. Update ov01a10.yaml replacing stepDenominator (removed) with proportionalGain for the new proportional AGC controller. Signed-off-by: d3vv3 --- src/ipa/simple/data/ov01a10.yaml | 2 +- src/ipa/simple/data/uncalibrated.yaml | 40 +++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/ipa/simple/data/ov01a10.yaml b/src/ipa/simple/data/ov01a10.yaml index d1f613c5..9663c6da 100644 --- a/src/ipa/simple/data/ov01a10.yaml +++ b/src/ipa/simple/data/ov01a10.yaml @@ -51,5 +51,5 @@ algorithms: - Agc: exposureTarget: 2.5 hysteresis: 0.2 - stepDenominator: 20 + proportionalGain: 0.04 ... diff --git a/src/ipa/simple/data/uncalibrated.yaml b/src/ipa/simple/data/uncalibrated.yaml index fc90ca52..7391c36e 100644 --- a/src/ipa/simple/data/uncalibrated.yaml +++ b/src/ipa/simple/data/uncalibrated.yaml @@ -3,17 +3,45 @@ --- version: 1 algorithms: + # --- Black Level --- + # blackLevel: 16-bit black level pedestal (optional). + # If omitted, auto-detected from histogram dark end. - BlackLevel: + + # --- Auto White Balance --- + # maxGainR: Maximum red channel gain (default 4.0). + # maxGainB: Maximum blue channel gain (default 4.0). + # speed: Temporal smoothing factor 0-1 (default 1.0 = instant). + # 0.25 = slow smooth, 0.5 = moderate, 1.0 = no smoothing. - Awb: - # Color correction matrices can be defined here. The CCM algorithm - # has a significant performance impact, and should only be enabled - # if tuned. + + # --- Color Correction Matrix --- + # Has a significant performance impact on the CPU ISP, and should + # only be enabled if tuned. Provide ccms as a list of color temperature + # entries with a 3x3 matrix: # - Ccm: # ccms: # - ct: 6500 - # ccm: [ 1, 0, 0, - # 0, 1, 0, - # 0, 0, 1] + # ccm: [ 1.0, 0.0, 0.0, + # 0.0, 1.0, 0.0, + # 0.0, 0.0, 1.0 ] + # - Ccm: + + # --- Image Adjustments --- + # gamma: Gamma encoding value (default 2.2, range 0.1-10.0). + # contrast: Contrast scaling (default 1.0, range 0.0-2.0). + # saturation: Saturation multiplier (default 1.0, range 0.0-2.0). + # Only active when CCM is enabled. - Adjust: + + # --- Auto Gain/Exposure Control (proportional) --- + # exposureTarget: Target MSV (mean sample value) in histogram bins. + # Default: 2.5 (middle of 5-bin range). + # Lower values target a darker exposure. + # hysteresis: Deadband around target where no adjustment occurs. + # Default 0.2. Larger values reduce sensitivity near target. + # proportionalGain: Step scaling factor (default 0.04). + # At max error (~2.5): factor 1.10 (~10% step). + # Near target: factor ~1.01 (~1% step). - Agc: ... From patchwork Wed May 6 23:07:22 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: devve X-Patchwork-Id: 26667 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 D6835C3307 for ; Wed, 6 May 2026 23:07:44 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7C3E4630BB; Thu, 7 May 2026 01:07:42 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="VyO39wYf"; dkim-atps=neutral Received: from mail-ua1-x92a.google.com (mail-ua1-x92a.google.com [IPv6:2607:f8b0:4864:20::92a]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 924D86327F for ; Thu, 7 May 2026 01:07:39 +0200 (CEST) Received: by mail-ua1-x92a.google.com with SMTP id a1e0cc1a2514c-95d0476492bso114819241.2 for ; Wed, 06 May 2026 16:07:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778108858; x=1778713658; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=iuKxaCdLNK1rpp1md2t0A+vNdZQgr0zrLCF8pVdKmlk=; b=VyO39wYfwsXUXygdkXIOKx8swGbqoFhkVBFnAb1whZT24zxp9DZ3SGik45/8/GNFGp +LTgY/Ay8+zsPkggU7mi4igVditmdnJSLJvEna2x8Ijg63y/eqWCeAzaLXaes5DxRzTG kYZxRhfKjxLz4mr7Y3PAYhLiWgeUKqmDdB3UbKj2BrTq22y/WaNPtAVge/nliY/fFb+a c8LfAHhkaAB890P5eqkdfOS//iMqSGeRyclPF2QEZoLBzvim+NuB+2u7qREMXno2Kemc DihM/vdyjstLnU43Cg2un6GCIMeXqcNdlhvrVUGlDN4kd5UHxc+/9le7zZizfWRsAM+7 BKGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778108858; x=1778713658; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=iuKxaCdLNK1rpp1md2t0A+vNdZQgr0zrLCF8pVdKmlk=; b=VgcUjX4yJnqiRmEZaiR/nw+1DUJRpPkqcfw0rFxYgXu6yd5kipNNTC5pNydpVyHmik bB1hGgrNTm+I0bcSR7x+Y4wJ7OFlA/p7I1BqdAK27OvikImnMlJHo7QUHse/BngXsbRW uY8wDKilV9a1jKKGYoPyFZs5OmZT8lXGBqUZ2VpzNC8DXIBMmGXh43nbR297Dx/eH2ld RsSnJC48poWquav9Pqq2l2OHaKyuwufAqBSxRt75nsbbS4XWztupFnWZJixVD0gMijQI dSSB6lqjCZEWufR7CQqqmfKpskSrb49oQLBCvTBmV2DEIo2ZU3ngXF0gRlu6ePpYs48I XHpw== X-Gm-Message-State: AOJu0YyXczirCMvcnpOkC/p2vCK4YmJ537UQz7xwz8D+KQ3BysmBxm0u ILtl+iNMffHmjn2VnHfmwlwNkdH8nKCLtJ56L8fIzpo2+DFN4MOLR7RhbmXGtA== X-Gm-Gg: AeBDiev6aRTpCityw56Wclc0x5wf8K3Z12Qqd1A6xl9OdSjFPbD/PtJML0/nSjzSsHo M5EnDQmucOlphL7bh7RV0wSGJP+8rtwMz2HwFEvl6nyh6ZAheFU/1XSpiBME9RgSeOAc17Qoczk f0meS79So/VLzqTEW4Efhyt+PxwMjmpiKlzWTFxHGqNDmavOIOx8iSQ+Y2Las+X7gdgx+NxudSv WFbAv40rtvVeCDG8kBWFCr6e0aljLZ6VzT8csJcqkFKYdFvR0X0x6xVrWcTMaDNhp7x9Imssa9O snk2vOXGipWMbK/tQegtF+cfSmn17HRY+n3YNSJn6ne5D8DcDJ58FMkDdU+J+LCa3ZmWx9wtWFx +zdRI6WNcLQUhzH2HKAdRewdxToW1EJu91YaN6m07f27uVfBA5Xxl/3flxwivg/fvHwEfmvYLtM cBfZizksgH99Udj4ILyaOJv/VN8ixXJZHlxcvs8gmWYaNI55lygGUNTWF2lubcM8E153p1agu5I fVO9wNAfECk34qbNRKGd92oU7U+RA4CZFlstJ/Zl9g= X-Received: by 2002:a67:e102:0:b0:628:5313:11a with SMTP id ada2fe7eead31-630f90ecf9bmr2955890137.28.1778108858210; Wed, 06 May 2026 16:07:38 -0700 (PDT) Received: from dexps.speedport.ip (p200300eda74453cf3cf3f7929e513b94.dip0.t-ipconnect.de. [2003:ed:a744:53cf:3cf3:f792:9e51:3b94]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8fc2c25324esm2035266385a.23.2026.05.06.16.07.37 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 May 2026 16:07:37 -0700 (PDT) From: d3vv3 To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 10/10] test: ipa: libipa: Add CCM row-sum validation test Date: Thu, 7 May 2026 01:07:22 +0200 Message-ID: <20260506230722.1041596-11-devve.3@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260506230722.1041596-1-devve.3@gmail.com> References: <20260506230722.1041596-1-devve.3@gmail.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" Verify that each row of a colour correction matrix sums to 1.0 (luminance preservation property). Tests cover: - identity and known-bad inline matrices - a real OV01A10 D65 calibrated CCM - parsing a multi-entry CCM YAML table via Interpolator - detection of a bad entry in YAML Tolerance is 5e-4 to accommodate 4-decimal-place rounding in tuning files. Signed-off-by: d3vv3 --- test/ipa/libipa/ccm.cpp | 158 ++++++++++++++++++++++++++++++++++++ test/ipa/libipa/meson.build | 1 + 2 files changed, 159 insertions(+) create mode 100644 test/ipa/libipa/ccm.cpp diff --git a/test/ipa/libipa/ccm.cpp b/test/ipa/libipa/ccm.cpp new file mode 100644 index 00000000..efc0035a --- /dev/null +++ b/test/ipa/libipa/ccm.cpp @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024-2026, Red Hat Inc. + * + * CCM matrix row-sum validation tests + * + * Each row of a colour correction matrix must sum to 1.0 (luminance + * preservation). This test verifies that property for inline matrix data + * and for matrices parsed from a YAML CCM table. + */ + +#include "../../../src/ipa/libipa/interpolator.h" + +#include +#include +#include +#include + +#include "libcamera/base/file.h" +#include "libcamera/internal/matrix.h" +#include "libcamera/internal/yaml_parser.h" + +#include "test.h" + +using namespace std; +using namespace libcamera; +using namespace ipa; + +/* Tolerance for floating-point row-sum comparison. + * CCM values in tuning files are typically given to 4 decimal places, + * which can introduce up to ~0.5e-3 rounding error per row. */ +static constexpr float kRowSumTolerance = 5e-4f; + +#define ASSERT_TRUE(cond) \ + do { \ + if (!(cond)) { \ + cerr << "FAIL: " #cond "\n"; \ + return TestFail; \ + } \ + } while (0) + +static bool allRowsSumToOne(const Matrix &m) +{ + for (unsigned int row = 0; row < 3; row++) { + float sum = 0.0f; + for (unsigned int col = 0; col < 3; col++) + sum += m[row][col]; + if (std::abs(sum - 1.0f) > kRowSumTolerance) + return false; + } + return true; +} + +class CcmRowSumTest : public Test +{ +protected: + bool writeTempYaml(const std::string &content, std::string &filename) + { + filename = "/tmp/libcamera.ccm.test.XXXXXX"; + int fd = mkstemp(&filename.front()); + if (fd == -1) + return false; + ssize_t ret = write(fd, content.c_str(), content.size()); + close(fd); + return ret == static_cast(content.size()); + } + + std::unique_ptr parseYaml(const std::string &content) + { + std::string filename; + if (!writeTempYaml(content, filename)) + return nullptr; + + File file{ filename }; + if (!file.open(File::OpenModeFlag::ReadOnly)) + return nullptr; + + auto root = YamlParser::parse(file); + unlink(filename.c_str()); + return root; + } + + int run() + { + /* --- 1. Known-good identity matrix --- */ + Matrix identity{ { 1, 0, 0, + 0, 1, 0, + 0, 0, 1 } }; + ASSERT_TRUE(allRowsSumToOne(identity)); + + /* --- 2. Known-bad matrix (rows do not sum to 1) --- */ + Matrix bad{ { 2, 0, 0, + 0, 1, 0, + 0, 0, 1 } }; + ASSERT_TRUE(!allRowsSumToOne(bad)); + + /* --- 3. Typical calibrated CCM (D65, OV01A10) --- */ + Matrix d65{ { 1.8163f, -0.7062f, -0.1100f, + -0.1640f, 1.5736f, -0.4096f, + -0.0084f, -0.8294f, 1.8378f } }; + ASSERT_TRUE(allRowsSumToOne(d65)); + + /* --- 4. Parse a valid CCM table from YAML and validate --- */ + const std::string validYaml = + "- ct: 2856\n" + " ccm: [ 1.1248, 0.2210, -0.3458,\n" + " -0.4616, 1.7736, -0.3120,\n" + " -0.4342, -0.9348, 2.3690 ]\n" + "- ct: 6500\n" + " ccm: [ 1.8163, -0.7062, -0.1100,\n" + " -0.1640, 1.5736, -0.4096,\n" + " -0.0084, -0.8294, 1.8378 ]\n" + "- ct: 7500\n" + " ccm: [ 1.8953, -0.7980, -0.0973,\n" + " -0.1539, 1.6001, -0.4462,\n" + " -0.0101, -0.7800, 1.7902 ]\n"; + + auto root = parseYaml(validYaml); + ASSERT_TRUE(root); + + Interpolator> interp; + ASSERT_TRUE(interp.readYaml(*root, "ct", "ccm") == 0); + ASSERT_TRUE(interp.data().size() == 3); + + for (const auto &[ct, m] : interp.data()) { + if (!allRowsSumToOne(m)) { + cerr << "CCM at ct=" << ct + << " has a row that does not sum to 1.0\n"; + return TestFail; + } + } + + /* --- 5. Detect a bad entry in YAML --- */ + const std::string badYaml = + "- ct: 5000\n" + " ccm: [ 2.0000, -0.7062, -0.1100,\n" + " -0.1640, 1.5736, -0.4096,\n" + " -0.0084, -0.8294, 1.8378 ]\n"; + + auto badRoot = parseYaml(badYaml); + ASSERT_TRUE(badRoot); + + Interpolator> badInterp; + ASSERT_TRUE(badInterp.readYaml(*badRoot, "ct", "ccm") == 0); + + for (const auto &[ct, m] : badInterp.data()) { + if (allRowsSumToOne(m)) { + cerr << "Expected bad CCM at ct=" << ct + << " to fail row-sum check, but it passed\n"; + return TestFail; + } + } + + return TestPass; + } +}; + +TEST_REGISTER(CcmRowSumTest) diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build index c3e25587..8c36800c 100644 --- a/test/ipa/libipa/meson.build +++ b/test/ipa/libipa/meson.build @@ -1,6 +1,7 @@ # SPDX-License-Identifier: CC0-1.0 libipa_test = [ + {'name': 'ccm', 'sources': ['ccm.cpp']}, {'name': 'fixedpoint', 'sources': ['fixedpoint.cpp']}, {'name': 'histogram', 'sources': ['histogram.cpp']}, {'name': 'interpolator', 'sources': ['interpolator.cpp']},