FaceMarkExample.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. using UnityEngine;
  2. using UnityEngine.SceneManagement;
  3. using UnityEngine.UI;
  4. using System;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using OpenCVForUnity.UnityUtils.Helper;
  9. using OpenCVForUnity.UnityUtils;
  10. using OpenCVForUnity.ImgprocModule;
  11. using OpenCVForUnity.CoreModule;
  12. using OpenCVForUnity.FaceModule;
  13. using OpenCVForUnity.ObjdetectModule;
  14. using Rect = OpenCVForUnity.CoreModule.Rect;
  15. namespace OpenCVForUnityExample
  16. {
  17. /// <summary>
  18. /// FaceMark Example
  19. /// An example of detecting facial landmark in a image of WebCamTexture using the face (FaceMark API) module.
  20. /// The facemark model file can be downloaded here: https://github.com/spmallick/GSOC2017/blob/master/data/lbfmodel.yaml
  21. /// Please copy to “Assets/StreamingAssets/facemark/” folder.
  22. /// </summary>
  23. [RequireComponent (typeof(WebCamTextureToMatHelper))]
  24. public class FaceMarkExample : MonoBehaviour
  25. {
  26. /// <summary>
  27. /// The gray mat.
  28. /// </summary>
  29. Mat grayMat;
  30. /// <summary>
  31. /// The texture.
  32. /// </summary>
  33. Texture2D texture;
  34. /// <summary>
  35. /// The cascade.
  36. /// </summary>
  37. CascadeClassifier cascade;
  38. /// <summary>
  39. /// The faces.
  40. /// </summary>
  41. MatOfRect faces;
  42. /// <summary>
  43. /// The webcam texture to mat helper.
  44. /// </summary>
  45. WebCamTextureToMatHelper webCamTextureToMatHelper;
  46. /// <summary>
  47. /// The facemark.
  48. /// </summary>
  49. Facemark facemark;
  50. /// <summary>
  51. /// The FPS monitor.
  52. /// </summary>
  53. FpsMonitor fpsMonitor;
  54. string facemark_cascade_filepath;
  55. string facemark_model_filepath;
  56. #if UNITY_WEBGL && !UNITY_EDITOR
  57. IEnumerator getFilePath_Coroutine;
  58. #endif
  59. // Use this for initialization
  60. void Start ()
  61. {
  62. fpsMonitor = GetComponent<FpsMonitor> ();
  63. webCamTextureToMatHelper = gameObject.GetComponent<WebCamTextureToMatHelper> ();
  64. #if UNITY_WEBGL && !UNITY_EDITOR
  65. getFilePath_Coroutine = GetFilePath ();
  66. StartCoroutine (getFilePath_Coroutine);
  67. #else
  68. facemark_cascade_filepath = Utils.getFilePath ("lbpcascade_frontalface.xml");
  69. facemark_model_filepath = Utils.getFilePath ("facemark/lbfmodel.yaml");
  70. Run ();
  71. #endif
  72. }
  73. #if UNITY_WEBGL && !UNITY_EDITOR
  74. private IEnumerator GetFilePath ()
  75. {
  76. var getFilePathAsync_0_Coroutine = Utils.getFilePathAsync ("lbpcascade_frontalface.xml", (result) => {
  77. facemark_cascade_filepath = result;
  78. });
  79. yield return getFilePathAsync_0_Coroutine;
  80. var getFilePathAsync_1_Coroutine = Utils.getFilePathAsync ("facemark/lbfmodel.yaml", (result) => {
  81. facemark_model_filepath = result;
  82. });
  83. yield return getFilePathAsync_1_Coroutine;
  84. getFilePath_Coroutine = null;
  85. Run ();
  86. }
  87. #endif
  88. // Use this for initialization
  89. void Run ()
  90. {
  91. if (string.IsNullOrEmpty (facemark_cascade_filepath) || string.IsNullOrEmpty (facemark_model_filepath)) {
  92. Debug.LogError ("model file is not loaded. The facemark model file can be downloaded here: https://github.com/spmallick/GSOC2017/blob/master/data/lbfmodel.yaml\n Please copy to “Assets/StreamingAssets/facemark/” folder. ");
  93. }
  94. // setup landmarks detector
  95. facemark = Face.createFacemarkLBF ();
  96. facemark.loadModel (facemark_model_filepath);
  97. // setup face detection
  98. cascade = new CascadeClassifier (facemark_cascade_filepath);
  99. #if !UNITY_WSA_10_0
  100. if (cascade.empty ()) {
  101. Debug.LogError ("cascade file is not loaded. Please copy from “OpenCVForUnity/StreamingAssets/” to “Assets/StreamingAssets/” folder. ");
  102. }
  103. #endif
  104. #if UNITY_ANDROID && !UNITY_EDITOR
  105. // Avoids the front camera low light issue that occurs in only some Android devices (e.g. Google Pixel, Pixel2).
  106. webCamTextureToMatHelper.avoidAndroidFrontCameraLowLightIssue = true;
  107. #endif
  108. webCamTextureToMatHelper.Initialize ();
  109. }
  110. /// <summary>
  111. /// Raises the webcam texture to mat helper initialized event.
  112. /// </summary>
  113. public void OnWebCamTextureToMatHelperInitialized ()
  114. {
  115. Debug.Log ("OnWebCamTextureToMatHelperInitialized");
  116. Mat webCamTextureMat = webCamTextureToMatHelper.GetMat ();
  117. texture = new Texture2D (webCamTextureMat.cols (), webCamTextureMat.rows (), TextureFormat.RGBA32, false);
  118. gameObject.GetComponent<Renderer> ().material.mainTexture = texture;
  119. gameObject.transform.localScale = new Vector3 (webCamTextureMat.cols (), webCamTextureMat.rows (), 1);
  120. Debug.Log ("Screen.width " + Screen.width + " Screen.height " + Screen.height + " Screen.orientation " + Screen.orientation);
  121. if (fpsMonitor != null) {
  122. fpsMonitor.Add ("width", webCamTextureMat.width ().ToString ());
  123. fpsMonitor.Add ("height", webCamTextureMat.height ().ToString ());
  124. fpsMonitor.Add ("orientation", Screen.orientation.ToString ());
  125. }
  126. float width = webCamTextureMat.width ();
  127. float height = webCamTextureMat.height ();
  128. float widthScale = (float)Screen.width / width;
  129. float heightScale = (float)Screen.height / height;
  130. if (widthScale < heightScale) {
  131. Camera.main.orthographicSize = (width * (float)Screen.height / (float)Screen.width) / 2;
  132. } else {
  133. Camera.main.orthographicSize = height / 2;
  134. }
  135. grayMat = new Mat (webCamTextureMat.rows (), webCamTextureMat.cols (), CvType.CV_8UC1);
  136. faces = new MatOfRect ();
  137. }
  138. /// <summary>
  139. /// Raises the webcam texture to mat helper disposed event.
  140. /// </summary>
  141. public void OnWebCamTextureToMatHelperDisposed ()
  142. {
  143. Debug.Log ("OnWebCamTextureToMatHelperDisposed");
  144. if (grayMat != null)
  145. grayMat.Dispose ();
  146. if (texture != null) {
  147. Texture2D.Destroy (texture);
  148. texture = null;
  149. }
  150. if (faces != null)
  151. faces.Dispose ();
  152. }
  153. /// <summary>
  154. /// Raises the webcam texture to mat helper error occurred event.
  155. /// </summary>
  156. /// <param name="errorCode">Error code.</param>
  157. public void OnWebCamTextureToMatHelperErrorOccurred (WebCamTextureToMatHelper.ErrorCode errorCode)
  158. {
  159. Debug.Log ("OnWebCamTextureToMatHelperErrorOccurred " + errorCode);
  160. }
  161. // Update is called once per frame
  162. void Update ()
  163. {
  164. if (webCamTextureToMatHelper.IsPlaying () && webCamTextureToMatHelper.DidUpdateThisFrame ()) {
  165. Mat rgbaMat = webCamTextureToMatHelper.GetMat ();
  166. Imgproc.cvtColor (rgbaMat, grayMat, Imgproc.COLOR_RGBA2GRAY);
  167. Imgproc.equalizeHist (grayMat, grayMat);
  168. // detect faces
  169. cascade.detectMultiScale (grayMat, faces, 1.1, 2, 2, // TODO: objdetect.CV_HAAR_SCALE_IMAGE
  170. new Size (grayMat.cols () * 0.2, grayMat.rows () * 0.2), new Size ());
  171. if (faces.total () > 0) {
  172. // fit landmarks for each found face
  173. List<MatOfPoint2f> landmarks = new List<MatOfPoint2f> ();
  174. facemark.fit (grayMat, faces, landmarks);
  175. Rect[] rects = faces.toArray ();
  176. for (int i = 0; i < rects.Length; i++) {
  177. // Debug.Log ("detect faces " + rects [i]);
  178. Imgproc.rectangle (rgbaMat, new Point (rects [i].x, rects [i].y), new Point (rects [i].x + rects [i].width, rects [i].y + rects [i].height), new Scalar (255, 0, 0, 255), 2);
  179. }
  180. // draw them
  181. for (int i = 0; i < landmarks.Count; i++) {
  182. MatOfPoint2f lm = landmarks [i];
  183. float[] lm_float = new float[lm.total () * lm.channels ()];
  184. Utils.copyFromMat<float> (lm, lm_float);
  185. DrawFaceLandmark (rgbaMat, ConvertArrayToPointList (lm_float), new Scalar (0, 255, 0, 255), 2);
  186. // for (int j = 0; j < lm_float.Length; j = j + 2) {
  187. // Point p = new Point (lm_float [j], lm_float [j + 1]);
  188. // Imgproc.circle (rgbaMat, p, 2, new Scalar (255, 0, 0, 255), 1);
  189. // }
  190. }
  191. }
  192. Utils.fastMatToTexture2D (rgbaMat, texture);
  193. }
  194. }
  195. private void DrawFaceLandmark (Mat imgMat, List<Point> points, Scalar color, int thickness, bool drawIndexNumbers = false)
  196. {
  197. if (points.Count == 5) {
  198. Imgproc.line (imgMat, points [0], points [1], color, thickness);
  199. Imgproc.line (imgMat, points [1], points [4], color, thickness);
  200. Imgproc.line (imgMat, points [4], points [3], color, thickness);
  201. Imgproc.line (imgMat, points [3], points [2], color, thickness);
  202. } else if (points.Count == 68) {
  203. for (int i = 1; i <= 16; ++i)
  204. Imgproc.line (imgMat, points [i], points [i - 1], color, thickness);
  205. for (int i = 28; i <= 30; ++i)
  206. Imgproc.line (imgMat, points [i], points [i - 1], color, thickness);
  207. for (int i = 18; i <= 21; ++i)
  208. Imgproc.line (imgMat, points [i], points [i - 1], color, thickness);
  209. for (int i = 23; i <= 26; ++i)
  210. Imgproc.line (imgMat, points [i], points [i - 1], color, thickness);
  211. for (int i = 31; i <= 35; ++i)
  212. Imgproc.line (imgMat, points [i], points [i - 1], color, thickness);
  213. Imgproc.line (imgMat, points [30], points [35], color, thickness);
  214. for (int i = 37; i <= 41; ++i)
  215. Imgproc.line (imgMat, points [i], points [i - 1], color, thickness);
  216. Imgproc.line (imgMat, points [36], points [41], color, thickness);
  217. for (int i = 43; i <= 47; ++i)
  218. Imgproc.line (imgMat, points [i], points [i - 1], color, thickness);
  219. Imgproc.line (imgMat, points [42], points [47], color, thickness);
  220. for (int i = 49; i <= 59; ++i)
  221. Imgproc.line (imgMat, points [i], points [i - 1], color, thickness);
  222. Imgproc.line (imgMat, points [48], points [59], color, thickness);
  223. for (int i = 61; i <= 67; ++i)
  224. Imgproc.line (imgMat, points [i], points [i - 1], color, thickness);
  225. Imgproc.line (imgMat, points [60], points [67], color, thickness);
  226. } else {
  227. for (int i = 0; i < points.Count; i++) {
  228. Imgproc.circle (imgMat, points [i], 2, color, -1);
  229. }
  230. }
  231. // Draw the index number of facelandmark points.
  232. if (drawIndexNumbers) {
  233. for (int i = 0; i < points.Count; ++i)
  234. Imgproc.putText (imgMat, i.ToString (), points [i], Imgproc.FONT_HERSHEY_SIMPLEX, 0.5, new Scalar (255, 255, 255, 255), 1, Imgproc.LINE_AA, false);
  235. }
  236. }
  237. private List<Point> ConvertArrayToPointList (float[] arr, List<Point> pts = null)
  238. {
  239. if (pts == null) {
  240. pts = new List<Point> ();
  241. }
  242. if (pts.Count != arr.Length / 2) {
  243. pts.Clear ();
  244. for (int i = 0; i < arr.Length / 2; i++) {
  245. pts.Add (new Point ());
  246. }
  247. }
  248. for (int i = 0; i < pts.Count; ++i) {
  249. pts [i].x = arr [i * 2];
  250. pts [i].y = arr [i * 2 + 1];
  251. }
  252. return pts;
  253. }
  254. /// <summary>
  255. /// Raises the destroy event.
  256. /// </summary>
  257. void OnDestroy ()
  258. {
  259. webCamTextureToMatHelper.Dispose ();
  260. if (cascade != null)
  261. cascade.Dispose ();
  262. if (facemark != null)
  263. facemark.Dispose ();
  264. #if UNITY_WEBGL && !UNITY_EDITOR
  265. if (getFilePath_Coroutine != null) {
  266. StopCoroutine (getFilePath_Coroutine);
  267. ((IDisposable)getFilePath_Coroutine).Dispose ();
  268. }
  269. #endif
  270. }
  271. /// <summary>
  272. /// Raises the back button click event.
  273. /// </summary>
  274. public void OnBackButtonClick ()
  275. {
  276. SceneManager.LoadScene ("OpenCVForUnityExample");
  277. }
  278. /// <summary>
  279. /// Raises the play button click event.
  280. /// </summary>
  281. public void OnPlayButtonClick ()
  282. {
  283. webCamTextureToMatHelper.Play ();
  284. }
  285. /// <summary>
  286. /// Raises the pause button click event.
  287. /// </summary>
  288. public void OnPauseButtonClick ()
  289. {
  290. webCamTextureToMatHelper.Pause ();
  291. }
  292. /// <summary>
  293. /// Raises the stop button click event.
  294. /// </summary>
  295. public void OnStopButtonClick ()
  296. {
  297. webCamTextureToMatHelper.Stop ();
  298. }
  299. /// <summary>
  300. /// Raises the change camera button click event.
  301. /// </summary>
  302. public void OnChangeCameraButtonClick ()
  303. {
  304. webCamTextureToMatHelper.requestedIsFrontFacing = !webCamTextureToMatHelper.IsFrontFacing ();
  305. }
  306. }
  307. }