ArUcoCameraCalibrationExample.cs 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516
  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. using UnityEngine.SceneManagement;
  4. using System;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. using System.Xml.Serialization;
  8. using System.IO;
  9. using System.Linq;
  10. using OpenCVForUnity.CoreModule;
  11. using OpenCVForUnity.ImgprocModule;
  12. using OpenCVForUnity.Calib3dModule;
  13. using OpenCVForUnity.UnityUtils;
  14. using OpenCVForUnity.ImgcodecsModule;
  15. using OpenCVForUnity.UnityUtils.Helper;
  16. using OpenCVForUnity.ObjdetectModule;
  17. using OpenCVForUnity.ArucoModule;
  18. namespace OpenCVForUnityExample
  19. {
  20. /// <summary>
  21. /// ArUco Camera Calibration Example
  22. /// An example of camera calibration using the objdetect module. (ChessBoard, CirclesGlid, AsymmetricCirclesGlid and ChArUcoBoard)
  23. /// Referring to https://docs.opencv.org/master/d4/d94/tutorial_camera_calibration.html.
  24. /// https://github.com/opencv/opencv/blob/master/samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp
  25. /// https://docs.opencv.org/3.4.0/d7/d21/tutorial_interactive_calibration.html
  26. /// https://github.com/opencv/opencv/tree/master/apps/interactive-calibration
  27. /// https://docs.opencv.org/3.2.0/da/d13/tutorial_aruco_calibration.html
  28. /// https://github.com/opencv/opencv_contrib/blob/master/modules/aruco/samples/calibrate_camera_charuco.cpp
  29. /// </summary>
  30. [RequireComponent(typeof(WebCamTextureToMatHelper))]
  31. public class ArUcoCameraCalibrationExample : MonoBehaviour
  32. {
  33. /// <summary>
  34. /// The marker type used for calibration.
  35. /// </summary>
  36. [Tooltip("The marker type used for calibration.")]
  37. public MarkerType markerType = MarkerType.ChessBoard;
  38. /// <summary>
  39. /// The marker type dropdown.
  40. /// </summary>
  41. public Dropdown markerTypeDropdown;
  42. /// <summary>
  43. /// Number of inner corners per a item column. (square, circle)
  44. /// </summary>
  45. [Tooltip("Number of inner corners per a item column. (square, circle)")]
  46. public NumberOfBoardSizeWidth boardSizeW = NumberOfBoardSizeWidth.W_9;
  47. /// <summary>
  48. /// The board size W dropdown.
  49. /// </summary>
  50. public Dropdown boardSizeWDropdown;
  51. /// <summary>
  52. /// Number of inner corners per a item row. (square, circle)
  53. /// </summary>
  54. [Tooltip("Number of inner corners per a item row. (square, circle)")]
  55. public NumberOfBoardSizeHeight boardSizeH = NumberOfBoardSizeHeight.H_6;
  56. /// <summary>
  57. /// The board size H dropdown.
  58. /// </summary>
  59. public Dropdown boardSizeHDropdown;
  60. /// <summary>
  61. /// The save path input field.
  62. /// </summary>
  63. public InputField savePathInputField;
  64. /// <summary>
  65. /// The show undistort image.
  66. /// </summary>
  67. public bool showUndistortImage = true;
  68. /// <summary>
  69. /// The show undistort image toggle.
  70. /// </summary>
  71. public Toggle showUndistortImageToggle;
  72. [Header("Normal Calibration Option")]
  73. /// <summary>
  74. /// The normal calibration options group.
  75. /// </summary>
  76. public GameObject normalCalibrationOptionsGroup;
  77. /// <summary>
  78. /// The size of a square in some user defined metric system (pixel, millimeter)
  79. /// </summary>
  80. [Tooltip("The size of a square in some user defined metric system (pixel, millimeter)")]
  81. public float squareSize = 50f;
  82. /// <summary>
  83. /// The square size input field.
  84. /// </summary>
  85. public InputField squareSizeInputField;
  86. /// <summary>
  87. /// If your calibration board is inaccurate, unmeasured, roughly planar targets
  88. /// (Checkerboard patterns on paper using off-the-shelf printers are the most convenient calibration targets and most of them are not accurate enough.),
  89. /// a method from [219] can be utilized to dramatically improve the accuracies of the estimated camera intrinsic parameters.
  90. /// Need to set the measured values from the actual chess board to "squareSize" and "gridWidth".
  91. /// https://docs.opencv.org/4.2.0/d9/d0c/group__calib3d.html#ga11eeb16e5a458e1ed382fb27f585b753
  92. /// </summary>
  93. [Tooltip("If your calibration board is inaccurate, unmeasured, roughly planar targets (Checkerboard patterns on paper using off-the-shelf printers are the most convenient calibration targets and most of them are not accurate enough.), a method from [219] can be utilized to dramatically improve the accuracies of the estimated camera intrinsic parameters. Need to set the measured values from the actual chess board to \"squareSize\" and \"gridWidth\".")]
  94. public bool useNewCalibrationMethod = true;
  95. /// <summary>
  96. /// The use new calibration method toggle.
  97. /// </summary>
  98. public Toggle useNewCalibrationMethodToggle;
  99. /// <summary>
  100. /// The measured distance between top-left (0, 0, 0) and top-right (squareSize*(boardSizeW - 1), 0, 0) corners of the pattern grid points.
  101. /// </summary>
  102. [Tooltip("The measured distance between top-left (0, 0, 0) and top-right (squareSize*(boardSizeW - 1), 0, 0) corners of the pattern grid points.")]
  103. public float gridWidth = 400f;
  104. /// <summary>
  105. /// The glid width input field.
  106. /// </summary>
  107. public InputField gridWidthInputField;
  108. /// <summary>
  109. /// Determines if use findChessboardCornersSB method. (More accurate than the findChessboardCorners and cornerSubPix methods)
  110. /// https://docs.opencv.org/4.2.0/d9/d0c/group__calib3d.html#gad0e88e13cd3d410870a99927510d7f91
  111. /// </summary>
  112. [Tooltip("Determines if use findChessboardCornersSB method. (More accurate than the findChessboardCorners and cornerSubPix methods)")]
  113. public bool useFindChessboardCornersSBMethod = true;
  114. /// <summary>
  115. /// Determines if enable CornerSubPix method. (Improve the found corners' coordinate accuracy for chessboard)
  116. /// </summary>
  117. [Tooltip("Determines if enable CornerSubPix method. (Improve the found corners' coordinate accuracy for chessboard)")]
  118. public bool enableCornerSubPix = true;
  119. [Header("ArUco Calibration Option")]
  120. /// <summary>
  121. /// The arUco calibration options group.
  122. /// </summary>
  123. public GameObject arUcoCalibrationOptionsGroup;
  124. /// <summary>
  125. /// The dictionary identifier used for ArUco marker detection.
  126. /// </summary>
  127. [Tooltip("The dictionary identifier used for ArUco marker detection.")]
  128. public ArUcoDictionary dictionaryId = ArUcoDictionary.DICT_6X6_250;
  129. /// <summary>
  130. /// The dictionary id dropdown.
  131. /// </summary>
  132. public Dropdown dictionaryIdDropdown;
  133. /// <summary>
  134. /// Determines if refine marker detection. (only valid for ArUco boards)
  135. /// </summary>
  136. [Tooltip("Determines if refine marker detection. (only valid for ArUco boards)")]
  137. public bool refineMarkerDetection = true;
  138. [Header("Image Input Option")]
  139. /// <summary>
  140. /// Determines if calibrates camera using the list of calibration images.
  141. /// </summary>
  142. [Tooltip("Determines if calibrates camera using the list of calibration images.")]
  143. public bool isImagesInputMode = false;
  144. /// <summary>
  145. /// The calibration images directory path.
  146. /// Set a relative directory path from the starting point of the "StreamingAssets" folder. e.g. "objdetect/calibration_images/".
  147. /// </summary>
  148. [Tooltip("Set a relative directory path from the starting point of the \"StreamingAssets\" folder. e.g. \"OpenCVForUnity/objdetect/calibration_images\"")]
  149. public string calibrationImagesDirectory = "OpenCVForUnity/objdetect/calibration_images";
  150. /// <summary>
  151. /// The texture.
  152. /// </summary>
  153. Texture2D texture;
  154. /// <summary>
  155. /// The webcam texture to mat helper.
  156. /// </summary>
  157. WebCamTextureToMatHelper webCamTextureToMatHelper;
  158. /// <summary>
  159. /// The gray mat.
  160. /// </summary>
  161. Mat grayMat;
  162. /// <summary>
  163. /// The bgr mat.
  164. /// </summary>
  165. Mat bgrMat;
  166. /// <summary>
  167. /// The undistorted bgr mat.
  168. /// </summary>
  169. Mat undistortedBgrMat;
  170. /// <summary>
  171. /// The rgba mat.
  172. /// </summary>
  173. Mat rgbaMat;
  174. /// <summary>
  175. /// The cameraparam matrix.
  176. /// </summary>
  177. Mat camMatrix;
  178. /// <summary>
  179. /// The distortion coeffs.
  180. /// </summary>
  181. MatOfDouble distCoeffs;
  182. /// <summary>
  183. /// The rvecs.
  184. /// </summary>
  185. List<Mat> rvecs;
  186. /// <summary>
  187. /// The tvecs.
  188. /// </summary>
  189. List<Mat> tvecs;
  190. List<Mat> imagePoints;
  191. List<Mat> allImgs;
  192. bool isInitialized = false;
  193. bool isCalibrating = false;
  194. double repErr = 0;
  195. bool shouldCaptureFrame = false;
  196. const int findChessboardCornersFlags =
  197. Calib3d.CALIB_CB_ADAPTIVE_THRESH |
  198. Calib3d.CALIB_CB_NORMALIZE_IMAGE |
  199. //Calib3d.CALIB_CB_FILTER_QUADS |
  200. Calib3d.CALIB_CB_FAST_CHECK |
  201. 0;
  202. const int findChessboardCornersSBFlags =
  203. Calib3d.CALIB_CB_NORMALIZE_IMAGE |
  204. Calib3d.CALIB_CB_EXHAUSTIVE |
  205. Calib3d.CALIB_CB_ACCURACY |
  206. 0;
  207. const int findCirclesGridFlags =
  208. //Calib3d.CALIB_CB_CLUSTERING |
  209. 0;
  210. const int calibrationFlags =
  211. //Calib3d.CALIB_USE_INTRINSIC_GUESS |
  212. //Calib3d.CALIB_FIX_PRINCIPAL_POINT |
  213. //Calib3d.CALIB_FIX_ASPECT_RATIO |
  214. //Calib3d.CALIB_ZERO_TANGENT_DIST |
  215. //Calib3d.CALIB_FIX_K1 |
  216. //Calib3d.CALIB_FIX_K2 |
  217. //Calib3d.CALIB_FIX_K3 |
  218. //Calib3d.CALIB_FIX_K4 |
  219. //Calib3d.CALIB_FIX_K5 |
  220. Calib3d.CALIB_USE_LU |
  221. 0;
  222. /*
  223. // for ChArUcoBoard.
  224. // chessboard square side length (normally in meters)
  225. const float chArUcoBoradSquareLength = 0.04f;
  226. // marker side length (same unit than squareLength)
  227. const float chArUcoBoradMarkerLength = 0.02f;
  228. const int charucoMinMarkers = 2;
  229. Mat ids;
  230. List<Mat> corners;
  231. List<Mat> rejectedCorners;
  232. Mat recoveredIdxs;
  233. Mat charucoCorners;
  234. Mat charucoIds;
  235. CharucoBoard charucoBoard;
  236. ArucoDetector arucoDetector;
  237. CharucoDetector charucoDetector;
  238. */
  239. Dictionary dictionary;
  240. List<List<Mat>> allCorners;
  241. List<Mat> allIds;
  242. // Use this for initialization
  243. IEnumerator Start()
  244. {
  245. //if true, The error log of the Native side OpenCV will be displayed on the Unity Editor Console.
  246. Utils.setDebugMode(true);
  247. webCamTextureToMatHelper = gameObject.GetComponent<WebCamTextureToMatHelper>();
  248. // fix the screen orientation.
  249. Screen.orientation = ScreenOrientation.LandscapeLeft;
  250. // wait for the screen orientation to change.
  251. yield return null;
  252. markerTypeDropdown.value = (int)markerType;
  253. boardSizeWDropdown.value = (int)boardSizeW - 1;
  254. boardSizeHDropdown.value = (int)boardSizeH - 1;
  255. showUndistortImageToggle.isOn = showUndistortImage;
  256. squareSizeInputField.text = squareSize.ToString();
  257. useNewCalibrationMethodToggle.isOn = useNewCalibrationMethod;
  258. gridWidthInputField.text = gridWidth.ToString();
  259. dictionaryIdDropdown.value = (int)dictionaryId;
  260. bool arUcoCalibMode = markerType == MarkerType.ChArUcoBoard;
  261. normalCalibrationOptionsGroup.gameObject.SetActive(!arUcoCalibMode);
  262. arUcoCalibrationOptionsGroup.gameObject.SetActive(arUcoCalibMode);
  263. #if UNITY_WEBGL && !UNITY_EDITOR
  264. isImagesInputMode = false;
  265. #endif
  266. if (isImagesInputMode)
  267. {
  268. isImagesInputMode = InitializeImagesInputMode();
  269. }
  270. if (!isImagesInputMode)
  271. {
  272. #if UNITY_ANDROID && !UNITY_EDITOR
  273. // Avoids the front camera low light issue that occurs in only some Android devices (e.g. Google Pixel, Pixel2).
  274. webCamTextureToMatHelper.avoidAndroidFrontCameraLowLightIssue = true;
  275. #endif
  276. webCamTextureToMatHelper.Initialize();
  277. }
  278. }
  279. /// <summary>
  280. /// Raises the webcam texture to mat helper initialized event.
  281. /// </summary>
  282. public void OnWebCamTextureToMatHelperInitialized()
  283. {
  284. Debug.Log("OnWebCamTextureToMatHelperInitialized");
  285. Mat webCamTextureMat = webCamTextureToMatHelper.GetMat();
  286. InitializeCalibraton(webCamTextureMat);
  287. // If the WebCam is front facing, flip the Mat horizontally. Required for successful detection of AR markers.
  288. if (webCamTextureToMatHelper.IsFrontFacing() && !webCamTextureToMatHelper.flipHorizontal)
  289. {
  290. webCamTextureToMatHelper.flipHorizontal = true;
  291. }
  292. else if (!webCamTextureToMatHelper.IsFrontFacing() && webCamTextureToMatHelper.flipHorizontal)
  293. {
  294. webCamTextureToMatHelper.flipHorizontal = false;
  295. }
  296. }
  297. /// <summary>
  298. /// Raises the webcam texture to mat helper disposed event.
  299. /// </summary>
  300. public void OnWebCamTextureToMatHelperDisposed()
  301. {
  302. Debug.Log("OnWebCamTextureToMatHelperDisposed");
  303. DisposeCalibraton();
  304. }
  305. /// <summary>
  306. /// Raises the webcam texture to mat helper error occurred event.
  307. /// </summary>
  308. /// <param name="errorCode">Error code.</param>
  309. public void OnWebCamTextureToMatHelperErrorOccurred(WebCamTextureToMatHelper.ErrorCode errorCode)
  310. {
  311. Debug.Log("OnWebCamTextureToMatHelperErrorOccurred " + errorCode);
  312. }
  313. // Update is called once per frame
  314. void Update()
  315. {
  316. if (isImagesInputMode)
  317. return;
  318. if (webCamTextureToMatHelper.IsPlaying() && webCamTextureToMatHelper.DidUpdateThisFrame())
  319. {
  320. Mat rgbaMat = webCamTextureToMatHelper.GetMat();
  321. Imgproc.cvtColor(rgbaMat, grayMat, Imgproc.COLOR_RGBA2GRAY);
  322. if (shouldCaptureFrame)
  323. {
  324. shouldCaptureFrame = false;
  325. Mat frameMat = grayMat.clone();
  326. double e = CaptureFrame(frameMat);
  327. if (e > 0)
  328. repErr = e;
  329. }
  330. DrawFrame(grayMat, bgrMat);
  331. if (showUndistortImage)
  332. {
  333. Calib3d.undistort(bgrMat, undistortedBgrMat, camMatrix, distCoeffs);
  334. DrawCalibrationResult(undistortedBgrMat);
  335. Imgproc.cvtColor(undistortedBgrMat, rgbaMat, Imgproc.COLOR_BGR2RGBA);
  336. }
  337. else
  338. {
  339. DrawCalibrationResult(bgrMat);
  340. Imgproc.cvtColor(bgrMat, rgbaMat, Imgproc.COLOR_BGR2RGBA);
  341. }
  342. Utils.matToTexture2D(rgbaMat, texture);
  343. }
  344. }
  345. private void InitializeCalibraton(Mat frameMat)
  346. {
  347. texture = new Texture2D(frameMat.cols(), frameMat.rows(), TextureFormat.RGBA32, false);
  348. Utils.matToTexture2D(frameMat, texture);
  349. gameObject.GetComponent<Renderer>().material.mainTexture = texture;
  350. gameObject.transform.localScale = new Vector3(frameMat.cols(), frameMat.rows(), 1);
  351. Debug.Log("Screen.width " + Screen.width + " Screen.height " + Screen.height + " Screen.orientation " + Screen.orientation);
  352. float width = frameMat.width();
  353. float height = frameMat.height();
  354. float imageSizeScale = 1.0f;
  355. float widthScale = (float)Screen.width / width;
  356. float heightScale = (float)Screen.height / height;
  357. if (widthScale < heightScale)
  358. {
  359. Camera.main.orthographicSize = (width * (float)Screen.height / (float)Screen.width) / 2;
  360. imageSizeScale = (float)Screen.height / (float)Screen.width;
  361. }
  362. else
  363. {
  364. Camera.main.orthographicSize = height / 2;
  365. }
  366. // set cameraparam.
  367. camMatrix = CreateCameraMatrix(width, height);
  368. Debug.Log("camMatrix " + camMatrix.dump());
  369. distCoeffs = new MatOfDouble(0, 0, 0, 0, 0);
  370. Debug.Log("distCoeffs " + distCoeffs.dump());
  371. // calibration camera.
  372. Size imageSize = new Size(width * imageSizeScale, height * imageSizeScale);
  373. double apertureWidth = 0;
  374. double apertureHeight = 0;
  375. double[] fovx = new double[1];
  376. double[] fovy = new double[1];
  377. double[] focalLength = new double[1];
  378. Point principalPoint = new Point(0, 0);
  379. double[] aspectratio = new double[1];
  380. Calib3d.calibrationMatrixValues(camMatrix, imageSize, apertureWidth, apertureHeight, fovx, fovy, focalLength, principalPoint, aspectratio);
  381. Debug.Log("imageSize " + imageSize.ToString());
  382. Debug.Log("apertureWidth " + apertureWidth);
  383. Debug.Log("apertureHeight " + apertureHeight);
  384. Debug.Log("fovx " + fovx[0]);
  385. Debug.Log("fovy " + fovy[0]);
  386. Debug.Log("focalLength " + focalLength[0]);
  387. Debug.Log("principalPoint " + principalPoint.ToString());
  388. Debug.Log("aspectratio " + aspectratio[0]);
  389. grayMat = new Mat(frameMat.rows(), frameMat.cols(), CvType.CV_8UC1);
  390. bgrMat = new Mat(frameMat.rows(), frameMat.cols(), CvType.CV_8UC3);
  391. undistortedBgrMat = new Mat();
  392. rgbaMat = new Mat(frameMat.rows(), frameMat.cols(), CvType.CV_8UC4);
  393. rvecs = new List<Mat>();
  394. tvecs = new List<Mat>();
  395. imagePoints = new List<Mat>();
  396. allImgs = new List<Mat>();
  397. /*
  398. ids = new Mat();
  399. corners = new List<Mat>();
  400. rejectedCorners = new List<Mat>();
  401. recoveredIdxs = new Mat();
  402. DetectorParameters detectorParams = new DetectorParameters();
  403. detectorParams.set_minDistanceToBorder(3);
  404. detectorParams.set_useAruco3Detection(true);
  405. detectorParams.set_cornerRefinementMethod(Objdetect.CORNER_REFINE_SUBPIX);
  406. detectorParams.set_minSideLengthCanonicalImg(16);
  407. detectorParams.set_errorCorrectionRate(0.8);
  408. dictionary = Objdetect.getPredefinedDictionary((int)dictionaryId);
  409. RefineParameters refineParameters = new RefineParameters(10f, 3f, true);
  410. arucoDetector = new ArucoDetector(dictionary, detectorParams, refineParameters);
  411. charucoCorners = new Mat();
  412. charucoIds = new Mat();
  413. charucoBoard = new CharucoBoard( new Size((int)boardSizeW, (int)boardSizeH), chArUcoBoradSquareLength, chArUcoBoradMarkerLength, dictionary);
  414. charucoDetector = new CharucoDetector(charucoBoard);
  415. CharucoParameters charucoParameters = charucoDetector.getCharucoParameters();
  416. charucoParameters.set_cameraMatrix(camMatrix);
  417. charucoParameters.set_distCoeffs(distCoeffs);
  418. charucoParameters.set_minMarkers(charucoMinMarkers);
  419. charucoDetector.setCharucoParameters(charucoParameters);
  420. charucoDetector.setDetectorParameters(detectorParams);
  421. charucoDetector.setRefineParameters(refineParameters);
  422. */
  423. allIds = new List<Mat>();
  424. allCorners = new List<List<Mat>>();
  425. isInitialized = true;
  426. }
  427. private void DisposeCalibraton()
  428. {
  429. ResetCalibration();
  430. if (grayMat != null)
  431. grayMat.Dispose();
  432. if (bgrMat != null)
  433. bgrMat.Dispose();
  434. if (undistortedBgrMat != null)
  435. undistortedBgrMat.Dispose();
  436. if (rgbaMat != null)
  437. rgbaMat.Dispose();
  438. if (texture != null)
  439. {
  440. Texture2D.Destroy(texture);
  441. texture = null;
  442. }
  443. foreach (var item in rvecs)
  444. {
  445. item.Dispose();
  446. }
  447. rvecs.Clear();
  448. foreach (var item in tvecs)
  449. {
  450. item.Dispose();
  451. }
  452. tvecs.Clear();
  453. /*
  454. if (ids != null)
  455. ids.Dispose();
  456. foreach (var item in corners)
  457. {
  458. item.Dispose();
  459. }
  460. corners.Clear();
  461. foreach (var item in rejectedCorners)
  462. {
  463. item.Dispose();
  464. }
  465. rejectedCorners.Clear();
  466. if (recoveredIdxs != null)
  467. recoveredIdxs.Dispose();
  468. if (charucoCorners != null)
  469. charucoCorners.Dispose();
  470. if (charucoIds != null)
  471. charucoIds.Dispose();
  472. if (charucoBoard != null)
  473. charucoBoard.Dispose();
  474. if (arucoDetector != null)
  475. arucoDetector.Dispose();
  476. if (charucoDetector != null)
  477. charucoDetector.Dispose();
  478. */
  479. isInitialized = false;
  480. }
  481. private void DrawFrame(Mat grayMat, Mat bgrMat)
  482. {
  483. Imgproc.cvtColor(grayMat, bgrMat, Imgproc.COLOR_GRAY2BGR);
  484. switch (markerType)
  485. {
  486. default:
  487. case MarkerType.ChessBoard:
  488. case MarkerType.CirclesGlid:
  489. case MarkerType.AsymmetricCirclesGlid:
  490. // detect markers.
  491. MatOfPoint2f points = new MatOfPoint2f();
  492. bool found = false;
  493. switch (markerType)
  494. {
  495. default:
  496. case MarkerType.ChessBoard:
  497. if (useFindChessboardCornersSBMethod)
  498. {
  499. found = Calib3d.findChessboardCornersSB(grayMat, new Size((int)boardSizeW, (int)boardSizeH), points, findChessboardCornersSBFlags);
  500. }
  501. else
  502. {
  503. found = Calib3d.findChessboardCorners(grayMat, new Size((int)boardSizeW, (int)boardSizeH), points, findChessboardCornersFlags);
  504. }
  505. break;
  506. case MarkerType.CirclesGlid:
  507. found = Calib3d.findCirclesGrid(grayMat, new Size((int)boardSizeW, (int)boardSizeH), points, findCirclesGridFlags | Calib3d.CALIB_CB_SYMMETRIC_GRID);
  508. break;
  509. case MarkerType.AsymmetricCirclesGlid:
  510. found = Calib3d.findCirclesGrid(grayMat, new Size((int)boardSizeW, (int)boardSizeH), points, findCirclesGridFlags | Calib3d.CALIB_CB_ASYMMETRIC_GRID);
  511. break;
  512. }
  513. if (found)
  514. {
  515. // draw markers.
  516. Calib3d.drawChessboardCorners(bgrMat, new Size((int)boardSizeW, (int)boardSizeH), points, found);
  517. }
  518. break;
  519. case MarkerType.ChArUcoBoard:
  520. /*
  521. // detect markers.
  522. arucoDetector.detectMarkers(grayMat, corners, ids, rejectedCorners);
  523. // refine marker detection.
  524. if (refineMarkerDetection)
  525. {
  526. // https://github.com/opencv/opencv/blob/377be68d923e40900ac5526242bcf221e3f355e5/modules/objdetect/src/aruco/charuco_detector.cpp#L310
  527. arucoDetector.refineDetectedMarkers(grayMat, charucoBoard, corners, ids, rejectedCorners);
  528. }
  529. // if at least one marker detected
  530. if (ids.total() > 0)
  531. {
  532. charucoDetector.detectBoard(grayMat, charucoCorners, charucoIds, corners, ids);
  533. // draw markers.
  534. if (corners.Count == ids.total() || ids.total() == 0)
  535. Objdetect.drawDetectedMarkers(bgrMat, corners, ids, new Scalar(0, 255, 0, 255));
  536. // if at least one charuco corner detected
  537. if (charucoCorners.total() == charucoIds.total() || charucoIds.total() == 0)
  538. {
  539. Objdetect.drawDetectedCornersCharuco(bgrMat, charucoCorners, charucoIds, new Scalar(0, 0, 255, 255));
  540. }
  541. }
  542. */
  543. break;
  544. }
  545. }
  546. private void DrawCalibrationResult(Mat bgrMat)
  547. {
  548. double[] camMatrixArr = new double[(int)camMatrix.total()];
  549. camMatrix.get(0, 0, camMatrixArr);
  550. double[] distCoeffsArr = new double[(int)distCoeffs.total()];
  551. distCoeffs.get(0, 0, distCoeffsArr);
  552. int textLeft = 320;
  553. int ff = Imgproc.FONT_HERSHEY_SIMPLEX;
  554. double fs = 0.4;
  555. Scalar c = new Scalar(255, 255, 255, 255);
  556. int t = 0;
  557. int lt = Imgproc.LINE_AA;
  558. bool blo = false;
  559. int frameCount = (markerType == MarkerType.ChArUcoBoard) ? allCorners.Count : imagePoints.Count;
  560. Imgproc.putText(bgrMat, frameCount + " FRAME CAPTURED", new Point(bgrMat.cols() - textLeft, 20), ff, fs, c, t, lt, blo);
  561. Imgproc.putText(bgrMat, "IMAGE_WIDTH: " + bgrMat.width(), new Point(bgrMat.cols() - textLeft, 40), ff, fs, c, t, lt, blo);
  562. Imgproc.putText(bgrMat, "IMAGE_HEIGHT: " + bgrMat.height(), new Point(bgrMat.cols() - textLeft, 60), ff, fs, c, t, lt, blo);
  563. Imgproc.putText(bgrMat, "CALIBRATION_FLAGS: " + calibrationFlags, new Point(bgrMat.cols() - textLeft, 80), ff, fs, c, t, lt, blo);
  564. Imgproc.putText(bgrMat, "CAMERA_MATRIX: ", new Point(bgrMat.cols() - 310, 100), ff, fs, c, t, lt, blo);
  565. for (int i = 0; i < camMatrixArr.Length; i = i + 3)
  566. {
  567. Imgproc.putText(bgrMat, " " + camMatrixArr[i] + ", " + camMatrixArr[i + 1] + ", " + camMatrixArr[i + 2] + ",", new Point(bgrMat.cols() - textLeft, 120 + 20 * i / 3), ff, fs, c, t, lt, blo);
  568. }
  569. Imgproc.putText(bgrMat, "DISTORTION_COEFFICIENTS: ", new Point(bgrMat.cols() - textLeft, 180), ff, fs, c, t, lt, blo);
  570. for (int i = 0; i < distCoeffsArr.Length; ++i)
  571. {
  572. Imgproc.putText(bgrMat, " " + distCoeffsArr[i] + ",", new Point(bgrMat.cols() - textLeft, 200 + 20 * i), ff, fs, c, t, lt, blo);
  573. }
  574. Imgproc.putText(bgrMat, "AVG_REPROJECTION_ERROR: " + repErr, new Point(bgrMat.cols() - textLeft, 300), ff, fs, c, t, lt, blo);
  575. if (frameCount == 0)
  576. Imgproc.putText(bgrMat, "Please press the capture button to start!", new Point(5, bgrMat.rows() - 10), Imgproc.FONT_HERSHEY_SIMPLEX, 0.5, new Scalar(255, 255, 255, 255), 1, Imgproc.LINE_AA, false);
  577. }
  578. private double CaptureFrame(Mat frameMat)
  579. {
  580. double repErr = -1;
  581. switch (markerType)
  582. {
  583. default:
  584. case MarkerType.ChessBoard:
  585. case MarkerType.CirclesGlid:
  586. case MarkerType.AsymmetricCirclesGlid:
  587. MatOfPoint2f points = new MatOfPoint2f();
  588. Size patternSize = new Size((int)boardSizeW, (int)boardSizeH);
  589. bool found = false;
  590. switch (markerType)
  591. {
  592. default:
  593. case MarkerType.ChessBoard:
  594. if (useFindChessboardCornersSBMethod)
  595. {
  596. found = Calib3d.findChessboardCornersSB(frameMat, patternSize, points, findChessboardCornersSBFlags);
  597. }
  598. else
  599. {
  600. found = Calib3d.findChessboardCorners(frameMat, patternSize, points, findChessboardCornersFlags);
  601. }
  602. break;
  603. case MarkerType.CirclesGlid:
  604. found = Calib3d.findCirclesGrid(frameMat, patternSize, points, findCirclesGridFlags | Calib3d.CALIB_CB_SYMMETRIC_GRID);
  605. break;
  606. case MarkerType.AsymmetricCirclesGlid:
  607. found = Calib3d.findCirclesGrid(frameMat, patternSize, points, findCirclesGridFlags | Calib3d.CALIB_CB_ASYMMETRIC_GRID);
  608. break;
  609. }
  610. if (found)
  611. {
  612. if (markerType == MarkerType.ChessBoard && !useFindChessboardCornersSBMethod && enableCornerSubPix)
  613. {
  614. int winSize = 11;
  615. Imgproc.cornerSubPix(frameMat, points, new Size(winSize, winSize), new Size(-1, -1), new TermCriteria(TermCriteria.EPS + TermCriteria.COUNT, 30, 0.0001));
  616. }
  617. imagePoints.Add(points);
  618. allImgs.Add(frameMat);
  619. Debug.Log(imagePoints.Count + " Frame captured.");
  620. }
  621. else
  622. {
  623. Debug.Log("Invalid frame.");
  624. frameMat.Dispose();
  625. if (points != null)
  626. points.Dispose();
  627. return -1;
  628. }
  629. if (imagePoints.Count < 1)
  630. {
  631. Debug.Log("Not enough points for calibration.");
  632. repErr = -1;
  633. }
  634. else
  635. {
  636. MatOfPoint3f objectPoint = new MatOfPoint3f(new Mat(imagePoints[0].rows(), 1, CvType.CV_32FC3));
  637. CalcChessboardCorners(patternSize, squareSize, objectPoint, markerType);
  638. float grid_width = squareSize * ((int)patternSize.width - 1);
  639. bool release_object = false;
  640. if (useNewCalibrationMethod)
  641. {
  642. grid_width = gridWidth;
  643. release_object = true;
  644. }
  645. float[] tlPt = new float[3]; // top-left point
  646. objectPoint.get(0, 0, tlPt);
  647. float[] trPt = new float[3]; // top-right point
  648. objectPoint.get((int)patternSize.width - 1, 0, trPt);
  649. trPt[0] = tlPt[0] + grid_width;
  650. objectPoint.put((int)patternSize.width - 1, 0, trPt);
  651. Mat newObjPoints = objectPoint.clone();
  652. List<Mat> objectPoints = new List<Mat>();
  653. for (int i = 0; i < imagePoints.Count; ++i)
  654. {
  655. objectPoints.Add(objectPoint.clone());
  656. }
  657. int iFixedPoint = -1;
  658. if (release_object)
  659. iFixedPoint = (int)patternSize.width - 1;
  660. repErr = Calib3d.calibrateCameraRO(
  661. objectPoints,
  662. imagePoints,
  663. frameMat.size(),
  664. iFixedPoint,
  665. camMatrix,
  666. distCoeffs,
  667. rvecs,
  668. tvecs,
  669. newObjPoints,
  670. calibrationFlags
  671. );
  672. //if (release_object)
  673. //{
  674. // Debug.Log("New board corners: ");
  675. // Point3[] newPoints = new MatOfPoint3f(newObjPoints).toArray();
  676. // Debug.Log(newPoints[0]);
  677. // Debug.Log(newPoints[(int)patternSize.width - 1]);
  678. // Debug.Log(newPoints[(int)patternSize.width * ((int)patternSize.height - 1)]);
  679. // Debug.Log(newPoints[newPoints.Length - 1]);
  680. //}
  681. objectPoint.Dispose();
  682. }
  683. break;
  684. case MarkerType.ChArUcoBoard:
  685. /*
  686. List<Mat> corners = new List<Mat>();
  687. Mat ids = new Mat();
  688. arucoDetector.detectMarkers(frameMat, corners, ids, rejectedCorners);
  689. if (refineMarkerDetection)
  690. {
  691. // https://github.com/opencv/opencv/blob/377be68d923e40900ac5526242bcf221e3f355e5/modules/objdetect/src/aruco/charuco_detector.cpp#L310
  692. arucoDetector.refineDetectedMarkers(frameMat, charucoBoard, corners, ids, rejectedCorners);
  693. }
  694. if (ids.total() > 0)
  695. {
  696. allCorners.Add(corners);
  697. allIds.Add(ids);
  698. allImgs.Add(frameMat);
  699. Debug.Log(allCorners.Count + " Frame captured.");
  700. }
  701. else
  702. {
  703. Debug.Log("Invalid frame.");
  704. frameMat.Dispose();
  705. if (ids != null)
  706. ids.Dispose();
  707. foreach (var item in corners)
  708. {
  709. item.Dispose();
  710. }
  711. corners.Clear();
  712. return -1;
  713. }
  714. // calibrate camera using charuco boards
  715. repErr = CalibrateCameraCharuco(allCorners, allIds, charucoBoard, frameMat.size(), camMatrix, distCoeffs, rvecs, tvecs, calibrationFlags, calibrationFlags);
  716. */
  717. break;
  718. }
  719. Debug.Log("repErr: " + repErr);
  720. Debug.Log("camMatrix: " + camMatrix.dump());
  721. Debug.Log("distCoeffs: " + distCoeffs.dump());
  722. return repErr;
  723. }
  724. /*
  725. private double CalibrateCameraCharuco(List<List<Mat>> allCorners, List<Mat> allIds, CharucoBoard board, Size imageSize, Mat cameraMatrix, Mat distCoeffs, List<Mat> rvecs = null, List<Mat> tvecs = null, int calibrationFlags = 0, int minMarkers = 2)
  726. {
  727. // prepare data for charuco calibration
  728. int nFrames = allCorners.Count;
  729. List<Mat> allCharucoCorners = new List<Mat>();
  730. List<Mat> allCharucoIds = new List<Mat>();
  731. List<Mat> filteredImages = new List<Mat>();
  732. for (int i = 0; i < nFrames; ++i)
  733. {
  734. // interpolate using camera parameters
  735. Mat currentCharucoCorners = new Mat();
  736. Mat currentCharucoIds = new Mat();
  737. charucoDetector.detectBoard(allImgs[i], currentCharucoCorners, currentCharucoIds, allCorners[i], allIds[i]);
  738. //if (currentCharucoIds.total() > 0)
  739. if (currentCharucoIds.total() > 0 && currentCharucoCorners.total() == currentCharucoIds.total())
  740. {
  741. allCharucoCorners.Add(currentCharucoCorners);
  742. allCharucoIds.Add(currentCharucoIds);
  743. filteredImages.Add(allImgs[i]);
  744. }
  745. else
  746. {
  747. currentCharucoCorners.Dispose();
  748. currentCharucoIds.Dispose();
  749. }
  750. }
  751. if (allCharucoCorners.Count < 1)
  752. {
  753. Debug.Log("Not enough corners for calibration.");
  754. return -1;
  755. }
  756. if (rvecs == null)
  757. rvecs = new List<Mat>();
  758. if (tvecs == null)
  759. tvecs = new List<Mat>();
  760. return Aruco.calibrateCameraCharuco(allCharucoCorners, allCharucoIds, board, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, calibrationFlags); // error
  761. }
  762. */
  763. private void ResetCalibration()
  764. {
  765. foreach (var item in allImgs)
  766. {
  767. item.Dispose();
  768. }
  769. allImgs.Clear();
  770. repErr = 0;
  771. camMatrix = CreateCameraMatrix(bgrMat.width(), bgrMat.height());
  772. distCoeffs = new MatOfDouble(0, 0, 0, 0, 0);
  773. foreach (var item in imagePoints)
  774. {
  775. item.Dispose();
  776. }
  777. imagePoints.Clear();
  778. foreach (var corners in allCorners)
  779. {
  780. foreach (var item in corners)
  781. {
  782. item.Dispose();
  783. }
  784. }
  785. allCorners.Clear();
  786. foreach (var item in allIds)
  787. {
  788. item.Dispose();
  789. }
  790. allIds.Clear();
  791. }
  792. private Mat CreateCameraMatrix(float width, float height)
  793. {
  794. int max_d = (int)Mathf.Max(width, height);
  795. double fx = max_d;
  796. double fy = max_d;
  797. double cx = width / 2.0f;
  798. double cy = height / 2.0f;
  799. Mat camMatrix = new Mat(3, 3, CvType.CV_64FC1);
  800. camMatrix.put(0, 0, fx);
  801. camMatrix.put(0, 1, 0);
  802. camMatrix.put(0, 2, cx);
  803. camMatrix.put(1, 0, 0);
  804. camMatrix.put(1, 1, fy);
  805. camMatrix.put(1, 2, cy);
  806. camMatrix.put(2, 0, 0);
  807. camMatrix.put(2, 1, 0);
  808. camMatrix.put(2, 2, 1.0f);
  809. return camMatrix;
  810. }
  811. private void CalcChessboardCorners(Size patternSize, float squareSize, MatOfPoint3f corners, MarkerType markerType)
  812. {
  813. if ((int)(patternSize.width * patternSize.height) != corners.rows())
  814. {
  815. Debug.Log("Invalid corners size.");
  816. corners.create((int)(patternSize.width * patternSize.height), 1, CvType.CV_32FC3);
  817. }
  818. int width = (int)patternSize.width;
  819. int height = (int)patternSize.height;
  820. switch (markerType)
  821. {
  822. default:
  823. case MarkerType.ChessBoard:
  824. case MarkerType.CirclesGlid:
  825. for (int i = 0; i < height; ++i)
  826. {
  827. for (int j = 0; j < width; ++j)
  828. {
  829. corners.put(width * i + j, 0, new float[] { j * squareSize, i * squareSize, 0f });
  830. }
  831. }
  832. break;
  833. case MarkerType.AsymmetricCirclesGlid:
  834. for (int i = 0; i < height; ++i)
  835. {
  836. for (int j = 0; j < width; ++j)
  837. {
  838. corners.put(width * i + j, 0, new float[] { (2 * j + i % 2) * squareSize, i * squareSize, 0f });
  839. }
  840. }
  841. break;
  842. }
  843. }
  844. private bool InitializeImagesInputMode()
  845. {
  846. if (isInitialized)
  847. DisposeCalibraton();
  848. if (String.IsNullOrEmpty(calibrationImagesDirectory))
  849. {
  850. Debug.LogWarning("When using the images input mode, please set a calibration images directory path.");
  851. return false;
  852. }
  853. string dirPath = Path.Combine(Application.streamingAssetsPath, calibrationImagesDirectory);
  854. if (!Directory.Exists(dirPath))
  855. {
  856. Debug.LogWarning("The directory does not exist.");
  857. return false;
  858. }
  859. string[] imageFiles = GetImageFilesInDirectory(dirPath);
  860. if (imageFiles.Length < 1)
  861. {
  862. Debug.LogWarning("The image file does not exist.");
  863. return false;
  864. }
  865. Uri rootPath = new Uri(Application.streamingAssetsPath + System.IO.Path.AltDirectorySeparatorChar);
  866. Uri fullPath = new Uri(imageFiles[0]);
  867. string relativePath = rootPath.MakeRelativeUri(fullPath).ToString();
  868. using (Mat gray = Imgcodecs.imread(Utils.getFilePath(relativePath), Imgcodecs.IMREAD_GRAYSCALE))
  869. {
  870. if (gray.total() == 0)
  871. {
  872. Debug.LogWarning("Invalid image file.");
  873. return false;
  874. }
  875. using (Mat bgr = new Mat(gray.size(), CvType.CV_8UC3))
  876. using (Mat rgba = new Mat(gray.size(), CvType.CV_8UC4))
  877. {
  878. Imgproc.cvtColor(gray, rgba, Imgproc.COLOR_GRAY2RGBA);
  879. InitializeCalibraton(rgba);
  880. DrawFrame(gray, bgr);
  881. DrawCalibrationResult(bgr);
  882. Imgproc.cvtColor(bgr, rgba, Imgproc.COLOR_BGR2RGBA);
  883. Utils.matToTexture2D(rgba, texture);
  884. }
  885. }
  886. return true;
  887. }
  888. private IEnumerator CalibrateCameraUsingImages()
  889. {
  890. string dirPath = Path.Combine(Application.streamingAssetsPath, calibrationImagesDirectory);
  891. string[] imageFiles = GetImageFilesInDirectory(dirPath);
  892. if (imageFiles.Length < 1)
  893. yield break;
  894. isCalibrating = true;
  895. markerTypeDropdown.interactable = boardSizeWDropdown.interactable = boardSizeHDropdown.interactable = false;
  896. normalCalibrationOptionsGroup.gameObject.SetActive(false);
  897. arUcoCalibrationOptionsGroup.gameObject.SetActive(false);
  898. Uri rootPath = new Uri(Application.streamingAssetsPath + System.IO.Path.AltDirectorySeparatorChar);
  899. foreach (var path in imageFiles)
  900. {
  901. Uri fullPath = new Uri(path);
  902. string relativePath = rootPath.MakeRelativeUri(fullPath).ToString();
  903. using (Mat gray = Imgcodecs.imread(Utils.getFilePath(relativePath), Imgcodecs.IMREAD_GRAYSCALE))
  904. {
  905. if (gray.width() != bgrMat.width() || gray.height() != bgrMat.height())
  906. continue;
  907. Mat frameMat = gray.clone();
  908. double e = CaptureFrame(frameMat);
  909. if (e > 0)
  910. repErr = e;
  911. DrawFrame(gray, bgrMat);
  912. DrawCalibrationResult(bgrMat);
  913. Imgproc.cvtColor(bgrMat, rgbaMat, Imgproc.COLOR_BGR2RGBA);
  914. Utils.matToTexture2D(rgbaMat, texture);
  915. }
  916. yield return new WaitForSeconds(0.5f);
  917. }
  918. isCalibrating = false;
  919. markerTypeDropdown.interactable = boardSizeWDropdown.interactable = boardSizeHDropdown.interactable = true;
  920. bool arUcoCalibMode = markerType == MarkerType.ChArUcoBoard;
  921. normalCalibrationOptionsGroup.gameObject.SetActive(!arUcoCalibMode);
  922. arUcoCalibrationOptionsGroup.gameObject.SetActive(arUcoCalibMode);
  923. }
  924. private string[] GetImageFilesInDirectory(string dirPath)
  925. {
  926. if (Directory.Exists(dirPath))
  927. {
  928. string[] files = Directory.GetFiles(dirPath, "*.jpg");
  929. files = files.Concat(Directory.GetFiles(dirPath, "*.jpeg")).ToArray();
  930. files = files.Concat(Directory.GetFiles(dirPath, "*.png")).ToArray();
  931. files = files.Concat(Directory.GetFiles(dirPath, "*.tiff")).ToArray();
  932. files = files.Concat(Directory.GetFiles(dirPath, "*.tif")).ToArray();
  933. return files;
  934. }
  935. return new string[0];
  936. }
  937. /// <summary>
  938. /// Raises the destroy event.
  939. /// </summary>
  940. void OnDestroy()
  941. {
  942. if (isImagesInputMode)
  943. {
  944. DisposeCalibraton();
  945. }
  946. else
  947. {
  948. webCamTextureToMatHelper.Dispose();
  949. }
  950. Screen.orientation = ScreenOrientation.AutoRotation;
  951. Utils.setDebugMode(false);
  952. }
  953. /// <summary>
  954. /// Raises the back button click event.
  955. /// </summary>
  956. public void OnBackButtonClick()
  957. {
  958. SceneManager.LoadScene("OpenCVForUnityExample");
  959. }
  960. /// <summary>
  961. /// Raises the play button click event.
  962. /// </summary>
  963. public void OnPlayButtonClick()
  964. {
  965. if (isImagesInputMode)
  966. return;
  967. webCamTextureToMatHelper.Play();
  968. }
  969. /// <summary>
  970. /// Raises the pause button click event.
  971. /// </summary>
  972. public void OnPauseButtonClick()
  973. {
  974. if (isImagesInputMode)
  975. return;
  976. webCamTextureToMatHelper.Pause();
  977. }
  978. /// <summary>
  979. /// Raises the stop button click event.
  980. /// </summary>
  981. public void OnStopButtonClick()
  982. {
  983. if (isImagesInputMode)
  984. return;
  985. webCamTextureToMatHelper.Stop();
  986. }
  987. /// <summary>
  988. /// Raises the change camera button click event.
  989. /// </summary>
  990. public void OnChangeCameraButtonClick()
  991. {
  992. if (isImagesInputMode)
  993. return;
  994. webCamTextureToMatHelper.requestedIsFrontFacing = !webCamTextureToMatHelper.requestedIsFrontFacing;
  995. }
  996. /// <summary>
  997. /// Raises the marker type dropdown value changed event.
  998. /// </summary>
  999. public void OnMarkerTypeDropdownValueChanged(int result)
  1000. {
  1001. if ((int)markerType != result)
  1002. {
  1003. markerType = (MarkerType)result;
  1004. bool arUcoCalibMode = markerType == MarkerType.ChArUcoBoard;
  1005. normalCalibrationOptionsGroup.gameObject.SetActive(!arUcoCalibMode);
  1006. arUcoCalibrationOptionsGroup.gameObject.SetActive(arUcoCalibMode);
  1007. if (isImagesInputMode)
  1008. {
  1009. InitializeImagesInputMode();
  1010. }
  1011. else
  1012. {
  1013. if (webCamTextureToMatHelper.IsInitialized())
  1014. webCamTextureToMatHelper.Initialize();
  1015. }
  1016. }
  1017. }
  1018. /// <summary>
  1019. /// Raises the board size W dropdown value changed event.
  1020. /// </summary>
  1021. public void OnBoardSizeWDropdownValueChanged(int result)
  1022. {
  1023. if ((int)boardSizeW != result + 1)
  1024. {
  1025. boardSizeW = (NumberOfBoardSizeWidth)(result + 1);
  1026. gridWidth = squareSize * ((int)boardSizeW - 1);
  1027. gridWidthInputField.text = gridWidth.ToString();
  1028. if (isImagesInputMode)
  1029. {
  1030. InitializeImagesInputMode();
  1031. }
  1032. else
  1033. {
  1034. if (webCamTextureToMatHelper.IsInitialized())
  1035. webCamTextureToMatHelper.Initialize();
  1036. }
  1037. }
  1038. }
  1039. /// <summary>
  1040. /// Raises the board size H dropdown value changed event.
  1041. /// </summary>
  1042. public void OnBoardSizeHDropdownValueChanged(int result)
  1043. {
  1044. if ((int)boardSizeH != result + 1)
  1045. {
  1046. boardSizeH = (NumberOfBoardSizeHeight)(result + 1);
  1047. if (isImagesInputMode)
  1048. {
  1049. InitializeImagesInputMode();
  1050. }
  1051. else
  1052. {
  1053. if (webCamTextureToMatHelper.IsInitialized())
  1054. webCamTextureToMatHelper.Initialize();
  1055. }
  1056. }
  1057. }
  1058. /// <summary>
  1059. /// Raises the show undistort image toggle value changed event.
  1060. /// </summary>
  1061. public void OnShowUndistortImageToggleValueChanged()
  1062. {
  1063. if (showUndistortImage != showUndistortImageToggle.isOn)
  1064. {
  1065. showUndistortImage = showUndistortImageToggle.isOn;
  1066. }
  1067. }
  1068. /// <summary>
  1069. /// Raises the square size input field end edit event.
  1070. /// </summary>
  1071. public void OnSquareSizeInputFieldEndEdit()
  1072. {
  1073. float f;
  1074. bool result = float.TryParse(squareSizeInputField.text, out f);
  1075. if (result)
  1076. {
  1077. squareSize = f;
  1078. squareSizeInputField.text = f.ToString();
  1079. }
  1080. else
  1081. {
  1082. squareSize = 1f;
  1083. squareSizeInputField.text = squareSize.ToString();
  1084. }
  1085. }
  1086. /// <summary>
  1087. /// Raises the use new calibration method toggle value changed event.
  1088. /// </summary>
  1089. public void OnUseNewCalibrationMethodToggleValueChanged()
  1090. {
  1091. if (useNewCalibrationMethod != useNewCalibrationMethodToggle.isOn)
  1092. {
  1093. useNewCalibrationMethod = useNewCalibrationMethodToggle.isOn;
  1094. }
  1095. }
  1096. /// <summary>
  1097. /// Raises the grid width input field end edit event.
  1098. /// </summary>
  1099. public void OnGridWidthInputFieldEndEdit()
  1100. {
  1101. float f;
  1102. bool result = float.TryParse(gridWidthInputField.text, out f);
  1103. if (result)
  1104. {
  1105. gridWidth = f;
  1106. gridWidthInputField.text = f.ToString();
  1107. }
  1108. else
  1109. {
  1110. gridWidth = squareSize * ((int)boardSizeW - 1);
  1111. gridWidthInputField.text = gridWidth.ToString();
  1112. }
  1113. }
  1114. /// <summary>
  1115. /// Raises the dictionary id dropdown value changed event.
  1116. /// </summary>
  1117. public void OnDictionaryIdDropdownValueChanged(int result)
  1118. {
  1119. if ((int)dictionaryId != result)
  1120. {
  1121. dictionaryId = (ArUcoDictionary)result;
  1122. dictionary = Objdetect.getPredefinedDictionary((int)dictionaryId);
  1123. if (isImagesInputMode)
  1124. {
  1125. InitializeImagesInputMode();
  1126. }
  1127. else
  1128. {
  1129. if (webCamTextureToMatHelper.IsInitialized())
  1130. webCamTextureToMatHelper.Initialize();
  1131. }
  1132. }
  1133. }
  1134. /// <summary>
  1135. /// Raises the capture button click event.
  1136. /// </summary>
  1137. public void OnCaptureButtonClick()
  1138. {
  1139. if (isImagesInputMode)
  1140. {
  1141. if (!isCalibrating)
  1142. InitializeImagesInputMode();
  1143. StartCoroutine("CalibrateCameraUsingImages");
  1144. }
  1145. else
  1146. {
  1147. shouldCaptureFrame = true;
  1148. }
  1149. }
  1150. /// <summary>
  1151. /// Raises the reset button click event.
  1152. /// </summary>
  1153. public void OnResetButtonClick()
  1154. {
  1155. if (isImagesInputMode)
  1156. {
  1157. if (!isCalibrating)
  1158. InitializeImagesInputMode();
  1159. }
  1160. else
  1161. {
  1162. ResetCalibration();
  1163. }
  1164. }
  1165. /// <summary>
  1166. /// Raises the save button click event.
  1167. /// </summary>
  1168. public void OnSaveButtonClick()
  1169. {
  1170. string saveDirectoryPath = Path.Combine(Application.persistentDataPath, "ArUcoCameraCalibrationExample");
  1171. if (!Directory.Exists(saveDirectoryPath))
  1172. {
  1173. Directory.CreateDirectory(saveDirectoryPath);
  1174. }
  1175. string calibratonDirectoryName = "camera_parameters" + bgrMat.width() + "x" + bgrMat.height();
  1176. string saveCalibratonFileDirectoryPath = Path.Combine(saveDirectoryPath, calibratonDirectoryName);
  1177. // Clean up old files.
  1178. if (Directory.Exists(saveCalibratonFileDirectoryPath))
  1179. {
  1180. DirectoryInfo directoryInfo = new DirectoryInfo(saveCalibratonFileDirectoryPath);
  1181. foreach (FileInfo fileInfo in directoryInfo.GetFiles())
  1182. {
  1183. if ((fileInfo.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
  1184. {
  1185. fileInfo.Attributes = FileAttributes.Normal;
  1186. }
  1187. }
  1188. if ((directoryInfo.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
  1189. {
  1190. directoryInfo.Attributes = FileAttributes.Directory;
  1191. }
  1192. directoryInfo.Delete(true);
  1193. }
  1194. Directory.CreateDirectory(saveCalibratonFileDirectoryPath);
  1195. // save the calibraton file.
  1196. string savePath = Path.Combine(saveCalibratonFileDirectoryPath, calibratonDirectoryName + ".xml");
  1197. int frameCount = (markerType == MarkerType.ChArUcoBoard) ? allCorners.Count : imagePoints.Count;
  1198. CameraParameters param = new CameraParameters(frameCount, bgrMat.width(), bgrMat.height(), calibrationFlags, camMatrix, distCoeffs, repErr);
  1199. XmlSerializer serializer = new XmlSerializer(typeof(CameraParameters));
  1200. using (var stream = new FileStream(savePath, FileMode.Create))
  1201. {
  1202. serializer.Serialize(stream, param);
  1203. }
  1204. // save the calibration images.
  1205. #if UNITY_WEBGL && !UNITY_EDITOR
  1206. string format = "jpg";
  1207. MatOfInt compressionParams = new MatOfInt(Imgcodecs.IMWRITE_JPEG_QUALITY, 100);
  1208. #else
  1209. string format = "png";
  1210. MatOfInt compressionParams = new MatOfInt(Imgcodecs.IMWRITE_PNG_COMPRESSION, 0);
  1211. #endif
  1212. for (int i = 0; i < allImgs.Count; ++i)
  1213. {
  1214. Imgcodecs.imwrite(Path.Combine(saveCalibratonFileDirectoryPath, calibratonDirectoryName + "_" + i.ToString("00") + "." + format), allImgs[i], compressionParams);
  1215. }
  1216. savePathInputField.text = savePath;
  1217. Debug.Log("Saved the CameraParameters to disk in XML file format.");
  1218. Debug.Log("savePath: " + savePath);
  1219. }
  1220. public enum MarkerType
  1221. {
  1222. ChessBoard,
  1223. CirclesGlid,
  1224. AsymmetricCirclesGlid,
  1225. ChArUcoBoard,
  1226. }
  1227. public enum NumberOfBoardSizeWidth
  1228. {
  1229. W_1 = 1,
  1230. W_2,
  1231. W_3,
  1232. W_4,
  1233. W_5,
  1234. W_6,
  1235. W_7,
  1236. W_8,
  1237. W_9,
  1238. W_10,
  1239. W_11,
  1240. W_12,
  1241. W_13,
  1242. W_14,
  1243. W_15,
  1244. }
  1245. public enum NumberOfBoardSizeHeight
  1246. {
  1247. H_1 = 1,
  1248. H_2,
  1249. H_3,
  1250. H_4,
  1251. H_5,
  1252. H_6,
  1253. H_7,
  1254. H_8,
  1255. H_9,
  1256. H_10,
  1257. H_11,
  1258. H_12,
  1259. H_13,
  1260. H_14,
  1261. H_15,
  1262. }
  1263. public enum ArUcoDictionary
  1264. {
  1265. DICT_4X4_50 = Objdetect.DICT_4X4_50,
  1266. DICT_4X4_100 = Objdetect.DICT_4X4_100,
  1267. DICT_4X4_250 = Objdetect.DICT_4X4_250,
  1268. DICT_4X4_1000 = Objdetect.DICT_4X4_1000,
  1269. DICT_5X5_50 = Objdetect.DICT_5X5_50,
  1270. DICT_5X5_100 = Objdetect.DICT_5X5_100,
  1271. DICT_5X5_250 = Objdetect.DICT_5X5_250,
  1272. DICT_5X5_1000 = Objdetect.DICT_5X5_1000,
  1273. DICT_6X6_50 = Objdetect.DICT_6X6_50,
  1274. DICT_6X6_100 = Objdetect.DICT_6X6_100,
  1275. DICT_6X6_250 = Objdetect.DICT_6X6_250,
  1276. DICT_6X6_1000 = Objdetect.DICT_6X6_1000,
  1277. DICT_7X7_50 = Objdetect.DICT_7X7_50,
  1278. DICT_7X7_100 = Objdetect.DICT_7X7_100,
  1279. DICT_7X7_250 = Objdetect.DICT_7X7_250,
  1280. DICT_7X7_1000 = Objdetect.DICT_7X7_1000,
  1281. DICT_ARUCO_ORIGINAL = Objdetect.DICT_ARUCO_ORIGINAL,
  1282. }
  1283. }
  1284. }