struct BaseResultType {
ResultType _type { ResultType::NONE };
+ bool _is_empty { true };
unsigned int _frame_number {};
BaseResultType(ResultType type) : _type(type)
{}
if (ret != MEDIA_VISION_ERROR_NONE)
throw runtime_error("Fail to get face detection result count.");
+ _output_data._is_empty = result_cnt == 0;
_output_data._rects.clear();
_output_data._frame_number = frame_number;
if (ret != MEDIA_VISION_ERROR_NONE)
throw runtime_error("Fail to get face landmark detection result count.");
+ _output_data._is_empty = result_cnt == 0;
_output_data._points.clear();
_output_data._frame_number = frame_number;
const char *out_label {};
int ret = mv_face_recognition_get_label(_handle, &out_label);
- if (ret != MEDIA_VISION_ERROR_NO_DATA) {
+ if (ret == MEDIA_VISION_ERROR_NO_DATA) {
+ _output_data._is_empty = true;
_output_data._label = "none";
return _output_data;
}
if (ret != MEDIA_VISION_ERROR_NONE)
throw runtime_error("Fail to get image classification result count.");
+ _output_data._is_empty = result_cnt == 0;
_output_data._labels.clear();
_output_data._frame_number = frame_number;
if (ret != MEDIA_VISION_ERROR_NONE)
throw runtime_error("Fail to get object detection result count.");
+ _output_data._is_empty = result_cnt == 0;
_output_data._rects.clear();
_output_data._frame_number = frame_number;
{
class BridgeNode : public CallbackNode
{
-private:
- bool _enabled { false };
-
public:
explicit BridgeNode(const std::string &name)
{
if (!_cb)
throw singleo::exception::InvalidOperation("Bridge node callback is not set");
+ _status = NodeStatus::INVALID;
+ // If at least one dependency is valid, bridge node is valid.
+ for (auto &dep : this->getDependencies()) {
+ if (std::dynamic_pointer_cast<TaskNode>(dep)->getStatus() == NodeStatus::VALID) {
+ _status = NodeStatus::VALID;
+ break;
+ }
+ }
+ if (_status == NodeStatus::INVALID)
+ return;
+
_results.clear();
for (const auto &d : _dependencies)
_cb(this);
_inputBuffer->release();
- _enabled = false;
+ _status = NodeStatus::INVALID;
// Bridge node got the result from previous task node so enable this bridge node.
if (_outputBuffer)
- _enabled = true;
- }
-
- bool isEnabled()
- {
- return _enabled;
+ _status = NodeStatus::VALID;
}
};
{
protected:
NodeType _type { NodeType::NONE };
+ NodeStatus _status { NodeStatus::NONE };
std::string _name;
std::vector<std::shared_ptr<INode> > _dependencies;
std::vector<std::shared_ptr<INode> > _nexts;
virtual ~CallbackNode() = default;
NodeType getType() override;
+ NodeStatus getStatus() override;
std::string &getName() override
{
return _name;
void invoke() final
{
+ _status = NodeStatus::INVALID;
+ // If at least one dependency is valid, endpoint node is valid.
+ for (auto &dep : this->getDependencies()) {
+ if (std::dynamic_pointer_cast<TaskNode>(dep)->getStatus() == NodeStatus::VALID) {
+ _status = NodeStatus::VALID;
+ break;
+ }
+ }
+ if (_status == NodeStatus::INVALID)
+ return;
+
_results.clear();
for (auto &d : _dependencies)
{
enum class NodeType { NONE, INFERENCE, TRAINING, BRIDGE, ENDPOINT };
+enum class NodeStatus { NONE, VALID, INVALID };
class INode
{
public:
virtual ~INode() {};
virtual NodeType getType() = 0;
+ virtual NodeStatus getStatus() = 0;
virtual std::string &getName() = 0;
virtual void setInputBuffer(std::shared_ptr<SharedBuffer> &inputBuffer) = 0;
virtual std::shared_ptr<SharedBuffer> &getInputBuffer() = 0;
{
protected:
NodeType _type { NodeType::NONE };
+ NodeStatus _status { NodeStatus::NONE };
std::string _name;
std::vector<std::shared_ptr<INode> > _dependencies;
std::vector<std::shared_ptr<INode> > _nexts;
virtual ~TaskNode() = default;
NodeType getType() override;
+ NodeStatus getStatus() override;
std::string &getName() override
{
return _name;
return _type;
}
+NodeStatus CallbackNode::getStatus()
+{
+ return _status;
+}
+
void CallbackNode::setInputBuffer(std::shared_ptr<SharedBuffer> &inputBuffer)
{
inputBuffer->addRef();
#include "SingleoLog.h"
#include "InferenceNode.h"
+#include "CallbackNode.h"
using namespace std;
using namespace singleo::inference;
void InferenceNode::invoke()
{
+ // Note: Inference node can have at max one dependency.
+ for (auto &dep : this->getDependencies()) {
+ if (dynamic_pointer_cast<CallbackNode>(dep)->getStatus() == NodeStatus::INVALID) {
+ _status = NodeStatus::INVALID;
+ return;
+ }
+ }
auto &inputs = _inputBuffer->getInputs();
// TODO. consider for multiple inputs later.
_results.clear();
- _resultMutex.lock();
- _results.push_back(_task->result().clone());
-
- _resultMutex.unlock();
+ _status = NodeStatus::INVALID;
+ if (!_task->result()._is_empty) {
+ _resultMutex.lock();
+ _results.push_back(_task->result().clone());
+ _resultMutex.unlock();
+ _status = NodeStatus::VALID;
+ }
}
std::vector<std::shared_ptr<BaseResultType> > &InferenceNode::results()
// If InferenceNode has dependency node then the dependency node must be BridgeNode like above graph.
// Add output from the BridgeNode to InferenceNode as input.
if (!node->getDependencies().empty()) {
- // TODO. consider for multiple dependencies later.
auto &callbackNode = node->getDependencies()[0];
auto &outputBuffer = callbackNode->getOutputBuffer();
- node->setInputBuffer(outputBuffer);
+ if (outputBuffer) {
+ node->setInputBuffer(outputBuffer);
- // output buffer has been shared to node so release it here.
- outputBuffer->release();
+ // output buffer has been shared to node so release it here.
+ outputBuffer->release();
+ }
}
} else {
// TODO. consider for mulitple inputs later.
// Spawn threads for next nodes
for (auto &n : node->getNexts()) {
- if (node->getType() == NodeType::BRIDGE) {
- auto b_node = dynamic_pointer_cast<BridgeNode>(node);
-
- // In case of BRIDGE node, if this bridge node didn't get the result from previous task node,
- // isEnabled() is false. So if isEnabled() is false, stop all sub graph pipelines connected to this node.
- if (!b_node->isEnabled()) {
- n->wakeup();
- continue;
- }
- }
-
std::lock_guard<std::mutex> lock(_thread_mutex);
if (_is_thread_created.find(n) == _is_thread_created.end()) {
_threads.push(make_shared<thread>(&TaskManager::threadCb, this, std::ref(n)));
// Verify graph rule.
for (auto &node : _nodes) {
if (node->getNexts().size() == 0 && node->getType() != NodeType::ENDPOINT) {
- SINGLEO_LOGE("Last node should be endpoint node.");
- throw InvalidOperation("Last node should be endpoint node.");
+ SINGLEO_LOGE("All nodes except endpoint node must have a next node.");
+ throw InvalidOperation("All nodes except endpoint node must have a next node.");
}
if (node->getType() == NodeType::BRIDGE) {
}
}
} else if (node->getType() == NodeType::INFERENCE || node->getType() == NodeType::TRAINING) {
+ if (node->getDependencies().size() > 1) {
+ SINGLEO_LOGE("The inference or training node cannot have more than one dependency.");
+ throw InvalidOperation("The inference or training node cannot have more than one dependency.");
+ }
// Verify if InferenceNode or TrainingNode is located in front of BridgeNode, behind BridgeNode or in front of EndpointNode.
// ... inference or training node ---- bridge node ...
// ... bridge node ---- inference or training node ...
return _type;
}
+NodeStatus TaskNode::getStatus()
+{
+ return _status;
+}
+
void TaskNode::setInputBuffer(std::shared_ptr<SharedBuffer> &inputBuffer)
{
inputBuffer->addRef();