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