From patchwork Thu Jun 6 10:15:11 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 20219 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 E1DACC3292 for ; Thu, 6 Jun 2024 10:15:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DB48F65464; Thu, 6 Jun 2024 12:15:41 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="kezno5NU"; dkim-atps=neutral Received: from mail-ej1-x631.google.com (mail-ej1-x631.google.com [IPv6:2a00:1450:4864:20::631]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D39366545D for ; Thu, 6 Jun 2024 12:15:33 +0200 (CEST) Received: by mail-ej1-x631.google.com with SMTP id a640c23a62f3a-a6266ffdba8so65596666b.1 for ; Thu, 06 Jun 2024 03:15:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1717668933; x=1718273733; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=W06p37Y1xwC67cFH+rZkEwLXRBquD8QbZ03xa8sUGrY=; b=kezno5NU+jWXfGFph/m6esRZDFiihgpVoN5pVkaSJb+EUGJS+WCVy0ZPXef93O4pua c4MUOogIAhZsWkqTCQ5Y2UEwPYGXL8FyV32k9kYqY6jh/EjmPdR1Hnqmy68TQHZ1J4IW EC1/OcilasMicM23aR2fDgf9nEMeBTI8fZnla+YUQVaDltXdNhdEsYk6i/BDwL/oZole M/3QPG0zK4x1GckgELSIaqq3CztOZkCKbWkUpACpsXQC4EsxhxjWm2pR72MVNqk6vn99 kdAQllRVZSqgsVxIVeilL5SeJ3dux5GQx7zdP2ZyJ8MWbzkQoBIw2EkgG7tAIgRkkWYS eaOA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1717668933; x=1718273733; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=W06p37Y1xwC67cFH+rZkEwLXRBquD8QbZ03xa8sUGrY=; b=mC0f7ZlWv7UbsWq/rqxYx65hWY1bwZuTFaLrvETF9sW+9k9XkXnpSGSNHfFJpyTbOU O7LgsgepeJARCF3AwvyZjApzg7zZ+7PPfK9c++BoUHPsAEsN/e1noK+OfDEdOATg3rRR pMQygAo0jQpegiPmslpewu+fzWt5Q8eugutYVxHwjPZ6yRHCJ/ryiwW7mJsC1Ggvd30e ctgC0/Wd5OKd4SLaC3Yu02BMcCOF7iTqrA8lFL3tRJdsAlFkWFQtjnMcqJBmRzbB4Q5o vKquc60J6ZmUb3AH4cWcVXYuYb9m965umdbYk3nKBuVAkZctQm8HXfQKAfWmz2beqpmq EeDg== X-Gm-Message-State: AOJu0Yy6G8+SbVnb5MwmvMM1WnOCWSmWFgnqBTtDSQSPycTZOGBLs5n9 tjFfysrSee+T6oY54CNWZCLP5yAn6y08/t+2BlHS9td6f5hpGJGkFMoH8ffmVAdjogwmC5fpfsM 0 X-Google-Smtp-Source: AGHT+IHWHGYONi98wQvaj4RBlbLj1FyXAzD9vqQmyNRINQdmqy7gwkR4ajuWomwSCm8U5qCxfOu5zw== X-Received: by 2002:a17:906:c059:b0:a6c:702e:73fc with SMTP id a640c23a62f3a-a6c702e760fmr240859666b.8.1717668932917; Thu, 06 Jun 2024 03:15:32 -0700 (PDT) Received: from pi5-davidp.pitowers.org ([2001:4d4e:300:1f:c732:5d0a:406b:ae46]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a6c805c59a2sm75809866b.50.2024.06.06.03.15.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Jun 2024 03:15:32 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [PATCH 5/6] utils: raspberrypi: ctt: Add option to convert between vc4/pisp targets Date: Thu, 6 Jun 2024 11:15:11 +0100 Message-Id: <20240606101512.375178-6-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240606101512.375178-1-david.plowman@raspberrypi.com> References: <20240606101512.375178-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" From: Naushir Patuck 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 --- 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)