From 3724c8b41b0f1055adcd513b0d4d77d626cd519e Mon Sep 17 00:00:00 2001 From: mcquin Date: Thu, 15 Jun 2017 10:05:50 -0400 Subject: [PATCH 01/18] Pixel classification predictions using Ilastik classifier --- predict.py | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 predict.py diff --git a/predict.py b/predict.py new file mode 100644 index 00000000..c7115dba --- /dev/null +++ b/predict.py @@ -0,0 +1,114 @@ +import os +import subprocess +import tempfile + +import h5py # HDF5 is Ilastik's preferred file format +import logging + +import cellprofiler.image +import cellprofiler.module +import cellprofiler.setting + +logger = logging.getLogger(__name__) + +__doc__ = """ +Use an Ilastik pixel classifier to generate a probability image. Each channel of represents the probability of the +pixels in the image belong to a particular class. Use ColorToGray to separate channels for further processing. For +example, use IdentifyPrimaryObjects on a (single-channel) probability map to generate a segmentation. + +It is recommended that you pre-process any training images with CellProfiler (e.g., RescaleIntensity) and apply the +same pre-processing steps to your analysis pipeline. You should use SaveImages to export training images as 64-bit +TIFFs. +""" + + +class Predict(cellprofiler.module.ImageProcessing): + module_name = "Predict" + + variable_revision_number = 1 + + def create_settings(self): + super(Predict, self).create_settings() + + self.executable = cellprofiler.setting.Pathname( + "Executable", + doc="Ilastik command line executable name, or location if it is not on your path." + ) + + self.project_file = cellprofiler.setting.Pathname( + "Project file", + doc="Path to the project file (*.ilp)." + ) + + def settings(self): + settings = super(Predict, self).settings() + + settings += [ + self.executable, + self.project_file + ] + + return settings + + def visible_settings(self): + visible_settings = super(Predict, self).visible_settings() + + visible_settings += [ + self.executable, + self.project_file + ] + + return visible_settings + + def run(self, workspace): + image = workspace.image_set.get_image(self.x_name.value) + + x_data = image.pixel_data + + fin = tempfile.NamedTemporaryFile(suffix=".h5", delete=False) + + fout = tempfile.NamedTemporaryFile(suffix=".h5", delete=False) + + try: + with h5py.File(fin.name, "w") as f: + f.create_dataset("data", data=x_data) + + fin.close() + + fout.close() + + cmd = [ + self.executable.value, + "--headless", + "--project", self.project_file.value, + "--output_format", "hdf5", + "--export_source", "Probabilities", + "--output_filename_format", fout.name, + fin.name + ] + + subprocess.check_call(cmd) + + with h5py.File(fout.name, "r") as f: + y_data = f["exported_data"].value + + y = cellprofiler.image.Image(y_data) + + workspace.image_set.add(self.y_name.value, y) + + if self.show_window: + workspace.display_data.x_data = x_data + + workspace.display_data.y_data = y_data + + workspace.display_data.dimensions = image.dimensions + except subprocess.CalledProcessError as cpe: + logger.error("Command {} exited with status {}".format(cpe.output, cpe.returncode), cpe) + + raise cpe + except IOError as ioe: + raise ioe + finally: + os.unlink(fin.name) + + os.unlink(fout.name) From 243f17a4ef693cc13c31a987f678fffd62b4ece2 Mon Sep 17 00:00:00 2001 From: karhohs Date: Thu, 17 Aug 2017 10:08:19 -0400 Subject: [PATCH 02/18] add autocontext functionality --- predict.py | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 predict.py diff --git a/predict.py b/predict.py new file mode 100644 index 00000000..352a0ea6 --- /dev/null +++ b/predict.py @@ -0,0 +1,149 @@ +import os +import subprocess +import tempfile + +import h5py # HDF5 is Ilastik's preferred file format +import logging +import skimage + +import cellprofiler.image +import cellprofiler.module +import cellprofiler.setting + +logger = logging.getLogger(__name__) + +__doc__ = """ +Use an Ilastik pixel classifier to generate a probability image. Each channel represents the probability of the +pixels in the image belong to a particular class. Use ColorToGray to separate channels for further processing. For +example, use IdentifyPrimaryObjects on a (single-channel) probability map to generate a segmentation. + +It is recommended that you pre-process any training images with CellProfiler (e.g., RescaleIntensity) and apply the +same pre-processing steps to your analysis pipeline. You should use SaveImages to export training images as 64-bit +TIFFs. +""" + + +class Predict(cellprofiler.module.ImageProcessing): + module_name = "Predict" + + variable_revision_number = 1 + + def create_settings(self): + super(Predict, self).create_settings() + + self.executable = cellprofiler.setting.Pathname( + "Executable", + doc="Ilastik command line executable name, or location if it is not on your path." + ) + + self.project_file = cellprofiler.setting.Pathname( + "Project file", + doc="Path to the project file (*.ilp)." + ) + + self.project_type = cellprofiler.setting.Choice( + "Select method for constructing file names", + [ + "Pixel Classification", + "Autocontext (2-stage)" + ], + "Pixel Classification", + doc=""" +

CellProfiler supports two types of ilastik projects:

