diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/codegen/controls.py | 24 | ||||
-rwxr-xr-x | utils/codegen/gen-controls.py | 2 | ||||
-rwxr-xr-x | utils/codegen/gen-gst-controls.py | 7 | ||||
-rwxr-xr-x | utils/gen-debug-controls.py | 1 | ||||
-rw-r--r-- | utils/tuning/libtuning/ctt_awb.py | 58 | ||||
-rw-r--r-- | utils/tuning/libtuning/image.py | 2 | ||||
-rw-r--r-- | utils/tuning/libtuning/modules/awb/__init__.py | 6 | ||||
-rw-r--r-- | utils/tuning/libtuning/modules/awb/awb.py | 36 | ||||
-rw-r--r-- | utils/tuning/libtuning/modules/awb/rkisp1.py | 27 | ||||
-rwxr-xr-x | utils/tuning/rkisp1.py | 4 |
10 files changed, 131 insertions, 36 deletions
diff --git a/utils/codegen/controls.py b/utils/codegen/controls.py index 03c77cc6..e5161048 100644 --- a/utils/codegen/controls.py +++ b/utils/codegen/controls.py @@ -28,7 +28,7 @@ class ControlEnum(object): class Control(object): - def __init__(self, name, data, vendor): + def __init__(self, name, data, vendor, mode): self.__name = name self.__data = data self.__enum_values = None @@ -60,6 +60,16 @@ class Control(object): self.__size = num_elems + if mode == 'properties': + self.__direction = 'out' + else: + direction = self.__data.get('direction') + if direction is None: + raise RuntimeError(f'Control `{self.__name}` missing required field `direction`') + if direction not in ['in', 'out', 'inout']: + raise RuntimeError(f'Control `{self.__name}` direction `{direction}` is invalid; must be one of `in`, `out`, or `inout`') + self.__direction = direction + @property def description(self): """The control description""" @@ -112,6 +122,18 @@ class Control(object): return f"Span<const {typ}>" @property + def direction(self): + in_flag = 'ControlId::Direction::In' + out_flag = 'ControlId::Direction::Out' + + if self.__direction == 'inout': + return f'{in_flag} | {out_flag}' + if self.__direction == 'in': + return in_flag + if self.__direction == 'out': + return out_flag + + @property def element_type(self): return self.__data.get('type') diff --git a/utils/codegen/gen-controls.py b/utils/codegen/gen-controls.py index 3034e9a5..59b716c1 100755 --- a/utils/codegen/gen-controls.py +++ b/utils/codegen/gen-controls.py @@ -71,7 +71,7 @@ def main(argv): ctrls = controls.setdefault(vendor, []) for i, ctrl in enumerate(data['controls']): - ctrl = Control(*ctrl.popitem(), vendor) + ctrl = Control(*ctrl.popitem(), vendor, args.mode) ctrls.append(extend_control(ctrl, i, ranges)) # Sort the vendors by range numerical value diff --git a/utils/codegen/gen-gst-controls.py b/utils/codegen/gen-gst-controls.py index 2601a675..07af7653 100755 --- a/utils/codegen/gen-gst-controls.py +++ b/utils/codegen/gen-gst-controls.py @@ -19,8 +19,9 @@ from controls import Control exposed_controls = [ - 'AeEnable', 'AeMeteringMode', 'AeConstraintMode', 'AeExposureMode', - 'ExposureValue', 'ExposureTime', 'AnalogueGain', 'AeFlickerPeriod', + 'AeMeteringMode', 'AeConstraintMode', 'AeExposureMode', + 'ExposureValue', 'ExposureTime', 'ExposureTimeMode', + 'AnalogueGain', 'AnalogueGainMode', 'AeFlickerPeriod', 'Brightness', 'Contrast', 'AwbEnable', 'AwbMode', 'ColourGains', 'Saturation', 'Sharpness', 'ColourCorrectionMatrix', 'ScalerCrop', 'DigitalGain', 'AfMode', 'AfRange', 'AfSpeed', 'AfMetering', 'AfWindows', @@ -154,7 +155,7 @@ def main(argv): ctrls = controls.setdefault(vendor, []) for ctrl in data['controls']: - ctrl = Control(*ctrl.popitem(), vendor) + ctrl = Control(*ctrl.popitem(), vendor, mode='controls') if ctrl.name in exposed_controls: ctrls.append(extend_control(ctrl)) diff --git a/utils/gen-debug-controls.py b/utils/gen-debug-controls.py index 02585073..272597f4 100755 --- a/utils/gen-debug-controls.py +++ b/utils/gen-debug-controls.py @@ -106,6 +106,7 @@ def main(argv): p = m.file.relative_to(root_dir) desc = {'type': m.type, + 'direction': 'out', 'description': f'Debug control {m.name} found in {p}:{m.line}'} if m.size is not None: desc['size'] = m.size diff --git a/utils/tuning/libtuning/ctt_awb.py b/utils/tuning/libtuning/ctt_awb.py index abf22321..240f37e6 100644 --- a/utils/tuning/libtuning/ctt_awb.py +++ b/utils/tuning/libtuning/ctt_awb.py @@ -4,6 +4,8 @@ # # camera tuning tool for AWB +import logging + import matplotlib.pyplot as plt from bisect import bisect_left from scipy.optimize import fmin @@ -11,12 +13,12 @@ import numpy as np from .image import Image +logger = logging.getLogger(__name__) """ obtain piecewise linear approximation for colour curve """ -def awb(Cam, cal_cr_list, cal_cb_list, plot): - imgs = Cam.imgs +def awb(imgs, cal_cr_list, cal_cb_list, plot): """ condense alsc calibration tables into one dictionary """ @@ -39,7 +41,7 @@ def awb(Cam, cal_cr_list, cal_cb_list, plot): rb_raw = [] rbs_hat = [] for Img in imgs: - Cam.log += '\nProcessing '+Img.name + logger.info(f'Processing {Img.name}') """ get greyscale patches with alsc applied if alsc enabled. Note: if alsc is disabled then colour_cals will be set to None and the @@ -51,7 +53,7 @@ def awb(Cam, cal_cr_list, cal_cb_list, plot): """ r_g = np.mean(r_patchs/g_patchs) b_g = np.mean(b_patchs/g_patchs) - Cam.log += '\n r : {:.4f} b : {:.4f}'.format(r_g, b_g) + logger.info(f' r : {r_g:.4f} b : {b_g:.4f}') """ The curve tends to be better behaved in so-called hatspace. R, B, G represent the individual channels. The colour curve is plotted in @@ -74,12 +76,11 @@ def awb(Cam, cal_cr_list, cal_cb_list, plot): """ r_g_hat = r_g/(1+r_g+b_g) b_g_hat = b_g/(1+r_g+b_g) - Cam.log += '\n r_hat : {:.4f} b_hat : {:.4f}'.format(r_g_hat, b_g_hat) - rbs_hat.append((r_g_hat, b_g_hat, Img.col)) + logger.info(f' r_hat : {r_g_hat:.4f} b_hat : {b_g_hat:.4f}') + rbs_hat.append((r_g_hat, b_g_hat, Img.color)) rb_raw.append((r_g, b_g)) - Cam.log += '\n' - Cam.log += '\nFinished processing images' + logger.info('Finished processing images') """ sort all lits simultaneously by r_hat """ @@ -95,7 +96,7 @@ def awb(Cam, cal_cr_list, cal_cb_list, plot): fit quadratic fit to r_g hat and b_g_hat """ a, b, c = np.polyfit(rbs_hat[0], rbs_hat[1], 2) - Cam.log += '\nFit quadratic curve in hatspace' + logger.info('Fit quadratic curve in hatspace') """ the algorithm now approximates the shortest distance from each point to the curve in dehatspace. Since the fit is done in hatspace, it is easier to @@ -151,14 +152,14 @@ def awb(Cam, cal_cr_list, cal_cb_list, plot): if (x+y) > (rr+bb): dist *= -1 dists.append(dist) - Cam.log += '\nFound closest point on fit line to each point in dehatspace' + logger.info('Found closest point on fit line to each point in dehatspace') """ calculate wiggle factors in awb. 10% added since this is an upper bound """ transverse_neg = - np.min(dists) * 1.1 transverse_pos = np.max(dists) * 1.1 - Cam.log += '\nTransverse pos : {:.5f}'.format(transverse_pos) - Cam.log += '\nTransverse neg : {:.5f}'.format(transverse_neg) + logger.info(f'Transverse pos : {transverse_pos:.5f}') + logger.info(f'Transverse neg : {transverse_neg:.5f}') """ set minimum transverse wiggles to 0.1 . Wiggle factors dictate how far off of the curve the algorithm searches. 0.1 @@ -167,10 +168,10 @@ def awb(Cam, cal_cr_list, cal_cb_list, plot): """ if transverse_pos < 0.01: transverse_pos = 0.01 - Cam.log += '\nForced transverse pos to 0.01' + logger.info('Forced transverse pos to 0.01') if transverse_neg < 0.01: transverse_neg = 0.01 - Cam.log += '\nForced transverse neg to 0.01' + logger.info('Forced transverse neg to 0.01') """ generate new b_hat values at each r_hat according to fit @@ -202,25 +203,25 @@ def awb(Cam, cal_cr_list, cal_cb_list, plot): i = len(c_fit) - 1 while i > 0: if c_fit[i] > c_fit[i-1]: - Cam.log += '\nColour temperature increase found\n' - Cam.log += '{} K at r = {} to '.format(c_fit[i-1], r_fit[i-1]) - Cam.log += '{} K at r = {}'.format(c_fit[i], r_fit[i]) + logger.info('Colour temperature increase found') + logger.info(f'{c_fit[i - 1]} K at r = {r_fit[i - 1]} to ') + logger.info(f'{c_fit[i]} K at r = {r_fit[i]}') """ if colour temperature increases then discard point furthest from the transformed fit (dehatspace) """ error_1 = abs(dists[i-1]) error_2 = abs(dists[i]) - Cam.log += '\nDistances from fit:\n' - Cam.log += '{} K : {:.5f} , '.format(c_fit[i], error_1) - Cam.log += '{} K : {:.5f}'.format(c_fit[i-1], error_2) + logger.info('Distances from fit:') + logger.info(f'{c_fit[i]} K : {error_1:.5f}') + logger.info(f'{c_fit[i - 1]} K : {error_2:.5f}') """ find bad index note that in python false = 0 and true = 1 """ bad = i - (error_1 < error_2) - Cam.log += '\nPoint at {} K deleted as '.format(c_fit[bad]) - Cam.log += 'it is furthest from fit' + logger.info(f'Point at {c_fit[bad]} K deleted as ') + logger.info('it is furthest from fit') """ delete bad point """ @@ -239,12 +240,12 @@ def awb(Cam, cal_cr_list, cal_cb_list, plot): return formatted ct curve, ordered by increasing colour temperature """ ct_curve = list(np.array(list(zip(b_fit, r_fit, c_fit))).flatten())[::-1] - Cam.log += '\nFinal CT curve:' + logger.info('Final CT curve:') for i in range(len(ct_curve)//3): j = 3*i - Cam.log += '\n ct: {} '.format(ct_curve[j]) - Cam.log += ' r: {} '.format(ct_curve[j+1]) - Cam.log += ' b: {} '.format(ct_curve[j+2]) + logger.info(f' ct: {ct_curve[j]} ') + logger.info(f' r: {ct_curve[j + 1]} ') + logger.info(f' b: {ct_curve[j + 2]} ') """ plotting code for debug @@ -301,10 +302,10 @@ def get_alsc_patches(Img, colour_cals, grey=True): patches for each channel, remembering to subtract blacklevel If grey then only greyscale patches considered """ + patches = Img.patches if grey: cen_coords = Img.cen_coords[3::4] - col = Img.col - patches = [np.array(Img.patches[i]) for i in Img.order] + col = Img.color r_patchs = patches[0][3::4] - Img.blacklevel_16 b_patchs = patches[3][3::4] - Img.blacklevel_16 """ @@ -314,7 +315,6 @@ def get_alsc_patches(Img, colour_cals, grey=True): else: cen_coords = Img.cen_coords col = Img.color - patches = [np.array(Img.patches[i]) for i in Img.order] r_patchs = patches[0] - Img.blacklevel_16 b_patchs = patches[3] - Img.blacklevel_16 g_patchs = (patches[1]+patches[2])/2 - Img.blacklevel_16 diff --git a/utils/tuning/libtuning/image.py b/utils/tuning/libtuning/image.py index c8911a0f..ecd334bd 100644 --- a/utils/tuning/libtuning/image.py +++ b/utils/tuning/libtuning/image.py @@ -135,6 +135,6 @@ class Image: all_patches.append(ch_patches) - self.patches = all_patches + self.patches = np.array(all_patches) return not saturated diff --git a/utils/tuning/libtuning/modules/awb/__init__.py b/utils/tuning/libtuning/modules/awb/__init__.py new file mode 100644 index 00000000..2d67f10c --- /dev/null +++ b/utils/tuning/libtuning/modules/awb/__init__.py @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2024, Ideas On Board + +from libtuning.modules.awb.awb import AWB +from libtuning.modules.awb.rkisp1 import AWBRkISP1 diff --git a/utils/tuning/libtuning/modules/awb/awb.py b/utils/tuning/libtuning/modules/awb/awb.py new file mode 100644 index 00000000..c154cf3b --- /dev/null +++ b/utils/tuning/libtuning/modules/awb/awb.py @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2024, Ideas On Board + +import logging + +from ..module import Module + +from libtuning.ctt_awb import awb +import numpy as np + +logger = logging.getLogger(__name__) + + +class AWB(Module): + type = 'awb' + hr_name = 'AWB (Base)' + out_name = 'GenericAWB' + + def __init__(self, *, debug: list): + super().__init__() + + self.debug = debug + + def do_calculation(self, images): + logger.info('Starting AWB calculation') + + imgs = [img for img in images if img.macbeth is not None] + + gains, _, _ = awb(imgs, None, None, False) + gains = np.reshape(gains, (-1, 3)) + + return [{ + 'ct': int(v[0]), + 'gains': [float(1.0 / v[1]), float(1.0 / v[2])] + } for v in gains] diff --git a/utils/tuning/libtuning/modules/awb/rkisp1.py b/utils/tuning/libtuning/modules/awb/rkisp1.py new file mode 100644 index 00000000..0c95843b --- /dev/null +++ b/utils/tuning/libtuning/modules/awb/rkisp1.py @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2024, Ideas On Board +# +# AWB module for tuning rkisp1 + +from .awb import AWB + +import libtuning as lt + + +class AWBRkISP1(AWB): + hr_name = 'AWB (RkISP1)' + out_name = 'Awb' + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def validate_config(self, config: dict) -> bool: + return True + + def process(self, config: dict, images: list, outputs: dict) -> dict: + output = {} + + output['colourGains'] = self.do_calculation(images) + + return output diff --git a/utils/tuning/rkisp1.py b/utils/tuning/rkisp1.py index f5c42a61..9f40fd8b 100755 --- a/utils/tuning/rkisp1.py +++ b/utils/tuning/rkisp1.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later # # Copyright (C) 2022, Paul Elder <paul.elder@ideasonboard.com> +# Copyright (C) 2024, Ideas On Board # # Tuning script for rkisp1 @@ -14,13 +15,14 @@ from libtuning.parsers import YamlParser from libtuning.generators import YamlOutput from libtuning.modules.lsc import LSCRkISP1 from libtuning.modules.agc import AGCRkISP1 +from libtuning.modules.awb import AWBRkISP1 from libtuning.modules.ccm import CCMRkISP1 from libtuning.modules.static import StaticModule coloredlogs.install(level=logging.INFO, fmt='%(name)s %(levelname)s %(message)s') agc = AGCRkISP1(debug=[lt.Debug.Plot]) -awb = StaticModule('Awb') +awb = AWBRkISP1(debug=[lt.Debug.Plot]) blc = StaticModule('BlackLevelCorrection') ccm = CCMRkISP1(debug=[lt.Debug.Plot]) color_processing = StaticModule('ColorProcessing') |