Patch Detail
Show a patch.
GET /api/1.1/patches/26529/?format=api
{ "id": 26529, "url": "https://patchwork.libcamera.org/api/1.1/patches/26529/?format=api", "web_url": "https://patchwork.libcamera.org/patch/26529/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20260420183949.110548-9-mzamazal@redhat.com>", "date": "2026-04-20T18:39:46", "name": "[RFC,v2,8/8] utils/tuning: Add LSC scripts", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "f61862f5a6c5374e93b385ea0b6c0b57fd5462d2", "submitter": { "id": 177, "url": "https://patchwork.libcamera.org/api/1.1/people/177/?format=api", "name": "Milan Zamazal", "email": "mzamazal@redhat.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/26529/mbox/", "series": [ { "id": 5883, "url": "https://patchwork.libcamera.org/api/1.1/series/5883/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5883", "date": "2026-04-20T18:39:38", "name": "LSC for SoftISP simple pipeline", "version": 2, "mbox": "https://patchwork.libcamera.org/series/5883/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/26529/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/26529/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 A3421BDCBD\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 20 Apr 2026 18:40:23 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 55A0062EFD;\n\tMon, 20 Apr 2026 20:40:23 +0200 (CEST)", "from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.133.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B97D762EF6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 20 Apr 2026 20:40:21 +0200 (CEST)", "from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com\n\t(ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97])\n\tby relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n\tcipher=TLS_AES_256_GCM_SHA384) id us-mta-616-Haj7pC93PJq8vKSs1TiTmg-1;\n\tMon, 20 Apr 2026 14:40:18 -0400", "from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com\n\t(mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com\n\t[10.30.177.93])\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-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTPS id B7C22180036E; Mon, 20 Apr 2026 18:40:17 +0000 (UTC)", "from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.44.48.16])\n\tby mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTP id A6221180047F; Mon, 20 Apr 2026 18:40:15 +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=\"dTI8PA4Q\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1776710420;\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=9sWLN5zFsvsjo7uIP+VvJtokKl+xHqKEhtQ8BXwskRc=;\n\tb=dTI8PA4QpXfX2Q5eNDfhVefI1+VakYln35SKeeHYg3Gn3CazS8xYk6iuRIx0cerk3CsuPW\n\t8e3/WMo/HX6/YyW6XpIEmC4FK6Xmtr4x8zURfoVIEF9Lcwpsmbprde5leEbrCB/pRf8k1u\n\tUvgVCy/PVdCcxLlpPzUAE+LCz5ZPBTM=", "X-MC-Unique": "Haj7pC93PJq8vKSs1TiTmg-1", "X-Mimecast-MFC-AGG-ID": "Haj7pC93PJq8vKSs1TiTmg_1776710417", "From": "Milan Zamazal <mzamazal@redhat.com>", "To": "libcamera-devel@lists.libcamera.org", "Cc": "Derek Gielen <derekgielen@outlook.com>,\n\tHans de Goede <johannes.goede@oss.qualcomm.com>,\n\tAron Dosti <aron-dosti@hotmail.com>, Milan Zamazal <mzamazal@redhat.com>", "Subject": "[RFC PATCH v2 8/8] utils/tuning: Add LSC scripts", "Date": "Mon, 20 Apr 2026 20:39:46 +0200", "Message-ID": "<20260420183949.110548-9-mzamazal@redhat.com>", "In-Reply-To": "<20260420183949.110548-1-mzamazal@redhat.com>", "References": "<20260420183949.110548-1-mzamazal@redhat.com>", "MIME-Version": "1.0", "X-Scanned-By": "MIMEDefang 3.4.1 on 10.30.177.93", "X-Mimecast-Spam-Score": "0", "X-Mimecast-MFC-PROC-ID": "JF6LWjGlCD4v81BBJbogK3USnkbeD4Fjyox4OLikmJQ_1776710417", "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": "From: Derek Gielen <derekgielen@outlook.com>\n\nAdd script for the conversion of the tuning file to a format usable by\nthe lens shading correction shader.\n\nAdd script for visualizing the lens shading correction using pyplot.\n\nCo-developed-by: Aron Dosti <aron-dosti@hotmail.com>\nSigned-off-by: Aron Dosti <aron-dosti@hotmail.com>\nSigned-off-by: Derek Gielen <derekgielen@outlook.com>\nSigned-off-by: Milan Zamazal <mzamazal@redhat.com>\n---\n utils/tuning/exportTuningToLscShader.py | 120 ++++++++++++++++++++++++\n utils/tuning/generate_lsc_map_plot.py | 76 +++++++++++++++\n 2 files changed, 196 insertions(+)\n create mode 100644 utils/tuning/exportTuningToLscShader.py\n create mode 100644 utils/tuning/generate_lsc_map_plot.py", "diff": "diff --git a/utils/tuning/exportTuningToLscShader.py b/utils/tuning/exportTuningToLscShader.py\nnew file mode 100644\nindex 000000000..60bc37ec2\n--- /dev/null\n+++ b/utils/tuning/exportTuningToLscShader.py\n@@ -0,0 +1,120 @@\n+import yaml\n+import numpy as np\n+import argparse\n+import sys\n+\n+# --- Configuration ---\n+GRID_W = 16\n+GRID_H = 16\n+\n+# Formula constants\n+BLACK_LEVEL = 1024.0\n+SCALE_FACTOR = 3071 # Divisor to map the range to 0-255\n+\n+def load_and_process_yaml(input_filename, target_ct):\n+ # 1. Load the YAML file\n+ try:\n+ with open(input_filename, 'r') as f:\n+ data = yaml.safe_load(f)\n+ except FileNotFoundError:\n+ print(f\"Error: Input file '{input_filename}' not found.\")\n+ return None, None, None\n+\n+ # 2. Find the LensShadingCorrection block\n+ lsc_data = None\n+ for algo in data['algorithms']:\n+ if 'LensShadingCorrection' in algo:\n+ lsc_data = algo['LensShadingCorrection']\n+ break\n+\n+ if not lsc_data:\n+ print(\"Error: LensShadingCorrection block not found.\")\n+ return None, None, None\n+\n+ # 3. Extract the set for the specific Color Temperature (CT) provided in arguments\n+ sets = lsc_data['sets']\n+ target_set = next((item for item in sets if item['ct'] == target_ct), None)\n+\n+ if not target_set:\n+ print(f\"Error: Set for Color Temperature {target_ct} not found in '{input_filename}'.\")\n+ return None, None, None\n+\n+ print(f\"Found data for CT {target_ct}. Applying formula: (x - {int(BLACK_LEVEL)}) / {SCALE_FACTOR} ...\")\n+\n+ # 4. Get Raw Data\n+ r_raw = np.array(target_set['r'])\n+ b_raw = np.array(target_set['b'])\n+ gr_raw = np.array(target_set['gr'])\n+ gb_raw = np.array(target_set['gb'])\n+\n+ # Calculate Green Channel (Average of GR and GB)\n+ g_raw = (gr_raw + gb_raw) / 2.0\n+\n+ # 5. Define the calculation logic\n+ def apply_formula(data_array):\n+ \"\"\"\n+ Applies the specific user formula:\n+ 1. Subtract Black Level (1024)\n+ 2. Divide by the Scale Factor (3071)\n+ 3. Multiply by 255 and convert to integer\n+ \"\"\"\n+ result = ((data_array - BLACK_LEVEL) / SCALE_FACTOR) * 255\n+ return result.astype(int)\n+\n+ # 6. Apply calculation to all channels\n+ r_final = apply_formula(r_raw)\n+ g_final = apply_formula(g_raw)\n+ b_final = apply_formula(b_raw)\n+\n+ return r_final, g_final, b_final\n+\n+def save_custom_grid_yaml(output_filename, r, g, b, target_ct):\n+\n+ # Helper function to format the array as a visual grid string\n+ def format_array_as_grid_string(arr):\n+ lines = []\n+ # Loop through the array in chunks of 16 (GRID_W)\n+ for i in range(0, len(arr), GRID_W):\n+ row = arr[i:i + GRID_W]\n+ # Join numbers with commas\n+ row_str = \", \".join(map(str, row))\n+ lines.append(f\" {row_str}\")\n+ # Wrap in brackets to form a valid YAML list, but visually formatted\n+ return \"[\\n\" + \",\\n\".join(lines) + \"\\n ]\"\n+\n+ # Write the file manually to ensure specific formatting\n+ with open(output_filename, 'w') as f:\n+ f.write(f\"description: 'LSC Fixed Formula ((x-{int(BLACK_LEVEL)})/{SCALE_FACTOR})'\\n\")\n+ f.write(f\"source_ct: {target_ct}\\n\")\n+ f.write(f\"grid_size: [{GRID_W}, {GRID_H}]\\n\")\n+ f.write(f\"formula_used: '(RawValue - {int(BLACK_LEVEL)}) / {SCALE_FACTOR} -> [0..255]'\\n\")\n+ f.write(f\"channels:\\n\")\n+\n+ f.write(\" red: \" + format_array_as_grid_string(r) + \"\\n\")\n+ f.write(\" green: \" + format_array_as_grid_string(g) + \"\\n\")\n+ f.write(\" blue: \" + format_array_as_grid_string(b) + \"\\n\")\n+\n+ print(f\"Success! Saved formatted grid to '{output_filename}'\")\n+\n+# --- Main Execution ---\n+if __name__ == \"__main__\":\n+ # 1. Setup Argument Parser\n+ parser = argparse.ArgumentParser(description=\"Convert LSC YAML data to shader grid format.\")\n+ parser.add_argument(\"ct\", type=int, help=\"The Color Temperature to process (e.g. 2700, 5000, 6500)\")\n+\n+ # 2. Parse arguments\n+ args = parser.parse_args()\n+ ct_val = args.ct\n+\n+ # 3. Construct filenames based on the CT value\n+ # Assumes input file is named 'tuning_XXXX.yaml'\n+ input_file = f'tuning{ct_val}.yaml'\n+ output_file = f'lsc_shader_16x16_{ct_val}_fixed.yaml'\n+\n+ print(f\"--- Processing for Color Temp: {ct_val} ---\")\n+\n+ # 4. Run Process\n+ r, g, b = load_and_process_yaml(input_file, ct_val)\n+\n+ if r is not None:\n+ save_custom_grid_yaml(output_file, r, g, b, ct_val)\ndiff --git a/utils/tuning/generate_lsc_map_plot.py b/utils/tuning/generate_lsc_map_plot.py\nnew file mode 100644\nindex 000000000..7a0aaeb8a\n--- /dev/null\n+++ b/utils/tuning/generate_lsc_map_plot.py\n@@ -0,0 +1,76 @@\n+import yaml\n+import numpy as np\n+import matplotlib.pyplot as plt\n+\n+# 1. Load the data\n+try:\n+ with open('tuning2700.yaml', 'r') as f:\n+ data = yaml.safe_load(f)\n+except FileNotFoundError:\n+ print(\"Error: 'tuning.yaml' not found. Please ensure the file exists.\")\n+ exit()\n+\n+# 2. Find LensShadingCorrection safely\n+lsc_data = None\n+for algo in data['algorithms']:\n+ if 'LensShadingCorrection' in algo:\n+ lsc_data = algo['LensShadingCorrection']\n+ break\n+\n+if not lsc_data:\n+ print(\"Error: LensShadingCorrection block not found in YAML.\")\n+ exit()\n+\n+# 3. Extract the set for disirable Kelvin\n+kelvin = 2700\n+sets = lsc_data['sets']\n+target_set = next((item for item in sets if item['ct'] == kelvin), None)\n+\n+if not target_set:\n+ print(\"Error: CT 6500 not found in sets.\")\n+ exit()\n+\n+# 4. Get lists and normalize (1024 = 1.0 gain)\n+r_list = np.array(target_set['r'])\n+gr_list = np.array(target_set['gr'])\n+gb_list = np.array(target_set['gb'])\n+b_list = np.array(target_set['b'])\n+\n+r_norm = r_list / 1024.0\n+b_norm = b_list / 1024.0\n+# Average the two greens for the shader\n+g_norm = (gr_list + gb_list) / 2.0 / 1024.0\n+\n+# 5. Reshape into 17x17 Grids\n+grid_size = (17, 17)\n+r_grid = r_norm.reshape(grid_size)\n+g_grid = g_norm.reshape(grid_size)\n+b_grid = b_norm.reshape(grid_size)\n+\n+# 6. Visualization\n+# We create 3 separate plots to see the data distribution correctly\n+fig, axs = plt.subplots(1, 3, figsize=(15, 5))\n+\n+# Plot Red\n+im1 = axs[0].imshow(r_grid, cmap='viridis')\n+axs[0].set_title('Red Gain Map')\n+fig.colorbar(im1, ax=axs[0])\n+\n+# Plot Green\n+im2 = axs[1].imshow(g_grid, cmap='viridis')\n+axs[1].set_title('Green Gain Map')\n+fig.colorbar(im2, ax=axs[1])\n+\n+# Plot Blue\n+im3 = axs[2].imshow(b_grid, cmap='viridis')\n+axs[2].set_title('Blue Gain Map')\n+fig.colorbar(im3, ax=axs[2])\n+\n+plt.suptitle(f\"LSC Gain Maps (Center ~1.0, Corners > 1.0) for collor temprature {kelvin}\")\n+plt.show()\n+\n+# 7. Prepare Texture for Export (Optional)\n+# Stack them for your shader: (17, 17, 3)\n+lsc_texture = np.dstack((r_grid, g_grid, b_grid))\n+print(f\"Final Texture Shape: {lsc_texture.shape}\")\n+print(f\"Sample Blue Value at Corner: {b_grid[0, 0]}\")\n", "prefixes": [ "RFC", "v2", "8/8" ] }