Skip to content

Commit 6a6f14a

Browse files
committed
Start do crash module
1 parent 1766d60 commit 6a6f14a

File tree

1 file changed

+260
-0
lines changed

1 file changed

+260
-0
lines changed

active_plugins/crashdocker.py

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
import logging
2+
3+
from cellprofiler_core.image import Image
4+
from cellprofiler_core.module.image_segmentation import ImageSegmentation
5+
from cellprofiler_core.object import Objects
6+
from cellprofiler_core.setting.choice import Choice
7+
from cellprofiler_core.preferences import get_default_output_directory
8+
9+
LOGGER = logging.getLogger(__name__)
10+
11+
__doc__ = f"""\
12+
CrashDocker
13+
===========
14+
15+
**CrashDocker** crashes docker
16+
17+
============ ============ ===============
18+
Supports 2D? Supports 3D? Respects masks?
19+
============ ============ ===============
20+
YES YES NO
21+
============ ============ ===============
22+
23+
"""
24+
25+
class CrashDocker(ImageSegmentation):
26+
category = "Object Processing"
27+
28+
module_name = "CrashDocker"
29+
30+
variable_revision_number = 1
31+
32+
33+
def create_settings(self):
34+
super(CrashDocker, self).create_settings()
35+
36+
self.docker_or_python = Choice(
37+
text="Run in docker or local python environment",
38+
choices=["Docker", "Python"],
39+
value="Docker",
40+
doc="""\
41+
If Docker is selected, ensure that Docker Desktop is open and running on your
42+
computer. On first run of the RunCellpose plugin, the Docker container will be
43+
downloaded. However, this slow downloading process will only have to happen
44+
once.
45+
46+
If Python is selected, the Python environment in which CellProfiler and Cellpose
47+
are installed will be used.
48+
""",
49+
)
50+
51+
self.do_crash = Choice(
52+
text="Crash the run",
53+
choices=["Yes", "No"],
54+
value="Yes",
55+
doc="""\
56+
Cause this module to crash or succeed
57+
""",
58+
)
59+
60+
def settings(self):
61+
return [
62+
self.x_name,
63+
self.docker_or_python,
64+
self.do_crash,
65+
]
66+
67+
def visible_settings(self):
68+
vis_settings = super.visible_settings() + [self.docker_or_python, self.do_crash]
69+
70+
return vis_settings
71+
72+
def run(self, workspace):
73+
x_name = self.x_name.value
74+
y_name = self.y_name.value
75+
images = workspace.image_set
76+
x = images.get_image(x_name)
77+
dimensions = x.dimensions
78+
x_data = x.pixel_data
79+
80+
if self.docker_or_python.value == "Python":
81+
raise Exception("I am crashing")
82+
83+
elif self.docker_or_python.value == "Docker":
84+
# Define how to call docker
85+
docker_path = "docker" if sys.platform.lower().startswith("win") else "/usr/local/bin/docker"
86+
# Create a UUID for this run
87+
unique_name = str(uuid.uuid4())
88+
# Directory that will be used to pass images to the docker container
89+
temp_dir = os.path.join(get_default_output_directory(), ".cellprofiler_temp", unique_name)
90+
temp_img_dir = os.path.join(temp_dir, "img")
91+
92+
os.makedirs(temp_dir, exist_ok=True)
93+
os.makedirs(temp_img_dir, exist_ok=True)
94+
95+
temp_img_path = os.path.join(temp_img_dir, unique_name+".tiff")
96+
if self.mode.value == "custom":
97+
model_file = self.model_file_name.value
98+
model_directory = self.model_directory.get_absolute_path()
99+
model_path = os.path.join(model_directory, model_file)
100+
temp_model_dir = os.path.join(temp_dir, "model")
101+
102+
os.makedirs(temp_model_dir, exist_ok=True)
103+
# Copy the model
104+
shutil.copy(model_path, os.path.join(temp_model_dir, model_file))
105+
106+
# Save the image to the Docker mounted directory
107+
skimage.io.imsave(temp_img_path, x_data)
108+
109+
cmd = f"""
110+
{docker_path} run --rm -v {temp_dir}:/data
111+
{self.docker_image.value}
112+
{'--gpus all' if self.use_gpu.value else ''}
113+
cellpose
114+
--dir /data/img
115+
{'--pretrained_model ' + self.mode.value if self.mode.value != 'custom' else '--pretrained_model /data/model/' + model_file}
116+
--chan {channels[0]}
117+
--chan2 {channels[1]}
118+
--diameter {diam}
119+
{'--net_avg' if self.use_averaging.value else ''}
120+
{'--do_3D' if self.do_3D.value else ''}
121+
--anisotropy {anisotropy}
122+
--flow_threshold {self.flow_threshold.value}
123+
--cellprob_threshold {self.cellprob_threshold.value}
124+
--stitch_threshold {self.stitch_threshold.value}
125+
--min_size {self.min_size.value}
126+
{'--invert' if self.invert.value else ''}
127+
{'--exclude_on_edges' if self.remove_edge_masks.value else ''}
128+
--verbose
129+
"""
130+
131+
try:
132+
subprocess.run(cmd.split(), text=True)
133+
cellpose_output = numpy.load(os.path.join(temp_img_dir, unique_name + "_seg.npy"), allow_pickle=True).item()
134+
135+
y_data = cellpose_output["masks"]
136+
flows = cellpose_output["flows"]
137+
finally:
138+
# Delete the temporary files
139+
try:
140+
shutil.rmtree(temp_dir)
141+
except:
142+
LOGGER.error("Unable to delete temporary directory, files may be in use by another program.")
143+
LOGGER.error("Temp folder is subfolder {tempdir} in your Default Output Folder.\nYou may need to remove it manually.")
144+
145+
146+
y = Objects()
147+
y.segmented = y_data
148+
y.parent_image = x.parent_image
149+
objects = workspace.object_set
150+
objects.add_objects(y, y_name)
151+
152+
if self.save_probabilities.value:
153+
if self.docker_or_python.value == "Docker":
154+
# get rid of extra dimension
155+
prob_map = numpy.squeeze(flows[1], axis=0) # ranges 0-255
156+
else:
157+
prob_map = flows[2]
158+
rescale_prob_map = prob_map.copy()
159+
prob_map01 = numpy.percentile(rescale_prob_map, 1)
160+
prob_map99 = numpy.percentile(rescale_prob_map, 99)
161+
prob_map = numpy.clip((rescale_prob_map - prob_map01) / (prob_map99 - prob_map01), a_min=0, a_max=1)
162+
# Flows come out sized relative to CellPose's inbuilt model size.
163+
# We need to slightly resize to match the original image.
164+
size_corrected = skimage.transform.resize(prob_map, y_data.shape)
165+
prob_image = Image(
166+
size_corrected,
167+
parent_image=x.parent_image,
168+
convert=False,
169+
dimensions=len(size_corrected.shape),
170+
)
171+
172+
workspace.image_set.add(self.probabilities_name.value, prob_image)
173+
174+
if self.show_window:
175+
workspace.display_data.probabilities = size_corrected
176+
177+
self.add_measurements(workspace)
178+
179+
if self.show_window:
180+
if x.volumetric:
181+
# Can't show CellPose-accepted colour images in 3D
182+
workspace.display_data.x_data = x.pixel_data
183+
else:
184+
workspace.display_data.x_data = x_data
185+
workspace.display_data.y_data = y_data
186+
workspace.display_data.dimensions = dimensions
187+
188+
def display(self, workspace, figure):
189+
if self.save_probabilities.value:
190+
layout = (2, 2)
191+
else:
192+
layout = (2, 1)
193+
194+
figure.set_subplots(
195+
dimensions=workspace.display_data.dimensions, subplots=layout
196+
)
197+
198+
figure.subplot_imshow(
199+
colormap="gray",
200+
image=workspace.display_data.x_data,
201+
title="Input Image",
202+
x=0,
203+
y=0,
204+
)
205+
206+
figure.subplot_imshow_labels(
207+
image=workspace.display_data.y_data,
208+
sharexy=figure.subplot(0, 0),
209+
title=self.y_name.value,
210+
x=1,
211+
y=0,
212+
)
213+
if self.save_probabilities.value:
214+
figure.subplot_imshow(
215+
colormap="gray",
216+
image=workspace.display_data.probabilities,
217+
sharexy=figure.subplot(0, 0),
218+
title=self.probabilities_name.value,
219+
x=0,
220+
y=1,
221+
)
222+
223+
def do_check_gpu(self):
224+
import importlib.util
225+
torch_installed = importlib.util.find_spec('torch') is not None
226+
self.cellpose_ver = importlib.metadata.version('cellpose')
227+
#if the old version of cellpose <2.0, then use istorch kwarg
228+
if float(self.cellpose_ver[0:3]) >= 0.7 and int(self.cellpose_ver[0])<2:
229+
GPU_works = core.use_gpu(istorch=torch_installed)
230+
else: # if new version of cellpose, use use_torch kwarg
231+
GPU_works = core.use_gpu(use_torch=torch_installed)
232+
if GPU_works:
233+
message = "GPU appears to be working correctly!"
234+
else:
235+
message = (
236+
"GPU test failed. There may be something wrong with your configuration."
237+
)
238+
import wx
239+
240+
wx.MessageBox(message, caption="GPU Test")
241+
242+
def upgrade_settings(self, setting_values, variable_revision_number, module_name):
243+
if variable_revision_number == 1:
244+
setting_values = setting_values + ["0.4", "0.0"]
245+
variable_revision_number = 2
246+
if variable_revision_number == 2:
247+
setting_values = setting_values + ["0.0", False, "15", "1.0", False, False]
248+
variable_revision_number = 3
249+
if variable_revision_number == 3:
250+
setting_values = [setting_values[0]] + ["Python",CELLPOSE_DOCKER_IMAGE_WITH_PRETRAINED] + setting_values[1:]
251+
variable_revision_number = 4
252+
if variable_revision_number == 4:
253+
setting_values = [setting_values[0]] + ['No'] + setting_values[1:]
254+
variable_revision_number = 5
255+
if variable_revision_number == 5:
256+
setting_values = setting_values + [False]
257+
variable_revision_number = 6
258+
return setting_values, variable_revision_number
259+
260+

0 commit comments

Comments
 (0)