ScreenLocate.cs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946
  1. #define ENABLE_LOG
  2. using InfraredManager;
  3. using o0;
  4. using Serenegiant.UVC;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using UnityEngine;
  9. using UnityEngine.UI;
  10. using ZIM;
  11. using ZIM.Unity;
  12. using static Serenegiant.UVC.UVCManager;
  13. using Color = UnityEngine.Color;
  14. [RequireComponent(typeof(Canvas))]
  15. public partial class ScreenLocate : MonoBehaviour
  16. {
  17. public InfraredCameraHelper InfraredCameraHelper;
  18. private const string TAG = "ScreenLocate#";
  19. enum Mode
  20. {
  21. InfraredLocate,
  22. ScreenMap,
  23. ScreenLocateManual
  24. }
  25. enum InfraredCount
  26. {
  27. Single,
  28. Double
  29. }
  30. enum Platform
  31. {
  32. Window,
  33. Android
  34. }
  35. Platform mPlatform = Platform.Android;
  36. // 2个灯,顺序根据红外灯的大小 由大到小, 坐标通过 InfraredSpot.ScreenUV 和 InfraredSpot.CameraLocation 获得
  37. public InfraredSpot[] InfraredSpots
  38. {
  39. get
  40. {
  41. infraredCount = InfraredCount.Double;
  42. return infraredSpotBuffer;
  43. }
  44. }
  45. // 1个灯, 坐标通过 InfraredSpot.ScreenUV 和 InfraredSpot.CameraLocation 获得
  46. public InfraredSpot InfraredSpotSingle
  47. {
  48. get
  49. {
  50. infraredCount = InfraredCount.Single;
  51. return infraredSpotBuffer[0];
  52. }
  53. }
  54. public InfraredSpot[] infraredSpotBuffer;
  55. InfraredCount infraredCount;
  56. public string GetInfraredCount() { return infraredCount.ToString(); }
  57. public InfraredDemo InfraredDemoMain => FindObjectOfType<InfraredDemo>();
  58. #region UVC 处理的对象
  59. public UVCManager mUVCManager;
  60. public UVCDrawer mUVCDrawer;
  61. public CameraInfo mUVCCameraInfo;
  62. public bool getUVCCameraInfo => mUVCCameraInfo != null ? true : false;
  63. public Vector2 getUVCCameraInfoSize => getUVCCameraInfo ? mUVCCameraInfo.Size : new Vector2(256, 256);
  64. private Texture mUVCTexture;
  65. public Texture getUVCTexture => mUVCTexture;
  66. private Texture2D mUVCTexture2D;
  67. // [SerializeField] Texture2DArray mUVCOutArray;
  68. #endregion
  69. public Text Info;
  70. public List<RectTransform> CrosshairInCamera;
  71. public List<RectTransform> CrosshairInScreen;
  72. public RectTransform ScreenQuad;
  73. public Toggle SaveToggle;
  74. public Vector2 ScreenLocateCameraSize; // 屏幕识别需要的目标分辨率,摄像机分辨率变化时该分辨率也会跟着调整
  75. public bool ShowScreenQuad = false;
  76. public RawImage rawImage;
  77. public RawImage rawImage1;
  78. public RawImage rawImage2;
  79. public RawImage rawImage3;
  80. public RawImage rawImage4;
  81. public RawImage rawImage5;
  82. public RawImage FullScreenImage;
  83. public InfraredSpotSettings InfraredSpotSettings;
  84. //public ZIMWebCamera zimWebCamera => GetComponent<ZIMWebCamera>();
  85. public Texture2D DebugScreenImage;
  86. public bool DebugOnEditorWin = false;
  87. // private SynchronizationContext mainContext;
  88. //是否单点显示
  89. public bool bSinglePoint = true;//默认单点识别
  90. bool bIdentifyRed = true;//默认设备红色
  91. bool bIdentifyGreen = true;
  92. #region 性能检测相关
  93. public Text m_UITime;
  94. const float m_UIUpdateInterval = 0.1f;
  95. float m_UIUpdateTimer = 0.0f;
  96. List<float> m_History = new List<float>(100);
  97. int m_ValidHistoryFrames = 0;
  98. float m_AverageTime = float.NaN;
  99. float m_MedianTime = float.NaN;
  100. float m_MinTime = float.NaN;
  101. float m_MaxTime = float.NaN;
  102. public float updateInterval = 0.5F;
  103. private double lastInterval;
  104. private int frames = 0;
  105. private float fps;
  106. public Text m_FPS;
  107. #endregion
  108. InfraredLocate infraredLocate;
  109. RectTransform canvas;
  110. Mode mode;
  111. List<Vector2> pointManual = new List<Vector2>();
  112. //o0.Project.WebCam o0WebCam = null;
  113. o0.Project.ScreenIdentification screenIdentification;
  114. public o0.Project.ScreenIdentification getScreenIdentification => screenIdentification;
  115. static public ScreenLocate Main;
  116. static public List<RawImage> DebugImage = new List<RawImage>();
  117. static public RectTransform BackQuad = null;
  118. static public void DebugTexture(int index, Texture texture)
  119. {
  120. Destroy(DebugImage[index].texture);
  121. DebugImage[index].texture = texture;
  122. }
  123. static public void SetScreen(UnityEngine.Color? color = null)
  124. {
  125. if (BackQuad == null)
  126. {
  127. var canvas = GameObject.Find("WebCameraView").GetComponent<RectTransform>();
  128. var background = canvas.Find("Background");
  129. BackQuad = background.GetChild(0).GetComponent<RectTransform>();
  130. }
  131. BackQuad.parent.gameObject.SetActive(color != null);
  132. BackQuad.GetComponent<RawImage>().color = color ?? Color.black;
  133. Debug.Log("Set Screen " + color.GetColorName());
  134. }
  135. static public void SetScreen(Rect rect, UnityEngine.Color? color = null)
  136. {
  137. if (BackQuad == null)
  138. {
  139. var canvas = GameObject.Find("WebCameraView").GetComponent<RectTransform>();
  140. var background = canvas.Find("Background");
  141. BackQuad = background.GetChild(0).GetComponent<RectTransform>();
  142. }
  143. BackQuad.parent.gameObject.SetActive(color != null);
  144. BackQuad.anchorMin = rect.min;
  145. BackQuad.anchorMax = rect.max;
  146. BackQuad.GetComponent<RawImage>().color = color ?? Color.black;
  147. Debug.Log("Set Screen " + color.GetColorName());
  148. }
  149. static void DebugBackQuad(Rect? rect = null)
  150. {
  151. if (BackQuad)
  152. {
  153. BackQuad.parent.GetComponent<RawImage>().enabled = false;
  154. BackQuad.GetComponent<RawImage>().color = Color.white;
  155. BackQuad.parent.gameObject.SetActive(!BackQuad.parent.gameObject.activeSelf);
  156. if (rect.HasValue)
  157. {
  158. BackQuad.anchorMin = rect.Value.min;
  159. BackQuad.anchorMax = rect.Value.max;
  160. }
  161. }
  162. }
  163. public void ReSizeTexture(int width, int height)
  164. {
  165. Debug.Log("Cur mUVCTexture Size: " + mUVCTexture);
  166. return;
  167. if (mUVCTexture.width < width || mUVCTexture.height < height) // 如果当前分辨率太小,则重新new一个texture
  168. {
  169. Texture2D tex = new Texture2D(
  170. width, height,
  171. TextureFormat.ARGB32,
  172. false, /* mipmap */
  173. true /* linear */);
  174. tex.filterMode = FilterMode.Point;
  175. tex.Apply();
  176. mUVCTexture = tex;
  177. mUVCCameraInfo.previewTexture = tex;
  178. var nativeTexPtr = mUVCCameraInfo.previewTexture.GetNativeTexturePtr();
  179. }
  180. }
  181. void Awake()
  182. {
  183. Main = this;
  184. #if !UNITY_EDITOR_WIN
  185. DebugOnEditorWin = false;
  186. #endif
  187. //if (mUVCDrawer)
  188. // mUVCDrawer.StartPreviewAction += UVCIsReady;
  189. }
  190. void OnDestroy()
  191. {
  192. //if (mUVCDrawer)
  193. // mUVCDrawer.StartPreviewAction -= UVCIsReady;
  194. }
  195. void Start()
  196. {
  197. //mainContext = SynchronizationContext.Current;
  198. DebugImage.Add(rawImage);
  199. DebugImage.Add(rawImage1);
  200. DebugImage.Add(rawImage2);
  201. DebugImage.Add(rawImage3);
  202. DebugImage.Add(rawImage4);
  203. DebugImage.Add(rawImage5);
  204. DebugImage.Add(FullScreenImage);
  205. canvas = transform.GetComponent<RectTransform>();
  206. mode = Mode.InfraredLocate;
  207. //if (DebugScreenImage)
  208. //{
  209. // screenIdentification = new o0.Project.ScreenIdentification(new o0.Geometry2D.Vector<int>(DebugScreenImage.width, DebugScreenImage.height));
  210. // WebCamIsReady(DebugScreenImage);
  211. // screenIdentification.LocateScreen();
  212. //}
  213. infraredCount = InfraredCount.Single;
  214. #region 性能检测相关
  215. for (var i = 0; i < m_History.Capacity; ++i)
  216. {
  217. m_History.Add(0.0f);
  218. }
  219. lastInterval = Time.realtimeSinceStartup;
  220. frames = 0;
  221. #endregion
  222. }
  223. //ZIMWebCamera场景使用
  224. public void WebCamIsReady(Texture texture)
  225. {
  226. mPlatform = Platform.Window;
  227. mUVCTexture = texture;
  228. mUVCCameraInfo = new CameraInfo(mUVCTexture);
  229. brightness = 0;
  230. //UVC准备好
  231. InfraredCameraHelper?.InvokeOnUVCIsReady(mUVCCameraInfo);
  232. }
  233. //手机端UVCCamra使用
  234. public void UVCIsReady(Texture texture)
  235. {
  236. mPlatform = Platform.Android;
  237. //this.startUVCBtn.interactable = true;
  238. mUVCTexture = texture;
  239. //ARGB32
  240. //mUVCTexture2D = Texture2D.CreateExternalTexture(texture.width, texture.height, TextureFormat.ARGB32, false, true, texture.GetNativeTexturePtr()); //TextureToTexture2D(texture);
  241. //Debug.Log("mUVCTexture2D isReable:" + mUVCTexture2D.isReadable);
  242. //Debug.Log("mUVCTexture2D mipmapCount:" + (mUVCTexture2D.mipmapCount > 1));
  243. List<CameraInfo> cameraInfos = mUVCManager.GetAttachedDevices();
  244. mUVCCameraInfo = cameraInfos[cameraInfos.Count - 1];
  245. Debug.Log("mUVCCameraInfo InvokeOnUVCIsReady:" + mUVCCameraInfo);
  246. //UVC准备好
  247. InfraredCameraHelper?.InvokeOnUVCIsReady(mUVCCameraInfo);
  248. }
  249. public void startUVC()
  250. {
  251. }
  252. int brightness = 0;
  253. //public Text brightnessText;
  254. //public void SliderBrightness(Slider slider)
  255. //{
  256. // var _value = slider.value;
  257. // brightness = (int)_value;
  258. // brightnessText.text = (2 + brightness) + "";
  259. //}
  260. public void SetInfraredLocateBrightnessThreshold(float value)
  261. {
  262. if (infraredLocate != null)
  263. {
  264. if (value >= 0 && value <= 1)
  265. infraredLocate.SetBrightnessThreshold(value); // 参数是 红外灯的亮度阈值,阈值越小能够检测到的亮度就越低,默认值是0.93
  266. }
  267. }
  268. void Update()
  269. {
  270. ++frames;
  271. float timeNow = Time.realtimeSinceStartup;
  272. if (timeNow > lastInterval + updateInterval)
  273. {
  274. fps = (float)(frames / (timeNow - lastInterval));
  275. frames = 0;
  276. lastInterval = timeNow;
  277. }
  278. if (m_FPS != null)
  279. m_FPS.text = "FPS:" + fps.ToString("f2");
  280. if (mUVCCameraInfo == null) return;
  281. if (screenIdentification == null)
  282. {
  283. screenIdentification = new o0.Project.ScreenIdentification();
  284. screenIdentification.OnLocateScreenEnter += OnLocateScreenEnter;
  285. screenIdentification.OnLocateScreenEnd += OnLocateScreenEnd;
  286. }
  287. if (infraredLocate == null)
  288. {
  289. infraredLocate = new InfraredLocate(mUVCCameraInfo, screenIdentification, InfraredSpotSettings);
  290. float redfilterValue = PlayerPrefs.GetFloat("Init redFilterSliderValue", 0.8f);
  291. Debug.Log("Init Red filterValue:" + redfilterValue);
  292. infraredLocate.SetBrightnessThreshold(redfilterValue); // 参数是 红外灯的亮度阈值,阈值越小能够检测到的亮度就越低,默认值是0.93
  293. }
  294. if (screenIdentification.Screen.RefreshCameraSize(getUVCCameraInfoSize)) // 同步分辨率, 分辨率变化后还需同步到InfraredDemo
  295. {
  296. ScreenLocate.quadUnityVectorList = screenIdentification.Screen.QuadInCamera.GetUnityVertexList();
  297. SyncInfraredDemo();
  298. }
  299. if (mode == Mode.ScreenLocateManual)
  300. {
  301. //if (Input.GetMouseButtonDown(0))
  302. //{
  303. // var mouse = Input.mousePosition;
  304. // var u = mouse.x / Screen.width;
  305. // var v = mouse.y / Screen.height;
  306. // u = Math.Clamp(u, 0, 1);
  307. // v = Math.Clamp(v, 0, 1);
  308. // pointManual.Add(new Vector2(u * mUVCTexture.width, v * mUVCTexture.height));
  309. // var obj = Instantiate(Resources.Load("Point")) as GameObject;
  310. // obj.transform.SetParent(FullScreenImage.transform);
  311. // obj.transform.localPosition = new Vector2(u, v).pixelToLocalPosition_AnchorCenter(new Vector2(1, 1), FullScreenImage.rectTransform.rect);
  312. // if (pointManual.Count == 1)
  313. // Info.text = "左键单击屏幕 右下角";
  314. // else if (pointManual.Count == 2)
  315. // Info.text = "左键单击屏幕 右上角";
  316. // else if (pointManual.Count == 3)
  317. // Info.text = "左键单击屏幕 左上角";
  318. // else if (pointManual.Count == 4)
  319. // {
  320. // screenIdentification.LocateScreenManual(new OrdinalQuadrilateral(pointManual[0].o0Vector(), pointManual[1].o0Vector(), pointManual[3].o0Vector(), pointManual[2].o0Vector()));
  321. // pointManual.Clear();
  322. // ShowScreen(screenIdentification.Screen.Quad);
  323. // foreach (Transform i in FullScreenImage.transform)
  324. // Destroy(i.gameObject);
  325. // ToMode(Mode.InfraredLocate);
  326. // }
  327. //}
  328. return;
  329. }
  330. //var t0 = Time.realtimeSinceStartup;
  331. /* New*/
  332. //Debug.Log((mUVCCameraInfo != null) +" = "+ mUVCCameraInfo.IsPreviewing + " = "+ screenIdentification.Screen.Active);
  333. if (mUVCCameraInfo != null && mUVCCameraInfo.IsPreviewing) // 成功定位屏幕后才做红外识别
  334. {
  335. CreateUVCTexture2DIfNeeded();
  336. if (!screenIdentification.Update(mUVCTexture2D))
  337. {
  338. if (!screenIdentification.Screen.Active)
  339. {
  340. //DebugTexture(1, mUVCTexture2D.zimAutoLightSimple());
  341. return;
  342. }
  343. //if (mUVCCameraInfo.Size != ScreenLocateCameraSize) // 摄像机分辨率发生改变时执行
  344. //{
  345. // RefreshScreenQuad(mUVCCameraInfo.Size);
  346. // return;
  347. //}
  348. if (mode == Mode.InfraredLocate)
  349. {
  350. //0,0, cameraTexture2D.width, cameraTexture2D.height,0
  351. var pixels = mUVCTexture2D.GetPixels(); // 从左往右、从下往上
  352. //InfraredSpots = infraredLocate.Update(pixels);
  353. if (bSinglePoint)
  354. infraredSpotBuffer = infraredLocate.UpdateSingle(pixels);
  355. else
  356. infraredSpotBuffer = infraredLocate.Update(pixels);
  357. if (mPlatform == Platform.Window) //渲染ui上面的点。进入游戏可以隐藏
  358. {
  359. for (int i = 0; i < infraredSpotBuffer.Length; i++)
  360. {
  361. if (infraredSpotBuffer[i].CameraLocation != null)
  362. {
  363. // 检测到光点
  364. var posInCanvas = infraredSpotBuffer[i].CameraLocation.Value.pixelToLocalPosition_AnchorCenter(mUVCCameraInfo.Size, rawImage.rectTransform.rect);
  365. CrosshairInCamera[i].gameObject.SetActive(true);
  366. CrosshairInCamera[i].anchoredPosition = posInCanvas;
  367. }
  368. else
  369. CrosshairInCamera[i].gameObject.SetActive(false);
  370. }
  371. }
  372. //手机端使用
  373. if (mPlatform == Platform.Android && infraredSpotBuffer.Length > 0)
  374. {
  375. int redIndex = 0;
  376. int greenIndex = 1;
  377. //仅仅第一个点显示(如果最大点出界了会闪烁)
  378. if (bSinglePoint)
  379. {
  380. redIndex = 0; //单点识别是,可以选择切换颜色
  381. if (infraredSpotBuffer[redIndex].ScreenUV != null)
  382. {
  383. string str = "Single:";
  384. Info.text = str + infraredSpotBuffer[redIndex].ScreenUV.Value.ToString("F4");
  385. //InfraredManager.ConnetDevicesSingle.ins.posAction?.Invoke(new Vector3(infraredSpotBuffer[redIndex].ScreenUV.Value.x * Screen.width, infraredSpotBuffer[redIndex].ScreenUV.Value.y * Screen.height, 0));
  386. onFilterPos(infraredSpotBuffer[redIndex].ScreenUV.Value);
  387. }
  388. }
  389. else
  390. {
  391. //雙點模式下選擇第一個點
  392. if (bIdentifyRed && !bIdentifyGreen)
  393. {
  394. if (infraredSpotBuffer[redIndex].ScreenUV != null)
  395. {
  396. Info.text = "Red" + redIndex + ":" + infraredSpotBuffer[redIndex].ScreenUV.Value.ToString("F4");
  397. //InfraredManager.ConnetDevicesSingle.ins.posAction?.Invoke(new Vector3(infraredSpotBuffer[redIndex].ScreenUV.Value.x * Screen.width, infraredSpotBuffer[redIndex].ScreenUV.Value.y * Screen.height, 0));
  398. onFilterPos2(infraredSpotBuffer[redIndex].ScreenUV.Value, redIndex);
  399. }
  400. else
  401. {
  402. Info.text = "未检测到红色最大点!";
  403. }
  404. }
  405. else if (!bIdentifyRed && bIdentifyGreen)
  406. {
  407. if (infraredSpotBuffer[greenIndex].ScreenUV != null)
  408. {
  409. Info.text = "Green:" + infraredSpotBuffer[greenIndex].ScreenUV.Value.ToString("F4");
  410. //InfraredManager.ConnetDevicesSingle.ins.posAction?.Invoke(new Vector3(infraredSpotBuffer[greenIndex].ScreenUV.Value.x * Screen.width, infraredSpotBuffer[greenIndex].ScreenUV.Value.y * Screen.height, 0));
  411. onFilterPos2(infraredSpotBuffer[greenIndex].ScreenUV.Value, greenIndex);
  412. }
  413. else
  414. {
  415. Info.text = "未检测到绿色点!";
  416. }
  417. }
  418. else
  419. {
  420. //两个不选择和两个全选都跑识别两个点
  421. //自動切換 检测到光点
  422. if (infraredSpotBuffer[redIndex].ScreenUV != null)
  423. {
  424. Info.text = "Red:" + infraredSpotBuffer[redIndex].ScreenUV.Value.ToString("F4");
  425. //InfraredManager.ConnetDevicesSingle.ins.posAction?.Invoke(new Vector3(infraredSpotBuffer[redIndex].ScreenUV.Value.x * Screen.width, infraredSpotBuffer[redIndex].ScreenUV.Value.y * Screen.height, 0));
  426. onFilterPos2(infraredSpotBuffer[redIndex].ScreenUV.Value, redIndex);
  427. }
  428. else if (infraredSpotBuffer[greenIndex].ScreenUV != null)
  429. {
  430. Info.text = "Green:" + infraredSpotBuffer[greenIndex].ScreenUV.Value.ToString("F4");
  431. //InfraredManager.ConnetDevicesSingle.ins.posAction?.Invoke(new Vector3(infraredSpotBuffer[greenIndex].ScreenUV.Value.x * Screen.width, infraredSpotBuffer[greenIndex].ScreenUV.Value.y * Screen.height, 0));
  432. onFilterPos2(infraredSpotBuffer[greenIndex].ScreenUV.Value, greenIndex);
  433. }
  434. else
  435. {
  436. Info.text = "未检测到点!";
  437. }
  438. }
  439. }
  440. }
  441. }
  442. else if (mode == Mode.ScreenMap && DebugOnEditorWin)
  443. {
  444. var pixels = mUVCTexture2D.GetPixels();
  445. if (infraredCount == InfraredCount.Single)
  446. infraredSpotBuffer = infraredLocate.UpdateSingle(pixels);
  447. else if (infraredCount == InfraredCount.Double)
  448. infraredSpotBuffer = infraredLocate.Update(pixels);
  449. for (int i = 0; i < infraredSpotBuffer.Length; i++)
  450. {
  451. if (infraredSpotBuffer[i].ScreenUV != null)
  452. {
  453. // 检测到光点
  454. var posInCanvas = infraredSpotBuffer[i].ScreenUV.Value.pixelToLocalPosition_AnchorCenter(new Vector2(1, 1), canvas.rect);
  455. CrosshairInScreen[i].gameObject.SetActive(true);
  456. CrosshairInScreen[i].anchoredPosition = posInCanvas;
  457. }
  458. else
  459. CrosshairInScreen[i].gameObject.SetActive(false);
  460. }
  461. if (Input.GetKeyDown(KeyCode.Escape))
  462. ToMode(Mode.InfraredLocate);
  463. }
  464. }
  465. }
  466. //var t1 = Time.realtimeSinceStartup;
  467. //var dt = t1 - t0;
  468. //m_History[m_ValidHistoryFrames % m_History.Count] = dt;
  469. //++m_ValidHistoryFrames;
  470. //m_UIUpdateTimer += Time.deltaTime;
  471. //if (m_UIUpdateTimer >= m_UIUpdateInterval)
  472. //{
  473. // m_UIUpdateTimer = 0.0f;
  474. // if (m_ValidHistoryFrames >= m_History.Count)
  475. // {
  476. // m_ValidHistoryFrames = 0;
  477. // m_AverageTime = 0.0f;
  478. // m_MinTime = float.PositiveInfinity;
  479. // m_MaxTime = float.NegativeInfinity;
  480. // {
  481. // for (var i = 0; i < m_History.Count; i++)
  482. // {
  483. // var time = m_History[i];
  484. // m_AverageTime += time;
  485. // m_MinTime = Mathf.Min(m_MinTime, time);
  486. // m_MaxTime = Mathf.Max(m_MaxTime, time);
  487. // }
  488. // m_AverageTime /= m_History.Count;
  489. // }
  490. // {
  491. // m_History.Sort();
  492. // // Odd-length history?
  493. // if ((m_History.Count & 1) != 0)
  494. // {
  495. // m_MedianTime = m_History[m_History.Count / 2];
  496. // }
  497. // else
  498. // {
  499. // m_MedianTime = (m_History[m_History.Count / 2] + m_History[m_History.Count / 2 - 1]) / 2.0f;
  500. // }
  501. // }
  502. // }
  503. // var statistics = $"{m_History.Count} 帧样本:\naverage: {m_AverageTime * 1000.0f:F2}ms\nmedian: {m_MedianTime * 1000.0f:F2}ms\nmin: {m_MinTime * 1000.0f:F2}ms\nmax: {m_MaxTime * 1000.0f:F2}ms\n";
  504. // //Method: {m_Method} {UnityEngine.SceneManagement.SceneManager.GetActiveScene().name} |
  505. // if (m_UITime != null)
  506. // m_UITime.text = $"Cam: {mUVCCameraInfo.CurrentWidth}x{mUVCCameraInfo.CurrentHeight}{(mUVCTexture2D? ",T2D:" : "")}{(mUVCTexture2D? mUVCTexture2D.width+ "x" : "")}{(mUVCTexture2D ? mUVCTexture2D.height:"")} \nLast Frame: {dt * 1000.0f:F2}ms \n{statistics}";
  507. //}
  508. //UpdateInputs();
  509. }
  510. Vector2 targetPos = Vector2.zero;
  511. Vector2 movePos = Vector2.zero;
  512. int moveSpeed = 20;
  513. public float filterDis = 3.0f;
  514. void onFilterPos(Vector2 _vector2Pos)
  515. {
  516. Vector2 np = new Vector2(_vector2Pos.x * Screen.width, _vector2Pos.y * Screen.height); //_vector2Pos.pixelToLocalPosition_AnchorCenter(Vector2.one, (transform as RectTransform).rect);
  517. if (Vector2.Distance(np, targetPos) >= filterDis)
  518. {
  519. targetPos = np;
  520. //InfraredManager.ConnetDevicesSingle.ins.posAction?.Invoke(new Vector3(targetPos.x, targetPos.y, 0));
  521. InfraredCameraHelper.InvokeOnPositionUpdate(targetPos);
  522. }
  523. //movePos = Vector3.Lerp(movePos, targetPos, Time.deltaTime * moveSpeed);
  524. //InfraredManager.ConnetDevicesSingle.ins.posAction?.Invoke(new Vector3(movePos.x, movePos.y, 0));
  525. }
  526. Vector2[] _targetPoints2 = new Vector2[] { Vector2.zero, Vector2.zero };
  527. void onFilterPos2(Vector2 _vector2Pos, int index)
  528. {
  529. Vector2 np = new Vector2(_vector2Pos.x * Screen.width, _vector2Pos.y * Screen.height);
  530. if (Vector2.Distance(np, _targetPoints2[index]) >= filterDis)
  531. {
  532. _targetPoints2[index] = np;
  533. InfraredCameraHelper.InvokeOnPositionUpdate2(_targetPoints2[index], index);
  534. }
  535. }
  536. #region 自动识别
  537. int Capture = 30;
  538. int Delay = 30;
  539. int DefaultResolutionIndex;
  540. readonly int HighScreenLocateResolutionIndex = 2; // 自动识别时,摄像机分辨率固定为1280 * 720 ( 对应索引是2 ),数值可以根据需要修改
  541. public void BtnScreenLocate()
  542. {
  543. if (DebugScreenImage)
  544. {
  545. screenIdentification = new o0.Project.ScreenIdentification();
  546. WebCamIsReady(DebugScreenImage);
  547. CreateUVCTexture2DIfNeeded();
  548. }
  549. Debug.Log("BtnScreenLocate Capture:" + Capture + " ,Delay: " + Delay);
  550. screenIdentification.LocateScreen(Capture, Delay);
  551. }
  552. public void OnLocateScreenEnter()
  553. {
  554. screenIdentification.Screen.QuadInCamera = null;
  555. DefaultResolutionIndex = InfraredDemoMain?.ResolutionIndex ?? 0; // 记录一下进入前的分辨率(游戏场景的分辨率,比识别时更低)
  556. InfraredDemoMain?.SetResolutionNew(HighScreenLocateResolutionIndex);
  557. CreateUVCTexture2DIfNeeded();
  558. }
  559. public void OnLocateScreenEnd()
  560. {
  561. Debug.Log("结束捕获,当前摄像机分辨率为: " + mUVCCameraInfo.Size);
  562. ScreenLocateCameraSize = mUVCCameraInfo.Size; // 记录本次屏幕识别的分辨率(目前采用高分辨率做识别,识别结束后调回低分辨率)
  563. InfraredDemoMain?.SetResolutionNew(DefaultResolutionIndex);
  564. }
  565. /**
  566. * 与UVC设备协商时
  567. * H.264是否优先协商
  568. * 仅安卓实机有效
  569. * true: H.264 > MJPEG > YUV
  570. * false: MJPEG > H.264 > YUV
  571. */
  572. public bool PreferH264 = false;
  573. private const int FRAME_TYPE_MJPEG = 0x000007;
  574. private const int FRAME_TYPE_H264 = 0x000014;
  575. public void Resize(int width, int height)
  576. {
  577. if (mUVCCameraInfo == null) return;
  578. bool PreferH264 = mUVCManager.PreferH264;
  579. int[] frameTypes = {
  580. PreferH264 ? FRAME_TYPE_H264 : FRAME_TYPE_MJPEG,
  581. PreferH264 ? FRAME_TYPE_MJPEG : FRAME_TYPE_H264,
  582. };
  583. foreach (var frameType in frameTypes)
  584. {
  585. Debug.Log("Resize frameType:" + frameType + " = " + width + " : " + height);
  586. if (UVCManager.onResize(mUVCCameraInfo.device.id, frameType, width, height) == 0)
  587. {
  588. Debug.Log("frameType:" + frameType);
  589. break;
  590. }
  591. }
  592. ReSizeTexture(width, height);
  593. mUVCCameraInfo.SetSize(width, height); // 手动记录分辨率,这里可能会有问题 width和height是期望的分辨率而不是当前摄像机实际分辨率
  594. }
  595. #endregion
  596. public void BtnScreenMap()
  597. {
  598. ToMode(Mode.ScreenMap);
  599. }
  600. //进入手动定位屏幕
  601. public void BtnScreenLocateManual()
  602. {
  603. ToMode(Mode.ScreenLocateManual);
  604. }
  605. public static List<Vector2> quadUnityVectorList = new();
  606. // 标记屏幕的四个角
  607. public void ShowScreen(QuadrilateralInCamera screen)
  608. {
  609. if (screen == null)
  610. {
  611. Info.text = "识别屏幕失败";
  612. return;
  613. }
  614. Info.text = "已识别到屏幕";
  615. if (ShowScreenQuad)
  616. {
  617. ScreenQuad.gameObject.SetActive(true);
  618. //quadUnityVectorList.Clear();
  619. for (int i = 0; i < 4; i++)
  620. {
  621. if (DebugOnEditorWin)
  622. {
  623. RectTransform t = ScreenQuad.GetChild(i) as RectTransform;
  624. t.anchoredPosition = screen.Quad[i].pixelToLocalPosition_AnchorCenter(screen.CameraSize, ScreenQuad.rect);
  625. }
  626. //mUVCCameraInfo.Size
  627. //自动识别时候,记录四个点
  628. //quadUnityVectorList.Add(quad[i].UnityVector());
  629. }
  630. quadUnityVectorList = screen.GetUnityVertexList(); // 记录四个点
  631. SaveScreenLocateVectorList();
  632. SyncInfraredDemo();
  633. SyncInfraredScreenPositioningView();
  634. }
  635. }
  636. static public void SaveScreenLocateVectorList()
  637. {
  638. string saveStr = string.Join(";", quadUnityVectorList.Select(v => $"{v.x},{v.y}")); //,{v.z}
  639. Debug.Log("SaveScreenLocateVectorList: " + saveStr);
  640. PlayerPrefs.SetString("ScreenLocateVectorList", saveStr);
  641. }
  642. static public void GetScreenLocateVectorList()
  643. {
  644. string posListStr = PlayerPrefs.GetString("ScreenLocateVectorList", "");
  645. if (!string.IsNullOrWhiteSpace(posListStr))
  646. {
  647. quadUnityVectorList.Clear();
  648. quadUnityVectorList = posListStr.Split(';')
  649. .Select(s =>
  650. {
  651. string[] parts = s.Split(',');
  652. return new Vector2(float.Parse(parts[0]), float.Parse(parts[1]));
  653. })
  654. .ToList();
  655. }
  656. }
  657. public void SyncInfraredDemo()
  658. {
  659. if (quadUnityVectorList.Count == 0) return;
  660. Vector2 texSize = getUVCCameraInfoSize;
  661. Debug.Log("[SyncInfraredDemo] quadUnityVectorList Count: " + quadUnityVectorList.Count);
  662. //同步到infaredDemo
  663. FindObjectOfType<InfraredDemo>()?.SetLocatePointsToCameraRender(
  664. quadUnityVectorList,
  665. 1,
  666. 1);
  667. }
  668. public void SyncInfraredScreenPositioningView()
  669. {
  670. if (quadUnityVectorList.Count == 0) return;
  671. //同步位置
  672. FindObjectOfType<InfraredScreenPositioningView>()?.SyncScreenPosition();
  673. }
  674. void ToMode(Mode mode)
  675. {
  676. if (this.mode == mode)
  677. return;
  678. if (mode == Mode.ScreenMap)
  679. {
  680. if (!screenIdentification.Screen.Active)
  681. {
  682. Info.text = "先定位屏幕";
  683. return;
  684. }
  685. Info.text = "按ESC退出";
  686. SetScreen(Color.black);
  687. Info.transform.SetAsLastSibling();
  688. this.mode = Mode.ScreenMap;
  689. }
  690. else if (mode == Mode.InfraredLocate)
  691. {
  692. Info.text = screenIdentification.Screen.Active ? "已定位屏幕" : "定位屏幕失败";
  693. //Info.text = "已识别到屏幕";
  694. SetScreen(null);
  695. foreach (var i in CrosshairInScreen)
  696. i.gameObject.SetActive(false);
  697. FullScreenImage.gameObject.SetActive(false);
  698. Info.transform.SetSiblingIndex(transform.childCount - 4);
  699. this.mode = Mode.InfraredLocate;
  700. DebugTexture(6, null);
  701. //DebugTexture(1, null); //null
  702. // rawImage1.texture = null;
  703. #if (!NDEBUG && DEBUG && ENABLE_LOG)
  704. Console.WriteLine($"{TAG} Mode.InfraredLocate:已识别到屏幕:{screenIdentification.Screen.Active}");
  705. #endif
  706. }
  707. else if (mode == Mode.ScreenLocateManual)
  708. {
  709. Info.text = "左键单击屏幕 左下角";
  710. FullScreenImage.gameObject.SetActive(true);
  711. Info.transform.SetSiblingIndex(transform.childCount - 1);
  712. // var newTex = WebCamera.webCamTexture.AutoLight(10);
  713. //DebugTexture(1, TextureToTexture2D(rawImage.texture));
  714. CreateUVCTexture2DIfNeeded();
  715. DebugTexture(6, mUVCTexture2D.zimAutoLight(brightness));
  716. //mUVCTexture2DTemp = TextureToTexture2D(mUVCCameraInfo.previewTexture);
  717. //DebugTexture(6, mUVCTexture2DTemp.zimAutoLight(brightness));
  718. this.mode = Mode.ScreenLocateManual;
  719. }
  720. }
  721. private Texture2D TextureToTexture2D(Texture texture, int width = 0, int height = 0)
  722. {
  723. if (width == 0)
  724. width = texture.width;
  725. if (height == 0)
  726. height = texture.height;
  727. Texture2D _texture2D = new Texture2D(width, height, TextureFormat.ARGB32, false, true);
  728. RenderTexture currentRT = RenderTexture.active;
  729. RenderTexture renderTexture = RenderTexture.GetTemporary(
  730. width,
  731. height,
  732. 0,
  733. RenderTextureFormat.ARGB32,
  734. RenderTextureReadWrite.Linear);
  735. Graphics.Blit(texture, renderTexture);
  736. RenderTexture.active = renderTexture;
  737. _texture2D.ReadPixels(new Rect(0, 0, width, height), 0, 0);
  738. _texture2D.Apply();
  739. RenderTexture.active = currentRT;
  740. RenderTexture.ReleaseTemporary(renderTexture);
  741. return _texture2D;
  742. }
  743. //public void CreateUVCTexture2DFocusSizeIfNeeded(int width, int height)
  744. //{
  745. // if (mUVCTexture2D != null)
  746. // Destroy(mUVCTexture2D);
  747. // mUVCTexture2D = TextureToTexture2D(mUVCTexture, width, height);
  748. //}
  749. private void CreateUVCTexture2DIfNeeded()
  750. {
  751. if (mUVCTexture2D != null)
  752. Destroy(mUVCTexture2D);
  753. mUVCTexture2D = TextureToTexture2D(mUVCTexture);
  754. }
  755. #region DoubleButton
  756. private DateTime m_firstTime;
  757. private DateTime m_secondTime;
  758. private void Press()
  759. {
  760. Debug.Log("进入手动定位");
  761. BtnScreenLocateManual();
  762. resetTime();
  763. }
  764. public void OnDoubleClick()
  765. {
  766. //超时重置
  767. if (!m_firstTime.Equals(default(DateTime)))
  768. {
  769. var intervalTime = DateTime.Now - m_firstTime;
  770. float milliSeconds = intervalTime.Seconds * 1000 + intervalTime.Milliseconds;
  771. if (milliSeconds >= 400)
  772. resetTime();
  773. }
  774. // 按下按钮时对两次的时间进行记录
  775. if (m_firstTime.Equals(default(DateTime)))
  776. m_firstTime = DateTime.Now;
  777. else
  778. m_secondTime = DateTime.Now;
  779. // 在第二次点击触发,时差小于400ms触发
  780. if (!m_firstTime.Equals(default(DateTime)) && !m_secondTime.Equals(default(DateTime)))
  781. {
  782. var intervalTime = m_secondTime - m_firstTime;
  783. float milliSeconds = intervalTime.Seconds * 1000 + intervalTime.Milliseconds;
  784. if (milliSeconds < 400)
  785. Press();
  786. else
  787. resetTime();
  788. }
  789. }
  790. private void resetTime()
  791. {
  792. m_firstTime = default(DateTime);
  793. m_secondTime = default(DateTime);
  794. }
  795. #endregion
  796. #region 性能检测相关
  797. void InvalidateTimings()
  798. {
  799. m_ValidHistoryFrames = 0;
  800. m_AverageTime = float.NaN;
  801. m_MedianTime = float.NaN;
  802. m_MinTime = float.NaN;
  803. m_MaxTime = float.NaN;
  804. }
  805. void UpdateInputs()
  806. {
  807. //重置
  808. if (Input.GetKeyDown(KeyCode.UpArrow))
  809. {
  810. InvalidateTimings();
  811. }
  812. }
  813. #endregion
  814. }