CamShiftExample.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. using UnityEngine;
  2. using UnityEngine.EventSystems;
  3. using UnityEngine.SceneManagement;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using OpenCVForUnity.CoreModule;
  7. using OpenCVForUnity.ImgprocModule;
  8. using OpenCVForUnity.VideoModule;
  9. using OpenCVForUnity.UnityUtils;
  10. using OpenCVForUnity.UnityUtils.Helper;
  11. namespace OpenCVForUnityExample
  12. {
  13. /// <summary>
  14. /// CamShift Example
  15. /// An example of object tracking using the Video.Camshift function.
  16. /// Referring to http://www.computervisiononline.com/blog/tutorial-using-camshift-track-objects-video.
  17. /// http://docs.opencv.org/3.2.0/db/df8/tutorial_py_meanshift.html
  18. /// </summary>
  19. [RequireComponent (typeof(WebCamTextureToMatHelper))]
  20. public class CamShiftExample : MonoBehaviour
  21. {
  22. /// <summary>
  23. /// The texture.
  24. /// </summary>
  25. Texture2D texture;
  26. /// <summary>
  27. /// The roi point list.
  28. /// </summary>
  29. List<Point> roiPointList;
  30. /// <summary>
  31. /// The roi rect.
  32. /// </summary>
  33. OpenCVForUnity.CoreModule.Rect roiRect;
  34. /// <summary>
  35. /// The hsv mat.
  36. /// </summary>
  37. Mat hsvMat;
  38. /// <summary>
  39. /// The roi hist mat.
  40. /// </summary>
  41. Mat roiHistMat;
  42. /// <summary>
  43. /// The termination.
  44. /// </summary>
  45. TermCriteria termination;
  46. /// <summary>
  47. /// The webcam texture to mat helper.
  48. /// </summary>
  49. WebCamTextureToMatHelper webCamTextureToMatHelper;
  50. /// <summary>
  51. /// The stored touch point.
  52. /// </summary>
  53. Point storedTouchPoint;
  54. /// <summary>
  55. /// The flag for requesting the start of the CamShift Method.
  56. /// </summary>
  57. bool shouldStartCamShift = false;
  58. /// <summary>
  59. /// The FPS monitor.
  60. /// </summary>
  61. FpsMonitor fpsMonitor;
  62. // Use this for initialization
  63. void Start ()
  64. {
  65. fpsMonitor = GetComponent<FpsMonitor> ();
  66. roiPointList = new List<Point> ();
  67. termination = new TermCriteria (TermCriteria.EPS | TermCriteria.COUNT, 10, 1);
  68. webCamTextureToMatHelper = gameObject.GetComponent<WebCamTextureToMatHelper> ();
  69. #if UNITY_ANDROID && !UNITY_EDITOR
  70. // Avoids the front camera low light issue that occurs in only some Android devices (e.g. Google Pixel, Pixel2).
  71. webCamTextureToMatHelper.avoidAndroidFrontCameraLowLightIssue = true;
  72. #endif
  73. webCamTextureToMatHelper.Initialize ();
  74. }
  75. /// <summary>
  76. /// Raises the webcam texture to mat helper initialized event.
  77. /// </summary>
  78. public void OnWebCamTextureToMatHelperInitialized ()
  79. {
  80. Debug.Log ("OnWebCamTextureToMatHelperInitialized");
  81. Mat webCamTextureMat = webCamTextureToMatHelper.GetMat ();
  82. texture = new Texture2D (webCamTextureMat.cols (), webCamTextureMat.rows (), TextureFormat.RGBA32, false);
  83. gameObject.GetComponent<Renderer> ().material.mainTexture = texture;
  84. gameObject.transform.localScale = new Vector3 (webCamTextureMat.cols (), webCamTextureMat.rows (), 1);
  85. Debug.Log ("Screen.width " + Screen.width + " Screen.height " + Screen.height + " Screen.orientation " + Screen.orientation);
  86. if (fpsMonitor != null) {
  87. fpsMonitor.Add ("width", webCamTextureMat.width ().ToString ());
  88. fpsMonitor.Add ("height", webCamTextureMat.height ().ToString ());
  89. fpsMonitor.Add ("orientation", Screen.orientation.ToString ());
  90. }
  91. if (fpsMonitor != null) {
  92. fpsMonitor.consoleText = "Please touch the 4 points surrounding the tracking object.";
  93. }
  94. float width = webCamTextureMat.width ();
  95. float height = webCamTextureMat.height ();
  96. float widthScale = (float)Screen.width / width;
  97. float heightScale = (float)Screen.height / height;
  98. if (widthScale < heightScale) {
  99. Camera.main.orthographicSize = (width * (float)Screen.height / (float)Screen.width) / 2;
  100. } else {
  101. Camera.main.orthographicSize = height / 2;
  102. }
  103. hsvMat = new Mat (webCamTextureMat.rows (), webCamTextureMat.cols (), CvType.CV_8UC3);
  104. }
  105. /// <summary>
  106. /// Raises the webcam texture to mat helper disposed event.
  107. /// </summary>
  108. public void OnWebCamTextureToMatHelperDisposed ()
  109. {
  110. Debug.Log ("OnWebCamTextureToMatHelperDisposed");
  111. hsvMat.Dispose ();
  112. if (roiHistMat != null)
  113. roiHistMat.Dispose ();
  114. roiPointList.Clear ();
  115. if (texture != null) {
  116. Texture2D.Destroy (texture);
  117. texture = null;
  118. }
  119. }
  120. /// <summary>
  121. /// Raises the webcam texture to mat helper error occurred event.
  122. /// </summary>
  123. /// <param name="errorCode">Error code.</param>
  124. public void OnWebCamTextureToMatHelperErrorOccurred (WebCamTextureToMatHelper.ErrorCode errorCode)
  125. {
  126. Debug.Log ("OnWebCamTextureToMatHelperErrorOccurred " + errorCode);
  127. }
  128. // Update is called once per frame
  129. void Update ()
  130. {
  131. #if ((UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR)
  132. //Touch
  133. int touchCount = Input.touchCount;
  134. if (touchCount == 1)
  135. {
  136. Touch t = Input.GetTouch(0);
  137. if(t.phase == TouchPhase.Ended && !EventSystem.current.IsPointerOverGameObject (t.fingerId)) {
  138. storedTouchPoint = new Point (t.position.x, t.position.y);
  139. //Debug.Log ("touch X " + t.position.x);
  140. //Debug.Log ("touch Y " + t.position.y);
  141. }
  142. }
  143. #else
  144. //Mouse
  145. if (Input.GetMouseButtonUp (0) && !EventSystem.current.IsPointerOverGameObject ()) {
  146. storedTouchPoint = new Point (Input.mousePosition.x, Input.mousePosition.y);
  147. //Debug.Log ("mouse X " + Input.mousePosition.x);
  148. //Debug.Log ("mouse Y " + Input.mousePosition.y);
  149. }
  150. #endif
  151. if (webCamTextureToMatHelper.IsPlaying () && webCamTextureToMatHelper.DidUpdateThisFrame ()) {
  152. Mat rgbaMat = webCamTextureToMatHelper.GetMat ();
  153. Imgproc.cvtColor (rgbaMat, hsvMat, Imgproc.COLOR_RGBA2RGB);
  154. Imgproc.cvtColor (hsvMat, hsvMat, Imgproc.COLOR_RGB2HSV);
  155. if (storedTouchPoint != null) {
  156. ConvertScreenPointToTexturePoint (storedTouchPoint, storedTouchPoint, gameObject, rgbaMat.cols (), rgbaMat.rows ());
  157. OnTouch (rgbaMat, storedTouchPoint);
  158. storedTouchPoint = null;
  159. }
  160. Point[] points = roiPointList.ToArray ();
  161. if (shouldStartCamShift) {
  162. shouldStartCamShift = false;
  163. using (MatOfPoint roiPointMat = new MatOfPoint (points)) {
  164. roiRect = Imgproc.boundingRect (roiPointMat);
  165. }
  166. if (roiHistMat != null) {
  167. roiHistMat.Dispose ();
  168. roiHistMat = null;
  169. }
  170. roiHistMat = new Mat ();
  171. using (Mat roiHSVMat = new Mat (hsvMat, roiRect))
  172. using (Mat maskMat = new Mat ()) {
  173. Imgproc.calcHist (new List<Mat> (new Mat[]{ roiHSVMat }), new MatOfInt (0), maskMat, roiHistMat, new MatOfInt (16), new MatOfFloat (0, 180));
  174. Core.normalize (roiHistMat, roiHistMat, 0, 255, Core.NORM_MINMAX);
  175. //Debug.Log ("roiHist " + roiHistMat.ToString ());
  176. }
  177. } else if (points.Length == 4) {
  178. using (Mat backProj = new Mat ()) {
  179. Imgproc.calcBackProject (new List<Mat> (new Mat[]{ hsvMat }), new MatOfInt (0), roiHistMat, backProj, new MatOfFloat (0, 180), 1.0);
  180. RotatedRect r = Video.CamShift (backProj, roiRect, termination);
  181. r.points (points);
  182. }
  183. }
  184. if (points.Length < 4) {
  185. for (int i = 0; i < points.Length; i++) {
  186. Imgproc.circle (rgbaMat, points [i], 6, new Scalar (0, 0, 255, 255), 2);
  187. }
  188. } else {
  189. for (int i = 0; i < 4; i++) {
  190. Imgproc.line (rgbaMat, points [i], points [(i + 1) % 4], new Scalar (255, 0, 0, 255), 2);
  191. }
  192. Imgproc.rectangle (rgbaMat, roiRect.tl (), roiRect.br (), new Scalar (0, 255, 0, 255), 2);
  193. }
  194. // Imgproc.putText (rgbaMat, "W:" + rgbaMat.width () + " H:" + rgbaMat.height () + " SO:" + Screen.orientation, new Point (5, rgbaMat.rows () - 10), Imgproc.FONT_HERSHEY_SIMPLEX, 1.0, new Scalar (255, 255, 255, 255), 2, Imgproc.LINE_AA, false);
  195. Utils.fastMatToTexture2D (rgbaMat, texture);
  196. }
  197. }
  198. private void OnTouch (Mat img, Point touchPoint)
  199. {
  200. if (roiPointList.Count == 4) {
  201. roiPointList.Clear ();
  202. }
  203. if (roiPointList.Count < 4) {
  204. roiPointList.Add (touchPoint);
  205. if (!(new OpenCVForUnity.CoreModule.Rect (0, 0, img.width (), img.height ()).contains (roiPointList [roiPointList.Count - 1]))) {
  206. roiPointList.RemoveAt (roiPointList.Count - 1);
  207. }
  208. if (roiPointList.Count == 4) {
  209. shouldStartCamShift = true;
  210. }
  211. }
  212. }
  213. /// <summary>
  214. /// Converts the screen point to texture point.
  215. /// </summary>
  216. /// <param name="screenPoint">Screen point.</param>
  217. /// <param name="dstPoint">Dst point.</param>
  218. /// <param name="texturQuad">Texture quad.</param>
  219. /// <param name="textureWidth">Texture width.</param>
  220. /// <param name="textureHeight">Texture height.</param>
  221. /// <param name="camera">Camera.</param>
  222. private void ConvertScreenPointToTexturePoint (Point screenPoint, Point dstPoint, GameObject textureQuad, int textureWidth = -1, int textureHeight = -1, Camera camera = null)
  223. {
  224. if (textureWidth < 0 || textureHeight < 0) {
  225. Renderer r = textureQuad.GetComponent<Renderer> ();
  226. if (r != null && r.material != null && r.material.mainTexture != null) {
  227. textureWidth = r.material.mainTexture.width;
  228. textureHeight = r.material.mainTexture.height;
  229. } else {
  230. textureWidth = (int)textureQuad.transform.localScale.x;
  231. textureHeight = (int)textureQuad.transform.localScale.y;
  232. }
  233. }
  234. if (camera == null)
  235. camera = Camera.main;
  236. Vector3 quadPosition = textureQuad.transform.localPosition;
  237. Vector3 quadScale = textureQuad.transform.localScale;
  238. Vector2 tl = camera.WorldToScreenPoint (new Vector3 (quadPosition.x - quadScale.x / 2, quadPosition.y + quadScale.y / 2, quadPosition.z));
  239. Vector2 tr = camera.WorldToScreenPoint (new Vector3 (quadPosition.x + quadScale.x / 2, quadPosition.y + quadScale.y / 2, quadPosition.z));
  240. Vector2 br = camera.WorldToScreenPoint (new Vector3 (quadPosition.x + quadScale.x / 2, quadPosition.y - quadScale.y / 2, quadPosition.z));
  241. Vector2 bl = camera.WorldToScreenPoint (new Vector3 (quadPosition.x - quadScale.x / 2, quadPosition.y - quadScale.y / 2, quadPosition.z));
  242. using (Mat srcRectMat = new Mat (4, 1, CvType.CV_32FC2))
  243. using (Mat dstRectMat = new Mat (4, 1, CvType.CV_32FC2)) {
  244. srcRectMat.put (0, 0, tl.x, tl.y, tr.x, tr.y, br.x, br.y, bl.x, bl.y);
  245. dstRectMat.put (0, 0, 0, 0, quadScale.x, 0, quadScale.x, quadScale.y, 0, quadScale.y);
  246. using (Mat perspectiveTransform = Imgproc.getPerspectiveTransform (srcRectMat, dstRectMat))
  247. using (MatOfPoint2f srcPointMat = new MatOfPoint2f (screenPoint))
  248. using (MatOfPoint2f dstPointMat = new MatOfPoint2f ()) {
  249. Core.perspectiveTransform (srcPointMat, dstPointMat, perspectiveTransform);
  250. dstPoint.x = dstPointMat.get (0, 0) [0] * textureWidth / quadScale.x;
  251. dstPoint.y = dstPointMat.get (0, 0) [1] * textureHeight / quadScale.y;
  252. }
  253. }
  254. }
  255. /// <summary>
  256. /// Raises the destroy event.
  257. /// </summary>
  258. void OnDestroy ()
  259. {
  260. webCamTextureToMatHelper.Dispose ();
  261. }
  262. /// <summary>
  263. /// Raises the back button click event.
  264. /// </summary>
  265. public void OnBackButtonClick ()
  266. {
  267. SceneManager.LoadScene ("OpenCVForUnityExample");
  268. }
  269. /// <summary>
  270. /// Raises the play button click event.
  271. /// </summary>
  272. public void OnPlayButtonClick ()
  273. {
  274. webCamTextureToMatHelper.Play ();
  275. }
  276. /// <summary>
  277. /// Raises the pause button click event.
  278. /// </summary>
  279. public void OnPauseButtonClick ()
  280. {
  281. webCamTextureToMatHelper.Pause ();
  282. }
  283. /// <summary>
  284. /// Raises the stop button click event.
  285. /// </summary>
  286. public void OnStopButtonClick ()
  287. {
  288. webCamTextureToMatHelper.Stop ();
  289. }
  290. /// <summary>
  291. /// Raises the change camera button click event.
  292. /// </summary>
  293. public void OnChangeCameraButtonClick ()
  294. {
  295. webCamTextureToMatHelper.requestedIsFrontFacing = !webCamTextureToMatHelper.IsFrontFacing ();
  296. }
  297. }
  298. }