From 04def2dd184467cdef0089257abf8e603b43a087 Mon Sep 17 00:00:00 2001 From: mdfeist Date: Thu, 6 Jun 2013 15:26:01 -0600 Subject: [PATCH] EyeCalibration now can calculate the Convex Hull. --- BlinkAnalysis/EyeCalibration.cpp | 307 ++++++++++++++++++++++- BlinkAnalysis/EyeCalibration.h | 39 ++- BlinkAnalysis/EyeCalibrationWizardForm.h | 2 + 3 files changed, 343 insertions(+), 5 deletions(-) diff --git a/BlinkAnalysis/EyeCalibration.cpp b/BlinkAnalysis/EyeCalibration.cpp index 8d709f8..76f7b25 100644 --- a/BlinkAnalysis/EyeCalibration.cpp +++ b/BlinkAnalysis/EyeCalibration.cpp @@ -4,11 +4,15 @@ #include "EyeCalibrationWizardFormController.h" +#include // std::sort #include EyeCalibration::EyeCalibration(void) { this->rbHeadId = -1; this->rbViewingObjectId = -1; + + this->center_x = 0; + this->center_y = 0; } char* EyeCalibration::getNameById(int id) { @@ -65,10 +69,309 @@ bool EyeCalibration::addPoint() { return true; } -void EyeCalibration::calibrate() { - EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog("Starting Calibration\n"); +bool EyeCalibration::isLess(CalibrationPoint a, CalibrationPoint b) +{ + if (a.x() >= 0 && b.x() < 0) + return true; + if (a.x() == 0 && b.x() == 0) + return a.y() > b.y(); + + // compute the cross product of vectors (center -> a) x (center -> b) + int det = (a.x() - center_x) * (b.y() - center_y) - (b.x() - center_x) * (a.y() - center_y); + if (det < 0) + return true; + if (det > 0) + return false; + + // points a and b are on the same line from the center + // check which point is closer to the center + int d1 = (a.x() - center_x) * (a.x() - center_x) + (a.y() - center_y) * (a.y() - center_y); + int d2 = (b.x() - center_x) * (b.x() - center_x) + (b.y() - center_y) * (b.y() - center_y); + return d1 > d2; +} + +bool EyeCalibration::isEdge(std::vector processingPoints, Segment edge) +{ + for(unsigned int k = 0; k < processingPoints.size(); k++) + { + if(isLeft(edge, processingPoints.at(k)) > 0) + { + return false; + } + } + return true; +} + +int EyeCalibration::isLeft(Segment segment, CalibrationPoint r) { + float D = 0; + float px, py, qx, qy, rx, ry = 0; + //The determinant + // | 1 px py | + // | 1 qx qy | + // | 1 rx ry | + //if the determinant result is positive the point is + //left of the segment + px = segment.x1(); + py = segment.y1(); + qx = segment.x2(); + qy = segment.y2(); + rx = r.x(); + ry = r.y(); + D = ((qx * ry) - (qy * rx)) - (px *(ry - qy)) + (py * (rx - qx)); + if (D > 0) + return 1; + else if (D < 0) + return -1; + else + return 0; +} + +bool EyeCalibration::calibrate() { char buf[512]; + + // Print starting message + EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog("Starting Calibration\n"); + + // Print the number of points used in the calibration sprintf_s(buf, "A total of %d points was collected.\n", calibrationPoints.size()); EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog(buf); + + if (calibrationPoints.size() < 4) { + EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog("Not enough points to calculate calibration.\n"); + return false; + } + + CalibrationPoint + minX = calibrationPoints.at(0), + minY = calibrationPoints.at(0), + maxX = calibrationPoints.at(0), + maxY = calibrationPoints.at(0); + + // Print each points information + for (unsigned int i = 0; i < calibrationPoints.size(); i++) { + CalibrationPoint point = calibrationPoints.at(i); + osg::Vec3 ray = point.getRay(); + + // Print info + sprintf_s(buf, "Point %d (%d, %d) has a value of (%f, %f, %f)\n", + i + 1, + point.x(), point.y(), + ray.x(), ray.y(), ray.z()); + EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog(buf); + + if (minX.x() > point.x()) + minX = point; + + if (minY.y() > point.y()) + minY = point; + + if (maxX.x() < point.x()) + maxX = point; + + if (maxY.y() < point.y()) + maxY = point; + } + + // Print Bounding Info + { + sprintf_s(buf, "Point with min X value (%d, %d)\n", + minX.x(), minX.y()); + EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog(buf); + + sprintf_s(buf, "Point with min Y value (%d, %d)\n", + minY.x(), minY.y()); + EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog(buf); + + sprintf_s(buf, "Point with max X value (%d, %d)\n", + maxX.x(), maxX.y()); + EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog(buf); + + sprintf_s(buf, "Point with max Y value (%d, %d)\n", + maxY.x(), maxY.y()); + EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog(buf); + + this->center_x = (maxX.x() - minX.x())/2; + this->center_y = (maxY.y() - minY.y())/2; + + sprintf_s(buf, "Center point is (%d, %d)\n", + center_x, center_y); + EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog(buf); + } + + // Ordered Point + std::vector orderedPoints(calibrationPoints); + + // Sort Points + EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog("Ordering Points ...\n"); + std::sort (orderedPoints.begin(), orderedPoints.end(), *this); + EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog("Ordered Points\n"); + + // Clearing previous segments + segments.clear(); + EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog("Cleared Previous Convex Hull\n"); + + // Create segments + for (unsigned int i = 0; i < orderedPoints.size(); i++) { + CalibrationPoint from = orderedPoints.at(i); + + for (unsigned int k = 0; k < orderedPoints.size(); k++) { + if (i == k) + continue; + + CalibrationPoint to = orderedPoints.at(k); + + Segment segment(from, to); + segments.push_back(segment); + } + } + + // Calculate Convex Hull + { + unsigned int i = 0; + unsigned int j = 0; + while ( i < segments.size() ) + { + //ProcessingPoints will be the points that are not in the current segment + std::vector processingPoints(orderedPoints); + Segment segment = segments.at(i); + + //this loop prepares the ProcessingPoints list for each segment + while ( j < processingPoints.size() ) + { + CalibrationPoint point = processingPoints.at(j); + if((segment.x1() == point.x() && segment.y1() == point.y()) || + (segment.x2() == point.x() && segment.y2() == point.y())) + { + //eliminating the points that are already in the current segment... + //we don't need them + processingPoints.erase(processingPoints.begin()+j); + j = 0; + } else { + j++; + } + } + + //checking if the current segment is an edge or notBy calling isEdge function + if( !isEdge(processingPoints, segments.at(i)) ) + { + segments.erase(segments.begin()+i); + i = 0; + } else { + i++; + } + } + } + + // Print each segment information + for (unsigned int i = 0; i < segments.size(); i++) { + Segment segment = segments.at(i); + sprintf_s(buf, "Edge %d: From (%d, %d) to (%d, %d)\n", + i + 1, + segment.x1(), segment.y1(), + segment.x2(), segment.y2()); + EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog(buf); + } + + // Group Points + std::vector convexHullPoints; + std::vector innerPoints; + + for (unsigned int i = 0; i < orderedPoints.size(); i++) { + CalibrationPoint point = orderedPoints.at(i); + + if (pointInPolygon(point)) + innerPoints.push_back(point); + else + convexHullPoints.push_back(point); + } + + // Print Inner points + for (unsigned int i = 0; i < innerPoints.size(); i++) { + CalibrationPoint point = innerPoints.at(i); + + sprintf_s(buf, "Inner Point %d: (%d, %d))\n", + i + 1, + point.x(), point.y()); + EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog(buf); + } + + // Print Convex Hull points + for (unsigned int i = 0; i < convexHullPoints.size(); i++) { + CalibrationPoint point = convexHullPoints.at(i); + + sprintf_s(buf, "Convex Hull Point %d: (%d, %d))\n", + i + 1, + point.x(), point.y()); + EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog(buf); + } + + CalibrationPoint pointIn(740, 320, osg::Vec3(0.f, 0.f, 0.f)); + testPointInPolygon(pointIn); + + CalibrationPoint pointOut(232, 2, osg::Vec3(0.f, 0.f, 0.f)); + testPointInPolygon(pointOut); + + return true; +} + +bool EyeCalibration::pointInPolygon(CalibrationPoint point) { + + unsigned int n = segments.size(); + + if (n < 3) + false; + + // loop through all edges of the polygon + for (unsigned int i = 0; i < n; i++) + { + Segment segment = segments.at(i); + + if(isLeft(segment, point) >= 0) + return false; + } + + return true; +} + +void EyeCalibration::testPointInPolygon(CalibrationPoint point) { + char buf[512]; + + if (pointInPolygon(point)) { + sprintf_s(buf, "Point (%d, %d) is inside.\n", point.x(), point.y()); + EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog(buf); + } else { + sprintf_s(buf, "Point (%d, %d) is outside.\n", point.x(), point.y()); + EyeCalibrationWizardFormController::getInstance()->calibrationOutputLog(buf); + } +} + +void EyeCalibration::createTestData() { + + CalibrationPoint point1(10, 5, osg::Vec3(-10.f, 10.f, 1.f)); + calibrationPoints.push_back(point1); + + CalibrationPoint point2(322, 50, osg::Vec3(-2.f, 12.f, 1.f)); + calibrationPoints.push_back(point2); + + CalibrationPoint point3(700, 25, osg::Vec3(12.f, 8.f, 1.f)); + calibrationPoints.push_back(point3); + + CalibrationPoint point4(15, 280, osg::Vec3(-8.f, 2.f, 1.f)); + calibrationPoints.push_back(point4); + + CalibrationPoint point5(374, 180, osg::Vec3(5.f, -2.f, 1.f)); + calibrationPoints.push_back(point5); + + CalibrationPoint point6(742, 320, osg::Vec3(14.f, -1.f, 1.f)); + calibrationPoints.push_back(point6); + + CalibrationPoint point7(4, 480, osg::Vec3(-12.f, -10.f, 1.f)); + calibrationPoints.push_back(point7); + + CalibrationPoint point8(280, 465, osg::Vec3(0.f, -9.f, 1.f)); + calibrationPoints.push_back(point8); + + CalibrationPoint point9(724, 520, osg::Vec3(11.f, -12.f, 1.f)); + calibrationPoints.push_back(point9); } \ No newline at end of file diff --git a/BlinkAnalysis/EyeCalibration.h b/BlinkAnalysis/EyeCalibration.h index d032d5d..d11b083 100644 --- a/BlinkAnalysis/EyeCalibration.h +++ b/BlinkAnalysis/EyeCalibration.h @@ -12,6 +12,7 @@ class EyeCalibration { int pixel_x, pixel_y; osg::Vec3 ray; public: + CalibrationPoint() {} CalibrationPoint(int pixel_x, int pixel_y, osg::Vec3 ray) { this->pixel_x = pixel_x; this->pixel_y = pixel_y; @@ -19,8 +20,24 @@ class EyeCalibration { } osg::Vec3 getRay() { return this->ray; } - int getX() { return this->pixel_x; } - int getY() { return this->pixel_y; } + int& x() { return this->pixel_x; } + int& y() { return this->pixel_y; } + }; + + class Segment { + private: + CalibrationPoint p1, p2; + public: + Segment() {} + Segment(CalibrationPoint from, CalibrationPoint to) { + this->p1 = from; + this->p2 = to; + } + + int x1() { return this->p1.x(); } + int y1() { return this->p1.y(); } + int x2() { return this->p2.x(); } + int y2() { return this->p2.y(); } }; int rbHeadId; @@ -30,6 +47,14 @@ class EyeCalibration { char* getNameById(int id); std::vector calibrationPoints; + std::vector segments; + + int center_x, center_y; + + bool isLess(CalibrationPoint a, CalibrationPoint b); + bool isEdge(std::vector processingPoints, Segment edge); + int isLeft(Segment segment, CalibrationPoint r); + public: EyeCalibration(void); ~EyeCalibration(void) {}; @@ -43,5 +68,13 @@ class EyeCalibration { char* getViewingObjectName() { return getNameById(this->rbViewingObjectId); } bool addPoint(); - void calibrate(); + bool calibrate(); + bool pointInPolygon(CalibrationPoint point); + + bool operator()(CalibrationPoint a, CalibrationPoint b) { + return isLess(a, b); + } + + void testPointInPolygon(CalibrationPoint point); + void createTestData(); }; \ No newline at end of file diff --git a/BlinkAnalysis/EyeCalibrationWizardForm.h b/BlinkAnalysis/EyeCalibrationWizardForm.h index 6c5bfd0..58f8120 100644 --- a/BlinkAnalysis/EyeCalibrationWizardForm.h +++ b/BlinkAnalysis/EyeCalibrationWizardForm.h @@ -697,6 +697,8 @@ private: System::Void selectAsObjectBtn_Click(System::Object^ sender, System::E } } private: System::Void addCalibrationPointBtn_Click(System::Object^ sender, System::EventArgs^ e) { + //this->eyeCalibration->addPoint(); + this->eyeCalibration->createTestData(); } public: System::Void calibrationOutputLog(String^ value) { if (this->calibrationOutputLogLabel->InvokeRequired)