Statistiques
| Révision :

root / src / lsm_cells.cpp @ 28

Historique | Voir | Annoter | Télécharger (16,08 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
 Need CImg.h and lsm_lib.h
8

9
To execute :
10
 ./lsm_cells img img_wat img_contour erosion
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
*/
18

    
19
#include <iostream>
20
#include <math.h>
21
#include <sstream>
22
#include <vector>
23
#include <fstream>
24
#include <omp.h>
25

    
26
#include "CImg.h"
27
#include "lsm_lib.h"
28

    
29
using namespace cimg_library;
30
using namespace std;
31

    
32

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

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

    
85
  //crop wat image to the size of box, make binary
86
  CImg<unsigned short>binary=wat.get_crop(xmin,ymin,zmin,0,xmax,ymax,zmax,0);
87
  cimg_forXYZ(binary,x,y,z)
88
    {
89
      if(binary(x,y,z)==indice){binary(x,y,z)=1;}
90
      else {binary(x,y,z)=0;}
91
    }
92

    
93
  //erode binary but not completely (vol stay >0)
94
  int vol=binary.sum();
95
  int nb_ero=0;
96
  while((vol>0)and(nb_ero<abs(erosion)))
97
  {
98
    CImg<unsigned char> binary_erode=binary.get_erode(3,3,3); 
99
        if (erosion<0) 
100
        {
101
                for(int i=0;i<3;i++)
102
                {
103
                binary=binary.get_dilate(2,2,2);
104
                }
105
                binary_erode=binary;
106
        }
107
      vol=binary_erode.sum();
108
      if(vol>0)
109
        {
110
          binary=binary_erode;
111
          nb_ero+=1;
112
        }
113
    }
114

    
115
  //initalize psi
116
  CImg<float>psi=binary;
117
  cimg_forXYZ(psi,x,y,z)
118
    {
119
      if(binary(x,y,z)==0){psi(x,y,z)=c0;}
120
      else {psi(x,y,z)=-c0;}
121
    }
122
  return psi;
123
}
124

    
125

    
126
//LSM segment : edge detection
127
//---------------------------------------------------------------------
128
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)
129
{
130
  int timestep_max=2000;
131
  int evolution_min=1;
132
  //int evolution_max=-1;
133
  int it=0;
134
  int it_stop=0;
135
  bool contour_evolve=true;
136
  int backsegm=0;
137
  CImg<float> psi_old=psi;
138
  cimg_forXYZ(psi,x,y,z)
139
    {
140
      if(psi(x,y,z)>=0){backsegm+=1;}
141
    }
142

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

    
175

    
176
      it+=1;
177
      backsegm=new_backsegm;
178
    }
179
  return psi;
