utils: raspberrypi: ctt: Output version 2.0 format tuning files
Update the ctt_pretty_print_json.py script to generate the new version 2.0 format camera tuning file. This script can be called through the command line to prettify an existing JSON file, or programatically by the CTT to format a new JSON config dictionary. Update the CTT to produce a version 2.0 format json structure and use ctt_pretty_print_json.pretty_print to prettify the output. Signed-off-by: Naushir Patuck <naush@raspberrypi.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Tested-by: Naushir Patuck <naush@raspberrypi.com>
This commit is contained in:
parent
2ef6eafb6f
commit
73c1a1d697
2 changed files with 111 additions and 97 deletions
|
@ -15,7 +15,7 @@ from ctt_alsc import *
|
||||||
from ctt_lux import *
|
from ctt_lux import *
|
||||||
from ctt_noise import *
|
from ctt_noise import *
|
||||||
from ctt_geq import *
|
from ctt_geq import *
|
||||||
from ctt_pretty_print_json import *
|
from ctt_pretty_print_json import pretty_print
|
||||||
import random
|
import random
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
@ -511,13 +511,17 @@ class Camera:
|
||||||
"""
|
"""
|
||||||
def write_json(self):
|
def write_json(self):
|
||||||
"""
|
"""
|
||||||
Write json dictionary to file
|
Write json dictionary to file using our version 2 format
|
||||||
"""
|
"""
|
||||||
jstring = json.dumps(self.json, sort_keys=False)
|
|
||||||
"""
|
out_json = {
|
||||||
make it pretty :)
|
"version": 2.0,
|
||||||
"""
|
'target': 'bcm2835',
|
||||||
pretty_print_json(jstring, self.jf)
|
"algorithms": [{name: data} for name, data in self.json.items()],
|
||||||
|
}
|
||||||
|
|
||||||
|
with open(self.jf, 'w') as f:
|
||||||
|
f.write(pretty_print(out_json))
|
||||||
|
|
||||||
"""
|
"""
|
||||||
add a new section to the log file
|
add a new section to the log file
|
||||||
|
|
190
utils/raspberrypi/ctt/ctt_pretty_print_json.py
Normal file → Executable file
190
utils/raspberrypi/ctt/ctt_pretty_print_json.py
Normal file → Executable file
|
@ -1,106 +1,116 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019, Raspberry Pi Ltd
|
# Copyright 2022 Raspberry Pi Ltd
|
||||||
#
|
#
|
||||||
# ctt_pretty_print_json.py - camera tuning tool JSON formatter
|
# Script to pretty print a Raspberry Pi tuning config JSON structure in
|
||||||
|
# version 2.0 and later formats.
|
||||||
|
|
||||||
import sys
|
import argparse
|
||||||
|
import json
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
|
||||||
class JSONPrettyPrinter(object):
|
class Encoder(json.JSONEncoder):
|
||||||
"""
|
|
||||||
Take a collapsed JSON file and make it more readable
|
def __init__(self, *args, **kwargs):
|
||||||
"""
|
super().__init__(*args, **kwargs)
|
||||||
def __init__(self, fout):
|
self.indentation_level = 0
|
||||||
self.state = {
|
self.hard_break = 120
|
||||||
"indent": 0,
|
self.custom_elems = {
|
||||||
"inarray": [False],
|
'table': 16,
|
||||||
"arraycount": [],
|
'luminance_lut': 16,
|
||||||
"skipnewline": True,
|
'ct_curve': 3,
|
||||||
"need_indent": False,
|
'ccm': 3,
|
||||||
"need_space": False,
|
'gamma_curve': 2,
|
||||||
|
'y_target': 2,
|
||||||
|
'prior': 2
|
||||||
}
|
}
|
||||||
|
|
||||||
self.fout = fout
|
def encode(self, o, node_key=None):
|
||||||
|
if isinstance(o, (list, tuple)):
|
||||||
def newline(self):
|
# Check if we are a flat list of numbers.
|
||||||
if not self.state["skipnewline"]:
|
if not any(isinstance(el, (list, tuple, dict)) for el in o):
|
||||||
self.fout.write('\n')
|
s = ', '.join(json.dumps(el) for el in o)
|
||||||
self.state["need_indent"] = True
|
if node_key in self.custom_elems.keys():
|
||||||
self.state["need_space"] = False
|
# Special case handling to specify number of elements in a row for tables, ccm, etc.
|
||||||
self.state["skipnewline"] = True
|
self.indentation_level += 1
|
||||||
|
sl = s.split(', ')
|
||||||
def write(self, c):
|
num = self.custom_elems[node_key]
|
||||||
if self.state["need_indent"]:
|
chunk = [self.indent_str + ', '.join(sl[x:x + num]) for x in range(0, len(sl), num)]
|
||||||
self.fout.write(' ' * self.state["indent"] * 4)
|
t = ',\n'.join(chunk)
|
||||||
self.state["need_indent"] = False
|
self.indentation_level -= 1
|
||||||
if self.state["need_space"]:
|
output = f'\n{self.indent_str}[\n{t}\n{self.indent_str}]'
|
||||||
self.fout.write(' ')
|
elif len(s) > self.hard_break - len(self.indent_str):
|
||||||
self.state["need_space"] = False
|
# Break a long list with wraps.
|
||||||
self.fout.write(c)
|
self.indentation_level += 1
|
||||||
self.state["skipnewline"] = False
|
t = textwrap.fill(s, self.hard_break, break_long_words=False,
|
||||||
|
initial_indent=self.indent_str, subsequent_indent=self.indent_str)
|
||||||
def process_char(self, c):
|
self.indentation_level -= 1
|
||||||
if c == '{':
|
output = f'\n{self.indent_str}[\n{t}\n{self.indent_str}]'
|
||||||
self.newline()
|
|
||||||
self.write(c)
|
|
||||||
self.state["indent"] += 1
|
|
||||||
self.newline()
|
|
||||||
elif c == '}':
|
|
||||||
self.state["indent"] -= 1
|
|
||||||
self.newline()
|
|
||||||
self.write(c)
|
|
||||||
elif c == '[':
|
|
||||||
self.newline()
|
|
||||||
self.write(c)
|
|
||||||
self.state["indent"] += 1
|
|
||||||
self.newline()
|
|
||||||
self.state["inarray"] = [True] + self.state["inarray"]
|
|
||||||
self.state["arraycount"] = [0] + self.state["arraycount"]
|
|
||||||
elif c == ']':
|
|
||||||
self.state["indent"] -= 1
|
|
||||||
self.newline()
|
|
||||||
self.state["inarray"].pop(0)
|
|
||||||
self.state["arraycount"].pop(0)
|
|
||||||
self.write(c)
|
|
||||||
elif c == ':':
|
|
||||||
self.write(c)
|
|
||||||
self.state["need_space"] = True
|
|
||||||
elif c == ',':
|
|
||||||
if not self.state["inarray"][0]:
|
|
||||||
self.write(c)
|
|
||||||
self.newline()
|
|
||||||
else:
|
|
||||||
self.write(c)
|
|
||||||
self.state["arraycount"][0] += 1
|
|
||||||
if self.state["arraycount"][0] == 16:
|
|
||||||
self.state["arraycount"][0] = 0
|
|
||||||
self.newline()
|
|
||||||
else:
|
else:
|
||||||
self.state["need_space"] = True
|
# Smaller lists can remain on a single line.
|
||||||
elif c.isspace():
|
output = f' [ {s} ]'
|
||||||
pass
|
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:
|
else:
|
||||||
self.write(c)
|
return ' ' + json.dumps(o)
|
||||||
|
|
||||||
def print(self, string):
|
@property
|
||||||
for c in string:
|
def indent_str(self) -> str:
|
||||||
self.process_char(c)
|
return ' ' * self.indentation_level * self.indent
|
||||||
self.newline()
|
|
||||||
|
def iterencode(self, o, **kwargs):
|
||||||
|
return self.encode(o)
|
||||||
|
|
||||||
|
|
||||||
def pretty_print_json(str_in, output_filename):
|
def pretty_print(in_json: dict) -> str:
|
||||||
with open(output_filename, "w") as fout:
|
|
||||||
printer = JSONPrettyPrinter(fout)
|
if 'version' not in in_json or \
|
||||||
printer.print(str_in)
|
'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)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
if len(sys.argv) != 2:
|
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, description=
|
||||||
print("Usage: %s filename" % sys.argv[0])
|
'Prettify a version 2.0 camera tuning config JSON file.')
|
||||||
sys.exit(1)
|
parser.add_argument('input', type=str, help='Input tuning file.')
|
||||||
|
parser.add_argument('output', type=str, nargs='?',
|
||||||
|
help='Output converted tuning file. If not provided, the input file will be updated in-place.',
|
||||||
|
default=None)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
input_filename = sys.argv[1]
|
with open(args.input, 'r') as f:
|
||||||
with open(input_filename, "r") as fin:
|
in_json = json.load(f)
|
||||||
printer = JSONPrettyPrinter(sys.stdout)
|
|
||||||
printer.print(fin.read())
|
out_json = pretty_print(in_json)
|
||||||
|
|
||||||
|
with open(args.output if args.output is not None else args.input, 'w') as f:
|
||||||
|
f.write(out_json)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue