20#include <darknet-nnpack/src/classifier.h>
21#include <darknet-nnpack/src/option_list.h>
22#include <darknet-nnpack/src/data.h>
23#include <darknet-nnpack/src/network.h>
24#include <darknet-nnpack/src/utils.h>
28Yolo::Yolo(std::string
const & instance) :
jevois::Component(instance), net(nullptr), names(nullptr), nboxes(0),
29 dets(nullptr), classes(0), map(nullptr), itsReady(false)
49 std::string root = dataroot::get();
if (root.empty() ==
false) root +=
'/';
52 std::string
const datacf =
absolutePath(root + datacfg::get());
53 std::string
const cfgfil =
absolutePath(root + cfgfile::get());
54 std::string
const weightfil =
absolutePath(root + weightfile::get());
56 list * options = read_data_cfg(
const_cast<char *
>(datacf.c_str()));
57 std::string name_list = namefile::get();
58 if (name_list.empty()) name_list =
absolutePath(root + option_find_str(options,
"names",
"data/names.list"));
61 LINFO(
"Using data config from " << datacf);
62 LINFO(
"Using cfg from " << cfgfil);
63 LINFO(
"Using weights from " << weightfil);
64 LINFO(
"Using names from " << name_list);
66 LINFO(
"Getting labels...");
67 names = get_labels(
const_cast<char *
>(name_list.c_str()));
69 char * mapf = option_find_str(options,
"map", 0);
70 if (mapf)
map = read_map(mapf);
72 LINFO(
"Parsing network and loading weights...");
74 net = load_network(
const_cast<char *
>(cfgfil.c_str()),
const_cast<char *
>(weightfil.c_str()), 0);
80 LFATAL(
"Failed to load YOLO network and/or weights -- ABORT");
83 classes = option_find_int(options,
"classes", 2);
85 set_batch_network(
net, 1);
87 LINFO(
"YOLO network ready");
90 net->threadpool = pthreadpool_create(threads::get());
109 if (
net->threadpool) pthreadpool_destroy(
net->threadpool);
123 if (
itsReady.load() ==
false)
throw std::logic_error(
"not ready yet...");
124 if (cvimg.type() != CV_8UC3)
LFATAL(
"cvimg must have type CV_8UC3 and RGB pixels");
127 int const w = cvimg.cols;
128 int const h = cvimg.rows;
130 image im = make_image(w,
h, c);
131 for (
int k = 0; k < c; ++k)
132 for (
int j = 0; j <
h; ++j)
133 for (
int i = 0; i < w; ++i)
135 int dst_index = i + w*j + w*
h*k;
136 int src_index = k + c*i + c*w*j;
137 im.data[dst_index] = float(cvimg.data[src_index]) / 255.0F;
150 image sized;
bool need_free =
false;
151 if (im.w ==
net->w && im.h ==
net->h) sized = im;
152 else { sized = letterbox_image(im,
net->w,
net->h); need_free =
true; }
154 struct timeval start, stop;
156 gettimeofday(&start, 0);
157 network_predict(*
net, sized.data);
158 gettimeofday(&stop, 0);
160 float predtime = (stop.tv_sec * 1000 + stop.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000);
162 if (need_free) free_image(sized);
170 layer & l =
net->layers[
net->n-1];
174 dets = get_network_boxes(
net, 1, 1, thresh::get() * 0.01F, hierthresh::get() * 0.01F,
map, 0, &
nboxes, 1);
176 float const nmsval = nms::get() * 0.01F;
178 if (nmsval) do_nms_sort(
dets,
nboxes, l.classes, nmsval);
184 float const thval = thresh::get();
186 for (
int i = 0; i <
nboxes; ++i)
189 std::vector<jevois::ObjReco> data;
191 for (
int j = 0; j <
classes; ++j)
193 float const p =
dets[i].prob[j] * 100.0F;
194 if (p > thval) data.push_back( { p, std::string(
names[j]) } );
198 if (data.empty())
continue;
201 std::sort(data.begin(), data.end(), [](
auto a,
auto b) { return (b.score < a.score); });
204 std::string labelstr;
205 for (
auto const & d : data)
207 if (labelstr.empty() ==
false) labelstr +=
", ";
211 box
const & b =
dets[i].bbox;
213 int const left = std::max(xoff,
int(xoff + (b.x - b.w / 2.0F) * inw + 0.499F));
214 int const bw = std::min(inw,
int(b.w * inw + 0.499F));
215 int const top = std::max(yoff,
int(yoff + (b.y - b.h / 2.0F) * inh + 0.499F));
216 int const bh = std::min(inh,
int(b.h * inh + 0.499F));
227 float const thval = thresh::get();
229 for (
int i = 0; i <
nboxes; ++i)
232 std::vector<jevois::ObjReco> data;
234 for (
int j = 0; j <
classes; ++j)
236 float const p =
dets[i].prob[j] * 100.0F;
237 if (p > thval) data.push_back({ p, std::string(
names[j]) });
241 if (data.empty())
continue;
244 std::sort(data.begin(), data.end(), [](
auto a,
auto b) { return (b.score < a.score); });
246 box
const & b =
dets[i].bbox;
248 int const left = (b.x - b.w / 2.0F) * inw;
249 int const bw = b.w * inw;
250 int const top = (b.y - b.h / 2.0F) * inh;
251 int const bh = b.h * inh;
260 if (
itsReady.load() ==
false)
throw std::logic_error(
"not ready yet...");
261 resize_network(
net, w,
h);
267 if (
itsReady.load() ==
false)
throw std::logic_error(
"not ready yet...");
void drawDetections(jevois::RawImage &outimg, int inw, int inh, int xoff, int yoff)
Draw the detections.
void sendSerial(jevois::StdModule *mod, int inw, int inh)
Send serial messages about detections.
void computeBoxes(int inw, int inh)
Compute the boxes.
void resizeInDims(int w, int h)
Resize the network's input image dims.
std::future< void > itsReadyFut
float predict(cv::Mat const &cvimg)
Processing function, results are stored internally in the underlying Darknet network object.
std::atomic< bool > itsReady
void postUninit() override
Un-initialize and free resources.
void getInDims(int &w, int &h, int &c) const
Get input width, height, channels.
void postInit() override
Initialize, configure and load the network in a thread.
virtual ~Yolo()
Virtual destructor for safe inheritance.
Yolo(std::string const &instance)
Constructor.
std::filesystem::path absolutePath(std::filesystem::path const &path="")
void sendSerialObjDetImg2D(unsigned int camw, unsigned int camh, float x, float y, float w, float h, std::vector< ObjReco > const &res)
void writeText(RawImage &img, std::string const &txt, int x, int y, unsigned int col, Font font=Font6x10)
void drawRect(RawImage &img, int x, int y, unsigned int w, unsigned int h, unsigned int thick, unsigned int col)
std::future< std::invoke_result_t< std::decay_t< Function >, std::decay_t< Args >... > > async(Function &&f, Args &&... args)
std::string sformat(char const *fmt,...) __attribute__((format(__printf__
unsigned short constexpr LightGreen