aimsalgo  5.1.2
Neuroimaging image processing
morphogreylevel_d.h
Go to the documentation of this file.
1 /* This software and supporting documentation are distributed by
2  * Institut Federatif de Recherche 49
3  * CEA/NeuroSpin, Batiment 145,
4  * 91191 Gif-sur-Yvette cedex
5  * France
6  *
7  * This software is governed by the CeCILL-B license under
8  * French law and abiding by the rules of distribution of free software.
9  * You can use, modify and/or redistribute the software under the
10  * terms of the CeCILL-B license as circulated by CEA, CNRS
11  * and INRIA at the following URL "http://www.cecill.info".
12  *
13  * As a counterpart to the access to the source code and rights to copy,
14  * modify and redistribute granted by the license, users are provided only
15  * with a limited warranty and the software's author, the holder of the
16  * economic rights, and the successive licensors have only limited
17  * liability.
18  *
19  * In this respect, the user's attention is drawn to the risks associated
20  * with loading, using, modifying and/or developing or reproducing the
21  * software by the user in light of its specific status of free software,
22  * that may mean that it is complicated to manipulate, and that also
23  * therefore means that it is reserved for developers and experienced
24  * professionals having in-depth computer knowledge. Users are therefore
25  * encouraged to load and test the software's suitability as regards their
26  * requirements in conditions enabling the security of their systems and/or
27  * data to be ensured and, more generally, to use and operate it in the
28  * same conditions as regards security.
29  *
30  * The fact that you are presently reading this means that you have had
31  * knowledge of the CeCILL-B license and that you accept its terms.
32  */
33 
34 
35 #ifndef AIMS_MORPHOLOGY_MORPHOGREYLEVEL_D_H
36 #define AIMS_MORPHOLOGY_MORPHOGREYLEVEL_D_H
37 
40 #include <aims/math/mathelem.h>
41 
42 namespace aims
43 {
44 
45  template <typename T>
46  MorphoGreyLevel<T>::MorphoGreyLevel(): radius(1.), int_radius(1,1,1),
47  _use_chamfer( true ), _chamfer_factor( 50.F ),
48  _chamfer_mask_size( 3, 3, 3 )
49  {
50  }
51 
52 
53  template <typename T>
55  {
56  }
57 
58 
59  template <typename T>
61  const Point4df & voxelsize )
62  {
63  int_radius[0] = int(ceil( radius/voxelsize[0] ));
64  int_radius[1] = int(ceil( radius/voxelsize[1] ));
65  int_radius[2] = int(ceil( radius/voxelsize[2] ));
66 
67  std::cout << "Radius = " << radius << " mm" << std::endl;
68  std::cout << "Voxel size = " << voxelsize << " mm" << std::endl;
69  std::cout << "Integer radius = " << int_radius << " voxel(s)" << std::endl;
70 
71  // Test on dimension
72  if( (int_radius[0]==0) || (int_radius[1]==0) || (int_radius[2]==0) )
73  {
74  std::cout << "ERROR: Radius too small according to voxelsize !!! STOP.\n" << std::endl;
75  throw std::runtime_error( "Radius too small according to voxelsize" );
76  }
77 
78  if( (int_radius[0]==1) || (int_radius[1]==1) || (int_radius[2]==1) )
79  {
80  std::cout << "WARNING: Radius generate a single voxel !!! STOP.\n" << std::endl;
81 // exit(2);
82  }
83 
84  return int_radius;
85  }
86 
87 
88 
89  template <typename T>
90  std::vector<Point3d> MorphoGreyLevel<T>::doStructElement(
91  float radius, const Point4df & voxelsize )
92  {
93  // Test if list have been already created
94  // List of the voxel of the strust elem
95  float sqradius = radius * radius;
96  if( !list.size() )
97  {
98  for(int u = -int_radius[0]; u <= int_radius[0]; ++u)
99  for(int v = -int_radius[1]; v <= int_radius[1]; ++v)
100  for(int w = -int_radius[2]; w <= int_radius[2]; ++w)
101  {
102  if( sqr( float(u) * voxelsize[0] ) +
103  sqr( float(v) * voxelsize[1] ) +
104  sqr( float(w) * voxelsize[2] ) <= sqradius )
105  {
106  list.push_back( Point3d(u, v, w) );
107  // sphere(u, v, w) = 1;
108  }
109  }
110  std::cout << "Number of voxels in the structuring element = "
111  << list.size() << std::endl << std::endl;
112  return list;
113  }
114  else return list;
115  }
116 
117 
118  template<typename T>
120  const carto::VolumeRef< T >& dataIn, float radius )
121  {
122  std::cout << "EROSION" << std::endl;
123 
124  carto::VolumeRef< T > dataOut;
125 
126  dataOut = tryChamferErosion( dataIn, radius );
127  if( dataOut.get() )
128  return dataOut;
129 
130  T value = carto::VolumeUtil<T>::max(dataIn);
131 
132  std::vector<float> vs = dataIn.getVoxelSize();
133  Point4df voxelsize( vs[0], vs[1], vs[2], vs[3] );
134 
135  Point4d dim( dataIn->getSizeX(),
136  dataIn->getSizeY(),
137  dataIn->getSizeZ(),
138  dataIn->getSizeT() );
139 
140  // FIXME: this copy sometimes gets a shared buffer. WHY ?
141  // carto::VolumeRef< T > dataOut( new carto::Volume<T>( dataIn ) );
142  dataOut.reset(
143  new carto::Volume<T>( dataIn->getSizeX(), dataIn->getSizeY(),
144  dataIn->getSizeZ(), dataIn->getSizeT() ) );
145  dataOut->fill( T(0) );
146  dataOut->copyHeaderFrom( dataIn->header() );
147 
148  int_radius = computeIntRadius( radius, voxelsize );
149  list = doStructElement( radius, voxelsize );
150 
151  for(int t = 0; t < dim[3]; ++t)
152  for(int z = 0; z < dim[2]; ++z)
153  {
154  // float pct = float(z*100./dim[2]);
155  // std::cout << "\rPercent done : " << pct << std::flush";
156  for(int y = 0; y < dim[1]; ++y)
157  for(int x = 0; x < dim[0]; ++x)
158  {
159  T min = value, tmp;
160  for(int l = 0; l < int(list.size()); ++l)
161  {
162  Point3d coord( x + list[l][0],
163  y + list[l][1],
164  z + list[l][2] );
165 
166  if( (coord[0]>=0) && (coord[0]<dim[0] )
167  && (coord[1]>=0) && (coord[1]<dim[1] )
168  && (coord[2]>=0) && (coord[2]<dim[2] ) )
169  {
170  tmp = dataIn->at( coord[0], coord[1], coord[2] );
171  if( tmp < min ) min = tmp;
172  }
173  }
174  dataOut->at( x, y, z, t ) = min;
175  }
176  }
177  std::cout << std::endl;
178  return dataOut;
179  }
180 
181 
182  template<typename T>
184  const carto::VolumeRef< T >& dataIn, float radius )
185  {
186  std::cout << "DILATION" << std::endl;
187 
188  carto::VolumeRef< T > dataOut;
189 
190  dataOut = tryChamferDilation( dataIn, radius );
191  if( dataOut.get() )
192  return dataOut;
193 
194  T value = carto::VolumeUtil<T>::min(dataIn);
195 
196  std::vector<float> vs = dataIn->getVoxelSize();
197  Point4df voxelsize( vs[0], vs[1], vs[2], vs[3] );
198 
199  Point4d dim( dataIn->getSizeX(),
200  dataIn->getSizeY(),
201  dataIn->getSizeZ(),
202  dataIn->getSizeT() );
203 
204  // FIXME: this copy sometimes gets a shared buffer. WHY ?
205 // carto::VolumeRef< T > dataOut( new carto::Volume<T>( dataIn ) );
206  dataOut.reset(
207  new carto::Volume<T>( dataIn->getSizeX(), dataIn->getSizeY(),
208  dataIn->getSizeZ(), dataIn->getSizeT() ) );
209  dataOut->fill( T(0) );
210  dataOut->copyHeaderFrom( dataIn.header() );
211 
212  int_radius = computeIntRadius( radius, voxelsize );
213  list = doStructElement( radius, voxelsize );
214 
215  for(int t = 0; t < dim[3]; ++t)
216  for(int z = 0; z < dim[2]; ++z)
217  {
218  float pct = float(z*100./dim[2]);
219  std::cout << "\rPercent done : " << pct << std::flush;
220  for(int y = 0; y < dim[1]; ++y)
221  for(int x = 0; x < dim[0]; ++x)
222  {
223  T max = value, tmp;
224  for(int l = 0; l < int(list.size()); ++l)
225  {
226  Point3d coord( x + list[l][0],
227  y + list[l][1],
228  z + list[l][2] );
229 
230  if( (coord[0]>=0) && (coord[0]<dim[0] )
231  && (coord[1]>=0) && (coord[1]<dim[1] )
232  && (coord[2]>=0) && (coord[2]<dim[2] ) )
233  {
234  tmp = dataIn->at( coord[0], coord[1], coord[2] );
235  if( tmp > max ) max = tmp;
236  }
237  }
238  dataOut->at( x, y, z, t ) = max;
239  }
240  }
241  std::cout << std::endl;
242  return dataOut;
243  }
244 
245 
246  template<typename T>
248  const carto::VolumeRef<T>& dataIn, float radius )
249  {
250  carto::VolumeRef< T > dataOut;
251 
252  dataOut = tryChamferClosing( dataIn, radius );
253  if( dataOut.get() )
254  return dataOut;
255 
256  dataOut = doDilation( dataIn, radius );
257  dataOut = doErosion( dataOut, radius );
258 
259  return dataOut;
260  }
261 
262 
263  template<typename T>
265  const carto::VolumeRef<T>& dataIn, float radius )
266  {
267  carto::VolumeRef< T > dataOut;
268 
269  dataOut = tryChamferOpening( dataIn, radius );
270  if( dataOut.get() )
271  return dataOut;
272 
273  dataOut = doErosion( dataIn, radius );
274  dataOut = doDilation( dataOut, radius );
275 
276  return dataOut;
277  }
278 
279  // chamfer-specific implementations
280 
281  template<typename T>
283  const carto::VolumeRef< T >& /*dataIn*/, float /*radius*/ )
284  {
285  return 0;
286  }
287 
288 
289  template<typename T>
290  carto::VolumeRef<T> MorphoGreyLevel<T>::tryChamferDilation(
291  const carto::VolumeRef< T >& /*dataIn*/, float /*radius*/ )
292  {
293  return 0;
294  }
295 
296 
297  template<typename T>
298  carto::VolumeRef<T> MorphoGreyLevel<T>::tryChamferClosing(
299  const carto::VolumeRef< T >& /*dataIn*/, float /*radius*/ )
300  {
301  return 0;
302  }
303 
304 
305  template<typename T>
306  carto::VolumeRef<T> MorphoGreyLevel<T>::tryChamferOpening(
307  const carto::VolumeRef< T >& /*dataIn*/, float /*radius*/ )
308  {
309  return 0;
310  }
311 
312 
313  template<>
314  carto::VolumeRef<int16_t> MorphoGreyLevel<int16_t>::tryChamferErosion(
315  const carto::VolumeRef< int16_t >& dataIn, float radius )
316  {
317  if( _use_chamfer && isBinary( dataIn ) )
318  {
319  carto::VolumeRef<int16_t> dataIn2 = checkBorderWidth( dataIn );
321  dataIn2,
322  radius, _chamfer_mask_size[0], _chamfer_mask_size[0],
323  _chamfer_mask_size[0], _chamfer_factor );
324  }
325  return 0;
326  }
327 
328 
329  template<>
330  carto::VolumeRef<int16_t> MorphoGreyLevel<int16_t>::tryChamferDilation(
331  const carto::VolumeRef< int16_t >& dataIn, float radius )
332  {
333  if( _use_chamfer && isBinary( dataIn ) )
334  {
335  carto::VolumeRef<int16_t> dataIn2 = checkBorderWidth( dataIn );
337  dataIn2,
338  radius, _chamfer_mask_size[0], _chamfer_mask_size[0],
339  _chamfer_mask_size[0], _chamfer_factor );
340  }
341  return 0;
342  }
343 
344 
345  template<>
346  carto::VolumeRef<int16_t> MorphoGreyLevel<int16_t>::tryChamferClosing(
347  const carto::VolumeRef< int16_t >& dataIn, float radius )
348  {
349  if( _use_chamfer && isBinary( dataIn ) )
350  {
351  carto::VolumeRef<int16_t> dataIn2 = checkBorderWidth( dataIn );
353  dataIn2,
354  radius, _chamfer_mask_size[0], _chamfer_mask_size[0],
355  _chamfer_mask_size[0], _chamfer_factor );
356  }
357  return 0;
358  }
359 
360 
361  template<>
362  carto::VolumeRef<int16_t> MorphoGreyLevel<int16_t>::tryChamferOpening(
363  const carto::VolumeRef< int16_t >& dataIn, float radius )
364  {
365  if( _use_chamfer && isBinary( dataIn ) )
366  {
367  carto::VolumeRef<int16_t> dataIn2 = checkDistanceToBorder( dataIn,
368  radius );
369  int size_diff = ( dataIn2->getSizeX() - dataIn->getSizeX() ) / 2;
371  dataIn2,
372  radius, 3, 3, 3, _chamfer_factor );
373  if( size_diff != 0 )
374  dataOut = reallocateVolume( dataOut, -size_diff, 1 ); // FIXME border
375  return dataOut;
376  }
377  return 0;
378  }
379 
380  // chamfer-specific sanity checks
381 
382  template<typename T>
383  carto::VolumeRef<T> MorphoGreyLevel<T>::checkDistanceToBorder(
384  const carto::VolumeRef<T>& dataIn, float radius ) const
385  {
386  std::set<float> f;
387  std::vector<float> vs = dataIn->getVoxelSize();
388  f.insert( vs[0] );
389  f.insert( vs[1] );
390  f.insert( vs[2] );
391  int offset ;
392  offset = (int) ( radius / *f.rbegin() );
393  float dist = distanceToBorder( dataIn );
394 
395  if( dist <= offset )
396  {
397  std::cout << "The distance between the object and the border is less "
398  "than the radius. Copying it.\n";
399 
400  int border_width = 1; // for now.
401  carto::VolumeRef<T> outvol = reallocateVolume( dataIn, offset,
402  border_width );
403  return outvol;
404  }
405  return dataIn;
406  }
407 
408 
409  template<typename T>
410  carto::VolumeRef<T> MorphoGreyLevel<T>::checkBorderWidth(
411  const carto::VolumeRef<T>& dataIn ) const
412  {
413  int needed_border = neededBorderWidth();
414  int border = 0;
415 
416  if( !dataIn->refVolume().isNull() ) // is a view to another volume
417  {
418  std::set<int> b;
419  b.insert( dataIn->refVolume()->getSizeX() - dataIn->getSizeX() );
420  b.insert( dataIn->refVolume()->getSizeY() - dataIn->getSizeY() );
421  b.insert( dataIn->refVolume()->getSizeZ() - dataIn->getSizeZ() );
422  border = *b.rbegin() / 2;
423  }
424  if( border < needed_border )
425  return reallocateVolume( dataIn, 0, needed_border );
426  return dataIn;
427  }
428 
429 
430  template<typename T>
431  float MorphoGreyLevel<T>::distanceToBorder( const carto::VolumeRef<T> &vol )
432  {
433  int x,y,z;
434  int dx = vol->getSizeX() ,dy = vol->getSizeY(), dz = vol->getSizeZ();
435  int distance = dx + dy + dz;
436 
437  for(x=0; x < dx; ++x)
438  for(y=0; y < dy; ++y)
439  for(z=0; z < dz; ++z)
440  if ( vol->at(x,y,z) == 32767 )
441  {
442  if (x < distance)
443  distance = x ;
444  else
445  if ( (dx - x) < distance && (dx - x) >= 0 )
446  distance = dx - x;
447 
448  if (y < distance)
449  distance = y ;
450  else
451  if ( (dy - y) < distance && (dy - y) >= 0 )
452  distance = dy - y;
453 
454  if (z < distance)
455  distance = z ;
456  else
457  if ( (dz - z) < distance && (dz - z) >= 0 )
458  distance = dz - z;
459  }
460 
461  return distance;
462  }
463 
464 
465  template<typename T>
466  carto::VolumeRef<T> MorphoGreyLevel<T>::reallocateVolume(
467  const carto::VolumeRef<T>& dataIn, int added_width, int border_width )
468  {
469  int x, y, z, ox = 0, oy = 0, oz = 0;
470  int dx = dataIn->getSizeX(), dy = dataIn->getSizeY(),
471  dz = dataIn->getSizeZ();
472 
473  carto::VolumeRef<T> volBig( dx + 2 * added_width,
474  dy + 2 * added_width,
475  dz + 2 * added_width,
476  1, border_width );
477 
478  if( added_width < 0 )
479  {
480  dx += added_width;
481  dy += added_width;
482  dz += added_width;
483  ox = -added_width;
484  oy = -added_width;
485  oz = -added_width;
486  }
487 
488  std::vector<float> voxelsize = dataIn->getVoxelSize();
489  volBig->copyHeaderFrom( dataIn->header() );
490 
491  volBig->fill( 0 );
492 
493  for(z=oz; z < dz; ++z)
494  for(y=oy; y < dy; ++y)
495  for(x=ox; x < dx; ++x)
496  volBig->at( x + added_width, y + added_width, z+added_width )
497  = dataIn->at( x, y, z );
498 
499  return volBig;
500  }
501 
502 
503  template<typename T>
505  {
506  int x,y,z;
507  int dx = dataIn->getSizeX(), dy = dataIn->getSizeY(),
508  dz = dataIn->getSizeZ();
509  T value = 0, v;
510 
511  for(z=0; z < dz; ++z)
512  for(y=0; y < dy; ++y)
513  for(x=0; x < dx; ++x)
514  {
515  v = dataIn->at( x, y, z );
516  if( v != 0 )
517  {
518  if( value == 0 )
519  {
520  value = v;
521  if( v != 32767 ) // for now
522  return false;
523  }
524  else if( value != v )
525  return false;
526  }
527  }
528  return true;
529  }
530 
531 
532  template<typename T>
534  {
535  std::set<unsigned> s;
536  s.insert( _chamfer_mask_size[0] );
537  s.insert( _chamfer_mask_size[1] );
538  s.insert( _chamfer_mask_size[2] );
539  int dimMax = *s.rbegin();
540  return (int) (dimMax - 1) / 2;
541  }
542 
543 }
544 
545 #endif
546 
547 
Grey-level mathematical morphology.
carto::VolumeRef< T > doClosing(const carto::VolumeRef< T > &dataIn, float radius)
carto::VolumeRef< T > doErosion(const carto::VolumeRef< T > &dataIn, float radius)
static bool isBinary(const carto::VolumeRef< T > &dataIn)
carto::VolumeRef< T > doDilation(const carto::VolumeRef< T > &dataIn, float radius)
carto::VolumeRef< T > doOpening(const carto::VolumeRef< T > &dataIn, float radius)
int getSizeZ() const
std::vector< float > getVoxelSize() const
virtual void copyHeaderFrom(const PropertySet &other)
int getSizeT() const
rc_ptr< Volume< T > > refVolume() const
const T & at(long x, long y=0, long z=0, long t=0) const
int getSizeY() const
const PropertySet & header() const
void fill(const T &value)
int getSizeX() const
bool isNull() const
void reset(T *p=NULL)
T * get() const
float min(float x, float y)
Definition: thickness.h:106
float max(float x, float y)
Definition: thickness.h:98
T sqr(const T &x)
carto::VolumeRef< T > AimsMorphoChamferErosion(const carto::rc_ptr< carto::Volume< T > > &vol, float size, int xmask=3, int ymask=3, int zmask=3, float mult_fact=50)
carto::VolumeRef< T > AimsMorphoChamferDilation(const carto::rc_ptr< carto::Volume< T > > &vol, float size, int xmask=3, int ymask=3, int zmask=3, float mult_fact=50)
carto::VolumeRef< T > AimsMorphoChamferClosing(const carto::rc_ptr< carto::Volume< T > > &vol, float size, int xmask=3, int ymask=3, int zmask=3, float mult_fact=50)
carto::VolumeRef< T > AimsMorphoChamferOpening(const carto::rc_ptr< carto::Volume< T > > &vol, float size, int xmask=3, int ymask=3, int zmask=3, float mult_fact=50)