Add a concrete class for ObjectDetection task.
This patch refactors the implementation of the Object Detection task
by encapsulating its dependencies within a dedicated concrete class.
Consequently, this design change allows us to extend the TaskAPI service
with additional Task APIs without modifying the existing TaskAPI service
itself.
Change-Id: I6940f0582589f3749d92b9e484c72870695cfaf0
Signed-off-by: Inki Dae <inki.dae@samsung.com>
SET(SINGLEO_SERVICE_SOURCE_FILES
${SINGLEO_SERVICE_SOURCE_FILES}
task_api/src/TaskApi.cpp
+ task_api/src/ObjectDetectionTask.cpp
)
LIST(APPEND SERVICE_LIBRARY_LIST ${SERVICE_LIBRARY_LIST})
--- /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 __ITASK_BASE_H__
+#define __ITASK_BASE_H__
+
+#include <memory>
+#include "SingleoException.h"
+#include "SingleoCommonTypes.h"
+#include "ServiceDataType.h"
+#include "DataTypes.h"
+#include "TaskManager.h"
+
+namespace singleo
+{
+namespace services
+{
+namespace taskapi
+{
+class ITaskBase
+{
+public:
+ virtual ~ITaskBase() {};
+
+ 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 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
+ // because _result in concrete class is used by getResultXxx().
+ // I.e.,
+ // int XxxTask::getNumOfResult(const TaskApiResult &result)
+ // {
+ // _result = result;
+ // return _result.bboxes.size();
+ // }
+ virtual int getNumOfResult(const TaskApiResult &result) = 0;
+ virtual unsigned int getResultInt(unsigned int idx, const std::string &key) = 0;
+ virtual const char *getResultStr(unsigned int idx, const std::string &key) = 0;
+};
+} // taskapi
+} // service
+} // singleo
+
+#endif
\ No newline at end of file
--- /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 __OBJECT_DETECTION_TASK_H__
+#define __OBJECT_DETECTION_TASK_H__
+
+#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 ObjectDetectionTask : 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;
+
+public:
+ ObjectDetectionTask();
+ virtual ~ObjectDetectionTask();
+
+ void setupPipeline(std::unique_ptr<TaskManager> &taskManager) final;
+ void resetPipeline(std::unique_ptr<TaskManager> &taskManager) final;
+ void renderOn(BaseDataType &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
#include "AsyncManager.h"
#include "IPreprocessor.h"
#include "TaskManager.h"
+#include "ITaskBase.h"
namespace singleo
{
{
private:
static bool _registered;
+ std::unique_ptr<ITaskBase> _task;
std::unique_ptr<TaskManager> _taskManager;
std::unique_ptr<singleo::input::IInputService> _input_service;
std::queue<std::shared_ptr<BaseDataType> > _inputQ;
std::mutex _inputMutex;
- TaskApiResult _result;
- std::mutex _result_lock;
- 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 _async_mode { false };
- bool isKeyValid(std::string key);
void updateResult(BaseDataType &input_data);
std::unique_ptr<AsyncManager<ImageDataType, TaskApiResult> > _async_manager;
--- /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 <opencv2/opencv.hpp>
+#include "SingleoException.h"
+#include "InferenceNode.h"
+#include "BridgeNode.h"
+#include "EndpointNode.h"
+#include "InferenceTaskFactory.h"
+#include "ObjectDetectionTask.h"
+
+using namespace std;
+using namespace singleo::inference;
+using namespace singleo::exception;
+
+namespace singleo
+{
+namespace services
+{
+namespace taskapi
+{
+ObjectDetectionTask::ObjectDetectionTask()
+{}
+
+ObjectDetectionTask::~ObjectDetectionTask()
+{}
+
+void ObjectDetectionTask::setupPipeline(std::unique_ptr<TaskManager> &taskManager)
+{
+ auto factory = InferenceTaskFactory::instance().create("MvInferenceTaskFactory");
+
+ taskManager->requestNewNode(NodeType::STARTPOINT, "startpoint");
+
+ auto object_detection_node = taskManager->requestNewNode(NodeType::INFERENCE, "object_detection");
+ dynamic_cast<InferenceNode *>(object_detection_node)->setInferenceTask(factory->createObjectDetection());
+
+ taskManager->requestNewNode(NodeType::ENDPOINT, "endpoint");
+ taskManager->resetPipeline();
+
+ taskManager->addEdge("startpoint", "object_detection");
+ taskManager->addEdge("object_detection", "endpoint");
+
+ taskManager->verify();
+}
+
+void ObjectDetectionTask::resetPipeline(std::unique_ptr<TaskManager> &taskManager)
+{
+ taskManager->clear();
+}
+
+void ObjectDetectionTask::renderOn(BaseDataType &data)
+{
+ auto preprocessedImg = dynamic_cast<ImageDataType &>(data);
+ unique_lock<mutex> lock(_result_lock);
+
+ if (_result.is_valid) {
+ auto rects = _result.bboxes;
+ auto labels = _result.labels;
+ cv::Mat imgCv(cv::Size(preprocessedImg.width, preprocessedImg.height), CV_MAKETYPE(CV_8U, 3),
+ preprocessedImg.ptr);
+
+ lock.unlock();
+
+ for (unsigned int idx = 0; idx < rects.size(); ++idx) {
+ cv::rectangle(imgCv, cv::Point(rects[idx].left, rects[idx].top),
+ cv::Point(rects[idx].right, rects[idx].bottom), cv::Scalar(255, 0, 0), 3);
+ 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);
+ }
+
+ lock.lock();
+ _result.is_valid = false;
+ }
+}
+
+void ObjectDetectionTask::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::OBJECT_DETECTION) {
+ _result.bboxes = dynamic_cast<OdResultType &>(*output)._rects;
+ _result.labels = dynamic_cast<OdResultType &>(*output)._labels;
+ _result.is_valid = true;
+ }
+ }
+}
+
+TaskApiResult &ObjectDetectionTask::getResult()
+{
+ unique_lock<mutex> lock(_result_lock);
+
+ return _result;
+}
+
+int ObjectDetectionTask::getNumOfResult(const TaskApiResult &result)
+{
+ unique_lock<mutex> lock(_result_lock);
+
+ _result = result;
+
+ return _result.bboxes.size();
+}
+
+bool ObjectDetectionTask::isKeyValid(const std::string &key)
+{
+ return (_result_keys.find(key) != _result_keys.end());
+}
+
+unsigned int ObjectDetectionTask::getResultInt(unsigned int idx, const std::string &key)
+{
+ if (!isKeyValid(key))
+ throw InvalidParameter("A given result key is inavlid.");
+
+ unique_lock<mutex> lock(_result_lock);
+
+ switch (_result_keys[key]) {
+ case TaskApiResultType::MIN_X:
+ return _result.bboxes[idx].left;
+ case TaskApiResultType::MIN_Y:
+ return _result.bboxes[idx].top;
+ case TaskApiResultType::MAX_X:
+ return _result.bboxes[idx].right;
+ case TaskApiResultType::MAX_Y:
+ return _result.bboxes[idx].bottom;
+ default:
+ throw InvalidParameter("A given result key is inavlid.");
+ }
+}
+
+const char *ObjectDetectionTask::getResultStr(unsigned int idx, const std::string &key)
+{
+ if (!isKeyValid(key))
+ throw InvalidParameter("A given result key is inavlid.");
+
+ unique_lock<mutex> lock(_result_lock);
+
+ if (_result.labels.empty())
+ throw NoData("Label not provided.");
+
+ switch (_result_keys[key]) {
+ case TaskApiResultType::LABEL:
+ return _result.labels[idx].c_str();
+ default:
+ throw InvalidParameter("A given result key is inavlid.");
+ }
+}
+
+}
+}
+}
#include <map>
#include "SingleoException.h"
#include "TaskApi.h"
-#include "InferenceTaskFactory.h"
#include "SingleoLog.h"
#include "ImagePreprocessor.h"
#include "ServiceFactory.h"
#include "BridgeNode.h"
#include "EndpointNode.h"
+#include "ObjectDetectionTask.h"
+
using namespace std;
using namespace singleo::inference;
using namespace singleo::input;
TaskApi::TaskApi()
{
- // In default, we will use Inference service factory for Mediavision to use Mediavision framework
- // for inference service. TODO. introduce meta config file approach later.
- auto factory = InferenceTaskFactory::instance().create("MvInferenceTaskFactory");
-
_taskManager = make_unique<TaskManager>();
-
- _taskManager->requestNewNode(NodeType::STARTPOINT, "startpoint");
-
- auto object_detection_node = _taskManager->requestNewNode(NodeType::INFERENCE, "object_detection");
- dynamic_cast<InferenceNode *>(object_detection_node)->setInferenceTask(factory->createObjectDetection());
-
- _taskManager->requestNewNode(NodeType::ENDPOINT, "endpoint");
-
_preprocessor = make_unique<ImagePreprocessor>();
+}
- _taskManager->resetPipeline();
-
- _taskManager->addEdge("startpoint", "object_detection");
- _taskManager->addEdge("object_detection", "endpoint");
+TaskApi::~TaskApi()
+{
+ if (_async_mode) {
+ _input_service->streamOff();
+ _async_manager->destroy();
+ }
- _taskManager->verify();
+ if (_task)
+ _task->resetPipeline(_taskManager);
}
void TaskApi::configure(ServiceConfigBase &config)
// Create InputCamera service if input service type is CAMERA.
if (taskapi_config.input_config->_input_feed_type == InputFeedType::CAMERA) {
- CameraConfig *cameraConfig = NULL;
-
try {
CameraConfig &cameraConfig = dynamic_cast<CameraConfig &>(*taskapi_config.input_config);
SINGLEO_LOGD("Camera input service has been initialized.");
}
- SINGLEO_LOGD("task name : %s", taskapi_config._task_name.c_str());
-}
-
-TaskApi::~TaskApi()
-{
- if (_async_mode) {
- _input_service->streamOff();
- _async_manager->destroy();
+ if (taskapi_config._task_name == "OBJECT_DETECTION") {
+ _task = make_unique<ObjectDetectionTask>();
+ _task->setupPipeline(_taskManager);
+ } else {
+ throw InvalidOperation("Unsupported task name.");
}
-
- _taskManager->clear();
}
void TaskApi::inputFeedCb(BaseDataType &data)
auto preprocessedImg = dynamic_cast<ImageDataType &>(_preprocessor->getOutput());
if (_user_cb) {
- cv::Mat imgCv(cv::Size(preprocessedImg.width, preprocessedImg.height), CV_MAKETYPE(CV_8U, 3),
- preprocessedImg.ptr);
-
- _result_lock.lock();
-
- if (_result.is_valid) {
- auto &rects = _result.bboxes;
- auto &labels = _result.labels;
-
- _result_lock.unlock();
-
- for (unsigned int idx = 0; idx < rects.size(); ++idx) {
- cv::rectangle(imgCv, cv::Point(rects[idx].left, rects[idx].top),
- cv::Point(rects[idx].right, rects[idx].bottom), cv::Scalar(255, 0, 0), 3);
- 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);
- }
-
- _result_lock.lock();
- _result.is_valid = false;
- _result_lock.unlock();
- }
-
- _result_lock.unlock();
+ _task->renderOn(data);
_user_cb(preprocessedImg.ptr, preprocessedImg.width, preprocessedImg.height, preprocessedImg.byte_per_pixel, _user_data);
}
delete originalImg->ptr;
}
-bool TaskApi::isKeyValid(std::string key)
-{
- auto it = _result_keys.find(key);
- if (it == _result_keys.end())
- return false;
-
- return true;
-}
-
void TaskApi::add_input(BaseDataType &input_data)
{
if (_inputQ.size() > 0) {
void TaskApi::updateResult(BaseDataType &in_data)
{
- TaskApiResult taskapi_result;
-
- for (auto &output : _taskManager->output()) {
- if (output->_is_empty)
- continue;
+ _task->updateResult(_taskManager->output());
- if (output->_type == ResultType::OBJECT_DETECTION) {
- taskapi_result.bboxes = dynamic_cast<OdResultType &>(*output)._rects;
- taskapi_result.labels = dynamic_cast<OdResultType &>(*output)._labels;
- taskapi_result.is_valid = true;
- }
- }
-
- if (_async_mode) {
- _async_manager->pushOutput(taskapi_result);
- } else {
- _result_lock.lock();
- _result = taskapi_result;
- _result_lock.unlock();
- }
+ if (_async_mode)
+ _async_manager->pushOutput(_task->getResult());
}
void TaskApi::getResultCnt(unsigned int *cnt)
{
if (_async_mode) {
- auto result = _async_manager->popOutput();
- std::unique_lock<std::mutex> lock(_result_lock);
-
- _result = result;
- *cnt = static_cast<unsigned int>(_result.bboxes.size());
+ *cnt = static_cast<unsigned int>(_task->getNumOfResult(_async_manager->popOutput()));
return;
}
- std::unique_lock<std::mutex> lock(_result_lock);
-
- *cnt = static_cast<unsigned int>(_result.bboxes.size());
+ *cnt = static_cast<unsigned int>(_task->getNumOfResult(_task->getResult()));
}
void TaskApi::getResultInt(unsigned int idx, std::string key, unsigned int *value)
SINGLEO_LOGD("given key = %s", key.c_str());
- if (!isKeyValid(key))
- throw InvalidParameter("A given result key is inavlid.");
-
- std::unique_lock<std::mutex> lock(_result_lock);
-
- switch (_result_keys[key]) {
- case TaskApiResultType::MIN_X:
- *value = _result.bboxes[idx].left;
- break;
- case TaskApiResultType::MIN_Y:
- *value = _result.bboxes[idx].top;
- break;
- case TaskApiResultType::MAX_X:
- *value = _result.bboxes[idx].right;
- break;
- case TaskApiResultType::MAX_Y:
- *value = _result.bboxes[idx].bottom;
- break;
- }
+ *value = _task->getResultInt(idx, key);
}
void TaskApi::getResultStr(unsigned int idx, std::string key, const char **value)
SINGLEO_LOGD("given key = %s", key.c_str());
- if (!isKeyValid(key))
- throw InvalidParameter("A given result key is inavlid.");
-
- std::unique_lock<std::mutex> lock(_result_lock);
-
- switch (_result_keys[key]) {
- case TaskApiResultType::LABEL:
- *value = _result.labels[idx].c_str();
- break;
- }
+ *value = _task->getResultStr(idx, key);
}
void TaskApi::registerUserCallback(singleo_user_cb_t user_cb, void *user_data)
ret = singleo_service_get_result_int(handle, idx, "MAX_Y", &max_y);
ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+ cout << "x1 = " << min_x << " y1 = " << min_y << " x2 = " << max_x << " y2 = " << max_y;
+
const char *label {};
ret = singleo_service_get_result_str(handle, idx, "LABEL", &label);
- ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+ if (ret == SINGLEO_ERROR_NONE)
+ cout << " label = " << label;
-
- cout << "x1 = " << min_x << " y1 = " << min_y << " x2 = " << max_x << " y2 = " << max_y << " label = " << label << endl;
+ cout << endl;
}
if (++frame_number > 5000 && cnt > 0)
void user_callback(unsigned char *buffer, unsigned int width, unsigned int height, unsigned int bytes_per_pixel,
void *user_data)
{
- Context *context = static_cast<Context *>(user_data);
cv::Mat result(cv::Size(width, height), CV_MAKETYPE(CV_8U, 3), buffer);
cv::Mat resized;