further filtering/resampling code improvements
parent
6b3246d15a
commit
8be3692ca6
|
@ -1023,9 +1023,9 @@ public:
|
||||||
* horizontal and vertical boundary conditions when looking up data
|
* horizontal and vertical boundary conditions when looking up data
|
||||||
* outside of the input domain.
|
* outside of the input domain.
|
||||||
*
|
*
|
||||||
* A maximum and maximum image value can be specified to prevent to prevent
|
* A maximum and maximum image value can be specified to prevent to
|
||||||
* out-of-range values that are created by the resampling process.
|
* prevent out-of-range values that are created by the resampling process.
|
||||||
*
|
*
|
||||||
* The optional 'temp' parameter can be used to pass an image of
|
* The optional 'temp' parameter can be used to pass an image of
|
||||||
* resolution <tt>Vector2i(target->getWidth(), this->getHeight())</tt>
|
* resolution <tt>Vector2i(target->getWidth(), this->getHeight())</tt>
|
||||||
* to avoid intermediate memory allocations.
|
* to avoid intermediate memory allocations.
|
||||||
|
@ -1036,25 +1036,6 @@ public:
|
||||||
Bitmap *target, Bitmap *temp = NULL,
|
Bitmap *target, Bitmap *temp = NULL,
|
||||||
Float minValue = 0.0f, Float maxValue = 1.0f) const;
|
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
|
* \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
|
* A maximum and maximum image value can be specified to prevent to prevent
|
||||||
* out-of-range values that are created by the resampling process.
|
* out-of-range values that are created by the resampling process.
|
||||||
|
*
|
||||||
|
* This function allocates a new output image and returns it.
|
||||||
*/
|
*/
|
||||||
ref<Bitmap> resample(const ReconstructionFilter *rfilter,
|
ref<Bitmap> resample(const ReconstructionFilter *rfilter,
|
||||||
ReconstructionFilter::EBoundaryCondition bch,
|
ReconstructionFilter::EBoundaryCondition bch,
|
||||||
|
@ -1071,6 +1054,48 @@ public:
|
||||||
const Vector2i &size, Float minValue = 0.0f,
|
const Vector2i &size, Float minValue = 0.0f,
|
||||||
Float maxValue = 1.0f) const;
|
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<Bitmap> filter(const ReconstructionFilter *rfilter,
|
||||||
|
ReconstructionFilter::EBoundaryCondition bch,
|
||||||
|
ReconstructionFilter::EBoundaryCondition bcv,
|
||||||
|
Float minValue = 0.0f, Float maxValue = 1.0f) const;
|
||||||
|
|
||||||
//! @}
|
//! @}
|
||||||
// ======================================================================
|
// ======================================================================
|
||||||
|
|
||||||
|
|
|
@ -84,8 +84,8 @@ public:
|
||||||
|
|
||||||
MTS_DECLARE_CLASS()
|
MTS_DECLARE_CLASS()
|
||||||
protected:
|
protected:
|
||||||
/// Create a new reconstruction filter
|
/// Create a new reconstruction filter
|
||||||
ReconstructionFilter(const Properties &props);
|
ReconstructionFilter(const Properties &props);
|
||||||
|
|
||||||
/// Unserialize a filter
|
/// Unserialize a filter
|
||||||
ReconstructionFilter(Stream *stream, InstanceManager *manager);
|
ReconstructionFilter(Stream *stream, InstanceManager *manager);
|
||||||
|
@ -121,126 +121,88 @@ template <typename Scalar> struct Resampler {
|
||||||
* outside of the defined input domain.
|
* outside of the defined input domain.
|
||||||
*/
|
*/
|
||||||
Resampler(const ReconstructionFilter *rfilter, ReconstructionFilter::EBoundaryCondition bc,
|
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);
|
SAssert(sourceRes > 0 && targetRes > 0);
|
||||||
Float filterRadius = rfilter->getRadius(), scale = 1.0f, invScale = 1.0f;
|
Float filterRadius = rfilter->getRadius(), scale = 1.0f, invScale = 1.0f;
|
||||||
|
|
||||||
/* Low-pass filter: scale reconstruction filters when downsampling */
|
/* Low-pass filter: scale reconstruction filters when downsampling */
|
||||||
if (targetRes < sourceRes) {
|
if (targetRes < sourceRes) {
|
||||||
scale = (Float) sourceRes / (Float) targetRes;
|
scale = (Float) sourceRes / (Float) targetRes;
|
||||||
invScale = 1 / scale;
|
invScale = 1 / scale;
|
||||||
filterRadius *= scale;
|
filterRadius *= scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_taps = floorToInt(filterRadius * 2);
|
m_taps = ceilToInt(filterRadius * 2);
|
||||||
m_start = new int[targetRes];
|
if (sourceRes == targetRes && (m_taps % 2) != 1)
|
||||||
m_weights = new Scalar[m_taps * targetRes];
|
--m_taps;
|
||||||
m_fastStart = 0;
|
m_halfTaps = m_taps / 2;
|
||||||
m_fastEnd = m_targetRes;
|
|
||||||
|
|
||||||
for (int i=0; i<targetRes; i++) {
|
if (sourceRes != targetRes) { /* Resampling mode */
|
||||||
/* Compute the fractional coordinates of the new sample i in the original coordinates */
|
m_start = new int[targetRes];
|
||||||
Float center = (i + (Float) 0.5f) / targetRes * sourceRes;
|
m_weights = new Scalar[m_taps * targetRes];
|
||||||
|
m_fastStart = 0;
|
||||||
|
m_fastEnd = m_targetRes;
|
||||||
|
|
||||||
/* Determine the index of the first original sample that might contribute */
|
for (int i=0; i<targetRes; i++) {
|
||||||
m_start[i] = floorToInt(center - filterRadius + (Float) 0.5f);
|
/* Compute the fractional coordinates of the new sample i in the original coordinates */
|
||||||
|
Float center = (i + (Float) 0.5f) / targetRes * sourceRes;
|
||||||
|
|
||||||
/* Determine the size of center region, on which to run fast non condition-aware code */
|
/* Determine the index of the first original sample that might contribute */
|
||||||
if (m_start[i] < 0)
|
m_start[i] = floorToInt(center - filterRadius + (Float) 0.5f);
|
||||||
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);
|
|
||||||
|
|
||||||
|
/* 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; j<m_taps; j++) {
|
||||||
|
/* Compute the the position where the filter should be evaluated */
|
||||||
|
Float pos = m_start[i] + j + (Float) 0.5f - center;
|
||||||
|
|
||||||
|
/* Perform the evaluation and record the weight */
|
||||||
|
Float weight = rfilter->eval(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; j<m_taps; j++) {
|
||||||
|
Scalar &value = m_weights[i * m_taps + j];
|
||||||
|
value = (Scalar) ((Float) value * normalization);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { /* Filtering mode */
|
||||||
|
m_weights = new Scalar[m_taps];
|
||||||
Float sum = 0;
|
Float sum = 0;
|
||||||
for (int j=0; j<m_taps; j++) {
|
for (int i=0; i<m_taps; i++) {
|
||||||
/* Compute the the position where the filter should be evaluated */
|
Float weight = (Scalar) rfilter->eval((Float) (i-m_halfTaps));
|
||||||
Float pos = m_start[i] + j + (Float) 0.5f - center;
|
m_weights[i] = weight;
|
||||||
|
|
||||||
/* Perform the evaluation and record the weight */
|
|
||||||
Float weight = rfilter->eval(pos * invScale);
|
|
||||||
m_weights[i * m_taps + j] = (Scalar) weight;
|
|
||||||
sum += weight;
|
sum += weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Normalize the contribution of each sample */
|
|
||||||
Float normalization = 1.0f / sum;
|
Float normalization = 1.0f / sum;
|
||||||
for (int j=0; j<m_taps; j++) {
|
for (int i=0; i<m_taps; i++) {
|
||||||
Scalar &value = m_weights[i * m_taps + j];
|
Scalar &value = m_weights[i];
|
||||||
value = (Scalar) ((Float) value * normalization);
|
value = (Scalar) ((Float) value * normalization);
|
||||||
}
|
}
|
||||||
|
m_fastStart = std::min(m_halfTaps, m_targetRes-1);
|
||||||
|
m_fastEnd = std::max(m_targetRes-m_halfTaps-1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Don't have overlapping fast start/end intervals when
|
||||||
|
the target image is very small compared to the source image */
|
||||||
m_fastStart = std::min(m_fastStart, m_fastEnd);
|
m_fastStart = std::min(m_fastStart, m_fastEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Release all memory
|
/// Release all memory
|
||||||
~Resampler() {
|
~Resampler() {
|
||||||
delete[] m_start;
|
if (m_start)
|
||||||
delete[] m_weights;
|
delete[] m_start;
|
||||||
}
|
if (m_weights)
|
||||||
|
delete[] m_weights;
|
||||||
/**
|
|
||||||
* \brief Resample a multi-channel array
|
|
||||||
*
|
|
||||||
* \param source
|
|
||||||
* Source array of samples
|
|
||||||
* \param target
|
|
||||||
* Target array of samples
|
|
||||||
* \param sourceStride
|
|
||||||
* Stride of samples in the source array. A value
|
|
||||||
* of '1' implies that they are densely packed.
|
|
||||||
* \param targetStride
|
|
||||||
* Stride of samples in the source array. A value
|
|
||||||
* of '1' implies that they are densely packed.
|
|
||||||
* \param channels
|
|
||||||
* Number of channels to be resampled
|
|
||||||
*/
|
|
||||||
void resample(const Scalar *source, size_t sourceStride,
|
|
||||||
Scalar *target, size_t targetStride, int channels) {
|
|
||||||
const int taps = m_taps;
|
|
||||||
targetStride = channels * (targetStride - 1);
|
|
||||||
sourceStride *= channels;
|
|
||||||
|
|
||||||
/* Resample the left border region, while accounting for the boundary conditions */
|
|
||||||
for (int i=0; i<m_fastStart; ++i) {
|
|
||||||
int start = m_start[i];
|
|
||||||
|
|
||||||
for (int ch=0; ch<channels; ++ch) {
|
|
||||||
Scalar result = 0;
|
|
||||||
for (int j=0; j<taps; ++j)
|
|
||||||
result += lookup(source, start + j, sourceStride, ch) * m_weights[i * taps + j];
|
|
||||||
*target++ = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
target += targetStride;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Use a faster, vectorizable loop for resampling the main portion */
|
|
||||||
for (int i=m_fastStart; i<m_fastEnd; ++i) {
|
|
||||||
int start = m_start[i];
|
|
||||||
|
|
||||||
for (int ch=0; ch<channels; ++ch) {
|
|
||||||
Scalar result = 0;
|
|
||||||
for (int j=0; j<taps; ++j)
|
|
||||||
result += source[sourceStride * (start + j) + ch] * m_weights[i * taps + j];
|
|
||||||
*target++ = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
target += targetStride;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resample the right border region, while accounting for the boundary conditions */
|
|
||||||
for (int i=m_fastEnd; i<m_targetRes; ++i) {
|
|
||||||
int start = m_start[i];
|
|
||||||
|
|
||||||
for (int ch=0; ch<channels; ++ch) {
|
|
||||||
Scalar result = 0;
|
|
||||||
for (int j=0; j<taps; ++j)
|
|
||||||
result += lookup(source, start + j, sourceStride, ch) * m_weights[i * taps + j];
|
|
||||||
*target++ = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
target += targetStride;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -270,53 +232,206 @@ template <typename Scalar> struct Resampler {
|
||||||
void resampleAndClamp(const Scalar *source, size_t sourceStride,
|
void resampleAndClamp(const Scalar *source, size_t sourceStride,
|
||||||
Scalar *target, size_t targetStride, int channels,
|
Scalar *target, size_t targetStride, int channels,
|
||||||
Scalar min = (Scalar) 0, Scalar max = (Scalar) 1) {
|
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);
|
targetStride = channels * (targetStride - 1);
|
||||||
sourceStride *= channels;
|
sourceStride *= channels;
|
||||||
|
|
||||||
/* Resample the left border region, while accounting for the boundary conditions */
|
if (m_start) {
|
||||||
for (int i=0; i<m_fastStart; ++i) {
|
/* Resample the left border region, while accounting for the boundary conditions */
|
||||||
int start = m_start[i];
|
for (int i=0; i<m_fastStart; ++i) {
|
||||||
|
int start = m_start[i];
|
||||||
|
|
||||||
for (int ch=0; ch<channels; ++ch) {
|
for (int ch=0; ch<channels; ++ch) {
|
||||||
Scalar result = 0;
|
Scalar result = 0;
|
||||||
for (int j=0; j<taps; ++j)
|
for (int j=0; j<taps; ++j)
|
||||||
result += lookup(source, start + j, sourceStride, ch) * m_weights[i * taps + j];
|
result += lookup(source, start + j, sourceStride, ch) * m_weights[i * taps + j];
|
||||||
*target++ = std::min(max, std::max(min, result));
|
*target++ = std::min(max, std::max(min, result));
|
||||||
|
}
|
||||||
|
|
||||||
|
target += targetStride;
|
||||||
}
|
}
|
||||||
|
|
||||||
target += targetStride;
|
/* Use a faster, vectorizable loop for resampling the main portion */
|
||||||
}
|
for (int i=m_fastStart; i<m_fastEnd; ++i) {
|
||||||
|
int start = m_start[i];
|
||||||
|
|
||||||
/* Use a faster, vectorizable loop for resampling the main portion */
|
for (int ch=0; ch<channels; ++ch) {
|
||||||
for (int i=m_fastStart; i<m_fastEnd; ++i) {
|
Scalar result = 0;
|
||||||
int start = m_start[i];
|
for (int j=0; j<taps; ++j)
|
||||||
|
result += source[sourceStride * (start + j) + ch] * m_weights[i * taps + j];
|
||||||
|
*target++ = std::min(max, std::max(min, result));
|
||||||
|
}
|
||||||
|
|
||||||
for (int ch=0; ch<channels; ++ch) {
|
target += targetStride;
|
||||||
Scalar result = 0;
|
|
||||||
for (int j=0; j<taps; ++j)
|
|
||||||
result += source[sourceStride * (start + j) + ch] * m_weights[i * taps + j];
|
|
||||||
*target++ = std::min(max, std::max(min, result));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target += targetStride;
|
/* Resample the right border region, while accounting for the boundary conditions */
|
||||||
}
|
for (int i=m_fastEnd; i<m_targetRes; ++i) {
|
||||||
|
int start = m_start[i];
|
||||||
|
|
||||||
/* Resample the right border region, while accounting for the boundary conditions */
|
for (int ch=0; ch<channels; ++ch) {
|
||||||
for (int i=m_fastEnd; i<m_targetRes; ++i) {
|
Scalar result = 0;
|
||||||
int start = m_start[i];
|
for (int j=0; j<taps; ++j)
|
||||||
|
result += lookup(source, start + j, sourceStride, ch) * m_weights[i * taps + j];
|
||||||
|
*target++ = std::min(max, std::max(min, result));
|
||||||
|
}
|
||||||
|
|
||||||
for (int ch=0; ch<channels; ++ch) {
|
target += targetStride;
|
||||||
Scalar result = 0;
|
}
|
||||||
for (int j=0; j<taps; ++j)
|
} else {
|
||||||
result += lookup(source, start + j, sourceStride, ch) * m_weights[i * taps + j];
|
/* Filter the left border region, while accounting for the boundary conditions */
|
||||||
*target++ = std::min(max, std::max(min, result));
|
for (int i=0; i<m_fastStart; ++i) {
|
||||||
|
int start = i - halfTaps;
|
||||||
|
|
||||||
|
for (int ch=0; ch<channels; ++ch) {
|
||||||
|
Scalar result = 0;
|
||||||
|
for (int j=0; j<taps; ++j)
|
||||||
|
result += lookup(source, start + j, sourceStride, ch) * m_weights[j];
|
||||||
|
*target++ = std::min(max, std::max(min, result));
|
||||||
|
}
|
||||||
|
|
||||||
|
target += targetStride;
|
||||||
}
|
}
|
||||||
|
|
||||||
target += targetStride;
|
/* Use a faster, vectorizable loop for filtering the main portion */
|
||||||
|
for (int i=m_fastStart; i<m_fastEnd; ++i) {
|
||||||
|
int start = i - halfTaps;
|
||||||
|
|
||||||
|
for (int ch=0; ch<channels; ++ch) {
|
||||||
|
Scalar result = 0;
|
||||||
|
for (int j=0; j<taps; ++j)
|
||||||
|
result += source[sourceStride * (start + j) + ch] * m_weights[j];
|
||||||
|
*target++ = std::min(max, std::max(min, result));
|
||||||
|
}
|
||||||
|
|
||||||
|
target += targetStride;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Filter the right border region, while accounting for the boundary conditions */
|
||||||
|
for (int i=m_fastEnd; i<m_targetRes; ++i) {
|
||||||
|
int start = i - halfTaps;
|
||||||
|
|
||||||
|
for (int ch=0; ch<channels; ++ch) {
|
||||||
|
Scalar result = 0;
|
||||||
|
for (int j=0; j<taps; ++j)
|
||||||
|
result += lookup(source, start + j, sourceStride, ch) * m_weights[j];
|
||||||
|
*target++ = std::min(max, std::max(min, result));
|
||||||
|
}
|
||||||
|
|
||||||
|
target += targetStride;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Resample a multi-channel array
|
||||||
|
*
|
||||||
|
* \param source
|
||||||
|
* Source array of samples
|
||||||
|
* \param target
|
||||||
|
* Target array of samples
|
||||||
|
* \param sourceStride
|
||||||
|
* Stride of samples in the source array. A value
|
||||||
|
* of '1' implies that they are densely packed.
|
||||||
|
* \param targetStride
|
||||||
|
* Stride of samples in the source array. A value
|
||||||
|
* of '1' implies that they are densely packed.
|
||||||
|
* \param channels
|
||||||
|
* Number of channels to be resampled
|
||||||
|
*/
|
||||||
|
void resample(const Scalar *source, size_t sourceStride,
|
||||||
|
Scalar *target, size_t targetStride, int channels) {
|
||||||
|
const int taps = m_taps, halfTaps = m_halfTaps;
|
||||||
|
|
||||||
|
targetStride = channels * (targetStride - 1);
|
||||||
|
sourceStride *= channels;
|
||||||
|
|
||||||
|
if (m_start) {
|
||||||
|
/* Resample the left border region, while accounting for the boundary conditions */
|
||||||
|
for (int i=0; i<m_fastStart; ++i) {
|
||||||
|
int start = m_start[i];
|
||||||
|
|
||||||
|
for (int ch=0; ch<channels; ++ch) {
|
||||||
|
Scalar result = 0;
|
||||||
|
for (int j=0; j<taps; ++j)
|
||||||
|
result += lookup(source, start + j, sourceStride, ch) * m_weights[i * taps + j];
|
||||||
|
*target++ = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
target += targetStride;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use a faster, vectorizable loop for resampling the main portion */
|
||||||
|
for (int i=m_fastStart; i<m_fastEnd; ++i) {
|
||||||
|
int start = m_start[i];
|
||||||
|
|
||||||
|
for (int ch=0; ch<channels; ++ch) {
|
||||||
|
Scalar result = 0;
|
||||||
|
for (int j=0; j<taps; ++j)
|
||||||
|
result += source[sourceStride * (start + j) + ch] * m_weights[i * taps + j];
|
||||||
|
*target++ = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
target += targetStride;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resample the right border region, while accounting for the boundary conditions */
|
||||||
|
for (int i=m_fastEnd; i<m_targetRes; ++i) {
|
||||||
|
int start = m_start[i];
|
||||||
|
|
||||||
|
for (int ch=0; ch<channels; ++ch) {
|
||||||
|
Scalar result = 0;
|
||||||
|
for (int j=0; j<taps; ++j)
|
||||||
|
result += lookup(source, start + j, sourceStride, ch) * m_weights[i * taps + j];
|
||||||
|
*target++ = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
target += targetStride;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Filter the left border region, while accounting for the boundary conditions */
|
||||||
|
for (int i=0; i<m_fastStart; ++i) {
|
||||||
|
int start = i - halfTaps;
|
||||||
|
|
||||||
|
for (int ch=0; ch<channels; ++ch) {
|
||||||
|
Scalar result = 0;
|
||||||
|
for (int j=0; j<taps; ++j)
|
||||||
|
result += lookup(source, start + j, sourceStride, ch) * m_weights[j];
|
||||||
|
*target++ = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
target += targetStride;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use a faster, vectorizable loop for filtering the main portion */
|
||||||
|
for (int i=m_fastStart; i<m_fastEnd; ++i) {
|
||||||
|
int start = i - halfTaps;
|
||||||
|
|
||||||
|
for (int ch=0; ch<channels; ++ch) {
|
||||||
|
Scalar result = 0;
|
||||||
|
for (int j=0; j<taps; ++j)
|
||||||
|
result += source[sourceStride * (start + j) + ch] * m_weights[j];
|
||||||
|
*target++ = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
target += targetStride;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Filter the right border region, while accounting for the boundary conditions */
|
||||||
|
for (int i=m_fastEnd; i<m_targetRes; ++i) {
|
||||||
|
int start = i - halfTaps;
|
||||||
|
|
||||||
|
for (int ch=0; ch<channels; ++ch) {
|
||||||
|
Scalar result = 0;
|
||||||
|
for (int j=0; j<taps; ++j)
|
||||||
|
result += lookup(source, start + j, sourceStride, ch) * m_weights[j];
|
||||||
|
*target++ = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
target += targetStride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FINLINE Scalar lookup(const Scalar *source, int pos, size_t stride, int offset) const {
|
FINLINE Scalar lookup(const Scalar *source, int pos, size_t stride, int offset) const {
|
||||||
|
@ -349,7 +464,7 @@ private:
|
||||||
int *m_start;
|
int *m_start;
|
||||||
Scalar *m_weights;
|
Scalar *m_weights;
|
||||||
int m_fastStart, m_fastEnd;
|
int m_fastStart, m_fastEnd;
|
||||||
int m_taps;
|
int m_taps, m_halfTaps;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MTS_EXPORT_CORE std::ostream &operator<<(std::ostream &os, const ReconstructionFilter::EBoundaryCondition &value);
|
extern MTS_EXPORT_CORE std::ostream &operator<<(std::ostream &os, const ReconstructionFilter::EBoundaryCondition &value);
|
||||||
|
|
|
@ -51,8 +51,8 @@ extern MTS_EXPORT_CORE std::string indent(const std::string &string, int amount=
|
||||||
extern MTS_EXPORT_CORE std::string formatString(const char *pFmt, ...);
|
extern MTS_EXPORT_CORE std::string formatString(const char *pFmt, ...);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Convert a time difference (in ms) to a string representation
|
* \brief Convert a time difference (in seconds) to a string representation
|
||||||
* \param time Time value in milliseconds
|
* \param time Time difference in (fractional) sections
|
||||||
* \param precise When set to true, a higher-precision string representation
|
* \param precise When set to true, a higher-precision string representation
|
||||||
* is generated.
|
* is generated.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -121,7 +121,7 @@ public:
|
||||||
/// Move the intersection forward or backward through time
|
/// Move the intersection forward or backward through time
|
||||||
inline void adjustTime(Float time);
|
inline void adjustTime(Float time);
|
||||||
|
|
||||||
/// Calls the suitable implementaiton of \ref Shape::getNormalDerivative()
|
/// Calls the suitable implementation of \ref Shape::getNormalDerivative()
|
||||||
inline void getNormalDerivative(Vector &dndu, Vector &dndv,
|
inline void getNormalDerivative(Vector &dndu, Vector &dndv,
|
||||||
bool shadingFrame = true) const;
|
bool shadingFrame = true) const;
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/scoped_array.hpp>
|
#include <boost/scoped_array.hpp>
|
||||||
#include <boost/thread/mutex.hpp>
|
#include <boost/thread/mutex.hpp>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#if defined(__WINDOWS__)
|
#if defined(__WINDOWS__)
|
||||||
#undef _CRT_SECURE_NO_WARNINGS
|
#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 <typename Scalar> static void resample(ref<const ReconstructionFilter> rfilter,
|
template <typename Scalar> static void resample(ref<const ReconstructionFilter> rfilter,
|
||||||
ReconstructionFilter::EBoundaryCondition bch,
|
ReconstructionFilter::EBoundaryCondition bch,
|
||||||
ReconstructionFilter::EBoundaryCondition bcv,
|
ReconstructionFilter::EBoundaryCondition bcv,
|
||||||
const Bitmap *source, Bitmap *target, ref<Bitmap> temp, Float minValue,
|
const Bitmap *source, Bitmap *target, ref<Bitmap> temp, Float minValue,
|
||||||
Float maxValue, bool forceFiltering) {
|
Float maxValue, bool filter) {
|
||||||
|
|
||||||
if (!rfilter) {
|
if (!rfilter) {
|
||||||
/* Resample using a 2-lobed Lanczos reconstruction filter */
|
/* Resample using a 2-lobed Lanczos reconstruction filter */
|
||||||
|
@ -2235,7 +2236,7 @@ template <typename Scalar> static void resample(ref<const ReconstructionFilter>
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source->getHeight() == target->getHeight() &&
|
if (source->getHeight() == target->getHeight() &&
|
||||||
source->getWidth() == target->getWidth() && !forceFiltering) {
|
source->getWidth() == target->getWidth() && !filter) {
|
||||||
memcpy(target->getData(), source->getData(), source->getBufferSize());
|
memcpy(target->getData(), source->getData(), source->getBufferSize());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2245,13 +2246,13 @@ template <typename Scalar> static void resample(ref<const ReconstructionFilter>
|
||||||
minValue != -std::numeric_limits<Float>::infinity() ||
|
minValue != -std::numeric_limits<Float>::infinity() ||
|
||||||
maxValue != std::numeric_limits<Float>::infinity();
|
maxValue != std::numeric_limits<Float>::infinity();
|
||||||
|
|
||||||
if (source->getWidth() != target->getWidth() || forceFiltering) {
|
if (source->getWidth() != target->getWidth() || filter) {
|
||||||
/* Re-sample along the X direction */
|
/* Re-sample along the X direction */
|
||||||
Resampler<Scalar> r(rfilter, bch, source->getWidth(), target->getWidth());
|
Resampler<Scalar> r(rfilter, bch, source->getWidth(), target->getWidth());
|
||||||
|
|
||||||
/* Create a bitmap for intermediate storage */
|
/* Create a bitmap for intermediate storage */
|
||||||
if (!temp) {
|
if (!temp) {
|
||||||
if (source->getHeight() == target->getHeight() && !forceFiltering)
|
if (source->getHeight() == target->getHeight() && !filter)
|
||||||
temp = target; // write directly to the output bitmap
|
temp = target; // write directly to the output bitmap
|
||||||
else // otherwise: write to a temporary bitmap
|
else // otherwise: write to a temporary bitmap
|
||||||
temp = new Bitmap(source->getPixelFormat(), source->getComponentFormat(),
|
temp = new Bitmap(source->getPixelFormat(), source->getComponentFormat(),
|
||||||
|
@ -2289,7 +2290,7 @@ template <typename Scalar> static void resample(ref<const ReconstructionFilter>
|
||||||
source = temp;
|
source = temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source->getHeight() != target->getHeight() || forceFiltering) {
|
if (source->getHeight() != target->getHeight() || filter) {
|
||||||
/* Re-sample along the Y direction */
|
/* Re-sample along the Y direction */
|
||||||
Resampler<Scalar> r(rfilter, bcv, source->getHeight(), target->getHeight());
|
Resampler<Scalar> r(rfilter, bcv, source->getHeight(), target->getHeight());
|
||||||
|
|
||||||
|
@ -2370,7 +2371,6 @@ void Bitmap::filter(const ReconstructionFilter *rfilter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ref<Bitmap> Bitmap::resample(const ReconstructionFilter *rfilter,
|
ref<Bitmap> Bitmap::resample(const ReconstructionFilter *rfilter,
|
||||||
ReconstructionFilter::EBoundaryCondition bch,
|
ReconstructionFilter::EBoundaryCondition bch,
|
||||||
ReconstructionFilter::EBoundaryCondition bcv,
|
ReconstructionFilter::EBoundaryCondition bcv,
|
||||||
|
@ -2378,10 +2378,23 @@ ref<Bitmap> Bitmap::resample(const ReconstructionFilter *rfilter,
|
||||||
ref<Bitmap> result = new Bitmap(m_pixelFormat, m_componentFormat, size);
|
ref<Bitmap> result = new Bitmap(m_pixelFormat, m_componentFormat, size);
|
||||||
result->m_metadata = m_metadata;
|
result->m_metadata = m_metadata;
|
||||||
result->m_gamma = m_gamma;
|
result->m_gamma = m_gamma;
|
||||||
|
result->m_channelNames = m_channelNames;
|
||||||
resample(rfilter, bch, bcv, result, NULL, minValue, maxValue);
|
resample(rfilter, bch, bcv, result, NULL, minValue, maxValue);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref<Bitmap> Bitmap::filter(const ReconstructionFilter *rfilter,
|
||||||
|
ReconstructionFilter::EBoundaryCondition bch,
|
||||||
|
ReconstructionFilter::EBoundaryCondition bcv,
|
||||||
|
Float minValue, Float maxValue) const {
|
||||||
|
ref<Bitmap> 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 {
|
bool Bitmap::operator==(const Bitmap &bitmap) const {
|
||||||
return m_pixelFormat == bitmap.m_pixelFormat &&
|
return m_pixelFormat == bitmap.m_pixelFormat &&
|
||||||
m_componentFormat == bitmap.m_componentFormat &&
|
m_componentFormat == bitmap.m_componentFormat &&
|
||||||
|
|
|
@ -26,6 +26,11 @@
|
||||||
#pragma warning(disable : 4267) // 'return' : conversion from 'size_t' to 'long', possible loss of data
|
#pragma warning(disable : 4267) // 'return' : conversion from 'size_t' to 'long', possible loss of data
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define BP_RETURN_CONSTREF bp::return_value_policy<bp::copy_const_reference>()
|
||||||
|
#define BP_RETURN_NONCONSTREF bp::return_value_policy<bp::copy_non_const_reference>()
|
||||||
|
#define BP_RETURN_VALUE bp::return_value_policy<bp::return_by_value>()
|
||||||
|
#define BP_RETURN_INTREF bp::return_internal_reference<>()
|
||||||
|
|
||||||
#define BP_STRUCT_DECL(Name, Init) \
|
#define BP_STRUCT_DECL(Name, Init) \
|
||||||
bp::class_<Name> Name ##_struct(#Name, Init); \
|
bp::class_<Name> Name ##_struct(#Name, Init); \
|
||||||
bp::register_ptr_to_python<Name*>();
|
bp::register_ptr_to_python<Name*>();
|
||||||
|
@ -149,11 +154,6 @@
|
||||||
bp::detail::current_scope = value.ptr(); \
|
bp::detail::current_scope = value.ptr(); \
|
||||||
} while (0);
|
} while (0);
|
||||||
|
|
||||||
#define BP_RETURN_CONSTREF bp::return_value_policy<bp::copy_const_reference>()
|
|
||||||
#define BP_RETURN_NONCONSTREF bp::return_value_policy<bp::copy_non_const_reference>()
|
|
||||||
#define BP_RETURN_VALUE bp::return_value_policy<bp::return_by_value>()
|
|
||||||
#define BP_RETURN_INTREF bp::return_internal_reference<>()
|
|
||||||
|
|
||||||
namespace boost {
|
namespace boost {
|
||||||
namespace python {
|
namespace python {
|
||||||
template <typename T> T* get_pointer(mitsuba::ref<T> & p) {
|
template <typename T> T* get_pointer(mitsuba::ref<T> & p) {
|
||||||
|
|
|
@ -604,13 +604,13 @@ static ref<ConfigurableObject> pluginmgr_create_helper(PluginManager *manager, b
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ref<ConfigurableObject> pluginmgr_create(PluginManager *manager, bp::dict dict) {
|
static bp::object pluginmgr_create(PluginManager *manager, bp::dict dict) {
|
||||||
std::map<std::string, ConfigurableObject *> objs;
|
std::map<std::string, ConfigurableObject *> objs;
|
||||||
ref<ConfigurableObject> result = pluginmgr_create_helper(manager, dict, objs);
|
ref<ConfigurableObject> result = pluginmgr_create_helper(manager, dict, objs);
|
||||||
for (std::map<std::string, ConfigurableObject *>::iterator it = objs.begin();
|
for (std::map<std::string, ConfigurableObject *>::iterator it = objs.begin();
|
||||||
it != objs.end(); ++it)
|
it != objs.end(); ++it)
|
||||||
it->second->decRef();
|
it->second->decRef();
|
||||||
return result;
|
return cast(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bp::tuple mkCoordinateSystem(const Vector &n) {
|
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(fromXYZ_overloads, fromXYZ, 3, 4)
|
||||||
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(fromIPT_overloads, fromIPT, 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(reset_overloads, reset, 0, 1)
|
||||||
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(filter_overloads, filter, 4, 7)
|
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(resample1_overloads, resample, 4, 7)
|
||||||
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(resample_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) \
|
#define IMPLEMENT_ANIMATION_TRACK(Name) \
|
||||||
BP_CLASS(Name, AbstractAnimationTrack, (bp::init<AbstractAnimationTrack::EType, size_t>())) \
|
BP_CLASS(Name, AbstractAnimationTrack, (bp::init<AbstractAnimationTrack::EType, size_t>())) \
|
||||||
|
@ -1475,13 +1477,17 @@ void export_core() {
|
||||||
ReconstructionFilter::EBoundaryCondition, ReconstructionFilter::EBoundaryCondition,
|
ReconstructionFilter::EBoundaryCondition, ReconstructionFilter::EBoundaryCondition,
|
||||||
Bitmap *, Bitmap *, Float, Float) const = &Bitmap::resample;
|
Bitmap *, Bitmap *, Float, Float) const = &Bitmap::resample;
|
||||||
|
|
||||||
|
ref<Bitmap> (Bitmap::*resample_2)(const ReconstructionFilter *,
|
||||||
|
ReconstructionFilter::EBoundaryCondition, ReconstructionFilter::EBoundaryCondition,
|
||||||
|
const Vector2i &, Float, Float) const = &Bitmap::resample;
|
||||||
|
|
||||||
void (Bitmap::*filter_1)(const ReconstructionFilter *,
|
void (Bitmap::*filter_1)(const ReconstructionFilter *,
|
||||||
ReconstructionFilter::EBoundaryCondition, ReconstructionFilter::EBoundaryCondition,
|
ReconstructionFilter::EBoundaryCondition, ReconstructionFilter::EBoundaryCondition,
|
||||||
Bitmap *, Bitmap *, Float, Float) const = &Bitmap::filter;
|
Bitmap *, Bitmap *, Float, Float) const = &Bitmap::filter;
|
||||||
|
|
||||||
ref<Bitmap> (Bitmap::*resample_2)(const ReconstructionFilter *,
|
ref<Bitmap> (Bitmap::*filter_2)(const ReconstructionFilter *,
|
||||||
ReconstructionFilter::EBoundaryCondition, ReconstructionFilter::EBoundaryCondition,
|
ReconstructionFilter::EBoundaryCondition, ReconstructionFilter::EBoundaryCondition,
|
||||||
const Vector2i &, Float, Float) const = &Bitmap::resample;
|
Float, Float) const = &Bitmap::filter;
|
||||||
|
|
||||||
const std::vector<std::string> & (Bitmap::*getChannelNames_1)() const = &Bitmap::getChannelNames;
|
const std::vector<std::string> & (Bitmap::*getChannelNames_1)() const = &Bitmap::getChannelNames;
|
||||||
|
|
||||||
|
@ -1541,9 +1547,10 @@ void export_core() {
|
||||||
.def("copyFrom", copyFrom_3)
|
.def("copyFrom", copyFrom_3)
|
||||||
.def("convolve", &Bitmap::convolve)
|
.def("convolve", &Bitmap::convolve)
|
||||||
.def("arithmeticOperation", &Bitmap::arithmeticOperation, BP_RETURN_VALUE)
|
.def("arithmeticOperation", &Bitmap::arithmeticOperation, BP_RETURN_VALUE)
|
||||||
.def("filter", filter_1, filter_overloads())
|
.def("resample", resample_1, resample1_overloads())
|
||||||
.def("resample", resample_1, resample_overloads())
|
.def("resample", resample_2, resample2_overloads()[BP_RETURN_VALUE])
|
||||||
.def("resample", resample_2, BP_RETURN_VALUE)
|
.def("filter", filter_1, filter1_overloads())
|
||||||
|
.def("filter", filter_2, filter2_overloads()[BP_RETURN_VALUE])
|
||||||
.def("setGamma", &Bitmap::setGamma)
|
.def("setGamma", &Bitmap::setGamma)
|
||||||
.def("getGamma", &Bitmap::getGamma)
|
.def("getGamma", &Bitmap::getGamma)
|
||||||
.def("setMetadataString", &Bitmap::setMetadataString)
|
.def("setMetadataString", &Bitmap::setMetadataString)
|
||||||
|
|
|
@ -48,7 +48,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string toString() const {
|
std::string toString() const {
|
||||||
return "BoxFilter[]";
|
return formatString("GaussianFilter[radius=%f]", m_radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
MTS_DECLARE_CLASS()
|
MTS_DECLARE_CLASS()
|
||||||
|
|
Loading…
Reference in New Issue