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
32 #include "../internal.h"
36 #include <tensorflow/c/c_api.h>
37
43
48
59
60 /**
61 * Stores execution parameters for single
62 * call to the TensorFlow C API
63 */
70
77
78 #define OFFSET(x) offsetof(TFContext, x)
79 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM
84 };
85
87
91
93 {
95 }
96
97 /**
98 * Free the contents of TensorFlow inference request.
99 * It does not free the TFInferRequest instance.
100 *
101 * @param request pointer to TFInferRequest instance.
102 * NULL pointer is allowed.
103 */
105 {
106 if (!request)
107 return;
111 }
116 for (uint32_t
i = 0;
i < nb_output; ++
i) {
120 }
121 }
123 }
124 }
125
126 /**
127 * Create a TensorFlow inference request. All properties
128 * are initially unallocated and set as NULL.
129 *
130 * @return pointer to the allocated TFInferRequest instance.
131 */
133 {
135 if (!infer_request) {
137 }
142 return infer_request;
143 }
144
145 /**
146 * Start synchronous inference for the TensorFlow model.
147 *
148 * @param request pointer to the TFRequestItem for inference
149 * @retval 0 if execution is successful
150 * @retval AVERROR(EINVAL) if request is NULL
151 * @retval DNN_GENERIC_ERROR if execution fails
152 */
154 {
160
161 if (!request) {
164 }
165
171 if (TF_GetCode(request->
status) != TF_OK) {
174 }
175 return 0;
176 }
177
178 /**
179 * Free the TFRequestItem completely.
180 *
181 * @param arg Address of the TFInferRequest instance.
182 */
186 return;
187 }
192 TF_DeleteStatus(request->
status);
195 }
196
198 {
202 if (!lltask) {
205 }
213 }
214 return 0;
215 }
216
218 {
219 TF_Buffer *graph_buf;
220 unsigned char *graph_data =
NULL;
222 long size, bytes_read;
223
226 }
227
229
231 if (!graph_data){
234 }
235 bytes_read =
avio_read(model_file_context, graph_data,
size);
237 if (bytes_read !=
size){
240 }
241
242 graph_buf = TF_NewBuffer();
243 graph_buf->data = graph_data;
244 graph_buf->length =
size;
246
247 return graph_buf;
248 }
249
251 {
252 TF_DataType dt;
255
256 input_dims[0] = 1;
262 dt = TF_FLOAT;
264 break;
266 dt = TF_UINT8;
268 break;
269 default:
271 }
272
273 return TF_AllocateTensor(dt, input_dims, 4,
274 input_dims[1] * input_dims[2] * input_dims[3] *
size);
275 }
276
278 {
282 TF_DataType dt;
284
285 TF_Output tf_output;
286 tf_output.oper = TF_GraphOperationByName(tf_model->
graph, input_name);
287 if (!tf_output.oper) {
290 }
291
292 tf_output.index = 0;
293 dt = TF_OperationOutputType(tf_output);
294 switch (dt) {
295 case TF_FLOAT:
297 break;
298 case TF_UINT8:
300 break;
301 default:
304 }
306
308 TF_GraphGetTensorShape(tf_model->
graph, tf_output, dims, 4,
status);
309 if (TF_GetCode(
status) != TF_OK){
313 }
315
316 // currently only NHWC is supported
318 for (
int i = 0;
i < 4;
i++)
321
322 return 0;
323 }
324
325 static int get_output_tf(
void *model,
const char *input_name,
int input_width,
int input_height,
326 const char *output_name, int *output_width, int *output_height)
327 {
335 .output_names = &output_name,
336 .nb_output = 1,
339 };
340
343 goto err;
344 }
345
349 goto err;
350 }
351
353 if (!request) {
356 goto err;
357 }
358
362
363 err:
367 }
368
369 #define SPACE_CHARS " \t\r\n"
371 {
373
375 v = 1;
376 for (;;) {
378 if (*p == '0円')
379 break;
381 if (
c >=
'0' &&
c <=
'9')
383 else if (
c >=
'A' &&
c <=
'F')
385 else
386 break;
388 if (v & 0x100) {
391 }
393 v = 1;
394 }
395 }
397 }
398
400 {
402 TF_Buffer *graph_def;
403 TF_ImportGraphDefOptions *graph_opts;
404 TF_SessionOptions *sess_opts;
405 const TF_Operation *init_op;
406 uint8_t *sess_config =
NULL;
407 int sess_config_length = 0;
408
409 // prepare the sess config data
412 /*
413 tf_model->ctx.options.sess_config is hex to present the serialized proto
414 required by TF_SetConfig below, so we need to first generate the serialized
415 proto in a python script, tools/python/tf_sess_config.py is a script example
416 to generate the configs of sess_config.
417 */
421 }
424
426 if (!sess_config) {
429 }
433 }
434 }
435
437 if (!graph_def){
441 }
442 tf_model->
graph = TF_NewGraph();
443 tf_model->
status = TF_NewStatus();
444 graph_opts = TF_NewImportGraphDefOptions();
445 TF_GraphImportGraphDef(tf_model->
graph, graph_def, graph_opts, tf_model->
status);
446 TF_DeleteImportGraphDefOptions(graph_opts);
447 TF_DeleteBuffer(graph_def);
448 if (TF_GetCode(tf_model->
status) != TF_OK){
452 }
453
454 init_op = TF_GraphOperationByName(tf_model->
graph,
"init");
455 sess_opts = TF_NewSessionOptions();
456
457 if (sess_config) {
458 TF_SetConfig(sess_opts, sess_config, sess_config_length,tf_model->
status);
460 if (TF_GetCode(tf_model->
status) != TF_OK) {
461 TF_DeleteSessionOptions(sess_opts);
465 }
466 }
467
469 TF_DeleteSessionOptions(sess_opts);
470 if (TF_GetCode(tf_model->
status) != TF_OK)
471 {
475 }
476
477 // Run initialization operation with name "init" if it is present in graph
478 if (init_op){
483 if (TF_GetCode(tf_model->
status) != TF_OK)
484 {
488 }
489 }
490
491 return 0;
492 }
493
495 {
497
498 if (*model){
499 tf_model = (*model)->
model;
503 }
505
509 }
511
517 }
519
520 if (tf_model->
graph){
521 TF_DeleteGraph(tf_model->
graph);
522 }
526 }
528 TF_DeleteStatus(tf_model->
status);
529 }
532 }
533 }
534
536 {
540
542 if (!model){
544 }
545
547 if (!tf_model){
550 }
551 model->
model = tf_model;
552 tf_model->
model = model;
554 ctx->class = &dnn_tensorflow_class;
555
556 //parse options
560 goto err;
561 }
562
565 goto err;
566 }
567
568 if (
ctx->options.nireq <= 0) {
570 }
571
572 #if !HAVE_PTHREAD_CANCEL
573 if (
ctx->options.async) {
574 ctx->options.async = 0;
576 }
577 #endif
578
581 goto err;
582 }
583
584 for (
int i = 0;
i <
ctx->options.nireq;
i++) {
586 if (!item) {
587 goto err;
588 }
594 goto err;
595 }
596 item->
status = TF_NewStatus();
600
603 goto err;
604 }
605 }
606
609 goto err;
610 }
611
614 goto err;
615 }
616
622
623 return model;
624 err:
627 }
628
636
641
644 goto err;
645 }
646
650
655 goto err;
656 }
657
659 if (!infer_request->
tf_input->oper){
662 goto err;
663 }
665
670 goto err;
671 }
673
679 } else {
681 }
682 }
683 break;
686 break;
687 default:
689 break;
690 }
691
696 goto err;
697 }
698
703 goto err;
704 }
705
712 goto err;
713 }
715 }
716
717 return 0;
718 err:
721 }
722
731
735 goto err;
736 }
737
747 }
750 //it only support 1 output if it's frame in & frame out
754 } else {
756 }
757 } else {
762 }
763 break;
767 return;
768 }
770 break;
771 default:
773 goto err;
774 }
776 err:
779
783 }
784 }
785
787 {
793
796 return 0;
797 }
798
801 tf_model = task->
model;
803
806 goto err;
807 }
808
811 goto err;
812 }
813 return 0;
814 }
815 else {
818 goto err;
819 }
822 }
823 err:
827 }
830 }
831
833 {
839
843 }
844
846 if (!task) {
849 }
850
856 }
857
862 }
863
869 }
870
872 if (!request) {
876 }
878 }
879
881 {
884 }
885
887 {
892
894 // no pending task need to flush
895 return 0;
896 }
897
899 if (!request) {
902 }
903
909 }
911 }
912
914 }
915
922 };