aimsalgo 6.0.0
Neuroimaging image processing
clusterArg_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_ROI_CLUSTERARG_D_H
36#define AIMS_ROI_CLUSTERARG_D_H
37
38#include <aims/roi/clusterArg.h>
39#include <aims/utility/threshold.h>
40#include <aims/connectivity/component.h>
41#include <aims/utility/converter_volume.h>
42#include <aims/utility/converter_bucket.h>
43#include <aims/resampling/motion.h>
44#include <aims/graph/graphmanip.h>
45#include <aims/bucket/bucket.h>
46#include <aims/mesh/surface.h>
47#include <aims/mesh/surfaceOperation.h>
48#include <aims/mesh/mesher.h>
49#include <graph/graph/graph.h>
50#include <cartobase/smart/rcptr.h>
51#include <cartobase/exception/file.h>
52
53namespace aims
54{
55
56 template<typename T>
58 const carto::rc_ptr<carto::Volume<T> > & data )
59 {
60 // graph and global attributes
61
62 std::string graphsyntax = gr.getSyntax();
63 std::string vertexsyntax = "cluster";
64 GraphElementCode bec, mec;
65
66 enum GraphType
67 {
68 Cluster,
69 Fold
70 };
71
72 GraphType gtype = Cluster;
73
74 // bucket element code
75 bec.objectType = "Bucket";
76 bec.dataType = "VOID";
78
79 // mesh element code
80 mec.objectType = "Mesh";
81 mec.dataType = "VOID";
83
84 if( graphsyntax == "CorticalFoldArg" )
85 {
86 gtype = Fold;
87 vertexsyntax = "fold";
88
89 bec.attribute = "aims_ss";
90 bec.global_index_attribute = "ss_label";
91 bec.local_file_attribute = "ss_filename";
92 bec.global_filename = "ss.bck";
93 bec.id = "ss";
94
95 mec.attribute = "aims_Tmtktri";
96 mec.global_index_attribute = "Tmtktri_label";
97 mec.local_file_attribute = "Tmtktri_filename";
98 mec.global_filename = "tmtktri.mesh";
99 mec.id = "Tmtktri";
100 }
101 else
102 {
103 bec.attribute = "aims_bucket";
104 bec.global_index_attribute = "bucket_label";
105 bec.local_file_attribute = "bucket_filename";
106 bec.global_filename = "cluster.bck";
107 bec.id = "cluster";
108
109 mec.attribute = "aims_Tmtktri";
110 mec.global_index_attribute = "Tmtktri_label";
111 mec.local_file_attribute = "Tmtktri_filename";
112 mec.global_filename = "Tmtktri.mesh";
113 mec.id = "Tmtktri";
114 }
115 bec.syntax = vertexsyntax;
116 mec.syntax = vertexsyntax;
117
118 Vertex *v;
119
120 gr.setProperty( graphsyntax + "_VERSION", std::string( "1.0" ) );
121 std::vector<float> vs = data->getVoxelSize();
122 gr.setProperty( "voxel_size", vs );
123 std::vector<int> bb;
124 bb.push_back( 0 );
125 bb.push_back( 0 );
126 bb.push_back( 0 );
127 gr.setProperty( "boundingbox_min", bb );
128 bb[0] = data->getSizeX() - 1;
129 bb[1] = data->getSizeY() - 1;
130 bb[2] = data->getSizeZ() - 1;
131 gr.setProperty( "boundingbox_max", bb );
132 std::string outdir = _fileout;
133 std::string::size_type pos = outdir.rfind( '.' );
134 if( pos != std::string::npos )
135 outdir.erase( pos, outdir.length() - pos );
136 pos = outdir.rfind( '/' );
137 if( pos != std::string::npos )
138 outdir.erase( 0, pos + 1 );
139 outdir += ".data";
140 gr.setProperty( "filename_base", outdir );
141 std::vector<int> colors;
142 colors.push_back( 0 );
143 colors.push_back( 255 );
144 colors.push_back( 0 );
145 gr.setProperty( bec.local_file_attribute, colors );
146 gr.setProperty( bec.global_index_attribute, colors );
147
149 objtable( new GraphElementTable );
150 std::map<std::string,GraphElementCode>
151 & cltab = (*objtable)[ vertexsyntax ];
152 cltab[ bec.id ] = bec;
153 if( _domesh )
154 {
155 cltab[ mec.id ] = mec;
156 gr.setProperty( mec.local_file_attribute, colors );
157 gr.setProperty( mec.global_index_attribute, colors );
158 }
159 gr.setProperty( "aims_objects_table", objtable );
160
161 // thresholding
162
163 bool thresh = false;
165
166 if( _upth != FLT_MAX )
167 {
168 thresh = true;
169 if( _lowth != -FLT_MAX )
170 thtype = AIMS_BETWEEN;
171 else
172 {
173 thtype = AIMS_LOWER_OR_EQUAL_TO;
174 _lowth = _upth;
175 _upth = 0;
176 }
177 }
178 else if( _lowth != -FLT_MAX )
179 {
180 thresh = true;
182 _upth = 0;
183 }
184 else if( _binarize )
185 {
186 thresh = true;
187 if( _lowth == -FLT_MAX )
188 {
189 _lowth = 0;
190 thtype = AIMS_GREATER_THAN;
191 }
192 // _upth = 0;
193 }
194
196
197 if( thresh )
198 {
199 AimsThreshold<T, T> th( thtype, (T) _lowth,
200 (T) _upth );
201 std::cout << "threshold: " << _lowth << ", bin: " << _binarize
202 << std::endl;
203 if( _binarize )
204 datat = th.bin( data );
205 else
206 datat = th( data );
207 }
208 else
209 datat = data;
210
211 // connected components
212
213 std::cout << "extracting connected components...\n";
214
215 carto::VolumeRef<short> datac( datat.getSize(), 1 );
217 conv2( true );
218 conv2.convert( datat, datac );
219 datac.fillBorder( -1 );
220
221 // find background level
222 short bg = 0;
223 bool bgfound = false;
224 int x, y, z;
225 std::vector<int> dim = datat->getSize();
226
227 for( z=0; z<dim[2] && !bgfound; ++z )
228 for( y=0; y<dim[1] && !bgfound; ++y )
229 for( x=0; x<dim[0]; ++x )
230 if( datat( x, y, z ) == 0 )
231 {
232 bg = datac( x, y, z );
233 bgfound = true;
234 break;
235 }
236
237 AimsConnectedComponent<short>( datac, _connectivity, bg, false, _minsize,
238 0, 0 );
239 std::cout << "done\n";
240
241 // transform
242
243 Motion motion;
244 if( !_matrix.empty() )
245 {
246 std::cout << "reading transformation...\n";
247 std::ifstream ms( _matrix.c_str() );
248 if( !ms )
249 {
250 std::cerr << "Can't open transformation file " << _matrix
251 << std::endl;
252 throw carto::file_error( _matrix );
253 }
254 std::vector<float> transl( 3 ), rot( 9 );
255 ms >> transl[0] >> transl[1] >> transl[2];
256 ms >> rot[0] >> rot[1] >> rot[2];
257 ms >> rot[3] >> rot[4] >> rot[5];
258 ms >> rot[6] >> rot[7] >> rot[8];
259 if( !ms )
260 {
261 std::cerr << "transformation file " << _matrix << " has wrong format\n";
262 throw carto::file_error( _matrix );
263 }
264 std::cout << "done\n";
265 // translation is in other form: t' = r^-1 * t
267 rm(0,0) = rot[0];
268 rm(0,1) = rot[1];
269 rm(0,2) = rot[2];
270 rm(1,0) = rot[3];
271 rm(1,1) = rot[4];
272 rm(1,2) = rot[5];
273 rm(2,0) = rot[6];
274 rm(2,1) = rot[7];
275 rm(2,2) = rot[8];
276 std::unique_ptr<AffineTransformation3d> mot2 = motion.inverse();
277 rm(0, 3) = transl[0];
278 rm(1, 3) = transl[1];
279 rm(2, 3) = transl[2];
280
281 Point3df t2 = mot2->transform( transl[0], transl[1], transl[2] );
282 transl[0] = t2[0];
283 transl[1] = t2[1];
284 transl[2] = t2[2];
285 gr.setProperty( "Talairach_translation", transl );
286 gr.setProperty( "Talairach_rotation", rot );
287 // Talairach_scale ?
288 }
289
290 // resampling
291 // well, we shouldn't resample anymore!
292
293 // buckets
294
295 std::cout << "converting to buckets...\n";
297 conv( true );
298 BucketMap<Void> bck;
299 conv.convert( datac, bck );
300 std::cout << "done, " << bck.size() << " buckets\n";
301
302 // meshes
303
304 std::map<size_t, AimsSurfaceTriangle> mesh;
305
306 if( _domesh )
307 {
308 std::cout << "meshing components...\n";
309 std::map<size_t, std::list<AimsSurfaceTriangle> > meshl;
310 Mesher mesher;
311 mesher.setDecimation( 100.0, _deciMaxClearance, _deciMaxError, 120.0 );
312 mesher.setMinFacetNumber( _minFacetNumber );
313 mesher.setSmoothing( Mesher::LAPLACIAN, 5, 0.4 );
314 mesher.setSmoothingLaplacian( 180. );
315 mesher.doit( datac, meshl );
316 std::cout << "done\n";
317 //if( onenode )
318 //{
319 std::cout << "regrouping meshes...\n";
320 std::map<size_t, std::list<AimsSurfaceTriangle> >::iterator
321 im, em = meshl.end();
322 for( im=meshl.begin(); im!=em; ++im )
323 {
324 //std::cout << "* " << im->first << ", " << im->second.size();
325 AimsSurfaceTriangle & mesh2 = mesh[im->first];
326 //im->second.erase( im->second.begin() );
327 SurfaceManip::meshMerge( mesh2, im->second );
328 //std::cout << " -> " << mesh2.vertex().size() << " vertices\n";
329 }
330 std::cout << "done\n";
331 //}
332 }
333
334 // vertices
335
337 BucketMap<Void>::iterator ib, eb = bck.end();
338 char num[50];
339 float voxvol = bck.sizeX() * bck.sizeY() * bck.sizeZ();
340 int index = 0;
342 Point3d pt;
343 BucketMap<Void>::Bucket::iterator ibb, ebb;
344 std::vector<float> pf(3);
345 Point3df pm;
346
347 std::cout << "making cluster vertices...\n";
348 for( ib=bck.begin(); ib!=eb; ++ib )
349 {
350 std::cout << "cluster " << ib->first << "..." << std::endl;
351 v = gr.addVertex( vertexsyntax );
352 b = new BucketMap<Void>;
353 b->setSizeXYZT( bck.sizeX(), bck.sizeY(), bck.sizeZ(), bck.sizeT() );
354 (*b)[0] = ib->second;
355 v->setProperty( bec.attribute, carto::rc_ptr<BucketMap<Void> >( b ) );
356 v->setProperty( "index", (int) ib->first );
357 pt = ib->second.begin()->first;
358 sprintf( num, "%d", (int) datat( pt[0], pt[1], pt[2] ) ); // label
359 v->setProperty( "name", std::string( num ) );
360 v->setProperty( "point_number", (int) ib->second.size() );
361 v->setProperty( "size", voxvol * ib->second.size() );
362 if( gtype == Fold )
363 {
364 v->setProperty( "skeleton_label", index++ );
365 v->setProperty( "ss_point_number", (int) ib->second.size() );
366 v->setProperty( "bottom_point_number", (int) 0 );
367 }
368 if( _domesh )
369 {
370 s = new AimsSurfaceTriangle( mesh[ ib->first ] );
371 v->setProperty( mec.attribute,
373 }
374 // cluster center
375 pf[0] = pf[1] = pf[2] = 0;
376 BucketMap<Void>::Bucket & bk = b->begin()->second;
377 for( ibb=bk.begin(), ebb=bk.end(); ibb!=ebb; ++ibb )
378 {
379 pf[0] += ibb->first[0];
380 pf[1] += ibb->first[1];
381 pf[2] += ibb->first[2];
382 }
383 pf[0] = vs[0] * pf[0] / bk.size();
384 pf[1] = vs[1] * pf[1] / bk.size();
385 pf[2] = vs[2] * pf[2] / bk.size();
386 v->setProperty( "gravity_center", pf );
387 // in talairach space
388 pm = motion.transform( pf[0], pf[1], pf[2] );
389 pf[0] = pm[0];
390 pf[1] = pm[1];
391 pf[2] = pm[2];
392 if( gtype == Fold )
393 v->setProperty( "refgravity_center", pf );
394 else
395 v->setProperty( "Talairach(mm)", pf );
396 sprintf( num, "%f %f %f", pf[0], pf[1], pf[2] );
397 v->setProperty( "label", std::string( num ) );
398 }
399 std::cout << "clusters created\n";
400
401 // Relations ?
402 }
403
404}
405
406#endif
carto::VolumeRef< U > bin(const carto::VolumeRef< T > &sqv)
Vertex * addVertex(const std::string &s="")
void doit(const carto::rc_ptr< carto::Volume< short > > &thing, const std::string &name, const std::string &mode="binar")
@ LAPLACIAN
Definition mesher.h:57
void setSmoothing(SmoothingType smoothType, int smoothIt, float smoothRate)
Smoothing.
void setDecimation(float deciReductionRate, float deciMaxClearance, float deciMaxError, float deciFeatureAngle)
Decimation.
void setMinFacetNumber(uint m)
Definition mesher.h:189
void setSmoothingLaplacian(float smoothFeatureAngle)
std::unique_ptr< AffineTransformation3d > inverse() const
float sizeZ() const
void setSizeXYZT(float sizex, float sizey, float sizez, float sizet)
std::map< Point3d, T, BucketMapLess > Bucket
float sizeT() const
float sizeY() const
std::map< int, Bucket >::iterator iterator
float sizeX() const
void make(Graph &gr, const carto::rc_ptr< carto::Volume< T > > &data)
static void meshMerge(AimsTimeSurface< D, T > &dst, const AimsTimeSurface< D, T > &add)
virtual void convert(const INP &in, OUTP &out) const
void convert(const INP &in, OUTP &out) const
void fillBorder(const T &value)
std::vector< int > getSize() const
Point3dd transform(double x, double y, double z) const
std::map< std::string, std::map< std::string, GraphElementCode > > GraphElementTable
void AimsConnectedComponent(carto::VolumeRef< T > data, aims::Connectivity::Type connectivity, std::map< T, size_t > &valids, const T &backgrnd=0, bool bin=true, size_t minSize=0, size_t maxSize=0, size_t numMax=0, bool verbose=true)
aims::AffineTransformation3d Motion
std::string global_filename
std::string global_index_attribute
std::string local_file_attribute
AIMSDATA_API AimsTimeSurface< 3, Void > AimsSurfaceTriangle
threshold_t
AIMS_LOWER_OR_EQUAL_TO
AIMS_GREATER_OR_EQUAL_TO
AIMS_BETWEEN
AIMS_GREATER_THAN
AimsVector< float, 3 > Point3df
AimsVector< int16_t, 3 > Point3d