Message ID | 20240606101512.375178-6-david.plowman@raspberrypi.com |
---|---|
State | Accepted |
Headers | show |
Series |
|
Related | show |
Quoting David Plowman (2024-06-06 11:15:11) > From: Naushir Patuck <naush@raspberrypi.com> > > This change adds functionality to the convert_tuning.py script to > convert between vc4 and pisp target tuning files. The conversion is > done on a best effort basis, and should provide functional tuning files. > However, a full tuning for the target platform is always preferred. > > Signed-off-by: Naushir Patuck <naush@raspberrypi.com> Hi David, Can I assume you have supplied your SoB and RB tag to this one already? -- Kieran > --- > utils/raspberrypi/ctt/convert_tuning.py | 98 ++++++++++++++++++++++--- > 1 file changed, 86 insertions(+), 12 deletions(-) > > diff --git a/utils/raspberrypi/ctt/convert_tuning.py b/utils/raspberrypi/ctt/convert_tuning.py > index f4504d45..83cf69d4 100755 > --- a/utils/raspberrypi/ctt/convert_tuning.py > +++ b/utils/raspberrypi/ctt/convert_tuning.py > @@ -8,30 +8,104 @@ > > import argparse > import json > +import numpy as np > import sys > > from ctt_pretty_print_json import pretty_print > +from ctt_pisp import grid_size as grid_size_pisp > +from ctt_pisp import json_template as json_template_pisp > +from ctt_vc4 import grid_size as grid_size_vc4 > +from ctt_vc4 import json_template as json_template_vc4 > > > -def convert_v2(in_json: dict) -> str: > +def interp_2d(in_ls, src_w, src_h, dst_w, dst_h): > > - if 'version' in in_json.keys() and in_json['version'] != 1.0: > - print(f'The JSON config reports version {in_json["version"]} that is incompatible with this tool.') > - sys.exit(-1) > + out_ls = np.zeros((dst_h, dst_w)) > + for i in range(src_h): > + out_ls[i] = np.interp(np.linspace(0, dst_w - 1, dst_w), > + np.linspace(0, dst_w - 1, src_w), > + in_ls[i]) > + for i in range(dst_w): > + out_ls[:,i] = np.interp(np.linspace(0, dst_h - 1, dst_h), > + np.linspace(0, dst_h - 1, src_h), > + out_ls[:src_h, i]) > + return out_ls > > - converted = { > - 'version': 2.0, > - 'target': 'bcm2835', > - 'algorithms': [{algo: config} for algo, config in in_json.items()] > - } > > - return pretty_print(converted) > +def convert_target(in_json: dict, target: str): > + > + src_w, src_h = grid_size_pisp if target == 'vc4' else grid_size_vc4 > + dst_w, dst_h = grid_size_vc4 if target == 'vc4' else grid_size_pisp > + json_template = json_template_vc4 if target == 'vc4' else json_template_pisp > + > + # ALSC grid sizes > + alsc = next(algo for algo in in_json['algorithms'] if 'rpi.alsc' in algo)['rpi.alsc'] > + for colour in ['calibrations_Cr', 'calibrations_Cb']: > + if colour not in alsc: > + continue > + for temperature in alsc[colour]: > + in_ls = np.reshape(temperature['table'], (src_h, src_w)) > + out_ls = interp_2d(in_ls, src_w, src_h, dst_w, dst_h) > + temperature['table'] = np.round(out_ls.flatten(), 3).tolist() > + > + if 'luminance_lut' in alsc: > + in_ls = np.reshape(alsc['luminance_lut'], (src_h, src_w)) > + out_ls = interp_2d(in_ls, src_w, src_h, dst_w, dst_h) > + alsc['luminance_lut'] = np.round(out_ls.flatten(), 3).tolist() > + > + # Denoise blocks > + for i, algo in enumerate(in_json['algorithms']): > + if list(algo.keys())[0] == 'rpi.sdn': > + in_json['algorithms'][i] = {'rpi.denoise': json_template['rpi.sdn'] if target == 'vc4' else json_template['rpi.denoise']} > + break > + > + # AGC mode weights > + agc = next(algo for algo in in_json['algorithms'] if 'rpi.agc' in algo)['rpi.agc'] > + if 'channels' in agc: > + for i, channel in enumerate(agc['channels']): > + target_agc_metering = json_template['rpi.agc']['channels'][i]['metering_modes'] > + for mode, v in channel['metering_modes'].items(): > + v['weights'] = target_agc_metering[mode]['weights'] > + else: > + for mode, v in agc["metering_modes"].items(): > + target_agc_metering = json_template['rpi.agc']['channels'][0]['metering_modes'] > + v['weights'] = target_agc_metering[mode]['weights'] > + > + # HDR > + if target == 'pisp': > + for i, algo in enumerate(in_json['algorithms']): > + if list(algo.keys())[0] == 'rpi.hdr': > + in_json['algorithms'][i] = {'rpi.hdr': json_template['rpi.hdr']} > + > + return in_json > + > + > +def convert_v2(in_json: dict, target: str) -> str: > + > + if 'version' in in_json.keys() and in_json['version'] == 1.0: > + converted = { > + 'version': 2.0, > + 'target': target, > + 'algorithms': [{algo: config} for algo, config in in_json.items()] > + } > + else: > + converted = in_json > + > + # Convert between vc4 <-> pisp targets. This is a best effort thing. > + if converted['target'] != target: > + converted = convert_target(converted, target) > + converted['target'] = target > + > + grid_size = grid_size_vc4[0] if target == 'vc4' else grid_size_pisp[0] > + return pretty_print(converted, custom_elems={'table': grid_size, 'luminance_lut': grid_size}) > > > if __name__ == "__main__": > parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, description= > - 'Convert the format of the Raspberry Pi camera tuning file from v1.0 to v2.0.\n') > + 'Convert the format of the Raspberry Pi camera tuning file from v1.0 to v2.0 and/or the vc4 <-> pisp targets.\n') > parser.add_argument('input', type=str, help='Input tuning file.') > + parser.add_argument('-t', '--target', type=str, help='Target platform.', > + choices=['pisp', 'vc4'], default='vc4') > 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) > @@ -40,7 +114,7 @@ if __name__ == "__main__": > with open(args.input, 'r') as f: > in_json = json.load(f) > > - out_json = convert_v2(in_json) > + out_json = convert_v2(in_json, args.target) > > with open(args.output if args.output is not None else args.input, 'w') as f: > f.write(out_json) > -- > 2.39.2 >
diff --git a/utils/raspberrypi/ctt/convert_tuning.py b/utils/raspberrypi/ctt/convert_tuning.py index f4504d45..83cf69d4 100755 --- a/utils/raspberrypi/ctt/convert_tuning.py +++ b/utils/raspberrypi/ctt/convert_tuning.py @@ -8,30 +8,104 @@ import argparse import json +import numpy as np import sys from ctt_pretty_print_json import pretty_print +from ctt_pisp import grid_size as grid_size_pisp +from ctt_pisp import json_template as json_template_pisp +from ctt_vc4 import grid_size as grid_size_vc4 +from ctt_vc4 import json_template as json_template_vc4 -def convert_v2(in_json: dict) -> str: +def interp_2d(in_ls, src_w, src_h, dst_w, dst_h): - if 'version' in in_json.keys() and in_json['version'] != 1.0: - print(f'The JSON config reports version {in_json["version"]} that is incompatible with this tool.') - sys.exit(-1) + out_ls = np.zeros((dst_h, dst_w)) + for i in range(src_h): + out_ls[i] = np.interp(np.linspace(0, dst_w - 1, dst_w), + np.linspace(0, dst_w - 1, src_w), + in_ls[i]) + for i in range(dst_w): + out_ls[:,i] = np.interp(np.linspace(0, dst_h - 1, dst_h), + np.linspace(0, dst_h - 1, src_h), + out_ls[:src_h, i]) + return out_ls - converted = { - 'version': 2.0, - 'target': 'bcm2835', - 'algorithms': [{algo: config} for algo, config in in_json.items()] - } - return pretty_print(converted) +def convert_target(in_json: dict, target: str): + + src_w, src_h = grid_size_pisp if target == 'vc4' else grid_size_vc4 + dst_w, dst_h = grid_size_vc4 if target == 'vc4' else grid_size_pisp + json_template = json_template_vc4 if target == 'vc4' else json_template_pisp + + # ALSC grid sizes + alsc = next(algo for algo in in_json['algorithms'] if 'rpi.alsc' in algo)['rpi.alsc'] + for colour in ['calibrations_Cr', 'calibrations_Cb']: + if colour not in alsc: + continue + for temperature in alsc[colour]: + in_ls = np.reshape(temperature['table'], (src_h, src_w)) + out_ls = interp_2d(in_ls, src_w, src_h, dst_w, dst_h) + temperature['table'] = np.round(out_ls.flatten(), 3).tolist() + + if 'luminance_lut' in alsc: + in_ls = np.reshape(alsc['luminance_lut'], (src_h, src_w)) + out_ls = interp_2d(in_ls, src_w, src_h, dst_w, dst_h) + alsc['luminance_lut'] = np.round(out_ls.flatten(), 3).tolist() + + # Denoise blocks + for i, algo in enumerate(in_json['algorithms']): + if list(algo.keys())[0] == 'rpi.sdn': + in_json['algorithms'][i] = {'rpi.denoise': json_template['rpi.sdn'] if target == 'vc4' else json_template['rpi.denoise']} + break + + # AGC mode weights + agc = next(algo for algo in in_json['algorithms'] if 'rpi.agc' in algo)['rpi.agc'] + if 'channels' in agc: + for i, channel in enumerate(agc['channels']): + target_agc_metering = json_template['rpi.agc']['channels'][i]['metering_modes'] + for mode, v in channel['metering_modes'].items(): + v['weights'] = target_agc_metering[mode]['weights'] + else: + for mode, v in agc["metering_modes"].items(): + target_agc_metering = json_template['rpi.agc']['channels'][0]['metering_modes'] + v['weights'] = target_agc_metering[mode]['weights'] + + # HDR + if target == 'pisp': + for i, algo in enumerate(in_json['algorithms']): + if list(algo.keys())[0] == 'rpi.hdr': + in_json['algorithms'][i] = {'rpi.hdr': json_template['rpi.hdr']} + + return in_json + + +def convert_v2(in_json: dict, target: str) -> str: + + if 'version' in in_json.keys() and in_json['version'] == 1.0: + converted = { + 'version': 2.0, + 'target': target, + 'algorithms': [{algo: config} for algo, config in in_json.items()] + } + else: + converted = in_json + + # Convert between vc4 <-> pisp targets. This is a best effort thing. + if converted['target'] != target: + converted = convert_target(converted, target) + converted['target'] = target + + grid_size = grid_size_vc4[0] if target == 'vc4' else grid_size_pisp[0] + return pretty_print(converted, custom_elems={'table': grid_size, 'luminance_lut': grid_size}) if __name__ == "__main__": parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, description= - 'Convert the format of the Raspberry Pi camera tuning file from v1.0 to v2.0.\n') + 'Convert the format of the Raspberry Pi camera tuning file from v1.0 to v2.0 and/or the vc4 <-> pisp targets.\n') parser.add_argument('input', type=str, help='Input tuning file.') + parser.add_argument('-t', '--target', type=str, help='Target platform.', + choices=['pisp', 'vc4'], default='vc4') 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) @@ -40,7 +114,7 @@ if __name__ == "__main__": with open(args.input, 'r') as f: in_json = json.load(f) - out_json = convert_v2(in_json) + out_json = convert_v2(in_json, args.target) with open(args.output if args.output is not None else args.input, 'w') as f: f.write(out_json)