]> git.sesse.net Git - ffmpeg/blob - libavfilter/dnn/dnn_backend_native.c
65a56704d386c1d919b129325d892df0ca9f2f68
[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
31 static DNNReturnType get_input_native(void *model, DNNData *input, const char *input_name)
32 {
33     NativeModel *native_model = (NativeModel *)model;
34
35     for (int i = 0; i < native_model->operands_num; ++i) {
36         DnnOperand *oprd = &native_model->operands[i];
37         if (strcmp(oprd->name, input_name) == 0) {
38             if (oprd->type != DOT_INPUT)
39                 return DNN_ERROR;
40             input->dt = oprd->data_type;
41             av_assert0(oprd->dims[0] == 1);
42             input->height = oprd->dims[1];
43             input->width = oprd->dims[2];
44             input->channels = oprd->dims[3];
45             return DNN_SUCCESS;
46         }
47     }
48
49     // do not find the input operand
50     return DNN_ERROR;
51 }
52
53 static DNNReturnType set_input_native(void *model, DNNData *input, const char *input_name)
54 {
55     NativeModel *native_model = (NativeModel *)model;
56     DnnOperand *oprd = NULL;
57
58     if (native_model->layers_num <= 0 || native_model->operands_num <= 0)
59         return DNN_ERROR;
60
61     /* inputs */
62     for (int i = 0; i < native_model->operands_num; ++i) {
63         oprd = &native_model->operands[i];
64         if (strcmp(oprd->name, input_name) == 0) {
65             if (oprd->type != DOT_INPUT)
66                 return DNN_ERROR;
67             break;
68         }
69         oprd = NULL;
70     }
71
72     if (!oprd)
73         return DNN_ERROR;
74
75     oprd->dims[0] = 1;
76     oprd->dims[1] = input->height;
77     oprd->dims[2] = input->width;
78     oprd->dims[3] = input->channels;
79
80     av_freep(&oprd->data);
81     oprd->length = calculate_operand_data_length(oprd);
82     if (oprd->length <= 0)
83         return DNN_ERROR;
84     oprd->data = av_malloc(oprd->length);
85     if (!oprd->data)
86         return DNN_ERROR;
87
88     input->data = oprd->data;
89
90     return DNN_SUCCESS;
91 }
92
93 // Loads model and its parameters that are stored in a binary file with following structure:
94 // layers_num,layer_type,layer_parameterss,layer_type,layer_parameters...
95 // For CONV layer: activation_function, input_num, output_num, kernel_size, kernel, biases
96 // For DEPTH_TO_SPACE layer: block_size
97 DNNModel *ff_dnn_load_model_native(const char *model_filename, const char *options)
98 {
99     DNNModel *model = NULL;
100     char header_expected[] = "FFMPEGDNNNATIVE";
101     char *buf;
102     size_t size;
103     int version, header_size, major_version_expected = 1;
104     NativeModel *native_model = NULL;
105     AVIOContext *model_file_context;
106     int file_size, dnn_size, parsed_size;
107     int32_t layer;
108     DNNLayerType layer_type;
109
110     if (avio_open(&model_file_context, model_filename, AVIO_FLAG_READ) < 0){
111         return NULL;
112     }
113     file_size = avio_size(model_file_context);
114
115     model = av_mallocz(sizeof(DNNModel));
116     if (!model){
117         goto fail;
118     }
119
120     /**
121      * check file header with string and version
122      */
123     size = sizeof(header_expected);
124     buf = av_malloc(size);
125     if (!buf) {
126         goto fail;
127     }
128
129     // size - 1 to skip the ending '\0' which is not saved in file
130     avio_get_str(model_file_context, size - 1, buf, size);
131     dnn_size = size - 1;
132     if (strncmp(buf, header_expected, size) != 0) {
133         av_freep(&buf);
134         goto fail;
135     }
136     av_freep(&buf);
137
138     version = (int32_t)avio_rl32(model_file_context);
139     dnn_size += 4;
140     if (version != major_version_expected) {
141         goto fail;
142     }
143
144     // currently no need to check minor version
145     version = (int32_t)avio_rl32(model_file_context);
146     dnn_size += 4;
147     header_size = dnn_size;
148
149     native_model = av_mallocz(sizeof(NativeModel));
150     if (!native_model){
151         goto fail;
152     }
153     model->model = (void *)native_model;
154
155     avio_seek(model_file_context, file_size - 8, SEEK_SET);
156     native_model->layers_num = (int32_t)avio_rl32(model_file_context);
157     native_model->operands_num = (int32_t)avio_rl32(model_file_context);
158     dnn_size += 8;
159     avio_seek(model_file_context, header_size, SEEK_SET);
160
161     native_model->layers = av_mallocz(native_model->layers_num * sizeof(Layer));
162     if (!native_model->layers){
163         goto fail;
164     }
165
166     native_model->operands = av_mallocz(native_model->operands_num * sizeof(DnnOperand));
167     if (!native_model->operands){
168         goto fail;
169     }
170
171     for (layer = 0; layer < native_model->layers_num; ++layer){
172         layer_type = (int32_t)avio_rl32(model_file_context);
173         dnn_size += 4;
174
175         if (layer_type >= DLT_COUNT) {
176             goto fail;
177         }
178
179         native_model->layers[layer].type = layer_type;
180         parsed_size = layer_funcs[layer_type].pf_load(&native_model->layers[layer], model_file_context, file_size, native_model->operands_num);
181         if (!parsed_size) {
182             goto fail;
183         }
184         dnn_size += parsed_size;
185     }
186
187     for (int32_t i = 0; i < native_model->operands_num; ++i){
188         DnnOperand *oprd;
189         int32_t name_len;
190         int32_t operand_index = (int32_t)avio_rl32(model_file_context);
191         dnn_size += 4;
192
193         if (operand_index >= native_model->operands_num) {
194             goto fail;
195         }
196
197         oprd = &native_model->operands[operand_index];
198         name_len = (int32_t)avio_rl32(model_file_context);
199         dnn_size += 4;
200
201         avio_get_str(model_file_context, name_len, oprd->name, sizeof(oprd->name));
202         dnn_size += name_len;
203
204         oprd->type = (int32_t)avio_rl32(model_file_context);
205         dnn_size += 4;
206
207         oprd->data_type = (int32_t)avio_rl32(model_file_context);
208         dnn_size += 4;
209
210         for (int32_t dim = 0; dim < 4; ++dim) {
211             oprd->dims[dim] = (int32_t)avio_rl32(model_file_context);
212             dnn_size += 4;
213         }
214
215         oprd->isNHWC = 1;
216     }
217
218     avio_closep(&model_file_context);
219
220     if (dnn_size != file_size){
221         ff_dnn_free_model_native(&model);
222         return NULL;
223     }
224
225     model->set_input = &set_input_native;
226     model->get_input = &get_input_native;
227     model->options = options;
228
229     return model;
230
231 fail:
232     ff_dnn_free_model_native(&model);
233     avio_closep(&model_file_context);
234     return NULL;
235 }
236
237 DNNReturnType ff_dnn_execute_model_native(const DNNModel *model, DNNData *outputs, const char **output_names, uint32_t nb_output)
238 {
239     NativeModel *native_model = (NativeModel *)model->model;
240     int32_t layer;
241
242     if (native_model->layers_num <= 0 || native_model->operands_num <= 0)
243         return DNN_ERROR;
244     if (!native_model->operands[0].data)
245         return DNN_ERROR;
246
247     for (layer = 0; layer < native_model->layers_num; ++layer){
248         DNNLayerType layer_type = native_model->layers[layer].type;
249         layer_funcs[layer_type].pf_exec(native_model->operands,
250                                         native_model->layers[layer].input_operand_indexes,
251                                         native_model->layers[layer].output_operand_index,
252                                         native_model->layers[layer].params);
253     }
254
255     for (uint32_t i = 0; i < nb_output; ++i) {
256         DnnOperand *oprd = NULL;
257         const char *output_name = output_names[i];
258         for (int j = 0; j < native_model->operands_num; ++j) {
259             if (strcmp(native_model->operands[j].name, output_name) == 0) {
260                 oprd = &native_model->operands[j];
261                 break;
262             }
263         }
264
265         if (oprd == NULL)
266             return DNN_ERROR;
267
268         outputs[i].data = oprd->data;
269         outputs[i].height = oprd->dims[1];
270         outputs[i].width = oprd->dims[2];
271         outputs[i].channels = oprd->dims[3];
272         outputs[i].dt = oprd->data_type;
273     }
274
275     return DNN_SUCCESS;
276 }
277
278 int32_t calculate_operand_dims_count(const DnnOperand *oprd)
279 {
280     int32_t result = 1;
281     for (int i = 0; i < 4; ++i)
282         result *= oprd->dims[i];
283
284     return result;
285 }
286
287 int32_t calculate_operand_data_length(const DnnOperand* oprd)
288 {
289     // currently, we just support DNN_FLOAT
290     uint64_t len = sizeof(float);
291     for (int i = 0; i < 4; i++) {
292         len *= oprd->dims[i];
293         if (len > INT32_MAX)
294             return 0;
295     }
296     return len;
297 }
298
299 void ff_dnn_free_model_native(DNNModel **model)
300 {
301     NativeModel *native_model;
302     ConvolutionalParams *conv_params;
303     int32_t layer;
304
305     if (*model)
306     {
307         if ((*model)->model) {
308             native_model = (NativeModel *)(*model)->model;
309             if (native_model->layers) {
310                 for (layer = 0; layer < native_model->layers_num; ++layer){
311                     if (native_model->layers[layer].type == DLT_CONV2D){
312                         conv_params = (ConvolutionalParams *)native_model->layers[layer].params;
313                         av_freep(&conv_params->kernel);
314                         av_freep(&conv_params->biases);
315                     }
316                     av_freep(&native_model->layers[layer].params);
317                 }
318                 av_freep(&native_model->layers);
319             }
320
321             if (native_model->operands) {
322                 for (uint32_t operand = 0; operand < native_model->operands_num; ++operand)
323                     av_freep(&native_model->operands[operand].data);
324                 av_freep(&native_model->operands);
325             }
326
327             av_freep(&native_model);
328         }
329         av_freep(model);
330     }
331 }