#if ZED_OPENCV_FOR_UNITY using OpenCVForUnity.CoreModule; using OpenCVForUnity.ImgprocModule; using OpenCVForUnity.UnityUtils; using OpenCVForUnity.UtilsModule; using sl; using System.Collections.Generic; using UnityEngine; /// /// Whenever the ZED captures an image, this calls events with an openCV version of that image along with /// camera information, for the purposes of making other OpenCV calls like marker detection. /// Currently only retrieves Left RGB and Left Grayscale images, but you can add any other kinds of /// sl.VIEW images by creating a new ImageUpdatedEvent event and adding a call to DeployGrabbedEvent() in OnImageUpdated(). /// public class ZEDToOpenCVRetriever : MonoBehaviour { #region Singleton Implementation private static ZEDToOpenCVRetriever _instance; /// /// Finds the first available ZEDToOpenCVRetriever in the scene, or creates a new one (Singleton). /// Note that it's better to have the component in the Scene ahead of time, especially if using multiple ZED cameras. /// /// public static ZEDToOpenCVRetriever GetInstance() { if(!_instance) { GameObject go = new GameObject("ZED to OpenCV Retriever"); _instance = go.AddComponent(); } return _instance; } #endregion /// /// ZEDManager in the scene used to grab the image. /// Note this script isn't currently designed for multiple ZED's all detecting markers. /// [Tooltip("ZEDManager in the scene used to grab the image." + "Note this script isn't currently designed for multiple ZEDs.")] public ZEDManager zedManager; /// /// Left camera on the scene's ZED rig. Assigned with ZEDManager.zedCamera. /// private ZEDCamera zedCam; /// /// OpenCV matrix representing the projection matrix of the ZED camera. /// private Mat camMat; public delegate void ImageUpdatedEvent(Camera cam, Mat camMat, Mat imageMat); /// /// Called whenever the ZED grabs an image from the left camera, and invoked with an OpenCV Mat of the color version of that image (BGRA format). /// public event ImageUpdatedEvent OnImageUpdated_LeftBGRA; /// /// Called whenever the ZED grabs an image from the left camera, and invoked with an OpenCV Mat of the color version of that image (BGRA format). /// public event ImageUpdatedEvent OnImageUpdated_LeftBGR; /// /// Called whenever the ZED grabs an image from the left camera, and invoked with an OpenCV Mat of the color version of that image (RGBA format). /// public event ImageUpdatedEvent OnImageUpdated_LeftRGBA; /// /// Called whenever the ZED grabs an image from the left camera, and invoked with an OpenCV Mat of the color version of that image (RGB format). /// public event ImageUpdatedEvent OnImageUpdated_LeftRGB; /// /// Called whenever the ZED grabs an image from the left camera, and invoked with an OpenCV Mat of the grayscale version of that image /// public event ImageUpdatedEvent OnImageUpdated_LeftGrayscale; //Cached matrices so we don't have to create new ones each frame. private ZEDMat zedLeftBGRAMat; private ZEDMat zedLeftGrayMat; /// /// Used to store OpenCV Mat buffers we use to copy from the ZED to OpenCV, before any relevant conversions. /// Key is the int that corresponds to a CvType. (It's not an enum for some reason) /// private Dictionary openCVBufferMats = new Dictionary(); private Mat cvLeftBGRAMat; private Mat cvLeftBGRMat; private Mat cvLeftRGBAMat; private Mat cvLeftRGBMat; private Mat cvLeftGrayMat; /// /// Whether the camera has been set up yet, used for blocking calls made too early. /// private bool isInit = false; private void Awake() { _instance = this; //Singleton implementation. if (!zedManager) zedManager = FindObjectOfType(); } // Use this for initialization void Start () { //We can't call initialize() now, because it needs the ZED's projection matrix to build an OpenCV Mat of it. //So we wait until OnZEDReady is called. Then we can get the ZED's calibration parameters. zedManager.OnZEDReady += Initialize; //Each grab, we'll fill the zedMat from the ZED SDK, copy it to cvMat, and call relevant events. zedManager.OnGrab += OnZEDGrabbed; } /// /// Creates an OpenCV matrix based on the ZED camera's rectified parameters. This is specific to each camera. /// private void Initialize() { zedCam = zedManager.zedCamera; sl.CameraParameters camparams = zedCam.CalibrationParametersRectified.leftCam; //Shorthand. double fx = camparams.fx; double fy = camparams.fy; double cx = camparams.cx; double cy = camparams.cy; //Build a rough 3x3 matrix for OpenCV with the camera's parameters. camMat = new Mat(3, 3, CvType.CV_64FC1); camMat.put(0, 0, fx); camMat.put(0, 1, 0); camMat.put(0, 2, cx); camMat.put(1, 0, 0); camMat.put(1, 1, fy); camMat.put(1, 2, cy); camMat.put(2, 0, 0); camMat.put(2, 1, 0); camMat.put(2, 2, 0.0f); Size imageSize = new Size(zedCam.ImageWidth, zedCam.ImageHeight); //Make ZED and OpenCV versions of the image matrices. //Each grab, we'll fill the zedMat from the ZED SDK, copy it to cvMat, and run marker detection on it. //8U_C1(8 bits, one channel) as we're using a grayscale image for performance. //zedMat = new ZEDMat((uint)zedCam.ImageWidth, (uint)zedCam.ImageHeight, sl.ZEDMat.MAT_TYPE.MAT_8U_C1); //cvMat = SLMat2CVMat(zedMat, ZEDMat.MAT_TYPE.MAT_8U_C1); isInit = true; } /// /// Gets all images needed for any of the listeners for any of the OnImageUpdated events, converts them to an OpenCV Mat, and calls the events. /// Called from zedManager.OnGrab() whenever the ZED updates the image. /// You can extend this event to add other kinds of ZED Images, like VIEW.RIGHT, and add more events appropriately. /// private void OnZEDGrabbed() { if (!isInit) return; //We haven't set up the camera mat yet, so we can't call any of the events that need it. if(OnImageUpdated_LeftBGRA != null && OnImageUpdated_LeftBGRA.GetInvocationList().Length > 0) //Regular left color image as BGRA. { DeployGrabbedEvent(zedManager.GetLeftCamera(), ref zedLeftBGRAMat, VIEW.LEFT, ZEDMat.MAT_TYPE.MAT_8U_C4, ref cvLeftBGRAMat, OnImageUpdated_LeftBGRA); } if (OnImageUpdated_LeftBGR != null && OnImageUpdated_LeftBGR.GetInvocationList().Length > 0) //Regular left color image as BGRA. { DeployGrabbedEvent(zedManager.GetLeftCamera(), ref zedLeftBGRAMat, VIEW.LEFT, ZEDMat.MAT_TYPE.MAT_8U_C4, ref cvLeftBGRMat, OnImageUpdated_LeftBGR, OpenCVConversion.BGRA2BGR); } if (OnImageUpdated_LeftRGBA != null && OnImageUpdated_LeftRGBA.GetInvocationList().Length > 0) //Regular left color image as BGRA. { DeployGrabbedEvent(zedManager.GetLeftCamera(), ref zedLeftBGRAMat, VIEW.LEFT, ZEDMat.MAT_TYPE.MAT_8U_C4, ref cvLeftRGBAMat, OnImageUpdated_LeftRGBA, OpenCVConversion.BGRA2RGBA); } if (OnImageUpdated_LeftRGB != null && OnImageUpdated_LeftRGB.GetInvocationList().Length > 0) //Regular left color image as BGRA. { DeployGrabbedEvent(zedManager.GetLeftCamera(), ref zedLeftBGRAMat, VIEW.LEFT, ZEDMat.MAT_TYPE.MAT_8U_C4, ref cvLeftRGBMat, OnImageUpdated_LeftRGB, OpenCVConversion.BGRA2RGB); } if (OnImageUpdated_LeftGrayscale != null && OnImageUpdated_LeftGrayscale.GetInvocationList().Length > 0) //Left grayscale image. { DeployGrabbedEvent(zedManager.GetLeftCamera(), ref zedLeftGrayMat, VIEW.LEFT_GREY, ZEDMat.MAT_TYPE.MAT_8U_C1, ref cvLeftGrayMat, OnImageUpdated_LeftGrayscale); } } /// /// Copies the given ZEDMat to a given OpenCV mat, creating either or both mats if necessary, then calls an ImageUpdatedEvent with them. /// Used in OnZEDGrabbed to call different events, and to make it easy to add more kinds of images/events by just adding more calls to this method. /// /// Unity Camera object that represents the ZED camera. Usually from ZEDManager.GetLeftCamera() or ZEDManager.GetRightCamera(). /// ZEDMat used to get the ZED image. Passing an empty one is okay - it'll get filled appropriately. /// Type of image requested, like LEFT or LEFT_GRAY. /// Data type and channel of required ZEDMat. See summaries over each enum entry to know which is correct for your image type. /// OpenCV mat to copy to. Passing an empty one is okay - it'll get filled appropriately. /// Event to call if the method retrieves the image successfully. private void DeployGrabbedEvent(Camera cam, ref ZEDMat zedmat, VIEW view, ZEDMat.MAT_TYPE mattype, ref Mat cvMat, ImageUpdatedEvent updateevent, OpenCVConversion conversionatend = OpenCVConversion.NONE) { if(zedmat == null) { zedmat = new ZEDMat(); zedmat.Create(new sl.Resolution((uint)zedCam.ImageWidth, (uint)zedCam.ImageHeight), mattype); } ERROR_CODE err = zedManager.zedCamera.RetrieveImage(zedmat, view, ZEDMat.MEM.MEM_CPU, zedmat.GetResolution()); if (err == ERROR_CODE.SUCCESS) { Mat buffermat = GetOpenCVBufferMat(zedCam.ImageHeight, zedCam.ImageWidth, SLMatType2CVMatType(mattype)); //copyToMat(zedmat.GetPtr(), cvMat); Utils.copyToMat(zedmat.GetPtr(), buffermat); ConvertColorSpace(buffermat, ref cvMat, conversionatend); //Mat convertedmat = ConvertColorSpace(buffermat, conversionatend); updateevent.Invoke(cam, camMat, cvMat); } } /// /// Creates an OpenCV version of a ZEDMat. /// In this sample, we only ever use 8U_C1 (for grayscale image) but you can call it yourself /// for any ZEDMat type and get a properly-formatted OpenCV Mat. /// /// Source ZEDMat. /// Type of ZEDMat - data type and channel number. Depends on the type of image /// it represents. See summaries of each enum value to choose the type, as you can't currently /// retrieve the material type from an instantiated ZEDMat. /// OpenCV Mat formatted correctly so that you can copy data from the source ZEDMat into it. private static Mat SLMat2CVMat(sl.ZEDMat zedmat, ZEDMat.MAT_TYPE zedmattype) { int cvmattype = SLMatType2CVMatType(zedmattype); Mat cvmat = new Mat(zedmat.GetHeight(), zedmat.GetWidth(), cvmattype); return cvmat; } /// /// Returns the OpenCV type that corresponds to a given ZED Mat type. /// private static int SLMatType2CVMatType(ZEDMat.MAT_TYPE zedmattype) { switch (zedmattype) { case ZEDMat.MAT_TYPE.MAT_32F_C1: return CvType.CV_32FC1; case ZEDMat.MAT_TYPE.MAT_32F_C2: return CvType.CV_32FC2; case ZEDMat.MAT_TYPE.MAT_32F_C3: return CvType.CV_32FC3; case ZEDMat.MAT_TYPE.MAT_32F_C4: return CvType.CV_32FC4; case ZEDMat.MAT_TYPE.MAT_8U_C1: return CvType.CV_8UC1; case ZEDMat.MAT_TYPE.MAT_8U_C2: return CvType.CV_8UC2; case ZEDMat.MAT_TYPE.MAT_8U_C3: return CvType.CV_8UC3; case ZEDMat.MAT_TYPE.MAT_8U_C4: return CvType.CV_8UC4; case ZEDMat.MAT_TYPE.MAT_16U_C1: return CvType.CV_16UC1; default: return -1; } } /// /// Returns a designated buffer mat for the OpenCV mat type (Defined in the CvType class). /// If none exists, creates one. /// Important: This assumes the height and width of the image stays static, since all ZED images do. /// /// Height of the ZED image. /// Widht of the ZED image. /// Type of the OpenCV mat - see CvType.cs. /// Pre-created buffer material. private Mat GetOpenCVBufferMat(int height, int width, int cvtype) { if(!openCVBufferMats.ContainsKey(cvtype)) { openCVBufferMats.Add(cvtype, new Mat(height, width, cvtype)); } return openCVBufferMats[cvtype]; } /// /// If we want an output format that differs from the default for ZED's image, here's where we change it. /// For example, the SDK provides the left view image in BGRA format. But we may want BGR (no alpha) or RGB. /// This gets called in DeployGrabbedEvent. private void ConvertColorSpace(Mat source, ref Mat dest, OpenCVConversion converttype) { switch (converttype) { case OpenCVConversion.NONE: default: dest = source; break; case OpenCVConversion.BGRA2BGR: //Mat bgrimage = new Mat(source.rows(), source.cols(), CvType.CV_8UC3, new Scalar(0, 0, 0)); if(dest == null) dest = new Mat(source.rows(), source.cols(), CvType.CV_8UC3, new Scalar(0, 0, 0)); Imgproc.cvtColor(source, dest, Imgproc.COLOR_BGRA2BGR); break; case OpenCVConversion.BGRA2RGB: if(dest == null) dest = new Mat(source.rows(), source.cols(), CvType.CV_8UC3, new Scalar(0, 0, 0)); Imgproc.cvtColor(source, dest, Imgproc.COLOR_BGRA2RGB); break; case OpenCVConversion.BGRA2RGBA: //Mat rgbaimage = new Mat(source.rows(), source.cols(), CvType.CV_8UC4, new Scalar(0, 0, 0)); if (dest == null) dest = new Mat(source.rows(), source.cols(), CvType.CV_8UC3, new Scalar(0, 0, 0)); Imgproc.cvtColor(source, dest, Imgproc.COLOR_BGRA2RGBA); break; } } /// /// Holds what kind of color format conversion you would like to perform on an OpenCV image within DeployGrabbedEvent. /// Each item corresponds to a const within OpenCVForUnity.ImgprocModule.Imgproc.cs. /// private enum OpenCVConversion { NONE, BGRA2BGR, BGRA2RGBA, BGRA2RGB } } #endif