KSquare Utilities
ConvexHull.cpp
Go to the documentation of this file.
1 #include "FirstIncludes.h"
2 
3 //**********************************************************************
4 //* Originally Developed by Tong Luo as a Java Applet. *
5 //* *
6 //* Feb-28-03 Ported to c++ by Kurt Kramer to work with Raster class *
7 //* *
8 //**********************************************************************
9 
10 /*
11  * ConvexHull.java
12  *
13  * Created on April 7, 2002, 10:52 PM
14  */
15 
16 #include <math.h>
17 #include <iostream>
18 #include <vector>
19 #include "MemoryDebug.h"
20 using namespace std;
21 
22 
23 #include "KKBaseTypes.h"
24 
25 #include "ConvexHull.h"
26 #include "KKException.h"
27 #include "Point.h"
28 #include "Raster.h"
29 using namespace KKB;
30 
31 
32 
33 
35  MorphOp (),
36  convexArea (0),
37  upperPoints (NULL),
38  lowerPoints (NULL),
39  upper (NULL),
40  lower (NULL)
41 {
42  AllocateMemory ();
43 }
44 
45 
47 {
48  CleanUpMemory ();
49 }
50 
51 
52 
53 void ConvexHull::AllocateMemory ()
54 {
55  delete upperPoints; upperPoints = new PointList (true);
56  delete lowerPoints; lowerPoints = new PointList (true);
57  delete upper; upper = new PointList (true);
58  delete lower; lower = new PointList (true);
59 }
60 
61 
62 
63 void ConvexHull::CleanUpMemory ()
64 {
65  delete upperPoints; upperPoints = NULL;
66  delete lowerPoints; lowerPoints = NULL;
67  delete upper; upper = NULL;
68  delete lower; lower = NULL;
69 }
70 
71 
72 
73 RasterPtr ConvexHull::PerformOperation (RasterConstPtr _image)
74 {
75  SetSrcRaster (_image);
76  AllocateMemory ();
77  RasterPtr result = Filter (*srcRaster);
78  return result;
79 } /* PerformOperation */
80 
81 
82 
83 /**
84  *@brief Returns a image that represents the convex-hull of the 'src' image.
85  *param[in] src Source image that convex-hull is to be found for.
86  *@returns A new raster that contains the convex-hull of the 'src' image; the caller will be responsible for deleting.
87  */
88 RasterPtr ConvexHull::Filter (const Raster& src)
89 {
90  SetSrcRaster (&src);
91 
92  kkint32 w = src.Width ();
93  kkint32 h = src.Height ();
94 
95  Store (src);
96 
97  RasterPtr dest = new Raster (h, w);
98 
99  if ((upperPoints->QueueSize () == 1) &&
100  (lowerPoints->QueueSize () == 1))
101  {
102  // We must have a Vertical Line
103  DrawLine (*dest, (*upperPoints)[0], (*lowerPoints)[0], 255);
104  CalcConvexArea (dest);
105  return dest;
106  }
107 
108  else if ((upperPoints->QueueSize () < 1) || (lowerPoints->QueueSize () < 1))
109  {
110  // We have Nothing
111  return dest;
112  }
113 
114  BuildUpperLink ();
115  BuildLowerLink ();
116 
117  Merge ();
118 
119  Draw (*dest);
120 
121  CalcConvexArea (dest);
122 
123  return dest;
124 } /* Filter */
125 
126 
127 
128 RasterPtr ConvexHull::Filter (const Raster& src,
129  RasterPtr dest
130  )
131 {
132  SetSrcRaster (&src);
133  //srcHeight = src.Height ();
134  //srcWidth = src.Width ();
135 
136 // kkint32 w = src.Width ();
137 // kkint32 h = src.Height ();
138 
139  if ((dest->Height () != srcHeight) || (dest->Width () != srcWidth))
140  dest->ReSize (srcHeight, srcWidth, false);
141 
142  Store (src);
143 
144  if ((upperPoints->QueueSize () == 1) &&
145  (lowerPoints->QueueSize () == 1))
146  {
147  // We must have a Vertical Line
148  DrawLine (*dest, (*upperPoints)[0], (*lowerPoints)[0], 255);
149  CalcConvexArea (dest);
150  return dest;
151  }
152 
153  else if ((upperPoints->QueueSize () < 1) || (lowerPoints->QueueSize () < 1))
154  {
155  // We have Nothing
156  return dest;
157  }
158 
159  BuildUpperLink ();
160  BuildLowerLink ();
161 
162  Merge ();
163 
164  Draw (*dest);
165 
166  CalcConvexArea (dest);
167 
168  return dest;
169 } /* Filter */
170 
171 
172 
173 
174 
175 inline
176 double ConvexHull::Distance (Point& p1,
177  Point& p2
178  )
179 {
180  double deltaY = 1.0 + p1.Row () - p2.Row ();
181  double deltaX = 1.0 + p1.Col () - p2.Col ();
182 
183  return sqrt (deltaX * deltaX + deltaY * deltaY);
184 } /* Distance */
185 
186 
187 
188 
190 {
191  return convexArea;
192 }
193 
194 
195 
196 
197 double ConvexHull::ConvexArea2 ()
198 //effects: if upper.size()<3 throw IllegalStateException
199 // else calculate the area for the convex area
200 
201 {
202  if (upper->QueueSize () == 2)
203  {
204  // We must have some kind of line.
205  return Distance ((*upper)[0], (*upper)[1]);
206  }
207 
208 
209  if (upper->QueueSize () == 1)
210  {
211  // We have a dot
212  return (double)1.0;
213  }
214 
215 
216  if (upper->QueueSize () == 0)
217  return 0.0;
218 
219  PointList::iterator it;
220  it = upper->begin ();
221 
222  double area = 0.0;
223 
224  PointPtr p = *it; ++it;
225  PointPtr m = *it; ++it;
226 
227  while (it != upper->end ())
228  {
229  PointPtr current = *it;
230  area = area + TriangleArea (*p, *m, *current);
231  m = current;
232  ++it;
233  }
234 
235  return area;
236 } /* ConvexArea */
237 
238 
239 
240 
241 void ConvexHull::CalcConvexArea (RasterPtr raster)
242 {
243  convexArea = 0;
244 
245  kkint32 w = raster->Width ();
246  kkint32 h = raster->Height ();
247 
248  uchar** rows = raster->Rows ();
249 
250  for (kkint32 col = 0; col < w; col++)
251  {
252  kkint32 topRow;
253  kkint32 botRow;
254 
255  for (topRow = h - 1; topRow >= 0; topRow--)
256  {
257  if (ForegroundPixel (rows[topRow][col]))
258  break;
259  }
260 
261  if (topRow >= 0)
262  {
263 
264  for (botRow = 0; botRow < h; botRow++)
265  {
266  if (ForegroundPixel (rows[botRow][col]))
267  break;
268  }
269 
270  convexArea += 1 + topRow - botRow;
271  }
272  }
273 
274  return;
275 } /* CalcConvexArea */
276 
277 
278 
279 
280 inline
281 double DistanceSquare (Point& p1,
282  Point& p2)
283 {
284  double deltaY = 1.0 + fabs ((float)(p1.Row () - p2.Row ()));
285  double deltaX = 1.0 + fabs ((float)(p1.Col () - p2.Col ()));
286 
287  return deltaX * deltaX + deltaY * deltaY;
288 } /* Distance */
289 
290 
291 
292 
293 
294 /*
295 double ConvexHull::TriangleArea (Point& a,
296  Point& b,
297  Point& c)
298 
299 //effects: if a or b or c==null, throw NullPointerException
300 // else return the positive area of a,b,c
301 
302 {
303  double distBetweenAB = Distance (a, b);
304  if (distBetweenAB == 0)
305  return 0;
306 
307  double h; // Height of Triangle.
308 
309  double area;
310 
311  h = (c.Row () - a.Row ()) * (b.Row () - a.Row ()) + (c.Col () - a.Col ()) * (b.Col () - a.Col ());
312  h = h / (distBetweenAB * distBetweenAB);
313 
314  area = distBetweenAB * h * 0.5;
315 
316  return area;
317 }
318 */
319 
320 
321 
322 double ConvexHull::TriangleArea (Point& a,
323  Point& b,
324  Point& c)
325 {
326  double abSqr = DistanceSquare (a, b);
327  double bcSqr = DistanceSquare (b, c);
328  double acSqr = DistanceSquare (a, c);
329  double ac = sqrt (acSqr);
330 
331  double x = (abSqr - bcSqr + acSqr) / (2 * ac);
332 
333  double h = sqrt (abSqr - (x * x));
334 
335  double area = (h * ac) / 2.0;
336 
337  return area;
338 }
339 
340 
341 
342 
343 
344 void ConvexHull::DrawLine (Raster& raster,
345  Point& p1,
346  Point& p2,
347  uchar pixVal
348  )
349 {
350  kkint32 col;
351  kkint32 row;
352 
353 
354 
355  if (p1.Col () == p2.Col ())
356  {
357  kkint32 startRow;
358  kkint32 endRow;
359 
360  col = p2.Col ();
361 
362  if (p1.Row() < p2.Row ())
363  {
364  startRow = p1.Row ();
365  endRow = p2.Row ();
366  }
367  else
368  {
369  startRow = p2.Row ();
370  endRow = p1.Row ();
371  }
372 
373  for (row = startRow; row <= endRow; row++)
374  {
375  raster.SetPixelValue (row, col, pixVal);
376  }
377 
378  return;
379  }
380 
381 
382  // If we made it here then we are not a vertical line.
383 
384 
385  double m = (double)(p1.Row () - p2.Row ()) / (double)(p1.Col () - p2.Col ());
386  double c = (double)p2.Row () - m * (double)p2.Col ();
387 
388  if (fabs (m) < 1)
389  {
390  kkint32 startCol;
391  kkint32 endCol;
392 
393  if (p1.Col () < p2.Col ())
394  {
395  startCol = p1.Col ();
396  endCol = p2.Col ();
397  }
398  else
399  {
400  startCol = p2.Col ();
401  endCol = p1.Col ();
402  }
403 
404  for (col = startCol; col <= endCol; col++)
405  {
406  row = (kkint32)(m * (double)col + c + 0.5); // The Extract 0.5 is for rounding.
407  raster.SetPixelValue (row, col, pixVal);
408  }
409  }
410  else
411  {
412  kkint32 startRow;
413  kkint32 endRow;
414 
415  if (p1.Row () < p2.Row ())
416  {
417  startRow = p1.Row ();
418  endRow = p2.Row ();
419  }
420  else
421  {
422  startRow = p2.Row ();
423  endRow = p1.Row ();
424  }
425 
426  for (row = startRow; row <= endRow; row++)
427  {
428  col = (kkint32)((((double)row - c) / m) + 0.5); // The Extract 0.5 is for rounding.
429  raster.SetPixelValue (row, col, pixVal);
430  }
431  }
432 } /* DrawLine */
433 
434 
435 
436 
437 
438 void ConvexHull::Draw (Raster& output)
439 {
440  PointList::iterator it; // (*upper);
441  it = upper->begin ();
442 
443  PointPtr lastPoint = NULL;
444  PointPtr nextPoint = NULL;
445 
446  if (it != upper->end ())
447  {lastPoint = *it; ++it;}
448 
449  if (it != upper->end ())
450  {nextPoint = *it; ++it;}
451 
452  PointPtr firstPoint = lastPoint;
453 
454  if ((!nextPoint) && (!lastPoint))
455  return;
456 
457  if ((nextPoint != NULL) && (lastPoint == NULL))
458  {
459  output.SetPixelValue (nextPoint->Row (), nextPoint->Col (), 255);
460  return;
461  }
462 
463  while (nextPoint)
464  {
465  DrawLine (output, *lastPoint, *nextPoint, 255);
466  lastPoint = nextPoint;
467  if (it == upper->end ())
468  nextPoint = NULL;
469  else
470  {
471  nextPoint = *it;
472  ++it;
473  }
474  }
475 
476  DrawLine (output, *lastPoint, *firstPoint, 255);
477 } /* Draw */
478 
479 
480 
481 
482 void ConvexHull::Merge ()
483 {
484  PointPtr p;
485 
486 
487  if (*(lower->LookAtFront ()) == *(upper->LookAtFront ()))
488  {
489  p = lower->PopFromFront ();
490  delete p;
491  }
492 
493 
494  if (*(lower->LookAtBack ()) == *(upper->LookAtBack ()))
495  {
496  p = lower->PopFromBack ();
497  delete p;
498  }
499 
500  if (lower->QueueSize () > 0)
501  {
502  p = lower->PopFromFront ();
503  while (p)
504  {
505  upper->PushOnBack (p);
506  p = lower->PopFromFront ();
507  }
508  }
509 } /* Merge */
510 
511 
512 
513 
514 kkint32 ConvexHull::RelativeCCW (Point& sp,
515  Point& ep,
516  Point& p
517  )
518 {
519  if (sp.Col () == ep.Col ())
520  {
521  // We are looking at a Vertical Line
522 
523  if (sp.Row () < ep.Row ())
524  {
525  // This is a vertical Line Pointing Up.
526 
527  if (p.Col () > ep.Col ())
528  {
529  return -1; // Clockwise Turn(Right)
530  }
531 
532  else if (p.Col () < ep.Col ())
533  {
534  return 1; // Counter-Clockwise Turn(Left).
535  }
536 
537  else
538  {
539  // Next Point is on Same Line
540  if (p.Row () < ep.Row ())
541  {
542  return 1; // In front of Line
543  }
544 
545  else if (p.Row () > sp.Row ())
546  {
547  return -1; // Behind Line
548  }
549 
550  else
551  {
552  return 0; // On Line
553  }
554  }
555  }
556  else
557  {
558  // This is a vertical Line Pointing Down.
559  if (p.Col () > ep.Col ())
560  {
561  return 1; // Counter-Clockwise Turn(Left).
562  }
563 
564  else if (p.Col () < ep.Col ())
565  {
566  return -1; // Clockwise Turn(Right)
567  }
568 
569  else
570  {
571  // Next Point is on Same Line
572  if (p.Row () > ep.Row ())
573  {
574  return 1; // In front of Line
575  }
576 
577  else if (p.Row () < sp.Row ())
578  {
579  return -1; // Behind Line
580  }
581 
582  else
583  {
584  return 0; // On Line.
585  }
586  }
587  }
588  }
589 
590 
591  // If we made it here then we are not a vertical line.
592 
593 
594  double m = (double)(sp.Row () - ep.Row ()) / (double)(sp.Col () - ep.Col ());
595  double c = (double)ep.Row () - m * (double)ep.Col ();
596 
597  double extendedY = m * p.Col () + c; // This is where the line segment will be
598  // if extended to same col as "p".
599 
600  if (extendedY == p.Row ())
601  return 0;
602 
603 
604  if (sp.Col () < ep.Col ())
605  {
606  // we are heading to the right
607 
608  if (extendedY < p.Row ())
609  return 1;
610 
611  else
612  return -1;
613  }
614  else
615  {
616  // We are heading to the left.
617  if (extendedY > p.Row ())
618  return 1;
619 
620  else
621  return -1;
622  }
623 } /* RelativeCCW */
624 
625 
626 
627 /**
628  *@brief Builds the upper list of points that make the top have of the convex-hull.
629  *@details if upperPoints.size()<2 throw IllegalStateException else use the incremental method to
630  * add the convex points into the upper and make the upperPoints=null.
631  */
632 void ConvexHull::BuildUpperLink ()
633 {
634  if (upperPoints->QueueSize () < 2)
635  {
636  KKStr msg = "ConvexHull::BuildUpperLink *** ERROR ***, Not Enough Points to Build Upper Link.";
637  cerr << msg << std::endl;
638  throw KKException (msg);
639  }
640 
641  PointList::iterator it; // (*upperPoints);
642  it = upperPoints->begin ();
643 
644  // upper.add (it.next());
645  upper->PushOnBack (new Point (**it)); ++it;
646 
647  // Point middle = (Point)(it.next ());
648  PointPtr middle = new Point (**it); ++it;
649 
650  PointPtr current;
651  PointPtr old;
652 
653  while (it != upperPoints->end ())
654  {
655  current = *it; ++it;
656 
657  old = upper->BackOfQueue ();
658 
659  while (RelativeCCW (*old, *middle, *current) >= 0)
660  {
661  delete middle;
662  middle = upper->PopFromBack ();
663 
664  if (upper->QueueSize () > 0)
665  old = upper->BackOfQueue ();
666  else
667  break;
668  }
669 
670  upper->PushOnBack (middle);
671  middle = new Point (*current);
672  }
673 
674  upper->PushOnBack (middle);
675 
676  delete upperPoints;
677  upperPoints = NULL;
678 } /* BuildUpperLink */
679 
680 
681 
682 void ConvexHull::BuildLowerLink ()
683 {
684  if (lowerPoints->QueueSize () < 2)
685  {
686  KKStr msg = "ConvexHull::BuildLowerLink *** ERROR ***, Not Enough Points to Build Upper Link.";
687  cerr << msg << std::endl;
688  throw KKException (msg);
689  }
690 
691  PointList::iterator it;
692  it = lowerPoints->begin ();
693 
694  lower->PushOnBack (new Point (**it)); ++it;
695 
696  PointPtr middle = new Point (**it); ++it;
697  PointPtr current = NULL;
698  PointPtr old = NULL;
699 
700  while (it != lowerPoints->end ())
701  {
702  current = *it; ++it;
703 
704  old = lower->GetLast ();
705 
706  while (RelativeCCW (*old, *middle, *current) >= 0)
707  {
708  delete middle;
709  middle = lower->RemoveLast (); // Same as PopFromBack
710 
711  if (lower->QueueSize () > 0)
712  old = lower->GetLast ();
713  else
714  break;
715  }
716 
717  lower->Add (middle);
718  middle = new Point (*current);
719  }
720 
721  lower->Add (middle);
722 
723  delete lowerPoints;
724  lowerPoints = NULL;
725 } /* BuildLowerLink */
726 
727 
728 
729 
730 
731 /**
732  *@brief Build list of the upper and lower points in the image.
733  *@details for each column in the image where there is at least one foreground pixel will add
734  * one point to 'upperPoints' for the pixel with the smallest row and one point to 'lowerPoints'
735  * for the pixel with the largest row.
736  *@param[in] input Source image that we are to generate a convex-hull for.
737  */
738 void ConvexHull::Store (const Raster& input)
739 {
740  kkint32 w = input.Width ();
741  kkint32 h = input.Height ();
742 
743  uchar** rows = input.Rows ();
744 
745  for (kkint32 col = 0; col < w; ++col)
746  {
747  kkint32 row;
748  for (row = h - 1; row >= 0; --row)
749  {
750  if (ForegroundPixel (row, col))
751  {
752  PointPtr u = new Point (row, col);
753  upperPoints->Add (u);
754  break;
755  }
756  }
757 
758  if (row >= 0)
759  {
760  for (row = 0; row < h; row++)
761  {
762  if (ForegroundPixel (row, col))
763  {
764  PointPtr l = new Point (row, col);
765  lowerPoints->AddFirst (l);
766  break;
767  }
768  }
769  }
770  }
771 } /* Store */
void SetSrcRaster(RasterConstPtr _srcRaster)
Definition: MorphOp.cpp:149
__int32 kkint32
Definition: KKBaseTypes.h:88
kkint32 Col() const
Definition: Point.h:40
void SetPixelValue(kkint32 row, kkint32 col, uchar pixVal)
Definition: Raster.cpp:1390
kkint32 Row() const
Definition: Point.h:39
A class that is used by to represent a single image in memory.
Definition: Raster.h:108
PointList(bool _owner)
Definition: Point.cpp:105
kkint32 srcHeight
Definition: MorphOp.h:123
Raster(kkint32 _height, kkint32 _width)
Constructs a blank image with given dimensions; all pixels will be initialized to 0...
Definition: Raster.cpp:318
void ReSize(kkint32 _height, kkint32 _width, bool _color)
Lets you resize the raster dimensions; old image data will be lost.
Definition: Raster.cpp:898
double DistanceSquare(Point &p1, Point &p2)
Definition: ConvexHull.cpp:281
Used by Raster class and MorphOp derived classes to denote a single pixel location in Raster image...
Definition: Point.h:20
kkint32 Height() const
Definition: Raster.h:319
KKTHread * KKTHreadPtr
RasterPtr Filter(const Raster &src, RasterPtr dest)
Definition: ConvexHull.cpp:128
bool ForegroundPixel(kkint32 row, kkint32 col) const
Definition: MorphOp.cpp:202
kkint32 srcWidth
Definition: MorphOp.h:124
unsigned char uchar
Unsigned character.
Definition: KKBaseTypes.h:77
kkint32 ConvexArea()
Definition: ConvexHull.cpp:189
RasterPtr Filter(const Raster &src)
Returns a image that represents the convex-hull of the &#39;src&#39; image. param[in] src Source image that c...
Definition: ConvexHull.cpp:88
Point(kkint32 _row, kkint32 _col)
Definition: Point.cpp:43
void Draw(Raster &output)
Definition: ConvexHull.cpp:438
kkint32 Width() const
Definition: Raster.h:324
Operator that will create a Convex Hull of a supplied image.
Definition: ConvexHull.h:42
Base class for all Morphological operations.
Definition: MorphOp.h:44
virtual RasterPtr PerformOperation(RasterConstPtr _image)
Definition: ConvexHull.cpp:73
Container object used to maintaining a list of pixel locations.
Definition: Point.h:75
RasterConstPtr srcRaster
Definition: MorphOp.h:112
bool ForegroundPixel(uchar pixel) const
Definition: MorphOp.cpp:195
uchar ** Rows() const
Definition: Raster.h:321
void Store(const Raster &input)
Build list of the upper and lower points in the image.
Definition: ConvexHull.cpp:738
virtual ~ConvexHull()
Definition: ConvexHull.cpp:46