Update tuning file utils for our own tuning files.
This commit is contained in:
parent
f74a51866a
commit
ec16cc17f8
3 changed files with 70 additions and 49 deletions
|
|
@ -112,7 +112,7 @@ class SensorModeSelector(BaseModel):
|
|||
|
||||
These values are the output size and the bit depth.
|
||||
|
||||
This is a Pydantic model so that it can be saved to disk.
|
||||
This is a Pydantic model so that it can sent by FastAPI
|
||||
"""
|
||||
|
||||
output_size: tuple[int, int]
|
||||
|
|
@ -126,12 +126,13 @@ class LensShading(BaseModel):
|
|||
(12, 16) in size. The arrays are luminance, red-difference chroma (Cr), and
|
||||
blue-difference chroma (Cb).
|
||||
|
||||
This is a Pydantic model so that it can be saved to the disk.
|
||||
This is a Pydantic model so that it can sent by FastAPI
|
||||
"""
|
||||
|
||||
luminance: list[list[float]]
|
||||
Cr: list[list[float]]
|
||||
Cb: list[list[float]]
|
||||
colour_temp: int
|
||||
|
||||
|
||||
class StreamingPiCamera2(BaseCamera):
|
||||
|
|
@ -216,7 +217,8 @@ class StreamingPiCamera2(BaseCamera):
|
|||
@lt.thing_property
|
||||
def calibration_required(self) -> bool:
|
||||
"""Whether the camera needs calibrating."""
|
||||
return not self.lens_shading_is_static
|
||||
# Check if the lens shading table is calibrated.
|
||||
return not tf_utils.lst_calibrated(self.tuning)
|
||||
|
||||
## Persistent controls! These are settings
|
||||
|
||||
|
|
@ -730,7 +732,12 @@ class StreamingPiCamera2(BaseCamera):
|
|||
# luminance (L), red-difference chroma (Cr), and blue-difference chroma
|
||||
# (Cb).
|
||||
L, Cr, Cb = recalibrate_utils.lst_from_camera(cam, self._sensor_info) # noqa: N806
|
||||
self.tuning = tf_utils.set_static_lst(self.tuning, L, Cr, Cb)
|
||||
self.tuning = tf_utils.set_lst(
|
||||
self.tuning,
|
||||
luminance=L,
|
||||
cr=Cr,
|
||||
cb=Cb,
|
||||
)
|
||||
|
||||
# Re-initialise the picamera to reload the tuning file.
|
||||
self._initialise_picamera()
|
||||
|
|
@ -750,14 +757,14 @@ class StreamingPiCamera2(BaseCamera):
|
|||
It is a 9 value tuple used to specify the 3x3 matrix that the GPU pipeline uses
|
||||
to convert from the camera R,G,B vector to the standard R,G,B.
|
||||
"""
|
||||
return tuple(tf_utils.get_static_ccm(self.tuning)[0]["ccm"])
|
||||
return tuple(tf_utils.get_ccm(self.tuning)[0]["ccm"])
|
||||
|
||||
@colour_correction_matrix.setter # type: ignore
|
||||
def colour_correction_matrix(
|
||||
self,
|
||||
value: tuple[float, float, float, float, float, float, float, float, float],
|
||||
) -> None:
|
||||
self.tuning = tf_utils.set_static_ccm(self.tuning, value)
|
||||
self.tuning = tf_utils.set_ccm(self.tuning, value)
|
||||
|
||||
if self._picamera is not None:
|
||||
with self._streaming_picamera(pause_stream=True):
|
||||
|
|
@ -833,8 +840,11 @@ class StreamingPiCamera2(BaseCamera):
|
|||
with self._streaming_picamera(pause_stream=True):
|
||||
# Generate and array of ones of the correct size for each channel
|
||||
flat_array = np.ones((12, 16))
|
||||
self.tuning = tf_utils.set_static_lst(
|
||||
self.tuning, flat_array, flat_array, flat_array
|
||||
self.tuning = tf_utils.set_lst(
|
||||
self.tuning,
|
||||
luminance=flat_array,
|
||||
cr=flat_array,
|
||||
cb=flat_array,
|
||||
)
|
||||
self._initialise_picamera()
|
||||
|
||||
|
|
@ -930,9 +940,6 @@ class StreamingPiCamera2(BaseCamera):
|
|||
- adaptive control is enabled
|
||||
- multiple LSTs in use (for different colour temperatures),
|
||||
"""
|
||||
if not self.lens_shading_is_static:
|
||||
return None
|
||||
|
||||
# Note "alsc" is the Picamera2 term for "Automatic Lens Shading Correction"
|
||||
alsc = self.get_tuning_algo("rpi.alsc")
|
||||
|
||||
|
|
@ -951,23 +958,25 @@ class StreamingPiCamera2(BaseCamera):
|
|||
luminance=reshape_lst(alsc["luminance_lut"]),
|
||||
Cr=reshape_lst(alsc["calibrations_Cr"][0]["table"]),
|
||||
Cb=reshape_lst(alsc["calibrations_Cb"][0]["table"]),
|
||||
colour_temp=alsc["calibrations_Cb"][0]["ct"],
|
||||
)
|
||||
|
||||
@lens_shading_tables.setter
|
||||
def lens_shading_tables(self, lst: LensShading) -> None:
|
||||
"""Set the lens shading tables."""
|
||||
with self._streaming_picamera(pause_stream=True):
|
||||
self.tuning = tf_utils.set_static_lst(
|
||||
self.tuning = tf_utils.set_lst(
|
||||
self.tuning,
|
||||
luminance=lst.luminance,
|
||||
cr=lst.Cr,
|
||||
cb=lst.Cb,
|
||||
colour_temp=lst.colour_temp,
|
||||
)
|
||||
self._initialise_picamera()
|
||||
|
||||
@lt.thing_action
|
||||
def flat_lens_shading_chrominance(self) -> None:
|
||||
"""Disable flat-field correction.
|
||||
"""Disable flat-field correction for colour only.
|
||||
|
||||
This method will set the chrominance of the lens shading table to be
|
||||
flat, i.e. we'll correct vignetting of intensity, but not any change in
|
||||
|
|
@ -977,7 +986,13 @@ class StreamingPiCamera2(BaseCamera):
|
|||
alsc = self.get_tuning_algo("rpi.alsc")
|
||||
luminance = alsc["luminance_lut"]
|
||||
flat = np.ones((12, 16))
|
||||
self.tuning = tf_utils.set_static_lst(self.tuning, luminance, flat, flat)
|
||||
self.tuning = tf_utils.set_lst(
|
||||
self.tuning,
|
||||
luminance=luminance,
|
||||
cr=flat,
|
||||
cb=flat,
|
||||
colour_temp=1234,
|
||||
)
|
||||
self._initialise_picamera()
|
||||
|
||||
@lt.thing_action
|
||||
|
|
@ -995,17 +1010,6 @@ class StreamingPiCamera2(BaseCamera):
|
|||
)
|
||||
self._initialise_picamera()
|
||||
|
||||
@lt.thing_property
|
||||
def lens_shading_is_static(self) -> bool:
|
||||
"""Whether the lens shading is static.
|
||||
|
||||
This property is true if the lens shading correction has been set to use
|
||||
a static table (i.e. the number of automatic correction iterations is zero).
|
||||
The default LST is not static, but all the calibration controls will set it
|
||||
to be static (except "reset")
|
||||
"""
|
||||
return tf_utils.lst_is_static(self.tuning)
|
||||
|
||||
@property
|
||||
def thing_state(self) -> Mapping[str, Any]:
|
||||
"""Update generic camera metadata with Picamera-specific data."""
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
"""Functions for loading, adjusting, or reading from the Picamera2 tuning file.
|
||||
|
||||
The functions that edit the tuning files edit them in place. This will change in
|
||||
the future.
|
||||
The functions that edit the tuning files return a new dictionary that is updated.
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
|
@ -60,44 +59,65 @@ def find_tuning_algo(tuning: dict[str, dict], name: str) -> dict[str, Any]:
|
|||
return algo_dict[name]
|
||||
|
||||
|
||||
def set_static_lst(
|
||||
def set_lst(
|
||||
tuning: dict,
|
||||
*,
|
||||
luminance: np.ndarray,
|
||||
cr: np.ndarray,
|
||||
cb: np.ndarray,
|
||||
colour_temp: int = 5000,
|
||||
) -> dict:
|
||||
"""Update the ``rpi.alsc`` section of a camera tuning dict to use a static correction.
|
||||
"""Update the ``rpi.alsc`` section of with new lens shading tables.
|
||||
|
||||
``tuning`` will be updated in-place to set its shading to static, and disable any
|
||||
adaptive tweaking by the algorithm.
|
||||
Only one set of tables is set so no adaptive lens shading will be used.
|
||||
|
||||
:param tuning: The current tuning file.
|
||||
:param luminance: The table of luminance values, as (12, 16) numpy array
|
||||
:param cr: The table of cr values, as (12, 16) numpy array
|
||||
:param cb: The table of cb values, as (12, 16) numpy array
|
||||
:param colour_temp: The colour temperature to set. By default this is 5000. Set a
|
||||
different value for the PiCamera Thing to report that the lens shading is not
|
||||
calibrated.
|
||||
:return: an updated tuning dict with the new lens shading tables.
|
||||
"""
|
||||
output_tuning = deepcopy(tuning)
|
||||
for table in luminance, cr, cb:
|
||||
if np.array(table).shape != (12, 16):
|
||||
raise ValueError("Lens shading tables must be 12x16!")
|
||||
alsc = find_tuning_algo(output_tuning, "rpi.alsc")
|
||||
alsc["n_iter"] = 0 # disable the adaptive part
|
||||
alsc["n_iter"] = 0 # disable the adaptive part.
|
||||
alsc["luminance_strength"] = 1.0
|
||||
alsc["calibrations_Cr"] = [
|
||||
{"ct": 4500, "table": _as_flat_rounded_list(cr, round_to=3)}
|
||||
{"ct": colour_temp, "table": _as_flat_rounded_list(cr, round_to=3)}
|
||||
]
|
||||
alsc["calibrations_Cb"] = [
|
||||
{"ct": 4500, "table": _as_flat_rounded_list(cb, round_to=3)}
|
||||
{"ct": colour_temp, "table": _as_flat_rounded_list(cb, round_to=3)}
|
||||
]
|
||||
alsc["luminance_lut"] = _as_flat_rounded_list(luminance, round_to=3)
|
||||
return output_tuning
|
||||
|
||||
|
||||
def set_static_ccm(
|
||||
def lst_calibrated(tuning: dict) -> bool:
|
||||
"""Whether the lens shading table is calibrated.
|
||||
|
||||
This checks whether the lens shading table is has a colour temperature of 5000. As
|
||||
this is what we set on calibration. Our tuning file sets a temperature of 1234.
|
||||
"""
|
||||
alsc = find_tuning_algo(tuning, "rpi.alsc")
|
||||
return alsc["calibrations_Cr"][0]["ct"] == 5000
|
||||
|
||||
|
||||
def set_ccm(
|
||||
tuning: dict,
|
||||
col_corr_matrix: tuple[
|
||||
float, float, float, float, float, float, float, float, float
|
||||
],
|
||||
) -> dict:
|
||||
"""Update the ``rpi.alsc`` section of a camera tuning dict to use a static correction.
|
||||
"""Update the ``rpi.alsc`` section of a camera tuning dict set the colour correction matrix.
|
||||
|
||||
``tuning`` will be updated in-place to set its shading to static, and disable any
|
||||
adaptive tweaking by the algorithm.
|
||||
:param tuning: The current tuning dict
|
||||
:param col_corr_matrix: The colour correction matrix to set
|
||||
:return: an updated tuning dict with the new colour correction matrix.
|
||||
"""
|
||||
output_tuning = deepcopy(tuning)
|
||||
ccm = find_tuning_algo(output_tuning, "rpi.ccm")
|
||||
|
|
@ -105,32 +125,26 @@ def set_static_ccm(
|
|||
return output_tuning
|
||||
|
||||
|
||||
def get_static_ccm(tuning: dict) -> None:
|
||||
def get_ccm(tuning: dict) -> None:
|
||||
"""Get a copy of the the ``rpi.ccm`` section of a camera tuning dict."""
|
||||
ccm = find_tuning_algo(tuning, "rpi.ccm")
|
||||
return deepcopy(ccm["ccms"])
|
||||
|
||||
|
||||
def lst_is_static(tuning: dict) -> bool:
|
||||
"""Whether the lens shading table is set to static."""
|
||||
alsc = find_tuning_algo(tuning, "rpi.alsc")
|
||||
return alsc["n_iter"] == 0
|
||||
|
||||
|
||||
def set_static_geq(
|
||||
tuning: dict,
|
||||
offset: int = 65535,
|
||||
) -> dict:
|
||||
"""Update the ``rpi.geq`` section of a camera tuning dict.
|
||||
|
||||
:param tuning: the raspberry pi tuning file. This will be updated in-place to
|
||||
set the geq offset to the given value.
|
||||
:param tuning: the raspberry pi tuning dictionary
|
||||
:param offset: The desired green equalisation offset. Default 65535. The default is
|
||||
the maximum allowed value. This means the brightness will always be below the
|
||||
threshold where averaging is used. This is default as we always need the green
|
||||
equalisation to averages the green pixels in the red and blue rows due to the
|
||||
chief ray angle compensation issue when the the stock lens is replaced by an
|
||||
objective.
|
||||
:param return: An updated tuning dictionary
|
||||
"""
|
||||
output_tuning = deepcopy(tuning)
|
||||
geq = find_tuning_algo(output_tuning, "rpi.geq")
|
||||
|
|
@ -182,7 +196,7 @@ def copy_algo_from_other_tuning(
|
|||
# Find the relevant sub-dict for each tuning file
|
||||
from_i = _index_of_algorithm(copy_from["algorithms"], algo)
|
||||
to_i = _index_of_algorithm(base_tuning_file["algorithms"], algo)
|
||||
# Updating the dictionary in place.
|
||||
# Updating the output_tuning copy.
|
||||
output_tuning["algorithms"][to_i] = deepcopy(copy_from["algorithms"][from_i])
|
||||
return output_tuning
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue