task_api: add semantic segmentation task API support 50/320250/5
authorInki Dae <inki.dae@samsung.com>
Tue, 25 Feb 2025 09:29:22 +0000 (18:29 +0900)
committerInki Dae <inki.dae@samsung.com>
Thu, 27 Feb 2025 03:32:14 +0000 (12:32 +0900)
Add semantic segmentation task API support including its test case.

Change-Id: If8c912f3e6e552897f8d51b585651465002de7df
Signed-off-by: Inki Dae <inki.dae@samsung.com>
services/task_api/CMakeLists.txt
services/task_api/include/ITaskBase.h
services/task_api/include/ObjectDetectionTask.h
services/task_api/include/SemanticSegmentationTask.h [new file with mode: 0644]
services/task_api/src/ObjectDetectionTask.cpp
services/task_api/src/SemanticSegmentationTask.cpp [new file with mode: 0644]
services/task_api/src/TaskApi.cpp
test/services/test_task_api_on_screen.cpp

index d984a1271179bdfa890af81b0d203f6b63e97155..5db0b5d275f99ce5d70102b7dfeedcbb7a80f8c0 100644 (file)
@@ -2,6 +2,7 @@ SET(SINGLEO_SERVICE_SOURCE_FILES
     ${SINGLEO_SERVICE_SOURCE_FILES}
     task_api/src/TaskApi.cpp
     task_api/src/ObjectDetectionTask.cpp
+       task_api/src/SemanticSegmentationTask.cpp
 )
 
 LIST(APPEND SERVICE_LIBRARY_LIST ${SERVICE_LIBRARY_LIST})
index 88972f0f191818b84fe761f080eef7c1b207c5a4..6d8a13d40a8a80e940d3e62810a0fe0b4754d98f 100644 (file)
@@ -37,7 +37,7 @@ public:
 
        virtual void setupPipeline(std::unique_ptr<TaskManager> &taskManager) = 0;
        virtual void resetPipeline(std::unique_ptr<TaskManager> &taskManager) = 0;
-       virtual void renderOn(BaseDataType &data) = 0;
+       virtual void renderOn(BaseDataType &data, singleo_user_cb_t user_cb, void *user_data) = 0;
        virtual void updateResult(std::vector<std::shared_ptr<BaseResultType> > &outputs) = 0;
        virtual TaskApiResult &getResult() = 0;
        // Make sure to update a _result member object in concrete class with a given result
index a90467fb0f9d6feb4080da123419df1ee0168b7e..760266ec820ced74d47f4dbf4e26911f4d689462 100644 (file)
@@ -51,7 +51,7 @@ public:
 
        void setupPipeline(std::unique_ptr<TaskManager> &taskManager) final;
        void resetPipeline(std::unique_ptr<TaskManager> &taskManager) final;
-       void renderOn(BaseDataType &data) final;
+       void renderOn(BaseDataType &data, singleo_user_cb_t user_cb, void *user_data) final;
        void updateResult(std::vector<std::shared_ptr<BaseResultType> > &outputs) final;
        TaskApiResult &getResult() final;
        int getNumOfResult(const TaskApiResult &result) final;
