Release 4.0.0-preview1-00051
[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
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.\n
39         ///     -or-\n
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         /// <since_tizen> 3</since_tizen>
46         public static async Task<FaceRecognitionResult> RecognizeAsync(MediaVisionSource source,
47             FaceRecognitionModel recognitionModel)
48         {
49             return await InvokeRecognizeAsync(source, recognitionModel, null, null);
50         }
51
52         /// <summary>
53         /// Performs face recognition on the source with <see cref="FaceRecognitionModel"/> and a bounding box.
54         /// </summary>
55         /// <param name="source">The <see cref="MediaVisionSource"/> of the media to recognize faces for.</param>
56         /// <param name="recognitionModel">The <see cref="FaceRecognitionModel"/> to be used for recognition.</param>
57         /// <param name="bound">Rectangular box bounding face image on the source.</param>
58         /// <returns>A task that represents the asynchronous recognition operation.</returns>
59         /// <exception cref="ArgumentNullException">
60         ///     <paramref name="source"/> is null.\n
61         ///     -or-\n
62         ///     <paramref name="recognitionModel"/> is null.
63         /// </exception>
64         /// <exception cref="NotSupportedException">The feature is not supported.</exception>
65         /// <exception cref="ObjectDisposedException"><paramref name="source"/> has already been disposed of.</exception>
66         /// <exception cref="InvalidOperationException"><paramref name="recognitionModel"/> is untrained model.</exception>
67         /// <since_tizen> 3</since_tizen>
68         public static async Task<FaceRecognitionResult> RecognizeAsync(MediaVisionSource source,
69             FaceRecognitionModel recognitionModel, Rectangle bound)
70         {
71             return await InvokeRecognizeAsync(source, recognitionModel, bound, null);
72         }
73
74         /// <summary>
75         /// Performs face recognition on the source with <see cref="FaceRecognitionModel"/> and <see cref="FaceRecognitionConfiguration"/>.
76         /// </summary>
77         /// <param name="source">The <see cref="MediaVisionSource"/> of the media to recognize faces for.</param>
78         /// <param name="recognitionModel">The <see cref="FaceRecognitionModel"/> to be used for recognition.</param>
79         /// <param name="config">The configuration used for recognition. This value can be null.</param>
80         /// <returns>A task that represents the asynchronous recognition operation.</returns>
81         /// <exception cref="ArgumentNullException">
82         ///     <paramref name="source"/> is null.\n
83         ///     -or-\n
84         ///     <paramref name="recognitionModel"/> is null.
85         /// </exception>
86         /// <exception cref="NotSupportedException">The feature is not supported.</exception>
87         /// <exception cref="ObjectDisposedException">
88         ///     <paramref name="source"/> has already been disposed of.\n
89         ///     -or-\n
90         ///     <paramref name="config"/> has already been disposed of.
91         /// </exception>
92         /// <exception cref="InvalidOperationException"><paramref name="recognitionModel"/> is untrained model.</exception>
93         /// <since_tizen> 3</since_tizen>
94         public static async Task<FaceRecognitionResult> RecognizeAsync(MediaVisionSource source,
95             FaceRecognitionModel recognitionModel, FaceRecognitionConfiguration config)
96         {
97             return await InvokeRecognizeAsync(source, recognitionModel, null, config);
98         }
99
100
101         /// <summary>
102         /// Performs face recognition on the source  with <see cref="FaceRecognitionModel"/>, <see cref="FaceRecognitionConfiguration"/>
103         /// and a bounding box.
104         /// </summary>
105         /// <param name="source">The <see cref="MediaVisionSource"/> of the media to recognize faces for.</param>
106         /// <param name="recognitionModel">The <see cref="FaceRecognitionModel"/> to be used for recognition.</param>
107         /// <param name="bound">Rectangular box bounding face image on the source.</param>
108         /// <param name="config">The <see cref="FaceRecognitionConfiguration"/> used for recognition. This value can be null.</param>
109         /// <returns>A task that represents the asynchronous recognition operation.</returns>
110         /// <exception cref="ArgumentNullException">
111         ///     <paramref name="source"/> is null.\n
112         ///     -or-\n
113         ///     <paramref name="recognitionModel"/> is null.
114         /// </exception>
115         /// <exception cref="NotSupportedException">The feature is not supported.</exception>
116         /// <exception cref="ObjectDisposedException">
117         ///     <paramref name="source"/> has already been disposed of.\n
118         ///     -or-\n
119         ///     <paramref name="config"/> has already been disposed of.
120         /// </exception>
121         /// <exception cref="InvalidOperationException"><paramref name="recognitionModel"/> is untrained model.</exception>
122         /// <since_tizen> 3</since_tizen>
123         public static async Task<FaceRecognitionResult> RecognizeAsync(MediaVisionSource source,
124             FaceRecognitionModel recognitionModel, Rectangle bound, FaceRecognitionConfiguration config)
125         {
126             return await InvokeRecognizeAsync(source, recognitionModel, bound, config);
127         }
128
129         private static MediaVisionError InvokeRecognize(IntPtr sourceHandle, IntPtr modelHandle,
130             IntPtr configHandle, InteropFace.RecognizedCallback cb, Rectangle? area)
131         {
132             if (area == null)
133             {
134                 return InteropFace.Recognize(sourceHandle, modelHandle, configHandle, IntPtr.Zero, cb);
135             }
136
137             var rect = area.Value.ToMarshalable();
138             return InteropFace.Recognize(sourceHandle, modelHandle, configHandle, ref rect, cb);
139         }
140
141         private static async Task<FaceRecognitionResult> InvokeRecognizeAsync(MediaVisionSource source,
142             FaceRecognitionModel recognitionModel, Rectangle? area,
143             FaceRecognitionConfiguration config)
144         {
145             if (source == null)
146             {
147                 throw new ArgumentNullException(nameof(source));
148             }
149             if (recognitionModel == null)
150             {
151                 throw new ArgumentNullException(nameof(recognitionModel));
152             }
153
154             TaskCompletionSource<FaceRecognitionResult> tcs = new TaskCompletionSource<FaceRecognitionResult>();
155
156             using (var cb = ObjectKeeper.Get(GetRecognizedCallback(tcs)))
157             {
158                 InvokeRecognize(source.Handle, recognitionModel.Handle, EngineConfiguration.GetHandle(config),
159                     cb.Target, area).Validate("Failed to perform face recognition.");
160
161                 return await tcs.Task;
162             }
163         }
164
165         private static FaceRecognitionResult CreateRecognitionResult(
166              IntPtr faceLocationPtr, IntPtr faceLabelPtr, double confidence)
167         {
168             int faceLabel = 0;
169             if (faceLabelPtr != IntPtr.Zero)
170             {
171                 faceLabel = Marshal.ReadInt32(faceLabelPtr);
172             }
173
174             Rectangle? faceLocation = null;
175             if (faceLocationPtr != IntPtr.Zero)
176             {
177                 var area = Marshal.PtrToStructure<global::Interop.MediaVision.Rectangle>(faceLocationPtr);
178                 faceLocation = area.ToApiStruct();
179             }
180
181             return new FaceRecognitionResult(faceLabelPtr != IntPtr.Zero, confidence, faceLabel, faceLocation);
182         }
183
184         private static InteropFace.RecognizedCallback GetRecognizedCallback(
185             TaskCompletionSource<FaceRecognitionResult> tcs)
186         {
187             return (IntPtr sourceHandle, IntPtr recognitionModelHandle,
188                 IntPtr engineCfgHandle, IntPtr faceLocationPtr, IntPtr faceLabelPtr, double confidence, IntPtr _) =>
189             {
190                 try
191                 {
192                     if (!tcs.TrySetResult(CreateRecognitionResult(faceLocationPtr, faceLabelPtr, confidence)))
193                     {
194                         Log.Error(MediaVisionLog.Tag, "Failed to set result");
195                     }
196                 }
197                 catch (Exception e)
198                 {
199                     MultimediaLog.Error(MediaVisionLog.Tag, "Setting recognition result failed.", e);
200                     tcs.TrySetException(e);
201                 }
202             };
203         }
204
205         /// <summary>
206         /// Determines eye-blink condition on media source.
207         /// </summary>
208         /// <param name="source">The source of the media to recognize eye-blink condition for.</param>
209         /// <param name="bound">The bounding the face at the source.</param>
210         /// <returns>A task that represents the asynchronous recognition operation.</returns>
211         /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
212         /// <exception cref="ObjectDisposedException"><paramref name="source"/> has already been disposed of.</exception>
213         /// <exception cref="NotSupportedException">The feature is not supported.</exception>
214         /// <since_tizen> 3</since_tizen>
215         public static async Task<EyeCondition> RecognizeEyeConditionAsync(MediaVisionSource source,
216             Rectangle bound)
217         {
218             return await RecognizeEyeConditionAsync(source, bound, null);
219         }
220
221         /// <summary>
222         /// Determines eye-blink condition on media source.
223         /// </summary>
224         /// <param name="source">The source of the media to recognize eye-blink condition for.</param>
225         /// <param name="bound">The bounding the face at the source.</param>
226         /// <param name="config">The configuration used for eye-blink condition recognition. This value can be null.</param>
227         /// <returns>A task that represents the asynchronous recognition operation.</returns>
228         /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
229         /// <exception cref="ObjectDisposedException">
230         ///     <paramref name="source"/> has already been disposed of.\n
231         ///     -or-\n
232         ///     <paramref name="config"/> has already been disposed of.
233         /// </exception>
234         /// <exception cref="NotSupportedException">The feature is not supported.</exception>
235         /// <since_tizen> 3</since_tizen>
236         public static async Task<EyeCondition> RecognizeEyeConditionAsync(MediaVisionSource source,
237             Rectangle bound, FaceRecognitionConfiguration config)
238         {
239             if (source == null)
240             {
241                 throw new ArgumentNullException(nameof(source));
242             }
243
244             TaskCompletionSource<EyeCondition> tcs = new TaskCompletionSource<EyeCondition>();
245
246             InteropFace.EyeConditionRecognizedCallback cb = (IntPtr sourceHandle, IntPtr engineCfgHandle,
247                 global::Interop.MediaVision.Rectangle faceLocation, EyeCondition eyeCondition, IntPtr _) =>
248             {
249                 Log.Info(MediaVisionLog.Tag, $"Eye condition recognized, eye condition : {eyeCondition}");
250                 if (!tcs.TrySetResult(eyeCondition))
251                 {
252                     Log.Error(MediaVisionLog.Tag, "Failed to set eye condition result");
253                 }
254             };
255
256             using (var cbKeeper = ObjectKeeper.Get(cb))
257             {
258                 InteropFace.RecognizeEyeCondition(source.Handle, EngineConfiguration.GetHandle(config),
259                     bound.ToMarshalable(), cb).Validate("Failed to perform eye condition recognition.");
260
261                 return await tcs.Task;
262             }
263         }
264
265         /// <summary>
266         /// Determines facial expression on media source.
267         /// </summary>
268         /// <param name="source">The source of the media to recognize facial expression for.</param>
269         /// <param name="bound">The location bounding the face at the source.</param>
270         /// <returns>A task that represents the asynchronous recognition operation.</returns>
271         /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
272         /// <exception cref="ObjectDisposedException"><paramref name="source"/> has already been disposed of.</exception>
273         /// <exception cref="NotSupportedException">The feature is not supported.</exception>
274         /// <since_tizen> 3</since_tizen>
275         public static async Task<FacialExpression> RecognizeFacialExpressionAsync(MediaVisionSource source,
276             Rectangle bound)
277         {
278             return await RecognizeFacialExpressionAsync(source, bound, null);
279         }
280
281         /// <summary>
282         /// Determines facial expression on media source.
283         /// </summary>
284         /// <param name="source">The source of the media to recognize facial expression for.</param>
285         /// <param name="bound">The location bounding the face at the source.</param>
286         /// <param name="config">The configuration used for expression recognition. This value can be null.</param>
287         /// <returns>A task that represents the asynchronous recognition operation.</returns>
288         /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
289         /// <exception cref="ObjectDisposedException">
290         ///     <paramref name="source"/> has already been disposed of.\n
291         ///     -or-\n
292         ///     <paramref name="config"/> has already been disposed of.
293         /// </exception>
294         /// <exception cref="NotSupportedException">The feature is not supported.</exception>
295         /// <since_tizen> 3</since_tizen>
296         public static async Task<FacialExpression> RecognizeFacialExpressionAsync(MediaVisionSource source,
297             Rectangle bound, FaceRecognitionConfiguration config)
298         {
299             if (source == null)
300             {
301                 throw new ArgumentNullException(nameof(source));
302             }
303
304             TaskCompletionSource<FacialExpression> tcsResult = new TaskCompletionSource<FacialExpression>();
305
306            InteropFace.MvFaceFacialExpressionRecognizedCallback cb = (IntPtr sourceHandle, IntPtr engineCfgHandle,
307                global::Interop.MediaVision.Rectangle faceLocation, FacialExpression facialExpression, IntPtr _) =>
308             {
309                 Log.Info(MediaVisionLog.Tag, $"Facial expression recognized, expression : {facialExpression}");
310                 if (!tcsResult.TrySetResult(facialExpression))
311                 {
312                     Log.Error(MediaVisionLog.Tag, "Failed to set facial result");
313                 }
314             };
315
316             using (var cbKeeper = ObjectKeeper.Get(cb))
317             {
318                 InteropFace.RecognizeFacialExpression(source.Handle, EngineConfiguration.GetHandle(config),
319                     bound.ToMarshalable(), cb).
320                     Validate("Failed to perform facial expression recognition.");
321
322                 return await tcsResult.Task;
323             }
324         }
325     }
326 }
327