VisionServer  v2.1.1-1-g21dc5465
FRC vision library
tfmodel.h
Go to the documentation of this file.
1#pragma once
2
3#ifndef EXCLUDE_TFLITE
4
5#include <vector>
6#include <string>
7#include <thread>
8#include <fstream>
9#include <initializer_list>
10
11#include <tensorflow/lite/interpreter.h>
12#include <tensorflow/lite/interpreter_builder.h>
13#include <tensorflow/lite/model_builder.h>
14#include <tensorflow/lite/kernels/register.h>
15#include <edgetpu.h>
16#include <opencv2/opencv.hpp>
17
18#include "cpp-tools/src/resources.h"
19#include "visionserver2.h"
20
21
22void loadObjectLabels(const std::string& file, std::vector<std::string>& objs);
23inline size_t tpusAvailable() { return edgetpu::EdgeTpuManager::GetSingleton()->EnumerateEdgeTpu().size(); }
24
26class TfModel {
27public:
28 inline static size_t
29 default_threading{std::thread::hardware_concurrency() / 2};
30 enum class Optimization {
31 DEFAULT,
33 };
34
35 TfModel() = delete;
36 TfModel(std::initializer_list<std::pair<const char*, Optimization> >, size_t = default_threading);
37 inline TfModel(const char* def, const char* edge, size_t th = default_threading) :
39 inline TfModel(const char* m, size_t t = default_threading) :
40 TfModel(m, nullptr, t) {}
41 TfModel(const TfModel&) = delete;
42
43 inline operator bool() const { return this->isValid(); }
44 inline bool isValid() const { return this->map && this->model; }
45 inline bool isValidBuffer() const { return !this->input_tensor.empty(); }
46
47
48protected:
49 std::unique_ptr<tflite::FlatBufferModel> map;
50 std::unique_ptr<tflite::Interpreter> model;
51 std::shared_ptr<edgetpu::EdgeTpuContext> edgetpu_context;
52 tflite::ops::builtin::BuiltinOpResolver resolver;
53
54 cv::Mat input_tensor;
55 cv::Size input_size;
56
57
58};
59
60
61
63 enum class OutputTensor {
64 BOX_COORDS = 0,
65 LABEL_IDX = 1,
66 CONFIDENCE = 2,
67 DETECTIONS = 3
68 };
69
70 inline static const char
71 *default_model = "unoptimized.tflite",
72 *edgetpu_model = "model.tflite",
73 *default_labels = "map.pbtxt"
74 ;
75};
76
78template<class derived_t = void>
79class AxonRunner_ : public vs2::VPipeline<AxonRunner_<derived_t> >, public TfModel, public AxonRunner_B {
80 typedef struct AxonRunner_<derived_t> This_t;
81public:
82 inline AxonRunner_(size_t th = default_threading) :
84 inline AxonRunner_(const char* model, Optimization opt = Optimization::DEFAULT, const char* map = default_labels, size_t th = default_threading) :
85 AxonRunner_({{model, opt}}, map, th) {}
86 AxonRunner_(std::initializer_list<std::pair<const char*, Optimization> >, const char* map = default_labels, size_t = default_threading);
87
88 inline bool isValidOutput() const { return this->coords && this->labels && this->confidence && this->detections; }
89
90 inline size_t getDetections() const { return this->detections ? *this->detections : -1; }
91 inline bool isValidIdx(size_t idx) const { return idx >= 0 && idx < this->getDetections(); }
92
93 virtual void process(cv::Mat& io_frame) override;
94
95protected:
96 inline float getX1(size_t idx) const { return this->coords[idx * 4 + 1]; }
97 inline float getY1(size_t idx) const { return this->coords[idx * 4]; }
98 inline float getX2(size_t idx) const { return this->coords[idx * 4 + 3]; }
99 inline float getY2(size_t idx) const { return this->coords[idx * 4 + 2]; }
100 inline const std::string& getLabel(size_t idx) const { return this->obj_labels.at(this->labels[idx]); }
101 inline uint32_t getConfidence(size_t idx) const { return (this->confidence[idx] * 100.f); }
102
103 std::vector<std::string> obj_labels;
104 const float
109 ;
110
111
112};
114
115
116
117struct MoveNet_B {
118 enum class Output {
119 NOSE,
120 L_EYE, R_EYE,
121 L_EAR, R_EAR,
125 L_HIP, R_HIP,
126 L_KNEE, R_KNEE,
128 TOTAL
129 };
130
131 inline static const float
133 inline static const char*
134 default_model = "lite-model_movenet_singlepose_lightning_tflite_int8_4.tflite";
135 inline static const std::array<const char*, (size_t)Output::TOTAL>
137 "nose",
138 "left eye", "right eye",
139 "left ear", "right ear",
140 "left shoulder", "right shoulder",
141 "left elbow", "right elbow",
142 "left wrist", "right wrist",
143 "left hip", "right hip",
144 "left knee", "right knee",
145 "left ankle", "right ankle"
146 };
147 inline static const std::array<std::pair<Output, Output>, 12>
149 {
162 }
163 };
164};
165constexpr inline size_t operator~(MoveNet_B::Output e) { return static_cast<size_t>(e); }
166
168template<class derived_t = void>
169class MoveNet_ : public vs2::VPipeline<MoveNet_<derived_t> >, public TfModel, public MoveNet_B {
170 typedef struct MoveNet_<derived_t> This_t;
171public:
172 inline MoveNet_(size_t th = default_threading) :
174 inline MoveNet_(const char* model, Optimization opt = Optimization::DEFAULT, size_t th = default_threading) :
175 MoveNet_({{model, opt}}, th) {}
176 MoveNet_(std::initializer_list<std::pair<const char*, Optimization> >, size_t th = default_threading);
177
178 inline bool isValidOutput() const { return this->outputs; }
179
180 virtual void process(cv::Mat& io_frame) override;
181
182protected:
183 inline static bool isValidIdx(size_t idx) { return idx >= 0 && idx < ~Output::TOTAL; }
184 inline float getX(size_t idx) const { return this->outputs[idx * 3 + 1]; }
185 inline float getY(size_t idx) const { return this->outputs[idx * 3]; }
186 inline float getConfidence(size_t idx) const { return this->outputs[idx * 3 + 2]; }
187
188 const float* outputs;
189 std::array<cv::Point2f, ~Output::TOTAL> points;
190
191
192};
194
195
196
197
198
199
200
201
202
203
206template<class derived_t>
207AxonRunner_<derived_t>::AxonRunner_(std::initializer_list<std::pair<const char*, Optimization> > options, const char* map, size_t th) :
208 vs2::VPipeline<This_t>("Axon Runner "), TfModel(options, th)
209{
211 if(this->isValidBuffer() && this->model->outputs().size() == 4) {
212 this->coords = reinterpret_cast<float*>(this->model->output_tensor(static_cast<size_t>(OutputTensor::BOX_COORDS))->data.data);
213 this->labels = reinterpret_cast<float*>(this->model->output_tensor(static_cast<size_t>(OutputTensor::LABEL_IDX))->data.data);
214 this->confidence = reinterpret_cast<float*>(this->model->output_tensor(static_cast<size_t>(OutputTensor::CONFIDENCE))->data.data);
215 this->detections = reinterpret_cast<float*>(this->model->output_tensor(static_cast<size_t>(OutputTensor::DETECTIONS))->data.data);
216 }
217}
218
219template<class derived_t>
220void AxonRunner_<derived_t>::process(cv::Mat& io_frame) {
221 if(this->isValidBuffer()) {
222 cv::resize(io_frame, this->input_tensor, this->input_size);
223 this->model->Invoke();
224
225 size_t detected = this->getDetections();
226 cv::Rect2f bb;
227 for(size_t i = 0; i < detected; i++) {
228 bb = std::move(cv::Rect2f(
229 cv::Point2f(
230 getX1(i) * io_frame.size().width,
231 getY1(i) * io_frame.size().height
232 ),
233 cv::Point2f(
234 getX2(i) * io_frame.size().width,
235 getY2(i) * io_frame.size().height
236 )
237 ));
238 cv::rectangle(io_frame, bb, {0, 100, 255}, 4);
239 cv::putText(
240 io_frame, this->getLabel(i) + ": " + std::to_string(this->getConfidence(i)) + "%",
241 cv::Point(bb.tl().x, bb.tl().y - 10), cv::FONT_HERSHEY_DUPLEX, 0.55, {0, 100, 255}, 1, cv::LINE_AA
242 );
243 }
244 }
245}
246
247template<class derived_t>
248MoveNet_<derived_t>::MoveNet_(std::initializer_list<std::pair<const char*, Optimization> > ops, size_t th) :
249 vs2::VPipeline<This_t>("MoveNet Runner "), TfModel(ops, th)
250{
251 if(this->model->outputs().size() == 1) {
252 this->outputs = reinterpret_cast<float*>(this->model->output_tensor(0)->data.data);
253 }
254}
255
256template<class derived_t>
257void MoveNet_<derived_t>::process(cv::Mat& io_frame) {
258 if(this->isValidBuffer() && this->isValidOutput()) {
259 cv::resize(io_frame, this->input_tensor, this->input_size);
260 this->model->Invoke();
261
262 for(size_t i = 0; i < ~Output::TOTAL; i++) {
263 this->points[i] = std::move(cv::Point2f(
264 this->getX(i) * io_frame.size().width,
265 this->getY(i) * io_frame.size().height
266 ));
267 if(this->getConfidence(i) > confid_thresh) {
268 cv::circle(io_frame, this->points[i], 1, {25, 255, 0}, 2, cv::LINE_AA);
269 }
270 }
271 for(size_t i = 0; i < connections.size(); i++) {
272 if(this->getConfidence(~connections[i].first) > confid_thresh && this->getConfidence(~connections[i].second) > confid_thresh) {
273 cv::line(io_frame, this->points[~connections[i].first], this->points[~connections[i].second], {25, 255, 0});
274 }
275 }
276 }
277}
278
279#else
280#define __TFMODEL_UNSUPPORTED
281#endif
std::vector< std::string > obj_labels
Definition: tfmodel.h:103
float getX2(size_t idx) const
Definition: tfmodel.h:98
bool isValidIdx(size_t idx) const
Definition: tfmodel.h:91
const std::string & getLabel(size_t idx) const
Definition: tfmodel.h:100
bool isValidOutput() const
Definition: tfmodel.h:88
virtual void process(cv::Mat &io_frame) override
Definition: tfmodel.h:220
const float * detections
Definition: tfmodel.h:109
AxonRunner_(size_t th=default_threading)
Definition: tfmodel.h:82
AxonRunner_(const char *model, Optimization opt=Optimization::DEFAULT, const char *map=default_labels, size_t th=default_threading)
Definition: tfmodel.h:84
const float * labels
Definition: tfmodel.h:106
size_t getDetections() const
Definition: tfmodel.h:90
const float * confidence
Definition: tfmodel.h:107
float getY2(size_t idx) const
Definition: tfmodel.h:99
float getX1(size_t idx) const
Definition: tfmodel.h:96
float getY1(size_t idx) const
Definition: tfmodel.h:97
uint32_t getConfidence(size_t idx) const
Definition: tfmodel.h:101
const float * coords
Definition: tfmodel.h:105
const float * outputs
Definition: tfmodel.h:188
std::array< cv::Point2f, ~Output::TOTAL > points
Definition: tfmodel.h:189
bool isValidOutput() const
Definition: tfmodel.h:178
virtual void process(cv::Mat &io_frame) override
Definition: tfmodel.h:257
float getX(size_t idx) const
Definition: tfmodel.h:184
MoveNet_(const char *model, Optimization opt=Optimization::DEFAULT, size_t th=default_threading)
Definition: tfmodel.h:174
float getConfidence(size_t idx) const
Definition: tfmodel.h:186
static bool isValidIdx(size_t idx)
Definition: tfmodel.h:183
MoveNet_(size_t th=default_threading)
Definition: tfmodel.h:172
float getY(size_t idx) const
Definition: tfmodel.h:185
TfModel(const char *def, const char *edge, size_t th=default_threading)
Definition: tfmodel.h:37
TfModel()=delete
cv::Size input_size
Definition: tfmodel.h:55
bool isValidBuffer() const
Definition: tfmodel.h:45
std::unique_ptr< tflite::FlatBufferModel > map
Definition: tfmodel.h:49
bool isValid() const
Definition: tfmodel.h:44
Optimization
Definition: tfmodel.h:30
TfModel(const char *m, size_t t=default_threading)
Definition: tfmodel.h:39
std::unique_ptr< tflite::Interpreter > model
Definition: tfmodel.h:50
cv::Mat input_tensor
Definition: tfmodel.h:54
static size_t default_threading
Definition: tfmodel.h:29
TfModel(const TfModel &)=delete
tflite::ops::builtin::BuiltinOpResolver resolver
Definition: tfmodel.h:52
std::shared_ptr< edgetpu::EdgeTpuContext > edgetpu_context
Definition: tfmodel.h:51
Definition: extensions.h:9
static const char * default_labels
Definition: tfmodel.h:73
static const char * default_model
Definition: tfmodel.h:71
static const char * edgetpu_model
Definition: tfmodel.h:72
static const char * default_model
Definition: tfmodel.h:134
static const std::array< std::pair< Output, Output >, 12 > connections
Definition: tfmodel.h:148
static const std::array< const char *,(size_t) Output::TOTAL > names
Definition: tfmodel.h:136
static const float confid_thresh
Definition: tfmodel.h:132
constexpr size_t operator~(MoveNet_B::Output e)
Definition: tfmodel.h:165
MoveNet_ MoveNet
Definition: tfmodel.h:193
AxonRunner_ AxonRunner
Definition: tfmodel.h:113
void loadObjectLabels(const std::string &file, std::vector< std::string > &objs)
Definition: tfmodel.cpp:6
size_t tpusAvailable()
Definition: tfmodel.h:23