aimsalgo  5.0.5
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 
39 #include <aims/data/data.h>
41 #include <aims/math/mathelem.h>
42 
43 namespace aims
44 {
45 
46  template <typename T>
47  MorphoGreyLevel<T>::MorphoGreyLevel(): radius(1.), int_radius(1,1,1),
48  _use_chamfer( true ), _chamfer_factor( 50.F ),
49  _chamfer_mask_size( 3, 3, 3 )
50  {
51  }
52 
53 
54  template <typename T>
56  {
57  }
58 
59 
60  template <typename T>
62  const Point4df & voxelsize )
63  {
64  int_radius[0] = int(ceil( radius/voxelsize[0] ));
65  int_radius[1] = int(ceil( radius/voxelsize[1] ));
66  int_radius[2] = int(ceil( radius/voxelsize[2] ));
67 
68  std::cout << "Radius = " << radius << " mm" << std::endl;
69  std::cout << "Voxel size = " << voxelsize << " mm" << std::endl;
70  std::cout << "Integer radius = " << int_radius << " voxel(s)" << std::endl;
71 
72  // Test on dimension
73  if( (int_radius[0]==0) || (int_radius[1]==0) || (int_radius[2]==0) )
74  {
75  std::cout << "ERROR: Radius too small according to voxelsize !!! STOP.\n" << std::endl;
76  throw std::runtime_error( "Radius too small according to voxelsize" );
77  }
78 
79  if( (int_radius[0]==1) || (int_radius[1]==1) || (int_radius[2]==1) )
80  {
81  std::cout << "WARNING: Radius generate a single voxel !!! STOP.\n" << std::endl;
82 // exit(2);
83  }
84 
85  return int_radius;
86  }
87 
88 
89 
90  template <typename T>
91  std::vector<Point3d> MorphoGreyLevel<T>::doStructElement(
92  float radius, const Point4df & voxelsize )
93  {
94  // AimsData< int16_t > sphere( 2*int_radius[0]-1,
95  // 2*int_radius[1]-1,
96  // 2*int_radius[2]-1 );
97  // sphere.setSizeXYZT( voxelsize[0],
98  // voxelsize[1],
99  // voxelsize[2] );
100  // sphere = 0;
101 
102  // Test if list have been already created
103  // List of the voxel of the strust elem
104  float sqradius = radius * radius;
105  if( !list.size() )
106  {
107  for(int u = -int_radius[0]; u <= int_radius[0]; ++u)
108  for(int v = -int_radius[1]; v <= int_radius[1]; ++v)
109  for(int w = -int_radius[2]; w <= int_radius[2]; ++w)
110  {
111  if( sqr( float(u) * voxelsize[0] ) +
112  sqr( float(v) * voxelsize[1] ) +
113  sqr( float(w) * voxelsize[2] ) <= sqradius )
114  {
115  list.push_back( Point3d(u, v, w) );
116  // sphere(u, v, w) = 1;
117  }
118  }
119  // Writer< AimsData< int16_t > > ws( "sphere.ima" );
120  // ws.write( sphere );
121  std::cout << "Number of voxels in the structuring element = "
122  << list.size() << std::endl << std::endl;
123  return list;
124  }
125  else return list;
126  }
127 
128 
129  template<typename T>
131  const carto::VolumeRef< T >& dataIn, float radius )
132  {
133  std::cout << "EROSION" << std::endl;
134 
135  carto::VolumeRef< T > dataOut;
136 
137  dataOut = tryChamferErosion( dataIn, radius );
138  if( dataOut.get() )
139  return dataOut;
140 
141  T value = carto::VolumeUtil<T>::max(dataIn);
142 
143  std::vector<float> vs = dataIn.getVoxelSize();
144  Point4df voxelsize( vs[0], vs[1], vs[2], vs[3] );
145 
146  Point4d dim( dataIn->getSizeX(),
147  dataIn->getSizeY(),
148  dataIn->getSizeZ(),
149  dataIn->getSizeT() );
150 
151  // FIXME: this copy sometimes gets a shared buffer. WHY ?
152  // carto::VolumeRef< T > dataOut( new carto::Volume<T>( dataIn ) );
153  dataOut.reset(
154  new carto::Volume<T>( dataIn->getSizeX(), dataIn->getSizeY(),
155  dataIn->getSizeZ(), dataIn->getSizeT() ) );
156  dataOut->fill( T(0) );
157  dataOut->copyHeaderFrom( dataIn->header() );
158 
159  int_radius = computeIntRadius( radius, voxelsize );
160  list = doStructElement( radius, voxelsize );
161 
162  for(int t = 0; t < dim[3]; ++t)
163  for(int z = 0; z < dim[2]; ++z)
164  {
165  // float pct = float(z*100./dim[2]);
166  // std::cout << "\rPercent done : " << pct << std::flush";
167  for(int y = 0; y < dim[1]; ++y)
168  for(int x = 0; x < dim[0]; ++x)
169  {
170  T min = value, tmp;
171  for(int l = 0; l < int(list.size()); ++l)
172  {
173  Point3d coord( x + list[l][0],
174  y + list[l][1],
175  z + list[l][2] );
176 
177  if( (coord[0]>=0) && (coord[0]<dim[0] )
178  && (coord[1]>=0) && (coord[1]<dim[1] )
179  && (coord[2]>=0) && (coord[2]<dim[2] ) )
180  {
181  tmp = dataIn->at( coord[0], coord[1], coord[2] );
182  if( tmp < min ) min = tmp;
183  }
184  }
185  dataOut->at( x, y, z, t ) = min;
186  }
187  }
188  std::cout << std::endl;
189  return dataOut;
190  }
191 
192 
193  template<typename T>
195  const carto::VolumeRef< T >& dataIn, float radius )
196  {
197  std::cout << "DILATION" << std::endl;
198 
199  carto::VolumeRef< T > dataOut;
200 
201  dataOut = tryChamferDilation( dataIn, radius );
202  if( dataOut.get() )
203  return dataOut;
204 
205  T value = carto::VolumeUtil<T>::min(dataIn);
206 
207  std::vector<float> vs = dataIn->getVoxelSize();
208  Point4df voxelsize( vs[0], vs[1], vs[2], vs[3] );
209 
210  Point4d dim( dataIn->getSizeX(),
211  dataIn->getSizeY(),
212  dataIn->getSizeZ(),
213  dataIn->getSizeT() );
214 
215  // FIXME: this copy sometimes gets a shared buffer. WHY ?
216 // carto::VolumeRef< T > dataOut( new carto::Volume<T>( dataIn ) );
217  dataOut.reset(
218  new carto::Volume<T>( dataIn->getSizeX(), dataIn->getSizeY(),
219  dataIn->getSizeZ(), dataIn->getSizeT() ) );
220  dataOut->fill( T(0) );
221  dataOut->copyHeaderFrom( dataIn.header() );
222 
223  int_radius = computeIntRadius( radius, voxelsize );
224  list = doStructElement( radius, voxelsize );
225 
226  for(int t = 0; t < dim[3]; ++t)
227  for(int z = 0; z < dim[2]; ++z)
228  {
229  float pct = float(z*100./dim[2]);
230  std::cout << "\rPercent done : " << pct << std::flush;
231  for(int y = 0; y < dim[1]; ++y)
232  for(int x = 0; x < dim[0]; ++x)
233  {
234  T max = value, tmp;
235  for(int l = 0; l < int(list.size()); ++l)
236  {
237  Point3d coord( x + list[l][0],
238  y + list[l][1],
239  z + list[l][2] );
240 
241  if( (coord[0]>=0) && (coord[0]<dim[0] )
242  && (coord[1]>=0) && (coord[1]<dim[1] )
243  && (coord[2]>=0) && (coord[2]<dim[2] ) )
244  {
245  tmp = dataIn->at( coord[0], coord[1], coord[2] );
246  if( tmp > max ) max = tmp;
247  }
248  }
249  dataOut->at( x, y, z, t ) = max;
250  }
251  }
252  std::cout << std::endl;
253  return dataOut;
254  }
255 
256 
257  template<typename T>
259  const carto::VolumeRef<T>& dataIn, float radius )
260  {
261  carto::VolumeRef< T > dataOut;
262 
263  dataOut = tryChamferClosing( dataIn, radius );
264  if( dataOut.get() )
265  return dataOut;
266 
267  dataOut = doDilation( dataIn, radius );
268  dataOut = doErosion( dataOut, radius );
269 
270  return dataOut;
271  }
272 
273 
274  template<typename T>
276  const carto::VolumeRef<T>& dataIn, float radius )
277  {
278  carto::VolumeRef< T > dataOut;
279 
280  dataOut = tryChamferOpening( dataIn, radius );
281  if( dataOut.get() )
282  return dataOut;
283 
284  dataOut = doErosion( dataIn, radius );
285  dataOut = doDilation( dataOut, radius );
286 
287  return dataOut;
288  }
289 
290  // chamfer-specific implementations
291 
292  template<typename T>
294  const carto::VolumeRef< T >& /*dataIn*/, float /*radius*/ )
295  {
296  return 0;
297  }
298 
299 
300  template<typename T>
302  const carto::VolumeRef< T >& /*dataIn*/, float /*radius*/ )
303  {
304  return 0;
305  }
306 
307 
308  template<typename T>
310  const carto::VolumeRef< T >& /*dataIn*/, float /*radius*/ )
311  {
312  return 0;
313  }
314 
315 
316  template<typename T>
318  const carto::VolumeRef< T >& /*dataIn*/, float /*radius*/ )
319  {
320  return 0;
321  }
322 
323 
324  template<>
326  const carto::VolumeRef< int16_t >& dataIn, float radius )
327  {
328  if( _use_chamfer && isBinary( dataIn ) )
329  {
330  carto::VolumeRef<int16_t> dataIn2 = checkBorderWidth( dataIn );
333  const_cast<carto::VolumeRef<int16_t> &>( dataIn2 ) ),
334  radius, _chamfer_mask_size[0], _chamfer_mask_size[0],
335  _chamfer_mask_size[0], _chamfer_factor ).volume();
336  }
337  return 0;
338  }
339 
340 
341  template<>
343  const carto::VolumeRef< int16_t >& dataIn, float radius )
344  {
345  if( _use_chamfer && isBinary( dataIn ) )
346  {
347  carto::VolumeRef<int16_t> dataIn2 = checkBorderWidth( dataIn );
350  const_cast<carto::VolumeRef<int16_t> &>( dataIn2 ) ),
351  radius, _chamfer_mask_size[0], _chamfer_mask_size[0],
352  _chamfer_mask_size[0], _chamfer_factor ).volume();
353  }
354  return 0;
355  }
356 
357 
358  template<>
360  const carto::VolumeRef< int16_t >& dataIn, float radius )
361  {
362  if( _use_chamfer && isBinary( dataIn ) )
363  {
364  carto::VolumeRef<int16_t> dataIn2 = checkBorderWidth( dataIn );
367  const_cast<carto::VolumeRef<int16_t> &>( dataIn2 ) ),
368  radius, _chamfer_mask_size[0], _chamfer_mask_size[0],
369  _chamfer_mask_size[0], _chamfer_factor ).volume();
370  }
371  return 0;
372  }
373 
374 
375  template<>
377  const carto::VolumeRef< int16_t >& dataIn, float radius )
378  {
379  if( _use_chamfer && isBinary( dataIn ) )
380  {
381  carto::VolumeRef<int16_t> dataIn2 = checkDistanceToBorder( dataIn,
382  radius );
383  int size_diff = ( dataIn2->getSizeX() - dataIn->getSizeX() ) / 2;
386  const_cast<carto::VolumeRef<int16_t> &>( dataIn2 ) ),
387  radius, 3, 3, 3, _chamfer_factor ).volume();
388  if( size_diff != 0 )
389  dataOut = reallocateVolume( dataOut, -size_diff, 1 ); // FIXME border
390  return dataOut;
391  }
392  return 0;
393  }
394 
395  // chamfer-specific sanity checks
396 
397  template<typename T>
399  const carto::VolumeRef<T>& dataIn, float radius ) const
400  {
401  std::set<float> f;
402  std::vector<float> vs = dataIn->getVoxelSize();
403  f.insert( vs[0] );
404  f.insert( vs[1] );
405  f.insert( vs[2] );
406  int offset ;
407  offset = (int) ( radius / *f.rbegin() );
408  float dist = distanceToBorder( dataIn );
409 
410  if( dist <= offset )
411  {
412  std::cout << "The distance between the object and the border is less "
413  "than the radius. Copying it.\n";
414 
415  int border_width = 1; // for now.
416  carto::VolumeRef<T> outvol = reallocateVolume( dataIn, offset,
417  border_width );
418  return outvol;
419  }
420  return dataIn;
421  }
422 
423 
424  template<typename T>
426  const carto::VolumeRef<T>& dataIn ) const
427  {
428  int needed_border = neededBorderWidth();
429  int border = 0;
430 
431  if( !dataIn->refVolume().isNull() ) // is a view to another volume
432  {
433  std::set<int> b;
434  b.insert( dataIn->refVolume()->getSizeX() - dataIn->getSizeX() );
435  b.insert( dataIn->refVolume()->getSizeY() - dataIn->getSizeY() );
436  b.insert( dataIn->refVolume()->getSizeZ() - dataIn->getSizeZ() );
437  border = *b.rbegin() / 2;
438  }
439  if( border < needed_border )
440  return reallocateVolume( dataIn, 0, needed_border );
441  return dataIn;
442  }
443 
444 
445  template<typename T>
447  {
448  int x,y,z;
449  int dx = vol->getSizeX() ,dy = vol->getSizeY(), dz = vol->getSizeZ();
450  int distance = dx + dy + dz;
451 
452  for(x=0; x < dx; ++x)
453  for(y=0; y < dy; ++y)
454  for(z=0; z < dz; ++z)
455  if ( vol->at(x,y,z) == 32767 )
456  {
457  if (x < distance)
458  distance = x ;
459  else
460  if ( (dx - x) < distance && (dx - x) >= 0 )
461  distance = dx - x;
462 
463  if (y < distance)
464  distance = y ;
465  else
466  if ( (dy - y) < distance && (dy - y) >= 0 )
467  distance = dy - y;
468 
469  if (z < distance)
470  distance = z ;
471  else
472  if ( (dz - z) < distance && (dz - z) >= 0 )
473  distance = dz - z;
474  }
475 
476  return distance;
477  }
478 
479 
480  template<typename T>
482  const carto::VolumeRef<T>& dataIn, int added_width, int border_width )
483  {
484  int x, y, z, ox = 0, oy = 0, oz = 0;
485  int dx = dataIn->getSizeX(), dy = dataIn->getSizeY(),
486  dz = dataIn->getSizeZ();
487 
488  carto::VolumeRef<T> volBig( dx + 2 * added_width,
489  dy + 2 * added_width,
490  dz + 2 * added_width,
491  1, border_width );
492 
493  if( added_width < 0 )
494  {
495  dx += added_width;
496  dy += added_width;
497  dz += added_width;
498  ox = -added_width;
499  oy = -added_width;
500  oz = -added_width;
501  }
502 
503  std::vector<float> voxelsize = dataIn->getVoxelSize();
504  volBig->copyHeaderFrom( dataIn->header() );
505 
506  volBig->fill( 0 );
507 
508  for(z=oz; z < dz; ++z)
509  for(y=oy; y < dy; ++y)
510  for(x=ox; x < dx; ++x)
511  volBig->at( x + added_width, y + added_width, z+added_width )
512  = dataIn->at( x, y, z );
513 
514  return volBig;
515  }
516 
517 
518  template<typename T>
520  {
521  int x,y,z;
522  int dx = dataIn->getSizeX(), dy = dataIn->getSizeY(),
523  dz = dataIn->getSizeZ();
524  T value = 0, v;
525 
526  for(z=0; z < dz; ++z)
527  for(y=0; y < dy; ++y)
528  for(x=0; x < dx; ++x)
529  {
530  v = dataIn->at( x, y, z );
531  if( v != 0 )
532  {
533  if( value == 0 )
534  {
535  value = v;
536  if( v != 32767 ) // for now
537  return false;
538  }
539  else if( value != v )
540  return false;
541  }
542  }
543  return true;
544  }
545 
546 
547  template<typename T>
549  {
550  std::set<unsigned> s;
551  s.insert( _chamfer_mask_size[0] );
552  s.insert( _chamfer_mask_size[1] );
553  s.insert( _chamfer_mask_size[2] );
554  int dimMax = *s.rbegin();
555  return (int) (dimMax - 1) / 2;
556  }
557 
558 }
559 
560 #endif
561 
562 
float min(float x, float y)
Definition: thickness.h:105
AIMSALGO_API AimsData< T > AimsMorphoChamferDilation(const AimsData< T > &vol, float size, int xmask=3, int ymask=3, int zmask=3, float mult_fact=50)
float max(float x, float y)
Definition: thickness.h:97
int getSizeX() const
int getSizeY() const
int getSizeZ() const
carto::VolumeRef< T > doDilation(const carto::VolumeRef< T > &dataIn, float radius)
int getSizeT() const
AIMSALGO_API AimsData< T > AimsMorphoChamferClosing(const AimsData< T > &vol, float size, int xmask=3, int ymask=3, int zmask=3, float mult_fact=50)
carto::VolumeRef< T > doErosion(const carto::VolumeRef< T > &dataIn, float radius)
bool isNull() const
const T & at(long x, long y=0, long z=0, long t=0) const
const PropertySet & header() const
std::vector< float > getVoxelSize() const
void fill(const T &value)
carto::VolumeRef< T > doClosing(const carto::VolumeRef< T > &dataIn, float radius)
carto::VolumeRef< T > doOpening(const carto::VolumeRef< T > &dataIn, float radius)
AIMSALGO_API AimsData< T > AimsMorphoChamferErosion(const AimsData< T > &vol, float size, int xmask=3, int ymask=3, int zmask=3, float mult_fact=50)
rc_ptr< Volume< T > > refVolume() const
Volume< T > * get() const
virtual void copyHeaderFrom(const PropertySet &other)
T sqr(const T &x)
AIMSALGO_API AimsData< T > AimsMorphoChamferOpening(const AimsData< T > &vol, float size, int xmask=3, int ymask=3, int zmask=3, float mult_fact=50)
static bool isBinary(const carto::VolumeRef< T > &dataIn)
void reset(Volume< T > *p=NULL)
Grey-level mathematical morphology.