mv_machine_learning: change async API behavior
authorInki Dae <inki.dae@samsung.com>
Wed, 23 Aug 2023 01:53:51 +0000 (10:53 +0900)
committerKwanghoon Son <k.son@samsung.com>
Mon, 4 Sep 2023 04:57:02 +0000 (13:57 +0900)
[Issue type] : code refactoring

Change async API behavior of object detection task group.

In original version, async API created an internal thread which is
different from main thread because the callback function registered by
user was called by the new thread context created by the internal framework.

With this patch, user has to create a new thread for the use of async API.
Below is an simple example,
void thread_cb(...)
{
...
while (condition) {
...
mv_object_detection_get_result(handle, &number_of_objects, ...);
...

for (unsigned int idx = 0; idx < number_of_objects; ++idx) {
...
mv_object_detection_get_label(handle, idx, &label);
...
}
}
}

In addition, this patch drops the use of mutex for thread safety due to
dead lock issue. I will introduce a new thread safety solution with
more fine-grained lock approach later.

Change-Id: I7621097a7b08456d61af79ca2659546083e0ece0
Signed-off-by: Inki Dae <inki.dae@samsung.com>
include/mv_face_detection_internal.h
include/mv_object_detection_internal.h
mv_machine_learning/object_detection/include/object_detection.h
mv_machine_learning/object_detection/include/object_detection_type.h
mv_machine_learning/object_detection/src/mv_face_detection.cpp
mv_machine_learning/object_detection/src/mv_object_detection.cpp
mv_machine_learning/object_detection/src/object_detection.cpp
mv_machine_learning/object_detection/src/object_detection_adapter.cpp
test/testsuites/machine_learning/object_detection/CMakeLists.txt
test/testsuites/machine_learning/object_detection/test_object_detection.cpp
test/testsuites/machine_learning/object_detection/test_object_detection_async.cpp

index 208a246..2cec2a4 100644 (file)
@@ -169,15 +169,14 @@ int mv_face_detection_inference(mv_face_detection_h handle, mv_source_h source);
  * @internal
  * @brief Performs asynchronously the face detection inference on the @a source.
  *
- * @since_tizen 7.5
+ * @since_tizen 8.0
  * @remarks This function operates asynchronously, so it returns immediately upon invocation.
-  *         Therefore, user needs to receive the result though a given callback function.
+ *          The inference results are inserted into the outgoing queue within the framework
+ *          in the order of processing, and the results can be obtained through mv_face_detection_get_result()
+ *          and mv_face_detection_get_label().
  *
  * @param[in] handle         The handle to the inference
  * @param[in] source         The handle to the source of the media
- * @param[in] completion_cb  A callback which is called internally by the framework
- *                           once the given inference request is completed.
- * @param[in] user_data      A pointer to user data object.
  *
  * @return @c 0 on success, otherwise a negative error value
  * @retval #MEDIA_VISION_ERROR_NONE Successful
@@ -192,8 +191,7 @@ int mv_face_detection_inference(mv_face_detection_h handle, mv_source_h source);
  * @pre Prepare an inference by calling mv_face_detect_configure()
  * @pre Prepare an inference by calling mv_face_detect_prepare()
  */
-int mv_face_detection_inference_async(mv_face_detection_h handle, mv_source_h source, mv_completion_cb completion_cb,
-                                                                         void *user_data);
+int mv_face_detection_inference_async(mv_face_detection_h handle, mv_source_h source);
 
 /**
  * @internal
@@ -201,9 +199,9 @@ int mv_face_detection_inference_async(mv_face_detection_h handle, mv_source_h so
  *
  * @since_tizen 8.0
  *
- * @param[in] handle               The handle to the inference
+ * @param[in] handle              The handle to the inference
  * @param[out] number_of_objects  A number of objects detected.
- * @param[out] indices            Label indices to detected objects.
+ * @param[out] frame_number       A frame number inferenced.
  * @param[out] confidences        Probability to detected objects.
  * @param[out] left               An left position array to bound boxes.
  * @param[out] top                An top position array to bound boxes.
@@ -223,7 +221,7 @@ int mv_face_detection_inference_async(mv_face_detection_h handle, mv_source_h so
  * @pre Prepare an inference by calling mv_face_detect_inference()
  */
 int mv_face_detection_get_result(mv_face_detection_h handle, unsigned int *number_of_objects,
-                                                                const unsigned int **indices, const float **confidences, const int **left,
+                                                                unsigned long *frame_number, const float **confidences, const int **left,
                                                                 const int **top, const int **right, const int **bottom);
 
 /**
index 0b4aa00..cf783c9 100644 (file)
@@ -169,15 +169,14 @@ int mv_object_detection_inference(mv_object_detection_h infer, mv_source_h sourc
  * @internal
  * @brief Performs asynchronously the object detection inference on the @a source.
  *
- * @since_tizen 7.5
+ * @since_tizen 8.0
  * @remarks This function operates asynchronously, so it returns immediately upon invocation.
-  *         Therefore, user needs to receive the result though a given callback function.
+ *          The inference results are inserted into the outgoing queue within the framework
+ *          in the order of processing, and the results can be obtained through mv_object_detection_get_result()
+ *          and mv_object_detection_get_label().
  *
  * @param[in] handle         The handle to the inference
  * @param[in] source         The handle to the source of the media
- * @param[in] completion_cb  A callback which is called internally by the framework
- *                           once the given inference request is completed.
- * @param[in] user_data      A pointer to user data object.
  *
  * @return @c 0 on success, otherwise a negative error value
  * @retval #MEDIA_VISION_ERROR_NONE Successful
@@ -192,8 +191,7 @@ int mv_object_detection_inference(mv_object_detection_h infer, mv_source_h sourc
  * @pre Prepare an inference by calling mv_object_detect_configure()
  * @pre Prepare an inference by calling mv_object_detect_prepare()
  */
