ScreenIdentification.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  1. #define ENABLE_LOG
  2. using o0.Geometry2D.Float;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Threading.Tasks;
  7. using UnityEngine;
  8. using UnityStandardAssets.ImageEffects;
  9. using ZIM;
  10. using ZIM.Unity;
  11. namespace o0.Project
  12. {
  13. public partial class ScreenIdentification
  14. {
  15. private const string TAG = "ScreenIdentification#";
  16. //static Rect[][] LocateAreaData = new Rect[][] {
  17. // new Rect[] { new Rect(0f, 0f, 0.3f, 0.3f), new Rect(0f, 0f, 0.4f, 0.4f), new Rect(0f, 0f, 0.5f, 0.5f), new Rect(0f, 0f, 0.6f, 0.6f) },
  18. // new Rect[] { new Rect(0.7f, 0f, 0.3f, 0.3f), new Rect(0.6f, 0f, 0.4f, 0.4f), new Rect(0.5f, 0f, 0.5f, 0.5f), new Rect(0.4f, 0f, 0.6f, 0.6f) },
  19. // new Rect[] { new Rect(0f, 0.7f, 0.3f, 0.3f), new Rect(0f, 0.6f, 0.4f, 0.4f), new Rect(0f, 0.5f, 0.5f, 0.5f), new Rect(0f, 0.4f, 0.6f, 0.6f) },
  20. // new Rect[] { new Rect(0.7f, 0.7f, 0.3f, 0.3f), new Rect(0.6f, 0.6f, 0.4f, 0.4f), new Rect(0.5f, 0.5f, 0.5f, 0.5f), new Rect(0.4f, 0.4f, 0.6f, 0.6f) }
  21. //};
  22. static Rect[][] LocateAreaData = new Rect[][] {
  23. new Rect[] { new Rect(0f, 0f, 0.3f, 0.3f), new Rect(0f, 0f, 0.4f, 0.4f), new Rect(0f, 0f, 0.5f, 0.5f) },
  24. new Rect[] { new Rect(0.7f, 0f, 0.3f, 0.3f), new Rect(0.6f, 0f, 0.4f, 0.4f), new Rect(0.5f, 0f, 0.5f, 0.5f) },
  25. new Rect[] { new Rect(0f, 0.7f, 0.3f, 0.3f), new Rect(0f, 0.6f, 0.4f, 0.4f), new Rect(0f, 0.5f, 0.5f, 0.5f) },
  26. new Rect[] { new Rect(0.7f, 0.7f, 0.3f, 0.3f), new Rect(0.6f, 0.6f, 0.4f, 0.4f), new Rect(0.5f, 0.5f, 0.5f, 0.5f) }
  27. };
  28. //static bool LocateDebug = false;
  29. static bool LocateDebug = true;
  30. public Geometry2D.Vector<int> Size => ScreenLocate.Main.CameraSize;
  31. public ScreenMap Screen; // 识别到的屏幕,用于执行透视变换
  32. int capture = 0;
  33. int delay = 0;
  34. int maxCapture;
  35. int maxDelay;
  36. Geometry.Vector<float>[] ScreenBlackTexture;
  37. Geometry.Vector<float>[] ScreenWhiteTexture;
  38. int locateIndex = -1;
  39. List<Rect> locateArea = new List<Rect> {
  40. new Rect(0f, 0f, 0.5f, 0.5f), new Rect(0.5f, 0f, 0.5f, 0.5f), new Rect(0f, 0.5f, 0.5f, 0.5f), new Rect(0.5f, 0.5f, 0.5f, 0.5f)
  41. }; // 屏幕显示白色的区域大小
  42. float areaPercent => locateArea[locateIndex].size.x; // 当前白色区域的占比
  43. int areaSelected = -1; // 选择哪个区域,顺序与Quadrilateral对应
  44. List<float> sumTemp = new List<float>();
  45. List<OrdinalQuadrilateral> quadTemp = new List<OrdinalQuadrilateral>();
  46. //public ScreenIdentification(WebCamTexture texture)
  47. //{
  48. // Size = new Geometry2D.Vector<int>(texture.width, texture.height);
  49. // Screen = new ScreenMap();
  50. //}
  51. public static UnityEngine.Color FloatValueToColor(float i)
  52. {
  53. switch (i)
  54. {
  55. case 1:
  56. return UnityEngine.Color.green;
  57. case 2:
  58. return UnityEngine.Color.red;
  59. case 3:
  60. return UnityEngine.Color.yellow;
  61. default:
  62. return UnityEngine.Color.black;
  63. }
  64. }
  65. public ScreenIdentification()
  66. {
  67. Screen = new ScreenMap();
  68. //OnLocateScreenEnter += () => Debug.Log("OnLocateScreenEnter");
  69. //OnLocateScreenEnd += () => Debug.Log("OnLocateScreenEnd");
  70. }
  71. public void SetScreenQuad(QuadrilateralInCamera quad) => Screen.QuadInCamera = quad;
  72. public event Action OnLocateScreenEnter;
  73. public event Action OnLocateScreenEnd;
  74. public bool bStartLocateScreen { get; set; } = false;//是否进行捕获
  75. // 自动识别开始的入口
  76. public void LocateScreen(int Capture = 30, int Delay = 30) //数值单位是frame
  77. {
  78. if (ScreenLocate.Main.DebugScreenImage != null && ScreenLocate.Main.DebugOnEditorWin) // 这段仅用于测试图片
  79. {
  80. ScreenLocate.Main.CameraSize = new Geometry2D.Vector<int>(ScreenLocate.Main.DebugScreenImage.width, ScreenLocate.Main.DebugScreenImage.height);
  81. DebugImage(ScreenLocate.Main.DebugScreenImage);
  82. Screen.QuadInCamera = new QuadrilateralInCamera(quadTemp[0], new Vector(ScreenLocate.Main.DebugScreenImage.width, ScreenLocate.Main.DebugScreenImage.height));
  83. ScreenLocate.SetScreen(null);
  84. ScreenLocate.Main.ShowScreen(ScreenLocate.Main.ScreenQuad, Screen.QuadInCamera);
  85. delay = 0;
  86. capture = 0;
  87. ScreenWhiteTexture = null;
  88. ScreenBlackTexture = null;
  89. locateIndex = -1;
  90. areaSelected = -1;
  91. quadTemp.Clear();
  92. sumTemp.Clear();
  93. return;
  94. }
  95. delay = Math.Max(Delay, 5);
  96. capture = Math.Max(Capture, 5);
  97. maxDelay = Delay;
  98. maxCapture = Capture;
  99. ScreenLocate.SetScreen(new Rect(0f, 0f, 1f, 1f), UnityEngine.Color.black);
  100. //ScreenLocate.SetScreen(new Rect(0f, 0f, 0.6f, 0.6f), UnityEngine.Color.white);
  101. //bStartLocateScreen = false;
  102. OnLocateScreenEnter?.Invoke();
  103. }
  104. /// <summary>
  105. /// 开始进行捕获
  106. /// 初始化了两个数据 capture 和 delay
  107. /// </summary>
  108. /// <returns></returns>
  109. public bool isInitLocateScreen() {
  110. return capture != 0 && delay != 0;
  111. }
  112. void DebugImage(Texture2D image)
  113. {
  114. QuadrilateralFit(out Texture2D LocateLightedRedTex,out Texture2D ChoosableLineTex, out Texture2D ScreenQuadTex, 5, image);
  115. ScreenLocate.DebugTexture(2, LocateLightedRedTex);
  116. ScreenLocate.DebugTexture(3, ScreenQuadTex);
  117. // 融合线段和原图
  118. ScreenLocate.DebugTexture(4, image.Merge(ScreenQuadTex));
  119. ScreenLocate.DebugTexture(5, ChoosableLineTex);
  120. //var watch = new System.Diagnostics.Stopwatch();
  121. //watch.Start();
  122. //var times = new List<double>() { 0.0 };
  123. #if (!NDEBUG && DEBUG && ENABLE_LOG)
  124. Console.WriteLine($"{TAG} quadTemp.Count:{quadTemp.Count}");
  125. #endif
  126. if (quadTemp.Count > 0)
  127. {
  128. var quad = quadTemp[0];
  129. ScreenLocate.Main.ShowScreen(ScreenLocate.Main.outputRawImages[4].transform.GetChild(0) as RectTransform,
  130. new QuadrilateralInCamera(quad, image.Size().o0Vector()));
  131. // 透视变换
  132. var srcWidth = LocateLightedRedTex.width;
  133. var transformWidth = (int)((quad.B.x - quad.A.x + quad.D.x - quad.C.x) / 2);
  134. var transformHeight = (int)((quad.C.y - quad.A.y + quad.D.y - quad.B.y) / 2);
  135. var transformTex = new Texture2D(transformWidth, transformHeight);
  136. var pt = new ZIMPerspectiveTransform(new OrdinalQuadrilateral(new Vector(0, 0), new Vector(transformWidth, 0), new Vector(0, transformHeight), new Vector(transformWidth, transformHeight)), quad);
  137. var dstPixel = new UnityEngine.Color[transformWidth * transformHeight];
  138. var srcPixel = LocateLightedRedTex.GetPixels();
  139. Parallel.For(0, transformWidth, (x) =>
  140. {
  141. for (int y = 0; y < transformHeight; y++)
  142. {
  143. var index = y * transformWidth + x;
  144. var sampleCoord = pt.TransformRound(x, y);
  145. dstPixel[index] = srcPixel[sampleCoord.y * srcWidth + sampleCoord.x];
  146. }
  147. });
  148. transformTex.SetPixels(dstPixel);
  149. transformTex.Apply();
  150. //ScreenLocate.DebugTexture(1, transformTex);
  151. #if (!NDEBUG && DEBUG && ENABLE_LOG)
  152. Console.WriteLine($"{TAG} ScreenLocate.DebugTexture 1:{transformTex.GetNativeTexturePtr()}");
  153. #endif
  154. }
  155. //times.Add(watch.ElapsedMilliseconds);
  156. //UnityEngine.Debug.Log("time: " + (times[times.Count - 1] - times[times.Count - 2]));
  157. }
  158. public void NextScreen()
  159. {
  160. // 测试用
  161. if (LocateDebug && areaSelected == -1)
  162. {
  163. LocateAreaData = new Rect[][] { new Rect[] { new Rect(0, 0, 1f, 1f) } };
  164. locateIndex = 3;
  165. areaSelected = 0;
  166. locateArea.AddRange(LocateAreaData[0]);
  167. }
  168. // index从-1开始
  169. locateIndex++;
  170. if (locateIndex < locateArea.Count) // 依次点亮屏幕区域
  171. {
  172. ScreenLocate.SetScreen(locateArea[locateIndex], UnityEngine.Color.white);
  173. delay = maxDelay;
  174. capture = maxCapture;
  175. }
  176. else // 退出屏幕黑白控制
  177. {
  178. ScreenLocate.SetScreen(null);
  179. ScreenLocate.Main.ShowScreen(ScreenLocate.Main.ScreenQuad, Screen.QuadInCamera);
  180. Reset();
  181. }
  182. }
  183. void Reset()
  184. {
  185. // bStartLocateScreen = false;
  186. delay = 0;
  187. capture = 0;
  188. ScreenWhiteTexture = null;
  189. ScreenBlackTexture = null;
  190. locateIndex = -1;
  191. areaSelected = -1;
  192. locateArea.RemoveRange(4, LocateAreaData[0].Length);
  193. quadTemp.Clear();
  194. sumTemp.Clear();
  195. }
  196. public void CaptureBlack(Texture2D cam)
  197. {
  198. if (ScreenBlackTexture == null)
  199. ScreenBlackTexture = new Geometry.Vector<float>[Size.x * Size.y];
  200. var pixel = cam.GetPixels();
  201. Parallel.For(0, Size.x * Size.y, i =>
  202. {
  203. var ip = pixel[i];
  204. ScreenBlackTexture[i] += new Geometry.Vector<float>(ip.r / maxCapture, ip.g / maxCapture, ip.b / maxCapture);
  205. });
  206. }
  207. public void CaptureWhite(Texture2D cam)
  208. {
  209. if (ScreenWhiteTexture == null)
  210. ScreenWhiteTexture = new Geometry.Vector<float>[Size.x * Size.y];
  211. var pixel = cam.GetPixels();
  212. Parallel.For(0, Size.x * Size.y, i =>
  213. {
  214. var ip = pixel[i];
  215. ScreenWhiteTexture[i] += new Geometry.Vector<float>(ip.r / maxCapture, ip.g / maxCapture, ip.b / maxCapture);
  216. });
  217. }
  218. public void CaptureStay(Texture2D cam)
  219. {
  220. if (locateIndex == -1) // 屏幕黑色
  221. {
  222. CaptureBlack(cam);
  223. }
  224. else // 屏幕部分为白色
  225. {
  226. CaptureWhite(cam);
  227. }
  228. }
  229. public void CaptureEnd()
  230. {
  231. //Debug.Log("locateIndex: " + locateIndex + ", quad: " + quadTemp.Count);
  232. if (locateIndex == -1)
  233. return;
  234. if (locateIndex < 4)
  235. {
  236. sumTemp.Add(GetBrightness());
  237. ScreenWhiteTexture = null;
  238. // 选择亮度差最大的区域
  239. if (locateIndex == 3)
  240. {
  241. areaSelected = sumTemp.MaxIndex();
  242. locateArea.AddRange(LocateAreaData[areaSelected]);
  243. }
  244. }
  245. else if (locateIndex >= 4 && locateIndex < locateArea.Count - 1)
  246. {
  247. QuadrilateralFit(out _, out _, out _);
  248. ScreenWhiteTexture = null;
  249. }
  250. else
  251. {
  252. QuadrilateralFit(out Texture2D LocateLightedRedTex,out Texture2D ChoosableLineTex, out Texture2D ScreenQuadTex);
  253. if (ScreenLocate.Main.DebugOnEditorWin)
  254. {
  255. ScreenLocate.DebugTexture(2, LocateLightedRedTex);
  256. ScreenLocate.DebugTexture(3, ScreenQuadTex);
  257. // 融合线段和原图
  258. ScreenLocate.DebugTexture(4, LocateLightedRedTex.Merge(ScreenQuadTex));
  259. ScreenLocate.DebugTexture(5, ChoosableLineTex);
  260. }
  261. if (quadTemp.Count != LocateAreaData[0].Length)
  262. {
  263. Debug.Log($"<color=yellow>[ScreenIdentification] 拟合四边形失败, quadTemp.Count: {quadTemp.Count}</color>");
  264. }
  265. else if (quadTemp.Count == 1)
  266. {
  267. Screen.QuadInCamera = new QuadrilateralInCamera(quadTemp[0], new Vector(Size.x, Size.y));
  268. Debug.Log($"<color=aqua>[ScreenIdentification] 拟合成功,Quad: {Screen.QuadInCamera.QuadString}____{Screen.QuadInCamera.SizeString}</color>");
  269. }
  270. else
  271. {
  272. // Debug.Log($"拟合四边形 2 , quadTemp.Count: {quadTemp.Count}");
  273. // 线性拟合
  274. var xValue = new List<float>() { 0 };
  275. var predicts = new List<Vector>();
  276. foreach (var i in LocateAreaData[0])
  277. xValue.Add(i.size.x);
  278. Vector baseVertex = Vector.Zero; // x==0 时的点
  279. {
  280. foreach (var q in quadTemp)
  281. {
  282. baseVertex += q[areaSelected];
  283. }
  284. baseVertex /= quadTemp.Count;
  285. }
  286. double rs = 0.0;
  287. for (int i = 0; i < 4; i++)
  288. {
  289. if (i == areaSelected)
  290. {
  291. predicts.Add(baseVertex);
  292. }
  293. else
  294. {
  295. var yValue = new List<Vector>() { baseVertex };
  296. foreach (var q in quadTemp)
  297. {
  298. yValue.Add(q[i]);
  299. }
  300. var lr = LinerRegression1D.Fit(2, xValue.ToArray(), yValue.ToArray());
  301. rs += lr.RSquared / 3;
  302. predicts.Add(lr.Predict<Vector>(1));
  303. }
  304. }
  305. Screen.QuadInCamera = new QuadrilateralInCamera(predicts, new Vector(Size.x, Size.y));
  306. Debug.Log($"<color=aqua>[ScreenIdentification] 拟合成功,RSquared: {rs}, Quad: {Screen.QuadInCamera.QuadString}____{Screen.QuadInCamera.SizeString}</color>");
  307. //if (rs < 0.8) Screen.Quad = null;
  308. }
  309. OnLocateScreenEnd?.Invoke();
  310. }
  311. }
  312. public bool Update(Texture2D cam)
  313. {
  314. //if (!bStartLocateScreen) return false;
  315. if (delay != 0)
  316. {
  317. //ScreenLocate.Main.CreateUVCTexture2DFocusSizeIfNeeded(1280, 720);
  318. delay--;
  319. if (delay == 0)
  320. {
  321. ScreenLocate.Main.CameraSize = new Geometry2D.Vector<int>(cam.width, cam.height); // 记录当前的分辨率
  322. Debug.Log("[ScreenIdentification] 采样纹理,分辨率: [" + Size.x + ", " + Size.y + "]");
  323. }
  324. return true;
  325. }
  326. if (capture != 0)
  327. {
  328. //ScreenLocate.Main.CreateUVCTexture2DFocusSizeIfNeeded(1280, 720);
  329. CaptureStay(cam);
  330. capture--;
  331. if (capture == 0)
  332. {
  333. CaptureEnd();
  334. NextScreen();
  335. }
  336. return true;
  337. }
  338. return false;
  339. #region Old
  340. /*
  341. if (delay != 0)
  342. {
  343. delay--;
  344. return true;
  345. }
  346. if (capture != 0)
  347. {
  348. capture--;
  349. if (ScreenBlackTexture == null)
  350. ScreenBlackTexture = new Geometry.Vector<float>[Size.x * Size.y];
  351. var pixel = cam.GetPixels();
  352. Parallel.For(0, Size.x * Size.y, i =>
  353. {
  354. var ip = pixel[i];
  355. ScreenBlackTexture[i] += new Geometry.Vector<float>(ip.r, ip.g, ip.b);
  356. });
  357. if (capture == 0)
  358. ScreenLocate.SetScreen(UnityEngine.Color.black);
  359. return true;
  360. }
  361. if (delay != 0)
  362. {
  363. delay--;
  364. return true;
  365. }
  366. if (capture != 0)
  367. {
  368. capture--;
  369. if (ScreenWhiteTexture == null)
  370. ScreenWhiteTexture = new Geometry.Vector<float>[Size.x * Size.y];
  371. var pixel = cam.GetPixels();
  372. Parallel.For(0, Size.x * Size.y, i =>
  373. {
  374. var ip = pixel[i];
  375. ScreenWhiteTexture[i] += new Geometry.Vector<float>(ip.r, ip.g, ip.b);
  376. });
  377. if (capture == 0)
  378. ScreenLocate.SetScreen(UnityEngine.Color.black);
  379. return true;
  380. }
  381. if (delay != 0)
  382. {
  383. delay--;
  384. return true;
  385. }
  386. if (capture != 0)
  387. {
  388. capture--;
  389. var pixel = cam.GetPixels();
  390. Parallel.For(0, Size.x * Size.y, i =>
  391. {
  392. var ip = pixel[i];
  393. ScreenWhiteTexture[i] -= new Geometry.Vector<float>(ip.r, ip.g, ip.b);
  394. });
  395. if (capture == 0)
  396. {
  397. ScreenLocate.SetScreen(null);
  398. UnityEngine.Color[] newPixel = new UnityEngine.Color[Size.x * Size.y];
  399. Parallel.For(0, Size.x * Size.y, i => {
  400. var pi = ScreenWhiteTexture[i] /= capture;
  401. newPixel[i] = new UnityEngine.Color(pi.x, pi.y, pi.z);
  402. });
  403. //读取数据
  404. //{
  405. // var fileName = "3.bin";
  406. // ScreenLocateTexture = $"2023 04 16 厦门测试数据/{fileName}".FileReadByte<Vector<float>[]>();
  407. // Debug.Log($"Read {fileName}");
  408. // Parallel.For(0, Size.x * Size.y, i =>
  409. // {
  410. // var pi = ScreenLocateTexture[i];
  411. // newPixel[i] = new UnityEngine.Color(pi.x, pi.y, pi.z);
  412. // });
  413. //}
  414. var ScreenLocateTex = new Texture2D(Size.x, Size.y);
  415. ScreenLocateTex.SetPixels(newPixel);
  416. ScreenLocateTex.Apply();
  417. //ScreenLocate.DebugTexture(2, ScreenLocateTex);
  418. var ScreenLocateTexLighted = ScreenLocateTex.AutoLight(10);
  419. //ScreenLocate.DebugTexture(2, ScreenLocateTexLighted);
  420. //var FileSavePath = Application.persistentDataPath + "/ScreenLocateTexture.bin";
  421. bool Save = ScreenLocate.Main.SaveToggle.isOn;
  422. string time;
  423. if (Save)
  424. {
  425. time = DateTime.Now.ToString("yyyyMMdd_HHmmss");
  426. var FileSavePath = $"屏幕定位数据{time}.bin";
  427. FileSavePath.FileWriteByte(ScreenWhiteTexture);
  428. var bytes = ScreenLocateTexLighted.EncodeToPNG();
  429. File.WriteAllBytes($"屏幕定位数据{time}.png", bytes);
  430. Debug.Log("ScreenLocateTexture Saved To: " + FileSavePath);
  431. }
  432. var ScreenLocateTexR = ScreenLocateTexLighted.ToRGB(ColorChannel.Red);
  433. var ScreenLocateTexG = ScreenLocateTexLighted.ToRGB(ColorChannel.Green);
  434. var ScreenLocateTexB = ScreenLocateTexLighted.ToRGB(ColorChannel.Blue);
  435. ScreenLocate.DebugTexture(2, ScreenLocateTexR);
  436. //ScreenLocate.DebugTexture(4, ScreenLocateTexG);
  437. //ScreenLocate.DebugTexture(5, ScreenLocateTexB);
  438. var watch = new System.Diagnostics.Stopwatch();
  439. watch.Start();
  440. var times = new List<double>() { 0.0 };
  441. var ScreenLocateTexLightedMat = ScreenLocateTexLighted.Too0Mat();
  442. //var ScreenLocateTexLightedMat = texture.Too0Mat();
  443. //var (edge, edgeDir) = ScreenLocateTexLightedMat.IdentifyEdge();
  444. var (edge, edgeDir) = ScreenLocateTexLightedMat.zimIdentifyEdgeGradientAny(15);
  445. //ScreenLocate.DebugTexture(4, ScreenLocateTexLighted.Too0Mat().IdentifyEdgeGradient().ToTex());
  446. //ScreenLocate.DebugTexture(4, edge.ToTex());
  447. var quadLines = ScreenLocateTexLightedMat.IdentifyQuadLSD(edge, edgeDir, out List<Line> lightLines, 30);
  448. var drawLineMap = new MatrixF2D(edge..Size.x, edge.Size.y);
  449. int lineCount = 0;
  450. foreach (var l in quadLines)
  451. {
  452. if (l != null)
  453. {
  454. o0Extension.DrawLine(drawLineMap.DrawLine(l, (x, y) => 1, new Geometry2D.Float.Vector(0, 10));
  455. lineCount++;
  456. }
  457. }
  458. if (lineCount == 4)
  459. {
  460. var a = quadLines[0].Intersect(quadLines[3], false).Value;
  461. var b = quadLines[0].Intersect(quadLines[1], false).Value;
  462. var c = quadLines[2].Intersect(quadLines[3], false).Value;
  463. var d = quadLines[1].Intersect(quadLines[2], false).Value;
  464. Quad = new Quadrilateral(a, b, c, d);
  465. if (!Quad.IsInScreen(ScreenLocate.Main.WebCamera.Size))
  466. Quad = null;
  467. }
  468. ScreenLocate.Main.ShowScreen(Quad);
  469. //var lines = edge.IdentifyLineLSD(edgeDir, 100);
  470. ////var lines = ScreenLocateTexLightedMat.IdentifyLineLSD();
  471. //var drawLineMap = new MatrixF2D(edge..Size.x, edge.Size.y);
  472. //var returnMaxLines = lines.Sub(0, 10);
  473. //foreach (var (line, sum, gradient) in returnMaxLines)
  474. // o0Extension.DrawLine(drawLineMap.DrawLine(line, (x, y) => 1, new Geometry2D.Float.Vector(0, 10));
  475. ScreenLocate.DebugTexture(3, drawLineMap.ToTex());
  476. //{
  477. // var bytes = drawLineMap.ToTex().EncodeToPNG();
  478. // File.WriteAllBytes($"屏幕定位数据DrawLineMap.png", bytes);
  479. //}
  480. times.Add(watch.ElapsedMilliseconds);
  481. UnityEngine.Debug.Log("time: " + (times[times.Count - 1] - times[times.Count - 2]));
  482. //ScreenLocate.DebugTexture(5, edge.IdentifyLine(edgeDir).ToTex());
  483. //ScreenLocate.DebugTexture(4, ScreenLocateTexLighted.Too0Mat().IdentifyEdgeGradientX().ToTex());
  484. //ScreenLocate.DebugTexture(5, ScreenLocateTexLighted.Too0Mat().IdentifyEdgeGradientY().ToTex());
  485. //var convolutionLighted2 = ScreenLocateTexLighted.Too0Mat().IdentifyEdgeVariance().ToTex();
  486. // opecncv处理
  487. // zim
  488. {
  489. //var cvLines = edge.cvHoughLinesP();
  490. //ScreenLocate.DebugTexture(5, cvLines);
  491. //var myLines = Hough.Transform(edgeMat);
  492. //var cvLines = edge.cvLine(myLines);
  493. //ScreenLocate.DebugTexture(5, cvLines);
  494. }
  495. UnityEngine.Object.Destroy(ScreenLocateTex);
  496. //ScreenLocate.DebugTexture(4, convolutionLighted2);
  497. }
  498. return true;
  499. }
  500. /*
  501. var avg = new Geometry4D.Vector<float>();
  502. var pixel = texture.GetPixels();
  503. foreach(var i in pixel.Index())
  504. {
  505. var iP = pixel[i];
  506. avg += new Geometry4D.Vector<float>(iP.r, iP.g, iP.b, iP.a);
  507. }
  508. avg /= pixel.Count();
  509. /*
  510. var (texLightedR, texLightedG, texLightedB) = ToRGB(newTex);
  511. ScreenLocate.DebugTexture(3, texLightedR);
  512. ScreenLocate.DebugTexture(4, texLightedG);
  513. ScreenLocate.DebugTexture(5, texLightedB);
  514. //Debug.Log(avg);
  515. return false;
  516. /**/
  517. #endregion
  518. }
  519. float GetBrightness()
  520. {
  521. UnityEngine.Color[] differPixel = new UnityEngine.Color[Size.x * Size.y];
  522. Parallel.For(0, Size.x * Size.y, i =>
  523. {
  524. var pi = ScreenWhiteTexture[i] - ScreenBlackTexture[i];
  525. differPixel[i] = new UnityEngine.Color(pi.x, pi.y, pi.z);
  526. });
  527. var sum = 0f;
  528. foreach (var i in differPixel)
  529. {
  530. sum += i.Brightness();
  531. }
  532. sum /= differPixel.Length;
  533. //Debug.Log(sum);
  534. return sum;
  535. }
  536. void QuadrilateralFit(out Texture2D LocateLightedRedTex,out Texture2D ChoosableLineTex, out Texture2D ScreenQuadTex, float lineWidth = 10, Texture2D debugImage = null)
  537. {
  538. UnityEngine.Color[] differPixel = new UnityEngine.Color[Size.x * Size.y];
  539. //读取数据
  540. if (debugImage != null)
  541. {
  542. Debug.Log($"Debug {debugImage.name}");
  543. differPixel = debugImage.GetPixels();
  544. }
  545. else // 获得屏幕差值
  546. {
  547. Parallel.For(0, Size.x * Size.y, i =>
  548. {
  549. var pi = ScreenWhiteTexture[i] - ScreenBlackTexture[i];
  550. differPixel[i] = new UnityEngine.Color(pi.x, pi.y, pi.z);
  551. });
  552. }
  553. var ScreenLocateTex = new Texture2D(Size.x, Size.y);
  554. ScreenLocateTex.SetPixels(differPixel);
  555. ScreenLocateTex.Apply();
  556. //ScreenLocate.DebugTexture(2, ScreenLocateTex);
  557. var ScreenLocateTexLighted = ScreenLocateTex.AutoLight(10);
  558. //ScreenLocate.DebugTexture(2, ScreenLocateTexLighted);
  559. var ScreenLocateTexR = ScreenLocateTexLighted.ToRGB(ColorChannel.Red);
  560. var ScreenLocateTexG = ScreenLocateTexLighted.ToRGB(ColorChannel.Green);
  561. var ScreenLocateTexB = ScreenLocateTexLighted.ToRGB(ColorChannel.Blue);
  562. LocateLightedRedTex = ScreenLocateTexR;
  563. //ScreenLocate.DebugTexture(2, ScreenLocateTexR);
  564. //ScreenLocate.DebugTexture(4, ScreenLocateTexG);
  565. //ScreenLocate.DebugTexture(5, ScreenLocateTexB);
  566. //var watch = new System.Diagnostics.Stopwatch();
  567. //watch.Start();
  568. //var times = new List<double>() { 0.0 };
  569. var ScreenLocateTexLightedMat = ScreenLocateTexLighted.Too0Mat();
  570. //var ScreenLocateTexLightedMat = texture.Too0Mat();
  571. //var (edge, edgeDir) = ScreenLocateTexLightedMat.IdentifyEdge();
  572. int conSize = (int)Math.Ceiling(0.007f * Size.y) * 2 + 1;
  573. float minLength = conSize * 1.2f;
  574. conSize = Math.Max(conSize, 7); // 设置最小为7
  575. minLength = Math.Max(conSize, 10); // 设置最小为10
  576. minLength = locateIndex == -1 ? minLength : minLength * areaPercent; // minLength需要按比例缩小
  577. Debug.Log($"[ScreenIdentification] Size: ({Size.x},{Size.y}), 卷积核Size: {conSize}, 最小线段长度: {minLength}");
  578. var (edge, edgeDir) = ScreenLocateTexLightedMat.zimIdentifyEdgeGradientAny(conSize);
  579. var quadLines = ScreenLocateTexLightedMat.ZIMIdentifyQuadLSD(edge, edgeDir, out Line[] oldLines, out List<Line> lightLines, Screen, conSize, conSize, minLength);
  580. // 将识别到的边画出来,并判断能否拼成屏幕,能拼成则设置ScreenMap
  581. // 线段顺序: 下、右、上、左
  582. List<Line> LineIdentified = new List<Line>();
  583. for (int i = 0; i < 4; i++)
  584. {
  585. if (quadLines[i] != null)
  586. LineIdentified.Add(quadLines[i]);
  587. else if (oldLines != null)
  588. LineIdentified.Add(oldLines[i]);
  589. }
  590. var drawScreenMap = new Matrix(ScreenLocateTexLightedMat.Size, Tiling: true);
  591. foreach (var l in LineIdentified)
  592. o0Extension.DrawLine(drawScreenMap, l, (x, y) => 1, new Geometry2D.Float.Vector(0, lineWidth));
  593. ScreenQuadTex = drawScreenMap.ToTex(); // out ScreenQuadTex
  594. QuadrilateralInCamera screenQuad = null;
  595. if (LineIdentified.Count == 4)
  596. {
  597. var a = LineIdentified[0].Intersect(LineIdentified[3], false).Value;
  598. var b = LineIdentified[0].Intersect(LineIdentified[1], false).Value;
  599. var c = LineIdentified[2].Intersect(LineIdentified[3], false).Value;
  600. var d = LineIdentified[1].Intersect(LineIdentified[2], false).Value;
  601. screenQuad = new QuadrilateralInCamera(a, b, c, d, new Vector(Size.x, Size.y));
  602. if (!screenQuad.IsQuadComplete())
  603. screenQuad = null;
  604. }
  605. if (screenQuad == null && Screen.QuadInCamera != null) // 如果可能,回退到上一个screen
  606. {
  607. Debug.Log("<color=aqua>[ScreenIdentification] 本次识别失败,回退到上次的识别结果</color>");
  608. quadTemp.Add(Screen.QuadInCamera.Quad);
  609. }
  610. else if (screenQuad != null)
  611. {
  612. Debug.Log("<color=aqua>[ScreenIdentification] 识别到四边形</color>");
  613. quadTemp.Add(screenQuad.Quad);
  614. }
  615. // 还需要输出一张识别结果图,包含干扰线段
  616. var LSDLineMap = new Matrix(ScreenLocateTexLightedMat.Size, Tiling: true);
  617. foreach (var l in lightLines)
  618. {
  619. if (l != null && !quadLines.Contains(l))
  620. o0Extension.DrawLine(LSDLineMap, l, (x, y) => 3, new Geometry2D.Float.Vector(0, 2), true); // 其他的备选线段
  621. }
  622. foreach (var l in quadLines)
  623. {
  624. if (l != null)
  625. o0Extension.DrawLine(LSDLineMap, l, (x, y) => 2, new Geometry2D.Float.Vector(0, 4)); // 这次识别到的线段
  626. }
  627. if (oldLines != null)
  628. {
  629. foreach (var l in oldLines)
  630. o0Extension.DrawLine(LSDLineMap, l, (x, y) => 1, new Geometry2D.Float.Vector(0, 2), true); // 旧的屏幕线段(例如上次手动识别的)
  631. }
  632. ChoosableLineTex = LSDLineMap.ToTexRGBA(FloatValueToColor);
  633. // 是否将图片保存到本地
  634. if (ScreenLocate.Main.SaveToggle.isOn)
  635. {
  636. var time = DateTime.Now.ToString("yyyyMMdd_HHmmss");
  637. var FileSavePath = $"{time}屏幕定位数据.bin";
  638. FileSavePath.FileWriteByte(ScreenWhiteTexture);
  639. var bytes = ScreenLocateTexLighted.EncodeToPNG();
  640. File.WriteAllBytes($"{time}屏幕.png", bytes);
  641. bytes = ScreenQuadTex.EncodeToPNG();
  642. File.WriteAllBytes($"{time}屏幕边框识别.png", bytes);
  643. Debug.Log("ScreenLocateTexture Saved To: " + FileSavePath);
  644. }
  645. //times.Add(watch.ElapsedMilliseconds);
  646. //UnityEngine.Debug.Log("time: " + (times[times.Count - 1] - times[times.Count - 2]));
  647. // opecncv处理, zim
  648. {
  649. //var cvLines = edge.cvHoughLinesP();
  650. //ScreenLocate.DebugTexture(5, cvLines);
  651. //var myLines = Hough.Transform(edgeMat);
  652. //var cvLines = edge.cvLine(myLines);
  653. //ScreenLocate.DebugTexture(5, cvLines);
  654. }
  655. UnityEngine.Object.Destroy(ScreenLocateTex);
  656. }
  657. }
  658. }