'''
Multispectral Camera Model - Filters and Sensors
================================================
* **Description:** Dataclasses and their methods for filters and sensors
* **Author:** Tomas Vacek
'''
from __future__ import annotations
import logging
from dataclasses import dataclass, field
import numpy as np
import pandas as pd
logger = logging.getLogger(__name__)
[docs]
@dataclass
class FilterSpecs:
""" Filter specification """
filter_transmission: np.ndarray
name: str = "Generic"
supplier: str = "Generic"
band_center: int = 0
band_width: int = 0
[docs]
@dataclass
class SensorSpecs:
""" Sensor specification """
sensor_qe_curve: np.ndarray
name: str = "Generic"
supplier: str = "Generic"
sensor_type: str = "CMOS"
[docs]
@dataclass
class FilterSensorUnit:
""" Combination of filter and sensor """
filter_spec: FilterSpecs
sensor_spec: SensorSpecs
combined_attenuation: np.ndarray = field(init=False)
[docs]
@classmethod
def from_excel(cls, filename_filter: str, filename_sensor: str) -> FilterSensorUnit:
""" Load filter transmission and sensor spectral sensitivity from Excel file
:param filename_filter: Filename or path to the filter xlsx file
:param filename_sensor: Filename or path to the sensor xlsx file
"""
logger.info(
f"[FilterSensorUnit] Loading specifications\nFilter data: {filename_filter}\nSensor data: {filename_sensor}"
)
filter_data = pd.read_excel(filename_filter)
sensor_data = pd.read_excel(filename_sensor)
filter_spec = FilterSpecs(np.array(filter_data))
sensor_spec = SensorSpecs(np.array(sensor_data))
return FilterSensorUnit(filter_spec, sensor_spec)
[docs]
def calculate_combined_attenuation(self) -> None:
""" Calculate combined attenuation of filter and sensor based on their transmission and qe (quantum efficiency) curve """
logger.info("[FilterSensorUnit] Calculating combined attenuation")
logger.info(
f"[FilterSensorUnit] Filter transmission: {self.filter_spec.filter_transmission.shape}, Sensor QE curve: {self.sensor_spec.sensor_qe_curve.shape}"
)
transmission = self.filter_spec.filter_transmission[:, 1]
sensor_qe = self.sensor_spec.sensor_qe_curve[:, 1]
self.combined_attenuation = transmission * sensor_qe
logger.info(f"[FilterSensorUnit] Combined attenuation: {self.combined_attenuation.shape}")
[docs]
def interpolate_to_hs_data(self, hs_band_centers: list[float] | None) -> FilterSensorUnit:
""" Interpolate the provided filter and sensor data to hyperspectral data """
logger.info("[FilterSensorUnit] Beginning FilterSensorUnit interpolation...")
if hs_band_centers is None:
raise ValueError("Missing band center data for interpolation")
filter_interp = np.interp(hs_band_centers, self.filter_spec.filter_transmission[:, 0],
self.filter_spec.filter_transmission[:, 1])
sensor_interp = np.interp(hs_band_centers, self.sensor_spec.sensor_qe_curve[:, 0],
self.sensor_spec.sensor_qe_curve[:, 1])
logger.info(f"[FilterSensorUnit] Filter interp {filter_interp.shape}")
logger.info(f"[FilterSensorUnit] Sensor interp {sensor_interp.shape}")
interpolated_filter = FilterSpecs(np.column_stack([hs_band_centers, filter_interp]), self.filter_spec.name,
self.filter_spec.supplier, self.filter_spec.band_center,
self.filter_spec.band_width)
interpolated_sensor = SensorSpecs(np.column_stack([hs_band_centers, sensor_interp]), self.sensor_spec.name,
self.sensor_spec.supplier, self.sensor_spec.sensor_type)
return FilterSensorUnit(interpolated_filter, interpolated_sensor)