Skip to content

Commit 662bb00

Browse files
authored
make addnoise plugin (#246)
* make addnoise plugin * add AddNoise to docs
1 parent f0e4db1 commit 662bb00

File tree

2 files changed

+294
-3
lines changed

2 files changed

+294
-3
lines changed

active_plugins/addnoise.py

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
"""
2+
AddNoise
3+
========================
4+
5+
**AddNoise** adds noise to an image.
6+
7+
|
8+
9+
============ ============ ===============
10+
Supports 2D? Supports 3D? Respects masks?
11+
============ ============ ===============
12+
YES YES NO
13+
============ ============ ===============
14+
15+
"""
16+
17+
import numpy
18+
from cellprofiler_core.image import Image
19+
from cellprofiler_core.module import Module
20+
from cellprofiler_core.setting import Divider, Binary
21+
from cellprofiler_core.setting import SettingsGroup
22+
from cellprofiler_core.setting.choice import Choice
23+
from cellprofiler_core.setting.do_something import DoSomething
24+
from cellprofiler_core.setting.do_something import RemoveSettingButton
25+
from cellprofiler_core.setting.subscriber import ImageSubscriber
26+
from cellprofiler_core.setting.text import ImageName
27+
from cellprofiler_core.setting.text import Float
28+
29+
SETTINGS_PER_IMAGE = 2
30+
31+
A_GAUSSIAN = "Gaussian"
32+
A_POISSON = "Poisson"
33+
A_SANDP = "Salt and Pepper"
34+
35+
G_MU = "mu"
36+
G_SIGMA = "sigma"
37+
38+
I_PERCENT = "Percent image with noise"
39+
40+
class AddNoise(Module):
41+
category = "Image Processing"
42+
variable_revision_number = 1
43+
module_name = "AddNoise"
44+
45+
def create_settings(self):
46+
"""Make settings here (and set the module name)"""
47+
self.images = []
48+
self.add_image(can_delete=False)
49+
self.add_image_button = DoSomething("", "Add another image", self.add_image)
50+
self.truncate_low = Binary(
51+
"Set output image values less than 0 equal to 0?",
52+
True,
53+
doc="""\
54+
Values outside the range 0 to 1 might not be handled well by other
55+
modules. Select *"Yes"* to set negative values to 0, which was previously
56+
done automatically without ability to override.
57+
""" )
58+
59+
self.truncate_high = Binary(
60+
"Set output image values greater than 1 equal to 1?",
61+
True,
62+
doc="""\
63+
Values outside the range 0 to 1 might not be handled well by other
64+
modules. Select *"Yes"* to set values greater than 1 to a maximum
65+
value of 1.
66+
""")
67+
self.method = Choice(
68+
"Select the operation",
69+
[A_GAUSSIAN, A_POISSON, A_SANDP],
70+
doc="""\
71+
Select what kind of noise you want to add.
72+
73+
- *{A_GAUSSIAN}:* Gaussian noise has a normally distributed probability density function.
74+
It is independent of the original intensities in the image.
75+
{G_MU} is the mean and {G_SIGMA} is the standard deviation.
76+
- *{A_POISSON}:* Poisson noise is correlated with the intensity of each pixel. Also called Shot Noise.
77+
- *{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.
78+
You can set the {I_PERCENT}.
79+
""".format(
80+
**{"A_GAUSSIAN": A_GAUSSIAN, "A_POISSON": A_POISSON, "A_SANDP": A_SANDP,
81+
"G_MU": G_MU, "G_SIGMA": G_SIGMA, "I_PERCENT": I_PERCENT},
82+
),
83+
)
84+
85+
self.mu = Float(
86+
"mu (mean)",
87+
value = 0,
88+
doc="""\
89+
*(Used only if “{A_GAUSSIAN}” is selected)*
90+
Enter the mean of the Gaussian noise
91+
""".format(
92+
**{
93+
"A_GAUSSIAN": A_GAUSSIAN
94+
}
95+
),
96+
)
97+
98+
self.sigma = Float(
99+
"sigma (standard deviation)",
100+
value = .1,
101+
doc="""\
102+
*(Used only if “{A_GAUSSIAN}” is selected)*
103+
Enter the standard deviation of the Gaussian noise
104+
""".format(
105+
**{
106+
"A_GAUSSIAN": A_GAUSSIAN
107+
}
108+
),
109+
)
110+
111+
self.percent = Float(
112+
"percent of image to salt and pepper",
113+
value = .1,
114+
doc="""\
115+
*(Used only if “{A_SANDP}” is selected)*
116+
Enter the percentage of the image to salt and pepper
117+
""".format(
118+
**{
119+
"A_SANDP": A_SANDP
120+
}
121+
),
122+
)
123+
124+
125+
def add_image(self, can_delete=True):
126+
"""Add an image and its settings to the list of images"""
127+
image_name = ImageSubscriber(
128+
"Select the input image", "None", doc="Select the image to add noise to."
129+
)
130+
131+
noised_image_name = ImageName(
132+
"Name the output image",
133+
"NoisedBlue",
134+
doc="Enter a name for the noisy image.",
135+
)
136+
137+
image_settings = SettingsGroup()
138+
image_settings.append("image_name", image_name)
139+
image_settings.append("noised_image_name", noised_image_name)
140+
141+
if can_delete:
142+
image_settings.append(
143+
"remover",
144+
RemoveSettingButton(
145+
"", "Remove this image", self.images, image_settings
146+
),
147+
)
148+
image_settings.append("divider", Divider())
149+
self.images.append(image_settings)
150+
151+
def settings(self):
152+
"""Return the settings to be loaded or saved to/from the pipeline
153+
154+
These are the settings (from cellprofiler_core.settings) that are
155+
either read from the strings in the pipeline or written out
156+
to the pipeline. The settings should appear in a consistent
157+
order so they can be matched to the strings in the pipeline.
158+
"""
159+
result = [self.method,self.mu,self.sigma]
160+
for image in self.images:
161+
result += [
162+
image.image_name,
163+
image.noised_image_name,
164+
]
165+
result += [
166+
self.truncate_low,
167+
self.truncate_high,
168+
]
169+
return result
170+
171+
def visible_settings(self):
172+
"""Return the list of displayed settings
173+
"""
174+
result = [self.method]
175+
for image in self.images:
176+
result += [
177+
image.image_name,
178+
image.noised_image_name,
179+
]
180+
#
181+
# Get the "remover" button if there is one
182+
#
183+
remover = getattr(image, "remover", None)
184+
if remover is not None:
185+
result.append(remover)
186+
result.append(image.divider)
187+
result.append(self.add_image_button)
188+
result.append(self.truncate_low)
189+
result.append(self.truncate_high)
190+
if self.method == A_GAUSSIAN:
191+
result.append(self.mu)
192+
result.append(self.sigma)
193+
if self.method == A_SANDP:
194+
result.append(self.percent)
195+
return result
196+
197+
def run(self, workspace):
198+
"""Run the module
199+
200+
workspace - The workspace contains
201+
pipeline - instance of cpp for this run
202+
image_set - the images in the image set being processed
203+
object_set - the objects (labeled masks) in this image set
204+
measurements - the measurements for this run
205+
frame - the parent frame to whatever frame is created. None means don't draw.
206+
"""
207+
for image in self.images:
208+
self.run_image(image, workspace)
209+
210+
def run_image(self, image, workspace):
211+
#
212+
# Get the image names from the settings
213+
#
214+
image_name = image.image_name.value
215+
noised_image_name = image.noised_image_name.value
216+
#
217+
# Get images from the image set
218+
#
219+
orig_image = workspace.image_set.get_image(image_name, must_be_grayscale=True)
220+
221+
if self.method.value == A_GAUSSIAN:
222+
output_pixels = self.add_gaussian(orig_image, self.mu.value, self.sigma.value)
223+
if self.method.value == A_POISSON:
224+
output_pixels = self.add_poisson(orig_image)
225+
if self.method.value == A_SANDP:
226+
output_pixels = self.add_impulse(orig_image, self.percent.value)
227+
#
228+
# Optionally, clip high and low values
229+
#
230+
if self.truncate_low.value:
231+
output_pixels = numpy.where(output_pixels < 0, 0, output_pixels)
232+
if self.truncate_high.value:
233+
output_pixels = numpy.where(output_pixels > 1, 1, output_pixels)
234+
235+
y = Image(dimensions=orig_image.dimensions, image=output_pixels, parent_image=orig_image, convert=False)
236+
workspace.image_set.add(noised_image_name, y)
237+
#
238+
# Save images for display
239+
#
240+
if self.show_window:
241+
if not hasattr(workspace.display_data, "images"):
242+
workspace.display_data.images = {}
243+
workspace.display_data.images[image_name] = orig_image.pixel_data
244+
workspace.display_data.images[noised_image_name] = output_pixels
245+
246+
def add_gaussian(self, orig_image, mu, sigma):
247+
noise_mask = numpy.random.normal(mu, sigma, orig_image.pixel_data.shape)
248+
noisy_pixels = orig_image.pixel_data + noise_mask
249+
return noisy_pixels
250+
def add_poisson(self, orig_image):
251+
noise_mask = numpy.random.poisson(orig_image.pixel_data)
252+
noisy_pixels = orig_image.pixel_data + noise_mask
253+
return noisy_pixels
254+
def add_impulse(self, orig_image, percent):
255+
random_indices = numpy.random.choice(orig_image.pixel_data.size, round(orig_image.pixel_data.size*percent))
256+
noise = numpy.random.choice([orig_image.pixel_data.min(), orig_image.pixel_data.max()], round(orig_image.pixel_data.size*percent))
257+
noisy_pixels = orig_image.pixel_data.copy()
258+
noisy_pixels.flat[random_indices] = noise
259+
return noisy_pixels
260+
261+
def display(self, workspace, figure):
262+
""" Display one row of orig / noised per image setting group"""
263+
figure.set_subplots((2, len(self.images)))
264+
for j, image in enumerate(self.images):
265+
image_name = image.image_name.value
266+
noised_image_name = image.noised_image_name.value
267+
orig_image = workspace.display_data.images[image_name]
268+
noised_image = workspace.display_data.images[noised_image_name]
269+
270+
def imshow(x, y, image, *args, **kwargs):
271+
if image.ndim == 2:
272+
f = figure.subplot_imshow_grayscale
273+
else:
274+
f = figure.subplot_imshow_color
275+
return f(x, y, image, *args, **kwargs)
276+
277+
imshow(
278+
0,
279+
j,
280+
orig_image,
281+
"Original image: %s" % image_name,
282+
sharexy=figure.subplot(0, 0),
283+
)
284+
imshow(
285+
1,
286+
j,
287+
noised_image,
288+
"Final image: %s" % noised_image_name,
289+
sharexy=figure.subplot(0, 0),
290+
)
291+

documentation/CP-plugins-documentation/supported_plugins.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
Below is a brief overview of our currently supported plugins.
44
For details about using any particular plugin, please read the module documentation inside the plugin in CellProfiler.
55

6-
Most plugins will run without any special installation of either CellProfiler or the plugins.
6+
Most plugins will run without any special installation of either CellProfiler or the plugins.
77
See [using plugins](using_plugins.md) for how to set up CellProfiler for plugin use as well as for installation information for those plugins that do require installation of dependencies.
88

99
Most plugin documentation can be found within the plugin itself and can be accessed through CellProfiler help.
1010
Those plugins that do have extra documentation contain links below.
1111

12-
1312
| Plugin | Description | Requires installation of dependencies? | Install flag | Docker version currently available? |
1413
|--------|-------------|----------------------------------------|--------------|-------------------------------------|
14+
| AddNoise | AddNoise adds Gaussian, Poisson, or Salt and Pepper noise to images. Of particular use for data augmentation in deep learning. | No | | N/A |
1515
| CalculateMoments | CalculateMoments extracts moments statistics from a given distribution of pixel values. | No | | N/A |
1616
| CallBarcodes | CallBarcodes is used for assigning a barcode to an object based on the channel with the strongest intensity for a given number of cycles. It is used for optical sequencing by synthesis (SBS). | No | | N/A |
1717
| CompensateColors | CompensateColors determines how much signal in any given channel is because of bleed-through from another channel and removes the bleed-through. It can be performed across an image or masked to objects and provides a number of preprocessing and rescaling options to allow for troubleshooting if input image intensities are not well matched. | No | | N/A |
@@ -21,7 +21,7 @@ Those plugins that do have extra documentation contain links below.
2121
| HistogramMatching | HistogramMatching manipulates the pixel intensity values an input image and matches them to the histogram of a reference image. It can be used as a way to normalize intensities across different 2D or 3D images or different frames of the same 3D image. It allows you to choose which frame to use as the reference. | No | | N/A |
2222
| PixelShuffle | PixelShuffle takes the intensity of each pixel in an image and randomly shuffles its position. | No | | N/A |
2323
| [RunCellpose](RunCellPose.md) | RunCellpose allows you to run Cellpose within CellProfiler. Cellpose is a generalist machine-learning algorithm for cellular segmentation and is a great starting point for segmenting non-round cells. You can use pre-trained Cellpose models or your custom model with this plugin. You can use a GPU with this module to dramatically increase your speed/efficiency. | Yes | `cellpose` | Yes |
24-
| Runilastik | Runilasitk allows to run ilastik within CellProfiler. You can use pre-trained ilastik projects/models to predict the probability of your input images. The plugin supports two types of ilastik projects: Pixel Classification and Autocontext (2-stage).| Yes | | Yes |
24+
| Runilastik | Runilasitk allows to run ilastik within CellProfiler. You can use pre-trained ilastik projects/models to predict the probability of your input images. The plugin supports two types of ilastik projects: Pixel Classification and Autocontext (2-stage).| Yes | | Yes |
2525
| RunImageJScript | RunImageJScript allows you to run any supported ImageJ script directly within CellProfiler. It is significantly more performant than RunImageJMacro, and is also less likely to leave behind temporary files. | Yes | `imagejscript` , though note that conda installation may be preferred, see [this link](https://py.imagej.net/en/latest/Install.html#installing-via-pip) for more information | No |
2626
| RunOmnipose | RunOmnipose allows you to run Omnipose within CellProfiler. Omnipose is a general image segmentation tool that builds on Cellpose. | Yes | `omnipose` | No |
2727
| RunStarDist | RunStarDist allows you to run StarDist within CellProfiler. StarDist is a machine-learning algorithm for object detection with star-convex shapes making it best suited for nuclei or round-ish cells. You can use pre-trained StarDist models or your custom model with this plugin. You can use a GPU with this module to dramatically increase your speed/efficiency. RunStarDist is generally faster than RunCellpose. | Yes | `stardist` | No |

0 commit comments

Comments
 (0)