-int mv_object_detection_inference_async(mv_object_detection_h handle, mv_source_h source,
-                                                                               mv_completion_cb completion_cb, void *user_data);
+int mv_object_detection_inference_async(mv_object_detection_h handle, mv_source_h source);
 
 /**
  * @internal
@@ -203,7 +201,7 @@ int mv_object_detection_inference_async(mv_object_detection_h handle, mv_source_
  *
  * @param[in] infer               The handle to the inference
  * @param[out] number_of_objects  A number of objects detected.
- * @param[out] indices            Label indices to detected objects.
+ * @param[out] frame_number       A frame number inferenced.
  * @param[out] confidences        Probability to detected objects.
  * @param[out] left               An left position array to bound boxes.
  * @param[out] top                An top position array to bound boxes.
@@ -223,7 +221,7 @@ int mv_object_detection_inference_async(mv_object_detection_h handle, mv_source_
  * @pre Prepare an inference by calling mv_object_detect_inference()
  */
 int mv_object_detection_get_result(mv_object_detection_h infer, unsigned int *number_of_objects,
-                                                                  const unsigned int **indices, const float **confidences, const int **left,
+                                                                  unsigned long *frame_number, const float **confidences, const int **left,
                                                                   const int **top, const int **right, const int **bottom);
 
 /**
index 7086632..ff9c90f 100644 (file)
@@ -20,6 +20,9 @@
 #include <queue>
 #include <thread>
 #include <mutex>
+#include <condition_variable>
+#include <atomic>
+
 #include <mv_common.h>
 #include <mv_inference_type.h>
 #include "mv_private.h"
@@ -40,31 +43,31 @@ class ObjectDetection : public IObjectDetection
 {
 private:
        ObjectDetectionTaskType _task_type;
-       template<typename T> std::queue<ObjectDetectionQueue<T> > static _incoming_queue;
+       std::queue<ObjectDetectionQueue<unsigned char> > _incoming_queue;
        std::queue<ObjectDetectionResult> _outgoing_queue;
        std::mutex _incoming_queue_mutex;
        std::mutex _outgoing_queue_mutex;
-       int _input_data_type {};
        std::unique_ptr<std::thread> _thread_handle;
-       bool _exit_thread {};
+       std::atomic<bool> _exit_thread { false };
        ObjectDetectionResult _current_result {};
-       unsigned long _input_index {};
+       unsigned long _input_frame_number {};
+       std::condition_variable _cv_event;
 
        void loadLabel();
        void getEngineList();
        void getDeviceList(const char *engine_type);
        template<typename T>
        void preprocess(mv_source_h &mv_src, std::shared_ptr<MetaInfo> metaInfo, std::vector<T> &inputVector);
-       template<typename T> void pushToInput(ObjectDetectionQueue<T> &input);
+       template<typename T> void pushToInput(ObjectDetectionQueue<T> &inputQueue);
        ObjectDetectionResult popFromOutput();
        bool isOutputQueueEmpty();
+       void waitforOutputQueue();
        template<typename T> ObjectDetectionQueue<T> popFromInput();
        template<typename T> bool isInputQueueEmpty();
        void pushToOutput(ObjectDetectionResult &output);
        std::shared_ptr<MetaInfo> getInputMetaInfo();
        template<typename T> void perform(mv_source_h &mv_src, std::shared_ptr<MetaInfo> metaInfo);
        template<typename T> void performAsync(ObjectDetectionInput &input, std::shared_ptr<MetaInfo> metaInfo);
-       bool exitThread();
 
 protected:
        std::unique_ptr<mediavision::inference::Inference> _inference;
index 8c04c4b..2084035 100644 (file)
@@ -28,16 +28,13 @@ namespace machine_learning
 struct ObjectDetectionInput {
        void *handle {};
        mv_source_h inference_src;
-       mv_completion_cb completion_cb;
-       void *user_data {};
        // TODO.
 };
 
 template<typename T> struct ObjectDetectionQueue {
-       unsigned long index {};
+       unsigned long frame_number {};
        void *handle {};
        mv_source_h inference_src;
-       mv_completion_cb completion_cb;
        std::vector<std::vector<T> > inputs;
        void *user_data {};
 };
@@ -47,6 +44,7 @@ template<typename T> struct ObjectDetectionQueue {
  * @details Contains object detection result.
  */
 struct ObjectDetectionResult {
+       unsigned long frame_number;
        unsigned int number_of_objects {};
        std::vector<unsigned int> indices;
        std::vector<std::string> names;
index 5521b1c..ce3e90e 100644 (file)
@@ -37,8 +37,6 @@ using namespace MediaVision::Common;
 using namespace mediavision::machine_learning::exception;
 using FaceDetectionTask = ITask<ObjectDetectionInput, ObjectDetectionResult>;
 
-static mutex g_face_detection_mutex;
-
 int mv_face_detection_create(mv_face_detection_h *handle)
 {
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_check_system_info_feature_supported());
@@ -67,13 +65,6 @@ int mv_face_detection_create(mv_face_detection_h *handle)
 
 int mv_face_detection_destroy(mv_face_detection_h handle)
 {
-       // TODO. find proper solution later.
-       // For thread safety, lock is needed here but if async API is used then dead lock occurs
-       // because mv_face_detection_destroy_open function acquires a lock and,
-       // while waiting for the thread loop to finish, the same lock is also acquired
-       // within functions - mv_face_detection_get_result_open and mv_face_detection_get_label_open
-       // - called to obtain results from the thread loop.
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_check_system_info_feature_supported());
        MEDIA_VISION_INSTANCE_CHECK(handle);
 
@@ -94,8 +85,6 @@ int mv_face_detection_destroy(mv_face_detection_h handle)
 int mv_face_detection_set_model(mv_face_detection_h handle, const char *model_name, const char *model_file,
                                                                const char *meta_file, const char *label_file)
 {
-       lock_guard<mutex> lock(g_face_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_face_check_system_info_feature_supported());
 
        MEDIA_VISION_INSTANCE_CHECK(handle);
@@ -123,8 +112,6 @@ int mv_face_detection_set_model(mv_face_detection_h handle, const char *model_na
 
 int mv_face_detection_set_engine(mv_face_detection_h handle, const char *backend_type, const char *device_type)
 {
-       lock_guard<mutex> lock(g_face_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_face_check_system_info_feature_supported());
 
        MEDIA_VISION_INSTANCE_CHECK(handle);
@@ -150,8 +137,6 @@ int mv_face_detection_set_engine(mv_face_detection_h handle, const char *backend
 
 int mv_face_detection_get_engine_count(mv_face_detection_h handle, unsigned int *engine_count)
 {
-       lock_guard<mutex> lock(g_face_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_face_check_system_info_feature_supported());
 
        MEDIA_VISION_INSTANCE_CHECK(handle);
@@ -177,8 +162,6 @@ int mv_face_detection_get_engine_count(mv_face_detection_h handle, unsigned int
 
 int mv_face_detection_get_engine_type(mv_face_detection_h handle, const unsigned int engine_index, char **engine_type)
 {
-       lock_guard<mutex> lock(g_face_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_face_check_system_info_feature_supported());
 
        MEDIA_VISION_INSTANCE_CHECK(handle);
@@ -204,8 +187,6 @@ int mv_face_detection_get_engine_type(mv_face_detection_h handle, const unsigned
 
 int mv_face_detection_get_device_count(mv_face_detection_h handle, const char *engine_type, unsigned int *device_count)
 {
-       lock_guard<mutex> lock(g_face_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_face_check_system_info_feature_supported());
 
        MEDIA_VISION_INSTANCE_CHECK(handle);
@@ -232,8 +213,6 @@ int mv_face_detection_get_device_count(mv_face_detection_h handle, const char *e
 int mv_face_detection_get_device_type(mv_face_detection_h handle, const char *engine_type,
                                                                          const unsigned int device_index, char **device_type)
 {
-       lock_guard<mutex> lock(g_face_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_face_check_system_info_feature_supported());
 
        MEDIA_VISION_INSTANCE_CHECK(handle);
@@ -260,8 +239,6 @@ int mv_face_detection_get_device_type(mv_face_detection_h handle, const char *en
 
 int mv_face_detection_configure(mv_face_detection_h handle)
 {
-       lock_guard<mutex> lock(g_face_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_check_system_info_feature_supported());
        MEDIA_VISION_INSTANCE_CHECK(handle);
 
@@ -284,8 +261,6 @@ int mv_face_detection_configure(mv_face_detection_h handle)
 
 int mv_face_detection_prepare(mv_face_detection_h handle)
 {
-       lock_guard<mutex> lock(g_face_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_check_system_info_feature_supported());
        MEDIA_VISION_INSTANCE_CHECK(handle);
 
@@ -308,8 +283,6 @@ int mv_face_detection_prepare(mv_face_detection_h handle)
 
 int mv_face_detection_inference(mv_face_detection_h handle, mv_source_h source)
 {
-       lock_guard<mutex> lock(g_face_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_image_check_system_info_feature_supported());
        MEDIA_VISION_INSTANCE_CHECK(source);
        MEDIA_VISION_INSTANCE_CHECK(handle);
@@ -334,12 +307,13 @@ int mv_face_detection_inference(mv_face_detection_h handle, mv_source_h source)
        return MEDIA_VISION_ERROR_NONE;
 }
 
-int mv_face_detection_inference_async(mv_face_detection_h handle, mv_source_h source, mv_completion_cb completion_cb,
-                                                                         void *user_data)
+int mv_face_detection_inference_async(mv_face_detection_h handle, mv_source_h source)
 {
-       LOGD("ENTER");
+       MEDIA_VISION_SUPPORT_CHECK(_mv_inference_image_check_system_info_feature_supported());
+       MEDIA_VISION_INSTANCE_CHECK(handle);
+       MEDIA_VISION_INSTANCE_CHECK(source);
 
-       lock_guard<mutex> lock(g_face_detection_mutex);
+       MEDIA_VISION_FUNCTION_ENTER();
 
        if (!handle) {
                LOGE("Handle is NULL.");
@@ -350,7 +324,7 @@ int mv_face_detection_inference_async(mv_face_detection_h handle, mv_source_h so
                auto context = static_cast<Context *>(handle);
                auto task = static_cast<FaceDetectionTask *>(context->__tasks.at("face_detection"));
 
-               ObjectDetectionInput input = { handle, source, completion_cb, user_data };
+               ObjectDetectionInput input = { handle, source };
 
                task->performAsync(input);
        } catch (const BaseException &e) {
@@ -364,15 +338,13 @@ int mv_face_detection_inference_async(mv_face_detection_h handle, mv_source_h so
 }
 
 int mv_face_detection_get_result(mv_face_detection_h handle, unsigned int *number_of_objects,
-                                                                const unsigned int **indices, const float **confidences, const int **left,
+                                                                unsigned long *frame_number, const float **confidences, const int **left,
                                                                 const int **top, const int **right, const int **bottom)
 {
-       lock_guard<mutex> lock(g_face_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_image_check_system_info_feature_supported());
        MEDIA_VISION_INSTANCE_CHECK(handle);
        MEDIA_VISION_INSTANCE_CHECK(number_of_objects);
-       MEDIA_VISION_INSTANCE_CHECK(indices);
+       MEDIA_VISION_INSTANCE_CHECK(frame_number);
        MEDIA_VISION_INSTANCE_CHECK(confidences);
        MEDIA_VISION_INSTANCE_CHECK(left);
        MEDIA_VISION_INSTANCE_CHECK(top);
@@ -387,7 +359,7 @@ int mv_face_detection_get_result(mv_face_detection_h handle, unsigned int *numbe
 
                ObjectDetectionResult &result = task->getOutput();
                *number_of_objects = result.number_of_objects;
-               *indices = result.indices.data();
+               *frame_number = result.frame_number;
                *confidences = result.confidences.data();
                *left = result.left.data();
                *top = result.top.data();
@@ -405,8 +377,6 @@ int mv_face_detection_get_result(mv_face_detection_h handle, unsigned int *numbe
 
 int mv_face_detection_get_label(mv_face_detection_h handle, const unsigned int index, const char **out_label)
 {
-       lock_guard<mutex> lock(g_face_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_image_check_system_info_feature_supported());
        MEDIA_VISION_INSTANCE_CHECK(handle);
        MEDIA_VISION_INSTANCE_CHECK(out_label);
index c84f20d..29c20da 100644 (file)
@@ -37,8 +37,6 @@ using namespace MediaVision::Common;
 using namespace mediavision::machine_learning::exception;
 using ObjectDetectionTask = ITask<ObjectDetectionInput, ObjectDetectionResult>;
 
-static mutex g_object_detection_mutex;
-
 int mv_object_detection_create(mv_object_detection_h *handle)
 {
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_check_system_info_feature_supported());
@@ -72,13 +70,6 @@ int mv_object_detection_destroy(mv_object_detection_h handle)
 
        MEDIA_VISION_FUNCTION_ENTER();
 
-       // TODO. find proper solution later.
-       // For thread safety, lock is needed here but if async API is used then dead lock occurs
-       // because mv_object_detection_destroy_open function acquires a lock and,
-       // while waiting for the thread loop to finish, the same lock is also acquired
-       // within functions - mv_object_detection_get_result_open and mv_object_detection_get_label_open
-       // - called to obtain results from the thread loop.
-
        auto context = static_cast<Context *>(handle);
 
        for (auto &m : context->__tasks)
@@ -94,8 +85,6 @@ int mv_object_detection_destroy(mv_object_detection_h handle)
 int mv_object_detection_set_model(mv_object_detection_h handle, const char *model_name, const char *model_file,
                                                                  const char *meta_file, const char *label_file)
 {
-       lock_guard<mutex> lock(g_object_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_face_check_system_info_feature_supported());
 
        MEDIA_VISION_INSTANCE_CHECK(handle);
@@ -119,8 +108,6 @@ int mv_object_detection_set_model(mv_object_detection_h handle, const char *mode
 
 int mv_object_detection_set_engine(mv_object_detection_h handle, const char *backend_type, const char *device_type)
 {
-       lock_guard<mutex> lock(g_object_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_face_check_system_info_feature_supported());
 
        MEDIA_VISION_INSTANCE_CHECK(handle);
@@ -146,8 +133,6 @@ int mv_object_detection_set_engine(mv_object_detection_h handle, const char *bac
 
 int mv_object_detection_get_engine_count(mv_object_detection_h handle, unsigned int *engine_count)
 {
-       lock_guard<mutex> lock(g_object_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_face_check_system_info_feature_supported());
 
        MEDIA_VISION_INSTANCE_CHECK(handle);
@@ -174,8 +159,6 @@ int mv_object_detection_get_engine_count(mv_object_detection_h handle, unsigned
 int mv_object_detection_get_engine_type(mv_object_detection_h handle, const unsigned int engine_index,
                                                                                char **engine_type)
 {
-       lock_guard<mutex> lock(g_object_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_face_check_system_info_feature_supported());
 
        MEDIA_VISION_INSTANCE_CHECK(handle);
@@ -202,8 +185,6 @@ int mv_object_detection_get_engine_type(mv_object_detection_h handle, const unsi
 int mv_object_detection_get_device_count(mv_object_detection_h handle, const char *engine_type,
                                                                                 unsigned int *device_count)
 {
-       lock_guard<mutex> lock(g_object_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_face_check_system_info_feature_supported());
 
        MEDIA_VISION_INSTANCE_CHECK(handle);
@@ -230,8 +211,6 @@ int mv_object_detection_get_device_count(mv_object_detection_h handle, const cha
 int mv_object_detection_get_device_type(mv_object_detection_h handle, const char *engine_type,
                                                                                const unsigned int device_index, char **device_type)
 {
-       lock_guard<mutex> lock(g_object_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_face_check_system_info_feature_supported());
 
        MEDIA_VISION_INSTANCE_CHECK(handle);
@@ -258,8 +237,6 @@ int mv_object_detection_get_device_type(mv_object_detection_h handle, const char
 
 int mv_object_detection_configure(mv_object_detection_h handle)
 {
-       lock_guard<mutex> lock(g_object_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_check_system_info_feature_supported());
        MEDIA_VISION_INSTANCE_CHECK(handle);
 
@@ -282,8 +259,6 @@ int mv_object_detection_configure(mv_object_detection_h handle)
 
 int mv_object_detection_prepare(mv_object_detection_h handle)
 {
-       lock_guard<mutex> lock(g_object_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_check_system_info_feature_supported());
        MEDIA_VISION_INSTANCE_CHECK(handle);
 
@@ -306,8 +281,6 @@ int mv_object_detection_prepare(mv_object_detection_h handle)
 
 int mv_object_detection_inference(mv_object_detection_h handle, mv_source_h source)
 {
-       lock_guard<mutex> lock(g_object_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_image_check_system_info_feature_supported());
        MEDIA_VISION_INSTANCE_CHECK(handle);
        MEDIA_VISION_INSTANCE_CHECK(source);
@@ -332,15 +305,11 @@ int mv_object_detection_inference(mv_object_detection_h handle, mv_source_h sour
        return MEDIA_VISION_ERROR_NONE;
 }
 
-int mv_object_detection_inference_async(mv_object_detection_h handle, mv_source_h source,
-                                                                               mv_completion_cb completion_cb, void *user_data)
+int mv_object_detection_inference_async(mv_object_detection_h handle, mv_source_h source)
 {
-       lock_guard<mutex> lock(g_object_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_image_check_system_info_feature_supported());
        MEDIA_VISION_INSTANCE_CHECK(handle);
        MEDIA_VISION_INSTANCE_CHECK(source);
-       MEDIA_VISION_INSTANCE_CHECK(completion_cb);
 
        MEDIA_VISION_FUNCTION_ENTER();
 
@@ -348,7 +317,7 @@ int mv_object_detection_inference_async(mv_object_detection_h handle, mv_source_
                auto context = static_cast<Context *>(handle);
                auto task = static_cast<ObjectDetectionTask *>(context->__tasks.at("object_detection"));
 
-               ObjectDetectionInput input = { handle, source, completion_cb, user_data };
+               ObjectDetectionInput input = { handle, source };
 
                task->performAsync(input);
        } catch (const BaseException &e) {
@@ -362,15 +331,13 @@ int mv_object_detection_inference_async(mv_object_detection_h handle, mv_source_
 }
 
 int mv_object_detection_get_result(mv_object_detection_h handle, unsigned int *number_of_objects,
-                                                                  const unsigned int **indices, const float **confidences, const int **left,
+                                                                  unsigned long *frame_number, const float **confidences, const int **left,
                                                                   const int **top, const int **right, const int **bottom)
 {
-       lock_guard<mutex> lock(g_object_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_image_check_system_info_feature_supported());
        MEDIA_VISION_INSTANCE_CHECK(handle);
        MEDIA_VISION_INSTANCE_CHECK(number_of_objects);
-       MEDIA_VISION_INSTANCE_CHECK(indices);
+       MEDIA_VISION_INSTANCE_CHECK(frame_number);
        MEDIA_VISION_INSTANCE_CHECK(confidences);
        MEDIA_VISION_INSTANCE_CHECK(left);
        MEDIA_VISION_INSTANCE_CHECK(top);
@@ -385,7 +352,7 @@ int mv_object_detection_get_result(mv_object_detection_h handle, unsigned int *n
 
                ObjectDetectionResult &result = task->getOutput();
                *number_of_objects = result.number_of_objects;
-               *indices = result.indices.data();
+               *frame_number = result.frame_number;
                *confidences = result.confidences.data();
                *left = result.left.data();
                *top = result.top.data();
@@ -403,8 +370,6 @@ int mv_object_detection_get_result(mv_object_detection_h handle, unsigned int *n
 
 int mv_object_detection_get_label(mv_object_detection_h handle, const unsigned int index, const char **label)
 {
-       lock_guard<mutex> lock(g_object_detection_mutex);
-
        MEDIA_VISION_SUPPORT_CHECK(_mv_inference_image_check_system_info_feature_supported());
        MEDIA_VISION_INSTANCE_CHECK(handle);
        MEDIA_VISION_INSTANCE_CHECK(label);
index 14681fd..5f39359 100644 (file)
@@ -26,6 +26,7 @@
 #include "object_detection.h"
 
 using namespace std;
+using namespace std::chrono_literals;
 using namespace mediavision::inference;
 using namespace MediaVision::Common;
 using namespace mediavision::common;
@@ -44,15 +45,19 @@ ObjectDetection::ObjectDetection(ObjectDetectionTaskType task_type)
 
 void ObjectDetection::preDestroy()
 {
-       if (_thread_handle) {
-               _exit_thread = true;
-               _thread_handle->join();
-       }
-}
+       if (!_thread_handle)
+               return;
 
-bool ObjectDetection::exitThread()
-{
-       return _exit_thread;
+       // Make sure to wait for the completion of all inference requests in the incoming queue.
+       _exit_thread = true;
+
+       _thread_handle->join();
+       _thread_handle = nullptr;
+
+       lock_guard<mutex> lock(_outgoing_queue_mutex);
+       queue<ObjectDetectionResult> empty;
+
+       swap(_outgoing_queue, empty);
 }
 
 ObjectDetectionTaskType ObjectDetection::getTaskType()
@@ -340,6 +345,8 @@ template<typename T> void ObjectDetection::perform(mv_source_h &mv_src, shared_p
        vector<vector<T> > inputVectors = { inputVector };
 
        inference<T>(inputVectors);
+
+       // TODO. Update operation status here.
 }
 
 void ObjectDetection::perform(mv_source_h &mv_src)
@@ -356,28 +363,31 @@ void ObjectDetection::perform(mv_source_h &mv_src)
 template<typename T> void inferenceThreadLoop(ObjectDetection *object)
 {
        // If user called destroy API then this thread loop will be terminated.
-       while (!object->exitThread() || !object->isInputQueueEmpty<T>()) {
+       while (!object->_exit_thread) {
                // If input queue is empty then skip inference request.
                if (object->isInputQueueEmpty<T>())
                        continue;
 
                ObjectDetectionQueue<T> input = object->popFromInput<T>();
 
-               LOGD("Popped : input index = %lu", input.index);
+               LOGD("Popped : input frame number = %lu", input.frame_number);
 
                object->inference<T>(input.inputs);
 
                ObjectDetectionResult &result = object->result();
+               result.frame_number = input.frame_number;
 
                object->pushToOutput(result);
-
-               input.completion_cb(input.handle, input.user_data);
        }
+
+       // waitforOutputQueue function could wait for the notify event after while loop is exited.
+       // So make sure to call notify_one() here.
+       object->_cv_event.notify_one();
 }
 
 template<typename T> void ObjectDetection::performAsync(ObjectDetectionInput &input, shared_ptr<MetaInfo> metaInfo)
 {
-       _input_index++;
+       _input_frame_number++;
 
        if (!isInputQueueEmpty<T>())
                return;
@@ -387,11 +397,10 @@ template<typename T> void ObjectDetection::performAsync(ObjectDetectionInput &in
        preprocess<T>(input.inference_src, metaInfo, inputVector);
 
        vector<vector<T> > inputVectors = { inputVector };
-       ObjectDetectionQueue<T> in_queue = { _input_index,                input.handle, input.inference_src,
-                                                                                input.completion_cb, inputVectors, input.user_data };
+       ObjectDetectionQueue<T> in_queue = { _input_frame_number, input.handle, input.inference_src, inputVectors };
 
        pushToInput<T>(in_queue);
-       LOGD("Pushed : input index = %lu", in_queue.index);
+       LOGD("Pushed : input frame number = %lu", in_queue.frame_number);
 
        if (!_thread_handle)
                _thread_handle = make_unique<thread>(&inferenceThreadLoop<T>, this);
@@ -414,11 +423,14 @@ void ObjectDetection::performAsync(ObjectDetectionInput &input)
 ObjectDetectionResult &ObjectDetection::getOutput()
 {
        if (_thread_handle) {
-               if (isOutputQueueEmpty())
-                       throw InvalidOperation("No inference result.");
+               if (_exit_thread)
+                       throw InvalidOperation("Object detection is already destroyed so invalid operation.");
 
+               waitforOutputQueue();
                _current_result = popFromOutput();
        } else {
+               // TODO. Check if inference request is completed or not here.
+               //       If not then throw an exception.
                _current_result = result();
        }
 
@@ -452,31 +464,73 @@ void ObjectDetection::getOutputTensor(string target_name, vector<float> &tensor)
        copy(&raw_buffer[0], &raw_buffer[tensor_buffer->size / sizeof(float)], back_inserter(tensor));
 }
 
-template<typename T> void ObjectDetection::pushToInput(ObjectDetectionQueue<T> &input)
+template<typename T> void ObjectDetection::pushToInput(ObjectDetectionQueue<T> &inputQueue)
 {
        lock_guard<mutex> lock(_incoming_queue_mutex);
-       _incoming_queue<T>.push(input);
+       ObjectDetectionQueue<unsigned char> dstQueue;
+
+       dstQueue.frame_number = inputQueue.frame_number;
+       dstQueue.handle = inputQueue.handle;
+       dstQueue.inference_src = inputQueue.inference_src;
+       dstQueue.user_data = inputQueue.user_data;
+
+       for (auto &elms : inputQueue.inputs) {
+               vector<unsigned char> dst_vector;
+
+               for (auto &elm : elms) {
+                       unsigned char *bytes = reinterpret_cast<unsigned char *>(&elm);
+
+                       copy_n(bytes, sizeof(T), back_inserter(dst_vector));
+               }
+
+               dstQueue.inputs.push_back(dst_vector);
+       }
+
+       _incoming_queue.push(dstQueue);
 }
 
 template<typename T> ObjectDetectionQueue<T> ObjectDetection::popFromInput()
 {
        lock_guard<mutex> lock(_incoming_queue_mutex);
-       ObjectDetectionQueue<T> input = _incoming_queue<T>.front();
-       _incoming_queue<T>.pop();
+       ObjectDetectionQueue<unsigned char> inputQueue = _incoming_queue.front();
+
+       _incoming_queue.pop();
+       ObjectDetectionQueue<T> dstQueue;
+
+       dstQueue.frame_number = inputQueue.frame_number;
+       dstQueue.handle = inputQueue.handle;
+       dstQueue.inference_src = inputQueue.inference_src;
+       dstQueue.user_data = inputQueue.user_data;
 
-       return input;
+       for (auto &elms : inputQueue.inputs) {
+               vector<T> dst_vector;
+
+               for (size_t idx = 0; idx < elms.size(); idx += sizeof(T)) {
+                       T dst_data;
+
+                       copy_n(elms.begin() + idx, sizeof(T), reinterpret_cast<unsigned char *>(&dst_data));
+                       dst_vector.push_back(dst_data);
+               }
+
+               dstQueue.inputs.push_back(dst_vector);
+       }
+
+       return dstQueue;
 }
 
 template<typename T> bool ObjectDetection::isInputQueueEmpty()
 {
        lock_guard<mutex> lock(_incoming_queue_mutex);
-       return _incoming_queue<T>.empty();
+
+       return _incoming_queue.empty();
 }
 
 void ObjectDetection::pushToOutput(ObjectDetectionResult &output)
 {
        lock_guard<mutex> lock(_outgoing_queue_mutex);
+
        _outgoing_queue.push(output);
+       _cv_event.notify_one();
 }
 
 ObjectDetectionResult ObjectDetection::popFromOutput()
@@ -495,13 +549,25 @@ bool ObjectDetection::isOutputQueueEmpty()
        return _outgoing_queue.empty();
 }
 
-template<typename T> queue<ObjectDetectionQueue<T> > ObjectDetection::_incoming_queue;
+void ObjectDetection::waitforOutputQueue()
+{
+       unique_lock<mutex> lock(_outgoing_queue_mutex);
+
+       if (!_cv_event.wait_for(lock, 10s, [this] {
+                       if (_exit_thread)
+                               throw InvalidOperation("already thread exit");
+
+                       return !_outgoing_queue.empty();
+               })) {
+               throw InvalidOperation("Waiting for output queue has been timed out.");
+       }
+}
 
 template void ObjectDetection::preprocess<float>(mv_source_h &mv_src, shared_ptr<MetaInfo> metaInfo,
                                                                                                 vector<float> &inputVector);
 template void ObjectDetection::inference<float>(vector<vector<float> > &inputVectors);
 template void ObjectDetection::perform<float>(mv_source_h &mv_src, shared_ptr<MetaInfo> metaInfo);
-template void ObjectDetection::pushToInput<float>(ObjectDetectionQueue<float> &input);
+template void ObjectDetection::pushToInput<float>(ObjectDetectionQueue<float> &inputQueue);
 template ObjectDetectionQueue<float> ObjectDetection::popFromInput();
 template bool ObjectDetection::isInputQueueEmpty<float>();
 template void ObjectDetection::performAsync<float>(ObjectDetectionInput &input, shared_ptr<MetaInfo> metaInfo);
@@ -510,7 +576,7 @@ template void ObjectDetection::preprocess<unsigned char>(mv_source_h &mv_src, sh
                                                                                                                 vector<unsigned char> &inputVector);
 template void ObjectDetection::inference<unsigned char>(vector<vector<unsigned char> > &inputVectors);
 template void ObjectDetection::perform<unsigned char>(mv_source_h &mv_src, shared_ptr<MetaInfo> metaInfo);
-template void ObjectDetection::pushToInput<unsigned char>(ObjectDetectionQueue<unsigned char> &input);
+template void ObjectDetection::pushToInput<unsigned char>(ObjectDetectionQueue<unsigned char> &inputQueue);
 template ObjectDetectionQueue<unsigned char> ObjectDetection::popFromInput();
 template bool ObjectDetection::isInputQueueEmpty<unsigned char>();
 
index eb80a63..bb86362 100644 (file)
@@ -81,9 +81,9 @@ ObjectDetectionTaskType ObjectDetectionAdapter<T, V>::convertToTaskType(string m
                return ObjectDetectionTaskType::OD_PLUGIN;
        else if (model_name == "FD_PLUGIN")
                return ObjectDetectionTaskType::FD_PLUGIN;
-       else if (model_name == string("MOBILENET_V1_SSD"))
+       else if (model_name == "MOBILENET_V1_SSD")
                return ObjectDetectionTaskType::MOBILENET_V1_SSD;
-       else if (model_name == string("MOBILENET_V2_SSD"))
+       else if (model_name == "MOBILENET_V2_SSD")
                return ObjectDetectionTaskType::MOBILENET_V2_SSD;
        // TODO.
 
index 2b0cc74..923b56f 100644 (file)
@@ -15,7 +15,7 @@ target_link_libraries(${TEST_OBJECT_DETECTION} gtest gtest_main
 )
 
 target_compile_definitions(${TEST_OBJECT_DETECTION_ASYNC} PRIVATE -DTEST_RES_PATH="${TEST_RES_PATH}")
-target_link_libraries(${TEST_OBJECT_DETECTION_ASYNC} gtest gtest_main
+target_link_libraries(${TEST_OBJECT_DETECTION_ASYNC} gtest gtest_main pthread
                       mv_inference
                       mv_object_detection
                       mv_image_helper
index a82e3d9..5f045f6 100644 (file)
@@ -172,24 +172,24 @@ TEST(ObjectDetectionTest, InferenceShouldBeOk)
 
                unsigned int number_of_objects;
                const int *left, *top, *right, *bottom;
-               const unsigned int *indices;
+               unsigned long frame_number;
                const float *confidences;
 
-               ret = mv_object_detection_get_result(handle, &number_of_objects, &indices, &confidences, &left, &top, &right,
-                                                                                        &bottom);
+               ret = mv_object_detection_get_result(handle, &number_of_objects, &frame_number, &confidences, &left, &top,
+                                                                                        &right, &bottom);
                ASSERT_EQ(ret, 0);
 
                for (unsigned int idx = 0; idx < number_of_objects; ++idx) {
-                       cout << "index = " << indices[idx] << " probability = " << confidences[idx] << " " << left[idx] << " x "
-                                << top[idx] << " ~ " << right[idx] << " x " << bottom[idx] << endl;
+                       cout << "Frame number = " << frame_number << " probability = " << confidences[idx] << " " << left[idx]
+                                << " x " << top[idx] << " ~ " << right[idx] << " x " << bottom[idx] << endl;
                }
 
                for (unsigned int idx = 0; idx < number_of_objects; ++idx) {
                        const char *label;
 
-                       ret = mv_object_detection_get_label(handle, indices[idx], &label);
+                       ret = mv_object_detection_get_label(handle, idx, &label);
                        ASSERT_EQ(ret, 0);
-                       cout << "index = " << indices[idx] << " label = " << label << endl;
+                       cout << "index = " << idx << " label = " << label << endl;
 
                        string label_str(label);
 
@@ -244,24 +244,24 @@ TEST(FaceDetectionTest, InferenceShouldBeOk)
 
                unsigned int number_of_objects;
                const int *left, *top, *right, *bottom;
-               const unsigned int *indices;
+               unsigned long frame_number;
                const float *confidences;
 
-               ret = mv_face_detection_get_result(handle, &number_of_objects, &indices, &confidences, &left, &top, &right,
+               ret = mv_face_detection_get_result(handle, &number_of_objects, &frame_number, &confidences, &left, &top, &right,
                                                                                   &bottom);
                ASSERT_EQ(ret, 0);
 
                for (unsigned int idx = 0; idx < number_of_objects; ++idx) {
-                       cout << "index = " << indices[idx] << " probability = " << confidences[idx] << " " << left[idx] << " x "
-                                << top[idx] << " ~ " << right[idx] << " x " << bottom[idx] << endl;
+                       cout << "Frame number = " << frame_number << " probability = " << confidences[idx] << " " << left[idx]
+                                << " x " << top[idx] << " ~ " << right[idx] << " x " << bottom[idx] << endl;
                }
 
                for (unsigned int idx = 0; idx < number_of_objects; ++idx) {
                        const char *label;
 
-                       ret = mv_face_detection_get_label(handle, indices[idx], &label);
+                       ret = mv_face_detection_get_label(handle, idx, &label);
                        ASSERT_EQ(ret, 0);
-                       cout << "index = " << indices[idx] << " label = " << label << endl;
+                       cout << "index = " << idx << " label = " << label << endl;
 
                        string label_str(label);
 
index 65cca28..7651a8e 100644 (file)
@@ -17,6 +17,7 @@
 #include <iostream>
 #include <algorithm>
 #include <string.h>
+#include <thread>
 
 #include "gtest/gtest.h"
 
@@ -26,7 +27,7 @@
 
 #define IMG_DOG TEST_RES_PATH "/res/inference/images/dog2.jpg"
 #define IMG_FACE TEST_RES_PATH "/res/inference/images/faceDetection.jpg"
-#define MAX_INFERENCE_ITERATION 20
+#define MAX_INFERENCE_ITERATION 50
 
 using namespace testing;
 using namespace std;
@@ -42,39 +43,99 @@ struct model_info {
        mv_source_h source;
 };
 
-void object_detection_completion_callback(mv_object_detection_h handle, void *user_data)
+void object_detection_callback(void *user_data)
 {
        unsigned int number_of_objects;
        const int *left, *top, *right, *bottom;
-       const unsigned int *indices;
+       unsigned long frame_number = 0;
        const float *confidences;
-       model_info *test_model(static_cast<model_info *>(user_data));
+       mv_object_detection_h handle = static_cast<mv_object_detection_h>(user_data);
 
-       int ret = mv_object_detection_get_result(handle, &number_of_objects, &indices, &confidences, &left, &top, &right,
-                                                                                        &bottom);
-       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+       while (frame_number < MAX_INFERENCE_ITERATION - 10) {
+               int ret = mv_object_detection_get_result(handle, &number_of_objects, &frame_number, &confidences, &left, &top,
+                                                                                                &right, &bottom);
+               if (ret == MEDIA_VISION_ERROR_INVALID_OPERATION)
+                       break;
 
-       for (unsigned int idx = 0; idx < number_of_objects; ++idx) {
-               cout << "index = " << indices[idx] << " probability = " << confidences[idx] << " " << left[idx] << " x "
-                        << top[idx] << " ~ " << right[idx] << " x " << bottom[idx] << endl;
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+               for (unsigned int idx = 0; idx < number_of_objects; ++idx) {
+                       cout << "frame number = " << frame_number << " probability = " << confidences[idx] << " " << left[idx]
+                                << " x " << top[idx] << " ~ " << right[idx] << " x " << bottom[idx] << endl;
+               }
+
+               for (unsigned int idx = 0; idx < number_of_objects; ++idx) {
+                       const char *label;
+
+                       ret = mv_object_detection_get_label(handle, idx, &label);
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+                       cout << "index = " << idx << " label = " << label << endl;
+
+                       string label_str(label);
+
+                       transform(label_str.begin(), label_str.end(), label_str.begin(), ::toupper);
+
+                       ASSERT_EQ(label_str, "DOG");
+               }
        }
+}
+
+TEST(ObjectDetectionAsyncTest, InferenceShouldBeOk)
+{
+       mv_object_detection_h handle;
+       vector<model_info> test_models {
+               { "", "", "", "", "DOG" }, // If empty then default model will be used.
+               { "mobilenet_v2_ssd", "od_mobilenet_v2_ssd_320x320.tflite", "od_mobilenet_v2_ssd_320x320.json",
+                 "od_mobilenet_v2_ssd_label.txt", "DOG" }
+               // TODO.
+       };
+
+       for (auto &model : test_models) {
+               int ret = mv_object_detection_create(&handle);
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+               cout << "model name : " << model.model_file << endl;
+
+               mv_object_detection_set_model(handle, model.model_name.c_str(), model.model_file.c_str(),
+                                                                         model.meta_file.c_str(), model.label_file.c_str());
+               mv_object_detection_set_engine(handle, "tflite", "cpu");
 
-       for (unsigned int idx = 0; idx < number_of_objects; ++idx) {
-               const char *label;
+               ret = mv_object_detection_configure(handle);
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
 
-               ret = mv_object_detection_get_label(handle, indices[idx], &label);
+               ret = mv_object_detection_prepare(handle);
                ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
-               cout << "index = " << indices[idx] << " label = " << label << endl;
 
-               string label_str(label);
+               unique_ptr<thread> thread_handle;
+
+               for (unsigned int iter = 0; iter < MAX_INFERENCE_ITERATION; ++iter) {
+                       mv_source_h mv_source = NULL;
+                       ret = mv_create_source(&mv_source);
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+                       ret = ImageHelper::loadImageToSource(IMG_DOG, mv_source);
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+                       model.source = mv_source;
+
+                       ret = mv_object_detection_inference_async(handle, mv_source);
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+                       if (iter == 0)
+                               thread_handle = make_unique<thread>(&object_detection_callback, static_cast<void *>(handle));
+
+                       ret = mv_destroy_source(mv_source);
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+               }
 
-               transform(label_str.begin(), label_str.end(), label_str.begin(), ::toupper);
+               thread_handle->join();
 
-               ASSERT_EQ(label_str, test_model->answer);
+               ret = mv_object_detection_destroy(handle);
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
        }
 }
 
-TEST(ObjectDetectionAsyncTest, InferenceShouldBeOk)
+TEST(ObjectDetectionAsyncTest, InferenceShouldBeOkWithDestroyFirst)
 {
        mv_object_detection_h handle;
        vector<model_info> test_models {
@@ -100,6 +161,8 @@ TEST(ObjectDetectionAsyncTest, InferenceShouldBeOk)
                ret = mv_object_detection_prepare(handle);
                ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
 
+               unique_ptr<thread> thread_handle;
+
                for (unsigned int iter = 0; iter < MAX_INFERENCE_ITERATION; ++iter) {
                        mv_source_h mv_source = NULL;
                        ret = mv_create_source(&mv_source);
@@ -110,52 +173,114 @@ TEST(ObjectDetectionAsyncTest, InferenceShouldBeOk)
 
                        model.source = mv_source;
 
-                       ret = mv_object_detection_inference_async(handle, mv_source, object_detection_completion_callback,
-                                                                                                         reinterpret_cast<void *>(&model));
+                       ret = mv_object_detection_inference_async(handle, mv_source);
                        ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
 
+                       if (iter == 0)
+                               thread_handle = make_unique<thread>(&object_detection_callback, static_cast<void *>(handle));
+
                        ret = mv_destroy_source(mv_source);
                        ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
                }
 
                ret = mv_object_detection_destroy(handle);
                ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+               thread_handle->join();
        }
 }
 
-void face_detection_completion_callback(mv_object_detection_h handle, void *user_data)
+void face_detection_callback(void *user_data)
 {
        unsigned int number_of_objects;
        const int *left, *top, *right, *bottom;
-       const unsigned int *indices;
+       unsigned long frame_number = 0;
        const float *confidences;
-       model_info *test_model(static_cast<model_info *>(user_data));
+       mv_object_detection_h handle = static_cast<mv_object_detection_h>(user_data);
+
+       while (frame_number < MAX_INFERENCE_ITERATION - 10) {
+               int ret = mv_face_detection_get_result(handle, &number_of_objects, &frame_number, &confidences, &left, &top,
+                                                                                          &right, &bottom);
+               if (ret == MEDIA_VISION_ERROR_INVALID_OPERATION)
+                       break;
+
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+               for (unsigned int idx = 0; idx < number_of_objects; ++idx) {
+                       cout << "Frame number = " << frame_number << " probability = " << confidences[idx] << " " << left[idx]
+                                << " x " << top[idx] << " ~ " << right[idx] << " x " << bottom[idx] << endl;
+               }
+
+               for (unsigned int idx = 0; idx < number_of_objects; ++idx) {
+                       const char *label;
 
-       int ret = mv_face_detection_get_result(handle, &number_of_objects, &indices, &confidences, &left, &top, &right,
-                                                                                  &bottom);
-       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+                       ret = mv_face_detection_get_label(handle, idx, &label);
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+                       cout << "index = " << idx << " label = " << label << endl;
+
+                       string label_str(label);
 
-       for (unsigned int idx = 0; idx < number_of_objects; ++idx) {
-               cout << "index = " << indices[idx] << " probability = " << confidences[idx] << " " << left[idx] << " x "
-                        << top[idx] << " ~ " << right[idx] << " x " << bottom[idx] << endl;
+                       transform(label_str.begin(), label_str.end(), label_str.begin(), ::toupper);
+
+                       ASSERT_EQ(label_str, "FACE");
+               }
        }
+}
+
+TEST(FaceDetectionAsyncTest, InferenceShouldBeOk)
+{
+       mv_object_detection_h handle;
+       vector<model_info> test_models {
+               { "", "", "", "", "FACE" } // If empty then default model will be used.
+               // TODO.
+       };
+
+       for (auto &model : test_models) {
+               int ret = mv_face_detection_create(&handle);
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+               cout << "model name : " << model.model_file << endl;
+
+               mv_face_detection_set_model(handle, model.model_name.c_str(), model.model_file.c_str(), model.meta_file.c_str(),
+                                                                       model.label_file.c_str());
+               mv_face_detection_set_engine(handle, "tflite", "cpu");
 
-       for (unsigned int idx = 0; idx < number_of_objects; ++idx) {
-               const char *label;
+               ret = mv_face_detection_configure(handle);
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
 
-               ret = mv_face_detection_get_label(handle, indices[idx], &label);
+               ret = mv_face_detection_prepare(handle);
                ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
-               cout << "index = " << indices[idx] << " label = " << label << endl;
 
-               string label_str(label);
+               unique_ptr<thread> thread_handle;
+
+               for (unsigned int iter = 0; iter < MAX_INFERENCE_ITERATION; ++iter) {
+                       mv_source_h mv_source = NULL;
+                       ret = mv_create_source(&mv_source);
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+                       ret = ImageHelper::loadImageToSource(IMG_FACE, mv_source);
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+                       model.source = mv_source;
+
+                       ret = mv_face_detection_inference_async(handle, mv_source);
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+                       if (iter == 0)
+                               thread_handle = make_unique<thread>(&face_detection_callback, static_cast<void *>(handle));
+
+                       ret = mv_destroy_source(mv_source);
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+               }
 
-               transform(label_str.begin(), label_str.end(), label_str.begin(), ::toupper);
+               thread_handle->join();
 
-               ASSERT_EQ(label_str, test_model->answer);
+               ret = mv_face_detection_destroy(handle);
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
        }
 }
 
-TEST(FaceDetectionAsyncTest, InferenceShouldBeOk)
+TEST(FaceDetectionAsyncTest, InferenceShouldBeOkWithDestroyFirst)
 {
        mv_object_detection_h handle;
        vector<model_info> test_models {
@@ -179,6 +304,8 @@ TEST(FaceDetectionAsyncTest, InferenceShouldBeOk)
                ret = mv_face_detection_prepare(handle);
                ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
 
+               unique_ptr<thread> thread_handle;
+
                for (unsigned int iter = 0; iter < MAX_INFERENCE_ITERATION; ++iter) {
                        mv_source_h mv_source = NULL;
                        ret = mv_create_source(&mv_source);
@@ -189,15 +316,19 @@ TEST(FaceDetectionAsyncTest, InferenceShouldBeOk)
 
                        model.source = mv_source;
 
-                       ret = mv_face_detection_inference_async(handle, mv_source, face_detection_completion_callback,
-                                                                                                       reinterpret_cast<void *>(&model));
+                       ret = mv_face_detection_inference_async(handle, mv_source);
                        ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
 
+                       if (iter == 0)
+                               thread_handle = make_unique<thread>(&face_detection_callback, static_cast<void *>(handle));
+
                        ret = mv_destroy_source(mv_source);
                        ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
                }
 
                ret = mv_face_detection_destroy(handle);
                ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+               thread_handle->join();
        }
 }
\ No newline at end of file