serices: add test suite app for testing task api
authorInki Dae <inki.dae@samsung.com>
Thu, 23 Jan 2025 08:12:15 +0000 (17:12 +0900)
committerInki Dae <inki.dae@samsung.com>
Mon, 3 Feb 2025 02:14:17 +0000 (11:14 +0900)
Change-Id: I1f33dd53fc7315fd50db365dcc40225ffaa6faea
Signed-off-by: Inki Dae <inki.dae@samsung.com>
17 files changed:
capi/singleo_native_capi.h
common/include/SingleoCommonTypes.h
inference/backends/mediavision/src/MvObjectDetection.cpp
packaging/singleo.spec
services/CMakeLists.txt
services/air_gesture/include/AirGesture.h
services/auto_zoom/include/AutoZoom.h
services/common/include/IService.h
services/common/include/ServiceDataType.h
services/focus_finder/include/FocusFinder.h
services/singleo_native_capi.cpp
services/task_api_test/CMakeLists.txt [new file with mode: 0644]
services/task_api_test/include/DataTypes.h [new file with mode: 0644]
services/task_api_test/include/TaskApiTest.h [new file with mode: 0644]
services/task_api_test/src/TaskApiTest.cpp [new file with mode: 0644]
test/services/CMakeLists.txt
test/services/test_task_api_on_screen.cpp [new file with mode: 0644]

