Seg3D  2.4
Seg3D is a free volume segmentation and processing tool developed by the NIH Center for Integrative Biomedical Computing at the University of Utah Scientific Computing and Imaging (SCI) Institute.
cluster.hxx
1 /*
2  For more information, please see: http://software.sci.utah.edu
3 
4  The MIT License
5 
6  Copyright (c) 2016 Scientific Computing and Imaging Institute,
7  University of Utah.
8 
9 
10  Permission is hereby granted, free of charge, to any person obtaining a
11  copy of this software and associated documentation files (the "Software"),
12  to deal in the Software without restriction, including without limitation
13  the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  and/or sell copies of the Software, and to permit persons to whom the
15  Software is furnished to do so, subject to the following conditions:
16 
17  The above copyright notice and this permission notice shall be included
18  in all copies or substantial portions of the Software.
19 
20  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  DEALINGS IN THE SOFTWARE.
27  */
28 
29 // File : cluster.hxx
30 // Author : Pavel A. Koshevoy
31 // Created : 2006/04/05 12:36
32 // Copyright : (C) 2004-2008 University of Utah
33 // Description : Helper functions for identifying pixel clusters in
34 // thresholded 1D/2D discrete function/image.
35 
36 #ifndef CLUSTER_HXX_
37 #define CLUSTER_HXX_
38 
39 // local includes:
40 #include <Core/ITKCommon/common.hxx>
41 #include <Core/ITKCommon/the_dynamic_array.hxx>
42 #include <Core/ITKCommon/the_utils.hxx>
43 
44 #include <Core/Utils/Exception.h>
45 
46 // system includes:
47 #include <list>
48 
49 // ITK includes:
50 #include <itkPoint.h>
51 
52 
53 //----------------------------------------------------------------
54 // cluster_t
55 //
56 // A list of 2D pixels.
57 //
58 typedef std::list<image_t::IndexType> cluster_t;
59 
60 
61 //----------------------------------------------------------------
62 // centerofmass_t
63 //
64 // A wrapper for a physical point and a scalar mass.
65 //
66 template <unsigned int d>
68 {
69 public:
71  mass_(0.0)
72  {}
73 
74  // comparison operators:
75  inline bool operator < (const centerofmass_t<d> & cm) const
76  { return mass_ < cm.mass_; }
77 
78  inline bool operator > (const centerofmass_t<d> & cm) const
79  { return mass_ > cm.mass_; }
80 
81  // accessors:
82  inline const double & operator [] (const unsigned int & index) const
83  { return cm_[index]; }
84 
85  inline double & operator [] (const unsigned int & index)
86  { return cm_[index]; }
87 
88  // the center of mass of the cluster:
89  itk::Point<double, d> cm_;
90 
91  // the mass of the cluster normalized by the size of the cluster:
92  double mass_;
93 };
94 
95 
96 //----------------------------------------------------------------
97 // identify_clusters_cm
98 //
99 // Identify clusters in the 1D data (anything that's not 0),
100 // calculate a center of mass for each cluster.
101 //
102 extern bool
103 identify_clusters_cm(const double * data,
104  const unsigned int & size,
105  std::list<centerofmass_t<1> > & centerofmass,
106  const bool periodic = true);
107 
108 //----------------------------------------------------------------
109 // identify_clusters_cm
110 //
111 // Calculate the centers of mass for a given set of
112 // 2D pixel clusters.
113 //
114 extern bool
115 identify_clusters_cm(const image_t * image,
116  const the_dynamic_array_t<cluster_t> & clusters,
117  std::list<centerofmass_t<2> > & centerofmass);
118 
119 //----------------------------------------------------------------
120 // assemble_clusters
121 //
122 // Identify 2D pixel clusters (above a given threshold -- the
123 // background), using either 4 or 8 connectivity stencil.
124 //
125 template <class image_t>
126 void
127 assemble_clusters(const image_t * image,
128  const mask_t * mask,
129  typename image_t::PixelType background,
130  const unsigned int connectivity,
131  the_dynamic_array_t<std::list<typename image_t::IndexType> >
132  & clusters)
133 {
134  typedef typename image_t::RegionType region_t;
135  typedef typename image_t::PixelType pixel_t;
136  typedef typename image_t::IndexType index_t;
137  typedef std::list<index_t> cluster_t;
138 
139  // assert(connectivity <= 8);
140  if (connectivity > 8)
141  {
142  CORE_THROW_EXCEPTION("cluster connectivity > 8");
143  }
144 
145  // classify the clusters:
146  static int stencil[][2] = {
147  // 4 connected:
148  { 0, -1 },
149  {-1, 0 },
150  { 0, 1 },
151  { 1, 0 },
152 
153  // 8 connected:
154  {-1, -1 },
155  { 1, 1 },
156  {-1, 1 },
157  { 1, -1 }
158  };
159 
160  mask_t::RegionType::SizeType mask_size =
161  image->GetLargestPossibleRegion().GetSize();
162  unsigned int spacing_scale = 1;
163 
164  if (mask)
165  {
166  mask_size = mask->GetLargestPossibleRegion().GetSize();
167  spacing_scale =
168  static_cast<unsigned int>(image->GetSpacing()[0] / mask->GetSpacing()[0]);
169  }
170 
171  region_t rn = image->GetLargestPossibleRegion();
172  typename region_t::SizeType sz = rn.GetSize();
173 
174  typedef itk::Image<unsigned int> cluster_map_t;
175  cluster_map_t::Pointer cluster_map = make_image<cluster_map_t>(sz, ~0u);
176 
177  typedef itk::ImageRegionConstIteratorWithIndex<image_t> itex_t;
178  itex_t itex(image, rn);
179 
180  for (itex.GoToBegin(); !itex.IsAtEnd(); ++itex)
181  {
182  const pixel_t v = itex.Get();
183 
184  // skip over the background:
185  if (v <= background) continue;
186 
187  const index_t index = itex.GetIndex();
188 
189  // skip over pixels outside the mask:
190  if (!pixel_in_mask<image_t>(mask, mask_size, index, spacing_scale))
191  {
192  continue;
193  }
194 
195  // iterate over the neighborhood, collect the cluster ids of the neighbors:
196  std::list<unsigned int> neighbors;
197 
198  for (unsigned int k = 0; k < connectivity; k++)
199  {
200  index_t uv = index;
201  uv[0] += stencil[k][0];
202  uv[1] += stencil[k][1];
203  if (!pixel_in_mask<image_t>(mask, mask_size, uv, spacing_scale))
204  {
205  continue;
206  }
207 
208 // assert(uv[0] >= 0 && image_size_value_t(uv[0]) < sz[0] &&
209 // uv[1] >= 0 && image_size_value_t(uv[1]) < sz[1]);
210  if (uv[0] < 0 || image_size_value_t(uv[0]) > sz[0] ||
211  uv[1] < 0 || image_size_value_t(uv[1]) > sz[1])
212  {
213  CORE_THROW_EXCEPTION("cluster ids of neighbors out of bounds");
214  }
215 
216  unsigned int cluster_id = cluster_map->GetPixel(uv);
217  if (cluster_id != ~0u)
218  {
219  push_back_unique(neighbors, cluster_id);
220  }
221  }
222 
223  if (neighbors.empty())
224  {
225  // make a new cluster:
226  clusters.append(cluster_t());
227 
228  unsigned int id = clusters.end_index(true);
229  cluster_map->SetPixel(index, id);
230  clusters[id].push_back(index);
231  }
232  else
233  {
234  // add this pixel to the cluster:
235  unsigned int id = neighbors.front();
236  cluster_map->SetPixel(index, id);
237  clusters[id].push_back(index);
238 
239  if (neighbors.size() > 1)
240  {
241  // merge the cluster into one (the first one):
242  for (std::list<unsigned int>::iterator bi = ++(neighbors.begin());
243  bi != neighbors.end(); ++bi)
244  {
245  unsigned int old_id = *bi;
246 
247  while (!clusters[old_id].empty())
248  {
249  index_t uv = remove_head(clusters[old_id]);
250 
251  cluster_map->SetPixel(uv, id);
252  clusters[id].push_back(uv);
253  }
254  }
255  }
256  }
257  }
258 }
259 
260 
261 #endif // CLUSTER_HXX_
Definition: cluster.hxx:67
Definition: the_dynamic_array.hxx:45