+ + """ + ) + + def settings(self): + settings = super(Predict, self).settings() + + settings += [ + self.executable, + self.project_file, + self.project_type + ] + + return settings + + def visible_settings(self): + visible_settings = super(Predict, self).visible_settings() + + visible_settings += [ + self.executable, + self.project_file, + self.project_type + ] + + return visible_settings + + def run(self, workspace): + image = workspace.image_set.get_image(self.x_name.value) + + x_data = image.pixel_data + + fin = tempfile.NamedTemporaryFile(suffix=".h5", delete=False) + + fout = tempfile.NamedTemporaryFile(suffix=".h5", delete=False) + + if self.project_type.value in ["Pixel Classification"]: + + cmd = [ + self.executable.value, + "--headless", + "--project", self.project_file.value, + "--output_format", "hdf5", + "--export_source", "Probabilities", + "--output_filename_format", fout.name, + fin.name + ] + + elif self.project_type.value in ["Autocontext (2-stage)"]: + + x_data = skimage.img_as_ubyte(x_data) # done to meet ilastik demand for UINT8. Might be relaxed in future. + + cmd = [ + self.executable.value, + "--headless", + "--project", self.project_file.value, + "--output_format", "hdf5", + "--export_source", "probabilities stage 2", + "--output_filename_format", fout.name, + fin.name + ] + + try: + with h5py.File(fin.name, "w") as f: + f.create_dataset("data", data=x_data) + + fin.close() + + fout.close() + + subprocess.check_call(cmd) + + with h5py.File(fout.name, "r") as f: + y_data = f["exported_data"].value + + y = cellprofiler.image.Image(y_data) + + workspace.image_set.add(self.y_name.value, y) + + if self.show_window: + workspace.display_data.x_data = x_data + + workspace.display_data.y_data = y_data + + workspace.display_data.dimensions = image.dimensions + except subprocess.CalledProcessError as cpe: + logger.error("Command {} exited with status {}".format(cpe.output, cpe.returncode), cpe) + + raise cpe + except IOError as ioe: + raise ioe + finally: + os.unlink(fin.name) + + os.unlink(fout.name) From fd917f799a30be84b4b4f3e1d487794761504960 Mon Sep 17 00:00:00 2001 From: karhohs Date: Thu, 17 Aug 2017 12:22:58 -0400 Subject: [PATCH 03/18] expand the documentation slightly --- predict.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/predict.py b/predict.py index 352a0ea6..6f369762 100644 --- a/predict.py +++ b/predict.py @@ -15,7 +15,8 @@ __doc__ = """ Use an Ilastik pixel classifier to generate a probability image. Each channel represents the probability of the pixels in the image belong to a particular class. Use ColorToGray to separate channels for further processing. For -example, use IdentifyPrimaryObjects on a (single-channel) probability map to generate a segmentation. +example, use IdentifyPrimaryObjects on a (single-channel) probability map to generate a segmentation. The order of +the channels in ColorToGray is the same as the order of the labels within the Ilastik project. It is recommended that you pre-process any training images with CellProfiler (e.g., RescaleIntensity) and apply the same pre-processing steps to your analysis pipeline. You should use SaveImages to export training images as 64-bit From fee5b6a5bb3affb669b719429708d8461023c012 Mon Sep 17 00:00:00 2001 From: mcquin Date: Fri, 20 Oct 2017 13:48:18 -0400 Subject: [PATCH 04/18] formatting --- predict.py | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/predict.py b/predict.py index 6f369762..d6dc0ec9 100644 --- a/predict.py +++ b/predict.py @@ -12,14 +12,17 @@ logger = logging.getLogger(__name__) -__doc__ = """ +__doc__ = """\ +Predict +======= + Use an Ilastik pixel classifier to generate a probability image. Each channel represents the probability of the -pixels in the image belong to a particular class. Use ColorToGray to separate channels for further processing. For -example, use IdentifyPrimaryObjects on a (single-channel) probability map to generate a segmentation. The order of -the channels in ColorToGray is the same as the order of the labels within the Ilastik project. +pixels in the image belong to a particular class. Use **ColorToGray** to separate channels for further processing. For +example, use **IdentifyPrimaryObjects** on a (single-channel) probability map to generate a segmentation. The order of +the channels in **ColorToGray** is the same as the order of the labels within the Ilastik project. -It is recommended that you pre-process any training images with CellProfiler (e.g., RescaleIntensity) and apply the -same pre-processing steps to your analysis pipeline. You should use SaveImages to export training images as 64-bit +It is recommended that you pre-process any training images with CellProfiler (e.g., **RescaleIntensity**) and apply the +same pre-processing steps to your analysis pipeline. You should use **SaveImages** to export training images as 32-bit TIFFs. """ @@ -49,13 +52,12 @@ def create_settings(self): "Autocontext (2-stage)" ], "Pixel Classification", - doc=""" -

CellProfiler supports two types of ilastik projects:

-
    -
  • Pixel Classification
  • -
  • Autocontext (2-stage)
  • -
