From 59887c385e26f645b54e98dc8dc875f4fa75ebfa Mon Sep 17 00:00:00 2001 From: TimMonko <47310455+TimMonko@users.noreply.github.com> Date: Fri, 10 Feb 2023 18:10:33 -0600 Subject: [PATCH 1/3] add batch training and predict --- setup.cfg | 6 ++ src/napari_ndev/__init__.py | 2 + src/napari_ndev/_widget.py | 125 ++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+) diff --git a/setup.cfg b/setup.cfg index c3e46f3..7d6e198 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,6 +37,12 @@ install_requires = magicgui qtpy aicsimageio + enum + napari + apoc + os + pyclesperanto_prototype + dask python_requires = >=3.8 include_package_data = True diff --git a/src/napari_ndev/__init__.py b/src/napari_ndev/__init__.py index 0992268..cb6f559 100644 --- a/src/napari_ndev/__init__.py +++ b/src/napari_ndev/__init__.py @@ -7,4 +7,6 @@ __all__ = [ "batch_annotator", + "batch_training", + "batch_predict", ] diff --git a/src/napari_ndev/_widget.py b/src/napari_ndev/_widget.py index ada4096..d13cf19 100644 --- a/src/napari_ndev/_widget.py +++ b/src/napari_ndev/_widget.py @@ -1,12 +1,17 @@ """ neural development (nDev) widget collection """ +import os import pathlib import string +from enum import Enum from typing import TYPE_CHECKING +import apoc +import dask.array as da import napari import numpy as np +import pyclesperanto_prototype as cle from aicsimageio import AICSImage from magicgui import magic_factory from napari import layers @@ -83,3 +88,123 @@ def saver(image_type, folder_suffix, save_suffix_str): saver(image, "_images", save_suffix) saver(labels, "_labels", save_suffix) return "Saved Successfully" + + +channel_nums = [0, 1, 2, 3, 4] +PDFS = Enum("PDFS", apoc.PredefinedFeatureSet._member_names_) + + +@magic_factory( + auto_call=False, + call_button="Batch Train", + result_widget=True, + image_directory=dict(widget_type="FileEdit", mode="d"), + label_directory=dict(widget_type="FileEdit", mode="d"), + predefined_features=dict(widget_type="ComboBox", choices=PDFS), + channel_list=dict(widget_type="Select", choices=channel_nums), +) +def batch_training( + image_directory=pathlib.Path(), + label_directory=pathlib.Path(), + cl_filename: str = "classifier.cl", + predefined_features=PDFS(1), + custom_features: str = None, + channel_list: int = 0, + img_dims: str = "TYX", + label_dims: str = "ZYX", +): + image_list = os.listdir(image_directory) + + apoc.erase_classifier(cl_filename) + custom_classifier = apoc.PixelClassifier(opencl_filename=cl_filename) + + for file in image_list: + + image_stack = [] + img = AICSImage(image_directory / file) + + def channel_image(img, dims: str, channel: str or int): + if isinstance(channel, str): + channel_index = img.channel_names.index(channel) + elif isinstance(channel, int): + channel_index = channel + channel_img = img.get_image_data(dims, C=channel_index) + return channel_img + + for channels in channel_list: + ch_img = channel_image(img=img, dims=img_dims, channel=channels) + image_stack.append(ch_img) + + dask_stack = da.stack(image_stack, axis=0) + + lbl = AICSImage(label_directory / file) + labels = channel_image(img=lbl, dims=label_dims, channel=0) + + if predefined_features.value == 1: + print("custom") + feature_set = custom_features + + else: + print("predefined") + feature_set = apoc.PredefinedFeatureSet[ + predefined_features.name + ].value + + custom_classifier.train( + features=feature_set, + image=dask_stack, + ground_truth=labels, + continue_training=True, + ) + + feature_importances = custom_classifier.feature_importances() + print("success") + # return pd.Series(feature_importances).plot.bar() + + return feature_importances + + +@magic_factory( + auto_call=False, + call_button="Batch Predict", + image_directory=dict(widget_type="FileEdit", mode="d"), + result_directory=dict(widget_type="FileEdit", mode="d"), + classifier_path=dict(widget_type="FileEdit", mode="r"), + channel_list=dict(widget_type="Select", choices=channel_nums), +) +def batch_predict( + image_directory=pathlib.Path(), + result_directory=pathlib.Path(), + classifier_path=pathlib.Path(), + channel_list: int = 0, + img_dims: str = "TYX", +): + image_list = os.listdir(image_directory) + custom_classifier = apoc.PixelClassifier(opencl_filename=classifier_path) + + for file in image_list: + # print('started predicting: ', file) + image_stack = [] + img = AICSImage(image_directory / file) + + def channel_image(img, dims: str, channel: str or int): + if isinstance(channel, str): + channel_index = img.channel_names.index(channel) + elif isinstance(channel, int): + channel_index = channel + channel_img = img.get_image_data(dims, C=channel_index) + return channel_img + + for channels in channel_list: + ch_img = channel_image(img=img, dims=img_dims, channel=channels) + image_stack.append(ch_img) + + dask_stack = da.stack(image_stack, axis=0) + + result = custom_classifier.predict( + image=dask_stack, + ) + + AICSImage(cle.pull(result)).save(uri=result_directory / file) + + return result From 3fd7fa4a0a984c3bd323d952a4db938ad3e649e9 Mon Sep 17 00:00:00 2001 From: TimMonko <47310455+TimMonko@users.noreply.github.com> Date: Fri, 10 Feb 2023 18:33:01 -0600 Subject: [PATCH 2/3] fix manifest --- setup.cfg | 2 -- src/napari_ndev/__init__.py | 2 +- src/napari_ndev/_tests/test_widget.py | 40 ++++++++++++++++++++++++++- src/napari_ndev/napari.yaml | 12 +++++++- 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/setup.cfg b/setup.cfg index 7d6e198..ca0ae2a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,10 +37,8 @@ install_requires = magicgui qtpy aicsimageio - enum napari apoc - os pyclesperanto_prototype dask diff --git a/src/napari_ndev/__init__.py b/src/napari_ndev/__init__.py index cb6f559..113a099 100644 --- a/src/napari_ndev/__init__.py +++ b/src/napari_ndev/__init__.py @@ -3,7 +3,7 @@ except ImportError: __version__ = "unknown" -from ._widget import batch_annotator +from ._widget import batch_annotator, batch_predict, batch_training __all__ = [ "batch_annotator", diff --git a/src/napari_ndev/_tests/test_widget.py b/src/napari_ndev/_tests/test_widget.py index 5292b0a..2e3aa67 100644 --- a/src/napari_ndev/_tests/test_widget.py +++ b/src/napari_ndev/_tests/test_widget.py @@ -1,6 +1,6 @@ import numpy as np -from napari_ndev import batch_annotator +from napari_ndev import batch_annotator, batch_predict, batch_training # make_napari_viewer is a pytest fixture that returns a napari viewer object @@ -22,3 +22,41 @@ def test_batch_annotator(make_napari_viewer, capsys): # read captured output and check that it's as we expected # captured = capsys.readouterr() # assert captured.out == "napari has 1 layers\n" + + +def test_batch_training(make_napari_viewer, capsys): + # make viewer and add an image layer using our fixture + viewer = make_napari_viewer() + test_image = np.random.random((100, 100)) + viewer.add_image(test_image) + test_thresh = test_image > 1 + viewer.add_labels(test_thresh) + + # create our widget, passing in the viewer + my_widget = batch_training() + my_widget() + # call our widget method + # my_widget._on_click() + + # read captured output and check that it's as we expected + # captured = capsys.readouterr() + # assert captured.out == "napari has 1 layers\n" + + +def test_batch_predict(make_napari_viewer, capsys): + # make viewer and add an image layer using our fixture + viewer = make_napari_viewer() + test_image = np.random.random((100, 100)) + viewer.add_image(test_image) + test_thresh = test_image > 1 + viewer.add_labels(test_thresh) + + # create our widget, passing in the viewer + my_widget = batch_predict() + my_widget() + # call our widget method + # my_widget._on_click() + + # read captured output and check that it's as we expected + # captured = capsys.readouterr() + # assert captured.out == "napari has 1 layers\n" diff --git a/src/napari_ndev/napari.yaml b/src/napari_ndev/napari.yaml index f442cf1..25f19e3 100644 --- a/src/napari_ndev/napari.yaml +++ b/src/napari_ndev/napari.yaml @@ -4,8 +4,18 @@ contributions: commands: - id: napari-ndev.make_batch_annotator python_name: napari_ndev._widget:batch_annotator - title: Make Batch Annotator + title: Make Batch Annotator Widget + - id: napari-ndev.make_batch_training + python_name: napari_ndev._widget:batch_training + title: Make Batch Training Widget + - id: napari-ndev.make_batch_predict + python_name: napari_ndev._widget:batch_predict + title: Make Batch Predict Widget widgets: - command: napari-ndev.make_batch_annotator display_name: Batch Annotator + - command: napari-ndev.make_batch_training + display_name: Batch APOC Training + - command: napari-ndev.make_batch_predict + display_name: Batch APOC Predict From 01bd63cdfd5b6fee73d21c0aabca8fdb9f38e420 Mon Sep 17 00:00:00 2001 From: TimMonko <47310455+TimMonko@users.noreply.github.com> Date: Fri, 10 Feb 2023 18:37:58 -0600 Subject: [PATCH 3/3] test removal --- src/napari_ndev/_tests/test_widget.py | 74 +++++++++++++-------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/napari_ndev/_tests/test_widget.py b/src/napari_ndev/_tests/test_widget.py index 2e3aa67..3811800 100644 --- a/src/napari_ndev/_tests/test_widget.py +++ b/src/napari_ndev/_tests/test_widget.py @@ -1,6 +1,6 @@ import numpy as np -from napari_ndev import batch_annotator, batch_predict, batch_training +from napari_ndev import batch_annotator # , batch_predict, batch_training # make_napari_viewer is a pytest fixture that returns a napari viewer object @@ -24,39 +24,39 @@ def test_batch_annotator(make_napari_viewer, capsys): # assert captured.out == "napari has 1 layers\n" -def test_batch_training(make_napari_viewer, capsys): - # make viewer and add an image layer using our fixture - viewer = make_napari_viewer() - test_image = np.random.random((100, 100)) - viewer.add_image(test_image) - test_thresh = test_image > 1 - viewer.add_labels(test_thresh) - - # create our widget, passing in the viewer - my_widget = batch_training() - my_widget() - # call our widget method - # my_widget._on_click() - - # read captured output and check that it's as we expected - # captured = capsys.readouterr() - # assert captured.out == "napari has 1 layers\n" - - -def test_batch_predict(make_napari_viewer, capsys): - # make viewer and add an image layer using our fixture - viewer = make_napari_viewer() - test_image = np.random.random((100, 100)) - viewer.add_image(test_image) - test_thresh = test_image > 1 - viewer.add_labels(test_thresh) - - # create our widget, passing in the viewer - my_widget = batch_predict() - my_widget() - # call our widget method - # my_widget._on_click() - - # read captured output and check that it's as we expected - # captured = capsys.readouterr() - # assert captured.out == "napari has 1 layers\n" +# def test_batch_training(make_napari_viewer, capsys): +# # make viewer and add an image layer using our fixture +# viewer = make_napari_viewer() +# test_image = np.random.random((100, 100)) +# viewer.add_image(test_image) +# test_thresh = test_image > 1 +# viewer.add_labels(test_thresh) + +# # create our widget, passing in the viewer +# my_widget = batch_training() +# my_widget() +# # call our widget method +# # my_widget._on_click() + +# # read captured output and check that it's as we expected +# # captured = capsys.readouterr() +# # assert captured.out == "napari has 1 layers\n" + + +# def test_batch_predict(make_napari_viewer, capsys): +# # make viewer and add an image layer using our fixture +# viewer = make_napari_viewer() +# test_image = np.random.random((100, 100)) +# viewer.add_image(test_image) +# test_thresh = test_image > 1 +# viewer.add_labels(test_thresh) + +# # create our widget, passing in the viewer +# my_widget = batch_predict() +# my_widget() +# # call our widget method +# # my_widget._on_click() + +# # read captured output and check that it's as we expected +# # captured = capsys.readouterr() +# # assert captured.out == "napari has 1 layers\n"