diff --git a/qim3d/detection/_common_detection_methods.py b/qim3d/detection/_common_detection_methods.py index 0a985d8543eacd858e968963b4a6c8491a011b4d..131ec1e45e40ebcd15c83de260938bcdbd360c59 100644 --- a/qim3d/detection/_common_detection_methods.py +++ b/qim3d/detection/_common_detection_methods.py @@ -15,7 +15,7 @@ def blobs( overlap: float = 0.5, threshold_rel: float = None, exclude_border: bool = False, -) -> np.ndarray: +) -> tuple[np.ndarray, np.ndarray]: """ Extract blobs from a volume using Difference of Gaussian (DoG) method, and retrieve a binary volume with the blobs marked as True diff --git a/qim3d/features/_common_features_methods.py b/qim3d/features/_common_features_methods.py index d7a6960ece0e120661cf66d3b8ff221bf9131f1f..63838ef17634d493fd5de0ec4e806d4568200918 100644 --- a/qim3d/features/_common_features_methods.py +++ b/qim3d/features/_common_features_methods.py @@ -5,7 +5,7 @@ import trimesh import qim3d -def volume(obj: np.ndarray, +def volume(obj: np.ndarray|trimesh.Trimesh, **mesh_kwargs ) -> float: """ @@ -51,7 +51,7 @@ def volume(obj: np.ndarray, return obj.volume -def area(obj: np.ndarray, +def area(obj: np.ndarray|trimesh.Trimesh, **mesh_kwargs ) -> float: """ @@ -96,7 +96,7 @@ def area(obj: np.ndarray, return obj.area -def sphericity(obj: np.ndarray, +def sphericity(obj: np.ndarray|trimesh.Trimesh, **mesh_kwargs ) -> float: """ diff --git a/qim3d/filters/_common_filter_methods.py b/qim3d/filters/_common_filter_methods.py index bc3bab5bba575ea5e01fb847f29e5f9a79971276..59b0be1291ff2f3480b44dc3df524c9794a2c6ed 100644 --- a/qim3d/filters/_common_filter_methods.py +++ b/qim3d/filters/_common_filter_methods.py @@ -43,7 +43,7 @@ class FilterBase: self.kwargs = kwargs class Gaussian(FilterBase): - def __call__(self, input: np.ndarray): + def __call__(self, input: np.ndarray) -> np.ndarray: """ Applies a Gaussian filter to the input. @@ -57,7 +57,7 @@ class Gaussian(FilterBase): class Median(FilterBase): - def __call__(self, input: np.ndarray): + def __call__(self, input: np.ndarray) -> np.ndarray: """ Applies a median filter to the input. @@ -71,7 +71,7 @@ class Median(FilterBase): class Maximum(FilterBase): - def __call__(self, input: np.ndarray): + def __call__(self, input: np.ndarray) -> np.ndarray: """ Applies a maximum filter to the input. @@ -85,7 +85,7 @@ class Maximum(FilterBase): class Minimum(FilterBase): - def __call__(self, input: np.ndarray): + def __call__(self, input: np.ndarray) -> np.ndarray: """ Applies a minimum filter to the input. @@ -98,7 +98,7 @@ class Minimum(FilterBase): return minimum(input, dask=self.dask, chunks=self.chunks, **self.kwargs) class Tophat(FilterBase): - def __call__(self, input: np.ndarray): + def __call__(self, input: np.ndarray) -> np.ndarray: """ Applies a tophat filter to the input. diff --git a/qim3d/generate/_aggregators.py b/qim3d/generate/_aggregators.py index 9d10df37e4efb270ac539e7ff4c3f22cf7cd6b06..262b047ef703ef379432f4a18441617cf5475613 100644 --- a/qim3d/generate/_aggregators.py +++ b/qim3d/generate/_aggregators.py @@ -142,7 +142,7 @@ def noise_object_collection( object_shape: str = None, seed: int = 0, verbose: bool = False, -) -> tuple[np.ndarray, object]: +) -> tuple[np.ndarray, np.ndarray]: """ Generate a 3D volume of multiple synthetic objects using Perlin noise. diff --git a/qim3d/gui/annotation_tool.py b/qim3d/gui/annotation_tool.py index 1ecbc5d30ed5336ab810ae12beb3b1dd655b7ab3..5ebae5f76cefe4d983f9dd1ac1dd24e5127feb62 100644 --- a/qim3d/gui/annotation_tool.py +++ b/qim3d/gui/annotation_tool.py @@ -55,7 +55,7 @@ class Interface(BaseInterface): self.masks_rgb = None self.temp_files = [] - def get_result(self): + def get_result(self) -> dict: # Get the temporary files from gradio temp_path_list = [] for filename in os.listdir(self.temp_dir): @@ -95,13 +95,13 @@ class Interface(BaseInterface): except FileNotFoundError: files = None - def create_preview(self, img_editor: gr.ImageEditor): + def create_preview(self, img_editor: gr.ImageEditor) -> np.ndarray: background = img_editor["background"] masks = img_editor["layers"][0] overlay_image = overlay_rgb_images(background, masks) return overlay_image - def cerate_download_list(self, img_editor: gr.ImageEditor): + def cerate_download_list(self, img_editor: gr.ImageEditor) -> list[str]: masks_rgb = img_editor["layers"][0] mask_threshold = 200 # This value is based diff --git a/qim3d/gui/data_explorer.py b/qim3d/gui/data_explorer.py index ecabeb81135bdbf1d4b0f64c184be62af3dbbf65..ee3ba16d5a90f3facbebc8580b906e52c745bc50 100644 --- a/qim3d/gui/data_explorer.py +++ b/qim3d/gui/data_explorer.py @@ -20,6 +20,7 @@ import os import re import gradio as gr +import matplotlib.figure import matplotlib.pyplot as plt import numpy as np import outputformat as ouf @@ -29,6 +30,8 @@ from qim3d.utils._logger import log from qim3d.utils import _misc from qim3d.gui.interface import BaseInterface +from typing import Callable, Any, Dict +import matplotlib class Interface(BaseInterface): @@ -271,7 +274,7 @@ class Interface(BaseInterface): operations.change(fn=self.show_results, inputs = operations, outputs = results) cmap.change(fn=self.run_operations, inputs = pipeline_inputs, outputs = pipeline_outputs) - def update_explorer(self, new_path): + def update_explorer(self, new_path: str): new_path = os.path.expanduser(new_path) # In case we have a directory @@ -367,7 +370,7 @@ class Interface(BaseInterface): except Exception as error_message: self.error_message = F"Error when loading data: {error_message}" - def run_operations(self, operations, *args): + def run_operations(self, operations: list[str], *args) -> list[Dict[str, Any]]: outputs = [] self.calculated_operations = [] for operation in self.all_operations: @@ -411,7 +414,7 @@ class Interface(BaseInterface): case _: raise NotImplementedError(F"Operation '{operation} is not defined") - def show_results(self, operations): + def show_results(self, operations: list[str]) -> list[Dict[str, Any]]: update_list = [] for operation in self.all_operations: if operation in operations and operation in self.calculated_operations: @@ -426,7 +429,7 @@ class Interface(BaseInterface): # ####################################################### - def create_img_fig(self, img, **kwargs): + def create_img_fig(self, img: np.ndarray, **kwargs) -> matplotlib.figure.Figure: fig, ax = plt.subplots(figsize=(self.figsize, self.figsize)) ax.imshow(img, interpolation="nearest", **kwargs) @@ -437,8 +440,8 @@ class Interface(BaseInterface): return fig - def update_slice_wrapper(self, letter): - def update_slice(position_slider:float, cmap:str): + def update_slice_wrapper(self, letter: str) -> Callable[[float, str], Dict[str, Any]]: + def update_slice(position_slider: float, cmap:str) -> Dict[str, Any]: """ position_slider: float from gradio slider, saying which relative slice we want to see cmap: string gradio drop down menu, saying what cmap we want to use for display @@ -465,7 +468,7 @@ class Interface(BaseInterface): return gr.update(value = fig_img, label = f"{letter} Slice: {slice_index}", visible = True) return update_slice - def vol_histogram(self, nbins, min_value, max_value): + def vol_histogram(self, nbins: int, min_value: float, max_value: float) -> tuple[np.ndarray, np.ndarray]: # Start histogram vol_hist = np.zeros(nbins) @@ -478,7 +481,7 @@ class Interface(BaseInterface): return vol_hist, bin_edges - def plot_histogram(self): + def plot_histogram(self) -> matplotlib.figure.Figure: # The Histogram needs results from the projections if not self.projections_calculated: _ = self.get_projections() @@ -498,7 +501,7 @@ class Interface(BaseInterface): return fig - def create_projections_figs(self): + def create_projections_figs(self) -> tuple[matplotlib.figure.Figure, matplotlib.figure.Figure]: if not self.projections_calculated: projections = self.get_projections() self.max_projection = projections[0] @@ -519,7 +522,7 @@ class Interface(BaseInterface): self.projections_calculated = True return max_projection_fig, min_projection_fig - def get_projections(self): + def get_projections(self) -> tuple[np.ndarray, np.ndarray]: # Create arrays for iteration max_projection = np.zeros(np.shape(self.vol[0])) min_projection = np.ones(np.shape(self.vol[0])) * float("inf") diff --git a/qim3d/gui/interface.py b/qim3d/gui/interface.py index 0241ce32f382a5168baac77fdcdc70e3efb150e0..36d820dde8ab24ac13c94590ae1f69c542f1a841 100644 --- a/qim3d/gui/interface.py +++ b/qim3d/gui/interface.py @@ -6,6 +6,7 @@ import gradio as gr from .qim_theme import QimTheme import qim3d.gui +import numpy as np # TODO: when offline it throws an error in cli @@ -51,7 +52,7 @@ class BaseInterface(ABC): def change_visibility(self, is_visible: bool): return gr.update(visible = is_visible) - def launch(self, img=None, force_light_mode: bool = True, **kwargs): + def launch(self, img: np.ndarray = None, force_light_mode: bool = True, **kwargs): """ img: If None, user can upload image after the interface is launched. If defined, the interface will be launched with the image already there @@ -76,7 +77,7 @@ class BaseInterface(ABC): **kwargs, ) - def clear(self): + def clear(self) -> None: """Used to reset outputs with the clear button""" return None diff --git a/qim3d/gui/layers2d.py b/qim3d/gui/layers2d.py index d3ba41644b3a7b67c294d6e39047f8efcf41324c..afb8832f18b4236b0d94caf2728c614c4b2664ea 100644 --- a/qim3d/gui/layers2d.py +++ b/qim3d/gui/layers2d.py @@ -28,6 +28,7 @@ from .interface import BaseInterface from qim3d.processing import overlay_rgb_images, segment_layers, get_lines from qim3d.io import load from qim3d.viz._layers2d import image_with_lines +from typing import Dict, Any #TODO figure out how not update anything and go through processing when there are no data loaded # So user could play with the widgets but it doesnt throw error @@ -302,14 +303,14 @@ class Interface(BaseInterface): - def change_plot_type(self, plot_type: str, ): + def change_plot_type(self, plot_type: str, ) -> tuple[Dict[str, Any], Dict[str, Any]]: self.plot_type = plot_type if plot_type == 'Segmentation lines': return gr.update(visible = False), gr.update(visible = True) else: return gr.update(visible = True), gr.update(visible = False) - def change_plot_size(self, x_check: int, y_check: int, z_check: int): + def change_plot_size(self, x_check: int, y_check: int, z_check: int) -> tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]: """ Based on how many plots are we displaying (controlled by checkboxes in the bottom) we define also their height because gradio doesn't do it automatically. The values of heights were set just by eye. diff --git a/qim3d/gui/local_thickness.py b/qim3d/gui/local_thickness.py index ad837b42934ac8fa49477cf19773517ccbdbede9..461f3e1774fedb3ac61e0f916b3c72bdafff6f92 100644 --- a/qim3d/gui/local_thickness.py +++ b/qim3d/gui/local_thickness.py @@ -47,7 +47,7 @@ from qim3d.gui.interface import InterfaceWithExamples class Interface(InterfaceWithExamples): def __init__(self, - img = None, + img: np.ndarray = None, verbose:bool = False, plot_height:int = 768, figsize:int = 6):