FFmpeg
Loading...
Searching...
No Matches
vf_dnn_detect.c
Go to the documentation of this file.
1/*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19/**
20 * @file
21 * implementing an object detecting filter using deep learning networks.
22 */
23
24#include "libavutil/common.h"
25#include "libavutil/file_open.h"
26#include "libavutil/mem.h"
27#include "libavutil/opt.h"
28#include "filters.h"
29#include "dnn_filter_common.h"
30#include "video.h"
31#include "libavutil/time.h"
32#include "libavutil/avstring.h"
34#include "libavutil/fifo.h"
35#include <float.h>
36
43
61
62static const AVOptionArrayDef anchor_array_def = { .sep = '&' };
63
64#define OFFSET(x) offsetof(DnnDetectContext, dnnctx.x)
65#define OFFSET2(x) offsetof(DnnDetectContext, x)
66#define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM
67static const AVOption dnn_detect_options[] = {
68 { "dnn_backend", "DNN backend", OFFSET(backend_type), AV_OPT_TYPE_INT, { .i64 = DNN_OV }, INT_MIN, INT_MAX, FLAGS, .unit = "backend" },
69#if (CONFIG_LIBTENSORFLOW == 1)
70 { "tensorflow", "tensorflow backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = DNN_TF }, 0, 0, FLAGS, .unit = "backend" },
71#endif
72#if (CONFIG_LIBOPENVINO == 1)
73 { "openvino", "openvino backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = DNN_OV }, 0, 0, FLAGS, .unit = "backend" },
74#endif
75#if (CONFIG_LIBONNXRUNTIME == 1)
76 { "onnx", "ONNX Runtime backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = DNN_ONNX }, 0, 0, FLAGS, .unit = "backend" },
77#endif
78 { "confidence", "threshold of confidence", OFFSET2(confidence), AV_OPT_TYPE_FLOAT, { .dbl = 0.5 }, 0, 1, FLAGS},
79 { "labels", "path to labels file", OFFSET2(labels_filename), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
80 { "model_type", "DNN detection model type", OFFSET2(model_type), AV_OPT_TYPE_INT, { .i64 = DDMT_SSD }, INT_MIN, INT_MAX, FLAGS, .unit = "model_type" },
81 { "ssd", "output shape [1, 1, N, 7]", 0, AV_OPT_TYPE_CONST, { .i64 = DDMT_SSD }, 0, 0, FLAGS, .unit = "model_type" },
82 { "yolo", "output shape [1, N*Cx*Cy*DetectionBox]", 0, AV_OPT_TYPE_CONST, { .i64 = DDMT_YOLOV1V2 }, 0, 0, FLAGS, .unit = "model_type" },
83 { "yolov3", "outputs shape [1, N*D, Cx, Cy]", 0, AV_OPT_TYPE_CONST, { .i64 = DDMT_YOLOV3 }, 0, 0, FLAGS, .unit = "model_type" },
84 { "yolov4", "outputs shape [1, N*D, Cx, Cy]", 0, AV_OPT_TYPE_CONST, { .i64 = DDMT_YOLOV4 }, 0, 0, FLAGS, .unit = "model_type" },
85 { "cell_w", "cell width", OFFSET2(cell_w), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INTMAX_MAX, FLAGS },
86 { "cell_h", "cell height", OFFSET2(cell_h), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INTMAX_MAX, FLAGS },
87 { "nb_classes", "The number of class", OFFSET2(nb_classes), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INTMAX_MAX, FLAGS },
88 { "anchors", "anchors, split by '&'", OFFSET2(anchors), AV_OPT_TYPE_FLOAT | AV_OPT_TYPE_FLAG_ARRAY, { .arr = &anchor_array_def }, FLT_MIN, FLT_MAX, FLAGS },
89 { NULL }
90};
91
93
94static inline float sigmoid(float x) {
95 return 1.f / (1.f + exp(-x));
96}
97
98static inline float linear(float x) {
99 return x;
100}
101
102static int dnn_detect_get_label_id(int nb_classes, int cell_size, float *label_data)
103{
104 float max_prob = 0;
105 int label_id = 0;
106 for (int i = 0; i < nb_classes; i++) {
107 if (label_data[i * cell_size] > max_prob) {
108 max_prob = label_data[i * cell_size];
109 label_id = i;
110 }
111 }
112 return label_id;
113}
114
116{
117 if (!isfinite(f) || f < 0 || (double)f > INT_MAX)
118 return -1;
119 return (int)f;
120}
121
122static int dnn_detect_float_to_coord(double v)
123{
124 if (!isfinite(v))
125 return 0;
126 if (v > INT_MAX)
127 return INT_MAX;
128 if (v < INT_MIN)
129 return INT_MIN;
130 return (int)v;
131}
132
133static int dnn_detect_confidence_num(double conf)
134{
135 double scaled = conf * 10000.0;
136 if (!isfinite(scaled))
137 return 0;
138 if (scaled > INT_MAX)
139 return INT_MAX;
140 if (scaled < 0)
141 return 0;
142 return (int)scaled;
143}
144
145static void dnn_detect_set_bbox_edges(AVDetectionBBox *bbox, double x0, double y0,
146 double x1, double y1, int frame_w, int frame_h)
147{
148 double lo_x = -1.0 * frame_w, hi_x = 2.0 * frame_w;
149 double lo_y = -1.0 * frame_h, hi_y = 2.0 * frame_h;
150
151 x0 = av_clipd(x0, lo_x, hi_x);
152 x1 = av_clipd(x1, lo_x, hi_x);
153 y0 = av_clipd(y0, lo_y, hi_y);
154 y1 = av_clipd(y1, lo_y, hi_y);
155
156 if (x1 < x0)
157 x1 = x0;
158 if (y1 < y0)
159 y1 = y0;
160
161 bbox->x = dnn_detect_float_to_coord(x0);
162 bbox->y = dnn_detect_float_to_coord(y0);
163 bbox->w = dnn_detect_float_to_coord(x1 - x0);
164 bbox->h = dnn_detect_float_to_coord(y1 - y0);
165}
166
167/* Calculate Intersection Over Union */
169{
170 double x1_min = bbox1->x, y1_min = bbox1->y;
171 double x1_max = (double)bbox1->x + bbox1->w, y1_max = (double)bbox1->y + bbox1->h;
172 double x2_min = bbox2->x, y2_min = bbox2->y;
173 double x2_max = (double)bbox2->x + bbox2->w, y2_max = (double)bbox2->y + bbox2->h;
174 double overlapping_width = FFMIN(x1_max, x2_max) - FFMAX(x1_min, x2_min);
175 double overlapping_height = FFMIN(y1_max, y2_max) - FFMAX(y1_min, y2_min);
176 double intersection_area =
177 (overlapping_width < 0 || overlapping_height < 0) ? 0 : overlapping_height * overlapping_width;
178 double area1 = (double)bbox1->w * bbox1->h;
179 double area2 = (double)bbox2->w * bbox2->h;
180 double union_area = area1 + area2 - intersection_area;
181
182 if (area1 <= 0 || area2 <= 0 || union_area <= 0)
183 return 0.f;
184 return (float)(intersection_area / union_area);
185}
186
187static int dnn_detect_parse_yolo_output(AVFrame *frame, DNNData *output, int output_index,
188 AVFilterContext *filter_ctx, int64_t *anchor_used)
189{
191 float conf_threshold = ctx->confidence;
192 int detection_boxes, box_size;
193 int cell_w = 0, cell_h = 0, scale_w = 0, scale_h = 0;
194 int nb_classes = ctx->nb_classes;
195 float *output_data = output[output_index].data;
196 float *anchors;
197 int64_t anchors_needed;
198 AVDetectionBBox *bbox;
199 float (*post_process_raw_data)(float x) = linear;
200 int is_NHWC = 0;
201
202 if (output[output_index].dims[0] != 1) {
204 "YOLO output batch dimension must be 1, got %d\n",
205 output[output_index].dims[0]);
206 return AVERROR_INVALIDDATA;
207 }
208
209 if (ctx->model_type == DDMT_YOLOV1V2) {
210 cell_w = ctx->cell_w;
211 cell_h = ctx->cell_h;
212 scale_w = cell_w;
213 scale_h = cell_h;
214 } else {
215 if (output[output_index].dims[2] != output[output_index].dims[3] &&
216 output[output_index].dims[2] == output[output_index].dims[1]) {
217 is_NHWC = 1;
218 cell_w = output[output_index].dims[2];
219 cell_h = output[output_index].dims[1];
220 } else {
221 cell_w = output[output_index].dims[3];
222 cell_h = output[output_index].dims[2];
223 }
224 scale_w = ctx->scale_width;
225 scale_h = ctx->scale_height;
226 }
227 switch (ctx->model_type) {
228 case DDMT_YOLOV1V2:
229 case DDMT_YOLOV3:
230 post_process_raw_data = linear;
231 break;
232 case DDMT_YOLOV4:
233 post_process_raw_data = sigmoid;
234 break;
235 }
236
237 if (cell_h <= 0 || cell_w <= 0) {
238 av_log(filter_ctx, AV_LOG_ERROR, "cell_w and cell_h are detected\n");
239 return AVERROR(EINVAL);
240 }
241
242 if (nb_classes <= 0) {
243 av_log(filter_ctx, AV_LOG_ERROR, "nb_classes is not set\n");
244 return AVERROR(EINVAL);
245 }
246
247 if (output[output_index].dims[1] <= 0 || output[output_index].dims[2] <= 0 ||
248 output[output_index].dims[3] <= 0) {
249 av_log(filter_ctx, AV_LOG_ERROR, "invalid output tensor dimensions\n");
250 return AVERROR_INVALIDDATA;
251 }
252
253 size_t box_size_sz = (size_t)nb_classes + 5;
254 size_t cell_area, elems_per_box, tmp, total_elems, detection_boxes_sz;
255
256 if (av_size_mult((size_t)cell_w, (size_t)cell_h, &cell_area) < 0 ||
257 av_size_mult(box_size_sz, cell_area, &elems_per_box) < 0 ||
258 elems_per_box == 0 || elems_per_box > INT_MAX) {
259 av_log(filter_ctx, AV_LOG_ERROR, "wrong cell_w, cell_h or nb_classes\n");
260 return AVERROR(EINVAL);
261 }
262
263 if (av_size_mult((size_t)output[output_index].dims[1],
264 (size_t)output[output_index].dims[2], &tmp) < 0 ||
265 av_size_mult(tmp, (size_t)output[output_index].dims[3], &total_elems) < 0 ||
266 total_elems > INT_MAX) {
267 av_log(filter_ctx, AV_LOG_ERROR, "output tensor is too large\n");
268 return AVERROR_INVALIDDATA;
269 }
270
271 if (total_elems % elems_per_box) {
272 av_log(filter_ctx, AV_LOG_ERROR, "wrong cell_w, cell_h or nb_classes\n");
273 return AVERROR(EINVAL);
274 }
275
276 detection_boxes_sz = total_elems / elems_per_box;
277 if (detection_boxes_sz == 0 || detection_boxes_sz > INT_MAX) {
278 av_log(filter_ctx, AV_LOG_ERROR, "wrong cell_w, cell_h or nb_classes\n");
279 return AVERROR(EINVAL);
280 }
281
282 box_size = (int)box_size_sz;
283 detection_boxes = (int)detection_boxes_sz;
284
285 anchors_needed = (int64_t)detection_boxes * 2;
286 if (anchors_needed < 0 || *anchor_used < 0 ||
287 *anchor_used + anchors_needed > ctx->nb_anchor) {
289 "anchors array (%d floats) is too small for %d detection box(es) "
290 "in output %d (needs %"PRId64" floats starting at offset %"PRId64")\n",
291 ctx->nb_anchor, detection_boxes, output_index, anchors_needed, *anchor_used);
292 return AVERROR_INVALIDDATA;
293 }
294 anchors = ctx->anchors + *anchor_used;
295 *anchor_used += anchors_needed;
296
297 for (int box_id = 0; box_id < detection_boxes; box_id++) {
298 for (int cx = 0; cx < cell_w; cx++)
299 for (int cy = 0; cy < cell_h; cy++) {
300 float x, y, w, h, conf;
301 float *detection_boxes_data;
302 int label_id;
303
304 if (is_NHWC) {
305 detection_boxes_data = output_data +
306 ((cy * cell_w + cx) * detection_boxes + box_id) * box_size;
307 conf = post_process_raw_data(detection_boxes_data[4]);
308 } else {
309 detection_boxes_data = output_data + box_id * box_size * cell_w * cell_h;
310 conf = post_process_raw_data(
311 detection_boxes_data[cy * cell_w + cx + 4 * cell_w * cell_h]);
312 }
313
314 if (is_NHWC) {
315 x = post_process_raw_data(detection_boxes_data[0]);
316 y = post_process_raw_data(detection_boxes_data[1]);
317 w = detection_boxes_data[2];
318 h = detection_boxes_data[3];
319 label_id = dnn_detect_get_label_id(ctx->nb_classes, 1, detection_boxes_data + 5);
320 conf = conf * post_process_raw_data(detection_boxes_data[label_id + 5]);
321 } else {
322 x = post_process_raw_data(detection_boxes_data[cy * cell_w + cx]);
323 y = post_process_raw_data(detection_boxes_data[cy * cell_w + cx + cell_w * cell_h]);
324 w = detection_boxes_data[cy * cell_w + cx + 2 * cell_w * cell_h];
325 h = detection_boxes_data[cy * cell_w + cx + 3 * cell_w * cell_h];
326 label_id = dnn_detect_get_label_id(ctx->nb_classes, cell_w * cell_h,
327 detection_boxes_data + cy * cell_w + cx + 5 * cell_w * cell_h);
328 conf = conf * post_process_raw_data(
329 detection_boxes_data[cy * cell_w + cx + (label_id + 5) * cell_w * cell_h]);
330 }
331 if (!isfinite(conf) || conf < conf_threshold) {
332 continue;
333 }
334
335 bbox = av_mallocz(sizeof(*bbox));
336 if (!bbox)
337 return AVERROR(ENOMEM);
338
339 double w_px = exp((double)w) * anchors[box_id * 2] * frame->width / scale_w;
340 double h_px = exp((double)h) * anchors[box_id * 2 + 1] * frame->height / scale_h;
341 double x_px = (cx + (double)x) / cell_w * frame->width - w_px / 2;
342 double y_px = (cy + (double)y) / cell_h * frame->height - h_px / 2;
343
344 dnn_detect_set_bbox_edges(bbox, x_px, y_px, x_px + w_px, y_px + h_px,
345 frame->width, frame->height);
346
348 if (ctx->labels && label_id < ctx->label_count) {
349 av_strlcpy(bbox->detect_label, ctx->labels[label_id], sizeof(bbox->detect_label));
350 } else {
351 snprintf(bbox->detect_label, sizeof(bbox->detect_label), "%d", label_id);
352 }
353
354 if (av_fifo_write(ctx->bboxes_fifo, &bbox, 1) < 0) {
355 av_freep(&bbox);
356 return AVERROR(ENOMEM);
357 }
358 bbox = NULL;
359 }
360 }
361 return 0;
362}
363
365{
367 float conf_threshold = ctx->confidence;
368 AVDetectionBBox *bbox;
369 int nb_bboxes = 0;
371 if (av_fifo_can_read(ctx->bboxes_fifo) == 0) {
372 av_log(filter_ctx, AV_LOG_VERBOSE, "nothing detected in this frame.\n");
373 return 0;
374 }
375
376 /* remove overlap bboxes */
377 for (int i = 0; i < av_fifo_can_read(ctx->bboxes_fifo); i++){
378 av_fifo_peek(ctx->bboxes_fifo, &bbox, 1, i);
379 for (int j = 0; j < av_fifo_can_read(ctx->bboxes_fifo); j++) {
380 AVDetectionBBox *overlap_bbox;
381 av_fifo_peek(ctx->bboxes_fifo, &overlap_bbox, 1, j);
382 if (!strcmp(bbox->detect_label, overlap_bbox->detect_label) &&
383 av_cmp_q(bbox->detect_confidence, overlap_bbox->detect_confidence) < 0 &&
384 dnn_detect_IOU(bbox, overlap_bbox) >= conf_threshold) {
385 bbox->classify_count = -1; // bad result
386 nb_bboxes++;
387 break;
388 }
389 }
390 }
391 nb_bboxes = av_fifo_can_read(ctx->bboxes_fifo) - nb_bboxes;
393 if (!header) {
394 av_log(filter_ctx, AV_LOG_ERROR, "failed to create side data with %d bounding boxes\n", nb_bboxes);
395 return -1;
396 }
397 av_strlcpy(header->source, ctx->dnnctx.model_filename, sizeof(header->source));
398
399 while(av_fifo_can_read(ctx->bboxes_fifo)) {
400 AVDetectionBBox *candidate_bbox;
401 av_fifo_read(ctx->bboxes_fifo, &candidate_bbox, 1);
402
403 if (nb_bboxes > 0 && candidate_bbox->classify_count != -1) {
404 bbox = av_get_detection_bbox(header, header->nb_bboxes - nb_bboxes);
405 memcpy(bbox, candidate_bbox, sizeof(*bbox));
406 nb_bboxes--;
407 }
408 av_freep(&candidate_bbox);
409 }
410 return 0;
411}
412
414{
415 int ret = 0;
416 int64_t anchor_used = 0;
417 ret = dnn_detect_parse_yolo_output(frame, output, 0, filter_ctx, &anchor_used);
418 if (ret < 0)
419 return ret;
421 if (ret < 0)
422 return ret;
423 return 0;
424}
425
427 AVFilterContext *filter_ctx, int nb_outputs)
428{
429 int ret = 0;
430 int64_t anchor_used = 0;
431 for (int i = 0; i < nb_outputs; i++) {
432 ret = dnn_detect_parse_yolo_output(frame, output, i, filter_ctx, &anchor_used);
433 if (ret < 0)
434 return ret;
435 }
437 if (ret < 0)
438 return ret;
439 return 0;
440}
441
442static int dnn_detect_post_proc_ssd(AVFrame *frame, DNNData *output, int nb_outputs,
444{
446 float conf_threshold = ctx->confidence;
447 int proposal_count = 0;
448 int detect_size = 0;
449 int detect_output_idx = 0;
450 int label_output_idx = -1;
451 float *detections = NULL, *labels = NULL;
452 int nb_bboxes = 0;
454 AVDetectionBBox *bbox;
455 int scale_w = ctx->scale_width;
456 int scale_h = ctx->scale_height;
457 size_t detect_elems, needed_elems;
458
459 if (nb_outputs == 1 && output->dims[3] == 7) {
460 detect_output_idx = 0;
461 proposal_count = output->dims[2];
462 detect_size = output->dims[3];
463 detections = output->data;
464 } else if (nb_outputs == 2 && output[0].dims[3] == 5) {
465 detect_output_idx = 0;
466 proposal_count = output[0].dims[2];
467 detect_size = output[0].dims[3];
468 detections = output[0].data;
469 labels = output[1].data;
470 label_output_idx = 1;
471 } else if (nb_outputs == 2 && output[1].dims[3] == 5) {
472 detect_output_idx = 1;
473 proposal_count = output[1].dims[2];
474 detect_size = output[1].dims[3];
475 detections = output[1].data;
476 labels = output[0].data;
477 label_output_idx = 0;
478 } else {
479 av_log(filter_ctx, AV_LOG_ERROR, "Model output shape doesn't match ssd requirement.\n");
480 return AVERROR(EINVAL);
481 }
482
483 if (proposal_count < 0) {
484 av_log(filter_ctx, AV_LOG_ERROR, "Invalid negative proposal count %d.\n", proposal_count);
485 return AVERROR_INVALIDDATA;
486 }
487
488 if (proposal_count == 0)
489 return 0;
490
491 if (av_size_mult((size_t)proposal_count, (size_t)detect_size,
492 &needed_elems) < 0) {
493 av_log(filter_ctx, AV_LOG_ERROR, "detection tensor element count overflows\n");
494 return AVERROR_INVALIDDATA;
495 }
496 if (needed_elems > INT_MAX) {
498 "detection tensor has %zu elements, more than the supported maximum\n",
499 needed_elems);
500 return AVERROR_INVALIDDATA;
501 }
502
503 if (av_size_mult((size_t)output[detect_output_idx].dims[0],
504 (size_t)output[detect_output_idx].dims[1], &detect_elems) < 0 ||
505 av_size_mult(detect_elems, (size_t)output[detect_output_idx].dims[2], &detect_elems) < 0 ||
506 av_size_mult(detect_elems, (size_t)output[detect_output_idx].dims[3], &detect_elems) < 0) {
507 av_log(filter_ctx, AV_LOG_ERROR, "detection tensor element count overflows\n");
508 return AVERROR_INVALIDDATA;
509 }
510 if (detect_elems != needed_elems) {
512 "detection tensor has %zu elements, expected %zu\n",
513 detect_elems, needed_elems);
514 return AVERROR_INVALIDDATA;
515 }
516
517 if (label_output_idx >= 0) {
518 size_t label_count;
519 if (av_size_mult((size_t)output[label_output_idx].dims[0],
520 (size_t)output[label_output_idx].dims[1], &label_count) < 0 ||
521 av_size_mult(label_count, (size_t)output[label_output_idx].dims[2], &label_count) < 0 ||
522 av_size_mult(label_count, (size_t)output[label_output_idx].dims[3], &label_count) < 0) {
523 av_log(filter_ctx, AV_LOG_ERROR, "labels tensor element count overflows\n");
524 return AVERROR_INVALIDDATA;
525 }
526 if (label_count < (size_t)proposal_count) {
528 "labels tensor has %zu element(s), too small for %d proposal(s)\n",
529 label_count, proposal_count);
530 return AVERROR_INVALIDDATA;
531 }
532 }
533
534 for (int i = 0; i < proposal_count; ++i) {
535 float conf;
536 if (nb_outputs == 1)
537 conf = detections[i * detect_size + 2];
538 else
539 conf = detections[i * detect_size + 4];
540 if (!isfinite(conf) || conf < conf_threshold) {
541 continue;
542 }
543 nb_bboxes++;
544 }
545
546 if (nb_bboxes == 0) {
547 av_log(filter_ctx, AV_LOG_VERBOSE, "nothing detected in this frame.\n");
548 return 0;
549 }
550
552 if (!header) {
553 av_log(filter_ctx, AV_LOG_ERROR, "failed to create side data with %d bounding boxes\n", nb_bboxes);
554 return -1;
555 }
556
557 av_strlcpy(header->source, ctx->dnnctx.model_filename, sizeof(header->source));
558
559 for (int i = 0; i < proposal_count; ++i) {
560 int label_id;
561 float conf, x0, y0, x1, y1;
562 double x0_px, y0_px, x1_px, y1_px;
563
564 if (nb_outputs == 1) {
565 label_id = dnn_detect_label_id_from_float(detections[i * detect_size + 1]);
566 conf = detections[i * detect_size + 2];
567 x0 = detections[i * detect_size + 3];
568 y0 = detections[i * detect_size + 4];
569 x1 = detections[i * detect_size + 5];
570 y1 = detections[i * detect_size + 6];
571 } else {
572 label_id = dnn_detect_label_id_from_float(labels[i]);
573 x0 = detections[i * detect_size] / scale_w;
574 y0 = detections[i * detect_size + 1] / scale_h;
575 x1 = detections[i * detect_size + 2] / scale_w;
576 y1 = detections[i * detect_size + 3] / scale_h;
577 conf = detections[i * detect_size + 4];
578 }
579
580 if (!isfinite(conf) || conf < conf_threshold) {
581 continue;
582 }
583
584 bbox = av_get_detection_bbox(header, header->nb_bboxes - nb_bboxes);
585
586 x0_px = (double)x0 * frame->width;
587 y0_px = (double)y0 * frame->height;
588 x1_px = (double)x1 * frame->width;
589 y1_px = (double)y1 * frame->height;
590
591 dnn_detect_set_bbox_edges(bbox, x0_px, y0_px, x1_px, y1_px,
592 frame->width, frame->height);
593
595 bbox->classify_count = 0;
596
597 if (ctx->labels && label_id >= 0 && label_id < ctx->label_count) {
598 av_strlcpy(bbox->detect_label, ctx->labels[label_id], sizeof(bbox->detect_label));
599 } else {
600 snprintf(bbox->detect_label, sizeof(bbox->detect_label), "%d", label_id);
601 }
602
603 nb_bboxes--;
604 if (nb_bboxes == 0) {
605 break;
606 }
607 }
608 return 0;
609}
610
611static int dnn_detect_post_proc_anchored(AVFrame *frame, DNNData *output, int nb_outputs,
613{
614 AVFrameSideData *sd;
616 int ret = 0;
617
619 if (sd) {
620 av_log(filter_ctx, AV_LOG_ERROR, "already have bounding boxes in side data.\n");
621 return -1;
622 }
623
624 switch (ctx->model_type) {
625 case DDMT_SSD:
626 ret = dnn_detect_post_proc_ssd(frame, output, nb_outputs, filter_ctx);
627 if (ret < 0)
628 return ret;
629 break;
630 case DDMT_YOLOV1V2:
632 if (ret < 0)
633 return ret;
634 break;
635 case DDMT_YOLOV3:
636 case DDMT_YOLOV4:
637 ret = dnn_detect_post_proc_yolov3(frame, output, filter_ctx, nb_outputs);
638 if (ret < 0)
639 return ret;
640 break;
641 }
642 return 0;
643}
644
646{
648 int proposal_count;
649 float conf_threshold = ctx->confidence;
650 float *conf, *position, *label_id, x0, y0, x1, y1;
651 int nb_bboxes = 0;
652 AVFrameSideData *sd;
653 AVDetectionBBox *bbox;
655
656 proposal_count = *(float *)(output[0].data);
657 conf = output[1].data;
658 position = output[3].data;
659 label_id = output[2].data;
660
662 if (sd) {
663 av_log(filter_ctx, AV_LOG_ERROR, "already have dnn bounding boxes in side data.\n");
664 return -1;
665 }
666
667 for (int i = 0; i < proposal_count; ++i) {
668 if (conf[i] < conf_threshold)
669 continue;
670 nb_bboxes++;
671 }
672
673 if (nb_bboxes == 0) {
674 av_log(filter_ctx, AV_LOG_VERBOSE, "nothing detected in this frame.\n");
675 return 0;
676 }
677
679 if (!header) {
680 av_log(filter_ctx, AV_LOG_ERROR, "failed to create side data with %d bounding boxes\n", nb_bboxes);
681 return -1;
682 }
683
684 av_strlcpy(header->source, ctx->dnnctx.model_filename, sizeof(header->source));
685
686 for (int i = 0; i < proposal_count; ++i) {
687 int label_id_i;
688 y0 = position[i * 4];
689 x0 = position[i * 4 + 1];
690 y1 = position[i * 4 + 2];
691 x1 = position[i * 4 + 3];
692
694
695 if (conf[i] < conf_threshold) {
696 continue;
697 }
698
699 bbox->x = (int)(x0 * frame->width);
700 bbox->w = (int)(x1 * frame->width) - bbox->x;
701 bbox->y = (int)(y0 * frame->height);
702 bbox->h = (int)(y1 * frame->height) - bbox->y;
703
704 bbox->detect_confidence = av_make_q((int)(conf[i] * 10000), 10000);
705 bbox->classify_count = 0;
706
707 label_id_i = dnn_detect_label_id_from_float(label_id[i]);
708 if (ctx->labels && label_id_i >= 0 && label_id_i < ctx->label_count) {
709 av_strlcpy(bbox->detect_label, ctx->labels[label_id_i], sizeof(bbox->detect_label));
710 } else {
711 snprintf(bbox->detect_label, sizeof(bbox->detect_label), "%d", label_id_i);
712 }
713
714 nb_bboxes--;
715 if (nb_bboxes == 0) {
716 break;
717 }
718 }
719 return 0;
720}
721
723{
725 DnnContext *dnn_ctx = &ctx->dnnctx;
726 switch (dnn_ctx->backend_type) {
727 case DNN_OV:
729 case DNN_TF:
731 case DNN_ONNX:
733 default:
734 avpriv_report_missing_feature(filter_ctx, "Current dnn backend does not support detect filter\n");
735 return AVERROR(EINVAL);
736 }
737}
738
740{
741 for (int i = 0; i < ctx->label_count; i++) {
742 av_freep(&ctx->labels[i]);
743 }
744 ctx->label_count = 0;
745 av_freep(&ctx->labels);
746}
747
749{
750 int line_len;
751 FILE *file;
752 DnnDetectContext *ctx = context->priv;
753
754 file = avpriv_fopen_utf8(ctx->labels_filename, "r");
755 if (!file){
756 av_log(context, AV_LOG_ERROR, "failed to open file %s\n", ctx->labels_filename);
757 return AVERROR(EINVAL);
758 }
759
760 while (!feof(file)) {
761 char *label;
762 char buf[256];
763 if (!fgets(buf, 256, file)) {
764 break;
765 }
766
767 line_len = strlen(buf);
768 while (line_len) {
769 int i = line_len - 1;
770 if (buf[i] == '\n' || buf[i] == '\r' || buf[i] == ' ') {
771 buf[i] = '\0';
772 line_len--;
773 } else {
774 break;
775 }
776 }
777
778 if (line_len == 0) // empty line
779 continue;
780
782 av_log(context, AV_LOG_ERROR, "label %s too long\n", buf);
783 fclose(file);
784 return AVERROR(EINVAL);
785 }
786
787 label = av_strdup(buf);
788 if (!label) {
789 av_log(context, AV_LOG_ERROR, "failed to allocate memory for label %s\n", buf);
790 fclose(file);
791 return AVERROR(ENOMEM);
792 }
793
794 if (av_dynarray_add_nofree(&ctx->labels, &ctx->label_count, label) < 0) {
795 av_log(context, AV_LOG_ERROR, "failed to do av_dynarray_add\n");
796 fclose(file);
797 av_freep(&label);
798 return AVERROR(ENOMEM);
799 }
800 }
801
802 fclose(file);
803 return 0;
804}
805
806static int check_output_nb(DnnDetectContext *ctx, DNNBackendType backend_type, int output_nb)
807{
808 switch(backend_type) {
809 case DNN_TF:
810 if (output_nb != 4) {
811 av_log(ctx, AV_LOG_ERROR, "Only support tensorflow detect model with 4 outputs, \
812 but get %d instead\n", output_nb);
813 return AVERROR(EINVAL);
814 }
815 return 0;
816 case DNN_OV:
817 return 0;
818 case DNN_ONNX:
819 return 0;
820 default:
821 avpriv_report_missing_feature(ctx, "Dnn detect filter does not support current backend\n");
822 return AVERROR(EINVAL);
823 }
824 return 0;
825}
826
828{
829 DnnDetectContext *ctx = context->priv;
830 DnnContext *dnn_ctx = &ctx->dnnctx;
831 int ret;
832 int using_yolo = (ctx->model_type == DDMT_YOLOV3 ||
833 ctx->model_type == DDMT_YOLOV4 ||
834 ctx->model_type == DDMT_YOLOV1V2);
835
836 if (using_yolo && !ctx->anchors) {
837 av_log(ctx, AV_LOG_ERROR, "anchors is not set while being required for YOLO models\n");
838 return AVERROR(EINVAL);
839 }
840
841 ret = ff_dnn_init(&ctx->dnnctx, DFT_ANALYTICS_DETECT, context);
842 if (ret < 0)
843 return ret;
844 ret = check_output_nb(ctx, dnn_ctx->backend_type, dnn_ctx->nb_outputs);
845 if (ret < 0)
846 return ret;
847 ctx->bboxes_fifo = av_fifo_alloc2(1, sizeof(AVDetectionBBox *), AV_FIFO_FLAG_AUTO_GROW);
848 if (!ctx->bboxes_fifo)
849 return AVERROR(ENOMEM);
851
852 if (ctx->labels_filename) {
853 ret = read_detect_label_file(context);
854 if (ret) {
855 return ret;
856 }
857 }
858
859 return 0;
860}
861
870
872{
873 DnnDetectContext *ctx = outlink->src->priv;
874 int ret;
875 DNNAsyncStatusType async_state;
876
877 ret = ff_dnn_flush(&ctx->dnnctx);
878 if (ret != 0) {
879 return -1;
880 }
881
882 do {
883 AVFrame *in_frame = NULL;
884 AVFrame *out_frame = NULL;
885 async_state = ff_dnn_get_result(&ctx->dnnctx, &in_frame, &out_frame);
886 if (async_state == DAST_SUCCESS) {
887 ret = ff_filter_frame(outlink, in_frame);
888 if (ret < 0)
889 return ret;
890 if (out_pts)
891 *out_pts = in_frame->pts + pts;
892 }
893 av_usleep(5000);
894 } while (async_state >= DAST_NOT_READY);
895
896 return 0;
897}
898
900{
901 AVFilterLink *inlink = filter_ctx->inputs[0];
902 AVFilterLink *outlink = filter_ctx->outputs[0];
904 AVFrame *in = NULL;
905 int64_t pts;
906 int ret, status;
907 int got_frame = 0;
908 int async_state;
909
910 FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink);
911
912 do {
913 // drain all input frames
914 ret = ff_inlink_consume_frame(inlink, &in);
915 if (ret < 0)
916 return ret;
917 if (ret > 0) {
918 if (ff_dnn_execute_model(&ctx->dnnctx, in, NULL) != 0) {
919 return AVERROR(EIO);
920 }
921 }
922 } while (ret > 0);
923
924 // drain all processed frames
925 do {
926 AVFrame *in_frame = NULL;
927 AVFrame *out_frame = NULL;
928 async_state = ff_dnn_get_result(&ctx->dnnctx, &in_frame, &out_frame);
929 if (async_state == DAST_SUCCESS) {
930 ret = ff_filter_frame(outlink, in_frame);
931 if (ret < 0)
932 return ret;
933 got_frame = 1;
934 }
935 } while (async_state == DAST_SUCCESS);
936
937 // if frame got, schedule to next filter
938 if (got_frame)
939 return 0;
940
941 if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {
942 if (status == AVERROR_EOF) {
943 int64_t out_pts = pts;
944 ret = dnn_detect_flush_frame(outlink, pts, &out_pts);
945 ff_outlink_set_status(outlink, status, out_pts);
946 return ret;
947 }
948 }
949
950 FF_FILTER_FORWARD_WANTED(outlink, inlink);
951
952 return 0;
953}
954
956{
957 DnnDetectContext *ctx = context->priv;
958 AVDetectionBBox *bbox;
959 ff_dnn_uninit(&ctx->dnnctx);
960 if (ctx->bboxes_fifo) {
961 while (av_fifo_can_read(ctx->bboxes_fifo)) {
962 av_fifo_read(ctx->bboxes_fifo, &bbox, 1);
963 av_freep(&bbox);
964 }
965 av_fifo_freep2(&ctx->bboxes_fifo);
966 }
967 av_freep(&ctx->anchors);
969}
970
971static int config_input(AVFilterLink *inlink)
972{
973 AVFilterContext *context = inlink->dst;
974 DnnDetectContext *ctx = context->priv;
975 DNNData model_input = { 0 };
976 int ret, width_idx, height_idx;
977
978 ret = ff_dnn_get_input(&ctx->dnnctx, &model_input);
979 if (ret != 0) {
980 av_log(ctx, AV_LOG_ERROR, "could not get input from the model\n");
981 return ret;
982 }
983 width_idx = dnn_get_width_idx_by_layout(model_input.layout);
984 height_idx = dnn_get_height_idx_by_layout(model_input.layout);
985 ctx->scale_width = model_input.dims[width_idx] == -1 ? inlink->w :
986 model_input.dims[width_idx];
987 ctx->scale_height = model_input.dims[height_idx] == -1 ? inlink->h :
988 model_input.dims[height_idx];
989
990 return 0;
991}
992
994 {
995 .name = "default",
996 .type = AVMEDIA_TYPE_VIDEO,
997 .config_props = config_input,
998 },
999};
1000
1002 .p.name = "dnn_detect",
1003 .p.description = NULL_IF_CONFIG_SMALL("Apply DNN detect filter to the input."),
1004 .p.priv_class = &dnn_detect_class,
1005 .priv_size = sizeof(DnnDetectContext),
1012 .activate = dnn_detect_activate,
1013};
static int config_input(AVFilterLink *inlink)
const FFFilter ff_vf_dnn_detect
static AVFormatContext * ctx
int ff_inlink_acknowledge_status(AVFilterLink *link, int *rstatus, int64_t *rpts)
Test and acknowledge the change of status on the link.
Definition avfilter.c:1467
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition avfilter.c:1068
int ff_inlink_consume_frame(AVFilterLink *link, AVFrame **rframe)
Take a frame from the link's FIFO and update the link's stats.
Definition avfilter.c:1520
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define f(width, name)
Definition cbs_vp8.c:236
#define FLAGS
Definition cmdutils.c:598
common internal and external API header
#define av_clipd
Definition common.h:148
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
static AVFrame * frame
AVDetectionBBoxHeader * av_detection_bbox_create_side_data(AVFrame *frame, uint32_t nb_bboxes)
Allocates memory for AVDetectionBBoxHeader, plus an array of nb_bboxes AVDetectionBBox,...
#define AV_DETECTION_BBOX_LABEL_NAME_MAX_SIZE
static av_always_inline AVDetectionBBox * av_get_detection_bbox(const AVDetectionBBoxHeader *header, unsigned int idx)
int ff_dnn_set_detect_post_proc(DnnContext *ctx, DetectPostProc post_proc)
void ff_dnn_uninit(DnnContext *ctx)
DNNAsyncStatusType ff_dnn_get_result(DnnContext *ctx, AVFrame **in_frame, AVFrame **out_frame)
int ff_dnn_execute_model(DnnContext *ctx, AVFrame *in_frame, AVFrame *out_frame)
int ff_dnn_init(DnnContext *ctx, DNNFunctionType func_type, AVFilterContext *filter_ctx)
int ff_dnn_get_input(DnnContext *ctx, DNNData *input)
int ff_dnn_flush(DnnContext *ctx)
int ff_dnn_filter_init_child_class(AVFilterContext *filter)
common functions for the dnn based filters
#define AVFILTER_DNN_DEFINE_CLASS(fname, backend_mask)
static int dnn_get_height_idx_by_layout(DNNLayout layout)
DNNAsyncStatusType
@ DAST_NOT_READY
@ DAST_SUCCESS
DNNBackendType
@ DNN_OV
@ DNN_ONNX
@ DNN_TF
@ DFT_ANALYTICS_DETECT
static int dnn_get_width_idx_by_layout(DNNLayout layout)
int(* init)(AVBSFContext *ctx)
Definition dts2pts.c:608
int8_t exp
Definition eval.c:76
A generic FIFO API.
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition opt.h:298
@ AV_OPT_TYPE_FLAG_ARRAY
May be combined with another regular option type to declare an array option.
Definition opt.h:345
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_OPT_TYPE_FLOAT
Underlying C type is float.
Definition opt.h:270
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
Definition opt.h:275
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition error.h:61
#define AVERROR_EOF
End of file.
Definition error.h:57
#define AVERROR(e)
Definition error.h:45
AVFifo * av_fifo_alloc2(size_t nb_elems, size_t elem_size, unsigned int flags)
Allocate and initialize an AVFifo with a given element size.
Definition fifo.c:47
void av_fifo_freep2(AVFifo **f)
Free an AVFifo and reset pointer to NULL.
Definition fifo.c:286
#define AV_FIFO_FLAG_AUTO_GROW
Automatically resize the FIFO on writes, so that the data fits.
Definition fifo.h:63
size_t av_fifo_can_read(const AVFifo *f)
Definition fifo.c:87
int av_fifo_peek(const AVFifo *f, void *buf, size_t nb_elems, size_t offset)
Read data from a FIFO without modifying FIFO state.
Definition fifo.c:255
int av_fifo_write(AVFifo *f, const void *buf, size_t nb_elems)
Write data into a FIFO.
Definition fifo.c:188
int av_fifo_read(AVFifo *f, void *buf, size_t nb_elems)
Read data from a FIFO.
Definition fifo.c:240
AVFrameSideData * av_frame_get_side_data(const AVFrame *frame, enum AVFrameSideDataType type)
Definition frame.c:659
@ AV_FRAME_DATA_DETECTION_BBOXES
Bounding boxes for object detection and classification, as described by AVDetectionBBoxHeader.
Definition frame.h:194
#define AV_LOG_VERBOSE
Detailed information.
Definition log.h:226
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
static AVRational av_make_q(int num, int den)
Create an AVRational.
Definition rational.h:71
static int av_cmp_q(AVRational a, AVRational b)
Compare two rationals.
Definition rational.h:89
int av_dynarray_add_nofree(void *tab_ptr, int *nb_ptr, void *elem)
Add an element to a dynamic array.
Definition mem.c:313
int av_size_mult(size_t a, size_t b, size_t *r)
Multiply two size_t values checking for overflow.
Definition mem.c:565
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
size_t av_strlcpy(char *dst, const char *src, size_t size)
Copy the string src to dst, but no more than size - 1 bytes, and null-terminate dst.
Definition avstring.c:85
static int linear(InterplayACMContext *s, unsigned ind, unsigned col)
#define area2
Definition intrax8dsp.c:45
#define area1
Definition intrax8dsp.c:44
static av_cold void uninit(AVBitStreamFilterContext *ctx)
static int output_data(MLPDecodeContext *m, unsigned int substr, AVFrame *frame, int *got_frame_ptr)
Write the audio data into the output buffer.
Definition mlpdec.c:1107
#define FILTER_INPUTS(array)
Definition filters.h:264
#define FILTER_OUTPUTS(array)
Definition filters.h:265
#define FF_FILTER_FORWARD_WANTED(outlink, inlink)
Forward the frame_wanted_out flag from an output link to an input link.
Definition filters.h:694
#define FILTER_PIXFMTS_ARRAY(array)
Definition filters.h:244
static void ff_outlink_set_status(AVFilterLink *link, int status, int64_t pts)
Set the status field of a link from the source filter.
Definition filters.h:629
#define FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink)
Forward the status on an output link to an input link.
Definition filters.h:639
#define av_cold
Definition attributes.h:117
FILE * avpriv_fopen_utf8(const char *path, const char *mode)
Open a file using a UTF-8 filename.
Definition file_open.c:160
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
void avpriv_report_missing_feature(void *avc, const char *msg,...) av_printf_format(2
Log a generic warning message about a missing feature.
static enum AVPixelFormat pix_fmts[]
Definition libkvazaar.c:296
#define isfinite(x)
Definition libm.h:361
uint8_t w
Definition llvidencdsp.c:39
#define FFMIN(a, b)
Definition macros.h:49
#define FFMAX(a, b)
Definition macros.h:47
Memory handling functions.
const char data[16]
Definition mxf.c:149
#define av_strdup(s)
Definition ops_static.c:55
AVOptions.
#define AV_PIX_FMT_GRAYF32
Definition pixfmt.h:588
AVPixelFormat
Pixel format.
Definition pixfmt.h:71
@ AV_PIX_FMT_NV12
planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (firs...
Definition pixfmt.h:96
@ AV_PIX_FMT_NONE
Definition pixfmt.h:72
@ AV_PIX_FMT_RGB24
packed RGB 8:8:8, 24bpp, RGBRGB...
Definition pixfmt.h:75
@ AV_PIX_FMT_YUV420P
planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
Definition pixfmt.h:73
@ AV_PIX_FMT_YUV422P
planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
Definition pixfmt.h:77
@ AV_PIX_FMT_GRAY8
Y , 8bpp.
Definition pixfmt.h:81
@ AV_PIX_FMT_YUV410P
planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples)
Definition pixfmt.h:79
@ AV_PIX_FMT_YUV411P
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
Definition pixfmt.h:80
@ AV_PIX_FMT_YUV444P
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
Definition pixfmt.h:78
@ AV_PIX_FMT_BGR24
packed RGB 8:8:8, 24bpp, BGRBGR...
Definition pixfmt.h:76
static const uint8_t header[24]
Definition sdr2.c:68
#define snprintf
Definition snprintf.h:34
static av_cold int preinit(AVBitStreamFilterContext *ctx)
Definition source.c:137
Describe the class of an AVClass context structure.
Definition log.h:76
char detect_label[AV_DETECTION_BBOX_LABEL_NAME_MAX_SIZE]
Detect result with confidence.
int x
Distance in pixels from the left/top edge of the frame, together with width and height,...
AVRational detect_confidence
uint32_t classify_count
Definition fifo.c:35
An instance of a filter.
Definition avfilter.h:273
void * priv
private data for use by the filter
Definition avfilter.h:288
A filter pad used for either input or output.
Definition filters.h:40
Structure to hold side data for an AVFrame.
Definition frame.h:327
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
int64_t pts
Presentation timestamp in time_base units (time when frame should be shown to user).
Definition frame.h:574
May be set as default_val for AV_OPT_TYPE_FLAG_ARRAY options.
Definition opt.h:394
AVOption.
Definition opt.h:428
int dims[4]
void * data
DNNLayout layout
DNNBackendType backend_type
uint32_t nb_outputs
DNNDetectionModelType model_type
DnnContext dnnctx
#define av_mallocz(s)
#define av_freep(p)
#define av_log(a,...)
static uint8_t tmp[40]
Definition aes_ctr.c:52
int av_usleep(unsigned usec)
Sleep for a period of time.
Definition time.c:93
static FilteringContext * filter_ctx
Definition transcode.c:52
static int64_t pts
static const AVFilterPad dnn_detect_inputs[]
static int read_detect_label_file(AVFilterContext *context)
static float sigmoid(float x)
static const AVOption dnn_detect_options[]
DNNDetectionModelType
@ DDMT_YOLOV3
@ DDMT_YOLOV1V2
@ DDMT_YOLOV4
@ DDMT_SSD
static float dnn_detect_IOU(AVDetectionBBox *bbox1, AVDetectionBBox *bbox2)
static int dnn_detect_flush_frame(AVFilterLink *outlink, int64_t pts, int64_t *out_pts)
static int config_input(AVFilterLink *inlink)
static int dnn_detect_parse_yolo_output(AVFrame *frame, DNNData *output, int output_index, AVFilterContext *filter_ctx, int64_t *anchor_used)
static int check_output_nb(DnnDetectContext *ctx, DNNBackendType backend_type, int output_nb)
static const AVOptionArrayDef anchor_array_def
static int dnn_detect_confidence_num(double conf)
static int dnn_detect_float_to_coord(double v)
static float linear(float x)
static av_cold int dnn_detect_init(AVFilterContext *context)
static int dnn_detect_post_proc_anchored(AVFrame *frame, DNNData *output, int nb_outputs, AVFilterContext *filter_ctx)
static void dnn_detect_set_bbox_edges(AVDetectionBBox *bbox, double x0, double y0, double x1, double y1, int frame_w, int frame_h)
static void free_detect_labels(DnnDetectContext *ctx)
static int dnn_detect_fill_side_data(AVFrame *frame, AVFilterContext *filter_ctx)
static av_cold void dnn_detect_uninit(AVFilterContext *context)
static int dnn_detect_activate(AVFilterContext *filter_ctx)
static int dnn_detect_get_label_id(int nb_classes, int cell_size, float *label_data)
static int dnn_detect_post_proc_ssd(AVFrame *frame, DNNData *output, int nb_outputs, AVFilterContext *filter_ctx)
static int dnn_detect_post_proc_yolo(AVFrame *frame, DNNData *output, AVFilterContext *filter_ctx)
static int dnn_detect_post_proc_tf(AVFrame *frame, DNNData *output, AVFilterContext *filter_ctx)
#define OFFSET(x)
static int dnn_detect_label_id_from_float(float f)
static int dnn_detect_post_proc(AVFrame *frame, DNNData *output, uint32_t nb, AVFilterContext *filter_ctx)
#define OFFSET2(x)
static int dnn_detect_post_proc_yolov3(AVFrame *frame, DNNData *output, AVFilterContext *filter_ctx, int nb_outputs)
const AVFilterPad ff_video_default_filterpad[1]
An AVFilterPad array whose only entry has name "default" and is of type AVMEDIA_TYPE_VIDEO.
Definition video.c:37