180
}
181

    
182

    
183
//Reconstruct full segmented image from cell's images
184
//---------------------------------------------------------------
185
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)
186
{
187
  CImg<unsigned short> res=background;
188
  for(int i=0;i<nbcells;i++)
189
    {
190
      cimg_forXYZ(psi_list[i],xb,yb,zb)
191
        {
192
          int x=xb+min_list[i][0];
193
          int y=yb+min_list[i][1];
194
          int z=zb+min_list[i][2];
195
          if(psi_list[i](xb,yb,zb)>=0)
196
            {res(x,y,z)=list[i];}
197
        }
198
    }
199
  return res;
200
}
201

    
202
//Reconstruct segmented image from cell's images and treat overlap
203
//---------------------------------------------------------------
204
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)
205
{
206
  CImg<unsigned short> res=background;
207
  free=background;
208
  for(int i=0;i<nbcells;i++)
209
    {
210
      cimg_forXYZ(psi_list[i],xb,yb,zb)
211
        {
212
          int x=xb+min_list[i][0];
213
          int y=yb+min_list[i][1];
214
          int z=zb+min_list[i][2];
215
          if(psi_list[i](xb,yb,zb)>=0)
216
            {
217
              res(x,y,z)=list[i];
218
              free(x,y,z)+=1;
219
            }
220
        }
221
    }
222
  cimg_forXYZ(free,x,y,z)
223
    {
224
      if(free(x,y,z)>1)
225
        {
226
          if(background(x,y,z)==1)
227
            {
228
              free(x,y,z)=1;
229
              res(x,y,z)=1;
230
            }
231
          else
232
            {
233
              free(x,y,z)=0;
234
              res(x,y,z)=0;
235
            }
236
        }
237
    }
238
  return res;
239
}
240

    
241

    
242
//-----------------------------------------------------------------------------------------------
243
//******************************************  MAIN **********************************************
244
//-----------------------------------------------------------------------------------------------
245

    
246
int main (int argc, char* argv[])
247
{
248
  double begin=omp_get_wtime();
249

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

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

    
354

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

    
404
  //numerical parameters
405
  float epsilon=0.5;
406
  int dt=1;   //it was 50, changed into 1 the 2015/10/16
407
  float mu=0.1/dt;
408
  int timestep_max=10000; // it was 1000, changed into 10000 the 2015/10/16
409

    
410

    
411
  //------------------------------------Names and directories
412
  //new name with arguments
413
  filename.insert(filename.size()-4,insert);
414

    
415
  //create directories and update names
416
  size_t test=filename.rfind("/");
417
  if(test!=filename.npos)
418
    {filename.erase(0,test+1);}
419
  string outputdir=filename;
420
  outputdir.erase(filename.size()-4);
421
  string mkdir="mkdir -p "+outputdir;
422
  if(system(mkdir.c_str())); 
423

    
424
  string filename_txt=outputdir+"/"+filename;
425
  filename_txt.erase(filename_txt.size()-4);
426
  filename=outputdir+"/"+filename;
427
  string wat_eroded_name=filename;
428
  wat_eroded_name.insert(filename.size()-4,"_eroded");
429
  string edge_detection_name=filename;
430
  edge_detection_name.insert(filename.size()-4,"_evoEdge");
431
  string edge_evolve_name=filename;
432
  edge_evolve_name.insert(filename.size()-4,"_final");
433

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

    
463
  //-----------------------------------------Image Pre-processing
464

    
465
  //smooth image
466
  file<<"smooth : "<<smooth<<endl;
467
  if (smooth>0)
468
  {
469
  img.blur(smooth);
470
  }
471

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

    
504
#pragma omp parallel shared(wat,marge,erosion,c0,psi_list,min_list,list)
505
  {
506
  int xmin=0;
507
  int ymin=0;
508
  int zmin=0;
509
#pragma omp for schedule(dynamic)
510
  for(int i=0;i<nbcells;i++) 
511
    {
512
      int ind=list[i];
513
      psi_list[i]=box_cell(wat,marge,erosion,c0,ind,xmin,ymin,zmin);
514
      min_list[i][0]=xmin;
515
      min_list[i][1]=ymin;
516
      min_list[i][2]=zmin;
517
      cout <<"cell "<<ind<<" initialised."<<endl;
518
    }
519
  }
520
  wat.assign();
521

    
522
  //reconstruct image of eroded cells
523
  CImg<unsigned short> wat_eroded=reconstruct(background,psi_list,min_list,nbcells,list);
524
  cout <<"saving file "<<wat_eroded_name<<"..."<<endl;
525
  wat_eroded.save_inr(wat_eroded_name.c_str(),tailleVoxel);
526
  string zip="gzip -f "+wat_eroded_name;
527
  if(system(zip.c_str()));
528
  cout <<"Eroded watershed segmentation saved in file:"<<wat_eroded_name<<endl;
529

    
530
  //Segmented inital = background segmentation
531
  CImg<unsigned char> segmented=background;
532

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

    
549
  //reconstruct image of edge detection, overlap=not segmented
550
  CImg<unsigned char>free=background;
551
  CImg<unsigned short> edge=reconstruct_overlap(background,psi_list,min_list,nbcells,free,list);
552
  edge.save_inr(edge_detection_name.c_str(),tailleVoxel);
553
  zip="gzip -f "+edge_detection_name;
554
  if(system(zip.c_str()));
555

    
556
// time measurements
557
  double end2=omp_get_wtime();
558
  double time2=double(end2-begin);
559
  
560
  double end=omp_get_wtime();
561
  double time=double(end-begin);
562
  cout<<"total time : "<<time<<"s"<<" (~"<<time/60<<" mn  ~"<<time/60/60<<" h)"<<endl;
563
  cout<<"-initialization with erosion : "<<time1<<"s"<<endl;
564
  cout<<"-edge detection : "<<time2<<"s"<<endl;
565
  file<<"total time : "<<time<<"s"<<" (~"<<time/60<<" mn  ~"<<time/60/60<<" h)"<<endl;
566
  file<<"-initialization with erosion : "<<time1<<"s"<<endl;
567
  file<<"-edge detection : "<<time2<<"s"<<endl;
568
  file.close();
569

    
570
  return 0;
571
}