[libcamera-devel] [PATCH v2 06/11] utils: libtuning: generators: Add raspberrypi output
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Wed Nov 9 11:34:25 CET 2022
Hi Paul,
Thank you for the patch.
On Sat, Oct 22, 2022 at 03:23:05PM +0900, Paul Elder via libcamera-devel wrote:
> 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 <paul.elder at ideasonboard.com>
>
> ---
> 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 | 5 +
> .../generators/raspberrypi_output.py | 115 ++++++++++++++++++
> 2 files changed, 120 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 e69de29b..937aff30 100644
> --- a/utils/tuning/libtuning/generators/__init__.py
> +++ b/utils/tuning/libtuning/generators/__init__.py
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +#
> +# Copyright (C) 2022, Paul Elder <paul.elder at ideasonboard.com>
> +
> +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..e06aeddf
> --- /dev/null
> +++ b/utils/tuning/libtuning/generators/raspberrypi_output.py
> @@ -0,0 +1,115 @@
> +# SPDX-License-Identifier: BSD-2-Clause
> +#
> +# Copyright 2022 Raspberry Pi Ltd
> +#
> +# Script to pretty print a Raspberry Pi tuning config JSON structure in
> +# version 2.0 and later formats.
> +# (Copied from ctt_pretty_print_json.py)
> +
> +from .generator import Generator
> +
> +import json
> +from pathlib import Path
> +import textwrap
> +
> +
> +class RaspberryPiOutput(Generator):
> + def __init__(self):
> + super().__init__()
> +
> + 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(pretty_print(out_json))
Please move this class to the end of the file. I would also possibly
make the pretty_print function a (private) class member.
Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> +
> +
> +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)
> +
> +
> +def pretty_print(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)
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list