ccIsolines.h 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080
  1. #ifndef ISOLINES_HEADER
  2. #define ISOLINES_HEADER
  3. /**
  4. * Transcription of FindIsolines.java for C++
  5. *
  6. * Fast implementation of marching squares
  7. *
  8. * AUTHOR: Murphy Stein, Greg Borenstein
  9. * New York University
  10. * CREATED: Jan-Sept 2012
  11. * MODIFIED: Dec 2014 (DGM)
  12. *
  13. * LICENSE: BSD
  14. *
  15. * Copyright (c) 2012 New York University.
  16. * All rights reserved.
  17. *
  18. * Redistribution and use in source and binary forms are permitted
  19. * provided that the above copyright notice and this paragraph are
  20. * duplicated in all such forms and that any documentation,
  21. * advertising materials, and other materials related to such
  22. * distribution and use acknowledge that the software was developed
  23. * by New York Univserity. The name of the
  24. * University may not be used to endorse or promote products derived
  25. * from this software without specific prior written permission.
  26. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  27. * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  28. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  29. *
  30. */
  31. /**
  32. * This is a fast implementation of the marching squares algorithm for finding isolines (lines of equal color) in an image.
  33. *
  34. */
  35. //qCC_db
  36. #include <ccLog.h>
  37. //system
  38. #include <assert.h>
  39. #include <cmath>
  40. #include <vector>
  41. template< typename T > class Isolines
  42. {
  43. protected:
  44. std::vector<double> m_minx;
  45. std::vector<double> m_miny;
  46. std::vector<double> m_maxx;
  47. std::vector<double> m_maxy;
  48. std::vector<int> m_cd;
  49. std::vector<double> m_contourX;
  50. std::vector<double> m_contourY;
  51. std::vector<int> m_contourLength;
  52. std::vector<int> m_contourOrigin;
  53. std::vector<int> m_contourIndexes;
  54. std::vector<bool> m_contourClosed;
  55. int m_w;
  56. int m_h;
  57. int m_numContours;
  58. T m_threshold;
  59. public:
  60. //! Default constructor
  61. Isolines(int w, int h)
  62. : m_w(std::max(0, w))
  63. , m_h(std::max(0, h))
  64. , m_numContours(0)
  65. , m_threshold(0)
  66. {
  67. //DGM: as this is done in the constructor,
  68. //we don't catch the exception (so that the
  69. //caller can properly handle the error!)
  70. //try
  71. {
  72. m_cd.resize(static_cast<size_t>(w)*static_cast<size_t>(h), 0);
  73. }
  74. //catch (const std::bad_alloc&)
  75. //{
  76. // //not enough memory!
  77. //}
  78. }
  79. //! Sets isoline value to trace
  80. inline void setThreshold(T t) { m_threshold = t; }
  81. //! Find isolines
  82. int find(const T* in)
  83. {
  84. //createOnePixelBorder(in, m_threshold + 1); //DGM: modifies 'in', the caller will have to do it itself :(
  85. preCodeImage(in);
  86. return findIsolines(in);
  87. }
  88. //! Returns the number of found contours
  89. inline int getNumContours() const { return m_numContours; }
  90. //! Returns the length of a given contour
  91. inline int getContourLength(int contour) const { return m_contourLength[contour]; }
  92. //! Returns whether a given contour is closed or not
  93. inline bool isContourClosed(int contour) const { return m_contourClosed[contour]; }
  94. //! Returns the given point (x,y) of a given contour
  95. void getContourPoint(int contour, size_t index, double& x, double& y) const
  96. {
  97. assert(static_cast<int>(index) < getContourLength(contour));
  98. x = getContourX(contour, index);
  99. y = getContourY(contour, index);
  100. }
  101. //! Measures the area delineated by a given contour
  102. inline double measureArea(int contour) const { return measureArea(contour, 0, getContourLength(contour)); }
  103. //! Measures the perimeter of a given contour
  104. inline double measurePerimeter(int contour) const { return measurePerimeter(contour, 0, getContourLength(contour)); }
  105. //! Creates a single pixel, 0-valued border around the grid
  106. void createOnePixelBorder(T* in, T borderval) const
  107. {
  108. //rows
  109. {
  110. int shift = (m_h - 1) * m_w;
  111. for (int i = 0; i < m_w; i++)
  112. {
  113. in[i] = in[i + shift] = borderval;
  114. }
  115. }
  116. //columns
  117. {
  118. for (int j = 0; j < m_h; j++)
  119. {
  120. in[j*m_w] = in[(j + 1)*m_w - 1] = borderval;
  121. }
  122. }
  123. }
  124. protected:
  125. //! Computes a code for each group of 4x4 cells
  126. /** The code depends only on whether each of four corners is
  127. above or below threshold.
  128. **/
  129. void preCodeImage(const T* in)
  130. {
  131. for (int x = 0; x < m_w - 1; x++)
  132. {
  133. for (int y = 0; y < m_h - 1; y++)
  134. {
  135. int b0(in[ixy(x + 0, y + 1)] < m_threshold ? 1 : 0); //bottom-left is below threshold
  136. int b1(in[ixy(x + 1, y + 1)] < m_threshold ? 2 : 0); //bottom-right is below threshold
  137. int b2(in[ixy(x + 1, y + 0)] < m_threshold ? 4 : 0); //top-right is below threshold
  138. int b3(in[ixy(x + 0, y + 0)] < m_threshold ? 8 : 0); //top-left is below threshold
  139. m_cd[ixy(x, y)] = b0 | b1 | b2 | b3;
  140. }
  141. }
  142. }
  143. //! 2x2 cell configuration codes
  144. enum ConfigurationCodes
  145. {
  146. CASE0 = 0,
  147. CASE1 = 1,
  148. CASE2 = 2,
  149. CASE3 = 3,
  150. CASE4 = 4,
  151. CASE5 = 5,
  152. CASE6 = 6,
  153. CASE7 = 7,
  154. CASE8 = 8,
  155. CASE9 = 9,
  156. CASE10 = 10,
  157. CASE11 = 11,
  158. CASE12 = 12,
  159. CASE13 = 13,
  160. CASE14 = 14,
  161. CASE15 = 15,
  162. VISITED = 16
  163. };
  164. //! Entry/exit edges
  165. enum Edges
  166. {
  167. NONE = -1,
  168. TOP = 0,
  169. RIGHT = 1,
  170. BOTTOM = 2,
  171. LEFT = 3
  172. };
  173. void endContour(bool closed, bool alternatePath)
  174. {
  175. if (alternatePath)
  176. {
  177. //we have to merge this path with the previous one!
  178. //try //will be taken care of by 'findIsolines'
  179. //{
  180. size_t length = m_contourLength.back();
  181. size_t firstIndex = m_contourOrigin.back();
  182. m_contourLength.pop_back();
  183. m_contourOrigin.pop_back();
  184. m_contourClosed.pop_back();
  185. //backup the alternate part of the contour
  186. std::vector<double> subContourX(length), subContourY(length);
  187. std::vector<int> subContourIndexes(length);
  188. {
  189. for (size_t i = 0; i < length; ++i)
  190. {
  191. subContourX[i] = m_contourX[firstIndex + i];
  192. subContourY[i] = m_contourY[firstIndex + i];
  193. subContourIndexes[i] = m_contourIndexes[firstIndex + i];
  194. }
  195. }
  196. assert(!m_contourLength.empty() && !m_contourOrigin.empty());
  197. size_t length0 = m_contourLength.back();
  198. size_t firstIndex0 = m_contourOrigin.back();
  199. //shift the first part values
  200. {
  201. for (int i = static_cast<int>(length0); i >= 0; --i) //we start by end so as to not overwrite values!
  202. {
  203. m_contourX[firstIndex0 + length + i] = m_contourX[firstIndex0 + i];
  204. m_contourX[firstIndex0 + length + i] = m_contourY[firstIndex0 + i];
  205. m_contourIndexes[firstIndex0 + length + i] = m_contourIndexes[firstIndex0 + i];
  206. }
  207. }
  208. //now copy the second part values
  209. {
  210. for (size_t i = 0; i < length; ++i)
  211. {
  212. m_contourX[firstIndex0 + i] = subContourX[i];
  213. m_contourY[firstIndex0 + i] = subContourY[i];
  214. m_contourIndexes[firstIndex0 + i] = subContourIndexes[i];
  215. }
  216. }
  217. //even though we are merging contours, if we are here it
  218. //means that the contour was not a proper loop!
  219. closed = false;
  220. assert(!m_contourClosed.empty());
  221. m_contourClosed.back() = false;
  222. //}
  223. //catch (const std::bad_alloc&)
  224. //{
  225. // return false;
  226. //}
  227. }
  228. //simply update the closed state (just to be sure)
  229. assert(!m_contourClosed.empty());
  230. assert(!closed || m_contourLength.back() > 2);
  231. m_contourClosed.back() = closed;
  232. //return true;
  233. }
  234. //! Searches image for contours from topleft to bottomright corners
  235. int findIsolines(const T* in)
  236. {
  237. //traversal case
  238. static const int TRAVERSAL[4] = { /*TOP=*/BOTTOM, /*RIGHT=*/LEFT, /*BOTTOM=*/TOP, /*LEFT=*/RIGHT };
  239. //disambiguation cases (CASES 5 and 10)
  240. static const int DISAMBIGUATION_UP [4] = { /*TOP=*/LEFT, /*RIGHT=*/BOTTOM, /*BOTTOM=*/RIGHT, /*LEFT=*/TOP };
  241. static const int DISAMBIGUATION_DOWN[4] = { /*TOP=*/RIGHT, /*RIGHT=*/TOP, /*BOTTOM=*/LEFT, /*LEFT=*/BOTTOM };
  242. m_contourX.clear();
  243. m_contourY.clear();
  244. m_contourLength.clear();
  245. m_contourOrigin.clear();
  246. m_contourIndexes.clear();
  247. m_contourClosed.clear();
  248. try
  249. {
  250. int toEdge = NONE;
  251. int cellIndex = -1;
  252. int x = 0, y = 0;
  253. //mechanism for merging two parts of a non-closed contour
  254. int altToEdge = NONE;
  255. int altStartIndex = 0;
  256. bool alternatePath = false;
  257. const int maxCellIndex = m_w * (m_h - 1); //DGM: last line is only 0!
  258. while (cellIndex < maxCellIndex)
  259. {
  260. //entry edge
  261. int fromEdge = (toEdge == NONE ? NONE : TRAVERSAL[toEdge]);
  262. //exit edge
  263. toEdge = NONE;
  264. //alternative exit edge (when starting a new contour)
  265. if (fromEdge == NONE)
  266. altToEdge = NONE;
  267. int currentCellIndex = -1;
  268. //last isoline is 'finished'
  269. if (fromEdge == NONE)
  270. {
  271. //do we have an alternate path?
  272. if (altToEdge != NONE && !alternatePath)
  273. {
  274. //we know that we are coming from the TOP (case 2 or 13)
  275. fromEdge = TOP;
  276. currentCellIndex = altStartIndex + m_w; //same coumn, next row
  277. alternatePath = true;
  278. //we start a new (temporary) contour
  279. m_contourLength.push_back(0);
  280. m_contourClosed.push_back(false);
  281. m_contourOrigin.push_back(static_cast<int>(m_contourX.size()) + 1);
  282. }
  283. else
  284. {
  285. // we have to look for a new starting point
  286. alternatePath = false;
  287. altToEdge = NONE;
  288. //skip empty cells
  289. while ( ++cellIndex < maxCellIndex
  290. && (m_cd[cellIndex] == CASE0 || m_cd[cellIndex] == CASE15) )
  291. {
  292. }
  293. if (cellIndex == maxCellIndex)
  294. break;
  295. currentCellIndex = cellIndex;
  296. }
  297. x = currentCellIndex % m_w;
  298. y = currentCellIndex / m_w;
  299. }
  300. //we have reached a border (bottom or right)
  301. if (x < 0 || x >= m_w - 1 || y < 0 || y >= m_h - 1)
  302. {
  303. //if an isoline was underway, it won't be closed!
  304. if (fromEdge != NONE)
  305. {
  306. endContour(false, alternatePath);
  307. }
  308. //toEdge = NONE;
  309. continue;
  310. }
  311. else if (fromEdge != NONE)
  312. {
  313. //isoline underway: we have to re-evaluate the
  314. //current position in the grid
  315. currentCellIndex = ixy(x, y);
  316. }
  317. assert(currentCellIndex >= 0);
  318. int& currentCode = m_cd[currentCellIndex];
  319. switch (currentCode)
  320. {
  321. case CASE0: // CASE 0
  322. //we have reached a border!
  323. //if an isoline was underway, it won't be closed!
  324. if (fromEdge != NONE)
  325. {
  326. endContour(false, alternatePath);
  327. }
  328. //toEdge = NONE;
  329. continue;
  330. case CASE1: // CASE 1
  331. case CASE14: // CASE 14
  332. toEdge = (fromEdge == NONE || fromEdge == LEFT ? BOTTOM : LEFT);
  333. currentCode |= VISITED;
  334. break;
  335. case CASE2: // CASE 2
  336. case CASE13: // CASE 13
  337. //if it's a new contour, we have 2 options (RIGHT and BOTTOM)
  338. if (fromEdge == NONE)
  339. {
  340. if (x >= m_w - 2) //can't go on the right!
  341. {
  342. if (y >= m_h - 2) //can't go lower
  343. {
  344. //toEdge = NONE;
  345. continue;
  346. }
  347. else
  348. {
  349. toEdge = BOTTOM;
  350. }
  351. }
  352. else
  353. {
  354. //go right by default
  355. toEdge = RIGHT;
  356. if (y < m_h - 2)
  357. {
  358. //if we can go lower, rembemr this as an alternate rout
  359. altToEdge = BOTTOM;
  360. altStartIndex = currentCellIndex;
  361. }
  362. }
  363. }
  364. else
  365. {
  366. toEdge = (fromEdge == BOTTOM ? RIGHT : BOTTOM);
  367. }
  368. currentCode |= VISITED;
  369. break;
  370. case CASE3: // CASE 3
  371. case CASE12: // CASE 12
  372. toEdge = (fromEdge == NONE || fromEdge == LEFT ? RIGHT : LEFT);
  373. currentCode |= VISITED;
  374. break;
  375. case CASE11: // CASE 11
  376. //assert(fromEdge != NONE);
  377. case CASE4: // CASE 4
  378. toEdge = (fromEdge == NONE || fromEdge == TOP ? RIGHT : TOP);
  379. currentCode |= VISITED;
  380. break;
  381. case CASE5: // CASE 5, saddle
  382. case CASE10: // CASE 10, saddle
  383. {
  384. if (fromEdge != NONE)
  385. {
  386. //check if we are not looping as the saddle points can't be flagged as VISITED!
  387. assert(!m_contourOrigin.empty() && !m_contourIndexes.empty());
  388. if (m_contourIndexes[m_contourOrigin.back()] == currentCellIndex)
  389. {
  390. //isoline loop is closed!
  391. assert(!alternatePath);
  392. endContour(true, false);
  393. //no need to look at the alternate path!
  394. altToEdge = NONE;
  395. //toEdge = NONE;
  396. continue;
  397. }
  398. }
  399. double avg = (in[ixy(x + 0, y + 0)]
  400. + in[ixy(x + 1, y + 0)]
  401. + in[ixy(x + 0, y + 1)]
  402. + in[ixy(x + 1, y + 1)]) / 4.0;
  403. //see http://en.wikipedia.org/wiki/Marching_squares for the disambiguation cases
  404. bool down = ((currentCode == CASE5 && avg < m_threshold)
  405. || (currentCode == CASE10 && avg >= m_threshold));
  406. if (down)
  407. {
  408. if (fromEdge == NONE)
  409. {
  410. //if we start here, we know that some routes have already been taken (the ones coming from UP or LEFT)
  411. //we can ignore the cell
  412. continue;
  413. }
  414. else
  415. {
  416. toEdge = DISAMBIGUATION_DOWN[fromEdge];
  417. }
  418. }
  419. else
  420. {
  421. if (fromEdge == NONE)
  422. {
  423. //if we start here, we know that some routes have already been taken (the ones coming from UP or LEFT)
  424. //it can only be on the right
  425. if (x < m_w - 2)
  426. {
  427. if (m_cd[currentCellIndex + 1] < VISITED)
  428. toEdge = RIGHT;
  429. else
  430. //we can ignore the cell
  431. continue;
  432. }
  433. else
  434. {
  435. //we can ignore the cell
  436. continue;
  437. }
  438. }
  439. else
  440. {
  441. toEdge = DISAMBIGUATION_UP[fromEdge];
  442. }
  443. }
  444. }
  445. break;
  446. case CASE6: // CASE 6
  447. //assert(fromEdge != NONE);
  448. case CASE9: // CASE 9
  449. toEdge = (fromEdge == NONE || fromEdge == TOP ? BOTTOM : TOP);
  450. currentCode |= VISITED;
  451. break;
  452. case CASE7: // CASE 7
  453. //assert(fromEdge != NONE);
  454. case CASE8: // CASE 8
  455. toEdge = (fromEdge == NONE || fromEdge == LEFT ? TOP : LEFT);
  456. currentCode |= VISITED;
  457. break;
  458. case CASE15: // CASE 15
  459. //assert(fromEdge == NONE); //apart at the very beginning!
  460. //toEdge = NONE;
  461. continue;
  462. default:
  463. assert(currentCode > 0 && ((currentCode & VISITED) == VISITED));
  464. if (fromEdge != NONE)
  465. {
  466. //check that we are indeed coming back to the start
  467. assert(!m_contourOrigin.empty() && !m_contourIndexes.empty());
  468. if (m_contourIndexes[m_contourOrigin.back()] == currentCellIndex)
  469. {
  470. //isoline loop is closed!
  471. assert(!alternatePath);
  472. endContour(true, false);
  473. //no need to look at the alternate path!
  474. altToEdge = NONE;
  475. }
  476. else
  477. {
  478. //have we reached a kind of tri-point? (DGM: not sure it's possible)
  479. endContour(false, alternatePath);
  480. }
  481. }
  482. //toEdge = NONE;
  483. continue;
  484. }
  485. assert(toEdge != NONE);
  486. if (fromEdge == NONE)
  487. {
  488. // starting a new contour
  489. m_contourLength.push_back(0);
  490. m_contourClosed.push_back(false);
  491. m_contourOrigin.push_back(static_cast<int>(m_contourX.size()));
  492. //ccLog::Print(QString("New contour: #%1 - origin = %2 - (x=%3, y=%4)").arg(m_contourLength.size()).arg(m_contourOrigin.back()).arg(x).arg(y));
  493. }
  494. double x2 = 0.0, y2 = 0.0;
  495. switch (toEdge)
  496. {
  497. case TOP:
  498. x2 = x + LERP(in[ixy(x + 0, y + 0)], in[ixy(x + 1, y + 0)]);
  499. y2 = y;
  500. y--;
  501. break;
  502. case RIGHT:
  503. x2 = x + 1;
  504. y2 = y + LERP(in[ixy(x + 1, y + 0)], in[ixy(x + 1, y + 1)]);
  505. x++;
  506. break;
  507. case BOTTOM:
  508. x2 = x + LERP(in[ixy(x + 0, y + 1)], in[ixy(x + 1, y + 1)]);
  509. y2 = y + 1;
  510. y++;
  511. break;
  512. case LEFT:
  513. x2 = x;
  514. y2 = y + LERP(in[ixy(x + 0, y + 0)], in[ixy(x + 0, y + 1)]);
  515. x--;
  516. break;
  517. default:
  518. assert(false);
  519. continue;
  520. }
  521. //if (m_contourLength.back() > 1)
  522. //{
  523. // size_t vertCount = m_contourX.size();
  524. // const double& x0 = m_contourX[vertCount - 2];
  525. // const double& y0 = m_contourY[vertCount - 2];
  526. // double& x1 = m_contourX.back();
  527. // double& y1 = m_contourY.back();
  528. // double ux = x1 - x0;
  529. // double uy = y1 - y0;
  530. // double vx = x2 - x0;
  531. // double vy = y2 - y0;
  532. // //test colinearity so as to merge both segments if possible
  533. // double dotprod = (ux*vx + uy*vy) / sqrt((vx*vx + vy*vy) * (ux*ux + uy*uy));
  534. // if (fabsl(dotprod - 1.0) < 1.0e-6)
  535. // {
  536. // //merge: we replace the last vertex by this one
  537. // x1 = x2;
  538. // y1 = y2;
  539. // m_contourIndexes.back() = currentCellIndex;
  540. // }
  541. // else
  542. // {
  543. // //new vertex
  544. // m_contourX.push_back(x2);
  545. // m_contourY.push_back(y2);
  546. // m_contourIndexes.push_back(currentCellIndex);
  547. // m_contourLength.back()++;
  548. // }
  549. //}
  550. //else
  551. {
  552. //new vertex
  553. m_contourX.push_back(x2);
  554. m_contourY.push_back(y2);
  555. m_contourIndexes.push_back(currentCellIndex);
  556. m_contourLength.back()++;
  557. }
  558. }
  559. }
  560. catch (const std::bad_alloc&)
  561. {
  562. //not enough memory
  563. m_contourX.clear();
  564. m_contourY.clear();
  565. m_contourLength.clear();
  566. m_contourIndexes.clear();
  567. return -1;
  568. }
  569. m_numContours = static_cast<int>(m_contourLength.size());
  570. computeBoundingBoxes();
  571. return m_numContours;
  572. }
  573. //! LERP between two values
  574. inline double LERP(T A, T B) const
  575. {
  576. T AB = A - B;
  577. return AB == 0 ? 0 : static_cast<double>(A - m_threshold) / AB;
  578. }
  579. inline int getLastIndex() const
  580. {
  581. int nc = getNumContours();
  582. return nc > 0 ? m_contourOrigin[nc - 1] + m_contourLength[nc - 1] : 0;
  583. }
  584. inline void setContourX(int contour, int v, double x)
  585. {
  586. int o = m_contourOrigin[contour];
  587. m_contourX[wrap(o + v, o, o + m_contourLength[contour])] = x;
  588. }
  589. inline void setContourY(int contour, int v, double y)
  590. {
  591. int o = m_contourOrigin[contour];
  592. m_contourY[wrap(o + v, o, o + m_contourLength[contour])] = y;
  593. }
  594. inline int getValidIndex(int contour, int v) const
  595. {
  596. int o = m_contourOrigin[contour];
  597. return wrap(o + v, o, o + m_contourLength[contour]);
  598. }
  599. double measureArea(int contour, int first, int last) const
  600. {
  601. double area = 0;
  602. if (getValidIndex(contour, first) == getValidIndex(contour, last))
  603. last = last - 1;
  604. double w = 0, h = 0;
  605. for (int i = first; i < last; i++)
  606. {
  607. w = getContourX(contour, i + 1) - getContourX(contour, i);
  608. h = (getContourY(contour, i + 1) + getContourY(contour, i)) / 2.0;
  609. area += w * h;
  610. }
  611. w = getContourX(contour, first) - getContourX(contour, last);
  612. h = (getContourY(contour, first) + getContourY(contour, last)) / 2.0;
  613. area += w * h;
  614. return area;
  615. }
  616. double measureMeanX(int contour) const
  617. {
  618. double mean = 0.0;
  619. int l = getContourLength(contour);
  620. for (int i = 0; i < l; i++)
  621. mean += getContourX(contour, i);
  622. return (l == 0 ? 0 : mean / l);
  623. }
  624. double measureMeanY(int contour) const
  625. {
  626. double mean = 0.0;
  627. int l = getContourLength(contour);
  628. for (int i = 0; i < l; i++)
  629. mean += getContourY(contour, i);
  630. return (l == 0 ? 0 : mean / l);
  631. }
  632. double measurePerimeter(int contour, int first, int last) const
  633. {
  634. if (getValidIndex(contour, first) == getValidIndex(contour, last))
  635. last = last - 1;
  636. double perim = 0;
  637. for (int i = first; i < last; i++)
  638. perim += measureLength(contour, i);
  639. perim += measureDistance(contour, first, last);
  640. return perim;
  641. }
  642. double measureNormalX(int contour, int i) const
  643. {
  644. double ret = getContourY(contour, i) - getContourY(contour, i + 1);
  645. ret = ret / measureLength(contour, i);
  646. return ret;
  647. }
  648. double measureNormalY(int contour, int i) const
  649. {
  650. double ret = getContourX(contour, i + 1) - getContourX(contour, i);
  651. ret = ret / measureLength(contour, i);
  652. return ret;
  653. }
  654. double measureNormalY(int contour, int first, int last) const
  655. {
  656. double ret = 0;
  657. for (int i = first; i < last; i++)
  658. ret += measureNormalY(contour, i);
  659. return ret;
  660. }
  661. double measureNormalX(int contour, int first, int last) const
  662. {
  663. double ret = 0;
  664. for (int i = first; i < last; i++)
  665. ret += measureNormalX(contour, i);
  666. return ret;
  667. }
  668. double measureAngleChange(int contour, int first, int last) const
  669. {
  670. double sum = 0;
  671. for (int i = first; i <= last; i++)
  672. sum += measureAngle(contour, i);
  673. return sum;
  674. }
  675. static int wrap(int i, int lo, int hi)
  676. {
  677. int l = hi - lo;
  678. int d = i - lo;
  679. int w = 0;
  680. if (d < 0)
  681. w = hi - ((-d) % l);
  682. else
  683. w = lo + (d % l);
  684. if (w == hi)
  685. w = lo;
  686. if (w < lo)
  687. {
  688. assert(false);
  689. printf("went below lo\n");
  690. }
  691. else if (w >= hi)
  692. {
  693. assert(false);
  694. printf("went above hi\n");
  695. }
  696. return w;
  697. }
  698. inline int ixy(int x, int y) const { return x + y * m_w; }
  699. inline double measureDistance(int contour, int first, int second) const
  700. {
  701. double dx = getContourX(contour, first) - getContourX(contour, second);
  702. double dy = getContourY(contour, first) - getContourY(contour, second);
  703. return std::sqrt(dx * dx + dy * dy);
  704. }
  705. // return length from i to i + 1
  706. double measureLength(int contour, int i) const
  707. {
  708. int lo = m_contourOrigin[contour];
  709. int n = m_contourLength[contour];
  710. int hi = lo + n;
  711. int v1 = wrap(lo + i + 0, lo, hi);
  712. int v2 = wrap(lo + i + 1, lo, hi);
  713. double aftx = m_contourX[v2] - m_contourX[v1];
  714. double afty = m_contourY[v2] - m_contourY[v1];
  715. return std::sqrt(aftx * aftx + afty * afty);
  716. }
  717. // return the relative angle change in radians
  718. // about the point i (assuming ccw is positive)
  719. double measureAngle(int contour, int i) const
  720. {
  721. double befx = getContourX(contour, i + 0) - getContourX(contour, i - 1);
  722. double befy = getContourY(contour, i + 0) - getContourY(contour, i - 1);
  723. double aftx = getContourX(contour, i + 1) - getContourX(contour, i + 0);
  724. double afty = getContourY(contour, i + 1) - getContourY(contour, i + 0);
  725. double befl = std::sqrt(befx * befx + befy * befy);
  726. befx /= befl;
  727. befy /= befl;
  728. double aftl = std::sqrt(aftx * aftx + afty * afty);
  729. aftx /= aftl;
  730. afty /= aftl;
  731. double dot = befx * aftx + befy * afty;
  732. if (dot > 1.0)
  733. dot = 1.0;
  734. else if (dot < 0)
  735. dot = 0;
  736. double rads = std::acos(dot);
  737. assert(rads == rads); //otherwise it means that rads is NaN!!!
  738. if (aftx * befy - afty * befx < 0)
  739. rads = -rads;
  740. return rads;
  741. }
  742. public:
  743. inline double getContourX(int contour, int v) const
  744. {
  745. int o = m_contourOrigin[contour];
  746. return m_contourX[wrap(o + v, o, o + m_contourLength[contour])];
  747. }
  748. inline double getContourY(int contour, int v) const
  749. {
  750. int o = m_contourOrigin[contour];
  751. return m_contourY[wrap(o + v, o, o + m_contourLength[contour])];
  752. }
  753. inline double measureCurvature(int contour, int i) const
  754. {
  755. return measureAngle(contour, i) / measureLength(contour, i);
  756. }
  757. void findAreas(int window, std::vector<double>& tips)
  758. {
  759. tips.resize(m_w * m_h);
  760. for (int k = 0; k < m_numContours; k++)
  761. {
  762. int l = getContourLength(k);
  763. for (int i = 0; i < l; i++)
  764. {
  765. int lo = i - window;
  766. int hi = i + window;
  767. tips[getValidIndex(k, i)] = measureArea(k, lo, hi);
  768. }
  769. }
  770. }
  771. void findRoundedCorners(int window, std::vector<double>& tips)
  772. {
  773. tips.resize(m_w * m_h);
  774. for (int k = 0; k < m_numContours; k++)
  775. {
  776. int l = getContourLength(k);
  777. for (int i = 0; i < l; i++)
  778. {
  779. int lo = i - window;
  780. int hi = i + window;
  781. tips[getValidIndex(k, i)] = measureArea(k, lo, hi) / measurePerimeter(k, lo, hi);
  782. }
  783. }
  784. }
  785. int getMaxContour() const
  786. {
  787. int maxlength = 0;
  788. int idx = 0;
  789. for (int k = 0; k < m_numContours; k++)
  790. {
  791. int l = getContourLength(k);
  792. if (l > maxlength)
  793. {
  794. maxlength = l;
  795. idx = k;
  796. }
  797. }
  798. return idx;
  799. }
  800. // POLYGON HIT TESTING ROUTINES
  801. bool computeBoundingBoxes()
  802. {
  803. int numContours = getNumContours();
  804. if (numContours == 0)
  805. {
  806. m_minx.clear();
  807. m_miny.clear();
  808. m_maxx.clear();
  809. m_maxy.clear();
  810. return true;
  811. }
  812. try
  813. {
  814. m_minx.resize(numContours);
  815. m_miny.resize(numContours);
  816. m_maxx.resize(numContours);
  817. m_maxy.resize(numContours);
  818. }
  819. catch (const std::bad_alloc&)
  820. {
  821. //not enough memory!
  822. return false;
  823. }
  824. for (int k = 0; k < numContours; k++)
  825. {
  826. int o = m_contourOrigin[k];
  827. m_minx[k] = m_contourX[o];
  828. m_miny[k] = m_contourY[o];
  829. m_maxx[k] = m_contourX[o];
  830. m_maxy[k] = m_contourY[o];
  831. for (int i = 1; i < getContourLength(k); i++)
  832. {
  833. int j = o + i;
  834. if (m_contourX[j] < m_minx[k])
  835. m_minx[k] = m_contourX[j];
  836. else if (m_contourX[j] > m_maxx[k])
  837. m_maxx[k] = m_contourX[j];
  838. if (m_contourY[j] < m_miny[k])
  839. m_miny[k] = m_contourY[j];
  840. else if (m_contourY[j] > m_maxy[k])
  841. m_maxy[k] = m_contourY[j];
  842. }
  843. }
  844. return true;
  845. }
  846. inline double getBBMinX(int contour) const { return m_minx[contour]; }
  847. inline double getBBMaxX(int contour) const { return m_maxx[contour]; }
  848. inline double getBBMinY(int contour) const { return m_miny[contour]; }
  849. inline double getBBMaxY(int contour) const { return m_maxy[contour]; }
  850. bool contains(int k, double x, double y) const
  851. {
  852. bool inside = false;
  853. int l = getContourLength(k);
  854. for (int i = 0, j = -1; i < l; j = i++)
  855. {
  856. double yi = getContourY(k, i);
  857. double yj = getContourY(k, j);
  858. if ((yj > y) != (yi > y))
  859. {
  860. double xi = getContourX(k, i);
  861. double xj = getContourX(k, j);
  862. if (yi == yj)
  863. {
  864. if (x < xi)
  865. inside = !inside;
  866. }
  867. else if (x < xi + (y - yi) * (xi - xj) / (yi - yj))
  868. {
  869. inside = !inside;
  870. }
  871. }
  872. }
  873. return inside;
  874. }
  875. bool containsContour(int k1, int k2)
  876. {
  877. if (!bbIntersect(k1, k2))
  878. return false;
  879. double minx = getBBMinX(k2);
  880. double maxx = getBBMaxX(k2);
  881. double miny = getBBMinY(k2);
  882. double maxy = getBBMaxY(k2);
  883. return ( contains(k1, minx, miny)
  884. && contains(k1, maxx, miny)
  885. && contains(k1, maxx, maxy)
  886. && contains(k1, minx, maxy) );
  887. }
  888. inline bool containsBoundingBox(int k, double minx, double miny, double maxx, double maxy) const
  889. {
  890. return ( contains(k, minx, miny)
  891. && contains(k, maxx, miny)
  892. && contains(k, maxx, maxy)
  893. && contains(k, minx, maxy) );
  894. }
  895. bool contains(const std::vector<double>& polyx, const std::vector<double>& polyy, double x, double y) const
  896. {
  897. bool inside = false;
  898. size_t l = polyx.size();
  899. if (l < 1)
  900. return false;
  901. for (size_t i = 0, j = l - 1; i < l; j = i++)
  902. {
  903. double yi = polyy[i];
  904. double yj = polyy[j];
  905. if ((yj > y) != (yi > y))
  906. {
  907. double xi = polyx[i];
  908. double xj = polyx[j];
  909. if (yi == yj)
  910. {
  911. if (x < xi)
  912. inside = !inside;
  913. }
  914. else if (x < xi + (y - yi) * (xi - xj) / (yi - yj))
  915. {
  916. inside = !inside;
  917. }
  918. }
  919. }
  920. return inside;
  921. }
  922. // intersects contour k1 with contour k2
  923. bool bbIntersect(int k1, int k2) const
  924. {
  925. double minx1 = getBBMinX(k1);
  926. double maxx1 = getBBMaxX(k1);
  927. double miny1 = getBBMinY(k1);
  928. double maxy1 = getBBMaxY(k1);
  929. double minx2 = getBBMinX(k2);
  930. double maxx2 = getBBMaxX(k2);
  931. double miny2 = getBBMinY(k2);
  932. double maxy2 = getBBMaxY(k2);
  933. double lt = minx1 > minx2 ? minx1 : minx2;
  934. double rt = maxx1 < maxx2 ? maxx1 : maxx2;
  935. double tp = miny1 > miny2 ? miny1 : miny2;
  936. double bt = maxy1 < maxy2 ? maxy1 : maxy2;
  937. return (lt < rt && tp < bt);
  938. }
  939. bool bbContainsBB(int k1, int k2) const
  940. {
  941. double minx1 = getBBMinX(k1);
  942. double maxx1 = getBBMaxX(k1);
  943. double miny1 = getBBMinY(k1);
  944. double maxy1 = getBBMaxY(k1);
  945. double minx2 = getBBMinX(k2);
  946. double maxx2 = getBBMaxX(k2);
  947. double miny2 = getBBMinY(k2);
  948. double maxy2 = getBBMaxY(k2);
  949. return (minx1 <= minx2 && maxx1 >= maxx2 && miny1 <= miny2 && maxy1 >= maxy2);
  950. }
  951. inline double bbArea(int k) const
  952. {
  953. double w = getBBMaxX(k) - getBBMinX(k);
  954. double h = getBBMaxY(k) - getBBMinY(k);
  955. return w * h;
  956. }
  957. inline double getBBCenterX(int k) const { return (getBBMinX(k) + getBBMaxX(k)) / 2.0; }
  958. inline double getBBCenterY(int k) const { return (getBBMinY(k) + getBBMaxY(k)) / 2.0; }
  959. };
  960. #endif //ISOLINES_HEADER