Browse Source

resolve pixel-offset issues with gaussian and box filters

David Rose 12 years ago
parent
commit
297864e834
2 changed files with 35 additions and 30 deletions
  1. 3 3
      panda/src/pnmimage/config_pnmimage.cxx
  2. 32 27
      panda/src/pnmimage/pnm-image-filter.cxx

+ 3 - 3
panda/src/pnmimage/config_pnmimage.cxx

@@ -37,7 +37,7 @@ ConfigVariableBool pfm_reverse_dimensions
           "on input.  Does not affect output, which is always written width height."));
 
 ConfigVariableBool pfm_resize_quick
-("pfm-resize-quick", false,
+("pfm-resize-quick", true,
  PRC_DESC("Specify true to implement PfmFile::resize() with a \"quick\" filter, "
           "but only when the pfm is being downsampled (to a smaller size).  "
           "This just controls the behavior of resize(); you can "
@@ -47,13 +47,13 @@ ConfigVariableBool pfm_resize_gaussian
 ("pfm-resize-gaussian", true,
  PRC_DESC("Specify true to implement PfmFile::resize() with a higher-quality "
           "Gaussian filter, or false to implement it with a faster box "
-          "filter.  If pfm-resize-quick is also true, then only takes effect "
+          "filter.  If pfm-resize-quick is also true, this only takes effect "
           "when the pfm is being upsampled.  This just controls the behavior "
           "of resize(); you can always call box_filter() or gaussian_filter() "
           "explicitly."));
 
 ConfigVariableDouble pfm_resize_radius
-("pfm-resize-radius", 0.5,
+("pfm-resize-radius", 1.0,
  PRC_DESC("Specify the default filter radius for PfmFile::resize().  "
           "This just controls the behavior of resize(); you can "
           "always call box_filter() or gaussian_filter() explicitly with "

+ 32 - 27
panda/src/pnmimage/pnm-image-filter.cxx

@@ -111,25 +111,28 @@ filter_row(StoreType dest[], int dest_len,
            double scale,                    //  == dest_len / source_len
            const WorkType filter[],
            double filter_width) {
-  // If we are expanding the row (scale>1.0), we need to look at a fractional
-  // granularity.  Hence, we scale our filter index by scale.  If we are
-  // compressing (scale<1.0), we don't need to fiddle with the filter index, so
-  // we leave it at one.
-  double iscale = max(scale, 1.0);
+  // If we are expanding the row (scale > 1.0), we need to look at a
+  // fractional granularity.  Hence, we scale our filter index by
+  // scale.  If we are compressing (scale < 1.0), we don't need to
+  // fiddle with the filter index, so we leave it at one.
 
-  // Similarly, if we are expanding the row, we want to start the new row at
-  // the far left edge of the original pixel, not in the center.  So we will
-  // have a non-zero offset.
-  int offset = (int)cfloor(iscale*0.5);
+  double iscale;
+  if (scale < 1.0) {
+    iscale = 1.0;
+    filter_width /= scale;
+  } else {
+    iscale = scale;
+  }
 
   for (int dest_x = 0; dest_x < dest_len; dest_x++) {
-    double center = (dest_x - offset) / scale;
+    // The additional offset of 0.5 keeps the pixel centered.
+    double center = (dest_x + 0.5) / scale - 0.5;
 
     // left and right are the starting and ending ranges of the radius of
     // interest of the filter function.  We need to apply the filter to each
     // value in this range.
     int left = max((int)cfloor(center - filter_width), 0);
-    int right = min((int)cceil(center + filter_width), source_len-1);
+    int right = min((int)cceil(center + filter_width), source_len - 1);
 
     // right_center is the point just to the right of the center.  This
     // allows us to flip the sign of the offset when we cross the center point.
@@ -144,13 +147,13 @@ filter_row(StoreType dest[], int dest_len,
     // of center--so we don't have to incur the overhead of calling fabs()
     // each time through the loop.
     for (source_x = left; source_x < right_center; source_x++) {
-      index = (int)(iscale * (center - source_x));
+      index = (int)(iscale * (center - source_x) + 0.5);
       net_value += filter[index] * source[source_x];
       net_weight += filter[index];
     }
 
     for (; source_x <= right; source_x++) {
-      index = (int)(iscale * (source_x - center));
+      index = (int)(iscale * (source_x - center) + 0.5);
       net_value += filter[index] * source[source_x];
       net_weight += filter[index];
     }
@@ -172,26 +175,28 @@ filter_sparse_row(StoreType dest[], StoreType dest_weight[], int dest_len,
                   double scale,                    //  == dest_len / source_len
                   const WorkType filter[],
                   double filter_width) {
-  // If we are expanding the row (scale>1.0), we need to look at a fractional
-  // granularity.  Hence, we scale our filter index by scale.  If we are
-  // compressing (scale<1.0), we don't need to fiddle with the filter index, so
-  // we leave it at one.
-  double iscale = max(scale, 1.0);
+  // If we are expanding the row (scale > 1.0), we need to look at a
+  // fractional granularity.  Hence, we scale our filter index by
+  // scale.  If we are compressing (scale < 1.0), we don't need to
+  // fiddle with the filter index, so we leave it at one.
 
-  // Similarly, if we are expanding the row, we want to start the new row at
-  // the far left edge of the original pixel, not in the center.  So we will
-  // have a non-zero offset.
-  int offset = (int)cfloor(iscale*0.5);
+  double iscale;
+  if (scale < 1.0) {
+    iscale = 1.0;
+    filter_width /= scale;
+  } else {
+    iscale = scale;
+  }
 
   for (int dest_x = 0; dest_x < dest_len; dest_x++) {
     // The additional offset of 0.5 keeps the pixel centered.
-    double center = (dest_x - offset + 0.5) / scale;
+    double center = (dest_x + 0.5) / scale - 0.5;
 
     // left and right are the starting and ending ranges of the radius of
     // interest of the filter function.  We need to apply the filter to each
     // value in this range.
     int left = max((int)cfloor(center - filter_width), 0);
-    int right = min((int)cceil(center + filter_width), source_len-1);
+    int right = min((int)cceil(center + filter_width), source_len - 1);
 
     // right_center is the point just to the right of the center.  This
     // allows us to flip the sign of the offset when we cross the center point.
@@ -206,13 +211,13 @@ filter_sparse_row(StoreType dest[], StoreType dest_weight[], int dest_len,
     // of center--so we don't have to incur the overhead of calling fabs()
     // each time through the loop.
     for (source_x = left; source_x < right_center; source_x++) {
-      index = (int)(iscale * (center - source_x));
+      index = (int)(iscale * (center - source_x) + 0.5);
       net_value += filter[index] * source[source_x] * source_weight[source_x];
       net_weight += filter[index] * source_weight[source_x];
     }
 
     for (; source_x <= right; source_x++) {
-      index = (int)(iscale * (source_x - center));
+      index = (int)(iscale * (source_x - center) + 0.5);
       net_value += filter[index] * source[source_x] * source_weight[source_x];
       net_weight += filter[index] * source_weight[source_x];
     }
@@ -258,7 +263,7 @@ box_filter_impl(double scale, double width,
     fscale = scale;
   }
   filter_width = width;
-  int actual_width = (int)cceil((filter_width + 1) * fscale);
+  int actual_width = (int)cceil((filter_width + 1) * fscale) + 1;
 
   filter = (WorkType *)PANDA_MALLOC_ARRAY(actual_width * sizeof(WorkType));