Source code for ms_camera_model.model

'''
Multispectral Camera Model - Simulation Model
=============================================

* **Description:** Simulated model of a multispectral camera. Takes in hyperspectral data and colour filter specs.
  Outputs multispectral data
* **Author:** Tomas Vacek
'''

from __future__ import annotations

import logging
from dataclasses import dataclass, field

import numpy as np

from ms_camera_model.errors import NoImageData
from ms_camera_model.filter_sensor import FilterSensorUnit
from ms_camera_model.image_data import HyperspectralImageData, ImageData

logger = logging.getLogger(__name__)


[docs] @dataclass class MultispectralCameraModel: """ Multispectral camera model :param hs_data: HyperspectralImageData class instance :param filter_sensor_units: list of FilterSensorUnit class instances :param out_data: simulated multispectral image data as ImageData class instance """ hs_data: HyperspectralImageData filter_sensor_units: list[FilterSensorUnit] corrected_filter_sensor_units: list[FilterSensorUnit] = field(init=False) out_data: ImageData = field(init=False)
[docs] def run_simulation(self) -> ImageData: """ Match filter_sensor_units specs with hs_data band_centers and calculate out_data :return: Returns resulting simulated ImageData """ logger.info( "[MSModel] Creating MultispectralCameraModel instance, proceeding with filter_sensor_units matching to provided hyperspectral data..." ) self._filter_sensor_data_matching() logger.info("[MSModel] Units matched. Proceeding with data extraction...") self._extract_data() logger.info("[MSModel] Simulated multispectral data extraction completed") return self.out_data
def _extract_data(self) -> None: """ Extract simulated multispectral data from the hyperspectral img_data using filter_sensor_units """ if self.hs_data.img_data.size == 0: raise NoImageData shape = self.hs_data.img_data.shape modeled_ms_data = np.zeros((shape[0], shape[1], len(self.corrected_filter_sensor_units))) modeled_ms_data_band_centers = [] for i_unit, unit in enumerate(self.corrected_filter_sensor_units): unit.calculate_combined_attenuation() logger.info(f"[MSModel] FS_{i_unit} max combined attenuation is {unit.combined_attenuation.max()}") modeled_ms_data[:, :, i_unit] = self._calculate_band(unit) modeled_ms_data_band_centers.append(unit.filter_spec.band_center) logger.info(f"[MSModel] modeled_ms_data max val: {modeled_ms_data.max()}") self.out_data = ImageData(modeled_ms_data, modeled_ms_data_band_centers, len(self.corrected_filter_sensor_units)) def _filter_sensor_data_matching(self) -> None: """ Interpolate filter transmission data to match bands from hyperspectral img data """ corrected_units = [] for filter_sensor_unit in self.filter_sensor_units: corrected_units.append(filter_sensor_unit.interpolate_to_hs_data(self.hs_data.band_centers)) self.corrected_filter_sensor_units = corrected_units def _calculate_band(self, filter_sensor_unit: FilterSensorUnit) -> np.ndarray: """ Calculate the image created by a filter sensor unit """ logger.info("[MSModel] Calculating single band image from hyperspectral data...") data_through_unit = self.hs_data.img_data * filter_sensor_unit.combined_attenuation logger.info(f"[MSModel] Max data val: {data_through_unit.max()}") logger.info(f"[MSModel] Data type: {data_through_unit.dtype}") logger.info("[MSModel] Performing trapezoidal integration...") out_img = np.trapezoid(data_through_unit, axis=2) logger.info(f"[MSModel] Max integrated val: {out_img.max()}") logger.info(f"[MSModel] Data type: {out_img.dtype}") # out_img = out_img / data_through_unit.shape[2] logger.info(f"[MSModel] Max out_img val: {out_img.max()}") logger.info(f"[MSModel] Data type: {out_img.dtype}") logger.info("[MSModel] Band calculation completed") return out_img