Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimizations #46

Merged
merged 14 commits into from
Jun 22, 2024
14 changes: 2 additions & 12 deletions src/napari_ndev/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,13 @@
except ImportError:
__version__ = "unknown"

from . import helpers
from ._apoc_container import ApocContainer
from ._apoc_feature_stack import ApocFeatureStack
from ._plate_mapper import PlateMapper
from ._rescale_by import RescaleBy
from ._utilities_container import UtilitiesContainer
from ._workflow_container import WorkflowContainer
from .helpers import (
check_for_missing_files,
get_channel_names,
get_directory_and_files,
get_squeezed_dim_order,
setup_logger,
)

__all__ = [
"WorkflowContainer",
Expand All @@ -24,9 +18,5 @@
"ApocFeatureStack",
"RescaleBy",
"PlateMapper",
"get_directory_and_files",
"check_for_missing_files",
"setup_logger",
"get_squeezed_dim_order",
"get_channel_names",
"helpers",
]
50 changes: 34 additions & 16 deletions src/napari_ndev/_apoc_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,8 @@
from enum import Enum
from typing import TYPE_CHECKING

import apoc
import numpy as np
import pandas as pd
import pyclesperanto_prototype as cle
from aicsimageio import AICSImage
from aicsimageio.writers import OmeTiffWriter
from magicgui.widgets import (
CheckBox,
ComboBox,
Expand All @@ -24,13 +20,19 @@
Table,
create_widget,
)
from napari import layers
from pyclesperanto_prototype import set_wait_for_kernel_finish

from napari_ndev import helpers

if TYPE_CHECKING:
import napari

# Lazy Imports:
# from aicsimageio import AICSImage
# from aicsimageio.writers import OmeTiffWriter
# from napari import layers
# import apoc


