From 63a51c8e1be4a4b1b6960bcf0ac5a8cf41ca16ce Mon Sep 17 00:00:00 2001 From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com> Date: Sun, 10 Nov 2024 15:22:07 -0800 Subject: [PATCH] Add cammatrices.json update tool (taken from ART) --- tools/update_camconst.py | 142 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 tools/update_camconst.py diff --git a/tools/update_camconst.py b/tools/update_camconst.py new file mode 100644 index 000000000..be80b5b0a --- /dev/null +++ b/tools/update_camconst.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python + +from __future__ import print_function +import json +import argparse +import subprocess +import re +import os +import glob +import time + + +def getopts(): + p = argparse.ArgumentParser() + p.add_argument('-c', '--cammatrices', required=True) + p.add_argument('dir') + p.add_argument('--dcamprof-path', default='.') + p.add_argument('-v', '--version') + return p.parse_args() + + +class Camconst(object): + def __init__(self, comment_lines): + self.comment = comment_lines + self.data = {} + self.aliases = {} + + def __getitem__(self, key): + return self.data[key] + + def __contains__(self, key): + return key in self.data + + def __setitem__(self, key, value): + self.data[key] = value + +# end of class Camconst + + +def load_camconst(fname): + jsondata = [] + header = [] + with open(fname) as f: + ok = True + for line in f: + l = line.strip() + if l.startswith('/*'): + ok = False + header.append(l[2:].lstrip()) + elif l.endswith('*/'): + header.append(l[:-2].rstrip()) + ok = True + elif ok: + jsondata.append(re.sub('//.*$', '', line)) + else: + header.append(l) + d = json.loads("\n".join(jsondata)) + res = Camconst(header) + for entry in d['camera_constants']: + try: + camera = entry['make_model'] + if isinstance(camera, list): + camera, rest = camera[0], camera + res.aliases[camera] = rest + res[camera] = entry['dcraw_matrix'] + except KeyError: + pass + return res + + +def dump_camconst(outname, camconst): + with open(outname, 'w') as out: + indent = ' ' * 4 + pr = out.write + if camconst.comment: + pr('/*\n') + for line in camconst.comment: + pr(line) + pr('\n') + pr('*/\n') + pr('{"camera_constants": [\n') + keysep = indent + for key in sorted(camconst.data): + pr(keysep) + camera = key + if key in camconst.aliases: + camera = camconst.aliases[key] + pr('{\n%s%s"make_model" : %s' % (indent, indent, + json.dumps(camera))) + sep = ',\n%s%s' % (indent, indent) + pr('%s"dcraw_matrix" : %s' % (sep, json.dumps(camconst.data[key]))) + pr('\n%s}' % indent) + keysep = ',\n%s' % indent + pr('\n]}\n') + + +def extract_matrix(camconst, opts, filename): + p = subprocess.Popen([os.path.join(opts.dcamprof_path, 'dcamprof'), + 'dcp2json', filename], stdout=subprocess.PIPE) + out, err = p.communicate() + try: + profile = json.loads(out) + camera = profile['UniqueCameraModel'].upper() + if camera in camconst: + return None + matrix = None + for i in '1', '2': + ill = 'CalibrationIlluminant' + i + m = 'ColorMatrix' + i + if ill in profile and m in profile and profile[ill] == 'D65': + matrix = profile[m] + break + if matrix is not None and len(matrix) == 3 and len(matrix[0]) == 3: + return camera, sum(([int(e * 10000) for e in row] + for row in matrix), []) + except ValueError: + return None + + +def main(): + opts = getopts() + cammatrices = load_camconst(opts.cammatrices) + updated = [] + for dcp in glob.glob(os.path.join(opts.dir, '*.dcp')): + res = extract_matrix(cammatrices, opts, dcp) + if res is not None: + camera, matrix = res + cammatrices[camera] = matrix + print('Updated matrix for %s: %s' % (camera, matrix)) + updated.append(camera) + if updated: + info = '' + if opts.version: + info = ' (with Adobe DNG converter %s)' % opts.version + cammatrices.comment.append('Updated on %s%s:' % (time.asctime(), info)) + for cam in sorted(updated): + cammatrices.comment.append(' %s' % cam) + dump_camconst(opts.cammatrices, cammatrices) + + +if __name__ == '__main__': + main()