/home/oan/prosjekt/gotools/segmentation/gpl_distro/lsseg_1.0_gpl/app/segmentation2D.C

Go to the documentation of this file.
00001 //===========================================================================
00002 // The Level-Set Segmentation Library (LSSEG)
00003 //
00004 //
00005 // Copyright (C) 2000-2005 SINTEF ICT, Applied Mathematics, Norway.
00006 //
00007 // This program is free software; you can redistribute it and/or          
00008 // modify it under the terms of the GNU General Public License            
00009 // as published by the Free Software Foundation version 2 of the License. 
00010 //
00011 // This program is distributed in the hope that it will be useful,        
00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of         
00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
00014 // GNU General Public License for more details.                           
00015 //
00016 // You should have received a copy of the GNU General Public License      
00017 // along with this program; if not, write to the Free Software            
00018 // Foundation, Inc.,                                                      
00019 // 59 Temple Place - Suite 330,                                           
00020 // Boston, MA  02111-1307, USA.                                           
00021 //
00022 // Contact information: e-mail: tor.dokken@sintef.no                      
00023 // SINTEF ICT, Department of Applied Mathematics,                         
00024 // P.O. Box 124 Blindern,                                                 
00025 // 0314 Oslo, Norway.                                                     
00026 // 
00027 //
00028 // Other licenses are also available for this software, notably licenses
00029 // for:
00030 // - Building commercial software.                                        
00031 // - Building software whose source code you wish to keep private.        
00032 //
00033 //===========================================================================
00034 #include "LevelSetFunction.h"
00035 #include "NormalDistributionForce.h"
00036 #include "ParzenDistributionForce.h"
00037 #include "SingleRegionAlgorithm.h"
00038 #include "MultiRegionAlgorithm.h"
00039 #include "Region.h"
00040 #include "simple_tools.h"
00041 #include "cimg_dependent.h"
00042 #include "level_set.h"
00043 #include "Filters.h"
00044 #include "image_utils.h"
00045 #include "colordefs.h"
00046 #include <time.h>
00047 #include <string>
00048 #include <assert.h>
00049 #include <stdexcept>
00050 
00051 using namespace std;
00052 using namespace lsseg;
00053 
00055 int LOWEST_RES            = 70; 
00057 double NU_FACTOR          = 1.5; 
00059 double P                  = 1.5; 
00061 double SMOOTHING_FACTOR   = 0.3; 
00063 double DOWNSCALING_FACTOR = sqrt(double(2)); 
00065 int FIRST_ITERATIONS      = 1800;
00067 int LATER_ITERATIONS      = 500; 
00069 int USE_ORIG_IMAGE        = 2; 
00071 int USE_STRUCTURE_TENSOR  = 1; 
00073 int USE_SCALE_INFO        = 1; 
00075 int NUM_REG               = 2; 
00076 
00079 int INIT_SEG_TYPE         = 0;  
00081 bool SHOW_INTERMEDIARY    = true;  
00083 bool SAVE_SEG             = false; 
00085 string SAVE_FILENAME; 
00087 bool USE_MASK = false;
00089 Mask mask_object;   
00091 int MFLIP                 = 0; 
00093 const double SCALE_FAC_DT = 1; 
00095 const double SCALE_FAC_T = 15; 
00097 const int REINIT_MODULO_LOWEST = 100;
00099 const int REINIT_MODULO_OTHER = 20;  
00101 const int ANISOTROPIC_DT = 3;
00103 const int PARZEN_LIMIT = 150 * 100; 
00105 const int TOTAL_NUM_COLORS = 9; 
00106 const int* COLORS[] = {YELLOW, CYAN, MAGENTA, WHITE, GREY, BROWN, GREEN, BLUE, RED, BLACK}; 
00107 
00108 void show_usage();
00109 void parse_command_line(int varnum, char** vararg);
00110 void initialize_levelsets(vector<LevelSetFunction>& phi);
00111 
00112 
00113 //===========================================================================
00114 int main(int varnum, char** vararg)
00115 //===========================================================================
00116 {
00117     if (varnum == 1) {
00118         show_usage();
00119         return 0;
00120     }
00121     parse_command_line(varnum, vararg);
00122     
00123     // loading image
00124     Image<double> img;
00125     const bool convert_to_greyscale = USE_ORIG_IMAGE != 2;
00126     load_image(vararg[1], img, convert_to_greyscale);
00127 
00128     // generating and initializing level-set-functions
00129     const bool multireg = NUM_REG > 2;
00130     vector<LevelSetFunction> phi(multireg ? NUM_REG : 1, LevelSetFunction(img.dimx(), img.dimy()));
00131     initialize_levelsets(phi);
00132 
00133     // making multiresolution version of image and level-set functions
00134     vector<Image<double> > img_stack;
00135     vector<Mask> mask_stack;
00136     vector<const Mask*> mask_stack_ptr;
00137     vector<vector<LevelSetFunction> > phi_stack(phi.size());
00138     const bool downsample_z = false;
00139     downsample_series(img, img_stack, LOWEST_RES, downsample_z, DOWNSCALING_FACTOR, false);
00140 
00141     // setting up mask
00142     mask_stack_ptr.resize(img_stack.size(), 0);
00143     if (USE_MASK) {
00144         if (MFLIP) {
00145             // inverting mask
00146             for (char* p = mask_object.begin(); p != mask_object.end(); ++p) {
00147                 *p = (*p) ? 0 : 1;
00148             }
00149         }
00150         downsample_series(mask_object, mask_stack, LOWEST_RES, downsample_z, DOWNSCALING_FACTOR, false);
00151         for (size_t i = 0; i != mask_stack.size(); ++i) {
00152             mask_stack_ptr[i] = &(mask_stack[i]);
00153         }
00154     }  
00155 
00156     for (size_t i = 0; i != phi.size(); ++i) {
00157         downsample_series(phi[i], phi_stack[i], LOWEST_RES, downsample_z, DOWNSCALING_FACTOR, false);
00158     }
00159     cout << "Downsampling finished" << endl;
00160 
00161     const int num_levels = img_stack.size();
00162     cout << "Number of levels: " << num_levels << endl;
00163     
00164 
00165     vector<Region> regs(phi.size());
00166     Image<double> multichan;
00167     Image<double> G, S, T_acc;
00168     UpdatableImage status_visualisator(img, "viewer");
00169 
00170     vector<ParzenDistributionForce> parzen_force(phi.size(), ParzenDistributionForce(multireg)); 
00171     vector<NormalDistributionForce> ndist_force(phi.size(), NormalDistributionForce(multireg)); 
00172 
00173     // running segmentation on each level
00174     for (int level = num_levels - 1; level >= 0; --level) {
00175         
00176         // initialize regions
00177         for (int r = 0; r != regs.size(); ++r) {
00178             regs[r].mu = compute_nu_Brox(phi_stack[r][level].size(), NU_FACTOR);
00179             regs[r].phi.swap(phi_stack[r][level]);
00180         }
00181         
00182         // making composite channel image to use for generating driving force
00183         vector<const Image<double>*> ch_vec;
00184         ch_vec.push_back(&img_stack[level]);
00185         Image<double> grayimage(img_stack[level]);
00186         if (grayimage.numChannels() > 1) {
00187             to_grayscale(grayimage); 
00188         }
00189 
00190         if (USE_STRUCTURE_TENSOR) {
00191             compute_structure_tensor_2D(grayimage, G, true);
00192             if (SHOW_INTERMEDIARY) {
00193                 display_image(G);
00194             }
00195             ch_vec.push_back(&G);
00196         } 
00197         if (USE_SCALE_INFO) {
00198             compute_scale_factor_2D(grayimage, S, T_acc, SCALE_FAC_DT, SCALE_FAC_T);
00199             if (SHOW_INTERMEDIARY) {
00200                 display_image(S);
00201             }
00202             ch_vec.push_back(&S);
00203         }
00204 
00205         combine_channel_images(&ch_vec[0], ch_vec.size(), multichan);
00206 
00207         // anisotropic blurring
00208         cout << "doing anisotropic blurring" << endl;
00209         Image<double> tmp, diff;
00210         while (level != 0) { // no smoothing at the highest level
00211             anisotropic_smoothing(multichan, tmp, ANISOTROPIC_DT, 0, P);
00212             diff = tmp;
00213             diff -= multichan;
00214             diff.setAbsolute();
00215             const double average = diff.getAverage();
00216             cout << "average pixel variation: " << average << endl;
00217             multichan.swap(tmp);
00218             if (average < SMOOTHING_FACTOR) {
00219                 break; // this is the only way to get out of here
00220             }
00221         }
00222         rescale_channels(multichan, 0, 255);
00223 
00224         const bool use_parzen = ((multichan.dimx() * multichan.dimy()) >= PARZEN_LIMIT) || (level == 0);
00225         for (int r = 0; r < regs.size(); ++r) {
00226             if (use_parzen) {
00227                 cout << "Using PARZEN distribution on level: " << level << endl;
00228                 regs[r].fgen = &parzen_force[r];
00229             } else {
00230                 cout << "Using NORMAL distribution on level: " << level << endl;
00231                 regs[r].fgen = &ndist_force[r];
00232             }
00233             regs[r].fgen->init(&multichan, mask_stack_ptr[level]);
00234         }
00235         
00236         const int num_iter =      (level == num_levels - 1) ? FIRST_ITERATIONS : LATER_ITERATIONS;
00237         const int reinit_modulo = (level == num_levels - 1) ? REINIT_MODULO_LOWEST : REINIT_MODULO_OTHER;
00238         
00239         Image<double> visualization;
00240         visualization.resize(regs[0].phi);
00241 
00242         for (int i = 0; i < num_iter; i+= reinit_modulo) {
00243             
00244             // develop region(s)
00245             if (NUM_REG > 2) { // multiregion
00246                 develop_multiregion_2D(&regs[0], regs.size(), reinit_modulo, 0, mask_stack_ptr[level]);
00247             } else {           // single region
00248                 develop_single_region_2D(regs[0], reinit_modulo, 0, mask_stack_ptr[level]);
00249             } 
00250             cout << "We are currently at iteration: " << i << ", level " << level << endl;
00251 
00252             if (SHOW_INTERMEDIARY) {
00253                 // display temporary result
00254                 Image<double> tmp(img_stack[level]);
00255                 Image<double> visu(regs[0].phi, false);
00256                 if (NUM_REG > 2) {
00257                     vector<const LevelSetFunction*> phipointers(regs.size());
00258                     for (size_t r = 0; r != regs.size(); ++r) {
00259                         phipointers[r] = &(regs[r].phi);
00260                     }
00261                     const int offset = TOTAL_NUM_COLORS - regs.size();
00262                     visualize_multisets(&phipointers[0], phipointers.size(), visu, COLORS + offset);
00263                 } else {
00264                     visualize_level_set(regs[0].phi, visu, 0);
00265                 }
00266                 if (visu.numChannels() > tmp.numChannels()) {
00267                     visu.swap(tmp);
00268                 }
00269                 tmp.superpose(visu);
00270                 if (mask_stack_ptr[level]) {
00271                     tmp.applyMask(*mask_stack_ptr[level]);
00272                 }
00273                 status_visualisator.update(tmp);
00274             }
00275             
00276             for (size_t r = 0; r != regs.size(); ++r) {
00277                 regs[r].phi.reinitialize2D();
00278             }
00279         }
00280         if (level > 0) {
00281             for (size_t r = 0; r != regs.size(); ++r) {
00282                 resample_into(regs[r].phi, phi_stack[r][level-1]);
00283             }
00284         } else {
00285             for (size_t r = 0; r != regs.size(); ++r) {
00286                 regs[r].phi.swap(phi_stack[r][0]);
00287             }
00288         }
00289     }
00290 
00291     Image<double> visu(phi_stack[0][0], false);
00292     Image<double> tmp(img_stack[0]);
00293     if (NUM_REG > 2) {
00294 
00295         vector<const LevelSetFunction*> phipointers(regs.size());
00296         for (size_t r = 0; r != regs.size(); ++r) {
00297             phipointers[r] = &(phi_stack[r][0]);
00298         }
00299         const int offset = TOTAL_NUM_COLORS - regs.size();
00300         visualize_multisets(&phipointers[0], phipointers.size(), visu, COLORS + offset);
00301 
00302     } else {
00303         visualize_level_set(phi_stack[0][0], visu, 0);
00304     }
00305     tmp.superpose(visu);
00306     display_image(tmp);
00307     if (SAVE_SEG) {
00308         Mask m;
00309         mask_from_segmentation(phi_stack[0][0], m, SEG_NEGATIVE);
00310         for (char* p = m.begin(); p != m.end(); ++p) {
00311             if (*p) {
00312                 *p = 255; // makes it easier to see the mask with standard imaging software
00313             }
00314         }
00315         save_image(SAVE_FILENAME.c_str(), m);
00316     }
00317     return 0;
00318 };
00319 
00320 //===========================================================================
00321 void parse_command_line(int varnum, char** vararg)
00322 //===========================================================================
00323 {
00324     for (int i = 2; i < varnum-1; ++i) {
00325         string opt(vararg[i]);
00326         if (opt == string("-m")) { 
00327             Image<int> tmp;
00328             load_image(vararg[++i], tmp, true);
00329             mask_object = Mask(tmp, true);
00330             USE_MASK = true;
00331         } else if (opt == string("-mflip")) {
00332             const int tmp = atoi(vararg[++i]);
00333             MFLIP = (tmp ? 1 : 0);
00334         } else if (opt == string("-save")) {
00335             SAVE_SEG = true;
00336             SAVE_FILENAME = string(vararg[++i]);
00337             SAVE_FILENAME += string(".png");
00338         } else if (opt == string("-lr")) {
00339             const int tmp = atoi(vararg[++i]);
00340             if (tmp > 0) {
00341                 LOWEST_RES = tmp;
00342             } else {
00343                 cerr << "Ignored invalid value for -lr option (must be positive)" << endl;
00344             }
00345         } else if (opt == string("-n")) {
00346             const float tmp  = atof(vararg[++i]);
00347             if (tmp > 0) {
00348                 NU_FACTOR = tmp; 
00349             } else {
00350                 cerr << "Ignored invalid value for -n option (must be positive)" << endl;
00351             }
00352         } else if (opt == string("-p")) {
00353             const float tmp = atof(vararg[++i]);
00354             if (tmp > 0) {
00355                 P = tmp;
00356             } else {
00357                 cerr << "Ignored invalid value for -p option (must be positive)" << endl;
00358             }
00359         } else if (opt == string("-s")) {
00360             const float tmp = atof(vararg[++i]);
00361             if (tmp > 0) {
00362                 SMOOTHING_FACTOR = tmp;
00363             } else {
00364                 cerr << "Ignored invalid value for -s option (must be positive)" << endl;
00365             }
00366         } else if (opt == string("-d")) {
00367             const float tmp = atof(vararg[++i]);
00368             if (tmp > 1) {
00369                 DOWNSCALING_FACTOR = tmp;
00370             } else {
00371                 cerr << "Ignored invalid value for -d option (must be > 1)" << endl;
00372             }
00373         } else if (opt == string("-i1")) {
00374             const int tmp = atoi(vararg[++i]);
00375             if (tmp > 0) {
00376                 FIRST_ITERATIONS = tmp;
00377             } else {
00378                 cerr << "Ignored invalid value for -i1 option (must be positive)" << endl;
00379             }
00380         } else if (opt == string("-i2")) {
00381             const int tmp = atoi(vararg[++i]);
00382             if (tmp > 0) {
00383                 LATER_ITERATIONS = tmp;
00384             } else {
00385                 cerr << "Ignored invalid value for -i2 option (must be positive)" << endl;
00386             } 
00387         } else if (opt == string("-c")) {
00388             const int tmp = atoi(vararg[++i]);
00389             if (tmp == 0 || tmp == 1 || tmp == 2) {
00390                 USE_ORIG_IMAGE = tmp;           
00391             } else {
00392                 cerr <<" Ignored invalid value for -c option (must be 0, 1 or 2)" << endl;
00393             }
00394         } else if (opt == string("-g")) {
00395             const int tmp = atoi(vararg[++i]);
00396             if (tmp == 0 || tmp == 1) {
00397                 USE_STRUCTURE_TENSOR = tmp;
00398             } else {
00399                 cerr << "Ignored invalid value for -g oiption (must be 0 or 1)" << endl;
00400             }
00401         } else if (opt == string("-scale")) {
00402             const int tmp = atoi(vararg[++i]);
00403             if (tmp == 0 || tmp == 1){
00404                 USE_SCALE_INFO = tmp;
00405             } else {
00406                 cerr << "Ignored invalid value for -scale option (must be 0 or 1)" << endl;
00407             }
00408         } else if (opt == string("-show")) {
00409             const int tmp = atoi(vararg[++i]);
00410             if (tmp == 0 || tmp == 1) {
00411                 SHOW_INTERMEDIARY = tmp;
00412             } else { 
00413                 cerr << "Ignored invalid value for -show option (must be 0 or 1)" << endl;
00414             }
00415         } else if (opt == string("-nr")) {
00416             const int tmp = atoi(vararg[++i]);
00417             if (tmp >= 2 && tmp <= TOTAL_NUM_COLORS) {
00418                 NUM_REG = tmp;
00419             } else {
00420                 cout << "Ignored invalid value for -nr option (must be >= 2 and not more than) " << TOTAL_NUM_COLORS << endl;
00421             }
00422         } else if (opt == string("-init")) {
00423             const int tmp = atoi(vararg[++i]);
00424             INIT_SEG_TYPE = tmp;
00425         } else {
00426             cerr << "Unknown option: " << opt << endl;
00427         }
00428     }
00429 }
00430 
00431 
00432 //===========================================================================
00433 void initialize_levelsets(vector<LevelSetFunction>& phi)
00434 //===========================================================================
00435 {
00436     if (phi.size() > 1) {
00437         // multiregion
00438         if (INIT_SEG_TYPE == 0) {
00439             // initialize using voronoi regions
00440             random_scattered_voronoi(&phi[0], phi.size(), 1);
00441         } else if (INIT_SEG_TYPE < 0) {
00442             // initialize using voronoi regions
00443             random_scattered_voronoi(&phi[0], phi.size(), -INIT_SEG_TYPE);
00444         } else {
00445             // initialize using horizontal bands
00446             const int total_num_bands = phi.size() * INIT_SEG_TYPE;
00447             const int bandwidth = phi[0].dimy() / total_num_bands;
00448             multiregion_bands(&phi[0], phi.size(), bandwidth);
00449         }
00450     } else {
00451         // single region
00452         assert(phi.size() == 1);
00453         if (INIT_SEG_TYPE == 0) {
00454             sphere(phi[0], 0.3, 0.5, 0.5);
00455         } else {
00456             const int num_bands = INIT_SEG_TYPE > 0 ? INIT_SEG_TYPE : -INIT_SEG_TYPE;
00457             horizontal_sinusoidal_bands(phi[0], num_bands);
00458         }
00459     }
00460 }
00461 
00462 //===========================================================================
00463 void show_usage()
00464 //===========================================================================
00465 {
00466     cerr << "This program carries out 2-region or multiregion segmentation of arbitrary images." << endl;
00467     cerr << endl;
00468     cerr << "Usage: segmentation2D <filename> [option list]" << endl;
00469     cerr << endl;
00470     cerr << "Where options are: " << endl;
00471     cerr << endl;
00472     cerr << "-m <mask name> mask to describe active part of image to segment (must have\n";
00473     cerr << "               same resolution as image)  \n";
00474     cerr << "-mflip <bool> decide if the zero or nonzero part of a mask should be the active part\n";
00475     cerr << "              of segmentation.  Only matters if a mask is actually used.  \n";
00476     cerr << "    -- default: " << (MFLIP ? " true " : " false " ) << endl;
00477     cerr << "-lr <positive integer> lowest level resolution\n";
00478     cerr << "    -- default: " << LOWEST_RES << endl;
00479     cerr << "-n <pos. floating-point number> boundary smoothness factor 'nu'\n";
00480     cerr << "    -- default: " << NU_FACTOR << endl;
00481     cerr << "-p <pos. floating-point number> anisotropic factor for pre-blurring\n";
00482     cerr << "    -- default: " << P << endl;
00483     cerr << "-s <pos. floating-point number> level of presmoothing (smaller value meens \n";
00484     cerr << "                                more smoothing)\n";
00485     cerr << "    -- default: " << SMOOTHING_FACTOR << endl;
00486     cerr << "-d <floating-point number greater than 1> downscaling factor\n";
00487     cerr << "    -- default: " << DOWNSCALING_FACTOR << endl;
00488     cerr << "-i1 <pos. integer> number of iterations at lowest level\n";
00489     cerr << "    -- default: " << FIRST_ITERATIONS << endl;
00490     cerr << "-i2 <pos. integer> number of iterations at higher levels\n";
00491     cerr << "    -- default: " << LATER_ITERATIONS << endl;
00492     cerr << "-c <number> 2->use color info, 1->use only grayscale, 0-> use neither\n";
00493     cerr << "    -- default: " << USE_ORIG_IMAGE << endl;
00494     cerr << "-g <bool> use structure tensor information, 1->yes, 0->no \n";
00495     cerr << "    -- default: " << (USE_STRUCTURE_TENSOR ? "use " : "do not use ") << "structure tensor\n";
00496     cerr << "-scale <bool> use scale information, 1->yes, 0->no \n";
00497     cerr << "    -- default: " << (USE_SCALE_INFO ? "use " : "do not use ") << "scale info\n";
00498     cerr << "-show <bool> whether intermediate segmentations should be shown\n";                        
00499     cerr << "    -- default: " << (SHOW_INTERMEDIARY ? "show " : "do not show ") << "intermediary results.\n";
00500     cerr << "-nr <number> number of distinct regions in segmentation (positive integer)\n";
00501     cerr << "    -- default: " << NUM_REG << endl;
00502     cerr << "-init <number> shape of initial segmentation.  The meaning of the number \n";
00503     cerr << "               depends on whether\n";
00504     cerr << "               we are segmenting using TWO regions or SEVERAL regions.\n";
00505     cerr << "               * for TWO regions, we have: \n";
00506     cerr << "                  0       -> centered circle\n";
00507     cerr << "                  n, n>0  -> number of horizontal bands\n";
00508     cerr << "               * for MULTI-regions, we have \n";
00509     cerr << "                  0       -> randomly placed voronoi-type regions\n";
00510     cerr << "                  n, n>0  -> number of horizontal bands\n";
00511     cerr << "                  n, n<0  -> randomly placed voronoi-type fragmented regions,\n";
00512     cerr << "                             where |n| is the number of fragments of each region\n";
00513     cerr << "    -- default: " << INIT_SEG_TYPE << endl;
00514     cerr << "-save <mask filename> Save the segmented region as a mask (image containing only \n";
00515     cerr << "                      white or black pixels).  Only works for 2-region segmentation.\n";
00516     cerr << "                      The mask will be saved as a PNG file, and the extension '.png' will\n";
00517     cerr << "                      be added to the filename given.\n";
00518     cerr << "    -- default: do not save" << endl;
00519     cerr << endl;
00520 };

Generated on Tue Nov 28 18:35:47 2006 for lsseg by  doxygen 1.4.7