#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