ZEDToOpenCVRetriever.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. #if ZED_OPENCV_FOR_UNITY
  2. using OpenCVForUnity.CoreModule;
  3. using OpenCVForUnity.ImgprocModule;
  4. using OpenCVForUnity.UnityUtils;
  5. using OpenCVForUnity.UtilsModule;
  6. using sl;
  7. using System.Collections.Generic;
  8. using UnityEngine;
  9. /// <summary>
  10. /// Whenever the ZED captures an image, this calls events with an openCV version of that image along with
  11. /// camera information, for the purposes of making other OpenCV calls like marker detection.
  12. /// <para>Currently only retrieves Left RGB and Left Grayscale images, but you can add any other kinds of
  13. /// sl.VIEW images by creating a new ImageUpdatedEvent event and adding a call to DeployGrabbedEvent() in OnImageUpdated().
  14. ///</para></summary>
  15. public class ZEDToOpenCVRetriever : MonoBehaviour
  16. {
  17. #region Singleton Implementation
  18. private static ZEDToOpenCVRetriever _instance;
  19. /// <summary>
  20. /// Finds the first available ZEDToOpenCVRetriever in the scene, or creates a new one (Singleton).
  21. /// Note that it's better to have the component in the Scene ahead of time, especially if using multiple ZED cameras.
  22. /// </summary>
  23. /// <returns></returns>
  24. public static ZEDToOpenCVRetriever GetInstance()
  25. {
  26. if(!_instance)
  27. {
  28. GameObject go = new GameObject("ZED to OpenCV Retriever");
  29. _instance = go.AddComponent<ZEDToOpenCVRetriever>();
  30. }
  31. return _instance;
  32. }
  33. #endregion
  34. /// <summary>
  35. /// ZEDManager in the scene used to grab the image.
  36. /// Note this script isn't currently designed for multiple ZED's all detecting markers.
  37. /// </summary>
  38. [Tooltip("ZEDManager in the scene used to grab the image." +
  39. "Note this script isn't currently designed for multiple ZEDs.")]
  40. public ZEDManager zedManager;
  41. /// <summary>
  42. /// Left camera on the scene's ZED rig. Assigned with ZEDManager.zedCamera.
  43. /// </summary>
  44. private ZEDCamera zedCam;
  45. /// <summary>
  46. /// OpenCV matrix representing the projection matrix of the ZED camera.
  47. /// </summary>
  48. private Mat camMat;
  49. public delegate void ImageUpdatedEvent(Camera cam, Mat camMat, Mat imageMat);
  50. /// <summary>
  51. /// 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).
  52. /// </summary>
  53. public event ImageUpdatedEvent OnImageUpdated_LeftBGRA;
  54. /// <summary>
  55. /// 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).
  56. /// </summary>
  57. public event ImageUpdatedEvent OnImageUpdated_LeftBGR;
  58. /// <summary>
  59. /// 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).
  60. /// </summary>
  61. public event ImageUpdatedEvent OnImageUpdated_LeftRGBA;
  62. /// <summary>
  63. /// 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).
  64. /// </summary>
  65. public event ImageUpdatedEvent OnImageUpdated_LeftRGB;
  66. /// <summary>
  67. /// Called whenever the ZED grabs an image from the left camera, and invoked with an OpenCV Mat of the grayscale version of that image
  68. /// </summary>
  69. public event ImageUpdatedEvent OnImageUpdated_LeftGrayscale;
  70. //Cached matrices so we don't have to create new ones each frame.
  71. private ZEDMat zedLeftBGRAMat;
  72. private ZEDMat zedLeftGrayMat;
  73. /// <summary>
  74. /// Used to store OpenCV Mat buffers we use to copy from the ZED to OpenCV, before any relevant conversions.
  75. /// Key is the int that corresponds to a CvType. (It's not an enum for some reason)
  76. /// </summary>
  77. private Dictionary<int, Mat> openCVBufferMats = new Dictionary<int, Mat>();
  78. private Mat cvLeftBGRAMat;
  79. private Mat cvLeftBGRMat;
  80. private Mat cvLeftRGBAMat;
  81. private Mat cvLeftRGBMat;
  82. private Mat cvLeftGrayMat;
  83. /// <summary>
  84. /// Whether the camera has been set up yet, used for blocking calls made too early.
  85. /// </summary>
  86. private bool isInit = false;
  87. private void Awake()
  88. {
  89. _instance = this; //Singleton implementation.
  90. if (!zedManager) zedManager = FindObjectOfType<ZEDManager>();
  91. }
  92. // Use this for initialization
  93. void Start ()
  94. {
  95. //We can't call initialize() now, because it needs the ZED's projection matrix to build an OpenCV Mat of it.
  96. //So we wait until OnZEDReady is called. Then we can get the ZED's calibration parameters.
  97. zedManager.OnZEDReady += Initialize;
  98. //Each grab, we'll fill the zedMat from the ZED SDK, copy it to cvMat, and call relevant events.
  99. zedManager.OnGrab += OnZEDGrabbed;
  100. }
  101. /// <summary>
  102. /// Creates an OpenCV matrix based on the ZED camera's rectified parameters. This is specific to each camera.
  103. /// </summary>
  104. private void Initialize()
  105. {
  106. zedCam = zedManager.zedCamera;
  107. sl.CameraParameters camparams = zedCam.CalibrationParametersRectified.leftCam;
  108. //Shorthand.
  109. double fx = camparams.fx;
  110. double fy = camparams.fy;
  111. double cx = camparams.cx;
  112. double cy = camparams.cy;
  113. //Build a rough 3x3 matrix for OpenCV with the camera's parameters.
  114. camMat = new Mat(3, 3, CvType.CV_64FC1);
  115. camMat.put(0, 0, fx);
  116. camMat.put(0, 1, 0);
  117. camMat.put(0, 2, cx);
  118. camMat.put(1, 0, 0);
  119. camMat.put(1, 1, fy);
  120. camMat.put(1, 2, cy);
  121. camMat.put(2, 0, 0);
  122. camMat.put(2, 1, 0);
  123. camMat.put(2, 2, 0.0f);
  124. Size imageSize = new Size(zedCam.ImageWidth, zedCam.ImageHeight);
  125. //Make ZED and OpenCV versions of the image matrices.
  126. //Each grab, we'll fill the zedMat from the ZED SDK, copy it to cvMat, and run marker detection on it.
  127. //8U_C1(8 bits, one channel) as we're using a grayscale image for performance.
  128. //zedMat = new ZEDMat((uint)zedCam.ImageWidth, (uint)zedCam.ImageHeight, sl.ZEDMat.MAT_TYPE.MAT_8U_C1);
  129. //cvMat = SLMat2CVMat(zedMat, ZEDMat.MAT_TYPE.MAT_8U_C1);
  130. isInit = true;
  131. }
  132. /// <summary>
  133. /// 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.
  134. /// Called from zedManager.OnGrab() whenever the ZED updates the image.
  135. /// You can extend this event to add other kinds of ZED Images, like VIEW.RIGHT, and add more events appropriately.
  136. /// </summary>
  137. private void OnZEDGrabbed()
  138. {
  139. if (!isInit) return; //We haven't set up the camera mat yet, so we can't call any of the events that need it.
  140. if(OnImageUpdated_LeftBGRA != null && OnImageUpdated_LeftBGRA.GetInvocationList().Length > 0) //Regular left color image as BGRA.
  141. {
  142. DeployGrabbedEvent(zedManager.GetLeftCamera(), ref zedLeftBGRAMat, VIEW.LEFT, ZEDMat.MAT_TYPE.MAT_8U_C4, ref cvLeftBGRAMat, OnImageUpdated_LeftBGRA);
  143. }
  144. if (OnImageUpdated_LeftBGR != null && OnImageUpdated_LeftBGR.GetInvocationList().Length > 0) //Regular left color image as BGRA.
  145. {
  146. DeployGrabbedEvent(zedManager.GetLeftCamera(), ref zedLeftBGRAMat, VIEW.LEFT, ZEDMat.MAT_TYPE.MAT_8U_C4, ref cvLeftBGRMat, OnImageUpdated_LeftBGR, OpenCVConversion.BGRA2BGR);
  147. }
  148. if (OnImageUpdated_LeftRGBA != null && OnImageUpdated_LeftRGBA.GetInvocationList().Length > 0) //Regular left color image as BGRA.
  149. {
  150. DeployGrabbedEvent(zedManager.GetLeftCamera(), ref zedLeftBGRAMat, VIEW.LEFT, ZEDMat.MAT_TYPE.MAT_8U_C4, ref cvLeftRGBAMat, OnImageUpdated_LeftRGBA, OpenCVConversion.BGRA2RGBA);
  151. }
  152. if (OnImageUpdated_LeftRGB != null && OnImageUpdated_LeftRGB.GetInvocationList().Length > 0) //Regular left color image as BGRA.
  153. {
  154. DeployGrabbedEvent(zedManager.GetLeftCamera(), ref zedLeftBGRAMat, VIEW.LEFT, ZEDMat.MAT_TYPE.MAT_8U_C4, ref cvLeftRGBMat, OnImageUpdated_LeftRGB, OpenCVConversion.BGRA2RGB);
  155. }
  156. if (OnImageUpdated_LeftGrayscale != null && OnImageUpdated_LeftGrayscale.GetInvocationList().Length > 0) //Left grayscale image.
  157. {
  158. DeployGrabbedEvent(zedManager.GetLeftCamera(), ref zedLeftGrayMat, VIEW.LEFT_GREY, ZEDMat.MAT_TYPE.MAT_8U_C1, ref cvLeftGrayMat, OnImageUpdated_LeftGrayscale);
  159. }
  160. }
  161. /// <summary>
  162. /// Copies the given ZEDMat to a given OpenCV mat, creating either or both mats if necessary, then calls an ImageUpdatedEvent with them.
  163. /// 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.
  164. /// </summary>
  165. /// <param name="cam">Unity Camera object that represents the ZED camera. Usually from ZEDManager.GetLeftCamera() or ZEDManager.GetRightCamera().</param>
  166. /// <param name="zedmat">ZEDMat used to get the ZED image. Passing an empty one is okay - it'll get filled appropriately.</param>
  167. /// <param name="view">Type of image requested, like LEFT or LEFT_GRAY.</param>
  168. /// <param name="mattype">Data type and channel of required ZEDMat. See summaries over each enum entry to know which is correct for your image type.</param>
  169. /// <param name="cvMat">OpenCV mat to copy to. Passing an empty one is okay - it'll get filled appropriately.</param>
  170. /// <param name="updateevent">Event to call if the method retrieves the image successfully.</param>
  171. private void DeployGrabbedEvent(Camera cam, ref ZEDMat zedmat, VIEW view, ZEDMat.MAT_TYPE mattype, ref Mat cvMat, ImageUpdatedEvent updateevent,
  172. OpenCVConversion conversionatend = OpenCVConversion.NONE)
  173. {
  174. if(zedmat == null)
  175. {
  176. zedmat = new ZEDMat();
  177. zedmat.Create(new sl.Resolution((uint)zedCam.ImageWidth, (uint)zedCam.ImageHeight), mattype);
  178. }
  179. ERROR_CODE err = zedManager.zedCamera.RetrieveImage(zedmat, view, ZEDMat.MEM.MEM_CPU, zedmat.GetResolution());
  180. if (err == ERROR_CODE.SUCCESS)
  181. {
  182. Mat buffermat = GetOpenCVBufferMat(zedCam.ImageHeight, zedCam.ImageWidth, SLMatType2CVMatType(mattype));
  183. //copyToMat(zedmat.GetPtr(), cvMat);
  184. Utils.copyToMat(zedmat.GetPtr(), buffermat);
  185. ConvertColorSpace(buffermat, ref cvMat, conversionatend);
  186. //Mat convertedmat = ConvertColorSpace(buffermat, conversionatend);
  187. updateevent.Invoke(cam, camMat, cvMat);
  188. }
  189. }
  190. /// <summary>
  191. /// Creates an OpenCV version of a ZEDMat.
  192. /// In this sample, we only ever use 8U_C1 (for grayscale image) but you can call it yourself
  193. /// for any ZEDMat type and get a properly-formatted OpenCV Mat.
  194. /// </summary>
  195. /// <param name="zedmat">Source ZEDMat.</param>
  196. /// <param name="zedmattype">Type of ZEDMat - data type and channel number. Depends on the type of image
  197. /// it represents. See summaries of each enum value to choose the type, as you can't currently
  198. /// retrieve the material type from an instantiated ZEDMat.</param>
  199. /// <returns>OpenCV Mat formatted correctly so that you can copy data from the source ZEDMat into it.</returns>
  200. private static Mat SLMat2CVMat(sl.ZEDMat zedmat, ZEDMat.MAT_TYPE zedmattype)
  201. {
  202. int cvmattype = SLMatType2CVMatType(zedmattype);
  203. Mat cvmat = new Mat(zedmat.GetHeight(), zedmat.GetWidth(), cvmattype);
  204. return cvmat;
  205. }
  206. /// <summary>
  207. /// Returns the OpenCV type that corresponds to a given ZED Mat type.
  208. /// </summary>
  209. private static int SLMatType2CVMatType(ZEDMat.MAT_TYPE zedmattype)
  210. {
  211. switch (zedmattype)
  212. {
  213. case ZEDMat.MAT_TYPE.MAT_32F_C1:
  214. return CvType.CV_32FC1;
  215. case ZEDMat.MAT_TYPE.MAT_32F_C2:
  216. return CvType.CV_32FC2;
  217. case ZEDMat.MAT_TYPE.MAT_32F_C3:
  218. return CvType.CV_32FC3;
  219. case ZEDMat.MAT_TYPE.MAT_32F_C4:
  220. return CvType.CV_32FC4;
  221. case ZEDMat.MAT_TYPE.MAT_8U_C1:
  222. return CvType.CV_8UC1;
  223. case ZEDMat.MAT_TYPE.MAT_8U_C2:
  224. return CvType.CV_8UC2;
  225. case ZEDMat.MAT_TYPE.MAT_8U_C3:
  226. return CvType.CV_8UC3;
  227. case ZEDMat.MAT_TYPE.MAT_8U_C4:
  228. return CvType.CV_8UC4;
  229. case ZEDMat.MAT_TYPE.MAT_16U_C1:
  230. return CvType.CV_16UC1;
  231. default:
  232. return -1;
  233. }
  234. }
  235. /// <summary>
  236. /// Returns a designated buffer mat for the OpenCV mat type (Defined in the CvType class).
  237. /// If none exists, creates one.
  238. /// Important: This assumes the height and width of the image stays static, since all ZED images do.
  239. /// </summary>
  240. /// <param name="height">Height of the ZED image.</param>
  241. /// <param name="width">Widht of the ZED image.</param>
  242. /// <param name="cvtype">Type of the OpenCV mat - see CvType.cs.</param>
  243. /// <returns>Pre-created buffer material.</returns>
  244. private Mat GetOpenCVBufferMat(int height, int width, int cvtype)
  245. {
  246. if(!openCVBufferMats.ContainsKey(cvtype))
  247. {
  248. openCVBufferMats.Add(cvtype, new Mat(height, width, cvtype));
  249. }
  250. return openCVBufferMats[cvtype];
  251. }
  252. /// <summary>
  253. /// If we want an output format that differs from the default for ZED's image, here's where we change it.
  254. /// For example, the SDK provides the left view image in BGRA format. But we may want BGR (no alpha) or RGB.
  255. /// This gets called in DeployGrabbedEvent.
  256. private void ConvertColorSpace(Mat source, ref Mat dest, OpenCVConversion converttype)
  257. {
  258. switch (converttype)
  259. {
  260. case OpenCVConversion.NONE:
  261. default:
  262. dest = source;
  263. break;
  264. case OpenCVConversion.BGRA2BGR:
  265. //Mat bgrimage = new Mat(source.rows(), source.cols(), CvType.CV_8UC3, new Scalar(0, 0, 0));
  266. if(dest == null) dest = new Mat(source.rows(), source.cols(), CvType.CV_8UC3, new Scalar(0, 0, 0));
  267. Imgproc.cvtColor(source, dest, Imgproc.COLOR_BGRA2BGR);
  268. break;
  269. case OpenCVConversion.BGRA2RGB:
  270. if(dest == null) dest = new Mat(source.rows(), source.cols(), CvType.CV_8UC3, new Scalar(0, 0, 0));
  271. Imgproc.cvtColor(source, dest, Imgproc.COLOR_BGRA2RGB);
  272. break;
  273. case OpenCVConversion.BGRA2RGBA:
  274. //Mat rgbaimage = new Mat(source.rows(), source.cols(), CvType.CV_8UC4, new Scalar(0, 0, 0));
  275. if (dest == null) dest = new Mat(source.rows(), source.cols(), CvType.CV_8UC3, new Scalar(0, 0, 0));
  276. Imgproc.cvtColor(source, dest, Imgproc.COLOR_BGRA2RGBA);
  277. break;
  278. }
  279. }
  280. /// <summary>
  281. /// Holds what kind of color format conversion you would like to perform on an OpenCV image within DeployGrabbedEvent.
  282. /// Each item corresponds to a const within OpenCVForUnity.ImgprocModule.Imgproc.cs.
  283. /// </summary>
  284. private enum OpenCVConversion
  285. {
  286. NONE,
  287. BGRA2BGR,
  288. BGRA2RGBA,
  289. BGRA2RGB
  290. }
  291. }
  292. #endif