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