-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into prebuild_plugins_update
- Loading branch information
Showing
12 changed files
with
959 additions
and
273 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
cff-version: 1.2.0 | ||
message: "If using CellProfiler Plugins in a pipeline used in a publication, please cite our CellProfiler-Plugins paper. Additionally, some plugins require citation themselves if used. You can determine which plugins require citation by reading the Help information for each plugin, accessible through CellProfiler, or by using the Citation generator built into CellProfiler (since CellProfiler 5)." | ||
type: software | ||
authors: | ||
- name: 'Imaging Platform, Broad Institute of Harvard and MIT' | ||
city: Cambridge | ||
country: US | ||
repository: https://github.com/CellProfiler/CellProfiler-plugins | ||
title: "CellProfiler plugins" | ||
doi: https://doi.org/10.1111/jmi.13223 | ||
date-released: 2023 | ||
preferred-citation: | ||
type: article | ||
authors: | ||
- family-names: "Weisbart" | ||
given-names: "Erin" | ||
orcid: "https://orcid.org/0000-0002-6437-2458" | ||
- family-names: "Tromans-Coia" | ||
given-names: "Callum" | ||
orcid: "https://orcid.org/0000-0002-5518-8915" | ||
- family-names: "Diaz-Rohrer" | ||
given-names: "Barbara" | ||
orcid: "https://orcid.org/0000-0002-4748-7077" | ||
- family-names: "Stirling" | ||
given-names: "David R." | ||
orcid: "https://orcid.org/0000-0001-6802-4103" | ||
- family-names: "Garcia-Fossa" | ||
given-names: "Fernanda" | ||
orcid: "https://orcid.org/0000-0003-2308-0149" | ||
- family-names: "Senft" | ||
given-names: "Rebecca A." | ||
orcid: "https://orcid.org/0000-0003-0081-4170" | ||
- family-names: "Hiner" | ||
given-names: "Mark C." | ||
orcid: "https://orcid.org/0000-0001-9404-7579" | ||
- family-names: "de Jesus" | ||
given-names: "Marcelo B." | ||
orcid: "https://orcid.org/0000-0003-0812-1491" | ||
- family-names: "Eliceiri" | ||
given-names: "Kevin W." | ||
orcid: "https://orcid.org/0000-0001-8678-670X" | ||
- family-names: "Cimini" | ||
given-names: "Beth A." | ||
orcid: "https://orcid.org/0000-0001-9640-9318" | ||
doi: "https://doi.org/10.1111/jmi.13223" | ||
journal: "Journal of Microscopy" | ||
title: "CellProfiler plugins – An easy image analysis platform integration for containers and Python tools." | ||
year: 2023 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,291 @@ | ||
""" | ||
AddNoise | ||
======================== | ||
**AddNoise** adds noise to an image. | ||
| | ||
============ ============ =============== | ||
Supports 2D? Supports 3D? Respects masks? | ||
============ ============ =============== | ||
YES YES NO | ||
============ ============ =============== | ||
""" | ||
|
||
import numpy | ||
from cellprofiler_core.image import Image | ||
from cellprofiler_core.module import Module | ||
from cellprofiler_core.setting import Divider, Binary | ||
from cellprofiler_core.setting import SettingsGroup | ||
from cellprofiler_core.setting.choice import Choice | ||
from cellprofiler_core.setting.do_something import DoSomething | ||
from cellprofiler_core.setting.do_something import RemoveSettingButton | ||
from cellprofiler_core.setting.subscriber import ImageSubscriber | ||
from cellprofiler_core.setting.text import ImageName | ||
from cellprofiler_core.setting.text import Float | ||
|
||
SETTINGS_PER_IMAGE = 2 | ||
|
||
A_GAUSSIAN = "Gaussian" | ||
A_POISSON = "Poisson" | ||
A_SANDP = "Salt and Pepper" | ||
|
||
G_MU = "mu" | ||
G_SIGMA = "sigma" | ||
|
||
I_PERCENT = "Percent image with noise" | ||
|
||
class AddNoise(Module): | ||
category = "Image Processing" | ||
variable_revision_number = 1 | ||
module_name = "AddNoise" | ||
|
||
def create_settings(self): | ||
"""Make settings here (and set the module name)""" | ||
self.images = [] | ||
self.add_image(can_delete=False) | ||
self.add_image_button = DoSomething("", "Add another image", self.add_image) | ||
self.truncate_low = Binary( | ||
"Set output image values less than 0 equal to 0?", | ||
True, | ||
doc="""\ | ||
Values outside the range 0 to 1 might not be handled well by other | ||
modules. Select *"Yes"* to set negative values to 0, which was previously | ||
done automatically without ability to override. | ||
""" ) | ||
|
||
self.truncate_high = Binary( | ||
"Set output image values greater than 1 equal to 1?", | ||
True, | ||
doc="""\ | ||
Values outside the range 0 to 1 might not be handled well by other | ||
modules. Select *"Yes"* to set values greater than 1 to a maximum | ||
value of 1. | ||
""") | ||
self.method = Choice( | ||
"Select the operation", | ||
[A_GAUSSIAN, A_POISSON, A_SANDP], | ||
doc="""\ | ||
Select what kind of noise you want to add. | ||
- *{A_GAUSSIAN}:* Gaussian noise has a normally distributed probability density function. | ||
It is independent of the original intensities in the image. | ||
{G_MU} is the mean and {G_SIGMA} is the standard deviation. | ||
- *{A_POISSON}:* Poisson noise is correlated with the intensity of each pixel. Also called Shot Noise. | ||
- *{A_SANDP}:* Salt and Pepper is a type of impulse noise where there is a sparse occurance of maximum and minimum pixel values in an image. | ||
You can set the {I_PERCENT}. | ||
""".format( | ||
**{"A_GAUSSIAN": A_GAUSSIAN, "A_POISSON": A_POISSON, "A_SANDP": A_SANDP, | ||
"G_MU": G_MU, "G_SIGMA": G_SIGMA, "I_PERCENT": I_PERCENT}, | ||
), | ||
) | ||
|
||
self.mu = Float( | ||
"mu (mean)", | ||
value = 0, | ||
doc="""\ | ||
*(Used only if “{A_GAUSSIAN}” is selected)* | ||
Enter the mean of the Gaussian noise | ||
""".format( | ||
**{ | ||
"A_GAUSSIAN": A_GAUSSIAN | ||
} | ||
), | ||
) | ||
|
||
self.sigma = Float( | ||
"sigma (standard deviation)", | ||
value = .1, | ||
doc="""\ | ||
*(Used only if “{A_GAUSSIAN}” is selected)* | ||
Enter the standard deviation of the Gaussian noise | ||
""".format( | ||
**{ | ||
"A_GAUSSIAN": A_GAUSSIAN | ||
} | ||
), | ||
) | ||
|
||
self.percent = Float( | ||
"percent of image to salt and pepper", | ||
value = .1, | ||
doc="""\ | ||
*(Used only if “{A_SANDP}” is selected)* | ||
Enter the percentage of the image to salt and pepper | ||
""".format( | ||
**{ | ||
"A_SANDP": A_SANDP | ||
} | ||
), | ||
) | ||
|
||
|
||
def add_image(self, can_delete=True): | ||
"""Add an image and its settings to the list of images""" | ||
image_name = ImageSubscriber( | ||
"Select the input image", "None", doc="Select the image to add noise to." | ||
) | ||
|
||
noised_image_name = ImageName( | ||
"Name the output image", | ||
"NoisedBlue", | ||
doc="Enter a name for the noisy image.", | ||
) | ||
|
||
image_settings = SettingsGroup() | ||
image_settings.append("image_name", image_name) | ||
image_settings.append("noised_image_name", noised_image_name) | ||
|
||
if can_delete: | ||
image_settings.append( | ||
"remover", | ||
RemoveSettingButton( | ||
"", "Remove this image", self.images, image_settings | ||
), | ||
) | ||
image_settings.append("divider", Divider()) | ||
self.images.append(image_settings) | ||
|
||
def settings(self): | ||
"""Return the settings to be loaded or saved to/from the pipeline | ||
These are the settings (from cellprofiler_core.settings) that are | ||
either read from the strings in the pipeline or written out | ||
to the pipeline. The settings should appear in a consistent | ||
order so they can be matched to the strings in the pipeline. | ||
""" | ||
result = [self.method,self.mu,self.sigma] | ||
for image in self.images: | ||
result += [ | ||
image.image_name, | ||
image.noised_image_name, | ||
] | ||
result += [ | ||
self.truncate_low, | ||
self.truncate_high, | ||
] | ||
return result | ||
|
||
def visible_settings(self): | ||
"""Return the list of displayed settings | ||
""" | ||
result = [self.method] | ||
for image in self.images: | ||
result += [ | ||
image.image_name, | ||
image.noised_image_name, | ||
] | ||
# | ||
# Get the "remover" button if there is one | ||
# | ||
remover = getattr(image, "remover", None) | ||
if remover is not None: | ||
result.append(remover) | ||
result.append(image.divider) | ||
result.append(self.add_image_button) | ||
result.append(self.truncate_low) | ||
result.append(self.truncate_high) | ||
if self.method == A_GAUSSIAN: | ||
result.append(self.mu) | ||
result.append(self.sigma) | ||
if self.method == A_SANDP: | ||
result.append(self.percent) | ||
return result | ||
|
||
def run(self, workspace): | ||
"""Run the module | ||
workspace - The workspace contains | ||
pipeline - instance of cpp for this run | ||
image_set - the images in the image set being processed | ||
object_set - the objects (labeled masks) in this image set | ||
measurements - the measurements for this run | ||
frame - the parent frame to whatever frame is created. None means don't draw. | ||
""" | ||
for image in self.images: | ||
self.run_image(image, workspace) | ||
|
||
def run_image(self, image, workspace): | ||
# | ||
# Get the image names from the settings | ||
# | ||
image_name = image.image_name.value | ||
noised_image_name = image.noised_image_name.value | ||
# | ||
# Get images from the image set | ||
# | ||
orig_image = workspace.image_set.get_image(image_name, must_be_grayscale=True) | ||
|
||
if self.method.value == A_GAUSSIAN: | ||
output_pixels = self.add_gaussian(orig_image, self.mu.value, self.sigma.value) | ||
if self.method.value == A_POISSON: | ||
output_pixels = self.add_poisson(orig_image) | ||
if self.method.value == A_SANDP: | ||
output_pixels = self.add_impulse(orig_image, self.percent.value) | ||
# | ||
# Optionally, clip high and low values | ||
# | ||
if self.truncate_low.value: | ||
output_pixels = numpy.where(output_pixels < 0, 0, output_pixels) | ||
if self.truncate_high.value: | ||
output_pixels = numpy.where(output_pixels > 1, 1, output_pixels) | ||
|
||
y = Image(dimensions=orig_image.dimensions, image=output_pixels, parent_image=orig_image, convert=False) | ||
workspace.image_set.add(noised_image_name, y) | ||
# | ||
# Save images for display | ||
# | ||
if self.show_window: | ||
if not hasattr(workspace.display_data, "images"): | ||
workspace.display_data.images = {} | ||
workspace.display_data.images[image_name] = orig_image.pixel_data | ||
workspace.display_data.images[noised_image_name] = output_pixels | ||
|
||
def add_gaussian(self, orig_image, mu, sigma): | ||
noise_mask = numpy.random.normal(mu, sigma, orig_image.pixel_data.shape) | ||
noisy_pixels = orig_image.pixel_data + noise_mask | ||
return noisy_pixels | ||
def add_poisson(self, orig_image): | ||
noise_mask = numpy.random.poisson(orig_image.pixel_data) | ||
noisy_pixels = orig_image.pixel_data + noise_mask | ||
return noisy_pixels | ||
def add_impulse(self, orig_image, percent): | ||
random_indices = numpy.random.choice(orig_image.pixel_data.size, round(orig_image.pixel_data.size*percent)) | ||
noise = numpy.random.choice([orig_image.pixel_data.min(), orig_image.pixel_data.max()], round(orig_image.pixel_data.size*percent)) | ||
noisy_pixels = orig_image.pixel_data.copy() | ||
noisy_pixels.flat[random_indices] = noise | ||
return noisy_pixels | ||
|
||
def display(self, workspace, figure): | ||
""" Display one row of orig / noised per image setting group""" | ||
figure.set_subplots((2, len(self.images))) | ||
for j, image in enumerate(self.images): | ||
image_name = image.image_name.value | ||
noised_image_name = image.noised_image_name.value | ||
orig_image = workspace.display_data.images[image_name] | ||
noised_image = workspace.display_data.images[noised_image_name] | ||
|
||
def imshow(x, y, image, *args, **kwargs): | ||
if image.ndim == 2: | ||
f = figure.subplot_imshow_grayscale | ||
else: | ||
f = figure.subplot_imshow_color | ||
return f(x, y, image, *args, **kwargs) | ||
|
||
imshow( | ||
0, | ||
j, | ||
orig_image, | ||
"Original image: %s" % image_name, | ||
sharexy=figure.subplot(0, 0), | ||
) | ||
imshow( | ||
1, | ||
j, | ||
noised_image, | ||
"Final image: %s" % noised_image_name, | ||
sharexy=figure.subplot(0, 0), | ||
) | ||
|
Oops, something went wrong.