- """ + doc="""\ +CellProfiler supports two types of ilastik projects: + +- Pixel Classification +- Autocontext (2-stage) +""" ) def settings(self): @@ -90,7 +92,6 @@ def run(self, workspace): fout = tempfile.NamedTemporaryFile(suffix=".h5", delete=False) if self.project_type.value in ["Pixel Classification"]: - cmd = [ self.executable.value, "--headless", @@ -100,10 +101,8 @@ def run(self, workspace): "--output_filename_format", fout.name, fin.name ] - elif self.project_type.value in ["Autocontext (2-stage)"]: - - x_data = skimage.img_as_ubyte(x_data) # done to meet ilastik demand for UINT8. Might be relaxed in future. + x_data = skimage.img_as_ubyte(x_data) # ilastik requires UINT8. Might be relaxed in future. cmd = [ self.executable.value, From 8fa30d390d3177ab169a5594d62b0a7e9412bcd8 Mon Sep 17 00:00:00 2001 From: mcquin Date: Fri, 20 Oct 2017 13:49:53 -0400 Subject: [PATCH 05/18] refactor duplicate command definition --- predict.py | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/predict.py b/predict.py index d6dc0ec9..3507ff4a 100644 --- a/predict.py +++ b/predict.py @@ -91,28 +91,24 @@ def run(self, workspace): fout = tempfile.NamedTemporaryFile(suffix=".h5", delete=False) + cmd = [ + self.executable.value, + "--headless", + "--project", self.project_file.value, + "--output_format", "hdf5" + ] + if self.project_type.value in ["Pixel Classification"]: - cmd = [ - self.executable.value, - "--headless", - "--project", self.project_file.value, - "--output_format", "hdf5", - "--export_source", "Probabilities", - "--output_filename_format", fout.name, - fin.name - ] + cmd += ["--export_source", "Probabilities"] elif self.project_type.value in ["Autocontext (2-stage)"]: x_data = skimage.img_as_ubyte(x_data) # ilastik requires UINT8. Might be relaxed in future. - cmd = [ - self.executable.value, - "--headless", - "--project", self.project_file.value, - "--output_format", "hdf5", - "--export_source", "probabilities stage 2", - "--output_filename_format", fout.name, - fin.name - ] + cmd = ["--export_source", "probabilities stage 2"] + + cmd += [ + "--output_filename_format", fout.name, + fin.name + ] try: with h5py.File(fin.name, "w") as f: From c4c6d154503a1914a96cc4a87beaf79c6b8e5729 Mon Sep 17 00:00:00 2001 From: mcquin Date: Fri, 20 Oct 2017 15:15:27 -0400 Subject: [PATCH 06/18] Document loading image compatibility with Ilastik --- predict.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/predict.py b/predict.py index 3507ff4a..7598f499 100644 --- a/predict.py +++ b/predict.py @@ -22,8 +22,13 @@ the channels in **ColorToGray** is the same as the order of the labels within the Ilastik project. It is recommended that you pre-process any training images with CellProfiler (e.g., **RescaleIntensity**) and apply the -same pre-processing steps to your analysis pipeline. You should use **SaveImages** to export training images as 32-bit +same pre-processing steps to your analysis pipeline. You can use **SaveImages** to export training images as 32-bit TIFFs. + +Additionally, please ensure CellProfiler is configured to load images in the same format as Ilastik. For example, +if your Ilastik classifier is trained on RGB images, use **NamesAndTypes** to load images as RGB by selecting +"*Color image*" from the *Select the image type* dropdown. If your classifier expects grayscale images, use +**NamesAndTypes** to load images as "*Grayscale image*". """ From 59194afaecc640c3c9277287709d0ac27813f63c Mon Sep 17 00:00:00 2001 From: mcquin Date: Fri, 3 Nov 2017 10:19:39 -0400 Subject: [PATCH 07/18] Documentation updates * Address how to handle rescaling by CellProfiler * Expand documentation for project types --- predict.py | 79 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 20 deletions(-) diff --git a/predict.py b/predict.py index 7598f499..3d319709 100644 --- a/predict.py +++ b/predict.py @@ -2,7 +2,7 @@ import subprocess import tempfile -import h5py # HDF5 is Ilastik's preferred file format +import h5py # HDF5 is ilastik's preferred file format import logging import skimage @@ -16,19 +16,51 @@ Predict ======= -Use an Ilastik pixel classifier to generate a probability image. Each channel represents the probability of the -pixels in the image belong to a particular class. Use **ColorToGray** to separate channels for further processing. For -example, use **IdentifyPrimaryObjects** on a (single-channel) probability map to generate a segmentation. The order of -the channels in **ColorToGray** is the same as the order of the labels within the Ilastik project. - -It is recommended that you pre-process any training images with CellProfiler (e.g., **RescaleIntensity**) and apply the -same pre-processing steps to your analysis pipeline. You can use **SaveImages** to export training images as 32-bit -TIFFs. - -Additionally, please ensure CellProfiler is configured to load images in the same format as Ilastik. For example, -if your Ilastik classifier is trained on RGB images, use **NamesAndTypes** to load images as RGB by selecting -"*Color image*" from the *Select the image type* dropdown. If your classifier expects grayscale images, use -**NamesAndTypes** to load images as "*Grayscale image*". +Use an ilastik pixel classifier to generate a probability image. Each +channel represents the probability of the pixels in the image belong to +a particular class. Use **ColorToGray** to separate channels for further +processing. For example, use **IdentifyPrimaryObjects** on a +(single-channel) probability map to generate a segmentation. The order +of the channels in **ColorToGray** is the same as the order of the +labels within the ilastik project. + +CellProfiler automatically scales grayscale and color images to the +[0.0, 1.0] range on load. Your ilastik classifier should be trained on +images with the same scale as the prediction images. You can ensure +consistent scales by: + +- using **ImageMath** to convert the images loaded by CellProfiler back + to their original scale. Use these settings to rescale an image: + + - **Operation**: *None* + - **Multiply the first image by**: *RESCALE_VALUE* + - **Set values greater than 1 equal to 1?**: *No* + + where *RESCALE_VALUE* is determined by your image data and the value + of *Set intensity range from* in **NamesAndTypes**. For example, the + *RESCALE_VALUE* for 32-bit images rescaled by "*Image bit-depth*" is + 65535 (the maximum value allowed by this data type). Please refer to + the help for the setting *Set intensity range from* in + **NamesAndTypes** for more information. + + This option is best when your training and prediction images do not + require any preprocessing by CellProfiler. + +- preprocessing any training images with CellProfiler (e.g., + **RescaleIntensity**) and applying the same pre-processing steps to + your analysis pipeline. You can use **SaveImages** to export training + images as 32-bit TIFFs. + + This option requires two CellProfiler pipelines, but is effective + when your training and prediction images require preprocessing by + CellProfiler. + +Additionally, please ensure CellProfiler is configured to load images in +the same format as ilastik. For example, if your ilastik classifier is +trained on RGB images, use **NamesAndTypes** to load images as RGB by +selecting "*Color image*" from the *Select the image type* dropdown. If +your classifier expects grayscale images, use **NamesAndTypes** to load +images as "*Grayscale image*". """ @@ -42,26 +74,33 @@ def create_settings(self): self.executable = cellprofiler.setting.Pathname( "Executable", - doc="Ilastik command line executable name, or location if it is not on your path." + doc="ilastik command line executable name, or location if it is not on your path." ) self.project_file = cellprofiler.setting.Pathname( "Project file", - doc="Path to the project file (*.ilp)." + doc="Path to the project file (\*.ilp)." ) self.project_type = cellprofiler.setting.Choice( - "Select method for constructing file names", + "Select the project type", [ "Pixel Classification", "Autocontext (2-stage)" ], "Pixel Classification", doc="""\ -CellProfiler supports two types of ilastik projects: +Select the project type which matches the project file specified by +*Project file*. CellProfiler supports two types of ilastik projects: + +- *Pixel Classification*: Classify the pixels of an image given user + annotations. `Read more`_. + +- *Autocontext (2-stage)*: Perform pixel classification in multiple + stages, sharing predictions between stages to improve results. `Read + more `__. -- Pixel Classification -- Autocontext (2-stage) +.. _Read more: http://ilastik.org/documentation/pixelclassification/pixelclassification """ ) From 2771e0a1f0f76097fdbac887cd19ba383adbfaa2 Mon Sep 17 00:00:00 2001 From: Tim Becker Date: Sat, 3 Feb 2018 08:27:57 -0500 Subject: [PATCH 08/18] added support for 1 channel images (gray value image data) --- predict.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/predict.py b/predict.py index 3d319709..5b60ebd1 100644 --- a/predict.py +++ b/predict.py @@ -147,7 +147,8 @@ def run(self, workspace): elif self.project_type.value in ["Autocontext (2-stage)"]: x_data = skimage.img_as_ubyte(x_data) # ilastik requires UINT8. Might be relaxed in future. - cmd = ["--export_source", "probabilities stage 2"] + cmd += ["--export_source", "probabilities stage 2"] + #cmd += ["--export_source", "probabilities all stages"] cmd += [ "--output_filename_format", fout.name, @@ -156,7 +157,14 @@ def run(self, workspace): try: with h5py.File(fin.name, "w") as f: - f.create_dataset("data", data=x_data) + shape = x_data.shape + + if x_data.ndim == 2: + # ilastik appears to add a channel dimension + # even if the image is grayscale + shape += (1,) + + f.create_dataset("data", shape, data=x_data) fin.close() From 49a413fcb9892ac73d29de66b00ac60e59ccbd96 Mon Sep 17 00:00:00 2001 From: Tim Becker Date: Mon, 7 May 2018 12:51:54 -0400 Subject: [PATCH 09/18] renamed functions to unet_prediction and module to UnetPredict, added documentation --- unet_prediction.py | 230 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 unet_prediction.py diff --git a/unet_prediction.py b/unet_prediction.py new file mode 100644 index 00000000..9cf70bb5 --- /dev/null +++ b/unet_prediction.py @@ -0,0 +1,230 @@ +# coding=utf-8 + +""" +Author: Tim Becker, Juan Caicedo, Claire McQuinn +""" +import logging +import os.path +import time + +import cellprofiler.module +import cellprofiler.setting +import keras +import numpy +import pkg_resources +import requests +import tensorflow + +logger = logging.getLogger(__name__) + +option_dict_conv = {"activation": "relu", "border_mode": "same"} +option_dict_bn = {"mode": 0, "momentum": 0.9} + + +__doc__ = """\ +UnetPredict calculates pixel wise class predictions using an UNet +network. The default network model is trained to identify nuclei, background +and the nuclei boundary. Predictions are returned as three channel images: + +* red channel stores background predictions +* green channel stores nuclei predictions +* blue channel stores boundary predictions + +In the simplest use case, the predictions are converted to gray value images +using the module ColorToGray. The module IdentifyPrimaryObjects can be +used to identify example images in the nuclei predictions (green channel). + +The default UNet model is downloaded and stored on the local machine. To +replace the model the function download_file_from_google_drive needs to +be updated. + + +Author: Tim Becker, Juan Caicedo, Claire McQuinn +""" + + +class UnetPredict(cellprofiler.module.ImageProcessing): + module_name = "UnetPredict" + variable_revision_number = 1 + + def run(self, workspace): + input_image = workspace.image_set.get_image(self.x_name.value) + + input_shape = input_image.pixel_data.shape + + t0 = time.time() + model = unet_initialize(input_shape) + t1 = time.time() + logger.debug('UNet initialization took {} seconds '.format(t1 - t0)) + + self.function = lambda input_image: unet_prediction(model, input_image) + + super(UnetPredict, self).run(workspace) + + +def unet_initialize(input_shape): + session = tensorflow.Session() + # apply session + keras.backend.set_session(session) + # create model + + dim1, dim2 = input_shape + + # build model + model = get_model_3_class(dim1, dim2) + + # load weights + weights_filename = pkg_resources.resource_filename( + "cellprofiler", + os.path.join(".cache", "unet-checkpoint.hdf5") + ) + + if not os.path.exists(weights_filename): + cache_directory = os.path.dirname(weights_filename) + if not os.path.exists(cache_directory): + os.makedirs(os.path.dirname(weights_filename)) + + # Download the weights + logger.debug("Downloading model weights to: {:s}".format(weights_filename)) + #id = "146sae_bv2rJa4YoJr8Hlo-MFKfuepoMX" + id = "1I9j4oABbcV8EnvO_ufACXP9e4KyfHMtE" + + download_file_from_google_drive(id, weights_filename) + + model.load_weights(weights_filename) + + return model + + +def unet_prediction(model, input_image): + dim1, dim2 = input_image.shape + + images = input_image.reshape((-1, dim1, dim2, 1)) + + images = images.astype(numpy.float32) + images = images - numpy.min(images) + images = images.astype(numpy.float32) / numpy.max(images) + + start = time.time() + prediction = model.predict(images, batch_size=1) + end = time.time() + logger.debug('UNet segmentation took {} seconds '.format(end - start)) + + return prediction[0, :, :, :] + + +def get_core(dim1, dim2): + x = keras.layers.Input(shape=(dim1, dim2, 1)) + + a = keras.layers.Convolution2D(64, 3, 3, **option_dict_conv)(x) + a = keras.layers.BatchNormalization(**option_dict_bn)(a) + + a = keras.layers.Convolution2D(64, 3, 3, **option_dict_conv)(a) + a = keras.layers.BatchNormalization(**option_dict_bn)(a) + + y = keras.layers.MaxPooling2D()(a) + + b = keras.layers.Convolution2D(128, 3, 3, **option_dict_conv)(y) + b = keras.layers.BatchNormalization(**option_dict_bn)(b) + + b = keras.layers.Convolution2D(128, 3, 3, **option_dict_conv)(b) + b = keras.layers.BatchNormalization(**option_dict_bn)(b) + + y = keras.layers.MaxPooling2D()(b) + + c = keras.layers.Convolution2D(256, 3, 3, **option_dict_conv)(y) + c = keras.layers.BatchNormalization(**option_dict_bn)(c) + + c = keras.layers.Convolution2D(256, 3, 3, **option_dict_conv)(c) + c = keras.layers.BatchNormalization(**option_dict_bn)(c) + + y = keras.layers.MaxPooling2D()(c) + + d = keras.layers.Convolution2D(512, 3, 3, **option_dict_conv)(y) + d = keras.layers.BatchNormalization(**option_dict_bn)(d) + + d = keras.layers.Convolution2D(512, 3, 3, **option_dict_conv)(d) + d = keras.layers.BatchNormalization(**option_dict_bn)(d) + + # UP + + d = keras.layers.UpSampling2D()(d) + + y = keras.layers.merge([d, c], concat_axis=3, mode="concat") + + e = keras.layers.Convolution2D(256, 3, 3, **option_dict_conv)(y) + e = keras.layers.BatchNormalization(**option_dict_bn)(e) + + e = keras.layers.Convolution2D(256, 3, 3, **option_dict_conv)(e) + e = keras.layers.BatchNormalization(**option_dict_bn)(e) + + e = keras.layers.UpSampling2D()(e) + + y = keras.layers.merge([e, b], concat_axis=3, mode="concat") + + f = keras.layers.Convolution2D(128, 3, 3, **option_dict_conv)(y) + f = keras.layers.BatchNormalization(**option_dict_bn)(f) + + f = keras.layers.Convolution2D(128, 3, 3, **option_dict_conv)(f) + f = keras.layers.BatchNormalization(**option_dict_bn)(f) + + f = keras.layers.UpSampling2D()(f) + + y = keras.layers.merge([f, a], concat_axis=3, mode="concat") + + y = keras.layers.Convolution2D(64, 3, 3, **option_dict_conv)(y) + y = keras.layers.BatchNormalization(**option_dict_bn)(y) + + y = keras.layers.Convolution2D(64, 3, 3, **option_dict_conv)(y) + y = keras.layers.BatchNormalization(**option_dict_bn)(y) + + return [x, y] + + +def get_model_3_class(dim1, dim2, activation="softmax"): + [x, y] = get_core(dim1, dim2) + + y = keras.layers.Convolution2D(3, 1, 1, **option_dict_conv)(y) + + if activation is not None: + y = keras.layers.Activation(activation)(y) + + model = keras.models.Model(x, y) + + return model + + +# https://stackoverflow.com/a/39225272 +def download_file_from_google_drive(id, destination): + url = "https://docs.google.com/uc?export=download" + + session = requests.Session() + + response = session.get(url, params={'id': id}, stream=True) + token = get_confirm_token(response) + + if token: + params = { + 'id': id, + 'confirm': token + } + response = session.get(url, params=params, stream=True) + + save_response_content(response, destination) + + +def get_confirm_token(response): + for key, value in response.cookies.items(): + if key.startswith('download_warning'): + return value + + return None + + +def save_response_content(response, destination): + chunk_size = 32768 + + with open(destination, "wb") as f: + for chunk in response.iter_content(chunk_size): + if chunk: # filter out keep-alive new chunks + f.write(chunk) From 16e3ee4f46b8b3a98678313fe28675a54f4fbe00 Mon Sep 17 00:00:00 2001 From: Tim Becker Date: Mon, 7 May 2018 12:55:36 -0400 Subject: [PATCH 10/18] removed unused file --- unet_segmentation.py | 206 ------------------------------------------- 1 file changed, 206 deletions(-) delete mode 100644 unet_segmentation.py diff --git a/unet_segmentation.py b/unet_segmentation.py deleted file mode 100644 index ebc228b6..00000000 --- a/unet_segmentation.py +++ /dev/null @@ -1,206 +0,0 @@ -import logging -import os.path -import time - -import cellprofiler.module -import cellprofiler.setting -import keras -import numpy -import pkg_resources -import requests -import tensorflow - -logger = logging.getLogger(__name__) - -option_dict_conv = {"activation": "relu", "border_mode": "same"} -option_dict_bn = {"mode": 0, "momentum": 0.9} - - -__doc__ = """\ -tbd -""" - - -class UnetSegment(cellprofiler.module.ImageProcessing): - module_name = "UnetSegment" - variable_revision_number = 1 - - def run(self, workspace): - input_image = workspace.image_set.get_image(self.x_name.value) - - input_shape = input_image.pixel_data.shape - - t0 = time.time() - model = unet_initialize(input_shape) - t1 = time.time() - logger.debug('UNet initialization took {} seconds '.format(t1 - t0)) - - self.function = lambda input_image: unet_segmentation(model, input_image) - - super(UnetSegment, self).run(workspace) - - -def unet_initialize(input_shape): - session = tensorflow.Session() - # apply session - keras.backend.set_session(session) - # create model - - dim1, dim2 = input_shape - - # build model - model = get_model_3_class(dim1, dim2) - - # load weights - weights_filename = pkg_resources.resource_filename( - "cellprofiler", - os.path.join(".cache", "unet-checkpoint.hdf5") - ) - - if not os.path.exists(weights_filename): - cache_directory = os.path.dirname(weights_filename) - if not os.path.exists(cache_directory): - os.makedirs(os.path.dirname(weights_filename)) - - # Download the weights - logger.debug("Downloading model weights to: {:s}".format(weights_filename)) - id = "146sae_bv2rJa4YoJr8Hlo-MFKfuepoMX" - download_file_from_google_drive(id, weights_filename) - - model.load_weights(weights_filename) - - return model - - -def unet_segmentation(model, input_image): - dim1, dim2 = input_image.shape - - images = input_image.reshape((-1, dim1, dim2, 1)) - - images = images.astype(numpy.float32) - images = images - numpy.min(images) - images = images.astype(numpy.float32) / numpy.max(images) - - start = time.time() - segmentation = model.predict(images, batch_size=1) - end = time.time() - logger.debug('UNet segmentation took {} seconds '.format(end - start)) - - return segmentation[0, :, :, :] - - -def get_core(dim1, dim2): - x = keras.layers.Input(shape=(dim1, dim2, 1)) - - a = keras.layers.Convolution2D(64, 3, 3, **option_dict_conv)(x) - a = keras.layers.BatchNormalization(**option_dict_bn)(a) - - a = keras.layers.Convolution2D(64, 3, 3, **option_dict_conv)(a) - a = keras.layers.BatchNormalization(**option_dict_bn)(a) - - y = keras.layers.MaxPooling2D()(a) - - b = keras.layers.Convolution2D(128, 3, 3, **option_dict_conv)(y) - b = keras.layers.BatchNormalization(**option_dict_bn)(b) - - b = keras.layers.Convolution2D(128, 3, 3, **option_dict_conv)(b) - b = keras.layers.BatchNormalization(**option_dict_bn)(b) - - y = keras.layers.MaxPooling2D()(b) - - c = keras.layers.Convolution2D(256, 3, 3, **option_dict_conv)(y) - c = keras.layers.BatchNormalization(**option_dict_bn)(c) - - c = keras.layers.Convolution2D(256, 3, 3, **option_dict_conv)(c) - c = keras.layers.BatchNormalization(**option_dict_bn)(c) - - y = keras.layers.MaxPooling2D()(c) - - d = keras.layers.Convolution2D(512, 3, 3, **option_dict_conv)(y) - d = keras.layers.BatchNormalization(**option_dict_bn)(d) - - d = keras.layers.Convolution2D(512, 3, 3, **option_dict_conv)(d) - d = keras.layers.BatchNormalization(**option_dict_bn)(d) - - # UP - - d = keras.layers.UpSampling2D()(d) - - y = keras.layers.merge([d, c], concat_axis=3, mode="concat") - - e = keras.layers.Convolution2D(256, 3, 3, **option_dict_conv)(y) - e = keras.layers.BatchNormalization(**option_dict_bn)(e) - - e = keras.layers.Convolution2D(256, 3, 3, **option_dict_conv)(e) - e = keras.layers.BatchNormalization(**option_dict_bn)(e) - - e = keras.layers.UpSampling2D()(e) - - y = keras.layers.merge([e, b], concat_axis=3, mode="concat") - - f = keras.layers.Convolution2D(128, 3, 3, **option_dict_conv)(y) - f = keras.layers.BatchNormalization(**option_dict_bn)(f) - - f = keras.layers.Convolution2D(128, 3, 3, **option_dict_conv)(f) - f = keras.layers.BatchNormalization(**option_dict_bn)(f) - - f = keras.layers.UpSampling2D()(f) - - y = keras.layers.merge([f, a], concat_axis=3, mode="concat") - - y = keras.layers.Convolution2D(64, 3, 3, **option_dict_conv)(y) - y = keras.layers.BatchNormalization(**option_dict_bn)(y) - - y = keras.layers.Convolution2D(64, 3, 3, **option_dict_conv)(y) - y = keras.layers.BatchNormalization(**option_dict_bn)(y) - - return [x, y] - - -def get_model_3_class(dim1, dim2, activation="softmax"): - [x, y] = get_core(dim1, dim2) - - y = keras.layers.Convolution2D(3, 1, 1, **option_dict_conv)(y) - - if activation is not None: - y = keras.layers.Activation(activation)(y) - - model = keras.models.Model(x, y) - - return model - - -# https://stackoverflow.com/a/39225272 -def download_file_from_google_drive(id, destination): - url = "https://docs.google.com/uc?export=download" - - session = requests.Session() - - response = session.get(url, params={'id': id}, stream=True) - token = get_confirm_token(response) - - if token: - params = { - 'id': id, - 'confirm': token - } - response = session.get(url, params=params, stream=True) - - save_response_content(response, destination) - - -def get_confirm_token(response): - for key, value in response.cookies.items(): - if key.startswith('download_warning'): - return value - - return None - - -def save_response_content(response, destination): - chunk_size = 32768 - - with open(destination, "wb") as f: - for chunk in response.iter_content(chunk_size): - if chunk: # filter out keep-alive new chunks - f.write(chunk) From 841c6026fe7a7d9fe599743d15e7127b287f6790 Mon Sep 17 00:00:00 2001 From: Tim Becker Date: Mon, 7 May 2018 15:33:11 -0400 Subject: [PATCH 11/18] renamed class to ClassifyPixels-Unet, removed old links, updated variable names --- classifypixelsunet.py | 230 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 classifypixelsunet.py diff --git a/classifypixelsunet.py b/classifypixelsunet.py new file mode 100644 index 00000000..70f2ba93 --- /dev/null +++ b/classifypixelsunet.py @@ -0,0 +1,230 @@ +# coding=utf-8 + +""" +Author: Tim Becker, Juan Caicedo, Claire McQuinn +""" +import logging +import os.path +import time + +import cellprofiler.module +import cellprofiler.setting +import keras +import numpy +import pkg_resources +import requests +import tensorflow + +logger = logging.getLogger(__name__) + +option_dict_conv = {"activation": "relu", "border_mode": "same"} +option_dict_bn = {"mode": 0, "momentum": 0.9} + + +__doc__ = """\ +ClassifyPixels-Unet calculates pixel wise classification using an UNet +network. The default network model is trained to identify nuclei, background +and the nuclei boundary. Classification results are returned as three channel +images: + +* red channel stores background classification +* green channel stores nuclei classification +* blue channel stores boundary classification + +In the simplest use case, the classifications are converted to gray value images +using the module ColorToGray. The module IdentifyPrimaryObjects can be +used to identify example images in the nuclei channel (green channel). + +The default UNet model is downloaded and stored on the local machine. To +replace the model the function download_file_from_google_drive needs to +be updated. + + +Author: Tim Becker, Juan Caicedo, Claire McQuinn +""" + + +class ClassifyPixelsUnet(cellprofiler.module.ImageProcessing): + module_name = "ClassifyPixels-Unet" + variable_revision_number = 1 + + def run(self, workspace): + input_image = workspace.image_set.get_image(self.x_name.value) + + input_shape = input_image.pixel_data.shape + + t0 = time.time() + model = unet_initialize(input_shape) + t1 = time.time() + logger.debug('UNet initialization took {} seconds '.format(t1 - t0)) + + self.function = lambda input_image: unet_classify(model, input_image) + + super(ClassifyPixelsUnet, self).run(workspace) + + +def unet_initialize(input_shape): + session = tensorflow.Session() + # apply session + keras.backend.set_session(session) + # create model + + dim1, dim2 = input_shape + + # build model + model = get_model_3_class(dim1, dim2) + + # load weights + weights_filename = pkg_resources.resource_filename( + "cellprofiler", + os.path.join(".cache", "unet-checkpoint.hdf5") + ) + + if not os.path.exists(weights_filename): + cache_directory = os.path.dirname(weights_filename) + if not os.path.exists(cache_directory): + os.makedirs(os.path.dirname(weights_filename)) + + # Download the weights + logger.debug("Downloading model weights to: {:s}".format(weights_filename)) + model_id = "1I9j4oABbcV8EnvO_ufACXP9e4KyfHMtE" + + download_file_from_google_drive(model_id, weights_filename) + + model.load_weights(weights_filename) + + return model + + +def unet_classify(model, input_image): + dim1, dim2 = input_image.shape + + images = input_image.reshape((-1, dim1, dim2, 1)) + + images = images.astype(numpy.float32) + images = images - numpy.min(images) + images = images.astype(numpy.float32) / numpy.max(images) + + start = time.time() + pixel_classification = model.predict(images, batch_size=1) + end = time.time() + logger.debug('UNet segmentation took {} seconds '.format(end - start)) + + return pixel_classification[0, :, :, :] + + +def get_core(dim1, dim2): + x = keras.layers.Input(shape=(dim1, dim2, 1)) + + a = keras.layers.Convolution2D(64, 3, 3, **option_dict_conv)(x) + a = keras.layers.BatchNormalization(**option_dict_bn)(a) + + a = keras.layers.Convolution2D(64, 3, 3, **option_dict_conv)(a) + a = keras.layers.BatchNormalization(**option_dict_bn)(a) + + y = keras.layers.MaxPooling2D()(a) + + b = keras.layers.Convolution2D(128, 3, 3, **option_dict_conv)(y) + b = keras.layers.BatchNormalization(**option_dict_bn)(b) + + b = keras.layers.Convolution2D(128, 3, 3, **option_dict_conv)(b) + b = keras.layers.BatchNormalization(**option_dict_bn)(b) + + y = keras.layers.MaxPooling2D()(b) + + c = keras.layers.Convolution2D(256, 3, 3, **option_dict_conv)(y) + c = keras.layers.BatchNormalization(**option_dict_bn)(c) + + c = keras.layers.Convolution2D(256, 3, 3, **option_dict_conv)(c) + c = keras.layers.BatchNormalization(**option_dict_bn)(c) + + y = keras.layers.MaxPooling2D()(c) + + d = keras.layers.Convolution2D(512, 3, 3, **option_dict_conv)(y) + d = keras.layers.BatchNormalization(**option_dict_bn)(d) + + d = keras.layers.Convolution2D(512, 3, 3, **option_dict_conv)(d) + d = keras.layers.BatchNormalization(**option_dict_bn)(d) + + # UP + + d = keras.layers.UpSampling2D()(d) + + y = keras.layers.merge([d, c], concat_axis=3, mode="concat") + + e = keras.layers.Convolution2D(256, 3, 3, **option_dict_conv)(y) + e = keras.layers.BatchNormalization(**option_dict_bn)(e) + + e = keras.layers.Convolution2D(256, 3, 3, **option_dict_conv)(e) + e = keras.layers.BatchNormalization(**option_dict_bn)(e) + + e = keras.layers.UpSampling2D()(e) + + y = keras.layers.merge([e, b], concat_axis=3, mode="concat") + + f = keras.layers.Convolution2D(128, 3, 3, **option_dict_conv)(y) + f = keras.layers.BatchNormalization(**option_dict_bn)(f) + + f = keras.layers.Convolution2D(128, 3, 3, **option_dict_conv)(f) + f = keras.layers.BatchNormalization(**option_dict_bn)(f) + + f = keras.layers.UpSampling2D()(f) + + y = keras.layers.merge([f, a], concat_axis=3, mode="concat") + + y = keras.layers.Convolution2D(64, 3, 3, **option_dict_conv)(y) + y = keras.layers.BatchNormalization(**option_dict_bn)(y) + + y = keras.layers.Convolution2D(64, 3, 3, **option_dict_conv)(y) + y = keras.layers.BatchNormalization(**option_dict_bn)(y) + + return [x, y] + + +def get_model_3_class(dim1, dim2, activation="softmax"): + [x, y] = get_core(dim1, dim2) + + y = keras.layers.Convolution2D(3, 1, 1, **option_dict_conv)(y) + + if activation is not None: + y = keras.layers.Activation(activation)(y) + + model = keras.models.Model(x, y) + + return model + + +# https://stackoverflow.com/a/39225272 +def download_file_from_google_drive(id, destination): + url = "https://docs.google.com/uc?export=download" + + session = requests.Session() + + response = session.get(url, params={'id': id}, stream=True) + token = get_confirm_token(response) + + if token: + params = { + 'id': id, + 'confirm': token + } + response = session.get(url, params=params, stream=True) + + save_response_content(response, destination) + + +def get_confirm_token(response): + for key, value in response.cookies.items(): + if key.startswith('download_warning'): + return value + + return None + + +def save_response_content(response, destination): + chunk_size = 32768 + + with open(destination, "wb") as f: + for chunk in response.iter_content(chunk_size): + if chunk: # filter out keep-alive new chunks + f.write(chunk) From 86ff853fb03eb2fea04296a9e0f4815c92535182 Mon Sep 17 00:00:00 2001 From: Tim Becker Date: Mon, 7 May 2018 17:23:43 -0400 Subject: [PATCH 12/18] added support for windows using cntk --- classifypixelsunet.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/classifypixelsunet.py b/classifypixelsunet.py index 70f2ba93..9e68680a 100644 --- a/classifypixelsunet.py +++ b/classifypixelsunet.py @@ -3,17 +3,26 @@ """ Author: Tim Becker, Juan Caicedo, Claire McQuinn """ +import keras import logging -import os.path +import numpy +import pkg_resources +import requests +import sys import time +import os.path import cellprofiler.module import cellprofiler.setting -import keras -import numpy -import pkg_resources -import requests -import tensorflow + +is_windows = hasattr(sys, 'getwindowsversion') + +if is_windows: + os.environ["KERAS_BACKEND"] = "cntk" + import cntk +else: + os.environ["KERAS_BACKEND"] = "tensorflow" + import tensorflow logger = logging.getLogger(__name__) From 1f813fbf24bb566f230ba943cc4d2c5f2763cb6f Mon Sep 17 00:00:00 2001 From: Tim Becker Date: Mon, 7 May 2018 17:26:26 -0400 Subject: [PATCH 13/18] added requirements files for windows machines --- requirements-windows.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 requirements-windows.txt diff --git a/requirements-windows.txt b/requirements-windows.txt new file mode 100644 index 00000000..24401941 --- /dev/null +++ b/requirements-windows.txt @@ -0,0 +1,3 @@ +cellh5 +keras +cntk From bf2014a0fd8b9f6d768d5f18e1d53f09fd23a972 Mon Sep 17 00:00:00 2001 From: Tim Becker Date: Tue, 8 May 2018 10:17:27 -0400 Subject: [PATCH 14/18] removed tensorflow backend in windows version --- classifypixelsunet.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/classifypixelsunet.py b/classifypixelsunet.py index 9e68680a..9f7e15dd 100644 --- a/classifypixelsunet.py +++ b/classifypixelsunet.py @@ -3,7 +3,7 @@ """ Author: Tim Becker, Juan Caicedo, Claire McQuinn """ -import keras + import logging import numpy import pkg_resources @@ -15,14 +15,12 @@ import cellprofiler.module import cellprofiler.setting -is_windows = hasattr(sys, 'getwindowsversion') - -if is_windows: +if sys.platform.startswith('win'): os.environ["KERAS_BACKEND"] = "cntk" - import cntk else: os.environ["KERAS_BACKEND"] = "tensorflow" - import tensorflow + +import keras logger = logging.getLogger(__name__) @@ -73,9 +71,6 @@ def run(self, workspace): def unet_initialize(input_shape): - session = tensorflow.Session() - # apply session - keras.backend.set_session(session) # create model dim1, dim2 = input_shape From e3d7f25f780f0ac750c48af9a335aa1de04f31d2 Mon Sep 17 00:00:00 2001 From: Tim Becker Date: Tue, 8 May 2018 10:52:40 -0400 Subject: [PATCH 15/18] deleted old file --- unet_prediction.py | 230 --------------------------------------------- 1 file changed, 230 deletions(-) delete mode 100644 unet_prediction.py diff --git a/unet_prediction.py b/unet_prediction.py deleted file mode 100644 index 9cf70bb5..00000000 --- a/unet_prediction.py +++ /dev/null @@ -1,230 +0,0 @@ -# coding=utf-8 - -""" -Author: Tim Becker, Juan Caicedo, Claire McQuinn -""" -import logging -import os.path -import time - -import cellprofiler.module -import cellprofiler.setting -import keras -import numpy -import pkg_resources -import requests -import tensorflow - -logger = logging.getLogger(__name__) - -option_dict_conv = {"activation": "relu", "border_mode": "same"} -option_dict_bn = {"mode": 0, "momentum": 0.9} - - -__doc__ = """\ -UnetPredict calculates pixel wise class predictions using an UNet -network. The default network model is trained to identify nuclei, background -and the nuclei boundary. Predictions are returned as three channel images: - -* red channel stores background predictions -* green channel stores nuclei predictions -* blue channel stores boundary predictions - -In the simplest use case, the predictions are converted to gray value images -using the module ColorToGray. The module IdentifyPrimaryObjects can be -used to identify example images in the nuclei predictions (green channel). - -The default UNet model is downloaded and stored on the local machine. To -replace the model the function download_file_from_google_drive needs to -be updated. - - -Author: Tim Becker, Juan Caicedo, Claire McQuinn -""" - - -class UnetPredict(cellprofiler.module.ImageProcessing): - module_name = "UnetPredict" - variable_revision_number = 1 - - def run(self, workspace): - input_image = workspace.image_set.get_image(self.x_name.value) - - input_shape = input_image.pixel_data.shape - - t0 = time.time() - model = unet_initialize(input_shape) - t1 = time.time() - logger.debug('UNet initialization took {} seconds '.format(t1 - t0)) - - self.function = lambda input_image: unet_prediction(model, input_image) - - super(UnetPredict, self).run(workspace) - - -def unet_initialize(input_shape): - session = tensorflow.Session() - # apply session - keras.backend.set_session(session) - # create model - - dim1, dim2 = input_shape - - # build model - model = get_model_3_class(dim1, dim2) - - # load weights - weights_filename = pkg_resources.resource_filename( - "cellprofiler", - os.path.join(".cache", "unet-checkpoint.hdf5") - ) - - if not os.path.exists(weights_filename): - cache_directory = os.path.dirname(weights_filename) - if not os.path.exists(cache_directory): - os.makedirs(os.path.dirname(weights_filename)) - - # Download the weights - logger.debug("Downloading model weights to: {:s}".format(weights_filename)) - #id = "146sae_bv2rJa4YoJr8Hlo-MFKfuepoMX" - id = "1I9j4oABbcV8EnvO_ufACXP9e4KyfHMtE" - - download_file_from_google_drive(id, weights_filename) - - model.load_weights(weights_filename) - - return model - - -def unet_prediction(model, input_image): - dim1, dim2 = input_image.shape - - images = input_image.reshape((-1, dim1, dim2, 1)) - - images = images.astype(numpy.float32) - images = images - numpy.min(images) - images = images.astype(numpy.float32) / numpy.max(images) - - start = time.time() - prediction = model.predict(images, batch_size=1) - end = time.time() - logger.debug('UNet segmentation took {} seconds '.format(end - start)) - - return prediction[0, :, :, :] - - -def get_core(dim1, dim2): - x = keras.layers.Input(shape=(dim1, dim2, 1)) - - a = keras.layers.Convolution2D(64, 3, 3, **option_dict_conv)(x) - a = keras.layers.BatchNormalization(**option_dict_bn)(a) - - a = keras.layers.Convolution2D(64, 3, 3, **option_dict_conv)(a) - a = keras.layers.BatchNormalization(**option_dict_bn)(a) - - y = keras.layers.MaxPooling2D()(a) - - b = keras.layers.Convolution2D(128, 3, 3, **option_dict_conv)(y) - b = keras.layers.BatchNormalization(**option_dict_bn)(b) - - b = keras.layers.Convolution2D(128, 3, 3, **option_dict_conv)(b) - b = keras.layers.BatchNormalization(**option_dict_bn)(b) - - y = keras.layers.MaxPooling2D()(b) - - c = keras.layers.Convolution2D(256, 3, 3, **option_dict_conv)(y) - c = keras.layers.BatchNormalization(**option_dict_bn)(c) - - c = keras.layers.Convolution2D(256, 3, 3, **option_dict_conv)(c) - c = keras.layers.BatchNormalization(**option_dict_bn)(c) - - y = keras.layers.MaxPooling2D()(c) - - d = keras.layers.Convolution2D(512, 3, 3, **option_dict_conv)(y) - d = keras.layers.BatchNormalization(**option_dict_bn)(d) - - d = keras.layers.Convolution2D(512, 3, 3, **option_dict_conv)(d) - d = keras.layers.BatchNormalization(**option_dict_bn)(d) - - # UP - - d = keras.layers.UpSampling2D()(d) - - y = keras.layers.merge([d, c], concat_axis=3, mode="concat") - - e = keras.layers.Convolution2D(256, 3, 3, **option_dict_conv)(y) - e = keras.layers.BatchNormalization(**option_dict_bn)(e) - - e = keras.layers.Convolution2D(256, 3, 3, **option_dict_conv)(e) - e = keras.layers.BatchNormalization(**option_dict_bn)(e) - - e = keras.layers.UpSampling2D()(e) - - y = keras.layers.merge([e, b], concat_axis=3, mode="concat") - - f = keras.layers.Convolution2D(128, 3, 3, **option_dict_conv)(y) - f = keras.layers.BatchNormalization(**option_dict_bn)(f) - - f = keras.layers.Convolution2D(128, 3, 3, **option_dict_conv)(f) - f = keras.layers.BatchNormalization(**option_dict_bn)(f) - - f = keras.layers.UpSampling2D()(f) - - y = keras.layers.merge([f, a], concat_axis=3, mode="concat") - - y = keras.layers.Convolution2D(64, 3, 3, **option_dict_conv)(y) - y = keras.layers.BatchNormalization(**option_dict_bn)(y) - - y = keras.layers.Convolution2D(64, 3, 3, **option_dict_conv)(y) - y = keras.layers.BatchNormalization(**option_dict_bn)(y) - - return [x, y] - - -def get_model_3_class(dim1, dim2, activation="softmax"): - [x, y] = get_core(dim1, dim2) - - y = keras.layers.Convolution2D(3, 1, 1, **option_dict_conv)(y) - - if activation is not None: - y = keras.layers.Activation(activation)(y) - - model = keras.models.Model(x, y) - - return model - - -# https://stackoverflow.com/a/39225272 -def download_file_from_google_drive(id, destination): - url = "https://docs.google.com/uc?export=download" - - session = requests.Session() - - response = session.get(url, params={'id': id}, stream=True) - token = get_confirm_token(response) - - if token: - params = { - 'id': id, - 'confirm': token - } - response = session.get(url, params=params, stream=True) - - save_response_content(response, destination) - - -def get_confirm_token(response): - for key, value in response.cookies.items(): - if key.startswith('download_warning'): - return value - - return None - - -def save_response_content(response, destination): - chunk_size = 32768 - - with open(destination, "wb") as f: - for chunk in response.iter_content(chunk_size): - if chunk: # filter out keep-alive new chunks - f.write(chunk) From fb66601d0fa02311800f99bc2d3d56f58fbf74af Mon Sep 17 00:00:00 2001 From: Tim Becker Date: Tue, 8 May 2018 13:08:29 -0400 Subject: [PATCH 16/18] updated install instructions --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 9136f8d2..b35227b6 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,12 @@ Please see help here: https://github.com/CellProfiler/CellProfiler/blob/master/c pip install -r requirements.txt ``` + To install CellProfiler-plugins on a windows machine with support for the deep learning module ClassifyPixels-UNet use + ``` + cd CellProfiler-plugins + pip install -r requirements-windows.txt + ``` + 1. Configure CellProfiler plugins directory in the GUI via `Preferences > CellProfiler plugins directory` (you will need to restart CellProfiler for the change to take effect). When running CellProfiler via the command line, use the `--plugins-directory` flag to specify the plugins directory, for example: ``` cellprofiler --run --run-headless --project PROJECT_FILE --plugins-directory PLUGIN_DIRECTORY/CellProfiler-plugins From 1151fbb3bed763d455e18e81ea9fbe1abb1e5c33 Mon Sep 17 00:00:00 2001 From: Matthew Bowden Date: Fri, 13 Apr 2018 18:23:46 -0700 Subject: [PATCH 17/18] Refactor tests so a new instance of a module is given on each run This was making it so changes to the settings of the module in other tests would affect downstream tests --- tests/conftest.py | 7 ++++--- tests/test_blobdetection.py | 2 +- tests/test_edgedetection.py | 2 +- tests/test_gammacorrection.py | 2 +- tests/test_histogramequalization.py | 2 +- tests/test_imagegradient.py | 2 +- tests/test_laplacianofgaussian.py | 2 +- tests/test_mergeobjects.py | 2 +- tests/test_randomwalkeralgorithm.py | 2 +- tests/test_tophattransform.py | 2 +- 10 files changed, 13 insertions(+), 12 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 97fb8483..7da939d5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -37,7 +37,7 @@ def image(request): return cellprofiler.image.Image(image=data, dimensions=dimensions) -@pytest.fixture(scope="module") +@pytest.fixture(scope="function") def image_empty(): image = cellprofiler.image.Image() @@ -71,11 +71,12 @@ def measurements(): return cellprofiler.measurement.Measurements() -@pytest.fixture(scope="module") +@pytest.fixture(scope="function") def module(request): instance = getattr(request.module, "instance") - return instance + return instance() + @pytest.fixture(scope="function") def objects(image): diff --git a/tests/test_blobdetection.py b/tests/test_blobdetection.py index 97e40edd..12bf02cb 100644 --- a/tests/test_blobdetection.py +++ b/tests/test_blobdetection.py @@ -35,7 +35,7 @@ def image(request): return cellprofiler.image.Image(image=data, dimensions=dimensions) -instance = blobdetection.BlobDetection() +instance = blobdetection.BlobDetection def test_run_dog(image, image_set, module, workspace): diff --git a/tests/test_edgedetection.py b/tests/test_edgedetection.py index 8760be82..11145455 100644 --- a/tests/test_edgedetection.py +++ b/tests/test_edgedetection.py @@ -7,7 +7,7 @@ import edgedetection -instance = edgedetection.EdgeDetection() +instance = edgedetection.EdgeDetection def test_run_without_mask(image, image_set, module, workspace): diff --git a/tests/test_gammacorrection.py b/tests/test_gammacorrection.py index d969f451..857d22d1 100644 --- a/tests/test_gammacorrection.py +++ b/tests/test_gammacorrection.py @@ -3,7 +3,7 @@ import gammacorrection -instance = gammacorrection.GammaCorrection() +instance = gammacorrection.GammaCorrection def test_run(image, module, image_set, workspace): diff --git a/tests/test_histogramequalization.py b/tests/test_histogramequalization.py index da574c02..ce54671c 100644 --- a/tests/test_histogramequalization.py +++ b/tests/test_histogramequalization.py @@ -5,7 +5,7 @@ import histogramequalization -instance = histogramequalization.HistogramEqualization() +instance = histogramequalization.HistogramEqualization def test_run(image, image_set, module, workspace): diff --git a/tests/test_imagegradient.py b/tests/test_imagegradient.py index 61ef2e66..34817a7e 100644 --- a/tests/test_imagegradient.py +++ b/tests/test_imagegradient.py @@ -6,7 +6,7 @@ import imagegradient -instance = imagegradient.ImageGradient() +instance = imagegradient.ImageGradient def test_run(image, module, image_set, workspace): diff --git a/tests/test_laplacianofgaussian.py b/tests/test_laplacianofgaussian.py index d20bbc9e..34cd1a86 100644 --- a/tests/test_laplacianofgaussian.py +++ b/tests/test_laplacianofgaussian.py @@ -5,7 +5,7 @@ import laplacianofgaussian -instance = laplacianofgaussian.LaplacianOfGaussian() +instance = laplacianofgaussian.LaplacianOfGaussian def test_run(image, image_set, module, workspace): diff --git a/tests/test_mergeobjects.py b/tests/test_mergeobjects.py index b7c812ba..51eda7b2 100644 --- a/tests/test_mergeobjects.py +++ b/tests/test_mergeobjects.py @@ -7,7 +7,7 @@ import mergeobjects -instance = mergeobjects.MergeObjects() +instance = mergeobjects.MergeObjects @pytest.fixture(scope="module") diff --git a/tests/test_randomwalkeralgorithm.py b/tests/test_randomwalkeralgorithm.py index c781cc59..94663661 100644 --- a/tests/test_randomwalkeralgorithm.py +++ b/tests/test_randomwalkeralgorithm.py @@ -7,7 +7,7 @@ import randomwalkeralgorithm -instance = randomwalkeralgorithm.RandomWalkerAlgorithm() +instance = randomwalkeralgorithm.RandomWalkerAlgorithm def test_run(image, module, workspace): diff --git a/tests/test_tophattransform.py b/tests/test_tophattransform.py index 670bc44e..470e546f 100644 --- a/tests/test_tophattransform.py +++ b/tests/test_tophattransform.py @@ -6,7 +6,7 @@ import tophattransform -instance = tophattransform.TopHatTransform() +instance = tophattransform.TopHatTransform @pytest.fixture( From 65de407dcf1e01db2ff132dcab8bc26b696b9a89 Mon Sep 17 00:00:00 2001 From: Carla Date: Fri, 11 May 2018 10:00:18 +0200 Subject: [PATCH 18/18] Fix caching section for Travis CI --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c767839b..c29b0fc3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ addons: packages: - libhdf5-serial-dev - python-pip +cache: apt: true directories: $HOME/.cache/pip dist: trusty @@ -21,4 +22,4 @@ notifications: email: false python: 2.7 script: pytest -sudo: false \ No newline at end of file +sudo: false