public void Rotate(Chessboard3DReprezentation boardRepresentation, CameraSpacePoint[] csp) { var firstVector = boardRepresentation.FieldVector1; var secondVector = boardRepresentation.FieldVector2; var cornerPoint = boardRepresentation.CornerOfChessboard; firstVector = MyVector3DStruct.Normalize(firstVector); secondVector = MyVector3DStruct.Normalize(secondVector); var a = MyVector3DStruct.CrossProduct(ref firstVector, ref secondVector); var b = MyVector3DStruct.CrossProduct(ref secondVector, ref firstVector); var xVec = new MyVector3DStruct(); var yVec = new MyVector3DStruct(); var zVec = new MyVector3DStruct(); // get new base based on two known vectors and their cross product if ((a.z > 0 && b.z < 0)) // originally (a.z > 0 && b.z < 0) { zVec = a; // a yVec = MyVector3DStruct.Normalize(MyVector3DStruct.CrossProduct(ref xVec, ref zVec)); } else if ((a.z < 0 && b.z > 0)) // originally (a.z < 0 && b.z > 0) { zVec = b; // b yVec = MyVector3DStruct.Normalize(MyVector3DStruct.CrossProduct(ref xVec, ref zVec)); } else { throw new InvalidOperationException(); } // normalize base xVec = MyVector3DStruct.Normalize(firstVector); yVec = MyVector3DStruct.Normalize(secondVector); zVec = MyVector3DStruct.Normalize(zVec); // get inverse mapping to base double[,] matrix = { { xVec.x, yVec.x, zVec.x }, { xVec.y, yVec.y, zVec.y }, { xVec.z, yVec.z, zVec.z } }; var inverseMatrix = matrix.Inverse(); // move all points for (int i = 0; i < csp.Length; i++) { // translation to corner point var nx = (float)(csp[i].X - cornerPoint.x); var ny = (float)(csp[i].Y - cornerPoint.y); var nz = (float)(csp[i].Z - cornerPoint.z); // rotation around it to given base csp[i].X = (float)(inverseMatrix[0, 0] * nx + inverseMatrix[0, 1] * ny + inverseMatrix[0, 2] * nz); csp[i].Y = (float)(inverseMatrix[1, 0] * nx + inverseMatrix[1, 1] * ny + inverseMatrix[1, 2] * nz); csp[i].Z = (float)(inverseMatrix[2, 0] * nx + inverseMatrix[2, 1] * ny + inverseMatrix[2, 2] * nz); } }
/// <summary> /// Finds most suitable chessboard fitting real points /// </summary> public static Chessboard3DReprezentation ChessboardFittingAlgorithm(List <Point2D> contractedPoints, ChessboardTrackingCompleteData chessboardData) { const int takeXNearestNeighbors = 7; var contractedPoints3D = ConvertIntersectionsTo3D(contractedPoints, chessboardData); var contractedPoints3DasStruct = contractedPoints3D.Select(x => new MyVector3DStruct(x.X, x.Y, x.Z)).ToArray(); double lowestError = double.MaxValue; Chessboard3DReprezentation boardRepresentation = null; Parallel.ForEach(contractedPoints3DasStruct, csp => { // take _ nearest neighbors var neighbors = contractedPoints3DasStruct.OrderBy((MyVector3DStruct x) => MyVector3DStruct.Distance(ref x, ref csp)).Take(takeXNearestNeighbors).ToArray(); // take all pairs of neighbors for (int i = 0; i < neighbors.Length; i++) { for (int j = i + 1; j < neighbors.Length; j++) { var first = neighbors[i]; var second = neighbors[j]; var firstPoint = new MyVector3DStruct(first.x, first.y, first.z); var secondPoint = new MyVector3DStruct(second.x, second.y, second.z); // st. point + 2 vectors var initialPoint = new MyVector3DStruct(csp.x, csp.y, csp.z); var firstVector = MyVector3DStruct.Difference(ref firstPoint, ref initialPoint); var secondVector = MyVector3DStruct.Difference(ref secondPoint, ref initialPoint); // perpendicularity check double angleBetweenVectors = MyVector3DStruct.AngleInDeg(ref firstVector, ref secondVector); if (!(angleBetweenVectors < 91.4 && angleBetweenVectors > 88.6)) { break; } // length check double ratio = firstVector.Magnitude() / secondVector.Magnitude(); if (!(ratio > 0.85f && ratio < 1.15f)) { break; } // ensure right mutual position of first and second vector - otherwise switch them if (MyVector3DStruct.CrossProduct(ref firstVector, ref secondVector).z < 0) { var temp = firstVector; firstVector = secondVector; secondVector = temp; } // length normalization of vectors double averageLength = (firstVector.Magnitude() + secondVector.Magnitude()) / 2; firstVector = MyVector3DStruct.MultiplyByNumber(MyVector3DStruct.Normalize(ref firstVector), averageLength); secondVector = MyVector3DStruct.MultiplyByNumber(MyVector3DStruct.Normalize(ref secondVector), averageLength); var negatedFirstVector = MyVector3DStruct.Negate(ref firstVector); var negatedSecondVector = MyVector3DStruct.Negate(ref secondVector); // locate all possible starting points for (int f = 0; f < 9; f++) { for (int s = 0; s < 9; s++) { var startingPoint = MyVector3DStruct.Addition( MyVector3DStruct.Addition( MyVector3DStruct.MultiplyByNumber(negatedFirstVector, f), MyVector3DStruct.MultiplyByNumber(negatedSecondVector, s) ) , initialPoint ); // generate all possible chessboards for given starting point and compute their error double currentError = 0; for (int ff = 0; ff < 9; ff++) { for (int ss = 0; ss < 9; ss++) { var currentPoint = MyVector3DStruct.Addition( startingPoint, MyVector3DStruct.Addition( MyVector3DStruct.MultiplyByNumber(firstVector, ff), MyVector3DStruct.MultiplyByNumber(secondVector, ss) ) ); var closestPointDistance = contractedPoints3DasStruct.Min(x => MyVector3DStruct.Distance(ref currentPoint, new MyVector3DStruct(x.x, x.y, x.z))); // clipping of max error per point var clippingDistance = chessboardData.UserParameters.ClippedDistanecInChessboardFittingMetric / 1000f; closestPointDistance = closestPointDistance > clippingDistance ? clippingDistance : closestPointDistance; if (chessboardData.UserParameters.IsDistanceMetricInChessboardFittingExperimental) { closestPointDistance = closestPointDistance * closestPointDistance; } currentError += closestPointDistance; } } if (currentError < lowestError) { lowestError = currentError; boardRepresentation = new Chessboard3DReprezentation(startingPoint, firstVector, secondVector); } } } } } }); return(boardRepresentation); }