e4a240824393f9186c5a7c259cf48ef996d503f3
[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 #if HAVE_OPENMP
334   #pragma omp parallel for
335 #endif
336   for (int ix = 0; ix < nx; ix++)
337     for (int iy = 0; iy < ny; iy++)
338       v[ix][iy] *= rotScale;
339 }
340
341 void Backproject::errorIndexOutsideDetector (int ix, int iy, double theta, double r, double phi, double L, int iDetPos)
342 {
343   sys_error (ERR_WARNING, "r=%f, phi=%f", r, phi);
344   errorIndexOutsideDetector (ix, iy, theta, L, iDetPos);
345 }
346
347 void Backproject::errorIndexOutsideDetector (int ix, int iy, double theta, double L, int iDetPos)
348 {
349 #if 1
350   std::ostringstream os;
351   os << "ix=" << ix << ", iy=" << iy << ", theta=" << theta << ", L=" << L << ", detinc=" << detInc << "\n";
352   os << "ndet=" << nDet << ", detInc=" << detInc << ", iDetCenter=" << iDetCenter << "\n";
353   os << "xMin=" << xMin << ", xMax=" << xMax << ", xInc=" << xInc << "\n";
354   os << "yMin=" << yMin << ", yMax=" << yMax << ", yInc=" << yInc << "\n";
355   os << "iDetPos index outside bounds: " << iDetPos << " [backprojector]";;
356
357   sys_error (ERR_WARNING, os.str().c_str());
358 #endif
359 }
360
361
362 // CLASS IDENTICATION
363 //   BackprojectTrig
364 //
365 // PURPOSE
366 //   Uses trigometric functions at each point in image for backprojection.
367
368 void
369 BackprojectTrig::BackprojectView (const double* const filteredProj, const double view_angle)
370 {
371   double theta = view_angle;
372
373   CubicPolyInterpolator* pCubicInterp = NULL;
374   if (interpType == Backprojector::INTERP_CUBIC)
375     pCubicInterp = new CubicPolyInterpolator (filteredProj, nDet);
376
377   double xstart = xMin + xInc / 2;   // Rectang coords of center of pixel
378 #if HAVE_OPENMP
379   #pragma omp parallel for
380 #endif
381   for (int ix = 0; ix < nx; ix++) {
382     double x = xstart + (ix * xInc);
383
384     double y = yMin + yInc / 2;
385     for (int iy = 0; iy < ny; y += yInc, iy++) {
386       double r = sqrt (x * x + y * y);   // distance of cell from center
387       double phi = atan2 (y, x);         // angle of cell from center
388       double L = r * cos (theta - phi);  // position on detector
389
390       if (interpType == Backprojector::INTERP_NEAREST) {
391         int iDetPos = iDetCenter + nearest<int> (L / detInc); // calc'd index in the filter raysum array
392
393         if (iDetPos >= 0 && iDetPos < nDet)
394           v[ix][iy] += rotScale * filteredProj[iDetPos];
395       } else if (interpType == Backprojector::INTERP_LINEAR) {
396         double p = L / detInc;  // position along detector
397         double pFloor = floor (p);
398         int iDetPos = iDetCenter + static_cast<int>(pFloor);
399         double frac = p - pFloor;       // fraction distance from det
400         if (iDetPos >= 0 && iDetPos < nDet - 1)
401           v[ix][iy] += rotScale * ((1-frac) * filteredProj[iDetPos] + frac * filteredProj[iDetPos+1]);
402       } else if (interpType == Backprojector::INTERP_CUBIC) {
403         double p = iDetCenter + (L / detInc);   // position along detector
404         if (p >= 0 && p < nDet)
405           v[ix][iy] += rotScale * pCubicInterp->interpolate (p);
406       }
407     }
408   }
409
410   if (interpType == Backprojector::INTERP_CUBIC)
411     delete pCubicInterp;
412 }
413
414
415 // CLASS IDENTICATION
416 //   BackprojectTable
417 //
418 // PURPOSE
419 //   Precalculates trigometric function value for each point in image for backprojection.
420
421 BackprojectTable::BackprojectTable (const Projections& proj, ImageFile& im, int interpType,
422                                     const int interpFactor, const ReconstructionROI* pROI)
423 : Backproject (proj, im, interpType, interpFactor, pROI)
424 {
425   arrayR.initSetSize (im.nx(), im.ny());
426   arrayPhi.initSetSize (im.nx(), im.ny());
427   r = arrayR.getArray();
428   phi = arrayPhi.getArray();
429
430   double xstart = xMin + xInc / 2;
431
432 #if HAVE_OPENMP
433   #pragma omp parallel for
434 #endif
435   for (int ix = 0; ix < nx; ix++) {
436     double x = xstart + (ix * xInc);
437     double y = yMin + yInc / 2;
438     for (int iy = 0; iy < ny; iy++, y += yInc) {
439       r[ix][iy] = sqrt (x * x + y * y);
440       phi[ix][iy] = atan2 (y, x);
441     }
442   }
443 }
444
445 BackprojectTable::~BackprojectTable ()
446 {
447 }
448
449 void
450 BackprojectTable::PostProcessing()
451 {
452   if (! m_bPostProcessingDone) {
453     ScaleImageByRotIncrement();
454     m_bPostProcessingDone = true;
455   }
456 }
457
458 void
459 BackprojectTable::BackprojectView (const double* const filteredProj, const double view_angle)
460 {
461   double theta = view_angle;
462
463   CubicPolyInterpolator* pCubicInterp = NULL;
464   if (interpType == Backprojector::INTERP_CUBIC)
465     pCubicInterp = new CubicPolyInterpolator (filteredProj, nDet);
466
467 #if HAVE_OPENMP
468   #pragma omp parallel for
469 #endif
470   for (int ix = 0; ix < nx; ix++) {
471     ImageFileColumn pImCol = v[ix];
472
473     for (int iy = 0; iy < ny; iy++) {
474       double L = r[ix][iy] * cos (theta - phi[ix][iy]);
475
476       if (interpType == Backprojector::INTERP_NEAREST) {
477         int iDetPos = iDetCenter + nearest<int>(L / detInc);    // calc index in the filtered raysum vector
478
479         if (iDetPos >= 0 && iDetPos < nDet) {
480           pImCol[iy] += filteredProj[iDetPos];
481         }
482       } else if (interpType == Backprojector::INTERP_LINEAR) {
483         double dPos = L / detInc;               // position along detector
484         double dPosFloor = floor (dPos);
485         int iDetPos = iDetCenter + static_cast<int>(dPosFloor);
486         double frac = dPos - dPosFloor; // fraction distance from det
487         if (iDetPos >= 0 && iDetPos < nDet - 1) {
488           pImCol[iy] += ((1-frac) * filteredProj[iDetPos] + frac * filteredProj[iDetPos+1]);
489         }
490       } else if (interpType == Backprojector::INTERP_CUBIC) {
491         double p = iDetCenter + (L / detInc);   // position along detector
492         if (p >= 0 && p < nDet) {
493           pImCol[iy] += pCubicInterp->interpolate (p);
494         }
495       }
496     }   // end for y
497   }     // end for x
498
499   if (interpType == Backprojector::INTERP_CUBIC)
500     delete pCubicInterp;
501 }
502
503
504 // CLASS IDENTICATION
505 //   BackprojectDiff
506 //
507 // PURPOSE
508 //   Backprojects by precalculating the change in L position for each x & y step in the image.
509 //   Iterates in x & y direction by adding difference in L position
510
511 BackprojectDiff::BackprojectDiff (const Projections& proj, ImageFile& im, int interpType,
512                                   const int interpFactor, const ReconstructionROI* pROI)
513 :  Backproject (proj, im, interpType, interpFactor, pROI)
514 {
515   // calculate center of first pixel v[0][0]
516   double x = xMin + xInc / 2;
517   double y = yMin + yInc / 2;
518   start_r = sqrt (x * x + y * y);
519   start_phi = atan2 (y, x);
520
521   im.arrayDataClear();
522 }
523
524 BackprojectDiff::~BackprojectDiff ()
525 {
526 }
527
528 void
529 BackprojectDiff::PostProcessing()
530 {
531   if (! m_bPostProcessingDone) {
532     ScaleImageByRotIncrement();
533     m_bPostProcessingDone = true;
534   }
535 }
536
537 void
538 BackprojectDiff::BackprojectView (const double* const filteredProj, const double view_angle)
539 {
540   double theta = view_angle;
541
542   // Distance between detectors for an angle given in units of detectors
543   double det_dx = xInc * cos (theta) / detInc;
544   double det_dy = yInc * sin (theta) / detInc;
545
546   // calculate detPosition for first point in image (ix=0, iy=0)
547   double detPosColBase = iDetCenter + start_r * cos (theta - start_phi) / detInc;
548
549   CubicPolyInterpolator* pCubicInterp = NULL;
550   double* deltaFilteredProj = NULL;
551   if (interpType == Backprojector::INTERP_LINEAR) {
552     // precalculate scaled difference for linear interpolation
553     deltaFilteredProj = new double [nDet];
554     for (int i = 0; i < nDet - 1; i++)
555       deltaFilteredProj[i] = filteredProj[i+1] - filteredProj[i];
556     deltaFilteredProj[nDet - 1] = 0;  // last detector
557   } else if (interpType == Backprojector::INTERP_CUBIC) {
558     pCubicInterp = new CubicPolyInterpolator (filteredProj, nDet);
559   }
560
561   int iLastDet = nDet - 1;
562
563 #if HAVE_OPENMP
564   #pragma omp parallel for
565 #endif
566   for (int ix = 0; ix < nx; ix++) {
567     double detPos = detPosColBase + (ix * det_dx);
568     ImageFileColumn pImCol = v[ix];
569
570     for (int iy = 0; iy < ny; iy++, detPos += det_dy) {
571       if (interpType == Backprojector::INTERP_NEAREST) {
572         int iDetPos = nearest<int> (detPos); // calc index in the filtered raysum vector
573         if (iDetPos >= 0 && iDetPos < nDet) {
574           *pImCol++ += filteredProj[iDetPos];
575         }
576       } else if (interpType == Backprojector::INTERP_LINEAR) {
577         double detPosFloor = floor (detPos);
578         int iDetPos = static_cast<int>(detPosFloor);
579         double frac = detPos - detPosFloor;  // fraction distance from det
580         if (iDetPos >= 0 && iDetPos <= iLastDet) {
581           *pImCol++ += filteredProj[iDetPos] + (frac * deltaFilteredProj[iDetPos]);
582         }
583       } else if (interpType == Backprojector::INTERP_CUBIC) {
584         double p = detPos;   // position along detector
585         if (p >= 0 && p < nDet) {
586           *pImCol++  += pCubicInterp->interpolate (p);
587         }
588       }
589     }   // end for y
590   }     // end for x
591
592   if (interpType == Backprojector::INTERP_LINEAR)
593     delete deltaFilteredProj;
594   else if (interpType == Backprojector::INTERP_CUBIC)
595     delete pCubicInterp;
596 }
597
598
599 // CLASS IDENTICATION
600 //   BackprojectIntDiff
601 //
602 // PURPOSE
603 //   Highly optimized and integer version of BackprojectDiff
604
605 void
606 BackprojectIntDiff::BackprojectView (const double* const filteredProj, const double view_angle)
607 {
608   double theta = view_angle;  // add half PI to view angle to get perpendicular theta angle
609 #if SIZEOF_LONG == 4
610   static const int scaleShift = 16;
611 #elif SIZEOF_LONG == 8
612   static const int scaleShift = 32;
613 #endif
614   static const long scale = (1L << scaleShift);
615   static const long scaleBitmask = scale - 1;
616   static const long halfScale = scale / 2;
617   static const double dInvScale = 1. / scale;
618
619   const long det_dx = nearest<long> (xInc * cos (theta) / detInc * scale);
620   const long det_dy = nearest<long> (yInc * sin (theta) / detInc * scale);
621
622   // calculate L for first point in image (0, 0)
623   long detPosColBase = nearest<long> ((start_r * cos (theta - start_phi) / detInc + iDetCenter) * scale);
624
625   double* deltaFilteredProj = NULL;
626   CubicPolyInterpolator* pCubicInterp = NULL;
627   if (interpType == Backprojector::INTERP_LINEAR) {
628     // precalculate scaled difference for linear interpolation
629     deltaFilteredProj = new double [nDet];
630     for (int i = 0; i < nDet - 1; i++)
631       deltaFilteredProj[i] = (filteredProj[i+1] - filteredProj[i]) * dInvScale;
632     deltaFilteredProj[nDet - 1] = 0;  // last detector
633   } else if (interpType == Backprojector::INTERP_CUBIC) {
634     pCubicInterp = new CubicPolyInterpolator (filteredProj, nDet);
635   }
636
637   int iLastDet = nDet - 1;
638 #if HAVE_OPENMP
639   #pragma omp parallel for
640 #endif
641   for (int ix = 0; ix < nx; ix++) {
642     long detPos = detPosColBase + (ix * det_dx);
643     ImageFileColumn pImCol = v[ix];
644
645     for (int iy = 0; iy < ny; iy++, detPos += det_dy) {
646       if (interpType == Backprojector::INTERP_NEAREST) {
647         const int iDetPos = (detPos + halfScale) >> scaleShift;
648         if (iDetPos >= 0 && iDetPos <= iLastDet) {
649           *pImCol++ += filteredProj[iDetPos];
650         } else
651           pImCol++;
652       } else if (interpType == Backprojector::INTERP_FREQ_PREINTERPOLATION) {
653         const int iDetPos = ((detPos + halfScale) >> scaleShift) * m_interpFactor;
654         if (iDetPos >= 0 && iDetPos <= iLastDet) {
655           *pImCol++ += filteredProj[iDetPos];
656         } else
657           pImCol++;
658       } else if (interpType == Backprojector::INTERP_LINEAR) {
659         const long iDetPos = detPos >> scaleShift;
660         if (iDetPos >= 0 && iDetPos <= iLastDet) {
661           const long detRemainder = detPos & scaleBitmask;
662           *pImCol++ += filteredProj[iDetPos] + (detRemainder * deltaFilteredProj[iDetPos]);
663         } else
664           pImCol++;
665       } else if (interpType == Backprojector::INTERP_CUBIC) {
666         *pImCol++ += pCubicInterp->interpolate (static_cast<double>(detPos) / scale);
667       } // end Cubic
668     } // end for iy
669   } // end for ix
670
671   if (interpType == Backprojector::INTERP_LINEAR)
672     delete deltaFilteredProj;
673   else if (interpType == Backprojector::INTERP_CUBIC)
674     delete pCubicInterp;
675 }
676
677
678 void
679 BackprojectEquiangular::BackprojectView (const double* const filteredProj, const double view_angle)
680 {
681   double beta = view_angle;
682
683   CubicPolyInterpolator* pCubicInterp = NULL;
684   if (interpType == Backprojector::INTERP_CUBIC)
685     pCubicInterp = new CubicPolyInterpolator (filteredProj, nDet);
686
687 #if HAVE_OPENMP
688   #pragma omp parallel for
689 #endif
690   for (int ix = 0; ix < nx; ix++) {
691     ImageFileColumn pImCol = v[ix];
692
693     for (int iy = 0; iy < ny; iy++) {
694       double dAngleDiff = beta - phi[ix][iy];
695       double rcos_t = r[ix][iy] * cos (dAngleDiff);
696       double rsin_t = r[ix][iy] * sin (dAngleDiff);
697       double dFLPlusSin = m_dFocalLength + rsin_t;
698       double gamma =  atan (rcos_t / dFLPlusSin);
699       double dPos = gamma / detInc;  // position along detector
700       double dL2 = dFLPlusSin * dFLPlusSin + (rcos_t * rcos_t);
701
702       if (interpType == Backprojector::INTERP_NEAREST) {
703         int iDetPos = iDetCenter + nearest<int>(dPos);  // calc index in the filtered raysum vector
704         if (iDetPos >= 0 && iDetPos < nDet)
705           pImCol[iy] += filteredProj[iDetPos] / dL2;
706       } else if (interpType == Backprojector::INTERP_LINEAR) {
707         double dPosFloor = floor (dPos);
708         int iDetPos = iDetCenter + static_cast<int>(dPosFloor);
709         double frac = dPos - dPosFloor; // fraction distance from det
710         if (iDetPos >= 0 && iDetPos < nDet - 1)
711           pImCol[iy] += (filteredProj[iDetPos] + frac * (filteredProj[iDetPos+1] - filteredProj[iDetPos])) / dL2;
712       } else if (interpType == Backprojector::INTERP_CUBIC) {
713         double d = iDetCenter + dPos;           // position along detector
714         if (d >= 0 && d < nDet)
715           pImCol[iy] += pCubicInterp->interpolate (d) / dL2;
716       }
717     }   // end for y
718   }     // end for x
719
720   if (interpType == Backprojector::INTERP_CUBIC)
721     delete pCubicInterp;
722 }
723
724 void
725 BackprojectEquilinear::BackprojectView (const double* const filteredProj, const double view_angle)
726 {
727   double beta = view_angle;
728
729   CubicPolyInterpolator* pCubicInterp = NULL;
730   if (interpType == Backprojector::INTERP_CUBIC)
731     pCubicInterp = new CubicPolyInterpolator (filteredProj, nDet);
732
733 #if HAVE_OPENMP
734   #pragma omp parallel for
735 #endif
736   for (int ix = 0; ix < nx; ix++) {
737     ImageFileColumn pImCol = v[ix];
738
739     for (int iy = 0; iy < ny; iy++) {
740       double dAngleDiff = beta - phi[ix][iy];
741       double rcos_t = r[ix][iy] * cos (dAngleDiff);
742       double rsin_t = r[ix][iy] * sin (dAngleDiff);
743
744       double dU = (m_dFocalLength + rsin_t) / m_dFocalLength;
745       double dU2 = dU * dU;
746
747       double dDetPos =  rcos_t / dU;
748       // Scale for imaginary detector that passes through origin of phantom, see Kak-Slaney Figure 3.22.
749       dDetPos *= m_dSourceDetectorLength / m_dFocalLength;
750       double dPos = dDetPos / detInc;  // position along detector array
751
752       if (interpType == Backprojector::INTERP_NEAREST) {
753         int iDetPos = iDetCenter + nearest<int>(dPos);  // calc index in the filtered raysum vector
754         if (iDetPos >= 0 && iDetPos < nDet)
755           pImCol[iy] += filteredProj[iDetPos] / dU2;
756       } else if (interpType == Backprojector::INTERP_LINEAR) {
757         double dPosFloor = floor (dPos);
758         int iDetPos = iDetCenter + static_cast<int>(dPosFloor);
759         double frac = dPos - dPosFloor; // fraction distance from det
760         if (iDetPos >= 0 && iDetPos < nDet - 1)
761           pImCol[iy] += (filteredProj[iDetPos] + frac * (filteredProj[iDetPos+1] - filteredProj[iDetPos])) / dU2;
762       } else if (interpType == Backprojector::INTERP_CUBIC) {
763         double d = iDetCenter + dPos;           // position along detector
764         if (d >= 0 && d < nDet)
765           pImCol[iy] += pCubicInterp->interpolate (d) / dU2;
766       }
767     }   // end for y
768   }     // end for x
769
770   if (interpType == Backprojector::INTERP_CUBIC)
771     delete pCubicInterp;
772 }
773