index 10058fc5d2ebe60f42e008cc96512d834840034d..95321c2ff1e2cad401bfa8b28aeba797368d32d0 100644 (file)
@@ -253,7 +253,7 @@ int singleo_service_get_result_float(singleo_service_h handle, unsigned int idx,
  * @pre Create a source handle by calling singleo_service_create()
  * @pre Perform a requested service by calling singleo_service_perform()
  */
-int singleo_service_get_result_str(singleo_service_h handle, unsigned int idx, const char *key, char **value);
+int singleo_service_get_result_str(singleo_service_h handle, unsigned int idx, const char *key, const char **value);
 
 /**
  * @internal
index fa165d59f92283d5650c0058ed214735344d83a5..c09dd52124ea801ffad0f6b67758cb846c00c27d 100644 (file)
@@ -189,6 +189,7 @@ struct OdResultType : public BaseResultType {
        OdResultType() : BaseResultType(ResultType::OBJECT_DETECTION)
        {}
        std::vector<Rect> _rects;
+       std::vector<std::string> _labels;
 
        std::shared_ptr<BaseResultType> clone() override
        {
index a23f3e5df7942c25c965f947c849888cc7370e09..b223082b5f75a4cb8699d51d98cff95bb81b6d06 100644 (file)
@@ -111,6 +111,16 @@ BaseResultType &MvObjectDetection::result()
                if (ret != MEDIA_VISION_ERROR_NONE)
                        throw runtime_error("Fail to get object detection bound box.");
 
+               const char *label = nullptr;
+
+               ret = mv_object_detection_get_label(_handle, idx, &label);
+               if (ret != MEDIA_VISION_ERROR_NO_DATA && ret != MEDIA_VISION_ERROR_NONE)
+                       throw runtime_error("Fail to get a label to detected object.");
+
+               // If there is no label, it means that the model does not provide a label. In this case, we do not push the label.
+               if (ret != MEDIA_VISION_ERROR_NO_DATA)
+                       _output_data._labels.push_back(label);
+
                _output_data._rects.push_back(rect);
        }
 
index 5fa7bf8ee1a2464e51997674f888af9fdbfac447..6a120a465befdab8325038615c0291d2d6e237d6 100644 (file)
@@ -132,3 +132,4 @@ rm -rf %{buildroot}
 %{_bindir}/test_singleo_airgesture
 %endif
 %{_bindir}/test_singleo_task_manger
+%{_bindir}/test_singleo_task_api
index ef6c4427eaffb0be303141471d61118088aa49ff..5675ee21e5dad04ed2ed412f54baea01f511ebc4 100644 (file)
@@ -24,6 +24,8 @@ IF (${USE_AIRGESTURE_API})
     INCLUDE(air_gesture/CMakeLists.txt)
 ENDIF()
 
+INCLUDE(task_api_test/CMakeLists.txt)
+
 ADD_LIBRARY(${PROJECT_NAME} SHARED ${SINGLEO_SERVICE_SOURCE_FILES})
 
 TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PRIVATE include common/include ${ROOT_DIRECTORY}/capi/ ${COMMON_HEADER_LIST} ${INPUT_HEADER_LIST} ${INFERENCE_HEADER_LIST} ${SERVICE_HEADER_LIST} ${LOG_HEADER_LIST})
index d3004eea514b9e2686b66c4845e4eb4aead44f8c..81803d20dcccd2cc04e57f79ac0c9390896778dd 100644 (file)
@@ -86,6 +86,10 @@ public:
                throw exception::InvalidOperation("Not supported yet.");
        }
        void getResultFloat(unsigned int idx, std::string key, float *value) override;
+       void getResultStr(unsigned int idx, std::string key, const char **value) override
+       {
+               throw exception::InvalidOperation("Not supported yet.");
+       }
        void registerUserCallback(singleo_user_cb_t user_cb, void *user_data) override;
        void unregisterUserCallback() override;
 
index 9cc75268887902ec38712418fd2e4cd8e31f96f8..cc58a132da29e2c4074e739ebf3b0eeb990ef6bb 100644 (file)
@@ -83,6 +83,10 @@ public:
        {
                throw exception::InvalidOperation("Not support yet.");
        }
+       void getResultStr(unsigned int idx, std::string key, const char **value) override
+       {
+               throw exception::InvalidOperation("Not supported yet.");
+       }
        void registerUserCallback(singleo_user_cb_t user_cb, void *user_data) override;
        void unregisterUserCallback() override;
 
index a21314adbc4d1a8221e1851b9feca9a6fb687361..282ce3c075fdff89d31ef254e60668cf4c8d1ad8 100644 (file)
@@ -37,6 +37,7 @@ public:
        virtual void getResultCnt(unsigned int *cnt) = 0;
        virtual void getResultInt(unsigned int idx, std::string key, unsigned int *value) = 0;
        virtual void getResultFloat(unsigned int idx, std::string key, float *value) = 0;
+       virtual void getResultStr(unsigned int idx, std::string key, const char **value) = 0;
        virtual void registerUserCallback(singleo_user_cb_t user_cb, void *user_data) = 0;
        virtual void unregisterUserCallback() = 0;
 };
index 3a28fc1e84b932105583e1f84eb78e2e1b6315ee..6e36a11c804272f357a2ad2a060282eb5b187475 100644 (file)
@@ -26,7 +26,7 @@ namespace services
 {
 // TODO
 
-enum class ServiceResultType { NONE, AUTO_ZOOM, FOCUS_FINDER, AIR_GESTURE };
+enum class ServiceResultType { NONE, AUTO_ZOOM, FOCUS_FINDER, AIR_GESTURE, TASK_API_TEST };
 
 struct ServiceBaseResultType {
        ServiceResultType _result_type { ServiceResultType::NONE };
index 3a7a35705f681c2de023937d28ee39af2f4d2b30..4dc7983c9fa91d3a72881e97e73795edec1615fa 100644 (file)
@@ -82,6 +82,10 @@ public:
        {
                throw exception::InvalidOperation("Not supported yet.");
        }
+       void getResultStr(unsigned int idx, std::string key, const char **value) override
+       {
+               throw exception::InvalidOperation("Not supported yet.");
+       }
        void registerUserCallback(singleo_user_cb_t user_cb, void *user_data) override;
        void unregisterUserCallback() override;
 };
index 2dfd8c8bf9f996e8379ef0cef41eabe4c0f5b624..179020ef4b6dfa1c9bbb8e57a1c4e242021f225d 100644 (file)
@@ -205,6 +205,19 @@ int singleo_service_get_result_float(singleo_service_h handle, unsigned int idx,
        return SINGLEO_ERROR_NONE;
 }
 
+int singleo_service_get_result_str(singleo_service_h handle, unsigned int idx, const char *key, const char **value)
+{
+       try {
+               auto context = static_cast<Context *>(handle);
+               context->_service_handle->getResultStr(idx, key, value);
+       } catch (const BaseException &e) {
+               SINGLEO_LOGE("%s", e.what());
+               return e.getError();
+       }
+
+       return SINGLEO_ERROR_NONE;
+}
+
 int singleo_service_register_user_callback(singleo_service_h handle, singleo_user_cb_t user_cb, void *user_data)
 {
        try {
diff --git a/services/task_api_test/CMakeLists.txt b/services/task_api_test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e23fa0d
--- /dev/null
@@ -0,0 +1,7 @@
+SET(SINGLEO_SERVICE_SOURCE_FILES
+    ${SINGLEO_SERVICE_SOURCE_FILES}
+    task_api_test/src/TaskApiTest.cpp
+)
+
+LIST(APPEND SERVICE_LIBRARY_LIST ${SERVICE_LIBRARY_LIST})
+LIST(APPEND SERVICE_HEADER_LIST ${SERVICE_HEADER_LIST} ${CMAKE_CURRENT_SOURCE_DIR}/task_api_test/include)
\ No newline at end of file
diff --git a/services/task_api_test/include/DataTypes.h b/services/task_api_test/include/DataTypes.h
new file mode 100644 (file)
index 0000000..6830c75
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * 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 __DATA_TYPES_H__
+#define __DATA_TYPES_H__
+
+#include <vector>
+#include "SingleoCommonTypes.h"
+#include "ServiceDataType.h"
+
+namespace singleo
+{
+namespace services
+{
+namespace taskapitest
+{
+enum class TaskApiResultType { MIN_X, MIN_Y, MAX_X, MAX_Y, LABEL };
+
+struct TaskApiResult : public ServiceBaseResultType {
+       TaskApiResult() : ServiceBaseResultType(ServiceResultType::TASK_API_TEST)
+       {}
+       bool is_valid { false };
+       unsigned int frame_number {};
+       unsigned int num_of_objects {};
+       std::vector<Rect> bboxes;
+       std::vector<std::string> labels;
+};
+
+}
+}
+}
+
+#endif
\ No newline at end of file
diff --git a/services/task_api_test/include/TaskApiTest.h b/services/task_api_test/include/TaskApiTest.h
new file mode 100644 (file)
index 0000000..f17acb4
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ * 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 __TASK_API_TEST_H__
+#define __TASK_API_TEST_H__
+
+#include <opencv2/opencv.hpp>
+#include <mutex>
+
+#include "IService.h"
+#include "SingleoException.h"
+#include "SingleoCommonTypes.h"
+#include "IInputService.h"
+#include "SingleoInferenceTypes.h"
+#include "InputTypes.h"
+#include "IInputObserver.h"
+#include "ServiceDataType.h"
+#include "DataTypes.h"
+#include "AsyncManager.h"
+#include "IPreprocessor.h"
+#include "TaskManager.h"
+
+namespace singleo
+{
+namespace services
+{
+namespace taskapitest
+{
+class TaskApiTest : public IService, public input::IInputObserver
+{
+private:
+       static bool _registered;
+       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;
+       singleo_user_cb_t _user_cb {};
+       void *_user_data {};
+       std::unique_ptr<IPreprocessor> _preprocessor;
+
+public:
+       TaskApiTest();
+       virtual ~TaskApiTest();
+
+       static IService *create()
+       {
+               return new TaskApiTest();
+       }
+
+       // This function will be called by specific input service internally.
+       // Ps. caller has to provide captured data with concrete class object as data parameter.
+       void inputFeedCb(BaseDataType &data) override;
+       void configure(input::InputConfigBase &config) override;
+       void add_input(BaseDataType &input_data) override;
+       void perform() override;
+       void performAsync() override;
+       void getResultCnt(unsigned int *cnt) override;
+       void getResultInt(unsigned int idx, std::string key, unsigned int *value) override;
+       void getResultFloat(unsigned int idx, std::string key, float *value) override
+       {
+               throw exception::InvalidOperation("Not supported yet.");
+       }
+       void getResultStr(unsigned int idx, std::string key, const char **value) override;
+       void registerUserCallback(singleo_user_cb_t user_cb, void *user_data) override;
+       void unregisterUserCallback() override;
+
+       void runTaskManager(BaseDataType &input_data);
+};
+
+} // taskapitest
+} // service
+} // singleo
+
+#endif
\ No newline at end of file
diff --git a/services/task_api_test/src/TaskApiTest.cpp b/services/task_api_test/src/TaskApiTest.cpp
new file mode 100644 (file)
index 0000000..7e162b6
--- /dev/null
@@ -0,0 +1,326 @@
+/**
+ * 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 "TaskApiTest.h"
+#include "InferenceTaskFactory.h"
+#include "SingleoLog.h"
+#include "ImagePreprocessor.h"
+#include "ServiceFactory.h"
+#include "InputCamera.h"
+#include "InputTypes.h"
+#include "InferenceNode.h"
+#include "BridgeNode.h"
+#include "EndpointNode.h"
+
+using namespace std;
+using namespace singleo::inference;
+using namespace singleo::input;
+using namespace singleo::exception;
+
+namespace singleo
+{
+namespace services
+{
+namespace taskapitest
+{
+bool TaskApiTest::_registered = registerService<TaskApiTest>("TaskApiTest");
+
+TaskApiTest::TaskApiTest()
+{
+       // 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");
+
+       _taskManager->verify();
+}
+
+void TaskApiTest::configure(InputConfigBase &config)
+{
+       // Create InputCamera service if input service type is CAMERA.
+       if (config._input_feed_type == InputFeedType::CAMERA) {
+               CameraConfig cameraConfig;
+
+               try {
+                       cameraConfig = dynamic_cast<CameraConfig &>(config);
+               } catch (bad_cast &e) {
+                       SINGLEO_LOGE("Camera input service requires CameraConfig.");
+                       throw InvalidOperation("Camera input service requires CameraConfig.");
+               }
+
+               _input_service = make_unique<InputCamera>(cameraConfig.backend_type);
+               _input_service->registerObserver(this);
+               _input_service->configure(cameraConfig);
+
+               SINGLEO_LOGD("Camera input service has been initialized.");
+       }
+}
+
+TaskApiTest::~TaskApiTest()
+{
+       if (_async_mode) {
+               _input_service->streamOff();
+               _async_manager->destroy();
+       }
+
+       _taskManager->clear();
+}
+
+void TaskApiTest::inputFeedCb(BaseDataType &data)
+{
+       _preprocessor->addInput(data);
+
+       auto originalImg = dynamic_pointer_cast<ImageDataType>(_preprocessor->getOutput().clone());
+       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();
+               _user_cb(preprocessedImg.ptr, preprocessedImg.width, preprocessedImg.height, preprocessedImg.byte_per_pixel, _user_data);
+       }
+
+       _preprocessor->invoke();
+
+       // Set is_owned to false to prevent from releasing copied image data
+       // because the owner of copied is AirGesture so the copied will be released
+       // in performAsync function.
+       // Ps. if is_owned is true, copied image data can be released while task manager is being performed.
+       originalImg->is_owned = false;
+
+       // Make sure to release copied buffer if incoming queue isn't empty so skipped pushing the buffer.
+       // If empty then push the buffer to the incoming queue. This buffer will be released at the end of runTaskManager function.
+       if (_async_manager->pushInput(*originalImg) != SINGLEO_ERROR_NONE)
+               delete originalImg->ptr;
+}
+
+bool TaskApiTest::isKeyValid(std::string key)
+{
+       auto it = _result_keys.find(key);
+       if (it == _result_keys.end())
+               return false;
+
+       return true;
+}
+
+void TaskApiTest::add_input(BaseDataType &input_data)
+{
+       if (_inputQ.size() > 0) {
+               SINGLEO_LOGE("Only one input data is allowed.");
+               throw InvalidOperation("Only one input data is allowed.");
+       }
+
+       if (input_data._data_type != DataType::IMAGE && input_data._data_type != DataType::FILE) {
+               SINGLEO_LOGE("Only IMAGE and FILE types are allowed.");
+               throw InvalidOperation("Only IMAGE and FILE types are allowed.");
+       }
+
+       std::lock_guard<std::mutex> lock(_inputMutex);
+
+       _inputQ.push(input_data.clone());
+}
+
+void TaskApiTest::runTaskManager(BaseDataType &input_data)
+{
+       _taskManager->addInput(input_data);
+       _taskManager->run();
+
+       updateResult(input_data);
+}
+
+void TaskApiTest::perform()
+{
+       ImagePreprocessor preprocessor;
+
+       // If input service is not set, input data comes from user.
+       // In this case, get input data from _inputs.
+       if (!_input_service) {
+               std::lock_guard<std::mutex> lock(_inputMutex);
+
+               _preprocessor->addInput(*_inputQ.front());
+               _inputQ.pop();
+       } else {
+               ImageDataType input_data;
+
+               _input_service->capture(input_data);
+               _preprocessor->addInput(input_data);
+       }
+
+       runTaskManager(_preprocessor->getOutput());
+}
+
+void TaskApiTest::performAsync()
+{
+       if (!_input_service) {
+               SINGLEO_LOGE("This API is valid only the case that input feed service is used.");
+               throw InvalidOperation("Invalid API request.");
+       }
+
+       _input_service->streamOn();
+
+       _async_mode = true;
+       _async_manager = make_unique<AsyncManager<ImageDataType, TaskApiResult> >();
+       _async_manager->registerInvokeCb(this, [this](IService *service, BaseDataType &data) {
+               auto task_api_test = static_cast<TaskApiTest *>(service);
+
+               task_api_test->runTaskManager(data);
+
+               auto imageData = dynamic_cast<ImageDataType &>(data);
+
+               // A given imageData - allocated by inputFeedCb function - has been used so release it here.
+               if (!imageData.is_owned)
+                       delete imageData.ptr;
+       });
+}
+
+void TaskApiTest::updateResult(BaseDataType &in_data)
+{
+       TaskApiResult taskapi_result;
+
+       for (auto &output : _taskManager->output()) {
+               if (output->_is_empty)
+                       continue;
+
+               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();
+       }
+}
+
+void TaskApiTest::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());
+               return;
+       }
+
+       std::unique_lock<std::mutex> lock(_result_lock);
+
+       *cnt = static_cast<unsigned int>(_result.bboxes.size());
+}
+
+void TaskApiTest::getResultInt(unsigned int idx, std::string key, unsigned int *value)
+{
+       transform(key.begin(), key.end(), key.begin(), ::toupper);
+
+       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;
+       }
+}
+
+void TaskApiTest::getResultStr(unsigned int idx, std::string key, const char **value)
+{
+       transform(key.begin(), key.end(), key.begin(), ::toupper);
+
+       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;
+       }
+}
+
+void TaskApiTest::registerUserCallback(singleo_user_cb_t user_cb, void *user_data)
+{
+       _user_cb = user_cb;
+       _user_data = user_data;
+}
+
+void TaskApiTest::unregisterUserCallback()
+{
+       _user_cb = nullptr;
+       _user_data = nullptr;
+}
+
+}
+}
+}
index 33f21d0466447ba57942ab8455b162155adf2267..66ecc383b49f782ce1ff9e6d5fb83b0a2f50832c 100644 (file)
@@ -60,3 +60,13 @@ TARGET_LINK_LIBRARIES(test_singleo_task_manger
     gtest gtest_main pthread singleo_service ${OpenCV_LIBS})
 
 INSTALL(TARGETS test_singleo_task_manger DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+SET(TEST_SOURCE_TASK_API_LIST test_task_api_on_screen.cpp)
+
+ADD_EXECUTABLE(test_singleo_task_api ${TEST_SOURCE_TASK_API_LIST})
+TARGET_COMPILE_DEFINITIONS(test_singleo_task_api PRIVATE -DTEST_RES_PATH="${TEST_RES_PATH}")
+TARGET_INCLUDE_DIRECTORIES(test_singleo_task_api PRIVATE ../../capi/ ../../common/include ../../log/include ../../visualizer/include)
+TARGET_LINK_LIBRARIES(test_singleo_task_api
+    gtest gtest_main pthread singleo_service singleo_visualizer opencv_imgcodecs)
+
+INSTALL(TARGETS test_singleo_task_api DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/test/services/test_task_api_on_screen.cpp b/test/services/test_task_api_on_screen.cpp
new file mode 100644 (file)
index 0000000..2b3cee7
--- /dev/null
@@ -0,0 +1,119 @@
+/**
+ * Copyright (c) 2024 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 <iostream>
+#include <thread>
+#include <opencv2/opencv.hpp>
+
+#include <mutex>
+#include "gtest/gtest.h"
+#include "singleo_native_capi_internal.h"
+#include "singleo_util_visualizer_2d.h"
+#include "singleo_error.h"
+#include "SingleoCommonTypes.h"
+
+using namespace testing;
+using namespace singleo;
+using namespace std;
+
+struct Context {
+       std::mutex _mutex;
+       singleo_service_h handle {};
+       vector<Point> points;
+};
+
+void taskapi_callback(void *user_data)
+{
+       Context *context = static_cast<Context *>(user_data);
+       singleo_service_h handle = static_cast<singleo_service_h>(context->handle);
+       bool is_loop_exit = false;
+       unsigned long frame_number = 0;
+
+       while (!is_loop_exit) {
+               unsigned int cnt;
+
+               int ret = singleo_service_get_result_cnt(handle, &cnt);
+               if (ret != SINGLEO_ERROR_NONE)
+                       break;
+
+               cout << "cnt = " << cnt << " frame number = " << frame_number << endl;
+
+               for (unsigned int idx = 0; idx < cnt; ++idx) {
+                       unsigned int min_x {}, min_y {}, max_x {}, max_y {};
+
+                       ret = singleo_service_get_result_int(handle, idx, "MIN_X", &min_x);
+                       ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+                       ret = singleo_service_get_result_int(handle, idx, "MIN_Y", &min_y);
+                       ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+                       ret = singleo_service_get_result_int(handle, idx, "MAX_X", &max_x);
+                       ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+                       ret = singleo_service_get_result_int(handle, idx, "MAX_Y", &max_y);
+                       ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+                       const char *label {};
+
+                       ret = singleo_service_get_result_str(handle, idx, "LABEL", &label);
+                       ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+
+                       cout << "x1 = " << min_x << " y1 = " << min_y << " x2 = " << max_x << " y2 = " << max_y << " label = " << label << endl;
+               }
+
+               if (++frame_number > 5000 && cnt > 0)
+                       is_loop_exit = true;
+       }
+}
+
+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;
+
+       cv::resize(result, resized, cv::Size(1600, 900));
+
+       singleo_util_visualizer_2d(resized, NULL);
+}
+
+TEST(TaskApiAsyncOnScreenTest, ODRequestWithCameraInputFeedShouldBeOk)
+{
+       Context context {};
+
+       int ret = singleo_service_create("service=TaskApiTest, 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>(&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);
+}