FastMarch3.h 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  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 FastMarch3 : public FastMarch<Real>
  20. {
  21. public:
  22. // Construction and destruction.
  23. FastMarch3(size_t xBound, size_t yBound, size_t zBound,
  24. Real xSpacing, Real ySpacing, Real zSpacing,
  25. std::vector<size_t> const& seeds, std::vector<Real> const& speeds)
  26. :
  27. FastMarch<Real>(xBound * yBound * zBound, seeds, speeds)
  28. {
  29. Initialize(xBound, yBound, zBound, xSpacing, ySpacing, zSpacing);
  30. }
  31. FastMarch3(size_t xBound, size_t yBound, size_t zBound,
  32. Real xSpacing, Real ySpacing, Real zSpacing,
  33. std::vector<size_t> const& seeds, Real speed)
  34. :
  35. FastMarch<Real>(xBound * yBound * zBound, seeds, speed)
  36. {
  37. Initialize(xBound, yBound, zBound, xSpacing, ySpacing, zSpacing);
  38. }
  39. virtual ~FastMarch3()
  40. {
  41. }
  42. // Member access.
  43. inline size_t GetXBound() const
  44. {
  45. return mXBound;
  46. }
  47. inline size_t GetYBound() const
  48. {
  49. return mYBound;
  50. }
  51. inline size_t GetZBound() const
  52. {
  53. return mZBound;
  54. }
  55. inline Real GetXSpacing() const
  56. {
  57. return mXSpacing;
  58. }
  59. inline Real GetYSpacing() const
  60. {
  61. return mYSpacing;
  62. }
  63. inline Real GetZSpacing() const
  64. {
  65. return mZSpacing;
  66. }
  67. inline size_t Index(size_t x, size_t y, size_t z) const
  68. {
  69. return x + mXBound * (y + mYBound * z);
  70. }
  71. // Voxel classification.
  72. virtual void GetBoundary(std::vector<size_t>& boundary) const override
  73. {
  74. for (size_t i = 0; i < this->mQuantity; ++i)
  75. {
  76. if (this->IsValid(i) && !this->IsTrial(i))
  77. {
  78. if (this->IsTrial(i - 1)
  79. || this->IsTrial(i + 1)
  80. || this->IsTrial(i - mXBound)
  81. || this->IsTrial(i + mXBound)
  82. || this->IsTrial(i - mXYBound)
  83. || this->IsTrial(i + mXYBound))
  84. {
  85. boundary.push_back(i);
  86. }
  87. }
  88. }
  89. }
  90. virtual bool IsBoundary(size_t i) const override
  91. {
  92. if (this->IsValid(i) && !this->IsTrial(i))
  93. {
  94. if (this->IsTrial(i - 1)
  95. || this->IsTrial(i + 1)
  96. || this->IsTrial(i - mXBound)
  97. || this->IsTrial(i + mXBound)
  98. || this->IsTrial(i - mXYBound)
  99. || this->IsTrial(i + mXYBound))
  100. {
  101. return true;
  102. }
  103. }
  104. return false;
  105. }
  106. // Run one step of the fast marching algorithm.
  107. virtual void Iterate() override
  108. {
  109. // Remove the minimum trial value from the heap.
  110. size_t i;
  111. Real value;
  112. this->mHeap.Remove(i, value);
  113. // Promote the trial value to a known value. The value was
  114. // negative but is now nonnegative (the heap stores only
  115. // nonnegative numbers).
  116. this->mTrials[i] = nullptr;
  117. // All trial pixels must be updated. All far neighbors must
  118. // become trial pixels.
  119. size_t iM1 = i - 1;
  120. if (this->IsTrial(iM1))
  121. {
  122. ComputeTime(iM1);
  123. this->mHeap.Update(this->mTrials[iM1], this->mTimes[iM1]);
  124. }
  125. else if (this->IsFar(iM1))
  126. {
  127. ComputeTime(iM1);
  128. this->mTrials[iM1] = this->mHeap.Insert(iM1, this->mTimes[iM1]);
  129. }
  130. size_t iP1 = i + 1;
  131. if (this->IsTrial(iP1))
  132. {
  133. ComputeTime(iP1);
  134. this->mHeap.Update(this->mTrials[iP1], this->mTimes[iP1]);
  135. }
  136. else if (this->IsFar(iP1))
  137. {
  138. ComputeTime(iP1);
  139. this->mTrials[iP1] = this->mHeap.Insert(iP1, this->mTimes[iP1]);
  140. }
  141. size_t iMXB = i - mXBound;
  142. if (this->IsTrial(iMXB))
  143. {
  144. ComputeTime(iMXB);
  145. this->mHeap.Update(this->mTrials[iMXB], this->mTimes[iMXB]);
  146. }
  147. else if (this->IsFar(iMXB))
  148. {
  149. ComputeTime(iMXB);
  150. this->mTrials[iMXB] = this->mHeap.Insert(iMXB, this->mTimes[iMXB]);
  151. }
  152. size_t iPXB = i + mXBound;
  153. if (this->IsTrial(iPXB))
  154. {
  155. ComputeTime(iPXB);
  156. this->mHeap.Update(this->mTrials[iPXB], this->mTimes[iPXB]);
  157. }
  158. else if (this->IsFar(iPXB))
  159. {
  160. ComputeTime(iPXB);
  161. this->mTrials[iPXB] = this->mHeap.Insert(iPXB, this->mTimes[iPXB]);
  162. }
  163. size_t iMXYB = i - mXYBound;
  164. if (this->IsTrial(iMXYB))
  165. {
  166. ComputeTime(iMXYB);
  167. this->mHeap.Update(this->mTrials[iMXYB], this->mTimes[iMXYB]);
  168. }
  169. else if (this->IsFar(iMXYB))
  170. {
  171. ComputeTime(iMXYB);
  172. this->mTrials[iMXYB] = this->mHeap.Insert(iMXYB, this->mTimes[iMXYB]);
  173. }
  174. size_t iPXYB = i + mXYBound;
  175. if (this->IsTrial(iPXYB))
  176. {
  177. ComputeTime(iPXYB);
  178. this->mHeap.Update(this->mTrials[iPXYB], this->mTimes[iPXYB]);
  179. }
  180. else if (this->IsFar(iPXYB))
  181. {
  182. ComputeTime(iPXYB);
  183. this->mTrials[iPXYB] = this->mHeap.Insert(iPXYB, this->mTimes[iPXYB]);
  184. }
  185. }
  186. protected:
  187. // Called by the constructors.
  188. void Initialize(size_t xBound, size_t yBound, size_t zBound,
  189. Real xSpacing, Real ySpacing, Real zSpacing)
  190. {
  191. mXBound = xBound;
  192. mYBound = yBound;
  193. mZBound = zBound;
  194. mXYBound = xBound * yBound;
  195. mXBoundM1 = mXBound - 1;
  196. mYBoundM1 = mYBound - 1;
  197. mZBoundM1 = mZBound - 1;
  198. mXSpacing = xSpacing;
  199. mYSpacing = ySpacing;
  200. mZSpacing = zSpacing;
  201. mInvXSpacing = (Real)1 / xSpacing;
  202. mInvYSpacing = (Real)1 / ySpacing;
  203. mInvZSpacing = (Real)1 / zSpacing;
  204. // Boundary pixels are marked as zero speed to allow us to avoid
  205. // having to process the boundary pixels separately during the
  206. // iteration.
  207. size_t x, y, z, i;
  208. // vertex (0,0,0)
  209. i = Index(0, 0, 0);
  210. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  211. this->mTimes[i] = -std::numeric_limits<Real>::max();
  212. // vertex (xmax,0,0)
  213. i = Index(mXBoundM1, 0, 0);
  214. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  215. this->mTimes[i] = -std::numeric_limits<Real>::max();
  216. // vertex (0,ymax,0)
  217. i = Index(0, mYBoundM1, 0);
  218. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  219. this->mTimes[i] = -std::numeric_limits<Real>::max();
  220. // vertex (xmax,ymax,0)
  221. i = Index(mXBoundM1, mYBoundM1, 0);
  222. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  223. this->mTimes[i] = -std::numeric_limits<Real>::max();
  224. // vertex (0,0,zmax)
  225. i = Index(0, 0, mZBoundM1);
  226. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  227. this->mTimes[i] = -std::numeric_limits<Real>::max();
  228. // vertex (xmax,0,zmax)
  229. i = Index(mXBoundM1, 0, mZBoundM1);
  230. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  231. this->mTimes[i] = -std::numeric_limits<Real>::max();
  232. // vertex (0,ymax,zmax)
  233. i = Index(0, mYBoundM1, mZBoundM1);
  234. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  235. this->mTimes[i] = -std::numeric_limits<Real>::max();
  236. // vertex (xmax,ymax,zmax)
  237. i = Index(mXBoundM1, mYBoundM1, mZBoundM1);
  238. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  239. this->mTimes[i] = -std::numeric_limits<Real>::max();
  240. // edges (x,0,0) and (x,ymax,0)
  241. for (x = 0; x < mXBound; ++x)
  242. {
  243. i = Index(x, 0, 0);
  244. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  245. this->mTimes[i] = -std::numeric_limits<Real>::max();
  246. i = Index(x, mYBoundM1, 0);
  247. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  248. this->mTimes[i] = -std::numeric_limits<Real>::max();
  249. }
  250. // edges (0,y,0) and (xmax,y,0)
  251. for (y = 0; y < mYBound; ++y)
  252. {
  253. i = Index(0, y, 0);
  254. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  255. this->mTimes[i] = -std::numeric_limits<Real>::max();
  256. i = Index(mXBoundM1, y, 0);
  257. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  258. this->mTimes[i] = -std::numeric_limits<Real>::max();
  259. }
  260. // edges (x,0,zmax) and (x,ymax,zmax)
  261. for (x = 0; x < mXBound; ++x)
  262. {
  263. i = Index(x, 0, mZBoundM1);
  264. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  265. this->mTimes[i] = -std::numeric_limits<Real>::max();
  266. i = Index(x, mYBoundM1, mZBoundM1);
  267. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  268. this->mTimes[i] = -std::numeric_limits<Real>::max();
  269. }
  270. // edges (0,y,zmax) and (xmax,y,zmax)
  271. for (y = 0; y < mYBound; ++y)
  272. {
  273. i = Index(0, y, mZBoundM1);
  274. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  275. this->mTimes[i] = -std::numeric_limits<Real>::max();
  276. i = Index(mXBoundM1, y, mZBoundM1);
  277. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  278. this->mTimes[i] = -std::numeric_limits<Real>::max();
  279. }
  280. // edges (0,0,z) and (xmax,0,z)
  281. for (z = 0; z < mZBound; ++z)
  282. {
  283. i = Index(0, 0, z);
  284. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  285. this->mTimes[i] = -std::numeric_limits<Real>::max();
  286. i = Index(mXBoundM1, 0, z);
  287. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  288. this->mTimes[i] = -std::numeric_limits<Real>::max();
  289. }
  290. // edges (0,ymax,z) and (xmax,ymax,z)
  291. for (z = 0; z < mZBound; ++z)
  292. {
  293. i = Index(0, mYBoundM1, z);
  294. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  295. this->mTimes[i] = -std::numeric_limits<Real>::max();
  296. i = Index(mXBoundM1, mYBoundM1, z);
  297. this->mInvSpeeds[i] = std::numeric_limits<Real>::max();
  298. this->mTimes[i] = -std::numeric_limits<Real>::max();
  299. }
  300. // Compute the first batch of trial pixels. These are pixels a grid
  301. // distance of one away from the seed pixels.
  302. for (z = 1; z < mZBoundM1; ++z)
  303. {
  304. for (y = 1; y < mYBoundM1; ++y)
  305. {
  306. for (x = 1; x < mXBoundM1; ++x)
  307. {
  308. i = Index(x, y, z);
  309. if (this->IsFar(i))
  310. {
  311. if ((this->IsValid(i - 1) && !this->IsTrial(i - 1))
  312. || (this->IsValid(i + 1) && !this->IsTrial(i + 1))
  313. || (this->IsValid(i - mXBound) && !this->IsTrial(i - mXBound))
  314. || (this->IsValid(i + mXBound) && !this->IsTrial(i + mXBound))
  315. || (this->IsValid(i - mXYBound) && !this->IsTrial(i - mXYBound))
  316. || (this->IsValid(i + mXYBound) && !this->IsTrial(i + mXYBound)))
  317. {
  318. ComputeTime(i);
  319. this->mTrials[i] = this->mHeap.Insert(i, this->mTimes[i]);
  320. }
  321. }
  322. }
  323. }
  324. }
  325. }
  326. // Called by Iterate().
  327. void ComputeTime(size_t i)
  328. {
  329. bool hasXTerm;
  330. Real xConst;
  331. if (this->IsValid(i - 1))
  332. {
  333. hasXTerm = true;
  334. xConst = this->mTimes[i - 1];
  335. if (this->IsValid(i + 1))
  336. {
  337. if (this->mTimes[i + 1] < xConst)
  338. {
  339. xConst = this->mTimes[i + 1];
  340. }
  341. }
  342. }
  343. else if (this->IsValid(i + 1))
  344. {
  345. hasXTerm = true;
  346. xConst = this->mTimes[i + 1];
  347. }
  348. else
  349. {
  350. hasXTerm = false;
  351. xConst = (Real)0;
  352. }
  353. bool hasYTerm;
  354. Real yConst;
  355. if (this->IsValid(i - mXBound))
  356. {
  357. hasYTerm = true;
  358. yConst = this->mTimes[i - mXBound];
  359. if (this->IsValid(i + mXBound))
  360. {
  361. if (this->mTimes[i + mXBound] < yConst)
  362. {
  363. yConst = this->mTimes[i + mXBound];
  364. }
  365. }
  366. }
  367. else if (this->IsValid(i + mXBound))
  368. {
  369. hasYTerm = true;
  370. yConst = this->mTimes[i + mXBound];
  371. }
  372. else
  373. {
  374. hasYTerm = false;
  375. yConst = (Real)0;
  376. }
  377. bool hasZTerm;
  378. Real zConst;
  379. if (this->IsValid(i - mXYBound))
  380. {
  381. hasZTerm = true;
  382. zConst = this->mTimes[i - mXYBound];
  383. if (this->IsValid(i + mXYBound))
  384. {
  385. if (this->mTimes[i + mXYBound] < zConst)
  386. {
  387. zConst = this->mTimes[i + mXYBound];
  388. }
  389. }
  390. }
  391. else if (this->IsValid(i + mXYBound))
  392. {
  393. hasZTerm = true;
  394. zConst = this->mTimes[i + mXYBound];
  395. }
  396. else
  397. {
  398. hasZTerm = false;
  399. zConst = (Real)0;
  400. }
  401. Real sum, diff, discr;
  402. if (hasXTerm)
  403. {
  404. if (hasYTerm)
  405. {
  406. if (hasZTerm)
  407. {
  408. // xyz
  409. sum = xConst + yConst + zConst;
  410. discr = (Real)3 * this->mInvSpeeds[i] * this->mInvSpeeds[i];
  411. diff = xConst - yConst;
  412. discr -= diff * diff;
  413. diff = xConst - zConst;
  414. discr -= diff * diff;
  415. diff = yConst - zConst;
  416. discr -= diff * diff;
  417. if (discr >= (Real)0)
  418. {
  419. // The quadratic equation has a real-valued
  420. // solution. Choose the largest positive root for
  421. // the crossing time.
  422. this->mTimes[i] = (sum + std::sqrt(discr)) / (Real)3;
  423. }
  424. else
  425. {
  426. // The quadratic equation does not have a
  427. // real-valued solution. This can happen when the
  428. // speed is so large that the time gradient has
  429. // very small length, which means that the time
  430. // has not changed significantly from the
  431. // neighbors to the current pixel. Just choose
  432. // the maximum time of the neighbors. (Is there a
  433. // better choice?)
  434. this->mTimes[i] = xConst;
  435. if (yConst > this->mTimes[i])
  436. {
  437. this->mTimes[i] = yConst;
  438. }
  439. if (zConst > this->mTimes[i])
  440. {
  441. this->mTimes[i] = zConst;
  442. }
  443. }
  444. }
  445. else
  446. {
  447. // xy
  448. sum = xConst + yConst;
  449. diff = xConst - yConst;
  450. discr = (Real)2 * this->mInvSpeeds[i] * this->mInvSpeeds[i] - diff * diff;
  451. if (discr >= (Real)0)
  452. {
  453. // The quadratic equation has a real-valued
  454. // solution. Choose the largest positive root for
  455. // the crossing time.
  456. this->mTimes[i] = (Real)0.5 * (sum + std::sqrt(discr));
  457. }
  458. else
  459. {
  460. // The quadratic equation does not have a
  461. // real-valued solution. This can happen when the
  462. // speed is so large that the time gradient has
  463. // very small length, which means that the time
  464. // has not changed significantly from the
  465. // neighbors to the current pixel. Just choose
  466. // the maximum time of the neighbors. (Is there a
  467. // better choice?)
  468. this->mTimes[i] = (diff >= (Real)0 ? xConst : yConst);
  469. }
  470. }
  471. }
  472. else
  473. {
  474. if (hasZTerm)
  475. {
  476. // xz
  477. sum = xConst + zConst;
  478. diff = xConst - zConst;
  479. discr = (Real)2 * this->mInvSpeeds[i] * this->mInvSpeeds[i] - diff * diff;
  480. if (discr >= (Real)0)
  481. {
  482. // The quadratic equation has a real-valued
  483. // solution. Choose the largest positive root for
  484. // the crossing time.
  485. this->mTimes[i] = (Real)0.5 * (sum + std::sqrt(discr));
  486. }
  487. else
  488. {
  489. // The quadratic equation does not have a
  490. // real-valued solution. This can happen when the
  491. // speed is so large that the time gradient has
  492. // very small length, which means that the time
  493. // has not changed significantly from the
  494. // neighbors to the current pixel. Just choose
  495. // the maximum time of the neighbors. (Is there a
  496. // better choice?)
  497. this->mTimes[i] = (diff >= (Real)0 ? xConst : zConst);
  498. }
  499. }
  500. else
  501. {
  502. // x
  503. this->mTimes[i] = this->mInvSpeeds[i] + xConst;
  504. }
  505. }
  506. }
  507. else
  508. {
  509. if (hasYTerm)
  510. {
  511. if (hasZTerm)
  512. {
  513. // yz
  514. sum = yConst + zConst;
  515. diff = yConst - zConst;
  516. discr = (Real)2 * this->mInvSpeeds[i] * this->mInvSpeeds[i] - diff * diff;
  517. if (discr >= (Real)0)
  518. {
  519. // The quadratic equation has a real-valued
  520. // solution. Choose the largest positive root for
  521. // the crossing time.
  522. this->mTimes[i] = (Real)0.5 * (sum + std::sqrt(discr));
  523. }
  524. else
  525. {
  526. // The quadratic equation does not have a
  527. // real-valued solution. This can happen when the
  528. // speed is so large that the time gradient has
  529. // very small length, which means that the time
  530. // has not changed significantly from the
  531. // neighbors to the current pixel. Just choose
  532. // the maximum time of the neighbors. (Is there a
  533. // better choice?)
  534. this->mTimes[i] = (diff >= (Real)0 ? yConst : zConst);
  535. }
  536. }
  537. else
  538. {
  539. // y
  540. this->mTimes[i] = this->mInvSpeeds[i] + yConst;
  541. }
  542. }
  543. else
  544. {
  545. if (hasZTerm)
  546. {
  547. // z
  548. this->mTimes[i] = this->mInvSpeeds[i] + zConst;
  549. }
  550. else
  551. {
  552. // Assert: The pixel must have at least one valid
  553. // neighbor.
  554. }
  555. }
  556. }
  557. }
  558. size_t mXBound, mYBound, mZBound, mXYBound;
  559. size_t mXBoundM1, mYBoundM1, mZBoundM1;
  560. Real mXSpacing, mYSpacing, mZSpacing;
  561. Real mInvXSpacing, mInvYSpacing, mInvZSpacing;
  562. };
  563. }