CellProfiler-nightly 5.0.0.dev318__py3-none-any.whl → 5.0.0.dev324__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cellprofiler/_version.py +2 -2
- cellprofiler/modules/crop.py +129 -228
- cellprofiler/modules/erodeimage.py +2 -4
- {cellprofiler_nightly-5.0.0.dev318.dist-info → cellprofiler_nightly-5.0.0.dev324.dist-info}/METADATA +1 -1
- {cellprofiler_nightly-5.0.0.dev318.dist-info → cellprofiler_nightly-5.0.0.dev324.dist-info}/RECORD +9 -9
- {cellprofiler_nightly-5.0.0.dev318.dist-info → cellprofiler_nightly-5.0.0.dev324.dist-info}/WHEEL +0 -0
- {cellprofiler_nightly-5.0.0.dev318.dist-info → cellprofiler_nightly-5.0.0.dev324.dist-info}/entry_points.txt +0 -0
- {cellprofiler_nightly-5.0.0.dev318.dist-info → cellprofiler_nightly-5.0.0.dev324.dist-info}/licenses/LICENSE +0 -0
- {cellprofiler_nightly-5.0.0.dev318.dist-info → cellprofiler_nightly-5.0.0.dev324.dist-info}/top_level.txt +0 -0
cellprofiler/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '5.0.0.
|
|
32
|
-
__version_tuple__ = version_tuple = (5, 0, 0, '
|
|
31
|
+
__version__ = version = '5.0.0.dev324'
|
|
32
|
+
__version_tuple__ = version_tuple = (5, 0, 0, 'dev324')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
cellprofiler/modules/crop.py
CHANGED
|
@@ -38,14 +38,13 @@ help for more information on saving cropping shapes.
|
|
|
38
38
|
"""
|
|
39
39
|
|
|
40
40
|
import logging
|
|
41
|
-
import math
|
|
42
|
-
|
|
43
41
|
import centrosome.filter
|
|
44
42
|
import matplotlib.axes
|
|
45
43
|
import matplotlib.cm
|
|
46
44
|
import matplotlib.figure
|
|
47
45
|
import matplotlib.patches
|
|
48
46
|
import numpy
|
|
47
|
+
from cellprofiler_core.constants.measurement import GROUP_INDEX
|
|
49
48
|
from cellprofiler_core.image import Image
|
|
50
49
|
from cellprofiler_core.module import Module
|
|
51
50
|
from cellprofiler_core.preferences import get_primary_outline_color
|
|
@@ -57,39 +56,11 @@ from cellprofiler_core.setting.subscriber import ImageSubscriber
|
|
|
57
56
|
from cellprofiler_core.setting.subscriber import LabelSubscriber
|
|
58
57
|
from cellprofiler_core.setting.text import CropImageName
|
|
59
58
|
from cellprofiler_core.setting.text import Integer
|
|
60
|
-
from
|
|
61
|
-
|
|
59
|
+
from cellprofiler_library.functions.image_processing import get_ellipse_cropping, get_rectangle_cropping
|
|
60
|
+
from cellprofiler_library.modules._crop import crop, get_measurements
|
|
61
|
+
from cellprofiler_library.opts.crop import RemovalMethod, Measurement, Shape, CroppingMethod, CroppingPattern, Limits, Ellipse, Rectangle
|
|
62
62
|
LOGGER = logging.getLogger(__name__)
|
|
63
63
|
|
|
64
|
-
SH_RECTANGLE = "Rectangle"
|
|
65
|
-
SH_ELLIPSE = "Ellipse"
|
|
66
|
-
SH_IMAGE = "Image"
|
|
67
|
-
SH_OBJECTS = "Objects"
|
|
68
|
-
SH_CROPPING = "Previous cropping"
|
|
69
|
-
CM_COORDINATES = "Coordinates"
|
|
70
|
-
CM_MOUSE = "Mouse"
|
|
71
|
-
IO_INDIVIDUALLY = "Every"
|
|
72
|
-
IO_FIRST = "First"
|
|
73
|
-
RM_NO = "No"
|
|
74
|
-
RM_EDGES = "Edges"
|
|
75
|
-
RM_ALL = "All"
|
|
76
|
-
|
|
77
|
-
# Doesn't seem to like importing defs from cellprofiler.gui.moduleview so define here
|
|
78
|
-
ABSOLUTE = "Absolute"
|
|
79
|
-
FROM_EDGE = "From edge"
|
|
80
|
-
|
|
81
|
-
EL_XCENTER = "xcenter"
|
|
82
|
-
EL_YCENTER = "ycenter"
|
|
83
|
-
EL_XRADIUS = "xradius"
|
|
84
|
-
EL_YRADIUS = "yradius"
|
|
85
|
-
|
|
86
|
-
RE_LEFT = "left"
|
|
87
|
-
RE_TOP = "top"
|
|
88
|
-
RE_RIGHT = "right"
|
|
89
|
-
RE_BOTTOM = "bottom"
|
|
90
|
-
|
|
91
|
-
FF_AREA_RETAINED = "Crop_AreaRetainedAfterCropping_%s"
|
|
92
|
-
FF_ORIGINAL_AREA = "Crop_OriginalImageArea_%s"
|
|
93
64
|
|
|
94
65
|
OFF_IMAGE_NAME = 0
|
|
95
66
|
OFF_CROPPED_IMAGE_NAME = 1
|
|
@@ -130,8 +101,8 @@ class Crop(Module):
|
|
|
130
101
|
|
|
131
102
|
self.shape = Choice(
|
|
132
103
|
text="Select the cropping shape",
|
|
133
|
-
choices=[
|
|
134
|
-
value=
|
|
104
|
+
choices=[Shape.RECTANGLE.value, Shape.ELLIPSE.value, Shape.IMAGE.value, Shape.OBJECTS.value, Shape.CROPPING.value],
|
|
105
|
+
value=Shape.RECTANGLE.value,
|
|
135
106
|
doc="""\
|
|
136
107
|
Choose the shape into which you would like to crop:
|
|
137
108
|
|
|
@@ -161,19 +132,19 @@ Choose the shape into which you would like to crop:
|
|
|
161
132
|
cropping that was used to generate whichever image you choose.
|
|
162
133
|
""".format(
|
|
163
134
|
**{
|
|
164
|
-
"SH_RECTANGLE":
|
|
165
|
-
"SH_ELLIPSE":
|
|
166
|
-
"SH_IMAGE":
|
|
167
|
-
"SH_OBJECTS":
|
|
168
|
-
"SH_CROPPING":
|
|
135
|
+
"SH_RECTANGLE": Shape.RECTANGLE.value,
|
|
136
|
+
"SH_ELLIPSE": Shape.ELLIPSE.value,
|
|
137
|
+
"SH_IMAGE": Shape.IMAGE.value,
|
|
138
|
+
"SH_OBJECTS": Shape.OBJECTS.value,
|
|
139
|
+
"SH_CROPPING": Shape.CROPPING.value,
|
|
169
140
|
}
|
|
170
141
|
),
|
|
171
142
|
)
|
|
172
143
|
|
|
173
144
|
self.crop_method = Choice(
|
|
174
145
|
text="Select the cropping method",
|
|
175
|
-
choices=[
|
|
176
|
-
value=
|
|
146
|
+
choices=[CroppingMethod.COORDINATES.value, CroppingMethod.MOUSE.value],
|
|
147
|
+
value=CroppingMethod.COORDINATES.value,
|
|
177
148
|
doc="""\
|
|
178
149
|
Choose whether you would like to crop by typing in pixel coordinates or
|
|
179
150
|
clicking with the mouse.
|
|
@@ -190,18 +161,18 @@ clicking with the mouse.
|
|
|
190
161
|
are in the interior of the region you wish to retain.
|
|
191
162
|
""".format(
|
|
192
163
|
**{
|
|
193
|
-
"CM_COORDINATES":
|
|
194
|
-
"SH_ELLIPSE":
|
|
195
|
-
"SH_RECTANGLE":
|
|
196
|
-
"CM_MOUSE":
|
|
164
|
+
"CM_COORDINATES": CroppingMethod.COORDINATES.value,
|
|
165
|
+
"SH_ELLIPSE": Shape.ELLIPSE.value,
|
|
166
|
+
"SH_RECTANGLE": Shape.RECTANGLE.value,
|
|
167
|
+
"CM_MOUSE": CroppingMethod.MOUSE.value,
|
|
197
168
|
}
|
|
198
169
|
),
|
|
199
170
|
)
|
|
200
171
|
|
|
201
172
|
self.individual_or_once = Choice(
|
|
202
173
|
text="Apply which cycle's cropping pattern?",
|
|
203
|
-
choices=[
|
|
204
|
-
value=
|
|
174
|
+
choices=[CroppingPattern.INDIVIDUALLY.value, CroppingPattern.FIRST.value],
|
|
175
|
+
value=CroppingPattern.INDIVIDUALLY.value,
|
|
205
176
|
doc="""\
|
|
206
177
|
Specify how a given cropping pattern should be applied to other image cycles:
|
|
207
178
|
|
|
@@ -210,7 +181,7 @@ Specify how a given cropping pattern should be applied to other image cycles:
|
|
|
210
181
|
intended to function as a template in some fashion.
|
|
211
182
|
- *{IO_INDIVIDUALLY}:* Every image cycle is cropped individually.
|
|
212
183
|
""".format(
|
|
213
|
-
**{"IO_FIRST":
|
|
184
|
+
**{"IO_FIRST": CroppingPattern.FIRST.value, "IO_INDIVIDUALLY": CroppingPattern.INDIVIDUALLY.value}
|
|
214
185
|
),
|
|
215
186
|
)
|
|
216
187
|
|
|
@@ -232,9 +203,9 @@ Specify the left and right positions for the bounding rectangle by selecting one
|
|
|
232
203
|
of the image’s original size.
|
|
233
204
|
""".format(
|
|
234
205
|
**{
|
|
235
|
-
"SH_RECTANGLE":
|
|
236
|
-
"ABSOLUTE": ABSOLUTE,
|
|
237
|
-
"FROM_EDGE": FROM_EDGE,
|
|
206
|
+
"SH_RECTANGLE": Shape.RECTANGLE.value,
|
|
207
|
+
"ABSOLUTE": Limits.ABSOLUTE.value,
|
|
208
|
+
"FROM_EDGE": Limits.FROM_EDGE.value,
|
|
238
209
|
}
|
|
239
210
|
),
|
|
240
211
|
)
|
|
@@ -255,9 +226,9 @@ Specify the top and bottom positions for the bounding rectangle by selecting one
|
|
|
255
226
|
from the edges of your images irrespective of their size.
|
|
256
227
|
""".format(
|
|
257
228
|
**{
|
|
258
|
-
"SH_RECTANGLE":
|
|
259
|
-
"ABSOLUTE": ABSOLUTE,
|
|
260
|
-
"FROM_EDGE": FROM_EDGE,
|
|
229
|
+
"SH_RECTANGLE": Shape.RECTANGLE.value,
|
|
230
|
+
"ABSOLUTE": Limits.ABSOLUTE.value,
|
|
231
|
+
"FROM_EDGE": Limits.FROM_EDGE.value,
|
|
261
232
|
}
|
|
262
233
|
),
|
|
263
234
|
)
|
|
@@ -270,7 +241,7 @@ Specify the top and bottom positions for the bounding rectangle by selecting one
|
|
|
270
241
|
|
|
271
242
|
Specify the center pixel position of the ellipse.
|
|
272
243
|
""".format(
|
|
273
|
-
**{"SH_ELLIPSE":
|
|
244
|
+
**{"SH_ELLIPSE": Shape.ELLIPSE.value}
|
|
274
245
|
),
|
|
275
246
|
)
|
|
276
247
|
|
|
@@ -282,7 +253,7 @@ Specify the center pixel position of the ellipse.
|
|
|
282
253
|
|
|
283
254
|
Specify the radius of the ellipse in the X direction.
|
|
284
255
|
""".format(
|
|
285
|
-
**{"SH_ELLIPSE":
|
|
256
|
+
**{"SH_ELLIPSE": Shape.ELLIPSE.value}
|
|
286
257
|
),
|
|
287
258
|
)
|
|
288
259
|
|
|
@@ -294,7 +265,7 @@ Specify the radius of the ellipse in the X direction.
|
|
|
294
265
|
|
|
295
266
|
Specify the radius of the ellipse in the Y direction.
|
|
296
267
|
""".format(
|
|
297
|
-
**{"SH_ELLIPSE":
|
|
268
|
+
**{"SH_ELLIPSE": Shape.ELLIPSE.value}
|
|
298
269
|
),
|
|
299
270
|
)
|
|
300
271
|
|
|
@@ -306,7 +277,7 @@ Specify the radius of the ellipse in the Y direction.
|
|
|
306
277
|
|
|
307
278
|
Select the image to be use as a cropping mask.
|
|
308
279
|
""".format(
|
|
309
|
-
**{"SH_IMAGE":
|
|
280
|
+
**{"SH_IMAGE": Shape.IMAGE.value}
|
|
310
281
|
),
|
|
311
282
|
)
|
|
312
283
|
|
|
@@ -318,7 +289,7 @@ Select the image to be use as a cropping mask.
|
|
|
318
289
|
|
|
319
290
|
Select the image associated with the cropping mask that you want to use.
|
|
320
291
|
""".format(
|
|
321
|
-
**{"SH_CROPPING":
|
|
292
|
+
**{"SH_CROPPING": Shape.CROPPING.value}
|
|
322
293
|
),
|
|
323
294
|
)
|
|
324
295
|
|
|
@@ -330,14 +301,14 @@ Select the image associated with the cropping mask that you want to use.
|
|
|
330
301
|
|
|
331
302
|
Select the objects that are to be used as a cropping mask.
|
|
332
303
|
""".format(
|
|
333
|
-
**{"SH_OBJECTS":
|
|
304
|
+
**{"SH_OBJECTS": Shape.OBJECTS.value}
|
|
334
305
|
),
|
|
335
306
|
)
|
|
336
307
|
|
|
337
308
|
self.remove_rows_and_columns = Choice(
|
|
338
309
|
text="Remove empty rows and columns?",
|
|
339
|
-
choices=[
|
|
340
|
-
value=
|
|
310
|
+
choices=[RemovalMethod.NO.value, RemovalMethod.EDGES.value, RemovalMethod.ALL.value],
|
|
311
|
+
value=RemovalMethod.ALL.value,
|
|
341
312
|
doc="""\
|
|
342
313
|
Use this option to choose whether to remove rows and columns that lack
|
|
343
314
|
objects:
|
|
@@ -349,7 +320,7 @@ objects:
|
|
|
349
320
|
- *{RM_ALL}:* Remove any row or column of all-blank pixels, even
|
|
350
321
|
from the internal portion of the image.
|
|
351
322
|
""".format(
|
|
352
|
-
**{"RM_NO":
|
|
323
|
+
**{"RM_NO": RemovalMethod.NO.value, "RM_EDGES": RemovalMethod.EDGES.value, "RM_ALL": RemovalMethod.ALL.value}
|
|
353
324
|
),
|
|
354
325
|
)
|
|
355
326
|
|
|
@@ -373,22 +344,22 @@ objects:
|
|
|
373
344
|
|
|
374
345
|
def visible_settings(self):
|
|
375
346
|
result = [self.image_name, self.cropped_image_name, self.shape]
|
|
376
|
-
if self.shape.value in (
|
|
347
|
+
if self.shape.value in (Shape.RECTANGLE, Shape.ELLIPSE):
|
|
377
348
|
result += [self.crop_method, self.individual_or_once]
|
|
378
|
-
if self.crop_method ==
|
|
379
|
-
if self.shape ==
|
|
349
|
+
if self.crop_method.value == CroppingMethod.COORDINATES:
|
|
350
|
+
if self.shape.value == Shape.RECTANGLE:
|
|
380
351
|
result += [self.horizontal_limits, self.vertical_limits]
|
|
381
|
-
elif self.shape ==
|
|
352
|
+
elif self.shape.value == Shape.ELLIPSE:
|
|
382
353
|
result += [
|
|
383
354
|
self.ellipse_center,
|
|
384
355
|
self.ellipse_x_radius,
|
|
385
356
|
self.ellipse_y_radius,
|
|
386
357
|
]
|
|
387
|
-
elif self.shape ==
|
|
358
|
+
elif self.shape.value == Shape.IMAGE:
|
|
388
359
|
result += [self.image_mask_source]
|
|
389
|
-
elif self.shape ==
|
|
360
|
+
elif self.shape.value == Shape.CROPPING:
|
|
390
361
|
result.append(self.cropping_mask_source)
|
|
391
|
-
elif self.shape ==
|
|
362
|
+
elif self.shape.value == Shape.OBJECTS:
|
|
392
363
|
result.append(self.objects_source)
|
|
393
364
|
else:
|
|
394
365
|
raise NotImplementedError("Unimplemented shape type: %s" % self.shape.value)
|
|
@@ -397,25 +368,25 @@ objects:
|
|
|
397
368
|
|
|
398
369
|
def run(self, workspace):
|
|
399
370
|
first_image_set = (
|
|
400
|
-
workspace.measurements.get_current_image_measurement(
|
|
371
|
+
workspace.measurements.get_current_image_measurement(GROUP_INDEX) == 1
|
|
401
372
|
)
|
|
402
373
|
image_set_list = workspace.image_set_list
|
|
403
|
-
|
|
374
|
+
cache_dict = self.get_dictionary(image_set_list)
|
|
404
375
|
orig_image = workspace.image_set.get_image(self.image_name.value)
|
|
405
376
|
recalculate_flag = (
|
|
406
|
-
self.shape not in (
|
|
407
|
-
or self.individual_or_once ==
|
|
377
|
+
self.shape.value not in (Shape.ELLIPSE, Shape.RECTANGLE)
|
|
378
|
+
or self.individual_or_once.value == CroppingPattern.INDIVIDUALLY
|
|
408
379
|
or first_image_set
|
|
409
380
|
or workspace.pipeline.test_mode
|
|
410
381
|
)
|
|
411
|
-
save_flag = self.individual_or_once ==
|
|
382
|
+
save_flag = self.individual_or_once.value == CroppingPattern.FIRST and first_image_set
|
|
412
383
|
if not recalculate_flag:
|
|
413
|
-
if
|
|
384
|
+
if cache_dict[D_FIRST_CROPPING].shape != orig_image.pixel_data.shape[:2]:
|
|
414
385
|
recalculate_flag = True
|
|
415
386
|
LOGGER.warning(
|
|
416
387
|
"""Image, "%s", size changed from %s to %s during cycle %d, recalculating""",
|
|
417
388
|
self.image_name.value,
|
|
418
|
-
str(
|
|
389
|
+
str(cache_dict[D_FIRST_CROPPING].shape),
|
|
419
390
|
str(orig_image.pixel_data.shape[:2]),
|
|
420
391
|
workspace.image_set.image_number,
|
|
421
392
|
)
|
|
@@ -423,59 +394,54 @@ objects:
|
|
|
423
394
|
cropping = None
|
|
424
395
|
masking_objects = None
|
|
425
396
|
if not recalculate_flag:
|
|
426
|
-
cropping =
|
|
427
|
-
mask =
|
|
428
|
-
elif self.shape ==
|
|
397
|
+
cropping = cache_dict[D_FIRST_CROPPING]
|
|
398
|
+
mask = cache_dict[D_FIRST_CROPPING_MASK]
|
|
399
|
+
elif self.shape.value == Shape.CROPPING:
|
|
429
400
|
cropping_image = workspace.image_set.get_image(
|
|
430
401
|
self.cropping_mask_source.value
|
|
431
402
|
)
|
|
432
403
|
cropping = cropping_image.crop_mask
|
|
433
|
-
elif self.shape ==
|
|
404
|
+
elif self.shape.value == Shape.IMAGE:
|
|
434
405
|
source_image = workspace.image_set.get_image(
|
|
435
406
|
self.image_mask_source.value
|
|
436
407
|
).pixel_data
|
|
437
408
|
|
|
438
409
|
cropping = source_image > 0
|
|
439
|
-
elif self.shape ==
|
|
410
|
+
elif self.shape.value == Shape.OBJECTS:
|
|
440
411
|
masking_objects = workspace.get_objects(self.objects_source.value)
|
|
441
412
|
cropping = masking_objects.segmented > 0
|
|
442
|
-
elif self.crop_method ==
|
|
413
|
+
elif self.crop_method.value == CroppingMethod.MOUSE:
|
|
443
414
|
cropping = self.ui_crop(workspace, orig_image)
|
|
444
|
-
elif self.shape ==
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
if orig_image.has_mask:
|
|
457
|
-
image_mask = mask & orig_image.mask
|
|
458
|
-
else:
|
|
459
|
-
image_mask = mask
|
|
460
|
-
else:
|
|
461
|
-
internal_cropping = self.remove_rows_and_columns == RM_ALL
|
|
462
|
-
cropped_pixel_data = crop_image(
|
|
463
|
-
orig_image.pixel_data, cropping, internal_cropping
|
|
415
|
+
elif self.shape.value == Shape.ELLIPSE:
|
|
416
|
+
cache_dict[Shape.ELLIPSE] = {
|
|
417
|
+
Ellipse.XCENTER: self.ellipse_center.x,
|
|
418
|
+
Ellipse.YCENTER: self.ellipse_center.y,
|
|
419
|
+
Ellipse.XRADIUS: self.ellipse_x_radius.value,
|
|
420
|
+
Ellipse.YRADIUS: self.ellipse_y_radius.value,
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
cropping = get_ellipse_cropping(
|
|
424
|
+
orig_image.pixel_data,
|
|
425
|
+
(self.ellipse_center.x, self.ellipse_center.y),
|
|
426
|
+
(self.ellipse_x_radius.value, self.ellipse_y_radius.value)
|
|
464
427
|
)
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
if
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
428
|
+
|
|
429
|
+
elif self.shape.value == Shape.RECTANGLE:
|
|
430
|
+
h_min = self.horizontal_limits.min if not self.horizontal_limits.unbounded_min else None
|
|
431
|
+
h_max = self.horizontal_limits.max if not self.horizontal_limits.unbounded_max else None
|
|
432
|
+
v_min = self.vertical_limits.min if not self.vertical_limits.unbounded_min else None
|
|
433
|
+
v_max = self.vertical_limits.max if not self.vertical_limits.unbounded_max else None
|
|
434
|
+
|
|
435
|
+
cropping = get_rectangle_cropping(orig_image.pixel_data, (h_min, h_max, v_min, v_max), validate_boundaries=True)
|
|
436
|
+
else:
|
|
437
|
+
raise NotImplementedError(f"Cropping shape {self.shape.value} or crop method {self.crop_method} not supported.")
|
|
438
|
+
|
|
439
|
+
assert(cropping is not None)
|
|
440
|
+
assert(cropping.dtype == bool)
|
|
441
|
+
|
|
442
|
+
cropped_pixel_data, mask, image_mask = crop(orig_image.pixel_data, cropping, mask, orig_image.mask, self.remove_rows_and_columns.value)
|
|
443
|
+
|
|
444
|
+
if self.shape.value == Shape.OBJECTS:
|
|
479
445
|
# Special handling for objects - masked objects instead of
|
|
480
446
|
# mask and crop mask
|
|
481
447
|
output_image = Image(
|
|
@@ -501,8 +467,8 @@ objects:
|
|
|
501
467
|
)
|
|
502
468
|
|
|
503
469
|
if save_flag:
|
|
504
|
-
|
|
505
|
-
|
|
470
|
+
cache_dict[D_FIRST_CROPPING_MASK] = mask
|
|
471
|
+
cache_dict[D_FIRST_CROPPING] = cropping
|
|
506
472
|
#
|
|
507
473
|
# Save the image / cropping / mask
|
|
508
474
|
#
|
|
@@ -510,13 +476,10 @@ objects:
|
|
|
510
476
|
#
|
|
511
477
|
# Save the old and new image sizes
|
|
512
478
|
#
|
|
513
|
-
original_image_area = numpy.product(orig_image.pixel_data.shape[:2])
|
|
514
|
-
area_retained_after_cropping = numpy.sum(cropping)
|
|
515
|
-
feature = FF_AREA_RETAINED % self.cropped_image_name.value
|
|
516
479
|
m = workspace.measurements
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
480
|
+
for measurement in get_measurements(cropping, orig_image.pixel_data, self.cropped_image_name.value):
|
|
481
|
+
m.add_measurement("Image", measurement[1], numpy.array([measurement[2]]))
|
|
482
|
+
|
|
520
483
|
|
|
521
484
|
def display(self, workspace, figure):
|
|
522
485
|
orig_image_pixel_data = workspace.display_data.orig_image_pixel_data
|
|
@@ -536,20 +499,28 @@ objects:
|
|
|
536
499
|
"""Return information on the measurements made during cropping"""
|
|
537
500
|
return [
|
|
538
501
|
("Image", x % self.cropped_image_name.value, "integer",)
|
|
539
|
-
for x in (
|
|
502
|
+
for x in (Measurement.AREA_RETAINED, Measurement.ORIGINAL_AREA)
|
|
540
503
|
]
|
|
541
504
|
|
|
542
505
|
def ui_crop(self, workspace, orig_image):
|
|
543
506
|
"""Crop into a rectangle or ellipse, guided by UI"""
|
|
544
|
-
|
|
545
|
-
if (self.shape.value not in
|
|
546
|
-
|
|
547
|
-
self,
|
|
507
|
+
cache_dict = self.get_dictionary(workspace.image_set_list)
|
|
508
|
+
if (self.shape.value not in cache_dict) or self.individual_or_once.value == CroppingPattern.INDIVIDUALLY:
|
|
509
|
+
cache_dict[self.shape.value] = workspace.interaction_request(
|
|
510
|
+
self, cache_dict.get(self.shape.value, None), orig_image.pixel_data
|
|
548
511
|
)
|
|
549
|
-
if self.shape ==
|
|
550
|
-
|
|
512
|
+
if self.shape.value == Shape.ELLIPSE:
|
|
513
|
+
center = cache_dict[Shape.ELLIPSE][Ellipse.XCENTER], cache_dict[Shape.ELLIPSE][Ellipse.YCENTER]
|
|
514
|
+
radius = cache_dict[Shape.ELLIPSE][Ellipse.XRADIUS], cache_dict[Shape.ELLIPSE][Ellipse.YRADIUS]
|
|
515
|
+
return get_ellipse_cropping(orig_image.pixel_data, center, radius)
|
|
551
516
|
else:
|
|
552
|
-
|
|
517
|
+
bounding_box = (
|
|
518
|
+
int(numpy.round(cache_dict[Shape.RECTANGLE][Rectangle.LEFT])),
|
|
519
|
+
int(numpy.round(cache_dict[Shape.RECTANGLE][Rectangle.RIGHT])),
|
|
520
|
+
int(numpy.round(cache_dict[Shape.RECTANGLE][Rectangle.TOP])),
|
|
521
|
+
int(numpy.round(cache_dict[Shape.RECTANGLE][Rectangle.BOTTOM])),
|
|
522
|
+
)
|
|
523
|
+
return get_rectangle_cropping(orig_image.pixel_data, bounding_box, validate_boundaries=True)
|
|
553
524
|
|
|
554
525
|
def handle_interaction(self, current_shape, orig_image):
|
|
555
526
|
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
|
|
@@ -788,31 +759,31 @@ objects:
|
|
|
788
759
|
def handles(self):
|
|
789
760
|
return [self.center_handle, self.radius_handle]
|
|
790
761
|
|
|
791
|
-
if self.shape ==
|
|
762
|
+
if self.shape.value == Shape.ELLIPSE:
|
|
792
763
|
if current_shape is None:
|
|
793
764
|
current_shape = {
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
765
|
+
Ellipse.XCENTER: pixel_data.shape[1] / 2,
|
|
766
|
+
Ellipse.YCENTER: pixel_data.shape[0] / 2,
|
|
767
|
+
Ellipse.XRADIUS: pixel_data.shape[1] / 2,
|
|
768
|
+
Ellipse.YRADIUS: pixel_data.shape[0] / 2,
|
|
798
769
|
}
|
|
799
770
|
ellipse = current_shape
|
|
800
771
|
shape = CropEllipse(
|
|
801
|
-
(ellipse[
|
|
802
|
-
(ellipse[
|
|
772
|
+
(ellipse[Ellipse.XCENTER], ellipse[Ellipse.YCENTER]),
|
|
773
|
+
(ellipse[Ellipse.XRADIUS], ellipse[Ellipse.YRADIUS]),
|
|
803
774
|
)
|
|
804
775
|
else:
|
|
805
776
|
if current_shape is None:
|
|
806
777
|
current_shape = {
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
778
|
+
Rectangle.LEFT: pixel_data.shape[1] / 4,
|
|
779
|
+
Rectangle.TOP: pixel_data.shape[0] / 4,
|
|
780
|
+
Rectangle.RIGHT: pixel_data.shape[1] * 3 / 4,
|
|
781
|
+
Rectangle.BOTTOM: pixel_data.shape[0] * 3 / 4,
|
|
811
782
|
}
|
|
812
783
|
rectangle = current_shape
|
|
813
784
|
shape = CropRectangle(
|
|
814
|
-
(rectangle[
|
|
815
|
-
(rectangle[
|
|
785
|
+
(rectangle[Rectangle.LEFT], rectangle[Rectangle.TOP]),
|
|
786
|
+
(rectangle[Rectangle.RIGHT], rectangle[Rectangle.BOTTOM]),
|
|
816
787
|
)
|
|
817
788
|
for patch in shape.patches:
|
|
818
789
|
axes.add_artist(patch)
|
|
@@ -843,91 +814,21 @@ objects:
|
|
|
843
814
|
raise ValueError("Cancelled by user")
|
|
844
815
|
finally:
|
|
845
816
|
dialog_box.Destroy()
|
|
846
|
-
if self.shape ==
|
|
817
|
+
if self.shape.value == Shape.RECTANGLE:
|
|
847
818
|
return {
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
819
|
+
Rectangle.LEFT: shape.left,
|
|
820
|
+
Rectangle.TOP: shape.top,
|
|
821
|
+
Rectangle.RIGHT: shape.right,
|
|
822
|
+
Rectangle.BOTTOM: shape.bottom,
|
|
852
823
|
}
|
|
853
824
|
else:
|
|
854
825
|
return {
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
826
|
+
Ellipse.XCENTER: shape.center_x,
|
|
827
|
+
Ellipse.YCENTER: shape.center_y,
|
|
828
|
+
Ellipse.XRADIUS: shape.width / 2,
|
|
829
|
+
Ellipse.YRADIUS: shape.height / 2,
|
|
859
830
|
}
|
|
860
831
|
|
|
861
|
-
def get_ellipse_cropping(self, workspace, orig_image):
|
|
862
|
-
"""Crop into an ellipse using user-specified coordinates"""
|
|
863
|
-
x_center = self.ellipse_center.x
|
|
864
|
-
y_center = self.ellipse_center.y
|
|
865
|
-
x_radius = self.ellipse_x_radius.value
|
|
866
|
-
y_radius = self.ellipse_y_radius.value
|
|
867
|
-
d = self.get_dictionary(workspace.image_set_list)
|
|
868
|
-
d[SH_ELLIPSE] = {
|
|
869
|
-
EL_XCENTER: x_center,
|
|
870
|
-
EL_YCENTER: y_center,
|
|
871
|
-
EL_XRADIUS: x_radius,
|
|
872
|
-
EL_YRADIUS: y_radius,
|
|
873
|
-
}
|
|
874
|
-
return self.apply_ellipse_cropping(workspace, orig_image)
|
|
875
|
-
|
|
876
|
-
def apply_ellipse_cropping(self, workspace, orig_image):
|
|
877
|
-
d = self.get_dictionary(workspace.image_set_list)
|
|
878
|
-
ellipse = d[SH_ELLIPSE]
|
|
879
|
-
x_center, y_center, x_radius, y_radius = [
|
|
880
|
-
ellipse[x] for x in (EL_XCENTER, EL_YCENTER, EL_XRADIUS, EL_YRADIUS)
|
|
881
|
-
]
|
|
882
|
-
pixel_data = orig_image.pixel_data
|
|
883
|
-
x_max = pixel_data.shape[1]
|
|
884
|
-
y_max = pixel_data.shape[0]
|
|
885
|
-
if x_radius > y_radius:
|
|
886
|
-
dist_x = math.sqrt(x_radius ** 2 - y_radius ** 2)
|
|
887
|
-
dist_y = 0
|
|
888
|
-
major_radius = x_radius
|
|
889
|
-
else:
|
|
890
|
-
dist_x = 0
|
|
891
|
-
dist_y = math.sqrt(y_radius ** 2 - x_radius ** 2)
|
|
892
|
-
major_radius = y_radius
|
|
893
|
-
|
|
894
|
-
focus_1_x, focus_1_y = (x_center - dist_x, y_center - dist_y)
|
|
895
|
-
focus_2_x, focus_2_y = (x_center + dist_x, y_center + dist_y)
|
|
896
|
-
y, x = numpy.mgrid[0:y_max, 0:x_max]
|
|
897
|
-
d1 = numpy.sqrt((x - focus_1_x) ** 2 + (y - focus_1_y) ** 2)
|
|
898
|
-
d2 = numpy.sqrt((x - focus_2_x) ** 2 + (y - focus_2_y) ** 2)
|
|
899
|
-
cropping = d1 + d2 <= major_radius * 2
|
|
900
|
-
return cropping
|
|
901
|
-
|
|
902
|
-
def get_rectangle_cropping(self, workspace, orig_image):
|
|
903
|
-
"""Crop into a rectangle using user-specified coordinates"""
|
|
904
|
-
cropping = numpy.ones(orig_image.pixel_data.shape[:2], bool)
|
|
905
|
-
if not self.horizontal_limits.unbounded_min:
|
|
906
|
-
cropping[:, : self.horizontal_limits.min] = False
|
|
907
|
-
if not self.horizontal_limits.unbounded_max:
|
|
908
|
-
cropping[:, self.horizontal_limits.max :] = False
|
|
909
|
-
if not self.vertical_limits.unbounded_min:
|
|
910
|
-
cropping[: self.vertical_limits.min, :] = False
|
|
911
|
-
if not self.vertical_limits.unbounded_max:
|
|
912
|
-
cropping[self.vertical_limits.max :, :] = False
|
|
913
|
-
return cropping
|
|
914
|
-
|
|
915
|
-
def apply_rectangle_cropping(self, workspace, orig_image):
|
|
916
|
-
cropping = numpy.ones(orig_image.pixel_data.shape[:2], bool)
|
|
917
|
-
d = self.get_dictionary(workspace.image_set_list)
|
|
918
|
-
r = d[SH_RECTANGLE]
|
|
919
|
-
left, top, right, bottom = [
|
|
920
|
-
int(numpy.round(r[x])) for x in (RE_LEFT, RE_TOP, RE_RIGHT, RE_BOTTOM)
|
|
921
|
-
]
|
|
922
|
-
if left > 0:
|
|
923
|
-
cropping[:, :left] = False
|
|
924
|
-
if right < cropping.shape[1]:
|
|
925
|
-
cropping[:, right:] = False
|
|
926
|
-
if top > 0:
|
|
927
|
-
cropping[:top, :] = False
|
|
928
|
-
if bottom < cropping.shape[0]:
|
|
929
|
-
cropping[bottom:, :] = False
|
|
930
|
-
return cropping
|
|
931
832
|
|
|
932
833
|
def upgrade_settings(self, setting_values, variable_revision_number, module_name):
|
|
933
834
|
if variable_revision_number == 1:
|
|
@@ -940,12 +841,12 @@ objects:
|
|
|
940
841
|
# minor - "Cropping" changed to "Previous cropping"
|
|
941
842
|
setting_values = list(setting_values)
|
|
942
843
|
if setting_values[OFF_SHAPE] == "Cropping":
|
|
943
|
-
setting_values[OFF_SHAPE] =
|
|
844
|
+
setting_values[OFF_SHAPE] = Shape.CROPPING
|
|
944
845
|
#
|
|
945
846
|
# Individually changed to "every"
|
|
946
847
|
#
|
|
947
848
|
if setting_values[OFF_INDIVIDUAL_OR_ONCE] == "Individually":
|
|
948
|
-
setting_values[OFF_INDIVIDUAL_OR_ONCE] =
|
|
849
|
+
setting_values[OFF_INDIVIDUAL_OR_ONCE] = CroppingPattern.INDIVIDUALLY
|
|
949
850
|
|
|
950
851
|
setting_values = setting_values[:10] + setting_values[11:]
|
|
951
852
|
|
|
@@ -19,9 +19,8 @@ YES YES NO
|
|
|
19
19
|
from cellprofiler_core.module import ImageProcessing
|
|
20
20
|
from cellprofiler_core.setting import StructuringElement
|
|
21
21
|
|
|
22
|
-
import cellprofiler.utilities.morphology
|
|
23
22
|
from cellprofiler.modules._help import HELP_FOR_STREL
|
|
24
|
-
|
|
23
|
+
from cellprofiler_library.modules._erodeimage import erode_image
|
|
25
24
|
|
|
26
25
|
class ErodeImage(ImageProcessing):
|
|
27
26
|
category = "Advanced"
|
|
@@ -48,6 +47,5 @@ class ErodeImage(ImageProcessing):
|
|
|
48
47
|
return __settings__ + [self.structuring_element]
|
|
49
48
|
|
|
50
49
|
def run(self, workspace):
|
|
51
|
-
self.function =
|
|
52
|
-
|
|
50
|
+
self.function = erode_image
|
|
53
51
|
super(ErodeImage, self).run(workspace)
|
{cellprofiler_nightly-5.0.0.dev318.dist-info → cellprofiler_nightly-5.0.0.dev324.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: CellProfiler-nightly
|
|
3
|
-
Version: 5.0.0.
|
|
3
|
+
Version: 5.0.0.dev324
|
|
4
4
|
Summary: CellProfiler is a free open-source software designed to enable biologists without training in computer vision or programming to quantitatively measure phenotypes from thousands of images automatically.
|
|
5
5
|
Author: Anne Carpenter, Thouis (Ray) Jones, Lee Kamentsky, Vebjorn Ljosa, David Logan, Mark Bray, Madison Swain-Bowden, Allen Goodman, Claire McQuinn, Alice Lucas, Callum Tromans-Coia
|
|
6
6
|
Author-email: Beth Cimini <bcimini@broadinstitute.org>, David Stirling <dstirling@glencoesoftware.com>, Nodar Gogoberidze <ngogober@broadinstitute.org>
|
{cellprofiler_nightly-5.0.0.dev318.dist-info → cellprofiler_nightly-5.0.0.dev324.dist-info}/RECORD
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
cellprofiler/__init__.py,sha256=AL2XeOBhIeYkBRyDd0QRgJan7j0DKjT1GD-RdzKvMUY,46
|
|
2
2
|
cellprofiler/__main__.py,sha256=uy78oz5c6NBGRwDZkZ2Gl4HfhbJZQH6K1n54Qfl2OFc,38075
|
|
3
|
-
cellprofiler/_version.py,sha256=
|
|
3
|
+
cellprofiler/_version.py,sha256=X_dsIS438JBC3vSFB9YEnaGPIPrvCqTB7mibbwXlkv0,721
|
|
4
4
|
cellprofiler/knime_bridge.py,sha256=T6Op-KO79oULx92nGXRQ6lHsEcTutx1Uep1L4ZOKJgc,27767
|
|
5
5
|
cellprofiler/misc.py,sha256=yqv873lP_mquxxkKcLgE_ZU4Hrc1trtuQ-NXLK2qQVc,553
|
|
6
6
|
cellprofiler/data/examples/ExampleFly/ExampleFly.cppipe,sha256=JGZK9IZuYlOHOI4hi6a3DK36IahY69cfeLEd7eJ_rO0,15409
|
|
@@ -290,7 +290,7 @@ cellprofiler/modules/convertobjectstoimage.py,sha256=zvn2nlnF3-i6g16F0um1yNT-Y03
|
|
|
290
290
|
cellprofiler/modules/correctilluminationapply.py,sha256=eEcXliF2xNkT36Bo7xkNNh7l2YGbSqkqpss4O7RbwaM,15862
|
|
291
291
|
cellprofiler/modules/correctilluminationcalculate.py,sha256=wJMCy3W9zK-xPI8PUvh25MLtE_JSI3QuVVzdLjuTwD4,54200
|
|
292
292
|
cellprofiler/modules/createbatchfiles.py,sha256=a8R5PpbaQYqF8OIvWBuC0uh-yhjzBaU4Uni4m3MRLok,19512
|
|
293
|
-
cellprofiler/modules/crop.py,sha256=
|
|
293
|
+
cellprofiler/modules/crop.py,sha256=gnAHZNjPCNr-RLc7xQjZYViRpmKCTfJjuZ8MwHRiT1Y,34177
|
|
294
294
|
cellprofiler/modules/definegrid.py,sha256=tjYcrzHu6TV7rkh1l2T0pNqcjBLwCfz9BCqTPWhQr_A,47160
|
|
295
295
|
cellprofiler/modules/dilateimage.py,sha256=xcdsDdgvuplD69pcyJTOmVTxXFlwDWRNxapoQZHTiVc,1382
|
|
296
296
|
cellprofiler/modules/dilateobjects.py,sha256=lnipjKkd6A3hhY3G1ttU9ZlhsTZp3oVU-QnU7_ayWbE,1828
|
|
@@ -302,7 +302,7 @@ cellprofiler/modules/displayscatterplot.py,sha256=sgB5Uf03mtUFMy3X6GqNNImhlKBWfZ
|
|
|
302
302
|
cellprofiler/modules/editobjectsmanually.py,sha256=fkTFMTlrTfIiHNhhPzYsfFvZBSBDEIJgM7pZ7gDrMNA,21527
|
|
303
303
|
cellprofiler/modules/enhanceedges.py,sha256=bivBs2OMNYvNnJ7tYrs9yheJP9JGIkgJDQfB2u837HE,11398
|
|
304
304
|
cellprofiler/modules/enhanceorsuppressfeatures.py,sha256=vc_LLANt2gEsntqog9JwbzCVaaBQEM3ayvvY7CSJCF0,24062
|
|
305
|
-
cellprofiler/modules/erodeimage.py,sha256=
|
|
305
|
+
cellprofiler/modules/erodeimage.py,sha256=GGO6EjgA_6A0pjrsATxcfs1a_QmZ1rS5Lec7Kkb21hw,1402
|
|
306
306
|
cellprofiler/modules/erodeobjects.py,sha256=cDQR8y7XqSt-ssp1nAQp3wXdQk5OE1uT7GrhTgjfdlI,4381
|
|
307
307
|
cellprofiler/modules/expandorshrinkobjects.py,sha256=fiFf4q2BplR4u6Cym-jdlOOdOKc6oZVtEkZE-O-Ansg,13911
|
|
308
308
|
cellprofiler/modules/exporttodatabase.py,sha256=Tb5Hf329m96zUNaxPStQoTkr_MV62HGNBetZL_Y1iyk,217927
|
|
@@ -374,9 +374,9 @@ cellprofiler/modules/plugins/segmentationtemplatewithdependencies.py,sha256=Sh76
|
|
|
374
374
|
cellprofiler/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
375
375
|
cellprofiler/utilities/morphology.py,sha256=8-81TrP8AmE3ETXIvQUKFD1vmKNBy2lfbc1QnM1eGIM,2685
|
|
376
376
|
cellprofiler/utilities/rules.py,sha256=NoIHwFTA37zGvIP7vcB-aYeys0MDYVYxspfhLJe00OU,8790
|
|
377
|
-
cellprofiler_nightly-5.0.0.
|
|
378
|
-
cellprofiler_nightly-5.0.0.
|
|
379
|
-
cellprofiler_nightly-5.0.0.
|
|
380
|
-
cellprofiler_nightly-5.0.0.
|
|
381
|
-
cellprofiler_nightly-5.0.0.
|
|
382
|
-
cellprofiler_nightly-5.0.0.
|
|
377
|
+
cellprofiler_nightly-5.0.0.dev324.dist-info/licenses/LICENSE,sha256=QLWaBS7kAioYx7PmJNXAMJaY8NODcFAag60YlUWuyz0,2276
|
|
378
|
+
cellprofiler_nightly-5.0.0.dev324.dist-info/METADATA,sha256=rcJmOQxLnbrpJc0xxW9H88YMgZrJgDMvTA-W-YDrcPE,6063
|
|
379
|
+
cellprofiler_nightly-5.0.0.dev324.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
380
|
+
cellprofiler_nightly-5.0.0.dev324.dist-info/entry_points.txt,sha256=MNDCjguFW3dKiS5Pcdu1NfWo4I0HHI3DekJLUJ4AKkY,60
|
|
381
|
+
cellprofiler_nightly-5.0.0.dev324.dist-info/top_level.txt,sha256=bK7AacDeSj9qAmW8MGlO5wA79hDj6-ACt_mENUNKSIk,13
|
|
382
|
+
cellprofiler_nightly-5.0.0.dev324.dist-info/RECORD,,
|
{cellprofiler_nightly-5.0.0.dev318.dist-info → cellprofiler_nightly-5.0.0.dev324.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|