Mitsuba Renderer  0.5.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
rfilter.h
Go to the documentation of this file.
1 /*
2  This file is part of Mitsuba, a physically based rendering system.
3 
4  Copyright (c) 2007-2014 by Wenzel Jakob and others.
5 
6  Mitsuba is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License Version 3
8  as published by the Free Software Foundation.
9 
10  Mitsuba is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #pragma once
20 #if !defined(__MITSUBA_CORE_RFILTER_H_)
21 #define __MITSUBA_CORE_RFILTER_H_
22 
23 #include <mitsuba/core/cobject.h>
24 
26 
27 /// Reconstruction filters will be tabulated at this resolution
28 #define MTS_FILTER_RESOLUTION 31
29 
30 /**
31  * \brief Generic interface to separable image reconstruction filters
32  *
33  * When resampling bitmaps or adding radiance-valued samples to a rendering in
34  * progress, Mitsuba first convolves them with a so-called image reconstruction
35  * filter. Various kinds are implemented as subclasses of this interface.
36  *
37  * Because image filters are generally too expensive to evaluate for
38  * each sample, the implementation of this class internally precomputes
39  * an discrete representation (resolution given by \ref MTS_FILTER_RESOLUTION)
40  *
41  * \ingroup librender
42  * \ingroup libpython
43  */
45 public:
46  /**
47  * \brief When resampling data to a different resolution using
48  * \ref Resampler::resample(), this enumeration specifies how lookups
49  * <em>outside</em> of the input domain are handled.
50  *
51  * \see Resampler
52  */
54  /// Clamp to the outermost sample position
55  EClamp = 0,
56  /// Assume that the input repeats in a periodic fashion
58  /// Assume that the input is mirrored along the boundary
60  /// Assume that the input function is zero outside of the defined domain
62  /// Assume that the input function is equal to one outside of the defined domain
63  EOne
64  };
65 
66  /// Return the filter's width
67  inline Float getRadius() const { return m_radius; }
68 
69  /// Return the block border size required when rendering with this filter
70  inline int getBorderSize() const { return m_borderSize; }
71 
72  /// Evaluate the filter function
73  virtual Float eval(Float x) const = 0;
74 
75  /// Perform a lookup into the discretized version
76  inline Float evalDiscretized(Float x) const { return m_values[
77  std::min((int) std::abs(x * m_scaleFactor), MTS_FILTER_RESOLUTION)]; }
78 
79  /// Serialize the filter to a binary data stream
80  void serialize(Stream *stream, InstanceManager *manager) const;
81 
82  /** \brief Configure the object (called \a once after construction) */
83  void configure();
84 
86 protected:
87  /// Create a new reconstruction filter
88  ReconstructionFilter(const Properties &props);
89 
90  /// Unserialize a filter
91  ReconstructionFilter(Stream *stream, InstanceManager *manager);
92 
93  /// Virtual destructor
94  virtual ~ReconstructionFilter();
95 protected:
96  Float m_radius, m_scaleFactor;
98  int m_borderSize;
99 };
100 
101 /**
102  * \brief Utility class for efficiently resampling discrete datasets to different resolutions
103  * \tparam Scalar
104  * Denotes the underlying floating point data type (i.e. <tt>half</tt>, <tt>float</tt>,
105  * or <tt>double</tt>)
106  */
107 template <typename Scalar> struct Resampler {
108  /**
109  * \brief Create a new Resampler object that transforms between the specified resolutions
110  *
111  * This constructor precomputes all information needed to efficiently perform the
112  * desired resampling operation. For that reason, it is most efficient if it can
113  * be used over and over again (e.g. to resample the equal-sized rows of a bitmap)
114  *
115  * \param sourceRes
116  * Source resolution
117  * \param targetRes
118  * Desired target resolution
119  * \param bc
120  * Boundary conditions that should be observed when looking up samples
121  * outside of the defined input domain.
122  */
123  Resampler(const ReconstructionFilter *rfilter, ReconstructionFilter::EBoundaryCondition bc,
124  int sourceRes, int targetRes) : m_bc(bc), m_sourceRes(sourceRes), m_targetRes(targetRes),
125  m_start(NULL), m_weights(NULL) {
126  SAssert(sourceRes > 0 && targetRes > 0);
127  Float filterRadius = rfilter->getRadius(), scale = (Float)1.0, invScale = (Float)1.0;
128 
129  /* Low-pass filter: scale reconstruction filters when downsampling */
130  if (targetRes < sourceRes) {
131  scale = (Float) sourceRes / (Float) targetRes;
132  invScale = 1 / scale;
133  filterRadius *= scale;
134  }
135 
136  m_taps = math::ceilToInt(filterRadius * 2);
137  if (sourceRes == targetRes && (m_taps % 2) != 1)
138  --m_taps;
139  m_halfTaps = m_taps / 2;
140 
141  if (sourceRes != targetRes) { /* Resampling mode */
142  m_start = new int[targetRes];
143  m_weights = new Scalar[m_taps * targetRes];
144  m_fastStart = 0;
145  m_fastEnd = m_targetRes;
146 
147  for (int i=0; i<targetRes; i++) {
148  /* Compute the fractional coordinates of the new sample i in the original coordinates */
149  Float center = (i + (Float) 0.5f) / targetRes * sourceRes;
150 
151  /* Determine the index of the first original sample that might contribute */
152  m_start[i] = math::floorToInt(center - filterRadius + (Float) 0.5f);
153 
154  /* Determine the size of center region, on which to run fast non condition-aware code */
155  if (m_start[i] < 0)
156  m_fastStart = std::max(m_fastStart, i + 1);
157  else if (m_start[i] + m_taps - 1 >= m_sourceRes)
158  m_fastEnd = std::min(m_fastEnd, i - 1);
159 
160  Float sum = 0;
161  for (int j=0; j<m_taps; j++) {
162  /* Compute the the position where the filter should be evaluated */
163  Float pos = m_start[i] + j + (Float) 0.5f - center;
164 
165  /* Perform the evaluation and record the weight */
166  Float weight = rfilter->eval(pos * invScale);
167  m_weights[i * m_taps + j] = (Scalar) weight;
168  sum += weight;
169  }
170 
171  /* Normalize the contribution of each sample */
172  Float normalization = 1.0f / sum;
173  for (int j=0; j<m_taps; j++) {
174  Scalar &value = m_weights[i * m_taps + j];
175  value = (Scalar) ((Float) value * normalization);
176  }
177  }
178  } else { /* Filtering mode */
179  m_weights = new Scalar[m_taps];
180  Float sum = 0;
181  for (int i=0; i<m_taps; i++) {
182  Scalar weight = (Scalar) rfilter->eval((Float) (i-m_halfTaps));
183  m_weights[i] = weight;
184  sum += (Float) weight;
185  }
186  Float normalization = 1.0f / sum;
187  for (int i=0; i<m_taps; i++) {
188  Scalar &value = m_weights[i];
189  value = (Scalar) ((Float) value * normalization);
190  }
191  m_fastStart = std::min(m_halfTaps, m_targetRes-1);
192  m_fastEnd = std::max(m_targetRes-m_halfTaps-1, 0);
193  }
194 
195  /* Don't have overlapping fast start/end intervals when
196  the target image is very small compared to the source image */
197  m_fastStart = std::min(m_fastStart, m_fastEnd);
198  }
199 
200  /// Release all memory
202  if (m_start)
203  delete[] m_start;
204  if (m_weights)
205  delete[] m_weights;
206  }
207 
208  /**
209  * \brief Resample a multi-channel array and clamp the results
210  * to a specified valid range
211  *
212  * This function is preferred if too large positive/negative values
213  * due to ringing are unacceptable.
214  *
215  * \param source
216  * Source array of samples
217  * \param target
218  * Target array of samples
219  * \param sourceStride
220  * Stride of samples in the source array. A value
221  * of '1' implies that they are densely packed.
222  * \param targetStride
223  * Stride of samples in the source array. A value
224  * of '1' implies that they are densely packed.
225  * \param channels
226  * Number of channels to be resampled
227  * \param min
228  * Minumum sample value after resampling
229  * \param max
230  * Maximum sample value after resampling
231  */
232  void resampleAndClamp(const Scalar *source, size_t sourceStride,
233  Scalar *target, size_t targetStride, int channels,
234  Scalar min = (Scalar) 0, Scalar max = (Scalar) 1) {
235  const int taps = m_taps, halfTaps = m_halfTaps;
236  targetStride = channels * (targetStride - 1);
237  sourceStride *= channels;
238 
239  if (m_start) {
240  /* Resample the left border region, while accounting for the boundary conditions */
241  for (int i=0; i<m_fastStart; ++i) {
242  int start = m_start[i];
243 
244  for (int ch=0; ch<channels; ++ch) {
245  Scalar result = 0;
246  for (int j=0; j<taps; ++j)
247  result += lookup(source, start + j, sourceStride, ch) * m_weights[i * taps + j];
248  *target++ = std::min(max, std::max(min, result));
249  }
250 
251  target += targetStride;
252  }
253 
254  /* Use a faster, vectorizable loop for resampling the main portion */
255  for (int i=m_fastStart; i<m_fastEnd; ++i) {
256  int start = m_start[i];
257 
258  for (int ch=0; ch<channels; ++ch) {
259  Scalar result = 0;
260  for (int j=0; j<taps; ++j)
261  result += source[sourceStride * (start + j) + ch] * m_weights[i * taps + j];
262  *target++ = std::min(max, std::max(min, result));
263  }
264 
265  target += targetStride;
266  }
267 
268  /* Resample the right border region, while accounting for the boundary conditions */
269  for (int i=m_fastEnd; i<m_targetRes; ++i) {
270  int start = m_start[i];
271 
272  for (int ch=0; ch<channels; ++ch) {
273  Scalar result = 0;
274  for (int j=0; j<taps; ++j)
275  result += lookup(source, start + j, sourceStride, ch) * m_weights[i * taps + j];
276  *target++ = std::min(max, std::max(min, result));
277  }
278 
279  target += targetStride;
280  }
281  } else {
282  /* Filter the left border region, while accounting for the boundary conditions */
283  for (int i=0; i<m_fastStart; ++i) {
284  int start = i - halfTaps;
285 
286  for (int ch=0; ch<channels; ++ch) {
287  Scalar result = 0;
288  for (int j=0; j<taps; ++j)
289  result += lookup(source, start + j, sourceStride, ch) * m_weights[j];
290  *target++ = std::min(max, std::max(min, result));
291  }
292 
293  target += targetStride;
294  }
295 
296  /* Use a faster, vectorizable loop for filtering the main portion */
297  for (int i=m_fastStart; i<m_fastEnd; ++i) {
298  int start = i - halfTaps;
299 
300  for (int ch=0; ch<channels; ++ch) {
301  Scalar result = 0;
302  for (int j=0; j<taps; ++j)
303  result += source[sourceStride * (start + j) + ch] * m_weights[j];
304  *target++ = std::min(max, std::max(min, result));
305  }
306 
307  target += targetStride;
308  }
309 
310  /* Filter the right border region, while accounting for the boundary conditions */
311  for (int i=m_fastEnd; i<m_targetRes; ++i) {
312  int start = i - halfTaps;
313 
314  for (int ch=0; ch<channels; ++ch) {
315  Scalar result = 0;
316  for (int j=0; j<taps; ++j)
317  result += lookup(source, start + j, sourceStride, ch) * m_weights[j];
318  *target++ = std::min(max, std::max(min, result));
319  }
320 
321  target += targetStride;
322  }
323  }
324  }
325 
326  /**
327  * \brief Resample a multi-channel array
328  *
329  * \param source
330  * Source array of samples
331  * \param target
332  * Target array of samples
333  * \param sourceStride
334  * Stride of samples in the source array. A value
335  * of '1' implies that they are densely packed.
336  * \param targetStride
337  * Stride of samples in the source array. A value
338  * of '1' implies that they are densely packed.
339  * \param channels
340  * Number of channels to be resampled
341  */
342  void resample(const Scalar *source, size_t sourceStride,
343  Scalar *target, size_t targetStride, int channels) {
344  const int taps = m_taps, halfTaps = m_halfTaps;
345 
346  targetStride = channels * (targetStride - 1);
347  sourceStride *= channels;
348 
349  if (m_start) {
350  /* Resample the left border region, while accounting for the boundary conditions */
351  for (int i=0; i<m_fastStart; ++i) {
352  int start = m_start[i];
353 
354  for (int ch=0; ch<channels; ++ch) {
355  Scalar result = 0;
356  for (int j=0; j<taps; ++j)
357  result += lookup(source, start + j, sourceStride, ch) * m_weights[i * taps + j];
358  *target++ = result;
359  }
360 
361  target += targetStride;
362  }
363 
364  /* Use a faster, vectorizable loop for resampling the main portion */
365  for (int i=m_fastStart; i<m_fastEnd; ++i) {
366  int start = m_start[i];
367 
368  for (int ch=0; ch<channels; ++ch) {
369  Scalar result = 0;
370  for (int j=0; j<taps; ++j)
371  result += source[sourceStride * (start + j) + ch] * m_weights[i * taps + j];
372  *target++ = result;
373  }
374 
375  target += targetStride;
376  }
377 
378  /* Resample the right border region, while accounting for the boundary conditions */
379  for (int i=m_fastEnd; i<m_targetRes; ++i) {
380  int start = m_start[i];
381 
382  for (int ch=0; ch<channels; ++ch) {
383  Scalar result = 0;
384  for (int j=0; j<taps; ++j)
385  result += lookup(source, start + j, sourceStride, ch) * m_weights[i * taps + j];
386  *target++ = result;
387  }
388 
389  target += targetStride;
390  }
391  } else {
392  /* Filter the left border region, while accounting for the boundary conditions */
393  for (int i=0; i<m_fastStart; ++i) {
394  int start = i - halfTaps;
395 
396  for (int ch=0; ch<channels; ++ch) {
397  Scalar result = 0;
398  for (int j=0; j<taps; ++j)
399  result += lookup(source, start + j, sourceStride, ch) * m_weights[j];
400  *target++ = result;
401  }
402 
403  target += targetStride;
404  }
405 
406  /* Use a faster, vectorizable loop for filtering the main portion */
407  for (int i=m_fastStart; i<m_fastEnd; ++i) {
408  int start = i - halfTaps;
409 
410  for (int ch=0; ch<channels; ++ch) {
411  Scalar result = 0;
412  for (int j=0; j<taps; ++j)
413  result += source[sourceStride * (start + j) + ch] * m_weights[j];
414  *target++ = result;
415  }
416 
417  target += targetStride;
418  }
419 
420  /* Filter the right border region, while accounting for the boundary conditions */
421  for (int i=m_fastEnd; i<m_targetRes; ++i) {
422  int start = i - halfTaps;
423 
424  for (int ch=0; ch<channels; ++ch) {
425  Scalar result = 0;
426  for (int j=0; j<taps; ++j)
427  result += lookup(source, start + j, sourceStride, ch) * m_weights[j];
428  *target++ = result;
429  }
430 
431  target += targetStride;
432  }
433  }
434  }
435 
436 private:
437  FINLINE Scalar lookup(const Scalar *source, int pos, size_t stride, int offset) const {
438  if (EXPECT_NOT_TAKEN(pos < 0 || pos >= m_sourceRes)) {
439  switch (m_bc) {
440  case ReconstructionFilter::EClamp:
441  pos = math::clamp(pos, 0, m_sourceRes - 1);
442  break;
443  case ReconstructionFilter::ERepeat:
444  pos = math::modulo(pos, m_sourceRes);
445  break;
446  case ReconstructionFilter::EMirror:
447  pos = math::modulo(pos, 2*m_sourceRes);
448  if (pos >= m_sourceRes)
449  pos = 2*m_sourceRes - pos - 1;
450  break;
451  case ReconstructionFilter::EZero:
452  return (Scalar) 0;
453  case ReconstructionFilter::EOne:
454  return (Scalar) 1;
455  }
456  }
457  return source[stride * pos + offset];
458  }
459 
460 private:
461  ReconstructionFilter::EBoundaryCondition m_bc;
462  int m_sourceRes;
463  int m_targetRes;
464  int *m_start;
465  Scalar *m_weights;
466  int m_fastStart, m_fastEnd;
467  int m_taps, m_halfTaps;
468 };
469 
470 extern MTS_EXPORT_CORE std::ostream &operator<<(std::ostream &os, const ReconstructionFilter::EBoundaryCondition &value);
471 
473 
474 #endif /* __MITSUBA_CORE_RFILTER_H_ */
EBoundaryCondition
When resampling data to a different resolution using Resampler::resample(), this enumeration specifie...
Definition: rfilter.h:53
#define MTS_FILTER_RESOLUTION
Reconstruction filters will be tabulated at this resolution.
Definition: rfilter.h:28
Assume that the input repeats in a periodic fashion.
Definition: rfilter.h:57
Generic serializable object, which supports construction from a Properties instance.
Definition: cobject.h:40
Resampler(const ReconstructionFilter *rfilter, ReconstructionFilter::EBoundaryCondition bc, int sourceRes, int targetRes)
Create a new Resampler object that transforms between the specified resolutions.
Definition: rfilter.h:123
int floorToInt(Scalar value)
Integer floor function (single precision)
Definition: math.h:100
std::ostream & operator<<(std::ostream &os, const ReconstructionFilter::EBoundaryCondition &value)
#define MTS_EXPORT_CORE
Definition: getopt.h:29
#define MTS_NAMESPACE_BEGIN
Definition: platform.h:137
Generic interface to separable image reconstruction filters.
Definition: rfilter.h:44
virtual void serialize(Stream *stream, InstanceManager *manager) const
Serialize this object to a binary data stream.
int32_t modulo(int32_t a, int32_t b)
Always-positive modulo function (assumes b &gt; 0)
Definition: math.h:67
Float evalDiscretized(Float x) const
Perform a lookup into the discretized version.
Definition: rfilter.h:76
virtual Float eval(Float x) const =0
Evaluate the filter function.
#define SAssert(cond)
``Static&#39;&#39; assertion (to be used outside of classes that derive from Object)
Definition: logger.h:79
Abstract seekable stream class.
Definition: stream.h:58
#define MTS_DECLARE_CLASS()
This macro must be used in the initial definition in classes that derive from Object.
Definition: class.h:158
virtual void configure()
Configure the object (called once after construction and addition of all child ConfigurableObject ins...
void resampleAndClamp(const Scalar *source, size_t sourceStride, Scalar *target, size_t targetStride, int channels, Scalar min=(Scalar) 0, Scalar max=(Scalar) 1)
Resample a multi-channel array and clamp the results to a specified valid range.
Definition: rfilter.h:232
int ceilToInt(Scalar value)
Integer ceil function (single precision)
Definition: math.h:103
Associative parameter map for constructing subclasses of ConfigurableObject.
Definition: properties.h:46
Coordinates the serialization and unserialization of object graphs.
Definition: serialization.h:65
~Resampler()
Release all memory.
Definition: rfilter.h:201
void resample(const Scalar *source, size_t sourceStride, Scalar *target, size_t targetStride, int channels)
Resample a multi-channel array.
Definition: rfilter.h:342
Assume that the input is mirrored along the boundary.
Definition: rfilter.h:59
#define MTS_NAMESPACE_END
Definition: platform.h:138
Utility class for efficiently resampling discrete datasets to different resolutions.
Definition: rfilter.h:107
Float getRadius() const
Return the filter&#39;s width.
Definition: rfilter.h:67
Assume that the input function is zero outside of the defined domain.
Definition: rfilter.h:61
int getBorderSize() const
Return the block border size required when rendering with this filter.
Definition: rfilter.h:70
Scalar clamp(Scalar value, Scalar min, Scalar max)
Generic clamping function.
Definition: math.h:51