};
}
- internal static Tizen.Multimedia.Rectangle[] ToApiStruct(MediaVision.Rectangle[] rects)
+ internal static Tizen.Multimedia.Rectangle[] ToApiStruct(this MediaVision.Rectangle[] rects)
{
var result = new Tizen.Multimedia.Rectangle[rects.Length];
return result;
}
+ internal static MediaVision.Rectangle[] ToMarShalable(this Tizen.Multimedia.Rectangle[] rects)
+ {
+ var result = new MediaVision.Rectangle[rects.Length];
+
+ for (int i = 0; i < rects.Length; i++)
+ {
+ result[i] = rects[i].ToMarshalable();
+ }
+ return result;
+ }
+
/// <summary>
/// Interop for Media Vision APIs.
/// </summary>
[DllImport(Libraries.MediaVision, EntryPoint = "mv_engine_config_set_string_attribute")]
internal static extern MediaVisionError SetString(IntPtr handle, string name, string value);
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_engine_config_set_array_string_attribute")]
+ internal static extern MediaVisionError SetStringArray(IntPtr handle, string name,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] string[] value, int size);
+
[DllImport(Libraries.MediaVision, EntryPoint = "mv_engine_config_get_double_attribute")]
internal static extern MediaVisionError GetDouble(IntPtr handle, string name, out double value);
[DllImport(Libraries.MediaVision, EntryPoint = "mv_engine_config_get_string_attribute")]
internal static extern MediaVisionError GetString(IntPtr handle, string name, out IntPtr value);
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_engine_config_get_array_string_attribute")]
+ internal static extern MediaVisionError GetStringArray(IntPtr handle, string name,
+ out IntPtr value, out int size);
}
}
}
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Runtime.InteropServices;
+using Tizen.Multimedia.Vision;
+
+/// <summary>
+/// Interop APIs.
+/// </summary>
+internal static partial class Interop
+{
+ /// <summary>
+ /// Interop for Media Vision APIs.
+ /// </summary>
+ internal static partial class MediaVision
+ {
+ /// <summary>
+ /// Interop for Face APIs.
+ /// </summary>
+ internal static partial class Inference
+ {
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void FaceDetectedCallback(IntPtr source, int numberOfFaces,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] float[] confidences,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Rectangle[] location,
+ IntPtr userData = default(IntPtr));
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void FacialLandmarkDetectedCallback(IntPtr source, int numberOfLandmarks,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Point[] locations,
+ IntPtr userData = default(IntPtr));
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void ImageClassifedCallback(IntPtr source, int numberOfClasses,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] int[] indices,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] string[] names,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] float[] confidences,
+ IntPtr userData = default(IntPtr));
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void ObjectDetectedCallback(IntPtr source, int numberOfObjects,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] int[] indices,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] string[] names,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] float[] confidences,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Rectangle[] location,
+ IntPtr userData = default(IntPtr));
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate bool SupportedBackendCallback(string backend, bool isSupported,
+ IntPtr userData = default(IntPtr));
+
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_create")]
+ internal static extern MediaVisionError Create(out IntPtr handle);
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_destroy")]
+ internal static extern MediaVisionError Destroy(IntPtr handle);
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_configure")]
+ internal static extern MediaVisionError Configure(IntPtr handle, IntPtr engineConfig);
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_prepare")]
+ internal static extern MediaVisionError Load(IntPtr handle);
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_foreach_supported_engine")]
+ internal static extern MediaVisionError ForeachSupportedBackend(IntPtr handle,
+ SupportedBackendCallback callback, IntPtr userData = default(IntPtr));
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_image_classify")]
+ internal static extern MediaVisionError ClassifyImage(IntPtr source, IntPtr inference,
+ IntPtr roi, ImageClassifedCallback callback, IntPtr userData = default(IntPtr));
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_object_detect")]
+ internal static extern MediaVisionError DetectObject(IntPtr source, IntPtr inference,
+ ObjectDetectedCallback callback, IntPtr userData = default(IntPtr));
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_face_detect")]
+ internal static extern MediaVisionError DetectFace(IntPtr source, IntPtr inference,
+ FaceDetectedCallback callback, IntPtr userData = default(IntPtr));
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_facial_landmark_detect")]
+ internal static extern MediaVisionError DetectFacialLandmark(IntPtr source, IntPtr inference,
+ IntPtr roi, FacialLandmarkDetectedCallback callback, IntPtr userData = default(IntPtr));
+ }
+ }
+}
EngineConfig.SetString(Handle, key, value).Validate("Failed to set attribute");
}
+ internal void Set(string key, string [] value)
+ {
+ EngineConfig.SetStringArray(Handle, key, value, value.Length).Validate("Failed to set attribute");
+ }
+
internal int GetInt(string key)
{
int value = 0;
}
}
+ internal string[] GetStringArray(string key)
+ {
+ IntPtr values = IntPtr.Zero;
+ int size = 0;
+
+ try
+ {
+ EngineConfig.GetStringArray(Handle, key, out values, out size).
+ Validate("Failed to get the value");
+
+ var current = values;
+ var result = new string[size];
+
+ for (int i = 0; i < size; i++)
+ {
+ result[i] = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(current));
+ current = (IntPtr)((long)current + Marshal.SizeOf(typeof(IntPtr)));
+ }
+
+ return result;
+ }
+ finally
+ {
+ var current = values;
+ for (int i = 0; i < size; i++)
+ {
+ LibcSupport.Free(Marshal.ReadIntPtr(current));
+ current = (IntPtr)((long)current + Marshal.SizeOf(typeof(IntPtr)));
+ }
+ }
+ }
+
/// <summary>
/// Releases all resources used by the <see cref="EngineConfiguration"/> object.
/// </summary>
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Collections.ObjectModel;
+
+namespace Tizen.Multimedia.Vision
+{
+ /// <summary>
+ /// Provides the ability to get the result of face detection using <see cref="InferenceModelConfiguration"/> and
+ /// <see cref="FaceDetector.DetectAsync(MediaVisionSource, InferenceModelConfiguration)"/>.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public class FaceDetectionResult
+ {
+ internal FaceDetectionResult(float confidence, global::Interop.MediaVision.Rectangle location)
+ {
+ Confidence = confidence;
+ Location = location.ToApiStruct();
+ }
+
+ /// <summary>
+ /// Gets the confidence of detected face.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public float Confidence { get; }
+
+ /// <summary>
+ /// Gets the location of detected face.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public Rectangle Location { get; }
+ }
+}
*/
using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Threading.Tasks;
using InteropFace = Interop.MediaVision.Face;
+using InteropInference = Interop.MediaVision.Inference;
namespace Tizen.Multimedia.Vision
{
/// <summary>
/// Provides the ability to detect faces on image sources.
/// </summary>
+ /// <remarks>
+ /// If you want to use face detection based on inference engine(<see cref="InferenceBackendType"/>),
+ /// please use <see cref="DetectAsync(MediaVisionSource, InferenceModelConfiguration)"/>.
+ /// </remarks>
/// <since_tizen> 4 </since_tizen>
public static class FaceDetector
{
-
/// <summary>
/// Detects faces on the source.<br/>
/// Each time when DetectAsync is called, a set of the detected faces at the media source are received asynchronously.
/// <since_tizen> 4 </since_tizen>
public static async Task<Rectangle[]> DetectAsync(MediaVisionSource source)
{
- return await DetectAsync(source, null);
+ return await DetectAsync(source, (FaceDetectionConfiguration)null);
}
/// <summary>
}
};
}
+
+ /// <summary>
+ /// Detects faces on the source image using inference engine set in <paramref name="config"/>.<br/>
+ /// Each time when DetectAsync is called, a set of the detected faces at the media source are received asynchronously.
+ /// </summary>
+ /// <remarks>
+ /// If there's no detected face, empty collection will be returned.
+ /// </remarks>
+ /// <feature>http://tizen.org/feature/vision.inference</feature>
+ /// <feature>http://tizen.org/feature/vision.inference.face</feature>
+ /// <param name="source">The source of the media where faces will be detected.</param>
+ /// <param name="config">The engine's configuration that will be used for detecting.</param>
+ /// <returns>
+ /// A task that represents the asynchronous detect operation.<br/>
+ /// If there's no detected face, empty collection will be returned.
+ /// </returns>
+ /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="config"/> is null.</exception>
+ /// <exception cref="InvalidOperationException">Internal error.</exception>
+ /// <exception cref="NotSupportedException">The feature is not supported.</exception>
+ /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
+ /// <seealso cref="InferenceModelConfiguration"/>
+ /// <since_tizen> 6 </since_tizen>
+ public static async Task<IEnumerable<FaceDetectionResult>> DetectAsync(MediaVisionSource source,
+ InferenceModelConfiguration config)
+ {
+ // `vision.inference` feature is already checked, when config is created.
+ ValidationUtil.ValidateFeatureSupported(VisionFeatures.InferenceFace);
+
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+ if (config == null)
+ {
+ throw new ArgumentNullException(nameof(config));
+ }
+
+ var tcs = new TaskCompletionSource<IEnumerable<FaceDetectionResult>>();
+
+ using (var cb = ObjectKeeper.Get(GetCallback(tcs)))
+ {
+ InteropInference.DetectFace(source.Handle, config.GetHandle(), cb.Target).
+ Validate("Failed to detect face.");
+
+ return await tcs.Task;
+ }
+ }
+
+ private static InteropInference.FaceDetectedCallback GetCallback(TaskCompletionSource<IEnumerable<FaceDetectionResult>> tcs)
+ {
+ return (IntPtr sourceHandle, int numberOfFaces, float[] confidences,
+ global::Interop.MediaVision.Rectangle[] locations, IntPtr _) =>
+ {
+ try
+ {
+ if (!tcs.TrySetResult(GetResults(numberOfFaces, confidences, locations)))
+ {
+ Log.Error(MediaVisionLog.Tag, "Failed to set face detection result.");
+ }
+ }
+ catch (Exception e)
+ {
+ tcs.TrySetException(e);
+ }
+ };
+ }
+
+ private static IEnumerable<FaceDetectionResult> GetResults(int number, float[] confidences,
+ global::Interop.MediaVision.Rectangle[] locations)
+ {
+ if (number == 0)
+ {
+ return Enumerable.Empty<FaceDetectionResult>();
+ }
+
+ var results = new List<FaceDetectionResult>();
+
+ for (int i = 0; i < number; i++)
+ {
+ results.Add(new FaceDetectionResult(confidences[i], locations[i]));
+ }
+
+ return results;
+ }
}
}
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using InteropInference = Interop.MediaVision.Inference;
+
+namespace Tizen.Multimedia.Vision
+{
+ /// <summary>
+ /// Provides the ability to detect facial landmarks on image source using inference engine.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public static class FacialLandmarkDetector
+ {
+ /// <summary>
+ /// Detects facial landmarks on the source image using inference engine set in <paramref name="config"/>.<br/>
+ /// </summary>
+ /// <remarks>
+ /// To set region-of-interest area in source image, please set <see cref="InferenceModelConfiguration.Roi"/>.
+ /// If not set, full image area will be used to detect facial landmark.
+ /// </remarks>
+ /// <feature>http://tizen.org/feature/vision.inference</feature>
+ /// <feature>http://tizen.org/feature/vision.inference.face</feature>
+ /// <param name="source">The source of the media where faces will be detected.</param>
+ /// <param name="config">The engine's configuration that will be used for detecting.</param>
+ /// <returns>
+ /// A task that represents the asynchronous detect operation.<br/>
+ /// If there's no detected facial landmark, empty collection will be returned.
+ /// </returns>
+ /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="config"/> is null.</exception>
+ /// <exception cref="InvalidOperationException">Internal error.</exception>
+ /// <exception cref="NotSupportedException">The feature is not supported.</exception>
+ /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
+ /// <seealso cref="InferenceModelConfiguration"/>
+ /// <since_tizen> 6 </since_tizen>
+ public static async Task<IEnumerable<Point>> DetectAsync(MediaVisionSource source,
+ InferenceModelConfiguration config)
+ {
+ // `vision.inference` feature is already checked, when config is created.
+ ValidationUtil.ValidateFeatureSupported(VisionFeatures.InferenceFace);
+
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+ if (config == null)
+ {
+ throw new ArgumentNullException(nameof(config));
+ }
+
+ var tcs = new TaskCompletionSource<IEnumerable<Point>>();
+
+ using (var cb = ObjectKeeper.Get(GetCallback(tcs)))
+ {
+ IntPtr roiUnmanaged = IntPtr.Zero;
+
+ try
+ {
+ if (config.Roi.HasValue)
+ {
+ var roi = config.Roi.Value.ToMarshalable();
+
+ roiUnmanaged = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(global::Interop.MediaVision.Rectangle)));
+ Marshal.WriteIntPtr(roiUnmanaged, IntPtr.Zero);
+ Marshal.StructureToPtr(roi, roiUnmanaged, false);
+ }
+
+ InteropInference.DetectFacialLandmark(source.Handle, config.GetHandle(), roiUnmanaged, cb.Target).
+ Validate("Failed to detect facial landmark.");
+ }
+ finally
+ {
+ if (roiUnmanaged != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(roiUnmanaged);
+ }
+ }
+
+ return await tcs.Task;
+ }
+ }
+
+ private static InteropInference.FacialLandmarkDetectedCallback GetCallback(TaskCompletionSource<IEnumerable<Point>> tcs)
+ {
+ return (IntPtr sourceHandle, int numberOfLandmarks, global::Interop.MediaVision.Point[] locations, IntPtr _) =>
+ {
+ try
+ {
+
+ if (!tcs.TrySetResult(GetResults(numberOfLandmarks, locations)))
+ {
+ Log.Error(MediaVisionLog.Tag, "Failed to set facial landmark detection result.");
+ }
+ }
+ catch (Exception e)
+ {
+ tcs.TrySetException(e);
+ }
+ };
+ }
+
+ private static IEnumerable<Point> GetResults(int number, global::Interop.MediaVision.Point[] locations)
+ {
+ if (number == 0)
+ {
+ return Enumerable.Empty<Point>();
+ }
+
+ var results = new List<Point>();
+
+ for (int i = 0; i < number; i++)
+ {
+ results.Add(locations[i].ToApiStruct());
+ }
+
+ return results;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using System.Collections.ObjectModel;
+
+namespace Tizen.Multimedia.Vision
+{
+ /// <summary>
+ /// Provides the ability to get the result of face detection using <see cref="InferenceModelConfiguration"/> and
+ /// <see cref="ImageClassifier"/>.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public class ImageClassificationResult
+ {
+ internal ImageClassificationResult(int indice, string name, float confidence)
+ {
+ Indice = indice;
+ Name = name;
+ Confidence = confidence;
+ }
+
+ /// <summary>
+ /// Gets the indice of detected object.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public int Indice { get; }
+
+ /// <summary>
+ /// Gets the name of detected object.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public string Name { get; }
+
+ /// <summary>
+ /// Gets the confidence of detected object.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public float Confidence { get; }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using InteropInference = Interop.MediaVision.Inference;
+
+namespace Tizen.Multimedia.Vision
+{
+ /// <summary>
+ /// Provides the ability to classify image objects on image source using inference engine.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public static class ImageClassifier
+ {
+ /// <summary>
+ /// Classifies image objects on the source image using inference engine set in <paramref name="config"/>.<br/>
+ /// Each time when DetectAsync is called, a set of the detected faces at the media source are received asynchronously.
+ /// </summary>
+ /// <feature>http://tizen.org/feature/vision.inference</feature>
+ /// <feature>http://tizen.org/feature/vision.inference.image</feature>
+ /// <param name="source">The source of the media where faces will be detected.</param>
+ /// <param name="config">The engine's configuration that will be used for classifying.</param>
+ /// <returns>
+ /// A task that represents the asynchronous classify operation.<br/>
+ /// If there's no classified image object, empty collection will be returned.
+ /// </returns>
+ /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="config"/> is null.</exception>
+ /// <exception cref="InvalidOperationException">Internal error.</exception>
+ /// <exception cref="NotSupportedException">The feature is not supported.</exception>
+ /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
+ /// <seealso cref="InferenceModelConfiguration"/>
+ /// <since_tizen> 6 </since_tizen>
+ public static async Task<IEnumerable<ImageClassificationResult>> ClassifyAsync(MediaVisionSource source,
+ InferenceModelConfiguration config)
+ {
+ // `vision.inference` feature is already checked, when config is created.
+ ValidationUtil.ValidateFeatureSupported(VisionFeatures.InferenceImage);
+
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+ if (config == null)
+ {
+ throw new ArgumentNullException(nameof(config));
+ }
+
+ var tcs = new TaskCompletionSource<IEnumerable<ImageClassificationResult>>();
+
+ using (var cb = ObjectKeeper.Get(GetCallback(tcs)))
+ {
+ IntPtr roiUnmanaged = IntPtr.Zero;
+
+ try
+ {
+ if (config.Roi.HasValue)
+ {
+ var roi = config.Roi.Value.ToMarshalable();
+
+ roiUnmanaged = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(global::Interop.MediaVision.Rectangle)));
+ Marshal.WriteIntPtr(roiUnmanaged, IntPtr.Zero);
+ Marshal.StructureToPtr(roi, roiUnmanaged, false);
+ }
+
+ InteropInference.ClassifyImage(source.Handle, config.GetHandle(), roiUnmanaged, cb.Target).
+ Validate("Failed to classify image.");
+ }
+ finally
+ {
+ if (roiUnmanaged != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(roiUnmanaged);
+ }
+ }
+
+ return await tcs.Task;
+ }
+ }
+
+ private static InteropInference.ImageClassifedCallback GetCallback(TaskCompletionSource<IEnumerable<ImageClassificationResult>> tcs)
+ {
+ return (IntPtr sourceHandle, int numberOfClasses, int[] indices, string[] names, float[] confidences, IntPtr _) =>
+ {
+ try
+ {
+ if (!tcs.TrySetResult(GetResults(numberOfClasses, indices, names, confidences)))
+ {
+ Log.Error(MediaVisionLog.Tag, "Failed to set image classification result.");
+ }
+ }
+ catch (Exception e)
+ {
+ tcs.TrySetException(e);
+ }
+ };
+ }
+
+ private static IEnumerable<ImageClassificationResult> GetResults(int number, int[] indices,
+ string[] names, float[] confidences)
+ {
+ if (number == 0)
+ {
+ return Enumerable.Empty<ImageClassificationResult>();
+ }
+
+ var results = new List<ImageClassificationResult>();
+
+ for (int i = 0; i < number; i++)
+ {
+ results.Add(new ImageClassificationResult(indices[i], names[i], confidences[i]));
+ }
+
+ return results;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Linq;
+using System.IO;
+using System.Collections.Generic;
+using InteropInference = Interop.MediaVision.Inference;
+
+namespace Tizen.Multimedia.Vision
+{
+ /// <summary>
+ /// Represents a configuration of <see cref="FaceDetector"/>, <see cref="FacialLandmarkDetector"/>,
+ /// <see cref="ImageClassifier"/> and <see cref="ObjectDetector"/>.
+ /// </summary>
+ /// <remarks>
+ /// 'Inference model' means pre-learned data, which is represented by <see cref="ConfigurationFilePath"/> and
+ /// <see cref="WeightFilePath"/>, <see cref="CategoryFilePath"/>.<br/>
+ /// If user want to use tizen default inference model and its related value,
+ /// Please refer Tizen guide page(https://developer.tizen.org/development/guides/.net-application).
+ /// </remarks>
+ /// <feature>http://tizen.org/feature/vision.inference.face</feature>
+ /// <feature>http://tizen.org/feature/vision.inference.image</feature>
+ /// <since_tizen> 6 </since_tizen>
+ public class InferenceModelConfiguration : EngineConfiguration
+ {
+ private IntPtr _inferenceHandle = IntPtr.Zero;
+
+ private const string _keyModelConfigurationFilePath = "MV_INFERENCE_MODEL_CONFIGURATION_FILE_PATH";
+ private const string _keyModelWeightFilePath = "MV_INFERENCE_MODEL_WEIGHT_FILE_PATH";
+ private const string _keyModelUserFilePath = "MV_INFERENCE_MODEL_USER_FILE_PATH";
+ private const string _keyModelMeanValue = "MV_INFERENCE_MODEL_MEAN_VALUE";
+ private const string _keyModelStdValue = "MV_INFERENCE_MODEL_STD_VALUE";
+ private const string _keyBackendType = "MV_INFERENCE_BACKEND_TYPE";
+ private const string _keyTargetType = "MV_INFERENCE_TARGET_TYPE";
+ private const string _keyInputTensorWidth = "MV_INFERENCE_INPUT_TENSOR_WIDTH";
+ private const string _keyInputTensorHeight = "MV_INFERENCE_INPUT_TENSOR_HEIGHT";
+ private const string _keyInputTensorChannels = "MV_INFERENCE_INPUT_TENSOR_CHANNELS";
+ private const string _keyInputNodeName = "MV_INFERENCE_INPUT_NODE_NAME";
+ private const string _keyOutputNodeNames = "MV_INFERENCE_OUTPUT_NODE_NAMES";
+ private const string _keyOutputMaxNumber = "MV_INFERENCE_OUTPUT_MAX_NUMBER";
+ private const string _keyConfidenceThreshold = "MV_INFERENCE_CONFIDENCE_THRESHOLD";
+
+ // The following strings are fixed in native and will not be changed.
+ private const string _backendTypeOpenCV = "opencv";
+ private const string _backendTypeTFLite = "tflite";
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="InferenceModelConfiguration"/> class.
+ /// </summary>
+ /// <feature>http://tizen.org/feature/vision.inference.face</feature>
+ /// <feature>http://tizen.org/feature/vision.inference.image</feature>
+ /// <exception cref="NotSupportedException">The feature is not supported.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public InferenceModelConfiguration() : base("inference")
+ {
+ InteropInference.Create(out _inferenceHandle).Validate("Failed to create inference configuration");
+ }
+
+ /// <summary>
+ /// Loads inference model data and its related attributes.
+ /// </summary>
+ /// <remarks>
+ /// Before calling this method, user should set all properties which is required by each inference model.<br/>
+ /// The properties set after calling this method will not be affected in the result.
+ /// </remarks>
+ /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
+ /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
+ /// <feature>http://tizen.org/feature/vision.inference.face</feature>
+ /// <feature>http://tizen.org/feature/vision.inference.image</feature>
+ /// <exception cref="FileNotFoundException">
+ /// <see cref="ConfigurationFilePath"/>, <see cref="WeightFilePath"/> or <see cref="CategoryFilePath"/> have invalid path.
+ /// </exception>
+ /// <exception cref="FileFormatException">Invalid data type is used in inference model data.</exception>
+ /// <exception cref="InvalidDataException">
+ /// Inference model data contains unsupported operations in current backend version.
+ /// -or-<br/>
+ /// Invalid data type is used in inference model data.<br/>
+ /// </exception>
+ /// <exception cref="InvalidOperationException">Internal operation error.</exception>
+ /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public void LoadInferenceModel()
+ {
+ InteropInference.Configure(_inferenceHandle, GetHandle(this)).
+ Validate("Failed to configure inference model.");
+
+ var ret = InteropInference.Load(_inferenceHandle);
+ if (ret == MediaVisionError.InvalidData)
+ {
+ throw new InvalidDataException("Inference model data contains unsupported operations in current backend version.");
+ }
+ else if (ret == MediaVisionError.NotSupportedFormat)
+ {
+ throw new FileFormatException("Invalid data type is used in inference model data.");
+ }
+ ret.Validate("Failed to load inference model.");
+ }
+
+ internal IntPtr GetHandle()
+ {
+ return _inferenceHandle;
+ }
+
+ private IEnumerable<InferenceBackendType> _supportedBackend;
+
+ /// <summary>
+ /// Gets the list of inference backend engine which is supported in the current device.
+ /// </summary>
+ /// <returns>If there's no supported backend, empty collection will be returned.</returns>
+ /// <since_tizen> 6 </since_tizen>
+ public IEnumerable<InferenceBackendType> SupportedBackend
+ {
+ get
+ {
+ if (_supportedBackend == null)
+ {
+ GetSupportedBackend();
+ }
+
+ return _supportedBackend.Any() ? _supportedBackend : Enumerable.Empty<InferenceBackendType>();
+ }
+ }
+
+ private void GetSupportedBackend()
+ {
+ var supportedBackend = new List<InferenceBackendType>();
+
+ InteropInference.SupportedBackendCallback cb = (backend, isSupported, _) =>
+ {
+ if (isSupported && backend != null)
+ {
+ switch (backend)
+ {
+ case _backendTypeOpenCV:
+ supportedBackend.Add(InferenceBackendType.OpenCV);
+ break;
+ case _backendTypeTFLite:
+ supportedBackend.Add(InferenceBackendType.TFLite);
+ break;
+ }
+ }
+
+ return true;
+ };
+
+ InteropInference.ForeachSupportedBackend(_inferenceHandle, cb, IntPtr.Zero).
+ Validate("Failed to get supported backend");
+
+ _supportedBackend = supportedBackend;
+ }
+
+ /// <summary>
+ /// Gets or sets the path of inference model's configuration data file.
+ /// </summary>
+ /// <exception cref="ArgumentNullException">Input file path is null.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public string ConfigurationFilePath
+ {
+ get
+ {
+ return GetString(_keyModelConfigurationFilePath);
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value), "File path is null.");
+ }
+
+ Set(_keyModelConfigurationFilePath, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the path of inference model's weight file.
+ /// </summary>
+ /// <exception cref="ArgumentNullException">Input file path is null.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public string WeightFilePath
+ {
+ get
+ {
+ return GetString(_keyModelWeightFilePath);
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value), "File path is null.");
+ }
+
+ Set(_keyModelWeightFilePath, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the path of inference model's category file.
+ /// </summary>
+ /// <remarks>
+ /// This value should be set to use <see cref="ImageClassifier"/> or <see cref="ObjectDetector"/>.
+ /// </remarks>
+ /// <exception cref="ArgumentNullException">Input file path is null.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public string CategoryFilePath
+ {
+ get
+ {
+ return GetString(_keyModelUserFilePath);
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value), "File path is null.");
+ }
+
+ Set(_keyModelUserFilePath, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the inference model's mean value.
+ /// </summary>
+ /// <remarks>It should be greater than or equal to 0.</remarks>
+ /// <exception cref="ArgumentOutOfRangeException">The value is invalid.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public double MeanValue
+ {
+ get
+ {
+ return GetDouble(_keyModelMeanValue);
+ }
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), value,
+ $"Value should be greater than or equal to 0");
+ }
+
+ Set(_keyModelMeanValue, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the inference model's STD(Standard deviation) value.
+ /// </summary>
+ /// <remarks>It should be greater than or equal to 0.</remarks>
+ /// <exception cref="ArgumentOutOfRangeException">The value is invalid.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public double StdValue
+ {
+ get
+ {
+ return GetDouble(_keyModelStdValue);
+ }
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), value,
+ $"Value should be greater than or equal to 0");
+ }
+
+ Set(_keyModelStdValue, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the inference model's backend engine.
+ /// </summary>
+ /// <remarks>The default backend type is <see cref="InferenceBackendType.OpenCV"/></remarks>
+ /// <exception cref="ArgumentException"><paramref name="value"/> is not valid.</exception>
+ /// <exception cref="NotSupportedException">The engine type is not supported.</exception>
+ /// <seealso cref="SupportedBackend"/>
+ /// <since_tizen> 6 </since_tizen>
+ public InferenceBackendType Backend
+ {
+ get
+ {
+ return (InferenceBackendType)GetInt(_keyBackendType);
+ }
+ set
+ {
+ ValidationUtil.ValidateEnum(typeof(InferenceBackendType), value, nameof(Backend));
+
+ if (!SupportedBackend.Contains(value))
+ {
+ throw new NotSupportedException("Not supported engine type. " +
+ "Please check supported engine using 'SupportedBackendType'.");
+ }
+
+ Set(_keyBackendType, (int)value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the inference model's target.
+ /// </summary>
+ /// <remarks>
+ /// The default target is <see cref="InferenceTargetType.CPU"/>.<br/>
+ /// If target doesn't support <see cref="InferenceTargetType.GPU"/> and <see cref="InferenceTargetType.Custom"/>,
+ /// <see cref="InferenceTargetType.CPU"/> will be used internally, despite the user's choice.
+ /// </remarks>
+ /// <exception cref="ArgumentException"><paramref name="value"/> is not valid.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public InferenceTargetType Target
+ {
+ get
+ {
+ return (InferenceTargetType)GetInt(_keyTargetType);
+ }
+ set
+ {
+ ValidationUtil.ValidateEnum(typeof(InferenceTargetType), value, nameof(Target));
+
+ Set(_keyTargetType, (int)value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the size of inference model's tensor.
+ /// </summary>
+ /// <remarks>
+ /// Both width and height of tensor should be greater than 0.<br/>
+ /// 'Size(-1, -1) is allowed when the intention is to use original image source size as TensorSize.
+ /// </remarks>
+ /// <exception cref="ArgumentException">
+ /// Only one of <paramref name="value.Width"/> or <paramref name="value.Height"/> have -1.</exception>
+ /// <exception cref="ArgumentOutOfRangeException">The value is invalid.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public Size TensorSize
+ {
+ get
+ {
+ var width = GetInt(_keyInputTensorWidth);
+ var height = GetInt(_keyInputTensorHeight);
+
+ return new Size(width, height);
+ }
+ set
+ {
+ if ((value.Width == -1 && value.Height != -1) || (value.Height == -1 && value.Width != -1))
+ {
+ throw new ArgumentException("Both width and height must be set to -1, or greater than 0.");
+ }
+
+ if (value.Width == 0 || value.Width <= -2)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), value,
+ "Both width and height must be set to -1, or greater than 0.");
+ }
+
+ if (value.Height == 0 || value.Height <= -2)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), value,
+ "Both width and height must be set to -1, or greater than 0.");
+ }
+
+ Set(_keyInputTensorWidth, value.Width);
+ Set(_keyInputTensorHeight, value.Height);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the number of inference model's tensor channel.
+ /// </summary>
+ /// <remarks>
+ /// For example, for RGB colorspace this value should be set to 3<br/>
+ /// It should be greater than 0.
+ /// </remarks>
+ /// <exception cref="ArgumentOutOfRangeException">The value is invalid.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public int TensorChannels
+ {
+ get
+ {
+ return GetInt(_keyInputTensorChannels);
+ }
+ set
+ {
+ if (value <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), value, "Tensor channel should be greater than 0.");
+ }
+
+ Set(_keyInputTensorChannels, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the name of an input node
+ /// </summary>
+ /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public string InputNodeName
+ {
+ get
+ {
+ return GetString(_keyInputNodeName);
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value), "InputNodeName can't be null.");
+ }
+
+ Set(_keyInputNodeName, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the name of an output node
+ /// </summary>
+ /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public IList<string> OutputNodeName
+ {
+ get
+ {
+ return GetStringArray(_keyOutputNodeNames);
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value), "OutputNodeName can't be null.");
+ }
+
+ var name = new string[value.Count];
+ value.CopyTo(name, 0);
+ Set(_keyOutputNodeNames, name);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the maximum output number of detection or classification.
+ /// </summary>
+ /// <remarks>
+ /// The input value over 10 will be set to 10 and the input value under 1 will be set to 1.<br/>
+ /// This value can be used to decide the size of <see cref="Roi"/>, it's length should be the same.
+ /// </remarks>
+ /// <since_tizen> 6 </since_tizen>
+ public int MaxOutputNumber
+ {
+ get
+ {
+ return GetInt(_keyOutputMaxNumber);
+ }
+ set
+ {
+ Set(_keyOutputMaxNumber, value > 10 ? 10 : value < 1 ? 1 : value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the threshold of confidence.
+ /// </summary>
+ /// <remarks>
+ /// The vaild range is greater than or equal to 0.0 and less than or equal to 1.0.<br/>
+ /// The value 1.0 means maximum accuracy.
+ /// </remarks>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/>is out of range.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public double ConfidenceThreshold
+ {
+ get
+ {
+ return GetDouble(_keyConfidenceThreshold);
+ }
+ set
+ {
+ if (value < 0.0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), value,
+ "Confidence threshold should be greater than or equal to 0.0.");
+ }
+ if (value > 1.0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), value,
+ "Confidence threshold should be less than or equal to 1.0.");
+ }
+
+ Set(_keyConfidenceThreshold, value);
+ }
+ }
+
+ private Rectangle? _roi;
+
+ /// <summary>
+ /// Gets or sets the ROI(Region Of Interest) of <see cref="ImageClassifier"/> and <see cref="FacialLandmarkDetector"/>
+ /// </summary>
+ /// <remarks>
+ /// Default value is null. If Roi is null, the entire region of <see cref="MediaVisionSource"/> will be analyzed.
+ /// </remarks>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// The width of <paramref name="value"/> is less than or equal to zero.<br/>
+ /// -or-<br/>
+ /// The height of <paramref name="value"/> is less than or equal to zero.<br/>
+ /// -or-<br/>
+ /// The x position of <paramref name="value"/> is less than zero.<br/>
+ /// -or-<br/>
+ /// The y position of <paramref name="value"/> is less than zero.
+ /// </exception>
+ /// <seealso cref="MaxOutputNumber"/>
+ /// <since_tizen> 6 </since_tizen>
+ public Rectangle? Roi
+ {
+ get
+ {
+ return _roi;
+ }
+ set
+ {
+ if (value != null)
+ {
+ ValidateRoi(value.Value);
+ _roi = value;
+ }
+ }
+ }
+
+ private static void ValidateRoi(Rectangle roi)
+ {
+ if (roi.Width <= 0)
+ {
+ throw new ArgumentOutOfRangeException("Roi.Width", roi.Width,
+ "The width of roi can't be less than or equal to zero.");
+ }
+
+ if (roi.Height <= 0)
+ {
+ throw new ArgumentOutOfRangeException("Roi.Height", roi.Height,
+ "The height of roi can't be less than or equal to zero.");
+ }
+
+ if (roi.X < 0)
+ {
+ throw new ArgumentOutOfRangeException("Roi.X", roi.X,
+ "The x position of roi can't be less than zero.");
+ }
+
+ if (roi.Y < 0)
+ {
+ throw new ArgumentOutOfRangeException("Roi.Y", roi.Y,
+ "The y position of roi can't be less than zero.");
+ }
+ }
+
+ /// <summary>
+ /// Releases the resources used by the <see cref="InferenceModelConfiguration"/> object.
+ /// </summary>
+ /// <param name="disposing">
+ /// true to release both managed and unmanaged resources, otherwise false to release only unmanaged resources.
+ /// </param>
+ /// <since_tizen> 6 </since_tizen>
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+
+ if (_inferenceHandle != IntPtr.Zero)
+ {
+ InteropInference.Destroy(_inferenceHandle).Validate("Failed to destroy inference configuration");
+ _inferenceHandle = IntPtr.Zero;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Tizen.Multimedia.Vision
+{
+ /// <summary>
+ /// Specifies the type of inference backend.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public enum InferenceBackendType
+ {
+ /// <summary>
+ /// OpenCV backend type
+ /// </summary>
+ OpenCV,
+
+ /// <summary>
+ /// Tensor Flow Lite backend type
+ /// </summary>
+ TFLite
+ }
+
+ /// <summary>
+ /// Specifies the type of target. It's used for running inference backend.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public enum InferenceTargetType
+ {
+ /// <summary>
+ /// CPU target
+ /// </summary>
+ CPU,
+
+ /// <summary>
+ /// GPU target
+ /// </summary>
+ GPU,
+
+ /// <summary>
+ /// Custom target
+ /// </summary>
+ Custom
+ }
+}
/// <summary>
/// Invalid path (Since 3.0).
/// </summary>
- InvalidPath = MediaVisionErrorCode | 0x04
+ InvalidPath = MediaVisionErrorCode | 0x04,
+ /// <summary>
+ /// Not supported engine.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ NotSupportedEngine = MediaVisionErrorCode | 0x05
}
internal static class MediaVisionErrorExtensions
switch (error)
{
case MediaVisionError.NotSupported:
+ case MediaVisionError.NotSupportedEngine:
throw new NotSupportedException(msg);
case MediaVisionError.MsgTooLong:
throw new ArgumentException($"{msg} : Message too long.");
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using System.Collections.ObjectModel;
+
+namespace Tizen.Multimedia.Vision
+{
+ /// <summary>
+ /// Provides the ability to get the result of object detection using <see cref="InferenceModelConfiguration"/> and
+ /// <see cref="ObjectDetector"/>.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public class ObjectDetectionResult
+ {
+ internal ObjectDetectionResult(int indice, string name, float confidence,
+ global::Interop.MediaVision.Rectangle location)
+ {
+ Indice = indice;
+ Name = name;
+ Confidence = confidence;
+ Location = location.ToApiStruct();
+ }
+
+ /// <summary>
+ /// Gets the indice of detected object.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public int Indice { get; }
+
+ /// <summary>
+ /// Gets the name of detected object.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public string Name { get; }
+
+ /// <summary>
+ /// Gets the confidence of detected object.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public float Confidence { get; }
+
+ /// <summary>
+ /// Gets the location of detected object.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public Rectangle Location { get; }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using InteropInference = Interop.MediaVision.Inference;
+
+namespace Tizen.Multimedia.Vision
+{
+ /// <summary>
+ /// Provides the ability to detect objects and get its locations on image source using inference engine.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public static class ObjectDetector
+ {
+ /// <summary>
+ /// Detects objects and gets its locations on the source image using inference engine set in <paramref name="config"/>.<br/>
+ /// Each time when DetectAsync is called, a set of the detected objects at the media source are received asynchronously.
+ /// </summary>
+ /// <feature>http://tizen.org/feature/vision.inference</feature>
+ /// <feature>http://tizen.org/feature/vision.inference.image</feature>
+ /// <param name="source">The source of the media where faces will be detected.</param>
+ /// <param name="config">The engine's configuration that will be used for detecting.</param>
+ /// <returns>
+ /// A task that represents the asynchronous detect operation.<br/>
+ /// If there's no detected object, empty collection will be returned.
+ /// </returns>
+ /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="config"/> is null.</exception>
+ /// <exception cref="InvalidOperationException">Internal error.</exception>
+ /// <exception cref="NotSupportedException">The feature is not supported.</exception>
+ /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
+ /// <seealso cref="InferenceModelConfiguration"/>
+ /// <since_tizen> 6 </since_tizen>
+ public static async Task<IEnumerable<ObjectDetectionResult>> DetectAsync(MediaVisionSource source,
+ InferenceModelConfiguration config)
+ {
+ // `vision.inference` feature is already checked, when config is created.
+ ValidationUtil.ValidateFeatureSupported(VisionFeatures.InferenceImage);
+
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+ if (config == null)
+ {
+ throw new ArgumentNullException(nameof(config));
+ }
+
+ var tcs = new TaskCompletionSource<IEnumerable<ObjectDetectionResult>>();
+
+ using (var cb = ObjectKeeper.Get(GetCallback(tcs)))
+ {
+ InteropInference.DetectObject(source.Handle, config.GetHandle(), cb.Target).
+ Validate("Failed to detect object.");
+
+ return await tcs.Task;
+ }
+ }
+
+ private static InteropInference.ObjectDetectedCallback GetCallback(TaskCompletionSource<IEnumerable<ObjectDetectionResult>> tcs)
+ {
+ return (IntPtr sourceHandle, int numberOfObjects, int[] indices, string[] names, float[] confidences,
+ global::Interop.MediaVision.Rectangle[] locations, IntPtr _) =>
+ {
+ try
+ {
+ if (!tcs.TrySetResult(GetResults(numberOfObjects, indices, names, confidences, locations)))
+ {
+ Log.Error(MediaVisionLog.Tag, "Failed to set object detection result.");
+ }
+ }
+ catch (Exception e)
+ {
+ tcs.TrySetException(e);
+ }
+ };
+ }
+
+ private static IEnumerable<ObjectDetectionResult> GetResults(int number, int[] indices,
+ string[] names, float[] confidences, global::Interop.MediaVision.Rectangle[] locations)
+ {
+ if (number == 0)
+ {
+ return Enumerable.Empty<ObjectDetectionResult>();
+ }
+
+ var results = new List<ObjectDetectionResult>();
+
+ for (int i = 0; i < number; i++)
+ {
+ results.Add(new ObjectDetectionResult(indices[i], names[i], confidences[i], locations[i]));
+ }
+
+ return results;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Tizen.Multimedia
+{
+ internal static class VisionFeatures
+ {
+ internal const string InferenceFace = "http://tizen.org/feature/vision.inference.face";
+ internal const string InferenceImage = "http://tizen.org/feature/vision.inference.image";
+ }
+}