resolve pixel-offset issues with gaussian and box filters

This commit is contained in:
David Rose 2013-05-30 20:32:18 +00:00
parent c7daa00c5b
commit 297864e834
2 changed files with 35 additions and 30 deletions

View File

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

View File

@ -111,25 +111,28 @@ filter_row(StoreType dest[], int dest_len,
double scale, // == dest_len / source_len double scale, // == dest_len / source_len
const WorkType filter[], const WorkType filter[],
double filter_width) { double filter_width) {
// If we are expanding the row (scale>1.0), we need to look at a fractional // If we are expanding the row (scale > 1.0), we need to look at a
// granularity. Hence, we scale our filter index by scale. If we are // fractional granularity. Hence, we scale our filter index by
// compressing (scale<1.0), we don't need to fiddle with the filter index, so // scale. If we are compressing (scale < 1.0), we don't need to
// we leave it at one. // fiddle with the filter index, so we leave it at one.
double iscale = max(scale, 1.0);
// Similarly, if we are expanding the row, we want to start the new row at double iscale;
// the far left edge of the original pixel, not in the center. So we will if (scale < 1.0) {
// have a non-zero offset. iscale = 1.0;
int offset = (int)cfloor(iscale*0.5); filter_width /= scale;
} else {
iscale = scale;
}
for (int dest_x = 0; dest_x < dest_len; dest_x++) { 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 // 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 // interest of the filter function. We need to apply the filter to each
// value in this range. // value in this range.
int left = max((int)cfloor(center - filter_width), 0); 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 // 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. // 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() // of center--so we don't have to incur the overhead of calling fabs()
// each time through the loop. // each time through the loop.
for (source_x = left; source_x < right_center; source_x++) { 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_value += filter[index] * source[source_x];
net_weight += filter[index]; net_weight += filter[index];
} }
for (; source_x <= right; 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]; net_value += filter[index] * source[source_x];
net_weight += filter[index]; 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 double scale, // == dest_len / source_len
const WorkType filter[], const WorkType filter[],
double filter_width) { double filter_width) {
// If we are expanding the row (scale>1.0), we need to look at a fractional // If we are expanding the row (scale > 1.0), we need to look at a
// granularity. Hence, we scale our filter index by scale. If we are // fractional granularity. Hence, we scale our filter index by
// compressing (scale<1.0), we don't need to fiddle with the filter index, so // scale. If we are compressing (scale < 1.0), we don't need to
// we leave it at one. // fiddle with the filter index, so we leave it at one.
double iscale = max(scale, 1.0);
// Similarly, if we are expanding the row, we want to start the new row at double iscale;
// the far left edge of the original pixel, not in the center. So we will if (scale < 1.0) {
// have a non-zero offset. iscale = 1.0;
int offset = (int)cfloor(iscale*0.5); filter_width /= scale;
} else {
iscale = scale;
}
for (int dest_x = 0; dest_x < dest_len; dest_x++) { for (int dest_x = 0; dest_x < dest_len; dest_x++) {
// The additional offset of 0.5 keeps the pixel centered. // 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 // 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 // interest of the filter function. We need to apply the filter to each
// value in this range. // value in this range.
int left = max((int)cfloor(center - filter_width), 0); 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 // 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. // 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() // of center--so we don't have to incur the overhead of calling fabs()
// each time through the loop. // each time through the loop.
for (source_x = left; source_x < right_center; source_x++) { 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_value += filter[index] * source[source_x] * source_weight[source_x];
net_weight += filter[index] * source_weight[source_x]; net_weight += filter[index] * source_weight[source_x];
} }
for (; source_x <= right; 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_value += filter[index] * source[source_x] * source_weight[source_x];
net_weight += filter[index] * 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; fscale = scale;
} }
filter_width = width; 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)); filter = (WorkType *)PANDA_MALLOC_ARRAY(actual_width * sizeof(WorkType));