Statistiques
| Révision :

root / src / lsm_cells.cpp @ 32

Historique | Voir | Annoter | Télécharger (16,54 ko)

1
/*
2
Level-set Method to detect cell's contours
3
Parallel standard version
4

5
To compile :
6
 g++ -o lsm_cells lsm_cells.cpp -O2 -L/usr/X11R6/lib -lm -lpthread -lX11 -fopenmp
7
 Needs CImg.h and lsm_lib.h
8

9
To execute :
10
 ./lsm_cells img img_wat img_contour erosion a b gamma smooth type
11

12
 image in .inr or .inr.gz, save in .inr.gz
13
 img : grayscale image of cells (unsigned char)
14
 img_wat : watershed 16bits image (unsigned short) with background label 1 and cells labels >1
15
 img_contour : binary image with background=1, all cells=0 (unsigned char)
16
 erosion : amount of erosion for each cell in watershed image (int)
17
 a : area term (float) --> 0 or 0.5 or 1 (the default is 0.5)
18
             if negative, the object retracts
19
             if positive, the object inflates
20
 b : curvature term (float) --> 0 or 1 (the default is 0)
21
 gamma : scale parameter (float>0) --> 0.5 or 1 (the default is 1)
22
 smooth : gaussian blur to apply to the image (int) --> 0 or 1 (the default is 0)
23
 lsm_type : image, gradient or hessien based evolution --> 'i', 'g' or 'h' (the default is g)
24
*/
25

    
26
#include <iostream>
27
#include <math.h>
28
#include <sstream>
29
#include <vector>
30
#include <fstream>
31
#include <omp.h>
32

    
33
#include "CImg.h"
34
#include "lsm_lib.h"
35

    
36
using namespace cimg_library;
37
using namespace std;
38

    
39

    
40
//Return list of cell label
41
//---------------------------------------------------------------------
42
vector<int> index(const CImg<unsigned short> & wat, int nbcells)
43
{
44
  vector<int> list;
45
  vector<bool> test(nbcells,false);
46
  cimg_forXYZ(wat,x,y,z)
47
    {
48
      int ind=wat(x,y,z);
49
      if((test[ind]==false)and(ind!=1)and(ind!=0))
50
        {
51
          list.push_back(ind);
52
          test[ind]=true;
53
        }
54
    }
55
  return list;
56
}
57

    
58
//Return crop psi image and it's min coordinates for one cell 
59
//--------------------------------------------------------------------
60
CImg<float> box_cell(const CImg<unsigned short> & wat, int marge,int erosion, int c0,int indice, int &xmin, int &ymin, int &zmin)
61
{
62
  //search box (min and max coordinates with a marge)
63
  xmin=wat._width;
64
  ymin=wat._height;
65
  zmin=wat._depth;
66
  int xmax=0;
67
  int ymax=0;
68
  int zmax=0;
69
  cimg_forXYZ(wat,x,y,z)
70
    {
71
      if(wat(x,y,z)==indice)
72
        {
73
          if(x>xmax){xmax=x;}else if(x<xmin){xmin=x;}
74
          if(y>ymax){ymax=y;}else if(y<ymin){ymin=y;}
75
          if(z>zmax){zmax=z;}else if(z<zmin){zmin=z;}
76
        }
77
    }
78
  //marge and border conditions
79
  xmin=xmin-marge;
80
  if(xmin<0){xmin=0;}
81
  ymin=ymin-marge;
82
  if(ymin<0){ymin=0;}
83
  zmin=zmin-marge;
84
  if(zmin<0){zmin=0;}
85
  xmax=xmax+marge;
86
  if(xmax>=wat._width){xmax=wat._width-1;}
87
  ymax=ymax+marge;
88
  if(ymax>=wat._height){ymax=wat._height-1;}
89
  zmax=zmax+marge;
90
  if(zmax>=wat._depth){zmax=wat._depth-1;}
91

    
92
  //crop wat image to the size of box, make binary
93
  CImg<unsigned short>binary=wat.get_crop(xmin,ymin,zmin,0,xmax,ymax,zmax,0);
94
  cimg_forXYZ(binary,x,y,z)
95
    {
96
      if(binary(x,y,z)==indice){binary(x,y,z)=1;}
97
      else {binary(x,y,z)=0;}
98
    }
99

    
100
  //erode binary but not completely (vol stay >0)
101
  int vol=binary.sum();
102
  int nb_ero=0;
103
  while((vol>0)and(nb_ero<abs(erosion)))
104
  {
105
    CImg<unsigned char> binary_erode=binary.get_erode(3,3,3); 
106
        if (erosion<0) 
107
        {
108
                for(int i=0;i<3;i++)
109
                {
110
                binary=binary.get_dilate(2,2,2);
111
                }
112
                binary_erode=binary;
113
        }
114
      vol=binary_erode.sum();
115
      if(vol>0)
116
        {
117
          binary=binary_erode;
118
          nb_ero+=1;
119
        }
120
    }
121

    
122
  //initalize psi
123
  CImg<float>psi=binary;
124
  cimg_forXYZ(psi,x,y,z)
125
    {
126
      if(binary(x,y,z)==0){psi(x,y,z)=c0;}
127
      else {psi(x,y,z)=-c0;}
128
    }
129
  return psi;
130
}
131

    
132

    
133
//LSM segment : edge detection
134
//---------------------------------------------------------------------
135
CImg<float> lsm_segment2(CImg<float> psi,CImg<float> const & g,CImgList<float> const & gg,CImg<float> const & h, int lam, float mu,float alf, float beta, float epsilon, int dt, vector<int> min_list)
136
{
137
  int timestep_max=2000;
138
  int evolution_min=1;
139
  //int evolution_max=-1;
140
  int it=0;
141
  int it_stop=0;
142
  bool contour_evolve=true;
143
  int backsegm=0;
144
  CImg<float> psi_old=psi;
145
  cimg_forXYZ(psi,x,y,z)
146
    {
147
      if(psi(x,y,z)>=0){backsegm+=1;}
148
    }
149

    
150
  while((it<timestep_max)and(contour_evolve==true)and(backsegm>30))
151
    {
152
      psi_old=psi;
153
      //evolution
154
      psi=evolution_AK2_contour_cells(psi,g,gg,h,lam,mu,alf,beta,epsilon,dt,min_list);
155
      //new segmentation
156
      int new_backsegm=0;
157
      cimg_forXYZ(psi,x,y,z)
158
        {
159
          if(psi(x,y,z)>=0){new_backsegm+=1;}
160
        }
161
          
162
      //Stop criteria
163
      // * if the cell would disappear, we stop
164
      if(new_backsegm==0)
165
                        {psi=psi_old;
166
                     contour_evolve=false;}
167
      
168
      int bg_evolution=abs(new_backsegm-backsegm);
169
        
170
      // * if the evolution is less then evolution_min 3 consecutive times
171
      if((it>10) and (bg_evolution<=evolution_min) )
172
        {
173
          it_stop+=1;
174
          if(it_stop>3)
175
            {contour_evolve=false;}
176
        }
177
      else
178
        {
179
          it_stop=0;
180
        }
181

    
182

    
183
      it+=1;
184
      backsegm=new_backsegm;
185
    }
186
  return psi;
187
}
188

    
189

    
190
//Reconstruct full segmented image from cell's images
191
//---------------------------------------------------------------
192
CImg<unsigned short> reconstruct(const CImg<unsigned char> & background,const vector<CImg<float> > & psi_list, const vector< vector<int> > & min_list, int nbcells,const vector<int> & list)
193
{
194
  CImg<unsigned short> res=background;
195
  for(int i=0;i<nbcells;i++)
196
    {
197
      cimg_forXYZ(psi_list[i],xb,yb,zb)
198
        {
199
          int x=xb+min_list[i][0];
200
          int y=yb+min_list[i][1];
201
          int z=zb+min_list[i][2];
202
          if(psi_list[i](xb,yb,zb)>=0)
203
            {res(x,y,z)=list[i];}
204
        }
205
    }
206
  return res;
207
}
208

    
209
//Reconstruct segmented image from cell's images and treat overlap
210
//---------------------------------------------------------------
211
CImg<unsigned short> reconstruct_overlap(CImg<unsigned char> const &  background,const vector<CImg<float> > & psi_list, const vector< vector<int> > & min_list, int nbcells, CImg<unsigned char> &free, const vector<int> & list)
212
{
213
  CImg<unsigned short> res=background;
214
  free=background;
215
  for(int i=0;i<nbcells;i++)
216
    {
217
      cimg_forXYZ(psi_list[i],xb,yb,zb)
218
        {
219
          int x=xb+min_list[i][0];
220
          int y=yb+min_list[i][1];
221
          int z=zb+min_list[i][2];
222
          if(psi_list[i](xb,yb,zb)>=0)
223
            {
224
              res(x,y,z)=list[i];
225
              free(x,y,z)+=1;
226
            }
227
        }
228
    }
229
  cimg_forXYZ(free,x,y,z)
230
    {
231
      if(free(x,y,z)>1)
232
        {
233
          if(background(x,y,z)==1)
234
            {
235
              free(x,y,z)=1;
236
              res(x,y,z)=1;
237
            }
238
          else
239
            {
240
              free(x,y,z)=0;
241
              res(x,y,z)=0;
242
            }
243
        }
244
    }
245
  return res;
246
}
247

    
248

    
249
//-----------------------------------------------------------------------------------------------
250
//******************************************  MAIN **********************************************
251
//-----------------------------------------------------------------------------------------------
252

    
253
int main (int argc, char* argv[])
254
{
255
  double begin=omp_get_wtime();
256

    
257
  if(argc<5)
258
    {
259
    cout<<"!! wrong number of arguments ("<<argc<<")"<<endl;
260
    cout<<"Usage : lsm_cells img img_wat img_contour erosion [a b smooth lsm_type]"<<endl;
261
    cout<<"----------------- "<<endl;  
262
        cout<<"img : grayscale image of cells, (.inr or .inr.gz)"<<endl;
263
        cout<<"img_wat : image of seeds, (.inr or .inr.gz)"<<endl;
264
        cout<<"img_contour : mask, where cells do not evolve, (.inr or .inr.gz)"<<endl;
265
        cout<<"              if 'None', then cells can evolve on the whole image"<<endl;
266
        cout<<"erosion : amount of erosion of seeds for initialisation (uint8) --> -2, 0, 2"<<endl;
267
        cout<<"              if 0, then no erosion or dilation"<<endl;
268
        cout<<"              if negative, then a dilation is performed"<<endl;
269
        cout<<"a : area term (float) --> 0 or 0.5 or 1 (the default is 0.5)"<<endl;
270
        cout<<"              if negative, the object retracts"<<endl;
271
        cout<<"              if positive, the object inflates"<<endl;
272
        cout<<"b : curvature term (float) --> 0 or 1 (the default is 0)"<<endl;
273
        cout<<"gamma : scale parameter (float>0) --> 0.5 or 1 (the default is 1)"<<endl;
274
        cout<<"smooth : gaussian blur to apply to the image (int) --> 0 or 1 (the default is 0)"<<endl;
275
    cout<<"lsm_type : image, gradient or hessien based evolution --> 'i', 'g' or 'h' (the default is g)"<<endl;  
276
      return 0;
277
    }
278

    
279
  //----------------------------------------------read images and check the names
280
  //Original image
281
  CImg<char> description;
282
  float tailleVoxel[3] = {0}; // resolution initialisation
283
  
284
  bool gzipped=false;
285
  
286
  string filename_img=argv[1];
287
  CImg<unsigned char> img_prev;
288
  if(filename_img.compare(filename_img.size()-4,4,".inr")==0)
289
    {
290
      img_prev.load(filename_img.c_str());
291
      img_prev.get_load_inr(filename_img.c_str(),tailleVoxel); // reads resolution
292
    }
293
  else if(filename_img.compare(filename_img.size()-7,7,".inr.gz")==0)
294
    {
295
          gzipped = true;
296
      string oldname = filename_img;
297
      filename_img.erase(filename_img.size()-3);
298
      string zip="gunzip -c "+oldname+" > "+filename_img;
299
      if(system(zip.c_str())); // decompress image file
300
      img_prev.load(filename_img.c_str());
301
      img_prev.get_load_inr(filename_img.c_str(),tailleVoxel); // reads resolution
302
      zip="rm "+filename_img;
303
      if(system(zip.c_str())); //removes decompressed image  
304
    }
305
  else
306
    {cout<<"!! wrong file extension : "<<filename_img<<endl;
307
      return 0;}
308
  CImg<float> img=img_prev;
309
  img_prev.assign();
310
  cout<<"original image : "<<filename_img<<endl;
311
  cout<<"size : "<<img.size()<<endl;
312
  cout<<"size of original image : "<< img._width<<' '<< img._height <<' '<< img._depth<<' '<< endl;
313
  
314
  //Watershed
315
  string filename=argv[2];
316
  CImg<unsigned short> wat;
317
  if(filename.compare(filename.size()-4,4,".inr")==0)
318
    {
319
      wat.load(filename.c_str());
320
    }
321
  else if(filename.compare(filename.size()-7,7,".inr.gz")==0)
322
    {
323
      wat.load_gzip_external(filename.c_str());
324
      filename.erase(filename.size()-3);
325
    }
326
  else
327
    {cout<<"!! wrong file extension : "<<filename<<endl;
328
      return 0;}
329
  cout<<"watershed image : "<<filename<<endl;
330
cout<<"size : "<<wat.size()<<endl;
331
  cout<<"size of original image : "<< wat._width<<' '<< wat._height <<' '<< wat._depth<<' '<< endl;
332
  
333
  //Background
334
  string filename_bg=argv[3];
335
  CImg<unsigned char> background(img._width, img._height, img._depth,1,0);
336
   if(filename_bg.compare("None")==0)
337
    {
338
       cout<<"!! no background entered !!"<<endl;
339
    }
340
    else if(filename_bg.compare(filename_bg.size()-4,4,".inr")==0)
341
    {
342
      background.load(filename_bg.c_str());
343
    }
344
  else if(filename_bg.compare(filename_bg.size()-7,7,".inr.gz")==0)
345
    {
346
      background.load_gzip_external(filename_bg.c_str());
347
      filename_bg.erase(filename_bg.size()-3);
348
    }
349
  else
350
    {cout<<"!! wrong file extension : "<<filename_bg<<endl;
351
      return 0;}
352
  cout<<"background image : "<<filename_bg<<endl;
353
  
354
  if((wat.size()!=img.size())or(background.size()!=img.size()))
355
    {
356
      cout<<"!! images are not the same size"<<endl;
357
      return 0;
358
    }
359
  else cout<<"size : "<<img.size()<<endl;
360

    
361

    
362
  //---------------------------------------------------------------Parameters
363
  //model parameters
364
  
365
  int c0=-4;
366
  int erosion=atoi(argv[4]);
367
  int marge=10; 
368
  
369
  int lam=10;
370
  float alf=0.5;
371
  float beta=0;
372
  float gamma=1;
373
  float smooth=0;
374
  string lsm_type="g";
375
  
376
  string ar=argv[4];
377
  string insert="_cellLSM-d"+ar;
378
  if(argc>5)
379
    {
380
    alf=atof(argv[5]);
381
    ar=argv[5];
382
    insert=insert+"-a"+ar;
383
    }
384
  if(argc>6)
385
    {
386
    beta=atof(argv[6]);
387
    ar=argv[6];
388
    insert+="-b"+ar;
389
    }
390
  if(argc>7)
391
    {
392
    gamma=atof(argv[7]);
393
    ar=argv[7];
394
    insert+="-g"+ar;
395
        }
396
  if(argc>8)
397
    {
398
    smooth=atof(argv[8]);
399
    ar=argv[8];
400
    insert+="-s"+ar;
401
        }
402
  if(argc>9)
403
    {
404
        lsm_type=argv[9];
405
        ar=argv[9];
406
        insert+="-"+ar;
407
        }
408
        
409
  
410

    
411
  //numerical parameters
412
  float epsilon=0.5;
413
  int dt=1;   //it was 50, changed into 1 the 2015/10/16
414
  float mu=0.1/dt;
415
  int timestep_max=10000; // it was 1000, changed into 10000 the 2015/10/16
416

    
417

    
418
  //------------------------------------Names and directories
419
  //new name with arguments
420
  filename.insert(filename.size()-4,insert);
421

    
422
  //create directories and update names
423
  size_t test=filename.rfind("/");
424
  if(test!=filename.npos)
425
    {filename.erase(0,test+1);}
426
  string outputdir=filename;
427
  outputdir.erase(filename.size()-4);
428
  string mkdir="mkdir -p "+outputdir;
429
  if(system(mkdir.c_str())); 
430

    
431
  string filename_txt=outputdir+"/"+filename;
432
  filename_txt.erase(filename_txt.size()-4);
433
  filename=outputdir+"/"+filename;
434
  string wat_eroded_name=filename;
435
  wat_eroded_name.insert(filename.size()-4,"_eroded");
436
  string edge_detection_name=filename;
437
  edge_detection_name.insert(filename.size()-4,"_evoEdge");
438
  string edge_evolve_name=filename;
439
  edge_evolve_name.insert(filename.size()-4,"_final");
440

    
441
  //txt files 
442
  ofstream file;
443
  string txt_name=filename_txt+".txt";
444
  file.open(txt_name.c_str());
445
  file<<argv[0]<<endl;
446
  time_t t;
447
  struct tm * timeinfo;
448
  time(&t);
449
  timeinfo=localtime(&t);
450
  file<<asctime(timeinfo);
451
  file<<"image : "<<argv[1]<<endl;
452
  file<<"watershed : "<<argv[2]<<endl;
453
  file<<"background : "<<argv[3]<<endl;
454
  file<<"_________________________________"<<endl;
455
  file<<"Parameters"<<endl;
456
  file<<"lsm_type : "<<lsm_type<<endl;
457
  file<<"lambda : "<<lam<<endl;
458
  file<<"alpha : "<<alf<<endl;
459
  file<<"beta : "<<beta<<endl;
460
  file<<"gamma :"<<gamma<<endl;
461
  //file<<"alphabis : "<<alfabis<<endl;
462
  //file<<"betabis : "<<betabis<<endl;
463
  file<<"erosion : "<<erosion<<endl;
464
  file<<"marge : "<<marge<<endl;
465
  file<<"epsilon : "<<epsilon <<endl;
466
  file<<"mu : "<<mu <<endl;
467
  file<<"dt : "<<dt <<endl;
468
  file<<"timestep_max : "<<timestep_max <<endl;
469

    
470
  //-----------------------------------------Image Pre-processing
471

    
472
  //smooth image
473
  file<<"smooth : "<<smooth<<endl;
474
  if (smooth>0)
475
  {
476
  img.blur(smooth);
477
  }
478

    
479
  //-------------------------------------Initialization with erosion
480
  //compute fixed terms
481
  CImg<float> g;
482
  if(lsm_type.compare("g")==0)
483
    {
484
  g=edge_indicator1(img, gamma);
485
    }
486
  else if (lsm_type.compare("h")==0)
487
   {
488
  g=edge_indicator2s(img, gamma);
489
   }
490
  else if (lsm_type.compare("i")==0)
491
   {
492
  g=edge_indicator3(img, gamma);
493
   }
494
  else
495
  cout<<"Wrong lsm type given :'"<<lsm_type<<"'('g' for gradient-based, 'h' for Hessien-based) "<<endl;
496
 
497
  CImgList<float> gg=gradient(g);
498
  CImg<float> h=g;
499
  img.assign();
500
  
501
  //initialize psi for every cell
502
  int maxcells=wat.max()+1; //indice maximum  
503
  vector<int> list=index(wat,maxcells);
504
  int nbcells=list.size();
505
  cout<<"number of cells : "<<nbcells<<endl;
506
  file<<"number of cells : "<<nbcells<<endl;
507
  vector<CImg<float> > psi_list(nbcells);
508
  vector<vector<int> > min_list(nbcells,vector<int>(3,0));
509
  vector<vector<int> > max_list(nbcells,vector<int>(3,0));
510

    
511
#pragma omp parallel shared(wat,marge,erosion,c0,psi_list,min_list,list)
512
  {
513
  int xmin=0;
514
  int ymin=0;
515
  int zmin=0;
516
#pragma omp for schedule(dynamic)
517
  for(int i=0;i<nbcells;i++) 
518
    {
519
      int ind=list[i];
520
      psi_list[i]=box_cell(wat,marge,erosion,c0,ind,xmin,ymin,zmin);
521
      min_list[i][0]=xmin;
522
      min_list[i][1]=ymin;
523
      min_list[i][2]=zmin;
524
      cout <<"cell "<<ind<<" initialised."<<endl;
525
    }
526
  }
527
  wat.assign();
528

    
529
  //reconstruct image of eroded cells
530
  CImg<unsigned short> wat_eroded=reconstruct(background,psi_list,min_list,nbcells,list);
531
  cout <<"saving file "<<wat_eroded_name<<"..."<<endl;
532
  wat_eroded.save_inr(wat_eroded_name.c_str(),tailleVoxel);
533
  string zip="gzip -f "+wat_eroded_name;
534
  if(system(zip.c_str()));
535
  cout <<"Eroded watershed segmentation saved in file:"<<wat_eroded_name<<endl;
536

    
537
  //Segmented inital = background segmentation
538
  CImg<unsigned char> segmented=background;
539

    
540
  double end1=omp_get_wtime();
541
  double time1=double(end1-begin);
542
  cout<<"Evolving cells..... "<<endl;
543
  
544
  //---------------------------------------------------------Edge detection
545
  //evolve each cell one by one, attract to maximal gradient
546
#pragma omp parallel shared(psi_list,min_list,g,gg,h,lam,mu,alf,beta,epsilon,dt,list)
547
  {
548
#pragma omp for schedule(dynamic)
549
  for(int i=0;i<nbcells;i++) 
550
    {
551
      psi_list[i]=lsm_segment2(psi_list[i],g,gg,h,lam,mu,alf,beta,epsilon,dt,min_list[i]);
552
      cout <<"cell "<<list[i]<<" evolved."<<endl;
553
    }
554
  }
555

    
556
  //reconstruct image of edge detection, overlap=not segmented
557
  CImg<unsigned char>free=background;
558
  CImg<unsigned short> edge=reconstruct_overlap(background,psi_list,min_list,nbcells,free,list);
559
  edge.save_inr(edge_detection_name.c_str(),tailleVoxel);
560
  zip="gzip -f "+edge_detection_name;
561
  if(system(zip.c_str()));
562

    
563
// time measurements
564
  double end2=omp_get_wtime();
565
  double time2=double(end2-begin);
566
  
567
  double end=omp_get_wtime();
568
  double time=double(end-begin);
569
  cout<<"total time : "<<time<<"s"<<" (~"<<time/60<<" mn  ~"<<time/60/60<<" h)"<<endl;
570
  cout<<"-initialization with erosion : "<<time1<<"s"<<endl;
571
  cout<<"-edge detection : "<<time2<<"s"<<endl;
572
  file<<"total time : "<<time<<"s"<<" (~"<<time/60<<" mn  ~"<<time/60/60<<" h)"<<endl;
573
  file<<"-initialization with erosion : "<<time1<<"s"<<endl;
574
  file<<"-edge detection : "<<time2<<"s"<<endl;
575
  file.close();
576

    
577
  return 0;
578
}