Image2.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  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/Logger.h>
  9. #include <Mathematics/Image.h>
  10. #include <array>
  11. #include <string>
  12. //#define GTE_THROW_ON_IMAGE2_ERRORS
  13. namespace WwiseGTE
  14. {
  15. template <typename PixelType>
  16. class Image2 : public Image<PixelType>
  17. {
  18. public:
  19. // Construction and destruction. The last constructor must have
  20. // positive dimensions; otherwise, the image is empty.
  21. virtual ~Image2()
  22. {
  23. }
  24. Image2()
  25. {
  26. }
  27. Image2(int dimension0, int dimension1)
  28. :
  29. Image<PixelType>(std::vector<int>{ dimension0, dimension1 })
  30. {
  31. }
  32. // Support for copy semantics.
  33. Image2(Image2 const& image)
  34. :
  35. Image<PixelType>(image)
  36. {
  37. }
  38. Image2& operator=(Image2 const& image)
  39. {
  40. Image<PixelType>::operator=(image);
  41. return *this;
  42. }
  43. // Support for move semantics.
  44. Image2(Image2&& image)
  45. {
  46. *this = std::move(image);
  47. }
  48. Image2& operator= (Image2&& image)
  49. {
  50. Image<PixelType>::operator=(image);
  51. return *this;
  52. }
  53. // Support for changing the image dimensions. All pixel data is lost
  54. // by this operation.
  55. void Reconstruct(int dimension0, int dimension1)
  56. {
  57. Image<PixelType>::Reconstruct(std::vector<int>{ dimension0, dimension1 });
  58. }
  59. // Conversion between 1-dimensional indices and 2-dimensional
  60. // coordinates.
  61. inline size_t GetIndex(int x, int y) const
  62. {
  63. #if defined(GTE_THROW_ON_IMAGE2_ERRORS)
  64. if (0 <= x && x < this->mDimensions[0]
  65. && 0 <= y && y < this->mDimensions[1])
  66. {
  67. return static_cast<size_t>(x) +
  68. static_cast<size_t>(this->mDimensions[0]) * static_cast<size_t>(y);
  69. }
  70. else
  71. {
  72. LogError(
  73. "Invalid coordinates (" + std::to_string(x) + "," +
  74. std::to_string(y) + ").");
  75. }
  76. #else
  77. return static_cast<size_t>(x) +
  78. static_cast<size_t>(this->mDimensions[0]) * static_cast<size_t>(y);
  79. #endif
  80. }
  81. inline size_t GetIndex(std::array<int, 2> const& coord) const
  82. {
  83. #if defined(GTE_THROW_ON_IMAGE2_ERRORS)
  84. if (0 <= coord[0] && coord[0] < this->mDimensions[0]
  85. && 0 <= coord[1] && coord[1] < this->mDimensions[1])
  86. {
  87. return static_cast<size_t>(coord[0]) +
  88. static_cast<size_t>(this->mDimensions[0]) * static_cast<size_t>(coord[1]);
  89. }
  90. else
  91. {
  92. LogError(
  93. "Invalid coordinates (" + std::to_string(coord[0]) + "," +
  94. std::to_string(coord[1]) + ").");
  95. }
  96. #else
  97. return static_cast<size_t>(coord[0]) +
  98. static_cast<size_t>(this->mDimensions[0]) * static_cast<size_t>(coord[1]);
  99. #endif
  100. }
  101. inline void GetCoordinates(size_t index, int& x, int& y) const
  102. {
  103. #if defined(GTE_THROW_ON_IMAGE2_ERRORS)
  104. if (index < this->mPixels.size())
  105. {
  106. x = static_cast<int>(index % this->mDimensions[0]);
  107. y = static_cast<int>(index / this->mDimensions[0]);
  108. }
  109. else
  110. {
  111. LogError(
  112. "Invalid index " + std::to_string(index) + ".");
  113. }
  114. #else
  115. x = static_cast<int>(index % this->mDimensions[0]);
  116. y = static_cast<int>(index / this->mDimensions[0]);
  117. #endif
  118. }
  119. inline std::array<int, 2> GetCoordinates(size_t index) const
  120. {
  121. std::array<int, 2> coord;
  122. #if defined(GTE_THROW_ON_IMAGE2_ERRORS)
  123. if (index < this->mPixels.size())
  124. {
  125. coord[0] = static_cast<int>(index % this->mDimensions[0]);
  126. coord[1] = static_cast<int>(index / this->mDimensions[0]);
  127. return coord;
  128. }
  129. else
  130. {
  131. LogError(
  132. "Invalid index " + std::to_string(index) + ".");
  133. }
  134. #else
  135. coord[0] = static_cast<int>(index % this->mDimensions[0]);
  136. coord[1] = static_cast<int>(index / this->mDimensions[0]);
  137. return coord;
  138. #endif
  139. }
  140. // Access the data as a 2-dimensional array. The operator() functions
  141. // test for valid (x,y) when iterator checking is enabled and throw
  142. // on invalid (x,y). The Get() functions test for valid (x,y) and
  143. // clamp when invalid; these functions cannot fail.
  144. inline PixelType& operator() (int x, int y)
  145. {
  146. #if defined(GTE_THROW_ON_IMAGE2_ERRORS)
  147. if (0 <= x && x < this->mDimensions[0]
  148. && 0 <= y && y < this->mDimensions[1])
  149. {
  150. return this->mPixels[x + this->mDimensions[0] * y];
  151. }
  152. else
  153. {
  154. LogError(
  155. "Invalid coordinates (" + std::to_string(x) + "," +
  156. std::to_string(y) + ").");
  157. }
  158. #else
  159. return this->mPixels[x + this->mDimensions[0] * y];
  160. #endif
  161. }
  162. inline PixelType const& operator() (int x, int y) const
  163. {
  164. #if defined(GTE_THROW_ON_IMAGE2_ERRORS)
  165. if (0 <= x && x < this->mDimensions[0]
  166. && 0 <= y && y < this->mDimensions[1])
  167. {
  168. return this->mPixels[x + this->mDimensions[0] * y];
  169. }
  170. else
  171. {
  172. LogError(
  173. "Invalid coordinates (" + std::to_string(x) + "," +
  174. std::to_string(y) + ").");
  175. }
  176. #else
  177. return this->mPixels[x + this->mDimensions[0] * y];
  178. #endif
  179. }
  180. inline PixelType& operator() (std::array<int, 2> const& coord)
  181. {
  182. #if defined(GTE_THROW_ON_IMAGE2_ERRORS)
  183. if (0 <= coord[0] && coord[0] < this->mDimensions[0]
  184. && 0 <= coord[1] && coord[1] < this->mDimensions[1])
  185. {
  186. return this->mPixels[coord[0] + this->mDimensions[0] * coord[1]];
  187. }
  188. else
  189. {
  190. LogError(
  191. "Invalid coordinates (" + std::to_string(coord[0]) + "," +
  192. std::to_string(coord[1]) + ").");
  193. }
  194. #else
  195. return this->mPixels[coord[0] + this->mDimensions[0] * coord[1]];
  196. #endif
  197. }
  198. inline PixelType const& operator() (std::array<int, 2> const& coord) const
  199. {
  200. #if defined(GTE_THROW_ON_IMAGE2_ERRORS)
  201. if (0 <= coord[0] && coord[0] < this->mDimensions[0]
  202. && 0 <= coord[1] && coord[1] < this->mDimensions[1])
  203. {
  204. return this->mPixels[coord[0] + this->mDimensions[0] * coord[1]];
  205. }
  206. else
  207. {
  208. LogError(
  209. "Invalid coordinates (" + std::to_string(coord[0]) + "," +
  210. std::to_string(coord[1]) + ").");
  211. }
  212. #else
  213. return this->mPixels[coord[0] + this->mDimensions[0] * coord[1]];
  214. #endif
  215. }
  216. inline PixelType& Get(int x, int y)
  217. {
  218. // Clamp to valid (x,y).
  219. if (x < 0)
  220. {
  221. x = 0;
  222. }
  223. else if (x >= this->mDimensions[0])
  224. {
  225. x = this->mDimensions[0] - 1;
  226. }
  227. if (y < 0)
  228. {
  229. y = 0;
  230. }
  231. else if (y >= this->mDimensions[1])
  232. {
  233. y = this->mDimensions[1] - 1;
  234. }
  235. return this->mPixels[x + this->mDimensions[0] * y];
  236. }
  237. inline PixelType const& Get(int x, int y) const
  238. {
  239. // Clamp to valid (x,y).
  240. if (x < 0)
  241. {
  242. x = 0;
  243. }
  244. else if (x >= this->mDimensions[0])
  245. {
  246. x = this->mDimensions[0] - 1;
  247. }
  248. if (y < 0)
  249. {
  250. y = 0;
  251. }
  252. else if (y >= this->mDimensions[1])
  253. {
  254. y = this->mDimensions[1] - 1;
  255. }
  256. return this->mPixels[x + this->mDimensions[0] * y];
  257. }
  258. inline PixelType& Get(std::array<int, 2> coord)
  259. {
  260. // Clamp to valid (x,y).
  261. for (int i = 0; i < 2; ++i)
  262. {
  263. if (coord[i] < 0)
  264. {
  265. coord[i] = 0;
  266. }
  267. else if (coord[i] >= this->mDimensions[i])
  268. {
  269. coord[i] = this->mDimensions[i] - 1;
  270. }
  271. }
  272. return this->mPixels[coord[0] + this->mDimensions[0] * coord[1]];
  273. }
  274. inline PixelType const& Get(std::array<int, 2> coord) const
  275. {
  276. // Clamp to valid (x,y).
  277. for (int i = 0; i < 2; ++i)
  278. {
  279. if (coord[i] < 0)
  280. {
  281. coord[i] = 0;
  282. }
  283. else if (coord[i] >= this->mDimensions[i])
  284. {
  285. coord[i] = this->mDimensions[i] - 1;
  286. }
  287. }
  288. return this->mPixels[coord[0] + this->mDimensions[0] * coord[1]];
  289. }
  290. // In the following discussion, u and v are in {-1,1}. Given a pixel
  291. // (x,y), the 4-connected neighbors have relative offsets (u,0) and
  292. // (0,v). The 8-connected neighbors include the 4-connected neighbors
  293. // and have additional relative offsets (u,v). The corner neighbors
  294. // have relative offsets (0,0), (1,0), (0,1), and (1,1) in that order.
  295. // The full neighborhood is the set of 3x3 pixels centered at (x,y).
  296. // The neighborhoods can be accessed as 1-dimensional indices using
  297. // these functions. The first four functions provide 1-dimensional
  298. // indices relative to any pixel location; these depend only on the
  299. // image dimensions. The last four functions provide 1-dimensional
  300. // indices for the actual pixels in the neighborhood; no clamping is
  301. // used when (x,y) is on the boundary.
  302. void GetNeighborhood(std::array<int, 4>& nbr) const
  303. {
  304. int dim0 = this->mDimensions[0];
  305. nbr[0] = -1; // (x-1,y)
  306. nbr[1] = +1; // (x+1,y)
  307. nbr[2] = -dim0; // (x,y-1)
  308. nbr[3] = +dim0; // (x,y+1)
  309. }
  310. void GetNeighborhood(std::array<int, 8>& nbr) const
  311. {
  312. int dim0 = this->mDimensions[0];
  313. nbr[0] = -1; // (x-1,y)
  314. nbr[1] = +1; // (x+1,y)
  315. nbr[2] = -dim0; // (x,y-1)
  316. nbr[3] = +dim0; // (x,y+1)
  317. nbr[4] = -1 - dim0; // (x-1,y-1)
  318. nbr[5] = +1 - dim0; // (x+1,y-1)
  319. nbr[6] = -1 + dim0; // (x-1,y+1)
  320. nbr[7] = +1 + dim0; // (x+1,y+1)
  321. }
  322. void GetCorners(std::array<int, 4>& nbr) const
  323. {
  324. int dim0 = this->mDimensions[0];
  325. nbr[0] = 0; // (x,y)
  326. nbr[1] = 1; // (x+1,y)
  327. nbr[2] = dim0; // (x,y+1)
  328. nbr[3] = dim0 + 1; // (x+1,y+1)
  329. }
  330. void GetFull(std::array<int, 9>& nbr) const
  331. {
  332. int dim0 = this->mDimensions[0];
  333. nbr[0] = -1 - dim0; // (x-1,y-1)
  334. nbr[1] = -dim0; // (x,y-1)
  335. nbr[2] = +1 - dim0; // (x+1,y-1)
  336. nbr[3] = -1; // (x-1,y)
  337. nbr[4] = 0; // (x,y)
  338. nbr[5] = +1; // (x+1,y)
  339. nbr[6] = -1 + dim0; // (x-1,y+1)
  340. nbr[7] = +dim0; // (x,y+1)
  341. nbr[8] = +1 + dim0; // (x+1,y+1)
  342. }
  343. void GetNeighborhood(int x, int y, std::array<size_t, 4>& nbr) const
  344. {
  345. size_t index = GetIndex(x, y);
  346. std::array<int, 4> inbr;
  347. GetNeighborhood(inbr);
  348. for (int i = 0; i < 4; ++i)
  349. {
  350. nbr[i] = index + inbr[i];
  351. }
  352. }
  353. void GetNeighborhood(int x, int y, std::array<size_t, 8>& nbr) const
  354. {
  355. size_t index = GetIndex(x, y);
  356. std::array<int, 8> inbr;
  357. GetNeighborhood(inbr);
  358. for (int i = 0; i < 8; ++i)
  359. {
  360. nbr[i] = index + inbr[i];
  361. }
  362. }
  363. void GetCorners(int x, int y, std::array<size_t, 4>& nbr) const
  364. {
  365. size_t index = GetIndex(x, y);
  366. std::array<int, 4> inbr;
  367. GetCorners(inbr);
  368. for (int i = 0; i < 4; ++i)
  369. {
  370. nbr[i] = index + inbr[i];
  371. }
  372. }
  373. void GetFull(int x, int y, std::array<size_t, 9>& nbr) const
  374. {
  375. size_t index = GetIndex(x, y);
  376. std::array<int, 9> inbr;
  377. GetFull(inbr);
  378. for (int i = 0; i < 9; ++i)
  379. {
  380. nbr[i] = index + inbr[i];
  381. }
  382. }
  383. // The neighborhoods can be accessed as 2-tuples using these
  384. // functions. The first four functions provide 2-tuples relative to
  385. // any pixel location; these depend only on the image dimensions. The
  386. // last four functions provide 2-tuples for the actual pixels in the
  387. // neighborhood; no clamping is used when (x,y) is on the boundary.
  388. void GetNeighborhood(std::array<std::array<int, 2>, 4>& nbr) const
  389. {
  390. nbr[0] = { { -1, 0 } };
  391. nbr[1] = { { +1, 0 } };
  392. nbr[2] = { { 0, -1 } };
  393. nbr[3] = { { 0, +1 } };
  394. }
  395. void GetNeighborhood(std::array<std::array<int, 2>, 8>& nbr) const
  396. {
  397. nbr[0] = { { -1, -1 } };
  398. nbr[1] = { { 0, -1 } };
  399. nbr[2] = { { +1, -1 } };
  400. nbr[3] = { { -1, 0 } };
  401. nbr[4] = { { +1, 0 } };
  402. nbr[5] = { { -1, +1 } };
  403. nbr[6] = { { 0, +1 } };
  404. nbr[7] = { { +1, +1 } };
  405. }
  406. void GetCorners(std::array<std::array<int, 2>, 4>& nbr) const
  407. {
  408. nbr[0] = { { 0, 0 } };
  409. nbr[1] = { { 1, 0 } };
  410. nbr[2] = { { 0, 1 } };
  411. nbr[3] = { { 1, 1 } };
  412. }
  413. void GetFull(std::array<std::array<int, 2>, 9>& nbr) const
  414. {
  415. nbr[0] = { { -1, -1 } };
  416. nbr[1] = { { 0, -1 } };
  417. nbr[2] = { { +1, -1 } };
  418. nbr[3] = { { -1, 0 } };
  419. nbr[4] = { { 0, 0 } };
  420. nbr[5] = { { +1, 0 } };
  421. nbr[6] = { { -1, +1 } };
  422. nbr[7] = { { 0, +1 } };
  423. nbr[8] = { { +1, +1 } };
  424. }
  425. void GetNeighborhood(int x, int y, std::array<std::array<size_t, 2>, 4>& nbr) const
  426. {
  427. std::array<std::array<int, 2>, 4> inbr;
  428. GetNeighborhood(inbr);
  429. for (int i = 0; i < 4; ++i)
  430. {
  431. nbr[i][0] = static_cast<size_t>(x) + inbr[i][0];
  432. nbr[i][1] = static_cast<size_t>(y) + inbr[i][1];
  433. }
  434. }
  435. void GetNeighborhood(int x, int y, std::array<std::array<size_t, 2>, 8>& nbr) const
  436. {
  437. std::array<std::array<int, 2>, 8> inbr;
  438. GetNeighborhood(inbr);
  439. for (int i = 0; i < 8; ++i)
  440. {
  441. nbr[i][0] = static_cast<size_t>(x) + inbr[i][0];
  442. nbr[i][1] = static_cast<size_t>(y) + inbr[i][1];
  443. }
  444. }
  445. void GetCorners(int x, int y, std::array<std::array<size_t, 2>, 4>& nbr) const
  446. {
  447. std::array<std::array<int, 2>, 4> inbr;
  448. GetCorners(inbr);
  449. for (int i = 0; i < 4; ++i)
  450. {
  451. nbr[i][0] = static_cast<size_t>(x) + inbr[i][0];
  452. nbr[i][1] = static_cast<size_t>(y) + inbr[i][1];
  453. }
  454. }
  455. void GetFull(int x, int y, std::array<std::array<size_t, 2>, 9>& nbr) const
  456. {
  457. std::array<std::array<int, 2>, 9> inbr;
  458. GetFull(inbr);
  459. for (int i = 0; i < 9; ++i)
  460. {
  461. nbr[i][0] = static_cast<size_t>(x) + inbr[i][0];
  462. nbr[i][1] = static_cast<size_t>(y) + inbr[i][1];
  463. }
  464. }
  465. };
  466. }