PR #23697 opened by Steven Xiao (younengxiao) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23697 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23697.patch
This patch extends the existing ONNX Runtime DNN backend with object detection support via dnn_detect and removes the single-output restriction in dnn_processing. Example usage: - SSD detection (single output, DirectML) ffmpeg -i input.mp4 -vf "scale=300:300,format=rgb24,dnn_detect=dnn_backend=onnx:model=ssd.onnx:model_type=ssd:device=dml:device_id=0" -f null - - YOLOv3 detection (two outputs, CPU) ffmpeg -i input.mp4 -vf "scale=416:416,format=rgb24,dnn_detect=dnn_backend=onnx:model=yolov3.onnx:output=yolo_13&yolo_26:model_type=yolov3:nb_classes=80:anchors=116&90&156&198" -f null - Signed-off-by: younengxiao <[email protected]> From df9ad604eb58e366a13fe10e9530667c7df43444 Mon Sep 17 00:00:00 2001 From: younengxiao <[email protected]> Date: Fri, 3 Jul 2026 22:50:48 -0400 Subject: [PATCH] avfilter/dnn: extend ONNX Runtime backend with dnn_detect and multi-output support This patch extends the existing ONNX Runtime DNN backend with object detection support via dnn_detect and removes the single-output restriction in dnn_processing. Example usage: # SSD detection (single output, DirectML) ffmpeg -i input.mp4 -vf "scale=300:300,format=rgb24,dnn_detect=dnn_backend=onnx:model=ssd.onnx:model_type=ssd:device=dml:device_id=0" -f null - # YOLOv3 detection (two outputs, CPU) ffmpeg -i input.mp4 -vf "scale=416:416,format=rgb24,dnn_detect=dnn_backend=onnx:model=yolov3.onnx:output=yolo_13&yolo_26:model_type=yolov3:nb_classes=80:anchors=116&90&156&198&373&326&30&61&62&45&59&119" -f null - Signed-off-by: younengxiao <[email protected]> --- doc/filters.texi | 55 +++++- libavfilter/dnn/dnn_backend_onnx.c | 307 +++++++++++++++++++---------- libavfilter/dnn_filter_common.c | 8 +- libavfilter/vf_dnn_detect.c | 14 +- 4 files changed, 270 insertions(+), 114 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index aa0059f9cc..204e31f862 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -12160,7 +12160,15 @@ The filter accepts the following options: @table @option @item dnn_backend Specify which DNN backend to use for model loading and execution. This option accepts -only openvino now, tensorflow backends will be added. +the following values: +@table @samp +@item tensorflow +TensorFlow backend. +@item openvino +OpenVINO backend. +@item onnx +ONNX Runtime backend. +@end table @item model Set path to model file specifying network architecture and its parameters. @@ -12170,7 +12178,9 @@ Note that different backends use different file formats. Set the input name of the dnn network. @item output -Set the output name of the dnn network. +Set the output name of the dnn network. For the ONNX Runtime backend multiple +output names may be specified separated by @samp{&} (e.g +@option{output=yolo_13&yolo_26}) @item confidence Set the confidence threshold (default: 0.5). @@ -12182,12 +12192,48 @@ The first line is the name of label id 0 (usually it is 'background'), and the second line is the name of label id 1, etc. The label id is considered as name if the label file is not provided. +@item model_type +Set the detection model output format. The following values are accepted: + +@table @samp +@item ssd +Single-stage detector (default). +@item yolo +YOLO v1/v2. +@item yolov3 +YOLOv3. +@item yolov4 +YOLOv4. +@end table +@item anchors +Anchor box dimensions, separated by @samp{&}. Required for YOLO-family models. +@item nb_classes +Number of detection classes. Required for YOLO-family models. @item backend_configs Set the configs to be passed into backend. To use async execution, set async (default: set). Roll back to sync execution if the backend does not support async. @end table +@subsection Examples + +@itemize +@item +Run SSD object detection with an ONNX model (single output, DirectML on Windows): +@example +ffmpeg -i input.mp4 -vf "scale=300:300,format=rgb24,dnn_detect=dnn_backend=onnx + :model=ssd.onnx:model_type=ssd:confidence=0.5:device=dml" -f null - +@end example + +@item +Run YOLOv3 object detection with an ONNX model (two outputs, CPU): +@example +ffmpeg -i input.mp4 -vf "scale=416:416,format=rgb24,dnn_detect=dnn_backend=onnx + :model=yolov3.onnx:output=yolo_13&yolo_26:model_type=yolov3:nb_classes=80 + :anchors=116&90&156&198&373&326&30&61&62&45&59&119" -f null - +@end example +@end itemize + @anchor{dnn_processing} @section dnn_processing @@ -12239,7 +12285,10 @@ exactly one input tensor when running the model. The @option{input} and @option{output} options are optional for the ONNX Runtime backend; when they are omitted the backend resolves the -tensor names from the session. +tensor names from the session. Multiple output names may be supplied +separated by @samp{&} (e.g. @option{output=out_a&out_b}); however for +@code{dnn_processing} only the first output tensor is used for frame +post-processing. The ONNX Runtime backend runs inference synchronously using a single inference request. The shared @option{async} and @option{nireq} options diff --git a/libavfilter/dnn/dnn_backend_onnx.c b/libavfilter/dnn/dnn_backend_onnx.c index 0ff0ffb285..559310ba24 100644 --- a/libavfilter/dnn/dnn_backend_onnx.c +++ b/libavfilter/dnn/dnn_backend_onnx.c @@ -29,6 +29,8 @@ #include "libavutil/avstring.h" #include "libavutil/thread.h" #include "libavutil/wchar_filename.h" +#include "libavutil/pixdesc.h" +#include "libswscale/swscale.h" #include "../filters.h" #include "dnn_io_proc.h" #include "dnn_backend_common.h" @@ -55,9 +57,10 @@ typedef struct ONNXModel { } ONNXModel; typedef struct ONNXInferRequest { - OrtValue *input_tensor; - OrtValue *output_tensor; - void *input_data; + OrtValue *input_tensor; + OrtValue **output_tensors; + uint32_t nb_outputs; + void *input_data; } ONNXInferRequest; typedef struct ONNXRequestItem { @@ -125,10 +128,16 @@ static void onnx_free_request(ONNXInferRequest *request) request->input_tensor = NULL; } av_freep(&request->input_data); - if (request->output_tensor) { - g_ort->ReleaseValue(request->output_tensor); - request->output_tensor = NULL; + if (request->output_tensors) { + for (uint32_t i = 0; i < request->nb_outputs; i++) { + if (request->output_tensors[i]) { + g_ort->ReleaseValue(request->output_tensors[i]); + request->output_tensors[i] = NULL; + } + } + av_freep(&request->output_tensors); } + request->nb_outputs = 0; } static inline void destroy_request_item(ONNXRequestItem **arg) @@ -319,6 +328,76 @@ static int get_input_onnx(DNNModel *model, DNNData *input, const char *input_nam return 0; } +static int onnx_fill_detect_input(const AVFrame *frame, DNNData *input, + void *log_ctx) +{ + struct SwsContext *sws_ctx; + uint8_t *uint8_buf = NULL; + float *dst = (float *)input->data; + enum AVPixelFormat fmt; + int w, h, ret = 0; + int linesizes[4] = { 0, 0, 0, 0 }; + + switch (input->order) { + case DCO_BGR: fmt = AV_PIX_FMT_BGR24; break; + case DCO_RGB: fmt = AV_PIX_FMT_RGB24; break; + default: + av_log(log_ctx, AV_LOG_ERROR, + "onnx detect: unsupported channel order %d\n", input->order); + return AVERROR(ENOSYS); + } + + w = input->dims[dnn_get_width_idx_by_layout(input->layout)]; + h = input->dims[dnn_get_height_idx_by_layout(input->layout)]; + + const uint8_t *src; + int src_stride; + + if (frame->format == fmt && frame->width == w && frame->height == h) { + src = frame->data[0]; + src_stride = frame->linesize[0]; + } else { + uint8_buf = av_malloc((size_t)w * h * 3); + if (!uint8_buf) + return AVERROR(ENOMEM); + + linesizes[0] = w * 3; + + sws_ctx = sws_getContext(frame->width, frame->height, frame->format, + w, h, fmt, + SWS_FAST_BILINEAR, NULL, NULL, NULL); + if (!sws_ctx) { + av_log(log_ctx, AV_LOG_ERROR, + "onnx detect: failed to create sws context " + "fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n", + av_get_pix_fmt_name(frame->format), frame->width, frame->height, + av_get_pix_fmt_name(fmt), w, h); + ret = AVERROR(EINVAL); + goto free_buf; + } + + sws_scale(sws_ctx, + (const uint8_t *const *)frame->data, frame->linesize, + 0, frame->height, + (uint8_t *const [4]){uint8_buf, 0, 0, 0}, linesizes); + sws_freeContext(sws_ctx); + + src = uint8_buf; + src_stride = w * 3; + } + + /* Transpose packed HWC uint8 -> planar NCHW float32. */ + for (int c = 0; c < 3; c++) + for (int hi = 0; hi < h; hi++) + for (int wi = 0; wi < w; wi++) + dst[c * h * w + hi * w + wi] = + (float)src[hi * src_stride + wi * 3 + c]; + +free_buf: + av_freep(&uint8_buf); + return ret; +} + static int fill_model_input_onnx(ONNXModel *onnx_model, ONNXRequestItem *request) { LastLevelTaskItem *lltask = NULL; @@ -380,7 +459,9 @@ static int fill_model_input_onnx(ONNXModel *onnx_model, ONNXRequestItem *request } break; case DFT_ANALYTICS_DETECT: - ff_frame_to_dnn_detect(task->in_frame, &input, ctx); + ret = onnx_fill_detect_input(task->in_frame, &input, ctx); + if (ret < 0) + goto err; break; default: avpriv_report_missing_feature(ctx, "model function type %d", onnx_model->model.func_type); @@ -425,8 +506,8 @@ static int onnx_start_inference(void *args) ONNXModel *onnx_model = NULL; DnnContext *ctx = NULL; OrtStatus *status; - const char *input_names[1]; - const char *output_names[1]; + const char *input_names[1]; + int ret = DNN_GENERIC_ERROR; if (!request) { av_log(NULL, AV_LOG_ERROR, "ONNXRequestItem is NULL\n"); @@ -439,12 +520,6 @@ static int onnx_start_inference(void *args) onnx_model = (ONNXModel *)task->model; ctx = onnx_model->ctx; - if (task->nb_output > 1) { - avpriv_report_missing_feature(ctx, - "Multiple output tensors (%u) for ONNX backend", task->nb_output); - return AVERROR(ENOSYS); - } - if (!task->input_name || !task->output_names || !task->output_names[0]) { av_log(ctx, AV_LOG_ERROR, "ONNX backend: input/output tensor name was not resolved at load time\n"); @@ -468,46 +543,63 @@ static int onnx_start_inference(void *args) return AVERROR(EINVAL); } - for (size_t i = 0; i < output_count; i++) { - char *name = NULL; - status = g_ort->SessionGetOutputName(onnx_model->session, i, - onnx_model->allocator, &name); - if (status != NULL) { - g_ort->ReleaseStatus(status); - continue; + for (uint32_t req = 0; req < task->nb_output; req++) { + found_output = 0; + for (size_t i = 0; i < output_count; i++) { + char *name = NULL; + status = g_ort->SessionGetOutputName(onnx_model->session, i, + onnx_model->allocator, &name); + if (status != NULL) { + g_ort->ReleaseStatus(status); + continue; + } + if (!strcmp(name, task->output_names[req])) + found_output = 1; + onnx_model->allocator->Free(onnx_model->allocator, name); + if (found_output) + break; + } + if (!found_output) { + av_log(ctx, AV_LOG_ERROR, + "Output name '%s' not found in ONNX model\n", + task->output_names[req]); + return AVERROR(EINVAL); } - if (!strcmp(name, task->output_names[0])) - found_output = 1; - onnx_model->allocator->Free(onnx_model->allocator, name); - if (found_output) - break; - } - - if (!found_output) { - av_log(ctx, AV_LOG_ERROR, - "Output name '%s' not found in ONNX model\n", - task->output_names[0]); - return AVERROR(EINVAL); } onnx_model->output_resolved = 1; } - input_names[0] = task->input_name; - output_names[0] = task->output_names[0]; + input_names[0] = task->input_name; + + /* ORT writes task->nb_output result handles into this array; it must be + * allocated (and NULL-initialised) before Run() so ORT owns each slot. */ + av_freep(&infer_request->output_tensors); + infer_request->output_tensors = av_calloc(task->nb_output, + sizeof(*infer_request->output_tensors)); + if (!infer_request->output_tensors) { + infer_request->nb_outputs = 0; + return AVERROR(ENOMEM); + } + infer_request->nb_outputs = task->nb_output; status = g_ort->Run(onnx_model->session, NULL, - input_names, (const OrtValue *const *)&infer_request->input_tensor, 1, - output_names, 1, &infer_request->output_tensor); + input_names, (const OrtValue *const *)&infer_request->input_tensor, 1, + task->output_names, task->nb_output, infer_request->output_tensors); if (status != NULL) { const char *msg = g_ort->GetErrorMessage(status); av_log(ctx, AV_LOG_ERROR, "ONNX inference failed: %s\n", msg); g_ort->ReleaseStatus(status); - return DNN_GENERIC_ERROR; + goto err; } return 0; + +err: + av_freep(&infer_request->output_tensors); + infer_request->nb_outputs = 0; + return ret; } static void infer_completion_callback(void *args) @@ -515,7 +607,7 @@ static void infer_completion_callback(void *args) ONNXRequestItem *request = (ONNXRequestItem *)args; LastLevelTaskItem *lltask = request->lltask; TaskItem *task = lltask->task; - DNNData outputs = { 0 }; + DNNData *outputs = NULL; ONNXInferRequest *infer_request = request->infer_request; ONNXModel *onnx_model = (ONNXModel *)task->model; DnnContext *ctx = onnx_model->ctx; @@ -523,93 +615,107 @@ static void infer_completion_callback(void *args) ONNXTensorElementDataType tensor_type; size_t num_dims; int64_t *dims; - void *output_data; OrtStatus *status; - if (!infer_request->output_tensor) { - av_log(ctx, AV_LOG_ERROR, "Output tensor is NULL\n"); + outputs = av_calloc(infer_request->nb_outputs, sizeof(*outputs)); + if (!outputs) { + av_log(ctx, AV_LOG_ERROR, "Failed to allocate output DNNData array\n"); goto err; } - status = g_ort->GetTensorTypeAndShape(infer_request->output_tensor, &tensor_info); - if (status != NULL) { - av_log(ctx, AV_LOG_ERROR, "Failed to get output tensor info\n"); - g_ort->ReleaseStatus(status); - goto err; - } + for (uint32_t i = 0; i < infer_request->nb_outputs; i++) { + status = g_ort->GetTensorTypeAndShape(infer_request->output_tensors[i], + &tensor_info); + if (status != NULL) { + av_log(ctx, AV_LOG_ERROR, "Failed to get output tensor[%u] type/shape\n", i); + g_ort->ReleaseStatus(status); + goto err; + } - g_ort->GetDimensionsCount(tensor_info, &num_dims); - dims = av_malloc(num_dims * sizeof(int64_t)); - if (!dims) { - av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for dimensions\n"); - g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info); - goto err; - } - g_ort->GetDimensions(tensor_info, dims, num_dims); + g_ort->GetDimensionsCount(tensor_info, &num_dims); + dims = av_malloc(num_dims * sizeof(int64_t)); + if (!dims) { + av_log(ctx, AV_LOG_ERROR, "Failed to allocate dims array\n"); + g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info); + goto err; + } + g_ort->GetDimensions(tensor_info, dims, num_dims); - /* Output is interpreted as NCHW, matching the input assumption. */ - outputs.layout = DL_NCHW; - outputs.order = DCO_RGB; + g_ort->GetTensorElementType(tensor_info, &tensor_type); + if (tensor_type == ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT) { + outputs[i].dt = DNN_FLOAT; + } else { + av_log(ctx, AV_LOG_ERROR, + "Unsupported output tensor[%u] data type, only float supported\n", i); + av_free(dims); + g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info); + goto err; + } + + /* Output is interpreted as NCHW, matching the input assumption. */ + outputs[i].layout = DL_NCHW; + outputs[i].order = DCO_RGB; + + if (num_dims == 4) { + outputs[i].dims[0] = dims[0]; + outputs[i].dims[1] = dims[1]; + outputs[i].dims[2] = dims[2]; + outputs[i].dims[3] = dims[3]; + } else if (num_dims == 3) { + /* Some detection model outputs are [1, N, D] — store in dims[0..2]. */ + outputs[i].dims[0] = dims[0]; + outputs[i].dims[1] = 1; + outputs[i].dims[2] = dims[1]; + outputs[i].dims[3] = dims[2]; + } else { + avpriv_report_missing_feature(ctx, + "Support for %zu-dimensional output (tensor[%u])", num_dims, i); + av_free(dims); + g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info); + goto err; + } + + status = g_ort->GetTensorMutableData(infer_request->output_tensors[i], &outputs[i].data); + if (status != NULL) { + av_log(ctx, AV_LOG_ERROR, "Failed to get tensor[%u] data pointer\n", i); + g_ort->ReleaseStatus(status); + av_free(dims); + g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info); + goto err; + } - g_ort->GetTensorElementType(tensor_info, &tensor_type); - if (tensor_type == ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT) { - outputs.dt = DNN_FLOAT; - } else { - av_log(ctx, AV_LOG_ERROR, "Unsupported output tensor data type, only float is supported\n"); av_free(dims); g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info); - goto err; } - if (num_dims == 4) { - outputs.dims[0] = dims[0]; - outputs.dims[1] = dims[1]; - outputs.dims[2] = dims[2]; - outputs.dims[3] = dims[3]; - } else { - avpriv_report_missing_feature(ctx, "Support for %zu dimensional output", num_dims); - av_free(dims); - g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info); - goto err; - } - - status = g_ort->GetTensorMutableData(infer_request->output_tensor, &output_data); - if (status != NULL) { - av_log(ctx, AV_LOG_ERROR, "Failed to get tensor data\n"); - g_ort->ReleaseStatus(status); - av_free(dims); - g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info); - goto err; - } - - outputs.data = output_data; - switch (onnx_model->model.func_type) { case DFT_PROCESS_FRAME: if (task->do_ioproc) { - outputs.scale = 255; + outputs[0].scale = 255; if (onnx_model->model.frame_post_proc != NULL) { - onnx_model->model.frame_post_proc(task->out_frame, &outputs, onnx_model->model.filter_ctx); + onnx_model->model.frame_post_proc(task->out_frame, outputs, onnx_model->model.filter_ctx); } else { - ff_proc_from_dnn_to_frame(task->out_frame, &outputs, ctx); + ff_proc_from_dnn_to_frame(task->out_frame, outputs, ctx); } } else { - task->out_frame->width = outputs.dims[dnn_get_width_idx_by_layout(outputs.layout)]; - task->out_frame->height = outputs.dims[dnn_get_height_idx_by_layout(outputs.layout)]; + task->out_frame->width = outputs[0].dims[dnn_get_width_idx_by_layout(outputs[0].layout)]; + task->out_frame->height = outputs[0].dims[dnn_get_height_idx_by_layout(outputs[0].layout)]; } break; + case DFT_ANALYTICS_DETECT: + onnx_model->model.detect_post_proc(task->in_frame, outputs, + infer_request->nb_outputs, + onnx_model->model.filter_ctx); + break; default: avpriv_report_missing_feature(ctx, "model function type %d", onnx_model->model.func_type); - av_free(dims); - g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info); goto err; } - av_free(dims); - g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info); task->inference_done++; err: + av_freep(&outputs); av_freep(&request->lltask); onnx_free_request(infer_request); if (ff_safe_queue_push_back(onnx_model->request_queue, request) < 0) { @@ -712,12 +818,9 @@ err: static ONNXInferRequest *onnx_create_inference_request(void) { - ONNXInferRequest *request = av_malloc(sizeof(ONNXInferRequest)); + ONNXInferRequest *request = av_mallocz(sizeof(ONNXInferRequest)); if (!request) return NULL; - request->input_tensor = NULL; - request->output_tensor = NULL; - request->input_data = NULL; return request; } diff --git a/libavfilter/dnn_filter_common.c b/libavfilter/dnn_filter_common.c index 73c5e6b33c..25f985766c 100644 --- a/libavfilter/dnn_filter_common.c +++ b/libavfilter/dnn_filter_common.c @@ -107,18 +107,14 @@ int ff_dnn_init(DnnContext *ctx, DNNFunctionType func_type, AVFilterContext *fil return AVERROR(EINVAL); } } else if (backend == DNN_ONNX) { - /* ONNX: input and output tensor names are optional.*/ + /* ONNX: input and output tensor names are optional. + * Multiple output names may be specified separated by '&'. */ if (ctx->model_outputnames_string) { ctx->model_outputnames = separate_output_names(ctx->model_outputnames_string, "&", &ctx->nb_outputs); if (!ctx->model_outputnames) { av_log(filter_ctx, AV_LOG_ERROR, "could not parse model output names\n"); return AVERROR(EINVAL); } - if (ctx->nb_outputs != 1) { - av_log(filter_ctx, AV_LOG_ERROR, - "ONNX backend supports a single output name only\n"); - return AVERROR(EINVAL); - } } } diff --git a/libavfilter/vf_dnn_detect.c b/libavfilter/vf_dnn_detect.c index 81487ba14b..63832984b5 100644 --- a/libavfilter/vf_dnn_detect.c +++ b/libavfilter/vf_dnn_detect.c @@ -22,6 +22,7 @@ */ #include "libavutil/file_open.h" +#include "libavutil/internal.h" #include "libavutil/mem.h" #include "libavutil/opt.h" #include "filters.h" @@ -70,6 +71,9 @@ static const AVOption dnn_detect_options[] = { #endif #if (CONFIG_LIBOPENVINO == 1) { "openvino", "openvino backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = DNN_OV }, 0, 0, FLAGS, .unit = "backend" }, +#endif +#if (CONFIG_LIBONNXRUNTIME == 1) + { "onnx", "ONNX Runtime backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = DNN_ONNX }, 0, 0, FLAGS, .unit = "backend" }, #endif { "confidence", "threshold of confidence", OFFSET2(confidence), AV_OPT_TYPE_FLOAT, { .dbl = 0.5 }, 0, 1, FLAGS}, { "labels", "path to labels file", OFFSET2(labels_filename), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, @@ -85,7 +89,7 @@ static const AVOption dnn_detect_options[] = { { NULL } }; -AVFILTER_DNN_DEFINE_CLASS(dnn_detect, DNN_TF | DNN_OV); +AVFILTER_DNN_DEFINE_CLASS(dnn_detect, DNN_TF | DNN_OV | DNN_ONNX); static inline float sigmoid(float x) { return 1.f / (1.f + exp(-x)); @@ -436,7 +440,7 @@ static int dnn_detect_post_proc_ssd(AVFrame *frame, DNNData *output, int nb_outp return 0; } -static int dnn_detect_post_proc_ov(AVFrame *frame, DNNData *output, int nb_outputs, +static int dnn_detect_post_proc_anchored(AVFrame *frame, DNNData *output, int nb_outputs, AVFilterContext *filter_ctx) { AVFrameSideData *sd; @@ -551,9 +555,11 @@ static int dnn_detect_post_proc(AVFrame *frame, DNNData *output, uint32_t nb, AV DnnContext *dnn_ctx = &ctx->dnnctx; switch (dnn_ctx->backend_type) { case DNN_OV: - return dnn_detect_post_proc_ov(frame, output, nb, filter_ctx); + return dnn_detect_post_proc_anchored(frame, output, nb, filter_ctx); case DNN_TF: return dnn_detect_post_proc_tf(frame, output, filter_ctx); + case DNN_ONNX: + return dnn_detect_post_proc_anchored(frame, output, nb, filter_ctx); default: avpriv_report_missing_feature(filter_ctx, "Current dnn backend does not support detect filter\n"); return AVERROR(EINVAL); @@ -639,6 +645,8 @@ static int check_output_nb(DnnDetectContext *ctx, DNNBackendType backend_type, i return 0; case DNN_OV: return 0; + case DNN_ONNX: + return 0; default: avpriv_report_missing_feature(ctx, "Dnn detect filter does not support current backend\n"); return AVERROR(EINVAL); -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
