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 208a2469647175059eba4471d5d46129f3a193e7..2cec2a4c636a6a171d72b603cbd97f2d472da4b5 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 0b4aa00aeda943a57de121246c18bba027730e81..cf783c93b9adf364ab852d401434a6de834b0357 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 70866322bf0990898485f76374beba85d2615aa0..ff9c90f2afbf2540741ce7e13ebf35a40dee5ac4 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 8c04c4b59ed6c7df5f4f0c1cf2ea12cef19d29e3..208403541d9d317ef02e4d32ec7069f4da593bdb 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 5521b1c364f46623bf5b8bfa6c117789bfb7d4a7..ce3e90eff8b5f3c16757e2e93a3b597ddb50e17c 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 c84f20dae26cfe8d100c3b0a5d2526401c5bdac6..29c20dabbb7da69017762ba289521bb437eedde8 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 14681fd84a111ab4da3ac1ccf4ce319abda8688f..5f3935982a7e1a227030109988f4d94899056911 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 eb80a63b96ba0fd802eb900ddaf31456c646d082..bb863622c5e914f347d514f7eea7c872271b87f9 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 2b0cc7426aa6207b31fcd14a44e259d021c79bea..923b56ff1c56b5939cddb78dde18f9af22e80204 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 a82e3d9cdc4b7e3e8c146ea901f7602175c96944..5f045f6ec301a7626662bbcde9651259407499d1 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 65cca28026f67fd9aae77c960f19e7ff3de1f0cf..7651a8e30711519bd901f333e1c86b9b48c27c4c 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