r168: *** empty log message ***
[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.5 2000/08/02 18:06:00 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 static const char* szIdStr = "$Id";
54
55 void 
56 phm2if_usage (const char *program)
57 {
58   cout << "phm2if_usage: " << fileBasename(program) << " outfile nx ny [--phantom phantom-name] [--phmfile filename] [--filter filter-name] [OPTIONS]" << endl;
59   cout << "Generate phantom image from a predefined --phantom or a --phmfile" << endl;
60   cout << endl;
61   cout << "     outfile         Name of output file for image" << endl;
62   cout << "     nx              Number of pixels X-axis" << endl;
63   cout << "     ny              Number of pixels Y-axis" << endl;
64   cout << "     --phantom       Phantom to use for projection" << endl;
65   cout << "        herman       Herman head phantom" << endl;
66   cout << "        bherman      Bordered Herman head phantom" << endl;
67   cout << "        rowland      Rowland head phantom" << endl;
68   cout << "        browland     Bordered Rowland head phantom" << endl;
69   cout << "        unitpulse    Unit pulse phantom" << endl;
70   cout << "     --phmfile       Generate Phantom from phantom file" << endl;
71   cout << "     --filter        Generate Phantom from a filter function" << endl;
72   cout << "       abs_bandlimit Abs * Bandlimiting" << endl;
73   cout << "       abs_sinc      Abs * Sinc" << endl;
74   cout << "       abs_cos       Abs * Cosine" << endl;
75   cout << "       abs_hamming   Abs * Hamming" << endl;
76   cout << "       shepp         Shepp-Logan" << endl;
77   cout << "       bandlimit     Bandlimiting" << endl;
78   cout << "       sinc          Sinc" << endl;
79   cout << "       cos           Cosine" << endl;
80   cout << "       triangle      Triangle" << endl;
81   cout << "       hamming       Hamming" << endl;
82   cout << "     --filter-param  Alpha level for Hamming filter" << endl;
83   cout << "     --filter-domain Set domain of filter" << endl;
84   cout << "         spatial     Spatial domain (default)" << endl;
85   cout << "         freq        Frequency domain" << endl;
86   cout << "     --filter-bw     Filter bandwidth (default = 1)" << endl;
87   cout << "     --desc          Description of raysum" << endl;
88   cout << "     --nsample       Number of samples per axis per pixel (default = 1)" << endl;
89   cout << "     --trace         Trace level to use" << endl;
90   cout << "        none         No tracing (default)" << endl;
91   cout << "        text         Trace text level" << endl;
92   cout << "        phm          Trace phantom" << endl;
93   cout << "        rays         Trace rays" << endl;
94   cout << "        plot         Trace plot" << endl;
95   cout << "        clipping     Trace clipping" << endl;
96   cout << "     --debug         Debug mode" << endl;
97   cout << "     --verbose       Verbose mode" << endl;
98   cout << "     --version       Print version" << endl;
99   cout << "     --help          Print this help message" << endl;
100 }
101
102 #ifdef HAVE_MPI
103 void mpi_gather_image (MPIWorld& mpiWorld, ImageFile* pImGlobal, ImageFile* pImLocal, const int optDebug);
104 #endif
105
106 int 
107 phm2if_main (int argc, char* argv[])
108 {
109   ImageFile* pImGlobal = NULL;
110   Phantom phm;
111   string optPhmName (Phantom::convertPhantomIDToName(Phantom::PHM_HERMAN));
112   string optFilterName;
113   string optDomainName (SignalFilter::convertDomainIDToName (SignalFilter::DOMAIN_SPATIAL));
114   string optOutFilename;
115   string optDesc;
116   string optPhmFilename;
117   int opt_nx = 0;
118   int opt_ny = 0;
119   int opt_nsample = 1;
120   double optFilterParam = 1.;
121   double optFilterBW = 1.;
122   int optTrace = TRACE_NONE;
123   bool optVerbose = false;
124   bool optDebug = false;
125   Timer timerProgram;
126
127 #ifdef HAVE_MPI
128   ImageFile* pImLocal = NULL;
129   MPIWorld mpiWorld (argc, argv);
130   if (mpiWorld.getRank() == 0) {
131 #endif
132     while (1) {
133       int c = getopt_long(argc, argv, "", my_options, NULL);
134       char *endptr = NULL;
135       char *endstr;
136       
137       if (c == -1)
138         break;
139       
140       switch (c) {
141       case O_PHANTOM:
142         optPhmName = optarg;
143         break;
144       case O_PHMFILE:
145         optPhmFilename = optarg;
146         break;
147       case O_VERBOSE:
148         optVerbose = true;
149         break;
150       case O_DEBUG:
151         optDebug = true;
152         break;
153       case O_TRACE:
154         if ((optTrace = TraceLevel::convertTraceNameToID(optarg)) == TRACE_INVALID) {
155           phm2if_usage(argv[0]);
156           return (1);
157         }
158         break;
159       case O_FILTER:
160         optFilterName = optarg;
161         break;
162       case O_FILTER_DOMAIN:
163         optDomainName = optarg;
164         break;
165       case O_DESC:
166         optDesc =  optarg;
167         break;
168       case O_FILTER_BW:
169         optFilterBW = strtod(optarg, &endptr);
170         endstr = optarg + strlen(optarg);
171         if (endptr != endstr) {
172           sys_error(ERR_SEVERE,"Error setting --filter-bw to %s\n", optarg);
173           phm2if_usage(argv[0]);
174           return (1);
175         }
176         break;
177       case O_FILTER_PARAM:
178         optFilterParam = strtod(optarg, &endptr);
179         endstr = optarg + strlen(optarg);
180         if (endptr != endstr) {
181           sys_error(ERR_SEVERE,"Error setting --filter-param to %s\n", optarg);
182           phm2if_usage(argv[0]);
183           return (1);
184         }
185         break;
186       case O_NSAMPLE:
187         opt_nsample = strtol(optarg, &endptr, 10);
188         endstr = optarg + strlen(optarg);
189         if (endptr != endstr) {
190           sys_error(ERR_SEVERE,"Error setting --nsample to %s\n", optarg);
191           phm2if_usage(argv[0]);
192           return (1);
193         }
194         break;
195         case O_VERSION:
196 #ifdef VERSION
197           cout <<  "Version " << VERSION << endl;
198 #else
199           cerr << "Unknown version number" << endl;
200 #endif
201       case O_HELP:
202       case '?':
203         phm2if_usage(argv[0]);
204         return (0);
205       default:
206         phm2if_usage(argv[0]);
207         return (1);
208       }
209     }
210     
211     if (optPhmName == "" && optFilterName == "" && optPhmFilename == "") {
212       cerr << "No phantom defined" << endl << endl;
213       phm2if_usage(argv[0]);
214       return (1);
215     }
216
217     if (optind + 3 != argc) {
218       phm2if_usage(argv[0]);
219       return (1);
220     }
221     optOutFilename = argv[optind];
222     opt_nx = strtol(argv[optind+1], &endptr, 10);
223     endstr = argv[optind+1] + strlen(argv[optind+1]);
224     if (endptr != endstr) {
225       sys_error(ERR_SEVERE,"Error setting nx to %s\n", argv[optind+1]);
226       phm2if_usage(argv[0]);
227       return (1);
228     }
229     opt_ny = strtol(argv[optind+2], &endptr, 10);
230     endstr = argv[optind+2] + strlen(argv[optind+2]);
231     if (endptr != endstr) {
232       sys_error(ERR_SEVERE,"Error setting ny to %s\n", argv[optind+2]);
233       phm2if_usage(argv[0]);
234       return (1);
235     }
236     
237     ostringstream oss;
238     oss << "phm2if: nx=" << opt_nx << ", ny=" << opt_ny << ", nsample=" << opt_nsample << ", ";
239     if (optPhmFilename != "")
240       oss << "phantomFile=" << optPhmFilename;
241     else if (optPhmName != "")
242       oss << "phantom=" << optPhmName;
243     else if (optFilterName != "") {
244       oss << "filter=" << optFilterName << " - " << optDomainName;
245     }
246     if (optDesc != "")
247       oss << ": " << optDesc;
248     optDesc = oss.str();
249     
250     if (optPhmName != "") {
251       phm.createFromPhantom (optPhmName.c_str());
252       if (phm.fail()) {
253         cout << phm.failMessage() << endl << endl;
254         phm2if_usage(argv[0]);
255         return (1);
256       }
257     }
258
259     if (optPhmFilename != "") {
260       phm.createFromFile(optPhmFilename.c_str());
261 #ifdef HAVE_MPI
262       if (mpiWorld.getRank() == 0) 
263         cerr << "Can't use phantom from file in MPI mode" << endl;
264       return (1);
265 #endif
266     }
267
268     if (optVerbose)
269       cout << "Rasterize Phantom to Image" << endl << endl;
270 #ifdef HAVE_MPI
271   }
272 #endif
273
274 #ifdef HAVE_MPI
275   TimerCollectiveMPI timerBcast (mpiWorld.getComm());
276   mpiWorld.BcastString (optPhmName);
277   mpiWorld.getComm().Bcast (&optVerbose, 1, MPI::INT, 0);
278   mpiWorld.getComm().Bcast (&optDebug, 1, MPI::INT, 0);
279   mpiWorld.getComm().Bcast (&optTrace, 1, MPI::INT, 0);
280   mpiWorld.getComm().Bcast (&opt_nx, 1, MPI::INT, 0);
281   mpiWorld.getComm().Bcast (&opt_ny, 1, MPI::INT, 0);
282   mpiWorld.getComm().Bcast (&opt_nsample, 1, MPI::INT, 0);
283   mpiWorld.getComm().Bcast (&optFilterParam, 1, MPI::DOUBLE, 0);
284   mpiWorld.getComm().Bcast (&optFilterBW, 1, MPI::DOUBLE, 0);
285
286   mpiWorld.BcastString (optFilterName);
287   mpiWorld.BcastString (optDomainName);
288
289   if (optVerbose)
290     timerBcast.timerEndAndReport ("Time to broadcast variables");
291
292   mpiWorld.setTotalWorkUnits (opt_nx);
293
294   if (mpiWorld.getRank() > 0 && optPhmName != "")
295       phm.createFromPhantom (optPhmName.c_str());
296
297   if (mpiWorld.getRank() == 0) {
298     pImGlobal = new ImageFile (opt_nx, opt_ny);
299   }
300   pImLocal = new ImageFile (opt_nx, opt_ny);
301 #else
302   pImGlobal = new ImageFile (opt_nx, opt_ny);
303 #endif
304
305   ImageFileArray v;
306 #ifdef HAVE_MPI
307   if (mpiWorld.getRank() == 0)
308     v = pImGlobal->getArray ();
309
310   if (phm.getComposition() == P_UNIT_PULSE) {
311     if (mpiWorld.getRank() == 0) {
312       v[opt_nx/2][opt_ny/2] = 1.;
313     }
314   } else if (optFilterName != "") {
315     if (mpiWorld.getRank() == 0) {
316       pImGlobal->filterResponse (optDomainName.c_str(), optFilterBW, optFilterName.c_str(), optFilterParam);
317     }
318   } else {
319     TimerCollectiveMPI timerRasterize (mpiWorld.getComm());
320     phm.convertToImagefile (*pImLocal, opt_nsample, optTrace, mpiWorld.getMyStartWorkUnit(), mpiWorld.getMyLocalWorkUnits());
321     if (optVerbose)
322       timerRasterize.timerEndAndReport ("Time to rasterize phantom");
323
324     TimerCollectiveMPI timerGather (mpiWorld.getComm());
325     mpi_gather_image (mpiWorld, pImGlobal, pImLocal, optDebug);
326     if (optVerbose)
327       timerGather.timerEndAndReport ("Time to gather image");
328   }
329 #else
330   v = pImGlobal->getArray ();
331   if (phm.getComposition() == P_UNIT_PULSE) {
332     v[opt_nx/2][opt_ny/2] = 1.;
333   } else if (optFilterName != "") {
334     pImGlobal->filterResponse (optDomainName.c_str(), optFilterBW, optFilterName.c_str(), optFilterParam);
335   } else {
336 #if HAVE_SGP
337       if (optTrace >= TRACE_PHM)
338         phm.show();
339 #endif
340       phm.convertToImagefile (*pImGlobal, opt_nsample, optTrace);
341   }
342 #endif
343
344 #ifdef HAVE_MPI
345   if (mpiWorld.getRank() == 0) 
346 #endif
347   {
348     double calctime = timerProgram.timerEnd ();
349     pImGlobal->labelAdd (Array2dFileLabel::L_HISTORY, optDesc.c_str(), calctime);
350     pImGlobal->fileWrite (optOutFilename.c_str());
351     if (optVerbose)
352       cout << "Time to rasterized phantom: " << calctime << " seconds" << endl;
353
354     if (optTrace >= TRACE_PHM) {
355       double dmin, dmax;
356       int nscale;
357       
358       printf ("Enter display size scale (nominal = 1): ");
359       scanf ("%d", &nscale);
360       printf ("Enter minimum and maximum densities (min, max): ");
361       scanf ("%lf %lf", &dmin, &dmax);
362       pImGlobal->displayScaling (nscale, dmin, dmax);
363     }
364   }
365
366   delete pImGlobal;
367 #ifdef HAVE_MPI
368   delete pImLocal;
369 #endif
370
371   return (0);
372 }
373
374
375
376 #ifdef HAVE_MPI
377 void mpi_gather_image (MPIWorld& mpiWorld, ImageFile* pImGlobal, ImageFile* pImLocal, const int optDebug)
378 {
379   ImageFileArray vLocal = pImLocal->getArray();
380   ImageFileArray vGlobal = NULL;
381   int nyLocal = pImLocal->ny();
382
383   if (mpiWorld.getRank() == 0)
384     vGlobal = pImGlobal->getArray();
385   
386   for (int iw = 0; iw < mpiWorld.getMyLocalWorkUnits(); iw++)
387     mpiWorld.getComm().Send(vLocal[iw], nyLocal, pImLocal->getMPIDataType(), 0, 0);
388
389   if (mpiWorld.getRank() == 0) {
390     for (int iProc = 0; iProc < mpiWorld.getNumProcessors(); iProc++) {
391       for (int iw = mpiWorld.getStartWorkUnit(iProc); iw <= mpiWorld.getEndWorkUnit(iProc); iw++) {
392         MPI::Status status;
393         mpiWorld.getComm().Recv(vGlobal[iw], nyLocal, pImLocal->getMPIDataType(), iProc, 0, status);
394       }
395     }
396   }
397
398 }
399 #endif
400
401 #ifndef NO_MAIN
402 int 
403 main (int argc, char* argv[])
404 {
405   int retval = 1;
406
407   try {
408     retval = phm2if_main(argc, argv);
409   } catch (exception e) {
410     cerr << "Exception: " << e.what() << endl;
411   } catch (...) {
412     cerr << "Unknown exception" << endl;
413   }
414
415   return (retval);
416 }
417 #endif