]> git.sesse.net Git - ffmpeg/blob - libavfilter/dnn/dnn_backend_tf.c
fb799d2b7033d53cea808df1784bbb2f05f9f4d4
[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 *kernel_tensor = NULL, *biases_tensor = NULL;
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     kernel_tensor = TF_AllocateTensor(TF_FLOAT, dims, dims_len, size * sizeof(float));
351     memcpy(TF_TensorData(kernel_tensor), params->kernel, size * sizeof(float));
352     TF_SetAttrTensor(op_desc, "value", kernel_tensor, tf_model->status);
353     if (TF_GetCode(tf_model->status) != TF_OK){
354         goto err;
355     }
356     op = TF_FinishOperation(op_desc, tf_model->status);
357     if (TF_GetCode(tf_model->status) != TF_OK){
358         goto err;
359     }
360
361     snprintf(name_buffer, NAME_BUFFER_SIZE, "transpose%d", layer);
362     op_desc = TF_NewOperation(tf_model->graph, "Transpose", name_buffer);
363     input.oper = op;
364     TF_AddInput(op_desc, input);
365     input.oper = transpose_op;
366     TF_AddInput(op_desc, input);
367     TF_SetAttrType(op_desc, "T", TF_FLOAT);
368     TF_SetAttrType(op_desc, "Tperm", TF_INT32);
369     op = TF_FinishOperation(op_desc, tf_model->status);
370     if (TF_GetCode(tf_model->status) != TF_OK){
371         goto err;
372     }
373
374     snprintf(name_buffer, NAME_BUFFER_SIZE, "conv2d%d", layer);
375     op_desc = TF_NewOperation(tf_model->graph, "Conv2D", name_buffer);
376     input.oper = *cur_op;
377     TF_AddInput(op_desc, input);
378     input.oper = op;
379     TF_AddInput(op_desc, input);
380     TF_SetAttrType(op_desc, "T", TF_FLOAT);
381     TF_SetAttrIntList(op_desc, "strides", strides, 4);
382     TF_SetAttrString(op_desc, "padding", "VALID", 5);
383     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
384     if (TF_GetCode(tf_model->status) != TF_OK){
385         goto err;
386     }
387
388     snprintf(name_buffer, NAME_BUFFER_SIZE, "conv_biases%d", layer);
389     op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer);
390     TF_SetAttrType(op_desc, "dtype", TF_FLOAT);
391     dims[0] = params->output_num;
392     dims_len = 1;
393     biases_tensor = TF_AllocateTensor(TF_FLOAT, dims, dims_len, params->output_num * sizeof(float));
394     memcpy(TF_TensorData(biases_tensor), params->biases, params->output_num * sizeof(float));
395     TF_SetAttrTensor(op_desc, "value", biases_tensor, tf_model->status);
396     if (TF_GetCode(tf_model->status) != TF_OK){
397         goto err;
398     }
399     op = TF_FinishOperation(op_desc, tf_model->status);
400     if (TF_GetCode(tf_model->status) != TF_OK){
401         goto err;
402     }
403
404     snprintf(name_buffer, NAME_BUFFER_SIZE, "bias_add%d", layer);
405     op_desc = TF_NewOperation(tf_model->graph, "BiasAdd", name_buffer);
406     input.oper = *cur_op;
407     TF_AddInput(op_desc, input);
408     input.oper = op;
409     TF_AddInput(op_desc, input);
410     TF_SetAttrType(op_desc, "T", TF_FLOAT);
411     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
412     if (TF_GetCode(tf_model->status) != TF_OK){
413         goto err;
414     }
415
416     snprintf(name_buffer, NAME_BUFFER_SIZE, "activation%d", layer);
417     switch (params->activation){
418     case RELU:
419         op_desc = TF_NewOperation(tf_model->graph, "Relu", name_buffer);
420         break;
421     case TANH:
422         op_desc = TF_NewOperation(tf_model->graph, "Tanh", name_buffer);
423         break;
424     case SIGMOID:
425         op_desc = TF_NewOperation(tf_model->graph, "Sigmoid", name_buffer);
426         break;
427     default:
428         avpriv_report_missing_feature(ctx, "convolutional activation function %d", params->activation);
429         return DNN_ERROR;
430     }
431     input.oper = *cur_op;
432     TF_AddInput(op_desc, input);
433     TF_SetAttrType(op_desc, "T", TF_FLOAT);
434     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
435     if (TF_GetCode(tf_model->status) != TF_OK){
436         goto err;
437     }
438
439     return DNN_SUCCESS;
440 err:
441     TF_DeleteTensor(kernel_tensor);
442     TF_DeleteTensor(biases_tensor);
443     av_log(ctx, AV_LOG_ERROR, "Failed to add conv layer %d\n", layer);
444     return DNN_ERROR;
445 }
446
447 static DNNReturnType add_depth_to_space_layer(TFModel *tf_model, TF_Operation **cur_op,
448                                               DepthToSpaceParams *params, const int layer)
449 {
450     TFContext *ctx = &tf_model->ctx;
451     TF_OperationDescription *op_desc;
452     TF_Output input;
453     char name_buffer[NAME_BUFFER_SIZE];
454
455     snprintf(name_buffer, NAME_BUFFER_SIZE, "depth_to_space%d", layer);
456     op_desc = TF_NewOperation(tf_model->graph, "DepthToSpace", name_buffer);
457     input.oper = *cur_op;
458     input.index = 0;
459     TF_AddInput(op_desc, input);
460     TF_SetAttrType(op_desc, "T", TF_FLOAT);
461     TF_SetAttrInt(op_desc, "block_size", params->block_size);
462     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
463     if (TF_GetCode(tf_model->status) != TF_OK){
464         av_log(ctx, AV_LOG_ERROR, "Failed to add depth_to_space to layer %d\n", layer);
465         return DNN_ERROR;
466     }
467
468     return DNN_SUCCESS;
469 }
470
471 static DNNReturnType add_pad_layer(TFModel *tf_model, TF_Operation **cur_op,
472                                               LayerPadParams *params, const int layer)
473 {
474     TFContext *ctx = &tf_model->ctx;
475     TF_Operation *op;
476     TF_Tensor *tensor;
477     TF_OperationDescription *op_desc;
478     TF_Output input;
479     int32_t *pads;
480     int64_t pads_shape[] = {4, 2};
481
482     char name_buffer[NAME_BUFFER_SIZE];
483     snprintf(name_buffer, NAME_BUFFER_SIZE, "pad%d", layer);
484
485     op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer);
486     TF_SetAttrType(op_desc, "dtype", TF_INT32);
487     tensor = TF_AllocateTensor(TF_INT32, pads_shape, 2, 4 * 2 * sizeof(int32_t));
488     pads = (int32_t *)TF_TensorData(tensor);
489     pads[0] = params->paddings[0][0];
490     pads[1] = params->paddings[0][1];
491     pads[2] = params->paddings[1][0];
492     pads[3] = params->paddings[1][1];
493     pads[4] = params->paddings[2][0];
494     pads[5] = params->paddings[2][1];
495     pads[6] = params->paddings[3][0];
496     pads[7] = params->paddings[3][1];
497     TF_SetAttrTensor(op_desc, "value", tensor, tf_model->status);
498     if (TF_GetCode(tf_model->status) != TF_OK){
499         TF_DeleteTensor(tensor);
500         av_log(ctx, AV_LOG_ERROR, "Failed to set value for pad of layer %d\n", layer);
501         return DNN_ERROR;
502     }
503     op = TF_FinishOperation(op_desc, tf_model->status);
504     if (TF_GetCode(tf_model->status) != TF_OK){
505         TF_DeleteTensor(tensor);
506         av_log(ctx, AV_LOG_ERROR, "Failed to add pad to layer %d\n", layer);
507         return DNN_ERROR;
508     }
509
510     op_desc = TF_NewOperation(tf_model->graph, "MirrorPad", "mirror_pad");
511     input.oper = *cur_op;
512     input.index = 0;
513     TF_AddInput(op_desc, input);
514     input.oper = op;
515     TF_AddInput(op_desc, input);
516     TF_SetAttrType(op_desc, "T", TF_FLOAT);
517     TF_SetAttrType(op_desc, "Tpaddings", TF_INT32);
518     TF_SetAttrString(op_desc, "mode", "SYMMETRIC", 9);
519     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
520     if (TF_GetCode(tf_model->status) != TF_OK){
521         TF_DeleteTensor(tensor);
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         TF_DeleteTensor(tensor);
550         av_log(ctx, AV_LOG_ERROR, "Failed to set value for maximum/y of layer %d", layer);
551         return DNN_ERROR;
552     }
553     op = TF_FinishOperation(op_desc, tf_model->status);
554     if (TF_GetCode(tf_model->status) != TF_OK){
555         TF_DeleteTensor(tensor);
556         av_log(ctx, AV_LOG_ERROR, "Failed to add maximum/y to layer %d\n", layer);
557         return DNN_ERROR;
558     }
559
560     snprintf(name_buffer, NAME_BUFFER_SIZE, "maximum%d", layer);
561     op_desc = TF_NewOperation(tf_model->graph, "Maximum", name_buffer);
562     input.oper = *cur_op;
563     input.index = 0;
564     TF_AddInput(op_desc, input);
565     input.oper = op;
566     TF_AddInput(op_desc, input);
567     TF_SetAttrType(op_desc, "T", TF_FLOAT);
568     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
569     if (TF_GetCode(tf_model->status) != TF_OK){
570         TF_DeleteTensor(tensor);
571         av_log(ctx, AV_LOG_ERROR, "Failed to add maximum to layer %d\n", layer);
572         return DNN_ERROR;
573     }
574
575     return DNN_SUCCESS;
576 }
577
578 static DNNReturnType load_native_model(TFModel *tf_model, const char *model_filename)
579 {
580     TFContext *ctx = &tf_model->ctx;
581     int32_t layer;
582     TF_OperationDescription *op_desc;
583     TF_Operation *op;
584     TF_Operation *transpose_op;
585     TF_Tensor *tensor = NULL;
586     TF_Output input;
587     int32_t *transpose_perm;
588     int64_t transpose_perm_shape[] = {4};
589     int64_t input_shape[] = {1, -1, -1, -1};
590     DNNReturnType layer_add_res;
591     DNNModel *model = NULL;
592     NativeModel *native_model;
593
594     model = ff_dnn_load_model_native(model_filename, DFT_PROCESS_FRAME, NULL, NULL);
595     if (!model){
596         av_log(ctx, AV_LOG_ERROR, "Failed to load native model\n");
597         return DNN_ERROR;
598     }
599
600     native_model = model->model;
601     tf_model->graph = TF_NewGraph();
602     tf_model->status = TF_NewStatus();
603
604 #define CLEANUP_ON_ERROR(tf_model) \
605     { \
606         TF_DeleteTensor(tensor); \
607         TF_DeleteGraph(tf_model->graph); \
608         TF_DeleteStatus(tf_model->status); \
609         av_log(ctx, AV_LOG_ERROR, "Failed to set value or add operator to layer\n"); \
610         return DNN_ERROR; \
611     }
612
613     op_desc = TF_NewOperation(tf_model->graph, "Placeholder", "x");
614     TF_SetAttrType(op_desc, "dtype", TF_FLOAT);
615     TF_SetAttrShape(op_desc, "shape", input_shape, 4);
616     op = TF_FinishOperation(op_desc, tf_model->status);
617     if (TF_GetCode(tf_model->status) != TF_OK){
618         CLEANUP_ON_ERROR(tf_model);
619     }
620
621     op_desc = TF_NewOperation(tf_model->graph, "Const", "transpose_perm");
622     TF_SetAttrType(op_desc, "dtype", TF_INT32);
623     tensor = TF_AllocateTensor(TF_INT32, transpose_perm_shape, 1, 4 * sizeof(int32_t));
624     transpose_perm = (int32_t *)TF_TensorData(tensor);
625     transpose_perm[0] = 1;
626     transpose_perm[1] = 2;
627     transpose_perm[2] = 3;
628     transpose_perm[3] = 0;
629     TF_SetAttrTensor(op_desc, "value", tensor, tf_model->status);
630     if (TF_GetCode(tf_model->status) != TF_OK){
631         CLEANUP_ON_ERROR(tf_model);
632     }
633     transpose_op = TF_FinishOperation(op_desc, tf_model->status);
634     if (TF_GetCode(tf_model->status) != TF_OK){
635         CLEANUP_ON_ERROR(tf_model);
636     }
637
638     for (layer = 0; layer < native_model->layers_num; ++layer){
639         switch (native_model->layers[layer].type){
640         case DLT_INPUT:
641             layer_add_res = DNN_SUCCESS;
642             break;
643         case DLT_CONV2D:
644             layer_add_res = add_conv_layer(tf_model, transpose_op, &op,
645                                            (ConvolutionalParams *)native_model->layers[layer].params, layer);
646             break;
647         case DLT_DEPTH_TO_SPACE:
648             layer_add_res = add_depth_to_space_layer(tf_model, &op,
649                                                      (DepthToSpaceParams *)native_model->layers[layer].params, layer);
650             break;
651         case DLT_MIRROR_PAD:
652             layer_add_res = add_pad_layer(tf_model, &op,
653                                           (LayerPadParams *)native_model->layers[layer].params, layer);
654             break;
655         case DLT_MAXIMUM:
656             layer_add_res = add_maximum_layer(tf_model, &op,
657                                           (DnnLayerMaximumParams *)native_model->layers[layer].params, layer);
658             break;
659         default:
660             CLEANUP_ON_ERROR(tf_model);
661         }
662
663         if (layer_add_res != DNN_SUCCESS){
664             CLEANUP_ON_ERROR(tf_model);
665         }
666     }
667
668     op_desc = TF_NewOperation(tf_model->graph, "Identity", "y");
669     input.oper = op;
670     input.index = 0;
671     TF_AddInput(op_desc, input);
672     TF_FinishOperation(op_desc, tf_model->status);
673     if (TF_GetCode(tf_model->status) != TF_OK){
674         CLEANUP_ON_ERROR(tf_model);
675     }
676
677     ff_dnn_free_model_native(&model);
678
679     return DNN_SUCCESS;
680 }
681
682 DNNModel *ff_dnn_load_model_tf(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx)
683 {
684     DNNModel *model = NULL;
685     TFModel *tf_model = NULL;
686
687     model = av_mallocz(sizeof(DNNModel));
688     if (!model){
689         return NULL;
690     }
691
692     tf_model = av_mallocz(sizeof(TFModel));
693     if (!tf_model){
694         av_freep(&model);
695         return NULL;
696     }
697     tf_model->ctx.class = &dnn_tensorflow_class;
698     tf_model->model = model;
699
700     //parse options
701     av_opt_set_defaults(&tf_model->ctx);
702     if (av_opt_set_from_string(&tf_model->ctx, options, NULL, "=", "&") < 0) {
703         av_log(&tf_model->ctx, AV_LOG_ERROR, "Failed to parse options \"%s\"\n", options);
704         av_freep(&tf_model);
705         av_freep(&model);
706         return NULL;
707     }
708
709     if (load_tf_model(tf_model, model_filename) != DNN_SUCCESS){
710         if (load_native_model(tf_model, model_filename) != DNN_SUCCESS){
711             av_freep(&tf_model);
712             av_freep(&model);
713
714             return NULL;
715         }
716     }
717
718     model->model = tf_model;
719     model->get_input = &get_input_tf;
720     model->get_output = &get_output_tf;
721     model->options = options;
722     model->filter_ctx = filter_ctx;
723     model->func_type = func_type;
724
725     return model;
726 }
727
728 static DNNReturnType execute_model_tf(const DNNModel *model, const char *input_name, AVFrame *in_frame,
729                                       const char **output_names, uint32_t nb_output, AVFrame *out_frame,
730                                       int do_ioproc)
731 {
732     TF_Output *tf_outputs;
733     TFModel *tf_model = model->model;
734     TFContext *ctx = &tf_model->ctx;
735     DNNData input, output;
736     TF_Tensor **output_tensors;
737     TF_Output tf_input;
738     TF_Tensor *input_tensor;
739
740     if (get_input_tf(tf_model, &input, input_name) != DNN_SUCCESS)
741         return DNN_ERROR;
742     input.height = in_frame->height;
743     input.width = in_frame->width;
744
745     tf_input.oper = TF_GraphOperationByName(tf_model->graph, input_name);
746     if (!tf_input.oper){
747         av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model\n", input_name);
748         return DNN_ERROR;
749     }
750     tf_input.index = 0;
751     input_tensor = allocate_input_tensor(&input);
752     if (!input_tensor){
753         av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for input tensor\n");
754         return DNN_ERROR;
755     }
756     input.data = (float *)TF_TensorData(input_tensor);
757
758     if (do_ioproc) {
759         if (tf_model->model->frame_pre_proc != NULL) {
760             tf_model->model->frame_pre_proc(in_frame, &input, tf_model->model->filter_ctx);
761         } else {
762             ff_proc_from_frame_to_dnn(in_frame, &input, tf_model->model->func_type, ctx);
763         }
764     }
765
766     if (nb_output != 1) {
767         // currently, the filter does not need multiple outputs,
768         // so we just pending the support until we really need it.
769         TF_DeleteTensor(input_tensor);
770         avpriv_report_missing_feature(ctx, "multiple outputs");
771         return DNN_ERROR;
772     }
773
774     tf_outputs = av_malloc_array(nb_output, sizeof(*tf_outputs));
775     if (tf_outputs == NULL) {
776         TF_DeleteTensor(input_tensor);
777         av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for *tf_outputs\n"); \
778         return DNN_ERROR;
779     }
780
781     output_tensors = av_mallocz_array(nb_output, sizeof(*output_tensors));
782     if (!output_tensors) {
783         TF_DeleteTensor(input_tensor);
784         av_freep(&tf_outputs);
785         av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for output tensor\n"); \
786         return DNN_ERROR;
787     }
788
789     for (int i = 0; i < nb_output; ++i) {
790         tf_outputs[i].oper = TF_GraphOperationByName(tf_model->graph, output_names[i]);
791         if (!tf_outputs[i].oper) {
792             TF_DeleteTensor(input_tensor);
793             av_freep(&tf_outputs);
794             av_freep(&output_tensors);
795             av_log(ctx, AV_LOG_ERROR, "Could not find output \"%s\" in model\n", output_names[i]); \
796             return DNN_ERROR;
797         }
798         tf_outputs[i].index = 0;
799     }
800
801     TF_SessionRun(tf_model->session, NULL,
802                   &tf_input, &input_tensor, 1,
803                   tf_outputs, output_tensors, nb_output,
804                   NULL, 0, NULL, tf_model->status);
805     if (TF_GetCode(tf_model->status) != TF_OK) {
806         TF_DeleteTensor(input_tensor);
807         av_freep(&tf_outputs);
808         av_freep(&output_tensors);
809         av_log(ctx, AV_LOG_ERROR, "Failed to run session when executing model\n");
810         return DNN_ERROR;
811     }
812
813     for (uint32_t i = 0; i < nb_output; ++i) {
814         output.height = TF_Dim(output_tensors[i], 1);
815         output.width = TF_Dim(output_tensors[i], 2);
816         output.channels = TF_Dim(output_tensors[i], 3);
817         output.data = TF_TensorData(output_tensors[i]);
818         output.dt = TF_TensorType(output_tensors[i]);
819
820         if (do_ioproc) {
821             if (tf_model->model->frame_post_proc != NULL) {
822                 tf_model->model->frame_post_proc(out_frame, &output, tf_model->model->filter_ctx);
823             } else {
824                 ff_proc_from_dnn_to_frame(out_frame, &output, ctx);
825             }
826         } else {
827             out_frame->width = output.width;
828             out_frame->height = output.height;
829         }
830     }
831
832     for (uint32_t i = 0; i < nb_output; ++i) {
833         if (output_tensors[i]) {
834             TF_DeleteTensor(output_tensors[i]);
835         }
836     }
837     TF_DeleteTensor(input_tensor);
838     av_freep(&output_tensors);
839     av_freep(&tf_outputs);
840     return DNN_SUCCESS;
841 }
842
843 DNNReturnType ff_dnn_execute_model_tf(const DNNModel *model, const char *input_name, AVFrame *in_frame,
844                                       const char **output_names, uint32_t nb_output, AVFrame *out_frame)
845 {
846     TFModel *tf_model = model->model;
847     TFContext *ctx = &tf_model->ctx;
848
849     if (!in_frame) {
850         av_log(ctx, AV_LOG_ERROR, "in frame is NULL when execute model.\n");
851         return DNN_ERROR;
852     }
853
854     if (!out_frame) {
855         av_log(ctx, AV_LOG_ERROR, "out frame is NULL when execute model.\n");
856         return DNN_ERROR;
857     }
858
859     return execute_model_tf(model, input_name, in_frame, output_names, nb_output, out_frame, 1);
860 }
861
862 void ff_dnn_free_model_tf(DNNModel **model)
863 {
864     TFModel *tf_model;
865
866     if (*model){
867         tf_model = (*model)->model;
868         if (tf_model->graph){
869             TF_DeleteGraph(tf_model->graph);
870         }
871         if (tf_model->session){
872             TF_CloseSession(tf_model->session, tf_model->status);
873             TF_DeleteSession(tf_model->session, tf_model->status);
874         }
875         if (tf_model->status){
876             TF_DeleteStatus(tf_model->status);
877         }
878         av_freep(&tf_model);
879         av_freep(model);
880     }
881 }