1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 from __future__ import division
32 import numpy
33 import _ni_support
34 import _nd_image
35 import filters
36 import types
37
38
40 structure = numpy.array(structure)
41 coor = tuple([oo + ss // 2 for ss, oo in zip(structure.shape,
42 origin)])
43 return bool(structure[coor])
44
46 """Iterate a structure by dilating it with itself.
47
48 If origin is None, only the iterated structure is returned. If
49 not, a tuple of the iterated structure and the modified origin is
50 returned.
51 """
52 structure = numpy.asarray(structure)
53 if iterations < 2:
54 return structure.copy()
55 ni = iterations - 1
56 shape = [ii + ni * (ii - 1) for ii in structure.shape]
57 pos = [ni * (structure.shape[ii] / 2) for ii in range(len(shape))]
58 slc = [slice(pos[ii], pos[ii] + structure.shape[ii], None)
59 for ii in range(len(shape))]
60 out = numpy.zeros(shape, bool)
61 out[slc] = structure != 0
62 out = binary_dilation(out, structure, iterations = ni)
63 if origin is None:
64 return out
65 else:
66 origin = _ni_support._normalize_sequence(origin, structure.ndim)
67 origin = [iterations * o for o in origin]
68 return out, origin
69
71 """Generate a binary structure for binary morphological operations.
72
73 The inputs are the rank of the array to which the structure will
74 be applied and the square of the connectivity of the structure.
75 """
76 if connectivity < 1:
77 connectivity = 1
78 if rank < 1:
79 if connectivity < 1:
80 return numpy.array(0, dtype = bool)
81 else:
82 return numpy.array(1, dtype = bool)
83 output = numpy.zeros([3] * rank, bool)
84 output = numpy.fabs(numpy.indices([3] * rank) - 1)
85 output = numpy.add.reduce(output, 0)
86 return numpy.asarray(output <= connectivity, dtype = bool)
87
88
89 -def _binary_erosion(input, structure, iterations, mask, output,
90 border_value, origin, invert, brute_force):
91 input = numpy.asarray(input)
92 if numpy.iscomplexobj(input):
93 raise TypeError, 'Complex type not supported'
94 if structure is None:
95 structure = generate_binary_structure(input.ndim, 1)
96 else:
97 structure = numpy.asarray(structure)
98 structure = structure.astype(bool)
99 if structure.ndim != input.ndim:
100 raise RuntimeError, 'structure rank must equal input rank'
101 if not structure.flags.contiguous:
102 structure = structure.copy()
103 if numpy.product(structure.shape,axis=0) < 1:
104 raise RuntimeError, 'structure must not be empty'
105 if mask is not None:
106 mask = numpy.asarray(mask)
107 if mask.shape != input.shape:
108 raise RuntimeError, 'mask and input must have equal sizes'
109 origin = _ni_support._normalize_sequence(origin, input.ndim)
110 cit = _center_is_true(structure, origin)
111 if isinstance(output, numpy.ndarray):
112 if numpy.iscomplexobj(output):
113 raise TypeError, 'Complex output type not supported'
114 else:
115 output = bool
116 output, return_value = _ni_support._get_output(output, input)
117
118
119 if iterations == 1:
120 _nd_image.binary_erosion(input, structure, mask, output,
121 border_value, origin, invert, cit, 0)
122 return return_value
123 elif cit and not brute_force:
124 changed, coordinate_list = _nd_image.binary_erosion(input,
125 structure, mask, output, border_value, origin, invert, cit, 1)
126 structure = structure[tuple([slice(None, None, -1)] *
127 structure.ndim)]
128 for ii in range(len(origin)):
129 origin[ii] = -origin[ii]
130 if not structure.shape[ii] & 1:
131 origin[ii] -= 1
132 if mask is not None:
133 msk = numpy.asarray(mask)
134 msk = mask.astype(numpy.int8)
135 if msk is mask:
136 msk = mask.copy()
137 mask = msk
138 if not structure.flags.contiguous:
139 structure = structure.copy()
140 _nd_image.binary_erosion2(output, structure, mask, iterations - 1,
141 origin, invert, coordinate_list)
142 return return_value
143 else:
144 tmp_in = numpy.zeros(input.shape, bool)
145 if return_value is None:
146 tmp_out = output
147 else:
148 tmp_out = numpy.zeros(input.shape, bool)
149 if not iterations & 1:
150 tmp_in, tmp_out = tmp_out, tmp_in
151 changed = _nd_image.binary_erosion(input, structure, mask,
152 tmp_out, border_value, origin, invert, cit, 0)
153 ii = 1
154 while (ii < iterations) or (iterations < 1) and changed:
155 tmp_in, tmp_out = tmp_out, tmp_in
156 changed = _nd_image.binary_erosion(tmp_in, structure, mask,
157 tmp_out, border_value, origin, invert, cit, 0)
158 ii += 1
159 if return_value is not None:
160 return tmp_out
161
162
163 -def binary_erosion(input, structure = None, iterations = 1, mask = None,
164 output = None, border_value = 0, origin = 0, brute_force = False):
165 """Multi-dimensional binary erosion with the given structure.
166
167 An output array can optionally be provided. The origin parameter
168 controls the placement of the filter. If no structuring element is
169 provided an element is generated with a squared connectivity equal
170 to one. The border_value parameter gives the value of the array
171 outside the border. The erosion operation is repeated iterations
172 times. If iterations is less than 1, the erosion is repeated until
173 the result does not change anymore. If a mask is given, only those
174 elements with a true value at the corresponding mask element are
175 modified at each iteration.
176 """
177 return _binary_erosion(input, structure, iterations, mask,
178 output, border_value, origin, 0, brute_force)
179
180 -def binary_dilation(input, structure = None, iterations = 1, mask = None,
181 output = None, border_value = 0, origin = 0, brute_force = False):
182 """Multi-dimensional binary dilation with the given structure.
183
184 An output array can optionally be provided. The origin parameter
185 controls the placement of the filter. If no structuring element is
186 provided an element is generated with a squared connectivity equal
187 to one. The dilation operation is repeated iterations times. If
188 iterations is less than 1, the dilation is repeated until the
189 result does not change anymore. If a mask is given, only those
190 elements with a true value at the corresponding mask element are
191 modified at each iteration.
192 """
193 input = numpy.asarray(input)
194 if structure is None:
195 structure = generate_binary_structure(input.ndim, 1)
196 origin = _ni_support._normalize_sequence(origin, input.ndim)
197 structure = numpy.asarray(structure)
198 structure = structure[tuple([slice(None, None, -1)] *
199 structure.ndim)]
200 for ii in range(len(origin)):
201 origin[ii] = -origin[ii]
202 if not structure.shape[ii] & 1:
203 origin[ii] -= 1
204 return _binary_erosion(input, structure, iterations, mask,
205 output, border_value, origin, 1, brute_force)
206
207
208 -def binary_opening(input, structure = None, iterations = 1, output = None,
209 origin = 0):
210 """Multi-dimensional binary opening with the given structure.
211
212 An output array can optionally be provided. The origin parameter
213 controls the placement of the filter. If no structuring element is
214 provided an element is generated with a squared connectivity equal
215 to one. The iterations parameter gives the number of times the
216 erosions and then the dilations are done.
217 """
218 input = numpy.asarray(input)
219 if structure is None:
220 rank = input.ndim
221 structure = generate_binary_structure(rank, 1)
222 tmp = binary_erosion(input, structure, iterations, None, None, 0,
223 origin)
224 return binary_dilation(tmp, structure, iterations, None, output, 0,
225 origin)
226
227
228 -def binary_closing(input, structure = None, iterations = 1, output = None,
229 origin = 0):
230 """Multi-dimensional binary closing with the given structure.
231
232 An output array can optionally be provided. The origin parameter
233 controls the placement of the filter. If no structuring element is
234 provided an element is generated with a squared connectivity equal
235 to one. The iterations parameter gives the number of times the
236 dilations and then the erosions are done.
237 """
238 input = numpy.asarray(input)
239 if structure is None:
240 rank = input.ndim
241 structure = generate_binary_structure(rank, 1)
242 tmp = binary_dilation(input, structure, iterations, None, None, 0,
243 origin)
244 return binary_erosion(tmp, structure, iterations, None, output, 0,
245 origin)
246
247
248 -def binary_hit_or_miss(input, structure1 = None, structure2 = None,
249 output = None, origin1 = 0, origin2 = None):
250 """Multi-dimensional binary hit-or-miss transform.
251
252 An output array can optionally be provided. The origin parameters
253 controls the placement of the structuring elements. If the first
254 structuring element is not given one is generated with a squared
255 connectivity equal to one. If the second structuring element is
256 not provided, it set equal to the inverse of the first structuring
257 element. If the origin for the second structure is equal to None
258 it is set equal to the origin of the first.
259 """
260 input = numpy.asarray(input)
261 if structure1 is None:
262 structure1 = generate_binary_structure(input.ndim, 1)
263 if structure2 is None:
264 structure2 = numpy.logical_not(structure1)
265 origin1 = _ni_support._normalize_sequence(origin1, input.ndim)
266 if origin2 is None:
267 origin2 = origin1
268 else:
269 origin2 = _ni_support._normalize_sequence(origin2, input.ndim)
270
271 tmp1 = _binary_erosion(input, structure1, 1, None, None, 0, origin1,
272 0, False)
273 inplace = isinstance(output, numpy.ndarray)
274 result = _binary_erosion(input, structure2, 1, None, output, 0,
275 origin2, 1, False)
276 if inplace:
277 numpy.logical_not(output, output)
278 numpy.logical_and(tmp1, output, output)
279 else:
280 numpy.logical_not(result, result)
281 return numpy.logical_and(tmp1, result)
282
283 -def binary_propagation(input, structure = None, mask = None,
284 output = None, border_value = 0, origin = 0):
285 """Multi-dimensional binary propagation with the given structure.
286
287 An output array can optionally be provided. The origin parameter
288 controls the placement of the filter. If no structuring element is
289 provided an element is generated with a squared connectivity equal
290 to one. If a mask is given, only those elements with a true value at
291 the corresponding mask element are.
292
293 This function is functionally equivalent to calling binary_dilation
294 with the number of iterations less then one: iterative dilation until
295 the result does not change anymore.
296 """
297 return binary_dilation(input, structure, -1, mask, output,
298 border_value, origin)
299
301 """Fill the holes in binary objects.
302
303 An output array can optionally be provided. The origin parameter
304 controls the placement of the filter. If no structuring element is
305 provided an element is generated with a squared connectivity equal
306 to one.
307 """
308 mask = numpy.logical_not(input)
309 tmp = numpy.zeros(mask.shape, bool)
310 inplace = isinstance(output, numpy.ndarray)
311 if inplace:
312 binary_dilation(tmp, structure, -1, mask, output, 1, origin)
313 numpy.logical_not(output, output)
314 else:
315 output = binary_dilation(tmp, structure, -1, mask, None, 1,
316 origin)
317 numpy.logical_not(output, output)
318 return output
319
320 -def grey_erosion(input, size = None, footprint = None, structure = None,
321 output = None, mode = "reflect", cval = 0.0, origin = 0):
322 """Calculate a grey values erosion.
323
324 Either a size or a footprint, or the structure must be provided. An
325 output array can optionally be provided. The origin parameter
326 controls the placement of the filter. The mode parameter
327 determines how the array borders are handled, where cval is the
328 value when mode is equal to 'constant'.
329 """
330 return filters._min_or_max_filter(input, size, footprint, structure,
331 output, mode, cval, origin, 1)
332
333
334 -def grey_dilation(input, size = None, footprint = None, structure = None,
335 output = None, mode = "reflect", cval = 0.0, origin = 0):
336 """Calculate a grey values dilation.
337
338 Either a size or a footprint, or the structure must be
339 provided. An output array can optionally be provided. The origin
340 parameter controls the placement of the filter. The mode parameter
341 determines how the array borders are handled, where cval is the
342 value when mode is equal to 'constant'.
343 """
344 if structure is not None:
345 structure = numpy.asarray(structure)
346 structure = structure[tuple([slice(None, None, -1)] *
347 structure.ndim)]
348 if footprint is not None:
349 footprint = numpy.asarray(footprint)
350 footprint = footprint[tuple([slice(None, None, -1)] *
351 footprint.ndim)]
352 input = numpy.asarray(input)
353 origin = _ni_support._normalize_sequence(origin, input.ndim)
354 for ii in range(len(origin)):
355 origin[ii] = -origin[ii]
356 if footprint is not None:
357 sz = footprint.shape[ii]
358 else:
359 sz = size[ii]
360 if not sz & 1:
361 origin[ii] -= 1
362 return filters._min_or_max_filter(input, size, footprint, structure,
363 output, mode, cval, origin, 0)
364
365
366 -def grey_opening(input, size = None, footprint = None, structure = None,
367 output = None, mode = "reflect", cval = 0.0, origin = 0):
368 """Multi-dimensional grey valued opening.
369
370 Either a size or a footprint, or the structure must be provided. An
371 output array can optionally be provided. The origin parameter
372 controls the placement of the filter. The mode parameter
373 determines how the array borders are handled, where cval is the
374 value when mode is equal to 'constant'.
375 """
376 tmp = grey_erosion(input, size, footprint, structure, None, mode,
377 cval, origin)
378 return grey_dilation(tmp, size, footprint, structure, output, mode,
379 cval, origin)
380
381
382 -def grey_closing(input, size = None, footprint = None, structure = None,
383 output = None, mode = "reflect", cval = 0.0, origin = 0):
384 """Multi-dimensional grey valued closing.
385
386 Either a size or a footprint, or the structure must be provided. An
387 output array can optionally be provided. The origin parameter
388 controls the placement of the filter. The mode parameter
389 determines how the array borders are handled, where cval is the
390 value when mode is equal to 'constant'.
391 """
392 tmp = grey_dilation(input, size, footprint, structure, None, mode,
393 cval, origin)
394 return grey_erosion(tmp, size, footprint, structure, output, mode,
395 cval, origin)
396
397
398 -def morphological_gradient(input, size = None, footprint = None,
399 structure = None, output = None, mode = "reflect",
400 cval = 0.0, origin = 0):
401 """Multi-dimensional morphological gradient.
402
403 Either a size or a footprint, or the structure must be provided. An
404 output array can optionally be provided. The origin parameter
405 controls the placement of the filter. The mode parameter
406 determines how the array borders are handled, where cval is the
407 value when mode is equal to 'constant'.
408 """
409 tmp = grey_dilation(input, size, footprint, structure, None, mode,
410 cval, origin)
411 if isinstance(output, numpy.ndarray):
412 grey_erosion(input, size, footprint, structure, output, mode,
413 cval, origin)
414 return numpy.subtract(tmp, output, output)
415 else:
416 return (tmp - grey_erosion(input, size, footprint, structure,
417 None, mode, cval, origin))
418
419
420 -def morphological_laplace(input, size = None, footprint = None,
421 structure = None, output = None,
422 mode = "reflect", cval = 0.0, origin = 0):
423 """Multi-dimensional morphological laplace.
424
425 Either a size or a footprint, or the structure must be provided. An
426 output array can optionally be provided. The origin parameter
427 controls the placement of the filter. The mode parameter
428 determines how the array borders are handled, where cval is the
429 value when mode is equal to 'constant'.
430 """
431 tmp1 = grey_dilation(input, size, footprint, structure, None, mode,
432 cval, origin)
433 if isinstance(output, numpy.ndarray):
434 grey_erosion(input, size, footprint, structure, output, mode,
435 cval, origin)
436 numpy.add(tmp1, output, output)
437 del tmp1
438 numpy.subtract(output, input, output)
439 return numpy.subtract(output, input, output)
440 else:
441 tmp2 = grey_erosion(input, size, footprint, structure, None, mode,
442 cval, origin)
443 numpy.add(tmp1, tmp2, tmp2)
444 del tmp1
445 numpy.subtract(tmp2, input, tmp2)
446 numpy.subtract(tmp2, input, tmp2)
447 return tmp2
448
449
450 -def white_tophat(input, size = None, footprint = None, structure = None,
451 output = None, mode = "reflect", cval = 0.0, origin = 0):
452 """Multi-dimensional white tophat filter.
453
454 Either a size or a footprint, or the structure must be provided. An
455 output array can optionally be provided. The origin parameter
456 controls the placement of the filter. The mode parameter
457 determines how the array borders are handled, where cval is the
458 value when mode is equal to 'constant'.
459 """
460 tmp = grey_erosion(input, size, footprint, structure, None, mode,
461 cval, origin)
462 if isinstance(output, numpy.ndarray):
463 grey_dilation(tmp, size, footprint, structure, output, mode, cval,
464 origin)
465 del tmp
466 return numpy.subtract(input, output, output)
467 else:
468 tmp = grey_dilation(tmp, size, footprint, structure, None, mode,
469 cval, origin)
470 return input - tmp
471
472
473 -def black_tophat(input, size = None, footprint = None,
474 structure = None, output = None, mode = "reflect",
475 cval = 0.0, origin = 0):
476 """Multi-dimensional black tophat filter.
477
478 Either a size or a footprint, or the structure must be provided. An
479 output array can optionally be provided. The origin parameter
480 controls the placement of the filter. The mode parameter
481 determines how the array borders are handled, where cval is the
482 value when mode is equal to 'constant'.
483 """
484 tmp = grey_dilation(input, size, footprint, structure, None, mode,
485 cval, origin)
486 if isinstance(output, numpy.ndarray):
487 grey_erosion(tmp, size, footprint, structure, output, mode, cval,
488 origin)
489 del tmp
490 return numpy.subtract(output, input, output)
491 else:
492 tmp = grey_erosion(tmp, size, footprint, structure, None, mode,
493 cval, origin)
494 return tmp - input
495
496
603
702
703
779