diff --git a/services/task_api/include/SemanticSegmentationTask.h b/services/task_api/include/SemanticSegmentationTask.h
new file mode 100644 (file)
index 0000000..9bb082d
--- /dev/null
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SEMANTIC_SEGMENTATION_TASK_H__
+#define __SEMANTIC_SEGMENTATION_TASK_H__
+
+#include <array>
+#include <opencv2/opencv.hpp>
+
+#include "SingleoException.h"
+#include "SingleoCommonTypes.h"
+#include "ServiceDataType.h"
+#include "DataTypes.h"
+#include "TaskManager.h"
+
+#include "ITaskBase.h"
+
+namespace singleo
+{
+namespace services
+{
+namespace taskapi
+{
+class SemanticSegmentationTask : public ITaskBase
+{
+private:
+       std::unique_ptr<TaskManager> _taskManager;
+       std::map<std::string, TaskApiResultType> _result_keys = { { "MIN_X", TaskApiResultType::MIN_X },
+                                                                                                                                { "MIN_Y", TaskApiResultType::MIN_Y },
+                                                                                                                                { "MAX_X", TaskApiResultType::MAX_X },
+                                                                                                                                { "MAX_Y", TaskApiResultType::MAX_Y },
+                                                                                                                                { "LABEL", TaskApiResultType::LABEL } };
+
+       bool isKeyValid(const std::string &key);
+       TaskApiResult _result;
+       std::mutex _result_lock;
+       const std::array<cv::Vec3b, 30> _color_map = {
+                               cv::Vec3b( 128, 64,  128 ), // purple - road 1
+                               cv::Vec3b( 244, 35,  232 ), // orange - sidewalk 2
+                               cv::Vec3b( 70,  70,  70 ), // yellow - building 3
+                               cv::Vec3b( 102, 102, 156 ), // yellow - wall 4
+                               cv::Vec3b( 190, 153, 153 ), // yellow - fence 5
+                               cv::Vec3b( 153, 153, 153 ), // grey   - pole 6
+                               cv::Vec3b( 250, 170, 30 ), // purple - traffic light 7
+                               cv::Vec3b( 220, 220, 0 ), // purple - traffic sign 8
+                               cv::Vec3b( 107, 142, 35 ), // orange - vegetation 9
+                               cv::Vec3b( 152, 251, 152 ), // orange - terrain 10
+                               cv::Vec3b( 70,  130, 180 ), // blue - sky 11
+                               cv::Vec3b( 220, 20,  60 ), // red - person 12
+                               cv::Vec3b( 255, 0,   0 ), // green - rider 13
+                               cv::Vec3b( 0,   0,   142 ), // green - car 14
+                               cv::Vec3b( 0,   0,   70 ), // green - truck 15
+                               cv::Vec3b( 0,   60,  100 ), // green - bus 16
+                               cv::Vec3b( 0,   80,  100 ), // green - train 17
+                               cv::Vec3b( 0,   0,   230 ), // green - motorcycle 18
+                               cv::Vec3b( 119, 11,  32 ), // green - bicycle 19
+                               cv::Vec3b( 244, 35,  232 ), // orange - ground 20
+                               cv::Vec3b( 190, 153, 153 ), // yellow - bridge 21
+                               cv::Vec3b( 128, 64,  128 ), // purple - rail track 22
+                               cv::Vec3b( 190, 153, 153 ), // yellow - guard rail 23
+                               cv::Vec3b( 70,  70,  70 ), // yellow - tunnel 24
+                               cv::Vec3b( 0,   60,  100 ), // green - polegroup 25
+                               cv::Vec3b( 0,   80,  100 ), // green - caravon 26
+                               cv::Vec3b( 0,   0,   70 ), // green - trailer 27
+                               cv::Vec3b( 119, 11,  32 ), // green - license plate 28
+                               cv::Vec3b( 0,   0,  0 ), // black - unlabeled 29
+                               cv::Vec3b( 0,   0,   70 ) // green - ego vehicle 30
+       };
+
+public:
+       SemanticSegmentationTask();
+       virtual ~SemanticSegmentationTask();
+
+       void setupPipeline(std::unique_ptr<TaskManager> &taskManager) final;
+       void resetPipeline(std::unique_ptr<TaskManager> &taskManager) final;
+       void renderOn(BaseDataType &data, singleo_user_cb_t user_cb, void *user_data) final;
+       void updateResult(std::vector<std::shared_ptr<BaseResultType> > &outputs) final;
+       TaskApiResult &getResult() final;
+       int getNumOfResult(const TaskApiResult &result) final;
+       unsigned int getResultInt(unsigned int idx, const std::string &key) final;
+       const char *getResultStr(unsigned int idx, const std::string &key) final;
+};
+
+} // taskapi
+} // service
+} // singleo
+
+#endif
\ No newline at end of file
index 0ab9bfbcd5e3512eac4b14a3ff48a556f724e30b..8b841c4f82fa350811a17b29fee6fcc0c032ad64 100644 (file)
@@ -63,7 +63,7 @@ void ObjectDetectionTask::resetPipeline(std::unique_ptr<TaskManager> &taskManage
        taskManager->clear();
 }
 
-void ObjectDetectionTask::renderOn(BaseDataType &data)
+void ObjectDetectionTask::renderOn(BaseDataType &data, singleo_user_cb_t user_cb, void *user_data)
 {
        auto preprocessedImg = dynamic_cast<ImageDataType &>(data);
        unique_lock<mutex> lock(_result_lock);
@@ -82,6 +82,9 @@ void ObjectDetectionTask::renderOn(BaseDataType &data)
                        cv::putText(imgCv, labels[idx], cv::Point(rects[idx].left, rects[idx].top - 10), cv::FONT_HERSHEY_SIMPLEX, 1.2, cv::Scalar(255, 0, 0), 2);
                }
 
+               if (user_cb)
+                       user_cb(preprocessedImg.ptr, preprocessedImg.width, preprocessedImg.height, preprocessedImg.byte_per_pixel, user_data);
+
                lock.lock();
                _result.object_detection_result.is_valid = false;
        }
