Statistiques
| Révision :

root / src / lsm_cells.cpp @ 27

Historique | Voir | Annoter | Télécharger (17,17 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) and (bg_evolution>=evolution_max))
165
      if((it>10) and (bg_evolution<=evolution_min) )
166
        {
167
          it_stop+=1;
168
          if(it_stop>3)
169
            {contour_evolve=false;
170
                cout<<"stopit "<<endl;}
171
        }
172
      else
173
        {
174
          it_stop=0;
175
        }
176

    
177

    
178
      it+=1;
179
      backsegm=new_backsegm;
180
      //cout<<"backsegm= "<<backsegm<<endl;     
181
    }
182

    
183
//  cimg_forXYZ(psi,x,y,z)
184
//    {
185
//      if(psi(x,y,z)>=0){psi(x,y,z)=4;}
186
//      else{psi(x,y,z)=-4;}
187
//    }
188

    
189
  return psi;
190
}
191

    
192

    
193
//Reconstruct full segmented image from cell's images
194
//---------------------------------------------------------------
195
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)
196
{
197
  CImg<unsigned short> res=background;
198
  for(int i=0;i<nbcells;i++)
199
    {
200
      cimg_forXYZ(psi_list[i],xb,yb,zb)
201
        {
202
          int x=xb+min_list[i][0];
203
          int y=yb+min_list[i][1];
204
          int z=zb+min_list[i][2];
205
          if(psi_list[i](xb,yb,zb)>=0)
206
            {res(x,y,z)=list[i];}
207
        }
208
    }
209
  return res;
210
}
211

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

    
251

    
252
//-----------------------------------------------------------------------------------------------
253
//******************************************  MAIN **********************************************
254
//-----------------------------------------------------------------------------------------------
255

    
256
int main (int argc, char* argv[])
257
{
258
  double begin=omp_get_wtime();
259

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

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

    
365

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

    
415
  //numerical parameters
416
  float epsilon=0.5;
417
  int dt=1;   //it was 50, changed into 1 the 2015/10/16
418
  float mu=0.1/dt;
419
  int timestep_max=10000; // it was 1000, changed into 10000 the 2015/10/16
420

    
421

    
422
  //------------------------------------Names and directories
423
  //new name with arguments
424
  filename.insert(filename.size()-4,insert);
425

    
426
  //create directories and update names
427
  size_t test=filename.rfind("/");
428
  if(test!=filename.npos)
429
    {filename.erase(0,test+1);}
430
  string outputdir=filename;
431
  outputdir.erase(filename.size()-4);
432
  string mkdir="mkdir -p "+outputdir;
433
  int syst=system(mkdir.c_str()); 
434
  mkdir="mkdir -p "+outputdir+"/evolution";
435
  syst=system(mkdir.c_str());
436

    
437
  string filename_cut=outputdir+"/evolution/"+filename;
438
  filename_cut.erase(filename_cut.size()-4);
439
  filename=outputdir+"/"+filename;
440
  string wat_eroded_name=filename;
441
  wat_eroded_name.insert(filename.size()-4,"_eroded");
442
  string edge_detection_name=filename;
443
  edge_detection_name.insert(filename.size()-4,"_evoEdge");
444
  string edge_evolve_name=filename;
445
  edge_evolve_name.insert(filename.size()-4,"_final");
446

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

    
476
  //-----------------------------------------Image Pre-processing
477
  //define cut
478
  int cut=img._depth/2;
479
  cut = 82;
480
  cout <<"cut z= "<<cut<<endl;
481
  file <<"\ncut z= "<<cut<<endl;
482
  CImg<float> imgCut=img.get_crop(0,0,cut,0,img._width,img._height,cut,0);
483
  imgCut.save((filename_cut+".png").c_str());
484

    
485
  //smooth image
486
  file<<"smooth : "<<smooth<<endl;
487
  if (smooth>0)
488
  {
489
  img.blur(smooth);
490
  }
491

    
492
  //-------------------------------------Initialization with erosion
493
  //compute fixed terms
494
  CImg<float> g;
495
  if(lsm_type.compare("g")==0)
496
    {
497
  g=edge_indicator1(img, gamma);
498
    }
499
  else if (lsm_type.compare("h")==0)
500
   {
501
  g=edge_indicator2s(img, gamma);
502
   }
503
  else if (lsm_type.compare("i")==0)
504
   {
505
  g=edge_indicator3(img, gamma);
506
   }
507
  else
508
  cout<<"Wrong lsm type given :'"<<lsm_type<<"'('g' for gradient-based, 'h' for Hessien-based) "<<endl;
509
 
510
  CImgList<float> gg=gradient(g);
511
  CImg<float> h=g;
512
  img.assign();
513

    
514
  
515
  CImg<float> gout=g;
516
  gout.crop(0,0,cut,0,g._width,g._height,cut,0);
517
  gout.normalize(0,255).save((filename_cut+"_edge_indicator.png").c_str());
518
  gout.assign();
519
  
520
  //initialize psi for every cell
521
  int maxcells=wat.max()+1; //indice maximum  
522
  vector<int> list=index(wat,maxcells);
523
  int nbcells=list.size();
524
  cout<<"number of cells : "<<nbcells<<endl;
525
  file<<"number of cells : "<<nbcells<<endl;
526
  vector<CImg<float> > psi_list(nbcells);
527
  vector<vector<int> > min_list(nbcells,vector<int>(3,0));
528
  vector<vector<int> > max_list(nbcells,vector<int>(3,0));
529

    
530
#pragma omp parallel shared(wat,marge,erosion,c0,psi_list,min_list,list)
531
  {
532
  int xmin=0;
533
  int ymin=0;
534
  int zmin=0;
535
#pragma omp for schedule(dynamic)
536
  for(int i=0;i<nbcells;i++) 
537
    {
538
      int ind=list[i];
539
      psi_list[i]=box_cell(wat,marge,erosion,c0,ind,xmin,ymin,zmin);
540
      min_list[i][0]=xmin;
541
      min_list[i][1]=ymin;
542
      min_list[i][2]=zmin;
543
      cout <<"cell "<<ind<<endl;
544
    }
545
  }
546
  wat.assign();
547

    
548
  //reconstruct image of eroded cell
549
  CImg<unsigned short> wat_eroded=reconstruct(background,psi_list,min_list,nbcells,list);
550
  wat_eroded.save_inr(wat_eroded_name.c_str(),tailleVoxel);
551
  string zip="gzip -f "+wat_eroded_name;
552
  syst=system(zip.c_str());
553
  wat_eroded.crop(0,0,cut,0,wat_eroded._width,wat_eroded._height,cut,0);
554
  wat_eroded.normalize(0,255).save((filename_cut+"_eroded.png").c_str());
555
  wat_eroded.assign();
556

    
557
  //Segmented inital = background segmentation
558
  CImg<unsigned char> segmented=background;
559

    
560
  double end1=omp_get_wtime();
561
  double time1=double(end1-begin);
562
  cout<<"initialization with erosion : "<<time1<<endl;
563
  file<<"\ninitialization with erosion : "<<time1<<endl;
564
  cout<<"Evolving cells..... "<<endl;
565
  //---------------------------------------------------------Edge detection
566
  //evolve each cell one by one, attract to maximal gradient
567
#pragma omp parallel shared(psi_list,min_list,g,gg,h,lam,mu,alf,beta,epsilon,dt,list)
568
  {
569
#pragma omp for schedule(dynamic)
570
  for(int i=0;i<nbcells;i++) 
571
    {
572
      psi_list[i]=lsm_segment2(psi_list[i],g,gg,h,lam,mu,alf,beta,epsilon,dt,min_list[i]);
573
      cout <<"cell evolution "<<list[i]<<endl;
574
    }
575
  }
576

    
577
  //reconstruct image of edge detection, overlap=not segmented
578
  CImg<unsigned char>free=background;
579
  CImg<unsigned short> edge=reconstruct_overlap(background,psi_list,min_list,nbcells,free,list);
580
  edge.save_inr(edge_detection_name.c_str(),tailleVoxel);
581
  zip="gzip -f "+edge_detection_name;
582
  syst=system(zip.c_str());
583
  edge.crop(0,0,cut,0,edge._width,edge._height,cut,0);
584
  edge.normalize(0,255).save((filename_cut+"_evoEdge.png").c_str());
585
  
586

    
587

    
588
  double end2=omp_get_wtime();
589
  double time2=double(end2-begin);
590
  cout<<"edge detection : "<<time2<<endl;
591
  file<<"edge detection : "<<time2<<endl;
592

    
593

    
594
  double end=omp_get_wtime();
595
  double time=double(end-begin);
596
  cout<<"total time : "<<time<<" (~"<<time/60<<" mn  ~"<<time/60/60<<" h)"<<endl;
597
  cout<<"initialization with erosion : "<<time1<<endl;
598
  cout<<"edge detection : "<<time2<<endl;
599
  file<<"total time : "<<time<<" (~"<<time/60<<" mn  ~"<<time/60/60<<" h)"<<endl;
600
  file<<"initialization with erosion : "<<time1<<endl;
601
  file<<"edge detection : "<<time2<<endl;
602
  file.close();
603

    
604
  return 0;
605
}