FastMarch2.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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/FastMarch.h>
  9. #include <Mathematics/Math.h>
  10. // The topic of fast marching methods are discussed in the book
  11. // Level Set Methods and Fast Marching Methods:
  12. // Evolving Interfaces in Computational Geometry, Fluid Mechanics,
  13. // Computer Vision, and Materials Science
  14. // J.A. Sethian,
  15. // Cambridge University Press, 1999
  16. namespace WwiseGTE
  17. {
  18. template <typename Real>
  19. class FastMarch2 : public FastMarch<Real>
  20. {
  21. public:
  22. // Construction and destruction.
  23. FastMarch2(size_t xBound, size_t yBound, Real xSpacing, Real ySpacing,
  24. std::vector<size_t> const& seeds, std::vector<Real> const& speeds)
  25. :
  26. FastMarch<Real>(xBound * yBound, seeds, speeds)
  27. {
  28. Initialize(xBound, yBound, xSpacing, ySpacing);
  29. }
  30. FastMarch2(size_t xBound, size_t yBound, Real xSpacing, Real ySpacing,
  31. std::vector<size_t> const& seeds, Real speed)
  32. :
  33. FastMarch<Real>(xBound * yBound, seeds, speed)
  34. {
  35. Initialize(xBound, yBound, xSpacing, ySpacing);
  36. }
  37. virtual ~FastMarch2()
  38. {
  39. }
  40. // Member access.
  41. inline size_t GetXBound() const
  42. {
  43. return mXBound;
  44. }
  45. inline size_t GetYBound() const
  46. {
  47. return mYBound;
  48. }
  49. inline Real GetXSpacing() const
  50. {
  51. return mXSpacing;
  52. }
  53. inline Real GetYSpacing() const
  54. {
  55. return mYSpacing;
  56. }
  57. inline size_t Index(size_t x, size_t y) const
  58. {
  59. return x + mXBound * y;
  60. }
  61. // Pixel classification.
  62. virtual void GetBoundary(std::vector<size_t>& boundary) const override
  63. {
  64. for (size_t i = 0; i < this->mQuantity; ++i)
  65. {
  66. if (this->IsValid(i) && !this->IsTrial(i))
  67. {
  68. if (this->IsTrial(i - 1)
  69. || this->IsTrial(i + 1)
  70. || this->IsTrial(i - mXBound)
  71. || this->IsTrial(i + mXBound))
  72. {
  73. boundary.push_back(i);
  74. }
  75. }
  76. }
  77. }
  78. virtual bool IsBoundary(size_t i) const override
  79. {
  80. if (this->IsValid(i) && !this->IsTrial(i))
  81. {
  82. if (this->IsTrial(i - 1)
  83. || this->IsTrial(i + 1)
  84. || this->IsTrial(i - mXBound)
  85. || this->IsTrial(i + mXBound))
  86. {
  87. return true;
  88. }
  89. }
  90. return false;
  91. }
  92. // Run one step of the fast marching algorithm.
  93. virtual void Iterate() override
  94. {
  95. // Remove the minimum trial value from the heap.
  96. size_t i;
  97. Real value;
  98. this->mHeap.Remove(i, value);
  99. // Promote the trial value to a known value. The value was
  100. // negative but is now nonnegative (the heap stores only
  101. // nonnegative numbers).
  102. this->mTrials[i] = nullptr;
  103. // All trial pixels must be updated. All far neighbors must become trial
  104. // pixels.
  105. size_t iM1 = i - 1;
  106. if (this->IsTrial(iM1))
  107. {
  108. ComputeTime(iM1);
  109. this->mHeap.Update(this->mTrials[iM1], this->mTimes[iM1]);
  110. }
  111. else if (this->IsFar(iM1))
  112. {
  113. ComputeTime(iM1);
  114. this->mTrials[iM1] = this->mHeap.Insert(iM1, this->mTimes[iM1]);
  115. }
  116. size_t iP1 = i + 1;
  117. if (this->IsTrial(iP1))
  118. {
  119. ComputeTime(iP1);
  120. this->mHeap.Update(this->mTrials[iP1], this->mTimes[iP1]);
  121. }
  122. else if (this->IsFar(iP1))
  123. {
  124. ComputeTime(iP1);
  125. this->mTrials[iP1] = this->mHeap.Insert(iP1, this->mTimes[iP1]);
  126. }
  127. size_t iMXB = i - mXBound;
  128. if (this->IsTrial(iMXB))
  129. {
  130. ComputeTime(iMXB);
  131. this->mHeap.Update(this->mTrials[iMXB], this->mTimes[iMXB]);
  132. }
  133. else if (this->IsFar(iMXB))
  134. {
  135. ComputeTime(iMXB);
  136. this->mTrials[iMXB] = this->mHeap.Insert(iMXB, this->mTimes[iMXB]);
  137. }
  138. size_t iPXB = i + mXBound;
  139. if (this->IsTrial(iPXB))
  140. {
  141. ComputeTime(iPXB);
  142. this->mHeap.Update(this->mTrials[iPXB], this->mTimes[iPXB]);
  143. }
  144. else if (this->IsFar(iPXB))
  145. {
  146. ComputeTime(iPXB);
  147. this->mTrials[iPXB] = this->mHeap.Insert(iPXB, this->mTimes[iPXB]);
  148. }
  149. }
  150. protected:
  151. // Called by the constructors.
  152. void Initialize(size_t xBound, size_t yBound, Real xSpacing, Real ySpacing)
  153. {
  154. mXBound = xBound;
  155. mYBound = yBound;
  156. mXBoundM1 = mXBound - 1;
  157. mYBoundM1 = mYBound - 1;
  158. mXSpacing = xSpacing;
  159. mYSpacing = ySpacing;
  160. mInvXSpacing = (Real)1 / xSpacing;
  161. mInvYSpacing = (Real)1 / ySpacing;
  162. // Boundary pixels are marked as zero speed to allow us to avoid
  163. // having to process the boundary pixels separately during the
  164. // iteration.
  165. size_t x, y, i;
  166. // vertex (0,0)
  167. i = Index(0, 0);
  168. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  169. this->mTimes[i] = -std::numeric_limits<Real>::max();
  170. // vertex (xmax,0)
  171. i = Index(mXBoundM1, 0);
  172. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  173. this->mTimes[i] = -std::numeric_limits<Real>::max();
  174. // vertex (0,ymax)
  175. i = Index(0, mYBoundM1);
  176. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  177. this->mTimes[i] = -std::numeric_limits<Real>::max();
  178. // vertex (xmax,ymax)
  179. i = Index(mXBoundM1, mYBoundM1);
  180. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  181. this->mTimes[i] = -std::numeric_limits<Real>::max();
  182. // edges (x,0) and (x,ymax)
  183. for (x = 0; x < mXBound; ++x)
  184. {
  185. i = Index(x, 0);
  186. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  187. this->mTimes[i] = -std::numeric_limits<Real>::max();
  188. i = Index(x, mYBoundM1);
  189. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  190. this->mTimes[i] = -std::numeric_limits<Real>::max();
  191. }
  192. // edges (0,y) and (xmax,y)
  193. for (y = 0; y < mYBound; ++y)
  194. {
  195. i = Index(0, y);
  196. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  197. this->mTimes[i] = -std::numeric_limits<Real>::max();
  198. i = Index(mXBoundM1, y);
  199. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  200. this->mTimes[i] = -std::numeric_limits<Real>::max();
  201. }
  202. // Compute the first batch of trial pixels. These are pixels a
  203. // grid distance of one away from the seed pixels.
  204. for (y = 1; y < mYBoundM1; ++y)
  205. {
  206. for (x = 1; x < mXBoundM1; ++x)
  207. {
  208. i = Index(x, y);
  209. if (this->IsFar(i))
  210. {
  211. if ((this->IsValid(i - 1) && !this->IsTrial(i - 1))
  212. || (this->IsValid(i + 1) && !this->IsTrial(i + 1))
  213. || (this->IsValid(i - mXBound) && !this->IsTrial(i - mXBound))
  214. || (this->IsValid(i + mXBound) && !this->IsTrial(i + mXBound)))
  215. {
  216. ComputeTime(i);
  217. this->mTrials[i] = this->mHeap.Insert(i, this->mTimes[i]);
  218. }
  219. }
  220. }
  221. }
  222. }
  223. // Called by Iterate().
  224. void ComputeTime(size_t i)
  225. {
  226. bool hasXTerm;
  227. Real xConst;
  228. if (this->IsValid(i - 1))
  229. {
  230. hasXTerm = true;
  231. xConst = this->mTimes[i - 1];
  232. if (this->IsValid(i + 1))
  233. {
  234. if (this->mTimes[i + 1] < xConst)
  235. {
  236. xConst = this->mTimes[i + 1];
  237. }
  238. }
  239. }
  240. else if (this->IsValid(i + 1))
  241. {
  242. hasXTerm = true;
  243. xConst = this->mTimes[i + 1];
  244. }
  245. else
  246. {
  247. hasXTerm = false;
  248. xConst = (Real)0;
  249. }
  250. bool hasYTerm;
  251. Real yConst;
  252. if (this->IsValid(i - mXBound))
  253. {
  254. hasYTerm = true;
  255. yConst = this->mTimes[i - mXBound];
  256. if (this->IsValid(i + mXBound))
  257. {
  258. if (this->mTimes[i + mXBound] < yConst)
  259. {
  260. yConst = this->mTimes[i + mXBound];
  261. }
  262. }
  263. }
  264. else if (this->IsValid(i + mXBound))
  265. {
  266. hasYTerm = true;
  267. yConst = this->mTimes[i + mXBound];
  268. }
  269. else
  270. {
  271. hasYTerm = false;
  272. yConst = (Real)0;
  273. }
  274. if (hasXTerm)
  275. {
  276. if (hasYTerm)
  277. {
  278. Real sum = xConst + yConst;
  279. Real diff = xConst - yConst;
  280. Real discr = (Real)2 * this->mInvSpeeds[i] * this->mInvSpeeds[i] - diff * diff;
  281. if (discr >= (Real)0)
  282. {
  283. // The quadratic equation has a real-valued solution.
  284. // Choose the largest positive root for the crossing
  285. // time.
  286. this->mTimes[i] = (Real)0.5 * (sum + std::sqrt(discr));
  287. }
  288. else
  289. {
  290. // The quadratic equation does not have a real-valued
  291. // solution. This can happen when the speed is so
  292. // large that the time gradient has very small length,
  293. // which means that the time has not changed
  294. // significantly from the neighbors to the current
  295. // pixel. Just choose the maximum time of the
  296. // neighbors. (Is there a better choice?)
  297. this->mTimes[i] = (diff >= (Real)0 ? xConst : yConst);
  298. }
  299. }
  300. else
  301. {
  302. // The equation is linear.
  303. this->mTimes[i] = this->mInvSpeeds[i] + xConst;
  304. }
  305. }
  306. else if (hasYTerm)
  307. {
  308. // The equation is linear.
  309. this->mTimes[i] = this->mInvSpeeds[i] + yConst;
  310. }
  311. else
  312. {
  313. // Assert: The pixel must have at least one known neighbor.
  314. }
  315. }
  316. size_t mXBound, mYBound, mXBoundM1, mYBoundM1;
  317. Real mXSpacing, mYSpacing, mInvXSpacing, mInvYSpacing;
  318. };
  319. }