Patch Detail
Show a patch.
GET /api/patches/25964/?format=api
{ "id": 25964, "url": "https://patchwork.libcamera.org/api/patches/25964/?format=api", "web_url": "https://patchwork.libcamera.org/patch/25964/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/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": "<20260126104256.119697-7-rick.w.ten.wolde@gmail.com>", "date": "2026-01-26T10:42:54", "name": "[6/7] utils/tuning: Add LSC scripts", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "f54deb28530dc7ab7a5fc7d04296dea0ded577e9", "submitter": { "id": 257, "url": "https://patchwork.libcamera.org/api/people/257/?format=api", "name": "Rick ten Wolde", "email": "rick.w.ten.wolde@gmail.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/25964/mbox/", "series": [ { "id": 5739, "url": "https://patchwork.libcamera.org/api/series/5739/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5739", "date": "2026-01-26T10:42:48", "name": "LSC for SoftISP simple pipeline", "version": 1, "mbox": "https://patchwork.libcamera.org/series/5739/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/25964/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/25964/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 792CBC32E7\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 26 Jan 2026 10:48:18 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E5B4C61FD1;\n\tMon, 26 Jan 2026 11:48:16 +0100 (CET)", "from mail-ej1-x62b.google.com (mail-ej1-x62b.google.com\n\t[IPv6:2a00:1450:4864:20::62b])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3399C61FD3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 26 Jan 2026 11:43:08 +0100 (CET)", "by mail-ej1-x62b.google.com with SMTP id\n\ta640c23a62f3a-b8850aa5b56so622017866b.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 26 Jan 2026 02:43:08 -0800 (PST)", "from castortop.wolde.loc (195-240-110-192.fixed.kpn.net.\n\t[195.240.110.192]) by smtp.gmail.com with ESMTPSA id\n\ta640c23a62f3a-b885b7661f7sm599220366b.54.2026.01.26.02.43.06\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tMon, 26 Jan 2026 02:43:06 -0800 (PST)" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"BiVQyINT\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=gmail.com; s=20230601; t=1769424187; x=1770028987;\n\tdarn=lists.libcamera.org; \n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=UGMhLAQJP7biuVIZMSesOWnaMhbq21WkJYjcWc6nhwQ=;\n\tb=BiVQyINTRtuMoSFKmFBtaVpfqeZqz5rQ2dsi5KveDjpb6HbnvFC6nY4p9Gp7Ky1FNn\n\taNoxO989p3Uj8w5HI8hZFe5/iymYryWIJkwnxpxHhieoHI3pY74Q3oRHZqyV8igzWEWh\n\t/RJPfhpxy9RnOBcrFAdLDSJVYQcuLWxZ8JxcznD9y5BRB+cys5R108IyOiIL989l7u8A\n\tFnnW8eGa5M4BOrcZo7NX3+tuTFZzZB9QIXZrzK0aENkx5GrAFKzfVEmO3RlRgS73slHE\n\t70/Osve22Q0mM20lWvMQpuaBLIWC0lK/p1aT9olgut+Oh18G2cyzB4aI6oHtBayUehK/\n\t5N/Q==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1769424187; x=1770028987;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n\t:to:cc:subject:date:message-id:reply-to;\n\tbh=UGMhLAQJP7biuVIZMSesOWnaMhbq21WkJYjcWc6nhwQ=;\n\tb=GRjrO6DEpuWyK6wPf6Vy3Z1uiqL3tQwHmT5Id1MKtgZjmy2sDCTcLNDYTfQQvKCoPM\n\tSe5WWomiDym4F6eftqqMCpFU2hUdsNkVSgLbmwAu+4no7Nsm9pd8WfKBKnl7J1olAa8V\n\t+7ylBK32OZ3ECWmCAAubLjmkwhJI92pGxYc+LfzFNLSy6my6kAyiujL7vzPvVrDuhQAA\n\tdsiVUBHyeEhJ/5kcooY3jH057pIR1y59RpJn+KcZTG0GAlpoKIvUEjpzlBy7YjN+QnBH\n\t4EXVMdJgOgnLExZF6KouqNb3PcVW+3VwKQXt2AWFLAJv39aUAdJ1bGfjRnbDZs+FcDMf\n\to9RQ==", "X-Gm-Message-State": "AOJu0YxRH0bogJ1fMlXyM3OrS/UDU7cZnp7MK+x73eLUl22zaGHp0qFW\n\t/Hp5pHCxCZfRWuL6Si5mIY+uEvqkdBx7SsgyYk0UJfHAa5wnEuvF4P55+cNrPw==", "X-Gm-Gg": "AZuq6aKkFGrITgDbsYMZL0+WXnfr8juzpyaHNIrYRiN9M4XW4zmPSOss7JfqcjKhT8r\n\toSbfsHQAAyozfIRm7iSi3h9a2QYpJRiX9Dg67iw9scMcCSN00GPDbSALWU5y1jR0pgxp4GZrPhL\n\tX99er6sn02sPsvkr69ZvsSUglmmUek6dv0Pdd8jedmRwN7Y/UWDDlejtNIHiGaY6gdYreGIxR8p\n\tTAgpgpnOPrY2MvWnyl2QTXnsTM+V/lGqCmV3rAPJpFgbp0vey57G+sTmsJPYv7kMXyhsBcELj+A\n\tNQOUO2Glx8MCHqBGq6shoyn8PQiIumIxCMPst7L6HiRpLbY3HmLTJp+bFKYvRI0Fc7EFABmQxtc\n\tMtJx7xXIrtkUoDs+Q6aQlcg7sH5YBM+iLC539Ot/Fnio5A2F8VLCf6didyvdRg7u/GtK+NF/8j0\n\thFUtp87gTOXFcbmmqwprn/QuKIOb/7B2/zt1KPl/0sEiaY9VAFpo0Je9tkgjuh7m4=", "X-Received": "by 2002:a17:907:7b85:b0:b88:2848:dec8 with SMTP id\n\ta640c23a62f3a-b8d2e8330ddmr324052666b.54.1769424187157; \n\tMon, 26 Jan 2026 02:43:07 -0800 (PST)", "From": "Rick ten Wolde <rick.w.ten.wolde@gmail.com>", "To": "libcamera-devel@lists.libcamera.org", "Cc": "xander.c.pronk@gmail.com, derekgielen@outlook.com,\n\t22012540@student.hhs.nl, \n\trick.w.ten.wolde@gmail.com, johannes.goede@oss.qualcomm.com,\n\tAron Dosti <aron-dosti@hotmail.com>", "Subject": "[PATCH 6/7] utils/tuning: Add LSC scripts", "Date": "Mon, 26 Jan 2026 11:42:54 +0100", "Message-ID": "<20260126104256.119697-7-rick.w.ten.wolde@gmail.com>", "X-Mailer": "git-send-email 2.51.0", "In-Reply-To": "<20260126104256.119697-1-rick.w.ten.wolde@gmail.com>", "References": "<20260126104256.119697-1-rick.w.ten.wolde@gmail.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "X-Mailman-Approved-At": "Mon, 26 Jan 2026 11:48:05 +0100", "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-authored-by: Aron Dosti <aron-dosti@hotmail.com>\nSigned-off-by: Aron Dosti <aron-dosti@hotmail.com>\nSigned-off-by: Derek Gielen <derekgielen@outlook.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 00000000..4f07aa1e\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 00000000..67b3a041\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": [ "6/7" ] }