scikit-image
122 строки · 4.0 Кб
1#cython: cdivision=True
2#cython: boundscheck=False
3#cython: nonecheck=False
4#cython: wraparound=False
5
6"""Cython code used in `remove_objects_by_distance` function."""
7
8cimport numpy as cnp
9
10from .._shared.fused_numerics cimport np_anyint
11
12
13def _remove_objects_by_distance(
14np_anyint[::1] out not None,
15Py_ssize_t[::1] border_indices not None,
16Py_ssize_t[::1] inner_indices not None,
17kdtree,
18cnp.float64_t p_norm,
19cnp.float64_t min_distance,
20tuple shape,
21):
22"""Remove objects, in specified order, until remaining are a minimum distance apart.
23
24Remove labeled objects from an image until the remaining ones are spaced
25more than a given distance from one another. By default, smaller objects
26are removed first.
27
28Parameters
29----------
30out :
31An array with labels for each object in `image` matching it in shape.
32border_indices, inner_indices :
33Indices into `out` for the border of objects (`border_indices`) and
34the inner part of objects (`inner_indices`). `border_indices`
35determines the iteration order; objects that are indexed first are
36preserved. Indices must be sorted such, that indices pointing to the
37same object are next to each other.
38kdtree : scipy.spatial.cKDTree
39A KDTree containing the coordinates of all objects in `image`.
40min_distance :
41The minimal allowed distance between objects.
42p_norm :
43The Minkowski p-norm used to calculate the distance between objects.
44Defaults to 2 which corresponds to the Euclidean distance.
45shape :
46The shape of the unraveled `image`.
47"""
48cdef:
49Py_ssize_t i_indices, j_indices # Loop variables to index `indices`
50Py_ssize_t i_out # Loop variable to index `out`
51np_anyint object_id, other_id
52list neighborhood
53set remembered_ids
54
55remembered_ids = set()
56for i_indices in range(border_indices.shape[0]):
57i_out = border_indices[i_indices]
58object_id = out[i_out]
59# Skip if sample is part of a removed object
60if object_id == 0:
61continue
62
63neighborhood = kdtree.query_ball_point(
64kdtree.data[i_indices, ...],
65r=min_distance,
66p=p_norm,
67)
68for j_indices in neighborhood:
69# Check object IDs in neighborhood
70other_id = out[border_indices[j_indices]]
71if other_id != 0 and other_id != object_id:
72# If neighbor ID wasn't already removed or is the current one
73# remove the boundary and remember the ID
74_remove_object(out, border_indices, j_indices)
75remembered_ids.add(other_id)
76
77# Delete inner parts of remembered objects
78for j_indices in range(inner_indices.shape[0]):
79object_id = out[inner_indices[j_indices]]
80if object_id != 0 and object_id in remembered_ids:
81_remove_object(out, inner_indices, j_indices)
82
83
84cdef inline _remove_object(
85np_anyint[::1] out,
86Py_ssize_t[::1] indices,
87Py_ssize_t start
88):
89"""Delete an object.
90
91Starting from `start`, iterate the `indices` in both directions and assign
920 until the object ID changes
93
94Parameters
95----------
96out :
97An array with labels for each object in `image` matching it in shape.
98indices :
99Indices into `out` for objects. Indices must be sorted such, that
100indices pointing to the same object are next to each other.
101start :
102Index into `indices` for the object.
103"""
104cdef:
105Py_ssize_t k_indices, k_labels
106np_anyint remove_id
107
108with nogil:
109remove_id = out[indices[start]]
110
111for k_indices in range(start, -1, -1):
112k_labels = indices[k_indices]
113if remove_id == out[k_labels]:
114out[k_labels] = 0
115else:
116break
117for k_indices in range(start + 1, indices.shape[0]):
118k_labels = indices[k_indices]
119if remove_id == out[k_labels]:
120out[k_labels] = 0
121else:
122break
123