"""
A template class for Detectors.
"""
import os
from abc import ABC, abstractmethod
from ...utils.config import save_config
[docs]class BaseFeature(ABC):
"""
Abstract class to setup and run follow-up computations, called features detectors.
Input is always the output of any method detector.
Attributes:
input_folders (list): A list of input folders.
input_files (list): A list of input files.
result_folders (dict): A dictionary of result folders.
out_folder (str): The output folder.
viz_folder (str): The visualization folder.
subjects_descr (str): The subjects description.
config_path (str): The path to the configuration file.
"""
def __init__(self, config, io, data, requires_out_folder=True) -> None:
"""
Sets up the input and output folders based on the provided configurations
and handles any necessary file checks. Input folders contain the the results
of the method detectors. Saves a copy of the configuration file for the feature
detector.
Args:
config (dict): The feature-specific configurations dictionary.
io (class): A class instance that handles input and output folders.
data (class): A class instance that contains the data.
requires_out_folder (bool, optional): Whether the output folder is required.
Defaults to True.
Returns:
None
"""
self.config = config
self.io = io
self.data = data
self.requires_out_folder = requires_out_folder
# (1) Input folder of the feature is the result folder of detector
self.input_map = {}
self.input_folders, self.input_files = [], [] # Keep for now for backward compatibility
for comp, alg in config["input_detector_names"]:
input_folder = io.get_detector_output_folder(comp, alg, "result")
input_file = os.path.join(input_folder, f"{alg}.npz")
if not os.path.isfile(input_file):
raise FileNotFoundError(f"Feature detector {self.components}: File '{input_file}' does " "not exist!")
self.input_folders.append(input_folder)
self.input_files.append(input_file)
self.input_map[(comp, alg)] = input_file
# (2) output folders
self.result_folders = dict(
(comp, io.get_detector_output_folder(comp, self.algorithm, "result")) for comp in self.components
)
main_component = self.components[0]
if requires_out_folder:
self.out_folder = io.get_detector_output_folder(main_component, self.algorithm, "output")
if config["visualize"]:
self.viz_folder = io.get_detector_output_folder(main_component, self.algorithm, "visualization")
self.subjects_descr = data.subjects_descr
# (3) save this feature config
self.config_path = os.path.join(
io.get_detector_output_folder(main_component, self.algorithm, "run_config"),
"run_config.toml",
)
save_config(config, self.config_path)
def __str__(self):
"""
Returns a description of the feature detector for printing.
Returns:
str: A string representation of the feature detector, including its
components, and the associated algorithm.
"""
return (f"Instance of component {self.components} \n\t" f"algorithm = {self.algorithm} \n\t " f"\n\t").join(
[f"{attr} = {value}" for (attr, value) in self.__dict__.items()]
)
[docs] @abstractmethod
def compute(self):
"""
Compute the components associated to the given feature detector.
This method is responsible for performing the main computation logic of the
feature detector. It should take the method detector output as input, process
it, and generate the desired components.
"""
pass
[docs] @abstractmethod
def post_compute(self):
"""
Post-processing after computation.
This method is intended to perform any necessary post-processing tasks
after the main computation method (compute) has been executed. It is
designed to be overridden in derived classes to provide specific
post-processing logic.
"""
pass
@property
@abstractmethod
def components(self):
"""
Abstract property that returns the components of the feature.
This property should be implemented in the derived classes to specify the
components that the feature detector is associated with.
Returns:
list: A list of strings representing the components associated with the
feature detector.
Raises:
NotImplementedError: If the property is not set in the derived classes.
"""
raise NotImplementedError
@property
@abstractmethod
def algorithm(self):
"""
Abstract property that returns the algorithm of the feature detector.
This property should be implemented in the derived classes to specify the
algorithm that the feature detector is associated with.
Returns:
str: A string representing the algorithm associated with the feature
detector.
Raises:
NotImplementedError: If the property is not set in the derived classes.
"""
raise NotImplementedError
[docs] @abstractmethod
def visualization(self, data):
"""
Abstract method to visualize the output of the method, preferably as a video.
This method is intended to generate a visual representation of the feature
detector's output. The visualization should be saved in the self.viz_folder.
Args:
data (any): The data to be visualized. The type and content of this
parameter depend on the specific implementation of the feature detector.
Returns:
None: This method does not return any value. However, it should save the
visualization in the self.viz_folder.
Raises:
NotImplementedError: If this method is not implemented in the derived
classes.
"""
pass