class ApocContainer(Container):
"""
Expand Down Expand Up @@ -155,6 +157,15 @@ def __init__(
# viewer = napari_viewer
):
super().__init__()

##############################
# Lazy Imports
##############################
import apoc
from napari.layers import Image as ImageLayer

self.apoc = apoc

##############################
# Attributes
##############################
Expand Down Expand Up @@ -250,7 +261,7 @@ def __init__(

def current_layers(_):
return [
x for x in self._viewer.layers if isinstance(x, layers.Image)
x for x in self._viewer.layers if isinstance(x, ImageLayer)
]

self._image_layer = Select(
Expand Down Expand Up @@ -308,6 +319,8 @@ def current_layers(_):
self._predict_image_layer.clicked.connect(self.image_predict)

def _update_metadata_from_file(self):
from aicsimageio import AICSImage

_, files = helpers.get_directory_and_files(self._image_directory.value)
img = AICSImage(files[0])
self._image_channels.choices = helpers.get_channel_names(img)
Expand Down Expand Up @@ -389,20 +402,20 @@ def _get_feature_set(self):
if self._predefined_features.value.value == 1:
return self._custom_features.value
else:
return apoc.PredefinedFeatureSet[
return self.apoc.PredefinedFeatureSet[
self._predefined_features.value.name
].value

def _get_training_classifier_instance(self):
if self._classifier_type.value == "PixelClassifier":
return apoc.PixelClassifier(
return self.apoc.PixelClassifier(
opencl_filename=self._classifier_file.value,
max_depth=self._max_depth.value,
num_ensembles=self._num_trees.value,
)

if self._classifier_type.value == "ObjectSegmenter":
return apoc.ObjectSegmenter(
return self.apoc.ObjectSegmenter(
opencl_filename=self._classifier_file.value,
positive_class_identifier=self._positive_class_id.value,
max_depth=self._max_depth.value,
Expand All @@ -420,6 +433,8 @@ def _get_channel_image(self, img, channel_index_list):
return channel_img

def batch_train(self):
from aicsimageio import AICSImage

image_directory, image_files = helpers.get_directory_and_files(
self._image_directory.value
)
Expand All @@ -442,14 +457,14 @@ def batch_train(self):
)

# https://github.com/clEsperanto/pyclesperanto_prototype/issues/163
cle.set_wait_for_kernel_finish(True)
set_wait_for_kernel_finish(True)

self._progress_bar.label = f"Training on {len(image_files)} Images"
self._progress_bar.value = 0
self._progress_bar.max = len(image_files)

if not self._continue_training:
apoc.erase_classifier(self._classifier_file.value)
self.apoc.erase_classifier(self._classifier_file.value)

custom_classifier = self._get_training_classifier_instance()
feature_set = self._get_feature_set()
Expand Down Expand Up @@ -506,10 +521,10 @@ def image_train(self):
label = self._label_layer.value.data

# https://github.com/clEsperanto/pyclesperanto_prototype/issues/163
cle.set_wait_for_kernel_finish(True)
set_wait_for_kernel_finish(True)

if not self._continue_training:
apoc.erase_classifier(self._classifier_file.value)
self.apoc.erase_classifier(self._classifier_file.value)

custom_classifier = self._get_training_classifier_instance()
feature_set = self._get_feature_set()
Expand All @@ -535,6 +550,9 @@ def _get_prediction_classifier_instance(self):
return None

def batch_predict(self):
from aicsimageio import AICSImage
from aicsimageio.writers import OmeTiffWriter

image_directory, image_files = helpers.get_directory_and_files(
dir=self._image_directory.value,
)
Expand All @@ -552,7 +570,7 @@ def batch_predict(self):
)

# https://github.com/clEsperanto/pyclesperanto_prototype/issues/163
cle.set_wait_for_kernel_finish(True)
set_wait_for_kernel_finish(True)

self._progress_bar.label = f"Predicting {len(image_files)} Images"
self._progress_bar.value = 0
Expand Down Expand Up @@ -583,7 +601,7 @@ def batch_predict(self):
self._progress_bar.value = idx + 1
continue

save_data = cle.pull(result)
save_data = np.asarray(result)
if save_data.max() > 65535:
save_data = save_data.astype(np.int32)
else:
Expand All @@ -607,7 +625,7 @@ def image_predict(self):
layer_name = self._image_layer.value[0].name
print(f"Predicting {layer_name}")
# https://github.com/clEsperanto/pyclesperanto_prototype/issues/163
cle.set_wait_for_kernel_finish(True)
set_wait_for_kernel_finish(True)

image_list = [image.data for image in self._image_layer.value]
image_stack = np.stack(image_list, axis=0)
Expand Down
5 changes: 3 additions & 2 deletions src/napari_ndev/_apoc_feature_stack.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from typing import TYPE_CHECKING

import apoc
from magicgui.widgets import (
CheckBox,
Container,
Expand Down Expand Up @@ -113,8 +112,10 @@ def process_feature(prefix, input_str):
self._feature_string.value = " ".join(feature_list)

def layer_to_feature_stack(self):
from apoc import generate_feature_stack

image = self._image_layer.value.data
feature_stack = apoc.generate_feature_stack(
feature_stack = generate_feature_stack(
image, self._feature_string.value
)

Expand Down
7 changes: 4 additions & 3 deletions src/napari_ndev/_plate_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import string

import pandas as pd
import seaborn as sns


class PlateMapper:
Expand Down Expand Up @@ -145,14 +144,16 @@ def get_styled_plate_map(self, treatment, palette="colorblind"):
map DataFrame with different background colors for each unique
value.
"""
from seaborn import color_palette

self.plate_map_pivot = self.get_pivoted_plate_map(treatment)

unique_values = pd.unique(self.plate_map_pivot.values.flatten())
unique_values = unique_values[pd.notna(unique_values)]

color_palette = sns.color_palette(palette).as_hex()
color_palette_hex = color_palette(palette).as_hex()
# Create an infinite iterator that cycles through the palette
palette_cycle = itertools.cycle(color_palette)
palette_cycle = itertools.cycle(color_palette_hex)
# Use next() to get the next color
color_dict = {value: next(palette_cycle) for value in unique_values}

Expand Down
6 changes: 3 additions & 3 deletions src/napari_ndev/_tests/test_apoc_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def test_batch_predict_normal_operation(make_napari_viewer, tmp_path):
container._image_directory.value = image_directory
container._output_directory.value = output_directory
# container._image_channels.value = ["IBA1"] # images need fixed
container._image_channels.value = ['Labels']
container._image_channels.value = ["Labels"]
container._classifier_file.value = classifier

container.batch_predict()
Expand All @@ -210,7 +210,7 @@ def test_update_metadata_from_file(make_napari_viewer):
)

