KINECT STATS GENERATOR FOR SPORTS VISUALISATION
1.0
|
00001 #include "ThreeDStatsGeneration.h" 00002 00003 //#include <Spline.hpp> 00004 00005 /* 00006 LICENSING: You are free to use any part of this project provided I am informed about it via email 00007 at santoshwins@hotmail.com and referenced appropriately in your works 00008 */ 00009 00010 00011 // to serve as comparison function object for vector SORT 00012 bool compare3FOnX(cv::Point3f i, cv::Point3f j){ return (i.x < j.x);} 00013 00014 ThreeDStatsGeneration::ThreeDStatsGeneration() 00015 { 00016 m_quadFitX = new QuadCurveFitUtility(); 00017 m_quadFitY = new QuadCurveFitUtility(); 00018 m_quadFitZ = new QuadCurveFitUtility(); 00019 } 00020 00021 ThreeDStatsGeneration::~ThreeDStatsGeneration() 00022 { 00023 delete m_quadFitX; 00024 delete m_quadFitY; 00025 delete m_quadFitZ; 00026 } 00027 00028 00029 void ThreeDStatsGeneration::generatePitchDensityGraphData(cv::Point& _userDefinedTopXY, int _userDefinedWidth, int _userDefinedHeight, 00030 int _gridResolution, 00031 std::vector<cv::Point> &_playerImpactPtsAllRallies, 00032 std::vector<cv::Point3f>& o_verticesSetOfCubeBarsGraph) 00033 { 00034 //for each point in the input array 00035 00036 // each cell width = inputwidth/gridresolution 00037 // each cell height = inputheight/gridresolution 00038 // hashx = floor((point.x - _userDefinedTopXY.x)/each cell width) 00039 // hashy = floor((point.y - _userDefinedTopXY.y)/each cell height) 00040 00041 // 1D hash value is hashx + hashy * gridresolution 00042 00043 // push the point as vec[hashvalue].push_back(point) 00044 00045 std::vector<std::vector<cv::Point> > hashMappedData; 00046 00047 // remember to mention these scale factors 00048 float _scaleFactor = 10.0; 00049 int _heightUpScaleFactor = 1.0; 00050 00051 00052 float _scaledWidth = ((float)_userDefinedWidth/_scaleFactor); 00053 float _scaledHeight = ((float)_userDefinedHeight/_scaleFactor); 00054 00055 // we scale down by 10 as pixel coords will be too large 00056 00057 00058 float _scaledTopX = ((float)_userDefinedTopXY.x/_scaleFactor); 00059 float _scaledTopY = ((float)_userDefinedTopXY.y/_scaleFactor); 00060 00061 float _individualCellWidth = _scaledWidth/(float)_gridResolution; // typecast fully if problem arises 00062 float _individualCellHeight = _scaledHeight/(float)_gridResolution; 00063 00064 int hashIncrement = 0; 00065 00066 hashMappedData.resize(_gridResolution * _gridResolution); 00067 00068 hashMappedData.clear(); 00069 00070 // // allocate enough data for hashmap 00071 for(int i=0;i<_gridResolution * _gridResolution;i++) 00072 { 00073 hashMappedData.push_back(std::vector<cv::Point> ()); 00074 00075 hashMappedData[i].push_back(cv::Point(0,0)); 00076 } 00077 00078 00079 00080 for(int i=0;i<_playerImpactPtsAllRallies.size();i++) 00081 { 00082 int hashX = floor((((float)_playerImpactPtsAllRallies[i].x/_scaleFactor) - _scaledTopX)/_individualCellWidth); 00083 int hashY = floor((((float)_playerImpactPtsAllRallies[i].y/_scaleFactor) - _scaledTopY)/_individualCellHeight); 00084 00085 int hashValue = (hashX + (hashY * _gridResolution)); 00086 00087 hashMappedData[hashValue].push_back(_playerImpactPtsAllRallies[i]); 00088 } 00089 00090 00091 // and then make the grid flow from -w to +w 00092 // and -z to +z with grid origin at center 00093 // so subtract w/2 and h/2 respectively 00094 00095 //float _scaledTopXTranslated = _scaledTopX - (_scaledWidth/2); 00096 //float _scaledTopYTranslated = _scaledTopY - (_scaledHeight/2); 00097 00098 00099 float _scaledTopXTranslated = -(_scaledWidth/2); 00100 float _scaledTopYTranslated = -(_scaledHeight/2); 00101 00102 // float tempBottomX4 = 0.0, tempBottomZ4 = 0.0, tempBottomY4 = 0.0 , tempBottomX3 = 0.0, tempBottomZ3 = 0.0,tempBottomY3 = 0.0; 00103 00104 // float tempBottomX2 = 0.0, tempBottomZ2 = 0.0,tempBottomY2 = 0.0,tempBottomX1 = 0.0,tempBottomZ1 = 0.0,tempBottomY1 = 0.0; 00105 00106 // float tempTopX4 = 0.0,tempTopZ4 = 0.0,tempTopY4 = 0.0,tempTopX3 = 0.0,tempTopZ3 = 0.0,tempTopY3 = 0.0,tempTopX2 = 0.0,tempTopZ2 = 0.0; 00107 // float tempTopY2 = 0.0,tempTopX1 = 0.0,tempTopZ1 = 0.0,tempTopY1 = 0.0; 00108 00109 00110 //for(float i=_scaledTopYTranslated;i<(_scaledTopYTranslated + _scaledHeight);i=i+_individualCellHeight) 00111 //{ 00112 // for(float j=_scaledTopXTranslated;j<(_scaledTopXTranslated + _scaledWidth);j=j+_individualCellWidth) 00113 //{ 00114 00115 float xPos = _scaledTopXTranslated; 00116 float zPos = _scaledTopYTranslated; 00117 00118 for(int i = 0;i<_gridResolution;i++) 00119 { 00120 for(int j=0;j<_gridResolution;j++) 00121 { 00122 00123 00124 if((hashMappedData[hashIncrement].size() - 1) == 0) 00125 { 00126 // no need to push the data as this quad should not be rendered 00127 // with 0 height 00128 hashIncrement++; 00129 00130 xPos = xPos + _individualCellWidth; 00131 continue; 00132 00133 } 00134 float tempBottomX4 = xPos; 00135 float tempBottomZ4 = zPos; 00136 float tempBottomY4 = 0.0; 00137 00138 float tempBottomX3 = xPos + _individualCellWidth; 00139 float tempBottomZ3 = zPos; 00140 float tempBottomY3 = 0.0; 00141 00142 float tempBottomX2 = xPos + _individualCellWidth; 00143 float tempBottomZ2 = zPos + _individualCellHeight; 00144 float tempBottomY2 = 0.0; 00145 00146 float tempBottomX1 = xPos; 00147 float tempBottomZ1 = zPos + _individualCellHeight; 00148 float tempBottomY1 = 0.0; 00149 00150 float tempTopX4 = tempBottomX4; 00151 float tempTopZ4 = tempBottomZ4; 00152 float tempTopY4 = (hashMappedData[hashIncrement].size() - 1) * _heightUpScaleFactor; 00153 00154 float tempTopX3 = tempBottomX3; 00155 float tempTopZ3 = tempBottomZ3; 00156 float tempTopY3 = (hashMappedData[hashIncrement].size() - 1) * _heightUpScaleFactor; 00157 00158 float tempTopX2 = tempBottomX2; 00159 float tempTopZ2 = tempBottomZ2; 00160 float tempTopY2 = (hashMappedData[hashIncrement].size() - 1) * _heightUpScaleFactor; 00161 00162 float tempTopX1 = tempBottomX1; 00163 float tempTopZ1 = tempBottomZ1; 00164 float tempTopY1 = (hashMappedData[hashIncrement].size() - 1) * _heightUpScaleFactor; 00165 00166 00167 //std::cout<<"tempTopX1:"<<tempTop 00168 00169 if((hashMappedData[hashIncrement].size() - 1) != 0) 00170 { 00171 std::cout<<"hashmap Y:"<<(hashMappedData[hashIncrement].size() - 1)<<"\n"; 00172 } 00173 00174 // top first - clockwise 00175 o_verticesSetOfCubeBarsGraph.push_back(cv::Point3f(tempTopX1,tempTopY1,tempTopZ1)); 00176 o_verticesSetOfCubeBarsGraph.push_back(cv::Point3f(tempTopX2,tempTopY2,tempTopZ2)); 00177 o_verticesSetOfCubeBarsGraph.push_back(cv::Point3f(tempTopX3,tempTopY3,tempTopZ3)); 00178 o_verticesSetOfCubeBarsGraph.push_back(cv::Point3f(tempTopX4,tempTopY4,tempTopZ4)); 00179 00180 // bottom next - clockwise 00181 o_verticesSetOfCubeBarsGraph.push_back(cv::Point3f(tempBottomX1,tempBottomY1,tempBottomZ1)); 00182 o_verticesSetOfCubeBarsGraph.push_back(cv::Point3f(tempBottomX2,tempBottomY2,tempBottomZ2)); 00183 o_verticesSetOfCubeBarsGraph.push_back(cv::Point3f(tempBottomX3,tempBottomY3,tempBottomZ3)); 00184 o_verticesSetOfCubeBarsGraph.push_back(cv::Point3f(tempBottomX4,tempBottomY4,tempBottomZ4)); 00185 00186 // incerement the hash index as we move on to the next row 00187 hashIncrement++; 00188 00189 xPos = xPos + _individualCellWidth; 00190 00191 } 00192 00193 xPos = _scaledTopXTranslated; 00194 zPos = zPos + _individualCellHeight; 00195 00196 00197 } 00198 00199 } 00200 00201 00202 void ThreeDStatsGeneration::generateInterpolatedDataForAllRallies(std::vector<std::vector<cv::Point3f> > &_inputPlayerBallPts, 00203 std::vector<std::vector<int> > &_playerDeepestPtIndices, 00204 float _inputDistanceFrmKinectToPlayArea, 00205 std::vector<std::vector<cv::Point3f> > &o_interpPlayerBallPts, 00206 std::vector<float> &o_speedData) 00207 { 00208 00209 int tempFirstIndex = 0; 00210 int tempLastIndex = 0; 00211 00212 float paramT = 0.0; 00213 00214 float deltaT = 0.0; 00215 00216 // coeffs for velocity calculation 00217 float coeffX,coeffY,coeffZ; 00218 00219 coeffX = coeffY = coeffZ = 0.0; 00220 00221 float tempVelocity = 0.0; 00222 float tempCoeffSquared = 0.0; 00223 00224 /* magnet::math::Spline splineX,splineY,splineZ; 00225 00226 splineX.clear(); 00227 splineY.clear(); 00228 splineZ.clear();*/ 00229 00230 00231 // for each rally 00232 for(int i=0;i<_inputPlayerBallPts.size();i++) 00233 { 00234 00235 00236 // if there is no data in the depth point for this rally, 00237 // then we have come to the end dummy space which we 00238 // pushed in for rally direction change in ballprocessing 00239 if(_playerDeepestPtIndices[i].size() == 0) 00240 { 00241 continue; 00242 } 00243 // allocate data for our grand big interpolated data 00244 // for this rally 00245 o_interpPlayerBallPts.push_back(std::vector<cv::Point3f>()); 00246 o_interpPlayerBallPts[i].clear(); 00247 00248 // allocate data correspondingly for our velocity 00249 // for the current rally impact points 00250 //o_speedData.push_back(std::vector<float> ()); 00251 //o_speedData[i].clear(); 00252 00253 // for each depth point in the current rally, 00254 // extract from rally[j] to rally[j+1] 00255 // execute this for depthsize + 1 as we need to extract from one start depth 00256 // to another depth..so we need j and j+1 always 00257 // we will understand when we write the code////note to self: fill this comment 00258 for(int j=0;j<(_playerDeepestPtIndices[i].size() + 1);j++) 00259 { 00260 // when j == size - 1 00261 // then we need to extract the rest from _inputpoints[j] until _inputpoints[_inputpoints.size - 1] 00262 //tempFirstIndex = j; 00263 00264 if(j == 0) 00265 { 00266 tempFirstIndex = j; 00267 if(_playerDeepestPtIndices[i][j] != -1) 00268 { 00269 tempLastIndex = _playerDeepestPtIndices[i][j]; 00270 } 00271 // if -1, then no impact data detected for this rally, so extract from first to last of the rally 00272 // nothing in intermediate 00273 else 00274 { 00275 tempLastIndex = (_inputPlayerBallPts[i].size() - 1); 00276 } 00277 } 00278 else if (j == _playerDeepestPtIndices[i].size()) 00279 { 00280 tempFirstIndex = _playerDeepestPtIndices[i][(j-1)]; 00281 tempLastIndex = (_inputPlayerBallPts[i].size() - 1); 00282 } 00283 else 00284 { 00285 tempFirstIndex = _playerDeepestPtIndices[i][(j-1)]; 00286 tempLastIndex = _playerDeepestPtIndices[i][j]; 00287 } 00288 00289 00290 std::vector<cv::Point3f>::const_iterator first = _inputPlayerBallPts[i].begin() + tempFirstIndex; 00291 std::vector<cv::Point3f>::const_iterator last = _inputPlayerBallPts[i].begin() + tempLastIndex + 1; 00292 00293 std::vector<cv::Point3f> tempExtractedPts(first,last); 00294 00295 // sort this temp vector of 3fs 00296 std::sort(tempExtractedPts.begin(),tempExtractedPts.end(),compare3FOnX); // need to check if this really compares 00297 00298 std::vector<cv::Point3f> tempExtractedPtsInWorldCoords; 00299 tempExtractedPtsInWorldCoords.resize(tempExtractedPts.size()); 00300 // convert to world coords before interpolating 00301 convertToWorldFromScreen(tempExtractedPts,tempExtractedPtsInWorldCoords, _inputDistanceFrmKinectToPlayArea); 00302 00303 00304 // 3 buffers for interpX,interpY, interpZ 00305 std::vector<float> _bufferX,_bufferY,_bufferZ; 00306 00307 _bufferX.clear(); 00308 _bufferY.clear(); 00309 _bufferZ.clear(); 00310 00311 paramT = 0.0; 00312 deltaT = 0.0; 00313 00314 // for each extracted stream different coeffs, so reset to 0 00315 coeffX = coeffY = coeffZ = 0.0; 00316 00317 /* splineX.clear(); 00318 splineY.clear(); 00319 splineZ.clear();*/ 00320 00321 if(tempExtractedPtsInWorldCoords.size() != 0) 00322 { 00323 deltaT = (float)(1.0/tempExtractedPtsInWorldCoords.size()); 00324 } 00325 00326 for(int xIndex =0; xIndex<tempExtractedPtsInWorldCoords.size();xIndex++) 00327 { 00328 00329 00330 m_quadFitX->addPoints(cv::Point2f(paramT,tempExtractedPtsInWorldCoords[xIndex].x)); 00331 00332 /*splineX.addPoint(paramT,tempExtractedPtsInWorldCoords[xIndex].x);*/ 00333 00334 paramT += deltaT; 00335 00336 00337 } 00338 00339 /* 00340 //A spline which turns into a parabola at the boundaries. 00341 00342 //This BC does not need extra parameters, so just the condition is 00343 //enough. 00344 splineX.setLowBC(magnet::math::Spline::PARABOLIC_RUNOUT_BC); 00345 splineX.setHighBC(magnet::math::Spline::PARABOLIC_RUNOUT_BC); 00346 00347 for (double x = (-0.2); x <= 1.2001; x += 0.005) 00348 { 00349 float temp = splineX(x); 00350 00351 _bufferX.push_back(temp); 00352 } 00353 */ 00354 00355 m_quadFitX->processParametricPoints3D(_bufferX,coeffX); 00356 00357 00358 paramT = 0.0; 00359 for(int yIndex =0; yIndex<tempExtractedPtsInWorldCoords.size();yIndex++) 00360 { 00361 00362 00363 m_quadFitY->addPoints(cv::Point2f(paramT,tempExtractedPtsInWorldCoords[yIndex].y)); 00364 00365 /*splineY.addPoint(paramT,tempExtractedPtsInWorldCoords[yIndex].y);*/ 00366 00367 paramT += deltaT; 00368 00369 00370 } 00371 00372 /* 00373 //A spline which turns into a parabola at the boundaries. 00374 00375 //This BC does not need extra parameters, so just the condition is 00376 //enough. 00377 splineY.setLowBC(magnet::math::Spline::PARABOLIC_RUNOUT_BC); 00378 splineY.setHighBC(magnet::math::Spline::PARABOLIC_RUNOUT_BC); 00379 00380 for (double x = (-0.2); x <= 1.2001; x += 0.005) 00381 { 00382 float temp = splineY(x); 00383 00384 _bufferY.push_back(temp); 00385 } 00386 */ 00387 00388 m_quadFitY->processParametricPoints3D(_bufferY,coeffY); 00389 00390 00391 paramT = 0.0; 00392 for(int zIndex =0; zIndex<tempExtractedPtsInWorldCoords.size();zIndex++) 00393 { 00394 00395 00396 m_quadFitZ->addPoints(cv::Point2f(paramT,tempExtractedPtsInWorldCoords[zIndex].z)); 00397 00398 /*splineZ.addPoint(paramT,tempExtractedPtsInWorldCoords[zIndex].z);*/ 00399 00400 paramT += deltaT; 00401 00402 00403 } 00404 00405 /* 00406 //A spline which turns into a parabola at the boundaries. 00407 00408 //This BC does not need extra parameters, so just the condition is 00409 //enough. 00410 splineZ.setLowBC(magnet::math::Spline::PARABOLIC_RUNOUT_BC); 00411 splineZ.setHighBC(magnet::math::Spline::PARABOLIC_RUNOUT_BC); 00412 00413 for (double x = (-0.2); x <= 1.2001; x += 0.005) 00414 { 00415 float temp = splineZ(x); 00416 00417 _bufferZ.push_back(temp); 00418 } 00419 */ 00420 00421 m_quadFitZ->processParametricPoints3D(_bufferZ,coeffZ); 00422 00423 00424 if((_bufferX.size() != 0) && (_bufferY.size() != 0) && (_bufferZ.size() != 0)) // checking if we have data in the buffers..need to place additional check to see if all the sizes are equal 00425 { 00426 for(int fillIndex =0; fillIndex<_bufferX.size();fillIndex++) 00427 { 00428 00429 00430 std::cout<<"BufferX:"<<_bufferX[fillIndex]<<"BufferY:"<<_bufferY[fillIndex]<<"BufferZ:"<<_bufferZ[fillIndex]<<"\n"; 00431 00432 // our grand data (for this rally for this extracted chunk) is pushed in 00433 o_interpPlayerBallPts[i].push_back(cv::Point3f(_bufferX[fillIndex],_bufferY[fillIndex],_bufferZ[fillIndex])); 00434 00435 00436 } 00437 } 00438 00439 // push speed data for current rally's current extracted stream 00440 // made it single dimensional as we do not need rally specific impact points 00441 // so its a straight 1D vector corresponding to impactpoints3d vector 00442 // filled thru the calculateimpactptin3dfromindices function 00443 00444 // this check will filter other coeeficients which are actually not at the depth index, 00445 // because, apart from depth index, we also pass in 00446 // parts of array from o to depthindex or from deothindex to next depthindex 00447 // where the intermediate depth index wud push 2 different speeds 00448 // for the same point 00449 // so we should avoid pushing speed at those times 00450 00451 tempCoeffSquared = pow(coeffX,2) + pow(coeffY,2) + pow(coeffZ,2); 00452 if(tempCoeffSquared <= 0.0) 00453 { 00454 tempVelocity = 0.0; 00455 } 00456 else 00457 { 00458 tempVelocity = sqrt(tempCoeffSquared); 00459 } 00460 00461 //std::cout<<"TempVelocity:"<<tempVelocity<<"mpersec\n"; 00462 00463 if(tempLastIndex == _playerDeepestPtIndices[i][j]) 00464 { 00465 o_speedData.push_back(tempVelocity); 00466 } 00467 00468 if(tempLastIndex == (_inputPlayerBallPts[i].size() - 1)) 00469 { 00470 // we break out of the for loop 00471 // because if lastindex has a value representing the last element of the 00472 // rally point, there is nothing more to extract 00473 // this last point could have been reached in many ways, 00474 // lik when there is no impact data, 0 to last is extracted 00475 // or if legitemately we have come until the last index like 00476 // 0 ---- impactindex1 ------ impactindex2 ----- lastindex 00477 // or 0 -----impactindex1-----impactindex2(which itself might be referring to the lastindex, 00478 // of the rally, so we should itself break out instead of extracting forward) 00479 break; 00480 } 00481 00482 } 00483 00484 } 00485 } 00486 00487 00488 void ThreeDStatsGeneration::convertToWorldFromScreen(std::vector<cv::Point3f>& _inputPointVec, std::vector<cv::Point3f>& o_PointsInWorld, float _inputDistanceFrmKinectToPlayArea) 00489 { 00490 // THIS IS TAKEN FROM THE OPENCV WIKI PAGE... 00491 // ALTERNATIVELY, THE TANGENT FORMULAE IN THE IMAGE SAVED IN DESKTOP CAN ALSO BE USED...DO THIS LATER IF ACCURACY SEEMS MISSING 00492 // or from this blog http://magicalkinect.wordpress.com/2013/02/16/metric-measuring-using-kinect/ 00493 00494 // float minDistance = -10; 00495 // float scaleFactor = 0.0021; 00496 // float x = (_iScreen - 640 / 2) * (_zDepth + minDistance) * scaleFactor * (640/480); 00497 // float y = (_jScreen - 480 / 2) * (_zDepth + minDistance) * scaleFactor; 00498 00499 for (int i=0;i<_inputPointVec.size();i++) 00500 { 00501 float x = (_inputPointVec[i].x/640 - 0.5) * _inputPointVec[i].z * 1.111467f; 00502 float y = (0.5 - _inputPointVec[i].y/480) * _inputPointVec[i].z * 0.833599f; 00503 00504 // we can dereference and assign instead of push back because 00505 // we have resized this vector in the parent function 00506 00507 // my conversion in the view of the kinect 00508 //o_PointsInWorld[i] = cv::Point3f(-(y),(1 - _inputPointVec[i].z),-x); 00509 00510 // this should actually be ditancefromkinecttotable - z as the table is at 0.762 00511 // so if the ball is at 0.762 from kinect it will be 0 frm the table 00512 // if its at 0.562 from the kinect, it would be at 0.2 from the table 00513 o_PointsInWorld[i] = cv::Point3f((x),fabs(_inputDistanceFrmKinectToPlayArea - _inputPointVec[i].z),-(y)); 00514 } 00515 00516 // float temp = (58.12/2) * (3.14/180); 00517 // std::cout<<".........."<< (2 * tan(temp)); 00518 00519 // std::cout<<" X, Y and Z are:"<<x<<"::"<<y<<"::"<<_zDepth<<"\n"; 00520 00521 00522 00523 // m_ballPoints.push_back(cv::Point3f(x,y,(_zDepth))); 00524 00525 00526 00527 } 00528 00529 00530 00531 00532 // rally specific trajectory if needed can be calculated using the rally number input, full ball points, 00533 // rally number should be valid...0 to size - 1 of the ball pts array 00534 00535 00536 //SPEED PITCH MAP 00537 00538 // once we have equation of 3d data for each rally, we can calculate and display the velocity at the impact points 00539 // for each player and use color coding to display the output 00540 00541 // this will convert the XY of impact point into XZ of world coordinates to 00542 // project in opengl 00543 // the resulting vector set would map directly to the 1D speed vector populated 00544 // in the getinterpolateddataforallrallies function 00545 void ThreeDStatsGeneration::calculatePlayerImpactPtsIn3DFromIndices(std::vector<std::vector<cv::Point3f> > &_inputPlayerBallPts, 00546 std::vector<std::vector<int> > &_playerDeepestPtIndices, 00547 std::vector<cv::Point3f> &o_playerImpactPtsAllRallies3D) 00548 { 00549 if(_playerDeepestPtIndices.size() != 0) 00550 { 00551 for(int i = 0;i<_playerDeepestPtIndices.size();i++) 00552 { 00553 for(int j=0;j<_playerDeepestPtIndices[i].size();j++) 00554 { 00555 00556 // if the impact point index is not -1, then there is an impact point for this rally 00557 if(_playerDeepestPtIndices[i][j] != -1) 00558 { 00559 00560 00561 00562 float x = (_inputPlayerBallPts[i][_playerDeepestPtIndices[i][j]].x/640 - 0.5) * 00563 _inputPlayerBallPts[i][_playerDeepestPtIndices[i][j]].z * 1.111467f; 00564 00565 float y = (0.5 - _inputPlayerBallPts[i][_playerDeepestPtIndices[i][j]].y/480) * 00566 _inputPlayerBallPts[i][_playerDeepestPtIndices[i][j]].z * 0.833599f; 00567 00568 // the plane is going to be at 0,0,0 only 00569 // and the Y is going to be 0 as this is going to be a projection of 00570 // XY of pixels into XZ in opengl but in world coords as opposed to 00571 // pixel coords 00572 // remeber this is scaled up to 10 00573 o_playerImpactPtsAllRallies3D.push_back(cv::Point3f(10 * x,0.0,-(10 * y))); 00574 } 00575 else 00576 { 00577 std::cout<<"No impact point for Rally number:"<<i<<"..so skipping to next rally.\n"; 00578 } 00579 00580 } 00581 } 00582 } 00583 else 00584 { 00585 std::cout<<"No data in indices array for the player.\n"; 00586 } 00587 } 00588