Developer documentation
Version 3.0.3-105-gd3941f44
connected_components.h
Go to the documentation of this file.
1/* Copyright (c) 2008-2022 the MRtrix3 contributors.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 *
7 * Covered Software is provided under this License on an "as is"
8 * basis, without warranty of any kind, either expressed, implied, or
9 * statutory, including, without limitation, warranties that the
10 * Covered Software is free of defects, merchantable, fit for a
11 * particular purpose or non-infringing.
12 * See the Mozilla Public License v. 2.0 for more details.
13 *
14 * For more details, see http://www.mrtrix.org/.
15 */
16
17#ifndef __filter_connected_h__
18#define __filter_connected_h__
19
20
21#include "image.h"
22#include "memory.h"
23#include "types.h"
24
25#include "filter/base.h"
26#include "misc/voxel2vector.h"
27
28#include <stack>
29#include <iostream>
30
31
32namespace MR
33{
34 namespace Filter
35 {
36
37
38
41
42 public:
43
44 // A class that pre-computes and stores, for each voxel, a
45 // list of voxels (represented as indices) that are adjacent
46 //
47 // If we were to re-implement dixel-wise connectivity, it would
48 // be done using an alternative initialise() function for this
49 // class, to define the volumes on the fourth axis that
50 // correspond to neighbouring directions using a Directions::Set.
53 public:
54 typedef Voxel2Vector::index_t index_t;
55
57 use_26_neighbours (false),
58 enabled_axes (3, true) { }
59
60 void toggle_axis (const size_t axis, const bool value) {
61 if (axis > enabled_axes.size())
62 enabled_axes.resize (axis+1, false);
63 enabled_axes[axis] = value;
64 data.clear();
65 }
66
67 void set_axes (const vector<bool>& i) {
68 enabled_axes = i;
69 data.clear();
70 }
71
72 void initialise (const Header&, const Voxel2Vector&);
73
74 const vector<index_t>& operator[] (const size_t index) const {
75 assert (size());
76 assert (index < size());
77 return data[index];
78 }
79
80 void set_26_adjacency (const bool i) {
81 use_26_neighbours = i;
82 data.clear();
83 }
84
85 size_t size() const { return data.size(); }
86
87 private:
88 bool use_26_neighbours;
89 vector<bool> enabled_axes;
92
93
94
95 class Cluster
97 public:
98 Cluster (const uint32_t l) :
99 label (l),
100 size (0) { }
101 uint32_t label;
102 uint32_t size;
103 bool operator< (const Cluster& j) const {
104 return size < j.size;
105 }
106 };
107 // Used for sorting clusters in order of size
108 static bool largest (const Cluster& i, const Cluster& j) {
109 return (i.size > j.size);
110 }
111
112
113
114
116
117 // Perform connected components on vectorized binary data
119 template <class VectorType>
121 const VectorType&, const float) const;
122
123
124 private:
125
126 // Utility functions that perform the actual connected
127 // components functionality
128 bool next_neighbour (uint32_t&, vector<uint32_t>&) const;
129 template <class VectorType>
130 bool next_neighbour (uint32_t&, vector<uint32_t>&,
131 const VectorType&, const float) const;
132
133 void depth_first_search (const uint32_t, Cluster&, vector<uint32_t>&) const;
134 template <class VectorType>
135 void depth_first_search (const uint32_t, Cluster&, vector<uint32_t>&,
136 const VectorType&, const float) const;
137
138
139 };
140
141
142
143 template <class VectorType>
145 vector<uint32_t>& labels,
146 const VectorType& data,
147 const float threshold) const
148 {
149 assert (adjacency.size());
150 labels.resize (adjacency.size(), 0);
151 uint32_t current_label = 0;
152 for (uint32_t i = 0; i < labels.size(); i++) {
153 // This node has not been already clustered and is above threshold
154 if (!labels[i] && data[i] > threshold) {
155 Cluster cluster (++current_label);
156 depth_first_search (i, cluster, labels, data, threshold);
157 clusters.push_back (cluster);
158 }
159 }
160 if (clusters.size() > std::numeric_limits<uint32_t>::max())
161 throw Exception ("The number of clusters is larger than can be labelled with an unsigned 32bit integer.");
162 }
163
164
165
166 template <class VectorType>
167 bool Connector::next_neighbour (uint32_t& node,
168 vector<uint32_t>& labels,
169 const VectorType& data,
170 const float threshold) const
171 {
172 for (auto n : adjacency[node]) {
173 if (!labels[n] && data[n] > threshold) {
174 node = n;
175 return true;
176 }
177 }
178 return false;
179 }
180
181
182
183 template <class VectorType>
184 void Connector::depth_first_search (const uint32_t root,
185 Cluster& cluster,
186 vector<uint32_t>& labels,
187 const VectorType& data,
188 const float threshold) const
189 {
190 uint32_t node = root;
191 std::stack<uint32_t> stack;
192 while (true) {
193 labels[node] = cluster.label;
194 stack.push (node);
195 cluster.size++;
196 if (next_neighbour (node, labels, data, threshold)) {
197 continue;
198 } else {
199 do {
200 if (stack.top() == root)
201 return;
202 stack.pop();
203 node = stack.top();
204 } while (!next_neighbour (node, labels, data, threshold));
205 }
206 }
207 }
208
209
210
211
212
229 public:
230
231 template <class HeaderType>
232 ConnectedComponents (const HeaderType& in) :
233 Base (in),
234 enabled_axes (ndim(), true),
235 largest_only (false),
236 do_26_connectivity (false)
237 {
238 if (this->ndim() > 4)
239 throw Exception ("Cannot run connected components analysis with more than 4 dimensions");
241 // By default, ignore all axes above the three spatial dimensions
242 for (size_t axis = 3; axis < ndim(); ++axis)
243 enabled_axes[axis] = false;
244 }
245
246 template <class HeaderType>
247 ConnectedComponents (const HeaderType& in, const std::string& message) :
249 {
250 set_message (message);
251 }
252
253
254 template <class InputVoxelType, class OutputVoxelType>
255 void operator() (InputVoxelType& in, OutputVoxelType& out)
256 {
257 Voxel2Vector v2v (in, *this);
258
259 Connector connector;
260 connector.adjacency.set_axes (enabled_axes);
262 connector.adjacency.initialise (in, v2v);
263
264 std::unique_ptr<ProgressBar> progress;
265 if (message.size()) {
266 progress.reset (new ProgressBar (message));
267 ++(*progress);
268 }
269
271 vector<uint32_t> labels;
272 connector.run (clusters, labels);
273 if (progress) ++(*progress);
274
275 // Sort clusters in order from largest to smallest
276 std::sort (clusters.begin(), clusters.end(), Connector::largest);
277 if (progress) ++(*progress);
278
279 // Generate a lookup table to map input cluster index to
280 // output cluster index following cluster-size sorting
281 vector<uint32_t> index_lookup (clusters.size() + 1, 0);
282 for (uint32_t c = 0; c < clusters.size(); c++)
283 index_lookup[clusters[c].label] = c + 1;
284
285 for (auto l = Loop (out) (out); l; ++l)
286 out.value() = 0;
287
288 for (uint32_t i = 0; i < v2v.size(); i++) {
289 assign_pos_of (v2v[i]).to (out);
290 if (largest_only) {
291 if (index_lookup[labels[i]] == 1)
292 out.value() = 1;
293 } else {
294 out.value() = index_lookup[labels[i]];
295 }
296 }
297 }
298
299
300
301 void set_axes (const vector<int>& i)
302 {
303 const size_t max_axis = *std::max_element (i.begin(), i.end());
304 if (max_axis >= ndim())
305 throw Exception ("Requested axis for connected component filter (" + str(max_axis) + " is beyond the dimensionality of the image (" + str(ndim()) + "D)");
306 enabled_axes.assign (std::max (max_axis+1, size_t(ndim())), false);
307 for (const auto& axis : i) {
308 if (axis < 0)
309 throw Exception ("Cannot specify negative axis index for connected-component filter");
310 enabled_axes[axis] = true;
311 }
312 }
313
314
315 void set_largest_only (bool value)
316 {
318 }
319
320
321 void set_26_connectivity (bool value)
322 {
324 }
325
326
327 protected:
331 };
333 }
334}
335
336
337#endif
static constexpr uint8_t UInt32
Definition: datatype.h:145
std::string message
Definition: base.h:66
const vector< index_t > & operator[](const size_t index) const
void toggle_axis(const size_t axis, const bool value)
void set_axes(const vector< bool > &i)
void initialise(const Header &, const Voxel2Vector &)
bool operator<(const Cluster &j) const
class MR::Filter::Connector::Adjacency adjacency
void run(vector< Cluster > &, vector< uint32_t > &) const
static bool largest(const Cluster &i, const Cluster &j)
DataType datatype_
the type of the data as stored on file
Definition: header.h:370
implements a progress meter to provide feedback to the user
Definition: progressbar.h:58
FORCE_INLINE LoopAlongAxes Loop()
Definition: loop.h:419
VectorType::Scalar value(const VectorType &coefs, typename VectorType::Scalar cos_elevation, typename VectorType::Scalar cos_azimuth, typename VectorType::Scalar sin_azimuth, int lmax)
Definition: SH.h:233
#define NOMEMALIGN
Definition: memory.h:22
Definition: base.h:24
std::string str(const T &value, int precision=0)
Definition: mrtrix.h:247
int axis
size_t index