r117: *** empty log message ***
[ctsim.git] / src / phm2if.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **   Name:          phm2if.cpp
5 **   Purpose:       Convert an phantom object to an image file
6 **   Programmer:    Kevin Rosenberg
7 **   Date Started:  1984
8 **
9 **  This is part of the CTSim program
10 **  Copyright (C) 1983-2000 Kevin Rosenberg
11 **
12 **  $Id: phm2if.cpp,v 1.13 2000/06/22 10:17:28 kevin Exp $
13 **
14 **  This program is free software; you can redistribute it and/or modify
15 **  it under the terms of the GNU General Public License (version 2) as
16 **  published by the Free Software Foundation.
17 **
18 **  This program is distributed in the hope that it will be useful,
19 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
20 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 **  GNU General Public License for more details.
22 **
23 **  You should have received a copy of the GNU General Public License
24 **  along with this program; if not, write to the Free Software
25 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26 ******************************************************************************/
27
28 #include "ct.h"
29 #include "timer.h"
30
31
32 enum { O_PHANTOM, O_DESC, O_NSAMPLE, O_FILTER, O_TRACE, O_VERBOSE, O_HELP, 
33        O_PHMFILE, O_FILTER_DOMAIN, O_FILTER_BW, O_FILTER_PARAM, O_DEBUG, O_VERSION };
34
35 static struct option my_options[] = 
36 {
37   {"phantom", 1, 0, O_PHANTOM},
38   {"phmfile", 1, 0, O_PHMFILE},
39   {"desc", 1, 0, O_DESC},
40   {"nsample", 1, 0, O_NSAMPLE},
41   {"filter", 1, 0, O_FILTER},
42   {"filter-domain", 1, 0, O_FILTER_DOMAIN},
43   {"filter-bw", 1, 0, O_FILTER_BW},
44   {"filter-param", 1, 0, O_FILTER_PARAM},
45   {"trace", 1, 0, O_TRACE},
46   {"verbose", 0, 0, O_VERBOSE},
47   {"debug", 0, 0, O_DEBUG},
48   {"help", 0, 0, O_HELP},
49   {"version", 0, 0, O_VERSION},
50   {0, 0, 0, 0}
51 };
52
53 void 
54 phm2if_usage (const char *program)
55 {
56   cout << "phm2if_usage: " << fileBasename(program) << " outfile nx ny [--phantom phantom-name] [--phmfile filename] [--filter filter-name] [OPTIONS]" << endl;
57   cout << "Generate phantom image from a predefined --phantom or a --phmfile" << endl;
58   cout << endl;
59   cout << "     outfile         Name of output file for image" << endl;
60   cout << "     nx              Number of pixels X-axis" << endl;
61   cout << "     ny              Number of pixels Y-axis" << endl;
62   cout << "     --phantom       Phantom to use for projection" << endl;
63   cout << "        herman       Herman head phantom" << endl;
64   cout << "        bherman      Bordered Herman head phantom" << endl;
65   cout << "        rowland      Rowland head phantom" << endl;
66   cout << "        browland     Bordered Rowland head phantom" << endl;
67   cout << "        unitpulse    Unit pulse phantom" << endl;
68   cout << "     --phmfile       Generate Phantom from phantom file" << endl;
69   cout << "     --filter        Generate Phantom from a filter function" << endl;
70   cout << "       abs_bandlimit Abs * Bandlimiting" << endl;
71   cout << "       abs_sinc      Abs * Sinc" << endl;
72   cout << "       abs_cos       Abs * Cosine" << endl;
73   cout << "       abs_hamming   Abs * Hamming" << endl;
74   cout << "       shepp         Shepp-Logan" << endl;
75   cout << "       bandlimit     Bandlimiting" << endl;
76   cout << "       sinc          Sinc" << endl;
77   cout << "       cos           Cosine" << endl;
78   cout << "       triangle      Triangle" << endl;
79   cout << "       hamming       Hamming" << endl;
80   cout << "     --filter-param  Alpha level for Hamming filter" << endl;
81   cout << "     --filter-domain Set domain of filter" << endl;
82   cout << "         spatial     Spatial domain (default)" << endl;
83   cout << "         freq        Frequency domain" << endl;
84   cout << "     --filter-bw     Filter bandwidth (default = 1)" << endl;
85   cout << "     --desc          Description of raysum" << endl;
86   cout << "     --nsample       Number of samples per axis per pixel (default = 1)" << endl;
87   cout << "     --trace         Trace level to use" << endl;
88   cout << "        none         No tracing (default)" << endl;
89   cout << "        text         Trace text level" << endl;
90   cout << "        phm          Trace phantom" << endl;
91   cout << "        rays         Trace rays" << endl;
92   cout << "        plot         Trace plot" << endl;
93   cout << "        clipping     Trace clipping" << endl;
94   cout << "     --debug         Debug mode" << endl;
95   cout << "     --verbose       Verbose mode" << endl;
96   cout << "     --version       Print version" << endl;
97   cout << "     --help          Print this help message" << endl;
98 }
99
100 #ifdef HAVE_MPI
101 void mpi_gather_image (MPIWorld& mpiWorld, ImageFile* imGlobal, ImageFile* imLocal, const int opt_debug);
102 #endif
103
104 int 
105 phm2if_main (int argc, char* argv[])
106 {
107   ImageFile* imGlobal = NULL;
108   Phantom phm;
109   int opt_nx = 0, opt_ny = 0;
110   int opt_nsample = 1;
111   string optPhmName;
112   string optFilterName;
113   string optDomainName = "spatial";
114   char *opt_outfile = NULL;
115   int opt_debug = 0;
116   string opt_desc;
117   string opt_phmfilename;
118   char *endstr, *endptr;
119   double opt_filter_param = 1;
120   double opt_filter_bw = 1.;
121   int opt_trace = TRACE_NONE;
122   bool opt_verbose = false;
123 #ifdef HAVE_MPI
124   ImageFile* imLocal = NULL;
125   MPIWorld mpiWorld (argc, argv);
126 #endif
127
128   Timer timerProgram;
129   
130 #ifdef HAVE_MPI
131   if (mpiWorld.getRank() == 0) {
132 #endif
133     while (1) {
134       int c = getopt_long(argc, argv, "", my_options, NULL);
135       char *endptr = NULL;
136       char *endstr;
137       
138       if (c == -1)
139         break;
140       
141       switch (c) {
142       case O_PHANTOM:
143         optPhmName = optarg;
144         if (! phm.createFromPhantom (optPhmName.c_str())) {
145           cout << "Invalid phantom name " << optPhmName << endl << endl;
146           phm2if_usage(argv[0]);
147           return (1);
148         }
149         break;
150       case O_PHMFILE:
151         opt_phmfilename = optarg;
152         phm.createFromFile(opt_phmfilename.c_str());
153 #ifdef HAVE_MPI
154         if (mpiWorld.getRank() == 0) 
155           cerr << "Can't use phantom from file in MPI mode" << endl;
156         return (1);
157 #endif
158         break;
159       case O_VERBOSE:
160         opt_verbose = true;
161         break;
162       case O_DEBUG:
163         opt_debug = 1;
164         break;
165       case O_TRACE:
166         if ((opt_trace = opt_set_trace(optarg)) < 0) {
167           phm2if_usage(argv[0]);
168           return (1);
169         }
170         break;
171       case O_FILTER:
172         optFilterName = optarg;
173         break;
174       case O_FILTER_DOMAIN:
175         optDomainName = optarg;
176         break;
177       case O_DESC:
178         opt_desc =  optarg;
179         break;
180       case O_FILTER_BW:
181         opt_filter_bw = strtod(optarg, &endptr);
182         endstr = optarg + strlen(optarg);
183         if (endptr != endstr) {
184           sys_error(ERR_SEVERE,"Error setting --filter-bw to %s\n", optarg);
185           phm2if_usage(argv[0]);
186           return (1);
187         }
188         break;
189       case O_FILTER_PARAM:
190         opt_filter_param = strtod(optarg, &endptr);
191         endstr = optarg + strlen(optarg);
192         if (endptr != endstr) {
193           sys_error(ERR_SEVERE,"Error setting --filter-param to %s\n", optarg);
194           phm2if_usage(argv[0]);
195           return (1);
196         }
197         break;
198       case O_NSAMPLE:
199         opt_nsample = strtol(optarg, &endptr, 10);
200         endstr = optarg + strlen(optarg);
201         if (endptr != endstr) {
202           sys_error(ERR_SEVERE,"Error setting --nsample to %s\n", optarg);
203           phm2if_usage(argv[0]);
204           return (1);
205         }
206         break;
207         case O_VERSION:
208 #ifdef VERSION
209           cout <<  "Version " << VERSION << endl;
210 #else
211           cerr << "Unknown version number" << endl;
212 #endif
213       case O_HELP:
214       case '?':
215         phm2if_usage(argv[0]);
216         return (0);
217       default:
218         phm2if_usage(argv[0]);
219         return (1);
220       }
221     }
222     
223     if (phm.nPElem() == 0 && optPhmName == "" && optFilterName == "") {
224       cerr << "No phantom defined" << endl;
225       phm2if_usage(argv[0]);
226       return (1);
227     }
228
229     if (optind + 3 != argc) {
230       phm2if_usage(argv[0]);
231       return (1);
232     }
233     opt_outfile = argv[optind];
234     opt_nx = strtol(argv[optind+1], &endptr, 10);
235     endstr = argv[optind+1] + strlen(argv[optind+1]);
236     if (endptr != endstr) {
237       sys_error(ERR_SEVERE,"Error setting nx to %s\n", argv[optind+1]);
238       phm2if_usage(argv[0]);
239       return (1);
240     }
241     opt_ny = strtol(argv[optind+2], &endptr, 10);
242     endstr = argv[optind+2] + strlen(argv[optind+2]);
243     if (endptr != endstr) {
244       sys_error(ERR_SEVERE,"Error setting ny to %s\n", argv[optind+2]);
245       phm2if_usage(argv[0]);
246       return (1);
247     }
248     
249     ostringstream oss;
250     oss << "nx=" << opt_nx << ", ny=" << opt_ny << ", nsample=" << opt_nsample << ", ";
251     if (opt_phmfilename.length())
252       oss << "phantom=" << opt_phmfilename;
253     else if (optPhmName != "")
254       oss << "phantom=" << optPhmName;
255     else if (optFilterName != "") {
256       oss << "filter=" << optFilterName << " - " << optDomainName;
257     }
258     if (opt_desc.length())
259       oss << ": " << opt_desc;
260     opt_desc = oss.str();
261     
262     if (opt_verbose)
263       cout << "Rasterize Phantom to Image" << endl << endl;
264 #ifdef HAVE_MPI
265   }
266 #endif
267
268 #ifdef HAVE_MPI
269   TimerCollectiveMPI timerBcast (mpiWorld.getComm());
270   mpiWorld.BcastString (optPhmName);
271   mpiWorld.getComm().Bcast (&opt_verbose, 1, MPI::INT, 0);
272   mpiWorld.getComm().Bcast (&opt_debug, 1, MPI::INT, 0);
273   mpiWorld.getComm().Bcast (&opt_trace, 1, MPI::INT, 0);
274   mpiWorld.getComm().Bcast (&opt_nx, 1, MPI::INT, 0);
275   mpiWorld.getComm().Bcast (&opt_ny, 1, MPI::INT, 0);
276   mpiWorld.getComm().Bcast (&opt_nsample, 1, MPI::INT, 0);
277   mpiWorld.getComm().Bcast (&opt_filter_param, 1, MPI::DOUBLE, 0);
278   mpiWorld.getComm().Bcast (&opt_filter_bw, 1, MPI::DOUBLE, 0);
279
280   mpiWorld.BcastString (optFilterName);
281   mpiWorld.BcastString (optDomainName);
282
283   if (opt_verbose)
284     timerBcast.timerEndAndReport ("Time to broadcast variables");
285
286   mpiWorld.setTotalWorkUnits (opt_nx);
287
288   if (mpiWorld.getRank() > 0 && optPhmName != "")
289       phm.createFromPhantom (optPhmName.c_str());
290
291   if (mpiWorld.getRank() == 0) {
292     imGlobal = new ImageFile (opt_outfile, opt_nx, opt_ny);
293     imGlobal->fileCreate();
294   }
295   imLocal = new ImageFile (opt_nx, opt_ny);
296 #else
297   imGlobal = new ImageFile (opt_outfile, opt_nx, opt_ny);
298   imGlobal->fileCreate ();
299 #endif
300
301   ImageFileArray v;
302 #ifdef HAVE_MPI
303   if (mpiWorld.getRank() == 0)
304     v = imGlobal->getArray ();
305
306   if (phm.getComposition() == P_UNIT_PULSE) {
307     if (mpiWorld.getRank() == 0) {
308       v[opt_nx/2][opt_ny/2] = 1.;
309     }
310   } else if (optFilterName != "") {
311     if (mpiWorld.getRank() == 0) {
312       image_filter_response (*imGlobal, optDomainName.c_str(), opt_filter_bw, optFilterName.c_str(), opt_filter_param, opt_trace);
313     }
314   } else {
315     TimerCollectiveMPI timerRasterize (mpiWorld.getComm());
316     phm.convertToImagefile (*imLocal, opt_nsample, opt_trace, mpiWorld.getMyStartWorkUnit(), mpiWorld.getMyLocalWorkUnits());
317     if (opt_verbose)
318       timerRasterize.timerEndAndReport ("Time to rasterize phantom");
319
320     TimerCollectiveMPI timerGather (mpiWorld.getComm());
321     mpi_gather_image (mpiWorld, imGlobal, imLocal, opt_debug);
322     if (opt_verbose)
323       timerGather.timerEndAndReport ("Time to gather image");
324   }
325 #else
326   v = imGlobal->getArray ();
327   if (phm.getComposition() == P_UNIT_PULSE) {
328     v[opt_nx/2][opt_ny/2] = 1.;
329   } else if (optFilterName != "") {
330     image_filter_response (*imGlobal, optDomainName.c_str(), opt_filter_bw, optFilterName.c_str(), opt_filter_param, opt_trace);
331   } else {
332 #if HAVE_SGP
333       if (opt_trace >= TRACE_PHM)
334         phm.show();
335 #endif
336       phm.convertToImagefile (*imGlobal, opt_nsample, opt_trace);
337   }
338 #endif
339
340 #ifdef HAVE_MPI
341   if (mpiWorld.getRank() == 0) 
342 #endif
343   {
344     imGlobal->arrayDataWrite ();
345     double calctime = timerProgram.timerEnd ();
346     imGlobal->labelAdd (Array2dFileLabel::L_HISTORY, opt_desc.c_str(), calctime);
347     imGlobal->fileClose ();
348     if (opt_verbose)
349       cout << "Time to rasterized phantom: " << calctime << " seconds" << endl;
350
351     if (opt_trace >= TRACE_PHM) {
352       double dmin, dmax;
353       int nscale;
354       
355       printf ("Enter display size scale (nominal = 1): ");
356       scanf ("%d", &nscale);
357       printf ("Enter minimum and maximum densities (min, max): ");
358       scanf ("%lf %lf", &dmin, &dmax);
359       image_display_scale (*imGlobal, nscale, dmin, dmax);
360     }
361   }
362
363   return (0);
364 }
365
366
367
368 #ifdef HAVE_MPI
369 void mpi_gather_image (MPIWorld& mpiWorld, ImageFile* imGlobal, ImageFile* imLocal, const int opt_debug)
370 {
371   ImageFileArray vLocal = imLocal->getArray();
372   ImageFileArray vGlobal = NULL;
373   int nyLocal = imLocal->ny();
374
375   if (mpiWorld.getRank() == 0)
376     vGlobal = imGlobal->getArray();
377   
378   for (int iw = 0; iw < mpiWorld.getMyLocalWorkUnits(); iw++)
379     mpiWorld.getComm().Send(vLocal[iw], nyLocal, imLocal->getMPIDataType(), 0, 0);
380
381   if (mpiWorld.getRank() == 0) {
382     for (int iProc = 0; iProc < mpiWorld.getNumProcessors(); iProc++) {
383       for (int iw = mpiWorld.getStartWorkUnit(iProc); iw <= mpiWorld.getEndWorkUnit(iProc); iw++) {
384         MPI::Status status;
385         mpiWorld.getComm().Recv(vGlobal[iw], nyLocal, imLocal->getMPIDataType(), iProc, 0, status);
386       }
387     }
388   }
389
390 }
391 #endif
392
393 #ifndef NO_MAIN
394 int 
395 main (int argc, char* argv[])
396 {
397   int retval = 1;
398
399   try {
400     retval = phm2if_main(argc, argv);
401   } catch (exception e) {
402     cerr << "Exception: " << e.what() << endl;
403   } catch (...) {
404     cerr << "Unknown exception" << endl;
405   }
406
407   return (retval);
408 }
409 #endif