Add semantic segmentation task API support including its test case.
Change-Id: If8c912f3e6e552897f8d51b585651465002de7df
Signed-off-by: Inki Dae <inki.dae@samsung.com>
${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})
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
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;
--- /dev/null
+/**
+ * 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
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);
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;
}
--- /dev/null
+/**
+ * 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()");
+}
+}
+}
+}
#include "EndpointNode.h"
#include "ObjectDetectionTask.h"
+#include "SemanticSegmentationTask.h"
using namespace std;
using namespace singleo::inference;
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.");
}
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();
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);
+}