]> git.sesse.net Git - ffmpeg/blob - libavfilter/dnn/dnn_backend_native.c
dnn: add a new interface DNNModel.get_output
[ffmpeg] / libavfilter / dnn / dnn_backend_native.c
1 /*
2  * Copyright (c) 2018 Sergey Lavrushkin
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20
21 /**
22  * @file
23  * DNN native backend implementation.
24  */
25
26 #include "dnn_backend_native.h"
27 #include "libavutil/avassert.h"
28 #include "dnn_backend_native_layer_conv2d.h"
29 #include "dnn_backend_native_layers.h"
30 #include "dnn_io_proc.h"
31
32 #define OFFSET(x) offsetof(NativeContext, x)
33 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM
34 static const AVOption dnn_native_options[] = {
35     { "conv2d_threads", "threads num for conv2d layer", OFFSET(options.conv2d_threads), AV_OPT_TYPE_INT,  { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS },
36     { NULL },
37 };
38
39 const AVClass dnn_native_class = {
40     .class_name = "dnn_native",
41     .item_name  = av_default_item_name,
42     .option     = dnn_native_options,
43     .version    = LIBAVUTIL_VERSION_INT,
44     .category   = AV_CLASS_CATEGORY_FILTER,
45 };
46
47 static DNNReturnType execute_model_native(const DNNModel *model, const char *input_name, AVFrame *in_frame,
48                                           const char **output_names, uint32_t nb_output, AVFrame *out_frame,
49                                           int do_ioproc);
50
51 static DNNReturnType get_input_native(void *model, DNNData *input, const char *input_name)
52 {
53     NativeModel *native_model = (NativeModel *)model;
54     NativeContext *ctx = &native_model->ctx;
55
56     for (int i = 0; i < native_model->operands_num; ++i) {
57         DnnOperand *oprd = &native_model->operands[i];
58         if (strcmp(oprd->name, input_name) == 0) {
59             if (oprd->type != DOT_INPUT) {
60                 av_log(ctx, AV_LOG_ERROR, "Found \"%s\" in model, but it is not input node\n", input_name);
61                 return DNN_ERROR;
62             }
63             input->dt = oprd->data_type;
64             av_assert0(oprd->dims[0] == 1);
65             input->height = oprd->dims[1];
66             input->width = oprd->dims[2];
67             input->channels = oprd->dims[3];
68             return DNN_SUCCESS;
69         }
70     }
71
72     // do not find the input operand
73     av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model\n", input_name);
74     return DNN_ERROR;
75 }
76
77 static DNNReturnType get_output_native(void *model, const char *input_name, int input_width, int input_height,
78                                        const char *output_name, int *output_width, int *output_height)
79 {
80     DNNReturnType ret;
81     NativeModel *native_model = (NativeModel *)model;
82     AVFrame *in_frame = av_frame_alloc();
83     AVFrame *out_frame = av_frame_alloc();
84     in_frame->width = input_width;
85     in_frame->height = input_height;
86
87     ret = execute_model_native(native_model->model, input_name, in_frame, &output_name, 1, out_frame, 0);
88     *output_width = out_frame->width;
89     *output_height = out_frame->height;
90
91     av_frame_free(&out_frame);
92     av_frame_free(&in_frame);
93     return ret;
94 }
95
96 // Loads model and its parameters that are stored in a binary file with following structure:
97 // layers_num,layer_type,layer_parameterss,layer_type,layer_parameters...
98 // For CONV layer: activation_function, input_num, output_num, kernel_size, kernel, biases
99 // For DEPTH_TO_SPACE layer: block_size
100 DNNModel *ff_dnn_load_model_native(const char *model_filename, const char *options, void *userdata)
101 {
102     DNNModel *model = NULL;
103     char header_expected[] = "FFMPEGDNNNATIVE";
104     char *buf;
105     size_t size;
106     int version, header_size, major_version_expected = 1;
107     NativeModel *native_model = NULL;
108     AVIOContext *model_file_context;
109     int file_size, dnn_size, parsed_size;
110     int32_t layer;
111     DNNLayerType layer_type;
112
113     if (avio_open(&model_file_context, model_filename, AVIO_FLAG_READ) < 0){
114         return NULL;
115     }
116     file_size = avio_size(model_file_context);
117
118     model = av_mallocz(sizeof(DNNModel));
119     if (!model){
120         goto fail;
121     }
122
123     /**
124      * check file header with string and version
125      */
126     size = sizeof(header_expected);
127     buf = av_malloc(size);
128     if (!buf) {
129         goto fail;
130     }
131
132     // size - 1 to skip the ending '\0' which is not saved in file
133     avio_get_str(model_file_context, size - 1, buf, size);
134     dnn_size = size - 1;
135     if (strncmp(buf, header_expected, size) != 0) {
136         av_freep(&buf);
137         goto fail;
138     }
139     av_freep(&buf);
140
141     version = (int32_t)avio_rl32(model_file_context);
142     dnn_size += 4;
143     if (version != major_version_expected) {
144         goto fail;
145     }
146
147     // currently no need to check minor version
148     version = (int32_t)avio_rl32(model_file_context);
149     dnn_size += 4;
150     header_size = dnn_size;
151
152     native_model = av_mallocz(sizeof(NativeModel));
153     if (!native_model){
154         goto fail;
155     }
156
157     native_model->ctx.class = &dnn_native_class;
158     model->options = options;
159     if (av_opt_set_from_string(&native_model->ctx, model->options, NULL, "=", "&") < 0)
160         goto fail;
161     model->model = (void *)native_model;
162     native_model->model = model;
163
164 #if !HAVE_PTHREAD_CANCEL
165     if (native_model->ctx.options.conv2d_threads > 1){
166         av_log(&native_model->ctx, AV_LOG_WARNING, "'conv2d_threads' option was set but it is not supported "
167                        "on this build (pthread support is required)\n");
168     }
169 #endif
170
171     avio_seek(model_file_context, file_size - 8, SEEK_SET);
172     native_model->layers_num = (int32_t)avio_rl32(model_file_context);
173     native_model->operands_num = (int32_t)avio_rl32(model_file_context);
174     dnn_size += 8;
175     avio_seek(model_file_context, header_size, SEEK_SET);
176
177     native_model->layers = av_mallocz(native_model->layers_num * sizeof(Layer));
178     if (!native_model->layers){
179         goto fail;
180     }
181
182     native_model->operands = av_mallocz(native_model->operands_num * sizeof(DnnOperand));
183     if (!native_model->operands){
184         goto fail;
185     }
186
187     for (layer = 0; layer < native_model->layers_num; ++layer){
188         layer_type = (int32_t)avio_rl32(model_file_context);
189         dnn_size += 4;
190
191         if (layer_type >= DLT_COUNT) {
192             goto fail;
193         }
194
195         native_model->layers[layer].type = layer_type;
196         parsed_size = layer_funcs[layer_type].pf_load(&native_model->layers[layer], model_file_context, file_size, native_model->operands_num);
197         if (!parsed_size) {
198             goto fail;
199         }
200         dnn_size += parsed_size;
201     }
202
203     for (int32_t i = 0; i < native_model->operands_num; ++i){
204         DnnOperand *oprd;
205         int32_t name_len;
206         int32_t operand_index = (int32_t)avio_rl32(model_file_context);
207         dnn_size += 4;
208
209         if (operand_index >= native_model->operands_num) {
210             goto fail;
211         }
212
213         oprd = &native_model->operands[operand_index];
214         name_len = (int32_t)avio_rl32(model_file_context);
215         dnn_size += 4;
216
217         avio_get_str(model_file_context, name_len, oprd->name, sizeof(oprd->name));
218         dnn_size += name_len;
219
220         oprd->type = (int32_t)avio_rl32(model_file_context);
221         dnn_size += 4;
222
223         oprd->data_type = (int32_t)avio_rl32(model_file_context);
224         dnn_size += 4;
225
226         for (int32_t dim = 0; dim < 4; ++dim) {
227             oprd->dims[dim] = (int32_t)avio_rl32(model_file_context);
228             dnn_size += 4;
229         }
230
231         oprd->isNHWC = 1;
232     }
233
234     avio_closep(&model_file_context);
235
236     if (dnn_size != file_size){
237         ff_dnn_free_model_native(&model);
238         return NULL;
239     }
240
241     model->get_input = &get_input_native;
242     model->get_output = &get_output_native;
243     model->userdata = userdata;
244
245     return model;
246
247 fail:
248     ff_dnn_free_model_native(&model);
249     avio_closep(&model_file_context);
250     return NULL;
251 }
252
253 static DNNReturnType execute_model_native(const DNNModel *model, const char *input_name, AVFrame *in_frame,
254                                           const char **output_names, uint32_t nb_output, AVFrame *out_frame,
255                                           int do_ioproc)
256 {
257     NativeModel *native_model = (NativeModel *)model->model;
258     NativeContext *ctx = &native_model->ctx;
259     int32_t layer;
260     DNNData input, output;
261     DnnOperand *oprd = NULL;
262
263     if (native_model->layers_num <= 0 || native_model->operands_num <= 0) {
264         av_log(ctx, AV_LOG_ERROR, "No operands or layers in model\n");
265         return DNN_ERROR;
266     }
267
268     for (int i = 0; i < native_model->operands_num; ++i) {
269         oprd = &native_model->operands[i];
270         if (strcmp(oprd->name, input_name) == 0) {
271             if (oprd->type != DOT_INPUT) {
272                 av_log(ctx, AV_LOG_ERROR, "Found \"%s\" in model, but it is not input node\n", input_name);
273                 return DNN_ERROR;
274             }
275             break;
276         }
277         oprd = NULL;
278     }
279     if (!oprd) {
280         av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model\n", input_name);
281         return DNN_ERROR;
282     }
283
284     oprd->dims[1] = in_frame->height;
285     oprd->dims[2] = in_frame->width;
286
287     av_freep(&oprd->data);
288     oprd->length = calculate_operand_data_length(oprd);
289     if (oprd->length <= 0) {
290         av_log(ctx, AV_LOG_ERROR, "The input data length overflow\n");
291         return DNN_ERROR;
292     }
293     oprd->data = av_malloc(oprd->length);
294     if (!oprd->data) {
295         av_log(ctx, AV_LOG_ERROR, "Failed to malloc memory for input data\n");
296         return DNN_ERROR;
297     }
298
299     input.height = oprd->dims[1];
300     input.width = oprd->dims[2];
301     input.channels = oprd->dims[3];
302     input.data = oprd->data;
303     input.dt = oprd->data_type;
304     if (do_ioproc) {
305         if (native_model->model->pre_proc != NULL) {
306             native_model->model->pre_proc(in_frame, &input, native_model->model->userdata);
307         } else {
308             proc_from_frame_to_dnn(in_frame, &input, ctx);
309         }
310     }
311
312     if (nb_output != 1) {
313         // currently, the filter does not need multiple outputs,
314         // so we just pending the support until we really need it.
315         av_log(ctx, AV_LOG_ERROR, "do not support multiple outputs\n");
316         return DNN_ERROR;
317     }
318
319     for (layer = 0; layer < native_model->layers_num; ++layer){
320         DNNLayerType layer_type = native_model->layers[layer].type;
321         if (layer_funcs[layer_type].pf_exec(native_model->operands,
322                                             native_model->layers[layer].input_operand_indexes,
323                                             native_model->layers[layer].output_operand_index,
324                                             native_model->layers[layer].params,
325                                             &native_model->ctx) == DNN_ERROR) {
326             av_log(ctx, AV_LOG_ERROR, "Failed to execuet model\n");
327             return DNN_ERROR;
328         }
329     }
330
331     for (uint32_t i = 0; i < nb_output; ++i) {
332         DnnOperand *oprd = NULL;
333         const char *output_name = output_names[i];
334         for (int j = 0; j < native_model->operands_num; ++j) {
335             if (strcmp(native_model->operands[j].name, output_name) == 0) {
336                 oprd = &native_model->operands[j];
337                 break;
338             }
339         }
340
341         if (oprd == NULL) {
342             av_log(ctx, AV_LOG_ERROR, "Could not find output in model\n");
343             return DNN_ERROR;
344         }
345
346         output.data = oprd->data;
347         output.height = oprd->dims[1];
348         output.width = oprd->dims[2];
349         output.channels = oprd->dims[3];
350         output.dt = oprd->data_type;
351
352         if (do_ioproc) {
353             if (native_model->model->post_proc != NULL) {
354                 native_model->model->post_proc(out_frame, &output, native_model->model->userdata);
355             } else {
356                 proc_from_dnn_to_frame(out_frame, &output, ctx);
357             }
358         } else {
359             out_frame->width = output.width;
360             out_frame->height = output.height;
361         }
362     }
363
364     return DNN_SUCCESS;
365 }
366
367 DNNReturnType ff_dnn_execute_model_native(const DNNModel *model, const char *input_name, AVFrame *in_frame,
368                                           const char **output_names, uint32_t nb_output, AVFrame *out_frame)
369 {
370     NativeModel *native_model = (NativeModel *)model->model;
371     NativeContext *ctx = &native_model->ctx;
372
373     if (!in_frame) {
374         av_log(ctx, AV_LOG_ERROR, "in frame is NULL when execute model.\n");
375         return DNN_ERROR;
376     }
377
378     if (!out_frame) {
379         av_log(ctx, AV_LOG_ERROR, "out frame is NULL when execute model.\n");
380         return DNN_ERROR;
381     }
382
383     return execute_model_native(model, input_name, in_frame, output_names, nb_output, out_frame, 1);
384 }
385
386 int32_t calculate_operand_dims_count(const DnnOperand *oprd)
387 {
388     int32_t result = 1;
389     for (int i = 0; i < 4; ++i)
390         result *= oprd->dims[i];
391
392     return result;
393 }
394
395 int32_t calculate_operand_data_length(const DnnOperand* oprd)
396 {
397     // currently, we just support DNN_FLOAT
398     uint64_t len = sizeof(float);
399     for (int i = 0; i < 4; i++) {
400         len *= oprd->dims[i];
401         if (len > INT32_MAX)
402             return 0;
403     }
404     return len;
405 }
406
407 void ff_dnn_free_model_native(DNNModel **model)
408 {
409     NativeModel *native_model;
410     ConvolutionalParams *conv_params;
411     int32_t layer;
412
413     if (*model)
414     {
415         if ((*model)->model) {
416             native_model = (NativeModel *)(*model)->model;
417             if (native_model->layers) {
418                 for (layer = 0; layer < native_model->layers_num; ++layer){
419                     if (native_model->layers[layer].type == DLT_CONV2D){
420                         conv_params = (ConvolutionalParams *)native_model->layers[layer].params;
421                         av_freep(&conv_params->kernel);
422                         av_freep(&conv_params->biases);
423                     }
424                     av_freep(&native_model->layers[layer].params);
425                 }
426                 av_freep(&native_model->layers);
427             }
428
429             if (native_model->operands) {
430                 for (uint32_t operand = 0; operand < native_model->operands_num; ++operand)
431                     av_freep(&native_model->operands[operand].data);
432                 av_freep(&native_model->operands);
433             }
434
435             av_freep(&native_model);
436         }
437         av_freep(model);
438     }
439 }