diff --git a/services/task_api/src/SemanticSegmentationTask.cpp b/services/task_api/src/SemanticSegmentationTask.cpp
new file mode 100644 (file)
index 0000000..7d5a473
--- /dev/null
@@ -0,0 +1,163 @@
+/**
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <map>
+#include "SingleoException.h"
+#include "InferenceNode.h"
+#include "BridgeNode.h"
+#include "EndpointNode.h"
+#include "InferenceTaskFactory.h"
+#include "SemanticSegmentationTask.h"
+
+using namespace std;
+using namespace singleo::inference;
+using namespace singleo::exception;
+
+namespace singleo
+{
+namespace services
+{
+namespace taskapi
+{
+SemanticSegmentationTask::SemanticSegmentationTask()
+{}
+
+SemanticSegmentationTask::~SemanticSegmentationTask()
+{}
+
+void SemanticSegmentationTask::setupPipeline(std::unique_ptr<TaskManager> &taskManager)
+{
+       auto factory = InferenceTaskFactory::instance().create("MvInferenceTaskFactory");
+
+       taskManager->requestNewNode(NodeType::STARTPOINT, "startpoint");
+
+       auto semantic_segmentation_node = taskManager->requestNewNode(NodeType::INFERENCE, "semantic_segmentation");
+       dynamic_cast<InferenceNode *>(semantic_segmentation_node)->setInferenceTask(factory->createSemanticSegmentation());
+
+       taskManager->requestNewNode(NodeType::ENDPOINT, "endpoint");
+       taskManager->resetPipeline();
+
+       taskManager->addEdge("startpoint", "semantic_segmentation");
+       taskManager->addEdge("semantic_segmentation", "endpoint");
+
+       taskManager->verify();
+}
+
+void SemanticSegmentationTask::resetPipeline(std::unique_ptr<TaskManager> &taskManager)
+{
+       taskManager->clear();
+}
+
+void SemanticSegmentationTask::renderOn(BaseDataType &data, singleo_user_cb_t user_cb, void *user_data)
+{
+       auto preprocessedImg = dynamic_cast<ImageDataType &>(data);
+       unique_lock<mutex> lock(_result_lock);
+
+       if (_result.semantic_segmentation_result.is_valid) {
+                auto &result = _result.semantic_segmentation_result;
+               cv::Mat imgCv(cv::Size(preprocessedImg.width, preprocessedImg.height), CV_MAKETYPE(CV_8U, 3),
+                                         preprocessedImg.ptr);
+               cv::Mat segMap(cv::Size(result.width, result.height), CV_MAKETYPE(CV_8U, result.pixel_size), result.data.data());
+               cv::Mat segMapRgb(result.height, result.width, CV_8UC3, cv::Scalar(0));
+
+               for (unsigned int h = 0; h < result.height; ++h) {
+                       for (unsigned int w = 0; w < result.width; ++w) {
+                               auto *value = segMap.ptr<unsigned char>(h, w);
+                               segMapRgb.at<cv::Vec3b>(h, w) = _color_map[*value];
+                       }
+               }
+
+               lock.unlock();
+
+               double alpha = 0.3;
+               double beta = 1.0 - alpha;
+               cv::addWeighted(imgCv, alpha, segMapRgb, beta, 0.0, imgCv);
+
+               user_cb(preprocessedImg.ptr, preprocessedImg.width, preprocessedImg.height, preprocessedImg.byte_per_pixel, user_data);
+
+               lock.lock();
+               _result.semantic_segmentation_result.is_valid = false;
+       }
+}
+
+void SemanticSegmentationTask::updateResult(std::vector<std::shared_ptr<BaseResultType> > &outputs)
+{
+       for (auto &output : outputs) {
+               if (output->_is_empty)
+                       continue;
+
+               unique_lock<mutex> lock(_result_lock);
+
+               if (output->_type == ResultType::SEMANTIC_SEGMENTATION) {
+                       _result.semantic_segmentation_result.width = dynamic_cast<SsResultType &>(*output).width;
+                       _result.semantic_segmentation_result.height = dynamic_cast<SsResultType &>(*output).height;
+                       _result.semantic_segmentation_result.pixel_size = dynamic_cast<SsResultType &>(*output).pixel_size;
+
+                       auto *segment_map = dynamic_cast<SsResultType &>(*output)._segment_map;
+                       size_t seg_map_size = _result.semantic_segmentation_result.width *
+                                                                       _result.semantic_segmentation_result.height *
+                                                                       _result.semantic_segmentation_result.pixel_size;
+
+                       _result.semantic_segmentation_result.data.assign(segment_map, segment_map + seg_map_size);
+                       _result.semantic_segmentation_result.is_valid = true;
+               }
+       }
+}
+
+TaskApiResult &SemanticSegmentationTask::getResult()
+{
+       unique_lock<mutex> lock(_result_lock);
+
+       return _result;
+}
+
+int SemanticSegmentationTask::getNumOfResult(const TaskApiResult &result)
+{
+       unique_lock<mutex> lock(_result_lock);
+
+       _result = result;
+
+       return _result.semantic_segmentation_result.data.empty() ? 0 : 1;
+}
+
+bool SemanticSegmentationTask::isKeyValid(const std::string &key)
+{
+       return (_result_keys.find(key) != _result_keys.end());
+}
+
+unsigned int SemanticSegmentationTask::getResultInt(unsigned int idx, const std::string &key)
+{
+       if (!isKeyValid(key))
+               throw InvalidParameter("A given result key is inavlid.");
+
+       // TODO.
+
+       throw FuncNotImpl("FuncNotImpl: getResultInt()");
+}
+
+const char *SemanticSegmentationTask::getResultStr(unsigned int idx, const std::string &key)
+{
+       if (!isKeyValid(key))
+               throw InvalidParameter("A given result key is inavlid.");
+
+       // TODO.
+
+       throw FuncNotImpl("FuncNotImpl: getResultStr()");
+}
+}
+}
+}
index 98d791ac0a4a6e0ccb1d3608bf2c8a9ccd662aaf..448dff92c8574c93f0187f8089b6f0f13714391a 100644 (file)
@@ -28,6 +28,7 @@
 #include "EndpointNode.h"
 
 #include "ObjectDetectionTask.h"
+#include "SemanticSegmentationTask.h"
 
 using namespace std;
 using namespace singleo::inference;
@@ -82,6 +83,9 @@ void TaskApi::configure(ServiceConfigBase &config)
        if (taskapi_config._task_name == "OBJECT_DETECTION") {
                _task = make_unique<ObjectDetectionTask>();
                _task->setupPipeline(_taskManager);
+       } else if (taskapi_config._task_name == "SEMANTIC_SEGMENTATION") {
+               _task = make_unique<SemanticSegmentationTask>();
+               _task->setupPipeline(_taskManager);
        } else {
                throw InvalidOperation("Unsupported task name.");
        }
@@ -94,10 +98,8 @@ void TaskApi::inputFeedCb(BaseDataType &data)
        auto originalImg = dynamic_pointer_cast<ImageDataType>(_preprocessor->getOutput().clone());
        auto preprocessedImg = dynamic_cast<ImageDataType &>(_preprocessor->getOutput());
 
-       if (_user_cb) {
-               _task->renderOn(data);
-               _user_cb(preprocessedImg.ptr, preprocessedImg.width, preprocessedImg.height, preprocessedImg.byte_per_pixel, _user_data);
-       }
+       if (_user_cb)
+               _task->renderOn(data, _user_cb, _user_data);
 
        _preprocessor->invoke();
 
index 40050552347d941a2761849c9c0242da025581bb..666b2c121b1d115039096686d73fc10f929f2824 100644 (file)
@@ -118,3 +118,46 @@ TEST(TaskApiAsyncOnScreenTest, ODRequestWithCameraInputFeedShouldBeOk)
        ret = singleo_service_destroy(context.handle);
        ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
 }
+
+void ss_taskapi_callback(void *user_data)
+{
+       auto context = static_cast<Context *>(user_data);
+       auto handle = static_cast<singleo_service_h>(context->handle);
+       unsigned long frame_number = 0;
+
+       while (1) {
+               unsigned int cnt;
+
+               int ret = singleo_service_get_result_cnt(handle, &cnt);
+               if (ret != SINGLEO_ERROR_NONE)
+                       break;
+
+               if (++frame_number > 5000 && cnt > 0)
+                       break;
+       }
+}
+
+TEST(TaskApiAsyncOnScreenTest, SSRequestWithCameraInputFeedShouldBeOk)
+{
+       Context context {};
+
+       int ret = singleo_service_create("service=TaskApi, task=semantic_segmentation, input_feed=camera, camera_id=0, fps=30, async=1",
+                                                                        &context.handle);
+       ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+       ret = singleo_service_register_user_callback(context.handle, user_callback, &context);
+       ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+       ret = singleo_service_perform(context.handle);
+       ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+       unique_ptr<thread> thread_handle = make_unique<thread>(&ss_taskapi_callback, static_cast<void *>(&context));
+
+       thread_handle->join();
+
+       ret = singleo_service_unregister_user_callback(context.handle);
+       ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+       ret = singleo_service_destroy(context.handle);
+       ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+}