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