Skip to content

Commit 885f05e

Browse files
committed
utils: raspberrypi: ctt: Add option to convert between vc4/pisp targets
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 <[email protected]>
1 parent 51296a8 commit 885f05e

File tree

1 file changed

+86
-12
lines changed

1 file changed

+86
-12
lines changed

utils/raspberrypi/ctt/convert_tuning.py

Lines changed: 86 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,104 @@
88

99
import argparse
1010
import json
11+
import numpy as np
1112
import sys
1213

1314
from ctt_pretty_print_json import pretty_print
15+
from ctt_pisp import grid_size as grid_size_pisp
16+
from ctt_pisp import json_template as json_template_pisp
17+
from ctt_vc4 import grid_size as grid_size_vc4
18+
from ctt_vc4 import json_template as json_template_vc4
1419

1520

16-
def convert_v2(in_json: dict) -> str:
21+
def interp_2d(in_ls, src_w, src_h, dst_w, dst_h):
1722

18-
if 'version' in in_json.keys() and in_json['version'] != 1.0:
19-
print(f'The JSON config reports version {in_json["version"]} that is incompatible with this tool.')
20-
sys.exit(-1)
23+
out_ls = np.zeros((dst_h, dst_w))
24+
for i in range(src_h):
25+
out_ls[i] = np.interp(np.linspace(0, dst_w - 1, dst_w),
26+
np.linspace(0, dst_w - 1, src_w),
27+
in_ls[i])
28+
for i in range(dst_w):
29+
out_ls[:,i] = np.interp(np.linspace(0, dst_h - 1, dst_h),
30+
np.linspace(0, dst_h - 1, src_h),
31+
out_ls[:src_h, i])
32+
return out_ls
2133

22-
converted = {
23-
'version': 2.0,
24-
'target': 'bcm2835',
25-
'algorithms': [{algo: config} for algo, config in in_json.items()]
26-
}
2734

28-
return pretty_print(converted)
35+
def convert_target(in_json: dict, target: str):
36+
37+
src_w, src_h = grid_size_pisp if target == 'vc4' else grid_size_vc4
38+
dst_w, dst_h = grid_size_vc4 if target == 'vc4' else grid_size_pisp
39+
json_template = json_template_vc4 if target == 'vc4' else json_template_pisp
40+
41+
# ALSC grid sizes
42+
alsc = next(algo for algo in in_json['algorithms'] if 'rpi.alsc' in algo)['rpi.alsc']
43+
for colour in ['calibrations_Cr', 'calibrations_Cb']:
44+
if colour not in alsc:
45+
continue
46+
for temperature in alsc[colour]:
47+
in_ls = np.reshape(temperature['table'], (src_h, src_w))
48+
out_ls = interp_2d(in_ls, src_w, src_h, dst_w, dst_h)
49+
temperature['table'] = np.round(out_ls.flatten(), 3).tolist()
50+
51+
if 'luminance_lut' in alsc:
52+
in_ls = np.reshape(alsc['luminance_lut'], (src_h, src_w))
53+
out_ls = interp_2d(in_ls, src_w, src_h, dst_w, dst_h)
54+
alsc['luminance_lut'] = np.round(out_ls.flatten(), 3).tolist()
55+
56+
# Denoise blocks
57+
for i, algo in enumerate(in_json['algorithms']):
58+
if list(algo.keys())[0] == 'rpi.sdn':
59+
in_json['algorithms'][i] = {'rpi.denoise': json_template['rpi.sdn'] if target == 'vc4' else json_template['rpi.denoise']}
60+
break
61+
62+
# AGC mode weights
63+
agc = next(algo for algo in in_json['algorithms'] if 'rpi.agc' in algo)['rpi.agc']
64+
if 'channels' in agc:
65+
for i, channel in enumerate(agc['channels']):
66+
target_agc_metering = json_template['rpi.agc']['channels'][i]['metering_modes']
67+
for mode, v in channel['metering_modes'].items():
68+
v['weights'] = target_agc_metering[mode]['weights']
69+
else:
70+
for mode, v in agc["metering_modes"].items():
71+
target_agc_metering = json_template['rpi.agc']['channels'][0]['metering_modes']
72+
v['weights'] = target_agc_metering[mode]['weights']
73+
74+
# HDR
75+
if target == 'pisp':
76+
for i, algo in enumerate(in_json['algorithms']):
77+
if list(algo.keys())[0] == 'rpi.hdr':
78+
in_json['algorithms'][i] = {'rpi.hdr': json_template['rpi.hdr']}
79+
80+
return in_json
81+
82+
83+
def convert_v2(in_json: dict, target: str) -> str:
84+
85+
if 'version' in in_json.keys() and in_json['version'] == 1.0:
86+
converted = {
87+
'version': 2.0,
88+
'target': target,
89+
'algorithms': [{algo: config} for algo, config in in_json.items()]
90+
}
91+
else:
92+
converted = in_json
93+
94+
# Convert between vc4 <-> pisp targets. This is a best effort thing.
95+
if converted['target'] != target:
96+
converted = convert_target(converted, target)
97+
converted['target'] = target
98+
99+
grid_size = grid_size_vc4[0] if target == 'vc4' else grid_size_pisp[0]
100+
return pretty_print(converted, custom_elems={'table': grid_size, 'luminance_lut': grid_size})
29101

30102

31103
if __name__ == "__main__":
32104
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, description=
33-
'Convert the format of the Raspberry Pi camera tuning file from v1.0 to v2.0.\n')
105+
'Convert the format of the Raspberry Pi camera tuning file from v1.0 to v2.0 and/or the vc4 <-> pisp targets.\n')
34106
parser.add_argument('input', type=str, help='Input tuning file.')
107+
parser.add_argument('-t', '--target', type=str, help='Target platform.',
108+
choices=['pisp', 'vc4'], default='vc4')
35109
parser.add_argument('output', type=str, nargs='?',
36110
help='Output converted tuning file. If not provided, the input file will be updated in-place.',
37111
default=None)
@@ -40,7 +114,7 @@ def convert_v2(in_json: dict) -> str:
40114
with open(args.input, 'r') as f:
41115
in_json = json.load(f)
42116

43-
out_json = convert_v2(in_json)
117+
out_json = convert_v2(in_json, args.target)
44118

45119
with open(args.output if args.output is not None else args.input, 'w') as f:
46120
f.write(out_json)

0 commit comments

Comments
 (0)