LineGuess.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. using o0.Geometry2D.Float;
  2. using o0.Num;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using UnityEngine;
  7. namespace o0.Project
  8. {
  9. // 大于阈值则猜测是Line,但是这个类不考虑梯度,外部仍需结合梯度评估
  10. public class LineGuess
  11. {
  12. public static float isLineThreshold = 1.0f;
  13. // 记录分段的线段数据
  14. class SubLineData
  15. {
  16. public List<float> DataList;
  17. public List<float> DiffList; // 数据与平均值的差
  18. // 幅度
  19. float diffExtent = -1;
  20. float dataExtent = -1;
  21. public SubLineData(float data, float diff)
  22. {
  23. DataList = new List<float> { data };
  24. DiffList = new List<float> { diff };
  25. }
  26. public void Add(float data, float diff)
  27. {
  28. DataList.Add(data);
  29. DiffList.Add(diff);
  30. }
  31. // 得到差异的幅度
  32. //public float GetDiffExtent()
  33. //{
  34. // if (diffExtent == -1)
  35. // {
  36. // var sum = 0.0f;
  37. // foreach (var i in DiffList)
  38. // sum += i * i;
  39. // return diffExtent = sum / DiffList.Count;
  40. // }
  41. // else
  42. // return diffExtent;
  43. //}
  44. // 得到数据的幅度(和0比)
  45. public float GetDataExtent()
  46. {
  47. if (dataExtent == -1)
  48. {
  49. var sum = 0.0f;
  50. foreach (var i in DataList)
  51. sum += i * i;
  52. return dataExtent = sum / DataList.Count;
  53. }
  54. else
  55. return dataExtent;
  56. }
  57. }
  58. public List<Matrix> ScreenLocateMatList;
  59. public float GradientLength;
  60. public float MinLineLength;
  61. public LineGuess(List<Matrix> screenLocateMatList, float gradientLength, float minLineLength)
  62. {
  63. ScreenLocateMatList = screenLocateMatList;
  64. GradientLength = gradientLength;
  65. MinLineLength = minLineLength;
  66. }
  67. // 猜测是直线返回true
  68. public bool GuessIsLine(LineIdentified line) => Guess(line) > isLineThreshold;
  69. // 越大越可能是直线
  70. public float Guess(LineIdentified line)
  71. {
  72. var dir = (line.Line.B - line.Line.A).Normalized;
  73. var vertical = new Vector(-dir.y, dir.x) * (GradientLength / 2);
  74. var verticalNormal = vertical.Normalized;
  75. float lineABStep = MinLineLength / 15;
  76. float lineGuessStart;
  77. float lineGuessEnd;
  78. if (line.Line.Length > 2 * MinLineLength)
  79. {
  80. lineGuessStart = 7.5f * lineABStep;
  81. lineGuessEnd = line.Line.Length - 7.5f * lineABStep; // 线的两端不计算
  82. }
  83. else
  84. {
  85. lineGuessStart = lineABStep;
  86. lineGuessEnd = line.Line.Length - lineABStep;
  87. }
  88. var gradData = new List<float>(); // 直线两侧梯度的平均值,垂直于直线变化
  89. var a0 = line.Line.A - vertical;
  90. var gradStep = 1.5f;
  91. for (float n = 0; n < GradientLength; n += gradStep)
  92. {
  93. var aN = a0 + verticalNormal * n;
  94. var gradThisLine = new List<float>();
  95. for (float i = lineGuessStart; i <= lineGuessEnd; i += lineABStep)
  96. {
  97. var point = aN + dir * i;
  98. gradThisLine.Add(ScreenLocateMatList[line.Batch][(int)point.x, (int)point.y]);
  99. }
  100. gradData.Add(gradThisLine.Mean());
  101. }
  102. //Debug.Log(line.Gradient + ", " + line.GradientDegree + ", " + gradData.ToJson());
  103. if (gradData.Count <= 3)
  104. return 400;
  105. var gradChangeData = new List<float>();
  106. for (int j = 1; j < gradData.Count; j++)
  107. gradChangeData.Add(Math.Abs(gradData[j] - gradData[j - 1]));
  108. var meanChange = gradChangeData.Mean();
  109. var diff = gradChangeData[0] - meanChange;
  110. var subList = new List<SubLineData>() { new SubLineData(gradChangeData[0], diff) };
  111. for (int i = 1; i < gradChangeData.Count; i++)
  112. {
  113. var newDiff = gradChangeData[i] - meanChange;
  114. if ((newDiff <= 0 && diff <= 0) || (newDiff > 0 && diff > 0)) // 同号,是一个分段
  115. subList.Last().Add(gradChangeData[i], newDiff);
  116. else
  117. subList.Add(new SubLineData(gradChangeData[i], newDiff));
  118. diff = newDiff;
  119. }
  120. int maxSubDataIndex = subList.MaxIndex((a, b) => a.GetDataExtent().CompareTo(b.GetDataExtent())); // 找到分段里和平均数差异最大的分段
  121. //TODO 这里要不要考虑加个阈值,过滤掉梯度太小的线条? -----------------------------------
  122. if (subList.Count < 3)
  123. maxSubDataIndex = -1; // ≥3的时候把SubDataExtent最高的这段剔除掉再计算
  124. var lineGuessExtent = 0.0f;
  125. for (int i = 0; i < subList.Count; i++)
  126. {
  127. if (i != maxSubDataIndex)
  128. lineGuessExtent += subList[i].GetDataExtent();
  129. }
  130. if (maxSubDataIndex == -1)
  131. lineGuessExtent /= subList.Count;
  132. else
  133. lineGuessExtent /= subList.Count - 1;
  134. lineGuessExtent = (float)Math.Sqrt(lineGuessExtent);
  135. var countFactor = -0.002f * Math.Abs(subList.Count - 3) + 0.015f; // 3时最大
  136. if (ScreenLocate.Main.DebugOnZIMDemo)
  137. Debug.Log("line.Index" + line.ScreenLineIndex + ", maxDiffIndex: " + maxSubDataIndex + ", sub count: " + subList.Count + ", lineGuess: " + (1 - lineGuessExtent + countFactor));
  138. return 1 - lineGuessExtent + countFactor;
  139. }
  140. }
  141. }