PositionCalculator.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. using Assets.StreetLight.Interfaces;
  2. using Assets.StreetLight.Poco;
  3. using MathNet.Numerics.LinearAlgebra;
  4. using MathNet.Numerics.LinearAlgebra.Double;
  5. using MathNet.Numerics.Statistics;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. using System.Security.Cryptography;
  10. using UnityEngine;
  11. using Random = System.Random;
  12. namespace Assets.StreetLight.Scripts
  13. {
  14. public class PositionCalculator
  15. {
  16. private List<CalibrationPoint> calibrationVectors;
  17. private Matrix<double> homography;
  18. public PositionCalculator(List<CalibrationPoint> calibrationVectors)
  19. {
  20. this.calibrationVectors = calibrationVectors;
  21. CalculateHomographyRansac();
  22. }
  23. /// <summary>
  24. /// Uses RANSAC and an SVD to calculate the homography from many calibration points.
  25. /// </summary>
  26. /// <exception cref="InvalidOperationException"></exception>
  27. /// <remarks>Heavily based on the formulas and algorithms discussed in Computer Vision I by Stefan Roth</remarks>
  28. private void CalculateHomographyRansac()
  29. {
  30. if (!(calibrationVectors?.Count >= 4))
  31. {
  32. throw new InvalidOperationException("Must have at least 4 correspondences to calculate a homography.");
  33. }
  34. var correspondences = calibrationVectors.Select(v =>
  35. new HomographyCorrespondence(DenseVector.OfArray(new double[] { v.WorldPosition.x, v.WorldPosition.z }), DenseVector.OfArray(new double[] { v.UnityPosition.x, v.UnityPosition.z }))).ToList();
  36. var iterations = RansacIterations(0.35f, 4, 0.99f);
  37. var threshold = 5.0f;
  38. var maxInliers = 0;
  39. Matrix<double> H = Matrix<double>.Build.Random(0, 0);
  40. List<HomographyCorrespondence> inliers = new List<HomographyCorrespondence>();
  41. var random = new Random();
  42. for (int i = 0; i < iterations; i++)
  43. {
  44. var sample = correspondences.OrderBy(_ => random.Next()).Take(4).ToArray();
  45. var (conditionedCorrespondences, T1, T2) = ConditionPoints(sample);
  46. var (currentH, HC) = ComputeHomography(conditionedCorrespondences, T1, T2);
  47. var homographyDistances = ComputeHomographyDistances(currentH, correspondences);
  48. var currentInliers = FindInliers(correspondences, homographyDistances, threshold);
  49. var numberOfCurrentInliers = currentInliers.Count;
  50. if (currentInliers.Count > maxInliers)
  51. {
  52. H = currentH;
  53. maxInliers = currentInliers.Count;
  54. inliers = currentInliers;
  55. }
  56. }
  57. var recomputedHomography = RecomputeHomography(inliers);
  58. homography = recomputedHomography;
  59. }
  60. private Matrix<double> RecomputeHomography(List<HomographyCorrespondence> inliers)
  61. {
  62. var (conditionedPoints, T1, T2) = ConditionPoints(inliers);
  63. var (H, HC) = ComputeHomography(conditionedPoints, T1, T2);
  64. return H;
  65. }
  66. private List<HomographyCorrespondence> FindInliers(IList<HomographyCorrespondence> correspondences, IList<double> homographyDistances, float threshold)
  67. {
  68. var inliers = new List<HomographyCorrespondence>();
  69. for (int i = 0; i < correspondences.Count; i++)
  70. {
  71. var distance = homographyDistances[i];
  72. if (distance < threshold)
  73. {
  74. inliers.Add(correspondences[i]);
  75. }
  76. }
  77. return inliers;
  78. }
  79. private IList<double> ComputeHomographyDistances(Matrix<double> currentH, IEnumerable<HomographyCorrespondence> correspondences)
  80. {
  81. var distances = new List<double>();
  82. var inverseH = currentH.Inverse();
  83. foreach (var correspondence in correspondences)
  84. {
  85. var x1 = correspondence.WorldPosition;
  86. var x2 = correspondence.UnityPosition;
  87. var x1Transformed = TransformPoint(x1, currentH);
  88. var x2Transformed = TransformPoint(x2, inverseH);
  89. distances.Add(Math.Pow((x1Transformed - x2).L1Norm(), 2) + Math.Pow((x1 - x2Transformed).L1Norm(), 2));
  90. }
  91. return distances;
  92. }
  93. private Vector<double> TransformPoint(Vector<double> point, Matrix<double> homography)
  94. {
  95. if (point.Count != 2 || homography.RowCount != 3 || homography.ColumnCount != 3)
  96. {
  97. throw new ArgumentException();
  98. }
  99. var homogeneousPoint = DenseVector.OfArray(new double[] { point[0], point[1], 1 });
  100. var transformedHomogeneousPoint = homography * homogeneousPoint;
  101. var normalizedTransformedHomogeneousPoint = transformedHomogeneousPoint / transformedHomogeneousPoint[2];
  102. var transformedPoint = DenseVector.OfArray(new double[] { normalizedTransformedHomogeneousPoint[0], normalizedTransformedHomogeneousPoint[1] });
  103. return transformedPoint;
  104. }
  105. private (Matrix<double>, Matrix<double>) ComputeHomography(ICollection<(Vector<double>, Vector<double>)> conditionedCorrespondences, Matrix<double> t1, Matrix<double> t2)
  106. {
  107. var numberOfCorrespondences = conditionedCorrespondences.Count;
  108. var A = new List<double[]>();
  109. foreach (var correspondence in conditionedCorrespondences)
  110. {
  111. var point1 = correspondence.Item1 / correspondence.Item1[2];
  112. var point2 = correspondence.Item2 / correspondence.Item2[2];
  113. A.Add(new double[] { 0, 0, 0, point1[0], point1[1], point1[2], -point2[1] * point1[0], -point2[1] * point1[1], -point2[1] * point1[2] });
  114. A.Add(new double[] { -point1[0], -point1[1], -point1[2], 0, 0, 0, point2[0] * point1[0], point2[0] * point1[1], point2[0] * point1[2] });
  115. }
  116. var matrix = DenseMatrix.OfRowArrays(A);
  117. var svd = matrix.Svd(true);
  118. var h = svd.VT.EnumerateRows().Last();
  119. var HC = DenseMatrix.OfArray(new[,]
  120. {
  121. { h[0], h[1], h[2] },
  122. { h[3], h[4], h[5] },
  123. { h[6], h[7], h[8] }
  124. });
  125. var normalizedHC = HC / HC[2, 2];
  126. var H = t2.Inverse() * (normalizedHC * t1);
  127. var normalizedH = H / H[2, 2];
  128. return (normalizedH, normalizedHC);
  129. }
  130. private (ICollection<(Vector<double>, Vector<double>)>, Matrix<double>, Matrix<double>) ConditionPoints(IEnumerable<HomographyCorrespondence> correspondences)
  131. {
  132. var worldPoints = correspondences.Select(i => i.WorldPosition);
  133. var s = worldPoints.Select(i => i.Count).Max(i => i) / 2;
  134. var meanX = worldPoints.Select(i => i[0]).Mean();
  135. var meanY = worldPoints.Select(i => i[1]).Mean();
  136. var T = DenseMatrix.OfArray(new double[,]
  137. {
  138. {1 / s, 0, - meanX / s},
  139. {0, 1 / s, - meanY / s},
  140. {0, 0, 1}
  141. });
  142. var unityPoints = correspondences.Select(i => i.UnityPosition);
  143. var s_prime = unityPoints.Select(i => i.Count).Max(i => i) / 2;
  144. var meanX_prime = unityPoints.Select(i => i[0]).Mean();
  145. var meanY_prime = unityPoints.Select(i => i[1]).Mean();
  146. var T_prime = DenseMatrix.OfArray(new double[,]
  147. {
  148. {1 / s_prime, 0, - meanX_prime / s_prime},
  149. {0, 1 / s_prime, - meanY_prime / s_prime},
  150. {0, 0, 1}
  151. });
  152. var result = new List<(Vector<double>, Vector<double>)>();
  153. foreach (var correspondence in correspondences)
  154. {
  155. var homogeneousPointWorld = DenseVector.OfArray(new double[] { correspondence.WorldPosition[0], correspondence.WorldPosition[1], 1 });
  156. var homogeneousTransformedPointWorld = T * homogeneousPointWorld;
  157. var homogeneousPointUnity = DenseVector.OfArray(new double[] { correspondence.UnityPosition[0], correspondence.UnityPosition[1], 1 });
  158. var homogeneousTransformedPointUnity = T_prime * homogeneousPointUnity;
  159. //if (new[] { homogeneousTransformedPointUnity[1], homogeneousTransformedPointUnity[0], homogeneousTransformedPointWorld[1], homogeneousTransformedPointWorld[0] }.Any(i => i < -1 || i > 1))
  160. //{
  161. //}
  162. result.Add((homogeneousTransformedPointWorld, homogeneousTransformedPointUnity));
  163. }
  164. return (result, T, T_prime);
  165. }
  166. private int RansacIterations(float inlierProbability, int samplesPerIteration, float successProbability)
  167. {
  168. return (int)Math.Ceiling(Math.Log(1 - successProbability, 1 - Math.Pow(inlierProbability, samplesPerIteration)));
  169. }
  170. private void CalculateHomographySimple()
  171. {
  172. if (!(calibrationVectors?.Count >= 4))
  173. {
  174. throw new InvalidOperationException("Must have at least 4 correspondences to calculate a homography.");
  175. }
  176. var cv = calibrationVectors;
  177. Matrix<double> matrixA = DenseMatrix.OfArray(new double[,]
  178. {
  179. {cv[0].WorldPosition.x, cv[0].WorldPosition.z, 1, 0, 0, 0, -cv[0].UnityPosition.x*cv[0].WorldPosition.x, -cv[0].UnityPosition.x*cv[0].WorldPosition.z},
  180. {0, 0, 0, cv[0].WorldPosition.x, cv[0].WorldPosition.z, 1, -cv[0].UnityPosition.z*cv[0].WorldPosition.x, -cv[0].UnityPosition.z*cv[0].WorldPosition.z},
  181. {cv[1].WorldPosition.x, cv[1].WorldPosition.z, 1, 0, 0, 0, -cv[1].UnityPosition.x*cv[1].WorldPosition.x, -cv[1].UnityPosition.x*cv[1].WorldPosition.z},
  182. {0, 0, 0, cv[1].WorldPosition.x, cv[1].WorldPosition.z, 1, -cv[1].UnityPosition.z*cv[1].WorldPosition.x, -cv[1].UnityPosition.z*cv[1].WorldPosition.z},
  183. {cv[2].WorldPosition.x, cv[2].WorldPosition.z, 1, 0, 0, 0, -cv[2].UnityPosition.x*cv[2].WorldPosition.x, -cv[2].UnityPosition.x*cv[2].WorldPosition.z},
  184. {0, 0, 0, cv[2].WorldPosition.x, cv[2].WorldPosition.z, 1, -cv[2].UnityPosition.z*cv[2].WorldPosition.x, -cv[2].UnityPosition.z*cv[2].WorldPosition.z},
  185. {cv[3].WorldPosition.x, cv[3].WorldPosition.z, 1, 0, 0, 0, -cv[3].UnityPosition.x*cv[3].WorldPosition.x, -cv[3].UnityPosition.x*cv[3].WorldPosition.z},
  186. {0, 0, 0, cv[3].WorldPosition.x, cv[3].WorldPosition.z, 1, -cv[3].UnityPosition.z*cv[3].WorldPosition.x, -cv[3].UnityPosition.z*cv[3].WorldPosition.z}
  187. });
  188. Matrix<double> matrixB = DenseMatrix.OfArray(new double[,]
  189. {
  190. {cv[0].UnityPosition.x},
  191. {cv[0].UnityPosition.z},
  192. {cv[1].UnityPosition.x},
  193. {cv[1].UnityPosition.z},
  194. {cv[2].UnityPosition.x},
  195. {cv[2].UnityPosition.z},
  196. {cv[3].UnityPosition.x},
  197. {cv[3].UnityPosition.z}
  198. });
  199. var lambda = (matrixA.Transpose() * matrixA).Inverse() * matrixA.Transpose() * matrixB;
  200. homography = DenseMatrix.OfArray(new double[,]
  201. {
  202. { lambda[0,0], lambda[1,0], lambda[2,0]},
  203. { lambda[3,0], lambda[4,0], lambda[5,0]},
  204. { lambda[6,0], lambda[7,0], 1}
  205. });
  206. testCalibration();
  207. }
  208. public Vector3 CalculateUnityPosition(Person person)
  209. {
  210. return WorldPositionToUnityPosition(person.WorldPosition);
  211. }
  212. private Vector3 WorldPositionToUnityPosition(Vector3 worldPosition)
  213. {
  214. var vector = DenseVector.OfArray(new double[] { worldPosition.x, worldPosition.z, 1 });
  215. var output = homography * vector;
  216. var scaledOutput = output / output[2];
  217. var resultVector = new Vector3((float)scaledOutput[0], 0, (float)scaledOutput[1]);
  218. return resultVector;
  219. }
  220. /// <summary>
  221. /// For internal debugging only.
  222. /// </summary>
  223. private void testCalibration()
  224. {
  225. foreach (var c in calibrationVectors)
  226. {
  227. var test = DenseVector.OfArray(new double[] { c.WorldPosition.x, c.WorldPosition.z, 1 });
  228. var testOutput = homography * test;
  229. var scaledTestOutput = testOutput / testOutput[2];
  230. }
  231. }
  232. }
  233. }