/code/FullApp/iSanta/iSanta/EllipseFitting.cpp

https://github.com/siegleal/iSanta · C++ · 176 lines · 111 code · 36 blank · 29 comment · 17 complexity · fdea18342e37daf1865da17cf3562916 MD5 · raw file

  1. //#include "highgui.h"
  2. //#include "cv.h"
  3. #include "EllipseFitting.h"
  4. // Threshold value
  5. const int threshold = 80;
  6. // Computes the average distance from the circle of an array of points
  7. float computeError(CvPoint center, int radius, CvPoint2D32f* points, int count)
  8. {
  9. float error = 0;
  10. int i;
  11. for (i = 0; i < count; i++) { // for each of the points
  12. float x = (float)points[i].x;
  13. float y = (float)points[i].y;
  14. // Compute the distance from the center of the circle
  15. float dist = sqrt((center.x - x) * (center.x - x) + (center.y - y) * (center.y - y));
  16. // Add the distance from the point to the circle
  17. error += fabs(dist - radius);
  18. }
  19. // Return the average error
  20. return error / count;
  21. }
  22. void AddBefore(PointsLinkedList* list, PointNode* node, ScoredPoint point){
  23. PointNode *current = list->head;
  24. while (current != node){
  25. current = current->Next;
  26. }
  27. PointNode *pn = (PointNode *)malloc(sizeof(PointNode));
  28. pn->Value = &point;
  29. current->Next = pn;
  30. }
  31. void AddLast(PointsLinkedList* list, ScoredPoint point){
  32. PointNode *current = list->head;
  33. while (current != NULL){
  34. current = current->Next;
  35. }
  36. PointNode *pn = (PointNode *)malloc(sizeof(PointNode));
  37. pn->Value = &point;
  38. current->Next = pn;
  39. }
  40. // Begins bullet-hole recognition
  41. Point* EllipseFitting::ProcessImage(char* imagePath, int numShots, int pixelsPerRadius, CvRect ROI)
  42. {
  43. char* path = imagePath;
  44. IplImage* src = cvLoadImage(path);
  45. cvSetImageROI(src, ROI);
  46. // Create dynamic structure and sequence
  47. CvMemStorage* stor = cvCreateMemStorage(0);
  48. CvSeq* cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), stor);
  49. // Create a grayscale image of the source image
  50. IplImage* graysrc = cvCreateImage(cvSize(src->width, src->height), 8, 1);
  51. cvSetImageROI(graysrc, ROI);
  52. cvCvtColor(src, graysrc, CV_BGR2GRAY);
  53. // Smooth the image to remove noise
  54. cvSmooth(graysrc, graysrc, CV_GAUSSIAN, 7, 7);
  55. // Apply threshold for edge detection
  56. IplImage* thresholdImg = cvCreateImage(cvSize(src->width, src->height), src->depth, 1);
  57. cvSetImageROI(thresholdImg, ROI);
  58. cvThreshold(graysrc, thresholdImg, threshold, 255, CV_THRESH_BINARY);
  59. // Erode then dilate by a 3 x 3 rectangular structuring element
  60. cvErode(thresholdImg, thresholdImg);
  61. cvDilate(thresholdImg, thresholdImg);
  62. // Find contours and store them all as a list
  63. cvFindContours(thresholdImg, stor, &cont, sizeof(CvContour),
  64. CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
  65. PointsLinkedList scoredPoints;
  66. // This cycle draw the contour and approximate it by an ellipse
  67. for (; cont; cont = cont->h_next) {
  68. int count = cont->total; // the number of points in the contour
  69. // The number of points must be more than or equal to 6 (for cvFitEllipse_32f)
  70. if (count < 6) {
  71. continue;
  72. }
  73. // Allocate memory for contour point set
  74. CvPoint* PointArray = (CvPoint*)malloc(count*sizeof(CvPoint));
  75. CvPoint2D32f* PointArray2D32f = (CvPoint2D32f*)malloc(count*sizeof(CvPoint2D32f));
  76. // Allocate memory for ellipse data
  77. CvBox2D32f* box = (CvBox2D32f*)malloc(sizeof(CvBox2D32f));
  78. // Get the contour point set
  79. cvCvtSeqToArray(cont, PointArray, CV_WHOLE_SEQ);
  80. // Convert CvPoint set to CvBox2D32f set
  81. int i;
  82. for (i = 0; i < count; i++) {
  83. PointArray2D32f[i].x = (float)PointArray[i].x;
  84. PointArray2D32f[i].y = (float)PointArray[i].y;
  85. }
  86. // Fit ellipse to current contour
  87. cvFitEllipse(PointArray2D32f, count, box);
  88. // Convert ellipse data from float to integer representation
  89. CvPoint center;
  90. CvSize size;
  91. center.x = cvRound(box->center.x);
  92. center.y = cvRound(box->center.y);
  93. size.width = cvRound(box->size.width * 0.5);
  94. size.height = cvRound(box->size.height * 0.5);
  95. box->angle = -box->angle;
  96. // Compute the error of the ellipse from the expected bullet hole
  97. float error = computeError(center, pixelsPerRadius, PointArray2D32f, count);
  98. Point p;
  99. p.x = center.x + ROI.x;
  100. p.y = center.y + ROI.y;
  101. ScoredPoint scoredPoint = *new ScoredPoint(error, p);
  102. // Add the ellipse to the list of possible bullet holes
  103. PointNode* current = scoredPoints.head;
  104. bool added = false;
  105. while (current != NULL) {
  106. if (error <= current->Value->score) {
  107. AddBefore(&scoredPoints,current, scoredPoint);
  108. added = true;
  109. break;
  110. }
  111. current = current->Next;
  112. }
  113. // Add the scored point to the end if it hasn't been added yet
  114. if (!added) {
  115. AddLast(&scoredPoints,scoredPoint);
  116. }
  117. // Free memory
  118. free(PointArray);
  119. free(PointArray2D32f);
  120. free(box);
  121. }
  122. // Pick the top numShots bullet holes to return
  123. //find the size of the list
  124. PointNode* current = scoredPoints.head;
  125. int listSize = 0;
  126. while (current != NULL){
  127. listSize++;
  128. current = current->Next;
  129. }
  130. Point* points = (Point*)malloc(listSize * sizeof(Point));
  131. current = scoredPoints.head;
  132. for (int i = 0; i < numShots && current != NULL; i++, current = current->Next) {
  133. points[i] = (current->Value->center);
  134. }
  135. // Free memory
  136. cvReleaseImage(&graysrc);
  137. cvReleaseImage(&thresholdImg);
  138. cvReleaseMemStorage(&stor);
  139. return points;
  140. }