Patch Detail
Show a patch.
GET /api/1.1/patches/17023/?format=api
{ "id": 17023, "url": "https://patchwork.libcamera.org/api/1.1/patches/17023/?format=api", "web_url": "https://patchwork.libcamera.org/patch/17023/", "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": "<20220808031449.19434-1-laurent.pinchart@ideasonboard.com>", "date": "2022-08-08T03:14:49", "name": "[libcamera-devel] utils: rkisp1: Add script to generate CSC coefficients", "commit_ref": null, "pull_url": null, "state": "accepted", "archived": false, "hash": "7c34636971d838bc814dc7a43c84ee307b8b05b3", "submitter": { "id": 2, "url": "https://patchwork.libcamera.org/api/1.1/people/2/?format=api", "name": "Laurent Pinchart", "email": "laurent.pinchart@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/17023/mbox/", "series": [ { "id": 3386, "url": "https://patchwork.libcamera.org/api/1.1/series/3386/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3386", "date": "2022-08-08T03:14:49", "name": "[libcamera-devel] utils: rkisp1: Add script to generate CSC coefficients", "version": 1, "mbox": "https://patchwork.libcamera.org/series/3386/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/17023/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/17023/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 B4361BE173\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 8 Aug 2022 03:15:03 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 9602B63327;\n\tMon, 8 Aug 2022 05:15:02 +0200 (CEST)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A1611603EA\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 8 Aug 2022 05:15:00 +0200 (CEST)", "from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id F3DBF49C;\n\tMon, 8 Aug 2022 05:14:59 +0200 (CEST)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1659928502;\n\tbh=bZH1GwzYcXQZ2/gMtAGZlg46C+q4vRIvroZBYhg4xVU=;\n\th=To:Date:Subject:List-Id:List-Unsubscribe:List-Archive:List-Post:\n\tList-Help:List-Subscribe:From:Reply-To:From;\n\tb=Xsf0ehNDzMy629QCO+EnkmydFcFHgF4EkjjNzDVf45RYRvS1d1tRdXT48I2ln3nTj\n\t7tfoSvBbrE+XAFVHjS6UeBGiDWJKsorxCj5vL6C+hhbnveXBTZepukQETjjRP70A6i\n\thPe/FMAkSP5VX2JLjNvUNUSv7JB0Z4IvBVBt1A1/lE0A3IsELKwGRD3s42eJwBPTs1\n\tWd1/tRSnVy4cmc2CVjYJ+tmBsldFhBR/fl9cNl99F2sMnXGwx7JBi4VIZXaVsXr0Wx\n\tBwxOjIuCo9gnukZ1PgWfFZ356cCNva6+w+IdKiVF3+/DjggjyerSRz0lKEBXKCtyG/\n\tIQ23TAMzeyzbQ==", "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1659928500;\n\tbh=bZH1GwzYcXQZ2/gMtAGZlg46C+q4vRIvroZBYhg4xVU=;\n\th=From:To:Cc:Subject:Date:From;\n\tb=mHFErFor4sX1NDVFhLc9PGjVi9Ma4BiKdAyZrfyLorO370+etNlUzEeMR0Tg1ff4T\n\tQaBhCMIZeI64FaDso6G5EY/ftunFUE1P5jRCVftS6XdeqegEIpUQH8+GnmcZUTM3AU\n\tYv6wr4ho0twsUNQ/w9GxWKuT97nV8SosbV965C4k=" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"mHFErFor\"; dkim-atps=neutral", "To": "libcamera-devel@lists.libcamera.org", "Date": "Mon, 8 Aug 2022 06:14:49 +0300", "Message-Id": "<20220808031449.19434-1-laurent.pinchart@ideasonboard.com>", "X-Mailer": "git-send-email 2.35.1", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH] utils: rkisp1: Add script to generate CSC\n\tcoefficients", "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>", "From": "Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>", "Reply-To": "Laurent Pinchart <laurent.pinchart@ideasonboard.com>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "This script generates fixed-point integer coefficients for the YCbCr\nencoding 3x3 matrix. The encoding, quantization and fixed-point\nprecision can be selected through command line arguments.\n\nThe main purpose of the script is to generate coefficient tables to\nextend the rkisp1 driver with support for additional YCbCr encodings,\nbut it may be useful for other purposes as well given that the rounding\nisn't trivial.\n\nThe Rec. 601 full and limited range coefficients have been verified to\nmatch the values currently used by the rkisp1 driver.\n\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\n utils/rkisp1/gen-csc-table.py | 204 ++++++++++++++++++++++++++++++++++\n 1 file changed, 204 insertions(+)\n create mode 100755 utils/rkisp1/gen-csc-table.py", "diff": "diff --git a/utils/rkisp1/gen-csc-table.py b/utils/rkisp1/gen-csc-table.py\nnew file mode 100755\nindex 000000000000..a966a31191ed\n--- /dev/null\n+++ b/utils/rkisp1/gen-csc-table.py\n@@ -0,0 +1,204 @@\n+#!/usr/bin/python3\n+# SPDX-License-Identifier: GPL-2.0-or-later\n+# Copyright (C) 2022, Ideas on Board Oy\n+#\n+# Generate color space conversion table coefficients with configurable\n+# fixed-point precision\n+\n+import argparse\n+import enum\n+import sys\n+\n+\n+encodings = {\n+ 'rec601': [\n+ [ 0.2990, 0.5870, 0.1140 ],\n+ [ -0.1687, -0.3313, 0.5 ],\n+ [ 0.5, -0.4187, -0.0813 ]\n+ ],\n+ 'rec709': [\n+ [ 0.2126, 0.7152, 0.0722 ],\n+ [ -0.1146, -0.3854, 0.5 ],\n+ [ 0.5, -0.4542, -0.0458 ]\n+ ],\n+ 'rec2020': [\n+ [ 0.2627, 0.6780, 0.0593 ],\n+ [ -0.1396, -0.3604, 0.5 ],\n+ [ 0.5, -0.4598, -0.0402 ]\n+ ],\n+ 'smpte240m': [\n+ [ 0.2122, 0.7013, 0.0865 ],\n+ [ -0.1161, -0.3839, 0.5 ],\n+ [ 0.5, -0.4451, -0.0549 ]\n+ ],\n+}\n+\n+\n+class Precision(object):\n+ def __init__(self, precision):\n+ if precision[0].upper() != 'Q':\n+ raise RuntimeError(f'Invalid precision `{precision}`')\n+ prec = precision[1:].split('.')\n+ if len(prec) != 2:\n+ raise RuntimeError(f'Invalid precision `{precision}`')\n+\n+ self.__prec = [int(v) for v in prec]\n+\n+ @property\n+ def integer(self):\n+ return self.__prec[0]\n+\n+ @property\n+ def fractional(self):\n+ return self.__prec[1]\n+\n+ @property\n+ def total(self):\n+ # Add 1 for the sign bit\n+ return self.__prec[0] + self.__prec[1] + 1\n+\n+\n+class Quantization(enum.Enum):\n+ FULL = 0\n+ LIMITED = 1\n+\n+\n+def scale_coeff(coeff, quantization, luma, precision):\n+ \"\"\"Scale a coefficient to the output range dictated by the quantization and\n+ the precision.\n+\n+ Parameters\n+ ----------\n+ coeff : float\n+ The CSC matrix coefficient to scale\n+ quantization : Quantization\n+ The quantization, either FULL or LIMITED\n+ luma : bool\n+ True if the coefficient corresponds to a luma value, False otherwise\n+ precision : int\n+ The desired precision for the scaled coefficient as a number of\n+ fractional bits\n+ \"\"\"\n+\n+ # Assume the input range is 8 bits. The output range is set by the\n+ # quantization and differs between luma and chrome components for limited\n+ # range.\n+ in_range = 255 - 0\n+ if quantization == Quantization.FULL:\n+ out_range = 255 - 0\n+ elif luma:\n+ out_range = 235 - 16\n+ else:\n+ out_range = 240 - 16\n+\n+ return coeff * out_range / in_range * (1 << precision)\n+\n+\n+def round_array(values):\n+ \"\"\"Round a list of signed floating point values to the closest integer while\n+ preserving the (rounded) value of the sum of all elements.\n+ \"\"\"\n+\n+ # Calculate the rounding error as the difference between the rounded sum of\n+ # values and the sum of rounded values. This is by definition an integer\n+ # (positive or negative), which indicates how many values will need to be\n+ # 'flipped' to the opposite rounding.\n+ rounded_values = [round(value) for value in values]\n+ sum_values = round(sum(values))\n+ sum_error = sum_values - sum(rounded_values)\n+\n+ if sum_error == 0:\n+ return rounded_values\n+\n+ # The next step is to distribute the error among the values, in a way that\n+ # will minimize the relative error introduced in individual values. We\n+ # extend the values list with the rounded value and original index for each\n+ # element, and sort by rounding error. Then we modify the elements with the\n+ # highest or lowest error, depending on whether the sum error is negative\n+ # or positive.\n+\n+ values = [[value, round(value), index] for index, value in enumerate(values)]\n+ values.sort(key=lambda v: v[1] - v[0])\n+\n+ # It could also be argued that the key for the sort order should not be the\n+ # absolute rouding error but the relative error, as the impact of identical\n+ # rounding errors will differ for coefficients with widely different values.\n+ # This is a topic for further research.\n+ #\n+ # values.sort(key=lambda v: (v[1] - v[0]) / abs(v[0]))\n+\n+ if sum_error > 0:\n+ for i in range(sum_error):\n+ values[i][1] += 1\n+ else:\n+ for i in range(-sum_error):\n+ values[len(values) - i - 1][1] -= 1\n+\n+ # Finally, sort back by index, make sure the total rounding error is now 0,\n+ # and return the rounded values.\n+ values.sort(key=lambda v: v[2])\n+ values = [value[1] for value in values]\n+ assert(sum(values) == sum_values)\n+\n+ return values\n+\n+\n+def main(argv):\n+\n+ # Parse command line arguments.\n+ parser = argparse.ArgumentParser(\n+ 'Generate color space conversion table coefficients with configurable '\n+ 'fixed-point precision'\n+ )\n+ parser.add_argument('--precision', '-p', default='Q1.7',\n+ help='The output fixed point precision in Q notation')\n+ parser.add_argument('--quantization', '-q', choices=['full', 'limited'],\n+ default='limited', help='Quantization range')\n+ parser.add_argument('encoding', choices=encodings.keys(), help='YCbCr encoding')\n+ args = parser.parse_args(argv[1:])\n+\n+ try:\n+ precision = Precision(args.precision)\n+ except Exception:\n+ print(f'Invalid precision `{args.precision}`')\n+ return 1\n+\n+ encoding = encodings[args.encoding]\n+ quantization = Quantization[args.quantization.upper()]\n+\n+ # Scale and round the encoding coefficients based on the precision and\n+ # quantization range.\n+ luma = True\n+ scaled_coeffs = []\n+ for line in encoding:\n+ line = [scale_coeff(coeff, quantization, luma, precision.fractional) for coeff in line]\n+ scaled_coeffs.append(line)\n+ luma = False\n+\n+ rounded_coeffs = []\n+ for line in scaled_coeffs:\n+ line = round_array(line)\n+\n+ # Convert coefficients to the number of bits selected by the precision.\n+ # Negative values will be turned into positive integers using 2's\n+ # complement.\n+ line = [coeff & ((1 << precision.total) - 1) for coeff in line]\n+ rounded_coeffs.append(line)\n+\n+ # Print the result as C code.\n+ nbits = 1 << (precision.total - 1).bit_length()\n+ nbytes = nbits // 4\n+ print(f'static const u{nbits} rgb2yuv_{args.encoding}_{quantization.name.lower()}_coeffs[] = {{')\n+\n+ for line in rounded_coeffs:\n+ line = [f'0x{coeff:0{nbytes}x}' for coeff in line]\n+\n+ print(f'\\t{\", \".join(line)},')\n+\n+ print('};')\n+\n+ return 0\n+\n+\n+if __name__ == '__main__':\n+ sys.exit(main(sys.argv))\n", "prefixes": [ "libcamera-devel" ] }