LightGlueResultUIController.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. using TMPro;
  4. using LightGlue.Unity.Game;
  5. using LightGlue.Unity.Networking;
  6. namespace LightGlue.Unity.UI
  7. {
  8. /// <summary>
  9. /// LightGlue算法结果UI控制器
  10. /// - 从HardwareToPythonUdpBridge获取算法结果
  11. /// - 实时显示相机位置、匹配数量、内点比例等信息
  12. /// </summary>
  13. public class LightGlueResultUIController : MonoBehaviour
  14. {
  15. [Header("结果来源")]
  16. [Tooltip("必须指定 LightGlueManager,由其统一分发算法结果。禁止直接从 Bridge 获取结果。")]
  17. public LightGlueManager manager;
  18. [Header("UI显示组件")]
  19. [Tooltip("结果有效性状态文本(显示Valid/Invalid)")]
  20. public Text isValidText;
  21. [Tooltip("匹配点数量文本")]
  22. public Text numMatchesText;
  23. [Tooltip("内点比例文本")]
  24. public Text inliersRatioText;
  25. [Tooltip("相机位置X坐标文本")]
  26. public Text cameraXText;
  27. [Tooltip("相机位置Y坐标文本")]
  28. public Text cameraYText;
  29. [Tooltip("结果状态文本(综合信息显示)")]
  30. public Text statusText;
  31. [Header("显示设置")]
  32. [Tooltip("更新频率(每秒更新次数,默认10次/秒)")]
  33. [Range(1, 60)]
  34. public int updateRate = 10;
  35. [Tooltip("是否显示无效结果(当isValid=false时)")]
  36. public bool showInvalidResults = true;
  37. [Tooltip("无效结果时的显示文本")]
  38. public string invalidResultText = "等待有效结果...";
  39. private float _updateInterval;
  40. private float _lastUpdateTime;
  41. private LightGlueResult _latestResult;
  42. private bool _hasResult = false;
  43. private void Start()
  44. {
  45. _updateInterval = 1.0f / updateRate;
  46. _lastUpdateTime = Time.time;
  47. // 结果来源:强制通过全局 LightGlueManager 获取
  48. if (manager == null)
  49. {
  50. manager = LightGlueManager.Instance;
  51. }
  52. if (manager == null)
  53. {
  54. Debug.LogError("[LightGlueResultUI] 未找到 LightGlueManager,无法获取算法结果。请在场景中放置 LightGlueSystem 预制并确保 LightGlueManager 存在。");
  55. }
  56. else
  57. {
  58. manager.OnResultUpdated += OnResultUpdated;
  59. if (manager.HasLatestResult)
  60. {
  61. OnResultUpdated(manager.LatestResult);
  62. }
  63. }
  64. }
  65. private void Update()
  66. {
  67. // 按指定频率更新
  68. if (Time.time - _lastUpdateTime < _updateInterval)
  69. return;
  70. _lastUpdateTime = Time.time;
  71. // 强制使用 Manager 分发的结果
  72. if (manager == null)
  73. return;
  74. if (_hasResult)
  75. {
  76. UpdateUI(_latestResult);
  77. }
  78. else if (!showInvalidResults)
  79. {
  80. ClearUI();
  81. }
  82. }
  83. /// <summary>
  84. /// 更新UI显示
  85. /// </summary>
  86. private void UpdateUI(LightGlueResult result)
  87. {
  88. // 显示结果有效性
  89. if (isValidText != null)
  90. {
  91. if (result.IsValid)
  92. {
  93. isValidText.text = "状态: 有效";
  94. isValidText.color = Color.green;
  95. }
  96. else
  97. {
  98. isValidText.text = "状态: 无效";
  99. isValidText.color = Color.red;
  100. }
  101. }
  102. // 显示匹配点数量
  103. if (numMatchesText != null)
  104. {
  105. numMatchesText.text = $"匹配点: {result.NumMatches}";
  106. }
  107. // 显示内点比例
  108. if (inliersRatioText != null)
  109. {
  110. inliersRatioText.text = $"内点比例: {result.InliersRatio:P1}";
  111. }
  112. // 显示相机位置(仅在有效时显示)
  113. if (result.IsValid)
  114. {
  115. if (cameraXText != null)
  116. {
  117. cameraXText.text = $"相机X: {result.CameraPosition.x:F1}";
  118. }
  119. if (cameraYText != null)
  120. {
  121. cameraYText.text = $"相机Y: {result.CameraPosition.y:F1}";
  122. }
  123. }
  124. else
  125. {
  126. if (cameraXText != null)
  127. {
  128. cameraXText.text = "相机X: --";
  129. }
  130. if (cameraYText != null)
  131. {
  132. cameraYText.text = "相机Y: --";
  133. }
  134. }
  135. // 显示综合状态信息
  136. if (statusText != null)
  137. {
  138. if (result.IsValid)
  139. {
  140. statusText.text = $"有效 | 匹配:{result.NumMatches} | 内点:{result.InliersRatio:P1} | 位置:({result.CameraPosition.x:F1}, {result.CameraPosition.y:F1})";
  141. statusText.color = Color.white;
  142. }
  143. else
  144. {
  145. statusText.text = showInvalidResults
  146. ? $"无效 | 匹配:{result.NumMatches} | {invalidResultText}"
  147. : invalidResultText;
  148. statusText.color = Color.yellow;
  149. }
  150. }
  151. }
  152. /// <summary>
  153. /// 清空UI显示
  154. /// </summary>
  155. private void ClearUI()
  156. {
  157. if (isValidText != null)
  158. isValidText.text = "状态: --";
  159. if (numMatchesText != null)
  160. numMatchesText.text = "匹配点: --";
  161. if (inliersRatioText != null)
  162. inliersRatioText.text = "内点比例: --";
  163. if (cameraXText != null)
  164. cameraXText.text = "相机X: --";
  165. if (cameraYText != null)
  166. cameraYText.text = "相机Y: --";
  167. if (statusText != null)
  168. {
  169. statusText.text = invalidResultText;
  170. statusText.color = Color.gray;
  171. }
  172. }
  173. /// <summary>
  174. /// 获取最新结果(供外部调用)
  175. /// </summary>
  176. public bool TryGetLatestResult(out LightGlueResult result)
  177. {
  178. if (_hasResult)
  179. {
  180. result = _latestResult;
  181. return true;
  182. }
  183. result = default(LightGlueResult);
  184. return false;
  185. }
  186. /// <summary>
  187. /// 检查是否有有效结果
  188. /// </summary>
  189. public bool HasValidResult()
  190. {
  191. return _hasResult && _latestResult.IsValid;
  192. }
  193. /// <summary>
  194. /// 由 LightGlueManager 触发的结果更新回调,仅做结果缓存,具体 UI 刷新仍由 Update 按频率驱动。
  195. /// </summary>
  196. private void OnResultUpdated(LightGlueResult result)
  197. {
  198. _latestResult = result;
  199. _hasResult = true;
  200. }
  201. private void OnDestroy()
  202. {
  203. if (manager != null)
  204. {
  205. manager.OnResultUpdated -= OnResultUpdated;
  206. }
  207. }
  208. }
  209. }