]> git.sesse.net Git - ffmpeg/blob - libavfilter/dnn/dnn_backend_tf.c
lavfi/dnn_backend_tensorflow.c: fix mem leak in load_tf_model
[ffmpeg] / libavfilter / dnn / dnn_backend_tf.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 tensorflow backend implementation.
24  */
25
26 #include "dnn_backend_tf.h"
27 #include "dnn_backend_native.h"
28 #include "dnn_backend_native_layer_conv2d.h"
29 #include "dnn_backend_native_layer_depth2space.h"
30 #include "libavformat/avio.h"
31 #include "libavutil/avassert.h"
32 #include "../internal.h"
33 #include "dnn_backend_native_layer_pad.h"
34 #include "dnn_backend_native_layer_maximum.h"
35 #include "dnn_io_proc.h"
36
37 #include <tensorflow/c/c_api.h>
38
39 typedef struct TFOptions{
40     char *sess_config;
41 } TFOptions;
42
43 typedef struct TFContext {
44     const AVClass *class;
45     TFOptions options;
46 } TFContext;
47
48 typedef struct TFModel{
49     TFContext ctx;
50     DNNModel *model;
51     TF_Graph *graph;
52     TF_Session *session;
53     TF_Status *status;
54 } TFModel;
55
56 #define OFFSET(x) offsetof(TFContext, x)
57 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM
58 static const AVOption dnn_tensorflow_options[] = {
59     { "sess_config", "config for SessionOptions", OFFSET(options.sess_config), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
60     { NULL }
61 };
62
63 AVFILTER_DEFINE_CLASS(dnn_tensorflow);
64
65 static DNNReturnType execute_model_tf(const DNNModel *model, const char *input_name, AVFrame *in_frame,
66                                       const char **output_names, uint32_t nb_output, AVFrame *out_frame,
67                                       int do_ioproc);
68
69 static void free_buffer(void *data, size_t length)
70 {
71     av_freep(&data);
72 }
73
74 static TF_Buffer *read_graph(const char *model_filename)
75 {
76     TF_Buffer *graph_buf;
77     unsigned char *graph_data = NULL;
78     AVIOContext *model_file_context;
79     long size, bytes_read;
80
81     if (avio_open(&model_file_context, model_filename, AVIO_FLAG_READ) < 0){
82         return NULL;
83     }
84
85     size = avio_size(model_file_context);
86
87     graph_data = av_malloc(size);
88     if (!graph_data){
89         avio_closep(&model_file_context);
90         return NULL;
91     }
92     bytes_read = avio_read(model_file_context, graph_data, size);
93     avio_closep(&model_file_context);
94     if (bytes_read != size){
95         av_freep(&graph_data);
96         return NULL;
97     }
98
99     graph_buf = TF_NewBuffer();
100     graph_buf->data = graph_data;
101     graph_buf->length = size;
102     graph_buf->data_deallocator = free_buffer;
103
104     return graph_buf;
105 }
106
107 static TF_Tensor *allocate_input_tensor(const DNNData *input)
108 {
109     TF_DataType dt;
110     size_t size;
111     int64_t input_dims[] = {1, input->height, input->width, input->channels};
112     switch (input->dt) {
113     case DNN_FLOAT:
114         dt = TF_FLOAT;
115         size = sizeof(float);
116         break;
117     case DNN_UINT8:
118         dt = TF_UINT8;
119         size = 1;
120         break;
121     default:
122         av_assert0(!"should not reach here");
123     }
124
125     return TF_AllocateTensor(dt, input_dims, 4,
126                              input_dims[1] * input_dims[2] * input_dims[3] * size);
127 }
128
129 static DNNReturnType get_input_tf(void *model, DNNData *input, const char *input_name)
130 {
131     TFModel *tf_model = model;
132     TFContext *ctx = &tf_model->ctx;
133     TF_Status *status;
134     int64_t dims[4];
135
136     TF_Output tf_output;
137     tf_output.oper = TF_GraphOperationByName(tf_model->graph, input_name);
138     if (!tf_output.oper) {
139         av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model\n", input_name);
140         return DNN_ERROR;
141     }
142
143     tf_output.index = 0;
144     input->dt = TF_OperationOutputType(tf_output);
145
146     status = TF_NewStatus();
147     TF_GraphGetTensorShape(tf_model->graph, tf_output, dims, 4, status);
148     if (TF_GetCode(status) != TF_OK){
149         TF_DeleteStatus(status);
150         av_log(ctx, AV_LOG_ERROR, "Failed to get input tensor shape: number of dimension incorrect\n");
151         return DNN_ERROR;
152     }
153     TF_DeleteStatus(status);
154
155     // currently only NHWC is supported
156     av_assert0(dims[0] == 1);
157     input->height = dims[1];
158     input->width = dims[2];
159     input->channels = dims[3];
160
161     return DNN_SUCCESS;
162 }
163
164 static DNNReturnType get_output_tf(void *model, const char *input_name, int input_width, int input_height,
165                                    const char *output_name, int *output_width, int *output_height)
166 {
167     DNNReturnType ret;
168     TFModel *tf_model = model;
169     TFContext *ctx = &tf_model->ctx;
170     AVFrame *in_frame = av_frame_alloc();
171     AVFrame *out_frame = NULL;
172
173     if (!in_frame) {
174         av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for input frame\n");
175         return DNN_ERROR;
176     }
177
178     out_frame = av_frame_alloc();
179     if (!out_frame) {
180         av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for output frame\n");
181         av_frame_free(&in_frame);
182         return DNN_ERROR;
183     }
184
185     in_frame->width = input_width;
186     in_frame->height = input_height;
187
188     ret = execute_model_tf(tf_model->model, input_name, in_frame, &output_name, 1, out_frame, 0);
189     *output_width = out_frame->width;
190     *output_height = out_frame->height;
191
192     av_frame_free(&out_frame);
193     av_frame_free(&in_frame);
194     return ret;
195 }
196
197 static DNNReturnType load_tf_model(TFModel *tf_model, const char *model_filename)
198 {
199     TFContext *ctx = &tf_model->ctx;
200     TF_Buffer *graph_def;
201     TF_ImportGraphDefOptions *graph_opts;
202     TF_SessionOptions *sess_opts;
203     const TF_Operation *init_op;
204     uint8_t *sess_config = NULL;
205     int sess_config_length = 0;
206
207     // prepare the sess config data
208     if (tf_model->ctx.options.sess_config != NULL) {
209         /*
210         tf_model->ctx.options.sess_config is hex to present the serialized proto
211         required by TF_SetConfig below, so we need to first generate the serialized
212         proto in a python script, the following is a script example to generate
213         serialized proto which specifies one GPU, we can change the script to add
214         more options.
215
216         import tensorflow as tf
217         gpu_options = tf.GPUOptions(visible_device_list='0')
218         config = tf.ConfigProto(gpu_options=gpu_options)
219         s = config.SerializeToString()
220         b = ''.join("%02x" % int(ord(b)) for b in s[::-1])
221         print('0x%s' % b)
222
223         the script output looks like: 0xab...cd, and then pass 0xab...cd to sess_config.
224         */
225         char tmp[3];
226         tmp[2] = '\0';
227
228         if (strncmp(tf_model->ctx.options.sess_config, "0x", 2) != 0) {
229             av_log(ctx, AV_LOG_ERROR, "sess_config should start with '0x'\n");
230             return DNN_ERROR;
231         }
232
233         sess_config_length = strlen(tf_model->ctx.options.sess_config);
234         if (sess_config_length % 2 != 0) {
235             av_log(ctx, AV_LOG_ERROR, "the length of sess_config is not even (%s), "
236                                       "please re-generate the config.\n",
237                                       tf_model->ctx.options.sess_config);
238             return DNN_ERROR;
239         }
240
241         sess_config_length -= 2; //ignore the first '0x'
242         sess_config_length /= 2; //get the data length in byte
243
244         sess_config = av_malloc(sess_config_length);
245         if (!sess_config) {
246             av_log(ctx, AV_LOG_ERROR, "failed to allocate memory\n");
247             return DNN_ERROR;
248         }
249
250         for (int i = 0; i < sess_config_length; i++) {
251             int index = 2 + (sess_config_length - 1 - i) * 2;
252             tmp[0] = tf_model->ctx.options.sess_config[index];
253             tmp[1] = tf_model->ctx.options.sess_config[index + 1];
254             sess_config[i] = strtol(tmp, NULL, 16);
255         }
256     }
257
258     graph_def = read_graph(model_filename);
259     if (!graph_def){
260         av_log(ctx, AV_LOG_ERROR, "Failed to read model \"%s\" graph\n", model_filename);
261         av_freep(&sess_config);
262         return DNN_ERROR;
263     }
264     tf_model->graph = TF_NewGraph();
265     tf_model->status = TF_NewStatus();
266     graph_opts = TF_NewImportGraphDefOptions();
267     TF_GraphImportGraphDef(tf_model->graph, graph_def, graph_opts, tf_model->status);
268     TF_DeleteImportGraphDefOptions(graph_opts);
269     TF_DeleteBuffer(graph_def);
270     if (TF_GetCode(tf_model->status) != TF_OK){
271         TF_DeleteGraph(tf_model->graph);
272         TF_DeleteStatus(tf_model->status);
273         av_log(ctx, AV_LOG_ERROR, "Failed to import serialized graph to model graph\n");
274         av_freep(&sess_config);
275         return DNN_ERROR;
276     }
277
278     init_op = TF_GraphOperationByName(tf_model->graph, "init");
279     sess_opts = TF_NewSessionOptions();
280
281     if (sess_config) {
282         TF_SetConfig(sess_opts, sess_config, sess_config_length,tf_model->status);
283         av_freep(&sess_config);
284         if (TF_GetCode(tf_model->status) != TF_OK) {
285             TF_DeleteGraph(tf_model->graph);
286             TF_DeleteStatus(tf_model->status);
287             TF_DeleteSessionOptions(sess_opts);
288             av_log(ctx, AV_LOG_ERROR, "Failed to set config for sess options with %s\n",
289                                       tf_model->ctx.options.sess_config);
290             return DNN_ERROR;
291         }
292     }
293
294     tf_model->session = TF_NewSession(tf_model->graph, sess_opts, tf_model->status);
295     TF_DeleteSessionOptions(sess_opts);
296     if (TF_GetCode(tf_model->status) != TF_OK)
297     {
298         TF_DeleteGraph(tf_model->graph);
299         TF_DeleteStatus(tf_model->status);
300         av_log(ctx, AV_LOG_ERROR, "Failed to create new session with model graph\n");
301         return DNN_ERROR;
302     }
303
304     // Run initialization operation with name "init" if it is present in graph
305     if (init_op){
306         TF_SessionRun(tf_model->session, NULL,
307                       NULL, NULL, 0,
308                       NULL, NULL, 0,
309                       &init_op, 1, NULL, tf_model->status);
310         if (TF_GetCode(tf_model->status) != TF_OK)
311         {
312             TF_DeleteSession(tf_model->session, tf_model->status);
313             TF_DeleteGraph(tf_model->graph);
314             TF_DeleteStatus(tf_model->status);
315             av_log(ctx, AV_LOG_ERROR, "Failed to run session when initializing\n");
316             return DNN_ERROR;
317         }
318     }
319
320     return DNN_SUCCESS;
321 }
322
323 #define NAME_BUFFER_SIZE 256
324
325 static DNNReturnType add_conv_layer(TFModel *tf_model, TF_Operation *transpose_op, TF_Operation **cur_op,
326                                     ConvolutionalParams* params, const int layer)
327 {
328     TFContext *ctx = &tf_model->ctx;
329     TF_Operation *op;
330     TF_OperationDescription *op_desc;
331     TF_Output input;
332     int64_t strides[] = {1, 1, 1, 1};
333     TF_Tensor *tensor;
334     int64_t dims[4];
335     int dims_len;
336     char name_buffer[NAME_BUFFER_SIZE];
337     int32_t size;
338
339     size = params->input_num * params->output_num * params->kernel_size * params->kernel_size;
340     input.index = 0;
341
342     snprintf(name_buffer, NAME_BUFFER_SIZE, "conv_kernel%d", layer);
343     op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer);
344     TF_SetAttrType(op_desc, "dtype", TF_FLOAT);
345     dims[0] = params->output_num;
346     dims[1] = params->kernel_size;
347     dims[2] = params->kernel_size;
348     dims[3] = params->input_num;
349     dims_len = 4;
350     tensor = TF_AllocateTensor(TF_FLOAT, dims, dims_len, size * sizeof(float));
351     memcpy(TF_TensorData(tensor), params->kernel, size * sizeof(float));
352     TF_SetAttrTensor(op_desc, "value", tensor, tf_model->status);
353     if (TF_GetCode(tf_model->status) != TF_OK){
354         av_log(ctx, AV_LOG_ERROR, "Failed to set value for kernel of conv layer %d\n", layer);
355         return DNN_ERROR;
356     }
357     op = TF_FinishOperation(op_desc, tf_model->status);
358     if (TF_GetCode(tf_model->status) != TF_OK){
359         av_log(ctx, AV_LOG_ERROR, "Failed to add kernel to conv layer %d\n", layer);
360         return DNN_ERROR;
361     }
362
363     snprintf(name_buffer, NAME_BUFFER_SIZE, "transpose%d", layer);
364     op_desc = TF_NewOperation(tf_model->graph, "Transpose", name_buffer);
365     input.oper = op;
366     TF_AddInput(op_desc, input);
367     input.oper = transpose_op;
368     TF_AddInput(op_desc, input);
369     TF_SetAttrType(op_desc, "T", TF_FLOAT);
370     TF_SetAttrType(op_desc, "Tperm", TF_INT32);
371     op = TF_FinishOperation(op_desc, tf_model->status);
372     if (TF_GetCode(tf_model->status) != TF_OK){
373         av_log(ctx, AV_LOG_ERROR, "Failed to add transpose to conv layer %d\n", layer);
374         return DNN_ERROR;
375     }
376
377     snprintf(name_buffer, NAME_BUFFER_SIZE, "conv2d%d", layer);
378     op_desc = TF_NewOperation(tf_model->graph, "Conv2D", name_buffer);
379     input.oper = *cur_op;
380     TF_AddInput(op_desc, input);
381     input.oper = op;
382     TF_AddInput(op_desc, input);
383     TF_SetAttrType(op_desc, "T", TF_FLOAT);
384     TF_SetAttrIntList(op_desc, "strides", strides, 4);
385     TF_SetAttrString(op_desc, "padding", "VALID", 5);
386     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
387     if (TF_GetCode(tf_model->status) != TF_OK){
388         av_log(ctx, AV_LOG_ERROR, "Failed to add conv2d to conv layer %d\n", layer);
389         return DNN_ERROR;
390     }
391
392     snprintf(name_buffer, NAME_BUFFER_SIZE, "conv_biases%d", layer);
393     op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer);
394     TF_SetAttrType(op_desc, "dtype", TF_FLOAT);
395     dims[0] = params->output_num;
396     dims_len = 1;
397     tensor = TF_AllocateTensor(TF_FLOAT, dims, dims_len, params->output_num * sizeof(float));
398     memcpy(TF_TensorData(tensor), params->biases, params->output_num * sizeof(float));
399     TF_SetAttrTensor(op_desc, "value", tensor, tf_model->status);
400     if (TF_GetCode(tf_model->status) != TF_OK){
401         av_log(ctx, AV_LOG_ERROR, "Failed to set value for conv_biases of conv layer %d\n", layer);
402         return DNN_ERROR;
403     }
404     op = TF_FinishOperation(op_desc, tf_model->status);
405     if (TF_GetCode(tf_model->status) != TF_OK){
406         av_log(ctx, AV_LOG_ERROR, "Failed to add conv_biases to conv layer %d\n", layer);
407         return DNN_ERROR;
408     }
409
410     snprintf(name_buffer, NAME_BUFFER_SIZE, "bias_add%d", layer);
411     op_desc = TF_NewOperation(tf_model->graph, "BiasAdd", name_buffer);
412     input.oper = *cur_op;
413     TF_AddInput(op_desc, input);
414     input.oper = op;
415     TF_AddInput(op_desc, input);
416     TF_SetAttrType(op_desc, "T", TF_FLOAT);
417     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
418     if (TF_GetCode(tf_model->status) != TF_OK){
419         av_log(ctx, AV_LOG_ERROR, "Failed to add bias_add to conv layer %d\n", layer);
420         return DNN_ERROR;
421     }
422
423     snprintf(name_buffer, NAME_BUFFER_SIZE, "activation%d", layer);
424     switch (params->activation){
425     case RELU:
426         op_desc = TF_NewOperation(tf_model->graph, "Relu", name_buffer);
427         break;
428     case TANH:
429         op_desc = TF_NewOperation(tf_model->graph, "Tanh", name_buffer);
430         break;
431     case SIGMOID:
432         op_desc = TF_NewOperation(tf_model->graph, "Sigmoid", name_buffer);
433         break;
434     default:
435         avpriv_report_missing_feature(ctx, "convolutional activation function %d", params->activation);
436         return DNN_ERROR;
437     }
438     input.oper = *cur_op;
439     TF_AddInput(op_desc, input);
440     TF_SetAttrType(op_desc, "T", TF_FLOAT);
441     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
442     if (TF_GetCode(tf_model->status) != TF_OK){
443         av_log(ctx, AV_LOG_ERROR, "Failed to add activation function to conv layer %d\n", layer);
444         return DNN_ERROR;
445     }
446
447     return DNN_SUCCESS;
448 }
449
450 static DNNReturnType add_depth_to_space_layer(TFModel *tf_model, TF_Operation **cur_op,
451                                               DepthToSpaceParams *params, const int layer)
452 {
453     TFContext *ctx = &tf_model->ctx;
454     TF_OperationDescription *op_desc;
455     TF_Output input;
456     char name_buffer[NAME_BUFFER_SIZE];
457
458     snprintf(name_buffer, NAME_BUFFER_SIZE, "depth_to_space%d", layer);
459     op_desc = TF_NewOperation(tf_model->graph, "DepthToSpace", name_buffer);
460     input.oper = *cur_op;
461     input.index = 0;
462     TF_AddInput(op_desc, input);
463     TF_SetAttrType(op_desc, "T", TF_FLOAT);
464     TF_SetAttrInt(op_desc, "block_size", params->block_size);
465     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
466     if (TF_GetCode(tf_model->status) != TF_OK){
467         av_log(ctx, AV_LOG_ERROR, "Failed to add depth_to_space to layer %d\n", layer);
468         return DNN_ERROR;
469     }
470
471     return DNN_SUCCESS;
472 }
473
474 static DNNReturnType add_pad_layer(TFModel *tf_model, TF_Operation **cur_op,
475                                               LayerPadParams *params, const int layer)
476 {
477     TFContext *ctx = &tf_model->ctx;
478     TF_Operation *op;
479     TF_Tensor *tensor;
480     TF_OperationDescription *op_desc;
481     TF_Output input;
482     int32_t *pads;
483     int64_t pads_shape[] = {4, 2};
484
485     char name_buffer[NAME_BUFFER_SIZE];
486     snprintf(name_buffer, NAME_BUFFER_SIZE, "pad%d", layer);
487
488     op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer);
489     TF_SetAttrType(op_desc, "dtype", TF_INT32);
490     tensor = TF_AllocateTensor(TF_INT32, pads_shape, 2, 4 * 2 * sizeof(int32_t));
491     pads = (int32_t *)TF_TensorData(tensor);
492     pads[0] = params->paddings[0][0];
493     pads[1] = params->paddings[0][1];
494     pads[2] = params->paddings[1][0];
495     pads[3] = params->paddings[1][1];
496     pads[4] = params->paddings[2][0];
497     pads[5] = params->paddings[2][1];
498     pads[6] = params->paddings[3][0];
499     pads[7] = params->paddings[3][1];
500     TF_SetAttrTensor(op_desc, "value", tensor, tf_model->status);
501     if (TF_GetCode(tf_model->status) != TF_OK){
502         av_log(ctx, AV_LOG_ERROR, "Failed to set value for pad of layer %d\n", layer);
503         return DNN_ERROR;
504     }
505     op = TF_FinishOperation(op_desc, tf_model->status);
506     if (TF_GetCode(tf_model->status) != TF_OK){
507         av_log(ctx, AV_LOG_ERROR, "Failed to add pad to layer %d\n", layer);
508         return DNN_ERROR;
509     }
510
511     op_desc = TF_NewOperation(tf_model->graph, "MirrorPad", "mirror_pad");
512     input.oper = *cur_op;
513     input.index = 0;
514     TF_AddInput(op_desc, input);
515     input.oper = op;
516     TF_AddInput(op_desc, input);
517     TF_SetAttrType(op_desc, "T", TF_FLOAT);
518     TF_SetAttrType(op_desc, "Tpaddings", TF_INT32);
519     TF_SetAttrString(op_desc, "mode", "SYMMETRIC", 9);
520     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
521     if (TF_GetCode(tf_model->status) != TF_OK){
522         av_log(ctx, AV_LOG_ERROR, "Failed to add mirror_pad to layer %d\n", layer);
523         return DNN_ERROR;
524     }
525
526     return DNN_SUCCESS;
527 }
528
529 static DNNReturnType add_maximum_layer(TFModel *tf_model, TF_Operation **cur_op,
530                                        DnnLayerMaximumParams *params, const int layer)
531 {
532     TFContext *ctx = &tf_model->ctx;
533     TF_Operation *op;
534     TF_Tensor *tensor;
535     TF_OperationDescription *op_desc;
536     TF_Output input;
537     float *y;
538
539     char name_buffer[NAME_BUFFER_SIZE];
540     snprintf(name_buffer, NAME_BUFFER_SIZE, "maximum/y%d", layer);
541
542     op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer);
543     TF_SetAttrType(op_desc, "dtype", TF_FLOAT);
544     tensor = TF_AllocateTensor(TF_FLOAT, NULL, 0, TF_DataTypeSize(TF_FLOAT));
545     y = (float *)TF_TensorData(tensor);
546     *y = params->val.y;
547     TF_SetAttrTensor(op_desc, "value", tensor, tf_model->status);
548     if (TF_GetCode(tf_model->status) != TF_OK){
549         av_log(ctx, AV_LOG_ERROR, "Failed to set value for maximum/y of layer %d", layer);
550         return DNN_ERROR;
551     }
552     op = TF_FinishOperation(op_desc, tf_model->status);
553     if (TF_GetCode(tf_model->status) != TF_OK){
554         av_log(ctx, AV_LOG_ERROR, "Failed to add maximum/y to layer %d\n", layer);
555         return DNN_ERROR;
556     }
557
558     snprintf(name_buffer, NAME_BUFFER_SIZE, "maximum%d", layer);
559     op_desc = TF_NewOperation(tf_model->graph, "Maximum", name_buffer);
560     input.oper = *cur_op;
561     input.index = 0;
562     TF_AddInput(op_desc, input);
563     input.oper = op;
564     TF_AddInput(op_desc, input);
565     TF_SetAttrType(op_desc, "T", TF_FLOAT);
566     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
567     if (TF_GetCode(tf_model->status) != TF_OK){
568         av_log(ctx, AV_LOG_ERROR, "Failed to add maximum to layer %d\n", layer);
569         return DNN_ERROR;
570     }
571
572     return DNN_SUCCESS;
573 }
574
575 static DNNReturnType load_native_model(TFModel *tf_model, const char *model_filename)
576 {
577     TFContext *ctx = &tf_model->ctx;
578     int32_t layer;
579     TF_OperationDescription *op_desc;
580     TF_Operation *op;
581     TF_Operation *transpose_op;
582     TF_Tensor *tensor;
583     TF_Output input;
584     int32_t *transpose_perm;
585     int64_t transpose_perm_shape[] = {4};
586     int64_t input_shape[] = {1, -1, -1, -1};
587     DNNReturnType layer_add_res;
588     DNNModel *model = NULL;
589     NativeModel *native_model;
590
591     model = ff_dnn_load_model_native(model_filename, DFT_PROCESS_FRAME, NULL, NULL);
592     if (!model){
593         av_log(ctx, AV_LOG_ERROR, "Failed to load native model\n");
594         return DNN_ERROR;
595     }
596
597     native_model = model->model;
598     tf_model->graph = TF_NewGraph();
599     tf_model->status = TF_NewStatus();
600
601 #define CLEANUP_ON_ERROR(tf_model) \
602     { \
603         TF_DeleteGraph(tf_model->graph); \
604         TF_DeleteStatus(tf_model->status); \
605         av_log(ctx, AV_LOG_ERROR, "Failed to set value or add operator to layer\n"); \
606         return DNN_ERROR; \
607     }
608
609     op_desc = TF_NewOperation(tf_model->graph, "Placeholder", "x");
610     TF_SetAttrType(op_desc, "dtype", TF_FLOAT);
611     TF_SetAttrShape(op_desc, "shape", input_shape, 4);
612     op = TF_FinishOperation(op_desc, tf_model->status);
613     if (TF_GetCode(tf_model->status) != TF_OK){
614         CLEANUP_ON_ERROR(tf_model);
615     }
616
617     op_desc = TF_NewOperation(tf_model->graph, "Const", "transpose_perm");
618     TF_SetAttrType(op_desc, "dtype", TF_INT32);
619     tensor = TF_AllocateTensor(TF_INT32, transpose_perm_shape, 1, 4 * sizeof(int32_t));
620     transpose_perm = (int32_t *)TF_TensorData(tensor);
621     transpose_perm[0] = 1;
622     transpose_perm[1] = 2;
623     transpose_perm[2] = 3;
624     transpose_perm[3] = 0;
625     TF_SetAttrTensor(op_desc, "value", tensor, tf_model->status);
626     if (TF_GetCode(tf_model->status) != TF_OK){
627         CLEANUP_ON_ERROR(tf_model);
628     }
629     transpose_op = TF_FinishOperation(op_desc, tf_model->status);
630
631     for (layer = 0; layer < native_model->layers_num; ++layer){
632         switch (native_model->layers[layer].type){
633         case DLT_INPUT:
634             layer_add_res = DNN_SUCCESS;
635             break;
636         case DLT_CONV2D:
637             layer_add_res = add_conv_layer(tf_model, transpose_op, &op,
638                                            (ConvolutionalParams *)native_model->layers[layer].params, layer);
639             break;
640         case DLT_DEPTH_TO_SPACE:
641             layer_add_res = add_depth_to_space_layer(tf_model, &op,
642                                                      (DepthToSpaceParams *)native_model->layers[layer].params, layer);
643             break;
644         case DLT_MIRROR_PAD:
645             layer_add_res = add_pad_layer(tf_model, &op,
646                                           (LayerPadParams *)native_model->layers[layer].params, layer);
647             break;
648         case DLT_MAXIMUM:
649             layer_add_res = add_maximum_layer(tf_model, &op,
650                                           (DnnLayerMaximumParams *)native_model->layers[layer].params, layer);
651             break;
652         default:
653             CLEANUP_ON_ERROR(tf_model);
654         }
655
656         if (layer_add_res != DNN_SUCCESS){
657             CLEANUP_ON_ERROR(tf_model);
658         }
659     }
660
661     op_desc = TF_NewOperation(tf_model->graph, "Identity", "y");
662     input.oper = op;
663     input.index = 0;
664     TF_AddInput(op_desc, input);
665     TF_FinishOperation(op_desc, tf_model->status);
666     if (TF_GetCode(tf_model->status) != TF_OK){
667         CLEANUP_ON_ERROR(tf_model);
668     }
669
670     ff_dnn_free_model_native(&model);
671
672     return DNN_SUCCESS;
673 }
674
675 DNNModel *ff_dnn_load_model_tf(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx)
676 {
677     DNNModel *model = NULL;
678     TFModel *tf_model = NULL;
679
680     model = av_mallocz(sizeof(DNNModel));
681     if (!model){
682         return NULL;
683     }
684
685     tf_model = av_mallocz(sizeof(TFModel));
686     if (!tf_model){
687         av_freep(&model);
688         return NULL;
689     }
690     tf_model->ctx.class = &dnn_tensorflow_class;
691     tf_model->model = model;
692
693     //parse options
694     av_opt_set_defaults(&tf_model->ctx);
695     if (av_opt_set_from_string(&tf_model->ctx, options, NULL, "=", "&") < 0) {
696         av_log(&tf_model->ctx, AV_LOG_ERROR, "Failed to parse options \"%s\"\n", options);
697         av_freep(&tf_model);
698         av_freep(&model);
699         return NULL;
700     }
701
702     if (load_tf_model(tf_model, model_filename) != DNN_SUCCESS){
703         if (load_native_model(tf_model, model_filename) != DNN_SUCCESS){
704             av_freep(&tf_model);
705             av_freep(&model);
706
707             return NULL;
708         }
709     }
710
711     model->model = tf_model;
712     model->get_input = &get_input_tf;
713     model->get_output = &get_output_tf;
714     model->options = options;
715     model->filter_ctx = filter_ctx;
716     model->func_type = func_type;
717
718     return model;
719 }
720
721 static DNNReturnType execute_model_tf(const DNNModel *model, const char *input_name, AVFrame *in_frame,
722                                       const char **output_names, uint32_t nb_output, AVFrame *out_frame,
723                                       int do_ioproc)
724 {
725     TF_Output *tf_outputs;
726     TFModel *tf_model = model->model;
727     TFContext *ctx = &tf_model->ctx;
728     DNNData input, output;
729     TF_Tensor **output_tensors;
730     TF_Output tf_input;
731     TF_Tensor *input_tensor;
732
733     if (get_input_tf(tf_model, &input, input_name) != DNN_SUCCESS)
734         return DNN_ERROR;
735     input.height = in_frame->height;
736     input.width = in_frame->width;
737
738     tf_input.oper = TF_GraphOperationByName(tf_model->graph, input_name);
739     if (!tf_input.oper){
740         av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model\n", input_name);
741         return DNN_ERROR;
742     }
743     tf_input.index = 0;
744     input_tensor = allocate_input_tensor(&input);
745     if (!input_tensor){
746         av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for input tensor\n");
747         return DNN_ERROR;
748     }
749     input.data = (float *)TF_TensorData(input_tensor);
750
751     if (do_ioproc) {
752         if (tf_model->model->pre_proc != NULL) {
753             tf_model->model->pre_proc(in_frame, &input, tf_model->model->filter_ctx);
754         } else {
755             ff_proc_from_frame_to_dnn(in_frame, &input, tf_model->model->func_type, ctx);
756         }
757     }
758
759     if (nb_output != 1) {
760         // currently, the filter does not need multiple outputs,
761         // so we just pending the support until we really need it.
762         avpriv_report_missing_feature(ctx, "multiple outputs");
763         return DNN_ERROR;
764     }
765
766     tf_outputs = av_malloc_array(nb_output, sizeof(*tf_outputs));
767     if (tf_outputs == NULL) {
768         av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for *tf_outputs\n"); \
769         return DNN_ERROR;
770     }
771
772     output_tensors = av_mallocz_array(nb_output, sizeof(*output_tensors));
773     if (!output_tensors) {
774         av_freep(&tf_outputs);
775         av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for output tensor\n"); \
776         return DNN_ERROR;
777     }
778
779     for (int i = 0; i < nb_output; ++i) {
780         tf_outputs[i].oper = TF_GraphOperationByName(tf_model->graph, output_names[i]);
781         if (!tf_outputs[i].oper) {
782             av_freep(&tf_outputs);
783             av_freep(&output_tensors);
784             av_log(ctx, AV_LOG_ERROR, "Could not find output \"%s\" in model\n", output_names[i]); \
785             return DNN_ERROR;
786         }
787         tf_outputs[i].index = 0;
788     }
789
790     TF_SessionRun(tf_model->session, NULL,
791                   &tf_input, &input_tensor, 1,
792                   tf_outputs, output_tensors, nb_output,
793                   NULL, 0, NULL, tf_model->status);
794     if (TF_GetCode(tf_model->status) != TF_OK) {
795         av_freep(&tf_outputs);
796         av_freep(&output_tensors);
797         av_log(ctx, AV_LOG_ERROR, "Failed to run session when executing model\n");
798         return DNN_ERROR;
799     }
800
801     for (uint32_t i = 0; i < nb_output; ++i) {
802         output.height = TF_Dim(output_tensors[i], 1);
803         output.width = TF_Dim(output_tensors[i], 2);
804         output.channels = TF_Dim(output_tensors[i], 3);
805         output.data = TF_TensorData(output_tensors[i]);
806         output.dt = TF_TensorType(output_tensors[i]);
807
808         if (do_ioproc) {
809             if (tf_model->model->post_proc != NULL) {
810                 tf_model->model->post_proc(out_frame, &output, tf_model->model->filter_ctx);
811             } else {
812                 ff_proc_from_dnn_to_frame(out_frame, &output, ctx);
813             }
814         } else {
815             out_frame->width = output.width;
816             out_frame->height = output.height;
817         }
818     }
819
820     for (uint32_t i = 0; i < nb_output; ++i) {
821         if (output_tensors[i]) {
822             TF_DeleteTensor(output_tensors[i]);
823         }
824     }
825     TF_DeleteTensor(input_tensor);
826     av_freep(&output_tensors);
827     av_freep(&tf_outputs);
828     return DNN_SUCCESS;
829 }
830
831 DNNReturnType ff_dnn_execute_model_tf(const DNNModel *model, const char *input_name, AVFrame *in_frame,
832                                       const char **output_names, uint32_t nb_output, AVFrame *out_frame)
833 {
834     TFModel *tf_model = model->model;
835     TFContext *ctx = &tf_model->ctx;
836
837     if (!in_frame) {
838         av_log(ctx, AV_LOG_ERROR, "in frame is NULL when execute model.\n");
839         return DNN_ERROR;
840     }
841
842     if (!out_frame) {
843         av_log(ctx, AV_LOG_ERROR, "out frame is NULL when execute model.\n");
844         return DNN_ERROR;
845     }
846
847     return execute_model_tf(model, input_name, in_frame, output_names, nb_output, out_frame, 1);
848 }
849
850 void ff_dnn_free_model_tf(DNNModel **model)
851 {
852     TFModel *tf_model;
853
854     if (*model){
855         tf_model = (*model)->model;
856         if (tf_model->graph){
857             TF_DeleteGraph(tf_model->graph);
858         }
859         if (tf_model->session){
860             TF_CloseSession(tf_model->session, tf_model->status);
861             TF_DeleteSession(tf_model->session, tf_model->status);
862         }
863         if (tf_model->status){
864             TF_DeleteStatus(tf_model->status);
865         }
866         av_freep(&tf_model);
867         av_freep(model);
868     }
869 }