ArUcoWebCamTextureExample.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824
  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. using UnityEngine.SceneManagement;
  4. using System.Xml.Serialization;
  5. using System.IO;
  6. using System.Collections;
  7. using System.Collections.Generic;
  8. using OpenCVForUnity.CoreModule;
  9. using OpenCVForUnity.ArucoModule;
  10. using OpenCVForUnity.Calib3dModule;
  11. using OpenCVForUnity.ImgprocModule;
  12. using OpenCVForUnity.UnityUtils;
  13. using OpenCVForUnity.UnityUtils.Helper;
  14. namespace OpenCVForUnityExample
  15. {
  16. /// <summary>
  17. /// ArUco WebCamTexture Example
  18. /// An example of marker-based AR view and camera pose estimation using the aruco (ArUco Marker Detection) module.
  19. /// Referring to https://github.com/opencv/opencv_contrib/blob/master/modules/aruco/samples/detect_markers.cpp.
  20. /// http://docs.opencv.org/3.1.0/d5/dae/tutorial_aruco_detection.html
  21. /// </summary>
  22. [RequireComponent (typeof(WebCamTextureToMatHelper))]
  23. public class ArUcoWebCamTextureExample : MonoBehaviour
  24. {
  25. /// <summary>
  26. /// Determines if restores the camera parameters when the file exists.
  27. /// </summary>
  28. public bool useStoredCameraParameters = true;
  29. /// <summary>
  30. /// The marker type.
  31. /// </summary>
  32. public MarkerType markerType = MarkerType.CanonicalMarker;
  33. /// <summary>
  34. /// The marker type dropdown.
  35. /// </summary>
  36. public Dropdown markerTypeDropdown;
  37. /// <summary>
  38. /// The dictionary identifier.
  39. /// </summary>
  40. public ArUcoDictionary dictionaryId = ArUcoDictionary.DICT_6X6_250;
  41. /// <summary>
  42. /// The dictionary id dropdown.
  43. /// </summary>
  44. public Dropdown dictionaryIdDropdown;
  45. /// <summary>
  46. /// Determines if shows rejected corners.
  47. /// </summary>
  48. public bool showRejectedCorners = false;
  49. /// <summary>
  50. /// The shows rejected corners toggle.
  51. /// </summary>
  52. public Toggle showRejectedCornersToggle;
  53. /// <summary>
  54. /// Determines if applied the pose estimation.
  55. /// </summary>
  56. public bool applyEstimationPose = true;
  57. /// <summary>
  58. /// Determines if refine marker detection. (only valid for ArUco boards)
  59. /// </summary>
  60. public bool refineMarkerDetection = true;
  61. /// <summary>
  62. /// The shows refine marker detection toggle.
  63. /// </summary>
  64. public Toggle refineMarkerDetectionToggle;
  65. [Space (10)]
  66. /// <summary>
  67. /// The length of the markers' side. Normally, unit is meters.
  68. /// </summary>
  69. public float markerLength = 0.1f;
  70. /// <summary>
  71. /// The AR game object.
  72. /// </summary>
  73. public GameObject arGameObject;
  74. /// <summary>
  75. /// The AR camera.
  76. /// </summary>
  77. public Camera arCamera;
  78. [Space (10)]
  79. /// <summary>
  80. /// Determines if request the AR camera moving.
  81. /// </summary>
  82. public bool shouldMoveARCamera = false;
  83. [Space (10)]
  84. /// <summary>
  85. /// Determines if enable low pass filter.
  86. /// </summary>
  87. public bool enableLowPassFilter;
  88. /// <summary>
  89. /// The enable low pass filter toggle.
  90. /// </summary>
  91. public Toggle enableLowPassFilterToggle;
  92. /// <summary>
  93. /// The position low pass. (Value in meters)
  94. /// </summary>
  95. public float positionLowPass = 0.005f;
  96. /// <summary>
  97. /// The rotation low pass. (Value in degrees)
  98. /// </summary>
  99. public float rotationLowPass = 2f;
  100. /// <summary>
  101. /// The old pose data.
  102. /// </summary>
  103. PoseData oldPoseData;
  104. /// <summary>
  105. /// The texture.
  106. /// </summary>
  107. Texture2D texture;
  108. /// <summary>
  109. /// The webcam texture to mat helper.
  110. /// </summary>
  111. WebCamTextureToMatHelper webCamTextureToMatHelper;
  112. /// <summary>
  113. /// The rgb mat.
  114. /// </summary>
  115. Mat rgbMat;
  116. /// <summary>
  117. /// The cameraparam matrix.
  118. /// </summary>
  119. Mat camMatrix;
  120. /// <summary>
  121. /// The distortion coeffs.
  122. /// </summary>
  123. MatOfDouble distCoeffs;
  124. /// <summary>
  125. /// The transformation matrix for AR.
  126. /// </summary>
  127. Matrix4x4 ARM;
  128. /// <summary>
  129. /// The identifiers.
  130. /// </summary>
  131. Mat ids;
  132. /// <summary>
  133. /// The corners.
  134. /// </summary>
  135. List<Mat> corners;
  136. /// <summary>
  137. /// The rejected corners.
  138. /// </summary>
  139. List<Mat> rejectedCorners;
  140. /// <summary>
  141. /// The rvecs.
  142. /// </summary>
  143. Mat rvecs;
  144. /// <summary>
  145. /// The tvecs.
  146. /// </summary>
  147. Mat tvecs;
  148. /// <summary>
  149. /// The rot mat.
  150. /// </summary>
  151. Mat rotMat;
  152. /// <summary>
  153. /// The detector parameters.
  154. /// </summary>
  155. DetectorParameters detectorParams;
  156. /// <summary>
  157. /// The dictionary.
  158. /// </summary>
  159. Dictionary dictionary;
  160. /// <summary>
  161. /// The FPS monitor.
  162. /// </summary>
  163. FpsMonitor fpsMonitor;
  164. Mat rvec;
  165. Mat tvec;
  166. Mat recoveredIdxs;
  167. // for GridBoard.
  168. // number of markers in X direction
  169. const int gridBoradMarkersX = 5;
  170. // number of markers in Y direction
  171. const int gridBoradMarkersY = 7;
  172. // marker side length (normally in meters)
  173. const float gridBoradMarkerLength = 0.04f;
  174. // separation between two markers (same unit as markerLength)
  175. const float gridBoradMarkerSeparation = 0.01f;
  176. // id of first marker in dictionary to use on board.
  177. const int gridBoradMarkerFirstMarker = 0;
  178. GridBoard gridBoard;
  179. // for ChArUcoBoard.
  180. // number of chessboard squares in X direction
  181. const int chArUcoBoradSquaresX = 5;
  182. // number of chessboard squares in Y direction
  183. const int chArUcoBoradSquaresY = 7;
  184. // chessboard square side length (normally in meters)
  185. const float chArUcoBoradSquareLength = 0.04f;
  186. // marker side length (same unit than squareLength)
  187. const float chArUcoBoradMarkerLength = 0.02f;
  188. const int charucoMinMarkers = 2;
  189. Mat charucoCorners;
  190. Mat charucoIds;
  191. CharucoBoard charucoBoard;
  192. // for ChArUcoDiamondMarker.
  193. // size of the chessboard squares in pixels
  194. const float diamondSquareLength = 0.1f;
  195. // size of the markers in pixels.
  196. const float diamondMarkerLength = 0.06f;
  197. // identifiers for diamonds in diamond corners.
  198. const int diamondId1 = 45;
  199. const int diamondId2 = 68;
  200. const int diamondId3 = 28;
  201. const int diamondId4 = 74;
  202. List<Mat> diamondCorners;
  203. Mat diamondIds;
  204. // Use this for initialization
  205. void Start ()
  206. {
  207. fpsMonitor = GetComponent<FpsMonitor> ();
  208. markerTypeDropdown.value = (int)markerType;
  209. dictionaryIdDropdown.value = (int)dictionaryId;
  210. showRejectedCornersToggle.isOn = showRejectedCorners;
  211. refineMarkerDetectionToggle.isOn = refineMarkerDetection;
  212. refineMarkerDetectionToggle.interactable = (markerType == MarkerType.GridBoard || markerType == MarkerType.ChArUcoBoard);
  213. enableLowPassFilterToggle.isOn = enableLowPassFilter;
  214. webCamTextureToMatHelper = gameObject.GetComponent<WebCamTextureToMatHelper> ();
  215. #if UNITY_ANDROID && !UNITY_EDITOR
  216. // Avoids the front camera low light issue that occurs in only some Android devices (e.g. Google Pixel, Pixel2).
  217. webCamTextureToMatHelper.avoidAndroidFrontCameraLowLightIssue = true;
  218. #endif
  219. webCamTextureToMatHelper.Initialize ();
  220. }
  221. /// <summary>
  222. /// Raises the webcam texture to mat helper initialized event.
  223. /// </summary>
  224. public void OnWebCamTextureToMatHelperInitialized ()
  225. {
  226. Debug.Log ("OnWebCamTextureToMatHelperInitialized");
  227. Mat webCamTextureMat = webCamTextureToMatHelper.GetMat ();
  228. texture = new Texture2D (webCamTextureMat.cols (), webCamTextureMat.rows (), TextureFormat.RGB24, false);
  229. gameObject.GetComponent<Renderer> ().material.mainTexture = texture;
  230. gameObject.transform.localScale = new Vector3 (webCamTextureMat.cols (), webCamTextureMat.rows (), 1);
  231. Debug.Log ("Screen.width " + Screen.width + " Screen.height " + Screen.height + " Screen.orientation " + Screen.orientation);
  232. if (fpsMonitor != null) {
  233. fpsMonitor.Add ("width", webCamTextureMat.width ().ToString ());
  234. fpsMonitor.Add ("height", webCamTextureMat.height ().ToString ());
  235. fpsMonitor.Add ("orientation", Screen.orientation.ToString ());
  236. }
  237. float width = webCamTextureMat.width ();
  238. float height = webCamTextureMat.height ();
  239. float imageSizeScale = 1.0f;
  240. float widthScale = (float)Screen.width / width;
  241. float heightScale = (float)Screen.height / height;
  242. if (widthScale < heightScale) {
  243. Camera.main.orthographicSize = (width * (float)Screen.height / (float)Screen.width) / 2;
  244. imageSizeScale = (float)Screen.height / (float)Screen.width;
  245. } else {
  246. Camera.main.orthographicSize = height / 2;
  247. }
  248. // set camera parameters.
  249. double fx;
  250. double fy;
  251. double cx;
  252. double cy;
  253. string loadDirectoryPath = Path.Combine (Application.persistentDataPath, "ArUcoCameraCalibrationExample");
  254. string calibratonDirectoryName = "camera_parameters" + width + "x" + height;
  255. string loadCalibratonFileDirectoryPath = Path.Combine (loadDirectoryPath, calibratonDirectoryName);
  256. string loadPath = Path.Combine (loadCalibratonFileDirectoryPath, calibratonDirectoryName + ".xml");
  257. if (useStoredCameraParameters && File.Exists (loadPath)) {
  258. CameraParameters param;
  259. XmlSerializer serializer = new XmlSerializer (typeof(CameraParameters));
  260. using (var stream = new FileStream (loadPath, FileMode.Open)) {
  261. param = (CameraParameters)serializer.Deserialize (stream);
  262. }
  263. camMatrix = param.GetCameraMatrix ();
  264. distCoeffs = new MatOfDouble (param.GetDistortionCoefficients ());
  265. fx = param.camera_matrix [0];
  266. fy = param.camera_matrix [4];
  267. cx = param.camera_matrix [2];
  268. cy = param.camera_matrix [5];
  269. Debug.Log ("Loaded CameraParameters from a stored XML file.");
  270. Debug.Log ("loadPath: " + loadPath);
  271. } else {
  272. int max_d = (int)Mathf.Max (width, height);
  273. fx = max_d;
  274. fy = max_d;
  275. cx = width / 2.0f;
  276. cy = height / 2.0f;
  277. camMatrix = new Mat (3, 3, CvType.CV_64FC1);
  278. camMatrix.put (0, 0, fx);
  279. camMatrix.put (0, 1, 0);
  280. camMatrix.put (0, 2, cx);
  281. camMatrix.put (1, 0, 0);
  282. camMatrix.put (1, 1, fy);
  283. camMatrix.put (1, 2, cy);
  284. camMatrix.put (2, 0, 0);
  285. camMatrix.put (2, 1, 0);
  286. camMatrix.put (2, 2, 1.0f);
  287. distCoeffs = new MatOfDouble (0, 0, 0, 0);
  288. Debug.Log ("Created a dummy CameraParameters.");
  289. }
  290. Debug.Log ("camMatrix " + camMatrix.dump ());
  291. Debug.Log ("distCoeffs " + distCoeffs.dump ());
  292. // calibration camera matrix values.
  293. Size imageSize = new Size (width * imageSizeScale, height * imageSizeScale);
  294. double apertureWidth = 0;
  295. double apertureHeight = 0;
  296. double[] fovx = new double[1];
  297. double[] fovy = new double[1];
  298. double[] focalLength = new double[1];
  299. Point principalPoint = new Point (0, 0);
  300. double[] aspectratio = new double[1];
  301. Calib3d.calibrationMatrixValues (camMatrix, imageSize, apertureWidth, apertureHeight, fovx, fovy, focalLength, principalPoint, aspectratio);
  302. Debug.Log ("imageSize " + imageSize.ToString ());
  303. Debug.Log ("apertureWidth " + apertureWidth);
  304. Debug.Log ("apertureHeight " + apertureHeight);
  305. Debug.Log ("fovx " + fovx [0]);
  306. Debug.Log ("fovy " + fovy [0]);
  307. Debug.Log ("focalLength " + focalLength [0]);
  308. Debug.Log ("principalPoint " + principalPoint.ToString ());
  309. Debug.Log ("aspectratio " + aspectratio [0]);
  310. // To convert the difference of the FOV value of the OpenCV and Unity.
  311. double fovXScale = (2.0 * Mathf.Atan ((float)(imageSize.width / (2.0 * fx)))) / (Mathf.Atan2 ((float)cx, (float)fx) + Mathf.Atan2 ((float)(imageSize.width - cx), (float)fx));
  312. double fovYScale = (2.0 * Mathf.Atan ((float)(imageSize.height / (2.0 * fy)))) / (Mathf.Atan2 ((float)cy, (float)fy) + Mathf.Atan2 ((float)(imageSize.height - cy), (float)fy));
  313. Debug.Log ("fovXScale " + fovXScale);
  314. Debug.Log ("fovYScale " + fovYScale);
  315. // Adjust Unity Camera FOV https://github.com/opencv/opencv/commit/8ed1945ccd52501f5ab22bdec6aa1f91f1e2cfd4
  316. if (widthScale < heightScale) {
  317. arCamera.fieldOfView = (float)(fovx [0] * fovXScale);
  318. } else {
  319. arCamera.fieldOfView = (float)(fovy [0] * fovYScale);
  320. }
  321. // Display objects near the camera.
  322. arCamera.nearClipPlane = 0.01f;
  323. rgbMat = new Mat (webCamTextureMat.rows (), webCamTextureMat.cols (), CvType.CV_8UC3);
  324. ids = new Mat ();
  325. corners = new List<Mat> ();
  326. rejectedCorners = new List<Mat> ();
  327. rvecs = new Mat ();
  328. tvecs = new Mat ();
  329. rotMat = new Mat (3, 3, CvType.CV_64FC1);
  330. detectorParams = DetectorParameters.create ();
  331. dictionary = Aruco.getPredefinedDictionary ((int)dictionaryId);
  332. rvec = new Mat ();
  333. tvec = new Mat ();
  334. recoveredIdxs = new Mat ();
  335. gridBoard = GridBoard.create (gridBoradMarkersX, gridBoradMarkersY, gridBoradMarkerLength, gridBoradMarkerSeparation, dictionary, gridBoradMarkerFirstMarker);
  336. charucoCorners = new Mat ();
  337. charucoIds = new Mat ();
  338. charucoBoard = CharucoBoard.create (chArUcoBoradSquaresX, chArUcoBoradSquaresY, chArUcoBoradSquareLength, chArUcoBoradMarkerLength, dictionary);
  339. diamondCorners = new List<Mat> ();
  340. diamondIds = new Mat (1, 1, CvType.CV_32SC4);
  341. diamondIds.put (0, 0, new int[] { diamondId1, diamondId2, diamondId3, diamondId4 });
  342. // if WebCamera is frontFaceing, flip Mat.
  343. if (webCamTextureToMatHelper.GetWebCamDevice ().isFrontFacing) {
  344. webCamTextureToMatHelper.flipHorizontal = true;
  345. }
  346. }
  347. /// <summary>
  348. /// Raises the webcam texture to mat helper disposed event.
  349. /// </summary>
  350. public void OnWebCamTextureToMatHelperDisposed ()
  351. {
  352. Debug.Log ("OnWebCamTextureToMatHelperDisposed");
  353. if (rgbMat != null)
  354. rgbMat.Dispose ();
  355. if (texture != null) {
  356. Texture2D.Destroy (texture);
  357. texture = null;
  358. }
  359. if (ids != null)
  360. ids.Dispose ();
  361. foreach (var item in corners) {
  362. item.Dispose ();
  363. }
  364. corners.Clear ();
  365. foreach (var item in rejectedCorners) {
  366. item.Dispose ();
  367. }
  368. rejectedCorners.Clear ();
  369. if (rvecs != null)
  370. rvecs.Dispose ();
  371. if (tvecs != null)
  372. tvecs.Dispose ();
  373. if (rotMat != null)
  374. rotMat.Dispose ();
  375. if (rvec != null)
  376. rvec.Dispose ();
  377. if (tvec != null)
  378. tvec.Dispose ();
  379. if (recoveredIdxs != null)
  380. recoveredIdxs.Dispose ();
  381. if (gridBoard != null)
  382. gridBoard.Dispose ();
  383. if (charucoCorners != null)
  384. charucoCorners.Dispose ();
  385. if (charucoIds != null)
  386. charucoIds.Dispose ();
  387. if (charucoBoard != null)
  388. charucoBoard.Dispose ();
  389. foreach (var item in diamondCorners) {
  390. item.Dispose ();
  391. }
  392. diamondCorners.Clear ();
  393. if (diamondIds != null)
  394. diamondIds.Dispose ();
  395. }
  396. /// <summary>
  397. /// Raises the webcam texture to mat helper error occurred event.
  398. /// </summary>
  399. /// <param name="errorCode">Error code.</param>
  400. public void OnWebCamTextureToMatHelperErrorOccurred (WebCamTextureToMatHelper.ErrorCode errorCode)
  401. {
  402. Debug.Log ("OnWebCamTextureToMatHelperErrorOccurred " + errorCode);
  403. }
  404. // Update is called once per frame
  405. void Update ()
  406. {
  407. if (webCamTextureToMatHelper.IsPlaying () && webCamTextureToMatHelper.DidUpdateThisFrame ()) {
  408. Mat rgbaMat = webCamTextureToMatHelper.GetMat ();
  409. Imgproc.cvtColor (rgbaMat, rgbMat, Imgproc.COLOR_RGBA2RGB);
  410. // detect markers.
  411. Aruco.detectMarkers (rgbMat, dictionary, corners, ids, detectorParams, rejectedCorners, camMatrix, distCoeffs);
  412. // refine marker detection.
  413. if (refineMarkerDetection && (markerType == MarkerType.GridBoard || markerType == MarkerType.ChArUcoBoard)) {
  414. switch (markerType) {
  415. case MarkerType.GridBoard:
  416. Aruco.refineDetectedMarkers (rgbMat, gridBoard, corners, ids, rejectedCorners, camMatrix, distCoeffs, 10f, 3f, true, recoveredIdxs, detectorParams);
  417. break;
  418. case MarkerType.ChArUcoBoard:
  419. Aruco.refineDetectedMarkers (rgbMat, charucoBoard, corners, ids, rejectedCorners, camMatrix, distCoeffs, 10f, 3f, true, recoveredIdxs, detectorParams);
  420. break;
  421. }
  422. }
  423. // if at least one marker detected
  424. if (ids.total () > 0) {
  425. if (markerType != MarkerType.ChArUcoDiamondMarker) {
  426. if (markerType == MarkerType.ChArUcoBoard) {
  427. Aruco.interpolateCornersCharuco (corners, ids, rgbMat, charucoBoard, charucoCorners, charucoIds, camMatrix, distCoeffs, charucoMinMarkers);
  428. // draw markers.
  429. Aruco.drawDetectedMarkers (rgbMat, corners, ids, new Scalar (0, 255, 0));
  430. if (charucoIds.total () > 0) {
  431. Aruco.drawDetectedCornersCharuco (rgbMat, charucoCorners, charucoIds, new Scalar (0, 0, 255));
  432. }
  433. } else {
  434. // draw markers.
  435. Aruco.drawDetectedMarkers (rgbMat, corners, ids, new Scalar (0, 255, 0));
  436. }
  437. // estimate pose.
  438. if (applyEstimationPose) {
  439. switch (markerType) {
  440. default:
  441. case MarkerType.CanonicalMarker:
  442. EstimatePoseCanonicalMarker (rgbMat);
  443. break;
  444. case MarkerType.GridBoard:
  445. EstimatePoseGridBoard (rgbMat);
  446. break;
  447. case MarkerType.ChArUcoBoard:
  448. EstimatePoseChArUcoBoard (rgbMat);
  449. break;
  450. }
  451. }
  452. } else {
  453. // detect diamond markers.
  454. Aruco.detectCharucoDiamond (rgbMat, corners, ids, diamondSquareLength / diamondMarkerLength, diamondCorners, diamondIds, camMatrix, distCoeffs);
  455. // draw markers.
  456. Aruco.drawDetectedMarkers (rgbMat, corners, ids, new Scalar (0, 255, 0));
  457. // draw diamond markers.
  458. Aruco.drawDetectedDiamonds (rgbMat, diamondCorners, diamondIds, new Scalar (0, 0, 255));
  459. // estimate pose.
  460. if (applyEstimationPose)
  461. EstimatePoseChArUcoDiamondMarker (rgbMat);
  462. }
  463. }
  464. if (showRejectedCorners && rejectedCorners.Count > 0)
  465. Aruco.drawDetectedMarkers (rgbMat, rejectedCorners, new Mat (), new Scalar (255, 0, 0));
  466. // Imgproc.putText (rgbaMat, "W:" + rgbaMat.width () + " H:" + rgbaMat.height () + " SO:" + Screen.orientation, new Point (5, rgbaMat.rows () - 10), Imgproc.FONT_HERSHEY_SIMPLEX, 1.0, new Scalar (255, 255, 255, 255), 2, Imgproc.LINE_AA, false);
  467. Utils.fastMatToTexture2D (rgbMat, texture);
  468. }
  469. }
  470. private void EstimatePoseCanonicalMarker (Mat rgbMat)
  471. {
  472. Aruco.estimatePoseSingleMarkers (corners, markerLength, camMatrix, distCoeffs, rvecs, tvecs);
  473. for (int i = 0; i < ids.total (); i++) {
  474. using (Mat rvec = new Mat (rvecs, new OpenCVForUnity.CoreModule.Rect (0, i, 1, 1)))
  475. using (Mat tvec = new Mat (tvecs, new OpenCVForUnity.CoreModule.Rect (0, i, 1, 1))) {
  476. // In this example we are processing with RGB color image, so Axis-color correspondences are X: blue, Y: green, Z: red. (Usually X: red, Y: green, Z: blue)
  477. Aruco.drawAxis (rgbMat, camMatrix, distCoeffs, rvec, tvec, markerLength * 0.5f);
  478. // This example can display the ARObject on only first detected marker.
  479. if (i == 0) {
  480. UpdateARObjectTransform (rvec, tvec);
  481. }
  482. }
  483. }
  484. }
  485. private void EstimatePoseGridBoard (Mat rgbMat)
  486. {
  487. int valid = Aruco.estimatePoseBoard (corners, ids, gridBoard, camMatrix, distCoeffs, rvec, tvec);
  488. // if at least one board marker detected
  489. if (valid > 0) {
  490. // In this example we are processing with RGB color image, so Axis-color correspondences are X: blue, Y: green, Z: red. (Usually X: red, Y: green, Z: blue)
  491. Aruco.drawAxis (rgbMat, camMatrix, distCoeffs, rvec, tvec, markerLength * 0.5f);
  492. UpdateARObjectTransform (rvec, tvec);
  493. }
  494. }
  495. private void EstimatePoseChArUcoBoard (Mat rgbMat)
  496. {
  497. // if at least one charuco corner detected
  498. if (charucoIds.total () > 0) {
  499. bool valid = Aruco.estimatePoseCharucoBoard (charucoCorners, charucoIds, charucoBoard, camMatrix, distCoeffs, rvec, tvec);
  500. // if at least one board marker detected
  501. if (valid) {
  502. // In this example we are processing with RGB color image, so Axis-color correspondences are X: blue, Y: green, Z: red. (Usually X: red, Y: green, Z: blue)
  503. Aruco.drawAxis (rgbMat, camMatrix, distCoeffs, rvec, tvec, markerLength * 0.5f);
  504. UpdateARObjectTransform (rvec, tvec);
  505. }
  506. }
  507. }
  508. private void EstimatePoseChArUcoDiamondMarker (Mat rgbMat)
  509. {
  510. Aruco.estimatePoseSingleMarkers (diamondCorners, diamondSquareLength, camMatrix, distCoeffs, rvecs, tvecs);
  511. for (int i = 0; i < rvecs.total (); i++) {
  512. using (Mat rvec = new Mat (rvecs, new OpenCVForUnity.CoreModule.Rect (0, i, 1, 1)))
  513. using (Mat tvec = new Mat (tvecs, new OpenCVForUnity.CoreModule.Rect (0, i, 1, 1))) {
  514. // In this example we are processing with RGB color image, so Axis-color correspondences are X: blue, Y: green, Z: red. (Usually X: red, Y: green, Z: blue)
  515. Aruco.drawAxis (rgbMat, camMatrix, distCoeffs, rvec, tvec, diamondSquareLength * 0.5f);
  516. // This example can display the ARObject on only first detected marker.
  517. if (i == 0) {
  518. UpdateARObjectTransform (rvec, tvec);
  519. }
  520. }
  521. }
  522. }
  523. private void UpdateARObjectTransform (Mat rvec, Mat tvec)
  524. {
  525. // Convert to unity pose data.
  526. double[] rvecArr = new double[3];
  527. rvec.get (0, 0, rvecArr);
  528. double[] tvecArr = new double[3];
  529. tvec.get (0, 0, tvecArr);
  530. PoseData poseData = ARUtils.ConvertRvecTvecToPoseData (rvecArr, tvecArr);
  531. // Changes in pos/rot below these thresholds are ignored.
  532. if (enableLowPassFilter) {
  533. ARUtils.LowpassPoseData (ref oldPoseData, ref poseData, positionLowPass, rotationLowPass);
  534. }
  535. oldPoseData = poseData;
  536. // Convert to transform matrix.
  537. ARM = ARUtils.ConvertPoseDataToMatrix (ref poseData, true, true);
  538. if (shouldMoveARCamera) {
  539. ARM = arGameObject.transform.localToWorldMatrix * ARM.inverse;
  540. ARUtils.SetTransformFromMatrix (arCamera.transform, ref ARM);
  541. } else {
  542. ARM = arCamera.transform.localToWorldMatrix * ARM;
  543. ARUtils.SetTransformFromMatrix (arGameObject.transform, ref ARM);
  544. }
  545. }
  546. private void ResetObjectTransform ()
  547. {
  548. // reset AR object transform.
  549. Matrix4x4 i = Matrix4x4.identity;
  550. ARUtils.SetTransformFromMatrix (arCamera.transform, ref i);
  551. ARUtils.SetTransformFromMatrix (arGameObject.transform, ref i);
  552. }
  553. /// <summary>
  554. /// Raises the destroy event.
  555. /// </summary>
  556. void OnDestroy ()
  557. {
  558. webCamTextureToMatHelper.Dispose ();
  559. }
  560. /// <summary>
  561. /// Raises the back button click event.
  562. /// </summary>
  563. public void OnBackButtonClick ()
  564. {
  565. SceneManager.LoadScene ("OpenCVForUnityExample");
  566. }
  567. /// <summary>
  568. /// Raises the play button click event.
  569. /// </summary>
  570. public void OnPlayButtonClick ()
  571. {
  572. webCamTextureToMatHelper.Play ();
  573. }
  574. /// <summary>
  575. /// Raises the pause button click event.
  576. /// </summary>
  577. public void OnPauseButtonClick ()
  578. {
  579. webCamTextureToMatHelper.Pause ();
  580. }
  581. /// <summary>
  582. /// Raises the stop button click event.
  583. /// </summary>
  584. public void OnStopButtonClick ()
  585. {
  586. webCamTextureToMatHelper.Stop ();
  587. }
  588. /// <summary>
  589. /// Raises the change camera button click event.
  590. /// </summary>
  591. public void OnChangeCameraButtonClick ()
  592. {
  593. webCamTextureToMatHelper.requestedIsFrontFacing = !webCamTextureToMatHelper.IsFrontFacing ();
  594. }
  595. /// <summary>
  596. /// Raises the marker type dropdown value changed event.
  597. /// </summary>
  598. public void OnMarkerTypeDropdownValueChanged (int result)
  599. {
  600. if ((int)markerType != result) {
  601. markerType = (MarkerType)result;
  602. refineMarkerDetectionToggle.interactable = (markerType == MarkerType.GridBoard || markerType == MarkerType.ChArUcoBoard);
  603. ResetObjectTransform ();
  604. if (webCamTextureToMatHelper.IsInitialized ())
  605. webCamTextureToMatHelper.Initialize ();
  606. }
  607. }
  608. /// <summary>
  609. /// Raises the dictionary id dropdown value changed event.
  610. /// </summary>
  611. public void OnDictionaryIdDropdownValueChanged (int result)
  612. {
  613. if ((int)dictionaryId != result) {
  614. dictionaryId = (ArUcoDictionary)result;
  615. dictionary = Aruco.getPredefinedDictionary ((int)dictionaryId);
  616. ResetObjectTransform ();
  617. if (webCamTextureToMatHelper.IsInitialized ())
  618. webCamTextureToMatHelper.Initialize ();
  619. }
  620. }
  621. /// <summary>
  622. /// Raises the show rejected corners toggle value changed event.
  623. /// </summary>
  624. public void OnShowRejectedCornersToggleValueChanged ()
  625. {
  626. showRejectedCorners = showRejectedCornersToggle.isOn;
  627. }
  628. /// <summary>
  629. /// Raises the refine marker detection toggle value changed event.
  630. /// </summary>
  631. public void OnRefineMarkerDetectionToggleValueChanged ()
  632. {
  633. refineMarkerDetection = refineMarkerDetectionToggle.isOn;
  634. }
  635. /// <summary>
  636. /// Raises the enable low pass filter toggle value changed event.
  637. /// </summary>
  638. public void OnEnableLowPassFilterToggleValueChanged ()
  639. {
  640. if (enableLowPassFilterToggle.isOn) {
  641. enableLowPassFilter = true;
  642. } else {
  643. enableLowPassFilter = false;
  644. }
  645. }
  646. public enum MarkerType
  647. {
  648. CanonicalMarker,
  649. GridBoard,
  650. ChArUcoBoard,
  651. ChArUcoDiamondMarker
  652. }
  653. public enum ArUcoDictionary
  654. {
  655. DICT_4X4_50 = Aruco.DICT_4X4_50,
  656. DICT_4X4_100 = Aruco.DICT_4X4_100,
  657. DICT_4X4_250 = Aruco.DICT_4X4_250,
  658. DICT_4X4_1000 = Aruco.DICT_4X4_1000,
  659. DICT_5X5_50 = Aruco.DICT_5X5_50,
  660. DICT_5X5_100 = Aruco.DICT_5X5_100,
  661. DICT_5X5_250 = Aruco.DICT_5X5_250,
  662. DICT_5X5_1000 = Aruco.DICT_5X5_1000,
  663. DICT_6X6_50 = Aruco.DICT_6X6_50,
  664. DICT_6X6_100 = Aruco.DICT_6X6_100,
  665. DICT_6X6_250 = Aruco.DICT_6X6_250,
  666. DICT_6X6_1000 = Aruco.DICT_6X6_1000,
  667. DICT_7X7_50 = Aruco.DICT_7X7_50,
  668. DICT_7X7_100 = Aruco.DICT_7X7_100,
  669. DICT_7X7_250 = Aruco.DICT_7X7_250,
  670. DICT_7X7_1000 = Aruco.DICT_7X7_1000,
  671. DICT_ARUCO_ORIGINAL = Aruco.DICT_ARUCO_ORIGINAL,
  672. }
  673. }
  674. }