ImageUtility3.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  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/Image3.h>
  9. #include <functional>
  10. // Image utilities for Image3<int> objects. TODO: Extend this to a template
  11. // class to allow the pixel type to be int*_t and uint*_t for * in
  12. // {8,16,32,64}.
  13. //
  14. // All but the Draw* functions are operations on binary images. Let the image
  15. // have d0 columns, d1 rows and d2 slices. The input image must have zeros on
  16. // its boundaries x = 0, x = d0-1, y = 0, y = d1-1, z = 0 and z = d2-1. The
  17. // 0-valued voxels are considered to be background. The 1-valued voxels are
  18. // considered to be foreground. In some of the operations, to save memory and
  19. // time the input image is modified by the algorithms. If you need to
  20. // preserve the input image, make a copy of it before calling these
  21. // functions.
  22. namespace WwiseGTE
  23. {
  24. class ImageUtility3
  25. {
  26. public:
  27. // Compute the 6-connected components of a binary image. The input
  28. // image is modified to avoid the cost of making a copy. On output,
  29. // the image values are the labels for the components. The array
  30. // components[k], k >= 1, contains the indices for the k-th component.
  31. static void GetComponents6(Image3<int>& image,
  32. std::vector<std::vector<size_t>>& components)
  33. {
  34. std::array<int, 6> neighbors;
  35. image.GetNeighborhood(neighbors);
  36. GetComponents(6, &neighbors[0], image, components);
  37. }
  38. // Compute the 18-connected components of a binary image. The input
  39. // image is modified to avoid the cost of making a copy. On output,
  40. // the image values are the labels for the components. The array
  41. // components[k], k >= 1, contains the indices for the k-th component.
  42. static void GetComponents18(Image3<int>& image,
  43. std::vector<std::vector<size_t>>& components)
  44. {
  45. std::array<int, 18> neighbors;
  46. image.GetNeighborhood(neighbors);
  47. GetComponents(18, &neighbors[0], image, components);
  48. }
  49. // Compute the 26-connected components of a binary image. The input
  50. // image is modified to avoid the cost of making a copy. On output,
  51. // the image values are the labels for the components. The array
  52. // components[k], k >= 1, contains the indices for the k-th component.
  53. static void GetComponents26(Image3<int>& image,
  54. std::vector<std::vector<size_t>>& components)
  55. {
  56. std::array<int, 26> neighbors;
  57. image.GetNeighborhood(neighbors);
  58. GetComponents(26, &neighbors[0], image, components);
  59. }
  60. // Dilate the image using a structuring element that contains the
  61. // 6-connected neighbors.
  62. static void Dilate6(Image3<int> const& inImage, Image3<int>& outImage)
  63. {
  64. std::array<std::array<int, 3>, 6> neighbors;
  65. inImage.GetNeighborhood(neighbors);
  66. Dilate(6, &neighbors[0], inImage, outImage);
  67. }
  68. // Dilate the image using a structuring element that contains the
  69. // 18-connected neighbors.
  70. static void Dilate18(Image3<int> const& inImage, Image3<int>& outImage)
  71. {
  72. std::array<std::array<int, 3>, 18> neighbors;
  73. inImage.GetNeighborhood(neighbors);
  74. Dilate(18, &neighbors[0], inImage, outImage);
  75. }
  76. // Dilate the image using a structuring element that contains the
  77. // 26-connected neighbors.
  78. static void Dilate26(Image3<int> const& inImage, Image3<int>& outImage)
  79. {
  80. std::array<std::array<int, 3>, 26> neighbors;
  81. inImage.GetNeighborhood(neighbors);
  82. Dilate(26, &neighbors[0], inImage, outImage);
  83. }
  84. // Compute coordinate-directional convex set. For a given coordinate
  85. // direction (x, y, or z), identify the first and last 1-valued voxels
  86. // on a segment of voxels in that direction. All voxels from first to
  87. // last are set to 1. This is done for all segments in each of the
  88. // coordinate directions.
  89. static void ComputeCDConvex(Image3<int>& image)
  90. {
  91. int const dim0 = image.GetDimension(0);
  92. int const dim1 = image.GetDimension(1);
  93. int const dim2 = image.GetDimension(2);
  94. Image3<int> temp = image;
  95. int i0, i1, i2;
  96. for (i1 = 0; i1 < dim1; ++i1)
  97. {
  98. for (i0 = 0; i0 < dim0; ++i0)
  99. {
  100. int i2min;
  101. for (i2min = 0; i2min < dim2; ++i2min)
  102. {
  103. if ((temp(i0, i1, i2min) & 1) == 0)
  104. {
  105. temp(i0, i1, i2min) |= 2;
  106. }
  107. else
  108. {
  109. break;
  110. }
  111. }
  112. if (i2min < dim2)
  113. {
  114. int i2max;
  115. for (i2max = dim2 - 1; i2max >= i2min; --i2max)
  116. {
  117. if ((temp(i0, i1, i2max) & 1) == 0)
  118. {
  119. temp(i0, i1, i2max) |= 2;
  120. }
  121. else
  122. {
  123. break;
  124. }
  125. }
  126. }
  127. }
  128. }
  129. for (i2 = 0; i2 < dim2; ++i2)
  130. {
  131. for (i0 = 0; i0 < dim0; ++i0)
  132. {
  133. int i1min;
  134. for (i1min = 0; i1min < dim1; ++i1min)
  135. {
  136. if ((temp(i0, i1min, i2) & 1) == 0)
  137. {
  138. temp(i0, i1min, i2) |= 2;
  139. }
  140. else
  141. {
  142. break;
  143. }
  144. }
  145. if (i1min < dim1)
  146. {
  147. int i1max;
  148. for (i1max = dim1 - 1; i1max >= i1min; --i1max)
  149. {
  150. if ((temp(i0, i1max, i2) & 1) == 0)
  151. {
  152. temp(i0, i1max, i2) |= 2;
  153. }
  154. else
  155. {
  156. break;
  157. }
  158. }
  159. }
  160. }
  161. }
  162. for (i2 = 0; i2 < dim2; ++i2)
  163. {
  164. for (i1 = 0; i1 < dim1; ++i1)
  165. {
  166. int i0min;
  167. for (i0min = 0; i0min < dim0; ++i0min)
  168. {
  169. if ((temp(i0min, i1, i2) & 1) == 0)
  170. {
  171. temp(i0min, i1, i2) |= 2;
  172. }
  173. else
  174. {
  175. break;
  176. }
  177. }
  178. if (i0min < dim0)
  179. {
  180. int i0max;
  181. for (i0max = dim0 - 1; i0max >= i0min; --i0max)
  182. {
  183. if ((temp(i0max, i1, i2) & 1) == 0)
  184. {
  185. temp(i0max, i1, i2) |= 2;
  186. }
  187. else
  188. {
  189. break;
  190. }
  191. }
  192. }
  193. }
  194. }
  195. for (size_t i = 0; i < image.GetNumPixels(); ++i)
  196. {
  197. image[i] = (temp[i] & 2 ? 0 : 1);
  198. }
  199. }
  200. // Use a depth-first search for filling a 6-connected region. This is
  201. // nonrecursive, simulated by using a heap-allocated "stack". The input
  202. // (x,y,z) is the seed point that starts the fill.
  203. template <typename PixelType>
  204. static void FloodFill6(Image3<PixelType> & image, int x, int y, int z,
  205. PixelType foreColor, PixelType backColor)
  206. {
  207. // Test for a valid seed.
  208. int const dim0 = image.GetDimension(0);
  209. int const dim1 = image.GetDimension(1);
  210. int const dim2 = image.GetDimension(2);
  211. if (x < 0 || x >= dim0 || y < 0 || y >= dim1 || z < 0 || z >= dim2)
  212. {
  213. // The seed point is outside the image domain, so there is
  214. // nothing to fill.
  215. return;
  216. }
  217. // Allocate the maximum amount of space needed for the stack. An
  218. // empty stack has top == -1.
  219. size_t const numVoxels = image.GetNumPixels();
  220. std::vector<int> xStack(numVoxels), yStack(numVoxels), zStack(numVoxels);
  221. // Push seed point onto stack if it has the background color. All
  222. // points pushed onto stack have background color backColor.
  223. int top = 0;
  224. xStack[top] = x;
  225. yStack[top] = y;
  226. zStack[top] = z;
  227. while (top >= 0) // stack is not empty
  228. {
  229. // Read top of stack. Do not pop since we need to return to
  230. // this top value later to restart the fill in a different
  231. // direction.
  232. x = xStack[top];
  233. y = yStack[top];
  234. z = zStack[top];
  235. // Fill the pixel.
  236. image(x, y, z) = foreColor;
  237. int xp1 = x + 1;
  238. if (xp1 < dim0 && image(xp1, y, z) == backColor)
  239. {
  240. // Push pixel with background color.
  241. ++top;
  242. xStack[top] = xp1;
  243. yStack[top] = y;
  244. zStack[top] = z;
  245. continue;
  246. }
  247. int xm1 = x - 1;
  248. if (0 <= xm1 && image(xm1, y, z) == backColor)
  249. {
  250. // Push pixel with background color.
  251. ++top;
  252. xStack[top] = xm1;
  253. yStack[top] = y;
  254. zStack[top] = z;
  255. continue;
  256. }
  257. int yp1 = y + 1;
  258. if (yp1 < dim1 && image(x, yp1, z) == backColor)
  259. {
  260. // Push pixel with background color.
  261. ++top;
  262. xStack[top] = x;
  263. yStack[top] = yp1;
  264. zStack[top] = z;
  265. continue;
  266. }
  267. int ym1 = y - 1;
  268. if (0 <= ym1 && image(x, ym1, z) == backColor)
  269. {
  270. // Push pixel with background color.
  271. ++top;
  272. xStack[top] = x;
  273. yStack[top] = ym1;
  274. zStack[top] = z;
  275. continue;
  276. }
  277. int zp1 = z + 1;
  278. if (zp1 < dim2 && image(x, y, zp1) == backColor)
  279. {
  280. // Push pixel with background color.
  281. ++top;
  282. xStack[top] = x;
  283. yStack[top] = y;
  284. zStack[top] = zp1;
  285. continue;
  286. }
  287. int zm1 = z - 1;
  288. if (0 <= zm1 && image(x, y, zm1) == backColor)
  289. {
  290. // Push pixel with background color.
  291. ++top;
  292. xStack[top] = x;
  293. yStack[top] = y;
  294. zStack[top] = zm1;
  295. continue;
  296. }
  297. // Done in all directions, pop and return to search other
  298. // directions for the predecessor.
  299. --top;
  300. }
  301. }
  302. // Visit pixels using Bresenham's line drawing algorithm. The callback
  303. // represents the action you want applied to each voxel as it is visited.
  304. static void DrawLine(int x0, int y0, int z0, int x1, int y1, int z1,
  305. std::function<void(int, int, int)> const& callback)
  306. {
  307. // Starting point of line.
  308. int x = x0, y = y0, z = z0;
  309. // Direction of line.
  310. int dx = x1 - x0, dy = y1 - y0, dz = z1 - z0;
  311. // Increment or decrement depending on direction of line.
  312. int sx = (dx > 0 ? 1 : (dx < 0 ? -1 : 0));
  313. int sy = (dy > 0 ? 1 : (dy < 0 ? -1 : 0));
  314. int sz = (dz > 0 ? 1 : (dz < 0 ? -1 : 0));
  315. // Decision parameters for voxel selection.
  316. if (dx < 0)
  317. {
  318. dx = -dx;
  319. }
  320. if (dy < 0)
  321. {
  322. dy = -dy;
  323. }
  324. if (dz < 0)
  325. {
  326. dz = -dz;
  327. }
  328. int ax = 2 * dx, ay = 2 * dy, az = 2 * dz;
  329. int decX, decY, decZ;
  330. // Determine largest direction component, single-step related
  331. // variable.
  332. int maxValue = dx, var = 0;
  333. if (dy > maxValue)
  334. {
  335. maxValue = dy;
  336. var = 1;
  337. }
  338. if (dz > maxValue)
  339. {
  340. var = 2;
  341. }
  342. // Traverse Bresenham line.
  343. switch (var)
  344. {
  345. case 0: // Single-step in x-direction.
  346. decY = ay - dx;
  347. decZ = az - dx;
  348. for (/**/; /**/; x += sx, decY += ay, decZ += az)
  349. {
  350. // Process voxel.
  351. callback(x, y, z);
  352. // Take Bresenham step.
  353. if (x == x1)
  354. {
  355. break;
  356. }
  357. if (decY >= 0)
  358. {
  359. decY -= ax;
  360. y += sy;
  361. }
  362. if (decZ >= 0)
  363. {
  364. decZ -= ax;
  365. z += sz;
  366. }
  367. }
  368. break;
  369. case 1: // Single-step in y-direction.
  370. decX = ax - dy;
  371. decZ = az - dy;
  372. for (/**/; /**/; y += sy, decX += ax, decZ += az)
  373. {
  374. // Process voxel.
  375. callback(x, y, z);
  376. // Take Bresenham step.
  377. if (y == y1)
  378. {
  379. break;
  380. }
  381. if (decX >= 0)
  382. {
  383. decX -= ay;
  384. x += sx;
  385. }
  386. if (decZ >= 0)
  387. {
  388. decZ -= ay;
  389. z += sz;
  390. }
  391. }
  392. break;
  393. case 2: // Single-step in z-direction.
  394. decX = ax - dz;
  395. decY = ay - dz;
  396. for (/**/; /**/; z += sz, decX += ax, decY += ay)
  397. {
  398. // Process voxel.
  399. callback(x, y, z);
  400. // Take Bresenham step.
  401. if (z == z1)
  402. {
  403. break;
  404. }
  405. if (decX >= 0)
  406. {
  407. decX -= az;
  408. x += sx;
  409. }
  410. if (decY >= 0)
  411. {
  412. decY -= az;
  413. y += sy;
  414. }
  415. }
  416. break;
  417. }
  418. }
  419. private:
  420. // Dilation using the specified structuring element.
  421. static void Dilate(int numNeighbors, std::array<int, 3> const* delta,
  422. Image3<int> const& inImage, Image3<int> & outImage)
  423. {
  424. int const bound0M1 = inImage.GetDimension(0) - 1;
  425. int const bound1M1 = inImage.GetDimension(1) - 1;
  426. int const bound2M1 = inImage.GetDimension(2) - 1;
  427. for (int i2 = 1; i2 < bound2M1; ++i2)
  428. {
  429. for (int i1 = 1; i1 < bound1M1; ++i1)
  430. {
  431. for (int i0 = 1; i0 < bound0M1; ++i0)
  432. {
  433. if (inImage(i0, i1, i2) == 0)
  434. {
  435. for (int n = 0; n < numNeighbors; ++n)
  436. {
  437. int d0 = delta[n][0];
  438. int d1 = delta[n][1];
  439. int d2 = delta[n][2];
  440. if (inImage(i0 + d0, i1 + d1, i2 + d2) == 1)
  441. {
  442. outImage(i0, i1, i2) = 1;
  443. break;
  444. }
  445. }
  446. }
  447. else
  448. {
  449. outImage(i0, i1, i2) = 1;
  450. }
  451. }
  452. }
  453. }
  454. }
  455. // Connected component labeling using depth-first search.
  456. static void GetComponents(int numNeighbors, int const* delta,
  457. Image3<int> & image, std::vector<std::vector<size_t>> & components)
  458. {
  459. size_t const numVoxels = image.GetNumPixels();
  460. std::vector<int> numElements(numVoxels);
  461. std::vector<size_t> vstack(numVoxels);
  462. size_t i, numComponents = 0;
  463. int label = 2;
  464. for (i = 0; i < numVoxels; ++i)
  465. {
  466. if (image[i] == 1)
  467. {
  468. int top = -1;
  469. vstack[++top] = i;
  470. int& count = numElements[numComponents + 1];
  471. count = 0;
  472. while (top >= 0)
  473. {
  474. size_t v = vstack[top];
  475. image[v] = -1;
  476. int j;
  477. for (j = 0; j < numNeighbors; ++j)
  478. {
  479. size_t adj = v + delta[j];
  480. if (image[adj] == 1)
  481. {
  482. vstack[++top] = adj;
  483. break;
  484. }
  485. }
  486. if (j == numNeighbors)
  487. {
  488. image[v] = label;
  489. ++count;
  490. --top;
  491. }
  492. }
  493. ++numComponents;
  494. ++label;
  495. }
  496. }
  497. if (numComponents > 0)
  498. {
  499. components.resize(numComponents + 1);
  500. for (i = 1; i <= numComponents; ++i)
  501. {
  502. components[i].resize(numElements[i]);
  503. numElements[i] = 0;
  504. }
  505. for (i = 0; i < numVoxels; ++i)
  506. {
  507. int value = image[i];
  508. if (value != 0)
  509. {
  510. // Labels started at 2 to support the depth-first
  511. // search, so they need to be decremented for the
  512. // correct labels.
  513. image[i] = --value;
  514. components[value][numElements[value]] = i;
  515. ++numElements[value];
  516. }
  517. }
  518. }
  519. }
  520. };
  521. }