r627: no message
[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: pjrec.cpp,v 1.25 2001/02/23 02:06:02 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 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: pjrec.cpp,v 1.25 2001/02/23 02:06:02 kevin Exp $";
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 << "     phantom     Trace phantom" << std::endl;
111   std::cout << "     proj        Trace allrays" << std::endl;
112   std::cout << "     plot        Trace plotting" << std::endl;
113   std::cout << "     clipping    Trace clipping" << std::endl;
114   std::cout << "  --verbose      Turn on verbose mode" << std::endl;
115   std::cout << "  --debug        Turn on debug mode" << std::endl;
116   std::cout << "  --version      Print version" << std::endl;
117   std::cout << "  --help         Print this help message" << std::endl;
118 }
119
120
121 #ifdef HAVE_MPI
122 static void ScatterProjectionsMPI (MPIWorld& mpiWorld, Projections& projGlobal, Projections& projLocal, const bool bDebug);
123 static void ReduceImageMPI (MPIWorld& mpiWorld, ImageFile* imLocal, ImageFile* imGlobal);
124 #endif
125
126
127 int 
128 pjrec_main (int argc, char * const argv[])
129 {
130   Projections projGlobal;
131   ImageFile* imGlobal = NULL;
132   char* pszFilenameProj = NULL;
133   char* pszFilenameImage = NULL;
134   std::string sRemark;
135   bool bOptVerbose = false;
136   bool bOptDebug = 1;
137   int iOptZeropad = 1;
138   int optTrace = Trace::TRACE_NONE;
139   double dOptFilterParam = -1;
140   std::string sOptFilterName (SignalFilter::convertFilterIDToName (SignalFilter::FILTER_ABS_BANDLIMIT));
141   std::string sOptFilterMethodName (ProcessSignal::convertFilterMethodIDToName (ProcessSignal::FILTER_METHOD_CONVOLUTION));
142   std::string sOptFilterGenerationName (ProcessSignal::convertFilterGenerationIDToName (ProcessSignal::FILTER_GENERATION_DIRECT));
143   std::string sOptInterpName (Backprojector::convertInterpIDToName (Backprojector::INTERP_LINEAR));
144   std::string sOptBackprojectName (Backprojector::convertBackprojectIDToName (Backprojector::BPROJ_IDIFF));
145   int iOptPreinterpolationFactor = 1;
146   int nx, ny;
147   char *endptr;
148 #ifdef HAVE_MPI
149   ImageFile* imLocal;
150   int mpi_nview, mpi_ndet;
151   double mpi_detinc, mpi_rotinc, mpi_phmlen;
152   MPIWorld mpiWorld (argc, argv);
153 #endif
154
155   Timer timerProgram;
156
157 #ifdef HAVE_MPI
158   if (mpiWorld.getRank() == 0) {
159 #endif
160     while (1) {
161       int c = getopt_long(argc, argv, "", my_options, NULL);
162       char *endptr = NULL;
163       
164       if (c == -1)
165         break;
166       
167       switch (c)
168         {
169         case O_INTERP:
170           sOptInterpName = optarg;
171           break;
172         case O_PREINTERPOLATION_FACTOR:
173           iOptPreinterpolationFactor = strtol(optarg, &endptr, 10);
174           if (endptr != optarg + strlen(optarg)) {
175             pjrec_usage(argv[0]);
176             return(1);
177           }
178           break;
179         case O_FILTER:
180           sOptFilterName = optarg;
181           break;
182         case O_FILTER_METHOD:
183           sOptFilterMethodName = optarg;
184           break;
185         case O_FILTER_GENERATION:
186           sOptFilterGenerationName = optarg;
187           break;
188         case O_FILTER_PARAM:
189           dOptFilterParam = strtod(optarg, &endptr);
190           if (endptr != optarg + strlen(optarg)) {
191             pjrec_usage(argv[0]);
192             return(1);
193           }
194           break;
195         case O_ZEROPAD:
196           iOptZeropad = strtol(optarg, &endptr, 10);
197           if (endptr != optarg + strlen(optarg)) {
198             pjrec_usage(argv[0]);
199             return(1);
200           }
201           break;
202         case O_BACKPROJ:
203           sOptBackprojectName = optarg;
204           break;
205         case O_VERBOSE:
206           bOptVerbose = true;
207           break;
208         case O_DEBUG:
209           bOptDebug = true;
210           break;
211         case O_TRACE:
212           if ((optTrace = Trace::convertTraceNameToID(optarg)) == Trace::TRACE_INVALID) {
213             pjrec_usage(argv[0]);
214             return (1);
215           }
216           break;
217         case O_VERSION:
218 #ifdef VERSION
219           std::cout <<  "Version " <<  VERSION << std::endl << g_szIdStr << std::endl;
220 #else
221           std::cout << "Unknown version number" << std::endl;
222 #endif
223           return (0);
224         case O_HELP:
225         case '?':
226           pjrec_usage(argv[0]);
227           return (0);
228         default:
229           pjrec_usage(argv[0]);
230           return (1);
231         }
232     }
233   
234     if (optind + 4 != argc) {
235       pjrec_usage(argv[0]);
236       return (1);
237     }
238
239     pszFilenameProj = argv[optind];
240   
241     pszFilenameImage = argv[optind + 1];
242   
243     nx = strtol(argv[optind + 2], &endptr, 10);
244     ny = strtol(argv[optind + 3], &endptr, 10);
245   
246     std::ostringstream filterDesc;
247     if (dOptFilterParam >= 0)
248       filterDesc << sOptFilterName << ": alpha=" << dOptFilterParam; 
249     else
250       filterDesc << sOptFilterName;
251
252     std::ostringstream label;
253     label << "pjrec: " << nx << "x" << ny << ", " << filterDesc.str() << ", " << sOptInterpName << ", preinterpolationFactor=" << iOptPreinterpolationFactor << ", " << sOptBackprojectName;
254     sRemark = label.str();
255   
256     if (bOptVerbose)
257       std::cout << "SRemark: " << sRemark << std::endl;
258 #ifdef HAVE_MPI
259   }
260 #endif
261
262 #ifdef HAVE_MPI
263   if (mpiWorld.getRank() == 0) {
264     projGlobal.read (pszFilenameProj);
265     if (bOptVerbose) {
266       ostringstream os;
267       projGlobal.printScanInfo (os);
268       std::cout << os.str();
269     }
270
271     mpi_ndet = projGlobal.nDet();
272     mpi_nview = projGlobal.nView();
273     mpi_detinc = projGlobal.detInc();
274     mpi_phmlen = projGlobal.phmLen();
275     mpi_rotinc = projGlobal.rotInc();
276   }
277
278   TimerCollectiveMPI timerBcast (mpiWorld.getComm());
279   mpiWorld.BcastString (sOptBackprojectName);
280   mpiWorld.BcastString (sOptFilterName);
281   mpiWorld.BcastString (sOptFilterMethodName);
282   mpiWorld.BcastString (sOptInterpName);
283   mpiWorld.getComm().Bcast (&bOptVerbose, 1, MPI::INT, 0);
284   mpiWorld.getComm().Bcast (&bOptDebug, 1, MPI::INT, 0);
285   mpiWorld.getComm().Bcast (&optTrace, 1, MPI::INT, 0);
286   mpiWorld.getComm().Bcast (&dOptFilterParam, 1, MPI::DOUBLE, 0);
287   mpiWorld.getComm().Bcast (&iOptZeropad, 1, MPI::INT, 0);
288   mpiWorld.getComm().Bcast (&iOptPreinterpolationFactor, 1, MPI::INT, 0);
289   mpiWorld.getComm().Bcast (&mpi_ndet, 1, MPI::INT, 0);
290   mpiWorld.getComm().Bcast (&mpi_nview, 1, MPI::INT, 0);
291   mpiWorld.getComm().Bcast (&mpi_detinc, 1, MPI::DOUBLE, 0);
292   mpiWorld.getComm().Bcast (&mpi_phmlen, 1, MPI::DOUBLE, 0);
293   mpiWorld.getComm().Bcast (&mpi_rotinc, 1, MPI::DOUBLE, 0);
294   mpiWorld.getComm().Bcast (&nx, 1, MPI::INT, 0);
295   mpiWorld.getComm().Bcast (&ny, 1, MPI::INT, 0);
296   if (bOptVerbose)
297       timerBcast.timerEndAndReport ("Time to broadcast variables");
298
299   mpiWorld.setTotalWorkUnits (mpi_nview);
300
301   Projections projLocal (mpiWorld.getMyLocalWorkUnits(), mpi_ndet);
302   projLocal.setDetInc (mpi_detinc);
303   projLocal.setPhmLen (mpi_phmlen);
304   projLocal.setRotInc (mpi_rotinc);
305
306   TimerCollectiveMPI timerScatter (mpiWorld.getComm());
307   ScatterProjectionsMPI (mpiWorld, projGlobal, projLocal, bOptDebug);
308   if (bOptVerbose)
309       timerScatter.timerEndAndReport ("Time to scatter projections");
310
311   if (mpiWorld.getRank() == 0) {
312     imGlobal = new ImageFile (nx, ny);
313   }
314
315   imLocal = new ImageFile (nx, ny);
316 #else
317   projGlobal.read (pszFilenameProj);
318   if (bOptVerbose) {
319     std::ostringstream os;
320     projGlobal.printScanInfo(os);
321     std::cout << os.str();
322   }
323
324   imGlobal = new ImageFile (nx, ny);
325 #endif
326
327 #ifdef HAVE_MPI
328   TimerCollectiveMPI timerReconstruct (mpiWorld.getComm());
329
330   Reconstructor reconstruct (projLocal, *imLocal, sOptFilterName.c_str(), dOptFilterParam, sOptFilterMethodName.c_str(), iOptZeropad, sOptFilterGenerationName.c_str(), sOptInterpName.c_str(), iOptPreinterpolationFactor, sOptBackprojectName.c_str(), optTrace);
331   if (reconstruct.fail()) {
332     std::cout << reconstruct.failMessage();
333     return (1);
334   }
335   reconstruct.reconstructAllViews();
336   
337   if (bOptVerbose)
338       timerReconstruct.timerEndAndReport ("Time to reconstruct");
339
340   TimerCollectiveMPI timerReduce (mpiWorld.getComm());
341   ReduceImageMPI (mpiWorld, imLocal, imGlobal);
342   if (bOptVerbose)
343       timerReduce.timerEndAndReport ("Time to reduce image");
344 #else
345   Reconstructor reconstruct (projGlobal, *imGlobal, sOptFilterName.c_str(), dOptFilterParam, sOptFilterMethodName.c_str(), iOptZeropad, sOptFilterGenerationName.c_str(), sOptInterpName.c_str(), iOptPreinterpolationFactor, sOptBackprojectName.c_str(), optTrace);
346   if (reconstruct.fail()) {
347     std::cout << reconstruct.failMessage();
348     return (1);
349   }
350   reconstruct.reconstructAllViews();
351 #endif
352
353 #ifdef HAVE_MPI
354   if (mpiWorld.getRank() == 0)
355 #endif
356     {
357       double dCalcTime = timerProgram.timerEnd();
358       imGlobal->labelAdd (projGlobal.getLabel());
359       imGlobal->labelAdd (Array2dFileLabel::L_HISTORY, sRemark.c_str(), dCalcTime);
360       imGlobal->fileWrite (pszFilenameImage);
361       if (bOptVerbose)
362         std::cout << "Run time: " << dCalcTime << " seconds" << std::endl;
363     }
364 #ifdef HAVE_MPI
365   MPI::Finalize();
366 #endif
367
368   return (0);
369 }
370
371
372 //////////////////////////////////////////////////////////////////////////////////////
373 // MPI Support Routines
374 //
375 //////////////////////////////////////////////////////////////////////////////////////
376
377 #ifdef HAVE_MPI
378 static void ScatterProjectionsMPI (MPIWorld& mpiWorld, Projections& projGlobal, Projections& projLocal, const bool bOptDebug)
379 {
380   if (mpiWorld.getRank() == 0) {
381     for (int iProc = 0; iProc < mpiWorld.getNumProcessors(); iProc++) {
382       for (int iw = mpiWorld.getStartWorkUnit(iProc); iw <= mpiWorld.getEndWorkUnit(iProc); iw++) {
383         DetectorArray& detarray = projGlobal.getDetectorArray( iw );
384         int nDet = detarray.nDet();
385         DetectorValue* detval = detarray.detValues();
386
387         double viewAngle = detarray.viewAngle();
388         mpiWorld.getComm().Send(&nDet, 1, MPI::INT, iProc, 0);
389         mpiWorld.getComm().Send(&viewAngle, 1, MPI::DOUBLE, iProc, 0);
390         mpiWorld.getComm().Send(detval, nDet, MPI::FLOAT, iProc, 0);
391       }
392     }
393   }
394
395   for (int iw = 0; iw < mpiWorld.getMyLocalWorkUnits(); iw++) {
396     MPI::Status status;
397     int nDet;
398     double viewAngle;
399     DetectorValue* detval = projLocal.getDetectorArray(iw).detValues();
400
401     mpiWorld.getComm().Recv(&nDet, 1, MPI::INT, 0, 0, status);
402     mpiWorld.getComm().Recv(&viewAngle, 1, MPI::DOUBLE, 0, 0, status);
403     mpiWorld.getComm().Recv(detval, nDet, MPI::FLOAT, 0, 0, status);
404     projLocal.getDetectorArray(iw).setViewAngle( viewAngle );
405   }
406 }
407
408 static void
409 ReduceImageMPI (MPIWorld& mpiWorld, ImageFile* imLocal, ImageFile* imGlobal)
410 {
411   ImageFileArray vLocal = imLocal->getArray();
412
413   for (unsigned int ix = 0; ix < imLocal->nx(); ix++) {
414     void *recvbuf = NULL;
415     if (mpiWorld.getRank() == 0) {
416       ImageFileArray vGlobal = imGlobal->getArray();
417       recvbuf = vGlobal[ix];
418     }
419     mpiWorld.getComm().Reduce (vLocal[ix], recvbuf, imLocal->ny(), imLocal->getMPIDataType(), MPI::SUM, 0);
420   }
421 }
422
423 #endif
424
425
426 #ifndef NO_MAIN
427 int 
428 main (int argc, char* argv[])
429 {
430   int retval = 1;
431
432   try {
433     retval = pjrec_main(argc, argv);
434   } catch (exception e) {
435           std::cerr << "Exception: " << e.what() << std::endl;
436   } catch (...) {
437           std::cerr << "Unknown exception" << std::endl;
438   }
439
440   return (retval);
441 }
442 #endif
443