NRLocalizer.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. /*===============================================================================
  2. Copyright (C) 2020 Immersal Ltd. All Rights Reserved.
  3. This file is part of the Immersal SDK.
  4. The Immersal SDK cannot be copied, distributed, or made available to
  5. third-parties for commercial purposes without written permission of Immersal Ltd.
  6. Contact sdk@immersal.com for licensing requests.
  7. ===============================================================================*/
  8. using UnityEngine;
  9. using System;
  10. using System.Runtime.InteropServices;
  11. using System.Threading.Tasks;
  12. using NRKernal;
  13. using Immersal.REST;
  14. using Unity.Collections.LowLevel.Unsafe;
  15. using UnityEngine.UI;
  16. namespace Immersal.AR.Nreal
  17. {
  18. public class NRLocalizer : LocalizerBase
  19. {
  20. [Tooltip("Disable if you want to use RGB video capture while localizing. Also disable Multithreaded Rendering.")]
  21. [SerializeField]
  22. private bool m_UseYUV;
  23. private static NRLocalizer instance = null;
  24. private CameraModelView CamTexture { get; set; }
  25. public static NRLocalizer Instance
  26. {
  27. get
  28. {
  29. #if UNITY_EDITOR
  30. if (instance == null && !Application.isPlaying)
  31. {
  32. instance = UnityEngine.Object.FindObjectOfType<NRLocalizer>();
  33. }
  34. #endif
  35. if (instance == null)
  36. {
  37. Debug.LogError("No NRLocalizer instance found. Ensure one exists in the scene.");
  38. }
  39. return instance;
  40. }
  41. }
  42. void Awake()
  43. {
  44. if (instance == null)
  45. {
  46. instance = this;
  47. }
  48. if (instance != this)
  49. {
  50. Debug.LogError("There must be only one NRLocalizer object in a scene.");
  51. UnityEngine.Object.DestroyImmediate(this);
  52. return;
  53. }
  54. }
  55. public override void Start()
  56. {
  57. base.Start();
  58. m_Sdk.RegisterLocalizer(instance);
  59. if (m_UseYUV)
  60. {
  61. CamTexture = new NRRGBCamTextureYUV();
  62. (CamTexture as NRRGBCamTextureYUV).OnUpdate += OnYUVCaptureUpdate;
  63. }
  64. else
  65. {
  66. CamTexture = new NRRGBCamTexture();
  67. (CamTexture as NRRGBCamTexture).OnUpdate += OnRGBCaptureUpdate;
  68. }
  69. if (autoStart)
  70. {
  71. CamTexture.Play();
  72. }
  73. }
  74. public override void StartLocalizing()
  75. {
  76. CamTexture.Play();
  77. base.StartLocalizing();
  78. }
  79. public override void StopLocalizing()
  80. {
  81. base.StopLocalizing();
  82. CamTexture.Stop();
  83. }
  84. public override void Pause()
  85. {
  86. base.Pause();
  87. CamTexture.Pause();
  88. }
  89. protected override void Update()
  90. {
  91. isTracking = NRFrame.SessionStatus == SessionState.Running;
  92. base.Update();
  93. }
  94. public override void OnDestroy()
  95. {
  96. CamTexture.Stop();
  97. if (m_UseYUV)
  98. {
  99. (CamTexture as NRRGBCamTextureYUV).OnUpdate -= OnYUVCaptureUpdate;
  100. }
  101. else
  102. {
  103. (CamTexture as NRRGBCamTexture).OnUpdate -= OnRGBCaptureUpdate;
  104. }
  105. base.OnDestroy();
  106. }
  107. public override async void Localize()
  108. {
  109. while (!CamTexture.DidUpdateThisFrame)
  110. {
  111. await Task.Yield();
  112. }
  113. if (m_PixelBuffer != IntPtr.Zero)
  114. {
  115. stats.localizationAttemptCount++;
  116. Vector4 intrinsics;
  117. Pose headPose = NRFrame.HeadPose;
  118. Pose rgbCameraFromEyePose = NRFrame.EyePoseFromHead.RGBEyePose;
  119. Vector3 camPos = headPose.position + rgbCameraFromEyePose.position;
  120. Quaternion camRot = headPose.rotation * rgbCameraFromEyePose.rotation;
  121. int width = CamTexture.Width;
  122. int height = CamTexture.Height;
  123. Vector3 pos = Vector3.zero;
  124. Quaternion rot = Quaternion.identity;
  125. GetIntrinsics(out intrinsics);
  126. float startTime = Time.realtimeSinceStartup;
  127. Task<int> t = Task.Run(() =>
  128. {
  129. return Immersal.Core.LocalizeImage(out pos, out rot, width, height, ref intrinsics, m_PixelBuffer);
  130. });
  131. await t;
  132. int mapHandle = t.Result;
  133. int mapId = ARMap.MapHandleToId(mapHandle);
  134. float elapsedTime = Time.realtimeSinceStartup - startTime;
  135. if (mapId > 0 && ARSpace.mapIdToMap.ContainsKey(mapId))
  136. {
  137. Debug.Log(string.Format("Relocalized in {0} seconds", elapsedTime));
  138. stats.localizationSuccessCount++;
  139. ARMap map = ARSpace.mapIdToMap[mapId];
  140. if (mapId != lastLocalizedMapId)
  141. {
  142. if (resetOnMapChange)
  143. {
  144. Reset();
  145. }
  146. lastLocalizedMapId = mapId;
  147. OnMapChanged?.Invoke(mapId);
  148. }
  149. rot *= Quaternion.Euler(0f, 0f, -90.0f);
  150. MapOffset mo = ARSpace.mapIdToOffset[mapId];
  151. Matrix4x4 offsetNoScale = Matrix4x4.TRS(mo.position, mo.rotation, Vector3.one);
  152. Vector3 scaledPos = Vector3.Scale(pos, mo.scale);
  153. Matrix4x4 cloudSpace = offsetNoScale * Matrix4x4.TRS(scaledPos, rot, Vector3.one);
  154. Matrix4x4 trackerSpace = Matrix4x4.TRS(camPos, camRot, Vector3.one);
  155. Matrix4x4 m = trackerSpace * (cloudSpace.inverse);
  156. if (useFiltering)
  157. mo.space.filter.RefinePose(m);
  158. else
  159. ARSpace.UpdateSpace(mo.space, m.GetColumn(3), m.rotation);
  160. Vector3 p = m.GetColumn(3);
  161. Vector3 euler = m.rotation.eulerAngles;
  162. GetLocalizerPose(out lastLocalizedPose, mapId, pos, rot, m.inverse);
  163. map.NotifySuccessfulLocalization(mapId);
  164. OnPoseFound?.Invoke(lastLocalizedPose);
  165. }
  166. else
  167. {
  168. Debug.Log(string.Format("Localization attempt failed after {0} seconds", elapsedTime));
  169. }
  170. }
  171. else
  172. {
  173. Debug.LogError("No camera pixel buffer");
  174. }
  175. base.Localize();
  176. }
  177. public override async void LocalizeServer(SDKMapId[] mapIds)
  178. {
  179. while (!CamTexture.DidUpdateThisFrame)
  180. {
  181. await Task.Yield();
  182. }
  183. if (m_PixelBuffer != IntPtr.Zero)
  184. {
  185. stats.localizationAttemptCount++;
  186. JobLocalizeServerAsync j = new JobLocalizeServerAsync();
  187. Vector4 intrinsics;
  188. Pose headPose = NRFrame.HeadPose;
  189. Pose rgbCameraFromEyePose = NRFrame.EyePoseFromHead.RGBEyePose;
  190. Vector3 camPos = headPose.position + rgbCameraFromEyePose.position;
  191. Quaternion camRot = headPose.rotation * rgbCameraFromEyePose.rotation;
  192. int channels = 1;
  193. int width = CamTexture.Width;
  194. int height = CamTexture.Height;
  195. byte[] pixels = m_UseYUV ? (CamTexture as NRRGBCamTextureYUV).GetTexture().YBuf : GetYBufFromTexture((CamTexture as NRRGBCamTexture).GetTexture());
  196. GetIntrinsics(out intrinsics);
  197. float startTime = Time.realtimeSinceStartup;
  198. Task<(byte[], icvCaptureInfo)> t = Task.Run(() =>
  199. {
  200. byte[] capture = new byte[channels * width * height + 8192];
  201. icvCaptureInfo info = Immersal.Core.CaptureImage(capture, capture.Length, pixels, width, height, channels);
  202. Array.Resize(ref capture, info.captureSize);
  203. return (capture, info);
  204. });
  205. await t;
  206. j.image = t.Result.Item1;
  207. j.intrinsics = intrinsics;
  208. j.mapIds = mapIds;
  209. j.OnResult += (SDKLocalizeResult result) =>
  210. {
  211. float elapsedTime = Time.realtimeSinceStartup - startTime;
  212. if (result.success)
  213. {
  214. Debug.Log("*************************** On-Server Localization Succeeded ***************************");
  215. Debug.Log(string.Format("Relocalized in {0} seconds", elapsedTime));
  216. int mapId = result.map;
  217. if (mapId > 0 && ARSpace.mapIdToMap.ContainsKey(mapId))
  218. {
  219. ARMap map = ARSpace.mapIdToMap[mapId];
  220. if (mapId != lastLocalizedMapId)
  221. {
  222. if (resetOnMapChange)
  223. {
  224. Reset();
  225. }
  226. lastLocalizedMapId = mapId;
  227. OnMapChanged?.Invoke(mapId);
  228. }
  229. MapOffset mo = ARSpace.mapIdToOffset[mapId];
  230. stats.localizationSuccessCount++;
  231. Matrix4x4 responseMatrix = Matrix4x4.identity;
  232. responseMatrix.m00 = result.r00; responseMatrix.m01 = result.r01; responseMatrix.m02 = result.r02; responseMatrix.m03 = result.px;
  233. responseMatrix.m10 = result.r10; responseMatrix.m11 = result.r11; responseMatrix.m12 = result.r12; responseMatrix.m13 = result.py;
  234. responseMatrix.m20 = result.r20; responseMatrix.m21 = result.r21; responseMatrix.m22 = result.r22; responseMatrix.m23 = result.pz;
  235. Vector3 pos = responseMatrix.GetColumn(3);
  236. Quaternion rot = responseMatrix.rotation;
  237. rot *= Quaternion.Euler(0f, 0f, -90f);
  238. Matrix4x4 offsetNoScale = Matrix4x4.TRS(mo.position, mo.rotation, Vector3.one);
  239. Vector3 scaledPos = Vector3.Scale(pos, mo.scale);
  240. Matrix4x4 cloudSpace = offsetNoScale * Matrix4x4.TRS(scaledPos, rot, Vector3.one);
  241. Matrix4x4 trackerSpace = Matrix4x4.TRS(camPos, camRot, Vector3.one);
  242. Matrix4x4 m = trackerSpace * (cloudSpace.inverse);
  243. if (useFiltering)
  244. mo.space.filter.RefinePose(m);
  245. else
  246. ARSpace.UpdateSpace(mo.space, m.GetColumn(3), m.rotation);
  247. double[] ecef = map.MapToEcefGet();
  248. LocalizerBase.GetLocalizerPose(out lastLocalizedPose, mapId, pos, rot, m.inverse, ecef);
  249. map.NotifySuccessfulLocalization(mapId);
  250. OnPoseFound?.Invoke(lastLocalizedPose);
  251. }
  252. }
  253. else
  254. {
  255. Debug.Log("*************************** On-Server Localization Failed ***************************");
  256. Debug.Log(string.Format("Localization attempt failed after {0} seconds", elapsedTime));
  257. }
  258. };
  259. await j.RunJobAsync();
  260. }
  261. else
  262. {
  263. Debug.LogError("No camera pixel buffer");
  264. }
  265. base.LocalizeServer(mapIds);
  266. }
  267. public override async void LocalizeGeoPose(SDKMapId[] mapIds)
  268. {
  269. while (!CamTexture.DidUpdateThisFrame)
  270. {
  271. await Task.Yield();
  272. }
  273. if (m_PixelBuffer != IntPtr.Zero)
  274. {
  275. stats.localizationAttemptCount++;
  276. JobGeoPoseAsync j = new JobGeoPoseAsync();
  277. Vector4 intrinsics;
  278. Pose headPose = NRFrame.HeadPose;
  279. Pose rgbCameraFromEyePose = NRFrame.EyePoseFromHead.RGBEyePose;
  280. Vector3 camPos = headPose.position + rgbCameraFromEyePose.position;
  281. Quaternion camRot = headPose.rotation * rgbCameraFromEyePose.rotation;
  282. int channels = 1;
  283. int width = CamTexture.Width;
  284. int height = CamTexture.Height;
  285. byte[] pixels = m_UseYUV ? (CamTexture as NRRGBCamTextureYUV).GetTexture().YBuf : GetYBufFromTexture((CamTexture as NRRGBCamTexture).GetTexture());
  286. GetIntrinsics(out intrinsics);
  287. float startTime = Time.realtimeSinceStartup;
  288. Task<(byte[], icvCaptureInfo)> t = Task.Run(() =>
  289. {
  290. byte[] capture = new byte[channels * width * height + 8192];
  291. icvCaptureInfo info = Immersal.Core.CaptureImage(capture, capture.Length, pixels, width, height, channels);
  292. Array.Resize(ref capture, info.captureSize);
  293. return (capture, info);
  294. });
  295. await t;
  296. j.image = t.Result.Item1;
  297. j.intrinsics = intrinsics;
  298. j.mapIds = mapIds;
  299. j.OnResult += (SDKGeoPoseResult result) =>
  300. {
  301. float elapsedTime = Time.realtimeSinceStartup - startTime;
  302. if (result.success)
  303. {
  304. Debug.Log("*************************** GeoPose Localization Succeeded ***************************");
  305. Debug.Log(string.Format("Relocalized in {0} seconds", elapsedTime));
  306. int mapId = result.map;
  307. double latitude = result.latitude;
  308. double longitude = result.longitude;
  309. double ellipsoidHeight = result.ellipsoidHeight;
  310. Quaternion rot = new Quaternion(result.quaternion[1], result.quaternion[2], result.quaternion[3], result.quaternion[0]);
  311. Debug.Log(string.Format("GeoPose returned latitude: {0}, longitude: {1}, ellipsoidHeight: {2}, quaternion: {3}", latitude, longitude, ellipsoidHeight, rot));
  312. double[] ecef = new double[3];
  313. double[] wgs84 = new double[3] { latitude, longitude, ellipsoidHeight };
  314. Core.PosWgs84ToEcef(ecef, wgs84);
  315. Debug.Log("*************************** GeoPose Localization PosWgs84ToEcef ***************************");
  316. if (ARSpace.mapIdToMap.ContainsKey(mapId) && rot != Quaternion.identity&& rot.eulerAngles!=Vector3.zero)
  317. {
  318. ARMap map = ARSpace.mapIdToMap[mapId];
  319. if (mapId != lastLocalizedMapId)
  320. {
  321. if (resetOnMapChange)
  322. {
  323. Reset();
  324. }
  325. lastLocalizedMapId = mapId;
  326. OnMapChanged?.Invoke(mapId);
  327. }
  328. MapOffset mo = ARSpace.mapIdToOffset[mapId];
  329. stats.localizationSuccessCount++;
  330. double[] mapToEcef = map.MapToEcefGet();
  331. Vector3 mapPos;
  332. Quaternion mapRot;
  333. Debug.Log("*************************** GeoPose Localization PosEcefToMap ***************************");
  334. Core.PosEcefToMap(out mapPos, ecef, mapToEcef);
  335. Debug.Log("*************************** GeoPose Localization RotEcefToMap ***************************");
  336. Core.RotEcefToMap(out mapRot, rot, mapToEcef);
  337. Matrix4x4 offsetNoScale = Matrix4x4.TRS(mo.position, mo.rotation, Vector3.one);
  338. Vector3 scaledPos = Vector3.Scale(mapPos, mo.scale);
  339. Matrix4x4 cloudSpace = offsetNoScale * Matrix4x4.TRS(scaledPos, mapRot, Vector3.one);
  340. Matrix4x4 trackerSpace = Matrix4x4.TRS(camPos, camRot, Vector3.one);
  341. Matrix4x4 m = trackerSpace*(cloudSpace.inverse);
  342. if (useFiltering)
  343. mo.space.filter.RefinePose(m);
  344. else
  345. ARSpace.UpdateSpace(mo.space, m.GetColumn(3), m.rotation);
  346. LocalizerBase.GetLocalizerPose(out lastLocalizedPose, mapId, cloudSpace.GetColumn(3), cloudSpace.rotation, m.inverse, mapToEcef);
  347. map.NotifySuccessfulLocalization(mapId);
  348. OnPoseFound?.Invoke(lastLocalizedPose);
  349. }
  350. }
  351. else
  352. {
  353. Debug.Log("*************************** GeoPose Localization Failed ***************************");
  354. Debug.Log(string.Format("GeoPose localization attempt failed after {0} seconds", elapsedTime));
  355. }
  356. };
  357. await j.RunJobAsync();
  358. }
  359. else
  360. {
  361. Debug.LogError("No camera pixel buffer");
  362. }
  363. base.LocalizeGeoPose(mapIds);
  364. }
  365. public RawImage rawimg;
  366. private void OnRGBCaptureUpdate(CameraTextureFrame frame)
  367. {
  368. rawimg.texture = (Texture2D)frame.texture;
  369. byte[] pixels = GetYBufFromTexture((Texture2D)frame.texture);
  370. unsafe
  371. {
  372. ulong handle;
  373. byte* ptr = (byte*)UnsafeUtility.PinGCArrayAndGetDataAddress(pixels, out handle);
  374. m_PixelBuffer = (IntPtr)ptr;
  375. UnsafeUtility.ReleaseGCObject(handle);
  376. }
  377. }
  378. private void OnYUVCaptureUpdate(NRRGBCamTextureYUV.YUVTextureFrame frame)
  379. {
  380. if (m_PixelBuffer == IntPtr.Zero)
  381. {
  382. unsafe
  383. {
  384. ulong handle;
  385. byte* ptr = (byte*)UnsafeUtility.PinGCArrayAndGetDataAddress(frame.YBuf, out handle);
  386. m_PixelBuffer = (IntPtr)ptr;
  387. UnsafeUtility.ReleaseGCObject(handle);
  388. }
  389. }
  390. }
  391. private byte[] GetYBufFromTexture(Texture2D tex)
  392. {
  393. Color32[] rgbArray = tex.GetPixels32();
  394. byte[] pixels = new byte[rgbArray.Length];
  395. for (int i = 0; i < rgbArray.Length; i++)
  396. {
  397. pixels[i] = rgbArray[i].g;
  398. }
  399. return pixels;
  400. }
  401. private void GetIntrinsics(out Vector4 intrinsics)
  402. {
  403. intrinsics = Vector4.zero;
  404. NativeMat3f m = NRFrame.GetRGBCameraIntrinsicMatrix();
  405. intrinsics.x = m.column0.X;
  406. intrinsics.y = m.column1.Y;
  407. intrinsics.z = m.column2.X;
  408. intrinsics.w = m.column2.Y;
  409. }
  410. }
  411. }