1
# Copyright (C) 2003-2005 Peter J. Verveer
3
# Redistribution and use in source and binary forms, with or without
4
# modification, are permitted provided that the following conditions
7
# 1. Redistributions of source code must retain the above copyright
8
# notice, this list of conditions and the following disclaimer.
10
# 2. Redistributions in binary form must reproduce the above
11
# copyright notice, this list of conditions and the following
12
# disclaimer in the documentation and/or other materials provided
13
# with the distribution.
15
# 3. The name of the author may not be used to endorse or promote
16
# products derived from this software without specific prior
19
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
def _center_is_true(structure, origin):
39
structure = numpy.array(structure)
40
coor = tuple([oo + ss // 2 for ss, oo in zip(structure.shape,
42
return bool(structure[coor])
44
def iterate_structure(structure, iterations, origin = None):
45
"""Iterate a structure by dilating it with itself.
47
If origin is None, only the iterated structure is returned. If
48
not, a tuple of the iterated structure and the modified origin is
51
structure = numpy.asarray(structure)
53
return structure.copy()
55
shape = [ii + ni * (ii - 1) for ii in structure.shape]
56
pos = [ni * (structure.shape[ii] / 2) for ii in range(len(shape))]
57
slc = [slice(pos[ii], pos[ii] + structure.shape[ii], None)
58
for ii in range(len(shape))]
59
out = numpy.zeros(shape, bool)
60
out[slc] = structure != 0
61
out = binary_dilation(out, structure, iterations = ni)
65
origin = _ni_support._normalize_sequence(origin, structure.ndim)
66
origin = [iterations * o for o in origin]
69
def generate_binary_structure(rank, connectivity):
70
"""Generate a binary structure for binary morphological operations.
72
The inputs are the rank of the array to which the structure will
73
be applied and the square of the connectivity of the structure.
79
return numpy.array(0, dtype = bool)
81
return numpy.array(1, dtype = bool)
82
output = numpy.zeros([3] * rank, bool)
83
output = numpy.fabs(numpy.indices([3] * rank) - 1)
84
output = numpy.add.reduce(output, 0)
85
return numpy.asarray(output <= connectivity, dtype = bool)
88
def _binary_erosion(input, structure, iterations, mask, output,
89
border_value, origin, invert, brute_force):
90
input = numpy.asarray(input)
91
if numpy.iscomplexobj(input):
92
raise TypeError, 'Complex type not supported'
94
structure = generate_binary_structure(input.ndim, 1)
96
structure = numpy.asarray(structure)
97
structure = structure.astype(bool)
98
if structure.ndim != input.ndim:
99
raise RuntimeError, 'structure rank must equal input rank'
100
if not structure.flags.contiguous:
101
structure = structure.copy()
102
if numpy.product(structure.shape,axis=0) < 1:
103
raise RuntimeError, 'structure must not be empty'
105
mask = numpy.asarray(mask)
106
if mask.shape != input.shape:
107
raise RuntimeError, 'mask and input must have equal sizes'
108
origin = _ni_support._normalize_sequence(origin, input.ndim)
109
cit = _center_is_true(structure, origin)
110
if isinstance(output, numpy.ndarray):
111
if numpy.iscomplexobj(output):
112
raise TypeError, 'Complex output type not supported'
115
output, return_value = _ni_support._get_output(output, input)
119
_nd_image.binary_erosion(input, structure, mask, output,
120
border_value, origin, invert, cit, 0)
122
elif cit and not brute_force:
123
changed, coordinate_list = _nd_image.binary_erosion(input,
124
structure, mask, output, border_value, origin, invert, cit, 1)
125
structure = structure[tuple([slice(None, None, -1)] *
127
for ii in range(len(origin)):
128
origin[ii] = -origin[ii]
129
if not structure.shape[ii] & 1:
132
msk = numpy.asarray(mask)
133
msk = mask.astype(numpy.int8)
137
if not structure.flags.contiguous:
138
structure = structure.copy()
139
_nd_image.binary_erosion2(output, structure, mask, iterations - 1,
140
origin, invert, coordinate_list)
143
tmp_in = numpy.zeros(input.shape, bool)
144
if return_value == None:
147
tmp_out = numpy.zeros(input.shape, bool)
148
if not iterations & 1:
149
tmp_in, tmp_out = tmp_out, tmp_in
150
changed = _nd_image.binary_erosion(input, structure, mask,
151
tmp_out, border_value, origin, invert, cit, 0)
153
while (ii < iterations) or (iterations < 1) and changed:
154
tmp_in, tmp_out = tmp_out, tmp_in
155
changed = _nd_image.binary_erosion(tmp_in, structure, mask,
156
tmp_out, border_value, origin, invert, cit, 0)
158
if return_value != None:
162
def binary_erosion(input, structure = None, iterations = 1, mask = None,
163
output = None, border_value = 0, origin = 0, brute_force = False):
164
"""Multi-dimensional binary erosion with the given structure.
166
An output array can optionally be provided. The origin parameter
167
controls the placement of the filter. If no structuring element is
168
provided an element is generated with a squared connectivity equal
169
to one. The border_value parameter gives the value of the array
170
outside the border. The erosion operation is repeated iterations
171
times. If iterations is less than 1, the erosion is repeated until
172
the result does not change anymore. If a mask is given, only those
173
elements with a true value at the corresponding mask element are
174
modified at each iteration.
176
return _binary_erosion(input, structure, iterations, mask,
177
output, border_value, origin, 0, brute_force)
179
def binary_dilation(input, structure = None, iterations = 1, mask = None,
180
output = None, border_value = 0, origin = 0, brute_force = False):
181
"""Multi-dimensional binary dilation with the given structure.
183
An output array can optionally be provided. The origin parameter
184
controls the placement of the filter. If no structuring element is
185
provided an element is generated with a squared connectivity equal
186
to one. The dilation operation is repeated iterations times. If
187
iterations is less than 1, the dilation is repeated until the
188
result does not change anymore. If a mask is given, only those
189
elements with a true value at the corresponding mask element are
190
modified at each iteration.
192
input = numpy.asarray(input)
193
if structure == None:
194
structure = generate_binary_structure(input.ndim, 1)
195
origin = _ni_support._normalize_sequence(origin, input.ndim)
196
structure = numpy.asarray(structure)
197
structure = structure[tuple([slice(None, None, -1)] *
199
for ii in range(len(origin)):
200
origin[ii] = -origin[ii]
201
if not structure.shape[ii] & 1:
203
return _binary_erosion(input, structure, iterations, mask,
204
output, border_value, origin, 1, brute_force)
207
def binary_opening(input, structure = None, iterations = 1, output = None,
209
"""Multi-dimensional binary opening with the given structure.
211
An output array can optionally be provided. The origin parameter
212
controls the placement of the filter. If no structuring element is
213
provided an element is generated with a squared connectivity equal
214
to one. The iterations parameter gives the number of times the
215
erosions and then the dilations are done.
217
input = numpy.asarray(input)
218
if structure is None:
220
structure = generate_binary_structure(rank, 1)
221
tmp = binary_erosion(input, structure, iterations, None, None, 0,
223
return binary_dilation(tmp, structure, iterations, None, output, 0,
227
def binary_closing(input, structure = None, iterations = 1, output = None,
229
"""Multi-dimensional binary closing with the given structure.
231
An output array can optionally be provided. The origin parameter
232
controls the placement of the filter. If no structuring element is
233
provided an element is generated with a squared connectivity equal
234
to one. The iterations parameter gives the number of times the
235
dilations and then the erosions are done.
237
input = numpy.asarray(input)
238
if structure is None:
240
structure = generate_binary_structure(rank, 1)
241
tmp = binary_dilation(input, structure, iterations, None, None, 0,
243
return binary_erosion(tmp, structure, iterations, None, output, 0,
247
def binary_hit_or_miss(input, structure1 = None, structure2 = None,
248
output = None, origin1 = 0, origin2 = None):
249
"""Multi-dimensional binary hit-or-miss transform.
251
An output array can optionally be provided. The origin parameters
252
controls the placement of the structuring elements. If the first
253
structuring element is not given one is generated with a squared
254
connectivity equal to one. If the second structuring element is
255
not provided, it set equal to the inverse of the first structuring
256
element. If the origin for the second structure is equal to None
257
it is set equal to the origin of the first.
259
input = numpy.asarray(input)
260
if structure1 is None:
261
structure1 = generate_binary_structure(input.ndim, 1)
262
if structure2 is None:
263
structure2 = numpy.logical_not(structure1)
264
origin1 = _ni_support._normalize_sequence(origin1, input.ndim)
268
origin2 = _ni_support._normalize_sequence(origin2, input.ndim)
270
tmp1 = _binary_erosion(input, structure1, 1, None, None, 0, origin1,
272
inplace = isinstance(output, numpy.ndarray)
273
result = _binary_erosion(input, structure2, 1, None, output, 0,
276
numpy.logical_not(output, output)
277
numpy.logical_and(tmp1, output, output)
279
numpy.logical_not(result, result)
280
return numpy.logical_and(tmp1, result)
282
def binary_propagation(input, structure = None, mask = None,
283
output = None, border_value = 0, origin = 0):
284
"""Multi-dimensional binary propagation with the given structure.
286
An output array can optionally be provided. The origin parameter
287
controls the placement of the filter. If no structuring element is
288
provided an element is generated with a squared connectivity equal
289
to one. If a mask is given, only those elements with a true value at
290
the corresponding mask element are.
292
This function is functionally equivalent to calling binary_dilation
293
with the number of iterations less then one: iterative dilation until
294
the result does not change anymore.
296
return binary_dilation(input, structure, -1, mask, output,
297
border_value, origin)
299
def binary_fill_holes(input, structure = None, output = None, origin = 0):
300
"""Fill the holes in binary objects.
302
An output array can optionally be provided. The origin parameter
303
controls the placement of the filter. If no structuring element is
304
provided an element is generated with a squared connectivity equal
307
mask = numpy.logical_not(input)
308
tmp = numpy.zeros(mask.shape, bool)
309
inplace = isinstance(output, numpy.ndarray)
311
binary_dilation(tmp, structure, -1, mask, output, 1, origin)
312
numpy.logical_not(output, output)
314
output = binary_dilation(tmp, structure, -1, mask, None, 1,
316
numpy.logical_not(output, output)
319
def grey_erosion(input, size = None, footprint = None, structure = None,
320
output = None, mode = "reflect", cval = 0.0, origin = 0):
321
"""Calculate a grey values erosion.
323
Either a size or a footprint, or the structure must be provided. An
324
output array can optionally be provided. The origin parameter
325
controls the placement of the filter. The mode parameter
326
determines how the array borders are handled, where cval is the
327
value when mode is equal to 'constant'.
329
return filters._min_or_max_filter(input, size, footprint, structure,
330
output, mode, cval, origin, 1)
333
def grey_dilation(input, size = None, footprint = None, structure = None,
334
output = None, mode = "reflect", cval = 0.0, origin = 0):
335
"""Calculate a grey values dilation.
337
Either a size or a footprint, or the structure must be
338
provided. An output array can optionally be provided. The origin
339
parameter controls the placement of the filter. The mode parameter
340
determines how the array borders are handled, where cval is the
341
value when mode is equal to 'constant'.
343
if structure is not None:
344
structure = numpy.asarray(structure)
345
structure = structure[tuple([slice(None, None, -1)] *
347
if footprint is not None:
348
footprint = numpy.asarray(footprint)
349
footprint = footprint[tuple([slice(None, None, -1)] *
351
input = numpy.asarray(input)
352
origin = _ni_support._normalize_sequence(origin, input.ndim)
353
for ii in range(len(origin)):
354
origin[ii] = -origin[ii]
355
if footprint is not None:
356
sz = footprint.shape[ii]
361
return filters._min_or_max_filter(input, size, footprint, structure,
362
output, mode, cval, origin, 0)
365
def grey_opening(input, size = None, footprint = None, structure = None,
366
output = None, mode = "reflect", cval = 0.0, origin = 0):
367
"""Multi-dimensional grey valued opening.
369
Either a size or a footprint, or the structure must be provided. An
370
output array can optionally be provided. The origin parameter
371
controls the placement of the filter. The mode parameter
372
determines how the array borders are handled, where cval is the
373
value when mode is equal to 'constant'.
375
tmp = grey_erosion(input, size, footprint, structure, None, mode,
377
return grey_dilation(tmp, size, footprint, structure, output, mode,
381
def grey_closing(input, size = None, footprint = None, structure = None,
382
output = None, mode = "reflect", cval = 0.0, origin = 0):
383
"""Multi-dimensional grey valued closing.
385
Either a size or a footprint, or the structure must be provided. An
386
output array can optionally be provided. The origin parameter
387
controls the placement of the filter. The mode parameter
388
determines how the array borders are handled, where cval is the
389
value when mode is equal to 'constant'.
391
tmp = grey_dilation(input, size, footprint, structure, None, mode,
393
return grey_erosion(tmp, size, footprint, structure, output, mode,
397
def morphological_gradient(input, size = None, footprint = None,
398
structure = None, output = None, mode = "reflect",
399
cval = 0.0, origin = 0):
400
"""Multi-dimensional morphological gradient.
402
Either a size or a footprint, or the structure must be provided. An
403
output array can optionally be provided. The origin parameter
404
controls the placement of the filter. The mode parameter
405
determines how the array borders are handled, where cval is the
406
value when mode is equal to 'constant'.
408
tmp = grey_dilation(input, size, footprint, structure, None, mode,
410
if isinstance(output, numpy.ndarray):
411
grey_erosion(input, size, footprint, structure, output, mode,
413
return numpy.subtract(tmp, output, output)
415
return (tmp - grey_erosion(input, size, footprint, structure,
416
None, mode, cval, origin))
419
def morphological_laplace(input, size = None, footprint = None,
420
structure = None, output = None,
421
mode = "reflect", cval = 0.0, origin = 0):
422
"""Multi-dimensional morphological laplace.
424
Either a size or a footprint, or the structure must be provided. An
425
output array can optionally be provided. The origin parameter
426
controls the placement of the filter. The mode parameter
427
determines how the array borders are handled, where cval is the
428
value when mode is equal to 'constant'.
430
tmp1 = grey_dilation(input, size, footprint, structure, None, mode,
432
if isinstance(output, numpy.ndarray):
433
grey_erosion(input, size, footprint, structure, output, mode,
435
numpy.add(tmp1, output, output)
437
numpy.subtract(output, input, output)
438
return numpy.subtract(output, input, output)
440
tmp2 = grey_erosion(input, size, footprint, structure, None, mode,
442
numpy.add(tmp1, tmp2, tmp2)
444
numpy.subtract(tmp2, input, tmp2)
445
numpy.subtract(tmp2, input, tmp2)
449
def white_tophat(input, size = None, footprint = None, structure = None,
450
output = None, mode = "reflect", cval = 0.0, origin = 0):
451
"""Multi-dimensional white tophat filter.
453
Either a size or a footprint, or the structure must be provided. An
454
output array can optionally be provided. The origin parameter
455
controls the placement of the filter. The mode parameter
456
determines how the array borders are handled, where cval is the
457
value when mode is equal to 'constant'.
459
tmp = grey_erosion(input, size, footprint, structure, None, mode,
461
if isinstance(output, numpy.ndarray):
462
grey_dilation(tmp, size, footprint, structure, output, mode, cval,
465
return numpy.subtract(input, output, output)
467
tmp = grey_dilation(tmp, size, footprint, structure, None, mode,
472
def black_tophat(input, size = None, footprint = None,
473
structure = None, output = None, mode = "reflect",
474
cval = 0.0, origin = 0):
475
"""Multi-dimensional black tophat filter.
477
Either a size or a footprint, or the structure must be provided. An
478
output array can optionally be provided. The origin parameter
479
controls the placement of the filter. The mode parameter
480
determines how the array borders are handled, where cval is the
481
value when mode is equal to 'constant'.
483
tmp = grey_dilation(input, size, footprint, structure, None, mode,
485
if isinstance(output, numpy.ndarray):
486
grey_erosion(tmp, size, footprint, structure, output, mode, cval,
489
return numpy.subtract(output, input, output)
491
tmp = grey_erosion(tmp, size, footprint, structure, None, mode,
496
def distance_transform_bf(input, metric = "euclidean", sampling = None,
497
return_distances = True, return_indices = False,
498
distances = None, indices = None):
499
"""Distance transform function by a brute force algorithm.
501
This function calculates the distance transform of the input, by
502
replacing each background element (zero values), with its
503
shortest distance to the foreground (any element non-zero). Three
504
types of distance metric are supported: 'euclidean', 'city_block'
507
In addition to the distance transform, the feature transform can
508
be calculated. In this case the index of the closest background
509
element is returned along the first axis of the result.
511
The return_distances, and return_indices flags can be used to
512
indicate if the distance transform, the feature transform, or both
515
Optionally the sampling along each axis can be given by the
516
sampling parameter which should be a sequence of length equal to
517
the input rank, or a single number in which the sampling is assumed
518
to be equal along all axes. This parameter is only used in the
519
case of the euclidean distance transform.
521
This function employs a slow brute force algorithm, see also the
522
function distance_transform_cdt for more efficient city_block and
523
chessboard algorithms.
525
the distances and indices arguments can be used to give optional
526
output arrays that must be of the correct size and type (float64
529
if (not return_distances) and (not return_indices):
530
msg = 'at least one of distances/indices must be specified'
531
raise RuntimeError, msg
532
tmp1 = numpy.asarray(input) != 0
533
struct = generate_binary_structure(tmp1.ndim, tmp1.ndim)
534
tmp2 = binary_dilation(tmp1, struct)
535
tmp2 = numpy.logical_xor(tmp1, tmp2)
536
tmp1 = tmp1.astype(numpy.int8) - tmp2.astype(numpy.int8)
538
metric = metric.lower()
539
if metric == 'euclidean':
541
elif metric == 'cityblock':
543
elif metric == 'chessboard':
546
raise RuntimeError, 'distance metric not supported'
548
sampling = _ni_support._normalize_sequence(sampling, tmp1.ndim)
549
sampling = numpy.asarray(sampling, dtype = numpy.float64)
550
if not sampling.flags.contiguous:
551
sampling = sampling.copy()
553
ft = numpy.zeros(tmp1.shape, dtype = numpy.int32)
557
if distances == None:
559
dt = numpy.zeros(tmp1.shape, dtype = numpy.float64)
561
dt = numpy.zeros(tmp1.shape, dtype = numpy.uint32)
563
if distances.shape != tmp1.shape:
564
raise RuntimeError, 'distances array has wrong shape'
566
if distances.dtype.type != numpy.float64:
567
raise RuntimeError, 'distances array must be float64'
569
if distances.dtype.type != numpy.uint32:
570
raise RuntimeError, 'distances array must be uint32'
574
_nd_image.distance_transform_bf(tmp1, metric, sampling, dt, ft)
576
if isinstance(indices, numpy.ndarray):
577
if indices.dtype.type != numpy.int32:
578
raise RuntimeError, 'indices must of int32 type'
579
if indices.shape != (tmp1.ndim,) + tmp1.shape:
580
raise RuntimeError, 'indices has wrong shape'
583
tmp2 = numpy.indices(tmp1.shape, dtype = numpy.int32)
585
for ii in range(tmp2.shape[0]):
586
rtmp = numpy.ravel(tmp2[ii, ...])[ft]
587
rtmp.shape = tmp1.shape
590
# construct and return the result
592
if return_distances and not isinstance(distances, numpy.ndarray):
594
if return_indices and not isinstance(indices, numpy.ndarray):
598
elif len(result) == 1:
603
def distance_transform_cdt(input, structure = 'chessboard',
604
return_distances = True, return_indices = False,
605
distances = None, indices = None):
606
"""Distance transform for chamfer type of transforms.
608
The structure determines the type of chamfering that is done. If
609
the structure is equal to 'cityblock' a structure is generated
610
using generate_binary_structure with a squared distance equal to
611
1. If the structure is equal to 'chessboard', a structure is
612
generated using generate_binary_structure with a squared distance
613
equal to the rank of the array. These choices correspond to the
614
common interpretations of the cityblock and the chessboard
615
distance metrics in two dimensions.
617
In addition to the distance transform, the feature transform can
618
be calculated. In this case the index of the closest background
619
element is returned along the first axis of the result.
621
The return_distances, and return_indices flags can be used to
622
indicate if the distance transform, the feature transform, or both
625
The distances and indices arguments can be used to give optional
626
output arrays that must be of the correct size and type (both int32).
628
if (not return_distances) and (not return_indices):
629
msg = 'at least one of distances/indices must be specified'
630
raise RuntimeError, msg
631
ft_inplace = isinstance(indices, numpy.ndarray)
632
dt_inplace = isinstance(distances, numpy.ndarray)
633
input = numpy.asarray(input)
634
if structure == 'cityblock':
636
structure = generate_binary_structure(rank, 1)
637
elif structure == 'chessboard':
639
structure = generate_binary_structure(rank, rank)
642
structure = numpy.asarray(structure)
644
raise RuntimeError, 'invalid structure provided'
645
for s in structure.shape:
647
raise RuntimeError, 'structure sizes must be equal to 3'
648
if not structure.flags.contiguous:
649
structure = structure.copy()
651
if distances.dtype.type != numpy.int32:
652
raise RuntimeError, 'distances must be of int32 type'
653
if distances.shape != input.shape:
654
raise RuntimeError, 'distances has wrong shape'
656
dt[...] = numpy.where(input, -1, 0).astype(numpy.int32)
658
dt = numpy.where(input, -1, 0).astype(numpy.int32)
661
sz = numpy.product(dt.shape,axis=0)
662
ft = numpy.arange(sz, dtype = numpy.int32)
666
_nd_image.distance_transform_op(structure, dt, ft)
667
dt = dt[tuple([slice(None, None, -1)] * rank)]
669
ft = ft[tuple([slice(None, None, -1)] * rank)]
670
_nd_image.distance_transform_op(structure, dt, ft)
671
dt = dt[tuple([slice(None, None, -1)] * rank)]
673
ft = ft[tuple([slice(None, None, -1)] * rank)]
676
if indices.dtype.type != numpy.int32:
677
raise RuntimeError, 'indices must of int32 type'
678
if indices.shape != (dt.ndim,) + dt.shape:
679
raise RuntimeError, 'indices has wrong shape'
682
tmp = numpy.indices(dt.shape, dtype = numpy.int32)
683
for ii in range(tmp.shape[0]):
684
rtmp = numpy.ravel(tmp[ii, ...])[ft]
685
rtmp.shape = dt.shape
689
# construct and return the result
691
if return_distances and not dt_inplace:
693
if return_indices and not ft_inplace:
697
elif len(result) == 1:
703
def distance_transform_edt(input, sampling = None,
704
return_distances = True, return_indices = False,
705
distances = None, indices = None):
706
"""Exact euclidean distance transform.
708
In addition to the distance transform, the feature transform can
709
be calculated. In this case the index of the closest background
710
element is returned along the first axis of the result.
712
The return_distances, and return_indices flags can be used to
713
indicate if the distance transform, the feature transform, or both
716
Optionally the sampling along each axis can be given by the
717
sampling parameter which should be a sequence of length equal to
718
the input rank, or a single number in which the sampling is assumed
719
to be equal along all axes.
721
the distances and indices arguments can be used to give optional
722
output arrays that must be of the correct size and type (float64
725
if (not return_distances) and (not return_indices):
726
msg = 'at least one of distances/indices must be specified'
727
raise RuntimeError, msg
728
ft_inplace = isinstance(indices, numpy.ndarray)
729
dt_inplace = isinstance(distances, numpy.ndarray)
730
# calculate the feature transform
731
input = numpy.where(input, 1, 0).astype(numpy.int8)
732
if sampling is not None:
733
sampling = _ni_support._normalize_sequence(sampling, input.ndim)
734
sampling = numpy.asarray(sampling, dtype = numpy.float64)
735
if not sampling.flags.contiguous:
736
sampling = sampling.copy()
739
if ft.shape != (input.ndim,) + input.shape:
740
raise RuntimeError, 'indices has wrong shape'
741
if ft.dtype.type != numpy.int32:
742
raise RuntimeError, 'indices must be of int32 type'
744
ft = numpy.zeros((input.ndim,) + input.shape,
746
_nd_image.euclidean_feature_transform(input, sampling, ft)
747
# if requested, calculate the distance transform
749
dt = ft - numpy.indices(input.shape, dtype = ft.dtype)
750
dt = dt.astype(numpy.float64)
751
if sampling is not None:
752
for ii in range(len(sampling)):
753
dt[ii, ...] *= sampling[ii]
754
numpy.multiply(dt, dt, dt)
756
dt = numpy.add.reduce(dt, axis = 0)
757
if distances.shape != dt.shape:
758
raise RuntimeError, 'indices has wrong shape'
759
if distances.dtype.type != numpy.float64:
760
raise RuntimeError, 'indices must be of float64 type'
761
numpy.sqrt(dt, distances)
764
dt = numpy.add.reduce(dt, axis = 0)
766
# construct and return the result
768
if return_distances and not dt_inplace:
770
if return_indices and not ft_inplace:
774
elif len(result) == 1: