services: add callback node support as last node 28/310928/2
authorInki Dae <inki.dae@samsung.com>
Thu, 9 May 2024 10:51:11 +0000 (19:51 +0900)
committerInki Dae <inki.dae@samsung.com>
Mon, 13 May 2024 06:02:57 +0000 (15:02 +0900)
Add callback node support as last node.

With this patch, we can place a callback node to last node
so that we can get the final results from the node.

For the test, this patch adds below test case - GraphC,
GraphC:
======
                             ---> callback ---> face_landmark_detection ---
 input --> face_detection --|                                              |--> callback --> output
                             ----------------------------------------------
Change-Id: Ia444b0d47480485cd81204bc0568badc8215ebfe
Signed-off-by: Inki Dae <inki.dae@samsung.com>
services/auto_zoom/src/AutoZoom.cpp
services/task_manager/include/CallbackNode.h
services/task_manager/include/TaskManager.h
services/task_manager/src/CallbackNode.cpp
services/task_manager/src/TaskManager.cpp
test/services/test_task_manager.cpp

index 0c5d6a1fc12f9cf058d8feffd9f838ddf0a0b44a..8e0e7d354aa0c68830ea7384503a77695251333a 100644 (file)
@@ -194,13 +194,13 @@ void AutoZoom::updateResult(BaseDataType &in_data)
 {
        auto &output_data = _taskManager->output();
 
-       if (output_data._type != ResultType::OBJECT_DETECTION && output_data._type != ResultType::FACE_DETECTION)
+       if (output_data[0]->_type != ResultType::OBJECT_DETECTION && output_data[0]->_type != ResultType::FACE_DETECTION)
                throw InvalidParameter("Invalid result type");
 
-       vector<Rect> &rects = dynamic_cast<FdResultType &>(output_data)._rects;
+       vector<Rect> &rects = dynamic_cast<FdResultType &>(*output_data[0])._rects;
        AutoZoomResult autozoom_result;
 
-       autozoom_result.frame_number = output_data._frame_number;
+       autozoom_result.frame_number = output_data[0]->_frame_number;
        autozoom_result.num_of_objects = rects.size();
 
        for (size_t idx = 0; idx < rects.size(); ++idx) {
index 0603dd09365d35eb70aac58560f1e3cb55214e0d..6fff52a8f8df6109163234bdd0b694fe622672d6 100644 (file)
@@ -36,6 +36,8 @@ private:
        NodeCb _cb;
        std::vector<std::shared_ptr<BaseDataType> > _inputs;
        std::shared_ptr<BaseDataType> _output;
+       // _results should be updated by user callback.
+       std::vector<std::shared_ptr<BaseResultType> > _results;
        void *_user_data { nullptr };
        bool _completed { false };
        std::condition_variable _event;
@@ -70,6 +72,8 @@ public:
        NodeCb getCb();
        void setUserData(void *user_data);
        void *getUserData();
+       std::vector<std::shared_ptr<BaseResultType> > &getResult();
+       void addResult(std::shared_ptr<BaseResultType> result);
 };
 
 }
index 03cdcba1865a8896240cb04aaffed61b7f9225ab..f621954217588993c4b1176cbbdc7212e2435747 100644 (file)
@@ -35,6 +35,7 @@ private:
        std::vector<std::shared_ptr<BaseDataType> > _inputs;
        std::vector<std::shared_ptr<INode> > _nodes;
        std::vector<std::shared_ptr<std::thread> > _threads;
+       std::vector<std::shared_ptr<BaseResultType> > _results;
 
        void threadCb(std::shared_ptr<INode> &node);
 
@@ -46,7 +47,7 @@ public:
        std::vector<std::shared_ptr<BaseDataType> > &getInputs();
        void addNode(std::shared_ptr<INode> node);
        void run();
-       BaseResultType &output();
+       std::vector<std::shared_ptr<BaseResultType> > &output();
        void clear();
 };
 
index 5be4e4bf897cd51866ffc1f3cb6f232a64fbb56f..95d1312b071a51fc0cc7f0d368d2d0798bd008fe 100644 (file)
@@ -74,6 +74,16 @@ std::shared_ptr<BaseDataType> &CallbackNode::getOutput()
        return _output;
 }
 
+void CallbackNode::addResult(std::shared_ptr<BaseResultType> result)
+{
+       _results.push_back(result);
+}
+
+vector<shared_ptr<BaseResultType> > &CallbackNode::getResult()
+{
+       return _results;
+}
+
 void CallbackNode::wait()
 {
        unique_lock<mutex> lock(_mutex);
index 54e07fc20e0ef6e042f4abfa606cb144ee8cfd5a..bd06436adc44476a16a6d14b4a8f6e46f8b7e720 100644 (file)
@@ -151,19 +151,33 @@ void TaskManager::run()
        _threads.clear();
 }
 
-BaseResultType &TaskManager::output()
+vector<shared_ptr<BaseResultType> > &TaskManager::output()
 {
        auto lastNode = _nodes[_nodes.size() - 1];
 
+       _results.clear();
+
        // TODO. consider for callback node support as last node.
-       if (lastNode->getType() != NodeType::INFERENCE) {
-               SINGLEO_LOGE("Last node is not an inference node.");
-               throw InvalidOperation("Last node is not an inference node.");
-       }
+       if (lastNode->getType() == NodeType::INFERENCE) {
+               auto inferenceNode = dynamic_pointer_cast<InferenceNode>(lastNode);
+               auto &result = inferenceNode->getInferenceService()->result();
+               if (result._type == ResultType::FACE_DETECTION) {
+                       auto fd_result = dynamic_cast<FdResultType &>(result);
+                       _results.push_back(make_shared<FdResultType>(fd_result));
+               } else if (result._type == ResultType::FACE_LANDMARK) {
+                       auto fld_result = dynamic_cast<FldResultType &>(result);
+                       _results.push_back(make_shared<FldResultType>(fld_result));
+               } else if (result._type == ResultType::OBJECT_DETECTION) {
+                       auto od_result = dynamic_cast<OdResultType &>(result);
+                       _results.push_back(make_shared<OdResultType>(od_result));
+               }
+       } else {
+               auto callbackNode = dynamic_pointer_cast<CallbackNode>(lastNode);
 
-       auto infereneNode = dynamic_pointer_cast<InferenceNode>(lastNode);
+               _results = callbackNode->getResult();
+       }
 
-       return infereneNode->getInferenceService()->result();
+       return _results;
 }
 
 void TaskManager::clear()
index e3d63203f13d9761fdc6682e75a2d7857910524a..5c8e315b34398667e4a949de5eaa882089a879e8 100644 (file)
@@ -34,7 +34,7 @@ using namespace singleo;
 using namespace singleo::inference;
 using namespace singleo::services;
 
-void ExampleNodeCallback(vector<BaseResultType *> &results, shared_ptr<BaseDataType> inputData, void *user_data)
+void BridgeNodeCallback(vector<BaseResultType *> &results, shared_ptr<BaseDataType> inputData, void *user_data)
 {
        shared_ptr<ImageDataType> imageData = dynamic_pointer_cast<ImageDataType>(inputData);
        size_t buffer_size = imageData->width * imageData->height * imageData->byte_per_pixel;
@@ -62,7 +62,7 @@ void ExampleNodeCallback(vector<BaseResultType *> &results, shared_ptr<BaseDataT
        }
 
        node->setOutput(make_shared<ImageDataType>(newImage));
-};
+}
 
 // GraphA : input ----> face_detection ----> callback ----> face_landmark_detection ----> output
 TEST(SingloTaskManager, MultipleNodesBasedGraphAShouldWork)
@@ -97,7 +97,7 @@ TEST(SingloTaskManager, MultipleNodesBasedGraphAShouldWork)
 
                auto callback_node = make_shared<CallbackNode>();
                callback_node->setName("callback");
-               callback_node->setCb(&ExampleNodeCallback, callback_node.get());
+               callback_node->setCb(BridgeNodeCallback, callback_node.get());
                callback_node->addDependency(face_detection_node);
                taskManager->addNode(callback_node);
 
@@ -110,9 +110,12 @@ TEST(SingloTaskManager, MultipleNodesBasedGraphAShouldWork)
                taskManager->run();
 
                cout << "Face landmark result" << endl;
-               auto result = dynamic_cast<FldResultType &>(taskManager->output());
-               for (auto &point : result._points)
-                       cout << point.x << " x " << point.y << endl;
+               auto results = taskManager->output();
+               for (auto &result : results) {
+                       auto fld_result = dynamic_pointer_cast<FldResultType>(result);
+                       for (auto &point : fld_result->_points)
+                               cout << point.x << " x " << point.y << endl;
+               }
 
                taskManager->clear();
        }
@@ -159,7 +162,7 @@ TEST(SingloTaskManager, MultipleNodesBasedGraphBShouldWork)
 
                auto callback_node = make_shared<CallbackNode>();
                callback_node->setName("callback");
-               callback_node->setCb(&ExampleNodeCallback, callback_node.get());
+               callback_node->setCb(BridgeNodeCallback, callback_node.get());
                callback_node->addDependency(face_detection_node_a);
                callback_node->addDependency(face_detection_node_b);
                taskManager->addNode(callback_node);
@@ -173,9 +176,108 @@ TEST(SingloTaskManager, MultipleNodesBasedGraphBShouldWork)
                taskManager->run();
 
                cout << "Face landmark result" << endl;
-               auto result = dynamic_cast<FldResultType &>(taskManager->output());
-               for (auto &point : result._points)
-                       cout << point.x << " x " << point.y << endl;
+
+               auto results = taskManager->output();
+               for (auto &result : results) {
+                       auto fld_result = dynamic_pointer_cast<FldResultType>(result);
+                       for (auto &point : fld_result->_points)
+                               cout << point.x << " x " << point.y << endl;
+               }
+
+               taskManager->clear();
+       }
+}
+
+void LastNodeCallback(vector<BaseResultType *> &results, shared_ptr<BaseDataType> inputData, void *user_data)
+{
+       CallbackNode *node = static_cast<CallbackNode *>(user_data);
+
+       for (auto &r : results) {
+               ASSERT_EQ((r->_type == ResultType::FACE_DETECTION || r->_type == ResultType::FACE_LANDMARK), true);
+
+               if (r->_type == ResultType::FACE_DETECTION) {
+                       auto fdResult = dynamic_cast<FdResultType *>(r);
+                       node->addResult(make_shared<FdResultType>(*fdResult));
+               } else {
+                       auto fldResult = dynamic_cast<FldResultType *>(r);
+                       node->addResult(make_shared<FldResultType>(*fldResult));
+               }
+       }
+}
+
+// GraphC:
+//                                        -----> callback -----> face_landmark_detection ------
+//         input -----> face_detection --|                                                     |----> callback ----> output
+//                                        -----------------------------------------------------
+TEST(SingloTaskManager, MultipleNodesBasedGraphDShouldWork)
+{
+       cv::Mat cv_image = cv::imread(IMG_FACE, cv::IMREAD_COLOR);
+       cv::cvtColor(cv_image, cv_image, cv::COLOR_BGR2RGB);
+
+       if (cv_image.empty()) {
+               cout << "Fail to read a given image file." << endl;
+               return;
+       }
+
+       ImageDataType image_data;
+
+       image_data.width = cv_image.cols;
+       image_data.height = cv_image.rows;
+       image_data.byte_per_pixel = cv_image.channels();
+       image_data.ptr = cv_image.data;
+
+       auto factory = InferenceServiceFactory::instance().create("MvInferenceServiceFactory");
+
+       auto taskManager = make_unique<TaskManager>();
+       const unsigned int maxIteration = 10;
+
+       for (unsigned int cnt = 0; cnt < maxIteration; ++cnt) {
+               taskManager->addInput(image_data);
+
+               auto face_detection_node = make_shared<InferenceNode>();
+               face_detection_node->setName("face_detection");
+               face_detection_node->setInferenceService(factory->createFaceDetection());
+               taskManager->addNode(face_detection_node);
+
+               auto bridge_callback_node = make_shared<CallbackNode>();
+               bridge_callback_node->setName("bridge_callback");
+               bridge_callback_node->setCb(BridgeNodeCallback, bridge_callback_node.get());
+               bridge_callback_node->addDependency(face_detection_node);
+               taskManager->addNode(bridge_callback_node);
+
+               auto face_landmark_node = make_shared<InferenceNode>();
+               face_landmark_node->setName("face_landmark");
+               face_landmark_node->setInferenceService(factory->createFaceLandmarkDetection());
+               face_landmark_node->addDependency(bridge_callback_node);
+               taskManager->addNode(face_landmark_node);
+
+               auto lase_callback_node = make_shared<CallbackNode>();
+               lase_callback_node->setName("last_callback");
+               lase_callback_node->setCb(LastNodeCallback, lase_callback_node.get());
+               lase_callback_node->addDependency(face_detection_node);
+               lase_callback_node->addDependency(face_landmark_node);
+               taskManager->addNode(lase_callback_node);
+
+               taskManager->run();
+
+               auto results = taskManager->output();
+               for (auto &result : results) {
+                       if (result->_type == ResultType::FACE_DETECTION) {
+                               auto fd_result = dynamic_pointer_cast<FdResultType>(result);
+
+                               cout << "Face detection result" << endl;
+
+                               for (auto rect : fd_result->_rects)
+                                       cout << rect.left << " x " << rect.top << " ~ " << rect.right << " x " << rect.bottom << endl;
+                       } else {
+                               auto fld_result = dynamic_pointer_cast<FldResultType>(result);
+
+                               cout << "Face landmark detection result" << endl;
+
+                               for (auto &point : fld_result->_points)
+                                       cout << point.x << " x " << point.y << endl;
+                       }
+               }
 
                taskManager->clear();
        }