From 8be3692ca6ed4d1033d6f3210ffc87fe528d0a47 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 6 Jun 2014 01:55:02 +0200 Subject: [PATCH] further filtering/resampling code improvements --- include/mitsuba/core/bitmap.h | 69 ++++-- include/mitsuba/core/rfilter.h | 371 +++++++++++++++++++++------------ include/mitsuba/core/util.h | 4 +- include/mitsuba/render/shape.h | 2 +- src/libcore/bitmap.cpp | 27 ++- src/libpython/base.h | 10 +- src/libpython/core.cpp | 25 ++- src/rfilters/box.cpp | 2 +- 8 files changed, 335 insertions(+), 175 deletions(-) diff --git a/include/mitsuba/core/bitmap.h b/include/mitsuba/core/bitmap.h index 3b64bb25..9384ff33 100644 --- a/include/mitsuba/core/bitmap.h +++ b/include/mitsuba/core/bitmap.h @@ -1023,9 +1023,9 @@ public: * horizontal and vertical boundary conditions when looking up data * outside of the input domain. * - * A maximum and maximum image value can be specified to prevent to prevent - * out-of-range values that are created by the resampling process. - * + * A maximum and maximum image value can be specified to prevent to + * prevent out-of-range values that are created by the resampling process. + * * The optional 'temp' parameter can be used to pass an image of * resolution Vector2i(target->getWidth(), this->getHeight()) * to avoid intermediate memory allocations. @@ -1036,25 +1036,6 @@ public: Bitmap *target, Bitmap *temp = NULL, Float minValue = 0.0f, Float maxValue = 1.0f) const; - /** - * \brief Apply a separable filter to the image - * - * Applies the provided filter while observing the specified - * horizontal and vertical boundary conditions when looking up data - * outside of the input domain. - * - * A maximum and maximum image value can be specified to prevent to prevent - * out-of-range values that are created by the filtering process. - * - * The optional 'temp' parameter can be used to pass an image of - * the same dimensions as the source and target image. - */ - void filter(const ReconstructionFilter *rfilter, - ReconstructionFilter::EBoundaryCondition bch, - ReconstructionFilter::EBoundaryCondition bcv, - Bitmap *target, Bitmap *temp = NULL, - Float minValue = 0.0f, Float maxValue = 1.0f) const; - /** * \brief Up- or down-sample this image to a different resolution * @@ -1064,6 +1045,8 @@ public: * * A maximum and maximum image value can be specified to prevent to prevent * out-of-range values that are created by the resampling process. + * + * This function allocates a new output image and returns it. */ ref resample(const ReconstructionFilter *rfilter, ReconstructionFilter::EBoundaryCondition bch, @@ -1071,6 +1054,48 @@ public: const Vector2i &size, Float minValue = 0.0f, Float maxValue = 1.0f) const; + /** + * \brief Apply a separable convolution filter to the image + * + * Applies the provided filter while observing the specified + * horizontal and vertical boundary conditions when looking up data + * outside of the input domain. + * + * In comparison to \ref convolve(), this function operates + * in the primal domain. + * + * A maximum and maximum image value can be specified to prevent to + * prevent out-of-range values that are created by the filtering process. + * + * The optional 'temp' parameter can be used to pass an image of + * the same dimensions as the source and target image. + */ + void filter(const ReconstructionFilter *rfilter, + ReconstructionFilter::EBoundaryCondition bch, + ReconstructionFilter::EBoundaryCondition bcv, + Bitmap *target, Bitmap *temp = NULL, + Float minValue = 0.0f, Float maxValue = 1.0f) const; + + /** + * \brief Apply a separable convolution filter to the image + * + * Applies the provided filter while observing the specified + * horizontal and vertical boundary conditions when looking up data + * outside of the input domain. + * + * In comparison to \ref convolve(), this function operates + * in the primal domain. + * + * A maximum and maximum image value can be specified to prevent to + * prevent out-of-range values that are created by the filtering process. + * + * This function allocates a new output image and returns it. + */ + ref filter(const ReconstructionFilter *rfilter, + ReconstructionFilter::EBoundaryCondition bch, + ReconstructionFilter::EBoundaryCondition bcv, + Float minValue = 0.0f, Float maxValue = 1.0f) const; + //! @} // ====================================================================== diff --git a/include/mitsuba/core/rfilter.h b/include/mitsuba/core/rfilter.h index 186d71cd..81e3617d 100644 --- a/include/mitsuba/core/rfilter.h +++ b/include/mitsuba/core/rfilter.h @@ -84,8 +84,8 @@ public: MTS_DECLARE_CLASS() protected: - /// Create a new reconstruction filter - ReconstructionFilter(const Properties &props); + /// Create a new reconstruction filter + ReconstructionFilter(const Properties &props); /// Unserialize a filter ReconstructionFilter(Stream *stream, InstanceManager *manager); @@ -121,126 +121,88 @@ template struct Resampler { * outside of the defined input domain. */ Resampler(const ReconstructionFilter *rfilter, ReconstructionFilter::EBoundaryCondition bc, - int sourceRes, int targetRes) : m_bc(bc), m_sourceRes(sourceRes), m_targetRes(targetRes) { + int sourceRes, int targetRes) : m_bc(bc), m_sourceRes(sourceRes), m_targetRes(targetRes), + m_start(NULL), m_weights(NULL) { SAssert(sourceRes > 0 && targetRes > 0); Float filterRadius = rfilter->getRadius(), scale = 1.0f, invScale = 1.0f; /* Low-pass filter: scale reconstruction filters when downsampling */ if (targetRes < sourceRes) { scale = (Float) sourceRes / (Float) targetRes; - invScale = 1 / scale; + invScale = 1 / scale; filterRadius *= scale; } - m_taps = floorToInt(filterRadius * 2); - m_start = new int[targetRes]; - m_weights = new Scalar[m_taps * targetRes]; - m_fastStart = 0; - m_fastEnd = m_targetRes; + m_taps = ceilToInt(filterRadius * 2); + if (sourceRes == targetRes && (m_taps % 2) != 1) + --m_taps; + m_halfTaps = m_taps / 2; - for (int i=0; i= m_sourceRes) - m_fastEnd = std::min(m_fastEnd, i - 1); + /* Determine the index of the first original sample that might contribute */ + m_start[i] = floorToInt(center - filterRadius + (Float) 0.5f); + /* Determine the size of center region, on which to run fast non condition-aware code */ + if (m_start[i] < 0) + m_fastStart = std::max(m_fastStart, i + 1); + else if (m_start[i] + m_taps - 1 >= m_sourceRes) + m_fastEnd = std::min(m_fastEnd, i - 1); + + Float sum = 0; + for (int j=0; jeval(pos * invScale); + m_weights[i * m_taps + j] = (Scalar) weight; + sum += weight; + } + + /* Normalize the contribution of each sample */ + Float normalization = 1.0f / sum; + for (int j=0; jeval(pos * invScale); - m_weights[i * m_taps + j] = (Scalar) weight; + for (int i=0; ieval((Float) (i-m_halfTaps)); + m_weights[i] = weight; sum += weight; } - - /* Normalize the contribution of each sample */ Float normalization = 1.0f / sum; - for (int j=0; j struct Resampler { void resampleAndClamp(const Scalar *source, size_t sourceStride, Scalar *target, size_t targetStride, int channels, Scalar min = (Scalar) 0, Scalar max = (Scalar) 1) { - const int taps = m_taps; + const int taps = m_taps, halfTaps = m_halfTaps; targetStride = channels * (targetStride - 1); sourceStride *= channels; - /* Resample the left border region, while accounting for the boundary conditions */ - for (int i=0; i #include #include +#include #if defined(__WINDOWS__) #undef _CRT_SECURE_NO_WARNINGS @@ -2216,12 +2217,12 @@ void Bitmap::applyMatrix(Float matrix_[3][3]) { } } -/// Bitmap resampling utility function +/// Bitmap filtering / resampling utility function template static void resample(ref rfilter, ReconstructionFilter::EBoundaryCondition bch, ReconstructionFilter::EBoundaryCondition bcv, const Bitmap *source, Bitmap *target, ref temp, Float minValue, - Float maxValue, bool forceFiltering) { + Float maxValue, bool filter) { if (!rfilter) { /* Resample using a 2-lobed Lanczos reconstruction filter */ @@ -2235,7 +2236,7 @@ template static void resample(ref } if (source->getHeight() == target->getHeight() && - source->getWidth() == target->getWidth() && !forceFiltering) { + source->getWidth() == target->getWidth() && !filter) { memcpy(target->getData(), source->getData(), source->getBufferSize()); return; } @@ -2245,13 +2246,13 @@ template static void resample(ref minValue != -std::numeric_limits::infinity() || maxValue != std::numeric_limits::infinity(); - if (source->getWidth() != target->getWidth() || forceFiltering) { + if (source->getWidth() != target->getWidth() || filter) { /* Re-sample along the X direction */ Resampler r(rfilter, bch, source->getWidth(), target->getWidth()); /* Create a bitmap for intermediate storage */ if (!temp) { - if (source->getHeight() == target->getHeight() && !forceFiltering) + if (source->getHeight() == target->getHeight() && !filter) temp = target; // write directly to the output bitmap else // otherwise: write to a temporary bitmap temp = new Bitmap(source->getPixelFormat(), source->getComponentFormat(), @@ -2289,7 +2290,7 @@ template static void resample(ref source = temp; } - if (source->getHeight() != target->getHeight() || forceFiltering) { + if (source->getHeight() != target->getHeight() || filter) { /* Re-sample along the Y direction */ Resampler r(rfilter, bcv, source->getHeight(), target->getHeight()); @@ -2370,7 +2371,6 @@ void Bitmap::filter(const ReconstructionFilter *rfilter, } } - ref Bitmap::resample(const ReconstructionFilter *rfilter, ReconstructionFilter::EBoundaryCondition bch, ReconstructionFilter::EBoundaryCondition bcv, @@ -2378,10 +2378,23 @@ ref Bitmap::resample(const ReconstructionFilter *rfilter, ref result = new Bitmap(m_pixelFormat, m_componentFormat, size); result->m_metadata = m_metadata; result->m_gamma = m_gamma; + result->m_channelNames = m_channelNames; resample(rfilter, bch, bcv, result, NULL, minValue, maxValue); return result; } +ref Bitmap::filter(const ReconstructionFilter *rfilter, + ReconstructionFilter::EBoundaryCondition bch, + ReconstructionFilter::EBoundaryCondition bcv, + Float minValue, Float maxValue) const { + ref result = new Bitmap(m_pixelFormat, m_componentFormat, getSize()); + result->m_metadata = m_metadata; + result->m_gamma = m_gamma; + result->m_channelNames = m_channelNames; + filter(rfilter, bch, bcv, result, NULL, minValue, maxValue); + return result; +} + bool Bitmap::operator==(const Bitmap &bitmap) const { return m_pixelFormat == bitmap.m_pixelFormat && m_componentFormat == bitmap.m_componentFormat && diff --git a/src/libpython/base.h b/src/libpython/base.h index 5e546a4d..71c75ed0 100644 --- a/src/libpython/base.h +++ b/src/libpython/base.h @@ -26,6 +26,11 @@ #pragma warning(disable : 4267) // 'return' : conversion from 'size_t' to 'long', possible loss of data #endif +#define BP_RETURN_CONSTREF bp::return_value_policy() +#define BP_RETURN_NONCONSTREF bp::return_value_policy() +#define BP_RETURN_VALUE bp::return_value_policy() +#define BP_RETURN_INTREF bp::return_internal_reference<>() + #define BP_STRUCT_DECL(Name, Init) \ bp::class_ Name ##_struct(#Name, Init); \ bp::register_ptr_to_python(); @@ -149,11 +154,6 @@ bp::detail::current_scope = value.ptr(); \ } while (0); -#define BP_RETURN_CONSTREF bp::return_value_policy() -#define BP_RETURN_NONCONSTREF bp::return_value_policy() -#define BP_RETURN_VALUE bp::return_value_policy() -#define BP_RETURN_INTREF bp::return_internal_reference<>() - namespace boost { namespace python { template T* get_pointer(mitsuba::ref & p) { diff --git a/src/libpython/core.cpp b/src/libpython/core.cpp index 233b73af..74e2473f 100644 --- a/src/libpython/core.cpp +++ b/src/libpython/core.cpp @@ -604,13 +604,13 @@ static ref pluginmgr_create_helper(PluginManager *manager, b return object; } -static ref pluginmgr_create(PluginManager *manager, bp::dict dict) { +static bp::object pluginmgr_create(PluginManager *manager, bp::dict dict) { std::map objs; ref result = pluginmgr_create_helper(manager, dict, objs); for (std::map::iterator it = objs.begin(); it != objs.end(); ++it) it->second->decRef(); - return result; + return cast(result); } static bp::tuple mkCoordinateSystem(const Vector &n) { @@ -1099,8 +1099,10 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(fromLinearRGB_overloads, fromLinearRGB, 3 BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(fromXYZ_overloads, fromXYZ, 3, 4) BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(fromIPT_overloads, fromIPT, 3, 4) BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(reset_overloads, reset, 0, 1) -BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(filter_overloads, filter, 4, 7) -BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(resample_overloads, resample, 4, 7) +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(resample1_overloads, resample, 4, 7) +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(resample2_overloads, resample, 6, 4) +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(filter1_overloads, filter, 4, 7) +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(filter2_overloads, filter, 3, 5) #define IMPLEMENT_ANIMATION_TRACK(Name) \ BP_CLASS(Name, AbstractAnimationTrack, (bp::init())) \ @@ -1475,13 +1477,17 @@ void export_core() { ReconstructionFilter::EBoundaryCondition, ReconstructionFilter::EBoundaryCondition, Bitmap *, Bitmap *, Float, Float) const = &Bitmap::resample; + ref (Bitmap::*resample_2)(const ReconstructionFilter *, + ReconstructionFilter::EBoundaryCondition, ReconstructionFilter::EBoundaryCondition, + const Vector2i &, Float, Float) const = &Bitmap::resample; + void (Bitmap::*filter_1)(const ReconstructionFilter *, ReconstructionFilter::EBoundaryCondition, ReconstructionFilter::EBoundaryCondition, Bitmap *, Bitmap *, Float, Float) const = &Bitmap::filter; - ref (Bitmap::*resample_2)(const ReconstructionFilter *, + ref (Bitmap::*filter_2)(const ReconstructionFilter *, ReconstructionFilter::EBoundaryCondition, ReconstructionFilter::EBoundaryCondition, - const Vector2i &, Float, Float) const = &Bitmap::resample; + Float, Float) const = &Bitmap::filter; const std::vector & (Bitmap::*getChannelNames_1)() const = &Bitmap::getChannelNames; @@ -1541,9 +1547,10 @@ void export_core() { .def("copyFrom", copyFrom_3) .def("convolve", &Bitmap::convolve) .def("arithmeticOperation", &Bitmap::arithmeticOperation, BP_RETURN_VALUE) - .def("filter", filter_1, filter_overloads()) - .def("resample", resample_1, resample_overloads()) - .def("resample", resample_2, BP_RETURN_VALUE) + .def("resample", resample_1, resample1_overloads()) + .def("resample", resample_2, resample2_overloads()[BP_RETURN_VALUE]) + .def("filter", filter_1, filter1_overloads()) + .def("filter", filter_2, filter2_overloads()[BP_RETURN_VALUE]) .def("setGamma", &Bitmap::setGamma) .def("getGamma", &Bitmap::getGamma) .def("setMetadataString", &Bitmap::setMetadataString) diff --git a/src/rfilters/box.cpp b/src/rfilters/box.cpp index 7f7e148a..072022ab 100644 --- a/src/rfilters/box.cpp +++ b/src/rfilters/box.cpp @@ -48,7 +48,7 @@ public: } std::string toString() const { - return "BoxFilter[]"; + return formatString("GaussianFilter[radius=%f]", m_radius); } MTS_DECLARE_CLASS()