FastGaussianBlur3.h 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <Mathematics/Math.h>
  9. // The algorithms here are based on solving the linear heat equation using
  10. // finite differences in scale, not in time. The following document has
  11. // a brief summary of the concept,
  12. // https://www.geometrictools.com/Documentation/FastGaussianBlur.pdf
  13. // The idea is to represent the blurred image as f(x,s) in terms of position
  14. // x and scale s. Gaussian blurring is accomplished by using the input image
  15. // I(x,s0) as the initial image (of scale s0 > 0) for the partial differential
  16. // equation
  17. // s*df/ds = s^2*Laplacian(f)
  18. // where the Laplacian operator is
  19. // Laplacian = (d/dx)^2, dimension 1
  20. // Laplacian = (d/dx)^2+(d/dy)^2, dimension 2
  21. // Laplacian = (d/dx)^2+(d/dy)^2+(d/dz)^2, dimension 3
  22. //
  23. // The term s*df/ds is approximated by
  24. // s*df(x,s)/ds = (f(x,b*s)-f(x,s))/ln(b)
  25. // for b > 1, but close to 1, where ln(b) is the natural logarithm of b. If
  26. // you take the limit of the right-hand side as b approaches 1, you get the
  27. // left-hand side.
  28. //
  29. // The term s^2*((d/dx)^2)f is approximated by
  30. // s^2*((d/dx)^2)f = (f(x+h*s,s)-2*f(x,s)+f(x-h*s,s))/h^2
  31. // for h > 0, but close to zero.
  32. //
  33. // Equating the approximations for the left-hand side and the right-hand side
  34. // of the partial differential equation leads to the numerical method used in
  35. // this code.
  36. //
  37. // For iterative application of these functions, the caller is responsible
  38. // for constructing a geometric sequence of scales,
  39. // s0, s1 = s0*b, s2 = s1*b = s0*b^2, ...
  40. // where the base b satisfies 1 < b < exp(0.5*d) where d is the dimension of
  41. // the image. The upper bound on b guarantees stability of the finite
  42. // difference method used to approximate the partial differential equation.
  43. // The method assumes a pixel size of h = 1.
  44. namespace WwiseGTE
  45. {
  46. template <typename T>
  47. class FastGaussianBlur3
  48. {
  49. public:
  50. void Execute(int xBound, int yBound, int zBound, T const* input, T* output,
  51. double scale, double logBase)
  52. {
  53. mXBound = xBound;
  54. mYBound = yBound;
  55. mZBound = zBound;
  56. mInput = input;
  57. mOutput = output;
  58. int xBoundM1 = xBound - 1, yBoundM1 = yBound - 1, zBoundM1 = zBound - 1;
  59. for (int z = 0; z < zBound; ++z)
  60. {
  61. double rzps = static_cast<double>(z) + scale;
  62. double rzms = static_cast<double>(z) - scale;
  63. int zp1 = static_cast<int>(std::floor(rzps));
  64. int zm1 = static_cast<int>(std::ceil(rzms));
  65. for (int y = 0; y < yBound; ++y)
  66. {
  67. double ryps = static_cast<double>(y) + scale;
  68. double ryms = static_cast<double>(y) - scale;
  69. int yp1 = static_cast<int>(std::floor(ryps));
  70. int ym1 = static_cast<int>(std::ceil(ryms));
  71. for (int x = 0; x < xBound; ++x)
  72. {
  73. double rxps = static_cast<double>(x) + scale;
  74. double rxms = static_cast<double>(x) - scale;
  75. int xp1 = static_cast<int>(std::floor(rxps));
  76. int xm1 = static_cast<int>(std::ceil(rxms));
  77. double center = Input(x, y, z);
  78. double xsum = -2.0 * center, ysum = xsum, zsum = xsum;
  79. // x portion of second central difference
  80. if (xp1 >= xBoundM1) // use boundary value
  81. {
  82. xsum += Input(xBoundM1, y, z);
  83. }
  84. else // linearly interpolate
  85. {
  86. double imgXp1 = Input(xp1, y, z);
  87. double imgXp2 = Input(xp1 + 1, y, z);
  88. double delta = rxps - static_cast<double>(xp1);
  89. xsum += imgXp1 + delta * (imgXp2 - imgXp1);
  90. }
  91. if (xm1 <= 0) // use boundary value
  92. {
  93. xsum += Input(0, y, z);
  94. }
  95. else // linearly interpolate
  96. {
  97. double imgXm1 = Input(xm1, y, z);
  98. double imgXm2 = Input(xm1 - 1, y, z);
  99. double delta = rxms - static_cast<double>(xm1);
  100. xsum += imgXm1 + delta * (imgXm1 - imgXm2);
  101. }
  102. // y portion of second central difference
  103. if (yp1 >= yBoundM1) // use boundary value
  104. {
  105. ysum += Input(x, yBoundM1, z);
  106. }
  107. else // linearly interpolate
  108. {
  109. double imgYp1 = Input(x, yp1, z);
  110. double imgYp2 = Input(x, yp1 + 1, z);
  111. double delta = ryps - static_cast<double>(yp1);
  112. ysum += imgYp1 + delta * (imgYp2 - imgYp1);
  113. }
  114. if (ym1 <= 0) // use boundary value
  115. {
  116. ysum += Input(x, 0, z);
  117. }
  118. else // linearly interpolate
  119. {
  120. double imgYm1 = Input(x, ym1, z);
  121. double imgYm2 = Input(x, ym1 - 1, z);
  122. double delta = ryms - static_cast<double>(ym1);
  123. ysum += imgYm1 + delta * (imgYm1 - imgYm2);
  124. }
  125. // z portion of second central difference
  126. if (zp1 >= zBoundM1) // use boundary value
  127. {
  128. zsum += Input(x, y, zBoundM1);
  129. }
  130. else // linearly interpolate
  131. {
  132. double imgZp1 = Input(x, y, zp1);
  133. double imgZp2 = Input(x, y, zp1 + 1);
  134. double delta = rzps - static_cast<double>(zp1);
  135. zsum += imgZp1 + delta * (imgZp2 - imgZp1);
  136. }
  137. if (zm1 <= 0) // use boundary value
  138. {
  139. zsum += Input(x, y, 0);
  140. }
  141. else // linearly interpolate
  142. {
  143. double imgZm1 = Input(x, y, zm1);
  144. double imgZm2 = Input(x, y, zm1 - 1);
  145. double delta = rzms - static_cast<double>(zm1);
  146. zsum += imgZm1 + delta * (imgZm1 - imgZm2);
  147. }
  148. Output(x, y, z) = static_cast<T>(center + logBase * (xsum + ysum + zsum));
  149. }
  150. }
  151. }
  152. }
  153. private:
  154. inline double Input(int x, int y, int z) const
  155. {
  156. return static_cast<double>(mInput[x + mXBound * (y + mYBound * z)]);
  157. }
  158. inline T& Output(int x, int y, int z)
  159. {
  160. return mOutput[x + mXBound * (y + mYBound * z)];
  161. }
  162. int mXBound, mYBound, mZBound;
  163. T const* mInput;
  164. T* mOutput;
  165. };
  166. }