PfmFile::box_filter() etc., improved forward_distort().

This commit is contained in:
David Rose 2013-01-26 00:24:47 +00:00
parent a746344970
commit cdbd54c9be
6 changed files with 690 additions and 137 deletions

View File

@ -144,13 +144,17 @@ set_point2(int x, int y, const LVecBase2f &point) {
_table[(y * _x_size + x)] = point[0];
break;
case 3:
(*(LPoint3f *)&_table[(y * _x_size + x) * _num_channels]).set(point[0], point[1], point[2]);
break;
case 2:
*(LPoint2f *)&_table[(y * _x_size + x) * _num_channels] = point;
break;
case 3:
(*(LPoint3f *)&_table[(y * _x_size + x) * _num_channels]).set(point[0], point[1], 0.0);
break;
case 4:
(*(LPoint4f *)&_table[(y * _x_size + x) * _num_channels]).set(point[0], point[1], 0.0, 0.0);
break;
}
}
@ -214,10 +218,17 @@ set_point(int x, int y, const LVecBase3f &point) {
_table[(y * _x_size + x)] = point[0];
break;
case 2:
(*(LPoint2f *)&_table[(y * _x_size + x) * _num_channels]).set(point[0], point[1]);
break;
case 3:
case 4:
*(LPoint3f *)&_table[(y * _x_size + x) * _num_channels] = point;
break;
case 4:
(*(LPoint4f *)&_table[(y * _x_size + x) * _num_channels]).set(point[0], point[1], 0.0f, 0.0f);
break;
}
}
@ -281,6 +292,10 @@ set_point4(int x, int y, const LVecBase4f &point) {
_table[(y * _x_size + x)] = point[0];
break;
case 2:
(*(LPoint2f *)&_table[(y * _x_size + x) * _num_channels]).set(point[0], point[1]);
break;
case 3:
(*(LPoint3f *)&_table[(y * _x_size + x) * _num_channels]).set(point[0], point[1], point[2]);
break;
@ -320,6 +335,36 @@ modify_point4(int x, int y) {
return *(LPoint4f *)&_table[(y * _x_size + x) * _num_channels];
}
////////////////////////////////////////////////////////////////////
// Function: PfmFile::fill
// Access: Published
// Description: Fills the table with all of the same value.
////////////////////////////////////////////////////////////////////
INLINE void PfmFile::
fill(PN_float32 value) {
fill(LPoint4f(value, 0.0f, 0.0f, 0.0f));
}
////////////////////////////////////////////////////////////////////
// Function: PfmFile::fill
// Access: Published
// Description: Fills the table with all of the same value.
////////////////////////////////////////////////////////////////////
INLINE void PfmFile::
fill(const LPoint2f &value) {
fill(LPoint4f(value[0], value[1], 0.0f, 0.0f));
}
////////////////////////////////////////////////////////////////////
// Function: PfmFile::fill
// Access: Published
// Description: Fills the table with all of the same value.
////////////////////////////////////////////////////////////////////
INLINE void PfmFile::
fill(const LPoint3f &value) {
fill(LPoint4f(value[0], value[1], value[2], 0.0f));
}
////////////////////////////////////////////////////////////////////
// Function: PfmFile::calc_autocrop
// Access: Published

View File

@ -470,6 +470,56 @@ store_mask(PNMImage &pnmimage) const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: PfmFile::fill
// Access: Published
// Description: Fills the table with all of the same value.
////////////////////////////////////////////////////////////////////
void PfmFile::
fill(const LPoint4f &value) {
switch (_num_channels) {
case 1:
{
for (int yi = 0; yi < _y_size; ++yi) {
for (int xi = 0; xi < _x_size; ++xi) {
_table[(yi * _x_size + xi)] = value[0];
}
}
}
break;
case 2:
{
for (int yi = 0; yi < _y_size; ++yi) {
for (int xi = 0; xi < _x_size; ++xi) {
(*(LPoint2f *)&_table[(yi * _x_size + xi) * _num_channels]).set(value[0], value[1]);
}
}
}
break;
case 3:
{
for (int yi = 0; yi < _y_size; ++yi) {
for (int xi = 0; xi < _x_size; ++xi) {
(*(LPoint3f *)&_table[(yi * _x_size + xi) * _num_channels]).set(value[0], value[1], value[2]);
}
}
}
break;
case 4:
{
for (int yi = 0; yi < _y_size; ++yi) {
for (int xi = 0; xi < _x_size; ++xi) {
*(LPoint4f *)&_table[(yi * _x_size + xi) * _num_channels] = value;
}
}
}
break;
}
}
////////////////////////////////////////////////////////////////////
// Function: PfmFile::calc_average_point
// Access: Published
@ -736,41 +786,73 @@ resize(int new_x_size, int new_y_size) {
return;
}
int new_size = new_x_size * new_y_size * _num_channels;
PfmFile result;
result.clear(new_x_size, new_y_size, _num_channels);
if (new_x_size < _x_size && new_y_size < _y_size) {
// If we're downscaling, we can use quick_filter, which is faster.
result.quick_filter_from(*this);
} else {
// Otherwise, we should use box_filter(), which is more general.
result.box_filter_from(0.5, *this);
}
_table.swap(result._table);
_x_size = new_x_size;
_y_size = new_y_size;
}
////////////////////////////////////////////////////////////////////
// Function: PfmFile::quick_filter_from
// Access: Public
// Description: Resizes from the given image, with a fixed radius of
// 0.5. This is a very specialized and simple algorithm
// that doesn't handle dropping below the Nyquist rate
// very well, but is quite a bit faster than the more
// general box_filter(), above.
////////////////////////////////////////////////////////////////////
void PfmFile::
quick_filter_from(const PfmFile &from) {
if (_x_size == 0 || _y_size == 0) {
return;
}
// We allocate a little bit bigger to allow safe overflow.
Table new_data;
new_data.reserve(new_size + 4);
new_data.reserve(_table.size());
PN_float32 from_x0, from_x1, from_y0, from_y1;
int orig_x_size = from.get_x_size();
int orig_y_size = from.get_y_size();
PN_float32 x_scale = 1.0;
PN_float32 y_scale = 1.0;
if (new_x_size > 1) {
x_scale = (PN_float32)_x_size / (PN_float32)new_x_size;
if (_x_size > 1) {
x_scale = (PN_float32)orig_x_size / (PN_float32)_x_size;
}
if (new_y_size > 1) {
y_scale = (PN_float32)_y_size / (PN_float32)new_y_size;
if (_y_size > 1) {
y_scale = (PN_float32)orig_y_size / (PN_float32)_y_size;
}
switch (_num_channels) {
case 1:
{
from_y0 = 0.0;
for (int to_y = 0; to_y < new_y_size; ++to_y) {
for (int to_y = 0; to_y < _y_size; ++to_y) {
from_y1 = (to_y + 1.0) * y_scale;
from_y1 = min(from_y1, (PN_float32) _y_size);
from_y1 = min(from_y1, (PN_float32)orig_y_size);
from_x0 = 0.0;
for (int to_x = 0; to_x < new_x_size; ++to_x) {
for (int to_x = 0; to_x < _x_size; ++to_x) {
from_x1 = (to_x + 1.0) * x_scale;
from_x1 = min(from_x1, (PN_float32) _x_size);
from_x1 = min(from_x1, (PN_float32)orig_x_size);
// Now the box from (from_x0, from_y0) - (from_x1, from_y1)
// but not including (from_x1, from_y1) maps to the pixel (to_x, to_y).
PN_float32 result;
box_filter_region(result, from_x0, from_y0, from_x1, from_y1);
from.box_filter_region(result, from_x0, from_y0, from_x1, from_y1);
new_data.push_back(result);
from_x0 = from_x1;
@ -783,19 +865,19 @@ resize(int new_x_size, int new_y_size) {
case 3:
{
from_y0 = 0.0;
for (int to_y = 0; to_y < new_y_size; ++to_y) {
for (int to_y = 0; to_y < _y_size; ++to_y) {
from_y1 = (to_y + 1.0) * y_scale;
from_y1 = min(from_y1, (PN_float32) _y_size);
from_y1 = min(from_y1, (PN_float32)orig_y_size);
from_x0 = 0.0;
for (int to_x = 0; to_x < new_x_size; ++to_x) {
for (int to_x = 0; to_x < _x_size; ++to_x) {
from_x1 = (to_x + 1.0) * x_scale;
from_x1 = min(from_x1, (PN_float32) _x_size);
from_x1 = min(from_x1, (PN_float32)orig_x_size);
// Now the box from (from_x0, from_y0) - (from_x1, from_y1)
// but not including (from_x1, from_y1) maps to the pixel (to_x, to_y).
LPoint3f result;
box_filter_region(result, from_x0, from_y0, from_x1, from_y1);
from.box_filter_region(result, from_x0, from_y0, from_x1, from_y1);
new_data.push_back(result[0]);
new_data.push_back(result[1]);
new_data.push_back(result[2]);
@ -810,19 +892,19 @@ resize(int new_x_size, int new_y_size) {
case 4:
{
from_y0 = 0.0;
for (int to_y = 0; to_y < new_y_size; ++to_y) {
for (int to_y = 0; to_y < _y_size; ++to_y) {
from_y1 = (to_y + 1.0) * y_scale;
from_y1 = min(from_y1, (PN_float32) _y_size);
from_y1 = min(from_y1, (PN_float32)orig_y_size);
from_x0 = 0.0;
for (int to_x = 0; to_x < new_x_size; ++to_x) {
for (int to_x = 0; to_x < _x_size; ++to_x) {
from_x1 = (to_x + 1.0) * x_scale;
from_x1 = min(from_x1, (PN_float32) _x_size);
from_x1 = min(from_x1, (PN_float32)orig_x_size);
// Now the box from (from_x0, from_y0) - (from_x1, from_y1)
// but not including (from_x1, from_y1) maps to the pixel (to_x, to_y).
LPoint4f result;
box_filter_region(result, from_x0, from_y0, from_x1, from_y1);
from.box_filter_region(result, from_x0, from_y0, from_x1, from_y1);
new_data.push_back(result[0]);
new_data.push_back(result[1]);
new_data.push_back(result[2]);
@ -844,10 +926,8 @@ resize(int new_x_size, int new_y_size) {
new_data.push_back(0.0);
new_data.push_back(0.0);
nassertv(new_data.size() == new_size + 4);
nassertv(new_data.size() == _table.size());
_table.swap(new_data);
_x_size = new_x_size;
_y_size = new_y_size;
}
////////////////////////////////////////////////////////////////////
@ -963,53 +1043,72 @@ xform(const LMatrix4f &transform) {
// current file is replaced with the point from the same
// file at (x,y), where (x,y) is the point value from
// the dist map.
//
// If scale_factor is not 1, it should be a value > 1,
// and it specifies the factor to upscale the working
// table while processing, to reduce artifacts from
// integer truncation.
////////////////////////////////////////////////////////////////////
void PfmFile::
forward_distort(const PfmFile &dist) {
PfmFile result;
forward_distort(const PfmFile &dist, PN_float32 scale_factor) {
int working_x_size = (int)cceil(_x_size * scale_factor);
int working_y_size = (int)cceil(_y_size * scale_factor);
working_x_size = max(working_x_size, dist.get_x_size());
working_y_size = max(working_y_size, dist.get_y_size());
const PfmFile *source_p = this;
PfmFile scaled_source;
if ((*this).get_x_size() != working_x_size || (*this).get_y_size() != working_y_size) {
// Rescale the source file as needed.
scaled_source = (*this);
scaled_source.resize(working_x_size, working_y_size);
source_p = &scaled_source;
}
const PfmFile *dist_p = &dist;
PfmFile scaled_dist;
if (dist.get_x_size() != _x_size || dist.get_y_size() != _y_size) {
if (dist.get_x_size() != working_x_size || dist.get_y_size() != working_y_size) {
// Rescale the dist file as needed.
scaled_dist = dist;
scaled_dist.resize(_x_size, _y_size);
scaled_dist.resize(working_x_size, working_y_size);
dist_p = &scaled_dist;
}
PfmFile result;
result.clear(working_x_size, working_y_size, _num_channels);
if (_has_no_data_value) {
result = *this;
for (int yi = 0; yi < _y_size; ++yi) {
for (int xi = 0; xi < _x_size; ++xi) {
result.set_point4(xi, yi, _no_data_value);
}
}
} else {
result.clear(_x_size, _y_size, _num_channels);
result.set_no_data_value(_no_data_value);
result.fill(_no_data_value);
}
for (int yi = 0; yi < _y_size; ++yi) {
for (int xi = 0; xi < _x_size; ++xi) {
for (int yi = 0; yi < working_y_size; ++yi) {
for (int xi = 0; xi < working_x_size; ++xi) {
if (!dist_p->has_point(xi, yi)) {
continue;
}
LPoint2f uv = dist_p->get_point2(xi, yi);
int dist_xi = (int)cfloor(uv[0] * (double)_x_size);
int dist_yi = (int)cfloor(uv[1] * (double)_y_size);
if (dist_xi < 0 || dist_xi >= _x_size ||
dist_yi < 0 || dist_yi >= _y_size) {
int dist_xi = (int)cfloor(uv[0] * (PN_float32)working_x_size);
int dist_yi = (int)cfloor(uv[1] * (PN_float32)working_y_size);
if (dist_xi < 0 || dist_xi >= working_x_size ||
dist_yi < 0 || dist_yi >= working_y_size) {
continue;
}
if (!has_point(dist_xi, dist_yi)) {
if (!source_p->has_point(dist_xi, dist_yi)) {
continue;
}
result.set_point(xi, yi, get_point(dist_xi, dist_yi));
result.set_point(xi, yi, source_p->get_point(dist_xi, dist_yi));
}
}
(*this) = result;
// Resize to the target size for completion.
result.resize(_x_size, _y_size);
nassertv(result._table.size() == _table.size());
_table.swap(result._table);
}
////////////////////////////////////////////////////////////////////
@ -1022,51 +1121,75 @@ forward_distort(const PfmFile &dist) {
// current file is replaced with the point from the same
// file at (u,v), where (x,y) is the point value from
// the dist map.
//
// If scale_factor is not 1, it should be a value > 1,
// and it specifies the factor to upscale the working
// table while processing, to reduce artifacts from
// integer truncation.
////////////////////////////////////////////////////////////////////
void PfmFile::
reverse_distort(const PfmFile &dist) {
PfmFile result;
reverse_distort(const PfmFile &dist, PN_float32 scale_factor) {
int working_x_size = (int)cceil(_x_size * scale_factor);
int working_y_size = (int)cceil(_y_size * scale_factor);
working_x_size = max(working_x_size, dist.get_x_size());
working_y_size = max(working_y_size, dist.get_y_size());
const PfmFile *source_p = this;
PfmFile scaled_source;
if ((*this).get_x_size() != working_x_size || (*this).get_y_size() != working_y_size) {
// Rescale the source file as needed.
scaled_source = (*this);
scaled_source.resize(working_x_size, working_y_size);
source_p = &scaled_source;
}
const PfmFile *dist_p = &dist;
PfmFile scaled_dist;
if (dist.get_x_size() != _x_size || dist.get_y_size() != _y_size) {
if (dist.get_x_size() != working_x_size || dist.get_y_size() != working_y_size) {
// Rescale the dist file as needed.
scaled_dist = dist;
scaled_dist.resize(_x_size, _y_size);
scaled_dist.resize(working_x_size, working_y_size);
dist_p = &scaled_dist;
}
PfmFile result;
result.clear(working_x_size, working_y_size, _num_channels);
if (_has_no_data_value) {
result = *this;
for (int yi = 0; yi < _y_size; ++yi) {
for (int xi = 0; xi < _x_size; ++xi) {
result.set_point4(xi, yi, _no_data_value);
}
}
} else {
result.clear(_x_size, _y_size, _num_channels);
result.set_no_data_value(_no_data_value);
result.fill(_no_data_value);
}
for (int yi = 0; yi < _y_size; ++yi) {
for (int xi = 0; xi < _x_size; ++xi) {
if (!has_point(xi, yi)) {
for (int yi = 0; yi < working_y_size; ++yi) {
for (int xi = 0; xi < working_x_size; ++xi) {
if (!source_p->has_point(xi, yi)) {
continue;
}
if (!dist_p->has_point(xi, yi)) {
continue;
}
LPoint2f uv = dist_p->get_point2(xi, yi);
int dist_xi = (int)cfloor(uv[0] * (double)_x_size);
int dist_yi = (int)cfloor(uv[1] * (double)_y_size);
if (dist_xi < 0 || dist_xi >= _x_size ||
dist_yi < 0 || dist_yi >= _y_size) {
int dist_xi = (int)cfloor(uv[0] * (PN_float32)working_x_size);
int dist_yi = (int)cfloor(uv[1] * (PN_float32)working_y_size);
if (dist_xi < 0 || dist_xi >= working_x_size ||
dist_yi < 0 || dist_yi >= working_y_size) {
continue;
}
result.set_point(dist_xi, dist_yi, get_point(xi, yi));
if (!source_p->has_point(dist_xi, dist_yi)) {
continue;
}
result.set_point(dist_xi, dist_yi, source_p->get_point(xi, yi));
}
}
(*this) = result;
// Resize to the target size for completion.
result.resize(_x_size, _y_size);
nassertv(result._table.size() == _table.size());
_table.swap(result._table);
}
////////////////////////////////////////////////////////////////////
@ -1089,7 +1212,7 @@ merge(const PfmFile &other) {
for (int yi = 0; yi < _y_size; ++yi) {
for (int xi = 0; xi < _x_size; ++xi) {
if (!has_point(xi, yi)) {
if (!has_point(xi, yi) && other.has_point(xi, yi)) {
set_point(xi, yi, other.get_point(xi, yi));
}
}
@ -1180,6 +1303,47 @@ clear_to_texcoords(int x_size, int y_size) {
}
}
////////////////////////////////////////////////////////////////////
// Function: PfmFile::calc_tight_bounds
// Access: Published
// Description: Calculates the minimum and maximum vertices of all
// points within the table. Assumes the table contains
// 3-D points.
//
// The return value is true if any points in the table,
// or false if none are.
////////////////////////////////////////////////////////////////////
bool PfmFile::
calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point) const {
min_point.set(0.0f, 0.0f, 0.0f);
max_point.set(0.0f, 0.0f, 0.0f);
bool found_any = false;
for (int yi = 0; yi < _y_size; ++yi) {
for (int xi = 0; xi < _x_size; ++xi) {
if (!has_point(xi, yi)) {
continue;
}
const LPoint3f &point = get_point(xi, yi);
if (!found_any) {
min_point = point;
max_point = point;
found_any = true;
} else {
min_point.set(min(min_point[0], point[0]),
min(min_point[0], point[0]),
min(min_point[0], point[0]));
max_point.set(max(max_point[0], point[0]),
max(max_point[0], point[0]),
max(max_point[0], point[0]));
}
}
}
return found_any;
}
////////////////////////////////////////////////////////////////////
// Function: PfmFile::compute_planar_bounds
// Access: Published

View File

@ -74,6 +74,11 @@ PUBLISHED:
INLINE void set_point4(int x, int y, const LVecBase4d &point);
INLINE LPoint4f &modify_point4(int x, int y);
INLINE void fill(PN_float32 value);
INLINE void fill(const LPoint2f &value);
INLINE void fill(const LPoint3f &value);
void fill(const LPoint4f &value);
BLOCKING bool calc_average_point(LPoint3f &result, PN_float32 x, PN_float32 y, PN_float32 radius) const;
BLOCKING bool calc_min_max(LVecBase3f &min_points, LVecBase3f &max_points) const;
BLOCKING bool calc_autocrop(int &x_begin, int &x_end, int &y_begin, int &y_end) const;
@ -92,17 +97,22 @@ PUBLISHED:
INLINE const LPoint4f &get_no_data_value() const;
BLOCKING void resize(int new_x_size, int new_y_size);
BLOCKING void box_filter_from(double radius, const PfmFile &copy);
BLOCKING void gaussian_filter_from(double radius, const PfmFile &copy);
BLOCKING void quick_filter_from(const PfmFile &copy);
BLOCKING void reverse_rows();
BLOCKING void flip(bool flip_x, bool flip_y, bool transpose);
BLOCKING void xform(const LMatrix4f &transform);
INLINE BLOCKING void xform(const LMatrix4d &transform);
BLOCKING void forward_distort(const PfmFile &dist);
BLOCKING void reverse_distort(const PfmFile &dist);
BLOCKING void forward_distort(const PfmFile &dist, PN_float32 scale_factor = 1.0);
BLOCKING void reverse_distort(const PfmFile &dist, PN_float32 scale_factor = 1.0);
BLOCKING void merge(const PfmFile &other);
BLOCKING void copy_channel(int to_channel, const PfmFile &other, int from_channel);
BLOCKING void apply_crop(int x_begin, int x_end, int y_begin, int y_end);
BLOCKING void clear_to_texcoords(int x_size, int y_size);
bool calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point) const;
BLOCKING PT(BoundingHexahedron) compute_planar_bounds(const LPoint2f &center, PN_float32 point_dist, PN_float32 sample_radius, bool points_only) const;
INLINE BLOCKING PT(BoundingHexahedron) compute_planar_bounds(const LPoint2d &center, PN_float32 point_dist, PN_float32 sample_radius, bool points_only) const;
void compute_sample_point(LPoint3f &result,

View File

@ -18,8 +18,8 @@
static void
FUNCTION_NAME(PNMImage &dest, const PNMImage &source,
double width, FilterFunction *make_filter) {
FUNCTION_NAME(IMAGETYPE &dest, const IMAGETYPE &source,
double width, FilterFunction *make_filter, int channel) {
if (!dest.is_valid() || !source.is_valid()) {
return;
}
@ -37,20 +37,22 @@ FUNCTION_NAME(PNMImage &dest, const PNMImage &source,
matrix[a] = (StoreType *)PANDA_MALLOC_ARRAY(source.BSIZE() * sizeof(StoreType));
}
// Now, scale the image in the A direction.
double scale = (double)dest.ASIZE() / (double)source.ASIZE();
// First, scale the image in the A direction.
double scale;
StoreType *temp_source, *temp_dest;
StoreType *temp_source = (StoreType *)PANDA_MALLOC_ARRAY(source.ASIZE() * sizeof(StoreType));
StoreType *temp_dest = (StoreType *)PANDA_MALLOC_ARRAY(dest.ASIZE() * sizeof(StoreType));
scale = (double)dest.ASIZE() / (double)source.ASIZE();
temp_source = (StoreType *)PANDA_MALLOC_ARRAY(source.ASIZE() * sizeof(StoreType));
temp_dest = (StoreType *)PANDA_MALLOC_ARRAY(dest.ASIZE() * sizeof(StoreType));
WorkType *filter;
double filter_width;
make_filter(scale, width, filter, filter_width);
for (b=0; b<source.BSIZE(); b++) {
for (a=0; a<source.ASIZE(); a++) {
temp_source[a] = (StoreType)(source_max * source.GETVAL(a, b));
for (b = 0; b < source.BSIZE(); b++) {
for (a = 0; a < source.ASIZE(); a++) {
temp_source[a] = (StoreType)(source_max * source.GETVAL(a, b, channel));
}
filter_row(temp_dest, dest.ASIZE(),
@ -58,7 +60,7 @@ FUNCTION_NAME(PNMImage &dest, const PNMImage &source,
scale,
filter, filter_width);
for (a=0; a<dest.ASIZE(); a++) {
for (a = 0; a < dest.ASIZE(); a++) {
matrix[a][b] = temp_dest[a];
}
}
@ -69,20 +71,18 @@ FUNCTION_NAME(PNMImage &dest, const PNMImage &source,
// Now, scale the image in the B direction.
scale = (double)dest.BSIZE() / (double)source.BSIZE();
temp_dest = (StoreType *)PANDA_MALLOC_ARRAY(dest.BSIZE() * sizeof(StoreType));
make_filter(scale, width, filter, filter_width);
for (a=0; a<dest.ASIZE(); a++) {
for (a = 0; a < dest.ASIZE(); a++) {
filter_row(temp_dest, dest.BSIZE(),
matrix[a], source.BSIZE(),
scale,
filter, filter_width);
for (b=0; b<dest.BSIZE(); b++) {
dest.SETVAL(a, b, (double)temp_dest[b]/(double)source_max);
for (b = 0; b < dest.BSIZE(); b++) {
dest.SETVAL(a, b, channel, (double)temp_dest[b]/(double)source_max);
}
}
@ -91,7 +91,7 @@ FUNCTION_NAME(PNMImage &dest, const PNMImage &source,
// Now, clean up our temp matrix and go home!
for (a=0; a<dest.ASIZE(); a++) {
for (a = 0; a < dest.ASIZE(); a++) {
PANDA_FREE_ARRAY(matrix[a]);
}
PANDA_FREE_ARRAY(matrix);

View File

@ -0,0 +1,114 @@
// Filename: pnm-image-filter-sparse-core.cxx
// Created by: drose (25Jan13)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
// We map X and Y to A and B, because we might change our minds about which
// is dominant, and we map get/set functions for the channel in question to
// GETVAL/SETVAL.
static void
FUNCTION_NAME(IMAGETYPE &dest, const IMAGETYPE &source,
double width, FilterFunction *make_filter, int channel) {
if (!dest.is_valid() || !source.is_valid()) {
return;
}
// First, set up a 2-d column-major matrix of StoreTypes, big enough to hold
// the image xelvals scaled in the A direction only. This will hold the
// adjusted xel data from our first pass.
typedef StoreType *StoreTypeP;
StoreType **matrix = (StoreType **)PANDA_MALLOC_ARRAY(dest.ASIZE() * sizeof(StoreType *));
StoreType **matrix_weight = (StoreType **)PANDA_MALLOC_ARRAY(dest.ASIZE() * sizeof(StoreType *));
int a, b;
for (a=0; a<dest.ASIZE(); a++) {
matrix[a] = (StoreType *)PANDA_MALLOC_ARRAY(source.BSIZE() * sizeof(StoreType));
matrix_weight[a] = (StoreType *)PANDA_MALLOC_ARRAY(source.BSIZE() * sizeof(StoreType));
}
// First, scale the image in the A direction.
double scale;
StoreType *temp_source, *temp_source_weight, *temp_dest, *temp_dest_weight;
scale = (double)dest.ASIZE() / (double)source.ASIZE();
temp_source = (StoreType *)PANDA_MALLOC_ARRAY(source.ASIZE() * sizeof(StoreType));
temp_source_weight = (StoreType *)PANDA_MALLOC_ARRAY(source.ASIZE() * sizeof(StoreType));
temp_dest = (StoreType *)PANDA_MALLOC_ARRAY(dest.ASIZE() * sizeof(StoreType));
temp_dest_weight = (StoreType *)PANDA_MALLOC_ARRAY(dest.ASIZE() * sizeof(StoreType));
WorkType *filter;
double filter_width;
make_filter(scale, width, filter, filter_width);
memset(temp_source_weight, 0, source.ASIZE() * sizeof(StoreType));
for (b = 0; b < source.BSIZE(); b++) {
for (a = 0; a < source.ASIZE(); a++) {
if (source.HASVAL(a, b)) {
temp_source[a] = (StoreType)(source_max * source.GETVAL(a, b, channel));
temp_source_weight[a] = filter_max;
}
}
filter_sparse_row(temp_dest, temp_dest_weight, dest.ASIZE(),
temp_source, temp_source_weight, source.ASIZE(),
scale,
filter, filter_width);
for (a = 0; a < dest.ASIZE(); a++) {
matrix[a][b] = temp_dest[a];
matrix_weight[a][b] = temp_dest_weight[a];
}
}
PANDA_FREE_ARRAY(temp_source);
PANDA_FREE_ARRAY(temp_source_weight);
PANDA_FREE_ARRAY(temp_dest);
PANDA_FREE_ARRAY(temp_dest_weight);
PANDA_FREE_ARRAY(filter);
// Now, scale the image in the B direction.
scale = (double)dest.BSIZE() / (double)source.BSIZE();
temp_dest = (StoreType *)PANDA_MALLOC_ARRAY(dest.BSIZE() * sizeof(StoreType));
temp_dest_weight = (StoreType *)PANDA_MALLOC_ARRAY(dest.BSIZE() * sizeof(StoreType));
make_filter(scale, width, filter, filter_width);
for (a = 0; a < dest.ASIZE(); a++) {
filter_sparse_row(temp_dest, temp_dest_weight, dest.BSIZE(),
matrix[a], matrix_weight[a], source.BSIZE(),
scale,
filter, filter_width);
for (b = 0; b < dest.BSIZE(); b++) {
dest.SETVAL(a, b, channel, (double)temp_dest[b]/(double)source_max);
}
}
PANDA_FREE_ARRAY(temp_dest);
PANDA_FREE_ARRAY(temp_dest_weight);
PANDA_FREE_ARRAY(filter);
// Now, clean up our temp matrix and go home!
for (a = 0; a < dest.ASIZE(); a++) {
PANDA_FREE_ARRAY(matrix[a]);
PANDA_FREE_ARRAY(matrix_weight[a]);
}
PANDA_FREE_ARRAY(matrix);
PANDA_FREE_ARRAY(matrix_weight);
}

View File

@ -37,6 +37,7 @@
#include "cmath.h"
#include "pnmImage.h"
#include "pfmFile.h"
// WorkType is an abstraction that allows the filtering process to be
// recompiled to use either floating-point or integer arithmetic. On SGI
@ -121,8 +122,8 @@ filter_row(StoreType dest[], int dest_len,
// have a non-zero offset.
int offset = (int)cfloor(iscale*0.5);
for (int dest_x=0; dest_x<dest_len; dest_x++) {
double center = (dest_x-offset)/scale;
for (int dest_x = 0; dest_x < dest_len; dest_x++) {
double center = (dest_x - offset) / scale;
// 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
@ -142,19 +143,19 @@ filter_row(StoreType dest[], int dest_len,
// This loop is broken into two pieces--the left of center and the right
// 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));
for (source_x = left; source_x < right_center; source_x++) {
index = (int)(iscale * (center - source_x));
net_value += filter[index] * source[source_x];
net_weight += filter[index];
}
for (; source_x<=right; source_x++) {
index = (int)(iscale*(source_x-center));
for (; source_x <= right; source_x++) {
index = (int)(iscale * (source_x - center));
net_value += filter[index] * source[source_x];
net_weight += filter[index];
}
if (net_weight>0) {
if (net_weight > 0) {
dest[dest_x] = (StoreType)(net_value / net_weight);
} else {
dest[dest_x] = 0;
@ -163,6 +164,68 @@ filter_row(StoreType dest[], int dest_len,
Thread::consider_yield();
}
// As above, but we also accept an array of weight values per
// element, to support scaling a sparse array (as in a PfmFile).
static void
filter_sparse_row(StoreType dest[], StoreType dest_weight[], int dest_len,
const StoreType source[], const StoreType source_weight[], int source_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);
// 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);
for (int dest_x = 0; dest_x < dest_len; dest_x++) {
double center = (dest_x - offset) / scale;
// 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);
// 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.
int right_center = (int)cceil(center);
WorkType net_weight = 0;
WorkType net_value = 0;
int index, source_x;
// This loop is broken into two pieces--the left of center and the right
// 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));
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));
net_value += filter[index] * source[source_x] * source_weight[source_x];
net_weight += filter[index] * source_weight[source_x];
}
if (net_weight > 0) {
dest[dest_x] = (StoreType)(net_value / net_weight);
} else {
dest[dest_x] = 0;
}
dest_weight[dest_x] = (StoreType)net_weight;
}
Thread::consider_yield();
}
// The various filter functions are called before each axis scaling to build
// an kernel array suitable for the given scaling factor. Given a scaling
@ -186,20 +249,20 @@ box_filter_impl(double scale, double width,
// the filter function to prevent dropping below the Nyquist rate.
// Hence, we divide by scale.
fscale = 1.0 / scale;
} else {
} else {
// If we are expanding the image, we want to increase the granularity
// of the filter function since we will need to access fractional cel
// values. Hence, we multiply by scale.
fscale = scale;
}
filter_width = width;
int actual_width = (int)cceil((filter_width+1) * fscale);
int actual_width = (int)cceil((filter_width + 1) * fscale);
filter = (WorkType *)PANDA_MALLOC_ARRAY(actual_width * sizeof(WorkType));
for (int i=0; i<actual_width; i++) {
filter[i] = (i<=filter_width*fscale) ? filter_max : 0;
for (int i = 0; i < actual_width; i++) {
filter[i] = (i <= filter_width * fscale) ? filter_max : 0;
}
}
@ -219,9 +282,10 @@ gaussian_filter_impl(double scale, double width,
// values. Hence, we multiply by scale (to make fscale larger).
fscale = scale;
}
double sigma = width/2;
filter_width = 3.0 * sigma;
int actual_width = (int)cceil((filter_width+1) * fscale);
int actual_width = (int)cceil((filter_width + 1) * fscale);
// G(x, y) = (1/(2 pi sigma^2)) * exp( - (x^2 + y^2) / (2 sigma^2))
@ -230,10 +294,10 @@ gaussian_filter_impl(double scale, double width,
// so we can ignore the y^2.)
filter = (WorkType *)PANDA_MALLOC_ARRAY(actual_width * sizeof(WorkType));
double div = 2*sigma*sigma;
double div = 2 * sigma * sigma;
for (int i=0; i<actual_width; i++) {
double x = i/fscale;
for (int i = 0; i < actual_width; i++) {
double x = i / fscale;
filter[i] = (WorkType)(filter_max * exp(-x*x / div));
// The highest value of the exp function in this range is always 1.0,
// at index value 0. Thus, we scale the whole range by filter_max,
@ -267,126 +331,146 @@ gaussian_filter_impl(double scale, double width,
// These instances scale by X first, then by Y.
#define FUNCTION_NAME filter_red_xy
#define IMAGETYPE PNMImage
#define ASIZE get_x_size
#define BSIZE get_y_size
#define GETVAL(a, b) get_red(a, b)
#define SETVAL(a, b, v) set_red(a, b, v)
#define GETVAL(a, b, channel) get_red(a, b)
#define SETVAL(a, b, channel, v) set_red(a, b, v)
#include "pnm-image-filter-core.cxx"
#undef SETVAL
#undef GETVAL
#undef BSIZE
#undef ASIZE
#undef IMAGETYPE
#undef FUNCTION_NAME
#define FUNCTION_NAME filter_green_xy
#define IMAGETYPE PNMImage
#define ASIZE get_x_size
#define BSIZE get_y_size
#define GETVAL(a, b) get_green(a, b)
#define SETVAL(a, b, v) set_green(a, b, v)
#define GETVAL(a, b, channel) get_green(a, b)
#define SETVAL(a, b, channel, v) set_green(a, b, v)
#include "pnm-image-filter-core.cxx"
#undef SETVAL
#undef GETVAL
#undef BSIZE
#undef ASIZE
#undef IMAGETYPE
#undef FUNCTION_NAME
#define FUNCTION_NAME filter_blue_xy
#define IMAGETYPE PNMImage
#define ASIZE get_x_size
#define BSIZE get_y_size
#define GETVAL(a, b) get_blue(a, b)
#define SETVAL(a, b, v) set_blue(a, b, v)
#define GETVAL(a, b, channel) get_blue(a, b)
#define SETVAL(a, b, channel, v) set_blue(a, b, v)
#include "pnm-image-filter-core.cxx"
#undef SETVAL
#undef GETVAL
#undef BSIZE
#undef ASIZE
#undef IMAGETYPE
#undef FUNCTION_NAME
#define FUNCTION_NAME filter_gray_xy
#define IMAGETYPE PNMImage
#define ASIZE get_x_size
#define BSIZE get_y_size
#define GETVAL(a, b) get_bright(a, b)
#define SETVAL(a, b, v) set_xel(a, b, v)
#define GETVAL(a, b, channel) get_bright(a, b)
#define SETVAL(a, b, channel, v) set_xel(a, b, v)
#include "pnm-image-filter-core.cxx"
#undef SETVAL
#undef GETVAL
#undef BSIZE
#undef ASIZE
#undef IMAGETYPE
#undef FUNCTION_NAME
#define FUNCTION_NAME filter_alpha_xy
#define IMAGETYPE PNMImage
#define ASIZE get_x_size
#define BSIZE get_y_size
#define GETVAL(a, b) get_alpha(a, b)
#define SETVAL(a, b, v) set_alpha(a, b, v)
#define GETVAL(a, b, channel) get_alpha(a, b)
#define SETVAL(a, b, channel, v) set_alpha(a, b, v)
#include "pnm-image-filter-core.cxx"
#undef SETVAL
#undef GETVAL
#undef BSIZE
#undef ASIZE
#undef IMAGETYPE
#undef FUNCTION_NAME
// These instances scale by Y first, then by X.
#define FUNCTION_NAME filter_red_yx
#define IMAGETYPE PNMImage
#define ASIZE get_y_size
#define BSIZE get_x_size
#define GETVAL(a, b) get_red(b, a)
#define SETVAL(a, b, v) set_red(b, a, v)
#define GETVAL(a, b, channel) get_red(b, a)
#define SETVAL(a, b, channel, v) set_red(b, a, v)
#include "pnm-image-filter-core.cxx"
#undef SETVAL
#undef GETVAL
#undef BSIZE
#undef ASIZE
#undef IMAGETYPE
#undef FUNCTION_NAME
#define FUNCTION_NAME filter_green_yx
#define IMAGETYPE PNMImage
#define ASIZE get_y_size
#define BSIZE get_x_size
#define GETVAL(a, b) get_green(b, a)
#define SETVAL(a, b, v) set_green(b, a, v)
#define GETVAL(a, b, channel) get_green(b, a)
#define SETVAL(a, b, channel, v) set_green(b, a, v)
#include "pnm-image-filter-core.cxx"
#undef SETVAL
#undef GETVAL
#undef BSIZE
#undef ASIZE
#undef IMAGETYPE
#undef FUNCTION_NAME
#define FUNCTION_NAME filter_blue_yx
#define IMAGETYPE PNMImage
#define ASIZE get_y_size
#define BSIZE get_x_size
#define GETVAL(a, b) get_blue(b, a)
#define SETVAL(a, b, v) set_blue(b, a, v)
#define GETVAL(a, b, channel) get_blue(b, a)
#define SETVAL(a, b, channel, v) set_blue(b, a, v)
#include "pnm-image-filter-core.cxx"
#undef SETVAL
#undef GETVAL
#undef BSIZE
#undef ASIZE
#undef IMAGETYPE
#undef FUNCTION_NAME
#define FUNCTION_NAME filter_gray_yx
#define IMAGETYPE PNMImage
#define ASIZE get_y_size
#define BSIZE get_x_size
#define GETVAL(a, b) get_bright(b, a)
#define SETVAL(a, b, v) set_xel(b, a, v)
#define GETVAL(a, b, channel) get_bright(b, a)
#define SETVAL(a, b, channel, v) set_xel(b, a, v)
#include "pnm-image-filter-core.cxx"
#undef SETVAL
#undef GETVAL
#undef BSIZE
#undef ASIZE
#undef IMAGETYPE
#undef FUNCTION_NAME
#define FUNCTION_NAME filter_alpha_yx
#define IMAGETYPE PNMImage
#define ASIZE get_y_size
#define BSIZE get_x_size
#define GETVAL(a, b) get_alpha(b, a)
#define SETVAL(a, b, v) set_alpha(b, a, v)
#define GETVAL(a, b, channel) get_alpha(b, a)
#define SETVAL(a, b, channel, v) set_alpha(b, a, v)
#include "pnm-image-filter-core.cxx"
#undef SETVAL
#undef GETVAL
#undef BSIZE
#undef ASIZE
#undef IMAGETYPE
#undef FUNCTION_NAME
@ -399,30 +483,35 @@ filter_image(PNMImage &dest, const PNMImage &source,
// We want to scale by the smallest destination axis first, for a
// slight performance gain.
// In the PNMImage case (unlike the PfmFile case), the channel
// parameter is not used. We *could* use it to avoid the
// replication of quite so many functions, but we replicate them
// anyway, for another tiny performance gain.
if (dest.get_x_size() <= dest.get_y_size()) {
if (dest.is_grayscale() || source.is_grayscale()) {
filter_gray_xy(dest, source, width, make_filter);
filter_gray_xy(dest, source, width, make_filter, 0);
} else {
filter_red_xy(dest, source, width, make_filter);
filter_green_xy(dest, source, width, make_filter);
filter_blue_xy(dest, source, width, make_filter);
filter_red_xy(dest, source, width, make_filter, 0);
filter_green_xy(dest, source, width, make_filter, 0);
filter_blue_xy(dest, source, width, make_filter, 0);
}
if (dest.has_alpha() && source.has_alpha()) {
filter_alpha_xy(dest, source, width, make_filter);
filter_alpha_xy(dest, source, width, make_filter, 0);
}
} else {
if (dest.is_grayscale() || source.is_grayscale()) {
filter_gray_yx(dest, source, width, make_filter);
filter_gray_yx(dest, source, width, make_filter, 0);
} else {
filter_red_yx(dest, source, width, make_filter);
filter_green_yx(dest, source, width, make_filter);
filter_blue_yx(dest, source, width, make_filter);
filter_red_yx(dest, source, width, make_filter, 0);
filter_green_yx(dest, source, width, make_filter, 0);
filter_blue_yx(dest, source, width, make_filter, 0);
}
if (dest.has_alpha() && source.has_alpha()) {
filter_alpha_yx(dest, source, width, make_filter);
filter_alpha_yx(dest, source, width, make_filter, 0);
}
}
}
@ -457,6 +546,137 @@ gaussian_filter_from(double width, const PNMImage &copy) {
filter_image(*this, copy, width, &gaussian_filter_impl);
}
// Now we do it again, this time for PfmFile. In this case we also
// need to support the sparse variants, since PfmFiles can be
// incomplete. However, we don't need to have a different function
// for each channel.
#define FUNCTION_NAME filter_pfm_xy
#define IMAGETYPE PfmFile
#define ASIZE get_x_size
#define BSIZE get_y_size
#define GETVAL(a, b, channel) get_component(a, b, channel)
#define SETVAL(a, b, channel, v) set_component(a, b, channel, v)
#include "pnm-image-filter-core.cxx"
#undef SETVAL
#undef GETVAL
#undef BSIZE
#undef ASIZE
#undef IMAGETYPE
#undef FUNCTION_NAME
#define FUNCTION_NAME filter_pfm_yx
#define IMAGETYPE PfmFile
#define ASIZE get_y_size
#define BSIZE get_x_size
#define GETVAL(a, b, channel) get_component(b, a, channel)
#define SETVAL(a, b, channel, v) set_component(b, a, channel, v)
#include "pnm-image-filter-core.cxx"
#undef SETVAL
#undef GETVAL
#undef BSIZE
#undef ASIZE
#undef IMAGETYPE
#undef FUNCTION_NAME
#define FUNCTION_NAME filter_pfm_sparse_xy
#define IMAGETYPE PfmFile
#define ASIZE get_x_size
#define BSIZE get_y_size
#define HASVAL(a, b) has_point(a, b)
#define GETVAL(a, b, channel) get_component(a, b, channel)
#define SETVAL(a, b, channel, v) set_component(a, b, channel, v)
#include "pnm-image-filter-sparse-core.cxx"
#undef SETVAL
#undef GETVAL
#undef HASVAL
#undef BSIZE
#undef ASIZE
#undef IMAGETYPE
#undef FUNCTION_NAME
#define FUNCTION_NAME filter_pfm_sparse_yx
#define IMAGETYPE PfmFile
#define ASIZE get_y_size
#define BSIZE get_x_size
#define HASVAL(a, b) has_point(b, a)
#define GETVAL(a, b, channel) get_component(b, a, channel)
#define SETVAL(a, b, channel, v) set_component(b, a, channel, v)
#include "pnm-image-filter-sparse-core.cxx"
#undef SETVAL
#undef GETVAL
#undef HASVAL
#undef BSIZE
#undef ASIZE
#undef IMAGETYPE
#undef FUNCTION_NAME
// filter_image pulls everything together, and filters one image into
// another. Both images can be the same with no ill effects.
static void
filter_image(PfmFile &dest, const PfmFile &source,
double width, FilterFunction *make_filter) {
int num_channels = min(dest.get_num_channels(), source.get_num_channels());
if (source.has_no_data_value()) {
// We need to use the sparse variant.
if (dest.get_x_size() <= dest.get_y_size()) {
for (int ci = 0; ci < num_channels; ++ci) {
filter_pfm_sparse_xy(dest, source, width, make_filter, ci);
}
} else {
for (int ci = 0; ci < num_channels; ++ci) {
filter_pfm_sparse_yx(dest, source, width, make_filter, ci);
}
}
} else {
// We can use the slightly faster fully-specified variant.
if (dest.get_x_size() <= dest.get_y_size()) {
for (int ci = 0; ci < num_channels; ++ci) {
filter_pfm_xy(dest, source, width, make_filter, ci);
}
} else {
for (int ci = 0; ci < num_channels; ++ci) {
filter_pfm_yx(dest, source, width, make_filter, ci);
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PfmFile::box_filter_from
// Access: Public
// Description: Makes a resized copy of the indicated image into this
// one using the indicated filter. The image to be
// copied is squashed and stretched to match the
// dimensions of the current image, applying the
// appropriate filter to perform the stretching.
////////////////////////////////////////////////////////////////////
void PfmFile::
box_filter_from(double width, const PfmFile &copy) {
filter_image(*this, copy, width, &box_filter_impl);
}
////////////////////////////////////////////////////////////////////
// Function: PfmFile::gaussian_filter_from
// Access: Public
// Description: Makes a resized copy of the indicated image into this
// one using the indicated filter. The image to be
// copied is squashed and stretched to match the
// dimensions of the current image, applying the
// appropriate filter to perform the stretching.
////////////////////////////////////////////////////////////////////
void PfmFile::
gaussian_filter_from(double width, const PfmFile &copy) {
filter_image(*this, copy, width, &gaussian_filter_impl);
}
//
// The following functions are support for quick_box_filter().