From dbb3e1aacb8e95495110157dd65d744607fc5ff4 Mon Sep 17 00:00:00 2001 From: mcquin Date: Mon, 19 Jun 2017 08:42:11 -0400 Subject: [PATCH] Move RandomWalkerAlgorithm from CellProfiler to CellProfiler-plugins (resolves #14). Additionally, enables Travis (resolves #9). --- .travis.yml | 24 ++++++ randomwalkeralgorithm.py | 114 ++++++++++++++++++++++++++++ tests/__init__.py | 0 tests/conftest.py | 68 +++++++++++++++++ tests/test_randomwalkeralgorithm.py | 51 +++++++++++++ 5 files changed, 257 insertions(+) create mode 100644 .travis.yml create mode 100644 randomwalkeralgorithm.py create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/test_randomwalkeralgorithm.py diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..c767839b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,24 @@ +addons: + apt: + packages: + - libhdf5-serial-dev + - python-pip + apt: true + directories: $HOME/.cache/pip +dist: trusty +env: + - LC_ALL="en_US.UTF-8" CP_MYSQL_TEST_HOST="127.0.0.1" CP_MYSQL_TEST_USER="root" CP_MYSQL_TEST_PASSWORD="" +install: + - pip install --upgrade pip + - pip install --upgrade cython + - pip install --upgrade joblib + - pip install --upgrade numpy + - pip install --upgrade scipy + - pip install --editable git+https://github.com/CellProfiler/CellProfiler.git#egg=CellProfiler + - pip freeze +language: python +notifications: + email: false +python: 2.7 +script: pytest +sudo: false \ No newline at end of file diff --git a/randomwalkeralgorithm.py b/randomwalkeralgorithm.py new file mode 100644 index 00000000..e5e1c43c --- /dev/null +++ b/randomwalkeralgorithm.py @@ -0,0 +1,114 @@ +# coding=utf-8 + +""" + +Random walker algorithm + +Single-channel images can be two-or-three-dimensional. + +""" + +import numpy +import skimage.color +import skimage.measure +import skimage.segmentation + +import cellprofiler.module +import cellprofiler.object +import cellprofiler.setting + + +class RandomWalkerAlgorithm(cellprofiler.module.ImageSegmentation): + module_name = "Random walker algorithm" + + variable_revision_number = 1 + + def create_settings(self): + super(RandomWalkerAlgorithm, self).create_settings() + + self.first_phase = cellprofiler.setting.Float( + doc="First phase demarcates an image’s first phase.", + text="First phase", + value=0.5 + ) + + self.second_phase = cellprofiler.setting.Float( + doc="Second phase demarcates an image’s second phase.", + text="Second phase", + value=0.5 + ) + + self.beta = cellprofiler.setting.Float( + doc=""" + Beta is the penalization coefficient for the random walker motion. Increasing the penalization + coefficient increases the difficulty of the diffusion. Likewise, decreasing the penalization coefficient + decreases the difficulty of the diffusion. + """, + text="Beta", + value=130.0 + ) + + def settings(self): + __settings__ = super(RandomWalkerAlgorithm, self).settings() + + return __settings__ + [ + self.first_phase, + self.second_phase, + self.beta + ] + + def visible_settings(self): + __settings__ = super(RandomWalkerAlgorithm, self).settings() + + return __settings__ + [ + self.first_phase, + self.second_phase, + self.beta + ] + + def run(self, workspace): + x_name = self.x_name.value + + y_name = self.y_name.value + + images = workspace.image_set + + x = images.get_image(x_name) + + x_data = x.pixel_data + + if x.multichannel: + x_data = skimage.color.rgb2gray(x_data) + + labels_data = numpy.zeros_like(x_data, numpy.uint8) + + labels_data[x_data > self.first_phase.value] = 1 + + labels_data[x_data < self.second_phase.value] = 2 + + y_data = skimage.segmentation.random_walker( + beta=self.beta.value, + data=x_data, + labels=labels_data, + multichannel=False, + spacing=x.spacing + ) + + y_data = skimage.measure.label(y_data) + + objects = cellprofiler.object.Objects() + + objects.segmented = y_data + + objects.parent_image = x + + workspace.object_set.add_objects(objects, y_name) + + self.add_measurements(workspace) + + if self.show_window: + workspace.display_data.x_data = x_data + + workspace.display_data.y_data = y_data + + workspace.display_data.dimensions = x.dimensions diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..6e32dc11 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,68 @@ +import cellprofiler.image +import cellprofiler.measurement +import cellprofiler.object +import cellprofiler.pipeline +import cellprofiler.workspace +import numpy +import skimage.data +import pytest + + +@pytest.fixture( + scope="module", + params=[ + (skimage.data.camera()[0:128, 0:128], 2), + (skimage.data.astronaut()[0:128, 0:128, :], 2), + (numpy.tile(skimage.data.camera()[0:32, 0:32], (2, 1)).reshape(2, 32, 32), 3) + ], + ids=[ + "grayscale_image", + "multichannel_image", + "grayscale_volume" + ] +) +def image(request): + data, dimensions = request.param + + return cellprofiler.image.Image(image=data, dimensions=dimensions) + + +@pytest.fixture(scope="function") +def image_set(image, image_set_list): + image_set = image_set_list.get_image_set(0) + + image_set.add("example", image) + + return image_set + + +@pytest.fixture(scope="function") +def image_set_list(): + return cellprofiler.image.ImageSetList() + + +@pytest.fixture(scope="function") +def measurements(): + return cellprofiler.measurement.Measurements() + + +@pytest.fixture(scope="module") +def module(request): + instance = getattr(request.module, "instance") + + return instance + + +@pytest.fixture(scope="function") +def object_set(): + return cellprofiler.object.ObjectSet() + + +@pytest.fixture(scope="function") +def pipeline(): + return cellprofiler.pipeline.Pipeline() + + +@pytest.fixture(scope="function") +def workspace(pipeline, module, image_set, object_set, measurements, image_set_list): + return cellprofiler.workspace.Workspace(pipeline, module, image_set, object_set, measurements, image_set_list) diff --git a/tests/test_randomwalkeralgorithm.py b/tests/test_randomwalkeralgorithm.py new file mode 100644 index 00000000..5294b4e9 --- /dev/null +++ b/tests/test_randomwalkeralgorithm.py @@ -0,0 +1,51 @@ +import cellprofiler.preferences +import numpy +import numpy.testing +import skimage.color +import skimage.measure +import skimage.segmentation + +import randomwalkeralgorithm + +cellprofiler.preferences.set_headless() + +instance = randomwalkeralgorithm.RandomWalkerAlgorithm() + + +def test_run(image, module, workspace): + module.x_name.value = "example" + + module.y_name.value = "RandomWalkerAlgorithm" + + module.first_phase.value = 0.5 + + module.second_phase.value = 0.5 + + module.beta.value = 130.0 + + module.run(workspace) + + x_data = image.pixel_data + + if image.multichannel: + x_data = skimage.color.rgb2gray(x_data) + + labels_data = numpy.zeros_like(x_data, numpy.uint) + + labels_data[x_data < 0.5] = 1 + + labels_data[x_data > 0.5] = 2 + + expected = skimage.segmentation.random_walker( + beta=130.0, + data=x_data, + labels=labels_data, + multichannel=False, + spacing=image.spacing + ) + + expected = skimage.measure.label(expected) + + actual = workspace.get_objects("RandomWalkerAlgorithm") + + numpy.testing.assert_array_equal(expected, actual.segmented)