ScreenLocateTestOld.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. using o0.Geometry2D.Float;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Net.NetworkInformation;
  8. using System.Threading.Tasks;
  9. using TMPro;
  10. using Unity.VisualScripting;
  11. using UnityEngine;
  12. using UnityEngine.UI;
  13. using ZIM;
  14. using ZIM.Unity;
  15. public class ScreenLocateTestOld : MonoBehaviour
  16. {
  17. public ZIMWebCamera webCamera;
  18. public TextMeshProUGUI info;
  19. public TextMeshProUGUI location;
  20. public RawImage crosshairInScreen;
  21. public RawImage background;
  22. public Transform Screen;
  23. //public Texture2D TextureTest;
  24. public RawImage testImage; // 测试用
  25. public RawImage testImage2; // 测试用
  26. public InfraredLocateTest infraredLocateTest;
  27. Rect canvasRect;
  28. Quadrilateral screenQuad;
  29. int record;
  30. float[] brightnessRecord; // record > 0 时记录一段时间内图像亮度的均值
  31. void Start()
  32. {
  33. canvasRect = webCamera.GetComponent<RectTransform>().rect;
  34. record = 0;
  35. //TextureTest = new Texture2D(2, 2);
  36. //TextureTest.wrapMode = TextureWrapMode.Clamp;
  37. //TextureTest.filterMode = FilterMode.Point;
  38. //TextureTest.SetPixel(0, 0, new UnityEngine.Color(1f, 0f, 0f));
  39. //TextureTest.SetPixel(1, 0, new UnityEngine.Color(0f, 1f, 0f));
  40. //TextureTest.SetPixel(0, 1, new UnityEngine.Color(0f, 0f, 1f));
  41. //TextureTest.SetPixel(1, 1, UnityEngine.Color.white);
  42. //TextureTest.Apply();
  43. //Color[] pixels = TextureTest.GetPixels();
  44. //location.text = pixels[0].ToString() + pixels[1].ToString() + pixels[2].ToString();
  45. }
  46. //int frame = 2;
  47. void Update()
  48. {
  49. WebCamTexture texture = webCamera?.webCamTexture;
  50. if (texture == null) return;
  51. if (texture.didUpdateThisFrame) // 检查camera是否更新帧
  52. {
  53. //frame--;
  54. //if (frame != 0)
  55. // return;
  56. //frame = 2;
  57. //var watch = new System.Diagnostics.Stopwatch();
  58. //watch.Start();
  59. //var times = new List<double>() { 0.0 };
  60. Color[] pixels = infraredLocateTest.pixels;
  61. if (record > 0)
  62. {
  63. if (record == 1)
  64. brightnessRecord = new float[pixels.Length];
  65. Parallel.For(0, pixels.Length, (i) =>
  66. {
  67. brightnessRecord[i] += (pixels[i].Brightness(64) - brightnessRecord[i]) / record;
  68. });
  69. record++;
  70. }
  71. else
  72. {
  73. //var il = infraredLocateTest.InfraredLocation;
  74. //if (il != null)
  75. //{
  76. // // 检测到亮点
  77. // if (screenQuad != null) // 已定位了屏幕区域
  78. // {
  79. // var uv = screenQuad.InterpolationFactors(il.Value.o0Vector());
  80. // var posInScreen = new Vector2(canvasRect.width * (uv.x - 0.5f), canvasRect.height * (uv.y - 0.5f));
  81. // crosshairInScreen.gameObject.SetActive(true);
  82. // crosshairInScreen.rectTransform.anchoredPosition = posInScreen;
  83. // location.text = "uv: " + uv;
  84. // }
  85. //}
  86. //else
  87. //{
  88. // // 没有检测到亮点
  89. // crosshairInScreen.gameObject.SetActive(false);
  90. //}
  91. }
  92. //times.Add(watch.ElapsedMilliseconds);
  93. //UnityEngine.Debug.Log("time: " + (times[times.Count - 1] - times[times.Count - 2]));
  94. }
  95. //if (Input.GetKeyUp(KeyCode.Q))
  96. //{
  97. // background.gameObject.SetActive(!background.gameObject.activeSelf);
  98. // testImage.gameObject.SetActive(!testImage.gameObject.activeSelf);
  99. // testImage2.gameObject.SetActive(!testImage2.gameObject.activeSelf);
  100. //}
  101. //if (Input.GetKeyUp(KeyCode.R))
  102. //{
  103. // SavePng("test");
  104. //}
  105. }
  106. public void SavePng(string name, Color[] pixels = null)
  107. {
  108. var testTexture = new Texture2D((int)webCamera.width, (int)webCamera.height);
  109. testTexture.wrapMode = TextureWrapMode.Clamp;
  110. testTexture.filterMode = FilterMode.Point;
  111. if (pixels == null)
  112. pixels = webCamera.webCamTexture.GetPixels();
  113. for (int i = 0; i < pixels.Count(); i++)
  114. {
  115. var vec = webCamera.IndexToCoord(i);
  116. testTexture.SetPixel(vec.x, vec.y, pixels[i]);
  117. }
  118. testTexture.Apply();
  119. var bytes = testTexture.EncodeToPNG();
  120. File.WriteAllBytes($"{name}.png", bytes);
  121. }
  122. public void SavePngFloat(string name, float[] pixels, int scale = 10)
  123. {
  124. var testTexture = new Texture2D((int)webCamera.width, (int)webCamera.height);
  125. testTexture.wrapMode = TextureWrapMode.Clamp;
  126. testTexture.filterMode = FilterMode.Point;
  127. for (int i = 0; i < pixels.Count(); i++)
  128. {
  129. var vec = webCamera.IndexToCoord(i);
  130. var f = Math.Min(pixels[i] / 64 * scale, 1);
  131. testTexture.SetPixel(vec.x, vec.y, new Color(f, f, f, 1));
  132. }
  133. testTexture.Apply();
  134. var bytes = testTexture.EncodeToPNG();
  135. File.WriteAllBytes($"{name}.png", bytes);
  136. }
  137. public void DoScreenLocate()
  138. {
  139. info.gameObject.SetActive(false);
  140. StartCoroutine(ScreenLocateCorutine2());
  141. //StartCoroutine(ScreenTestCoruine());
  142. }
  143. // 用协程控制游戏画面闪烁,用来识别屏幕
  144. IEnumerator ScreenLocateCorutine()
  145. {
  146. background.color = Color.white;
  147. background.gameObject.SetActive(true);
  148. yield return new WaitForSeconds(0.3f); //摄像机画面有延时
  149. //while (true)
  150. //{
  151. // if (webCamera.webCamTexture.didUpdateThisFrame)
  152. // break;
  153. // else
  154. // yield return null;
  155. //}
  156. record = 1;
  157. yield return new WaitForSeconds(0.8f);
  158. record = 0;
  159. float[] brightness0 = (float[])brightnessRecord.Clone();
  160. Color[] piexl0 = webCamera.webCamTexture.GetPixels();
  161. background.color = Color.black;
  162. yield return new WaitForSeconds(0.3f);
  163. record = 1;
  164. yield return new WaitForSeconds(0.8f);
  165. float[] brightness1 = (float[])brightnessRecord.Clone();
  166. record = 0;
  167. Color[] piexl1 = webCamera.webCamTexture.GetPixels();
  168. background.color = Color.white;
  169. yield return new WaitForSeconds(0.3f);
  170. record = 1;
  171. yield return new WaitForSeconds(0.8f);
  172. float[] brightness2 = (float[])brightnessRecord.Clone();
  173. record = 0;
  174. Color[] piexl2 = webCamera.webCamTexture.GetPixels();
  175. background.color = Color.black;
  176. yield return new WaitForSeconds(0.3f);
  177. record = 1;
  178. yield return new WaitForSeconds(0.8f);
  179. float[] brightness3 = (float[])brightnessRecord.Clone();
  180. record = 0;
  181. Color[] piexl3 = webCamera.webCamTexture.GetPixels();
  182. //background.gameObject.SetActive(false);
  183. yield return null;
  184. // 用 int数组 记录随着屏幕亮度变化的点,变化的记为 1,其他为 0
  185. int[] pixelsInScreen = new int[brightness0.Length];
  186. const float threshold0 = 0.1f;
  187. const float threshold1 = 300f;
  188. Parallel.For(0, (int)webCamera.width, (i) =>
  189. {
  190. for (int j = 0; j < webCamera.height; j++)
  191. {
  192. var index = webCamera.CoordToIndex(i, j);
  193. var a0 = brightness0[index];
  194. var a1 = brightness1[index];
  195. var a2 = brightness2[index];
  196. var a3 = brightness3[index];
  197. var b0 = Math.Pow(a0 + 1, 8);
  198. var b1 = Math.Pow(a1 + 1, 8);
  199. var b2 = Math.Pow(a2 + 1, 8);
  200. var b3 = Math.Pow(a3 + 1, 8);
  201. //var b0 = getBrightness(piexl0[index]);
  202. //var b1 = getBrightness(piexl1[index]);
  203. //var b2 = getBrightness(piexl2[index]);
  204. //var b3 = getBrightness(piexl3[index]);
  205. if (a0 - a1 > threshold0 && a2 - a1 > threshold0 && a0 - a3 > threshold0 && a2 - a3 > threshold0 &&
  206. b0 - b1 > threshold1 && b2 - b1 > threshold1 && b0 - b3 > threshold1 && b2 - b3 > threshold1)
  207. pixelsInScreen[index] = 1;
  208. }
  209. });
  210. // 测试亮度变化的屏幕区域
  211. var testTexture = new Texture2D((int)webCamera.width, (int)webCamera.height);
  212. testTexture.wrapMode = TextureWrapMode.Clamp;
  213. testTexture.filterMode = FilterMode.Point;
  214. for (int i = 0; i < pixelsInScreen.Count(); i++)
  215. {
  216. var vec = webCamera.IndexToCoord(i);
  217. var c = 0.5f + 0.5f * pixelsInScreen[i];
  218. testTexture.SetPixel(vec.x, vec.y, new Color(c, c, c, 1));
  219. }
  220. testTexture.Apply();
  221. testImage.gameObject.SetActive(true);
  222. testImage.texture = testTexture;
  223. // 处理得到屏幕的边缘像素点,拟合成四边形
  224. var Edge = PixlesProcessToEdge(pixelsInScreen);
  225. //screenQuad = Quadrilateral.Fit(Edge, webCamera.Size);
  226. //Debug.Log(ScreenQuad);
  227. info.gameObject.SetActive(true);
  228. info.text = "Locate Complete!\r\n" + screenQuad;
  229. // 标记屏幕的四个角
  230. Screen.gameObject.SetActive(true);
  231. for (int i = 0; i < 4; i++)
  232. {
  233. RectTransform t = (RectTransform)Screen.GetChild(i);
  234. t.anchoredPosition = screenQuad[i].UnityVector().pixelToLocalPosition_AnchorCenter(webCamera.Size, webCamera.rawImage.rectTransform.rect);
  235. }
  236. //if (ScreenQuad.IsInScreen(cameraSize))
  237. // info.text = "Screen Locate succeed";
  238. //else
  239. // info.text = "Screen Locate failed";
  240. // 测试识别的屏幕区域
  241. var testTexture2 = new Texture2D(webCamera.width,webCamera.height);
  242. testTexture2.wrapMode = TextureWrapMode.Clamp;
  243. testTexture2.filterMode = FilterMode.Point;
  244. for (int i = 0; i < pixelsInScreen.Count(); i++)
  245. {
  246. var vec = webCamera.IndexToCoord(i);
  247. var c = 0.5f + 0.5f * pixelsInScreen[i];
  248. testTexture2.SetPixel(vec.x, vec.y, new Color(c, c, c, 1));
  249. }
  250. testTexture2.Apply();
  251. testImage2.gameObject.SetActive(true);
  252. testImage2.texture = testTexture2;
  253. }
  254. IEnumerator ScreenLocateCorutine2()
  255. {
  256. background.color = Color.black;
  257. background.gameObject.SetActive(true);
  258. yield return new WaitForSeconds(0.4f); //摄像机画面有延时
  259. record = 1;
  260. yield return new WaitForSeconds(1.0f);
  261. record = 0;
  262. float[] brightness0 = (float[])brightnessRecord.Clone();
  263. var mean0 = brightnessRecord.Sum() / brightnessRecord.Length;
  264. //Debug.Log(mean0);
  265. SavePngFloat("显示器黑色", brightness0);
  266. background.color = Color.white;
  267. yield return new WaitForSeconds(0.4f);
  268. // 测试当前画面
  269. Color[] _piexl = webCamera.webCamTexture.GetPixels();
  270. var testTexture = new Texture2D(webCamera.width, webCamera.height);
  271. testTexture.wrapMode = TextureWrapMode.Clamp;
  272. testTexture.filterMode = FilterMode.Point;
  273. for (int i = 0; i < _piexl.Count(); i++)
  274. {
  275. var vec = webCamera.IndexToCoord(i);
  276. testTexture.SetPixel(vec.x, vec.y, _piexl[i]);
  277. }
  278. testTexture.Apply();
  279. int[] BrightnessValue = new int[brightness0.Length];
  280. int? threshold = null;
  281. for (int i = 0; i < 20; i++)
  282. {
  283. record = 1;
  284. yield return new WaitForSeconds(0.3f);
  285. record = 0;
  286. // 求阈值
  287. if (threshold == null)
  288. {
  289. var mean = brightnessRecord.Sum() / brightnessRecord.Length;
  290. //Debug.Log(mean);
  291. threshold = (int)Math.Ceiling(Math.Pow(mean - mean0, 2) * 4);
  292. threshold = (int)Math.Pow((double)threshold, 2);
  293. //Debug.Log("threshold: " + threshold);
  294. SavePngFloat("显示器白色", brightnessRecord);
  295. }
  296. Parallel.For(0, brightness0.Length, (j) =>
  297. {
  298. if (BrightnessValue[j] >= 0)
  299. {
  300. var value = (brightnessRecord[j] - brightness0[j]) * 100;
  301. if (value > threshold)
  302. BrightnessValue[j] = 1;
  303. else
  304. {
  305. lock (BrightnessValue)
  306. BrightnessValue[j] = -1;
  307. }
  308. }
  309. });
  310. }
  311. background.color = Color.black;
  312. //background.gameObject.SetActive(false);
  313. yield return null;
  314. // 处理得到屏幕的边缘像素点,拟合成四边形
  315. var Edge = PixlesProcessToEdge(BrightnessValue);
  316. //screenQuad = Quadrilateral.Fit(Edge, webCamera.Size);
  317. //Debug.Log(ScreenQuad);
  318. info.gameObject.SetActive(true);
  319. info.text = $"Locate Complete!\r\nthreshold: {threshold}\r\n{screenQuad}";
  320. // 标记屏幕的四个角
  321. Screen.gameObject.SetActive(true);
  322. for (int i = 0; i < 4; i++)
  323. {
  324. RectTransform t = (RectTransform)Screen.GetChild(i);
  325. t.anchoredPosition =screenQuad[i].UnityVector().pixelToLocalPosition_AnchorCenter(webCamera.Size, webCamera.rawImage.rectTransform.rect);
  326. }
  327. //if (ScreenQuad.IsInScreen(cameraSize))
  328. // info.text = "Screen Locate succeed";
  329. //else
  330. // info.text = "Screen Locate failed";
  331. // 测试识别的屏幕区域
  332. testImage.gameObject.SetActive(true);
  333. testImage.texture = testTexture;
  334. var testTexture2 = new Texture2D(webCamera.width, webCamera.height);
  335. testTexture2.wrapMode = TextureWrapMode.Clamp;
  336. testTexture2.filterMode = FilterMode.Point;
  337. for (int i = 0; i < BrightnessValue.Count(); i++)
  338. {
  339. var vec = webCamera.IndexToCoord(i);
  340. var c = 0.5f + 0.5f * BrightnessValue[i];
  341. testTexture2.SetPixel(vec.x, vec.y, new Color(c, c, c, 1));
  342. }
  343. testTexture2.Apply();
  344. testImage2.gameObject.SetActive(true);
  345. testImage2.texture = testTexture2;
  346. }
  347. IEnumerator ScreenTestCoruine()
  348. {
  349. background.color = Color.black;
  350. background.gameObject.SetActive(true);
  351. yield return new WaitForSeconds(0.4f); //摄像机画面有延时
  352. record = 1;
  353. yield return new WaitForSeconds(3.0f);
  354. record = 0;
  355. float[] brightness0 = (float[])brightnessRecord.Clone();
  356. SavePngFloat("显示器黑色", brightness0);
  357. background.color = Color.white;
  358. yield return new WaitForSeconds(0.4f);
  359. record = 1;
  360. yield return new WaitForSeconds(3.0f);
  361. record = 0;
  362. SavePngFloat("显示器白色", brightnessRecord);
  363. var sub = new float[brightnessRecord.Count()];
  364. Parallel.For(0, brightnessRecord.Count(), (i) =>
  365. {
  366. sub[i] = brightnessRecord[i] - brightness0[i];
  367. });
  368. SavePngFloat("亮度差值", sub);
  369. background.gameObject.SetActive(false);
  370. }
  371. HashSet<Vector2> PixlesProcessToEdge(int[] pixelsInScreen)
  372. {
  373. // 去噪
  374. {
  375. int[] pixelsInScreen2 = new int[pixelsInScreen.Length];
  376. int[] pixelsIn, pixelsOut;
  377. for (int n = 0; n < 6; n++)
  378. {
  379. if (n % 2 == 0)
  380. {
  381. pixelsIn = pixelsInScreen;
  382. pixelsOut = pixelsInScreen2;
  383. }
  384. else
  385. {
  386. pixelsIn = pixelsInScreen2;
  387. pixelsOut = pixelsInScreen;
  388. }
  389. Parallel.For(0, (int)(webCamera.width), (i) =>
  390. {
  391. for (int j = 0; j < webCamera.height; j++)
  392. {
  393. var index = webCamera.CoordToIndex(i, j);
  394. pixelsOut[index] = 0;
  395. if (pixelsIn[index] == 1)
  396. {
  397. var conv = Conv2DAverageInt(pixelsIn, new Vector2Int(i, j), 7, 7 - n % 4);
  398. if (conv > 0.25f)
  399. {
  400. //Debug.Log("white");
  401. pixelsOut[index] = 1;
  402. }
  403. }
  404. }
  405. });
  406. }
  407. }
  408. // 边缘提取
  409. HashSet<Vector2> Edge = new HashSet<Vector2>();
  410. int[] pixelsLast = new int[pixelsInScreen.Length];
  411. Parallel.For(0, (int)(webCamera.width), (i) =>
  412. {
  413. for (int j = 0; j < webCamera.height; j++)
  414. {
  415. var index = webCamera.CoordToIndex(i, j);
  416. pixelsLast[index] = 0;
  417. if (pixelsInScreen[index] == 1)
  418. {
  419. if (EdgeCalculation(pixelsInScreen, new Vector2Int(i, j), 5))
  420. {
  421. lock (Edge)
  422. Edge.Add(new Vector2(i, j));
  423. pixelsLast[index] = 1;
  424. }
  425. }
  426. }
  427. });
  428. return Edge;
  429. }
  430. //float Conv2DAverage(Color[] pixels, int index, int kernel_size, int scale)
  431. //{
  432. // var vec = indexToVector2(index);
  433. // var sum = 0f;
  434. // for (int i = vec.x - kernel_size / 2 * scale; i < vec.x + kernel_size / 2 * scale; i += scale)
  435. // {
  436. // for (int j = vec.y - kernel_size / 2 * scale; j < vec.y + kernel_size / 2 * scale; j += scale)
  437. // {
  438. // var index2 = Vector2ToIndex(i, j);
  439. // if (index2 > 0 && index2 < pixels.Length) // 超出边缘不计算
  440. // sum += getBrightness(pixels[index2]);
  441. // }
  442. // }
  443. // return sum / kernel_size / kernel_size;
  444. //}
  445. float Conv2DAverageInt(int[] pixels, Vector2Int vec, int kernel_size, int scale)
  446. {
  447. var sum = 0f;
  448. for (int i = vec.x - kernel_size / 2 * scale; i < vec.x + kernel_size / 2 * scale; i += scale)
  449. {
  450. for (int j = vec.y - kernel_size / 2 * scale; j < vec.y + kernel_size / 2 * scale; j += scale)
  451. {
  452. var index2 = webCamera.CoordToIndex(i, j);
  453. if (index2 > 0 && index2 < pixels.Length) // 超出边缘不计算
  454. sum += pixels[index2];
  455. }
  456. }
  457. return sum / kernel_size / kernel_size;
  458. }
  459. bool EdgeCalculation(int[] pixels, Vector2Int vec, int kernel_size)
  460. {
  461. var sum = 0f;
  462. for (int i = vec.x - kernel_size / 2; i < vec.x + kernel_size / 2; i++)
  463. {
  464. for (int j = vec.y - kernel_size / 2; j < vec.y + kernel_size / 2; j++)
  465. {
  466. var index2 = webCamera.CoordToIndex(i, j);
  467. if (i == vec.x && j == vec.y)
  468. sum += (kernel_size * kernel_size - 1) * pixels[index2];
  469. else if (index2 > 0 && index2 < pixels.Length) // 超出边缘不计算
  470. sum -= pixels[index2];
  471. }
  472. }
  473. return sum > kernel_size * kernel_size / 2;
  474. }
  475. }