r144: Initial CVS import
[ctsim.git] / tools / 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.1 2000/07/13 07:01:35 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 = Phantom::PHM_HERMAN_STR;
112   string optFilterName;
113   string optDomainName = SignalFilter::DOMAIN_SPATIAL_STR;
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         break;
145       case O_PHMFILE:
146         opt_phmFileName = optarg;
147         break;
148       case O_VERBOSE:
149         opt_verbose = true;
150         break;
151       case O_DEBUG:
152         opt_debug = 1;
153         break;
154       case O_TRACE:
155         if ((opt_trace = convertTraceNameToID(optarg)) == TRACE_INVALID) {
156           phm2if_usage(argv[0]);
157           return (1);
158         }
159         break;
160       case O_FILTER:
161         optFilterName = optarg;
162         break;
163       case O_FILTER_DOMAIN:
164         optDomainName = optarg;
165         break;
166       case O_DESC:
167         opt_desc =  optarg;
168         break;
169       case O_FILTER_BW:
170         opt_filter_bw = strtod(optarg, &endptr);
171         endstr = optarg + strlen(optarg);
172         if (endptr != endstr) {
173           sys_error(ERR_SEVERE,"Error setting --filter-bw to %s\n", optarg);
174           phm2if_usage(argv[0]);
175           return (1);
176         }
177         break;
178       case O_FILTER_PARAM:
179         opt_filter_param = strtod(optarg, &endptr);
180         endstr = optarg + strlen(optarg);
181         if (endptr != endstr) {
182           sys_error(ERR_SEVERE,"Error setting --filter-param to %s\n", optarg);
183           phm2if_usage(argv[0]);
184           return (1);
185         }
186         break;
187       case O_NSAMPLE:
188         opt_nsample = strtol(optarg, &endptr, 10);
189         endstr = optarg + strlen(optarg);
190         if (endptr != endstr) {
191           sys_error(ERR_SEVERE,"Error setting --nsample to %s\n", optarg);
192           phm2if_usage(argv[0]);
193           return (1);
194         }
195         break;
196         case O_VERSION:
197 #ifdef VERSION
198           cout <<  "Version " << VERSION << endl;
199 #else
200           cerr << "Unknown version number" << endl;
201 #endif
202       case O_HELP:
203       case '?':
204         phm2if_usage(argv[0]);
205         return (0);
206       default:
207         phm2if_usage(argv[0]);
208         return (1);
209       }
210     }
211     
212     if (optPhmName == "" && optFilterName == "" && opt_phmFileName == "") {
213       cerr << "No phantom defined" << endl << endl;
214       phm2if_usage(argv[0]);
215       return (1);
216     }
217
218     if (optind + 3 != argc) {
219       phm2if_usage(argv[0]);
220       return (1);
221     }
222     opt_outfile = argv[optind];
223     opt_nx = strtol(argv[optind+1], &endptr, 10);
224     endstr = argv[optind+1] + strlen(argv[optind+1]);
225     if (endptr != endstr) {
226       sys_error(ERR_SEVERE,"Error setting nx to %s\n", argv[optind+1]);
227       phm2if_usage(argv[0]);
228       return (1);
229     }
230     opt_ny = strtol(argv[optind+2], &endptr, 10);
231     endstr = argv[optind+2] + strlen(argv[optind+2]);
232     if (endptr != endstr) {
233       sys_error(ERR_SEVERE,"Error setting ny to %s\n", argv[optind+2]);
234       phm2if_usage(argv[0]);
235       return (1);
236     }
237     
238     ostringstream oss;
239     oss << "phm2if: nx=" << opt_nx << ", ny=" << opt_ny << ", nsample=" << opt_nsample << ", ";
240     if (opt_phmFileName != "")
241       oss << "phantom=" << opt_phmFileName;
242     else if (optPhmName != "")
243       oss << "phantom=" << optPhmName;
244     else if (optFilterName != "") {
245       oss << "filter=" << optFilterName << " - " << optDomainName;
246     }
247     if (opt_desc != "")
248       oss << ": " << opt_desc;
249     opt_desc = oss.str();
250     
251     if (optPhmName != "") {
252       phm.createFromPhantom (optPhmName.c_str());
253       if (phm.fail()) {
254         cout << phm.failMessage() << endl << endl;
255         phm2if_usage(argv[0]);
256         return (1);
257       }
258     }
259
260     if (opt_phmFileName != "") {
261       phm.createFromFile(opt_phmFileName.c_str());
262 #ifdef HAVE_MPI
263       if (mpiWorld.getRank() == 0) 
264         cerr << "Can't use phantom from file in MPI mode" << endl;
265       return (1);
266 #endif
267     }
268
269     if (opt_verbose)
270       cout << "Rasterize Phantom to Image" << endl << endl;
271 #ifdef HAVE_MPI
272   }
273 #endif
274
275 #ifdef HAVE_MPI
276   TimerCollectiveMPI timerBcast (mpiWorld.getComm());
277   mpiWorld.BcastString (optPhmName);
278   mpiWorld.getComm().Bcast (&opt_verbose, 1, MPI::INT, 0);
279   mpiWorld.getComm().Bcast (&opt_debug, 1, MPI::INT, 0);
280   mpiWorld.getComm().Bcast (&opt_trace, 1, MPI::INT, 0);
281   mpiWorld.getComm().Bcast (&opt_nx, 1, MPI::INT, 0);
282   mpiWorld.getComm().Bcast (&opt_ny, 1, MPI::INT, 0);
283   mpiWorld.getComm().Bcast (&opt_nsample, 1, MPI::INT, 0);
284   mpiWorld.getComm().Bcast (&opt_filter_param, 1, MPI::DOUBLE, 0);
285   mpiWorld.getComm().Bcast (&opt_filter_bw, 1, MPI::DOUBLE, 0);
286
287   mpiWorld.BcastString (optFilterName);
288   mpiWorld.BcastString (optDomainName);
289
290   if (opt_verbose)
291     timerBcast.timerEndAndReport ("Time to broadcast variables");
292
293   mpiWorld.setTotalWorkUnits (opt_nx);
294
295   if (mpiWorld.getRank() > 0 && optPhmName != "")
296       phm.createFromPhantom (optPhmName.c_str());
297
298   if (mpiWorld.getRank() == 0) {
299     imGlobal = new ImageFile (opt_nx, opt_ny);
300   }
301   imLocal = new ImageFile (opt_nx, opt_ny);
302 #else
303   imGlobal = new ImageFile (opt_nx, opt_ny);
304 #endif
305
306   ImageFileArray v;
307 #ifdef HAVE_MPI
308   if (mpiWorld.getRank() == 0)
309     v = imGlobal->getArray ();
310
311   if (phm.getComposition() == P_UNIT_PULSE) {
312     if (mpiWorld.getRank() == 0) {
313       v[opt_nx/2][opt_ny/2] = 1.;
314     }
315   } else if (optFilterName != "") {
316     if (mpiWorld.getRank() == 0) {
317       imGlobal->filterResponse (optDomainName.c_str(), opt_filter_bw, optFilterName.c_str(), opt_filter_param);
318     }
319   } else {
320     TimerCollectiveMPI timerRasterize (mpiWorld.getComm());
321     phm.convertToImagefile (*imLocal, opt_nsample, opt_trace, mpiWorld.getMyStartWorkUnit(), mpiWorld.getMyLocalWorkUnits());
322     if (opt_verbose)
323       timerRasterize.timerEndAndReport ("Time to rasterize phantom");
324
325     TimerCollectiveMPI timerGather (mpiWorld.getComm());
326     mpi_gather_image (mpiWorld, imGlobal, imLocal, opt_debug);
327     if (opt_verbose)
328       timerGather.timerEndAndReport ("Time to gather image");
329   }
330 #else
331   v = imGlobal->getArray ();
332   if (phm.getComposition() == P_UNIT_PULSE) {
333     v[opt_nx/2][opt_ny/2] = 1.;
334   } else if (optFilterName != "") {
335     imGlobal->filterResponse (optDomainName.c_str(), opt_filter_bw, optFilterName.c_str(), opt_filter_param);
336   } else {
337 #if HAVE_SGP
338       if (opt_trace >= TRACE_PHM)
339         phm.show();
340 #endif
341       phm.convertToImagefile (*imGlobal, opt_nsample, opt_trace);
342   }
343 #endif
344
345 #ifdef HAVE_MPI
346   if (mpiWorld.getRank() == 0) 
347 #endif
348   {
349     double calctime = timerProgram.timerEnd ();
350     imGlobal->labelAdd (Array2dFileLabel::L_HISTORY, opt_desc.c_str(), calctime);
351     imGlobal->fileWrite (opt_outfile);
352     if (opt_verbose)
353       cout << "Time to rasterized phantom: " << calctime << " seconds" << endl;
354
355     if (opt_trace >= TRACE_PHM) {
356       double dmin, dmax;
357       int nscale;
358       
359       printf ("Enter display size scale (nominal = 1): ");
360       scanf ("%d", &nscale);
361       printf ("Enter minimum and maximum densities (min, max): ");
362       scanf ("%lf %lf", &dmin, &dmax);
363       imGlobal->displayScaling (nscale, dmin, dmax);
364     }
365   }
366
367   return (0);
368 }
369
370
371
372 #ifdef HAVE_MPI
373 void mpi_gather_image (MPIWorld& mpiWorld, ImageFile* imGlobal, ImageFile* imLocal, const int opt_debug)
374 {
375   ImageFileArray vLocal = imLocal->getArray();
376   ImageFileArray vGlobal = NULL;
377   int nyLocal = imLocal->ny();
378
379   if (mpiWorld.getRank() == 0)
380     vGlobal = imGlobal->getArray();
381   
382   for (int iw = 0; iw < mpiWorld.getMyLocalWorkUnits(); iw++)
383     mpiWorld.getComm().Send(vLocal[iw], nyLocal, imLocal->getMPIDataType(), 0, 0);
384
385   if (mpiWorld.getRank() == 0) {
386     for (int iProc = 0; iProc < mpiWorld.getNumProcessors(); iProc++) {
387       for (int iw = mpiWorld.getStartWorkUnit(iProc); iw <= mpiWorld.getEndWorkUnit(iProc); iw++) {
388         MPI::Status status;
389         mpiWorld.getComm().Recv(vGlobal[iw], nyLocal, imLocal->getMPIDataType(), iProc, 0, status);
390       }
391     }
392   }
393
394 }
395 #endif
396
397 #ifndef NO_MAIN
398 int 
399 main (int argc, char* argv[])
400 {
401   int retval = 1;
402
403   try {
404     retval = phm2if_main(argc, argv);
405   } catch (exception e) {
406     cerr << "Exception: " << e.what() << endl;
407   } catch (...) {
408     cerr << "Unknown exception" << endl;
409   }
410
411   return (retval);
412 }
413 #endif