PositionCalculator.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  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 Newtonsoft.Json;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Globalization;
  10. using System.IO;
  11. using System.Linq;
  12. using System.Security.Cryptography;
  13. using System.Threading;
  14. using UnityEngine;
  15. using Random = System.Random;
  16. namespace Assets.StreetLight.Scripts
  17. {
  18. public class PositionCalculator
  19. {
  20. private List<CalibrationPoint> calibrationVectors;
  21. private Matrix<double> homography;
  22. public PositionCalculator(List<CalibrationPoint> calibrationVectors)
  23. {
  24. this.calibrationVectors = calibrationVectors;
  25. CalculateHomographyRansac();
  26. }
  27. private List<HomographyCorrespondence> LoadSampleCorrespondences()
  28. {
  29. var p1 = File.ReadAllLines(Path.Combine(Application.streamingAssetsPath, "p1.csv"));
  30. var p2 = File.ReadAllLines(Path.Combine(Application.streamingAssetsPath, "p2.csv"));
  31. var correspondences = new List<HomographyCorrespondence>();
  32. int length = p1.Length;
  33. for (int i = 0; i < length; i++)
  34. {
  35. var p1Coordinates = p1[i].Split(',').Select(i => double.Parse(i, CultureInfo.InvariantCulture)).ToArray();
  36. var p2Coordinates = p2[i].Split(',').Select(i => double.Parse(i, CultureInfo.InvariantCulture)).ToArray();
  37. correspondences.Add(new HomographyCorrespondence(
  38. DenseVector.OfArray(p1Coordinates),
  39. DenseVector.OfArray(p2Coordinates)));
  40. }
  41. return correspondences;
  42. }
  43. /// <summary>
  44. /// Uses RANSAC and an SVD to calculate the homography from many calibration points.
  45. /// </summary>
  46. /// <exception cref="InvalidOperationException"></exception>
  47. /// <remarks>Heavily based on the formulas and algorithms discussed in Computer Vision I by Stefan Roth</remarks>
  48. private void CalculateHomographyRansac()
  49. {
  50. if (!(calibrationVectors?.Count >= 4))
  51. {
  52. throw new InvalidOperationException("Must have at least 4 correspondences to calculate a homography.");
  53. }
  54. var lines = calibrationVectors.Select(v => $"{v.WorldPosition.x.ToString("0." + new string('#', 50), CultureInfo.InvariantCulture)} {v.WorldPosition.z.ToString("0." + new string('#', 50), CultureInfo.InvariantCulture)} {v.UnityPosition.x.ToString("0." + new string('#', 50), CultureInfo.InvariantCulture)} {v.UnityPosition.z.ToString("0." + new string('#', 50), CultureInfo.InvariantCulture)}");
  55. var pairsString = string.Join(Environment.NewLine, lines);
  56. File.WriteAllText(@"C:\Git\git.tk.informatik.tu-darmstadt.de\StreetLight\Assets\StreamingAssets\pairs.csv", pairsString);
  57. var homographyString = File.ReadAllLines(@"C:\Git\git.tk.informatik.tu-darmstadt.de\StreetLight\Assets\StreamingAssets\homography.csv");
  58. var values = homographyString.Select(l => l.Split(' ').Select(s => double.Parse(s, CultureInfo.InvariantCulture)).ToArray()).ToArray();
  59. double[,] array = new double[3, 3];
  60. for (int i = 0; i < 3; i++)
  61. {
  62. for (int j = 0; j < 3; j++)
  63. {
  64. array[i, j] = values[i][j];
  65. }
  66. }
  67. homography = DenseMatrix.OfArray(array);
  68. return;
  69. var correspondences = calibrationVectors.Select(v =>
  70. new HomographyCorrespondence(DenseVector.OfArray(new double[] { v.WorldPosition.x, v.WorldPosition.z }), DenseVector.OfArray(new double[] { v.UnityPosition.x, v.UnityPosition.z }))).ToList();
  71. correspondences = LoadSampleCorrespondences();
  72. var iterations = RansacIterations(0.35f, 4, 0.99f);
  73. var threshold = 5.0f;
  74. var maxInliers = 0;
  75. Matrix<double> H = Matrix<double>.Build.Random(0, 0);
  76. List<HomographyCorrespondence> inliers = new List<HomographyCorrespondence>();
  77. var random = new Random();
  78. for (int i = 0; i < iterations; i++)
  79. {
  80. //var sample = correspondences.OrderBy(_ => random.Next()).Take(4).ToArray();
  81. var indices = new[] { 96, 93, 63, 118 };
  82. var sample = correspondences.Where((_, index) => indices.Contains(index)).ToArray();
  83. var (conditionedCorrespondences, T1, T2) = ConditionPoints(sample);
  84. var (currentH, HC) = ComputeHomography(conditionedCorrespondences, T1, T2);
  85. var homographyDistances = ComputeHomographyDistances(currentH, correspondences);
  86. var currentInliers = FindInliers(correspondences, homographyDistances, threshold);
  87. var numberOfCurrentInliers = currentInliers.Count;
  88. if (currentInliers.Count > maxInliers)
  89. {
  90. H = currentH;
  91. maxInliers = currentInliers.Count;
  92. inliers = currentInliers;
  93. }
  94. }
  95. var recomputedHomography = RecomputeHomography(inliers);
  96. homography = recomputedHomography;
  97. }
  98. private Matrix<double> RecomputeHomography(List<HomographyCorrespondence> inliers)
  99. {
  100. var (conditionedPoints, T1, T2) = ConditionPoints(inliers);
  101. var (H, HC) = ComputeHomography(conditionedPoints, T1, T2);
  102. return H;
  103. }
  104. private List<HomographyCorrespondence> FindInliers(IList<HomographyCorrespondence> correspondences, IList<double> homographyDistances, float threshold)
  105. {
  106. var inliers = new List<HomographyCorrespondence>();
  107. for (int i = 0; i < correspondences.Count; i++)
  108. {
  109. var distance = homographyDistances[i];
  110. if (distance < threshold)
  111. {
  112. inliers.Add(correspondences[i]);
  113. }
  114. }
  115. return inliers;
  116. }
  117. private IList<double> ComputeHomographyDistances(Matrix<double> currentH, IEnumerable<HomographyCorrespondence> correspondences)
  118. {
  119. var distances = new List<double>();
  120. currentH.MapInplace(q => Math.Round(q, 15));
  121. var inverseH = currentH.Inverse();
  122. var matrixString = string.Join(Environment.NewLine, currentH.EnumerateRows().Select(row => string.Join(" ", row.Enumerate().Select(number => number.ToString("0." + new string('#', 50), CultureInfo.InvariantCulture)))));
  123. File.WriteAllText(@"C:\Users\Nick\Desktop\currentH.csv", matrixString);
  124. var calc = new HomographyCalculator();
  125. //var foo = calc.InverseMatrix(currentH);
  126. foreach (var correspondence in correspondences)
  127. {
  128. var x1 = correspondence.WorldPosition;
  129. var x2 = correspondence.UnityPosition;
  130. var x1Transformed = TransformPoint(x1, currentH);
  131. var x2Transformed = TransformPoint(x2, inverseH);
  132. distances.Add(Math.Pow((x1Transformed - x2).L1Norm(), 2) + Math.Pow((x1 - x2Transformed).L1Norm(), 2));
  133. }
  134. return distances;
  135. }
  136. private Vector<double> TransformPoint(Vector<double> point, Matrix<double> homography)
  137. {
  138. if (point.Count != 2 || homography.RowCount != 3 || homography.ColumnCount != 3)
  139. {
  140. throw new ArgumentException();
  141. }
  142. var homogeneousPoint = DenseVector.OfArray(new double[] { point[0], point[1], 1 });
  143. var transformedHomogeneousPoint = homography * homogeneousPoint;
  144. var normalizedTransformedHomogeneousPoint = transformedHomogeneousPoint / transformedHomogeneousPoint[2];
  145. var transformedPoint = DenseVector.OfArray(new double[] { normalizedTransformedHomogeneousPoint[0], normalizedTransformedHomogeneousPoint[1] });
  146. return transformedPoint;
  147. }
  148. private (Matrix<double>, Matrix<double>) ComputeHomography(ICollection<(Vector<double>, Vector<double>)> conditionedCorrespondences, Matrix<double> t1, Matrix<double> t2)
  149. {
  150. var numberOfCorrespondences = conditionedCorrespondences.Count;
  151. var A = new List<double[]>();
  152. foreach (var correspondence in conditionedCorrespondences)
  153. {
  154. var point1 = correspondence.Item1 / correspondence.Item1[2];
  155. var point2 = correspondence.Item2 / correspondence.Item2[2];
  156. 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] });
  157. 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] });
  158. }
  159. var matrix = DenseMatrix.OfRowArrays(A);
  160. var svd = matrix.Svd(true);
  161. var h = svd.VT.EnumerateRows().Last();
  162. var HC = DenseMatrix.OfArray(new[,]
  163. {
  164. { h[0], h[1], h[2] },
  165. { h[3], h[4], h[5] },
  166. { h[6], h[7], h[8] }
  167. });
  168. var normalizedHC = HC / HC[2, 2];
  169. var H = t2.Inverse() * (normalizedHC * t1);
  170. var normalizedH = H / H[2, 2];
  171. return (normalizedH, normalizedHC);
  172. }
  173. private (ICollection<(Vector<double>, Vector<double>)>, Matrix<double>, Matrix<double>) ConditionPoints(IEnumerable<HomographyCorrespondence> correspondences)
  174. {
  175. var worldPoints = correspondences.Select(i => i.WorldPosition);
  176. var sx = worldPoints.Select(i => i[0]).Max(i => i) / 2;
  177. var sy = worldPoints.Select(i => i[1]).Max(i => i) / 2;
  178. var meanX = worldPoints.Select(i => i[0]).Mean();
  179. var meanY = worldPoints.Select(i => i[1]).Mean();
  180. var T = DenseMatrix.OfArray(new double[,]
  181. {
  182. {1 / sx, 0, - meanX / sx},
  183. {0, 1 / sy, - meanY / sy},
  184. {0, 0, 1}
  185. });
  186. var unityPoints = correspondences.Select(i => i.UnityPosition);
  187. var sx_prime = unityPoints.Select(i => i[0]).Max(i => i) / 2;
  188. var sy_prime = unityPoints.Select(i => i[1]).Max(i => i) / 2;
  189. var meanX_prime = unityPoints.Select(i => i[0]).Mean();
  190. var meanY_prime = unityPoints.Select(i => i[1]).Mean();
  191. var T_prime = DenseMatrix.OfArray(new double[,]
  192. {
  193. {1 / sx_prime, 0, - meanX_prime / sx_prime},
  194. {0, 1 / sy_prime, - meanY_prime / sy_prime},
  195. {0, 0, 1}
  196. });
  197. var result = new List<(Vector<double>, Vector<double>)>();
  198. foreach (var correspondence in correspondences)
  199. {
  200. var homogeneousPointWorld = DenseVector.OfArray(new double[] { correspondence.WorldPosition[0], correspondence.WorldPosition[1], 1 });
  201. var homogeneousTransformedPointWorld = T * homogeneousPointWorld;
  202. var homogeneousPointUnity = DenseVector.OfArray(new double[] { correspondence.UnityPosition[0], correspondence.UnityPosition[1], 1 });
  203. var homogeneousTransformedPointUnity = T_prime * homogeneousPointUnity;
  204. //if (new[] { homogeneousTransformedPointUnity[1], homogeneousTransformedPointUnity[0], homogeneousTransformedPointWorld[1], homogeneousTransformedPointWorld[0] }.Any(i => i < -1 || i > 1))
  205. //{
  206. //}
  207. result.Add((homogeneousTransformedPointWorld, homogeneousTransformedPointUnity));
  208. }
  209. return (result, T, T_prime);
  210. }
  211. private int RansacIterations(float inlierProbability, int samplesPerIteration, float successProbability)
  212. {
  213. return (int)Math.Ceiling(Math.Log(1 - successProbability, 1 - Math.Pow(inlierProbability, samplesPerIteration)));
  214. }
  215. private void CalculateHomographySimple()
  216. {
  217. if (!(calibrationVectors?.Count >= 4))
  218. {
  219. throw new InvalidOperationException("Must have at least 4 correspondences to calculate a homography.");
  220. }
  221. var cv = calibrationVectors;
  222. Matrix<double> matrixA = DenseMatrix.OfArray(new double[,]
  223. {
  224. {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},
  225. {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},
  226. {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},
  227. {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},
  228. {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},
  229. {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},
  230. {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},
  231. {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}
  232. });
  233. Matrix<double> matrixB = DenseMatrix.OfArray(new double[,]
  234. {
  235. {cv[0].UnityPosition.x},
  236. {cv[0].UnityPosition.z},
  237. {cv[1].UnityPosition.x},
  238. {cv[1].UnityPosition.z},
  239. {cv[2].UnityPosition.x},
  240. {cv[2].UnityPosition.z},
  241. {cv[3].UnityPosition.x},
  242. {cv[3].UnityPosition.z}
  243. });
  244. var lambda = (matrixA.Transpose() * matrixA).Inverse() * matrixA.Transpose() * matrixB;
  245. homography = DenseMatrix.OfArray(new double[,]
  246. {
  247. { lambda[0,0], lambda[1,0], lambda[2,0]},
  248. { lambda[3,0], lambda[4,0], lambda[5,0]},
  249. { lambda[6,0], lambda[7,0], 1}
  250. });
  251. testCalibration();
  252. }
  253. public Vector3 CalculateUnityPosition(Person person)
  254. {
  255. return WorldPositionToUnityPosition(person.WorldPosition);
  256. }
  257. private Vector3 WorldPositionToUnityPosition(Vector3 worldPosition)
  258. {
  259. var vector = DenseVector.OfArray(new double[] { worldPosition.x, worldPosition.z, 1 });
  260. var output = homography * vector;
  261. var scaledOutput = output / output[2];
  262. var resultVector = new Vector3((float)scaledOutput[0], 0, (float)scaledOutput[1]);
  263. return resultVector;
  264. }
  265. /// <summary>
  266. /// For internal debugging only.
  267. /// </summary>
  268. private void testCalibration()
  269. {
  270. foreach (var c in calibrationVectors)
  271. {
  272. var test = DenseVector.OfArray(new double[] { c.WorldPosition.x, c.WorldPosition.z, 1 });
  273. var testOutput = homography * test;
  274. var scaledTestOutput = testOutput / testOutput[2];
  275. }
  276. }
  277. }
  278. }