62 if (outs.empty())
LFATAL(
"No outputs received, we need at least one.");
63 float const confThreshold = cthresh::get() * 0.01F;
64 float const nmsThreshold = nms::get() * 0.01F;
65 float const jointThreshold = jthresh::get() * 0.01F;
66 bool const sigmo = sigmoid::get();
67 bool const clampbox = boxclamp::get();
68 int const fudge = classoffset::get();
70 size_t const boxmax = maxnbox::get();
73 itsDetections.clear();
81 cv::Size
const bsiz = preproc->
blobsize(0);
85 std::vector<int> classIds;
86 std::vector<float> confidences;
87 std::vector<cv::Rect> boxes;
90 std::vector<PoseSkeleton> skeletons;
95 switch(posetype::get())
99 case jevois::dnn::postprocessor::PoseType::YOLOv8HAILO:
102 if (outs.size() != 9)
LTHROW(
"Expected 9 quantized output blobs from split YOLOv8-pose Hailo network");
103 if (outs[0].type() == CV_32F)
LTHROW(
"Expected quantized outputs, turn 'dequant' off");
108 std::shared_ptr<jevois::Component> comp = engine()->
getComponent(dd[0]); dd.erase(dd.begin());
109 for (std::string
const & c : dd) { comp = comp->getSubComponent(c);
if (!comp)
LFATAL(
"Internal error"); }
111 std::vector<hailo_vstream_info_t> outinfos = net->
outputInfos();
114 itsROI = std::make_shared<HailoROI>(HailoROI(HailoBBox(0.0f, 0.0f, 1.0f, 1.0f)));
115 for (
size_t i = 0; i < outs.size(); ++i)
116 itsROI->add_tensor(std::make_shared<HailoTensor>(outs[i].data, outinfos[i]));
119 int constexpr regression_length = 15;
120 std::vector<int>
const strides = { 8, 16, 32 };
121 std::vector<int>
const network_dims = { bsiz.width, bsiz.height };
123 std::vector<HailoTensorPtr> tensors = itsROI->get_tensors();
125 regression_length, 1 , confThreshold,
129 itsDetections.clear();
130 std::vector<HailoDetection> detections;
131 for (
auto & dec : filtered_decodings)
134 HailoDetection & detbox = dec.detection_box;
135 detections.push_back(detbox);
138 if (detbox.get_confidence() == 0.0)
continue;
140 HailoBBox box = detbox.get_bbox();
141 float xmin = box.xmin() * bsiz.width, ymin = box.ymin() * bsiz.height;
142 float xmax = box.xmax() * bsiz.width, ymax = box.ymax() * bsiz.height;
143 preproc->
b2i(xmin, ymin);
144 preproc->
b2i(xmax, ymax);
147 std::vector<jevois::ObjReco> ov; ov.emplace_back(
o);
148 jevois::ObjDetect od { int(xmin), int(ymin), int(xmax), int(ymax), ov, std::vector<cv::Point>() };
149 itsDetections.emplace_back(od);
152 hailo_common::add_detections(itsROI, detections);
154 itsKeypointsAndPairs =
filter_keypoints(filtered_decodings, network_dims, jointThreshold);
157 for (
auto & keypoint : itsKeypointsAndPairs.first)
159 float xs = keypoint.xs * bsiz.width, ys = keypoint.ys * bsiz.height;
160 preproc->
b2i(xs, ys);
161 keypoint.xs = xs; keypoint.ys = ys;
164 for (PairPairs & p : itsKeypointsAndPairs.second)
166 float x1 = p.pt1.first * bsiz.width, y1 = p.pt1.second * bsiz.height;
167 float x2 = p.pt2.first * bsiz.width, y2 = p.pt2.second * bsiz.height;
168 preproc->
b2i(x1, y1);
169 preproc->
b2i(x2, y2);
170 p.pt1.first = x1; p.pt1.second = y1; p.pt2.first = x2; p.pt2.second = y2;
177 case jevois::dnn::postprocessor::PoseType::YOLOv8t:
179 if ((outs.size() % 3) != 0)
180 LTHROW(
"Expected several (usually 3, for 3 strides) sets of 3 blobs: 1xHxWx64 (raw boxes), "
181 "1xHxWxC (class scores), and 1xHxWx3K for K skeleton keypoints [x,y,score]");
184 int constexpr reg_max = 16;
186 for (
size_t idx = 0; idx < outs.size(); idx += 3)
188 cv::Mat
const & bx = outs[idx]; cv::MatSize
const & bx_siz = bx.size;
189 if (bx_siz.dims() != 4 || bx_siz[3] != 4 * reg_max)
LTHROW(
"Output " << idx <<
" is not 4D 1xHxWx64");
190 float const * bx_data = (
float const *)bx.data;
192 cv::Mat
const & cls = outs[idx + 1]; cv::MatSize
const & cls_siz = cls.size;
193 if (cls_siz.dims() != 4)
LTHROW(
"Output " << idx <<
" is not 4D 1xHxWxC");
194 float const * cls_data = (
float const *)cls.data;
195 size_t const nclass = cls_siz[3];
197 cv::Mat
const & kpt = outs[idx + 2]; cv::MatSize
const & kpt_siz = kpt.size;
198 if (kpt_siz.dims() != 4 || (kpt_siz[3] % 3) != 0)
LTHROW(
"Output " << idx <<
" is not 4D 1xHxWx3K");
199 float const * kpt_data = (
float const *)kpt.data;
200 size_t const nkpt = kpt_siz[3];
202 unsigned int const nn = itsSkeletonDef->nodeNames.size();
204 LFATAL(
"Received keypoint data size " << nkpt <<
" but skeleton has " << nn <<
205 " joints: data size must be 3x number of joints, for [x,y,conf]");
207 for (
int i = 1; i < 3; ++i)
208 if (cls_siz[i] != bx_siz[i] || cls_siz[i] != kpt_siz[i])
209 LTHROW(
"Mismatched HxW sizes for outputs " << idx <<
" .. " << idx + 1);
212 bool has_joint[nn];
float jconf[nn];
float jx[nn];
float jy[nn];
215 for (
int y = 0; y < cls_siz[1]; ++y)
216 for (
int x = 0; x < cls_siz[2]; ++x)
219 size_t best_idx = 0;
float confidence = cls_data[0];
220 for (
size_t i = 1; i < nclass; ++i)
221 if (cls_data[i] > confidence) { confidence = cls_data[i]; best_idx = i; }
226 if (confidence >= confThreshold)
232 float xmin = (x + 0.5f -
softmax_dfl(bx_data, dst, reg_max)) * stride;
233 float ymin = (y + 0.5f -
softmax_dfl(bx_data + reg_max, dst, reg_max)) * stride;
234 float xmax = (x + 0.5f +
softmax_dfl(bx_data + 2 * reg_max, dst, reg_max)) * stride;
235 float ymax = (y + 0.5f +
softmax_dfl(bx_data + 3 * reg_max, dst, reg_max)) * stride;
238 boxes.emplace_back(cv::Rect(xmin, ymin, xmax - xmin, ymax - ymin));
239 classIds.emplace_back(
int(best_idx) + fudge);
240 confidences.emplace_back(confidence);
246 for (
unsigned int i = 0; i < nn; ++i)
252 if (kconf >= jointThreshold)
254 float kpx = (x + kpt_data[3*i] * 2.0F) * stride;
255 float kpy = (y + kpt_data[3*i + 1] * 2.0F) * stride;
256 preproc->
b2i(kpx, kpy);
258 has_joint[i] =
true; jconf[i] = kconf; jx[i] = kpx; jy[i] = kpy;
260 else has_joint[i] =
false;
264 if (skel.
nodes.empty() ==
false)
266 std::vector<std::pair<unsigned int, unsigned int>>
const & linkdefs = skel.
linkDefinitions();
269 for (std::pair<unsigned int, unsigned int>
const & lnk : linkdefs)
271 if (has_joint[lnk.first] && has_joint[lnk.second])
273 jx[lnk.second], jy[lnk.second],
274 jconf[lnk.first]*jconf[lnk.second]*10000.0F });
282 bx_data += 4 * reg_max;
293 case jevois::dnn::postprocessor::PoseType::YOLOv8:
295 if ((outs.size() % 3) != 0)
296 LTHROW(
"Expected several (usually 3, for 3 strides) sets of 3 blobs: 1x64xHxW (raw boxes), "
297 "1xCxHxW (class scores), and 1x3KxHxW for K skeleton keypoints [x,y,score]");
300 int constexpr reg_max = 16;
302 for (
size_t idx = 0; idx < outs.size(); idx += 3)
304 cv::Mat
const & bx = outs[idx]; cv::MatSize
const & bx_siz = bx.size;
305 if (bx_siz.dims() != 4 || bx_siz[1] != 4 * reg_max)
LTHROW(
"Output " << idx <<
" is not 4D 1x64xHxW");
306 float const * bx_data = (
float const *)bx.data;
308 cv::Mat
const & cls = outs[idx + 1]; cv::MatSize
const & cls_siz = cls.size;
309 if (cls_siz.dims() != 4)
LTHROW(
"Output " << idx <<
" is not 4D 1xCxHxW");
310 float const * cls_data = (
float const *)cls.data;
311 size_t const nclass = cls_siz[1];
313 cv::Mat
const & kpt = outs[idx + 2]; cv::MatSize
const & kpt_siz = kpt.size;
314 if (kpt_siz.dims() != 4 || (kpt_siz[1] % 3) != 0)
LTHROW(
"Output " << idx <<
" is not 4D 1x3KxHxW");
315 float const * kpt_data = (
float const *)kpt.data;
316 size_t const nkpt = kpt_siz[1];
318 unsigned int const nn = itsSkeletonDef->nodeNames.size();
320 LFATAL(
"Received keypoint data size " << nkpt <<
" but skeleton has " << nn <<
321 " joints: data size must be 3x number of joints, for [x,y,conf]");
323 for (
int i = 2; i < 4; ++i)
324 if (cls_siz[i] != bx_siz[i] || cls_siz[i] != kpt_siz[i])
325 LTHROW(
"Mismatched HxW sizes for outputs " << idx <<
" .. " << idx + 1);
327 size_t const step = cls_siz[2] * cls_siz[3];
330 bool has_joint[nn];
float jconf[nn];
float jx[nn];
float jy[nn];
333 for (
int y = 0; y < cls_siz[2]; ++y)
334 for (
int x = 0; x < cls_siz[3]; ++x)
337 size_t best_idx = 0;
float confidence = cls_data[0];
338 for (
size_t i = 1; i < nclass; ++i)
339 if (cls_data[i * step] > confidence) { confidence = cls_data[i * step]; best_idx = i; }
344 if (confidence >= confThreshold)
350 float xmin = (x + 0.5f -
softmax_dfl(bx_data, dst, reg_max, step)) * stride;
351 float ymin = (y + 0.5f -
softmax_dfl(bx_data + reg_max * step, dst, reg_max, step)) * stride;
352 float xmax = (x + 0.5f +
softmax_dfl(bx_data + 2 * reg_max * step, dst, reg_max, step)) * stride;
353 float ymax = (y + 0.5f +
softmax_dfl(bx_data + 3 * reg_max * step, dst, reg_max, step)) * stride;
356 boxes.emplace_back(cv::Rect(xmin, ymin, xmax - xmin, ymax - ymin));
357 classIds.emplace_back(
int(best_idx) + fudge);
358 confidences.emplace_back(confidence);
364 for (
unsigned int i = 0; i < nn; ++i)
366 unsigned int const offset = 3 * i * step;
372 if (kconf >= jointThreshold)
374 float kpx = (x + kpt_data[offset] * 2.0F) * stride;
375 float kpy = (y + kpt_data[offset + step] * 2.0F) * stride;
376 preproc->
b2i(kpx, kpy);
378 has_joint[i] =
true; jconf[i] = kconf; jx[i] = kpx; jy[i] = kpy;
380 else has_joint[i] =
false;
384 if (skel.
nodes.empty() ==
false)
386 std::vector<std::pair<unsigned int, unsigned int>>
const & linkdefs = skel.
linkDefinitions();
389 for (std::pair<unsigned int, unsigned int>
const & lnk : linkdefs)
391 if (has_joint[lnk.first] && has_joint[lnk.second])
393 jx[lnk.second], jy[lnk.second],
394 jconf[lnk.first]*jconf[lnk.second]*10000.0F });
401 ++cls_data; ++bx_data; ++kpt_data;
412 LTHROW(
"Unsupported Post-processor posetype " <<
int(posetype::get()));
417 catch (std::exception
const & e)
419 std::string err =
"Selected posetype is " + posetype::strget() +
" and network produced:\n\n";
421 err +=
"\nFATAL ERROR(s):\n\n";
428 if (itsDetections.empty() ==
false)
return;
433 std::vector<int> indices;
434 if (nmsperclass::get())
435 cv::dnn::NMSBoxesBatched(boxes, confidences, classIds, confThreshold, nmsThreshold, indices, 1.0F, boxmax);
437 cv::dnn::NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices, 1.0F, boxmax);
440 for (cv::Rect & b : boxes)
444 cv::Point2f tl = b.tl(); preproc->
b2i(tl.x, tl.y);
445 cv::Point2f br = b.br(); preproc->
b2i(br.x, br.y);
446 b.x = tl.x; b.y = tl.y; b.width = br.x - tl.x; b.height = br.y - tl.y;
450 bool namonly = namedonly::get();
451 for (
size_t i = 0; i < indices.size(); ++i)
453 int idx = indices[i];
454 cv::Rect
const & box = boxes[idx];
456 if (namonly ==
false || label.empty() ==
false)
459 std::vector<jevois::ObjReco> ov;
461 itsDetections.emplace_back(
jevois::ObjDetect{ box.x, box.y, box.x+box.width, box.y+box.height,
462 std::move(ov), std::vector<cv::Point>() });
465 itsSkeletons.emplace_back(std::move(skeletons[idx]));