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
35 #include "../internal.h"
41 #include <tensorflow/c/c_api.h>
42
48
53
64
65 /**
66 * Stores execution parameters for single
67 * call to the TensorFlow C API
68 */
75
82
83 #define OFFSET(x) offsetof(TFContext, x)
84 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM
89 };
90
92
96
98 {
100 }
101
102 /**
103 * Free the contents of TensorFlow inference request.
104 * It does not free the TFInferRequest instance.
105 *
106 * @param request pointer to TFInferRequest instance.
107 * NULL pointer is allowed.
108 */
110 {
111 if (!request)
112 return;
116 }
121 for (uint32_t
i = 0;
i < nb_output; ++
i) {
125 }
126 }
128 }
129 }
130
131 /**
132 * Create a TensorFlow inference request. All properties
133 * are initially unallocated and set as NULL.
134 *
135 * @return pointer to the allocated TFInferRequest instance.
136 */
138 {
140 if (!infer_request) {
142 }
147 return infer_request;
148 }
149
150 /**
151 * Start synchronous inference for the TensorFlow model.
152 *
153 * @param request pointer to the TFRequestItem for inference
154 * @retval 0 if execution is successful
155 * @retval AVERROR(EINVAL) if request is NULL
156 * @retval DNN_GENERIC_ERROR if execution fails
157 */
159 {
165
166 if (!request) {
169 }
170
176 if (TF_GetCode(request->
status) != TF_OK) {
181 }
183 }
184 return 0;
185 }
186
187 /**
188 * Free the TFRequestItem completely.
189 *
190 * @param arg Address of the TFInferRequest instance.
191 */
195 return;
196 }
201 TF_DeleteStatus(request->
status);
204 }
205
207 {
211 if (!lltask) {
214 }
222 }
223 return 0;
224 }
225
227 {
228 TF_Buffer *graph_buf;
229 unsigned char *graph_data =
NULL;
231 long size, bytes_read;
232
235 }
236
238
240 if (!graph_data){
243 }
244 bytes_read =
avio_read(model_file_context, graph_data,
size);
246 if (bytes_read !=
size){
249 }
250
251 graph_buf = TF_NewBuffer();
252 graph_buf->data = graph_data;
253 graph_buf->length =
size;
255
256 return graph_buf;
257 }
258
260 {
261 TF_DataType dt;
266 dt = TF_FLOAT;
268 break;
270 dt = TF_UINT8;
272 break;
273 default:
275 }
276
277 return TF_AllocateTensor(dt, input_dims, 4,
278 input_dims[1] * input_dims[2] * input_dims[3] *
size);
279 }
280
282 {
286 int64_t dims[4];
287
288 TF_Output tf_output;
289 tf_output.oper = TF_GraphOperationByName(tf_model->
graph, input_name);
290 if (!tf_output.oper) {
293 }
294
295 tf_output.index = 0;
296 input->dt = TF_OperationOutputType(tf_output);
298
300 TF_GraphGetTensorShape(tf_model->
graph, tf_output, dims, 4,
status);
301 if (TF_GetCode(
status) != TF_OK){
305 }
307
308 // currently only NHWC is supported
312 input->channels = dims[3];
313
314 return 0;
315 }
316
317 static int get_output_tf(
void *model,
const char *input_name,
int input_width,
int input_height,
318 const char *output_name, int *output_width, int *output_height)
319 {
327 .output_names = &output_name,
328 .nb_output = 1,
331 };
332
335 goto err;
336 }
337
341 goto err;
342 }
343
345 if (!request) {
348 goto err;
349 }
350
354
355 err:
359 }
360
361 #define SPACE_CHARS " \t\r\n"
363 {
365
367 v = 1;
368 for (;;) {
370 if (*p == '0円')
371 break;
373 if (
c >=
'0' &&
c <=
'9')
375 else if (
c >=
'A' &&
c <=
'F')
377 else
378 break;
380 if (v & 0x100) {
383 }
385 v = 1;
386 }
387 }
389 }
390
392 {
394 TF_Buffer *graph_def;
395 TF_ImportGraphDefOptions *graph_opts;
396 TF_SessionOptions *sess_opts;
397 const TF_Operation *init_op;
398 uint8_t *sess_config =
NULL;
399 int sess_config_length = 0;
400
401 // prepare the sess config data
404 /*
405 tf_model->ctx.options.sess_config is hex to present the serialized proto
406 required by TF_SetConfig below, so we need to first generate the serialized
407 proto in a python script, tools/python/tf_sess_config.py is a script example
408 to generate the configs of sess_config.
409 */
413 }
416
418 if (!sess_config) {
421 }
425 }
426 }
427
429 if (!graph_def){
433 }
434 tf_model->
graph = TF_NewGraph();
435 tf_model->
status = TF_NewStatus();
436 graph_opts = TF_NewImportGraphDefOptions();
437 TF_GraphImportGraphDef(tf_model->
graph, graph_def, graph_opts, tf_model->
status);
438 TF_DeleteImportGraphDefOptions(graph_opts);
439 TF_DeleteBuffer(graph_def);
440 if (TF_GetCode(tf_model->
status) != TF_OK){
441 TF_DeleteGraph(tf_model->
graph);
442 TF_DeleteStatus(tf_model->
status);
446 }
447
448 init_op = TF_GraphOperationByName(tf_model->
graph,
"init");
449 sess_opts = TF_NewSessionOptions();
450
451 if (sess_config) {
452 TF_SetConfig(sess_opts, sess_config, sess_config_length,tf_model->
status);
454 if (TF_GetCode(tf_model->
status) != TF_OK) {
455 TF_DeleteGraph(tf_model->
graph);
456 TF_DeleteStatus(tf_model->
status);
457 TF_DeleteSessionOptions(sess_opts);
461 }
462 }
463
465 TF_DeleteSessionOptions(sess_opts);
466 if (TF_GetCode(tf_model->
status) != TF_OK)
467 {
468 TF_DeleteGraph(tf_model->
graph);
469 TF_DeleteStatus(tf_model->
status);
472 }
473
474 // Run initialization operation with name "init" if it is present in graph
475 if (init_op){
480 if (TF_GetCode(tf_model->
status) != TF_OK)
481 {
483 TF_DeleteGraph(tf_model->
graph);
484 TF_DeleteStatus(tf_model->
status);
487 }
488 }
489
490 return 0;
491 }
492
493 #define NAME_BUFFER_SIZE 256
494
497 {
500 TF_OperationDescription *op_desc;
502 int64_t strides[] = {1, 1, 1, 1};
503 TF_Tensor *kernel_tensor =
NULL, *biases_tensor =
NULL;
504 int64_t dims[4];
505 int dims_len;
508
511
513 op_desc = TF_NewOperation(tf_model->
graph,
"Const", name_buffer);
514 TF_SetAttrType(op_desc, "dtype", TF_FLOAT);
519 dims_len = 4;
520 kernel_tensor = TF_AllocateTensor(TF_FLOAT, dims, dims_len,
size *
sizeof(
float));
521 memcpy(TF_TensorData(kernel_tensor), params->
kernel,
size *
sizeof(
float));
522 TF_SetAttrTensor(op_desc,
"value", kernel_tensor, tf_model->
status);
523 if (TF_GetCode(tf_model->
status) != TF_OK){
524 goto err;
525 }
526 op = TF_FinishOperation(op_desc, tf_model->
status);
527 if (TF_GetCode(tf_model->
status) != TF_OK){
528 goto err;
529 }
530
532 op_desc = TF_NewOperation(tf_model->
graph,
"Transpose", name_buffer);
534 TF_AddInput(op_desc,
input);
535 input.oper = transpose_op;
536 TF_AddInput(op_desc,
input);
537 TF_SetAttrType(op_desc, "T", TF_FLOAT);
538 TF_SetAttrType(op_desc, "Tperm", TF_INT32);
539 op = TF_FinishOperation(op_desc, tf_model->
status);
540 if (TF_GetCode(tf_model->
status) != TF_OK){
541 goto err;
542 }
543
545 op_desc = TF_NewOperation(tf_model->
graph,
"Conv2D", name_buffer);
546 input.oper = *cur_op;
547 TF_AddInput(op_desc,
input);
549 TF_AddInput(op_desc,
input);
550 TF_SetAttrType(op_desc, "T", TF_FLOAT);
551 TF_SetAttrIntList(op_desc, "strides", strides, 4);
552 TF_SetAttrString(op_desc, "padding", "VALID", 5);
553 *cur_op = TF_FinishOperation(op_desc, tf_model->
status);
554 if (TF_GetCode(tf_model->
status) != TF_OK){
555 goto err;
556 }
557
559 op_desc = TF_NewOperation(tf_model->
graph,
"Const", name_buffer);
560 TF_SetAttrType(op_desc, "dtype", TF_FLOAT);
562 dims_len = 1;
563 biases_tensor = TF_AllocateTensor(TF_FLOAT, dims, dims_len, params->
output_num *
sizeof(
float));
564 memcpy(TF_TensorData(biases_tensor), params->
biases, params->
output_num *
sizeof(
float));
565 TF_SetAttrTensor(op_desc,
"value", biases_tensor, tf_model->
status);
566 if (TF_GetCode(tf_model->
status) != TF_OK){
567 goto err;
568 }
569 op = TF_FinishOperation(op_desc, tf_model->
status);
570 if (TF_GetCode(tf_model->
status) != TF_OK){
571 goto err;
572 }
573
575 op_desc = TF_NewOperation(tf_model->
graph,
"BiasAdd", name_buffer);
576 input.oper = *cur_op;
577 TF_AddInput(op_desc,
input);
579 TF_AddInput(op_desc,
input);
580 TF_SetAttrType(op_desc, "T", TF_FLOAT);
581 *cur_op = TF_FinishOperation(op_desc, tf_model->
status);
582 if (TF_GetCode(tf_model->
status) != TF_OK){
583 goto err;
584 }
585
589 op_desc = TF_NewOperation(tf_model->
graph,
"Relu", name_buffer);
590 break;
592 op_desc = TF_NewOperation(tf_model->
graph,
"Tanh", name_buffer);
593 break;
595 op_desc = TF_NewOperation(tf_model->
graph,
"Sigmoid", name_buffer);
596 break;
597 default:
600 }
601 input.oper = *cur_op;
602 TF_AddInput(op_desc,
input);
603 TF_SetAttrType(op_desc, "T", TF_FLOAT);
604 *cur_op = TF_FinishOperation(op_desc, tf_model->
status);
605 if (TF_GetCode(tf_model->
status) != TF_OK){
606 goto err;
607 }
608
609 return 0;
610 err:
611 TF_DeleteTensor(kernel_tensor);
612 TF_DeleteTensor(biases_tensor);
615 }
616
619 {
621 TF_OperationDescription *op_desc;
624
626 op_desc = TF_NewOperation(tf_model->
graph,
"DepthToSpace", name_buffer);
627 input.oper = *cur_op;
629 TF_AddInput(op_desc,
input);
630 TF_SetAttrType(op_desc, "T", TF_FLOAT);
631 TF_SetAttrInt(op_desc,
"block_size", params->
block_size);
632 *cur_op = TF_FinishOperation(op_desc, tf_model->
status);
633 if (TF_GetCode(tf_model->
status) != TF_OK){
636 }
637
638 return 0;
639 }
640
643 {
646 TF_Tensor *tensor;
647 TF_OperationDescription *op_desc;
650 int64_t pads_shape[] = {4, 2};
651
654
655 op_desc = TF_NewOperation(tf_model->
graph,
"Const", name_buffer);
656 TF_SetAttrType(op_desc, "dtype", TF_INT32);
657 tensor = TF_AllocateTensor(TF_INT32, pads_shape, 2, 4 * 2 *
sizeof(
int32_t));
658 pads = (
int32_t *)TF_TensorData(tensor);
667 TF_SetAttrTensor(op_desc,
"value", tensor, tf_model->
status);
668 if (TF_GetCode(tf_model->
status) != TF_OK){
669 TF_DeleteTensor(tensor);
672 }
673 op = TF_FinishOperation(op_desc, tf_model->
status);
674 if (TF_GetCode(tf_model->
status) != TF_OK){
675 TF_DeleteTensor(tensor);
678 }
679
680 op_desc = TF_NewOperation(tf_model->
graph,
"MirrorPad",
"mirror_pad");
681 input.oper = *cur_op;
683 TF_AddInput(op_desc,
input);
685 TF_AddInput(op_desc,
input);
686 TF_SetAttrType(op_desc, "T", TF_FLOAT);
687 TF_SetAttrType(op_desc, "Tpaddings", TF_INT32);
688 TF_SetAttrString(op_desc, "mode", "SYMMETRIC", 9);
689 *cur_op = TF_FinishOperation(op_desc, tf_model->
status);
690 if (TF_GetCode(tf_model->
status) != TF_OK){
691 TF_DeleteTensor(tensor);
694 }
695
696 return 0;
697 }
698
701 {
704 TF_Tensor *tensor;
705 TF_OperationDescription *op_desc;
707 float *y;
708
711
712 op_desc = TF_NewOperation(tf_model->
graph,
"Const", name_buffer);
713 TF_SetAttrType(op_desc, "dtype", TF_FLOAT);
714 tensor = TF_AllocateTensor(TF_FLOAT,
NULL, 0, TF_DataTypeSize(TF_FLOAT));
715 y = (float *)TF_TensorData(tensor);
717 TF_SetAttrTensor(op_desc,
"value", tensor, tf_model->
status);
718 if (TF_GetCode(tf_model->
status) != TF_OK){
719 TF_DeleteTensor(tensor);
722 }
723 op = TF_FinishOperation(op_desc, tf_model->
status);
724 if (TF_GetCode(tf_model->
status) != TF_OK){
725 TF_DeleteTensor(tensor);
728 }
729
731 op_desc = TF_NewOperation(tf_model->
graph,
"Maximum", name_buffer);
732 input.oper = *cur_op;
734 TF_AddInput(op_desc,
input);
736 TF_AddInput(op_desc,
input);
737 TF_SetAttrType(op_desc, "T", TF_FLOAT);
738 *cur_op = TF_FinishOperation(op_desc, tf_model->
status);
739 if (TF_GetCode(tf_model->
status) != TF_OK){
740 TF_DeleteTensor(tensor);
743 }
744
745 return 0;
746 }
747
749 {
752 TF_OperationDescription *op_desc;
754 TF_Operation *transpose_op;
755 TF_Tensor *tensor =
NULL;
758 int64_t transpose_perm_shape[] = {4};
759 int64_t input_shape[] = {1, -1, -1, -1};
760 int layer_add_res;
763
765 if (!model){
768 }
769
770 native_model = model->
model;
771 tf_model->
graph = TF_NewGraph();
772 tf_model->
status = TF_NewStatus();
773
774 #define CLEANUP_ON_ERROR(tf_model) \
775 { \
776 TF_DeleteTensor(tensor); \
777 TF_DeleteGraph(tf_model->graph); \
778 TF_DeleteStatus(tf_model->status); \
779 av_log(ctx, AV_LOG_ERROR, "Failed to set value or add operator to layer\n"); \
780 return DNN_GENERIC_ERROR; \
781 }
782
783 op_desc = TF_NewOperation(tf_model->
graph,
"Placeholder",
"x");
784 TF_SetAttrType(op_desc, "dtype", TF_FLOAT);
785 TF_SetAttrShape(op_desc, "shape", input_shape, 4);
786 op = TF_FinishOperation(op_desc, tf_model->
status);
787 if (TF_GetCode(tf_model->
status) != TF_OK){
789 }
790
791 op_desc = TF_NewOperation(tf_model->
graph,
"Const",
"transpose_perm");
792 TF_SetAttrType(op_desc, "dtype", TF_INT32);
793 tensor = TF_AllocateTensor(TF_INT32, transpose_perm_shape, 1, 4 *
sizeof(
int32_t));
799 TF_SetAttrTensor(op_desc,
"value", tensor, tf_model->
status);
800 if (TF_GetCode(tf_model->
status) != TF_OK){
802 }
803 transpose_op = TF_FinishOperation(op_desc, tf_model->
status);
804 if (TF_GetCode(tf_model->
status) != TF_OK){
806 }
807
808 for (layer = 0; layer < native_model->
layers_num; ++layer){
811 layer_add_res = 0;
812 break;
816 break;
820 break;
824 break;
828 break;
829 default:
831 }
832
833 if (layer_add_res != 0){
835 }
836 }
837
838 op_desc = TF_NewOperation(tf_model->
graph,
"Identity",
"y");
841 TF_AddInput(op_desc,
input);
842 TF_FinishOperation(op_desc, tf_model->
status);
843 if (TF_GetCode(tf_model->
status) != TF_OK){
845 }
846
848
849 return 0;
850 }
851
853 {
857
859 if (!model){
861 }
862
864 if (!tf_model){
867 }
868 tf_model->
model = model;
870 ctx->class = &dnn_tensorflow_class;
871
872 //parse options
876 goto err;
877 }
878
881 goto err;
882 }
883 }
884
885 if (
ctx->options.nireq <= 0) {
887 }
888
889 #if !HAVE_PTHREAD_CANCEL
890 if (
ctx->options.async) {
891 ctx->options.async = 0;
893 }
894 #endif
895
898 goto err;
899 }
900
901 for (
int i = 0;
i <
ctx->options.nireq;
i++) {
903 if (!item) {
904 goto err;
905 }
911 goto err;
912 }
913 item->
status = TF_NewStatus();
917
920 goto err;
921 }
922 }
923
926 goto err;
927 }
928
931 goto err;
932 }
933
934 model->
model = tf_model;
940
941 return model;
942 err:
945 }
946
954
959
962 goto err;
963 }
964
968
973 goto err;
974 }
975
977 if (!infer_request->
tf_input->oper){
980 goto err;
981 }
983
988 goto err;
989 }
991
997 } else {
999 }
1000 }
1001 break;
1004 break;
1005 default:
1007 break;
1008 }
1009
1014 goto err;
1015 }
1016
1021 goto err;
1022 }
1023
1030 goto err;
1031 }
1033 }
1034
1035 return 0;
1036 err:
1039 }
1040
1049
1053 goto err;
1054 }
1055
1062 }
1065 //it only support 1 output if it's frame in & frame out
1069 } else {
1071 }
1072 } else {
1075 }
1076 break;
1080 return;
1081 }
1083 break;
1084 default:
1086 goto err;
1087 }
1089 err:
1092
1096 }
1097 }
1098
1100 {
1106
1109 return 0;
1110 }
1111
1113 task = lltask->
task;
1114 tf_model = task->
model;
1116
1119 goto err;
1120 }
1121
1124 goto err;
1125 }
1126 return 0;
1127 }
1128 else {
1131 goto err;
1132 }
1135 }
1136 err:
1140 }
1142 }
1143
1145 {
1151
1155 }
1156
1158 if (!task) {
1161 }
1162
1167 }
1168
1173 }
1174
1179 }
1180
1182 if (!request) {
1185 }
1187 }
1188
1190 {
1193 }
1194
1196 {
1201
1203 // no pending task need to flush
1204 return 0;
1205 }
1206
1208 if (!request) {
1211 }
1212
1218 }
1220 }
1221
1223 }
1224
1226 {
1228
1229 if (*model){
1230 tf_model = (*model)->
model;
1234 }
1236
1240 }
1242
1248 }
1250
1251 if (tf_model->
graph){
1252 TF_DeleteGraph(tf_model->
graph);
1253 }
1257 }
1259 TF_DeleteStatus(tf_model->
status);
1260 }
1263 }
1264 }