r7061: initial property settings
[ctsim.git] / tools / pjrec.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **   Name:          pjrec.cpp
5 **   Purpose:       Reconstruct an image from projections
6 **   Programmer:    Kevin Rosenberg
7 **   Date Started:  Aug 1984
8 **
9 **  This is part of the CTSim program
10 **  Copyright (C) 1983-2000 Kevin Rosenberg
11 **
12 **  $Id$
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 enum {O_INTERP, O_FILTER, O_FILTER_METHOD, O_ZEROPAD, O_FILTER_PARAM, O_FILTER_GENERATION, O_BACKPROJ, O_PREINTERPOLATION_FACTOR, O_VERBOSE, O_TRACE, O_HELP, O_DEBUG, O_VERSION};
32
33 static struct option my_options[] =
34 {
35   {"interp", 1, 0, O_INTERP},
36   {"preinterpolation-factor", 1, 0, O_PREINTERPOLATION_FACTOR},
37   {"filter", 1, 0, O_FILTER},
38   {"filter-method", 1, 0, O_FILTER_METHOD},
39   {"zeropad", 1, 0, O_ZEROPAD},
40   {"filter-generation", 1, 0, O_FILTER_GENERATION},
41   {"filter-param", 1, 0, O_FILTER_PARAM},
42   {"backproj", 1, 0, O_BACKPROJ},
43   {"trace", 1, 0, O_TRACE},
44   {"debug", 0, 0, O_DEBUG},
45   {"verbose", 0, 0, O_VERBOSE},
46   {"help", 0, 0, O_HELP},
47   {"version", 0, 0, O_VERSION},
48   {0, 0, 0, 0}
49 };
50
51 static const char* g_szIdStr = "$Id$";
52
53 void 
54 pjrec_usage (const char *program)
55 {
56   std::cout << "usage: " << fileBasename(program) << " raysum-file image-file nx-image ny-image [OPTIONS]" << std::endl;
57   std::cout << "Image reconstruction from raysum projections" << std::endl;
58   std::cout << std::endl;
59   std::cout << "  raysum-file     Input raysum file" << std::endl;
60   std::cout << "  image-file      Output image file in SDF2D format" << std::endl;
61   std::cout << "  nx-image        Number of columns in output image" << std::endl;
62   std::cout << "  ny-image        Number of rows in output image" << std::endl;
63   std::cout << "  --interp        Interpolation method during backprojection" << std::endl;
64   std::cout << "    nearest         Nearest neighbor interpolation" << std::endl;
65   std::cout << "    linear          Linear interpolation (default)" << std::endl;
66   std::cout << "    cubic           Cubic interpolation\n";
67 #if HAVE_BSPLINE_INTERP
68   std::cout << "    bspline         B-spline interpolation" << std::endl;
69 #endif
70   std::cout << "  --preinterpolate  Preinterpolation factor (default = 1)\n";
71   std::cout << "                    Used only with frequency-based filtering\n";
72   std::cout << "  --filter       Filter name" << std::endl;
73   std::cout << "    abs_bandlimit  Abs * Bandlimiting (default)" << std::endl;
74   std::cout << "    abs_sinc       Abs * Sinc" << std::endl;
75   std::cout << "    abs_cosine     Abs * Cosine" << std::endl;
76   std::cout << "    abs_hamming    Abs * Hamming" << std::endl;
77   std::cout << "    abs_hanning    Abs * Hanning" << std::endl;
78   std::cout << "    shepp          Shepp-Logan" << std::endl;
79   std::cout << "    bandlimit      Bandlimiting" << std::endl;
80   std::cout << "    sinc           Sinc" << std::endl;
81   std::cout << "    cosine         Cosine" << std::endl;
82   std::cout << "    triangle       Triangle" << std::endl;
83   std::cout << "    hamming        Hamming" << std::endl;
84   std::cout << "    hanning        Hanning" << std::endl;
85   std::cout << "  --filter-method  Filter method before backprojections\n";;
86   std::cout << "    convolution      Spatial filtering (default)\n";
87   std::cout << "    fourier          Frequency filtering with discete fourier\n";
88   std::cout << "    fourier_table    Frequency filtering with table lookup fourier\n";
89   std::cout << "    fft              Fast Fourier Transform\n";
90 #if HAVE_FFTW
91   std::cout << "    fftw             Fast Fourier Transform West library\n";
92   std::cout << "    rfftw            Fast Fourier Transform West (real-mode) library\n";
93 #endif
94   std::cout << "  --zeropad n   Set zeropad level (default = 0)\n";
95   std::cout << "                set n to number of powers to two to pad\n";
96   std::cout << "  --filter-generation  Filter Generation mode\n";
97   std::cout << "    direct       Use direct filter in spatial or frequency domain (default)\n";
98   std::cout << "    inverse_fourier  Use inverse fourier transform of inverse filter\n";
99   std::cout << "  --backproj    Backprojection Method" << std::endl;
100   std::cout << "    trig        Trigometric functions at every point" << std::endl;
101   std::cout << "    table       Trigometric functions with precalculated table" << std::endl;
102   std::cout << "    diff        Difference method" << std::endl;
103   std::cout << "    diff2       Optimized difference method (default)" << std::endl;
104   std::cout << "    idiff2      Optimized difference method with integer math" << std::endl;
105   std::cout << "    idiff3      Highly-optimized difference method with integer math" << std::endl;
106   std::cout << "  --filter-param Alpha level for Hamming filter" << std::endl;
107   std::cout << "  --trace        Set tracing to level" << std::endl;
108   std::cout << "     none        No tracing (default)" << std::endl;
109   std::cout << "     console     Text level tracing" << std::endl;
110   std::cout << "  --verbose      Turn on verbose mode" << std::endl;
111   std::cout << "  --debug        Turn on debug mode" << std::endl;
112   std::cout << "  --version      Print version" << std::endl;
113   std::cout << "  --help         Print this help message" << std::endl;
114 }
115
116
117 #ifdef HAVE_MPI
118 static void ScatterProjectionsMPI (MPIWorld& mpiWorld, Projections& projGlobal, Projections& projLocal, const bool bDebug);
119 static void ReduceImageMPI (MPIWorld& mpiWorld, ImageFile* imLocal, ImageFile* imGlobal);
120 #endif
121
122
123 int 
124 pjrec_main (int argc, char * const argv[])
125 {
126   Projections projGlobal;
127   ImageFile* imGlobal = NULL;
128   char* pszFilenameProj = NULL;
129   char* pszFilenameImage = NULL;
130   std::string sRemark;
131   bool bOptVerbose = false;
132   bool bOptDebug = 1;
133   int iOptZeropad = 1;
134   int optTrace = Trace::TRACE_NONE;
135   double dOptFilterParam = -1;
136   std::string sOptFilterName (SignalFilter::convertFilterIDToName (SignalFilter::FILTER_ABS_BANDLIMIT));
137   std::string sOptFilterMethodName (ProcessSignal::convertFilterMethodIDToName (ProcessSignal::FILTER_METHOD_CONVOLUTION));
138   std::string sOptFilterGenerationName (ProcessSignal::convertFilterGenerationIDToName (ProcessSignal::FILTER_GENERATION_DIRECT));
139   std::string sOptInterpName (Backprojector::convertInterpIDToName (Backprojector::INTERP_LINEAR));
140   std::string sOptBackprojectName (Backprojector::convertBackprojectIDToName (Backprojector::BPROJ_IDIFF));
141   int iOptPreinterpolationFactor = 1;
142   int nx, ny;
143   char *endptr;
144 #ifdef HAVE_MPI
145   ImageFile* imLocal;
146   int mpi_nview, mpi_ndet;
147   double mpi_detinc, mpi_rotinc, mpi_phmlen;
148   MPIWorld mpiWorld (argc, argv);
149 #endif
150
151   Timer timerProgram;
152
153 #ifdef HAVE_MPI
154   if (mpiWorld.getRank() == 0) {
155 #endif
156     while (1) {
157       int c = getopt_long(argc, argv, "", my_options, NULL);
158       char *endptr = NULL;
159       
160       if (c == -1)
161         break;
162       
163       switch (c)
164         {
165         case O_INTERP:
166           sOptInterpName = optarg;
167           break;
168         case O_PREINTERPOLATION_FACTOR:
169           iOptPreinterpolationFactor = strtol(optarg, &endptr, 10);
170           if (endptr != optarg + strlen(optarg)) {
171             pjrec_usage(argv[0]);
172             return(1);
173           }
174           break;
175         case O_FILTER:
176           sOptFilterName = optarg;
177           break;
178         case O_FILTER_METHOD:
179           sOptFilterMethodName = optarg;
180           break;
181         case O_FILTER_GENERATION:
182           sOptFilterGenerationName = optarg;
183           break;
184         case O_FILTER_PARAM:
185           dOptFilterParam = strtod(optarg, &endptr);
186           if (endptr != optarg + strlen(optarg)) {
187             pjrec_usage(argv[0]);
188             return(1);
189           }
190           break;
191         case O_ZEROPAD:
192           iOptZeropad = strtol(optarg, &endptr, 10);
193           if (endptr != optarg + strlen(optarg)) {
194             pjrec_usage(argv[0]);
195             return(1);
196           }
197           break;
198         case O_BACKPROJ:
199           sOptBackprojectName = optarg;
200           break;
201         case O_VERBOSE:
202           bOptVerbose = true;
203           break;
204         case O_DEBUG:
205           bOptDebug = true;
206           break;
207         case O_TRACE:
208           if ((optTrace = Trace::convertTraceNameToID(optarg)) == Trace::TRACE_INVALID) {
209             pjrec_usage(argv[0]);
210             return (1);
211           }
212           break;
213         case O_VERSION:
214 #ifdef VERSION
215           std::cout <<  "Version " <<  VERSION << std::endl << g_szIdStr << std::endl;
216 #else
217           std::cout << "Unknown version number" << std::endl;
218 #endif
219           return (0);
220         case O_HELP:
221         case '?':
222           pjrec_usage(argv[0]);
223           return (0);
224         default:
225           pjrec_usage(argv[0]);
226           return (1);
227         }
228     }
229   
230     if (optind + 4 != argc) {
231       pjrec_usage(argv[0]);
232       return (1);
233     }
234
235     pszFilenameProj = argv[optind];
236   
237     pszFilenameImage = argv[optind + 1];
238   
239     nx = strtol(argv[optind + 2], &endptr, 10);
240     ny = strtol(argv[optind + 3], &endptr, 10);
241   
242     std::ostringstream filterDesc;
243     if (dOptFilterParam >= 0)
244       filterDesc << sOptFilterName << ": alpha=" << dOptFilterParam; 
245     else
246       filterDesc << sOptFilterName;
247
248     std::ostringstream label;
249     label << "pjrec: " << nx << "x" << ny << ", " << filterDesc.str() << ", " << sOptInterpName << ", preinterpolationFactor=" << iOptPreinterpolationFactor << ", " << sOptBackprojectName;
250     sRemark = label.str();
251   
252     if (bOptVerbose)
253       std::cout << "SRemark: " << sRemark << std::endl;
254 #ifdef HAVE_MPI
255   }
256 #endif
257
258 #ifdef HAVE_MPI
259   if (mpiWorld.getRank() == 0) {
260     projGlobal.read (pszFilenameProj);
261     if (bOptVerbose) {
262       ostringstream os;
263       projGlobal.printScanInfo (os);
264       std::cout << os.str();
265     }
266
267     mpi_ndet = projGlobal.nDet();
268     mpi_nview = projGlobal.nView();
269     mpi_detinc = projGlobal.detInc();
270     mpi_phmlen = projGlobal.phmLen();
271     mpi_rotinc = projGlobal.rotInc();
272   }
273
274   TimerCollectiveMPI timerBcast (mpiWorld.getComm());
275   mpiWorld.BcastString (sOptBackprojectName);
276   mpiWorld.BcastString (sOptFilterName);
277   mpiWorld.BcastString (sOptFilterMethodName);
278   mpiWorld.BcastString (sOptInterpName);
279   mpiWorld.getComm().Bcast (&bOptVerbose, 1, MPI::INT, 0);
280   mpiWorld.getComm().Bcast (&bOptDebug, 1, MPI::INT, 0);
281   mpiWorld.getComm().Bcast (&optTrace, 1, MPI::INT, 0);
282   mpiWorld.getComm().Bcast (&dOptFilterParam, 1, MPI::DOUBLE, 0);
283   mpiWorld.getComm().Bcast (&iOptZeropad, 1, MPI::INT, 0);
284   mpiWorld.getComm().Bcast (&iOptPreinterpolationFactor, 1, MPI::INT, 0);
285   mpiWorld.getComm().Bcast (&mpi_ndet, 1, MPI::INT, 0);
286   mpiWorld.getComm().Bcast (&mpi_nview, 1, MPI::INT, 0);
287   mpiWorld.getComm().Bcast (&mpi_detinc, 1, MPI::DOUBLE, 0);
288   mpiWorld.getComm().Bcast (&mpi_phmlen, 1, MPI::DOUBLE, 0);
289   mpiWorld.getComm().Bcast (&mpi_rotinc, 1, MPI::DOUBLE, 0);
290   mpiWorld.getComm().Bcast (&nx, 1, MPI::INT, 0);
291   mpiWorld.getComm().Bcast (&ny, 1, MPI::INT, 0);
292   if (bOptVerbose)
293       timerBcast.timerEndAndReport ("Time to broadcast variables");
294
295   mpiWorld.setTotalWorkUnits (mpi_nview);
296
297   Projections projLocal (mpiWorld.getMyLocalWorkUnits(), mpi_ndet);
298   projLocal.setDetInc (mpi_detinc);
299   projLocal.setPhmLen (mpi_phmlen);
300   projLocal.setRotInc (mpi_rotinc);
301
302   TimerCollectiveMPI timerScatter (mpiWorld.getComm());
303   ScatterProjectionsMPI (mpiWorld, projGlobal, projLocal, bOptDebug);
304   if (bOptVerbose)
305       timerScatter.timerEndAndReport ("Time to scatter projections");
306
307   if (mpiWorld.getRank() == 0) {
308     imGlobal = new ImageFile (nx, ny);
309   }
310
311   imLocal = new ImageFile (nx, ny);
312 #else
313   projGlobal.read (pszFilenameProj);
314   if (bOptVerbose) {
315     std::ostringstream os;
316     projGlobal.printScanInfo(os);
317     std::cout << os.str();
318   }
319
320   imGlobal = new ImageFile (nx, ny);
321 #endif
322
323 #ifdef HAVE_MPI
324   TimerCollectiveMPI timerReconstruct (mpiWorld.getComm());
325
326   Reconstructor reconstruct (projLocal, *imLocal, sOptFilterName.c_str(), dOptFilterParam, sOptFilterMethodName.c_str(), iOptZeropad, sOptFilterGenerationName.c_str(), sOptInterpName.c_str(), iOptPreinterpolationFactor, sOptBackprojectName.c_str(), optTrace);
327   if (reconstruct.fail()) {
328     std::cout << reconstruct.failMessage();
329     return (1);
330   }
331   reconstruct.reconstructAllViews();
332   
333   if (bOptVerbose)
334       timerReconstruct.timerEndAndReport ("Time to reconstruct");
335
336   TimerCollectiveMPI timerReduce (mpiWorld.getComm());
337   ReduceImageMPI (mpiWorld, imLocal, imGlobal);
338   if (bOptVerbose)
339       timerReduce.timerEndAndReport ("Time to reduce image");
340 #else
341   Reconstructor reconstruct (projGlobal, *imGlobal, sOptFilterName.c_str(), dOptFilterParam, sOptFilterMethodName.c_str(), iOptZeropad, sOptFilterGenerationName.c_str(), sOptInterpName.c_str(), iOptPreinterpolationFactor, sOptBackprojectName.c_str(), optTrace);
342   if (reconstruct.fail()) {
343     std::cout << reconstruct.failMessage();
344     return (1);
345   }
346   reconstruct.reconstructAllViews();
347 #endif
348
349 #ifdef HAVE_MPI
350   if (mpiWorld.getRank() == 0)
351 #endif
352     {
353       double dCalcTime = timerProgram.timerEnd();
354       imGlobal->labelAdd (projGlobal.getLabel());
355       imGlobal->labelAdd (Array2dFileLabel::L_HISTORY, sRemark.c_str(), dCalcTime);
356       imGlobal->fileWrite (pszFilenameImage);
357       if (bOptVerbose)
358         std::cout << "Run time: " << dCalcTime << " seconds" << std::endl;
359     }
360 #ifdef HAVE_MPI
361   MPI::Finalize();
362 #endif
363
364   return (0);
365 }
366
367
368 //////////////////////////////////////////////////////////////////////////////////////
369 // MPI Support Routines
370 //
371 //////////////////////////////////////////////////////////////////////////////////////
372
373 #ifdef HAVE_MPI
374 static void ScatterProjectionsMPI (MPIWorld& mpiWorld, Projections& projGlobal, Projections& projLocal, const bool bOptDebug)
375 {
376   if (mpiWorld.getRank() == 0) {
377     for (int iProc = 0; iProc < mpiWorld.getNumProcessors(); iProc++) {
378       for (int iw = mpiWorld.getStartWorkUnit(iProc); iw <= mpiWorld.getEndWorkUnit(iProc); iw++) {
379         DetectorArray& detarray = projGlobal.getDetectorArray( iw );
380         int nDet = detarray.nDet();
381         DetectorValue* detval = detarray.detValues();
382
383         double viewAngle = detarray.viewAngle();
384         mpiWorld.getComm().Send(&nDet, 1, MPI::INT, iProc, 0);
385         mpiWorld.getComm().Send(&viewAngle, 1, MPI::DOUBLE, iProc, 0);
386         mpiWorld.getComm().Send(detval, nDet, MPI::FLOAT, iProc, 0);
387       }
388     }
389   }
390
391   for (int iw = 0; iw < mpiWorld.getMyLocalWorkUnits(); iw++) {
392     MPI::Status status;
393     int nDet;
394     double viewAngle;
395     DetectorValue* detval = projLocal.getDetectorArray(iw).detValues();
396
397     mpiWorld.getComm().Recv(&nDet, 1, MPI::INT, 0, 0, status);
398     mpiWorld.getComm().Recv(&viewAngle, 1, MPI::DOUBLE, 0, 0, status);
399     mpiWorld.getComm().Recv(detval, nDet, MPI::FLOAT, 0, 0, status);
400     projLocal.getDetectorArray(iw).setViewAngle( viewAngle );
401   }
402 }
403
404 static void
405 ReduceImageMPI (MPIWorld& mpiWorld, ImageFile* imLocal, ImageFile* imGlobal)
406 {
407   ImageFileArray vLocal = imLocal->getArray();
408
409   for (unsigned int ix = 0; ix < imLocal->nx(); ix++) {
410     void *recvbuf = NULL;
411     if (mpiWorld.getRank() == 0) {
412       ImageFileArray vGlobal = imGlobal->getArray();
413       recvbuf = vGlobal[ix];
414     }
415     mpiWorld.getComm().Reduce (vLocal[ix], recvbuf, imLocal->ny(), imLocal->getMPIDataType(), MPI::SUM, 0);
416   }
417 }
418
419 #endif
420
421
422 #ifndef NO_MAIN
423 int 
424 main (int argc, char* argv[])
425 {
426   int retval = 1;
427
428   try {
429     retval = pjrec_main(argc, argv);
430   } catch (exception e) {
431           std::cerr << "Exception: " << e.what() << std::endl;
432   } catch (...) {
433           std::cerr << "Unknown exception" << std::endl;
434   }
435
436   return (retval);
437 }
438 #endif
439