00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
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
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
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
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
00142 mask_stack_ptr.resize(img_stack.size(), 0);
00143 if (USE_MASK) {
00144 if (MFLIP) {
00145
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
00174 for (int level = num_levels - 1; level >= 0; --level) {
00175
00176
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
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
00208 cout << "doing anisotropic blurring" << endl;
00209 Image<double> tmp, diff;
00210 while (level != 0) {
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;
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
00245 if (NUM_REG > 2) {
00246 develop_multiregion_2D(®s[0], regs.size(), reinit_modulo, 0, mask_stack_ptr[level]);
00247 } else {
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
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;
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
00438 if (INIT_SEG_TYPE == 0) {
00439
00440 random_scattered_voronoi(&phi[0], phi.size(), 1);
00441 } else if (INIT_SEG_TYPE < 0) {
00442
00443 random_scattered_voronoi(&phi[0], phi.size(), -INIT_SEG_TYPE);
00444 } else {
00445
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
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 };