Ptex
PtexSeparableKernel.h
Go to the documentation of this file.
1 #ifndef PtexSeparableKernel_h
2 #define PtexSeparableKernel_h
3 
4 /*
5 PTEX SOFTWARE
6 Copyright 2014 Disney Enterprises, Inc. 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 are
10 met:
11 
12  * Redistributions of source code must retain the above copyright
13  notice, this list of conditions and the following disclaimer.
14 
15  * Redistributions in binary form must reproduce the above copyright
16  notice, this list of conditions and the following disclaimer in
17  the documentation and/or other materials provided with the
18  distribution.
19 
20  * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
21  Studios" or the names of its contributors may NOT be used to
22  endorse or promote products derived from this software without
23  specific prior written permission from Walt Disney Pictures.
24 
25 Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
26 CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
27 BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
28 FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
29 IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
30 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
37 */
38 
39 #include <assert.h>
40 #include <algorithm>
41 #include <numeric>
42 #include "Ptexture.h"
43 #include "PtexUtils.h"
44 
46 
47 // Separable convolution kernel
49  public:
50  Res res; // resolution that kernel was built for
51  int u, v; // uv offset within face data
52  int uw, vw; // kernel width
53  float* ku; // kernel weights in u
54  float* kv; // kernel weights in v
55  static const int kmax = 10; // max kernel width
56  float kubuff[kmax];
57  float kvbuff[kmax];
58  int rot;
59 
61  : res(0), u(0), v(0), uw(0), vw(0), ku(kubuff), kv(kvbuff), rot(0)
62  {
63  kubuff[0] = 0; // keep cppcheck happy
64  kvbuff[0] = 0;
65  }
66 
68  {
69  set(k.res, k.u, k.v, k.uw, k.vw, k.ku, k.kv, k.rot);
70  }
71 
73  {
74  set(k.res, k.u, k.v, k.uw, k.vw, k.ku, k.kv, k.rot);
75  return *this;
76  }
77 
78  void set(Res resVal,
79  int uVal, int vVal,
80  int uwVal, int vwVal,
81  const float* kuVal, const float* kvVal, int rotVal=0)
82  {
83  assert(uwVal <= kmax && vwVal <= kmax);
84  res = resVal;
85  u = uVal;
86  v = vVal;
87  uw = uwVal;
88  vw = vwVal;
89  memcpy(kubuff, kuVal, sizeof(*ku)*uw);
90  memcpy(kvbuff, kvVal, sizeof(*kv)*vw);
91  ku = kubuff;
92  kv = kvbuff;
93  rot = rotVal;
94  }
95 
96  void stripZeros()
97  {
98  while (ku[0] == 0) { ku++; u++; uw--; }
99  while (ku[uw-1] == 0) { uw--; }
100  while (kv[0] == 0) { kv++; v++; vw--; }
101  while (kv[vw-1] == 0) { vw--; }
102  assert(uw > 0 && vw > 0);
103  }
104 
105  float weight() const
106  {
107  return accumulate(ku, uw) * accumulate(kv, vw);
108  }
109 
110  void mergeL(BorderMode mode)
111  {
112  int w = -u;
113  if (mode != m_black)
114  ku[w] += accumulate(ku, w);
115  ku += w;
116  uw -= w;
117  u = 0;
118  }
119 
120  void mergeR(BorderMode mode)
121  {
122  int w = uw + u - res.u();
123  float* kp = ku + uw - w;
124  if (mode != m_black)
125  kp[-1] += accumulate(kp, w);
126  uw -= w;
127  }
128 
129  void mergeB(BorderMode mode)
130  {
131  int w = -v;
132  if (mode != m_black)
133  kv[w] += accumulate(kv, w);
134  kv += w;
135  vw -= w;
136  v = 0;
137  }
138 
139  void mergeT(BorderMode mode)
140  {
141  int w = vw + v - res.v();
142  float* kp = kv + vw - w;
143  if (mode != m_black)
144  kp[-1] += accumulate(kp, w);
145  vw -= w;
146  }
147 
149  {
150  // split off left piece of width w into k
151  int w = -u;
152 
153  if (w < uw) {
154  // normal case - split off a portion
155  // res u v uw vw ku kv
156  k.set(res, res.u()-w, v, w, vw, ku, kv);
157 
158  // update local
159  u = 0;
160  uw -= w;
161  ku += w;
162  }
163  else {
164  // entire kernel is split off
165  k = *this;
166  k.u += res.u();
167  u = 0; uw = 0;
168  }
169  }
170 
172  {
173  // split off right piece of width w into k
174  int w = u + uw - res.u();
175 
176  if (w < uw) {
177  // normal case - split off a portion
178  // res u v uw vw ku kv
179  k.set(res, 0, v, w, vw, ku + uw - w, kv);
180 
181  // update local
182  uw -= w;
183  }
184  else {
185  // entire kernel is split off
186  k = *this;
187  k.u -= res.u();
188  u = 0; uw = 0;
189  }
190  }
191 
193  {
194  // split off bottom piece of width w into k
195  int w = -v;
196  if (w < vw) {
197  // normal case - split off a portion
198  // res u v uw vw ku kv
199  k.set(res, u, res.v()-w, uw, w, ku, kv);
200 
201  // update local
202  v = 0;
203  vw -= w;
204  kv += w;
205  }
206  else {
207  // entire kernel is split off
208  k = *this;
209  k.v += res.v();
210  v = 0; vw = 0;
211  }
212  }
213 
215  {
216  // split off top piece of width w into k
217  int w = v + vw - res.v();
218  if (w < vw) {
219  // normal case - split off a portion
220  // res u v uw vw ku kv
221  k.set(res, u, 0, uw, w, ku, kv + vw - w);
222 
223  // update local
224  vw -= w;
225  }
226  else {
227  // entire kernel is split off
228  k = *this;
229  k.v -= res.v();
230  v = 0; vw = 0;
231  }
232  }
233 
234  void flipU()
235  {
236  u = res.u() - u - uw;
237  std::reverse(ku, ku+uw);
238  }
239 
240  void flipV()
241  {
242  v = res.v() - v - vw;
243  std::reverse(kv, kv+vw);
244  }
245 
246  void swapUV()
247  {
248  res.swapuv();
249  std::swap(u, v);
250  std::swap(uw, vw);
251  std::swap(ku, kv);
252  }
253 
254  void rotate(int rotVal)
255  {
256  // rotate kernel 'rot' steps ccw
257  switch (rotVal & 3) {
258  default: return;
259  case 1: flipU(); swapUV(); break;
260  case 2: flipU(); flipV(); break;
261  case 3: flipV(); swapUV(); break;
262  }
263  rot = (rot + rotVal)&3;
264  }
265 
266  bool adjustMainToSubface(int eid)
267  {
268  // to adjust the kernel for the subface, we must adjust the res down and offset the uv coords
269  // however, if the res is already zero, we must upres the kernel first
270  if (res.ulog2 == 0) upresU();
271  if (res.vlog2 == 0) upresV();
272 
273  if (res.ulog2 > 0) res.ulog2--;
274  if (res.vlog2 > 0) res.vlog2--;
275 
276  // offset uv coords and determine whether target subface is the primary one
277  bool primary = 0;
278  int resu = res.u(), resv = res.v();
279  switch (eid&3) {
280  case e_bottom:
281  primary = (u < resu);
282  v -= resv;
283  if (!primary) u -= resu;
284  break;
285  case e_right:
286  primary = (v < resv);
287  if (!primary) v -= resv;
288  break;
289  case e_top:
290  primary = (u >= resu);
291  if (primary) u -= resu;
292  break;
293  case e_left:
294  primary = (v >= resv);
295  u -= resu;
296  if (primary) v -= resv;
297  break;
298  }
299  return primary;
300  }
301 
302  void adjustSubfaceToMain(int eid)
303  {
304  switch (eid&3) {
305  case e_bottom: v += res.v(); break;
306  case e_right: break;
307  case e_top: u += res.u(); break;
308  case e_left: u += res.u(); v += res.v(); break;
309  }
310  res.ulog2++; res.vlog2++;
311  }
312 
313  void downresU()
314  {
315  float* src = ku;
316  float* dst = ku;
317 
318  // skip odd leading sample (if any)
319  if (u & 1) {
320  dst++;
321  src++;
322  uw--;
323  }
324 
325  // combine even pairs
326  for (int i = uw/2; i > 0; i--) {
327  *dst++ = src[0] + src[1];
328  src += 2;
329  }
330 
331  // copy odd trailing sample (if any)
332  if (uw & 1) {
333  *dst++ = *src++;
334  }
335 
336  // update state
337  u /= 2;
338  uw = int(dst - ku);
339  res.ulog2--;
340  }
341 
342  void downresV()
343  {
344  float* src = kv;
345  float* dst = kv;
346 
347  // skip odd leading sample (if any)
348  if (v & 1) {
349  dst++;
350  src++;
351  vw--;
352  }
353 
354  // combine even pairs
355  for (int i = vw/2; i > 0; i--) {
356  *dst++ = src[0] + src[1];
357  src += 2;
358  }
359 
360  // copy odd trailing sample (if any)
361  if (vw & 1) {
362  *dst++ = *src++;
363  }
364 
365  // update state
366  v /= 2;
367  vw = int(dst - kv);
368  res.vlog2--;
369  }
370 
371  void upresU()
372  {
373  float* src = ku + uw-1;
374  float* dst = ku + uw*2-2;
375  for (int i = uw; i > 0; i--) {
376  dst[0] = dst[1] = *src-- / 2;
377  dst -=2;
378  }
379  uw *= 2;
380  u *= 2;
381  res.ulog2++;
382  }
383 
384  void upresV()
385  {
386  float* src = kv + vw-1;
387  float* dst = kv + vw*2-2;
388  for (int i = vw; i > 0; i--) {
389  dst[0] = dst[1] = *src-- / 2;
390  dst -=2;
391  }
392  vw *= 2;
393  v *= 2;
394  res.vlog2++;
395  }
396 
397  float makeSymmetric(float initialWeight)
398  {
399  assert(u == 0 && v == 0);
400 
401  // downres higher-res dimension until equal
402  if (res.ulog2 > res.vlog2) {
403  do { downresU(); } while(res.ulog2 > res.vlog2);
404  }
405  else if (res.vlog2 > res.ulog2) {
406  do { downresV(); } while (res.vlog2 > res.ulog2);
407  }
408 
409  // truncate excess samples in longer dimension
410  uw = vw = PtexUtils::min(uw, vw);
411 
412  // combine corresponding u and v samples and compute new kernel weight
413  float newWeight = 0;
414  for (int i = 0; i < uw; i++) {
415  float sum = ku[i] + kv[i];
416  ku[i] = kv[i] = sum;
417  newWeight += sum;
418  }
419  newWeight *= newWeight; // equivalent to k.weight() ( = sum(ku)*sum(kv) )
420 
421  // compute scale factor to compensate for weight change
422  float scale = newWeight == 0 ? 1.f : initialWeight / newWeight;
423 
424  // Note: a sharpening kernel (like Mitchell) can produce
425  // negative weights which may cancel out when adding the two
426  // kernel axes together, and this can cause the compensation
427  // scale factor to spike up. We expect the scale factor to be
428  // less than one in "normal" cases (i.e. ku*kv <= (ku+kv)^2 if ku
429  // and kv are both positive), so clamping to -1..1 will have
430  // no effect on positive kernels. If there are negative
431  // weights, the clamping will just limit the amount of
432  // sharpening happening at the corners, and the result will
433  // still be smooth.
434 
435  // clamp scale factor to -1..1 range
436  if (scale >= 1) {
437  // scale by 1 (i.e. do nothing)
438  }
439  else {
440  if (scale < -1) {
441  // a negative scale means the original kernel had an overall negative weight
442  // after making symmetric, the kernel will always be positive
443  // scale ku by -1
444  // note: choice of u is arbitrary; we could have scaled u or v (but not both)
445  for (int i = 0; i < uw; i++) ku[i] *= -1;
446  newWeight = -newWeight;
447  }
448  else {
449  // scale ku to restore initialWeight (again, choice of u instead of v is arbitrary)
450  for (int i = 0; i < uw; i++) ku[i] *= scale;
451  newWeight = initialWeight;
452  }
453  }
454  return newWeight;
455  }
456 
457  void apply(float* dst, void* data, DataType dt, int nChan, int nTxChan)
458  {
459  // dispatch specialized apply function
460  ApplyFn fn = applyFunctions[(nChan!=nTxChan)*20 + ((unsigned)nChan<=4)*nChan*4 + dt];
461  fn(*this, dst, data, nChan, nTxChan);
462  }
463 
464  void applyConst(float* dst, void* data, DataType dt, int nChan)
465  {
466  PtexUtils::applyConst(weight(), dst, data, dt, nChan);
467  }
468 
469  private:
470  typedef void (*ApplyFn)(PtexSeparableKernel& k, float* dst, void* data, int nChan, int nTxChan);
471  typedef void (*ApplyConstFn)(float weight, float* dst, void* data, int nChan);
474  static inline float accumulate(const float* p, int n)
475  {
476  float result = 0;
477  for (const float* e = p + n; p != e; p++) result += *p;
478  return result;
479  }
480 };
481 
483 
484 #endif
PtexUtils::min
T min(T a, T b)
Definition: PtexUtils.h:147
PtexSeparableKernel::v
int v
Definition: PtexSeparableKernel.h:51
PtexSeparableKernel::stripZeros
void stripZeros()
Definition: PtexSeparableKernel.h:96
PtexSeparableKernel::flipU
void flipU()
Definition: PtexSeparableKernel.h:234
PtexSeparableKernel::upresV
void upresV()
Definition: PtexSeparableKernel.h:384
PtexSeparableKernel::uw
int uw
Definition: PtexSeparableKernel.h:52
PtexSeparableKernel::ApplyConstFn
void(* ApplyConstFn)(float weight, float *dst, void *data, int nChan)
Definition: PtexSeparableKernel.h:471
Ptex::e_top
@ e_top
Top edge, from UV (1,1) to (0,1)
Definition: Ptexture.h:110
PtexSeparableKernel::applyFunctions
static ApplyFn applyFunctions[40]
Definition: PtexSeparableKernel.h:472
PTEX_NAMESPACE_END
#define PTEX_NAMESPACE_END
Definition: PtexVersion.h:62
PtexSeparableKernel::splitT
void splitT(PtexSeparableKernel &k)
Definition: PtexSeparableKernel.h:214
PtexSeparableKernel::upresU
void upresU()
Definition: PtexSeparableKernel.h:371
PtexSeparableKernel::kubuff
float kubuff[kmax]
Definition: PtexSeparableKernel.h:56
PtexUtils.h
PtexSeparableKernel::mergeT
void mergeT(BorderMode mode)
Definition: PtexSeparableKernel.h:139
PtexSeparableKernel::adjustSubfaceToMain
void adjustSubfaceToMain(int eid)
Definition: PtexSeparableKernel.h:302
PtexSeparableKernel::splitR
void splitR(PtexSeparableKernel &k)
Definition: PtexSeparableKernel.h:171
PtexSeparableKernel::adjustMainToSubface
bool adjustMainToSubface(int eid)
Definition: PtexSeparableKernel.h:266
PtexSeparableKernel::swapUV
void swapUV()
Definition: PtexSeparableKernel.h:246
PtexSeparableKernel::rotate
void rotate(int rotVal)
Definition: PtexSeparableKernel.h:254
PtexSeparableKernel
Definition: PtexSeparableKernel.h:48
PtexSeparableKernel::operator=
PtexSeparableKernel & operator=(const PtexSeparableKernel &k)
Definition: PtexSeparableKernel.h:72
Ptex::m_black
@ m_black
texel beyond border are assumed to be black
Definition: Ptexture.h:101
PtexSeparableKernel::applyConstFunctions
static ApplyConstFn applyConstFunctions[20]
Definition: PtexSeparableKernel.h:473
PtexSeparableKernel::kvbuff
float kvbuff[kmax]
Definition: PtexSeparableKernel.h:57
Ptex::DataType
DataType
Type of data stored in texture file.
Definition: Ptexture.h:85
PtexSeparableKernel::splitL
void splitL(PtexSeparableKernel &k)
Definition: PtexSeparableKernel.h:148
PtexSeparableKernel::vw
int vw
Definition: PtexSeparableKernel.h:52
PtexSeparableKernel::mergeL
void mergeL(BorderMode mode)
Definition: PtexSeparableKernel.h:110
PtexSeparableKernel::set
void set(Res resVal, int uVal, int vVal, int uwVal, int vwVal, const float *kuVal, const float *kvVal, int rotVal=0)
Definition: PtexSeparableKernel.h:78
PtexSeparableKernel::ku
float * ku
Definition: PtexSeparableKernel.h:53
PtexSeparableKernel::mergeB
void mergeB(BorderMode mode)
Definition: PtexSeparableKernel.h:129
PTEX_NAMESPACE_BEGIN
Definition: PtexSeparableKernel.cpp:42
PtexUtils::applyConst
void applyConst(float weight, float *dst, void *data, Ptex::DataType dt, int nChan)
Definition: PtexUtils.h:246
PtexSeparableKernel::PtexSeparableKernel
PtexSeparableKernel(const PtexSeparableKernel &k)
Definition: PtexSeparableKernel.h:67
PtexSeparableKernel::downresV
void downresV()
Definition: PtexSeparableKernel.h:342
PtexSeparableKernel::flipV
void flipV()
Definition: PtexSeparableKernel.h:240
PtexSeparableKernel::applyConst
void applyConst(float *dst, void *data, DataType dt, int nChan)
Definition: PtexSeparableKernel.h:464
PtexSeparableKernel::res
Res res
Definition: PtexSeparableKernel.h:50
Ptex::e_bottom
@ e_bottom
Bottom edge, from UV (0,0) to (1,0)
Definition: Ptexture.h:108
Ptex::e_right
@ e_right
Right edge, from UV (1,0) to (1,1)
Definition: Ptexture.h:109
PtexSeparableKernel::u
int u
Definition: PtexSeparableKernel.h:51
PtexSeparableKernel::accumulate
static float accumulate(const float *p, int n)
Definition: PtexSeparableKernel.h:474
Ptex::e_left
@ e_left
Left edge, from UV (0,1) to (0,0)
Definition: Ptexture.h:111
PtexSeparableKernel::ApplyFn
void(* ApplyFn)(PtexSeparableKernel &k, float *dst, void *data, int nChan, int nTxChan)
Definition: PtexSeparableKernel.h:470
PtexSeparableKernel::kv
float * kv
Definition: PtexSeparableKernel.h:54
PtexSeparableKernel::splitB
void splitB(PtexSeparableKernel &k)
Definition: PtexSeparableKernel.h:192
PtexSeparableKernel::apply
void apply(float *dst, void *data, DataType dt, int nChan, int nTxChan)
Definition: PtexSeparableKernel.h:457
PtexSeparableKernel::mergeR
void mergeR(BorderMode mode)
Definition: PtexSeparableKernel.h:120
PtexSeparableKernel::makeSymmetric
float makeSymmetric(float initialWeight)
Definition: PtexSeparableKernel.h:397
PtexSeparableKernel::weight
float weight() const
Definition: PtexSeparableKernel.h:105
PtexSeparableKernel::rot
int rot
Definition: PtexSeparableKernel.h:58
PtexSeparableKernel::downresU
void downresU()
Definition: PtexSeparableKernel.h:313
Ptexture.h
Public API classes for reading, writing, caching, and filtering Ptex files.
Ptex::BorderMode
BorderMode
How to handle mesh border when filtering.
Definition: Ptexture.h:99
PtexSeparableKernel::kmax
static const int kmax
Definition: PtexSeparableKernel.h:55
PtexSeparableKernel::PtexSeparableKernel
PtexSeparableKernel()
Definition: PtexSeparableKernel.h:60