r279: msvc compatibility changes
[ctsim.git] / tools / phm2pj.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **   Name:          phm2pj.cpp
5 **   Purpose:       Take projections of a phantom object
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: phm2pj.cpp,v 1.18 2000/12/17 23:30:48 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_NRAY, O_ROTANGLE, O_PHMFILE, O_GEOMETRY, O_FOCAL_LENGTH, O_FIELD_OF_VIEW,
33        O_TRACE, O_VERBOSE, O_HELP, O_DEBUG, O_VERSION };
34
35 static struct option phm2pj_options[] = 
36 {
37   {"phantom", 1, 0, O_PHANTOM},
38   {"phmfile", 1, 0, O_PHMFILE},
39   {"desc", 1, 0, O_DESC},
40   {"nray", 1, 0, O_NRAY},
41   {"rotangle", 1, 0, O_ROTANGLE},
42   {"geometry", 1, 0, O_GEOMETRY},
43   {"focal-length", 1, 0, O_FOCAL_LENGTH},
44   {"field-of-view", 1, 0, O_FIELD_OF_VIEW},
45   {"trace", 1, 0, O_TRACE},
46   {"verbose", 0, 0, O_VERBOSE},
47   {"help", 0, 0, O_HELP},
48   {"debug", 0, 0, O_DEBUG},
49   {"version", 0, 0, O_VERSION},
50   {0, 0, 0, 0}
51 };
52
53 static const char* g_szIdStr = "$Id: phm2pj.cpp,v 1.18 2000/12/17 23:30:48 kevin Exp $";
54
55
56 void 
57 phm2pj_usage (const char *program)
58 {
59   std::cout << "usage: " << fileBasename(program) << " outfile ndet nview [--phantom phantom-name] [--phmfile filename] [OPTIONS]\n";
60   std::cout << "Calculate (projections) through phantom object, either a predefined --phantom or a --phmfile\n\n";
61   std::cout << "     outfile          Name of output file for projections\n";
62   std::cout << "     ndet             Number of detectors\n";
63   std::cout << "     nview            Number of rotated views\n";
64   std::cout << "     --phantom        Phantom to use for projection\n";
65   std::cout << "        herman        Herman head phantom\n";
66   std::cout << "        herman-b      Herman head phantom (Bordered)\n";
67   std::cout << "        shepp-logan   Shepp-Logan head phantom\n";
68   std::cout << "        shepp-logan-b Shepp-Logan head phantom (Bordered)\n";
69   std::cout << "        unitpulse     Unit pulse phantom\n";
70   std::cout << "     --phmfile        Get Phantom from phantom file\n";
71   std::cout << "     --desc           Description of raysum\n";
72   std::cout << "     --nray           Number of rays per detector (default = 1)\n";
73   std::cout << "     --rotangle       Degrees to rotate view through (multiple of PI)\n";
74   std::cout << "                      (default = 1)\n";
75   std::cout << "     --geometry       Geometry of scanning\n";
76   std::cout << "        parallel      Parallel scan beams (default)\n";
77   std::cout << "        equilinear    Equilinear divergent scan beams\n";
78   std::cout << "        equiangular   Equiangular divergent scan beams\n";
79   std::cout << "     --focal-length   Focal length ratio (ratio to radius of phantom)\n";
80   std::cout << "                      (default = 1)\n";
81   std::cout << "     --field-of-view  Field of view (ratio to diameter of phantom square)\n";
82   std::cout << "                      (default = 1)\n";
83   std::cout << "     --trace          Trace level to use\n";
84   std::cout << "        none          No tracing (default)\n";
85   std::cout << "        console       Trace text level\n";
86   std::cout << "        phantom       Trace phantom image\n";
87   std::cout << "        proj          Trace projections\n";
88   std::cout << "        plot          Trace plot\n";
89   std::cout << "        clipping      Trace clipping\n";
90   std::cout << "     --verbose        Verbose mode\n";
91   std::cout << "     --debug          Debug mode\n";
92   std::cout << "     --version        Print version\n";
93   std::cout << "     --help           Print this help message\n";
94 }
95
96 #ifdef HAVE_MPI
97 void GatherProjectionsMPI (MPIWorld& mpiWorld, Projections& pjGlobal, Projections& pjLocal, const int opt_debug);
98 #endif
99
100 int 
101 phm2pj_main (int argc, char* argv[])
102 {
103   Phantom phm;
104   std::string optGeometryName = Scanner::convertGeometryIDToName(Scanner::GEOMETRY_PARALLEL);
105   char *opt_outfile = NULL;
106   std::string opt_desc;
107   std::string optPhmName;
108   std::string optPhmFileName;
109   int opt_ndet;
110   int opt_nview;
111   int opt_nray = 1;
112   double dOptFocalLength = 1.;
113   double dOptFieldOfView = 1.;
114   int opt_trace = Trace::TRACE_NONE;
115   int opt_verbose = 0;
116   int opt_debug = 0;
117   double opt_rotangle = 1;
118   char* endptr = NULL;
119   char* endstr;
120
121 #ifdef HAVE_MPI
122   MPIWorld mpiWorld (argc, argv);
123 #endif
124
125   Timer timerProgram;
126
127 #ifdef HAVE_MPI
128   if (mpiWorld.getRank() == 0) {
129 #endif
130     while (1) {
131       int c = getopt_long(argc, argv, "", phm2pj_options, NULL);
132
133       if (c == -1)
134         break;
135       
136       switch (c) {
137       case O_PHANTOM:
138         optPhmName = optarg;
139         break;
140       case O_PHMFILE:
141         optPhmFileName = optarg;
142         break;
143       case O_VERBOSE:
144         opt_verbose = 1;
145         break;
146       case O_DEBUG:
147         opt_debug = 1;
148         break;
149         break;
150       case O_TRACE:
151         if ((opt_trace = Trace::convertTraceNameToID(optarg)) == Trace::TRACE_INVALID) {
152           phm2pj_usage(argv[0]);
153           return (1);
154         }
155         break;
156       case O_DESC:
157         opt_desc = optarg;
158         break;
159       case O_ROTANGLE:
160         opt_rotangle = strtod(optarg, &endptr);
161         endstr = optarg + strlen(optarg);
162         if (endptr != endstr) {
163           std::cerr << "Error setting --rotangle to " << optarg << endl;
164           phm2pj_usage(argv[0]);
165           return (1);
166         }
167         break;
168       case O_GEOMETRY:
169         optGeometryName = optarg;
170         break;
171       case O_FOCAL_LENGTH:
172         dOptFocalLength = strtod(optarg, &endptr);
173         endstr = optarg + strlen(optarg);
174         if (endptr != endstr) {
175           std::cerr << "Error setting --focal-length to " << optarg << endl;
176           phm2pj_usage(argv[0]);
177           return (1);
178         }
179         break;
180       case O_FIELD_OF_VIEW:
181         dOptFieldOfView = strtod(optarg, &endptr);
182         endstr = optarg + strlen(optarg);
183         if (endptr != endstr) {
184           std::cerr << "Error setting --field-of-view to " << optarg << endl;
185           phm2pj_usage(argv[0]);
186           return (1);
187         }
188         break;
189       case O_NRAY:
190         opt_nray = strtol(optarg, &endptr, 10);
191         endstr = optarg + strlen(optarg);
192         if (endptr != endstr) {
193           std::cerr << "Error setting --nray to %s" << optarg << endl;
194           phm2pj_usage(argv[0]);
195           return (1);
196         }
197         break;
198         case O_VERSION:
199 #ifdef VERSION
200           std::cout << "Version: " << VERSION << endl << g_szIdStr << endl;
201 #else
202           std::cout << "Unknown version number\n";
203 #endif
204           return (0);
205       case O_HELP:
206       case '?':
207         phm2pj_usage(argv[0]);
208         return (0);
209       default:
210         phm2pj_usage(argv[0]);
211         return (1);
212       }
213     }
214   
215     if (optPhmName == "" && optPhmFileName == "") {
216       std::cerr << "No phantom defined\n" << endl;
217       phm2pj_usage(argv[0]);
218       return (1);
219     }
220     if (optind + 3 != argc) {
221       phm2pj_usage(argv[0]);
222       return (1);
223     }
224
225     opt_outfile = argv[optind];
226     opt_ndet = strtol(argv[optind+1], &endptr, 10);
227     endstr = argv[optind+1] + strlen(argv[optind+1]);
228     if (endptr != endstr) {
229       std::cerr << "Error setting --ndet to " << argv[optind+1] << endl;
230       phm2pj_usage(argv[0]);
231       return (1);
232     }
233     opt_nview = strtol(argv[optind+2], &endptr, 10);
234     endstr = argv[optind+2] + strlen(argv[optind+2]);
235     if (endptr != endstr) {
236       std::cerr << "Error setting --nview to " << argv[optind+2] << endl;
237       phm2pj_usage(argv[0]);
238       return (1);
239     }
240
241     std::ostringstream desc;
242     desc << "phm2pj: NDet=" << opt_ndet << ", Nview=" << opt_nview << ", NRay=" << opt_nray << ", RotAngle=" << opt_rotangle << ", Geometry=" << optGeometryName << ", ";
243     if (optPhmFileName.length()) {
244       desc << "PhantomFile=" << optPhmFileName;
245     } else if (optPhmName != "") {
246       desc << "Phantom=" << optPhmName;
247     }
248     if (opt_desc.length()) {
249       desc << ": " << opt_desc;
250     }
251     opt_desc = desc.str();
252
253     if (optPhmName != "") {
254       phm.createFromPhantom (optPhmName.c_str());
255       if (phm.fail()) {
256         std::cout << phm.failMessage() << endl << endl;
257         phm2pj_usage(argv[0]);
258         return (1);
259       }
260     }
261
262     if (optPhmFileName != "") {
263 #ifdef HAVE_MPI
264       std::cerr << "Can not read phantom from file in MPI mode\n";
265       return (1);
266 #endif
267       phm.createFromFile (optPhmFileName.c_str());
268     }
269
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 (&opt_rotangle, 1, MPI::DOUBLE, 0);
278   mpiWorld.getComm().Bcast (&opt_nview, 1, MPI::INT, 0);
279   mpiWorld.getComm().Bcast (&opt_ndet, 1, MPI::INT, 0);
280   mpiWorld.getComm().Bcast (&opt_nray, 1, MPI::INT, 0);
281   mpiWorld.getComm().Bcast (&opt_verbose, 1, MPI::INT, 0);
282   mpiWorld.getComm().Bcast (&opt_debug, 1, MPI::INT, 0);
283   mpiWorld.getComm().Bcast (&opt_trace, 1, MPI::INT, 0);
284   timerBcast.timerEndAndReport ("Time to broadcast variables");
285
286   if (mpiWorld.getRank() > 0 && optPhmName != "")
287     phm.createFromPhantom (optPhmName.c_str());
288 #endif
289
290   opt_rotangle *= PI;
291   Scanner scanner (phm, optGeometryName.c_str(), opt_ndet, opt_nview, opt_nray, opt_rotangle, dOptFocalLength, dOptFieldOfView);
292   if (scanner.fail()) {
293     std::cout << "Scanner Creation Error: " << scanner.failMessage() << endl;
294     return (1);
295   }
296 #ifdef HAVE_MPI
297   mpiWorld.setTotalWorkUnits (opt_nview);
298
299   Projections pjGlobal;
300   if (mpiWorld.getRank() == 0)
301     pjGlobal.initFromScanner (scanner);
302   
303   if (opt_verbose) {
304     std::ostringstream os;
305     pjGlobal.printScanInfo(os);
306     std::cout << os.str();
307   }
308
309   Projections pjLocal (scanner);
310   pjLocal.setNView (mpiWorld.getMyLocalWorkUnits());
311
312   if (opt_debug)
313     std::cout << "pjLocal->nview = " << pjLocal.nView() << " (process " << mpiWorld.getRank() << ")\n";;
314
315   TimerCollectiveMPI timerProject (mpiWorld.getComm());
316   scanner.collectProjections (pjLocal, phm, mpiWorld.getMyStartWorkUnit(), mpiWorld.getMyLocalWorkUnits(), false, opt_trace);
317   if (opt_verbose)
318     timerProject.timerEndAndReport ("Time to collect projections");
319
320   TimerCollectiveMPI timerGather (mpiWorld.getComm());
321   GatherProjectionsMPI (mpiWorld, pjGlobal, pjLocal, opt_debug);
322   if (opt_verbose) 
323      timerGather.timerEndAndReport ("Time to gather projections");
324
325 #else
326   Projections pjGlobal (scanner);
327 #if HAVE_SGP
328   SGPDriver* pSGPDriver = NULL;
329   SGP* pSGP = NULL;
330   if (opt_trace >= Trace::TRACE_PHANTOM) {
331     pSGPDriver = new SGPDriver ("phm2pj", 600, 600);
332     pSGP = new SGP (*pSGPDriver);
333   }
334   scanner.collectProjections (pjGlobal, phm, opt_trace, pSGP);
335   if (opt_trace >= Trace::TRACE_PHANTOM) {
336     std::cout << "Press enter to continue\n";
337     cio_kb_getc();
338     delete pSGP;  pSGP = NULL;
339     delete pSGPDriver;  pSGPDriver = NULL;
340   }
341
342 #else
343   scanner.collectProjections (pjGlobal, phm, opt_trace);
344 #endif
345
346 #endif
347   
348 #ifdef HAVE_MPI
349   if (mpiWorld.getRank() == 0) 
350 #endif
351     {
352       pjGlobal.setCalcTime (timerProgram.timerEnd());
353       pjGlobal.setRemark (opt_desc);
354       pjGlobal.write (opt_outfile);
355       if (opt_verbose) {
356         phm.print();
357         std::cout << endl;
358         ostringstream os;
359         pjGlobal.printScanInfo (os);
360         std::cout << os.str() << endl;
361         std::cout << "  Remark: " << pjGlobal.remark() << endl;
362         std::cout << "Run time: " << pjGlobal.calcTime() << " seconds\n";
363       }
364     }
365
366   return (0);
367 }
368
369
370 /* FUNCTION
371  *    GatherProjectionsMPI
372  *
373  * SYNOPSIS
374  *    Gather's raysums from all processes in pjLocal in pjGlobal in process 0
375  */
376
377 #ifdef HAVE_MPI
378 void GatherProjectionsMPI (MPIWorld& mpiWorld, Projections& pjGlobal, Projections& pjLocal, const int opt_debug)
379 {
380   for (int iw = 0; iw < mpiWorld.getMyLocalWorkUnits(); iw++) {
381     DetectorArray& detArray = pjLocal.getDetectorArray(iw);
382     double viewAngle = detArray.viewAngle();
383     int nDet = detArray.nDet();
384     DetectorValue* detval = detArray.detValues();
385
386     mpiWorld.getComm().Send(&viewAngle, 1, MPI::DOUBLE, 0, 0);
387     mpiWorld.getComm().Send(&nDet, 1, MPI::INT, 0, 0);
388     mpiWorld.getComm().Send(detval, nDet, MPI::FLOAT, 0, 0);
389   }
390
391   if (mpiWorld.getRank() == 0) {
392     for (int iProc = 0; iProc < mpiWorld.getNumProcessors(); iProc++) {
393       for (int iw = mpiWorld.getStartWorkUnit(iProc); iw <= mpiWorld.getEndWorkUnit(iProc); iw++) {
394         MPI::Status status;
395         double viewAngle;
396         int nDet;
397         DetectorArray& detArray = pjGlobal.getDetectorArray(iw);
398         DetectorValue* detval = detArray.detValues();
399
400         mpiWorld.getComm().Recv(&viewAngle, 1, MPI::DOUBLE, iProc, 0, status);
401         mpiWorld.getComm().Recv(&nDet, 1, MPI::INT, iProc, 0, status);
402         mpiWorld.getComm().Recv(detval, nDet, MPI::FLOAT, iProc, 0, status);
403         detArray.setViewAngle (viewAngle);
404       }
405     }
406   }
407 }
408 #endif
409
410
411 #ifndef NO_MAIN
412 int 
413 main (int argc, char* argv[])
414 {
415   int retval = 1;
416
417   try {
418     retval = phm2pj_main(argc, argv);
419 #if HAVE_DMALLOC
420     //    dmalloc_shutdown();
421 #endif
422   } catch (exception e) {
423     std::cerr << "Exception: " << e.what() << endl;
424   } catch (...) {
425     std::cerr << "Unknown exception\n";
426   }
427
428   return (retval);
429 }
430 #endif
431