r121: *** empty log message ***
[ctsim.git] / src / pjrec.cpp
diff --git a/src/pjrec.cpp b/src/pjrec.cpp
new file mode 100644 (file)
index 0000000..b5760a7
--- /dev/null
@@ -0,0 +1,376 @@
+/*****************************************************************************
+** FILE IDENTIFICATION
+**
+**   Name:          pjrec.cpp
+**   Purpose:       Reconstruct an image from projections
+**   Programmer:    Kevin Rosenberg
+**   Date Started:  Aug 1984
+**
+**  This is part of the CTSim program
+**  Copyright (C) 1983-2000 Kevin Rosenberg
+**
+**  $Id: pjrec.cpp,v 1.1 2000/06/26 21:15:24 kevin Exp $
+**
+**  This program is free software; you can redistribute it and/or modify
+**  it under the terms of the GNU General Public License (version 2) as
+**  published by the Free Software Foundation.
+**
+**  This program is distributed in the hope that it will be useful,
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+**  GNU General Public License for more details.
+**
+**  You should have received a copy of the GNU General Public License
+**  along with this program; if not, write to the Free Software
+**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+******************************************************************************/
+
+#include "ct.h"
+#include "timer.h"
+
+
+enum {O_INTERP, O_FILTER, O_FILTER_PARAM, O_BACKPROJ, O_VERBOSE, O_TRACE, O_HELP, O_DEBUG, O_VERSION};
+
+static struct option my_options[] =
+{
+  {"interp", 1, 0, O_INTERP},
+  {"filter", 1, 0, O_FILTER},
+  {"filter-param", 1, 0, O_FILTER_PARAM},
+  {"backproj", 1, 0, O_BACKPROJ},
+  {"trace", 1, 0, O_TRACE},
+  {"debug", 0, 0, O_DEBUG},
+  {"verbose", 0, 0, O_VERBOSE},
+  {"help", 0, 0, O_HELP},
+  {"version", 0, 0, O_VERSION},
+  {0, 0, 0, 0}
+};
+
+
+void 
+pjrec_usage (const char *program)
+{
+  cout << "usage: " << fileBasename(program) << " raysum-file image-file nx-image ny-image [OPTIONS]" << endl;
+  cout << "Image reconstruction from raysum projections" << endl;
+  cout << endl;
+  cout << "   raysum-file     Input raysum file" << endl;
+  cout << "   image-file      Output image file in SDF2D format" << endl;
+  cout << "   nx-image        Number of columns in output image" << endl;
+  cout << "   ny-image        Number of rows in output image" << endl;
+  cout << "   --interp        Interpolation method during backprojection" << endl;
+  cout << "       nearest     Nearest neighbor interpolation" << endl;
+  cout << "       linear      Linear interpolation" << endl;
+#if HAVE_BSPLINE_INTERP
+  cout << "       bspline     B-spline interpolation" << endl;
+#endif
+  cout << "    --filter       Filter name" << endl;
+  cout << "       abs_bandlimit Abs * Bandlimiting (default)" << endl;
+  cout << "       abs_sinc      Abs * Sinc" << endl;
+  cout << "       abs_cos       Abs * Cosine" << endl;
+  cout << "       abs_hamming   Abs * Hamming" << endl;
+  cout << "       shepp         Shepp-Logan" << endl;
+  cout << "       bandlimit     Bandlimiting" << endl;
+  cout << "       sinc          Sinc" << endl;
+  cout << "       cos           Cosine" << endl;
+  cout << "       triangle      Triangle" << endl;
+  cout << "       hamming       Hamming" << endl;
+  cout << "    --backproj     Backprojection Method" << endl;
+  cout << "       trig        Trigometric functions at every point" << endl;
+  cout << "       table       Trigometric functions with precalculated table" << endl;
+  cout << "       diff        Difference method" << endl;
+  cout << "       diff2       Optimized difference method (default)" << endl;
+  cout << "       idiff2      Optimized difference method with integer math" << endl;
+  cout << "    --filter-param Alpha level for Hamming filter" << endl;
+  cout << "    --trace        Set tracing to level" << endl;
+  cout << "         none      No tracing (default)" << endl;
+  cout << "         text      Text level tracing" << endl;
+  cout << "         phm       Trace phantom" << endl;
+  cout << "         rays      Trace allrays" << endl;
+  cout << "         plot      Trace plotting" << endl;
+  cout << "         clipping  Trace clipping" << endl;
+  cout << "    --verbose      Turn on verbose mode" << endl;
+  cout << "    --debug        Turn on debug mode" << endl;
+  cout << "    --version      Print version" << endl;
+  cout << "    --help         Print this help message" << endl;
+}
+
+
+#ifdef HAVE_MPI
+static void ScatterProjectionsMPI (MPIWorld& mpiWorld, Projections& projGlobal, Projections& projLocal, const int debug);
+static void ReduceImageMPI (MPIWorld& mpiWorld, ImageFile* imLocal, ImageFile* imGlobal);
+#endif
+
+
+int 
+pjrec_main (int argc, char * argv[])
+{
+  Projections projGlobal;
+  ImageFile* imGlobal = NULL;
+  char* filenameProj = NULL;
+  char* filenameImage = NULL;
+  string remark;
+  char *endptr;
+  int optVerbose = 0;
+  int optDebug = 0;
+  int optTrace = TRACE_NONE;
+  double optFilterParam = -1;
+  string optFilterName = SignalFilter::FILTER_ABS_BANDLIMIT_STR;
+  string optInterpName = Backprojector::INTERP_LINEAR_STR;
+  string optBackprojName = Backprojector::BPROJ_IDIFF2_STR;
+  //  string optFilterMethodName = SignalFilter::FILTER_METHOD_CONVOLUTION_STR;
+  int optInterpParam = 1;
+  int nx, ny;
+#ifdef HAVE_MPI
+  ImageFile* imLocal;
+  int mpi_nview, mpi_ndet;
+  double mpi_detinc, mpi_rotinc, mpi_phmlen;
+  MPIWorld mpiWorld (argc, argv);
+#endif
+
+  Timer timerProgram;
+
+#ifdef HAVE_MPI
+  if (mpiWorld.getRank() == 0) {
+#endif
+    while (1) {
+      int c = getopt_long(argc, argv, "", my_options, NULL);
+      char *endptr = NULL;
+      
+      if (c == -1)
+       break;
+      
+      switch (c)
+       {
+       case O_INTERP:
+         optInterpName = optarg;
+         break;
+       case O_FILTER:
+         optFilterName = optarg;
+         break;
+       case O_BACKPROJ:
+         optBackprojName = optarg;
+         break;
+       case O_FILTER_PARAM:
+         optFilterParam = strtod(optarg, &endptr);
+         if (endptr != optarg + strlen(optarg)) {
+           pjrec_usage(argv[0]);
+         }
+         break;
+       case O_VERBOSE:
+         optVerbose = 1;
+         break;
+       case O_DEBUG:
+         optDebug = 1;
+         break;
+       case O_TRACE:
+         if ((optTrace = opt_set_trace(optarg)) < 0) {
+           pjrec_usage(argv[0]);
+           return (1);
+         }
+         break;
+        case O_VERSION:
+#ifdef VERSION
+         cout <<  "Version " <<  VERSION << endl;
+#else
+          cout << "Unknown version number" << endl;
+#endif
+         return (0);
+       case O_HELP:
+       case '?':
+         pjrec_usage(argv[0]);
+         return (0);
+       default:
+         pjrec_usage(argv[0]);
+         return (1);
+       }
+    }
+  
+    if (optind + 4 != argc) {
+      pjrec_usage(argv[0]);
+      return (1);
+    }
+
+    filenameProj = argv[optind];
+  
+    filenameImage = argv[optind + 1];
+  
+    nx = strtol(argv[optind + 2], &endptr, 10);
+    ny = strtol(argv[optind + 3], &endptr, 10);
+  
+    ostringstream filterDesc;
+    if (optFilterParam >= 0)
+      filterDesc << optFilterName << ": alpha=" << optFilterParam; 
+    else
+      filterDesc << optFilterName;
+
+    ostringstream label;
+    label << "pjrec: " << nx << "x" << ny << ", " << filterDesc.str() << ", " << optInterpName << ", " << optBackprojName;
+    remark = label.str();
+  
+    if (optVerbose)
+      cout << "Remark: " << remark << endl;
+#ifdef HAVE_MPI
+  }
+#endif
+
+#ifdef HAVE_MPI
+  if (mpiWorld.getRank() == 0) {
+    projGlobal.read (filenameProj);
+    if (optVerbose)
+      projGlobal.printScanInfo();
+
+    mpi_ndet = projGlobal.nDet();
+    mpi_nview = projGlobal.nView();
+    mpi_detinc = projGlobal.detInc();
+    mpi_phmlen = projGlobal.phmLen();
+    mpi_rotinc = projGlobal.rotInc();
+  }
+
+  TimerCollectiveMPI timerBcast (mpiWorld.getComm());
+  mpiWorld.BcastString (optBackprojName);
+  mpiWorld.BcastString (optFilterName);
+  mpiWorld.BcastString (optInterpName);
+  mpiWorld.getComm().Bcast (&optVerbose, 1, MPI::INT, 0);
+  mpiWorld.getComm().Bcast (&optDebug, 1, MPI::INT, 0);
+  mpiWorld.getComm().Bcast (&optTrace, 1, MPI::INT, 0);
+  mpiWorld.getComm().Bcast (&optFilterParam, 1, MPI::DOUBLE, 0);
+  mpiWorld.getComm().Bcast (&optInterpParam, 1, MPI::INT, 0);
+  mpiWorld.getComm().Bcast (&mpi_ndet, 1, MPI::INT, 0);
+  mpiWorld.getComm().Bcast (&mpi_nview, 1, MPI::INT, 0);
+  mpiWorld.getComm().Bcast (&mpi_detinc, 1, MPI::DOUBLE, 0);
+  mpiWorld.getComm().Bcast (&mpi_phmlen, 1, MPI::DOUBLE, 0);
+  mpiWorld.getComm().Bcast (&mpi_rotinc, 1, MPI::DOUBLE, 0);
+  mpiWorld.getComm().Bcast (&nx, 1, MPI::INT, 0);
+  mpiWorld.getComm().Bcast (&ny, 1, MPI::INT, 0);
+  if (optVerbose)
+      timerBcast.timerEndAndReport ("Time to broadcast variables");
+
+  mpiWorld.setTotalWorkUnits (mpi_nview);
+
+  Projections projLocal (mpiWorld.getMyLocalWorkUnits(), mpi_ndet);
+  projLocal.setDetInc (mpi_detinc);
+  projLocal.setPhmLen (mpi_phmlen);
+  projLocal.setRotInc (mpi_rotinc);
+
+  TimerCollectiveMPI timerScatter (mpiWorld.getComm());
+  ScatterProjectionsMPI (mpiWorld, projGlobal, projLocal, optDebug);
+  if (optVerbose)
+      timerScatter.timerEndAndReport ("Time to scatter projections");
+
+  if (mpiWorld.getRank() == 0) {
+    imGlobal = new ImageFile (nx, ny);
+  }
+
+  imLocal = new ImageFile (nx, ny);
+#else
+  projGlobal.read (filenameProj);
+  if (optVerbose)
+    projGlobal.printScanInfo();
+
+  imGlobal = new ImageFile (nx, ny);
+#endif
+
+#ifdef HAVE_MPI
+  TimerCollectiveMPI timerReconstruct (mpiWorld.getComm());
+  projLocal.reconstruct (*imLocal, optFilterName.c_str(), optFilterParam, optInterpName.c_str(), optInterpParam, optBackprojName.c_str(), optTrace);
+  if (optVerbose)
+      timerReconstruct.timerEndAndReport ("Time to reconstruct");
+
+  TimerCollectiveMPI timerReduce (mpiWorld.getComm());
+  ReduceImageMPI (mpiWorld, imLocal, imGlobal);
+  if (optVerbose)
+      timerReduce.timerEndAndReport ("Time to reduce image");
+#else
+  projGlobal.reconstruct (*imGlobal, optFilterName.c_str(), optFilterParam, optInterpName.c_str(), optInterpParam, optBackprojName.c_str(), optTrace);
+#endif
+
+#ifdef HAVE_MPI
+  if (mpiWorld.getRank() == 0)
+#endif
+    {
+      double calcTime = timerProgram.timerEnd();
+      imGlobal->labelAdd (Array2dFileLabel::L_HISTORY, projGlobal.remark(), projGlobal.calcTime());
+      imGlobal->labelAdd (Array2dFileLabel::L_HISTORY, remark.c_str(), calcTime);
+      imGlobal->fileWrite (filenameImage);
+      if (optVerbose)
+       cout << "Run time: " << calcTime << " seconds" << endl;
+    }
+#ifdef HAVE_MPI
+  MPI::Finalize();
+#endif
+
+  return (0);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////
+// MPI Support Routines
+//
+//////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef HAVE_MPI
+static void ScatterProjectionsMPI (MPIWorld& mpiWorld, Projections& projGlobal, Projections& projLocal, const int optDebug)
+{
+  if (mpiWorld.getRank() == 0) {
+    for (int iProc = 0; iProc < mpiWorld.getNumProcessors(); iProc++) {
+      for (int iw = mpiWorld.getStartWorkUnit(iProc); iw <= mpiWorld.getEndWorkUnit(iProc); iw++) {
+       DetectorArray& detarray = projGlobal.getDetectorArray( iw );
+       int nDet = detarray.nDet();
+       DetectorValue* detval = detarray.detValues();
+
+       double viewAngle = detarray.viewAngle();
+       mpiWorld.getComm().Send(&nDet, 1, MPI::INT, iProc, 0);
+       mpiWorld.getComm().Send(&viewAngle, 1, MPI::DOUBLE, iProc, 0);
+       mpiWorld.getComm().Send(detval, nDet, MPI::FLOAT, iProc, 0);
+      }
+    }
+  }
+
+  for (int iw = 0; iw < mpiWorld.getMyLocalWorkUnits(); iw++) {
+    MPI::Status status;
+    int nDet;
+    double viewAngle;
+    DetectorValue* detval = projLocal.getDetectorArray(iw).detValues();
+
+    mpiWorld.getComm().Recv(&nDet, 1, MPI::INT, 0, 0, status);
+    mpiWorld.getComm().Recv(&viewAngle, 1, MPI::DOUBLE, 0, 0, status);
+    mpiWorld.getComm().Recv(detval, nDet, MPI::FLOAT, 0, 0, status);
+    projLocal.getDetectorArray(iw).setViewAngle( viewAngle );
+  }
+}
+
+static void
+ReduceImageMPI (MPIWorld& mpiWorld, ImageFile* imLocal, ImageFile* imGlobal)
+{
+  ImageFileArray vLocal = imLocal->getArray();
+
+  for (int ix = 0; ix < imLocal->nx(); ix++) {
+    void *recvbuf = NULL;
+    if (mpiWorld.getRank() == 0) {
+      ImageFileArray vGlobal = imGlobal->getArray();
+      recvbuf = vGlobal[ix];
+    }
+    mpiWorld.getComm().Reduce (vLocal[ix], recvbuf, imLocal->ny(), imLocal->getMPIDataType(), MPI::SUM, 0);
+  }
+}
+
+#endif
+
+
+#ifndef NO_MAIN
+int 
+main (int argc, char* argv[])
+{
+  int retval = 1;
+
+  try {
+    retval = pjrec_main(argc, argv);
+  } catch (exception e) {
+    cerr << "Exception: " << e.what() << endl;
+  } catch (...) {
+    cerr << "Unknown exception" << endl;
+  }
+
+  return (retval);
+}
+#endif
+