[Multimedia] Added feature tags and fixed errors in doc-comments of MediaVision.
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.Vision / MediaVision / FaceRecognizer.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 using System;
18 using System.Runtime.InteropServices;
19 using System.Threading.Tasks;
20 using InteropFace = Interop.MediaVision.Face;
21
22 namespace Tizen.Multimedia.Vision
23 {
24     /// <summary>
25     /// Provides the ability to recognize faces, face expressions, and eye condition on image sources.
26     /// </summary>
27     /// <since_tizen> 3 </since_tizen>
28     public static class FaceRecognizer
29     {
30
31         /// <summary>
32         /// Performs face recognition on the source with <see cref="FaceRecognitionModel"/>.
33         /// </summary>
34         /// <param name="source">The <see cref="MediaVisionSource"/> of the media to recognize faces for.</param>
35         /// <param name="recognitionModel">The <see cref="FaceRecognitionConfiguration"/> to be used for recognition.</param>
36         /// <returns>A task that represents the asynchronous recognition operation.</returns>
37         /// <exception cref="ArgumentNullException">
38         ///     <paramref name="source"/> is null.<br/>
39         ///     -or-<br/>
40         ///     <paramref name="recognitionModel"/> is null.
41         /// </exception>
42         /// <exception cref="NotSupportedException">The feature is not supported.</exception>
43         /// <exception cref="ObjectDisposedException"><paramref name="source"/> has already been disposed of.</exception>
44         /// <exception cref="InvalidOperationException"><paramref name="recognitionModel"/> is untrained model.</exception>
45         /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
46         /// <since_tizen> 3 </since_tizen>
47         public static async Task<FaceRecognitionResult> RecognizeAsync(MediaVisionSource source,
48             FaceRecognitionModel recognitionModel)
49         {
50             return await InvokeRecognizeAsync(source, recognitionModel, null, null);
51         }
52
53         /// <summary>
54         /// Performs face recognition on the source with <see cref="FaceRecognitionModel"/> and a bounding box.
55         /// </summary>
56         /// <param name="source">The <see cref="MediaVisionSource"/> of the media to recognize faces for.</param>
57         /// <param name="recognitionModel">The <see cref="FaceRecognitionModel"/> to be used for recognition.</param>
58         /// <param name="bound">Rectangular box bounding face image on the source.</param>
59         /// <returns>A task that represents the asynchronous recognition operation.</returns>
60         /// <exception cref="ArgumentNullException">
61         ///     <paramref name="source"/> is null.<br/>
62         ///     -or-<br/>
63         ///     <paramref name="recognitionModel"/> is null.
64         /// </exception>
65         /// <exception cref="NotSupportedException">The feature is not supported.</exception>
66         /// <exception cref="ObjectDisposedException"><paramref name="source"/> has already been disposed of.</exception>
67         /// <exception cref="InvalidOperationException"><paramref name="recognitionModel"/> is untrained model.</exception>
68         /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
69         /// <since_tizen> 3 </since_tizen>
70         public static async Task<FaceRecognitionResult> RecognizeAsync(MediaVisionSource source,
71             FaceRecognitionModel recognitionModel, Rectangle bound)
72         {
73             return await InvokeRecognizeAsync(source, recognitionModel, bound, null);
74         }
75
76         /// <summary>
77         /// Performs face recognition on the source with <see cref="FaceRecognitionModel"/> and <see cref="FaceRecognitionConfiguration"/>.
78         /// </summary>
79         /// <param name="source">The <see cref="MediaVisionSource"/> of the media to recognize faces for.</param>
80         /// <param name="recognitionModel">The <see cref="FaceRecognitionModel"/> to be used for recognition.</param>
81         /// <param name="config">The configuration used for recognition. This value can be null.</param>
82         /// <returns>A task that represents the asynchronous recognition operation.</returns>
83         /// <exception cref="ArgumentNullException">
84         ///     <paramref name="source"/> is null.<br/>
85         ///     -or-<br/>
86         ///     <paramref name="recognitionModel"/> is null.
87         /// </exception>
88         /// <exception cref="NotSupportedException">The feature is not supported.</exception>
89         /// <exception cref="ObjectDisposedException">
90         ///     <paramref name="source"/> has already been disposed of.<br/>
91         ///     -or-<br/>
92         ///     <paramref name="config"/> has already been disposed of.
93         /// </exception>
94         /// <exception cref="InvalidOperationException"><paramref name="recognitionModel"/> is untrained model.</exception>
95         /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
96         /// <since_tizen> 3 </since_tizen>
97         public static async Task<FaceRecognitionResult> RecognizeAsync(MediaVisionSource source,
98             FaceRecognitionModel recognitionModel, FaceRecognitionConfiguration config)
99         {
100             return await InvokeRecognizeAsync(source, recognitionModel, null, config);
101         }
102
103         /// <summary>
104         /// Performs face recognition on the source with <see cref="FaceRecognitionModel"/>, <see cref="FaceRecognitionConfiguration"/>
105         /// and a bounding box.
106         /// </summary>
107         /// <param name="source">The <see cref="MediaVisionSource"/> of the media to recognize faces for.</param>
108         /// <param name="recognitionModel">The <see cref="FaceRecognitionModel"/> to be used for recognition.</param>
109         /// <param name="bound">Rectangular box bounding face image on the source.</param>
110         /// <param name="config">The <see cref="FaceRecognitionConfiguration"/> used for recognition. This value can be null.</param>
111         /// <returns>A task that represents the asynchronous recognition operation.</returns>
112         /// <exception cref="ArgumentNullException">
113         ///     <paramref name="source"/> is null.<br/>
114         ///     -or-<br/>
115         ///     <paramref name="recognitionModel"/> is null.
116         /// </exception>
117         /// <exception cref="NotSupportedException">The feature is not supported.</exception>
118         /// <exception cref="ObjectDisposedException">
119         ///     <paramref name="source"/> has already been disposed of.<br/>
120         ///     -or-<br/>
121         ///     <paramref name="config"/> has already been disposed of.
122         /// </exception>
123         /// <exception cref="InvalidOperationException"><paramref name="recognitionModel"/> is untrained model.</exception>
124         /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
125         /// <since_tizen> 3 </since_tizen>
126         public static async Task<FaceRecognitionResult> RecognizeAsync(MediaVisionSource source,
127             FaceRecognitionModel recognitionModel, Rectangle bound, FaceRecognitionConfiguration config)
128         {
129             return await InvokeRecognizeAsync(source, recognitionModel, bound, config);
130         }
131
132         private static MediaVisionError InvokeRecognize(IntPtr sourceHandle, IntPtr modelHandle,
133             IntPtr configHandle, InteropFace.RecognizedCallback cb, Rectangle? area)
134         {
135             if (area == null)
136             {
137                 return InteropFace.Recognize(sourceHandle, modelHandle, configHandle, IntPtr.Zero, cb);
138             }
139
140             var rect = area.Value.ToMarshalable();
141             return InteropFace.Recognize(sourceHandle, modelHandle, configHandle, ref rect, cb);
142         }
143
144         private static async Task<FaceRecognitionResult> InvokeRecognizeAsync(MediaVisionSource source,
145             FaceRecognitionModel recognitionModel, Rectangle? area,
146             FaceRecognitionConfiguration config)
147         {
148             if (source == null)
149             {
150                 throw new ArgumentNullException(nameof(source));
151             }
152             if (recognitionModel == null)
153             {
154                 throw new ArgumentNullException(nameof(recognitionModel));
155             }
156
157             TaskCompletionSource<FaceRecognitionResult> tcs = new TaskCompletionSource<FaceRecognitionResult>();
158
159             using (var cb = ObjectKeeper.Get(GetRecognizedCallback(tcs)))
160             {
161                 InvokeRecognize(source.Handle, recognitionModel.Handle, EngineConfiguration.GetHandle(config),
162                     cb.Target, area).Validate("Failed to perform face recognition.");
163
164                 return await tcs.Task;
165             }
166         }
167
168         private static FaceRecognitionResult CreateRecognitionResult(
169              IntPtr faceLocationPtr, IntPtr faceLabelPtr, double confidence)
170         {
171             int faceLabel = 0;
172             if (faceLabelPtr != IntPtr.Zero)
173             {
174                 faceLabel = Marshal.ReadInt32(faceLabelPtr);
175             }
176
177             Rectangle? faceLocation = null;
178             if (faceLocationPtr != IntPtr.Zero)
179             {
180                 var area = Marshal.PtrToStructure<global::Interop.MediaVision.Rectangle>(faceLocationPtr);
181                 faceLocation = area.ToApiStruct();
182             }
183
184             return new FaceRecognitionResult(faceLabelPtr != IntPtr.Zero, confidence, faceLabel, faceLocation);
185         }
186
187         private static InteropFace.RecognizedCallback GetRecognizedCallback(
188             TaskCompletionSource<FaceRecognitionResult> tcs)
189         {
190             return (IntPtr sourceHandle, IntPtr recognitionModelHandle,
191                 IntPtr engineCfgHandle, IntPtr faceLocationPtr, IntPtr faceLabelPtr, double confidence, IntPtr _) =>
192             {
193                 try
194                 {
195                     if (!tcs.TrySetResult(CreateRecognitionResult(faceLocationPtr, faceLabelPtr, confidence)))
196                     {
197                         Log.Error(MediaVisionLog.Tag, "Failed to set result");
198                     }
199                 }
200                 catch (Exception e)
201                 {
202                     MultimediaLog.Error(MediaVisionLog.Tag, "Setting recognition result failed.", e);
203                     tcs.TrySetException(e);
204                 }
205             };
206         }
207
208         /// <summary>
209         /// Determines eye-blink condition on media source.
210         /// </summary>
211         /// <param name="source">The source of the media to recognize eye-blink condition for.</param>
212         /// <param name="bound">The bounding the face at the source.</param>
213         /// <returns>A task that represents the asynchronous recognition operation.</returns>
214         /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
215         /// <exception cref="ObjectDisposedException"><paramref name="source"/> has already been disposed of.</exception>
216         /// <exception cref="NotSupportedException">The feature is not supported.</exception>
217         /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
218         /// <since_tizen> 3 </since_tizen>
219         public static async Task<EyeCondition> RecognizeEyeConditionAsync(MediaVisionSource source,
220             Rectangle bound)
221         {
222             return await RecognizeEyeConditionAsync(source, bound, null);
223         }
224
225         /// <summary>
226         /// Determines eye-blink condition on the media source.
227         /// </summary>
228         /// <param name="source">The source of the media to recognize eye-blink condition for.</param>
229         /// <param name="bound">The bounding the face at the source.</param>
230         /// <param name="config">The configuration used for eye-blink condition recognition. This value can be null.</param>
231         /// <returns>A task that represents the asynchronous recognition operation.</returns>
232         /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
233         /// <exception cref="ObjectDisposedException">
234         ///     <paramref name="source"/> has already been disposed of.<br/>
235         ///     -or-<br/>
236         ///     <paramref name="config"/> has already been disposed of.
237         /// </exception>
238         /// <exception cref="NotSupportedException">The feature is not supported.</exception>
239         /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
240         /// <since_tizen> 3 </since_tizen>
241         public static async Task<EyeCondition> RecognizeEyeConditionAsync(MediaVisionSource source,
242             Rectangle bound, FaceRecognitionConfiguration config)
243         {
244             if (source == null)
245             {
246                 throw new ArgumentNullException(nameof(source));
247             }
248
249             TaskCompletionSource<EyeCondition> tcs = new TaskCompletionSource<EyeCondition>();
250
251             InteropFace.EyeConditionRecognizedCallback cb = (IntPtr sourceHandle, IntPtr engineCfgHandle,
252                 global::Interop.MediaVision.Rectangle faceLocation, EyeCondition eyeCondition, IntPtr _) =>
253             {
254                 Log.Info(MediaVisionLog.Tag, $"Eye condition recognized, eye condition : {eyeCondition}");
255                 if (!tcs.TrySetResult(eyeCondition))
256                 {
257                     Log.Error(MediaVisionLog.Tag, "Failed to set eye condition result");
258                 }
259             };
260
261             using (var cbKeeper = ObjectKeeper.Get(cb))
262             {
263                 InteropFace.RecognizeEyeCondition(source.Handle, EngineConfiguration.GetHandle(config),
264                     bound.ToMarshalable(), cb).Validate("Failed to perform eye condition recognition.");
265
266                 return await tcs.Task;
267             }
268         }
269
270         /// <summary>
271         /// Determines facial expression on media source.
272         /// </summary>
273         /// <param name="source">The source of the media to recognize facial expression for.</param>
274         /// <param name="bound">The location bounding the face at the source.</param>
275         /// <returns>A task that represents the asynchronous recognition operation.</returns>
276         /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
277         /// <exception cref="ObjectDisposedException"><paramref name="source"/> has already been disposed of.</exception>
278         /// <exception cref="NotSupportedException">The feature is not supported.</exception>
279         /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
280         /// <since_tizen> 3 </since_tizen>
281         public static async Task<FacialExpression> RecognizeFacialExpressionAsync(MediaVisionSource source,
282             Rectangle bound)
283         {
284             return await RecognizeFacialExpressionAsync(source, bound, null);
285         }
286
287         /// <summary>
288         /// Determines facial expression on media source.
289         /// </summary>
290         /// <param name="source">The source of the media to recognize facial expression for.</param>
291         /// <param name="bound">The location bounding the face at the source.</param>
292         /// <param name="config">The configuration used for expression recognition. This value can be null.</param>
293         /// <returns>A task that represents the asynchronous recognition operation.</returns>
294         /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
295         /// <exception cref="ObjectDisposedException">
296         ///     <paramref name="source"/> has already been disposed of.<br/>
297         ///     -or-<br/>
298         ///     <paramref name="config"/> has already been disposed of.
299         /// </exception>
300         /// <exception cref="NotSupportedException">The feature is not supported.</exception>
301         /// <feature>http://tizen.org/feature/vision.face_recognition</feature>
302         /// <since_tizen> 3 </since_tizen>
303         public static async Task<FacialExpression> RecognizeFacialExpressionAsync(MediaVisionSource source,
304             Rectangle bound, FaceRecognitionConfiguration config)
305         {
306             if (source == null)
307             {
308                 throw new ArgumentNullException(nameof(source));
309             }
310
311             TaskCompletionSource<FacialExpression> tcsResult = new TaskCompletionSource<FacialExpression>();
312
313             InteropFace.MvFaceFacialExpressionRecognizedCallback cb = (IntPtr sourceHandle, IntPtr engineCfgHandle,
314                 global::Interop.MediaVision.Rectangle faceLocation, FacialExpression facialExpression, IntPtr _) =>
315              {
316                  Log.Info(MediaVisionLog.Tag, $"Facial expression recognized, expression : {facialExpression}");
317                  if (!tcsResult.TrySetResult(facialExpression))
318                  {
319                      Log.Error(MediaVisionLog.Tag, "Failed to set facial result");
320                  }
321              };
322
323             using (var cbKeeper = ObjectKeeper.Get(cb))
324             {
325                 InteropFace.RecognizeFacialExpression(source.Handle, EngineConfiguration.GetHandle(config),
326                     bound.ToMarshalable(), cb).
327                     Validate("Failed to perform facial expression recognition.");
328
329                 return await tcsResult.Task;
330             }
331         }
332     }
333 }
334