Interim developed on DDA for projecting through imagefiles
[ctsim.git] / libctsim / scanner.cpp
index 34864fb018a6ce94a1ba63bd1cb2904777226614..204c9cb13b86b30a3c8130ad0eea0d1a05af3c61 100644 (file)
@@ -308,11 +308,17 @@ Scanner::collectProjections (Projections& proj, const Phantom& phm, const int iS
 {
   m_trace = trace;
   double start_angle = (iStartView + iOffsetView) * proj.rotInc();
+  int parallel_enabled = 1;
+  UNUSED(parallel_enabled);
 
-#ifdef HAVE_OPENMP
-  #pragma omp parallel for
+#if HAVE_SGP
+  if (pSGP && (m_trace >= Trace::TRACE_PHANTOM))
+    parallel_enabled = 0;
 #endif
 
+#if HAVE_OPENMP
+  #pragma omp parallel for if (parallel_enabled)
+#endif
   for (int iView = 0;  iView < iNumViews; iView++) {
     double viewAngle = start_angle + (iView * proj.rotInc());
 
@@ -591,6 +597,133 @@ Scanner::traceShowParamRasterOp (int iRasterOp, const char *szLabel, const char
   }
 }
 
+void swap_xy_points (double& x1, double& y1, double& x2, double& y2)
+{
+  double temp = x1; x1 = x2; x2 = temp;
+  temp = y1; y1 = y2; y2 = temp;
+}
+
+class WeightedPoint {
+public:
+  int x, y;
+  double weight;
+  WeightedPoint (int _x, int _y, double _weight)
+    : x(_x), y(_y), weight(_weight)
+  {}
+};
+
+
+/* FUNCTION
+ *  Name:    projection_pixel_weights
+ *  Purpose: Returns a vector of WeightedPoint with the length of
+ *           line that intersects with each pixel
+ */
+
+void
+projection_pixel_weights (std::vector<WeightedPoint>& wp, const int nx, const int ny,
+                          double x1, double y1, double x2, double y2)
+{
+  double ylen = fabs(y2-y1);
+  double xlen = fabs(x2-x1);
+  bool swap_xy = false, invert_slope = false;
+  double slope;
+
+  if (ylen > xlen) {
+    swap_xy = true;
+    slope = xlen / ylen;
+    if (y2 < y1)      // swap start/end so always moving from bottom to top
+      swap_xy_points (x1, y1, x2, y2);
+    if (x2 < x1) {
+      invert_slope = true;
+    }
+  } else {
+#if DEBUG
+    if (ylen == xlen)
+      sys_error(ERR_WARNING, "Slope == 1");
+#endif
+    slope = ylen / xlen;
+    if (x2 < x1)      // swap start/end so always moving from left to right in image
+      swap_xy_points (x1, y1, x2, y2);
+    if (y2 < y1) {
+      invert_slope = true;
+    }
+  }
+  double angle = atan(fabs(slope));
+  double minor_dist = sin(angle); // distance along minor axis
+  double pixel_len = 1 / cos(angle);
+
+  int minor_dir = 1;
+  if (invert_slope) {
+    minor_dir = -1;
+    slope = -slope;
+  }
+
+  double x = x1, y = y1;
+  int ix = floor(x);
+  int iy = floor(y);
+  double ydelta = y - iy;
+  double xdelta = x - ix;
+
+  double min_delta;
+  int *imaj, *imin;
+  int max_maj, max_min;
+  if (swap_xy) {
+    min_delta = xdelta;
+    imaj = &iy;
+    imin = &ix;
+    max_maj = ny;
+    max_min = nx;
+  } else {
+    min_delta = ydelta;
+    imaj = &ix;
+    imin = &iy;
+    max_maj = nx;
+    max_min = ny;
+  }
+
+#if DEBUG
+  sys_error(ERR_TRACE, "m=%6.3f swap_xy=%d invert=%d len=%8.6f min_delta=%.4g minor_dist=%6.3f (%.3f,%.3f)-(%.3f,%.3f)",
+            slope, swap_xy, invert_slope, pixel_len, min_delta, minor_dist, x1, y1, x2, y2);
+#endif
+
+  // if position of minor axis is at edge of image, but will be moving into pixel within image
+  if (*imin == max_min && invert_slope) {
+    (*imin)--;  // select the pixel within image
+#if DEBUG
+    sys_error(ERR_TRACE, "Moving pixel inside image, adding %f to min_delta", (1+slope));
+#endif
+    min_delta += (1+slope);
+  }
+
+  while (*imaj < max_maj && *imin < max_min && *imin >= 0) {
+    double next_min_delta = min_delta + slope;
+
+    if (((!invert_slope) && (next_min_delta < 1)) ||
+        (invert_slope && (next_min_delta > 0))) {
+      // stay within same pixel
+      double w = pixel_len;
+      WeightedPoint p (ix, iy, w);
+      wp.push_back(p);
+#if DEBUG
+      sys_error(ERR_TRACE, "  Full pixel: (%3d,%3d)=%.4g, min_delta=%.4g", ix, iy, w, min_delta);
+#endif
+      min_delta = next_min_delta;
+    } else {
+      // Scale partial pixel_len into pixel
+      double norm_delta = invert_slope ? min_delta : (1 - min_delta);
+      double p1_line = norm_delta * pixel_len;
+      WeightedPoint p1 (ix, iy, p1_line);
+      wp.push_back (p1);
+#if DEBUG
+      sys_error(ERR_TRACE, "  Part pixel: (%3d,%3d)=%.4g, min_delta=%.4g", ix, iy, p1_line, min_delta);
+#endif
+      (*imin) += minor_dir;
+      min_delta = next_min_delta - minor_dir;
+    }
+    (*imaj)++;
+  }
+
+}
 
 
 /* NAME
@@ -613,30 +746,36 @@ Scanner::projectSingleLine (const Phantom& phm, double x1, double y1, double x2,
 
     const ImageFile* im = phm.getImagefile();
     const ImageFileArray v = im->getArray();
-    int nx = im->nx(), ny = im->ny();
 
-    // Convert endpoints into image pixel coordinates
+    // Get image axis extents
+    int nx = im->nx(), ny = im->ny();
     double xmin=0, xmax=nx, ymin=0, ymax=ny; // default coordinate
     if (! im->getAxisExtent (xmin, xmax, ymin, ymax)) {
       sys_error(ERR_WARNING, "Axis extent not available [Scanner::projectSingleLine]");
     }
+
+    // Clip line in image object coordinates
     double rect[4];
     rect[0] = xmin; rect[1] = ymin;
-    rect[2] = xmax;  rect[3] = ymax;
+    rect[2] = xmax; rect[3] = ymax;
     bool accept = clip_rect (x1, y1, x2, y2, rect);
     if (! accept)
       return (0.0);
 
-    double xlen = xmax - xmin, ylen = ymax - ymin;
+    // Convert to pixel coordinates
+    double xlen = xmax - xmin;
+    double ylen = ymax - ymin;
     double px1 = nx * (x1 - xmin) / xlen;
     double px2 = nx * (x2 - xmin) / xlen;
     double py1 = ny * (y1 - ymin) / ylen;
     double py2 = ny * (y2 - ymin) / ylen;
 
-    // Use Bresenham integer line stepping to step to each pixel, and walked through image
-
-    sys_error(ERR_WARNING, "Not yet able to project through imagefile. Line (%.3f,%.3f) - (%.3f,%.3f)", px1, py1, px2, py2);
-
+    std::vector<WeightedPoint> wp;
+    projection_pixel_weights (wp, nx, ny, px1, py1, px2, py2);
+    for (unsigned int i = 0; i < wp.size(); i++) {
+      WeightedPoint& p = wp[i];
+      rsum += v[p.x][p.y] * p.weight;
+    }
   } else {
 
     // Project through each pelem in Phantom