diff options
Diffstat (limited to 'utils/tuning')
-rw-r--r-- | utils/tuning/config-example.yaml | 44 | ||||
-rw-r--r-- | utils/tuning/libtuning/modules/awb/awb.py | 16 | ||||
-rw-r--r-- | utils/tuning/libtuning/modules/awb/rkisp1.py | 21 | ||||
-rw-r--r-- | utils/tuning/libtuning/modules/lux/__init__.py | 6 | ||||
-rw-r--r-- | utils/tuning/libtuning/modules/lux/lux.py | 70 | ||||
-rw-r--r-- | utils/tuning/libtuning/modules/lux/rkisp1.py | 22 | ||||
-rwxr-xr-x | utils/tuning/rkisp1.py | 14 |
7 files changed, 175 insertions, 18 deletions
diff --git a/utils/tuning/config-example.yaml b/utils/tuning/config-example.yaml index 1b7f52cd..5593eaef 100644 --- a/utils/tuning/config-example.yaml +++ b/utils/tuning/config-example.yaml @@ -5,7 +5,49 @@ general: do_alsc_colour: 1 luminance_strength: 0.5 awb: - greyworld: 0 + # Algorithm can either be 'grey' or 'bayes' + algorithm: bayes + # Priors is only used for the bayes algorithm. They are defined in linear + # space. A good staring point is: + # - lux: 0 + # ct: [ 2000, 3000, 13000 ] + # probability: [ 1.005, 1.0, 1.0 ] + # - lux: 800 + # ct: [ 2000, 6000, 13000 ] + # probability: [ 1.0, 1.01, 1.01 ] + # - lux: 1500 + # ct: [ 2000, 4000, 6000, 6500, 7000, 13000 ] + # probability: [ 1.0, 1.005, 1.032, 1.037, 1.01, 1.01 ] + priors: + - lux: 0 + ct: [ 2000, 13000 ] + probability: [ 1.0, 1.0 ] + AwbMode: + AwbAuto: + lo: 2500 + hi: 8000 + AwbIncandescent: + lo: 2500 + hi: 3000 + AwbTungsten: + lo: 3000 + hi: 3500 + AwbFluorescent: + lo: 4000 + hi: 4700 + AwbIndoor: + lo: 3000 + hi: 5000 + AwbDaylight: + lo: 5500 + hi: 6500 + AwbCloudy: + lo: 6500 + hi: 8000 + # One custom mode can be defined if needed + #AwbCustom: + # lo: 2000 + # hi: 1300 macbeth: small: 1 show: 0 diff --git a/utils/tuning/libtuning/modules/awb/awb.py b/utils/tuning/libtuning/modules/awb/awb.py index c154cf3b..0dc4f59d 100644 --- a/utils/tuning/libtuning/modules/awb/awb.py +++ b/utils/tuning/libtuning/modules/awb/awb.py @@ -27,10 +27,14 @@ class AWB(Module): imgs = [img for img in images if img.macbeth is not None] - gains, _, _ = awb(imgs, None, None, False) - gains = np.reshape(gains, (-1, 3)) + ct_curve, transverse_pos, transverse_neg = awb(imgs, None, None, False) + ct_curve = np.reshape(ct_curve, (-1, 3)) + gains = [{ + 'ct': int(v[0]), + 'gains': [float(1.0 / v[1]), float(1.0 / v[2])] + } for v in ct_curve] + + return {'colourGains': gains, + 'transversePos': transverse_pos, + 'transverseNeg': transverse_neg} - 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 index 0c95843b..d562d26e 100644 --- a/utils/tuning/libtuning/modules/awb/rkisp1.py +++ b/utils/tuning/libtuning/modules/awb/rkisp1.py @@ -6,9 +6,6 @@ from .awb import AWB -import libtuning as lt - - class AWBRkISP1(AWB): hr_name = 'AWB (RkISP1)' out_name = 'Awb' @@ -20,8 +17,20 @@ class AWBRkISP1(AWB): return True def process(self, config: dict, images: list, outputs: dict) -> dict: - output = {} - - output['colourGains'] = self.do_calculation(images) + if not 'awb' in config['general']: + raise ValueError('AWB configuration missing') + awb_config = config['general']['awb'] + algorithm = awb_config['algorithm'] + + output = {'algorithm': algorithm} + data = self.do_calculation(images) + if algorithm == 'grey': + output['colourGains'] = data['colourGains'] + elif algorithm == 'bayes': + output['AwbMode'] = awb_config['AwbMode'] + output['priors'] = awb_config['priors'] + output.update(data) + else: + raise ValueError(f"Unknown AWB algorithm {output['algorithm']}") return output diff --git a/utils/tuning/libtuning/modules/lux/__init__.py b/utils/tuning/libtuning/modules/lux/__init__.py new file mode 100644 index 00000000..af9d4e08 --- /dev/null +++ b/utils/tuning/libtuning/modules/lux/__init__.py @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2025, Ideas on Board + +from libtuning.modules.lux.lux import Lux +from libtuning.modules.lux.rkisp1 import LuxRkISP1 diff --git a/utils/tuning/libtuning/modules/lux/lux.py b/utils/tuning/libtuning/modules/lux/lux.py new file mode 100644 index 00000000..4bad429a --- /dev/null +++ b/utils/tuning/libtuning/modules/lux/lux.py @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2019, Raspberry Pi Ltd +# Copyright (C) 2025, Ideas on Board +# +# Base Lux tuning module + +from ..module import Module + +import logging +import numpy as np + +logger = logging.getLogger(__name__) + + +class Lux(Module): + type = 'lux' + hr_name = 'Lux (Base)' + out_name = 'GenericLux' + + def __init__(self, debug: list): + super().__init__() + + self.debug = debug + + def calculate_lux_reference_values(self, images): + # The lux calibration is done on a single image. For best effects, the + # image with lux level closest to 1000 is chosen. + imgs = [img for img in images if img.macbeth is not None] + lux_values = [img.lux for img in imgs] + index = lux_values.index(min(lux_values, key=lambda l: abs(1000 - l))) + img = imgs[index] + logger.info(f'Selected image {img.name} for lux calibration') + + if img.lux < 50: + logger.warning(f'A Lux level of {img.lux} is very low for proper lux calibration') + + ref_y = self.calculate_y(img) + exposure_time = img.exposure + gain = img.againQ8_norm + aperture = 1 + logger.info(f'RefY:{ref_y} Exposure time:{exposure_time}µs Gain:{gain} Aperture:{aperture}') + return {'referenceY': ref_y, + 'referenceExposureTime': exposure_time, + 'referenceAnalogueGain': gain, + 'referenceDigitalGain': 1.0, + 'referenceLux': img.lux} + + def calculate_y(self, img): + max16Bit = 0xffff + # Average over all grey patches. + ap_r = np.mean(img.patches[0][3::4]) / max16Bit + ap_g = (np.mean(img.patches[1][3::4]) + np.mean(img.patches[2][3::4])) / 2 / max16Bit + ap_b = np.mean(img.patches[3][3::4]) / max16Bit + logger.debug(f'Averaged grey patches: Red: {ap_r}, Green: {ap_g}, Blue: {ap_b}') + + # Calculate white balance gains. + gr = ap_g / ap_r + gb = ap_g / ap_b + logger.debug(f'WB gains: Red: {gr} Blue: {gb}') + + # Calculate the mean Y value of the whole image + a_r = np.mean(img.channels[0]) * gr + a_g = (np.mean(img.channels[1]) + np.mean(img.channels[2])) / 2 + a_b = np.mean(img.channels[3]) * gb + y = 0.299 * a_r + 0.587 * a_g + 0.114 * a_b + y /= max16Bit + + return y + diff --git a/utils/tuning/libtuning/modules/lux/rkisp1.py b/utils/tuning/libtuning/modules/lux/rkisp1.py new file mode 100644 index 00000000..62d3f94c --- /dev/null +++ b/utils/tuning/libtuning/modules/lux/rkisp1.py @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2024, Ideas on Board +# +# Lux module for tuning rkisp1 + +from .lux import Lux + + +class LuxRkISP1(Lux): + hr_name = 'Lux (RkISP1)' + out_name = 'Lux' + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + # We don't need anything from the config file. + def validate_config(self, config: dict) -> bool: + return True + + def process(self, config: dict, images: list, outputs: dict) -> dict: + return self.calculate_lux_reference_values(images) diff --git a/utils/tuning/rkisp1.py b/utils/tuning/rkisp1.py index 9f40fd8b..207b717a 100755 --- a/utils/tuning/rkisp1.py +++ b/utils/tuning/rkisp1.py @@ -6,18 +6,19 @@ # # Tuning script for rkisp1 -import coloredlogs import logging import sys +import coloredlogs import libtuning as lt -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.lsc import LSCRkISP1 +from libtuning.modules.lux import LuxRkISP1 from libtuning.modules.static import StaticModule +from libtuning.parsers import YamlParser coloredlogs.install(level=logging.INFO, fmt='%(name)s %(levelname)s %(message)s') @@ -45,12 +46,15 @@ lsc = LSCRkISP1(debug=[lt.Debug.Plot], # This is the function that will be used to smooth the color ratio # values. This can also be a custom function. smoothing_function=lt.smoothing.MedianBlur(3),) +lux = LuxRkISP1(debug=[lt.Debug.Plot]) tuner = lt.Tuner('RkISP1') -tuner.add([agc, awb, blc, ccm, color_processing, filter, gamma_out, lsc]) +tuner.add([agc, awb, blc, ccm, color_processing, filter, gamma_out, lsc, lux]) tuner.set_input_parser(YamlParser()) tuner.set_output_formatter(YamlOutput()) -tuner.set_output_order([agc, awb, blc, ccm, color_processing, + +# Bayesian AWB uses the lux value, so insert the lux algorithm before AWB. +tuner.set_output_order([agc, lux, awb, blc, ccm, color_processing, filter, gamma_out, lsc]) if __name__ == '__main__': |