# Mock the AICSImage class to return sample channel names
with patch("napari_ndev._apoc_container.AICSImage") as mock_AICSImage:
with patch("aicsimageio.AICSImage") as mock_AICSImage:
mock_AICSImage.return_value.channel_names = ["C0", "C1", "C2"]

# Call the _update_metadata_from_file method
Expand All @@ -236,7 +236,7 @@ def test_batch_predict_exception_logging(make_napari_viewer, tmp_path):
container._image_directory.value = image_directory
container._output_directory.value = output_directory
# container._image_channels.value = ["IBA1"] # fix these images
container._image_channels.value = ['Labels']
container._image_channels.value = ["Labels"]

# Mock the custom_classifier.predict() method to raise an exception
class MockClassifier:
Expand Down
29 changes: 20 additions & 9 deletions src/napari_ndev/_utilities_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
from typing import TYPE_CHECKING, List, Union

import numpy as np
from aicsimageio import AICSImage
from aicsimageio.types import PhysicalPixelSizes
from aicsimageio.writers import OmeTiffWriter
from magicgui.widgets import (
CheckBox,
Container,
Expand All @@ -18,12 +15,12 @@
TextEdit,
create_widget,
)
from napari import layers

from napari_ndev import helpers

if TYPE_CHECKING:
import napari
from napari.layers import Image as ImageLayer


class UtilitiesContainer(Container):
Expand Down Expand Up @@ -117,6 +114,7 @@ def __init__(
viewer: "napari.viewer.Viewer",
):
super().__init__()

##############################
# Attributes
##############################
Expand Down Expand Up @@ -182,8 +180,10 @@ def __init__(
# Use a function for layer inputs so that it is constantly updated
# when the dependency changes
def current_layers(_):
from napari.layers import Image as ImageLayer

return [
x for x in self._viewer.layers if isinstance(x, layers.Image)
x for x in self._viewer.layers if isinstance(x, ImageLayer)
]

self._image_layer = Select(
Expand Down Expand Up @@ -285,13 +285,14 @@ def _update_metadata(self, img):
self._physical_pixel_sizes_x.value = img.physical_pixel_sizes.X

def update_metadata_from_file(self):
from aicsimageio import AICSImage

img = AICSImage(self._files.value[0])
self._img = img
self._update_metadata(img)
self._save_name.value = str(self._files.value[0].stem + ".tiff")
self._scenes.value = len(img.scenes)


def update_metadata_from_layer(self):
try:
img = self._image_layer.value[0].metadata["aicsimage"]
Expand All @@ -308,8 +309,10 @@ def concatenate_images(
concatenate_files: bool,
files: List[Union[str, Path]],
concatenate_layers: bool,
layers: List[layers.Image],
layers: List["ImageLayer"],
):
from aicsimageio import AICSImage

array_list = []
if concatenate_files:
for file in files:
Expand Down Expand Up @@ -338,6 +341,8 @@ def concatenate_images(

@property
def p_sizes(self):
from aicsimageio.types import PhysicalPixelSizes

return PhysicalPixelSizes(
self._physical_pixel_sizes_z.value,
self._physical_pixel_sizes_y.value,
Expand All @@ -357,6 +362,8 @@ def _common_save_logic(
channel_names: List[str],
layer: str,
) -> None:
from aicsimageio.writers import OmeTiffWriter

# AICSImage does not allow saving labels as np.int64
# napari generates labels differently depending on the OS
# so we need to convert to np.int32 in case np.int64 generated
Expand Down Expand Up @@ -393,6 +400,8 @@ def _common_save_logic(
return

def save_scenes_ome_tiff(self) -> None:
from aicsimageio import AICSImage

img = AICSImage(self._files.value[0])
scenes = self._scenes_to_extract.value
scenes_list = ast.literal_eval(scenes) if scenes else None
Expand All @@ -401,8 +410,10 @@ def save_scenes_ome_tiff(self) -> None:
for scene in scenes_list:
img.set_scene(scene)

img_save_name = (f'{self._save_name.value.split(".")[0]}'
f'_scene_{img.current_scene}.ome.tiff')
img_save_name = (
f'{self._save_name.value.split(".")[0]}'
f"_scene_{img.current_scene}.ome.tiff"
)
img_save_loc = save_directory / img_save_name

# get channel names from widget if truthy
Expand Down
Loading
Loading