aimsalgo 6.0.0
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 <cartodata/volume/volumeutil_d.h>
40#include <aims/math/mathelem.h>
41
42namespace 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>
57
58
59 template <typename T>
60 Point3d MorphoGreyLevel<T>::computeIntRadius( float radius,
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>
282 carto::VolumeRef<T> MorphoGreyLevel<T>::tryChamferErosion(
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;
370 carto::VolumeRef<int16_t> dataOut = AimsMorphoChamferOpening(
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
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
T sqr(const T &x)
T min(const Volume< T > &vol)
T max(const Volume< T > &vol)
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 > 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 > 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 > AimsMorphoChamferOpening(const carto::rc_ptr< carto::Volume< T > > &vol, float size, int xmask=3, int ymask=3, int zmask=3, float mult_fact=50)
AimsVector< int16_t, 3 > Point3d
AimsVector< float, 4 > Point4df
AimsVector< int16_t, 4 > Point4d