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 <fstream>
00035 #include <limits>
00036 #include <iostream>
00037 #include <assert.h>
00038 #include <time.h>
00039 #include <boost/tokenizer.hpp>
00040
00041 #include "Image.h"
00042 #include "Filters.h"
00043 #include "LIC.h"
00044 #include "simple_tools.h"
00045 #include"cimg_dependent.h"
00046
00047 using namespace std;
00048 using namespace lsseg;
00049
00050 const double PI = 3.1415926535897932384;
00051
00052
00054 double DT = 30;
00056 double DL = 0.8;
00058 double ALPHA = 1.2;
00060 double SIGMA = 1;
00062 int NUM_TIMESTEPS = 1;
00065 double P1 = 0.3;
00068 double P2 = 0.7;
00071 double P3 = 0.9;
00073 double L = 2 * sqrt(DT);
00075 const int DIV_THETA = 6;
00077 const double DTHETA = PI / DIV_THETA;
00079 const int DIV_PHI = 6;
00081 const double DPHI = PI / DIV_PHI;
00083 const char SAVED_3D_FILE[] = "greycstoration_result.stack";
00084
00085 double C = double(1) / sqrt(4 * PI * DT);
00086 double D = double(1) / (4 * DT);
00087
00088
00089 void make_vector_field_2D(double angle, const Image<double>& tensor, Image<double>& vec);
00090
00091
00092
00093 void make_vector_field_3D(double phi, double theta, const Image<double>& tensor, Image<double>& vec);
00094
00095
00096
00097 void show_command_info();
00098
00099
00100
00101 void show_current_vars();
00102
00103
00104
00105 void read_command_info(int varnum, char** vararg);
00106
00107
00108
00109 double gauss(double t)
00110
00111 {
00112
00113 const double res = C * exp(-(t*t) * D);
00114 return res;
00115 }
00116
00117
00118 bool refers_to_stack(string str)
00119
00120 {
00121 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
00122 boost::char_separator<char> sep(".");
00123 tokenizer tokens(str, sep);
00124 string saved;
00125 for (tokenizer::iterator it = tokens.begin(); it != tokens.end(); ++it) {
00126 saved = *it;
00127 }
00128 if (saved == string("stack") || saved == string("Stack") || saved == string("STACK")) {
00129 return true;
00130 }
00131 return false;
00132 }
00133
00134
00135 void to_field(const Image<double>& T, Image<double>& W)
00136
00137 {
00138 const int X = T.dimx();
00139 const int Y = T.dimy();
00140 const int Z = T.dimz();
00141 W.resize(X, Y, Z, 4);
00142 for (int z = 0; z < Z; ++z) {
00143 for (int y = 0; y < Y; ++y) {
00144 for (int x = 0; x < X; ++x) {
00145 double wx = T(x, y, z, 0);
00146 double wy = T(x, y, z, 1);
00147 double wz = T(x, y, z, 2);
00148 double norm = sqrt(wx * wx + wy * wy + wz*wz);
00149 double norm_inv = (norm == 0) ? 1 : double(1) / norm;
00150 W(x, y, z, 0) = wx * norm_inv;
00151 W(x, y, z, 1) = wy * norm_inv;
00152 W(x, y, z, 2) = wz * norm_inv;
00153 W(x, y, z, 3) = norm;
00154 }
00155 }
00156 }
00157 }
00158
00159 void make_debug_field(const Image<double>& T, Image<double>& W)
00160 {
00161
00162
00163 const int X = T.dimx();
00164 const int Y = T.dimy();
00165 const int Z = T.dimz();
00166 W.resize(X, Y, Z, 4);
00167 for (int z = 0; z < Z; ++z) {
00168 for (int y = 0; y < Y; ++y) {
00169 for (int x = 0; x < X; ++x) {
00170 double vx = (x - X/2+0.5);
00171 double vy = (y - Y/2 +0.5);
00172 double vz =(z - Z/2 + 0.5);
00173 double norm = sqrt(vx * vx + vy * vy + vz * vz);
00174 vx /= norm;
00175 vy /= norm;
00176 vz /= norm;
00177
00178 W(x, y, z, 0) = vx;
00179 W(x, y, z, 1) = vy;
00180 W(x, y, z, 2) = vz;
00181 W(x, y, z, 3) = 1;
00182 }
00183 }
00184 }
00185
00186 }
00187
00188
00189 int main(int varnum, char** vararg)
00190
00191 {
00192 if (varnum == 1) {
00193 show_command_info();
00194 return -1;
00195 }
00196 read_command_info(varnum, vararg);
00197 cout << "Now running with the following options: " << endl;
00198 show_current_vars();
00199
00200
00201 L = 2 * sqrt(DT);
00202 C = double(1) / sqrt(4 * PI * DT);
00203 D = double(1) / (4 * DT);
00204
00205 clock_t start_time = clock();
00206
00207 Image<double> img;
00208 if (refers_to_stack(vararg[1])) {
00209 cout << "Reading 3D image stack...";
00210 ifstream is(vararg[1]);
00211 if (!is) {
00212 cerr << "Could not open file " << vararg[1] << endl;
00213 return 1;
00214 }
00215 img.read(is);
00216 cout << "Finished!" << endl;
00217 } else {
00218 cout << "Image is 2D " << endl;
00219 load_image(vararg[1], img, false);
00220 }
00221 permanent_display(img);
00222
00223 const int X = img.dimx();
00224 const int Y = img.dimy();
00225 const int Z = img.dimz();
00226 const bool three_dimensional = Z > 1;
00227 const int tensor_components = three_dimensional ? 6 : 3;
00228 const int vector_components = three_dimensional ? 4 : 3;
00229
00230 Image<double> target(img, false);
00231 target = 0;
00232
00233 cout << "Allocating memory.." << endl;
00234 Image<double> G(X, Y, Z, tensor_components);
00235 Image<double> T(X, Y, Z, tensor_components);
00236 Image<double> W(X, Y, Z, vector_components);
00237 Image<double> img_blurred(img, false);
00238 cout << "Finished allocating memory" << endl;
00239 const double norm_fac =
00240 three_dimensional ? double(1) / (DIV_THETA * DIV_PHI) : double(1) / DIV_THETA;
00241
00242 for (int tstep = 0; tstep < NUM_TIMESTEPS; ++tstep) {
00243 img_blurred = img;
00244 cout << "Bluring image" << endl;
00245 blur_image(img_blurred, ALPHA);
00246 cout << "Rescaling image" << endl;
00247 rescale(img_blurred, 0, 255);
00248
00249
00250 if (three_dimensional) {
00251 cout << "Computing structure tensor" << endl;
00252 compute_structure_tensor_3D(img_blurred, G);
00253 cout << "Bluring structure tensor" << endl;
00254 blur_image(G, SIGMA);
00255
00256
00257 cout << "Computing smoothing geometry " << endl;
00258 compute_smoothing_geometry_3D(G, P1, P2, P3, T, true);
00259
00260
00261
00262
00263 for (int phistep = 0; phistep < DIV_PHI; ++phistep) {
00264 for (int thetastep = 0; thetastep < DIV_THETA; ++thetastep) {
00265 const double phi = phistep * DPHI - PI/2;
00266 const double theta = thetastep * DTHETA;
00267 make_vector_field_3D(phi, theta, T, W);
00268
00269
00270 cout << "Convoluting with PHI = " << phi << " and THETA = " << theta << endl;
00271 LIC_3D_FS(img, W, target, &gauss, DL, L);
00272 }
00273 }
00274
00275 target *= norm_fac;
00276 } else {
00277
00278
00279 compute_structure_tensor_2D(img_blurred, G);
00280 blur_image(G, SIGMA);
00281 compute_smoothing_geometry_2D(G, P1, P2, T, true);
00282
00283 for (int a = 0; a < DIV_THETA; ++a) {
00284 const double angle = a * DTHETA;
00285 make_vector_field_2D(angle, T, W);
00286
00287
00288 LIC_2D_FS(img, W, target, &gauss, DL, L);
00289 }
00290 target *= norm_fac;
00291 }
00292
00293 target.swap(img);
00294 }
00295 clock_t end_time = clock();
00296
00297 display_image(img);
00298 if (three_dimensional) {
00299 ofstream os(SAVED_3D_FILE);
00300 if (!os) {
00301 cerr << "Warning: not able to save 3D result because unable to open file." << endl;
00302 } else {
00303 img.write(os);
00304 os.close();
00305 }
00306 }
00307 cout << "Total CPU time was: " << double(end_time - start_time) / 1000 << " millisecs." << endl;
00308 return 1;
00309 };
00310
00311
00312 void make_vector_field_3D(double phi, double theta, const Image<double>& tensor, Image<double>& vec)
00313
00314 {
00315 const double POLE_CORRECTION_FACTOR = sqrt(0.5);
00316 const double TINY = numeric_limits<double>::epsilon();
00317 assert(tensor.numChannels() == 6);
00318 const int X = tensor.dimx();
00319 const int Y = tensor.dimy();
00320 const int Z = tensor.dimz();
00321 const double v_x = cos(phi) * cos(theta);
00322 const double v_y = cos(phi) * sin(theta);
00323 const double v_z = POLE_CORRECTION_FACTOR * sin(phi);
00324 vec.resize(X, Y, Z, 4);
00325 for (int z = 0; z < Z; ++z) {
00326 for (int y = 0; y < Y; ++y) {
00327 for (int x = 0; x < X; ++x) {
00328 const double txx = tensor(x, y, z, 0);
00329 const double txy = tensor(x, y, z, 1);
00330 const double tyy = tensor(x, y, z, 2);
00331 const double txz = tensor(x, y, z, 3);
00332 const double tyz = tensor(x, y, z, 4);
00333 const double tzz = tensor(x, y, z, 5);
00334
00335 const double vx = txx * v_x + txy * v_y + txz * v_z;
00336 const double vy = txy * v_x + tyy * v_y + tyz * v_z;
00337 const double vz = txz * v_x + tyz * v_y + tzz * v_z;
00338 const double norm = sqrt(vx * vx + vy * vy + vz * vz);
00339 const double norm_inv = (norm > TINY) ? double(1) / norm : 0;
00340 vec(x, y, z, 0) = vx * norm_inv;
00341 vec(x, y, z, 1) = vy * norm_inv;
00342 vec(x, y, z, 2) = vz * norm_inv;
00343 vec(x, y, z, 3) = norm;
00344
00345 assert(vec(x, y, z, 0) <= 1 && vec(x, y, z, 0) >= -1);
00346 assert(vec(x, y, z, 1) <= 1 && vec(x, y, z, 1) >= -1);
00347 assert(vec(x, y, z, 2) <= 1 && vec(x, y, z, 2) >= -1);
00348 }
00349 }
00350 }
00351 }
00352
00353
00354 void make_vector_field_2D(double theta, const Image<double>& tensor, Image<double>& vec)
00355
00356 {
00357 assert(tensor.dimz() == 1 && tensor.numChannels() == 3);
00358 const int X = tensor.dimx();
00359 const int Y = tensor.dimy();
00360 const double sin_theta = sin(theta);
00361 const double cos_theta = cos(theta);
00362
00363 vec.resize(X, Y, 1, 3);
00364
00365 for (int y = 0; y < Y; ++y) {
00366 for (int x = 0; x < X; ++x) {
00367 const double a = tensor(x, y, 0, 0);
00368 const double b = tensor(x, y, 0, 1);
00369 const double c = tensor(x, y, 0, 2);
00370 const double vx = a * cos_theta + b * sin_theta;
00371 const double vy = b * cos_theta + c * sin_theta;
00372 const double norm = sqrt(vx*vx + vy * vy);
00373 const double norm_inv = double(1) / norm;
00374 vec(x, y, 0, 0) = vx * norm_inv;
00375 vec(x, y, 0, 1) = vy * norm_inv;
00376 vec(x, y, 0, 2) = norm;
00377 }
00378 }
00379 }
00380
00381
00382
00383 void show_command_info()
00384
00385 {
00386 cerr << "Usage: greycstoration2D <image> <option(s)>" << endl;
00387 cerr << "Where options are: " << endl;
00388 cerr << "-DT <timestep value (pos. double)> -- default: " << DT << endl;
00389 cerr << "-ALPHA <smooth value(pos. double)>" << endl;
00390 cerr << " -> how much to smooth the image prior to computing" << endl;
00391 cerr << " the structure tensor G. -- default: " << ALPHA << endl;
00392 cerr << "-SIGMA <smooth value (pos.double)>" << endl;
00393 cerr << " -> how much to smooth the structure tensor G prior" << endl;
00394 cerr << " to line integral convolution." << endl;
00395 cerr << " -- default: " << SIGMA << endl;
00396 cerr << "-TIMESTEPS <positive integer>" << endl;
00397 cerr << " -> how many timesteps (of length DT) that should be " << endl;
00398 cerr << " computed. -- default: " << NUM_TIMESTEPS << endl;
00399 cerr << "-P1 <fac 1 (pos. double)> " << endl;
00400 cerr << " -> specify how to smooth along the main smoothing direction." << endl;
00401 cerr << " (approx. along isophote). Higher values yield less" << endl;
00402 cerr << " smoothing. -- default: " << P1 << endl;
00403 cerr << "-P2 <fac 2 (pos. double)> " << endl;
00404 cerr << " -> specify how to smooth along the minor smoothing direction. " << endl;
00405 cerr << " (approx. along image gradient). Higher values yield less" << endl;
00406 cerr << " smoothing. Should be higher than P1." << endl;
00407 cerr << " -- default: " << P2 << endl;
00408 cerr << "-P3 <fac 3 (pos. double)> " << endl;
00409 cerr << " -> specify how to smooth along the minor smoothing direction (3D). " << endl;
00410 cerr << " Higher values yield less smoothing. Should be higher than P2." << endl;
00411 cerr << " -- default: " << P3 << endl;
00412 cerr << endl;
00413 cerr << "The <image> argument can be the name of a picture in any of the most common ";
00414 cerr << "formats (jpeg, tiff, png, etc.). In that case, the 2D image will be read ";
00415 cerr << "and treated by the algorithm in a 2D way. You can also give the algorithm ";
00416 cerr << "a stack of images, and it will process this stack as one 3D image. A ";
00417 cerr << "stack file has 'stack' as its suffix, and can be made using the ";
00418 cerr << "'generate_stack' utility program (run it to get an explanation of " << endl;
00419 cerr << "its use. In case a 3D stack is to be processed, the result is ALWAYS saved ";
00420 cerr << "to the file \"" << SAVED_3D_FILE << "\"." << endl;
00421 }
00422
00423
00424 void read_command_info(int varnum, char** vararg)
00425
00426 {
00427 for (int i = 2; i != varnum; i+=2) {
00428 string arg(vararg[i]);
00429 if (arg=="-DT") {
00430 DT = atof(vararg[i+1]);
00431 } else if (arg=="-ALPHA") {
00432 ALPHA = atof(vararg[i+1]);
00433 } else if (arg=="-SIGMA") {
00434 SIGMA = atof(vararg[i+1]);
00435 } else if (arg=="-TIMESTEPS") {
00436 NUM_TIMESTEPS = atoi(vararg[i+1]);
00437 } else if (arg=="-P1") {
00438 P1 = atof(vararg[i+1]);
00439 } else if (arg=="-P2") {
00440 P2 = atof(vararg[i+1]);
00441 } else if (arg=="-P3") {
00442 P3 = atof(vararg[i+1]);
00443 } else {
00444 cerr << "Unrecognized option: " << arg << endl;
00445 exit(-1);
00446 }
00447 }
00448 }
00449
00450
00451 void show_current_vars()
00452
00453 {
00454 cout << "--------------------------" << endl;
00455 cout << "DT: " << DT << endl;
00456 cout << "ALPHA: " << ALPHA << endl;
00457 cout << "SIGMA: " << SIGMA << endl;
00458 cout << "NUM_TIMESTEPS: " << NUM_TIMESTEPS << endl;
00459 cout << "P1: " << P1 << endl;
00460 cout << "P2: " << P2 << endl;
00461 cout << "P3: " << P3 << endl;
00462 cout << "--------------------------" << endl;
00463 }