aimstil  5.0.5
regionGrowing.h
Go to the documentation of this file.
1 #ifndef TIL_REGIONGROWING_H
2 #define TIL_REGIONGROWING_H
3 
4 #include <cartobase/config/cartobase_config.h>
5 
6 // Includes from TIL library
7 #include "til/til_common.h"
8 #include "til/functors.h"
9 #include "til/imageTools.h"
10 #include "til/Neighborhood.h"
11 #include "til/numeric_array.h"
12 
13 
15 //
16 // Generic tools to do region growing.
17 //
18 // Region growing is split into two parts. The first part is the
19 // regionGrowing function, that does all the mechanics: finding neighbors
20 // (neighborhood is a parameter) and adding them to a segmentation image
21 // (another parameter) in a given color (parameter) starting from a
22 // list of given points (parameter).
23 //
24 // However, all the criteria for this mechanic, i.e. the logic of region
25 // growing, is put elsewhere in a Ghost class, which is the last parameter
26 // of the region growing function. Basically, the ghost class interacts with
27 // the region growing in three ways. (1), it tells the region growing whether
28 // to accept a point as being part of the region, via its test() member.
29 // (2), it tells the region growing whether to stop, via its stop() member.
30 // (3), it collects information from the region growing via its update
31 // member, which takes the position of any accepted point as an input.
32 //
33 // This separation allows minimal recoding. E.g. to do region growing for
34 // intensies below or above (or equal, or different, or...) from a certain
35 // threshold, one has only to code this tests in a region growing ghost,
36 // not to re-code an entire region-growing algorithm.
37 //
38 // Actually, for standard region growings, that provides only an acceptance
39 // criterion (no extra stopping criterion, no information collection),
40 // a conveniance SimpleGhost is provided, that takes a PixelTest class as
41 // a parameter.
42 //
43 //
45 
46 
47 
48 
49 
50 
51 namespace til
52 {
53 
54  typedef std::vector<numeric_array<int,3> > VoxelList;
55 
56 template < class TExpr, class TImage >
58 {
59 public: // constructors & destructor
60 
61  RegionGrowingExpr(TImage & im, TExpr expr) :
62  m_im(im), m_expr(expr) {};
63 
64 public: // functions
65  // nothing to update
66  static void update(const numeric_array<int,3> & pos) {}
67  // never stops
68  static bool stop() { return false; }
69  // use the pixel test to decide whether to add a value
70  bool test(const numeric_array<int,3> & pos) const { return m_expr(&((*m_im)(pos))); }
71 
72 private:
73 
74  TImage m_im;
75  TExpr m_expr;
76 };
77 
78 template < class PixelTest >
80 {
81 
82 public: // typedefs
83 
84  typedef typename PixelTest::TImage TImage;
85 
86 public:
87 
88  SimpleGhost(TImage &im, const PixelTest & test)
89  {
90  m_im.shallowCopy(im);
91  m_test = &test;
92  }
93 
94  // nothing to update
95  static void update(const numeric_array<int,3> &) {}
96 
97  // use the pixel test to decide whether to add a value
98  bool test(const numeric_array<int,3> &pos) const { return m_test->compute(m_im, pos); }
99 
100  // never stops
101  static bool stop() { return false; }
102 
103 private:
104 
105  TImage m_im;
106  const PixelTest *m_test;
107 };
108 
109 
110 
111 template < class PixelTest, class Plugin >
113 {
114 public: // typedefs
115 
116  typedef typename PixelTest::TImage TImage;
117 
118 public:
119 
120  PluginGhost(TImage & im, const PixelTest & test, Plugin & plugin)
121  {
122  m_im.shallowCopy(im);
123  m_test = &test;
124  m_plugin = &plugin;
125  }
126 
127  // nothing to update
128  void update(const numeric_array<int,3> &pos) { m_plugin->update(pos); }
129 
130  // use the pixel test to decide whether to add a value
131  bool test(const numeric_array<int,3> &pos) const { return m_test->compute(m_im, pos); }
132 
133  // never stops
134  static bool stop() { return false; }
135 
136 private:
137 
138  TImage m_im;
139  const PixelTest *m_test;
140  Plugin *m_plugin;
141 };
142 
143 
146 // TODO: Actually, should we not also mark points that are rejected,
147 // to avoid that they are selected again?
148 // TODO: change order: I think it is more natural if pos comes first
149 template < class TImage, class Ghost >
150 INLINE void
151 _addPoint
152 (
153  VoxelList & vl,
154  TImage & seg,
155  const numeric_array<int,3> & pos,
156  Ghost & ghost,
157  typename TImage::value_type newColor
158 )
159 {
160  if (contains(seg, pos) && seg(pos) != newColor && ghost.test(pos))
161  {
162  vl.push_back(pos);
163 
164  // NB: update could be directly linked in Ghost::test() and not
165  // appear here...
166  // NB: It is important that the update takes place before the seg value
167  // is actually changed
168  ghost.update(pos);
169 
170  seg(pos) = newColor;
171  }
172 }
173 
174 template < class TImage, class Ghost >
175 INLINE void _addPoint2
176 (
177  VoxelList & vl,
178  TImage & seg,
179  const numeric_array<int,3> & pos,
180  Ghost & ghost,
181  typename TImage::value_type newColor
182 )
183 {
184  if (seg(pos) != newColor && ghost.test(pos))
185  {
186  vl.push_back(pos);
187 
188  // NB: update could be directly linked in Ghost::test() and not
189  // appear here...
190  // NB: It is important that the update takes place before the seg value
191  // is actually changed
192  ghost.update(pos);
193 
194  seg(pos) = newColor;
195  }
196 }
197 
198 // TODO: there is a deep thinking to be done on this. E.g. I am not sure
199 // having the new color parameter here is right. It should probably be part
200 // of some policy.
201 
202 template < class TImage, class Ghost >
203 std::unique_ptr<VoxelList>
204 addNeighbors(TImage &seg,
205  const std::vector<numeric_array<int,3> > &vl,
206  const std::vector<numeric_array<int,3> > &vnh,
207  Ghost &ghost,
208  typename TImage::value_type newColor)
209 {
210  std::unique_ptr<VoxelList> newVl(new VoxelList);
211  // A factor of 3 is usually more than enough
212  newVl->reserve(3 * vl.size());
213  VoxelList::const_iterator iVl;
214 
215  std::vector<numeric_array<int,3> >::const_iterator iVnh;
216 
217  numeric_array<int,3> neighborCoord;
218 
219  for (iVl = vl.begin(); iVl != vl.end(); ++iVl)
220  {
221  for (iVnh = vnh.begin(); iVnh != vnh.end(); ++iVnh)
222  {
223  // Computes the coordinates of current neighbor
224  neighborCoord = *iVl + *iVnh;
225  //addTo(*iVl, *iVnh, neighborCoord);
226  _addPoint(*(newVl.get()), seg, neighborCoord, ghost, newColor);
227 
228  if (ghost.stop()) goto endloop;
229  }
230  }
231 
232 endloop:
233 
234  return newVl;
235 }
236 
237 // Local macro to help writing function
238 // To be undefined after use
239 // add_T<(i),(j),(k)>(*iPl, neighborCoord);
240 #define ADD_NEIGHBORS2(i,j,k) \
241 if (nh.template isNeighbor<(i),(j),(k)>() && containsNeighbor<(i),(j),(k)>(iSeg)) \
242 { \
243  addTo<(i),(j),(k)>(*iVl, neighborCoord); \
244  _addPoint2(*(newVl.get()), seg, neighborCoord, ghost, newColor); \
245  if (ghost.stop()) break; \
246 } \
247 
248 
249 template < class TImage, class Ghost, class TNeighborhood >
250 std::unique_ptr<VoxelList>
252 (
253  TImage & seg,
254  const VoxelList & vl,
255  const TNeighborhood & nh,
256  Ghost & ghost,
257  typename TImage::value_type newColor
258 )
259 {
260  std::unique_ptr<VoxelList> newVl(new VoxelList);
261  // A factor of 3 is usually more than enough
262  newVl->reserve(3 * vl.size());
263  VoxelList::const_iterator iVl;
264 
265  numeric_array<int,3> neighborCoord;
266 
267  typename Iterator<TImage>::Volumetric iSeg(seg);
268 
269  for (iVl = vl.begin(); iVl != vl.end(); ++iVl)
270  {
271  iSeg.setUnsafePos(*iVl);
272 
273  // First 6 neighbors
274  ADD_NEIGHBORS2(-1, 0, 0)
275  ADD_NEIGHBORS2( 0,-1, 0)
276  ADD_NEIGHBORS2( 0, 0,-1)
277  ADD_NEIGHBORS2(+1, 0, 0)
278  ADD_NEIGHBORS2( 0,+1, 0)
279  ADD_NEIGHBORS2( 0, 0,+1)
280 
281  // Next 12
282  ADD_NEIGHBORS2(-1,-1, 0)
283  ADD_NEIGHBORS2(-1, 0,-1)
284  ADD_NEIGHBORS2( 0,-1,-1)
285  ADD_NEIGHBORS2(+1,+1, 0)
286  ADD_NEIGHBORS2(+1, 0,+1)
287  ADD_NEIGHBORS2( 0,+1,+1)
288  ADD_NEIGHBORS2(-1,+1, 0)
289  ADD_NEIGHBORS2(-1, 0,+1)
290  ADD_NEIGHBORS2( 0,-1,+1)
291  ADD_NEIGHBORS2(+1,-1, 0)
292  ADD_NEIGHBORS2(+1, 0,-1)
293  ADD_NEIGHBORS2( 0,+1,-1)
294 
295  // Last 8
296  ADD_NEIGHBORS2(-1,-1,-1)
297  ADD_NEIGHBORS2(+1,+1,+1)
298  ADD_NEIGHBORS2(+1,-1,-1)
299  ADD_NEIGHBORS2(-1,+1,-1)
300  ADD_NEIGHBORS2(-1,-1,+1)
301  ADD_NEIGHBORS2(-1,+1,+1)
302  ADD_NEIGHBORS2(+1,-1,+1)
303  ADD_NEIGHBORS2(+1,+1,-1)
304  }
305 
306  return newVl;
307 }
308 
309 // Undefine local macro after use
310 #undef ADD_NEIGHBORS2
311 
312 
313 template < class TImage, class Ghost>
314 std::unique_ptr<VoxelList>
315 addSeeds
316 (
317  TImage &seg,
318  const VoxelList &vl,
319  Ghost &ghost,
320  typename TImage::value_type newColor
321 )
322 {
323  catro::unique_ptr<VoxelList> newVl(new VoxelList);
324  VoxelList::const_iterator iVl;
325 
326  for (iVl = vl.begin(); iVl != vl.end(); ++iVl)
327  {
328  _addPoint(*(newVl.get()), seg, *iVl, ghost, newColor);
329 
330  if (ghost.stop()) break;
331  }
332 
333  return newVl;
334 }
335 
336 
337 template < typename TImage, typename RegionGrowingGhost, typename TNeighborhood >
338 size_t regionGrowing2
339 (
340  TImage & seg,
341  const VoxelList & seeds,
342  const TNeighborhood & nh,
343  RegionGrowingGhost & ghost,
344  typename TImage::value_type color
345 )
346 {
347  // Initialize point list
348  std::unique_ptr<VoxelList> vl;
349 
350  // Initialize the region growing
351  // NB: Not all seeds are necesarily taken into account
352  // They have to follow the same rules that apply to future
353  // neighbors
354  // Seeds that no not meet requirements are thrown out.
355  // Seeds order might therefore be important, depending
356  // on the ghost used.
357 
358  //seg.reset();
359  vl = addSeeds(seg, seeds, ghost, color);
360 
361  // Counts the number of points in region
362  size_t nPoints = vl->size();
363 
364  while (vl->size() > 0 && !ghost.stop())
365  {
366  vl = addNeighbors2(seg, *vl.get(), nh, ghost, color);
367  nPoints += vl->size();
368  }
369 
370  return nPoints;
371 }
372 
373 
374 
375 //
376 template < typename TImage, typename RegionGrowingGhost >
377 size_t regionGrowing
378 (
379  TImage & seg,
380  const VoxelList & seeds,
381  const Neighborhood & nh,
382  RegionGrowingGhost & ghost,
383  typename TImage::value_type color
384 )
385 {
386  // Initialize point list
387  std::unique_ptr<VoxelList> vl;
388 
389  // Initialize the region growing
390  // NB: Not all seeds are necesarily taken into account
391  // They have to follow the same rules that apply to future
392  // neighbors
393  // Seeds that no not meet requirements are thrown out.
394  // Seeds order might therefore be important, depending
395  // on the ghost used.
396 
397  //seg.reset();
398  vl = addSeeds(seg, seeds, ghost, color);
399 
400  // Initialize neighborhood
401  std::vector<numeric_array<int,3> > vnh;
402  getNeighbors(nh, vnh);
403 
404 
405  // Counts the number of points in region
406  size_t nPoints = vl->size();
407 
408  while (vl->size() > 0 && !ghost.stop())
409  {
410  vl = addNeighbors(seg, *(vl.get()), vnh, ghost, color);
411  nPoints += vl->size();
412  }
413 
414  return nPoints;
415 }
416 
417 
418 template < typename TImage, typename RegionGrowingGhost >
419 size_t regionGrowing
420 (
421  TImage & seg,
422  const numeric_array<int,3> & seed,
423  const Neighborhood & nh,
424  RegionGrowingGhost & ghost,
425  typename TImage::value_type color
426 )
427 {
428  VoxelList seeds;
429  seeds.push_back(seed);
430  return regionGrowing(seg, seeds, nh, ghost, color);
431 }
432 
433 
434 /*
435 // If seg = im, assumes that 'color' is not present in image
436 
437 template < typename TImage, typename PixelTest, typename PlugIn = DoNothing >
438 int regionGrowing(Ptr<TImage> &seg,
439  ConstPtr<PointList<int> > &seeds,
440  const Neighborhood &nh,
441  const PixelTest &pixelTest,
442  typename TImage::value_type color,
443  PlugIn &plugin = DoNothing())
444 {
445  // Initialize the segmentation
446  seg.reset();
447  dumpPointListInImage(seeds, seg, color);
448 
449  // Initialize neighborhood
450  std::vector<numeric_array<int,3> > vnh;
451  getNeighbors(nh, vnh);
452 
453  // Initialize point list
454  Ptr<PointList<int> > pl = new PointList<int>(*seeds);
455 
456  // Counts the number of points in region
457  int nPoints = pl->size();
458 
459  while (pl->size() > 0)
460  {
461  addNeighbors(seg, pl, vnh, pixelTest, color);
462  plugin.update(pl);
463  nPoints += pl->size();
464  }
465 
466  return nPoints;
467 }
468 */
469 
470 } // namespace til
471 
472 #endif
473 
A trait class to assign iterators to image types.
RegionGrowingExpr(TImage &im, TExpr expr)
Definition: regionGrowing.h:61
std::unique_ptr< VoxelList > addSeeds(TImage &seg, const VoxelList &vl, Ghost &ghost, typename TImage::value_type newColor)
INLINE void _addPoint2(VoxelList &vl, TImage &seg, const numeric_array< int, 3 > &pos, Ghost &ghost, typename TImage::value_type newColor)
bool test(const numeric_array< int, 3 > &pos) const
Definition: regionGrowing.h:98
static bool stop()
static void update(const numeric_array< int, 3 > &pos)
Definition: regionGrowing.h:66
static void update(const numeric_array< int, 3 > &)
Definition: regionGrowing.h:95
PluginGhost(TImage &im, const PixelTest &test, Plugin &plugin)
Belongs to package Box Do not include directly, include til/Box.h instead.
Definition: Accumulator.h:10
size_t regionGrowing(TImage &seg, const VoxelList &seeds, const Neighborhood &nh, RegionGrowingGhost &ghost, typename TImage::value_type color)
size_t regionGrowing2(TImage &seg, const VoxelList &seeds, const TNeighborhood &nh, RegionGrowingGhost &ghost, typename TImage::value_type color)
#define ADD_NEIGHBORS2(i, j, k)
TIL_API void getNeighbors(const Neighborhood &nh, std::vector< numeric_array< int, 3 > > &res)
Pushes the neighbor coordinates in a container.
General macros, definitions and functions.
SimpleGhost(TImage &im, const PixelTest &test)
Definition: regionGrowing.h:88
Small miscellaneous utility functions for images.
bool test(const numeric_array< int, 3 > &pos) const
Definition: regionGrowing.h:70
#define INLINE
Definition: til_common.h:26
std::unique_ptr< VoxelList > addNeighbors(TImage &seg, const std::vector< numeric_array< int, 3 > > &vl, const std::vector< numeric_array< int, 3 > > &vnh, Ghost &ghost, typename TImage::value_type newColor)
< New boundary points
void update(const numeric_array< int, 3 > &pos)
boost::enable_if< is_numeric_container< TStorage >, bool >::type contains(const Box< T, D > &box, const TStorage &v)
Check whether a point lies within box.
Definition: boxTools.h:15
static bool stop()
std::vector< numeric_array< int, 3 > > VoxelList
Definition: regionGrowing.h:54
INLINE void _addPoint(VoxelList &vl, TImage &seg, const numeric_array< int, 3 > &pos, Ghost &ghost, typename TImage::value_type newColor)
If point at position &#39;pos&#39; satisfy region-growing criteria, it is added to the list of voxel &#39;vl&#39; and...
bool test(const numeric_array< int, 3 > &pos) const
std::unique_ptr< VoxelList > addNeighbors2(TImage &seg, const VoxelList &vl, const TNeighborhood &nh, Ghost &ghost, typename TImage::value_type newColor)
PixelTest::TImage TImage
Definition: regionGrowing.h:84
PixelTest::TImage TImage