zimIdentifyLineLSD.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. using o0.Geometry;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using UnityEngine;
  8. using UnityEngine.UI;
  9. using o0.Geometry2D.Float;
  10. using ZIM;
  11. using ZIM.Unity;
  12. using o0.Num;
  13. using o0.Geometry2D;
  14. namespace o0.Project
  15. {
  16. public static partial class Extension
  17. { // (垂直方向,水平方向)
  18. public static (Vector, float) ZIMLineSegGradient(Matrix mat, Matrix dir, float gradient, Vector start, Vector end, (int, int) range, bool reverseXY)
  19. {
  20. int startX, endX;
  21. if (start.x < end.x)
  22. {
  23. startX = Mathf.RoundToInt(start.x) + 1;
  24. endX = Mathf.RoundToInt(end.x) + 1;
  25. }
  26. else
  27. {
  28. startX = Mathf.RoundToInt(start.x) - 1;
  29. endX = Mathf.RoundToInt(end.x) - 1;
  30. }
  31. if (startX == endX)
  32. return (new Vector(start.x, start.y), 0);
  33. var xDiff = end.x - start.x;
  34. var xToYRate = (float)(end.y - start.y) / xDiff;
  35. Func<float, float> xToYFunc = (x) => (x - start.x) * xToYRate + start.y;
  36. var B1 = Mathf.RoundToInt(start.x);
  37. var B1V = mat[B1, Mathf.RoundToInt(start.y)];
  38. var Sum1 = B1V;
  39. foreach (var Bx in startX.Range(endX))
  40. {
  41. int[,] CIArray = new int[range.Item1, range.Item2];
  42. float[] CAArray = new float[range.Item1 * range.Item2];
  43. float[] CMArray = new float[range.Item1 * range.Item2];
  44. for (int j = 0; j < range.Item2; j++)
  45. {
  46. var x = xDiff > 0 ? Bx + j : Bx - j;
  47. var y = xToYFunc(x);
  48. var m1 = range.Item1 / 2 - 1;
  49. var m2 = range.Item1 / 2;
  50. var y1 = Mathf.FloorToInt(y);
  51. var y2 = Mathf.CeilToInt(y);
  52. for (int i = 0; i < range.Item1 / 2; i++)
  53. {
  54. if (reverseXY)
  55. CIArray[i, j] = mat.Index(y1 + i - m1, x);
  56. else
  57. CIArray[i, j] = mat.Index(x, y1 + i - m1);
  58. CAArray[i + j * range.Item1] = dir.Element[CIArray[i, j]];
  59. CMArray[i + j * range.Item1] = mat.Element[CIArray[i, j]];
  60. }
  61. for (int i = range.Item1 / 2; i < range.Item1; i++)
  62. {
  63. if (reverseXY)
  64. CIArray[i, j] = mat.Index(y2 + i - m2, x);
  65. else
  66. CIArray[i, j] = mat.Index(x, y2 + i - m2);
  67. CAArray[i + j * range.Item1] = dir.Element[CIArray[i, j]];
  68. CMArray[i + j * range.Item1] = mat.Element[CIArray[i, j]];
  69. }
  70. }
  71. var CV = o0.Max(CMArray);
  72. var avaAngle = 60;
  73. var halfAvaAngle = avaAngle / 2;
  74. var breakFlag = true;
  75. foreach (var ca in CAArray)
  76. {
  77. if (MathF.Abs(ca - gradient) <= halfAvaAngle || MathF.Abs(ca - 360 - gradient) <= halfAvaAngle || MathF.Abs(ca + 360 - gradient) <= halfAvaAngle)
  78. {
  79. breakFlag = false;
  80. break;
  81. }
  82. }
  83. if (breakFlag)
  84. break;
  85. B1 = Bx;
  86. Sum1 += CV;
  87. }
  88. return (new Vector(B1, xToYFunc(B1)), Sum1);
  89. }
  90. // 返回四边形的四条边,List长度一定是4 (如果没有识别到就是null),且线段顺序是: 下、右、上、左
  91. public static List<Line> ZIMIdentifyQuadLSD(this Matrix screenLocateMat, Matrix edgeMat, Matrix edgeDirMat,
  92. out Line[] oldLines, out List<Line> lightLines, ScreenMap screen,
  93. float conSize, float gradientLength, float minLength = 100)
  94. {
  95. // 加权平均
  96. Vector[] avgPointsColumn = new Vector[screenLocateMat.Size.x];
  97. float[] valueSumsColumn = new float[screenLocateMat.Size.x];
  98. Parallel.For(0, screenLocateMat.Size.x, i =>
  99. {
  100. for (int j = 0; j < screenLocateMat.Size.y; j++)
  101. {
  102. var value = screenLocateMat[i, j];
  103. valueSumsColumn[i] += value;
  104. avgPointsColumn[i] += new Vector(i, j) * value;
  105. }
  106. });
  107. Vector avgPoint = Vector.Zero;
  108. var valueSum = 0f;
  109. for (int i = 0; i < screenLocateMat.Size.x; i++)
  110. {
  111. avgPoint += avgPointsColumn[i];
  112. valueSum += valueSumsColumn[i];
  113. }
  114. avgPoint /= valueSum;
  115. var lines = edgeMat.IdentifyLineLSD(edgeDirMat, minLength, 20, LineCaptureSize: new Vector(0, 5));
  116. {
  117. // 将lines输出一张图片
  118. var LSDLineMap = new Matrix(new Geometry2D.Vector<int>((int)(edgeMat.Size.x + conSize - 1), (int)(edgeMat.Size.y + conSize - 1)), Tiling: true);
  119. foreach (var l in lines)
  120. {
  121. if (l.Item1 != null)
  122. o0Extension.DrawLine(LSDLineMap, l.Item1, (x, y) => 3, new Geometry2D.Float.Vector(0, 2), true); // 其他的备选线段
  123. }
  124. ScreenLocate.DebugTexture(1, LSDLineMap.ToTexRGBA(ScreenIdentification.FloatValueToColor));
  125. }
  126. //Debug.Log("[IdentifyLineLSD] lines.Count: " + lines.Count);
  127. // LSD计算得到的矩阵尺寸较小(因为卷积),这里必须进行位移
  128. var offset = new Vector((conSize - 1) / 2, (conSize - 1) / 2);
  129. for (int i = 0; i < lines.Count; i++)
  130. lines[i] = (lines[i].Item1 + offset, lines[i].Item2, lines[i].Item3);
  131. // 沿直线计算平均梯度
  132. float averageGradient(Line line)
  133. {
  134. var dir = (line.B - line.A).Normalized;
  135. var vertical = new Vector(-dir.y, dir.x) * (gradientLength / 2);
  136. var step = 2;
  137. var ll = line.Length;
  138. var lg = new List<float>();
  139. for (int i = 0; i <= ll; i += step)
  140. {
  141. var point = line.A + dir * i;
  142. var ga = point + vertical;
  143. var gb = point - vertical;
  144. lg.Add(screenLocateMat[(int)ga.x, (int)ga.y] - screenLocateMat[(int)gb.x, (int)gb.y]);
  145. }
  146. return Math.Abs(lg.Mean());
  147. }
  148. // 下、右、上、左
  149. var quadLines = new List<(float, Line)>[4] {new List<(float, Line)>(), new List<(float, Line)>(), new List<(float, Line)>(), new List<(float, Line)>() };
  150. lightLines = new List<Line>();
  151. oldLines = null;
  152. // 如果已有定位数据,根据现有数据筛选线条
  153. if (screen.QuadInCamera != null)
  154. {
  155. Debug.Log("[IdentifyLineLSD] 根据已有定位数据做筛选");
  156. screen.RefreshCameraSize(new Vector2(screenLocateMat.Size.x, screenLocateMat.Size.y));
  157. var calibration = ScreenLocate.Main.ReDoLocateCalibrationRatio * screenLocateMat.Size.y;
  158. oldLines = screen.QuadInCamera.GetLines();
  159. var pedals = oldLines.Select((i) => o0Extension.PointPedal(i, avgPoint)).ToArray(); // 当前定位的垂足,下、右、上、左
  160. foreach (var i in lines)
  161. {
  162. float minDistance = float.MaxValue;
  163. int index = -1;
  164. foreach (var j in pedals.Index())
  165. {
  166. var d = (o0Extension.PointPedal(i.Item1, avgPoint) - pedals[j]).Length;
  167. if (d < minDistance)
  168. {
  169. minDistance = d;
  170. index = j;
  171. }
  172. }
  173. //Debug.Log(minDistance +", -----------"+ calibration);
  174. if (minDistance < calibration) // 垂足的距离足够近
  175. {
  176. quadLines[index].Add((averageGradient(i.Item1), i.Item1));
  177. lightLines.Add(i.Item1);
  178. }
  179. }
  180. }
  181. else
  182. {
  183. var avaAngleHalf = 75f;
  184. foreach (var (line, sum, gradient) in lines)
  185. {
  186. lightLines.Add(line);
  187. var a = (avgPoint - (line.A + line.B) / 2).DegreeToXAxis();
  188. //Debug.Log(a + ", " + gradient + ", " + sum);
  189. int index = -1;
  190. if (Math.Abs(a - gradient) < avaAngleHalf || Math.Abs(a - 360 - gradient) < avaAngleHalf || Math.Abs(a + 360 - gradient) < avaAngleHalf)
  191. {
  192. if (gradient > 45 && gradient < 135) // 下
  193. index = 0;
  194. else if (gradient > 135 && gradient < 225) // 右
  195. index = 1;
  196. else if (gradient > 225 && gradient < 315) // 上
  197. index = 2;
  198. else
  199. index = 3;
  200. //var g = Math.Abs(lg.Mean());
  201. //Debug.Log(gradient + ", " + g);
  202. //List<float> lp1 = new List<float>(), lp2 = new List<float>(); // 线两侧的值
  203. //for (float i = 0; i <= ll; i += step)
  204. //{
  205. // var point = line.A + dir * i;
  206. // var ga = point + vertical;
  207. // var gb = point - vertical;
  208. // lp1.Add(screenLocateMat[(int)ga.x, (int)ga.y]);
  209. // lp2.Add(screenLocateMat[(int)gb.x, (int)gb.y]);
  210. //}
  211. //var avg1 = lp1.Mean();
  212. //var avg2 = lp2.Mean();
  213. //var v1 = lp1.Variance();
  214. //var v2 = lp2.Variance();
  215. //var lineGradient = Math.Abs(avg1 - avg2) / (v1 + v2 + 0.2f); // 方差越小,梯度的价值越高
  216. ////var g = Math.Abs(lg.Mean());
  217. ////Debug.Log(gradient + ", " + g);
  218. //Debug.Log(v1 + ", " + v2 + ", " + lineGradient);
  219. //quadLines[index].Add((lineGradient, line));
  220. quadLines[index].Add((averageGradient(line), line));
  221. }
  222. }
  223. }
  224. var result = new Line[4];
  225. for (int i = 0; i < 4; i++)
  226. {
  227. if (quadLines[i].Count > 0)
  228. result[i] = quadLines[i].Max((a, b) => a.Item1.CompareTo(b.Item1)).Item2;
  229. }
  230. return result.ToList();
  231. }
  232. }
  233. }