using o0.Geometry2D.Float; using o0.Num; using System; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace o0.Project { // 大于阈值则猜测是Line,但是这个类不考虑梯度,外部仍需结合梯度评估 public class LineGuess { public static float isLineThreshold = 1.0f; // 记录分段的线段数据 class SubLineData { public List DataList; public List DiffList; // 数据与平均值的差 // 幅度 float diffExtent = -1; float dataExtent = -1; public SubLineData(float data, float diff) { DataList = new List { data }; DiffList = new List { diff }; } public void Add(float data, float diff) { DataList.Add(data); DiffList.Add(diff); } // 得到差异的幅度 //public float GetDiffExtent() //{ // if (diffExtent == -1) // { // var sum = 0.0f; // foreach (var i in DiffList) // sum += i * i; // return diffExtent = sum / DiffList.Count; // } // else // return diffExtent; //} // 得到数据的幅度(和0比) public float GetDataExtent() { if (dataExtent == -1) { var sum = 0.0f; foreach (var i in DataList) sum += i * i; return dataExtent = sum / DataList.Count; } else return dataExtent; } } public List ScreenLocateMatList; public float GradientLength; public float MinLineLength; public LineGuess(List screenLocateMatList, float gradientLength, float minLineLength) { ScreenLocateMatList = screenLocateMatList; GradientLength = gradientLength; MinLineLength = minLineLength; } // 猜测是直线返回true public bool GuessIsLine(LineIdentified line) => Guess(line) > isLineThreshold; // 越大越可能是直线 public float Guess(LineIdentified line) { var dir = (line.Line.B - line.Line.A).Normalized; var vertical = new Vector(-dir.y, dir.x) * (GradientLength / 2); var verticalNormal = vertical.Normalized; float lineABStep = MinLineLength / 15; float lineGuessStart; float lineGuessEnd; if (line.Line.Length > 2 * MinLineLength) { lineGuessStart = 7.5f * lineABStep; lineGuessEnd = line.Line.Length - 7.5f * lineABStep; // 线的两端不计算 } else { lineGuessStart = lineABStep; lineGuessEnd = line.Line.Length - lineABStep; } var gradData = new List(); // 直线两侧梯度的平均值,垂直于直线变化 var a0 = line.Line.A - vertical; var gradStep = 1.5f; for (float n = 0; n < GradientLength; n += gradStep) { var aN = a0 + verticalNormal * n; var gradThisLine = new List(); for (float i = lineGuessStart; i <= lineGuessEnd; i += lineABStep) { var point = aN + dir * i; gradThisLine.Add(ScreenLocateMatList[line.Batch][(int)point.x, (int)point.y]); } gradData.Add(gradThisLine.Mean()); } //Debug.Log(line.Gradient + ", " + line.GradientDegree + ", " + gradData.ToJson()); if (gradData.Count <= 3) return 400; var gradChangeData = new List(); for (int j = 1; j < gradData.Count; j++) gradChangeData.Add(Math.Abs(gradData[j] - gradData[j - 1])); var meanChange = gradChangeData.Mean(); var diff = gradChangeData[0] - meanChange; var subList = new List() { new SubLineData(gradChangeData[0], diff) }; for (int i = 1; i < gradChangeData.Count; i++) { var newDiff = gradChangeData[i] - meanChange; if ((newDiff <= 0 && diff <= 0) || (newDiff > 0 && diff > 0)) // 同号,是一个分段 subList.Last().Add(gradChangeData[i], newDiff); else subList.Add(new SubLineData(gradChangeData[i], newDiff)); diff = newDiff; } int maxSubDataIndex = subList.MaxIndex((a, b) => a.GetDataExtent().CompareTo(b.GetDataExtent())); // 找到分段里和平均数差异最大的分段 //TODO 这里要不要考虑加个阈值,过滤掉梯度太小的线条? ----------------------------------- if (subList.Count < 3) maxSubDataIndex = -1; // ≥3的时候把SubDataExtent最高的这段剔除掉再计算 var lineGuessExtent = 0.0f; for (int i = 0; i < subList.Count; i++) { if (i != maxSubDataIndex) lineGuessExtent += subList[i].GetDataExtent(); } if (maxSubDataIndex == -1) lineGuessExtent /= subList.Count; else lineGuessExtent /= subList.Count - 1; lineGuessExtent = (float)Math.Sqrt(lineGuessExtent); var countFactor = -0.002f * Math.Abs(subList.Count - 3) + 0.015f; // 3时最大 if (ScreenLocate.Main.DebugOnZIMDemo) Debug.Log("line.Index" + line.ScreenLineIndex + ", maxDiffIndex: " + maxSubDataIndex + ", sub count: " + subList.Count + ", lineGuess: " + (1 - lineGuessExtent + countFactor)); return 1 - lineGuessExtent + countFactor; } } }