Add OpenMP for more backprojectors
[ctsim.git] / libctsim / backprojectors.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **   Name:         backprojectors.cpp         Classes for backprojection
5 **   Programmer:   Kevin Rosenberg
6 **   Date Started: June 2000
7 **
8 **  This is part of the CTSim program
9 **  Copyright (c) 1983-2009 Kevin Rosenberg
10 **
11 **  This program is free software; you can redistribute it and/or modify
12 **  it under the terms of the GNU General Public License (version 2) as
13 **  published by the Free Software Foundation.
14 **
15 **  This program is distributed in the hope that it will be useful,
16 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 **  GNU General Public License for more details.
19 **
20 **  You should have received a copy of the GNU General Public License
21 **  along with this program; if not, write to the Free Software
22 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 ******************************************************************************/
24
25 #include "ct.h"
26 #include "interpolator.h"
27
28 const int Backprojector::BPROJ_INVALID = -1;
29 const int Backprojector::BPROJ_TRIG = 0;
30 const int Backprojector::BPROJ_TABLE = 1;
31 const int Backprojector::BPROJ_DIFF = 2;
32 const int Backprojector::BPROJ_IDIFF = 3;
33
34 const char* const Backprojector::s_aszBackprojectName[] =
35 {
36   "trig",
37   "table",
38   "diff",
39   "idiff",
40 };
41
42 const char* const Backprojector::s_aszBackprojectTitle[] =
43 {
44   "Direct Trigometric",
45   "Trigometric Table",
46   "Difference Iteration",
47   "Integer Difference Iteration",
48 };
49
50 const int Backprojector::s_iBackprojectCount = sizeof(s_aszBackprojectName) / sizeof(const char*);
51
52 const int Backprojector::INTERP_INVALID = -1;
53 const int Backprojector::INTERP_NEAREST = 0;
54 const int Backprojector::INTERP_LINEAR = 1;
55 const int Backprojector::INTERP_CUBIC = 2;
56 const int Backprojector::INTERP_FREQ_PREINTERPOLATION = 3;
57 #if HAVE_BSPLINE_INTERP
58 const int Backprojector::INTERP_BSPLINE = 4;
59 const int Backprojector::INTERP_1BSPLINE = 5;
60 const int Backprojector::INTERP_2BSPLINE = 6;
61 const int Backprojector::INTERP_3BSPLINE = 7;
62 #endif
63
64 const char* const Backprojector::s_aszInterpName[] =
65 {
66   "nearest",
67   "linear",
68   "cubic",
69 #if HAVE_FREQ_PREINTERP
70   "freq_preinterpolationj",
71 #endif
72 #if HAVE_BSPLINE_INTERP
73   "bspline",
74   "1bspline",
75   "2bspline",
76   "3bspline",
77 #endif
78 };
79
80 const char* const Backprojector::s_aszInterpTitle[] =
81 {
82   "Nearest",
83   "Linear",
84   "Cubic",
85 #if HAVE_FREQ_PREINTERP
86   "Frequency Preinterpolation",
87 #endif
88 #if HAVE_BSPLINE_INTERP
89   "B-Spline",
90   "B-Spline 1st Order",
91   "B-Spline 2nd Order",
92   "B-Spline 3rd Order",
93 #endif
94 };
95
96 const int Backprojector::s_iInterpCount = sizeof(s_aszInterpName) / sizeof(const char*);
97
98
99
100 Backprojector::Backprojector (const Projections& proj, ImageFile& im, const char* const backprojName,
101                               const char* const interpName, const int interpFactor, const ReconstructionROI* pROI)
102 {
103   m_fail = false;
104   m_pBackprojectImplem = NULL;
105
106   initBackprojector (proj, im, backprojName, interpName, interpFactor, pROI);
107 }
108
109 void
110 Backprojector::BackprojectView (const double* const viewData, const double viewAngle)
111 {
112   if (m_pBackprojectImplem != NULL)
113     m_pBackprojectImplem->BackprojectView (viewData, viewAngle);
114 }
115
116 void
117 Backprojector::PostProcessing()
118 {
119   if (m_pBackprojectImplem != NULL)
120     m_pBackprojectImplem->PostProcessing();
121 }
122
123 Backprojector::~Backprojector ()
124 {
125   delete m_pBackprojectImplem;
126 }
127
128 // FUNCTION IDENTIFICATION
129 //     Backproject* projector = selectBackprojector (...)
130 //
131 // PURPOSE
132 //     Selects a backprojector based on BackprojType
133 //     and initializes the backprojector
134
135 bool
136 Backprojector::initBackprojector (const Projections& proj, ImageFile& im, const char* const backprojName,
137                                   const char* const interpName, const int interpFactor, const ReconstructionROI* pROI)
138 {
139   m_nameBackproject = backprojName;
140   m_nameInterpolation = interpName;
141   m_pBackprojectImplem = NULL;
142   m_idBackproject = convertBackprojectNameToID (backprojName);
143   if (m_idBackproject == BPROJ_INVALID) {
144     m_fail = true;
145     m_failMessage = "Invalid backprojection name ";
146     m_failMessage += backprojName;
147   }
148   m_idInterpolation = convertInterpNameToID (interpName);
149   if (m_idInterpolation == INTERP_INVALID) {
150     m_fail = true;
151     m_failMessage = "Invalid interpolation name ";
152     m_failMessage += interpName;
153   }
154
155   if (m_fail || m_idBackproject == BPROJ_INVALID || m_idInterpolation == INTERP_INVALID) {
156     m_fail = true;
157     return false;
158   }
159
160   if (proj.geometry() == Scanner::GEOMETRY_EQUILINEAR)
161     m_pBackprojectImplem = static_cast<Backproject*>(new BackprojectEquilinear(proj, im, m_idInterpolation, interpFactor, pROI));
162   else if (proj.geometry() == Scanner::GEOMETRY_EQUIANGULAR)
163     m_pBackprojectImplem = static_cast<Backproject*>(new BackprojectEquiangular(proj, im, m_idInterpolation, interpFactor, pROI));
164   else if (proj.geometry() == Scanner::GEOMETRY_PARALLEL) {
165     if (m_idBackproject == BPROJ_TRIG)
166       m_pBackprojectImplem = static_cast<Backproject*>(new BackprojectTrig (proj, im, m_idInterpolation, interpFactor, pROI));
167     else if (m_idBackproject == BPROJ_TABLE)
168       m_pBackprojectImplem = static_cast<Backproject*>(new BackprojectTable (proj, im, m_idInterpolation, interpFactor, pROI));
169     else if (m_idBackproject == BPROJ_DIFF)
170       m_pBackprojectImplem = static_cast<Backproject*>(new BackprojectDiff (proj, im, m_idInterpolation, interpFactor, pROI));
171     else if (m_idBackproject == BPROJ_IDIFF)
172       m_pBackprojectImplem = static_cast<Backproject*>(new BackprojectIntDiff (proj, im, m_idInterpolation, interpFactor, pROI));
173   } else {
174     m_fail = true;
175     m_failMessage = "Unable to select a backprojection method [Backprojector::initBackprojector]";
176     return false;
177   }
178
179   return true;
180 }
181
182
183 int
184 Backprojector::convertBackprojectNameToID (const char* const backprojName)
185 {
186   int backprojID = BPROJ_INVALID;
187
188   for (int i = 0; i < s_iBackprojectCount; i++) {
189     if (strcasecmp (backprojName, s_aszBackprojectName[i]) == 0) {
190       backprojID = i;
191       break;
192     }
193   }
194   return (backprojID);
195 }
196
197 const char*
198 Backprojector::convertBackprojectIDToName (int bprojID)
199 {
200   static const char *bprojName = "";
201
202   if (bprojID >= 0 && bprojID < s_iBackprojectCount)
203     return (s_aszBackprojectName[bprojID]);
204
205   return (bprojName);
206 }
207
208 const char*
209 Backprojector::convertBackprojectIDToTitle (const int bprojID)
210 {
211   static const char *bprojTitle = "";
212
213   if (bprojID >= 0 && bprojID < s_iBackprojectCount)
214     return (s_aszBackprojectTitle[bprojID]);
215
216   return (bprojTitle);
217 }
218
219
220 int
221 Backprojector::convertInterpNameToID (const char* const interpName)
222 {
223   int interpID = INTERP_INVALID;
224
225   for (int i = 0; i < s_iInterpCount; i++) {
226     if (strcasecmp (interpName, s_aszInterpName[i]) == 0) {
227       interpID = i;
228       break;
229     }
230   }
231   return (interpID);
232 }
233
234 const char*
235 Backprojector::convertInterpIDToName (const int interpID)
236 {
237   static const char *interpName = "";
238
239   if (interpID >= 0 && interpID < s_iInterpCount)
240     return (s_aszInterpName[interpID]);
241
242   return (interpName);
243 }
244
245 const char*
246 Backprojector::convertInterpIDToTitle (const int interpID)
247 {
248   static const char *interpTitle = "";
249
250   if (interpID >= 0 && interpID < s_iInterpCount)
251     return (s_aszInterpTitle[interpID]);
252
253   return (interpTitle);
254 }
255
256
257
258 // CLASS IDENTICATION
259 //   Backproject
260 //
261 // PURPOSE
262 //   Pure virtual base class for all backprojectors.
263
264 Backproject::Backproject (const Projections& proj, ImageFile& im, int interpType, const int interpFactor,
265                           const ReconstructionROI* pROI)
266 : proj(proj), im(im), interpType(interpType), m_interpFactor(interpFactor), m_bPostProcessingDone(false)
267 {
268   detInc = proj.detInc();
269   nDet = proj.nDet();
270   iDetCenter = (nDet - 1) / 2;  // index refering to L=0 projection
271
272   if (proj.geometry() == Scanner::GEOMETRY_PARALLEL)
273     rotScale = PI / proj.nView(); // scale by number of PI rotations
274   else if (proj.geometry() == Scanner::GEOMETRY_EQUIANGULAR || proj.geometry() == Scanner::GEOMETRY_EQUILINEAR)
275     rotScale =  (2 * PI) / proj.nView(); // scale by number of 2PI rotations
276   else
277     sys_error (ERR_SEVERE, "Invalid geometry type %d [Backproject::Backproject]", proj.geometry());
278
279   v = im.getArray();
280   nx = im.nx();
281   ny = im.ny();
282   im.arrayDataClear();
283
284   xMin = -proj.phmLen() / 2;      // Retangular coords of phantom
285   xMax = xMin + proj.phmLen();
286   yMin = -proj.phmLen() / 2;
287   yMax = yMin + proj.phmLen();
288
289   if (pROI) {
290     if (pROI->m_dXMin > xMin)
291       xMin = pROI->m_dXMin;
292     if (pROI->m_dXMax < xMax)
293       xMax = pROI->m_dXMax;
294     if (pROI->m_dYMin > yMin)
295       yMin = pROI->m_dYMin;
296     if (pROI->m_dYMax < yMax)
297       yMax = pROI->m_dYMax;
298
299     if (xMin > xMax) {
300       double temp = xMin;
301       xMin = xMax;
302       xMax = temp;
303     }
304     if (yMin > yMax) {
305       double temp = yMin;
306       yMin = yMax;
307       yMax = temp;
308     }
309   }
310
311   xInc = (xMax - xMin) / nx;    // size of cells
312   yInc = (yMax - yMin) / ny;
313
314   im.setAxisIncrement (xInc, yInc);
315   im.setAxisExtent (xMin, xMax, yMin, yMax);
316
317   m_dFocalLength = proj.focalLength();
318   m_dSourceDetectorLength = proj.sourceDetectorLength();
319 }
320
321 Backproject::~Backproject ()
322 {}
323
324 void
325 Backproject::PostProcessing()
326 {
327   m_bPostProcessingDone = true;
328 }
329
330 void
331 Backproject::ScaleImageByRotIncrement ()
332 {
333   for (int ix = 0; ix < nx; ix++)
334     for (int iy = 0; iy < ny; iy++)
335       v[ix][iy] *= rotScale;
336 }
337
338 void Backproject::errorIndexOutsideDetector (int ix, int iy, double theta, double r, double phi, double L, int iDetPos)
339 {
340   sys_error (ERR_WARNING, "r=%f, phi=%f", r, phi);
341   errorIndexOutsideDetector (ix, iy, theta, L, iDetPos);
342 }
343
344 void Backproject::errorIndexOutsideDetector (int ix, int iy, double theta, double L, int iDetPos)
345 {
346 #if 1
347   std::ostringstream os;
348   os << "ix=" << ix << ", iy=" << iy << ", theta=" << theta << ", L=" << L << ", detinc=" << detInc << "\n";
349   os << "ndet=" << nDet << ", detInc=" << detInc << ", iDetCenter=" << iDetCenter << "\n";
350   os << "xMin=" << xMin << ", xMax=" << xMax << ", xInc=" << xInc << "\n";
351   os << "yMin=" << yMin << ", yMax=" << yMax << ", yInc=" << yInc << "\n";
352   os << "iDetPos index outside bounds: " << iDetPos << " [backprojector]";;
353
354   sys_error (ERR_WARNING, os.str().c_str());
355 #endif
356 }
357
358
359 // CLASS IDENTICATION
360 //   BackprojectTrig
361 //
362 // PURPOSE
363 //   Uses trigometric functions at each point in image for backprojection.
364
365 void
366 BackprojectTrig::BackprojectView (const double* const filteredProj, const double view_angle)
367 {
368   double theta = view_angle;
369
370   CubicPolyInterpolator* pCubicInterp = NULL;
371   if (interpType == Backprojector::INTERP_CUBIC)
372     pCubicInterp = new CubicPolyInterpolator (filteredProj, nDet);
373
374   double xstart = xMin + xInc / 2;   // Rectang coords of center of pixel
375 #if HAVE_OPENMP
376   #pragma omp parallel for
377 #endif
378   for (int ix = 0; ix < nx; ix++) {
379     double x = xstart + (ix * xInc);
380
381     double y = yMin + yInc / 2;
382     for (int iy = 0; iy < ny; y += yInc, iy++) {
383       double r = sqrt (x * x + y * y);   // distance of cell from center
384       double phi = atan2 (y, x);         // angle of cell from center
385       double L = r * cos (theta - phi);  // position on detector
386
387       if (interpType == Backprojector::INTERP_NEAREST) {
388         int iDetPos = iDetCenter + nearest<int> (L / detInc); // calc'd index in the filter raysum array
389
390         if (iDetPos >= 0 && iDetPos < nDet)
391           v[ix][iy] += rotScale * filteredProj[iDetPos];
392       } else if (interpType == Backprojector::INTERP_LINEAR) {
393         double p = L / detInc;  // position along detector
394         double pFloor = floor (p);
395         int iDetPos = iDetCenter + static_cast<int>(pFloor);
396         double frac = p - pFloor;       // fraction distance from det
397         if (iDetPos >= 0 && iDetPos < nDet - 1)
398           v[ix][iy] += rotScale * ((1-frac) * filteredProj[iDetPos] + frac * filteredProj[iDetPos+1]);
399       } else if (interpType == Backprojector::INTERP_CUBIC) {
400         double p = iDetCenter + (L / detInc);   // position along detector
401         if (p >= 0 && p < nDet)
402           v[ix][iy] += rotScale * pCubicInterp->interpolate (p);
403       }
404     }
405   }
406
407   if (interpType == Backprojector::INTERP_CUBIC)
408     delete pCubicInterp;
409 }
410
411
412 // CLASS IDENTICATION
413 //   BackprojectTable
414 //
415 // PURPOSE
416 //   Precalculates trigometric function value for each point in image for backprojection.
417
418 BackprojectTable::BackprojectTable (const Projections& proj, ImageFile& im, int interpType,
419                                     const int interpFactor, const ReconstructionROI* pROI)
420 : Backproject (proj, im, interpType, interpFactor, pROI)
421 {
422   arrayR.initSetSize (im.nx(), im.ny());
423   arrayPhi.initSetSize (im.nx(), im.ny());
424   r = arrayR.getArray();
425   phi = arrayPhi.getArray();
426
427   double xstart = xMin + xInc / 2;
428
429 #if HAVE_OPENMP
430   #pragma omp parallel for
431 #endif
432   for (int ix = 0; ix < nx; ix++) {
433     double x = xstart + (ix * xInc);
434     double y = yMin + yInc / 2;
435     for (int iy = 0; iy < ny; iy++, y += yInc) {
436       r[ix][iy] = sqrt (x * x + y * y);
437       phi[ix][iy] = atan2 (y, x);
438     }
439   }
440 }
441
442 BackprojectTable::~BackprojectTable ()
443 {
444 }
445
446 void
447 BackprojectTable::PostProcessing()
448 {
449   if (! m_bPostProcessingDone) {
450     ScaleImageByRotIncrement();
451     m_bPostProcessingDone = true;
452   }
453 }
454
455 void
456 BackprojectTable::BackprojectView (const double* const filteredProj, const double view_angle)
457 {
458   double theta = view_angle;
459
460   CubicPolyInterpolator* pCubicInterp = NULL;
461   if (interpType == Backprojector::INTERP_CUBIC)
462     pCubicInterp = new CubicPolyInterpolator (filteredProj, nDet);
463
464 #if HAVE_OPENMP
465   #pragma omp parallel for
466 #endif
467   for (int ix = 0; ix < nx; ix++) {
468     ImageFileColumn pImCol = v[ix];
469
470     for (int iy = 0; iy < ny; iy++) {
471       double L = r[ix][iy] * cos (theta - phi[ix][iy]);
472
473       if (interpType == Backprojector::INTERP_NEAREST) {
474         int iDetPos = iDetCenter + nearest<int>(L / detInc);    // calc index in the filtered raysum vector
475
476         if (iDetPos >= 0 && iDetPos < nDet) {
477           pImCol[iy] += filteredProj[iDetPos];
478         }
479       } else if (interpType == Backprojector::INTERP_LINEAR) {
480         double dPos = L / detInc;               // position along detector
481         double dPosFloor = floor (dPos);
482         int iDetPos = iDetCenter + static_cast<int>(dPosFloor);
483         double frac = dPos - dPosFloor; // fraction distance from det
484         if (iDetPos >= 0 && iDetPos < nDet - 1) {
485           pImCol[iy] += ((1-frac) * filteredProj[iDetPos] + frac * filteredProj[iDetPos+1]);
486         }
487       } else if (interpType == Backprojector::INTERP_CUBIC) {
488         double p = iDetCenter + (L / detInc);   // position along detector
489         if (p >= 0 && p < nDet) {
490           pImCol[iy] += pCubicInterp->interpolate (p);
491         }
492       }
493     }   // end for y
494   }     // end for x
495
496   if (interpType == Backprojector::INTERP_CUBIC)
497     delete pCubicInterp;
498 }
499
500
501 // CLASS IDENTICATION
502 //   BackprojectDiff
503 //
504 // PURPOSE
505 //   Backprojects by precalculating the change in L position for each x & y step in the image.
506 //   Iterates in x & y direction by adding difference in L position
507
508 BackprojectDiff::BackprojectDiff (const Projections& proj, ImageFile& im, int interpType,
509                                   const int interpFactor, const ReconstructionROI* pROI)
510 :  Backproject (proj, im, interpType, interpFactor, pROI)
511 {
512   // calculate center of first pixel v[0][0]
513   double x = xMin + xInc / 2;
514   double y = yMin + yInc / 2;
515   start_r = sqrt (x * x + y * y);
516   start_phi = atan2 (y, x);
517
518   im.arrayDataClear();
519 }
520
521 BackprojectDiff::~BackprojectDiff ()
522 {
523 }
524
525 void
526 BackprojectDiff::PostProcessing()
527 {
528   if (! m_bPostProcessingDone) {
529     ScaleImageByRotIncrement();
530     m_bPostProcessingDone = true;
531   }
532 }
533
534 void
535 BackprojectDiff::BackprojectView (const double* const filteredProj, const double view_angle)
536 {
537   double theta = view_angle;
538
539   // Distance between detectors for an angle given in units of detectors
540   double det_dx = xInc * cos (theta) / detInc;
541   double det_dy = yInc * sin (theta) / detInc;
542
543   // calculate detPosition for first point in image (ix=0, iy=0)
544   double detPosColBase = iDetCenter + start_r * cos (theta - start_phi) / detInc;
545
546   CubicPolyInterpolator* pCubicInterp = NULL;
547   double* deltaFilteredProj = NULL;
548   if (interpType == Backprojector::INTERP_LINEAR) {
549     // precalculate scaled difference for linear interpolation
550     deltaFilteredProj = new double [nDet];
551     for (int i = 0; i < nDet - 1; i++)
552       deltaFilteredProj[i] = filteredProj[i+1] - filteredProj[i];
553     deltaFilteredProj[nDet - 1] = 0;  // last detector
554   } else if (interpType == Backprojector::INTERP_CUBIC) {
555     pCubicInterp = new CubicPolyInterpolator (filteredProj, nDet);
556   }
557
558   int iLastDet = nDet - 1;
559
560 #if HAVE_OPENMP
561   #pragma omp parallel for
562 #endif
563   for (int ix = 0; ix < nx; ix++) {
564     double detPos = detPosColBase + (ix * det_dx);
565     ImageFileColumn pImCol = v[ix];
566
567     for (int iy = 0; iy < ny; iy++, detPos += det_dy) {
568       if (interpType == Backprojector::INTERP_NEAREST) {
569         int iDetPos = nearest<int> (detPos); // calc index in the filtered raysum vector
570         if (iDetPos >= 0 && iDetPos < nDet) {
571           *pImCol++ += filteredProj[iDetPos];
572         }
573       } else if (interpType == Backprojector::INTERP_LINEAR) {
574         double detPosFloor = floor (detPos);
575         int iDetPos = static_cast<int>(detPosFloor);
576         double frac = detPos - detPosFloor;  // fraction distance from det
577         if (iDetPos >= 0 && iDetPos <= iLastDet) {
578           *pImCol++ += filteredProj[iDetPos] + (frac * deltaFilteredProj[iDetPos]);
579         }
580       } else if (interpType == Backprojector::INTERP_CUBIC) {
581         double p = detPos;   // position along detector
582         if (p >= 0 && p < nDet) {
583           *pImCol++  += pCubicInterp->interpolate (p);
584         }
585       }
586     }   // end for y
587   }     // end for x
588
589   if (interpType == Backprojector::INTERP_LINEAR)
590     delete deltaFilteredProj;
591   else if (interpType == Backprojector::INTERP_CUBIC)
592     delete pCubicInterp;
593 }
594
595
596 // CLASS IDENTICATION
597 //   BackprojectIntDiff
598 //
599 // PURPOSE
600 //   Highly optimized and integer version of BackprojectDiff
601
602 void
603 BackprojectIntDiff::BackprojectView (const double* const filteredProj, const double view_angle)
604 {
605   double theta = view_angle;  // add half PI to view angle to get perpendicular theta angle
606 #if SIZEOF_LONG == 4
607   static const int scaleShift = 16;
608 #elif SIZEOF_LONG == 8
609   static const int scaleShift = 32;
610 #endif
611   static const long scale = (1L << scaleShift);
612   static const long scaleBitmask = scale - 1;
613   static const long halfScale = scale / 2;
614   static const double dInvScale = 1. / scale;
615
616   const long det_dx = nearest<long> (xInc * cos (theta) / detInc * scale);
617   const long det_dy = nearest<long> (yInc * sin (theta) / detInc * scale);
618
619   // calculate L for first point in image (0, 0)
620   long detPosColBase = nearest<long> ((start_r * cos (theta - start_phi) / detInc + iDetCenter) * scale);
621
622   double* deltaFilteredProj = NULL;
623   CubicPolyInterpolator* pCubicInterp = NULL;
624   if (interpType == Backprojector::INTERP_LINEAR) {
625     // precalculate scaled difference for linear interpolation
626     deltaFilteredProj = new double [nDet];
627     for (int i = 0; i < nDet - 1; i++)
628       deltaFilteredProj[i] = (filteredProj[i+1] - filteredProj[i]) * dInvScale;
629     deltaFilteredProj[nDet - 1] = 0;  // last detector
630   } else if (interpType == Backprojector::INTERP_CUBIC) {
631     pCubicInterp = new CubicPolyInterpolator (filteredProj, nDet);
632   }
633
634   int iLastDet = nDet - 1;
635 #if HAVE_OPENMP
636   #pragma omp parallel for
637 #endif
638   for (int ix = 0; ix < nx; ix++) {
639     long detPos = detPosColBase + (ix * det_dx);
640     ImageFileColumn pImCol = v[ix];
641
642     for (int iy = 0; iy < ny; iy++, detPos += det_dy) {
643       if (interpType == Backprojector::INTERP_NEAREST) {
644         const int iDetPos = (detPos + halfScale) >> scaleShift;
645         if (iDetPos >= 0 && iDetPos <= iLastDet) {
646           *pImCol++ += filteredProj[iDetPos];
647         } else
648           pImCol++;
649       } else if (interpType == Backprojector::INTERP_FREQ_PREINTERPOLATION) {
650         const int iDetPos = ((detPos + halfScale) >> scaleShift) * m_interpFactor;
651         if (iDetPos >= 0 && iDetPos <= iLastDet) {
652           *pImCol++ += filteredProj[iDetPos];
653         } else
654           pImCol++;
655       } else if (interpType == Backprojector::INTERP_LINEAR) {
656         const long iDetPos = detPos >> scaleShift;
657         if (iDetPos >= 0 && iDetPos <= iLastDet) {
658           const long detRemainder = detPos & scaleBitmask;
659           *pImCol++ += filteredProj[iDetPos] + (detRemainder * deltaFilteredProj[iDetPos]);
660         } else
661           pImCol++;
662       } else if (interpType == Backprojector::INTERP_CUBIC) {
663         *pImCol++ += pCubicInterp->interpolate (static_cast<double>(detPos) / scale);
664       } // end Cubic
665     } // end for iy
666   } // end for ix
667
668   if (interpType == Backprojector::INTERP_LINEAR)
669     delete deltaFilteredProj;
670   else if (interpType == Backprojector::INTERP_CUBIC)
671     delete pCubicInterp;
672 }
673
674
675 void
676 BackprojectEquiangular::BackprojectView (const double* const filteredProj, const double view_angle)
677 {
678   double beta = view_angle;
679
680   CubicPolyInterpolator* pCubicInterp = NULL;
681   if (interpType == Backprojector::INTERP_CUBIC)
682     pCubicInterp = new CubicPolyInterpolator (filteredProj, nDet);
683
684 #if HAVE_OPENMP
685   #pragma omp parallel for
686 #endif
687   for (int ix = 0; ix < nx; ix++) {
688     ImageFileColumn pImCol = v[ix];
689
690     for (int iy = 0; iy < ny; iy++) {
691       double dAngleDiff = beta - phi[ix][iy];
692       double rcos_t = r[ix][iy] * cos (dAngleDiff);
693       double rsin_t = r[ix][iy] * sin (dAngleDiff);
694       double dFLPlusSin = m_dFocalLength + rsin_t;
695       double gamma =  atan (rcos_t / dFLPlusSin);
696       double dPos = gamma / detInc;  // position along detector
697       double dL2 = dFLPlusSin * dFLPlusSin + (rcos_t * rcos_t);
698
699       if (interpType == Backprojector::INTERP_NEAREST) {
700         int iDetPos = iDetCenter + nearest<int>(dPos);  // calc index in the filtered raysum vector
701         if (iDetPos >= 0 && iDetPos < nDet)
702           pImCol[iy] += filteredProj[iDetPos] / dL2;
703       } else if (interpType == Backprojector::INTERP_LINEAR) {
704         double dPosFloor = floor (dPos);
705         int iDetPos = iDetCenter + static_cast<int>(dPosFloor);
706         double frac = dPos - dPosFloor; // fraction distance from det
707         if (iDetPos >= 0 && iDetPos < nDet - 1)
708           pImCol[iy] += (filteredProj[iDetPos] + frac * (filteredProj[iDetPos+1] - filteredProj[iDetPos])) / dL2;
709       } else if (interpType == Backprojector::INTERP_CUBIC) {
710         double d = iDetCenter + dPos;           // position along detector
711         if (d >= 0 && d < nDet)
712           pImCol[iy] += pCubicInterp->interpolate (d) / dL2;
713       }
714     }   // end for y
715   }     // end for x
716
717   if (interpType == Backprojector::INTERP_CUBIC)
718     delete pCubicInterp;
719 }
720
721 void
722 BackprojectEquilinear::BackprojectView (const double* const filteredProj, const double view_angle)
723 {
724   double beta = view_angle;
725
726   CubicPolyInterpolator* pCubicInterp = NULL;
727   if (interpType == Backprojector::INTERP_CUBIC)
728     pCubicInterp = new CubicPolyInterpolator (filteredProj, nDet);
729
730 #if HAVE_OPENMP
731   #pragma omp parallel for
732 #endif
733   for (int ix = 0; ix < nx; ix++) {
734     ImageFileColumn pImCol = v[ix];
735
736     for (int iy = 0; iy < ny; iy++) {
737       double dAngleDiff = beta - phi[ix][iy];
738       double rcos_t = r[ix][iy] * cos (dAngleDiff);
739       double rsin_t = r[ix][iy] * sin (dAngleDiff);
740
741       double dU = (m_dFocalLength + rsin_t) / m_dFocalLength;
742       double dU2 = dU * dU;
743
744       double dDetPos =  rcos_t / dU;
745       // Scale for imaginary detector that passes through origin of phantom, see Kak-Slaney Figure 3.22.
746       dDetPos *= m_dSourceDetectorLength / m_dFocalLength;
747       double dPos = dDetPos / detInc;  // position along detector array
748
749       if (interpType == Backprojector::INTERP_NEAREST) {
750         int iDetPos = iDetCenter + nearest<int>(dPos);  // calc index in the filtered raysum vector
751         if (iDetPos >= 0 && iDetPos < nDet)
752           pImCol[iy] += filteredProj[iDetPos] / dU2;
753       } else if (interpType == Backprojector::INTERP_LINEAR) {
754         double dPosFloor = floor (dPos);
755         int iDetPos = iDetCenter + static_cast<int>(dPosFloor);
756         double frac = dPos - dPosFloor; // fraction distance from det
757         if (iDetPos >= 0 && iDetPos < nDet - 1)
758           pImCol[iy] += (filteredProj[iDetPos] + frac * (filteredProj[iDetPos+1] - filteredProj[iDetPos])) / dU2;
759       } else if (interpType == Backprojector::INTERP_CUBIC) {
760         double d = iDetCenter + dPos;           // position along detector
761         if (d >= 0 && d < nDet)
762           pImCol[iy] += pCubicInterp->interpolate (d) / dU2;
763       }
764     }   // end for y
765   }     // end for x
766
767   if (interpType == Backprojector::INTERP_CUBIC)
768     delete pCubicInterp;
769 }
770