SSCMA-Micro CPP SDK  v2.0.0
SSCMA-Micro is a cross-platform machine learning inference framework designed for embedded devices.
invoke.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include <ma_config_board.h>
4 
5 #include <algorithm>
6 #include <functional>
7 #include <memory>
8 #include <string>
9 #include <vector>
10 
11 #include "core/ma_core.h"
12 #include "core/utils/ma_base64.h"
13 #include "porting/ma_porting.h"
14 #include "refactor_required.hpp"
15 #include "resource.hpp"
17 
18 extern "C" {
19 
20 #if MA_INVOKE_ENABLE_RUN_HOOK
21 extern void ma_invoke_pre_hook(void*);
22 extern void ma_invoke_post_hook(void*);
23 #endif
24 }
25 
26 namespace ma::server::callback {
27 
28 using namespace ma;
29 
30 class Invoke final : public std::enable_shared_from_this<Invoke> {
31 public:
32  std::shared_ptr<Invoke> getptr() {
33  return shared_from_this();
34  }
35 
36  [[nodiscard]] static std::shared_ptr<Invoke> create(const std::vector<std::string>& args, Transport& transport, Encoder& encoder, size_t task_id) {
37  return std::shared_ptr<Invoke>{new Invoke{args, transport, encoder, task_id}};
38  }
39 
40  ~Invoke() {
41  if (_algorithm != nullptr) {
42  ModelFactory::remove(_algorithm);
43  _algorithm = nullptr;
44  }
45 
46  static_resource->is_sample = false;
47  }
48 
49  inline void run() {
50  prepare();
51  }
52 
53 protected:
54  Invoke(const std::vector<std::string>& args, Transport& transport, Encoder& encoder, size_t task_id) {
55  MA_ASSERT(args.size() >= 2);
56  _cmd = args[0];
57  _n_times = std::atoi(args[1].c_str());
58  _results_only = std::atoi(args[2].c_str());
59 
60  _ret = MA_OK;
61 
62  _sensor = nullptr;
63  _transport = &transport;
64  _encoder = &encoder;
65  _model = ma_model_t{};
66  _algorithm = nullptr;
67 
68  _times = 0;
69 
70  _task_id = task_id;
71 
72  _preprocess_hook_injected = false;
73 
74 #if MA_SENSOR_ENCODE_USE_STATIC_BUFFER
75  _buffer = reinterpret_cast<void*>(MA_SENSOR_ENCODE_STATIC_BUFFER_ADDR);
76  _buffer_size = 0;
77 #endif
78 
79  static_resource->is_sample = true;
80  }
81 
82 private:
83  void prepare() {
84  if (!prepareSensor()) [[unlikely]]
85  goto Err;
86 
87  if (!prepareModel()) [[unlikely]]
88  goto Err;
89 
90  if (!prepareAlgorithm()) [[unlikely]]
91  goto Err;
92 
93  switch (_sensor->getType()) {
95  directReply();
96  return static_resource->executor->submit([_this = std::move(getptr())](const std::atomic<bool>&) { _this->eventLoopCamera(); });
97  default:
98  _ret = MA_ENOTSUP;
99  directReply();
100  }
101 
102 Err:
103  directReply();
104  }
105 
106  bool prepareSensor() {
107  const auto& sensors = static_resource->device->getSensors();
108  auto it = std::find_if(sensors.begin(), sensors.end(), [&](const Sensor* s) { return s->getID() == static_resource->current_sensor_id; });
109  if (it == sensors.end()) {
110  return false;
111  }
112  _sensor = *it;
113  auto* camera = static_cast<Camera*>(_sensor);
114  _ret = camera->startStream(Camera::StreamMode::kRefreshOnReturn);
115  return isEverythingOk();
116  }
117 
118  bool prepareModel() {
119  const auto& models = static_resource->device->getModels();
120  auto it = std::find_if(models.begin(), models.end(), [&](const ma_model_t& m) { return m.id == static_resource->current_model_id; });
121  if (it == models.end()) {
122  _ret = MA_ENOENT;
123  }
124  if (it->addr == nullptr) {
125  _ret = MA_ENOENT;
126  }
127  _model = *it;
128  return isEverythingOk();
129  }
130 
131  bool prepareAlgorithm() {
132 #if MA_USE_FILESYSTEM
133  _ret = static_resource->engine->load(static_cast<const char*>(_model.addr));
134 #else
135  _ret = static_resource->engine->load(_model.addr, _model.size);
136 #endif
137  if (!isEverythingOk()) {
138  return false;
139  }
140  _algorithm = ModelFactory::create(static_resource->engine, static_resource->current_model_id);
141  if (_algorithm == nullptr) {
142  _ret = MA_ENOTSUP;
143  }
144 #if MA_INVOKE_ENABLE_RUN_HOOK
145  _algorithm->setRunDone([](void*) { ma_invoke_post_hook(nullptr); });
146 #endif
147  return isEverythingOk();
148  }
149 
150  void directReply() {
151  _encoder->begin(MA_MSG_TYPE_RESP, _ret, _cmd);
152  _encoder->write(_sensor, _sensor->currentPresetIdx());
153  std::vector<ma_model_t> model{_model};
154  _encoder->write(model);
155  _encoder->write(_algorithm ? static_cast<int>(_algorithm->getType()) : static_cast<int>(static_resource->current_algorithm_id),
156  0,
157  _sensor ? static_cast<int>(_sensor->getType()) : 0,
158  static_cast<int>(static_resource->shared_threshold_score * 100.0),
159  static_cast<int>(static_resource->shared_threshold_nms * 100.0));
160  _encoder->end();
161  _transport->send(reinterpret_cast<const char*>(_encoder->data()), _encoder->size());
162  }
163 
164 
165  void eventReply(int width, int height) {
166 
167  _encoder->begin(MA_MSG_TYPE_EVT, _ret, _cmd);
168  _encoder->write("count", _times);
169 
170  if (!_results_only) {
171 #if MA_SENSOR_ENCODE_USE_STATIC_BUFFER
172  reinterpret_cast<char*>(_buffer)[_buffer_size] = '\0';
173  _encoder->write("image", reinterpret_cast<const char*>(_buffer), _buffer_size);
174 #else
175  _encoder->write("image", _buffer);
176 #endif
177  }
178 
179  serializeAlgorithmOutput(_algorithm, _encoder, width, height);
180 
181  auto perf = _algorithm->getPerf();
182  _encoder->write(perf);
183  if (_event_hook)
184  _event_hook(*_encoder);
185  _encoder->end();
186  _transport->send(reinterpret_cast<const char*>(_encoder->data()), _encoder->size());
187  }
188 
189  void eventLoopCamera() {
190  if ((_n_times >= 0) & (_times++ >= _n_times)) [[unlikely]]
191  return;
192  if (static_resource->current_task_id.load() != _task_id) [[unlikely]]
193  return;
194 
195  MA_LOGD(MA_TAG, "eventLoopCamera");
196 
197  auto camera = static_cast<Camera*>(_sensor);
198  auto frame = ma_img_t{};
199  auto raw_frame = ma_img_t{};
200  int buffer_size = 0;
201 
202  _ret = camera->retrieveFrame(raw_frame, MA_PIXEL_FORMAT_AUTO);
203  if (!isEverythingOk()) [[unlikely]]
204  goto Err;
205  if (!_results_only) {
206  _ret = camera->retrieveFrame(frame, MA_PIXEL_FORMAT_JPEG);
207  if (!isEverythingOk()) [[unlikely]]
208  goto Err;
209 
210  buffer_size = 4 * ((frame.size + 2) / 3);
211 #if MA_SENSOR_ENCODE_USE_STATIC_BUFFER
212  if (buffer_size > MA_SENSOR_ENCODE_STATIC_BUFFER_SIZE) {
213  MA_LOGE(MA_TAG, "buffer_size > MA_SENSOR_ENCODE_STATIC_BUFFER_SIZE");
214  goto Err;
215  }
216 
217 #else
218  if (buffer_size > _buffer.size()) {
219  _buffer.resize(buffer_size + 1);
220  }
221 #endif
222 
223  {
224  auto ret = ma::utils::base64_encode(reinterpret_cast<unsigned char*>(frame.data),
225  frame.size,
226 #if MA_SENSOR_ENCODE_USE_STATIC_BUFFER
227  reinterpret_cast<char*>(_buffer),
228 #else
229  reinterpret_cast<char*>(_buffer.data()),
230 #endif
231  &buffer_size);
232 #if MA_SENSOR_ENCODE_USE_STATIC_BUFFER
233  _buffer_size = buffer_size;
234  static_cast<char*>(_buffer)[buffer_size] = '\0';
235 #else
236  _buffer[buffer_size] = '\0';
237 #endif
238  if (ret != MA_OK) {
239  MA_LOGE(MA_TAG, "base64_encode failed: %d", ret);
240  }
241  }
242 
243  camera->returnFrame(frame);
244  }
245  if (!_preprocess_hook_injected) {
246  _preprocess_hook_injected = true;
247  _algorithm->setPreprocessDone([camera, &raw_frame](void*) {
248  camera->returnFrame(raw_frame);
249 #if MA_INVOKE_ENABLE_RUN_HOOK
250  ma_invoke_pre_hook(nullptr);
251 #endif
252  });
253  }
254 
255  if (!_event_hook) {
256  _event_hook = [&raw_frame](Encoder& encoder) {
257  int16_t rotation = static_cast<int>(raw_frame.rotate) * 90;
258  encoder.write("rotation", rotation);
259  encoder.write("width", raw_frame.width);
260  encoder.write("height", raw_frame.height);
261  };
262  }
263 
264  // update configs TODO: refactor
265  _algorithm->setConfig(MA_MODEL_CFG_OPT_THRESHOLD, static_resource->shared_threshold_score);
266  _algorithm->setConfig(MA_MODEL_CFG_OPT_NMS, static_resource->shared_threshold_nms);
267 
268  _ret = setAlgorithmInput(_algorithm, raw_frame);
269  if (!isEverythingOk()) [[unlikely]]
270  goto Err;
271 
272  {
274  auto trigger_copy = trigger_rules;
276  for (auto& rule : trigger_copy) {
277  if (rule) {
278  (*rule.get())(_algorithm);
279  }
280  }
281  }
282 
283  eventReply(raw_frame.width, raw_frame.height);
284 
285  static_resource->executor->submit([_this = std::move(getptr())](const std::atomic<bool>&) { _this->eventLoopCamera(); });
286  return;
287 
288 Err:
289  eventReply(raw_frame.width, raw_frame.height);
290  }
291 
292  inline bool isEverythingOk() const {
293  return _ret == MA_OK;
294  }
295 
296 private:
297  std::string _cmd;
298  int32_t _n_times;
299 
300  ma_err_t _ret;
301 
302  Sensor* _sensor;
303  Transport* _transport;
304  Encoder* _encoder;
305  ma_model_t _model;
306  Model* _algorithm;
307 
308  size_t _task_id;
309  int32_t _times;
310  bool _results_only;
311 
312  bool _preprocess_hook_injected;
313  std::function<void(Encoder&)> _event_hook;
314 
315 #if MA_SENSOR_ENCODE_USE_STATIC_BUFFER
316 #ifndef MA_SENSOR_ENCODE_STATIC_BUFFER_ADDR
317 #error "MA_SENSOR_ENCODE_STATIC_BUFFER_ADDR is not defined"
318 #endif
319 #ifndef MA_SENSOR_ENCODE_STATIC_BUFFER_SIZE
320 #error "MA_SENSOR_ENCODE_STATIC_BUFFER_SIZE is not defined"
321 #endif
322  void* _buffer;
323  size_t _buffer_size;
324 #else
325  std::string _buffer;
326 #endif
327 };
328 
329 } // namespace ma::server::callback
Definition: ma_camera.h:13
virtual ma_err_t startStream(StreamMode mode) noexcept=0
Definition: ma_codec_base.h:14
static Model * create(Engine *engine, size_t algorithm_id=0)
Definition: ma_model_factory.cpp:8
static ma_err_t remove(Model *model)
Definition: ma_model_factory.cpp:71
Definition: ma_model_base.h:14
bool lock() const
bool unlock() const
Definition: ma_sensor.h:12
Definition: ma_transport.h:12
Definition: invoke.hpp:30
Invoke(const std::vector< std::string > &args, Transport &transport, Encoder &encoder, size_t task_id)
Definition: invoke.hpp:54
std::shared_ptr< Invoke > getptr()
Definition: invoke.hpp:32
~Invoke()
Definition: invoke.hpp:40
void run()
Definition: invoke.hpp:49
static std::shared_ptr< Invoke > create(const std::vector< std::string > &args, Transport &transport, Encoder &encoder, size_t task_id)
Definition: invoke.hpp:36
#define MA_LOGD(TAG,...)
Definition: ma_debug.h:79
#define MA_LOGE(TAG,...)
Definition: ma_debug.h:30
#define MA_ASSERT(expr)
Definition: ma_debug.h:119
#define MA_TAG
Definition: ma_debug.h:9
@ MA_MSG_TYPE_RESP
Definition: ma_types.h:252
@ MA_MSG_TYPE_EVT
Definition: ma_types.h:252
ma_err_t
Definition: ma_types.h:21
@ MA_ENOENT
Definition: ma_types.h:33
@ MA_ENOTSUP
Definition: ma_types.h:31
@ MA_OK
Definition: ma_types.h:23
@ MA_MODEL_CFG_OPT_NMS
Definition: ma_types.h:231
@ MA_MODEL_CFG_OPT_THRESHOLD
Definition: ma_types.h:230
@ MA_PIXEL_FORMAT_JPEG
Definition: ma_types.h:109
@ MA_PIXEL_FORMAT_AUTO
Definition: ma_types.h:104
Definition: algorithm.hpp:11
ma_err_t base64_encode(const unsigned char *in, int in_len, char *out, int *out_len)
Definition: ma_base64.cpp:41
Definition: ma_cv.cpp:7
ma_err_t serializeAlgorithmOutput(Model *algorithm, Encoder *encoder, int width, int height)
Definition: refactor_required.hpp:69
ma_err_t setAlgorithmInput(Model *algorithm, ma_img_t &img)
Definition: refactor_required.hpp:42
std::forward_list< std::shared_ptr< TriggerRule > > trigger_rules
Definition: refactor_required.hpp:296
ma::Mutex trigger_rules_mutex
Definition: refactor_required.hpp:297
#define static_resource
Definition: resource.hpp:64
Definition: ma_types.h:124
Definition: ma_types.h:289