libtuning: Add module for lux calibration
For the lux algorithm, reference values get calculated based on a tuning image taken at a known lux level. The reference data contains the mean Y of the image, lux level, exposure time, gain and aperture. This module calculates these values for insertion into the tuning file. Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
This commit is contained in:
parent
bab4db2d6d
commit
a783a90dec
3 changed files with 98 additions and 0 deletions
6
utils/tuning/libtuning/modules/lux/__init__.py
Normal file
6
utils/tuning/libtuning/modules/lux/__init__.py
Normal file
|
@ -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
|
70
utils/tuning/libtuning/modules/lux/lux.py
Normal file
70
utils/tuning/libtuning/modules/lux/lux.py
Normal file
|
@ -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
|
||||
|
22
utils/tuning/libtuning/modules/lux/rkisp1.py
Normal file
22
utils/tuning/libtuning/modules/lux/rkisp1.py
Normal file
|
@ -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)
|
Loading…
Add table
Add a link
Reference in a new issue