123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173 |
- using UnityEngine;
- using UnityEngine.UI;
- using UnityEngine.SceneManagement;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Xml.Serialization;
- using System.IO;
- using System.Linq;
- using OpenCVForUnity.ArucoModule;
- using OpenCVForUnity.CoreModule;
- using OpenCVForUnity.ImgprocModule;
- using OpenCVForUnity.Calib3dModule;
- using OpenCVForUnity.UnityUtils;
- using OpenCVForUnity.ImgcodecsModule;
- using OpenCVForUnity.UnityUtils.Helper;
- namespace OpenCVForUnityExample
- {
- /// <summary>
- /// ArUco Camera Calibration Example
- /// An example of camera calibration using the aruco module.
- /// Referring to https://github.com/opencv/opencv_contrib/blob/master/modules/aruco/samples/calibrate_camera.cpp.
- /// https://github.com/opencv/opencv/blob/master/samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp
- /// https://docs.opencv.org/3.2.0/da/d13/tutorial_aruco_calibration.html
- /// https://docs.opencv.org/3.4.0/d7/d21/tutorial_interactive_calibration.html
- /// </summary>
- [RequireComponent (typeof(WebCamTextureToMatHelper))]
- public class ArUcoCameraCalibrationExample : MonoBehaviour
- {
- /// <summary>
- /// The marker type.
- /// </summary>
- public MarkerType markerType = MarkerType.ChArUcoBoard;
- /// <summary>
- /// The marker type dropdown.
- /// </summary>
- public Dropdown markerTypeDropdown;
- /// <summary>
- /// The dictionary identifier.
- /// </summary>
- public ArUcoDictionary dictionaryId = ArUcoDictionary.DICT_6X6_250;
- /// <summary>
- /// The dictionary id dropdown.
- /// </summary>
- public Dropdown dictionaryIdDropdown;
- /// <summary>
- /// Number of squares in X direction.
- /// </summary>
- public NumberOfSquaresX squaresX = NumberOfSquaresX.X_5;
- /// <summary>
- /// The squares X dropdown.
- /// </summary>
- public Dropdown squaresXDropdown;
- /// <summary>
- /// Number of squares in X direction.
- /// </summary>
- public NumberOfSquaresY squaresY = NumberOfSquaresY.Y_7;
- /// <summary>
- /// The squares X dropdown.
- /// </summary>
- public Dropdown squaresYDropdown;
- /// <summary>
- /// The save path input field.
- /// </summary>
- public InputField savePathInputField;
- /// <summary>
- /// Determines if refine marker detection. (only valid for ArUco boards)
- /// </summary>
- public bool refineMarkerDetection = true;
- [Header ("Extra Option")]
- /// <summary>
- /// Determines if calibrates camera using the list of calibration images.
- /// </summary>
- [TooltipAttribute ("Determines if calibrates camera using the list of calibration images.")]
- public bool isImagesInputMode = false;
- /// <summary>
- /// The calibration images directory path.
- /// Set a relative directory path from the starting point of the "StreamingAssets" folder. e.g. "calibration_images/".
- /// </summary>
- [TooltipAttribute ("Set a relative directory path from the starting point of the \"StreamingAssets\" folder. e.g. \"calibration_images\"")]
- public string calibrationImagesDirectory = "calibration_images";
- /// <summary>
- /// The texture.
- /// </summary>
- Texture2D texture;
-
- /// <summary>
- /// The webcam texture to mat helper.
- /// </summary>
- WebCamTextureToMatHelper webCamTextureToMatHelper;
- /// <summary>
- /// The gray mat.
- /// </summary>
- Mat grayMat;
- /// <summary>
- /// The bgr mat.
- /// </summary>
- Mat bgrMat;
- /// <summary>
- /// The rgba mat.
- /// </summary>
- Mat rgbaMat;
-
- /// <summary>
- /// The cameraparam matrix.
- /// </summary>
- Mat camMatrix;
-
- /// <summary>
- /// The distortion coeffs.
- /// </summary>
- MatOfDouble distCoeffs;
- /// <summary>
- /// The identifiers.
- /// </summary>
- Mat ids;
-
- /// <summary>
- /// The corners.
- /// </summary>
- List<Mat> corners;
-
- /// <summary>
- /// The rejected corners.
- /// </summary>
- List<Mat> rejectedCorners;
-
- /// <summary>
- /// The rvecs.
- /// </summary>
- List<Mat> rvecs;
-
- /// <summary>
- /// The tvecs.
- /// </summary>
- List<Mat> tvecs;
-
- /// <summary>
- /// The detector parameters.
- /// </summary>
- DetectorParameters detectorParams;
-
- /// <summary>
- /// The dictionary.
- /// </summary>
- Dictionary dictionary;
- /// <summary>
- /// The recovered identifiers.
- /// </summary>
- Mat recoveredIdxs;
- const int calibrationFlags = 0;
- // Calib3d.CALIB_FIX_K3 | Calib3d.CALIB_FIX_K4 | Calib3d.CALIB_FIX_K5
- double repErr = 0;
- bool shouldCaptureFrame = false;
- // for ChArUcoBoard.
- // chessboard square side length (normally in meters)
- const float chArUcoBoradSquareLength = 0.04f;
- // marker side length (same unit than squareLength)
- const float chArUcoBoradMarkerLength = 0.02f;
- const int charucoMinMarkers = 2;
- Mat charucoCorners;
- Mat charucoIds;
- CharucoBoard charucoBoard;
- List<List<Mat>> allCorners;
- List<Mat> allIds;
- List<Mat> allImgs;
- // for OthearMarkers.
- // square size in some user-defined units (1 by default)
- const float squareSize = 1f;
- List<Mat> imagePoints;
- bool isInitialized = false;
- bool isCalibrating = false;
- // Use this for initialization
- IEnumerator Start ()
- {
- webCamTextureToMatHelper = gameObject.GetComponent<WebCamTextureToMatHelper> ();
- // fix the screen orientation.
- Screen.orientation = ScreenOrientation.LandscapeLeft;
- // wait for the screen orientation to change.
- yield return null;
- markerTypeDropdown.value = (int)markerType;
- dictionaryIdDropdown.value = (int)dictionaryId;
- squaresXDropdown.value = (int)squaresX - 1;
- squaresYDropdown.value = (int)squaresY - 1;
- dictionaryIdDropdown.interactable = (markerType == MarkerType.ChArUcoBoard);
- #if UNITY_WEBGL && !UNITY_EDITOR
- isImagesInputMode = false;
- #endif
- if (isImagesInputMode) {
- isImagesInputMode = InitializeImagesInputMode ();
- }
- if (!isImagesInputMode) {
- #if UNITY_ANDROID && !UNITY_EDITOR
- // Avoids the front camera low light issue that occurs in only some Android devices (e.g. Google Pixel, Pixel2).
- webCamTextureToMatHelper.avoidAndroidFrontCameraLowLightIssue = true;
- #endif
- webCamTextureToMatHelper.Initialize ();
- }
- }
- /// <summary>
- /// Raises the webcam texture to mat helper initialized event.
- /// </summary>
- public void OnWebCamTextureToMatHelperInitialized ()
- {
- Debug.Log ("OnWebCamTextureToMatHelperInitialized");
-
- Mat webCamTextureMat = webCamTextureToMatHelper.GetMat ();
-
- InitializeCalibraton (webCamTextureMat);
- // if WebCamera is frontFaceing, flip Mat.
- if (webCamTextureToMatHelper.GetWebCamDevice ().isFrontFacing) {
- webCamTextureToMatHelper.flipHorizontal = true;
- }
- }
- /// <summary>
- /// Raises the webcam texture to mat helper disposed event.
- /// </summary>
- public void OnWebCamTextureToMatHelperDisposed ()
- {
- Debug.Log ("OnWebCamTextureToMatHelperDisposed");
- DisposeCalibraton ();
- }
- /// <summary>
- /// Raises the webcam texture to mat helper error occurred event.
- /// </summary>
- /// <param name="errorCode">Error code.</param>
- public void OnWebCamTextureToMatHelperErrorOccurred (WebCamTextureToMatHelper.ErrorCode errorCode)
- {
- Debug.Log ("OnWebCamTextureToMatHelperErrorOccurred " + errorCode);
- }
-
- // Update is called once per frame
- void Update ()
- {
- if (isImagesInputMode)
- return;
- if (webCamTextureToMatHelper.IsPlaying () && webCamTextureToMatHelper.DidUpdateThisFrame ()) {
- Mat rgbaMat = webCamTextureToMatHelper.GetMat ();
- Imgproc.cvtColor (rgbaMat, grayMat, Imgproc.COLOR_RGBA2GRAY);
- if (shouldCaptureFrame) {
- shouldCaptureFrame = false;
- Mat frameMat = grayMat.clone ();
- double e = CaptureFrame (frameMat);
- if (e > 0)
- repErr = e;
- }
- DrawFrame (grayMat, bgrMat);
- Imgproc.cvtColor (bgrMat, rgbaMat, Imgproc.COLOR_BGR2RGBA);
- Utils.fastMatToTexture2D (rgbaMat, texture);
- }
- }
- private void InitializeCalibraton (Mat frameMat)
- {
- texture = new Texture2D (frameMat.cols (), frameMat.rows (), TextureFormat.RGBA32, false);
- gameObject.GetComponent<Renderer> ().material.mainTexture = texture;
- gameObject.transform.localScale = new Vector3 (frameMat.cols (), frameMat.rows (), 1);
- Debug.Log ("Screen.width " + Screen.width + " Screen.height " + Screen.height + " Screen.orientation " + Screen.orientation);
- float width = frameMat.width ();
- float height = frameMat.height ();
- float imageSizeScale = 1.0f;
- float widthScale = (float)Screen.width / width;
- float heightScale = (float)Screen.height / height;
- if (widthScale < heightScale) {
- Camera.main.orthographicSize = (width * (float)Screen.height / (float)Screen.width) / 2;
- imageSizeScale = (float)Screen.height / (float)Screen.width;
- } else {
- Camera.main.orthographicSize = height / 2;
- }
- // set cameraparam.
- camMatrix = CreateCameraMatrix (width, height);
- Debug.Log ("camMatrix " + camMatrix.dump ());
- distCoeffs = new MatOfDouble (0, 0, 0, 0, 0);
- Debug.Log ("distCoeffs " + distCoeffs.dump ());
- // calibration camera.
- Size imageSize = new Size (width * imageSizeScale, height * imageSizeScale);
- double apertureWidth = 0;
- double apertureHeight = 0;
- double[] fovx = new double[1];
- double[] fovy = new double[1];
- double[] focalLength = new double[1];
- Point principalPoint = new Point (0, 0);
- double[] aspectratio = new double[1];
- Calib3d.calibrationMatrixValues (camMatrix, imageSize, apertureWidth, apertureHeight, fovx, fovy, focalLength, principalPoint, aspectratio);
- Debug.Log ("imageSize " + imageSize.ToString ());
- Debug.Log ("apertureWidth " + apertureWidth);
- Debug.Log ("apertureHeight " + apertureHeight);
- Debug.Log ("fovx " + fovx [0]);
- Debug.Log ("fovy " + fovy [0]);
- Debug.Log ("focalLength " + focalLength [0]);
- Debug.Log ("principalPoint " + principalPoint.ToString ());
- Debug.Log ("aspectratio " + aspectratio [0]);
- grayMat = new Mat (frameMat.rows (), frameMat.cols (), CvType.CV_8UC1);
- bgrMat = new Mat (frameMat.rows (), frameMat.cols (), CvType.CV_8UC3);
- rgbaMat = new Mat (frameMat.rows (), frameMat.cols (), CvType.CV_8UC4);
- ids = new Mat ();
- corners = new List<Mat> ();
- rejectedCorners = new List<Mat> ();
- rvecs = new List<Mat> ();
- tvecs = new List<Mat> ();
- detectorParams = DetectorParameters.create ();
- detectorParams.set_cornerRefinementMethod (1);// do cornerSubPix() of OpenCV.
- dictionary = Aruco.getPredefinedDictionary ((int)dictionaryId);
- recoveredIdxs = new Mat ();
- charucoCorners = new Mat ();
- charucoIds = new Mat ();
- charucoBoard = CharucoBoard.create ((int)squaresX, (int)squaresY, chArUcoBoradSquareLength, chArUcoBoradMarkerLength, dictionary);
- allCorners = new List<List<Mat>> ();
- allIds = new List<Mat> ();
- allImgs = new List<Mat> ();
- imagePoints = new List<Mat> ();
- isInitialized = true;
- }
- private void DisposeCalibraton ()
- {
- ResetCalibration ();
- if (grayMat != null)
- grayMat.Dispose ();
- if (bgrMat != null)
- bgrMat.Dispose ();
- if (rgbaMat != null)
- rgbaMat.Dispose ();
-
- if (texture != null) {
- Texture2D.Destroy (texture);
- texture = null;
- }
- if (ids != null)
- ids.Dispose ();
- foreach (var item in corners) {
- item.Dispose ();
- }
- corners.Clear ();
- foreach (var item in rejectedCorners) {
- item.Dispose ();
- }
- rejectedCorners.Clear ();
- foreach (var item in rvecs) {
- item.Dispose ();
- }
- rvecs.Clear ();
- foreach (var item in tvecs) {
- item.Dispose ();
- }
- tvecs.Clear ();
- if (recoveredIdxs != null)
- recoveredIdxs.Dispose ();
- if (charucoCorners != null)
- charucoCorners.Dispose ();
- if (charucoIds != null)
- charucoIds.Dispose ();
- if (charucoBoard != null)
- charucoBoard.Dispose ();
- isInitialized = false;
- }
- private void DrawFrame (Mat grayMat, Mat bgrMat)
- {
- Imgproc.cvtColor (grayMat, bgrMat, Imgproc.COLOR_GRAY2BGR);
- switch (markerType) {
- default:
- case MarkerType.ChArUcoBoard:
- // detect markers.
- Aruco.detectMarkers (grayMat, dictionary, corners, ids, detectorParams, rejectedCorners, camMatrix, distCoeffs);
- // refine marker detection.
- if (refineMarkerDetection) {
- Aruco.refineDetectedMarkers (grayMat, charucoBoard, corners, ids, rejectedCorners, camMatrix, distCoeffs, 10f, 3f, true, recoveredIdxs, detectorParams);
- }
- // if at least one marker detected
- if (ids.total () > 0) {
- Aruco.interpolateCornersCharuco (corners, ids, grayMat, charucoBoard, charucoCorners, charucoIds, camMatrix, distCoeffs, charucoMinMarkers);
- // draw markers.
- Aruco.drawDetectedMarkers (bgrMat, corners, ids, new Scalar (0, 255, 0, 255));
- // if at least one charuco corner detected
- if (charucoIds.total () > 0) {
- Aruco.drawDetectedCornersCharuco (bgrMat, charucoCorners, charucoIds, new Scalar (0, 0, 255, 255));
- }
- }
- break;
- case MarkerType.ChessBoard:
- case MarkerType.CirclesGlid:
- case MarkerType.AsymmetricCirclesGlid:
- // detect markers.
- MatOfPoint2f points = new MatOfPoint2f ();
- bool found = false;
- switch (markerType) {
- default:
- case MarkerType.ChessBoard:
- found = Calib3d.findChessboardCorners (grayMat, new Size ((int)squaresX, (int)squaresY), points, Calib3d.CALIB_CB_ADAPTIVE_THRESH | Calib3d.CALIB_CB_FAST_CHECK | Calib3d.CALIB_CB_NORMALIZE_IMAGE);
- break;
- case MarkerType.CirclesGlid:
- found = Calib3d.findCirclesGrid (grayMat, new Size ((int)squaresX, (int)squaresY), points, Calib3d.CALIB_CB_SYMMETRIC_GRID);
- break;
- case MarkerType.AsymmetricCirclesGlid:
- found = Calib3d.findCirclesGrid (grayMat, new Size ((int)squaresX, (int)squaresY), points, Calib3d.CALIB_CB_ASYMMETRIC_GRID);
- break;
- }
- if (found) {
- if (markerType == MarkerType.ChessBoard)
- Imgproc.cornerSubPix (grayMat, points, new Size (5, 5), new Size (-1, -1), new TermCriteria (TermCriteria.EPS + TermCriteria.COUNT, 30, 0.1));
- // draw markers.
- Calib3d.drawChessboardCorners (bgrMat, new Size ((int)squaresX, (int)squaresY), points, found);
- }
- break;
- }
- double[] camMatrixArr = new double[(int)camMatrix.total ()];
- camMatrix.get (0, 0, camMatrixArr);
- double[] distCoeffsArr = new double[(int)distCoeffs.total ()];
- distCoeffs.get (0, 0, distCoeffsArr);
- int ff = Imgproc.FONT_HERSHEY_SIMPLEX;
- double fs = 0.4;
- Scalar c = new Scalar (255, 255, 255, 255);
- int t = 0;
- int lt = Imgproc.LINE_AA;
- bool blo = false;
- int frameCount = (markerType == MarkerType.ChArUcoBoard) ? allCorners.Count : imagePoints.Count;
- Imgproc.putText (bgrMat, frameCount + " FRAME CAPTURED", new Point (bgrMat.cols () - 300, 20), ff, fs, c, t, lt, blo);
- Imgproc.putText (bgrMat, "IMAGE_WIDTH: " + bgrMat.width (), new Point (bgrMat.cols () - 300, 40), ff, fs, c, t, lt, blo);
- Imgproc.putText (bgrMat, "IMAGE_HEIGHT: " + bgrMat.height (), new Point (bgrMat.cols () - 300, 60), ff, fs, c, t, lt, blo);
- Imgproc.putText (bgrMat, "CALIBRATION_FLAGS: " + calibrationFlags, new Point (bgrMat.cols () - 300, 80), ff, fs, c, t, lt, blo);
- Imgproc.putText (bgrMat, "CAMERA_MATRIX: ", new Point (bgrMat.cols () - 300, 100), ff, fs, c, t, lt, blo);
- for (int i = 0; i < camMatrixArr.Length; i = i + 3) {
- Imgproc.putText (bgrMat, " " + camMatrixArr [i] + ", " + camMatrixArr [i + 1] + ", " + camMatrixArr [i + 2] + ",", new Point (bgrMat.cols () - 300, 120 + 20 * i / 3), ff, fs, c, t, lt, blo);
- }
- Imgproc.putText (bgrMat, "DISTORTION_COEFFICIENTS: ", new Point (bgrMat.cols () - 300, 180), ff, fs, c, t, lt, blo);
- for (int i = 0; i < distCoeffsArr.Length; ++i) {
- Imgproc.putText (bgrMat, " " + distCoeffsArr [i] + ",", new Point (bgrMat.cols () - 300, 200 + 20 * i), ff, fs, c, t, lt, blo);
- }
- Imgproc.putText (bgrMat, "AVG_REPROJECTION_ERROR: " + repErr, new Point (bgrMat.cols () - 300, 300), ff, fs, c, t, lt, blo);
- if (frameCount == 0)
- 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);
- }
- private double CaptureFrame (Mat frameMat)
- {
- double repErr = -1;
- switch (markerType) {
- default:
- case MarkerType.ChArUcoBoard:
- List<Mat> corners = new List<Mat> ();
- Mat ids = new Mat ();
- Aruco.detectMarkers (frameMat, dictionary, corners, ids, detectorParams, rejectedCorners, camMatrix, distCoeffs);
- if (refineMarkerDetection) {
- Aruco.refineDetectedMarkers (frameMat, charucoBoard, corners, ids, rejectedCorners, camMatrix, distCoeffs, 10f, 3f, true, recoveredIdxs, detectorParams);
- }
- if (ids.total () > 0) {
- Debug.Log ("Frame captured.");
- allCorners.Add (corners);
- allIds.Add (ids);
- allImgs.Add (frameMat);
- } else {
- Debug.Log ("Invalid frame.");
- frameMat.Dispose ();
- if (ids != null)
- ids.Dispose ();
- foreach (var item in corners) {
- item.Dispose ();
- }
- corners.Clear ();
- return -1;
- }
- // calibrate camera using aruco markers
- //double arucoRepErr = CalibrateCameraAruco (allCorners, allIds, charucoBoard, frameMat.size(), camMatrix, distCoeffs, rvecs, tvecs, calibrationFlags);
- //Debug.Log ("arucoRepErr: " + arucoRepErr);
- // calibrate camera using charuco
- repErr = CalibrateCameraCharuco (allCorners, allIds, charucoBoard, frameMat.size (), camMatrix, distCoeffs, rvecs, tvecs, calibrationFlags, calibrationFlags);
- break;
- case MarkerType.ChessBoard:
- case MarkerType.CirclesGlid:
- case MarkerType.AsymmetricCirclesGlid:
-
- MatOfPoint2f points = new MatOfPoint2f ();
- Size patternSize = new Size ((int)squaresX, (int)squaresY);
- bool found = false;
- switch (markerType) {
- default:
- case MarkerType.ChessBoard:
- found = Calib3d.findChessboardCorners (frameMat, patternSize, points, Calib3d.CALIB_CB_ADAPTIVE_THRESH | Calib3d.CALIB_CB_FAST_CHECK | Calib3d.CALIB_CB_NORMALIZE_IMAGE);
- break;
- case MarkerType.CirclesGlid:
- found = Calib3d.findCirclesGrid (frameMat, patternSize, points, Calib3d.CALIB_CB_SYMMETRIC_GRID);
- break;
- case MarkerType.AsymmetricCirclesGlid:
- found = Calib3d.findCirclesGrid (frameMat, patternSize, points, Calib3d.CALIB_CB_ASYMMETRIC_GRID);
- break;
- }
- if (found) {
- Debug.Log ("Frame captured.");
- if (markerType == MarkerType.ChessBoard)
- Imgproc.cornerSubPix (frameMat, points, new Size (5, 5), new Size (-1, -1), new TermCriteria (TermCriteria.EPS + TermCriteria.COUNT, 30, 0.1));
-
- imagePoints.Add (points);
- allImgs.Add (frameMat);
- } else {
- Debug.Log ("Invalid frame.");
- frameMat.Dispose ();
- if (points != null)
- points.Dispose ();
- return -1;
- }
-
- if (imagePoints.Count < 1) {
- Debug.Log ("Not enough points for calibration.");
- repErr = -1;
- } else {
- MatOfPoint3f objectPoint = new MatOfPoint3f (new Mat (imagePoints [0].rows (), 1, CvType.CV_32FC3));
- CalcChessboardCorners (patternSize, squareSize, objectPoint, markerType);
- List<Mat> objectPoints = new List<Mat> ();
- for (int i = 0; i < imagePoints.Count; ++i) {
- objectPoints.Add (objectPoint);
- }
- repErr = Calib3d.calibrateCamera (objectPoints, imagePoints, frameMat.size (), camMatrix, distCoeffs, rvecs, tvecs, calibrationFlags);
- objectPoint.Dispose ();
- }
- break;
- }
- Debug.Log ("repErr: " + repErr);
- Debug.Log ("camMatrix: " + camMatrix.dump ());
- Debug.Log ("distCoeffs: " + distCoeffs.dump ());
-
- return repErr;
- }
- private double CalibrateCameraAruco (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)
- {
- // prepare data for calibration
- int nFrames = allCorners.Count;
- int allLen = 0;
- int[] markerCounterPerFrameArr = new int[allCorners.Count];
- for (int i = 0; i < nFrames; ++i) {
- markerCounterPerFrameArr [i] = allCorners [i].Count;
- allLen += allCorners [i].Count;
- }
- int[] allIdsConcatenatedArr = new int[allLen];
- int index = 0;
- for (int j = 0; j < allIds.Count; ++j) {
- int[] idsArr = new int[(int)allIds [j].total ()];
- allIds [j].get (0, 0, idsArr);
- for (int k = 0; k < idsArr.Length; ++k) {
- allIdsConcatenatedArr [index + k] = (int)idsArr [k];
- }
- index += idsArr.Length;
- }
- using (Mat allIdsConcatenated = new Mat (1, allLen, CvType.CV_32SC1))
- using (Mat markerCounterPerFrame = new Mat (1, nFrames, CvType.CV_32SC1)) {
- List<Mat> allCornersConcatenated = new List<Mat> ();
- foreach (var c in allCorners) {
- foreach (var m in c) {
- allCornersConcatenated.Add (m);
- }
- }
- allIdsConcatenated.put (0, 0, allIdsConcatenatedArr);
- markerCounterPerFrame.put (0, 0, markerCounterPerFrameArr);
- if (rvecs == null)
- rvecs = new List<Mat> ();
- if (tvecs == null)
- tvecs = new List<Mat> ();
- return Aruco.calibrateCameraAruco (allCornersConcatenated, allIdsConcatenated, markerCounterPerFrame, board, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, calibrationFlags);
- }
- }
- 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)
- {
- // prepare data for charuco calibration
- int nFrames = allCorners.Count;
- List<Mat> allCharucoCorners = new List<Mat> ();
- List<Mat> allCharucoIds = new List<Mat> ();
- List<Mat> filteredImages = new List<Mat> ();
- for (int i = 0; i < nFrames; ++i) {
- // interpolate using camera parameters
- Mat currentCharucoCorners = new Mat ();
- Mat currentCharucoIds = new Mat ();
- Aruco.interpolateCornersCharuco (allCorners [i], allIds [i], allImgs [i], board, currentCharucoCorners, currentCharucoIds, cameraMatrix, distCoeffs, minMarkers);
- if (charucoIds.total () > 0) {
- allCharucoCorners.Add (currentCharucoCorners);
- allCharucoIds.Add (currentCharucoIds);
- filteredImages.Add (allImgs [i]);
- } else {
- currentCharucoCorners.Dispose ();
- currentCharucoIds.Dispose ();
- }
- }
- if (allCharucoCorners.Count < 1) {
- Debug.Log ("Not enough corners for calibration.");
- return -1;
- }
- if (rvecs == null)
- rvecs = new List<Mat> ();
- if (tvecs == null)
- tvecs = new List<Mat> ();
- return Aruco.calibrateCameraCharuco (allCharucoCorners, allCharucoIds, board, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, calibrationFlags);
- }
- private void ResetCalibration ()
- {
- foreach (var corners in allCorners) {
- foreach (var item in corners) {
- item.Dispose ();
- }
- }
- allCorners.Clear ();
- foreach (var item in allIds) {
- item.Dispose ();
- }
- allIds.Clear ();
- foreach (var item in allImgs) {
- item.Dispose ();
- }
- allImgs.Clear ();
- repErr = 0;
- camMatrix = CreateCameraMatrix (bgrMat.width (), bgrMat.height ());
- distCoeffs = new MatOfDouble (0, 0, 0, 0, 0);
- foreach (var item in imagePoints) {
- item.Dispose ();
- }
- imagePoints.Clear ();
- }
- private Mat CreateCameraMatrix (float width, float height)
- {
- int max_d = (int)Mathf.Max (width, height);
- double fx = max_d;
- double fy = max_d;
- double cx = width / 2.0f;
- double cy = height / 2.0f;
- Mat camMatrix = new Mat (3, 3, CvType.CV_64FC1);
- camMatrix.put (0, 0, fx);
- camMatrix.put (0, 1, 0);
- camMatrix.put (0, 2, cx);
- camMatrix.put (1, 0, 0);
- camMatrix.put (1, 1, fy);
- camMatrix.put (1, 2, cy);
- camMatrix.put (2, 0, 0);
- camMatrix.put (2, 1, 0);
- camMatrix.put (2, 2, 1.0f);
- return camMatrix;
- }
- private void CalcChessboardCorners (Size patternSize, float squareSize, MatOfPoint3f corners, MarkerType markerType)
- {
- if ((int)(patternSize.width * patternSize.height) != corners.rows ()) {
- Debug.Log ("Invalid corners size.");
- corners.create ((int)(patternSize.width * patternSize.height), 1, CvType.CV_32FC3);
- }
- const int cn = 3;
- float[] cornersArr = new float[corners.rows () * cn];
- int width = (int)patternSize.width;
- int height = (int)patternSize.height;
- switch (markerType) {
- default:
- case MarkerType.ChessBoard:
- case MarkerType.CirclesGlid:
- for (int i = 0; i < height; ++i) {
- for (int j = 0; j < width; ++j) {
- cornersArr [(i * width * cn) + (j * cn)] = j * squareSize;
- cornersArr [(i * width * cn) + (j * cn) + 1] = i * squareSize;
- cornersArr [(i * width * cn) + (j * cn) + 2] = 0;
- }
- }
- corners.put (0, 0, cornersArr);
- break;
- case MarkerType.AsymmetricCirclesGlid:
- for (int i = 0; i < height; ++i) {
- for (int j = 0; j < width; ++j) {
- cornersArr [(i * width * cn) + (j * cn)] = (2 * j + i % 2) * squareSize;
- cornersArr [(i * width * cn) + (j * cn) + 1] = i * squareSize;
- cornersArr [(i * width * cn) + (j * cn) + 2] = 0;
- }
- }
- corners.put (0, 0, cornersArr);
- break;
- }
- }
- private bool InitializeImagesInputMode ()
- {
- if (isInitialized)
- DisposeCalibraton ();
- if (String.IsNullOrEmpty (calibrationImagesDirectory)) {
- Debug.LogWarning ("When using the images input mode, please set a calibration images directory path.");
- return false;
- }
- string dirPath = Path.Combine (Application.streamingAssetsPath, calibrationImagesDirectory);
- if (!Directory.Exists (dirPath)) {
- Debug.LogWarning ("The directory does not exist.");
- return false;
- }
- string[] imageFiles = GetImageFilesInDirectory (dirPath);
- if (imageFiles.Length < 1) {
- Debug.LogWarning ("The image file does not exist.");
- return false;
- }
-
- Uri rootPath = new Uri (Application.streamingAssetsPath + System.IO.Path.AltDirectorySeparatorChar);
- Uri fullPath = new Uri (imageFiles [0]);
- string relativePath = rootPath.MakeRelativeUri (fullPath).ToString ();
- using (Mat gray = Imgcodecs.imread (Utils.getFilePath (relativePath), Imgcodecs.IMREAD_GRAYSCALE)) {
- if (gray.total () == 0) {
- Debug.LogWarning ("Invalid image file.");
- return false;
- }
- using (Mat bgr = new Mat (gray.size (), CvType.CV_8UC3))
- using (Mat bgra = new Mat (gray.size (), CvType.CV_8UC4)) {
- InitializeCalibraton (gray);
- DrawFrame (gray, bgr);
- Imgproc.cvtColor (bgr, bgra, Imgproc.COLOR_BGR2RGBA);
- Utils.fastMatToTexture2D (bgra, texture);
- }
- }
- return true;
- }
- private IEnumerator CalibrateCameraUsingImages ()
- {
- string dirPath = Path.Combine (Application.streamingAssetsPath, calibrationImagesDirectory);
- string[] imageFiles = GetImageFilesInDirectory (dirPath);
- if (imageFiles.Length < 1)
- yield break;
- isCalibrating = true;
- markerTypeDropdown.interactable = dictionaryIdDropdown.interactable = squaresXDropdown.interactable = squaresYDropdown.interactable = false;
- Uri rootPath = new Uri (Application.streamingAssetsPath + System.IO.Path.AltDirectorySeparatorChar);
- foreach (var path in imageFiles) {
- Uri fullPath = new Uri (path);
- string relativePath = rootPath.MakeRelativeUri (fullPath).ToString ();
- using (Mat gray = Imgcodecs.imread (Utils.getFilePath (relativePath), Imgcodecs.IMREAD_GRAYSCALE)) {
- if (gray.width () != bgrMat.width () || gray.height () != bgrMat.height ())
- continue;
- Mat frameMat = gray.clone ();
- double e = CaptureFrame (frameMat);
- if (e > 0)
- repErr = e;
- DrawFrame (gray, bgrMat);
- Imgproc.cvtColor (bgrMat, rgbaMat, Imgproc.COLOR_BGR2RGBA);
- Utils.matToTexture2D (rgbaMat, texture);
- }
- yield return new WaitForSeconds (0.5f);
- }
- isCalibrating = false;
- markerTypeDropdown.interactable = dictionaryIdDropdown.interactable = squaresXDropdown.interactable = squaresYDropdown.interactable = true;
- }
- private string[] GetImageFilesInDirectory (string dirPath)
- {
- if (Directory.Exists (dirPath)) {
- string[] files = Directory.GetFiles (dirPath, "*.jpg");
- files = files.Concat (Directory.GetFiles (dirPath, "*.jpeg")).ToArray ();
- files = files.Concat (Directory.GetFiles (dirPath, "*.png")).ToArray ();
- files = files.Concat (Directory.GetFiles (dirPath, "*.tiff")).ToArray ();
- files = files.Concat (Directory.GetFiles (dirPath, "*.tif")).ToArray ();
- return files;
- }
- return new string[0];
- }
- /// <summary>
- /// Raises the destroy event.
- /// </summary>
- void OnDestroy ()
- {
- if (isImagesInputMode) {
- DisposeCalibraton ();
- } else {
- webCamTextureToMatHelper.Dispose ();
- }
-
- Screen.orientation = ScreenOrientation.AutoRotation;
- }
- /// <summary>
- /// Raises the back button click event.
- /// </summary>
- public void OnBackButtonClick ()
- {
- SceneManager.LoadScene ("OpenCVForUnityExample");
- }
- /// <summary>
- /// Raises the play button click event.
- /// </summary>
- public void OnPlayButtonClick ()
- {
- if (isImagesInputMode)
- return;
- webCamTextureToMatHelper.Play ();
- }
- /// <summary>
- /// Raises the pause button click event.
- /// </summary>
- public void OnPauseButtonClick ()
- {
- if (isImagesInputMode)
- return;
- webCamTextureToMatHelper.Pause ();
- }
- /// <summary>
- /// Raises the stop button click event.
- /// </summary>
- public void OnStopButtonClick ()
- {
- if (isImagesInputMode)
- return;
- webCamTextureToMatHelper.Stop ();
- }
- /// <summary>
- /// Raises the change camera button click event.
- /// </summary>
- public void OnChangeCameraButtonClick ()
- {
- if (isImagesInputMode)
- return;
- webCamTextureToMatHelper.requestedIsFrontFacing = !webCamTextureToMatHelper.IsFrontFacing ();
- }
- /// <summary>
- /// Raises the marker type dropdown value changed event.
- /// </summary>
- public void OnMarkerTypeDropdownValueChanged (int result)
- {
- if ((int)markerType != result) {
- markerType = (MarkerType)result;
- dictionaryIdDropdown.interactable = (markerType == MarkerType.ChArUcoBoard);
- if (isImagesInputMode) {
- InitializeImagesInputMode ();
- } else {
- if (webCamTextureToMatHelper.IsInitialized ())
- webCamTextureToMatHelper.Initialize ();
- }
- }
- }
- /// <summary>
- /// Raises the dictionary id dropdown value changed event.
- /// </summary>
- public void OnDictionaryIdDropdownValueChanged (int result)
- {
- if ((int)dictionaryId != result) {
- dictionaryId = (ArUcoDictionary)result;
- dictionary = Aruco.getPredefinedDictionary ((int)dictionaryId);
- if (isImagesInputMode) {
- InitializeImagesInputMode ();
- } else {
- if (webCamTextureToMatHelper.IsInitialized ())
- webCamTextureToMatHelper.Initialize ();
- }
- }
- }
- /// <summary>
- /// Raises the squares X dropdown value changed event.
- /// </summary>
- public void OnSquaresXDropdownValueChanged (int result)
- {
- if ((int)squaresX != result + 1) {
- squaresX = (NumberOfSquaresX)(result + 1);
- if (isImagesInputMode) {
- InitializeImagesInputMode ();
- } else {
- if (webCamTextureToMatHelper.IsInitialized ())
- webCamTextureToMatHelper.Initialize ();
- }
- }
- }
- /// <summary>
- /// Raises the squares Y dropdown value changed event.
- /// </summary>
- public void OnSquaresYDropdownValueChanged (int result)
- {
- if ((int)squaresY != result + 1) {
- squaresY = (NumberOfSquaresY)(result + 1);
- if (isImagesInputMode) {
- InitializeImagesInputMode ();
- } else {
- if (webCamTextureToMatHelper.IsInitialized ())
- webCamTextureToMatHelper.Initialize ();
- }
- }
- }
- /// <summary>
- /// Raises the capture button click event.
- /// </summary>
- public void OnCaptureButtonClick ()
- {
- if (isImagesInputMode) {
- if (!isCalibrating)
- InitializeImagesInputMode ();
- StartCoroutine ("CalibrateCameraUsingImages");
- } else {
- shouldCaptureFrame = true;
- }
- }
- /// <summary>
- /// Raises the reset button click event.
- /// </summary>
- public void OnResetButtonClick ()
- {
- if (isImagesInputMode) {
- if (!isCalibrating)
- InitializeImagesInputMode ();
- } else {
- ResetCalibration ();
- }
- }
- /// <summary>
- /// Raises the save button click event.
- /// </summary>
- public void OnSaveButtonClick ()
- {
- string saveDirectoryPath = Path.Combine (Application.persistentDataPath, "ArUcoCameraCalibrationExample");
- if (!Directory.Exists (saveDirectoryPath)) {
- Directory.CreateDirectory (saveDirectoryPath);
- }
- string calibratonDirectoryName = "camera_parameters" + bgrMat.width () + "x" + bgrMat.height ();
- string saveCalibratonFileDirectoryPath = Path.Combine (saveDirectoryPath, calibratonDirectoryName);
- // Clean up old files.
- if (Directory.Exists (saveCalibratonFileDirectoryPath)) {
- DirectoryInfo directoryInfo = new DirectoryInfo (saveCalibratonFileDirectoryPath);
- foreach (FileInfo fileInfo in directoryInfo.GetFiles()) {
- if ((fileInfo.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) {
- fileInfo.Attributes = FileAttributes.Normal;
- }
- }
- if ((directoryInfo.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) {
- directoryInfo.Attributes = FileAttributes.Directory;
- }
- directoryInfo.Delete (true);
- }
- Directory.CreateDirectory (saveCalibratonFileDirectoryPath);
- // save the calibraton file.
- string savePath = Path.Combine (saveCalibratonFileDirectoryPath, calibratonDirectoryName + ".xml");
- int frameCount = (markerType == (int)MarkerType.ChArUcoBoard) ? allCorners.Count : imagePoints.Count;
- CameraParameters param = new CameraParameters (frameCount, bgrMat.width (), bgrMat.height (), calibrationFlags, camMatrix, distCoeffs, repErr);
- XmlSerializer serializer = new XmlSerializer (typeof(CameraParameters));
- using (var stream = new FileStream (savePath, FileMode.Create)) {
- serializer.Serialize (stream, param);
- }
- // save the calibration images.
- #if UNITY_WEBGL && !UNITY_EDITOR
- string format = "jpg";
- MatOfInt compressionParams = new MatOfInt(Imgcodecs.IMWRITE_JPEG_QUALITY, 100);
- #else
- string format = "png";
- MatOfInt compressionParams = new MatOfInt (Imgcodecs.IMWRITE_PNG_COMPRESSION, 0);
- #endif
- for (int i = 0; i < allImgs.Count; ++i) {
- Imgcodecs.imwrite (Path.Combine (saveCalibratonFileDirectoryPath, calibratonDirectoryName + "_" + i.ToString ("00") + "." + format), allImgs [i], compressionParams);
- }
- savePathInputField.text = savePath;
- Debug.Log ("Saved the CameraParameters to disk in XML file format.");
- Debug.Log ("savePath: " + savePath);
- }
- public enum MarkerType
- {
- ChArUcoBoard,
- ChessBoard,
- CirclesGlid,
- AsymmetricCirclesGlid
- }
- public enum ArUcoDictionary
- {
- DICT_4X4_50 = Aruco.DICT_4X4_50,
- DICT_4X4_100 = Aruco.DICT_4X4_100,
- DICT_4X4_250 = Aruco.DICT_4X4_250,
- DICT_4X4_1000 = Aruco.DICT_4X4_1000,
- DICT_5X5_50 = Aruco.DICT_5X5_50,
- DICT_5X5_100 = Aruco.DICT_5X5_100,
- DICT_5X5_250 = Aruco.DICT_5X5_250,
- DICT_5X5_1000 = Aruco.DICT_5X5_1000,
- DICT_6X6_50 = Aruco.DICT_6X6_50,
- DICT_6X6_100 = Aruco.DICT_6X6_100,
- DICT_6X6_250 = Aruco.DICT_6X6_250,
- DICT_6X6_1000 = Aruco.DICT_6X6_1000,
- DICT_7X7_50 = Aruco.DICT_7X7_50,
- DICT_7X7_100 = Aruco.DICT_7X7_100,
- DICT_7X7_250 = Aruco.DICT_7X7_250,
- DICT_7X7_1000 = Aruco.DICT_7X7_1000,
- DICT_ARUCO_ORIGINAL = Aruco.DICT_ARUCO_ORIGINAL,
- }
- public enum NumberOfSquaresX
- {
- X_1 = 1,
- X_2,
- X_3,
- X_4,
- X_5,
- X_6,
- X_7,
- X_8,
- X_9,
- X_10,
- X_11,
- X_12,
- X_13,
- X_14,
- X_15,
- }
- public enum NumberOfSquaresY
- {
- Y_1 = 1,
- Y_2,
- Y_3,
- Y_4,
- Y_5,
- Y_6,
- Y_7,
- Y_8,
- Y_9,
- Y_10,
- Y_11,
- Y_12,
- Y_13,
- Y_14,
- Y_15,
- }
- }
- }
|