Field3D
MIPUtil.h
Go to the documentation of this file.
1 //----------------------------------------------------------------------------//
2 
3 /*
4  * Copyright (c) 2013 Sony Pictures Imageworks Inc
5  *
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * Redistributions in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in the
16  * documentation and/or other materials provided with the
17  * distribution. Neither the name of Sony Pictures Imageworks nor the
18  * names of its contributors may be used to endorse or promote
19  * products derived from this software without specific prior written
20  * permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33  * OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 //----------------------------------------------------------------------------//
37 
42 //----------------------------------------------------------------------------//
43 
44 #ifndef _INCLUDED_Field3D_MIPUtil_H_
45 #define _INCLUDED_Field3D_MIPUtil_H_
46 
47 //----------------------------------------------------------------------------//
48 
49 #include <vector>
50 
51 #include <boost/thread/thread.hpp>
52 #include <boost/thread/mutex.hpp>
53 
54 #include "Resample.h"
55 #include "SparseField.h"
56 #include "Types.h"
57 
58 //----------------------------------------------------------------------------//
59 
60 #include "ns.h"
61 
63 
64 //----------------------------------------------------------------------------//
65 // Functions
66 //----------------------------------------------------------------------------//
67 
69 template <typename MIPField_T, typename Filter_T>
70 typename MIPField_T::Ptr
71 makeMIP(const typename MIPField_T::NestedType &base, const int minSize,
72  const size_t numThreads);
73 
74 //----------------------------------------------------------------------------//
75 // Implementation details
76 //----------------------------------------------------------------------------//
77 
78 namespace detail {
79 
80  //--------------------------------------------------------------------------//
81 
83  template <typename T>
85  {
86  typedef T type;
87  };
88 
90  template <>
91  struct ComputationType<Field3D::half>
92  {
93  typedef float type;
94  };
95 
96  //--------------------------------------------------------------------------//
97 
98  FIELD3D_API V3i mipResolution(const V3i &baseRes, const size_t level);
99 
100  //--------------------------------------------------------------------------//
101 
103  template <typename Data_T>
104  size_t threadingBlockSize(const DenseField<Data_T> & /* f */)
105  {
106  return 16;
107  }
108 
110  template <typename Data_T>
112  {
113  return f.blockSize();
114  }
115 
116  //--------------------------------------------------------------------------//
117 
118  template <typename Data_T>
120  const SparseField<Data_T> &/*tgt*/,
121  const Box3i &tgtBox, const float support,
122  const size_t dim)
123  {
124  const int intSupport = static_cast<int>(std::ceil(support * 0.5));
125  const int pad = std::max(0, intSupport);
126  Box3i tgtBoxPad = tgtBox;
127  tgtBoxPad.min[dim] -= pad;
128  tgtBoxPad.max[dim] += pad;
129  Box3i srcBoxPad = tgtBoxPad;
130  srcBoxPad.min[dim] *= 2;
131  srcBoxPad.max[dim] *= 2;
132 
133  // Get the block coordinates
134  const Box3i dbsBounds = blockCoords(clipBounds(srcBoxPad, src.dataWindow()),
135  &src);
136 
137  static boost::mutex mutex;
138  boost::mutex::scoped_lock lock(mutex);
139 
140  // Check all blocks
141  for (int k = dbsBounds.min.z; k <= dbsBounds.max.z; ++k) {
142  for (int j = dbsBounds.min.y; j <= dbsBounds.max.y; ++j) {
143  for (int i = dbsBounds.min.x; i <= dbsBounds.max.x; ++i) {
144  if (src.blockIsAllocated(i, j, k) ||
145  src.getBlockEmptyValue(i, j, k) != static_cast<Data_T>(0)) {
146  return false;
147  }
148  }
149  }
150  }
151 
152  // No hits. Empty
153  return true;
154  }
155 
156  //--------------------------------------------------------------------------//
157 
159  template <typename Field_T>
160  bool checkInputEmpty(const Field_T &/*src*/, const Field_T &/*tgt*/,
161  const Box3i &/*tgtBox*/, const float /*support*/,
162  const size_t /*dim*/)
163  {
164  return false;
165  }
166 
167  //--------------------------------------------------------------------------//
168 
169  template <typename Field_T, typename FilterOp_T>
171  {
172  MIPSeparableThreadOp(const Field_T &src, Field_T &tgt,
173  const size_t level, const FilterOp_T &filterOp,
174  const size_t dim,
175  const std::vector<Box3i> &blocks,
176  size_t &nextIdx, boost::mutex &mutex)
177  : m_src(src),
178  m_tgt(tgt),
179  m_filterOp(filterOp),
180  m_level(level),
181  m_dim(dim),
182  m_blocks(blocks),
183  m_nextIdx(nextIdx),
184  m_mutex(mutex),
185  m_numBlocks(blocks.size())
186  {
187  // Empty
188  }
189 
190  void operator() ()
191  {
192  using namespace std;
193 
194  // Defer to ComputationType to determine the processing data type
195  typedef typename Field_T::value_type Data_T;
196  typedef typename ComputationType<Data_T>::type Value_T;
197 
198  // To ensure we don't sample outside source data
199  Box3i srcDw = m_src.dataWindow();
200 
201  // Coordinate frame conversion constants
202  const float tgtToSrcMult = 2.0;
203  const float filterCoordMult = 1.0f / (tgtToSrcMult);
204 
205  // Filter info
206  const float support = m_filterOp.support();
207 
208  // Get next index to process
209  size_t idx;
210  {
211  boost::mutex::scoped_lock lock(m_mutex);
212  idx = m_nextIdx;
213  m_nextIdx++;
214  }
215  // Keep going while there is data to process
216  while (idx < m_numBlocks) {
217  // Grab the bounds
218  const Box3i box = m_blocks[idx];
219  // Early exit if input blocks are all empty
220  if (!detail::checkInputEmpty(m_src, m_tgt, box, support, m_dim)) {
221  // For each output voxel
222  for (int k = box.min.z; k <= box.max.z; ++k) {
223  for (int j = box.min.y; j <= box.max.y; ++j) {
224  for (int i = box.min.x; i <= box.max.x; ++i) {
225  Value_T accumValue = static_cast<Value_T>(0.0);
226  float accumWeight = 0.0f;
227  // Transform from current point in target frame to source frame
228  const int curTgt = V3i(i, j, k)[m_dim];
229  const float curSrc = discToCont(curTgt) * tgtToSrcMult;
230  // Find interval
231  int startSrc =
232  static_cast<int>(std::floor(curSrc - support * tgtToSrcMult));
233  int endSrc =
234  static_cast<int>(std::ceil(curSrc + support *
235  tgtToSrcMult)) - 1;
236  startSrc = std::max(startSrc, srcDw.min[m_dim]);
237  endSrc = std::min(endSrc, srcDw.max[m_dim]);
238  // Loop over source voxels
239  for (int s = startSrc; s <= endSrc; ++s) {
240  // Source index
241  const int xIdx = m_dim == 0 ? s : i;
242  const int yIdx = m_dim == 1 ? s : j;
243  const int zIdx = m_dim == 2 ? s : k;
244  // Source voxel in continuous coords
245  const float srcP = discToCont(s);
246  // Compute filter weight in source space (twice as wide)
247  const float weight = m_filterOp.eval(std::abs(srcP - curSrc) *
248  filterCoordMult);
249  // Value
250  const Value_T value = m_src.fastValue(xIdx, yIdx, zIdx);
251  // Update
252  accumWeight += weight;
253  accumValue += value * weight;
254  }
255  // Update final value
256  if (accumWeight > 0.0f &&
257  accumValue != static_cast<Value_T>(0.0)) {
258  m_tgt.fastLValue(i, j, k) = accumValue / accumWeight;
259  }
260  }
261  }
262  }
263  } // Empty input
264  // Get next index
265  {
266  boost::mutex::scoped_lock lock(m_mutex);
267  idx = m_nextIdx;
268  m_nextIdx++;
269  }
270  }
271  }
272 
273  private:
274 
275  // Data members ---
276 
277  const Field_T &m_src;
278  Field_T &m_tgt;
279  const FilterOp_T &m_filterOp;
280  const size_t m_level;
281  const size_t m_dim;
282  const std::vector<Box3i> &m_blocks;
283  size_t &m_nextIdx;
284  boost::mutex &m_mutex;
285  const size_t m_numBlocks;
286 
287  };
288 
289  //--------------------------------------------------------------------------//
290 
292  template <typename Field_T, typename FilterOp_T>
293  void mipSeparable(const Field_T &src, Field_T &tgt,
294  const V3i &oldRes, const V3i &newRes, const size_t level,
295  const FilterOp_T &filterOp, const size_t dim,
296  const size_t numThreads)
297  {
298  using namespace std;
299 
300  // Compute new res
301  V3i res;
302  if (dim == 2) {
303  res = newRes;
304  } else if (dim == 1) {
305  res = V3i(newRes.x, newRes.y, oldRes.z);
306  } else {
307  res = V3i(newRes.x, oldRes.y, oldRes.z);
308  }
309 
310  // Resize new field
311  tgt.setSize(res);
312 
313  // Determine granularity
314  const size_t blockSize = threadingBlockSize(src);
315 
316  // Build block list
317  std::vector<Box3i> blocks;
318  for (int k = 0; k < res.z; k += blockSize) {
319  for (int j = 0; j < res.y; j += blockSize) {
320  for (int i = 0; i < res.x; i += blockSize) {
321  Box3i box;
322  // Initialize block size
323  box.min = V3i(i, j, k);
324  box.max = box.min + V3i(blockSize - 1);
325  // Clip against resolution
326  box.max.x = std::min(box.max.x, res.x - 1);
327  box.max.y = std::min(box.max.y, res.y - 1);
328  box.max.z = std::min(box.max.z, res.z - 1);
329  // Add to list
330  blocks.push_back(box);
331  }
332  }
333  }
334 
335  // Next index counter and mutex
336  size_t nextIdx = 0;
337  boost::mutex mutex;
338 
339  // Launch threads ---
340 
341  boost::thread_group threads;
342 
343  for (size_t i = 0; i < numThreads; ++i) {
344  threads.create_thread(
345  MIPSeparableThreadOp<Field_T, FilterOp_T>(src, tgt, level, filterOp,
346  dim, blocks, nextIdx, mutex));
347  }
348 
349  // Join
350  threads.join_all();
351  }
352 
353  //--------------------------------------------------------------------------//
354 
355  template <typename Field_T, typename FilterOp_T>
356  void mipResample(const Field_T &base, const Field_T &src, Field_T &tgt,
357  const size_t level, const FilterOp_T &filterOp,
358  const size_t numThreads)
359  {
360  using std::ceil;
361 
362  // Compute new res
363  const Box3i baseDw = base.dataWindow();
364  const V3i baseRes = baseDw.size() + V3i(1);
365  const V3i newRes = mipResolution(baseRes, level);
366 
367  // Source res
368  const Box3i srcDw = src.dataWindow();
369  const V3i srcRes = srcDw.size() + V3i(1);
370 
371  // Temporary field for y component
372  Field_T tmp;
373 
374  // X axis (src into tgt)
375  mipSeparable(src, tgt, srcRes, newRes, level, filterOp, 0, numThreads);
376  // Y axis (tgt into temp)
377  mipSeparable(tgt, tmp, srcRes, newRes, level, filterOp, 1, numThreads);
378  // Z axis (temp into tgt)
379  mipSeparable(tmp, tgt, srcRes, newRes, level, filterOp, 2, numThreads);
380 
381  // Update final target with mapping and metadata
382  tgt.name = base.name;
383  tgt.attribute = base.attribute;
384  tgt.setMapping(base.mapping());
385  tgt.copyMetadata(base);
386  }
387 
388  //--------------------------------------------------------------------------//
389 
392  const V3i &baseRes,
393  const Box3i &extents,
394  const size_t level);
395 
396  //--------------------------------------------------------------------------//
397 
398 } // namespace detail
399 
400 //----------------------------------------------------------------------------//
401 // Function implementations
402 //----------------------------------------------------------------------------//
403 
404 template <typename MIPField_T, typename Filter_T>
405 typename MIPField_T::Ptr
406 makeMIP(const typename MIPField_T::NestedType &base, const int minSize,
407  const size_t numThreads)
408 {
409  using namespace Field3D::detail;
410 
411  typedef typename MIPField_T::value_type Data_T;
412  typedef typename MIPField_T::NestedType Src_T;
413  typedef typename Src_T::Ptr SrcPtr;
414  typedef typename MIPField_T::Ptr MIPPtr;
415  typedef std::vector<typename Src_T::Ptr> SrcVec;
416 
417  if (base.extents() != base.dataWindow()) {
418  return MIPPtr();
419  }
420 
421  // Initialize output vector with base resolution
422  SrcVec result;
423  result.push_back(field_dynamic_cast<Src_T>(base.clone()));
424 
425  // Iteration variables
426  V3i res = base.extents().size() + V3i(1);
427 
428  // Loop until minimum size is found
429  size_t level = 1;
430  while ((res.x > minSize || res.y > minSize || res.z > minSize) &&
431  (res.x > 1 && res.y > 1 && res.z > 1)) {
432  // Perform filtering
433  SrcPtr nextField(new Src_T);
434  mipResample(base, *result.back(), *nextField, level,
435  Filter_T(), numThreads);
436  // Add to vector of filtered fields
437  result.push_back(nextField);
438  // Set up for next iteration
439  res = nextField->dataWindow().size() + V3i(1);
440  level++;
441  }
442 
443  MIPPtr mipField(new MIPField_T);
444  mipField->setup(result);
445  mipField->name = base.name;
446  mipField->attribute = base.attribute;
447  mipField->copyMetadata(base);
448 
449  return mipField;
450 }
451 
452 //----------------------------------------------------------------------------//
453 
455 
456 //----------------------------------------------------------------------------//
457 
458 #endif // Include guard
bool checkInputEmpty(const SparseField< Data_T > &src, const SparseField< Data_T > &, const Box3i &tgtBox, const float support, const size_t dim)
Definition: MIPUtil.h:119
This subclass of Field stores data in a contiguous std::vector.
Definition: DenseField.h:85
#define FIELD3D_NAMESPACE_HEADER_CLOSE
Definition: ns.h:58
Box3i clipBounds(const Box3i &bbox, const Box3i &bounds)
Definition: Field.h:1144
Imath::Box3i Box3i
Definition: SpiMathLib.h:77
Box3i blockCoords(const Box3i &dvsBounds, const SparseField< Data_T > *f)
Definition: SparseField.h:676
void mipSeparable(const Field_T &src, Field_T &tgt, const V3i &oldRes, const V3i &newRes, const size_t level, const FilterOp_T &filterOp, const size_t dim, const size_t numThreads)
Threaded implementation of separable MIP filtering.
Definition: MIPUtil.h:293
Contains typedefs for the commonly used types in Field3D.
const Data_T getBlockEmptyValue(int bi, int bj, int bk) const
Returns the constant value of an block, whether it&#39;s allocated already or not..
Definition: SparseField.h:1481
const std::vector< Box3i > & m_blocks
Definition: MIPUtil.h:282
Contains functions for resampling fields.
FIELD3D_API FieldMapping::Ptr adjustedMIPFieldMapping(const FieldMapping::Ptr baseMapping, const V3i &baseRes, const Box3i &extents, const size_t level)
Definition: MIPUtil.cpp:77
MIPSeparableThreadOp(const Field_T &src, Field_T &tgt, const size_t level, const FilterOp_T &filterOp, const size_t dim, const std::vector< Box3i > &blocks, size_t &nextIdx, boost::mutex &mutex)
Definition: MIPUtil.h:172
Used to delegate the choice of bit depth to process at.
Definition: MIPUtil.h:84
FIELD3D_NAMESPACE_OPEN typedef::half half
Definition: SpiMathLib.h:64
Imath::V3i V3i
Definition: SpiMathLib.h:71
boost::intrusive_ptr< FieldMapping > Ptr
Definition: FieldMapping.h:92
const FilterOp_T & m_filterOp
Definition: MIPUtil.h:279
size_t threadingBlockSize(const DenseField< Data_T > &)
Constant size for all dense fields.
Definition: MIPUtil.h:104
FIELD3D_VEC3_T< T > floor(const FIELD3D_VEC3_T< T > &v)
Floor function for Vec3.
Definition: CoordSys.h:95
bool blockIsAllocated(int bi, int bj, int bk) const
Checks if a block is allocated.
Definition: SparseField.h:1472
#define FIELD3D_API
Definition: ns.h:77
const size_t m_numBlocks
Definition: MIPUtil.h:285
FIELD3D_NAMESPACE_OPEN MIPField_T::Ptr makeMIP(const typename MIPField_T::NestedType &base, const int minSize, const size_t numThreads)
Constructs a MIP representation of the given field.
Definition: MIPUtil.h:406
void mipResample(const Field_T &base, const Field_T &src, Field_T &tgt, const size_t level, const FilterOp_T &filterOp, const size_t numThreads)
Definition: MIPUtil.h:356
boost::mutex & m_mutex
Definition: MIPUtil.h:284
FIELD3D_API V3i mipResolution(const V3i &baseRes, const size_t level)
Definition: MIPUtil.cpp:66
Contains the SparseField class.
This Field subclass stores voxel data in block-allocated arrays.
Definition: SparseField.h:350
FIELD3D_VEC3_T< T > ceil(const FIELD3D_VEC3_T< T > &v)
Ceil function for Vec3.
Definition: CoordSys.h:105
const Box3i & dataWindow() const
Returns the data window. Any coordinate inside this window is safe to pass to value() in the Field su...
Definition: Field.h:258
const Field_T & m_src
Definition: MIPUtil.h:277
int blockSize() const
Returns the block size.
Definition: SparseField.h:1453
double discToCont(int discCoord)
Goes from discrete coordinates to continuous coordinates See Graphics Gems - What is a pixel...
Definition: Field.h:1075