From patchwork Thu Jul 14 15:24:07 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 16633 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id A9669BD1F1 for ; Thu, 14 Jul 2022 15:24:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3CD5C63327; Thu, 14 Jul 2022 17:24:27 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1657812267; bh=aRQLgXOOl5tP0UfobEXAX64m0ppM/5b594xVTJdupLk=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=RNlE5ZBaIGtb1wHEReKbFGb0uJa0ahjG6thECPRNQH2K3iTHQb8O4s3JgND95JAkY wSW/oIFPAFGSzVOIp6L7SsuJmdfKyW+OHFfNlTwHGqDWqfzQYb/UtPJvJckXZHI+UU u6PtobAn3+/oFuZqCfDJeHCAqmWJgTpxAW+NzTGcmQhQKrN3RcKKMN1id6S8I/sHuy 47EjEguFC4sBPlNGuewdtfXjqbbwa3N6LE3sYU2qbAS8iwoBX2MVxudHJ+rPUGev59 mR/JIWOxF5iqC3CLHlKb96Qeu6rd5u9By9/KtsgP/qhqCHF3kxGgwHA0NEeWcjq/KY DpPxDbzBLFJXg== Received: from mail-wm1-x332.google.com (mail-wm1-x332.google.com [IPv6:2a00:1450:4864:20::332]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 769DB63318 for ; Thu, 14 Jul 2022 17:24:22 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="VxzSyoRC"; dkim-atps=neutral Received: by mail-wm1-x332.google.com with SMTP id ay25so1265482wmb.1 for ; Thu, 14 Jul 2022 08:24:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=r/sF2eU2UliSzf+2+JVSBBZmVC5drTcI+/bKTQNrWhc=; b=VxzSyoRC8Z09/uI2oUn0dynpTKFxSoR0cm2q/9ztvq+5VOxcBSz36dYe2af3KivHs3 uzKZ0sprjYwJVPsKKZIN+/BLbM3/PbNKKpoIoni0W7y/cw9ZxnVet9oMZc2XGuCbq7m9 VR4+5zzh8lZZgvUd9CtvM6/hjbWKvxuFmnsQFlQW1HgmwDw4/8+MfTLxzVjLJzFBGFAX mNhFjRKKnEzM5vRzNRJTddLYGSrJGweDwDwEYGTzg1UqqEsgvZ1r13SH8dWdWcFIXEZV 6CLD727ekTLAWbleaj92GMZkWu8IKQ8Ia6VdSM/QBK4M25deDpu8486khOepSH2U77BS vPZQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=r/sF2eU2UliSzf+2+JVSBBZmVC5drTcI+/bKTQNrWhc=; b=htM+qIvIFafV8GoJWZN+fBzy8gt2I0h0R0vFlzP5zI6CeONcqdN6/h/xFlKaz8w4+J DbRWVlAPXDMpudLh0GOa/pdRV90YKDKv0xsrdJ8KgWjqzAqffENq/H0xbrd2WB++IH4W TkOc/zKSCiR8fTm4eV+LK6gaDvT8kvuQksQAYcnaAGXvJduA0akQlRoWh/842B/1qPGw 3q7STKyMbrnma07BnHSxs9D7l2t49IhSVCM8K8fjyojv/87kW3U0EeeI72o0PmfFJgQf oxydXwBDBglsp5PPkvERLFd+jG6VnZ9x34B4U2/lkamQ6ajJtXi7OfOI+vsJ7Czi76A/ ujwQ== X-Gm-Message-State: AJIora9Mr7pDc+XH7ZBMEf8CdALf69PVQdec0mv4rz25hfP3qVxHIu3z 9rvBrZQigvYl5JKwl8Tj1xkHXZcLF14jHw== X-Google-Smtp-Source: AGRyM1tDHPHHkqxzFSnvKAfZIHpe9YSO3RxMaODa/jX7YB+PC0oEsVE8KHmm6kBHy2gDvWTvtlEIlw== X-Received: by 2002:a05:600c:154a:b0:3a1:70dd:9a12 with SMTP id f10-20020a05600c154a00b003a170dd9a12mr16253617wmg.70.1657812261715; Thu, 14 Jul 2022 08:24:21 -0700 (PDT) Received: from naush-laptop.localdomain ([93.93.133.154]) by smtp.gmail.com with ESMTPSA id h14-20020a1ccc0e000000b003a2e7c13a3asm2322476wmb.42.2022.07.14.08.24.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 14 Jul 2022 08:24:20 -0700 (PDT) To: libcamera-devel@lists.libcamera.org Date: Thu, 14 Jul 2022 16:24:07 +0100 Message-Id: <20220714152409.9780-7-naush@raspberrypi.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220714152409.9780-1-naush@raspberrypi.com> References: <20220714152409.9780-1-naush@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 6/8] utils: raspberrypi: Add tuning file conversion script X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Naushir Patuck via libcamera-devel From: Naushir Patuck Reply-To: Naushir Patuck Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a script to convert the Raspberry Pi camera tuning file format from version 1.0 to 2.0. The version 1.0 format was originally used with the boost JSON parser that happen to provided algorithm ordering based on the ordering in the file. The new format provides implicit ordering by having the algorithms listed in an array. This script also adds a root level version key set to 2.0 to the config file, allowing the controller to distinguish between the two formats. Signed-off-by: Naushir Patuck Reviewed-by: Laurent Pinchart --- utils/raspberrypi/ctt/convert_tuning.py | 117 ++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100755 utils/raspberrypi/ctt/convert_tuning.py diff --git a/utils/raspberrypi/ctt/convert_tuning.py b/utils/raspberrypi/ctt/convert_tuning.py new file mode 100755 index 000000000000..c915bcb46f64 --- /dev/null +++ b/utils/raspberrypi/ctt/convert_tuning.py @@ -0,0 +1,117 @@ +#!/bin/python3 +# Script to convert version 1.0 Raspberry Pi camera tuning files to version 2.0 +# and later. +# +# Copyright 2022 Raspberry Pi Ltd. + +import argparse +import json +import textwrap + + +class Encoder(json.JSONEncoder): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.indentation_level = 0 + self.hard_break = 120 + self.custom_elems = { + 'table': 16, + 'luminance_lut': 16, + 'ct_curve': 3, + 'ccm': 3 + } + + def encode(self, o, node_key=None): + if isinstance(o, (list, tuple)): + # Check if we are a flat list of numbers. + if not any(isinstance(el, (list, tuple, dict)) for el in o): + s = ', '.join(json.dumps(el) for el in o) + if node_key in self.custom_elems.keys(): + # Special case handling to specify number of elements in a row for tables, ccm, etc. + self.indentation_level += 1 + sl = s.split(', ') + num = self.custom_elems[node_key] + chunk = [self.indent_str + ', '.join(sl[x:x + num]) for x in range(0, len(sl), num)] + t = ',\n'.join(chunk) + self.indentation_level -= 1 + output = f'\n{self.indent_str}[\n{t}\n{self.indent_str}]' + elif len(s) > self.hard_break - len(self.indent_str): + # Break a long list with wraps. + self.indentation_level += 1 + t = textwrap.fill(s, self.hard_break, break_long_words=False, + initial_indent=self.indent_str, subsequent_indent=self.indent_str) + self.indentation_level -= 1 + output = f'\n{self.indent_str}[\n{t}\n{self.indent_str}]' + else: + # Smaller lists can remain on a single line. + output = f' [ {s} ]' + 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: + return ' ' + json.dumps(o) + + @property + def indent_str(self) -> str: + return ' ' * self.indentation_level * self.indent + + def iterencode(self, o, **kwargs): + return self.encode(o) + + +def convert_v2(in_json): + + ver = 1.0 if 'version' not in in_json.keys() else in_json['version'] + + if ver == 1.0: + converted = {} + converted['version'] = 2.0 + converted['algorithms'] = [] + + for k, v in in_json.items(): + if k == 'version': + continue + converted['algorithms'].append(dict(zip([k], [v]))) + else: + converted = in_json + + return json.dumps(converted, cls=Encoder, indent=4, sort_keys=False) + + +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' + 'If a v2.0 format file is provided, the tool prettifies its contents.') + parser.add_argument('input', type=str, nargs=1, help='Input tuning file') + parser.add_argument('output', type=str, nargs='?', help='Output converted tuning file', default=None) + args = parser.parse_args() + + with open(args.input[0], 'r') as f: + in_json = json.load(f) + + out_json = convert_v2(in_json) + + with open(args.output if args.output is not None else args.input[0], 'w') as f: + f.write(out_json)