zimHoughTransform.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. using o0.Geometry2D;
  2. using SixLabors.ImageSharp.PixelFormats;
  3. using SixLabors.ImageSharp.Processing.Processors;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.IO;
  8. using System.Linq;
  9. using System.Text;
  10. using System.Threading.Tasks;
  11. using Unity.VisualScripting;
  12. using UnityEngine;
  13. using UnityEngine.UIElements;
  14. namespace o0.Project
  15. {
  16. public static partial class Extension
  17. {
  18. }
  19. public static class Hough
  20. {
  21. public static List<(float Ax, float Ay, float Bx, float By)> Transform(MatrixF2D mat, float LigntPointRate = 0.01f, int threshold = 100, double minLineLength = 300)
  22. {
  23. var lines = Lines(mat, LigntPointRate);
  24. var result = new List<(float, float, float, float)>();
  25. foreach (var l in lines)
  26. {
  27. if (l.PointsCount > threshold && l.Length > minLineLength)
  28. {
  29. //UnityEngine.Debug.Log($"线段: {l.A}, {l.B}, {mat[(int)l.A.x, (int)l.A.y]}, {mat[(int)l.B.x, (int)l.B.y]}");
  30. result.Add((l.A.x, l.A.y, l.B.x, l.B.y));
  31. }
  32. }
  33. return result;
  34. }
  35. static Line[,] Lines(MatrixF2D mat, float LigntPointRate, double rho = 1, double theta = Math.PI / 180, double maxLineGap = 10)
  36. {
  37. var TrigValues = GetTrigValues(theta);
  38. int maxR = (int)Math.Floor(Math.Sqrt(mat.Width * mat.Width + mat.Height * mat.Height) / rho);
  39. var Acumulator = new Line[maxR, TrigValues.Length];
  40. Parallel.For(0, maxR, (i) =>
  41. {
  42. for (int j = 0; j < TrigValues.Length; j++)
  43. Acumulator[i, j] = new Line();
  44. });
  45. // 取亮度较高的点
  46. int LigntPointCount = (int)(mat.Element.Length * LigntPointRate);
  47. var Bright = GetBrightElements(mat, LigntPointCount);
  48. /**/
  49. var mm = new MatrixF2D(mat.Width, mat.Height);
  50. foreach (var i in Bright)
  51. mm[i.x, i.y] = 1;
  52. var tt = mm.ToTex();
  53. var bytes = tt.EncodeToPNG();
  54. File.WriteAllBytes($"output/Bright.png", bytes);
  55. /**/
  56. Parallel.For(0, TrigValues.Length, a =>
  57. {
  58. foreach (var i in Bright)
  59. {
  60. var r = (int)Math.Floor((i.x * TrigValues[a].cos + i.y * TrigValues[a].sin) / rho);
  61. //UnityEngine.Debug.Log($"{i.x}, {i.y}, {angle}, maxR {maxR}");
  62. Acumulator[r, a].Add(new Vector<float>(i.x, i.y));
  63. }
  64. });
  65. return Acumulator;
  66. }
  67. // 0 - 180度, cos 取绝对值
  68. static (float cos, float sin)[] GetTrigValues(double theta)
  69. {
  70. (float, float)[] result = new (float, float)[(int)Math.Floor((Math.PI / theta))];
  71. Parallel.For(0, result.Length, i => result[i] = ((float)Math.Abs(Math.Cos(i * theta)), (float)Math.Sin(i * theta)));
  72. return result;
  73. }
  74. // 取不少于指定数量的亮点
  75. static List<(int x, int y)> GetBrightElements(MatrixF2D mat, int leastCount, int bit = 256)
  76. {
  77. bit -= 1;
  78. var p = mat.Element;
  79. float scale = 1 / Enumerable.Max(p);
  80. Parallel.For(0, p.Length, (i) => p[i] *= scale); // 归一化
  81. int[] pint = new int[p.Length];
  82. Parallel.For(0, p.Length, i => pint[i] = (int)(p[i] * bit)); // 映射到256色彩
  83. // 得到色阶
  84. int[] H = new int[bit + 1];
  85. for (int i = 0; i < p.Length; i++)
  86. H[pint[i]]++;
  87. int b = bit;
  88. var sum = 0;
  89. for (; b >= 0; b--)
  90. {
  91. sum += H[b];
  92. if (sum >= leastCount)
  93. break;
  94. }
  95. var result = new List<(int x, int y)>();
  96. for (int i = 0; i < p.Length; i++)
  97. {
  98. if (pint[i] >= b)
  99. result.Add((i % mat.Width, i / mat.Width));
  100. }
  101. return result;
  102. }
  103. class Line
  104. {
  105. public int PointsCount => Points.Count;
  106. public float Length => (A - B).Length;
  107. public Vector<float> A { get;private set; }
  108. public Vector<float> B { get;private set; }
  109. List<Vector<float>> Points = new List<Vector<float>>();
  110. public Line()
  111. {
  112. }
  113. public void Add( Vector<float> p)
  114. {
  115. Points.Add(p);
  116. if (Points.Count == 1)
  117. A = B = p;
  118. else
  119. {
  120. A = A < p ? A : p;
  121. B = B > p ? B : p;
  122. }
  123. }
  124. }
  125. }
  126. }