summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xutils/rkisp1/lsc_parse_android_config.py187
1 files changed, 187 insertions, 0 deletions
diff --git a/utils/rkisp1/lsc_parse_android_config.py b/utils/rkisp1/lsc_parse_android_config.py
new file mode 100755
index 00000000..a7c2c160
--- /dev/null
+++ b/utils/rkisp1/lsc_parse_android_config.py
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2023, Jacopo Mondi - Ideas on Board Oy
+#
+# Parse Android .xml configuration file and extract the LSC tables.
+#
+# Print to standard output a "LensShadingCorrection" section, understandable by
+# libcamera LSC algorithm, that can be pasted to the sensor configuration file.
+
+import argparse
+import string
+import sys
+import re
+import xml.etree.ElementTree as et
+
+
+def sanitize(name):
+ return re.sub(r"[\n\t\s]*", "", name)
+
+
+def split_table(table):
+ values = ""
+ for v in table.text.strip(' ').split():
+ values += v.strip('[').strip(']') + ", "
+ return values
+
+
+def print_cell(cell):
+ lsc_template = string.Template(''' #${name} - ${illuminant}
+ - ct: ${ct}
+ resolution: ${res}
+ r: [${red}]
+ gr: [${greenr}]
+ gb: [${greenb}]
+ b: [${blue}]''')
+
+ illuminant = cell.find("illumination")
+ ct = illuminant_to_ct(illuminant)
+
+ template_dict = {
+ 'name': sanitize(cell.find("name").text),
+ 'illuminant': sanitize(illuminant.text),
+ 'ct': ct,
+ 'res': sanitize(cell.find("resolution").text)
+ }
+
+ red_table = cell.find("LSC_SAMPLES_red")
+ greenr_table = cell.find("LSC_SAMPLES_greenR")
+ greenb_table = cell.find("LSC_SAMPLES_greenB")
+ blue_table = cell.find("LSC_SAMPLES_blue")
+
+ if red_table is None or greenr_table is None or greenb_table is None or blue_table is None:
+ return
+
+ template_dict['red'] = split_table(red_table)
+ template_dict['greenr'] = split_table(greenr_table)
+ template_dict['greenb'] = split_table(greenb_table)
+ template_dict['blue'] = split_table(blue_table)
+
+ return lsc_template.substitute(template_dict)
+
+
+def illuminant_to_ct(illuminant):
+ # Standard CIE Illiminants to Color Temperature in Kelvin
+ # https://en.wikipedia.org/wiki/Standard_illuminant
+ #
+ # Not included (and then ignored when parsing the configuration file):
+ # - "Horizon" == D50 == 5003
+ # - "BW" == ?
+ # - "PREFLASH" == ?
+ illuminants_dict = {
+ 'A': 2856,
+ 'D50': 5003,
+ 'D65': 6504,
+ 'D75': 7504,
+ 'F11_TL84': 4000,
+ 'F2_CWF': 4230,
+ }
+
+ ill_key = sanitize(illuminant.text)
+ try:
+ ct = illuminants_dict[ill_key]
+ except KeyError:
+ return None
+
+ return ct
+
+
+# Make sure the cell is well formed and return it
+def filter_cells(cell, res, lsc_cells):
+ name = cell.find("name")
+ resolution = cell.find("resolution")
+ illumination = cell.find("illumination")
+ vignetting = cell.find("vignetting")
+
+ if name is None or resolution is None or \
+ illumination is None or vignetting is None:
+ return
+
+ # Skip tables for smaller sensor resolutions
+ if res != sanitize(resolution.text):
+ return
+
+ # Skip tables for which we don't know how to translate the illuminant value
+ ct = illuminant_to_ct(illumination)
+ if ct is None:
+ return
+
+ # Only pick tables with vignetting == 70
+ if sanitize(vignetting.text) != "[70]":
+ return
+
+ lsc_cells.append(cell)
+
+
+# Get the "LSC" node
+def find_lsc_table(root):
+ sensor = root.find('sensor')
+ if sensor is None:
+ print("Failed to find \"sensor\" node in config file")
+ raise Exception
+
+ lsc = sensor.find('LSC')
+ if lsc is None:
+ print("Filed to find \"LSC\" node in config file")
+ raise Exception
+
+ return lsc
+
+# libcamera LSC algorithm only operates on a single resolution.
+# Find the largest sensor mode among the ones reported in the LSC tables
+
+
+def parse_max_res(cells):
+ max_res = ""
+ max_size = 0
+
+ for cell in cells:
+ resolution = sanitize(cell.find("resolution").text)
+ [w, h] = resolution.split('x')
+
+ area = int(w) * int(h)
+ if area > max_size:
+ max_res = resolution
+
+ return max_res
+
+
+def main(argv):
+ # Parse command line arguments.
+ parser = argparse.ArgumentParser(
+ description='Parse Android camera configuration file to extract LSC tables')
+ parser.add_argument('--file', '-f', required=True,
+ help='Path to the Android .xml configuration file')
+ args = parser.parse_args(argv[1:])
+
+ root = et.parse(args.file).getroot()
+ try:
+ lsc_node = find_lsc_table(root)
+ except Exception:
+ return 1
+
+ cells = lsc_node.findall("cell")
+
+ max_res = parse_max_res(cells)
+ if max_res == "":
+ return
+
+ lsc_cells = []
+ for cell in cells:
+ filter_cells(cell, max_res, lsc_cells)
+
+ lsc_section = ''' - LensShadingCorrection:
+ x-size: [ 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625 ]
+ y-size: [ 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625 ]
+ sets:
+'''
+
+ for cell in lsc_cells:
+ lsc_section += print_cell(cell) + "\n"
+
+ print(lsc_section)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))