summaryrefslogtreecommitdiff
path: root/utils/tuning
diff options
context:
space:
mode:
Diffstat (limited to 'utils/tuning')
-rw-r--r--utils/tuning/config-example.yaml44
-rw-r--r--utils/tuning/libtuning/modules/awb/awb.py16
-rw-r--r--utils/tuning/libtuning/modules/awb/rkisp1.py21
-rw-r--r--utils/tuning/libtuning/modules/lux/__init__.py6
-rw-r--r--utils/tuning/libtuning/modules/lux/lux.py70
-rw-r--r--utils/tuning/libtuning/modules/lux/rkisp1.py22
-rwxr-xr-xutils/tuning/rkisp1.py14
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__':