From patchwork Thu Nov 24 11:35:46 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 17874 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 0F4CFC3286 for ; Thu, 24 Nov 2022 11:36:18 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B888C63329; Thu, 24 Nov 2022 12:36:17 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1669289777; bh=Y7nEBUYH5b4W9avHl2TdJIL1TOaNPAQgLWaKihtoTmY=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=ab5gvRuu45GvV68r4vHXw/bYUJM184WeH6DYkbctkdqRFeNfmuqiUlSsNucl8peyc KqQTlZfB/DgqGOpMC6+W5G1VliZfVaEyJfMT9F1iigRDJdx9NstA376JJqGwBV3My7 V8BaMCmNN2pI658krDBLzYJiy0mAMHvQoK9tzDHhUNs6h6qV6m8UkeScW+pFDwqAuh mNLwIRePegeyPnZuP7jANzMz3/tFoCrFBX03CHEERcBX/H3GyRiKrPQq3Z647wQI/G 2GRWYi03qSUTJl323CmAIYo+SATwE8shb8h+0Xf7IG54Yw4ci/gpPaYQiG/Qw2AWPL Z8RDU/mrANbBg== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id AE01863319 for ; Thu, 24 Nov 2022 12:36:16 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="iVeUAOGY"; dkim-atps=neutral Received: from pyrite.tail37cf.ts.net (h175-177-042-159.catv02.itscom.jp [175.177.42.159]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 398A0496; Thu, 24 Nov 2022 12:36:14 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1669289776; bh=Y7nEBUYH5b4W9avHl2TdJIL1TOaNPAQgLWaKihtoTmY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=iVeUAOGYqEzve9MYV9jz+FEfzNRTaphfKy7x476Ustsv9jgOvruQHry4Cnamo5FwS LC+Xu9mr8gnLpVDqjs8pYTyYeg1Rd+0qb5jrQxS5nEZml2a0q5akqvDEAJcJkv6I+y K1PjgUzNoIh9nG9IwDz3jZWnXguankTPSSC5s0tw= To: libcamera-devel@lists.libcamera.org Date: Thu, 24 Nov 2022 20:35:46 +0900 Message-Id: <20221124113550.2182342-9-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20221124113550.2182342-1-paul.elder@ideasonboard.com> References: <20221124113550.2182342-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 08/12] utils: libtuning: generators: Add raspberrypi output 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: , X-Patchwork-Original-From: Paul Elder via libcamera-devel From: Paul Elder Reply-To: Paul Elder Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a generator to libtuning for writing tuning output to a json file formatted the same way that raspberrypi's ctt formats them. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- Changes in v4: - move adding the license to generators __init__.py to the appropriate patch earlier in the series Changes in v3: - move pretty_print into a private class member - move RaspberryPiOutput to end of file - add file descriptions - remove indirection from fake polymorphism Changes in v2: - add SPDX and copyright - fix style - move the 'rpi.' prefix of module names in the tuning output from here to into the Modules' out_name field --- utils/tuning/libtuning/generators/__init__.py | 2 + .../generators/raspberrypi_output.py | 114 ++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 utils/tuning/libtuning/generators/raspberrypi_output.py diff --git a/utils/tuning/libtuning/generators/__init__.py b/utils/tuning/libtuning/generators/__init__.py index 9ccabb0e..937aff30 100644 --- a/utils/tuning/libtuning/generators/__init__.py +++ b/utils/tuning/libtuning/generators/__init__.py @@ -1,3 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later # # Copyright (C) 2022, Paul Elder + +from libtuning.generators.raspberrypi_output import RaspberryPiOutput diff --git a/utils/tuning/libtuning/generators/raspberrypi_output.py b/utils/tuning/libtuning/generators/raspberrypi_output.py new file mode 100644 index 00000000..813491cd --- /dev/null +++ b/utils/tuning/libtuning/generators/raspberrypi_output.py @@ -0,0 +1,114 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright 2022 Raspberry Pi Ltd +# +# raspberrypi_output.py - Generate tuning file in Raspberry Pi's json format +# +# (Copied from ctt_pretty_print_json.py) + +from .generator import Generator + +import json +from pathlib import Path +import textwrap + + +class Encoder(json.JSONEncoder): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.indentation_level = 0 + self.hard_break = 120 + self.custom_elems = { + 'table': 16, + 'luminance_lut': 16, + 'ct_curve': 3, + 'ccm': 3, + 'gamma_curve': 2, + 'y_target': 2, + 'prior': 2 + } + + def encode(self, o, node_key=None): + if isinstance(o, (list, tuple)): + # Check if we are a flat list of numbers. + if not any(isinstance(el, (list, tuple, dict)) for el in o): + s = ', '.join(json.dumps(el) for el in o) + if node_key in self.custom_elems.keys(): + # Special case handling to specify number of elements in a row for tables, ccm, etc. + self.indentation_level += 1 + sl = s.split(', ') + num = self.custom_elems[node_key] + chunk = [self.indent_str + ', '.join(sl[x:x + num]) for x in range(0, len(sl), num)] + t = ',\n'.join(chunk) + self.indentation_level -= 1 + output = f'\n{self.indent_str}[\n{t}\n{self.indent_str}]' + elif len(s) > self.hard_break - len(self.indent_str): + # Break a long list with wraps. + self.indentation_level += 1 + t = textwrap.fill(s, self.hard_break, break_long_words=False, + initial_indent=self.indent_str, subsequent_indent=self.indent_str) + self.indentation_level -= 1 + output = f'\n{self.indent_str}[\n{t}\n{self.indent_str}]' + else: + # Smaller lists can remain on a single line. + output = f' [ {s} ]' + return output + else: + # Sub-structures in the list case. + self.indentation_level += 1 + output = [self.indent_str + self.encode(el) for el in o] + self.indentation_level -= 1 + output = ',\n'.join(output) + return f' [\n{output}\n{self.indent_str}]' + + elif isinstance(o, dict): + self.indentation_level += 1 + output = [] + for k, v in o.items(): + if isinstance(v, dict) and len(v) == 0: + # Empty config block special case. + output.append(self.indent_str + f'{json.dumps(k)}: {{ }}') + else: + # Only linebreak if the next node is a config block. + sep = f'\n{self.indent_str}' if isinstance(v, dict) else '' + output.append(self.indent_str + f'{json.dumps(k)}:{sep}{self.encode(v, k)}') + output = ',\n'.join(output) + self.indentation_level -= 1 + return f'{{\n{output}\n{self.indent_str}}}' + + else: + return ' ' + json.dumps(o) + + @property + def indent_str(self) -> str: + return ' ' * self.indentation_level * self.indent + + def iterencode(self, o, **kwargs): + return self.encode(o) + + +class RaspberryPiOutput(Generator): + def __init__(self): + super().__init__() + + def _pretty_print(self, in_json: dict) -> str: + + if 'version' not in in_json or \ + 'target' not in in_json or \ + 'algorithms' not in in_json or \ + in_json['version'] < 2.0: + raise RuntimeError('Incompatible JSON dictionary has been provided') + + return json.dumps(in_json, cls=Encoder, indent=4, sort_keys=False) + + def write(self, output_file: Path, output_dict: dict, output_order: list): + # Write json dictionary to file using ctt's version 2 format + out_json = { + "version": 2.0, + 'target': 'bcm2835', + "algorithms": [{f'{module.out_name}': output_dict[module]} for module in output_order] + } + + with open(output_file, 'w') as f: + f.write(self._pretty_print(out_json))