From 0923303e0201c5b59386ab146d0e30b2ef79272d Mon Sep 17 00:00:00 2001 From: Alexey Suhov Date: Fri, 4 Oct 2019 19:26:43 +0300 Subject: [PATCH] Publishing 2019 R3 content --- README.md | 2 +- inference-engine/CMakeLists.txt | 3 + inference-engine/cmake/FindlibGNA.cmake | 82 +- inference-engine/cmake/check_features.cmake | 41 +- inference-engine/cmake/config.cmake.in | 3 + inference-engine/cmake/dependencies.cmake | 227 +- .../cmake/developer_package.cmake | 18 +- .../cmake/download_and_extract.cmake | 2 +- inference-engine/cmake/features.cmake | 17 +- inference-engine/cmake/ie_parallel.cmake | 115 +- inference-engine/cmake/os_flags.cmake | 25 +- inference-engine/cmake/sdl.cmake | 16 +- .../InferenceEngineConfig-version.cmake.in | 2 +- .../share/InferenceEngineConfig.cmake.in | 6 +- inference-engine/cmake/vpu_dependencies.cmake | 68 + .../ie_bridges/python/CMakeLists.txt | 8 +- .../ie_bridges/python/docs/api_overview.md | 8 +- .../affinity_setting_sample.py | 0 .../python/sample/benchmark_app/README.md | 155 - .../benchmark_app/benchmark/benchmark.py | 343 - .../benchmark/utils/infer_request_wrap.py | 81 - .../benchmark/utils/inputs_filling.py | 194 - .../benchmark/utils/parameters.py | 92 - .../benchmark_app/benchmark/utils/utils.py | 99 - .../sample/benchmark_app/benchmark_app.py | 4 - .../sample/classification_sample/README.md | 4 +- .../classification_sample_async/README.md | 6 +- .../hello_query_device/hello_query_device.py | 15 +- .../object_detection_sample_ssd/README.md | 73 + .../object_detection_sample_ssd.py | 189 + .../sample/style_transfer_sample/README.md | 2 +- .../src/openvino/inference_engine/ie_api.pyx | 14 +- .../openvino/inference_engine/ie_api_impl.cpp | 31 +- .../openvino/inference_engine/ie_api_impl.hpp | 4 +- .../inference_engine/ie_api_impl_defs.pxd | 2 +- .../tools/statistics_collector/CMakeLists.txt | 8 +- .../include/builders/ie_concat_layer.hpp | 3 - inference-engine/include/cpp/ie_cnn_network.h | 6 +- .../include/cpp/ie_executable_network.hpp | 96 +- .../include/cpp/ie_infer_request.hpp | 22 +- .../include/cpp/ie_memory_state.hpp | 37 +- .../include/cpp/ie_plugin_cpp.hpp | 27 +- .../include/details/ie_inetwork_iterator.hpp | 2 +- .../include/details/ie_pre_allocator.hpp | 4 +- inference-engine/include/dlia/dlia_config.hpp | 81 + .../include/hetero/hetero_plugin_config.hpp | 4 + inference-engine/include/ie_allocator.hpp | 3 + inference-engine/include/ie_api.h | 15 +- inference-engine/include/ie_blob.h | 7 +- inference-engine/include/ie_common.h | 4 +- inference-engine/include/ie_core.hpp | 11 +- inference-engine/include/ie_data.h | 4 +- inference-engine/include/ie_device.hpp | 16 +- .../include/ie_icnn_network_stats.hpp | 49 +- .../include/ie_iexecutable_network.hpp | 79 +- inference-engine/include/ie_iextension.h | 8 +- .../include/ie_ihetero_plugin.hpp | 17 +- .../include/ie_iinfer_request.hpp | 18 +- inference-engine/include/ie_layers.h | 144 +- inference-engine/include/ie_layouts.h | 1 + inference-engine/include/ie_parallel.hpp | 15 + inference-engine/include/ie_parameter.hpp | 8 +- inference-engine/include/ie_plugin.hpp | 15 +- inference-engine/include/ie_plugin_config.hpp | 26 +- .../include/ie_plugin_dispatcher.hpp | 3 + inference-engine/include/ie_precision.hpp | 30 +- .../include/ie_primitive_info.hpp | 35 +- inference-engine/include/ie_tensor_info.hpp | 16 +- .../multi-device/multi_device_config.hpp | 36 + .../include/vpu/hddl_plugin_config.hpp | 184 + .../include/vpu/vpu_plugin_config.hpp | 3 + inference-engine/samples/CMakeLists.txt | 12 +- .../samples/benchmark_app/README.md | 130 +- .../samples/benchmark_app/benchmark_app.hpp | 11 +- .../samples/benchmark_app/main.cpp | 178 +- .../benchmark_app/statistics_report.cpp | 288 +- .../benchmark_app/statistics_report.hpp | 62 +- .../samples/benchmark_app/utils.hpp | 1 - .../common/format_reader/CMakeLists.txt | 17 +- .../samples/common/os/windows/w_dirent.h | 64 +- .../samples/common/samples/common.hpp | 3 +- .../common/samples/console_progress.hpp | 57 +- .../samples/hello_classification/README.md | 4 +- .../samples/hello_query_device/README.md | 4 +- .../object_detection_sample_ssd/README.md | 2 + .../samples/speech_sample/README.md | 14 +- .../samples/speech_sample/main.cpp | 21 +- .../samples/speech_sample/speech_sample.hpp | 2 +- .../samples/thirdparty/gflags/.gitmodules | 4 - inference-engine/src/CMakeLists.txt | 4 +- .../src/cldnn_engine/CMakeLists.txt | 5 +- .../src/cldnn_engine/cldnn_config.h | 2 +- .../src/cldnn_engine/cldnn_custom_layer.h | 4 +- .../src/cldnn_engine/cldnn_engine.cpp | 22 +- .../src/cldnn_engine/cldnn_engine.h | 2 +- .../cldnn_engine/cldnn_executable_network.cpp | 4 +- .../src/cldnn_engine/cldnn_graph.cpp | 157 +- .../src/cldnn_engine/cldnn_graph.h | 20 +- .../src/cldnn_engine/cldnn_infer_request.cpp | 3 +- .../src/cldnn_engine/cldnn_lstm.cpp | 116 +- .../src/cldnn_engine/cldnn_program.cpp | 826 +- .../src/cldnn_engine/cldnn_program.h | 33 +- .../src/cldnn_engine/debug_options.cpp | 2 +- .../src/cldnn_engine/debug_options.h | 11 +- inference-engine/src/extension/README.md | 2 + .../src/extension/ext_broadcast.cpp | 135 +- .../extension/ext_detectionoutput_onnx.cpp | 2 +- inference-engine/src/extension/ext_gather.cpp | 99 +- inference-engine/src/extension/ext_list.cpp | 2 +- .../src/extension/ext_log_softmax.cpp | 2 + .../src/extension/ext_non_max_suppression.cpp | 244 + .../src/extension/ext_proposal_onnx.cpp | 2 +- inference-engine/src/extension/ext_reduce.cpp | 10 +- .../src/extension/ext_resample.cpp | 7 +- .../src/extension/ext_scatter.cpp | 174 + .../src/extension/ext_simplernms.cpp | 2 +- .../extension/ext_sparse_fill_empty_rows.cpp | 232 + .../src/extension/ext_strided_slice.cpp | 3 + inference-engine/src/extension/ext_unique.cpp | 206 + .../src/gna_plugin/CMakeLists.txt | 14 +- inference-engine/src/gna_plugin/dnn.cpp | 1 - .../src/gna_plugin/gna_infer_request.hpp | 4 +- .../src/gna_plugin/gna_pass_manager.cpp | 124 +- .../src/gna_plugin/gna_pass_manager.hpp | 6 + .../src/gna_plugin/gna_plugin.cpp | 408 +- .../src/gna_plugin/gna_plugin.hpp | 15 - .../src/gna_plugin/gna_plugin_config.hpp | 3 +- .../gna_plugin/gna_plugin_entry_points.cpp | 2 +- .../src/gna_plugin/gna_plugin_internal.hpp | 4 + .../src/gna_plugin/gna_plugin_log.hpp | 2 +- .../quantization/layer_quantizer.hpp | 4 +- .../gna_plugin/quantization/quantization.h | 1 + .../quantization/scale_factor_calc.hpp | 25 +- .../src/hetero_plugin/CMakeLists.txt | 16 +- .../hetero_ade_util.cpp} | 2 +- .../hetero_ade_util.hpp} | 0 .../hetero_async_infer_request.cpp | 1 - .../hetero_async_infer_request.hpp | 0 .../hetero_device_loader.cpp | 7 - .../hetero_device_loader.hpp | 0 .../hetero_executable_network.cpp | 21 +- .../hetero_executable_network.hpp | 0 .../hetero_fallback_policy.cpp} | 37 +- .../hetero_fallback_policy.hpp} | 0 .../hetero_graph_splitter.cpp} | 5 +- .../hetero_graph_splitter.hpp} | 4 +- .../hetero_infer_request.cpp | 0 .../hetero_infer_request.hpp | 0 .../src/hetero_plugin/hetero_plugin.cpp | 150 +- .../hetero_plugin.hpp | 0 .../hetero_plugin_base.hpp | 0 .../src/inference_engine/CMakeLists.txt | 53 +- .../src/inference_engine/blob_factory.hpp | 33 + .../src/inference_engine/blob_transform.cpp | 90 +- .../builders/ie_layer_builder.cpp | 2 +- .../src/inference_engine/cnn_network_impl.cpp | 11 +- .../cnn_network_int8_normalizer.cpp | 9 +- .../base/ie_inference_plugin_api.hpp | 4 +- .../cpp_interfaces/ie_task.hpp | 1 + .../cpp_interfaces/ie_task_synchronizer.hpp | 1 + ...nfer_async_request_thread_safe_default.hpp | 4 +- .../impl/ie_memory_state_internal.hpp | 3 +- .../interface/ie_imemory_state_internal.hpp | 5 +- .../interface/ie_internal_plugin_config.hpp | 33 + inference-engine/src/inference_engine/debug.h | 8 +- .../src/inference_engine/graph_tools.hpp | 3 - .../src/inference_engine/graph_transformer.h | 1 + .../inference_engine/hetero/hetero_plugin.cpp | 161 - .../inference_engine/ie_cnn_layer_builder.h | 2 + .../ie_cnn_net_reader_impl.cpp | 2 +- .../inference_engine/ie_cnn_net_reader_impl.h | 1 + .../src/inference_engine/ie_core.cpp | 92 +- .../src/inference_engine/ie_data.cpp | 2 +- .../src/inference_engine/ie_device.cpp | 6 + .../src/inference_engine/ie_format_parser.cpp | 9 +- .../src/inference_engine/ie_icore.hpp | 3 +- .../src/inference_engine/ie_ir_parser.hpp | 3 +- .../src/inference_engine/ie_layer_parsers.cpp | 8 +- .../src/inference_engine/ie_layer_parsers.h | 1 - .../inference_engine/ie_layer_validators.cpp | 388 +- .../inference_engine/ie_layer_validators.hpp | 341 +- .../inference_engine/ie_layers_internal.cpp | 9 +- .../src/inference_engine/ie_layouts.cpp | 20 +- .../inference_engine/ie_metric_helpers.hpp | 2 +- .../inference_engine/ie_plugin_dispatcher.cpp | 10 + .../ie_preprocess_gapi_kernels.cpp | 1 - .../src/inference_engine/ie_util_internal.cpp | 13 +- .../src/inference_engine/ie_utils.cpp | 43 + .../src/inference_engine/ie_version.cpp | 2 +- .../src/inference_engine/layer_transform.hpp | 63 +- .../src/inference_engine/net_pass.cpp | 29 +- .../inference_engine/network_serializer.cpp | 5 +- .../src/inference_engine/range_iterator.hpp | 83 - .../built-in/ie_broadcast_shape_infer.hpp | 26 +- .../built-in/ie_built_in_holder.cpp | 10 + .../ie_non_max_suppression_shape_infer.hpp | 39 + .../built-in/ie_resample_shape_infer.hpp | 32 +- .../built-in/ie_scatter_shape_infer.hpp | 39 + .../ie_sparse_fill_empty_rows_shape_infer.hpp | 33 + .../built-in/ie_topk_shape_infer.hpp | 2 +- .../built-in/ie_unique_shape_infer.hpp | 44 + .../built-in/ie_unsqueeze_shape_infer.hpp | 4 + .../const_infer/broadcast_offset.hpp | 71 + .../const_infer/ie_add_const_infer.hpp | 200 +- .../const_infer/ie_broadcast_const_infer.hpp | 123 +- .../const_infer/ie_concat_const_infer.hpp | 11 +- .../const_infer/ie_const_infer_holder.cpp | 17 + .../const_infer/ie_const_infer_impl.cpp | 3 +- .../const_infer/ie_convert_const_infer.hpp | 91 + .../const_infer/ie_eltw_const_infer.hpp | 6 +- .../const_infer/ie_gather_const_infer.hpp | 98 +- .../const_infer/ie_mul_const_infer.hpp | 224 +- .../const_infer/ie_onehot_const_infer.hpp | 71 +- .../const_infer/ie_permute_const_infer.hpp | 75 + .../const_infer/ie_pow_const_infer.hpp | 99 + .../const_infer/ie_reduce_const_infer.hpp | 374 + .../const_infer/ie_shape_const_infer.hpp | 12 +- .../ie_strided_slice_const_infer.hpp | 87 +- .../shape_infer/ie_reshape_launcher.cpp | 3 +- .../transform/transform_network.cpp | 353 - .../transform/transform_network.hpp | 116 - .../transform/transformation.cpp | 20 - .../transform/transformation.hpp | 25 - .../transformations/eltwise_broadcast.cpp | 68 - .../transformations/eltwise_broadcast.hpp | 19 - .../transform/transformations/lrn.cpp | 63 - .../transform/transformations/lrn.hpp | 19 - .../transform/transformations/sub.cpp | 47 - .../transform/transformations/sub.hpp | 19 - .../src/inference_engine/w_dirent.h | 56 +- .../mkldnn_plugin/mkldnn/desc_iterator.hpp | 44 +- .../src/mkldnn_plugin/mkldnn_edge.cpp | 8 +- .../src/mkldnn_plugin/mkldnn_exec_network.cpp | 244 + .../src/mkldnn_plugin/mkldnn_exec_network.h | 55 + .../mkldnn_plugin/mkldnn_extension_utils.cpp | 2 + .../src/mkldnn_plugin/mkldnn_graph.cpp | 251 +- .../src/mkldnn_plugin/mkldnn_graph.h | 49 +- .../mkldnn_plugin/mkldnn_graph_optimizer.cpp | 46 +- .../src/mkldnn_plugin/mkldnn_memory.cpp | 39 +- .../src/mkldnn_plugin/mkldnn_memory.h | 2 +- .../mkldnn_memory_solver.cpp} | 8 +- .../mkldnn_memory_solver.hpp} | 6 +- .../src/mkldnn_plugin/mkldnn_memory_state.cpp | 35 + .../src/mkldnn_plugin/mkldnn_memory_state.h | 29 + .../src/mkldnn_plugin/mkldnn_node.cpp | 217 +- .../src/mkldnn_plugin/mkldnn_node.h | 55 +- .../src/mkldnn_plugin/mkldnn_plugin.cpp | 2 +- .../src/mkldnn_plugin/mkldnn_plugin.h | 5 +- .../src/mkldnn_plugin/mkldnn_streams.cpp | 4 +- .../nodes/mkldnn_batchnorm_node.cpp | 5 +- .../nodes/mkldnn_bin_conv_node.cpp | 137 +- .../mkldnn_plugin/nodes/mkldnn_conv_node.cpp | 190 +- .../nodes/mkldnn_def_conv_node.cpp | 133 +- .../nodes/mkldnn_eltwise_node.cpp | 4 +- .../nodes/mkldnn_generic_node.cpp | 3 +- .../nodes/mkldnn_memory_node.cpp | 12 - .../nodes/mkldnn_memory_node.hpp | 9 +- .../nodes/mkldnn_permute_node.cpp | 66 +- .../nodes/mkldnn_quantize_node.cpp | 12 +- .../nodes/mkldnn_reorder_node.cpp | 5 +- .../src/mkldnn_plugin/nodes/mkldnn_rnn.h | 2 +- .../nodes/mkldnn_softmax_node.cpp | 5 +- .../nodes/mkldnn_tensoriterator_node.h | 1 + .../src/mkldnn_plugin/utils/blob_dump.cpp | 2 +- inference-engine/src/vpu/CMakeLists.txt | 27 +- .../src/vpu/common/CMakeLists.txt | 42 + .../common/include/vpu/parsed_config_base.hpp | 93 + .../include/vpu/utils/any.hpp | 0 .../include/vpu/utils/attributes_map.hpp | 20 +- .../include/vpu/utils/auto_scope.hpp | 0 .../include/vpu/utils/checked_cast.hpp | 0 .../include/vpu/utils/containers.hpp | 28 +- .../include/vpu/utils/dot_io.hpp | 0 .../include/vpu/utils/enums.hpp | 0 .../include/vpu/utils/extra.hpp | 2 +- .../include/vpu/utils/file_system.hpp | 0 .../include/vpu/utils/func_ref.hpp | 6 + .../include/vpu/utils/handle.hpp | 27 +- .../src/vpu/common/include/vpu/utils/heap.hpp | 104 + .../include/vpu/utils/ie_helpers.hpp | 11 +- .../include/vpu/utils/io.hpp | 23 +- .../include/vpu/utils/logger.hpp | 0 .../include/vpu/utils/numeric.hpp | 0 .../include/vpu/utils/optional.hpp | 0 .../include/vpu/utils/perf_report.hpp | 26 + .../include/vpu/utils/range.hpp | 0 .../include/vpu/utils/simple_math.hpp | 0 .../include/vpu/utils/string.hpp | 6 +- .../src/vpu/common/src/parsed_config_base.cpp | 126 + .../src/utils/dot_io.cpp | 0 .../src/utils/enums.cpp | 0 .../src/utils/file_system.cpp | 0 .../src/utils/ie_helpers.cpp | 68 +- .../src/utils/io.cpp | 0 .../src/utils/logger.cpp | 0 .../src/utils/perf_report.cpp | 2 +- .../src/utils/simple_math.cpp | 0 .../binary_layers.cl | 0 .../ctc.cl | 0 .../custom_kernels/customLayerBindings.xml | 227 + .../cvtf32f16.cl | 0 .../cvtu8f16.cl | 0 .../grn.cl | 0 .../mvn.cl | 0 .../src/vpu/custom_kernels/region_chw.cl | 85 + .../custom_kernels/region_chw_m7_branch0.cl | 68 + .../custom_kernels/region_chw_m7_branch1.cl | 53 + .../reorg_chw.cl | 0 .../reorg_chw_local.cl | 0 .../reorg_chw_stack.cl | 0 .../src/vpu/custom_kernels/reorg_hwc.cl | 64 + .../resample_nn.cl | 6 +- .../custom_kernels/resample_with_antialias.cl | 75 + .../shuffle_channels.cl | 0 .../src/vpu/graph_transformer/CMakeLists.txt | 5 +- .../include/vpu/backend/backend.hpp | 2 +- .../include/vpu/custom_layer.hpp | 10 +- .../include/vpu/frontend/frontend.hpp | 7 + .../include/vpu/frontend/stage_builder.hpp | 12 +- .../include/vpu/graph_transformer.hpp | 10 +- .../include/vpu/hw/mx_stage.hpp | 9 +- .../include/vpu/hw/tiling.hpp | 24 +- .../include/vpu/model/data.hpp | 7 +- .../include/vpu/model/data_desc.hpp | 91 +- .../include/vpu/model/model.hpp | 6 +- .../include/vpu/model/stage.hpp | 87 +- .../include/vpu/parsed_config.hpp | 72 +- .../include/vpu/pass_manager.hpp | 10 + .../hw_conv_tiling/hw_convolution_tiler.hpp | 319 + .../passes/hw_conv_tiling/hw_stage_tiler.hpp | 126 + .../hw_pooling_tiling/hw_pooling_tiler.hpp | 209 + .../hw_pooling_tiling/hw_stage_tiler.hpp | 83 + .../include/vpu/private_plugin_config.hpp | 13 +- .../include/vpu/special_stage_processor.hpp | 45 + .../include/vpu/stub_stage.hpp | 11 +- .../include/vpu/sw/post_op_stage.hpp | 8 +- .../vpu/graph_transformer/src/allocator.cpp | 2 - .../graph_transformer/src/backend/backend.cpp | 2 +- .../src/backend/get_meta_data.cpp | 151 +- .../vpu/graph_transformer/src/blob_reader.cpp | 64 +- .../graph_transformer/src/custom_layer.cpp | 10 +- .../src/frontend/detect_network_batch.cpp | 24 +- .../src/frontend/frontend.cpp | 15 +- .../src/frontend/in_out_convert.cpp | 212 +- .../src/frontend/parse_data.cpp | 93 +- .../src/frontend/parse_network.cpp | 15 - .../src/frontend/pre_process.cpp | 1 - .../vpu/graph_transformer/src/hw/mx_stage.cpp | 75 +- .../vpu/graph_transformer/src/hw/tiling.cpp | 187 +- .../vpu/graph_transformer/src/model/data.cpp | 125 +- .../graph_transformer/src/model/data_desc.cpp | 178 +- .../vpu/graph_transformer/src/model/model.cpp | 12 +- .../vpu/graph_transformer/src/model/stage.cpp | 95 +- .../graph_transformer/src/parsed_config.cpp | 128 +- .../graph_transformer/src/pass_manager.cpp | 18 +- .../add_copy_for_outputs_inside_network.cpp | 53 + .../src/passes/adjust_data_batch.cpp | 2 +- .../src/passes/adjust_data_layout.cpp | 46 +- .../src/passes/adjust_data_location.cpp | 2 + .../src/passes/final_check.cpp | 2 +- .../src/passes/hw_conv_tiling.cpp | 1313 +- .../hw_conv_tiling/hw_convolution_tiler.cpp | 757 + .../passes/hw_conv_tiling/hw_stage_tiler.cpp | 481 + .../src/passes/hw_fc_tiling.cpp | 51 +- .../src/passes/hw_padding.cpp | 49 +- .../src/passes/hw_pooling_tiling.cpp | 748 +- .../hw_pooling_tiling/hw_pooling_tiler.cpp | 477 + .../hw_pooling_tiling/hw_stage_tiler.cpp | 234 + .../src/passes/initial_check.cpp | 28 + .../src/passes/inject_sw.cpp | 16 +- .../src/passes/merge_hw_stages.cpp | 4 +- .../src/passes/process_special_stages.cpp | 579 +- .../passes/remove_unused_stages_outputs.cpp | 50 + .../src/passes/replace_deconv_by_conv.cpp | 51 +- .../src/passes/replace_fc_by_conv.cpp | 304 +- .../src/passes/reshape_dilation_conv.cpp | 335 +- .../src/passes/split_grouped_conv.cpp | 4 +- .../src/passes/split_hw_conv_and_pool.cpp | 8 +- .../src/passes/split_hw_depth_convolution.cpp | 4 +- .../src/passes/strided_slice.cpp | 317 + .../src/passes/sw_conv_adaptation.cpp | 73 +- .../src/passes/sw_deconv_adaptation.cpp | 81 +- .../src/passes/sw_fc_adaptation.cpp | 57 +- .../src/passes/sw_pooling_adaptation.cpp | 39 +- .../src/passes/swap_concat_and_hw_ops.cpp | 4 +- .../src/special_stage_processor.cpp | 573 + .../graph_transformer/src/stages/argmax.cpp | 36 +- .../vpu/graph_transformer/src/stages/bias.cpp | 16 +- .../graph_transformer/src/stages/clamp.cpp | 12 +- .../graph_transformer/src/stages/concat.cpp | 63 +- .../src/stages/convolution.cpp | 45 +- .../vpu/graph_transformer/src/stages/copy.cpp | 42 +- .../vpu/graph_transformer/src/stages/crop.cpp | 64 +- .../src/stages/ctc_decoder.cpp | 42 +- .../graph_transformer/src/stages/custom.cpp | 98 +- .../src/stages/detection_output.cpp | 42 +- .../graph_transformer/src/stages/eltwise.cpp | 132 +- .../vpu/graph_transformer/src/stages/exp.cpp | 46 + .../src/stages/{broadcast.cpp => expand.cpp} | 53 +- .../graph_transformer/src/stages/floor.cpp | 45 + .../graph_transformer/src/stages/gather.cpp | 85 +- .../vpu/graph_transformer/src/stages/gemm.cpp | 90 +- .../vpu/graph_transformer/src/stages/grn.cpp | 31 +- .../graph_transformer/src/stages/interp.cpp | 31 +- .../vpu/graph_transformer/src/stages/log.cpp | 16 +- .../graph_transformer/src/stages/mtcnn.cpp | 46 +- .../vpu/graph_transformer/src/stages/mvn.cpp | 44 +- .../vpu/graph_transformer/src/stages/none.cpp | 16 +- .../vpu/graph_transformer/src/stages/norm.cpp | 40 +- .../src/stages/normalize.cpp | 43 +- .../vpu/graph_transformer/src/stages/pad.cpp | 47 +- .../graph_transformer/src/stages/permute.cpp | 124 +- .../graph_transformer/src/stages/power.cpp | 12 +- .../graph_transformer/src/stages/proposal.cpp | 45 +- .../src/stages/psroipooling.cpp | 39 +- .../graph_transformer/src/stages/reduce.cpp | 82 +- .../src/stages/region_yolo.cpp | 37 +- .../vpu/graph_transformer/src/stages/relu.cpp | 14 +- .../src/stages/reorg_yolo.cpp | 52 +- .../graph_transformer/src/stages/resample.cpp | 31 +- .../graph_transformer/src/stages/reshape.cpp | 48 +- .../src/stages/reverse_sequence.cpp | 64 +- .../vpu/graph_transformer/src/stages/rnn.cpp | 87 +- .../src/stages/roipooling.cpp | 39 +- .../graph_transformer/src/stages/scale.cpp | 14 +- .../graph_transformer/src/stages/shrink.cpp | 31 +- .../graph_transformer/src/stages/softmax.cpp | 28 +- .../graph_transformer/src/stages/split.cpp | 63 +- .../src/stages/strided_slice.cpp | 77 + .../vpu/graph_transformer/src/stages/tile.cpp | 45 +- .../vpu/graph_transformer/src/stages/topk.cpp | 147 + .../vpu/graph_transformer/src/stub_stage.cpp | 58 +- .../src/sw/post_op_stage.cpp | 41 +- .../src/vpu/myriad_plugin/CMakeLists.txt | 10 +- .../src/vpu/myriad_plugin/api/myriad_api.cpp | 2 +- .../myriad_async_infer_request.cpp | 2 + .../src/vpu/myriad_plugin/myriad_config.cpp | 13 + .../src/vpu/myriad_plugin/myriad_config.h | 9 + .../myriad_executable_network.cpp | 145 +- .../myriad_plugin/myriad_executable_network.h | 11 +- .../src/vpu/myriad_plugin/myriad_executor.cpp | 21 +- .../src/vpu/myriad_plugin/myriad_executor.h | 3 +- .../myriad_plugin/myriad_infer_request.cpp | 68 +- .../vpu/myriad_plugin/myriad_infer_request.h | 3 +- .../src/vpu/myriad_plugin/myriad_plugin.cpp | 4 + inference-engine/tests/CMakeLists.txt | 2 - .../tests/helpers/single_layer_common.cpp | 88 +- .../tests/helpers/single_layer_common.hpp | 25 +- .../tests/helpers/tests_common.hpp | 34 +- .../tests/helpers/tests_vpu_common.cpp | 43 + .../tests/helpers/tests_vpu_common.hpp | 100 + inference-engine/tests/unit/CMakeLists.txt | 14 +- .../unit/builders/network_builder_test.cpp | 20 +- .../unit/builders/transform_network_test.cpp | 185 - .../cnn_layer_validation_tests.cpp | 2 +- .../tests/unit/cnn_network/parameters.h | 4 +- .../tests/unit/cnn_network/shapes.h | 4 +- .../cnn_network/v2_format_parser_test.cpp | 4 +- .../unit/engines/gna/configuration_test.cpp | 39 - .../engines/gna/fp32_non_quantized_tests.cpp | 211 + .../unit/engines/gna/gna_cppwraper_test.cpp | 2 +- .../unit/engines/gna/gna_graph_aot_test.cpp | 4 +- .../tests/unit/engines/gna/gna_matcher.cpp | 19 +- .../tests/unit/engines/gna/gna_matcher.hpp | 78 +- .../engines/gna/i16_quantisation_test.cpp | 14 +- .../engines/gna/matchers/diag_matcher.hpp | 24 +- .../tests/unit/engines/gna/test_irs.cpp | 2986 ++- .../tests/unit/engines/gna/test_irs.hpp | 35 + .../layers/extensions/broadcast_tests.cpp | 70 +- .../graph/layers/extensions/fake_layer.cpp | 4 +- .../graph/layers/extensions/gather_tests.cpp | 122 +- .../layers/extensions/gather_tree_tests.cpp | 286 - .../layers/extensions/graph_generic_test.cpp | 2 +- .../graph/layers/extensions/math_tests.cpp | 9 +- .../extensions/non_max_suppression_tests.cpp | 586 + .../graph/layers/extensions/onehot_tests.cpp | 5 - .../graph/layers/extensions/reduce_tests.cpp | 18 +- .../graph/layers/extensions/scatter_tests.cpp | 205 + .../sparse_fill_empty_rows_tests.cpp | 553 + .../graph/layers/extensions/topk_tests.cpp | 22 +- .../graph/layers/extensions/unique_tests.cpp | 378 + .../layers/internal/graph_activation_test.cpp | 12 +- .../graph/layers/internal/graph_conv_test.cpp | 24 +- .../layers/internal/graph_eltwise_test.cpp | 52 +- .../layers/internal/graph_leaks_test.cpp | 1 + .../layers/internal/graph_permute_test.cpp | 8 +- .../graph/structure/graph_structure_test.cpp | 19 +- .../unit/engines/mkldnn/graph/test_graph.hpp | 6 +- .../mkldnn}/mem_solver_test.cpp | 8 +- .../unit/graph_tools/graph_copy_tests.cpp | 52 +- .../unit/graph_tools/graph_test_base.hpp | 9 +- .../graph_tools_functional_tests.cpp | 39 + .../unit/graph_tools/graph_tools_test.cpp | 132 +- .../blob_proxy_test.cpp | 20 +- .../unit/inference_engine_tests/blob_test.cpp | 5 +- .../cnn_network_test.cpp | 5 +- .../cpp_interfaces/executor_manager_tests.cpp | 15 +- .../iinference_plugin_internal_tests.cpp | 4 +- .../cpp_interfaces/plugin_base_tests.cpp | 4 +- .../inference_engine_tests/device_tests.cpp | 59 - .../inference_engine_tests/local_test.cpp | 143 +- .../ngraph_reader_tests.cpp | 132 +- .../plugin_dispatcher_tests.cpp | 18 +- .../range_iterator_tests.cpp | 50 - .../util_const_infer_test.cpp | 782 +- .../util_const_infer_test.hpp | 4 + ...ync_infer_request_thread_safe_internal.hpp | 2 +- .../tests/unit/mocks/mock_icnn_network.hpp | 8 +- .../mocks/mock_not_empty_icnn_network.hpp | 2 +- .../built_in_shape_infer_general_test.cpp | 36 + .../eltwise_broadcast_test.cpp | 63 - .../tests/unit/transformations/sub_test.cpp | 39 - .../transformations/tranformations_test.hpp | 13 - inference-engine/thirdparty/CMakeLists.txt | 44 +- inference-engine/thirdparty/ade | 2 +- .../thirdparty/clDNN/CMakeLists.txt | 29 +- .../thirdparty/clDNN/api/C/activation.h | 63 - .../thirdparty/clDNN/api/C/activation_grad.h | 58 - .../thirdparty/clDNN/api/C/apply_adam.h | 73 - .../thirdparty/clDNN/api/C/arg_max_min.h | 71 - .../clDNN/api/C/average_unpooling.h | 52 - .../thirdparty/clDNN/api/C/batch_norm.h | 64 - .../thirdparty/clDNN/api/C/batch_norm_grad.h | 48 - .../clDNN/api/C/binary_convolution.h | 64 - .../thirdparty/clDNN/api/C/border.h | 82 - .../thirdparty/clDNN/api/C/broadcast.h | 89 - .../thirdparty/clDNN/api/C/cldnn.h | 891 - .../thirdparty/clDNN/api/C/concatenation.h | 74 - .../thirdparty/clDNN/api/C/condition.h | 65 - .../thirdparty/clDNN/api/C/contract.h | 84 - .../thirdparty/clDNN/api/C/convolution.h | 88 - .../clDNN/api/C/convolution_grad_input.h | 58 - .../clDNN/api/C/convolution_grad_weights.h | 72 - .../thirdparty/clDNN/api/C/crop.h | 71 - .../clDNN/api/C/custom_gpu_primitive.h | 67 - .../thirdparty/clDNN/api/C/data.h | 51 - .../thirdparty/clDNN/api/C/deconvolution.h | 69 - .../thirdparty/clDNN/api/C/deformable_conv.h | 55 - .../clDNN/api/C/deformable_interp.h | 66 - .../thirdparty/clDNN/api/C/detection_output.h | 89 - .../clDNN/api/C/detection_output_sort.h | 58 - .../thirdparty/clDNN/api/C/eltwise.h | 110 - .../thirdparty/clDNN/api/C/embed.h | 49 - .../thirdparty/clDNN/api/C/fully_connected.h | 62 - .../clDNN/api/C/fully_connected_grad_input.h | 46 - .../api/C/fully_connected_grad_weights.h | 55 - .../thirdparty/clDNN/api/C/gather.h | 54 - .../thirdparty/clDNN/api/C/gemm.h | 54 - .../thirdparty/clDNN/api/C/index_select.h | 75 - .../thirdparty/clDNN/api/C/input_layout.h | 51 - .../thirdparty/clDNN/api/C/lookup_table.h | 57 - inference-engine/thirdparty/clDNN/api/C/lrn.h | 69 - .../thirdparty/clDNN/api/C/lstm.h | 147 - .../thirdparty/clDNN/api/C/lstm_dynamic.h | 72 - .../thirdparty/clDNN/api/C/max_unpooling.h | 58 - .../thirdparty/clDNN/api/C/mutable_data.h | 60 - inference-engine/thirdparty/clDNN/api/C/mvn.h | 52 - .../thirdparty/clDNN/api/C/normalize.h | 67 - .../thirdparty/clDNN/api/C/one_hot.h | 72 - .../thirdparty/clDNN/api/C/permute.h | 53 - .../thirdparty/clDNN/api/C/pooling.h | 77 - .../thirdparty/clDNN/api/C/prior_box.h | 69 - .../thirdparty/clDNN/api/C/proposal.h | 65 - .../thirdparty/clDNN/api/C/reduce.h | 87 - .../thirdparty/clDNN/api/C/region_yolo.h | 59 - .../thirdparty/clDNN/api/C/reorder.h | 55 - .../thirdparty/clDNN/api/C/reorg_yolo.h | 52 - .../thirdparty/clDNN/api/C/reshape.h | 50 - .../thirdparty/clDNN/api/C/reverse_sequence.h | 48 - .../thirdparty/clDNN/api/C/roi_pooling.h | 68 - .../thirdparty/clDNN/api/C/scale.h | 58 - .../thirdparty/clDNN/api/C/scale_grad_input.h | 45 - .../clDNN/api/C/scale_grad_weights.h | 54 - .../thirdparty/clDNN/api/C/select.h | 48 - .../thirdparty/clDNN/api/C/shuffle_channels.h | 48 - .../thirdparty/clDNN/api/C/softmax.h | 71 - .../clDNN/api/C/softmax_loss_grad.h | 46 - .../thirdparty/clDNN/api/C/split.h | 68 - .../thirdparty/clDNN/api/C/strided_slice.h | 52 - .../thirdparty/clDNN/api/C/tile.h | 55 - .../thirdparty/clDNN/api/C/upsampling.h | 62 - .../thirdparty/clDNN/api/CPP/compounds.h | 231 - .../thirdparty/clDNN/api/CPP/event.hpp | 132 - .../thirdparty/clDNN/api/CPP/network.hpp | 366 - .../thirdparty/clDNN/api/CPP/primitive.hpp | 314 - .../clDNN/api/{CPP => }/activation.hpp | 86 +- .../clDNN/api/{CPP => }/activation_grad.hpp | 33 +- .../clDNN/api/{CPP => }/apply_adam.hpp | 28 +- .../clDNN/api/{CPP => }/arg_max_min.hpp | 25 +- .../clDNN/api/{CPP => }/average_unpooling.hpp | 16 +- .../clDNN/api/{CPP => }/batch_norm.hpp | 22 +- .../clDNN/api/{CPP => }/batch_norm_grad.hpp | 14 +- .../api/{CPP => }/binary_convolution.hpp | 38 +- .../thirdparty/clDNN/api/{CPP => }/border.hpp | 30 +- .../clDNN/api/{CPP => }/broadcast.hpp | 17 +- .../clDNN/api/{CPP/cldnn_defs.h => cldnn.hpp} | 203 +- .../thirdparty/clDNN/api/compounds.h | 101 + .../clDNN/api/{CPP => }/concatenation.hpp | 24 +- .../clDNN/api/{CPP => }/condition.hpp | 21 +- .../clDNN/api/{CPP => }/contract.hpp | 28 +- .../clDNN/api/{CPP => }/convolution.hpp | 413 +- .../api/{CPP => }/convolution_grad_input.hpp | 10 +- .../{CPP => }/convolution_grad_weights.hpp | 119 +- .../thirdparty/clDNN/api/{CPP => }/crop.hpp | 12 +- .../api/{CPP => }/custom_gpu_primitive.hpp | 57 +- .../thirdparty/clDNN/api/{CPP => }/data.hpp | 14 +- .../clDNN/api/{CPP => }/deconvolution.hpp | 133 +- .../clDNN/api/{CPP => }/depth_to_space.hpp | 10 +- .../clDNN/api/{CPP => }/detection_output.hpp | 78 +- .../clDNN/api/{CPP => }/eltwise.hpp | 150 +- .../thirdparty/clDNN/api/{CPP => }/embed.hpp | 15 +- .../thirdparty/clDNN/api/{CPP => }/engine.hpp | 141 +- .../thirdparty/clDNN/api/event.hpp | 94 + .../clDNN/api/{CPP => }/fully_connected.hpp | 50 +- .../{CPP => }/fully_connected_grad_input.hpp | 15 +- .../fully_connected_grad_weights.hpp | 23 +- .../thirdparty/clDNN/api/{CPP => }/gather.hpp | 21 +- .../thirdparty/clDNN/api/gather_tree.hpp | 54 + .../thirdparty/clDNN/api/{CPP => }/gemm.hpp | 19 +- .../clDNN/api/{CPP => }/index_select.hpp | 23 +- .../clDNN/api/{CPP => }/input_layout.hpp | 16 +- .../thirdparty/clDNN/api/{CPP => }/layout.hpp | 44 +- .../clDNN/api/{CPP => }/lookup_table.hpp | 15 +- .../thirdparty/clDNN/api/{CPP => }/lrn.hpp | 32 +- .../thirdparty/clDNN/api/{CPP => }/lstm.hpp | 148 +- .../clDNN/api/{CPP => }/lstm_dynamic.hpp | 30 +- .../clDNN/api/{CPP => }/max_unpooling.hpp | 24 +- .../thirdparty/clDNN/api/{CPP => }/memory.hpp | 117 +- .../clDNN/api/{CPP => }/meta_utils.hpp | 2 +- .../clDNN/api/{CPP => }/mutable_data.hpp | 15 +- .../thirdparty/clDNN/api/{CPP => }/mvn.hpp | 19 +- .../thirdparty/clDNN/api/network.hpp | 200 + .../clDNN/api/{CPP => }/normalize.hpp | 18 +- .../clDNN/api/{CPP => }/one_hot.hpp | 16 +- .../clDNN/api/{CPP => }/permute.hpp | 9 +- .../clDNN/api/{CPP => }/pooling.hpp | 40 +- .../thirdparty/clDNN/api/primitive.hpp | 173 + .../clDNN/api/{CPP => }/prior_box.hpp | 47 +- .../clDNN/api/{CPP => }/profiling.hpp | 2 - .../clDNN/api/{CPP => }/program.hpp | 282 +- .../clDNN/api/{CPP => }/proposal.hpp | 55 +- .../clDNN/api/{CPP => }/pyramid_roi_align.hpp | 14 +- .../clDNN/api/{CPP => }/quantize.hpp | 9 +- .../thirdparty/clDNN/api/{CPP => }/reduce.hpp | 49 +- .../clDNN/api/{CPP => }/region_yolo.hpp | 21 +- .../clDNN/api/{CPP => }/reorder.hpp | 37 +- .../clDNN/api/{CPP => }/reorg_yolo.hpp | 9 +- .../clDNN/api/{CPP => }/reshape.hpp | 11 +- .../clDNN/api/{CPP => }/reverse_sequence.hpp | 12 +- .../clDNN/api/{CPP => }/roi_pooling.hpp | 34 +- .../thirdparty/clDNN/api/{CPP => }/scale.hpp | 11 +- .../clDNN/api/{CPP => }/scale_grad_input.hpp | 11 +- .../api/{CPP => }/scale_grad_weights.hpp | 20 +- .../thirdparty/clDNN/api/{CPP => }/select.hpp | 9 +- .../clDNN/api/{CPP => }/shuffle_channels.hpp | 12 +- .../clDNN/api/{CPP => }/softmax.hpp | 21 +- .../clDNN/api/{CPP => }/softmax_loss_grad.hpp | 11 +- .../thirdparty/clDNN/api/{CPP => }/split.hpp | 27 +- .../clDNN/api/{CPP => }/strided_slice.hpp | 19 +- .../thirdparty/clDNN/api/{CPP => }/tensor.hpp | 266 +- .../thirdparty/clDNN/api/{CPP => }/tile.hpp | 22 +- .../clDNN/api/{CPP => }/topology.hpp | 60 +- .../clDNN/api/{CPP => }/upsampling.hpp | 31 +- .../api_extension/C/fused_conv_bn_scale.h | 69 - .../api_extension/C/fused_conv_eltwise.h | 103 - .../api_extension/C/lstm_dynamic_input.h | 58 - .../api_extension/C/lstm_dynamic_timeloop.h | 68 - .../{CPP => }/fused_conv_bn_scale.hpp | 69 +- .../{CPP => }/fused_conv_eltwise.hpp | 122 +- .../{CPP => }/lstm_dynamic_input.hpp | 15 +- .../{CPP => }/lstm_dynamic_timeloop.hpp | 28 +- .../clDNN/api_test_builds/CMakeLists.txt | 26 - .../clDNN/kernel_selector/CMakeLists.txt | 2 +- .../kernel_selector/common/common_types.h | 3 +- .../kernel_selector/common/tensor_type.cpp | 19 + .../kernel_selector/common/tensor_type.h | 17 +- .../activation/activation_kernel_base.cpp | 2 +- .../activation/activation_kernel_selector.cpp | 2 - .../activation/activation_kernel_tutorial.cpp | 143 - .../binary_convolution_kernel_1x1.cpp | 115 +- ...y_convolution_kernel_1x1_b_fs_yx_fsv16.cpp | 190 + ...ary_convolution_kernel_1x1_b_fs_yx_fsv16.h | 43 + .../binary_convolution_kernel_base.cpp | 61 +- .../binary_convolution_kernel_generic.cpp | 132 +- .../binary_convolution_kernel_ref.cpp | 43 +- .../binary_convolution_kernel_selector.cpp | 2 + .../binary_convolution_params.h | 16 +- .../border/border_kernel_base.cpp | 2 +- .../border/border_kernel_ref.cpp | 4 + .../concatenation_kernel_simple_ref.cpp | 10 +- .../contract/contract_kernel_base.h | 2 +- .../convolution/convolution_kernel_base.cpp | 16 +- .../convolution_kernel_bfyx_1x1.cpp | 2 +- .../convolution_kernel_bfyx_1x1_opt.cpp | 2 +- .../convolution_kernel_bfyx_f16.cpp | 87 +- .../convolution/convolution_kernel_bfyx_f16.h | 1 - .../convolution_kernel_bfyx_f16_1x1.cpp | 87 +- .../convolution_kernel_bfyx_f16_1x1.h | 1 - .../convolution_kernel_bfyx_f16_depthwise.cpp | 93 +- .../convolution_kernel_bfyx_f16_depthwise.h | 1 - .../convolution_kernel_bfyx_to_bfyx_f16.cpp | 88 +- .../convolution_kernel_bfyx_to_bfyx_f16.h | 1 - .../convolution_kernel_bfzyx_f16.cpp | 194 + ...orial.h => convolution_kernel_bfzyx_f16.h} | 37 +- .../convolution_kernel_bfzyx_f16_fp16.h} | 24 +- .../convolution_kernel_bfzyx_f16_fp32.h} | 36 +- .../convolution_kernel_bfzyx_ref.cpp | 54 - ...volution_kernel_fs_byx_fsv32_depthwise.cpp | 192 + ...onvolution_kernel_fs_byx_fsv32_depthwise.h | 56 + .../convolution_kernel_imad_3x3.cpp | 2 +- .../convolution/convolution_kernel_ref.cpp | 9 +- .../convolution/convolution_kernel_ref.h | 2 + .../convolution_kernel_selector.cpp | 11 +- .../convolution_kernel_tutorial.cpp | 184 - .../convolution/convolution_params.h | 16 - ...eformable_convolution_kernel_bfyx_conv.cpp | 2 +- .../deconvolution_kernel_base.cpp | 38 +- .../deconvolution/deconvolution_kernel_base.h | 11 + .../deconvolution_kernel_bfzyx_f16.cpp | 152 + .../deconvolution_kernel_bfzyx_f16.h | 42 + .../deconvolution_kernel_ref.cpp | 2 + .../deconvolution_kernel_selector.cpp | 2 + .../depth_to_space_kernel_ref.h | 2 +- .../eltwise/eltwise_kernel_base.cpp | 24 +- .../eltwise/eltwise_kernel_ref.cpp | 1 + .../fully_connected_block_kernel_base.cpp | 14 +- .../fully_connected_block_kernel_base.h | 14 +- .../fully_connected_kernel_base.h | 20 +- .../fully_connected_kernel_fb_io_b8_f8.cpp | 13 + .../fully_connected_kernel_fb_io_b8_f8.h | 4 +- .../fully_connected_kernel_image_tutorial.cpp | 77 - .../fully_connected_kernel_selector.cpp | 1 - .../fused_conv_eltwise_kernel_base.cpp | 46 +- .../fused_conv_eltwise_kernel_base.h | 2 +- .../fused_conv_eltwise_kernel_ref.cpp | 8 +- .../fused_conv_eltwise_kernel_ref.h | 1 + .../actual_kernels/gather/gather_kernel_ref.h | 2 +- .../gather_tree/gather_tree_kernel_base.cpp | 63 + .../gather_tree/gather_tree_kernel_base.h | 47 + .../gather_tree/gather_tree_kernel_ref.cpp | 41 + .../gather_tree_kernel_ref.h} | 17 +- .../gather_tree_kernel_selector.cpp} | 39 +- .../gather_tree/gather_tree_kernel_selector.h | 31 + .../lookup_table/lookup_table_kernel_base.h | 2 +- .../lstm_dynamic_input_bfyx_opt.cpp | 122 + .../lstm_dynamic_input_bfyx_opt.h} | 26 +- .../lstm_dynamic_input_kernel_base.cpp | 46 +- .../lstm_dynamic_input_kernel_base.h | 37 +- .../lstm_dynamic_input_kernel_selector.cpp | 6 +- .../lstm_dynamic_input_ref_kernel.cpp | 5 + .../lstm_dynamic_timeloop_kernel_base.cpp | 39 +- .../lstm_dynamic_timeloop_kernel_base.h | 2 +- .../actual_kernels/mvn/mvn_kernel_ref.cpp | 2 + .../one_hot/one_hot_kernel_base.h | 3 +- .../pooling/pooling_kernel_base.cpp | 2 +- .../pooling_kernel_gpu_average_opt.cpp | 2 +- .../pooling/pooling_kernel_gpu_ref.cpp | 2 + .../quantize/quantize_kernel_base.cpp | 87 + .../quantize_kernel_base.h} | 30 +- .../quantize/quantize_kernel_params.h | 46 + .../quantize/quantize_kernel_ref.cpp | 60 +- .../quantize/quantize_kernel_ref.h | 32 +- .../quantize/qunatize_kernel_selector.cpp | 4 +- .../actual_kernels/reduce/reduce_kernel_ref.h | 2 +- .../region_yolo/region_yolo_kernel_ref.h | 3 +- .../reorder/reorder_kernel_base.cpp | 6 +- .../reorder/reorder_kernel_base.h | 3 +- .../reorder/reorder_kernel_binary.cpp | 17 +- .../reorg_yolo/reorg_yolo_kernel_ref.h | 2 +- .../reverse_sequence_kernel_ref.h | 3 +- .../roi_pooling/roi_pooling_kernel_base.cpp | 2 +- .../shuffle_channels_kernel_ref.h | 2 +- .../softmax_items_class_kernel_base.cpp | 4 + .../softmax/softmax_kernel_base.cpp | 2 +- .../actual_kernels/tile/tile_kernel_ref.h | 2 +- .../clDNN/kernel_selector/core/auto_tuner.cpp | 1 + .../kernel_selector/core/cache/cache.json | 16969 +++++++++++++++- .../core/cl_kernels/activation_tutorial.cl | 85 - .../core/cl_kernels/arg_max_min_axis.cl | 10 +- .../cl_kernels/binary_convolution_gpu_1x1.cl | 15 +- ...inary_convolution_gpu_1x1_b_fs_yx_fsv16.cl | 176 + .../binary_convolution_gpu_generic.cl | 42 +- .../cl_kernels/binary_convolution_gpu_ref.cl | 9 +- .../core/cl_kernels/border_gpu_ref.cl | 58 +- .../concatenation_gpu_simple_ref.cl | 4 + .../cl_kernels/convolution_gpu_bfyx_f16.cl | 22 +- .../convolution_gpu_bfyx_f16_1x1.cl | 28 +- .../convolution_gpu_bfyx_f16_depthwise.cl | 25 +- .../convolution_gpu_bfyx_to_bfyx_f16.cl | 21 +- .../cl_kernels/convolution_gpu_bfzyx_ref.cl | 147 - .../convolution_gpu_fs_byx_fsv32_depthwise.cl | 216 + .../core/cl_kernels/convolution_tutorial.cl | 117 - .../core/cl_kernels/deconvolution_gpu_ref.cl | 23 +- .../fully_connected_gpu_image_tutorial.cl | 55 - .../cl_kernels/fused_conv_eltwise_gpu_ref.cl | 67 +- .../core/cl_kernels/gather_tree_gpu_ref.cl | 43 + .../cl_kernels/gen9_common_conv_bwd_data.cl | 312 + .../gen9_common_conv_fwd_data_f16.cl | 1155 ++ .../gen9_common_conv_fwd_data_f32.cl | 810 + .../core/cl_kernels/generic_eltwise_ref.cl | 20 +- .../core/cl_kernels/include/fetch.cl | 116 +- .../cl_kernels/lstm_dynamic_input_bfyx_opt.cl | 121 + .../core/cl_kernels/lstm_dynamic_input_ref.cl | 2 +- .../mvn_gpu_ref_accross_channels.cl | 42 +- .../cl_kernels/mvn_gpu_ref_within_channels.cl | 39 +- .../core/cl_kernels/ocl_types.h | 444 + .../core/cl_kernels/permute_ref.cl | 22 +- .../core/cl_kernels/pooling_gpu_ref.cl | 30 +- .../{quantize_ref.cl => quantize_gpu_ref.cl} | 45 +- .../core/cl_kernels/reorder_data.cl | 17 +- .../core/cl_kernels/reorder_data_binary.cl | 44 +- .../reorder_data_byxf_f32_to_byx8_f4_i8.cl | 6 +- .../core/cl_kernels/reorder_data_fast_b1.cl | 55 +- .../reorder_data_to_yxfb_batched.cl | 2 +- .../core/cl_kernels/reorder_weights.cl | 33 +- .../kernel_selector/core/common/jitter.cpp | 208 +- .../kernel_selector/core/common/jitter.h | 11 +- .../core/common/training_params.h | 4 +- .../kernel_selector/core/kernel_base.cpp | 70 +- .../clDNN/kernel_selector/core/kernel_base.h | 3 + .../core/kernel_runner_interface.h | 5 +- .../kernel_selector/core/kernel_selector.cpp | 8 +- .../core/kernel_selector_common.cpp | 4 + .../core/kernel_selector_params.cpp | 248 +- .../core/kernel_selector_params.h | 146 + .../thirdparty/clDNN/src/CMakeLists.txt | 47 +- .../thirdparty/clDNN/src/activation.cpp | 20 +- .../thirdparty/clDNN/src/activation_grad.cpp | 4 +- .../thirdparty/clDNN/src/apply_adam.cpp | 2 +- .../thirdparty/clDNN/src/arg_max_min.cpp | 2 +- .../clDNN/src/average_unpooling.cpp | 2 +- .../thirdparty/clDNN/src/batch_norm.cpp | 2 +- .../thirdparty/clDNN/src/batch_norm_grad.cpp | 2 +- .../clDNN/src/binary_convolution.cpp | 27 +- .../thirdparty/clDNN/src/border.cpp | 39 +- .../thirdparty/clDNN/src/broadcast.cpp | 2 +- .../thirdparty/clDNN/src/cldnn.cpp | 843 +- .../thirdparty/clDNN/src/concatenation.cpp | 2 +- .../thirdparty/clDNN/src/condition.cpp | 2 +- .../thirdparty/clDNN/src/contract.cpp | 2 +- .../thirdparty/clDNN/src/convolution.cpp | 53 +- .../clDNN/src/convolution_grad_weights.cpp | 6 +- .../thirdparty/clDNN/src/crop.cpp | 12 +- .../clDNN/src/custom_gpu_primitive.cpp | 4 +- .../thirdparty/clDNN/src/data.cpp | 4 +- .../thirdparty/clDNN/src/deconvolution.cpp | 26 +- .../clDNN/src/deformable_convolution.cpp | 5 +- .../thirdparty/clDNN/src/depth_to_space.cpp | 2 +- .../thirdparty/clDNN/src/detection_output.cpp | 4 +- .../thirdparty/clDNN/src/eltwise.cpp | 17 +- .../thirdparty/clDNN/src/embed.cpp | 2 +- .../thirdparty/clDNN/src/engine.cpp | 87 +- .../thirdparty/clDNN/src/event.cpp | 43 +- .../thirdparty/clDNN/src/fully_connected.cpp | 4 +- .../clDNN/src/fully_connected_grad_input.cpp | 2 +- .../src/fully_connected_grad_weights.cpp | 2 +- .../clDNN/src/fused_conv_bn_scale.cpp | 15 +- .../clDNN/src/fused_conv_eltwise.cpp | 27 +- .../thirdparty/clDNN/src/gather.cpp | 2 +- .../thirdparty/clDNN/src/gather_tree.cpp | 71 + .../thirdparty/clDNN/src/gemm.cpp | 2 +- .../thirdparty/clDNN/src/generic_layer.cpp | 2 +- .../clDNN/src/gpu/activation_gpu.cpp | 70 +- .../clDNN/src/gpu/activation_grad_gpu.cpp | 45 +- .../clDNN/src/gpu/apply_adam_gpu.cpp | 30 +- .../clDNN/src/gpu/arg_max_min_gpu.cpp | 12 +- .../clDNN/src/gpu/average_unpooling_gpu.cpp | 50 +- .../clDNN/src/gpu/batch_norm_gpu.cpp | 30 +- .../clDNN/src/gpu/batch_norm_grad_gpu.cpp | 40 +- .../clDNN/src/gpu/binary_convolution_gpu.cpp | 57 +- .../thirdparty/clDNN/src/gpu/border_gpu.cpp | 58 +- .../clDNN/src/gpu/broadcast_gpu.cpp | 38 +- .../kernels/pooling_gpu_bfyx_average_opt.cl | 1 - .../clDNN/src/gpu/command_queues_builder.cpp | 28 +- .../clDNN/src/gpu/command_queues_builder.h | 8 +- .../clDNN/src/gpu/concatenation_gpu.cpp | 99 +- .../clDNN/src/gpu/condition_gpu.cpp | 23 +- .../clDNN/src/gpu/configuration.cpp | 4 + .../thirdparty/clDNN/src/gpu/confiugration.h | 8 +- .../thirdparty/clDNN/src/gpu/contract_gpu.cpp | 26 +- .../clDNN/src/gpu/convolution_gpu.cpp | 161 +- .../src/gpu/convolution_grad_weights_gpu.cpp | 50 +- .../thirdparty/clDNN/src/gpu/crop_gpu.cpp | 80 +- .../src/gpu/custom_gpu_primitive_gpu.cpp | 27 +- .../clDNN/src/gpu/deconvolution_gpu.cpp | 53 +- .../src/gpu/deformable_convolution_gpu.cpp | 28 +- .../clDNN/src/gpu/depth_to_space_gpu.cpp | 24 +- .../clDNN/src/gpu/detection_output_gpu.cpp | 33 +- .../thirdparty/clDNN/src/gpu/eltwise_gpu.cpp | 85 +- .../thirdparty/clDNN/src/gpu/embed_gpu.cpp | 20 +- .../thirdparty/clDNN/src/gpu/engine_info.cpp | 1 - .../thirdparty/clDNN/src/gpu/engine_info.h | 5 +- .../thirdparty/clDNN/src/gpu/events_pool.h | 4 +- .../clDNN/src/gpu/fully_connected_gpu.cpp | 65 +- .../gpu/fully_connected_grad_input_gpu.cpp | 36 +- .../gpu/fully_connected_grad_weights_gpu.cpp | 38 +- .../clDNN/src/gpu/fused_conv_bn_scale_gpu.cpp | 25 +- .../clDNN/src/gpu/fused_conv_eltwise_gpu.cpp | 76 +- .../thirdparty/clDNN/src/gpu/gather_gpu.cpp | 20 +- .../clDNN/src/gpu/gather_tree_gpu.cpp | 63 + .../thirdparty/clDNN/src/gpu/gemm_gpu.cpp | 26 +- .../clDNN/src/gpu/generic_layer_gpu.cpp | 15 +- .../clDNN/src/gpu/index_select_gpu.cpp | 46 +- .../clDNN/src/gpu/kernel_runner.cpp | 14 +- .../thirdparty/clDNN/src/gpu/kernel_runner.h | 2 +- .../clDNN/src/gpu/lookup_table_gpu.cpp | 38 +- .../thirdparty/clDNN/src/gpu/lrn_gpu.cpp | 40 +- .../clDNN/src/gpu/lstm_dynamic_input_gpu.cpp | 39 +- .../src/gpu/lstm_dynamic_timeloop_gpu.cpp | 26 +- .../thirdparty/clDNN/src/gpu/lstm_elt_gpu.cpp | 32 +- .../clDNN/src/gpu/lstm_gemm_gpu.cpp | 30 +- .../clDNN/src/gpu/max_unpooling_gpu.cpp | 50 +- .../thirdparty/clDNN/src/gpu/memory_gpu.cpp | 4 +- .../clDNN/src/gpu/mutable_data_gpu.cpp | 14 +- .../thirdparty/clDNN/src/gpu/mvn_gpu.cpp | 50 +- .../clDNN/src/gpu/normalize_gpu.cpp | 38 +- .../clDNN/src/gpu/ocl_base_event.cpp | 21 +- .../thirdparty/clDNN/src/gpu/ocl_base_event.h | 7 +- .../clDNN/src/gpu/ocl_queue_wrapper.h | 14 + .../thirdparty/clDNN/src/gpu/ocl_toolkit.cpp | 8 +- .../thirdparty/clDNN/src/gpu/ocl_toolkit.h | 3 +- .../clDNN/src/gpu/ocl_user_event.cpp | 7 +- .../thirdparty/clDNN/src/gpu/ocl_user_event.h | 4 +- .../thirdparty/clDNN/src/gpu/one_hot_gpu.cpp | 38 +- .../thirdparty/clDNN/src/gpu/permute_gpu.cpp | 20 +- .../thirdparty/clDNN/src/gpu/pooling_gpu.cpp | 118 +- .../clDNN/src/gpu/primitive_gpu_base.cpp | 15 +- .../clDNN/src/gpu/primitive_gpu_base.h | 8 + .../thirdparty/clDNN/src/gpu/proposal_gpu.cpp | 24 +- .../clDNN/src/gpu/pyramid_roi_align_gpu.cpp | 24 +- .../thirdparty/clDNN/src/gpu/quantize_gpu.cpp | 28 +- .../thirdparty/clDNN/src/gpu/reduce_gpu.cpp | 34 +- .../clDNN/src/gpu/region_yolo_gpu.cpp | 22 +- .../thirdparty/clDNN/src/gpu/register_gpu.cpp | 99 + .../thirdparty/clDNN/src/gpu/register_gpu.hpp | 175 + .../thirdparty/clDNN/src/gpu/reorder_gpu.cpp | 20 +- .../clDNN/src/gpu/reorg_yolo_gpu.cpp | 26 +- .../thirdparty/clDNN/src/gpu/reshape_gpu.cpp | 14 +- .../clDNN/src/gpu/reverse_sequence_gpu.cpp | 24 +- .../clDNN/src/gpu/roi_pooling_gpu.cpp | 20 +- .../thirdparty/clDNN/src/gpu/scale_gpu.cpp | 45 +- .../clDNN/src/gpu/scale_grad_input_gpu.cpp | 42 +- .../clDNN/src/gpu/scale_grad_weights_gpu.cpp | 42 +- .../thirdparty/clDNN/src/gpu/select_gpu.cpp | 39 +- .../clDNN/src/gpu/shuffle_channels_gpu.cpp | 24 +- .../thirdparty/clDNN/src/gpu/softmax_gpu.cpp | 36 +- .../clDNN/src/gpu/softmax_loss_grad_gpu.cpp | 38 +- .../clDNN/src/gpu/strided_slice_gpu.cpp | 24 +- .../thirdparty/clDNN/src/gpu/tile_gpu.cpp | 24 +- .../clDNN/src/gpu/upsampling_gpu.cpp | 30 +- .../clDNN/src/gpu/wait_for_events_gpu.cpp | 24 +- .../graph_optimizer/add_required_reorders.cpp | 16 +- .../graph_optimizer/calculate_prior_boxes.cpp | 8 +- .../graph_optimizer/eltwise_remove_stride.cpp | 2 +- .../src/graph_optimizer/eltwise_shrinking.cpp | 3 + .../graph_optimizer/graph_initializations.cpp | 48 +- .../src/graph_optimizer/handle_reshape.cpp | 2 +- .../graph_optimizer/post_input_reorder.cpp | 6 +- .../graph_optimizer/post_optimize_weights.cpp | 91 +- .../src/graph_optimizer/pre_optimize_bias.cpp | 39 +- .../prep_opt_depthwise_sep_post.cpp | 4 +- .../graph_optimizer/prepare_binarization.cpp | 133 - .../graph_optimizer/prepare_buffer_fusing.cpp | 124 +- .../prepare_depthwise_sep_opt.cpp | 6 +- .../src/graph_optimizer/prepare_padding.cpp | 6 +- .../prepare_primitive_fusing.cpp | 640 +- .../graph_optimizer/prepare_quantization.cpp | 98 + .../graph_optimizer/propagate_constants.cpp | 4 +- .../remove_redundant_reorders.cpp | 290 +- .../src/graph_optimizer/reorder_inputs.cpp | 685 +- .../reverse_optional_nodes_outputs.cpp | 1 - .../strided_slice_optimize.cpp | 4 +- .../thirdparty/clDNN/src/half.cpp | 3 +- .../clDNN/src/include/activation_grad_inst.h | 2 +- .../clDNN/src/include/activation_inst.h | 2 +- .../thirdparty/clDNN/src/include/api_impl.h | 125 - .../clDNN/src/include/apply_adam_inst.h | 2 +- .../clDNN/src/include/arg_max_min_inst.h | 2 +- .../src/include/average_unpooling_inst.h | 2 +- .../clDNN/src/include/batch_norm_grad_inst.h | 2 +- .../clDNN/src/include/batch_norm_inst.h | 2 +- .../src/include/binary_convolution_inst.h | 46 +- .../clDNN/src/include/border_inst.h | 2 +- .../clDNN/src/include/broadcast_inst.h | 2 +- .../clDNN/src/include/concatenation_inst.h | 2 +- .../clDNN/src/include/condition_inst.h | 8 +- .../clDNN/src/include/contract_inst.h | 2 +- .../include/convolution_grad_weights_inst.h | 2 +- .../clDNN/src/include/convolution_inst.h | 70 +- .../thirdparty/clDNN/src/include/crop_inst.h | 2 +- .../src/include/custom_gpu_primitive_inst.h | 2 +- .../thirdparty/clDNN/src/include/data_inst.h | 2 +- .../clDNN/src/include/deconvolution_inst.h | 2 +- .../src/include/deformable_convolution_inst.h | 4 +- .../clDNN/src/include/depth_to_space_inst.h | 2 +- .../clDNN/src/include/detection_output_inst.h | 2 +- .../clDNN/src/include/eltwise_inst.h | 2 +- .../thirdparty/clDNN/src/include/embed_inst.h | 2 +- .../clDNN/src/include/engine_impl.h | 11 +- .../clDNN/src/include/error_handler.h | 2 +- .../thirdparty/clDNN/src/include/event_impl.h | 23 +- .../include/fully_connected_grad_input_inst.h | 2 +- .../fully_connected_grad_weights_inst.h | 2 +- .../clDNN/src/include/fully_connected_inst.h | 2 +- .../src/include/fused_conv_bn_scale_inst.h | 2 +- .../src/include/fused_conv_eltwise_inst.h | 6 +- .../clDNN/src/include/gather_inst.h | 2 +- .../clDNN/src/include/gather_tree_inst.h | 49 + .../thirdparty/clDNN/src/include/gemm_inst.h | 2 +- .../clDNN/src/include/generic_layer.h | 35 - .../clDNN/src/include/generic_layer.hpp | 18 +- .../clDNN/src/include/index_select_inst.h | 2 +- .../clDNN/src/include/input_layout_inst.h | 2 +- .../clDNN/src/include/internal_primitive.h | 2 +- .../include/internal_primitive_type_base.h | 7 +- .../src/include/kernel_selector_helper.h | 90 +- .../clDNN/src/include/layout_optimizer.h | 187 +- .../clDNN/src/include/lookup_table_inst.h | 2 +- .../thirdparty/clDNN/src/include/lrn_inst.h | 2 +- .../src/include/lstm_dynamic_input_inst.h | 2 +- .../clDNN/src/include/lstm_dynamic_inst.h | 2 +- .../src/include/lstm_dynamic_timeloop_inst.h | 2 +- .../clDNN/src/include/lstm_elt_inst.h | 6 +- .../clDNN/src/include/lstm_gemm_inst.h | 2 +- .../thirdparty/clDNN/src/include/lstm_inst.h | 10 +- .../clDNN/src/include/max_unpooling_inst.h | 2 +- .../clDNN/src/include/memory_impl.h | 18 +- .../clDNN/src/include/memory_pool.h | 5 +- .../thirdparty/clDNN/src/include/meta_utils.h | 2 +- .../clDNN/src/include/mutable_data_inst.h | 2 +- .../thirdparty/clDNN/src/include/mvn_inst.h | 2 +- .../clDNN/src/include/network_impl.h | 5 +- .../clDNN/src/include/normalize_inst.h | 2 +- .../clDNN/src/include/one_hot_inst.h | 2 +- .../clDNN/src/include/pass_manager.h | 54 +- .../clDNN/src/include/permute_inst.h | 2 +- .../clDNN/src/include/pooling_inst.h | 2 +- .../clDNN/src/include/primitive_inst.h | 14 +- .../clDNN/src/include/primitive_type.h | 36 +- .../clDNN/src/include/primitive_type_base.h | 9 +- .../clDNN/src/include/prior_box_inst.h | 4 +- .../clDNN/src/include/program_helpers.h | 14 +- .../clDNN/src/include/program_impl.h | 14 +- .../clDNN/src/include/program_node.h | 91 +- .../clDNN/src/include/proposal_inst.h | 2 +- .../src/include/pyramid_roi_align_inst.h | 2 +- .../clDNN/src/include/quantize_inst.h | 10 +- .../clDNN/src/include/reduce_inst.h | 2 +- .../clDNN/src/include/region_yolo_inst.h | 2 +- .../clDNN/src/include/reorder_inst.h | 2 +- .../clDNN/src/include/reorg_yolo_inst.h | 2 +- .../clDNN/src/include/reshape_inst.h | 10 +- .../clDNN/src/include/reverse_sequence_inst.h | 2 +- .../clDNN/src/include/roi_pooling_inst.h | 2 +- .../clDNN/src/include/scale_grad_input_inst.h | 2 +- .../src/include/scale_grad_weights_inst.h | 2 +- .../thirdparty/clDNN/src/include/scale_inst.h | 2 +- .../clDNN/src/include/select_inst.h | 2 +- .../clDNN/src/include/shuffle_channels_inst.h | 2 +- .../clDNN/src/include/sliding_window_utils.h | 4 +- .../clDNN/src/include/softmax_inst.h | 2 +- .../src/include/softmax_loss_grad_inst.h | 2 +- .../thirdparty/clDNN/src/include/split_inst.h | 2 +- .../clDNN/src/include/strided_slice_inst.h | 2 +- .../thirdparty/clDNN/src/include/tile_inst.h | 2 +- .../clDNN/src/include/to_string_utils.h | 13 +- .../clDNN/src/include/topology_impl.h | 9 +- .../clDNN/src/include/upsampling_inst.h | 2 +- .../thirdparty/clDNN/src/index_select.cpp | 2 +- .../thirdparty/clDNN/src/input_layout.cpp | 2 +- .../clDNN/src/kernel_selector_helper.cpp | 98 +- .../thirdparty/clDNN/src/layout_optimizer.cpp | 613 +- .../thirdparty/clDNN/src/lookup_table.cpp | 2 +- inference-engine/thirdparty/clDNN/src/lrn.cpp | 4 +- .../thirdparty/clDNN/src/lstm.cpp | 2 +- .../thirdparty/clDNN/src/lstm_dynamic.cpp | 2 +- .../clDNN/src/lstm_dynamic_input.cpp | 2 +- .../clDNN/src/lstm_dynamic_timeloop.cpp | 3 +- .../thirdparty/clDNN/src/lstm_elt.cpp | 2 +- .../thirdparty/clDNN/src/lstm_gemm.cpp | 2 +- .../thirdparty/clDNN/src/max_unpooling.cpp | 2 +- .../thirdparty/clDNN/src/memory.cpp | 86 + .../thirdparty/clDNN/src/memory_pool.cpp | 9 +- .../thirdparty/clDNN/src/mutable_data.cpp | 4 +- inference-engine/thirdparty/clDNN/src/mvn.cpp | 2 +- .../thirdparty/clDNN/src/network.cpp | 118 +- .../thirdparty/clDNN/src/normalize.cpp | 6 +- .../thirdparty/clDNN/src/one_hot.cpp | 4 +- .../thirdparty/clDNN/src/permute.cpp | 2 +- .../thirdparty/clDNN/src/pooling.cpp | 2 +- .../thirdparty/clDNN/src/prior_box.cpp | 2 +- .../thirdparty/clDNN/src/program.cpp | 215 +- .../thirdparty/clDNN/src/program_helpers.cpp | 10 +- .../thirdparty/clDNN/src/program_node.cpp | 26 +- .../thirdparty/clDNN/src/proposal.cpp | 2 +- .../clDNN/src/pyramid_roi_align.cpp | 2 +- .../thirdparty/clDNN/src/quantize.cpp | 18 +- .../thirdparty/clDNN/src/reduce.cpp | 2 +- .../thirdparty/clDNN/src/region_yolo.cpp | 2 +- .../thirdparty/clDNN/src/reorder.cpp | 8 +- .../thirdparty/clDNN/src/reorg_yolo.cpp | 2 +- .../thirdparty/clDNN/src/reshape.cpp | 2 +- .../thirdparty/clDNN/src/reverse_sequence.cpp | 2 +- .../thirdparty/clDNN/src/roi_pooling.cpp | 2 +- .../thirdparty/clDNN/src/scale.cpp | 2 +- .../thirdparty/clDNN/src/scale_grad_input.cpp | 2 +- .../clDNN/src/scale_grad_weights.cpp | 2 +- .../thirdparty/clDNN/src/select.cpp | 2 +- .../thirdparty/clDNN/src/shuffle_channels.cpp | 2 +- .../thirdparty/clDNN/src/softmax.cpp | 2 +- .../clDNN/src/softmax_loss_grad.cpp | 2 +- .../thirdparty/clDNN/src/split.cpp | 2 +- .../thirdparty/clDNN/src/strided_slice.cpp | 2 +- .../thirdparty/clDNN/src/tile.cpp | 2 +- .../thirdparty/clDNN/src/topology.cpp | 56 + .../thirdparty/clDNN/src/upsampling.cpp | 15 +- .../thirdparty/clDNN/tests/CMakeLists.txt | 4 +- .../tests/module_tests/events_pool_test.cpp | 20 +- .../tests/module_tests/gpu_toolkit_test.cpp | 16 +- .../module_tests/test_uqr_distribution.cpp | 106 +- .../test_cases/activation_grad_gpu_test.cpp | 48 +- .../test_cases/activation_simple_gpu_test.cpp | 206 +- .../test_cases/add_reorders_gpu_test.cpp | 30 +- .../tests/test_cases/apply_adam_gpu_test.cpp | 26 +- .../tests/test_cases/arg_max_gpu_test.cpp | 71 +- .../test_cases/average_unpooling_gpu_test.cpp | 20 +- .../clDNN/tests/test_cases/barriers_test.cpp | 14 +- .../tests/test_cases/batch_norm_gpu_test.cpp | 61 +- .../test_cases/batch_norm_grad_gpu_test.cpp | 16 +- .../binary_convolution_gpu_test.cpp | 21 +- .../tests/test_cases/border_gpu_test.cpp | 623 +- .../tests/test_cases/broadcast_gpu_test.cpp | 14 +- .../tests/test_cases/command_queue_test.cpp | 11 +- .../tests/test_cases/condition_gpu_test.cpp | 50 +- .../tests/test_cases/contract_gpu_test.cpp | 14 +- .../tests/test_cases/convolution_gpu_test.cpp | 401 +- .../convolution_grad_input_gpu_test.cpp | 17 +- .../convolution_grad_weights_gpu_test.cpp | 42 +- .../clDNN/tests/test_cases/crop_gpu_test.cpp | 65 +- .../test_cases/custom_gpu_primitive_test.cpp | 36 +- .../test_cases/deconvolution_gpu_test.cpp | 90 +- .../test_cases/depth_concatenate_gpu_test.cpp | 71 +- .../test_cases/depth_to_space_gpu_test.cpp | 12 +- .../test_cases/detection_output_test.cpp | 13 +- .../tests/test_cases/eltwise_gpu_test.cpp | 79 +- .../clDNN/tests/test_cases/embed_gpu_test.cpp | 18 +- .../test_cases/fully_connected_gpu_test.cpp | 66 +- .../fully_connected_grad_input_gpu_test.cpp | 15 +- .../fully_connected_grad_weights_gpu_test.cpp | 22 +- .../fused_conv_eltwise_gpu_test.cpp | 23 +- .../tests/test_cases/fusings_gpu_test.cpp | 440 + .../tests/test_cases/gather_gpu_test.cpp | 12 +- .../clDNN/tests/test_cases/gemm_gpu_test.cpp | 25 +- .../test_cases/index_select_gpu_test.cpp | 27 +- .../tests/test_cases/lookup_table_test.cpp | 15 +- .../test_cases/lstm_dynamic_gpu_test.cpp | 222 +- .../clDNN/tests/test_cases/lstm_gpu_test.cpp | 118 +- .../test_cases/max_unpooling_gpu_test.cpp | 21 +- .../clDNN/tests/test_cases/memory_test.cpp | 117 +- .../clDNN/tests/test_cases/mvn_gpu_test.cpp | 14 +- .../tests/test_cases/one_hot_gpu_test.cpp | 12 +- .../tests/test_cases/permute_gpu_test.cpp | 26 +- .../tests/test_cases/pooling_gpu_test.cpp | 57 +- .../propagate_constants_gpu_test.cpp | 18 +- .../tests/test_cases/proposal_cpu_test.cpp | 12 +- .../tests/test_cases/proposal_test_data.cpp | 3 - .../test_cases/pyramid_roi_align_gpu_test.cpp | 15 +- .../tests/test_cases/quantize_gpu_test.cpp | 232 +- .../tests/test_cases/reduce_gpu_test.cpp | 12 +- .../test_cases/removing_output_node_test.cpp | 18 +- .../tests/test_cases/reorder_gpu_test.cpp | 129 +- .../tests/test_cases/reshape_gpu_test.cpp | 18 +- .../test_cases/reverse_sequence_gpu_test.cpp | 12 +- .../clDNN/tests/test_cases/scale_gpu_test.cpp | 33 +- .../test_cases/scale_grad_input_test.cpp | 12 +- .../test_cases/scale_grad_weights_test.cpp | 16 +- .../tests/test_cases/select_gpu_test.cpp | 12 +- .../test_cases/shuffle_channels_test.cpp | 13 +- .../tests/test_cases/softmax_gpu_test.cpp | 30 +- .../test_cases/softmax_loss_grad_gpu_test.cpp | 14 +- .../spatial_concatenate_gpu_test.cpp | 13 +- .../clDNN/tests/test_cases/split_gpu_test.cpp | 19 +- .../clDNN/tests/test_cases/streams_test.cpp | 14 +- .../test_cases/strided_slice_gpu_test.cpp | 14 +- .../clDNN/tests/test_cases/tensor_test.cpp | 8 +- .../clDNN/tests/test_cases/tile_gpu_test.cpp | 16 +- .../clDNN/tests/test_cases/topology_test.cpp | 34 +- .../test_cases/trim_to_outputs_gpu_test.cpp | 15 +- .../tests/test_cases/upsampling_gpu_test.cpp | 124 +- .../clDNN/tests/test_utils/float16.h | 2 - .../tests/test_utils/instrumentation.cpp | 9 +- .../clDNN/tests/test_utils/instrumentation.h | 2 +- .../clDNN/tests/test_utils/random_gen.h | 2 - .../clDNN/tests/test_utils/test_utils.cpp | 18 +- .../clDNN/tests/test_utils/test_utils.h | 68 +- .../uniform_quantized_real_distribution.hpp | 37 - .../clDNN/tests_core_internal/CMakeLists.txt | 184 +- .../graph_manipulation_gpu_test.cpp | 19 +- .../test_cases/prepare_conv_eltw_fusing.cpp | 12 +- .../thirdparty/fluid/checksum.txt | 2 +- .../fluid/modules/gapi/CMakeLists.txt | 1 + .../fluid/modules/gapi/cmake/standalone.cmake | 1 + .../modules/gapi/include/opencv2/gapi.hpp | 14 +- .../gapi/include/opencv2/gapi/core.hpp | 55 +- .../gapi/include/opencv2/gapi/cpu/core.hpp | 2 +- .../include/opencv2/gapi/cpu/gcpukernel.hpp | 15 +- .../opencv2/gapi/fluid/gfluidbuffer.hpp | 6 +- .../opencv2/gapi/fluid/gfluidkernel.hpp | 25 +- .../gapi/include/opencv2/gapi/garg.hpp | 18 +- .../gapi/include/opencv2/gapi/garray.hpp | 32 +- .../include/opencv2/gapi/gasync_context.hpp | 38 + .../gapi/include/opencv2/gapi/gcall.hpp | 8 +- .../gapi/include/opencv2/gapi/gcommon.hpp | 12 +- .../gapi/include/opencv2/gapi/gcompiled.hpp | 6 +- .../include/opencv2/gapi/gcompiled_async.hpp | 14 +- .../include/opencv2/gapi/gcompoundkernel.hpp | 21 +- .../include/opencv2/gapi/gcomputation.hpp | 12 +- .../opencv2/gapi/gcomputation_async.hpp | 19 +- .../gapi/include/opencv2/gapi/gkernel.hpp | 193 +- .../gapi/include/opencv2/gapi/gmat.hpp | 6 +- .../gapi/include/opencv2/gapi/gmetaarg.hpp | 10 +- .../gapi/include/opencv2/gapi/gproto.hpp | 12 +- .../gapi/include/opencv2/gapi/gpu/core.hpp | 4 +- .../include/opencv2/gapi/gpu/ggpukernel.hpp | 4 +- .../gapi/include/opencv2/gapi/gpu/imgproc.hpp | 4 +- .../gapi/include/opencv2/gapi/gscalar.hpp | 2 +- .../gapi/include/opencv2/gapi/gtransform.hpp | 103 + .../gapi/include/opencv2/gapi/gtyped.hpp | 8 +- .../gapi/include/opencv2/gapi/imgproc.hpp | 136 +- .../include/opencv2/gapi/ocl/goclkernel.hpp | 5 +- .../gapi/include/opencv2/gapi/operators.hpp | 4 +- .../gapi/include/opencv2/gapi/own/assert.hpp | 2 +- .../gapi/include/opencv2/gapi/own/convert.hpp | 2 +- .../gapi/include/opencv2/gapi/own/cvdefs.hpp | 4 + .../gapi/include/opencv2/gapi/own/exports.hpp | 7 +- .../gapi/include/opencv2/gapi/own/mat.hpp | 12 +- .../gapi/include/opencv2/gapi/render.hpp | 113 + .../gapi/include/opencv2/gapi/util/any.hpp | 2 +- .../include/opencv2/gapi/util/optional.hpp | 2 +- .../gapi/include/opencv2/gapi/util/util.hpp | 36 +- .../include/opencv2/gapi/util/variant.hpp | 4 +- .../gapi/perf/common/gapi_core_perf_tests.hpp | 2 +- .../perf/common/gapi_imgproc_perf_tests.hpp | 5 +- .../common/gapi_imgproc_perf_tests_inl.hpp | 169 +- .../perf/cpu/gapi_core_perf_tests_cpu.cpp | 2 +- .../perf/cpu/gapi_imgproc_perf_tests_cpu.cpp | 17 +- .../cpu/gapi_imgproc_perf_tests_fluid.cpp | 15 + .../fluid/modules/gapi/perf/perf_precomp.hpp | 22 +- .../modules/gapi/samples/api_ref_snippets.cpp | 3 +- .../fluid/modules/gapi/src/api/garray.cpp | 2 +- .../fluid/modules/gapi/src/api/gbackend.cpp | 11 +- .../modules/gapi/src/api/gbackend_priv.hpp | 2 + .../fluid/modules/gapi/src/api/gcall.cpp | 2 +- .../modules/gapi/src/api/gcomputation.cpp | 12 +- .../fluid/modules/gapi/src/api/gkernel.cpp | 129 +- .../fluid/modules/gapi/src/api/gmat.cpp | 2 +- .../fluid/modules/gapi/src/api/gorigin.hpp | 8 +- .../fluid/modules/gapi/src/api/gproto.cpp | 6 +- .../fluid/modules/gapi/src/api/gscalar.cpp | 4 +- .../modules/gapi/src/api/kernels_core.cpp | 13 +- .../modules/gapi/src/api/kernels_imgproc.cpp | 30 +- .../fluid/modules/gapi/src/api/operators.cpp | 8 +- .../fluid/modules/gapi/src/api/render.cpp | 91 + .../modules/gapi/src/api/render_priv.hpp | 30 + .../src/backends/common/gcompoundbackend.cpp | 2 +- .../src/backends/common/gcompoundkernel.cpp | 2 +- .../gapi/src/backends/cpu/gcpubackend.cpp | 10 +- .../gapi/src/backends/cpu/gcpubackend.hpp | 6 +- .../gapi/src/backends/cpu/gcpucore.cpp | 23 +- .../gapi/src/backends/cpu/gcpuimgproc.cpp | 81 +- .../gapi/src/backends/cpu/gcpukernel.cpp | 2 +- .../gapi/src/backends/fluid/gfluidbackend.cpp | 154 +- .../gapi/src/backends/fluid/gfluidbackend.hpp | 63 +- .../gapi/src/backends/fluid/gfluidbuffer.cpp | 8 +- .../gapi/src/backends/fluid/gfluidcore.cpp | 16 +- .../gapi/src/backends/fluid/gfluidimgproc.cpp | 140 +- .../fluid/gfluidimgproc_func.dispatch.cpp | 39 +- .../src/backends/fluid/gfluidimgproc_func.hpp | 25 +- .../fluid/gfluidimgproc_func.simd.hpp | 577 +- .../gapi/src/backends/ocl/goclbackend.cpp | 26 +- .../gapi/src/backends/ocl/goclbackend.hpp | 6 +- .../gapi/src/backends/ocl/goclcore.cpp | 4 +- .../gapi/src/backends/ocl/goclcore.hpp | 2 +- .../gapi/src/backends/ocl/goclimgproc.cpp | 4 +- .../gapi/src/backends/ocl/goclimgproc.hpp | 2 +- .../gapi/src/backends/ocl/goclkernel.cpp | 2 +- .../modules/gapi/src/compiler/gcompiled.cpp | 4 +- .../modules/gapi/src/compiler/gcompiler.cpp | 28 +- .../modules/gapi/src/compiler/gcompiler.hpp | 6 +- .../gapi/src/compiler/gislandmodel.hpp | 4 +- .../modules/gapi/src/compiler/gmodel.cpp | 6 +- .../modules/gapi/src/compiler/gmodel.hpp | 8 +- .../gapi/src/compiler/gmodelbuilder.hpp | 4 +- .../gapi/src/compiler/passes/dump_dot.cpp | 2 +- .../modules/gapi/src/compiler/passes/exec.cpp | 2 +- .../gapi/src/compiler/passes/helpers.cpp | 2 +- .../gapi/src/compiler/passes/kernels.cpp | 10 +- .../modules/gapi/src/compiler/passes/meta.cpp | 2 +- .../gapi/src/compiler/passes/passes.hpp | 5 +- .../modules/gapi/src/executor/gasync.cpp | 129 +- .../modules/gapi/src/executor/gexecutor.cpp | 28 +- .../fluid/modules/gapi/src/precomp.hpp | 12 +- .../test/common/gapi_compoundkernel_tests.cpp | 26 +- .../gapi/test/common/gapi_core_tests.hpp | 197 +- .../gapi/test/common/gapi_core_tests_inl.hpp | 749 +- .../gapi/test/common/gapi_imgproc_tests.hpp | 67 +- .../test/common/gapi_imgproc_tests_inl.hpp | 439 +- .../gapi/test/common/gapi_operators_tests.hpp | 10 +- .../test/common/gapi_operators_tests_inl.hpp | 30 +- .../gapi/test/common/gapi_render_tests.cpp | 9 + .../gapi/test/common/gapi_render_tests.hpp | 73 + .../test/common/gapi_render_tests_inl.hpp | 96 + .../gapi/test/common/gapi_tests_common.hpp | 269 +- .../gapi/test/common/gapi_tests_helpers.hpp | 67 + .../gapi/test/cpu/gapi_core_tests_cpu.cpp | 319 +- .../gapi/test/cpu/gapi_core_tests_fluid.cpp | 200 +- .../gapi/test/cpu/gapi_imgproc_tests_cpu.cpp | 309 +- .../test/cpu/gapi_imgproc_tests_fluid.cpp | 204 +- .../test/cpu/gapi_operators_tests_cpu.cpp | 62 +- .../test/cpu/gapi_operators_tests_fluid.cpp | 59 +- .../gapi/test/cpu/gapi_render_tests_cpu.cpp | 66 + .../modules/gapi/test/gapi_async_test.cpp | 269 +- .../gapi/test/gapi_basic_hetero_tests.cpp | 2 +- .../modules/gapi/test/gapi_desc_tests.cpp | 2 +- .../test/gapi_fluid_parallel_rois_test.cpp | 315 + .../gapi/test/gapi_fluid_resize_test.cpp | 7 +- .../modules/gapi/test/gapi_fluid_test.cpp | 6 +- .../gapi/test/gapi_fluid_test_kernels.cpp | 4 +- .../gapi/test/gapi_fluid_test_kernels.hpp | 2 +- .../gapi/test/gapi_gcomputation_tests.cpp | 71 +- .../fluid/modules/gapi/test/gapi_gpu_test.cpp | 2 +- .../modules/gapi/test/gapi_kernel_tests.cpp | 389 +- .../modules/gapi/test/gapi_mock_kernels.hpp | 2 +- .../gapi/test/gapi_sample_pipelines.cpp | 16 + .../gapi/test/gapi_transform_tests.cpp | 189 + .../modules/gapi/test/gapi_util_tests.cpp | 2 +- .../gapi/test/gpu/gapi_core_tests_gpu.cpp | 285 +- .../gapi/test/gpu/gapi_imgproc_tests_gpu.cpp | 234 +- .../test/gpu/gapi_operators_tests_gpu.cpp | 60 +- .../internal/gapi_int_gmodel_builder_test.cpp | 2 +- .../internal/gapi_int_recompilation_test.cpp | 9 +- .../gapi/test/opencl_kernels_test_gapi.hpp | 6 +- .../gapi/test/own/gapi_types_tests.cpp | 2 +- .../fluid/modules/gapi/test/own/mat_tests.cpp | 2 +- .../modules/gapi/test/own/scalar_tests.cpp | 2 +- .../fluid/modules/gapi/test/test_precomp.hpp | 24 +- .../modules/gapi/test/util/any_tests.cpp | 2 +- .../modules/gapi/test/util/optional_tests.cpp | 2 +- .../modules/gapi/test/util/variant_tests.cpp | 2 +- .../thirdparty/fluid/revision.txt | 2 +- .../thirdparty/mkl-dnn/include/mkldnn.h | 3 + .../thirdparty/mkl-dnn/include/mkldnn.hpp | 5 + .../thirdparty/mkl-dnn/src/common/memory.cpp | 8 +- .../mkl-dnn/src/common/primitive.hpp | 3 +- .../mkl-dnn/src/cpu/cpu_isa_traits.hpp | 4 + .../thirdparty/mkl-dnn/src/cpu/cpu_memory.hpp | 4 +- .../src/cpu/jit_uni_bin_conv_kernel.cpp | 56 +- .../mkl-dnn/src/cpu/ref_depthwise.cpp | 4 +- .../thirdparty/movidius/CMakeLists.txt | 7 +- .../movidius/WinPthread/pthread_semaphore.c | 265 + .../movidius/WinPthread/pthread_semaphore.h | 48 + .../thirdparty/movidius/XLink/CMakeLists.txt | 57 +- .../thirdparty/movidius/XLink/XLink.cmake | 36 + .../movidius/XLink/pc/XLinkPlatform.c | 433 +- .../thirdparty/movidius/XLink/pc/pcie_host.c | 287 +- .../thirdparty/movidius/XLink/pc/pcie_host.h | 44 +- .../thirdparty/movidius/XLink/pc/usb_boot.c | 243 +- .../thirdparty/movidius/XLink/pc/usb_boot.h | 7 +- .../thirdparty/movidius/XLink/shared/XLink.c | 905 +- .../thirdparty/movidius/XLink/shared/XLink.h | 34 +- .../movidius/XLink/shared/XLinkDispatcher.c | 293 +- .../movidius/XLink/shared/XLinkDispatcher.h | 4 +- .../movidius/XLink/shared/XLinkPlatform.h | 40 +- .../XLink/shared/XLinkPlatform_tool.h | 41 + .../XLink/shared/XLinkPrivateDefines.h | 79 +- .../XLink/shared/XLinkPublicDefines.h | 24 +- .../movidius/XLink/shared/XLinkVersion.h | 1 - .../movidius/XLink/shared/XLink_tool.h | 100 + .../movidius/XLink/tests/CMakeLists.txt | 23 - .../movidius/XLink/tests/XLink_tests.cpp | 390 - .../thirdparty/movidius/mvnc/CMakeLists.txt | 4 +- .../thirdparty/movidius/mvnc/include/mvnc.h | 2 + .../movidius/mvnc/include/mvnc_data.h | 4 + .../movidius/mvnc/include/mvnc_tool.h | 4 + .../movidius/mvnc/include/ncCommPrivate.h | 7 + .../movidius/mvnc/include/ncPrivateTypes.h | 11 +- .../thirdparty/movidius/mvnc/src/mvnc_api.c | 685 +- .../thirdparty/movidius/mvnc/src/mvnc_data.c | 20 +- .../movidius/mvnc/tests/mvnc_tests_common.cpp | 101 +- .../movidius/mvnc/tests/mvnc_tests_common.hpp | 89 +- .../movidius/mvnc/tests/mvnc_tests_usb.cpp | 10 +- .../movidius/shared/include/mvLog.h | 4 + .../movidius/shared/include/mvStringUtils.h | 20 +- .../movidius/shared/src/mvStringUtils.c | 20 +- .../thirdparty/movidius/watchdog/watchdog.cpp | 23 +- .../thirdparty/movidius/watchdog/watchdog.h | 3 +- inference-engine/thirdparty/ngraph.cmake | 12 +- .../thirdparty/stb_lib/CMakeLists.txt | 16 +- inference-engine/tools/CMakeLists.txt | 1 - .../accuracy_checker_tool/accuracy_check.py | 2 +- .../convert_annotation.py | 2 +- .../tools/benchmark_tool/README.md | 198 +- .../tools/benchmark_tool/benchmark.py | 22 - .../tools/benchmark_tool/benchmark_app.py | 200 + .../tools/benchmark_tool/parameters.py | 98 + .../tools/benchmark_tool/requirements.txt | 21 +- .../tools/calibration_tool/README.md | 170 +- .../statistics_collector/CMakeLists.txt | 6 +- .../statistics_collector/data_stats.cpp | 2 +- .../statistics_collector/data_stats.hpp | 4 +- .../statistics_collector/main.cpp | 13 +- .../statistics_processor.cpp | 5 +- inference-engine/tools/vpu/CMakeLists.txt | 1 + .../tools/vpu/common/vpu_tools_common.cpp | 2 +- .../tools/vpu/vpu_compile/CMakeLists.txt | 2 +- .../tools/vpu/vpu_compile/main.cpp | 2 +- .../tools/vpu/vpu_perfcheck/CMakeLists.txt | 69 + .../tools/vpu/vpu_perfcheck/main.cpp | 788 + .../tools/vpu/vpu_profile/README.md | 11 +- .../tools/vpu/vpu_profile/main.cpp | 2 +- model-optimizer/README.md | 33 +- model-optimizer/extensions/back/CutMemory.py | 65 + .../extensions/back/CutMemory_test.py | 71 + .../extensions/back/FuseReshapesSequence.py | 5 + model-optimizer/extensions/back/Gather0D.py | 61 + .../extensions/back/ReduceToPooling.py | 14 +- .../extensions/back/ReduceToPooling_test.py | 52 +- .../extensions/back/ScalarConstNormalize.py | 34 + .../extensions/front/mxnet/conv_ext.py | 4 + .../extensions/front/mxnet/conv_ext_test.py | 67 + .../extensions/front/mxnet/elementwise_ext.py | 219 +- .../front/mxnet/eltwise_scalar_replacers.py | 65 +- .../extensions/front/mxnet/expand_dims_ext.py | 31 + .../front/onnx/constant_of_shape_ext.py | 33 + .../onnx/constant_of_shape_to_broadcast.py | 41 + .../extensions/front/onnx/elementwise_ext.py | 62 +- .../extensions/front/onnx/expand_ext.py | 28 + .../extensions/front/onnx/floor_ext.py | 28 + .../extensions/front/onnx/not_ext.py | 15 +- .../extensions/front/onnx/reduce_min_ext.py | 33 + .../extensions/front/onnx/slice_ext.py | 4 +- .../extensions/front/onnx/top_k_ext.py | 30 + .../front/reduce_axis_normalizer.py | 2 + .../front/tf/BatchToSpaceNDToUpsample.py | 108 + .../front/tf/InterpolateTransposes.py | 58 + .../extensions/front/tf/ObjectDetectionAPI.py | 26 +- .../extensions/front/tf/elementwise_ext.py | 22 +- .../front/tf/sparse_fill_empty_rows_ext.py | 33 + model-optimizer/extensions/front/tf/swish.py | 37 + .../extensions/front/tf/swish_test.py | 56 + .../extensions/front/tf/unique_ext.py | 39 + .../extensions/middle/BiasAddBroadcasting.py | 75 + model-optimizer/extensions/middle/Cast.py | 15 +- .../extensions/middle/EltwiseInputReshape.py | 24 +- .../extensions/middle/InsertSelect.py | 146 + .../extensions/middle/InsertSelect_test.py | 271 + .../middle/RemoveDuplicationMemory.py | 127 +- .../middle/RemoveDuplicationMemory_test.py | 70 +- .../extensions/middle/RemoveUselessCrops.py | 46 +- .../middle/RemoveUselessCrops_test.py | 87 + .../middle/ReplaceMemoryOffsetWithSplice.py | 60 +- .../ReplaceMemoryOffsetWithSplice_test.py | 24 +- .../extensions/middle/ReplacePNorm.py | 62 + .../middle/ReplacePNormNodePattern_test.py | 76 + .../middle/ReplaceSpliceNodePattern.py | 88 +- .../middle/ReplaceSpliceNodePattern_test.py | 148 +- .../extensions/middle/SliceConvert_test.py | 202 + .../extensions/middle/SliceConverter.py | 102 +- .../extensions/middle/UpsampleToResample.py | 18 +- model-optimizer/extensions/ops/Cast.py | 11 + model-optimizer/extensions/ops/ReduceOps.py | 21 +- .../extensions/ops/activation_ops.py | 7 +- .../extensions/ops/detectionoutput_onnx.py | 18 +- model-optimizer/extensions/ops/elementwise.py | 41 +- model-optimizer/extensions/ops/gather.py | 2 +- .../extensions/ops/non_max_suppression.py | 55 + model-optimizer/extensions/ops/pnorm.py | 42 + model-optimizer/extensions/ops/range.py | 2 +- .../ops/roifeatureextractor_onnx.py | 8 +- .../extensions/ops/sparse_fill_empty_rows.py | 84 + .../ops/sparse_fill_empty_rows_test.py | 128 + model-optimizer/extensions/ops/splice.py | 3 +- model-optimizer/extensions/ops/unique.py | 171 + model-optimizer/extensions/ops/unique_test.py | 273 + .../mo/back/ie_ir_ver_2/emitter.py | 4 +- .../mo/front/common/partial_infer/concat.py | 2 +- .../mo/front/common/partial_infer/eltwise.py | 10 +- .../partial_infer/multi_box_detection.py | 8 +- .../common/partial_infer/space_to_batch.py | 10 +- .../mo/front/common/partial_infer/split.py | 7 +- .../kaldi/extractors/pnorm_component_ext.py | 58 + .../extractors/pnorm_component_ext_test.py | 41 + .../kaldi/extractors/splice_component_ext.py | 7 + .../mo/front/kaldi/loader/loader.py | 51 +- .../mo/front/kaldi/loader/loader_test.py | 26 +- .../mo/front/kaldi/loader/utils.py | 34 +- .../mo/front/kaldi/loader/utils_test.py | 7 +- model-optimizer/mo/front/mxnet/extractor.py | 1 - model-optimizer/mo/front/tf/extractor.py | 1 + model-optimizer/mo/graph/graph.py | 13 +- model-optimizer/mo/graph/port.py | 4 +- .../mo/middle/passes/convert_data_type.py | 7 + model-optimizer/mo/middle/passes/eliminate.py | 11 +- model-optimizer/mo/ops/broadcast.py | 12 +- model-optimizer/mo/ops/constant_of_shape.py | 41 + model-optimizer/mo/ops/crop.py | 6 +- model-optimizer/mo/ops/expand_dims.py | 4 +- model-optimizer/mo/ops/memoryoffset.py | 35 +- model-optimizer/mo/ops/slice.py | 72 +- model-optimizer/mo/ops/squeeze.py | 4 +- model-optimizer/mo/ops/strided_slice.py | 3 +- model-optimizer/mo/ops/unsqueeze.py | 6 +- model-optimizer/mo/pipeline/caffe.py | 9 +- model-optimizer/mo/pipeline/kaldi.py | 31 +- model-optimizer/mo/pipeline/mx.py | 6 + model-optimizer/mo/pipeline/onnx.py | 7 + model-optimizer/mo/pipeline/tf.py | 7 + model-optimizer/mo/utils/cli_parser.py | 26 +- model-optimizer/mo/utils/error.py | 7 +- model-optimizer/mo/utils/graph.py | 3 +- model-optimizer/mo/utils/logger.py | 21 +- model-optimizer/mo/utils/pipeline_config.py | 7 +- .../mo/utils/pipeline_config_test.py | 3 + model-optimizer/mo/utils/unittest/graph.py | 90 +- .../mo/utils/unittest/ir_engine.py | 332 + .../mo/utils/unittest/ir_engine_test.py | 136 + ..._synthetic_gru_bidirectional_FP16_1_v6.bin | Bin 0 -> 222768 bytes ..._synthetic_gru_bidirectional_FP16_1_v6.xml | 626 + ...c_gru_bidirectional_FP16_1_v6_negative.xml | 626 + model-optimizer/mo/utils/versions_checker.py | 55 +- .../mo/utils/versions_checker_test.py | 55 + model-optimizer/requirements.txt | 1 - model-optimizer/requirements_caffe.txt | 3 +- model-optimizer/requirements_dev.txt | 8 + model-optimizer/requirements_kaldi.txt | 1 - model-optimizer/requirements_mxnet.txt | 3 +- model-optimizer/requirements_onnx.txt | 3 +- model-optimizer/requirements_tf.txt | 3 +- tools/.gitignore | 1 + tools/accuracy_checker/.pylintrc | 31 - tools/accuracy_checker/README.md | 60 - .../accuracy_checker/__init__.py | 17 - .../accuracy_checker/adapters/README.md | 73 - .../accuracy_checker/adapters/__init__.py | 80 - .../adapters/action_recognition.py | 119 - .../accuracy_checker/adapters/adapter.py | 91 - .../adapters/attributes_recognition.py | 211 - .../adapters/classification.py | 45 - .../accuracy_checker/adapters/detection.py | 501 - .../adapters/dummy_adapters.py | 64 - .../accuracy_checker/adapters/hit_ratio.py | 47 - .../adapters/image_processing.py | 35 - .../adapters/pose_estimation.py | 331 - .../adapters/reidentification.py | 58 - .../accuracy_checker/adapters/segmentation.py | 74 - .../adapters/text_detection.py | 309 - .../annotation_converters/README.md | 108 - .../annotation_converters/__init__.py | 63 - .../annotation_converters/_reid_common.py | 45 - .../annotation_converters/bitvehicle.py | 115 - .../annotation_converters/brats.py | 60 - .../annotation_converters/cityscapes.py | 73 - .../annotation_converters/convert.py | 155 - .../detection_opencv_storage.py | 114 - .../annotation_converters/format_converter.py | 108 - .../annotation_converters/icdar.py | 63 - .../annotation_converters/imagenet.py | 52 - .../annotation_converters/lfw.py | 111 - .../annotation_converters/mapillary_20.py | 79 - .../annotation_converters/market1501.py | 41 - .../annotation_converters/mars.py | 38 - .../annotation_converters/mighty.py | 34 - .../annotation_converters/ms_coco.py | 129 - .../annotation_converters/ncf_converter.py | 75 - .../annotation_converters/pascal_voc.py | 158 - .../annotation_converters/sample_converter.py | 100 - .../super_resolution_converter.py | 52 - .../vgg_face_regression.py | 64 - .../annotation_converters/wider.py | 63 - .../accuracy_checker/config/__init__.py | 48 - .../accuracy_checker/config/config_reader.py | 414 - .../config/config_validator.py | 341 - .../accuracy_checker/data_readers/__init__.py | 48 - .../data_readers/data_reader.py | 267 - .../accuracy_checker/dataset.py | 155 - .../accuracy_checker/dependency.py | 108 - .../accuracy_checker/evaluators/__init__.py | 8 - .../evaluators/model_evaluator.py | 162 - .../evaluators/pipeline_evaluator.py | 229 - .../accuracy_checker/launcher/__init__.py | 35 - .../launcher/caffe_installation_readme.md | 56 - .../launcher/caffe_launcher.py | 135 - .../launcher/caffe_launcher_readme.md | 24 - .../launcher/dlsdk_launcher.py | 474 - .../launcher/dlsdk_launcher_readme.md | 55 - .../launcher/dummy_launcher.py | 73 - .../accuracy_checker/launcher/input_feeder.py | 151 - .../accuracy_checker/launcher/launcher.py | 164 - .../launcher/loaders/__init__.py | 26 - .../launcher/loaders/loader.py | 54 - .../launcher/loaders/pickle_loader.py | 34 - .../launcher/loaders/xml_loader.py | 29 - .../launcher/model_conversion.py | 201 - .../accuracy_checker/logging.py | 134 - .../accuracy_checker/accuracy_checker/main.py | 239 - .../accuracy_checker/metrics/README.md | 127 - .../accuracy_checker/metrics/__init__.py | 93 - .../accuracy_checker/metrics/average_meter.py | 46 - .../metrics/character_recognition.py | 35 - .../metrics/classification.py | 136 - .../accuracy_checker/metrics/coco_metrics.py | 317 - .../accuracy_checker/metrics/detection.py | 473 - .../accuracy_checker/metrics/hit_ratio.py | 97 - .../accuracy_checker/metrics/metric.py | 171 - .../metrics/metric_executor.py | 120 - .../metrics/multilabel_recognition.py | 185 - .../accuracy_checker/metrics/overlap.py | 71 - .../accuracy_checker/metrics/regression.py | 357 - .../accuracy_checker/metrics/reid.py | 369 - .../metrics/semantic_segmentation.py | 134 - .../metrics/text_detection.py | 119 - .../pipeline_connectors/__init__.py | 7 - .../pipeline_connectors/connectors.py | 69 - .../accuracy_checker/postprocessor/README.md | 40 - .../postprocessor/__init__.py | 69 - .../postprocessor/cast_to_int.py | 67 - .../postprocessor/clip_boxes.py | 63 - .../postprocessor/clip_points.py | 63 - .../postprocessor/clip_segmentation_mask.py | 47 - .../postprocessor/correct_yolo_v2_boxes.py | 71 - .../postprocessor/crop_segmentation_mask.py | 48 - .../postprocessor/encode_segmentation_mask.py | 46 - .../postprocessor/extend_segmentation_mask.py | 60 - .../accuracy_checker/postprocessor/filter.py | 316 - .../accuracy_checker/postprocessor/nms.py | 85 - .../normalize_landmarks_points.py | 55 - .../postprocessor/postprocessing_executor.py | 84 - .../postprocessor/postprocessor.py | 184 - .../postprocessor/resize_prediction_boxes.py | 40 - .../postprocessor/resize_segmentation_mask.py | 70 - .../postprocessor/zoom_segmentation_mask.py | 61 - .../accuracy_checker/preprocessor/README.md | 55 - .../accuracy_checker/preprocessor/__init__.py | 51 - .../preprocessor/preprocessing_executor.py | 57 - .../preprocessor/preprocessors.py | 642 - .../accuracy_checker/presenters.py | 159 - .../accuracy_checker/progress_reporters.py | 100 - .../representation/__init__.py | 103 - .../representation/base_representation.py | 42 - .../character_recognition_representation.py | 31 - .../classification_representation.py | 44 - .../detection_representation.py | 87 - .../hit_ratio_representation.py | 40 - .../representation/multilabel_recognition.py | 32 - .../pose_estimation_representation.py | 63 - .../regression_representation.py | 72 - .../representation/reid_representation.py | 42 - .../representation/representaton_container.py | 78 - .../segmentation_representation.py | 94 - .../super_resolution_representation.py | 67 - .../text_detection_representation.py | 46 - .../accuracy_checker/utils.py | 385 - .../configs/face-detection-adas-0001.yml | 106 - .../configs/face-detection-retail-0004.yml | 113 - .../face-reidentification-retail-0095.yml | 96 - .../configs/human-pose-estimation-0001.yml | 155 - .../landmarks-regression-retail-0009.yml | 106 - .../person-reidentification-retail-0031.yml | 110 - .../person-reidentification-retail-0076.yml | 106 - .../person-reidentification-retail-0079.yml | 106 - .../configs/resnet50-binary-0001.yml | 40 - .../configs/text-detection-0002.yml | 140 - .../configs/text-recognition-0012.yml | 100 - tools/accuracy_checker/data/test_data/1.jpg | Bin 147595 -> 0 bytes .../data/test_models/SampLeNet.bin | Bin 248024 -> 0 bytes .../data/test_models/SampLeNet.caffemodel | Bin 248617 -> 0 bytes .../data/test_models/SampLeNet.prototxt | 116 - .../data/test_models/SampLeNet.xml | 239 - tools/accuracy_checker/pylint_checkers.py | 144 - tools/accuracy_checker/requirements.txt | 11 - tools/accuracy_checker/sample/README.md | 30 - .../accuracy_checker/sample/sample_config.yml | 66 - tools/accuracy_checker/setup.cfg | 8 - tools/accuracy_checker/tests/__init__.py | 16 - tools/accuracy_checker/tests/common.py | 132 - tools/accuracy_checker/tests/conftest.py | 52 - tools/accuracy_checker/tests/test_adapters.py | 121 - .../tests/test_caffe_launcher.py | 77 - .../tests/test_config_reader.py | 1295 -- .../tests/test_config_validator.py | 385 - tools/accuracy_checker/tests/test_dataset.py | 220 - .../accuracy_checker/tests/test_dependency.py | 89 - .../tests/test_detection_metrics.py | 459 - .../tests/test_dlsdk_launcher.py | 1121 - .../tests/test_input_feeder.py | 255 - .../tests/test_metric_evaluator.py | 482 - .../tests/test_model_conversion.py | 80 - .../tests/test_model_evaluator.py | 147 - .../tests/test_postprocessor.py | 1070 - .../tests/test_preprocessor.py | 611 - .../accuracy_checker/tests/test_presenter.py | 552 - .../tests/test_regression_metrics.py | 342 - .../tests/test_reid_metrics.py | 77 - .../tests/test_segmentation_metrics.py | 164 - tools/accuracy_checker/tests/test_utils.py | 127 - tools/benchmark/README.md | 172 +- tools/benchmark/__init__.py | 26 - tools/benchmark/__main__.py | 28 - tools/benchmark/benchmark.py | 334 +- tools/benchmark/command_line_reader.py | 155 - tools/benchmark/configuration.py | 64 - tools/benchmark/logging.py | 125 - tools/benchmark/requirements.txt | 6 +- .../benchmark/utils/__init__.py | 0 tools/benchmark/utils/constants.py | 53 + tools/benchmark/utils/infer_request_wrap.py | 82 + tools/benchmark/utils/inputs_filling.py | 189 + .../benchmark/utils/logging.py | 0 .../benchmark/utils/progress_bar.py | 30 +- tools/benchmark/utils/statistics_report.py | 119 + tools/benchmark/utils/utils.py | 248 + tools/calibration/aggregated_statistics.py | 4 +- tools/calibration/base_calibrator.py | 135 +- tools/calibration/benchmark_facade.py | 49 + .../calibration/calibration_configuration.py | 2 +- tools/calibration/calibrator.py | 107 +- tools/calibration/command_line_processor.py | 6 +- tools/calibration/command_line_reader.py | 2 +- .../calculate_accuracy_callback.py | 19 +- .../collect_results_callback.py | 11 +- tools/calibration/requirements.txt | 2 +- tools/network.py | 24 + tools/utils/layer.py | 5 - tools/utils/network_info.py | 8 +- 1734 files changed, 72081 insertions(+), 58959 deletions(-) create mode 100644 inference-engine/cmake/vpu_dependencies.cmake delete mode 100644 inference-engine/ie_bridges/python/sample/affinity_setting_sample/affinity_setting_sample.py delete mode 100644 inference-engine/ie_bridges/python/sample/benchmark_app/README.md delete mode 100644 inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/benchmark.py delete mode 100644 inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/infer_request_wrap.py delete mode 100644 inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/inputs_filling.py delete mode 100644 inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/parameters.py delete mode 100644 inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/utils.py delete mode 100644 inference-engine/ie_bridges/python/sample/benchmark_app/benchmark_app.py create mode 100644 inference-engine/ie_bridges/python/sample/object_detection_sample_ssd/README.md create mode 100644 inference-engine/ie_bridges/python/sample/object_detection_sample_ssd/object_detection_sample_ssd.py create mode 100644 inference-engine/include/dlia/dlia_config.hpp create mode 100644 inference-engine/include/multi-device/multi_device_config.hpp create mode 100644 inference-engine/include/vpu/hddl_plugin_config.hpp delete mode 100644 inference-engine/samples/thirdparty/gflags/.gitmodules create mode 100644 inference-engine/src/extension/ext_non_max_suppression.cpp create mode 100644 inference-engine/src/extension/ext_scatter.cpp create mode 100644 inference-engine/src/extension/ext_sparse_fill_empty_rows.cpp create mode 100644 inference-engine/src/extension/ext_unique.cpp rename inference-engine/src/{inference_engine/ade_util.cpp => hetero_plugin/hetero_ade_util.cpp} (98%) rename inference-engine/src/{inference_engine/ade_util.hpp => hetero_plugin/hetero_ade_util.hpp} (100%) rename inference-engine/src/{inference_engine/hetero => hetero_plugin}/hetero_async_infer_request.cpp (99%) rename inference-engine/src/{inference_engine/hetero => hetero_plugin}/hetero_async_infer_request.hpp (100%) rename inference-engine/src/{inference_engine/hetero => hetero_plugin}/hetero_device_loader.cpp (97%) rename inference-engine/src/{inference_engine/hetero => hetero_plugin}/hetero_device_loader.hpp (100%) rename inference-engine/src/{inference_engine/hetero => hetero_plugin}/hetero_executable_network.cpp (94%) rename inference-engine/src/{inference_engine/hetero => hetero_plugin}/hetero_executable_network.hpp (100%) rename inference-engine/src/{inference_engine/hetero/fallback_policy.cpp => hetero_plugin/hetero_fallback_policy.cpp} (84%) rename inference-engine/src/{inference_engine/hetero/fallback_policy.hpp => hetero_plugin/hetero_fallback_policy.hpp} (100%) rename inference-engine/src/{inference_engine/ie_graph_splitter.cpp => hetero_plugin/hetero_graph_splitter.cpp} (99%) rename inference-engine/src/{inference_engine/ie_graph_splitter.hpp => hetero_plugin/hetero_graph_splitter.hpp} (91%) rename inference-engine/src/{inference_engine/hetero => hetero_plugin}/hetero_infer_request.cpp (100%) rename inference-engine/src/{inference_engine/hetero => hetero_plugin}/hetero_infer_request.hpp (100%) rename inference-engine/src/{inference_engine/hetero => hetero_plugin}/hetero_plugin.hpp (100%) rename inference-engine/src/{inference_engine/hetero => hetero_plugin}/hetero_plugin_base.hpp (100%) create mode 100644 inference-engine/src/inference_engine/cpp_interfaces/interface/ie_internal_plugin_config.hpp delete mode 100644 inference-engine/src/inference_engine/hetero/hetero_plugin.cpp delete mode 100644 inference-engine/src/inference_engine/range_iterator.hpp create mode 100644 inference-engine/src/inference_engine/shape_infer/built-in/ie_non_max_suppression_shape_infer.hpp create mode 100644 inference-engine/src/inference_engine/shape_infer/built-in/ie_scatter_shape_infer.hpp create mode 100644 inference-engine/src/inference_engine/shape_infer/built-in/ie_sparse_fill_empty_rows_shape_infer.hpp create mode 100644 inference-engine/src/inference_engine/shape_infer/built-in/ie_unique_shape_infer.hpp create mode 100644 inference-engine/src/inference_engine/shape_infer/const_infer/broadcast_offset.hpp create mode 100644 inference-engine/src/inference_engine/shape_infer/const_infer/ie_convert_const_infer.hpp create mode 100644 inference-engine/src/inference_engine/shape_infer/const_infer/ie_permute_const_infer.hpp create mode 100644 inference-engine/src/inference_engine/shape_infer/const_infer/ie_pow_const_infer.hpp create mode 100644 inference-engine/src/inference_engine/shape_infer/const_infer/ie_reduce_const_infer.hpp delete mode 100644 inference-engine/src/inference_engine/transform/transform_network.cpp delete mode 100644 inference-engine/src/inference_engine/transform/transform_network.hpp delete mode 100644 inference-engine/src/inference_engine/transform/transformation.cpp delete mode 100644 inference-engine/src/inference_engine/transform/transformation.hpp delete mode 100644 inference-engine/src/inference_engine/transform/transformations/eltwise_broadcast.cpp delete mode 100644 inference-engine/src/inference_engine/transform/transformations/eltwise_broadcast.hpp delete mode 100644 inference-engine/src/inference_engine/transform/transformations/lrn.cpp delete mode 100644 inference-engine/src/inference_engine/transform/transformations/lrn.hpp delete mode 100644 inference-engine/src/inference_engine/transform/transformations/sub.cpp delete mode 100644 inference-engine/src/inference_engine/transform/transformations/sub.hpp create mode 100644 inference-engine/src/mkldnn_plugin/mkldnn_exec_network.cpp create mode 100644 inference-engine/src/mkldnn_plugin/mkldnn_exec_network.h rename inference-engine/src/{inference_engine/memory_solver.cpp => mkldnn_plugin/mkldnn_memory_solver.cpp} (97%) rename inference-engine/src/{inference_engine/memory_solver.hpp => mkldnn_plugin/mkldnn_memory_solver.hpp} (95%) create mode 100644 inference-engine/src/mkldnn_plugin/mkldnn_memory_state.cpp create mode 100644 inference-engine/src/mkldnn_plugin/mkldnn_memory_state.h create mode 100644 inference-engine/src/vpu/common/CMakeLists.txt create mode 100644 inference-engine/src/vpu/common/include/vpu/parsed_config_base.hpp rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/any.hpp (100%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/attributes_map.hpp (87%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/auto_scope.hpp (100%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/checked_cast.hpp (100%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/containers.hpp (95%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/dot_io.hpp (100%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/enums.hpp (100%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/extra.hpp (95%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/file_system.hpp (100%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/func_ref.hpp (88%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/handle.hpp (85%) create mode 100644 inference-engine/src/vpu/common/include/vpu/utils/heap.hpp rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/ie_helpers.hpp (54%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/io.hpp (91%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/logger.hpp (100%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/numeric.hpp (100%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/optional.hpp (100%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/perf_report.hpp (60%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/range.hpp (100%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/simple_math.hpp (100%) rename inference-engine/src/vpu/{graph_transformer => common}/include/vpu/utils/string.hpp (92%) create mode 100644 inference-engine/src/vpu/common/src/parsed_config_base.cpp rename inference-engine/src/vpu/{graph_transformer => common}/src/utils/dot_io.cpp (100%) rename inference-engine/src/vpu/{graph_transformer => common}/src/utils/enums.cpp (100%) rename inference-engine/src/vpu/{graph_transformer => common}/src/utils/file_system.cpp (100%) rename inference-engine/src/vpu/{graph_transformer => common}/src/utils/ie_helpers.cpp (50%) rename inference-engine/src/vpu/{graph_transformer => common}/src/utils/io.cpp (100%) rename inference-engine/src/vpu/{graph_transformer => common}/src/utils/logger.cpp (100%) rename inference-engine/src/vpu/{graph_transformer => common}/src/utils/perf_report.cpp (97%) rename inference-engine/src/vpu/{graph_transformer => common}/src/utils/simple_math.cpp (100%) rename inference-engine/src/vpu/{vpu_custom_kernels => custom_kernels}/binary_layers.cl (100%) rename inference-engine/src/vpu/{vpu_custom_kernels => custom_kernels}/ctc.cl (100%) create mode 100644 inference-engine/src/vpu/custom_kernels/customLayerBindings.xml rename inference-engine/src/vpu/{vpu_custom_kernels => custom_kernels}/cvtf32f16.cl (100%) rename inference-engine/src/vpu/{vpu_custom_kernels => custom_kernels}/cvtu8f16.cl (100%) rename inference-engine/src/vpu/{vpu_custom_kernels => custom_kernels}/grn.cl (100%) rename inference-engine/src/vpu/{vpu_custom_kernels => custom_kernels}/mvn.cl (100%) create mode 100644 inference-engine/src/vpu/custom_kernels/region_chw.cl create mode 100644 inference-engine/src/vpu/custom_kernels/region_chw_m7_branch0.cl create mode 100644 inference-engine/src/vpu/custom_kernels/region_chw_m7_branch1.cl rename inference-engine/src/vpu/{vpu_custom_kernels => custom_kernels}/reorg_chw.cl (100%) rename inference-engine/src/vpu/{vpu_custom_kernels => custom_kernels}/reorg_chw_local.cl (100%) rename inference-engine/src/vpu/{vpu_custom_kernels => custom_kernels}/reorg_chw_stack.cl (100%) create mode 100644 inference-engine/src/vpu/custom_kernels/reorg_hwc.cl rename inference-engine/src/vpu/{vpu_custom_kernels => custom_kernels}/resample_nn.cl (93%) create mode 100644 inference-engine/src/vpu/custom_kernels/resample_with_antialias.cl rename inference-engine/src/vpu/{vpu_custom_kernels => custom_kernels}/shuffle_channels.cl (100%) create mode 100644 inference-engine/src/vpu/graph_transformer/include/vpu/passes/hw_conv_tiling/hw_convolution_tiler.hpp create mode 100644 inference-engine/src/vpu/graph_transformer/include/vpu/passes/hw_conv_tiling/hw_stage_tiler.hpp create mode 100644 inference-engine/src/vpu/graph_transformer/include/vpu/passes/hw_pooling_tiling/hw_pooling_tiler.hpp create mode 100644 inference-engine/src/vpu/graph_transformer/include/vpu/passes/hw_pooling_tiling/hw_stage_tiler.hpp create mode 100644 inference-engine/src/vpu/graph_transformer/include/vpu/special_stage_processor.hpp create mode 100644 inference-engine/src/vpu/graph_transformer/src/passes/add_copy_for_outputs_inside_network.cpp create mode 100644 inference-engine/src/vpu/graph_transformer/src/passes/hw_conv_tiling/hw_convolution_tiler.cpp create mode 100644 inference-engine/src/vpu/graph_transformer/src/passes/hw_conv_tiling/hw_stage_tiler.cpp create mode 100644 inference-engine/src/vpu/graph_transformer/src/passes/hw_pooling_tiling/hw_pooling_tiler.cpp create mode 100644 inference-engine/src/vpu/graph_transformer/src/passes/hw_pooling_tiling/hw_stage_tiler.cpp create mode 100644 inference-engine/src/vpu/graph_transformer/src/passes/initial_check.cpp create mode 100644 inference-engine/src/vpu/graph_transformer/src/passes/remove_unused_stages_outputs.cpp create mode 100644 inference-engine/src/vpu/graph_transformer/src/passes/strided_slice.cpp create mode 100644 inference-engine/src/vpu/graph_transformer/src/special_stage_processor.cpp create mode 100644 inference-engine/src/vpu/graph_transformer/src/stages/exp.cpp rename inference-engine/src/vpu/graph_transformer/src/stages/{broadcast.cpp => expand.cpp} (62%) create mode 100644 inference-engine/src/vpu/graph_transformer/src/stages/floor.cpp create mode 100644 inference-engine/src/vpu/graph_transformer/src/stages/strided_slice.cpp create mode 100644 inference-engine/src/vpu/graph_transformer/src/stages/topk.cpp create mode 100644 inference-engine/tests/helpers/tests_vpu_common.cpp create mode 100644 inference-engine/tests/helpers/tests_vpu_common.hpp delete mode 100644 inference-engine/tests/unit/builders/transform_network_test.cpp delete mode 100644 inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/gather_tree_tests.cpp create mode 100644 inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/non_max_suppression_tests.cpp create mode 100644 inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/scatter_tests.cpp create mode 100644 inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/sparse_fill_empty_rows_tests.cpp create mode 100644 inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/unique_tests.cpp rename inference-engine/tests/unit/{mem_solver => engines/mkldnn}/mem_solver_test.cpp (97%) create mode 100644 inference-engine/tests/unit/graph_tools/graph_tools_functional_tests.cpp delete mode 100644 inference-engine/tests/unit/inference_engine_tests/device_tests.cpp delete mode 100644 inference-engine/tests/unit/inference_engine_tests/range_iterator_tests.cpp delete mode 100644 inference-engine/tests/unit/transformations/eltwise_broadcast_test.cpp delete mode 100644 inference-engine/tests/unit/transformations/sub_test.cpp delete mode 100644 inference-engine/tests/unit/transformations/tranformations_test.hpp delete mode 100644 inference-engine/thirdparty/clDNN/api/C/activation.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/activation_grad.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/apply_adam.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/arg_max_min.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/average_unpooling.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/batch_norm.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/batch_norm_grad.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/binary_convolution.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/border.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/broadcast.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/cldnn.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/concatenation.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/condition.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/contract.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/convolution.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/convolution_grad_input.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/convolution_grad_weights.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/crop.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/custom_gpu_primitive.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/data.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/deconvolution.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/deformable_conv.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/deformable_interp.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/detection_output.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/detection_output_sort.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/eltwise.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/embed.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/fully_connected.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/fully_connected_grad_input.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/fully_connected_grad_weights.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/gather.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/gemm.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/index_select.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/input_layout.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/lookup_table.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/lrn.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/lstm.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/lstm_dynamic.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/max_unpooling.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/mutable_data.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/mvn.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/normalize.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/one_hot.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/permute.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/pooling.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/prior_box.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/proposal.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/reduce.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/region_yolo.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/reorder.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/reorg_yolo.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/reshape.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/reverse_sequence.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/roi_pooling.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/scale.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/scale_grad_input.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/scale_grad_weights.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/select.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/shuffle_channels.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/softmax.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/softmax_loss_grad.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/split.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/strided_slice.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/tile.h delete mode 100644 inference-engine/thirdparty/clDNN/api/C/upsampling.h delete mode 100644 inference-engine/thirdparty/clDNN/api/CPP/compounds.h delete mode 100644 inference-engine/thirdparty/clDNN/api/CPP/event.hpp delete mode 100644 inference-engine/thirdparty/clDNN/api/CPP/network.hpp delete mode 100644 inference-engine/thirdparty/clDNN/api/CPP/primitive.hpp rename inference-engine/thirdparty/clDNN/api/{CPP => }/activation.hpp (55%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/activation_grad.hpp (74%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/apply_adam.hpp (81%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/arg_max_min.hpp (78%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/average_unpooling.hpp (81%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/batch_norm.hpp (90%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/batch_norm_grad.hpp (81%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/binary_convolution.hpp (82%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/border.hpp (86%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/broadcast.hpp (89%) rename inference-engine/thirdparty/clDNN/api/{CPP/cldnn_defs.h => cldnn.hpp} (67%) create mode 100644 inference-engine/thirdparty/clDNN/api/compounds.h rename inference-engine/thirdparty/clDNN/api/{CPP => }/concatenation.hpp (77%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/condition.hpp (81%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/contract.hpp (82%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/convolution.hpp (75%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/convolution_grad_input.hpp (94%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/convolution_grad_weights.hpp (75%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/crop.hpp (92%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/custom_gpu_primitive.hpp (67%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/data.hpp (83%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/deconvolution.hpp (77%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/depth_to_space.hpp (79%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/detection_output.hpp (74%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/eltwise.hpp (65%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/embed.hpp (85%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/engine.hpp (61%) create mode 100644 inference-engine/thirdparty/clDNN/api/event.hpp rename inference-engine/thirdparty/clDNN/api/{CPP => }/fully_connected.hpp (76%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/fully_connected_grad_input.hpp (81%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/fully_connected_grad_weights.hpp (86%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/gather.hpp (73%) create mode 100644 inference-engine/thirdparty/clDNN/api/gather_tree.hpp rename inference-engine/thirdparty/clDNN/api/{CPP => }/gemm.hpp (82%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/index_select.hpp (89%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/input_layout.hpp (80%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/layout.hpp (93%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/lookup_table.hpp (79%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/lrn.hpp (79%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/lstm.hpp (70%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/lstm_dynamic.hpp (82%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/max_unpooling.hpp (84%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/memory.hpp (65%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/meta_utils.hpp (98%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/mutable_data.hpp (84%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/mvn.hpp (79%) create mode 100644 inference-engine/thirdparty/clDNN/api/network.hpp rename inference-engine/thirdparty/clDNN/api/{CPP => }/normalize.hpp (86%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/one_hot.hpp (86%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/permute.hpp (83%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/pooling.hpp (90%) create mode 100644 inference-engine/thirdparty/clDNN/api/primitive.hpp rename inference-engine/thirdparty/clDNN/api/{CPP => }/prior_box.hpp (76%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/profiling.hpp (97%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/program.hpp (58%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/proposal.hpp (75%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/pyramid_roi_align.hpp (83%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/quantize.hpp (85%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/reduce.hpp (65%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/region_yolo.hpp (76%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/reorder.hpp (81%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/reorg_yolo.hpp (82%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/reshape.hpp (85%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/reverse_sequence.hpp (86%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/roi_pooling.hpp (71%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/scale.hpp (89%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/scale_grad_input.hpp (82%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/scale_grad_weights.hpp (89%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/select.hpp (86%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/shuffle_channels.hpp (83%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/softmax.hpp (79%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/softmax_loss_grad.hpp (84%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/split.hpp (77%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/strided_slice.hpp (78%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/tensor.hpp (71%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/tile.hpp (72%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/topology.hpp (54%) rename inference-engine/thirdparty/clDNN/api/{CPP => }/upsampling.hpp (72%) delete mode 100644 inference-engine/thirdparty/clDNN/api_extension/C/fused_conv_bn_scale.h delete mode 100644 inference-engine/thirdparty/clDNN/api_extension/C/fused_conv_eltwise.h delete mode 100644 inference-engine/thirdparty/clDNN/api_extension/C/lstm_dynamic_input.h delete mode 100644 inference-engine/thirdparty/clDNN/api_extension/C/lstm_dynamic_timeloop.h rename inference-engine/thirdparty/clDNN/api_extension/{CPP => }/fused_conv_bn_scale.hpp (70%) rename inference-engine/thirdparty/clDNN/api_extension/{CPP => }/fused_conv_eltwise.hpp (64%) rename inference-engine/thirdparty/clDNN/api_extension/{CPP => }/lstm_dynamic_input.hpp (85%) rename inference-engine/thirdparty/clDNN/api_extension/{CPP => }/lstm_dynamic_timeloop.hpp (81%) delete mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/activation/activation_kernel_tutorial.cpp create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/binary_convolution/binary_convolution_kernel_1x1_b_fs_yx_fsv16.cpp create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/binary_convolution/binary_convolution_kernel_1x1_b_fs_yx_fsv16.h create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/convolution/convolution_kernel_bfzyx_f16.cpp rename inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/convolution/{convolution_kernel_tutorial.h => convolution_kernel_bfzyx_f16.h} (69%) rename inference-engine/thirdparty/clDNN/{api/C/pyramid_roi_align.h => kernel_selector/core/actual_kernels/convolution/convolution_kernel_bfzyx_f16_fp16.h} (57%) rename inference-engine/thirdparty/clDNN/{api/C/quantize.h => kernel_selector/core/actual_kernels/convolution/convolution_kernel_bfzyx_f16_fp32.h} (53%) delete mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/convolution/convolution_kernel_bfzyx_ref.cpp create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/convolution/convolution_kernel_fs_byx_fsv32_depthwise.cpp create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/convolution/convolution_kernel_fs_byx_fsv32_depthwise.h delete mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/convolution/convolution_kernel_tutorial.cpp create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/deconvolution/deconvolution_kernel_bfzyx_f16.cpp create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/deconvolution/deconvolution_kernel_bfzyx_f16.h delete mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/fully_connected/fully_connected_kernel_image_tutorial.cpp create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/gather_tree/gather_tree_kernel_base.cpp create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/gather_tree/gather_tree_kernel_base.h create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/gather_tree/gather_tree_kernel_ref.cpp rename inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/{fully_connected/fully_connected_kernel_image_tutorial.h => gather_tree/gather_tree_kernel_ref.h} (63%) rename inference-engine/thirdparty/clDNN/{api/C/depth_to_space.h => kernel_selector/core/actual_kernels/gather_tree/gather_tree_kernel_selector.cpp} (50%) create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/gather_tree/gather_tree_kernel_selector.h create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/lstm_dynamic/lstm_dynamic_input_bfyx_opt.cpp rename inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/{convolution/convolution_kernel_bfzyx_ref.h => lstm_dynamic/lstm_dynamic_input_bfyx_opt.h} (63%) create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/quantize/quantize_kernel_base.cpp rename inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/{activation/activation_kernel_tutorial.h => quantize/quantize_kernel_base.h} (53%) create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/quantize/quantize_kernel_params.h delete mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/cl_kernels/activation_tutorial.cl create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/cl_kernels/binary_convolution_gpu_1x1_b_fs_yx_fsv16.cl delete mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/cl_kernels/convolution_gpu_bfzyx_ref.cl create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/cl_kernels/convolution_gpu_fs_byx_fsv32_depthwise.cl delete mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/cl_kernels/convolution_tutorial.cl delete mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/cl_kernels/fully_connected_gpu_image_tutorial.cl create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/cl_kernels/gather_tree_gpu_ref.cl create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/cl_kernels/gen9_common_conv_bwd_data.cl create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/cl_kernels/gen9_common_conv_fwd_data_f16.cl create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/cl_kernels/gen9_common_conv_fwd_data_f32.cl create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/cl_kernels/lstm_dynamic_input_bfyx_opt.cl create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/cl_kernels/ocl_types.h rename inference-engine/thirdparty/clDNN/kernel_selector/core/cl_kernels/{quantize_ref.cl => quantize_gpu_ref.cl} (74%) create mode 100644 inference-engine/thirdparty/clDNN/src/gather_tree.cpp create mode 100644 inference-engine/thirdparty/clDNN/src/gpu/gather_tree_gpu.cpp create mode 100644 inference-engine/thirdparty/clDNN/src/gpu/register_gpu.cpp create mode 100644 inference-engine/thirdparty/clDNN/src/gpu/register_gpu.hpp delete mode 100644 inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_binarization.cpp create mode 100644 inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_quantization.cpp delete mode 100644 inference-engine/thirdparty/clDNN/src/include/api_impl.h create mode 100644 inference-engine/thirdparty/clDNN/src/include/gather_tree_inst.h delete mode 100644 inference-engine/thirdparty/clDNN/src/include/generic_layer.h create mode 100644 inference-engine/thirdparty/clDNN/src/memory.cpp create mode 100644 inference-engine/thirdparty/clDNN/src/topology.cpp create mode 100644 inference-engine/thirdparty/clDNN/tests/test_cases/fusings_gpu_test.cpp create mode 100644 inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gasync_context.hpp create mode 100644 inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gtransform.hpp create mode 100644 inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/render.hpp create mode 100644 inference-engine/thirdparty/fluid/modules/gapi/src/api/render.cpp create mode 100644 inference-engine/thirdparty/fluid/modules/gapi/src/api/render_priv.hpp create mode 100644 inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_render_tests.cpp create mode 100644 inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_render_tests.hpp create mode 100644 inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_render_tests_inl.hpp create mode 100644 inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_tests_helpers.hpp create mode 100644 inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_render_tests_cpu.cpp create mode 100644 inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_parallel_rois_test.cpp create mode 100644 inference-engine/thirdparty/fluid/modules/gapi/test/gapi_transform_tests.cpp create mode 100644 inference-engine/thirdparty/movidius/WinPthread/pthread_semaphore.c create mode 100644 inference-engine/thirdparty/movidius/WinPthread/pthread_semaphore.h create mode 100644 inference-engine/thirdparty/movidius/XLink/XLink.cmake create mode 100644 inference-engine/thirdparty/movidius/XLink/shared/XLinkPlatform_tool.h create mode 100644 inference-engine/thirdparty/movidius/XLink/shared/XLink_tool.h delete mode 100644 inference-engine/thirdparty/movidius/XLink/tests/CMakeLists.txt delete mode 100644 inference-engine/thirdparty/movidius/XLink/tests/XLink_tests.cpp delete mode 100644 inference-engine/tools/benchmark_tool/benchmark.py create mode 100644 inference-engine/tools/benchmark_tool/benchmark_app.py create mode 100644 inference-engine/tools/benchmark_tool/parameters.py create mode 100644 inference-engine/tools/vpu/vpu_perfcheck/CMakeLists.txt create mode 100644 inference-engine/tools/vpu/vpu_perfcheck/main.cpp create mode 100644 model-optimizer/extensions/back/CutMemory.py create mode 100644 model-optimizer/extensions/back/CutMemory_test.py create mode 100644 model-optimizer/extensions/back/Gather0D.py create mode 100644 model-optimizer/extensions/front/mxnet/expand_dims_ext.py create mode 100644 model-optimizer/extensions/front/onnx/constant_of_shape_ext.py create mode 100644 model-optimizer/extensions/front/onnx/constant_of_shape_to_broadcast.py create mode 100644 model-optimizer/extensions/front/onnx/expand_ext.py create mode 100644 model-optimizer/extensions/front/onnx/floor_ext.py rename inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/__init__.py => model-optimizer/extensions/front/onnx/not_ext.py (63%) create mode 100644 model-optimizer/extensions/front/onnx/reduce_min_ext.py create mode 100644 model-optimizer/extensions/front/onnx/top_k_ext.py create mode 100644 model-optimizer/extensions/front/tf/BatchToSpaceNDToUpsample.py create mode 100644 model-optimizer/extensions/front/tf/InterpolateTransposes.py create mode 100644 model-optimizer/extensions/front/tf/sparse_fill_empty_rows_ext.py create mode 100644 model-optimizer/extensions/front/tf/swish.py create mode 100644 model-optimizer/extensions/front/tf/swish_test.py create mode 100644 model-optimizer/extensions/front/tf/unique_ext.py create mode 100644 model-optimizer/extensions/middle/BiasAddBroadcasting.py create mode 100644 model-optimizer/extensions/middle/InsertSelect.py create mode 100644 model-optimizer/extensions/middle/InsertSelect_test.py create mode 100644 model-optimizer/extensions/middle/ReplacePNorm.py create mode 100644 model-optimizer/extensions/middle/ReplacePNormNodePattern_test.py create mode 100644 model-optimizer/extensions/ops/non_max_suppression.py create mode 100644 model-optimizer/extensions/ops/pnorm.py create mode 100644 model-optimizer/extensions/ops/sparse_fill_empty_rows.py create mode 100644 model-optimizer/extensions/ops/sparse_fill_empty_rows_test.py create mode 100644 model-optimizer/extensions/ops/unique.py create mode 100644 model-optimizer/extensions/ops/unique_test.py create mode 100644 model-optimizer/mo/front/kaldi/extractors/pnorm_component_ext.py create mode 100644 model-optimizer/mo/front/kaldi/extractors/pnorm_component_ext_test.py create mode 100644 model-optimizer/mo/ops/constant_of_shape.py create mode 100644 model-optimizer/mo/utils/unittest/ir_engine.py create mode 100644 model-optimizer/mo/utils/unittest/ir_engine_test.py create mode 100644 model-optimizer/mo/utils/unittest/test_data/mxnet_synthetic_gru_bidirectional_FP16_1_v6.bin create mode 100644 model-optimizer/mo/utils/unittest/test_data/mxnet_synthetic_gru_bidirectional_FP16_1_v6.xml create mode 100644 model-optimizer/mo/utils/unittest/test_data/mxnet_synthetic_gru_bidirectional_FP16_1_v6_negative.xml create mode 100644 model-optimizer/mo/utils/versions_checker_test.py create mode 100644 model-optimizer/requirements_dev.txt delete mode 100644 tools/accuracy_checker/.pylintrc delete mode 100644 tools/accuracy_checker/README.md delete mode 100644 tools/accuracy_checker/accuracy_checker/__init__.py delete mode 100644 tools/accuracy_checker/accuracy_checker/adapters/README.md delete mode 100644 tools/accuracy_checker/accuracy_checker/adapters/__init__.py delete mode 100644 tools/accuracy_checker/accuracy_checker/adapters/action_recognition.py delete mode 100644 tools/accuracy_checker/accuracy_checker/adapters/adapter.py delete mode 100644 tools/accuracy_checker/accuracy_checker/adapters/attributes_recognition.py delete mode 100644 tools/accuracy_checker/accuracy_checker/adapters/classification.py delete mode 100644 tools/accuracy_checker/accuracy_checker/adapters/detection.py delete mode 100644 tools/accuracy_checker/accuracy_checker/adapters/dummy_adapters.py delete mode 100644 tools/accuracy_checker/accuracy_checker/adapters/hit_ratio.py delete mode 100644 tools/accuracy_checker/accuracy_checker/adapters/image_processing.py delete mode 100644 tools/accuracy_checker/accuracy_checker/adapters/pose_estimation.py delete mode 100644 tools/accuracy_checker/accuracy_checker/adapters/reidentification.py delete mode 100644 tools/accuracy_checker/accuracy_checker/adapters/segmentation.py delete mode 100644 tools/accuracy_checker/accuracy_checker/adapters/text_detection.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/README.md delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/__init__.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/_reid_common.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/bitvehicle.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/brats.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/cityscapes.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/convert.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/detection_opencv_storage.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/format_converter.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/icdar.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/imagenet.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/lfw.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/mapillary_20.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/market1501.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/mars.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/mighty.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/ms_coco.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/ncf_converter.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/pascal_voc.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/sample_converter.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/super_resolution_converter.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/vgg_face_regression.py delete mode 100644 tools/accuracy_checker/accuracy_checker/annotation_converters/wider.py delete mode 100644 tools/accuracy_checker/accuracy_checker/config/__init__.py delete mode 100644 tools/accuracy_checker/accuracy_checker/config/config_reader.py delete mode 100644 tools/accuracy_checker/accuracy_checker/config/config_validator.py delete mode 100644 tools/accuracy_checker/accuracy_checker/data_readers/__init__.py delete mode 100644 tools/accuracy_checker/accuracy_checker/data_readers/data_reader.py delete mode 100644 tools/accuracy_checker/accuracy_checker/dataset.py delete mode 100644 tools/accuracy_checker/accuracy_checker/dependency.py delete mode 100644 tools/accuracy_checker/accuracy_checker/evaluators/__init__.py delete mode 100644 tools/accuracy_checker/accuracy_checker/evaluators/model_evaluator.py delete mode 100644 tools/accuracy_checker/accuracy_checker/evaluators/pipeline_evaluator.py delete mode 100644 tools/accuracy_checker/accuracy_checker/launcher/__init__.py delete mode 100644 tools/accuracy_checker/accuracy_checker/launcher/caffe_installation_readme.md delete mode 100644 tools/accuracy_checker/accuracy_checker/launcher/caffe_launcher.py delete mode 100644 tools/accuracy_checker/accuracy_checker/launcher/caffe_launcher_readme.md delete mode 100644 tools/accuracy_checker/accuracy_checker/launcher/dlsdk_launcher.py delete mode 100644 tools/accuracy_checker/accuracy_checker/launcher/dlsdk_launcher_readme.md delete mode 100644 tools/accuracy_checker/accuracy_checker/launcher/dummy_launcher.py delete mode 100644 tools/accuracy_checker/accuracy_checker/launcher/input_feeder.py delete mode 100644 tools/accuracy_checker/accuracy_checker/launcher/launcher.py delete mode 100644 tools/accuracy_checker/accuracy_checker/launcher/loaders/__init__.py delete mode 100644 tools/accuracy_checker/accuracy_checker/launcher/loaders/loader.py delete mode 100644 tools/accuracy_checker/accuracy_checker/launcher/loaders/pickle_loader.py delete mode 100644 tools/accuracy_checker/accuracy_checker/launcher/loaders/xml_loader.py delete mode 100644 tools/accuracy_checker/accuracy_checker/launcher/model_conversion.py delete mode 100644 tools/accuracy_checker/accuracy_checker/logging.py delete mode 100644 tools/accuracy_checker/accuracy_checker/main.py delete mode 100644 tools/accuracy_checker/accuracy_checker/metrics/README.md delete mode 100644 tools/accuracy_checker/accuracy_checker/metrics/__init__.py delete mode 100644 tools/accuracy_checker/accuracy_checker/metrics/average_meter.py delete mode 100644 tools/accuracy_checker/accuracy_checker/metrics/character_recognition.py delete mode 100644 tools/accuracy_checker/accuracy_checker/metrics/classification.py delete mode 100644 tools/accuracy_checker/accuracy_checker/metrics/coco_metrics.py delete mode 100644 tools/accuracy_checker/accuracy_checker/metrics/detection.py delete mode 100644 tools/accuracy_checker/accuracy_checker/metrics/hit_ratio.py delete mode 100644 tools/accuracy_checker/accuracy_checker/metrics/metric.py delete mode 100644 tools/accuracy_checker/accuracy_checker/metrics/metric_executor.py delete mode 100644 tools/accuracy_checker/accuracy_checker/metrics/multilabel_recognition.py delete mode 100644 tools/accuracy_checker/accuracy_checker/metrics/overlap.py delete mode 100644 tools/accuracy_checker/accuracy_checker/metrics/regression.py delete mode 100644 tools/accuracy_checker/accuracy_checker/metrics/reid.py delete mode 100644 tools/accuracy_checker/accuracy_checker/metrics/semantic_segmentation.py delete mode 100644 tools/accuracy_checker/accuracy_checker/metrics/text_detection.py delete mode 100644 tools/accuracy_checker/accuracy_checker/pipeline_connectors/__init__.py delete mode 100644 tools/accuracy_checker/accuracy_checker/pipeline_connectors/connectors.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/README.md delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/__init__.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/cast_to_int.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/clip_boxes.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/clip_points.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/clip_segmentation_mask.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/correct_yolo_v2_boxes.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/crop_segmentation_mask.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/encode_segmentation_mask.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/extend_segmentation_mask.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/filter.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/nms.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/normalize_landmarks_points.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/postprocessing_executor.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/postprocessor.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/resize_prediction_boxes.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/resize_segmentation_mask.py delete mode 100644 tools/accuracy_checker/accuracy_checker/postprocessor/zoom_segmentation_mask.py delete mode 100644 tools/accuracy_checker/accuracy_checker/preprocessor/README.md delete mode 100644 tools/accuracy_checker/accuracy_checker/preprocessor/__init__.py delete mode 100644 tools/accuracy_checker/accuracy_checker/preprocessor/preprocessing_executor.py delete mode 100644 tools/accuracy_checker/accuracy_checker/preprocessor/preprocessors.py delete mode 100644 tools/accuracy_checker/accuracy_checker/presenters.py delete mode 100644 tools/accuracy_checker/accuracy_checker/progress_reporters.py delete mode 100644 tools/accuracy_checker/accuracy_checker/representation/__init__.py delete mode 100644 tools/accuracy_checker/accuracy_checker/representation/base_representation.py delete mode 100644 tools/accuracy_checker/accuracy_checker/representation/character_recognition_representation.py delete mode 100644 tools/accuracy_checker/accuracy_checker/representation/classification_representation.py delete mode 100644 tools/accuracy_checker/accuracy_checker/representation/detection_representation.py delete mode 100644 tools/accuracy_checker/accuracy_checker/representation/hit_ratio_representation.py delete mode 100644 tools/accuracy_checker/accuracy_checker/representation/multilabel_recognition.py delete mode 100644 tools/accuracy_checker/accuracy_checker/representation/pose_estimation_representation.py delete mode 100644 tools/accuracy_checker/accuracy_checker/representation/regression_representation.py delete mode 100644 tools/accuracy_checker/accuracy_checker/representation/reid_representation.py delete mode 100644 tools/accuracy_checker/accuracy_checker/representation/representaton_container.py delete mode 100644 tools/accuracy_checker/accuracy_checker/representation/segmentation_representation.py delete mode 100644 tools/accuracy_checker/accuracy_checker/representation/super_resolution_representation.py delete mode 100644 tools/accuracy_checker/accuracy_checker/representation/text_detection_representation.py delete mode 100644 tools/accuracy_checker/accuracy_checker/utils.py delete mode 100644 tools/accuracy_checker/configs/face-detection-adas-0001.yml delete mode 100644 tools/accuracy_checker/configs/face-detection-retail-0004.yml delete mode 100644 tools/accuracy_checker/configs/face-reidentification-retail-0095.yml delete mode 100644 tools/accuracy_checker/configs/human-pose-estimation-0001.yml delete mode 100644 tools/accuracy_checker/configs/landmarks-regression-retail-0009.yml delete mode 100644 tools/accuracy_checker/configs/person-reidentification-retail-0031.yml delete mode 100644 tools/accuracy_checker/configs/person-reidentification-retail-0076.yml delete mode 100644 tools/accuracy_checker/configs/person-reidentification-retail-0079.yml delete mode 100644 tools/accuracy_checker/configs/resnet50-binary-0001.yml delete mode 100644 tools/accuracy_checker/configs/text-detection-0002.yml delete mode 100644 tools/accuracy_checker/configs/text-recognition-0012.yml delete mode 100644 tools/accuracy_checker/data/test_data/1.jpg delete mode 100644 tools/accuracy_checker/data/test_models/SampLeNet.bin delete mode 100644 tools/accuracy_checker/data/test_models/SampLeNet.caffemodel delete mode 100644 tools/accuracy_checker/data/test_models/SampLeNet.prototxt delete mode 100644 tools/accuracy_checker/data/test_models/SampLeNet.xml delete mode 100644 tools/accuracy_checker/pylint_checkers.py delete mode 100644 tools/accuracy_checker/requirements.txt delete mode 100644 tools/accuracy_checker/sample/README.md delete mode 100644 tools/accuracy_checker/sample/sample_config.yml delete mode 100644 tools/accuracy_checker/setup.cfg delete mode 100644 tools/accuracy_checker/tests/__init__.py delete mode 100644 tools/accuracy_checker/tests/common.py delete mode 100644 tools/accuracy_checker/tests/conftest.py delete mode 100644 tools/accuracy_checker/tests/test_adapters.py delete mode 100644 tools/accuracy_checker/tests/test_caffe_launcher.py delete mode 100644 tools/accuracy_checker/tests/test_config_reader.py delete mode 100644 tools/accuracy_checker/tests/test_config_validator.py delete mode 100644 tools/accuracy_checker/tests/test_dataset.py delete mode 100644 tools/accuracy_checker/tests/test_dependency.py delete mode 100644 tools/accuracy_checker/tests/test_detection_metrics.py delete mode 100644 tools/accuracy_checker/tests/test_dlsdk_launcher.py delete mode 100644 tools/accuracy_checker/tests/test_input_feeder.py delete mode 100644 tools/accuracy_checker/tests/test_metric_evaluator.py delete mode 100644 tools/accuracy_checker/tests/test_model_conversion.py delete mode 100644 tools/accuracy_checker/tests/test_model_evaluator.py delete mode 100644 tools/accuracy_checker/tests/test_postprocessor.py delete mode 100644 tools/accuracy_checker/tests/test_preprocessor.py delete mode 100644 tools/accuracy_checker/tests/test_presenter.py delete mode 100644 tools/accuracy_checker/tests/test_regression_metrics.py delete mode 100644 tools/accuracy_checker/tests/test_reid_metrics.py delete mode 100644 tools/accuracy_checker/tests/test_segmentation_metrics.py delete mode 100644 tools/accuracy_checker/tests/test_utils.py delete mode 100644 tools/benchmark/__main__.py delete mode 100644 tools/benchmark/command_line_reader.py delete mode 100644 tools/benchmark/configuration.py delete mode 100644 tools/benchmark/logging.py rename {inference-engine/ie_bridges/python/sample/benchmark_app => tools}/benchmark/utils/__init__.py (100%) create mode 100644 tools/benchmark/utils/constants.py create mode 100644 tools/benchmark/utils/infer_request_wrap.py create mode 100644 tools/benchmark/utils/inputs_filling.py rename {inference-engine/ie_bridges/python/sample/benchmark_app => tools}/benchmark/utils/logging.py (100%) rename {inference-engine/ie_bridges/python/sample/benchmark_app => tools}/benchmark/utils/progress_bar.py (57%) create mode 100644 tools/benchmark/utils/statistics_report.py create mode 100644 tools/benchmark/utils/utils.py create mode 100644 tools/calibration/benchmark_facade.py diff --git a/README.md b/README.md index 8d1dd329b887a6..139578aba46852 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # [OpenVINOâ„¢ Toolkit](https://01.org/openvinotoolkit) - Deep Learning Deployment Toolkit repository -[![Stable release](https://img.shields.io/badge/version-2019.R2-green.svg)](https://github.com/opencv/dldt/releases/tag/2019_R2) +[![Stable release](https://img.shields.io/badge/version-2019.R3-green.svg)](https://github.com/opencv/dldt/releases/tag/2019_R3) [![Apache License Version 2.0](https://img.shields.io/badge/license-Apache_2.0-green.svg)](LICENSE) This toolkit allows developers to deploy pre-trained deep learning models through a high-level C++ Inference Engine API integrated with application logic. diff --git a/inference-engine/CMakeLists.txt b/inference-engine/CMakeLists.txt index 0d449c9432fdb4..f41d9df93e83e5 100644 --- a/inference-engine/CMakeLists.txt +++ b/inference-engine/CMakeLists.txt @@ -34,6 +34,9 @@ message (STATUS "CMAKE_GENERATOR ....................... " ${CMAKE_GENERATOR}) message (STATUS "CMAKE_C_COMPILER_ID ................... " ${CMAKE_C_COMPILER_ID}) message (STATUS "CMAKE_BUILD_TYPE ...................... " ${CMAKE_BUILD_TYPE}) +# remove file with exported developer targets to force its regeneration +file(REMOVE "${CMAKE_BINARY_DIR}/targets_developer.cmake") + add_subdirectory(src) if(ENABLE_TESTS) diff --git a/inference-engine/cmake/FindlibGNA.cmake b/inference-engine/cmake/FindlibGNA.cmake index 4d7978241addc5..53b17836cf8005 100644 --- a/inference-engine/cmake/FindlibGNA.cmake +++ b/inference-engine/cmake/FindlibGNA.cmake @@ -2,35 +2,79 @@ # SPDX-License-Identifier: Apache-2.0 # -#module to locate GNA libraries +# module to locate GNA libraries if (WIN32) set(GNA_PLATFORM_DIR win64) - set(GNA_LIB_DIR x64) - set(GNA_LIB gna) elseif (UNIX) set(GNA_PLATFORM_DIR linux) - set(GNA_LIB_DIR lib) - set(GNA_LIB gna_api) - set(GNA_KERNEL_LIB gna_kernel) else () message(FATAL_ERROR "GNA not supported on this platform, only linux, and windows") endif () -find_library(GNA_API_LIBRARY - ${GNA_LIB} - HINTS - ${GNA}/${GNA_PLATFORM_DIR}/${GNA_LIB_DIR}) +set(libGNA_FOUND TRUE) + +set(GNA_KERNEL_LIB_NAME gna) +set(GNA_LIBS_LIST + "libGNA::API" + "libGNA::KERNEL") -set(libGNA_INCLUDE_DIRS ${GNA}/${GNA_PLATFORM_DIR}/include) -set(libGNA_LIBRARY ${GNA_API_LIBRARY}) +if (GNA_LIBRARY_VERSION STREQUAL "GNA1") + # use old version of GNA Library from gna_20181120 + if (WIN32) + set(GNA_LIB_DIR x64) + else () + list(APPEND GNA_LIBS_LIST + "libGNA::OLD_API_LIB") + set(GNA_LIB_DIR lib) + set(GNA_KERNEL_LIB_NAME gna_kernel) + endif() + set(libGNA_INCLUDE_DIRS "${GNA}/${GNA_PLATFORM_DIR}/include") +else() + # use current version of GNA library + set(GNA_LIB_DIR x64) + set(libGNA_INCLUDE_DIRS "${GNA}/include") +endif() +set(libGNA_LIBRARIES_BASE_PATH ${GNA}/${GNA_PLATFORM_DIR}/${GNA_LIB_DIR}) + +add_library(libGNA::KERNEL SHARED IMPORTED) +find_library(GNA_KERNEL_LIBRARY + ${GNA_KERNEL_LIB_NAME} + HINTS + ${libGNA_LIBRARIES_BASE_PATH}) +set_target_properties(libGNA::KERNEL PROPERTIES IMPORTED_LOCATION ${GNA_KERNEL_LIBRARY}) -if (UNIX) - #message("Searching for libgna_kernel.so in: ${GNA}/${GNA_PLATFORM_DIR}/${GNA_KERNEL_LIB}") - find_library(GNA_KERNEL_LIBRARY - ${GNA_KERNEL_LIB} +if ((GNA_LIBRARY_VERSION STREQUAL "GNA1") AND (NOT WIN32)) + add_library(libGNA::OLD_API_LIB SHARED IMPORTED) + find_library(GNA_API_LIBRARY + gna_api HINTS - ${GNA}/${GNA_PLATFORM_DIR}/${GNA_LIB_DIR}) -endif () + ${libGNA_LIBRARIES_BASE_PATH}) + set_target_properties(libGNA::OLD_API_LIB PROPERTIES IMPORTED_LOCATION ${GNA_API_LIBRARY}) + target_link_libraries(libGNA::OLD_API_LIB INTERFACE libGNA::KERNEL) + set_target_properties(libGNA::OLD_API_LIB PROPERTIES IMPORTED_NO_SONAME TRUE) + set_target_properties(libGNA::KERNEL PROPERTIES IMPORTED_NO_SONAME TRUE) +endif() + +add_library(libGNA::API INTERFACE IMPORTED) +set_property(TARGET libGNA::API PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${libGNA_INCLUDE_DIRS}) + +add_library(libGNA INTERFACE IMPORTED) +foreach(_lib_name ${GNA_LIBS_LIST}) + set_property(TARGET libGNA APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${_lib_name}) -set(libGNA_LIBRARIES ${libGNA_LIBRARY} ${GNA_KERNEL_LIBRARY}) + get_target_property(_target_type ${_lib_name} TYPE) + if (${_target_type} STREQUAL "INTERFACE_LIBRARY") + get_target_property(_target_location ${_lib_name} INTERFACE_INCLUDE_DIRECTORIES) + else() + get_target_property(_target_location ${_lib_name} IMPORTED_LOCATION) + endif () + message(STATUS "${_lib_name} ${_target_type} : ${_target_location}") +endforeach(_lib_name) + +if (WIN32) + set_target_properties(libGNA::KERNEL PROPERTIES + IMPORTED_IMPLIB ${GNA_KERNEL_LIBRARY}) +elseif(NOT GNA_LIBRARY_VERSION STREQUAL "GNA1") + set_target_properties(libGNA PROPERTIES INTERFACE_LINK_OPTIONS "-Wl,-rpath-link,${libGNA_LIBRARIES_BASE_PATH}") +endif () diff --git a/inference-engine/cmake/check_features.cmake b/inference-engine/cmake/check_features.cmake index 71c9007b032152..3e89b168a39944 100644 --- a/inference-engine/cmake/check_features.cmake +++ b/inference-engine/cmake/check_features.cmake @@ -24,8 +24,6 @@ endif() if (APPLE) set(ENABLE_GNA OFF) set(ENABLE_CLDNN OFF) - SET(ENABLE_MYRIAD OFF) - SET(ENABLE_VPU OFF) endif() @@ -66,18 +64,39 @@ if (ENABLE_MKL_DNN) add_definitions(-DENABLE_MKL_DNN=1) endif() -if (ENABLE_UNICODE_PATH_SUPPORT) - add_definitions(-DENABLE_UNICODE_PATH_SUPPORT=1) -endif() - if (ENABLE_GNA) add_definitions(-DENABLE_GNA) + + set (DEFAULT_GNA_LIB GNA1_1401) + + # "GNA library version: GNA1|GNA1_1401|GNA2" - default is 1401 + if (NOT GNA_LIBRARY_VERSION STREQUAL "GNA1" + AND NOT GNA_LIBRARY_VERSION STREQUAL "GNA1_1401" + AND NOT GNA_LIBRARY_VERSION STREQUAL "GNA2") + set (GNA_LIBRARY_VERSION ${DEFAULT_GNA_LIB}) + message(STATUS "GNA_LIBRARY_VERSION not set. Can be GNA1, GNA1_1401 or GNA2. Default is ${GNA_LIBRARY_VERSION}") + endif() + + if (GNA_LIBRARY_VERSION STREQUAL "GNA2") + message(WARNING "GNA2 is not currently supported. Fallback to ${DEFAULT_GNA_LIB}") + set(GNA_LIBRARY_VERSION ${DEFAULT_GNA_LIB}) + endif() + + if (UNIX AND NOT APPLE AND CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.4) + message(WARNING "${GNA_LIBRARY_VERSION} no supported on GCC version ${CMAKE_CXX_COMPILER_VERSION}. Fallback to GNA1") + set(GNA_LIBRARY_VERSION GNA1) + endif() + + set(GNA_LIBRARY_VERSION "${GNA_LIBRARY_VERSION}" CACHE STRING "GNAVersion" FORCE) + list (APPEND IE_OPTIONS GNA_LIBRARY_VERSION) endif() if (ENABLE_SAMPLES) set (ENABLE_SAMPLES_CORE ON) endif() +#models dependend tests + if (DEVELOPMENT_PLUGIN_MODE) message (STATUS "Enabled development plugin mode") @@ -93,8 +112,18 @@ if (DEVELOPMENT_PLUGIN_MODE) endif() endif() +if (NOT ENABLE_TESTS) + set(ENABLE_GNA_MODELS OFF) +endif () + if (VERBOSE_BUILD) set(CMAKE_VERBOSE_MAKEFILE ON) endif() + +if(ENABLE_DUMP) + add_definitions(-DDEBUG_DUMP) +endif() + + print_enabled_features() diff --git a/inference-engine/cmake/config.cmake.in b/inference-engine/cmake/config.cmake.in index d9a6918d6ce96d..ebe82ee90a76b1 100644 --- a/inference-engine/cmake/config.cmake.in +++ b/inference-engine/cmake/config.cmake.in @@ -7,6 +7,9 @@ if(DEFINED IE_MAIN_SOURCE_DIR AND TARGET inference_engine) set(InferenceEngine_LIBRARIES inference_engine) else() include("${CMAKE_CURRENT_LIST_DIR}/targets.cmake") + if(NOT WIN32) + set_target_properties(IE::inference_engine PROPERTIES INTERFACE_COMPILE_OPTIONS "-Wno-error=deprecated-declarations") + endif() get_target_property(InferenceEngine_INCLUDE_DIRS IE::inference_engine INTERFACE_INCLUDE_DIRECTORIES) set(InferenceEngine_LIBRARIES IE::inference_engine) endif() diff --git a/inference-engine/cmake/dependencies.cmake b/inference-engine/cmake/dependencies.cmake index 00a5b8e0f73fec..682f2e556392f5 100644 --- a/inference-engine/cmake/dependencies.cmake +++ b/inference-engine/cmake/dependencies.cmake @@ -11,46 +11,26 @@ set_temp_directory(TEMP "${IE_MAIN_SOURCE_DIR}") include(ExternalProject) -if (ENABLE_SAME_BRANCH_FOR_MODELS) - branchName(MODELS_BRANCH) -else() - set(MODELS_BRANCH "master") -endif() - include(linux_name) if(COMMAND get_linux_name) get_linux_name(LINUX_OS_NAME) endif() if (ENABLE_MYRIAD) - RESOLVE_DEPENDENCY(VPU_FIRMWARE_MA2450 - ARCHIVE_UNIFIED firmware_ma2450_676.zip - TARGET_PATH "${TEMP}/vpu/firmware/ma2450" - ENVIRONMENT "VPU_FIRMWARE_MA2450" - FOLDER) - debug_message(STATUS "ma2450=" ${VPU_FIRMWARE_MA2450}) -endif () - -if (ENABLE_MYRIAD) - RESOLVE_DEPENDENCY(VPU_FIRMWARE_MA2X8X - ARCHIVE_UNIFIED firmware_ma2x8x_mdk_R8_9.zip - TARGET_PATH "${TEMP}/vpu/firmware/ma2x8x" - ENVIRONMENT "VPU_FIRMWARE_MA2X8X" - FOLDER) - debug_message(STATUS "ma2x8x=" ${VPU_FIRMWARE_MA2X8X}) -endif () + include(vpu_dependencies) +endif() ## enable cblas_gemm from OpenBLAS package if (GEMM STREQUAL "OPENBLAS") -if(NOT BLAS_LIBRARIES OR NOT BLAS_INCLUDE_DIRS) - find_package(BLAS REQUIRED) - if(BLAS_FOUND) - find_path(BLAS_INCLUDE_DIRS cblas.h) - else() - message(ERROR "OpenBLAS not found: install OpenBLAS or set -DBLAS_INCLUDE_DIRS= and -DBLAS_LIBRARIES=") + if(NOT BLAS_LIBRARIES OR NOT BLAS_INCLUDE_DIRS) + find_package(BLAS REQUIRED) + if(BLAS_FOUND) + find_path(BLAS_INCLUDE_DIRS cblas.h) + else() + message(ERROR "OpenBLAS not found: install OpenBLAS or set -DBLAS_INCLUDE_DIRS= and -DBLAS_LIBRARIES=") + endif() endif() -endif() -debug_message(STATUS "openblas=" ${BLAS_LIBRARIES}) + debug_message(STATUS "openblas=" ${BLAS_LIBRARIES}) endif () #MKL-ml package @@ -64,111 +44,116 @@ endif () ## Intel OMP package if (THREADING STREQUAL "OMP") -if (WIN32) - RESOLVE_DEPENDENCY(OMP - ARCHIVE_WIN "iomp.zip" - TARGET_PATH "${TEMP}/omp" - ENVIRONMENT "OMP" - VERSION_REGEX ".*_([a-z]*_([a-z0-9]+\\.)*[0-9]+).*") -elseif(LINUX) - RESOLVE_DEPENDENCY(OMP - ARCHIVE_LIN "iomp.tgz" - TARGET_PATH "${TEMP}/omp" - ENVIRONMENT "OMP" - VERSION_REGEX ".*_([a-z]*_([a-z0-9]+\\.)*[0-9]+).*") -else(APPLE) - RESOLVE_DEPENDENCY(OMP - ARCHIVE_MAC "iomp_20190130_mac.tgz" - TARGET_PATH "${TEMP}/omp" - ENVIRONMENT "OMP" - VERSION_REGEX ".*_([a-z]*_([a-z0-9]+\\.)*[0-9]+).*") -endif() -log_rpath_from_dir(OMP "${OMP}/lib") -debug_message(STATUS "intel_omp=" ${OMP}) + if (WIN32) + RESOLVE_DEPENDENCY(OMP + ARCHIVE_WIN "iomp.zip" + TARGET_PATH "${TEMP}/omp" + ENVIRONMENT "OMP" + VERSION_REGEX ".*_([a-z]*_([a-z0-9]+\\.)*[0-9]+).*") + elseif(LINUX) + RESOLVE_DEPENDENCY(OMP + ARCHIVE_LIN "iomp.tgz" + TARGET_PATH "${TEMP}/omp" + ENVIRONMENT "OMP" + VERSION_REGEX ".*_([a-z]*_([a-z0-9]+\\.)*[0-9]+).*") + else(APPLE) + RESOLVE_DEPENDENCY(OMP + ARCHIVE_MAC "iomp_20190130_mac.tgz" + TARGET_PATH "${TEMP}/omp" + ENVIRONMENT "OMP" + VERSION_REGEX ".*_([a-z]*_([a-z0-9]+\\.)*[0-9]+).*") + endif() + log_rpath_from_dir(OMP "${OMP}/lib") + debug_message(STATUS "intel_omp=" ${OMP}) endif () ## TBB package if (THREADING STREQUAL "TBB" OR THREADING STREQUAL "TBB_AUTO") -if (WIN32) - #TODO: add target_path to be platform specific as well, to avoid following if - RESOLVE_DEPENDENCY(TBB - ARCHIVE_WIN "tbb2019_20181010_win.zip" #TODO: windows zip archive created incorrectly using old name for folder - TARGET_PATH "${TEMP}/tbb" - ENVIRONMENT "TBBROOT" - VERSION_REGEX ".*_([a-z]*_([a-z0-9]+\\.)*[0-9]+).*") -elseif(LINUX) - RESOLVE_DEPENDENCY(TBB - ARCHIVE_LIN "tbb2019_20181010_lin.tgz" - TARGET_PATH "${TEMP}/tbb" - ENVIRONMENT "TBBROOT") -else(APPLE) - RESOLVE_DEPENDENCY(TBB - ARCHIVE_MAC "tbb2019_20190414_mac.tgz" - TARGET_PATH "${TEMP}/tbb" - ENVIRONMENT "TBBROOT" - VERSION_REGEX ".*_([a-z]*_([a-z0-9]+\\.)*[0-9]+).*") -endif() -log_rpath_from_dir(TBB "${TBB}/lib") -debug_message(STATUS "tbb=" ${TBB}) + if (WIN32) + #TODO: add target_path to be platform specific as well, to avoid following if + RESOLVE_DEPENDENCY(TBB + ARCHIVE_WIN "tbb2019_20181010_win.zip" #TODO: windows zip archive created incorrectly using old name for folder + TARGET_PATH "${TEMP}/tbb" + ENVIRONMENT "TBBROOT" + VERSION_REGEX ".*_([a-z]*_([a-z0-9]+\\.)*[0-9]+).*") + elseif(LINUX) + RESOLVE_DEPENDENCY(TBB + ARCHIVE_LIN "tbb2019_20181010_lin.tgz" + TARGET_PATH "${TEMP}/tbb" + ENVIRONMENT "TBBROOT") + else(APPLE) + RESOLVE_DEPENDENCY(TBB + ARCHIVE_MAC "tbb2019_20190414_v1_mac.tgz" + TARGET_PATH "${TEMP}/tbb" + ENVIRONMENT "TBBROOT" + VERSION_REGEX ".*_([a-z]*_([a-z0-9]+\\.)*[0-9]+).*") + endif() + log_rpath_from_dir(TBB "${TBB}/lib") + debug_message(STATUS "tbb=" ${TBB}) endif () if (ENABLE_OPENCV) - set(OPENCV_VERSION "4.1.1") - set(OPENCV_BUILD "595") - set(OPENCV_SUFFIX "") -if (WIN32) - RESOLVE_DEPENDENCY(OPENCV - ARCHIVE_WIN "opencv_${OPENCV_VERSION}-${OPENCV_BUILD}.zip" - TARGET_PATH "${TEMP}/opencv_${OPENCV_VERSION}" - ENVIRONMENT "OpenCV_DIR" - VERSION_REGEX ".*_([0-9]+.[0-9]+.[0-9]+).*") - log_rpath_from_dir(OPENCV "\\opencv_${OPENCV_VERSION}\\bin") - set( ENV{OpenCV_DIR} ${OPENCV}/cmake ) -elseif(APPLE) - RESOLVE_DEPENDENCY(OPENCV - ARCHIVE_MAC "opencv_${OPENCV_VERSION}-${OPENCV_BUILD}_osx.tar.xz" - TARGET_PATH "${TEMP}/opencv_${OPENCV_VERSION}_osx" - ENVIRONMENT "OpenCV_DIR" - VERSION_REGEX ".*_([0-9]+.[0-9]+.[0-9]+).*") - log_rpath_from_dir(OPENCV "opencv_${OPENCV_VERSION}_osx/lib") - set( ENV{OpenCV_DIR} ${OPENCV}/cmake ) -elseif(LINUX) - if (${LINUX_OS_NAME} STREQUAL "Ubuntu 16.04") - set(OPENCV_SUFFIX "ubuntu16") - elseif (${LINUX_OS_NAME} STREQUAL "Ubuntu 18.04") - set(OPENCV_SUFFIX "ubuntu18") - elseif (${LINUX_OS_NAME} STREQUAL "CentOS 7") - set(OPENCV_SUFFIX "centos7") - elseif (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "armv7l" AND - (${LINUX_OS_NAME} STREQUAL "Debian 9" OR - ${LINUX_OS_NAME} STREQUAL "Raspbian 9" OR - ${LINUX_OS_NAME} STREQUAL "Debian 10" OR - ${LINUX_OS_NAME} STREQUAL "Raspbian 10")) - set(OPENCV_SUFFIX "debian9arm") + set(OPENCV_VERSION "4.1.2") + set(OPENCV_BUILD "624") + set(OPENCV_SUFFIX "") + if (WIN32) + RESOLVE_DEPENDENCY(OPENCV + ARCHIVE_WIN "opencv_${OPENCV_VERSION}-${OPENCV_BUILD}.zip" + TARGET_PATH "${TEMP}/opencv_${OPENCV_VERSION}" + ENVIRONMENT "OpenCV_DIR" + VERSION_REGEX ".*_([0-9]+.[0-9]+.[0-9]+).*") + log_rpath_from_dir(OPENCV "\\opencv_${OPENCV_VERSION}\\bin") + elseif(APPLE) + RESOLVE_DEPENDENCY(OPENCV + ARCHIVE_MAC "opencv_${OPENCV_VERSION}-${OPENCV_BUILD}_osx.tar.xz" + TARGET_PATH "${TEMP}/opencv_${OPENCV_VERSION}_osx" + ENVIRONMENT "OpenCV_DIR" + VERSION_REGEX ".*_([0-9]+.[0-9]+.[0-9]+).*") + log_rpath_from_dir(OPENCV "opencv_${OPENCV_VERSION}_osx/lib") + elseif(LINUX) + if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "armv7l") + set(OPENCV_SUFFIX "debian9arm") + elseif (${LINUX_OS_NAME} STREQUAL "Ubuntu 16.04") + set(OPENCV_SUFFIX "ubuntu16") + elseif (${LINUX_OS_NAME} STREQUAL "Ubuntu 18.04") + set(OPENCV_SUFFIX "ubuntu18") + elseif (${LINUX_OS_NAME} STREQUAL "CentOS 7") + set(OPENCV_SUFFIX "centos7") + endif() endif() -endif() - -if (OPENCV_SUFFIX) - RESOLVE_DEPENDENCY(OPENCV - ARCHIVE_LIN "opencv_${OPENCV_VERSION}-${OPENCV_BUILD}_${OPENCV_SUFFIX}.tar.xz" - TARGET_PATH "${TEMP}/opencv_${OPENCV_VERSION}_${OPENCV_SUFFIX}" - ENVIRONMENT "OpenCV_DIR" - VERSION_REGEX ".*_([0-9]+.[0-9]+.[0-9]+).*") - log_rpath_from_dir(OPENCV "opencv_${OPENCV_VERSION}_${OPENCV_SUFFIX}/lib") - set( ENV{OpenCV_DIR} ${OPENCV}/cmake ) -endif() -debug_message(STATUS "opencv=" ${OPENCV}) -set(OpenCV_DIR "${OPENCV}" CACHE PATH "Path to OpenCV in temp directory") + if (OPENCV_SUFFIX) + RESOLVE_DEPENDENCY(OPENCV + ARCHIVE_LIN "opencv_${OPENCV_VERSION}-${OPENCV_BUILD}_${OPENCV_SUFFIX}.tar.xz" + TARGET_PATH "${TEMP}/opencv_${OPENCV_VERSION}_${OPENCV_SUFFIX}" + ENVIRONMENT "OpenCV_DIR" + VERSION_REGEX ".*_([0-9]+.[0-9]+.[0-9]+).*") + log_rpath_from_dir(OPENCV "opencv_${OPENCV_VERSION}_${OPENCV_SUFFIX}/lib") + endif() + debug_message(STATUS "opencv=" ${OPENCV}) + # OpenCV_DIR should point to cmake folder within the specified OpenCV binary package. + # It's required to successsfully find OpenCV libs using find_package(OpenCV ...) command. + # So, the cached OpenCV_DIR variable should be update if custom value wasn't previously set here. + if (NOT DEFINED ENV{OpenCV_DIR}) + set(OpenCV_DIR "${OPENCV}/cmake" CACHE PATH "Path to OpenCV in temp directory") + endif() endif() - include(ie_parallel) if (ENABLE_GNA) - RESOLVE_DEPENDENCY(GNA - ARCHIVE_UNIFIED "gna_20181120.zip" - TARGET_PATH "${TEMP}/gna") + if (GNA_LIBRARY_VERSION STREQUAL "GNA1") + RESOLVE_DEPENDENCY(GNA + ARCHIVE_UNIFIED "gna_20181120.zip" + TARGET_PATH "${TEMP}/gna") + elseif(GNA_LIBRARY_VERSION STREQUAL "GNA1_1401") + set(GNA_VERSION "01.00.00.1401") + RESOLVE_DEPENDENCY(GNA + ARCHIVE_UNIFIED "GNA_${GNA_VERSION}.zip" + TARGET_PATH "${TEMP}/gna_${GNA_VERSION}" + VERSION_REGEX ".*_([0-9]+.[0-9]+.[0-9]+.[0-9]+).*") + endif() + debug_message(STATUS "gna=" ${GNA}) endif() configure_file( diff --git a/inference-engine/cmake/developer_package.cmake b/inference-engine/cmake/developer_package.cmake index 52e0fefd0b078c..a27143d64279c7 100644 --- a/inference-engine/cmake/developer_package.cmake +++ b/inference-engine/cmake/developer_package.cmake @@ -6,7 +6,7 @@ include(debug) if (UNIX AND NOT APPLE) - set(LINUX TRUE) + set(LINUX ON) endif() string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} ARCH_FOLDER) @@ -68,16 +68,14 @@ set(CMAKE_RELEASE_POSTFIX ${IE_RELEASE_POSTFIX}) if (WIN32) # Support CMake multiconfiguration for Visual Studio build set(IE_BUILD_POSTFIX $<$:${IE_DEBUG_POSTFIX}>$<$:${IE_RELEASE_POSTFIX}>) - set(IE_BUILD_CONFIGURATION $) else () if (${CMAKE_BUILD_TYPE} STREQUAL "Debug" ) set(IE_BUILD_POSTFIX ${IE_DEBUG_POSTFIX}) else() set(IE_BUILD_POSTFIX ${IE_RELEASE_POSTFIX}) endif() - set(IE_BUILD_CONFIGURATION ${CMAKE_BUILD_TYPE}) endif() -message(STATUS "BUILD_CONFIGURATION: ${IE_BUILD_CONFIGURATION}") +message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") add_definitions(-DIE_BUILD_POSTFIX=\"${IE_BUILD_POSTFIX}\") @@ -95,12 +93,12 @@ if(NOT UNIX) set(LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_ROOT}/${BIN_FOLDER}) set(LIBRARY_OUTPUT_PATH ${LIBRARY_OUTPUT_DIRECTORY}) # compatibility issue: linux uses LIBRARY_OUTPUT_PATH, windows uses LIBRARY_OUTPUT_DIRECTORY else() - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_ROOT}/${BIN_FOLDER}/${IE_BUILD_CONFIGURATION}/lib) - set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_ROOT}/${BIN_FOLDER}/${IE_BUILD_CONFIGURATION}/lib) - set(CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY ${OUTPUT_ROOT}/${BIN_FOLDER}/${IE_BUILD_CONFIGURATION}) - set(CMAKE_PDB_OUTPUT_DIRECTORY ${OUTPUT_ROOT}/${BIN_FOLDER}/${IE_BUILD_CONFIGURATION}) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_ROOT}/${BIN_FOLDER}/${IE_BUILD_CONFIGURATION}) - set(LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_ROOT}/${BIN_FOLDER}/${IE_BUILD_CONFIGURATION}/lib) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_ROOT}/${BIN_FOLDER}/${CMAKE_BUILD_TYPE}/lib) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_ROOT}/${BIN_FOLDER}/${CMAKE_BUILD_TYPE}/lib) + set(CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY ${OUTPUT_ROOT}/${BIN_FOLDER}/${CMAKE_BUILD_TYPE}) + set(CMAKE_PDB_OUTPUT_DIRECTORY ${OUTPUT_ROOT}/${BIN_FOLDER}/${CMAKE_BUILD_TYPE}) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_ROOT}/${BIN_FOLDER}/${CMAKE_BUILD_TYPE}) + set(LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_ROOT}/${BIN_FOLDER}/${CMAKE_BUILD_TYPE}/lib) set(LIBRARY_OUTPUT_PATH ${LIBRARY_OUTPUT_DIRECTORY}/lib) endif() diff --git a/inference-engine/cmake/download_and_extract.cmake b/inference-engine/cmake/download_and_extract.cmake index bd837be292c27a..930a640355ac31 100644 --- a/inference-engine/cmake/download_and_extract.cmake +++ b/inference-engine/cmake/download_and_extract.cmake @@ -145,7 +145,7 @@ function (CheckOrDownloadAndExtract component RELATIVE_URL archive_name unpacked if(DEFINED ENV{IE_PATH_TO_DEPS}) set(URL "$ENV{IE_PATH_TO_DEPS}/${RELATIVE_URL}") else() - set(URL "https://download.01.org/opencv/2019/openvinotoolkit/R2/inference_engine/${RELATIVE_URL}") + set(URL "https://download.01.org/opencv/2019/openvinotoolkit/R3/inference_engine/${RELATIVE_URL}") endif() #no message on recursive calls diff --git a/inference-engine/cmake/features.cmake b/inference-engine/cmake/features.cmake index 9fa537dbff439f..2d7c8270a0ad5d 100644 --- a/inference-engine/cmake/features.cmake +++ b/inference-engine/cmake/features.cmake @@ -4,15 +4,20 @@ include (options) -#this options are aimed to optimize build time on development system +#these options are aimed to optimize build time on development system #backed targets ie_option (ENABLE_GNA "GNA support for inference engine" ON) +ie_option (ENABLE_ROCKHOPER "use Rockhopper decoder for converting / output scores" ON) ie_option (ENABLE_MKL_DNN "MKL-DNN plugin for inference engine" ON) ie_option (ENABLE_CLDNN "clDnn based plugin for inference engine" ON) +ie_option (ENABLE_CLDNN_TESTS "Enable clDNN unit tests" OFF) + +ie_option (ENABLE_CLDNN_BUILD "build clDnn from sources" OFF) + ie_option (ENABLE_PROFILING_ITT "ITT tracing of IE and plugins internals" ON) ie_option (ENABLE_PROFILING_RAW "Raw counters profiling (just values, no start/stop time or timeline)" OFF) @@ -90,8 +95,18 @@ ie_option (DEVELOPMENT_PLUGIN_MODE "Disabled build of all plugins" OFF) ie_option (TREAT_WARNING_AS_ERROR "Treat build warnings as errors" ON) +ie_option (ENABLE_CPP_CCT "enables C++ version of Cross Check Tool" OFF) + ie_option (ENABLE_UNICODE_PATH_SUPPORT "Enable loading models from Unicode paths" ON) +ie_option (ENABLE_LTO "Enable Link Time Optimization" OFF) + +# FIXME: there are compiler failures with LTO and Cross-Compile toolchains. Disabling for now, but +# this must be addressed in a proper way +if(CMAKE_CROSSCOMPILING OR NOT (UNIX AND NOT APPLE)) + set(ENABLE_LTO OFF) +endif() + if (UNIX AND NOT APPLE AND CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) set(ENABLE_UNICODE_PATH_SUPPORT OFF) endif() diff --git a/inference-engine/cmake/ie_parallel.cmake b/inference-engine/cmake/ie_parallel.cmake index 8265701a57726d..97e8c5e9588da7 100644 --- a/inference-engine/cmake/ie_parallel.cmake +++ b/inference-engine/cmake/ie_parallel.cmake @@ -6,57 +6,77 @@ function(set_ie_threading_interface_for TARGET_NAME) set(IE_THREAD_DEFINE "IE_THREAD_SEQ") if (THREADING STREQUAL "TBB" OR THREADING STREQUAL "TBB_AUTO") - if (NOT (IE_MAIN_SOURCE_DIR)) - set(incl_path ${IE_EXTERNAL_DIR}/tbb/include) - if (WIN32) - set(lib_rel_path ${IE_LIB_REL_DIR}) - set(lib_dbg_path ${IE_LIB_DBG_DIR}) + if (DEFINED ENV{TBBROOT}) + # Check TBB package in case if custom TBBROOT path configured + find_package(TBB QUIET PATHS "$ENV{TBBROOT}/cmake") + if (TBB_FOUND) + set(IE_THREAD_DEFINE "IE_THREAD_TBB") + if (WIN32) + target_link_libraries(${TARGET_NAME} PUBLIC "-nodefaultlib:vcomp") + endif () + target_link_libraries(${TARGET_NAME} PUBLIC ${TBB_IMPORTED_TARGETS}) + else () + # TBB was not found by the configured TBBROOT path, SEQ method will be used + ext_message(WARNING "TBB not found by the configured TBBROOT path $ENV{TBBROOT}") + endif () + else() + if (NOT (IE_MAIN_SOURCE_DIR)) + set(incl_path ${IE_EXTERNAL_DIR}/tbb/include) + if (WIN32) + set(lib_rel_path ${IE_LIB_REL_DIR}) + set(lib_dbg_path ${IE_LIB_DBG_DIR}) + else () + set(lib_rel_path ${IE_EXTERNAL_DIR}/tbb/lib) + set(lib_dbg_path ${lib_rel_path}) + endif () else () - set(lib_rel_path ${IE_EXTERNAL_DIR}/tbb/lib) + set(incl_path ${TBB}/include) + set(lib_rel_path ${TBB}/lib) set(lib_dbg_path ${lib_rel_path}) endif () - else () - set(incl_path ${TBB}/include) - set(lib_rel_path ${TBB}/lib) - set(lib_dbg_path ${lib_rel_path}) - endif () - if (NOT TBB_INCLUDE_DIRS OR NOT TBB_LIBRARIES_RELEASE) - find_path(TBB_INCLUDE_DIRS tbb/tbb.h ${incl_path} NO_DEFAULT_PATH) - find_library(TBB_LIBRARIES_RELEASE tbb ${lib_rel_path} NO_DEFAULT_PATH) - find_library(TBB_LIBRARIES_DEBUG tbb_debug ${lib_dbg_path} NO_DEFAULT_PATH) - ext_message(STATUS "TBB include: ${TBB_INCLUDE_DIRS}") - ext_message(STATUS "TBB Release lib: ${TBB_LIBRARIES_RELEASE}") - ext_message(STATUS "TBB Debug lib: ${TBB_LIBRARIES_DEBUG}") - endif () - - if (NOT TBB_INCLUDE_DIRS OR NOT TBB_LIBRARIES_RELEASE) - ext_message(WARNING "TBB not found. TBB support will be disabled. ${IE_THREAD_DEFINE} is defined") - else () - set(IE_THREAD_DEFINE "IE_THREAD_TBB") - - target_include_directories(${TARGET_NAME} PUBLIC ${TBB_INCLUDE_DIRS}) - if (WIN32) - target_link_libraries(${TARGET_NAME} PUBLIC "-nodefaultlib:vcomp") + if (NOT TBB_INCLUDE_DIRS OR NOT TBB_LIBRARIES_RELEASE) + find_path(TBB_INCLUDE_DIRS tbb/tbb.h ${incl_path} NO_DEFAULT_PATH) + find_library(TBB_LIBRARIES_RELEASE tbb ${lib_rel_path} NO_DEFAULT_PATH) + ext_message(STATUS "TBB include: ${TBB_INCLUDE_DIRS}") + ext_message(STATUS "TBB Release lib: ${TBB_LIBRARIES_RELEASE}") + if (NOT LINUX) + find_library(TBB_LIBRARIES_DEBUG tbb_debug ${lib_dbg_path} NO_DEFAULT_PATH) + if (TBB_LIBRARIES_DEBUG) + ext_message(STATUS "TBB Debug lib: ${TBB_LIBRARIES_DEBUG}") + else () + ext_message(WARNING "TBB Debug binaries are missed.") + endif () + endif () endif () - # Debug binaries are optional. - if (TBB_LIBRARIES_DEBUG) + if (NOT TBB_INCLUDE_DIRS OR NOT TBB_LIBRARIES_RELEASE) + ext_message(WARNING "TBB not found. TBB support will be disabled. ${IE_THREAD_DEFINE} is defined") + else () + set(IE_THREAD_DEFINE "IE_THREAD_TBB") + + target_include_directories(${TARGET_NAME} PUBLIC ${TBB_INCLUDE_DIRS}) if (WIN32) - target_link_libraries(${TARGET_NAME} PUBLIC "$<$:${TBB_LIBRARIES_DEBUG}>;$<$>:${TBB_LIBRARIES_RELEASE}>") - else () - if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - target_link_libraries(${TARGET_NAME} PUBLIC ${TBB_LIBRARIES_DEBUG}) - else() - target_link_libraries(${TARGET_NAME} PUBLIC ${TBB_LIBRARIES_RELEASE}) + target_link_libraries(${TARGET_NAME} PUBLIC "-nodefaultlib:vcomp") + endif () + + # Debug binaries are optional. + if (TBB_LIBRARIES_DEBUG AND NOT LINUX) + if (WIN32) + target_link_libraries(${TARGET_NAME} PUBLIC "$<$:${TBB_LIBRARIES_DEBUG}>;$<$>:${TBB_LIBRARIES_RELEASE}>") + else () + if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + target_link_libraries(${TARGET_NAME} PUBLIC ${TBB_LIBRARIES_DEBUG}) + else() + target_link_libraries(${TARGET_NAME} PUBLIC ${TBB_LIBRARIES_RELEASE}) + endif () endif () + else () + # Link Release library to all configurations. + target_link_libraries(${TARGET_NAME} PUBLIC ${TBB_LIBRARIES_RELEASE}) endif () - else () - # Link Release library to all configurations. - ext_message(WARNING "TBB Debug binaries are missed.") - target_link_libraries(${TARGET_NAME} PUBLIC ${TBB_LIBRARIES_RELEASE}) endif () - endif () + endif() elseif (THREADING STREQUAL "OMP") if (WIN32) set(omp_lib_name libiomp5md) @@ -79,9 +99,15 @@ function(set_ie_threading_interface_for TARGET_NAME) if (NOT OMP_LIBRARIES_RELEASE) find_library(OMP_LIBRARIES_RELEASE ${omp_lib_name} ${lib_rel_path} NO_DEFAULT_PATH) - find_library(OMP_LIBRARIES_DEBUG ${omp_lib_name} ${lib_dbg_path} NO_DEFAULT_PATH) ext_message(STATUS "OMP Release lib: ${OMP_LIBRARIES_RELEASE}") - ext_message(STATUS "OMP Debug lib: ${OMP_LIBRARIES_DEBUG}") + if (NOT LINUX) + find_library(OMP_LIBRARIES_DEBUG ${omp_lib_name} ${lib_dbg_path} NO_DEFAULT_PATH) + if (OMP_LIBRARIES_DEBUG) + ext_message(STATUS "OMP Debug lib: ${OMP_LIBRARIES_DEBUG}") + else () + ext_message(WARNING "OMP Debug binaries are missed.") + endif () + endif () endif () if (NOT OMP_LIBRARIES_RELEASE) @@ -98,7 +124,7 @@ function(set_ie_threading_interface_for TARGET_NAME) endif () # Debug binaries are optional. - if (OMP_LIBRARIES_DEBUG) + if (OMP_LIBRARIES_DEBUG AND NOT LINUX) if (WIN32) target_link_libraries(${TARGET_NAME} PUBLIC "$<$:${OMP_LIBRARIES_DEBUG}>;$<$>:${OMP_LIBRARIES_RELEASE}>") else() @@ -110,7 +136,6 @@ function(set_ie_threading_interface_for TARGET_NAME) endif () else () # Link Release library to all configurations. - ext_message(WARNING "OMP Debug binaries are missed.") target_link_libraries(${TARGET_NAME} PUBLIC ${OMP_LIBRARIES_RELEASE}) endif () endif () diff --git a/inference-engine/cmake/os_flags.cmake b/inference-engine/cmake/os_flags.cmake index 6a5442f854c79f..ad1585997427f8 100644 --- a/inference-engine/cmake/os_flags.cmake +++ b/inference-engine/cmake/os_flags.cmake @@ -4,9 +4,9 @@ macro(disable_deprecated_warnings) if(WIN32) - if("${CMAKE_CXX_COMPILER_ID}" MATCHES Intel) + if(CMAKE_CXX_COMPILER_ID MATCHES Intel) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Qdiag-warning:1478") - elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL MSVC) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4996") # disable warning on deprecated API endif() else() @@ -29,7 +29,6 @@ if (WIN32) endif() endif() - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Z7") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Z7") @@ -38,7 +37,7 @@ if (WIN32) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Z7") set(DEBUG_SYMBOLS_LINKER_FLAGS "/DEBUG") - if ("${CMAKE_BUILD_TYPE}" STREQUAL "Release") + if (CMAKE_BUILD_TYPE STREQUAL "Release") # Keep default /OPT values. See /DEBUG reference for details. set(DEBUG_SYMBOLS_LINKER_FLAGS "${DEBUG_SYMBOLS_LINKER_FLAGS} /OPT:REF /OPT:ICF") endif() @@ -51,12 +50,28 @@ else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Werror=return-type ") if (APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=unused-command-line-argument") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-function") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-variable") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-private-field") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-reorder") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wswitch") elseif(UNIX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wuninitialized -Winit-self") - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-switch") else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wmaybe-uninitialized") endif() endif() + + if(CMAKE_CXX_COMPILER_ID STREQUAL "Intel") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -diag-disable=remark") + endif() + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden") + + if(LINUX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,--exclude-libs,ALL") + endif() endif() diff --git a/inference-engine/cmake/sdl.cmake b/inference-engine/cmake/sdl.cmake index ee57890454afa5..b1a355fa75b685 100644 --- a/inference-engine/cmake/sdl.cmake +++ b/inference-engine/cmake/sdl.cmake @@ -2,12 +2,16 @@ # SPDX-License-Identifier: Apache-2.0 # -if (UNIX OR APPLE AND ${CMAKE_BUILD_TYPE} STREQUAL "Release") +if (UNIX OR APPLE AND CMAKE_BUILD_TYPE STREQUAL "Release") set(CMAKE_CCXX_FLAGS "${CMAKE_CCXX_FLAGS} -fPIE -fPIC -Wformat -Wformat-security") + # TODO: double check it it's OK + if(CMAKE_CXX_COMPILER_ID MATCHES Intel) + string(REPLACE "-fPIE" "" CMAKE_CCXX_FLAGS "${CMAKE_CCXX_FLAGS}") + endif() set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -D_FORTIFY_SOURCE=2") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -D_FORTIFY_SOURCE=2") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pie") - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -z noexecstack -z relro -z now") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -z noexecstack -z relro -z now") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) @@ -17,12 +21,12 @@ if (UNIX OR APPLE AND ${CMAKE_BUILD_TYPE} STREQUAL "Release") endif() set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s -fvisibility=hidden") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s -fvisibility=hidden") - elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_CCXX_FLAGS "${CMAKE_CCXX_FLAGS} -fstack-protector-all") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fvisibility=hidden") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fvisibility=hidden") - elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector") + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-strong") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -z noexecstack -z relro -z now") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -z noexecstack -z relro -z now") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wl,--strip-all -fvisibility=hidden") @@ -32,7 +36,7 @@ if (UNIX OR APPLE AND ${CMAKE_BUILD_TYPE} STREQUAL "Release") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CCXX_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CCXX_FLAGS}") elseif (WIN32) - if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC) + if (CMAKE_CXX_COMPILER_ID STREQUAL MSVC) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MP /sdl") endif() endif() diff --git a/inference-engine/cmake/share/InferenceEngineConfig-version.cmake.in b/inference-engine/cmake/share/InferenceEngineConfig-version.cmake.in index c7911a622b9383..bdfa5f388da6a5 100644 --- a/inference-engine/cmake/share/InferenceEngineConfig-version.cmake.in +++ b/inference-engine/cmake/share/InferenceEngineConfig-version.cmake.in @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # -set(InferenceEngine_VERSION 2.0.0) +set(InferenceEngine_VERSION 2.1.0) set(PACKAGE_VERSION ${InferenceEngine_VERSION}) set(PACKAGE_VERSION_EXACT False) diff --git a/inference-engine/cmake/share/InferenceEngineConfig.cmake.in b/inference-engine/cmake/share/InferenceEngineConfig.cmake.in index 3bbb0cf7ec19f2..0d492277a50537 100644 --- a/inference-engine/cmake/share/InferenceEngineConfig.cmake.in +++ b/inference-engine/cmake/share/InferenceEngineConfig.cmake.in @@ -121,7 +121,8 @@ else() elseif (APPLE) set_target_properties(IE::inference_engine PROPERTIES IMPORTED_LOCATION_RELEASE "${IE_RELEASE_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${IE_INCLUDE_DIR}") + INTERFACE_INCLUDE_DIRECTORIES "${IE_INCLUDE_DIR}" + INTERFACE_COMPILE_OPTIONS "-Wno-error=deprecated-declarations") # Debug binaries are optional find_library(IE_DEBUG_LIBRARY inference_engine@IE_DEBUG_POSTFIX_MAC@ "${IE_LIB_DIR}" NO_DEFAULT_PATH) @@ -137,7 +138,8 @@ else() # Only Release binaries are distributed for Linux systems set_target_properties(IE::inference_engine PROPERTIES IMPORTED_LOCATION "${IE_RELEASE_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${IE_INCLUDE_DIR}") + INTERFACE_INCLUDE_DIRECTORIES "${IE_INCLUDE_DIR}" + INTERFACE_COMPILE_OPTIONS "-Wno-error=deprecated-declarations") target_link_libraries(IE::inference_engine INTERFACE ${CMAKE_DL_LIBS}) endif() diff --git a/inference-engine/cmake/vpu_dependencies.cmake b/inference-engine/cmake/vpu_dependencies.cmake new file mode 100644 index 00000000000000..1550163d65f222 --- /dev/null +++ b/inference-engine/cmake/vpu_dependencies.cmake @@ -0,0 +1,68 @@ +# Copyright (C) 2019 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +# + +set(VPU_SUPPORTED_SOC ma2450 ma2x8x mv0262) + +# +# Default firmware packages +# + +RESOLVE_DEPENDENCY(VPU_FIRMWARE_MA2450 + ARCHIVE_UNIFIED firmware_ma2450_759W.zip + TARGET_PATH "${TEMP}/vpu/firmware/ma2450" + ENVIRONMENT "VPU_FIRMWARE_MA2450" + FOLDER) +debug_message(STATUS "ma2450=" ${VPU_FIRMWARE_MA2450}) + +RESOLVE_DEPENDENCY(VPU_FIRMWARE_MV0262 + ARCHIVE_UNIFIED firmware_mv0262_mdk_R9.8.zip + TARGET_PATH "${TEMP}/vpu/firmware/mv0262" + ENVIRONMENT "VPU_FIRMWARE_MV0262" + FOLDER) +debug_message(STATUS "mv0262=" ${VPU_FIRMWARE_MV0262}) + +RESOLVE_DEPENDENCY(VPU_FIRMWARE_MA2X8X + ARCHIVE_UNIFIED firmware_ma2x8x_mdk_R9.8.zip + TARGET_PATH "${TEMP}/vpu/firmware/ma2x8x" + ENVIRONMENT "VPU_FIRMWARE_MA2X8X" + FOLDER) +debug_message(STATUS "ma2x8x=" ${VPU_FIRMWARE_MA2X8X}) + +# +# CMake variables to override default firmware files +# + +foreach(soc IN LISTS VPU_SUPPORTED_SOC) + string(TOUPPER "${soc}" soc_upper) + set(var_name VPU_FIRMWARE_${soc_upper}_FILE) + + find_file(${var_name} MvNCAPI-${soc}.mvcmd "${VPU_FIRMWARE_${soc_upper}}/mvnc") + if(NOT ${var_name}) + message(FATAL_ERROR "[VPU] Missing ${soc} firmware") + endif() +endforeach() + +# +# `vpu_copy_firmware` CMake target +# + +foreach(soc IN LISTS VPU_SUPPORTED_SOC) + string(TOUPPER "${soc}" soc_upper) + set(var_name VPU_FIRMWARE_${soc_upper}_FILE) + + set(firmware_out_file "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/MvNCAPI-${soc}.mvcmd") + list(APPEND all_firmware_files ${firmware_out_file}) + + add_custom_command( + OUTPUT ${firmware_out_file} + COMMAND + ${CMAKE_COMMAND} -E copy ${${var_name}} ${firmware_out_file} + MAIN_DEPENDENCY ${${var_name}} + COMMENT "[VPU] Copy ${${var_name}} to ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}" + VERBATIM) +endforeach() + +add_custom_target(vpu_copy_firmware + DEPENDS ${all_firmware_files} + COMMENT "[VPU] Copy firmware files") diff --git a/inference-engine/ie_bridges/python/CMakeLists.txt b/inference-engine/ie_bridges/python/CMakeLists.txt index 9fca2145d36705..bba853fb289ecc 100644 --- a/inference-engine/ie_bridges/python/CMakeLists.txt +++ b/inference-engine/ie_bridges/python/CMakeLists.txt @@ -13,7 +13,7 @@ elseif(ARCH STREQUAL "i386") endif() # in case of independent python api build (out of Inference Engine root Cmake) -if (NOT(IE_MAIN_SOURCE_DIR)) +if (NOT DEFINED IE_MAIN_SOURCE_DIR) if("${CMAKE_BUILD_TYPE}" STREQUAL "") message(STATUS "CMAKE_BUILD_TYPE not defined, 'Release' will be used") set(CMAKE_BUILD_TYPE "Release") @@ -45,7 +45,11 @@ else() set (PYTHON_BRIDGE_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/python_api/${PYTHON_VERSION}/openvino) endif() -find_package (InferenceEngine REQUIRED) +if(DEFINED IE_MAIN_SOURCE_DIR) + find_package(InferenceEngine REQUIRED) +else() + find_package(InferenceEngineDeveloperPackage REQUIRED) +endif() set (PYTHON_BRIDGE_SRC_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) add_subdirectory (src/openvino/inference_engine) diff --git a/inference-engine/ie_bridges/python/docs/api_overview.md b/inference-engine/ie_bridges/python/docs/api_overview.md index e874177d6fb617..9cee6260f31e15 100644 --- a/inference-engine/ie_bridges/python/docs/api_overview.md +++ b/inference-engine/ie_bridges/python/docs/api_overview.md @@ -260,7 +260,7 @@ This class stores main information about the layer and allow to modify some laye * `weights`- Dictionary with layer weights, biases or custom blobs if any * `params` - Layer specific parameters. Provides getter and setter interfaces to get and modify layer parameters. - Please note that some modifications can be ignored and\or overwriten by target plugin (e.g. modification of + Please note that some modifications can be ignored and/or overwriten by target plugin (e.g. modification of convolution kernel size will be reflected in layer parameters but finally the plugin will ignore it and will use initial kernel size) @@ -280,9 +280,7 @@ layers affinity and output layers. * `init_from_buffer` - Defines the way of how `model` and `weights` attributes are interpreted. If `True`, attributes are interpreted as strings with paths to .xml and .bin files of IR. If `False`, they are interpreted as Python `bytes` object with .xml and .bin files content. - * `ngrpah_compatibility` - Default value: `False`. If `IENetwork` initializes from - [experimental IR V7](./docs/OperationsSpecification-V7.md), set to `True` - + * Usage examples: * Initializing `IENetwork` object from IR files: @@ -506,7 +504,7 @@ This class is the main plugin interface and serves to initialize and configure t * Description: Loads extensions library to the plugin. Applicable only for a CPU device and a HETERO device with CPU * Parameters: * `extension_path` - A full path to CPU extensions library - * Return value: None + * Return value: None * Usage example: ```py >>> plugin = IEPlugin(device="CPU") diff --git a/inference-engine/ie_bridges/python/sample/affinity_setting_sample/affinity_setting_sample.py b/inference-engine/ie_bridges/python/sample/affinity_setting_sample/affinity_setting_sample.py deleted file mode 100644 index e69de29bb2d1d6..00000000000000 diff --git a/inference-engine/ie_bridges/python/sample/benchmark_app/README.md b/inference-engine/ie_bridges/python/sample/benchmark_app/README.md deleted file mode 100644 index 7bb4b20f7ec0ab..00000000000000 --- a/inference-engine/ie_bridges/python/sample/benchmark_app/README.md +++ /dev/null @@ -1,155 +0,0 @@ -# Benchmark Python* Application - -This topic demonstrates how to run the Benchmark Application demo, which performs inference using convolutional networks. - -## How It Works - -Upon start-up, the application reads command-line parameters and loads a network and images/binary files to the Inference Engine -plugin, which is chosen depending on a specified device. The number of infer requests and execution approach depend -on the mode defined with the `-api` command-line parameter. - -> **NOTE**: By default, Inference Engine samples and demos expect input with BGR channels order. If you trained your model to work with RGB order, you need to manually rearrange the default channels order in the sample or demo application or reconvert your model using the Model Optimizer tool with `--reverse_input_channels` argument specified. For more information about the argument, refer to **When to Reverse Input Channels** section of [Converting a Model Using General Conversion Parameters](./docs/MO_DG/prepare_model/convert_model/Converting_Model_General.md). - -### Synchronous API - -For synchronous mode, the primary metric is latency. The application creates one infer request and executes the `Infer` method. A number of executions is defined by one of the two values: -* Number of iterations defined with the `-niter` command-line argument -* Time duration specified with the `-t` command-line argument -* Both of them (execution will continue until both conditions are met) -* Predefined duration if `-niter` and `-t` are not specified. Predefined duration value depends on device. - -During the execution, the application collects two types of metrics: -* Latency for each infer request executed with `Infer` method -* Duration of all executions - -Reported latency value is calculated as mean value of all collected latencies. Reported throughput value is a derivative from reported latency and additionally depends on batch size. - -### Asynchronous API -For asynchronous mode, the primary metric is throughput in frames per second (FPS). The application creates a certain number of infer requests and executes the `StartAsync` method. A number of infer is specified with the `-nireq` command-line parameter. A number of executions is defined by one of the two values: -* Number of iterations defined with the `-niter` command-line argument -* Time duration specified with the `-t` command-line argument -* Both of them (execution will continue until both conditions are met) -* Predefined duration if `-niter` and `-t` are not specified. Predefined duration value depends on device. - -The infer requests are executed asynchronously. Callback is used to wait for previous execution to complete. The application measures all infer requests executions and reports the throughput metric based on batch size and total execution duration. - -## Running -Notice that the benchmark_app usually produces optimal performance for any device out of the box. - -**So in most cases you don't need to play the app options explicitly and the plain device name is enough**, e.g.: -``` -$benchmark_app -m -i -d CPU -``` - -Running the application with the `-h` or `--help`' option yields the following usage message: - -``` -usage: benchmark_app.py [-h] [-i PATH_TO_INPUT] -m PATH_TO_MODEL - [-pp PLUGIN_DIR] [-d TARGET_DEVICE] - [-l PATH_TO_EXTENSION] [-c PATH_TO_CLDNN_CONFIG] - [-api {sync,async}] [-niter NUMBER_ITERATIONS] - [-nireq NUMBER_INFER_REQUESTS] [-b BATCH_SIZE] - [-stream_output [STREAM_OUTPUT]] [-t TIME] - [-progress [PROGRESS]] [-nstreams NUMBER_STREAMS] - [-nthreads NUMBER_THREADS] [-pin {YES,NO}] - [--exec_graph_path EXEC_GRAPH_PATH] - [-pc [PERF_COUNTS]] - -Options: - -h, --help Show this help message and exit. - -i PATH_TO_INPUT, --path_to_input PATH_TO_INPUT - Optional. Path to a folder with images and/or binaries - or to specific image or binary file. - -m PATH_TO_MODEL, --path_to_model PATH_TO_MODEL - Required. Path to an .xml file with a trained model. - -pp PLUGIN_DIR, --plugin_dir PLUGIN_DIR - Optional. Path to a plugin folder. - -d TARGET_DEVICE, --target_device TARGET_DEVICE - Optional. Specify a target device to infer on: CPU, - GPU, FPGA, HDDL or MYRIAD. - Use "-d HETERO:" format to specify HETERO plugin. - -l PATH_TO_EXTENSION, --path_to_extension PATH_TO_EXTENSION - Optional. Required for CPU custom layers. Absolute - path to a shared library with the kernels - implementations. - -c PATH_TO_CLDNN_CONFIG, --path_to_cldnn_config PATH_TO_CLDNN_CONFIG - Optional. Required for GPU custom kernels. Absolute - path to an .xml file with the kernels description. - -api {sync,async}, --api_type {sync,async} - Optional. Enable using sync/async API. Default value - is async. - -niter NUMBER_ITERATIONS, --number_iterations NUMBER_ITERATIONS - Optional. Number of iterations. If not specified, the - number of iterations is calculated depending on a - device. - -nireq NUMBER_INFER_REQUESTS, --number_infer_requests NUMBER_INFER_REQUESTS - Optional. Number of infer requests. Default value is - determined automatically for device. - -b BATCH_SIZE, --batch_size BATCH_SIZE - Optional. Batch size value. If not specified, the - batch size value is determined from IR - -stream_output [STREAM_OUTPUT] - Optional. Print progress as a plain text. When - specified, an interactive progress bar is replaced - with a multiline output. - -t TIME, --time TIME Optional. Time in seconds to execute topology. - -progress [PROGRESS] Optional. Show progress bar (can affect performance - measurement). Default values is "False". - -nstreams NUMBER_STREAMS, --number_streams NUMBER_STREAMS - Optional. Number of streams to use for inference on the CPU/GPU in throughput mode - (for HETERO device case use format :,: or just ). - -nthreads NUMBER_THREADS, --number_threads NUMBER_THREADS - Number of threads to use for inference on the CPU - (including HETERO case). - -pin {YES,NO}, --infer_threads_pinning {YES,NO} - Optional. Enable ("YES" is default value) or disable - ("NO")CPU threads pinning for CPU-involved inference. - --exec_graph_path EXEC_GRAPH_PATH - Optional. Path to a file where to store executable - graph information serialized. - -pc [PERF_COUNTS], --perf_counts [PERF_COUNTS] - Optional. Report performance counters. - -``` - -Running the application with the empty list of options yields the usage message given above and an error message. - -Application supports topologies with one or more inputs. If a topology is not data sensitive, you can skip the input parameter. In this case, inputs are filled with random values. -If a model has only image input(s), please a provide folder with images or a path to an image as input. -If a model has some specific input(s) (not images), please prepare a binary file(s), which is filled with data of appropriate precision and provide a path to them as input. -If a model has mixed input types, input folder should contain all required files. Image inputs are filled with image files one by one. Binary inputs are filled with binary inputs one by one. - -To run the demo, you can use public or pre-trained models. To download the pre-trained models, use the OpenVINO [Model Downloader](https://github.com/opencv/open_model_zoo/tree/2018/model_downloader) or go to [https://download.01.org/opencv/](https://download.01.org/opencv/). - -> **NOTE**: Before running the demo with a trained model, make sure the model is converted to the Inference Engine format (\*.xml + \*.bin) using the [Model Optimizer tool](./docs/MO_DG/Deep_Learning_Model_Optimizer_DevGuide.md). - -For example, to do inference of an image using a trained network with multiple outputs on CPU, run the following command: - -``` -python3 benchmark_app.py -i /inputImage.bmp -m /multiple-output.xml -d CPU -``` - -## Demo Output - -The application outputs number of executed iterations, total duration of execution, latency and throughput. -Additionally, if you set the `-pc` parameter, the application outputs performance counters. -If you set `-exec_graph_path`, the application reports executable graph information serialized. - -``` -[Step 8/9] Measuring performance (Start inference asyncronously, 60000 ms duration, 4 inference requests in parallel using 4 streams) -Progress: |................................| 100.00% - -[Step 9/9] Dumping statistics report -Progress: |................................| 100.00% - -Count: 4408 iterations -Duration: 60153.52 ms -Latency: 51.8244 ms -Throughput: 73.28 FPS - -``` - -## See Also -* [Using Inference Engine Samples](./docs/IE_DG/Samples_Overview.md) -* [Model Optimizer](./docs/MO_DG/Deep_Learning_Model_Optimizer_DevGuide.md) -* [Model Downloader](https://github.com/opencv/open_model_zoo/tree/2018/model_downloader) diff --git a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/benchmark.py b/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/benchmark.py deleted file mode 100644 index ccee15538961d7..00000000000000 --- a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/benchmark.py +++ /dev/null @@ -1,343 +0,0 @@ -""" - Copyright (C) 2018-2019 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -""" - -from statistics import median -from openvino.inference_engine import IENetwork, IECore, get_version - -from .utils.parameters import * -from .utils.inputs_filling import * -from .utils.utils import * -from .utils.infer_request_wrap import * -from .utils.progress_bar import * - -def getDurationInMilliseconds(duration): - return duration * 1000 - -def static_vars(**kwargs): - def decorate(func): - for k in kwargs: - setattr(func, k, kwargs[k]) - return func - return decorate - -@static_vars(step_id = 0) -def next_step(additional_info = ""): - step_names = { - 1 : "Parsing and validating input arguments", - 2 : "Loading Inference Engine", - 3 : "Read the Intermediate Representation of the network", - 4 : "Resizing network to match image sizes and given batch", - 5 : "Configuring input of the model", - 6 : "Setting device configuration", - 7 : "Loading the model to the device", - 8 : "Setting optimal runtime parameters", - 9 : "Creating infer requests and filling input blobs with images", - 10 : "Measuring performance", - 11 : "Dumping statistics report", - } - - next_step.step_id += 1 - if (next_step.step_id not in step_names.keys()): - raise Exception("Step ID " + str(next_step.step_id) + " is out of total steps number " + len(step_names)) - - print("[Step {}/{}] {}".format(next_step.step_id, len(step_names), step_names[next_step.step_id]) + (" (" + additional_info + ")" if len(additional_info) else "")) - -def main(args=None): - try: - # ------------------------------ 1. Parsing and validating input arguments ------------------------------------- - next_step() - - if not args: - args = parse_args() - - # ------------------------------ 2. Loading Inference Engine --------------------------------------------------- - next_step() - - device_name = args.target_device.upper() - - ie = IECore() - - if CPU_DEVICE_NAME in device_name: - if args.path_to_extension: - ie.add_extension(extension_path=args.path_to_extension, device_name=CPU_DEVICE_NAME) - if GPU_DEVICE_NAME in device_name: - if args.path_to_cldnn_config: - ie.set_config({'CONFIG_FILE' : args.path_to_cldnn_config}, GPU_DEVICE_NAME) - logger.info("GPU extensions is loaded {}".format(args.path_to_cldnn_config)) - - logger.info("InferenceEngine:\n{: <9}{}".format("",get_version())) - version_string = "Device is {}\n".format(device_name) - for device, version in ie.get_versions(device_name).items(): - version_string += "{: <9}{}\n".format("", device) - version_string += "{: <9}{:.<24}{} {}.{}\n".format("",version.description," version", version.major, version.minor) - version_string += "{: <9}{:.<24} {}\n".format("","Build", version.build_number) - logger.info(version_string) - - # --------------------- 3. Read the Intermediate Representation of the network --------------------------------- - next_step() - - xml_filename = os.path.abspath(args.path_to_model) - head, tail = os.path.splitext(xml_filename) - bin_filename = os.path.abspath(head + BIN_EXTENSION) - - ie_network = IENetwork(xml_filename, bin_filename) - - input_info = ie_network.inputs - - if len(input_info) == 0: - raise AttributeError('No inputs info is provided') - - # --------------------- 4. Resizing network to match image sizes and given batch ------------------------------- - next_step() - - batch_size = ie_network.batch_size - precision = ie_network.precision - - if args.batch_size and args.batch_size != ie_network.batch_size: - new_shapes = {} - for key in input_info.keys(): - shape = input_info[key].shape - layout = input_info[key].layout - - batchIndex = -1 - if ((layout == 'NCHW') or (layout == 'NCDHW') or - (layout == 'NHWC') or (layout == 'NDHWC') or - (layout == 'NC')): - batchIndex = 0 - elif (layout == 'CN'): - batchIndex = 1 - - if ((batchIndex != -1) and (shape[batchIndex] != args.batch_size)): - shape[batchIndex] = args.batch_size - new_shapes[key] = shape - - if (len(new_shapes) > 0): - logger.info("Resizing network to batch = {}".format(args.batch_size)) - ie_network.reshape(new_shapes) - - batch_size = args.batch_size - - logger.info("Network batch size: {}, precision {}".format(batch_size, precision)) - - # --------------------- 5. Configuring input of the model ------------------------------------------------------ - next_step() - - for key in input_info.keys(): - if (isImage(input_info[key])): - # Set the precision of input data provided by the user - # Should be called before load of the network to the plugin - input_info[key].precision = 'U8' - - # --------------------- 6. Setting device configuration -------------------------------------------------------- - next_step() - - devices = parseDevices(device_name) - device_nstreams = parseValuePerDevice(devices, args.number_streams) - for device in devices: - if device == CPU_DEVICE_NAME: ## CPU supports few special performance-oriented keys - ## limit threading for CPU portion of inference - if args.number_threads: - ie.set_config({'CPU_THREADS_NUM': str(args.number_threads)}, device) - - # pin threads for CPU portion of inference - ie.set_config({'CPU_BIND_THREAD': args.infer_threads_pinning}, device) - - ## for CPU execution, more throughput-oriented execution via streams - # for pure CPU execution, more throughput-oriented execution via streams - if args.api_type == 'async': - ie.set_config({'CPU_THROUGHPUT_STREAMS': str(device_nstreams.get(device)) - if device in device_nstreams.keys() - else 'CPU_THROUGHPUT_AUTO' }, device) - device_nstreams[device] = int(ie.get_config(device, 'CPU_THROUGHPUT_STREAMS')) - - elif device == GPU_DEVICE_NAME: - if args.api_type == 'async': - ie.set_config({'GPU_THROUGHPUT_STREAMS' : str(device_nstreams.get(device)) - if device in device_nstreams.keys() - else 'GPU_THROUGHPUT_AUTO'}, device) - device_nstreams[device] = int(ie.get_config(device, 'GPU_THROUGHPUT_STREAMS')) - - elif device == MYRIAD_DEVICE_NAME: - ie.set_config({'LOG_LEVEL': 'LOG_INFO', - 'VPU_LOG_LEVEL': 'LOG_WARNING'}, MYRIAD_DEVICE_NAME) - - # --------------------- 7. Loading the model to the device ----------------------------------------------------- - next_step() - - config = { 'PERF_COUNT' : ('YES' if args.perf_counts else 'NO')} - - exe_network = ie.load_network(ie_network, - device_name, - config=config, - num_requests=args.number_infer_requests if args.number_infer_requests else 0) - - # --------------------- 8. Setting optimal runtime parameters -------------------------------------------------- - next_step() - - ## Number of requests - infer_requests = exe_network.requests - nireq = len(infer_requests) - - ## Iteration limit - niter = args.number_iterations - if niter and args.api_type == 'async': - niter = (int)((niter + nireq - 1)/nireq)*nireq - if (args.number_iterations != niter): - logger.warn("Number of iterations was aligned by request number " - "from {} to {} using number of requests {}".format(args.number_iterations, niter, nireq)) - - ## Time limit - duration_seconds = 0 - if args.time: - ## time limit - duration_seconds = args.time - elif not args.number_iterations: - ## default time limit - duration_seconds = get_duration_in_secs(device) - - # ------------------------------------ 8. Creating infer requests and filling input blobs ---------------------- - next_step() - - request_queue = InferRequestsQueue(infer_requests) - - path_to_input = os.path.abspath(args.path_to_input) if args.path_to_input else None - requests_input_data = getInputs(path_to_input, batch_size, ie_network.inputs, infer_requests) - - # ------------------------------------ 9. Measuring performance ------------------------------------------------ - - progress_count = 0 - progress_bar_total_count = 10000 - - output_string = "Start inference {}ronously".format(args.api_type) - if (args.api_type == "async"): - if output_string != "": - output_string += ", " - - output_string += str(nireq) + " inference requests" - device_ss = '' - for device, nstreams in device_nstreams.items(): - if device_ss != '': - device_ss += ', ' - device_ss += "{} streams for {}".format(str(nstreams), device) - if device_ss != '': - output_string += " using " + device_ss - - output_string += ", limits: " - if niter: - if not duration_seconds: - progress_bar_total_count = niter - output_string += str(niter) + " iterations" - - if duration_seconds: - if niter: - output_string += ", " - output_string += str(getDurationInMilliseconds(duration_seconds)) + " ms duration" - - next_step(output_string) - - ## warming up - out of scope - infer_request = request_queue.getIdleRequest() - if not infer_request: - raise Exception("No idle Infer Requests!") - - if (args.api_type == 'sync'): - infer_request.infer(requests_input_data[infer_request.id]) - else: - infer_request.startAsync(requests_input_data[infer_request.id]) - - request_queue.waitAll() - request_queue.resetTimes() - - start_time = datetime.now() - exec_time = (datetime.now() - start_time).total_seconds() - iteration = 0 - - progress_bar = ProgressBar(progress_bar_total_count, args.stream_output, args.progress) - - ## Start inference & calculate performance - ## to align number if iterations to guarantee that last infer requests are executed in the same conditions **/ - while ((niter and iteration < niter) or - (duration_seconds and exec_time < duration_seconds) or - (args.api_type == "async" and iteration % nireq != 0)): - infer_request = request_queue.getIdleRequest() - if not infer_request: - raise Exception("No idle Infer Requests!") - - if (args.api_type == 'sync'): - infer_request.infer(requests_input_data[infer_request.id]) - else: - infer_request.startAsync(requests_input_data[infer_request.id]) - iteration += 1 - - exec_time = (datetime.now() - start_time).total_seconds() - - if niter: - progress_bar.add_progress(1) - else: - ## calculate how many progress intervals are covered by current iteration. - ## depends on the current iteration time and time of each progress interval. - ## Previously covered progress intervals must be skipped. - progress_interval_time = duration_seconds / progress_bar_total_count - new_progress = (int) (exec_time / progress_interval_time - progress_count) - progress_bar.add_progress(new_progress) - progress_count += new_progress - - ## wait the latest inference executions - request_queue.waitAll() - - total_duration_sec = request_queue.getDurationInSeconds() - times = request_queue.times - times.sort() - latency_ms = median(times) - fps = batch_size * 1000 / latency_ms if args.api_type == 'sync' else batch_size * iteration / total_duration_sec - - progress_bar.finish() - - # ------------------------------------ 10. Dumping statistics report ------------------------------------------- - next_step() - - if args.exec_graph_path: - try: - exec_graph_info = exe_network.get_exec_graph_info() - exec_graph_info.serialize(args.exec_graph_path) - logger.info("Executable graph is stored to {}".format(args.exec_graph_path)) - del exec_graph_info - except Exception as e: - logging.exception(e) - - if args.perf_counts: - for ni in range(int(nireq)): - perf_counts = exe_network.requests[ni].get_perf_counts() - logger.info("Pefrormance counts for {}-th infer request".format(ni)) - for layer, stats in perf_counts.items(): - max_layer_name = 30 - print("{:<30}{:<15}{:<30}{:<20}{:<20}{:<20}".format(layer[:max_layer_name - 4] + '...' if (len(layer) >= max_layer_name) else layer, - stats['status'], - 'layerType: ' + str(stats['layer_type']), - 'realTime: ' + str(stats['real_time']), - 'cpu: ' + str(stats['cpu_time']), - 'execType: ' + str(stats['exec_type']))) - - print("Count: {} iterations".format(iteration)) - print("Duration: {:.2f} ms".format(getDurationInMilliseconds(total_duration_sec))) - print("Latency: {:.4f} ms".format(latency_ms)) - print("Throughput: {:.2f} FPS".format(fps)) - - del exe_network - del ie - next_step.step_id = 0 - except Exception as e: - logging.exception(e) diff --git a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/infer_request_wrap.py b/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/infer_request_wrap.py deleted file mode 100644 index cf801fe18a9607..00000000000000 --- a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/infer_request_wrap.py +++ /dev/null @@ -1,81 +0,0 @@ -""" - Copyright (C) 2018-2019 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -""" - -from ctypes import * -from datetime import datetime -import threading - -class InferReqWrap: - def __init__(self, request, id, callbackQueue): - self.id = id - self.request = request - self.request.set_completion_callback(self.callback, self.id) - self.callbackQueue = callbackQueue - - def callback(self, statusCode, userdata): - if (userdata != self.id): - print("Request ID {} does not correspond to user data {}".format(self.id, userdata)) - elif statusCode != 0: - print("Request {} failed with status code {}".format(self.id, statusCode)) - self.callbackQueue(self.id, self.request.latency) - - def startAsync(self, input_data): - self.request.async_infer(input_data) - - def infer(self, input_data): - self.request.infer(input_data) - self.callbackQueue(self.id, self.request.latency); - -class InferRequestsQueue: - def __init__(self, requests): - self.idleIds = [] - self.requests = [] - self.times = [] - for id in range(0, len(requests)): - self.requests.append(InferReqWrap(requests[id], id, self.putIdleRequest)) - self.idleIds.append(id) - self.startTime = datetime.max - self.endTime = datetime.min - self.cv = threading.Condition() - - def resetTimes(self): - self.times.clear() - - def getDurationInSeconds(self): - return (self.endTime - self.startTime).total_seconds() - - def putIdleRequest(self, id, latency): - self.cv.acquire() - self.times.append(latency) - self.idleIds.append(id) - self.endTime = max(self.endTime, datetime.now()) - self.cv.notify() - self.cv.release() - - def getIdleRequest(self): - self.cv.acquire() - while len(self.idleIds) == 0: - self.cv.wait() - id = self.idleIds.pop(); - self.startTime = min(datetime.now(), self.startTime); - self.cv.release() - return self.requests[id] - - def waitAll(self): - self.cv.acquire() - while len(self.idleIds) != len(self.requests): - self.cv.wait() - self.cv.release() diff --git a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/inputs_filling.py b/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/inputs_filling.py deleted file mode 100644 index 00a29452471605..00000000000000 --- a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/inputs_filling.py +++ /dev/null @@ -1,194 +0,0 @@ -""" - Copyright (C) 2018-2019 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -""" - -import logging -import os -import cv2 -import numpy as np -import sys - -from glob import glob -from random import choice - -from .logging import logger - -IMAGE_EXTENSIONS = ['JPEG', 'JPG', 'PNG', 'BMP'] -BINARY_EXTENSIONS = ['BIN'] - -def isImage(blob): - if (blob.layout != "NCHW"): - return False - channels = blob.shape[1] - return (channels == 3) - -def isImageInfo(blob): - if (blob.layout != "NC"): - return False - channels = blob.shape[1] - return (channels >= 2) - -def getInputs(path_to_input, batch_size, input_info, requests): - input_image_sizes = {} - for key in input_info.keys(): - if (isImage(input_info[key])): - input_image_sizes[key] = (input_info[key].shape[2], input_info[key].shape[3]) - logger.info("Network input '{}' precision {}, dimensions ({}): {}".format(key, - input_info[key].precision, - input_info[key].layout, - " ".join(str(x) for x in input_info[key].shape))) - - images_count = len(input_image_sizes.keys()) - binaries_count = len(input_info) - images_count - - image_files = list() - binary_files = list() - - if (path_to_input): - image_files = get_files_by_extensions(path_to_input, IMAGE_EXTENSIONS) - image_files.sort() - binary_files = get_files_by_extensions(path_to_input, BINARY_EXTENSIONS) - binary_files.sort() - - if (len(image_files) == 0) and (len(binary_files) == 0): - logger.warn("No input files were given: all inputs will be filled with random values!") - else: - binary_to_be_used = binaries_count*batch_size*len(requests) - if binary_to_be_used > 0 and len(binary_files) == 0: - logger.warn("No supported binary inputs found! Please check your file extensions: {}".format(",".join(BINARY_EXTENSIONS))) - elif binary_to_be_used > len(binary_files): - logger.warn("Some binary input files will be duplicated: {} files are required, but only {} were provided".format(binary_to_be_used, len(binary_files))) - elif binary_to_be_used < len(binary_files): - logger.warn("Some binary input files will be ignored: only {} files are required from {}".format(binary_to_be_used, len(binary_files))) - - images_to_be_used = images_count*batch_size*len(requests) - if images_to_be_used > 0 and len(image_files) == 0: - logger.warn("No supported image inputs found! Please check your file extensions: {}".format(",".join(IMAGE_EXTENSIONS))) - elif images_to_be_used > len(image_files): - logger.warn("Some image input files will be duplicated: {} files are required, but only {} were provided".format(images_to_be_used, len(image_files))) - elif images_to_be_used < len(image_files): - logger.warn("Some image input files will be ignored: only {} files are required from {}".format(images_to_be_used, len(image_files))) - - requests_input_data = [] - for request_id in range(0, len(requests)): - logger.info("Infer Request {} filling".format(request_id)) - input_data = {} - keys = list(input_info.keys()) - for key in keys: - if isImage(input_info[key]): - # input is image - if (len(image_files) > 0): - input_data[key] = fill_blob_with_image(image_files, request_id, batch_size, keys.index(key), len(keys), input_info[key].shape) - continue - - # input is binary - if (len(binary_files) > 0): - input_data[key] = fill_blob_with_binary(binary_files, input_info[key].shape) - continue - - # most likely input is image info - if isImageInfo(input_info[key]) and len(input_image_sizes) == 1: - image_size = input_image_sizes[list(input_image_sizes.keys()).pop()] - logger.info("Fill input '" + key + "' with image size " + str(image_size[0]) + "x" + - str(image_size[1])) - input_data[key] = fill_blob_with_image_info(image_size, input_info[key].shape) - continue - - # fill with random data - logger.info("Fill input '{}' with random values ({} is expected)".format(key, "image" if isImage(input_info[key]) else "some binary data")) - input_data[key] = fill_blob_with_random(input_info[key].precision, input_info[key].shape) - - requests_input_data.append(input_data) - - return requests_input_data - -def get_files_by_extensions(path_to_input, extensions): - input_files = list() - if os.path.isfile(path_to_input): - input_files.append(path_to_input) - else: - path = os.path.join(path_to_input, '*') - files = glob(path, recursive=True) - for file in files: - file_extension = file.rsplit('.').pop().upper() - if file_extension in extensions: - input_files.append(file) - return input_files - -def fill_blob_with_image(image_paths, request_id, batch_size, input_id, input_size, shape): - images = np.ndarray(shape) - image_index = request_id*batch_size*input_size + input_id - for b in range(batch_size): - image_index %= len(image_paths) - image_filename = image_paths[image_index] - image = cv2.imread(image_filename) - - new_im_size = tuple(shape[2:]) - if image.shape[:-1] != new_im_size: - logger.warn("Image {} is resized from ({}) to ({})".format(image_filename, image.shape[:-1], new_im_size)) - image = cv2.resize(image, new_im_size) - - image = image.transpose((2, 1, 0)) - images[b] = image - - image_index += input_size - return images - -def fill_blob_with_binary(binary_paths, request_id, batch_size, input_id, input_size, shape): - binaries = np.ndarray(shape) - binary_index = request_id*batch_size*input_size + input_id - for b in range(batch_size): - binary_index %= len(image_paths) - binary_filename = binary_paths[binary_index] - - binary_file_size = os.path.getsize(binary_file) - input_size = np.prod(shape)/batch_size - if (input_size != binary_file_size): - raise Exception("File " + binary_filename + " contains " << str(binary_file_size) + " bytes " + - "but network expects " + str(input_size)) - - with open(binary_file, 'r') as f: - binary_data = f.read() - - binaries[b] = binary_data - binary_index += input_size - - return binaries - -def fill_blob_with_image_info(image_size, shape): - im_info = np.ndarray(shape) - for b in range(shape[0]): - for i in range(shape[1]): - im_info[b][i] = image_size[i] if i in [0, 1] else 1 - - return im_info - -def fill_blob_with_random(precision, shape): - if precision == "FP32": - return np.random.rand(*shape).astype(np.float32) - elif precision == "FP16": - return np.random.rand(*shape).astype(np.float16) - elif precision == "I32": - return np.random.rand(*shape).astype(np.int32) - elif precision == "U8": - return np.random.rand(*shape).astype(np.uint8) - elif precision == "I8": - return np.random.rand(*shape).astype(np.int8) - elif precision == "U16": - return np.random.rand(*shape).astype(np.uint16) - elif precision == "I16": - return np.random.rand(*shape).astype(np.int16) - else: - raise Exception("Input precision is not supported: " + precision) diff --git a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/parameters.py b/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/parameters.py deleted file mode 100644 index 3e8b59b848e3be..00000000000000 --- a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/parameters.py +++ /dev/null @@ -1,92 +0,0 @@ -""" - Copyright (C) 2018-2019 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -""" - -import argparse -from fnmatch import fnmatch - -XML_EXTENSION = ".xml" -BIN_EXTENSION = ".bin" - -XML_EXTENSION_PATTERN = '*' + XML_EXTENSION - -def validate_args(args): - if args.number_iterations is not None and args.number_iterations < 0: - raise Exception("Number of iterations should be positive (invalid -niter option value)") - if args.number_infer_requests and args.number_infer_requests < 0: - raise Exception("Number of inference requests should be positive (invalid -nireq option value)") - if not fnmatch(args.path_to_model, XML_EXTENSION_PATTERN): - raise Exception('Path {} is not xml file.') - -def str2bool(v): - if v.lower() in ('yes', 'true', 't', 'y', '1'): - return True - elif v.lower() in ('no', 'false', 'f', 'n', '0'): - return False - else: - raise argparse.ArgumentTypeError('Boolean value expected.') - -def parse_args(): - parser = argparse.ArgumentParser(add_help=False) - args = parser.add_argument_group('Options') - args.add_argument('-h', '--help', action='help', default=argparse.SUPPRESS, - help="Show this help message and exit.") - args.add_argument('-i', '--path_to_input', type=str, required=False, - help="Optional. Path to a folder with images and/or binaries or to specific image or binary file.") - args.add_argument('-m', '--path_to_model', type=str, required=True, - help="Required. Path to an .xml file with a trained model.") - args.add_argument('-d', '--target_device', type=str, required=False, default="CPU", - help="Optional. Specify a target device to infer on: CPU, GPU, FPGA, HDDL or MYRIAD. " - "Use \"-d HETERO:\" format to specify HETERO plugin. ") - args.add_argument('-l', '--path_to_extension', type=str, required=False, default=None, - help="Optional. Required for CPU custom layers. " - "Absolute path to a shared library with the kernels implementations.") - args.add_argument('-c', '--path_to_cldnn_config', type=str, required=False, - help="Optional. Required for GPU custom kernels. Absolute path to an .xml file with the " - "kernels description.") - args.add_argument('-api', '--api_type', type=str, required=False, default='async', choices=['sync', 'async'], - help="Optional. Enable using sync/async API. Default value is async.") - args.add_argument('-niter', '--number_iterations', type=int, required=False, default=None, - help="Optional. Number of iterations. " - "If not specified, the number of iterations is calculated depending on a device.") - args.add_argument('-nireq', '--number_infer_requests', type=int, required=False, default=None, - help="Optional. Number of infer requests. Default value is determined automatically for device.") - args.add_argument('-b', '--batch_size', type=int, required=False, default=None, - help="Optional. Batch size value. If not specified, the batch size value is determined from Intermediate Representation") - args.add_argument('-stream_output', type=str2bool, required=False, default=False, nargs='?', const=True, - help="Optional. Print progress as a plain text. When specified, an interactive progress bar is replaced with a " - "multiline output.") - args.add_argument('-t', '--time', type=int, required=False, default=None, - help="Optional. Time in seconds to execute topology.") - args.add_argument('-progress', type=str2bool, required=False, default=False, nargs='?', const=True, - help="Optional. Show progress bar (can affect performance measurement). Default values is \"False\".") - args.add_argument('-nstreams', '--number_streams', type=str, required=False, default=None, - help="Optional. Number of streams to use for inference on the CPU/GPU in throughput mode " - "(for HETERO device case use format :,: or just ).") - args.add_argument('-nthreads', '--number_threads', type=int, required=False, default=None, - help="Number of threads to use for inference on the CPU " - "(including HETERO case).") - args.add_argument('-pin', '--infer_threads_pinning', type=str, required=False, default='YES', choices=['YES', 'NO'], - help="Optional. Enable (\"YES\" is default value) or disable (\"NO\")" - "CPU threads pinning for CPU-involved inference.") - args.add_argument('--exec_graph_path', type=str, required=False, - help="Optional. Path to a file where to store executable graph information serialized.") - args.add_argument("-pc", "--perf_counts", type=str2bool, required=False, default=False, nargs='?', const=True, - help="Optional. Report performance counters.", ) - parsed_args = parser.parse_args() - - validate_args(parsed_args) - - return parsed_args diff --git a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/utils.py b/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/utils.py deleted file mode 100644 index c1f0afe8230c64..00000000000000 --- a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/utils.py +++ /dev/null @@ -1,99 +0,0 @@ -""" - Copyright (C) 2018-2019 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -""" - -import multiprocessing -from .logging import logger - -VPU_DEVICE_NAME = "VPU" -MYRIAD_DEVICE_NAME = "MYRIAD" -HDDL_DEVICE_NAME = "HDDL" -FPGA_DEVICE_NAME = "FPGA" -CPU_DEVICE_NAME = "CPU" -GPU_DEVICE_NAME = "GPU" -HETERO_DEVICE_NAME = "HETERO" -UNKNOWN_DEVICE_TYPE = "UNKNOWN" - -DEVICE_DURATION_IN_SECS = { - CPU_DEVICE_NAME: 60, - GPU_DEVICE_NAME: 60, - VPU_DEVICE_NAME: 60, - MYRIAD_DEVICE_NAME: 60, - HDDL_DEVICE_NAME: 60, - FPGA_DEVICE_NAME: 120, - UNKNOWN_DEVICE_TYPE: 120 -} - -DEVICE_NIREQ_ASYNC = { - CPU_DEVICE_NAME: 2, - GPU_DEVICE_NAME: 2, - VPU_DEVICE_NAME: 4, - MYRIAD_DEVICE_NAME: 4, - HDDL_DEVICE_NAME: 100, - FPGA_DEVICE_NAME: 3, - UNKNOWN_DEVICE_TYPE: 1 -} - -def get_duration_in_secs(target_device): - duration = 0 - for device in DEVICE_DURATION_IN_SECS: - if device in target_device: - duration = max(duration, DEVICE_DURATION_IN_SECS[device]) - - if duration == 0: - duration = DEVICE_DURATION_IN_SECS[UNKNOWN_DEVICE_TYPE] - logger.warn("Default duration {} seconds is used for unknown device {}".format(duration, target_device)) - - return duration - -def get_nireq(target_device): - nireq = 0 - for device in DEVICE_NIREQ_ASYNC: - if device in target_device: - nireq = max(nireq, DEVICE_NIREQ_ASYNC[device]) - - if nireq == 0: - nireq = DEVICE_NIREQ_ASYNC[UNKNOWN_DEVICE_TYPE] - logger.warn("Default number of requests {} is used for unknown device {}".format(duration, target_device)) - - return nireq - -def parseDevices(device_string): - devices = device_string - if ':' in devices: - devices = devices.partition(':')[2] - return [ d[:d.index('(')] if '(' in d else d for d in devices.split(',') ] - -def parseValuePerDevice(devices, values_string): - ## Format: :,: or just - result = {} - if not values_string: - return result - device_value_strings = values_string.upper().split(',') - for device_value_string in device_value_strings: - device_value_vec = device_value_string.split(':') - if len(device_value_vec) == 2: - for device in devices: - if device == device_value_vec[0]: - value = int(device_value_vec[1]) - result[device_value_vec[0]] = value - break - elif len(device_value_vec) == 1: - value = int(device_value_vec[0]) - for device in devices: - result[device] = value - elif not device_value_vec: - raise Exception("Unknown string format: " + values_string) - return result diff --git a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark_app.py b/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark_app.py deleted file mode 100644 index cf1139a9af2ba2..00000000000000 --- a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark_app.py +++ /dev/null @@ -1,4 +0,0 @@ -import benchmark - -if __name__ == "__main__": - benchmark.main() diff --git a/inference-engine/ie_bridges/python/sample/classification_sample/README.md b/inference-engine/ie_bridges/python/sample/classification_sample/README.md index 98691c7da53312..7812e07a4cfc12 100644 --- a/inference-engine/ie_bridges/python/sample/classification_sample/README.md +++ b/inference-engine/ie_bridges/python/sample/classification_sample/README.md @@ -20,7 +20,7 @@ python3 classification_sample.py -h The command yields the following usage message: ``` usage: classification_sample.py [-h] -m MODEL -i INPUT [INPUT ...] - [-l CPU_EXTENSION] [-pp PLUGIN_DIR] + [-l CPU_EXTENSION] [-d DEVICE] [--labels LABELS] [-nt NUMBER_TOP] Options: @@ -34,8 +34,6 @@ Options: Optional. Required for CPU custom layers. MKLDNN (CPU)-targeted custom layers. Absolute path to a shared library with the kernels implementations. - -pp PLUGIN_DIR, --plugin_dir PLUGIN_DIR - Optional. Path to a plugin folder -d DEVICE, --device DEVICE Optional. Specify the target device to infer on; CPU, GPU, FPGA, HDDL or MYRIAD is acceptable. The sample diff --git a/inference-engine/ie_bridges/python/sample/classification_sample_async/README.md b/inference-engine/ie_bridges/python/sample/classification_sample_async/README.md index b409f5d1d81110..18ada6fecabc20 100644 --- a/inference-engine/ie_bridges/python/sample/classification_sample_async/README.md +++ b/inference-engine/ie_bridges/python/sample/classification_sample_async/README.md @@ -32,7 +32,7 @@ python3 classification_sample_async.py -h The command yields the following usage message: ``` usage: classification_sample_async.py [-h] -m MODEL -i INPUT [INPUT ...] - [-l CPU_EXTENSION] [-pp PLUGIN_DIR] + [-l CPU_EXTENSION] [-d DEVICE] [--labels LABELS] [-nt NUMBER_TOP] @@ -47,8 +47,6 @@ Options: Optional. Required for CPU custom layers. Absolute path to a shared library with the kernels implementations. - -pp PLUGIN_DIR, --plugin_dir PLUGIN_DIR - Optional. Path to a plugin folder -d DEVICE, --device DEVICE Optional. Specify the target device to infer on; CPU, GPU, FPGA, HDDL or MYRIAD is acceptable. The sample @@ -68,7 +66,7 @@ To run the sample, you can use AlexNet and GoogLeNet or other image classificati You can do inference of an image using a trained AlexNet network on FPGA with fallback to CPU using the following command: ``` - python3 classification_sample_async.py -i /cat.bmp -m /alexnet_fp32.xml -nt 5 -d HETERO:FPGA,CPU -nireq 2 -ni 200 + python3 classification_sample_async.py -i /cat.bmp -m /alexnet_fp32.xml -nt 5 -d HETERO:FPGA,CPU ``` ## Sample Output diff --git a/inference-engine/ie_bridges/python/sample/hello_query_device/hello_query_device.py b/inference-engine/ie_bridges/python/sample/hello_query_device/hello_query_device.py index 9d78438eebd967..c0178e92ad2453 100644 --- a/inference-engine/ie_bridges/python/sample/hello_query_device/hello_query_device.py +++ b/inference-engine/ie_bridges/python/sample/hello_query_device/hello_query_device.py @@ -22,14 +22,19 @@ def main(): print("\tDevice: {}".format(device)) print("\tMetrics:") for metric in ie.get_metric(device, "SUPPORTED_METRICS"): - metric_val = ie.get_metric(device, metric) - print("\t\t{}: {}".format(metric, param_to_string(metric_val))) + try: + metric_val = ie.get_metric(device, metric) + print("\t\t{}: {}".format(metric, param_to_string(metric_val))) + except TypeError: + print("\t\t{}: UNSUPPORTED TYPE".format(metric)) print("\n\tDefault values for device configuration keys:") for cfg in ie.get_metric(device, "SUPPORTED_CONFIG_KEYS"): - cfg_val = ie.get_config(device, cfg) - print("\t\t{}: {}".format(cfg, param_to_string(cfg_val))) - + try: + cfg_val = ie.get_config(device, cfg) + print("\t\t{}: {}".format(cfg, param_to_string(cfg_val))) + except TypeError: + print("\t\t{}: UNSUPPORTED TYPE".format(cfg)) if __name__ == '__main__': sys.exit(main() or 0) diff --git a/inference-engine/ie_bridges/python/sample/object_detection_sample_ssd/README.md b/inference-engine/ie_bridges/python/sample/object_detection_sample_ssd/README.md new file mode 100644 index 00000000000000..7b9182538b5dcb --- /dev/null +++ b/inference-engine/ie_bridges/python/sample/object_detection_sample_ssd/README.md @@ -0,0 +1,73 @@ +# Object Detection Python* Sample SSD + +This sample demonstrates how to run the Object Detection sample application. + +The sample demonstrates how to use the new Infer Request API of Inference Engine in applications. +Refer to [Integrate the Inference Engine New Request API with Your Application](./docs/IE_DG/Integrate_with_customer_application_new_API.md) for details. +The sample demonstrates how to build and execute an inference request on example of object detection networks. + +Due to properties of SSD networks, this sample works correctly only on a batch of the size 1. For a greater number of images in a batch, network reshape is required. + +## How It Works + +Upon the start-up, the sample application reads command line parameters and loads specified network and input images (or a +folder with images) to the Inference Engine plugin. + +Then, the sample creates an inference request object and executes inference on it. + +When inference is done, the application outputs data to the standard output stream and creates an output image with bounding boxes drawn atop the initial image. + +> **NOTE**: By default, Inference Engine samples and demos expect input with BGR channels order. If you trained your model to work with RGB order, you need to manually rearrange the default channels order in the sample or demo application or reconvert your model using the Model Optimizer tool with `--reverse_input_channels` argument specified. For more information about the argument, refer to **When to Reverse Input Channels** section of [Converting a Model Using General Conversion Parameters](./docs/MO_DG/prepare_model/convert_model/Converting_Model_General.md). + +## Running + +Running the application with the -h option yields the following usage message: +``` +python3 object_detection_sample_ssd.py -h +``` +The command yields the following usage message: +``` +usage: object_detection_sample_ssd.py [-h] -m MODEL -i INPUT [INPUT ...] + [-l CPU_EXTENSION] + [-d DEVICE] [--labels LABELS] + [-nt NUMBER_TOP] + +Options: + -h, --help Show this help message and exit + -m MODEL, --model MODEL + Required. Path to an .xml file with a trained model + -i INPUT [INPUT ...], --input INPUT [INPUT ...] + Required. Path to a folder with images or path to an + image files + -l CPU_EXTENSION, --cpu_extension CPU_EXTENSION + Optional. Required for CPU custom layers. Absolute + path to a shared library with the kernels + implementations + -d DEVICE, --device DEVICE + Optional. Specify the target device to infer on; CPU, + GPU, FPGA, HDDL or MYRIAD is acceptable. The sample + will look for a suitable plugin for device specified + Default value is CPU + --labels LABELS Optional. Labels mapping file + -nt NUMBER_TOP, --number_top NUMBER_TOP + Optional. Number of top results +``` + +Running the application with the empty list of options yields the usage message given above and an error message. + +To run the sample, you can use RMNet_SSD or other object-detection models. You can download the pre-trained models with the OpenVINO [Model Downloader](https://github.com/opencv/open_model_zoo/tree/2018/model_downloader) or from [https://download.01.org/opencv/](https://download.01.org/opencv/). + +> **NOTE**: Before running the sample with a trained model, make sure the model is converted to the Inference Engine format (\*.xml + \*.bin) using the [Model Optimizer tool](./docs/MO_DG/Deep_Learning_Model_Optimizer_DevGuide.md). + + +You can do inference of an image using a trained RMNet_SSD network on FPGA with fallback to CPU using the following command: +``` + python3 object_detection_sample_ssd.py -i /cat.bmp -m /alexnet_fp32.xml -nt 5 -d HETERO:FPGA,CPU +``` + +## Sample Output + +By default, the application outputs all inference results and draws bounding boxes for inference results with an over 50% confidence. + +## See Also +* [Using Inference Engine Samples](./docs/IE_DG/Samples_Overview.md) diff --git a/inference-engine/ie_bridges/python/sample/object_detection_sample_ssd/object_detection_sample_ssd.py b/inference-engine/ie_bridges/python/sample/object_detection_sample_ssd/object_detection_sample_ssd.py new file mode 100644 index 00000000000000..20274696e55c6b --- /dev/null +++ b/inference-engine/ie_bridges/python/sample/object_detection_sample_ssd/object_detection_sample_ssd.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python +""" + Copyright (c) 2018 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from __future__ import print_function +import sys +import os +from argparse import ArgumentParser, SUPPRESS +import cv2 +import numpy as np +import logging as log +from time import time +from openvino.inference_engine import IENetwork, IECore + + +def build_argparser(): + parser = ArgumentParser(add_help=False) + args = parser.add_argument_group("Options") + args.add_argument('-h', '--help', action='help', default=SUPPRESS, help='Show this help message and exit.') + args.add_argument("-m", "--model", help="Required. Path to an .xml file with a trained model.", + required=True, type=str) + args.add_argument("-i", "--input", help="Required. Path to image file.", + required=True, type=str, nargs="+") + args.add_argument("-l", "--cpu_extension", + help="Optional. Required for CPU custom layers. Absolute path to a shared library with the kernels implementations.", + type=str, default=None) + args.add_argument("-d", "--device", + help="Optional. Specify the target device to infer on; CPU, GPU, FPGA or MYRIAD is acceptable. Sample will look for a suitable plugin for device specified (CPU by default)", + default="CPU", type=str) + args.add_argument("--labels", help="Optional. Labels mapping file", default=None, type=str) + args.add_argument("-nt", "--number_top", help="Optional. Number of top results", default=10, type=int) + + return parser + + +def main(): + log.basicConfig(format="[ %(levelname)s ] %(message)s", level=log.INFO, stream=sys.stdout) + args = build_argparser().parse_args() + # --------------------------- 1. Read IR Generated by ModelOptimizer (.xml and .bin files) ------------ + model_xml = args.model + model_bin = os.path.splitext(model_xml)[0] + ".bin" + log.info("Loading network files:\n\t{}\n\t{}".format(model_xml, model_bin)) + net = IENetwork(model=model_xml, weights=model_bin) + # ----------------------------------------------------------------------------------------------------- + + # ------------- 2. Load Plugin for inference engine and extensions library if specified -------------- + log.info("Loading Inference Engine") + ie = IECore() + log.info("Device info:") + versions = ie.get_versions(args.device) + print("{}{}".format(" "*8, args.device)) + print("{}MKLDNNPlugin version ......... {}.{}".format(" "*8, versions[args.device].major, versions[args.device].minor)) + print("{}Build ........... {}".format(" "*8, versions[args.device].build_number)) + + if args.cpu_extension and "CPU" in args.device: + ie.add_extension(args.cpu_extension, "CPU") + log.info("CPU extension loaded: {}".format(args.cpu_extension)) + + if "CPU" in args.device: + supported_layers = ie.query_network(net, "CPU") + not_supported_layers = [l for l in net.layers.keys() if l not in supported_layers] + if len(not_supported_layers) != 0: + log.error("Following layers are not supported by the plugin for specified device {}:\n {}". + format(args.device, ', '.join(not_supported_layers))) + log.error("Please try to specify cpu extensions library path in sample's command line parameters using -l " + "or --cpu_extension command line argument") + sys.exit(1) + # ----------------------------------------------------------------------------------------------------- + + # --------------------------- 3. Read and preprocess input -------------------------------------------- + input_blob = next(iter(net.inputs)) + n, c, h, w = net.inputs[input_blob].shape + images = np.ndarray(shape=(n, c, h, w)) + images_hw = [] + for i in range(n): + image = cv2.imread(args.input[i]) + ih, iw = image.shape[:-1] + images_hw.append((ih, iw)) + log.info("File was added: ") + log.info(" {}".format(args.input[i])) + if (ih, iw) != (h, w): + image = cv2.resize(image, (w, h)) + log.warning("Image {} is resized from {} to {}".format(args.input[i], image.shape[:-1], (h, w))) + image = image.transpose((2, 0, 1)) # Change data layout from HWC to CHW + images[i] = image + # ----------------------------------------------------------------------------------------------------- + + # --------------------------- 4. Configure input & output --------------------------------------------- + # --------------------------- Prepare input blobs ----------------------------------------------------- + log.info("Preparing input blobs") + assert (len(net.inputs.keys()) == 1 or len(net.inputs.keys()) == 2), "Sample supports topologies only with 1 or 2 inputs" + input_blob = next(iter(net.inputs)) + out_blob = next(iter(net.outputs)) + input_name, input_info_name = "", "" + + for input_key in net.inputs: + if len(net.inputs[input_key].layout) == 4: + input_name = input_key + log.info("Batch size is {}".format(net.batch_size)) + net.inputs[input_key].precision = 'U8' + elif len(net.inputs[input_key].layout) == 2: + input_info_name = input_key + net.inputs[input_key].precision = 'FP32' + if net.inputs[input_key].shape[1] != 3 and net.inputs[input_key].shape[1] != 6 or net.inputs[input_key].shape[0] != 1: + log.error('Invalid input info. Should be 3 or 6 values length.') + + # --------------------------- Prepare output blobs ---------------------------------------------------- + log.info('Preparing output blobs') + + output_name, output_info = "", net.outputs[next(iter(net.outputs.keys()))] + for output_key in net.outputs: + if net.layers[output_key].type == "DetectionOutput": + output_name, output_info = output_key, net.outputs[output_key] + + if output_name == "": + log.error("Can't find a DetectionOutput layer in the topology") + + output_dims = output_info.shape + if len(output_dims) != 4: + log.error("Incorrect output dimensions for SSD model") + max_proposal_count, object_size = output_dims[2], output_dims[3] + + if object_size != 7: + log.error("Output item should have 7 as a last dimension") + + output_info.precision = "FP32" + # ----------------------------------------------------------------------------------------------------- + + # --------------------------- Performing inference ---------------------------------------------------- + log.info("Loading model to the device") + exec_net = ie.load_network(network=net, device_name=args.device) + log.info("Creating infer request and starting inference") + res = exec_net.infer(inputs={input_blob: images}) + # ----------------------------------------------------------------------------------------------------- + + # --------------------------- Read and postprocess output --------------------------------------------- + log.info("Processing output blobs") + res = res[out_blob] + boxes, classes = {}, {} + data = res[0][0] + for number, proposal in enumerate(data): + if proposal[2] > 0: + imid = np.int(proposal[0]) + ih, iw = images_hw[imid] + label = np.int(proposal[1]) + confidence = proposal[2] + xmin = np.int(iw * proposal[3]) + ymin = np.int(ih * proposal[4]) + xmax = np.int(iw * proposal[5]) + ymax = np.int(ih * proposal[6]) + print("[{},{}] element, prob = {:.6} ({},{})-({},{}) batch id : {}"\ + .format(number, label, confidence, xmin, ymin, xmax, ymax, imid), end="") + if proposal[2] > 0.5: + print(" WILL BE PRINTED!") + if not imid in boxes.keys(): + boxes[imid] = [] + boxes[imid].append([xmin, ymin, xmax, ymax]) + if not imid in classes.keys(): + classes[imid] = [] + classes[imid].append(label) + else: + print() + + for imid in classes: + tmp_image = cv2.imread(args.input[imid]) + for box in boxes[imid]: + cv2.rectangle(tmp_image, (box[0], box[1]), (box[2], box[3]), (232, 35, 244), 2) + cv2.imwrite("out.bmp", tmp_image) + log.info("Image out.bmp created!") + # ----------------------------------------------------------------------------------------------------- + + log.info("Execution successful\n") + log.info("This sample is an API example, for any performance measurements please use the dedicated benchmark_app tool") + + +if __name__ == '__main__': + sys.exit(main() or 0) \ No newline at end of file diff --git a/inference-engine/ie_bridges/python/sample/style_transfer_sample/README.md b/inference-engine/ie_bridges/python/sample/style_transfer_sample/README.md index 272432b402e81f..9671006f7a9d01 100644 --- a/inference-engine/ie_bridges/python/sample/style_transfer_sample/README.md +++ b/inference-engine/ie_bridges/python/sample/style_transfer_sample/README.md @@ -19,7 +19,7 @@ The command yields the following usage message: ``` usage: style_transfer_sample.py [-h] -m MODEL -i INPUT [INPUT ...] [-l CPU_EXTENSION] [-d DEVICE] - [-nt NUMBER_TOP] [-ni NUMBER_ITER] + [-nt NUMBER_TOP] [--mean_val_r MEAN_VAL_R] [--mean_val_g MEAN_VAL_G] [--mean_val_b MEAN_VAL_B] diff --git a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api.pyx b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api.pyx index d79a32a4080de5..12fd3294ed1e81 100644 --- a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api.pyx +++ b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api.pyx @@ -44,7 +44,7 @@ cdef c_map_to_dict(map[string, string] c_map): supported_precisions = ["FP32", "FP16", "Q78", "I32", "I16", "I8", "U32", "U16", "U8"] supported_layouts = ["NCHW", "NHWC", "OIHW", "C", "CHW", "HW", "NC", "CN", "BLOCKED", "NCDHW"] -known_plugins = ['CPU', 'GPU', 'FPGA', 'MYRIAD', 'HETERO', 'HDDL'] +known_plugins = ['CPU', 'GPU', 'FPGA', 'MYRIAD', 'HETERO', 'HDDL', 'MULTI'] ctypedef enum StatusCode: OK = 0 @@ -336,7 +336,7 @@ cdef class InferRequest: # TODO: add execution index. Check if unsigned int is properly converted to int in python. profile[l.first.decode()] = {"status": info.status.decode(), "exec_type": info.exec_type.decode(), "layer_type": info.layer_type.decode(), "real_time": info.real_time, - "cpu_time": info.cpu_time} + "cpu_time": info.cpu_time, "execution_index": info.execution_index} return profile @property @@ -493,18 +493,14 @@ cdef class IENetwork: cdef IENetwork net = IENetwork(model, weights) return net - # TODO: Use enum with precision type instead of srting parameter when python2 support will not be required. - def add_outputs(self, outputs, precision="FP32"): - if precision.upper() not in supported_precisions: - raise AttributeError( - "Unsupported precision {}! List of supported precisions: {}".format(precision, supported_precisions)) + def add_outputs(self, outputs): if not isinstance(outputs, list): outputs = [outputs] for i, l in enumerate(outputs): if isinstance(l, str): - self.impl.addOutput(l.encode(), 0, precision.upper().encode()) + self.impl.addOutput(l.encode(), 0) elif isinstance(l, tuple) and len(l) == 2: - self.impl.addOutput(l[0].encode(), l[1], precision.upper().encode()) + self.impl.addOutput(l[0].encode(), l[1]) else: raise TypeError("Incorrect type {type} for layer to add at index {ind}. " "Expected string with layer name or tuple with two elements: layer name as " diff --git a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl.cpp b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl.cpp index 371ffcfdbba121..c9a1ad3192c987 100644 --- a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl.cpp +++ b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl.cpp @@ -68,6 +68,11 @@ PyObject* parse_parameter(const InferenceEngine::Parameter & param){ else if (param.is()) { auto val = param.as(); return PyLong_FromLong((long)val); + } + // Check for unsinged int + else if (param.is()) { + auto val = param.as(); + return PyLong_FromLong((unsigned long)val); } // Check for float else if (param.is()) { @@ -97,6 +102,15 @@ PyObject* parse_parameter(const InferenceEngine::Parameter & param){ PyList_Append(list, PyLong_FromLong(it)); } return list; + } + // Check for std::vector + else if (param.is>()){ + auto val = param.as>(); + PyObject *list = PyList_New(0); + for (const auto & it : val){ + PyList_Append(list, PyLong_FromLong(it)); + } + return list; } // Check for std::vector else if (param.is>()){ @@ -243,7 +257,7 @@ const std::map InferenceEnginePyt const InferenceEngine::InputsDataMap &inputsInfo = actual.getInputsInfo(); for (auto &in : inputsInfo) { InferenceEnginePython::InputInfo info; - info.actual = *in.second; + info.actual = in.second; const InferenceEngine::TensorDesc &inputTensorDesc = in.second->getTensorDesc(); info.dims = inputTensorDesc.getDims(); for (auto it : precision_map) @@ -277,16 +291,8 @@ const std::map InferenceEnginePy } void -InferenceEnginePython::IENetwork::addOutput(const std::string &out_layer, size_t port_id, const std::string &precision) { +InferenceEnginePython::IENetwork::addOutput(const std::string &out_layer, size_t port_id) { actual.addOutput(out_layer, port_id); - InferenceEngine::OutputsDataMap outputsDataMapUpd = actual.getOutputsInfo(); - if (outputsDataMapUpd.count(out_layer)) { - outputsDataMapUpd[out_layer]->setPrecision(precision_map[precision]); - } else if (outputsDataMapUpd.count(out_layer + "." + std::to_string(port_id))){ - outputsDataMapUpd[out_layer + "." + std::to_string(port_id)]->setPrecision(precision_map[precision]); - } else { - THROW_IE_EXCEPTION << "Failed to set precision for layer " << out_layer; - } } void InferenceEnginePython::IENetwork::setBatch(const size_t size) { @@ -329,11 +335,11 @@ void InferenceEnginePython::IENetwork::setStats(const std::mapsetPrecision(precision_map[precision]); } void InferenceEnginePython::InputInfo::setLayout(std::string layout) { - actual.setLayout(layout_map[layout]); + actual->setLayout(layout_map[layout]); } void InferenceEnginePython::OutputInfo::setPrecision(std::string precision) { @@ -567,6 +573,7 @@ InferenceEnginePython::InferRequestWrap::getPerformanceCounts() { profile_info.layer_type = it.second.layer_type; profile_info.cpu_time = it.second.cpu_uSec; profile_info.real_time = it.second.realTime_uSec; + profile_info.execution_index = it.second.execution_index; perf_map[it.first] = profile_info; } return perf_map; diff --git a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl.hpp b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl.hpp index 59e632080776df..08cb8cb7230725 100644 --- a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl.hpp +++ b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl.hpp @@ -49,7 +49,7 @@ struct IENetLayer { }; struct InputInfo { - InferenceEngine::InputInfo actual; + InferenceEngine::InputInfo::Ptr actual; std::vector dims; std::string precision; std::string layout; @@ -85,7 +85,7 @@ struct IENetwork { void setBatch(const size_t size); - void addOutput(const std::string &out_layer, size_t port_id, const std::string &precision); + void addOutput(const std::string &out_layer, size_t port_id); const std::vector> getLayers(); diff --git a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl_defs.pxd b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl_defs.pxd index 95db6d98cc946f..e838a26a2088d6 100644 --- a/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl_defs.pxd +++ b/inference-engine/ie_bridges/python/src/openvino/inference_engine/ie_api_impl_defs.pxd @@ -91,7 +91,7 @@ cdef extern from "ie_api_impl.hpp" namespace "InferenceEnginePython": const vector[pair[string, IENetLayer]] getLayers() except + map[string, InputInfo] getInputs() except + map[string, OutputInfo] getOutputs() except + - void addOutput(string &, size_t, string &) except + + void addOutput(string &, size_t) except + void setAffinity(map[string, string] & types_affinity_map, map[string, string] & layers_affinity_map) except + void setBatch(size_t size) except + void setLayerParams(map[string, map[string, string]] params_map) except + diff --git a/inference-engine/ie_bridges/python/src/openvino/tools/statistics_collector/CMakeLists.txt b/inference-engine/ie_bridges/python/src/openvino/tools/statistics_collector/CMakeLists.txt index a649bd2655fc1d..8e4871d17b8720 100644 --- a/inference-engine/ie_bridges/python/src/openvino/tools/statistics_collector/CMakeLists.txt +++ b/inference-engine/ie_bridges/python/src/openvino/tools/statistics_collector/CMakeLists.txt @@ -23,7 +23,13 @@ endif() cython_add_module (${TARGET_NAME} ${SOURCE}) set_target_properties (${TARGET_NAME} PROPERTIES CXX_STANDARD 11 LINKER_LANGUAGE CXX) -target_link_libraries (${TARGET_NAME} PRIVATE statistics_collector_s) +target_link_libraries (${TARGET_NAME} PRIVATE ${InferenceEngine_LIBRARIES}) + +if(TARGET IE::statistics_collector_s) + target_link_libraries(${TARGET_NAME} PRIVATE IE::statistics_collector_s) +else() + target_link_libraries(${TARGET_NAME} PRIVATE statistics_collector_s) +endif() # perform copy ADD_CUSTOM_COMMAND (TARGET ${TARGET_NAME} diff --git a/inference-engine/include/builders/ie_concat_layer.hpp b/inference-engine/include/builders/ie_concat_layer.hpp index 57464672d4737a..0df940f622d970 100644 --- a/inference-engine/include/builders/ie_concat_layer.hpp +++ b/inference-engine/include/builders/ie_concat_layer.hpp @@ -72,9 +72,6 @@ class INFERENCE_ENGINE_API_CLASS(ConcatLayer): public LayerDecorator { * @return reference to layer builder */ ConcatLayer& setAxis(size_t axis); - -private: - size_t axis = 1; }; } // namespace Builder diff --git a/inference-engine/include/cpp/ie_cnn_network.h b/inference-engine/include/cpp/ie_cnn_network.h index 9f4f5bb05e5a6a..cef99ef7c7a1a6 100644 --- a/inference-engine/include/cpp/ie_cnn_network.h +++ b/inference-engine/include/cpp/ie_cnn_network.h @@ -89,7 +89,7 @@ class CNNNetwork { virtual OutputsDataMap getOutputsInfo() const { OutputsDataMap outputs; actual->getOutputsInfo(outputs); - return std::move(outputs); + return outputs; } /** @@ -99,7 +99,7 @@ class CNNNetwork { virtual InputsDataMap getInputsInfo() const { InputsDataMap inputs; actual->getInputsInfo(inputs); - return std::move(inputs); + return inputs; } /** @@ -223,7 +223,7 @@ class CNNNetwork { } } } - return std::move(shapes); + return shapes; } /** diff --git a/inference-engine/include/cpp/ie_executable_network.hpp b/inference-engine/include/cpp/ie_executable_network.hpp index 2eb235af55748e..6a3e716c4cb7ca 100644 --- a/inference-engine/include/cpp/ie_executable_network.hpp +++ b/inference-engine/include/cpp/ie_executable_network.hpp @@ -30,17 +30,31 @@ class ExecutableNetwork { InferenceEnginePluginPtr plg; public: + /** + * @brief Default constructor + */ ExecutableNetwork() = default; + + /** + * @brief Destructor + */ ~ExecutableNetwork() { actual = nullptr; } + /** + * @brief Constructs ExecutableNetwork from the initialized shared_pointer + * @param actual Initialized shared pointer + * @param plg Plugin to use + */ explicit ExecutableNetwork(IExecutableNetwork::Ptr actual, InferenceEnginePluginPtr plg = {}) : actual(actual), plg(plg) {} /** - * @brief Wraps original method - * IExecutableNetwork::getOutputsInfo + * @copybrief IExecutableNetwork::GetOutputsInfo + * + * Wraps IExecutableNetwork::GetOutputsInfo. + * @return A collection that contains string as key, and const Data smart pointer as value */ ConstOutputsDataMap GetOutputsInfo() const { ConstOutputsDataMap data; @@ -49,8 +63,10 @@ class ExecutableNetwork { } /** - * @brief Wraps original method - * IExecutableNetwork::getInputsInfo + * @copybrief IExecutableNetwork::GetInputsInfo + * + * Wraps IExecutableNetwork::GetInputsInfo + * @return A collection that contains string as key, and const InputInfo smart pointer as value */ ConstInputsDataMap GetInputsInfo() const { ConstInputsDataMap info; @@ -59,16 +75,20 @@ class ExecutableNetwork { } /** - * @brief reset owned object to new pointer, essential for cases when simultaneously loaded networks not expected - * @param actual actual pointed object + * @brief reset owned object to new pointer. + * + * Eessential for cases when simultaneously loaded networks not expected. + * @param newActual actual pointed object */ void reset(IExecutableNetwork::Ptr newActual) { this->actual.swap(newActual); } /** - * @brief Wraps original method - * IExecutableNetwork::CreateInferRequest + * @copybrief IExecutableNetwork::CreateInferRequest + * + * Wraps IExecutableNetwork::CreateInferRequest. + * @return InferRequest object */ InferRequest CreateInferRequest() { IInferRequest::Ptr req; @@ -78,9 +98,10 @@ class ExecutableNetwork { } /** - * @brief Wraps original method - * IExecutableNetwork::CreateInferRequestPtr - * @return shared pointer on InferRequest object + * @copybrief IExecutableNetwork::CreateInferRequest + * + * Wraps IExecutableNetwork::CreateInferRequest. + * @return shared pointer on InferenceEngine::InferRequest object */ InferRequest::Ptr CreateInferRequestPtr() { IInferRequest::Ptr req; @@ -89,18 +110,24 @@ class ExecutableNetwork { } /** - * @brief Exports the current executable network so it can be used later in the Import() main API + * @copybrief IExecutableNetwork::Export + * + * Wraps IExecutableNetwork::Export. + * + * @see Core::ImportNetwork + * @see InferencePlugin::ImportNetwork + * * @param modelFileName Full path to the location of the exported file - * @param resp Optional: pointer to an already allocated object to contain information in case of failure */ void Export(const std::string &modelFileName) { CALL_STATUS_FNC(Export, modelFileName); } /** - * @brief Gets the mapping of IR layer names to implemented kernels + * @copybrief IExecutableNetwork::GetMappedTopology + * + * Wraps IExecutableNetwork::GetMappedTopology. * @param deployedTopology Map of PrimitiveInfo objects that represent the deployed topology - * @param resp Optional: pointer to an already allocated object to contain information in case of failure */ void GetMappedTopology(std::map> &deployedTopology) { CALL_STATUS_FNC(GetMappedTopology, deployedTopology); @@ -115,7 +142,9 @@ class ExecutableNetwork { } /** - * @brief Get executable graph information from a plugin represented as CNNNetwork + * @copybrief IExecutableNetwork::GetExecGraphInfo + * + * Wraps IExecutableNetwork::GetExecGraphInfo. * @return CNNetwork containing Executable Graph Info */ CNNNetwork GetExecGraphInfo() { @@ -125,7 +154,10 @@ class ExecutableNetwork { } /** - *@brief see original function InferenceEngine::IExecutableNetwork::QueryState + * @copybrief IExecutableNetwork::QueryState + * + * Wraps IExecutableNetwork::QueryState + * @return A vector of Memory State objects */ std::vector QueryState() { IMemoryState::Ptr pState = nullptr; @@ -146,20 +178,21 @@ class ExecutableNetwork { } /** - * @brief Sets configuration for current executable network + * @copybrief IExecutableNetwork::SetConfig + * + * Wraps IExecutableNetwork::SetConfig. * @param config Map of pairs: (config parameter name, config parameter value) - * @param resp Pointer to the response message that holds a description of an error if any occurred */ void SetConfig(const std::map &config) { CALL_STATUS_FNC(SetConfig, config); } - /** @brief Gets configuration dedicated to plugin behaviour - * @param name - config key, can be found in ie_plugin_config.hpp - * @param options - configuration details for coonfig value - * @param result - value of config corresponding to config key - * @param resp Pointer to the response message that holds a description of an error if any occurred - */ + /** @copybrief IExecutableNetwork::GetConfig + * + * Wraps IExecutableNetwork::GetConfig + * @param name - config key, can be found in ie_plugin_config.hpp + * @return Configuration paramater value + */ Parameter GetConfig(const std::string &name) const { Parameter configValue; CALL_STATUS_FNC(GetConfig, name, configValue); @@ -167,13 +200,11 @@ class ExecutableNetwork { } /** - * @brief Gets general runtime metric for dedicated hardware + * @copybrief IExecutableNetwork::GetMetric + * + * Wraps IExecutableNetwork::GetMetric * @param name - metric name to request - * @param options - configuration details for metric - * @param result - metric value corresponding to metric key - * @param resp - Pointer to the response message that holds a description of an error if any - * occurred - * @return code of the operation. OK if succeeded + * @return Metric paramater value */ Parameter GetMetric(const std::string &name) const { Parameter metricValue; @@ -181,6 +212,9 @@ class ExecutableNetwork { return metricValue; } + /** + * @brief A smart pointer to the ExecutableNetwork object + */ using Ptr = std::shared_ptr; }; diff --git a/inference-engine/include/cpp/ie_infer_request.hpp b/inference-engine/include/cpp/ie_infer_request.hpp index 5d1eeb4838fff0..9e42226e8128c5 100644 --- a/inference-engine/include/cpp/ie_infer_request.hpp +++ b/inference-engine/include/cpp/ie_infer_request.hpp @@ -69,8 +69,14 @@ class InferRequest { } public: + /** + * @brief Default constructor + */ InferRequest() = default; + /** + * @brief Destructor + */ ~InferRequest() { actual = nullptr; } @@ -150,8 +156,9 @@ class InferRequest { } /** - * constructs InferRequest from initialised shared_pointer - * @param actual + * constructs InferRequest from the initialized shared_pointer + * @param request Initialized shared pointer + * @param plg Plugin to use */ explicit InferRequest(IInferRequest::Ptr request, InferenceEnginePluginPtr plg = {}) : actual(request), plg(plg) {} @@ -192,14 +199,25 @@ class InferRequest { return actual; } + /** + * @brief Checks if current InferRequest object is not initialized + * @return true if current InferRequest object is not initialized, false - otherwise + */ bool operator!() const noexcept { return !actual; } + /** + * @brief Checks if current InferRequest object is initialized + * @return true if current InferRequest object is initialized, false - otherwise + */ explicit operator bool() const noexcept { return !!actual; } + /** + * @brief A smart pointer to the InferRequest object + */ using Ptr = std::shared_ptr; }; diff --git a/inference-engine/include/cpp/ie_memory_state.hpp b/inference-engine/include/cpp/ie_memory_state.hpp index d20fcae91bc73d..d1867c83843e6a 100644 --- a/inference-engine/include/cpp/ie_memory_state.hpp +++ b/inference-engine/include/cpp/ie_memory_state.hpp @@ -14,40 +14,47 @@ class MemoryState { IMemoryState::Ptr actual = nullptr; public: + /** + * constructs MemoryState from the initialized shared_pointer + * @param pState Initialized shared pointer + */ explicit MemoryState(IMemoryState::Ptr pState) : actual(pState) {} /** * @brief Wraps original method * IMemoryState::Reset */ - void Reset() { + void Reset() { CALL_STATUS_FNC_NO_ARGS(Reset); - } + } + /** * @brief Wraps original method * IMemoryState::GetName */ - std::string GetName() const { - char name[256]; - CALL_STATUS_FNC(GetName, name, sizeof(name)); - return name; - } + std::string GetName() const { + char name[256]; + CALL_STATUS_FNC(GetName, name, sizeof(name)); + return name; + } + /** * @brief Wraps original method * IMemoryState::GetLastState */ - Blob::CPtr GetLastState() const { - Blob::CPtr stateBlob; - CALL_STATUS_FNC(GetLastState, stateBlob); - return stateBlob; - } + Blob::CPtr GetLastState() const { + Blob::CPtr stateBlob; + CALL_STATUS_FNC(GetLastState, stateBlob); + return stateBlob; + } + /** * @brief Wraps original method * IMemoryState::SetState */ - void SetState(Blob::Ptr state) { - CALL_STATUS_FNC(SetState, state); - } + void SetState(Blob::Ptr state) { + CALL_STATUS_FNC(SetState, state); + } }; } // namespace InferenceEngine \ No newline at end of file diff --git a/inference-engine/include/cpp/ie_plugin_cpp.hpp b/inference-engine/include/cpp/ie_plugin_cpp.hpp index 8d5744a016f9f4..093b16cda823f3 100644 --- a/inference-engine/include/cpp/ie_plugin_cpp.hpp +++ b/inference-engine/include/cpp/ie_plugin_cpp.hpp @@ -34,6 +34,7 @@ class InferencePlugin { /** * @brief Constructs a plugin instance from the given pointer. + * @param pointer Initialized Plugin pointer */ explicit InferencePlugin(const InferenceEnginePluginPtr &pointer) : actual(pointer) {} @@ -53,6 +54,7 @@ class InferencePlugin { /** * @deprecated Use InferencePlugin::LoadNetwork(ICNNNetwork &, const std::map &) * @brief Wraps original method IInferencePlugin::LoadNetwork(ICNNNetwork &, ResponseDesc *) + * @param network A network object to load */ INFERENCE_ENGINE_DEPRECATED void LoadNetwork(ICNNNetwork &network) { @@ -64,6 +66,9 @@ class InferencePlugin { /** * @brief Wraps original method * IInferencePlugin::LoadNetwork(IExecutableNetwork::Ptr&, ICNNNetwork&, const std::map &, ResponseDesc*). + * @param network A network object to load + * @param config A map of configuration options + * @return Created Executable Network object */ ExecutableNetwork LoadNetwork(ICNNNetwork &network, const std::map &config) { IExecutableNetwork::Ptr ret; @@ -74,6 +79,9 @@ class InferencePlugin { /** * @brief Wraps original method * IInferencePlugin::LoadNetwork(IExecutableNetwork::Ptr&, ICNNNetwork&, const std::map &, ResponseDesc*). + * @param network A network object to load + * @param config A map of configuration options + * @return Created Executable Network object */ ExecutableNetwork LoadNetwork(CNNNetwork network, const std::map &config) { IExecutableNetwork::Ptr ret; @@ -85,6 +93,8 @@ class InferencePlugin { /** * @deprecated Use IExecutableNetwork to create IInferRequest. * @brief Wraps original method IInferencePlugin::Infer(const BlobMap&, BlobMap&, ResponseDesc *) + * @param input A map of input blobs accessed by input names + * @param result A map of output blobs accessed by output names */ INFERENCE_ENGINE_DEPRECATED void Infer(const BlobMap &input, BlobMap &result) { @@ -96,6 +106,7 @@ class InferencePlugin { /** * @deprecated Use IInferRequest to get performance counters * @brief Wraps original method IInferencePlugin::GetPerformanceCounts + * @return Map of layers names to profiling information for that layers */ INFERENCE_ENGINE_DEPRECATED std::map GetPerformanceCounts() const { @@ -109,6 +120,7 @@ class InferencePlugin { /** * @brief Wraps original method * IInferencePlugin::AddExtension + * @param extension Pointer to loaded Extension */ void AddExtension(InferenceEngine::IExtensionPtr extension) { CALL_STATUS_FNC(AddExtension, extension); @@ -117,6 +129,7 @@ class InferencePlugin { /** * @brief Wraps original method * IInferencePlugin::SetConfig + * @param config A configuration map */ void SetConfig(const std::map &config) { CALL_STATUS_FNC(SetConfig, config); @@ -125,7 +138,10 @@ class InferencePlugin { /** * @brief Wraps original method * IInferencePlugin::ImportNetwork - */ + * @param modelFileName A path to the imported network + * @param config A configuration map + * @return Created Executable Network object + */ ExecutableNetwork ImportNetwork(const std::string &modelFileName, const std::map &config) { IExecutableNetwork::Ptr ret; CALL_STATUS_FNC(ImportNetwork, ret, modelFileName, config); @@ -136,6 +152,8 @@ class InferencePlugin { * @deprecated Use InferencePlugin::QueryNetwork(const ICNNNetwork &, const std::map &, QueryNetworkResult &) const * @brief Wraps original method * IInferencePlugin::QueryNetwork(const ICNNNetwork&, QueryNetworkResult& ) const + * @param network A network object to query + * @param res Query results */ INFERENCE_ENGINE_DEPRECATED void QueryNetwork(const ICNNNetwork &network, QueryNetworkResult &res) const { @@ -145,6 +163,9 @@ class InferencePlugin { /** * @brief Wraps original method * IInferencePlugin::QueryNetwork(const ICNNNetwork&, const std::map &, QueryNetworkResult&) const + * @param network A network object to query + * @param config A configuration map + * @param res Query results */ void QueryNetwork(const ICNNNetwork &network, const std::map &config, QueryNetworkResult &res) const { actual->QueryNetwork(network, config, res); @@ -153,7 +174,7 @@ class InferencePlugin { /** * @brief Converts InferenceEngine to InferenceEnginePluginPtr pointer - * @brief Returns wrapped object + * @return Wrapped object */ operator InferenceEngine::InferenceEnginePluginPtr() { return actual; @@ -162,7 +183,7 @@ class InferencePlugin { /** * @deprecated Deprecated since HeteroPluginPtr is deprecated * @brief Converts InferenceEngine to HeteroPluginPtr pointer - * @return wrapped Hetero object if underlined object is HeteroPlugin instance, nullptr otherwise + * @return Wrapped Hetero object if underlined object is HeteroPlugin instance, nullptr otherwise */ IE_SUPPRESS_DEPRECATED_START operator InferenceEngine::HeteroPluginPtr() { diff --git a/inference-engine/include/details/ie_inetwork_iterator.hpp b/inference-engine/include/details/ie_inetwork_iterator.hpp index 70b505049d4bfe..e7be114553840c 100644 --- a/inference-engine/include/details/ie_inetwork_iterator.hpp +++ b/inference-engine/include/details/ie_inetwork_iterator.hpp @@ -71,8 +71,8 @@ class INetworkIterator: public std::iterator> sortedLayers; std::shared_ptr currentLayer; - size_t currentIdx; NT *network = nullptr; + size_t currentIdx; std::shared_ptr getNextLayer() { return (sortedLayers.size() > currentIdx) ? sortedLayers[currentIdx++] : nullptr; diff --git a/inference-engine/include/details/ie_pre_allocator.hpp b/inference-engine/include/details/ie_pre_allocator.hpp index d4801ba13dcf16..850f90513e061f 100644 --- a/inference-engine/include/details/ie_pre_allocator.hpp +++ b/inference-engine/include/details/ie_pre_allocator.hpp @@ -49,7 +49,7 @@ class PreAllocator : public IAllocator { return _actualData; } - return this; + return nullptr; } /** * @brief The PreAllocator class cannot release the handle @@ -83,4 +83,4 @@ std::shared_ptr make_pre_allocator(T *ptr, size_t size) { } } // namespace details -} // namespace InferenceEngine \ No newline at end of file +} // namespace InferenceEngine diff --git a/inference-engine/include/dlia/dlia_config.hpp b/inference-engine/include/dlia/dlia_config.hpp new file mode 100644 index 00000000000000..1adca7ed33bdea --- /dev/null +++ b/inference-engine/include/dlia/dlia_config.hpp @@ -0,0 +1,81 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +/** + * @brief A header that defines advanced related properties for DLIA plugins. + * These properties should be used in SetConfig() and LoadNetwork() methods of plugins + * + * @file dlia_config.hpp + */ + +#pragma once + +#include +#include "ie_plugin_config.hpp" + +namespace InferenceEngine { + +namespace DliaMetrics { + +/** + * @def DLIA_METRIC_VALUE(name) + * @brief Shortcut for defining FPGA metric values + */ +#define DLIA_METRIC_VALUE(name) InferenceEngine::DliaMetrics::name +#define DECLARE_DLIA_METRIC_VALUE(name) static constexpr auto name = #name + +/** + * @brief FP11 optimization capability. It's specific for FPGA device which can perform computations in FP11 data type. + */ +DECLARE_DLIA_METRIC_VALUE(FP11); + +/** + * @brief Input Streaming capability. It's specific for FPGA device which can perform input streaming + */ +DECLARE_DLIA_METRIC_VALUE(INPUT_STREAMING); + +} // namespace DliaMetrics + +namespace DLIAConfigParams { + +/** + * @def DLIA_CONFIG_KEY(name) + * @brief Shortcut for defining FPGA configuration keys + */ +#define DLIA_CONFIG_KEY(name) InferenceEngine::DLIAConfigParams::_CONFIG_KEY(DLIA_##name) + +#define DECLARE_DLIA_CONFIG_KEY(name) DECLARE_CONFIG_KEY(DLIA_##name) +#define DECLARE_DLIA_CONFIG_VALUE(name) DECLARE_CONFIG_VALUE(DLIA_##name) + +/** + * @brief The key to define the type of transformations for DLIA inputs and outputs. + * DLIA use custom data layout for input and output blobs. IE DLIA Plugin provides custom + * optimized version of transformation functions that do not use OpenMP and much more faster + * than native DLIA functions. Values: "NO" - optimized plugin transformations + * are used, "YES" - native DLIA transformations are used. + */ +DECLARE_DLIA_CONFIG_KEY(IO_TRANSFORMATIONS_NATIVE); + +/** + * @brief The key to define path to DLA bitstreams architectures folder + */ +DECLARE_DLIA_CONFIG_KEY(ARCH_ROOT_DIR); + +/** + * @brief The bool key to define whether theoretical performance estimation should be performed. + * If true, the estimated performance is dumped via performance counters as "FPGA theoretical execute time" + */ +DECLARE_DLIA_CONFIG_KEY(PERF_ESTIMATION); + +// TODO: Temporarily adding dlia config to test streaming feature +// Values - "YES" or "NO" +DECLARE_DLIA_CONFIG_KEY(ENABLE_STREAMING); + +/** + * @brief The bool key to define whether information messages with a reason are printed in case the layer is unsupported by DLA + */ +DECLARE_DLIA_CONFIG_KEY(DUMP_SUPPORTED_LAYERS_INFORMATION); + +} // namespace DLIAConfigParams +} // namespace InferenceEngine diff --git a/inference-engine/include/hetero/hetero_plugin_config.hpp b/inference-engine/include/hetero/hetero_plugin_config.hpp index fb17d07d6d5657..2eb362137697e6 100644 --- a/inference-engine/include/hetero/hetero_plugin_config.hpp +++ b/inference-engine/include/hetero/hetero_plugin_config.hpp @@ -20,6 +20,10 @@ namespace InferenceEngine { namespace HeteroConfigParams { +/** + * @def HETERO_CONFIG_KEY(name) + * @brief Shortcut for defining HETERO configuration keys + */ #define HETERO_CONFIG_KEY(name) InferenceEngine::HeteroConfigParams::_CONFIG_KEY(HETERO_##name) #define DECLARE_HETERO_CONFIG_KEY(name) DECLARE_CONFIG_KEY(HETERO_##name) #define DECLARE_HETERO_CONFIG_VALUE(name) DECLARE_CONFIG_VALUE(HETERO_##name) diff --git a/inference-engine/include/ie_allocator.hpp b/inference-engine/include/ie_allocator.hpp index 08b68382f0b89a..af2e0d142b828b 100644 --- a/inference-engine/include/ie_allocator.hpp +++ b/inference-engine/include/ie_allocator.hpp @@ -28,6 +28,8 @@ class IAllocator : public details::IRelease { public: /** * @brief Maps handle to heap memory accessible by any memory manipulation routines. + * @param handle Handle to the allocated memory to be locked + * @param LockOp Operation to lock memory for * @return Generic pointer to memory */ virtual void * lock(void * handle, LockOp = LOCK_FOR_WRITE) noexcept = 0; @@ -35,6 +37,7 @@ class IAllocator : public details::IRelease { * @brief Unmaps memory by handle with multiple sequential mappings of the same handle. * The multiple sequential mappings of the same handle are suppose to get the same * result while there isn't a ref counter supported. + * @param handle Handle to the locked memory to unlock */ virtual void unlock(void * handle) noexcept = 0; /** diff --git a/inference-engine/include/ie_api.h b/inference-engine/include/ie_api.h index 2394c0c5da0356..e9132f17c06824 100644 --- a/inference-engine/include/ie_api.h +++ b/inference-engine/include/ie_api.h @@ -57,11 +57,16 @@ #define IE_DO_PRAGMA(x) #endif -#ifdef _MSC_VER +#if defined (_MSC_VER) && !defined (__clang__) #define IE_SUPPRESS_DEPRECATED_START \ IE_DO_PRAGMA(warning(push)) \ IE_DO_PRAGMA(warning(disable: 4996)) #define IE_SUPPRESS_DEPRECATED_END IE_DO_PRAGMA(warning(pop)) +#elif defined(__INTEL_COMPILER) +#define IE_SUPPRESS_DEPRECATED_START \ + IE_DO_PRAGMA(warning(push)) \ + IE_DO_PRAGMA(warning(disable:1478)) +#define IE_SUPPRESS_DEPRECATED_END IE_DO_PRAGMA(warning(pop)) #elif defined(__clang__) || ((__GNUC__) && (__GNUC__*100 + __GNUC_MINOR__ > 405)) #define IE_SUPPRESS_DEPRECATED_START \ IE_DO_PRAGMA(GCC diagnostic push) \ @@ -71,3 +76,11 @@ #define IE_SUPPRESS_DEPRECATED_START #define IE_SUPPRESS_DEPRECATED_END #endif + +#ifndef ENABLE_UNICODE_PATH_SUPPORT + #if defined(_WIN32) + #define ENABLE_UNICODE_PATH_SUPPORT + #elif defined(__GNUC__) && (__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ > 2)) + #define ENABLE_UNICODE_PATH_SUPPORT + #endif +#endif diff --git a/inference-engine/include/ie_blob.h b/inference-engine/include/ie_blob.h index d2628ad5cd4150..12d341d9f74b2a 100644 --- a/inference-engine/include/ie_blob.h +++ b/inference-engine/include/ie_blob.h @@ -678,7 +678,7 @@ class TBlob : public MemoryBlob { if (_handle != nullptr) { getAllocator()->free(_handle); } - _handle = getAllocator()->alloc(byteSize()); + _handle = getAllocator()->alloc(size() * sizeof(T)); } /** @@ -779,10 +779,7 @@ class TBlob : public MemoryBlob { * @brief Frees handler and cleans up the stored data. */ virtual bool free() { - bool bCanRelease = true; - if (_handle == nullptr) return bCanRelease; - - bCanRelease = getAllocator()->free(_handle); + bool bCanRelease = getAllocator()->free(_handle); _handle = nullptr; return bCanRelease; } diff --git a/inference-engine/include/ie_common.h b/inference-engine/include/ie_common.h index 9e7241da4321a0..581b877e5b35d4 100644 --- a/inference-engine/include/ie_common.h +++ b/inference-engine/include/ie_common.h @@ -109,6 +109,8 @@ inline std::ostream & operator << (std::ostream &out, const Layout & p) { PRINT_LAYOUT(ANY); PRINT_LAYOUT(NCHW); PRINT_LAYOUT(NHWC); + PRINT_LAYOUT(NCDHW); + PRINT_LAYOUT(NDHWC); PRINT_LAYOUT(OIHW); PRINT_LAYOUT(C); PRINT_LAYOUT(CHW); @@ -125,7 +127,7 @@ inline std::ostream & operator << (std::ostream &out, const Layout & p) { } /** - * @enum Color format + * @enum ColorFormat * @brief Extra information about input color format for preprocessing */ enum ColorFormat : uint32_t { diff --git a/inference-engine/include/ie_core.hpp b/inference-engine/include/ie_core.hpp index b47357291f4948..d0d266017f9561 100644 --- a/inference-engine/include/ie_core.hpp +++ b/inference-engine/include/ie_core.hpp @@ -35,7 +35,7 @@ class INFERENCE_ENGINE_API_CLASS(Core) { /** * @brief Returns plugins version information - * @param Device name to indentify plugin + * @param deviceName Device name to indentify plugin * @return A vector of versions */ std::map GetVersions(const std::string & deviceName) const; @@ -133,7 +133,8 @@ class INFERENCE_ENGINE_API_CLASS(Core) { /** @brief Registers plugin to Inference Engine Core instance using XML configuration file with * plugins description. XML file has the following structure: - * + * + * ```xml * * * @@ -144,14 +145,16 @@ class INFERENCE_ENGINE_API_CLASS(Core) { * * * - * + * * - * + * ``` + * * - `name` identifies name of device enabled by plugin * - `location` specifies absolute path to dynamic library with plugin. A path can also be relative to inference engine shared library. * It allows to have common config for different systems with different configurations. * - Properties are set to plugin via the `SetConfig` method. * - Extensions are set to plugin via the `AddExtension` method. + * @param xmlConfigFile A path to .xml file with plugins to register. */ void RegisterPlugins(const std::string & xmlConfigFile); }; diff --git a/inference-engine/include/ie_data.h b/inference-engine/include/ie_data.h index 75e906f4424c2f..d43824d6198e20 100644 --- a/inference-engine/include/ie_data.h +++ b/inference-engine/include/ie_data.h @@ -74,6 +74,7 @@ class INFERENCE_ENGINE_API_CLASS(Data) { * @brief An empty constructor (dimensionless) * @param name Name of the data node * @param _precision Precision of the data + * @param layout Data layout */ Data(const std::string &name, Precision _precision, Layout layout = NCHW); @@ -82,6 +83,7 @@ class INFERENCE_ENGINE_API_CLASS(Data) { * @param name Name of the data node * @param a_dims Data tensor dimensions * @param _precision Precision of the data + * @param layout Data layout */ Data(const std::string &name, const SizeVector &a_dims, Precision _precision, Layout layout = NCHW); /** @@ -183,7 +185,7 @@ class INFERENCE_ENGINE_API_CLASS(Data) { /** * @brief Sets a name the Data object - * @param name Name of the data node + * @param newName Name of the data node */ void setName(const std::string& newName); diff --git a/inference-engine/include/ie_device.hpp b/inference-engine/include/ie_device.hpp index c16602113e768e..fef50c206333a6 100644 --- a/inference-engine/include/ie_device.hpp +++ b/inference-engine/include/ie_device.hpp @@ -28,8 +28,10 @@ enum class TargetDevice : uint8_t { eGPU = 3, eFPGA = 4, eMYRIAD = 5, + eHDDL = 6, eGNA = 7, eHETERO = 8, + eMULTI = 10, }; /** @@ -53,8 +55,10 @@ class INFERENCE_ENGINE_DEPRECATED TargetDeviceInfo { DECL_DEVICE(GPU), DECL_DEVICE(FPGA), DECL_DEVICE(MYRIAD), + DECL_DEVICE(HDDL), DECL_DEVICE(GNA), - DECL_DEVICE(HETERO) + DECL_DEVICE(HETERO), + DECL_DEVICE(MULTI) }; #undef DECLARE return g_allDeviceInfos; @@ -64,6 +68,8 @@ class INFERENCE_ENGINE_DEPRECATED TargetDeviceInfo { /** * @deprecated Deprecated since InferenceEngine::TargetDevice is deprecated * @brief Converts string representation of device to InferenceEngine::TargetDevice enum value + * @param deviceName A string representation of a device name + * @return An instance of InferenceEngine::TargetDevice */ INFERENCE_ENGINE_DEPRECATED static TargetDevice fromStr(const std::string &deviceName) { @@ -72,9 +78,11 @@ class INFERENCE_ENGINE_DEPRECATED TargetDeviceInfo { { "GPU", InferenceEngine::TargetDevice::eGPU }, { "FPGA", InferenceEngine::TargetDevice::eFPGA }, { "MYRIAD", InferenceEngine::TargetDevice::eMYRIAD }, + { "HDDL", InferenceEngine::TargetDevice::eHDDL }, { "GNA", InferenceEngine::TargetDevice::eGNA }, { "BALANCED", InferenceEngine::TargetDevice::eBalanced }, - { "HETERO", InferenceEngine::TargetDevice::eHETERO } + { "HETERO", InferenceEngine::TargetDevice::eHETERO }, + { "MULTI", InferenceEngine::TargetDevice::eMULTI} }; auto val = deviceFromNameMap.find(deviceName); return val != deviceFromNameMap.end() ? val->second : InferenceEngine::TargetDevice::eDefault; @@ -82,7 +90,9 @@ class INFERENCE_ENGINE_DEPRECATED TargetDeviceInfo { /** * @deprecated Deprecated since InferenceEngine::TargetDevice is deprecated - * @brief Converts InferenceEngine::TargetDevice enum value to string representation + * @brief Converts an instance of InferenceEngine::TargetDevice to string representation + * @param device Instance of InferenceEngine::TargetDevice + * @return A c-string with the name */ INFERENCE_ENGINE_DEPRECATED static const char * name(TargetDevice device) { diff --git a/inference-engine/include/ie_icnn_network_stats.hpp b/inference-engine/include/ie_icnn_network_stats.hpp index 2547fb6ba45a02..fb4847d7973e46 100644 --- a/inference-engine/include/ie_icnn_network_stats.hpp +++ b/inference-engine/include/ie_icnn_network_stats.hpp @@ -4,6 +4,7 @@ /** * @brief This is a header file for the ICNNNetworkStats class + * * @file ie_icnn_network_stats.hpp */ #pragma once @@ -18,9 +19,17 @@ namespace InferenceEngine { class NetworkNodeStats; - +/** + * @brief A shared pointer to the NetworkNodeStats object + */ using NetworkNodeStatsPtr = std::shared_ptr; +/** + * @brief A smart pointer to the NetworkNodeStats object + */ using NetworkNodeStatsWeakPtr = std::weak_ptr; +/** + * @brief A map of pairs: name of a layer and related statistics + */ using NetworkStatsMap = std::map; /** * @class ICNNNetworkStats @@ -28,16 +37,44 @@ using NetworkStatsMap = std::map; */ class ICNNNetworkStats : public details::IRelease { public: + /** + * @brief Sets a map which contains layers with statistics + * + * @param stats A map which is set + * Abstract method + */ virtual void setNodesStats(const NetworkStatsMap& stats) = 0; + /** + * @brief Gets a map which contains layers with statistics + * + * Abstract method + * @return A NetworkStatsMap object + */ virtual const NetworkStatsMap& getNodesStats() const = 0; - + /** + * @brief Checks if a container is empty + * + * Abstract method + * @return A bool value which shows whether a container is empty + */ virtual bool isEmpty() const = 0; }; - +/** + * @class NetworkNodeStats + * @brief This class implements a container which stores statistics for a layer + */ class NetworkNodeStats { public: + /** + * @brief The constructor which creates NetworkNodeStats object + */ NetworkNodeStats() { } + /** + * @brief The constructor which creates NetworkNodeStats object with filled statistics + * + * @param statCount The number of minimum/maximum values in statistics + */ explicit NetworkNodeStats(int statCount) { float mn = (std::numeric_limits::max)(); float mx = (std::numeric_limits::min)(); @@ -49,7 +86,13 @@ class NetworkNodeStats { } public: + /** + * @brief Vector of floats which contains minimum values of layers activations + */ std::vector _minOutputs; + /** + * @brief Vector of floats which contains maximum values of layers activations + */ std::vector _maxOutputs; }; diff --git a/inference-engine/include/ie_iexecutable_network.hpp b/inference-engine/include/ie_iexecutable_network.hpp index 7dafa4086582a9..a64403890f6404 100644 --- a/inference-engine/include/ie_iexecutable_network.hpp +++ b/inference-engine/include/ie_iexecutable_network.hpp @@ -38,39 +38,51 @@ class IExecutableNetwork : public details::IRelease { using Ptr = std::shared_ptr; /** - * @brief Gets the Executable network output Data node information. The received info is stored in the given ConstOutputsDataMap node. + * @brief Gets the Executable network output Data node information. + * + * The received info is stored in the given ::ConstOutputsDataMap node. * This method need to be called to find output names for using them later during filling of a map * of blobs passed to InferenceEngine::IInferencePlugin::Infer() - * @param out Reference to the ConstOutputsDataMap object + * + * @param out Reference to the ::ConstOutputsDataMap object * @param resp Optional: pointer to an already allocated object to contain information in case of failure - * @return Status code of the operation: OK (0) for success + * @return Status code of the operation: InferenceEngine::OK (0) for success */ virtual StatusCode GetOutputsInfo(ConstOutputsDataMap &out, ResponseDesc *resp) const noexcept = 0; /** - * @brief Gets the Executable network input Data node information. The received info is stored in the given ConstInputsDataMap object. + * @brief Gets the executable network input Data node information. + * + * The received info is stored in the given ::ConstInputsDataMap object. * This method need to be called to find out input names for using them later during filling of a map * of blobs passed to InferenceEngine::IInferencePlugin::Infer() - * @param inputs Reference to ConstInputsDataMap object. + * + * @param inputs Reference to ::ConstInputsDataMap object. * @param resp Optional: pointer to an already allocated object to contain information in case of failure - * @return Status code of the operation: OK (0) for success + * @return Status code of the operation: InferenceEngine::OK (0) for success */ virtual StatusCode GetInputsInfo(ConstInputsDataMap &inputs, ResponseDesc *resp) const noexcept = 0; /** * @brief Creates an inference request object used to infer the network. + * * The created request has allocated input and output blobs (that can be changed later). + * * @param req Shared pointer to the created request object * @param resp Optional: pointer to an already allocated object to contain information in case of failure - * @return Status code of the operation: OK (0) for success + * @return Status code of the operation: InferenceEngine::OK (0) for success */ virtual StatusCode CreateInferRequest(IInferRequest::Ptr& req, ResponseDesc *resp) noexcept = 0; /** - * @brief Exports the current executable network so it can be used later in the Import() main API + * @brief Exports the current executable network. + * + * @see Core::ImportNetwork + * @see IInferencePlugin::ImportNetwork + * * @param modelFileName Full path to the location of the exported file * @param resp Optional: pointer to an already allocated object to contain information in case of failure - * @return Status code of the operation: OK (0) for success + * @return Status code of the operation: InferenceEngine::OK (0) for success */ virtual StatusCode Export(const std::string& modelFileName, ResponseDesc *resp) noexcept = 0; @@ -78,53 +90,64 @@ class IExecutableNetwork : public details::IRelease { * @brief Get the mapping of IR layer names to implemented kernels * @param deployedTopology Map of PrimitiveInfo objects that represent the deployed topology * @param resp Optional: pointer to an already allocated object to contain information in case of failure - * @return Status code of the operation: OK (0) for success + * @return Status code of the operation: InferenceEngine::OK (0) for success */ virtual StatusCode GetMappedTopology(std::map> &deployedTopology, ResponseDesc *resp) noexcept = 0; /** * @brief Get executable graph information from a device + * * @param graphPtr network ptr to store executable graph information * @param resp Optional: pointer to an already allocated object to contain information in case of failure - * @return Status code of the operation: OK (0) for success + * @return Status code of the operation: InferenceEngine::OK (0) for success */ virtual StatusCode GetExecGraphInfo(ICNNNetwork::Ptr &graphPtr, ResponseDesc *resp) noexcept = 0; /** - * @brief Gets state control interface for given executable network, State control essential for recurrent networks + * @brief Gets state control interface for given executable network. + * + * State control essential for recurrent networks + * * @param pState reference to a pointer that receives internal states * @param idx requested index for receiving memory state * @param resp Optional: pointer to an already allocated object to contain information in case of failure - * @return Status code of the operation: OK (0) for success, OUT_OF_BOUNDS (-6) no memory state for given index + * @return Status code of the operation: InferenceEngine::OK (0) for success, OUT_OF_BOUNDS (-6) no memory state for given index */ virtual StatusCode QueryState(IMemoryState::Ptr & pState, size_t idx, ResponseDesc *resp) noexcept = 0; /** * @brief Sets configuration for current executable network + * * @param config Map of pairs: (config parameter name, config parameter value) * @param resp Pointer to the response message that holds a description of an error if any occurred - * @return code of the operation. OK if succeeded + * @return code of the operation. InferenceEngine::OK if succeeded */ virtual StatusCode SetConfig(const std::map &config, ResponseDesc *resp) noexcept = 0; - /** @brief Gets configuration for current executable network. The method is responsible to extract information - * which affects executable network execution. The list of supported configuration values can be extracted via - * ExecutableNetwork::GetMetric with the SUPPORTED_CONFIG_KEYS key, but some of these keys cannot be changed dymanically, - * e.g. DEVICE_ID cannot changed if an executable network has already been compiled for particular device. - * @param name - config key, can be found in ie_plugin_config.hpp - * @param result - value of config corresponding to config key - * @param resp - Pointer to the response message that holds a description of an error if any occurred - * @return code of the operation. OK if succeeded - */ + /** @brief Gets configuration for current executable network. + * + * The method is responsible to extract information + * which affects executable network execution. The list of supported configuration values can be extracted via + * ExecutableNetwork::GetMetric with the SUPPORTED_CONFIG_KEYS key, but some of these keys cannot be changed dymanically, + * e.g. DEVICE_ID cannot changed if an executable network has already been compiled for particular device. + * + * @param name config key, can be found in ie_plugin_config.hpp + * @param result value of config corresponding to config key + * @param resp Pointer to the response message that holds a description of an error if any occurred + * @return code of the operation. InferenceEngine::OK if succeeded + */ virtual StatusCode GetConfig(const std::string &name, Parameter &result, ResponseDesc *resp) const noexcept = 0; /** - * @brief Gets general runtime metric for an executable network. It can be network name, actual device ID on + * @brief Gets general runtime metric for an executable network. + * + * It can be network name, actual device ID on * which executable network is running or all other properties which cannot be changed dynamically. - * @param name - metric name to request - * @param result - metric value corresponding to metric key - * @param resp - Pointer to the response message that holds a description of an error if any occurred - * @return code of the operation. OK if succeeded + * + * @param name metric name to request + * @param result metric value corresponding to metric key + * @param resp Pointer to the response message that holds a description of an error if any occurred + * @return code of the operation. InferenceEngine::OK if succeeded */ virtual StatusCode GetMetric(const std::string &name, Parameter &result, ResponseDesc *resp) const noexcept = 0; }; diff --git a/inference-engine/include/ie_iextension.h b/inference-engine/include/ie_iextension.h index e1510d91c652be..22c963398ef206 100644 --- a/inference-engine/include/ie_iextension.h +++ b/inference-engine/include/ie_iextension.h @@ -20,11 +20,15 @@ #include "details/ie_no_copy.hpp" +/** + * @def INFERENCE_EXTENSION_API(TYPE) + * @brief Defines Inference Engine Extension API method + */ #if defined(_WIN32) && defined(IMPLEMENT_INFERENCE_EXTENSION_API) -#define INFERENCE_EXTENSION_API(TYPE) extern "C" __declspec(dllexport) TYPE +# define INFERENCE_EXTENSION_API(TYPE) extern "C" __declspec(dllexport) TYPE #else -#define INFERENCE_EXTENSION_API(TYPE) INFERENCE_ENGINE_API(TYPE) +# define INFERENCE_EXTENSION_API(TYPE) INFERENCE_ENGINE_API(TYPE) #endif diff --git a/inference-engine/include/ie_ihetero_plugin.hpp b/inference-engine/include/ie_ihetero_plugin.hpp index c1e3fae543cd27..6ef3506c2fa8a5 100644 --- a/inference-engine/include/ie_ihetero_plugin.hpp +++ b/inference-engine/include/ie_ihetero_plugin.hpp @@ -23,8 +23,10 @@ namespace InferenceEngine { * plugin during setting of affinity and loading of split sub-network to the plugins * The custom loader can define addition settings for the plugins or network loading * Examples of cases when this interface should be implemented in the application: + * * 1. add custom layers to existing plugins if it is not pointed to the heterogeneous plugin * or registration of custom layer is different than supported in available public plugins + * * 2. set affinity manually for the same plugin being initialized by different parameters, * e.g different device id * In this case there will be mapping of @@ -89,18 +91,29 @@ class INFERENCE_ENGINE_DEPRECATED INFERENCE_ENGINE_API_CLASS(IHeteroDeviceLoader INFERENCE_ENGINE_DEPRECATED virtual void QueryNetwork(const std::string &device, const ICNNNetwork &network, - const std::map& /*config*/, + const std::map& config, QueryNetworkResult &res) noexcept = 0; - INFERENCE_ENGINE_DEPRECATED + + /** + * @deprecated Use InferenceEngine::Core with HETERO device in InferenceEngine::Core::QueryNetwork. + * @brief Sets log callback + * @param listener A reference to IErrorListener object + */ virtual void SetLogCallback(IErrorListener &listener) = 0; IE_SUPPRESS_DEPRECATED_START + /** + * @brief Shared pointer to IHeteroDeviceLoader instance + */ using Ptr = std::shared_ptr; IE_SUPPRESS_DEPRECATED_END }; IE_SUPPRESS_DEPRECATED_START +/** + * @brief Represents map from device name to device-specific loader + */ using MapDeviceLoaders = std::map; IE_SUPPRESS_DEPRECATED_END diff --git a/inference-engine/include/ie_iinfer_request.hpp b/inference-engine/include/ie_iinfer_request.hpp index d922f5b49f0cbe..e8dc9eed2f1c51 100644 --- a/inference-engine/include/ie_iinfer_request.hpp +++ b/inference-engine/include/ie_iinfer_request.hpp @@ -33,12 +33,18 @@ class IInferRequest : public details::IRelease { /** IInferRequest doesn't block or interrupt current thread and immediately returns inference status */ STATUS_ONLY = 0, }; - + /** + * @brief A shared pointer to the IInferRequest object + */ using Ptr = std::shared_ptr; + /** + * @brief A smart pointer to the IInferRequest object + */ using WeakPtr = std::weak_ptr; /** * @brief Sets input/output data to infer + * * @note: Memory allocation does not happen * @param name Name of input or output blob. * @param data Reference to input or output blob. The type of a blob must match the network input precision and size. @@ -49,6 +55,7 @@ class IInferRequest : public details::IRelease { /** * @brief Gets input/output data for inference + * * @note: Memory allocation does not happen * @param name Name of input or output blob. * @param data Reference to input or output blob. The type of Blob must match the network input precision and size. @@ -59,6 +66,7 @@ class IInferRequest : public details::IRelease { /** * @brief Infers specified input(s) in synchronous mode + * * @note blocks all methods of IInferRequest while request is ongoing (running or waiting in queue) * @param resp Optional: pointer to an already allocated object to contain information in case of failure * @return Status code of the operation: OK (0) for success @@ -67,6 +75,7 @@ class IInferRequest : public details::IRelease { /** * @brief Queries performance measures per layer to get feedback of what is the most time consuming layer + * * @note: not all plugins provide meaningful data * @param perfMap Map of layer names to profiling information for that layer * @param resp Optional: pointer to an already allocated object to contain information in case of failure @@ -77,6 +86,7 @@ class IInferRequest : public details::IRelease { /** * @brief Waits for the result to become available. Blocks until specified millis_timeout has elapsed or the result becomes available, whichever comes first. + * * @param millis_timeout Maximum duration in milliseconds to block for * @note There are special cases when millis_timeout is equal some value of the WaitMode enum: * * STATUS_ONLY - immediately returns inference status (IInferRequest::RequestStatus). It does not block or interrupt current thread @@ -88,6 +98,7 @@ class IInferRequest : public details::IRelease { /** * @brief Starts inference of specified input(s) in asynchronous mode + * * @note: It returns immediately. Inference starts also immediately * @param resp Optional: a pointer to an already allocated object to contain extra information of a failure (if occurred) * @return Enumeration of the resulted action: OK (0) for success @@ -96,6 +107,7 @@ class IInferRequest : public details::IRelease { /** * @brief Completion callback definition as pointer to a function + * * @param context Pointer to request for providing context inside callback * @param code Completion result status: OK (0) for success */ @@ -104,6 +116,7 @@ class IInferRequest : public details::IRelease { /** * @brief Sets a callback function that will be called on success or failure of asynchronous request + * * @param callback A function to be called * @return Enumeration of the resulted action: OK (0) for success */ @@ -111,6 +124,7 @@ class IInferRequest : public details::IRelease { /** * @brief Gets arbitrary data for the request and stores a pointer to a pointer to the obtained data + * * @param data Pointer to a pointer to the gotten arbitrary data * @param resp Optional: a pointer to an already allocated object to contain extra information of a failure (if occurred) * @return Enumeration of the resulted action: OK (0) for success @@ -119,6 +133,7 @@ class IInferRequest : public details::IRelease { /** * @brief Sets arbitrary data for the request + * * @param data Pointer to a pointer to arbitrary data to set * @param resp Optional: a pointer to an already allocated object to contain extra information of a failure (if occurred) * @return Enumeration of the resulted action: OK (0) for success @@ -127,6 +142,7 @@ class IInferRequest : public details::IRelease { /** * @brief Sets new batch size when dynamic batching is enabled in executable network that created this request. + * * @param batch_size new batch size to be used by all the following inference calls for this request. * @param resp Optional: a pointer to an already allocated object to contain extra information of a failure (if occurred) * @return Enumeration of the resulted action: OK (0) for success diff --git a/inference-engine/include/ie_layers.h b/inference-engine/include/ie_layers.h index 66f43fa00c684d..25941b7cd409e2 100644 --- a/inference-engine/include/ie_layers.h +++ b/inference-engine/include/ie_layers.h @@ -139,6 +139,16 @@ class CNNLayer { return res; } } + /** + * @brief serialize float with c_locale formating + * used for default values serializing + */ + static std::string ie_serialize_float(float value) { + std::stringstream val_stream; + val_stream.imbue(std::locale("C")); + val_stream << value; + return val_stream.str(); + } /** * @brief Gets float value for the given parameter @@ -147,7 +157,7 @@ class CNNLayer { * @return float value */ float GetParamAsFloat(const char* param, float def) const { - std::string val = GetParamAsString(param, std::to_string(def).c_str()); + std::string val = GetParamAsString(param, ie_serialize_float(def).c_str()); try { return ie_parse_float(val); } catch (...) { @@ -391,11 +401,11 @@ class CNNLayer { return result; } /** - * @brief Returns an boolean value for the given parameter. + * @brief Returns a boolean value for the given parameter. * The valid values are (true, false, 1, 0). * @param param Name of the layer parameter * @param def Default value of the parameter if not found - * @return An bool value for the specified parameter + * @return A bool value for the specified parameter */ bool GetParamAsBool(const char *param, bool def) const { std::string val = GetParamAsString(param, std::to_string(def).c_str()); @@ -414,7 +424,29 @@ class CNNLayer { return result; } /** - * @deprecated Use CNNLayer::GetParamAsBool + * @brief Returns a boolean value for the given parameter + * @param param Name of the layer parameter + * @return A bool value for the specified parameter + */ + bool GetParamAsBool(const char *param) const { + std::string val = GetParamAsString(param); + std::string loweredCaseValue; + std::transform(val.begin(), val.end(), std::back_inserter(loweredCaseValue), [](char value) { + return std::tolower(value); + }); + + bool result = false; + + if (!(std::istringstream(loweredCaseValue) >> std::boolalpha >> result)) { + // attempting parse using non alpha bool + return (GetParamAsInt(param) != 0); + } + + return result; + } + + /** + * @deprecated Use GetParamAsBool function for that functionality */ INFERENCE_ENGINE_DEPRECATED bool GetParamsAsBool(const char *param, bool def) const { @@ -588,10 +620,6 @@ class ConvolutionLayer : public WeightableLayer { } return *this; } - /** - * @brief move assignment operator - */ - ConvolutionLayer& operator = (ConvolutionLayer &&) = default; /** * @brief copy constructor */ @@ -696,11 +724,6 @@ class PoolingLayer : public CNNLayer { } return *this; } - /** - * @brief move assignment operator - */ - PoolingLayer& operator = (PoolingLayer &&) = default; - /** * @brief copy constructor */ @@ -799,10 +822,6 @@ class BinaryConvolutionLayer : public WeightableLayer { } return *this; } - /** - * @brief move assignment operator - */ - BinaryConvolutionLayer& operator = (BinaryConvolutionLayer &&) = default; /** * @brief copy constructor */ @@ -1020,7 +1039,7 @@ class EltwiseLayer : public CNNLayer { enum eOperation { Sum = 0, Prod, Max, Sub, Min, Div, Squared_diff, Floor_mod, Pow, Equal, Not_equal, Less, Less_equal, Greater, Greater_equal, - Logical_AND, Logical_OR, Logical_XOR, Logical_NOT, Mean, Select + Logical_AND, Logical_OR, Logical_XOR, Logical_NOT, Mean }; /** @@ -1249,7 +1268,11 @@ class RNNCellBase : public WeightableLayer { * - Ct = ft (.) Ct-1 + it (.) ct * - Ht = ot (.) _h(Ct) */ -using LSTMCell = RNNCellBase; +class LSTMCell : public RNNCellBase { + public: + using RNNCellBase::RNNCellBase; + using RNNCellBase::operator=; +}; /** * @brief GRU Cell layer @@ -1284,7 +1307,11 @@ using LSTMCell = RNNCellBase; * - ht = _g(Wh*[rt (.) Ht-1, Xt] + Bh) * - Ht = (1 - zt) (.) ht + zt (.) Ht-1 */ -using GRUCell = RNNCellBase; +class GRUCell : public RNNCellBase { + public: + using RNNCellBase::RNNCellBase; + using RNNCellBase::operator=; +}; /** * @brief RNN Cell layer @@ -1314,7 +1341,12 @@ using GRUCell = RNNCellBase; * * - Ht = _f(Wi*[Ht-1, Xt] + Bi) */ -using RNNCell = RNNCellBase; +class RNNCell : public RNNCellBase { + public: + using RNNCellBase::RNNCellBase; + using RNNCellBase::operator=; +}; + /** * @brief Sequence of recurrent cells @@ -1603,6 +1635,19 @@ class SpaceToDepthLayer : public CNNLayer { }; +/** + * @brief This class represents SparseFillEmptyRows layer + * SparseFillEmptyRows fills empty rows in a sparse tensor + */ +class SparseFillEmptyRowsLayer : public CNNLayer { +public: + /** + * @brief Creates a new SparseFillEmptyRowsLayer instance. + */ + using CNNLayer::CNNLayer; +}; + + /** * @brief This class represents a standard Reverse Sequence layer * Reverse Sequence modifies input tensor according parameters @@ -1787,4 +1832,61 @@ class TopKLayer : public CNNLayer { }; +/** + * @brief This class represents Unique layer. + * The Unique operation searches for unique elements in 1-D input + */ +class UniqueLayer : public CNNLayer { +public: + /** + * @brief A flag indicating whether to sort unique elements + */ + bool sorted; + /** + * @brief A flag indicating whether to return indices of input data elements in the output of uniques + */ + bool return_inverse; + /** + * @brief A flag indicating whether to return a number of occurences for each unique element + */ + bool return_counts; + + /** + * @brief Creates a new UniqueLayer instance. + */ + using CNNLayer::CNNLayer; +}; + + +/** + * @brief This class represents a standard NonMaxSuppression layer + */ +class NonMaxSuppressionLayer : public CNNLayer { +public: + /** + * @brief The 'center_point_box' indicates the format of the box data + */ + bool center_point_box = false; + /** + * @brief Creates a new NonMaxSuppressionLayer instance. + */ + using CNNLayer::CNNLayer; +}; + + +/** + * @brief This class represents a standard Scatter layer + */ +class ScatterLayer : public CNNLayer { +public: + /** + * @brief The axis in Dictionary to scatter Indexes from + */ + int axis = 0; + /** + * @brief Creates a new ScatterLayer instance. + */ + using CNNLayer::CNNLayer; +}; + } // namespace InferenceEngine diff --git a/inference-engine/include/ie_layouts.h b/inference-engine/include/ie_layouts.h index 38901c07dbb66f..45e07ba6b9f731 100644 --- a/inference-engine/include/ie_layouts.h +++ b/inference-engine/include/ie_layouts.h @@ -226,6 +226,7 @@ class INFERENCE_ENGINE_API_CLASS(TensorDesc) { inconsistentLayout = dims.size() != 1; break; case Layout::BLOCKED: + case Layout::ANY: inconsistentLayout = false; break; case Layout::NCDHW: diff --git a/inference-engine/include/ie_parallel.hpp b/inference-engine/include/ie_parallel.hpp index a214b10b01a7cd..4d9ada8aac3afd 100644 --- a/inference-engine/include/ie_parallel.hpp +++ b/inference-engine/include/ie_parallel.hpp @@ -24,6 +24,7 @@ #include "tbb/parallel_for.h" #include "tbb/task_arena.h" +#include "tbb/parallel_sort.h" #include "tbb/parallel_reduce.h" #include "tbb/blocked_range.h" #include "tbb/blocked_range2d.h" @@ -40,6 +41,7 @@ inline int parallel_get_env_threads() { return 0; } #define PARTITIONING #endif #elif IE_THREAD == IE_THREAD_OMP +#include #include #include #include @@ -66,6 +68,7 @@ inline int parallel_get_env_threads() { } #elif IE_THREAD == IE_THREAD_SEQ +#include // NOLINT inline int parallel_get_env_threads() { return 1; } inline int parallel_get_max_threads() { return 1; } inline int parallel_get_num_threads() { return 1; } @@ -130,6 +133,18 @@ void parallel_nt_static(int nthr, const F &func) { #endif } +template +void parallel_sort(I begin, I end, const F &comparator) { +#if (IE_THREAD == IE_THREAD_TBB || IE_THREAD == IE_THREAD_TBB_AUTO) + tbb::parallel_sort(begin, end, comparator); +#elif IE_THREAD == IE_THREAD_OMP + // TODO: propose OpenMP version + std::sort(begin, end, comparator); +#elif IE_THREAD == IE_THREAD_SEQ + std::sort(begin, end, comparator); +#endif +} + template R parallel_sum(const T0 &D0, const R &input, const F &func) { #if (IE_THREAD == IE_THREAD_TBB || IE_THREAD == IE_THREAD_TBB_AUTO) diff --git a/inference-engine/include/ie_parameter.hpp b/inference-engine/include/ie_parameter.hpp index 9114118d86acea..67a044c51f9f21 100644 --- a/inference-engine/include/ie_parameter.hpp +++ b/inference-engine/include/ie_parameter.hpp @@ -35,7 +35,9 @@ class Parameter { * @brief Move constructor * @param parameter Parameter object */ - Parameter(Parameter &¶meter) noexcept: ptr(std::move(parameter.ptr)) {} + Parameter(Parameter &¶meter) noexcept { + std::swap(ptr, parameter.ptr); + } /** * @brief Copy constructor @@ -233,11 +235,11 @@ class Parameter { } T& get() & { - return std::get<0>(*this); + return std::get<0>(*static_cast*>(this)); } const T& get() const & { - return std::get<0>(*this); + return std::get<0>(*static_cast*>(this)); } template diff --git a/inference-engine/include/ie_plugin.hpp b/inference-engine/include/ie_plugin.hpp index 9229a74f5b040e..6caf1cf4f23a5f 100644 --- a/inference-engine/include/ie_plugin.hpp +++ b/inference-engine/include/ie_plugin.hpp @@ -21,6 +21,10 @@ #include #include +/** + * @def INFERENCE_PLUGIN_API(type) + * @brief Defines Inference Engine Plugin API method + */ #if defined(_WIN32) #ifdef IMPLEMENT_INFERENCE_ENGINE_PLUGIN @@ -82,12 +86,14 @@ struct INFERENCE_ENGINE_API_CLASS(QueryNetworkResult) { /** * @brief A copy assignment operator * @param q A value to copy from + * @return A copied object */ const QueryNetworkResult & operator= (const QueryNetworkResult & q); /** * @brief A move assignment operator * @param q A value to move from + * @return A moved object */ QueryNetworkResult & operator= (QueryNetworkResult && q); @@ -220,7 +226,8 @@ class IInferencePlugin : public details::IRelease { * @param res Reference to query network result */ INFERENCE_ENGINE_DEPRECATED - virtual void QueryNetwork(const ICNNNetwork& /*network*/, QueryNetworkResult& res) const noexcept { + virtual void QueryNetwork(const ICNNNetwork& network, QueryNetworkResult& res) const noexcept { + (void)network; res.rc = InferenceEngine::NOT_IMPLEMENTED; } @@ -230,8 +237,10 @@ class IInferencePlugin : public details::IRelease { * @param config Map of pairs: (config parameter name, config parameter value) * @param res Reference to query network result */ - virtual void QueryNetwork(const ICNNNetwork& /*network*/, - const std::map &/*config*/, QueryNetworkResult& res) const noexcept { + virtual void QueryNetwork(const ICNNNetwork& network, + const std::map & config, QueryNetworkResult& res) const noexcept { + (void)network; + (void)config; res.rc = InferenceEngine::NOT_IMPLEMENTED; } }; diff --git a/inference-engine/include/ie_plugin_config.hpp b/inference-engine/include/ie_plugin_config.hpp index a3764e8216f393..2d14316e0bdb88 100644 --- a/inference-engine/include/ie_plugin_config.hpp +++ b/inference-engine/include/ie_plugin_config.hpp @@ -24,10 +24,15 @@ namespace Metrics { #endif /** -* @brief shortcut for defining common Inference Engine metrics -*/ - + * @def METRIC_KEY(name) + * @brief shortcut for defining common Inference Engine metrics + */ #define METRIC_KEY(name) InferenceEngine::Metrics::METRIC_##name + +/** + * @def EXEC_NETWORK_METRIC_KEY(name) + * @brief shortcut for defining common Inference Engine ExecutableNetwork metrics + */ #define EXEC_NETWORK_METRIC_KEY(name) METRIC_KEY(name) #define DECLARE_METRIC_KEY(name, ...) \ @@ -37,8 +42,9 @@ namespace Metrics { #define DECLARE_EXEC_NETWORK_METRIC_KEY(name, ...) DECLARE_METRIC_KEY(name, __VA_ARGS__) /** -* @brief shortcut for defining metric values -*/ + * @def METRIC_VALUE(name) + * @brief shortcut for defining metric values + */ #define METRIC_VALUE(name) InferenceEngine::Metrics::name #define DECLARE_METRIC_VALUE(name) static constexpr auto name = #name @@ -141,15 +147,17 @@ DECLARE_EXEC_NETWORK_METRIC_KEY(OPTIMAL_NUMBER_OF_INFER_REQUESTS, unsigned int); namespace PluginConfigParams { /** -* @brief shortcut for defining configuration keys -*/ + * @def CONFIG_KEY(name) + * @brief shortcut for defining configuration keys + */ #define CONFIG_KEY(name) InferenceEngine::PluginConfigParams::_CONFIG_KEY(name) #define _CONFIG_KEY(name) KEY_##name #define DECLARE_CONFIG_KEY(name) static constexpr auto _CONFIG_KEY(name) = #name /** -* @brief shortcut for defining configuration values -*/ + * @def CONFIG_VALUE(name) + * @brief shortcut for defining configuration values + */ #define CONFIG_VALUE(name) InferenceEngine::PluginConfigParams::name #define DECLARE_CONFIG_VALUE(name) static constexpr auto name = #name diff --git a/inference-engine/include/ie_plugin_dispatcher.hpp b/inference-engine/include/ie_plugin_dispatcher.hpp index 41b4e41cbb76ba..67730fc9636c2d 100644 --- a/inference-engine/include/ie_plugin_dispatcher.hpp +++ b/inference-engine/include/ie_plugin_dispatcher.hpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace InferenceEngine { /** @@ -35,6 +36,7 @@ class INFERENCE_ENGINE_API_CLASS(PluginDispatcher) { /** * @deprecated Use InferenceEngine::Core to work with devices by name * @brief Loads a plugin from directories that is suitable for the device string + * @param deviceName A string value representing target device * @return A pointer to the plugin */ INFERENCE_ENGINE_DEPRECATED @@ -43,6 +45,7 @@ class INFERENCE_ENGINE_API_CLASS(PluginDispatcher) { /** * @deprecated Use InferenceEngine::Core to work with devices by name * @brief Loads a plugin from directories that is suitable for the device + * @param device An instance of InferenceEngine::TargetDevice * @return A pointer to the plugin */ INFERENCE_ENGINE_DEPRECATED diff --git a/inference-engine/include/ie_precision.hpp b/inference-engine/include/ie_precision.hpp index c4d63a59563a58..cbfc19b0ca9cd6 100644 --- a/inference-engine/include/ie_precision.hpp +++ b/inference-engine/include/ie_precision.hpp @@ -59,7 +59,8 @@ class Precision { /** * @brief Custom precision constructor - * @param byteSize size of elements + * + * @param bitsSize size of elements * @param name optional name string, used in serialisation */ explicit Precision(size_t bitsSize, const char * name = nullptr) { @@ -179,8 +180,9 @@ class Precision { } /** - * @brief Returns size in bytes of single element of that precision - * @deprecated : size of precision will be reported in bits in future releases + * @brief Returns size of single element of that precision in bits + * + * @returns Number of bits per element */ size_t size() const { if (precisionInfo.bitsSize == 0) { @@ -195,9 +197,21 @@ class Precision { } protected: + /** + * @brief Returns PrecisionInfo by its name + * + * @param name Name of precision + */ template static PrecisionInfo makePrecisionInfo(const char * name); + /** + * @brief Compare two c-strings + * + * @param l Const pointer to first string + * @param r Const pointer to another string + * @returns True if strings are the same + */ static bool areSameStrings(const char *l, const char *r) noexcept { if (l == r) return true; @@ -211,6 +225,9 @@ class Precision { return *l == *r; } + /** + * @brief Return PrecisionInfo + */ static PrecisionInfo getPrecisionInfo(ePrecision v) { #define CASE(x) case x: return makePrecisionInfo(#x); switch (v) { @@ -334,6 +351,13 @@ inline std::ostream & operator << (std::ostream &out, const InferenceEngine::Pre return out << Precision(p).name(); } +inline constexpr uint32_t getPrecisionMask(InferenceEngine::Precision::ePrecision precision1, + InferenceEngine::Precision::ePrecision precision2, + InferenceEngine::Precision::ePrecision precision3 = InferenceEngine::Precision::MIXED, + InferenceEngine::Precision::ePrecision precision4 = InferenceEngine::Precision::MIXED) { + return (precision1) | (precision2 << 8) | (precision3 << 16) | (precision4 << 24); +} + /** @endcond */ } // namespace InferenceEngine diff --git a/inference-engine/include/ie_primitive_info.hpp b/inference-engine/include/ie_primitive_info.hpp index 31afb2007f9297..cf745f39222e28 100644 --- a/inference-engine/include/ie_primitive_info.hpp +++ b/inference-engine/include/ie_primitive_info.hpp @@ -17,17 +17,44 @@ namespace InferenceEngine { +/** +* @brief Structure with information about Primitive +*/ struct PrimitiveInfo { + /** + * @brief A shared pointer to PrimitiveInfo object + */ using Ptr = std::shared_ptr; - std::string sId; // some internal id, could be used as a name - std::string sType; // implementation type of this kernel - int iPreAllocatedMemory; // mainly the allocation of the output tensor + /** + * @brief Some internal id, could be used as a name + */ + std::string sId; + + /** + * @brief Implementation type of this kernel + */ + std::string sType; + /** + * @brief Mainly the allocation of the output tensor + */ + int iPreAllocatedMemory; + + /** + * @brief Vector of TensorInfo objects that are related to input tensors + */ std::vector inputs; + + /** + * @brief Vector of TensorInfo object that are related to outputs tensors + */ std::vector outputs; - std::map extraInfo; // any other important textual information user might find interesting about this kernel + /** + * @brief Any other important textual information user might find interesting about this kernel + */ + std::map extraInfo; }; } // namespace InferenceEngine diff --git a/inference-engine/include/ie_tensor_info.hpp b/inference-engine/include/ie_tensor_info.hpp index ccbf3e852df0f0..69d092ab3eaa4b 100644 --- a/inference-engine/include/ie_tensor_info.hpp +++ b/inference-engine/include/ie_tensor_info.hpp @@ -15,12 +15,22 @@ namespace InferenceEngine { +/** +* @struct TensorInfo +* @brief This structure describes tensor information +*/ struct TensorInfo { + /** + * @brief A shared pointer to the TensorInfo object + */ using Ptr = std::shared_ptr; - // memory layout BFYX, BXYF (enum) - // size - // precision + /** + * @brief A map of extra info: + * - memory layout BFYX, BXYF (enum) + * - size + * - precision + */ std::map extraInfo; }; diff --git a/inference-engine/include/multi-device/multi_device_config.hpp b/inference-engine/include/multi-device/multi_device_config.hpp new file mode 100644 index 00000000000000..a5f037a53d9e9d --- /dev/null +++ b/inference-engine/include/multi-device/multi_device_config.hpp @@ -0,0 +1,36 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +/** + * @brief A header that defines advanced related properties for Multi_Device plugin. + * These properties should be used in SetConfig() and LoadNetwork() methods + * + * @file multi_device_config.hpp + */ + +#pragma once + +#include +#include "ie_plugin_config.hpp" + +namespace InferenceEngine { + +namespace MultiDeviceConfigParams { + +/** + * @def MULTI_CONFIG_KEY(name) + * @brief A macro which provides a MULTI-mangled name for configuration key with name `name` + */ +#define MULTI_CONFIG_KEY(name) InferenceEngine::MultiDeviceConfigParams::_CONFIG_KEY(MULTI_##name) + +#define DECLARE_MULTI_CONFIG_KEY(name) DECLARE_CONFIG_KEY(MULTI_##name) +#define DECLARE_MULTI_CONFIG_VALUE(name) DECLARE_CONFIG_VALUE(MULTI_##name) + +/** + * @brief Device Priorities config option, with comma-separated devices listed in the desired priority + */ +DECLARE_MULTI_CONFIG_KEY(DEVICE_PRIORITIES); + +} // namespace MultiDeviceConfigParams +} // namespace InferenceEngine diff --git a/inference-engine/include/vpu/hddl_plugin_config.hpp b/inference-engine/include/vpu/hddl_plugin_config.hpp new file mode 100644 index 00000000000000..d2a87c12c1bb16 --- /dev/null +++ b/inference-engine/include/vpu/hddl_plugin_config.hpp @@ -0,0 +1,184 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +/** + * @brief A header that defines advanced related properties for VPU plugins. + * These properties should be used in SetConfig() and LoadNetwork() methods of plugins + * + * @file vpu_plugin_config.hpp + */ + +#pragma once + +#include +#include + +#include "ie_plugin_config.hpp" +#include "ie_api.h" + +// +// Options +// + +#define VPU_HDDL_CONFIG_KEY(name) InferenceEngine::VPUConfigParams::_CONFIG_KEY(VPU_HDDL_##name) +#define VPU_HDDL_CONFIG_VALUE(name) InferenceEngine::VPUConfigParams::VPU_HDDL_##name + +#define DECLARE_VPU_HDDL_CONFIG_KEY(name) DECLARE_CONFIG_KEY(VPU_HDDL_##name) +#define DECLARE_VPU_HDDL_CONFIG_VALUE(name) DECLARE_CONFIG_VALUE(VPU_HDDL_##name) + +// +// Metrics +// + +#define VPU_HDDL_METRIC(name) METRIC_KEY(VPU_HDDL_##name) +#define DECLARE_VPU_HDDL_METRIC(name, ...) DECLARE_METRIC_KEY(VPU_HDDL_##name, __VA_ARGS__) + +namespace InferenceEngine { + +namespace Metrics { + +/** +* @brief Metric to get a int of the device number, String value is METRIC_VPU_HDDL_DEVICE_NUM +*/ +DECLARE_VPU_HDDL_METRIC(DEVICE_NUM, int); + +/** +* @brief Metric to get a std::vector of device names, String value is METRIC_VPU_HDDL_DEVICE_NAME +*/ +DECLARE_VPU_HDDL_METRIC(DEVICE_NAME, std::vector); + +/** +* @brief Metric to get a std::vector of device models, String value is METRIC_VPU_HDDL_DEVICE_MODEL +*/ +DECLARE_VPU_HDDL_METRIC(DEVICE_MODEL, std::vector); + +/** +* @brief Metric to get a std::vector of device thermal, String value is METRIC_VPU_HDDL_DEVICE_THERMAL +*/ +DECLARE_VPU_HDDL_METRIC(DEVICE_THERMAL, std::vector); + +/** +* @brief Metric to get a std::vector of device ids, String value is METRIC_VPU_HDDL_DEVICE_ID +*/ +DECLARE_VPU_HDDL_METRIC(DEVICE_ID, std::vector); + +/** +* @brief Metric to get a std::vector of device subclasses, String value is METRIC_VPU_HDDL_DEVICE_SUBCLASS +*/ +DECLARE_VPU_HDDL_METRIC(DEVICE_SUBCLASS, std::vector); + +/** +* @brief Metric to get a std::vector of device total memory, String value is METRIC_VPU_HDDL_MEMORY_TOTAL +*/ +DECLARE_VPU_HDDL_METRIC(DEVICE_MEMORY_TOTAL, std::vector); + +/** +* @brief Metric to get a std::vector of device used memory, String value is METRIC_VPU_HDDL_DEVICE_MEMORY_USED +*/ +DECLARE_VPU_HDDL_METRIC(DEVICE_MEMORY_USED, std::vector); + +/** +* @brief Metric to get a std::vector of device utilization, String value is METRIC_VPU_HDDL_DEVICE_UTILIZATION +*/ +DECLARE_VPU_HDDL_METRIC(DEVICE_UTILIZATION, std::vector); + +/** +* @brief Metric to get a std::vector of stream ids, String value is METRIC_VPU_HDDL_DEVICE_STREAM_ID +*/ +DECLARE_VPU_HDDL_METRIC(STREAM_ID, std::vector); + + +/** +* @brief Metric to get a std::vector of device tags, String value is METRIC_VPU_HDDL_DEVICE_TAG +*/ +DECLARE_VPU_HDDL_METRIC(DEVICE_TAG, std::vector); + +} // namespace Metrics + +namespace VPUConfigParams { + +/** + * @brief [Only for HDDLPlugin] + * Type: Arbitrary non-empty string. If empty (""), equals no set, default: ""; + * This option allows to specify the number of MYX devices used for inference a specific Executable network. + * Note: Only one network would be allocated to one device. + * The number of devices for the tag is specified in the hddl_service.config file. + * Example: + * "service_settings": + * { + * "graph_tag_map": + * { + * "tagA":3 + * } + * } + * It means that an executable network marked with tagA will be executed on 3 devices + */ +DECLARE_VPU_HDDL_CONFIG_KEY(GRAPH_TAG); + +/** + * @brief [Only for HDDLPlugin] + * Type: Arbitrary non-empty string. If empty (""), equals no set, default: ""; + * This config makes the executable networks to be allocated on one certain device (instead of multiple devices). + * And all inference through this executable network, will be done on this device. + * Note: Only one network would be allocated to one device. + * The number of devices which will be used for stream-affinity must be specified in hddl_service.config file. + * Example: + * "service_settings": + * { + * "stream_device_number":5 + * } + * It means that 5 device will be used for stream-affinity + */ +DECLARE_VPU_HDDL_CONFIG_KEY(STREAM_ID); + +/** + * @brief [Only for HDDLPlugin] + * Type: Arbitrary non-empty string. If empty (""), equals no set, default: ""; + * This config allows user to control device flexibly. This config gives a "tag" for a certain device while + * allocating a network to it. Afterward, user can allocating/deallocating networks to this device with this "tag". + * Devices used for such use case is controlled by a so-called "Bypass Scheduler" in HDDL backend, and the number + * of such device need to be specified in hddl_service.config file. + * Example: + * "service_settings": + * { + * "bypass_device_number": 5 + * } + * It means that 5 device will be used for Bypass scheduler. + */ +DECLARE_VPU_HDDL_CONFIG_KEY(DEVICE_TAG); + +/** + * @brief [Only for HDDLPlugin] + * Type: "YES/NO", default is "NO". + * This config is a sub-config of DEVICE_TAG, and only available when "DEVICE_TAG" is set. After a user load a + * network, the user got a handle for the network. + * If "YES", the network allocated is bind to the device (with the specified "DEVICE_TAG"), which means all afterwards + * inference through this network handle will be executed on this device only. + * If "NO", the network allocated is not bind to the device (with the specified "DEVICE_TAG"). If the same network + * is allocated on multiple other devices (also set BIND_DEVICE to "False"), then inference through any handle of these + * networks may be executed on any of these devices those have the network loaded. + */ +DECLARE_VPU_HDDL_CONFIG_KEY(BIND_DEVICE); + +/** + * @brief [Only for HDDLPlugin] + * Type: A signed int wrapped in a string, default is "0". + * This config is a sub-config of DEVICE_TAG, and only available when "DEVICE_TAG" is set and "BIND_DEVICE" is "False". + * When there are multiple devices running a certain network (a same network running on multiple devices in Bypass Scheduler), + * the device with a larger number has a higher priority, and more inference tasks will be fed to it with priority. + */ +DECLARE_VPU_HDDL_CONFIG_KEY(RUNTIME_PRIORITY); + +/** + * @brief [Only for HDDLPlugin] + * Type: "YES/NO", default is "NO". + * SGAD is short for "Single Graph All Device". With this scheduler, once application allocates 1 network, all devices + * (managed by SGAD scheduler) will be loaded with this graph. The number of network that can be loaded to one device + * can exceed one. Once application deallocates 1 network from device, all devices will unload the network from them. + */ +DECLARE_VPU_HDDL_CONFIG_KEY(USE_SGAD); + +} // namespace VPUConfigParams + +} // namespace InferenceEngine diff --git a/inference-engine/include/vpu/vpu_plugin_config.hpp b/inference-engine/include/vpu/vpu_plugin_config.hpp index 69d04f1ed76977..5462acf194073a 100644 --- a/inference-engine/include/vpu/vpu_plugin_config.hpp +++ b/inference-engine/include/vpu/vpu_plugin_config.hpp @@ -15,6 +15,7 @@ #include "ie_plugin_config.hpp" #include "myriad_plugin_config.hpp" +#include "hddl_plugin_config.hpp" #include "ie_api.h" // @@ -105,6 +106,8 @@ DECLARE_VPU_CONFIG_KEY(COMPUTE_LAYOUT); DECLARE_VPU_CONFIG_VALUE(AUTO); DECLARE_VPU_CONFIG_VALUE(NCHW); DECLARE_VPU_CONFIG_VALUE(NHWC); +DECLARE_VPU_CONFIG_VALUE(NCDHW); +DECLARE_VPU_CONFIG_VALUE(NDHWC); /** * @brief This option allows to pass custom layers binding xml. diff --git a/inference-engine/samples/CMakeLists.txt b/inference-engine/samples/CMakeLists.txt index d354f647cecb44..d3a094c8beced2 100644 --- a/inference-engine/samples/CMakeLists.txt +++ b/inference-engine/samples/CMakeLists.txt @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # -cmake_minimum_required (VERSION 2.8.11) +cmake_minimum_required (VERSION 2.8.12) project(Samples) @@ -150,8 +150,6 @@ macro(ie_add_sample) if(NOT OpenCV_FOUND) message(WARNING "OPENCV is disabled or not found, " ${IE_SAMPLE_NAME} " skipped") return() - else() - add_definitions(-DUSE_OPENCV) endif() endif() @@ -164,6 +162,9 @@ macro(ie_add_sample) # Create executable file from sources add_executable(${IE_SAMPLE_NAME} ${IE_SAMPLE_SOURCES} ${IE_SAMPLES_HEADERS}) + if(IE_SAMPLE_OPENCV_DEPENDENCIES) + target_compile_definitions(${IE_SAMPLE_NAME} PRIVATE USE_OPENCV) + endif() if(WIN32) set_target_properties(${IE_SAMPLE_NAME} PROPERTIES COMPILE_PDB_NAME ${IE_SAMPLE_NAME}) @@ -176,7 +177,6 @@ macro(ie_add_sample) target_link_libraries(${IE_SAMPLE_NAME} PRIVATE ${OpenCV_LIBRARIES} ${InferenceEngine_LIBRARIES} ${IE_SAMPLE_DEPENDENCIES} IE::ie_cpu_extension gflags) - if(UNIX) target_link_libraries(${IE_SAMPLE_NAME} PRIVATE pthread) endif() @@ -195,12 +195,12 @@ endmacro() # use this flag if you need to throw custom message in case if the IE package is not found. if (IE_NOT_FOUND_MESSAGE) - find_package(InferenceEngine 2.0 QUIET) + find_package(InferenceEngine 2.1 QUIET) if (NOT(InferenceEngine_FOUND)) message(FATAL_ERROR ${IE_NOT_FOUND_MESSAGE}) endif() else() - find_package(InferenceEngine 2.0 REQUIRED) + find_package(InferenceEngine 2.1 REQUIRED) endif() # collect all samples subdirectories diff --git a/inference-engine/samples/benchmark_app/README.md b/inference-engine/samples/benchmark_app/README.md index b1bde478a55725..9e4e9d508f88ec 100644 --- a/inference-engine/samples/benchmark_app/README.md +++ b/inference-engine/samples/benchmark_app/README.md @@ -1,21 +1,18 @@ -# Benchmark C++ Application +# Benchmark C++ Tool -This topic demonstrates how to use the Benchmark Application to estimate deep learning inference performance on -supported devices. Performance can be measured for two inference modes: synchronous (latency-oriented) and asynchronous (throughput-oriented). +This topic demonstrates how to use the Benchmark C++ Tool to estimate deep learning inference performance on supported devices. Performance can be measured for two inference modes: synchronous (latency-oriented) and asynchronous (throughput-oriented). -> **NOTE:** This topic describes usage of C++ implementation of the Benchmark Application. For the Python* implementation, refer to [Benchmark Application (Python*)](./inference-engine/ie_bridges/python/sample/benchmark_app/README.md). +> **NOTE:** This topic describes usage of C++ implementation of the Benchmark Tool. For the Python* implementation, refer to [Benchmark Python* Tool](./inference-engine/tools/benchmark_tool/README.md). ## How It Works -Upon start-up, the application reads command-line parameters and loads a network and images/binary files to the Inference Engine -plugin, which is chosen depending on a specified device. The number of infer requests and execution approach depend -on the mode defined with the `-api` command-line parameter. +Upon start-up, the application reads command-line parameters and loads a network and images/binary files to the Inference Engine plugin, which is chosen depending on a specified device. The number of infer requests and execution approach depend on the mode defined with the `-api` command-line parameter. -> **NOTE**: By default, Inference Engine samples and demos expect input with BGR channels order. If you trained your model to work with RGB order, you need to manually rearrange the default channels order in the sample or demo application or reconvert your model using the Model Optimizer tool with `--reverse_input_channels` argument specified. For more information about the argument, refer to **When to Reverse Input Channels** section of [Converting a Model Using General Conversion Parameters](./docs/MO_DG/prepare_model/convert_model/Converting_Model_General.md). +> **NOTE**: By default, Inference Engine samples, tools and demos expect input with BGR channels order. If you trained your model to work with RGB order, you need to manually rearrange the default channels order in the sample or demo application or reconvert your model using the Model Optimizer tool with `--reverse_input_channels` argument specified. For more information about the argument, refer to **When to Reverse Input Channels** section of [Converting a Model Using General Conversion Parameters](./docs/MO_DG/prepare_model/convert_model/Converting_Model_General.md). If you run the application in the synchronous mode, it creates one infer request and executes the `Infer` method. -If you run the application in the asynchronous mode, it creates as many infer requests as specified in the `-nireq` command-line parameter and executes the `StartAsync` method for each of them. If `-nireq` is not set, the demo will use the default value for specified device. +If you run the application in the asynchronous mode, it creates as many infer requests as specified in the `-nireq` command-line parameter and executes the `StartAsync` method for each of them. If `-nireq` is not set, the application will use the default value for specified device. A number of execution steps is defined by one of the following parameters: * Number of iterations specified with the `-niter` command-line argument @@ -45,14 +42,19 @@ The application also saves executable graph information serialized to a XML file `-exec_graph_path` parameter. -## Running +## Run the Tool Notice that the benchmark_app usually produces optimal performance for any device out of the box. -**So in most cases you don't need to play the app options explicitly and the plain device name is enough**, e.g.: -``` -$benchmark_app -m -i -d CPU +**So in most cases you don't need to play the app options explicitly and the plain device name is enough**, for example, for CPU: +```sh +./benchmark_app -m -i -d CPU ``` -As explained in the [Introduction to Performance Topics](./docs/IE_DG/Intro_to_Performance.md) section, it is preferable to use the FP16 IR for the model. + +But it is still may be non-optimal for some cases, especially for very small networks. More details can read in [Introduction to Performance Topics](./docs/IE_DG/Intro_to_Performance.md). + +As explained in the [Introduction to Performance Topics](./docs/IE_DG/Intro_to_Performance.md) section, for all devices, including new [MULTI device](./docs/IE_DG/supported_plugins/MULTI.md) it is preferable to use the FP16 IR for the model. +Also if latency of the CPU inference on the multi-socket machines is of concern, please refer to the same +[Introduction to Performance Topics](./docs/IE_DG/Intro_to_Performance.md) document. Running the application with the `-h` option yields the following usage message: ``` @@ -70,6 +72,7 @@ Options: -m "" Required. Path to an .xml file with a trained model. -d "" Optional. Specify a target device to infer on (the list of available devices is shown below). Default value is CPU. Use "-d HETERO:" format to specify HETERO plugin. + Use "-d MULTI:" format to specify MULTI plugin. The application looks for a suitable plugin for the specified device. -l "" Required for CPU custom layers. Absolute path to a shared library with the kernels implementations. Or @@ -84,8 +87,11 @@ Options: CPU-specific performance options: -nstreams "" Optional. Number of streams to use for inference on the CPU or/and GPU in throughput mode - (for HETERO device case use format :,: or just ). - -nthreads "" Optional. Number of threads to use for inference on the CPU (including HETERO case). + (for HETERO and MULTI device cases use format :,: or just ). + Default value is determined automatically for a device. + Please note that although the automatic selection usually provides a reasonable performance, + it still may be non-optimal for some cases, especially for very small networks. + -nthreads "" Optional. Number of threads to use for inference on the CPU (including HETERO and MULTI cases). -pin "YES"/"NO" Optional. Enable ("YES" is default value) or disable ("NO") CPU threads pinning for CPU-involved inference. Statistics dumping options: @@ -102,48 +108,74 @@ If a model has only image input(s), please a provide folder with images or a pat If a model has some specific input(s) (not images), please prepare a binary file(s), which is filled with data of appropriate precision and provide a path to them as input. If a model has mixed input types, input folder should contain all required files. Image inputs are filled with image files one by one. Binary inputs are filled with binary inputs one by one. -To download the pre-trained models, use the OpenVINO [Model Downloader](https://github.com/opencv/open_model_zoo/tree/2018/model_downloader) or go to [https://download.01.org/opencv/](https://download.01.org/opencv/). - -> **NOTE**: Before running the demo with a trained model, make sure the model is converted to the Inference Engine format (\*.xml + \*.bin) using the [Model Optimizer tool](./docs/MO_DG/Deep_Learning_Model_Optimizer_DevGuide.md). - -For example, to perform inference on CPU in the synchronous mode and get estimated performance metrics for AlexNet model, -run the following command: - -```sh -./benchmark_app -i /inputImage.bmp -m /alexnet_fp32.xml -d CPU -api sync -``` - -For the asynchronous mode: -```sh -./benchmark_app -i /inputImage.bmp -m /alexnet_fp32.xml -d CPU -api async -``` - -## Demo Output +To run the tool, you can use public or Intel's pre-trained models. To download the models, use the OpenVINO [Model Downloader](./tools/downloader/README.md) or go to [https://download.01.org/opencv/](https://download.01.org/opencv/). + +> **NOTE**: Before running the tool with a trained model, make sure the model is converted to the Inference Engine format (\*.xml + \*.bin) using the [Model Optimizer tool](./docs/MO_DG/Deep_Learning_Model_Optimizer_DevGuide.md). + +## Examples of Running the Tool + +This section provides step-by-step instructions on how to run the Benchmark Tool with the `googlenet-v1` public model on CPU or FPGA devices. As an input, the `car.png` file from the `/deployment_tools/demo/` directory is used. + +> **NOTE:** The Internet access is required to execute the following steps successfully. If you have access to the Internet through the proxy server only, please make sure that it is configured in your OS environment. + +1. Download the model. Go to the the Model Downloader directory and run the `downloader.py` script with specifying the model name and directory to download the model to: + ```sh + cd /deployment_tools/open_model_zoo/tools/downloader + ``` + ```sh + python3 downloader.py --name googlenet-v1 -o + ``` +2. Convert the model to the Inference Engine IR format. Go to the Model Optimizer directory and run the `mo.py` script with specifying the path to the model, model format (which must be FP32 for CPU and FPG) and output directory to generate the IR files: + ```sh + cd /deployment_tools/model_optimizer + ``` + ```sh + python3 mo.py --input_model /public/googlenet-v1/googlenet-v1.caffemodel --data_type FP32 --output_dir + ``` +3. Run the tool with specifying the `/deployment_tools/demo/car.png` file as an input image, the IR of the `googlenet-v1` model and a device to perform inference on. The following commands demonstrate running the Benchmark Tool in the asynchronous mode on CPU and FPGA devices: + + * On CPU: + ```sh + ./benchmark_app -m /googlenet-v1.xml -d CPU -api async -i /deployment_tools/demo/car.png --progress true + ``` + * On FPGA: + ```sh + ./benchmark_app -m /googlenet-v1.xml -d HETERO:FPGA,CPU -api async -i /deployment_tools/demo/car.png --progress true + ``` The application outputs the number of executed iterations, total duration of execution, latency and throughput. -Additionally, if you set the `-report_type` parameter, the application outputs statistics report. -If you set the `-pc` parameter, the application outputs performance counters. -If you set `-exec_graph_path`, the application reports executable graph information serialized. +Additionally, if you set the `-report_type` parameter, the application outputs statistics report. If you set the `-pc` parameter, the application outputs performance counters. If you set `-exec_graph_path`, the application reports executable graph information serialized. All measurements including per-layer PM counters are reported in milliseconds. -``` -[Step 8/9] Measuring performance (Start inference asyncronously, 60000 ms duration, 4 inference requests in parallel using 4 streams) -Progress: [....................] 100.00% done +Below are fragments of sample output for CPU and FPGA devices: -[Step 9/9] Dumping statistics report -[ INFO ] Statistics collecting was not requested. No reports are dumped. -Progress: [....................] 100.00% done +* For CPU: + ``` + [Step 8/9] Measuring performance (Start inference asyncronously, 60000 ms duration, 4 inference requests in parallel using 4 streams) + Progress: [....................] 100.00% done -Count: 4612 iterations -Duration: 60110.04 ms -Latency: 50.99 ms -Throughput: 76.73 FPS + [Step 9/9] Dumping statistics report + [ INFO ] Statistics collecting was not requested. No reports are dumped. + Progress: [....................] 100.00% done -``` + Count: 4612 iterations + Duration: 60110.04 ms + Latency: 50.99 ms + Throughput: 76.73 FPS + ``` -All measurements including per-layer PM counters are reported in milliseconds. +* For FPGA: + ``` + [Step 10/11] Measuring performance (Start inference asynchronously, 5 inference requests using 4 streams for CPU, limits: 120000 ms duration) + Progress: [....................] 100% done + [Step 11/11] Dumping statistics report + Count: 102515 iterations + Duration: 120007.38 ms + Latency: 5.84 ms + Throughput: 854.24 FP + ``` ## See Also * [Using Inference Engine Samples](./docs/IE_DG/Samples_Overview.md) * [Model Optimizer](./docs/MO_DG/Deep_Learning_Model_Optimizer_DevGuide.md) -* [Model Downloader](https://github.com/opencv/open_model_zoo/tree/2018/model_downloader) +* [Model Downloader](./tools/downloader/README.md) \ No newline at end of file diff --git a/inference-engine/samples/benchmark_app/benchmark_app.hpp b/inference-engine/samples/benchmark_app/benchmark_app.hpp index 6b6991fcfef966..c9e2da221a4243 100644 --- a/inference-engine/samples/benchmark_app/benchmark_app.hpp +++ b/inference-engine/samples/benchmark_app/benchmark_app.hpp @@ -23,7 +23,9 @@ static const char api_message[] = "Optional. Enable Sync/Async API. Default valu /// @brief message for assigning cnn calculation to device static const char target_device_message[] = "Optional. Specify a target device to infer on (the list of available devices is shown below). " \ -"Default value is CPU. Use \"-d HETERO:\" format to specify HETERO plugin. "; +"Default value is CPU. Use \"-d HETERO:\" format to specify HETERO plugin. " \ +"Use \"-d MULTI:\" format to specify MULTI plugin. " \ +"The application looks for a suitable plugin for the specified device."; /// @brief message for iterations count static const char iterations_count_message[] = "Optional. Number of iterations. " \ @@ -37,11 +39,14 @@ static const char execution_time_message[] = "Optional. Time in seconds to execu /// @brief message for #threads for CPU inference static const char infer_num_threads_message[] = "Optional. Number of threads to use for inference on the CPU " - "(including HETERO case)."; + "(including HETERO and MULTI cases)."; /// @brief message for #streams for CPU inference static const char infer_num_streams_message[] = "Optional. Number of streams to use for inference on the CPU or/and GPU in throughput mode " - "(for HETERO device case use format :,: or just )"; + "(for HETERO and MULTI device cases use format :,: or just ). " + "Default value is determined automatically for a device.Please note that although the automatic selection " + "usually provides a reasonable performance, it still may be non - optimal for some cases, especially for " + "very small networks. See sample's README for more details."; /// @brief message for user library argument static const char custom_cpu_library_message[] = "Required for CPU custom layers. Absolute path to a shared library with the kernels implementations."; diff --git a/inference-engine/samples/benchmark_app/main.cpp b/inference-engine/samples/benchmark_app/main.cpp index 892bd5daf5566d..ca71319fe2a5a1 100644 --- a/inference-engine/samples/benchmark_app/main.cpp +++ b/inference-engine/samples/benchmark_app/main.cpp @@ -62,6 +62,10 @@ bool ParseAndCheckCommandLine(int argc, char *argv[]) { throw std::logic_error(err); } + if ((FLAGS_report_type == averageCntReport) && ((FLAGS_d.find("MULTI") != std::string::npos))) { + throw std::logic_error("only " + std::string(detailedCntReport) + " report type is supported for MULTI device"); + } + return true; } @@ -89,10 +93,20 @@ static void next_step(const std::string additional_info = "") { << (additional_info.empty() ? "" : " (" + additional_info + ")") << std::endl; } +template +T getMedianValue(const std::vector &vec) { + std::vector sortedVec(vec); + std::sort(sortedVec.begin(), sortedVec.end()); + return (sortedVec.size() % 2 != 0) ? + sortedVec[sortedVec.size() / 2ULL] : + (sortedVec[sortedVec.size() / 2ULL] + sortedVec[sortedVec.size() / 2ULL - 1ULL]) / static_cast(2.0); +} + /** * @brief The entry point of the benchmark application */ int main(int argc, char *argv[]) { + std::shared_ptr statistics; try { // ----------------- 1. Parsing and validating input arguments ------------------------------------------------- next_step(); @@ -101,10 +115,30 @@ int main(int argc, char *argv[]) { return 0; } + if (!FLAGS_report_type.empty()) { + std::vector flags; + StatisticsReport::Parameters command_line_arguments; + gflags::GetAllFlags(&flags); + + for (auto &flag : flags) { + if (!flag.is_default) { + command_line_arguments.push_back({ flag.name, flag.current_value }); + } + } + statistics = std::make_shared(StatisticsReport::Config{FLAGS_report_type, FLAGS_report_folder}); + statistics->addParameters(StatisticsReport::Category::COMMAND_LINE_PARAMETERS, command_line_arguments); + } + /** This vector stores paths to the processed images **/ std::vector inputFiles; parseInputFilesArguments(inputFiles); + if (FLAGS_nstreams.empty()) { + slog::warn << "-nstreams default value is determined automatically for a device. " + "Although the automatic selection usually provides a reasonable performance," + "but it still may be non-optimal for some cases, for more information look at README." << slog::endl<< slog::endl; + } + // ----------------- 2. Loading the Inference Engine ----------------------------------------------------------- next_step(); @@ -141,9 +175,25 @@ int main(int argc, char *argv[]) { slog::info << "Loading network files" << slog::endl; CNNNetReader netBuilder; + auto startTime = Time::now(); netBuilder.ReadNetwork(FLAGS_m); const std::string binFileName = fileNameNoExt(FLAGS_m) + ".bin"; netBuilder.ReadWeights(binFileName); + auto float_to_string = [] (const float number) { + std::stringstream ss; + ss << std::fixed << std::setprecision(2) << number; + return ss.str(); + }; + auto get_total_ms_time = [ &startTime ] () { + return std::chrono::duration_cast(Time::now() - startTime).count() * 0.000001; + }; + auto duration_ms = float_to_string(get_total_ms_time()); + slog::info << "Read network took " << duration_ms << " ms" << slog::endl; + if (statistics) + statistics->addParameters(StatisticsReport::Category::EXECUTION_RESULTS, + { + {"read network time (ms)", duration_ms} + }); CNNNetwork cnnNetwork = netBuilder.getNetwork(); const InputsDataMap inputInfo(cnnNetwork.getInputsInfo()); @@ -180,8 +230,9 @@ int main(int argc, char *argv[]) { } const size_t batchSize = cnnNetwork.getBatchSize(); + const Precision precision = cnnNetwork.getPrecision(); slog::info << (FLAGS_b != 0 ? "Network batch size was changed to: " : "Network batch size: ") << batchSize << - ", precision: " << cnnNetwork.getPrecision() << slog::endl; + ", precision: " << precision << slog::endl; // ----------------- 5. Configuring input ---------------------------------------------------------------------- next_step(); @@ -198,7 +249,8 @@ int main(int argc, char *argv[]) { bool perf_counts = (FLAGS_report_type == detailedCntReport || FLAGS_report_type == averageCntReport || - FLAGS_pc); + FLAGS_pc || + !FLAGS_exec_graph_path.empty()); auto devices = parseDevices(device_name); std::map device_nstreams = parseValuePerDevice(devices, FLAGS_nstreams); @@ -208,8 +260,13 @@ int main(int argc, char *argv[]) { if (FLAGS_nthreads != 0) ie.SetConfig({{ CONFIG_KEY(CPU_THREADS_NUM), std::to_string(FLAGS_nthreads) }}, device); - // pin threads for CPU portion of inference - ie.SetConfig({{ CONFIG_KEY(CPU_BIND_THREAD), FLAGS_pin }}, device); + if ((device_name.find("MULTI") != std::string::npos) && + (device_name.find("GPU") != std::string::npos)) { + ie.SetConfig({{ CONFIG_KEY(CPU_BIND_THREAD), CONFIG_VALUE(NO) }}, device); + } else { + // pin threads for CPU portion of inference + ie.SetConfig({{ CONFIG_KEY(CPU_BIND_THREAD), FLAGS_pin }}, device); + } // for CPU execution, more throughput-oriented execution via streams if (FLAGS_api == "async") @@ -223,6 +280,13 @@ int main(int argc, char *argv[]) { (device_nstreams.count(device) > 0 ? std::to_string(device_nstreams.at(device)) : "GPU_THROUGHPUT_AUTO") }}, device); device_nstreams[device] = std::stoi(ie.GetConfig(device, CONFIG_KEY(GPU_THROUGHPUT_STREAMS)).as()); + + if ((device_name.find("MULTI") != std::string::npos) && + (device_name.find("CPU") != std::string::npos)) { + // multi-device execution with the CPU + GPU performs best with GPU trottling hint, + // which releases another CPU thread (that is otherwise used by the GPU driver for active polling) + ie.SetConfig({{ CLDNN_CONFIG_KEY(PLUGIN_THROTTLE), "1" }}, "GPU"); + } } else if (device == "MYRIAD") { ie.SetConfig({{ CONFIG_KEY(LOG_LEVEL), CONFIG_VALUE(LOG_NONE) }, { VPU_CONFIG_KEY(LOG_LEVEL), CONFIG_VALUE(LOG_WARNING) }}, device); @@ -234,7 +298,15 @@ int main(int argc, char *argv[]) { std::map config = {{ CONFIG_KEY(PERF_COUNT), perf_counts ? CONFIG_VALUE(YES) : CONFIG_VALUE(NO) }}; + startTime = Time::now(); ExecutableNetwork exeNetwork = ie.LoadNetwork(cnnNetwork, device_name, config); + duration_ms = float_to_string(get_total_ms_time()); + slog::info << "Load network took " << duration_ms << " ms" << slog::endl; + if (statistics) + statistics->addParameters(StatisticsReport::Category::EXECUTION_RESULTS, + { + {"load network time (ms)", duration_ms} + }); // ----------------- 8. Setting optimal runtime parameters ----------------------------------------------------- next_step(); @@ -274,6 +346,28 @@ int main(int argc, char *argv[]) { } uint64_t duration_nanoseconds = getDurationInNanoseconds(duration_seconds); + if (statistics) { + statistics->addParameters(StatisticsReport::Category::RUNTIME_CONFIG, + { + {"topology", cnnNetwork.getName()}, + {"target device", device_name}, + {"API", FLAGS_api}, + {"precision", std::string(precision.name())}, + {"batch size", std::to_string(batchSize)}, + {"number of iterations", std::to_string(niter)}, + {"number of parallel infer requests", std::to_string(nireq)}, + {"duration (ms)", std::to_string(getDurationInMilliseconds(duration_seconds))}, + }); + for (auto& nstreams : device_nstreams) { + std::stringstream ss; + ss << "number of " << nstreams.first << " streams"; + statistics->addParameters(StatisticsReport::Category::RUNTIME_CONFIG, + { + {ss.str(), std::to_string(nstreams.second)}, + }); + } + } + // ----------------- 9. Creating infer requests and filling input blobs ---------------------------------------- next_step(); @@ -333,7 +427,7 @@ int main(int argc, char *argv[]) { inferRequestsQueue.waitAll(); inferRequestsQueue.resetTimes(); - const auto startTime = Time::now(); + startTime = Time::now(); auto execTime = std::chrono::duration_cast(Time::now() - startTime).count(); /** Start inference & calculate performance **/ @@ -373,35 +467,34 @@ int main(int argc, char *argv[]) { // wait the latest inference executions inferRequestsQueue.waitAll(); - StatisticsReport statistics({ FLAGS_d, - FLAGS_api, - batchSize, - nireq, - niter, - getDurationInMilliseconds(duration_seconds), - FLAGS_nthreads, - device_nstreams, - FLAGS_pin, - FLAGS_report_type, - FLAGS_report_folder - }); - if (perf_counts) { - for (auto& request : inferRequestsQueue.requests) { - statistics.addPerfCounts(request->getPerformanceCounts()); + double latency = getMedianValue(inferRequestsQueue.getLatencies()); + double totalDuration = inferRequestsQueue.getDurationInMilliseconds(); + double fps = (FLAGS_api == "sync") ? batchSize * 1000.0 / latency : + batchSize * 1000.0 * iteration / totalDuration; + + if (statistics) { + statistics->addParameters(StatisticsReport::Category::EXECUTION_RESULTS, + { + {"total execution time (ms)", float_to_string(totalDuration)}, + {"total number of iterations", std::to_string(iteration)}, + }); + if (device_name.find("MULTI") == std::string::npos) { + statistics->addParameters(StatisticsReport::Category::EXECUTION_RESULTS, + { + {"latency (ms)", float_to_string(latency)}, + }); } + statistics->addParameters(StatisticsReport::Category::EXECUTION_RESULTS, + { + {"throughput", float_to_string(fps)} + }); } - statistics.addLatencies(inferRequestsQueue.getLatencies()); - double totalDuration = inferRequestsQueue.getDurationInMilliseconds(); - double fps = (FLAGS_api == "sync") ? batchSize * 1000.0 / statistics.getMedianLatency() : - batchSize * 1000.0 * iteration / totalDuration; progressBar.finish(); // ----------------- 11. Dumping statistics report ------------------------------------------------------------- next_step(); - statistics.dump(fps, iteration, totalDuration); - if (!FLAGS_exec_graph_path.empty()) { try { CNNNetwork execGraphInfo = exeNetwork.GetExecGraphInfo(); @@ -412,19 +505,40 @@ int main(int argc, char *argv[]) { } } - if (FLAGS_pc) { + if (perf_counts) { + std::vector> perfCounts; for (size_t ireq = 0; ireq < nireq; ireq++) { - slog::info << "Pefrormance counts for " << ireq << "-th infer request:" << slog::endl; - printPerformanceCounts(inferRequestsQueue.requests[ireq]->getPerformanceCounts(), std::cout, getFullDeviceName(ie, FLAGS_d), false); + auto reqPerfCounts = inferRequestsQueue.requests[ireq]->getPerformanceCounts(); + if (FLAGS_pc) { + slog::info << "Pefrormance counts for " << ireq << "-th infer request:" << slog::endl; + printPerformanceCounts(reqPerfCounts, std::cout, getFullDeviceName(ie, FLAGS_d), false); + } + perfCounts.push_back(reqPerfCounts); + } + if (statistics) { + statistics->dumpPerformanceCounters(perfCounts); } } + if (statistics) + statistics->dump(); + std::cout << "Count: " << iteration << " iterations" << std::endl; - std::cout << "Duration: " << totalDuration << " ms" << std::endl; - std::cout << "Latency: " << statistics.getMedianLatency() << " ms" << std::endl; - std::cout << "Throughput: " << fps << " FPS" << std::endl; + std::cout << "Duration: " << float_to_string(totalDuration) << " ms" << std::endl; + if (device_name.find("MULTI") == std::string::npos) + std::cout << "Latency: " << float_to_string(latency) << " ms" << std::endl; + std::cout << "Throughput: " << float_to_string(fps) << " FPS" << std::endl; } catch (const std::exception& ex) { slog::err << ex.what() << slog::endl; + + if (statistics) { + statistics->addParameters(StatisticsReport::Category::EXECUTION_RESULTS, + { + {"error", ex.what()}, + }); + statistics->dump(); + } + return 3; } diff --git a/inference-engine/samples/benchmark_app/statistics_report.cpp b/inference-engine/samples/benchmark_app/statistics_report.cpp index 821f4fe55e3911..2f8005dfced23f 100644 --- a/inference-engine/samples/benchmark_app/statistics_report.cpp +++ b/inference-engine/samples/benchmark_app/statistics_report.cpp @@ -10,215 +10,127 @@ #include "statistics_report.hpp" -void StatisticsReport::addPerfCounts(const std::map &pmStat) { - if (_config.report_type == averageCntReport || _config.report_type == detailedCntReport) { - // collect per-iteration statistics only in case of enabled median/detailed statistic collecting - _performanceCounters.push_back(pmStat); - } +void StatisticsReport::addParameters(const Category &category, const Parameters& parameters) { + if (_parameters.count(category) == 0) + _parameters[category] = parameters; + else + _parameters[category].insert(_parameters[category].end(), parameters.begin(), parameters.end()); } -void StatisticsReport::addLatencies(const std::vector &latencies) { - _latencies.insert(_latencies.end(), latencies.begin(), latencies.end()); -} +void StatisticsReport::dump() { + CsvDumper dumper(true, _config.report_folder + _separator + "benchmark_report.csv"); -void StatisticsReport::dump(const double &fps, const size_t &iteration_number, const double &totalExecTime) { - if (_config.report_type.empty()) { - slog::info << "Statistics collecting was not requested. No reports are dumped." << slog::endl; - return; - } + auto dump_parameters = [ &dumper ] (const Parameters ¶meters) { + for (auto& parameter : parameters) { + dumper << parameter.first << parameter.second; + dumper.endLine(); + } + }; + if (_parameters.count(Category::COMMAND_LINE_PARAMETERS)) { + dumper << "Command line parameters"; + dumper.endLine(); - std::string separator = -#if defined _WIN32 || defined __CYGWIN__ - # if defined UNICODE - L"\\"; - # else - "\\"; - # endif -#else - "/"; -#endif - if (_config.report_folder.empty()) - separator = ""; - - CsvDumper dumper(true, _config.report_folder + separator + "benchmark_" + _config.report_type + "_report.csv"); - - // resulting number of columns in csv file depends on the report_type. If it's noCntReport, then - // no PM data is collected and there are only 3 columns in the file (in configuration section). If it's - // averageCntReport then median PM values are collected per each layer and the number of columns is 6. - // Example from GPU: - // - // layer name;exec status;layer type;exec type;real time;cpu time; - // conv1;EXECUTED;Convolution;convolution_gpu_bfyx_gemm_like;615;3; - // Here, all the data are taken from InferenceEngine::InferenceEngineProfileInfo. - // - // In case of detailedCntReport the number of columns is 4 + _config.nireq * 2, because first 4 parameters - // are the same but realTime and cpuTime can be different on each iteration (example from 5 GPU requests): - // conv1;EXECUTED;Convolution;convolution_gpu_bfyx_gemm_like;630,3;617,3;616,3;615,3;617,3; - size_t numOfColumns = 0; - if (_config.report_type == noCntReport) { - numOfColumns = 3; - } else if (_config.report_type == averageCntReport) { - numOfColumns = 6; - } else { - // for detailedCntReport - numOfColumns = 4 + _config.nireq * 2; + dump_parameters(_parameters.at(Category::COMMAND_LINE_PARAMETERS)); + dumper.endLine(); } - auto completeCsvRow = [](CsvDumper &dumper, size_t numOfColumns, size_t filled) { - for (size_t i = 0; i < numOfColumns - filled; i++) - dumper << ""; + if (_parameters.count(Category::RUNTIME_CONFIG)) { + dumper << "Configuration setup"; dumper.endLine(); - }; - - // dump execution configuration - dumper << "Configuration setup"; - completeCsvRow(dumper, numOfColumns, 1); - dumper << "config option" << "CLI parameter" << "value"; - completeCsvRow(dumper, numOfColumns, 3); - - dumper << "target device" << " -d" << _config.device; - completeCsvRow(dumper, numOfColumns, 3); - dumper << "execution mode" << " -api" << _config.api; - completeCsvRow(dumper, numOfColumns, 3); - dumper << "batch size" << " -b" << _config.batch; - completeCsvRow(dumper, numOfColumns, 3); - dumper << "number of iterations" << " -niter" << _config.niter; - completeCsvRow(dumper, numOfColumns, 3); - dumper << "number of parallel infer requests" << " -nireq" << _config.nireq; - completeCsvRow(dumper, numOfColumns, 3); - dumper << "duration in ms" << " -t" << _config.duration; - completeCsvRow(dumper, numOfColumns, 3); - dumper << "number of CPU threads" << " -nthreads" << _config.cpu_nthreads; - completeCsvRow(dumper, numOfColumns, 3); - for (auto& item : _config.nstreams) - dumper << "number of " << item.first << " streams" << " -nstreams" << item.second; - completeCsvRow(dumper, numOfColumns, 3); - dumper << "CPU pinning enabled" << " -pin" << _config.cpu_pin; - completeCsvRow(dumper, numOfColumns, 3); - dumper.endLine(); - - // write PM data from each iteration - if (!_performanceCounters.empty()) { - if (_config.report_type != averageCntReport && _config.report_type != detailedCntReport) { - throw std::logic_error("PM data can only be collected for average or detailed report types"); - } - - // this vector is sorted according to network layers execution order. - auto performanceMapSorted = preparePmStatistics(); - - dumper << "Performance counters"; - completeCsvRow(dumper, numOfColumns, 1); - dumper << "layer name" << "exec status" << "layer type" << "exec type"; - - if (_config.report_type == averageCntReport) { - dumper << "average real time" << "average cpu time"; - completeCsvRow(dumper, numOfColumns, 6); - } else { - // detailedCntReport case - for (size_t i = 0; i< _performanceCounters.size(); i++) { - dumper << "realTime_req" + std::to_string(i) << "cpuTime_req" + std::to_string(i); - } - completeCsvRow(dumper, numOfColumns, 4 + _performanceCounters.size() * 2); - } - - for (const auto &layer : performanceMapSorted) { - dumper << layer.first; // layer name - - switch (layer.second.status) { - case InferenceEngine::InferenceEngineProfileInfo::EXECUTED: - dumper << "EXECUTED"; - break; - case InferenceEngine::InferenceEngineProfileInfo::NOT_RUN: - dumper << "NOT_RUN"; - break; - case InferenceEngine::InferenceEngineProfileInfo::OPTIMIZED_OUT: - dumper << "OPTIMIZED_OUT"; - break; - } - dumper << layer.second.layer_type << layer.second.exec_type; - - if (_config.report_type == averageCntReport) { - // write average realTime and cpuTime from each processed request for current layer - dumper << - std::to_string(std::accumulate(_perLayerRealTime[layer.first].begin(), - _perLayerRealTime[layer.first].end(), 0.0) / _perLayerRealTime[layer.first].size() / 1000.0) << - std::to_string(std::accumulate(_perLayerCpuTime[layer.first].begin(), - _perLayerCpuTime[layer.first].end(), 0.0) / _perLayerCpuTime[layer.first].size() / 1000.0); - } else { - // write all realTime and cpuTime from each processed request for current layer - for (size_t i = 0; i < _config.nireq; i++) { - dumper << std::to_string(_perLayerRealTime[layer.first][i] / 1000.0) << std::to_string(_perLayerCpuTime[layer.first][i] / 1000.0); - } - } - dumper.endLine(); - } + dump_parameters(_parameters.at(Category::RUNTIME_CONFIG)); dumper.endLine(); } - if (_config.report_type == detailedCntReport) { - dumper << "Statistics"; - completeCsvRow(dumper, numOfColumns, 1); + if (_parameters.count(Category::EXECUTION_RESULTS)) { + dumper << "Execution results"; + dumper.endLine(); - dumper << "metric"; - for (size_t i = 0; i < _totalLayersTime.size(); i++) { - // detailedCntReport case - dumper << "req" + std::to_string(i); - } - completeCsvRow(dumper, numOfColumns, 4 + _totalLayersTime.size()); - dumper << "latencies"; - for (const auto &lat : _totalLayersTime) { - dumper << lat / 1000.0; - } - completeCsvRow(dumper, numOfColumns, _totalLayersTime.size()); + dump_parameters(_parameters.at(Category::EXECUTION_RESULTS)); dumper.endLine(); } - dumper << "Execution results"; - completeCsvRow(dumper, numOfColumns, 1); - dumper << "number of iterations" << iteration_number; - completeCsvRow(dumper, numOfColumns, 2); - dumper << "latency" << getMedianValue(_latencies); - completeCsvRow(dumper, numOfColumns, 2); - dumper << "throughput" << fps; - completeCsvRow(dumper, numOfColumns, 2); - dumper << "total execution time" << totalExecTime; - completeCsvRow(dumper, numOfColumns, 2); - - slog::info << "statistics report is stored to " << dumper.getFilename() << slog::endl; + slog::info << "Statistics report is stored to " << dumper.getFilename() << slog::endl; } -double StatisticsReport::getMedianLatency() { - return getMedianValue(_latencies); -} +void StatisticsReport::dumpPerformanceCountersRequest(CsvDumper& dumper, + const PerformaceCounters& perfCounts) { + auto performanceMapSorted = perfCountersSorted(perfCounts); -std::vector> StatisticsReport::preparePmStatistics() { - if (_performanceCounters.empty()) { - throw std::logic_error("preparePmStatistics() was called when no PM data was collected"); - } + long long total = 0L; + long long total_cpu = 0L; + + dumper << "layerName" << "execStatus" << "layerType" << "execType"; + dumper << "realTime (ms)" << "cpuTime (ms)"; + dumper.endLine(); - // sort PM data of first processed request according to layers execution order - auto performanceMapSorted = perfCountersSorted(_performanceCounters[0]); - - // iterate over each processed infer request and handle its PM data - for (auto &pm : _performanceCounters) { - long long total = 0L; - // iterate over each layer from sorted vector and add required PM data to the per-layer maps - for (const auto & it : performanceMapSorted) { - _perLayerRealTime[it.first].push_back(pm[it.first].realTime_uSec); - _perLayerCpuTime[it.first].push_back(pm[it.first].cpu_uSec); - total += pm[it.first].realTime_uSec; + for (const auto &layer : performanceMapSorted) { + dumper << layer.first; // layer name + + switch (layer.second.status) { + case InferenceEngine::InferenceEngineProfileInfo::EXECUTED: + dumper << "EXECUTED"; + break; + case InferenceEngine::InferenceEngineProfileInfo::NOT_RUN: + dumper << "NOT_RUN"; + break; + case InferenceEngine::InferenceEngineProfileInfo::OPTIMIZED_OUT: + dumper << "OPTIMIZED_OUT"; + break; } - _totalLayersTime.push_back(total); + dumper << layer.second.layer_type << layer.second.exec_type; + dumper << std::to_string(layer.second.realTime_uSec / 1000.0) << std::to_string(layer.second.cpu_uSec/ 1000.0); + total += layer.second.realTime_uSec; + total_cpu += layer.second.cpu_uSec; + dumper.endLine(); } - return performanceMapSorted; + dumper << "Total" << "" << "" << ""; + dumper << total / 1000.0 << total_cpu / 1000.0; + dumper.endLine(); + dumper.endLine(); } -template -T StatisticsReport::getMedianValue(const std::vector &vec) { - std::vector sortedVec(vec); - std::sort(sortedVec.begin(), sortedVec.end()); - return (sortedVec.size() % 2 != 0) ? - sortedVec[sortedVec.size() / 2ULL] : - (sortedVec[sortedVec.size() / 2ULL] + sortedVec[sortedVec.size() / 2ULL - 1ULL]) / static_cast(2.0); +void StatisticsReport::dumpPerformanceCounters(const std::vector &perfCounts) { + if ((_config.report_type.empty()) || (_config.report_type == noCntReport)) { + slog::info << "Statistics collecting for performance counters was not requested. No reports are dumped." << slog::endl; + return; + } + if (perfCounts.empty()) { + slog::info << "Peformance counters are empty. No reports are dumped." << slog::endl; + return; + } + CsvDumper dumper(true, _config.report_folder + _separator + "benchmark_" + _config.report_type + "_report.csv"); + if (_config.report_type == detailedCntReport) { + for (auto& pc : perfCounts) { + dumpPerformanceCountersRequest(dumper, pc); + } + } else if (_config.report_type == averageCntReport) { + auto getAveragePerformanceCounters = [ &perfCounts ] () { + std::map performanceCountersAvg; + // sort PM data of first processed request according to layers execution order + auto performanceMapSorted = perfCountersSorted(perfCounts[0]); + + // iterate over each processed infer request and handle its PM data + for (size_t i = 0; i < perfCounts.size(); i++) { + // iterate over each layer from sorted vector and add required PM data to the per-layer maps + for (const auto& pm : performanceMapSorted) { + if (performanceCountersAvg.count(pm.first) == 0) { + performanceCountersAvg[pm.first] = perfCounts.at(i).at(pm.first); + } else { + performanceCountersAvg[pm.first].realTime_uSec += perfCounts.at(i).at(pm.first).realTime_uSec; + performanceCountersAvg[pm.first].cpu_uSec += perfCounts.at(i).at(pm.first).cpu_uSec; + } + } + } + for (auto& pm : performanceCountersAvg) { + pm.second.realTime_uSec /= perfCounts.size(); + pm.second.cpu_uSec /= perfCounts.size(); + } + return performanceCountersAvg; + }; + dumpPerformanceCountersRequest(dumper, getAveragePerformanceCounters()); + } else { + throw std::logic_error("PM data can only be collected for average or detailed report types"); + } + slog::info << "Pefromance counters report is stored to " << dumper.getFilename() << slog::endl; } diff --git a/inference-engine/samples/benchmark_app/statistics_report.hpp b/inference-engine/samples/benchmark_app/statistics_report.hpp index f7e0bb27198248..58eae04a041d2a 100644 --- a/inference-engine/samples/benchmark_app/statistics_report.hpp +++ b/inference-engine/samples/benchmark_app/statistics_report.hpp @@ -22,51 +22,51 @@ static constexpr char detailedCntReport[] = "detailed_counters"; /// @brief Responsible for collecting of statistics and dumping to .csv file class StatisticsReport { public: + typedef std::map PerformaceCounters; + typedef std::vector> Parameters; + struct Config { - std::string device; - std::string api; - size_t batch; - size_t nireq; - size_t niter; - uint64_t duration; - size_t cpu_nthreads; - std::map nstreams; - std::string cpu_pin; std::string report_type; std::string report_folder; }; + enum class Category { + COMMAND_LINE_PARAMETERS, + RUNTIME_CONFIG, + EXECUTION_RESULTS, + }; + explicit StatisticsReport(Config config) : _config(std::move(config)) { - if (_config.nireq > 0) { - _performanceCounters.reserve(_config.nireq); - } + _separator = +#if defined _WIN32 || defined __CYGWIN__ + # if defined UNICODE + L"\\"; + # else + "\\"; + # endif +#else + "/"; +#endif + if (_config.report_folder.empty()) + _separator = ""; } - void addPerfCounts(const std::map &pmStat); - - void addLatencies(const std::vector &latency); + void addParameters(const Category &category, const Parameters& parameters); - void dump(const double &fps, const size_t &numProcessedReq, const double &totalExecTime); + void dump(); - double getMedianLatency(); + void dumpPerformanceCounters(const std::vector &perfCounts); private: - std::vector> preparePmStatistics(); - - template - T getMedianValue(const std::vector &vec); - - // Contains PM data for each processed infer request - std::vector> _performanceCounters; - // Contains latency of each processed infer request - std::vector _latencies; + void dumpPerformanceCountersRequest(CsvDumper& dumper, + const PerformaceCounters& perfCounts); // configuration of current benchmark execution const Config _config; - // mapping from network layer to a vector of calculated RealTime values from each processed infer request. - std::map> _perLayerRealTime; - // mapping from network layer to a vector of calculated CPU Time values from each processed infer request. - std::map> _perLayerCpuTime; - std::vector _totalLayersTime; + // parameters + std::map _parameters; + + // csv separator + std::string _separator; }; diff --git a/inference-engine/samples/benchmark_app/utils.hpp b/inference-engine/samples/benchmark_app/utils.hpp index 4c2634d8dc0093..0dbd8c3222365c 100644 --- a/inference-engine/samples/benchmark_app/utils.hpp +++ b/inference-engine/samples/benchmark_app/utils.hpp @@ -12,4 +12,3 @@ std::vector parseDevices(const std::string& device_string); uint32_t deviceDefaultDeviceDurationInSeconds(const std::string& device); std::map parseValuePerDevice(const std::vector& devices, const std::string& values_string); -uint32_t deviceDefaultRequestsNumber(const std::string& device); diff --git a/inference-engine/samples/common/format_reader/CMakeLists.txt b/inference-engine/samples/common/format_reader/CMakeLists.txt index c4011c48a7034e..a8c9caf0b5f676 100644 --- a/inference-engine/samples/common/format_reader/CMakeLists.txt +++ b/inference-engine/samples/common/format_reader/CMakeLists.txt @@ -12,24 +12,21 @@ file (GLOB LIBRARY_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h ) -# Find OpenCV components if exist -find_package(OpenCV COMPONENTS imgcodecs videoio imgproc QUIET) -if(NOT(OpenCV_FOUND)) - message(WARNING "OPENCV is disabled or not found, " ${TARGET_NAME} " is built without OPENCV support") -endif() - # Create named folders for the sources within the .vcproj # Empty name lists them directly under the .vcproj source_group("src" FILES ${LIBRARY_SRC}) source_group("include" FILES ${LIBRARY_HEADERS}) - # Create library file from sources. add_library(${TARGET_NAME} SHARED ${MAIN_SRC} ${LIBRARY_HEADERS}) -if(OpenCV_FOUND) - target_link_libraries(${TARGET_NAME} PRIVATE ${OpenCV_LIBRARIES}) - target_compile_definitions(${TARGET_NAME} PRIVATE USE_OPENCV) +# Find OpenCV components if exist +find_package(OpenCV COMPONENTS imgcodecs videoio imgproc QUIET) +if(NOT OpenCV_FOUND) + message(WARNING "OPENCV is disabled or not found, " ${TARGET_NAME} " will be built without OPENCV support") +else() + target_link_libraries(${TARGET_NAME} PRIVATE ${OpenCV_LIBRARIES}) + target_compile_definitions(${TARGET_NAME} PRIVATE USE_OPENCV) endif() target_compile_definitions(${TARGET_NAME} PRIVATE IMPLEMENT_FORMAT_READER) diff --git a/inference-engine/samples/common/os/windows/w_dirent.h b/inference-engine/samples/common/os/windows/w_dirent.h index e9111d9a466e17..4fa56118616524 100644 --- a/inference-engine/samples/common/os/windows/w_dirent.h +++ b/inference-engine/samples/common/os/windows/w_dirent.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2019 Intel Corporation +// Copyright (C) 2018-2019 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // @@ -6,31 +6,33 @@ #if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN_UNDEF +#endif + #ifndef NOMINMAX # define NOMINMAX +# define NOMINMAX_UNDEF #endif -#include -#include -#include - -#else - -#include -#include -#include +#if defined(_M_IX86) && !defined(_X86_) && !defined(_AMD64_) +# define _X86_ +#endif +#if defined(_M_X64) && !defined(_X86_) && !defined(_AMD64_) +# define _AMD64_ #endif #include - +#include +#include +#include #include -#if defined(WIN32) - // Copied from linux libc sys/stat.h: - #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) - #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) -#endif +// Copied from linux libc sys/stat.h: +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) struct dirent { char *d_name; @@ -38,10 +40,9 @@ struct dirent { explicit dirent(const wchar_t *wsFilePath) { size_t i; auto slen = wcslen(wsFilePath); - d_name = static_cast(malloc(slen + 1)); + d_name = static_cast(malloc(slen + 1)); wcstombs_s(&i, d_name, slen + 1, wsFilePath, slen); } - ~dirent() { free(d_name); } @@ -60,6 +61,11 @@ class DIR { } public: + DIR(const DIR &other) = delete; + DIR(DIR &&other) = delete; + DIR& operator=(const DIR &other) = delete; + DIR& operator=(DIR &&other) = delete; + explicit DIR(const char *dirPath) : next(nullptr) { std::string ws = dirPath; if (endsWith(ws, "\\")) @@ -72,6 +78,7 @@ class DIR { ~DIR() { if (!next) delete next; + next = nullptr; FindClose(hFind); } @@ -96,7 +103,7 @@ class DIR { }; -static DIR *opendir(const char* dirPath) { +static DIR* opendir(const char *dirPath) { auto dp = new DIR(dirPath); if (!dp->isValid()) { delete dp; @@ -105,10 +112,27 @@ static DIR *opendir(const char* dirPath) { return dp; } -static struct dirent *readdir(DIR *dp) { +static struct dirent* readdir(DIR *dp) { return dp->nextEnt(); } static void closedir(DIR *dp) { delete dp; } + +#ifdef WIN32_LEAN_AND_MEAN_UNDEF +# undef WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN_UNDEF +#endif + +#ifdef NOMINMAX_UNDEF +# undef NOMINMAX_UNDEF +# undef NOMINMAX +#endif + +#else + +#include +#include + +#endif diff --git a/inference-engine/samples/common/samples/common.hpp b/inference-engine/samples/common/samples/common.hpp index fbcd249f1c2e34..58efdc673ade5f 100644 --- a/inference-engine/samples/common/samples/common.hpp +++ b/inference-engine/samples/common/samples/common.hpp @@ -27,7 +27,7 @@ #include #ifndef UNUSED - #ifdef WIN32 + #if defined (_MSC_VER) && !defined (__clang__) #define UNUSED #else #define UNUSED __attribute__((unused)) @@ -1120,5 +1120,4 @@ inline void showAvailableDevices() { for (const auto& device : devices) { std::cout << " " << device; } - std::cout << " HDDL" << std::endl; } diff --git a/inference-engine/samples/common/samples/console_progress.hpp b/inference-engine/samples/common/samples/console_progress.hpp index 5edfea80fbc8c7..c6adc5e88d970e 100644 --- a/inference-engine/samples/common/samples/console_progress.hpp +++ b/inference-engine/samples/common/samples/console_progress.hpp @@ -4,7 +4,8 @@ #pragma once -#include +#include +#include #include /** @@ -12,12 +13,15 @@ * @brief A ConsoleProgress class provides functionality for printing progress dynamics */ class ConsoleProgress { - static const int DEFAULT_DETALIZATION = 20; + static const size_t DEFAULT_DETALIZATION = 20; + static const size_t DEFAULT_PERCENT_TO_UPDATE_PROGRESS = 1; size_t total; - size_t current = 0; + size_t cur_progress = 0; + size_t prev_progress = 0; bool stream_output; size_t detalization; + size_t percent_to_update; public: /** @@ -25,18 +29,19 @@ class ConsoleProgress { * @param _total - maximum value that is correspondent to 100% * @param _detalization - number of symbols(.) to use to represent progress */ - explicit ConsoleProgress(size_t _total, bool _stream_output = false, size_t _detalization = DEFAULT_DETALIZATION) : - total(_total), detalization(_detalization) { + explicit ConsoleProgress(size_t _total, + bool _stream_output = false, + size_t _percent_to_update = DEFAULT_PERCENT_TO_UPDATE_PROGRESS, + size_t _detalization = DEFAULT_DETALIZATION) : + total(_total), detalization(_detalization), percent_to_update(_percent_to_update) { stream_output = _stream_output; if (total == 0) { total = 1; } - std::cout << std::unitbuf; } /** * @brief Shows progress with current data. Progress is shown from the beginning of the current line. - * @return */ void showProgress() const { std::stringstream strm; @@ -45,28 +50,34 @@ class ConsoleProgress { } strm << "Progress: ["; size_t i = 0; - for (; i < detalization * current / total; i++) { + for (; i < detalization * cur_progress / total; i++) { strm << "."; } for (; i < detalization; i++) { strm << " "; } - strm << "] " << std::fixed << std::setprecision(2) << 100 * static_cast(current) / total << "% done"; + strm << "] " << std::setw(3) << 100 * cur_progress / total << "% done"; if (stream_output) { - std::cout << strm.str() << std::endl; - } else { - std::cout << strm.str() << std::flush; + strm << std::endl; } + std::fputs(strm.str().c_str(), stdout); + std::fflush(stdout); } /** * @brief Updates current value and progressbar - * @param newProgress - new value to represent */ - void updateProgress(size_t newProgress) { - current = newProgress; - if (current > total) current = total; - showProgress(); + void updateProgress() { + if (cur_progress > total) cur_progress = total; + size_t prev_percent = 100 * prev_progress / total; + size_t cur_percent = 100 * cur_progress / total; + + if (prev_progress == 0 || + cur_progress == total || + prev_percent + percent_to_update <= cur_percent) { + showProgress(); + prev_progress = cur_progress; + } } /** @@ -74,10 +85,11 @@ class ConsoleProgress { * @param add - value to add */ void addProgress(int add) { - if (add < 0 && -add > static_cast(current)) { - add = -static_cast(current); + if (add < 0 && -add > static_cast(cur_progress)) { + add = -static_cast(cur_progress); } - updateProgress(current + add); + cur_progress += add; + updateProgress(); } /** @@ -85,6 +97,9 @@ class ConsoleProgress { * @return */ void finish() { - std::cerr << std::nounitbuf << "\n"; + std::stringstream strm; + strm << std::endl; + std::fputs(strm.str().c_str(), stdout); + std::fflush(stdout); } }; diff --git a/inference-engine/samples/hello_classification/README.md b/inference-engine/samples/hello_classification/README.md index 0d7db2eba30756..90833a58ea699c 100644 --- a/inference-engine/samples/hello_classification/README.md +++ b/inference-engine/samples/hello_classification/README.md @@ -10,8 +10,8 @@ It demonstrates how to use the following Inference Engine API in applications: There is also an API introduced to crop a ROI object and set it as input without additional memory re-allocation. To properly demonstrate this API, it is required to run several networks in pipeline which is out of scope of this sample. -Please refer to [Security Barrier Camera Demo](./inference-engine/samples/security_barrier_camera_demo/README.md), or -[Crossroad Camera Demo](./inference-engine/samples/crossroad_camera_demo/README.md) with an example of using of new crop ROI API. +Please refer to [Security Barrier Camera Demo](./demos/security_barrier_camera_demo/README.md), or +[Crossroad Camera Demo](./demos/crossroad_camera_demo/README.md) with an example of using of new crop ROI API. Refer to [Integrate the Inference Engine New Request API with Your Application](./docs/IE_DG/Integrate_with_customer_application_new_API.md) for details. diff --git a/inference-engine/samples/hello_query_device/README.md b/inference-engine/samples/hello_query_device/README.md index 9d32bd118efefe..0d796de305a14e 100644 --- a/inference-engine/samples/hello_query_device/README.md +++ b/inference-engine/samples/hello_query_device/README.md @@ -1,8 +1,8 @@ # Hello Query Device C++ Sample -This topic demonstrates how to run the Hello Query Device sample application, which queries Inference Engine devices and prints their metrics and default configuration values. The sample shows how to use [Query Device API feature](./docs/IE_DG/QueryDeviceAPI.md). +This topic demonstrates how to run the Hello Query Device sample application, which queries Inference Engine devices and prints their metrics and default configuration values. The sample shows how to use [Query Device API feature](./docs/IE_DG/InferenceEngine_QueryAPI.md). > **NOTE:** This topic describes usage of C++ implementation of the Query Device Sample. -> For the Python* implementation, refer to [Hello Query Device Python* Sample](./inference-engine/ie_brudges/python/sample/hello_query_device/README.md) +> For the Python* implementation, refer to [Hello Query Device Python* Sample](./inference-engine/ie_bridges/python/sample/hello_query_device/README.md) ## Running To see quired information, run the following: diff --git a/inference-engine/samples/object_detection_sample_ssd/README.md b/inference-engine/samples/object_detection_sample_ssd/README.md index 237067075be818..4ef6a14485f0c2 100644 --- a/inference-engine/samples/object_detection_sample_ssd/README.md +++ b/inference-engine/samples/object_detection_sample_ssd/README.md @@ -3,6 +3,8 @@ This topic demonstrates how to run the Object Detection sample application, which does inference using object detection networks like SSD-VGG on Intel® Processors and Intel® HD Graphics. +> **NOTE:** This topic describes usage of C++ implementation of the Object Detection Sample SSD. For the Python* implementation, refer to [Object Detection Python* Sample SSD](./inference-engine/ie_bridges/python/sample/object_detection_sample_ssd/README.md). + ## How It Works Upon the start-up the sample application reads command line parameters and loads a network and an image to the Inference diff --git a/inference-engine/samples/speech_sample/README.md b/inference-engine/samples/speech_sample/README.md index 0046c97f3f92db..0785ee7c3f8dcc 100644 --- a/inference-engine/samples/speech_sample/README.md +++ b/inference-engine/samples/speech_sample/README.md @@ -48,17 +48,15 @@ will be removed in GNA hardware version 3 and higher. #### Execution Modes Several execution modes are supported via the `-d` flag. If the device -is set to `CPU` and the GNA plugin is selected, the GNA device is -emulated in fast-but-not-bit-exact mode. If the device is set to -`GNA_AUTO`, then the GNA hardware is used if available and the driver is -installed. Otherwise, the GNA device is emulated in -fast-but-not-bit-exact mode. If the device is set to `GNA_HW`, then the -GNA hardware is used if available and the driver is installed. +is set to `CPU` mode, then all calculation will be performed on CPU device +using CPU Plugin. If the device is set to `GNA_AUTO`, then the GNA hardware is +used if available and the driver is installed. Otherwise, the GNA device is +emulated in fast-but-not-bit-exact mode. If the device is set to `GNA_HW`, +then the GNA hardware is used if available and the driver is installed. Otherwise, an error will occur. If the device is set to `GNA_SW`, the GNA device is emulated in fast-but-not-bit-exact mode. Finally, if the device is set to `GNA_SW_EXACT`, the GNA device is emulated in bit-exact mode. -`GNA_SW_FP32` mode is used for calculation on CPU device using GNA Plugin. #### Loading and Saving Models @@ -94,7 +92,7 @@ Options: -m "" Required. Path to an .xml file with a trained model (required if -rg is missing). -o "" Optional. Output file name (default name is "scores.ark"). -l "" Required for CPU custom layers. Absolute path to a shared library with the kernel implementations. - -d "" Optional. Specify a target device to infer on. CPU, GPU, GNA_AUTO, GNA_HW, GNA_SW, GNA_SW_EXACT, GNA_SW_FP32 and HETERO with combination of GNA + -d "" Optional. Specify a target device to infer on. CPU, GPU, GNA_AUTO, GNA_HW, GNA_SW, GNA_SW_EXACT and HETERO with combination of GNA as the primary device and CPU as a secondary (e.g. HETERO:GNA,CPU) are supported. The list of available devices is shown below. The sample will look for a suitable plugin for device specified. -p Optional. Plugin name. For example, GPU. If this parameter is set, the sample will look for this plugin only -pc Optional. Enables performance report diff --git a/inference-engine/samples/speech_sample/main.cpp b/inference-engine/samples/speech_sample/main.cpp index efc38ca398c203..be52db459f7d14 100644 --- a/inference-engine/samples/speech_sample/main.cpp +++ b/inference-engine/samples/speech_sample/main.cpp @@ -706,7 +706,7 @@ int main(int argc, char *argv[]) { outputInfo = netBuilder.getNetwork().getOutputsInfo(); } - Blob::Ptr ptrOutputBlob = inferRequests[0].inferRequest.GetBlob(cOutputInfo.rbegin()->first); + Blob::Ptr ptrOutputBlob = inferRequests.begin()->inferRequest.GetBlob(cOutputInfo.rbegin()->first); for (auto &item : outputInfo) { DataPtr outData = item.second; @@ -839,7 +839,7 @@ int main(int argc, char *argv[]) { if (!FLAGS_o.empty()) { outputFrame = &ptrScores.front() + numScoresPerFrame * sizeof(float) * (inferRequest.frameIndex); - Blob::Ptr outputBlob = inferRequest.inferRequest.GetBlob(cOutputInfo.begin()->first); + Blob::Ptr outputBlob = inferRequest.inferRequest.GetBlob(cOutputInfo.rbegin()->first); auto byteSize = inferRequest.numFramesThisBatch * numScoresPerFrame * sizeof(float); std::memcpy(outputFrame, outputBlob->buffer(), @@ -848,7 +848,7 @@ int main(int argc, char *argv[]) { if (!FLAGS_r.empty()) { Blob::Ptr outputBlob = inferRequest.inferRequest.GetBlob(cOutputInfo.begin()->first); - CompareScores(outputBlob->buffer().as(), + CompareScores(outputBlob->buffer().as(), &ptrReferenceScores[inferRequest.frameIndex * numFrameElementsReference * numBytesPerElementReference], @@ -876,7 +876,7 @@ int main(int argc, char *argv[]) { ptrInputBlobs.push_back(inferRequest.inferRequest.GetBlob(input.first)); } - for (size_t i = 0; i < numInputArkFiles; i++) { + for (size_t i = 0; i < numInputArkFiles; ++i) { std::memcpy(ptrInputBlobs[i]->buffer(), inputFrame[i], ptrInputBlobs[i]->byteSize()); @@ -890,14 +890,14 @@ int main(int argc, char *argv[]) { frameIndex += numFramesThisBatch; for (size_t j = 0; j < inputArkFiles.size(); j++) { if (FLAGS_cw_l > 0 || FLAGS_cw_r > 0) { - int i = frameIndex - FLAGS_cw_l; - if (i > 0 && i < static_cast(numFramesArkFile)) { + int idx = frameIndex - FLAGS_cw_l; + if (idx > 0 && idx < static_cast(numFramesArkFile)) { inputFrame[j] += sizeof(float) * numFrameElementsInput[j] * numFramesThisBatch; - } else if (i >= static_cast(numFramesArkFile)) { - inputFrame[j] = &ptrUtterances[0].front() + + } else if (idx >= static_cast(numFramesArkFile)) { + inputFrame[j] = &ptrUtterances[j].front() + (numFramesArkFile - 1) * sizeof(float) * numFrameElementsInput[j] * numFramesThisBatch; - } else if (i < 0) { - inputFrame[j] = &ptrUtterances[0].front(); + } else if (idx <= 0) { + inputFrame[j] = &ptrUtterances[j].front(); } } else { inputFrame[j] += sizeof(float) * numFrameElementsInput[j] * numFramesThisBatch; @@ -905,7 +905,6 @@ int main(int argc, char *argv[]) { } inferRequestFetched |= true; } - if (!inferRequestFetched) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; diff --git a/inference-engine/samples/speech_sample/speech_sample.hpp b/inference-engine/samples/speech_sample/speech_sample.hpp index 63a18b2fe0594a..02f866966f789c 100644 --- a/inference-engine/samples/speech_sample/speech_sample.hpp +++ b/inference-engine/samples/speech_sample/speech_sample.hpp @@ -23,7 +23,7 @@ static const char plugin_message[] = "Plugin name. For example MKLDNNPlugin. If "the sample will look for this plugin only"; /// @brief message for assigning cnn calculation to device -static const char target_device_message[] = "Specify a target device to infer on. CPU, GPU, GNA_AUTO, GNA_HW, GNA_SW, GNA_SW_FP32 " +static const char target_device_message[] = "Specify a target device to infer on. CPU, GPU, GNA_AUTO, GNA_HW, GNA_SW, " "GNA_SW_EXACT and HETERO with combination of GNA as the primary device and CPU" " as a secondary (e.g. HETERO:GNA,CPU) are supported. The list of available devices is shown below. " "The sample will look for a suitable plugin for device specified."; diff --git a/inference-engine/samples/thirdparty/gflags/.gitmodules b/inference-engine/samples/thirdparty/gflags/.gitmodules deleted file mode 100644 index aa2072c575fee4..00000000000000 --- a/inference-engine/samples/thirdparty/gflags/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "doc"] - path = doc - url = https://github.com/gflags/gflags.git - branch = gh-pages diff --git a/inference-engine/src/CMakeLists.txt b/inference-engine/src/CMakeLists.txt index bd1793f8e0a260..63fda2ada6f380 100644 --- a/inference-engine/src/CMakeLists.txt +++ b/inference-engine/src/CMakeLists.txt @@ -24,10 +24,10 @@ if (ENABLE_GNA) add_subdirectory(gna_plugin) endif() -add_subdirectory(inference_engine) - add_subdirectory(hetero_plugin) +add_subdirectory(inference_engine) + set(InferenceEngine_LIBRARIES inference_engine) set(InferenceEngine_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/include) set(InferenceEngine_SRC_DIRS ${CMAKE_SOURCE_DIR}/src) diff --git a/inference-engine/src/cldnn_engine/CMakeLists.txt b/inference-engine/src/cldnn_engine/CMakeLists.txt index 211f6600286c95..b618084b3b5c37 100644 --- a/inference-engine/src/cldnn_engine/CMakeLists.txt +++ b/inference-engine/src/cldnn_engine/CMakeLists.txt @@ -14,12 +14,11 @@ ie_add_plugin(NAME ${TARGET_NAME} SOURCES ${MAIN_SRC} ${LIBRARY_HEADERS} VERSION_DEFINES_FOR cldnn_engine.cpp) -target_link_libraries(${TARGET_NAME} PRIVATE ${INTEL_ITT_LIBS} inference_engine clDNN_shlib pugixml) +target_link_libraries(${TARGET_NAME} PRIVATE ${INTEL_ITT_LIBS} inference_engine clDNN_lib pugixml) set (CLDNN_TOP_FOLDER ${IE_MAIN_SOURCE_DIR}/thirdparty/clDNN) target_include_directories(${TARGET_NAME} PRIVATE - ${CLDNN_TOP_FOLDER}/api - ${CLDNN_TOP_FOLDER}/include + ${CLDNN_TOP_FOLDER} ${IE_MAIN_SOURCE_DIR}/src/inference_engine ${IE_MAIN_SOURCE_DIR}/thirdparty/pugixml/src) diff --git a/inference-engine/src/cldnn_engine/cldnn_config.h b/inference-engine/src/cldnn_engine/cldnn_config.h index cf863fb5447c43..29b3491601419e 100644 --- a/inference-engine/src/cldnn_engine/cldnn_config.h +++ b/inference-engine/src/cldnn_engine/cldnn_config.h @@ -16,7 +16,7 @@ #include "cldnn_custom_layer.h" -#include +#include namespace CLDNNPlugin { diff --git a/inference-engine/src/cldnn_engine/cldnn_custom_layer.h b/inference-engine/src/cldnn_engine/cldnn_custom_layer.h index e948f29842f53a..ee4cd3267afca3 100644 --- a/inference-engine/src/cldnn_engine/cldnn_custom_layer.h +++ b/inference-engine/src/cldnn_engine/cldnn_custom_layer.h @@ -10,7 +10,7 @@ #include #include #include "pugixml.hpp" -#include "CPP/tensor.hpp" +#include "api/tensor.hpp" namespace CLDNNPlugin { @@ -54,7 +54,7 @@ class CLDNNCustomLayer{ const std::vector& GlobalSizeRules()const { return m_globalSizeRules; } const std::vector& LocalSizeRules()const { return m_localSizeRules; } const std::vector& KernelParams()const { return m_kernelParams; } - const int InputDimSourceIndex() { return m_wgDimInputIdx; } + int InputDimSourceIndex() { return m_wgDimInputIdx; } protected: CLDNNCustomLayer() : m_wgDimInputIdx(0) {} diff --git a/inference-engine/src/cldnn_engine/cldnn_engine.cpp b/inference-engine/src/cldnn_engine/cldnn_engine.cpp index 8aba309dff5652..a43fc916db6d3b 100644 --- a/inference-engine/src/cldnn_engine/cldnn_engine.cpp +++ b/inference-engine/src/cldnn_engine/cldnn_engine.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "ie_metric_helpers.hpp" #include @@ -132,7 +133,7 @@ ExecutableNetworkInternal::Ptr clDNNEngine::LoadExeNetworkImpl(const InferenceEn INFERENCE_PLUGIN_API(StatusCode) CreatePluginEngine(IInferencePlugin *&plugin, ResponseDesc *resp) noexcept { try { plugin = make_ie_compatible_plugin( - {2, 0, + {2, 1, CI_BUILD_NUMBER, "clDNNPlugin"}, std::make_shared()); return OK; @@ -233,6 +234,23 @@ Parameter clDNNEngine::GetConfig(const std::string& name, const std::map& /*options*/) const { if (name == METRIC_KEY(SUPPORTED_METRICS)) { std::vector metrics; @@ -250,7 +268,7 @@ Parameter clDNNEngine::GetMetric(const std::string& name, const std::map availableDevices = { "" }; IE_SET_METRIC_RETURN(AVAILABLE_DEVICES, availableDevices); } else if (name == METRIC_KEY(FULL_DEVICE_NAME)) { - IE_SET_METRIC_RETURN(FULL_DEVICE_NAME, std::string(engine_info.ocl_device_name)); + IE_SET_METRIC_RETURN(FULL_DEVICE_NAME, StringRightTrim(engine_info.dev_name, "NEO", false)); } else if (name == METRIC_KEY(SUPPORTED_CONFIG_KEYS)) { std::vector configKeys; for (auto opt : _impl->m_config.key_config_map) diff --git a/inference-engine/src/cldnn_engine/cldnn_engine.h b/inference-engine/src/cldnn_engine/cldnn_engine.h index 1fb31908393b97..aa2baa22c88e80 100644 --- a/inference-engine/src/cldnn_engine/cldnn_engine.h +++ b/inference-engine/src/cldnn_engine/cldnn_engine.h @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include namespace CLDNNPlugin { diff --git a/inference-engine/src/cldnn_engine/cldnn_executable_network.cpp b/inference-engine/src/cldnn_engine/cldnn_executable_network.cpp index 77fcfe490a21b3..7a3ce7a4598880 100644 --- a/inference-engine/src/cldnn_engine/cldnn_executable_network.cpp +++ b/inference-engine/src/cldnn_engine/cldnn_executable_network.cpp @@ -7,8 +7,8 @@ #include #include "ie_metric_helpers.hpp" -#include -#include +#include +#include #include #include #include diff --git a/inference-engine/src/cldnn_engine/cldnn_graph.cpp b/inference-engine/src/cldnn_engine/cldnn_graph.cpp index b1f98c98877c0e..928491bf67ef1f 100644 --- a/inference-engine/src/cldnn_engine/cldnn_graph.cpp +++ b/inference-engine/src/cldnn_engine/cldnn_graph.cpp @@ -6,10 +6,10 @@ #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include @@ -238,7 +238,7 @@ InferenceEngine::ICNNNetwork::Ptr CLDNNGraph::GetExecGraphInfoByPrimitivesInfo(s layer->type = to_IE_type_name(prim_info.type_id); layer->precision = data_type_to_precision(prim_info.output_layout.data_type); std::vector originalNames{find_origin_layers(prim_info.original_id)}; - for (auto& fused_id : prim_info.c_fused_ids.cpp_ids) + for (auto& fused_id : prim_info.c_fused_ids) for (auto& origin_id : find_origin_layers(fused_id)) originalNames.push_back(origin_id); @@ -266,7 +266,7 @@ InferenceEngine::ICNNNetwork::Ptr CLDNNGraph::GetExecGraphInfoByPrimitivesInfo(s if (filter_const_primitives) { // Decrease expected dependencies count if there is a const input without original id in the IR - for (auto& dep : prim_info.c_dependencies.cpp_ids) { + for (auto& dep : prim_info.c_dependencies) { auto it = std::find_if(primitives_info.begin(), primitives_info.end(), [&](cldnn::primitive_info& entry) { return entry.original_id == dep; }); @@ -290,16 +290,16 @@ InferenceEngine::ICNNNetwork::Ptr CLDNNGraph::GetExecGraphInfoByPrimitivesInfo(s for (auto& pi : primitives_info) { // extract mutable_data primitives and connect it's dependencies and users directly if (pi.type_id == "mutable_data") { - if (pi.c_dependencies.cpp_ids.size() == 1 && !pi.c_users.cpp_ids.empty()) { - auto dep = pi.c_dependencies.cpp_ids[0]; - auto users = pi.c_users.cpp_ids; + if (pi.c_dependencies.size() == 1 && !pi.c_users.empty()) { + auto dep = pi.c_dependencies[0]; + auto users = pi.c_users; auto it = std::find_if(primitives_info.begin(), primitives_info.end(), [&](cldnn::primitive_info& entry) { return entry.original_id == dep; }); if (it == primitives_info.end()) continue; - auto& dep_users = it->c_users.cpp_ids; + auto& dep_users = it->c_users; // Remove mutable data from users list dep_users.erase(std::find_if(dep_users.begin(), dep_users.end(), [&](std::string user_id) { return user_id == pi.original_id; @@ -315,7 +315,7 @@ InferenceEngine::ICNNNetwork::Ptr CLDNNGraph::GetExecGraphInfoByPrimitivesInfo(s if (it == primitives_info.end()) continue; - for (auto& d : it->c_dependencies.cpp_ids) { + for (auto& d : it->c_dependencies) { if (d == pi.original_id) d = dep; } @@ -334,8 +334,8 @@ InferenceEngine::ICNNNetwork::Ptr CLDNNGraph::GetExecGraphInfoByPrimitivesInfo(s // Skip mutable_data if (pi.type_id == "mutable_data" && - pi.c_dependencies.cpp_ids.size() == 1 && - !pi.c_users.cpp_ids.empty()) { + pi.c_dependencies.size() == 1 && + !pi.c_users.empty()) { continue; } } @@ -377,7 +377,7 @@ InferenceEngine::ICNNNetwork::Ptr CLDNNGraph::GetExecGraphInfoByPrimitivesInfo(s for (auto& pair : node2layer) { auto pi = pair.first; auto layer = pair.second; - auto user_ids = pi.c_users.cpp_ids; + auto user_ids = pi.c_users; for (int i = 0; i < user_ids.size(); i++) { auto it = std::find_if(node2layer.begin(), node2layer.end(), [&](std::pair& entry) { return entry.first.original_id == user_ids[i]; @@ -399,7 +399,7 @@ InferenceEngine::ICNNNetwork::Ptr CLDNNGraph::GetExecGraphInfoByPrimitivesInfo(s } int in_port_id = 0; - for (auto& dep : it->first.c_dependencies.cpp_ids) { + for (auto& dep : it->first.c_dependencies) { if (filter_const_primitives) { auto it = std::find_if(node2layer.begin(), node2layer.end(), [&](std::pair& entry) { return entry.first.original_id == dep; @@ -461,16 +461,8 @@ void CLDNNGraph::UpdatePerfStatistics() { for (auto &profiledID : profilingIDs) { auto& perfCount = perfMap[profiledID].second; // Change status if layer wasn't executed by cldnn engine - if (perfCount.num == 0 && - executedPrimitives.find(profiledID) == executedPrimitives.end()) { - if (allPrimitives.find(profiledID) != allPrimitives.end() && - allPrimitives.at(profiledID) == "_optimized_") { - // Layer was marked as optimized by cldnn - perfCount.status = InferenceEngineProfileInfo::OPTIMIZED_OUT; - } else { - // Layer wasn't run for some reason - perfCount.status = InferenceEngineProfileInfo::NOT_RUN; - } + if (perfCount.num == 0 && executedPrimitives.find(profiledID) == executedPrimitives.end()) { + perfCount.status = InferenceEngineProfileInfo::OPTIMIZED_OUT; continue; } @@ -546,22 +538,30 @@ void CLDNNGraph::UpdateImplementationsMap() { } void CLDNNGraph::GetPerformanceCounts(std::map &result) const { + bool combinePrimByIRLayers = false; unsigned i = 0; - for (auto& profiledID : profilingIDs) { - const auto& layerName = perfMap.at(profiledID).first; - if (layerName.length() == 0) // no layer directly associated - continue; + auto allIds = GetNetwork()->get_all_primitive_org_ids(); + auto executedPrimitives = GetNetwork()->get_executed_primitives(); + auto primitivesInfo = GetNetwork()->get_primitives_info(); + + auto getFromProfiling = [&](std::string primId) -> bool { + const auto& layerName = perfMap.at(primId).first; + if (layerName.length() == 0) // no layer directly associated + return false; + + const auto& perfCounter = perfMap.at(primId).second; + + if (!perfCounter.parentPrimitive.empty() && combinePrimByIRLayers) + return false; - const auto& perfCounter = perfMap.at(profiledID).second; auto& extPerfEntry = result[layerName]; - // copy layer implementation + memset(extPerfEntry.exec_type, 0, sizeof(extPerfEntry.exec_type)); if (perfCounter.isCPU) { static const std::string cpuExecType("CPU"); - memset(extPerfEntry.exec_type, 0, sizeof(extPerfEntry.exec_type)); cpuExecType.copy(extPerfEntry.exec_type, cpuExecType.length()); // Override execType as CPU } else { - std::string impl = implementationsMap.at(profiledID); + std::string impl = implementationsMap.at(primId); impl.copy(extPerfEntry.exec_type, impl.length()); } @@ -570,14 +570,97 @@ void CLDNNGraph::GetPerformanceCounts(std::map kernelTime) { + kernelTime = pc.realTime_avg(); + kernelId = id; + } + allIds.erase(std::find(allIds.begin(), allIds.end(), id)); + } + } + if (!kernelId.empty()) + implementationsMap.at(kernelId).copy(extPerfEntry.exec_type, implementationsMap.at(kernelId).length()); + } + perfCounter.layerType.copy(extPerfEntry.layer_type, perfCounter.layerType.length()); - } + return true; + }; + + for (auto& primId : allIds) { + if (std::find(profilingIDs.begin(), profilingIDs.end(), primId) != profilingIDs.end()) { + getFromProfiling(primId); + } else if (executedPrimitives.find(primId) != executedPrimitives.end()) { + auto event = executedPrimitives.at(primId); + + cldnn::instrumentation::profiling_info cldnnInfo{primId, event.get_profiling_info()}; + + // Collect timings + long long cpuTime = 0; + long long deviceTime = 0; - for (auto& prim : GetNetwork()->get_executed_primitive_ids()) { - if (std::find(profilingIDs.begin(), profilingIDs.end(), prim) == profilingIDs.end()) { - // TODO: add primitives that was added inside cldnn to perf stat + for (auto &interval : cldnnInfo.intervals) { + using duration_t = std::chrono::duration; + auto count = std::chrono::duration_cast(interval.value->value()).count(); + + if (interval.name == "submission") { + cpuTime += count; + } else if (interval.name == "executing") { + deviceTime += count; + } else if (interval.name == "duration") { // "duration" is used for CPU layers + cpuTime += count; + } + } + + std::string layerName = primId; + if (primId.find(":") != std::string::npos) { + layerName = primId.substr(primId.find(":") + 1, primId.length()); + } + + for (auto& pi : primitivesInfo) { + if (pi.original_id == primId) { + if (pi.type_id == "mutable_data") + continue; + + auto& extPerfEntry = result[layerName]; + + if (pi.is_cpu) { + static const std::string cpuExecType("CPU"); + memset(extPerfEntry.exec_type, 0, sizeof(extPerfEntry.exec_type)); + cpuExecType.copy(extPerfEntry.exec_type, cpuExecType.length()); // Override execType as CPU + } else { + std::string impl = pi.kernel_id; + impl.copy(extPerfEntry.exec_type, impl.length()); + } + + pi.type_id.copy(extPerfEntry.layer_type, 256); + extPerfEntry.execution_index = i++; + extPerfEntry.status = InferenceEngineProfileInfo::LayerStatus::EXECUTED; + extPerfEntry.cpu_uSec = cpuTime; + extPerfEntry.realTime_uSec = deviceTime; + + if (pi.type_id == "input_layout") { + const std::string input_string = "Input"; + const std::string undef_string = "undef"; + input_string.copy(extPerfEntry.layer_type, 256); + undef_string.copy(extPerfEntry.exec_type, 256); + } + } + } } } + + // Checking primitives which has been deleted from execution order but added by clDNNPlugin + for (auto& primId : profilingIDs) + if (std::find(allIds.begin(), allIds.end(), primId) == allIds.end()) { + getFromProfiling(primId); + } } std::shared_ptr CLDNNGraph::GetNetwork(size_t idx) const { diff --git a/inference-engine/src/cldnn_engine/cldnn_graph.h b/inference-engine/src/cldnn_engine/cldnn_graph.h index 48c414d6cb371f..aeda853950568d 100644 --- a/inference-engine/src/cldnn_engine/cldnn_graph.h +++ b/inference-engine/src/cldnn_engine/cldnn_graph.h @@ -15,17 +15,17 @@ #include "cpp/ie_cnn_network.h" #include "debug_options.h" #include "inference_engine.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include #include "cldnn_custom_layer.h" #include "cldnn_config.h" #include "cldnn_program.h" diff --git a/inference-engine/src/cldnn_engine/cldnn_infer_request.cpp b/inference-engine/src/cldnn_engine/cldnn_infer_request.cpp index f2b7e10c8ef0b2..8065e71c01e75e 100644 --- a/inference-engine/src/cldnn_engine/cldnn_infer_request.cpp +++ b/inference-engine/src/cldnn_engine/cldnn_infer_request.cpp @@ -6,7 +6,7 @@ #include #include #include -#include // todo: find a way to remove this +#include // todo: find a way to remove this #include #include "cldnn_infer_request.h" #include "cldnn_streams_task_executor.h" @@ -356,7 +356,6 @@ void CLDNNInferRequest::SetBatch(int new_batch) { size_t offset = 0; size_t bsz = single_batch; - int b = 0; // calculate metadata for input buffers for (unsigned nb = 0; nb < m_graph->GetNetworksCount(); nb++) { diff --git a/inference-engine/src/cldnn_engine/cldnn_lstm.cpp b/inference-engine/src/cldnn_engine/cldnn_lstm.cpp index 6ca467fdb89a78..f80c76ae1cfd14 100644 --- a/inference-engine/src/cldnn_engine/cldnn_lstm.cpp +++ b/inference-engine/src/cldnn_engine/cldnn_lstm.cpp @@ -6,19 +6,19 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "cldnn_program.h" using namespace InferenceEngine; @@ -102,8 +102,8 @@ void Program::CreateLSTMCellPrimitive(cldnn::topology& topology, InferenceEngine topology.add(cldnn::reshape(inReshapeID, inputPrimitives[0], inputShape)); topology.add(cldnn::reorder(permuteID, inReshapeID, inputLayout)); - primitivesToIRLayersMap[inReshapeID] = { layer->name }; - primitivesToIRLayersMap[permuteID] = { layer->name }; + addInnerPrimitiveToProfiler(inReshapeID, layer->name, layer); + addInnerPrimitiveToProfiler(permuteID, layer->name, layer); std::string hiddenInResh = inHiddenReshapeID + "_1"; std::string hiddenInStr = inHiddenReorderID + "_1"; @@ -115,8 +115,11 @@ void Program::CreateLSTMCellPrimitive(cldnn::topology& topology, InferenceEngine topology.add(cldnn::reorder(cellInStr, cellInResh, hiddenLayout)); topology.add(cldnn::concatenation(concatID, { permuteID, hiddenInStr }, cldnn::concatenation::concatenation_axis::along_x)); - primitivesToIRLayersMap[hiddenInStr] = { layer->name }; - primitivesToIRLayersMap[cellInStr] = { layer->name }; + addInnerPrimitiveToProfiler(hiddenInResh, layer->name, layer); + addInnerPrimitiveToProfiler(hiddenInStr, layer->name, layer); + addInnerPrimitiveToProfiler(cellInResh, layer->name, layer); + addInnerPrimitiveToProfiler(cellInStr, layer->name, layer); + addInnerPrimitiveToProfiler(concatID, layer->name, layer); cldnn::tensor gemmSz = cldnn::tensor{ lstm_batch_size, 1, 4 * lstm_hidden_size, 1 }; cldnn::layout gemmLayout = cldnn::layout(DataTypeFromPrecision(layer->precision), cldnn::format::bfyx, gemmSz); @@ -131,25 +134,26 @@ void Program::CreateLSTMCellPrimitive(cldnn::topology& topology, InferenceEngine topology.add(cldnn::reshape(gemmReshapeID, lstm_fc_id, gemmSz)); topology.add(cldnn::reorder(gemmReorderID, gemmReshapeID, gemmLayout)); topology.add(cldnn::lstm_elt(lstm_elt_id, gemmReorderID, cellInStr, - 0, 0, {}, {}, cldnn_lstm_offset_order_fizo)); + 0, 0, {}, {}, cldnn::lstm_weights_order::fizo)); - primitivesToIRLayersMap[lstm_fc_id] = { layer->name }; - primitivesToIRLayersMap[lstm_elt_id] = { layer->name }; + addInnerPrimitiveToProfiler(lstm_fc_id, layer->name, layer); + addInnerPrimitiveToProfiler(gemmReshapeID, layer->name, layer); + addInnerPrimitiveToProfiler(gemmReorderID, layer->name, layer); + addInnerPrimitiveToProfiler(lstm_elt_id, layer->name, layer); cldnn::primitive_id outputHiddenID = layerName; topology.add(cldnn::crop(outputHiddenID, lstm_elt_id, hiddenSz, cldnn::tensor{0, 0, 0, 0})); + addInnerPrimitiveToProfiler(outputHiddenID, layer->name, layer); cldnn::primitive_id outputCellID = layer_type_lower(layer) + ":" + layer->outData[1]->getName(); topology.add(cldnn::crop(outputCellID, lstm_elt_id, hiddenSz, cellCropSz)); - - primitivesToIRLayersMap[outputHiddenID] = { layer->name }; - primitivesToIRLayersMap[outputCellID] = { layer->name }; + addInnerPrimitiveToProfiler(outputCellID, layer->name, layer); // output primitive IDs primitiveIDs[outputHiddenID] = outputHiddenID; // LSTMCell:LSTMCell - "concat hidden" primitiveIDs[layer_type_lower(layer) + ":" + layer->outData[0]->getName()] = outputHiddenID; // LSTMCell:LSTMCell:0 - hidden state primitiveIDs[outputCellID] = outputCellID; // LSTMCell:LSTMCell:1 - cell state - profilingIDs.push_back(layerName); + addPrimitiveToProfiler(layerName, layer, outputHiddenID); } void Program::CreateRegularLSTM(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -250,10 +254,10 @@ void Program::CreateRegularLSTM(cldnn::topology& topology, InferenceEngine::CNNL topology.add(cldnn::reshape(inHiddenReshapeID+"_1", inputPrimitives[1], hiddenStateShape)); topology.add(cldnn::reshape(inHiddenReshapeID+"_2", inputPrimitives[2], hiddenStateShape)); - primitivesToIRLayersMap[inReshapeID] = { layer->name }; - primitivesToIRLayersMap[permuteID] = { layer->name }; - primitivesToIRLayersMap[inHiddenReshapeID+"_1"] = { layer->name }; - primitivesToIRLayersMap[inHiddenReshapeID+"_2"] = { layer->name }; + addInnerPrimitiveToProfiler(inReshapeID, layerName, layer); + addInnerPrimitiveToProfiler(permuteID, layerName, layer); + addInnerPrimitiveToProfiler(inHiddenReshapeID+"_1", layerName, layer); + addInnerPrimitiveToProfiler(inHiddenReshapeID+"_2", layerName, layer); for (int i = 0; i < lstm_sequence_len; ++i) input_ids_offsets.push_back({ get_string_id(i), {0, i, 0, 0} }); @@ -262,14 +266,12 @@ void Program::CreateRegularLSTM(cldnn::topology& topology, InferenceEngine::CNNL if (permute_input) { topology.add(cldnn::permute(layerName + "_inputSwap", permuteID, { 1, 0, 2, 3 })); + addInnerPrimitiveToProfiler(layerName + "_inputSwap", layerName, layer); topology.add(cldnn::split(inputSplitID, layerName + "_inputSwap", input_ids_offsets)); - - primitivesToIRLayersMap[layerName + "_inputSwap"] = { layer->name }; - primitivesToIRLayersMap[inputSplitID] = { layer->name }; } else { topology.add(cldnn::split(inputSplitID, permuteID, input_ids_offsets)); - primitivesToIRLayersMap[inputSplitID] = { layer->name }; } + addInnerPrimitiveToProfiler(inputSplitID, layerName, layer); cldnn::tensor gemmSz = cldnn::tensor{ lstm_batch_size, 1, 4 * lstm_hidden_size, 1 }; cldnn::layout gemmLayout = cldnn::layout(DataTypeFromPrecision(layer->precision), cldnn::format::bfyx, gemmSz); @@ -290,29 +292,33 @@ void Program::CreateRegularLSTM(cldnn::topology& topology, InferenceEngine::CNNL if (hiddenStr != "") { topology.add(cldnn::concatenation(concatID, { inputSplitID + ":" + get_string_id(seqIdx), hiddenStr }, cldnn::concatenation::concatenation_axis::along_x)); + addInnerPrimitiveToProfiler(concatID, layerName, layer); topology.add(cldnn::fully_connected(lstm_fc_id, concatID, weightID, hasBias ? biasID : "")); + addInnerPrimitiveToProfiler(lstm_fc_id, layerName, layer); + addInnerPrimitiveToProfiler(inputSplitID + ":" + get_string_id(seqIdx), layerName, layer); } else { topology.add(cldnn::fully_connected(lstm_fc_id, inputSplitID + ":" + get_string_id(seqIdx), weightID, hasBias ? biasID : "")); + addInnerPrimitiveToProfiler(lstm_fc_id, layerName, layer); } topology.add(cldnn::reshape(lstm_fc_resh_id, lstm_fc_id, gemmSz)); topology.add(cldnn::reorder(lstm_fc_reor_id, lstm_fc_resh_id, gemmLayout)); topology.add(cldnn::lstm_elt(lstm_elt_id, lstm_fc_reor_id, cellStr, 0, 0, {}, {}, - cldnn_lstm_offset_order_fizo)); + cldnn::lstm_weights_order::fizo)); + addInnerPrimitiveToProfiler(lstm_fc_resh_id, layerName, layer); + addInnerPrimitiveToProfiler(lstm_fc_reor_id, layerName, layer); + addInnerPrimitiveToProfiler(lstm_elt_id, layerName, layer); hiddenStr = crop_id + ":hidden"; cellStr = crop_id + ":cell"; topology.add(cldnn::crop(hiddenStr, lstm_elt_id, hiddenSz, cldnn::tensor{ 0, 0, 0, 0 })); + addInnerPrimitiveToProfiler(hiddenStr, layerName, layer); output_ids_offsets.push_back(hiddenStr); - primitivesToIRLayersMap[lstm_fc_id] = { layer->name }; - primitivesToIRLayersMap[lstm_elt_id] = { layer->name }; - primitivesToIRLayersMap[hiddenStr] = { layer->name }; - if (i < lstm_sequence_len - 1) { topology.add(cldnn::crop(cellStr, lstm_elt_id, hiddenSz, cellCropSz)); - primitivesToIRLayersMap[cellStr] = { layer->name }; + addInnerPrimitiveToProfiler(cellStr, layerName, layer); } else { // last hidden state crop (output 2) if (layer->outData.size() > 1) { @@ -325,8 +331,7 @@ void Program::CreateRegularLSTM(cldnn::topology& topology, InferenceEngine::CNNL if (layer->outData.size() > 2) { topology.add(cldnn::crop(cellStr, lstm_elt_id, hiddenSz, cellCropSz)); cldnn::primitive_id outputCellID = layer_type_lower(layer) + ":" + layer->outData[2]->getName(); - primitivesToIRLayersMap[cellStr] = { layer->name }; - primitiveIDs[cellStr] = cellStr; + addInnerPrimitiveToProfiler(cellStr, layerName, layer); primitiveIDs[outputCellID] = cellStr; } } @@ -336,16 +341,13 @@ void Program::CreateRegularLSTM(cldnn::topology& topology, InferenceEngine::CNNL if (permute_input) { topology.add(cldnn::concatenation(layerName + "_outputConcat", output_ids_offsets, cldnn::concatenation::along_f)); + addInnerPrimitiveToProfiler(layerName + "_outputConcat", layerName, layer); topology.add(cldnn::permute(layerName, layerName + "_outputConcat", { 1, 0, 2, 3 })); - primitivesToIRLayersMap[layerName + "_outputConcat"] = { layer->name }; } else { topology.add(cldnn::concatenation(layerName, output_ids_offsets, cldnn::concatenation::along_f)); } - - primitivesToIRLayersMap[layerName] = { layer->name }; - primitiveIDs[layerName] = layerName; primitiveIDs[layer_type_lower(layer) + ":" + layer->outData[0]->getName()] = layerName; - profilingIDs.push_back(layerName); + addPrimitiveToProfiler(layerName, layer); } void Program::CreateDynamicLSTM(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -478,9 +480,15 @@ void Program::CreateDynamicLSTM(cldnn::topology& topology, InferenceEngine::CNNL topology.add(cldnn::reshape(inReshapeID, inputPrimitives[0], inputShape)); topology.add(cldnn::reorder(permuteID, inReshapeID, inputLayout)); + addInnerPrimitiveToProfiler(inReshapeID, layerName, layer); + addInnerPrimitiveToProfiler(permuteID, layerName, layer); + topology.add(cldnn::reshape(inHiddenReshapeID + "_1", inputPrimitives[1], hiddenStateShape)); topology.add(cldnn::reshape(inHiddenReshapeID + "_2", inputPrimitives[2], hiddenStateShape)); + addInnerPrimitiveToProfiler(inHiddenReshapeID + "_1", layerName, layer); + addInnerPrimitiveToProfiler(inHiddenReshapeID + "_2", layerName, layer); + cldnn::primitive_id dynID = layerName + "_dynLength"; cldnn::primitive_id dynReshapeID = layerName + "_dynReshape"; cldnn::tensor dynShape = { 1, 1, lstm_batch_size, 1 }; @@ -488,10 +496,8 @@ void Program::CreateDynamicLSTM(cldnn::topology& topology, InferenceEngine::CNNL topology.add(cldnn::reshape(dynReshapeID, inputPrimitives[3], dynShape)); topology.add(cldnn::reorder(dynID, dynReshapeID, dynLayout)); - primitivesToIRLayersMap[inReshapeID] = { layer->name }; - primitivesToIRLayersMap[permuteID] = { layer->name }; - primitivesToIRLayersMap[inHiddenReshapeID + "_1"] = { layer->name }; - primitivesToIRLayersMap[inHiddenReshapeID + "_2"] = { layer->name }; + addInnerPrimitiveToProfiler(dynReshapeID, layerName, layer); + addInnerPrimitiveToProfiler(dynID, layerName, layer); cldnn::primitive_id inputID = permuteID; cldnn::primitive_id prevInputID = permuteID; @@ -500,14 +506,15 @@ void Program::CreateDynamicLSTM(cldnn::topology& topology, InferenceEngine::CNNL inputID = layerName + "_inputSwap"; topology.add(cldnn::permute(inputID, prevInputID, { 1, 0, 2, 3 })); prevInputID = inputID; + addInnerPrimitiveToProfiler(inputID, layerName, layer); } - primitivesToIRLayersMap[inputID] = { layer->name }; cldnn::primitive_id seq_len_id = layer->name + "seq_lengths"; if (reverseSeq) { inputID = layerName + "_inputReverse"; topology.add(cldnn::reverse_sequence(inputID, prevInputID, dynID, 1, 0)); primitivesToIRLayersMap[inputID] = { layer->name }; + addInnerPrimitiveToProfiler(inputID, layerName, layer); prevInputID = inputID; } @@ -538,26 +545,25 @@ void Program::CreateDynamicLSTM(cldnn::topology& topology, InferenceEngine::CNNL weightID, recurrentID, outputHiddenID, outputCellID, biasID, inHiddenReshapeID + "_1", inHiddenReshapeID + "_2")); prevInputID = inputID = dlstmID; - primitivesToIRLayersMap[dlstmID] = { layer->name }; + addInnerPrimitiveToProfiler(dlstmID, layerName, layer); if (reverseSeq) { inputID = layerName + "_outputReverse"; topology.add(cldnn::reverse_sequence(inputID, prevInputID, dynID, 1, 0)); - primitivesToIRLayersMap[inputID] = { layer->name }; + addInnerPrimitiveToProfiler(inputID, layerName, layer); prevInputID = inputID; } if (permute_input) { inputID = layerName + "_outputSwap"; topology.add(cldnn::permute(inputID, prevInputID, { 1, 0, 2, 3 })); - primitivesToIRLayersMap[inputID] = { layer->name }; + addInnerPrimitiveToProfiler(inputID, layerName, layer); prevInputID = inputID; } - primitiveIDs[layerName] = inputID; primitiveIDs[inputID] = inputID; primitiveIDs[layer_type_lower(layer) + ":" + layer->outData[0]->getName()] = inputID; - profilingIDs.push_back(layerName); + addPrimitiveToProfiler(layerName, layer, inputID); } void Program::CreateRNNPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { diff --git a/inference-engine/src/cldnn_engine/cldnn_program.cpp b/inference-engine/src/cldnn_engine/cldnn_program.cpp index cd8cf98fc24bca..7c19b50ba44ce6 100644 --- a/inference-engine/src/cldnn_engine/cldnn_program.cpp +++ b/inference-engine/src/cldnn_engine/cldnn_program.cpp @@ -7,52 +7,56 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include #include @@ -87,18 +91,38 @@ const cldnn::primitive_id Program::m_scalesTag("_cldnn_scales"); const cldnn::primitive_id Program::m_preCustomLayerTag("_cldnn_custom_preprocess"); const cldnn::primitive_id Program::m_postCustomLayerTag("_cldnn_custom_postprocess"); -static void ValidateLayer(const InferenceEngine::CNNLayerPtr& layer, unsigned inputs) { // todo: add more checks +static bool isValid(const InferenceEngine::CNNLayerPtr& layer, unsigned inputs) { // todo: add more checks if (inputs && layer->insData.size() != inputs) { - THROW_CLDNN_EXCEPTION("Invalid number of inputs for layer: " << layer->name); + return false; } + if (layer->_fusedWith) { - THROW_CLDNN_EXCEPTION("Unsupported fuse in layer: " << layer->name << " with: " << layer->_fusedWith->name); + return false; + } + + return true; +} + +static void ValidateLayer(const InferenceEngine::CNNLayerPtr& layer, unsigned inputs) { + if (!isValid(layer, inputs)) { + THROW_CLDNN_EXCEPTION("Layer " << layer->name << " is inconsistent"); } } -static void ValidateEltwiseLayer(const InferenceEngine::CNNLayerPtr& layer) { - if (layer->_fusedWith) { - THROW_CLDNN_EXCEPTION("Unsupported fuse in layer: " << layer->name << " with: " << layer->_fusedWith->name); +static void ValidateLayer(const InferenceEngine::CNNLayerPtr& layer, std::vector inputs) { // todo: add more checks + bool is_valid = false; + if (inputs.empty()) { + if (!layer->_fusedWith) { + is_valid = true; + } + } else { + for (auto& input : inputs) { + is_valid |= isValid(layer, input); + } + } + + if (!is_valid) { + THROW_CLDNN_EXCEPTION("Layer " << layer->name << " is inconsistent"); } } @@ -198,8 +222,7 @@ Program::Program(InferenceEngine::ICNNNetwork& network, std::shared_ptrisEmpty() && network.getPrecision() == Precision::FP32) { + if (s == StatusCode::OK && pstats && !pstats->isEmpty()) { CNNNetworkInt8Normalizer normalizer; normalizer.NormalizeNetwork(network, *pstats); } @@ -478,6 +501,7 @@ Program::LayerType Program::LayerTypeFromStr(const std::string &str) { { "StridedSlice" , StridedSlice }, { "ReverseSequence" , ReverseSequence }, { "BinaryConvolution" , BinaryConvolution }, + { "FakeQuantize" , Quantize }, { "Quantize" , Quantize }, { "Broadcast" , Broadcast }, { "Squeeze" , Squeeze }, @@ -514,7 +538,9 @@ Program::LayerType Program::LayerTypeFromStr(const std::string &str) { { "SoftSign" , SoftSign }, { "Tan" , Tan }, { "GEMM", Gemm }, - { "OneHot", OneHot} + { "OneHot", OneHot}, + { "GatherTree", GatherTree}, + { "Convert", Convert } }; auto it = LayerNameToType.find(str); if (it != LayerNameToType.end()) @@ -594,6 +620,23 @@ auto CldnnTensorFromIEDims = [](const InferenceEngine::SizeVector& dims, int def } }; +template +std::vector PermuteIEDimsToCldnnOrder(const std::vector& ie_order, Type value_to_align = 0) { + static_assert(std::is_integral::value, "Integeral required."); + std::vector cldnn_order = ie_order; + + // 1. Align to min. 4 sizes + if (cldnn_order.size() < 4) + cldnn_order.push_back(value_to_align); + + // 2. Swap spatial positions + for (int i = 0; i < (cldnn_order.size() - 2) / 2; i++) { + std::swap(cldnn_order[2 + i], cldnn_order[1 + cldnn_order.size() - (2 + i)]); + } + + return cldnn_order; +} + cldnn::primitive_id Program::CreatePrimitiveFromBlob(cldnn::topology& topology, cldnn::primitive_id primID, const InferenceEngine::Blob::Ptr pBlob, @@ -708,7 +751,7 @@ void Program::CreateWeightAndBiasPrimitives(cldnn::topology& topology, weightDimsVec.push_back(TensorValue(convLayer->_kernel[i])); } outFeatures = convLayer->_out_depth; - pWeightsBlob = getBlob(layer, "weights"); + pWeightsBlob = getBlobOrNull(layer, "weights"); pBiasBlob = getBlobOrNull(layer, "biases"); break; } @@ -726,7 +769,7 @@ void Program::CreateWeightAndBiasPrimitives(cldnn::topology& topology, weightDimsVec.push_back(TensorValue(deconvLayer->_kernel[i])); } outFeatures = deconvLayer->_out_depth; - pWeightsBlob = getBlob(layer, "weights"); + pWeightsBlob = getBlobOrNull(layer, "weights"); pBiasBlob = getBlobOrNull(layer, "biases"); if ((groupSize < outFeatures) || (groupSize < inFeatures)) @@ -752,26 +795,35 @@ void Program::CreateWeightAndBiasPrimitives(cldnn::topology& topology, break; } + if (pWeightsBlob == nullptr) { + if (layer->insData.size() == 1) + THROW_IE_EXCEPTION << "No weights found in weightable layer " + layer->name; + } + // create weights primitive cldnn::format wFmt = m_defaultFormat; if (weightDimsVec.size() > 4) wFmt = cldnn::format::bfzyx; - cldnn::layout weightsLayout = cldnn::layout( - DataTypeFromPrecision(pWeightsBlob->getTensorDesc().getPrecision()), - wFmt, - cldnn::tensor(weightDimsVec)); - size_t bytesPerGroup = weightsLayout.bytes_count(); - - for (unsigned g = 0; g < groupSize; g++) { - cldnn::primitive_id weightID = layer_type_name_ID(layer) + m_weightsTag + std::to_string(g); - weightID = CreatePrimitiveFromBlob(topology, - weightID, - pWeightsBlob, - weightsLayout, - g * bytesPerGroup, - rearrange); - weightsPrimID.push_back(weightID); + if (pWeightsBlob == nullptr) { + auto wei_name = layer_type_name_ID(layer->insData[1].lock()->getCreatorLayer().lock()); + weightsPrimID.push_back(wei_name); + } else { + cldnn::layout weightsLayout = cldnn::layout( + DataTypeFromPrecision(pWeightsBlob->getTensorDesc().getPrecision()), + wFmt, + cldnn::tensor(weightDimsVec)); + size_t bytesPerGroup = weightsLayout.bytes_count(); + for (unsigned g = 0; g < groupSize; g++) { + cldnn::primitive_id weightID = layer_type_name_ID(layer) + m_weightsTag + std::to_string(g); + weightID = CreatePrimitiveFromBlob(topology, + weightID, + pWeightsBlob, + weightsLayout, + g * bytesPerGroup, + rearrange); + weightsPrimID.push_back(weightID); + } } // create bias primitive @@ -779,7 +831,7 @@ void Program::CreateWeightAndBiasPrimitives(cldnn::topology& topology, cldnn::layout biasesLayout = cldnn::layout( DataTypeFromPrecision(pBiasBlob->getTensorDesc().getPrecision()), FormatFromLayout(pBiasBlob->getTensorDesc().getLayout()), - (cldnn::tensor) cldnn::spatial(TensorValue(outFeatures / groupSize))); + (cldnn::tensor) cldnn::feature(TensorValue(outFeatures / groupSize))); size_t bytesPerGroup = biasesLayout.bytes_count(); for (unsigned g = 0; g < groupSize; g++) { cldnn::primitive_id biasID = layer_type_name_ID(layer) + m_biasesTag + std::to_string(g); @@ -790,6 +842,9 @@ void Program::CreateWeightAndBiasPrimitives(cldnn::topology& topology, g * bytesPerGroup); biasesPrimID.push_back(biasID); } + } else if (layer->insData.size() == 3) { + auto bias_name = layer_type_name_ID(layer->insData[2].lock()->getCreatorLayer().lock()); + biasesPrimID.push_back(bias_name); } } @@ -824,6 +879,11 @@ void Program::CreateBinaryWeightAndBiasPrimitives(cldnn::topology& topology, }; pWeightsBlob = binaryConvLayer->_weights; pBiasBlob = binaryConvLayer->_biases; + + if (pWeightsBlob == nullptr) { + if (binaryConvLayer->insData.size() == 1) + THROW_IE_EXCEPTION << "No weights found in binary convolution layer " + layer->name; + } break; } default: @@ -831,19 +891,24 @@ void Program::CreateBinaryWeightAndBiasPrimitives(cldnn::topology& topology, } // create weights primitive - cldnn::layout weightsLayout = cldnn::layout( - cldnn::data_types::bin, - cldnn::format::bfyx, - cldnn::tensor(weightDimsVec)); - - cldnn::primitive_id weightID = layer->name + m_weightsTag; - weightID = CreatePrimitiveFromBlob(topology, - weightID, - pWeightsBlob, - weightsLayout, - 0, - rearrange); - weightsPrimID.push_back(weightID); + if (pWeightsBlob == nullptr) { + auto wei_name = layer_type_name_ID(layer->insData[1].lock()->getCreatorLayer().lock()); + weightsPrimID.push_back(wei_name); + } else { + cldnn::layout weightsLayout = cldnn::layout( + cldnn::data_types::bin, + cldnn::format::bfyx, + cldnn::tensor(weightDimsVec)); + + cldnn::primitive_id weightID = layer->name + m_weightsTag; + weightID = CreatePrimitiveFromBlob(topology, + weightID, + pWeightsBlob, + weightsLayout, + 0, + rearrange); + weightsPrimID.push_back(weightID); + } // create bias primitive if (pBiasBlob != nullptr) { @@ -891,18 +956,13 @@ void Program::CreateScaleWeightsAndBiasesFromBN(cldnn::topology& topology, auto varianceData = static_cast(bnLayer->_weights->buffer()); auto meanData = static_cast(bnLayer->_biases->buffer()); - cldnn_status status = CLDNN_SUCCESS; for (size_t i = 0; i < weightsBlob.size(); i++) { - auto variance = cldnn_half_to_float(varianceData[i], &status); - if (status != CLDNN_SUCCESS) THROW_CLDNN_EXCEPTION("Error during fp16 conversion for layer " << bnLayer->name); - auto mean = cldnn_half_to_float(meanData[i], &status); - if (status != CLDNN_SUCCESS) THROW_CLDNN_EXCEPTION("Error during fp16 conversion for layer " << bnLayer->name); + auto variance = cldnn::half_to_float(varianceData[i]); + auto mean = cldnn::half_to_float(meanData[i]); float scale = 1.0f / sqrt(variance + bnLayer->epsilon); - weightsData[i] = cldnn_float_to_half(scale, &status); - if (status != CLDNN_SUCCESS) THROW_CLDNN_EXCEPTION("Error during fp16 conversion for layer " << bnLayer->name); - biasesData[i] = cldnn_float_to_half((-mean) * scale, &status); - if (status != CLDNN_SUCCESS) THROW_CLDNN_EXCEPTION("Error during fp16 conversion for layer " << bnLayer->name); + weightsData[i] = cldnn::float_to_half(scale); + biasesData[i] = cldnn::float_to_half((-mean) * scale); } weightsPrimID = CreatePrimitiveFromBlob(topology, weightsPrimID, std::make_shared>(weightsBlob), blobLayout); @@ -997,7 +1057,7 @@ void Program::CreateQuantizationPrimitives(cldnn::topology& topology, void Program::CreateSingleLayerPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { // Initialize a profiling entry - InitProfileInfo(layer->name, layer_type_lower(layer)); + InitProfileInfo(layer->name, layer->type); // First check for custom layer auto customLayer = m_config.customLayers.find(layer->type); @@ -1144,6 +1204,10 @@ void Program::CreateSingleLayerPrimitive(cldnn::topology& topology, InferenceEng break; case OneHot: CreateOneHotPrimitive(topology, layer); break; + case Convert: CreateConvertPrimitive(topology, layer); + break; + case GatherTree: CreateGatherTreePrimitive(topology, layer); + break; default: THROW_CLDNN_EXCEPTION("Unknown Layer Type: " << layer->type); } } @@ -1202,9 +1266,7 @@ void Program::CreateScaleShiftPrimitive(cldnn::topology& topology, InferenceEngi DataTypeFromPrecision(layerPrecision)); topology.add(inReorderPrim); - profilingIDs.push_back(inReorderName); - primitivesToIRLayersMap[inReorderName] = { layer->name }; - primitiveIDs[inReorderName] = inReorderName; + addInnerPrimitiveToProfiler(inReorderName, scaleShiftLayerName, layer); prevLayerName = inReorderName; } @@ -1218,8 +1280,6 @@ void Program::CreateScaleShiftPrimitive(cldnn::topology& topology, InferenceEngi prevLayerName = scaleShiftLayerName; topology.add(scaleShiftPrim); - profilingIDs.push_back(scaleShiftLayerName); - primitivesToIRLayersMap[scaleShiftLayerName] = { layer->name }; // Cast output data if it doesn't match operating precision if (outPrecision != layerPrecision) { @@ -1232,14 +1292,12 @@ void Program::CreateScaleShiftPrimitive(cldnn::topology& topology, InferenceEngi DataTypeFromPrecision(outPrecision)); topology.add(outReorderPrim); - profilingIDs.push_back(outReorderName); - primitivesToIRLayersMap[outReorderName] = { layer->name }; - primitiveIDs[outReorderName] = outReorderName; + addInnerPrimitiveToProfiler(outReorderName, scaleShiftLayerName, layer); prevLayerName = outReorderName; } - primitiveIDs[scaleShiftLayerName] = prevLayerName; + addPrimitiveToProfiler(scaleShiftLayerName, layer, prevLayerName); } void Program::CreateProposalPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr & layer) { @@ -1328,8 +1386,6 @@ void Program::CreateProposalPrimitive(cldnn::topology& topology, InferenceEngine shift_anchors, normalize); - primitivesToIRLayersMap[proposalLayerName] = { layer->name }; - primitiveIDs[proposalLayerName] = proposalLayerName; topology.add(proposalPrim); cldnn::primitive_id proposal_mutable_id_r = layer_type_lower(layer) + ":" + layer->outData[1]->getName(); @@ -1338,7 +1394,7 @@ void Program::CreateProposalPrimitive(cldnn::topology& topology, InferenceEngine primitiveIDs[proposal_mutable_id_r] = proposal_mutable_id_r; topology.add(argmax_mutable_prim_r); - profilingIDs.push_back(proposalLayerName); + addPrimitiveToProfiler(proposalLayerName, layer); return; } @@ -1369,10 +1425,8 @@ void Program::CreateProposalPrimitive(cldnn::topology& topology, InferenceEngine shift_anchors, normalize); - primitivesToIRLayersMap[proposalLayerName] = { layer->name }; - primitiveIDs[proposalLayerName] = proposalLayerName; topology.add(proposalPrim); - profilingIDs.push_back(proposalLayerName); + addPrimitiveToProfiler(proposalLayerName, layer); } void Program::CreatePReLUPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -1405,25 +1459,19 @@ void Program::CreatePReLUPrimitive(cldnn::topology& topology, InferenceEngine::C break; case InferenceEngine::Precision::FP16: { - cldnn_status status = CLDNN_SUCCESS; - slope = cldnn_half_to_float(*static_cast(slopeBlob->buffer()), &status); - if (status != CLDNN_SUCCESS) { - THROW_CLDNN_EXCEPTION("Error converting fp16 value in " << preluLayer->name); - } + slope = cldnn::half_to_float(*static_cast(slopeBlob->buffer())); } break; default: THROW_CLDNN_EXCEPTION("Invalid PReLU slope blob precision in " << preluLayer->name); } - topology.add(cldnn::activation(preluLayerName, inputPrimitives[0], activation_relu_negative_slope, { slope, 0.f })); + topology.add(cldnn::activation(preluLayerName, inputPrimitives[0], cldnn::activation_func::relu_negative_slope, { slope, 0.f })); } else { cldnn::primitive_id slopePrimID(preluLayerName + "_" + blobName + m_weightsTag); auto map = CreateGenericLayerBlobPrimitives(topology, preluLayer); - topology.add(cldnn::activation(preluLayerName, inputPrimitives[0], map.at(slopePrimID), activation_relu_negative_slope)); + topology.add(cldnn::activation(preluLayerName, inputPrimitives[0], map.at(slopePrimID), cldnn::activation_func::relu_negative_slope)); } - primitivesToIRLayersMap[preluLayerName] = { layer->name }; - primitiveIDs[preluLayerName] = preluLayerName; - profilingIDs.push_back(preluLayerName); + addPrimitiveToProfiler(preluLayerName, layer); } void Program::CreateBatchNormalizationPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr & layer) { @@ -1442,11 +1490,7 @@ void Program::CreateBatchNormalizationPrimitive(cldnn::topology& topology, Infer CreateScaleWeightsAndBiasesFromBN(topology, bnLayer, weightID, biasID); auto scalePrim = cldnn::scale(bnLayerName, inputPrimitives[0], weightID, biasID); - primitivesToIRLayersMap[bnLayerName] = { layer->name }; - primitiveIDs[bnLayerName] = bnLayerName; topology.add(scalePrim); - profilingIDs.push_back(bnLayerName); - return; #else cldnn::tensor blobTensor(0); const auto bnDims = bnLayer->outData[0]->getTensorDesc().getDims(); @@ -1480,11 +1524,9 @@ void Program::CreateBatchNormalizationPrimitive(cldnn::topology& topology, Infer varianceID, bnLayer->epsilon); - primitivesToIRLayersMap[bnLayerName] = { layer->name }; - primitiveIDs[bnLayerName] = bnLayerName; topology.add(bnPrim); - profilingIDs.push_back(bnLayerName); #endif // _SCALE_BN_OPT + addPrimitiveToProfiler(bnLayerName, layer); } void Program::CreateFlattenPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -1498,10 +1540,8 @@ void Program::CreateFlattenPrimitive(cldnn::topology& topology, InferenceEngine: inputPrimitives[0], CldnnTensorFromIEDims(flattenLayer->outData[0]->getTensorDesc().getDims())); - primitivesToIRLayersMap[flattenLayerName] = { layer->name }; - primitiveIDs[flattenLayerName] = flattenLayerName; topology.add(flattenPrim); - profilingIDs.push_back(flattenLayerName); + addPrimitiveToProfiler(flattenLayerName, layer); } void Program::CreatePermutePrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -1528,10 +1568,7 @@ void Program::CreatePermutePrimitive(cldnn::topology& topology, InferenceEngine: else cldnn_permute_order.push_back(o); } - // 2. Swap spatial positions - for (int i = 0; i < (cldnn_permute_order.size() - 2) / 2; i++) { - std::swap(cldnn_permute_order[2 + i], cldnn_permute_order[1 + cldnn_permute_order.size() - (2 + i)]); - } + cldnn_permute_order = PermuteIEDimsToCldnnOrder(cldnn_permute_order); std::string permuteLayerName = layer_type_name_ID(layer); @@ -1540,10 +1577,8 @@ void Program::CreatePermutePrimitive(cldnn::topology& topology, InferenceEngine: inputPrimitives[0], cldnn_permute_order); - primitivesToIRLayersMap[permuteLayerName] = { layer->name }; - primitiveIDs[permuteLayerName] = permuteLayerName; topology.add(permutePrim); - profilingIDs.push_back(permuteLayerName); + addPrimitiveToProfiler(permuteLayerName, layer); } void Program::CreateReshapePrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -1574,8 +1609,8 @@ void Program::CreateReshapePrimitive(cldnn::topology& topology, InferenceEngine: cldnn::layout outputLayout(DataTypeFromPrecision(outDesc.getPrecision()), outputFormat, outTensor); topology.add(cldnn::reorder(reorderId, reshapeInputId, outputLayout)); + addInnerPrimitiveToProfiler(reorderId, reshapeLayerName, layer); reshapeInputId = reorderId; - primitivesToIRLayersMap[reorderId] = { layer->name }; } auto reshapePrim = cldnn::reshape( @@ -1583,10 +1618,8 @@ void Program::CreateReshapePrimitive(cldnn::topology& topology, InferenceEngine: reshapeInputId, outTensor); - primitivesToIRLayersMap[reshapeLayerName] = { layer->name }; - primitiveIDs[reshapeLayerName] = reshapeLayerName; topology.add(reshapePrim); - profilingIDs.push_back(reshapeLayerName); + addPrimitiveToProfiler(reshapeLayerName, layer); } void Program::CreateNormalizePrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -1613,10 +1646,8 @@ void Program::CreateNormalizePrimitive(cldnn::topology& topology, InferenceEngin across_spatial, eps); - primitivesToIRLayersMap[normLayerName] = { layer->name }; - primitiveIDs[normLayerName] = normLayerName; topology.add(normPrim); - profilingIDs.push_back(normLayerName); + addPrimitiveToProfiler(normLayerName, layer); } void Program::CreateDetectionOutputPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -1670,10 +1701,8 @@ void Program::CreateDetectionOutputPrimitive(cldnn::topology& topology, Inferenc clip_before_nms, clip_after_nms); - primitivesToIRLayersMap[detectionLayerName] = { layer->name }; - primitiveIDs[detectionLayerName] = detectionLayerName; topology.add(detectionPrim); - profilingIDs.push_back(detectionLayerName); + addPrimitiveToProfiler(detectionLayerName, layer); } void Program::CreatePriorBoxPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -1744,14 +1773,12 @@ void Program::CreatePriorBoxPrimitive(cldnn::topology& topology, InferenceEngine offset, scale_all_sizes); - primitivesToIRLayersMap[priorBoxLayerName] = { layer->name }; - primitiveIDs[priorBoxLayerName] = priorBoxLayerName; topology.add(priorBoxPrim); - profilingIDs.push_back(priorBoxLayerName); + addPrimitiveToProfiler(priorBoxLayerName, layer); } void Program::CreateDeconvolutionPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { - ValidateLayer(layer, 1); + ValidateLayer(layer, {1, 2, 3}); auto inputPrimitives = GetPrevLayersPrimitives(layer); auto deconvLayer = as (layer); @@ -1789,7 +1816,7 @@ void Program::CreateDeconvolutionPrimitive(cldnn::topology& topology, InferenceE std::string deconvLayerName = layer_type_name_ID(layer); - if (deconvLayer->_group >= 16) { + if (deconvLayer->_group >= 16 || layer->insData.size() > 1) { auto deconvPrim = cldnn::deconvolution(deconvLayerName, inputPrimitives[0], weightPrimID, @@ -1797,8 +1824,6 @@ void Program::CreateDeconvolutionPrimitive(cldnn::topology& topology, InferenceE deconvLayer->_group, stride, padding, - false, - 0.0f, CldnnTensorFromIEDims(deconvLayer->outData[0]->getTensorDesc().getDims())); topology.add(deconvPrim); } else { @@ -1808,14 +1833,10 @@ void Program::CreateDeconvolutionPrimitive(cldnn::topology& topology, InferenceE biasPrimID, stride, padding, - false, - 0.0f, CldnnTensorFromIEDims(deconvLayer->outData[0]->getTensorDesc().getDims())); topology.add(deconvPrim); } - primitivesToIRLayersMap[deconvLayerName] = { layer->name }; - primitiveIDs[deconvLayerName] = deconvLayerName; - profilingIDs.push_back(deconvLayerName); + addPrimitiveToProfiler(deconvLayerName, layer); } void Program::CreateCropPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -1858,10 +1879,8 @@ void Program::CreateCropPrimitive(cldnn::topology& topology, InferenceEngine::CN refSize, offSize); - primitivesToIRLayersMap[cropLayerName] = { layer->name }; - primitiveIDs[cropLayerName] = cropLayerName; topology.add(cropPrim); - profilingIDs.push_back(cropLayerName); + addPrimitiveToProfiler(cropLayerName, layer); } void Program::CreateROIPoolingPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -1891,10 +1910,8 @@ void Program::CreateROIPoolingPrimitive(cldnn::topology& topology, InferenceEngi pooled_height, spatial_scale); - primitivesToIRLayersMap[roiPoolingLayerName] = { layer->name }; - primitiveIDs[roiPoolingLayerName] = roiPoolingLayerName; topology.add(roiPoolingPrim); - profilingIDs.push_back(roiPoolingLayerName); + addPrimitiveToProfiler(roiPoolingLayerName, layer); } void Program::CreatePSROIPoolingPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -1954,12 +1971,10 @@ void Program::CreatePSROIPoolingPrimitive(cldnn::topology& topology, InferenceEn spatial_bins_y); topology.add(psROIPoolingPrim); } - primitivesToIRLayersMap[psROIPoolingLayerName] = {layer->name}; - primitiveIDs[psROIPoolingLayerName] = psROIPoolingLayerName; - profilingIDs.push_back(psROIPoolingLayerName); + addPrimitiveToProfiler(psROIPoolingLayerName, layer); } -void Program::CreateCustomLayerPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr & layer, CLDNNCustomLayerPtr customLayer) { +void Program::CreateCustomLayerPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr& layer, CLDNNCustomLayerPtr customLayer) { ValidateLayer(layer, 0); // todo: handling fusing auto genericLayer = as (layer); @@ -2002,14 +2017,15 @@ void Program::CreateCustomLayerPrimitive(cldnn::topology& topology, InferenceEng } // Handle kernel parameters - std::vector kernelParameters; + std::vector kernelParameters; cldnn::format outputFormat(cldnn::format::any); for (const auto& param : customLayer->KernelParams()) { switch (param.type) { case CLDNNCustomLayer::ParamType::Input: { kernelParameters.resize(kernelParameters.size() > size_t(param.paramIndex + 1) ? kernelParameters.size() : size_t(param.paramIndex + 1)); - kernelParameters[param.paramIndex].arg_type = cldnn_arg_type::arg_input; - kernelParameters[param.paramIndex].index = static_cast((param.portIndex >= inputPrimitives.size()) ? -1 : param.portIndex); + kernelParameters[param.paramIndex].type = cldnn::custom_gpu_primitive::arg_input; + kernelParameters[param.paramIndex].index = + static_cast((param.portIndex >= inputPrimitives.size()) ? -1 : param.portIndex); // Handle input reorder if (param.portIndex < inputPrimitives.size() && reorderedInputs[param.portIndex].empty()) { @@ -2022,10 +2038,8 @@ void Program::CreateCustomLayerPrimitive(cldnn::topology& topology, InferenceEng param.format, DataTypeFromPrecision(layer->precision)); - primitivesToIRLayersMap[reorderPrimName] = { layer->name }; topology.add(preprocessPrim); - profilingIDs.push_back(reorderPrimName); - InitProfileInfo(reorderPrimName, "Reorder"); + addInnerPrimitiveToProfiler(reorderPrimName, layer_type_name_ID(layer), layer); reorderedInputs[param.portIndex] = (reorderPrimName); } else { reorderedInputs[param.portIndex] = inputPrimitives[param.portIndex]; @@ -2035,17 +2049,17 @@ void Program::CreateCustomLayerPrimitive(cldnn::topology& topology, InferenceEng break; case CLDNNCustomLayer::ParamType::Output: { kernelParameters.resize(kernelParameters.size() > size_t(param.paramIndex + 1) ? kernelParameters.size() : size_t(param.paramIndex + 1)); - kernelParameters[param.paramIndex].arg_type = cldnn_arg_type::arg_output; + kernelParameters[param.paramIndex].type = cldnn::custom_gpu_primitive::arg_output; kernelParameters[param.paramIndex].index = - static_cast((param.portIndex >= inputPrimitives.size()) ? -1 : param.portIndex); + static_cast((param.portIndex >= inputPrimitives.size()) ? -1 : param.portIndex); outputFormat = param.format; } break; case CLDNNCustomLayer::ParamType::Data: { kernelParameters.resize(kernelParameters.size() > size_t(param.paramIndex + 1) ? kernelParameters.size() : size_t(param.paramIndex + 1)); - kernelParameters[param.paramIndex].arg_type = cldnn_arg_type::arg_input; + kernelParameters[param.paramIndex].type = cldnn::custom_gpu_primitive::arg_input; kernelParameters[param.paramIndex].index = - static_cast((blobIndex.find(param.blobName) == blobIndex.end()) ? -1 : blobIndex.at(param.blobName)); + static_cast((blobIndex.find(param.blobName) == blobIndex.end()) ? -1 : blobIndex.at(param.blobName)); } break; default: @@ -2121,6 +2135,7 @@ void Program::CreateCustomLayerPrimitive(cldnn::topology& topology, InferenceEng gws, lws); + auto prevLayerName = genericLayerName; if (outputLayout.format != cldnn::format::any && p_currentOutputs.find(genericLayerName) == p_currentOutputs.end()) { // Handle output reorder @@ -2131,17 +2146,12 @@ void Program::CreateCustomLayerPrimitive(cldnn::topology& topology, InferenceEng genericLayerName, m_defaultFormat, customPrim.output_layout.data_type)); - primitivesToIRLayersMap[reorderPrimName] = { layer->name }; - primitiveIDs[genericLayerName] = reorderPrimName; - primitiveIDs[reorderPrimName] = reorderPrimName; - profilingIDs.push_back(reorderPrimName); - InitProfileInfo(reorderPrimName, "Reorder"); - } else { - primitiveIDs[genericLayerName] = genericLayerName; + prevLayerName = reorderPrimName; + addInnerPrimitiveToProfiler(reorderPrimName, layer_type_name_ID(layer), layer); } - primitivesToIRLayersMap[genericLayerName] = { layer->name }; topology.add(customPrim); - profilingIDs.push_back(genericLayerName); + addPrimitiveToProfiler(genericLayerName, layer); + primitiveIDs[genericLayerName] = prevLayerName; } void Program::CreateSimplerNMSPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -2174,14 +2184,12 @@ void Program::CreateSimplerNMSPrimitive(cldnn::topology& topology, InferenceEngi { 0.5f, 1.0f, 2.0f }, // ratios for the SimplerNMS variant scale); - primitivesToIRLayersMap[simpleNMSLayerName] = { layer->name }; - primitiveIDs[simpleNMSLayerName] = simpleNMSLayerName; topology.add(simpleNMSPrim); - profilingIDs.push_back(simpleNMSLayerName); + addPrimitiveToProfiler(simpleNMSLayerName, layer); } void Program::CreateEltwisePrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { - ValidateEltwiseLayer(layer); + ValidateLayer(layer, {}); auto eltwiseLayer = as (layer); auto inputPrimitives = GetPrevLayersPrimitives(layer); @@ -2228,8 +2236,6 @@ void Program::CreateEltwisePrimitive(cldnn::topology& topology, InferenceEngine: } topology.add(eltwisePrim); - primitivesToIRLayersMap[eltwiseLayerName] = { layer->name }; - profilingIDs.push_back(eltwiseLayerName); // Cast output data type if it differs from operation precision auto operationPrecision = layer->precision; @@ -2246,14 +2252,12 @@ void Program::CreateEltwisePrimitive(cldnn::topology& topology, InferenceEngine: DataTypeFromPrecision(outputPrecision)); topology.add(reorderPrim); - primitivesToIRLayersMap[reorderLayerName] = { layer->name }; - profilingIDs.push_back(reorderLayerName); - primitiveIDs[reorderLayerName] = reorderLayerName; + addInnerPrimitiveToProfiler(reorderLayerName, eltwiseLayerName, layer); lastLayerName = reorderLayerName; } - primitiveIDs[eltwiseLayerName] = lastLayerName; + addPrimitiveToProfiler(eltwiseLayerName, layer, lastLayerName); } inline cldnn::concatenation::concatenation_axis ConcatAxisFromIEAxis(unsigned axis, unsigned sz) { @@ -2301,10 +2305,8 @@ void Program::CreateConcatenatePrimitive(cldnn::topology& topology, InferenceEng ConcatAxisFromIEAxis(concatLayer->_axis, concatLayer->input().get()->getTensorDesc().getDims().size())); - primitivesToIRLayersMap[concatLayerName] = { layer->name }; - primitiveIDs[concatLayerName] = concatLayerName; topology.add(concatPrim); - profilingIDs.push_back(concatLayerName); + addPrimitiveToProfiler(concatLayerName, layer); } void Program::CreateSplitPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -2445,8 +2447,6 @@ void Program::CreateFusedSplitConvMergePrimitive(cldnn::topology& topology, Infe stride, padding, dilation, - false, - 0.0f, CldnnTensorFromIEDims(concatLayer->outData[0]->getTensorDesc().getDims())); layer = concatLayerPtr; @@ -2473,20 +2473,18 @@ void Program::CreatePowerPrimitive(cldnn::topology& topology, InferenceEngine::C std::string powerLayerName = layer_type_name_ID(layer); std::string linearLayerName = powerLayerName + "_linear_activation"; - auto linearActivationPrim = cldnn::activation(linearLayerName, inputPrimitives[0], activation_linear, { scale, shift }); + auto linearActivationPrim = cldnn::activation(linearLayerName, inputPrimitives[0], cldnn::activation_func::linear, { scale, shift }); topology.add(linearActivationPrim); - profilingIDs.push_back(linearLayerName); - primitiveIDs[linearLayerName] = linearLayerName; + addInnerPrimitiveToProfiler(linearLayerName, powerLayerName, layer); - auto powActivationPrim = cldnn::activation(powerLayerName, linearLayerName, activation_pow, { power, 0.f }); + auto powActivationPrim = cldnn::activation(powerLayerName, linearLayerName, cldnn::activation_func::pow, { power, 0.f }); topology.add(powActivationPrim); - profilingIDs.push_back(powerLayerName); - primitiveIDs[powerLayerName] = powerLayerName; + addPrimitiveToProfiler(powerLayerName, layer); } else { std::string powerLayerName = layer_type_name_ID(layer); if ((powerLayer->scale == 1.0f) && (powerLayer->offset == 0.0f)) { if (powerLayer->power == 0.5f) { - auto activationPrim = cldnn::activation(powerLayerName, inputPrimitives[0], activation_sqrt); + auto activationPrim = cldnn::activation(powerLayerName, inputPrimitives[0], cldnn::activation_func::sqrt); topology.add(activationPrim); profilingIDs.push_back(powerLayerName); primitiveIDs[powerLayerName] = powerLayerName; @@ -2520,7 +2518,7 @@ void Program::CreatePowerPrimitive(cldnn::topology& topology, InferenceEngine::C profilingIDs.push_back(powerLayerName); if (powerLayer->power == 0.5f) { - auto activationPrim = cldnn::activation(powerLayerName + "_sqrt", powerLayerName, activation_sqrt); + auto activationPrim = cldnn::activation(powerLayerName + "_sqrt", powerLayerName, cldnn::activation_func::sqrt); topology.add(activationPrim); profilingIDs.push_back(powerLayerName + "_sqrt"); } @@ -2537,14 +2535,12 @@ void Program::CreateSoftMaxPrimitive(cldnn::topology& topology, InferenceEngine: auto softmaxPrim = cldnn::softmax(softmaxLayerName, inputPrimitives[0], SoftmaxDimensionFromIEAxis(softmaxLayer)); - primitivesToIRLayersMap[softmaxLayerName] = { layer->name }; - primitiveIDs[softmaxLayerName] = softmaxLayerName; topology.add(softmaxPrim); - profilingIDs.push_back(softmaxLayerName); + addPrimitiveToProfiler(softmaxLayerName, layer); } void Program::CreateFullyConnectedPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { - ValidateLayer(layer, 1); + ValidateLayer(layer, {1, 2, 3}); auto inputPrimitives = GetPrevLayersPrimitives(layer); auto fcLayer = as (layer); @@ -2586,12 +2582,17 @@ void Program::CreateFullyConnectedPrimitive(cldnn::topology& topology, Inference break; default: THROW_CLDNN_EXCEPTION("Invalid data dimensions"); } - auto weightsBlob = getBlob(layer, "weights"); - cldnn::layout fcwLayout( - DataTypeFromPrecision(weightsBlob->getTensorDesc().getPrecision()), - m_defaultFormat, - weightsDims); - weightsPrimID = CreatePrimitiveFromBlob(topology, weightsPrimID, weightsBlob, fcwLayout); + auto weightsBlob = getBlobOrNull(layer, "weights"); + if (weightsBlob != nullptr) { + cldnn::layout fcwLayout( + DataTypeFromPrecision(weightsBlob->getTensorDesc().getPrecision()), + m_defaultFormat, + weightsDims); + weightsPrimID = CreatePrimitiveFromBlob(topology, weightsPrimID, weightsBlob, fcwLayout); + } else { + auto wei_name = layer_type_name_ID(layer->insData[1].lock()->getCreatorLayer().lock()); + weightsPrimID = wei_name; + } auto inputPrecision = layer->insData[0].lock()->getPrecision(); auto inputQuantized = @@ -2612,9 +2613,7 @@ void Program::CreateFullyConnectedPrimitive(cldnn::topology& topology, Inference auto fcPrim = cldnn::fully_connected(fcLayerName, inputPrimitives[0], weightsPrimID, - biasesPrimID, - false, - 0.0f); + biasesPrimID); // Add quantization if (!wQuantizationPrimID.empty()) { @@ -2639,6 +2638,7 @@ void Program::CreateFullyConnectedPrimitive(cldnn::topology& topology, Inference auto reshapePrim = cldnn::reshape(newWeightsPrimID, weightsPrimID, newShape); topology.add(reshapePrim); + addInnerPrimitiveToProfiler(newWeightsPrimID, fcLayerName, layer); weightsPrimID = newWeightsPrimID; } @@ -2648,22 +2648,19 @@ void Program::CreateFullyConnectedPrimitive(cldnn::topology& topology, Inference { biasesPrimID }, cldnn::tensor(1), cldnn::tensor(0), - cldnn::tensor(1), - false, - 0.f); + cldnn::tensor(1)); convPrim.output_data_type = DataTypeFromPrecision(outputPrecision); // TODO Fix in clDNN - there is no reason this should be immutable, most other fields are mutable - auto& wq = const_cast&>(convPrim.weights_quantization_factors.ref()); + auto& wq = const_cast&>(convPrim.weights_quantization_factors); wq.insert(wq.end(), wQuantizationPrimID.begin(), wQuantizationPrimID.end()); topology.add(convPrim); + addInnerPrimitiveToProfiler(convPrim, fcLayerName, layer); } - primitivesToIRLayersMap[fcLayerName] = { layer->name }; - primitiveIDs[fcLayerName] = fcLayerName; - profilingIDs.push_back(fcLayerName); + addPrimitiveToProfiler(fcLayerName, layer); } void Program::CreatePoolingPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -2785,19 +2782,17 @@ void Program::CreateLRNPrimitive(cldnn::topology& topology, InferenceEngine::CNN static_cast(lrnLayer->_k), lrnLayer->_alpha, lrnLayer->_beta, - lrnLayer->_isAcrossMaps ? cldnn_lrn_norm_region_across_channel : cldnn_lrn_norm_region_within_channel); + lrnLayer->_isAcrossMaps ? cldnn::lrn_norm_region_across_channel : cldnn::lrn_norm_region_within_channel); - primitivesToIRLayersMap[lrnLayerName] = { layer->name }; - primitiveIDs[lrnLayerName] = lrnLayerName; topology.add(lrnPrim); - profilingIDs.push_back(lrnLayerName); + addPrimitiveToProfiler(lrnLayerName, layer); } void Program::CreateActivationPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer, const LayerType type) { ValidateLayer(layer, 1); auto inputPrimitives = GetPrevLayersPrimitives(layer); - cldnn_activation_additional_params params{ 0.0f, 0.0f }; - cldnn_activation_func func = cldnn_activation_func_t::activation_none; + cldnn::activation_additional_params params{ 0.0f, 0.0f }; + cldnn::activation_func func = cldnn::activation_func::none; LayerType activationType; if (type == Activation) { @@ -2829,142 +2824,142 @@ void Program::CreateActivationPrimitive(cldnn::topology& topology, InferenceEngi switch (activationType) { case TanH: { - func = cldnn_activation_func_t::activation_hyperbolic_tan; + func = cldnn::activation_func::hyperbolic_tan; break; } case ELU: { - func = cldnn_activation_func_t::activation_elu; + func = cldnn::activation_func::elu; params.a = layer->GetParamAsFloat("alpha", 1.0f); break; } case Sigmoid: { - func = cldnn_activation_func_t::activation_logistic; + func = cldnn::activation_func::logistic; break; } case ReLU: { auto negative_slope = layer->GetParamAsFloat("negative_slope", 0.0f); if (negative_slope == 0.f) { - func = cldnn_activation_func_t::activation_relu; + func = cldnn::activation_func::relu; } else { - func = cldnn_activation_func_t::activation_relu_negative_slope; + func = cldnn::activation_func::relu_negative_slope; params.a = negative_slope; } break; } case ReLU6: { - func = cldnn_activation_func_t::activation_clamp; + func = cldnn::activation_func::clamp; params.b = layer->GetParamAsFloat("n", 6.0f); break; } case Clamp: { - func = cldnn_activation_func_t::activation_clamp; + func = cldnn::activation_func::clamp; params.a = layer->GetParamAsFloat("min"); params.b = layer->GetParamAsFloat("max"); break; } case Exp: { - func = cldnn_activation_func_t::activation_exp; + func = cldnn::activation_func::exp; break; } case Not: { - func = cldnn_activation_func_t::activation_not; + func = cldnn::activation_func::negation; break; } case Asin: { - func = cldnn_activation_func_t::activation_asin; + func = cldnn::activation_func::asin; break; } case Asinh: { - func = cldnn_activation_func_t::activation_asinh; + func = cldnn::activation_func::asinh; break; } case Acos: { - func = cldnn_activation_func_t::activation_acos; + func = cldnn::activation_func::acos; break; } case Acosh: { - func = cldnn_activation_func_t::activation_acosh; + func = cldnn::activation_func::acosh; break; } case Atan: { - func = cldnn_activation_func_t::activation_atan; + func = cldnn::activation_func::atan; break; } case Atanh: { - func = cldnn_activation_func_t::activation_atanh; + func = cldnn::activation_func::atanh; break; } case Abs: { - func = cldnn_activation_func_t::activation_abs; + func = cldnn::activation_func::abs; break; } case Floor: { - func = cldnn_activation_func_t::activation_floor; + func = cldnn::activation_func::floor; break; } case Ceil: { - func = cldnn_activation_func_t::activation_ceil; + func = cldnn::activation_func::ceil; break; } case Erf: { - func = cldnn_activation_func_t::activation_erf; + func = cldnn::activation_func::erf; break; } case HardSigmoid: { - func = cldnn_activation_func_t::activation_hard_sigmoid; + func = cldnn::activation_func::hard_sigmoid; break; } case Log: { - func = cldnn_activation_func_t::activation_log; + func = cldnn::activation_func::log; break; } case Neg: { - func = cldnn_activation_func_t::activation_negative; + func = cldnn::activation_func::negative; break; } case Reciprocal: { - func = cldnn_activation_func_t::activation_reciprocal; + func = cldnn::activation_func::reciprocal; break; } case Selu: { - func = cldnn_activation_func_t::activation_selu; + func = cldnn::activation_func::selu; break; } case SoftPlus: { - func = cldnn_activation_func_t::activation_softplus; + func = cldnn::activation_func::softplus; break; } case SoftSign: { - func = cldnn_activation_func_t::activation_softsign; + func = cldnn::activation_func::softsign; break; } case Tan: { - func = cldnn_activation_func_t::activation_tan; + func = cldnn::activation_func::tan; break; } default: @@ -2974,10 +2969,8 @@ void Program::CreateActivationPrimitive(cldnn::topology& topology, InferenceEngi std::string layerName = layer_type_name_ID(layer); auto activationPrimitive = cldnn::activation(layerName, inputPrimitives[0], func, params); - primitivesToIRLayersMap[layerName] = { layer->name }; - primitiveIDs[layerName] = layerName; topology.add(activationPrimitive); - profilingIDs.push_back(layerName); + addPrimitiveToProfiler(layerName, layer); } void Program::CreateCopyPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -2996,7 +2989,8 @@ void Program::CreateUpsamplingPrimitive(cldnn::topology& topology, InferenceEngi ValidateLayer(layer, 1); auto inputPrimitives = GetPrevLayersPrimitives(layer); auto upsamplingLayer = as (layer); - uint32_t scale = upsamplingLayer->GetParamAsUInt("scale"); + + auto output_tensor = CldnnTensorFromIEDims(upsamplingLayer->outData[0]->getTensorDesc().getDims()); uint32_t numFilter = upsamplingLayer->GetParamAsUInt("num_filter"); std::string sampleType = upsamplingLayer->GetParamAsString("sample_type"); @@ -3004,14 +2998,12 @@ void Program::CreateUpsamplingPrimitive(cldnn::topology& topology, InferenceEngi auto upsamplingPrim = cldnn::upsampling( upsamplingLayerName, inputPrimitives[0], - scale, + output_tensor, numFilter, UpsamplingTypeFromString(sampleType)); - primitivesToIRLayersMap[upsamplingLayerName] = { layer->name }; - primitiveIDs[upsamplingLayerName] = upsamplingLayerName; topology.add(upsamplingPrim); - profilingIDs.push_back(upsamplingLayerName); + addPrimitiveToProfiler(upsamplingLayerName, layer); } void Program::CreateResamplePrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3020,15 +3012,15 @@ void Program::CreateResamplePrimitive(cldnn::topology& topology, InferenceEngine auto resampleLayer = as (layer); size_t inFeatures = 1; - float scale = 1.0f; std::shared_ptr insData0 = layer->insData[0].lock(); IE_ASSERT(insData0 != nullptr); auto insData0dims = insData0->getTensorDesc().getDims(); auto outDims = layer->outData[0]->getTensorDesc().getDims(); + auto outTensor = CldnnTensorFromIEDims(outDims); if (insData0dims.size() > 1) { inFeatures = insData0dims[1]; - scale = static_cast(outDims.back()) / static_cast(insData0dims.back()); + auto scale = static_cast(outDims.back()) / static_cast(insData0dims.back()); if (scale < 1.0f) { THROW_CLDNN_EXCEPTION("Unsupported scale in layer " + layer->name); } @@ -3047,14 +3039,12 @@ void Program::CreateResamplePrimitive(cldnn::topology& topology, InferenceEngine auto upsamplingPrim = cldnn::upsampling( resampleLayerName, inputPrimitives[0], - scale, + outTensor, inFeatures, cldnnSampleType); - primitivesToIRLayersMap[resampleLayerName] = { layer->name }; - primitiveIDs[resampleLayerName] = resampleLayerName; topology.add(upsamplingPrim); - profilingIDs.push_back(resampleLayerName); + addPrimitiveToProfiler(resampleLayerName, layer); } void Program::CreateYOLO2RegionPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3083,10 +3073,8 @@ void Program::CreateYOLO2RegionPrimitive(cldnn::topology& topology, InferenceEng mask_size, do_softmax); - primitivesToIRLayersMap[YOLOregionLayerName] = { layer->name }; - primitiveIDs[YOLOregionLayerName] = YOLOregionLayerName; topology.add(regionPrim); - profilingIDs.push_back(YOLOregionLayerName); + addPrimitiveToProfiler(YOLOregionLayerName, layer); } void Program::CreateYOLO2ReorgPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3101,10 +3089,8 @@ void Program::CreateYOLO2ReorgPrimitive(cldnn::topology& topology, InferenceEngi inputPrimitives[0], stride); - primitivesToIRLayersMap[YOLOreorgLayerName] = { layer->name }; - primitiveIDs[YOLOreorgLayerName] = YOLOreorgLayerName; topology.add(reorgPrim); - profilingIDs.push_back(YOLOreorgLayerName); + addPrimitiveToProfiler(YOLOreorgLayerName, layer); } void Program::CreateArgMaxMinPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer, const LayerType type) { @@ -3160,10 +3146,8 @@ void Program::CreateArgMaxMinPrimitive(cldnn::topology& topology, InferenceEngin top_k, chosen_axis); - primitivesToIRLayersMap[ArgMaxLayerName] = { layer->name }; - primitiveIDs[ArgMaxLayerName] = ArgMaxLayerName; topology.add(argmaxPrim); - profilingIDs.push_back(ArgMaxLayerName); + addPrimitiveToProfiler(ArgMaxLayerName, layer); } void Program::CreateTopKPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3252,7 +3236,7 @@ void Program::CreateTopKPrimitive(cldnn::topology& topology, InferenceEngine::CN topology.add(argmax_mutable_prim); inputPrimitives.push_back(argmax_mutable_id_w); - std::string ArgMaxLayerName = layer_type_lower(layer) + ":" + layer->outData[1]->getName(); + std::string ArgMaxLayerName = layer_type_lower(layer) + ":" + layer->outData[0]->getName(); auto argmaxPrim = cldnn::arg_max_min( ArgMaxLayerName, inputPrimitives, @@ -3260,19 +3244,18 @@ void Program::CreateTopKPrimitive(cldnn::topology& topology, InferenceEngine::CN top_k, chosen_axis, stype, - true); + true, + cldnn::padding({0, 0, 0, 0}, 0), + DataTypeFromPrecision(layer->precision)); - primitivesToIRLayersMap[ArgMaxLayerName] = {layer->name}; - primitiveIDs[ArgMaxLayerName] = ArgMaxLayerName; topology.add(argmaxPrim); - cldnn::primitive_id argmax_mutable_id_r = layer_type_lower(layer) + ":" + layer->outData[0]->getName(); + cldnn::primitive_id argmax_mutable_id_r = layer_type_lower(layer) + ":" + layer->outData[1]->getName(); auto argmax_mutable_prim_r = cldnn::mutable_data(argmax_mutable_id_r, {ArgMaxLayerName}, shared_memory); primitivesToIRLayersMap[argmax_mutable_id_r] = {layer->name}; primitiveIDs[argmax_mutable_id_r] = argmax_mutable_id_r; topology.add(argmax_mutable_prim_r); - - profilingIDs.push_back(ArgMaxLayerName); + addPrimitiveToProfiler(ArgMaxLayerName, layer); } else if (layer->outData.size() == 1) { std::string ArgMaxLayerName = layer_type_lower(layer) + ":" + layer->outData[0]->getName(); auto argmaxPrim = cldnn::arg_max_min( @@ -3282,12 +3265,12 @@ void Program::CreateTopKPrimitive(cldnn::topology& topology, InferenceEngine::CN top_k, chosen_axis, stype, - true); + true, + cldnn::padding({0, 0, 0, 0}, 0), + DataTypeFromPrecision(layer->precision)); - primitivesToIRLayersMap[ArgMaxLayerName] = {layer->name}; - primitiveIDs[ArgMaxLayerName] = ArgMaxLayerName; topology.add(argmaxPrim); - profilingIDs.push_back(ArgMaxLayerName); + addPrimitiveToProfiler(ArgMaxLayerName, layer); } else { THROW_IE_EXCEPTION << layer->name << " Incorrect TopK outputs number"; } @@ -3333,10 +3316,8 @@ void Program::CreateMaxUnpoolingPrimitive(cldnn::topology& topology, InferenceEn (cldnn::tensor) cldnn::spatial(kernel_size, kernel_size), // size (cldnn::tensor) cldnn::spatial(stride, stride) ); // stride - primitivesToIRLayersMap[UnpoolingLayerName] = { layer->name }; - primitiveIDs[UnpoolingLayerName] = UnpoolingLayerName; topology.add(unpoolingPrim); - profilingIDs.push_back(UnpoolingLayerName); + addPrimitiveToProfiler(UnpoolingLayerName, layer); } void Program::CreateMVNPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3356,10 +3337,8 @@ void Program::CreateMVNPrimitive(cldnn::topology& topology, InferenceEngine::CNN normalize_variance, eps); - primitivesToIRLayersMap[MvnLayerName] = { layer->name }; - primitiveIDs[MvnLayerName] = MvnLayerName; topology.add(mvnPrim); - profilingIDs.push_back(MvnLayerName); + addPrimitiveToProfiler(MvnLayerName, layer); } void Program::CreateTilePrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3398,10 +3377,8 @@ void Program::CreateTilePrimitive(cldnn::topology& topology, InferenceEngine::CN cldnnAxisFromIE(axis), tiles); - primitivesToIRLayersMap[tileLayerName] = { layer->name }; - primitiveIDs[tileLayerName] = tileLayerName; topology.add(tilePrim); - profilingIDs.push_back(tileLayerName); + addPrimitiveToProfiler(tileLayerName, layer); } void Program::CreatePadPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3409,30 +3386,10 @@ void Program::CreatePadPrimitive(cldnn::topology& topology, InferenceEngine::CNN auto inputPrimitives = GetPrevLayersPrimitives(layer); auto padLayer = as (layer); - auto PadTensorFromArgs = [](const std::string &s) -> cldnn::tensor { - std::stringstream ss(s); - std::string item; - std::vector elems; - while (std::getline(ss, item, ',')) { - elems.push_back(static_cast(std::atoll(item.c_str()))); - } - - while (elems.size() < 4) { - elems.push_back(0); - } - - // Swap x and y - auto tmp = elems[2]; - elems[2] = elems[3]; - elems[3] = tmp; - - return cldnn::tensor(elems, 0); - }; - - auto pads_begin = PadTensorFromArgs(padLayer->GetParamAsString("pads_begin")); - auto pads_end = PadTensorFromArgs(padLayer->GetParamAsString("pads_end")); + auto pads_begin = cldnn::tensor(PermuteIEDimsToCldnnOrder(padLayer->GetParamAsInts("pads_begin")), 0); + auto pads_end = cldnn::tensor(PermuteIEDimsToCldnnOrder(padLayer->GetParamAsInts("pads_end")), 0); std::string mode = padLayer->GetParamAsString("pad_mode"); - float pad_value = padLayer->GetParamAsFloat("pad_value", 0.0f); + float pad_value = padLayer->GetParamAsFloat("pad_value", 0.0f); cldnn::border_type border_mode; if (mode == "constant") @@ -3455,10 +3412,8 @@ void Program::CreatePadPrimitive(cldnn::topology& topology, InferenceEngine::CNN border_mode, pad_value); - primitivesToIRLayersMap[padLayerName] = { layer->name }; - primitiveIDs[padLayerName] = padLayerName; topology.add(tilePrim); - profilingIDs.push_back(padLayerName); + addPrimitiveToProfiler(padLayerName, layer); } void Program::AddConstantBlobInput(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3486,8 +3441,37 @@ void Program::AddConstantBlobInput(cldnn::topology& topology, InferenceEngine::C break; case 1: constTensor = cldnn::tensor(1, TensorValue(constDims[0]), 1, 1); break; + case 0: + if (constBlob->size() != 1) + THROW_CLDNN_EXCEPTION("Invalid constant blob with 0-dim shape"); + + constTensor = cldnn::tensor(1, 1, 1, 1); + break; default: THROW_CLDNN_EXCEPTION("Invalid constant blob dimensions"); } + + if (GetNextLayers(layer->outData[0]).size() == 1) { + auto next = GetNextSingleLayer(layer->outData[0]); + auto nextConv = tryAs(next); + auto nextDeconv = tryAs(next); + auto nextDefConv = tryAs(next); + auto nextBinConv = tryAs(next); + + bool isWeights = (nextConv != nullptr && nextConv->insData.size() > 1 && nextConv->insData[1].lock() == layer->outData[0]) || + (nextDeconv != nullptr && nextDeconv->insData.size() > 1 && nextDeconv->insData[1].lock() == layer->outData[0]) || + (nextDefConv != nullptr && nextDefConv->insData.size() > 2 && nextDefConv->insData[2].lock() == layer->outData[0]) || + (nextBinConv != nullptr && nextBinConv->insData.size() > 1 && nextBinConv->insData[1].lock() == layer->outData[0]); + + // TODO: Need to change format of weights that is passed to cldnn + // Group dimension should be a part of size tensor and split should be done only inside cldnn (if necessary) + // Unless this is implemented we have to divide feature dimension by group size for const inputs that represent weights + // in order to have shape expected by cldnn + if (isWeights) { + auto group = next->GetParamAsUInt("group", 1); + constTensor.feature[0] /= group; + } + } + cldnn::layout constLayout = cldnn::layout( DataTypeFromPrecision(layer->blobs.begin()->second->getTensorDesc().getPrecision()), FormatFromLayout(constBlob->getTensorDesc().getLayout()), @@ -3495,12 +3479,11 @@ void Program::AddConstantBlobInput(cldnn::topology& topology, InferenceEngine::C cldnn::primitive_id initialconstPrimID = layer_type_name_ID(layer); cldnn::primitive_id constPrimID = CreatePrimitiveFromBlob(topology, initialconstPrimID, constBlob, constLayout); - primitiveIDs[initialconstPrimID] = constPrimID; - primitivesToIRLayersMap[initialconstPrimID] = { layer->name }; + addPrimitiveToProfiler(initialconstPrimID, layer, constPrimID); } void Program::CreateConvolutionPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { - ValidateLayer(layer, 1); + ValidateLayer(layer, {1, 2, 3}); auto inputPrimitives = GetPrevLayersPrimitives(layer); auto convLayer = as(layer); std::string convLayerName = layer_type_name_ID(layer); @@ -3541,8 +3524,6 @@ void Program::CreateConvolutionPrimitive(cldnn::topology& topology, InferenceEng stride, padding, dilation, - false, - 0.0f, CldnnTensorFromIEDims(convLayer->outData[0]->getTensorDesc().getDims())); if (convLayer->precision == Precision::I8 || convLayer->precision == Precision::U8) { @@ -3550,7 +3531,7 @@ void Program::CreateConvolutionPrimitive(cldnn::topology& topology, InferenceEng convPrim.output_data_type = DataTypeFromPrecision(convLayer->outData[0]->getTensorDesc().getPrecision()); } - if (convLayer->_group >= 16) { + if (convLayer->_group >= 16 || layer->insData.size() > 1) { convPrim.groups = convLayer->_group; } @@ -3559,14 +3540,12 @@ void Program::CreateConvolutionPrimitive(cldnn::topology& topology, InferenceEng if (!wScalePrimID.empty()) { // TODO Fix in clDNN - there is no reason this should be immutable, most other fields are mutable - auto& wq = const_cast&>(convPrim.weights_quantization_factors.ref()); + auto& wq = const_cast&>(convPrim.weights_quantization_factors); wq.insert(wq.end(), wScalePrimID.begin(), wScalePrimID.end()); } topology.add(convPrim); - primitivesToIRLayersMap[convLayerName] = { layer->name }; - primitiveIDs[convLayerName] = convLayerName; - profilingIDs.push_back(convLayerName); + addPrimitiveToProfiler(convLayerName, layer); } void Program::CreateDeformableConvolutionPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3604,9 +3583,7 @@ void Program::CreateDeformableConvolutionPrimitive(cldnn::topology& topology, In dilation, CldnnTensorFromIEDims(defConvLayer->outData[0]->getTensorDesc().getDims())); topology.add(defConvPrim); - primitivesToIRLayersMap[defConvLayerName] = { layer->name }; - primitiveIDs[defConvLayerName] = defConvLayerName; - profilingIDs.push_back(defConvLayerName); + addPrimitiveToProfiler(defConvLayerName, layer); } else { std::string defConvLayerNameInterp = layer_type_name_ID(layer)+"_interp"; std::string defConvLayerNameConv = layer_type_name_ID(layer); @@ -3621,9 +3598,7 @@ void Program::CreateDeformableConvolutionPrimitive(cldnn::topology& topology, In CldnnTensorFromIEDims(defConvLayer->outData[0]->getTensorDesc().getDims()), kernel); topology.add(defConvPrimInterp); - primitivesToIRLayersMap[defConvLayerNameInterp] = { layer->name }; - primitiveIDs[defConvLayerNameInterp] = defConvLayerNameInterp; - profilingIDs.push_back(defConvLayerNameInterp); + addInnerPrimitiveToProfiler(defConvLayerNameInterp, defConvLayerNameConv, layer); auto defConvPrim = cldnn::deformable_conv(defConvLayerNameConv, defConvLayerNameInterp, weightPrimID, @@ -3631,9 +3606,7 @@ void Program::CreateDeformableConvolutionPrimitive(cldnn::topology& topology, In defConvLayer->_group, CldnnTensorFromIEDims(defConvLayer->outData[0]->getTensorDesc().getDims())); topology.add(defConvPrim); - primitivesToIRLayersMap[defConvLayerNameConv] = { layer->name }; - primitiveIDs[defConvLayerNameConv] = defConvLayerNameConv; - profilingIDs.push_back(defConvLayerNameConv); + addPrimitiveToProfiler(defConvLayerNameConv, layer); } } @@ -3670,10 +3643,8 @@ void Program::CreateBinaryConvolutionPrimitive(cldnn::topology& topology, Infere binaryConvLayer->_pad_value, calc_precision); - primitivesToIRLayersMap[binaryConvLayerName] = { layer->name }; - primitiveIDs[binaryConvLayerName] = binaryConvLayerName; topology.add(binaryConvPrim); - profilingIDs.push_back(binaryConvLayerName); + addPrimitiveToProfiler(binaryConvLayerName, layer); } void Program::CreateQuantizePrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3697,10 +3668,8 @@ void Program::CreateQuantizePrimitive(cldnn::topology& topology, InferenceEngine output_high_id, levels); - primitivesToIRLayersMap[quantizeLayerName] = { layer->name }; - primitiveIDs[quantizeLayerName] = quantizeLayerName; topology.add(quantizationPrim); - profilingIDs.push_back(quantizeLayerName); + addPrimitiveToProfiler(quantizeLayerName, layer); } void Program::CreateGatherPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3733,10 +3702,26 @@ void Program::CreateGatherPrimitive(cldnn::topology& topology, InferenceEngine:: cldnnAxisFromIE(axis), CldnnTensorFromIEDims(gatherLayer->outData[0]->getTensorDesc().getDims())); - primitivesToIRLayersMap[gatherLayerName] = { layer->name }; - primitiveIDs[gatherLayerName] = gatherLayerName; topology.add(gatherPrim); - profilingIDs.push_back(gatherLayerName); + addPrimitiveToProfiler(gatherLayerName, layer); +} + +void CLDNNPlugin::Program::CreateGatherTreePrimitive(cldnn::topology & topology, InferenceEngine::CNNLayerPtr & layer) { + ValidateLayer(layer, 4); + + auto inputPrimitives = GetPrevLayersPrimitives(layer); + auto gatherTreeLayer = as(layer); + + std::string gatherTreeLayerName = layer_type_name_ID(layer); + auto gatherTreePrim = cldnn::gather_tree( + gatherTreeLayerName, + inputPrimitives[0], + inputPrimitives[1], + inputPrimitives[2], + inputPrimitives[3]); + + topology.add(gatherTreePrim); + addPrimitiveToProfiler(gatherTreeLayerName, layer); } void Program::CreateDepthToSpacePrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3762,10 +3747,8 @@ void Program::CreateDepthToSpacePrimitive(cldnn::topology& topology, InferenceEn inputPrimitives[0], blockSize); - primitivesToIRLayersMap[depthToSpaceName] = { layer->name }; - primitiveIDs[depthToSpaceName] = depthToSpaceName; topology.add(depthToSpacePrim); - profilingIDs.push_back(depthToSpaceName); + addPrimitiveToProfiler(depthToSpaceName, layer); } void Program::CreateShuffleChannelsPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3799,10 +3782,8 @@ void Program::CreateShuffleChannelsPrimitive(cldnn::topology& topology, Inferenc group, axis); - primitivesToIRLayersMap[shuffleChannelsName] = { layer->name }; - primitiveIDs[shuffleChannelsName] = shuffleChannelsName; topology.add(shuffleChannelsPrim); - profilingIDs.push_back(shuffleChannelsName); + addPrimitiveToProfiler(shuffleChannelsName, layer); } void Program::CreateStridedSlicePrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3824,10 +3805,8 @@ void Program::CreateStridedSlicePrimitive(cldnn::topology& topology, InferenceEn inputPrimitives[0], inputPrimitives[1], inputPrimitives[2], inputPrimitives[3], begin_mask, end_mask, new_axis_mask, shrink_axis_mask); - primitivesToIRLayersMap[stridedSliceLayerName] = { layer->name }; - primitiveIDs[stridedSliceLayerName] = stridedSliceLayerName; topology.add(stridedSlicePrim); - profilingIDs.push_back(stridedSliceLayerName); + addPrimitiveToProfiler(stridedSliceLayerName, layer); } void Program::CreateReverseSequencePrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3869,10 +3848,8 @@ void Program::CreateReverseSequencePrimitive(cldnn::topology& topology, Inferenc seq_axis, batch_axis); - primitivesToIRLayersMap[reverseSequenceLayerName] = { layer->name }; - primitiveIDs[reverseSequenceLayerName] = reverseSequenceLayerName; topology.add(reverseSequencePrim); - profilingIDs.push_back(reverseSequence->name); + addPrimitiveToProfiler(reverseSequenceLayerName, layer); } void Program::CreateBroadcastPrimitive(cldnn::topology &topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3887,9 +3864,8 @@ void Program::CreateBroadcastPrimitive(cldnn::topology &topology, InferenceEngin inputPrimitives[0], CldnnTensorFromIEDims(broadcast->outData[0]->getTensorDesc().getDims())); - primitiveIDs[broadcastLayerName] = broadcastLayerName; topology.add(broadcastPrim); - profilingIDs.push_back(broadcast->name); + addPrimitiveToProfiler(broadcastLayerName, layer); } void Program::CreateGemmPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -3933,9 +3909,7 @@ void Program::CreateGemmPrimitive(cldnn::topology& topology, InferenceEngine::CN auto reorderPrim = cldnn::reorder(reorderName, inputPrimitives[i], targetFormat, targetDatatype); topology.add(reorderPrim); - primitivesToIRLayersMap[reorderName] = { layer->name }; - profilingIDs.push_back(reorderName); - primitiveIDs[reorderName] = reorderName; + addInnerPrimitiveToProfiler(reorderName, gemmLayerName, layer); inputPrimitives[i] = reorderName; } @@ -3952,9 +3926,7 @@ void Program::CreateGemmPrimitive(cldnn::topology& topology, InferenceEngine::CN auto reshapePrim = cldnn::reshape(reshapeName, inputPrimitives[i], targetShape); topology.add(reshapePrim); - primitivesToIRLayersMap[reshapeName] = { layer->name }; - profilingIDs.push_back(reshapeName); - primitiveIDs[reshapeName] = reshapeName; + addInnerPrimitiveToProfiler(reshapeName, gemmLayerName, layer); inputPrimitives[i] = reshapeName; } @@ -3975,8 +3947,6 @@ void Program::CreateGemmPrimitive(cldnn::topology& topology, InferenceEngine::CN beta); topology.add(gemmPrim); - primitivesToIRLayersMap[gemmLayerName] = { layer->name }; - profilingIDs.push_back(gemmLayerName); auto lastLayerName = gemmLayerName; @@ -3987,14 +3957,12 @@ void Program::CreateGemmPrimitive(cldnn::topology& topology, InferenceEngine::CN auto outReshapePrim = cldnn::reshape(outReshapeName, gemmLayerName, outputShape); topology.add(outReshapePrim); - primitivesToIRLayersMap[outReshapeName] = { layer->name }; - profilingIDs.push_back(outReshapeName); - primitiveIDs[outReshapeName] = outReshapeName; + addInnerPrimitiveToProfiler(outReshapeName, gemmLayerName, layer); lastLayerName = outReshapeName; } - primitiveIDs[gemmLayerName] = lastLayerName; + addPrimitiveToProfiler(gemmLayerName, layer, lastLayerName); } @@ -4087,9 +4055,8 @@ void Program::CreateReducePrimitive(cldnn::topology& topology, InferenceEngine:: axes, static_cast(reduce->keep_dims)); - primitiveIDs[reduceLayerName] = reduceLayerName; topology.add(reducePrim); - profilingIDs.push_back(reduce->name); + addPrimitiveToProfiler(reduceLayerName, layer); } void Program::CreateOneHotPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { @@ -4125,9 +4092,24 @@ void Program::CreateOneHotPrimitive(cldnn::topology& topology, InferenceEngine:: on_value, off_value); - primitiveIDs[oneHotLayerName] = oneHotLayerName; topology.add(oneHotPrim); - profilingIDs.push_back(oneHot->name); + addPrimitiveToProfiler(oneHotLayerName, layer); +} + +void Program::CreateConvertPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer) { + ValidateLayer(layer, 1); + + auto inputPrimitives = GetPrevLayersPrimitives(layer); + + auto precisionParam = layer->GetParamAsString("precision"); + auto outPrecision = Precision::FromStr(precisionParam); + auto outDataType = DataTypeFromPrecision(outPrecision); + + auto name = layer_type_name_ID(layer); + auto prim = cldnn::reorder(name, inputPrimitives[0], cldnn::format::any, outDataType); + + topology.add(prim); + addPrimitiveToProfiler(name, layer); } bool Program::IsValidSplitConvMerge(const InferenceEngine::SplitLayer *splitLayer) const { @@ -4258,7 +4240,7 @@ void Program::AddInputPrimitive(cldnn::topology& topology, InferenceEngine::Inpu inputLayout.format = inputFormat; inputLayout.size = inputLayout.size.transform(inputFormat, 1); inputLayout.data_type = DataTypeFromPrecision(inputPrecision); - auto preprocessPrimID = inputName + m_preProcessTag; + auto preprocessPrimID = "reorder:" + inputName + m_preProcessTag; if ((meanChannels > 0) && (meanChannels != inputLayout.size.feature[0])) { @@ -4277,12 +4259,11 @@ void Program::AddInputPrimitive(cldnn::topology& topology, InferenceEngine::Inpu } } topology.add(cldnn::reorder(preprocessPrimID, inputName, inputLayout, meanValues)); - primitivesToIRLayersMap[preprocessPrimID] = { inputInfo->name() }; + InitProfileInfo(preprocessPrimID, "reorder"); + primitiveIDs[preprocessPrimID] = preprocessPrimID; profilingIDs.push_back(preprocessPrimID); - InitProfileInfo(preprocessPrimID, "Reorder"); - } break; - + } case MEAN_IMAGE: { IE_ASSERT(meanChannels); // first merge all mean values to a single blob @@ -4325,9 +4306,9 @@ void Program::AddInputPrimitive(cldnn::topology& topology, InferenceEngine::Inpu inputName, inputLayout, meanBlobID)); - primitivesToIRLayersMap[preprocessPrimID] = { inputInfo->name() }; + InitProfileInfo(preprocessPrimID, "reorder"); + primitiveIDs[preprocessPrimID] = preprocessPrimID; profilingIDs.push_back(preprocessPrimID); - InitProfileInfo(preprocessPrimID, "Reorder"); break; } default: THROW_CLDNN_EXCEPTION("Invalid mean variant in input " + inputName); @@ -4388,7 +4369,7 @@ void Program::AddOutputPrimitive(cldnn::topology& topology, std::string outputNa else outLayerName += outputCreator->name; - auto outputReorderID = outputName + m_postProcessTag; + auto outputReorderID = "reorder:" + outputName + m_postProcessTag; Precision precision = outputPrecision == Precision::UNSPECIFIED ? outputData->getPrecision() : outputPrecision; // Find correct output ID. Start with name stored in IR. @@ -4408,9 +4389,10 @@ void Program::AddOutputPrimitive(cldnn::topology& topology, std::string outputNa topology.add(cldnn::reorder(outputReorderID, outputID, FormatFromLayout(outputData->getLayout()), DataTypeFromPrecision(precision))); - primitiveIDs[outputName] = outputReorderID; + InitProfileInfo(outputReorderID, "reorder"); + primitiveIDs[outputReorderID] = outputReorderID; profilingIDs.push_back(outputReorderID); - InitProfileInfo(outputReorderID, "Reorder"); + primitiveIDs[outputName] = outputReorderID; outputDims[outputName] = outputDesc.getDims(); prevPrimitiveIDs[outputReorderID] = {outputName}; @@ -4429,11 +4411,7 @@ void Program::AddSingleValuePrimitive(cldnn::topology& topology, cldnn::primitiv case cldnn::data_types::f16: { auto tmpPointer = primMem.pointer(); // implicitly maps buffer - unmap in destructor - cldnn_status status = CLDNN_SUCCESS; - tmpPointer[0] = cldnn_float_to_half(value, &status); - if (status != CLDNN_SUCCESS) { - THROW_CLDNN_EXCEPTION("Error converting value to fp16."); - } + tmpPointer[0] = cldnn::float_to_half(value); } break; default: @@ -4544,7 +4522,7 @@ Program::GenericBlobMap Program::CreateGenericLayerBlobPrimitives(cldnn::topolog cldnn::layout genericLayout(DataTypeFromPrecision(blob.second->getTensorDesc().getPrecision()), m_defaultFormat, - (cldnn::tensor) cldnn::spatial(TensorValue(blobDims.back()))); + (cldnn::tensor) cldnn::feature(TensorValue(blobDims.back()))); cldnn::primitive_id initialWeightID = layer_type_name_ID(layer) + "_" + blob.first + m_weightsTag; cldnn::primitive_id weightID = CreatePrimitiveFromBlob(topology, initialWeightID, blob.second, genericLayout); @@ -4563,17 +4541,41 @@ void Program::ValidateGenericLayerBlobs(const InferenceEngine::GenericLayer* lay } } +void Program::addPrimitiveToProfiler(cldnn::primitive_id id, const InferenceEngine::CNNLayerPtr &layer, + cldnn::primitive_id customOutputId) { + primitivesToIRLayersMap[id] = { layer->name }; + primitiveIDs[id] = customOutputId.empty() ? id : customOutputId; + profilingIDs.push_back(id); +} + +void Program::addInnerPrimitiveToProfiler(cldnn::primitive_id id, cldnn::primitive_id parentId, + const InferenceEngine::CNNLayerPtr &layer) { + InitProfileInfo(id, layer_type_lower(layer), false, InferenceEngine::InferenceEngineProfileInfo::EXECUTED, parentId); + primitivesToIRLayersMap[id] = { layer->name }; + primitiveIDs[id] = id; + profilingIDs.push_back(id); +} + void Program::InitProfileInfo(const std::string& layerName, const std::string& layerType, bool isCPU, - InferenceEngine::InferenceEngineProfileInfo::LayerStatus status) { - perfMap[layerType + ":" + layerName].first = layerName; - auto& perfEntry = perfMap[layerType + ":" + layerName].second; + InferenceEngine::InferenceEngineProfileInfo::LayerStatus status, std::string parentId) { + std::string layer_type_lower = layerType; + for (auto& c : layer_type_lower) + c = tolower(c); + + std::string name = layerName; + if (name.find(layerType + ":") != std::string::npos) { + name = layerName.substr(layerName.find(":") + 1, layerName.length()); + } + + perfMap[layer_type_lower + ":" + name].first = name; + auto& perfEntry = perfMap[layer_type_lower + ":" + name].second; perfEntry.layerType = layerType; perfEntry.status = status; perfEntry.cpu_uSec = perfEntry.realTime_uSec = 0; perfEntry.isCPU = isCPU; - perfEntry.status = status; + perfEntry.parentPrimitive = parentId; } } // namespace CLDNNPlugin diff --git a/inference-engine/src/cldnn_engine/cldnn_program.h b/inference-engine/src/cldnn_engine/cldnn_program.h index 25c7310cf3dee4..325670e40407a9 100644 --- a/inference-engine/src/cldnn_engine/cldnn_program.h +++ b/inference-engine/src/cldnn_engine/cldnn_program.h @@ -22,16 +22,16 @@ #include "cldnn_custom_layer.h" #include "cldnn_config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #ifndef NDEBUG #include @@ -88,6 +88,7 @@ struct PerfCounter { uint64_t cpu_uSec; uint32_t num; std::string layerType; + std::string parentPrimitive; public: PerfCounter() : realTime_uSec(0), cpu_uSec(0), num(0), @@ -122,6 +123,11 @@ class Program { const std::map& getInputLayouts() const { return inputLayouts; } int GetMaxBatchSizeForSingleProgram(); + void addPrimitiveToProfiler(cldnn::primitive_id id, const InferenceEngine::CNNLayerPtr &layer, + cldnn::primitive_id customOutputId = ""); + + void addInnerPrimitiveToProfiler(cldnn::primitive_id id, cldnn::primitive_id parentId, + const InferenceEngine::CNNLayerPtr &layer); // internal types enum LayerType { @@ -206,6 +212,8 @@ class Program { Tan, Gemm, OneHot, + Convert, + GatherTree, NO_TYPE }; using GenericBlobMap = std::map; @@ -223,7 +231,8 @@ class Program { const std::string& layerType, bool isCPU = false, InferenceEngine::InferenceEngineProfileInfo::LayerStatus status - = InferenceEngine::InferenceEngineProfileInfo::EXECUTED); + = InferenceEngine::InferenceEngineProfileInfo::EXECUTED, + std::string parentId = ""); static const cldnn::primitive_id m_preProcessTag; static const cldnn::primitive_id m_weightsTag; @@ -357,6 +366,8 @@ class Program { void CreateGemmPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer); void CreateReducePrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer); void CreateOneHotPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer); + void CreateGatherTreePrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer); + void CreateConvertPrimitive(cldnn::topology& topology, InferenceEngine::CNNLayerPtr &layer); }; } // namespace CLDNNPlugin diff --git a/inference-engine/src/cldnn_engine/debug_options.cpp b/inference-engine/src/cldnn_engine/debug_options.cpp index c0767c28014033..ada1385625c622 100644 --- a/inference-engine/src/cldnn_engine/debug_options.cpp +++ b/inference-engine/src/cldnn_engine/debug_options.cpp @@ -338,4 +338,4 @@ std::string DebugOptions::IELayoutToString(InferenceEngine::Layout layout) { } } -}; // namespace CLDNNPlugin \ No newline at end of file +}; // namespace CLDNNPlugin diff --git a/inference-engine/src/cldnn_engine/debug_options.h b/inference-engine/src/cldnn_engine/debug_options.h index 1dad92eda3eaaf..7fab969c5c7822 100644 --- a/inference-engine/src/cldnn_engine/debug_options.h +++ b/inference-engine/src/cldnn_engine/debug_options.h @@ -12,9 +12,9 @@ #include #include #include "cpp/ie_cnn_network.h" -#include -#include -#include +#include +#include +#include // Debugging options flags // #define _DEBUG_LAYER_CONTENT @@ -68,11 +68,10 @@ class DebugOptions { auto ptr = mem.pointer(); auto data = ptr.data(); // +offset; auto elements = std::min(layout.count(), numElements); - cldnn::status_t status = CLDNN_SUCCESS; for (size_t i = 0; i < elements;) { // size_t linearAddress = ... // todo calc linear with pitches std::cout << std::setprecision(10) - << ((layout.data_type == cldnn::data_types::f32) ? data[i] : cldnn_half_to_float(uint16_t(data[i]), &status)) + << ((layout.data_type == cldnn::data_types::f32) ? data[i] : cldnn::half_to_float(uint16_t(data[i]))) << ", "; i++; for (auto& pitch : pitches) { @@ -85,4 +84,4 @@ class DebugOptions { } }; -}; // namespace CLDNNPlugin \ No newline at end of file +}; // namespace CLDNNPlugin diff --git a/inference-engine/src/extension/README.md b/inference-engine/src/extension/README.md index 91a26ec9a7e669..e05e80ede123ed 100644 --- a/inference-engine/src/extension/README.md +++ b/inference-engine/src/extension/README.md @@ -28,6 +28,7 @@ when cross-compiling this library for another platform. * LogSoftmax * Math (Abs, Acos, Acosh, Asin, Asinh, Atan, Atanh, Ceil, Cos, Cosh, Erf, Floor, HardSigmoid, Log, Neg, Reciprocal, Selu, Sign, Sin, Sinh, Softplus, Softsign, Tan) * MVN + * NonMaxSuppression * Normalize * OneHot * Pad @@ -42,6 +43,7 @@ when cross-compiling this library for another platform. * ReorgYolo * Resample * ReverseSequence + * ScatterUpdate * ShuffleChannels * SimplerNMS * SpaceToDepth diff --git a/inference-engine/src/extension/ext_broadcast.cpp b/inference-engine/src/extension/ext_broadcast.cpp index 8de54b35f4d942..a76b825fae90a9 100644 --- a/inference-engine/src/extension/ext_broadcast.cpp +++ b/inference-engine/src/extension/ext_broadcast.cpp @@ -10,6 +10,7 @@ #include #include #include "ie_parallel.hpp" +#include "common/simple_copy.h" namespace InferenceEngine { namespace Extensions { @@ -29,19 +30,7 @@ class BroadcastImpl: public ExtLayerBase { if (shape_dims.size() > 1) THROW_IE_EXCEPTION << layer->name << " Shape vector should be 1 dimension"; - if (layer->insData[BROADCAST_SHAPE].lock()->getTensorDesc().getPrecision() != Precision::I32) - THROW_IE_EXCEPTION << layer->name << " Shape vector should be I32!"; - - if (!(layer->insData[BROADCAST_INPUT].lock()->getTensorDesc().getPrecision() == Precision::I32 && - layer->outData[0]->getTensorDesc().getPrecision() == Precision::I32) && - !(layer->insData[BROADCAST_INPUT].lock()->getTensorDesc().getPrecision() == Precision::FP32 && - layer->outData[0]->getTensorDesc().getPrecision() == Precision::FP32)) { - THROW_IE_EXCEPTION << layer->name << - " Input and output tensors should have same precision and only FP32 and I32 are supported!"; - } - - src_dims = layer->insData[BROADCAST_INPUT].lock()->getTensorDesc().getDims(); - srcStrides = layer->insData[BROADCAST_INPUT].lock()->getTensorDesc().getBlockingDesc().getStrides(); + data_size = layer->insData[BROADCAST_INPUT].lock()->getTensorDesc().getPrecision().size(); addConfig(layer, { DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN) }, { DataConfigurator(ConfLayout::PLN) }); } catch (InferenceEngine::details::InferenceEngineException &ex) { @@ -50,10 +39,15 @@ class BroadcastImpl: public ExtLayerBase { } StatusCode execute(std::vector& inputs, std::vector& outputs, ResponseDesc *resp) noexcept override { - int32_t* shape_dims = inputs[BROADCAST_SHAPE]->cbuffer().as() + - inputs[BROADCAST_SHAPE]->getTensorDesc().getBlockingDesc().getOffsetPadding(); size_t shape_size = (inputs[BROADCAST_SHAPE]->getTensorDesc().getDims())[0]; SizeVector dst_dims = outputs[0]->getTensorDesc().getDims(); + SizeVector src_dims = inputs[BROADCAST_INPUT]->getTensorDesc().getDims(); + SizeVector srcStrides = inputs[BROADCAST_INPUT]->getTensorDesc().getBlockingDesc().getStrides(); + + if (!src_dims.size()) + src_dims = SizeVector(1, 1); + if (!srcStrides.size()) + srcStrides = SizeVector(1, 1); if (dst_dims.size() != shape_size) { if (resp) { @@ -71,33 +65,11 @@ class BroadcastImpl: public ExtLayerBase { return PARAMETER_MISMATCH; } - size_t i; - for (i = 0; i < dst_dims.size(); i++) { - if (static_cast(dst_dims[i]) != shape_dims[i]) { - if (resp) { - std::string errorMsg = "Output tensor dimension size mismatch"; - errorMsg.copy(resp->msg, sizeof(resp->msg) - 1); - } - return PARAMETER_MISMATCH; - } - } - - size_t prefix_size = dst_dims.size() - src_dims.size(); - for (i = 0; i < src_dims.size(); i++) { - if (src_dims[i] != 1 && - static_cast(src_dims[i]) != shape_dims[i + prefix_size]) { - if (resp) { - std::string errorMsg = "In/Output corresponding dimension must have the same value, or Input dimension is equal to 1"; - errorMsg.copy(resp->msg, sizeof(resp->msg) - 1); - } - return PARAMETER_MISMATCH; - } - } - InferenceEngine::SizeVector dstStrides = outputs[0]->getTensorDesc().getBlockingDesc().getStrides(); InferenceEngine::SizeVector src_aligned(dst_dims.size()); InferenceEngine::SizeVector srcStrides_aligned(dst_dims.size()); - for (i = 0; i < dst_dims.size(); i++) { + size_t prefix_size = dst_dims.size() - src_dims.size(); + for (size_t i = 0; i < dst_dims.size(); i++) { if (i < prefix_size) { src_aligned[i] = 1; srcStrides_aligned[i] = srcStrides[0]; @@ -108,71 +80,31 @@ class BroadcastImpl: public ExtLayerBase { } size_t work_amount_dst = dstStrides[0] * dst_dims[0]; + const uint8_t *src_data = inputs[BROADCAST_INPUT]->cbuffer().as() + + inputs[BROADCAST_INPUT]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + uint8_t* dst_data = outputs[0]->cbuffer().as() + + outputs[0]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + + parallel_nt(0, [&](const int ithr, const int nthr) { + size_t i, src_idx, start = 0, end = 0; + SizeVector counters(dst_dims.size(), 0); + splitter(work_amount_dst, nthr, ithr, start, end); + for (int j = dst_dims.size() - 1, i = start; j >= 0; j--) { + counters[j] = i % dst_dims[j]; + i /= dst_dims[j]; + } + for (size_t iwork = start * data_size; iwork < end * data_size; iwork += data_size) { + for (i = 0, src_idx = 0; i < dst_dims.size(); ++i) + src_idx += counters[i] ? ((counters[i] % src_aligned[i]) * srcStrides_aligned[i]) : 0; - switch (outputs[0]->getTensorDesc().getPrecision()) { - case Precision::FP32: { - const float *src_data = inputs[BROADCAST_INPUT]->cbuffer().as() + - inputs[BROADCAST_INPUT]->getTensorDesc().getBlockingDesc().getOffsetPadding(); - float* dst_data = outputs[0]->cbuffer().as() + - outputs[0]->getTensorDesc().getBlockingDesc().getOffsetPadding(); - - parallel_nt(0, [&](const int ithr, const int nthr) { - size_t i, src_idx, start = 0, end = 0; - SizeVector counters(dst_dims.size(), 0); - splitter(work_amount_dst, nthr, ithr, start, end); - for (int j = dst_dims.size() - 1, i = start; j >= 0; j--) { - counters[j] = i % dst_dims[j]; - i /= dst_dims[j]; - } - for (size_t iwork = start; iwork < end; ++iwork) { - for (i = 0, src_idx = 0; i < dst_dims.size(); ++i) - src_idx += counters[i] ? ((counters[i] % src_aligned[i]) * srcStrides_aligned[i]) : 0; - - dst_data[iwork] = src_data[src_idx]; - - for (int j = dst_dims.size() - 1; j >= 0; j--) { - counters[j] = (counters[j] + 1) % dst_dims[j]; - if (counters[j] != 0) break; - } - } - }); - } - break; - case Precision::I32: { - const int32_t *src_data = inputs[BROADCAST_INPUT]->cbuffer().as() + - inputs[BROADCAST_INPUT]->getTensorDesc().getBlockingDesc().getOffsetPadding(); - int32_t* dst_data = outputs[0]->cbuffer().as() + - outputs[0]->getTensorDesc().getBlockingDesc().getOffsetPadding(); - - parallel_nt(0, [&](const int ithr, const int nthr) { - size_t i, src_idx, start = 0, end = 0; - SizeVector counters(dst_dims.size(), 0); - splitter(work_amount_dst, nthr, ithr, start, end); - for (int j = dst_dims.size() - 1, i = start; j >= 0; j--) { - counters[j] = i % dst_dims[j]; - i /= dst_dims[j]; - } - for (size_t iwork = start; iwork < end; ++iwork) { - for (i = 0, src_idx = 0; i < dst_dims.size(); ++i) - src_idx += counters[i] ? ((counters[i] % src_aligned[i]) * srcStrides_aligned[i]) : 0; - - dst_data[iwork] = src_data[src_idx]; + simple_copy(&dst_data[iwork], data_size, &src_data[src_idx * data_size], data_size); - for (int j = dst_dims.size() - 1; j >= 0; j--) { - counters[j] = (counters[j] + 1) % dst_dims[j]; - if (counters[j] != 0) break; - } + for (int j = dst_dims.size() - 1; j >= 0; j--) { + counters[j] = (counters[j] + 1) % dst_dims[j]; + if (counters[j] != 0) break; } - }); - } - break; - default: - if (resp) { - std::string errorMsg = "Incorrect output precision. Only FP32 and I32 are supported!"; - errorMsg.copy(resp->msg, sizeof(resp->msg) - 1); } - return GENERAL_ERROR; - } + }); return OK; } @@ -181,8 +113,7 @@ class BroadcastImpl: public ExtLayerBase { const size_t BROADCAST_INPUT = 0; const size_t BROADCAST_SHAPE = 1; - SizeVector src_dims; - SizeVector srcStrides; + size_t data_size = 1; }; REG_FACTORY_FOR(ImplFactory, Broadcast); diff --git a/inference-engine/src/extension/ext_detectionoutput_onnx.cpp b/inference-engine/src/extension/ext_detectionoutput_onnx.cpp index 6c1f24325949dc..fa06d3f8d44584 100644 --- a/inference-engine/src/extension/ext_detectionoutput_onnx.cpp +++ b/inference-engine/src/extension/ext_detectionoutput_onnx.cpp @@ -27,7 +27,7 @@ struct Indexer { } } - const int operator()(const std::vector& idx) const { + int operator()(const std::vector& idx) const { int flat_idx = 0; assert(idx.size() == dims_.size()); for (size_t i = 0; i < dims_.size(); ++i) { diff --git a/inference-engine/src/extension/ext_gather.cpp b/inference-engine/src/extension/ext_gather.cpp index 898149b0031d9e..ea9b79fb472213 100644 --- a/inference-engine/src/extension/ext_gather.cpp +++ b/inference-engine/src/extension/ext_gather.cpp @@ -30,44 +30,28 @@ class GatherImpl: public ExtLayerBase { if (inIdxPrecision != Precision::FP32 && inIdxPrecision != Precision::I32 && inIdxPrecision != Precision::FP16) THROW_IE_EXCEPTION << layer->name << " Incorrect input precision. Only FP32, FP16 or I32 are supported!"; - Precision inDataPrecision = layer->insData[GATHER_DICTIONARY].lock()->getTensorDesc().getPrecision(); - if (inDataPrecision != Precision::FP32 && inDataPrecision != Precision::FP16) - THROW_IE_EXCEPTION << layer->name << " Incorrect input precision. Only FP32 or FP16 are supported!"; + axis = layer->GetParamAsInt("axis"); - // Remove redundant dimensions const SizeVector& dictionary_dims = layer->insData[GATHER_DICTIONARY].lock()->getTensorDesc().getDims(); - SizeVector dims_actual; - for (size_t i = 0; i < dictionary_dims.size(); i++) { - if (dictionary_dims[i] > 1) { - for (size_t j = i; j < dictionary_dims.size(); j++) - dims_actual.push_back(dictionary_dims[j]); - break; - } - } - - if (dims_actual.size() == 0) + if (dictionary_dims.size() == 0) THROW_IE_EXCEPTION << layer->name << " Incorrect input parameters dimension!"; - - axis = static_cast(layer->GetParamAsInt("axis")); // Dictionary must be at least rank axis + 1 - if (axis > 0 && static_cast(dims_actual.size()) < (1 + axis)) - THROW_IE_EXCEPTION << layer->name << " Incorrect input parameters dimensions and axis number!"; - else if (axis < 0 && (static_cast(dims_actual.size()) + axis) < 0) - THROW_IE_EXCEPTION << layer->name << " Incorrect input parameters dimensions and axis number!"; - + IE_ASSERT(-static_cast(dictionary_dims.size()) <= axis && axis < static_cast(dictionary_dims.size())) + << layer->name << " Incorrect input parameters dimensions and axis number!"; if (axis < 0) - axis += dims_actual.size(); + axis += dictionary_dims.size(); // Find number of dictionaries, index range and data length for (int i = 0; i < axis; i++) - numDictionaries *= dims_actual[i]; - indexRange = dims_actual[axis]; - for (size_t i = axis + 1; i < dims_actual.size(); i++) - dataLength *= dims_actual[i]; + numDictionaries *= dictionary_dims[i]; + indexRange = dictionary_dims[axis]; + for (size_t i = axis + 1; i < dictionary_dims.size(); i++) + dataLength *= dictionary_dims[i]; if (dataLength == 0) THROW_IE_EXCEPTION << layer->name << " Incorrect input parameters dimension!"; + dataLength *= layer->insData[GATHER_DICTIONARY].lock()->getTensorDesc().getPrecision().size(); addConfig(layer, { DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN) }, { DataConfigurator(ConfLayout::PLN) }); } catch (InferenceEngine::details::InferenceEngineException &ex) { @@ -96,13 +80,13 @@ class GatherImpl: public ExtLayerBase { StatusCode execute(std::vector& inputs, std::vector& outputs, ResponseDesc *resp) noexcept override { switch (inputs[GATHER_INDEXES]->getTensorDesc().getPrecision()) { case Precision::FP32: - gather(inputs[GATHER_INDEXES], inputs[GATHER_DICTIONARY], outputs[0]); + gather(inputs[GATHER_INDEXES], inputs[GATHER_DICTIONARY], outputs[0]); break; case Precision::FP16: - gather(inputs[GATHER_INDEXES], inputs[GATHER_DICTIONARY], outputs[0]); + gather(inputs[GATHER_INDEXES], inputs[GATHER_DICTIONARY], outputs[0]); break; case Precision::I32: - gather(inputs[GATHER_INDEXES], inputs[GATHER_DICTIONARY], outputs[0]); + gather(inputs[GATHER_INDEXES], inputs[GATHER_DICTIONARY], outputs[0]); break; default: return GENERAL_ERROR; @@ -112,48 +96,31 @@ class GatherImpl: public ExtLayerBase { } private: - template + template void gather(Blob::Ptr indexes, Blob::Ptr dictionary, Blob::Ptr output) { size_t src_indexSize = indexes->size(); const index_t *src_index = indexes->cbuffer().as() + indexes->getTensorDesc().getBlockingDesc().getOffsetPadding(); - const data_t *src_dataDict = dictionary->cbuffer().as() + dictionary->getTensorDesc().getBlockingDesc().getOffsetPadding(); - data_t *dst_data = output->cbuffer().as() + output->getTensorDesc().getBlockingDesc().getOffsetPadding(); - - if (axis == 0) { - parallel_for(src_indexSize, [&](size_t i) { - unsigned int idx = Conversion()(src_index[i]); - - // Index clipping - if (idx < indexRange) { - // Copying data to destination from Dictionary - simple_copy(&dst_data[i * dataLength], - output->byteSize() - (dataLength * i), - &src_dataDict[dataLength * idx], - sizeof(data_t) * dataLength); - } else { - memset(&dst_data[i * dataLength], 0, sizeof(data_t) * dataLength); + const uint8_t *src_dataDict = dictionary->cbuffer().as() + dictionary->getTensorDesc().getBlockingDesc().getOffsetPadding(); + uint8_t *dst_data = output->cbuffer().as() + output->getTensorDesc().getBlockingDesc().getOffsetPadding(); + + parallel_for(src_indexSize, [&](size_t i) { + unsigned int idx = Conversion()(src_index[i]); + + // Index clipping + if (idx < indexRange) { + // Copying data to destination from Dictionary + for (size_t j = 0; j < numDictionaries; j++) { + simple_copy(&dst_data[dataLength * (i + j * src_indexSize)], + output->byteSize() - (dataLength * (i + j * src_indexSize)), + &src_dataDict[dataLength * (idx + j * indexRange)], + dataLength); } - }); - } else { - parallel_for(src_indexSize, [&](size_t i) { - unsigned int idx = Conversion()(src_index[i]); - - // Index clipping - if (idx < indexRange) { - // Copying data to destination from Dictionary - for (size_t j = 0; j < numDictionaries; j++) { - simple_copy(&dst_data[dataLength * (i + j * src_indexSize)], - output->byteSize() - (dataLength * (i + j * src_indexSize)), - &src_dataDict[dataLength * (idx + j * indexRange)], - sizeof(data_t) * dataLength); - } - } else { - for (size_t j = 0; j < numDictionaries; j++) { - memset(&dst_data[dataLength * (i + j * src_indexSize)], 0, sizeof(data_t) * dataLength); - } + } else { + for (size_t j = 0; j < numDictionaries; j++) { + memset(&dst_data[dataLength * (i + j * src_indexSize)], 0, dataLength); } - }); - } + } + }); } int axis = 0; diff --git a/inference-engine/src/extension/ext_list.cpp b/inference-engine/src/extension/ext_list.cpp index 4bcd9fa653574f..533821c606bff0 100644 --- a/inference-engine/src/extension/ext_list.cpp +++ b/inference-engine/src/extension/ext_list.cpp @@ -31,7 +31,7 @@ void CpuExtensions::AddShapeInferImpl(std::string name, const IShapeInferImpl::P void CpuExtensions::GetVersion(const Version*& versionInfo) const noexcept { static Version ExtensionDescription = { - { 2, 0 }, // extension API version + { 2, 1 }, // extension API version "2.0", "ie-cpu-ext" // extension description message }; diff --git a/inference-engine/src/extension/ext_log_softmax.cpp b/inference-engine/src/extension/ext_log_softmax.cpp index ba53dc846143fb..6effeef28d2997 100644 --- a/inference-engine/src/extension/ext_log_softmax.cpp +++ b/inference-engine/src/extension/ext_log_softmax.cpp @@ -31,6 +31,8 @@ class LogSoftmaxImpl: public ExtLayerBase { THROW_IE_EXCEPTION << layer->name << " Incorrect input data tensor precision. Only FP32 is supported!"; SizeVector dims = layer->insData[0].lock()->getTensorDesc().getDims(); + if (!dims.size()) + dims = SizeVector(1, 1); int axis = layer->GetParamAsInt("axis", -1); if (axis < 0) axis += dims.size(); diff --git a/inference-engine/src/extension/ext_non_max_suppression.cpp b/inference-engine/src/extension/ext_non_max_suppression.cpp new file mode 100644 index 00000000000000..e90a084af30820 --- /dev/null +++ b/inference-engine/src/extension/ext_non_max_suppression.cpp @@ -0,0 +1,244 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ext_list.hpp" +#include "ext_base.hpp" + +#include +#include +#include +#include +#include +#include +#include "ie_parallel.hpp" + +namespace InferenceEngine { +namespace Extensions { +namespace Cpu { + +class NonMaxSuppressionImpl: public ExtLayerBase { +public: + explicit NonMaxSuppressionImpl(const CNNLayer* layer) { + try { + if (layer->insData.size() < 2 || layer->insData.size() > 5) + THROW_IE_EXCEPTION << layer->name << " Incorrect number of input edges!"; + + if (layer->outData.size() != 1) + THROW_IE_EXCEPTION << layer->name << " Incorrect number of output edges!"; + + if (layer->insData[NMS_BOXES].lock()->getTensorDesc().getPrecision() != Precision::FP32) + THROW_IE_EXCEPTION << layer->name << " Incorrect 'boxes' input precision. Only FP32 is supported!"; + SizeVector boxes_dims = layer->insData[NMS_BOXES].lock()->getTensorDesc().getDims(); + if (boxes_dims.size() != 3 || boxes_dims[2] != 4) + THROW_IE_EXCEPTION << layer->name << " 'boxes' should be with shape [num_batches, spatial_dimension, 4]"; + + if (layer->insData[NMS_SCORES].lock()->getTensorDesc().getPrecision() != Precision::FP32) + THROW_IE_EXCEPTION << layer->name << " Incorrect 'scores' input precision. Only FP32 is supported!"; + SizeVector scores_dims = layer->insData[NMS_SCORES].lock()->getTensorDesc().getDims(); + if (scores_dims.size() != 3) + THROW_IE_EXCEPTION << layer->name << " 'scores' should be with shape [num_batches, num_classes, spatial_dimension]"; + + if (boxes_dims[0] != scores_dims[0]) + THROW_IE_EXCEPTION << layer->name << " num_batches is different in 'boxes' and 'scores' tensors"; + if (boxes_dims[1] != scores_dims[2]) + THROW_IE_EXCEPTION << layer->name << " spatial_dimension is different in 'boxes' and 'scores' tensors"; + + if (layer->insData.size() > 2) { + if (layer->insData[NMS_MAXOUTPUTBOXESPERCLASS].lock()->getTensorDesc().getPrecision() != Precision::I32) + THROW_IE_EXCEPTION << layer->name << " Incorrect 'max_output_boxes_per_class' input precision. Only I32 is supported!"; + SizeVector max_output_boxes_per_class_dims = layer->insData[NMS_MAXOUTPUTBOXESPERCLASS].lock()->getTensorDesc().getDims(); + if (max_output_boxes_per_class_dims.size() != 1 || max_output_boxes_per_class_dims[0] != 1) + THROW_IE_EXCEPTION << layer->name << " 'max_output_boxes_per_class' should be scalar"; + } + + if (layer->insData.size() > 3) { + if (layer->insData[NMS_IOUTHRESHOLD].lock()->getTensorDesc().getPrecision() != Precision::FP32) + THROW_IE_EXCEPTION << layer->name << " Incorrect 'iou_threshold' input precision. Only FP32 is supported!"; + SizeVector iou_threshold_dims = layer->insData[NMS_IOUTHRESHOLD].lock()->getTensorDesc().getDims(); + if (iou_threshold_dims.size() != 1 || iou_threshold_dims[0] != 1) + THROW_IE_EXCEPTION << layer->name << " 'iou_threshold' should be scalar"; + } + + if (layer->insData.size() > 4) { + if (layer->insData[NMS_SCORETHRESHOLD].lock()->getTensorDesc().getPrecision() != Precision::FP32) + THROW_IE_EXCEPTION << layer->name << " Incorrect 'score_threshold' input precision. Only FP32 is supported!"; + SizeVector score_threshold_dims = layer->insData[NMS_SCORETHRESHOLD].lock()->getTensorDesc().getDims(); + if (score_threshold_dims.size() != 1 || score_threshold_dims[0] != 1) + THROW_IE_EXCEPTION << layer->name << " 'score_threshold' should be scalar"; + } + + if (layer->outData[0]->getTensorDesc().getPrecision() != Precision::I32) + THROW_IE_EXCEPTION << layer->name << " Incorrect 'selected_indices' input precision. Only I32 is supported!"; + SizeVector selected_indices_dims = layer->outData[0]->getTensorDesc().getDims(); + if (selected_indices_dims.size() != 2 || selected_indices_dims[1] != 3) + THROW_IE_EXCEPTION << layer->name << " 'selected_indices' should be with shape [num_selected_indices, 3]"; + + center_point_box = layer->GetParamAsBool("center_point_box", false); + + if (layer->insData.size() == 2) { + addConfig(layer, { DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN) }, { DataConfigurator(ConfLayout::PLN) }); + } else if (layer->insData.size() == 3) { + addConfig(layer, { DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN) }, + { DataConfigurator(ConfLayout::PLN) }); + } else if (layer->insData.size() == 4) { + addConfig(layer, { DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN), + DataConfigurator(ConfLayout::PLN) }, { DataConfigurator(ConfLayout::PLN) }); + } else { + addConfig(layer, { DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN), + DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN) }, { DataConfigurator(ConfLayout::PLN) }); + } + } catch (InferenceEngine::details::InferenceEngineException &ex) { + errorMsg = ex.what(); + } + } + + static float intersectionOverUnion(float* boxesI, float* boxesJ, bool center_point_box) { + float yminI, xminI, ymaxI, xmaxI, yminJ, xminJ, ymaxJ, xmaxJ; + if (center_point_box) { + // box format: x_center, y_center, width, height + yminI = boxesI[1] - boxesI[3] / 2.f; + xminI = boxesI[0] - boxesI[2] / 2.f; + ymaxI = boxesI[1] + boxesI[3] / 2.f; + xmaxI = boxesI[0] + boxesI[2] / 2.f; + yminJ = boxesJ[1] - boxesJ[3] / 2.f; + xminJ = boxesJ[0] - boxesJ[2] / 2.f; + ymaxJ = boxesJ[1] + boxesJ[3] / 2.f; + xmaxJ = boxesJ[0] + boxesJ[2] / 2.f; + } else { + // box format: y1, x1, y2, x2 + yminI = (std::min)(boxesI[0], boxesI[2]); + xminI = (std::min)(boxesI[1], boxesI[3]); + ymaxI = (std::max)(boxesI[0], boxesI[2]); + xmaxI = (std::max)(boxesI[1], boxesI[3]); + yminJ = (std::min)(boxesJ[0], boxesJ[2]); + xminJ = (std::min)(boxesJ[1], boxesJ[3]); + ymaxJ = (std::max)(boxesJ[0], boxesJ[2]); + xmaxJ = (std::max)(boxesJ[1], boxesJ[3]); + } + + float areaI = (ymaxI - yminI) * (xmaxI - xminI); + float areaJ = (ymaxJ - yminJ) * (xmaxJ - xminJ); + if (areaI <= 0.f || areaJ <= 0.f) + return 0.f; + + float intersection_area = + (std::max)((std::min)(ymaxI, ymaxJ) - (std::max)(yminI, yminJ), 0.f) * + (std::max)((std::min)(xmaxI, xmaxJ) - (std::max)(xminI, xminJ), 0.f); + return intersection_area / (areaI + areaJ - intersection_area); + } + + typedef struct { + float score; + int batch_index; + int class_index; + int box_index; + } filteredBoxes; + + StatusCode execute(std::vector& inputs, std::vector& outputs, ResponseDesc *resp) noexcept override { + float *boxes = inputs[NMS_BOXES]->cbuffer().as() + + inputs[NMS_BOXES]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + float *scores = inputs[NMS_SCORES]->cbuffer().as() + + inputs[NMS_SCORES]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + + SizeVector scores_dims = inputs[NMS_SCORES]->getTensorDesc().getDims(); + int num_boxes = static_cast(scores_dims[2]); + int max_output_boxes_per_class = num_boxes; + if (inputs.size() > 2) + max_output_boxes_per_class = (std::min)(max_output_boxes_per_class, + (inputs[NMS_MAXOUTPUTBOXESPERCLASS]->cbuffer().as() + + inputs[NMS_MAXOUTPUTBOXESPERCLASS]->getTensorDesc().getBlockingDesc().getOffsetPadding())[0]); + + float iou_threshold = 1.f; // Value range [0, 1] + if (inputs.size() > 3) + iou_threshold = (std::min)(iou_threshold, (inputs[NMS_IOUTHRESHOLD]->cbuffer().as() + + inputs[NMS_IOUTHRESHOLD]->getTensorDesc().getBlockingDesc().getOffsetPadding())[0]); + + float score_threshold = 0.f; + if (inputs.size() > 4) + score_threshold = (inputs[NMS_SCORETHRESHOLD]->cbuffer().as() + + inputs[NMS_SCORETHRESHOLD]->getTensorDesc().getBlockingDesc().getOffsetPadding())[0]; + int* selected_indices = outputs[0]->cbuffer().as() + + outputs[0]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + SizeVector selected_indices_dims = outputs[0]->getTensorDesc().getDims(); + + SizeVector boxesStrides = inputs[NMS_BOXES]->getTensorDesc().getBlockingDesc().getStrides(); + SizeVector scoresStrides = inputs[NMS_SCORES]->getTensorDesc().getBlockingDesc().getStrides(); + + // boxes shape: {num_batches, num_boxes, 4} + // scores shape: {num_batches, num_classes, num_boxes} + int num_batches = static_cast(scores_dims[0]); + int num_classes = static_cast(scores_dims[1]); + std::vector fb; + + for (int batch = 0; batch < num_batches; batch++) { + float *boxesPtr = boxes + batch * boxesStrides[0]; + for (int class_idx = 0; class_idx < num_classes; class_idx++) { + float *scoresPtr = scores + batch * scoresStrides[0] + class_idx * scoresStrides[1]; + std::vector > scores_vector; + for (int box_idx = 0; box_idx < num_boxes; box_idx++) { + if (scoresPtr[box_idx] > score_threshold) + scores_vector.push_back(std::make_pair(scoresPtr[box_idx], box_idx)); + } + + if (scores_vector.size()) { + parallel_sort(scores_vector.begin(), scores_vector.end(), + [](const std::pair& l, const std::pair& r) { return l.first > r.first; }); + + int io_selection_size = 1; + fb.push_back({ scores_vector[0].first, batch, class_idx, scores_vector[0].second }); + for (int box_idx = 1; (box_idx < static_cast(scores_vector.size()) && io_selection_size < max_output_boxes_per_class); box_idx++) { + bool box_is_selected = true; + for (int idx = io_selection_size - 1; idx >= 0; idx--) { + float iou = intersectionOverUnion(&boxesPtr[scores_vector[box_idx].second * 4], + &boxesPtr[scores_vector[idx].second * 4], center_point_box); + if (iou > iou_threshold) { + box_is_selected = false; + break; + } + } + + if (box_is_selected) { + scores_vector[io_selection_size] = scores_vector[box_idx]; + io_selection_size++; + fb.push_back({ scores_vector[box_idx].first, batch, class_idx, scores_vector[box_idx].second }); + } + } + } + } + } + + parallel_sort(fb.begin(), fb.end(), [](const filteredBoxes& l, const filteredBoxes& r) { return l.score > r.score; }); + int selected_indicesStride = outputs[0]->getTensorDesc().getBlockingDesc().getStrides()[0]; + int* selected_indicesPtr = selected_indices; + size_t idx; + for (idx = 0; idx < (std::min)(selected_indices_dims[0], fb.size()); idx++) { + selected_indicesPtr[0] = fb[idx].batch_index; + selected_indicesPtr[1] = fb[idx].class_index; + selected_indicesPtr[2] = fb[idx].box_index; + selected_indicesPtr += selected_indicesStride; + } + for (; idx < selected_indices_dims[0]; idx++) { + selected_indicesPtr[0] = -1; + selected_indicesPtr[1] = -1; + selected_indicesPtr[2] = -1; + selected_indicesPtr += selected_indicesStride; + } + + return OK; + } + +private: + const size_t NMS_BOXES = 0; + const size_t NMS_SCORES = 1; + const size_t NMS_MAXOUTPUTBOXESPERCLASS = 2; + const size_t NMS_IOUTHRESHOLD = 3; + const size_t NMS_SCORETHRESHOLD = 4; + bool center_point_box = false; +}; + +REG_FACTORY_FOR(ImplFactory, NonMaxSuppression); + +} // namespace Cpu +} // namespace Extensions +} // namespace InferenceEngine diff --git a/inference-engine/src/extension/ext_proposal_onnx.cpp b/inference-engine/src/extension/ext_proposal_onnx.cpp index 39ff4a45b80925..338e054e622641 100644 --- a/inference-engine/src/extension/ext_proposal_onnx.cpp +++ b/inference-engine/src/extension/ext_proposal_onnx.cpp @@ -29,7 +29,7 @@ struct Indexer { } } - const int operator()(const std::vector& idx) const { + int operator()(const std::vector& idx) const { int flat_idx = 0; assert(idx.size() == dims_.size()); for (size_t i = 0; i < dims_.size(); ++i) { diff --git a/inference-engine/src/extension/ext_reduce.cpp b/inference-engine/src/extension/ext_reduce.cpp index 16d6decfee7bba..233479287959cb 100644 --- a/inference-engine/src/extension/ext_reduce.cpp +++ b/inference-engine/src/extension/ext_reduce.cpp @@ -111,6 +111,9 @@ class ReduceImpl: public ExtLayerBase { } } + if (!our_dims.size()) + our_dims = InferenceEngine::SizeVector(1, 1); + InferenceEngine::SizeVector dst_dims = outputs[0]->getTensorDesc().getDims(); for (size_t i = 0; i < (std::min)(out_dims.size(), dst_dims.size()); i++) { if (out_dims[i] != dst_dims[i]) { @@ -126,7 +129,12 @@ class ReduceImpl: public ExtLayerBase { inputs[REDUCE_DATA]->getTensorDesc().getBlockingDesc().getOffsetPadding(); float* dst_data = outputs[0]->cbuffer().as() + outputs[0]->getTensorDesc().getBlockingDesc().getOffsetPadding(); - size_t work_amount_dst = outputs[0]->getTensorDesc().getBlockingDesc().getStrides()[0] * dst_dims[0]; + + size_t work_amount_dst; + if (!dst_dims.size()) + work_amount_dst = 1; + else + work_amount_dst = outputs[0]->getTensorDesc().getBlockingDesc().getStrides()[0] * dst_dims[0]; switch (reduceMode) { case Reduce::And: diff --git a/inference-engine/src/extension/ext_resample.cpp b/inference-engine/src/extension/ext_resample.cpp index d4d187d86d3711..e3f717d8020d77 100644 --- a/inference-engine/src/extension/ext_resample.cpp +++ b/inference-engine/src/extension/ext_resample.cpp @@ -54,6 +54,11 @@ class ResampleImpl: public ExtLayerBase { addConfig(layer, {DataConfigurator(ConfLayout::PLN)}, {DataConfigurator(ConfLayout::PLN)}); if (type == "caffe.ResampleParameter.NEAREST") addConfig(layer, {DataConfigurator(blk_layout)}, {DataConfigurator(blk_layout)}); + + // WA to enable the implementation only for equal input and output precisions + for (auto &conf : confs) { + conf.inConfs[0].desc.setPrecision(conf.outConfs[0].desc.getPrecision()); + } } catch (InferenceEngine::details::InferenceEngineException &ex) { errorMsg = ex.what(); } @@ -63,7 +68,7 @@ class ResampleImpl: public ExtLayerBase { ResponseDesc *resp) noexcept override { const auto *src_data = inputs[0]->cbuffer().as(); auto *dst_data = outputs[0]->buffer().as(); -#ifdef WIN32 +#ifdef _WIN32 #undef IN #endif const Layout &layout = inputs[0]->getTensorDesc().getLayout(); diff --git a/inference-engine/src/extension/ext_scatter.cpp b/inference-engine/src/extension/ext_scatter.cpp new file mode 100644 index 00000000000000..0ec01be57c6696 --- /dev/null +++ b/inference-engine/src/extension/ext_scatter.cpp @@ -0,0 +1,174 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ext_list.hpp" +#include "ext_base.hpp" + +#include +#include +#include +#include +#include +#include +#include "ie_parallel.hpp" +#include "common/simple_copy.h" + +namespace InferenceEngine { +namespace Extensions { +namespace Cpu { + +class ScatterImpl: public ExtLayerBase { +public: + explicit ScatterImpl(const CNNLayer* layer) { + try { + if (layer->insData.size() != 3 || layer->outData.size() != 1) + THROW_IE_EXCEPTION << layer->name << " Incorrect number of input/output tensors!"; + + + inIdxPrecision = layer->insData[SCATTER_INDEXES].lock()->getTensorDesc().getPrecision(); + if (inIdxPrecision != Precision::FP32 && inIdxPrecision != Precision::I32) + THROW_IE_EXCEPTION << layer->name << " Incorrect input 'Indexes' precision. Only FP32 or I32 are supported!"; + + Precision inDataPrecision = layer->insData[SCATTER_DATA].lock()->getTensorDesc().getPrecision(); + if (inDataPrecision != layer->insData[SCATTER_UPDATES].lock()->getTensorDesc().getPrecision()) + THROW_IE_EXCEPTION << layer->name << " Precision should be equal for input tensors 'Data' and 'Updates'"; + + if (inDataPrecision != layer->outData[0]->getTensorDesc().getPrecision()) + THROW_IE_EXCEPTION << layer->name << " Precision should be equal for input tensor 'Data' and output"; + + // Remove redundant dimensions + const SizeVector& data_dims = layer->insData[SCATTER_DATA].lock()->getTensorDesc().getDims(); + if (data_dims.size() == 0 || + (data_dims.size() == 1 && data_dims[0] == 1) || + layer->insData[SCATTER_DATA].lock()->getTensorDesc().getLayout() == Layout::SCALAR) + THROW_IE_EXCEPTION << layer->name << " 'Data' tensor rank should be >= 1"; + + axis = layer->GetParamAsInt("axis", 0); + + IE_ASSERT(-static_cast(data_dims.size()) <= axis && axis < static_cast(data_dims.size())) + << layer->name << " Incorrect input parameters dimensions and axis number!"; + + if (axis < 0) + axis += data_dims.size(); + + SizeVector dst_dims = layer->outData[0]->getTensorDesc().getDims(); + if (data_dims != dst_dims) + THROW_IE_EXCEPTION << layer->name << " Incorrect number of input/output dimensions!"; + + SizeVector idx_dims = layer->insData[SCATTER_INDEXES].lock()->getTensorDesc().getDims(); + if (idx_dims.size() == 0 || + (idx_dims.size() == 1 && idx_dims[0] == 1) || + layer->insData[SCATTER_INDEXES].lock()->getTensorDesc().getLayout() == Layout::SCALAR) + THROW_IE_EXCEPTION << layer->name << " 'Indexes' tensor rank should be >= 1"; + + SizeVector upd_dims = layer->insData[SCATTER_UPDATES].lock()->getTensorDesc().getDims(); + if (layer->insData[SCATTER_UPDATES].lock()->getTensorDesc().getLayout() == Layout::SCALAR) + THROW_IE_EXCEPTION << layer->name << " 'Indexes' tensor rank should be >= 1"; + + if (idx_dims != upd_dims) + THROW_IE_EXCEPTION << layer->name << " Incorrect number of 'indexes' and 'updates' tensors dimension"; + + for (size_t i = 0; i < idx_dims.size(); i++) { + if (i == static_cast(axis)) continue; + if (idx_dims[i] > data_dims[i]) + THROW_IE_EXCEPTION << layer->name << " Incorrect number of data and indexes dimensions!"; + } + + data_size = layer->insData[SCATTER_DATA].lock()->getTensorDesc().getPrecision().size(); + + addConfig(layer, { DataConfigurator(ConfLayout::PLN, false, 0), DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN) }, + { DataConfigurator(ConfLayout::PLN, false, 0) }); + } catch (InferenceEngine::details::InferenceEngineException &ex) { + errorMsg = ex.what(); + } + } + + StatusCode execute(std::vector& inputs, std::vector& outputs, ResponseDesc *resp) noexcept override { + switch (inIdxPrecision) { + case Precision::FP32: + scatter(inputs[SCATTER_DATA], inputs[SCATTER_INDEXES], inputs[SCATTER_UPDATES], outputs[0]); + break; + case Precision::I32: + scatter(inputs[SCATTER_DATA], inputs[SCATTER_INDEXES], inputs[SCATTER_UPDATES], outputs[0]); + break; + default: + return GENERAL_ERROR; + } + + return OK; + } + +private: + template + void scatter(Blob::Ptr data, Blob::Ptr indexes, Blob::Ptr updates, Blob::Ptr output) { + const uint8_t *src_data = data->cbuffer().as() + data->getTensorDesc().getBlockingDesc().getOffsetPadding(); + const index_t *src_index = indexes->cbuffer().as() + indexes->getTensorDesc().getBlockingDesc().getOffsetPadding(); + const uint8_t *src_updates = updates->cbuffer().as() + updates->getTensorDesc().getBlockingDesc().getOffsetPadding(); + uint8_t *dst_data = output->cbuffer().as() + output->getTensorDesc().getBlockingDesc().getOffsetPadding(); + + InferenceEngine::SizeVector index_dims = indexes->getTensorDesc().getDims(); + InferenceEngine::SizeVector data_dims = data->getTensorDesc().getDims(); + InferenceEngine::SizeVector dataStrides = data->getTensorDesc().getBlockingDesc().getStrides(); + + if (src_data != dst_data) { + parallel_nt(0, [&](const int ithr, const int nthr) { + size_t start = 0, end = 0; + splitter(output->size(), nthr, ithr, start, end); + size_t size = (end - start) * data_size; + start *= data_size; + simple_copy(dst_data + start, size, src_data + start, size); + }); + } + + parallel_nt(0, [&](const int ithr, const int nthr) { + int j; + size_t i, dst_idx = 0, start = 0, end = 0; + SizeVector counters(index_dims.size(), 0); + splitter(indexes->size(), nthr, ithr, start, end); + for (j = index_dims.size() - 1, i = start; j >= 0; j--) { + counters[j] = i % index_dims[j]; + i /= index_dims[j]; + } + + for (i = 0; i < static_cast(axis); ++i) + dst_idx += counters[i] * dataStrides[i]; + for (i++; i < data_dims.size(); ++i) + dst_idx += counters[i] * dataStrides[i]; + + for (size_t iwork = start; iwork < end; iwork++) { + unsigned int idx = static_cast(src_index[iwork]); + if (idx < data_dims[axis]) + simple_copy(dst_data + data_size * (dst_idx + idx * dataStrides[axis]), data_size, + src_updates + iwork * data_size, data_size); + + for (j = index_dims.size() - 1; j >= 0; j--) { + counters[j]++; + if (counters[j] < index_dims[j]) { + dst_idx += dataStrides[j]; + break; + } else { + counters[j] = 0; + for (dst_idx = 0, i = 0; i < static_cast(axis); ++i) + dst_idx += counters[i] * dataStrides[i]; + for (i++; i < data_dims.size(); ++i) + dst_idx += counters[i] * dataStrides[i]; + } + } + } + }); + } + + int axis = 0; + Precision inIdxPrecision; + const size_t SCATTER_DATA = 0; + const size_t SCATTER_INDEXES = 1; + const size_t SCATTER_UPDATES = 2; + size_t data_size = 1; +}; + +REG_FACTORY_FOR(ImplFactory, ScatterUpdate); + +} // namespace Cpu +} // namespace Extensions +} // namespace InferenceEngine diff --git a/inference-engine/src/extension/ext_simplernms.cpp b/inference-engine/src/extension/ext_simplernms.cpp index c00e76fd91758d..933b26f08fc6df 100644 --- a/inference-engine/src/extension/ext_simplernms.cpp +++ b/inference-engine/src/extension/ext_simplernms.cpp @@ -17,7 +17,7 @@ namespace Cpu { struct simpler_nms_roi_t { float x0, y0, x1, y1; - static inline const float clamp_v(const float v, const float v_min, const float v_max) { + static inline float clamp_v(const float v, const float v_min, const float v_max) { return std::max(v_min, std::min(v, v_max)); } diff --git a/inference-engine/src/extension/ext_sparse_fill_empty_rows.cpp b/inference-engine/src/extension/ext_sparse_fill_empty_rows.cpp new file mode 100644 index 00000000000000..e07d54a6d42bf3 --- /dev/null +++ b/inference-engine/src/extension/ext_sparse_fill_empty_rows.cpp @@ -0,0 +1,232 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ext_list.hpp" +#include "ext_base.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include "ie_parallel.hpp" +#include "simple_copy.h" + +namespace InferenceEngine { +namespace Extensions { +namespace Cpu { + +class SparseFillEmptyRowsImpl : public ExtLayerBase { +public: + explicit SparseFillEmptyRowsImpl(const CNNLayer* layer) { + try { + if (layer->insData.size() != 4 || layer->outData.size() != 3) { + THROW_IE_EXCEPTION << layer->name << " Incorrect number of input/output edges!"; + } + + Precision input_indices_precision = layer->insData[INPUT_INDICES_PORT].lock()->getTensorDesc().getPrecision(); + if (input_indices_precision != Precision::FP32) { + THROW_IE_EXCEPTION << layer->name << " Incorrect input precision. Only FP32 is supported!"; + } + + // check dimensions of input tensors + SizeVector input_indices_dims = layer->insData[INPUT_INDICES_PORT].lock()->getTensorDesc().getDims(); + if (input_indices_dims.size() != 2 || input_indices_dims[1] != 2) { + THROW_IE_EXCEPTION << layer->name << " Incorrect dimensions for input indices. It must be Nx2 dimension tensor."; + } + SizeVector input_values_dims = layer->insData[INPUT_VALUES_PORT].lock()->getTensorDesc().getDims(); + if (input_values_dims.size() != 1) { + THROW_IE_EXCEPTION << layer->name << " Incorrect dimensions for input values. It must be N dimension tensor."; + } + if (input_indices_dims[0] != input_values_dims[0]) { + THROW_IE_EXCEPTION << layer->name << " Mismatch of the first dimensions of input indices and values."; + } + SizeVector input_dense_shape_dims = layer->insData[INPUT_DENSE_SHAPE_PORT].lock()->getTensorDesc().getDims(); + if (input_dense_shape_dims.size() != 1 || input_dense_shape_dims[0] != 2) { + THROW_IE_EXCEPTION << layer->name << " Incorrect dimensions for input dense shape."; + } + SizeVector input_default_value_dims = layer->insData[INPUT_DEFAULT_VALUE_PORT].lock()->getTensorDesc().getDims(); + if (input_default_value_dims[0] != 1) { + THROW_IE_EXCEPTION << layer->name << " Incorrect dimensions for input dense shape."; + } + inMaxNumValues = input_indices_dims[0]; + + // check dimensions of output tensors + SizeVector output_indices_dims = layer->outData[OUTPUT_INDICES_PORT]->getTensorDesc().getDims(); + if (output_indices_dims.size() != 2 || output_indices_dims[1] != 2) { + THROW_IE_EXCEPTION << layer->name << " Incorrect dimensions for output indices. It must be Nx2 dimension tensor."; + } + SizeVector output_values_dims = layer->outData[OUTPUT_VALUES_PORT]->getTensorDesc().getDims(); + if (output_values_dims.size() != 1) { + THROW_IE_EXCEPTION << layer->name << " Incorrect dimensions for output values. It must be N dimension tensor."; + } + if (output_indices_dims[0] != output_values_dims[0]) { + THROW_IE_EXCEPTION << layer->name << " Mismatch of the first dimensions of output indices and values."; + } + SizeVector output_empty_rows_indicator_dims = layer->outData[OUTPUT_EMPTY_ROWS_INDICATOR_PORT]->getTensorDesc().getDims(); + if (output_empty_rows_indicator_dims.size() != 1) { + THROW_IE_EXCEPTION << layer->name << " Incorrect dimensions for output empty rows indicator. It must be 1-D tensor."; + } + outMaxNumValues = output_indices_dims[0]; + if (outMaxNumValues < inMaxNumValues) { + THROW_IE_EXCEPTION << layer->name << " The first dimension size of input indices can not be greater the first dimension of output indices."; + } + + // TODO: check that dense shape value is set + addConfig(layer, + {DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN)}, + {DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN)}); + } + catch (InferenceEngine::details::InferenceEngineException &ex) { + errorMsg = ex.what(); + } + } + + StatusCode execute(std::vector& inputs, std::vector& outputs, ResponseDesc *resp) noexcept override { + const float *input_indices_ptr = inputs[INPUT_INDICES_PORT]->cbuffer().as() + + inputs[INPUT_INDICES_PORT]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + const float *input_values_ptr = inputs[INPUT_VALUES_PORT]->cbuffer().as() + + inputs[INPUT_VALUES_PORT]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + const float *dense_shape_ptr = inputs[INPUT_DENSE_SHAPE_PORT]->cbuffer().as() + + inputs[INPUT_DENSE_SHAPE_PORT]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + const float *default_value_ptr = inputs[INPUT_DEFAULT_VALUE_PORT]->cbuffer().as() + + inputs[INPUT_DEFAULT_VALUE_PORT]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + + float default_value = default_value_ptr[0]; + float num_rows = dense_shape_ptr[0]; + float num_cols = dense_shape_ptr[1]; + + // compute actual number of values by searching out of range indice that serves as a marker + size_t in_actual_num_values = 0; + for (in_actual_num_values = 0; in_actual_num_values < inMaxNumValues; in_actual_num_values++) { + float indice_x = input_indices_ptr[2 * in_actual_num_values]; + float indice_y = input_indices_ptr[2 * in_actual_num_values + 1]; + if (indice_x < 0 || indice_y < 0 || indice_x >= num_rows || indice_y >= num_cols) break; + } + + // create auxiliary container for sorting + std::vector> indices_values(in_actual_num_values); + parallel_for(in_actual_num_values, [&](size_t i) { + float row = input_indices_ptr[2 * i]; + float col = input_indices_ptr[2 * i + 1]; + float value = input_values_ptr[i]; + std::array elem = { row, col, value }; + indices_values[i] = elem; + }); + + // sort values by row + parallel_sort(indices_values.begin(), indices_values.end(), + [](const std::array& first, const std::array& second) { + return first[0] < second[0]; + }); + + // unsplit indices and values + std::vector indices_with_sorted_rows(in_actual_num_values * 2); + std::vector values_for_sorted_rows(in_actual_num_values); + parallel_for(in_actual_num_values, [&](size_t i) { + auto elem = indices_values[i]; + indices_with_sorted_rows[i * 2] = elem[0]; + indices_with_sorted_rows[i * 2 + 1] = elem[1]; + values_for_sorted_rows[i] = elem[2]; + }); + + // compute start indice for each row and a number of values at each row + std::vector values_at_row(static_cast(num_rows)); + std::fill(values_at_row.begin(), values_at_row.end(), 0); + float prev_row_with_value = -1.0f; + unsigned int total_num_values = 0; + std::vector>::iterator curr_it, prev_it; + for (float row_ind = 0.0; row_ind < num_rows; row_ind = row_ind + 1.0f) { + curr_it = std::find_if(indices_values.begin(), indices_values.end(), + [row_ind](std::array elem) { return elem[0] == row_ind; }); + if (curr_it != indices_values.end()) { + if (prev_row_with_value != -1.0f) { + unsigned int num_values_at_prev_row = static_cast(std::distance(prev_it, curr_it)); + values_at_row[static_cast(prev_row_with_value)] = num_values_at_prev_row; + total_num_values += num_values_at_prev_row; + } + prev_row_with_value = row_ind; + prev_it = curr_it; + } else { + total_num_values++; + } + } + if (prev_row_with_value != -1.0) { + unsigned int num_values_at_prev_row = static_cast(std::distance(prev_it, indices_values.end())); + values_at_row[static_cast(prev_row_with_value)] = num_values_at_prev_row; + total_num_values += num_values_at_prev_row; + } + + // check that output buffer size is sufficient + if (outMaxNumValues < total_num_values) return GENERAL_ERROR; + + // create output indices + float *output_indices_ptr = outputs[OUTPUT_INDICES_PORT]->cbuffer().as() + + inputs[OUTPUT_INDICES_PORT]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + float *output_values_ptr = outputs[OUTPUT_VALUES_PORT]->cbuffer().as() + + inputs[OUTPUT_VALUES_PORT]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + float *output_empty_rows_indicator_ptr = outputs[OUTPUT_EMPTY_ROWS_INDICATOR_PORT]->cbuffer().as() + + inputs[OUTPUT_EMPTY_ROWS_INDICATOR_PORT]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + + auto output_indices_size = outputs[OUTPUT_INDICES_PORT]->byteSize(); + memset(output_indices_ptr, 0, output_indices_size); + + auto output_values_size = outputs[OUTPUT_VALUES_PORT]->byteSize(); + memset(output_values_ptr, 0, output_values_size); + + auto output_empty_rows_indicator_size = outputs[OUTPUT_EMPTY_ROWS_INDICATOR_PORT]->byteSize(); + memset(output_empty_rows_indicator_ptr, 0, output_empty_rows_indicator_size); + + + unsigned int curr_pos_from_copy = 0; + unsigned int curr_pos_to_copy = 0; + for (int row_ind = 0; row_ind < static_cast(num_rows); row_ind++) { + unsigned int num_values_at_row = values_at_row[row_ind]; + if (num_values_at_row == 0) { + output_empty_rows_indicator_ptr[row_ind] = 1.0; + output_values_ptr[curr_pos_to_copy] = default_value; + output_indices_ptr[curr_pos_to_copy * 2] = static_cast(row_ind); + output_indices_ptr[curr_pos_to_copy * 2 + 1] = 0.0; + curr_pos_to_copy++; + } else { + output_empty_rows_indicator_ptr[row_ind] = 0.0; + std::copy(values_for_sorted_rows.begin() + curr_pos_from_copy, + values_for_sorted_rows.begin() + curr_pos_from_copy + num_values_at_row, + output_values_ptr + curr_pos_to_copy); + std::copy(indices_with_sorted_rows.begin() + 2 * curr_pos_from_copy, + indices_with_sorted_rows.begin() + 2 * curr_pos_from_copy + 2 * num_values_at_row, output_indices_ptr + curr_pos_to_copy * 2); + curr_pos_to_copy += num_values_at_row; + curr_pos_from_copy += num_values_at_row; + } + } + + // mark the end of output using (-1, -1) indice + if (total_num_values < outMaxNumValues) { + output_indices_ptr[total_num_values * 2] = -1.0; + output_indices_ptr[total_num_values * 2 + 1] = -1.0; + } + + return OK; + } + +private: + const size_t INPUT_INDICES_PORT = 0; + const size_t INPUT_VALUES_PORT = 1; + const size_t INPUT_DENSE_SHAPE_PORT = 2; + const size_t INPUT_DEFAULT_VALUE_PORT = 3; + const size_t OUTPUT_INDICES_PORT = 0; + const size_t OUTPUT_VALUES_PORT = 1; + const size_t OUTPUT_EMPTY_ROWS_INDICATOR_PORT = 2; + + size_t inMaxNumValues = 0; + size_t outMaxNumValues = 0; +}; + +REG_FACTORY_FOR(ImplFactory, SparseFillEmptyRows); + +} // namespace Cpu +} // namespace Extensions +} // namespace InferenceEngine diff --git a/inference-engine/src/extension/ext_strided_slice.cpp b/inference-engine/src/extension/ext_strided_slice.cpp index 477377074354e5..bb20a604c211ee 100644 --- a/inference-engine/src/extension/ext_strided_slice.cpp +++ b/inference-engine/src/extension/ext_strided_slice.cpp @@ -168,6 +168,9 @@ class StridedSliceImpl: public ExtLayerBase { InferenceEngine::SizeVector dst_dims = outputs[0]->getTensorDesc().getDims(); InferenceEngine::SizeVector dstStrides = outputs[0]->getTensorDesc().getBlockingDesc().getStrides(); + auto dst_size = outputs[0]->byteSize(); + memset(dst_data, 0, dst_size); + size_t i, j, k, bj, ej, sj; InferenceEngine::SizeVector our_dims; InferenceEngine::SizeVector out_dims; diff --git a/inference-engine/src/extension/ext_unique.cpp b/inference-engine/src/extension/ext_unique.cpp new file mode 100644 index 00000000000000..939128c5119a07 --- /dev/null +++ b/inference-engine/src/extension/ext_unique.cpp @@ -0,0 +1,206 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ext_list.hpp" +#include "ext_base.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ie_parallel.hpp" +#include "simple_copy.h" + +namespace InferenceEngine { +namespace Extensions { +namespace Cpu { + +class UniqueImpl : public ExtLayerBase { +public: + explicit UniqueImpl(const CNNLayer* layer) { + try { + // check number of inputs and outputs + if (layer->insData.size() != 1 || layer->outData.size() < 1 || layer->outData.size() > 3) { + THROW_IE_EXCEPTION << layer->name << " Incorrect number of input/output edges!"; + } + + // check precision of tensors + Precision input_indices_precision = layer->insData[0].lock()->getTensorDesc().getPrecision(); + if (input_indices_precision != Precision::FP32) { + THROW_IE_EXCEPTION << layer->name << " Incorrect input precision. Only FP32 is supported!"; + } + + // check attributes + sorted = layer->GetParamAsBool("sorted"); + return_inverse = layer->GetParamAsBool("return_inverse"); + return_counts = layer->GetParamAsBool("return_counts"); + + // check that a real number of outputs matches one claimed by attributes + size_t claimed_num_outputs = 1; + if (return_inverse) { + claimed_num_outputs++; + } + if (return_counts) { + claimed_num_outputs++; + } + if (layer->outData.size() != claimed_num_outputs) { + THROW_IE_EXCEPTION << layer->name << " A number of outputs claimed by attributes does not match a real number of outputs!"; + } + + // check dimensions of input tensors + SizeVector input_dims = layer->insData[0].lock()->getTensorDesc().getDims(); + if (input_dims.size() != 1) { + THROW_IE_EXCEPTION << layer->name << " Input must be 1-D tensor."; + } + num_elements = input_dims[0]; + + // check dimensions of output tensors and its precisions + size_t cur_output_port = 0; + SizeVector output_uniques_dims = layer->outData[cur_output_port]->getTensorDesc().getDims(); + Precision output_uniques_precision = layer->outData[cur_output_port]->getTensorDesc().getPrecision(); + if (output_uniques_precision != Precision::FP32) { + THROW_IE_EXCEPTION << layer->name << " Incorrect precision for output tensor of unique elements. Only FP32 is supported!"; + } + if (output_uniques_dims.size() != 1 || output_uniques_dims[0] != num_elements) { + THROW_IE_EXCEPTION << layer->name << " Incorrect dimensions for output tensor of unique elements."; + } + if (return_inverse) { + cur_output_port++; + SizeVector output_indices_dims = layer->outData[cur_output_port]->getTensorDesc().getDims(); + Precision output_indices_precision = layer->outData[cur_output_port]->getTensorDesc().getPrecision(); + if (output_indices_precision != Precision::FP32) { + THROW_IE_EXCEPTION << layer->name << " Incorrect precision for output tensor of indices. Only FP32 is supported!"; + } + if (output_indices_dims.size() != 1 || output_indices_dims[0] != num_elements) { + THROW_IE_EXCEPTION << layer->name << " Incorrect dimensions for output tensor of indices."; + } + } + if (return_counts) { + cur_output_port++; + SizeVector output_counts_dims = layer->outData[cur_output_port]->getTensorDesc().getDims(); + Precision output_counts_precision = layer->outData[cur_output_port]->getTensorDesc().getPrecision(); + if (output_counts_precision != Precision::FP32) { + THROW_IE_EXCEPTION << layer->name << " Incorrect precision for output tensor of counts. Only FP32 is supported!"; + } + if (output_counts_dims.size() != 1 || output_counts_dims[0] != num_elements) { + THROW_IE_EXCEPTION << layer->name << " Incorrect dimensions for output tensor of counts."; + } + } + + // add a layer configuration + if (layer->outData.size() == 1) { + addConfig(layer, + { DataConfigurator(ConfLayout::PLN) }, + { DataConfigurator(ConfLayout::PLN) }); + } else if (layer->outData.size() == 2) { + addConfig(layer, + { DataConfigurator(ConfLayout::PLN) }, + { DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN) }); + } else if (layer->outData.size() == 3) { + addConfig(layer, + { DataConfigurator(ConfLayout::PLN) }, + { DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN), DataConfigurator(ConfLayout::PLN) }); + } + } + catch (InferenceEngine::details::InferenceEngineException &ex) { + errorMsg = ex.what(); + } + } + + StatusCode execute(std::vector& inputs, std::vector& outputs, ResponseDesc *resp) noexcept override { + const float *input_ptr = inputs[0]->cbuffer().as() + + inputs[0]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + size_t cur_output_port = 0; + float *output_uniques_ptr = outputs[cur_output_port]->cbuffer().as() + + outputs[cur_output_port]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + float *output_indices_ptr = nullptr; + if (return_inverse) { + cur_output_port++; + output_indices_ptr = outputs[cur_output_port]->cbuffer().as() + + outputs[cur_output_port]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + } + float *output_counts_ptr = nullptr; + if (return_counts) { + cur_output_port++; + output_counts_ptr = outputs[cur_output_port]->cbuffer().as() + + outputs[cur_output_port]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + } + + // create a copy since input can be changed by sorting + std::vector input_copy(num_elements); + std::copy(input_ptr, input_ptr + num_elements, input_copy.begin()); + + // sort elements in the input copy + if (sorted) { + parallel_sort(input_copy.begin(), input_copy.end(), std::less()); + } + + // walk through elements and save them along with its indice and occurences + std::unordered_map indices; + for (size_t i = 0, num_unique_elements = 0; i < num_elements; i++) { + auto it = indices.find(input_copy[i]); + if (it == indices.end()) { + indices.insert(std::make_pair(input_copy[i], static_cast(num_unique_elements))); + output_uniques_ptr[num_unique_elements] = input_copy[i]; + if (return_inverse && !sorted) { + output_indices_ptr[i] = static_cast(num_unique_elements); + } + if (return_counts) { + output_counts_ptr[num_unique_elements] = 1.0f; + } + num_unique_elements++; + } else { + if (return_inverse && !sorted) { + output_indices_ptr[i] = it->second; + } + if (return_counts) { + output_counts_ptr[static_cast(it->second)] += 1.0f; + } + } + } + + // compute indices individually when unique elements are known + if (sorted && return_inverse) { + for (size_t i = 0; i < num_elements; i++) { + auto it = indices.find(input_ptr[i]); + output_indices_ptr[i] = it->second; + } + } + + // fill a tail with the latest unique element used as an end mark + size_t num_unique_elements = indices.size(); + if ((num_elements - num_unique_elements) > 0) { + std::fill(output_uniques_ptr + num_unique_elements, + output_uniques_ptr + num_elements, + output_uniques_ptr[num_unique_elements - 1]); + } + + // fill a tail for output buffer with counts + if (return_counts && (num_elements - num_unique_elements) > 0) { + std::fill(output_counts_ptr + num_unique_elements, + output_counts_ptr + num_elements, 0.f); + } + + return OK; + } + +private: + // attributes + bool sorted; + bool return_inverse; + bool return_counts; + + size_t num_elements = 0; +}; + +REG_FACTORY_FOR(ImplFactory, Unique); + +} // namespace Cpu +} // namespace Extensions +} // namespace InferenceEngine diff --git a/inference-engine/src/gna_plugin/CMakeLists.txt b/inference-engine/src/gna_plugin/CMakeLists.txt index f5972af555ff23..6db82107290725 100644 --- a/inference-engine/src/gna_plugin/CMakeLists.txt +++ b/inference-engine/src/gna_plugin/CMakeLists.txt @@ -3,7 +3,6 @@ set(TARGET_NAME "GNAPlugin") -disable_deprecated_warnings() file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) @@ -14,11 +13,10 @@ file(GLOB_RECURSE HEADERS find_package(libGNA) -include_directories( +set(TARGET_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/inference_engine ${CMAKE_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR} - ${libGNA_INCLUDE_DIRS}) + ${CMAKE_CURRENT_SOURCE_DIR}) add_definitions(-D_NO_MKL_) @@ -31,10 +29,10 @@ if (LINUX) endif() #saving rpath to GNA shared library be used by CI -log_rpath_remove_top(GNA FALSE "/gna${libGNA_LIBRARY}" TRUE) - -target_link_libraries(${TARGET_NAME} PRIVATE inference_engine ${INTEL_ITT_LIBS} ${libGNA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +log_rpath_from_dir(GNA ${libGNA_LIBRARIES_BASE_PATH}) +target_link_libraries(${TARGET_NAME} PRIVATE inference_engine ${INTEL_ITT_LIBS} ${CMAKE_THREAD_LIBS_INIT} libGNA) +target_include_directories(${TARGET_NAME} PUBLIC ${TARGET_INCLUDE_DIRS}) set(TEST_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/gna_plugin.cpp" @@ -53,5 +51,7 @@ add_library(${TARGET_NAME}_test_static STATIC ${TEST_SOURCES} ${HEADERS}) target_compile_definitions(${TARGET_NAME}_test_static PUBLIC -DINTEGER_LOW_P -DUSE_STATIC_IE) +target_link_libraries(${TARGET_NAME}_test_static PUBLIC libGNA::API) +target_include_directories(${TARGET_NAME}_test_static PUBLIC ${TARGET_INCLUDE_DIRS}) set_target_properties(${TARGET_NAME}_test_static PROPERTIES COMPILE_PDB_NAME ${TARGET_NAME}_test_static) diff --git a/inference-engine/src/gna_plugin/dnn.cpp b/inference-engine/src/gna_plugin/dnn.cpp index a3d0be7ed14800..50b83166b6f958 100644 --- a/inference-engine/src/gna_plugin/dnn.cpp +++ b/inference-engine/src/gna_plugin/dnn.cpp @@ -1661,7 +1661,6 @@ void AmIntelDnn::WriteDnnText(const char *filename, intel_dnn_number_type_t numb out_wfile << std::setprecision(12) << ptr_weight[row * num_filter_coefficients + col] << "\n"; } - out_wfile << "\n"; } #endif } else { diff --git a/inference-engine/src/gna_plugin/gna_infer_request.hpp b/inference-engine/src/gna_plugin/gna_infer_request.hpp index 84f6c1d8b57547..e45bc1fe2281e4 100644 --- a/inference-engine/src/gna_plugin/gna_infer_request.hpp +++ b/inference-engine/src/gna_plugin/gna_infer_request.hpp @@ -31,10 +31,10 @@ class GNAInferRequest : public InferenceEngine::AsyncInferRequestInternal { } // copy inputs blobs since we need to have them in separate address space to allow simultaneous infer requests - _outputs[_networkOutputs.rbegin()->first] = plg->GetOutputBlob(networkOutputs.begin()->second->getPrecision()); + _outputs[_networkOutputs.rbegin()->first] = plg->GetOutputBlob(networkOutputs.begin()->second->getTensorDesc().getPrecision()); for (auto input : _networkInputs) { _inputs[input.first] = - plg->GetInputBlob(input.first, networkInputs.begin()->second->getPrecision()); + plg->GetInputBlob(input.first, networkInputs.begin()->second->getTensorDesc().getPrecision()); } } /** diff --git a/inference-engine/src/gna_plugin/gna_pass_manager.cpp b/inference-engine/src/gna_plugin/gna_pass_manager.cpp index e9d149b7bde005..aceb42a1934e2d 100644 --- a/inference-engine/src/gna_plugin/gna_pass_manager.cpp +++ b/inference-engine/src/gna_plugin/gna_pass_manager.cpp @@ -22,6 +22,7 @@ #include
#include #include +#include #include "gna_pass_manager.hpp" #include "gna_layer_info.hpp" @@ -59,17 +60,21 @@ static void insertDiagonalLayerBetween(InferenceEngine::CNNLayerPtr prevLayer, auto diagLayer = std::make_shared(LayerParams({diagName, "ScaleShift", Precision::FP32})); // TODO: diagonal size - std::vector weightsValues(nextLayer->outData[0]->dims[0], fillValue); - diagLayer->_weights = make_shared_blob(nextLayer->outData[0]->precision, Layout::C, weightsValues); - auto newDims = nextLayer->outData[0]->getDims(); - auto dataPtr = std::make_shared(diagName, - TensorDesc(nextLayer->outData[0]->precision, - newDims, - nextLayer->outData[0]->layout)); + auto dimsIndex = nextLayer->outData[0]->getTensorDesc().getDims().size() - 1; + std::vector weightsValues(nextLayer->outData[0]->getTensorDesc().getDims()[dimsIndex], fillValue); + diagLayer->_weights = make_shared_blob( + TensorDesc( + nextLayer->outData[0]->getTensorDesc().getPrecision(), + SizeVector({weightsValues.size()}), + Layout::C)); + diagLayer->_weights->allocate(); + CopyVectorToBlob(diagLayer->_weights, weightsValues); + auto dataPtr = std::make_shared(diagName, nextLayer->outData[0]->getTensorDesc()); + auto diagonalWithQuant = quantized ? InferenceEngine::injectData(diagLayer) : diagLayer; - dataPtr->creatorLayer = diagonalWithQuant; + dataPtr->getCreatorLayer() = diagonalWithQuant; diagonalWithQuant->outData.push_back(dataPtr); // actual insertion @@ -88,16 +93,11 @@ static CNNLayerPtr InsertCopyLayer(CNNLayerPtr prevLayer, CNNLayerPtr nextLayer, CNNLayerPtr copyLayer = std::make_shared(LayerParams({copyName, "Copy", Precision::FP32})); auto inputData = nextLayer->insData[beforeIdx].lock(); - auto newDims = inputData->getDims(); - auto dataPtr = std::make_shared(copyName, - TensorDesc(inputData->precision, - inputData->getDims(), - inputData->layout)); - + auto dataPtr = std::make_shared(copyName, inputData->getTensorDesc()); auto copyWithQuant = quantized ? InferenceEngine::injectData(copyLayer) : copyLayer; - dataPtr->creatorLayer = copyWithQuant; + dataPtr->getCreatorLayer() = copyWithQuant; copyWithQuant->outData.push_back(dataPtr); CNNNetworkInsertLayer(prevLayer, nextLayer, copyWithQuant); return copyWithQuant; @@ -217,26 +217,23 @@ void InsertDiagonalLayerPass::run() { } void HandleMultipleActivationsForTheLayerPass::run() { - // found layer followed by with multiple activations + // found layer followed by multiple activations for (auto & l : *pLayers) { std::set activations; - std::set identities; for (auto && odata : l->outData) { for (auto && inputTo : odata->getInputTo()) { LayerInfo info(inputTo.second); - if (info.isIdentity()) { - identities.insert(inputTo.second); - } else if (info.isActivation()) { + if (info.isActivation()) { activations.insert(inputTo.second); } } } // single or not activations case - if (activations.size() + identities.size() < 2) continue; + if (activations.size() < 2) continue; - // insert diagonals, but not for identity activations + // insert diagonals one per each activation for (auto && activation : activations) { insertDiagonalLayerBetween(l, activation, getPassManager(), 0.0f); } @@ -286,14 +283,14 @@ void SubstitutePReluPass::run() { CNNLayer* next = nullptr; if (layer == nullptr) return next; if (layer->outData.size() != 1) return next; - return layer->outData[0]->inputTo.begin()->second.get(); + return layer->outData[0]->getInputTo().begin()->second.get(); }; // TODO: unit tests for bad cases for (auto & l : *pLayers) { // assume l is starting layer, that is followed by eltwise_sum(relu, negate/relu/scale/negate) if (l->outData.size() != 1) continue; - auto &outputLayers = l->outData[0]->inputTo; + auto &outputLayers = l->outData[0]->getInputTo(); if (outputLayers.size() != 2) continue; // one of followed layers need to be generic relu @@ -328,8 +325,8 @@ void SubstitutePReluPass::run() { if (!LayerInfo(sum).isEltwiseSum()) continue; if (sum->insData.size() != 2) continue; - auto s1 = sum->insData[0].lock()->creatorLayer.lock().get(); - auto s2 = sum->insData[1].lock()->creatorLayer.lock().get(); + auto s1 = sum->insData[0].lock()->getCreatorLayer().lock().get(); + auto s2 = sum->insData[1].lock()->getCreatorLayer().lock().get(); if (s1 != static_cast(first) && s2 != static_cast(first)) { @@ -345,10 +342,10 @@ void SubstitutePReluPass::run() { // pointing relu to output of eltwise_summ relu1->outData = sum->outData; // changing creator layer - relu1->outData[0]->creatorLayer = relu1; + relu1->outData[0]->getCreatorLayer() = relu1; // pointing back to relu if any - if (!relu1->outData[0]->inputTo.empty()) { - auto summOutputLayer = relu1->outData[0]->inputTo.begin()->second; + if (!relu1->outData[0]->getInputTo().empty()) { + auto summOutputLayer = relu1->outData[0]->getInputTo().begin()->second; summOutputLayer->insData.clear(); summOutputLayer->insData.push_back(relu1->outData[0]); } @@ -382,10 +379,10 @@ void ReversePermutationsPass::run() { if (layer->outData.empty()) { return nullptr; } - if (layer->outData.front()->inputTo.size() != 1) { + if (layer->outData.front()->getInputTo().size() != 1) { return nullptr; } - auto next = layer->outData.front()->inputTo.begin()->second; + auto next = layer->outData.front()->getInputTo().begin()->second; if (LayerInfo(next).isReshape()) return nextLayerSkipReshape(next); @@ -470,22 +467,17 @@ void InsertIdentityLayerPass::run() { CNNLayerPtr activationLayer = std::make_shared(LayerParams({activationName, "identity", Precision::FP32})); auto inputData = l->insData[0].lock(); - auto newDims = inputData->dims; - std::reverse(begin(newDims), end(newDims)); - auto dataPtr = std::make_shared("identity_data_" + std::to_string(numOfIdentityLayers), - TensorDesc(inputData->precision, - newDims, - inputData->layout)); + auto dataPtr = std::make_shared("identity_data_" + std::to_string(numOfIdentityLayers), inputData->getTensorDesc()); auto activationLayerWithQuant = quantized ? InferenceEngine::injectData(activationLayer) : activationLayer; - dataPtr->creatorLayer = activationLayerWithQuant; + dataPtr->getCreatorLayer() = activationLayerWithQuant; activationLayerWithQuant->outData.push_back(dataPtr); // wether 1 identity or all outputs TODO possible grouping here, need to implement special groupped inserter bool notAll = false; for (auto && nextData : prev->outData) { - for (auto && nextLayer : nextData->inputTo) { + for (auto && nextLayer : nextData->getInputTo()) { if (nextLayer.second.get() == l.get()) continue; if (getCandidatesForIdentityInsertion(nextLayer.second).empty()) { @@ -613,20 +605,27 @@ void InsertConcatAligningFilterPass::run() { identityIdx += num_rows_in + 1; } - concatAligningFilter->_weights = make_shared_blob(concatInput->precision, Layout::C, filterWeights); + concatAligningFilter->_weights = make_shared_blob( + TensorDesc( + concatInput->getTensorDesc().getPrecision(), + SizeVector({filterWeights.size()}), + Layout::C)); + concatAligningFilter->_weights->allocate(); + + CopyVectorToBlob(concatAligningFilter->_weights, filterWeights); // modifying output rows to be used - to avoid modification to original concat we are store num of elements in params dims[1] = num_rows_out; auto outData = std::make_shared(filterName, - TensorDesc(concatInput->precision, + TensorDesc(concatInput->getPrecision(), dims, - concatInput->layout)); + concatInput->getLayout())); auto filterWithQuant = quantized ? InferenceEngine::injectData(concatAligningFilter) : concatAligningFilter; - outData->creatorLayer = filterWithQuant; + outData->getCreatorLayer() = filterWithQuant; filterWithQuant->outData.push_back(outData); CNNNetworkInsertLayer(prevLayer, l, filterWithQuant); @@ -665,8 +664,8 @@ void ReorderConcatInputsPass::run() { THROW_GNA_EXCEPTION << "no concat layer after concat-aligning layer" << l->name << ", but was: " << concat->type; } // 3stage locate first input in concat - if (concat->insData.size() != 2) { - THROW_GNA_EXCEPTION << "unsupported concat layer: " << concat->name; + if (concat->insData.size() < 2) { + THROW_GNA_EXCEPTION << "Concat layer has unsupported number of incoming layers: " << concat->name; } auto inputsToConcatFirst = CNNNetGetPrevLayersSkip(concat, [](CNNLayerPtr origin){ return !LayerInfo(origin).isReshape(); @@ -712,7 +711,7 @@ void ReorderConcatInputsPass::run() { auto linkOutData = std::make_shared(linkName, TensorDesc(Precision::FP32, - {1}, + SizeVector({1}), Layout::C)); linkOutData->getCreatorLayer() = link; @@ -763,7 +762,6 @@ void InsertSplitAligningFilterPass::run() { auto inputData = splitOutput; - auto newDims = splitOutput->dims; size_t aligned64_offset = std::max(0, static_cast(ALIGN64(currentOffset) - 64)); size_t newOutputSize = (currentOffset + ALIGN(outputSize, 8) * bytesPerSplitElement - aligned64_offset) @@ -772,12 +770,12 @@ void InsertSplitAligningFilterPass::run() { // encodes offset to beginning of split layer input filterLayer->params["offset"] = std::to_string(aligned64_offset); - auto dims = splitOutput->getDims(); + auto dims = splitOutput->getTensorDesc().getDims(); if (dims.size() > 3) { THROW_GNA_EXCEPTION << "unsupported split layer dims size: " << dims.size(); } - auto num_rows_out = dims[1] * (dims.size() != 2 ? dims[2] : 1); + auto num_rows_out = dims[1] * (dims.size() != 2 ? dims[2] : 1); std::vector filterWeights(newOutputSize * num_rows_out, 0.f); auto offset = (currentOffset - aligned64_offset) / bytesPerSplitElement; @@ -787,19 +785,22 @@ void InsertSplitAligningFilterPass::run() { offset += newOutputSize + 1; } - filterLayer->_weights = make_shared_blob(inputData->precision, Layout::C, filterWeights); - - std::reverse(begin(newDims), end(newDims)); + filterLayer->_weights = make_shared_blob(TensorDesc( + inputData->getTensorDesc().getPrecision(), + SizeVector({filterWeights.size()}), + Layout::C)); + filterLayer->_weights->allocate(); + CopyVectorToBlob(filterLayer->_weights, filterWeights); auto outData = std::make_shared(filterName, - TensorDesc(splitOutput->precision, - newDims, - inputData->layout)); + TensorDesc(splitOutput->getTensorDesc().getPrecision(), + splitOutput->getTensorDesc().getDims(), + inputData->getTensorDesc().getLayout())); auto filterWithQuant = quantized ? InferenceEngine::injectData(filterLayer) : filterLayer; - outData->creatorLayer = filterWithQuant; + outData->getCreatorLayer() = filterWithQuant; filterWithQuant->outData.push_back(outData); CNNNetworkInsertLayer(l, nullptr, filterWithQuant, splitOutIndex); } @@ -896,7 +897,6 @@ void SubstituteScaleShiftBroadCastPass::run() { } void UnrollLSTMCellPass::run() { - // TODO: iefode: refactor this code InferenceEngine::NetPass::UnrollRNN_if(*getPassManager()->getNetwork(), [] (const RNNCellBase& rnn) -> bool { if (rnn.clip != 0.0f) return true; @@ -919,6 +919,16 @@ void UnrollTIPass::run() { } } +void RemoveConstPass::run() { + auto network = getPassManager()->getNetwork(); + auto* implNetwork = dynamic_cast(network.get()); + if (!implNetwork) { + THROW_GNA_EXCEPTION << "Remove const layers pass can only work on cnnnetworkimpl type"; + } + ConstTransformer transformer(implNetwork); + transformer.fullTrim(); +} + void PassManager::run() { int index = 0; #ifdef PLOT diff --git a/inference-engine/src/gna_plugin/gna_pass_manager.hpp b/inference-engine/src/gna_plugin/gna_pass_manager.hpp index 2ea35ac1e1c87f..77c0c78b6ea22b 100644 --- a/inference-engine/src/gna_plugin/gna_pass_manager.hpp +++ b/inference-engine/src/gna_plugin/gna_pass_manager.hpp @@ -129,6 +129,12 @@ DECL_PASS(UnrollLSTMCell); */ DECL_PASS(UnrollTI); +/** +* @brief removed const layer before reshape layer +*/ +DECL_PASS(RemoveConst); + + class PassManager : public IPassManager, public std::enable_shared_from_this { Policy policy; InferenceEngine::CNNNetPtr network; diff --git a/inference-engine/src/gna_plugin/gna_plugin.cpp b/inference-engine/src/gna_plugin/gna_plugin.cpp index c15343cd11e212..9967b929621d4c 100644 --- a/inference-engine/src/gna_plugin/gna_plugin.cpp +++ b/inference-engine/src/gna_plugin/gna_plugin.cpp @@ -74,7 +74,7 @@ using namespace InferenceEngine::details; #define PAGE_SIZE_BYTES 4096 #define FROM_IR_DIM(mem, idx)\ -((mem->dims.size() > idx - 1) ? mem->dims[idx - 1] : 1) +((mem->getTensorDesc().getDims().size() > (idx) - 1) ? mem->getTensorDesc().getDims()[mem->getTensorDesc().getDims().size() - (idx)] : 1) inline int16_t GNAPluginNS::ConvertFloatToInt16(float src) { float rounding_value = (src > 0) ? 0.5f : -0.5f; @@ -248,7 +248,7 @@ void GNAPlugin::ExportScores(void *ptr_dst, break; } case 4 : { - *dst_ptr = *reinterpret_cast(input_ptr); + *dst_ptr = *reinterpret_cast(input_ptr); break; } default: @@ -370,7 +370,7 @@ void GNAPlugin::fillConcatConnections(InferenceEngine::CNNLayerPtr layer) { THROW_GNA_EXCEPTION << "Input layer pointer for concat is unexpectedly absent"; } - auto ptrConcatLayerInput = dataInput->creatorLayer.lock(); + auto ptrConcatLayerInput = dataInput->getCreatorLayer().lock(); if (!ptrConcatLayerInput) { THROW_GNA_EXCEPTION << "Input layer for concat is unexpectedly absent"; } @@ -378,8 +378,9 @@ void GNAPlugin::fillConcatConnections(InferenceEngine::CNNLayerPtr layer) { GNAPlugin::GNAConcatLayer::ConcatConnectedLayerInfo({ptrConcatLayerInput->name, concat_size})); size_t layer_size = - InferenceEngine::details::product(begin(dataInput->dims), - end(dataInput->dims)) * dataInput->precision.size(); + InferenceEngine::details::product(begin( + dataInput->getTensorDesc().getDims()), + end(dataInput->getTensorDesc().getDims())) * dataInput->getTensorDesc().getPrecision().size(); concat_size += layer_size; } layerInfoItem.reserved_size = concat_size; @@ -395,7 +396,7 @@ void GNAPlugin::fillSplitConnections(InferenceEngine::CNNLayerPtr layer) { if (!dataInput) { THROW_GNA_EXCEPTION << "Input layer pointer for split/slice is unexpectedly absent"; } - auto ptrSplitLayerInput = dataInput->creatorLayer.lock(); + auto ptrSplitLayerInput = dataInput->getCreatorLayer().lock(); if (!ptrSplitLayerInput) { THROW_GNA_EXCEPTION << "Input layer for split/slice is unexpectedly absent"; } @@ -417,10 +418,10 @@ void GNAPlugin::fillSplitConnections(InferenceEngine::CNNLayerPtr layer) { } padding = std::max(padding, LayerInfo(ptrSplitLayerOutput).paddingSize()) - * dataOutput->precision.size(); + * dataOutput->getPrecision().size(); output_layer_size = - InferenceEngine::details::product(begin(dataOutput->dims), - end(dataOutput->dims)) * dataOutput->precision.size(); + InferenceEngine::details::product(begin(dataOutput->getTensorDesc().getDims()), + end(dataOutput->getTensorDesc().getDims())) * dataOutput->getTensorDesc().getPrecision().size(); if (ptrSplitLayerOutput->type == "AffineFilter") { size_t aligned64_offset = ptrSplitLayerOutput->GetParamAsInt("offset"); @@ -435,8 +436,10 @@ void GNAPlugin::fillSplitConnections(InferenceEngine::CNNLayerPtr layer) { layerInfoItem.reserved_size = split_size; layerInfoItem.splitInputLayer = GNAPlugin::GNASplitLayer::SplitConnectedLayerInfo({ptrSplitLayerInput->type, 0, - InferenceEngine::details::product(begin(dataInput->dims), - end(dataInput->dims)) * dataInput->precision.size()}); + InferenceEngine::details::product( + begin(dataInput->getTensorDesc().getDims()), + end(dataInput->getTensorDesc().getDims())) + * dataInput->getTensorDesc().getPrecision().size()}); split_connection.emplace(id, layerInfoItem); } @@ -472,13 +475,29 @@ void GNAPlugin::ConvolutionPrimitive(InferenceEngine::CNNLayerPtr layer) { auto inputs = layer->insData.begin()->lock(); auto outputs = *layer->outData.begin(); - uint32_t num_feature_map_rows = FROM_IR_DIM(inputs, 1) / convolution._stride_x; - uint32_t num_feature_map_columns = FROM_IR_DIM(inputs, 3) * convolution._stride_x / num_feature_maps; + uint32_t w_dim_in = FROM_IR_DIM(inputs, 1); + uint32_t h_dim_in = FROM_IR_DIM(inputs, 2); + uint32_t c_dim_in = FROM_IR_DIM(inputs, 3); + uint32_t n_dim_in = FROM_IR_DIM(inputs, 4); + uint32_t w_dim_out = FROM_IR_DIM(outputs, 1); + uint32_t h_dim_out = FROM_IR_DIM(outputs, 2); - uint32_t num_rows_in = FROM_IR_DIM(inputs, 1); - uint32_t num_columns_in = FROM_IR_DIM(inputs, 3); - uint32_t num_rows_out = FROM_IR_DIM(outputs, 1); - uint32_t num_padding = ALIGN(convolution._kernel_x * num_feature_map_columns * num_feature_maps, 8) + if (w_dim_in == 1) { // swap dimensions if needed to support swapped 1D case + swap(h_dim_in, w_dim_in); + swap(h_dim_out, w_dim_out); + swap(convolution._kernel_x, convolution._kernel_y); + swap(convolution._stride_x, convolution._stride_y); + } + + uint32_t num_feature_map_rows = w_dim_in / convolution._stride_x; + uint32_t num_feature_map_columns = c_dim_in * convolution._stride_x / num_feature_maps; + + uint32_t num_rows_in = w_dim_in; + uint32_t num_columns_in = c_dim_in; + uint32_t num_rows_out = w_dim_out; + + // padding of convolution kernel to be multiply of 8 + uint32_t num_conv_kernel_padding = ALIGN(convolution._kernel_x * num_feature_map_columns * num_feature_maps, 8) - convolution._kernel_x * num_feature_map_columns * num_feature_maps; void *ptr_inputs; void *ptr_outputs; @@ -486,7 +505,7 @@ void GNAPlugin::ConvolutionPrimitive(InferenceEngine::CNNLayerPtr layer) { void *ptr_biases; // TODO: questionable why for biases that are not in IR we inventing precision - auto biasPrecision = convolution._biases ? convolution._biases->precision() : outputs->precision; + auto biasPrecision = convolution._biases ? convolution._biases->getTensorDesc().getPrecision() : outputs->getTensorDesc().getPrecision(); dnnComponentsForLayer.emplace_back(layer->name, intel_dnn_component_t()); auto ¤tComponent = dnnComponentsForLayer.back().second; @@ -494,21 +513,22 @@ void GNAPlugin::ConvolutionPrimitive(InferenceEngine::CNNLayerPtr layer) { #ifdef PLOT std::cout << "IR layer : " << std::left << std::setw(20) << layer->name << " convolution_" << dnnComponentsForLayer.size() - 1 << std::endl; #endif - auto num_input_padding = ALIGN(num_feature_maps * num_feature_map_columns * num_feature_map_rows, 8) - - num_feature_maps * num_feature_map_columns * num_feature_map_rows; + // have to pad input to let last kernel meets it's corresponding input + auto num_inputs = num_feature_maps * num_feature_map_columns * num_feature_map_rows + num_conv_kernel_padding; + auto num_input_padding = ALIGN(num_inputs, 8)- num_inputs; auto num_filter_rows = convolution._kernel_x / convolution._stride_x; dnn.InitConvolutional1DComponent(currentComponent, 1, - num_feature_maps * num_feature_map_columns * num_feature_map_rows + num_input_padding, + num_inputs + num_input_padding, 1, num_rows_out * convolution._out_depth, - inputs->precision.size(), - outputs->precision.size(), - convolution._weights->precision().size(), + inputs->getTensorDesc().getPrecision().size(), + outputs->getTensorDesc().getPrecision().size(), + convolution._weights->getTensorDesc().getPrecision().size(), biasPrecision.size(), convolution._out_depth, num_filter_rows, - num_feature_maps * num_feature_map_columns * num_filter_rows + num_padding, + num_feature_maps * num_feature_map_columns * num_filter_rows + num_conv_kernel_padding, num_feature_maps, // interesting - why this is so in gna_example num_feature_map_rows, @@ -525,10 +545,10 @@ void GNAPlugin::ConvolutionPrimitive(InferenceEngine::CNNLayerPtr layer) { num_feature_maps = convolution._out_depth; // = number of filters size_t num_data_bytes_out = - InferenceEngine::details::product(begin(outputs->dims), end(outputs->dims)) - * outputs->precision.size(); + InferenceEngine::details::product(begin(outputs->getDims()), end(outputs->getDims())) + * outputs->getPrecision().size(); - size_t num_data_bytes_in = num_columns_in * (num_rows_in + num_padding) * inputs->precision.size(); + size_t num_data_bytes_in = (num_inputs + num_input_padding) * inputs->getPrecision().size(); auto connectedInputLayer = connectInput(layer, ptr_inputs, num_data_bytes_in).input; @@ -563,10 +583,10 @@ void GNAPlugin::ConvolutionPrimitive(InferenceEngine::CNNLayerPtr layer) { transposedWeights.insert(transposedWeights.end(), transposedPart.begin(), transposedPart.end()); } - if (num_padding == 0) { + if (num_conv_kernel_padding == 0) { gnamem->readonly().push_local_ptr(ptr_weights, transposedWeights.data(), convolution._weights->byteSize(), 64); } else { - auto elementsIn = convolution._kernel_x * num_feature_map_columns + num_padding; + auto elementsIn = convolution._kernel_x * num_feature_map_columns + num_conv_kernel_padding; auto paddedWeights = elementsIn * convolution._out_depth; auto paddedWeightsSize = paddedWeights * convolution.precision.size(); auto elements_in_row = convolution._kernel_x * num_feature_map_columns; @@ -619,11 +639,11 @@ void GNAPlugin::PowerPrimitive(InferenceEngine::CNNLayerPtr layer) { num_rows_in, num_columns_in, num_rows_out, - input->precision.size(), - outputs->precision.size(), + input->getPrecision().size(), + outputs->getPrecision().size(), // TODO: only fp32 and Int16 tested - quantized == nullptr ? input->precision.size() : 2, - quantized == nullptr ? input->precision.size() : 4, + quantized == nullptr ? input->getPrecision().size() : 2, + quantized == nullptr ? input->getPrecision().size() : 4, quantized == nullptr ? 1 : quantized->_weights_quant.scale, quantized == nullptr ? 1 : quantized->_dst_quant.scale, ptr_inputs, @@ -636,11 +656,11 @@ void GNAPlugin::PowerPrimitive(InferenceEngine::CNNLayerPtr layer) { std::cout << "IR layer : " << std::left << std::setw(20) << layer->name << " diagonal_"<< dnnComponentsForLayer.size() - 1 << std::endl; #endif - size_t num_data_bytes_out = InferenceEngine::details::product(begin(outputs->dims), end(outputs->dims)) - * outputs->precision.size(); + size_t num_data_bytes_out = InferenceEngine::details::product(begin(outputs->getDims()), end(outputs->getDims())) + * outputs->getPrecision().size(); - size_t num_data_bytes_in = InferenceEngine::details::product(begin(input->dims), end(input->dims)) - * input->precision.size(); + size_t num_data_bytes_in = InferenceEngine::details::product(begin(input->getDims()), end(input->getDims())) + * input->getPrecision().size(); connectOutput(layer, ptr_outputs, num_data_bytes_out); connectInput(layer, ptr_inputs, num_data_bytes_in, 0, 0); @@ -678,10 +698,25 @@ void GNAPlugin::PoolingPrimitive(InferenceEngine::CNNLayerPtr layer) { auto inputs = layer->insData.begin()->lock(); auto outputs = *layer->outData.begin(); - uint32_t num_rows_in = FROM_IR_DIM(inputs, 1); - uint32_t num_columns_in = FROM_IR_DIM(inputs, 3); - uint32_t num_rows_out = FROM_IR_DIM(outputs, 1); - uint32_t num_columns_out = FROM_IR_DIM(outputs, 3); + uint32_t w_dim_in = FROM_IR_DIM(inputs, 1); + uint32_t h_dim_in = FROM_IR_DIM(inputs, 2); + uint32_t c_dim_in = FROM_IR_DIM(inputs, 3); + uint32_t n_dim_in = FROM_IR_DIM(inputs, 4); + uint32_t w_dim_out = FROM_IR_DIM(outputs, 1); + uint32_t h_dim_out = FROM_IR_DIM(outputs, 2); + uint32_t c_dim_out = FROM_IR_DIM(outputs, 3); + uint32_t n_dim_out = FROM_IR_DIM(outputs, 4); + + if (w_dim_in == 1) { // swap dimensions if needed to support swapped 1D case + swap(h_dim_in, w_dim_in); + swap(h_dim_out, w_dim_out); + swap(pooling._kernel[X_AXIS], pooling._kernel[Y_AXIS]); + } + + uint32_t num_rows_in = w_dim_in; + uint32_t num_columns_in = c_dim_in; + uint32_t num_rows_out = w_dim_out; + uint32_t num_columns_out = c_dim_out; uint32_t num_padding = ALIGN(num_rows_in, 8) - num_rows_in; void *ptr_inputs; @@ -707,8 +742,8 @@ void GNAPlugin::PoolingPrimitive(InferenceEngine::CNNLayerPtr layer) { num_columns_in * num_rows_in , 1, num_columns_out * num_rows_out, - inputs->precision.size(), - outputs->precision.size(), + inputs->getPrecision().size(), + outputs->getPrecision().size(), pooling._kernel[X_AXIS], pooling._kernel[X_AXIS], num_columns_in, @@ -717,10 +752,10 @@ void GNAPlugin::PoolingPrimitive(InferenceEngine::CNNLayerPtr layer) { ptr_inputs, ptr_outputs); - size_t num_data_bytes_out = InferenceEngine::details::product(begin(outputs->dims), end(outputs->dims)) - * outputs->precision.size(); + size_t num_data_bytes_out = InferenceEngine::details::product(begin(outputs->getDims()), end(outputs->getDims())) + * outputs->getPrecision().size(); - size_t num_data_bytes_in = num_columns_in * (num_rows_in + num_padding) * inputs->precision.size(); + size_t num_data_bytes_in = num_columns_in * (num_rows_in + num_padding) * inputs->getPrecision().size(); connectInput(layer, ptr_inputs, num_data_bytes_in); connectOutput(layer, ptr_outputs, num_data_bytes_out); @@ -755,8 +790,8 @@ void GNAPlugin::CopyPrimitive(InferenceEngine::CNNLayerPtr layer) { num_columns_in, ALIGN(num_rows_out, 8), num_columns_out, - inputs->precision.size(), - outputs->precision.size(), + inputs->getPrecision().size(), + outputs->getPrecision().size(), quantized == nullptr ? 1 : quantized->_dst_quant.scale, num_rows_out + num_padding_out, num_columns_out, @@ -764,9 +799,9 @@ void GNAPlugin::CopyPrimitive(InferenceEngine::CNNLayerPtr layer) { ptr_outputs); size_t num_data_bytes_out = ALIGN(InferenceEngine::details::product( - begin(outputs->dims), end(outputs->dims)), 8) - * outputs->precision.size(); - size_t num_data_bytes_in = num_columns_in * ALIGN(num_rows_in, 8) * inputs->precision.size(); + begin(outputs->getDims()), end(outputs->getDims())), 8) + * outputs->getPrecision().size(); + size_t num_data_bytes_in = num_columns_in * ALIGN(num_rows_in, 8) * inputs->getPrecision().size(); connectInput(layer, ptr_inputs, num_data_bytes_in); connectOutput(layer, ptr_outputs, num_data_bytes_out); @@ -778,17 +813,25 @@ void GNAPlugin::ConcatPrimitive(InferenceEngine::CNNLayerPtr layer) { if (concatLayer == nullptr) { return; } - if (concatLayer->insData.size() != 2) { + if (concatLayer->insData.size() < 2) { THROW_GNA_EXCEPTION << "Concat layer has unsupported number of incoming layers."; } - auto prevInput0 = concatLayer->insData[0].lock(); - auto prevInput1 = concatLayer->insData[1].lock(); - if (!prevInput0 || !prevInput1) { - THROW_GNA_EXCEPTION << "Input layer for concat is unexpectedly absent"; + for (std::size_t layerIndex = 0; layerIndex < concatLayer->insData.size(); layerIndex++) { + auto input = concatLayer->insData[layerIndex].lock(); + if (!input) { + THROW_GNA_EXCEPTION << "Input layer " << layerIndex << " for concat is unexpectedly absent"; + } } - if (prevInput0->precision.size() != prevInput1->precision.size()) { - THROW_GNA_EXCEPTION << "Different precision for Concat input layers are not supported"; + + std::size_t layerPrecisionSize = concatLayer->insData[0].lock()->getPrecision().size(); + for (std::size_t layerIndex = 0; layerIndex < concatLayer->insData.size(); layerIndex++) { + auto currentSize = concatLayer->insData[layerIndex].lock()->getPrecision().size(); + if (layerPrecisionSize != currentSize) { + THROW_GNA_EXCEPTION << "Different precision for Concat Layer '" << concatLayer->name << "' input layers." << + "input 0 precision is '" << concatLayer->insData[0].lock()->getPrecision().name() << "' but input " << layerIndex << + " precision is '" << concatLayer->insData[layerIndex].lock()->getPrecision().name() << "'"; + } } auto& concatLayerInfo = concat_connection.find(concatLayer->name)->second; @@ -871,7 +914,7 @@ void GNAPlugin::CropPrimitive(InferenceEngine::CNNLayerPtr layer) { uint32_t num_rows_in = FROM_IR_DIM(inputs, inputs->getDims().size() - cropLayer->axis[0]); uint32_t num_columns_in = 1; - uint32_t num_rows_out = FROM_IR_DIM(outputs, outputs->getDims().size() - cropLayer->axis[0]); + uint32_t num_rows_out = FROM_IR_DIM(outputs, inputs->getDims().size() - cropLayer->axis[0]); uint32_t num_padding = ALIGN(num_rows_in, 8) - num_rows_in; void *ptr_inputs; @@ -890,9 +933,9 @@ void GNAPlugin::CropPrimitive(InferenceEngine::CNNLayerPtr layer) { num_rows_in + num_padding, num_columns_in, num_rows_out, - inputs->precision.size(), + inputs->getPrecision().size(), 4, - quantized == nullptr ? inputs->precision.size() : 2, + quantized == nullptr ? inputs->getPrecision().size() : 2, 4, quantized == nullptr ? 1 : quantized->_weights_quant.scale, quantized == nullptr ? 1 : quantized->_dst_quant.scale, @@ -904,10 +947,10 @@ void GNAPlugin::CropPrimitive(InferenceEngine::CNNLayerPtr layer) { size_t num_data_bytes_out = InferenceEngine::details::product( - begin(outputs->dims), end(outputs->dims)) * 4; + begin(outputs->getDims()), end(outputs->getDims())) * 4; size_t num_data_bytes_in = num_columns_in * - ALIGN(num_rows_in, 8) * inputs->precision.size(); + ALIGN(num_rows_in, 8) * inputs->getPrecision().size(); connectInput(layer, ptr_inputs, num_data_bytes_in, 0, 0); connectOutput(layer, ptr_outputs, num_data_bytes_out); @@ -940,16 +983,16 @@ void GNAPlugin::EltwisePrimitive(InferenceEngine::CNNLayerPtr layer) { if (quantized) { if (eltwise._operation == EltwiseLayer::Sum) { - if (inputs4Bytes->precision.size() != 4) { + if (inputs4Bytes->getPrecision().size() != 4) { std::swap(inputs4Bytes, inputs2Bytes); biasesLayerIdx = 0; } - IE_ASSERT(inputs2Bytes->precision.size() == 2); - IE_ASSERT(inputs4Bytes->precision.size() == 4); + IE_ASSERT(inputs2Bytes->getPrecision().size() == 2); + IE_ASSERT(inputs4Bytes->getPrecision().size() == 4); } else { // for mul both inputs should be 2 bytes precision - IE_ASSERT(inputs2Bytes->precision.size() == 2); - IE_ASSERT(inputs4Bytes->precision.size() == 2); + IE_ASSERT(inputs2Bytes->getPrecision().size() == 2); + IE_ASSERT(inputs4Bytes->getPrecision().size() == 2); } } @@ -971,11 +1014,11 @@ void GNAPlugin::EltwisePrimitive(InferenceEngine::CNNLayerPtr layer) { num_rows_in + num_padding, num_columns_in, num_rows_out + num_padding, - inputs2Bytes->precision.size(), - outputs->precision.size(), + inputs2Bytes->getPrecision().size(), + outputs->getPrecision().size(), // TODO: only fp32 and Int16 tested - quantized == nullptr ? inputs2Bytes->precision.size() : 2, - quantized == nullptr ? inputs4Bytes->precision.size() : 4, + quantized == nullptr ? inputs2Bytes->getPrecision().size() : 2, + quantized == nullptr ? inputs4Bytes->getPrecision().size() : 4, quantized == nullptr ? 1 : quantized->_weights_quant.scale, quantized == nullptr ? 1 : quantized->_dst_quant.scale, ptr_inputs, @@ -989,10 +1032,10 @@ void GNAPlugin::EltwisePrimitive(InferenceEngine::CNNLayerPtr layer) { #endif size_t num_data_bytes_out = - InferenceEngine::details::product(begin(outputs->dims), end(outputs->dims)) * outputs->precision.size(); + InferenceEngine::details::product(begin(outputs->getDims()), end(outputs->getDims())) * outputs->getPrecision().size(); size_t num_data_bytes_in = - num_columns_in * (num_rows_in + num_padding) * inputs2Bytes->precision.size(); + num_columns_in * (num_rows_in + num_padding) * inputs2Bytes->getPrecision().size(); connectOutput(layer, ptr_outputs, num_data_bytes_out); connectInput(layer, ptr_inputs, num_data_bytes_in, 0, 1 - biasesLayerIdx); @@ -1033,12 +1076,13 @@ void GNAPlugin::AffinePrimitive(InferenceEngine::CNNLayerPtr layer, bool isDiag) auto inputs = layer->insData.begin()->lock(); auto outputs = *layer->outData.begin(); - auto inputPrecision = quantized ? Precision(Precision::I16) : inputs->precision; + auto inputPrecision = quantized ? Precision(Precision::I16) : inputs->getPrecision(); uint32_t num_rows_in = FROM_IR_DIM(inputs, 1); uint32_t num_columns_in = FROM_IR_DIM(inputs, 2); uint32_t num_rows_out = isDiag ? num_rows_in : FROM_IR_DIM(outputs, 1); uint32_t num_padding = ALIGN(num_rows_in, 8) - num_rows_in; + uint32_t num_padding_out = isDiag ? num_padding : 0; void *ptr_inputs; void *ptr_outputs; @@ -1046,7 +1090,7 @@ void GNAPlugin::AffinePrimitive(InferenceEngine::CNNLayerPtr layer, bool isDiag) void *ptr_biases; // TODO: questionable why for biases that are no in Model we inventing precision - auto biasPrecision = weightable._biases ? weightable._biases->precision() : outputs->precision; + auto biasPrecision = weightable._biases ? weightable._biases->getTensorDesc().getPrecision() : outputs->getPrecision(); // layer without biases might be connected to functional layer without activations auto prevLayer = CNNNetPrevLayer(layer); @@ -1071,10 +1115,10 @@ void GNAPlugin::AffinePrimitive(InferenceEngine::CNNLayerPtr layer, bool isDiag) dnn.InitAffineComponent(currentComponent, num_rows_in + num_padding, num_columns_in, - num_rows_out, + num_rows_out + num_padding_out, inputPrecision.size(), - outputs->precision.size(), - weightable._weights->precision().size(), + outputs->getPrecision().size(), + weightable._weights->getTensorDesc().getPrecision().size(), biasPrecision.size(), quantized == nullptr ? 1 : quantized->_weights_quant.scale, quantized == nullptr ? 1 : quantized->_dst_quant.scale, @@ -1084,10 +1128,10 @@ void GNAPlugin::AffinePrimitive(InferenceEngine::CNNLayerPtr layer, bool isDiag) ptr_biases, isDiag); - size_t num_data_bytes_out = InferenceEngine::details::product(begin(outputs->dims), end(outputs->dims)) - * outputs->precision.size(); + size_t num_data_bytes_out = + num_columns_in * (num_rows_out + num_padding_out) * outputs->getPrecision().size(); - size_t num_data_bytes_in = num_columns_in * (num_rows_in + num_padding) * inputs->precision.size(); + size_t num_data_bytes_in = num_columns_in * (num_rows_in + num_padding) * inputs->getPrecision().size(); auto connectionInfo = connectInput(layer, useBiasConnection ? ptr_biases : ptr_inputs, num_data_bytes_in); connectOutput(layer, ptr_outputs, num_data_bytes_out); @@ -1107,7 +1151,7 @@ void GNAPlugin::AffinePrimitive(InferenceEngine::CNNLayerPtr layer, bool isDiag) /** * TODO: weights transpose happened after quantisation might result in poor quality for in 8 - move this to passes */ - if (weightable._weights->precision() == Precision::I8) { + if (weightable._weights->getTensorDesc().getPrecision() == Precision::I8) { THROW_IE_EXCEPTION << "[GNA plugin] Unsupported permute operation for 8 bit weights for layer: " << layer->name; } @@ -1149,7 +1193,7 @@ void GNAPlugin::AffinePrimitive(InferenceEngine::CNNLayerPtr layer, bool isDiag) } } else { if (transpose) { - THROW_GNA_EXCEPTION << "transpozed weights with non zero padding not yet supported"; + THROW_GNA_EXCEPTION << "transposed weights with non zero padding not yet supported"; } auto elementsIn = (num_rows_in + num_padding) * num_columns_in; auto paddedWeights = isDiag ? elementsIn : elementsIn * num_rows_out; @@ -1167,15 +1211,15 @@ void GNAPlugin::AffinePrimitive(InferenceEngine::CNNLayerPtr layer, bool isDiag) if (weightable._biases) { gnamem->readonly().push_ptr(ptr_biases, - weightable._biases->cbuffer().as(), - weightable._biases->byteSize(), - 64); + weightable._biases->cbuffer().as(), + weightable._biases->byteSize(), + 64); } else { // in that case input from previous layer goes into biases, so we have to initialize input pointer by zero if (useBiasConnection) { - gnamem->readonly().push_value(ptr_inputs, 0.0f, num_rows_in, 64); + gnamem->readonly().push_value(ptr_inputs, 0.0f, num_rows_in + num_padding, 64); } else { - gnamem->readonly().push_value(ptr_biases, 0.0f, num_rows_out, 64); + gnamem->readonly().push_value(ptr_biases, 0.0f, num_rows_out + num_padding_out, 64); } } } @@ -1232,7 +1276,7 @@ void GNAPlugin::ConcatAlignFilterPrimitive(InferenceEngine::CNNLayerPtr layer) { uint32_t num_rows_in = filterLayer->_weights->size() / num_rows_out; uint32_t num_padding = ALIGN(num_rows_in, 8) - num_rows_in; - auto biasPrecision = filterLayer->_biases ? filterLayer->_biases->precision() : outputs->precision; + auto biasPrecision = filterLayer->_biases ? filterLayer->_biases->getTensorDesc().getPrecision() : outputs->getPrecision(); dnnComponentsForLayer.emplace_back(layer->name, intel_dnn_component_t()); auto ¤tComponent = dnnComponentsForLayer.back().second; #ifdef PLOT @@ -1243,9 +1287,9 @@ void GNAPlugin::ConcatAlignFilterPrimitive(InferenceEngine::CNNLayerPtr layer) { num_rows_in + num_padding, num_columns_in, num_rows_out, - inputs->precision.size(), - outputs->precision.size(), - filterLayer->_weights->precision().size(), + inputs->getPrecision().size(), + outputs->getPrecision().size(), + filterLayer->_weights->getTensorDesc().getPrecision().size(), biasPrecision.size(), quantized == nullptr ? 1 : quantized->_weights_quant.scale, quantized == nullptr ? 1 : quantized->_dst_quant.scale, @@ -1257,10 +1301,10 @@ void GNAPlugin::ConcatAlignFilterPrimitive(InferenceEngine::CNNLayerPtr layer) { size_t num_data_bytes_out = InferenceEngine::details::product( - begin(outputs->dims), end(outputs->dims)) * 4; + begin(outputs->getDims()), end(outputs->getDims())) * 4; size_t num_data_bytes_in = num_columns_in * - ALIGN(num_rows_in, 8) * inputs->precision.size(); + ALIGN(num_rows_in, 8) * inputs->getPrecision().size(); connectInput(layer, ptr_inputs, num_data_bytes_in, 0, 0); connectOutput(layer, ptr_outputs, num_data_bytes_out); @@ -1327,16 +1371,16 @@ void GNAPlugin::AffineFilterPrimitive(InferenceEngine::CNNLayerPtr layer) { #ifdef PLOT gnalog() << "IR layer : " << std::left << std::setw(20) << layer->name << (" affine_") << dnnComponentsForLayer.size() - 1 << std::endl; #endif - auto biasPrecision = filterLayer->_biases ? filterLayer->_biases->precision() : outputs->precision; + auto biasPrecision = filterLayer->_biases ? filterLayer->_biases->getTensorDesc().getPrecision() : outputs->getPrecision(); dnnComponentsForLayer.emplace_back(layer->name, intel_dnn_component_t()); auto ¤tComponent = dnnComponentsForLayer.back().second; dnn.InitAffineComponent(currentComponent, num_rows_in + num_padding, num_columns_in, num_rows_out, - inputs->precision.size(), - outputs->precision.size(), - filterLayer->_weights->precision().size(), + inputs->getPrecision().size(), + outputs->getPrecision().size(), + filterLayer->_weights->getTensorDesc().getPrecision().size(), biasPrecision.size(), quantized == nullptr ? 1 : quantized->_weights_quant.scale, quantized == nullptr ? 1 : quantized->_dst_quant.scale, @@ -1348,10 +1392,10 @@ void GNAPlugin::AffineFilterPrimitive(InferenceEngine::CNNLayerPtr layer) { size_t num_data_bytes_out = InferenceEngine::details::product( - begin(outputs->dims), end(outputs->dims)) * 4; + begin(outputs->getDims()), end(outputs->getDims())) * 4; size_t num_data_bytes_in = num_columns_in * - ALIGN(num_rows_in, 8) * inputs->precision.size(); + ALIGN(num_rows_in, 8) * inputs->getPrecision().size(); connectInput(layer, ptr_inputs, num_data_bytes_in, 0, 0); connectOutput(layer, ptr_outputs, num_data_bytes_out); @@ -1418,19 +1462,24 @@ void GNAPlugin::PWLPrimitive(InferenceEngine::CNNLayerPtr layer) { auto orientation = (num_cnn_rows_out > 0) ? kDnnNonInterleavedOrientation : kDnnInterleavedOrientation; - if (inputs->dims.size() == 4) { - num_columns = FROM_IR_DIM(inputs, 3) * FROM_IR_DIM(inputs, 1); + if (inputs->getDims().size() == 4) { + uint32_t w_dim_in = FROM_IR_DIM(inputs, 1); + uint32_t h_dim_in = FROM_IR_DIM(inputs, 2); + uint32_t c_dim_in = FROM_IR_DIM(inputs, 3); + uint32_t n_dim_in = FROM_IR_DIM(inputs, 4); + + num_columns = (w_dim_in == 1) ? h_dim_in * c_dim_in : w_dim_in * c_dim_in; num_rows = 1; } else { num_columns = FROM_IR_DIM(inputs, 2); num_rows = FROM_IR_DIM(inputs, 1); } - size_t num_data_bytes_out = InferenceEngine::details::product(begin(outputs->dims), end(outputs->dims)) - * outputs->precision.size(); + size_t num_data_bytes_out = InferenceEngine::details::product(begin(outputs->getDims()), end(outputs->getDims())) + * outputs->getPrecision().size(); - size_t num_data_bytes_in = InferenceEngine::details::product(begin(inputs->dims), end(inputs->dims)) - * inputs->precision.size(); + size_t num_data_bytes_in = InferenceEngine::details::product(begin(inputs->getDims()), end(inputs->getDims())) + * inputs->getPrecision().size(); static caseless_unordered_map supportedActivations = { {"sigmoid", kActSigmoid}, @@ -1460,7 +1509,7 @@ void GNAPlugin::PWLPrimitive(InferenceEngine::CNNLayerPtr layer) { intel_pwl_segment_t *ptr_pwl_segments_target = nullptr; - if (!inputs->precision.is_float()) { + if (!inputs->getPrecision().is_float()) { // TODO: generalize activation function code // now that scale factors are known, create PWL approximations to activation functions float input_scale_factor = dnn.OutputScaleFactor(prevComponent); @@ -1499,8 +1548,8 @@ void GNAPlugin::PWLPrimitive(InferenceEngine::CNNLayerPtr layer) { orientation, num_rows, num_columns, - inputs->precision.size(), - outputs->precision.size(), + inputs->getPrecision().size(), + outputs->getPrecision().size(), ptr_pwl_segments.size(), output_scale_factor, ptr_inputs, @@ -1632,6 +1681,7 @@ GNAPluginNS::GNAPlugin::LayerType GNAPlugin::LayerTypeFromStr(const std::string } bool GNAPlugin::AreLayersSupported(ICNNNetwork& network, std::string& errMessage) { + IE_SUPPRESS_DEPRECATED_START CNNLayerSet inputLayers; InferenceEngine::InputsDataMap inputs; std::unordered_set allLayers; @@ -1640,9 +1690,12 @@ bool GNAPlugin::AreLayersSupported(ICNNNetwork& network, std::string& errMessage network.getInputsInfo(inputs); auto network_input_precision = inputs.begin()->second->getPrecision(); auto batch_size = network.getBatchSize(); - if (network_precision != Precision::FP32 && network_precision != Precision::FP16) { - errMessage = "The plugin does not support networks with " + std::string(network_precision.name()) + " format. Supported network precisions are FP32, " - "FP16\n"; + + if (network_precision != Precision::FP32 && + network_precision != Precision::FP16 && + network_precision != Precision::MIXED) { + errMessage = "The plugin does not support networks with " + + std::string(network_precision.name()) + " format. Supported network precisions are FP32, FP16, MIXED\n"; return false; } if (network_input_precision != Precision::FP32 && @@ -1684,7 +1737,7 @@ bool GNAPlugin::AreLayersSupported(ICNNNetwork& network, std::string& errMessage check_result = false; } }, false); - + IE_SUPPRESS_DEPRECATED_END return check_result; } @@ -1698,6 +1751,7 @@ void GNAPlugin::LoadNetwork(ICNNNetwork &network) { // network optimisation phases auto run_passes = [&] (CNNNetPtr network) { auto passes = make_shared(policy, network); + passes->registerPass(); passes->registerPass(); passes->registerPass(); passes->registerPass(); @@ -1714,12 +1768,11 @@ void GNAPlugin::LoadNetwork(ICNNNetwork &network) { passes->registerPass(); passes->registerPass(); passes->registerPass(); - passes->run(); }; Config supported = Config({ - {TargetDevice::eGNA, {Precision::FP32, Precision::FP16}, [&](InferenceEngine::ICNNNetwork &network) -> CNNNetworkPtr { + {TargetDevice::eGNA, {Precision::FP32, Precision::FP16, Precision::MIXED}, [&](InferenceEngine::ICNNNetwork &network) -> CNNNetworkPtr { if (gnaPrecision == Precision::I16) { ModelQuantizer q; return q.quantize(network, run_passes, inputScaleFactors); @@ -1734,7 +1787,7 @@ void GNAPlugin::LoadNetwork(ICNNNetwork &network) { // TODO: need to have advanced precision matcher based on layers/biases {TargetDevice::eGNA, {Precision::MIXED}}, {TargetDevice::eGNA, {Precision::I16}}, - {TargetDevice::eCPU, {Precision::FP32} + {TargetDevice::eCPU, {Precision::FP32, Precision::MIXED} #define EMULATE_GNA_API_LAYERS #ifdef EMULATE_GNA_API_LAYERS , [&](InferenceEngine::ICNNNetwork & network) { @@ -1754,6 +1807,7 @@ void GNAPlugin::LoadNetwork(ICNNNetwork &network) { supported.setDefaultDevice(sw_fp32 ? TargetDevice::eCPU : TargetDevice::eGNA); auto newNet = supported.find_configuration(network).convert(network); + auto inputLayers = CNNNetGetAllInputLayers(*newNet); auto sortedNet = CNNNetSortTopologicallyEx(*newNet, make_fuzed_order); std::vector sortedNoMem; @@ -1821,7 +1875,6 @@ void GNAPlugin::LoadNetwork(ICNNNetwork &network) { THROW_GNA_EXCEPTION << "cannot infer topologies with more than one output"; } } - outputDims = outputsDataMap.begin()->second->dims; for (auto && input : inputsDataMap) { get_ptr_inputs_global(input.first).resize(gna_lib_async_threads_num); @@ -1834,6 +1887,12 @@ void GNAPlugin::LoadNetwork(ICNNNetwork &network) { for (auto layer = sortedNoMem.begin(); layer != sortedNoMem.end(); ++layer) { CreateLayerPrimitive(*layer); } + for (auto& inputLayer : inputLayers) { + auto layerInfo = LayerInfo(inputLayer); + if (layerInfo.isInput() && 0 == bytes_alllocated_for_input[inputLayer->name]) { + connectOutput(inputLayer, &get_ptr_inputs_global(inputLayer->name).front(), 0); + } + } if (dnnComponentsForLayer.empty()) { THROW_GNA_EXCEPTION << "No outputs found in dnn components structure"; } @@ -2055,16 +2114,17 @@ uint32_t GNAPlugin::QueueInference(const InferenceEngine::BlobMap &inputs, Infer auto nnet = std::get<0>(*freeNnet).get(); auto idx = static_cast(std::distance(std::begin(nnets), freeNnet)); + int inputNum = 0; for (auto &input : inputs) { - auto inputLayout = input.second->layout(); + auto inputLayout = input.second->getTensorDesc().getLayout(); if (inputLayout != Layout::NC && inputLayout != Layout::CN && inputLayout != NCHW) { THROW_GNA_EXCEPTION << "Expected input blob to have Layout::NC or Layout::CN, but was: " - << input.second->layout(); + << input.second->getTensorDesc().getLayout(); } if (inputLayout == NCHW) { inputLayout = NC; } - auto is2D = input.second->layout() == Layout::NC || input.second->layout() == Layout::CN; + auto is2D = input.second->getTensorDesc().getLayout() == Layout::NC || input.second->getTensorDesc().getLayout() == Layout::CN; if (!ptr_inputs_global_id.count(input.first)) { // should not happen in user code however might happen if there any non executable network based integration of GNAPlugin instance @@ -2087,17 +2147,17 @@ uint32_t GNAPlugin::QueueInference(const InferenceEngine::BlobMap &inputs, Infer THROW_GNA_EXCEPTION << "network not loaded : output orientation not set"; } - auto dims = input.second->dims(); + auto dims = input.second->getTensorDesc().getDims(); ImportFrames(get_ptr_inputs_global(input.first)[idx], input.second->cbuffer().as(), - input.second->precision(), - inputScaleFactors.size() <= idx ? 1.0 : inputScaleFactors[idx], + input.second->getTensorDesc().getPrecision(), + sw_fp32 ? 1.0f : inputScaleFactors[inputNum], orientation_in[input.first], - dims[dims.size() - 1], - is2D ? dims[1] : dims[dims.size() - 1], - is2D ? dims[0] : dims[0] * dims[1] * dims[2], - is2D ? dims[0] : dims[0] * dims[1] * dims[2]); + dims[0], + is2D ? dims[dims.size() - 2] : dims[0], + is2D ? dims[dims.size() - 1] : dims[dims.size() - 1] * dims[dims.size() - 2] * dims[dims.size() - 3], + is2D ? dims[dims.size() - 1] : dims[dims.size() - 1] * dims[dims.size() - 2] * dims[dims.size() - 3]); bool isOneChannel = input.second->getTensorDesc().getDims()[1] == 1; if (((inputLayout == Layout::NC || inputLayout == Layout::NCHW) @@ -2106,11 +2166,12 @@ uint32_t GNAPlugin::QueueInference(const InferenceEngine::BlobMap &inputs, Infer RotateFeatures(reinterpret_cast(get_ptr_inputs_global(input.first)[idx]), gnadevice ? 2 : 4, // TODO: only works for cnn4a and google command so far - dims[dims.size() - 1], - is2D ? dims[0] : dims[0] * dims[2], // num_feature_vectors looks batch should be there + dims[0], + is2D ? dims[dims.size() - 1] : dims[dims.size() - 1] * dims[dims.size() - 3], // num_feature_vectors looks batch should be there num_rotate_rows, num_rotate_columns); } + ++inputNum; } if (!gnadevice) { @@ -2139,14 +2200,14 @@ void GNAPlugin::Wait(uint32_t idx) { dnn.WriteDnnText("Net_.txt", kDnnFloat); dnn.WriteInputAndOutputText(); } - dnn.WriteInputAndOutputTextGNA(&std::get<0>(nnets.front())->obj); + dnn.WriteInputAndOutputTextGNA(&std::get<0>(nnets[idx])->obj); #endif if (result.size() != 1) { THROW_GNA_EXCEPTION << "Invalid number of outputs for infer request: " << result.size() << ", only 1 supported"; } auto & output = *result.begin()->second; - if (output.layout() == Layout::NC) { + if (output.getTensorDesc().getLayout() == Layout::NC) { // TODO: rotate can be incorporated with exporting - used only in unit tests so far // TODO: restore: // if (orientation_out != kDnnInterleavedOrientation) { @@ -2174,19 +2235,20 @@ void GNAPlugin::Wait(uint32_t idx) { } } + auto exportOutputDims = output.getTensorDesc().getDims(); ExportScores(output.buffer(), ptr_outputs_global[idx], orientation_out, - output.dims()[output.dims().size() - 1], - output.dims()[1], - output.dims()[0], - output.dims()[0], - output.dims()[0], + exportOutputDims[0], + exportOutputDims[exportOutputDims.size() - 2], + exportOutputDims[exportOutputDims.size() - 1], + exportOutputDims[exportOutputDims.size() - 1], + exportOutputDims[exportOutputDims.size() - 1], // TODO: create better getter consider multiple outputs case gnadevice ? std::get<0>(nnets[idx])->obj.pLayers[output_layer_index].nBytesPerOutput : sizeof(float), sizeof(float)); - } else if (output.layout() != Layout::CN) { - THROW_GNA_EXCEPTION << "Expected output blob to have Layout::NC or Layout::CN. But was " << output.layout(); + } else if (output.getTensorDesc().getLayout() != Layout::CN) { + THROW_GNA_EXCEPTION << "Expected output blob to have Layout::NC or Layout::CN. But was " << output.getTensorDesc().getLayout(); } if (gnadevice) { @@ -2198,9 +2260,10 @@ void GNAPlugin::Wait(uint32_t idx) { } num_infers++; if (f) { - for (int i = 0; i < output.dims()[1]; i++) { - for (int j = 0; j < output.dims()[0]; j++) { - fprintf(f, "%d ", output.cbuffer().as()[output.dims()[0] * i + j]); + auto dims = output.getTensorDesc().getDims(); + for (int i = 0; i < dims[dims.size() - 2]; i++) { + for (int j = 0; j < dims[dims.size() - 1]; j++) { + fprintf(f, "%d ", output.cbuffer().as()[dims[dims.size() - 1] * i + j]); } fprintf(f, "\n"); } @@ -2209,14 +2272,15 @@ void GNAPlugin::Wait(uint32_t idx) { #endif ConvertToFloat(output.buffer(), output.buffer(), - output.dims()[0], - output.dims()[1], + output.getTensorDesc().getDims()[output.getTensorDesc().getDims().size() - 1], + output.getTensorDesc().getDims()[output.getTensorDesc().getDims().size() - 2], output_scale_factor); #ifdef PLOT if (f) { - for (int i = 0; i < output.dims()[1]; i++) { - for (int j = 0; j < output.dims()[0]; j++) { - fprintf(f, "%.2f ", output.cbuffer().as()[output.dims()[0] * i + j]); + auto dims = output.getTensorDesc().getDims(); + for (int i = 0; i < dims[dims.size() - 2]; i++) { + for (int j = 0; j < dims[dims.size() - 1]; j++) { + fprintf(f, "%.2f ", output.cbuffer().as()[dims[dims.size() - 1] * i + j]); } fprintf(f, "\n"); } @@ -2257,7 +2321,8 @@ void GNAPlugin::Infer(const InferenceEngine::BlobMap &input, InferenceEngine::Bl Blob::Ptr GNAPlugin::GetOutputBlob(InferenceEngine::Precision precision) { // need to have intermediate blob for interleave conversion InferenceEngine::Blob::Ptr outputBlob; - outputBlob = make_blob_with_precision(precision, NC, outputDims); + auto outputDims = outputsDataMap.begin()->second->getTensorDesc().getDims(); + outputBlob = make_blob_with_precision(TensorDesc(precision, outputDims, outputDims.size() == 2 ? NC : NCHW)); outputBlob->allocate(); return outputBlob; } @@ -2266,8 +2331,8 @@ Blob::Ptr GNAPlugin::GetInputBlob(std::string name, InferenceEngine::Precision p InferenceEngine::Blob::Ptr inputBlob; // need to have intermediate blob for interleave conversion // TODO: NCHW format support is experimental = c++ MO did insert reshape, while TF mo - not - auto inputDims = inputsDataMap[name]->getDims(); - inputBlob = make_blob_with_precision(precision, inputDims.size() == 2 ? NC : NCHW, inputDims); + auto inputDims = inputsDataMap[name]->getTensorDesc().getDims(); + inputBlob = make_blob_with_precision(TensorDesc(precision, inputDims, inputDims.size() == 2 ? NC : NCHW)); inputBlob->allocate(); return inputBlob; } @@ -2326,19 +2391,20 @@ InferenceEngine::IExecutableNetwork::Ptr GNAPlugin::ImportNetwork(const std::str num_bytes_per_output = header.output.element_size; - - outputDims = SizeVector({header.output.elements_count / header.nGroup, header.nGroup}); - auto inputDims = SizeVector({header.input.elements_count / header.nGroup, header.nGroup}); + auto outputDims = SizeVector({header.nGroup, header.output.elements_count / header.nGroup}); + auto inputDims = SizeVector({header.nGroup, header.input.elements_count / header.nGroup}); inputsDataMap["input"] = std::make_shared(); inputsDataMap["input"]->setInputData(make_shared("input", - inputDims, - Precision::FP32, - Layout::NC)); + TensorDesc( + Precision::FP32, + inputDims, + Layout::NC))); outputsDataMap["output"] = make_shared("output", - outputDims, - Precision::FP32, - Layout::NC); + TensorDesc( + Precision::FP32, + outputDims, + Layout::NC)); output_scale_factor = header.output.scaleFactor; inputScaleFactors.push_back(header.input.scaleFactor); @@ -2375,20 +2441,20 @@ void GNAPlugin::Export(const std::string &fileName) { std::fstream outStream(fileName, ios_base::out | ios_base::binary); // TODO: nnet group parameter looks only used in application - so can we move this line into load network. - auto inputDims = inputsDataMap.begin()->second->getDims(); + auto inputDims = inputsDataMap.begin()->second->getTensorDesc().getDims(); if (inputDims.size() == 2) { - std::get<0>(nnets.front())->obj.nGroup = inputDims[1]; + std::get<0>(nnets.front())->obj.nGroup = inputDims[0]; } auto serial = GNAModelSerial(&std::get<0>(nnets.front())->obj, {inputScaleFactors.front(), ptr_inputs_global_storage.front()[0], 2, - static_cast(InferenceEngine::details::product(inputsDataMap.begin()->second->getDims()))}, + static_cast(InferenceEngine::details::product(inputsDataMap.begin()->second->getTensorDesc().getDims()))}, {output_scale_factor, ptr_outputs_global[0], num_bytes_per_output, - static_cast(InferenceEngine::details::product(outputsDataMap.begin()->second->getDims()))}) + static_cast(InferenceEngine::details::product(outputsDataMap.begin()->second->getTensorDesc().getDims()))}) .SetInputRotation(dnn.num_rotate_rows, dnn.num_rotate_columns); for (auto && memoryConnection : memory_connection) { @@ -2460,12 +2526,16 @@ void GNAPlugin::SetConfig(const std::map &config) { if (inputScaleFactors.size() <= scaleForInput) { inputScaleFactors.resize(scaleForInput + 1, 1.f); } - inputScaleFactors[scaleForInput] = std::stod(value); + inputScaleFactors[scaleForInput] = InferenceEngine::CNNLayer::ie_parse_float(value); }); if (inputScaleFactors.empty()) { if_set(GNA_CONFIG_KEY(SCALE_FACTOR), [&] { - inputScaleFactors.push_back(std::stod(value)); + auto scaleFactor = InferenceEngine::CNNLayer::ie_parse_float(value); + if (fp32eq(scaleFactor, 0.0f)) { + THROW_GNA_EXCEPTION << "input scale factor of 0.0f not supported"; + } + inputScaleFactors.push_back(scaleFactor); }); } @@ -2573,6 +2643,10 @@ void GNAPlugin::SetConfig(const std::map &config) { THROW_GNA_EXCEPTION << "EXCLUSIVE_ASYNC_REQUESTS should be YES/NO, but not" << value; } }); + + if (sw_fp32 && gna_lib_async_threads_num > 1) { + THROW_GNA_EXCEPTION << "GNA plugin not support async mode on GNA_SW_FP32!"; + } } /** @@ -2616,7 +2690,7 @@ void GNAPlugin::QueryNetwork(const InferenceEngine::ICNNNetwork& network, intel_dnn_component_t * GNAPlugin::find_first_unused_input(InferenceEngine::CNNLayerPtr current) { if (current->insData.empty()) return nullptr; - auto prev_layer = current->insData.front().lock()->creatorLayer.lock(); + auto prev_layer = current->insData.front().lock()->getCreatorLayer().lock(); return findDnnLayer(prev_layer); } diff --git a/inference-engine/src/gna_plugin/gna_plugin.hpp b/inference-engine/src/gna_plugin/gna_plugin.hpp index f0d9fc38b818f8..e4ae5cc407cfaa 100644 --- a/inference-engine/src/gna_plugin/gna_plugin.hpp +++ b/inference-engine/src/gna_plugin/gna_plugin.hpp @@ -475,19 +475,6 @@ class GNAPlugin : public InferenceEngine::IInferencePluginInternal, public std:: uint32_t num_bytes_per_element_input, uint32_t num_bytes_per_element); - friend void GNAPluginNS::ConvertToInt16(int16_t *ptr_dst, - const float *ptr_src, - const uint32_t num_rows, - const uint32_t num_columns, - const float scale_factor); - friend void GNAPluginNS::ConvertToFloat(float *ptr_dst, - int32_t *ptr_src, - const uint32_t num_rows, - const uint32_t num_columns, - const float scale_factor); - - friend int16_t GNAPluginNS::ConvertFloatToInt16(float src); - template void copyInputData(T *dst, const U *src, @@ -509,8 +496,6 @@ class GNAPlugin : public InferenceEngine::IInferencePluginInternal, public std:: intel_dnn_component_t * find_first_unused_input(InferenceEngine::CNNLayerPtr current); std::map bytes_alllocated_for_input; InferenceEngine::InputsDataMap inputsDataMap; - - InferenceEngine::SizeVector outputDims; InferenceEngine::OutputsDataMap outputsDataMap; }; } // namespace GNAPluginNS diff --git a/inference-engine/src/gna_plugin/gna_plugin_config.hpp b/inference-engine/src/gna_plugin/gna_plugin_config.hpp index efe3490084ecb8..528e789b27d487 100644 --- a/inference-engine/src/gna_plugin/gna_plugin_config.hpp +++ b/inference-engine/src/gna_plugin/gna_plugin_config.hpp @@ -46,6 +46,7 @@ class Config { } inline Endpoint find_configuration(InferenceEngine::ICNNNetwork &network) { + IE_SUPPRESS_DEPRECATED_START auto device = network.getTargetDevice(); auto targetDevice = device == InferenceEngine::TargetDevice::eDefault ? _defaultDevice : device; auto res = std::find_if(std::begin(supported), std::end(supported), [&](Endpoint &e) { @@ -62,7 +63,7 @@ class Config { << InferenceEngine::TargetDeviceInfo::name(network.getTargetDevice()) << ".\nSupported target device: " << InferenceEngine::TargetDeviceInfo::name(InferenceEngine::TargetDevice::eGNA); } - + IE_SUPPRESS_DEPRECATED_END return *res; } }; diff --git a/inference-engine/src/gna_plugin/gna_plugin_entry_points.cpp b/inference-engine/src/gna_plugin/gna_plugin_entry_points.cpp index 6ea717ebd5030c..82d2d8cdd3889e 100644 --- a/inference-engine/src/gna_plugin/gna_plugin_entry_points.cpp +++ b/inference-engine/src/gna_plugin/gna_plugin_entry_points.cpp @@ -13,7 +13,7 @@ using namespace GNAPluginNS; INFERENCE_PLUGIN_API(StatusCode) CreatePluginEngine(IInferencePlugin *&plugin, ResponseDesc *resp) noexcept { try { - plugin = make_ie_compatible_plugin({2, 0, "GNAPlugin", "GNAPlugin"}, make_shared()); + plugin = make_ie_compatible_plugin({2, 1, "GNAPlugin", "GNAPlugin"}, make_shared()); return OK; } catch (std::exception &ex) { diff --git a/inference-engine/src/gna_plugin/gna_plugin_internal.hpp b/inference-engine/src/gna_plugin/gna_plugin_internal.hpp index 130a094ea35256..dab1e156d88116 100644 --- a/inference-engine/src/gna_plugin/gna_plugin_internal.hpp +++ b/inference-engine/src/gna_plugin/gna_plugin_internal.hpp @@ -35,6 +35,10 @@ class GNAPluginInternal : public InferenceEngine::InferencePluginInternal { return plg->GetName(); } + InferenceEngine::ICNNNetwork& RemoveConstLayers(InferenceEngine::ICNNNetwork &network) override { + return network; + } + /** * @deprecated Use the version with config parameter */ diff --git a/inference-engine/src/gna_plugin/gna_plugin_log.hpp b/inference-engine/src/gna_plugin/gna_plugin_log.hpp index 416b590dfc37ed..b68be9e8285189 100644 --- a/inference-engine/src/gna_plugin/gna_plugin_log.hpp +++ b/inference-engine/src/gna_plugin/gna_plugin_log.hpp @@ -42,7 +42,7 @@ inline GnaLog & gnawarn() { #ifdef __PRETTY_FUNCTION__ #undef __PRETTY_FUNCTION__ #endif -#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +#ifdef _WIN32 # define __PRETTY_FUNCTION__ __FUNCSIG__ #else # define __PRETTY_FUNCTION__ __FUNCTION__ diff --git a/inference-engine/src/gna_plugin/quantization/layer_quantizer.hpp b/inference-engine/src/gna_plugin/quantization/layer_quantizer.hpp index 3f3fcf331f7427..8637545f637bc8 100644 --- a/inference-engine/src/gna_plugin/quantization/layer_quantizer.hpp +++ b/inference-engine/src/gna_plugin/quantization/layer_quantizer.hpp @@ -376,7 +376,9 @@ class DataQuantizer : public DataQuantizerBas } } else { if (LayerInfo(*cnnLayer).isActivation() || - LayerInfo(*cnnLayer).isCopy()) { + LayerInfo(*cnnLayer).isCopy() || + LayerInfo(*cnnLayer).isReshape() || + LayerInfo(*cnnLayer).isPermute()) { // precision of activation layers is always equal input precision for (auto &&outData : cnnLayer->outData) { outData->setPrecision(Desc::mandatory().getInputPrecision()); diff --git a/inference-engine/src/gna_plugin/quantization/quantization.h b/inference-engine/src/gna_plugin/quantization/quantization.h index 8e704fdec5aeb0..6f46c318cb1266 100644 --- a/inference-engine/src/gna_plugin/quantization/quantization.h +++ b/inference-engine/src/gna_plugin/quantization/quantization.h @@ -14,6 +14,7 @@ #define MAX_VAL_1B_WEIGHT 127 #define MAX_VAL_2B_WEIGHT 16384 #define MAX_VAL_2B_FEAT 16384 +#define MAX_VAL_4B_BIAS 1073741824 #ifdef DEBUG #define QUANTWARNING(...) (fprintf(stderr, __VA_ARGS__)) #else diff --git a/inference-engine/src/gna_plugin/quantization/scale_factor_calc.hpp b/inference-engine/src/gna_plugin/quantization/scale_factor_calc.hpp index b6d3b0312919ba..aa8e45819ec645 100644 --- a/inference-engine/src/gna_plugin/quantization/scale_factor_calc.hpp +++ b/inference-engine/src/gna_plugin/quantization/scale_factor_calc.hpp @@ -287,7 +287,19 @@ class ScaleFactorPerLayer { } if (!sourceQuantParams) { - THROW_GNA_EXCEPTION << "Concat quantization for this case need to be implemented!!! \n"; + auto in0LayerInfo = LayerInfo(in0); + auto in1LayerInfo = LayerInfo(in1); + if (in0LayerInfo.isActivation()) { + quantParams0->_weights_quant = quantParams1->_dst_quant; + quantParams0->_dst_quant = quantParams1->_dst_quant; + sourceQuantParams = quantParams1; + } else if (in1LayerInfo.isActivation()) { + quantParams1->_weights_quant = quantParams0->_dst_quant; + quantParams1->_dst_quant = quantParams0->_dst_quant; + sourceQuantParams = quantParams0; + } else { + THROW_GNA_EXCEPTION << "Concat quantization for this case need to be implemented!!! \n"; + } } if (!fp32eq(quantParams0->_dst_quant.scale, quantParams1->_dst_quant.scale) && concatIdxToUpdate == -1) { @@ -368,6 +380,7 @@ class ScaleFactorPerLayer { InferenceEngine::getInjectedData(*InferenceEngine::CNNNetPrevLayer(wl).get()); auto quant = InferenceEngine::getInjectedData(*wl); + quant->_src_quant.scale = quantDataForInputLayer->_dst_quant.scale; // TODO: pass 8 bits somehow if (quant->_weights_quant.scale == 1.0f) { size_t scaleRange = 0; @@ -381,6 +394,14 @@ class ScaleFactorPerLayer { quant->_weights_quant.scale = ScaleFactorForQuantization(wl->_weights->buffer().as(), scaleRange, wl->_weights->size()); + if (wl->_biases) { + quant->_bias_quant.scale = ScaleFactorForQuantization(wl->_biases->buffer().as(), + MAX_VAL_4B_BIAS, + wl->_biases->size()); + quant->_bias_quant.scale = std::min(quant->_weights_quant.scale * quant->_src_quant.scale, quant->_bias_quant.scale); + quant->_weights_quant.scale = quant->_bias_quant.scale / quant->_src_quant.scale; + } + // TODO: findout why ??? if (weightsSize == 1) { quant->_weights_quant.scale *= MAX_OUT_MULTIPLIER; @@ -398,8 +419,6 @@ class ScaleFactorPerLayer { } - quant->_src_quant.scale = quantDataForInputLayer->_dst_quant.scale; - double tmp_dst_quant_scale = quant->_weights_quant.scale * quantDataForInputLayer->_dst_quant.scale; if (weightsSize == 1 && diff --git a/inference-engine/src/hetero_plugin/CMakeLists.txt b/inference-engine/src/hetero_plugin/CMakeLists.txt index 91eb1890f815e4..fdb1b0fda9b640 100644 --- a/inference-engine/src/hetero_plugin/CMakeLists.txt +++ b/inference-engine/src/hetero_plugin/CMakeLists.txt @@ -8,16 +8,16 @@ file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) -add_library(${TARGET_NAME} SHARED ${SOURCES}) +file(GLOB_RECURSE HEADERS + ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp) + +ie_add_plugin(NAME ${TARGET_NAME} + DEVICE_NAME "HETERO" + SOURCES ${SOURCES} ${HEADERS} + VERSION_DEFINES_FOR hetero_plugin.cpp) target_include_directories(${TARGET_NAME} PRIVATE "${IE_MAIN_SOURCE_DIR}/src/inference_engine" ) -target_compile_definitions(${TARGET_NAME} PRIVATE IMPLEMENT_INFERENCE_ENGINE_PLUGIN) - -target_link_libraries(${TARGET_NAME} PRIVATE inference_engine) - -set_target_properties(${TARGET_NAME} PROPERTIES COMPILE_PDB_NAME ${TARGET_NAME}) - -add_cpplint_target(${TARGET_NAME}_cpplint FOR_TARGETS ${TARGET_NAME}) +target_link_libraries(${TARGET_NAME} PRIVATE inference_engine ade) diff --git a/inference-engine/src/inference_engine/ade_util.cpp b/inference-engine/src/hetero_plugin/hetero_ade_util.cpp similarity index 98% rename from inference-engine/src/inference_engine/ade_util.cpp rename to inference-engine/src/hetero_plugin/hetero_ade_util.cpp index 0bd1e7e2d2c1e6..3408a4c73f6f9d 100644 --- a/inference-engine/src/inference_engine/ade_util.cpp +++ b/inference-engine/src/hetero_plugin/hetero_ade_util.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "ade_util.hpp" +#include "hetero_ade_util.hpp" #include #include diff --git a/inference-engine/src/inference_engine/ade_util.hpp b/inference-engine/src/hetero_plugin/hetero_ade_util.hpp similarity index 100% rename from inference-engine/src/inference_engine/ade_util.hpp rename to inference-engine/src/hetero_plugin/hetero_ade_util.hpp diff --git a/inference-engine/src/inference_engine/hetero/hetero_async_infer_request.cpp b/inference-engine/src/hetero_plugin/hetero_async_infer_request.cpp similarity index 99% rename from inference-engine/src/inference_engine/hetero/hetero_async_infer_request.cpp rename to inference-engine/src/hetero_plugin/hetero_async_infer_request.cpp index a282b013398944..b4704e773c9767 100644 --- a/inference-engine/src/inference_engine/hetero/hetero_async_infer_request.cpp +++ b/inference-engine/src/hetero_plugin/hetero_async_infer_request.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // -#include #include "hetero_async_infer_request.hpp" #include #include diff --git a/inference-engine/src/inference_engine/hetero/hetero_async_infer_request.hpp b/inference-engine/src/hetero_plugin/hetero_async_infer_request.hpp similarity index 100% rename from inference-engine/src/inference_engine/hetero/hetero_async_infer_request.hpp rename to inference-engine/src/hetero_plugin/hetero_async_infer_request.hpp diff --git a/inference-engine/src/inference_engine/hetero/hetero_device_loader.cpp b/inference-engine/src/hetero_plugin/hetero_device_loader.cpp similarity index 97% rename from inference-engine/src/inference_engine/hetero/hetero_device_loader.cpp rename to inference-engine/src/hetero_plugin/hetero_device_loader.cpp index 5057ce0e7bf7ca..7dcdbb438562cd 100644 --- a/inference-engine/src/inference_engine/hetero/hetero_device_loader.cpp +++ b/inference-engine/src/hetero_plugin/hetero_device_loader.cpp @@ -14,13 +14,6 @@ using namespace InferenceEngine; -IE_SUPPRESS_DEPRECATED_START - -IHeteroDeviceLoader::~IHeteroDeviceLoader() { -} - -IE_SUPPRESS_DEPRECATED_START - StatusCode HeteroDeviceLoader::LoadNetwork( const std::string &device, IExecutableNetwork::Ptr &ret, diff --git a/inference-engine/src/inference_engine/hetero/hetero_device_loader.hpp b/inference-engine/src/hetero_plugin/hetero_device_loader.hpp similarity index 100% rename from inference-engine/src/inference_engine/hetero/hetero_device_loader.hpp rename to inference-engine/src/hetero_plugin/hetero_device_loader.hpp diff --git a/inference-engine/src/inference_engine/hetero/hetero_executable_network.cpp b/inference-engine/src/hetero_plugin/hetero_executable_network.cpp similarity index 94% rename from inference-engine/src/inference_engine/hetero/hetero_executable_network.cpp rename to inference-engine/src/hetero_plugin/hetero_executable_network.cpp index 475cc0b2971ef9..c92256047dd997 100644 --- a/inference-engine/src/inference_engine/hetero/hetero_executable_network.cpp +++ b/inference-engine/src/hetero_plugin/hetero_executable_network.cpp @@ -7,6 +7,8 @@ #include "hetero_async_infer_request.hpp" #include "ie_util_internal.hpp" #include "hetero_device_loader.hpp" +#include "hetero_fallback_policy.hpp" +#include "hetero_graph_splitter.hpp" #include #include @@ -19,10 +21,9 @@ #include #include -#include -#include "fallback_policy.hpp" #include "details/caseless.hpp" #include "ie_plugin_config.hpp" +#include "cpp_interfaces/interface/ie_internal_plugin_config.hpp" #include "cpp_interfaces/base/ie_inference_plugin_api.hpp" #include "cpp_interfaces/impl/ie_plugin_internal.hpp" #include "hetero/hetero_plugin_config.hpp" @@ -298,8 +299,22 @@ void HeteroExecutableNetwork::load(InferenceEngine::ICNNNetwork &network_, for (auto &&d : descs) { IExecutableNetwork::Ptr ret; ResponseDesc resp; + + InputsDataMap subnetworkInputs; + d._clonedNetwork->getInputsInfo(subnetworkInputs); + bool isInputSubnetwork = (subnetworkInputs.end() != std::find_first_of( + subnetworkInputs.begin(), subnetworkInputs.end(), + externalInputsData.begin(), externalInputsData.end(), + [] (const InputsDataMap::value_type& lhs, const InputsDataMap::value_type& rhs) { + return lhs.first == rhs.first; + })); + + auto cfg = config; + cfg[IE_INTERNAL_CONFIG_KEY(SUBNETWORK_WITH_NETWORK_INPUTS)] = isInputSubnetwork + ? CONFIG_VALUE(YES) + : CONFIG_VALUE(NO); IE_SUPPRESS_DEPRECATED_START - StatusCode status = d._deviceLoader->LoadNetwork(d._device, ret, *d._clonedNetwork, config, &resp); + StatusCode status = d._deviceLoader->LoadNetwork(d._device, ret, *d._clonedNetwork, cfg, &resp); IE_SUPPRESS_DEPRECATED_END if (status != OK) { THROW_IE_EXCEPTION << resp.msg; diff --git a/inference-engine/src/inference_engine/hetero/hetero_executable_network.hpp b/inference-engine/src/hetero_plugin/hetero_executable_network.hpp similarity index 100% rename from inference-engine/src/inference_engine/hetero/hetero_executable_network.hpp rename to inference-engine/src/hetero_plugin/hetero_executable_network.hpp diff --git a/inference-engine/src/inference_engine/hetero/fallback_policy.cpp b/inference-engine/src/hetero_plugin/hetero_fallback_policy.cpp similarity index 84% rename from inference-engine/src/inference_engine/hetero/fallback_policy.cpp rename to inference-engine/src/hetero_plugin/hetero_fallback_policy.cpp index a402b887db9bdc..682212bbd36a40 100644 --- a/inference-engine/src/inference_engine/hetero/fallback_policy.cpp +++ b/inference-engine/src/hetero_plugin/hetero_fallback_policy.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "fallback_policy.hpp" +#include "hetero_fallback_policy.hpp" #include "hetero_device_loader.hpp" #include "details/ie_cnn_network_iterator.hpp" #include "ie_layers.h" @@ -16,41 +16,6 @@ using namespace InferenceEngine; -IE_SUPPRESS_DEPRECATED_START - -QueryNetworkResult::QueryNetworkResult() : rc(OK) { -} - -const QueryNetworkResult & QueryNetworkResult::operator= (const QueryNetworkResult & q) { - supportedLayers = q.supportedLayers; - supportedLayersMap = q.supportedLayersMap; - rc = q.rc; - resp = q.resp; - - return *this; -} - -QueryNetworkResult & QueryNetworkResult::operator= (QueryNetworkResult && q) { - supportedLayers = q.supportedLayers; - supportedLayersMap = q.supportedLayersMap; - rc = q.rc; - resp = q.resp; - - return *this; -} - -QueryNetworkResult::QueryNetworkResult(const QueryNetworkResult & instance) : - supportedLayers(instance.supportedLayers), - supportedLayersMap(instance.supportedLayersMap), - rc(instance.rc), - resp(instance.resp) { -} - -QueryNetworkResult::~QueryNetworkResult() { -} - -IE_SUPPRESS_DEPRECATED_END - void dla_layer_colorer(const CNNLayerPtr layer, ordered_properties &printed_properties, ordered_properties &node_properties) { diff --git a/inference-engine/src/inference_engine/hetero/fallback_policy.hpp b/inference-engine/src/hetero_plugin/hetero_fallback_policy.hpp similarity index 100% rename from inference-engine/src/inference_engine/hetero/fallback_policy.hpp rename to inference-engine/src/hetero_plugin/hetero_fallback_policy.hpp diff --git a/inference-engine/src/inference_engine/ie_graph_splitter.cpp b/inference-engine/src/hetero_plugin/hetero_graph_splitter.cpp similarity index 99% rename from inference-engine/src/inference_engine/ie_graph_splitter.cpp rename to inference-engine/src/hetero_plugin/hetero_graph_splitter.cpp index 57981738ff86e8..11ffa390b3fdf0 100644 --- a/inference-engine/src/inference_engine/ie_graph_splitter.cpp +++ b/inference-engine/src/hetero_plugin/hetero_graph_splitter.cpp @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "ie_graph_splitter.hpp" +#include "hetero_graph_splitter.hpp" +#include "hetero_ade_util.hpp" #include #include @@ -11,8 +12,6 @@ #include #include -#include - #include #include diff --git a/inference-engine/src/inference_engine/ie_graph_splitter.hpp b/inference-engine/src/hetero_plugin/hetero_graph_splitter.hpp similarity index 91% rename from inference-engine/src/inference_engine/ie_graph_splitter.hpp rename to inference-engine/src/hetero_plugin/hetero_graph_splitter.hpp index 3252632da739ba..4a6fdac969b0a9 100644 --- a/inference-engine/src/inference_engine/ie_graph_splitter.hpp +++ b/inference-engine/src/hetero_plugin/hetero_graph_splitter.hpp @@ -24,7 +24,7 @@ using LayersSet = std::unordered_set; /// @param checkers - list of supported plugins /// /// @return list of subgraphs -INFERENCE_ENGINE_API_CPP(std::vector) +std::vector splitGraph(ICNNNetwork& network, const std::vector& plugins); @@ -32,7 +32,7 @@ splitGraph(ICNNNetwork& network, /// refences between subgraps /// /// @param subgraphs - list of subgraphs -INFERENCE_ENGINE_API_CPP(void) +void sortSubgraphs(std::vector& subgraphs); } // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/hetero/hetero_infer_request.cpp b/inference-engine/src/hetero_plugin/hetero_infer_request.cpp similarity index 100% rename from inference-engine/src/inference_engine/hetero/hetero_infer_request.cpp rename to inference-engine/src/hetero_plugin/hetero_infer_request.cpp diff --git a/inference-engine/src/inference_engine/hetero/hetero_infer_request.hpp b/inference-engine/src/hetero_plugin/hetero_infer_request.hpp similarity index 100% rename from inference-engine/src/inference_engine/hetero/hetero_infer_request.hpp rename to inference-engine/src/hetero_plugin/hetero_infer_request.hpp diff --git a/inference-engine/src/hetero_plugin/hetero_plugin.cpp b/inference-engine/src/hetero_plugin/hetero_plugin.cpp index e76f71778e030c..ecd29622115972 100644 --- a/inference-engine/src/hetero_plugin/hetero_plugin.cpp +++ b/inference-engine/src/hetero_plugin/hetero_plugin.cpp @@ -2,7 +2,155 @@ // SPDX-License-Identifier: Apache-2.0 // -#include +#include "ie_metric_helpers.hpp" +#include "hetero_plugin.hpp" +#include +#include +#include +#include +#include "ie_plugin_config.hpp" +#include "hetero/hetero_plugin_config.hpp" +#include +#include "hetero_plugin_base.hpp" +#include "hetero_executable_network.hpp" +#include "hetero_fallback_policy.hpp" + +using namespace InferenceEngine; +using namespace InferenceEngine::PluginConfigParams; +using namespace InferenceEngine::HeteroConfigParams; +using namespace HeteroPlugin; +using namespace std; + +static Version heteroPluginDescription = { + {2, 1}, // plugin API version + CI_BUILD_NUMBER, + "heteroPlugin" // plugin description message +}; + +void Engine::GetVersion(const Version *&versionInfo)noexcept { + versionInfo = &heteroPluginDescription; +} + +Engine::Engine() { + _pluginName = "HETERO"; + _config[InferenceEngine::PluginConfigParams::KEY_EXCLUSIVE_ASYNC_REQUESTS] = "YES"; + _config[KEY_HETERO_DUMP_GRAPH_DOT] = NO; +} + +InferenceEngine::ExecutableNetworkInternal::Ptr Engine::LoadExeNetworkImpl(const ICore * core, InferenceEngine::ICNNNetwork &network, + const std::map &config) { + // TODO(amalyshe) do we need here verification of input precisions? + std::map tconfig; + tconfig = config; + + // we must not override the parameter, but need to copy everything from plugin config + for (auto && c : _config) { + if (tconfig.find(c.first) == tconfig.end()) { + tconfig[c.first] = c.second; + } + } + + return std::make_shared(network, core, tconfig, _extensions, _deviceLoaders, error_listener); +} + +void Engine::SetConfig(const std::map &config) { + if (_config.find("TARGET_FALLBACK") == _config.end()) { + _config["TARGET_FALLBACK"] = ""; + } + + for (auto &&i : config) { + _config[i.first] = i.second; + } +} + +IE_SUPPRESS_DEPRECATED_START +void Engine::SetDeviceLoader(const std::string &device, + IHeteroDeviceLoader::Ptr pLoader) { + _deviceLoaders[device] = pLoader; +} +IE_SUPPRESS_DEPRECATED_END + +void Engine::AddExtension(InferenceEngine::IExtensionPtr extension) { + _extensions.push_back(extension); +} + +void Engine::SetAffinity(InferenceEngine::ICNNNetwork &network, + const std::map &config) { + FallbackPolicy fbPolicy(_deviceLoaders, _config[KEY_HETERO_DUMP_GRAPH_DOT] == YES, GetCore()); + fbPolicy.init(_config["TARGET_FALLBACK"], config, _extensions); + fbPolicy.setAffinity(fbPolicy.getAffinities(config, network), network); +} + +void Engine::SetLogCallback(IErrorListener &listener) { + error_listener = &listener; + + IE_SUPPRESS_DEPRECATED_START + for (auto& device_loader : _deviceLoaders) + device_loader.second->SetLogCallback(*error_listener); + IE_SUPPRESS_DEPRECATED_END +} + +void Engine::QueryNetwork(const ICNNNetwork &network, const std::map& config, QueryNetworkResult &res) const { + auto _deviceLoaders_ = _deviceLoaders; + + auto it = _config.find(KEY_HETERO_DUMP_GRAPH_DOT); + IE_ASSERT(it != _config.end()); + FallbackPolicy fbPolicy(_deviceLoaders_, it->second == YES, GetCore()); + it = config.find("TARGET_FALLBACK"); + if (it == config.end()) { + it = _config.find("TARGET_FALLBACK"); + + if (it == _config.end()) { + THROW_IE_EXCEPTION << "The 'TARGET_FALLBACK' option was not defined for heterogeneous plugin"; + } + } + fbPolicy.init(it->second, config, _extensions); + res = fbPolicy.getAffinities(config, network); +} + +Parameter Engine::GetMetric(const std::string& name, const std::map & options) const { + if (METRIC_KEY(SUPPORTED_METRICS) == name) { + IE_SET_METRIC_RETURN(SUPPORTED_METRICS, std::vector{ + METRIC_KEY(SUPPORTED_METRICS), + METRIC_KEY(SUPPORTED_CONFIG_KEYS)}); + } else if (METRIC_KEY(SUPPORTED_CONFIG_KEYS) == name) { + IE_SET_METRIC_RETURN(SUPPORTED_CONFIG_KEYS, std::vector{ + HETERO_CONFIG_KEY(DUMP_GRAPH_DOT), + "TARGET_FALLBACK", + CONFIG_KEY(EXCLUSIVE_ASYNC_REQUESTS)}); + } else { + THROW_IE_EXCEPTION << "Unsupported Plugin metric: " << name; + } +} + +Parameter Engine::GetConfig(const std::string& name, const std::map & options) const { + if (name == HETERO_CONFIG_KEY(DUMP_GRAPH_DOT)) { + auto it = _config.find(KEY_HETERO_DUMP_GRAPH_DOT); + IE_ASSERT(it != _config.end()); + bool dump = it->second == YES; + return { dump }; + } else { + THROW_IE_EXCEPTION << "Unsupported config key: " << name; + } +} + +namespace HeteroPlugin { + +InferenceEngine::StatusCode CreateHeteroPluginEngine( + InferenceEngine::IInferencePlugin *&plugin, + InferenceEngine::ResponseDesc *resp) noexcept { + try { + plugin = new HeteroPluginBase( + {{2, 1}, "heteroPlugin", "heteroPlugin"}, + std::make_shared()); + return OK; + } + catch (std::exception &ex) { + return DescriptionBuffer(GENERAL_ERROR, resp) << ex.what(); + } +} + +} // namespace HeteroPlugin INFERENCE_PLUGIN_API(InferenceEngine::StatusCode) CreatePluginEngine( InferenceEngine::IInferencePlugin *&plugin, diff --git a/inference-engine/src/inference_engine/hetero/hetero_plugin.hpp b/inference-engine/src/hetero_plugin/hetero_plugin.hpp similarity index 100% rename from inference-engine/src/inference_engine/hetero/hetero_plugin.hpp rename to inference-engine/src/hetero_plugin/hetero_plugin.hpp diff --git a/inference-engine/src/inference_engine/hetero/hetero_plugin_base.hpp b/inference-engine/src/hetero_plugin/hetero_plugin_base.hpp similarity index 100% rename from inference-engine/src/inference_engine/hetero/hetero_plugin_base.hpp rename to inference-engine/src/hetero_plugin/hetero_plugin_base.hpp diff --git a/inference-engine/src/inference_engine/CMakeLists.txt b/inference-engine/src/inference_engine/CMakeLists.txt index f3514ed9afb91c..f4b34addcdda0b 100644 --- a/inference-engine/src/inference_engine/CMakeLists.txt +++ b/inference-engine/src/inference_engine/CMakeLists.txt @@ -6,6 +6,10 @@ set (TARGET_NAME "inference_engine") if (WIN32) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNOMINMAX") +elseif(ENABLE_LTO) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto") + set(CMAKE_AR "gcc-ar") + set(CMAKE_RANLIB "gcc-ranlib") endif() file (GLOB LIBRARY_SRC @@ -18,7 +22,6 @@ file (GLOB LIBRARY_SRC ${CMAKE_CURRENT_SOURCE_DIR}/shape_infer/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/shape_infer/built-in/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/shape_infer/const_infer/*.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/hetero/*.cpp ) file (GLOB LIBRARY_HEADERS @@ -31,7 +34,6 @@ file (GLOB LIBRARY_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/cpp_interfaces/base/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/cpp_interfaces/impl/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/cpp_interfaces/interface/*.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/hetero/*.hpp ) if( (NOT DEFINED ENABLE_SSE42) OR ENABLE_SSE42) @@ -43,21 +45,39 @@ if( (NOT DEFINED ENABLE_SSE42) OR ENABLE_SSE42) ${LIBRARY_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/cpu_x86_sse42/*.hpp ) + + file (GLOB SSE_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/cpu_x86_sse42/*.cpp + ) + file (GLOB SSE_HEADERS + ${CMAKE_CURRENT_SOURCE_DIR}/cpu_x86_sse42/*.hpp + ) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/cpu_x86_sse42) + if (WIN32) - set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/cpu_x86_sse42/blob_transform_sse42.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/cpu_x86_sse42/ie_preprocess_data_sse42.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/cpu_x86_sse42/ie_preprocess_gapi_kernels_sse42.cpp" PROPERTIES COMPILE_FLAGS /arch:SSE2) + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL MSVC) + set_source_files_properties(${SSE_SRC} + PROPERTIES COMPILE_FLAGS /arch:SSE4.2 + ) + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL Intel) + set_source_files_properties(${SSE_SRC} + PROPERTIES COMPILE_FLAGS /arch:SSE4.2 /QxSSE4.2 /Qvc14 + ) + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL Clang) + set_source_files_properties(${SSE_SRC} + PROPERTIES COMPILE_FLAGS -msse4.2 + ) + endif() else() - set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/cpu_x86_sse42/blob_transform_sse42.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/cpu_x86_sse42/ie_preprocess_data_sse42.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/cpu_x86_sse42/ie_preprocess_gapi_kernels_sse42.cpp" PROPERTIES COMPILE_FLAGS -msse4.2) + set_source_files_properties(${SSE_SRC} + PROPERTIES COMPILE_FLAGS -msse4.2 + ) endif() add_definitions(-DHAVE_SSE=1) endif() addVersionDefines(ie_version.cpp CI_BUILD_NUMBER) -addVersionDefines(hetero/hetero_plugin.cpp CI_BUILD_NUMBER) set (PUBLIC_HEADERS_DIR "${IE_MAIN_SOURCE_DIR}/include") @@ -82,7 +102,7 @@ add_library(${TARGET_NAME} SHARED ${PUBLIC_HEADERS}) set_ie_threading_interface_for(${TARGET_NAME}) -target_link_libraries(${TARGET_NAME} PRIVATE fluid ngraph ade ${INTEL_ITT_LIBS} pugixml PUBLIC ${CMAKE_DL_LIBS}) +target_link_libraries(${TARGET_NAME} PRIVATE fluid ngraph ${INTEL_ITT_LIBS} pugixml PUBLIC ${CMAKE_DL_LIBS}) if(WIN32) #To disable min/max macro in windows.h @@ -91,8 +111,7 @@ endif() # Properties->C/C++->General->Additional Include Directories target_include_directories(${TARGET_NAME} PUBLIC ${PUBLIC_HEADERS_DIR} - PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" - "${IE_MAIN_SOURCE_DIR}/src/dumper") + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") target_include_directories(${TARGET_NAME} SYSTEM PRIVATE "${IE_MAIN_SOURCE_DIR}/thirdparty/pugixml/src") target_include_directories(${TARGET_NAME} SYSTEM PRIVATE "${IE_MAIN_SOURCE_DIR}/thirdparty/ngraph/src") @@ -102,6 +121,10 @@ if(ENABLE_MKL_DNN) target_include_directories(${TARGET_NAME} SYSTEM PRIVATE "${IE_MAIN_SOURCE_DIR}/thirdparty/mkl-dnn/src/cpu/xbyak") endif() +if(ENABLE_UNICODE_PATH_SUPPORT) + target_compile_definitions(${TARGET_NAME} PUBLIC ENABLE_UNICODE_PATH_SUPPORT) +endif() + set_target_properties(${TARGET_NAME} PROPERTIES COMPILE_PDB_NAME ${TARGET_NAME}) # Static library used for unit tests which are always built @@ -132,6 +155,10 @@ if(WIN32) target_compile_definitions(${TARGET_NAME}_s PRIVATE -DNOMINMAX) endif() +if(ENABLE_UNICODE_PATH_SUPPORT) + target_compile_definitions(${TARGET_NAME}_s PUBLIC ENABLE_UNICODE_PATH_SUPPORT) +endif() + set_target_properties(${TARGET_NAME}_s PROPERTIES COMPILE_PDB_NAME ${TARGET_NAME}_s) target_link_libraries(${TARGET_NAME}_s PRIVATE fluid @@ -145,7 +172,7 @@ target_link_libraries(${TARGET_NAME}_s PRIVATE fluid add_cpplint_target(${TARGET_NAME}_cpplint FOR_TARGETS ${TARGET_NAME}) ie_register_plugins(MAIN_TARGET ${TARGET_NAME} - POSSIBLE_PLUGINS clDNNPlugin dliaPlugin GNAPlugin MKLDNNPlugin myriadPlugin) + POSSIBLE_PLUGINS MultiDevicePlugin HeteroPlugin clDNNPlugin dliaPlugin GNAPlugin MKLDNNPlugin myriadPlugin HDDLPlugin) # export targets export(TARGETS ${TARGET_NAME} NAMESPACE IE:: FILE "${CMAKE_BINARY_DIR}/targets.cmake") diff --git a/inference-engine/src/inference_engine/blob_factory.hpp b/inference-engine/src/inference_engine/blob_factory.hpp index 08a356d4acbdac..28f48328fcf3f7 100644 --- a/inference-engine/src/inference_engine/blob_factory.hpp +++ b/inference-engine/src/inference_engine/blob_factory.hpp @@ -6,7 +6,9 @@ #include #include +#include #include "inference_engine.hpp" +#include "ie_memcpy.h" template class BlobFactory { @@ -81,9 +83,40 @@ InferenceEngine::Blob::Ptr make_custom_blob(Args &&... args) { return InferenceEngine::make_shared_blob(InferenceEngine::Precision::fromType(), std::forward(args) ...); } +/** + * Create blob with custom precision + * @tparam T - type off underlined elements + * @param args + * @return + */ +template +InferenceEngine::Blob::Ptr make_custom_blob(InferenceEngine::Layout layout, InferenceEngine::SizeVector size) { + return InferenceEngine::make_shared_blob(InferenceEngine::TensorDesc( + InferenceEngine::Precision::fromType(), + size, + layout)); +} + /** * @brief Creates a TBlob<> object from a Data node * @param Data reference to a smart pointer of the Data node * @return Smart pointer to TBlob<> with the relevant C type to the precision of the data node */ INFERENCE_ENGINE_API_CPP(InferenceEngine::Blob::Ptr) CreateBlobFromData(const InferenceEngine::DataPtr &data); + +/** + * Copy data from vector to Blob + * @tparam T type of data in vector + * @return + */ +template void CopyVectorToBlob(const InferenceEngine::Blob::Ptr outputBlob, const std::vector& inputVector) { + if (outputBlob->size() != inputVector.size()) + THROW_IE_EXCEPTION << "Size mismatch between dims and vector"; + if (outputBlob->element_size() != sizeof(T)) + THROW_IE_EXCEPTION << "Element size mismatch between blob and vector"; + ie_memcpy( + outputBlob->buffer().as(), + outputBlob->byteSize(), + &inputVector[0], + inputVector.size() * sizeof(T)); +} diff --git a/inference-engine/src/inference_engine/blob_transform.cpp b/inference-engine/src/inference_engine/blob_transform.cpp index 2e4fb74e77aaba..0873ff10d8b045 100644 --- a/inference-engine/src/inference_engine/blob_transform.cpp +++ b/inference-engine/src/inference_engine/blob_transform.cpp @@ -158,6 +158,92 @@ static inline void blob_copy_4d(Blob::Ptr src, Blob::Ptr dst) { } } +template +static void blob_copy_5d_t(Blob::Ptr src, Blob::Ptr dst) { + using data_t = typename InferenceEngine::PrecisionTrait::value_type; + + const auto &src_blk_desc = src->getTensorDesc().getBlockingDesc(); + const auto &dst_blk_desc = dst->getTensorDesc().getBlockingDesc(); + + data_t *src_ptr = src->buffer().as() + src_blk_desc.getOffsetPadding(); + data_t *dst_ptr = dst->buffer().as() + dst_blk_desc.getOffsetPadding(); + + SizeVector dims = src->getTensorDesc().getDims(); // == dst's dims + + const size_t N = dims[0]; + const size_t C = dims[1]; + const size_t D = dims[2]; + const size_t H = dims[3]; + const size_t W = dims[4]; + + const Layout src_l = src->getTensorDesc().getLayout(); + const auto &src_strides = src_blk_desc.getStrides(); + const auto N_src_stride = src_strides[0]; + const auto C_src_stride = src_l == NDHWC ? src_strides[4] : src_strides[1]; + const auto D_src_stride = src_l == NDHWC ? src_strides[1] : src_strides[2]; + const auto H_src_stride = src_l == NDHWC ? src_strides[2] : src_strides[3]; + const auto W_src_stride = src_l == NDHWC ? src_strides[3] : src_strides[4]; + + const Layout dst_l = dst->getTensorDesc().getLayout(); + const auto &dst_strides = dst_blk_desc.getStrides(); + const auto N_dst_stride = dst_strides[0]; + const auto C_dst_stride = dst_l == NDHWC ? dst_strides[4] : dst_strides[1]; + const auto D_dst_stride = dst_l == NDHWC ? dst_strides[1] : dst_strides[2]; + const auto H_dst_stride = dst_l == NDHWC ? dst_strides[2] : dst_strides[3]; + const auto W_dst_stride = dst_l == NDHWC ? dst_strides[3] : dst_strides[4]; + + if (src_l != dst_l) { + for (int n = 0; n < N; n++) { + for (int c = 0; c < C; c++) { + for (int d = 0; d < D; d++) { + for (int h = 0; h < H; h++) { + for (int w = 0; w < W; w++) { + dst_ptr[n * N_dst_stride + + c * C_dst_stride + + d * D_dst_stride + + h * H_dst_stride + + w * W_dst_stride] + = + src_ptr[n * N_src_stride + + c * C_src_stride + + d * D_src_stride + + h * H_src_stride + + w * W_src_stride]; + } + } + } + } + } + } else { + for (int i = 0; i < N*C*D*H*W; i++) { + dst_ptr[i] = src_ptr[i]; + } + } +} + +static inline void blob_copy_5d(Blob::Ptr src, Blob::Ptr dst) { + switch (src->getTensorDesc().getPrecision()) { + case Precision::FP32: + case Precision::I32: + blob_copy_5d_t(src, dst); + break; + + case Precision::FP16: + case Precision::U16: + case Precision::I16: + blob_copy_5d_t(src, dst); + break; + + case Precision::U8: + case Precision::I8: + blob_copy_5d_t(src, dst); + break; + + default: + THROW_IE_EXCEPTION << "Unsupported blob transformation for precision " << src->getTensorDesc().getPrecision(); + } +} + void blob_copy(Blob::Ptr src, Blob::Ptr dst) { if (src->buffer() == nullptr) THROW_IE_EXCEPTION << "Cannot copy blob data. Source is not allocated."; @@ -174,8 +260,10 @@ void blob_copy(Blob::Ptr src, Blob::Ptr dst) { if (src->getTensorDesc().getDims().size() == 4) blob_copy_4d(src, dst); + else if (src->getTensorDesc().getDims().size() == 5) + blob_copy_5d(src, dst); else - THROW_IE_EXCEPTION << "Unimplemented blob transformation. Only 4d supported."; + THROW_IE_EXCEPTION << "Unimplemented blob transformation. Only 4d or 5d supported."; } } // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/builders/ie_layer_builder.cpp b/inference-engine/src/inference_engine/builders/ie_layer_builder.cpp index 99af91ca20b506..d7c0dca35307c8 100644 --- a/inference-engine/src/inference_engine/builders/ie_layer_builder.cpp +++ b/inference-engine/src/inference_engine/builders/ie_layer_builder.cpp @@ -14,7 +14,7 @@ using namespace InferenceEngine; Builder::Layer::Layer(const std::string& type, const std::string& name): - name(name), type(type), id((std::numeric_limits::max)()) {} + id((std::numeric_limits::max)()), type(type), name(name) {} Builder::Layer::Layer(const ILayer::CPtr& layer) { id = layer->getId(); diff --git a/inference-engine/src/inference_engine/cnn_network_impl.cpp b/inference-engine/src/inference_engine/cnn_network_impl.cpp index 9691ee3934eefb..d2b179c4f396ac 100644 --- a/inference-engine/src/inference_engine/cnn_network_impl.cpp +++ b/inference-engine/src/inference_engine/cnn_network_impl.cpp @@ -13,6 +13,7 @@ #include "debug.h" #include "graph_tools.hpp" #include +#include #include "network_serializer.h" using namespace std; @@ -248,12 +249,16 @@ StatusCode CNNNetworkImpl::setBatchSize(size_t size, ResponseDesc* responseDesc) return DescriptionBuffer(PARAMETER_MISMATCH, responseDesc) << "Cannot set batch for 1D/3D input"; } + std::string constType = "Const"; for (auto layer : _data) { SizeVector dims = layer.second->getDims(); // Calculates original size for batch = 1 - size_t diff = dims.at(0) / originalBatchSize; - dims.at(0) = size * diff; - layer.second->setDims(dims); + CNNLayerPtr layerT = layer.second->getCreatorLayer().lock(); + if (!layerT || !equal(layerT->type, constType)) { + float diff = static_cast(dims.at(0)) / static_cast(originalBatchSize); + dims.at(0) = static_cast(std::ceil(size * diff)); + layer.second->setDims(dims); + } } return OK; } catch (const InferenceEngineException& e) { diff --git a/inference-engine/src/inference_engine/cnn_network_int8_normalizer.cpp b/inference-engine/src/inference_engine/cnn_network_int8_normalizer.cpp index 5633cb242cf53f..7561cb81d69c96 100644 --- a/inference-engine/src/inference_engine/cnn_network_int8_normalizer.cpp +++ b/inference-engine/src/inference_engine/cnn_network_int8_normalizer.cpp @@ -955,6 +955,11 @@ void CNNNetworkInt8Normalizer::QuantizeConvolutionOrFullyConnected(CNNLayer::Ptr // debug scales. Need to compare with actual values in FP32 scoring target_layer->blobs["ext-scale"] = target_layer->blobs["o-scale"]; + } else { + // we do not have statistics here, we cannot calculate requantizatin scales, + // next layer will be calculated in fp32 + // it's time to return forcedly edge to fp32 as well + target_layer->outData[0]->setPrecision(Precision::FP32); } // Normalizing the weights @@ -1619,9 +1624,7 @@ void precisionColoring(const CNNLayerPtr layer, } void CNNNetworkInt8Normalizer::NormalizeNetwork(ICNNNetwork& network, ICNNNetworkStats& netStats) { - IE_SUPPRESS_DEPRECATED_START - CNNNetwork cnnn(&network); - IE_SUPPRESS_DEPRECATED_END + CNNNetwork cnnn(ICNNNetwork::Ptr(&network, [](void *) {})); int maxSign = 0x7F; int maxUnsign = 0xFF; diff --git a/inference-engine/src/inference_engine/cpp_interfaces/base/ie_inference_plugin_api.hpp b/inference-engine/src/inference_engine/cpp_interfaces/base/ie_inference_plugin_api.hpp index b6138ed031e3bc..3cc1cc238392bc 100644 --- a/inference-engine/src/inference_engine/cpp_interfaces/base/ie_inference_plugin_api.hpp +++ b/inference-engine/src/inference_engine/cpp_interfaces/base/ie_inference_plugin_api.hpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace InferenceEngine { @@ -70,7 +71,7 @@ class IInferencePluginAPI { virtual ~IInferencePluginAPI() = default; }; -class DeviceIDParser { +class INFERENCE_ENGINE_API_CLASS(DeviceIDParser) { std::string deviceName; std::string deviceID; @@ -81,6 +82,7 @@ class DeviceIDParser { std::string getDeviceName() const; static std::vector getHeteroDevices(std::string fallbackDevice); + static std::vector getMultiDevices(std::string devicesList); }; } // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/cpp_interfaces/ie_task.hpp b/inference-engine/src/inference_engine/cpp_interfaces/ie_task.hpp index aba3c13de6a4fe..eb8fd8d722d660 100644 --- a/inference-engine/src/inference_engine/cpp_interfaces/ie_task.hpp +++ b/inference-engine/src/inference_engine/cpp_interfaces/ie_task.hpp @@ -42,6 +42,7 @@ class INFERENCE_ENGINE_API_CLASS(Task) { Task(); explicit Task(const std::function &function); + virtual ~Task() = default; /** * @brief Executes the task with catching all exceptions. It doesn't check that task is running diff --git a/inference-engine/src/inference_engine/cpp_interfaces/ie_task_synchronizer.hpp b/inference-engine/src/inference_engine/cpp_interfaces/ie_task_synchronizer.hpp index 16082934137fc0..d5f2018bff66d9 100644 --- a/inference-engine/src/inference_engine/cpp_interfaces/ie_task_synchronizer.hpp +++ b/inference-engine/src/inference_engine/cpp_interfaces/ie_task_synchronizer.hpp @@ -21,6 +21,7 @@ class TaskSynchronizer { typedef std::shared_ptr Ptr; TaskSynchronizer() : _taskCount(0) {} + virtual ~TaskSynchronizer() = default; virtual void lock() { auto taskID = _addTaskToQueue(); diff --git a/inference-engine/src/inference_engine/cpp_interfaces/impl/ie_infer_async_request_thread_safe_default.hpp b/inference-engine/src/inference_engine/cpp_interfaces/impl/ie_infer_async_request_thread_safe_default.hpp index 18ed1c15c48c4b..414f65dd0aa915 100644 --- a/inference-engine/src/inference_engine/cpp_interfaces/impl/ie_infer_async_request_thread_safe_default.hpp +++ b/inference-engine/src/inference_engine/cpp_interfaces/impl/ie_infer_async_request_thread_safe_default.hpp @@ -101,13 +101,13 @@ class AsyncInferRequestThreadSafeDefault : public AsyncInferRequestThreadSafeInt void waitAllAsyncTasks() { try { while (!_listAsyncTasks.empty()) { - _listAsyncTasks.remove_if([this](StagedTask::Ptr task) -> bool { + _listAsyncTasks.remove_if([](StagedTask::Ptr task) -> bool { auto sts = task->getStatus(); return !task->isOnWait() && (Task::Status::TS_DONE == sts || Task::Status::TS_ERROR == sts || Task::Status::TS_INITIAL == sts); }); auto findIter = std::find_if(_listAsyncTasks.begin(), _listAsyncTasks.end(), - [this](StagedTask::Ptr task) { return !task->isOnWait(); }); + [](StagedTask::Ptr task) { return !task->isOnWait(); }); if (findIter != _listAsyncTasks.end()) { try { (*findIter)->wait(-1); diff --git a/inference-engine/src/inference_engine/cpp_interfaces/impl/ie_memory_state_internal.hpp b/inference-engine/src/inference_engine/cpp_interfaces/impl/ie_memory_state_internal.hpp index 7d5a9fd8550f1d..10f11e768acafa 100644 --- a/inference-engine/src/inference_engine/cpp_interfaces/impl/ie_memory_state_internal.hpp +++ b/inference-engine/src/inference_engine/cpp_interfaces/impl/ie_memory_state_internal.hpp @@ -3,6 +3,7 @@ // #pragma once + #include #include @@ -16,7 +17,7 @@ class MemoryStateInternal : public IMemoryStateInternal { std::string name; Blob::Ptr state; - public: +public: explicit MemoryStateInternal(std::string name) : name(name) { } std::string GetName() const override { diff --git a/inference-engine/src/inference_engine/cpp_interfaces/interface/ie_imemory_state_internal.hpp b/inference-engine/src/inference_engine/cpp_interfaces/interface/ie_imemory_state_internal.hpp index 387c19b78ec932..cebc688cad0b43 100644 --- a/inference-engine/src/inference_engine/cpp_interfaces/interface/ie_imemory_state_internal.hpp +++ b/inference-engine/src/inference_engine/cpp_interfaces/interface/ie_imemory_state_internal.hpp @@ -3,6 +3,9 @@ // #pragma once + +#include + #include #include @@ -11,7 +14,7 @@ namespace InferenceEngine { * @brief minimal interface for memory state implementation */ class IMemoryStateInternal { - public: +public: using Ptr = std::shared_ptr; virtual ~IMemoryStateInternal() = default; diff --git a/inference-engine/src/inference_engine/cpp_interfaces/interface/ie_internal_plugin_config.hpp b/inference-engine/src/inference_engine/cpp_interfaces/interface/ie_internal_plugin_config.hpp new file mode 100644 index 00000000000000..8be8cae7dcdc2e --- /dev/null +++ b/inference-engine/src/inference_engine/cpp_interfaces/interface/ie_internal_plugin_config.hpp @@ -0,0 +1,33 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +/** + * @brief a header for properties that are passed from IE to plguins + * or from one plugin to another + * @file ie_internal_plugin_config.hpp + */ +#pragma once + +#include +#include +#include + +namespace InferenceEngine { + +namespace InternalPluginConfigParams { + +/** +* @brief shortcut for defining internal configuration keys +*/ +#define IE_INTERNAL_CONFIG_KEY(name) InferenceEngine::InternalPluginConfigParams::_IE_INTERNAL_CONFIG_KEY(name) +#define _IE_INTERNAL_CONFIG_KEY(name) KEY_##name +#define DECLARE_IE_INTERNAL_CONFIG_KEY(name) static constexpr auto _IE_INTERNAL_CONFIG_KEY(name) = #name + +/** + * @brief This key should be used to mark input executable subnetworks + */ +DECLARE_IE_INTERNAL_CONFIG_KEY(SUBNETWORK_WITH_NETWORK_INPUTS); + +} // namespace InternalPluginConfigParams +} // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/debug.h b/inference-engine/src/inference_engine/debug.h index 2e9200de177a61..6f9070572ca3d0 100644 --- a/inference-engine/src/inference_engine/debug.h +++ b/inference-engine/src/inference_engine/debug.h @@ -84,7 +84,9 @@ inline std::ostream & operator << (std::ostream &out, const std::vector &vec) * @param s - string to trim */ inline void ltrim(std::string &s) { - s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int c){ + return !std::isspace(c); + })); } /** @@ -92,7 +94,9 @@ inline void ltrim(std::string &s) { * @param s - string to trim */ inline void rtrim(std::string &s) { - s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + s.erase(std::find_if(s.rbegin(), s.rend(), [](int c) { + return !std::isspace(c); + }).base(), s.end()); } /** diff --git a/inference-engine/src/inference_engine/graph_tools.hpp b/inference-engine/src/inference_engine/graph_tools.hpp index eab19b7bc39e35..ab7dbc1545c433 100644 --- a/inference-engine/src/inference_engine/graph_tools.hpp +++ b/inference-engine/src/inference_engine/graph_tools.hpp @@ -702,9 +702,6 @@ inline CNNNetPtr CNNNetCopy(const ICNNNetwork &input, const Copier &cp) { auto net = std::make_shared(); // setting base args - IE_SUPPRESS_DEPRECATED_START - net->setTargetDevice(input.getTargetDevice()); - IE_SUPPRESS_DEPRECATED_END net->setPrecision(input.getPrecision()); char name[1024]; diff --git a/inference-engine/src/inference_engine/graph_transformer.h b/inference-engine/src/inference_engine/graph_transformer.h index 609a1332f3cea7..3c8e5401ae1113 100644 --- a/inference-engine/src/inference_engine/graph_transformer.h +++ b/inference-engine/src/inference_engine/graph_transformer.h @@ -24,6 +24,7 @@ namespace InferenceEngine { class INFERENCE_ENGINE_API_CLASS(ConstTransformer) { public: explicit ConstTransformer(details::CNNNetworkImpl* _network); + virtual ~ConstTransformer() = default; /** * @brief calculates const layers, combines const subgraph into a single const layers diff --git a/inference-engine/src/inference_engine/hetero/hetero_plugin.cpp b/inference-engine/src/inference_engine/hetero/hetero_plugin.cpp deleted file mode 100644 index c8528f8560ef01..00000000000000 --- a/inference-engine/src/inference_engine/hetero/hetero_plugin.cpp +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include "ie_metric_helpers.hpp" -#include "hetero_plugin.hpp" -#include -#include -#include -#include -#include "ie_plugin_config.hpp" -#include "hetero/hetero_plugin_config.hpp" -#include -#include "hetero_plugin_base.hpp" -#include "inference_engine.hpp" -#include "hetero_executable_network.hpp" -#include "fallback_policy.hpp" - -using namespace InferenceEngine; -using namespace InferenceEngine::PluginConfigParams; -using namespace InferenceEngine::HeteroConfigParams; -using namespace HeteroPlugin; -using namespace std; - -IE_SUPPRESS_DEPRECATED_START - -IHeteroInferencePlugin::~IHeteroInferencePlugin() { -} - -IE_SUPPRESS_DEPRECATED_START - -static Version heteroPluginDescription = { - {2, 0}, // plugin API version - CI_BUILD_NUMBER, - "heteroPlugin" // plugin description message -}; - -void Engine::GetVersion(const Version *&versionInfo)noexcept { - versionInfo = &heteroPluginDescription; -} - -Engine::Engine() { - _pluginName = "HETERO"; - _config[InferenceEngine::PluginConfigParams::KEY_EXCLUSIVE_ASYNC_REQUESTS] = "YES"; - _config[KEY_HETERO_DUMP_GRAPH_DOT] = NO; -} - -InferenceEngine::ExecutableNetworkInternal::Ptr Engine::LoadExeNetworkImpl(const ICore * core, InferenceEngine::ICNNNetwork &network, - const std::map &config) { - // TODO(amalyshe) do we need here verification of input precisions? - std::map tconfig; - tconfig = config; - - // we must not override the parameter, but need to copy everything from plugin config - for (auto && c : _config) { - if (tconfig.find(c.first) == tconfig.end()) { - tconfig[c.first] = c.second; - } - } - - return std::make_shared(network, core, tconfig, _extensions, _deviceLoaders, error_listener); -} - -void Engine::SetConfig(const std::map &config) { - if (_config.find("TARGET_FALLBACK") == _config.end()) { - _config["TARGET_FALLBACK"] = ""; - } - - for (auto &&i : config) { - _config[i.first] = i.second; - } -} - -IE_SUPPRESS_DEPRECATED_START -void Engine::SetDeviceLoader(const std::string &device, - IHeteroDeviceLoader::Ptr pLoader) { - _deviceLoaders[device] = pLoader; -} -IE_SUPPRESS_DEPRECATED_END - -void Engine::AddExtension(InferenceEngine::IExtensionPtr extension) { - _extensions.push_back(extension); -} - -void Engine::SetAffinity(InferenceEngine::ICNNNetwork &network, - const std::map &config) { - FallbackPolicy fbPolicy(_deviceLoaders, _config[KEY_HETERO_DUMP_GRAPH_DOT] == YES, GetCore()); - fbPolicy.init(_config["TARGET_FALLBACK"], config, _extensions); - fbPolicy.setAffinity(fbPolicy.getAffinities(config, network), network); -} - -void Engine::SetLogCallback(IErrorListener &listener) { - error_listener = &listener; - - IE_SUPPRESS_DEPRECATED_START - for (auto& device_loader : _deviceLoaders) - device_loader.second->SetLogCallback(*error_listener); - IE_SUPPRESS_DEPRECATED_END -} - -void Engine::QueryNetwork(const ICNNNetwork &network, const std::map& config, QueryNetworkResult &res) const { - auto _deviceLoaders_ = _deviceLoaders; - - auto it = _config.find(KEY_HETERO_DUMP_GRAPH_DOT); - IE_ASSERT(it != _config.end()); - FallbackPolicy fbPolicy(_deviceLoaders_, it->second == YES, GetCore()); - it = _config.find("TARGET_FALLBACK"); - if (it == _config.end()) { - it = config.find("TARGET_FALLBACK"); - - if (it == config.end()) { - THROW_IE_EXCEPTION << "The 'TARGET_FALLBACK' option was not defined for heterogeneous plugin"; - } - } - fbPolicy.init(it->second, config, _extensions); - res = fbPolicy.getAffinities(config, network); -} - -Parameter Engine::GetMetric(const std::string& name, const std::map & options) const { - if (METRIC_KEY(SUPPORTED_METRICS) == name) { - IE_SET_METRIC_RETURN(SUPPORTED_METRICS, std::vector{ - METRIC_KEY(SUPPORTED_METRICS), - METRIC_KEY(SUPPORTED_CONFIG_KEYS)}); - } else if (METRIC_KEY(SUPPORTED_CONFIG_KEYS) == name) { - IE_SET_METRIC_RETURN(SUPPORTED_CONFIG_KEYS, std::vector{ - HETERO_CONFIG_KEY(DUMP_GRAPH_DOT), - "TARGET_FALLBACK", - CONFIG_KEY(EXCLUSIVE_ASYNC_REQUESTS)}); - } else { - THROW_IE_EXCEPTION << "Unsupported Plugin metric: " << name; - } -} - -Parameter Engine::GetConfig(const std::string& name, const std::map & options) const { - if (name == HETERO_CONFIG_KEY(DUMP_GRAPH_DOT)) { - auto it = _config.find(KEY_HETERO_DUMP_GRAPH_DOT); - IE_ASSERT(it != _config.end()); - bool dump = it->second == YES; - return { dump }; - } else { - THROW_IE_EXCEPTION << "Unsupported config key: " << name; - } -} - -namespace HeteroPlugin { - -InferenceEngine::StatusCode CreateHeteroPluginEngine( - InferenceEngine::IInferencePlugin *&plugin, - InferenceEngine::ResponseDesc *resp) noexcept { - try { - plugin = new HeteroPluginBase( - {{2, 0}, "heteroPlugin", "heteroPlugin"}, - std::make_shared()); - return OK; - } - catch (std::exception &ex) { - return DescriptionBuffer(GENERAL_ERROR, resp) << ex.what(); - } -} - -} // namespace HeteroPlugin diff --git a/inference-engine/src/inference_engine/ie_cnn_layer_builder.h b/inference-engine/src/inference_engine/ie_cnn_layer_builder.h index 6283695a1f3d71..9fbc3107210d78 100644 --- a/inference-engine/src/inference_engine/ie_cnn_layer_builder.h +++ b/inference-engine/src/inference_engine/ie_cnn_layer_builder.h @@ -72,6 +72,7 @@ static InferenceEngine::Builder::ConverterRegister _reg_converter_##__type(#__ty class INodeConverter { public: + virtual ~INodeConverter() = default; virtual CNNLayer::Ptr createLayer(const std::shared_ptr& layer, const Precision &precision) const = 0; virtual bool canCreate(const std::shared_ptr& node) const = 0; @@ -97,6 +98,7 @@ class NodeConverter: public INodeConverter { class BaseConverter { public: explicit BaseConverter(const std::string& type): type(type) {} + virtual ~BaseConverter() = default; virtual CNNLayer::Ptr createLayer(const std::shared_ptr& layer, Precision precision) = 0; virtual bool canCreate(const std::string& nodeType) const = 0; diff --git a/inference-engine/src/inference_engine/ie_cnn_net_reader_impl.cpp b/inference-engine/src/inference_engine/ie_cnn_net_reader_impl.cpp index 83351a1ba99f5c..8a02da429022dd 100644 --- a/inference-engine/src/inference_engine/ie_cnn_net_reader_impl.cpp +++ b/inference-engine/src/inference_engine/ie_cnn_net_reader_impl.cpp @@ -134,7 +134,7 @@ StatusCode CNNNetReaderImpl::ReadNetwork(pugi::xml_document& xmlDoc) { _version = GetFileVersion(root); if (_version < 2) THROW_IE_EXCEPTION << "deprecated IR version: " << _version; - if (_version > 6) THROW_IE_EXCEPTION << "cannot parse future versions: " << _version; + if (_version > 7) THROW_IE_EXCEPTION << "cannot parse future versions: " << _version; _parser = parserCreator->create(_version); network = _parser->Parse(root); name = network->getName(); diff --git a/inference-engine/src/inference_engine/ie_cnn_net_reader_impl.h b/inference-engine/src/inference_engine/ie_cnn_net_reader_impl.h index 7c01e37ef6191a..94c882d622168b 100644 --- a/inference-engine/src/inference_engine/ie_cnn_net_reader_impl.h +++ b/inference-engine/src/inference_engine/ie_cnn_net_reader_impl.h @@ -25,6 +25,7 @@ namespace details { struct FormatParserCreator { using Ptr = std::shared_ptr; virtual std::shared_ptr create(size_t version) = 0; + virtual ~FormatParserCreator() = default; }; struct V2FormatParserCreator : public FormatParserCreator { diff --git a/inference-engine/src/inference_engine/ie_core.cpp b/inference-engine/src/inference_engine/ie_core.cpp index 3146ec7e5799e9..06dce037229968 100644 --- a/inference-engine/src/inference_engine/ie_core.cpp +++ b/inference-engine/src/inference_engine/ie_core.cpp @@ -8,12 +8,11 @@ #include "details/ie_exception_conversion.hpp" #include "cpp_interfaces/base/ie_plugin_base.hpp" #include "details/ie_so_pointer.hpp" +#include "multi-device/multi_device_config.hpp" -#include "hetero/hetero_plugin.hpp" #include "ie_util_internal.hpp" #include "file_utils.h" #include "ie_icore.hpp" -#include "cpp_interfaces/ie_itask_executor.hpp" #include #include @@ -84,25 +83,31 @@ std::vector DeviceIDParser::getHeteroDevices(std::string fallbackDe return deviceNames; } -class Core::Impl : public ICore { - void RegisterHeteroPlugin() { - IInferencePlugin * plugin = nullptr; - ResponseDesc resp; - HeteroPlugin::CreateHeteroPluginEngine(plugin, &resp); +std::vector DeviceIDParser::getMultiDevices(std::string devicesList) { + std::vector deviceNames; + auto trim_request_info = [] (std::string device_with_requests){ + auto opening_bracket = device_with_requests.find_first_of('('); + return device_with_requests.substr(0, opening_bracket); + }; + std::string device; + char delimiter = ','; + size_t pos = 0; + // in addition to the list of devices, every device can have a #requests in the brackets e.g. "CPU(100)" + // we skip the #requests info here + while ((pos = devicesList.find(delimiter)) != std::string::npos) { + auto d = devicesList.substr(0, pos); + deviceNames.push_back(trim_request_info(d)); + devicesList.erase(0, pos + 1); + } - IInferencePluginAPI * iplugin_api_ptr = getInferencePluginAPIInterface(plugin); - IE_ASSERT(iplugin_api_ptr != nullptr); + if (!devicesList.empty()) + deviceNames.push_back(trim_request_info(devicesList)); - // set reference to ICore interface - iplugin_api_ptr->SetCore(this); + return deviceNames; +} - std::string name = iplugin_api_ptr->GetName(); - plugins[name] = InferencePlugin(InferenceEnginePluginPtr(plugin)); - - // put info about HETERO plugin to registry as well - pluginRegistry[name] = { "", { }, { } }; - } +class Core::Impl : public ICore { ITaskExecutor::Ptr _taskExecutor = nullptr; mutable std::map > plugins; @@ -115,13 +120,6 @@ class Core::Impl : public ICore { IErrorListener * listener = nullptr; public: - /** - * @brief Constructs Impl with HETERO plugin only - */ - Impl() { - RegisterHeteroPlugin(); - } - ~Impl() override; /** @@ -385,6 +383,9 @@ std::map Core::GetVersions(const std::string & deviceName) if (deviceName.find("HETERO:") == 0) { deviceNames = DeviceIDParser::getHeteroDevices(deviceName.substr(7)); deviceNames.push_back("HETERO"); + } else if (deviceName.find("MULTI") == 0) { + deviceNames.push_back("MULTI"); + deviceNames = DeviceIDParser::getMultiDevices(deviceName.substr(6)); } else { deviceNames.push_back(deviceName); } @@ -413,6 +414,9 @@ ExecutableNetwork Core::LoadNetwork(CNNNetwork network, const std::string & devi if (deviceName_.find("HETERO:") == 0) { deviceName_ = "HETERO"; config_["TARGET_FALLBACK"] = deviceName.substr(7); + } else if (deviceName_.find("MULTI:") == 0) { + deviceName_ = "MULTI"; + config_[InferenceEngine::MultiDeviceConfigParams::KEY_MULTI_DEVICE_PRIORITIES] = deviceName.substr(6); } else { DeviceIDParser parser(deviceName_); deviceName_ = parser.getDeviceName(); @@ -430,6 +434,9 @@ void Core::AddExtension(IExtensionPtr extension, const std::string & deviceName_ if (deviceName_.find("HETERO") == 0) { THROW_IE_EXCEPTION << "HETERO device does not support extensions. Please, set extensions directly to fallback devices"; } + if (deviceName_.find("MULTI") == 0) { + THROW_IE_EXCEPTION << "MULTI device does not support extensions. Please, set extensions directly to fallback devices"; + } DeviceIDParser parser(deviceName_); std::string deviceName = parser.getDeviceName(); @@ -442,6 +449,9 @@ ExecutableNetwork Core::ImportNetwork(const std::string &modelFileName, const st if (deviceName_.find("HETERO") == 0) { THROW_IE_EXCEPTION << "HETERO device does not support ImportNetwork"; } + if (deviceName_.find("MULTI") == 0) { + THROW_IE_EXCEPTION << "MULTI device does not support ImportNetwork"; + } DeviceIDParser parser(deviceName_); std::string deviceName = parser.getDeviceName(); @@ -462,6 +472,9 @@ QueryNetworkResult Core::QueryNetwork(const ICNNNetwork &network, const std::str auto config_ = config; std::string deviceName_ = deviceName; + if (deviceName_.find("MULTI") == 0) { + THROW_IE_EXCEPTION << "MULTI device does not support QueryNetwork"; + } if (deviceName_.find("HETERO:") == 0) { deviceName_ = "HETERO"; @@ -494,6 +507,18 @@ void Core::SetConfig(const std::map & config_, const s } } + // MULTI case + { + if (deviceName_.find("MULTI:") == 0) { + THROW_IE_EXCEPTION << "SetConfig is supported only for MULTI itself (without devices). " + "You can configure the devices with SetConfig before creating the MULTI on top."; + } + + if (config_.find(MultiDeviceConfigParams::KEY_MULTI_DEVICE_PRIORITIES) != config_.end()) { + THROW_IE_EXCEPTION << "Please, specify DEVICE_PRIORITIES to the LoadNetwork directly, " + "as you will need to pass the same DEVICE_PRIORITIES anyway."; + } + } if (deviceName_.empty()) { _impl->SetConfigForPlugins(config_, std::string()); @@ -521,6 +546,13 @@ Parameter Core::GetConfig(const std::string & deviceName_, const std::string & n "GetConfig is also possible for the individual devices before creating the HETERO on top."; } } + // MULTI case + { + if (deviceName_.find("MULTI:") == 0) { + THROW_IE_EXCEPTION << "You can only GetConfig of the MULTI itself (without devices). " + "GetConfig is also possible for the individual devices before creating the MULTI on top."; + } + } DeviceIDParser device(deviceName_); std::string deviceName = device.getDeviceName(); @@ -550,6 +582,14 @@ Parameter Core::GetMetric(const std::string & deviceName_, const std::string & n } } + // MULTI case + { + if (deviceName_.find("MULTI:") == 0) { + THROW_IE_EXCEPTION + << "You can get specific metrics with the GetMetric only for the MULTI itself (without devices). " + "To get individual devices's metrics call GetMetric for each device separately"; + } + } DeviceIDParser device(deviceName_); std::string deviceName = device.getDeviceName(); @@ -612,10 +652,6 @@ void Core::RegisterPlugins(const std::string & xmlConfigFile) { } void Core::UnregisterPlugin(const std::string & deviceName_) { - if (deviceName_.find("HETERO") == 0) { - THROW_IE_EXCEPTION << "HETERO device cannot be unregistered from Inference Engine"; - } - DeviceIDParser parser(deviceName_); std::string deviceName = parser.getDeviceName(); diff --git a/inference-engine/src/inference_engine/ie_data.cpp b/inference-engine/src/inference_engine/ie_data.cpp index 9c4c3197c5f80a..678b0bad840436 100644 --- a/inference-engine/src/inference_engine/ie_data.cpp +++ b/inference-engine/src/inference_engine/ie_data.cpp @@ -73,7 +73,7 @@ const Precision& Data::getPrecision() const { } const TensorDesc& Data::getTensorDesc() const { - if ((tensorDesc.getDims().size() == 0 && tensorDesc.getDims() != dims) || + if ((tensorDesc.getDims().size() == 0 && tensorDesc.getDims() != dims && dims[0] != 1) || (tensorDesc.getLayout() == Layout::ANY && layout != Layout::ANY) || (!tensorDesc.getPrecision() && precision)) { THROW_IE_EXCEPTION << "Tensor descriptor is empty!"; diff --git a/inference-engine/src/inference_engine/ie_device.cpp b/inference-engine/src/inference_engine/ie_device.cpp index fb06cf3b2d1865..e078e0ca266666 100644 --- a/inference-engine/src/inference_engine/ie_device.cpp +++ b/inference-engine/src/inference_engine/ie_device.cpp @@ -33,11 +33,17 @@ FindPluginResponse InferenceEngine::findPlugin(const FindPluginRequest& req) { pluginVec.push_back("myriadPlugin"); #endif break; + case TargetDevice::eHDDL: + pluginVec.push_back("HDDLPlugin"); + break; case TargetDevice::eGNA: #ifdef ENABLE_GNA pluginVec.push_back("GNAPlugin"); #endif break; + case TargetDevice::eMULTI: + pluginVec.push_back("MultiDevicePlugin"); + break; case TargetDevice::eHETERO: pluginVec.push_back("HeteroPlugin"); break; diff --git a/inference-engine/src/inference_engine/ie_format_parser.cpp b/inference-engine/src/inference_engine/ie_format_parser.cpp index 62d9924f79a404..51db65c3617e88 100644 --- a/inference-engine/src/inference_engine/ie_format_parser.cpp +++ b/inference-engine/src/inference_engine/ie_format_parser.cpp @@ -8,7 +8,6 @@ #include "ie_layer_parsers.h" #include "xml_parse_utils.h" #include "ie_blob_proxy.hpp" -#include "range_iterator.hpp" #include #include #include "ie_icnn_network_stats.hpp" @@ -82,6 +81,8 @@ void FormatParser::ParseGenericParams(pugi::xml_node& node, LayerParseParameters LayerParseParameters::LayerPortData port; port.precision = prms.precision; ParsePort(port, _cn); + if (prms.type == "Const") + prms.precision = port.precision; layerParsePrms.addOutputPort(port); } } @@ -192,6 +193,7 @@ FormatParser::FormatParser(size_t version): _version(version) { std::make_shared>("ShuffleChannels"), std::make_shared>("DepthToSpace"), std::make_shared>("SpaceToDepth"), + std::make_shared>("SparseFillEmptyRows"), std::make_shared>("ReverseSequence"), std::make_shared>("Squeeze"), std::make_shared>("Unsqueeze"), @@ -251,7 +253,10 @@ FormatParser::FormatParser(size_t version): _version(version) { std::make_shared>("ReduceSum"), std::make_shared>("ReduceSumSquare"), std::make_shared>("GatherTree"), - std::make_shared>("TopK") + std::make_shared>("TopK"), + std::make_shared>("Unique"), + std::make_shared>("NonMaxSuppression"), + std::make_shared>("ScatterUpdate") }; creators.emplace_back(_version < 6 ? std::make_shared>("Quantize") : std::make_shared>("FakeQuantize")); diff --git a/inference-engine/src/inference_engine/ie_icore.hpp b/inference-engine/src/inference_engine/ie_icore.hpp index a6fcf44d1a0a04..67b6b0710722cb 100644 --- a/inference-engine/src/inference_engine/ie_icore.hpp +++ b/inference-engine/src/inference_engine/ie_icore.hpp @@ -10,7 +10,8 @@ #include #include -#include +#include "ie_plugin_ptr.hpp" +#include "cpp_interfaces/ie_itask_executor.hpp" namespace InferenceEngine { diff --git a/inference-engine/src/inference_engine/ie_ir_parser.hpp b/inference-engine/src/inference_engine/ie_ir_parser.hpp index 3fc177b216c032..0c77a676767b60 100644 --- a/inference-engine/src/inference_engine/ie_ir_parser.hpp +++ b/inference-engine/src/inference_engine/ie_ir_parser.hpp @@ -27,7 +27,7 @@ namespace InferenceEngine { class IParser { public: using Ptr = std::shared_ptr; - + virtual ~IParser() = default; virtual std::shared_ptr parse(const pugi::xml_node &root, const Blob::CPtr& weights) = 0; }; @@ -35,6 +35,7 @@ class IRParser { public: explicit IRParser(size_t version); std::shared_ptr parse(const pugi::xml_node &root, const Blob::CPtr& weights); + virtual ~IRParser() = default; private: IParser::Ptr parser; diff --git a/inference-engine/src/inference_engine/ie_layer_parsers.cpp b/inference-engine/src/inference_engine/ie_layer_parsers.cpp index 2867cd1534d80b..e4ef8a6fbddb25 100644 --- a/inference-engine/src/inference_engine/ie_layer_parsers.cpp +++ b/inference-engine/src/inference_engine/ie_layer_parsers.cpp @@ -107,10 +107,11 @@ using WBlob = TBlob::Ptr; class BodyParser { public: - BodyParser(pugi::xml_node &net_node, size_t ir_version) : - body(net_node), parser(FormatParser(ir_version)) {} + BodyParser(pugi::xml_node &net_node, size_t ir_version, Precision prec) : + body(net_node), parser(FormatParser(ir_version)), default_precision(prec) {} void parse(PortSet in_request, PortSet out_request) { + body.append_attribute("precision").set_value(default_precision.name()); auto net = parser.Parse(body); for (const auto &pi : in_request) @@ -148,6 +149,7 @@ class BodyParser { private: pugi::xml_node &body; FormatParser parser; + Precision default_precision; PortMap inputs; PortMap outputs; @@ -163,7 +165,7 @@ CNNLayer::Ptr TILayerCreator::CreateLayer(pugi::xml_node& node, LayerParseParame auto all_inputs = allRequiredInputs(node); auto all_outputs = allRequiredOutputs(node); - auto parser = std::make_shared(body, layerParsePrms.underIRVersion); + auto parser = std::make_shared(body, layerParsePrms.underIRVersion, layerParsePrms.prms.precision); parser->parse(all_inputs, all_outputs); auto ins = parser->getInsMap(); diff --git a/inference-engine/src/inference_engine/ie_layer_parsers.h b/inference-engine/src/inference_engine/ie_layer_parsers.h index f5d6e106c58479..85d7454db377cc 100644 --- a/inference-engine/src/inference_engine/ie_layer_parsers.h +++ b/inference-engine/src/inference_engine/ie_layer_parsers.h @@ -8,7 +8,6 @@ #include #include "ie_format_parser.h" #include "xml_parse_utils.h" -#include "range_iterator.hpp" #include "details/caseless.hpp" #include #include diff --git a/inference-engine/src/inference_engine/ie_layer_validators.cpp b/inference-engine/src/inference_engine/ie_layer_validators.cpp index b7f5ff56c3634f..f37889fdaf8889 100644 --- a/inference-engine/src/inference_engine/ie_layer_validators.cpp +++ b/inference-engine/src/inference_engine/ie_layer_validators.cpp @@ -189,10 +189,6 @@ LayerValidator::Ptr LayerValidators::getValidator(const std::string& type) { return _validators[type]; } -void LayerValidators::addImpl(const std::string& type, const LayerValidator::Ptr& validator) { - _validators[type] = validator; -} - LayerValidators* LayerValidators::_instance = nullptr; GeneralValidator::GeneralValidator(const std::string& _type) : LayerValidator(_type) {} @@ -224,7 +220,7 @@ void FullyConnectedValidator::checkCorrespondence(const CNNLayer* layer, FullyConnectedValidator::FullyConnectedValidator(const std::string& _type) : LayerValidator(_type) {} void FullyConnectedValidator::checkShapes(const CNNLayer* layer, const std::vector& inShapes) const { - checkNumOfInput(inShapes, {1}); + checkNumOfInput(inShapes, {1, 2, 3}); } void CropValidator::parseParams(CNNLayer* layer) { @@ -437,7 +433,7 @@ void ConvolutionValidator::checkCorrespondence(const CNNLayer* layer, } void ConvolutionValidator::checkShapes(const CNNLayer* layer, const std::vector& inShapes) const { - checkNumOfInput(inShapes, {1}); + checkNumOfInput(inShapes, {1, 2, 3}); } void DeconvolutionValidator::parseParams(CNNLayer* layer) { @@ -498,7 +494,7 @@ void DeconvolutionValidator::checkCorrespondence(const CNNLayer* layer, } void DeconvolutionValidator::checkShapes(const CNNLayer* layer, const std::vector& inShapes) const { - checkNumOfInput(inShapes, {1}); + checkNumOfInput(inShapes, {1, 2, 3}); } void DeformableConvolutionValidator::parseParams(CNNLayer* layer) { @@ -543,7 +539,7 @@ void DeformableConvolutionValidator::checkCorrespondence(const CNNLayer* layer, } void DeformableConvolutionValidator::checkShapes(const CNNLayer* layer, const std::vector& inShapes) const { - checkNumOfInput(inShapes, {2}); + checkNumOfInput(inShapes, {2, 3, 4}); } PoolingValidator::PoolingValidator(const std::string& _type) : LayerValidator(_type) {} @@ -858,8 +854,6 @@ void EltwiseValidator::parseParams(CNNLayer* layer) { casted->_operation = EltwiseLayer::Pow; } else if (op == "mean") { casted->_operation = EltwiseLayer::Mean; - } else if (op == "select") { - casted->_operation = EltwiseLayer::Select; } else { THROW_IE_EXCEPTION << "Unsupported element wise operation: " << op; } @@ -1447,6 +1441,52 @@ void SpaceToDepthValidator::checkShapes(const CNNLayer* layer, const vector(layer); + if (!casted) { + THROW_IE_EXCEPTION << layer->name << " Layer is not instance of SparseFillEmptyRows class"; + } +} + +void SparseFillEmptyRowsValidator::checkParams(const CNNLayer* layer) { + LayerValidator::checkParams(layer); +} + +void SparseFillEmptyRowsValidator::checkShapes(const CNNLayer* layer, const vector& inShapes) const { + auto casted = dynamic_cast(layer); + if (!casted) { + THROW_IE_EXCEPTION << layer->name << " Layer is not instance of SparseFillEmptyRows class"; + } + + size_t numInputs = inShapes.size(); + if (numInputs != 4) + THROW_IE_EXCEPTION << layer->name << " SparseFillEmptyRows must have 4 inputs, but actually it has: " << numInputs; + + // Check dimensions of a tensor with input indices + if (inShapes[0].size() != 2) + THROW_IE_EXCEPTION << layer->name << " Input indices of SparseFillEmptyRows must be 2-D tensor"; + if (inShapes[0][1] != 2) + THROW_IE_EXCEPTION << layer->name << " Input indices must be two-dimensional"; + + // Check dimensions of a tensor with input values + if (inShapes[1].size() != 1) + THROW_IE_EXCEPTION << layer->name << " Input values of SparseFillEmptyRows must be 1-D tensor"; + if (inShapes[1][0] != inShapes[0][0]) + THROW_IE_EXCEPTION << layer->name << " Number of input indices and values must match"; + + // Check dimensions of a tensor with a dense shape + if (inShapes[2].size() != 1) + THROW_IE_EXCEPTION << layer->name << " Dense shape of SparseFillEmptyRows must be 1-D tensor"; + // TODO: check that dense shape value is set + + // Check dimensions of a tensor with default value + if (inShapes[3].size() != 1) + THROW_IE_EXCEPTION << layer->name << " Default value of SparseFillEmptyRows must be 1-D tensor"; +} + + ReverseSequenceValidator::ReverseSequenceValidator(const std::string& _type) : LayerValidator(_type) {} void ReverseSequenceValidator::parseParams(CNNLayer* layer) { @@ -1665,59 +1705,31 @@ static RNNSequenceLayer::Direction direction_from(string direction_name) { RNNSequenceLayer::FWD; } -template<> -std::vector -RNNBaseValidator::def_acts = {"sigmoid", "tanh", "tanh"}; -template<> -std::vector -RNNBaseValidator::def_alpha = {0, 0, 0}; -template<> -std::vector -RNNBaseValidator::def_beta = {0, 0, 0}; -template<> -size_t -RNNBaseValidator::G = 4; -template<> -size_t -RNNBaseValidator::NS = 2; - -template<> -std::vector -RNNBaseValidator::def_acts = {"sigmoid", "tanh"}; -template<> -std::vector -RNNBaseValidator::def_alpha = {0, 0}; -template<> -std::vector -RNNBaseValidator::def_beta = {0, 0}; -template<> -size_t -RNNBaseValidator::G = 3; -template<> -size_t -RNNBaseValidator::NS = 1; - -template<> -std::vector -RNNBaseValidator::def_acts = {"tanh"}; -template<> -std::vector -RNNBaseValidator::def_alpha = {0}; -template<> -std::vector -RNNBaseValidator::def_beta = {0}; -template<> -size_t -RNNBaseValidator::G = 1; -template<> -size_t -RNNBaseValidator::NS = 1; - -template -RNNBaseValidator::RNNBaseValidator(const std::string& _type) : LayerValidator(_type) {} +RNNBaseValidator::RNNBaseValidator(const std::string& _type, RNNSequenceLayer::CellType CELL) : LayerValidator(_type) { + if (RNNSequenceLayer::LSTM == CELL) { + def_acts = {"sigmoid", "tanh", "tanh"}; + def_alpha = {0, 0, 0}; + def_beta = {0, 0, 0}; + G = 4; + NS = 2; + } else if (RNNSequenceLayer::GRU == CELL) { + def_acts = {"sigmoid", "tanh"}; + def_alpha = {0, 0}; + def_beta = {0, 0}; + G = 3; + NS = 1; + } else if (RNNSequenceLayer::RNN == CELL) { + def_acts = {"tanh"}; + def_alpha = {0}; + def_beta = {0}; + G = 1; + NS = 1; + } else { + IE_ASSERT(false); + } +} -template -void RNNBaseValidator::parseParams(CNNLayer* layer) { +void RNNBaseValidator::parseParams(CNNLayer* layer) { auto rnn = dynamic_cast(layer); if (!rnn) THROW_IE_EXCEPTION << "Layer is not instance of RNNLayer class"; @@ -1735,8 +1747,7 @@ void RNNBaseValidator::parseParams(CNNLayer* layer) { } } -template -void RNNBaseValidator::checkParams(const InferenceEngine::CNNLayer *layer) { +void RNNBaseValidator::checkParams(const InferenceEngine::CNNLayer *layer) { auto rnn = dynamic_cast(layer); if (!rnn) THROW_IE_EXCEPTION << "Layer is not instance of RNNLayer class"; @@ -1761,8 +1772,7 @@ void RNNBaseValidator::checkParams(const InferenceEngine::CNNLayer *layer) << "but provided " << rnn->activation_beta.size(); } -template -void RNNBaseValidator::checkCorrespondence(const CNNLayer* layer, +void RNNBaseValidator::checkCorrespondence(const CNNLayer* layer, const map& blobs, const vector& inShapes) const { auto rnn = dynamic_cast(layer); @@ -1799,11 +1809,11 @@ void RNNBaseValidator::checkCorrespondence(const CNNLayer* layer, } template -RNNSequenceValidator::RNNSequenceValidator(const std::string& _type) : RNNBaseValidator(_type) {} +RNNSequenceValidator::RNNSequenceValidator(const std::string& _type) : RNNBaseValidator(_type, CELL) {} template void RNNSequenceValidator::parseParams(CNNLayer* layer) { - RNNBaseValidator::parseParams(layer); + RNNBaseValidator::parseParams(layer); auto casted = dynamic_cast(layer); if (!casted) @@ -1817,7 +1827,7 @@ void RNNSequenceValidator::parseParams(CNNLayer* layer) { template void RNNSequenceValidator::checkParams(const InferenceEngine::CNNLayer *layer) { - RNNBaseValidator::checkParams(layer); + RNNBaseValidator::checkParams(layer); auto casted = dynamic_cast(layer); if (!casted) @@ -1873,7 +1883,7 @@ template class details::RNNSequenceValidator; template class details::RNNSequenceValidator; template -RNNCellValidator::RNNCellValidator(const std::string& _type) : RNNBaseValidator(_type) {} +RNNCellValidator::RNNCellValidator(const std::string& _type) : RNNBaseValidator(_type, CELL) {} template void RNNCellValidator::checkShapes(const CNNLayer* layer, const vector& inShapes) const { @@ -2739,4 +2749,242 @@ void TopKValidator::checkShapes(const CNNLayer* layer, const vector& THROW_IE_EXCEPTION << layer->name << " TopK can take only 2 inputs, but actually it has: " << numInputs; } + +UniqueValidator::UniqueValidator(const std::string& _type) : LayerValidator(_type) {} + +void UniqueValidator::parseParams(CNNLayer* layer) { + auto casted = dynamic_cast(layer); + if (!casted) { + THROW_IE_EXCEPTION << layer->name << " Layer is not instance of Unique class"; + } + + casted->sorted = layer->GetParamAsBool("sorted"); + casted->return_inverse = layer->GetParamAsBool("return_inverse"); + casted->return_counts = layer->GetParamAsBool("return_counts"); +} + +void UniqueValidator::checkShapes(const CNNLayer* layer, const vector& inShapes) const { + size_t numInputs = inShapes.size(); + if (numInputs != 1) + THROW_IE_EXCEPTION << layer->name << " Unique can take only 1 input, but actually it has: " << numInputs; +} + + +NMSValidator::NMSValidator(const std::string& _type) : LayerValidator(_type) {} + +void NMSValidator::parseParams(CNNLayer* layer) { + auto casted = dynamic_cast(layer); + if (!casted) { + THROW_IE_EXCEPTION << layer->name << " Layer is not instance of NonMaxSuppression class"; + } + + casted->center_point_box = layer->GetParamAsBool("center_point_box", false); +} + +void NMSValidator::checkParams(const CNNLayer* layer) { + LayerValidator::checkParams(layer); +} + +void NMSValidator::checkShapes(const CNNLayer* layer, const vector& inShapes) const { + size_t numInputs = inShapes.size(); + if (numInputs < 2 || numInputs > 5) + THROW_IE_EXCEPTION << layer->name << " NonMaxSuppression can take 2 - 5 inputs, but actually it has: " << numInputs; + + if (inShapes[0].size() != 3 || inShapes[0][2] != 4) + THROW_IE_EXCEPTION << layer->name << " 'boxes' should be with shape [num_batches, spatial_dimension, 4]"; + + if (inShapes[1].size() != 3) + THROW_IE_EXCEPTION << layer->name << " 'scores' should be with shape [num_batches, num_classes, spatial_dimension]"; + + if (inShapes[0][0] != inShapes[1][0]) + THROW_IE_EXCEPTION << layer->name << " num_batches is different in 'boxes' and 'scores' tensors"; + + if (inShapes[0][1] != inShapes[1][2]) + THROW_IE_EXCEPTION << layer->name << " spatial_dimension is different in 'boxes' and 'scores' tensors"; + + if (numInputs > 2 && !(inShapes[2].size() == 1 && inShapes[2][0] == 1)) + THROW_IE_EXCEPTION << layer->name << " 'max_output_boxes_per_class' should be scalar"; + + if (numInputs > 3 && !(inShapes[3].size() == 1 && inShapes[3][0] == 1)) + THROW_IE_EXCEPTION << layer->name << " 'iou_threshold' should be scalar"; + + if (numInputs > 4 && !(inShapes[4].size() == 1 && inShapes[4][0] == 1)) + THROW_IE_EXCEPTION << layer->name << " 'score_threshold' should be scalar"; +} + + +ScatterValidator::ScatterValidator(const std::string& _type) : LayerValidator(_type) {} + +void ScatterValidator::parseParams(CNNLayer* layer) { + auto casted = dynamic_cast(layer); + if (!casted) { + THROW_IE_EXCEPTION << layer->name << " Layer is not instance of ScatterLayer class"; + } + + casted->axis = casted->GetParamAsInt("axis", 0); +} + +void ScatterValidator::checkShapes(const CNNLayer* layer, const vector& inShapes) const { + auto casted = dynamic_cast(layer); + if (!casted) { + THROW_IE_EXCEPTION << layer->name << " Layer is not instance of ScatterLayer class"; + } + + size_t numInputs = inShapes.size(); + if (numInputs != 3) + THROW_IE_EXCEPTION << layer->name << " Scatter can take only 3 inputs, but actually it has: " << numInputs; + + if (!(-static_cast(inShapes[0].size()) <= casted->axis && casted->axis < static_cast(inShapes[0].size()))) + THROW_IE_EXCEPTION << layer->name << " Incorrect input parameters dimensions and axis number!"; + + if (inShapes[0].size() == 0 || (inShapes[0].size() == 1 && inShapes[0][0] == 1)) + THROW_IE_EXCEPTION << layer->name << " 'Data' tensor rank should be >= 1"; + + if (inShapes[1].size() == 0 || (inShapes[1].size() == 1 && inShapes[1][0] == 1)) + THROW_IE_EXCEPTION << layer->name << " 'Indexes' tensor rank should be >= 1"; + + if (inShapes[1].size() == 0 || (inShapes[1].size() == 1 && inShapes[1][0] == 1)) + THROW_IE_EXCEPTION << layer->name << " 'Updates' tensor rank should be >= 1"; + + if (inShapes[1] != inShapes[2]) + THROW_IE_EXCEPTION << layer->name << " Incorrect number of 'indexes' and 'updates' tensors dimension"; + + const size_t SCATTER_DATA = 0; + const size_t SCATTER_INDEXES = 1; + const size_t SCATTER_UPDATES = 2; + + Precision inIdxPrecision = layer->insData[SCATTER_INDEXES].lock()->getTensorDesc().getPrecision(); + if (inIdxPrecision != Precision::FP32 && inIdxPrecision != Precision::I32) + THROW_IE_EXCEPTION << layer->name << " Incorrect input 'Indexes' precision. Only FP32 or I32 are supported!"; + + if (layer->insData[SCATTER_DATA].lock()->getTensorDesc().getPrecision() != + layer->insData[SCATTER_UPDATES].lock()->getTensorDesc().getPrecision()) + THROW_IE_EXCEPTION << layer->name << " Precision should be equal for input tensors 'Data' and 'Updates'"; +} + + +#define REG_LAYER_VALIDATOR_FOR_TYPE(__validator, __type) \ +_validators[#__type] = std::make_shared<__validator>(#__type) + +LayerValidators::LayerValidators() { + REG_LAYER_VALIDATOR_FOR_TYPE(ActivationValidator, Activation); + REG_LAYER_VALIDATOR_FOR_TYPE(ArgMaxValidator, ArgMax); + REG_LAYER_VALIDATOR_FOR_TYPE(BatchNormalizationValidator, BatchNormalization); + REG_LAYER_VALIDATOR_FOR_TYPE(CTCGreedyDecoderValidator, CTCGreedyDecoder); + REG_LAYER_VALIDATOR_FOR_TYPE(ClampValidator, Clamp); + REG_LAYER_VALIDATOR_FOR_TYPE(ConcatValidator, Concat); + REG_LAYER_VALIDATOR_FOR_TYPE(ConstValidator, Const); + REG_LAYER_VALIDATOR_FOR_TYPE(ConvolutionValidator, Convolution); + REG_LAYER_VALIDATOR_FOR_TYPE(CopyValidator, Copy); + REG_LAYER_VALIDATOR_FOR_TYPE(CropValidator, Crop); + REG_LAYER_VALIDATOR_FOR_TYPE(DeconvolutionValidator, Deconvolution); + REG_LAYER_VALIDATOR_FOR_TYPE(DeformableConvolutionValidator, DeformableConvolution); + REG_LAYER_VALIDATOR_FOR_TYPE(DetectionOutputValidator, DetectionOutput); + REG_LAYER_VALIDATOR_FOR_TYPE(ELUValidator, ELU); + REG_LAYER_VALIDATOR_FOR_TYPE(EltwiseValidator, Eltwise); + REG_LAYER_VALIDATOR_FOR_TYPE(FullyConnectedValidator, InnerProduct); + REG_LAYER_VALIDATOR_FOR_TYPE(FullyConnectedValidator, FullyConnected); + REG_LAYER_VALIDATOR_FOR_TYPE(GRNValidator, GRN); + REG_LAYER_VALIDATOR_FOR_TYPE(InputValidator, Input); + REG_LAYER_VALIDATOR_FOR_TYPE(InterpValidator, Interp); + REG_LAYER_VALIDATOR_FOR_TYPE(MVNValidator, MVN); + REG_LAYER_VALIDATOR_FOR_TYPE(MemoryValidator, Memory); + REG_LAYER_VALIDATOR_FOR_TYPE(NormValidator, Norm); + REG_LAYER_VALIDATOR_FOR_TYPE(NormValidator, LRN); + REG_LAYER_VALIDATOR_FOR_TYPE(NormalizeValidator, Normalize); + REG_LAYER_VALIDATOR_FOR_TYPE(PReLUValidator, PReLU); + REG_LAYER_VALIDATOR_FOR_TYPE(PSROIPoolingValidator, PSROIPooling); + REG_LAYER_VALIDATOR_FOR_TYPE(PermuteValidator, Permute); + REG_LAYER_VALIDATOR_FOR_TYPE(PoolingValidator, Pooling); + REG_LAYER_VALIDATOR_FOR_TYPE(PowerValidator, Power); + REG_LAYER_VALIDATOR_FOR_TYPE(PowerFileValidator, PowerFile); + REG_LAYER_VALIDATOR_FOR_TYPE(PriorBoxClusteredValidator, PriorBoxClustered); + REG_LAYER_VALIDATOR_FOR_TYPE(PriorBoxValidator, PriorBox); + REG_LAYER_VALIDATOR_FOR_TYPE(ProposalValidator, Proposal); + REG_LAYER_VALIDATOR_FOR_TYPE(ROIPoolingValidator, ROIPooling); + REG_LAYER_VALIDATOR_FOR_TYPE(ReLUValidator, ReLU); + REG_LAYER_VALIDATOR_FOR_TYPE(ReLU6Validator, ReLU6); + REG_LAYER_VALIDATOR_FOR_TYPE(RegionYoloValidator, RegionYolo); + REG_LAYER_VALIDATOR_FOR_TYPE(ReorgYoloValidator, ReorgYolo); + REG_LAYER_VALIDATOR_FOR_TYPE(ResampleValidator, Resample); + REG_LAYER_VALIDATOR_FOR_TYPE(ReshapeValidator, Reshape); + REG_LAYER_VALIDATOR_FOR_TYPE(ReshapeValidator, Flatten); + REG_LAYER_VALIDATOR_FOR_TYPE(ScaleShiftValidator, ScaleShift); + REG_LAYER_VALIDATOR_FOR_TYPE(SigmoidValidator, Sigmoid); + REG_LAYER_VALIDATOR_FOR_TYPE(SigmoidValidator, Logistic); + REG_LAYER_VALIDATOR_FOR_TYPE(SimplerNMSValidator, SimplerNMS); + REG_LAYER_VALIDATOR_FOR_TYPE(SoftMaxValidator, SoftMax); + REG_LAYER_VALIDATOR_FOR_TYPE(SpatialTransformerValidator, SpatialTransformer); + REG_LAYER_VALIDATOR_FOR_TYPE(SplitValidator, Split); + REG_LAYER_VALIDATOR_FOR_TYPE(SplitValidator, Slice); + REG_LAYER_VALIDATOR_FOR_TYPE(GemmValidator, Gemm); + REG_LAYER_VALIDATOR_FOR_TYPE(PadValidator, Pad); + REG_LAYER_VALIDATOR_FOR_TYPE(GatherValidator, Gather); + REG_LAYER_VALIDATOR_FOR_TYPE(StridedSliceValidator, StridedSlice); + REG_LAYER_VALIDATOR_FOR_TYPE(ShuffleChannelsValidator, ShuffleChannels); + REG_LAYER_VALIDATOR_FOR_TYPE(DepthToSpaceValidator, DepthToSpace); + REG_LAYER_VALIDATOR_FOR_TYPE(SpaceToDepthValidator, SpaceToDepth); + REG_LAYER_VALIDATOR_FOR_TYPE(SparseFillEmptyRowsValidator, SparseFillEmptyRows); + REG_LAYER_VALIDATOR_FOR_TYPE(ReverseSequenceValidator, ReverseSequence); + REG_LAYER_VALIDATOR_FOR_TYPE(RNNCellValidator, RNNCell); + REG_LAYER_VALIDATOR_FOR_TYPE(RNNCellValidator, GRUCell); + REG_LAYER_VALIDATOR_FOR_TYPE(RNNCellValidator, LSTMCell); + REG_LAYER_VALIDATOR_FOR_TYPE(RNNSequenceValidator, RNNSequence); + REG_LAYER_VALIDATOR_FOR_TYPE(RNNSequenceValidator, GRUSequence); + REG_LAYER_VALIDATOR_FOR_TYPE(RNNSequenceValidator, LSTMSequence); + REG_LAYER_VALIDATOR_FOR_TYPE(SelectValidator, Select); + REG_LAYER_VALIDATOR_FOR_TYPE(SqueezeValidator, Squeeze); + REG_LAYER_VALIDATOR_FOR_TYPE(UnsqueezeValidator, Unsqueeze); + REG_LAYER_VALIDATOR_FOR_TYPE(RangeValidator, Range); + REG_LAYER_VALIDATOR_FOR_TYPE(FillValidator, Fill); + REG_LAYER_VALIDATOR_FOR_TYPE(BroadcastValidator, Broadcast); + REG_LAYER_VALIDATOR_FOR_TYPE(TanHValidator, TanH); + REG_LAYER_VALIDATOR_FOR_TYPE(TileValidator, Tile); + REG_LAYER_VALIDATOR_FOR_TYPE(UnpoolingValidator, Unpooling); + REG_LAYER_VALIDATOR_FOR_TYPE(UpsamplingValidator, Upsampling); + REG_LAYER_VALIDATOR_FOR_TYPE(OneHotValidator, OneHot); + REG_LAYER_VALIDATOR_FOR_TYPE(QuantizeValidator, Quantize); + REG_LAYER_VALIDATOR_FOR_TYPE(BinaryConvolutionValidator, BinaryConvolution); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Abs); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Acos); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Acosh); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Asin); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Asinh); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Atan); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Atanh); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Ceil); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Cos); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Cosh); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Erf); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Floor); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, HardSigmoid); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Log); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Neg); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Reciprocal); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Selu); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Sign); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Sin); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Sinh); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Softplus); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Softsign); + REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Tan); + REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceAnd); + REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceL1); + REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceL2); + REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceLogSum); + REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceLogSumExp); + REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceMax); + REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceMean); + REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceMin); + REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceOr); + REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceProd); + REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceSum); + REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceSumSquare); + REG_LAYER_VALIDATOR_FOR_TYPE(GatherTreeValidator, GatherTree); + REG_LAYER_VALIDATOR_FOR_TYPE(TopKValidator, TopK); + REG_LAYER_VALIDATOR_FOR_TYPE(UniqueValidator, Unique); + REG_LAYER_VALIDATOR_FOR_TYPE(NMSValidator, NonMaxSuppression); + REG_LAYER_VALIDATOR_FOR_TYPE(ScatterValidator, ScatterUpdate); +} + } // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/ie_layer_validators.hpp b/inference-engine/src/inference_engine/ie_layer_validators.hpp index 0072f01211380e..2a295a8d061cb1 100644 --- a/inference-engine/src/inference_engine/ie_layer_validators.hpp +++ b/inference-engine/src/inference_engine/ie_layer_validators.hpp @@ -22,11 +22,12 @@ struct InOutDims { /** * @brief Contains methods to validate layer of specific type */ -class INFERENCE_ENGINE_API_CLASS(LayerValidator) { +class LayerValidator { public: using Ptr = std::shared_ptr; explicit LayerValidator(const std::string& _type) : _type(_type) {} + virtual ~LayerValidator() = default; /** * @brief It parses map of params and applies to the layer's fields. @@ -65,7 +66,7 @@ class INFERENCE_ENGINE_API_CLASS(LayerValidator) { /** * @brief Contains all validators, registered for specific layer type */ -class INFERENCE_ENGINE_API_CLASS(LayerValidators) { +class LayerValidators { public: static LayerValidators* getInstance(); @@ -75,17 +76,15 @@ class INFERENCE_ENGINE_API_CLASS(LayerValidators) { LayerValidator::Ptr getValidator(const std::string& type); - void addImpl(const std::string& type, const LayerValidator::Ptr& validator); - private: - LayerValidators() = default; + LayerValidators(); private: static LayerValidators* _instance; InferenceEngine::details::caseless_unordered_map _validators; }; -static void getInOutShapes(const CNNLayer* layer, InOutDims& inOutShapes) { +inline static void getInOutShapes(const CNNLayer* layer, InOutDims& inOutShapes) { inOutShapes.inDims.clear(); inOutShapes.outDims.clear(); if (layer) { @@ -108,7 +107,7 @@ class GeneralValidator : public LayerValidator { explicit GeneralValidator(const std::string& _type); }; -class INFERENCE_ENGINE_API_CLASS(ConvolutionValidator) : public LayerValidator { +class ConvolutionValidator : public LayerValidator { public: void parseParams(CNNLayer* layer) override; @@ -123,7 +122,7 @@ class INFERENCE_ENGINE_API_CLASS(ConvolutionValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(DeconvolutionValidator) : public ConvolutionValidator { +class DeconvolutionValidator : public ConvolutionValidator { public: void parseParams(CNNLayer* layer) override; @@ -138,7 +137,7 @@ class INFERENCE_ENGINE_API_CLASS(DeconvolutionValidator) : public ConvolutionVal void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(DeformableConvolutionValidator) : public ConvolutionValidator { +class DeformableConvolutionValidator : public ConvolutionValidator { public: void parseParams(CNNLayer* layer) override; @@ -153,7 +152,7 @@ class INFERENCE_ENGINE_API_CLASS(DeformableConvolutionValidator) : public Convol void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(PoolingValidator) : public LayerValidator { +class PoolingValidator : public LayerValidator { public: void parseParams(CNNLayer* layer) override; @@ -164,7 +163,7 @@ class INFERENCE_ENGINE_API_CLASS(PoolingValidator) : public LayerValidator { explicit PoolingValidator(const std::string& _type); }; -class INFERENCE_ENGINE_API_CLASS(FullyConnectedValidator) : public LayerValidator { +class FullyConnectedValidator : public LayerValidator { public: explicit FullyConnectedValidator(const std::string& _type); @@ -179,7 +178,7 @@ class INFERENCE_ENGINE_API_CLASS(FullyConnectedValidator) : public LayerValidato void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(CropValidator) : public LayerValidator { +class CropValidator : public LayerValidator { public: explicit CropValidator(const std::string& _type); @@ -190,7 +189,7 @@ class INFERENCE_ENGINE_API_CLASS(CropValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(TileValidator) : public LayerValidator { +class TileValidator : public LayerValidator { public: explicit TileValidator(const std::string& _type); @@ -201,7 +200,7 @@ class INFERENCE_ENGINE_API_CLASS(TileValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(BatchNormalizationValidator) : public LayerValidator { +class BatchNormalizationValidator : public LayerValidator { public: explicit BatchNormalizationValidator(const std::string& _type); @@ -212,7 +211,7 @@ class INFERENCE_ENGINE_API_CLASS(BatchNormalizationValidator) : public LayerVali void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(PowerValidator) : public LayerValidator { +class PowerValidator : public LayerValidator { public: explicit PowerValidator(const std::string& _type); @@ -223,7 +222,7 @@ class INFERENCE_ENGINE_API_CLASS(PowerValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(PReLUValidator) : public LayerValidator { +class PReLUValidator : public LayerValidator { public: explicit PReLUValidator(const std::string& _type); @@ -234,7 +233,7 @@ class INFERENCE_ENGINE_API_CLASS(PReLUValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(ScaleShiftValidator) : public LayerValidator { +class ScaleShiftValidator : public LayerValidator { public: explicit ScaleShiftValidator(const std::string& _type); @@ -245,7 +244,7 @@ class INFERENCE_ENGINE_API_CLASS(ScaleShiftValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(ReshapeValidator) : public LayerValidator { +class ReshapeValidator : public LayerValidator { public: explicit ReshapeValidator(const std::string& _type); @@ -254,7 +253,7 @@ class INFERENCE_ENGINE_API_CLASS(ReshapeValidator) : public LayerValidator { void checkParams(const CNNLayer* layer) override; }; -class INFERENCE_ENGINE_API_CLASS(EltwiseValidator) : public LayerValidator { +class EltwiseValidator : public LayerValidator { public: explicit EltwiseValidator(const std::string& _type); @@ -265,7 +264,7 @@ class INFERENCE_ENGINE_API_CLASS(EltwiseValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(ClampValidator) : public LayerValidator { +class ClampValidator : public LayerValidator { public: explicit ClampValidator(const std::string& _type); @@ -274,7 +273,7 @@ class INFERENCE_ENGINE_API_CLASS(ClampValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(ReLUValidator) : public LayerValidator { +class ReLUValidator : public LayerValidator { public: explicit ReLUValidator(const std::string& _type); @@ -285,7 +284,7 @@ class INFERENCE_ENGINE_API_CLASS(ReLUValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(MVNValidator) : public LayerValidator { +class MVNValidator : public LayerValidator { public: explicit MVNValidator(const std::string& _type); @@ -296,7 +295,7 @@ class INFERENCE_ENGINE_API_CLASS(MVNValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(GRNValidator) : public LayerValidator { +class GRNValidator : public LayerValidator { public: explicit GRNValidator(const std::string& _type); @@ -307,7 +306,7 @@ class INFERENCE_ENGINE_API_CLASS(GRNValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(SoftMaxValidator) : public LayerValidator { +class SoftMaxValidator : public LayerValidator { public: explicit SoftMaxValidator(const std::string& _type); @@ -318,7 +317,7 @@ class INFERENCE_ENGINE_API_CLASS(SoftMaxValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(NormValidator) : public LayerValidator { +class NormValidator : public LayerValidator { public: explicit NormValidator(const std::string& _type); @@ -329,7 +328,7 @@ class INFERENCE_ENGINE_API_CLASS(NormValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(SplitValidator) : public LayerValidator { +class SplitValidator : public LayerValidator { public: explicit SplitValidator(const std::string& _type); @@ -340,7 +339,7 @@ class INFERENCE_ENGINE_API_CLASS(SplitValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(ConcatValidator) : public LayerValidator { +class ConcatValidator : public LayerValidator { public: explicit ConcatValidator(const std::string& _type); @@ -351,7 +350,7 @@ class INFERENCE_ENGINE_API_CLASS(ConcatValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(GemmValidator) : public LayerValidator { +class GemmValidator : public LayerValidator { public: explicit GemmValidator(const std::string& _type); @@ -362,7 +361,7 @@ class INFERENCE_ENGINE_API_CLASS(GemmValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(PadValidator) : public LayerValidator { +class PadValidator : public LayerValidator { public: explicit PadValidator(const std::string& _type); @@ -373,7 +372,7 @@ class INFERENCE_ENGINE_API_CLASS(PadValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(GatherValidator) : public LayerValidator { +class GatherValidator : public LayerValidator { public: explicit GatherValidator(const std::string& _type); @@ -384,7 +383,7 @@ class INFERENCE_ENGINE_API_CLASS(GatherValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(StridedSliceValidator) : public LayerValidator { +class StridedSliceValidator : public LayerValidator { public: explicit StridedSliceValidator(const std::string& _type); @@ -395,7 +394,7 @@ class INFERENCE_ENGINE_API_CLASS(StridedSliceValidator) : public LayerValidator void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(ShuffleChannelsValidator) : public LayerValidator { +class ShuffleChannelsValidator : public LayerValidator { public: explicit ShuffleChannelsValidator(const std::string& _type); @@ -406,7 +405,7 @@ class INFERENCE_ENGINE_API_CLASS(ShuffleChannelsValidator) : public LayerValidat void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(DepthToSpaceValidator) : public LayerValidator { +class DepthToSpaceValidator : public LayerValidator { public: explicit DepthToSpaceValidator(const std::string& _type); @@ -417,7 +416,7 @@ class INFERENCE_ENGINE_API_CLASS(DepthToSpaceValidator) : public LayerValidator void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(SpaceToDepthValidator) : public LayerValidator { +class SpaceToDepthValidator : public LayerValidator { public: explicit SpaceToDepthValidator(const std::string& _type); @@ -428,7 +427,18 @@ class INFERENCE_ENGINE_API_CLASS(SpaceToDepthValidator) : public LayerValidator void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(ReverseSequenceValidator) : public LayerValidator { +class SparseFillEmptyRowsValidator : public LayerValidator { +public: + explicit SparseFillEmptyRowsValidator(const std::string& _type); + + void parseParams(CNNLayer* layer) override; + + void checkParams(const CNNLayer* layer) override; + + void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; +}; + +class ReverseSequenceValidator : public LayerValidator { public: explicit ReverseSequenceValidator(const std::string& _type); @@ -439,7 +449,7 @@ class INFERENCE_ENGINE_API_CLASS(ReverseSequenceValidator) : public LayerValidat void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(SqueezeValidator) : public LayerValidator { +class SqueezeValidator : public LayerValidator { public: explicit SqueezeValidator(const std::string& _type); @@ -450,7 +460,7 @@ class INFERENCE_ENGINE_API_CLASS(SqueezeValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(UnsqueezeValidator) : public LayerValidator { +class UnsqueezeValidator : public LayerValidator { public: explicit UnsqueezeValidator(const std::string& _type); @@ -461,7 +471,7 @@ class INFERENCE_ENGINE_API_CLASS(UnsqueezeValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(RangeValidator) : public LayerValidator { +class RangeValidator : public LayerValidator { public: explicit RangeValidator(const std::string& _type); @@ -472,7 +482,7 @@ class INFERENCE_ENGINE_API_CLASS(RangeValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(FillValidator) : public LayerValidator { +class FillValidator : public LayerValidator { public: explicit FillValidator(const std::string& _type); @@ -483,7 +493,7 @@ class INFERENCE_ENGINE_API_CLASS(FillValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(BroadcastValidator) : public LayerValidator { +class BroadcastValidator : public LayerValidator { public: explicit BroadcastValidator(const std::string& _type); @@ -494,10 +504,9 @@ class INFERENCE_ENGINE_API_CLASS(BroadcastValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -template -class INFERENCE_ENGINE_API_CLASS(RNNBaseValidator) : public LayerValidator { +class RNNBaseValidator : public LayerValidator { public: - explicit RNNBaseValidator(const std::string& _type); + RNNBaseValidator(const std::string& _type, RNNSequenceLayer::CellType CELL); void parseParams(CNNLayer* layer) override; @@ -508,27 +517,27 @@ class INFERENCE_ENGINE_API_CLASS(RNNBaseValidator) : public LayerValidator { const std::vector& inShapes) const override; protected: - static std::vector def_acts; // Default values for cell gate activations - static std::vector def_alpha; // Default activation alpha parameter - static std::vector def_beta; // Default activation beta parameter - static size_t G; // gate number - static size_t NS; // state number + std::vector def_acts; // Default values for cell gate activations + std::vector def_alpha; // Default activation alpha parameter + std::vector def_beta; // Default activation beta parameter + size_t G; // gate number + size_t NS; // state number }; template -class INFERENCE_ENGINE_API_CLASS(RNNCellValidator) : public RNNBaseValidator { +class RNNCellValidator : public RNNBaseValidator { public: explicit RNNCellValidator(const std::string& _type); void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -extern template class INFERENCE_ENGINE_API_CLASS(RNNCellValidator); -extern template class INFERENCE_ENGINE_API_CLASS(RNNCellValidator); -extern template class INFERENCE_ENGINE_API_CLASS(RNNCellValidator); +extern template class RNNCellValidator; +extern template class RNNCellValidator; +extern template class RNNCellValidator; template -class INFERENCE_ENGINE_API_CLASS(RNNSequenceValidator) : public RNNBaseValidator { +class RNNSequenceValidator : public RNNBaseValidator { public: explicit RNNSequenceValidator(const std::string& _type); @@ -539,11 +548,11 @@ class INFERENCE_ENGINE_API_CLASS(RNNSequenceValidator) : public RNNBaseValidator void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -extern template class INFERENCE_ENGINE_API_CLASS(RNNSequenceValidator); -extern template class INFERENCE_ENGINE_API_CLASS(RNNSequenceValidator); -extern template class INFERENCE_ENGINE_API_CLASS(RNNSequenceValidator); +extern template class RNNSequenceValidator; +extern template class RNNSequenceValidator; +extern template class RNNSequenceValidator; -class INFERENCE_ENGINE_API_CLASS(ArgMaxValidator) : public LayerValidator { +class ArgMaxValidator : public LayerValidator { public: explicit ArgMaxValidator(const std::string& _type); @@ -552,7 +561,7 @@ class INFERENCE_ENGINE_API_CLASS(ArgMaxValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(CTCGreedyDecoderValidator) : public LayerValidator { +class CTCGreedyDecoderValidator : public LayerValidator { public: explicit CTCGreedyDecoderValidator(const std::string& _type); @@ -561,7 +570,7 @@ class INFERENCE_ENGINE_API_CLASS(CTCGreedyDecoderValidator) : public LayerValida void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(DetectionOutputValidator) : public LayerValidator { +class DetectionOutputValidator : public LayerValidator { public: explicit DetectionOutputValidator(const std::string& _type); @@ -572,7 +581,7 @@ class INFERENCE_ENGINE_API_CLASS(DetectionOutputValidator) : public LayerValidat void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(InterpValidator) : public LayerValidator { +class InterpValidator : public LayerValidator { public: explicit InterpValidator(const std::string& _type); @@ -583,7 +592,7 @@ class INFERENCE_ENGINE_API_CLASS(InterpValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(PermuteValidator) : public LayerValidator { +class PermuteValidator : public LayerValidator { public: explicit PermuteValidator(const std::string& _type); @@ -592,7 +601,7 @@ class INFERENCE_ENGINE_API_CLASS(PermuteValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(PriorBoxValidator) : public LayerValidator { +class PriorBoxValidator : public LayerValidator { public: explicit PriorBoxValidator(const std::string& _type); @@ -601,7 +610,7 @@ class INFERENCE_ENGINE_API_CLASS(PriorBoxValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(PriorBoxClusteredValidator) : public LayerValidator { +class PriorBoxClusteredValidator : public LayerValidator { public: explicit PriorBoxClusteredValidator(const std::string& _type); @@ -610,7 +619,7 @@ class INFERENCE_ENGINE_API_CLASS(PriorBoxClusteredValidator) : public LayerValid void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(ProposalValidator) : public LayerValidator { +class ProposalValidator : public LayerValidator { public: explicit ProposalValidator(const std::string& _type); @@ -619,7 +628,7 @@ class INFERENCE_ENGINE_API_CLASS(ProposalValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(PSROIPoolingValidator) : public LayerValidator { +class PSROIPoolingValidator : public LayerValidator { public: explicit PSROIPoolingValidator(const std::string& _type); @@ -628,7 +637,7 @@ class INFERENCE_ENGINE_API_CLASS(PSROIPoolingValidator) : public LayerValidator void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(RegionYoloValidator) : public LayerValidator { +class RegionYoloValidator : public LayerValidator { public: explicit RegionYoloValidator(const std::string& _type); @@ -637,7 +646,7 @@ class INFERENCE_ENGINE_API_CLASS(RegionYoloValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(ReorgYoloValidator) : public LayerValidator { +class ReorgYoloValidator : public LayerValidator { public: explicit ReorgYoloValidator(const std::string& _type); @@ -646,7 +655,7 @@ class INFERENCE_ENGINE_API_CLASS(ReorgYoloValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(ResampleValidator) : public LayerValidator { +class ResampleValidator : public LayerValidator { public: explicit ResampleValidator(const std::string& _type); @@ -655,7 +664,7 @@ class INFERENCE_ENGINE_API_CLASS(ResampleValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(ROIPoolingValidator) : public LayerValidator { +class ROIPoolingValidator : public LayerValidator { public: explicit ROIPoolingValidator(const std::string& _type); @@ -664,7 +673,7 @@ class INFERENCE_ENGINE_API_CLASS(ROIPoolingValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(SimplerNMSValidator) : public LayerValidator { +class SimplerNMSValidator : public LayerValidator { public: explicit SimplerNMSValidator(const std::string& _type); @@ -673,7 +682,7 @@ class INFERENCE_ENGINE_API_CLASS(SimplerNMSValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(SpatialTransformerValidator) : public LayerValidator { +class SpatialTransformerValidator : public LayerValidator { public: explicit SpatialTransformerValidator(const std::string& _type); @@ -682,7 +691,7 @@ class INFERENCE_ENGINE_API_CLASS(SpatialTransformerValidator) : public LayerVali void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(OneHotValidator) : public LayerValidator { +class OneHotValidator : public LayerValidator { public: explicit OneHotValidator(const std::string& _type); @@ -693,7 +702,7 @@ class INFERENCE_ENGINE_API_CLASS(OneHotValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(UpsamplingValidator) : public LayerValidator { +class UpsamplingValidator : public LayerValidator { public: explicit UpsamplingValidator(const std::string& _type); @@ -702,7 +711,7 @@ class INFERENCE_ENGINE_API_CLASS(UpsamplingValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(ActivationValidator) : public LayerValidator { +class ActivationValidator : public LayerValidator { public: explicit ActivationValidator(const std::string& _type); @@ -711,7 +720,7 @@ class INFERENCE_ENGINE_API_CLASS(ActivationValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(ConstValidator) : public LayerValidator { +class ConstValidator : public LayerValidator { public: explicit ConstValidator(const std::string& _type); @@ -720,7 +729,7 @@ class INFERENCE_ENGINE_API_CLASS(ConstValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(ELUValidator) : public LayerValidator { +class ELUValidator : public LayerValidator { public: explicit ELUValidator(const std::string& _type); @@ -729,7 +738,7 @@ class INFERENCE_ENGINE_API_CLASS(ELUValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(InputValidator) : public LayerValidator { +class InputValidator : public LayerValidator { public: explicit InputValidator(const std::string& _type); @@ -738,7 +747,7 @@ class INFERENCE_ENGINE_API_CLASS(InputValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(MemoryValidator) : public LayerValidator { +class MemoryValidator : public LayerValidator { public: explicit MemoryValidator(const std::string& _type); @@ -747,7 +756,7 @@ class INFERENCE_ENGINE_API_CLASS(MemoryValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(NormalizeValidator) : public LayerValidator { +class NormalizeValidator : public LayerValidator { public: explicit NormalizeValidator(const std::string& _type); @@ -756,7 +765,7 @@ class INFERENCE_ENGINE_API_CLASS(NormalizeValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(CopyValidator) : public LayerValidator { +class CopyValidator : public LayerValidator { public: explicit CopyValidator(const std::string& _type); @@ -765,7 +774,7 @@ class INFERENCE_ENGINE_API_CLASS(CopyValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(PowerFileValidator) : public LayerValidator { +class PowerFileValidator : public LayerValidator { public: explicit PowerFileValidator(const std::string& _type); @@ -774,7 +783,7 @@ class INFERENCE_ENGINE_API_CLASS(PowerFileValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(ReLU6Validator) : public LayerValidator { +class ReLU6Validator : public LayerValidator { public: explicit ReLU6Validator(const std::string& _type); @@ -783,7 +792,7 @@ class INFERENCE_ENGINE_API_CLASS(ReLU6Validator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(SigmoidValidator) : public LayerValidator { +class SigmoidValidator : public LayerValidator { public: explicit SigmoidValidator(const std::string& _type); @@ -792,14 +801,14 @@ class INFERENCE_ENGINE_API_CLASS(SigmoidValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(TanHValidator) : public LayerValidator { +class TanHValidator : public LayerValidator { public: explicit TanHValidator(const std::string& _type); void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(UnpoolingValidator) : public LayerValidator { +class UnpoolingValidator : public LayerValidator { public: explicit UnpoolingValidator(const std::string& _type); @@ -808,7 +817,7 @@ class INFERENCE_ENGINE_API_CLASS(UnpoolingValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(QuantizeValidator) : public LayerValidator { +class QuantizeValidator : public LayerValidator { public: explicit QuantizeValidator(const std::string& _type); @@ -819,7 +828,7 @@ class INFERENCE_ENGINE_API_CLASS(QuantizeValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(BinaryConvolutionValidator) : public LayerValidator { +class BinaryConvolutionValidator : public LayerValidator { public: void parseParams(CNNLayer* layer) override; @@ -834,21 +843,21 @@ class INFERENCE_ENGINE_API_CLASS(BinaryConvolutionValidator) : public LayerValid void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(SelectValidator) : public LayerValidator { +class SelectValidator : public LayerValidator { public: explicit SelectValidator(const std::string& _type); void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(MathValidator) : public LayerValidator { +class MathValidator : public LayerValidator { public: explicit MathValidator(const std::string& _type); void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(ReduceValidator) : public LayerValidator { +class ReduceValidator : public LayerValidator { public: explicit ReduceValidator(const std::string& _type); @@ -859,7 +868,7 @@ class INFERENCE_ENGINE_API_CLASS(ReduceValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(GatherTreeValidator) : public LayerValidator { +class GatherTreeValidator : public LayerValidator { public: explicit GatherTreeValidator(const std::string& _type); @@ -870,7 +879,7 @@ class INFERENCE_ENGINE_API_CLASS(GatherTreeValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -class INFERENCE_ENGINE_API_CLASS(TopKValidator) : public LayerValidator { +class TopKValidator : public LayerValidator { public: explicit TopKValidator(const std::string& _type); @@ -879,130 +888,34 @@ class INFERENCE_ENGINE_API_CLASS(TopKValidator) : public LayerValidator { void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -template -class ValidatorRegisterBase { +class UniqueValidator : public LayerValidator { public: - explicit ValidatorRegisterBase(const std::string& type) { - LayerValidators::getInstance()->addImpl(type, std::make_shared(type)); - } + explicit UniqueValidator(const std::string& _type); + + void parseParams(CNNLayer* layer) override; + + void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; +}; + +class NMSValidator : public LayerValidator { +public: + explicit NMSValidator(const std::string& _type); + + void parseParams(CNNLayer* layer) override; + + void checkParams(const CNNLayer* layer) override; + + void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; +}; + +class ScatterValidator : public LayerValidator { +public: + explicit ScatterValidator(const std::string& _type); + + void parseParams(CNNLayer* layer) override; + + void checkShapes(const CNNLayer* layer, const std::vector& inShapes) const override; }; -#define REG_LAYER_VALIDATOR_FOR_TYPE(__validator, __type) \ -static ValidatorRegisterBase<__validator> __reg__##__type(#__type) - -REG_LAYER_VALIDATOR_FOR_TYPE(ActivationValidator, Activation); -REG_LAYER_VALIDATOR_FOR_TYPE(ArgMaxValidator, ArgMax); -REG_LAYER_VALIDATOR_FOR_TYPE(BatchNormalizationValidator, BatchNormalization); -REG_LAYER_VALIDATOR_FOR_TYPE(CTCGreedyDecoderValidator, CTCGreedyDecoder); -REG_LAYER_VALIDATOR_FOR_TYPE(ClampValidator, Clamp); -REG_LAYER_VALIDATOR_FOR_TYPE(ConcatValidator, Concat); -REG_LAYER_VALIDATOR_FOR_TYPE(ConstValidator, Const); -REG_LAYER_VALIDATOR_FOR_TYPE(ConvolutionValidator, Convolution); -REG_LAYER_VALIDATOR_FOR_TYPE(CopyValidator, Copy); -REG_LAYER_VALIDATOR_FOR_TYPE(CropValidator, Crop); -REG_LAYER_VALIDATOR_FOR_TYPE(DeconvolutionValidator, Deconvolution); -REG_LAYER_VALIDATOR_FOR_TYPE(DeformableConvolutionValidator, DeformableConvolution); -REG_LAYER_VALIDATOR_FOR_TYPE(DetectionOutputValidator, DetectionOutput); -REG_LAYER_VALIDATOR_FOR_TYPE(ELUValidator, ELU); -REG_LAYER_VALIDATOR_FOR_TYPE(EltwiseValidator, Eltwise); -REG_LAYER_VALIDATOR_FOR_TYPE(FullyConnectedValidator, InnerProduct); -REG_LAYER_VALIDATOR_FOR_TYPE(FullyConnectedValidator, FullyConnected); -REG_LAYER_VALIDATOR_FOR_TYPE(GRNValidator, GRN); -REG_LAYER_VALIDATOR_FOR_TYPE(InputValidator, Input); -REG_LAYER_VALIDATOR_FOR_TYPE(InterpValidator, Interp); -REG_LAYER_VALIDATOR_FOR_TYPE(MVNValidator, MVN); -REG_LAYER_VALIDATOR_FOR_TYPE(MemoryValidator, Memory); -REG_LAYER_VALIDATOR_FOR_TYPE(NormValidator, Norm); -REG_LAYER_VALIDATOR_FOR_TYPE(NormValidator, LRN); -REG_LAYER_VALIDATOR_FOR_TYPE(NormalizeValidator, Normalize); -REG_LAYER_VALIDATOR_FOR_TYPE(PReLUValidator, PReLU); -REG_LAYER_VALIDATOR_FOR_TYPE(PSROIPoolingValidator, PSROIPooling); -REG_LAYER_VALIDATOR_FOR_TYPE(PermuteValidator, Permute); -REG_LAYER_VALIDATOR_FOR_TYPE(PoolingValidator, Pooling); -REG_LAYER_VALIDATOR_FOR_TYPE(PowerValidator, Power); -REG_LAYER_VALIDATOR_FOR_TYPE(PowerFileValidator, PowerFile); -REG_LAYER_VALIDATOR_FOR_TYPE(PriorBoxClusteredValidator, PriorBoxClustered); -REG_LAYER_VALIDATOR_FOR_TYPE(PriorBoxValidator, PriorBox); -REG_LAYER_VALIDATOR_FOR_TYPE(ProposalValidator, Proposal); -REG_LAYER_VALIDATOR_FOR_TYPE(ROIPoolingValidator, ROIPooling); -REG_LAYER_VALIDATOR_FOR_TYPE(ReLUValidator, ReLU); -REG_LAYER_VALIDATOR_FOR_TYPE(ReLU6Validator, ReLU6); -REG_LAYER_VALIDATOR_FOR_TYPE(RegionYoloValidator, RegionYolo); -REG_LAYER_VALIDATOR_FOR_TYPE(ReorgYoloValidator, ReorgYolo); -REG_LAYER_VALIDATOR_FOR_TYPE(ResampleValidator, Resample); -REG_LAYER_VALIDATOR_FOR_TYPE(ReshapeValidator, Reshape); -REG_LAYER_VALIDATOR_FOR_TYPE(ReshapeValidator, Flatten); -REG_LAYER_VALIDATOR_FOR_TYPE(ScaleShiftValidator, ScaleShift); -REG_LAYER_VALIDATOR_FOR_TYPE(SigmoidValidator, Sigmoid); -REG_LAYER_VALIDATOR_FOR_TYPE(SigmoidValidator, Logistic); -REG_LAYER_VALIDATOR_FOR_TYPE(SimplerNMSValidator, SimplerNMS); -REG_LAYER_VALIDATOR_FOR_TYPE(SoftMaxValidator, SoftMax); -REG_LAYER_VALIDATOR_FOR_TYPE(SpatialTransformerValidator, SpatialTransformer); -REG_LAYER_VALIDATOR_FOR_TYPE(SplitValidator, Split); -REG_LAYER_VALIDATOR_FOR_TYPE(SplitValidator, Slice); -REG_LAYER_VALIDATOR_FOR_TYPE(GemmValidator, Gemm); -REG_LAYER_VALIDATOR_FOR_TYPE(PadValidator, Pad); -REG_LAYER_VALIDATOR_FOR_TYPE(GatherValidator, Gather); -REG_LAYER_VALIDATOR_FOR_TYPE(StridedSliceValidator, StridedSlice); -REG_LAYER_VALIDATOR_FOR_TYPE(ShuffleChannelsValidator, ShuffleChannels); -REG_LAYER_VALIDATOR_FOR_TYPE(DepthToSpaceValidator, DepthToSpace); -REG_LAYER_VALIDATOR_FOR_TYPE(SpaceToDepthValidator, SpaceToDepth); -REG_LAYER_VALIDATOR_FOR_TYPE(ReverseSequenceValidator, ReverseSequence); -REG_LAYER_VALIDATOR_FOR_TYPE(RNNCellValidator, RNNCell); -REG_LAYER_VALIDATOR_FOR_TYPE(RNNCellValidator, GRUCell); -REG_LAYER_VALIDATOR_FOR_TYPE(RNNCellValidator, LSTMCell); -REG_LAYER_VALIDATOR_FOR_TYPE(RNNSequenceValidator, RNNSequence); -REG_LAYER_VALIDATOR_FOR_TYPE(RNNSequenceValidator, GRUSequence); -REG_LAYER_VALIDATOR_FOR_TYPE(RNNSequenceValidator, LSTMSequence); -REG_LAYER_VALIDATOR_FOR_TYPE(SelectValidator, Select); -REG_LAYER_VALIDATOR_FOR_TYPE(SqueezeValidator, Squeeze); -REG_LAYER_VALIDATOR_FOR_TYPE(UnsqueezeValidator, Unsqueeze); -REG_LAYER_VALIDATOR_FOR_TYPE(RangeValidator, Range); -REG_LAYER_VALIDATOR_FOR_TYPE(FillValidator, Fill); -REG_LAYER_VALIDATOR_FOR_TYPE(BroadcastValidator, Broadcast); -REG_LAYER_VALIDATOR_FOR_TYPE(TanHValidator, TanH); -REG_LAYER_VALIDATOR_FOR_TYPE(TileValidator, Tile); -REG_LAYER_VALIDATOR_FOR_TYPE(UnpoolingValidator, Unpooling); -REG_LAYER_VALIDATOR_FOR_TYPE(UpsamplingValidator, Upsampling); -REG_LAYER_VALIDATOR_FOR_TYPE(OneHotValidator, OneHot); -REG_LAYER_VALIDATOR_FOR_TYPE(QuantizeValidator, Quantize); -REG_LAYER_VALIDATOR_FOR_TYPE(BinaryConvolutionValidator, BinaryConvolution); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Abs); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Acos); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Acosh); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Asin); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Asinh); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Atan); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Atanh); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Ceil); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Cos); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Cosh); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Erf); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Floor); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, HardSigmoid); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Log); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Neg); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Reciprocal); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Selu); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Sign); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Sin); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Sinh); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Softplus); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Softsign); -REG_LAYER_VALIDATOR_FOR_TYPE(MathValidator, Tan); -REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceAnd); -REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceL1); -REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceL2); -REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceLogSum); -REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceLogSumExp); -REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceMax); -REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceMean); -REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceMin); -REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceOr); -REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceProd); -REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceSum); -REG_LAYER_VALIDATOR_FOR_TYPE(ReduceValidator, ReduceSumSquare); -REG_LAYER_VALIDATOR_FOR_TYPE(GatherTreeValidator, GatherTree); -REG_LAYER_VALIDATOR_FOR_TYPE(TopKValidator, TopK); } // namespace details } // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/ie_layers_internal.cpp b/inference-engine/src/inference_engine/ie_layers_internal.cpp index fd6b7df5cbec8d..771c52c8f13117 100644 --- a/inference-engine/src/inference_engine/ie_layers_internal.cpp +++ b/inference-engine/src/inference_engine/ie_layers_internal.cpp @@ -11,6 +11,7 @@ #include #include "ie_layers_internal.hpp" #include "layer_transform.hpp" +#include namespace InferenceEngine { @@ -38,10 +39,10 @@ Paddings getPaddingsInternal(const Layer &layer) { return {PropertyVector(layer._kernel.size(), 0u), PropertyVector(layer._kernel.size(), 0u)}; } else { - if (insData.size() != 1 && layer.type != "DeformableConvolution") - THROW_IE_EXCEPTION << "number of inputs should be equal 1"; - if (insData.size() != 2 && layer.type == "DeformableConvolution") - THROW_IE_EXCEPTION << "number of inputs should be equal 2"; + if ((insData.size() > 3 || insData.empty()) && layer.type != "DeformableConvolution") + THROW_IE_EXCEPTION << "number of inputs should be in range [1, 3]"; + if ((insData.size() > 4 || insData.empty()) && layer.type == "DeformableConvolution") + THROW_IE_EXCEPTION << "number of inputs should be in range [2, 4]"; auto firstInput = insData[0].lock(); if (!firstInput) THROW_IE_EXCEPTION << "input is empty"; diff --git a/inference-engine/src/inference_engine/ie_layouts.cpp b/inference-engine/src/inference_engine/ie_layouts.cpp index d61bdd3df7b916..0588df9f2dee04 100644 --- a/inference-engine/src/inference_engine/ie_layouts.cpp +++ b/inference-engine/src/inference_engine/ie_layouts.cpp @@ -67,15 +67,16 @@ TensorDesc::TensorDesc(const Precision& precision, Layout layout): blockingDesc( TensorDesc::TensorDesc(const Precision &precision, SizeVector dims, const BlockingDesc &blockDesc) : dims(dims), blockingDesc(blockDesc), precision(precision) { + if (dims.size() == 0 || blockingDesc.getBlockDims().size() == 0) { + layout = Layout::SCALAR; + return; + } if (dims.size() != *std::max_element(blockDesc.getOrder().begin(), blockDesc.getOrder().end()) + 1) THROW_IE_EXCEPTION << "Cannot create TensorDesc! Blocked dims are inconsistent with original dims."; layout = Layout::BLOCKED; if (dims.size() == blockingDesc.getBlockDims().size()) { switch (dims.size()) { - case 0: - layout = Layout::SCALAR; - break; case 1: layout = Layout::C; break; @@ -123,7 +124,6 @@ TensorDesc::TensorDesc() { } void TensorDesc::setDims(const SizeVector &dims) { - this->dims = dims; if (layout == Layout::BLOCKED) { auto newDims = blockingDesc.getBlockDims(); auto newOrder = blockingDesc.getOrder(); @@ -135,8 +135,12 @@ void TensorDesc::setDims(const SizeVector &dims) { } blockingDesc = BlockingDesc(newDims, newOrder); } else { + if (layout == Layout::SCALAR && (dims.size() > 1 || (dims.size() == 1 && dims[0] != 1))) + THROW_IE_EXCEPTION << "Cannot set dimensions for SCALAR layout!"; blockingDesc = BlockingDesc(dims, layout); } + if (layout != Layout::SCALAR) + this->dims = dims; } bool TensorDesc::operator==(const TensorDesc &rhs) const { @@ -173,6 +177,9 @@ size_t TensorDesc::offset(const SizeVector& v) const { if (layout == Layout::ANY) THROW_IE_EXCEPTION << "Cannot calculate offset for any format!"; + if (layout == Layout::SCALAR) + return blockingDesc.getOffsetPadding(); + SizeVector off_v = v; const SizeVector& blockedDims = blockingDesc.getBlockDims(); const SizeVector& strides = blockingDesc.getStrides(); @@ -298,7 +305,7 @@ BlockingDesc::BlockingDesc(const SizeVector& dims, Layout layout): offsetPadding case Layout::NDHWC: checkDims(dims.size(), 5); l_order = {0, 2, 3, 4, 1}; - l_dims = dims; + l_dims = {dims[0], dims[2], dims[3], dims[4], dims[1]}; break; case Layout::CHW: checkDims(dims.size(), 3); @@ -308,7 +315,7 @@ BlockingDesc::BlockingDesc(const SizeVector& dims, Layout layout): offsetPadding case Layout::CN: checkDims(dims.size(), 2); l_order = {1, 0}; - l_dims = {dims[1], dims[2]}; + l_dims = {dims[1], dims[0]}; break; case Layout::NC: case Layout::HW: @@ -358,3 +365,4 @@ bool BlockingDesc::operator==(const BlockingDesc &rhs) const { bool BlockingDesc::operator!=(const BlockingDesc &rhs) const { return !(*this == rhs); } + diff --git a/inference-engine/src/inference_engine/ie_metric_helpers.hpp b/inference-engine/src/inference_engine/ie_metric_helpers.hpp index ea85d68ded35ee..52d371eb37fbf2 100644 --- a/inference-engine/src/inference_engine/ie_metric_helpers.hpp +++ b/inference-engine/src/inference_engine/ie_metric_helpers.hpp @@ -10,7 +10,7 @@ namespace InferenceEngine { namespace Metrics { template -class MetricType; +struct MetricType; #define DECLARE_METRIC_KEY_IMPL(name, ...) \ struct name { }; \ diff --git a/inference-engine/src/inference_engine/ie_plugin_dispatcher.cpp b/inference-engine/src/inference_engine/ie_plugin_dispatcher.cpp index 6dfdfc4d94aa34..061bc41e87e504 100644 --- a/inference-engine/src/inference_engine/ie_plugin_dispatcher.cpp +++ b/inference-engine/src/inference_engine/ie_plugin_dispatcher.cpp @@ -35,6 +35,16 @@ InferencePlugin PluginDispatcher::getPluginByDevice(const std::string& deviceNam InferenceEngine::ResponseDesc response; ptr->SetConfig({{"TARGET_FALLBACK", deviceName.substr(7, deviceName.length() - 7)}}, &response); } + } else if (deviceName.find("MULTI:") == 0) { + // MULTI found: everything after ':' to the options of the multi-device plugin + ptr = getSuitablePlugin(InferenceEngine::TargetDeviceInfo::fromStr("MULTI")); + if (ptr) { + InferenceEngine::ResponseDesc response; + if (deviceName.length() < 6) + THROW_IE_EXCEPTION << "Missing devices priorities for the multi-device case"; + ptr->SetConfig({{InferenceEngine::MultiDeviceConfigParams::KEY_MULTI_DEVICE_PRIORITIES, + deviceName.substr(6, deviceName.length() - 6)}}, &response); + } } else { ptr = getSuitablePlugin(InferenceEngine::TargetDeviceInfo::fromStr(deviceName)); } diff --git a/inference-engine/src/inference_engine/ie_preprocess_gapi_kernels.cpp b/inference-engine/src/inference_engine/ie_preprocess_gapi_kernels.cpp index 6588d4c4001bd1..b8abf79a19861d 100644 --- a/inference-engine/src/inference_engine/ie_preprocess_gapi_kernels.cpp +++ b/inference-engine/src/inference_engine/ie_preprocess_gapi_kernels.cpp @@ -462,7 +462,6 @@ static void initScratchLinear(const cv::GMatDesc& in, cv::gapi::fluid::Buffer& scratch, int lpi) { using alpha_type = typename Mapper::alpha_type; - using index_type = typename Mapper::index_type; static const auto unity = Mapper::unity; auto inSz = in.size; diff --git a/inference-engine/src/inference_engine/ie_util_internal.cpp b/inference-engine/src/inference_engine/ie_util_internal.cpp index 489ab244825558..1a2f0784607028 100644 --- a/inference-engine/src/inference_engine/ie_util_internal.cpp +++ b/inference-engine/src/inference_engine/ie_util_internal.cpp @@ -149,6 +149,7 @@ CNNLayerPtr clonelayer(const CNNLayer& source) { static const fptr cloners[] = { &layerCloneImpl, &layerCloneImpl, + &layerCloneImpl, &layerCloneImpl, &layerCloneImpl, &layerCloneImpl, @@ -163,6 +164,7 @@ CNNLayerPtr clonelayer(const CNNLayer& source) { &layerCloneImpl, &layerCloneImpl, &layerCloneImpl, + &layerCloneImpl, &layerCloneImpl, &layerCloneImpl, &layerCloneImpl, @@ -184,12 +186,17 @@ CNNLayerPtr clonelayer(const CNNLayer& source) { &layerCloneImpl, &layerCloneImpl, &layerCloneImpl, - &layerCloneImpl, + &layerCloneImpl, + &layerCloneImpl, + &layerCloneImpl, &layerCloneImpl, &layerCloneImpl, &layerCloneImpl, &layerCloneImpl, - &layerCloneImpl + &layerCloneImpl, + &layerCloneImpl, + &layerCloneImpl, + &layerCloneImpl }; for (auto cloner : cloners) { auto cloned = cloner(&source); @@ -611,8 +618,6 @@ struct NodePrinter { operation = "Pow"; else if (eltwise->_operation == EltwiseLayer::Mean) operation = "Mean"; - else if (eltwise->_operation == EltwiseLayer::Select) - operation = "Select"; printed_properties.emplace_back("operation", operation); } diff --git a/inference-engine/src/inference_engine/ie_utils.cpp b/inference-engine/src/inference_engine/ie_utils.cpp index b5f8b322656052..bbdce7bd4a39e6 100644 --- a/inference-engine/src/inference_engine/ie_utils.cpp +++ b/inference-engine/src/inference_engine/ie_utils.cpp @@ -6,6 +6,8 @@ #include "graph_tools.hpp" #include "details/caseless.hpp" #include "ie_utils.hpp" +#include "ie_plugin.hpp" +#include "ie_ihetero_plugin.hpp" #include @@ -23,6 +25,47 @@ using namespace InferenceEngine; using namespace details; +IE_SUPPRESS_DEPRECATED_START + +IHeteroInferencePlugin::~IHeteroInferencePlugin() { +} + +IHeteroDeviceLoader::~IHeteroDeviceLoader() { +} + +QueryNetworkResult::QueryNetworkResult() : rc(OK) { +} + +const QueryNetworkResult & QueryNetworkResult::operator= (const QueryNetworkResult & q) { + supportedLayers = q.supportedLayers; + supportedLayersMap = q.supportedLayersMap; + rc = q.rc; + resp = q.resp; + + return *this; +} + +QueryNetworkResult & QueryNetworkResult::operator= (QueryNetworkResult && q) { + supportedLayers = q.supportedLayers; + supportedLayersMap = q.supportedLayersMap; + rc = q.rc; + resp = q.resp; + + return *this; +} + +QueryNetworkResult::QueryNetworkResult(const QueryNetworkResult & instance) : + supportedLayers(instance.supportedLayers), + supportedLayersMap(instance.supportedLayersMap), + rc(instance.rc), + resp(instance.resp) { +} + +QueryNetworkResult::~QueryNetworkResult() { +} + +IE_SUPPRESS_DEPRECATED_END + namespace { InferenceEngine::LayerComplexity getComplexity(const InferenceEngine::CNNLayerPtr &layer) { diff --git a/inference-engine/src/inference_engine/ie_version.cpp b/inference-engine/src/inference_engine/ie_version.cpp index 8b84be6c685e54..3712ba71f3215b 100644 --- a/inference-engine/src/inference_engine/ie_version.cpp +++ b/inference-engine/src/inference_engine/ie_version.cpp @@ -10,7 +10,7 @@ INFERENCE_ENGINE_API(const Version*) GetInferenceEngineVersion() noexcept { // Use local static variable to make sure it is always properly initialized // even if called from global constructor static Version inferenceEngineVersion = { - {2, 0}, // inference engine API version + {2, 1}, // inference engine API version CI_BUILD_NUMBER, "API" }; diff --git a/inference-engine/src/inference_engine/layer_transform.hpp b/inference-engine/src/inference_engine/layer_transform.hpp index e386575d5e64d8..b2fb31823b59ff 100644 --- a/inference-engine/src/inference_engine/layer_transform.hpp +++ b/inference-engine/src/inference_engine/layer_transform.hpp @@ -27,6 +27,7 @@ using AllLayers = std::tuple < DeformableConvolutionLayer*, DeconvolutionLayer*, ConvolutionLayer *, + TopKLayer*, PoolingLayer*, FullyConnectedLayer*, GemmLayer*, @@ -36,6 +37,7 @@ using AllLayers = std::tuple < ShuffleChannelsLayer*, DepthToSpaceLayer*, SpaceToDepthLayer*, + SparseFillEmptyRowsLayer*, ReverseSequenceLayer*, RangeLayer*, FillLayer*, @@ -65,13 +67,68 @@ using AllLayers = std::tuple < BinaryConvolutionLayer*, WeightableLayer*, OneHotLayer*, - CNNLayer*, MathLayer*, - ReduceLayer* + ReduceLayer*, + UniqueLayer*, + NonMaxSuppressionLayer*, + ScatterLayer*, + CNNLayer* >; + +/** + * @brief checks whether type inxed as P has a parent among element in range I..N + * can be used only for P < I + * */ +template +struct is_base_of_any; + +template +struct is_base_of_any< + IBase, IDerived, Tuple, + typename std::enable_if::value, void>::type > : public std::true_type { + using base = typename std::remove_pointer::type>::type; + using derived = typename std::remove_pointer::type>::type; + + static_assert(IDerived < IBase, "cannot match parent using incorrect indices"); + static_assert(!std::is_base_of::value, "probing type is a parent of followed type"); + + // check that incoming type have parents in range I..N, and any of I..N not a child of derivedd type + static_assert((std::is_base_of::value || is_base_of_any::value), "parent matching failed"); +}; + +// for matches any->after last +template < + size_t IBase, + size_t IDerived, + class Tuple> +struct is_base_of_any< + IBase, IDerived, Tuple, typename std::enable_if= std::tuple_size::value, void>::type> : public std::false_type { +}; + +/** +* @brief check wether type ordered from child to base within given list +*/ +template +struct is_types_ordered_from_child_to_base {}; + +template +struct is_types_ordered_from_child_to_base ::value - 2, void>::type> { + static constexpr bool value = is_base_of_any

:: value && is_types_ordered_from_child_to_base

:: value; +}; + +template +struct is_types_ordered_from_child_to_base::value - 2, void>::type> { + static constexpr bool value = is_base_of_any

:: value; +}; + +static_assert(is_types_ordered_from_child_to_base<0, AllLayers>::value, + "All layers must be topologically sorted as so for any layer, it's father appeared later in a types list"); + template -void dynamic_cast_layer(const CNNLayer &source, CNNLayerPtr &target, T & /*, InjectedType value*/) { +inline void dynamic_cast_layer(const CNNLayer &source, CNNLayerPtr &target, T & /*, InjectedType value*/) { if (target) { return; } diff --git a/inference-engine/src/inference_engine/net_pass.cpp b/inference-engine/src/inference_engine/net_pass.cpp index 9721c5e9a8481d..6a254116c03de7 100644 --- a/inference-engine/src/inference_engine/net_pass.cpp +++ b/inference-engine/src/inference_engine/net_pass.cpp @@ -635,9 +635,9 @@ static CNNLayerPtr _pwr(std::string name, Precision prc, SizeVector dims, float res->power = 1.0; res->scale = scale; res->offset = shift; - res->params["power"] = std::to_string(res->power); - res->params["scale"] = std::to_string(res->scale); - res->params["shift"] = std::to_string(res->offset); + res->params["power"] = CNNLayer::ie_serialize_float(res->power); + res->params["scale"] = CNNLayer::ie_serialize_float(res->scale); + res->params["shift"] = CNNLayer::ie_serialize_float(res->offset); res->insData.resize(1); res->outData.resize(1); @@ -747,8 +747,8 @@ static void _link_with_clip(CNNLayerPtr src, CNNLayerPtr dst, const float clip_v auto clip_prc = dst->precision; auto clip_shape = src->outData[src_port]->getTensorDesc().getDims(); auto clip = _act(clip_name, clip_prc, clip_shape, "clamp"); - clip->params["min"] = std::to_string(-clip_val); - clip->params["max"] = std::to_string(clip_val); + clip->params["min"] = CNNLayer::ie_serialize_float(-clip_val); + clip->params["max"] = CNNLayer::ie_serialize_float(clip_val); _link(src, clip, src_port, 0); _link(clip, dst, 0, dst_port); @@ -1197,6 +1197,11 @@ std::vector TopolSort(const TensorIterator::Body &net) { return TIBodySortTopologically(net); } +void restore_net_consistency(ICNNNetwork &net) { + // At first all layers should be available via findByName() api. + // In other words all layers should be present in internal map + for (auto &l : TopolSort(net)) net.addLayer(l); +} template bool ApplyForAll(N &net, T action) { @@ -1210,7 +1215,6 @@ bool ApplyForAll(N &net, T action) { } - template bool ApplyForAll_if(N &net, T action, P pred) { auto all_layers = TopolSort(net); @@ -1224,14 +1228,19 @@ bool ApplyForAll_if(N &net, T action, P pred) { } bool CombineRNNSeq(ICNNNetwork &net) { - return ApplyForAll(net, convertToRNNSeq); + auto res = ApplyForAll(net, convertToRNNSeq); + restore_net_consistency(net); + return res; } + bool CombineRNNSeq(TensorIterator::Body &net) { return ApplyForAll(net, convertToRNNSeq); } bool UnrollTI(ICNNNetwork &net) { - return ApplyForAll(net, unrollTI); + auto res = ApplyForAll(net, unrollTI); + restore_net_consistency(net); + return res; } @@ -1256,7 +1265,9 @@ bool UnrollRNN_if_impl(NET &net, const std::function p } bool UnrollRNN_if(ICNNNetwork &net, const std::function pred) { - return UnrollRNN_if_impl(net, pred); + auto res = UnrollRNN_if_impl(net, pred); + restore_net_consistency(net); + return res; } bool UnrollRNN_if(TensorIterator::Body &net, const std::function pred) { diff --git a/inference-engine/src/inference_engine/network_serializer.cpp b/inference-engine/src/inference_engine/network_serializer.cpp index db740bef1ab99c..bf15718367a7a8 100644 --- a/inference-engine/src/inference_engine/network_serializer.cpp +++ b/inference-engine/src/inference_engine/network_serializer.cpp @@ -121,11 +121,12 @@ void NetworkSerializer::serialize( } } if (!node->outData.empty()) { - pugi::xml_node input = layer.append_child("output"); + pugi::xml_node output = layer.append_child("output"); for (size_t oport = 0; oport < node->outData.size(); oport++) { - pugi::xml_node port = input.append_child("port"); + pugi::xml_node port = output.append_child("port"); port.append_attribute("id").set_value(node->insData.size() + oport); + port.append_attribute("precision").set_value(node->outData[oport]->getPrecision().name()); for (const auto dim : node->outData[oport]->getDims()) { port.append_child("dim").text().set(dim); diff --git a/inference-engine/src/inference_engine/range_iterator.hpp b/inference-engine/src/inference_engine/range_iterator.hpp deleted file mode 100644 index cf4578f5bc7da8..00000000000000 --- a/inference-engine/src/inference_engine/range_iterator.hpp +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include - -namespace InferenceEngine { - -/** - * @Brief iterator for accesing standard c-style null terminated strings withing c++ algorithms - * @tparam Char - */ -template -struct null_terminated_range_iterator : public std::iterator { - public: - null_terminated_range_iterator() = delete; - - // make a non-end iterator (well, unless you pass nullptr ;) - explicit null_terminated_range_iterator(Char *ptr) : ptr(ptr) {} - - bool operator != (null_terminated_range_iterator const &that) const { - // iterators are equal if they point to the same location - return !(operator==(that)); - } - - bool operator == (null_terminated_range_iterator const &that) const { - // iterators are equal if they point to the same location - return ptr == that.ptr - // or if they are both end iterators - || (is_end() && that.is_end()); - } - - null_terminated_range_iterator &operator++() { - get_accessor()++; - return *this; - } - - null_terminated_range_iterator &operator++(int) { - return this->operator++(); - } - - Char &operator*() { - return *get_accessor(); - } - - protected: - Char *& get_accessor() { - if (ptr == nullptr) { - throw std::logic_error("null_terminated_range_iterator dereference: pointer is zero"); - } - return ptr; - } - bool is_end() const { - // end iterators can be created by the default ctor - return !ptr - // or by advancing until a null character - || !*ptr; - } - - Char *ptr; -}; - -template -struct null_terminated_range_iterator_end : public null_terminated_range_iterator { - public: - // make an end iterator - null_terminated_range_iterator_end() : null_terminated_range_iterator(nullptr) { - null_terminated_range_iterator::ptr = nullptr; - } -}; - - -inline null_terminated_range_iterator null_terminated_string(const char *a) { - return null_terminated_range_iterator(a); -} - -inline null_terminated_range_iterator null_terminated_string_end() { - return null_terminated_range_iterator_end(); -} - -} // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/shape_infer/built-in/ie_broadcast_shape_infer.hpp b/inference-engine/src/inference_engine/shape_infer/built-in/ie_broadcast_shape_infer.hpp index 2e9c33d962894a..663bb80b91f6fe 100644 --- a/inference-engine/src/inference_engine/shape_infer/built-in/ie_broadcast_shape_infer.hpp +++ b/inference-engine/src/inference_engine/shape_infer/built-in/ie_broadcast_shape_infer.hpp @@ -5,6 +5,7 @@ #pragma once #include "ie_built_in_impl.hpp" +#include "precision_utils.h" #include #include #include @@ -38,8 +39,31 @@ class BroadcastShapeProp : public BuiltInShapeInferImpl { } else { THROW_IE_EXCEPTION << "Second input must have allocated data"; } + } else if (inBlobs[1]->getTensorDesc().getPrecision() == Precision::FP32) { + auto* buffer = inBlobs[1]->cbuffer().as(); + if (buffer != nullptr) { + for (int i = 0; i < inBlobs[1]->size(); i++) { + shapes.push_back(static_cast(buffer[i])); + } + } else { + THROW_IE_EXCEPTION << "Second input must have allocated data"; + } + } else if (inBlobs[1]->getTensorDesc().getPrecision() == Precision::FP16) { + auto* buffer = inBlobs[1]->cbuffer().as(); + if (buffer != nullptr) { + for (int i = 0; i < inBlobs[1]->size(); i++) { + shapes.push_back(static_cast(PrecisionUtils::f16tof32(buffer[i]))); + } + } + } else if (inBlobs[1]->getTensorDesc().getPrecision() == Precision::I64) { + auto *buffer = inBlobs[1]->cbuffer().as(); + if (buffer != nullptr) { + shapes.assign(buffer, buffer + inBlobs[1]->size()); + } else { + THROW_IE_EXCEPTION << "Second input must have allocated data"; + } } else { - THROW_IE_EXCEPTION << "Second input must have I32 precision"; + THROW_IE_EXCEPTION << "Second input must have I32 or FP32 or FP16 precision"; } outShapes = {shapes}; diff --git a/inference-engine/src/inference_engine/shape_infer/built-in/ie_built_in_holder.cpp b/inference-engine/src/inference_engine/shape_infer/built-in/ie_built_in_holder.cpp index 744093ac15cdbe..1b731e3b190b0e 100644 --- a/inference-engine/src/inference_engine/shape_infer/built-in/ie_built_in_holder.cpp +++ b/inference-engine/src/inference_engine/shape_infer/built-in/ie_built_in_holder.cpp @@ -39,6 +39,7 @@ #include "ie_shuffle_channels_shape_infer.hpp" #include "ie_depth_to_space_shape_infer.hpp" #include "ie_space_to_depth_shape_infer.hpp" +#include "ie_sparse_fill_empty_rows_shape_infer.hpp" #include "ie_reverse_sequence_shape_infer.hpp" #include "ie_one_hot_shape_infer.hpp" #include "ie_shape_shape_infer.hpp" @@ -57,6 +58,9 @@ #include "ie_reduce_shape_infer.hpp" #include "ie_gather_tree_shape_infer.hpp" #include "ie_topk_shape_infer.hpp" +#include "ie_unique_shape_infer.hpp" +#include "ie_scatter_shape_infer.hpp" +#include "ie_non_max_suppression_shape_infer.hpp" #include #include #include @@ -121,6 +125,7 @@ REG_SHAPE_INFER_FOR_TYPE(EqualShapeProp, LogSoftMax); REG_SHAPE_INFER_FOR_TYPE(EqualShapeProp, LRN); REG_SHAPE_INFER_FOR_TYPE(EqualShapeProp, Norm); REG_SHAPE_INFER_FOR_TYPE(EqualShapeProp, Normalize); +REG_SHAPE_INFER_FOR_TYPE(EqualShapeProp, Convert); // FIXME: Really Copy??? New MO doesn't generate this layer REG_SHAPE_INFER_FOR_TYPE(EqualShapeProp, Copy); REG_SHAPE_INFER_FOR_TYPE(EqualShapeProp, Power); @@ -178,6 +183,7 @@ REG_SHAPE_INFER_FOR_TYPE(StridedSliceShapeProp, StridedSlice); REG_SHAPE_INFER_FOR_TYPE(ShuffleChannelsShapeProp, ShuffleChannels); REG_SHAPE_INFER_FOR_TYPE(DepthToSpaceShapeProp, DepthToSpace); REG_SHAPE_INFER_FOR_TYPE(SpaceToDepthShapeProp, SpaceToDepth); +REG_SHAPE_INFER_FOR_TYPE(SparseFillEmptyRowsShapeProp, SparseFillEmptyRows); REG_SHAPE_INFER_FOR_TYPE(ReverseSequenceShapeProp, ReverseSequence); REG_SHAPE_INFER_FOR_TYPE(SelectShapeProp, Select); REG_SHAPE_INFER_FOR_TYPE(SqueezeShapeProp, Squeeze); @@ -203,6 +209,7 @@ REG_SHAPE_INFER_FOR_TYPE(MathShapeProp, Erf); REG_SHAPE_INFER_FOR_TYPE(MathShapeProp, Floor); REG_SHAPE_INFER_FOR_TYPE(MathShapeProp, HardSigmoid); REG_SHAPE_INFER_FOR_TYPE(MathShapeProp, Log); +REG_SHAPE_INFER_FOR_TYPE(MathShapeProp, Exp); REG_SHAPE_INFER_FOR_TYPE(MathShapeProp, Neg); REG_SHAPE_INFER_FOR_TYPE(MathShapeProp, Reciprocal); REG_SHAPE_INFER_FOR_TYPE(MathShapeProp, Selu); @@ -226,6 +233,9 @@ REG_SHAPE_INFER_FOR_TYPE(ReduceShapeProp, ReduceSum); REG_SHAPE_INFER_FOR_TYPE(ReduceShapeProp, ReduceSumSquare); REG_SHAPE_INFER_FOR_TYPE(GatherTreeShapeProp, GatherTree); REG_SHAPE_INFER_FOR_TYPE(TopKShapeProp, TopK); +REG_SHAPE_INFER_FOR_TYPE(UniqueShapeProp, Unique); +REG_SHAPE_INFER_FOR_TYPE(NMSShapeProp, NonMaxSuppression); +REG_SHAPE_INFER_FOR_TYPE(ScatterShapeProp, Scatter); } // namespace ShapeInfer } // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/shape_infer/built-in/ie_non_max_suppression_shape_infer.hpp b/inference-engine/src/inference_engine/shape_infer/built-in/ie_non_max_suppression_shape_infer.hpp new file mode 100644 index 00000000000000..3b62e2b7cfb154 --- /dev/null +++ b/inference-engine/src/inference_engine/shape_infer/built-in/ie_non_max_suppression_shape_infer.hpp @@ -0,0 +1,39 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "ie_built_in_impl.hpp" +#include +#include +#include +#include + +namespace InferenceEngine { +namespace ShapeInfer { + +/** + *@brief Implementation of Shape inference for NonMaxSuppression layer + */ +class NMSShapeProp : public BuiltInShapeInferImpl { +public: + explicit NMSShapeProp(const std::string& type) : BuiltInShapeInferImpl(type) {} + + void inferShapesImpl(const std::vector& inBlobs, + const std::map& params, + const std::map& blobs, + std::vector& outShapes) override { + LayerParams lp{}; + NonMaxSuppressionLayer nmsLayer(lp); + nmsLayer.params = params; + nmsLayer.type = _type; + validate(&nmsLayer, inBlobs, params, blobs); + + outShapes.push_back({inShapes[1][0] * inShapes[1][1] * inShapes[1][2], 3}); + } +}; + +} // namespace ShapeInfer +} // namespace InferenceEngine + diff --git a/inference-engine/src/inference_engine/shape_infer/built-in/ie_resample_shape_infer.hpp b/inference-engine/src/inference_engine/shape_infer/built-in/ie_resample_shape_infer.hpp index ee4014414cae3e..a2662cd057af6e 100644 --- a/inference-engine/src/inference_engine/shape_infer/built-in/ie_resample_shape_infer.hpp +++ b/inference-engine/src/inference_engine/shape_infer/built-in/ie_resample_shape_infer.hpp @@ -33,13 +33,33 @@ class ResampleShapeProp : public BuiltInShapeInferImpl { validate(&cnnLayer, inBlobs, params, blobs); SizeVector outShape; if (inBlobs.size() == 2) { - auto* buffer = inBlobs[1]->cbuffer().as(); - if (buffer != nullptr) { - for (int i = 0; i < inBlobs[1]->size(); i++) { - outShape.push_back(static_cast(buffer[i])); + switch (inBlobs[1]->getTensorDesc().getPrecision()) { + case Precision::FP32: { + auto *buffer = inBlobs[1]->cbuffer().as(); + + if (buffer != nullptr) { + for (int i = 0; i < inBlobs[1]->size(); i++) { + outShape.push_back(static_cast(buffer[i])); + } + } else { + THROW_IE_EXCEPTION << "Second input must have allocated data"; + } + break; + } + case Precision::I32: { + auto *buffer = inBlobs[1]->cbuffer().as(); + + if (buffer != nullptr) { + for (int i = 0; i < inBlobs[1]->size(); i++) { + outShape.push_back(static_cast(buffer[i])); + } + } else { + THROW_IE_EXCEPTION << "Second input must have allocated data"; + } + break; } - } else { - THROW_IE_EXCEPTION << "Second input must have allocated data"; + default: + THROW_IE_EXCEPTION << "Unsupported second input precision"; } } else { auto scale = cnnLayer.GetParamAsFloat("factor"); diff --git a/inference-engine/src/inference_engine/shape_infer/built-in/ie_scatter_shape_infer.hpp b/inference-engine/src/inference_engine/shape_infer/built-in/ie_scatter_shape_infer.hpp new file mode 100644 index 00000000000000..2d9efcb3f941db --- /dev/null +++ b/inference-engine/src/inference_engine/shape_infer/built-in/ie_scatter_shape_infer.hpp @@ -0,0 +1,39 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "ie_built_in_impl.hpp" +#include +#include +#include +#include + +namespace InferenceEngine { +namespace ShapeInfer { + +/** + *@brief Implementation of Shape inference for Scatter layer + */ +class ScatterShapeProp : public BuiltInShapeInferImpl { +public: + explicit ScatterShapeProp(const std::string& type) : BuiltInShapeInferImpl(type) {} + + void inferShapesImpl(const std::vector& inBlobs, + const std::map& params, + const std::map& blobs, + std::vector& outShapes) override { + LayerParams lp{}; + ScatterLayer scatterLayer(lp); + scatterLayer.params = params; + scatterLayer.type = _type; + validate(&scatterLayer, inBlobs, params, blobs); + + outShapes = {inShapes[0]}; + } +}; + +} // namespace ShapeInfer +} // namespace InferenceEngine + diff --git a/inference-engine/src/inference_engine/shape_infer/built-in/ie_sparse_fill_empty_rows_shape_infer.hpp b/inference-engine/src/inference_engine/shape_infer/built-in/ie_sparse_fill_empty_rows_shape_infer.hpp new file mode 100644 index 00000000000000..353529af59928d --- /dev/null +++ b/inference-engine/src/inference_engine/shape_infer/built-in/ie_sparse_fill_empty_rows_shape_infer.hpp @@ -0,0 +1,33 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "ie_built_in_impl.hpp" +#include +#include +#include +#include + +namespace InferenceEngine { +namespace ShapeInfer { + +/** + *@brief Implementation of Shape inference for SparseFillEmptyRows layer + */ +class SparseFillEmptyRowsShapeProp : public BuiltInShapeInferImpl { +public: + explicit SparseFillEmptyRowsShapeProp(const std::string& type) : BuiltInShapeInferImpl(type) {} + + void inferShapesImpl(const std::vector& inBlobs, + const std::map& params, + const std::map& blobs, + std::vector& outShapes) override { + THROW_IE_EXCEPTION << "SparseFillEmptyRows is not re-shapeable layer."; + } +}; + +} // namespace ShapeInfer +} // namespace InferenceEngine + diff --git a/inference-engine/src/inference_engine/shape_infer/built-in/ie_topk_shape_infer.hpp b/inference-engine/src/inference_engine/shape_infer/built-in/ie_topk_shape_infer.hpp index 8846bcbba02e9e..bf562cf6cd4bfa 100644 --- a/inference-engine/src/inference_engine/shape_infer/built-in/ie_topk_shape_infer.hpp +++ b/inference-engine/src/inference_engine/shape_infer/built-in/ie_topk_shape_infer.hpp @@ -54,7 +54,7 @@ class TopKShapeProp : public BuiltInShapeInferImpl { THROW_IE_EXCEPTION << " Incorrect input parameters dimensions and axis number!"; int *src_k = inBlobs[TOPK_K]->cbuffer().as(); - if (src_k != nullptr) + if (src_k == nullptr) THROW_IE_EXCEPTION << " Only const input for 'k' is supported!"; src_k += inBlobs[TOPK_K]->getTensorDesc().getBlockingDesc().getOffsetPadding(); diff --git a/inference-engine/src/inference_engine/shape_infer/built-in/ie_unique_shape_infer.hpp b/inference-engine/src/inference_engine/shape_infer/built-in/ie_unique_shape_infer.hpp new file mode 100644 index 00000000000000..06fe26a5c3cb6e --- /dev/null +++ b/inference-engine/src/inference_engine/shape_infer/built-in/ie_unique_shape_infer.hpp @@ -0,0 +1,44 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "ie_built_in_impl.hpp" +#include +#include +#include +#include + +namespace InferenceEngine { +namespace ShapeInfer { + +/** + *@brief Implementation of Shape inference for Unique layer + */ +class UniqueShapeProp : public BuiltInShapeInferImpl { +public: + explicit UniqueShapeProp(const std::string& type) : BuiltInShapeInferImpl(type) {} + + void inferShapesImpl(const std::vector& inBlobs, + const std::map& params, + const std::map& blobs, + std::vector& outShapes) override { + LayerParams lp{}; + UniqueLayer unique_layer(lp); + unique_layer.params = params; + unique_layer.type = _type; + validate(&unique_layer, inBlobs, params, blobs); + + // reshape available outputs + size_t num_output_edges = unique_layer.outData.size(); + outShapes.resize(num_output_edges); + for (size_t i = 0; i < num_output_edges; i++) { + outShapes[i].resize(1); + outShapes[i][0] = inShapes[0][0]; + } + } +}; + +} // namespace ShapeInfer +} // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/shape_infer/built-in/ie_unsqueeze_shape_infer.hpp b/inference-engine/src/inference_engine/shape_infer/built-in/ie_unsqueeze_shape_infer.hpp index 076cb0005fe730..a344a716ee40b6 100644 --- a/inference-engine/src/inference_engine/shape_infer/built-in/ie_unsqueeze_shape_infer.hpp +++ b/inference-engine/src/inference_engine/shape_infer/built-in/ie_unsqueeze_shape_infer.hpp @@ -74,6 +74,10 @@ class UnsqueezeShapeProp : public BuiltInShapeInferImpl { const SizeVector &idx_dims) { T* idx_data = inBlobs[UNSQUEEZE_INDEXES]->cbuffer().as() + inBlobs[UNSQUEEZE_INDEXES]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + if (!idx_data) { + outShape = data_dims; + return; + } size_t max = data_dims.size(); for (size_t i = 0; i < idx_dims[0]; i++) { auto axis = static_cast(castToInt32(idx_data[i])); diff --git a/inference-engine/src/inference_engine/shape_infer/const_infer/broadcast_offset.hpp b/inference-engine/src/inference_engine/shape_infer/const_infer/broadcast_offset.hpp new file mode 100644 index 00000000000000..a085c7d5e58d7b --- /dev/null +++ b/inference-engine/src/inference_engine/shape_infer/const_infer/broadcast_offset.hpp @@ -0,0 +1,71 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace InferenceEngine { +namespace ShapeInfer { +class BroadcastOffset { + SizeVector dims; + SizeVector offset_v; + + SizeVector getDims(const SizeVector& originDims, const SizeVector& outputDims) { + SizeVector d(outputDims.size(), 1); + for (int i = 0; i < originDims.size(); i++) { + d[d.size() - 1 - i] = originDims[originDims.size() - 1 - i]; + } + return d; + } + + SizeVector getOffset(const SizeVector& originDims, const SizeVector& outDims) { + SizeVector o(originDims.size()); + if (originDims.size() != outDims.size()) + THROW_IE_EXCEPTION << "Cannot calculate offsets! Incorrect patameters for eltwise broadcast!"; + int k = 1; + for (int i = originDims.size() - 1; i >= 0; i--) { + o[i] = (originDims[i] == outDims[i]) ? k : 0; + k *= originDims[i]; + } + return o; + } + + public: + BroadcastOffset(const SizeVector& originDims, const SizeVector& outputDims) { + dims = getDims(originDims, outputDims); + offset_v = getOffset(dims, outputDims); + } + + size_t offset(const SizeVector& v) const { + size_t off = 0; + if (v.size() != offset_v.size()) + THROW_IE_EXCEPTION << "Cannot calculate offsets! Incorrect patameters for eltwise broadcast!"; + for (size_t i = 0; i < v.size(); i++) { + off += v[i] * offset_v[i]; + } + return off; + } + + SizeVector offset_dims(size_t l) const { + size_t n_dims = dims.size(); + SizeVector pos(n_dims); + for (int rd = 1; rd <= n_dims; ++rd) { + const size_t d = n_dims - rd; + const size_t cur_dim = dims[d]; + pos[d] = l % cur_dim; + l /= cur_dim; + } + return pos; + } +}; +} // namespace ShapeInfer +} // namespace InferenceEngine \ No newline at end of file diff --git a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_add_const_infer.hpp b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_add_const_infer.hpp index 8435ba64b4237c..7116098df58bee 100644 --- a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_add_const_infer.hpp +++ b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_add_const_infer.hpp @@ -10,17 +10,85 @@ #include #include #include +#include +#include +#include "ie_const_infer_impl.hpp" namespace InferenceEngine { namespace ShapeInfer { /** *@brief Implementation of Const inference for TBD layer + * + * Table of output data type value with given input parameters + * + * + * U8 I32 I64 FP16 FP32 + * ============================================================= + * U8 == U8 I32 I64 FP16 FP32 + * == + * I32 == I32 I32 I64 FP32 FP32 + * == + * I64 == I64 I64 I64 FP32 FP32 + * == + * FP16 == FP16 FP32 FP32 FP16 FP32 + * == + * FP32 == FP32 FP32 FP32 FP32 FP32 + * + * There is a special case with FP16 precision. Convert input data to FP32 and add. After that + * convert output data to FP16, if both of input parameters have FP16 precision or one - FP16 and another - U8. */ + class AddConstInfer : public ConstInferImpl { public: explicit AddConstInfer(const std::string& type) : ConstInferImpl(type) {} + struct fp16tofp32{ + inline float operator()(ie_fp16 value){ + return static_cast(PrecisionUtils::f16tof32(value)); + } + }; + + struct fp32tofp16{ + inline ie_fp16 operator()(float value){ + return static_cast(PrecisionUtils::f32tof16(value)); + } + }; + + template + struct noConversion{ + inline dataType operator()(dataType value){ + return value; + } + }; + + template + void add(const std::vector& inData, + const std::map& params, + const std::map& blobs, + std::vector& outData) { + auto *firstBlobBuffer = inData[0]->cbuffer().as(); + auto *secondBlobBuffer = inData[1]->cbuffer().as(); + + if (!firstBlobBuffer || !secondBlobBuffer) { + THROW_IE_EXCEPTION << "empty input data"; + } + + auto outBlob = *outData.begin(); + auto *outBuffer = outBlob->buffer().as(); + if (!outBuffer) THROW_IE_EXCEPTION << "empty output data"; + + BroadcastOffset outOff(outBlob->getTensorDesc().getDims(), outBlob->getTensorDesc().getDims()); + BroadcastOffset inOff1(inData[0]->getTensorDesc().getDims(), outBlob->getTensorDesc().getDims()); + BroadcastOffset inOff2(inData[1]->getTensorDesc().getDims(), outBlob->getTensorDesc().getDims()); + + for (size_t i = 0; i < outBlob->size(); i++) { + SizeVector offsetDims = outOff.offset_dims(i); + outBuffer[outOff.offset(offsetDims)] = ConversionOutData()(ConversionInData1()(firstBlobBuffer[inOff1.offset(offsetDims)]) + + ConversionInData2()(secondBlobBuffer[inOff2.offset(offsetDims)])); + } + } + void inferImpl(const std::vector& inData, const std::map& params, const std::map& blobs, @@ -28,20 +96,126 @@ class AddConstInfer : public ConstInferImpl { size_t numInputs = inData.size(); if (inData.size() != 2) THROW_IE_EXCEPTION << "Unsupported number of inputs: " << numInputs << ". 2 inputs is supported"; - auto* firstBlobBuffer = inData[0]->cbuffer().as(); - auto* secondBlobBuffer = inData[1]->cbuffer().as(); - if (!firstBlobBuffer || !secondBlobBuffer) { - THROW_IE_EXCEPTION << "empty input data"; - } - auto outBlob = *outData.begin(); - auto* outBuffer = outBlob->buffer().as(); - if (!outBuffer) THROW_IE_EXCEPTION << "empty output data"; - if (inData[0]->size() != inData[1]->size()) { - THROW_IE_EXCEPTION << "inputs with different shapes are not supported"; - } - for (int i = 0; i < outBlob->size(); i++) { - outBuffer[i] = firstBlobBuffer[i] + secondBlobBuffer[i]; + auto compare = getPrecisionMask(inData[0]->getTensorDesc().getPrecision(), + inData[1]->getTensorDesc().getPrecision(), + outData[0]->getTensorDesc().getPrecision()); + + switch (compare) { + case getPrecisionMask(Precision::U8, Precision::U8, Precision::U8): + add, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::U8, Precision::I32, Precision::I32): + add, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::U8, Precision::I64, Precision::I64): + add, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::U8, Precision::FP16, Precision::FP16): + add, fp16tofp32, + fp32tofp16>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::U8, Precision::FP32, Precision::FP32): + add, noConversion, + noConversion>(inData, params, blobs, outData); + break; + + case getPrecisionMask(Precision::I32, Precision::U8, Precision::I32): + add, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I32, Precision::I32, Precision::I32): + add, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I32, Precision::I64, Precision::I64): + add, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I32, Precision::FP16, Precision::FP32): + add, fp16tofp32, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I32, Precision::FP32, Precision::FP32): + add, noConversion, + noConversion>(inData, params, blobs, outData); + break; + + case getPrecisionMask(Precision::I64, Precision::U8, Precision::I64): + add, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I64, Precision::I32, Precision::I64): + add, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I64, Precision::I64, Precision::I64): + add, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I64, Precision::FP16, Precision::FP32): + add, fp16tofp32, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I64, Precision::FP32, Precision::FP32): + add, noConversion, + noConversion>(inData, params, blobs, outData); + break; + + case getPrecisionMask(Precision::FP16, Precision::U8, Precision::FP16): + add, + fp32tofp16>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP16, Precision::I32, Precision::FP32): + add, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP16, Precision::I64, Precision::FP32): + add, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP16, Precision::FP16, Precision::FP16): + add(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP16, Precision::FP32, Precision::FP16): + add, + fp32tofp16>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP16, Precision::FP32, Precision::FP32): + add, + noConversion>(inData, params, blobs, outData); + break; + + case getPrecisionMask(Precision::FP32, Precision::U8, Precision::FP32): + add, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP32, Precision::I32, Precision::FP32): + add, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP32, Precision::I64, Precision::FP32): + add, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP32, Precision::FP16, Precision::FP32): + add, fp16tofp32, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP32, Precision::FP16, Precision::FP16): + add, fp16tofp32, + fp32tofp16>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP32, Precision::FP32, Precision::FP32): + add, noConversion, + noConversion>(inData, params, blobs, outData); + break; + default: + THROW_IE_EXCEPTION << "Unsupported precision!"; } } }; diff --git a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_broadcast_const_infer.hpp b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_broadcast_const_infer.hpp index fc4f39d4275e5e..8951469d118abb 100644 --- a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_broadcast_const_infer.hpp +++ b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_broadcast_const_infer.hpp @@ -14,7 +14,8 @@ #include #include "ie_const_infer_impl.hpp" #include "ie_parallel.hpp" -#include "../../precision_utils.h" +#include "precision_utils.h" +#include "ie_memcpy.h" namespace InferenceEngine { namespace ShapeInfer { @@ -47,23 +48,14 @@ class BroadcastConstInfer : public ConstInferImpl { if (inData[BROADCAST_SHAPE]->getTensorDesc().getDims().size() > 1) THROW_IE_EXCEPTION << "Shape vector should be 1 dimension"; - if (inData[BROADCAST_SHAPE]->getTensorDesc().getPrecision() != Precision::I32) - THROW_IE_EXCEPTION << "Shape vector should be I32!"; - - if (!(inData[BROADCAST_INPUT]->getTensorDesc().getPrecision() == Precision::I32 && - outData[0]->getTensorDesc().getPrecision() == Precision::I32) && - !(inData[BROADCAST_INPUT]->getTensorDesc().getPrecision() == Precision::FP32 && - outData[0]->getTensorDesc().getPrecision() == Precision::FP32)) { - THROW_IE_EXCEPTION - << "Input and output tensors should have same precision and only FP32 and I32 are supported!"; - } - - const int32_t *shape_dims = inData[BROADCAST_SHAPE]->cbuffer().as() + - inData[BROADCAST_SHAPE]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + size_t data_size = inData[BROADCAST_INPUT]->getTensorDesc().getPrecision().size(); size_t shape_size = (inData[BROADCAST_SHAPE]->getTensorDesc().getDims())[0]; SizeVector dst_dims = outData[0]->getTensorDesc().getDims(); SizeVector src_dims = inData[BROADCAST_INPUT]->getTensorDesc().getDims(); + if (!src_dims.size()) + src_dims = SizeVector(1, 1); + if (dst_dims.size() != shape_size) { THROW_IE_EXCEPTION << "Output tensor dimension mismatch"; } @@ -72,26 +64,15 @@ class BroadcastConstInfer : public ConstInferImpl { THROW_IE_EXCEPTION << "Output tensor dimension is smaller then input tensor dimension"; } - size_t i; - for (i = 0; i < dst_dims.size(); i++) { - if (static_cast(dst_dims[i]) != shape_dims[i]) { - THROW_IE_EXCEPTION << "Output tensor dimension size mismatch"; - } - } - - size_t prefix_size = dst_dims.size() - src_dims.size(); - for (i = 0; i < src_dims.size(); i++) { - if (src_dims[i] != 1 && static_cast(src_dims[i]) != shape_dims[i + prefix_size]) { - THROW_IE_EXCEPTION - << "In/Output corresponding dimension must have the same value, or Input dimension is equal to 1"; - } - } - InferenceEngine::SizeVector dstStrides = outData[0]->getTensorDesc().getBlockingDesc().getStrides(); InferenceEngine::SizeVector srcStrides = inData[BROADCAST_INPUT]->getTensorDesc().getBlockingDesc().getStrides(); InferenceEngine::SizeVector src_aligned(dst_dims.size()); InferenceEngine::SizeVector srcStrides_aligned(dst_dims.size()); - for (i = 0; i < dst_dims.size(); i++) { + if (!srcStrides.size()) + srcStrides = SizeVector(1, 1); + + size_t prefix_size = dst_dims.size() - src_dims.size(); + for (size_t i = 0; i < dst_dims.size(); i++) { if (i < prefix_size) { src_aligned[i] = 1; srcStrides_aligned[i] = srcStrides[0]; @@ -102,67 +83,31 @@ class BroadcastConstInfer : public ConstInferImpl { } size_t work_amount_dst = dstStrides[0] * dst_dims[0]; - - switch (outData[0]->getTensorDesc().getPrecision()) { - case Precision::FP32: { - const float *src_data = inData[BROADCAST_INPUT]->cbuffer().as() + - inData[BROADCAST_INPUT]->getTensorDesc().getBlockingDesc().getOffsetPadding(); - float *dst_data = outData[0]->cbuffer().as() + - outData[0]->getTensorDesc().getBlockingDesc().getOffsetPadding(); - - parallel_nt(0, [&](const int ithr, const int nthr) { - size_t i, src_idx, start = 0, end = 0; - SizeVector counters(dst_dims.size(), 0); - splitter(work_amount_dst, nthr, ithr, start, end); - for (int j = dst_dims.size() - 1, i = start; j >= 0; j--) { - counters[j] = i % dst_dims[j]; - i /= dst_dims[j]; - } - for (size_t iwork = start; iwork < end; ++iwork) { - for (i = 0, src_idx = 0; i < dst_dims.size(); ++i) - src_idx += counters[i] ? ((counters[i] % src_aligned[i]) * srcStrides_aligned[i]) : 0; - - dst_data[iwork] = src_data[src_idx]; - - for (int j = dst_dims.size() - 1; j >= 0; j--) { - counters[j] = (counters[j] + 1) % dst_dims[j]; - if (counters[j] != 0) break; - } - } - }); + const uint8_t *src_data = inData[BROADCAST_INPUT]->cbuffer().as() + + inData[BROADCAST_INPUT]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + uint8_t* dst_data = outData[0]->cbuffer().as() + + outData[0]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + + parallel_nt(0, [&](const int ithr, const int nthr) { + size_t i, src_idx, start = 0, end = 0; + SizeVector counters(dst_dims.size(), 0); + splitter(work_amount_dst, nthr, ithr, start, end); + for (int j = dst_dims.size() - 1, i = start; j >= 0; j--) { + counters[j] = i % dst_dims[j]; + i /= dst_dims[j]; } - break; - case Precision::I32: { - const int32_t *src_data = inData[BROADCAST_INPUT]->cbuffer().as() + - inData[BROADCAST_INPUT]->getTensorDesc().getBlockingDesc().getOffsetPadding(); - int32_t *dst_data = outData[0]->cbuffer().as() + - outData[0]->getTensorDesc().getBlockingDesc().getOffsetPadding(); - - parallel_nt(0, [&](const int ithr, const int nthr) { - size_t i, src_idx, start = 0, end = 0; - SizeVector counters(dst_dims.size(), 0); - splitter(work_amount_dst, nthr, ithr, start, end); - for (int j = dst_dims.size() - 1, i = start; j >= 0; j--) { - counters[j] = i % dst_dims[j]; - i /= dst_dims[j]; - } - for (size_t iwork = start; iwork < end; ++iwork) { - for (i = 0, src_idx = 0; i < dst_dims.size(); ++i) - src_idx += counters[i] ? ((counters[i] % src_aligned[i]) * srcStrides_aligned[i]) : 0; - - dst_data[iwork] = src_data[src_idx]; - - for (int j = dst_dims.size() - 1; j >= 0; j--) { - counters[j] = (counters[j] + 1) % dst_dims[j]; - if (counters[j] != 0) break; - } - } - }); + for (size_t iwork = start * data_size; iwork < end * data_size; iwork += data_size) { + for (i = 0, src_idx = 0; i < dst_dims.size(); ++i) + src_idx += counters[i] ? ((counters[i] % src_aligned[i]) * srcStrides_aligned[i]) : 0; + + ie_memcpy(&dst_data[iwork], data_size, &src_data[src_idx * data_size], data_size); + + for (int j = dst_dims.size() - 1; j >= 0; j--) { + counters[j] = (counters[j] + 1) % dst_dims[j]; + if (counters[j] != 0) break; + } } - break; - default: - THROW_IE_EXCEPTION << "Incorrect output precision. Only FP32 and I32 are supported!"; - } + }); } }; diff --git a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_concat_const_infer.hpp b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_concat_const_infer.hpp index 904c16bb2412cd..10945ef9bf16fe 100644 --- a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_concat_const_infer.hpp +++ b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_concat_const_infer.hpp @@ -35,7 +35,7 @@ class ConcatConstInfer : public ConstInferImpl { auto outBlob = *outData.begin(); SizeVector outShape = outBlob->getTensorDesc().getDims(); - auto* outBuffer = outBlob->buffer().as(); + auto* outBuffer = outBlob->buffer().as(); size_t outerSize = 1; for (int i = 0; i < layer._axis; i++) @@ -44,11 +44,16 @@ class ConcatConstInfer : public ConstInferImpl { size_t outIdx = 0; for (size_t osIdx = 0; osIdx < outerSize; osIdx++) { for (auto& inBlob : inData) { - const auto* inBuffer = inBlob->cbuffer().as(); + if (inBlob->getTensorDesc().getPrecision() != outBlob->getTensorDesc().getPrecision()) + THROW_IE_EXCEPTION << "Unsupported concat layer with different precisions! Out precision: " + + std::string(outBlob->getTensorDesc().getPrecision().name()); + const auto* inBuffer = inBlob->cbuffer().as(); size_t innerSize = inBlob->size() / outerSize; for (size_t j = 0; j < innerSize; j++, outIdx++) { - outBuffer[outIdx] = inBuffer[osIdx * innerSize + j]; + memcpy(outBuffer + outIdx*outBlob->element_size(), + inBuffer + (osIdx * innerSize + j)*inBlob->element_size(), + inBlob->element_size()); } } } diff --git a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_const_infer_holder.cpp b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_const_infer_holder.cpp index d874ba33ac420f..4f5d3c3841a05b 100644 --- a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_const_infer_holder.cpp +++ b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_const_infer_holder.cpp @@ -20,12 +20,15 @@ #include "ie_gather_const_infer.hpp" #include "ie_split_const_infer.hpp" #include "ie_concat_const_infer.hpp" +#include "ie_convert_const_infer.hpp" #include "ie_in_place_const_infer.hpp" #include "ie_strided_slice_const_infer.hpp" #include "ie_fill_const_infer.hpp" #include "ie_range_const_infer.hpp" #include "ie_broadcast_const_infer.hpp" +#include "ie_permute_const_infer.hpp" #include "ie_onehot_const_infer.hpp" +#include "ie_reduce_const_infer.hpp" #include #include #include @@ -81,6 +84,20 @@ REG_CONST_INFER_FOR_TYPE(FillConstInfer, Fill); REG_CONST_INFER_FOR_TYPE(RangeConstInfer, Range); REG_CONST_INFER_FOR_TYPE(BroadcastConstInfer, Broadcast); REG_CONST_INFER_FOR_TYPE(OneHotConstInfer, OneHot); +REG_CONST_INFER_FOR_TYPE(ReduceConstInfer, ReduceAnd); +REG_CONST_INFER_FOR_TYPE(ReduceConstInfer, ReduceL1); +REG_CONST_INFER_FOR_TYPE(ReduceConstInfer, ReduceL2); +REG_CONST_INFER_FOR_TYPE(ReduceConstInfer, ReduceLogSum); +REG_CONST_INFER_FOR_TYPE(ReduceConstInfer, ReduceLogSumExp); +REG_CONST_INFER_FOR_TYPE(ReduceConstInfer, ReduceMax); +REG_CONST_INFER_FOR_TYPE(ReduceConstInfer, ReduceMean); +REG_CONST_INFER_FOR_TYPE(ReduceConstInfer, ReduceMin); +REG_CONST_INFER_FOR_TYPE(ReduceConstInfer, ReduceOr); +REG_CONST_INFER_FOR_TYPE(ReduceConstInfer, ReduceProd); +REG_CONST_INFER_FOR_TYPE(ReduceConstInfer, ReduceSum); +REG_CONST_INFER_FOR_TYPE(ReduceConstInfer, ReduceSumSquare); +REG_CONST_INFER_FOR_TYPE(PermuteConstInfer, Permute); +REG_CONST_INFER_FOR_TYPE(ConvertConstInfer, Convert); } // namespace ShapeInfer } // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_const_infer_impl.cpp b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_const_infer_impl.cpp index 45883dd5508518..304f0e8901ca9f 100644 --- a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_const_infer_impl.cpp +++ b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_const_infer_impl.cpp @@ -17,7 +17,8 @@ void ConstInferImpl::infer(const std::vector& inData, std::string errorPrefix = "Ref infer error for Layer with `" + _type + "` type: "; if (outData.empty()) THROW_IE_EXCEPTION << errorPrefix + "output data is empty"; for (auto const& data : outData) { - if (data->buffer() == nullptr) THROW_IE_EXCEPTION << errorPrefix + "output data is not allocated"; + if (data->buffer() == nullptr) + THROW_IE_EXCEPTION << errorPrefix + "output data is not allocated"; } // TODO: check for direct (NCHW, NCH, NC) and FP32 inferImpl(inData, params, blobs, outData); diff --git a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_convert_const_infer.hpp b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_convert_const_infer.hpp new file mode 100644 index 00000000000000..918a5cc4455184 --- /dev/null +++ b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_convert_const_infer.hpp @@ -0,0 +1,91 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include "ie_precision.hpp" +#include "ie_parallel.hpp" +#include "ie_const_infer_impl.hpp" + +namespace InferenceEngine { +namespace ShapeInfer { + +/** + *@brief Implementation of Const inference for Tile layer + */ +class ConvertConstInfer : public ConstInferImpl { + template + void exec_cast(const Blob::CPtr& inData, Blob::Ptr& outData) { + const src_d *src_data = inData->cbuffer().as() + + inData->getTensorDesc().getBlockingDesc().getOffsetPadding(); + dst_d* dst_data = outData->buffer().as() + + outData->getTensorDesc().getBlockingDesc().getOffsetPadding(); + if (inData->size() != outData->size()) + THROW_IE_EXCEPTION << " Convert constant inference error: Input and output buffers have different sizes! Input buffer size = `" << inData->size() + << "` output buffer size = `" << outData->size() << "`"; + parallel_for(inData->size(), [&](size_t i) { + dst_data[i] = static_cast(src_data[i]); + }); + } + +public: + explicit ConvertConstInfer(const std::string& type) : ConstInferImpl(type) {} + + void inferImpl(const std::vector& inData, + const std::map& params, + const std::map& blobs, + std::vector& outData) override { + LayerParams lp{}; + ConcatLayer layer(lp); + layer.params = params; + _validator->parseParams(&layer); + if (inData.size() != 1) + THROW_IE_EXCEPTION << " Convert constant inference error: incorrect number of inputs! Expected 1, got " << inData.size(); + if (outData.size() != 1) + THROW_IE_EXCEPTION << " Convert constant inference error: incorrect number of outputs! Expected 1, got " << outData.size(); + if (layer.params["precision"] != outData[0]->getTensorDesc().getPrecision().name()) + THROW_IE_EXCEPTION << " Convert constant inference error: layer `precision` parameter and actual output data precision mismatch! " + "`precision`=\"" << layer.params["precision"] << "\", " << + "`output_data_precision`=\"" << outData[0]->getTensorDesc().getPrecision() << "\""; + + auto compare = getPrecisionMask(inData[0]->getTensorDesc().getPrecision(), outData[0]->getTensorDesc().getPrecision()); + switch (compare) { + case getPrecisionMask(Precision::I32, Precision::I32): + exec_cast::value_type, PrecisionTrait::value_type>(inData[0], outData[0]); + break; + case getPrecisionMask(Precision::I64, Precision::I64): + exec_cast::value_type, PrecisionTrait::value_type>(inData[0], outData[0]); + break; + case getPrecisionMask(Precision::FP32, Precision::FP32): + exec_cast::value_type, PrecisionTrait::value_type>(inData[0], outData[0]); + break; + case getPrecisionMask(Precision::I32, Precision::I64): + exec_cast::value_type, PrecisionTrait::value_type>(inData[0], outData[0]); + break; + case getPrecisionMask(Precision::I32, Precision::FP32): + exec_cast::value_type, PrecisionTrait::value_type>(inData[0], outData[0]); + break; + case getPrecisionMask(Precision::FP32, Precision::I32): + exec_cast::value_type, PrecisionTrait::value_type>(inData[0], outData[0]); + break; + case getPrecisionMask(Precision::FP32, Precision::I64): + exec_cast::value_type, PrecisionTrait::value_type>(inData[0], outData[0]); + break; + default: + THROW_IE_EXCEPTION << " Convert constant inference error: Unsupported precision configuration! " << + " Input precision: " << inData[0]->getTensorDesc().getPrecision() << ", output precision: " + << outData[0]->getTensorDesc().getPrecision(); + } + } +}; + +} // namespace ShapeInfer +} // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_eltw_const_infer.hpp b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_eltw_const_infer.hpp index b8e40c5b1c3778..865abd3c8e101c 100644 --- a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_eltw_const_infer.hpp +++ b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_eltw_const_infer.hpp @@ -7,6 +7,7 @@ #include "ie_div_const_infer.hpp" #include "ie_add_const_infer.hpp" #include "ie_mul_const_infer.hpp" +#include "ie_pow_const_infer.hpp" #include #include #include @@ -26,6 +27,7 @@ class EltwiseConstInfer : public ConstInferImpl { _sum = std::shared_ptr(new AddConstInfer(_type)); _mul = std::shared_ptr(new MulConstInfer(_type)); _div = std::shared_ptr(new DivConstInfer(_type)); + _pow = std::shared_ptr(new PowConstInfer(_type)); } void inferImpl(const std::vector& inData, @@ -43,6 +45,8 @@ class EltwiseConstInfer : public ConstInferImpl { actual = _mul; else if (operation == "div") actual = _div; + else if (operation == "pow") + actual = _pow; else THROW_IE_EXCEPTION << "Unsupported eltwise operation type " << operation << ". " "IE cannot propagate constants through this layer."; @@ -51,7 +55,7 @@ class EltwiseConstInfer : public ConstInferImpl { } private: - std::shared_ptr _mul, _div, _sum; + std::shared_ptr _mul, _div, _sum, _pow; }; } // namespace ShapeInfer diff --git a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_gather_const_infer.hpp b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_gather_const_infer.hpp index fc8821653acdf9..9c43eff2455cc0 100644 --- a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_gather_const_infer.hpp +++ b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_gather_const_infer.hpp @@ -14,7 +14,7 @@ #include #include "ie_const_infer_impl.hpp" #include "ie_parallel.hpp" -#include "../../precision_utils.h" +#include "precision_utils.h" namespace InferenceEngine { namespace ShapeInfer { @@ -51,48 +51,31 @@ class GatherConstInfer : public ConstInferImpl { } }; - template + template void gather(const Blob::CPtr& indexes, const Blob::CPtr& dictionary, Blob::Ptr output, const GatherParams& p) { size_t src_indexSize = indexes->size(); const index_t *src_index = indexes->cbuffer().as() + indexes->getTensorDesc().getBlockingDesc().getOffsetPadding(); - const data_t *src_dataDict = dictionary->cbuffer().as() + dictionary->getTensorDesc().getBlockingDesc().getOffsetPadding(); - data_t *dst_data = output->cbuffer().as() + output->getTensorDesc().getBlockingDesc().getOffsetPadding(); - - if (p.axis == 0) { - parallel_for(src_indexSize, [&](size_t i) { - unsigned int idx = Conversion()(src_index[i]); - - // Index clipping - if (idx < p.indexRange) { - // Copying data to destination from Dictionary - ie_memcpy(&dst_data[i * p.dataLength], - output->byteSize() - (p.dataLength * i), - &src_dataDict[p.dataLength * idx], - sizeof(data_t) * p.dataLength); - } else { - memset(&dst_data[i * p.dataLength], 0, sizeof(data_t) * p.dataLength); + const uint8_t *src_dataDict = dictionary->cbuffer().as() + dictionary->getTensorDesc().getBlockingDesc().getOffsetPadding(); + uint8_t *dst_data = output->cbuffer().as() + output->getTensorDesc().getBlockingDesc().getOffsetPadding(); + + parallel_for(src_indexSize, [&](size_t i) { + unsigned int idx = Conversion()(src_index[i]); + + // Index clipping + if (idx < p.indexRange) { + // Copying data to destination from Dictionary + for (size_t j = 0; j < p.numDictionaries; j++) { + ie_memcpy(&dst_data[p.dataLength * (i + j * src_indexSize)], + output->byteSize() - (p.dataLength * (i + j * src_indexSize)), + &src_dataDict[p.dataLength * (idx + j * p.indexRange)], + p.dataLength); } - }); - } else { - parallel_for(src_indexSize, [&](size_t i) { - unsigned int idx = Conversion()(src_index[i]); - - // Index clipping - if (idx < p.indexRange) { - // Copying data to destination from Dictionary - for (size_t j = 0; j < p.numDictionaries; j++) { - ie_memcpy(&dst_data[p.dataLength * (i + j * src_indexSize)], - output->byteSize() - (p.dataLength * (i + j * src_indexSize)), - &src_dataDict[p.dataLength * (idx + j * p.indexRange)], - sizeof(data_t) * p.dataLength); - } - } else { - for (size_t j = 0; j < p.numDictionaries; j++) { - memset(&dst_data[p.dataLength * (i + j * src_indexSize)], 0, sizeof(data_t) * p.dataLength); - } + } else { + for (size_t j = 0; j < p.numDictionaries; j++) { + memset(&dst_data[p.dataLength * (i + j * src_indexSize)], 0, p.dataLength); } - }); - } + } + }); } void inferImpl(const std::vector& inData, @@ -118,54 +101,45 @@ class GatherConstInfer : public ConstInferImpl { Precision inDataPrecision = inData[GATHER_DICTIONARY]->getTensorDesc().getPrecision(); if (inDataPrecision != Precision::FP32 && - inDataPrecision != Precision::FP16) - THROW_IE_EXCEPTION << " Incorrect input precision. Only FP32 or FP16 are supported!"; + inDataPrecision != Precision::FP16 && + inIdxPrecision != Precision::I32) + THROW_IE_EXCEPTION << " Incorrect input precision. Only FP32|FP16|I32 are supported!"; // Remove redundant dimensions const SizeVector& dictionary_dims = inData[GATHER_DICTIONARY]->getTensorDesc().getDims(); - size_t actualAxis = 0; - SizeVector dims_actual; - for (size_t i = 0; i < dictionary_dims.size(); i++) { - if (dictionary_dims[i] > 1) { - for (size_t j = i; j < dictionary_dims.size(); j++) - dims_actual.push_back(dictionary_dims[j]); - break; - } - } - - if (dims_actual.size() == 0) + if (dictionary_dims.size() == 0) THROW_IE_EXCEPTION << " Incorrect input parameters dimension!"; GatherParams p; p.axis = static_cast(layer.GetParamAsInt("axis")); // Dictionary must be at least rank axis + 1 - if (p.axis > 0 && dims_actual.size() < (1 + p.axis)) - THROW_IE_EXCEPTION << " Incorrect input parameters dimensions and axis number!"; - else if (p.axis < 0 && (static_cast(dims_actual.size()) + p.axis) < 0) + if (!(-static_cast(dictionary_dims.size()) <= p.axis && p.axis < static_cast(dictionary_dims.size()))) THROW_IE_EXCEPTION << " Incorrect input parameters dimensions and axis number!"; if (p.axis < 0) - p.axis += dims_actual.size(); + p.axis += dictionary_dims.size(); // Find number of dictionaries, index range and data length for (size_t i = 0; i < p.axis; i++) - p.numDictionaries *= dims_actual[i]; - p.indexRange = dims_actual[p.axis]; - for (size_t i = p.axis + 1; i < dims_actual.size(); i++) - p.dataLength *= dims_actual[i]; + p.numDictionaries *= dictionary_dims[i]; + p.indexRange = dictionary_dims[p.axis]; + for (size_t i = p.axis + 1; i < dictionary_dims.size(); i++) + p.dataLength *= dictionary_dims[i]; if (p.dataLength == 0) THROW_IE_EXCEPTION << " Incorrect input parameters dimension!"; + p.dataLength *= inData[GATHER_DICTIONARY]->getTensorDesc().getPrecision().size(); + switch (inData[GATHER_INDEXES]->getTensorDesc().getPrecision()) { case Precision::FP32: - gather(inData[GATHER_INDEXES], inData[GATHER_DICTIONARY], outData[0], p); + gather(inData[GATHER_INDEXES], inData[GATHER_DICTIONARY], outData[0], p); break; case Precision::FP16: - gather(inData[GATHER_INDEXES], inData[GATHER_DICTIONARY], outData[0], p); + gather(inData[GATHER_INDEXES], inData[GATHER_DICTIONARY], outData[0], p); break; case Precision::I32: - gather(inData[GATHER_INDEXES], inData[GATHER_DICTIONARY], outData[0], p); + gather(inData[GATHER_INDEXES], inData[GATHER_DICTIONARY], outData[0], p); break; default: THROW_IE_EXCEPTION << " Unsupported precision!"; diff --git a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_mul_const_infer.hpp b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_mul_const_infer.hpp index d9afcce7dd487c..8cd4dfa47b1516 100644 --- a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_mul_const_infer.hpp +++ b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_mul_const_infer.hpp @@ -10,41 +10,215 @@ #include #include #include +#include +#include +#include "ie_const_infer_impl.hpp" +#include "broadcast_offset.hpp" namespace InferenceEngine { namespace ShapeInfer { /** *@brief Implementation of Const inference for TBD layer + * + * Table of output data type value with given input parameters + * + * + * U8 I32 I64 FP16 FP32 + * ============================================================= + * U8 == U8 I32 I64 FP16 FP32 + * == + * I32 == I32 I32 I64 FP32 FP32 + * == + * I64 == I64 I64 I64 FP32 FP32 + * == + * FP16 == FP16 FP32 FP32 FP16 FP32 + * == + * FP32 == FP32 FP32 FP32 FP32 FP32 + * + * There is a special case with FP16 precision. Convert input data to FP32 and multiply. After that + * convert output data to FP16, if both of input parameters have FP16 precision or one - FP16 and another - U8. */ + class MulConstInfer : public ConstInferImpl { -public: - explicit MulConstInfer(const std::string& type) : ConstInferImpl(type) {} - - void inferImpl(const std::vector& inData, - const std::map& params, - const std::map& blobs, - std::vector& outData) override { - size_t numInputs = inData.size(); - if (inData.size() != 2) - THROW_IE_EXCEPTION << "Unsupported number of inputs: " << numInputs << ". 2 inputs is supported"; - auto* firstBlobBuffer = inData[0]->cbuffer().as(); - auto* secondBlobBuffer = inData[1]->cbuffer().as(); - - if (!firstBlobBuffer || !secondBlobBuffer) { - THROW_IE_EXCEPTION << "empty input data"; - } - auto outBlob = *outData.begin(); - auto* outBuffer = outBlob->buffer().as(); - if (!outBuffer) THROW_IE_EXCEPTION << "empty output data"; - if (inData[0]->size() != inData[1]->size()) { - THROW_IE_EXCEPTION << "inputs with different shapes are not supported"; + public: + explicit MulConstInfer(const std::string &type) : ConstInferImpl(type) {} + + struct fp16tofp32{ + inline float operator()(ie_fp16 value){ + return static_cast(PrecisionUtils::f16tof32(value)); + } + }; + + struct fp32tofp16{ + inline ie_fp16 operator()(float value){ + return static_cast(PrecisionUtils::f32tof16(value)); + } + }; + + template + struct noConversion{ + inline dataType operator()(dataType value){ + return value; + } + }; + + template + void mul(const std::vector &inData, + const std::map ¶ms, + const std::map &blobs, + std::vector &outData) { + auto* firstBlobBuffer = inData[0]->cbuffer().as(); + auto* secondBlobBuffer = inData[1]->cbuffer().as(); + if (!firstBlobBuffer || !secondBlobBuffer) { + THROW_IE_EXCEPTION << "empty input data"; + } + + auto outBlob = *outData.begin(); + auto* outBuffer = outBlob->buffer().as(); + if (!outBuffer) THROW_IE_EXCEPTION << "empty output data"; + + BroadcastOffset outOff(outBlob->getTensorDesc().getDims(), outBlob->getTensorDesc().getDims()); + BroadcastOffset inOff1(inData[0]->getTensorDesc().getDims(), outBlob->getTensorDesc().getDims()); + BroadcastOffset inOff2(inData[1]->getTensorDesc().getDims(), outBlob->getTensorDesc().getDims()); + + for (size_t i = 0; i < outBlob->size(); i++) { + SizeVector offsetDims = outOff.offset_dims(i); + outBuffer[outOff.offset(offsetDims)] = ConversionOutData()(ConversionInData1()(firstBlobBuffer[inOff1.offset(offsetDims)]) * + ConversionInData2()(secondBlobBuffer[inOff2.offset(offsetDims)])); + } } - for (int i = 0; i < outBlob->size(); i++) { - outBuffer[i] = firstBlobBuffer[i] * secondBlobBuffer[i]; + + void inferImpl(const std::vector &inData, + const std::map ¶ms, + const std::map &blobs, + std::vector &outData) override { + size_t numInputs = inData.size(); + if (inData.size() != 2) + THROW_IE_EXCEPTION << "Unsupported number of inputs: " << numInputs << ". 2 inputs is supported"; + + auto compare = getPrecisionMask(inData[0]->getTensorDesc().getPrecision(), + inData[1]->getTensorDesc().getPrecision(), + outData[0]->getTensorDesc().getPrecision()); + + switch (compare) { + case getPrecisionMask(Precision::U8, Precision::U8, Precision::U8): + mul, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::U8, Precision::I32, Precision::I32): + mul, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::U8, Precision::I64, Precision::I64): + mul, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::U8, Precision::FP16, Precision::FP16): + mul, fp16tofp32, + fp32tofp16>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::U8, Precision::FP32, Precision::FP32): + mul, noConversion, + noConversion>(inData, params, blobs, outData); + break; + + case getPrecisionMask(Precision::I32, Precision::U8, Precision::I32): + mul, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I32, Precision::I32, Precision::I32): + mul, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I32, Precision::I64, Precision::I64): + mul, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I32, Precision::FP16, Precision::FP32): + mul, fp16tofp32, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I32, Precision::FP32, Precision::FP32): + mul, noConversion, + noConversion>(inData, params, blobs, outData); + break; + + case getPrecisionMask(Precision::I64, Precision::U8, Precision::I64): + mul, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I64, Precision::I32, Precision::I64): + mul, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I64, Precision::I64, Precision::I64): + mul, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I64, Precision::FP16, Precision::FP32): + mul, fp16tofp32, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I64, Precision::FP32, Precision::FP32): + mul, noConversion, + noConversion>(inData, params, blobs, outData); + break; + + case getPrecisionMask(Precision::FP16, Precision::U8, Precision::FP16): + mul, + fp32tofp16>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP16, Precision::I32, Precision::FP32): + mul, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP16, Precision::I64, Precision::FP32): + mul, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP16, Precision::FP16, Precision::FP16): + mul(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP16, Precision::FP32, Precision::FP32): + mul, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP16, Precision::FP32, Precision::FP16): + mul, + fp32tofp16>(inData, params, blobs, outData); + break; + + case getPrecisionMask(Precision::FP32, Precision::U8, Precision::FP32): + mul, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP32, Precision::I32, Precision::FP32): + mul, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP32, Precision::I64, Precision::FP32): + mul, noConversion, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP32, Precision::FP16, Precision::FP32): + mul, fp16tofp32, + noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP32, Precision::FP16, Precision::FP16): + mul, fp16tofp32, + fp32tofp16>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP32, Precision::FP32, Precision::FP32): + mul, noConversion, + noConversion>(inData, params, blobs, outData); + break; + default: + THROW_IE_EXCEPTION << "Unsupported precision!"; + } } - } }; - } // namespace ShapeInfer } // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_onehot_const_infer.hpp b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_onehot_const_infer.hpp index ef96d577411dd7..de159e82189f68 100644 --- a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_onehot_const_infer.hpp +++ b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_onehot_const_infer.hpp @@ -11,6 +11,7 @@ #include #include #include "ie_const_infer_impl.hpp" +#include "precision_utils.h" namespace InferenceEngine { namespace ShapeInfer { @@ -22,10 +23,10 @@ class OneHotConstInfer : public ConstInferImpl { public: explicit OneHotConstInfer(const std::string& type) : ConstInferImpl(type) {} - void inferImpl(const std::vector& inData, - const std::map& params, - const std::map& blobs, - std::vector& outData) override { + template + void inferImplBody(const std::vector& inData, + const std::map& params, + std::vector& outData) { OneHotLayer layer(LayerParams {}); layer.params = params; layer.type = _type; @@ -33,8 +34,8 @@ class OneHotConstInfer : public ConstInferImpl { _validator->checkParams(&layer); auto src_dims = inData[0]->getTensorDesc().getDims(); - const auto *src_data = inData[0]->cbuffer().as(); - auto *dst_data = outData[0]->buffer().as(); + const auto *src_data = inData[0]->cbuffer().as(); + auto *dst_data = outData[0]->buffer().as(); std::size_t prefix_size = 1; auto input_dims = inData[0]->getTensorDesc().getDims(); @@ -49,12 +50,68 @@ class OneHotConstInfer : public ConstInferImpl { for (std::size_t depth_idx = 0; depth_idx < layer.depth; ++depth_idx) { for (std::size_t suffix_idx = 0; suffix_idx < suffix_size; suffix_idx++) { auto src_index = prefix_idx * suffix_size + suffix_idx; - std::size_t v = static_cast(src_data[src_index]); + auto v = static_cast(src_data[src_index]); dst_data[dst_offset++] = (v == depth_idx) ? layer.on_value : layer.off_value; } } } } + + void inferImplBody_fp16(const std::vector& inData, + const std::map& params, + std::vector& outData) { + OneHotLayer layer(LayerParams {}); + layer.params = params; + layer.type = _type; + _validator->parseParams(&layer); + _validator->checkParams(&layer); + auto src_dims = inData[0]->getTensorDesc().getDims(); + + const auto *src_data = inData[0]->cbuffer().as(); + auto *dst_data = outData[0]->buffer().as(); + std::size_t prefix_size = 1; + auto input_dims = inData[0]->getTensorDesc().getDims(); + + std::size_t actual_axis = (layer.axis == -1) ? src_dims.size() : layer.axis; + for (size_t i = 0; i < actual_axis; ++i) + prefix_size *= input_dims[i]; + + std::size_t suffix_size = inData[0]->size() / prefix_size; + + int16_t val_on = PrecisionUtils::f32tof16(layer.on_value); + int16_t val_off = PrecisionUtils::f32tof16(layer.off_value); + + std::size_t dst_offset = 0; + for (std::size_t prefix_idx = 0; prefix_idx < prefix_size; ++prefix_idx) { + for (std::size_t depth_idx = 0; depth_idx < layer.depth; ++depth_idx) { + for (std::size_t suffix_idx = 0; suffix_idx < suffix_size; suffix_idx++) { + auto src_index = prefix_idx * suffix_size + suffix_idx; + auto v = static_cast(src_data[src_index]); + dst_data[dst_offset++] = (v == depth_idx) ? val_on : val_off; + } + } + } + } + + void inferImpl(const std::vector& inData, + const std::map& params, + const std::map& blobs, + std::vector& outData) override { + auto inputBlob = inData.front(); + Precision precision = inputBlob->getTensorDesc().getPrecision(); + switch (precision) { + case Precision::FP32: inferImplBody::value_type>(inData, params, outData); break; + case Precision::FP16: inferImplBody_fp16(inData, params, outData); break; + case Precision::Q78: inferImplBody::value_type>(inData, params, outData); break; + case Precision::I16: inferImplBody::value_type>(inData, params, outData); break; + case Precision::U8: inferImplBody::value_type>(inData, params, outData); break; + case Precision::I8: inferImplBody::value_type>(inData, params, outData); break; + case Precision::U16: inferImplBody::value_type>(inData, params, outData); break; + case Precision::I32: inferImplBody::value_type>(inData, params, outData); break; + case Precision::I64: inferImplBody::value_type>(inData, params, outData); break; + default: THROW_IE_EXCEPTION << "OneHot const inference: Unsupported precision " << precision.name(); + } + } }; } // namespace ShapeInfer diff --git a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_permute_const_infer.hpp b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_permute_const_infer.hpp new file mode 100644 index 00000000000000..bf88a027a3acc1 --- /dev/null +++ b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_permute_const_infer.hpp @@ -0,0 +1,75 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ie_const_infer_impl.hpp" +#include "ie_parallel.hpp" +#include "../../precision_utils.h" + +namespace InferenceEngine { +namespace ShapeInfer { + +/** + *@brief Implementation of Const inference for Broadcast layer + */ +class PermuteConstInfer : public ConstInferImpl { +public: + explicit PermuteConstInfer(const std::string& type) : ConstInferImpl(type) {} + + void inferImpl(const std::vector& inData, + const std::map& params, + const std::map& blobs, + std::vector& outData) override { + LayerParams lp{}; + CNNLayer layer(lp); + layer.params = params; + + if (outData.empty()) + THROW_IE_EXCEPTION << "Incorrect number of input/output edges!"; + + if (inData.size() != 1) + THROW_IE_EXCEPTION << "Incorrect number of input edges!"; + + if (inData[0]->getTensorDesc().getPrecision() != outData[0]->getTensorDesc().getPrecision()) { + THROW_IE_EXCEPTION + << "Input and output tensors should have same precision!"; + } + + std::vector order; + std::vector layerOrder = layer.GetParamAsInts("order"); + for (auto ord : layerOrder) + order.push_back(static_cast(ord)); + + TensorDesc srcDesc = inData[0]->getTensorDesc(); + + SizeVector& dims = srcDesc.getDims(); + InferenceEngine::SizeVector orderedDims; + for (auto ord : order) { + orderedDims.push_back(dims[ord]); + } + TensorDesc dstDesc(InferenceEngine::Precision::FP32, dims, {orderedDims, order}); + + size_t dataSize = inData[0]->size(); + const auto * src_data = inData[0]->cbuffer().as(); + auto * dst_data = outData[0]->buffer().as(); + + parallel_for(dataSize, [&](size_t i) { + memcpy(dst_data + dstDesc.offset(i)*outData[0]->element_size(), + src_data + srcDesc.offset(i)*inData[0]->element_size(), + inData[0]->element_size()); + }); + } +}; + +} // namespace ShapeInfer +} // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_pow_const_infer.hpp b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_pow_const_infer.hpp new file mode 100644 index 00000000000000..a16af70184166c --- /dev/null +++ b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_pow_const_infer.hpp @@ -0,0 +1,99 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "broadcast_offset.hpp" + +namespace InferenceEngine { +namespace ShapeInfer { +class PowConstInfer : public ConstInferImpl { +public: + explicit PowConstInfer(const std::string &type) : ConstInferImpl(type) {} + + struct fp16tofp32{ + inline float operator()(ie_fp16 value){ + return static_cast(PrecisionUtils::f16tof32(value)); + } + }; + + struct fp32tofp16{ + inline ie_fp16 operator()(float value){ + return static_cast(PrecisionUtils::f32tof16(value)); + } + }; + + template + struct noConversion{ + inline dataType operator()(dataType value){ + return value; + } + }; + + template + void pow(const std::vector &inData, + const std::map ¶ms, + const std::map &blobs, + std::vector &outData) { + auto* firstBlobBuffer = inData[0]->cbuffer().as(); + auto* secondBlobBuffer = inData[1]->cbuffer().as(); + if (!firstBlobBuffer || !secondBlobBuffer) { + THROW_IE_EXCEPTION << "empty input data"; + } + + auto outBlob = *outData.begin(); + auto* outBuffer = outBlob->buffer().as(); + if (!outBuffer) THROW_IE_EXCEPTION << "empty output data"; + + BroadcastOffset outOff(outBlob->getTensorDesc().getDims(), outBlob->getTensorDesc().getDims()); + BroadcastOffset inOff1(inData[0]->getTensorDesc().getDims(), outBlob->getTensorDesc().getDims()); + BroadcastOffset inOff2(inData[1]->getTensorDesc().getDims(), outBlob->getTensorDesc().getDims()); + for (size_t i = 0; i < outBlob->size(); i++) { + SizeVector offsetDims = outOff.offset_dims(i); + outBuffer[outOff.offset(offsetDims)] = ConversionOutData()( + std::pow(ConversionInData1()(firstBlobBuffer[inOff1.offset(offsetDims)]), + ConversionInData2()(secondBlobBuffer[inOff2.offset(offsetDims)]))); + } + } + + void inferImpl(const std::vector &inData, + const std::map ¶ms, + const std::map &blobs, + std::vector &outData) override { + size_t numInputs = inData.size(); + if (inData.size() != 2) + THROW_IE_EXCEPTION << "Unsupported number of inputs: " << numInputs << ". 2 inputs is supported"; + + auto compare = getPrecisionMask(inData[0]->getTensorDesc().getPrecision(), inData[1]->getTensorDesc().getPrecision(), + outData[0]->getTensorDesc().getPrecision()); + switch (compare) { + case getPrecisionMask(Precision::FP32, Precision::FP32, Precision::FP32): + pow, noConversion, noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I32, Precision::I32, Precision::FP32): + pow, noConversion, noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::FP16, Precision::FP16, Precision::FP16): + pow, noConversion, noConversion>(inData, params, blobs, outData); + break; + case getPrecisionMask(Precision::I32, Precision::I32, Precision::FP16): + pow, noConversion, fp32tofp16>(inData, params, blobs, outData); + break; + default: + THROW_IE_EXCEPTION << "Not supported data type in port 0"; + } + } +}; +} // namespace ShapeInfer +} // namespace InferenceEngine \ No newline at end of file diff --git a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_reduce_const_infer.hpp b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_reduce_const_infer.hpp new file mode 100644 index 00000000000000..50b85fd0931e5d --- /dev/null +++ b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_reduce_const_infer.hpp @@ -0,0 +1,374 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ie_const_infer_impl.hpp" +#include "ie_parallel.hpp" + +namespace InferenceEngine { +namespace ShapeInfer { + +/** + *@brief Implementation of Const inference for Reduce layer + */ +class ReduceConstInfer : public ConstInferImpl { +private: + const size_t REDUCE_DATA = 0; + const size_t REDUCE_INDEXES = 1; + +template +void reduce( + SizeVector src_dims, + SizeVector srcStrides, + const src_t *src_data, + dst_t *dst_data, + size_t work_amount_dst, + size_t reduced_dims_work_amount, + SizeVector axes_for_reduction, + SizeVector dst_dims, + dst_t init_value, + std::string reduceType +) { + // I don't know why func 2 is necessary! + std::function func1; + std::function func2; + if (reduceType == "ReduceAnd") { + func1 = [](dst_t x, src_t y) -> dst_t { return x && y; }; + func2 = [](dst_t x, src_t y) -> dst_t { return x && y; }; + } else if (reduceType == "ReduceL1") { + func1 = [](dst_t x, src_t y) -> dst_t { return x + (std::abs)(y); }; + func2 = [](dst_t x, src_t y) -> dst_t { return x + y; }; + } else if (reduceType == "ReduceL2") { + func1 = [](dst_t x, src_t y) -> dst_t { return x + y * y; }; + func2 = [](dst_t x, src_t y) -> dst_t { return x + y; }; + } else if (reduceType == "ReduceLogSum") { + func1 = [](dst_t x, src_t y)->dst_t { return x + y; }; + func2 = [](dst_t x, src_t y)->dst_t { return x + y; }; + } else if (reduceType == "ReduceLogSumExp") { + func1 = [](dst_t x, src_t y)->dst_t { return x + expf(y); }; + func2 = [](dst_t x, src_t y)->dst_t { return x + y; }; + } else if (reduceType == "ReduceMax") { + func1 = [](dst_t x, src_t y)->dst_t { return x > y ? x : y; }; + func2 = [](dst_t x, src_t y)->dst_t { return x > y ? x : y; }; + } else if (reduceType == "ReduceMean") { + func1 = [](dst_t x, src_t y) -> dst_t { return (x + y); }; + func2 = [](dst_t x, src_t y) -> dst_t { return (x + y); }; + } else if (reduceType == "ReduceMin") { + func1 = [](dst_t x, src_t y)->dst_t { return x < y ? x : y; }; + func2 = [](dst_t x, src_t y)->dst_t { return x < y ? x : y; }; + } else if (reduceType == "ReduceOr") { + func1 = [](dst_t x, src_t y) -> dst_t { return x || y; }; + func2 = [](dst_t x, src_t y) -> dst_t { return x || y; }; + } else if (reduceType == "ReduceProd") { + func1 = [](dst_t x, src_t y)->dst_t { return x * y; }; + func2 = [](dst_t x, src_t y)->dst_t { return x * y; }; + } else if (reduceType == "ReduceSum") { + func1 = [](dst_t x, src_t y)->dst_t { return x + y; }; + func2 = [](dst_t x, src_t y)->dst_t { return x + y; }; + } else if (reduceType == "ReduceSumSquare") { + func1 = [](dst_t x, src_t y) -> dst_t { return x + y * y; }; + func2 = [](dst_t x, src_t y) -> dst_t { return x + y; }; + } + + unsigned int nthr = parallel_get_max_threads(); + if ((work_amount_dst + 1) >= nthr) { + parallel_nt(0, [&](const int ithr, const int nthr) { + int j; + size_t i, start = 0, end = 0; + SizeVector dst_counters(dst_dims.size(), 0); + splitter(work_amount_dst, nthr, ithr, start, end); + for (j = dst_dims.size() - 1, i = start; j >= 0; j--) { + dst_counters[j] = i % dst_dims[j]; + i /= dst_dims[j]; + } + for (size_t src_idx, dst_idx = start; dst_idx < end; ++dst_idx) { + dst_t reduce_prod = init_value; + bool update_idx = true; + SizeVector src_counters = dst_counters; + for (i = 0; i < reduced_dims_work_amount; ++i) { + if (update_idx) { + src_idx = 0; + for (j = 0; j < static_cast(src_dims.size()); ++j) + src_idx += (src_counters[j] % src_dims[j]) * srcStrides[j]; + update_idx = false; + } + reduce_prod = func1(reduce_prod, src_data[src_idx]); + for (j = axes_for_reduction.size() - 1; j >= 0; j--) { + src_counters[axes_for_reduction[j]]++; + if (src_counters[axes_for_reduction[j]] < src_dims[axes_for_reduction[j]]) { + src_idx += srcStrides[axes_for_reduction[j]]; + break; + } else { + src_counters[axes_for_reduction[j]] = 0; + update_idx = true; + } + } + } + dst_data[dst_idx] = reduce_prod; + for (j = dst_dims.size() - 1; j >= 0; j--) { + dst_counters[j]++; + if (dst_counters[j] < dst_dims[j]) + break; + else + dst_counters[j] = 0; + } + } + }); + } else { + std::vector reduce_prod((nthr * work_amount_dst), init_value); + if (work_amount_dst == 1) { + parallel_nt(nthr, [&](const int ithr, const int nthr) { + size_t i, start = 0, end = 0; + splitter((srcStrides[0] * src_dims[0]), nthr, ithr, start, end); + for (i = start; i < end; ++i) + reduce_prod[ithr] = func1(reduce_prod[ithr], src_data[i]); + }); + } else { + SizeVector dstStrides(dst_dims.size(), 1); + for (int j = dst_dims.size() - 1; j >= 1; --j) + dstStrides[j - 1] = dstStrides[j] * dst_dims[j]; + parallel_nt(nthr, [&](const int ithr, const int nthr) { + int j; + bool update_idx = true; + size_t i, src_idx, dst_idx = 0, start = 0, end = 0; + splitter((srcStrides[0] * src_dims[0]), nthr, ithr, start, end); + SizeVector src_counters(src_dims.size(), 0); + for (j = src_dims.size() - 1, src_idx = start; j >= 0; j--) { + src_counters[j] = src_idx % src_dims[j]; + src_idx /= src_dims[j]; + } + for (src_idx = start; src_idx < end; ++src_idx) { + if (update_idx) { + for (i = 0, dst_idx = 0; i < dst_dims.size(); ++i) + dst_idx += (src_counters[i] % dst_dims[i]) * dstStrides[i]; + update_idx = false; + } + reduce_prod[ithr * work_amount_dst + dst_idx] = func1(reduce_prod[ithr * work_amount_dst + dst_idx], src_data[src_idx]); + for (j = src_dims.size() - 1; j >= 0; j--) { + src_counters[j]++; + if (src_counters[j] < src_dims[j]) { + if (dst_dims[j] > 1) dst_idx += dstStrides[j]; + break; + } else { + src_counters[j] = 0; + update_idx = true; + } + } + } + }); + } + for (size_t dst_idx = 0; dst_idx < work_amount_dst; dst_idx++) { + for (size_t ithr = work_amount_dst; ithr < (nthr * work_amount_dst); ithr += work_amount_dst) + reduce_prod[dst_idx] = func2(reduce_prod[dst_idx], reduce_prod[dst_idx + ithr]); + dst_data[dst_idx] = reduce_prod[dst_idx]; + } + } +} + +template +void exec_reduce(const std::vector& insData, std::vector& outData, std::string reduce_mode, + SizeVector src_dims, + SizeVector srcStrides, + size_t work_amount_dst, + size_t reduced_dims_work_amount, + SizeVector axes_for_reduction, + SizeVector our_dims, + dst_d min_val, + dst_d max_val) { + const src_d *src_data = insData[REDUCE_DATA]->cbuffer().as() + + insData[REDUCE_DATA]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + dst_d* dst_data = outData[0]->cbuffer().as() + + outData[0]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + if (reduce_mode == "ReduceAnd") { + reduce(src_dims, srcStrides, src_data, dst_data, work_amount_dst, reduced_dims_work_amount, axes_for_reduction, our_dims, 1, reduce_mode); + } else if (reduce_mode == "ReduceL1") { + reduce(src_dims, srcStrides, src_data, dst_data, work_amount_dst, reduced_dims_work_amount, axes_for_reduction, our_dims, 0, reduce_mode); + } else if (reduce_mode == "ReduceL2") { + reduce(src_dims, srcStrides, src_data, dst_data, work_amount_dst, reduced_dims_work_amount, axes_for_reduction, our_dims, 0, reduce_mode); + + parallel_for(work_amount_dst, [&](size_t i) { + dst_data[i] = sqrt(dst_data[i]); + }); + } else if (reduce_mode == "ReduceLogSum") { + reduce(src_dims, srcStrides, src_data, dst_data, work_amount_dst, reduced_dims_work_amount, axes_for_reduction, our_dims, 0, + reduce_mode); + + parallel_for(work_amount_dst, [&](size_t i) { + dst_data[i] = logf(dst_data[i]); + }); + } else if (reduce_mode == "ReduceLogSumExp") { + reduce(src_dims, srcStrides, src_data, dst_data, work_amount_dst, reduced_dims_work_amount, axes_for_reduction, our_dims, 0, + reduce_mode); + + parallel_for(work_amount_dst, [&](size_t i) { + dst_data[i] = logf(dst_data[i]); + }); + } else if (reduce_mode == "ReduceMax") { + reduce(src_dims, srcStrides, src_data, dst_data, work_amount_dst, reduced_dims_work_amount, axes_for_reduction, our_dims, min_val, + reduce_mode); + } else if (reduce_mode == "ReduceMean") { + reduce(src_dims, srcStrides, src_data, dst_data, work_amount_dst, reduced_dims_work_amount, axes_for_reduction, our_dims, 0, + reduce_mode); + + parallel_for(work_amount_dst, [&](size_t i) { + dst_data[i] /= static_cast(reduced_dims_work_amount); + }); + } else if (reduce_mode == "ReduceMin") { + reduce(src_dims, srcStrides, src_data, dst_data, work_amount_dst, reduced_dims_work_amount, axes_for_reduction, our_dims, max_val, + reduce_mode); + } else if (reduce_mode == "ReduceOr") { + reduce(src_dims, srcStrides, src_data, dst_data, work_amount_dst, reduced_dims_work_amount, axes_for_reduction, our_dims, 0, + reduce_mode); + } else if (reduce_mode == "ReduceProd") { + reduce(src_dims, srcStrides, src_data, dst_data, work_amount_dst, reduced_dims_work_amount, axes_for_reduction, our_dims, 1, + reduce_mode); + } else if (reduce_mode == "ReduceSum") { + reduce(src_dims, srcStrides, src_data, dst_data, work_amount_dst, reduced_dims_work_amount, axes_for_reduction, our_dims, 0, + reduce_mode); + } else if (reduce_mode == "ReduceSumSquare") { + reduce(src_dims, srcStrides, src_data, dst_data, work_amount_dst, reduced_dims_work_amount, axes_for_reduction, our_dims, 0, + reduce_mode); + } else { + THROW_IE_EXCEPTION << " Incorrect Reduce layer type!"; + } +} + +public: + explicit ReduceConstInfer(const std::string& type) : ConstInferImpl(type) {} + + void inferImpl(const std::vector& insData, + const std::map& params, + const std::map& blobs, + std::vector& outData) override { + LayerParams lp{"", _type}; + CNNLayer layer(lp); + layer.params = params; + + if (insData.empty() || outData.empty()) + THROW_IE_EXCEPTION << " Reduce constant inference error: empty input or output data!"; + + if (insData.size() != 2) + THROW_IE_EXCEPTION << " Reduce constant inference error: Incorrect number of input edges! Should be 2 edges, got " << insData.size(); + + SizeVector idx_dims = insData[REDUCE_INDEXES]->getTensorDesc().getDims(); + if (idx_dims.size() > 1) + THROW_IE_EXCEPTION << " Reduce constant inference error: Index vector should be 1 dimension, got " << idx_dims.size() << " dimensions"; + + if (insData[REDUCE_INDEXES]->getTensorDesc().getPrecision() != Precision::I32) + THROW_IE_EXCEPTION << " Reduce constant inference error: Incorrect 'axes_to_reduction' input precision. Only I32 is supported! Current precision: " + << insData[REDUCE_INDEXES]->getTensorDesc().getPrecision(); + + SizeVector data_dims = insData[REDUCE_DATA]->getTensorDesc().getDims(); + SizeVector dst_dims = outData[0]->getTensorDesc().getDims(); + + bool keep_dims = layer.GetParamAsBool("keep_dims", true); + if (keep_dims) { + if (data_dims.size() != dst_dims.size()) + THROW_IE_EXCEPTION << " Reduce constant inference error: Incorrect number of input/output dimensions!"; + } else { + if (data_dims.size() <= dst_dims.size()) + THROW_IE_EXCEPTION << " Reduce constant inference error: Incorrect number of input/output dimensions!"; + } + + SizeVector src_dims = insData[REDUCE_DATA]->getTensorDesc().getDims(); + SizeVector srcStrides = insData[REDUCE_DATA]->getTensorDesc().getBlockingDesc().getStrides(); + + int32_t *idx_data = insData[REDUCE_INDEXES]->cbuffer().as() + + insData[REDUCE_INDEXES]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + SizeVector axes; + for (size_t i = 0; i < idx_dims[0]; i++) { + int32_t axis = idx_data[i]; + if (axis < 0) + axis += data_dims.size(); + + if (static_cast(axis) > data_dims.size()) + THROW_IE_EXCEPTION << " Reduce constant inference error: Index to reduce exceeds data tensor dimension"; + axes.push_back(static_cast(axis)); + } + + size_t reduced_dims_work_amount = 1; + InferenceEngine::SizeVector our_dims, out_dims, axes_for_reduction; + for (size_t i = 0; i < src_dims.size(); i++) { + bool found = false; + for (size_t axis : axes) + if (i == axis) found = true; + + if (found) { + axes_for_reduction.push_back(i); + reduced_dims_work_amount *= src_dims[i]; + if (keep_dims) out_dims.push_back(1); + our_dims.push_back(1); + } else { + out_dims.push_back(src_dims[i]); + our_dims.push_back(src_dims[i]); + } + } + + if (!our_dims.size()) + our_dims = SizeVector(1, 1); + + for (size_t i = 0; i < (std::min)(out_dims.size(), dst_dims.size()); i++) + if (out_dims[i] != dst_dims[i]) + THROW_IE_EXCEPTION << " Reduce constant inference error: Incorrect number of output dimensions!"; + + size_t work_amount_dst; + if (!dst_dims.size()) + work_amount_dst = 1; + else + work_amount_dst = outData[0]->getTensorDesc().getBlockingDesc().getStrides()[0] * dst_dims[0]; + + std::string reduce_mode = layer.type; + + auto compare = getPrecisionMask(insData[REDUCE_DATA]->getTensorDesc().getPrecision(), outData[0]->getTensorDesc().getPrecision()); + switch (compare) { + case getPrecisionMask(Precision::FP32, Precision::FP32): + exec_reduce::value_type, PrecisionTrait::value_type>( + insData, outData, reduce_mode, src_dims, srcStrides, work_amount_dst, + reduced_dims_work_amount, axes_for_reduction, dst_dims, + (std::numeric_limits::value_type>::min)(), + (std::numeric_limits::value_type>::max)()); + break; + + case getPrecisionMask(Precision::I32, Precision::I64): + exec_reduce::value_type, PrecisionTrait::value_type>( + insData, outData, reduce_mode, src_dims, srcStrides, work_amount_dst, + reduced_dims_work_amount, axes_for_reduction, dst_dims, + (std::numeric_limits::value_type>::min)(), + (std::numeric_limits::value_type>::max)()); + break; + case getPrecisionMask(Precision::I32, Precision::FP32): + exec_reduce::value_type, PrecisionTrait::value_type>( + insData, outData, reduce_mode, src_dims, srcStrides, work_amount_dst, + reduced_dims_work_amount, axes_for_reduction, dst_dims, + (std::numeric_limits::value_type>::min)(), + (std::numeric_limits::value_type>::max)()); + break; + case getPrecisionMask(Precision::I32, Precision::I32): + exec_reduce::value_type, PrecisionTrait::value_type>( + insData, outData, reduce_mode, src_dims, srcStrides, work_amount_dst, + reduced_dims_work_amount, axes_for_reduction, dst_dims, + (std::numeric_limits::value_type>::min)(), + (std::numeric_limits::value_type>::max)()); + break; + default: + THROW_IE_EXCEPTION << "Reduce constant inference error: Incorrect data tensor precisions. REDUCE_DATA precision: " << + insData[REDUCE_DATA]->getTensorDesc().getPrecision() << + " Output precision: " << outData[0]->getTensorDesc().getPrecision(); + } + } +}; + +} // namespace ShapeInfer +} // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_shape_const_infer.hpp b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_shape_const_infer.hpp index c5da316ede7d4e..3360dd338945e0 100644 --- a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_shape_const_infer.hpp +++ b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_shape_const_infer.hpp @@ -10,7 +10,7 @@ #include #include #include -#include "../../precision_utils.h" +#include "precision_utils.h" namespace InferenceEngine { namespace ShapeInfer { @@ -35,6 +35,16 @@ class ShapeConstInfer : public ConstInferImpl { for (int i = 0; i < outBlob->size(); i++) { outBuffer[i] = PrecisionUtils::f32tof16(static_cast(inShape[i])); } + } else if (outBlob->getTensorDesc().getPrecision() == Precision::I32) { + auto* outBuffer = outBlob->buffer().as(); + for (int i = 0; i < outBlob->size(); i++) { + outBuffer[i] = static_cast(inShape[i]); + } + } else if (outBlob->getTensorDesc().getPrecision() == Precision::I64) { + auto* outBuffer = outBlob->buffer().as(); + for (int i = 0; i < outBlob->size(); i++) { + outBuffer[i] = static_cast(inShape[i]); + } } else { auto* outBuffer = outBlob->buffer().as(); for (int i = 0; i < outBlob->size(); i++) { diff --git a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_strided_slice_const_infer.hpp b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_strided_slice_const_infer.hpp index d9e7682016873d..45b6d581b5ece5 100644 --- a/inference-engine/src/inference_engine/shape_infer/const_infer/ie_strided_slice_const_infer.hpp +++ b/inference-engine/src/inference_engine/shape_infer/const_infer/ie_strided_slice_const_infer.hpp @@ -12,6 +12,7 @@ #include #include #include +#include "ie_precision.hpp" #include "ie_const_infer_impl.hpp" #include "ie_parallel.hpp" @@ -26,11 +27,8 @@ class StridedSliceHelper { CNNLayer layer(lp); layer.params = params; - src_data = inData[STRIDEDSLICE_DATA]->cbuffer().as() + - inData[STRIDEDSLICE_DATA]->getTensorDesc().getBlockingDesc().getOffsetPadding(); - if (inData.size() > 4) - THROW_IE_EXCEPTION << " Incorrect number of input/output edges!"; + THROW_IE_EXCEPTION << "StridedSlice constant inference error: Incorrect number of input edges!"; src_dims = inData[STRIDEDSLICE_DATA]->getTensorDesc().getDims(); @@ -38,30 +36,33 @@ class StridedSliceHelper { if (inData.size() > 1) { begin_dims = inData[STRIDEDSLICE_BEGIN]->getTensorDesc().getDims(); if (inData[STRIDEDSLICE_BEGIN]->getTensorDesc().getPrecision() != Precision::I32) - THROW_IE_EXCEPTION << " Incorrect 'begin' input precision. Only I32 is supported!"; + THROW_IE_EXCEPTION << "StridedSlice constant inference error: Incorrect 'begin' input precision. Only I32 is supported! Current precision: " << + inData[STRIDEDSLICE_BEGIN]->getTensorDesc().getPrecision(); if (begin_dims.size() > 1) - THROW_IE_EXCEPTION << " Begin vector should be 1 dimension"; + THROW_IE_EXCEPTION << "StridedSlice constant inference error: Begin vector should be 1 dimension, got: " << begin_dims.size() << " dimensions"; bounds_size = begin_dims[0]; } if (inData.size() > 2) { end_dims = inData[STRIDEDSLICE_END]->getTensorDesc().getDims(); if (inData[STRIDEDSLICE_END]->getTensorDesc().getPrecision() != Precision::I32) - THROW_IE_EXCEPTION << " Incorrect 'end' input precision. Only I32 is supported!"; + THROW_IE_EXCEPTION << "StridedSlice constant inference error: Incorrect 'end' input precision. Only I32 is supported! Current precision: " << + inData[STRIDEDSLICE_END]->getTensorDesc().getPrecision(); if (end_dims.size() > 1) - THROW_IE_EXCEPTION << " End vector should be 1 dimension"; + THROW_IE_EXCEPTION << "StridedSlice constant inference error: End vector should be 1 dimension, got: " << end_dims.size() << " dimensions"; if (begin_dims[0] != end_dims[0]) - THROW_IE_EXCEPTION << " Begin vector size should be equal end vectror size"; + THROW_IE_EXCEPTION << "StridedSlice constant inference error: Begin vector size should be equal end vector size"; } if (inData.size() > 3) { stride_dims = inData[STRIDEDSLICE_STRIDE]->getTensorDesc().getDims(); if (inData[STRIDEDSLICE_STRIDE]->getTensorDesc().getPrecision() != Precision::I32) - THROW_IE_EXCEPTION << " Incorrect 'strides' input precision. Only I32 is supported!"; + THROW_IE_EXCEPTION << "StridedSlice constant inference error: Incorrect 'strides' input precision. Only I32 is supported! Current precision: " + << inData[STRIDEDSLICE_STRIDE]->getTensorDesc().getPrecision(); if (stride_dims.size() > 1) - THROW_IE_EXCEPTION << " End vector should be 1 dimension"; + THROW_IE_EXCEPTION << "StridedSlice constant inference error: End vector should be 1 dimension, got: " << stride_dims.size() << " dimensions"; if (begin_dims[0] != stride_dims[0]) - THROW_IE_EXCEPTION << " Stride vector size should be equal begin vectror size"; + THROW_IE_EXCEPTION << "StridedSlice constant inference error: Stride vector size should be equal begin vector size"; } std::string::size_type i; @@ -209,35 +210,60 @@ class StridedSliceHelper { return out_dims; } - void infer(std::vector& outData) { + template + void exec_strided_slice(const std::vector& inData, std::vector& outData) { + const src_t* src_data = inData[STRIDEDSLICE_DATA]->cbuffer().as() + + inData[STRIDEDSLICE_DATA]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + + dst_t* dst_data = outData[0]->cbuffer().as() + + outData[0]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + + if (src_dims.size() == max_dims && shrink_axis == 0 && stride_dms[stride_dms.size() - 1] == 1 && + stride_dms.size() > 1) + strided_slice_vp(src_data, dst_data); + else if (src_dims.size() == max_dims && shrink_axis == 0) + strided_slice_p(src_data, dst_data); + else + strided_slice(src_data, dst_data, our_dims); + } + + void infer(const std::vector& inData, std::vector& outData) { dst_dims = outData[0]->getTensorDesc().getDims(); size_t range = out_dims.size() < dst_dims.size() ? out_dims.size() : dst_dims.size(); for (int i = 0; i < range; i++) { if (out_dims[i] != dst_dims[i]) - THROW_IE_EXCEPTION << "parameter mismatch"; + THROW_IE_EXCEPTION << "StridedSlice constant inference error: parameter mismatch"; } dstStrides = outData[0]->getTensorDesc().getBlockingDesc().getStrides(); if (dst_dims.size() == 1 && dst_dims[0] == 1) dstStrides.push_back(1); if (outData.size() != 1) - THROW_IE_EXCEPTION << " Incorrect number of input/output edges!"; - float* dst_data = outData[0]->cbuffer().as() + - outData[0]->getTensorDesc().getBlockingDesc().getOffsetPadding(); + THROW_IE_EXCEPTION << "StridedSlice constant inference error: Incorrect number of output edges!"; - if (src_dims.size() == max_dims && shrink_axis == 0 && stride_dms[stride_dms.size() - 1] == 1 && - stride_dms.size() > 1) - strided_slice_vp(src_data, dst_data); - else if (src_dims.size() == max_dims && shrink_axis == 0) - strided_slice_p(src_data, dst_data); - else - strided_slice(src_data, dst_data, our_dims); + auto compare = getPrecisionMask(inData[0]->getTensorDesc().getPrecision(), outData[0]->getTensorDesc().getPrecision()); + switch (compare) { + case getPrecisionMask(Precision::FP32, Precision::FP32): + exec_strided_slice::value_type, PrecisionTrait::value_type>(inData, outData); + break; + case getPrecisionMask(Precision::I32, Precision::I32): + exec_strided_slice::value_type, PrecisionTrait::value_type>(inData, outData); + break; + case getPrecisionMask(Precision::I32, Precision::I64): + exec_strided_slice::value_type, PrecisionTrait::value_type>(inData, outData); + break; + default: + THROW_IE_EXCEPTION << "StridedSlice constant inference error: Unsupported precision configuration:" << + " input precision: " << inData[0]->getTensorDesc().getPrecision() << + " output precision: " << outData[0]->getTensorDesc().getPrecision(); + } } private: - void strided_slice(const float* src_data, float* dst_data, std::vector& dims) { + template + void strided_slice(const src_t* src_data, dst_t* dst_data, std::vector& dims) { size_t i; int j; - size_t work_amount_dst = dstStrides[0] * dst_dims[0]; + size_t work_amount_dst = (dstStrides.empty() && dst_dims.empty()) ? 1 : dstStrides[0] * dst_dims[0]; SizeVector counters(max_dims, 0); for (size_t iwork = 0; iwork < work_amount_dst; ++iwork) { @@ -259,7 +285,8 @@ class StridedSliceHelper { } } - void strided_slice_vp(const float* src_data, float* dst_data) { + template + void strided_slice_vp(const src_t* src_data, dst_t* dst_data) { // Vectorized copy size_t dims_size_1 = dst_dims.size() - 1; size_t dataLength = dst_dims[dims_size_1]; @@ -296,7 +323,8 @@ class StridedSliceHelper { }); } - void strided_slice_p(const float* src_data, float* dst_data) { + template + void strided_slice_p(const src_t* src_data, dst_t* dst_data) { size_t dims_size = dst_dims.size(); size_t work_amount_dst = dstStrides[0] * dst_dims[0]; @@ -360,7 +388,6 @@ class StridedSliceHelper { InferenceEngine::SizeVector out_dims; InferenceEngine::SizeVector our_dims; - const float* src_data; }; /** @@ -381,7 +408,7 @@ class StridedSliceConstInfer : public ConstInferImpl { _validator->parseParams(&layer); StridedSliceHelper helper(inData, params); - helper.infer(outData); + helper.infer(inData, outData); } }; diff --git a/inference-engine/src/inference_engine/shape_infer/ie_reshape_launcher.cpp b/inference-engine/src/inference_engine/shape_infer/ie_reshape_launcher.cpp index 9834ad939a7183..722e65e21a838c 100644 --- a/inference-engine/src/inference_engine/shape_infer/ie_reshape_launcher.cpp +++ b/inference-engine/src/inference_engine/shape_infer/ie_reshape_launcher.cpp @@ -14,7 +14,6 @@ #include "shape_infer/ie_reshape_launcher.hpp" #include "shape_infer/ie_reshape_io_controllers.hpp" #include "ie_reshape_launcher.hpp" - #include "built-in/ie_tensor_iterator_shape_infer.hpp" using namespace InferenceEngine; @@ -332,4 +331,4 @@ void OutMemoryReshapeLauncher::applyChanges(CNNLayer* layer) { void OutMemoryReshapeLauncher::reset() { _iController->reset(); -} +} \ No newline at end of file diff --git a/inference-engine/src/inference_engine/transform/transform_network.cpp b/inference-engine/src/inference_engine/transform/transform_network.cpp deleted file mode 100644 index 923fa3f63d80aa..00000000000000 --- a/inference-engine/src/inference_engine/transform/transform_network.cpp +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include -#include -#include -#include -#include -#include - -using namespace InferenceEngine; - -Transform::Port::Port(Builder::Network& network, PortInfo port, bool isInput) - : network(network), port(port), input(isInput) { - const auto& layer = network.getLayer(port.layerId()); - if (isInput) { - if (layer->getInputPorts().size() < port.portId()) - THROW_IE_EXCEPTION << "Cannot find input port " - << port.portId() << " in layer " - << layer->getName(); - } else { - if (layer->getOutputPorts().size() < port.portId()) - THROW_IE_EXCEPTION << "Cannot find output port " - << port.portId() << " in layer " - << layer->getName(); - } -} - -PortData::Ptr Transform::Port::getData() const { - return input ? - network.getLayer(port.layerId())->getInputPorts()[port.portId()].getData() : - network.getLayer(port.layerId())->getOutputPorts()[port.portId()].getData(); -} - -const std::map &Transform::Port::getParameters() const { - return input ? - network.getLayer(port.layerId())->getInputPorts()[port.portId()].getParameters() : - network.getLayer(port.layerId())->getOutputPorts()[port.portId()].getParameters(); -} - -Transform::Layer Transform::Port::getLayer() const { - return Transform::Network(network).getLayer(getPortInfo().layerId()); -} - -Transform::Connection Transform::Port::getConnection() const { - return Connection(*this); -} - -void Transform::Port::connect(const Port& port) { - if (this->input) - this->getConnection().setSource(port); - else - this->getConnection().addDestination(port); -} - -void Transform::Port::disconnect() { - getConnection().remove(); -} - -const SizeVector& Transform::Port::shape() const { - return this->getData()->getData()->getTensorDesc().getDims(); -} - -PortInfo Transform::Port::getPortInfo() const { - return port; -} - -bool Transform::Port::operator==(const Port& rObj) const { - return &network == &rObj.network && - port == rObj.port && - input == rObj.input; -} - -bool Transform::Port::operator!=(const Port& rObj) const { - return !(*this == rObj); -} - - -Transform::Layer::Layer(Builder::Network& network, idx_t id) - : network(network), layerId(id) {} - -idx_t Transform::Layer::getId() const { - return layerId; -} - -std::string Transform::Layer::getName() const { - return getLayer()->getName(); -} - -std::string Transform::Layer::getType() const { - return getLayer()->getType(); -} - -Builder::Layer::Ptr Transform::Layer::getLayer() const { - return network.getLayer(layerId); -} - -Transform::Layer::operator Builder::Layer::Ptr() const { - return getLayer(); -} - -Transform::Port Transform::Layer::getInPort() const { - if (getLayer()->getInputPorts().size() != 1) - THROW_IE_EXCEPTION << "Layer " << getName() - << " has more than 1 input port."; - return Transform::Port(network, {layerId, 0}, true); -} - -Transform::Port Transform::Layer::getInPort(idx_t idx) const { - if (getLayer()->getInputPorts().size() <= idx) - THROW_IE_EXCEPTION << "Layer " << getName() - << " has less than " << idx << " input port(s)."; - return Transform::Port(network, {layerId, idx}, true); -} - -std::vector Transform::Layer::getInPorts() const { - std::vector ports; - for (size_t i = 0; i < getLayer()->getInputPorts().size(); i++) { - ports.push_back({network, {layerId, i}, true}); - } - return ports; -} - -Transform::Port Transform::Layer::getOutPort() const { - if (getLayer()->getOutputPorts().size() != 1) - THROW_IE_EXCEPTION << "Layer " << getName() - << " has more than 1 output port."; - return Transform::Port(network, {layerId, 0}, false); -} - -Transform::Port Transform::Layer::getOutPort(idx_t idx) const { - if (getLayer()->getOutputPorts().size() <= idx) - THROW_IE_EXCEPTION << "Layer " << getName() - << " has less than " << idx << " output port(s)."; - return Transform::Port(network, {layerId, idx}, false); -} - -std::vector Transform::Layer::getOutPorts() const { - std::vector ports; - for (size_t i = 0; i < getLayer()->getInputPorts().size(); i++) { - ports.push_back({network, {layerId, i}, false}); - } - return ports; -} - -void Transform::Layer::setParameter(const std::string& key, const Parameter& value) { - auto& params = getLayer()->getParameters(); - params[key] = value; -} - -Parameter& Transform::Layer::getParameter(const std::string& key) const { - auto& params = getLayer()->getParameters(); - if (params.find(key) == params.end()) - THROW_IE_EXCEPTION << "Layer " << getName() << " has no parameter " << key; - return params[key]; -} - -Transform::Connection::Connection(const Transform::Port& port) - : network(port.network), inPort({(std::numeric_limits::max)(), (std::numeric_limits::max)()}) { - if (port.input) { - outPorts = {port.getPortInfo()}; - for (const auto& connection : network.getLayerConnections(port.getPortInfo().layerId())) { - if (connection.to() == port.getPortInfo()) { - inPort = connection.from(); - break; - } - } - } else { - inPort = port.getPortInfo(); - for (const auto& connection : network.getLayerConnections(port.getPortInfo().layerId())) { - if (connection.from() == port.getPortInfo()) { - outPorts.emplace_back(connection.to()); - } - } - } -} -Transform::Connection::Connection(Builder::Network& network, const InferenceEngine::Connection& connection) - : Connection(network, connection.from(), connection.to()) {} -Transform::Connection::Connection(Builder::Network& network, const PortInfo& inPort, const PortInfo& outPort) - : Connection(network, inPort, std::vector({outPort})) {} -Transform::Connection::Connection(Builder::Network& network, const PortInfo& inPort, const std::vector& outPorts) - : network(network), inPort(inPort), outPorts(outPorts) {} - -Transform::Port Transform::Connection::getSource() const { - if (!inPortExist()) - THROW_IE_EXCEPTION << "Connection doesn't have source port!"; - return Port(network, inPort, false); -} - -void Transform::Connection::setSource(const Transform::Port &port) { - if (inPortExist()) { - // disconnect old port - for (const auto& outPort : outPorts) { - network.disconnect({inPort, outPort}); - } - } - inPort = port.getPortInfo(); - for (const auto& outPort : outPorts) { - network.connect(inPort, outPort); - } -} - -Transform::Port Transform::Connection::getDestination() const { - if (outPorts.size() != 1) - THROW_IE_EXCEPTION << "Connection has more than 1 output."; - return Transform::Port(network, outPorts[0], true); -} - -Transform::Port Transform::Connection::getDestination(idx_t idx) { - if (outPorts.size() <= idx) - THROW_IE_EXCEPTION << "Connection has less than " - << idx << " input port(s)."; - return Transform::Port(network, outPorts[idx], true); -} - -std::vector Transform::Connection::getDestinations() const { - std::vector ports; - for (const auto& port : outPorts) { - ports.emplace_back(network, port, true); - } - return ports; -} - -void Transform::Connection::addDestination(const Transform::Port &port) { - for (const auto& outPort : outPorts) { - if (outPort == port.getPortInfo()) { - THROW_IE_EXCEPTION << "Cannot connect twice with one port!"; - } - } - outPorts.emplace_back(port.getPortInfo()); - if (!inPortExist()) - return; - network.connect(inPort, outPorts[outPorts.size() - 1]); -} - -void Transform::Connection::setDestination(const Transform::Port &port) { - if (outPorts.size() > 1) { - THROW_IE_EXCEPTION << "Cannot set destination for connection which has more than 1 consumer." - << "Please use addDestination or setDestinations methods!"; - } - - if (!outPorts.empty()) { - if (inPortExist()) - network.disconnect({inPort, outPorts[0]}); - outPorts.clear(); - } - addDestination(port); -} - -void Transform::Connection::setDestinations(const std::vector &ports) { - if (!outPorts.empty() && outPorts.size() != ports.size()) - THROW_IE_EXCEPTION << "Cannot change number of output connections!"; - - if (inPortExist()) { - for (const auto &port : outPorts) { - network.disconnect({inPort, port}); - } - } - outPorts.clear(); - for (const auto &port : ports) { - addDestination(port); - } -} - -void Transform::Connection::remove() { - if (!inPortExist()) - return; - for (const auto& port : outPorts) { - network.disconnect({inPort, port}); - } -} - -bool Transform::Connection::inPortExist() const { - static PortInfo uninitPort((std::numeric_limits::max)(), (std::numeric_limits::max)()); - return inPort != uninitPort; -} - -Transform::Layer Transform::Network::addLayer(const Builder::Layer &layer) { - idx_t layerId = network.addLayer(layer); - return Transform::Layer(network, layerId); -} - -void Transform::Network::removeLayer(const Transform::Layer &layer) { - for (const auto& connection : network.getLayerConnections(layer.getId())) - network.disconnect(connection); - network.removeLayer(layer.getId()); -} - -Transform::Layer Transform::Network::getLayer(const std::string &name) const { - for (const auto& layer : network) { - if (layer->getName() == name) - return Transform::Layer(network, layer->getId()); - } - THROW_IE_EXCEPTION << "Layer with name: " << name << " was not found!"; -} - -Transform::Layer Transform::Network::getLayer(idx_t id) const { - for (const auto& layer : network) { - if (layer->getId() == id) - return Transform::Layer(network, layer->getId()); - } - THROW_IE_EXCEPTION << "Layer with id: " << id << " was not found!"; -} - -Transform::Connection Transform::Network::connect(const Transform::Layer &src, - const Transform::Layer &dst) { - Port srcPort = src.getOutPort(); - Port dstPort = dst.getInPort(); - - network.connect(srcPort.getPortInfo(), dstPort.getPortInfo()); - return Connection(network, srcPort.getPortInfo(), dstPort.getPortInfo()); -} - -Transform::Connection Transform::Network::connect(const Transform::Port &src, - const Transform::Port &dst) { - network.connect(src.getPortInfo(), dst.getPortInfo()); - return Connection(network, src.getPortInfo(), dst.getPortInfo()); -} - -void Transform::Network::disconnect(const Transform::Layer &src, const Transform::Layer &dst) { - getConnection(src, dst).remove(); -} - -void Transform::Network::disconnect(const Transform::Port &src, const Transform::Port &dst) { - getConnection(src, dst).remove(); -} - -Builder::Network& Transform::Network::getBuilderNetwork() const { - return network; -} - -Transform::Connection Transform::Network::getConnection(const Transform::Layer &src, - const Transform::Layer &dst) const { - Port srcPort = src.getOutPort(); - Port dstPort = dst.getInPort(); - - for (const auto& connection : network.getConnections()) { - if (connection.from() == srcPort.getPortInfo() && connection.to() == dstPort.getPortInfo()) - return Connection(network, srcPort.getPortInfo(), dstPort.getPortInfo()); - } - THROW_IE_EXCEPTION << "Connection " << src.getName() << " -> " << dst.getName() << " was not found!"; -} - -Transform::Connection Transform::Network::getConnection(const Transform::Port &src, - const Transform::Port &dst) const { - for (const auto& connection : network.getConnections()) { - if (connection.from() == src.getPortInfo() && connection.to() == dst.getPortInfo()) - return Connection(network, src.getPortInfo(), dst.getPortInfo()); - } - THROW_IE_EXCEPTION << "Connection " << getLayer(src.getPortInfo().layerId()).getName() - << " -> " << getLayer(dst.getPortInfo().layerId()).getName() << " was not found!"; -} diff --git a/inference-engine/src/inference_engine/transform/transform_network.hpp b/inference-engine/src/inference_engine/transform/transform_network.hpp deleted file mode 100644 index a712203588e5a7..00000000000000 --- a/inference-engine/src/inference_engine/transform/transform_network.hpp +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace InferenceEngine { -namespace Transform { - -class Connection; -class Layer; - -class INFERENCE_ENGINE_API_CLASS(Port) { -public: - Port(Builder::Network& network, PortInfo port, bool isInput); - PortData::Ptr getData() const; - const std::map& getParameters() const; - Layer getLayer() const; - Connection getConnection() const; - void connect(const Port& port); - void disconnect(); - const SizeVector& shape() const; - PortInfo getPortInfo() const; - bool operator==(const Port& rObj) const; - bool operator!=(const Port& rObj) const; - -private: - Builder::Network& network; - PortInfo port; - bool input; - - friend class Connection; -}; - -class INFERENCE_ENGINE_API_CLASS(Layer) { -public: - Layer(Builder::Network& network, idx_t id); - Port getInPort() const; - Port getInPort(idx_t idx) const; - std::vector getInPorts() const; - Port getOutPort() const; - Port getOutPort(idx_t idx) const; - std::vector getOutPorts() const; - - void setParameter(const std::string& key, const Parameter& value); - Parameter& getParameter(const std::string& value) const; - - idx_t getId() const; - std::string getName() const; - std::string getType() const; - operator Builder::Layer::Ptr() const; - -private: - Builder::Network& network; - idx_t layerId; - - Builder::Layer::Ptr getLayer() const; -}; - -class INFERENCE_ENGINE_API_CLASS(Connection) { -public: - explicit Connection(const Port& port); - Connection(Builder::Network& network, const InferenceEngine::Connection& connection); - Connection(Builder::Network& network, const PortInfo& inPort, const PortInfo& outPort); - Connection(Builder::Network& network, const PortInfo& inPort, const std::vector& outPorts); - - Port getSource() const; - void setSource(const Port& port); - Port getDestination() const; - Port getDestination(idx_t idx); - std::vector getDestinations() const; - void addDestination(const Port& port); - void setDestination(const Port& port); - void setDestinations(const std::vector& ports); - void remove(); - -private: - Builder::Network& network; - PortInfo inPort; - std::vector outPorts; - - bool inPortExist() const; -}; - -class INFERENCE_ENGINE_API_CLASS(Network) { -public: - explicit Network(Builder::Network& network): network(network) {} - virtual ~Network() = default; - - Layer addLayer(const Builder::Layer& layer); - void removeLayer(const Layer& layer); - Layer getLayer(const std::string& name) const; - Layer getLayer(idx_t id) const; - - Builder::Network& getBuilderNetwork() const; - - Connection connect(const Layer& src, const Layer& dst); - Connection connect(const Port& src, const Port& dst); - void disconnect(const Layer& src, const Layer& dst); - void disconnect(const Port& src, const Port& dst); - Connection getConnection(const Layer& src, const Layer& dst) const; - Connection getConnection(const Port& src, const Port& dst) const; - -private: - Builder::Network& network; -}; - -} // namespace Transform -} // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/transform/transformation.cpp b/inference-engine/src/inference_engine/transform/transformation.cpp deleted file mode 100644 index 785213990cd02e..00000000000000 --- a/inference-engine/src/inference_engine/transform/transformation.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include -#include - -namespace InferenceEngine { -namespace Transform { - -std::string Transformation::getName() const { - return name; -} - -void Transformation::setName(const std::string& name) { - this->name = name; -} - -} // namespace Transform -} // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/transform/transformation.hpp b/inference-engine/src/inference_engine/transform/transformation.hpp deleted file mode 100644 index 6a9a13d9610b94..00000000000000 --- a/inference-engine/src/inference_engine/transform/transformation.hpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include -#include -#include -#include - -namespace InferenceEngine { -namespace Transform { - -class Transformation { - std::string name; -public: - std::string getName() const; - void setName(const std::string& name); - virtual ~Transformation() = default; - virtual void execute(Network& network) = 0; -}; - -} // namespace Transform -} // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/transform/transformations/eltwise_broadcast.cpp b/inference-engine/src/inference_engine/transform/transformations/eltwise_broadcast.cpp deleted file mode 100644 index 19e76f980e9f4d..00000000000000 --- a/inference-engine/src/inference_engine/transform/transformations/eltwise_broadcast.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include "eltwise_broadcast.hpp" -#include "builders/ie_network_builder.hpp" -#include "builders/ie_reshape_layer.hpp" -#include "builders/ie_tile_layer.hpp" -#include "debug.h" -#include -#include -#include - -namespace InferenceEngine { -namespace Transform { - -TransformationEltwiseBroadcast::TransformationEltwiseBroadcast() { - this->setName("ie.transform.eltwise_broadcast"); -} - -void insertTileOverDimension(Transform::Network& network, Transform::Port& inputPort, size_t axis, size_t tile) { - auto tileLayerBuilder = Builder::TileLayer("Tile" + std::to_string(axis) + "_" + std::to_string(tile)).setAxis(axis).setTiles(tile); - auto tileLayer = network.addLayer(tileLayerBuilder); - inputPort.getConnection().setDestination(tileLayer.getInPort()); - tileLayer.getOutPort().connect(inputPort); -} - -void TransformationEltwiseBroadcast::execute(Network& network) { - for (auto layer : network.getBuilderNetwork()) { - if (layer->getType() == "Eltwise") { - auto eltwiseLayer = network.getLayer(layer->getName()); - auto outShape = eltwiseLayer.getOutPort(0).shape(); - for (auto& eltwiseInPort : eltwiseLayer.getInPorts()) { - auto inShape = eltwiseInPort.shape(); - // if shape lengths are not equal then insert Reshape with shape prepended with ones - if (inShape.size() < outShape.size()) { - std::vector reshapeDims(inShape.begin(), inShape.end()); - reshapeDims.insert(reshapeDims.begin(), outShape.size() - inShape.size(), 1); - auto reshapeLayerBuilder = Builder::ReshapeLayer(eltwiseInPort.getLayer().getName() + "/Reshape").setDims(reshapeDims); - auto reshapeLayer = network.addLayer(reshapeLayerBuilder); - eltwiseInPort.getConnection().setDestination(reshapeLayer.getInPort()); - reshapeLayer.getOutPort().connect(eltwiseInPort); - SizeVector newOutShape(reshapeDims.size()); - // update shape of the Port - for (size_t ind = 0; ind < reshapeDims.size(); ++ind) - newOutShape[ind] = reshapeDims[ind]; - eltwiseInPort.getData()->setShape(newOutShape); - inShape = newOutShape; - } - for (size_t axis = 0; axis < inShape.size(); ++axis) { - if (inShape[axis] != outShape[axis]) { - if (inShape[axis] != 1) { - THROW_IE_EXCEPTION << "Layer " << layer->getName() - << " input has invalid shape " - << details::dumpVec(inShape) - << " which can not be broadcasted to output shape " - << details::dumpVec(outShape); - } - insertTileOverDimension(network, eltwiseInPort, axis, outShape[axis]); - } - } - } - } - } -} - -} // namespace Transform -} // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/transform/transformations/eltwise_broadcast.hpp b/inference-engine/src/inference_engine/transform/transformations/eltwise_broadcast.hpp deleted file mode 100644 index 634a70547f20e9..00000000000000 --- a/inference-engine/src/inference_engine/transform/transformations/eltwise_broadcast.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include - -namespace InferenceEngine { -namespace Transform { - -class TransformationEltwiseBroadcast: public Transformation { -public: - TransformationEltwiseBroadcast(); - void execute(Network& network) override; -}; - -} // namespace Transform -} // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/transform/transformations/lrn.cpp b/inference-engine/src/inference_engine/transform/transformations/lrn.cpp deleted file mode 100644 index ab630a270ab889..00000000000000 --- a/inference-engine/src/inference_engine/transform/transformations/lrn.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include "lrn.hpp" -#include "builders/ie_network_builder.hpp" -#include "builders/ie_power_layer.hpp" -#include "builders/ie_eltwise_layer.hpp" -#include "builders/ie_norm_layer.hpp" -#include -#include - -namespace InferenceEngine { -namespace Transform { - -TransformationLRN::TransformationLRN() { - this->setName("ie.transform.lrn"); -} - -void TransformationLRN::execute(Network& network) { - for (auto layer : network.getBuilderNetwork()) { - if (layer->getType() == "LRN") { - auto lrnLayer = network.getLayer(layer->getName()); - float scale_value = 1.0f / std::pow(static_cast(lrnLayer.getParameter("bias")), - static_cast(lrnLayer.getParameter("beta"))); - - auto normLayerBuilder = Builder::NormLayer(lrnLayer.getName() + "/Norm"). - setAlpha(static_cast(lrnLayer.getParameter("alpha")) / static_cast(lrnLayer.getParameter("bias"))). - setSize(static_cast(lrnLayer.getParameter("size"))). - setBeta(static_cast(lrnLayer.getParameter("beta"))). - setAcrossMaps(true); - auto normLayer = network.addLayer(normLayerBuilder); - - auto mulLayerBuilder = Builder::EltwiseLayer(lrnLayer.getName() + "/Mul").setEltwiseType( - Builder::EltwiseLayer::EltwiseType::MUL); - auto mulLayer = network.addLayer(mulLayerBuilder); - - auto tensorDesc = TensorDesc(Precision::FP32, SizeVector(4, 1), Layout::NCHW); - auto blob = make_shared_blob(tensorDesc); - blob->allocate(); - float *buffer = blob->buffer().as::value_type *>(); - buffer[0] = scale_value; - - auto constLayerBuilder = Builder::ConstLayer(mulLayerBuilder.getName() + "/Const").setData(blob); - auto constLayer = network.addLayer(constLayerBuilder); - - // re-connect input of LRN layer to input of Norm layer - lrnLayer.getInPort().getConnection().setDestination(normLayer.getInPort()); - - // multiple output of Norm with a constant - mulLayer.getInPort(0).connect(normLayer.getOutPort()); - mulLayer.getInPort(1).connect(constLayer.getOutPort()); - - // connect consumers of LRN with mul - lrnLayer.getOutPort().getConnection().setSource(mulLayer.getOutPort()); - - network.removeLayer(lrnLayer); - } - } -} - -} // namespace Transform -} // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/transform/transformations/lrn.hpp b/inference-engine/src/inference_engine/transform/transformations/lrn.hpp deleted file mode 100644 index d17fede0075dec..00000000000000 --- a/inference-engine/src/inference_engine/transform/transformations/lrn.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include - -namespace InferenceEngine { -namespace Transform { - -class TransformationLRN: public Transformation { -public: - TransformationLRN(); - void execute(Network& network) override; -}; - -} // namespace Transform -} // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/transform/transformations/sub.cpp b/inference-engine/src/inference_engine/transform/transformations/sub.cpp deleted file mode 100644 index 5a3eeb8957d970..00000000000000 --- a/inference-engine/src/inference_engine/transform/transformations/sub.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include "sub.hpp" -#include "builders/ie_network_builder.hpp" -#include "builders/ie_power_layer.hpp" -#include "builders/ie_eltwise_layer.hpp" -#include -#include -#include - -namespace InferenceEngine { -namespace Transform { - -TransformationSub::TransformationSub() { - this->setName("ie.transform.sub"); -} - -void TransformationSub::execute(Network& network) { - for (auto layer : network.getBuilderNetwork()) { - if (layer->getType() == "Eltwise" && layer->getParameters()["operation"].as() == "sub") { - auto subLayer = network.getLayer(layer->getName()); - - auto powerLayerBuilder = Builder::PowerLayer(subLayer.getName() + "/Power").setPower(1.0f).setScale(-1.0f).setShift(0.0f); - auto powerLayer = network.addLayer(powerLayerBuilder); - - auto eltwiseLayerBuilder = Builder::EltwiseLayer(subLayer.getName() + "/Add").setEltwiseType(Builder::EltwiseLayer::EltwiseType::SUM); - auto eltwiseLayer = network.addLayer(eltwiseLayerBuilder); - - // negate the second input to the sub layer - subLayer.getInPort(1).getConnection().setDestination(powerLayer.getInPort()); - - // connect new eltwise with sum with two inputs - subLayer.getInPort(0).getConnection().setDestination(eltwiseLayer.getInPort(0)); - eltwiseLayer.getInPort(1).connect(powerLayer.getOutPort()); - - // reconnect new eltwise with outputs of all eltwise with sub - subLayer.getOutPort().getConnection().setSource(eltwiseLayer.getOutPort()); - - network.removeLayer(subLayer); - } - } -} - -} // namespace Transform -} // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/transform/transformations/sub.hpp b/inference-engine/src/inference_engine/transform/transformations/sub.hpp deleted file mode 100644 index bcefc62d6173f3..00000000000000 --- a/inference-engine/src/inference_engine/transform/transformations/sub.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include - -namespace InferenceEngine { -namespace Transform { - -class TransformationSub: public Transformation { -public: - TransformationSub(); - void execute(Network& network) override; -}; - -} // namespace Transform -} // namespace InferenceEngine diff --git a/inference-engine/src/inference_engine/w_dirent.h b/inference-engine/src/inference_engine/w_dirent.h index d100d5130eb87f..4fa56118616524 100644 --- a/inference-engine/src/inference_engine/w_dirent.h +++ b/inference-engine/src/inference_engine/w_dirent.h @@ -3,10 +3,31 @@ // #pragma once + +#if defined(_WIN32) + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN_UNDEF +#endif + +#ifndef NOMINMAX +# define NOMINMAX +# define NOMINMAX_UNDEF +#endif + +#if defined(_M_IX86) && !defined(_X86_) && !defined(_AMD64_) +# define _X86_ +#endif + +#if defined(_M_X64) && !defined(_X86_) && !defined(_AMD64_) +# define _AMD64_ +#endif + #include -#if defined(WIN32) -#include "w_unistd.h" -#include "debug.h" +#include +#include +#include #include // Copied from linux libc sys/stat.h: @@ -28,10 +49,17 @@ struct dirent { }; class DIR { - WIN32_FIND_DATA FindFileData; + WIN32_FIND_DATAA FindFileData; HANDLE hFind; dirent *next; + static inline bool endsWith(const std::string &src, const char *with) { + int wl = static_cast(strlen(with)); + int so = static_cast(src.length()) - wl; + if (so < 0) return false; + return 0 == strncmp(with, &src[so], wl); + } + public: DIR(const DIR &other) = delete; DIR(DIR &&other) = delete; @@ -39,14 +67,12 @@ class DIR { DIR& operator=(DIR &&other) = delete; explicit DIR(const char *dirPath) : next(nullptr) { - // wchar_t ws[1024]; - // swprintf(ws, 1024, L"%hs\\*", dirPath); std::string ws = dirPath; - if (InferenceEngine::details::endsWith(ws, "\\")) + if (endsWith(ws, "\\")) ws += "*"; else ws += "\\*"; - hFind = FindFirstFile(ws.c_str(), &FindFileData); + hFind = FindFirstFileA(ws.c_str(), &FindFileData); FindFileData.dwReserved0 = hFind != INVALID_HANDLE_VALUE; } @@ -71,7 +97,7 @@ class DIR { size_t outSize; mbstowcs_s(&outSize, wbuf, 4094, FindFileData.cFileName, 4094); next = new dirent(wbuf); - FindFileData.dwReserved0 = FindNextFile(hFind, &FindFileData); + FindFileData.dwReserved0 = FindNextFileA(hFind, &FindFileData); return next; } }; @@ -93,10 +119,20 @@ static struct dirent* readdir(DIR *dp) { static void closedir(DIR *dp) { delete dp; } + +#ifdef WIN32_LEAN_AND_MEAN_UNDEF +# undef WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN_UNDEF +#endif + +#ifdef NOMINMAX_UNDEF +# undef NOMINMAX_UNDEF +# undef NOMINMAX +#endif + #else #include #include #endif - diff --git a/inference-engine/src/mkldnn_plugin/mkldnn/desc_iterator.hpp b/inference-engine/src/mkldnn_plugin/mkldnn/desc_iterator.hpp index 271bc564cc53ed..201545ba2b1d06 100644 --- a/inference-engine/src/mkldnn_plugin/mkldnn/desc_iterator.hpp +++ b/inference-engine/src/mkldnn_plugin/mkldnn/desc_iterator.hpp @@ -16,25 +16,38 @@ struct primitive_desc_iterator : public handle template primitive_desc_iterator(const T &adesc, const mkldnn::primitive_attr &aattr, const engine &aengine) { mkldnn_primitive_desc_iterator_t result; - error::wrap_c_api(mkldnn_primitive_desc_iterator_create_v2( - &result, &adesc.data, aattr.get(), aengine.get(), nullptr), - "could not create a primitive descriptor iterator"); - reset(result); + auto sts = mkldnn_primitive_desc_iterator_create_v2( + &result, &adesc.data, aattr.get(), aengine.get(), nullptr); + + if (sts == mkldnn_status_t::mkldnn_success) + reset(result); + else if (sts == mkldnn_status_t::mkldnn_unimplemented) + reset(nullptr); + else + THROW_IE_EXCEPTION << "could not create a primitive descriptor iterator"; } template primitive_desc_iterator(const T &adesc, const mkldnn::primitive_attr &aattr, const engine &aengine, const TF &hint_fwd_primitive_desc) { mkldnn_primitive_desc_iterator_t result; - error::wrap_c_api(mkldnn_primitive_desc_iterator_create_v2(&result, - &adesc.data, - aattr.get(), - aengine.get(), - hint_fwd_primitive_desc.get()), - "could not create a primitive descriptor iterator"); - reset(result); + auto sts = mkldnn_primitive_desc_iterator_create_v2(&result, + &adesc.data, + aattr.get(), + aengine.get(), + hint_fwd_primitive_desc.get()); + + if (sts == mkldnn_status_t::mkldnn_success) + reset(result); + else if (sts == mkldnn_status_t::mkldnn_unimplemented) + reset(nullptr); + else + THROW_IE_EXCEPTION << "could not create a primitive descriptor iterator"; } + bool is_not_end() const { + return (handle::get() != nullptr); + } memory::primitive_desc fetch() const { memory::primitive_desc adesc; @@ -46,9 +59,14 @@ struct primitive_desc_iterator : public handle return adesc; } - bool next() { + primitive_desc_iterator operator++(int) { mkldnn_status_t status = mkldnn_primitive_desc_iterator_next(get()); - return status == mkldnn_status_t::mkldnn_success; + if (status == mkldnn_status_t::mkldnn_iterator_ends) + reset(nullptr); + else if (status != mkldnn_status_t::mkldnn_success) + THROW_IE_EXCEPTION << "could not get next iteration"; + + return *this; } memory::primitive_desc src_primitive_desc(size_t index = 0) const { diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_edge.cpp b/inference-engine/src/mkldnn_plugin/mkldnn_edge.cpp index af464ec696c858..9debb88752af8b 100644 --- a/inference-engine/src/mkldnn_plugin/mkldnn_edge.cpp +++ b/inference-engine/src/mkldnn_plugin/mkldnn_edge.cpp @@ -151,14 +151,14 @@ void MKLDNNEdge::allocate(const void* mem_ptr) { auto inputDesc = getInputDesc(); auto outputDesc = getOutputDesc(); if (!MKLDNNExtensionUtils::initTensorsAreEqual(outputDesc, inputDesc) || - (inputDesc.getDims()[0] != 1 && inputDesc != outputDesc)) + (inputDesc.getDims().size() > 0 && inputDesc.getDims()[0] != 1 && inputDesc != outputDesc)) THROW_IE_EXCEPTION << "Cannot allocate memory. Nodes have primitive descriptors with different formats."; if (inputDesc.getLayout() == InferenceEngine::Layout::ANY) THROW_IE_EXCEPTION << "Cannot get input descriptor!"; auto parentPtr = getParent(); memoryPtr.reset(new MKLDNNMemory(parentPtr->getEngine())); - memoryPtr->Create(MKLDNNMemoryDesc(inputDesc), mem_ptr); + memoryPtr->Create(MKLDNNMemoryDesc(inputDesc), mem_ptr, false); // no pads zeroing status = Status::Allocated; } @@ -209,7 +209,7 @@ const MKLDNNDims& MKLDNNEdge::getDims() { dims = outDims.ndims() ? outDims : inDims; - if (!dims.ndims()) + if (!(outDims.ndims() == 0 && inDims.ndims() == 0) && !dims.ndims()) THROW_IE_EXCEPTION << "Cannot detect right dims for nodes " << getParent()->getName() << " and " << getChild()->getName(); } @@ -549,7 +549,7 @@ MKLDNNMemoryPtr &MKLDNNEdge::getMemoryPtr() { } InferenceEngine::Blob::Ptr MKLDNNEdge::getBlob() { - if (!memoryPtr || !dims.ndims()) + if (!memoryPtr) THROW_IE_EXCEPTION << "Cannot get blob! Edge isn't initialized."; InferenceEngine::TensorDesc desc = getDesc(); diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_exec_network.cpp b/inference-engine/src/mkldnn_plugin/mkldnn_exec_network.cpp new file mode 100644 index 00000000000000..b7916b58ee1bcb --- /dev/null +++ b/inference-engine/src/mkldnn_plugin/mkldnn_exec_network.cpp @@ -0,0 +1,244 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include "mkldnn_exec_network.h" + +#include "mkldnn_async_infer_request.h" +#include "mkldnn_infer_request.h" +#include "mkldnn_memory_state.h" +#include +#include +#include +#include + +#include +#include + +using namespace MKLDNNPlugin; +using namespace MKLDNNPlugin::cpu; +using namespace InferenceEngine; +using InferenceEngine::details::CNNNetworkInt8Normalizer; + +InferenceEngine::InferRequestInternal::Ptr +MKLDNNExecNetwork::CreateInferRequestImpl(InferenceEngine::InputsDataMap networkInputs, + InferenceEngine::OutputsDataMap networkOutputs) { + if (graphs.size() > 1) // streams uses special requests that are not connected to graphs + return std::make_shared(networkInputs, networkOutputs); + else + return std::make_shared(networkInputs, networkOutputs); +} + +MKLDNNExecNetwork::MKLDNNExecNetwork(const InferenceEngine::ICNNNetwork &network, + const Config &cfg, + const MKLDNNExtensionManager::Ptr& extMgr) : extensionManager(extMgr) { + ICNNNetworkStats* pstats = nullptr; + StatusCode s = network.getStats(&pstats, nullptr); + // we are cloning network if we have statistics and we can transform network. + auto clonedNetwork = cloneNet(network); + + if (Precision::FP16 == network.getPrecision()) { + clonedNetwork->setPrecision(Precision::FP32); + } + details::CNNNetworkIterator itLayer(static_cast(clonedNetwork.get())); + while (itLayer != details::CNNNetworkIterator()) { + CNNLayer::Ptr layer = *itLayer; + convertLayerFP16toFP32(layer); + itLayer++; + } + + if (s == StatusCode::OK && pstats && !pstats->isEmpty()) { + CNNNetworkInt8Normalizer cnnorm; + cnnorm.NormalizeNetwork(*clonedNetwork, *pstats); + } + + MKLDNNGraph::ApplyUnrollPasses(static_cast(*clonedNetwork)); + + if (cfg.batchLimit > 1) { + // check topology for applicability + if (!CanProcessDynBatch(*clonedNetwork)) { + THROW_IE_EXCEPTION << "MKLDNNGraph::CreateGraph: such topology cannot be compiled for dynamic batch!"; + } + } + // check whether any (affinity-related) envs are set and if user requested thread binding + const bool bPinningRequested = !check_env_variables() && cfg.useThreadBinding; + // general #threads logic + const int env_threads = parallel_get_env_threads(); + const int sockets = MKLDNNPlugin::cpu::getNumberOfCPUSockets(); + // use logical cores only for single-socket targets in throughput mode + const int hw_cores = cfg.throughputStreams > 1 && sockets == 1 ? parallel_get_max_threads() : getNumberOfCPUCores(); + + const int threads = cfg.threadsNum ? cfg.threadsNum : (env_threads ? env_threads : hw_cores); + const int threads_per_stream = std::max(1, threads/cfg.throughputStreams); + + // graph(s) initialization in taskExecutor threads (streams), in parallel (in case of streams) + std::vector tasks; + const int workers_per_socket = std::max(1, static_cast(std::ceil(static_cast(cfg.throughputStreams)/sockets))); + for (int n = 0; n < cfg.throughputStreams; n++) { + MKLDNNGraph::Ptr _graph = std::make_shared(); + graphs.push_back(_graph); + auto task = std::make_shared([=, &cfg]() { + _graph->CreateArena(threads_per_stream); + + if (bPinningRequested) { + _graph->CreateObserver(n, threads_per_stream); + } + + _graph->setConfig(cfg); + int socket = n / workers_per_socket; + _graph->CreateGraph(static_cast(*clonedNetwork), extensionManager, socket); + if (cfg.throughputStreams > 1) // for streams, each worker thread has it's own graph + MKLDNNPlugin::MultiWorkerTaskExecutor::ptrContext.ptrGraph = _graph; + }); + tasks.push_back(task); + } + + if (cfg.throughputStreams > 1) { + // special executor with as many threads as requested #streams, each with it's own initialization task + _taskExecutor = std::make_shared(tasks); + } else { + if (cfg.exclusiveAsyncRequests) { + // special case when all InferRequests are muxed into a single queue + ExecutorManager *executorManager = ExecutorManager::getInstance(); + _taskExecutor = executorManager->getExecutor("CPU"); + } + _taskExecutor->startTask(tasks[0]); + Task::Status sts = tasks[0]->wait(InferenceEngine::IInferRequest::WaitMode::RESULT_READY); + } + for (auto t : tasks) + t->checkException(); + + // Save all MemoryLayer data tensors. Will use insight about mechanics + // of MemoryLayer implementation. It uses output edge of MemoryLayer + // producer as storage for tensor to keep it between infer calls. + if (graphs.size() == 1) { + for (auto &node : graphs[0]->GetNodes()) { + if (node->getType() == MemoryInput) { + auto state_store = node->getChildEdgeAt(0)->getMemoryPtr(); + auto state_name = node->getName(); + + // Remove suffix with pair ID. Internal information. + auto suffix_idx = state_name.find("/id="); + if (suffix_idx != std::string::npos) + state_name = state_name.substr(0, suffix_idx); + + memoryStates.emplace_back(new MKLDNNMemoryState(state_name, state_store)); + } + } + } +} + +void MKLDNNExecNetwork::setProperty(const std::map &properties) { + for (auto g : graphs) + g->setProperty(properties); +} + +void MKLDNNExecNetwork::CreateInferRequest(InferenceEngine::IInferRequest::Ptr &asyncRequest) { + auto syncRequestImpl = CreateInferRequestImpl(_networkInputs, _networkOutputs); + syncRequestImpl->setPointerToExecutableNetworkInternal(shared_from_this()); + auto asyncRequestImpl = std::make_shared(syncRequestImpl, _taskExecutor, + _taskSynchronizer, _callbackExecutor); + asyncRequest.reset(new InferRequestBase(asyncRequestImpl), + [](IInferRequest *p) { p->Release(); }); + + asyncRequestImpl->SetPointerToPublicInterface(asyncRequest); + + if (graphs.size() == 1) { // single-stream (legacy/hetero) case - single graph for all requests + auto mkldnnSyncRequest = dynamic_cast(syncRequestImpl.get()); + if (!mkldnnSyncRequest) + THROW_IE_EXCEPTION << " Cannot get mkldnn sync request."; + mkldnnSyncRequest->SetGraph(graphs[0]); + } +} + +void MKLDNNExecNetwork::GetExecGraphInfo(InferenceEngine::ICNNNetwork::Ptr &graphPtr) { + graphPtr = graphs[0]->dump(); +} + +void MKLDNNExecNetwork::GetConfig(const std::string &name, Parameter &result, ResponseDesc *resp) const { + Config engConfig = graphs[0]->getProperty(); + auto option = engConfig._config.find(name); + if (option != engConfig._config.end()) { + result = option->second; + } else { + THROW_IE_EXCEPTION << "Unsupported ExecutableNetwork config key: " << name; + } +} + +void MKLDNNExecNetwork::GetMetric(const std::string &name, Parameter &result, ResponseDesc *resp) const { + if (name == METRIC_KEY(NETWORK_NAME)) { + result = IE_SET_METRIC(NETWORK_NAME, graphs[0]->dump()->getName()); + } else if (name == METRIC_KEY(SUPPORTED_METRICS)) { + std::vector metrics; + metrics.push_back(METRIC_KEY(NETWORK_NAME)); + metrics.push_back(METRIC_KEY(SUPPORTED_METRICS)); + metrics.push_back(METRIC_KEY(SUPPORTED_CONFIG_KEYS)); + metrics.push_back(METRIC_KEY(OPTIMAL_NUMBER_OF_INFER_REQUESTS)); + result = IE_SET_METRIC(SUPPORTED_METRICS, metrics); + } else if (name == METRIC_KEY(SUPPORTED_CONFIG_KEYS)) { + std::vector configKeys; + for (auto && key : graphs[0]->getProperty()._config) { + configKeys.push_back(key.first); + } + result = IE_SET_METRIC(SUPPORTED_CONFIG_KEYS, configKeys); + } else if (name == METRIC_KEY(OPTIMAL_NUMBER_OF_INFER_REQUESTS)) { + Config engConfig = graphs[0]->getProperty(); + auto option = engConfig._config.find(CONFIG_KEY(CPU_THROUGHPUT_STREAMS)); + IE_ASSERT(option != engConfig._config.end()); + result = IE_SET_METRIC(OPTIMAL_NUMBER_OF_INFER_REQUESTS, static_cast(std::stoi(option->second))); + } else { + THROW_IE_EXCEPTION << "Unsupported ExecutableNetwork metric: " << name; + } +} + +bool MKLDNNExecNetwork::CanProcessDynBatch(const InferenceEngine::ICNNNetwork &network) const { + InputsDataMap inputs; + network.getInputsInfo(inputs); + + CNNLayerSet inputLayers; + std::unordered_set allLayers; + + if (inputs.empty()) + return false; + + auto & secondLayers = inputs.begin()->second->getInputData()->getInputTo(); + if (secondLayers.empty()) + return false; + + bool check_result = true; + details::UnorderedDFS(allLayers, secondLayers.begin()->second, [&](CNNLayerPtr layer) { + auto type = TypeFromName(layer->type); + // This is WA for Tile layer + auto tileLayer = dynamic_cast(layer.get()); + if (tileLayer && tileLayer->axis) + return; + + if (type != Input && + type != Output && + type != Convolution && + type != Deconvolution && + type != Activation && + type != Depthwise && + type != Lrn && + type != Pooling && + type != FullyConnected && + type != Gemm && + type != SoftMax && + type != Split && + type != Concatenation && + type != Power && + type != Eltwise && + type != Crop && + type != BatchNormalization && + type != Copy) { + check_result = false; + } + }, false); + + return check_result; +} + +std::vector MKLDNNExecNetwork::QueryState() { + return memoryStates; +} diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_exec_network.h b/inference-engine/src/mkldnn_plugin/mkldnn_exec_network.h new file mode 100644 index 00000000000000..2f70ac2dd9cf21 --- /dev/null +++ b/inference-engine/src/mkldnn_plugin/mkldnn_exec_network.h @@ -0,0 +1,55 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "mkldnn_graph.h" +#include "mkldnn_extension_mngr.h" + +#include +#include +#include +#include + +namespace MKLDNNPlugin { + +class MKLDNNExecNetwork: public InferenceEngine::ExecutableNetworkThreadSafeDefault { +public: + typedef std::shared_ptr Ptr; + + InferenceEngine::InferRequestInternal::Ptr + CreateInferRequestImpl(InferenceEngine::InputsDataMap networkInputs, + InferenceEngine::OutputsDataMap networkOutputs) override; + + void CreateInferRequest(InferenceEngine::IInferRequest::Ptr &asyncRequest) override; + + MKLDNNExecNetwork(const InferenceEngine::ICNNNetwork &network, const Config &cfg, + const MKLDNNExtensionManager::Ptr& extMgr); + + virtual ~MKLDNNExecNetwork() { + graphs.clear(); + extensionManager.reset(); + } + + void setProperty(const std::map &properties); + + void GetConfig(const std::string &name, Parameter &result, ResponseDesc *resp) const override; + + void GetMetric(const std::string &name, Parameter &result, ResponseDesc *resp) const override; + + void GetExecGraphInfo(InferenceEngine::ICNNNetwork::Ptr &graphPtr) override; + + std::vector QueryState() override; + +protected: + MKLDNNExtensionManager::Ptr extensionManager; + std::vector graphs; + std::vector memoryStates; + + bool CanProcessDynBatch(const InferenceEngine::ICNNNetwork &network) const; +}; + +} // namespace MKLDNNPlugin \ No newline at end of file diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_extension_utils.cpp b/inference-engine/src/mkldnn_plugin/mkldnn_extension_utils.cpp index e687e43ec182fb..96bd3c7e70c7f1 100644 --- a/inference-engine/src/mkldnn_plugin/mkldnn_extension_utils.cpp +++ b/inference-engine/src/mkldnn_plugin/mkldnn_extension_utils.cpp @@ -90,6 +90,8 @@ InferenceEngine::TensorDesc MKLDNNExtensionUtils::getUninitTensorDesc(const Infe bool MKLDNNExtensionUtils::initTensorsAreEqual(const InferenceEngine::TensorDesc &desc1, const InferenceEngine::TensorDesc &desc2) { if (desc1.getDims() != desc2.getDims() || desc1.getPrecision() != desc2.getPrecision()) return false; + if (desc1.getLayout() == InferenceEngine::Layout::SCALAR && desc2.getLayout() == InferenceEngine::Layout::SCALAR) + return true; if (desc1.getLayout() == InferenceEngine::Layout::ANY || desc2.getLayout() == InferenceEngine::Layout::ANY) return true; bool batch1 = desc1.getDims()[0] == 1; diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_graph.cpp b/inference-engine/src/mkldnn_plugin/mkldnn_graph.cpp index 494accc63df86a..6cdf8c07ef7f0a 100644 --- a/inference-engine/src/mkldnn_plugin/mkldnn_graph.cpp +++ b/inference-engine/src/mkldnn_plugin/mkldnn_graph.cpp @@ -13,48 +13,28 @@ #include #include -#include "details/caseless.hpp" - -#include "ie_metric_helpers.hpp" #include "mkldnn_graph.h" +#include "mkldnn_graph_dumper.h" #include "mkldnn_graph_optimizer.h" -#include +#include "mkldnn_extension_utils.h" +#include "mkldnn_extension_mngr.h" +#include "mkldnn_memory_solver.hpp" #include #include -#include -#include -#include "mkldnn_extension_utils.h" -#include "mkldnn_extension_mngr.h" -#include "mkldnn/omp_manager.h" +#include #include -#include -#include "ie_algorithm.hpp" -#include "memory_solver.hpp" -#include "mkldnn_infer_request.h" -#include "mkldnn_async_infer_request.h" +#include #include -#include #include #include

- -#include - -#include -#include "cnn_network_int8_normalizer.hpp" -#include "ie_memcpy.h" - -#include "precision_utils.h" -#include +#include #define XBYAK_NO_OP_NAMES #define XBYAK_UNDEF_JNL #include "../../thirdparty/mkl-dnn/src/cpu/xbyak/xbyak_util.h" -#include "cnn_network_stats_impl.hpp" - #include "utils/blob_dump.h" -#include "mkldnn_plugin.h" /***************************************************** * Debug capability @@ -328,7 +308,11 @@ void MKLDNNGraph::Replicate(const ICNNNetwork &network, const MKLDNNExtensionMan inputNodes[input.first] = layer2node[inputLayer]; // Loading mean images - MKLDNNDims outDims(inputNodes[input.first]->getChildEdgeAt(0)->getDims()); + MKLDNNDims outDims; + if (!inputNodes[input.first]->getChildEdgeAt(0)->getDims().ndims()) + outDims = MKLDNNDims(InferenceEngine::SizeVector(1, 1)); + else + outDims = MKLDNNDims(inputNodes[input.first]->getChildEdgeAt(0)->getDims()); if (inputs.find(input.first) != inputs.end()) { InputInfo::Ptr ii = inputs[input.first]; if (ii && ii->getPreProcess().getNumberOfChannels()) { @@ -629,6 +613,13 @@ void MKLDNNGraph::AllocateWithReuse() { // !! Fallback to individual memory allocation !! // if you like to check infer without reuse just call this function without arguments. edge->allocate(workspace_ptr + offset * alignment); // alignment in byte + + // TODO: WA for some test (like strided_slice_test) which use tensors with + // shapes {0}. And it is implisitly converted into {1} tensor. + // Zeroing of input data allow pass tests. + if (edge->getParent()->type == Input) + edge->getMemoryPtr()->FillZero(); + count++; } } @@ -652,8 +643,11 @@ void MKLDNNGraph::Allocate() { for (auto& edge : graphEdges) edge->validate(); } -void MKLDNNGraph::CreatePrimitives() { +void MKLDNNGraph::CreatePrimitives() { IE_PROFILING_AUTO_SCOPE(MKLDNNGraph::CreatePrimitives) + bool weights_caching = config.throughputStreams != 1; for (auto& node : graphNodes) { + // disable caching if graph was created only once + node->enableWeightCaching(weights_caching); node->createPrimitive(); } } @@ -1080,202 +1074,3 @@ void MKLDNNGraph::do_after(const std::string &dir, const MKLDNNNodePtr &node) { InferenceEngine::ICNNNetwork::Ptr MKLDNNGraph::dump() const { return dump_graph_as_ie_net(*this); } - -bool MKLDNNExecNetwork::CanProcessDynBatch(const InferenceEngine::ICNNNetwork &network) const { - InputsDataMap inputs; - network.getInputsInfo(inputs); - - CNNLayerSet inputLayers; - std::unordered_set allLayers; - - if (inputs.empty()) - return false; - - auto & secondLayers = inputs.begin()->second->getInputData()->getInputTo(); - if (secondLayers.empty()) - return false; - - bool check_result = true; - details::UnorderedDFS(allLayers, secondLayers.begin()->second, [&](CNNLayerPtr layer) { - auto type = TypeFromName(layer->type); - // This is WA for Tile layer - auto tileLayer = dynamic_cast(layer.get()); - if (tileLayer && tileLayer->axis) - return; - - if (type != Input && - type != Output && - type != Convolution && - type != Deconvolution && - type != Activation && - type != Depthwise && - type != Lrn && - type != Pooling && - type != FullyConnected && - type != Gemm && - type != SoftMax && - type != Split && - type != Concatenation && - type != Power && - type != Eltwise && - type != Crop && - type != BatchNormalization && - type != Copy) { - check_result = false; - } - }, false); - - return check_result; -} - -InferenceEngine::InferRequestInternal::Ptr -MKLDNNExecNetwork::CreateInferRequestImpl(InferenceEngine::InputsDataMap networkInputs, - InferenceEngine::OutputsDataMap networkOutputs) { - if (graphs.size() > 1) // streams uses special requests that are not connected to graphs - return std::make_shared(networkInputs, networkOutputs); - else - return std::make_shared(networkInputs, networkOutputs); -} - -MKLDNNExecNetwork::MKLDNNExecNetwork(const InferenceEngine::ICNNNetwork &network, - const Config &cfg, - const MKLDNNExtensionManager::Ptr& extMgr) : extensionManager(extMgr) { - ICNNNetworkStats* pstats = nullptr; - StatusCode s = network.getStats(&pstats, nullptr); - // we are cloning network if we have statistics and we can transform network. - auto clonedNetwork = cloneNet(network); - - if (Precision::FP16 == network.getPrecision()) { - clonedNetwork->setPrecision(Precision::FP32); - } - details::CNNNetworkIterator itLayer(reinterpret_cast(clonedNetwork.get())); - while (itLayer != details::CNNNetworkIterator()) { - CNNLayer::Ptr layer = *itLayer; - convertLayerFP16toFP32(layer); - itLayer++; - } - - if (s == StatusCode::OK && pstats && !pstats->isEmpty()) { - CNNNetworkInt8Normalizer cnnorm; - cnnorm.NormalizeNetwork(*clonedNetwork, *pstats); - } - - MKLDNNGraph::ApplyUnrollPasses(*clonedNetwork); - - if (cfg.batchLimit > 1) { - // check topology for applicability - if (!CanProcessDynBatch(*clonedNetwork)) { - THROW_IE_EXCEPTION << "MKLDNNGraph::CreateGraph: such topology cannot be compiled for dynamic batch!"; - } - } - // check whether any (affinity-related) envs are set and if user requested thread binding - const bool bPinningRequested = !check_env_variables() && cfg.useThreadBinding; - // general #threads logic - const int env_threads = parallel_get_env_threads(); - const int sockets = MKLDNNPlugin::cpu::getNumberOfCPUSockets(); - // use logical cores only for single-socket targets in throughput mode - const int hw_cores = cfg.throughputStreams > 1 && sockets == 1 ? parallel_get_max_threads() : getNumberOfCPUCores(); - - const int threads = cfg.threadsNum ? cfg.threadsNum : (env_threads ? env_threads : hw_cores); - const int threads_per_stream = std::max(1, threads/cfg.throughputStreams); - - // graph(s) initialization in taskExecutor threads (streams), in parallel (in case of streams) - std::vector tasks; - const int workers_per_socket = std::max(1, static_cast(std::ceil(static_cast(cfg.throughputStreams)/sockets))); - for (int n = 0; n < cfg.throughputStreams; n++) { - MKLDNNGraph::Ptr _graph = std::make_shared(); - graphs.push_back(_graph); - auto task = std::make_shared([=, &cfg, &network]() { - _graph->CreateArena(threads_per_stream); - - if (bPinningRequested) { - _graph->CreateObserver(n, threads_per_stream); - } - - _graph->setConfig(cfg); - int socket = n / workers_per_socket; - _graph->CreateGraph(*clonedNetwork, extensionManager, socket); - if (cfg.throughputStreams > 1) // for streams, each worker thread has it's own graph - MKLDNNPlugin::MultiWorkerTaskExecutor::ptrContext.ptrGraph = _graph; - }); - tasks.push_back(task); - } - - if (cfg.throughputStreams > 1) { - // special executor with as many threads as requested #streams, each with it's own initialization task - _taskExecutor = std::make_shared(tasks); - } else { - if (cfg.exclusiveAsyncRequests) { - // special case when all InferRequests are muxed into a single queue - ExecutorManager *executorManager = ExecutorManager::getInstance(); - _taskExecutor = executorManager->getExecutor("CPU"); - } - _taskExecutor->startTask(tasks[0]); - Task::Status sts = tasks[0]->wait(InferenceEngine::IInferRequest::WaitMode::RESULT_READY); - } - for (auto t : tasks) - t->checkException(); -} - -void MKLDNNExecNetwork::setProperty(const std::map &properties) { - for (auto g : graphs) - g->setProperty(properties); -} - -void MKLDNNExecNetwork::CreateInferRequest(InferenceEngine::IInferRequest::Ptr &asyncRequest) { - auto syncRequestImpl = CreateInferRequestImpl(_networkInputs, _networkOutputs); - syncRequestImpl->setPointerToExecutableNetworkInternal(shared_from_this()); - auto asyncRequestImpl = std::make_shared(syncRequestImpl, _taskExecutor, - _taskSynchronizer, _callbackExecutor); - asyncRequest.reset(new InferRequestBase(asyncRequestImpl), - [](IInferRequest *p) { p->Release(); }); - - asyncRequestImpl->SetPointerToPublicInterface(asyncRequest); - - if (graphs.size() == 1) { // single-stream (legacy/hetero) case - single graph for all requests - auto mkldnnSyncRequest = dynamic_cast(syncRequestImpl.get()); - if (!mkldnnSyncRequest) - THROW_IE_EXCEPTION << " Cannot get mkldnn sync request."; - mkldnnSyncRequest->SetGraph(graphs[0]); - } -} - -void MKLDNNExecNetwork::GetExecGraphInfo(InferenceEngine::ICNNNetwork::Ptr &graphPtr) { - graphPtr = graphs[0]->dump(); -} - -void MKLDNNExecNetwork::GetConfig(const std::string &name, Parameter &result, ResponseDesc *resp) const { - Config engConfig = graphs[0]->getProperty(); - auto option = engConfig._config.find(name); - if (option != engConfig._config.end()) { - result = option->second; - } else { - THROW_IE_EXCEPTION << "Unsupported ExecutableNetwork config key: " << name; - } -} - -void MKLDNNExecNetwork::GetMetric(const std::string &name, Parameter &result, ResponseDesc *resp) const { - if (name == METRIC_KEY(NETWORK_NAME)) { - result = IE_SET_METRIC(NETWORK_NAME, graphs[0]->dump()->getName()); - } else if (name == METRIC_KEY(SUPPORTED_METRICS)) { - std::vector metrics; - metrics.push_back(METRIC_KEY(NETWORK_NAME)); - metrics.push_back(METRIC_KEY(SUPPORTED_METRICS)); - metrics.push_back(METRIC_KEY(SUPPORTED_CONFIG_KEYS)); - metrics.push_back(METRIC_KEY(OPTIMAL_NUMBER_OF_INFER_REQUESTS)); - result = IE_SET_METRIC(SUPPORTED_METRICS, metrics); - } else if (name == METRIC_KEY(SUPPORTED_CONFIG_KEYS)) { - std::vector configKeys; - for (auto && key : graphs[0]->getProperty()._config) { - configKeys.push_back(key.first); - } - result = IE_SET_METRIC(SUPPORTED_CONFIG_KEYS, configKeys); - } else if (name == METRIC_KEY(OPTIMAL_NUMBER_OF_INFER_REQUESTS)) { - Config engConfig = graphs[0]->getProperty(); - auto option = engConfig._config.find(CONFIG_KEY(CPU_THROUGHPUT_STREAMS)); - IE_ASSERT(option != engConfig._config.end()); - result = IE_SET_METRIC(OPTIMAL_NUMBER_OF_INFER_REQUESTS, static_cast(std::stoi(option->second))); - } else { - THROW_IE_EXCEPTION << "Unsupported ExecutableNetwork metric: " << name; - } -} diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_graph.h b/inference-engine/src/mkldnn_plugin/mkldnn_graph.h index f02d982a20a9df..057fb8220fff2a 100644 --- a/inference-engine/src/mkldnn_plugin/mkldnn_graph.h +++ b/inference-engine/src/mkldnn_plugin/mkldnn_graph.h @@ -4,23 +4,19 @@ #pragma once -#include -#include -#include -#include -#include - #include "ie_parallel.hpp" -#include "mkldnn_memory.h" #include "config.h" -#include "perf_count.h" -#include "mkldnn_dims.h" +#include "mkldnn_memory.h" #include "mean_image.h" #include "mkldnn_node.h" #include "mkldnn_edge.h" -#include "mkldnn_extension_utils.h" #include "mkldnn_streams.h" +#include +#include +#include +#include + namespace MKLDNNPlugin { class MKLDNNGraph { @@ -186,37 +182,4 @@ class MKLDNNGraph { }; }; - -class MKLDNNExecNetwork: public InferenceEngine::ExecutableNetworkThreadSafeDefault { -public: - typedef std::shared_ptr Ptr; - - InferenceEngine::InferRequestInternal::Ptr CreateInferRequestImpl(InferenceEngine::InputsDataMap networkInputs, - InferenceEngine::OutputsDataMap networkOutputs) override; - - void CreateInferRequest(InferenceEngine::IInferRequest::Ptr &asyncRequest) override; - - MKLDNNExecNetwork(const InferenceEngine::ICNNNetwork &network, const Config &cfg, - const MKLDNNExtensionManager::Ptr& extMgr); - - ~MKLDNNExecNetwork() { - graphs.clear(); - extensionManager.reset(); - } - - void setProperty(const std::map &properties); - - void GetConfig(const std::string &name, Parameter &result, ResponseDesc *resp) const override; - - void GetMetric(const std::string &name, Parameter &result, ResponseDesc *resp) const override; - - void GetExecGraphInfo(InferenceEngine::ICNNNetwork::Ptr &graphPtr) override; - -protected: - std::vector graphs; - MKLDNNExtensionManager::Ptr extensionManager; - - bool CanProcessDynBatch(const InferenceEngine::ICNNNetwork &network) const; -}; - } // namespace MKLDNNPlugin diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_graph_optimizer.cpp b/inference-engine/src/mkldnn_plugin/mkldnn_graph_optimizer.cpp index 5a3728e20a0828..7ff46e50bee5cc 100644 --- a/inference-engine/src/mkldnn_plugin/mkldnn_graph_optimizer.cpp +++ b/inference-engine/src/mkldnn_plugin/mkldnn_graph_optimizer.cpp @@ -2,24 +2,28 @@ // SPDX-License-Identifier: Apache-2.0 // -#include -#include "nodes/mkldnn_reshape_node.h" #include "mkldnn_graph_optimizer.h" -#include + +#include "mkldnn_extension_utils.h" +#include "nodes/mkldnn_reshape_node.h" +#include "nodes/mkldnn_activation_node.h" #include "nodes/mkldnn_pooling_node.h" #include "nodes/mkldnn_eltwise_node.h" #include "nodes/mkldnn_depthwise_node.h" #include "nodes/mkldnn_concat_node.h" #include "nodes/mkldnn_reorder_node.h" +#include "nodes/mkldnn_conv_node.h" +#include "nodes/mkldnn_bin_conv_node.h" +#include "nodes/mkldnn_quantize_node.h" + +#include +#include +#include #include #include #include #include -#include -#include -#include -#include "cpu_isa_traits.hpp" using namespace mkldnn; using namespace MKLDNNPlugin; @@ -111,8 +115,8 @@ void MKLDNNGraphOptimizer::MergeGroupConvolution(MKLDNNGraph &graph) { // TODO: Rewrite topology optimizer at all. it should be clean and understandable auto concat = conv->getChildEdgeAt(0)->getChild(); // Merge and remove Convolution - for (size_t i = 1; i < split->getChildEdges().size(); i++) { - auto peerInEdge = split->getChildEdgeAt(i); + while (split->getChildEdges().size() > 1) { + auto peerInEdge = split->getChildEdgeAt(1); auto peer = peerInEdge->getChild(); conv->mergeWith(peer); convInDims[1] += (peerInEdge->getDims())[1]; @@ -537,16 +541,28 @@ void MKLDNNGraphOptimizer::FuseConvolutionSumAndConvolutionSumActivation(MKLDNNG auto parent1 = graphNode->getParentEdgeAt(0)->getParent(); auto parent2 = graphNode->getParentEdgeAt(1)->getParent(); - bool isSutableParent1 = (parent1->getType() == Convolution && parent1->fusedWith.empty()) || - parent1->getType() == BinaryConvolution; - bool isSutableParent2 = (parent2->getType() == Convolution && parent2->fusedWith.empty()) || - parent2->getType() == BinaryConvolution; + bool isSutableParent1 = parent1->getType() == Convolution || parent1->getType() == BinaryConvolution; + bool isSutableParent2 = parent2->getType() == Convolution || parent2->getType() == BinaryConvolution; + + auto* parentNode1 = dynamic_cast(parent1.get()); + if (parentNode1) { + if (parentNode1->getCnnLayer()->precision == Precision::FP32) { + isSutableParent1 = isSutableParent1 && parentNode1->getFusedWith().empty(); + } + } + + auto* parentNode2 = dynamic_cast(parent2.get()); + if (parentNode2) { + if (parentNode2->getCnnLayer()->precision == Precision::FP32) { + isSutableParent2 = isSutableParent2 && parentNode2->getFusedWith().empty(); + } + } if (!isSutableParent1 && !isSutableParent2) continue; - auto mergedConv = (parent1->getType() == Convolution || parent1->getType() == BinaryConvolution) ? parent1 : parent2; - auto peerNode = (parent1->getType() == Convolution || parent1->getType() == BinaryConvolution) ? parent2 : parent1; + auto mergedConv = isSutableParent1 ? parent1 : parent2; + auto peerNode = isSutableParent1 ? parent2 : parent1; if ((peerNode->getType() == Convolution || peerNode->getType() == BinaryConvolution) && mergedConv->getChildEdges().size() != 1) { mergedConv = parent2; diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_memory.cpp b/inference-engine/src/mkldnn_plugin/mkldnn_memory.cpp index 0ab8ed5e380269..68929cd9ba0af2 100644 --- a/inference-engine/src/mkldnn_plugin/mkldnn_memory.cpp +++ b/inference-engine/src/mkldnn_plugin/mkldnn_memory.cpp @@ -48,7 +48,7 @@ void MKLDNNMemory::Create(memory::dims dims, memory::data_type data_type, memory Create(desc, data); } -void MKLDNNMemory::Create(const mkldnn::memory::desc& desc, const void *data) { +void MKLDNNMemory::Create(const mkldnn::memory::desc& desc, const void *data, bool pads_zeroing) { auto primitive_desc = memory::primitive_desc(desc, eng); uint8_t itemSize = MKLDNNExtensionUtils::sizeOfDataType(mkldnn::memory::data_type(desc.data.data_type)); @@ -64,13 +64,25 @@ void MKLDNNMemory::Create(const mkldnn::memory::desc& desc, const void *data) { real_size *= prim->get_primitive_desc().desc().data.layout_desc.blocking.padding_dims[i]; } } - uint8_t* dataPtr = static_cast(GetData()); - dataPtr += itemSize * prim->get_primitive_desc().desc().data.layout_desc.blocking.offset_padding; - - memset(dataPtr, 0, real_size * itemSize); } else { // MKLDNN accepts not a const data, probably need to remove some level of consteness in a call stack - prim.reset(new memory(primitive_desc, const_cast(data))); + + // ======================== + // Equivalent of constructor memory(const primitive_desc &desc, void *hdl) + // but with ability to skipp pads zeroing. + mkldnn_primitive_t result; + error::wrap_c_api(mkldnn_primitive_create(&result, primitive_desc.get(), nullptr, nullptr), + "could not create a memory primitive"); + auto *mem = new memory(nullptr); + mem->reset(result); + if (pads_zeroing) + mem->set_data_handle(const_cast(data)); + else + mem->set_data_handle_no_pads_proc(const_cast(data)); + // + // ======================== + + prim.reset(mem); } } @@ -83,10 +95,10 @@ void MKLDNNMemory::SetData(memory::data_type dataType, memory::format format, co std::vector dims(memData.dims, memData.dims + memData.ndims); - auto dataType = GetDataType(); + auto data_type = GetDataType(); MKLDNNMemory src(eng); - src.Create(dims, dataType, format, data); + src.Create(dims, data_type, format, data); std::shared_ptr pReorder = std::shared_ptr(new mkldnn::reorder(src.GetPrimitive(), GetPrimitive())); @@ -238,6 +250,8 @@ bool MKLDNNMemory::IsPlainFormat(memory::format format) { memory::format MKLDNNMemory::GetPlainFormat(memory::dims dims) { switch (dims.size()) { + case 0: + return memory::x; case 1: return memory::x; case 2: @@ -313,6 +327,8 @@ memory::format MKLDNNMemory::Convert(const InferenceEngine::Layout layout) { return memory::nc; case C: return memory::x; + case SCALAR: + return memory::x; default: return memory::blocked; } @@ -437,7 +453,12 @@ MKLDNNMemoryDesc::operator mkldnn::memory::desc() const { MKLDNNMemoryDesc::MKLDNNMemoryDesc(mkldnn::memory::dims dims, mkldnn::memory::data_type dataType, mkldnn::memory::format format): desc(dims, dataType, mkldnn::memory::any) { if (format != memory::blocked) { - desc = mkldnn::memory::desc(dims, dataType, format); + if (format == memory::x && dims.size() == 0) { + desc = mkldnn::memory::desc(mkldnn::memory::dims(1, 1), dataType, format); + MKLDNNMemory::CreateBlockingDesc(desc); + } else { + desc = mkldnn::memory::desc(dims, dataType, format); + } return; } MKLDNNMemory::CreateBlockingDesc(desc); diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_memory.h b/inference-engine/src/mkldnn_plugin/mkldnn_memory.h index 0a047dd5103301..f11a9a3b379a80 100644 --- a/inference-engine/src/mkldnn_plugin/mkldnn_memory.h +++ b/inference-engine/src/mkldnn_plugin/mkldnn_memory.h @@ -101,7 +101,7 @@ class MKLDNNMemory { void Create(mkldnn::memory::dims dims, mkldnn::memory::data_type data_type, mkldnn::memory::format format, const void* data = nullptr); - void Create(const mkldnn::memory::desc& desc, const void* data = nullptr); + void Create(const mkldnn::memory::desc& desc, const void* data = nullptr, bool pads_zeroing = true); void SetData(mkldnn::memory::data_type dataType, mkldnn::memory::format format, const void* data, size_t size, bool ftz = true) const; void SetData(const MKLDNNMemory& memory, bool ftz = true) const; diff --git a/inference-engine/src/inference_engine/memory_solver.cpp b/inference-engine/src/mkldnn_plugin/mkldnn_memory_solver.cpp similarity index 97% rename from inference-engine/src/inference_engine/memory_solver.cpp rename to inference-engine/src/mkldnn_plugin/mkldnn_memory_solver.cpp index e70caabfa05174..4620b48e329be3 100644 --- a/inference-engine/src/inference_engine/memory_solver.cpp +++ b/inference-engine/src/mkldnn_plugin/mkldnn_memory_solver.cpp @@ -2,15 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "memory_solver.hpp" +#include "mkldnn_memory_solver.hpp" -#include "details/ie_exception.hpp" +#include
#include #include #include -namespace InferenceEngine { +namespace MKLDNNPlugin { MemorySolver::MemorySolver(const std::vector& boxes) : _boxes(boxes) { int max_ts = 0; @@ -133,4 +133,4 @@ void MemorySolver::calcDepth() { } } -} // namespace InferenceEngine +} // namespace MKLDNNPlugin diff --git a/inference-engine/src/inference_engine/memory_solver.hpp b/inference-engine/src/mkldnn_plugin/mkldnn_memory_solver.hpp similarity index 95% rename from inference-engine/src/inference_engine/memory_solver.hpp rename to inference-engine/src/mkldnn_plugin/mkldnn_memory_solver.hpp index 0881b859fb15c6..5e09ea939506a5 100644 --- a/inference-engine/src/inference_engine/memory_solver.hpp +++ b/inference-engine/src/mkldnn_plugin/mkldnn_memory_solver.hpp @@ -13,7 +13,7 @@ #include #include -namespace InferenceEngine { +namespace MKLDNNPlugin { /** * @brief Helps to solve issue of optimal memory allocation only for particular @@ -42,7 +42,7 @@ namespace InferenceEngine { * Exec order is predefined. */ -class INFERENCE_ENGINE_API_CLASS(MemorySolver) { +class MemorySolver { public: /** @brief Representation of edge (size and live time)*/ struct Box { @@ -89,4 +89,4 @@ class INFERENCE_ENGINE_API_CLASS(MemorySolver) { void calcDepth(); }; -} // namespace InferenceEngine +} // namespace MKLDNNPlugin diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_memory_state.cpp b/inference-engine/src/mkldnn_plugin/mkldnn_memory_state.cpp new file mode 100644 index 00000000000000..aff1134cfb25c3 --- /dev/null +++ b/inference-engine/src/mkldnn_plugin/mkldnn_memory_state.cpp @@ -0,0 +1,35 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mkldnn_memory_state.h" +#include "mkldnn_extension_utils.h" + +using namespace InferenceEngine; + +namespace MKLDNNPlugin { + +std::string MKLDNNMemoryState::GetName() const { + return name; +} + +void MKLDNNMemoryState::Reset() { + storage->FillZero(); +} + +void MKLDNNMemoryState::SetState(Blob::Ptr newState) { + auto prec = newState->getTensorDesc().getPrecision(); + auto data_type = MKLDNNExtensionUtils::IEPrecisionToDataType(prec); + auto data_layout = MKLDNNMemory::Convert(newState->getTensorDesc().getLayout()); + auto data_ptr = newState->cbuffer().as(); + auto data_size = newState->byteSize(); + + storage->SetData(data_type, data_layout, data_ptr, data_size); +} + +InferenceEngine::Blob::CPtr MKLDNNMemoryState::GetLastState() const { + THROW_IE_EXCEPTION << "GetLastState method is not implemented for MemoryState"; + return nullptr; +} + +} // namespace MKLDNNPlugin \ No newline at end of file diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_memory_state.h b/inference-engine/src/mkldnn_plugin/mkldnn_memory_state.h new file mode 100644 index 00000000000000..a2d94ab108f5ec --- /dev/null +++ b/inference-engine/src/mkldnn_plugin/mkldnn_memory_state.h @@ -0,0 +1,29 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "cpp_interfaces/impl/ie_memory_state_internal.hpp" +#include "mkldnn_memory.h" + +#include + +namespace MKLDNNPlugin { + +class MKLDNNMemoryState : public InferenceEngine::IMemoryStateInternal { +public: + MKLDNNMemoryState(std::string name, MKLDNNMemoryPtr storage) : + name(name), storage(storage) {} + + std::string GetName() const override; + void Reset() override; + void SetState(InferenceEngine::Blob::Ptr newState) override; + InferenceEngine::Blob::CPtr GetLastState() const override; + +private: + std::string name; + MKLDNNMemoryPtr storage; +}; + +} // namespace MKLDNNPlugin \ No newline at end of file diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_node.cpp b/inference-engine/src/mkldnn_plugin/mkldnn_node.cpp index 3739d31fe249c6..7edda28a9e35ec 100644 --- a/inference-engine/src/mkldnn_plugin/mkldnn_node.cpp +++ b/inference-engine/src/mkldnn_plugin/mkldnn_node.cpp @@ -382,7 +382,9 @@ const std::vector MKLDNNNode::getChildEdgesAtPort(size_t idx) con std::vector MKLDNNNode::getAvailableFormatsForDims(const MKLDNNDims &dims) const { - if (dims.ndims() == 1) + if (dims.ndims() == 0) + return {memory::format::x}; + else if (dims.ndims() == 1) return {memory::format::x}; else if (dims.ndims() == 2) return {memory::format::nc}; @@ -406,46 +408,42 @@ void MKLDNNNode::initSupportedPrimitiveDescriptors() { return; for (auto& desc : descs) { - try { - std::shared_ptr itpd = std::make_shared(desc.createPrimitiveDescriptorIterator(engine)); - do { - InferenceEngine::LayerConfig config; - config.dynBatchSupport = true; - for (size_t i = 0; i < desc.inputNumbers(); i++) { - InferenceEngine::DataConfig dataConfig; - dataConfig.inPlace = -1; - dataConfig.constant = false; - dataConfig.desc = MKLDNNExtensionUtils::getUninitTensorDesc(getSrcMemDesc(*itpd, i)); - config.inConfs.push_back(dataConfig); - } + auto itpd = desc.createPrimitiveDescriptorIterator(engine); + while (itpd.is_not_end()) { + InferenceEngine::LayerConfig config; + config.dynBatchSupport = true; + for (size_t i = 0; i < desc.inputNumbers(); i++) { + InferenceEngine::DataConfig dataConfig; + dataConfig.inPlace = -1; + dataConfig.constant = false; + dataConfig.desc = MKLDNNExtensionUtils::getUninitTensorDesc(getSrcMemDesc(itpd, i)); + config.inConfs.push_back(dataConfig); + } - std::vector outFormats; - for (size_t i = 0; i < desc.outputNumbers(); i++) { - InferenceEngine::DataConfig dataConfig; - dataConfig.inPlace = canBeInPlace() ? 0 : -1; - dataConfig.constant = false; - dataConfig.desc = MKLDNNExtensionUtils::getUninitTensorDesc(getDstMemDesc(*itpd, i)); - config.outConfs.push_back(dataConfig); - - auto primDesc = itpd->fetch(); - auto dstPrimDesc = mkldnn_primitive_desc_query_pd(primDesc.get(), mkldnn::convert_to_c(dst_pd), 0); - if (dstPrimDesc) { - outFormats.emplace_back(static_cast(itpd->dst_primitive_desc().desc().data.format)); - } else { - // This path is needed to correctly handle Deconvolution node - auto diffSrcPrimDesc = mkldnn_primitive_desc_query_pd(primDesc.get(), mkldnn::convert_to_c(diff_src_pd), 0); - if (diffSrcPrimDesc) { - outFormats.emplace_back(static_cast(itpd->diff_src_primitive_desc().desc().data.format)); - } + std::vector outFormats; + for (size_t i = 0; i < desc.outputNumbers(); i++) { + InferenceEngine::DataConfig dataConfig; + dataConfig.inPlace = canBeInPlace() ? 0 : -1; + dataConfig.constant = false; + dataConfig.desc = MKLDNNExtensionUtils::getUninitTensorDesc(getDstMemDesc(itpd, i)); + config.outConfs.push_back(dataConfig); + + auto primDesc = itpd.fetch(); + auto dstPrimDesc = mkldnn_primitive_desc_query_pd(primDesc.get(), mkldnn::convert_to_c(dst_pd), 0); + if (dstPrimDesc) { + outFormats.emplace_back(static_cast(itpd.dst_primitive_desc().desc().data.format)); + } else { + // This path is needed to correctly handle Deconvolution node + auto diffSrcPrimDesc = mkldnn_primitive_desc_query_pd(primDesc.get(), mkldnn::convert_to_c(diff_src_pd), 0); + if (diffSrcPrimDesc) { + outFormats.emplace_back(static_cast(itpd.diff_src_primitive_desc().desc().data.format)); } } - impl_desc_type impl_type = parse_impl_name(itpd->get_impl_info_str()); + } + impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); - supportedPrimitiveDescriptors.emplace_back(config, impl_type, outFormats); - } while (itpd->next()); - } catch (std::exception& e) { - // it throw exception in case of no implementation found - continue; + supportedPrimitiveDescriptors.emplace_back(config, impl_type, outFormats); + itpd++; } } } @@ -468,47 +466,46 @@ void MKLDNNNode::initDescriptor(const InferenceEngine::LayerConfig &config) { InferenceEngine::LayerConfig rightConfig = selectedPD->getConfig(); size_t selected_count = 0; for (size_t j = 0; j < descs.size(); j++) { - try { - const auto &desc = descs[j]; - std::shared_ptr itpd; - if (attr == nullptr) { - itpd = std::make_shared(desc.createPrimitiveDescriptorIterator(engine)); - } else { - itpd = std::make_shared(desc.createPrimitiveDescriptorIterator(engine, *(attr.get()))); + const auto &desc = descs[j]; + std::shared_ptr itpd; + if (attr == nullptr) { + itpd = std::make_shared(desc.createPrimitiveDescriptorIterator(engine)); + } else { + itpd = std::make_shared(desc.createPrimitiveDescriptorIterator(engine, *(attr.get()))); + } + while (itpd->is_not_end()) { + InferenceEngine::LayerConfig cfg; + cfg.dynBatchSupport = true; + for (size_t i = 0; i < desc.inputNumbers(); i++) { + InferenceEngine::DataConfig dataConfig; + dataConfig.inPlace = canBeInPlace() ? 0 : -1; + dataConfig.constant = false; + dataConfig.desc = getSrcMemDesc(*itpd, i); + cfg.inConfs.push_back(dataConfig); } - do { - InferenceEngine::LayerConfig cfg; - cfg.dynBatchSupport = true; - for (size_t i = 0; i < desc.inputNumbers(); i++) { - InferenceEngine::DataConfig dataConfig; - dataConfig.inPlace = canBeInPlace() ? 0 : -1; - dataConfig.constant = false; - dataConfig.desc = getSrcMemDesc(*itpd, i); - cfg.inConfs.push_back(dataConfig); - } - for (size_t i = 0; i < desc.outputNumbers(); i++) { - InferenceEngine::DataConfig dataConfig; - dataConfig.inPlace = -1; - dataConfig.constant = false; - dataConfig.desc = getDstMemDesc(*itpd, i); - cfg.outConfs.push_back(dataConfig); - } - impl_desc_type impl_type = parse_impl_name(itpd->get_impl_info_str().c_str()); - if (selected_count == selectedPrimitiveDescriptorIndex) { - if (impl_type != selectedPD->getImplementationType()) { - THROW_IE_EXCEPTION << "Cannot get the original layer configuration!"; - } - rightConfig = cfg; + for (size_t i = 0; i < desc.outputNumbers(); i++) { + InferenceEngine::DataConfig dataConfig; + dataConfig.inPlace = -1; + dataConfig.constant = false; + dataConfig.desc = getDstMemDesc(*itpd, i); + cfg.outConfs.push_back(dataConfig); + } + impl_desc_type impl_type = parse_impl_name(itpd->get_impl_info_str().c_str()); + if (selected_count == selectedPrimitiveDescriptorIndex) { + if (impl_type != selectedPD->getImplementationType()) { + THROW_IE_EXCEPTION << "Cannot get the original layer configuration!"; } - if (j == descs.size() - 1) { - if (impl_type == selectedPD->getImplementationType()) { - rightConfig = config; - } + rightConfig = cfg; + } + if (j == descs.size() - 1) { + if (impl_type == selectedPD->getImplementationType()) { + rightConfig = config; } - selected_count++; - } while (itpd->next()); - } catch(...) {} + } + selected_count++; + (*itpd)++; + } } if (descs.empty()) { @@ -615,30 +612,38 @@ void MKLDNNNode::prepareMemory(const PrimitiveDescInfo *selected_pd, mkldnn::pri for (size_t i = 0; i < internalBlobs.size(); i++) { const auto &internalBlob = internalBlobs[i]; - const uint64_t data_hash = Engine::GetWeightsSharing(socket)->GetHashFunc().hash( - internalBlob->buffer(), internalBlob->byteSize()); - const std::string string_hash = name + "_" + std::to_string(i) - + "_" + std::to_string(internalBlob->byteSize()) - + "_" + std::to_string(data_hash); - MKLDNNMemoryPtr ptr = - Engine::GetWeightsSharing(socket)->findOrCreate(string_hash, [&] () { - MKLDNNMemoryPtr _ptr = MKLDNNMemoryPtr(new MKLDNNMemory(engine)); - _ptr->Create(intDescs[i]); - MKLDNNMemory memory(engine); - - auto newDesc = MKLDNNMemoryDesc(internalBlob->getTensorDesc()); - auto newFormat = newDesc.getFormat(); - if (newFormat == mkldnn::memory::ncdhw) { - newFormat = mkldnn::memory::goihw; - } - if (newFormat == mkldnn::memory::nchw) { - newFormat = mkldnn::memory::oihw; - } - memory.Create(MKLDNNMemoryDesc(newDesc.getDims(), newDesc.getDataType(), newFormat), internalBlob->buffer()); - auto aformat = memory.GetFormat(); - _ptr->SetData(memory); - return _ptr; - }); + auto create = [&] () { + MKLDNNMemoryPtr _ptr = MKLDNNMemoryPtr(new MKLDNNMemory(engine)); + _ptr->Create(intDescs[i]); + MKLDNNMemory memory(engine); + + auto newDesc = MKLDNNMemoryDesc(internalBlob->getTensorDesc()); + auto newFormat = newDesc.getFormat(); + if (newFormat == mkldnn::memory::ncdhw) { + newFormat = mkldnn::memory::goihw; + } + if (newFormat == mkldnn::memory::nchw) { + newFormat = mkldnn::memory::oihw; + } + memory.Create(MKLDNNMemoryDesc(newDesc.getDims(), newDesc.getDataType(), newFormat), internalBlob->buffer()); + auto aformat = memory.GetFormat(); + _ptr->SetData(memory); + return _ptr; + }; + + MKLDNNMemoryPtr ptr; + if (weight_caching) { + const uint64_t data_hash = Engine::GetWeightsSharing(socket)->GetHashFunc().hash( + internalBlob->buffer(), internalBlob->byteSize()); + + const std::string string_hash = name + "_" + std::to_string(i) + + "_" + std::to_string(internalBlob->byteSize()) + + "_" + std::to_string(data_hash); + + ptr = Engine::GetWeightsSharing(socket)->findOrCreate(string_hash, create); + } else { + ptr = create(); + } internalBlobMemory.push_back(ptr); } } @@ -928,10 +933,18 @@ int MKLDNNNode::batchToProcess() { int MKLDNNNode::getMaxBatch() { // FIXME: batch != 0 dims number - if (!inDims.empty()) - return inDims[0][0]; - if (!outDims.empty()) - return outDims[0][0]; + if (!inDims.empty()) { + if (inDims[0].ndims()) + return inDims[0][0]; + else + return 1; + } + if (!outDims.empty() && outDims[0].ndims()) { + if (outDims[0].ndims()) + return outDims[0][0]; + else + return 1; + } return 0; } diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_node.h b/inference-engine/src/mkldnn_plugin/mkldnn_node.h index 60206d8375a44e..04e61d0262f7e2 100644 --- a/inference-engine/src/mkldnn_plugin/mkldnn_node.h +++ b/inference-engine/src/mkldnn_plugin/mkldnn_node.h @@ -402,31 +402,28 @@ class MKLDNNNode : public InferenceEngine::details::no_copy { THROW_IE_EXCEPTION << "Preferable primitive descriptor is not set for node " << getName() << "."; for (const auto& desc : descs) { - try { - mkldnn::primitive_desc_iterator itpd = desc.createPrimitiveDescriptorIterator(engine, attr); - do { - std::vector srcDescs; - for (size_t i = 0; i < desc.inputNumbers(); i++) - srcDescs.push_back(getSrcMemDesc(itpd, i)); - - std::vector dstDescs; - for (size_t i = 0; i < desc.outputNumbers(); i++) - dstDescs.push_back(getDstMemDesc(itpd, i)); - - impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); - - if (impl_type == selected_pd->getImplementationType() && - descsEqual(srcDescs, selected_pd->getConfig().inConfs) && - descsEqual(dstDescs, selected_pd->getConfig().outConfs)) { - prepareMemory(selected_pd, itpd); - PD prim_desc = createPd(desc); - itpd.getPrimitiveDescriptor(prim_desc); - return prim_desc; - } - } while (itpd.next()); - } catch (std::exception& e) { - // it throw exception in case of no implementation found - continue; + auto itpd = desc.createPrimitiveDescriptorIterator(engine, attr); + + while (itpd.is_not_end()) { + std::vector srcDescs; + for (size_t i = 0; i < desc.inputNumbers(); i++) + srcDescs.push_back(getSrcMemDesc(itpd, i)); + + std::vector dstDescs; + for (size_t i = 0; i < desc.outputNumbers(); i++) + dstDescs.push_back(getDstMemDesc(itpd, i)); + + impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); + + if (impl_type == selected_pd->getImplementationType() && + descsEqual(srcDescs, selected_pd->getConfig().inConfs) && + descsEqual(dstDescs, selected_pd->getConfig().outConfs)) { + prepareMemory(selected_pd, itpd); + PD prim_desc = createPd(desc); + itpd.getPrimitiveDescriptor(prim_desc); + return prim_desc; + } + itpd++; } } @@ -512,6 +509,13 @@ class MKLDNNNode : public InferenceEngine::details::no_copy { int batchToProcess(); int whichSocket() { return socket; } + // TODO: While CPU plugin has no ease way to clone graph object we use weight + // caching in global Engine context to avoid tensor duplication. Just to + // improve memory consumption in case of throughput streams when we have + // duplicate of graph for single input ICNNNetwork. + // Remove this flag when graph clone functionality will be added. + void enableWeightCaching(bool val) { weight_caching = val; } + InferenceEngine::Blob::Ptr createInternalBlob(InferenceEngine::SizeVector dims, bool weights); template @@ -537,6 +541,7 @@ class MKLDNNNode : public InferenceEngine::details::no_copy { Type type; int execIndex = -1; int socket; + bool weight_caching = false; std::string typeToStr(Type type); diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_plugin.cpp b/inference-engine/src/mkldnn_plugin/mkldnn_plugin.cpp index 6ca5ba6a72d375..9fbfcd1dd108c7 100644 --- a/inference-engine/src/mkldnn_plugin/mkldnn_plugin.cpp +++ b/inference-engine/src/mkldnn_plugin/mkldnn_plugin.cpp @@ -209,7 +209,7 @@ void Engine::QueryNetwork(const ICNNNetwork& network, const std::map()); return OK; diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_plugin.h b/inference-engine/src/mkldnn_plugin/mkldnn_plugin.h index 4ae7fed1f34f8f..f46c70d424f9a5 100644 --- a/inference-engine/src/mkldnn_plugin/mkldnn_plugin.h +++ b/inference-engine/src/mkldnn_plugin/mkldnn_plugin.h @@ -4,14 +4,15 @@ #pragma once -#include "mkldnn_graph.h" +#include +#include "mkldnn_exec_network.h" + #include #include #include #include #include #include -#include namespace MKLDNNPlugin { diff --git a/inference-engine/src/mkldnn_plugin/mkldnn_streams.cpp b/inference-engine/src/mkldnn_plugin/mkldnn_streams.cpp index 68ad8a3c0cc96e..a0c2159dc8a084 100644 --- a/inference-engine/src/mkldnn_plugin/mkldnn_streams.cpp +++ b/inference-engine/src/mkldnn_plugin/mkldnn_streams.cpp @@ -134,10 +134,10 @@ bool pin_current_thread_to_socket(int socket) { MultiWorkerTaskExecutor::MultiWorkerTaskExecutor(const std::vector& init_tasks, std::string name) : _isStopped(false), _name(name), _initCount(0) { const int sockets = MKLDNNPlugin::cpu::getNumberOfCPUSockets(); - const int worker_per_sockets = init_tasks.size() / sockets; + const int worker_per_sockets = (std::max)(1, static_cast(std::ceil(static_cast(init_tasks.size()) / sockets))); for (int t= 0; t < init_tasks.size(); t++) { _threads.push_back(std::thread([&, t, init_tasks] { - int socket = t/worker_per_sockets; + int socket = t / worker_per_sockets; pin_current_thread_to_socket(socket); // initialization (no contention, every worker thread is doing it's own task) init_tasks[t]->runNoThrowNoBusyCheck(); diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_batchnorm_node.cpp b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_batchnorm_node.cpp index 5fa64e388302e1..1dad2401354672 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_batchnorm_node.cpp +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_batchnorm_node.cpp @@ -238,7 +238,7 @@ void MKLDNNBatchNormalizationNode::initSupportedPrimitiveDescriptors() { // BN primitive doesn't support strides for (auto& desc : descs) { primitive_desc_iterator itpd = desc.createPrimitiveDescriptorIterator(getEngine()); - do { + while (itpd.is_not_end()) { InferenceEngine::LayerConfig config; config.dynBatchSupport = true; for (size_t i = 0; i < desc.inputNumbers(); i++) { @@ -262,7 +262,8 @@ void MKLDNNBatchNormalizationNode::initSupportedPrimitiveDescriptors() { impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); supportedPrimitiveDescriptors.emplace_back(config, impl_type, outFormats); - } while (itpd.next()); + itpd++; + } } } diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_bin_conv_node.cpp b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_bin_conv_node.cpp index f17a3babc1175d..0f6320c2005e2d 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_bin_conv_node.cpp +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_bin_conv_node.cpp @@ -321,47 +321,43 @@ void MKLDNNBinaryConvolutionNode::initSupportedPrimitiveDescriptors() { setPostOps(attr); for (auto& desc : descs) { - try { - primitive_desc_iterator itpd = desc.createPrimitiveDescriptorIterator(getEngine(), attr); - do { - InferenceEngine::LayerConfig config; - config.dynBatchSupport = true; - for (size_t i = 0; i < desc.inputNumbers(); i++) { - InferenceEngine::DataConfig dataConfig; - dataConfig.inPlace = -1; - dataConfig.constant = false; - dataConfig.desc = getSrcMemDesc(itpd, i); - if (!isGrouped) - dataConfig.desc = MKLDNNExtensionUtils::getUninitTensorDesc(dataConfig.desc); - config.inConfs.push_back(dataConfig); - } + auto itpd = desc.createPrimitiveDescriptorIterator(getEngine(), attr); + while (itpd.is_not_end()) { + InferenceEngine::LayerConfig config; + config.dynBatchSupport = true; + for (size_t i = 0; i < desc.inputNumbers(); i++) { + InferenceEngine::DataConfig dataConfig; + dataConfig.inPlace = -1; + dataConfig.constant = false; + dataConfig.desc = getSrcMemDesc(itpd, i); + if (!isGrouped) + dataConfig.desc = MKLDNNExtensionUtils::getUninitTensorDesc(dataConfig.desc); + config.inConfs.push_back(dataConfig); + } - std::vector outFormats; - for (size_t i = 0; i < desc.outputNumbers(); i++) { - InferenceEngine::DataConfig dataConfig; - if (withSum) { - dataConfig.inPlace = 1; - } + std::vector outFormats; + for (size_t i = 0; i < desc.outputNumbers(); i++) { + InferenceEngine::DataConfig dataConfig; + if (withSum) { + dataConfig.inPlace = 1; + } - dataConfig.constant = false; - dataConfig.desc = getDstMemDesc(itpd, i); - if (!isGrouped) - dataConfig.desc = MKLDNNExtensionUtils::getUninitTensorDesc(dataConfig.desc); - config.outConfs.push_back(dataConfig); - outFormats.emplace_back(static_cast(itpd.dst_primitive_desc().desc().data.format)); + dataConfig.constant = false; + dataConfig.desc = getDstMemDesc(itpd, i); + if (!isGrouped) + dataConfig.desc = MKLDNNExtensionUtils::getUninitTensorDesc(dataConfig.desc); + config.outConfs.push_back(dataConfig); + outFormats.emplace_back(static_cast(itpd.dst_primitive_desc().desc().data.format)); - if (withSum) { - dataConfig.inPlace = -1; - config.inConfs.push_back(dataConfig); - } + if (withSum) { + dataConfig.inPlace = -1; + config.inConfs.push_back(dataConfig); } - impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); + } + impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); - supportedPrimitiveDescriptors.emplace_back(config, impl_type, outFormats); - } while (itpd.next()); - } catch (std::exception& e) { - // it throw exception in case of no implementation found - continue; + supportedPrimitiveDescriptors.emplace_back(config, impl_type, outFormats); + itpd++; } } } @@ -426,48 +422,45 @@ void MKLDNNBinaryConvolutionNode::initDescriptor(const InferenceEngine::LayerCon size_t selected_count = 0; for (size_t i = 0; i < descs.size(); i++) { const auto& desc = descs[i]; - try { - primitive_desc_iterator itpd = desc.createPrimitiveDescriptorIterator(getEngine(), attr); - do { - InferenceEngine::LayerConfig cfg; - cfg.dynBatchSupport = true; - for (size_t j = 0; j < desc.inputNumbers(); j++) { - InferenceEngine::DataConfig dataConfig; - dataConfig.inPlace = -1; - dataConfig.constant = false; - dataConfig.desc = getSrcMemDesc(itpd, j); + auto itpd = desc.createPrimitiveDescriptorIterator(getEngine(), attr); + while (itpd.is_not_end()) { + InferenceEngine::LayerConfig cfg; + cfg.dynBatchSupport = true; + for (size_t j = 0; j < desc.inputNumbers(); j++) { + InferenceEngine::DataConfig dataConfig; + dataConfig.inPlace = -1; + dataConfig.constant = false; + dataConfig.desc = getSrcMemDesc(itpd, j); + cfg.inConfs.push_back(dataConfig); + } + + for (size_t j = 0; j < desc.outputNumbers(); j++) { + InferenceEngine::DataConfig dataConfig; + dataConfig.inPlace = -1; + if (withSum) { cfg.inConfs.push_back(dataConfig); + dataConfig.inPlace = 1; } + dataConfig.constant = false; + dataConfig.desc = getDstMemDesc(itpd, j); - for (size_t j = 0; j < desc.outputNumbers(); j++) { - InferenceEngine::DataConfig dataConfig; - dataConfig.inPlace = -1; - if (withSum) { - cfg.inConfs.push_back(dataConfig); - dataConfig.inPlace = 1; - } - dataConfig.constant = false; - dataConfig.desc = getDstMemDesc(itpd, j); - - cfg.outConfs.push_back(dataConfig); - } - impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); + cfg.outConfs.push_back(dataConfig); + } + impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); - if (selected_count == selectedPrimitiveDescriptorIndex) { - if (impl_type != selectedPD->getImplementationType()) { - THROW_IE_EXCEPTION << "Cannot get the original layer configuration!"; - } - rightConfig = cfg; + if (selected_count == selectedPrimitiveDescriptorIndex) { + if (impl_type != selectedPD->getImplementationType()) { + THROW_IE_EXCEPTION << "Cannot get the original layer configuration!"; } - if (i == descs.size() - 1) { - if (impl_type == selectedPD->getImplementationType()) { - rightConfig = config; - } + rightConfig = cfg; + } + if (i == descs.size() - 1) { + if (impl_type == selectedPD->getImplementationType()) { + rightConfig = config; } - selected_count++; - } while (itpd.next()); - } catch (std::exception& e) { - continue; + } + selected_count++; + itpd++; } } selectedPD->getConfig() = rightConfig; diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_conv_node.cpp b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_conv_node.cpp index 11848962e62380..5ff69e61029432 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_conv_node.cpp +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_conv_node.cpp @@ -148,18 +148,6 @@ void MKLDNNConvolutionNode::getSupportedDescriptors() { invertVectorCopyUtoI(allPads.end, paddingR); MKLDNNDims weightsDims = MKLDNNDims(weightDims); - - for (int i = 0; i < paddingR.size(); i++) { - int with_group = (isGrouped || isMerged) ? 1 : 0; - int krn = weightsDims[with_group + 2 + i]; - int src = getParentEdgeAt(0)->getDims()[2 + i]; - int dst = getChildEdgeAt(0)->getDims()[2 + i]; - - krn = (krn - 1)*(dilation[i] + 1) + 1; - int calc_dst = (src - krn + paddingL[i]) / stride[i] + 1; - paddingR[i] = (dst - calc_dst) * stride[i]; - } - withSum = isFusedWith(Eltwise); for (auto &node : fusedWith) { @@ -176,6 +164,17 @@ void MKLDNNConvolutionNode::getSupportedDescriptors() { dw_conv_strides.push_back(convLayer->_stride[i]); } dw_conv_in_dt = MKLDNNExtensionUtils::IEPrecisionToDataType(convLayer->outData[0]->getPrecision()); + + for (int i = 0; i < paddingR.size(); i++) { + int with_group = (isGrouped || isMerged) ? 1 : 0; + int krn = weightsDims[with_group + 2 + i]; + int src = getParentEdgeAt(0)->getDims()[2 + i]; + int dst = getChildEdgeAt(0)->getDims()[2 + i]; + + krn = (krn - 1)*(dilation[i] + 1) + 1; + int calc_dst = (src - krn + paddingL[i]) / stride[i] + 1; + paddingR[i] = (dst - calc_dst) * stride[i]; + } } } @@ -480,48 +479,44 @@ void MKLDNNConvolutionNode::initSupportedPrimitiveDescriptors() { setPostOps(attr); for (auto& desc : descs) { - try { - primitive_desc_iterator itpd = desc.createPrimitiveDescriptorIterator(getEngine(), attr); - do { - InferenceEngine::LayerConfig config; - config.dynBatchSupport = true; - for (size_t i = 0; i < desc.inputNumbers(); i++) { - InferenceEngine::DataConfig dataConfig; - dataConfig.inPlace = -1; - dataConfig.constant = false; - dataConfig.desc = getSrcMemDesc(itpd, i); - if (!isGrouped) - dataConfig.desc = MKLDNNExtensionUtils::getUninitTensorDesc(dataConfig.desc); - config.inConfs.push_back(dataConfig); - } - - std::vector outFormats; - for (size_t i = 0; i < desc.outputNumbers(); i++) { - InferenceEngine::DataConfig dataConfig; - if (withSum) { - dataConfig.inPlace = 1; - } + auto itpd = desc.createPrimitiveDescriptorIterator(getEngine(), attr); + while (itpd.is_not_end()) { + InferenceEngine::LayerConfig config; + config.dynBatchSupport = true; + for (size_t i = 0; i < desc.inputNumbers(); i++) { + InferenceEngine::DataConfig dataConfig; + dataConfig.inPlace = -1; + dataConfig.constant = false; + dataConfig.desc = getSrcMemDesc(itpd, i); + if (!isGrouped) + dataConfig.desc = MKLDNNExtensionUtils::getUninitTensorDesc(dataConfig.desc); + config.inConfs.push_back(dataConfig); + } - dataConfig.constant = false; - dataConfig.desc = getDstMemDesc(itpd, i); - if (!isGrouped) - dataConfig.desc = MKLDNNExtensionUtils::getUninitTensorDesc(dataConfig.desc); - config.outConfs.push_back(dataConfig); + std::vector outFormats; + for (size_t i = 0; i < desc.outputNumbers(); i++) { + InferenceEngine::DataConfig dataConfig; + if (withSum) { + dataConfig.inPlace = 1; + } - if (withSum) { - dataConfig.inPlace = -1; - config.inConfs.push_back(dataConfig); - } + dataConfig.constant = false; + dataConfig.desc = getDstMemDesc(itpd, i); + if (!isGrouped) + dataConfig.desc = MKLDNNExtensionUtils::getUninitTensorDesc(dataConfig.desc); + config.outConfs.push_back(dataConfig); - outFormats.emplace_back(static_cast(itpd.dst_primitive_desc().desc().data.format)); + if (withSum) { + dataConfig.inPlace = -1; + config.inConfs.push_back(dataConfig); } - impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); - supportedPrimitiveDescriptors.emplace_back(config, impl_type, outFormats); - } while (itpd.next()); - } catch (std::exception& e) { - // it throw exception in case of no implementation found - continue; + outFormats.emplace_back(static_cast(itpd.dst_primitive_desc().desc().data.format)); + } + impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); + + supportedPrimitiveDescriptors.emplace_back(config, impl_type, outFormats); + itpd++; } } } @@ -599,20 +594,24 @@ void MKLDNNConvolutionNode::createDescriptor(const std::vector conv_desc; - if (withBiases) { - MKLDNNMemoryDesc bias_candidate{blocked_biasesDims, bdt, memory::any}; + try { + std::shared_ptr conv_desc; + if (withBiases) { + MKLDNNMemoryDesc bias_candidate{blocked_biasesDims, bdt, memory::any}; - conv_desc.reset(new convolution_forward::desc(prop_kind::forward_scoring, alg, - in_candidate, wgh_candidate, bias_candidate, out_candidate, - stride, dilation, paddingL, paddingR, padding_kind::zero)); - } else { - conv_desc.reset(new convolution_forward::desc(prop_kind::forward_scoring, alg, - in_candidate, wgh_candidate, out_candidate, stride, dilation, - paddingL, paddingR, padding_kind::zero)); - } + conv_desc.reset(new convolution_forward::desc(prop_kind::forward_scoring, alg, + in_candidate, wgh_candidate, bias_candidate, out_candidate, + stride, dilation, paddingL, paddingR, padding_kind::zero)); + } else { + conv_desc.reset(new convolution_forward::desc(prop_kind::forward_scoring, alg, + in_candidate, wgh_candidate, out_candidate, stride, dilation, + paddingL, paddingR, padding_kind::zero)); + } - descs.emplace_back(conv_desc); + descs.emplace_back(conv_desc); + } catch (...) { + THROW_IE_EXCEPTION << "Cannot create convolution forward descriptor for layer: " << getName(); + } } } @@ -659,48 +658,45 @@ void MKLDNNConvolutionNode::initDescriptor(const InferenceEngine::LayerConfig& c size_t selected_count = 0; for (size_t i = 0; i < descs.size(); i++) { const auto& desc = descs[i]; - try { - primitive_desc_iterator itpd = desc.createPrimitiveDescriptorIterator(getEngine(), attr); - do { - InferenceEngine::LayerConfig cfg; - cfg.dynBatchSupport = true; - for (size_t j = 0; j < desc.inputNumbers(); j++) { - InferenceEngine::DataConfig dataConfig; - dataConfig.inPlace = -1; - dataConfig.constant = false; - dataConfig.desc = getSrcMemDesc(itpd, j); + auto itpd = desc.createPrimitiveDescriptorIterator(getEngine(), attr); + while (itpd.is_not_end()) { + InferenceEngine::LayerConfig cfg; + cfg.dynBatchSupport = true; + for (size_t j = 0; j < desc.inputNumbers(); j++) { + InferenceEngine::DataConfig dataConfig; + dataConfig.inPlace = -1; + dataConfig.constant = false; + dataConfig.desc = getSrcMemDesc(itpd, j); + cfg.inConfs.push_back(dataConfig); + } + + for (size_t j = 0; j < desc.outputNumbers(); j++) { + InferenceEngine::DataConfig dataConfig; + dataConfig.inPlace = -1; + dataConfig.constant = false; + dataConfig.desc = getDstMemDesc(itpd, j); + if (withSum) { cfg.inConfs.push_back(dataConfig); + dataConfig.inPlace = 1; } - for (size_t j = 0; j < desc.outputNumbers(); j++) { - InferenceEngine::DataConfig dataConfig; - dataConfig.inPlace = -1; - if (withSum) { - cfg.inConfs.push_back(dataConfig); - dataConfig.inPlace = 1; - } - dataConfig.constant = false; - dataConfig.desc = getDstMemDesc(itpd, j); - - cfg.outConfs.push_back(dataConfig); - } - impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); + cfg.outConfs.push_back(dataConfig); + } + impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); - if (selected_count == selectedPrimitiveDescriptorIndex) { - if (impl_type != selectedPD->getImplementationType()) { - THROW_IE_EXCEPTION << "Cannot get the original layer configuration!"; - } - rightConfig = cfg; + if (selected_count == selectedPrimitiveDescriptorIndex) { + if (impl_type != selectedPD->getImplementationType()) { + THROW_IE_EXCEPTION << "Cannot get the original layer configuration!"; } - if (i == descs.size() - 1 && addedNewDesc) { - if (impl_type == selectedPD->getImplementationType()) { - rightConfig = config; - } + rightConfig = cfg; + } + if (i == descs.size() - 1 && addedNewDesc) { + if (impl_type == selectedPD->getImplementationType()) { + rightConfig = config; } - selected_count++; - } while (itpd.next()); - } catch (std::exception& e) { - continue; + } + selected_count++; + itpd++; } } selectedPD->getConfig() = rightConfig; diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_def_conv_node.cpp b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_def_conv_node.cpp index 01a81728217e59..5d400165436f5c 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_def_conv_node.cpp +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_def_conv_node.cpp @@ -123,40 +123,36 @@ void MKLDNNDeformableConvolutionNode::initSupportedPrimitiveDescriptors() { mkldnn::primitive_attr attr; for (auto& desc : descs) { - try { - primitive_desc_iterator itpd = desc.createPrimitiveDescriptorIterator(getEngine(), attr); - do { - InferenceEngine::LayerConfig config; - config.dynBatchSupport = true; - for (size_t i = 0; i < desc.inputNumbers(); i++) { - InferenceEngine::DataConfig dataConfig; - dataConfig.inPlace = -1; - dataConfig.constant = false; - dataConfig.desc = getSrcMemDesc(itpd, i); - if (!isGrouped) - dataConfig.desc = MKLDNNExtensionUtils::getUninitTensorDesc(dataConfig.desc); - config.inConfs.push_back(dataConfig); - } - - std::vector outFormats; - for (size_t i = 0; i < desc.outputNumbers(); i++) { - InferenceEngine::DataConfig dataConfig; - - dataConfig.constant = false; - dataConfig.desc = getDstMemDesc(itpd, i); - if (!isGrouped) - dataConfig.desc = MKLDNNExtensionUtils::getUninitTensorDesc(dataConfig.desc); - config.outConfs.push_back(dataConfig); - - outFormats.emplace_back(static_cast(itpd.dst_primitive_desc().desc().data.format)); - } - impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); - - supportedPrimitiveDescriptors.emplace_back(config, impl_type, outFormats); - } while (itpd.next()); - } catch (std::exception& e) { - // it throw exception in case of no implementation found - continue; + auto itpd = desc.createPrimitiveDescriptorIterator(getEngine(), attr); + while (itpd.is_not_end()) { + InferenceEngine::LayerConfig config; + config.dynBatchSupport = true; + for (size_t i = 0; i < desc.inputNumbers(); i++) { + InferenceEngine::DataConfig dataConfig; + dataConfig.inPlace = -1; + dataConfig.constant = false; + dataConfig.desc = getSrcMemDesc(itpd, i); + if (!isGrouped) + dataConfig.desc = MKLDNNExtensionUtils::getUninitTensorDesc(dataConfig.desc); + config.inConfs.push_back(dataConfig); + } + + std::vector outFormats; + for (size_t i = 0; i < desc.outputNumbers(); i++) { + InferenceEngine::DataConfig dataConfig; + + dataConfig.constant = false; + dataConfig.desc = getDstMemDesc(itpd, i); + if (!isGrouped) + dataConfig.desc = MKLDNNExtensionUtils::getUninitTensorDesc(dataConfig.desc); + config.outConfs.push_back(dataConfig); + + outFormats.emplace_back(static_cast(itpd.dst_primitive_desc().desc().data.format)); + } + impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); + + supportedPrimitiveDescriptors.emplace_back(config, impl_type, outFormats); + itpd++; } } } @@ -246,44 +242,41 @@ void MKLDNNDeformableConvolutionNode::initDescriptor(const InferenceEngine::Laye size_t selected_count = 0; for (size_t i = 0; i < descs.size(); i++) { const auto& desc = descs[i]; - try { - primitive_desc_iterator itpd = desc.createPrimitiveDescriptorIterator(getEngine(), attr); - do { - InferenceEngine::LayerConfig cfg; - cfg.dynBatchSupport = true; - for (size_t j = 0; j < desc.inputNumbers(); j++) { - InferenceEngine::DataConfig dataConfig; - dataConfig.inPlace = -1; - dataConfig.constant = false; - dataConfig.desc = getSrcMemDesc(itpd, j); - cfg.inConfs.push_back(dataConfig); - } - - for (size_t j = 0; j < desc.outputNumbers(); j++) { - InferenceEngine::DataConfig dataConfig; - dataConfig.inPlace = -1; - dataConfig.constant = false; - dataConfig.desc = getDstMemDesc(itpd, j); - - cfg.outConfs.push_back(dataConfig); - } - impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); - - if (selected_count == selectedPrimitiveDescriptorIndex) { - if (impl_type != selectedPD->getImplementationType()) { - THROW_IE_EXCEPTION << "Cannot get the original layer configuration!"; - } - rightConfig = cfg; + auto itpd = desc.createPrimitiveDescriptorIterator(getEngine(), attr); + while (itpd.is_not_end()) { + InferenceEngine::LayerConfig cfg; + cfg.dynBatchSupport = true; + for (size_t j = 0; j < desc.inputNumbers(); j++) { + InferenceEngine::DataConfig dataConfig; + dataConfig.inPlace = -1; + dataConfig.constant = false; + dataConfig.desc = getSrcMemDesc(itpd, j); + cfg.inConfs.push_back(dataConfig); + } + + for (size_t j = 0; j < desc.outputNumbers(); j++) { + InferenceEngine::DataConfig dataConfig; + dataConfig.inPlace = -1; + dataConfig.constant = false; + dataConfig.desc = getDstMemDesc(itpd, j); + + cfg.outConfs.push_back(dataConfig); + } + impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); + + if (selected_count == selectedPrimitiveDescriptorIndex) { + if (impl_type != selectedPD->getImplementationType()) { + THROW_IE_EXCEPTION << "Cannot get the original layer configuration!"; } - if (i == descs.size() - 1 && addedNewDesc) { - if (impl_type == selectedPD->getImplementationType()) { - rightConfig = config; - } + rightConfig = cfg; + } + if (i == descs.size() - 1 && addedNewDesc) { + if (impl_type == selectedPD->getImplementationType()) { + rightConfig = config; } - selected_count++; - } while (itpd.next()); - } catch (std::exception& e) { - continue; + } + selected_count++; + itpd++; } } selectedPD->getConfig() = rightConfig; diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_eltwise_node.cpp b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_eltwise_node.cpp index ab8e2ac05dcbec..7609e05bf36112 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_eltwise_node.cpp +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_eltwise_node.cpp @@ -66,6 +66,8 @@ bool MKLDNNEltwiseNode::isWithBroadcast() { if (inDims.size() < outDims.size()) withBroadcast = true; } + if (inDims.size() == 0 && outDims.size()) + withBroadcast = true; } return withBroadcast; @@ -235,7 +237,7 @@ void MKLDNNEltwiseNode::dims_calc(int *dims, const MKLDNNDims &edge_dims) { for (int i = 0; i < ndims; i++) { dims[4 - i] = edge_dims[ndims - 1 - i]; } - if (!(broadcast && edge_dims[0] == getChildEdgeAt(0)->getDims()[0])) + if (edge_dims.ndims() && !(broadcast && edge_dims[0] == getChildEdgeAt(0)->getDims()[0])) dims[batch_dim] = std::min(dims[batch_dim], batchToProcess()); } diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_generic_node.cpp b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_generic_node.cpp index 326f9e4ce44948..030dc7f7f77b05 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_generic_node.cpp +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_generic_node.cpp @@ -120,7 +120,8 @@ void MKLDNNGenericNode::execLayer() { } else { // TODO: Ask the right dims using getShape() from previous node inputDescs.push_back(inputs[inputs.size() - 1]->getTensorDesc()); - inputDescs[inputDescs.size() - 1].getDims()[0] = static_cast(batchToProcess()); + if (inputDescs[inputDescs.size() - 1].getDims().size() > 0) + inputDescs[inputDescs.size() - 1].getDims()[0] = static_cast(batchToProcess()); } } diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_memory_node.cpp b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_memory_node.cpp index 3c17bfd3097fb3..7d9e7049167acd 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_memory_node.cpp +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_memory_node.cpp @@ -60,16 +60,6 @@ void MKLDNNMemoryOutputNode::execute(mkldnn::stream strm) { memcpy(dst_ptr, src_ptr, srcMemory.GetSize()); } -std::string MKLDNNMemoryInputNode::nameFromCombinedName(std::string name) { - auto idSplitter = name.find("/id="); - return name.substr(0, idSplitter); -} - -std::string MKLDNNMemoryInputNode::idFromCombinedName(std::string name) { - auto idSplitter = name.find("/id="); - return name.substr(idSplitter == std::string::npos ? 0 : idSplitter + 4); -} - MKLDNNMemoryInputNode::MKLDNNMemoryInputNode(const InferenceEngine::CNNLayerPtr& layer, const mkldnn::engine& eng, int socket) : MKLDNNInputNode(layer, eng, socket), MKLDNNMemoryNode(layer) { if (created()) { @@ -91,7 +81,6 @@ void MKLDNNMemoryNodeVirtualEdge::registerInput(MKLDNNMemoryInputNode * node) { } else { getExisted()[node->getId()] = node; } - // std::cout <<"[register] " << node << ", size="<< getExisted().size() <<"\n" << std::flush; } void MKLDNNMemoryNodeVirtualEdge::registerOutput(MKLDNNMemoryOutputNode * node) { @@ -104,5 +93,4 @@ void MKLDNNMemoryNodeVirtualEdge::registerOutput(MKLDNNMemoryOutputNode * node) } else { getExisted()[node->getId()] = node; } - // std::cout <<"[register] " << node << ", size="<< getExisted().size() <<"\n" << std::flush; } diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_memory_node.hpp b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_memory_node.hpp index d389d166b2dac6..6ea799d6e7b416 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_memory_node.hpp +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_memory_node.hpp @@ -58,7 +58,6 @@ class MKLDNNMemoryNodeVirtualEdge { InferenceEngine::details::erase_if(getExisted(), [&](const Holder::value_type & it){ return it.second == node; }); - // std::cout <<"[remove] " << node << ", size="<< getExisted().size() <<"\n" << std::flush; } }; @@ -86,12 +85,8 @@ class MKLDNNMemoryOutputNode : public MKLDNNNode, public MKLDNNMemoryNode { static Register reg; }; - class MKLDNNMemoryInputNode : public MKLDNNInputNode, public MKLDNNMemoryNode { - protected: - static std::string nameFromCombinedName(std::string name); - static std::string idFromCombinedName(std::string name); - public: +public: MKLDNNMemoryInputNode(const InferenceEngine::CNNLayerPtr& layer, const mkldnn::engine& eng, int socket); ~MKLDNNMemoryInputNode() override; @@ -104,7 +99,5 @@ class MKLDNNMemoryInputNode : public MKLDNNInputNode, public MKLDNNMemoryNode { static Register reg; }; - - } // namespace MKLDNNPlugin diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_permute_node.cpp b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_permute_node.cpp index 8d8272835996b9..a8013addfb7055 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_permute_node.cpp +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_permute_node.cpp @@ -504,6 +504,64 @@ static void permute_to_102(int MB, MKLDNNMemoryPtr& srcMemPtr, MKLDNNMemoryPtr& }); } +static void permute_to_02341(int MB, MKLDNNMemoryPtr& srcMemPtr, MKLDNNMemoryPtr& dstMemPtr) { + auto src_data = reinterpret_cast(srcMemPtr->GetData()); + auto dst_data = reinterpret_cast(dstMemPtr->GetData()); + src_data += srcMemPtr->GetDescriptor().data.layout_desc.blocking.offset_padding; + dst_data += dstMemPtr->GetDescriptor().data.layout_desc.blocking.offset_padding; + + const int DIM1 = srcMemPtr->GetDims()[1]; + const int DIM2 = srcMemPtr->GetDims()[2]; + const int DIM3 = srcMemPtr->GetDims()[3]; + const int DIM4 = srcMemPtr->GetDims()[4]; + + parallel_for4d(MB, DIM2, DIM3, DIM4, [&](int n, int dim2, int dim3, int dim4) { + for (int dim1 = 0; dim1 < DIM1; dim1++) { + int src_off = n * DIM1 * DIM2 * DIM3 * DIM4 + + dim1 * DIM2 * DIM3 * DIM4 + + dim2 * DIM3 * DIM4 + + dim3 * DIM4 + + dim4; + int dst_off = n * DIM2 * DIM3 * DIM4 * DIM1 + + dim2 * DIM3 * DIM4 * DIM1 + + dim3 * DIM4 * DIM1 + + dim4 * DIM1 + + dim1; + + dst_data[dst_off] = src_data[src_off]; + } + }); +} + +static void permute_to_04123(int MB, MKLDNNMemoryPtr& srcMemPtr, MKLDNNMemoryPtr& dstMemPtr) { + auto src_data = reinterpret_cast(srcMemPtr->GetData()); + auto dst_data = reinterpret_cast(dstMemPtr->GetData()); + src_data += srcMemPtr->GetDescriptor().data.layout_desc.blocking.offset_padding; + dst_data += dstMemPtr->GetDescriptor().data.layout_desc.blocking.offset_padding; + + const int DIM1 = srcMemPtr->GetDims()[1]; + const int DIM2 = srcMemPtr->GetDims()[2]; + const int DIM3 = srcMemPtr->GetDims()[3]; + const int DIM4 = srcMemPtr->GetDims()[4]; + + parallel_for4d(MB, DIM4, DIM1, DIM2, [&](int n, int dim4, int dim1, int dim2) { + for (int dim3 = 0; dim3 < DIM3; dim3++) { + int src_off = n * DIM1 * DIM2 * DIM3 * DIM4 + + dim1 * DIM2 * DIM3 * DIM4 + + dim2 * DIM3 * DIM4 + + dim3 * DIM4 + + dim4; + int dst_off = n * DIM4 * DIM1 * DIM2 * DIM3 + + dim4 * DIM1 * DIM2 * DIM3 + + dim1 * DIM2 * DIM3 + + dim2 * DIM3 + + dim3; + + dst_data[dst_off] = src_data[src_off]; + } + }); +} + std::multimap MKLDNNPermuteNode::OptimizedCases = { {{0, 2, 3, 1}, MKLDNNPermuteNode::PermuteImpl(permute_to_0231, [](int MB, MKLDNNMemoryPtr& srcMemPtr, MKLDNNMemoryPtr& dstMemPtr) { return true; @@ -549,7 +607,13 @@ std::multimap MKLDN })}, {{1, 0, 2}, MKLDNNPermuteNode::PermuteImpl(permute_to_102, [](int MB, MKLDNNMemoryPtr& srcMemPtr, MKLDNNMemoryPtr& dstMemPtr) { return MKLDNNMemory::IsPlainFormat(srcMemPtr->GetFormat()) && MB == srcMemPtr->GetDims()[0]; - })} + })}, + {{0, 2, 3, 4, 1}, MKLDNNPermuteNode::PermuteImpl(permute_to_02341, [](int MB, MKLDNNMemoryPtr& srcMemPtr, MKLDNNMemoryPtr& dstMemPtr) { + return MKLDNNMemory::IsPlainFormat(srcMemPtr->GetFormat()); + })}, + {{0, 4, 1, 2, 3}, MKLDNNPermuteNode::PermuteImpl(permute_to_04123, [](int MB, MKLDNNMemoryPtr& srcMemPtr, MKLDNNMemoryPtr& dstMemPtr) { + return MKLDNNMemory::IsPlainFormat(srcMemPtr->GetFormat()); + })}, }; void MKLDNNPermuteNode::execute(mkldnn::stream strm) { diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_quantize_node.cpp b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_quantize_node.cpp index 1cacf763ee4735..959a4e3f122095 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_quantize_node.cpp +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_quantize_node.cpp @@ -173,15 +173,17 @@ void MKLDNNQuantizeNode::initSupportedPrimitiveDescriptors() { return {config, impl, fmt}; }; - supportedPrimitiveDescriptors.push_back(same(memory::nhwc, ref_any)); - if (isPackedStore()) { - primitive_desc_iterator itpd = descs[0].createPrimitiveDescriptorIterator(getEngine()); - do { + auto itpd = descs[0].createPrimitiveDescriptorIterator(getEngine()); + while (itpd.is_not_end()) { impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); supportedPrimitiveDescriptors.push_back(same(memory::nhwc, impl_type)); - } while (itpd.next()); + itpd++; + } } + + // Ref implementation. Not from MKLDNN. + supportedPrimitiveDescriptors.push_back(same(memory::nhwc, ref_any)); } void MKLDNNQuantizeNode::createPrimitive() { diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_reorder_node.cpp b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_reorder_node.cpp index 1e6d2494b2c71c..14da20c3d8b38d 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_reorder_node.cpp +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_reorder_node.cpp @@ -77,10 +77,10 @@ void MKLDNNReorderNode::createPrimitive() { void MKLDNNReorderNode::createReorderPrimitive(const mkldnn::memory::desc &srcDesc, void* srcPtr, const mkldnn::memory::desc &dstDesc, void* dstPtr) { src_blocked = std::make_shared(getEngine()); - src_blocked->Create(srcDesc, srcPtr); + src_blocked->Create(srcDesc, srcPtr, false); dst_blocked = std::make_shared(getEngine()); - dst_blocked->Create(dstDesc, dstPtr); + dst_blocked->Create(dstDesc, dstPtr, false); mkldnn::primitive_attr attr; @@ -126,6 +126,7 @@ bool MKLDNNReorderNode::created() const { void MKLDNNReorderNode::execute(mkldnn::stream strm) { src_blocked->GetPrimitivePtr()->set_data_handle(getParentEdgeAt(0)->getMemory().GetPrimitive().get_data_handle()); dst_blocked->GetPrimitivePtr()->set_data_handle(getChildEdgeAt(0)->getMemory().GetPrimitive().get_data_handle()); + MKLDNNNode::execute(strm); } diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_rnn.h b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_rnn.h index a5323a45896a5a..184252e52ed897 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_rnn.h +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_rnn.h @@ -20,7 +20,7 @@ class MKLDNNRNN : public MKLDNNNode { void getSupportedDescriptors() override; void createPrimitive() override; bool created() const override; - + using MKLDNNNode::createDescriptor; void createDescriptor(const std::vector& inputDesc, const std::vector& outputDesc, const std::vector &outputFormats); diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_softmax_node.cpp b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_softmax_node.cpp index f8a4cad1c4d2b2..88ef22964c8844 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_softmax_node.cpp +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_softmax_node.cpp @@ -73,14 +73,15 @@ void MKLDNNSoftMaxNode::createPrimitive() { auto prim_desc = softmax_forward::primitive_desc(*selected_desc_ptr, getEngine()); primitive_desc_iterator itpd = descs[0].createPrimitiveDescriptorIterator(getEngine()); - do { + while (itpd.is_not_end()) { impl_desc_type impl_type = parse_impl_name(itpd.get_impl_info_str()); auto primitiveDescriptor = getSelectedPrimitiveDescriptor(); if ((primitiveDescriptor != nullptr) && (impl_type == primitiveDescriptor->getImplementationType())) { itpd.getPrimitiveDescriptor(prim_desc); break; } - } while (itpd.next()); + itpd++; + } prim.reset(new softmax_forward(prim_desc, getParentEdgeAt(0)->getMemory().GetPrimitive(), getChildEdgeAt(0)->getMemory().GetPrimitive())); diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_tensoriterator_node.h b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_tensoriterator_node.h index 2f909bdf852860..cffd6a8a4881f5 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_tensoriterator_node.h +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_tensoriterator_node.h @@ -15,6 +15,7 @@ namespace MKLDNNPlugin { class PortMapHelper { public: + virtual ~PortMapHelper() = default; virtual void execute(int n_iter, mkldnn::stream strm) = 0; protected: std::vector reorders; diff --git a/inference-engine/src/mkldnn_plugin/utils/blob_dump.cpp b/inference-engine/src/mkldnn_plugin/utils/blob_dump.cpp index 737d98e033ac45..875f260f1ef151 100644 --- a/inference-engine/src/mkldnn_plugin/utils/blob_dump.cpp +++ b/inference-engine/src/mkldnn_plugin/utils/blob_dump.cpp @@ -37,7 +37,7 @@ struct IEB_HEADER { }; static IEB_HEADER prepare_header(const TensorDesc& desc) { - IEB_HEADER header = {0}; + IEB_HEADER header = {}; header.magic[0] = IEB_MAGIC[0]; header.magic[1] = IEB_MAGIC[1]; diff --git a/inference-engine/src/vpu/CMakeLists.txt b/inference-engine/src/vpu/CMakeLists.txt index 1cd49785126989..01b93ff08cf3b0 100644 --- a/inference-engine/src/vpu/CMakeLists.txt +++ b/inference-engine/src/vpu/CMakeLists.txt @@ -2,23 +2,11 @@ # SPDX-License-Identifier: Apache-2.0 # -# -# Locate firmware files -# - -if (ENABLE_MYRIAD) - find_file(VPU_FIRMWARE_MA2450_FILE MvNCAPI-ma2450.mvcmd "${VPU_FIRMWARE_MA2450}/mvnc") - find_file(VPU_FIRMWARE_MA2X8X_FILE MvNCAPI-ma2x8x.mvcmd "${VPU_FIRMWARE_MA2X8X}/mvnc") - - if(NOT VPU_FIRMWARE_MA2450_FILE OR NOT VPU_FIRMWARE_MA2X8X_FILE) - message(FATAL_ERROR "[VPU] Missing firmware") - endif() -endif() - # # Build common part # +add_subdirectory(common) add_subdirectory(graph_transformer) add_subdirectory( @@ -29,19 +17,6 @@ add_subdirectory( # Build plugins # -set(plugin_target "") - if(ENABLE_MYRIAD) add_subdirectory(myriad_plugin) - set(plugin_target "myriadPlugin") -endif() - - -if(ENABLE_MYRIAD) - set(firmware_out_dir "$") - add_custom_target(vpu_copy_firmware ALL - COMMAND "${CMAKE_COMMAND}" -E copy "${VPU_FIRMWARE_MA2450_FILE}" "${firmware_out_dir}/MvNCAPI-ma2450.mvcmd" - COMMAND "${CMAKE_COMMAND}" -E copy "${VPU_FIRMWARE_MA2X8X_FILE}" "${firmware_out_dir}/MvNCAPI-ma2x8x.mvcmd" - COMMENT "[VPU] Copy firmware") - endif() diff --git a/inference-engine/src/vpu/common/CMakeLists.txt b/inference-engine/src/vpu/common/CMakeLists.txt new file mode 100644 index 00000000000000..969670cc5d0614 --- /dev/null +++ b/inference-engine/src/vpu/common/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright (C) 2018-2019 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +# + +set(TARGET_NAME "vpu_common_lib") + +file(GLOB_RECURSE SOURCES *.cpp *.hpp *.h) + +add_library(${TARGET_NAME} STATIC ${SOURCES}) + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # TODO: enable some day and fix all warnings + # target_compile_options(${TARGET_NAME} PRIVATE "-Wall") + target_compile_options(${TARGET_NAME} PRIVATE "-Werror=unused-variable") + target_compile_options(${TARGET_NAME} PRIVATE "-Werror=unused-function") + target_compile_options(${TARGET_NAME} PRIVATE "-Werror=strict-aliasing") +endif() + +target_include_directories(${TARGET_NAME} + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/include") + +target_include_directories(${TARGET_NAME} + SYSTEM PUBLIC + "${IE_MAIN_SOURCE_DIR}/include" + "${IE_MAIN_SOURCE_DIR}/src/inference_engine") + +if(WIN32) + target_compile_definitions(${TARGET_NAME} PRIVATE NOMINMAX) + + set_target_properties(${TARGET_NAME} PROPERTIES COMPILE_PDB_NAME ${TARGET_NAME}) +endif() + +add_cpplint_target(${TARGET_NAME}_cpplint FOR_TARGETS ${TARGET_NAME}) +add_cppcheck(${TARGET_NAME}) + +# +# developer package +# + +export(TARGETS ${TARGET_NAME} NAMESPACE IE:: + APPEND FILE "${CMAKE_BINARY_DIR}/targets_developer.cmake") diff --git a/inference-engine/src/vpu/common/include/vpu/parsed_config_base.hpp b/inference-engine/src/vpu/common/include/vpu/parsed_config_base.hpp new file mode 100644 index 00000000000000..32d6bbeab61741 --- /dev/null +++ b/inference-engine/src/vpu/common/include/vpu/parsed_config_base.hpp @@ -0,0 +1,93 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include +#include + +#include + +#include +#include + +namespace vpu { + +VPU_DECLARE_ENUM(ConfigMode, + DEFAULT_MODE = 0, + RUNTIME_MODE = 1, + COMPILE_MODE = 2, +) + +struct ParsedConfigBase { + LogLevel deviceLogLevel = LogLevel::None; + LogLevel hostLogLevel = LogLevel::None; + + bool exclusiveAsyncRequests = false; + + virtual std::map getDefaultConfig() const { return {}; } + + ~ParsedConfigBase() = default; + +protected: + explicit ParsedConfigBase(ConfigMode configMode); + + virtual void checkSupportedValues(const std::unordered_map> &supported, + const std::map &config) const; + virtual void checkUnknownOptions(const std::map &config) const; + virtual void checkInvalidValues(const std::map &config) const; + virtual void checkOptionsAccordingToMode(const std::map &config) const; + + std::map parse(const std::map &config) { + checkInvalidValues(config); + checkUnknownOptions(config); + checkOptionsAccordingToMode(config); + + auto defaultConfig = getDefaultConfig(); + for (auto &&entry : config) { + defaultConfig[entry.first] = entry.second; + } + + return defaultConfig; + } + + virtual void configure(const std::map &config); + + + virtual std::unordered_set getKnownOptions() const; + virtual std::unordered_set getCompileOptions() const { return {}; } + virtual std::unordered_set getRuntimeOptions() const; + +protected: + Logger::Ptr _log; + +private: + ConfigMode _mode = ConfigMode::DEFAULT_MODE; +}; + +template +inline void setOption(T &dst, const V &supported, const std::map &config, const std::string &key) { + auto value = config.find(key); + if (value != config.end()) { + dst = supported.at(value->second); + } +} + +inline void setOption(std::string &dst, const std::map &config, const std::string &key) { + auto value = config.find(key); + if (value != config.end()) { + dst = value->second; + } +} + +template +inline void setOption(T &dst, const std::map &config, const std::string &key, const C &preprocess) { + auto value = config.find(key); + if (value != config.end()) { + dst = preprocess(value->second); + } +} +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/any.hpp b/inference-engine/src/vpu/common/include/vpu/utils/any.hpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/any.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/any.hpp diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/attributes_map.hpp b/inference-engine/src/vpu/common/include/vpu/utils/attributes_map.hpp similarity index 87% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/attributes_map.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/attributes_map.hpp index 98902dc8a37826..4fa69d9203df76 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/attributes_map.hpp +++ b/inference-engine/src/vpu/common/include/vpu/utils/attributes_map.hpp @@ -74,7 +74,15 @@ class AttributesMap final { } template - inline const T& getOrDefault(const std::string& name, const T& def) const { + inline T getOrDefault(const std::string& name) const { + auto it = _tbl.find(name); + if (it != _tbl.end()) { + return it->second.get(); + } + return T(); + } + template + inline T getOrDefault(const std::string& name, const T& def) const { auto it = _tbl.find(name); if (it != _tbl.end()) { return it->second.get(); @@ -82,6 +90,16 @@ class AttributesMap final { return def; } + template + inline T& getOrSet(const std::string& name) { + auto it = _tbl.find(name); + if (it != _tbl.end()) { + return it->second.get(); + } + auto res = _tbl.insert({name, Any(T())}); + assert(res.second); + return res.first->second.get(); + } template inline T& getOrSet(const std::string& name, const T& def) { auto it = _tbl.find(name); diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/auto_scope.hpp b/inference-engine/src/vpu/common/include/vpu/utils/auto_scope.hpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/auto_scope.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/auto_scope.hpp diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/checked_cast.hpp b/inference-engine/src/vpu/common/include/vpu/utils/checked_cast.hpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/checked_cast.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/checked_cast.hpp diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/containers.hpp b/inference-engine/src/vpu/common/include/vpu/utils/containers.hpp similarity index 95% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/containers.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/containers.hpp index 178eb174fd8216..13f15515c6f87b 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/containers.hpp +++ b/inference-engine/src/vpu/common/include/vpu/utils/containers.hpp @@ -30,9 +30,11 @@ namespace vpu { // -// SmallBufAllocator +// SmallVector // +namespace details { + template struct SmallBufElemMemory { static constexpr const size_t ElemSize = sizeof(T); @@ -219,14 +221,12 @@ inline bool operator!=( return a1.getBuf() != a2.getBuf() || a1.getBaseAllocator() != a2.getBaseAllocator(); } -// -// SmallVector -// +} // namespace details template > class SmallVector { - using BufHolder = SmallBufHolder; - using Alloc = SmallBufAllocator; + using BufHolder = details::SmallBufHolder; + using Alloc = details::SmallBufAllocator; using BaseCont = std::vector; public: @@ -236,6 +236,8 @@ class SmallVector { using iterator = typename BaseCont::iterator; using const_iterator = typename BaseCont::const_iterator; + using reverse_iterator = typename BaseCont::reverse_iterator; + using const_reverse_iterator = typename BaseCont::const_reverse_iterator; inline SmallVector() : _allocator(_bufs), _base(_allocator) { _base.reserve(Capacity); @@ -243,7 +245,7 @@ class SmallVector { inline ~SmallVector() = default; - inline explicit SmallVector(size_type count) : _allocator(_bufs), _base(count, _allocator) {} + inline explicit SmallVector(size_type count) : _allocator(_bufs), _base(count, T(), _allocator) {} inline SmallVector(size_type count, const T& value) : _allocator(_bufs), _base(count, value, _allocator) {} inline SmallVector(std::initializer_list init) : _allocator(_bufs), _base(init, _allocator) {} @@ -311,6 +313,13 @@ class SmallVector { inline const_iterator cbegin() const noexcept { return _base.cbegin(); } inline const_iterator cend() const noexcept { return _base.cend(); } + inline reverse_iterator rbegin() noexcept { return _base.rbegin(); } + inline reverse_iterator rend() noexcept { return _base.rend(); } + inline const_reverse_iterator rbegin() const noexcept { return _base.rbegin(); } + inline const_reverse_iterator rend() const noexcept { return _base.rend(); } + inline const_reverse_iterator crbegin() const noexcept { return _base.crbegin(); } + inline const_reverse_iterator crend() const noexcept { return _base.crend(); } + inline bool empty() const noexcept { return _base.empty(); } inline size_type size() const noexcept { return _base.size(); } @@ -337,6 +346,11 @@ class SmallVector { template inline iterator emplace(iterator pos, Args&&... args) { return _base.emplace(pos, std::forward(args)...); } + inline void assign(size_type count, const T& value) { _base.assign(count, value); } + template + inline void assign(InputIt first, InputIt last) { _base.assign(first, last); } + inline void assign(std::initializer_list ilist) { _base.assign(ilist); } + inline void pop_back() { _base.pop_back(); } inline iterator erase(iterator pos) { return _base.erase(pos); } diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/dot_io.hpp b/inference-engine/src/vpu/common/include/vpu/utils/dot_io.hpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/dot_io.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/dot_io.hpp diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/enums.hpp b/inference-engine/src/vpu/common/include/vpu/utils/enums.hpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/enums.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/enums.hpp diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/extra.hpp b/inference-engine/src/vpu/common/include/vpu/utils/extra.hpp similarity index 95% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/extra.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/extra.hpp index 6db9678ddff802..ed3f11769e60bd 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/extra.hpp +++ b/inference-engine/src/vpu/common/include/vpu/utils/extra.hpp @@ -26,7 +26,7 @@ namespace vpu { THROW_IE_EXCEPTION << "[VPU] " #define VPU_THROW_UNLESS(EXPRESSION) \ - if (!(EXPRESSION)) VPU_THROW_EXCEPTION << "AssertionFailed: " << #EXPRESSION // NOLINT + if (!(EXPRESSION)) VPU_THROW_EXCEPTION << "AssertionFailed: " << #EXPRESSION << " " // NOLINT // // Packed structure declaration diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/file_system.hpp b/inference-engine/src/vpu/common/include/vpu/utils/file_system.hpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/file_system.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/file_system.hpp diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/func_ref.hpp b/inference-engine/src/vpu/common/include/vpu/utils/func_ref.hpp similarity index 88% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/func_ref.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/func_ref.hpp index 58454f9e110a8e..c7585fa6acc2d1 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/func_ref.hpp +++ b/inference-engine/src/vpu/common/include/vpu/utils/func_ref.hpp @@ -28,6 +28,12 @@ class FuncRef { "Mismatch between Func and FuncRef prototype"); } + FuncRef(const FuncRef&) = delete; + FuncRef& operator=(const FuncRef&) = delete; + + FuncRef(FuncRef&&) = delete; + FuncRef& operator=(FuncRef&&) = delete; + R operator()(Args... args) const { return _impl(_realFuncPtr, std::forward(args)...); } diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/handle.hpp b/inference-engine/src/vpu/common/include/vpu/utils/handle.hpp similarity index 85% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/handle.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/handle.hpp index 574a26cd7668df..012fb5223bab33 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/handle.hpp +++ b/inference-engine/src/vpu/common/include/vpu/utils/handle.hpp @@ -6,6 +6,7 @@ #include #include +#include #include
@@ -20,12 +21,26 @@ class Handle final { inline Handle(std::nullptr_t) {} // NOLINT - template + template < + typename U, + typename = typename std::enable_if< + std::is_constructible< + std::weak_ptr, + std::shared_ptr + >::value + >::type> inline Handle(const std::shared_ptr& ptr) : _weak(ptr), _plain(ptr.get()) { // NOLINT IE_ASSERT(_plain != nullptr); } - template + template < + typename U, + typename = typename std::enable_if< + std::is_constructible< + std::weak_ptr, + std::weak_ptr + >::value + >::type> inline Handle(const Handle& other) : _weak(other._weak), _plain(other._plain) {} // NOLINT inline Handle(const Handle&) = default; @@ -74,6 +89,14 @@ class Handle final { return _plain; } + template + inline Handle staticCast() const { + if (auto newPtr = std::static_pointer_cast(_weak.lock())) { + return Handle(newPtr); + } + return nullptr; + } + template inline Handle dynamicCast() const { if (auto newPtr = std::dynamic_pointer_cast(_weak.lock())) { diff --git a/inference-engine/src/vpu/common/include/vpu/utils/heap.hpp b/inference-engine/src/vpu/common/include/vpu/utils/heap.hpp new file mode 100644 index 00000000000000..1adb2da9a28185 --- /dev/null +++ b/inference-engine/src/vpu/common/include/vpu/utils/heap.hpp @@ -0,0 +1,104 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include +#include + +namespace vpu { + +// Max-heap. Collects all elements until capacity is reached; then collects only elements lesser than the max one +// Maintains its size not more than specified in constructor. +template +class FixedMaxHeap { +private: + size_t _capacity; + std::vector v; + +public: + explicit FixedMaxHeap(size_t capacity): _capacity(capacity) { + v.reserve(_capacity); + } + + FixedMaxHeap(const FixedMaxHeap&) = delete; + + FixedMaxHeap(FixedMaxHeap &&other): _capacity(other._capacity), v(std::move(other.v)) { + } + + FixedMaxHeap& operator=(FixedMaxHeap &&other) { + _capacity = other._capacity; + v = std::move(other.v); + return *this; + } + + auto begin() -> decltype(v.begin()) { + return v.begin(); + } + + auto end() -> decltype(v.begin()) { + return v.end(); + } + + auto begin() const -> decltype(v.begin()) const { + return v.begin(); + } + + auto end() const -> decltype(v.begin()) const { + return v.end(); + } + + bool empty() const { + return v.empty(); + } + + size_t size() const { + return v.size(); + } + + // keep max-heap of constant size: insert only values smaller than max element, discard others + bool push(const T& val) { + if (_capacity == 0) { + return false; + } + + if (v.size() < _capacity) { + v.push_back(val); + } else { + if (!(val < v.front())) { + return false; + } + std::pop_heap(v.begin(), v.end()); + v[_capacity - 1] = val; + } + + std::push_heap(v.begin(), v.end()); + + return true; + } + + std::vector sorted() const { + std::vector s = v; + std::sort_heap(s.begin(), s.end()); + return s; + } + + void print(std::ostream &o) const { + o << "Heap [" << v.size() << "]: "; + for (int i : v) { + o << i << " "; + } + o << " is_heap: " << std::is_heap(v.begin(), v.end()) << " "; + o << std::endl; + } + + friend std::ostream& operator<<(std::ostream& o, const FixedMaxHeap &h) { + h.print(o); + return o; + } +}; + +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/ie_helpers.hpp b/inference-engine/src/vpu/common/include/vpu/utils/ie_helpers.hpp similarity index 54% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/ie_helpers.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/ie_helpers.hpp index 4b797982457b5a..2c48129acb7bdc 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/ie_helpers.hpp +++ b/inference-engine/src/vpu/common/include/vpu/utils/ie_helpers.hpp @@ -5,14 +5,23 @@ #pragma once #include +#include namespace vpu { namespace ie = InferenceEngine; +VPU_DECLARE_ENUM(LayoutPreference, + AUTO, + ChannelMajor, // CHW, NCHW, NCDHW + ChannelMinor // HWC, NHWC, NDHWC +) + +InferenceEngine::Layout deviceLayout(InferenceEngine::Layout const& layout, + vpu::LayoutPreference const& layoutPreference); + ie::Blob::Ptr getBlobFP16(const ie::Blob::Ptr& in); -ie::Blob::Ptr copyBlob(const ie::Blob::Ptr& in); ie::Blob::Ptr copyBlob(const ie::Blob::Ptr& in, ie::Layout outLayout); void copyBlob(const ie::Blob::Ptr& in, const ie::Blob::Ptr& out); diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/io.hpp b/inference-engine/src/vpu/common/include/vpu/utils/io.hpp similarity index 91% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/io.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/io.hpp index 1b222b44906893..c583c45a7bbde6 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/io.hpp +++ b/inference-engine/src/vpu/common/include/vpu/utils/io.hpp @@ -90,16 +90,35 @@ std::string toString(const T& val) noexcept; // Implementation // +namespace details { + template -void printTo(std::ostream& os, const T& val) noexcept { +auto printToDefault(std::ostream& os, const T& val, int) noexcept -> decltype(os << val) { try { - os << val; + return os << val; } catch (...) { std::cerr << "[VPU] Unknown error while printing\n"; std::abort(); } } +template +void printToDefault(std::ostream& os, const T& val, ...) noexcept { + try { + os << ""; + } catch (...) { + std::cerr << "[VPU] Unknown error while printing\n"; + std::abort(); + } +} + +} // namespace details + +template +inline void printTo(std::ostream& os, const T& val) noexcept { + details::printToDefault(os, val, 0); +} + template void printTo(std::ostream& os, const std::pair& p) noexcept { try { diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/logger.hpp b/inference-engine/src/vpu/common/include/vpu/utils/logger.hpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/logger.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/logger.hpp diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/numeric.hpp b/inference-engine/src/vpu/common/include/vpu/utils/numeric.hpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/numeric.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/numeric.hpp diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/optional.hpp b/inference-engine/src/vpu/common/include/vpu/utils/optional.hpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/optional.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/optional.hpp diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/perf_report.hpp b/inference-engine/src/vpu/common/include/vpu/utils/perf_report.hpp similarity index 60% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/perf_report.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/perf_report.hpp index f166a5c7e76a64..c5578dd7eb1d52 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/perf_report.hpp +++ b/inference-engine/src/vpu/common/include/vpu/utils/perf_report.hpp @@ -6,9 +6,13 @@ #include #include +#include #include +#include #include +#include +#include #include @@ -18,12 +22,34 @@ namespace ie = InferenceEngine; struct StageMetaInfo final { ie::InferenceEngineProfileInfo::LayerStatus status = ie::InferenceEngineProfileInfo::LayerStatus::NOT_RUN; + std::vector outPrecisions; + std::vector outLayouts; + + int inputsNum = 0; std::string layerName; std::string layerType; + std::string displayStageName; + std::string stageName; std::string stageType; + + int execOrder = -1; + float execTime = 0; +}; + +struct DataMetaInfo final { + std::string name; + ie::TensorDesc desc; + size_t parentIndex; + std::vector childrenIndices; +}; + +struct GraphMetaInfo final { + std::string graphName; + std::vector stagesMeta; + std::vector datasMeta; }; VPU_DECLARE_ENUM(PerfReport, diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/range.hpp b/inference-engine/src/vpu/common/include/vpu/utils/range.hpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/range.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/range.hpp diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/simple_math.hpp b/inference-engine/src/vpu/common/include/vpu/utils/simple_math.hpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/simple_math.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/simple_math.hpp diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/string.hpp b/inference-engine/src/vpu/common/include/vpu/utils/string.hpp similarity index 92% rename from inference-engine/src/vpu/graph_transformer/include/vpu/utils/string.hpp rename to inference-engine/src/vpu/common/include/vpu/utils/string.hpp index b4a7af9cc94b2f..05d87b02900c36 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/utils/string.hpp +++ b/inference-engine/src/vpu/common/include/vpu/utils/string.hpp @@ -19,7 +19,7 @@ namespace vpu { namespace ie = InferenceEngine; -namespace impl { +namespace details { inline void insertToContainer(std::vector& cont, std::string&& val) { cont.emplace_back(val); @@ -42,7 +42,7 @@ inline void insertToContainer(ie::details::caseless_set& cont, std: cont.emplace(val); } -} // namespace impl +} // namespace details template void splitStringList(const std::string& str, Cont& out, char delim) { @@ -59,7 +59,7 @@ void splitStringList(const std::string& str, Cont& out, char delim) { continue; } - impl::insertToContainer(out, std::move(elem)); + details::insertToContainer(out, std::move(elem)); } } diff --git a/inference-engine/src/vpu/common/src/parsed_config_base.cpp b/inference-engine/src/vpu/common/src/parsed_config_base.cpp new file mode 100644 index 00000000000000..40396777ffd091 --- /dev/null +++ b/inference-engine/src/vpu/common/src/parsed_config_base.cpp @@ -0,0 +1,126 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include
+#include + +namespace vpu { +namespace { +template +void check_input(const I &input, const T &options, const C &check) { + for (const auto& option : options) { + auto input_entry = input.find(option.first); + if (input_entry == input.end()) { + continue; + } + + auto input_key = input_entry->first; + auto input_val = input_entry->second; + auto values = option.second; + + if (!check(values, input_val)) { + THROW_IE_EXCEPTION << "Incorrect value " << "\"" << input_val << "\"" << " for key " << input_key; + } + } +} + +} // namespace + +ParsedConfigBase::ParsedConfigBase(ConfigMode configMode): _mode(configMode) { + _log = std::make_shared("Config", LogLevel::Warning, consoleOutput()); +} + +void ParsedConfigBase::checkSupportedValues( + const std::unordered_map> &supported, + const std::map &config) const { + auto contains = [](const std::unordered_set &supported, const std::string &option) { + return supported.find(option) != supported.end(); + }; + + check_input(config, supported, contains); +} + +void ParsedConfigBase::checkUnknownOptions(const std::map &config) const { + auto knownOptions = getKnownOptions(); + for (auto &&entry : config) { + if (knownOptions.find(entry.first) == knownOptions.end()) { + THROW_IE_EXCEPTION << NOT_FOUND_str << entry.first << " key is not supported for VPU"; + } + } +} + +void ParsedConfigBase::checkOptionsAccordingToMode(const std::map &config) const { + auto compileOptions = getCompileOptions(); + for (auto &&entry : config) { + std::stringstream errorMsgStream; + if (compileOptions.find(entry.first) != compileOptions.end() && _mode == ConfigMode::RUNTIME_MODE) { + _log->warning("%s option will be ignored. Seems you are using compiled graph", entry.first); + } + } +} + +void ParsedConfigBase::checkInvalidValues(const std::map &config) const { + const std::unordered_map> supported_values = { + { CONFIG_KEY(LOG_LEVEL), + { CONFIG_VALUE(LOG_NONE), CONFIG_VALUE(LOG_WARNING), CONFIG_VALUE(LOG_INFO), CONFIG_VALUE(LOG_DEBUG) }}, + { VPU_CONFIG_KEY(LOG_LEVEL), + { CONFIG_VALUE(LOG_NONE), CONFIG_VALUE(LOG_WARNING), CONFIG_VALUE(LOG_INFO), CONFIG_VALUE(LOG_DEBUG) }}, + { CONFIG_KEY(EXCLUSIVE_ASYNC_REQUESTS), { CONFIG_VALUE(YES), CONFIG_VALUE(NO) }} + }; + + checkSupportedValues(supported_values, config); +} + +void ParsedConfigBase::configure(const std::map &config) { + static const std::unordered_map logLevels = { + { CONFIG_VALUE(LOG_NONE), LogLevel::None }, + { CONFIG_VALUE(LOG_WARNING), LogLevel::Warning }, + { CONFIG_VALUE(LOG_INFO), LogLevel::Info }, + { CONFIG_VALUE(LOG_DEBUG), LogLevel::Debug } + }; + + setOption(hostLogLevel, logLevels, config, CONFIG_KEY(LOG_LEVEL)); + setOption(deviceLogLevel, logLevels, config, VPU_CONFIG_KEY(LOG_LEVEL)); + +#ifndef NDEBUG + if (auto envVar = std::getenv("IE_VPU_LOG_LEVEL")) { + hostLogLevel = logLevels.at(envVar); + } +#endif + + static const std::unordered_map switches = { + { CONFIG_VALUE(YES), true }, + { CONFIG_VALUE(NO), false } + }; + + setOption(exclusiveAsyncRequests, switches, config, CONFIG_KEY(EXCLUSIVE_ASYNC_REQUESTS)); +} + +std::unordered_set ParsedConfigBase::getRuntimeOptions() const { + return { CONFIG_KEY(EXCLUSIVE_ASYNC_REQUESTS), + CONFIG_KEY(LOG_LEVEL), + VPU_CONFIG_KEY(LOG_LEVEL)}; } + +std::unordered_set ParsedConfigBase::getKnownOptions() const { + std::unordered_set knownOptions; + auto compileOptions = getCompileOptions(); + knownOptions.insert(compileOptions.begin(), compileOptions.end()); + + auto runtimeOptions = getRuntimeOptions(); + knownOptions.insert(runtimeOptions.begin(), runtimeOptions.end()); + + return knownOptions; +} +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/utils/dot_io.cpp b/inference-engine/src/vpu/common/src/utils/dot_io.cpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/src/utils/dot_io.cpp rename to inference-engine/src/vpu/common/src/utils/dot_io.cpp diff --git a/inference-engine/src/vpu/graph_transformer/src/utils/enums.cpp b/inference-engine/src/vpu/common/src/utils/enums.cpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/src/utils/enums.cpp rename to inference-engine/src/vpu/common/src/utils/enums.cpp diff --git a/inference-engine/src/vpu/graph_transformer/src/utils/file_system.cpp b/inference-engine/src/vpu/common/src/utils/file_system.cpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/src/utils/file_system.cpp rename to inference-engine/src/vpu/common/src/utils/file_system.cpp diff --git a/inference-engine/src/vpu/graph_transformer/src/utils/ie_helpers.cpp b/inference-engine/src/vpu/common/src/utils/ie_helpers.cpp similarity index 50% rename from inference-engine/src/vpu/graph_transformer/src/utils/ie_helpers.cpp rename to inference-engine/src/vpu/common/src/utils/ie_helpers.cpp index df409147db4b27..155e5aa9098137 100644 --- a/inference-engine/src/vpu/graph_transformer/src/utils/ie_helpers.cpp +++ b/inference-engine/src/vpu/common/src/utils/ie_helpers.cpp @@ -2,23 +2,46 @@ // SPDX-License-Identifier: Apache-2.0 // +#include +#include #include #include #include
#include #include +#include #include #include -#include namespace vpu { -ie::Blob::Ptr getBlobFP16(const ie::Blob::Ptr& in) { - VPU_PROFILE(getBlobFP16); +InferenceEngine::Layout deviceLayout(InferenceEngine::Layout const& layout, + vpu::LayoutPreference const& layoutPreference) { + using namespace InferenceEngine; + auto ChannelMajor = vpu::LayoutPreference::ChannelMajor; + auto ChannelMinor = vpu::LayoutPreference::ChannelMinor; + + if (layoutPreference == ChannelMajor) { + if (layout == NHWC) + return NCHW; + if (layout == NDHWC) + return NCDHW; + } + + if (layoutPreference == ChannelMinor) { + if (layout == NCHW) + return NHWC; + if (layout == NCDHW) + return NDHWC; + } + + return layout; +} - const auto& env = CompileEnv::get(); +ie::Blob::Ptr getBlobFP16(const ie::Blob::Ptr& in) { + IE_PROFILING_AUTO_SCOPE(getBlobFP16); auto inDesc = in->getTensorDesc(); @@ -27,7 +50,7 @@ ie::Blob::Ptr getBlobFP16(const ie::Blob::Ptr& in) { if (precision == ie::Precision::FP16) return in; - if (precision != ie::Precision::FP32 || !env.config.allowFP32Models) { + if (precision != ie::Precision::FP32) { VPU_THROW_EXCEPTION << "Unsupported precision " << precision.name(); } @@ -41,10 +64,6 @@ ie::Blob::Ptr getBlobFP16(const ie::Blob::Ptr& in) { return out; } -ie::Blob::Ptr copyBlob(const ie::Blob::Ptr& in) { - return copyBlob(in, in->getTensorDesc().getLayout()); -} - ie::Blob::Ptr copyBlob(const ie::Blob::Ptr& in, ie::Layout outLayout) { auto inDesc = in->getTensorDesc(); @@ -59,18 +78,33 @@ ie::Blob::Ptr copyBlob(const ie::Blob::Ptr& in, ie::Layout outLayout) { } void copyBlob(const ie::Blob::Ptr& in, const ie::Blob::Ptr& out) { - auto inLayout = in->getTensorDesc().getLayout(); - auto outLayout = out->getTensorDesc().getLayout(); + const auto inLayout = in->getTensorDesc().getLayout(); + const auto outLayout = out->getTensorDesc().getLayout(); + + const auto& inDims = in->getTensorDesc().getDims(); + const auto& outDims = out->getTensorDesc().getDims(); + + IE_ASSERT(inDims == outDims); if (inLayout != outLayout) { - IE_ASSERT(inLayout == ie::Layout::NCHW || inLayout == ie::Layout::NHWC); - IE_ASSERT(outLayout == ie::Layout::NCHW || outLayout == ie::Layout::NHWC); + if (outDims.size() == 4) { + IE_ASSERT(inLayout == ie::Layout::NCHW || inLayout == ie::Layout::NHWC); + IE_ASSERT(outLayout == ie::Layout::NCHW || outLayout == ie::Layout::NHWC); + + if (outDims[1] != 1 && (outDims[2] != 1 || outDims[3] != 1)) { + ie::blob_copy(in, out); + return; + } + } - const auto& dims = out->getTensorDesc().getDims(); + if (outDims.size() == 5) { + IE_ASSERT(inLayout == ie::Layout::NCDHW || inLayout == ie::Layout::NDHWC); + IE_ASSERT(outLayout == ie::Layout::NCDHW || outLayout == ie::Layout::NDHWC); - if ((dims[0] != 1 || dims[1] != 1) && (dims[2] != 1 || dims[3] != 1)) { - ie::blob_copy(in, out); - return; + if (outDims[1] != 1 && (outDims[2] != 1 || outDims[3] != 1 || outDims[4] != 1)) { + ie::blob_copy(in, out); + return; + } } } diff --git a/inference-engine/src/vpu/graph_transformer/src/utils/io.cpp b/inference-engine/src/vpu/common/src/utils/io.cpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/src/utils/io.cpp rename to inference-engine/src/vpu/common/src/utils/io.cpp diff --git a/inference-engine/src/vpu/graph_transformer/src/utils/logger.cpp b/inference-engine/src/vpu/common/src/utils/logger.cpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/src/utils/logger.cpp rename to inference-engine/src/vpu/common/src/utils/logger.cpp diff --git a/inference-engine/src/vpu/graph_transformer/src/utils/perf_report.cpp b/inference-engine/src/vpu/common/src/utils/perf_report.cpp similarity index 97% rename from inference-engine/src/vpu/graph_transformer/src/utils/perf_report.cpp rename to inference-engine/src/vpu/common/src/utils/perf_report.cpp index 5d5d2f57f3d37c..10f67c71af89dc 100644 --- a/inference-engine/src/vpu/graph_transformer/src/utils/perf_report.cpp +++ b/inference-engine/src/vpu/common/src/utils/perf_report.cpp @@ -55,7 +55,7 @@ std::map parsePerformanceReport( } if (perfReport == PerfReport::PerStage) { - outPerfMap[stageMeta.stageName] = profInfo; + outPerfMap[stageMeta.displayStageName] = profInfo; } else if (perfReport == PerfReport::PerLayer) { auto it = outPerfMap.find(stageMeta.layerName); if (it == outPerfMap.end()) { diff --git a/inference-engine/src/vpu/graph_transformer/src/utils/simple_math.cpp b/inference-engine/src/vpu/common/src/utils/simple_math.cpp similarity index 100% rename from inference-engine/src/vpu/graph_transformer/src/utils/simple_math.cpp rename to inference-engine/src/vpu/common/src/utils/simple_math.cpp diff --git a/inference-engine/src/vpu/vpu_custom_kernels/binary_layers.cl b/inference-engine/src/vpu/custom_kernels/binary_layers.cl similarity index 100% rename from inference-engine/src/vpu/vpu_custom_kernels/binary_layers.cl rename to inference-engine/src/vpu/custom_kernels/binary_layers.cl diff --git a/inference-engine/src/vpu/vpu_custom_kernels/ctc.cl b/inference-engine/src/vpu/custom_kernels/ctc.cl similarity index 100% rename from inference-engine/src/vpu/vpu_custom_kernels/ctc.cl rename to inference-engine/src/vpu/custom_kernels/ctc.cl diff --git a/inference-engine/src/vpu/custom_kernels/customLayerBindings.xml b/inference-engine/src/vpu/custom_kernels/customLayerBindings.xml new file mode 100644 index 00000000000000..69ba1efc76571b --- /dev/null +++ b/inference-engine/src/vpu/custom_kernels/customLayerBindings.xml @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inference-engine/src/vpu/vpu_custom_kernels/cvtf32f16.cl b/inference-engine/src/vpu/custom_kernels/cvtf32f16.cl similarity index 100% rename from inference-engine/src/vpu/vpu_custom_kernels/cvtf32f16.cl rename to inference-engine/src/vpu/custom_kernels/cvtf32f16.cl diff --git a/inference-engine/src/vpu/vpu_custom_kernels/cvtu8f16.cl b/inference-engine/src/vpu/custom_kernels/cvtu8f16.cl similarity index 100% rename from inference-engine/src/vpu/vpu_custom_kernels/cvtu8f16.cl rename to inference-engine/src/vpu/custom_kernels/cvtu8f16.cl diff --git a/inference-engine/src/vpu/vpu_custom_kernels/grn.cl b/inference-engine/src/vpu/custom_kernels/grn.cl similarity index 100% rename from inference-engine/src/vpu/vpu_custom_kernels/grn.cl rename to inference-engine/src/vpu/custom_kernels/grn.cl diff --git a/inference-engine/src/vpu/vpu_custom_kernels/mvn.cl b/inference-engine/src/vpu/custom_kernels/mvn.cl similarity index 100% rename from inference-engine/src/vpu/vpu_custom_kernels/mvn.cl rename to inference-engine/src/vpu/custom_kernels/mvn.cl diff --git a/inference-engine/src/vpu/custom_kernels/region_chw.cl b/inference-engine/src/vpu/custom_kernels/region_chw.cl new file mode 100644 index 00000000000000..2aae3a6f71d5e8 --- /dev/null +++ b/inference-engine/src/vpu/custom_kernels/region_chw.cl @@ -0,0 +1,85 @@ +// Copyright (C) 2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma OPENCL EXTENSION cl_khr_fp16 : enable + +#define NUM_CLASSES 80 + +static void logistic_activate(__global const half* restrict src_data, + __global half* restrict dst_data, + int offset) +{ + half val = src_data[offset]; + val = 1.0f/(1.0f + half_exp(-val)); + dst_data[offset] = val; +} + +__kernel void region_ocl(__global const half* restrict src_data, + __global half* restrict dst_data, + int W, + int H, + int classes, + int coords, + int num, + int maskSize, + int doSoftmax) +{ + int box_sz = H * W * (classes + coords + 1); + int pixel_pos =  min((int)get_global_id(0), H*W); + int box = get_global_id(1); + + //if (pixel_pos >= H*W) return; + + logistic_activate(src_data, dst_data, box * box_sz + pixel_pos + 0*H*W); + logistic_activate(src_data, dst_data, box * box_sz + pixel_pos + 1*H*W); + + //copy plane 2 and 3 + dst_data[box * box_sz + pixel_pos + 2*H*W] = src_data[box * box_sz + pixel_pos + 2*H*W]; + dst_data[box * box_sz + pixel_pos + 3*H*W] = src_data[box * box_sz + pixel_pos + 3*H*W]; + + logistic_activate(src_data, dst_data, box * box_sz + pixel_pos + 4*H*W); + + int data_offset = box * box_sz + (coords + 1) * W * H; + + __private half data[NUM_CLASSES]; + + if (doSoftmax) { + half max_val = src_data[data_offset + 0*H*W + pixel_pos]; + for (int c = 0; c < classes; c++) { + half tmp = src_data[data_offset + c*H*W + pixel_pos]; + data[c] = tmp; + max_val = max( max_val, tmp); + } + + half expSum = 0.0f; + + for (int c = 0; c < classes; c++) { + half tmp = half_exp(data[c] - max_val); + data[c] = tmp; + expSum += tmp; + } + for (int c = 0; c < classes; c++) { + data[c] = data[c] / expSum; + } + + for (int c = 0; c < classes; c++) { + dst_data[data_offset + c*H*W + pixel_pos + 0] = data[c]; + } + } + else { + for (int i = 0; i < classes; i++) { + logistic_activate(src_data, dst_data, box * box_sz + pixel_pos + (5 + i)*H*W); + } + } +} diff --git a/inference-engine/src/vpu/custom_kernels/region_chw_m7_branch0.cl b/inference-engine/src/vpu/custom_kernels/region_chw_m7_branch0.cl new file mode 100644 index 00000000000000..4a8b3f0df8676c --- /dev/null +++ b/inference-engine/src/vpu/custom_kernels/region_chw_m7_branch0.cl @@ -0,0 +1,68 @@ +// Copyright (C) 2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma OPENCL EXTENSION cl_khr_fp16 : enable + +#define NUM_CLASSES 80 + +static void logistic_activate(__global const half* restrict src_data, + __global half* restrict dst_data, + int offset) +{ + half val = src_data[offset]; + val = 1.0f/(1.0f + native_exp(-val)); + dst_data[offset] = val; +} + +__kernel void region_ocl(__global const half* restrict src_data, + __global half* restrict dst_data, + int W, + int H, + int classes, + int coords) +{ + const int box_sz = H * W * (classes + coords + 1); + const int pixel_pos = min((int)get_global_id(0), ((H*W) - 1)); + const int box = get_global_id(1); + + logistic_activate(src_data, dst_data, box * box_sz + pixel_pos + 0*H*W); + logistic_activate(src_data, dst_data, box * box_sz + pixel_pos + 1*H*W); + + //copy plane 2 and 3 + dst_data[box * box_sz + pixel_pos + 2*H*W] = src_data[box * box_sz + pixel_pos + 2*H*W]; + dst_data[box * box_sz + pixel_pos + 3*H*W] = src_data[box * box_sz + pixel_pos + 3*H*W]; + + logistic_activate(src_data, dst_data, box * box_sz + pixel_pos + 4*H*W); + int data_offset = box * box_sz + (coords + 1) * W * H; + + __private half data[NUM_CLASSES]; + + half max_val = src_data[data_offset + 0*H*W + pixel_pos]; + for (int c = 0; c < classes; c++) { + half tmp = src_data[data_offset + c*H*W + pixel_pos]; + data[c] = tmp; + max_val = max( max_val, tmp); + } + + half expSum = 0.0f; + + for (int c = 0; c < classes; c++) { + half tmp = half_exp(data[c] - max_val); + data[c] = tmp; + expSum += tmp; + } + for (int c = 0; c < classes; c++) { + dst_data[data_offset + c*H*W + pixel_pos + 0] = data[c] / expSum; + } +} diff --git a/inference-engine/src/vpu/custom_kernels/region_chw_m7_branch1.cl b/inference-engine/src/vpu/custom_kernels/region_chw_m7_branch1.cl new file mode 100644 index 00000000000000..059e3dd093ab91 --- /dev/null +++ b/inference-engine/src/vpu/custom_kernels/region_chw_m7_branch1.cl @@ -0,0 +1,53 @@ +// Copyright (C) 2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma OPENCL EXTENSION cl_khr_fp16 : enable + +#define NUM_CLASSES 80 + +static void logistic_activate(__global const half* restrict src_data, + __global half* restrict dst_data, + int offset) +{ + half val = src_data[offset]; + val = 1.0f/(1.0f + native_exp(-val)); + dst_data[offset] = val; +} + +__kernel void region_ocl(__global const half* restrict src_data, + __global half* restrict dst_data, + int W, + int H, + int classes, + int coords) +{ + int box_sz = H * W * (classes + coords + 1); + int pixel_pos = min((int)get_global_id(0), ((H*W) - 1)); + int box = get_global_id(1); + + logistic_activate(src_data, dst_data, box * box_sz + pixel_pos + 0*H*W); + logistic_activate(src_data, dst_data, box * box_sz + pixel_pos + 1*H*W); + + //copy plane 2 and 3 + dst_data[box * box_sz + pixel_pos + 2*H*W] = src_data[box * box_sz + pixel_pos + 2*H*W]; + dst_data[box * box_sz + pixel_pos + 3*H*W] = src_data[box * box_sz + pixel_pos + 3*H*W]; + + logistic_activate(src_data, dst_data, box * box_sz + pixel_pos + 4*H*W); + + int data_offset = box * box_sz + (coords + 1) * W * H; + + for (int i = 0; i < classes; i++) { + logistic_activate(src_data, dst_data, box * box_sz + pixel_pos + (5 + i)*H*W); + } +} diff --git a/inference-engine/src/vpu/vpu_custom_kernels/reorg_chw.cl b/inference-engine/src/vpu/custom_kernels/reorg_chw.cl similarity index 100% rename from inference-engine/src/vpu/vpu_custom_kernels/reorg_chw.cl rename to inference-engine/src/vpu/custom_kernels/reorg_chw.cl diff --git a/inference-engine/src/vpu/vpu_custom_kernels/reorg_chw_local.cl b/inference-engine/src/vpu/custom_kernels/reorg_chw_local.cl similarity index 100% rename from inference-engine/src/vpu/vpu_custom_kernels/reorg_chw_local.cl rename to inference-engine/src/vpu/custom_kernels/reorg_chw_local.cl diff --git a/inference-engine/src/vpu/vpu_custom_kernels/reorg_chw_stack.cl b/inference-engine/src/vpu/custom_kernels/reorg_chw_stack.cl similarity index 100% rename from inference-engine/src/vpu/vpu_custom_kernels/reorg_chw_stack.cl rename to inference-engine/src/vpu/custom_kernels/reorg_chw_stack.cl diff --git a/inference-engine/src/vpu/custom_kernels/reorg_hwc.cl b/inference-engine/src/vpu/custom_kernels/reorg_hwc.cl new file mode 100644 index 00000000000000..9d0d475219ddcb --- /dev/null +++ b/inference-engine/src/vpu/custom_kernels/reorg_hwc.cl @@ -0,0 +1,64 @@ +// Copyright (C) 2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#define MIN(v1, v2) ((v1) < (v2) ? (v1) : (v2)) + +#pragma OPENCL EXTENSION cl_khr_fp16 : enable + +__kernel void reorg(__global half* restrict src, + __global half* restrict out, + int h, + int w, + int stride) +{ + int j = MIN(get_global_id(0), h-1); + + int k = get_global_id(1); + int c = get_global_size(1); + + int out_c = c / (stride * stride); + int oc = c * (stride * stride); + int oh = h / stride; + int ow = w / stride; + + int in_index = w * (j + h*k); + + int new_z = in_index / (oh*ow); + int new_y = (in_index %(oh*ow)) / ow; + int new_x = (in_index %(oh*ow)) % ow; + int new_index = new_z + new_x * oc + new_y * oc * ow; + + in_index++; + + int c2 = k % out_c; + int offset = k / out_c; + int w2 = 0 * stride + offset % stride; + int h2 = j * stride + offset / stride; + int out_index = w2 + w * stride * (h2 + h * stride * c2); + + for (int i = 0; i < w; ++i, out_index+=stride, in_index++) + { + // repacking coordinates + int k0 = out_index / (h*w); + int j0 = (out_index % (h*w)) / w; + int i0 = (out_index % (h*w)) % w; + int out_index_repack = k0 + c * i0 + c * w * j0; + out[new_index] = src[out_index_repack]; + + int new_z = in_index / (oh*ow); + int new_y = (in_index %(oh*ow)) / ow; + int new_x = (in_index %(oh*ow)) % ow; + new_index = new_z + new_x * oc + new_y * oc * ow; + } +} diff --git a/inference-engine/src/vpu/vpu_custom_kernels/resample_nn.cl b/inference-engine/src/vpu/custom_kernels/resample_nn.cl similarity index 93% rename from inference-engine/src/vpu/vpu_custom_kernels/resample_nn.cl rename to inference-engine/src/vpu/custom_kernels/resample_nn.cl index 3bf8a5fc281673..526bffbed7f3b1 100644 --- a/inference-engine/src/vpu/vpu_custom_kernels/resample_nn.cl +++ b/inference-engine/src/vpu/custom_kernels/resample_nn.cl @@ -18,8 +18,7 @@ kernel void resample_nearest(__global const half* restrict src, __global half* restrict dst, int iw, int ih, - float fx, - float fy, + float factor, int ow, int oh, int channels) @@ -28,6 +27,9 @@ kernel void resample_nearest(__global const half* restrict src, int c = get_global_id(1); int b = get_global_id(2); + float fx = 1.f / factor; + float fy = 1.f / factor; + __global const half* start_src = src + b * iw * ih * channels + iw * ih * c; __global half* start_dst = dst + b * ow * oh * channels + ow * oh * c; diff --git a/inference-engine/src/vpu/custom_kernels/resample_with_antialias.cl b/inference-engine/src/vpu/custom_kernels/resample_with_antialias.cl new file mode 100644 index 00000000000000..618c84e813bd60 --- /dev/null +++ b/inference-engine/src/vpu/custom_kernels/resample_with_antialias.cl @@ -0,0 +1,75 @@ +// Copyright (C) 2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma OPENCL EXTENSION cl_khr_fp16 : enable + +static inline float triangleCoeff(float x) +{ + return 1.0f - fabs(x);//fmax(0.0f, 1 - fabs(x)); +} +__kernel void resample_with_antialias(const __global half* restrict src, + __global half* restrict dst, + int iw, + int ih, + float factor, + int ow, + int oh, + int channels) +{ + int oy = min((int)get_global_id(0), oh-1); + int c = get_global_id(1); + int b = get_global_id(2); + + float fx = 1.f / factor; + float fy = 1.f / factor; + + float ax = 1.0f / fx; + float ay = 1.0f / fy; + + int rx = (fx < 1.0f) ? 2 : ceil((1.0f)/ax); + int ry = (fy < 1.0f) ? 2 : ceil((1.0f)/ay); + + const __global half* restrict start_src = src + b * iw * ih * channels + iw * ih * c; + __global half* restrict start_dst = dst + b * ow * oh * channels + ow * oh * c; + + for (int ox = 0; ox < ow; ox++) + { + float ix_r0 = ox*fx + fx / 2.0f - 0.5f; + float iy_r0 = oy*fy + fy / 2.0f - 0.5f; + int ix_r1 = (int)(round(ix_r0)); + int iy_r1 = (int)(round(iy_r0)); + + float wsum = 0.f; + float sum = 0.f; + + for (int y = iy_r1 - ry; y <= iy_r1 + ry; y++) + { + for (int x = ix_r1 - rx; x <= ix_r1 + rx; x++) + { + if (y < 0 || x < 0) continue; + if (y >= (int)ih || x >= (int)iw) continue; + + float dx = ix_r0 - x; + float dy = iy_r0 - y; + + float w = ax*triangleCoeff(ax*dx) * ay*triangleCoeff(ay*dy); + + sum += w * start_src[y*iw + x]; + wsum += w; + } + } + + start_dst[oy*ow + ox] = (!wsum) ? (half)0.0f : (half)(sum / wsum); + } +} diff --git a/inference-engine/src/vpu/vpu_custom_kernels/shuffle_channels.cl b/inference-engine/src/vpu/custom_kernels/shuffle_channels.cl similarity index 100% rename from inference-engine/src/vpu/vpu_custom_kernels/shuffle_channels.cl rename to inference-engine/src/vpu/custom_kernels/shuffle_channels.cl diff --git a/inference-engine/src/vpu/graph_transformer/CMakeLists.txt b/inference-engine/src/vpu/graph_transformer/CMakeLists.txt index 80cbcba30373fe..879d5838b336f5 100644 --- a/inference-engine/src/vpu/graph_transformer/CMakeLists.txt +++ b/inference-engine/src/vpu/graph_transformer/CMakeLists.txt @@ -21,6 +21,7 @@ endif() target_include_directories(${TARGET_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") + target_include_directories(${TARGET_NAME} SYSTEM PUBLIC "${IE_MAIN_SOURCE_DIR}/thirdparty/pugixml/src" @@ -28,7 +29,7 @@ target_include_directories(${TARGET_NAME} "${IE_MAIN_SOURCE_DIR}/src/inference_engine" "${IE_MAIN_SOURCE_DIR}/thirdparty/movidius/mvnc/include") -target_link_libraries(${TARGET_NAME} PUBLIC pugixml) +target_link_libraries(${TARGET_NAME} PUBLIC pugixml vpu_common_lib) if(WIN32) target_compile_definitions(${TARGET_NAME} PRIVATE NOMINMAX) @@ -52,6 +53,8 @@ if (WIN32) target_include_directories(${TARGET_NAME}_test_static SYSTEM PUBLIC ${target_includes}) set_target_properties(${TARGET_NAME}_test_static PROPERTIES COMPILE_PDB_NAME ${TARGET_NAME}_test_static) + + target_link_libraries(${TARGET_NAME}_test_static PUBLIC vpu_common_lib) else() add_library(${TARGET_NAME}_test_static ALIAS ${TARGET_NAME}) endif() diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/backend/backend.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/backend/backend.hpp index f7b5cab22e7957..123ec2ba3310bc 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/backend/backend.hpp +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/backend/backend.hpp @@ -41,7 +41,7 @@ class BackEnd final : public std::enable_shared_from_this { void getMetaData( const Model::Ptr& model, const std::vector& allLayers, - std::vector& metaData); + GraphMetaInfo& graphMetaData); void extractDataInfo( const Model::Ptr& model, diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/custom_layer.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/custom_layer.hpp index 7d137519478582..28a784d38771b0 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/custom_layer.hpp +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/custom_layer.hpp @@ -23,10 +23,12 @@ namespace vpu { namespace ie = InferenceEngine; VPU_DECLARE_ENUM(CustomDataFormat, - BYXF = 0, // HWC used in most software layers - BFYX = 1, // CHW used if HW module is enabled - Any = 2, // doesn't really matter - None = 3 + BYXF = 0, // NHWC used in most software layers + BFYX = 1, // NCHW used if HW module is enabled + YXF = 2, // HWC used in most software layers + FYX = 3, // CHW used if HW module is enabled + Any = 4, // doesn't really matter + None = 5 ) VPU_DECLARE_ENUM(CustomParamType, diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/frontend/frontend.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/frontend/frontend.hpp index d3521e75203a6d..6cc4eb58e1cffa 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/frontend/frontend.hpp +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/frontend/frontend.hpp @@ -118,9 +118,13 @@ class FrontEnd final : public std::enable_shared_from_this { void parseRNN(const Model::Ptr& model, const ie::CNNLayerPtr& layer, const DataVector& inputs, const DataVector& outputs); void parseGEMM(const Model::Ptr& model, const ie::CNNLayerPtr& layer, const DataVector& inputs, const DataVector& outputs); void parseLog(const Model::Ptr& model, const ie::CNNLayerPtr& layer, const DataVector& inputs, const DataVector& outputs); + void parseExp(const Model::Ptr& model, const ie::CNNLayerPtr& layer, const DataVector& inputs, const DataVector& outputs); void parseReverseSequence(const Model::Ptr& model, const ie::CNNLayerPtr& layer, const DataVector& inputs, const DataVector& outputs); void parseGather(const Model::Ptr& model, const ie::CNNLayerPtr& layer, const DataVector& inputs, const DataVector& outputs); void parseReduce(const Model::Ptr& model, const ie::CNNLayerPtr& layer, const DataVector& inputs, const DataVector& outputs); + void parseFloor(const Model::Ptr& model, const ie::CNNLayerPtr& layer, const DataVector& inputs, const DataVector& outputs); + void parseTopK(const Model::Ptr& model, const ie::CNNLayerPtr& layer, const DataVector& inputs, const DataVector& outputs); + void parseSelect(const Model::Ptr& model, const ie::CNNLayerPtr& layer, const DataVector& inputs, const DataVector& outputs); // // Special layers @@ -131,6 +135,7 @@ class FrontEnd final : public std::enable_shared_from_this { void parseReshape(const Model::Ptr& model, const ie::CNNLayerPtr& layer, const DataVector& inputs, const DataVector& outputs); void parseConcat(const Model::Ptr& model, const ie::CNNLayerPtr& layer, const DataVector& inputs, const DataVector& outputs); void parseSplit(const Model::Ptr& model, const ie::CNNLayerPtr& layer, const DataVector& inputs, const DataVector& outputs); + void parseStridedSlice(const Model::Ptr& model, const ie::CNNLayerPtr& layer, const DataVector& inputs, const DataVector& outputs); // // Utility @@ -162,6 +167,8 @@ class FrontEnd final : public std::enable_shared_from_this { ie::details::caseless_map> _customLayers; ie::details::caseless_map kernelNodes; + std::unordered_map lstmWeights; + std::unordered_map lstmBiases; vpu::IeNetworkParser _ieNetworkParser; }; diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/frontend/stage_builder.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/frontend/stage_builder.hpp index bb0bcc8c6adf8f..bfba1cc0a4ebfd 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/frontend/stage_builder.hpp +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/frontend/stage_builder.hpp @@ -150,7 +150,7 @@ class StageBuilder final : public std::enable_shared_from_this { const Data& biases, Data output); - Stage addBroadcastStage( + Stage addExpandStage( const Model::Ptr& model, const std::string& name, const ie::CNNLayerPtr& layer, @@ -191,9 +191,7 @@ class StageBuilder final : public std::enable_shared_from_this { float beta, bool transposeA, bool transposeB, - const Data& inputA, - const Data& inputB, - const Data& inputC, + const DataVector& inputs, const Data& output); @@ -210,9 +208,9 @@ class StageBuilder final : public std::enable_shared_from_this { const Model::Ptr& model, const std::string& name, const ie::CNNLayerPtr& layer, - const DataVector& input, - const DataVector& output, - const SmallVector& ieOrder); + const Data& input, + const Data& output, + const DimValues_& permutation); }; } // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/graph_transformer.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/graph_transformer.hpp index e34511875bce21..ad71f09955184e 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/graph_transformer.hpp +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/graph_transformer.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -44,7 +45,9 @@ VPU_DECLARE_ENUM(ExecutionMode, VPU_DECLARE_ENUM(ComputeLayout, AUTO, NCHW, - NHWC + NHWC, + NCDHW, + NDHWC ) struct CompilationConfig final { @@ -73,8 +76,6 @@ struct CompilationConfig final { bool detectBatch = true; - bool allowFP32Models = false; - std::string hwWhiteList; std::string hwBlackList; @@ -96,6 +97,7 @@ struct CompilationConfig final { float inputBias = 0.0f; bool hwDilation = false; + std::map> ioStrides; }; @@ -122,7 +124,7 @@ struct CompiledGraph final { int networkBatch = 0; - std::vector stagesMeta; + GraphMetaInfo graphMeta; int numActiveStages = 0; DataInfo inputInfo; diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/hw/mx_stage.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/hw/mx_stage.hpp index f090e33ae049bc..a2f292fe88b1ad 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/hw/mx_stage.hpp +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/hw/mx_stage.hpp @@ -16,15 +16,16 @@ class MyriadXHwStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override; + ScalePropagationStep step, + StageDataInfo& scaleInfo) override; - void propagateDataOrderImpl() const override; + void propagateDataOrderImpl(StageDataInfo& orderInfo) override; - void getDataStridesRequirementsImpl() const override; + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override; void finalizeDataLayoutImpl() override; - void getBatchSupportInfoImpl() const override; + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override; void finalCheckImpl() const override; diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/hw/tiling.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/hw/tiling.hpp index c12613cebd603d..42ee16fbedfa3e 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/hw/tiling.hpp +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/hw/tiling.hpp @@ -38,9 +38,10 @@ const int CNN_MAX_INPUT_CHANNELS = 2048; const int CNN_MAX_OUTPUT_CHANNELS = 2048; const int CNN_MAX_BYTES = 128 * 1024; -const int CNN_MAX_CHANNELS_PER_BLOCK = 2048; const int CNN_MAX_COEFF_PER_BLOCK = 256; +const int CMX_DATA_BYTE_WIDTH = 16; + // // Tiling scheme // @@ -222,19 +223,38 @@ SmallVector splitIntoPlaneTilesWithPool( int pad, int maxOutputSize); +// Due to possible junk may return more tiles than requested (1) (O -> I) SmallVector splitIntoPlaneTiles( int inputSize, int outputSize, int kernelSize, int kernelStride, int padBefore, int padAfter, + // max size of output tile with junk included int maxOutputSize, - bool alignInputTile, bool useCeil); +// +// Check HW-unit memory restrictions for tile. +// + +bool checkPoolingHWRestrictions( + int inTileWidth, int inTileHeight, + int inTileChannels, int outTileChannels, + int kernelSizeX, int kernelSizeY, + int kernelStride); + +bool checkConvHWRestrictions( + int inTileWidth, int inTileHeight, + int inTileChannels, int outTileChannels, + int kernelSizeX, int kernelSizeY, + int kernelStride, + HwOpMode mode); + // // HW Convolution tiling over output channels. // // This function tries to split the output over channels. +// split OC is invoked at the very end (3) HwConvTileInfo splitHwConvIntoOutChannelsTiles( int inTileWidth, int inTileHeight, int inTileChannels, int outTileChannels, diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/model/data.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/model/data.hpp index 7253a8037f0f0b..0c26454b4e7336 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/model/data.hpp +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/model/data.hpp @@ -263,7 +263,10 @@ class DataNode final : bool checkStrides(const StridesRequirement& reqs) const; - inline void resetRequiredStrides() { _requiredStrides = StridesRequirement::empty(); } + inline void resetRequiredStrides() { + _requiredStrides = StridesRequirement::empty(); + } + void updateRequiredStrides(const StridesRequirement& newReqs); // @@ -306,7 +309,7 @@ class DataNode final : const Stage& stage, BlobSerializer& serializer, DimsOrder newOrder = DimsOrder(), - const EnumMap>& dimsReloc = EnumMap>()); + const EnumMap& dimsReloc = EnumMap()); void serializeOldBufferNC( const Stage& stage, diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/model/data_desc.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/model/data_desc.hpp index 19b6175f1e49c4..86b71024c5c32f 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/model/data_desc.hpp +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/model/data_desc.hpp @@ -15,7 +15,6 @@ #include -#include #include #include #include @@ -44,11 +43,29 @@ namespace ie = InferenceEngine; VPU_DECLARE_ENUM(DataType, FP16 = 0, U8 = 1, -// S32 = 2, // TODO: remove from MvTensor + S32 = 2, FP32 = 3, I8 = 4 ) +DataType fromIEPrecision(const InferenceEngine::Precision& precision); + +// +// StorageOrder +// + +// +// Types that are used to store order permutation in packed format. +// + +using StorageOrder64 = uint64_t; +using StorageOrder32 = uint32_t; + +// High-order digit excluded. +const int MAX_DIMS_64 = std::numeric_limits::digits / 4 - 1; + +const int MAX_DIMS_32 = std::numeric_limits::digits / 4; + // // Dim // @@ -63,27 +80,15 @@ VPU_DECLARE_ENUM(Dim, H = 1, C = 2, N = 3, - _5 = 4, - _6 = 5, - _7 = 6, - _8 = 7 + D = 4 ) -// -// StorageOrder -// +// TODO: identify casts like static_cast(Dim), +// and replace all with calling this function +// JIRA: #21163 +int dimToIeInd(vpu::Dim const& dim, int numDims); -// -// Types that are used to store order permutation in packed format. -// - -using StorageOrder64 = uint64_t; -using StorageOrder32 = uint32_t; - -// High-order digit excluded. -const int MAX_DIMS_64 = std::numeric_limits::digits / 4 - 1; - -const int MAX_DIMS_32 = std::numeric_limits::digits / 4; +using DimVector = SmallVector; // // DimValues @@ -252,29 +257,29 @@ class DimValues_ final { auto ind = static_cast(d); IE_ASSERT(ind >= 0 && ind < MAX_DIMS_64); - return _flags[ind]; + return _flags[static_cast(ind)]; } const T& operator[](Dim d) const { auto ind = static_cast(d); IE_ASSERT(ind >= 0 && ind < MAX_DIMS_64); - IE_ASSERT(_flags[ind]); + IE_ASSERT(_flags[static_cast(ind)]); - return _values[ind].second; + return _values[static_cast(ind)].second; } const T& get(Dim d, const T& def) const { auto ind = static_cast(d); IE_ASSERT(ind >= 0 && ind < MAX_DIMS_64); - return _flags[ind] ? _values[ind].second : def; + return _flags[static_cast(ind)] ? _values[static_cast(ind)].second : def; } void set(Dim d, const T& val) { auto ind = static_cast(d); IE_ASSERT(ind >= 0 && ind < MAX_DIMS_64); - if (!_flags[ind]) { - _flags[ind] = true; + if (!_flags[static_cast(ind)]) { + _flags[static_cast(ind)] = true; ++_size; } @@ -351,6 +356,12 @@ class DimValues_ final { size_t _size = 0; }; +template +std::ostream& operator<<(std::ostream& o, const DimValues_& dimValues) { + dimValues.printTo(o); + return o; +} + template void printTo(std::ostream& os, const DimValues_& dims) { dims.printTo(os); @@ -378,6 +389,8 @@ class DimsOrder final { static DimsOrder NCHW; static DimsOrder NHWC; static DimsOrder NHCW; + static DimsOrder NCDHW; + static DimsOrder NDHWC; // // Constructor @@ -386,7 +399,8 @@ class DimsOrder final { DimsOrder() = default; static DimsOrder fromCode(StorageOrder64 code); static DimsOrder fromNumDims(int numDims); - static DimsOrder fromPermutation(const SmallVector& perm); + static DimsOrder fromPermutation(const DimVector& perm); + static DimsOrder fromLayout(ie::Layout const& layout); // // Accessors @@ -518,6 +532,12 @@ class DataDesc final { void reorder(DimsOrder dimsOrder); + // + // Export + // + + ie::TensorDesc toTensorDesc() const; + private: DataType _type = DataType::FP16; DimsOrder _dimsOrder; @@ -534,7 +554,8 @@ void printTo(DotLabel& lbl, const DataDesc& desc); VPU_DECLARE_ENUM(DimStride, Any, Compact, - Aligned + Aligned, + Fixed ) const int STRIDE_ALIGNMENT = 16; @@ -553,22 +574,23 @@ class StridesRequirement final { static StridesRequirement empty() { return StridesRequirement().add(0, DimStride::Any); } static StridesRequirement compact(); + static StridesRequirement fixed(const std::vector& strides, const DataDesc& desc); StridesRequirement& add(int index, DimStride stride) { IE_ASSERT(index >= 0 && index < MAX_DIMS_64); - _map[index] = stride; + _map[static_cast(index)] = stride; return *this; } StridesRequirement& remove(int index) { IE_ASSERT(index >= 0 && index < MAX_DIMS_64); - _map[index] = DimStride::Any; + _map[static_cast(index)] = DimStride::Any; return *this; } DimStride get(int index) const { IE_ASSERT(index >= 0 && index < MAX_DIMS_64); - return _map[index]; + return _map[static_cast(index)]; } bool operator==(const StridesRequirement& other) const { @@ -578,8 +600,13 @@ class StridesRequirement final { return (_map != other._map); } + const DimValues& fixedStrides() const { return _fixedStrides; } + + int getFixedStride(Dim d) const { return _fixedStrides[d]; } + private: std::array _map{{DimStride::Any}}; + DimValues _fixedStrides; }; void printTo(std::ostream& os, const StridesRequirement& reqs); @@ -591,7 +618,7 @@ bool checkStride( const DimValues& strides, const DataDesc& desc, int ind, - DimStride req); + const StridesRequirement& req); bool checkStrides( const DataDesc& desc, const DimValues& strides, diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/model/model.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/model/model.hpp index 9ce39fb79aa5d9..3e2d03bcedb465 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/model/model.hpp +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/model/model.hpp @@ -123,8 +123,8 @@ class Model final : const DataVector& outputs); Stage duplicateStage( - const std::string& name, const Stage& origStage, + const std::string& postfix, const DataVector& inputs, const DataVector& outputs); @@ -238,13 +238,13 @@ class Model final : // Nodes removal // - void disconnectStageDatas(const Stage& stage); + void disconnectStage(const Stage& stage); void removeStage(const Stage& stage); void removeUnusedData(const Data& data); - void cleanUpDatas(); + void cleanUp(); // // Stage order diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/model/stage.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/model/stage.hpp index 44b48b7a9550b1..683ec4dfedb665 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/model/stage.hpp +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/model/stage.hpp @@ -48,8 +48,9 @@ VPU_DECLARE_ENUM(StageType, Concat, Split, Reshape, - Broadcast, + Expand, Shrink, + StridedSlice, Empty = -1, @@ -136,6 +137,10 @@ VPU_DECLARE_ENUM(StageType, ReduceAnd = 93, ReverseSequence = 94, Gather = 100, + Exp = 101, + Floor = 102, + TopK = 104, + ReduceMin = 105, ) // @@ -172,7 +177,7 @@ VPU_DECLARE_ENUM(StageSHAVEsRequirements, ); // -// StageNode +// ScalePropagationStep // VPU_DECLARE_ENUM(ScalePropagationStep, @@ -181,6 +186,29 @@ VPU_DECLARE_ENUM(ScalePropagationStep, Propagate ); +// +// TopKMode +// + +// Firmware implementations must be aligned with these values +VPU_DECLARE_ENUM(TopKMode, + Max = 0, + Min = 1) + +// +// TopKSort +// + +// Firmware implementations must be aligned with these values +VPU_DECLARE_ENUM(TopKSort, + None = 0, + Value = 1, + Index = 2) + +// +// StageDataInfo +// + template class StageDataInfo final { public: @@ -258,6 +286,10 @@ class StageDataInfo final { SmallVector> _outputVals; }; +// +// StageNode +// + class StageNode : public EnableHandleFromThis, public EnableCustomAttributes { @@ -297,6 +329,8 @@ class StageNode : // Edges wrappers // + VPU_MODEL_ATTRIBUTE(Handle, model, nullptr) + public: struct StageNameCmp final { inline bool operator()(const Stage& left, const Stage& right) const { @@ -445,7 +479,9 @@ class StageNode : // Bindings with IE // - inline std::string origLayerName() const { return _origLayer != nullptr ? _origLayer->name : std::string(); } + inline std::string origLayerName() const { + return _origLayer != nullptr ? _origLayer->name : std::string(); + } // // SHAVEs allocation @@ -463,25 +499,27 @@ class StageNode : ScalePropagationStep step); // Data order propagation from inputs to outputs. - const StageDataInfo& propagateDataOrder() const; + const StageDataInfo& propagateDataOrder(); // Get Data strides requirements - const StageDataInfo& getDataStridesRequirements() const; + const StageDataInfo& getDataStridesRequirements(); // Finalize internal parameter to final Data layout. void finalizeDataLayout(); // Information about batch support. - const StageDataInfo& getBatchSupportInfo() const; + const StageDataInfo& getBatchSupportInfo(); // Resources requirements. StageSHAVEsRequirements getSHAVEsRequirements() const; - // Final check. + void initialCheck() const; void finalCheck() const; // Name postfix for modified stage - inline void appendNamePostfix(const std::string& postfix) { _name = _name + postfix; } + inline void appendNamePostfix(const std::string& postfix) { + _name = _name + postfix; + } // // Backend utilities @@ -498,19 +536,21 @@ class StageNode : virtual void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step); + ScalePropagationStep step, + StageDataInfo& scaleInfo); - virtual void propagateDataOrderImpl() const = 0; + virtual void propagateDataOrderImpl(StageDataInfo& orderInfo) = 0; - virtual void getDataStridesRequirementsImpl() const = 0; + virtual void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) = 0; virtual void finalizeDataLayoutImpl() = 0; - virtual void getBatchSupportInfoImpl() const = 0; + virtual void getBatchSupportInfoImpl(StageDataInfo& batchInfo) = 0; virtual StageSHAVEsRequirements getSHAVEsRequirementsImpl() const; - virtual void finalCheckImpl() const = 0; + virtual void initialCheckImpl() const {} + virtual void finalCheckImpl() const {} virtual void serializeParamsImpl(BlobSerializer& serializer) const = 0; @@ -535,15 +575,12 @@ class StageNode : _posInModel(this) { } -protected: - Handle _model; - - mutable StageDataInfo _scaleInfo; - mutable StageDataInfo _orderInfo; - mutable StageDataInfo _stridesInfo; - mutable StageDataInfo _batchInfo; - private: + StageDataInfo _scaleInfo; + StageDataInfo _orderInfo; + StageDataInfo _stridesInfo; + StageDataInfo _batchInfo; + StagePtrList::iterator _ptrPosInModel; IntrusivePtrListNode _posInModel; @@ -552,4 +589,12 @@ class StageNode : void printTo(std::ostream& os, const Stage& stage); +void assertAllInputsOutputsTypes(const StageNode* stage, + const DataType& expectedInputsType, + const DataType& expectedOutputsType); + +void assertInputsOutputsTypes(const StageNode* stage, + const std::vector>& expectedInputsTypes, + const std::vector>& expectedOutputsTypes); + } // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/parsed_config.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/parsed_config.hpp index 97e6338db9706a..e81fa48589a815 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/parsed_config.hpp +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/parsed_config.hpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include #include @@ -19,82 +21,30 @@ namespace vpu { -VPU_DECLARE_ENUM(ConfigMode, - DEFAULT_MODE = 0, - RUNTIME_MODE = 1, - COMPILE_MODE = 2, -) - -struct ParsedConfig { +struct ParsedConfig : public ParsedConfigBase{ CompilationConfig compileConfig; bool printReceiveTensorTime = false; - bool exclusiveAsyncRequests = false; bool perfCount = false; - LogLevel deviceLogLevel = LogLevel::None; - LogLevel hostLogLevel = LogLevel::None; - PerfReport perfReport = PerfReport::PerLayer; - virtual std::map getDefaultConfig() const; + std::map getDefaultConfig() const override; - virtual ~ParsedConfig() = default; + ~ParsedConfig() = default; protected: - explicit ParsedConfig(ConfigMode configMode = ConfigMode::DEFAULT_MODE); - - void checkUnknownOptions(const std::map &config) const; - virtual void checkInvalidValues(const std::map &config) const; - std::unordered_set getKnownOptions() const; + explicit ParsedConfig(ConfigMode configMode); - std::map parse(const std::map &config) { - checkInvalidValues(config); - checkUnknownOptions(config); - checkOptionsAccordingToMode(config); + void checkInvalidValues(const std::map &config) const override; - auto defaultConfig = getDefaultConfig(); - for (auto &&entry : config) { - defaultConfig[entry.first] = entry.second; - } + void configure(const std::map &config) override; - return defaultConfig; - } - - void configure(const std::map &config); - void checkSupportedValues(const std::unordered_map> &supported, - const std::map &config) const; - - virtual void checkOptionsAccordingToMode(const std::map &config) const; - virtual std::unordered_set getCompileOptions() const; - virtual std::unordered_set getRuntimeOptions() const; + std::unordered_set getKnownOptions() const override; + std::unordered_set getCompileOptions() const override; + std::unordered_set getRuntimeOptions() const override; private: ConfigMode _mode = ConfigMode::DEFAULT_MODE; - Logger::Ptr _log; }; - -template -inline void setOption(T &dst, const V &supported, const std::map &config, const std::string &key) { - auto value = config.find(key); - if (value != config.end()) { - dst = supported.at(value->second); - } -} - -inline void setOption(std::string &dst, const std::map &config, const std::string &key) { - auto value = config.find(key); - if (value != config.end()) { - dst = value->second; - } -} - -template -inline void setOption(T &dst, const std::map &config, const std::string &key, const C &preprocess) { - auto value = config.find(key); - if (value != config.end()) { - dst = preprocess(value->second); - } -} - } // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/pass_manager.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/pass_manager.hpp index 63845762916158..9644e5a4339a01 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/pass_manager.hpp +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/pass_manager.hpp @@ -139,6 +139,12 @@ class PassManager final : public std::enable_shared_from_this { Pass::Ptr mergeReLUAndBias(); Pass::Ptr mergeEltwiseAndReLU(); + // + // StridedSlice processing + // + + Pass::Ptr stridedSlice(); + // // Data layout adjustment // @@ -162,6 +168,7 @@ class PassManager final : public std::enable_shared_from_this { // Pass::Ptr eliminateCopyStages(); + Pass::Ptr removeUnusedStagesOutputs(); // // HW/SW injection @@ -199,6 +206,9 @@ class PassManager final : public std::enable_shared_from_this { Pass::Ptr reshapeDilationConv(); + Pass::Ptr addCopyForOutputsInsideNetwork(); + + Pass::Ptr initialCheck(); protected: StageBuilder::Ptr _stageBuilder; diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/passes/hw_conv_tiling/hw_convolution_tiler.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/passes/hw_conv_tiling/hw_convolution_tiler.hpp new file mode 100644 index 00000000000000..a379ca48062003 --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/passes/hw_conv_tiling/hw_convolution_tiler.hpp @@ -0,0 +1,319 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vpu { + +namespace HWTilingNS { + +struct ConvolutionOptions final { + const std::string _stageName; + + const DimValues _inputDims; + const DimValues _outputDims; + const DimValues _origOutputDims; + + const int _kernelSizeX; + const int _kernelSizeY; + const int _kernelStride; + const int _paddingLeft; + const int _paddingRight; + const int _paddingTop; + const int _paddingBottom; + + const bool _withPool; + +public: + ConvolutionOptions(const std::string &stageName, const DimValues &inputDims, const DimValues &outputDims, + const DimValues &origOutputDims, const int kernelSizeX, const int kernelSizeY, + const int kernelStride, const int paddingLeft, const int paddingRight, + const int paddingTop, const int paddingBottom, const bool withPool) + : _stageName(stageName), _inputDims(inputDims), _outputDims(outputDims), + _origOutputDims(origOutputDims), _kernelSizeX(kernelSizeX), _kernelSizeY(kernelSizeY), + _kernelStride(kernelStride), _paddingLeft(paddingLeft), _paddingRight(paddingRight), + _paddingTop(paddingTop), _paddingBottom(paddingBottom), _withPool(withPool) {} +}; + +struct TilingOption final { + int numWidthTiles; + int numHeightTiles; + int numChannelTiles; + int totalNumTiles; + double cost; +}; + +bool operator<(const TilingOption &a, const TilingOption &b); + +std::ostream &operator<<(std::ostream &o, const TilingOption &to); + +enum class Direction { + INPUT_TO_OUTPUT = 0, OUTPUT_TO_INPUT = 1 +}; + +// Tensors can be split going either from input to output or vice versa +class GraphDataTiling { +protected: + const ConvolutionOptions &_co; + // size of every tile for input tensor in each dimension + DimValues _inputTileDims; + // size of every tile for output tensor in each dimension + DimValues _outputTileDims; + bool _useCeil = false; + const enum Direction _direction; + +public: + GraphDataTiling() = delete; + virtual ~GraphDataTiling() = default; + GraphDataTiling(const GraphDataTiling &other): _co(other._co), _inputTileDims(other._inputTileDims), + _outputTileDims(other._outputTileDims), _useCeil(other._useCeil), _direction(other._direction) { + } + + explicit GraphDataTiling(const ConvolutionOptions &__co, Direction direction) : + _co(__co), _direction(direction) {} + + const DimValues &getInputTileDims() const { return _inputTileDims; } + + const DimValues &getOutputTileDims() const { return _outputTileDims; } + + DimValues &getInputTileDims() { return _inputTileDims; } + + DimValues &getOutputTileDims() { return _outputTileDims; } + + void resetInputTileDims(const DimValues &dimVals) { _inputTileDims = dimVals; } + + void resetOutputTileDims(const DimValues &dimVals) { _outputTileDims = dimVals; } + + virtual void initTileSizes() = 0; + + virtual void applyTilingOption(const TilingOption &tilingOption) = 0; + + virtual void setInputNOutputTileDimensions(const int tileDimW, const int tileDimH, const int tileDimC) = 0; + + virtual void correctPlaneSize() = 0; + + virtual const DimValues &splitOverTensorDims() = 0; + + virtual void patternMatching() = 0; + + bool useCeil() const { + return _useCeil; + } + + Direction getDirection() const { + return _direction; + } + + const ConvolutionOptions& co() const { return _co; } +}; + +class ConvGraphDataTilingFactory final { +public: + static std::unique_ptr makeDirTiling(const ConvolutionOptions &co, Direction direction); + static std::unique_ptr makeDirTiling(const GraphDataTiling &o); +}; + +class HWConvolutionTileLayoutCut; + +// iterates over all the tiling options and chooses few with minimal cost +class HWConvolutionTilingSearcher { + const ConvolutionOptions _co; + const size_t _maxTilingOptions; + const std::unique_ptr _dirTiling; + std::vector _tilingOptions; + +public: + HWConvolutionTilingSearcher() = delete; + HWConvolutionTilingSearcher(const HWConvolutionTilingSearcher &other): _co(other._co), + _maxTilingOptions(other._maxTilingOptions), + _dirTiling(ConvGraphDataTilingFactory::makeDirTiling(*other._dirTiling)), + _tilingOptions(other._tilingOptions) { + } + + HWConvolutionTilingSearcher(const ConvolutionOptions &co, + Direction direction, + size_t maxTilingOptions) : _co(co), + _dirTiling(ConvGraphDataTilingFactory::makeDirTiling(_co, direction)), + _maxTilingOptions(maxTilingOptions) { + IE_ASSERT(maxTilingOptions > 0); + _dirTiling->initTileSizes(); + _tilingOptions = selectBetterTiling(); + } + + const std::vector &tilingOptions() const { + return _tilingOptions; + } + + size_t tilingOptionsCount() const { + return _tilingOptions.size(); + } + + const ConvolutionOptions& co() const { return _co; } + + HWConvolutionTileLayoutCut tileLayoutCut(const TilingOption &option) const; + +private: + std::vector selectBetterTiling() const; +}; + +// Search for tiling options and applies them to prepare hw tilings +class HWConvolutionTiler final { +private: + const ConvolutionOptions _co; + std::vector _hwTilings; + bool _tilingPossible; + const HWConvolutionTilingSearcher _searcher; + +public: + HWConvolutionTiler() = delete; + + HWConvolutionTiler(const HWConvolutionTiler &other): _co(other._co), _hwTilings(other._hwTilings), + _searcher(other._searcher), _tilingPossible(other._tilingPossible) { + } + + explicit HWConvolutionTiler(const ConvolutionOptions &co, + Direction direction, + size_t maxTilingOptions); + + + bool isTilingPossible() const { + return _tilingPossible; + } + + bool withPool() const { + return _co._withPool; + } + + const std::vector &getHwTilings() const { + return _hwTilings; + } + +private: + bool tileForHW(); +}; + +SmallVector calcHeightTiles(const ConvolutionOptions &_co, + const DimValues &outputTileDims, bool useCeil); +SmallVector calcWidthTiles(const ConvolutionOptions &_co, + const DimValues &outputTileDims, bool useCeil); + +// Based on chosen { inputTileDims, outputTileDims } constructs plane's tiling structure; +// (same for both input and output, contains only number of tiles in each dimension) +class HWConvolutionTileLayoutCut { +private: + const ConvolutionOptions &_co; + GraphDataTiling &_dirTiling; + HwConvTilingPtr _hwTiling; + bool _tileCutPossible; + +public: + HWConvolutionTileLayoutCut() = delete; + HWConvolutionTileLayoutCut(const HWConvolutionTileLayoutCut &other): _co(other._co), _dirTiling(other._dirTiling), + _hwTiling(other._hwTiling), _tileCutPossible(other._tileCutPossible) { + } + + HWConvolutionTileLayoutCut(HWConvolutionTileLayoutCut &&other): _co(other._co), _dirTiling(other._dirTiling) { + _hwTiling = std::move(other._hwTiling); + _tileCutPossible = other.tileCutPossible(); + } + HWConvolutionTileLayoutCut(GraphDataTiling &dirTiling, const TilingOption &tilingOption) : + _dirTiling(dirTiling), + _co(dirTiling.co()), _hwTiling(std::make_shared()) { + dirTiling.applyTilingOption(tilingOption); + + dirTiling.patternMatching(); + + // Merged Pooling and SoC can't be used together. + if (_co._withPool) { + IE_ASSERT(!hasSoC(dirTiling)); + } + + _tileCutPossible = createTiles(calcHeightTiles(_co, dirTiling.getOutputTileDims(), dirTiling.useCeil()), + calcWidthTiles(_co, dirTiling.getOutputTileDims(), dirTiling.useCeil()), + dirTiling.getInputTileDims(), dirTiling.getOutputTileDims()); + } + + bool tileCutPossible() const { return _tileCutPossible; } + + HwConvTilingPtr hwTiling() const { + IE_ASSERT(_tileCutPossible); + return _hwTiling; + } + +private: + bool createTiles(const SmallVector &heightTiles, + const SmallVector &widthTiles, + const DimValues &inputTileDims, const DimValues &outputTileDims) const { + IE_ASSERT(!heightTiles.empty()); + IE_ASSERT(!widthTiles.empty()); + + _hwTiling->sohTiles = heightTiles.size(); + _hwTiling->sowTiles = widthTiles.size(); + _hwTiling->socTiles = divUp(_co._inputDims[Dim::C], inputTileDims[Dim::C]); + + for (int sohInd = 0; sohInd < _hwTiling->sohTiles; ++sohInd) { + const auto &heightTileInfo = heightTiles[sohInd]; + + for (int sowInd = 0; sowInd < _hwTiling->sowTiles; ++sowInd) { + const auto &widthTileInfo = widthTiles[sowInd]; + + auto planeTile = std::make_shared(); + planeTile->parent = _hwTiling; + + planeTile->sohInd = sohInd; + planeTile->sowInd = sowInd; + + planeTile->heightInfo = heightTileInfo; + planeTile->widthInfo = widthTileInfo; + + for (int socInd = 0; socInd < _hwTiling->socTiles; ++socInd) { + auto channelTile = std::make_shared(); + channelTile->parent = planeTile; + + channelTile->socInd = socInd; + + channelTile->finalTiles = splitHwConvIntoOutChannelsTiles( + widthTileInfo.inputWithJunk, heightTileInfo.inputWithJunk, inputTileDims[Dim::C], + outputTileDims[Dim::C], + _co._kernelSizeX, _co._kernelSizeY, _co._kernelStride); + + if (channelTile->finalTiles.numDescr == 0) { + return false; + } + + channelTile->extendedInputDimC = channelTile->finalTiles.extendedInputDimC; + channelTile->extendedOutputDimC = channelTile->finalTiles.extendedOutputDimC; + + channelTile->channelStartIndex = socInd * inputTileDims[Dim::C]; + channelTile->numInputChannels = inputTileDims[Dim::C]; + + planeTile->channelTiles.emplace_back(channelTile); + } + + _hwTiling->planeTiles.emplace_back(planeTile); + } + } + return true; + } + + bool hasSoC(const GraphDataTiling &dirTile) const { + return dirTile.getInputTileDims()[Dim::C] != _co._inputDims[Dim::C]; + } +}; +} // namespace HWTilingNS + +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/passes/hw_conv_tiling/hw_stage_tiler.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/passes/hw_conv_tiling/hw_stage_tiler.hpp new file mode 100644 index 00000000000000..3804e8a1da3aff --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/passes/hw_conv_tiling/hw_stage_tiler.hpp @@ -0,0 +1,126 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include +#include + +namespace vpu { + +struct HWConvStageOptions; +struct HWConvStageIO; + +// Builds graph which composes tiled analogue of the single stage 'origStage' +class HWConvStageTiler { +private: + HWConvStageTiler() = delete; + HWConvStageTiler(const HWConvStageTiler&) = delete; + +public: + DataVector hwInputTiles; + std::vector hwInputTilesOffsets; + + DataVector hwOutputTiles; + std::vector hwOutputTilesOffsets; + + Data hwInput; + Data hwOutput; + + HWConvStageTiler(const HWConvStageOptions &so, const HWConvStageIO &sio, + const Model::Ptr &model, const Handle &origStage, + const StageBuilder::Ptr &stageBuilder, const HwConvTilingPtr &tiling, + const bool makeExplicitPoolStage); +}; + +struct HWConvStageIO { +private: + HWConvStageIO() = delete; + HWConvStageIO(const HWConvStageIO&) = delete; + +public: + Data origInput; + Data origWeights; + Data origBiases; + Data origOutput; + DataDesc origOutputDesc; + + explicit HWConvStageIO(const Handle &origStage, const Data &originOutput) { + origInput = origStage->input(0); + origWeights = origStage->input(1); + origBiases = origStage->input(2); + origOutput = originOutput; + origOutputDesc = origStage->attrs().getOrDefault("origConvOutput", origOutput->desc()); + } +}; + +// Attributes of the stage collected into the structure +struct HWConvStageOptions { +private: + HWConvStageOptions() = delete; + HWConvStageOptions(const HWConvStageOptions&) = delete; + +public: + int kernelSizeX; + int kernelSizeY; + int kernelStride; + int padLeft; + int padRight; + int padTop; + int padBottom; + + bool withReLU; + float negativeSlope; + uint32_t a0; + uint32_t a1; + float reluScale; + + bool withClamp; + float clampMax; + + bool withPool; + int poolKernelSizeX; + int poolKernelSizeY; + int poolKernelStride; + int poolPadLeft; + int poolPadRight; + int poolPadTop; + int poolPadBottom; + + float scaleFactor; + + explicit HWConvStageOptions(const Handle &origStage) { + kernelSizeX = origStage->attrs().get("kernelSizeX"); + kernelSizeY = origStage->attrs().get("kernelSizeY"); + kernelStride = origStage->attrs().get("kernelStrideX"); + padLeft = origStage->attrs().get("padLeft"); + padRight = origStage->attrs().get("padRight"); + padTop = origStage->attrs().get("padTop"); + padBottom = origStage->attrs().get("padBottom"); + + withReLU = origStage->attrs().getOrDefault("withReLU", false); + negativeSlope = origStage->attrs().getOrDefault("negativeSlope", 0.0f); + a0 = origStage->attrs().getOrDefault("a0", 0); + a1 = origStage->attrs().getOrDefault("a1", 0); + reluScale = origStage->attrs().getOrDefault("reluScale", 1.0f); + + withClamp = origStage->attrs().getOrDefault("withClamp", false); + clampMax = origStage->attrs().getOrDefault("clampMax", 6.0); + + withPool = origStage->attrs().getOrDefault("withPool", false); + poolKernelSizeX = origStage->attrs().getOrDefault("poolKernelSizeX", 0); + poolKernelSizeY = origStage->attrs().getOrDefault("poolKernelSizeY", 0); + poolKernelStride = origStage->attrs().getOrDefault("poolKernelStride", 0); + poolPadLeft = origStage->attrs().getOrDefault("poolPadLeft", 0); + poolPadRight = origStage->attrs().getOrDefault("poolPadRight", 0); + poolPadTop = origStage->attrs().getOrDefault("poolPadTop", 0); + poolPadBottom = origStage->attrs().getOrDefault("poolPadBottom", 0); + + scaleFactor = origStage->attrs().getOrDefault("scaleFactor", 1.0f); + } +}; + +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/passes/hw_pooling_tiling/hw_pooling_tiler.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/passes/hw_pooling_tiling/hw_pooling_tiler.hpp new file mode 100644 index 00000000000000..b1266ce8c34f30 --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/passes/hw_pooling_tiling/hw_pooling_tiler.hpp @@ -0,0 +1,209 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vpu { + +namespace HWTilingNS { + +using HWTilingNS::GraphDataTiling; +using HWTilingNS::ConvolutionOptions; +using HWTilingNS::Direction; +using HWTilingNS::TilingOption; + +const int CHANS_PER_DESCR = 16; + +HwPoolTileInfo splitPooling(int outZ); + +class PoolGraphDataTilingFactory final { +public: + static std::unique_ptr makeDirTiling(const ConvolutionOptions &co, Direction direction); + static std::unique_ptr makeDirTiling(const GraphDataTiling &o); +}; + +class HWPoolingTileLayoutCut; + +// iterates over all the tiling options and chooses few with minimal cost +class HWPoolingTilingSearcher { + const ConvolutionOptions _co; + const size_t _maxTilingOptions; + const std::unique_ptr _dirTiling; + std::vector _tilingOptions; + +public: + HWPoolingTilingSearcher() = delete; + HWPoolingTilingSearcher(const HWPoolingTilingSearcher &other): _co(other._co), + _maxTilingOptions(other._maxTilingOptions), + _dirTiling(PoolGraphDataTilingFactory::makeDirTiling(*other._dirTiling)), + _tilingOptions(other._tilingOptions) { + } + + HWPoolingTilingSearcher(const ConvolutionOptions &co, + Direction direction, + size_t maxTilingOptions) : _co(co), + _dirTiling(PoolGraphDataTilingFactory::makeDirTiling(_co, direction)), + _maxTilingOptions(maxTilingOptions) { + IE_ASSERT(maxTilingOptions > 0); + _dirTiling->initTileSizes(); + _tilingOptions = selectBetterTiling(); + } + + const std::vector &tilingOptions() const { + return _tilingOptions; + } + + size_t tilingOptionsCount() const { + return _tilingOptions.size(); + } + + const ConvolutionOptions& co() const { return _co; } + + const HWPoolingTileLayoutCut tileLayoutCut(const TilingOption &option) const; + +private: + std::vector selectBetterTiling() const; +}; + +// Search for tiling options and applies them to prepare hw tilings +class HWPoolingTiler final { +private: + const ConvolutionOptions _co; + std::vector _hwTilings; + bool _tilingPossible; + const HWPoolingTilingSearcher _searcher; + +public: + HWPoolingTiler() = delete; + + HWPoolingTiler(const HWPoolingTiler &other): _co(other._co), _hwTilings(other._hwTilings), + _searcher(other._searcher), _tilingPossible(other._tilingPossible) { + } + + explicit HWPoolingTiler(const ConvolutionOptions &co, + Direction direction, + size_t maxTilingOptions); + + bool isTilingPossible() const { + return _tilingPossible; + } + + const std::vector &getHwTilings() const { + return _hwTilings; + } + +private: + bool tileForHW(); +}; + +SmallVector calcHeightTilesP(const ConvolutionOptions &_co, + const DimValues &outputTileDims, bool useCeil); +SmallVector calcWidthTilesP(const ConvolutionOptions &_co, + const DimValues &outputTileDims, bool useCeil); + +// Based on chosen { inputTileDims, outputTileDims } constructs plane's tiling structure; +// (same for both input and output, contains only number of tiles in each dimension) +class HWPoolingTileLayoutCut { +private: + const ConvolutionOptions &_co; + GraphDataTiling &_dirTiling; + HwPoolTilingPtr _hwTiling; + bool _tileCutPossible; + +public: + HWPoolingTileLayoutCut() = delete; + HWPoolingTileLayoutCut(const HWPoolingTileLayoutCut &other): _co(other._co), _dirTiling(other._dirTiling), + _hwTiling(other._hwTiling), _tileCutPossible(other._tileCutPossible) { + } + + HWPoolingTileLayoutCut(HWPoolingTileLayoutCut &&other): _co(other._co), _dirTiling(other._dirTiling) { + _hwTiling = std::move(other._hwTiling); + _tileCutPossible = other.tileCutPossible(); + } + HWPoolingTileLayoutCut(GraphDataTiling &dirTiling, const TilingOption &tilingOption) : + _dirTiling(dirTiling), + _co(dirTiling.co()), _hwTiling(std::make_shared()) { + dirTiling.applyTilingOption(tilingOption); + + _tileCutPossible = createTiles(calcHeightTilesP(_co, dirTiling.getOutputTileDims(), dirTiling.useCeil()), + calcWidthTilesP(_co, dirTiling.getOutputTileDims(), dirTiling.useCeil()), + dirTiling.getInputTileDims(), dirTiling.getOutputTileDims()); + } + + bool tileCutPossible() const { return _tileCutPossible; } + + HwPoolTilingPtr hwTiling() const { + IE_ASSERT(_tileCutPossible); + return _hwTiling; + } + +private: + bool createTiles(const SmallVector &heightTiles, + const SmallVector &widthTiles, + const DimValues &inputTileDims, const DimValues &outputTileDims) const { + IE_ASSERT(!heightTiles.empty()); + IE_ASSERT(!widthTiles.empty()); + + _hwTiling->sohTiles = heightTiles.size(); + _hwTiling->sowTiles = widthTiles.size(); + _hwTiling->socTiles = divUp(_co._inputDims.get(Dim::N, 1), inputTileDims[Dim::N]); + + for (int sohInd = 0; sohInd < _hwTiling->sohTiles; ++sohInd) { + const auto& heightTileInfo = heightTiles[sohInd]; + + for (int sowInd = 0; sowInd < _hwTiling->sowTiles; ++sowInd) { + const auto& widthTileInfo = widthTiles[sowInd]; + + auto planeTile = std::make_shared(); + planeTile->parent = _hwTiling; + + planeTile->sohInd = sohInd; + planeTile->sowInd = sowInd; + + planeTile->heightInfo = heightTileInfo; + planeTile->widthInfo = widthTileInfo; + + for (int socInd = 0; socInd < _hwTiling->socTiles; ++socInd) { + auto channelTile = std::make_shared(); + channelTile->parent = planeTile; + + channelTile->socInd = socInd; + + channelTile->finalTiles = splitPooling(inputTileDims[Dim::C] * inputTileDims[Dim::N]); + + if (channelTile->finalTiles.numDescr == 0) { + return false; + } + + channelTile->channelStartIndex = socInd * inputTileDims[Dim::N]; + channelTile->numInputChannels = inputTileDims[Dim::N]; + + planeTile->channelTiles.emplace_back(channelTile); + } + + _hwTiling->planeTiles.emplace_back(planeTile); + } + } + + return true; + } +}; + +} // namespace HWTilingNS + +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/passes/hw_pooling_tiling/hw_stage_tiler.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/passes/hw_pooling_tiling/hw_stage_tiler.hpp new file mode 100644 index 00000000000000..bff3178cd603a5 --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/passes/hw_pooling_tiling/hw_stage_tiler.hpp @@ -0,0 +1,83 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include +#include + +namespace vpu { + +struct HWPoolStageOptions; +struct HWPoolStageIO; + +// Builds graph which composes tiled analogue of the single stage 'origStage' +class HWPoolStageTiler { +private: + HWPoolStageTiler() = delete; + HWPoolStageTiler(const HWPoolStageTiler&) = delete; + +public: + DataVector hwInputTiles; + std::vector hwInputTilesOffsets; + + DataVector hwOutputTiles; + std::vector hwOutputTilesOffsets; + + Data hwInput; + Data hwOutput; + + HWPoolStageTiler(const HWPoolStageOptions &so, const HWPoolStageIO &sio, + const Model::Ptr &model, const Handle &origStage, + const StageBuilder::Ptr &stageBuilder, const HwPoolTilingPtr &tiling); +}; + +struct HWPoolStageIO { +private: + HWPoolStageIO() = delete; + HWPoolStageIO(const HWPoolStageIO&) = delete; + +public: + Data origInput; + Data origOutput; + + explicit HWPoolStageIO(const Handle &origStage, const Data &originOutput) { + origInput = origStage->input(0); + origOutput = originOutput; + } +}; + +// Attributes of the stage collected into the structure +struct HWPoolStageOptions { +private: + HWPoolStageOptions() = delete; + HWPoolStageOptions(const HWPoolStageOptions&) = delete; + +public: + int kernelSizeX; + int kernelSizeY; + int kernelStride; + int padLeft; + int padRight; + int padTop; + int padBottom; + + bool withReLU; + + explicit HWPoolStageOptions(const Handle &origStage) { + kernelSizeX = origStage->attrs().get("kernelSizeX"); + kernelSizeY = origStage->attrs().get("kernelSizeY"); + kernelStride = origStage->attrs().get("kernelStrideX"); + padLeft = origStage->attrs().get("padLeft"); + padRight = origStage->attrs().get("padRight"); + padTop = origStage->attrs().get("padTop"); + padBottom = origStage->attrs().get("padBottom"); + + withReLU = origStage->attrs().getOrDefault("withReLU", false); + } +}; + +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/private_plugin_config.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/private_plugin_config.hpp index 7cc5dfb50367eb..9f2670efafc194 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/private_plugin_config.hpp +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/private_plugin_config.hpp @@ -19,6 +19,7 @@ namespace VPUConfigParams { DECLARE_VPU_CONFIG_KEY(NUMBER_OF_SHAVES); DECLARE_VPU_CONFIG_KEY(NUMBER_OF_CMX_SLICES); +DECLARE_VPU_CONFIG_KEY(TENSOR_STRIDES); DECLARE_VPU_CONFIG_KEY(HW_ADAPTIVE_MODE); @@ -39,8 +40,6 @@ DECLARE_VPU_CONFIG_KEY(HW_DILATION); DECLARE_VPU_CONFIG_KEY(DETECT_NETWORK_BATCH); -DECLARE_VPU_CONFIG_KEY(ALLOW_FP32_MODELS); - DECLARE_VPU_CONFIG_KEY(HW_WHITE_LIST); DECLARE_VPU_CONFIG_KEY(HW_BLACK_LIST); @@ -52,6 +51,16 @@ DECLARE_VPU_CONFIG_KEY(IGNORE_UNKNOWN_LAYERS); // Myriad plugin options // +// Power Manager + +DECLARE_VPU_MYRIAD_CONFIG_KEY(POWER_MANAGEMENT); + +DECLARE_VPU_MYRIAD_CONFIG_VALUE(POWER_FULL); +DECLARE_VPU_MYRIAD_CONFIG_VALUE(POWER_INFER); +DECLARE_VPU_MYRIAD_CONFIG_VALUE(POWER_STAGE); +DECLARE_VPU_MYRIAD_CONFIG_VALUE(POWER_STAGE_SHAVES); +DECLARE_VPU_MYRIAD_CONFIG_VALUE(POWER_STAGE_NCES); + DECLARE_VPU_MYRIAD_CONFIG_KEY(WATCHDOG); INFERENCE_ENGINE_DEPRECATED DECLARE_VPU_CONFIG_KEY(WATCHDOG); diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/special_stage_processor.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/special_stage_processor.hpp new file mode 100644 index 00000000000000..30a445ffcee940 --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/special_stage_processor.hpp @@ -0,0 +1,45 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include +#include +#include + +namespace vpu { + +class SpecialStageProcessor final { +public: + inline explicit SpecialStageProcessor(const StageBuilder::Ptr& stageBuilder) : + _stageBuilder(stageBuilder) { + } + + void processSplit( + const Model::Ptr& model, + const Stage& stage); + + void processConcat( + const Model::Ptr& model, + const Stage& stage); + + void processReshape( + const Model::Ptr& model, + const Stage& stage); + + void processExpand( + const Model::Ptr& model, + const Stage& stage); + + void processShrink( + const Model::Ptr& model, + const Stage& stage); + +private: + StageBuilder::Ptr _stageBuilder; +}; + +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/stub_stage.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/stub_stage.hpp index 72264c4a6e5cae..383cdb6754e5d2 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/stub_stage.hpp +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/stub_stage.hpp @@ -16,15 +16,18 @@ class StubStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override; + ScalePropagationStep step, + StageDataInfo& scaleInfo) override; - void propagateDataOrderImpl() const override; + void propagateDataOrderImpl(StageDataInfo& orderInfo) override; - void getDataStridesRequirementsImpl() const override; + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override; void finalizeDataLayoutImpl() override; - void getBatchSupportInfoImpl() const override; + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override; + + void initialCheckImpl() const override; void finalCheckImpl() const override; diff --git a/inference-engine/src/vpu/graph_transformer/include/vpu/sw/post_op_stage.hpp b/inference-engine/src/vpu/graph_transformer/include/vpu/sw/post_op_stage.hpp index 27df0419f925dc..77ec0173d68f3c 100644 --- a/inference-engine/src/vpu/graph_transformer/include/vpu/sw/post_op_stage.hpp +++ b/inference-engine/src/vpu/graph_transformer/include/vpu/sw/post_op_stage.hpp @@ -10,17 +10,17 @@ namespace vpu { class PostOpStage : public StageNode { protected: - void propagateDataOrderImpl() const override; + void propagateDataOrderImpl(StageDataInfo& orderInfo) override; - void getDataStridesRequirementsImpl() const override; + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override; void finalizeDataLayoutImpl() override; - void getBatchSupportInfoImpl() const override; + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override; StageSHAVEsRequirements getSHAVEsRequirementsImpl() const override; - void finalCheckImpl() const override; + void initialCheckImpl() const override; void serializeDataImpl(BlobSerializer& serializer) const override; }; diff --git a/inference-engine/src/vpu/graph_transformer/src/allocator.cpp b/inference-engine/src/vpu/graph_transformer/src/allocator.cpp index 8f07987e777321..bc30e8d34dff07 100644 --- a/inference-engine/src/vpu/graph_transformer/src/allocator.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/allocator.cpp @@ -124,7 +124,6 @@ bool Allocator::allocateData(const Data& data) { if (data->usage() == DataUsage::Input) { if (_allocatedData.count(data) == 0) { IE_ASSERT(data->parentDataEdge() == nullptr); - IE_ASSERT(data->checkStrides(StridesRequirement::compact())); auto finalByteSize = alignVal(data->totalByteSize() * _modelBatchSize, DATA_ALIGNMENT); @@ -146,7 +145,6 @@ bool Allocator::allocateData(const Data& data) { if (data->usage() == DataUsage::Output) { if (_allocatedData.count(data) == 0) { IE_ASSERT(data->parentDataEdge() == nullptr); - IE_ASSERT(data->checkStrides(StridesRequirement::compact())); int finalByteSize = 0; if (data->attrs().getOrDefault("unbatched", false)) { diff --git a/inference-engine/src/vpu/graph_transformer/src/backend/backend.cpp b/inference-engine/src/vpu/graph_transformer/src/backend/backend.cpp index 3cc1f291d6212e..307a358d8d60f5 100644 --- a/inference-engine/src/vpu/graph_transformer/src/backend/backend.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/backend/backend.cpp @@ -59,7 +59,7 @@ CompiledGraph::Ptr BackEnd::build( extractDataInfo(model, compiledGraph->inputInfo, compiledGraph->outputInfo); serialize(model, compiledGraph->blob, compiledGraph->blobHeader, compiledGraph->numActiveStages); - getMetaData(model, allLayers, compiledGraph->stagesMeta); + getMetaData(model, allLayers, compiledGraph->graphMeta); return compiledGraph; } diff --git a/inference-engine/src/vpu/graph_transformer/src/backend/get_meta_data.cpp b/inference-engine/src/vpu/graph_transformer/src/backend/get_meta_data.cpp index 8f251b4b070047..2b3dc5ea18e34e 100644 --- a/inference-engine/src/vpu/graph_transformer/src/backend/get_meta_data.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/backend/get_meta_data.cpp @@ -42,51 +42,111 @@ namespace vpu { void BackEnd::getMetaData( const Model::Ptr& model, const std::vector& allLayers, - std::vector& metaData) { + GraphMetaInfo& graphMeta) { VPU_PROFILE(getMetaData); - metaData.clear(); - metaData.reserve(3 * model->numStages() / 2 + 1); + std::vector stagesMeta; + std::vector datasMeta; std::unordered_set visitedLayers; + int execOrder{}; + StageMap stageToMetaIndex; - auto getStageMeta = [&visitedLayers](const Stage& stage) -> StageMetaInfo { - StageMetaInfo meta; + stagesMeta.reserve(3 * model->numStages() / 2 + 1); + datasMeta.reserve(3 * model->numDatas() / 2 + 1); - meta.stageName = stage->name(); - meta.stageType = toString(stage->type()); + graphMeta.graphName = model->name(); + + auto getStageMeta = [&](const Stage& stage) -> StageMetaInfo { + StageMetaInfo stageMeta; + + stageMeta.displayStageName = stageMeta.stageName = stage->name(); + stageMeta.stageType = toString(stage->type()); + + if (stage->category() != StageCategory::Special) { + stageMeta.execOrder = execOrder++; + } else { + stageMeta.execOrder = -1; + } if (stage->numInjectedStages() > 0) { - meta.stageName += " + injected["; - meta.stageType += " + injected["; + stageMeta.displayStageName += " + injected["; + stageMeta.stageType += " + injected["; int ind = 0; for (const auto& injectedStageEdge : stage->injectedStageEdges()) { if (ind != 0) { - meta.stageName += ", "; - meta.stageType += ", "; + stageMeta.displayStageName += ", "; + stageMeta.stageType += ", "; } - meta.stageName += injectedStageEdge->child()->name(); - meta.stageType += toString(injectedStageEdge->child()->type()); + stageMeta.displayStageName += injectedStageEdge->child()->name(); + stageMeta.stageType += toString(injectedStageEdge->child()->type()); ++ind; } - meta.stageName += "]"; - meta.stageType += "]"; + stageMeta.displayStageName += "]"; + stageMeta.stageType += "]"; } if (stage->origLayer() == nullptr) { - meta.layerName = ""; - meta.layerType = ""; + stageMeta.layerName = ""; + stageMeta.layerType = ""; } else { - meta.layerName = stage->origLayer()->name; - meta.layerType = stage->origLayer()->type; + stageMeta.layerName = stage->origLayer()->name; + stageMeta.layerType = stage->origLayer()->type; visitedLayers.insert(stage->origLayer()); } - return meta; + return stageMeta; + }; + + auto getDataMeta = [&](const Data& data) -> DataMetaInfo { + DataMetaInfo dataMeta; + + dataMeta.name = data->name(); + dataMeta.desc = data->desc().toTensorDesc(); + + if (data->usage() == DataUsage::Input) { + // Create fake input layer + StageMetaInfo inputInfo; + + inputInfo.layerType = "Input"; + inputInfo.layerName = inputInfo.stageName = inputInfo.displayStageName = data->name(); + inputInfo.stageType = "NONE"; + inputInfo.outPrecisions.push_back(dataMeta.desc.getPrecision()); + inputInfo.outLayouts.push_back(dataMeta.desc.getLayout()); + stagesMeta.push_back(std::move(inputInfo)); + + dataMeta.parentIndex = stagesMeta.size() - 1; + } else { + auto it = stageToMetaIndex.find(data->producer()); + + if (it != stageToMetaIndex.end()) { + StageMetaInfo& meta = stagesMeta[it->second]; + + meta.outPrecisions.push_back(dataMeta.desc.getPrecision()); + meta.outLayouts.push_back(dataMeta.desc.getLayout()); + + dataMeta.parentIndex = it->second; + } + } + + if (data->usage() != DataUsage::Output) { + for (const auto &child : data->consumers()) { + auto it = stageToMetaIndex.find(child); + + if (it != stageToMetaIndex.end()) { + StageMetaInfo& meta = stagesMeta[it->second]; + + meta.inputsNum++; + dataMeta.childrenIndices.push_back(it->second); + } + } + } + + return dataMeta; }; // @@ -98,9 +158,11 @@ void BackEnd::getMetaData( continue; } - auto meta = getStageMeta(stage); - meta.status = ie::InferenceEngineProfileInfo::EXECUTED; - metaData.emplace_back(std::move(meta)); + auto stageMeta = getStageMeta(stage); + + stageMeta.status = ie::InferenceEngineProfileInfo::EXECUTED; + stagesMeta.emplace_back(std::move(stageMeta)); + stageToMetaIndex[stage] = stagesMeta.size() - 1; } // @@ -109,12 +171,12 @@ void BackEnd::getMetaData( // TODO : support config to disable timings and not to add this meta if it is not required by user StageMetaInfo receiveTensorMeta; - receiveTensorMeta.stageName = ""; + receiveTensorMeta.displayStageName = receiveTensorMeta.stageName = ""; receiveTensorMeta.stageType = ""; receiveTensorMeta.layerName = ""; receiveTensorMeta.layerType = ""; receiveTensorMeta.status = ie::InferenceEngineProfileInfo::EXECUTED; - metaData.emplace_back(std::move(receiveTensorMeta)); + stagesMeta.emplace_back(std::move(receiveTensorMeta)); // // Add special stages @@ -125,9 +187,10 @@ void BackEnd::getMetaData( continue; } - auto meta = getStageMeta(stage); - meta.status = ie::InferenceEngineProfileInfo::OPTIMIZED_OUT; - metaData.emplace_back(std::move(meta)); + auto stageMeta = getStageMeta(stage); + stageMeta.status = ie::InferenceEngineProfileInfo::NOT_RUN; + stagesMeta.emplace_back(std::move(stageMeta)); + stageToMetaIndex[stage] = stagesMeta.size() - 1; } // @@ -139,14 +202,32 @@ void BackEnd::getMetaData( continue; } - StageMetaInfo meta; - meta.stageName = ""; - meta.stageType = ""; - meta.layerName = layer->name; - meta.layerType = layer->type; - meta.status = ie::InferenceEngineProfileInfo::LayerStatus::OPTIMIZED_OUT; - metaData.emplace_back(std::move(meta)); + StageMetaInfo stageMeta; + stageMeta.stageName = ""; + stageMeta.stageType = ""; + stageMeta.layerName = layer->name; + stageMeta.layerType = layer->type; + stageMeta.status = ie::InferenceEngineProfileInfo::LayerStatus::OPTIMIZED_OUT; + stagesMeta.emplace_back(std::move(stageMeta)); } + + // + // Add data info + // + + for (const auto& data : model->datas()) { + if (data->usage() != DataUsage::Input && + data->usage() != DataUsage::Intermediate && + data->usage() != DataUsage::Output) { + continue; + } + + auto dataMeta = getDataMeta(data); + datasMeta.emplace_back(std::move(dataMeta)); + } + + graphMeta.stagesMeta = std::move(stagesMeta); + graphMeta.datasMeta = std::move(datasMeta); } } // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/blob_reader.cpp b/inference-engine/src/vpu/graph_transformer/src/blob_reader.cpp index 3ed75161347b99..1179077d82dda4 100644 --- a/inference-engine/src/vpu/graph_transformer/src/blob_reader.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/blob_reader.cpp @@ -29,58 +29,6 @@ T readFromBlob(const std::vector& blob, uint32_t& offset) { return *reinterpret_cast(srcPtr); } -ie::Precision vpuDataTypeToIE(DataType dataType) { - auto iePrecision = ie::Precision::UNSPECIFIED; - - switch (dataType) { - case DataType::U8: - iePrecision = ie::Precision::U8; - break; - case DataType::FP16: - iePrecision = ie::Precision::FP16; - break; - case DataType::FP32: - iePrecision = ie::Precision::FP32; - break; - default: - VPU_THROW_EXCEPTION << "BlobReader error: unsupported dataType " << dataType; - } - - return iePrecision; -} - -ie::Layout vpuDimsOrderToIE(DimsOrder dimsOrder) { - auto ieLayout = ie::Layout::ANY; - - if (DimsOrder::C == dimsOrder) { - ieLayout = ie::Layout::C; - } else if (DimsOrder::NC == dimsOrder) { - ieLayout = ie::Layout::NC; - } else if (DimsOrder::CHW == dimsOrder) { - ieLayout = ie::Layout::CHW; - } else if (DimsOrder::NCHW == dimsOrder) { - ieLayout = ie::Layout::NCHW; - } else if (DimsOrder::NHWC == dimsOrder) { - ieLayout = ie::Layout::NHWC; - } else { - VPU_THROW_EXCEPTION << "BlobReader error: unsupported dimsOrder " << toString(dimsOrder); - } - - return ieLayout; -} - -ie::SizeVector vpuDimsToIE(const DimValues& dimValues) { - auto order = DimsOrder::fromNumDims(dimValues.size()); - auto perm = order.toPermutation(); - - ie::SizeVector ieDims(perm.size()); - for (int i = 0; i < perm.size(); ++i) { - ieDims[ieDims.size() - 1 - i] = dimValues[perm[i]]; - } - - return ieDims; -} - } // namespace void BlobReader::parse(const std::vector& blob) { @@ -134,11 +82,7 @@ void BlobReader::parse(const std::vector& blob) { // Skip strides inputInfoSecOffset += perm.size() * sizeof(uint32_t); - auto iePrecision = vpuDataTypeToIE(dataType); - auto ieLayout = vpuDimsOrderToIE(dimsOrder); - auto ieDims = vpuDimsToIE(vpuDims); - - ie::TensorDesc ieDesc(iePrecision, ieDims, ieLayout); + ie::TensorDesc ieDesc = DataDesc(dataType, dimsOrder, vpuDims).toTensorDesc(); ie::Data inputData(inputName, ieDesc); ie::InputInfo input; @@ -181,11 +125,7 @@ void BlobReader::parse(const std::vector& blob) { // Skip strides outputInfoSecOffset += perm.size() * sizeof(uint32_t); - auto iePrecision = vpuDataTypeToIE(dataType); - auto ieLayout = vpuDimsOrderToIE(dimsOrder); - auto ieDims = vpuDimsToIE(vpuDims); - - ie::TensorDesc ieDesc(iePrecision, ieDims, ieLayout); + ie::TensorDesc ieDesc = DataDesc(dataType, dimsOrder, vpuDims).toTensorDesc(); ie::Data outputData(outputName, ieDesc); _networkOutputs[outputData.getName()] = std::make_shared(outputData); diff --git a/inference-engine/src/vpu/graph_transformer/src/custom_layer.cpp b/inference-engine/src/vpu/graph_transformer/src/custom_layer.cpp index d95263ce9ee339..bae11a81b82b51 100644 --- a/inference-engine/src/vpu/graph_transformer/src/custom_layer.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/custom_layer.cpp @@ -15,7 +15,7 @@ #include #include -#ifdef __linux__ +#if defined(__linux__) || defined (__APPLE__) # include #endif @@ -293,7 +293,7 @@ ie::details::caseless_map> CustomLaye #ifdef _WIN32 char path[MAX_PATH]; auto abs_path_ptr = _fullpath(path, configFile.c_str(), MAX_PATH); -#elif __linux__ +#elif defined(__linux__) || defined(__APPLE__) char path[PATH_MAX]; auto abs_path_ptr = realpath(configFile.c_str(), path); #endif @@ -427,8 +427,8 @@ void CustomLayer::processKernelNode(const pugi::xml_node& node) { contentStream << inputFile.rdbuf(); _kernelBinary.append(contentStream.str()); - if (_kernelBinary.size() >= 16*1024) { - VPU_THROW_EXCEPTION << "Kernel binary exceeds 16KB." << fileName; + if (_kernelBinary.size() >= 32*1024) { + VPU_THROW_EXCEPTION << "Kernel binary exceeds 32KB." << fileName; } } @@ -677,6 +677,8 @@ CustomDataFormat CustomLayer::formatFromString(const std::string & str) { static const ie::details::caseless_map FormatNameToType = { { "BFYX" , CustomDataFormat::BFYX }, { "BYXF" , CustomDataFormat::BYXF }, + { "FYX" , CustomDataFormat::FYX }, + { "YXF" , CustomDataFormat::YXF }, { "ANY" , CustomDataFormat::Any }, }; diff --git a/inference-engine/src/vpu/graph_transformer/src/frontend/detect_network_batch.cpp b/inference-engine/src/vpu/graph_transformer/src/frontend/detect_network_batch.cpp index faea63d16265cd..e71714408399de 100644 --- a/inference-engine/src/vpu/graph_transformer/src/frontend/detect_network_batch.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/frontend/detect_network_batch.cpp @@ -35,9 +35,7 @@ ie::CNNNetwork FrontEnd::detectNetworkBatch( if (batchSize == 1 || !env.config.detectBatch) { env.log->debug("Keep original network"); - IE_SUPPRESS_DEPRECATED_START - return ie::CNNNetwork(const_cast(&origNetwork)); - IE_SUPPRESS_DEPRECATED_END + return ie::CNNNetwork(ie::ICNNNetwork::Ptr(const_cast(&origNetwork), [](void *) {})); } model->setBatchSize(batchSize); @@ -72,16 +70,16 @@ ie::CNNNetwork FrontEnd::detectNetworkBatch( env.log->debug("Input [%s] : %v", p.first, ieShapes); switch (ieData->getLayout()) { - case ie::Layout::NCHW: - case ie::Layout::NHWC: - case ie::Layout::NC: - ieShapes[0] = 1; - break; - case ie::Layout::CN: - ieShapes[1] = 1; - break; - default: - VPU_THROW_EXCEPTION << "Unexpected input layout : " << ieData->getLayout(); + case ie::Layout::NCDHW: + case ie::Layout::NDHWC: + case ie::Layout::NCHW: + case ie::Layout::NHWC: + case ie::Layout::NC: + case ie::Layout::CN: + ieShapes[0] = 1; + break; + default: + VPU_THROW_EXCEPTION << "Unexpected input layout : " << ieData->getLayout(); } inputShapes[ieData->getName()] = ieShapes; diff --git a/inference-engine/src/vpu/graph_transformer/src/frontend/frontend.cpp b/inference-engine/src/vpu/graph_transformer/src/frontend/frontend.cpp index ea3805531fd30f..819e08a6d8fb9e 100644 --- a/inference-engine/src/vpu/graph_transformer/src/frontend/frontend.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/frontend/frontend.cpp @@ -77,9 +77,15 @@ ie::details::caseless_map g_parsers = { {"LSTMSequence", &FrontEnd::parseRNN}, {"GEMM", &FrontEnd::parseGEMM}, {"Log", &FrontEnd::parseLog}, + {"Exp", &FrontEnd::parseExp}, {"ReverseSequence", &FrontEnd::parseReverseSequence}, {"Gather", &FrontEnd::parseGather}, {"ReduceAnd", &FrontEnd::parseReduce}, + {"Floor", &FrontEnd::parseFloor}, + {"TopK", &FrontEnd::parseTopK}, + {"ReduceMin", &FrontEnd::parseReduce}, + {"StridedSlice", &FrontEnd::parseStridedSlice}, + {"Select", &FrontEnd::parseSelect}, }; std::atomic g_counter(0); @@ -211,7 +217,7 @@ Model::Ptr FrontEnd::buildInitialModel(const ie::ICNNNetwork& network) { eliminatePriorBoxData(model); - model->cleanUpDatas(); + model->cleanUp(); return model; } @@ -368,7 +374,6 @@ void FrontEnd::getInputAndOutputData( inputs[i] = getVpuData(layerInput); IE_ASSERT(inputs[i] != nullptr); - IE_ASSERT(inputs[i]->desc().type() == DataType::FP16); } outputs.resize(layer->outData.size()); @@ -377,11 +382,13 @@ void FrontEnd::getInputAndOutputData( IE_ASSERT(layerOutput != nullptr); if (auto data = getVpuData(layerOutput)) { - IE_ASSERT(data->desc().type() == DataType::FP16); outputs[i] = data; } else { DataDesc dataDesc(layerOutput->getTensorDesc()); - dataDesc.setType(DataType::FP16); + if (dataDesc.type() == DataType::FP32) { + // To infer the same FP32 models on different devices (CPU, GPU, VPU and so on) + dataDesc.setType(DataType::FP16); + } outputs[i] = model->addNewData( layerOutput->getName(), diff --git a/inference-engine/src/vpu/graph_transformer/src/frontend/in_out_convert.cpp b/inference-engine/src/vpu/graph_transformer/src/frontend/in_out_convert.cpp index 71a73b324766f6..f7065ac32bd6d6 100644 --- a/inference-engine/src/vpu/graph_transformer/src/frontend/in_out_convert.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/frontend/in_out_convert.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -23,12 +24,10 @@ class ConvertStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); auto inputScale = inputScales[0]; @@ -36,12 +35,12 @@ class ConvertStage final : public StageNode { IE_ASSERT(output->usage() == DataUsage::Output); IE_ASSERT(step == ScalePropagationStep::Propagate); - _scaleInfo.setInput(_inputEdges[0], 1.0f); - _scaleInfo.setOutput(_outputEdges[0], 1.0f); + scaleInfo.setInput(inputEdge(0), 1.0f); + scaleInfo.setOutput(outputEdge(0), 1.0f); } else { IE_ASSERT(input->usage() == DataUsage::Input); - _scaleInfo.setOutput(_outputEdges[0], inputScale); + scaleInfo.setOutput(outputEdge(0), inputScale); if (step == ScalePropagationStep::ScaleInput) { attrs().get("scale") *= inputScale; @@ -50,40 +49,34 @@ class ConvertStage final : public StageNode { } } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); if (_type == StageType::Convert_f16f32) { - IE_ASSERT(output->usage() == DataUsage::Output); + IE_ASSERT(output->usage() == DataUsage::Output || output->usage() == DataUsage::Intermediate); auto outDimsOrder = output->desc().dimsOrder(); // HCW is not supported IE_ASSERT(outDimsOrder.dimInd(Dim::C) != 1); - _orderInfo.setInput(_inputEdges[0], outDimsOrder); + orderInfo.setInput(inputEdge(0), outDimsOrder); } else { - IE_ASSERT(input->usage() == DataUsage::Input); + IE_ASSERT(input->usage() == DataUsage::Input || input->usage() == DataUsage::Intermediate); auto inDimsOrder = input->desc().dimsOrder(); // HCW is not supported IE_ASSERT(inDimsOrder.dimInd(Dim::C) != 1); - _orderInfo.setOutput(_outputEdges[0], inDimsOrder); + orderInfo.setOutput(outputEdge(0), inDimsOrder); } } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); auto inDimsOrder = input->desc().dimsOrder(); @@ -95,22 +88,22 @@ class ConvertStage final : public StageNode { } if (_type == StageType::Convert_f16f32) { - IE_ASSERT(output->usage() == DataUsage::Output); + IE_ASSERT(output->usage() == DataUsage::Output || output->usage() == DataUsage::Intermediate); - _stridesInfo.setInput(_inputEdges[0], reqs); - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement::compact()); + stridesInfo.setInput(inputEdge(0), reqs); + stridesInfo.setOutput(outputEdge(0), StridesRequirement::compact()); } else { - IE_ASSERT(input->usage() == DataUsage::Input); + IE_ASSERT(input->usage() == DataUsage::Input || input->usage() == DataUsage::Intermediate); - _stridesInfo.setInput(_inputEdges[0], StridesRequirement::compact()); - _stridesInfo.setOutput(_outputEdges[0], reqs); + stridesInfo.setInput(inputEdge(0), StridesRequirement::compact()); + stridesInfo.setOutput(outputEdge(0), reqs); } } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { // Convert will support batch by merging it with previous dimension. } @@ -119,7 +112,22 @@ class ConvertStage final : public StageNode { return StageSHAVEsRequirements::TwoOrOne; } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + const auto expectedTypes = EnumMap>{ + {StageType::Convert_u8f16, {DataType::U8, DataType::FP16}}, + {StageType::Convert_f16f32, {DataType::FP16, DataType::FP32}}, + {StageType::Convert_f32f16, {DataType::FP32, DataType::FP16}}, + }; + + auto match = expectedTypes.find(_type); + if (match == expectedTypes.end()) { + VPU_THROW_EXCEPTION << "unknown type"; + } + const auto& types = match->second; + + const auto& srcType = types.first; + const auto& dstType = types.second; + assertInputsOutputsTypes(this, {{srcType}}, {{dstType}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -135,12 +143,8 @@ class ConvertStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); if (input->desc().dimsOrder() == DimsOrder::NC) { input->serializeOldBuffer( @@ -230,27 +234,61 @@ void FrontEnd::addDataTypeConvertStages(const Model::Ptr& model) { env.log->warning("[VPU] GraphTransformer : INPUT_BIAS option is deprecated"); } + const bool hasScaleBias = env.config.inputScale != 1.0f || env.config.inputBias != 0.0f; for (const auto& input : model->datas()) { if (input->usage() != DataUsage::Input) continue; - if (input->desc().type() != DataType::FP16) { - env.log->debug("convert input %s to FP16", input->name()); + const auto& type = input->desc().type(); + + if (type == DataType::FP16 && hasScaleBias) { + std::ostringstream postfixOstr; + if (env.config.inputScale != 1.0f) { + postfixOstr << "@SCALE=" << std::to_string(env.config.inputScale); + } + if (env.config.inputBias != 0.0f) { + postfixOstr << "@BIAS=" << std::to_string(env.config.inputBias); + } + + auto postfix = postfixOstr.str(); + + auto scaledInput = model->duplicateData( + input, + postfix); + + bindData(scaledInput, input->origData()); + + _stageBuilder->addPowerStage( + model, + scaledInput->name(), + nullptr, + env.config.inputScale, + 1.0f, + env.config.inputBias, + input, + scaledInput); + } + + if (type != DataType::FP32 && type != DataType::U8) { + continue; + } + + env.log->debug("convert input %s to FP16", input->name()); - auto fp16Desc = input->desc(); - fp16Desc.setType(DataType::FP16); + auto fp16Desc = input->desc(); + fp16Desc.setType(DataType::FP16); - auto inputFP16 = model->duplicateData( + auto inputFP16 = model->duplicateData( input, "@FP16", fp16Desc); - input->attrs().set("fp16_copy", inputFP16); + input->attrs().set("fp16_copy", inputFP16); - bindData(inputFP16, input->origData()); + bindData(inputFP16, input->origData()); - auto stageType = StageType::None; - switch (input->desc().type()) { + auto stageType = StageType::None; + switch (input->desc().type()) { case DataType::U8: stageType = StageType::Convert_u8f16; break; @@ -259,9 +297,9 @@ void FrontEnd::addDataTypeConvertStages(const Model::Ptr& model) { break; default: VPU_THROW_EXCEPTION << "Unsupported input data type : " << input->desc().type(); - } + } - _stageBuilder->createConvertStage( + _stageBuilder->createConvertStage( model, inputFP16->name(), input, @@ -269,69 +307,45 @@ void FrontEnd::addDataTypeConvertStages(const Model::Ptr& model) { stageType, env.config.inputScale, env.config.inputBias); - } else if (env.config.inputScale != 1.0f || env.config.inputBias != 0.0f) { - std::ostringstream postfixOstr; - if (env.config.inputScale != 1.0f) { - postfixOstr << "@SCALE=" << std::to_string(env.config.inputScale); - } - if (env.config.inputBias != 0.0f) { - postfixOstr << "@BIAS=" << std::to_string(env.config.inputBias); - } - - auto postfix = postfixOstr.str(); - - auto scaledInput = model->duplicateData( - input, - postfix); - - bindData(scaledInput, input->origData()); - - _stageBuilder->addPowerStage( - model, - scaledInput->name(), - nullptr, - env.config.inputScale, - 1.0f, - env.config.inputBias, - input, - scaledInput); - } } for (const auto& output : model->datas()) { if (output->usage() != DataUsage::Output) continue; - if (output->desc().type() != DataType::FP16) { - env.log->debug("convert output %s from FP16", output->name()); + const auto& actualType = output->desc().type(); + if (actualType != DataType::FP32) { + // Output datas keep their precision (intermeadiate have been forced to FP16 in case of FP32 from IR). + // If FP32 output has been requested VPU executes in FP16 with following convert FP16 -> FP32 + continue; + } - IE_ASSERT(output->desc().type() == DataType::FP32); + env.log->debug("convert output %s from FP16", output->name()); - auto fp16Desc = output->desc(); - fp16Desc.setType(DataType::FP16); + auto fp16Desc = output->desc(); + fp16Desc.setType(DataType::FP16); - auto outputFP16 = model->duplicateData( - output, - "@FP16", - fp16Desc); + auto outputFP16 = model->duplicateData( + output, + "@FP16", + fp16Desc); - output->attrs().set("fp16_copy", outputFP16); + output->attrs().set("fp16_copy", outputFP16); - bindData(outputFP16, output->origData()); + bindData(outputFP16, output->origData()); - auto stage = _stageBuilder->createConvertStage( - model, - outputFP16->name(), - outputFP16, - output, - StageType::Convert_f16f32); + auto stage = _stageBuilder->createConvertStage( + model, + outputFP16->name(), + outputFP16, + output, + StageType::Convert_f16f32); - auto withDetectionOutput = model->attrs().getOrDefault("withDetectionOutput", false); - stage->attrs().set("convertFromDetOutput", withDetectionOutput); + auto withDetectionOutput = model->attrs().getOrDefault("withDetectionOutput", false); + stage->attrs().set("convertFromDetOutput", withDetectionOutput); - auto haveBatch = _unbatchedOutputs.count(output->origData()) == 0; - stage->attrs().set("haveBatch", haveBatch); - } + auto haveBatch = _unbatchedOutputs.count(output->origData()) == 0; + stage->attrs().set("haveBatch", haveBatch); } } diff --git a/inference-engine/src/vpu/graph_transformer/src/frontend/parse_data.cpp b/inference-engine/src/vpu/graph_transformer/src/frontend/parse_data.cpp index 4aea3d26695a14..67205d71f8eb16 100644 --- a/inference-engine/src/vpu/graph_transformer/src/frontend/parse_data.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/frontend/parse_data.cpp @@ -7,8 +7,11 @@ #include #include #include +#include +#include #include +#include namespace vpu { @@ -17,6 +20,27 @@ void FrontEnd::parseInputAndOutputData(const Model::Ptr& model) { const auto& env = CompileEnv::get(); + auto layoutPreference = LayoutPreference::AUTO; + if (env.config.hwOptimization || + env.config.forceLayout == ComputeLayout::NCHW || + env.config.forceLayout == ComputeLayout::NCDHW) { + layoutPreference = LayoutPreference::ChannelMajor; // CHW, NCHW, NCDHW + } else { + layoutPreference = LayoutPreference::ChannelMinor; // HWC, NHWC, NDHWC + } + + // TODO: InferenceEngine doesn't support 3D HWC. + + auto parseIOStrides = [&](const std::string& name, Data& data) { + const auto& match = env.config.ioStrides.find(name); + if (match == env.config.ioStrides.end()) { + return; + } + + const auto reqs = StridesRequirement::fixed(match->second, data->desc()); + data->updateRequiredStrides(reqs); + }; + // // Parse network inputs // @@ -29,15 +53,20 @@ void FrontEnd::parseInputAndOutputData(const Model::Ptr& model) { IE_ASSERT(ieData != nullptr); DataDesc vpuDesc(ieData->getTensorDesc()); - if (vpuDesc.numDims() >= 3) { - if (env.config.hwOptimization || env.config.forceLayout == ComputeLayout::NCHW) { - vpuDesc.moveDim(Dim::C, 2); + if (vpuDesc.numDims() >= 4) { + if (LayoutPreference::ChannelMajor == layoutPreference) { + if (vpuDesc.dimsOrder() == DimsOrder::NDHWC) + vpuDesc.moveDim(Dim::C, 3); + if (vpuDesc.dimsOrder() == DimsOrder::NHWC) + vpuDesc.moveDim(Dim::C, 2); } else { vpuDesc.moveDim(Dim::C, 0); } } auto vpuData = model->addInputData(ieData->getName(), vpuDesc); + parseIOStrides(inputInfo.first, vpuData); + bindData(vpuData, ieData); } @@ -52,15 +81,20 @@ void FrontEnd::parseInputAndOutputData(const Model::Ptr& model) { IE_ASSERT(ieData != nullptr); DataDesc vpuDesc(ieData->getTensorDesc()); - if (vpuDesc.numDims() >= 3) { - if (env.config.hwOptimization || env.config.forceLayout == ComputeLayout::NCHW) { - vpuDesc.moveDim(Dim::C, 2); + if (vpuDesc.numDims() >= 4) { + if (LayoutPreference::ChannelMajor == layoutPreference) { + if (vpuDesc.dimsOrder() == DimsOrder::NDHWC) + vpuDesc.moveDim(Dim::C, 3); + if (vpuDesc.dimsOrder() == DimsOrder::NHWC) + vpuDesc.moveDim(Dim::C, 2); } else { vpuDesc.moveDim(Dim::C, 0); } } auto vpuData = model->addOutputData(ieData->getName(), vpuDesc); + parseIOStrides(outputInfo.first, vpuData); + bindData(vpuData, ieData); if (_unbatchedOutputs.count(ieData) > 0) { @@ -81,16 +115,7 @@ void FrontEnd::parseInputAndOutputData(const Model::Ptr& model) { auto ieBlob = constInfo.second; IE_ASSERT(ieBlob != nullptr); - auto ieDesc = ieData->getTensorDesc(); - - if (ieDesc.getPrecision() != ie::Precision::FP16) { - if (ieDesc.getPrecision() != ie::Precision::FP32 || !env.config.allowFP32Models) { - VPU_THROW_EXCEPTION << "Unsupported precision " << ieDesc.getPrecision() << "for data " << ieData->getName(); - } - } - - DataDesc vpuDesc(ieDesc); - vpuDesc.setType(DataType::FP16); + DataDesc vpuDesc(ieData->getTensorDesc()); auto vpuData = model->addConstData( ieData->getName(), @@ -111,42 +136,6 @@ void FrontEnd::parseInputAndOutputData(const Model::Ptr& model) { bindData(vpuData, ieData); } - - // - // Add Copy stages after network outputs, if they are in the middle - // - - for (const auto& outputInfo : _ieNetworkParser.networkOutputs) { - auto ieData = outputInfo.second; - IE_ASSERT(ieData != nullptr); - - auto vpuData = getVpuData(ieData); - IE_ASSERT(vpuData != nullptr); - - // It might be Const. - if (vpuData->usage() != DataUsage::Output) - continue; - - // Convert stage will be added. - if (vpuData->desc().type() != DataType::FP16) - continue; - - if (!ieData->getInputTo().empty()) { - auto vpuTempData = model->duplicateData( - vpuData, - "@intermediate", - vpuData->desc()); - - _stageBuilder->addCopyStage( - model, - formatString("%s@copy-to-output", vpuData->name()), - nullptr, - vpuTempData, - vpuData); - - bindData(vpuTempData, ieData); - } - } } } // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/frontend/parse_network.cpp b/inference-engine/src/vpu/graph_transformer/src/frontend/parse_network.cpp index 24e3814c130b61..00f1d0a2ca6ac8 100644 --- a/inference-engine/src/vpu/graph_transformer/src/frontend/parse_network.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/frontend/parse_network.cpp @@ -97,26 +97,11 @@ void IeNetworkParser::checkNetwork(const ie::CNNNetwork& network) { for (const auto& netInput : networkInputs) { auto inputInfo = netInput.second; IE_ASSERT(inputInfo != nullptr); - - auto inputPrecision = inputInfo->getPrecision(); - - if (inputPrecision != ie::Precision::U8 && - inputPrecision != ie::Precision::FP16 && - inputPrecision != ie::Precision::FP32) { - THROW_IE_EXCEPTION << "[PARAMETER_MISMATCH] Unsupported input precision: " << inputPrecision.name() << "!"; - } } for (const auto& netOutput : networkOutputs) { auto outputData = netOutput.second; IE_ASSERT(outputData != nullptr); - - auto outputPrecision = outputData->getPrecision(); - - if (outputPrecision != ie::Precision::FP16 && - outputPrecision != ie::Precision::FP32) { - THROW_IE_EXCEPTION << "[PARAMETER_MISMATCH] Unsupported output precision: " << outputPrecision.name() << "!"; - } } } diff --git a/inference-engine/src/vpu/graph_transformer/src/frontend/pre_process.cpp b/inference-engine/src/vpu/graph_transformer/src/frontend/pre_process.cpp index 42cde670157654..af5a8a6159ec37 100644 --- a/inference-engine/src/vpu/graph_transformer/src/frontend/pre_process.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/frontend/pre_process.cpp @@ -114,7 +114,6 @@ void FrontEnd::addPreProcessStages(const Model::Ptr& model) { if (preProcess.getMeanVariant() != ie::NONE) { auto input = getVpuData(ieData); IE_ASSERT(input != nullptr); - IE_ASSERT(input->desc().type() == DataType::FP16); int numOfChannel = preProcess.getNumberOfChannels(); diff --git a/inference-engine/src/vpu/graph_transformer/src/hw/mx_stage.cpp b/inference-engine/src/vpu/graph_transformer/src/hw/mx_stage.cpp index dd8bb4e8d84dfe..fc9f332adf1902 100644 --- a/inference-engine/src/vpu/graph_transformer/src/hw/mx_stage.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/hw/mx_stage.cpp @@ -16,7 +16,10 @@ StagePtr MyriadXHwStage::cloneImpl() const { return std::make_shared(*this); } -void MyriadXHwStage::propagateScaleFactorsImpl(const SmallVector&, ScalePropagationStep) { +void MyriadXHwStage::propagateScaleFactorsImpl( + const SmallVector&, + ScalePropagationStep, + StageDataInfo&) { VPU_THROW_EXCEPTION << "Must never be called"; } @@ -44,81 +47,69 @@ StridesRequirement getHwStridesRequirement(const Stage& stage, const DataDesc& d } // namespace -void MyriadXHwStage::propagateDataOrderImpl() const { - IE_ASSERT(_inputEdges.size() >= 4); - IE_ASSERT(_outputEdges.size() >= 1); - +void MyriadXHwStage::propagateDataOrderImpl(StageDataInfo& orderInfo) { if (attrs().get("hwOpType") != HwOpType::POOL) { - auto weights = _inputEdges[1]->input(); - auto biases = _inputEdges[2]->input(); - auto scales = _inputEdges[3]->input(); + auto weights = inputEdge(1)->input(); + auto biases = inputEdge(2)->input(); + auto scales = inputEdge(3)->input(); IE_ASSERT(weights->usage() == DataUsage::Const); IE_ASSERT(biases->usage() == DataUsage::Const || biases->usage() == DataUsage::Fake); IE_ASSERT(scales->usage() == DataUsage::Const || scales->usage() == DataUsage::Fake); } - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); // TODO: support HCW if (input->desc().numDims() >= 3) { - _orderInfo.setInput(_inputEdges[0], input->desc().dimsOrder().createMovedDim(Dim::C, 2)); + orderInfo.setInput(inputEdge(0), input->desc().dimsOrder().createMovedDim(Dim::C, 2)); } else { IE_ASSERT(input->desc().dimsOrder() == DimsOrder::NC); } if (output->desc().numDims() >= 3) { - _orderInfo.setOutput(_outputEdges[0], output->desc().dimsOrder().createMovedDim(Dim::C, 2)); + orderInfo.setOutput(outputEdge(0), output->desc().dimsOrder().createMovedDim(Dim::C, 2)); } else { IE_ASSERT(output->desc().dimsOrder() == DimsOrder::NC); } } -void MyriadXHwStage::getDataStridesRequirementsImpl() const { - IE_ASSERT(_inputEdges.size() >= 4); - IE_ASSERT(_outputEdges.size() >= 1); - +void MyriadXHwStage::getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) { if (attrs().get("hwOpType") != HwOpType::POOL) { - auto weights = _inputEdges[1]->input(); - auto biases = _inputEdges[2]->input(); - auto scales = _inputEdges[3]->input(); + auto weights = inputEdge(1)->input(); + auto biases = inputEdge(2)->input(); + auto scales = inputEdge(3)->input(); IE_ASSERT(weights->usage() == DataUsage::Const); IE_ASSERT(biases->usage() == DataUsage::Const || biases->usage() == DataUsage::Fake); IE_ASSERT(scales->usage() == DataUsage::Const || scales->usage() == DataUsage::Fake); } - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); - _stridesInfo.setInput(_inputEdges[0], getHwStridesRequirement(handle_from_this(), input->desc())); - _stridesInfo.setOutput(_outputEdges[0], getHwStridesRequirement(handle_from_this(), output->desc())); + stridesInfo.setInput(inputEdge(0), getHwStridesRequirement(handle_from_this(), input->desc())); + stridesInfo.setOutput(outputEdge(0), getHwStridesRequirement(handle_from_this(), output->desc())); } void MyriadXHwStage::finalizeDataLayoutImpl() { } -void MyriadXHwStage::getBatchSupportInfoImpl() const { +void MyriadXHwStage::getBatchSupportInfoImpl(StageDataInfo& batchInfo) { if (attrs().get("hwOpType") != HwOpType::POOL) { - IE_ASSERT(_inputEdges.size() >= 4); - IE_ASSERT(_outputEdges.size() >= 1); - - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } } void MyriadXHwStage::finalCheckImpl() const { - IE_ASSERT(_inputEdges.size() >= 4); - IE_ASSERT(_outputEdges.size() >= 1); - - auto input = _inputEdges[0]->input(); - auto weights = _inputEdges[1]->input(); - auto biases = _inputEdges[2]->input(); - auto scales = _inputEdges[3]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto weights = inputEdge(1)->input(); + auto biases = inputEdge(2)->input(); + auto scales = inputEdge(3)->input(); + auto output = outputEdge(0)->output(); IE_ASSERT(input->memoryOffset() % 16 == 0); IE_ASSERT(weights->memoryOffset() % 16 == 0); @@ -189,9 +180,9 @@ void MyriadXHwStage::serializeParamsImpl(BlobSerializer& serializer) const { serializer.append(checked_cast(hwOpParams.reuseCoeff)); } - serializer.append(checked_cast(_injectedStageEdges.size())); - for (const auto& injectedStageEdge : _injectedStageEdges) { - injectedStageEdge->child()->serialize(serializer); + serializer.append(checked_cast(numInjectedStages())); + for (const auto& injectedStage : injectedStages()) { + injectedStage->serialize(serializer); } } @@ -200,7 +191,7 @@ void MyriadXHwStage::serializeDataImpl(BlobSerializer& serializer) const { uint32_t numBuffers = 0; - for (const auto& inEdge : _inputEdges) { + for (const auto& inEdge : inputEdges()) { if (inEdge->childEdge() != nullptr) continue; @@ -212,7 +203,7 @@ void MyriadXHwStage::serializeDataImpl(BlobSerializer& serializer) const { ++numBuffers; } - for (const auto& outEdge : _outputEdges) { + for (const auto& outEdge : outputEdges()) { if (outEdge->childEdge() != nullptr) continue; diff --git a/inference-engine/src/vpu/graph_transformer/src/hw/tiling.cpp b/inference-engine/src/vpu/graph_transformer/src/hw/tiling.cpp index f545c35cefa818..3aad4d241aa1d1 100644 --- a/inference-engine/src/vpu/graph_transformer/src/hw/tiling.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/hw/tiling.cpp @@ -184,8 +184,7 @@ std::tuple int inputSize, int kernelSize, int kernelStride, int padBefore, int padAfter, - int outputStartIndex, int outputEndIndex, - bool alignInputTile) { + int outputStartIndex, int outputEndIndex) { // Negative value encodes the padding int inputStartIndex = outputStartIndex * kernelStride - padBefore; int inputEndIndex = (outputEndIndex - 1) * kernelStride + kernelSize - padBefore; @@ -212,14 +211,6 @@ std::tuple inputLinesBefore -= kernelStride; } - if (alignInputTile) { - const int reqAlignment = 8; - while ((inputLinesBefore < inputStartIndex) && - (inputStartIndex - inputLinesBefore) % reqAlignment != 0) { - ++inputLinesBefore; - } - } - // Compute the junkOutputBefore junkOutputBefore = (inputLinesBefore + padBefore) / kernelStride; } @@ -266,14 +257,13 @@ int maximizeOutput( int kernelSize, int kernelStride, int padBefore, int padAfter, int outputStartIndex, int outputEndIndex, - bool alignInputTile, bool useCeil) { int outputSize = calcOutputSize(inputSize, kernelSize, kernelStride, padBefore, padAfter, useCeil); int _ = 0; int junkOutputBefore = 0, junkOutputAfter = 0; std::tie(_, _, _, _, _, _, junkOutputBefore, junkOutputAfter) = - inputTileForOutputTile(inputSize, kernelSize, kernelStride, padBefore, padAfter, outputStartIndex, outputEndIndex, alignInputTile); + inputTileForOutputTile(inputSize, kernelSize, kernelStride, padBefore, padAfter, outputStartIndex, outputEndIndex); int totalOutputSlice = junkOutputBefore + (outputEndIndex - outputStartIndex) + junkOutputAfter; @@ -286,7 +276,7 @@ int maximizeOutput( extraLines -= 1; std::tie(_, _, _, _, _, _, junkOutputBefore, junkOutputAfter) = - inputTileForOutputTile(inputSize, kernelSize, kernelStride, padBefore, padAfter, outputStartIndex, outputEndIndex + extraLines, alignInputTile); + inputTileForOutputTile(inputSize, kernelSize, kernelStride, padBefore, padAfter, outputStartIndex, outputEndIndex + extraLines); totalOutputSlice = junkOutputBefore + (outputEndIndex + extraLines - outputStartIndex) + junkOutputAfter; } @@ -301,7 +291,6 @@ SmallVector splitIntoPlaneTiles( int kernelSize, int kernelStride, int padBefore, int padAfter, int maxOutputSize, - bool alignInputTile, bool useCeil) { IE_ASSERT(inputSize > 0); IE_ASSERT(outputSize > 0); @@ -320,7 +309,6 @@ SmallVector splitIntoPlaneTiles( kernelSize, kernelStride, padBefore, padAfter, outputStartIndex, outputEndIndex, - alignInputTile, useCeil); if (newOutputEndIndex <= outputStartIndex) { return SmallVector(); @@ -333,7 +321,7 @@ SmallVector splitIntoPlaneTiles( inputLinesBefore, inputLinesAfter, outputStartIndex, outputEndIndex, junkOutputBefore, junkOutputAfter) = - inputTileForOutputTile(inputSize, kernelSize, kernelStride, padBefore, padAfter, outputStartIndex, newOutputEndIndex, alignInputTile); + inputTileForOutputTile(inputSize, kernelSize, kernelStride, padBefore, padAfter, outputStartIndex, newOutputEndIndex); IE_ASSERT(inputStartIndex >= 0); IE_ASSERT(inputEndIndex >= 0); @@ -376,64 +364,126 @@ SmallVector splitIntoPlaneTiles( } // -// HW Convolution tiling over output channels. +// Check HW-unit memory restrictions for tile. // namespace { -// Returns (status, cost). -std::tuple checkHwConvMode( - int inTileWidth, int inTileHeight, int inTileChannels, - int outTileChannels, +bool checkDimensions(int inTileWidth, int inTileHeight, int inTileChannels, int outTileChannels) { + return inTileWidth <= CNN_MAX_INPUT_WIDTH && + inTileHeight <= CNN_MAX_INPUT_HEIGHT && + inTileChannels <= CNN_MAX_INPUT_CHANNELS && + outTileChannels <= CNN_MAX_OUTPUT_CHANNELS; +} + +bool checkCoeffPerBlockConv(int kernelSizeX, int kernelSizeY, int noOfBlocks) { + auto coeffPerWord = CNN_COEFF_PER_WORD_VALUES[static_cast(CNN_COEFF_TYPE)]; + auto coeffSetSize = kernelSizeX * kernelSizeY; + auto coeffLPB = divUp(noOfBlocks * coeffSetSize, coeffPerWord); + + return coeffLPB <= CNN_MAX_COEFF_PER_BLOCK; +} + +bool checkLinesPerChanRestrictions( + int inTileWidth, int inTileHeight, + int kernelSizeY, int kernelStride, + int noOfBlocks, int chansPerBlock) { + const int bytesPerPixel = CNN_BYTES_PER_PIXEL[static_cast(CNN_DATA_TYPE)]; + const int bytesPerLine = alignVal(inTileWidth * bytesPerPixel, CMX_DATA_BYTE_WIDTH); + + const int linesPerChan = std::min(CNN_MAX_BYTES / (noOfBlocks * chansPerBlock * bytesPerLine), inTileHeight); + const int minLines = std::min(kernelSizeY + kernelStride + 2 + ((inTileWidth <= 8) ? 1 : 0), inTileHeight); + + return minLines <= linesPerChan; +} + +bool checkLinesPerChanRestrictionsPool( + int inTileWidth, int inTileHeight, + int kernelSizeY, + HwOpMode mode) { + const int sizeOfBlock = CNN_MAX_BYTES >> static_cast(mode); + const int bytesPerPixel = CNN_BYTES_PER_PIXEL[static_cast(CNN_DATA_TYPE)]; + const int pixelsPerCMXLine = CMX_DATA_BYTE_WIDTH / bytesPerPixel; + + const int localLineStride = (inTileWidth + (pixelsPerCMXLine - 1)) / pixelsPerCMXLine; + + const int chanPerBlock = 1; + const int availableBytesPerChan = sizeOfBlock / chanPerBlock; + const int bytesPerLine = localLineStride * pixelsPerCMXLine * bytesPerPixel; + + const int linesPerChan = std::min(availableBytesPerChan / bytesPerLine, inTileHeight); + + return linesPerChan >= kernelSizeY; +} + +bool checkHWRestrictions( + int inTileWidth, int inTileHeight, + int inTileChannels, int outTileChannels, int kernelSizeX, int kernelSizeY, int kernelStride, - HwOpMode mode) { - if (inTileWidth > CNN_MAX_INPUT_WIDTH || - inTileHeight > CNN_MAX_INPUT_HEIGHT || - inTileChannels > CNN_MAX_INPUT_CHANNELS || - outTileChannels > CNN_MAX_OUTPUT_CHANNELS) { - return std::make_tuple(false, 0); - } + HwOpMode mode, HwOpType type) { + const int chansPerBlock = 1 << static_cast(mode); + int noOfBlocks = divUp(inTileChannels, chansPerBlock); - auto noOfBlocks = 1 << static_cast(mode); - if (noOfBlocks > inTileChannels) { - return std::make_tuple(false, 0); - } + bool result = true; - auto inChansPerBlock = inTileChannels / noOfBlocks; - if (inChansPerBlock > CNN_MAX_CHANNELS_PER_BLOCK) { - return std::make_tuple(false, 0); - } + result &= checkDimensions(inTileWidth, inTileHeight, inTileChannels, outTileChannels); - auto coeffPerWord = CNN_COEFF_PER_WORD_VALUES[static_cast(CNN_COEFF_TYPE)]; - auto coeffSetSize = kernelSizeX * kernelSizeY; - auto coeffLPB = (inChansPerBlock * coeffSetSize + coeffPerWord - 1) / coeffPerWord; - if (coeffLPB > CNN_MAX_COEFF_PER_BLOCK) { - return std::make_tuple(false, 0); - } + if (type == HwOpType::POOL) { + // The number of blocks is 1 because the HW-unit does not use data from other blocks + // for calculating pooling. These blocks are loaded into CMX memory one by one. + noOfBlocks = 1; - auto bytesPerPixel = CNN_BYTES_PER_PIXEL[static_cast(CNN_DATA_TYPE)]; - auto pixelsPerCMXLine = 128 / (bytesPerPixel * 8); - auto localLineStride = (inTileWidth + (pixelsPerCMXLine - 1)) / pixelsPerCMXLine; - auto bytesPerLine = localLineStride * pixelsPerCMXLine * bytesPerPixel; - auto sizeOfBlock = CNN_MAX_BYTES >> static_cast(mode); - auto chanPerBlock = inTileChannels / noOfBlocks; - if (chanPerBlock == 0) { - return std::make_tuple(false, 0); + // TODO: verify the code on firmware side. + result &= checkLinesPerChanRestrictionsPool( + inTileWidth, inTileHeight, + kernelSizeY, + mode); + } else { + result &= checkCoeffPerBlockConv( + kernelSizeX, kernelSizeY, + noOfBlocks); } - auto availableBytesPerChan = sizeOfBlock / chanPerBlock; - auto linesPerChan = std::min(availableBytesPerChan / bytesPerLine, inTileHeight); - auto minLines = std::min(kernelSizeY / 1 + (kernelStride + 1) + 1 + ((inTileWidth <= 8) ? 1 : 0), inTileHeight); - if (minLines > linesPerChan) { - return std::make_tuple(false, 0); - } + result &= checkLinesPerChanRestrictions( + inTileWidth, inTileHeight, + kernelSizeY, kernelStride, + noOfBlocks, chansPerBlock); - return std::make_tuple(true, (inTileChannels / noOfBlocks) * kernelSizeX * kernelSizeY + CNN_MODES_COST[static_cast(mode)]); + return result; } } // namespace +bool checkPoolingHWRestrictions( + int inTileWidth, int inTileHeight, + int inTileChannels, int outTileChannels, + int kernelSizeX, int kernelSizeY, + int kernelStride) { + return checkHWRestrictions(inTileWidth, inTileHeight, + inTileChannels, outTileChannels, + kernelSizeX, kernelSizeY, + kernelStride, + HwOpMode::MODE_16_16, HwOpType::POOL); +} + +bool checkConvHWRestrictions( + int inTileWidth, int inTileHeight, + int inTileChannels, int outTileChannels, + int kernelSizeX, int kernelSizeY, + int kernelStride, + HwOpMode mode) { + return checkHWRestrictions(inTileWidth, inTileHeight, + inTileChannels, outTileChannels, + kernelSizeX, kernelSizeY, + kernelStride, + mode, HwOpType::CONV); +} + +// +// HW Convolution tiling over output channels. +// + HwConvTileInfo splitHwConvIntoOutChannelsTiles( int inTileWidth, int inTileHeight, int inTileChannels, int outTileChannels, @@ -452,26 +502,25 @@ HwConvTileInfo splitHwConvIntoOutChannelsTiles( Solution bestSol; for (auto mode : CNN_MODES) { - auto ramBlocks = 1 << static_cast(mode); + // inChansPerBlock * outChansPerBlock = 256 + auto inChansPerBlock = 1 << static_cast(mode); + auto outChansPerBlock = 256 / inChansPerBlock; - auto extendedInputDimC = alignVal(inTileChannels, ramBlocks); + auto extendedInputDimC = alignVal(inTileChannels, inChansPerBlock); auto extendedOutputDimC = alignVal(outTileChannels, 8); - auto outChansPerDescr = std::min(256 / ramBlocks, extendedOutputDimC); - - bool valid = false; - int descCost = 0; - std::tie(valid, descCost) = checkHwConvMode( - inTileWidth, inTileHeight, extendedInputDimC, - outChansPerDescr, - kernelSizeX, kernelSizeY, - kernelStride, - mode); + auto outChansPerDescr = std::min(outChansPerBlock, extendedOutputDimC); + bool valid = checkConvHWRestrictions( + inTileWidth, inTileHeight, inTileChannels, outChansPerDescr, + kernelSizeX, kernelSizeY, kernelStride, mode); if (!valid) { continue; } + int descCost = (extendedInputDimC / inChansPerBlock) * kernelSizeX * kernelSizeY + + CNN_MODES_COST[static_cast(mode)]; + auto numDescr = divUp(outTileChannels, outChansPerDescr); auto remOutChans = outTileChannels - (numDescr - 1) * outChansPerDescr; diff --git a/inference-engine/src/vpu/graph_transformer/src/model/data.cpp b/inference-engine/src/vpu/graph_transformer/src/model/data.cpp index df4c534704987e..9c2ee8a80459a9 100644 --- a/inference-engine/src/vpu/graph_transformer/src/model/data.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/model/data.cpp @@ -40,7 +40,8 @@ const void* CalculatedDataContent::getRaw() const { } size_t CalculatedDataContent::getTempBufSize(const SmallVector&) const { - return _desc.totalDimSize() * _desc.elemSize(); + return checked_cast(_desc.totalDimSize()) * + checked_cast(_desc.elemSize()); } namespace { @@ -51,35 +52,61 @@ class IeBlobContent final : public DataContent { protected: const void* getRaw() const override { - IE_ASSERT(_desc.type() == DataType::FP16); + if (_desc.type() == DataType::FP16) { + if (_blobFp16 == nullptr) { + _blobFp16 = getBlobFP16(_blob); + _blob.reset(); + } - if (_blobFp16 == nullptr) { - _blobFp16 = getBlobFP16(_blob); - _blob.reset(); - } + if (_repeat == 1) { + return _blobFp16->cbuffer(); + } else { + if (_tempFp16.empty()) { + VPU_PROFILE(IeBlobContent); - if (_repeat == 1) { - return _blobFp16->cbuffer(); - } else { - if (_temp.empty()) { - VPU_PROFILE(IeBlobContent); + IE_ASSERT(_desc.totalDimSize() % _repeat == 0); - IE_ASSERT(_desc.totalDimSize() % _repeat == 0); + auto origNumElems = _desc.totalDimSize() / _repeat; + IE_ASSERT(checked_cast(origNumElems) <= _blobFp16->size()); - auto origNumElems = _desc.totalDimSize() / _repeat; - IE_ASSERT(origNumElems <= _blobFp16->size()); + auto origPtr = _blobFp16->cbuffer().as(); + IE_ASSERT(origPtr != nullptr); - auto origPtr = _blobFp16->cbuffer().as(); - IE_ASSERT(origPtr != nullptr); + _tempFp16.resize(checked_cast(_desc.totalDimSize())); - _temp.resize(_desc.totalDimSize()); + ie::parallel_for(_repeat, [this, origPtr, origNumElems](int i) { + std::copy_n(origPtr, origNumElems, _tempFp16.data() + i * origNumElems); + }); + } - ie::parallel_for(_repeat, [this, origPtr, origNumElems](int i) { - std::copy_n(origPtr, origNumElems, _temp.data() + i * origNumElems); - }); + return _tempFp16.data(); } + } else if (_desc.type() == DataType::S32) { + if (_repeat == 1) { + return _blob->cbuffer(); + } else { + if (_tempS32.empty()) { + VPU_PROFILE(IeBlobContent); - return _temp.data(); + IE_ASSERT(_desc.totalDimSize() % _repeat == 0); + + auto origNumElems = _desc.totalDimSize() / _repeat; + IE_ASSERT(checked_cast(origNumElems) <= _blob->size()); + + auto origPtr = _blob->cbuffer().as(); + IE_ASSERT(origPtr != nullptr); + + _tempS32.resize(checked_cast(_desc.totalDimSize())); + + ie::parallel_for(_repeat, [this, origPtr, origNumElems](int i) { + std::copy_n(origPtr, origNumElems, _tempS32.data() + i * origNumElems); + }); + } + + return _tempS32.data(); + } + } else { + VPU_THROW_EXCEPTION << "Unsupported data type " << _desc.type(); } } @@ -88,7 +115,8 @@ class IeBlobContent final : public DataContent { int _repeat = 0; mutable ie::Blob::Ptr _blobFp16; - mutable std::vector _temp; + mutable std::vector _tempFp16; + mutable std::vector _tempS32; }; } // namespace @@ -110,12 +138,12 @@ class ReplicatedContent final : public CalculatedDataContent { protected: size_t getTempBufSize(const SmallVector& baseContents) const override { if (baseContents.empty()) { - return _count * sizeof(fp16_t); + return checked_cast(_count) * sizeof(fp16_t); } else { IE_ASSERT(baseContents.size() == 1); IE_ASSERT(_desc.totalDimSize() % _count == 0); - return _desc.totalDimSize() * sizeof(fp16_t); + return checked_cast(_desc.totalDimSize()) * sizeof(fp16_t); } } @@ -265,21 +293,26 @@ void DataNode::updateRequiredStrides(const StridesRequirement& newReqs) { auto prevReqs = _requiredStrides; StridesRequirement mergedReqs; - for (int i = 0; i < _desc.numDims(); ++i) { - auto prevReq = prevReqs.get(i); - auto newReq = newReqs.get(i); + const auto& fixedRequirements = prevReqs.fixedStrides().empty() ? newReqs : prevReqs; + if (!fixedRequirements.fixedStrides().empty()) { + mergedReqs = fixedRequirements; + } else { + for (int i = 0; i < _desc.numDims(); ++i) { + auto prevReq = prevReqs.get(i); + auto newReq = newReqs.get(i); - if (prevReq == DimStride::Any && - newReq == DimStride::Any) { - continue; - } + if (prevReq == DimStride::Any && + newReq == DimStride::Any) { + continue; + } - // In case if both requirements are defined, use `prevReq`. - // We'll check that both requirements are satisfied at the end. - if (prevReq != DimStride::Any) { - mergedReqs.add(i, prevReq); - } else { - mergedReqs.add(i, newReq); + // In case if both requirements are defined, use `prevReq`. + // We'll check that both requirements are satisfied at the end. + if (prevReq != DimStride::Any) { + mergedReqs.add(i, prevReq); + } else { + mergedReqs.add(i, newReq); + } } } @@ -345,8 +378,8 @@ void DataNode::serializeNewBuffer( auto origOrder = _desc.dimsOrder(); auto origPerm = origOrder.toPermutation(); - int origPermInd = 0; - for (int i = 0; i < newPerm.size(); i++) { + size_t origPermInd = 0; + for (size_t i = 0; i < newPerm.size(); i++) { auto d = newPerm[i]; if (origPermInd < origPerm.size() && origPerm[origPermInd] == d) { @@ -383,7 +416,7 @@ void rebaseOrderToOne(DimsOrder& ord, DimValues& dims, DimValues& strides) { DimValues newDims; DimValues newStrides; - for (int i = 0; i < perm.size(); ++i) { + for (size_t i = 0; i < perm.size(); ++i) { auto oldDim = perm[i]; auto newDim = static_cast(static_cast(oldDim) - minDim); @@ -403,7 +436,7 @@ void DataNode::serializeOldBuffer( const Stage& stage, BlobSerializer& serializer, DimsOrder newOrder, - const EnumMap>& dimsReloc) { + const EnumMap& dimsReloc) { const int OLD_FORMAT_NUM_DIMS = 3; auto newDims = _desc.dims(); @@ -435,7 +468,7 @@ void DataNode::serializeOldBuffer( EnumSet usedOrigDims; int prevOrigDimInd = -1; - for (int i = 0; i < newPerm.size(); ++i) { + for (size_t i = 0; i < newPerm.size(); ++i) { auto newDim = newPerm[i]; int newDimVal = 1; @@ -451,7 +484,7 @@ void DataNode::serializeOldBuffer( auto origDimsToReloc = it->second; IE_ASSERT(!origDimsToReloc.empty()); - for (int j = 0; j < origDimsToReloc.size(); ++j) { + for (size_t j = 0; j < origDimsToReloc.size(); ++j) { auto origDim = origDimsToReloc[j]; auto origDimInd = origIndeces[origDim]; @@ -462,7 +495,7 @@ void DataNode::serializeOldBuffer( usedOrigDims.insert(origDim); if (j > 0 && origDims[origDim] > 1) { - IE_ASSERT(checkStride(origStrides, _desc, origDimInd, DimStride::Compact)); + IE_ASSERT(checkStride(origStrides, _desc, origDimInd, StridesRequirement::compact())); } newDimVal *= origDims[origDim]; @@ -498,7 +531,7 @@ void DataNode::serializeOldBuffer( IE_ASSERT(maxDimDigit >= 0); if (newPerm.size() < OLD_FORMAT_NUM_DIMS) { - for (int i = newPerm.size(); i < OLD_FORMAT_NUM_DIMS; i++) { + for (size_t i = newPerm.size(); i < OLD_FORMAT_NUM_DIMS; i++) { auto lastDim = newPerm.back(); auto newLastDim = static_cast(++maxDimDigit); @@ -512,7 +545,7 @@ void DataNode::serializeOldBuffer( } if (newPerm.size() > OLD_FORMAT_NUM_DIMS) { - for (int i = OLD_FORMAT_NUM_DIMS; i < newPerm.size(); i++) { + for (size_t i = OLD_FORMAT_NUM_DIMS; i < newPerm.size(); i++) { IE_ASSERT(newDims[newPerm[i]] == 1); newDims.erase(newPerm[i]); newStrides.erase(newPerm[i]); diff --git a/inference-engine/src/vpu/graph_transformer/src/model/data_desc.cpp b/inference-engine/src/vpu/graph_transformer/src/model/data_desc.cpp index d97efe269d2c5b..9001946c35a9e7 100644 --- a/inference-engine/src/vpu/graph_transformer/src/model/data_desc.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/model/data_desc.cpp @@ -44,6 +44,8 @@ DimsOrder DimsOrder::HCW = DimsOrder::fromCode(0x231); DimsOrder DimsOrder::NCHW = DimsOrder::fromCode(0x4321); DimsOrder DimsOrder::NHWC = DimsOrder::fromCode(0x4213); DimsOrder DimsOrder::NHCW = DimsOrder::fromCode(0x4231); +DimsOrder DimsOrder::NCDHW = DimsOrder::fromCode(0x43521); +DimsOrder DimsOrder::NDHWC = DimsOrder::fromCode(0x45213); namespace { @@ -109,12 +111,18 @@ DimsOrder DimsOrder::fromNumDims(int numDims) { return DimsOrder::C; } else if (numDims == 2) { return DimsOrder::NC; + } else if (numDims == 3) { + return DimsOrder::CHW; + } else if (numDims == 4) { + return DimsOrder::NCHW; + } else if (numDims == 5) { + return DimsOrder::NCDHW; } else { return DimsOrder::fromCode(maskOrder(FULL_ORDER_DEFAULT, numDims)); } } -DimsOrder DimsOrder::fromPermutation(const SmallVector& perm) { +DimsOrder DimsOrder::fromPermutation(const DimVector& perm) { StorageOrder64 code = 0; for (int sh = 0, i = 0; i < perm.size(); i++, sh += 4) { @@ -124,6 +132,20 @@ DimsOrder DimsOrder::fromPermutation(const SmallVector& perm) return DimsOrder::fromCode(code); } +DimsOrder DimsOrder::fromLayout(ie::Layout const& layout) { + switch (layout) { + case ie::Layout::C : return DimsOrder::C; + case ie::Layout::NC : return DimsOrder::NC; + case ie::Layout::CHW : return DimsOrder::CHW; + case ie::Layout::NCHW : return DimsOrder::NCHW; + case ie::Layout::NHWC : return DimsOrder::NHWC; + case ie::Layout::NCDHW : return DimsOrder::NCDHW; + case ie::Layout::NDHWC : return DimsOrder::NDHWC; + default: + VPU_THROW_EXCEPTION << "Unsupported layout " << layout; + } +} + int DimsOrder::numDims() const { int out = 0; @@ -262,7 +284,8 @@ void printTo(std::ostream& os, DimsOrder order) { {1, 'W'}, {2, 'H'}, {3, 'C'}, - {4, 'N'} + {4, 'N'}, + {5, 'D'} }); auto code = order.code(); @@ -288,6 +311,17 @@ void printTo(std::ostream& os, DimsOrder order) { } } +// +// Dim +// + +int dimToIeInd(vpu::Dim const& dim, int numDims) { + IE_ASSERT(1 <= numDims && numDims <= 8); + auto dimsOrder = DimsOrder::fromNumDims(numDims); + int dimInd = dimsOrder.dimInd(dim); + return (numDims - 1) - dimInd; +} + // // DataDesc // @@ -297,22 +331,7 @@ DataDesc::DataDesc(const ie::TensorDesc& ieDesc) { // Parse precision // - switch (ieDesc.getPrecision()) { - case ie::Precision::U8: - _type = DataType::U8; - break; - case ie::Precision::I8: - _type = DataType::I8; - break; - case ie::Precision::FP16: - _type = DataType::FP16; - break; - case ie::Precision::FP32: - _type = DataType::FP32; - break; - default: - VPU_THROW_EXCEPTION << "Unsupported precision " << ieDesc.getPrecision().name(); - } + _type = fromIEPrecision(ieDesc.getPrecision()); // // Parse dimensions and layout @@ -342,10 +361,14 @@ int DataDesc::elemSize() const { switch (_type) { case DataType::U8: return sizeof(uint8_t); + case DataType::I8: + return sizeof(int8_t); case DataType::FP16: return sizeof(fp16_t); case DataType::FP32: return sizeof(float); + case DataType::S32: + return sizeof(int32_t); default: VPU_THROW_EXCEPTION << "Unknown data type " << _type; } @@ -372,6 +395,62 @@ void DataDesc::reorder(DimsOrder dimsOrder) { _dimsOrder = dimsOrder; } +ie::TensorDesc DataDesc::toTensorDesc() const { + ie::TensorDesc desc; + + switch (this->type()) { + case DataType::FP16: + desc.setPrecision(ie::Precision::FP16); + break; + case DataType::FP32: + desc.setPrecision(ie::Precision::FP32); + break; + case DataType::I8: + desc.setPrecision(ie::Precision::I8); + break; + case DataType::U8: + desc.setPrecision(ie::Precision::U8); + break; + case DataType::S32: + desc.setPrecision(ie::Precision::I32); + break; + default: + desc.setPrecision(ie::Precision::UNSPECIFIED); + } + + ie::SizeVector dims{}; + + DataDesc descCopy = *this; + descCopy.reorder(DimsOrder::fromNumDims(this->numDims())); + auto perm = descCopy.dimsOrder().toPermutation(); + std::reverse(perm.begin(), perm.end()); + for (auto &p : perm) { + dims.push_back(descCopy.dim(p)); + } + + desc.setDims(dims); + + if (DimsOrder::C == this->dimsOrder()) { + desc.setLayout(ie::Layout::C); + } else if (DimsOrder::NC == this->dimsOrder()) { + desc.setLayout(ie::Layout::NC); + } else if (DimsOrder::CHW == this->dimsOrder()) { + desc.setLayout(ie::Layout::CHW); + } else if (DimsOrder::NCHW == this->dimsOrder()) { + desc.setLayout(ie::Layout::NCHW); + } else if (DimsOrder::NHWC == this->dimsOrder()) { + desc.setLayout(ie::Layout::NHWC); + } else if (DimsOrder::NCDHW == this->dimsOrder()) { + desc.setLayout(ie::Layout::NCDHW); + } else if (DimsOrder::NDHWC == this->dimsOrder()) { + desc.setLayout(ie::Layout::NDHWC); + } else { + desc.setLayout(ie::Layout::BLOCKED); + } + + return desc; +} + void printTo(std::ostream& os, const DataDesc& desc) { os << "[" << std::endl; @@ -409,6 +488,35 @@ StridesRequirement StridesRequirement::compact() { return reqs; } +StridesRequirement StridesRequirement::fixed(const std::vector& strides, const DataDesc& desc) { + StridesRequirement reqs; + + const auto dims = desc.dims(); + const auto dimsOrder = desc.dimsOrder(); + const auto dimOrderVec = dimsOrder.toPermutation(); + auto setStride = [&] (Dim d, int val) { + IE_ASSERT(dimsOrder.hasDim(d)); + + auto perm = dimsOrder.toPermutation(); + auto idx = dimsOrder.dimInd(d); + + auto minStrideVal = idx == 0 ? desc.elemSize() : reqs._fixedStrides[perm[idx - 1]] * dims[perm[idx - 1]]; + IE_ASSERT(val >= minStrideVal); + + reqs._fixedStrides.set(d, val); + }; + + for (const auto& dim : dimOrderVec) { + const auto idx = dimToIeInd(dim, dims.size()); + setStride(dim, strides[idx]); + } + + for (int i = 0; i < MAX_DIMS_64; ++i) { + reqs.add(i, DimStride::Fixed); + } + return reqs; +} + void printTo(std::ostream& os, const StridesRequirement& reqs) { os << "[" << std::endl; @@ -457,12 +565,16 @@ DimValues calcStrides(const DataDesc& desc, const StridesRequirement& reqs) { auto perm = desc.dimsOrder().toPermutation(); IE_ASSERT(!perm.empty()); - strides.set(perm[0], desc.elemSize()); - strides.set(perm[0], applyStrideRequirement(strides[perm[0]], 0, reqs)); + strides = reqs.fixedStrides(); + + if (strides.empty()) { + strides.set(perm[0], desc.elemSize()); + strides.set(perm[0], applyStrideRequirement(strides[perm[0]], 0, reqs)); - for (int i = 1; i < perm.size(); i++) { - strides.set(perm[i], strides[perm[i - 1]] * desc.dim(perm[i - 1])); - strides.set(perm[i], applyStrideRequirement(strides[perm[i]], i, reqs)); + for (std::size_t i = 1; i < perm.size(); i++) { + strides.set(perm[i], strides[perm[i - 1]] * desc.dim(perm[i - 1])); + strides.set(perm[i], applyStrideRequirement(strides[perm[i]], i, reqs)); + } } return strides; @@ -472,7 +584,8 @@ bool checkStride( const DimValues& strides, const DataDesc& desc, int ind, - DimStride req) { + const StridesRequirement& reqs) { + const auto req = reqs.get(ind); if (req == DimStride::Any) { return true; } @@ -496,6 +609,10 @@ bool checkStride( if (strideVal % STRIDE_ALIGNMENT != 0) { return false; } + } else if (req == DimStride::Fixed) { + if (strideVal != reqs.getFixedStride(perm[ind])) { + return false; + } } else { VPU_THROW_EXCEPTION << "Unsupported stride requirement : " << req; } @@ -511,7 +628,7 @@ bool checkStrides( IE_ASSERT(!perm.empty()); for (int i = 0; i < perm.size(); i++) { - if (!checkStride(strides, desc, i, reqs.get(i))) { + if (!checkStride(strides, desc, i, reqs)) { return false; } } @@ -524,4 +641,15 @@ int calcTotalByteSize(const DataDesc& desc, const DimValues& strides) { return strides[perm.back()] * desc.dim(perm.back()); } +DataType fromIEPrecision(const InferenceEngine::Precision& precision) { + switch (precision) { + case InferenceEngine::Precision::U8: return DataType::U8; + case InferenceEngine::Precision::I8: return DataType::I8; + case InferenceEngine::Precision::I32: return DataType::S32; + case InferenceEngine::Precision::FP16: return DataType::FP16; + case InferenceEngine::Precision::FP32: return DataType::FP32; + default: VPU_THROW_EXCEPTION << precision << " isn't supported"; + } +} + } // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/model/model.cpp b/inference-engine/src/vpu/graph_transformer/src/model/model.cpp index 9c65d7bb004a7e..52541b31082edc 100644 --- a/inference-engine/src/vpu/graph_transformer/src/model/model.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/model/model.cpp @@ -186,8 +186,8 @@ Data Model::duplicateData( } Stage Model::duplicateStage( - const std::string& name, const Stage& origStage, + const std::string& postfix, const DataVector& inputs, const DataVector& outputs) { // @@ -230,7 +230,7 @@ Stage Model::duplicateStage( auto stage = origStage->cloneImpl(); - stage->_name = name; + stage->_name = origStage->name() + postfix; stage->_type = origStage->_type; stage->_origLayer = origStage->_origLayer; stage->_model = handle_from_this(); @@ -1305,7 +1305,7 @@ SharedAllocation Model::connectDatasImpl( // if (connectionStage->_type == StageType::Concat || - connectionStage->_type == StageType::Broadcast) { + connectionStage->_type == StageType::Expand) { IE_ASSERT(producer == child); IE_ASSERT(consumer == parent); } else if (connectionStage->_type == StageType::Split || @@ -1415,7 +1415,7 @@ SharedAllocation Model::connectDatasImpl( return edge; } -void Model::disconnectStageDatas(const Stage& stage) { +void Model::disconnectStage(const Stage& stage) { // // Check that objects belong to the same Model. // @@ -1507,7 +1507,7 @@ void Model::removeStage(const Stage& stage) { _resetStageOrder = true;; - disconnectStageDatas(stage); + disconnectStage(stage); _initialStages.erase(stage); @@ -1515,7 +1515,7 @@ void Model::removeStage(const Stage& stage) { _stagePtrList.erase(stage->_ptrPosInModel); } -void Model::cleanUpDatas() { +void Model::cleanUp() { bool needAllocatorPreprocess = false; for (const auto& data : datas()) { diff --git a/inference-engine/src/vpu/graph_transformer/src/model/stage.cpp b/inference-engine/src/vpu/graph_transformer/src/model/stage.cpp index 98a705967abb49..21d8fff0b63cbd 100644 --- a/inference-engine/src/vpu/graph_transformer/src/model/stage.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/model/stage.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -66,7 +67,7 @@ const StageDataInfo& StageNode::propagateScaleFactors( // _scaleInfo.init(_inputEdges.size(), _outputEdges.size()); - propagateScaleFactorsImpl(inputScales, step); + propagateScaleFactorsImpl(inputScales, step, _scaleInfo); // // Check that implementation returned valid map. @@ -81,13 +82,13 @@ const StageDataInfo& StageNode::propagateScaleFactors( return _scaleInfo; } -const StageDataInfo& StageNode::propagateDataOrder() const { +const StageDataInfo& StageNode::propagateDataOrder() { // // Get result from Stage implementation. // _orderInfo.init(_inputEdges.size(), _outputEdges.size()); - propagateDataOrderImpl(); + propagateDataOrderImpl(_orderInfo); // // Merge with the results from injected Stages. @@ -114,13 +115,13 @@ const StageDataInfo& StageNode::propagateDataOrder() const { return _orderInfo; } -const StageDataInfo& StageNode::getDataStridesRequirements() const { +const StageDataInfo& StageNode::getDataStridesRequirements() { // // Get result from Stage implementation. // _stridesInfo.init(_inputEdges.size(), _outputEdges.size()); - getDataStridesRequirementsImpl(); + getDataStridesRequirementsImpl(_stridesInfo); // // Merge with the results from injected Stages. @@ -158,13 +159,13 @@ void StageNode::finalizeDataLayout() { finalizeDataLayoutImpl(); } -const StageDataInfo& StageNode::getBatchSupportInfo() const { +const StageDataInfo& StageNode::getBatchSupportInfo() { // // Get result from Stage implementation. // _batchInfo.init(_inputEdges.size(), _outputEdges.size()); - getBatchSupportInfoImpl(); + getBatchSupportInfoImpl(_batchInfo); // // Check that implemenation returned valid map. @@ -262,11 +263,35 @@ StageSHAVEsRequirements StageNode::getSHAVEsRequirements() const { return reqs; } +void StageNode::initialCheck() const { + try { + initialCheckImpl(); + } catch (const InferenceEngine::details::InferenceEngineException& exception) { + VPU_THROW_EXCEPTION << name() << " of type " << type() << ": " << exception.what(); + } + + for (const auto& injectedStageEdge : injectedStageEdges()) { + try { + injectedStageEdge->child()->initialCheck(); + } catch (const InferenceEngine::details::InferenceEngineException& exception) { + VPU_THROW_EXCEPTION << name() << " of type " << type() << ": " << exception.what(); + } + } +} + void StageNode::finalCheck() const { - finalCheckImpl(); + try { + finalCheckImpl(); + } catch (const InferenceEngine::details::InferenceEngineException& exception) { + VPU_THROW_EXCEPTION << name() << " of type " << type() << ": " << exception.what(); + } for (const auto& injectedStageEdge : injectedStageEdges()) { - injectedStageEdge->child()->finalCheck(); + try { + injectedStageEdge->child()->finalCheck(); + } catch (const InferenceEngine::details::InferenceEngineException& exception) { + VPU_THROW_EXCEPTION << name() << " of type " << type() << ": " << exception.what(); + } } } @@ -296,16 +321,17 @@ void StageNode::serialize(BlobSerializer& serializer) const { void StageNode::propagateScaleFactorsImpl( const SmallVector&, - ScalePropagationStep) { + ScalePropagationStep, + StageDataInfo& scaleInfo) { // // Default implementation assumes no scaling support. // for (const auto& inEdge : _inputEdges) { - _scaleInfo.setInput(inEdge, 1.0f); + scaleInfo.setInput(inEdge, 1.0f); } for (const auto& outEdge : _outputEdges) { - _scaleInfo.setOutput(outEdge, 1.0f); + scaleInfo.setOutput(outEdge, 1.0f); } } @@ -321,4 +347,49 @@ void printTo(std::ostream& os, const Stage& stage) { os << (stage == nullptr ? "" : stage->name()); } +void assertAllInputsOutputsTypes(const StageNode* stage, + const DataType& expectedInputsType, + const DataType& expectedOutputsType) { + auto assertTypes = [](const DataType& expectedType, + const std::vector& datas, const std::string& token) { + for (decltype(datas.size()) idx = 0; idx < datas.size(); ++idx) { + if (datas[idx]->usage() == DataUsage::Fake) + continue; + const auto& actualType = datas[idx]->desc().type(); + + IE_ASSERT(actualType == expectedType) + << ": " << token << "#" << std::to_string(idx) << " of type " << actualType << " given, but one of " + << expectedType << " is expected"; + } + }; + + assertTypes(expectedInputsType, toVector(stage->inputs()), "input"); + assertTypes(expectedOutputsType, toVector(stage->outputs()), "output"); +} + + +void assertInputsOutputsTypes(const StageNode* stage, + const std::vector>& expectedInputsTypes, + const std::vector>& expectedOutputsTypes) { + auto assertTypes = [](const std::vector>& expectedTypes, + const std::vector& datas, const std::string& token) { + IE_ASSERT(expectedTypes.size() == datas.size()) + << ": " << datas.size() << " " << token << "s given, but " << expectedTypes.size() << " is expected"; + + for (decltype(datas.size()) idx = 0; idx < datas.size(); ++idx) { + if (datas[idx]->usage() == DataUsage::Fake) + continue; + const auto& possibleTypes = expectedTypes[idx]; + const auto& actualType = datas[idx]->desc().type(); + + IE_ASSERT(possibleTypes.find(actualType) != possibleTypes.end()) + << ": " << token << "#" << std::to_string(idx) << " of type " << actualType << " given, but one of " + << toString(possibleTypes) << " is expected"; + } + }; + + assertTypes(expectedInputsTypes, toVector(stage->inputs()), "input"); + assertTypes(expectedOutputsTypes, toVector(stage->outputs()), "output"); +} + } // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/parsed_config.cpp b/inference-engine/src/vpu/graph_transformer/src/parsed_config.cpp index 437d0138ce90c7..ca774be0d11420 100644 --- a/inference-engine/src/vpu/graph_transformer/src/parsed_config.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/parsed_config.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include
#include @@ -38,39 +39,50 @@ void check_input(const I &input, const T &options, const C &check) { } } -} // namespace +void checkStridesConfig(std::string configStrides) { + try { + configStrides.pop_back(); -ParsedConfig::ParsedConfig(ConfigMode configMode): _mode(configMode) { - _log = std::make_shared("Config", LogLevel::Warning, consoleOutput()); -} + auto tensorStrides = InferenceEngine::details::split(configStrides, "],"); -void ParsedConfig::checkSupportedValues( - const std::unordered_map> &supported, - const std::map &config) const { + for (const auto& stride : tensorStrides) { + auto pair = InferenceEngine::details::split(stride, "["); + auto message = "Invalid config value '" + stride + "' for VPU_TENSOR_STRIDES, does not match the pattern: tensor_name[strides]"; + IE_ASSERT(pair.size() == 2) << message; - auto contains = [](const std::unordered_set &supported, const std::string &option) { - return supported.find(option) != supported.end(); - }; + auto strideValues = InferenceEngine::details::split(pair.at(1), ","); + for (auto entry : strideValues) { + std::stoi(entry); + } + } + } + catch(const std::out_of_range& e) { + auto message = "Invalid config value for VPU_TENSOR_STRIDES, values out of range of unsigned int"; + THROW_IE_EXCEPTION << message; + } - check_input(config, supported, contains); + catch(const std::invalid_argument& e) { + auto message = "Invalid config value for VPU_TENSOR_STRIDES, can't cast values to unsigned int"; + THROW_IE_EXCEPTION << message; + } } +} // namespace + +ParsedConfig::ParsedConfig(ConfigMode configMode): ParsedConfigBase(configMode) {} + void ParsedConfig::checkInvalidValues(const std::map &config) const { + ParsedConfigBase::checkInvalidValues(config); + const std::unordered_map> supported_values = { - { CONFIG_KEY(LOG_LEVEL), - { CONFIG_VALUE(LOG_NONE), CONFIG_VALUE(LOG_WARNING), CONFIG_VALUE(LOG_INFO), CONFIG_VALUE(LOG_DEBUG) }}, - { VPU_CONFIG_KEY(LOG_LEVEL), - { CONFIG_VALUE(LOG_NONE), CONFIG_VALUE(LOG_WARNING), CONFIG_VALUE(LOG_INFO), CONFIG_VALUE(LOG_DEBUG) }}, { VPU_CONFIG_KEY(COMPUTE_LAYOUT), - { VPU_CONFIG_VALUE(AUTO), VPU_CONFIG_VALUE(NCHW), VPU_CONFIG_VALUE(NHWC) }}, + { VPU_CONFIG_VALUE(AUTO), VPU_CONFIG_VALUE(NCHW), VPU_CONFIG_VALUE(NHWC), VPU_CONFIG_VALUE(NCDHW), VPU_CONFIG_VALUE(NDHWC) }}, { VPU_CONFIG_KEY(COPY_OPTIMIZATION), { CONFIG_VALUE(YES), CONFIG_VALUE(NO) }}, { VPU_CONFIG_KEY(PACK_DATA_IN_CMX), { CONFIG_VALUE(YES), CONFIG_VALUE(NO) }}, { VPU_CONFIG_KEY(IGNORE_UNKNOWN_LAYERS), { CONFIG_VALUE(YES), CONFIG_VALUE(NO) }}, { CONFIG_KEY(PERF_COUNT), { CONFIG_VALUE(YES), CONFIG_VALUE(NO) }}, - { CONFIG_KEY(EXCLUSIVE_ASYNC_REQUESTS), { CONFIG_VALUE(YES), CONFIG_VALUE(NO) }}, { VPU_CONFIG_KEY(HW_STAGES_OPTIMIZATION), { CONFIG_VALUE(YES), CONFIG_VALUE(NO) }}, { VPU_CONFIG_KEY(HW_ADAPTIVE_MODE), { CONFIG_VALUE(YES), CONFIG_VALUE(NO) }}, - { VPU_CONFIG_KEY(ALLOW_FP32_MODELS), { CONFIG_VALUE(YES), CONFIG_VALUE(NO) }}, { VPU_CONFIG_KEY(HW_INJECT_STAGES), { CONFIG_VALUE(YES), CONFIG_VALUE(NO) }}, { VPU_CONFIG_KEY(HW_POOL_CONV_MERGE), { CONFIG_VALUE(YES), CONFIG_VALUE(NO) }}, { VPU_CONFIG_KEY(PERF_REPORT_MODE), @@ -125,34 +137,21 @@ IE_SUPPRESS_DEPRECATED_END if ((number_of_shaves == config.end()) && (number_of_CMX != config.end())) { THROW_IE_EXCEPTION << "You should set both option for resource management: VPU_NUMBER_OF_CMX_SLICES and VPU_NUMBER_OF_SHAVES"; } -} -void ParsedConfig::checkUnknownOptions(const std::map &config) const { - auto knownOptions = getKnownOptions(); - for (auto &&entry : config) { - if (knownOptions.find(entry.first) == knownOptions.end()) { - THROW_IE_EXCEPTION << NOT_FOUND_str << entry.first << " key is not supported for VPU"; - } - } -} + auto tensor_strides = config.find(VPU_CONFIG_KEY(TENSOR_STRIDES)); -void ParsedConfig::checkOptionsAccordingToMode(const std::map &config) const { - auto compileOptions = getCompileOptions(); - for (auto &&entry : config) { - std::stringstream errorMsgStream; - if (compileOptions.find(entry.first) != compileOptions.end() && _mode == ConfigMode::RUNTIME_MODE) { - _log->warning("%s option will be ignored. Seems you are using compiled graph", entry.first); - } + if (tensor_strides != config.end()) { + checkStridesConfig(tensor_strides->second); } } std::unordered_set ParsedConfig::getCompileOptions() const { IE_SUPPRESS_DEPRECATED_START return { + VPU_CONFIG_KEY(TENSOR_STRIDES), VPU_CONFIG_KEY(COMPUTE_LAYOUT), VPU_CONFIG_KEY(NETWORK_CONFIG), VPU_CONFIG_KEY(HW_ADAPTIVE_MODE), - VPU_CONFIG_KEY(ALLOW_FP32_MODELS), VPU_CONFIG_KEY(COPY_OPTIMIZATION), VPU_CONFIG_KEY(PACK_DATA_IN_CMX), VPU_CONFIG_KEY(DETECT_NETWORK_BATCH), @@ -176,15 +175,17 @@ IE_SUPPRESS_DEPRECATED_END } std::unordered_set ParsedConfig::getRuntimeOptions() const { - return { - CONFIG_KEY(EXCLUSIVE_ASYNC_REQUESTS), - CONFIG_KEY(LOG_LEVEL), - VPU_CONFIG_KEY(LOG_LEVEL), + auto runtimeOptions = ParsedConfigBase::getRuntimeOptions(); + + std::unordered_set specificOptions = { CONFIG_KEY(PERF_COUNT), VPU_CONFIG_KEY(PRINT_RECEIVE_TENSOR_TIME), CONFIG_KEY(CONFIG_FILE), - VPU_CONFIG_KEY(PERF_REPORT_MODE), - }; + VPU_CONFIG_KEY(PERF_REPORT_MODE) }; + + runtimeOptions.insert(specificOptions.begin(), specificOptions.end()); + + return runtimeOptions; } std::unordered_set ParsedConfig::getKnownOptions() const { @@ -203,10 +204,14 @@ std::map ParsedConfig::getDefaultConfig() const { } void ParsedConfig::configure(const std::map &config) { + ParsedConfigBase::configure(config); + static const std::unordered_map layouts { { VPU_CONFIG_VALUE(AUTO), ComputeLayout::AUTO }, { VPU_CONFIG_VALUE(NCHW), ComputeLayout::NCHW }, { VPU_CONFIG_VALUE(NHWC), ComputeLayout::NHWC }, + { VPU_CONFIG_VALUE(NCDHW), ComputeLayout::NCDHW }, + { VPU_CONFIG_VALUE(NDHWC), ComputeLayout::NDHWC } }; setOption(compileConfig.forceLayout, layouts, config, VPU_CONFIG_KEY(COMPUTE_LAYOUT)); @@ -222,7 +227,6 @@ void ParsedConfig::configure(const std::map &config) { setOption(compileConfig.ignoreUnknownLayers, switches, config, VPU_CONFIG_KEY(IGNORE_UNKNOWN_LAYERS)); setOption(compileConfig.hwOptimization, switches, config, VPU_CONFIG_KEY(HW_STAGES_OPTIMIZATION)); setOption(compileConfig.hwAdaptiveMode, switches, config, VPU_CONFIG_KEY(HW_ADAPTIVE_MODE)); - setOption(compileConfig.allowFP32Models, switches, config, VPU_CONFIG_KEY(ALLOW_FP32_MODELS)); setOption(compileConfig.injectSwOps, switches, config, VPU_CONFIG_KEY(HW_INJECT_STAGES)); setOption(compileConfig.mergeHwPoolToConv, switches, config, VPU_CONFIG_KEY(HW_POOL_CONV_MERGE)); setOption(compileConfig.ignoreIRStatistic, switches, config, VPU_CONFIG_KEY(IGNORE_IR_STATISTIC)); @@ -245,19 +249,33 @@ void ParsedConfig::configure(const std::map &config) { setOption(compileConfig.numCMXSlices, config, VPU_CONFIG_KEY(NUMBER_OF_CMX_SLICES), [](const std::string &src) { return std::stoi(src); }); - setOption(exclusiveAsyncRequests, switches, config, CONFIG_KEY(EXCLUSIVE_ASYNC_REQUESTS)); + setOption(compileConfig.ioStrides, config, VPU_CONFIG_KEY(TENSOR_STRIDES), + [](const std::string &src) { + auto configStrides = src; + configStrides.pop_back(); + + auto inputs = InferenceEngine::details::split(configStrides, "],"); + std::map > stridesMap; + + for (const auto& input : inputs) { + std::vector strides; + + auto pair = InferenceEngine::details::split(input, "["); + auto strideValues = InferenceEngine::details::split(pair.at(1), ","); + + for (const auto& stride : strideValues) { + strides.insert(strides.begin(), std::stoi(stride)); + } + + stridesMap.insert({pair.at(0), strides}); + } + + return stridesMap; + }); + setOption(printReceiveTensorTime, switches, config, VPU_CONFIG_KEY(PRINT_RECEIVE_TENSOR_TIME)); setOption(perfCount, switches, config, CONFIG_KEY(PERF_COUNT)); - static const std::unordered_map logLevels = { - { CONFIG_VALUE(LOG_NONE), LogLevel::None }, - { CONFIG_VALUE(LOG_WARNING), LogLevel::Warning }, - { CONFIG_VALUE(LOG_INFO), LogLevel::Info }, - { CONFIG_VALUE(LOG_DEBUG), LogLevel::Debug } - }; - - setOption(hostLogLevel, logLevels, config, CONFIG_KEY(LOG_LEVEL)); - setOption(deviceLogLevel, logLevels, config, VPU_CONFIG_KEY(LOG_LEVEL)); static const std::unordered_map perfReports { { VPU_CONFIG_VALUE(PER_LAYER), PerfReport::PerLayer }, @@ -273,12 +291,6 @@ IE_SUPPRESS_DEPRECATED_START setOption(compileConfig.inputBias, config, VPU_CONFIG_KEY(INPUT_BIAS), [](const std::string &src) { return std::stof(src); }); IE_SUPPRESS_DEPRECATED_END - -#ifndef NDEBUG - if (auto envVar = std::getenv("IE_VPU_LOG_LEVEL")) { - hostLogLevel = logLevels.at(envVar); - } -#endif } } // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/pass_manager.cpp b/inference-engine/src/vpu/graph_transformer/src/pass_manager.cpp index 84985d3701c196..8368eebbe08e00 100644 --- a/inference-engine/src/vpu/graph_transformer/src/pass_manager.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/pass_manager.cpp @@ -45,7 +45,8 @@ void PassSet::run(const Model::Ptr& model) const { auto startTime = std::chrono::high_resolution_clock::now(); - model->cleanUpDatas(); + model->cleanUp(); + p.first->run(model); auto endTime = std::chrono::high_resolution_clock::now(); @@ -58,7 +59,7 @@ void PassSet::run(const Model::Ptr& model) const { ++passInd; } - model->cleanUpDatas(); + model->cleanUp(); } // @@ -82,6 +83,9 @@ PassSet::Ptr PassManager::buildMiddleEnd() { _dumpInd = 0; ADD_DUMP_PASS("initial"); + ADD_PASS(addCopyForOutputsInsideNetwork); + + ADD_PASS(initialCheck); // // To overcome fp16 limitations @@ -105,6 +109,9 @@ PassSet::Ptr PassManager::buildMiddleEnd() { // Model common adaptation // + ADD_PASS(removeUnusedStagesOutputs); + ADD_DUMP_PASS("removeUnusedStagesOutputs"); + ADD_PASS(splitGroupedConv); ADD_DUMP_PASS("splitGroupedConv"); @@ -147,6 +154,13 @@ PassSet::Ptr PassManager::buildMiddleEnd() { ADD_PASS(adjustDataBatch); ADD_DUMP_PASS("adjustDataBatch"); + // + // Replace StridedSlice to other stages + // + + ADD_PASS(stridedSlice); + ADD_DUMP_PASS("stridedSlice"); + // // HW stages tiling // diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/add_copy_for_outputs_inside_network.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/add_copy_for_outputs_inside_network.cpp new file mode 100644 index 00000000000000..e28123aa5b3a16 --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/src/passes/add_copy_for_outputs_inside_network.cpp @@ -0,0 +1,53 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include + +namespace vpu { +namespace { + +class PassImpl final : public Pass { +public: + explicit PassImpl(const StageBuilder::Ptr& stageBuilder) : _stageBuilder(stageBuilder) {} + + void run(const Model::Ptr& model) override { + VPU_PROFILE(initialCheck); + + for (const auto& outputData : model->datas()) { + if (outputData->usage() != DataUsage::Output || outputData->numConsumers() == 0) { + continue; + } + + auto newIntermediateData = model->duplicateData( + outputData, + "@intermediate", + outputData->desc()); + + auto producer = outputData->producerEdge(); + model->replaceStageOutput(producer, newIntermediateData); + for (auto consumerEdge : outputData->consumerEdges()) { + model->replaceStageInput(consumerEdge, newIntermediateData); + } + + _stageBuilder->addCopyStage( + model, + formatString("%s@copy-to-output", outputData->name()), + nullptr, + newIntermediateData, + outputData); + } + } + +private: + StageBuilder::Ptr _stageBuilder; +}; + +} // namespace + +Pass::Ptr PassManager::addCopyForOutputsInsideNetwork() { + return std::make_shared(_stageBuilder); +} + +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/adjust_data_batch.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/adjust_data_batch.cpp index b704114e1a6921..b6492f0e0f2618 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/adjust_data_batch.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/adjust_data_batch.cpp @@ -383,8 +383,8 @@ void PassImpl::replicateStage( } auto tileStage = model->duplicateStage( - stage->name() + postfix, stage, + postfix, newInputs, newOutputs); diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/adjust_data_layout.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/adjust_data_layout.cpp index b28099436b69f1..d9bfd2ebbe9391 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/adjust_data_layout.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/adjust_data_layout.cpp @@ -25,20 +25,21 @@ class ConvertOrderStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector&, - ScalePropagationStep) override { + ScalePropagationStep, + StageDataInfo&) override { VPU_THROW_EXCEPTION << "Must never be called"; } - void propagateDataOrderImpl() const override { + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { } - void getDataStridesRequirementsImpl() const override { + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } StageSHAVEsRequirements getSHAVEsRequirementsImpl() const override { @@ -46,11 +47,8 @@ class ConvertOrderStage final : public StageNode { } void finalCheckImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); auto inDimsOrder = input->desc().dimsOrder(); auto outDimsOrder = output->desc().dimsOrder(); @@ -63,12 +61,8 @@ class ConvertOrderStage final : public StageNode { } void serializeParamsImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); auto inDimsOrder = input->desc().dimsOrder(); auto outDimsOrder = output->desc().dimsOrder(); @@ -94,12 +88,8 @@ class ConvertOrderStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeNewBuffer(serializer); output->serializeNewBuffer(serializer); @@ -145,6 +135,12 @@ void PassImpl::run(const Model::Ptr& model) { if (data->usage() == DataUsage::Intermediate) continue; + if (data->usage() == DataUsage::Input || data->usage() == DataUsage::Output) { + if (!data->requiredStrides().fixedStrides().empty()) { + continue; + } + } + data->updateRequiredStrides(StridesRequirement::compact()); } @@ -201,6 +197,10 @@ void PassImpl::run(const Model::Ptr& model) { auto output = outEdge->output(); auto portInd = outEdge->portInd(); + if (output->usage() == DataUsage::Fake) { + continue; + } + auto requiredOrder = output->desc().dimsOrder(); if (curStageInfo.hasOutput(outEdge)) { @@ -310,6 +310,10 @@ void PassImpl::run(const Model::Ptr& model) { auto output = outEdge->output(); auto portInd = outEdge->portInd(); + if (output->usage() == DataUsage::Fake) { + continue; + } + auto requiredStrides = StridesRequirement(); if (curStageInfo.hasOutput(outEdge)) { diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/adjust_data_location.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/adjust_data_location.cpp index d15e6f2e770b10..9c242f76cef0fc 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/adjust_data_location.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/adjust_data_location.cpp @@ -81,6 +81,8 @@ void PassImpl::copyHwNetOutputs(const Model::Ptr& model) { model->replaceStageOutput(stage->outputEdge(0), newOutput); + newOutput->updateRequiredStrides(stage->getDataStridesRequirements().getOutput(stage->outputEdge(0))); + _stageBuilder->addCopyStage( model, stage->name() + "@flush-output", diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/final_check.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/final_check.cpp index 5d9264400940a9..e9d3613385390d 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/final_check.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/final_check.cpp @@ -112,7 +112,7 @@ void PassImpl::run(const Model::Ptr& model) { // if (connectionStage->type() == StageType::Concat || - connectionStage->type() == StageType::Broadcast) { + connectionStage->type() == StageType::Expand) { IE_ASSERT(producer == child); IE_ASSERT(consumer == parent); } else if (connectionStage->type() == StageType::Split || diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/hw_conv_tiling.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/hw_conv_tiling.cpp index d9411099bbebca..992632df175b87 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/hw_conv_tiling.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/hw_conv_tiling.cpp @@ -4,726 +4,23 @@ #include -#include +#include #include #include -#include -#include -#include -#include -#include -#include #include -#include - #include #include #include #include #include +#include +#include namespace vpu { namespace { -class Optimizer final { -public: - Optimizer(const std::string& stageName, - const DimValues& inputDims, const DimValues& outputDims, - const DimValues& origOutputDims, - bool withPool, - int kernelSizeX, int kernelSizeY, - int kernelStride, - int paddingX, int paddingY) - : _stageName(stageName), - _inputDims(inputDims), _outputDims(outputDims), - _origOutputDims(origOutputDims), - _withPool(withPool), - _kernelSizeX(kernelSizeX), _kernelSizeY(kernelSizeY), - _kernelStride(kernelStride), - _paddingX(paddingX), _paddingY(paddingY) { - } - - bool optimize() { - initTileSizes(); - - if (!selectBestTile()) { - if (_withPool) { - removePool(); - return optimize(); - } - - return false; - } - - patternMatching(); - - // Merged Pooling and SoC can't be used together. - if (_withPool) { - IE_ASSERT(!hasSoC()); - } - - if (!createTiles()) { - if (_withPool) { - removePool(); - return optimize(); - } - - return false; - } - - return true; - } - - bool withPool() const { - return _withPool; - } - - const HwConvTilingPtr& getTiling() const { - return _tiling; - } - -private: - void initTileSizes() { - int tempX = _inputDims[Dim::W] + 2 * _paddingX - _kernelSizeX; - int tempY = _inputDims[Dim::H] + 2 * _paddingY - _kernelSizeY; - - int outWidthWithOutCeil = (tempX + _kernelStride) / _kernelStride; - int outHeightWithOutCeil = (tempY + _kernelStride) / _kernelStride; - - int outWidthWithCeil = static_cast(std::ceil(static_cast(tempX) / _kernelStride + 1)); - int outHeightWithCeil = static_cast(std::ceil(static_cast(tempY) / _kernelStride + 1)); - - if ((_origOutputDims[Dim::W] != outWidthWithCeil) && (_origOutputDims[Dim::W] != outWidthWithOutCeil)) { - VPU_THROW_EXCEPTION - << "Internal error: Output in " << _stageName << " has incorrect width dimension. Expected: " - << outWidthWithCeil << " or " << outWidthWithOutCeil << " Actual: " << _origOutputDims[Dim::W]; - } - - if ((_origOutputDims[Dim::H] != outHeightWithCeil) && (_origOutputDims[Dim::H] != outHeightWithOutCeil)) { - VPU_THROW_EXCEPTION - << "Internal error: Output in " << _stageName << " has incorrect height dimension. Expected: " - << outHeightWithCeil << " or " << outHeightWithOutCeil << " Actual: " << _origOutputDims[Dim::H]; - } - - if ((_origOutputDims[Dim::W] == outWidthWithCeil) && (_origOutputDims[Dim::H] == outHeightWithCeil)) { - _useCeil = true; - } else { - IE_ASSERT((_origOutputDims[Dim::W] == outWidthWithOutCeil) && (_origOutputDims[Dim::H] == outHeightWithOutCeil)); - } - - _inputTileDims.set(Dim::W, std::min(CNN_MAX_INPUT_WIDTH, _inputDims[Dim::W])); - _inputTileDims.set(Dim::H, std::min(CNN_MAX_INPUT_HEIGHT, _inputDims[Dim::H])); - _inputTileDims.set(Dim::C, std::min(CNN_MAX_INPUT_CHANNELS, _inputDims[Dim::C])); - - _outputTileDims.set(Dim::W, _outputDims[Dim::W]); - _outputTileDims.set(Dim::H, _outputDims[Dim::H]); - _outputTileDims.set(Dim::C, _outputDims[Dim::C]); - - correctOutputPlaneSize(); - } - - void patternMatching() { - if (!_withPool && - _kernelSizeX == 3 && _kernelSizeY == 3 && _paddingX == 1 && _paddingY == 1 && _kernelStride == 1 && - _inputDims[Dim::C] == 512 && _inputDims[Dim::H] == 28 && _inputDims[Dim::W] == 28 && - _outputDims[Dim::C] == 512) { - _inputTileDims.set(Dim::H, 28); - _inputTileDims.set(Dim::C, 172); - _outputTileDims.set(Dim::H, _outputDims[Dim::H]); - _outputTileDims.set(Dim::W, _outputDims[Dim::W]); - correctOutputPlaneSize(); - return; - } - - if (!_withPool && - _kernelSizeX == 3 && _kernelSizeY == 3 && _paddingX == 1 && _paddingY == 1 && _kernelStride == 1 && - _inputDims[Dim::C] == 256 && _inputDims[Dim::H] == 56 && _inputDims[Dim::W] == 56 && - _outputDims[Dim::C] == 256) { - _inputTileDims.set(Dim::H, 30); - _inputTileDims.set(Dim::C, 128); - _outputTileDims.set(Dim::H, _outputDims[Dim::H]); - _outputTileDims.set(Dim::W, _outputDims[Dim::W]); - correctOutputPlaneSize(); - return; - } - - if (!_withPool && - _kernelSizeX == 3 && _kernelSizeY == 3 && _paddingX == 1 && _paddingY == 1 && _kernelStride == 1 && - _inputDims[Dim::C] == 64 && _inputDims[Dim::H] == 224 && _inputDims[Dim::W] == 224 && - _outputDims[Dim::C] == 64) { - _inputTileDims.set(Dim::H, 82); - _inputTileDims.set(Dim::W, 82); - _outputTileDims.set(Dim::H, _outputDims[Dim::H]); - _outputTileDims.set(Dim::W, _outputDims[Dim::W]); - correctOutputPlaneSize(); - return; - } - - if (_inputDims[Dim::C] == 512 && - _inputDims[Dim::H] == 7 && - _inputDims[Dim::W] == 7 && - _outputDims[Dim::C] == 4096) { - _inputTileDims.set(Dim::C, 64); - correctOutputPlaneSize(); - return; - } - - if (!_withPool && - _kernelSizeX == 3 && _kernelSizeY == 3 && _paddingX == 1 && _paddingY == 1 && _kernelStride == 1 && - _inputDims[Dim::C] == 128 && _inputDims[Dim::H] == 112 && _inputDims[Dim::W] == 112 && - _outputDims[Dim::C] == 128) { - _inputTileDims.set(Dim::H, 32); - _inputTileDims.set(Dim::W, 112); - _inputTileDims.set(Dim::C, 32); - _outputTileDims.set(Dim::H, _outputDims[Dim::H]); - _outputTileDims.set(Dim::W, _outputDims[Dim::W]); - correctOutputPlaneSize(); - return; - } - - if (_inputDims[Dim::C] == 1088 && - _inputDims[Dim::H] == 17 && - _inputDims[Dim::W] == 17 && - (_outputDims[Dim::C] == 128 || _outputDims[Dim::C] == 192)) { - _inputTileDims.set(Dim::H, 17); - _inputTileDims.set(Dim::C, 544); - _outputTileDims.set(Dim::H, _outputDims[Dim::H]); - _outputTileDims.set(Dim::W, _outputDims[Dim::W]); - correctOutputPlaneSize(); - return; - } - - if (_inputDims[Dim::C] == 1024 && - _inputDims[Dim::H] == 17 && - _inputDims[Dim::W] == 17 && - _outputDims[Dim::C] == 384) { - _inputTileDims.set(Dim::H, 17); - _inputTileDims.set(Dim::C, 512); - _outputTileDims.set(Dim::H, _outputDims[Dim::H]); - _outputTileDims.set(Dim::W, _outputDims[Dim::W]); - correctOutputPlaneSize(); - return; - } - - if (!_withPool && - _kernelSizeX == 3 && _kernelSizeY == 3 && _paddingX == 0 && _paddingY == 0 && _kernelStride == 2 && - _inputDims[Dim::C] == 384 && _inputDims[Dim::H] == 35 && _inputDims[Dim::W] == 35 && - _outputDims[Dim::C] == 384) { - _inputTileDims.set(Dim::C, 194); - _inputTileDims.set(Dim::H, 35); - _inputTileDims.set(Dim::W, 35); - _outputTileDims.set(Dim::H, _outputDims[Dim::H]); - _outputTileDims.set(Dim::W, _outputDims[Dim::W]); - correctOutputPlaneSize(); - return; - } - - if (_inputDims[Dim::C] == 192 && - _inputDims[Dim::H] == 71 && - _inputDims[Dim::W] == 71 && - _outputDims[Dim::H] == 35) { - _inputTileDims.set(Dim::W, 71); - _inputTileDims.set(Dim::C, 96); - _outputTileDims.set(Dim::H, _outputDims[Dim::H]); - _outputTileDims.set(Dim::W, _outputDims[Dim::W]); - correctOutputPlaneSize(); - return; - } - - if (!_withPool && - _inputDims[Dim::C] == 256 && - _inputDims[Dim::H] == 128 && - _inputDims[Dim::W] == 128 && - _outputDims[Dim::C] == 256) { - _inputTileDims.set(Dim::W, 128); - _inputTileDims.set(Dim::H, 15); - _inputTileDims.set(Dim::C, 64); - _outputTileDims.set(Dim::H, _outputDims[Dim::H]); - _outputTileDims.set(Dim::W, _outputDims[Dim::W]); - correctOutputPlaneSize(); - return; - } - - if (!_withPool && - _inputDims[Dim::C] == 512 && - _inputDims[Dim::H] == 64 && - _inputDims[Dim::W] == 64 && - _outputDims[Dim::C] == 512) { - _inputTileDims.set(Dim::W, 64); - _inputTileDims.set(Dim::H, 10); - _inputTileDims.set(Dim::C, 128); - _outputTileDims.set(Dim::H, _outputDims[Dim::H]); - _outputTileDims.set(Dim::W, _outputDims[Dim::W]); - correctOutputPlaneSize(); - return; - } - - if (!_withPool && - _kernelSizeX == 1 && _kernelSizeY == 1 && _paddingX == 0 && _paddingY == 0 && _kernelStride == 1 && - _inputDims[Dim::C] == 384 && - _inputDims[Dim::H] == 56 && - _inputDims[Dim::W] == 56 && - _outputDims[Dim::C] == 64) { - _inputTileDims.set(Dim::C, 384); - _inputTileDims.set(Dim::H, 56); - _inputTileDims.set(Dim::W, 20); - _outputTileDims.set(Dim::H, _outputDims[Dim::H]); - _outputTileDims.set(Dim::W, _outputDims[Dim::W]); - correctOutputPlaneSize(); - return; - } - - if (!_withPool && - _kernelSizeX == 1 && _kernelSizeY == 1 && _paddingX == 0 && _paddingY == 0 && _kernelStride == 1 && - _inputDims[Dim::C] == 2112 && - _inputDims[Dim::H] == 14 && - _inputDims[Dim::W] == 14 && - _outputDims[Dim::C] == 1056) { - _inputTileDims.set(Dim::C, 556); - _inputTileDims.set(Dim::H, 14); - _inputTileDims.set(Dim::W, 14); - _outputTileDims.set(Dim::H, _outputDims[Dim::H]); - _outputTileDims.set(Dim::W, _outputDims[Dim::W]); - correctOutputPlaneSize(); - return; - } - - if (!_withPool && - _kernelSizeX == 3 && _kernelSizeY == 3 && _paddingX == 1 && _paddingY == 1 && _kernelStride == 2 && - _inputDims[Dim::C] == 256 && - _inputDims[Dim::H] == 52 && - _inputDims[Dim::W] == 52 && - _outputDims[Dim::C] == 512) { - _inputTileDims.set(Dim::C, 128); - _inputTileDims.set(Dim::H, 52); - _inputTileDims.set(Dim::W, 52); - _outputTileDims.set(Dim::H, _outputDims[Dim::H]); - _outputTileDims.set(Dim::W, _outputDims[Dim::W]); - correctOutputPlaneSize(); - return; - } - - if (!_withPool && - _kernelSizeX == 3 && _kernelSizeY == 3 && _paddingX == 1 && _paddingY == 1 && _kernelStride == 1 && - _inputDims[Dim::C] == 256 && - _inputDims[Dim::H] == 23 && - _inputDims[Dim::W] == 23 && - _outputDims[Dim::C] == 640) { - _inputTileDims.set(Dim::C, 256); - _inputTileDims.set(Dim::H, 14); - _inputTileDims.set(Dim::W, 23); - _outputTileDims.set(Dim::H, _outputDims[Dim::H]); - _outputTileDims.set(Dim::W, _outputDims[Dim::W]); - correctOutputPlaneSize(); - return; - } - } - - bool selectBestTile() { - struct Solution final { - int numWidthTiles = 0; - int numHeightTiles = 0; - int numChannelTiles = 0; - int totalNumTiles = 0; - double cost = std::numeric_limits::max(); - }; - - const auto& env = CompileEnv::get(); - - // TODO: estimate this numbers - const int maxNumWidthTiles = 15; - const int maxNumHeightTiles = 15; - const int maxNumChannelTiles = _withPool ? 1 : 15; - - Solution bestSol; - - auto outputTileCopy = _outputTileDims; - - auto minInputTileDimW = 64; - auto minInputTileDimH = _kernelSizeY; - if (_withPool) { - minInputTileDimW *= 2; - minInputTileDimH *= 2; - } - - for (int numChannelTiles = 1; numChannelTiles <= maxNumChannelTiles; numChannelTiles++) { - int inputTileDimC = divUp(_inputDims[Dim::C], numChannelTiles); - - for (int numWidthTiles = 1; numWidthTiles <= maxNumWidthTiles; numWidthTiles++) { - int inputTileDimW = divUp(_inputDims[Dim::W], numWidthTiles); - - // - // Filter-out too small SoW tiles. - // - - if (numWidthTiles > 1 && inputTileDimW < minInputTileDimW) { - break; - } - - for (int numHeightTiles = 1; numHeightTiles <= maxNumHeightTiles; numHeightTiles++) { - int inputTileDimH = divUp(_inputDims[Dim::H], numHeightTiles); - - // - // Filter-out too small SoH tiles. - // - - if (numHeightTiles > 1 && inputTileDimH < minInputTileDimH) { - break; - } - - // - // Try current tile size. - // - - _inputTileDims.set(Dim::W, inputTileDimW); - _inputTileDims.set(Dim::H, inputTileDimH); - _inputTileDims.set(Dim::C, inputTileDimC); - - _outputTileDims = outputTileCopy; - correctOutputPlaneSize(); - - // - // Limitations for Conv+Pool case. - // - - if (_withPool) { - if (_outputTileDims[Dim::W] <= 2 || - _outputTileDims[Dim::H] <= 2) { - break; - } - } - - // - // Check that tiling is valid. - // - - auto heightTiles = calcHeightTiles(); - auto widthTiles = calcWidthTiles(); - - if (heightTiles.empty()) { - continue; - } - if (widthTiles.empty()) { - break; - } - - bool isOK = true; - double solutionCost = 0.0; - - for (const auto& heightTile : heightTiles) { - for (const auto& widthTile : widthTiles) { - // - // Limitations for Conv+Pool case. - // - - if (_withPool) { - if (widthTile.inputWithJunk % 2 != 0 || - heightTile.inputWithJunk % 2 != 0 || - widthTile.outputWithJunk % 2 != 0 || - widthTile.outputWithJunk <= 2 || - heightTile.outputWithJunk <= 2) { - isOK = false; - break; - } - } - - // - // Can use this tile. - // - - auto tileInfo = splitHwConvIntoOutChannelsTiles( - widthTile.inputWithJunk, heightTile.inputWithJunk, inputTileDimC, - outputTileCopy[Dim::C], - _kernelSizeX, _kernelSizeY, _kernelStride); - - if (tileInfo.numDescr == 0) { - isOK = false; - break; - } - - // - // Output tile fits to CMX limitation. - // - - DimValues fullOutputTileDims; - fullOutputTileDims.set(Dim::W, widthTile.outputWithJunk); - fullOutputTileDims.set(Dim::H, heightTile.outputWithJunk); - fullOutputTileDims.set(Dim::C, outputTileCopy[Dim::C]); - - // TODO: support HCW - if (calculateHwBufferSize(fullOutputTileDims) > env.resources.cmxLimit) { - isOK = false; - break; - } - - // - // Calc tile cost. - // - - solutionCost += tileInfo.cost * numChannelTiles; - - // Alignment for output - if ((widthTile.outputStartIndex * sizeof(fp16_t)) % 16 != 0) { - solutionCost += 1.0 - * widthTile.outputWithJunk - * heightTile.outputWithJunk - * outputTileCopy[Dim::C]; - } - - // Alignment for input - if ((widthTile.inputStartIndex * sizeof(fp16_t)) % 16 != 0) { - solutionCost += 1.0 - * widthTile.inputWithJunk - * heightTile.inputWithJunk - * tileInfo.extendedInputDimC; - } - - // SoC overhead - solutionCost += 1.0 - * (numChannelTiles - 1) - * widthTile.outputWithJunk - * heightTile.outputWithJunk - * outputTileCopy[Dim::C]; - } - - if (!isOK) { - break; - } - } - - if (!isOK) { - continue; - } - - // - // Compare with current best solution. - // - - Solution curSol; - curSol.numWidthTiles = numWidthTiles; - curSol.numHeightTiles = numHeightTiles; - curSol.numChannelTiles = numChannelTiles; - curSol.totalNumTiles = numWidthTiles * numHeightTiles * numChannelTiles; - curSol.cost = solutionCost; - - if (curSol.cost < bestSol.cost || (isDoubleEqual(curSol.cost, bestSol.cost) && curSol.totalNumTiles < bestSol.totalNumTiles)) { - bestSol = curSol; - } - - // Skip smaller SoC tiling. - break; - } - } - } - - if (bestSol.totalNumTiles == 0) { - return false; - } - - int inputTileDimW = divUp(_inputDims[Dim::W], bestSol.numWidthTiles); - int inputTileDimH = divUp(_inputDims[Dim::H], bestSol.numHeightTiles); - int inputTileDimC = divUp(_inputDims[Dim::C], bestSol.numChannelTiles); - - _inputTileDims.set(Dim::W, inputTileDimW); - _inputTileDims.set(Dim::H, inputTileDimH); - _inputTileDims.set(Dim::C, inputTileDimC); - - _outputTileDims = outputTileCopy; - correctOutputPlaneSize(); - - return true; - } - - bool createTiles() { - auto heightTiles = calcHeightTiles(); - IE_ASSERT(!heightTiles.empty()); - - auto widthTiles = calcWidthTiles(); - IE_ASSERT(!widthTiles.empty()); - - _tiling = std::make_shared(); - _tiling->sohTiles = heightTiles.size(); - _tiling->sowTiles = widthTiles.size(); - _tiling->socTiles = divUp(_inputDims[Dim::C], _inputTileDims[Dim::C]); - - for (int sohInd = 0; sohInd < _tiling->sohTiles; ++sohInd) { - const auto& heightTileInfo = heightTiles[sohInd]; - - for (int sowInd = 0; sowInd < _tiling->sowTiles; ++sowInd) { - const auto& widthTileInfo = widthTiles[sowInd]; - - auto planeTile = std::make_shared(); - planeTile->parent = _tiling; - - planeTile->sohInd = sohInd; - planeTile->sowInd = sowInd; - - planeTile->heightInfo = heightTileInfo; - planeTile->widthInfo = widthTileInfo; - - for (int socInd = 0; socInd < _tiling->socTiles; ++socInd) { - auto channelTile = std::make_shared(); - channelTile->parent = planeTile; - - channelTile->socInd = socInd; - - channelTile->finalTiles = splitHwConvIntoOutChannelsTiles( - widthTileInfo.inputWithJunk, heightTileInfo.inputWithJunk, _inputTileDims[Dim::C], - _outputTileDims[Dim::C], - _kernelSizeX, _kernelSizeY, _kernelStride); - - if (channelTile->finalTiles.numDescr == 0) { - return false; - } - - channelTile->extendedInputDimC = channelTile->finalTiles.extendedInputDimC; - channelTile->extendedOutputDimC = channelTile->finalTiles.extendedOutputDimC; - - channelTile->channelStartIndex = socInd * _inputTileDims[Dim::C]; - channelTile->numInputChannels = _inputTileDims[Dim::C]; - - planeTile->channelTiles.emplace_back(channelTile); - } - - _tiling->planeTiles.emplace_back(planeTile); - } - } - - return true; - } - -private: - void correctOutputPlaneSize() { - int maxOutputWidth = calcOutputSize(_inputTileDims[Dim::W], _kernelSizeX, _kernelStride, _paddingX, _paddingX, _useCeil); - if (_withPool) { - maxOutputWidth /= 2; - } - _outputTileDims.set(Dim::W, std::min(_outputTileDims[Dim::W], maxOutputWidth)); - - int maxOutputHeight = calcOutputSize(_inputTileDims[Dim::H], _kernelSizeY, _kernelStride, _paddingY, _paddingY, _useCeil); - if (_withPool) { - maxOutputHeight /= 2; - } - _outputTileDims.set(Dim::H, std::min(_outputTileDims[Dim::H], maxOutputHeight)); - } - - bool hasSoC() const { - return _inputTileDims[Dim::C] != _inputDims[Dim::C]; - } - - void removePool() { - _withPool = false; - _outputDims = _origOutputDims; - } - - SmallVector calcHeightTiles() { - SmallVector heightTiles; - - if (_outputTileDims[Dim::H] == _outputDims[Dim::H]) { - HwPlaneTileInfo info; - info.inputWithJunk = _inputDims[Dim::H]; - info.outputWithJunk = _outputDims[Dim::H]; - info.outputJunkBefore = 0; - info.outputJunkAfter = 0; - info.inputStartIndex = 0; - info.inputEndIndex = _inputDims[Dim::H]; - info.outputStartIndex = 0; - info.outputEndIndex = _outputDims[Dim::H]; - - heightTiles.emplace_back(info); - } else { - if (_withPool) { - heightTiles = splitIntoPlaneTilesWithPool( - _inputDims[Dim::H], - _kernelSizeY, - _kernelStride, - _paddingY, - _outputTileDims[Dim::H]); - } else { - heightTiles = splitIntoPlaneTiles( - _inputDims[Dim::H], - _outputDims[Dim::H], - _kernelSizeY, - _kernelStride, - _paddingY, _paddingY, - _outputTileDims[Dim::H], - false, - _useCeil); - } - } - - return heightTiles; - } - - SmallVector calcWidthTiles() { - SmallVector widthTiles; - - if (_outputTileDims[Dim::W] == _outputDims[Dim::W]) { - HwPlaneTileInfo info; - info.inputWithJunk = _inputDims[Dim::W]; - info.outputWithJunk = _outputDims[Dim::W]; - info.outputJunkBefore = 0; - info.outputJunkAfter = 0; - info.inputStartIndex = 0; - info.inputEndIndex = _inputDims[Dim::W]; - info.outputStartIndex = 0; - info.outputEndIndex = _outputDims[Dim::W]; - - widthTiles.emplace_back(info); - } else { - if (_withPool) { - widthTiles = splitIntoPlaneTilesWithPool( - _inputDims[Dim::W], - _kernelSizeX, - _kernelStride, - _paddingX, - _outputTileDims[Dim::W]); - } else { - widthTiles = splitIntoPlaneTiles( - _inputDims[Dim::W], - _outputDims[Dim::W], - _kernelSizeX, - _kernelStride, - _paddingX, _paddingX, - _outputTileDims[Dim::W], - true, - _useCeil); - } - } - - return widthTiles; - } - -private: - std::string _stageName; - - DimValues _inputDims; - DimValues _outputDims; - DimValues _origOutputDims; - - bool _withPool = false; - - int _kernelSizeX = 0; - int _kernelSizeY = 0; - int _kernelStride = 0; - int _paddingX = 0; - int _paddingY = 0; - - DimValues _inputTileDims; - DimValues _outputTileDims; - - HwConvTilingPtr _tiling; - - bool _useCeil = false; -}; - -using TileWeightsMap = std::unordered_map; - -const int BIASES_IND = -1; -const int SCALES_IND = -2; - class PassImpl final : public Pass { public: explicit PassImpl(const StageBuilder::Ptr& stageBuilder) : _stageBuilder(stageBuilder) {} @@ -742,110 +39,93 @@ void PassImpl::run(const Model::Ptr& model) { continue; } - auto tryHW = origStage->attrs().getOrDefault("tryHW", false); + const auto tryHW = origStage->attrs().getOrDefault("tryHW", false); if (!tryHW) { continue; } - auto origInput = origStage->input(0); - auto origWeights = origStage->input(1); - auto origBiases = origStage->input(2); - auto origOutput = origStage->output(0); - - auto kernelSizeX = origStage->attrs().get("kernelSizeX"); - auto kernelSizeY = origStage->attrs().get("kernelSizeY"); - auto kernelStride = origStage->attrs().get("kernelStrideX"); - auto padLeft = origStage->attrs().get("padLeft"); - auto padTop = origStage->attrs().get("padTop"); - - auto withReLU = origStage->attrs().getOrDefault("withReLU", false); - auto negativeSlope = origStage->attrs().getOrDefault("negativeSlope", 0.0f); - auto a0 = origStage->attrs().getOrDefault("a0", 0); - auto a1 = origStage->attrs().getOrDefault("a1", 0); - auto reluScale = origStage->attrs().getOrDefault("reluScale", 1.0f); - - auto withClamp = origStage->attrs().getOrDefault("withClamp", false); - auto clampMax = origStage->attrs().getOrDefault("clampMax", 6.0); - - auto withPool = origStage->attrs().getOrDefault("withPool", false); - auto poolKernelSizeX = origStage->attrs().getOrDefault("poolKernelSizeX", 0); - auto poolKernelSizeY = origStage->attrs().getOrDefault("poolKernelSizeY", 0); - auto poolKernelStride = origStage->attrs().getOrDefault("poolKernelStride", 0); - auto poolPadLeft = origStage->attrs().getOrDefault("poolPadLeft", 0); - auto poolPadRight = origStage->attrs().getOrDefault("poolPadRight", 0); - auto poolPadTop = origStage->attrs().getOrDefault("poolPadTop", 0); - auto poolPadBottom = origStage->attrs().getOrDefault("poolPadBottom", 0); - - auto origOutputDesc = origStage->attrs().getOrDefault("origConvOutput", origOutput->desc()); - - auto scaleFactor = origStage->attrs().getOrDefault("scaleFactor", 1.0f); - - auto& tileWeightsMap = origWeights->attrs().getOrSet("weightsPerTile", TileWeightsMap()); + const HWConvStageOptions so(origStage); + const HWConvStageIO sio(origStage, origStage->output(0)); // // Unsupported paddings // - auto hwInput = origInput; - auto hwOutput = origOutput; - // // Try to find "best" tiling // - Optimizer opt(origStage->name(), - hwInput->desc().dims(), hwOutput->desc().dims(), - origOutputDesc.dims(), - withPool, - kernelSizeX, kernelSizeY, - kernelStride, - padLeft, padTop); + const size_t tilingsCount = 1; + const HWTilingNS::Direction direction = + HWTilingNS::Direction::INPUT_TO_OUTPUT; + // HWTilingNS::Direction::OUTPUT_TO_INPUT; + + const HWTilingNS::HWConvolutionTiler tiler1stAttempt( + HWTilingNS::ConvolutionOptions(origStage->name(), + sio.origInput->desc().dims(), sio.origOutput->desc().dims(), + sio.origOutputDesc.dims(), + so.kernelSizeX, so.kernelSizeY, + so.kernelStride, + so.padLeft, so.padRight, so.padTop, so.padBottom, so.withPool), + direction, tilingsCount); + + const HWTilingNS::HWConvolutionTiler& tiler = + (!tiler1stAttempt.isTilingPossible() && tiler1stAttempt.withPool()) ? + HWTilingNS::HWConvolutionTiler( + HWTilingNS::ConvolutionOptions(origStage->name(), + sio.origInput->desc().dims(), sio.origOutputDesc.dims(), + sio.origOutputDesc.dims(), + so.kernelSizeX, so.kernelSizeY, + so.kernelStride, + so.padLeft, so.padRight, so.padTop, so.padBottom, false), + direction, tilingsCount) : + tiler1stAttempt; // // Use SW stage if tiling optimization failed // - if (!opt.optimize()) { + if (!tiler.isTilingPossible()) { origStage->attrs().set("tryHW", false); - auto swConvOutput = origOutput; - if (withReLU || withPool || withClamp) { + auto swConvOutput = sio.origOutput; + if (so.withReLU || so.withPool || so.withClamp) { swConvOutput = model->addNewData( origStage->name(), - origOutputDesc); - swConvOutput->attrs().copyFrom(origOutput->attrs()); + sio.origOutputDesc); + swConvOutput->attrs().copyFrom(sio.origOutput->attrs()); model->replaceStageOutput(origStage->outputEdge(0), swConvOutput); } auto hwPoolInput = swConvOutput; - if (withReLU) { - auto swReluOutput = origOutput; - if (withPool) { + if (so.withReLU) { + auto swReluOutput = sio.origOutput; + if (so.withPool) { swReluOutput = model->addNewData( origStage->name() + "@ReLU", - origOutputDesc); - swReluOutput->attrs().copyFrom(origOutput->attrs()); + sio.origOutputDesc); + swReluOutput->attrs().copyFrom(sio.origOutput->attrs()); } _stageBuilder->addReLUStage( model, origStage->name() + "@ReLU", origStage->origLayer(), - negativeSlope, + so.negativeSlope, swConvOutput, swReluOutput); hwPoolInput = swReluOutput; } - if (withClamp) { - auto swClampOutput = origOutput; - if (withPool) { + if (so.withClamp) { + auto swClampOutput = sio.origOutput; + if (so.withPool) { swClampOutput = model->addNewData( origStage->name() + "@Clamp", - origOutputDesc); - swClampOutput->attrs().copyFrom(origOutput->attrs()); + sio.origOutputDesc); + swClampOutput->attrs().copyFrom(sio.origOutput->attrs()); } _stageBuilder->addClampStage( @@ -853,31 +133,31 @@ void PassImpl::run(const Model::Ptr& model) { origStage->name() + "@Clamp", origStage->origLayer(), 0.0, - clampMax, + so.clampMax, swConvOutput, swClampOutput); hwPoolInput = swClampOutput; } - if (withPool) { + if (so.withPool) { auto hwPoolStage = model->addNewStage( origStage->name() + "@Pool", StageType::StubMaxPool, origStage->origLayer(), {hwPoolInput}, - {origOutput}); + {sio.origOutput}); - hwPoolStage->attrs().set("kernelSizeX", poolKernelSizeX); - hwPoolStage->attrs().set("kernelSizeY", poolKernelSizeY); + hwPoolStage->attrs().set("kernelSizeX", so.poolKernelSizeX); + hwPoolStage->attrs().set("kernelSizeY", so.poolKernelSizeY); - hwPoolStage->attrs().set("kernelStrideX", poolKernelStride); - hwPoolStage->attrs().set("kernelStrideY", poolKernelStride); + hwPoolStage->attrs().set("kernelStrideX", so.poolKernelStride); + hwPoolStage->attrs().set("kernelStrideY", so.poolKernelStride); - hwPoolStage->attrs().set("padLeft", poolPadLeft); - hwPoolStage->attrs().set("padRight", poolPadRight); - hwPoolStage->attrs().set("padTop", poolPadTop); - hwPoolStage->attrs().set("padBottom", poolPadBottom); + hwPoolStage->attrs().set("padLeft", so.poolPadLeft); + hwPoolStage->attrs().set("padRight", so.poolPadRight); + hwPoolStage->attrs().set("padTop", so.poolPadTop); + hwPoolStage->attrs().set("padBottom", so.poolPadBottom); hwPoolStage->attrs().set("excludePad", false); @@ -887,478 +167,37 @@ void PassImpl::run(const Model::Ptr& model) { continue; } - // - // Remove merged pool if we failed to optimize tiling with it - // - - model->disconnectStageDatas(origStage); - - if (withPool && !opt.withPool()) { - auto hwPoolInput = model->addNewData( - origStage->name(), - origOutputDesc); - hwPoolInput->attrs().copyFrom(origOutput->attrs()); - - auto hwPoolStage = model->addNewStage( - origStage->name() + "@Pool", - StageType::StubMaxPool, - origStage->origLayer(), - {hwPoolInput}, - {hwOutput}); - - hwPoolStage->attrs().set("kernelSizeX", poolKernelSizeX); - hwPoolStage->attrs().set("kernelSizeY", poolKernelSizeY); - - hwPoolStage->attrs().set("kernelStrideX", poolKernelStride); - hwPoolStage->attrs().set("kernelStrideY", poolKernelStride); - - hwPoolStage->attrs().set("padLeft", poolPadLeft); - hwPoolStage->attrs().set("padRight", poolPadRight); - hwPoolStage->attrs().set("padTop", poolPadTop); - hwPoolStage->attrs().set("padBottom", poolPadBottom); - - hwPoolStage->attrs().set("excludePad", false); - - hwPoolStage->attrs().set("tryHW", true); - - hwOutput = hwPoolInput; - - withPool = false; - } - - // - // Broadcast input/output if needed - // - - const auto& tiling = opt.getTiling(); - - int totalExtendedInputDimC = 0; - int maxExtendedOutputDimC = 0; - for (const auto& planeTile : tiling->planeTiles) { - for (const auto& channelTile : planeTile->channelTiles) { - totalExtendedInputDimC = std::max(totalExtendedInputDimC, channelTile->channelStartIndex + channelTile->extendedInputDimC); - maxExtendedOutputDimC = std::max(maxExtendedOutputDimC, channelTile->extendedOutputDimC); - } - } - - auto origOutputDimC = hwOutput->desc().dim(Dim::C); - - if (totalExtendedInputDimC > hwInput->desc().dim(Dim::C)) { - auto newDesc = hwInput->desc(); - newDesc.setDim(Dim::C, totalExtendedInputDimC); - - auto hwInputExtended = model->duplicateData( - hwInput, - "@extended", - newDesc); - - _stageBuilder->addBroadcastStage( - model, - origStage->name() + "@broadcast-input", - origStage->origLayer(), - hwInput, - hwInputExtended); - - hwInput = hwInputExtended; - } - - // - // Create HW biases - // - - auto hwBiases = tileWeightsMap[BIASES_IND]; - if (hwBiases == nullptr) { - if (origBiases->usage() == DataUsage::Fake) { - hwBiases = model->addFakeData(); - } else { - auto origBiasesContent = origBiases->content(); - IE_ASSERT(origBiasesContent != nullptr); - - auto origBiasesPtr = origBiasesContent->get(); - IE_ASSERT(origBiasesPtr != nullptr); - - auto hwTileBiasesBlob = ie::make_shared_blob(InferenceEngine::TensorDesc( - ie::Precision::FP16, - {static_cast(maxExtendedOutputDimC)}, - ie::Layout::C)); - hwTileBiasesBlob->allocate(); - - auto hwTileBiasesBlobPtr = hwTileBiasesBlob->buffer().as(); - IE_ASSERT(hwTileBiasesBlobPtr != nullptr); - - std::fill_n(hwTileBiasesBlobPtr, maxExtendedOutputDimC, ie::PrecisionUtils::f32tof16(0.0f)); - std::copy_n(origBiasesPtr, origOutputDimC, hwTileBiasesBlobPtr); - - hwBiases = model->duplicateData( - origBiases, - "@HW", - DataDesc({maxExtendedOutputDimC}), - ieBlobContent(hwTileBiasesBlob)); - - if (scaleFactor != 1.0f) { - auto hwBiasesScaled = model->duplicateData( - hwBiases, - formatString("@SCALE=%f", scaleFactor), - hwBiases->desc(), - scaleContent(hwBiases->content(), scaleFactor)); - hwBiasesScaled->attrs().getOrSet("scaleFactor", 1.0f) *= scaleFactor; - - hwBiases = hwBiasesScaled; - } - } - - tileWeightsMap[BIASES_IND] = hwBiases; - } - - // - // Create HW scales - // - - auto hwScales = tileWeightsMap[SCALES_IND]; - if (hwScales == nullptr) { - float fullScale = 1.0f / scaleFactor; - if (tiling->socTiles == 1 && reluScale != 1.0f) { - fullScale *= reluScale; - } - - if (fullScale == 1.0f) { - hwScales = model->addFakeData(); - } else { - hwScales = model->addConstData( - origStage->name() + "@scales", - DataDesc({maxExtendedOutputDimC}), - replicateContent(fullScale, maxExtendedOutputDimC)); - } - - tileWeightsMap[SCALES_IND] = hwScales; - } - - // - // Create HW tiles - // - - DataVector hwInputTiles; - std::vector hwInputTilesOffsets; + model->disconnectStage(origStage); - DataVector hwOutputTiles; - std::vector hwOutputTilesOffsets; - - hwInputTiles.reserve(tiling->socTiles * tiling->sohTiles * tiling->sowTiles); - hwInputTilesOffsets.reserve(tiling->socTiles * tiling->sohTiles * tiling->sowTiles); - hwOutputTiles.reserve(tiling->socTiles * tiling->sohTiles * tiling->sowTiles); - hwOutputTilesOffsets.reserve(tiling->socTiles * tiling->sohTiles * tiling->sowTiles); - - for (const auto& planeTile : tiling->planeTiles) { - auto planeTilePostfix = getPlaneTilePostfix(planeTile); - - // - // Create output tile - // - - Data hwOutputPlaneTile; - - if (tiling->sohTiles == 1 && tiling->sowTiles == 1) { - hwOutputPlaneTile = hwOutput; - } else { - auto newDesc = hwOutput->desc(); - newDesc.setDim(Dim::W, planeTile->widthInfo.outputEndIndex - planeTile->widthInfo.outputStartIndex); - newDesc.setDim(Dim::H, planeTile->heightInfo.outputEndIndex - planeTile->heightInfo.outputStartIndex); - - hwOutputPlaneTile = model->duplicateData( - hwOutput, - planeTilePostfix, - newDesc); - - hwOutputTiles.emplace_back(hwOutputPlaneTile); - hwOutputTilesOffsets.emplace_back( - DimValues({ - {Dim::W, planeTile->widthInfo.outputStartIndex}, - {Dim::H, planeTile->heightInfo.outputStartIndex} - })); - } + for (const auto &tiling : tiler.getHwTilings()) { + HWConvStageTiler hwStageTiler(so, sio, model, + origStage, _stageBuilder, tiling, so.withPool && !tiler.withPool()); // - // Add alignment to output tile if needed + // Split/concat input/output tiles // - if ((planeTile->widthInfo.outputStartIndex * sizeof(fp16_t)) % 16 != 0) { - auto hwOutputPlaneTileAligned = model->duplicateData( - hwOutputPlaneTile, - "@aligned"); - - _stageBuilder->addCopyStage( + if (!hwStageTiler.hwInputTiles.empty()) { + _stageBuilder->addSplitStage( model, - origStage->name() + planeTilePostfix + "@align-output-ptr", + origStage->name() + "@split-input", origStage->origLayer(), - hwOutputPlaneTileAligned, - hwOutputPlaneTile); - - hwOutputPlaneTile = hwOutputPlaneTileAligned; + std::move(hwStageTiler.hwInputTilesOffsets), + hwStageTiler.hwInput, + hwStageTiler.hwInputTiles); } - Data prevPartialSum; - - for (const auto& channelTile : planeTile->channelTiles) { - auto channelTilePostfix = getChannelTilePostfix(channelTile); - - auto tilePostfix = planeTilePostfix + channelTilePostfix; - - auto hwOutputTile = hwOutputPlaneTile; - - // - // Create input tile - // - - Data hwInputTile; - - if (tiling->sohTiles == 1 && tiling->sowTiles == 1 && tiling->socTiles == 1) { - hwInputTile = hwInput; - } else { - auto newDesc = hwInput->desc(); - newDesc.setDim(Dim::W, planeTile->widthInfo.inputWithJunk); - newDesc.setDim(Dim::H, planeTile->heightInfo.inputWithJunk); - newDesc.setDim(Dim::C, channelTile->extendedInputDimC); - - hwInputTile = model->duplicateData( - hwInput, - tilePostfix, - newDesc); - - hwInputTiles.emplace_back(hwInputTile); - hwInputTilesOffsets.emplace_back( - DimValues({ - {Dim::W, planeTile->widthInfo.inputStartIndex}, - {Dim::H, planeTile->heightInfo.inputStartIndex}, - {Dim::C, channelTile->channelStartIndex} - })); - } - - // - // Add alignment to input tile if needed - // - - if ((planeTile->widthInfo.inputStartIndex * sizeof(fp16_t)) % 16 != 0) { - auto hwInputTileAligned = model->duplicateData( - hwInputTile, - "@aligned"); - - _stageBuilder->addCopyStage( - model, - origStage->name() + tilePostfix + "@align-input-ptr", - origStage->origLayer(), - hwInputTile, - hwInputTileAligned); - - hwInputTile = hwInputTileAligned; - } - - // - // Process partial output for split-over-channels - // - - if (tiling->socTiles > 1) { - auto hwConvPartialOutput = model->duplicateData( - hwOutputTile, - channelTilePostfix + "@partial"); - - if (channelTile->socInd == 0) { - prevPartialSum = hwConvPartialOutput; - } else { - auto sumPartialOutput = hwOutputTile; - if (channelTile->socInd < tiling->socTiles - 1 || withReLU || withClamp) { - sumPartialOutput = model->duplicateData( - hwOutputTile, - channelTilePostfix + "@accum"); - } - - _stageBuilder->addSumStage( - model, - origStage->name() + tilePostfix + "@accum", - origStage->origLayer(), - prevPartialSum, hwConvPartialOutput, - sumPartialOutput); - - if (channelTile->socInd == tiling->socTiles - 1 && withReLU) { - _stageBuilder->addReLUStage( - model, - origStage->name() + tilePostfix + "@ReLU", - origStage->origLayer(), - negativeSlope, - sumPartialOutput, - hwOutputTile); - } - - if (channelTile->socInd == tiling->socTiles - 1 && withClamp) { - _stageBuilder->addClampStage( - model, - origStage->name() + tilePostfix + "@Clamp", - origStage->origLayer(), - 0.0, - clampMax, - sumPartialOutput, - hwOutputTile); - } - - prevPartialSum = sumPartialOutput; - } - - hwOutputTile = hwConvPartialOutput; - } - - // - // Process output junk if needed - // - - if (planeTile->heightInfo.outputJunkBefore != 0 || - planeTile->heightInfo.outputJunkAfter != 0 || - planeTile->widthInfo.outputJunkBefore != 0 || - planeTile->widthInfo.outputJunkAfter != 0) { - auto newDesc = hwOutputTile->desc(); - newDesc.setDim(Dim::W, planeTile->widthInfo.outputWithJunk); - newDesc.setDim(Dim::H, planeTile->heightInfo.outputWithJunk); - - auto hwOutputTileWithJunk = model->duplicateData( - hwOutputTile, - "@with-junk", - newDesc); - - DimValues innerOffset; - innerOffset.set(Dim::W, planeTile->widthInfo.outputJunkBefore); - innerOffset.set(Dim::H, planeTile->heightInfo.outputJunkBefore); - - _stageBuilder->addShrinkStage( - model, - origStage->name() + tilePostfix + "@remove-junk", - origStage->origLayer(), - hwOutputTileWithJunk, - hwOutputTile, - innerOffset); - - hwOutputTile = hwOutputTileWithJunk; - } - - // - // Create tile weights - // - - auto hwTileWeights = tileWeightsMap[channelTile->socInd]; - - if (hwTileWeights == nullptr) { - hwTileWeights = model->duplicateData( - origWeights, - "@HW" + channelTilePostfix, - DataDesc({8, kernelSizeX * kernelSizeY, channelTile->extendedInputDimC, channelTile->extendedOutputDimC / 8}), - std::make_shared( - origWeights->content(), - origWeights->desc(), - channelTile->numInputChannels, - channelTile->channelStartIndex)); - - if (scaleFactor != 1.0f) { - auto hwTileWeightsScaled = model->duplicateData( - hwTileWeights, - formatString("@SCALE=%f", scaleFactor), - hwTileWeights->desc(), - scaleContent(hwTileWeights->content(), scaleFactor)); - hwTileWeightsScaled->attrs().getOrSet("scaleFactor", 1.0f) *= scaleFactor; - - hwTileWeights = hwTileWeightsScaled; - } - - tileWeightsMap[channelTile->socInd] = hwTileWeights; - } - - // - // Create tile biases - // - - Data hwTileBiases; - - if (channelTile->socInd > 0) { - hwTileBiases = model->addFakeData(); - } else { - hwTileBiases = hwBiases; - } - - // - // Create HW stage for tile - // - - auto hwOutputTileDims = hwOutputTile->desc().dims(); - if (withPool) { - hwOutputTileDims.set(Dim::W, hwOutputTileDims[Dim::W] * poolKernelStride - poolPadLeft - poolPadRight); - hwOutputTileDims.set(Dim::H, hwOutputTileDims[Dim::H] * poolKernelStride - poolPadTop - poolPadBottom); - } - - auto hwPad = getHwPaddingInfo( - hwInputTile->desc().dims(), hwOutputTileDims, - kernelSizeX, kernelSizeY, - kernelStride, kernelStride, - padLeft, padTop); - - auto hwStage = model->addNewStage( - origStage->name() + tilePostfix, - StageType::MyriadXHwOp, + if (!hwStageTiler.hwOutputTiles.empty()) { + _stageBuilder->addConcatStage( + model, + origStage->name() + "@concat-output", origStage->origLayer(), - {hwInputTile, hwTileWeights, hwTileBiases, hwScales}, - {hwOutputTile}); - - hwStage->attrs().set("hwOpType", withPool ? HwOpType::CONV_POOL : HwOpType::CONV); - - hwStage->attrs().set("kernelSizeX", kernelSizeX); - hwStage->attrs().set("kernelSizeY", kernelSizeY); - hwStage->attrs().set("kernelStride", kernelStride); - - if (withPool) { - hwStage->attrs().set("poolKernelSizeX", poolKernelSizeX); - hwStage->attrs().set("poolKernelSizeY", poolKernelSizeY); - } - - hwStage->attrs().set("pad", hwPad); - - hwStage->attrs().set("tiling", channelTile->finalTiles); - - if (tiling->socTiles > 1) { - hwStage->attrs().set("withReLU", false); - hwStage->attrs().set("withClamp", false); - } else { - hwStage->attrs().set("withReLU", withReLU); - hwStage->attrs().set("a0", a0); - hwStage->attrs().set("a1", a1); - hwStage->attrs().set("negativeSlope", negativeSlope); - - hwStage->attrs().set("withClamp", withClamp); - hwStage->attrs().set("clampMax", clampMax); - } - - hwStage->attrs().set("scaleFactor", scaleFactor); + std::move(hwStageTiler.hwOutputTilesOffsets), + hwStageTiler.hwOutputTiles, + hwStageTiler.hwOutput); } } - // - // Split/concat input/output tiles - // - - if (!hwInputTiles.empty()) { - _stageBuilder->addSplitStage( - model, - origStage->name() + "@split-input", - origStage->origLayer(), - std::move(hwInputTilesOffsets), - hwInput, - hwInputTiles); - } - - if (!hwOutputTiles.empty()) { - _stageBuilder->addConcatStage( - model, - origStage->name() + "@concat-output", - origStage->origLayer(), - std::move(hwOutputTilesOffsets), - hwOutputTiles, - hwOutput); - } - // // Remove original stage // diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/hw_conv_tiling/hw_convolution_tiler.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/hw_conv_tiling/hw_convolution_tiler.cpp new file mode 100644 index 00000000000000..0152984c95ac38 --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/src/passes/hw_conv_tiling/hw_convolution_tiler.cpp @@ -0,0 +1,757 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include +#include +#include +#include +#include + +namespace vpu { + +namespace HWTilingNS { + +bool operator<(const TilingOption& a, const TilingOption& b) { + return a.cost < b.cost || (isDoubleEqual(a.cost, b.cost) && a.totalNumTiles < b.totalNumTiles); +} + +class ConvInputToOutputDirection; +class ConvOutputToInputDirection; + +// Input -> Output case +class ConvInputToOutputDirection: public GraphDataTiling { +public: + explicit ConvInputToOutputDirection(const ConvolutionOptions &co): GraphDataTiling(co, Direction::INPUT_TO_OUTPUT) {} + ConvInputToOutputDirection(const ConvInputToOutputDirection &other): GraphDataTiling(other) {} + void initTileSizes() override { + _useCeil = ceilNeeded(); + + _inputTileDims.set(Dim::W, std::min(CNN_MAX_INPUT_WIDTH, _co._inputDims[Dim::W])); + _inputTileDims.set(Dim::H, std::min(CNN_MAX_INPUT_HEIGHT, _co._inputDims[Dim::H])); + _inputTileDims.set(Dim::C, std::min(CNN_MAX_INPUT_CHANNELS, _co._inputDims[Dim::C])); + + _outputTileDims.set(Dim::W, _co._outputDims[Dim::W]); + _outputTileDims.set(Dim::H, _co._outputDims[Dim::H]); + _outputTileDims.set(Dim::C, _co._outputDims[Dim::C]); + + correctOutputPlaneSize(); + } + + // Input -> Output case + void setInputNOutputTileDimensions(const int tileDimW, const int tileDimH, const int tileDimC) override { + _inputTileDims.set(Dim::W, tileDimW); + _inputTileDims.set(Dim::H, tileDimH); + _inputTileDims.set(Dim::C, tileDimC); + + correctOutputPlaneSize(); + } + + // Input -> Output case + void applyTilingOption(const TilingOption &tilingOption) override { + int tileDimW = divUp(_co._inputDims[Dim::W], tilingOption.numWidthTiles); + int tileDimH = divUp(_co._inputDims[Dim::H], tilingOption.numHeightTiles); + const int tileDimC = divUp(_co._inputDims[Dim::C], tilingOption.numChannelTiles); + + tileDimW = divUp(tileDimW, _co._kernelStride) * _co._kernelStride; + tileDimH = divUp(tileDimH, _co._kernelStride) * _co._kernelStride; + + _inputTileDims.set(Dim::W, tileDimW); + _inputTileDims.set(Dim::H, tileDimH); + _inputTileDims.set(Dim::C, tileDimC); + + correctOutputPlaneSize(); + } + + void correctPlaneSize() override { + correctOutputPlaneSize(); + } + + void correctOutputPlaneSize() { + int maxOutputWidth = calcOutputSize(_inputTileDims[Dim::W], _co._kernelSizeX, _co._kernelStride, + _co._paddingLeft, _co._paddingRight, _useCeil); + if (_co._withPool) { + maxOutputWidth /= 2; + } + _outputTileDims.set(Dim::W, std::min(_outputTileDims[Dim::W], maxOutputWidth)); + + int maxOutputHeight = calcOutputSize(_inputTileDims[Dim::H], _co._kernelSizeY, _co._kernelStride, + _co._paddingTop, _co._paddingBottom, _useCeil); + if (_co._withPool) { + maxOutputHeight /= 2; + } + _outputTileDims.set(Dim::H, std::min(_outputTileDims[Dim::H], maxOutputHeight)); + } + + const DimValues &splitOverTensorDims() override { + return _co._inputDims; + } + + void patternMatching() override; + +private: + bool ceilNeeded() { + int tempX = _co._inputDims[Dim::W] + _co._paddingLeft + _co._paddingRight - _co._kernelSizeX; + int tempY = _co._inputDims[Dim::H] + _co._paddingTop + _co._paddingBottom - _co._kernelSizeY; + + int outWidthWithOutCeil = (tempX + _co._kernelStride) / _co._kernelStride; + int outHeightWithOutCeil = (tempY + _co._kernelStride) / _co._kernelStride; + + int outWidthWithCeil = static_cast(std::ceil(static_cast(tempX) / _co._kernelStride + 1)); + int outHeightWithCeil = static_cast(std::ceil(static_cast(tempY) / _co._kernelStride + 1)); + + if ((_co._origOutputDims[Dim::W] != outWidthWithCeil) && (_co._origOutputDims[Dim::W] != outWidthWithOutCeil)) { + VPU_THROW_EXCEPTION + << "Internal error: Output in " << _co._stageName << " has incorrect width dimension. Expected: " + << outWidthWithCeil << " or " << outWidthWithOutCeil << " Actual: " << _co._origOutputDims[Dim::W]; + } + + if ((_co._origOutputDims[Dim::H] != outHeightWithCeil) && (_co._origOutputDims[Dim::H] != outHeightWithOutCeil)) { + VPU_THROW_EXCEPTION + << "Internal error: Output in " << _co._stageName << " has incorrect height dimension. Expected: " + << outHeightWithCeil << " or " << outHeightWithOutCeil << " Actual: " << _co._origOutputDims[Dim::H]; + } + + if ((_co._origOutputDims[Dim::W] == outWidthWithOutCeil) && (_co._origOutputDims[Dim::H] == outHeightWithOutCeil)) { + return false; + } else { + return true; + } + } +}; + +// Output -> Input case +class ConvOutputToInputDirection: public GraphDataTiling { +public: + explicit ConvOutputToInputDirection(const ConvolutionOptions &co): GraphDataTiling(co, Direction::OUTPUT_TO_INPUT) {} + ConvOutputToInputDirection(const ConvOutputToInputDirection &other): GraphDataTiling(other) {} + void initTileSizes() override { + _useCeil = false; // no ceiling needed for ConvOutputToInputDirection + + _outputTileDims.set(Dim::W, std::min(CNN_MAX_INPUT_WIDTH, _co._outputDims[Dim::W])); + _outputTileDims.set(Dim::H, std::min(CNN_MAX_INPUT_HEIGHT, _co._outputDims[Dim::H])); + _outputTileDims.set(Dim::C, _co._outputDims[Dim::C]); + + _inputTileDims.set(Dim::W, std::min(CNN_MAX_INPUT_WIDTH, _co._inputDims[Dim::W])); + _inputTileDims.set(Dim::H, std::min(CNN_MAX_INPUT_HEIGHT, _co._inputDims[Dim::H])); + _inputTileDims.set(Dim::C, std::min(CNN_MAX_INPUT_CHANNELS, _co._inputDims[Dim::C])); + + correctInputPlaneSize(); + } + // Output -> Input case + void setInputNOutputTileDimensions(const int tileDimW, const int tileDimH, const int tileDimC) override { + _outputTileDims.set(Dim::W, tileDimW); + _outputTileDims.set(Dim::H, tileDimH); + _outputTileDims.set(Dim::C, tileDimC); + + correctInputPlaneSize(); + } + + // Output -> Input case + void applyTilingOption(const TilingOption &tilingOption) override { + const int tileDimW = divUp(_co._outputDims[Dim::W], tilingOption.numWidthTiles); + const int tileDimH = divUp(_co._outputDims[Dim::H], tilingOption.numHeightTiles); + // split only input tensor over C dim + const int tileDimC = divUp(_co._inputDims[Dim::C], tilingOption.numChannelTiles); + + _outputTileDims.set(Dim::W, tileDimW); + _outputTileDims.set(Dim::H, tileDimH); + _inputTileDims.set(Dim::C, tileDimC); + + correctInputPlaneSize(); + } + + int calcInputSize( + int outputSize, + int kernelSize, int kernelStride, + int padBefore, int padAfter + ) { + return (outputSize - 1) * kernelStride + kernelSize - padBefore - padAfter; + } + + void correctPlaneSize() override { + correctInputPlaneSize(); + } + + void correctInputPlaneSize() { + int maxInputWidth = calcInputSize(_outputTileDims[Dim::W], _co._kernelSizeX, _co._kernelStride, _co._paddingLeft, + _co._paddingRight); + if (_co._withPool) { + maxInputWidth *= 2; + } + _inputTileDims.set(Dim::W, std::min(_inputTileDims[Dim::W], maxInputWidth)); + + int maxInputHeight = calcInputSize(_outputTileDims[Dim::H], _co._kernelSizeY, _co._kernelStride, _co._paddingTop, + _co._paddingBottom); + if (_co._withPool) { + maxInputHeight *= 2; + } + _inputTileDims.set(Dim::H, std::min(_inputTileDims[Dim::H], maxInputHeight)); + } + + const DimValues &splitOverTensorDims() override { + return _co._outputDims; + } + + void patternMatching() override { + // noop + } +}; + +HWConvolutionTiler::HWConvolutionTiler(const ConvolutionOptions &co, + Direction direction, + size_t maxTilingOptions) : + _co(co), + _searcher(_co, direction, maxTilingOptions) { + _tilingPossible = tileForHW(); +} + +bool HWConvolutionTiler::tileForHW() { + const std::vector &tilingOptions = _searcher.tilingOptions(); + if (tilingOptions.empty()) { + return false; + } + + for (const TilingOption &tilingOption : tilingOptions) { + const HWConvolutionTileLayoutCut tileLayoutCut = _searcher.tileLayoutCut(tilingOption); + if (tileLayoutCut.tileCutPossible()) { + _hwTilings.push_back(tileLayoutCut.hwTiling()); + } + } + + return _hwTilings.size() != 0; +} + +void ConvInputToOutputDirection::patternMatching() { + if (!_co._withPool && + _co._kernelSizeX == 3 && _co._kernelSizeY == 3 && _co._paddingLeft == 1 && _co._paddingRight == 1 && + _co._paddingTop == 1 && _co._paddingBottom == 1 && _co._kernelStride == 1 && + _co._inputDims[Dim::C] == 512 && _co._inputDims[Dim::H] == 28 && _co._inputDims[Dim::W] == 28 && + _co._outputDims[Dim::C] == 512) { + _inputTileDims.set(Dim::H, 28); + _inputTileDims.set(Dim::C, 172); + _outputTileDims.set(Dim::H, _co._outputDims[Dim::H]); + _outputTileDims.set(Dim::W, _co._outputDims[Dim::W]); + correctPlaneSize(); + return; + } + + if (!_co._withPool && + _co._kernelSizeX == 3 && _co._kernelSizeY == 3 && _co._paddingLeft == 1 && _co._paddingRight == 1 && + _co._paddingTop == 1 && _co._paddingBottom == 1 && _co._kernelStride == 1 && + _co._inputDims[Dim::C] == 256 && _co._inputDims[Dim::H] == 56 && _co._inputDims[Dim::W] == 56 && + _co._outputDims[Dim::C] == 256) { + _inputTileDims.set(Dim::H, 30); + _inputTileDims.set(Dim::C, 128); + _outputTileDims.set(Dim::H, _co._outputDims[Dim::H]); + _outputTileDims.set(Dim::W, _co._outputDims[Dim::W]); + correctPlaneSize(); + return; + } + + if (!_co._withPool && + _co._kernelSizeX == 3 && _co._kernelSizeY == 3 && _co._paddingLeft == 1 && _co._paddingRight == 1 && + _co._paddingTop == 1 && _co._paddingBottom == 1 && _co._kernelStride == 1 && + _co._inputDims[Dim::C] == 64 && _co._inputDims[Dim::H] == 224 && _co._inputDims[Dim::W] == 224 && + _co._outputDims[Dim::C] == 64) { + _inputTileDims.set(Dim::H, 82); + _inputTileDims.set(Dim::W, 82); + _outputTileDims.set(Dim::H, _co._outputDims[Dim::H]); + _outputTileDims.set(Dim::W, _co._outputDims[Dim::W]); + correctPlaneSize(); + return; + } + + if (_co._inputDims[Dim::C] == 512 && + _co._inputDims[Dim::H] == 7 && + _co._inputDims[Dim::W] == 7 && + _co._outputDims[Dim::C] == 4096) { + _inputTileDims.set(Dim::C, 64); + correctPlaneSize(); + return; + } + + if (!_co._withPool && + _co._kernelSizeX == 3 && _co._kernelSizeY == 3 && _co._paddingLeft == 1 && _co._paddingRight == 1 && + _co._paddingTop == 1 && _co._paddingBottom == 1 && _co._kernelStride == 1 && + _co._inputDims[Dim::C] == 128 && _co._inputDims[Dim::H] == 112 && _co._inputDims[Dim::W] == 112 && + _co._outputDims[Dim::C] == 128) { + _inputTileDims.set(Dim::H, 32); + _inputTileDims.set(Dim::W, 112); + _inputTileDims.set(Dim::C, 32); + _outputTileDims.set(Dim::H, _co._outputDims[Dim::H]); + _outputTileDims.set(Dim::W, _co._outputDims[Dim::W]); + correctPlaneSize(); + return; + } + + if (_co._inputDims[Dim::C] == 1088 && + _co._inputDims[Dim::H] == 17 && + _co._inputDims[Dim::W] == 17 && + (_co._outputDims[Dim::C] == 128 || _co._outputDims[Dim::C] == 192)) { + _inputTileDims.set(Dim::H, 17); + _inputTileDims.set(Dim::C, 544); + _outputTileDims.set(Dim::H, _co._outputDims[Dim::H]); + _outputTileDims.set(Dim::W, _co._outputDims[Dim::W]); + correctPlaneSize(); + return; + } + + if (_co._inputDims[Dim::C] == 1024 && + _co._inputDims[Dim::H] == 17 && + _co._inputDims[Dim::W] == 17 && + _co._outputDims[Dim::C] == 384) { + _inputTileDims.set(Dim::H, 17); + _inputTileDims.set(Dim::C, 512); + _outputTileDims.set(Dim::H, _co._outputDims[Dim::H]); + _outputTileDims.set(Dim::W, _co._outputDims[Dim::W]); + correctPlaneSize(); + return; + } + + if (!_co._withPool && + _co._kernelSizeX == 3 && _co._kernelSizeY == 3 && _co._paddingLeft == 0 && _co._paddingRight == 0 && + _co._paddingTop == 0 && _co._paddingBottom == 0 && _co._kernelStride == 2 && + _co._inputDims[Dim::C] == 384 && _co._inputDims[Dim::H] == 35 && _co._inputDims[Dim::W] == 35 && + _co._outputDims[Dim::C] == 384) { + _inputTileDims.set(Dim::C, 194); + _inputTileDims.set(Dim::H, 35); + _inputTileDims.set(Dim::W, 35); + _outputTileDims.set(Dim::H, _co._outputDims[Dim::H]); + _outputTileDims.set(Dim::W, _co._outputDims[Dim::W]); + correctPlaneSize(); + return; + } + + if (_co._inputDims[Dim::C] == 192 && + _co._inputDims[Dim::H] == 71 && + _co._inputDims[Dim::W] == 71 && + _co._outputDims[Dim::H] == 35) { + _inputTileDims.set(Dim::W, 71); + _inputTileDims.set(Dim::C, 96); + _outputTileDims.set(Dim::H, _co._outputDims[Dim::H]); + _outputTileDims.set(Dim::W, _co._outputDims[Dim::W]); + correctPlaneSize(); + return; + } + + if (!_co._withPool && + _co._inputDims[Dim::C] == 256 && + _co._inputDims[Dim::H] == 128 && + _co._inputDims[Dim::W] == 128 && + _co._outputDims[Dim::C] == 256) { + _inputTileDims.set(Dim::W, 128); + _inputTileDims.set(Dim::H, 15); + _inputTileDims.set(Dim::C, 64); + _outputTileDims.set(Dim::H, _co._outputDims[Dim::H]); + _outputTileDims.set(Dim::W, _co._outputDims[Dim::W]); + correctPlaneSize(); + return; + } + + if (!_co._withPool && + _co._inputDims[Dim::C] == 512 && + _co._inputDims[Dim::H] == 64 && + _co._inputDims[Dim::W] == 64 && + _co._outputDims[Dim::C] == 512) { + _inputTileDims.set(Dim::W, 64); + _inputTileDims.set(Dim::H, 10); + _inputTileDims.set(Dim::C, 128); + _outputTileDims.set(Dim::H, _co._outputDims[Dim::H]); + _outputTileDims.set(Dim::W, _co._outputDims[Dim::W]); + correctPlaneSize(); + return; + } + + if (!_co._withPool && + _co._kernelSizeX == 1 && _co._kernelSizeY == 1 && _co._paddingLeft == 0 && _co._paddingRight == 0 && + _co._paddingTop == 0 && _co._paddingBottom == 0 && _co._kernelStride == 1 && + _co._inputDims[Dim::C] == 384 && + _co._inputDims[Dim::H] == 56 && + _co._inputDims[Dim::W] == 56 && + _co._outputDims[Dim::C] == 64) { + _inputTileDims.set(Dim::C, 384); + _inputTileDims.set(Dim::H, 56); + _inputTileDims.set(Dim::W, 20); + _outputTileDims.set(Dim::H, _co._outputDims[Dim::H]); + _outputTileDims.set(Dim::W, _co._outputDims[Dim::W]); + correctPlaneSize(); + return; + } + + if (!_co._withPool && + _co._kernelSizeX == 1 && _co._kernelSizeY == 1 && _co._paddingLeft == 0 && _co._paddingRight == 0 && + _co._paddingTop == 0 && _co._paddingBottom == 0 && _co._kernelStride == 1 && + _co._inputDims[Dim::C] == 2112 && + _co._inputDims[Dim::H] == 14 && + _co._inputDims[Dim::W] == 14 && + _co._outputDims[Dim::C] == 1056) { + _inputTileDims.set(Dim::C, 556); + _inputTileDims.set(Dim::H, 14); + _inputTileDims.set(Dim::W, 14); + _outputTileDims.set(Dim::H, _co._outputDims[Dim::H]); + _outputTileDims.set(Dim::W, _co._outputDims[Dim::W]); + correctPlaneSize(); + return; + } + + if (!_co._withPool && + _co._kernelSizeX == 3 && _co._kernelSizeY == 3 && _co._paddingLeft == 1 && _co._paddingRight == 1 && + _co._paddingTop == 1 && _co._paddingBottom == 1 && _co._kernelStride == 2 && + _co._inputDims[Dim::C] == 256 && + _co._inputDims[Dim::H] == 52 && + _co._inputDims[Dim::W] == 52 && + _co._outputDims[Dim::C] == 512) { + _inputTileDims.set(Dim::C, 128); + _inputTileDims.set(Dim::H, 52); + _inputTileDims.set(Dim::W, 52); + _outputTileDims.set(Dim::H, _co._outputDims[Dim::H]); + _outputTileDims.set(Dim::W, _co._outputDims[Dim::W]); + correctPlaneSize(); + return; + } + + if (!_co._withPool && + _co._kernelSizeX == 3 && _co._kernelSizeY == 3 && _co._paddingLeft == 1 && _co._paddingRight == 1 && + _co._paddingTop == 1 && _co._paddingBottom == 1 && _co._kernelStride == 1 && + _co._inputDims[Dim::C] == 256 && + _co._inputDims[Dim::H] == 23 && + _co._inputDims[Dim::W] == 23 && + _co._outputDims[Dim::C] == 640) { + _inputTileDims.set(Dim::C, 256); + _inputTileDims.set(Dim::H, 14); + _inputTileDims.set(Dim::W, 23); + _outputTileDims.set(Dim::H, _co._outputDims[Dim::H]); + _outputTileDims.set(Dim::W, _co._outputDims[Dim::W]); + correctPlaneSize(); + return; + } +} + +std::unique_ptr ConvGraphDataTilingFactory::makeDirTiling(const ConvolutionOptions &co, + Direction direction) { + if (direction == Direction::INPUT_TO_OUTPUT) { + return std::unique_ptr(new ConvInputToOutputDirection(co)); + } else if (direction == Direction::OUTPUT_TO_INPUT) { + return std::unique_ptr(new ConvOutputToInputDirection(co)); + } else { + IE_ASSERT(false) << "Unsupported direction"; + } +} + +std::unique_ptr ConvGraphDataTilingFactory::makeDirTiling(const GraphDataTiling &o) { + if (o.getDirection() == Direction::INPUT_TO_OUTPUT) { + return std::unique_ptr( + new ConvInputToOutputDirection(dynamic_cast(o))); + } else if (o.getDirection() == Direction::OUTPUT_TO_INPUT) { + return std::unique_ptr( + new ConvOutputToInputDirection(dynamic_cast(o))); + } else { + IE_ASSERT(false) << "Unsupported direction"; + } +} + +// +// Looks for the optimal tiling accordingly to the cost function. Modifies dimensions in dirTiling during search. +// +std::vector HWConvolutionTilingSearcher::selectBetterTiling() const { + const auto &env = CompileEnv::get(); + GraphDataTiling &dirTiling = *_dirTiling; + FixedMaxHeap tilingOptions(_maxTilingOptions); + + // TODO: estimate this numbers + const int maxNumWidthTiles = 15; + const int maxNumHeightTiles = 15; + const int maxNumChannelTiles = _co._withPool ? 1 : 15; + + const auto outputTileInitial = dirTiling.getOutputTileDims(); + const auto inputTileInitial = dirTiling.getInputTileDims(); + + auto minInputTileDimW = 64; + auto minInputTileDimH = _co._kernelSizeY; + if (_co._withPool) { + minInputTileDimW *= 2; + minInputTileDimH *= 2; + } + + const DimValues &splitOver = dirTiling.splitOverTensorDims(); + const auto direction = dirTiling.getDirection(); + // split over Input tensor for the Channel dimension always + for (int numChannelTiles = 1; numChannelTiles <= maxNumChannelTiles; numChannelTiles++) { + const int tileSizeDimC = divUp(_co._inputDims[Dim::C], numChannelTiles); + + // here split and iterate either over input tensors or over output tensors depending on the direction. + for (int numWidthTiles = 1; numWidthTiles <= maxNumWidthTiles; numWidthTiles++) { + int tileSizeDimW = divUp(splitOver[Dim::W], numWidthTiles); + + // + // Filter-out too small SoW input tiles when loops split input tensors. + // + + if (numWidthTiles > 1 && direction == Direction::INPUT_TO_OUTPUT) { + tileSizeDimW = divUp(tileSizeDimW, _co._kernelStride) * _co._kernelStride; + + if (tileSizeDimW < minInputTileDimW) { + break; + } + } + + for (int numHeightTiles = 1; numHeightTiles <= maxNumHeightTiles; numHeightTiles++) { + int tileSizeDimH = divUp(splitOver[Dim::H], numHeightTiles); + + // + // Filter-out too small SoH input tiles when loops split input tensors. + // + + if (numHeightTiles > 1 && direction == Direction::INPUT_TO_OUTPUT) { + tileSizeDimH = divUp(tileSizeDimH, _co._kernelStride) * _co._kernelStride; + + if (tileSizeDimH < minInputTileDimH) { + break; + } + } + + // + // Try current tile size. + // + + dirTiling.resetInputTileDims(inputTileInitial); + dirTiling.resetOutputTileDims(outputTileInitial); + + dirTiling.setInputNOutputTileDimensions(tileSizeDimW, tileSizeDimH, tileSizeDimC); + + // + // Limitations for Conv+Pool case. + // + + if (_co._withPool) { + if (dirTiling.getOutputTileDims()[Dim::W] <= 2 || + dirTiling.getOutputTileDims()[Dim::H] <= 2) { + break; + } + } + + // + // Check that tiling is valid. + // + + // todo: check internal in/out hardcodes + const auto heightTiles = calcHeightTiles(_co, dirTiling.getOutputTileDims(), + dirTiling.useCeil()); + const auto widthTiles = calcWidthTiles(_co, dirTiling.getOutputTileDims(), dirTiling.useCeil()); + + if (heightTiles.empty()) { + continue; + } + if (widthTiles.empty()) { + break; + } + + bool isOK = true; + double solutionCost = 0.0; + + for (const auto &heightTile : heightTiles) { + for (const auto &widthTile : widthTiles) { + // + // Limitations for Conv+Pool case. + // + + if (_co._withPool) { + if (widthTile.inputWithJunk % 2 != 0 || + heightTile.inputWithJunk % 2 != 0 || + widthTile.outputWithJunk % 2 != 0 || + widthTile.outputWithJunk <= 2 || + heightTile.outputWithJunk <= 2) { + isOK = false; + break; + } + } + + // + // Can use this tile. + // + + auto tileInfo = splitHwConvIntoOutChannelsTiles( // left asis, not new ver in new api + widthTile.inputWithJunk, heightTile.inputWithJunk, tileSizeDimC, + outputTileInitial[Dim::C], + _co._kernelSizeX, _co._kernelSizeY, _co._kernelStride); + + if (tileInfo.numDescr == 0) { + isOK = false; + break; + } + + // + // Output tile fits to CMX limitation. + // + + DimValues fullOutputTileDims; + fullOutputTileDims.set(Dim::W, widthTile.outputWithJunk); + fullOutputTileDims.set(Dim::H, heightTile.outputWithJunk); + fullOutputTileDims.set(Dim::C, outputTileInitial[Dim::C]); + + // TODO: support HCW + if (calculateHwBufferSize(fullOutputTileDims) > env.resources.cmxLimit) { + isOK = false; + break; + } + + // + // Calc tile cost. + // + + solutionCost += tileInfo.cost * numChannelTiles; + + // Alignment for output + if ((widthTile.outputStartIndex * sizeof(fp16_t)) % 16 != 0) { + solutionCost += 1.0 + * widthTile.outputWithJunk + * heightTile.outputWithJunk + * outputTileInitial[Dim::C]; + } + + // Alignment for input + if ((widthTile.inputStartIndex * sizeof(fp16_t)) % 16 != 0) { + solutionCost += 1.0 + * widthTile.inputWithJunk + * heightTile.inputWithJunk + * tileInfo.extendedInputDimC; + } + + // SoC overhead + solutionCost += 1.0 + * (numChannelTiles - 1) + * widthTile.outputWithJunk + * heightTile.outputWithJunk + * outputTileInitial[Dim::C]; + } + + if (!isOK) { + break; + } + } + + if (!isOK) { + continue; + } + + // + // Put to the pool of best options. + // + + const int totalNumTiles = numWidthTiles * numHeightTiles * numChannelTiles; + + const TilingOption to = + {numWidthTiles, numHeightTiles, numChannelTiles, totalNumTiles, solutionCost}; + tilingOptions.push(to); + + // Skip smaller SoC tiling. + break; + } + } + } + + dirTiling.resetInputTileDims(inputTileInitial); + dirTiling.resetOutputTileDims(outputTileInitial); + + return tilingOptions.sorted(); +} + +HWConvolutionTileLayoutCut HWConvolutionTilingSearcher::tileLayoutCut(const TilingOption &option) const { + return HWConvolutionTileLayoutCut(*_dirTiling, option); +} + +std::ostream& operator<<(std::ostream &o, const TilingOption &to) { + o << "WHC: " + << to.numWidthTiles << "x" + << to.numHeightTiles << "x" + << to.numChannelTiles + << " Tot: " << to.totalNumTiles << " " << " cost: " << to.cost; + + return o; +} + +// based on height of the tile for output tensor +SmallVector calcHeightTiles(const ConvolutionOptions &_co, + const DimValues &outputTileDims, bool useCeil) { + SmallVector heightTiles; + + if (outputTileDims[Dim::H] == _co._outputDims[Dim::H]) { + HwPlaneTileInfo info; + info.inputWithJunk = _co._inputDims[Dim::H]; + info.outputWithJunk = _co._outputDims[Dim::H]; + info.outputJunkBefore = 0; + info.outputJunkAfter = 0; + info.inputStartIndex = 0; + info.inputEndIndex = _co._inputDims[Dim::H]; + info.outputStartIndex = 0; + info.outputEndIndex = _co._outputDims[Dim::H]; + + heightTiles.emplace_back(info); + } else { + if (_co._withPool) { + heightTiles = splitIntoPlaneTilesWithPool( + _co._inputDims[Dim::H], + _co._kernelSizeY, + _co._kernelStride, + _co._paddingTop, + outputTileDims[Dim::H]); + } else { + heightTiles = splitIntoPlaneTiles( + _co._inputDims[Dim::H], + _co._outputDims[Dim::H], + _co._kernelSizeY, + _co._kernelStride, + _co._paddingTop, _co._paddingBottom, + outputTileDims[Dim::H], + useCeil); + } + } + + return heightTiles; +} + +SmallVector calcWidthTiles(const ConvolutionOptions &_co, + const DimValues &outputTileDims, bool useCeil) { + SmallVector widthTiles; + + if (outputTileDims[Dim::W] == _co._outputDims[Dim::W]) { + HwPlaneTileInfo info; + info.inputWithJunk = _co._inputDims[Dim::W]; + info.outputWithJunk = _co._outputDims[Dim::W]; + info.outputJunkBefore = 0; + info.outputJunkAfter = 0; + info.inputStartIndex = 0; + info.inputEndIndex = _co._inputDims[Dim::W]; + info.outputStartIndex = 0; + info.outputEndIndex = _co._outputDims[Dim::W]; + + widthTiles.emplace_back(info); + } else { + if (_co._withPool) { + widthTiles = splitIntoPlaneTilesWithPool( + _co._inputDims[Dim::W], + _co._kernelSizeX, + _co._kernelStride, + _co._paddingLeft, + outputTileDims[Dim::W]); + } else { + widthTiles = splitIntoPlaneTiles( + _co._inputDims[Dim::W], + _co._outputDims[Dim::W], + _co._kernelSizeX, + _co._kernelStride, + _co._paddingLeft, _co._paddingRight, + outputTileDims[Dim::W], + useCeil); + } + } + + return widthTiles; +} + +} // namespace HWTilingNS + +} // namespace vpu + diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/hw_conv_tiling/hw_stage_tiler.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/hw_conv_tiling/hw_stage_tiler.cpp new file mode 100644 index 00000000000000..040c5461c1bb7d --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/src/passes/hw_conv_tiling/hw_stage_tiler.cpp @@ -0,0 +1,481 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace vpu { + +const int BIASES_IND = -1; +const int SCALES_IND = -2; + +using TileWeightsMap = std::unordered_map; + +HWConvStageTiler::HWConvStageTiler(const HWConvStageOptions &so, const HWConvStageIO &sio, + const Model::Ptr &model, const Handle &origStage, + const StageBuilder::Ptr &_stageBuilder, const HwConvTilingPtr &tiling, + const bool makeExplicitPoolStage) { + hwInput = sio.origInput; + hwOutput = sio.origOutput; + + // + // Create explicit pool stage if tiling with pool is not possible + // + bool tileStageWithPool = so.withPool; + if (makeExplicitPoolStage) { + auto hwPoolInput = model->addNewData( + origStage->name(), + sio.origOutputDesc); + hwPoolInput->attrs().copyFrom(sio.origOutput->attrs()); + + auto hwPoolStage = model->addNewStage( + origStage->name() + "@Pool", + StageType::StubMaxPool, + origStage->origLayer(), + {hwPoolInput}, + {hwOutput}); + + hwPoolStage->attrs().set("kernelSizeX", so.poolKernelSizeX); + hwPoolStage->attrs().set("kernelSizeY", so.poolKernelSizeY); + + hwPoolStage->attrs().set("kernelStrideX", so.poolKernelStride); + hwPoolStage->attrs().set("kernelStrideY", so.poolKernelStride); + + hwPoolStage->attrs().set("padLeft", so.poolPadLeft); + hwPoolStage->attrs().set("padRight", so.poolPadRight); + hwPoolStage->attrs().set("padTop", so.poolPadTop); + hwPoolStage->attrs().set("padBottom", so.poolPadBottom); + + hwPoolStage->attrs().set("excludePad", false); + + hwPoolStage->attrs().set("tryHW", true); + + hwOutput = hwPoolInput; + tileStageWithPool = false; + } + + // + // Expand input/output if needed + // + + int totalExtendedInputDimC = 0; + int maxExtendedOutputDimC = 0; + + for (const auto& planeTile : tiling->planeTiles) { + for (const auto& channelTile : planeTile->channelTiles) { + totalExtendedInputDimC = std::max(totalExtendedInputDimC, channelTile->channelStartIndex + channelTile->extendedInputDimC); + maxExtendedOutputDimC = std::max(maxExtendedOutputDimC, channelTile->extendedOutputDimC); + } + } + + auto origOutputDimC = hwOutput->desc().dim(Dim::C); + + if (totalExtendedInputDimC > hwInput->desc().dim(Dim::C)) { + auto newDesc = hwInput->desc(); + newDesc.setDim(Dim::C, totalExtendedInputDimC); + + auto hwInputExtended = model->duplicateData( + hwInput, + "@extended", + newDesc); + + _stageBuilder->addExpandStage( + model, + origStage->name() + "@expand-input", + origStage->origLayer(), + hwInput, + hwInputExtended); + + hwInput = hwInputExtended; + } + + // + // Create HW biases + // + + auto& tileWeightsMap = sio.origWeights->attrs().getOrSet("weightsPerTile", TileWeightsMap()); + auto hwBiases = tileWeightsMap[BIASES_IND]; + if (hwBiases == nullptr) { + if (sio.origBiases->usage() == DataUsage::Fake) { + hwBiases = model->addFakeData(); + } else { + auto origBiasesContent = sio.origBiases->content(); + IE_ASSERT(origBiasesContent != nullptr); + + auto origBiasesPtr = origBiasesContent->get(); + IE_ASSERT(origBiasesPtr != nullptr); + + auto hwTileBiasesBlob = ie::make_shared_blob(InferenceEngine::TensorDesc( + ie::Precision::FP16, + {static_cast(maxExtendedOutputDimC)}, + ie::Layout::C)); + hwTileBiasesBlob->allocate(); + + auto hwTileBiasesBlobPtr = hwTileBiasesBlob->buffer().as(); + IE_ASSERT(hwTileBiasesBlobPtr != nullptr); + + std::fill_n(hwTileBiasesBlobPtr, maxExtendedOutputDimC, ie::PrecisionUtils::f32tof16(0.0f)); + std::copy_n(origBiasesPtr, origOutputDimC, hwTileBiasesBlobPtr); + + hwBiases = model->duplicateData( + sio.origBiases, + "@HW", + DataDesc({maxExtendedOutputDimC}), + ieBlobContent(hwTileBiasesBlob)); + + if (so.scaleFactor != 1.0f) { + auto hwBiasesScaled = model->duplicateData( + hwBiases, + formatString("@SCALE=%f", so.scaleFactor), + hwBiases->desc(), + scaleContent(hwBiases->content(), so.scaleFactor)); + hwBiasesScaled->attrs().getOrSet("scaleFactor", 1.0f) *= so.scaleFactor; + + hwBiases = hwBiasesScaled; + } + } + + tileWeightsMap[BIASES_IND] = hwBiases; + } + + // + // Create HW scales + // + + auto hwScales = tileWeightsMap[SCALES_IND]; + if (hwScales == nullptr) { + float fullScale = 1.0f / so.scaleFactor; + if (tiling->socTiles == 1 && so.reluScale != 1.0f) { + fullScale *= so.reluScale; + } + + if (fullScale == 1.0f) { + hwScales = model->addFakeData(); + } else { + hwScales = model->addConstData( + origStage->name() + "@scales", + DataDesc({maxExtendedOutputDimC}), + replicateContent(fullScale, maxExtendedOutputDimC)); + } + + tileWeightsMap[SCALES_IND] = hwScales; + } + + // + // Create HW tiles + // + + hwInputTiles.reserve(tiling->socTiles * tiling->sohTiles * tiling->sowTiles); + hwInputTilesOffsets.reserve(tiling->socTiles * tiling->sohTiles * tiling->sowTiles); + hwOutputTiles.reserve(tiling->socTiles * tiling->sohTiles * tiling->sowTiles); + hwOutputTilesOffsets.reserve(tiling->socTiles * tiling->sohTiles * tiling->sowTiles); + + for (const auto& planeTile : tiling->planeTiles) { + auto planeTilePostfix = getPlaneTilePostfix(planeTile); + + // + // Create output tile + // + + Data hwOutputPlaneTile; + + if (tiling->sohTiles == 1 && tiling->sowTiles == 1) { + hwOutputPlaneTile = hwOutput; + } else { + auto newDesc = hwOutput->desc(); + newDesc.setDim(Dim::W, planeTile->widthInfo.outputEndIndex - planeTile->widthInfo.outputStartIndex); + newDesc.setDim(Dim::H, planeTile->heightInfo.outputEndIndex - planeTile->heightInfo.outputStartIndex); + + hwOutputPlaneTile = model->duplicateData( + hwOutput, + planeTilePostfix, + newDesc); + + hwOutputTiles.emplace_back(hwOutputPlaneTile); + hwOutputTilesOffsets.emplace_back( + DimValues({ + {Dim::W, planeTile->widthInfo.outputStartIndex}, + {Dim::H, planeTile->heightInfo.outputStartIndex} + })); + } + + // + // Add alignment to output tile if needed + // + + if ((planeTile->widthInfo.outputStartIndex * sizeof(fp16_t)) % 16 != 0) { + auto hwOutputPlaneTileAligned = model->duplicateData( + hwOutputPlaneTile, + "@aligned"); + + _stageBuilder->addCopyStage( + model, + origStage->name() + planeTilePostfix + "@align-output-ptr", + origStage->origLayer(), + hwOutputPlaneTileAligned, + hwOutputPlaneTile); + + hwOutputPlaneTile = hwOutputPlaneTileAligned; + } + + Data prevPartialSum; + + for (const auto& channelTile : planeTile->channelTiles) { + auto channelTilePostfix = getChannelTilePostfix(channelTile); + + auto tilePostfix = planeTilePostfix + channelTilePostfix; + + auto hwOutputTile = hwOutputPlaneTile; + + // + // Create input tile + // + + Data hwInputTile; + + if (tiling->sohTiles == 1 && tiling->sowTiles == 1 && tiling->socTiles == 1) { + hwInputTile = hwInput; + } else { + auto newDesc = hwInput->desc(); + newDesc.setDim(Dim::W, planeTile->widthInfo.inputWithJunk); + newDesc.setDim(Dim::H, planeTile->heightInfo.inputWithJunk); + newDesc.setDim(Dim::C, channelTile->extendedInputDimC); + + hwInputTile = model->duplicateData( + hwInput, + tilePostfix, + newDesc); + + hwInputTiles.emplace_back(hwInputTile); + hwInputTilesOffsets.emplace_back( + DimValues({ + {Dim::W, planeTile->widthInfo.inputStartIndex}, + {Dim::H, planeTile->heightInfo.inputStartIndex}, + {Dim::C, channelTile->channelStartIndex} + })); + } + + // + // Add alignment to input tile if needed + // + + if ((planeTile->widthInfo.inputStartIndex * sizeof(fp16_t)) % 16 != 0) { + auto hwInputTileAligned = model->duplicateData( + hwInputTile, + "@aligned"); + + _stageBuilder->addCopyStage( + model, + origStage->name() + tilePostfix + "@align-input-ptr", + origStage->origLayer(), + hwInputTile, + hwInputTileAligned); + + hwInputTile = hwInputTileAligned; + } + + // + // Process partial output for split-over-channels + // + + if (tiling->socTiles > 1) { + auto hwConvPartialOutput = model->duplicateData( + hwOutputTile, + channelTilePostfix + "@partial"); + + if (channelTile->socInd == 0) { + prevPartialSum = hwConvPartialOutput; + } else { + auto sumPartialOutput = hwOutputTile; + if (channelTile->socInd < tiling->socTiles - 1 || so.withReLU || so.withClamp) { + sumPartialOutput = model->duplicateData( + hwOutputTile, + channelTilePostfix + "@accum"); + } + + _stageBuilder->addSumStage( + model, + origStage->name() + tilePostfix + "@accum", + origStage->origLayer(), + prevPartialSum, hwConvPartialOutput, + sumPartialOutput); + + if (channelTile->socInd == tiling->socTiles - 1 && so.withReLU) { + _stageBuilder->addReLUStage( + model, + origStage->name() + tilePostfix + "@ReLU", + origStage->origLayer(), + so.negativeSlope, + sumPartialOutput, + hwOutputTile); + } + + if (channelTile->socInd == tiling->socTiles - 1 && so.withClamp) { + _stageBuilder->addClampStage( + model, + origStage->name() + tilePostfix + "@Clamp", + origStage->origLayer(), + 0.0, + so.clampMax, + sumPartialOutput, + hwOutputTile); + } + + prevPartialSum = sumPartialOutput; + } + + hwOutputTile = hwConvPartialOutput; + } + + // + // Process output junk if needed + // + + if (planeTile->heightInfo.outputJunkBefore != 0 || + planeTile->heightInfo.outputJunkAfter != 0 || + planeTile->widthInfo.outputJunkBefore != 0 || + planeTile->widthInfo.outputJunkAfter != 0) { + auto newDesc = hwOutputTile->desc(); + newDesc.setDim(Dim::W, planeTile->widthInfo.outputWithJunk); + newDesc.setDim(Dim::H, planeTile->heightInfo.outputWithJunk); + + auto hwOutputTileWithJunk = model->duplicateData( + hwOutputTile, + "@with-junk", + newDesc); + + DimValues innerOffset; + innerOffset.set(Dim::W, planeTile->widthInfo.outputJunkBefore); + innerOffset.set(Dim::H, planeTile->heightInfo.outputJunkBefore); + + _stageBuilder->addShrinkStage( + model, + origStage->name() + tilePostfix + "@remove-junk", + origStage->origLayer(), + hwOutputTileWithJunk, + hwOutputTile, + innerOffset); + + hwOutputTile = hwOutputTileWithJunk; + } + + // + // Create tile weights + // + + auto hwTileWeights = tileWeightsMap[channelTile->socInd]; + + if (hwTileWeights == nullptr) { + hwTileWeights = model->duplicateData( + sio.origWeights, + "@HW" + channelTilePostfix, + DataDesc({8, so.kernelSizeX * so.kernelSizeY, channelTile->extendedInputDimC, channelTile->extendedOutputDimC / 8}), + std::make_shared( + sio.origWeights->content(), + sio.origWeights->desc(), + channelTile->numInputChannels, + channelTile->channelStartIndex)); + + if (so.scaleFactor != 1.0f) { + auto hwTileWeightsScaled = model->duplicateData( + hwTileWeights, + formatString("@SCALE=%f", so.scaleFactor), + hwTileWeights->desc(), + scaleContent(hwTileWeights->content(), so.scaleFactor)); + hwTileWeightsScaled->attrs().getOrSet("scaleFactor", 1.0f) *= so.scaleFactor; + + hwTileWeights = hwTileWeightsScaled; + } + + tileWeightsMap[channelTile->socInd] = hwTileWeights; + } + + // + // Create tile biases + // + + Data hwTileBiases; + + if (channelTile->socInd > 0) { + hwTileBiases = model->addFakeData(); + } else { + hwTileBiases = hwBiases; + } + + // + // Create HW stage for tile + // + + auto hwOutputTileDims = hwOutputTile->desc().dims(); + if (tileStageWithPool) { + hwOutputTileDims.set(Dim::W, hwOutputTileDims[Dim::W] * so.poolKernelStride - so.poolPadLeft - so.poolPadRight); + hwOutputTileDims.set(Dim::H, hwOutputTileDims[Dim::H] * so.poolKernelStride - so.poolPadTop - so.poolPadBottom); + } + + auto hwPad = getHwPaddingInfo( + hwInputTile->desc().dims(), hwOutputTileDims, + so.kernelSizeX, so.kernelSizeY, + so.kernelStride, so.kernelStride, + so.padLeft, so.padTop); + + auto hwStage = model->addNewStage( + origStage->name() + tilePostfix, + StageType::MyriadXHwOp, + origStage->origLayer(), + {hwInputTile, hwTileWeights, hwTileBiases, hwScales}, + {hwOutputTile}); + + hwStage->attrs().set("hwOpType", tileStageWithPool ? HwOpType::CONV_POOL : HwOpType::CONV); + + hwStage->attrs().set("kernelSizeX", so.kernelSizeX); + hwStage->attrs().set("kernelSizeY", so.kernelSizeY); + hwStage->attrs().set("kernelStride", so.kernelStride); + + if (tileStageWithPool) { + hwStage->attrs().set("poolKernelSizeX", so.poolKernelSizeX); + hwStage->attrs().set("poolKernelSizeY", so.poolKernelSizeY); + } + + hwStage->attrs().set("pad", hwPad); + + hwStage->attrs().set("tiling", channelTile->finalTiles); + + if (tiling->socTiles > 1) { + hwStage->attrs().set("withReLU", false); + hwStage->attrs().set("withClamp", false); + } else { + hwStage->attrs().set("withReLU", so.withReLU); + hwStage->attrs().set("a0", so.a0); + hwStage->attrs().set("a1", so.a1); + hwStage->attrs().set("negativeSlope", so.negativeSlope); + + hwStage->attrs().set("withClamp", so.withClamp); + hwStage->attrs().set("clampMax", so.clampMax); + } + + hwStage->attrs().set("scaleFactor", so.scaleFactor); + } + } +} + +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/hw_fc_tiling.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/hw_fc_tiling.cpp index e0eaae62cae520..351384e7415772 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/hw_fc_tiling.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/hw_fc_tiling.cpp @@ -101,39 +101,31 @@ class HwFcRelayoutStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector&, - ScalePropagationStep) override { + ScalePropagationStep, + StageDataInfo&) override { VPU_THROW_EXCEPTION << "Must never be called"; } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); - - _orderInfo.setInput(_inputEdges[0], input->desc().dimsOrder().createMovedDim(Dim::C, 2)); - _orderInfo.setOutput(_outputEdges[0], output->desc().dimsOrder().createMovedDim(Dim::C, 2)); + orderInfo.setInput(inputEdge(0), input->desc().dimsOrder().createMovedDim(Dim::C, 2)); + orderInfo.setOutput(outputEdge(0), output->desc().dimsOrder().createMovedDim(Dim::C, 2)); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto output = _outputEdges[0]->output(); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + auto output = outputEdge(0)->output(); - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement().add(1, DimStride::Aligned)); + stridesInfo.setOutput(outputEdge(0), StridesRequirement().add(1, DimStride::Aligned)); } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } StageSHAVEsRequirements getSHAVEsRequirementsImpl() const override { @@ -141,18 +133,15 @@ class HwFcRelayoutStage final : public StageNode { } void finalCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer&) const override { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeOldBuffer(handle_from_this(), serializer); output->serializeOldBuffer(handle_from_this(), serializer); @@ -273,10 +262,10 @@ void PassImpl::run(const Model::Ptr& model) { continue; } - model->disconnectStageDatas(origStage); + model->disconnectStage(origStage); // - // Broadcast input/output if needed + // Expand input/output if needed // auto origInputDimC = hwInput->desc().dim(Dim::C); @@ -291,9 +280,9 @@ void PassImpl::run(const Model::Ptr& model) { "@extended", newDesc); - _stageBuilder->addBroadcastStage( + _stageBuilder->addExpandStage( model, - origStage->name() + "@broadcast-input", + origStage->name() + "@expand-input", origStage->origLayer(), hwInput, hwInputExtended); diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/hw_padding.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/hw_padding.cpp index f2acbdd280a6d5..183c46decf0fb3 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/hw_padding.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/hw_padding.cpp @@ -80,25 +80,19 @@ bool supportedPaddingPool(const Stage& stage) { kernelStride, kernelStride, padLeft, padTop); - bool originalUnsupportedPad = ( - (padRight != padLeft && padRight != padLeft + 1) || - (padBottom != padTop && padBottom != padTop + 1) || - (padLeft != 0 && padLeft != (kernelSizeX / 2)) || - (padRight != 0 && padRight != (kernelSizeX / 2)) || - (padTop != 0 && padTop != (kernelSizeY / 2)) || - (padBottom != 0 && padBottom != (kernelSizeY / 2))); - - bool hwUnsupportedPad = ( - (hwInitialPad.right != hwInitialPad.left && hwInitialPad.right != hwInitialPad.left + 1) || - (hwInitialPad.bottom != hwInitialPad.top && hwInitialPad.bottom != hwInitialPad.top + 1) || - (hwInitialPad.left != 0 && hwInitialPad.left != (kernelSizeX / 2)) || - (hwInitialPad.right != 0 && hwInitialPad.right != (kernelSizeX / 2)) || - (hwInitialPad.top != 0 && hwInitialPad.top != (kernelSizeY / 2)) || - (hwInitialPad.bottom != 0 && hwInitialPad.bottom != (kernelSizeY / 2))); - - return !originalUnsupportedPad && - !hwUnsupportedPad && - !forcePaddingStage; + // + // HW unit supports pooling with even-sized kernel with such asymmetrical paddings. + // But it does not support inverted paddings. + // For odd-sized kernels supported paddings are symmetrical. + // + + bool isPadSupported = + (hwInitialPad.left == 0 || hwInitialPad.left == kernelSizeX / 2) && + (hwInitialPad.right == 0 || hwInitialPad.right == (kernelSizeX - 1) / 2) && + (hwInitialPad.top == 0 || hwInitialPad.top == kernelSizeY / 2) && + (hwInitialPad.bottom == 0 || hwInitialPad.bottom == (kernelSizeY - 1) / 2); + + return isPadSupported && !forcePaddingStage; } bool supportedPaddingConv(const Stage& stage) { @@ -111,15 +105,20 @@ bool supportedPaddingConv(const Stage& stage) { auto padTop = stage->attrs().get("padTop"); auto padBottom = stage->attrs().get("padBottom"); - bool kernelIsOdd = kernelSizeX % 2 == 1 && kernelSizeY % 2 == 1; + // + // HW unit supports convolution with even-sized kernel with such asymmetrical paddings. + // But it does not support inverted paddings. + // For odd-sized kernels supported paddings are symmetrical. + // + bool paddingsAreZeros = padLeft == 0 && padTop == 0 && padRight == 0 && padBottom == 0; bool paddingsAreSupported = - padLeft == (kernelSizeX - 1) / 2 && - padTop == (kernelSizeY - 1) / 2 && - padRight == kernelSizeX / 2 && - padBottom == kernelSizeY / 2; + padLeft == kernelSizeX / 2 && + padTop == kernelSizeY / 2 && + padRight == (kernelSizeX - 1) / 2 && + padBottom == (kernelSizeY - 1) / 2; - return paddingsAreZeros || (kernelIsOdd && paddingsAreSupported); + return paddingsAreZeros || paddingsAreSupported; } void insertPaddingStageBefore(const Model::Ptr& model, StageBuilder::Ptr& stageBuilder, const Stage& origStage) { diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/hw_pooling_tiling.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/hw_pooling_tiling.cpp index da0011a6ef90cc..2c38e9275f4885 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/hw_pooling_tiling.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/hw_pooling_tiling.cpp @@ -21,496 +21,24 @@ #include #include #include +#include +#include +#include namespace vpu { namespace { -const int CHANS_PER_DESCR = 16; - -HwPoolTileInfo splitPooling(int outZ) { - HwPoolTileInfo tiles; - tiles.mode = HwOpMode::MODE_16_16; - tiles.numDescr = (outZ + CHANS_PER_DESCR - 1) / CHANS_PER_DESCR; - tiles.chansPerDescr = CHANS_PER_DESCR; - return tiles; -} - -class Optimizer final { -public: - Optimizer(const std::string& stageName, - const DimValues& inputDims, const DimValues& outputDims, - int kernelSizeX, int kernelSizeY, - int kernelStride, - int padLeft, int padRight, - int padTop, int padBottom) - : _stageName(stageName), - _inputDims(inputDims), _outputDims(outputDims), - _kernelSizeX(kernelSizeX), _kernelSizeY(kernelSizeY), - _kernelStride(kernelStride), - _padLeft(padLeft), _padRight(padRight), - _padTop(padTop), _padBottom(padBottom) { - } - - bool optimize() { - initTileSizes(); - - if (!selectBestTile()) { - return false; - } - - return createTiles(); - } - - const HwPoolTilingPtr& getTiling() const { - return _tiling; - } - -private: - void initTileSizes() { - int tempX = _inputDims[Dim::W] + _padLeft + _padRight - _kernelSizeX; - int tempY = _inputDims[Dim::H] + _padTop + _padBottom - _kernelSizeY; - - int outWidthWithOutCeil = (tempX + _kernelStride) / _kernelStride; - int outHeightWithOutCeil = (tempY + _kernelStride) / _kernelStride; - - int outWidthWithCeil = static_cast(std::ceil(static_cast(tempX) / _kernelStride + 1)); - int outHeightWithCeil = static_cast(std::ceil(static_cast(tempY) / _kernelStride + 1)); - - if ((_outputDims[Dim::W] != outWidthWithCeil) && (_outputDims[Dim::W] != outWidthWithOutCeil)) { - VPU_THROW_EXCEPTION - << "Internal error: Output in " << _stageName << " has incorrect width dimension. Expected: " - << outWidthWithCeil << " or " << outWidthWithOutCeil << " Actual: " << _outputDims[Dim::W]; - } - - if ((_outputDims[Dim::H] != outHeightWithCeil) && (_outputDims[Dim::H] != outHeightWithOutCeil)) { - VPU_THROW_EXCEPTION - << "Internal error: Output in " << _stageName << " has incorrect height dimension. Expected: " - << outHeightWithCeil << " or " << outHeightWithOutCeil << " Actual: " << _outputDims[Dim::H]; - } - - if ((_outputDims[Dim::W] == outWidthWithCeil) && (_outputDims[Dim::H] == outHeightWithCeil)) { - _useCeil = true; - } else { - IE_ASSERT((_outputDims[Dim::W] == outWidthWithOutCeil) && (_outputDims[Dim::H] == outHeightWithOutCeil)); - } - - _inputTileDims.set(Dim::W, _inputDims[Dim::W]); - _inputTileDims.set(Dim::H, _inputDims[Dim::H]); - _inputTileDims.set(Dim::C, _inputDims[Dim::C]); - _inputTileDims.set(Dim::N, _inputDims.get(Dim::N, 1)); - - _outputTileDims.set(Dim::W, _outputDims[Dim::W]); - _outputTileDims.set(Dim::H, _outputDims[Dim::H]); - _outputTileDims.set(Dim::C, _outputDims[Dim::C]); - _outputTileDims.set(Dim::N, _outputDims.get(Dim::N, 1)); - } - - bool selectBestTile() { - struct Solution final { - int numWidthTiles = 0; - int numHeightTiles = 0; - int numBatchTiles = 0; - int totalNumTiles = 0; - double cost = std::numeric_limits::max(); - }; - - const auto& env = CompileEnv::get(); - - // TODO: estimate this numbers - const int maxNumWidthTiles = 15; - const int maxNumHeightTiles = 15; - const int maxNumBatchTiles = _outputDims.get(Dim::N, 1); - - Solution bestSol; - - auto outputTileCopy = _outputTileDims; - - for (int numBatchTiles = 1; numBatchTiles <= maxNumBatchTiles; numBatchTiles++) { - // - // Filter-out misaligned SoN tiles. - // - - if (outputTileCopy[Dim::N] % numBatchTiles != 0) { - continue; - } - - auto tileDimN = outputTileCopy[Dim::N] / numBatchTiles; - - for (int numWidthTiles = 1; numWidthTiles <= maxNumWidthTiles; numWidthTiles++) { - auto inputTileDimW = divUp(_inputDims[Dim::W], numWidthTiles); - - // - // Filter-out too small SoW tiles. - // - - if (numWidthTiles > 1 && (inputTileDimW < 8 || inputTileDimW < _kernelSizeX)) { - break; - } - - for (int numHeightTiles = 1; numHeightTiles <= maxNumHeightTiles ; numHeightTiles++) { - auto inputTileDimH = divUp(_inputDims[Dim::H], numHeightTiles); - - // - // Filter-out too small SoH tiles. - // - - if (numHeightTiles > 1 && inputTileDimH < _kernelSizeY) { - break; - } - - // - // Try current tile size. - // - - _inputTileDims.set(Dim::W, inputTileDimW); - _inputTileDims.set(Dim::H, inputTileDimH); - _inputTileDims.set(Dim::N, tileDimN); - - _outputTileDims = outputTileCopy; - _outputTileDims.set(Dim::N, tileDimN); - correctOutputPlaneSize(); - - // - // Check that tiling is valid. - // - - auto heightTiles = calcHeightTiles(); - auto widthTiles = calcWidthTiles(); - - if (heightTiles.empty()) { - continue; - } - if (widthTiles.empty()) { - break; - } - - bool isOK = true; - double solutionCost = 0.0; - - for (const auto& heightTile : heightTiles) { - for (const auto& widthTile : widthTiles) { - // - // Output tile fits to CMX limitation. - // - - DimValues fullOutputTileDims; - fullOutputTileDims.set(Dim::W, widthTile.outputWithJunk); - fullOutputTileDims.set(Dim::H, heightTile.outputWithJunk); - fullOutputTileDims.set(Dim::C, _outputTileDims[Dim::C]); - fullOutputTileDims.set(Dim::N, _outputTileDims[Dim::N]); - - // TODO: support HCW - if (calculateHwBufferSize(fullOutputTileDims) > env.resources.cmxLimit) { - isOK = false; - break; - } - - // - // `linesPerChan` restrictions. - // - - if (heightTile.inputWithJunk < _kernelSizeY) { - isOK = false; - break; - } - - const uint32_t LOCAL_RAM_SIZE = 128 * 1024; - const uint32_t CMX_DATA_BIT_WIDTH = 128; - - uint32_t sizeOfBlock = LOCAL_RAM_SIZE >> static_cast(HwOpMode::MODE_16_16); - uint32_t bytesPerPixel = 1 << (1 - static_cast(HwDataMode::FP16)); - uint32_t pixelsPerCMXLine = CMX_DATA_BIT_WIDTH / (bytesPerPixel * 8u); - uint32_t localLineStride = (widthTile.inputWithJunk + (pixelsPerCMXLine - 1)) / pixelsPerCMXLine; - uint32_t chanPerBlock = 1; - uint32_t availableBytesPerChan = sizeOfBlock / chanPerBlock; - uint32_t bytesPerLine = localLineStride * pixelsPerCMXLine * bytesPerPixel; - uint32_t linesPerChan = availableBytesPerChan / bytesPerLine; - if (linesPerChan < _kernelSizeY) { - isOK = false; - break; - } - - // - // Replicate padding in case of large input plane - #-16783. - // - - DimValues fullInputTileDims; - fullInputTileDims.set(Dim::W, widthTile.inputWithJunk); - fullInputTileDims.set(Dim::H, heightTile.inputWithJunk); - - auto pad = getHwPaddingInfo( - fullInputTileDims, fullOutputTileDims, - _kernelSizeX, _kernelSizeY, - _kernelStride, _kernelStride, - _padLeft, _padTop); - - if (pad.enable && (pad.left > 0 || pad.right > 0 || pad.bottom > 0)) { - int memPerPlane = alignVal( - fullInputTileDims[Dim::W], 8) * sizeof(fp16_t) - * ((fullInputTileDims[Dim::H] - 1) + (_kernelSizeY - 1)); - int memLimit = pad.bottom > 0 ? 0x800 : 0x1000; - if (memPerPlane > memLimit) { - isOK = false; - break; - } - } - - // - // Calc tile cost. - // - - auto noOfBlocks = 1 << static_cast(HwOpMode::MODE_16_16); - solutionCost += 1.0 - * ((_inputTileDims[Dim::C] * _inputTileDims[Dim::N]) / noOfBlocks) * _kernelSizeX * _kernelSizeY - * numBatchTiles; - - // Alignment for output - if ((widthTile.outputStartIndex * sizeof(fp16_t)) % 16 != 0) { - solutionCost += 1.0 - * widthTile.outputWithJunk - * heightTile.outputWithJunk - * _outputTileDims[Dim::C] - * _outputTileDims[Dim::N]; - } - - // Alignment for input - if ((widthTile.inputStartIndex * sizeof(fp16_t)) % 16 != 0) { - solutionCost += 1.0 - * widthTile.inputWithJunk - * heightTile.inputWithJunk - * _inputTileDims[Dim::C] - * _inputTileDims[Dim::N]; - } - } - - if (!isOK) { - break; - } - } - - if (!isOK) { - continue; - } - - // - // Compare with current best solution. - // - - Solution curSol; - curSol.numWidthTiles = numWidthTiles; - curSol.numHeightTiles = numHeightTiles; - curSol.numBatchTiles = numBatchTiles; - curSol.totalNumTiles = numWidthTiles * numHeightTiles * numBatchTiles; - curSol.cost = solutionCost; - - if (curSol.cost < bestSol.cost || (isDoubleEqual(curSol.cost, bestSol.cost) && curSol.totalNumTiles < bestSol.totalNumTiles)) { - bestSol = curSol; - } - } - } - } - - if (bestSol.totalNumTiles == 0) { - return false; - } - - int inputTileDimW = divUp(_inputDims[Dim::W], bestSol.numWidthTiles); - int inputTileDimH = divUp(_inputDims[Dim::H], bestSol.numHeightTiles); - auto tileDimN = outputTileCopy[Dim::N] / bestSol.numBatchTiles; - - _inputTileDims.set(Dim::W, inputTileDimW); - _inputTileDims.set(Dim::H, inputTileDimH); - _inputTileDims.set(Dim::N, tileDimN); - - _outputTileDims = outputTileCopy; - _outputTileDims.set(Dim::N, tileDimN); - correctOutputPlaneSize(); - - return true; - } - - bool createTiles() { - auto heightTiles = calcHeightTiles(); - IE_ASSERT(!heightTiles.empty()); - - auto widthTiles = calcWidthTiles(); - IE_ASSERT(!widthTiles.empty()); - - _tiling = std::make_shared(); - _tiling->sohTiles = heightTiles.size(); - _tiling->sowTiles = widthTiles.size(); - _tiling->socTiles = divUp(_inputDims.get(Dim::N, 1), _inputTileDims[Dim::N]); - - for (int sohInd = 0; sohInd < _tiling->sohTiles; ++sohInd) { - const auto& heightTileInfo = heightTiles[sohInd]; - - for (int sowInd = 0; sowInd < _tiling->sowTiles; ++sowInd) { - const auto& widthTileInfo = widthTiles[sowInd]; - - auto planeTile = std::make_shared(); - planeTile->parent = _tiling; - - planeTile->sohInd = sohInd; - planeTile->sowInd = sowInd; - - planeTile->heightInfo = heightTileInfo; - planeTile->widthInfo = widthTileInfo; - - for (int socInd = 0; socInd < _tiling->socTiles; ++socInd) { - auto channelTile = std::make_shared(); - channelTile->parent = planeTile; - - channelTile->socInd = socInd; - - channelTile->finalTiles = splitPooling(_inputTileDims[Dim::C] * _inputTileDims[Dim::N]); - - if (channelTile->finalTiles.numDescr == 0) { - return false; - } - - channelTile->channelStartIndex = socInd * _inputTileDims[Dim::N]; - channelTile->numInputChannels = _inputTileDims[Dim::N]; - - planeTile->channelTiles.emplace_back(channelTile); - } - - _tiling->planeTiles.emplace_back(planeTile); - } - } - - return true; - } - -private: - void correctOutputPlaneSize() { - int maxOutputWidth = calcOutputSize(_inputTileDims[Dim::W], _kernelSizeX, _kernelStride, _padLeft, _padRight, _useCeil); - _outputTileDims.set(Dim::W, std::min(_outputTileDims[Dim::W], maxOutputWidth)); - - int maxOutputHeight = calcOutputSize(_inputTileDims[Dim::H], _kernelSizeY, _kernelStride, _padTop, _padBottom, _useCeil); - _outputTileDims.set(Dim::H, std::min(_outputTileDims[Dim::H], maxOutputHeight)); - } - - SmallVector calcHeightTiles() { - SmallVector heightTiles; - - if (_outputTileDims[Dim::H] == _outputDims[Dim::H]) { - HwPlaneTileInfo info; - info.inputWithJunk = _inputDims[Dim::H]; - info.outputWithJunk = _outputDims[Dim::H]; - info.outputJunkBefore = 0; - info.outputJunkAfter = 0; - info.inputStartIndex = 0; - info.inputEndIndex = _inputDims[Dim::H]; - info.outputStartIndex = 0; - info.outputEndIndex = _outputDims[Dim::H]; - - heightTiles.emplace_back(info); - } else { - heightTiles = splitIntoPlaneTiles( - _inputDims[Dim::H], - _outputDims[Dim::H], - _kernelSizeY, - _kernelStride, - _padTop, _padBottom, - _outputTileDims[Dim::H], - false, - _useCeil); - } - - return heightTiles; - } - - SmallVector calcWidthTiles() { - SmallVector widthTiles; - - if (_outputTileDims[Dim::W] == _outputDims[Dim::W]) { - HwPlaneTileInfo info; - info.inputWithJunk = _inputDims[Dim::W]; - info.outputWithJunk = _outputDims[Dim::W]; - info.outputJunkBefore = 0; - info.outputJunkAfter = 0; - info.inputStartIndex = 0; - info.inputEndIndex = _inputDims[Dim::W]; - info.outputStartIndex = 0; - info.outputEndIndex = _outputDims[Dim::W]; - - widthTiles.emplace_back(info); - } else { - widthTiles = splitIntoPlaneTiles( - _inputDims[Dim::W], - _outputDims[Dim::W], - _kernelSizeX, - _kernelStride, - _padLeft, _padRight, - _outputTileDims[Dim::W], - true, - _useCeil); - } - - return widthTiles; - } - -private: - std::string _stageName; - - DimValues _inputDims; - DimValues _outputDims; - - int _kernelSizeX = 0; - int _kernelSizeY = 0; - int _kernelStride = 0; - int _padLeft = 0; - int _padRight = 0; - int _padTop = 0; - int _padBottom = 0; - - DimValues _inputTileDims; - DimValues _outputTileDims; - - HwPoolTilingPtr _tiling; - - bool _useCeil = false; -}; - class PassImpl final : public Pass { public: - explicit PassImpl(const StageBuilder::Ptr& stageBuidler) : _stageBuidler(stageBuidler) {} + explicit PassImpl(const StageBuilder::Ptr& stageBuidler) : _stageBuilder(stageBuidler) {} void run(const Model::Ptr& model) override; private: - StageBuilder::Ptr _stageBuidler; + StageBuilder::Ptr _stageBuilder; }; -HwPaddingInfo getPoolPadding(const HwPlaneTilePtr& tile, - const DimValues& dims, - int kernelSizeX, - int kernelSizeY, - int kernelStrideX, - int kernelStrideY, - int padLeft, - int padRight, - int padTop, - int padBottom) { - const auto& widthInfo = tile->widthInfo; - const auto& heightInfo = tile->heightInfo; - - auto padW = (widthInfo.outputWithJunk - 1)*kernelStrideX + kernelSizeX - widthInfo.inputWithJunk; - auto padH = (heightInfo.outputWithJunk - 1)*kernelStrideY + kernelSizeY - heightInfo.inputWithJunk; - - HwPaddingInfo pad; - - pad.left = padLeft; - pad.right = (dims[Dim::W] <= widthInfo.inputEndIndex) ? padRight : padW - pad.left; - pad.top = padTop; - pad.bottom = (dims[Dim::H] <= heightInfo.inputEndIndex) ? padBottom : padH - pad.top; - - pad.enable = pad.left || pad.right || pad.top || pad.bottom; - - return pad; -} - void PassImpl::run(const Model::Ptr& model) { VPU_PROFILE(hwPoolTiling); @@ -525,51 +53,46 @@ void PassImpl::run(const Model::Ptr& model) { continue; } - auto origInput = origStage->input(0); - auto origOutput = origStage->output(0); - - auto kernelSizeX = origStage->attrs().get("kernelSizeX"); - auto kernelSizeY = origStage->attrs().get("kernelSizeY"); - auto kernelStride = origStage->attrs().get("kernelStrideX"); - auto padLeft = origStage->attrs().get("padLeft"); - auto padRight = origStage->attrs().get("padRight"); - auto padTop = origStage->attrs().get("padTop"); - auto padBottom = origStage->attrs().get("padBottom"); - - auto withReLU = origStage->attrs().getOrDefault("withReLU", false); - - auto hwInput = origInput; - auto hwOutput = origOutput; + const HWPoolStageOptions so(origStage); + const HWPoolStageIO sio(origStage, origStage->output(0)); // // Try to find "best" tiling // - Optimizer opt(origStage->name(), - hwInput->desc().dims(), hwOutput->desc().dims(), - kernelSizeX, kernelSizeY, - kernelStride, - padLeft, padRight, padTop, padBottom); - - if (!opt.optimize()) { + const size_t tilingsCount = 1; + const HWTilingNS::Direction direction = + HWTilingNS::Direction::INPUT_TO_OUTPUT; + // HWTilingNS::Direction::OUTPUT_TO_INPUT; + + const HWTilingNS::HWPoolingTiler tiler( + HWTilingNS::ConvolutionOptions(origStage->name(), + sio.origInput->desc().dims(), sio.origOutput->desc().dims(), + sio.origOutput->desc().dims(), + so.kernelSizeX, so.kernelSizeY, + so.kernelStride, + so.padLeft, so.padRight, so.padTop, so.padBottom, false), + direction, tilingsCount); + + if (!tiler.isTilingPossible()) { origStage->attrs().set("tryHW", false); - auto swOutput = origOutput; - if (withReLU) { + auto swOutput = sio.origOutput; + if (so.withReLU) { swOutput = model->addNewData( origStage->name(), - origOutput->desc()); - swOutput->attrs().copyFrom(origOutput->attrs()); + sio.origOutput->desc()); + swOutput->attrs().copyFrom(sio.origOutput->attrs()); model->replaceStageOutput(origStage->outputEdge(0), swOutput); - _stageBuidler->addReLUStage( + _stageBuilder->addReLUStage( model, origStage->name() + "@ReLU", origStage->origLayer(), 0.0, swOutput, - origOutput); + sio.origOutput); } continue; @@ -579,211 +102,36 @@ void PassImpl::run(const Model::Ptr& model) { // Create HW tiles // - model->disconnectStageDatas(origStage); - - const auto& tiling = opt.getTiling(); - - DataVector hwInputTiles; - std::vector hwInputTilesOffsets; - - DataVector hwOutputTiles; - std::vector hwOutputTilesOffsets; + model->disconnectStage(origStage); - hwInputTiles.reserve(tiling->socTiles * tiling->sohTiles * tiling->sowTiles); - hwInputTilesOffsets.reserve(tiling->socTiles * tiling->sohTiles * tiling->sowTiles); - hwOutputTiles.reserve(tiling->socTiles * tiling->sohTiles * tiling->sowTiles); - hwOutputTilesOffsets.reserve(tiling->socTiles * tiling->sohTiles * tiling->sowTiles); - for (const auto& planeTile : tiling->planeTiles) { - for (const auto& channelTile : planeTile->channelTiles) { - auto tilePostfix = getPlaneTilePostfix(planeTile) + getChannelTilePostfix(channelTile); - - // - // Create input tile - // - - Data hwInputTile; - - if (tiling->sohTiles == 1 && tiling->sowTiles == 1 && tiling->socTiles == 1) { - hwInputTile = hwInput; - } else { - auto newDesc = hwInput->desc(); - newDesc.setDim(Dim::W, planeTile->widthInfo.inputWithJunk); - newDesc.setDim(Dim::H, planeTile->heightInfo.inputWithJunk); - newDesc.setDim(Dim::N, channelTile->numInputChannels); - - hwInputTile = model->duplicateData( - hwInput, - tilePostfix, - newDesc); - - hwInputTiles.emplace_back(hwInputTile); - hwInputTilesOffsets.emplace_back( - DimValues({ - {Dim::W, planeTile->widthInfo.inputStartIndex}, - {Dim::H, planeTile->heightInfo.inputStartIndex}, - {Dim::N, channelTile->channelStartIndex} - })); - } - - // - // Add alignement to input tile if needed - // - - if ((planeTile->widthInfo.inputStartIndex * sizeof(fp16_t)) % 16 != 0) { - auto hwInputTileAligned = model->duplicateData( - hwInputTile, - "@aligned"); - - _stageBuidler->addCopyStage( - model, - origStage->name() + tilePostfix + "@align-input-ptr", - origStage->origLayer(), - hwInputTile, - hwInputTileAligned); - - hwInputTile = hwInputTileAligned; - } - - // - // Create output tile - // - - Data hwOutputTile; - - if (tiling->sohTiles == 1 && tiling->sowTiles == 1 && tiling->socTiles == 1) { - hwOutputTile = hwOutput; - } else { - auto newDesc = hwOutput->desc(); - newDesc.setDim(Dim::W, planeTile->widthInfo.outputEndIndex - planeTile->widthInfo.outputStartIndex); - newDesc.setDim(Dim::H, planeTile->heightInfo.outputEndIndex - planeTile->heightInfo.outputStartIndex); - newDesc.setDim(Dim::N, channelTile->numInputChannels); - - hwOutputTile = model->duplicateData( - hwOutput, - tilePostfix, - newDesc); - - hwOutputTiles.emplace_back(hwOutputTile); - hwOutputTilesOffsets.emplace_back( - DimValues({ - {Dim::W, planeTile->widthInfo.outputStartIndex}, - {Dim::H, planeTile->heightInfo.outputStartIndex}, - {Dim::N, channelTile->channelStartIndex} - })); - } - - // - // Add alignement to output tile if needed - // - - if ((planeTile->widthInfo.outputStartIndex * sizeof(fp16_t)) % 16 != 0) { - auto hwOutputTileAligned = model->duplicateData( - hwOutputTile, - "@aligned"); + for (const auto &tiling : tiler.getHwTilings()) { + HWPoolStageTiler hwStageTiler(so, sio, model, + origStage, _stageBuilder, tiling); + // + // Split/concat input/output tiles + // - _stageBuidler->addCopyStage( + if (!hwStageTiler.hwInputTiles.empty()) { + _stageBuilder->addSplitStage( model, - origStage->name() + tilePostfix + "@align-output-ptr", + origStage->name() + "@split-input", origStage->origLayer(), - hwOutputTileAligned, - hwOutputTile); - - hwOutputTile = hwOutputTileAligned; - } - - // - // Process output junk if needed - // - - if (planeTile->heightInfo.outputJunkBefore != 0 || - planeTile->heightInfo.outputJunkAfter != 0 || - planeTile->widthInfo.outputJunkBefore != 0 || - planeTile->widthInfo.outputJunkAfter != 0) { - auto newDesc = hwOutputTile->desc(); - newDesc.setDim(Dim::W, planeTile->widthInfo.outputWithJunk); - newDesc.setDim(Dim::H, planeTile->heightInfo.outputWithJunk); - - auto hwOutputTileWithJunk = model->duplicateData( - hwOutputTile, - "@with-junk", - newDesc); - - DimValues innerOffset; - innerOffset.set(Dim::W, planeTile->widthInfo.outputJunkBefore); - innerOffset.set(Dim::H, planeTile->heightInfo.outputJunkBefore); + std::move(hwStageTiler.hwInputTilesOffsets), + hwStageTiler.hwInput, + hwStageTiler.hwInputTiles); + } - _stageBuidler->addShrinkStage( + if (!hwStageTiler.hwOutputTiles.empty()) { + _stageBuilder->addConcatStage( model, - origStage->name() + tilePostfix + "@remove-junk", + origStage->name() + "@concat-output", origStage->origLayer(), - hwOutputTileWithJunk, - hwOutputTile, - innerOffset); - - hwOutputTile = hwOutputTileWithJunk; - } - - // - // Create HW stage for tile - // - - auto hwPad = getPoolPadding( - planeTile, hwInput->desc().dims(), - kernelSizeX, kernelSizeY, - kernelStride, kernelStride, - padLeft, padRight, padTop, padBottom); - - auto hwTileWeights = model->addFakeData(); - auto hwTileBiases = model->addFakeData(); - auto hwTileScales = model->addFakeData(); - - auto hwStage = model->addNewStage( - origStage->name() + tilePostfix, - StageType::MyriadXHwOp, - origStage->origLayer(), - {hwInputTile, hwTileWeights, hwTileBiases, hwTileScales}, - {hwOutputTile}); - - hwStage->attrs().set("hwOpType", HwOpType::POOL); - hwStage->attrs().set("poolType", origStage->type() == StageType::StubMaxPool ? HwPoolType::MAX : HwPoolType::AVERAGE); - - hwStage->attrs().set("kernelSizeX", kernelSizeX); - hwStage->attrs().set("kernelSizeY", kernelSizeY); - hwStage->attrs().set("kernelStride", kernelStride); - - hwStage->attrs().set("pad", hwPad); - - hwStage->attrs().set("tiling", channelTile->finalTiles); - - hwStage->attrs().set("withReLU", withReLU); + std::move(hwStageTiler.hwOutputTilesOffsets), + hwStageTiler.hwOutputTiles, + hwStageTiler.hwOutput); } } - - // - // Split/concat input/output tiles - // - - if (!hwInputTiles.empty()) { - _stageBuidler->addSplitStage( - model, - origStage->name() + "@split-input", - origStage->origLayer(), - std::move(hwInputTilesOffsets), - hwInput, - hwInputTiles); - } - - if (!hwOutputTiles.empty()) { - _stageBuidler->addConcatStage( - model, - origStage->name() + "@concat-output", - origStage->origLayer(), - std::move(hwOutputTilesOffsets), - hwOutputTiles, - hwOutput); - } - // // Remove SW stage // diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/hw_pooling_tiling/hw_pooling_tiler.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/hw_pooling_tiling/hw_pooling_tiler.cpp new file mode 100644 index 00000000000000..4d3a5c9ca9d124 --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/src/passes/hw_pooling_tiling/hw_pooling_tiler.cpp @@ -0,0 +1,477 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include +#include +#include +#include +#include + +namespace vpu { + +namespace HWTilingNS { + +class PoolingInputToOutputDirection; +class PoolingOutputToInputDirection; + +// Input -> Output case +class PoolingInputToOutputDirection: public GraphDataTiling { +public: + explicit PoolingInputToOutputDirection(const ConvolutionOptions &co): GraphDataTiling(co, Direction::INPUT_TO_OUTPUT) {} + PoolingInputToOutputDirection(const PoolingInputToOutputDirection &other): GraphDataTiling(other) {} + // ok + void initTileSizes() override { + _useCeil = ceilNeeded(); + + _inputTileDims.set(Dim::W, _co._inputDims[Dim::W]); + _inputTileDims.set(Dim::H, _co._inputDims[Dim::H]); + _inputTileDims.set(Dim::C, _co._inputDims[Dim::C]); + _inputTileDims.set(Dim::N, _co._inputDims.get(Dim::N, 1)); + + _outputTileDims.set(Dim::W, _co._outputDims[Dim::W]); + _outputTileDims.set(Dim::H, _co._outputDims[Dim::H]); + _outputTileDims.set(Dim::C, _co._outputDims[Dim::C]); + _outputTileDims.set(Dim::N, _co._outputDims.get(Dim::N, 1)); + } + + // Input -> Output case + // ok + void setInputNOutputTileDimensions(const int tileDimW, const int tileDimH, const int tileDimN) override { + _inputTileDims.set(Dim::W, tileDimW); + _inputTileDims.set(Dim::H, tileDimH); + _inputTileDims.set(Dim::N, tileDimN); + + _outputTileDims.set(Dim::N, tileDimN); + + correctOutputPlaneSize(); + } + + // Input -> Output case + // .. + void applyTilingOption(const TilingOption &tilingOption) override { + int tileDimW = divUp(_co._inputDims[Dim::W], tilingOption.numWidthTiles); + int tileDimH = divUp(_co._inputDims[Dim::H], tilingOption.numHeightTiles); + const int tileDimN = divUp(_co._inputDims[Dim::N], tilingOption.numChannelTiles); + + tileDimW = divUp(tileDimW, _co._kernelStride) * _co._kernelStride; + tileDimH = divUp(tileDimH, _co._kernelStride) * _co._kernelStride; + + _inputTileDims.set(Dim::W, tileDimW); + _inputTileDims.set(Dim::H, tileDimH); + _inputTileDims.set(Dim::N, tileDimN); + + correctOutputPlaneSize(); + } + + void correctPlaneSize() override { + correctOutputPlaneSize(); + } + + void correctOutputPlaneSize() { + int maxOutputWidth = calcOutputSize(_inputTileDims[Dim::W], _co._kernelSizeX, _co._kernelStride, _co._paddingLeft, _co._paddingRight, _useCeil); + _outputTileDims.set(Dim::W, std::min(_outputTileDims[Dim::W], maxOutputWidth)); + + int maxOutputHeight = calcOutputSize(_inputTileDims[Dim::H], _co._kernelSizeY, _co._kernelStride, _co._paddingTop, _co._paddingBottom, _useCeil); + _outputTileDims.set(Dim::H, std::min(_outputTileDims[Dim::H], maxOutputHeight)); + } + + const DimValues &splitOverTensorDims() override { + return _co._inputDims; + } + + void patternMatching() override {}; + +private: + // ok + bool ceilNeeded() { + int tempX = _co._inputDims[Dim::W] + _co._paddingLeft + _co._paddingRight - _co._kernelSizeX; + int tempY = _co._inputDims[Dim::H] + _co._paddingTop + _co._paddingBottom - _co._kernelSizeY; + + int outWidthWithOutCeil = (tempX + _co._kernelStride) / _co._kernelStride; + int outHeightWithOutCeil = (tempY + _co._kernelStride) / _co._kernelStride; + + int outWidthWithCeil = static_cast(std::ceil(static_cast(tempX) / _co._kernelStride + 1)); + int outHeightWithCeil = static_cast(std::ceil(static_cast(tempY) / _co._kernelStride + 1)); + + if ((_co._outputDims[Dim::W] != outWidthWithCeil) && (_co._outputDims[Dim::W] != outWidthWithOutCeil)) { + VPU_THROW_EXCEPTION + << "Internal error: Output in " << _co._stageName << " has incorrect width dimension. Expected: " + << outWidthWithCeil << " or " << outWidthWithOutCeil << " Actual: " << _co._outputDims[Dim::W]; + } + + if ((_co._outputDims[Dim::H] != outHeightWithCeil) && (_co._outputDims[Dim::H] != outHeightWithOutCeil)) { + VPU_THROW_EXCEPTION + << "Internal error: Output in " << _co._stageName << " has incorrect height dimension. Expected: " + << outHeightWithCeil << " or " << outHeightWithOutCeil << " Actual: " << _co._outputDims[Dim::H]; + } + + if ((_co._origOutputDims[Dim::W] == outWidthWithOutCeil) && (_co._origOutputDims[Dim::H] == outHeightWithOutCeil)) { + return false; + } else { + return true; + } + } +}; + +HWPoolingTiler::HWPoolingTiler(const ConvolutionOptions &co, + Direction direction, + size_t maxTilingOptions) : + _co(co), + _searcher(_co, direction, maxTilingOptions) { + _tilingPossible = tileForHW(); +} + +bool HWPoolingTiler::tileForHW() { + const std::vector &tilingOptions = _searcher.tilingOptions(); + if (tilingOptions.empty()) { + return false; + } + + for (const TilingOption &tilingOption : tilingOptions) { + const HWPoolingTileLayoutCut tileLayoutCut = _searcher.tileLayoutCut(tilingOption); + if (tileLayoutCut.tileCutPossible()) { + _hwTilings.push_back(tileLayoutCut.hwTiling()); + } + } + + return _hwTilings.size() != 0; +} + +std::unique_ptr PoolGraphDataTilingFactory::makeDirTiling(const ConvolutionOptions &co, + Direction direction) { + if (direction == Direction::INPUT_TO_OUTPUT) { + return std::unique_ptr(new PoolingInputToOutputDirection(co)); + // } else if (direction == Direction::OUTPUT_TO_INPUT) { + // return std::unique_ptr(new PoolingOutputToInputDirection(co)); + } else { + IE_ASSERT(false) << "Unsupported direction"; + } +} + +std::unique_ptr PoolGraphDataTilingFactory::makeDirTiling(const GraphDataTiling &o) { + if (o.getDirection() == Direction::INPUT_TO_OUTPUT) { + return std::unique_ptr( + new PoolingInputToOutputDirection(dynamic_cast(o))); + // } else if (o.getDirection() == Direction::OUTPUT_TO_INPUT) { + // return std::unique_ptr( + // new PoolingOutputToInputDirection(dynamic_cast(o))); + } else { + IE_ASSERT(false) << "Unsupported direction"; + } +} + +// +// Looks for the optimal tiling accordingly to the cost function. Modifies dimensions in dirTiling during search. +// +std::vector HWPoolingTilingSearcher::selectBetterTiling() const { + const auto& env = CompileEnv::get(); + GraphDataTiling &dirTiling = *_dirTiling; + FixedMaxHeap tilingOptions(_maxTilingOptions); + + // TODO: estimate this numbers + const int maxNumWidthTiles = 15; + const int maxNumHeightTiles = 15; + const int maxNumBatchTiles = _co._outputDims.get(Dim::N, 1); + + const auto outputTileInitial = dirTiling.getOutputTileDims(); + const auto inputTileInitial = dirTiling.getInputTileDims(); + + const auto minInputTileDimW = std::max(8, _co._kernelSizeX); + const auto minInputTileDimH = _co._kernelSizeY; + + // const DimValues &splitOver = dirTiling.splitOverTensorDims(); + const auto direction = dirTiling.getDirection(); + + for (int numBatchTiles = 1; numBatchTiles <= maxNumBatchTiles; numBatchTiles++) { + // + // Filter-out misaligned SoN tiles. + // + + if (outputTileInitial[Dim::N] % numBatchTiles != 0) { + continue; + } + + auto tileSizeDimN = outputTileInitial[Dim::N] / numBatchTiles; + + for (int numWidthTiles = 1; numWidthTiles <= maxNumWidthTiles; numWidthTiles++) { + // const int tileSizeDimW = divUp(splitOver[Dim::W], numWidthTiles); + int tileSizeDimW = divUp(_co._inputDims[Dim::W], numWidthTiles); + + // + // Filter-out too small SoW tiles. + // + + if (numWidthTiles > 1 && direction == Direction::INPUT_TO_OUTPUT) { + tileSizeDimW = divUp(tileSizeDimW, _co._kernelStride) * _co._kernelStride; + + if (tileSizeDimW < minInputTileDimW) { + break; + } + } + + for (int numHeightTiles = 1; numHeightTiles <= maxNumHeightTiles ; numHeightTiles++) { + // const int tileSizeDimH = divUp(splitOver[Dim::H], numHeightTiles); + int tileSizeDimH = divUp(_co._inputDims[Dim::H], numHeightTiles); + + if (direction == Direction::INPUT_TO_OUTPUT) { + tileSizeDimH = divUp(tileSizeDimH, _co._kernelStride) * _co._kernelStride; + } + + // + // Filter-out too small SoH tiles. + // + + if (numHeightTiles > 1 && direction == Direction::INPUT_TO_OUTPUT) { + tileSizeDimH = divUp(tileSizeDimH, _co._kernelStride) * _co._kernelStride; + + if (tileSizeDimH < minInputTileDimH) { + break; + } + } + + // + // Try current tile size. + // + + dirTiling.resetInputTileDims(inputTileInitial); + dirTiling.resetOutputTileDims(outputTileInitial); + + dirTiling.setInputNOutputTileDimensions(tileSizeDimW, tileSizeDimH, tileSizeDimN); + + + // + // Check that tiling is valid. + // + + const auto heightTiles = calcHeightTilesP(_co, dirTiling.getOutputTileDims(), + dirTiling.useCeil()); + const auto widthTiles = calcWidthTilesP(_co, dirTiling.getOutputTileDims(), dirTiling.useCeil()); + + if (heightTiles.empty()) { + continue; + } + if (widthTiles.empty()) { + break; + } + + bool isOK = true; + double solutionCost = 0.0; + + for (const auto& heightTile : heightTiles) { + for (const auto& widthTile : widthTiles) { + // + // Output tile fits to CMX limitation. + // + + DimValues fullOutputTileDims; + fullOutputTileDims.set(Dim::W, widthTile.outputWithJunk); + fullOutputTileDims.set(Dim::H, heightTile.outputWithJunk); + fullOutputTileDims.set(Dim::C, dirTiling.getOutputTileDims()[Dim::C]); + fullOutputTileDims.set(Dim::N, dirTiling.getOutputTileDims()[Dim::N]); + + // TODO: support HCW + if (calculateHwBufferSize(fullOutputTileDims) > env.resources.cmxLimit) { + isOK = false; + break; + } + + // + // `linesPerChan` restrictions. + // + + if (heightTile.inputWithJunk < _co._kernelSizeY) { + isOK = false; + break; + } + + if (!checkPoolingHWRestrictions( + widthTile.inputWithJunk, + heightTile.inputWithJunk, + dirTiling.getInputTileDims()[Dim::C], + dirTiling.getOutputTileDims()[Dim::C], + _co._kernelSizeX, _co._kernelSizeY, _co._kernelStride)) { + isOK = false; + break; + } + + // + // Replicate padding in case of large input plane - #-16783. + // + + DimValues fullInputTileDims; + fullInputTileDims.set(Dim::W, widthTile.inputWithJunk); + fullInputTileDims.set(Dim::H, heightTile.inputWithJunk); + + auto pad = getHwPaddingInfo( + fullInputTileDims, fullOutputTileDims, + _co._kernelSizeX, _co._kernelSizeY, + _co._kernelStride, _co._kernelStride, + _co._paddingLeft, _co._paddingTop); + + if (pad.enable && (pad.left > 0 || pad.right > 0 || pad.bottom > 0)) { + int memPerPlane = alignVal( + fullInputTileDims[Dim::W], 8) * sizeof(fp16_t) + * ((fullInputTileDims[Dim::H] - 1) + (_co._kernelSizeY - 1)); + int memLimit = pad.bottom > 0 ? 0x800 : 0x1000; + if (memPerPlane > memLimit) { + isOK = false; + break; + } + } + + // + // Calc tile cost. + // + const auto& _inputTileDims = dirTiling.getInputTileDims(); + const auto& _outputTileDims = dirTiling.getOutputTileDims(); + auto chansPerBlock = 1 << static_cast(HwOpMode::MODE_16_16); + solutionCost += 1.0 + * ((_inputTileDims[Dim::C] * _inputTileDims[Dim::N]) / chansPerBlock) * _co._kernelSizeX * _co._kernelSizeY + * numBatchTiles; + + // Alignment for output + if ((widthTile.outputStartIndex * sizeof(fp16_t)) % 16 != 0) { + solutionCost += 1.0 + * widthTile.outputWithJunk + * heightTile.outputWithJunk + * _outputTileDims[Dim::C] + * _outputTileDims[Dim::N]; + } + + // Alignment for input + if ((widthTile.inputStartIndex * sizeof(fp16_t)) % 16 != 0) { + solutionCost += 1.0 + * widthTile.inputWithJunk + * heightTile.inputWithJunk + * _inputTileDims[Dim::C] + * _inputTileDims[Dim::N]; + } + } + + if (!isOK) { + break; + } + } + + if (!isOK) { + continue; + } + + // + // Put to the pool of best options. + // + + const int totalNumTiles = numWidthTiles * numHeightTiles * numBatchTiles; + + const TilingOption to = + {numWidthTiles, numHeightTiles, numBatchTiles, totalNumTiles, solutionCost}; + tilingOptions.push(to); + } + } + } + + const auto sorted = tilingOptions.sorted(); + + if (sorted.size() != 0) { + const TilingOption& best = sorted.front(); + int inputTileDimW = divUp(_co._inputDims[Dim::W], best.numWidthTiles); + int inputTileDimH = divUp(_co._inputDims[Dim::H], best.numHeightTiles); + auto tileDimN = outputTileInitial[Dim::N] / best.numChannelTiles; + + inputTileDimW = divUp(inputTileDimW, _co._kernelStride) * _co._kernelStride; + inputTileDimH = divUp(inputTileDimH, _co._kernelStride) * _co._kernelStride; + + auto& _inputTileDims = dirTiling.getInputTileDims(); + auto& _outputTileDims = dirTiling.getOutputTileDims(); + + _inputTileDims.set(Dim::W, inputTileDimW); + _inputTileDims.set(Dim::H, inputTileDimH); + _inputTileDims.set(Dim::N, tileDimN); + + dirTiling.resetOutputTileDims(outputTileInitial); + _outputTileDims.set(Dim::N, tileDimN); + + dirTiling.correctPlaneSize(); + } + + return sorted; +} + +const vpu::HWTilingNS::HWPoolingTileLayoutCut HWPoolingTilingSearcher::tileLayoutCut(const TilingOption &option) const { + return HWPoolingTileLayoutCut(*_dirTiling, option); +} + +SmallVector calcHeightTilesP(const ConvolutionOptions &_co, + const DimValues &outputTileDims, bool useCeil) { + SmallVector heightTiles; + + if (outputTileDims[Dim::H] == _co._outputDims[Dim::H]) { + HwPlaneTileInfo info; + info.inputWithJunk = _co._inputDims[Dim::H]; + info.outputWithJunk = _co._outputDims[Dim::H]; + info.outputJunkBefore = 0; + info.outputJunkAfter = 0; + info.inputStartIndex = 0; + info.inputEndIndex = _co._inputDims[Dim::H]; + info.outputStartIndex = 0; + info.outputEndIndex = _co._outputDims[Dim::H]; + + heightTiles.emplace_back(info); + } else { + heightTiles = splitIntoPlaneTiles( + _co._inputDims[Dim::H], + _co._outputDims[Dim::H], + _co._kernelSizeY, + _co._kernelStride, + _co._paddingTop, _co._paddingBottom, + outputTileDims[Dim::H], + useCeil); + } + + return heightTiles; +} + +SmallVector calcWidthTilesP(const ConvolutionOptions &_co, + const DimValues &outputTileDims, bool useCeil) { + SmallVector widthTiles; + + if (outputTileDims[Dim::W] == _co._outputDims[Dim::W]) { + HwPlaneTileInfo info; + info.inputWithJunk = _co._inputDims[Dim::W]; + info.outputWithJunk = _co._outputDims[Dim::W]; + info.outputJunkBefore = 0; + info.outputJunkAfter = 0; + info.inputStartIndex = 0; + info.inputEndIndex = _co._inputDims[Dim::W]; + info.outputStartIndex = 0; + info.outputEndIndex = _co._outputDims[Dim::W]; + + widthTiles.emplace_back(info); + } else { + widthTiles = splitIntoPlaneTiles( + _co._inputDims[Dim::W], + _co._outputDims[Dim::W], + _co._kernelSizeX, + _co._kernelStride, + _co._paddingLeft, _co._paddingRight, + outputTileDims[Dim::W], + useCeil); + } + + return widthTiles; +} + +HwPoolTileInfo splitPooling(int outZ) { + HwPoolTileInfo tiles; + tiles.mode = HwOpMode::MODE_16_16; + tiles.numDescr = (outZ + CHANS_PER_DESCR - 1) / CHANS_PER_DESCR; + tiles.chansPerDescr = CHANS_PER_DESCR; + return tiles; +} + +} // namespace HWTilingNS + +} // namespace vpu + diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/hw_pooling_tiling/hw_stage_tiler.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/hw_pooling_tiling/hw_stage_tiler.cpp new file mode 100644 index 00000000000000..7a19a96c6a095d --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/src/passes/hw_pooling_tiling/hw_stage_tiler.cpp @@ -0,0 +1,234 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace vpu { + +HwPaddingInfo getPoolPadding(const HwPlaneTilePtr& tile, + const DimValues& dims, + int kernelSizeX, + int kernelSizeY, + int kernelStrideX, + int kernelStrideY, + int padLeft, + int padRight, + int padTop, + int padBottom) { + const auto& widthInfo = tile->widthInfo; + const auto& heightInfo = tile->heightInfo; + + auto padW = (widthInfo.outputWithJunk - 1)*kernelStrideX + kernelSizeX - widthInfo.inputWithJunk; + auto padH = (heightInfo.outputWithJunk - 1)*kernelStrideY + kernelSizeY - heightInfo.inputWithJunk; + + HwPaddingInfo pad; + + pad.left = padLeft; + pad.right = (dims[Dim::W] <= widthInfo.inputEndIndex) ? padRight : padW - pad.left; + pad.top = padTop; + pad.bottom = (dims[Dim::H] <= heightInfo.inputEndIndex) ? padBottom : padH - pad.top; + + pad.enable = pad.left || pad.right || pad.top || pad.bottom; + + return pad; +} + +HWPoolStageTiler::HWPoolStageTiler(const HWPoolStageOptions &so, const HWPoolStageIO &sio, + const Model::Ptr &model, const Handle &origStage, + const StageBuilder::Ptr &stageBuilder, const HwPoolTilingPtr &tiling) { + hwInput = sio.origInput; + hwOutput = sio.origOutput; + + hwInputTiles.reserve(tiling->socTiles * tiling->sohTiles * tiling->sowTiles); + hwInputTilesOffsets.reserve(tiling->socTiles * tiling->sohTiles * tiling->sowTiles); + hwOutputTiles.reserve(tiling->socTiles * tiling->sohTiles * tiling->sowTiles); + hwOutputTilesOffsets.reserve(tiling->socTiles * tiling->sohTiles * tiling->sowTiles); + + for (const auto& planeTile : tiling->planeTiles) { + for (const auto& channelTile : planeTile->channelTiles) { + auto tilePostfix = getPlaneTilePostfix(planeTile) + getChannelTilePostfix(channelTile); + + // + // Create input tile + // + + Data hwInputTile; + + if (tiling->sohTiles == 1 && tiling->sowTiles == 1 && tiling->socTiles == 1) { + hwInputTile = hwInput; + } else { + auto newDesc = hwInput->desc(); + newDesc.setDim(Dim::W, planeTile->widthInfo.inputWithJunk); + newDesc.setDim(Dim::H, planeTile->heightInfo.inputWithJunk); + newDesc.setDim(Dim::N, channelTile->numInputChannels); + + hwInputTile = model->duplicateData( + hwInput, + tilePostfix, + newDesc); + + hwInputTiles.emplace_back(hwInputTile); + hwInputTilesOffsets.emplace_back( + DimValues({ + {Dim::W, planeTile->widthInfo.inputStartIndex}, + {Dim::H, planeTile->heightInfo.inputStartIndex}, + {Dim::N, channelTile->channelStartIndex} + })); + } + + // + // Add alignement to input tile if needed + // + + if ((planeTile->widthInfo.inputStartIndex * sizeof(fp16_t)) % 16 != 0) { + auto hwInputTileAligned = model->duplicateData( + hwInputTile, + "@aligned"); + + stageBuilder->addCopyStage( + model, + origStage->name() + tilePostfix + "@align-input-ptr", + origStage->origLayer(), + hwInputTile, + hwInputTileAligned); + + hwInputTile = hwInputTileAligned; + } + + // + // Create output tile + // + + Data hwOutputTile; + + if (tiling->sohTiles == 1 && tiling->sowTiles == 1 && tiling->socTiles == 1) { + hwOutputTile = hwOutput; + } else { + auto newDesc = hwOutput->desc(); + newDesc.setDim(Dim::W, planeTile->widthInfo.outputEndIndex - planeTile->widthInfo.outputStartIndex); + newDesc.setDim(Dim::H, planeTile->heightInfo.outputEndIndex - planeTile->heightInfo.outputStartIndex); + newDesc.setDim(Dim::N, channelTile->numInputChannels); + + hwOutputTile = model->duplicateData( + hwOutput, + tilePostfix, + newDesc); + + hwOutputTiles.emplace_back(hwOutputTile); + hwOutputTilesOffsets.emplace_back( + DimValues({ + {Dim::W, planeTile->widthInfo.outputStartIndex}, + {Dim::H, planeTile->heightInfo.outputStartIndex}, + {Dim::N, channelTile->channelStartIndex} + })); + } + + // + // Add alignement to output tile if needed + // + + if ((planeTile->widthInfo.outputStartIndex * sizeof(fp16_t)) % 16 != 0) { + auto hwOutputTileAligned = model->duplicateData( + hwOutputTile, + "@aligned"); + + stageBuilder->addCopyStage( + model, + origStage->name() + tilePostfix + "@align-output-ptr", + origStage->origLayer(), + hwOutputTileAligned, + hwOutputTile); + + hwOutputTile = hwOutputTileAligned; + } + + // + // Process output junk if needed + // + + if (planeTile->heightInfo.outputJunkBefore != 0 || + planeTile->heightInfo.outputJunkAfter != 0 || + planeTile->widthInfo.outputJunkBefore != 0 || + planeTile->widthInfo.outputJunkAfter != 0) { + auto newDesc = hwOutputTile->desc(); + newDesc.setDim(Dim::W, planeTile->widthInfo.outputWithJunk); + newDesc.setDim(Dim::H, planeTile->heightInfo.outputWithJunk); + + auto hwOutputTileWithJunk = model->duplicateData( + hwOutputTile, + "@with-junk", + newDesc); + + DimValues innerOffset; + innerOffset.set(Dim::W, planeTile->widthInfo.outputJunkBefore); + innerOffset.set(Dim::H, planeTile->heightInfo.outputJunkBefore); + + stageBuilder->addShrinkStage( + model, + origStage->name() + tilePostfix + "@remove-junk", + origStage->origLayer(), + hwOutputTileWithJunk, + hwOutputTile, + innerOffset); + + hwOutputTile = hwOutputTileWithJunk; + } + + // + // Create HW stage for tile + // + + auto hwPad = getPoolPadding( + planeTile, hwInput->desc().dims(), + so.kernelSizeX, so.kernelSizeY, + so.kernelStride, so.kernelStride, + so.padLeft, so.padRight, so.padTop, so.padBottom); + + auto hwTileWeights = model->addFakeData(); + auto hwTileBiases = model->addFakeData(); + auto hwTileScales = model->addFakeData(); + + auto hwStage = model->addNewStage( + origStage->name() + tilePostfix, + StageType::MyriadXHwOp, + origStage->origLayer(), + {hwInputTile, hwTileWeights, hwTileBiases, hwTileScales}, + {hwOutputTile}); + + hwStage->attrs().set("hwOpType", HwOpType::POOL); + hwStage->attrs().set("poolType", origStage->type() == StageType::StubMaxPool ? HwPoolType::MAX : HwPoolType::AVERAGE); + + hwStage->attrs().set("kernelSizeX", so.kernelSizeX); + hwStage->attrs().set("kernelSizeY", so.kernelSizeY); + hwStage->attrs().set("kernelStride", so.kernelStride); + + hwStage->attrs().set("pad", hwPad); + + hwStage->attrs().set("tiling", channelTile->finalTiles); + + hwStage->attrs().set("withReLU", so.withReLU); + } + } +} +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/initial_check.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/initial_check.cpp new file mode 100644 index 00000000000000..cafcfc1ff1309f --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/src/passes/initial_check.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include + +namespace vpu { +namespace { + +class PassImpl final : public Pass { +public: + void run(const Model::Ptr& model) override { + VPU_PROFILE(initialCheck); + + for (const auto& stage : model->getStages()) { + stage->initialCheck(); + } + } +}; + +} // namespace + +Pass::Ptr PassManager::initialCheck() { + return std::make_shared(); +} + +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/inject_sw.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/inject_sw.cpp index 58525dd6542004..4972a1ef5dbde3 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/inject_sw.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/inject_sw.cpp @@ -50,7 +50,7 @@ void PassImpl::run(const Model::Ptr& model) { StageVector hwStages; std::list swStages; - hwStages.reserve(model->numStages()); + hwStages.reserve(checked_cast(model->numStages())); for (const auto& stage : model->getStages()) { if (stage->category() == StageCategory::HW) { hwStages.emplace_back(stage); @@ -66,10 +66,14 @@ void PassImpl::run(const Model::Ptr& model) { // Try to merge HW and SW stages // + StageVector swCandidates; + for (const auto& hwStage : hwStages) { - for (const auto& swStage : swStages) { - model->buildStageOrder(); + swCandidates.clear(); + + model->buildStageOrder(); + for (const auto& swStage : swStages) { auto hwInd = hwStage->index(); IE_ASSERT(hwInd >= 0); @@ -115,10 +119,12 @@ void PassImpl::run(const Model::Ptr& model) { } } - if (!isOK) { - continue; + if (isOK) { + swCandidates.push_back(swStage); } + } + for (const auto& swStage : swCandidates) { // // Try to inject and check allocation, if it is failed -> revert // diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/merge_hw_stages.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/merge_hw_stages.cpp index 1ca839dde6d260..a26eb1cd181d64 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/merge_hw_stages.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/merge_hw_stages.cpp @@ -138,7 +138,7 @@ void PassImpl::run(const Model::Ptr& model) { if (isOK) { output = nextPostOpStage->output(0); - model->disconnectStageDatas(nextPostOpStage); + model->disconnectStage(nextPostOpStage); model->replaceStageOutput(stage->outputEdge(0), output); @@ -175,7 +175,7 @@ void PassImpl::run(const Model::Ptr& model) { if (auto nextPoolStage = getNextPoolStage(stage, output)) { output = nextPoolStage->output(0); - model->disconnectStageDatas(nextPoolStage); + model->disconnectStage(nextPoolStage); model->replaceStageOutput(stage->outputEdge(0), output); diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/process_special_stages.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/process_special_stages.cpp index 128868f6d76cd6..b49b5c1472a2fd 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/process_special_stages.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/process_special_stages.cpp @@ -3,6 +3,7 @@ // #include +#include #include #include @@ -19,47 +20,42 @@ namespace { class PassImpl final : public Pass { public: - explicit PassImpl(const StageBuilder::Ptr& stageBuilder) : _stageBuilder(stageBuilder) {} + explicit PassImpl(const StageBuilder::Ptr& stageBuilder) : + _stageBuilder(stageBuilder), _processor(stageBuilder) {} void run(const Model::Ptr& model) override; -private: - void processConcat(const Model::Ptr& model, const Stage& stage); - void processSplit(const Model::Ptr& model, const Stage& stage); - void processReshape(const Model::Ptr& model, const Stage& stage); - void processBroadcast(const Model::Ptr& model, const Stage& stage); - void processShrink(const Model::Ptr& model, const Stage& stage); - private: StageBuilder::Ptr _stageBuilder; + SpecialStageProcessor _processor; }; void PassImpl::run(const Model::Ptr& model) { VPU_PROFILE(processSpecialStages); // - // Merge multiple Broadcast stages applied to the same input. + // Merge multiple Expand stages applied to the same input. // - for (const auto& curBroadcastStage : model->getStages()) { - if (curBroadcastStage == nullptr) { + for (const auto& curExpandStage : model->getStages()) { + if (curExpandStage == nullptr) { continue; } - if (curBroadcastStage->type() != StageType::Broadcast) { + if (curExpandStage->type() != StageType::Expand) { continue; } - auto input = curBroadcastStage->input(0); - auto output = curBroadcastStage->output(0); + auto input = curExpandStage->input(0); + auto output = curExpandStage->output(0); bool hasDuplicates = false; for (const auto& inputConsumer : input->consumers()) { - if (inputConsumer->type() != StageType::Broadcast) { + if (inputConsumer->type() != StageType::Expand) { continue; } - if (inputConsumer == curBroadcastStage) { + if (inputConsumer == curExpandStage) { continue; } @@ -83,11 +79,11 @@ void PassImpl::run(const Model::Ptr& model) { } for (const auto& inputConsumer : input->consumers()) { - if (inputConsumer->type() != StageType::Broadcast) { + if (inputConsumer->type() != StageType::Expand) { continue; } - if (inputConsumer == curBroadcastStage) { + if (inputConsumer == curExpandStage) { continue; } @@ -111,552 +107,17 @@ void PassImpl::run(const Model::Ptr& model) { } if (stage->type() == StageType::Concat) { - processConcat(model, stage); + _processor.processConcat(model, stage); } else if (stage->type() == StageType::Split) { - processSplit(model, stage); + _processor.processSplit(model, stage); } else if (stage->type() == StageType::Reshape) { - processReshape(model, stage); - } else if (stage->type() == StageType::Broadcast) { - processBroadcast(model, stage); + _processor.processReshape(model, stage); + } else if (stage->type() == StageType::Expand) { + _processor.processExpand(model, stage); } else if (stage->type() == StageType::Shrink) { - processShrink(model, stage); - } - } -} - -void PassImpl::processConcat(const Model::Ptr& model, const Stage& stage) { - auto output = stage->output(0); - - const auto& offsets = stage->attrs().get>("offsets"); - IE_ASSERT(offsets.size() == stage->numInputs()); - - for (const auto& inEdge : stage->inputEdges()) { - IE_ASSERT(inEdge->portInd() >= 0); - IE_ASSERT(inEdge->portInd() < offsets.size()); - - auto input = inEdge->input(); - const auto& offsetFromOutput = offsets[inEdge->portInd()]; - - IE_ASSERT(input->desc().dimsOrder() == output->desc().dimsOrder()); - IE_ASSERT(offsetFromOutput.size() <= output->desc().numDims()); - for (const auto& p : offsetFromOutput) { - IE_ASSERT(output->desc().dimsOrder().hasDim(p.first)); - IE_ASSERT(p.second + input->desc().dim(p.first) <= output->desc().dim(p.first)); - } - - // - // Check if we need to insert Copy stage - // - - bool needCopy = false; - bool optionalCopy = false; - if (input->usage() != DataUsage::Intermediate) { - needCopy = true; - optionalCopy = false; - } else if (input->parentDataEdge() != nullptr) { - needCopy = true; - optionalCopy = false; - } else { - // - // Check input StridesRequirement. - // - - IE_ASSERT(input->checkStrides(input->requiredStrides())); - if (!checkStrides(input->desc(), output->strides(), input->requiredStrides())) { - needCopy = true; - optionalCopy = false; - } - - // - // Check consumers StridesRequirement. - // - - if (!needCopy) { - for (const auto& consumerEdge : input->consumerEdges()) { - const auto& consumerInfo = consumerEdge->consumer()->getDataStridesRequirements(); - - if (consumerInfo.hasInput(consumerEdge)) { - const auto& consumerStrideReqs = consumerInfo.getInput(consumerEdge); - IE_ASSERT(input->checkStrides(consumerStrideReqs)); - - if (!checkStrides(input->desc(), output->strides(), consumerStrideReqs)) { - needCopy = true; - optionalCopy = false; - } - } - } - } - - // - // Check producer StridesRequirement. - // - - if (!needCopy) { - if (auto producerEdge = input->producerEdge()) { - const auto& producerInfo = producerEdge->producer()->getDataStridesRequirements(); - - if (producerInfo.hasOutput(producerEdge)) { - const auto& producerStrideReqs = producerInfo.getOutput(producerEdge); - IE_ASSERT(input->checkStrides(producerStrideReqs)); - - if (!checkStrides(input->desc(), output->strides(), producerStrideReqs)) { - needCopy = true; - optionalCopy = false; - } - } - - if (!needCopy) { - // - // To reduce the size of HW output (still can be optimized). - // - - if (producerEdge->producer()->category() == StageCategory::HW) { - needCopy = true; - optionalCopy = true; - } - } - } - } - } - - // - // Insert Copy if needed - // - - if (needCopy) { - Data inputCopy; - if (input->usage() == DataUsage::Const) { - inputCopy = model->addNewData( - input->name() + "@copy", - input->desc()); - } else { - inputCopy = model->duplicateData( - input, - "@copy"); - inputCopy->resetRequiredStrides(); - } - - auto copyStage = _stageBuilder->addCopyStage( - model, - formatString("%s@input=%d@copy-for-concat", stage->name(), inEdge->portInd()), - stage->origLayer(), - input, - inputCopy); - copyStage->attrs().set("optional", optionalCopy); - - model->replaceStageInput(inEdge, inputCopy); - - input = inputCopy; - } - - // - // Add Data<->Data edge - // - - model->connectDatas() - .parent(output) - .child(input) - .mode(SharedDataMode::ROI) - .order(SharedDataOrder::ChildWritesToParent) - .offset(offsetFromOutput) - .done(); - } -} - -void PassImpl::processSplit(const Model::Ptr& model, const Stage& stage) { - auto input = stage->input(0); - - const auto& offsets = stage->attrs().get>("offsets"); - IE_ASSERT(offsets.size() == stage->numOutputs()); - - for (const auto& outEdge : stage->outputEdges()) { - IE_ASSERT(outEdge->portInd() >= 0); - IE_ASSERT(outEdge->portInd() < offsets.size()); - - auto output = outEdge->output(); - const auto& offsetFromInput = offsets[outEdge->portInd()]; - - IE_ASSERT(input->desc().dimsOrder() == output->desc().dimsOrder()); - IE_ASSERT(offsetFromInput.size() <= input->desc().numDims()); - for (const auto& p : offsetFromInput) { - IE_ASSERT(input->desc().dimsOrder().hasDim(p.first)); - IE_ASSERT(p.second + output->desc().dim(p.first) <= input->desc().dim(p.first)); - } - - // - // Check if we need to insert Copy stage - // - - bool needCopy = false; - if (output->usage() != DataUsage::Intermediate) { - needCopy = true; - } else if (output->parentDataEdge() != nullptr) { - needCopy = true; - } else { - // - // Check output StridesRequirement. - // - - IE_ASSERT(output->checkStrides(output->requiredStrides())); - if (!checkStrides(output->desc(), input->strides(), output->requiredStrides())) { - needCopy = true; - } - - // - // Check consumers StridesRequirement. - // - - if (!needCopy) { - for (const auto& consumerEdge : output->consumerEdges()) { - const auto& consumerInfo = consumerEdge->consumer()->getDataStridesRequirements(); - - if (consumerInfo.hasInput(consumerEdge)) { - const auto& consumerStrideReqs = consumerInfo.getInput(consumerEdge); - IE_ASSERT(output->checkStrides(consumerStrideReqs)); - - if (!checkStrides(output->desc(), input->strides(), consumerStrideReqs)) { - needCopy = true; - break; - } - } - } - } - } - - // - // Insert Copy if needed - // - - if (needCopy) { - auto outputCopy = model->duplicateData( - output, - "@copy"); - outputCopy->resetRequiredStrides(); - - auto outPortInd = outEdge->portInd(); - - model->replaceStageOutput(outEdge, outputCopy); - - _stageBuilder->addCopyStage( - model, - formatString("%s@output=%d@copy-for-split", stage->name(), outPortInd), - stage->origLayer(), - outputCopy, - output); - - output = outputCopy; - } - - // - // Add Data<->Data edge - // - - model->connectDatas() - .parent(input) - .child(output) - .mode(SharedDataMode::ROI) - .order(SharedDataOrder::ParentWritesToChild) - .offset(offsetFromInput) - .done(); - } -} - -void PassImpl::processReshape(const Model::Ptr& model, const Stage& stage) { - auto input = stage->input(0); - auto output = stage->output(0); - - IE_ASSERT(input->desc().dimsOrder() == DimsOrder::fromNumDims(input->desc().numDims())); - IE_ASSERT(input->checkStrides(StridesRequirement::compact())); - - IE_ASSERT(output->desc().dimsOrder() == DimsOrder::fromNumDims(output->desc().numDims())); - IE_ASSERT(output->checkStrides(StridesRequirement::compact())); - - // - // Check if we need to insert Copy stage - // - - bool needCopy = false; - if (input->usage() != DataUsage::Intermediate && - output->usage() != DataUsage::Intermediate) { - needCopy = true; - } else if (input->parentDataEdge() != nullptr && - output->parentDataEdge() != nullptr) { - needCopy = true; - } - - // - // Insert Copy if needed - // - - if (needCopy) { - Data inputCopy; - if (input->usage() == DataUsage::Const) { - inputCopy = model->addNewData( - input->name() + "@copy", - input->desc()); - } else { - inputCopy = model->duplicateData( - input, - "@copy"); - } - inputCopy->updateRequiredStrides(StridesRequirement::compact()); - - _stageBuilder->addCopyStage( - model, - formatString("%s@copy-for-reshape", stage->name()), - stage->origLayer(), - input, - inputCopy); - - model->replaceStageInput(stage->inputEdge(0), inputCopy); - - input = inputCopy; - } - - // - // Add Data<->Data edge - // - - if (input->usage() == DataUsage::Intermediate && - input->parentDataEdge() == nullptr) { - model->connectDatas() - .parent(output) - .child(input) - .mode(SharedDataMode::Reshape) - .order(SharedDataOrder::ChildWritesToParent) - .done(); - } else { - IE_ASSERT(output->usage() == DataUsage::Intermediate); - IE_ASSERT(output->parentDataEdge() == nullptr); - - model->connectDatas() - .parent(input) - .child(output) - .mode(SharedDataMode::Reshape) - .order(SharedDataOrder::ParentWritesToChild) - .done(); - } -} - -void PassImpl::processBroadcast(const Model::Ptr& model, const Stage& stage) { - auto input = stage->input(0); - auto output = stage->output(0); - - const auto& offset = stage->attrs().get("offset"); - - IE_ASSERT(input->desc().dimsOrder() == output->desc().dimsOrder()); - - IE_ASSERT(offset.size() <= output->desc().numDims()); - for (const auto& p : offset) { - IE_ASSERT(output->desc().dimsOrder().hasDim(p.first)); - IE_ASSERT(p.second + input->desc().dim(p.first) <= output->desc().dim(p.first)); - } - - // - // Check if we need to insert Copy stage - // - - bool needCopy = false; - bool optionalCopy = false; - if (input->usage() != DataUsage::Intermediate) { - needCopy = true; - optionalCopy = false; - } else if (input->parentDataEdge() != nullptr) { - needCopy = true; - optionalCopy = false; - } else { - // - // Check input StridesRequirement. - // - - IE_ASSERT(input->checkStrides(input->requiredStrides())); - if (!checkStrides(input->desc(), output->strides(), input->requiredStrides())) { - needCopy = true; - optionalCopy = false; - } - - // - // Check consumers StridesRequirement. - // - - if (!needCopy) { - for (const auto& consumerEdge : input->consumerEdges()) { - const auto& consumerInfo = consumerEdge->consumer()->getDataStridesRequirements(); - - if (consumerInfo.hasInput(consumerEdge)) { - const auto& consumerStrideReqs = consumerInfo.getInput(consumerEdge); - IE_ASSERT(input->checkStrides(consumerStrideReqs)); - - if (!checkStrides(input->desc(), output->strides(), consumerStrideReqs)) { - needCopy = true; - optionalCopy = false; - } - } - } - } - - // - // Check producer StridesRequirement. - // - - if (!needCopy) { - if (auto producerEdge = input->producerEdge()) { - const auto& producerInfo = producerEdge->producer()->getDataStridesRequirements(); - - if (producerInfo.hasOutput(producerEdge)) { - const auto& producerStrideReqs = producerInfo.getOutput(producerEdge); - IE_ASSERT(input->checkStrides(producerStrideReqs)); - - if (!checkStrides(input->desc(), output->strides(), producerStrideReqs)) { - needCopy = true; - optionalCopy = false; - } - } - - if (!needCopy) { - // - // To reduce the size of HW output (still can be optimized). - // - - if (producerEdge->producer()->category() == StageCategory::HW) { - needCopy = true; - optionalCopy = true; - } - } - } + _processor.processShrink(model, stage); } } - - // - // Insert Copy if needed - // - - if (needCopy) { - Data inputCopy; - if (input->usage() == DataUsage::Const) { - inputCopy = model->addNewData( - input->name() + "@copy", - input->desc()); - } else { - inputCopy = model->duplicateData( - input, - "@copy"); - inputCopy->resetRequiredStrides(); - } - - auto copyStage = _stageBuilder->addCopyStage( - model, - formatString("%s@copy-for-broadcast", stage->name()), - stage->origLayer(), - input, - inputCopy); - copyStage->attrs().set("optional", optionalCopy); - - model->replaceStageInput(stage->inputEdge(0), inputCopy); - - input = inputCopy; - } - - // - // Add Data<->Data edge - // - - model->connectDatas() - .parent(output) - .child(input) - .mode(SharedDataMode::ROI) - .order(SharedDataOrder::ChildWritesToParent) - .offset(offset) - .done(); -} - -void PassImpl::processShrink(const Model::Ptr& model, const Stage& stage) { - auto input = stage->input(0); - auto output = stage->output(0); - - const auto& offset = stage->attrs().get("offset"); - - IE_ASSERT(input->desc().dimsOrder() == output->desc().dimsOrder()); - - IE_ASSERT(offset.size() <= input->desc().numDims()); - for (const auto& p : offset) { - IE_ASSERT(input->desc().dimsOrder().hasDim(p.first)); - IE_ASSERT(p.second + output->desc().dim(p.first) <= input->desc().dim(p.first)); - } - - // - // Check if we need to insert Copy for output - // - - bool needCopy = false; - if (output->usage() != DataUsage::Intermediate) { - needCopy = true; - } else if (output->parentDataEdge() != nullptr) { - needCopy = true; - } else { - // - // Check output StridesRequirement. - // - - IE_ASSERT(output->checkStrides(output->requiredStrides())); - if (!checkStrides(output->desc(), input->strides(), output->requiredStrides())) { - needCopy = true; - } - - // - // Check consumers StridesRequirement. - // - - if (!needCopy) { - for (const auto& consumerEdge : output->consumerEdges()) { - const auto& consumerInfo = consumerEdge->consumer()->getDataStridesRequirements(); - - if (consumerInfo.hasInput(consumerEdge)) { - const auto& consumerStrideReqs = consumerInfo.getInput(consumerEdge); - IE_ASSERT(output->checkStrides(consumerStrideReqs)); - - if (!checkStrides(output->desc(), input->strides(), consumerStrideReqs)) { - needCopy = true; - break; - } - } - } - } - } - - // - // Insert output Copy if needed - // - - if (needCopy) { - auto outputCopy = model->duplicateData( - output, - "@copy"); - outputCopy->resetRequiredStrides(); - - model->replaceStageOutput(stage->outputEdge(0), outputCopy); - - _stageBuilder->addCopyStage( - model, - formatString("%s@copy-output-for-shrink", stage->name()), - stage->origLayer(), - outputCopy, - output); - - output = outputCopy; - } - - // - // Add Data<->Data edge - // - - model->connectDatas() - .parent(input) - .child(output) - .mode(SharedDataMode::ROI) - .order(SharedDataOrder::ParentWritesToChild) - .offset(offset) - .done(); } } // namespace diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/remove_unused_stages_outputs.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/remove_unused_stages_outputs.cpp new file mode 100644 index 00000000000000..4793fcdaa9fdf4 --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/src/passes/remove_unused_stages_outputs.cpp @@ -0,0 +1,50 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include + +#include + +namespace vpu { + +namespace { + +class PassImpl final : public Pass { +public: + explicit PassImpl(const StageBuilder::Ptr& stageBuilder) : _stageBuilder(stageBuilder) {} + + void run(const Model::Ptr& model) override; + +private: + StageBuilder::Ptr _stageBuilder; +}; + +void PassImpl::run(const Model::Ptr& model) { + VPU_PROFILE(removeUnusedStagesOutputs); + + for (const auto& stage : model->getStages()) { + if (stage == nullptr || ((stage->type() != StageType::LSTMCell) && (stage->type() != StageType::TopK))) { + continue; + } + + for (const auto& outEdge : stage->outputEdges()) { + auto output = outEdge->output(); + + if (output->usage() == DataUsage::Intermediate && output->numConsumers() == 0) { + model->replaceStageOutput(outEdge, model->addFakeData()); + } + } + } +} + +} // namespace + +Pass::Ptr PassManager::removeUnusedStagesOutputs() { + return std::make_shared(_stageBuilder); +} + +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/replace_deconv_by_conv.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/replace_deconv_by_conv.cpp index 2044a18c99f1d4..f8381d24b57d52 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/replace_deconv_by_conv.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/replace_deconv_by_conv.cpp @@ -34,34 +34,26 @@ class UpsamplingStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector&, - ScalePropagationStep) override { + ScalePropagationStep, + StageDataInfo&) override { VPU_THROW_EXCEPTION << "Must never be called"; } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - _orderInfo.setInput(_inputEdges[0], DimsOrder::NCHW); - _orderInfo.setOutput(_outputEdges[0], DimsOrder::NCHW); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + orderInfo.setInput(inputEdge(0), DimsOrder::NCHW); + orderInfo.setOutput(outputEdge(0), DimsOrder::NCHW); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement().add(1, DimStride::Aligned)); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + stridesInfo.setOutput(outputEdge(0), StridesRequirement().add(1, DimStride::Aligned)); } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } StageSHAVEsRequirements getSHAVEsRequirementsImpl() const override { @@ -69,6 +61,7 @@ class UpsamplingStage final : public StageNode { } void finalCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -94,12 +87,8 @@ class UpsamplingStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeNewBuffer(serializer); output->serializeNewBuffer(serializer); @@ -110,10 +99,8 @@ class UpsamplingStage final : public StageNode { class DeconvolutionToConvolutionContent final : public CalculatedDataContent { public: DeconvolutionToConvolutionContent( - const DataContent::Ptr& origContent, - int kernelSizeX, int kernelSizeY) : - CalculatedDataContent({origContent}), - _kerneSizeX(kernelSizeX), _kernelSizeY(kernelSizeY) { + const DataContent::Ptr& origContent) : + CalculatedDataContent({origContent}) { } void fillTempBuf(const SmallVector& baseContents, void* tempBuf) const { @@ -124,10 +111,6 @@ class DeconvolutionToConvolutionContent final : public CalculatedDataContent { deconv_to_conv(baseContents[0]->get(), static_cast(tempBuf), _desc); } - -private: - int _kerneSizeX; - int _kernelSizeY; }; @@ -202,7 +185,7 @@ void PassImpl::run(const Model::Ptr& model) { continue; } - model->disconnectStageDatas(stage); + model->disconnectStage(stage); DataDesc newDesc({1, 1, output->desc().dim(Dim::C), output->desc().dim(Dim::N)}); newDesc.setDim(Dim::N, input->desc().dim(Dim::N)); @@ -212,7 +195,7 @@ void PassImpl::run(const Model::Ptr& model) { auto newOutput = model->duplicateData(output, "@upsampleData", newDesc); auto newWeights = model->duplicateData(weights, "@upsampleData", weights->desc(), - std::make_shared(weights->content(), kernelSizeX, kernelSizeY)); + std::make_shared(weights->content())); auto upsampleStage = model->addNewStage( stage->origLayerName() + "@Upsample", diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/replace_fc_by_conv.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/replace_fc_by_conv.cpp index b2e74315f8ee1f..72ffb1e5171a65 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/replace_fc_by_conv.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/replace_fc_by_conv.cpp @@ -22,21 +22,24 @@ namespace vpu { namespace { -using ReplicatedDataMap = std::unordered_map; - void setConvParameters(const vpu::Stage& stage, int kX, int kY) { - stage->attrs().set("kernelSizeX", kX); - stage->attrs().set("kernelSizeY", kY); - stage->attrs().set("kernelStrideX", 1); - stage->attrs().set("kernelStrideY", 1); - stage->attrs().set("padLeft", 0); - stage->attrs().set("padRight", 0); - stage->attrs().set("padTop", 0); - stage->attrs().set("padBottom", 0); - stage->attrs().set("dilationX", 1); - stage->attrs().set("dilationY", 1); - stage->attrs().set("groupSize", 1); - stage->attrs().set("tryHW", true); + stage->attrs().set("kernelSizeX", kX); + stage->attrs().set("kernelSizeY", kY); + + stage->attrs().set("kernelStrideX", kX); + stage->attrs().set("kernelStrideY", kY); + + stage->attrs().set("padLeft", 0); + stage->attrs().set("padRight", 0); + stage->attrs().set("padTop", 0); + stage->attrs().set("padBottom", 0); + + stage->attrs().set("dilationX", 1); + stage->attrs().set("dilationY", 1); + + stage->attrs().set("groupSize", 1); + + stage->attrs().set("tryHW", true); } class PassImpl final : public Pass { @@ -57,129 +60,202 @@ void PassImpl::run(const Model::Ptr& model) { continue; } - auto tryHW = stage->attrs().getOrDefault("tryHW", false); + const auto tryHW = stage->attrs().getOrDefault("tryHW", false); if (!tryHW) { continue; } - auto input = stage->input(0); - auto weights = stage->input(1); - auto biases = stage->input(2); - auto output = stage->output(0); - - auto dims = input->desc().dims(); - - if (input->desc().numDims() == 4) { - bool required = dims.has(Dim::N); - required &= dims.has(Dim::C); - required &= dims.has(Dim::H); - required &= dims.has(Dim::W); - - if (required && - input->desc().dim(Dim::H, 1) < 16 && - input->desc().dim(Dim::W, 1) < 16) { - /* can convert to convolution layers */ - model->disconnectStageDatas(stage); - - auto kernelSizeX = input->desc().dim(Dim::W, 1); - auto kernelSizeY = input->desc().dim(Dim::H, 1); - IE_ASSERT(weights->desc().totalDimSize() >= - kernelSizeX * kernelSizeY * (input->desc().dim(Dim::C)) * output->desc().dim(Dim::C)); - - auto newWeights = model->duplicateData( - weights, - "", - DataDesc({ - kernelSizeX, - kernelSizeY, - input->desc().dim(Dim::C), - output->desc().dim(Dim::C)})); - - auto newBiases = model->addFakeData(); - if (biases->usage() != DataUsage::Fake) { - IE_ASSERT(biases->desc().totalDimSize() >= output->desc().dim(Dim::C)); - newBiases = model->duplicateData(biases, - biases->name(), - DataDesc({output->desc().dim(Dim::C)})); - } + const auto input = stage->input(0); + const auto weights = stage->input(1); + const auto biases = stage->input(2); + const auto output = stage->output(0); + + const auto inDims = input->desc().dims(); - DataDesc newDesc({1, 1, output->desc().dim(Dim::C), output->desc().dim(Dim::N)}); - auto newOutput = model->duplicateData(output, "@reshapeData", newDesc); - - auto newStage = model->addNewStage( - stage->origLayerName(), - StageType::StubConv, - stage->origLayer(), - {input, newWeights, newBiases}, - {newOutput}); - newStage->attrs().copyFrom(stage->attrs()); - setConvParameters(newStage, kernelSizeX, kernelSizeY); - - _stageBuilder->addReshapeStage( - model, - stage->name() + "@reshapeOut", - stage->origLayer(), - newOutput, - output); - - model->removeStage(stage); + if (inDims.size() != 2 && inDims.size() != 4) { + continue; + } + + const auto inBatch = inDims[Dim::N]; + const auto inSize = input->desc().totalDimSize() / inBatch; + + IE_ASSERT(output->desc().dim(Dim::N) == inBatch); + + // HW restriction for kernel stride (we use stride equal to kernel size). + const int maxKernelSize = 8; + + // TODO: something more sophisticated? + int convKernelSizeX = -1; + int convKernelSizeY = -1; + for (int k = maxKernelSize; k >= 1; --k) { + if (inSize >= (k * k) && inSize % (k * k) == 0 && isPowerOfTwo(inSize / (k * k))) { + convKernelSizeX = k; + convKernelSizeY = k; + break; } - } else if (dims.has(Dim::N) && - dims.has(Dim::C) && - (!dims.has(Dim::H)) && - (!dims.has(Dim::W))) { - IE_ASSERT(weights->desc().totalDimSize() >= - (input->desc().dim(Dim::C)) * output->desc().dim(Dim::C)); - - model->disconnectStageDatas(stage); - - auto newWeights = model->duplicateData(weights, - weights->name(), - DataDesc({ - 1, - 1, - input->desc().dim(Dim::C), - output->desc().dim(Dim::C)})); - - auto newBiases = model->addFakeData(); - if (biases->usage() != DataUsage::Fake) { - IE_ASSERT(biases->desc().totalDimSize() >= output->desc().dim(Dim::C)); - newBiases = model->duplicateData(biases, - biases->name(), - DataDesc({output->desc().dim(Dim::C)})); + } + if (convKernelSizeX == -1 || convKernelSizeY == -1) { + for (int k = maxKernelSize; k >= 1; --k) { + if (inSize >= (k * k) && inSize % (k * k) == 0) { + convKernelSizeX = k; + convKernelSizeY = k; + break; + } + } + } + + if (convKernelSizeX == -1 || convKernelSizeY == -1) { + continue; + } + + const auto convInputC = inSize / (convKernelSizeX * convKernelSizeY); + + model->disconnectStage(stage); + + // TODO: something more sophisticated? + int batchStepW = 1; + int batchStepH = 1; + for (auto div : {100, 50, 20, 10}) { + if (inBatch >= div && inBatch % div == 0) { + batchStepW = div; + batchStepH = inBatch / div; + break; } + } - DataDesc newDescIn({1, 1, input->desc().dim(Dim::C), input->desc().dim(Dim::N)}); - auto newInput = model->duplicateData(output, "@reshapeDataIn", newDescIn); + Data convInput; + if (batchStepW == 1 && batchStepH == 1) { + convInput = model->duplicateData( + input, + "@reshape", + DataDesc{convKernelSizeX, convKernelSizeY, convInputC, inBatch}); - DataDesc newDescOut({1, 1, output->desc().dim(Dim::C), output->desc().dim(Dim::N)}); - auto newOutput = model->duplicateData(output, "@reshapeDataOut", newDescOut); + _stageBuilder->addReshapeStage( + model, + convInput->name(), + stage->origLayer(), + input, + convInput); + } else { + // NCDHW + const auto reshaped = model->duplicateData( + input, + "@reshape", + DataDesc{convKernelSizeX, convKernelSizeY, convInputC, batchStepW, batchStepH}); _stageBuilder->addReshapeStage( model, - stage->name() + "@reshapeIn", + reshaped->name(), stage->origLayer(), input, - newInput); + reshaped); + + // NCDHW + const auto permuted = model->duplicateData( + input, + "@permute-batch", + DataDesc{convKernelSizeX, batchStepW, convKernelSizeY, batchStepH, convInputC}); - auto newStage = model->addNewStage( - stage->origLayerName(), - StageType::StubConv, + _stageBuilder->addPermuteStage( + model, + permuted->name(), stage->origLayer(), - {newInput, newWeights, newBiases}, - {newOutput}); - newStage->attrs().copyFrom(stage->attrs()); - setConvParameters(newStage, 1, 1); + reshaped, + permuted, + DimValues_{{Dim::W, Dim::W}, {Dim::H, Dim::C}, {Dim::D, Dim::H}, {Dim::C, Dim::N}, {Dim::N, Dim::D}}); + + // NCHW + const auto merged = model->duplicateData( + input, + "@merge-batch", + DataDesc{convKernelSizeX * batchStepW, convKernelSizeY * batchStepH, convInputC, 1}); _stageBuilder->addReshapeStage( model, - stage->name() + "@reshapeOut", + merged->name(), stage->origLayer(), - newOutput, + permuted, + merged); + + convInput = merged; + } + + Data convOutput; + if (batchStepW == 1 && batchStepH == 1) { + convOutput = model->duplicateData( + output, + "@reshape", + DataDesc{1, 1, output->desc().dim(Dim::C), inBatch}); + + _stageBuilder->addReshapeStage( + model, + convOutput->name(), + stage->origLayer(), + convOutput, output); + } else { + // NCDHW + const auto reshaped = model->duplicateData( + output, + "@reshape", + DataDesc{1, 1, output->desc().dim(Dim::C), batchStepW, batchStepH}); - model->removeStage(stage); + _stageBuilder->addReshapeStage( + model, + reshaped->name(), + stage->origLayer(), + reshaped, + output); + + // NCDHW + const auto permuted = model->duplicateData( + output, + "@permute-batch", + DataDesc{1, batchStepW, 1, batchStepH, output->desc().dim(Dim::C)}); + + _stageBuilder->addPermuteStage( + model, + permuted->name(), + stage->origLayer(), + permuted, + reshaped, + DimValues_{{Dim::W, Dim::W}, {Dim::H, Dim::D}, {Dim::D, Dim::N}, {Dim::C, Dim::H}, {Dim::N, Dim::C}}); + + // NCHW + const auto merged = model->duplicateData( + output, + "@merge-batch", + DataDesc{batchStepW, batchStepH, output->desc().dim(Dim::C), 1}); + + _stageBuilder->addReshapeStage( + model, + merged->name(), + stage->origLayer(), + merged, + permuted); + + convOutput = merged; } + + const auto convWeights = model->duplicateData( + weights, + "@fc-to-conv", + DataDesc({ + convKernelSizeX, + convKernelSizeY, + convInputC, + output->desc().dim(Dim::C)})); + + auto convStage = model->addNewStage( + stage->name() + "@fc-to-conv", + StageType::StubConv, + stage->origLayer(), + {convInput, convWeights, biases}, + {convOutput}); + convStage->attrs().copyFrom(stage->attrs()); + setConvParameters(convStage, convKernelSizeX, convKernelSizeY); + + model->removeStage(stage); } } diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/reshape_dilation_conv.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/reshape_dilation_conv.cpp index 0a1fb476fe1900..36976eadfd2243 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/reshape_dilation_conv.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/reshape_dilation_conv.cpp @@ -66,6 +66,10 @@ void PassImpl::run(const Model::Ptr& model) { auto padTop = stage->attrs().get("padTop"); auto padBottom = stage->attrs().get("padBottom"); + auto scaleFactor = stage->attrs().getOrDefault("scaleFactor", 1.0f); + + IE_ASSERT(dilationX >= 1); + IE_ASSERT(dilationY >= 1); if (dilationX <= 1 && dilationY <= 1) { continue; } @@ -75,17 +79,13 @@ void PassImpl::run(const Model::Ptr& model) { continue; } - if ((padTop != padBottom) || (padLeft != padRight)) { - stage->attrs().set("tryHW", false); - continue; - } - - if ((dilationX != dilationY) || (dilationX != 2)) { + if (((padLeft % dilationX) !=0) || ((padTop % dilationY) !=0)) { stage->attrs().set("tryHW", false); continue; } - if ((kernelStrideX != 1) || (kernelStrideY != 1)) { + if ((std::max(dilationX, kernelStrideX) % std::min(dilationX, kernelStrideX)) || + (std::max(dilationY, kernelStrideY) % std::min(dilationY, kernelStrideY))) { stage->attrs().set("tryHW", false); continue; } @@ -94,7 +94,6 @@ void PassImpl::run(const Model::Ptr& model) { auto weights = stage->input(1); auto biases = stage->input(2); auto output = stage->output(0); - auto input_org = input; if (input->desc().dim(Dim::N) > 1) { stage->attrs().set("tryHW", false); @@ -107,9 +106,7 @@ void PassImpl::run(const Model::Ptr& model) { continue; } - bool Expand_mark = false; - // TODO - const bool Use_pixel_alignment = false; + const bool Use_pixel_alignment = true; int pixel_stride_alignment = STRIDE_ALIGNMENT / input->desc().elemSize(); int InputExtended_width = input->desc().dim(Dim::W); @@ -119,15 +116,9 @@ void PassImpl::run(const Model::Ptr& model) { InputExtended_width = divUp(input->desc().dim(Dim::W), dilationX * pixel_stride_alignment) * dilationX * pixel_stride_alignment; - InputExtended_height = divUp(input->desc().dim(Dim::H), - dilationY * pixel_stride_alignment) * dilationY - * pixel_stride_alignment; - } else if ((divUp(input->desc().dim(Dim::W), dilationX) - < pixel_stride_alignment) - || (divUp(input->desc().dim(Dim::H), dilationY) - < pixel_stride_alignment)) { - InputExtended_width = pixel_stride_alignment * dilationX; - InputExtended_height = pixel_stride_alignment * dilationY; + + InputExtended_height = divUp(input->desc().dim(Dim::H), dilationY) + * dilationY; } else { InputExtended_width = divUp(input->desc().dim(Dim::W), dilationX) * dilationX; @@ -135,12 +126,6 @@ void PassImpl::run(const Model::Ptr& model) { * dilationY; } - if ((((InputExtended_width % pixel_stride_alignment) == 0) && (InputExtended_width % (dilationX * pixel_stride_alignment) != 0)) - || (((InputExtended_height % pixel_stride_alignment) == 0) && (InputExtended_height % (dilationX * dilationY) != 0))) { - stage->attrs().set("tryHW", false); - continue; - } - float InputExtended_scale = std::max( static_cast(InputExtended_width) / static_cast(input->desc().dim(Dim::W)), @@ -150,14 +135,16 @@ void PassImpl::run(const Model::Ptr& model) { const float MAX_INPUTEXTENDED_SCALE = 1.8; const float MIN_INPUTEXTENDED_SCALE = 1; - if (InputExtended_scale >= MAX_INPUTEXTENDED_SCALE) { + if (InputExtended_scale >= MAX_INPUTEXTENDED_SCALE) { stage->attrs().set("tryHW", false); continue; } + bool Expand_mark = false; + Expand_mark = (InputExtended_scale > MIN_INPUTEXTENDED_SCALE); - model->disconnectStageDatas(stage); + model->disconnectStage(stage); // Expand input if need auto newDesc_input = input->desc(); @@ -170,15 +157,24 @@ void PassImpl::run(const Model::Ptr& model) { InputExtended = model->duplicateData(input, "@extended-input", newDesc_input); - _stageBuilder->addBroadcastStage(model, - stage->name() + "@expand-input", stage->origLayer(), input, + _stageBuilder->addPadStage(model, stage->name() + "@padding", + stage->origLayer(), + PadMode::Constant, 0.0f, DimValues(), + DimValues({ { Dim::W, (InputExtended_width - input->desc().dim(Dim::W)) }, + { Dim::H, (InputExtended_height - input->desc().dim(Dim::H)) }, }), + input, InputExtended); } - DataDesc Reinterpret_inputdataDesc(DataType::FP16, DimsOrder::NCHW, - { dilationX, InputExtended->desc().dim(Dim::W) / dilationX, - InputExtended->desc().dim(Dim::H), - InputExtended->desc().dim(Dim::C) }); + DataDesc Reinterpret_inputdataDesc( + DataType::FP16, + DimsOrder::NCHW, + { + dilationX, + InputExtended->desc().dim(Dim::W) / dilationX, + InputExtended->desc().dim(Dim::H), + InputExtended->desc().dim(Dim::C) + }); Data Reinterpret_inputdata; Reinterpret_inputdata = model->duplicateData(InputExtended, @@ -188,44 +184,27 @@ void PassImpl::run(const Model::Ptr& model) { stage->name() + "@copy-reinterpret-input-data", stage->origLayer(), InputExtended, Reinterpret_inputdata); - DataDesc Permuted_inputdataDesc(DataType::FP16, DimsOrder::NCHW, - { InputExtended->desc().dim(Dim::W) / dilationX, - InputExtended->desc().dim(Dim::H), - InputExtended->desc().dim(Dim::C), - dilationX }); + DataDesc Permuted_inputdataDesc( + DataType::FP16, + DimsOrder::NCHW, + { + InputExtended->desc().dim(Dim::W) / dilationX, + InputExtended->desc().dim(Dim::H), + InputExtended->desc().dim(Dim::C), + dilationX + }); Data Permuted_inputdata; Permuted_inputdata = model->duplicateData(InputExtended, "@permuted-input-data", Permuted_inputdataDesc); - SmallVector ieOrder(4, -1); - - ieOrder[0] = 3; - ieOrder[1] = 0; - ieOrder[2] = 1; - ieOrder[3] = 2; - - _stageBuilder->addPermuteStage(model, - stage->origLayerName() + "@permute-input-data", - stage->origLayer(), { Reinterpret_inputdata }, { - Permuted_inputdata }, ieOrder); - - // for conv output of subtensors - auto padx_new = padLeft - (kernelSizeX - 1) * (dilationX - 1) / 2; - auto pady_new = padTop - (kernelSizeY - 1) * (dilationY - 1) / 2; - - auto newDesc_Permuted_input = InputExtended->desc(); - newDesc_Permuted_input.setDim(Dim::W, - (((InputExtended->desc().dim(Dim::W) + 2 * padx_new - - kernelSizeX) / kernelStrideX) + 1) / dilationX); - newDesc_Permuted_input.setDim(Dim::H, - ((InputExtended->desc().dim(Dim::H) + 2 * pady_new - - kernelSizeY) / kernelStrideY) + 1); - newDesc_Permuted_input.setDim(Dim::C, output->desc().dim(Dim::C)); - newDesc_Permuted_input.setDim(Dim::N, dilationX); - - auto Subtensors_outputdata = model->duplicateData(output, - "@SubTensors-OutputData", newDesc_Permuted_input); + _stageBuilder->addPermuteStage( + model, + stage->origLayerName() + "@permute-input-data", + stage->origLayer(), + Reinterpret_inputdata, + Permuted_inputdata, + DimValues_{{Dim::W, Dim::H}, {Dim::H, Dim::C}, {Dim::C, Dim::N}, {Dim::N, Dim::W}}); // for skip rows, use reshape n c h w/2 -> n c h/2 w auto Reshape_Permuted_inputdata_Desc = Permuted_inputdata->desc(); @@ -242,36 +221,82 @@ void PassImpl::run(const Model::Ptr& model) { stage->origLayer(), Permuted_inputdata, Reshape_Permuted_inputdata); - auto Reshape_Permuted_outputdata_Desc = Subtensors_outputdata->desc(); - Reshape_Permuted_outputdata_Desc.setDim(Dim::H, - Subtensors_outputdata->desc().dim(Dim::H) / dilationY); - Reshape_Permuted_outputdata_Desc.setDim(Dim::W, - Subtensors_outputdata->desc().dim(Dim::W) * dilationY); - auto Reshape_Permuted_outputdata = model->duplicateData( - Subtensors_outputdata, "@Reshape-Permuted-outputdata", - Reshape_Permuted_outputdata_Desc); - // Desc of sub input tensor DataDesc Sub_inputdataDesc( { Permuted_inputdata->desc().dim(Dim::W), Permuted_inputdata->desc().dim(Dim::H) / dilationY, - Permuted_inputdata->desc().dim(Dim::C), - 1 }); + Permuted_inputdata->desc().dim(Dim::C), 1 }); Sub_inputdataDesc.reorder(DimsOrder::NCHW); + auto Sub_output_dilationX_dimenion = (dilationX / kernelStrideX) > 1 ? (dilationX / kernelStrideX) : 1; + auto Sub_output_dilationY_dimenion = (dilationY / kernelStrideY) > 1 ? (dilationY / kernelStrideY) : 1; + auto kernelStrideX_new = (dilationX / kernelStrideX) > 1 ? 1 : (kernelStrideX / dilationX); + auto kernelStrideY_new = (dilationY / kernelStrideY) > 1 ? 1 : (kernelStrideY / dilationY); + + // for conv output of subtensors + auto padLeft_new = padLeft / dilationX; + auto padRight_new = padRight / dilationX; + auto padTop_new = padTop / dilationY; + auto padBottom_new = padBottom / dilationY; + + auto Subtensors_outputdataDesc = InputExtended->desc(); + + Subtensors_outputdataDesc.setDim(Dim::W, + ((InputExtended->desc().dim(Dim::W) + padLeft + padRight + - dilationX * (kernelSizeX - 1) - 1 + kernelStrideX) + / kernelStrideX) / Sub_output_dilationX_dimenion); + + Subtensors_outputdataDesc.setDim(Dim::H, + (InputExtended->desc().dim(Dim::H) + padTop + padBottom + - dilationY * (kernelSizeY - 1) - 1 + kernelStrideY) + / kernelStrideY); + + Subtensors_outputdataDesc.setDim(Dim::C, output->desc().dim(Dim::C)); + Subtensors_outputdataDesc.setDim(Dim::N, Sub_output_dilationX_dimenion); + + auto Subtensors_outputdata = model->duplicateData(output, + "@SubTensors-OutputData", Subtensors_outputdataDesc); + // Desc of sub output tensor - auto Sub_outputdataDesc = Subtensors_outputdata->desc(); + auto Real_sub_outputdataDesc = Subtensors_outputdata->desc(); + + int Real_sub_outputdata_width = ((Sub_inputdataDesc.dim(Dim::W) + + padLeft_new + padRight_new - kernelSizeX) / kernelStrideX_new) + + 1; + int Real_sub_outputdata_height = ((Sub_inputdataDesc.dim(Dim::H) + + padTop_new + padBottom_new - kernelSizeY) / kernelStrideY_new) + + 1; + + if (Real_sub_outputdata_width != Subtensors_outputdataDesc.dim(Dim::W)) { + padRight_new = (Subtensors_outputdataDesc.dim(Dim::W) - 1) * kernelStrideX_new + + kernelSizeX - padLeft_new - Sub_inputdataDesc.dim(Dim::W); + Real_sub_outputdata_width = Subtensors_outputdataDesc.dim(Dim::W); + } + + if (Real_sub_outputdata_height != (Subtensors_outputdataDesc.dim(Dim::H) / Sub_output_dilationY_dimenion)) { + padBottom_new = (Subtensors_outputdataDesc.dim(Dim::H) - 1) * kernelStrideY_new + + kernelSizeY - padTop_new - Sub_inputdataDesc.dim(Dim::H); + Real_sub_outputdata_height = (Subtensors_outputdataDesc.dim(Dim::H) / Sub_output_dilationY_dimenion); + } - Sub_outputdataDesc.setDim(Dim::N, 1); - Sub_outputdataDesc.setDim(Dim::C, + bool Sub_outputdata_expand = false; + int Sub_outputdata_width = Real_sub_outputdata_width; + + if ((Real_sub_outputdata_width % pixel_stride_alignment) != 0) { + Sub_outputdata_expand = true; + Sub_outputdata_width = divUp(Real_sub_outputdata_width, + pixel_stride_alignment) * pixel_stride_alignment; + padRight_new = (Sub_outputdata_width - 1) * kernelStrideX_new + + kernelSizeX - padLeft_new - Sub_inputdataDesc.dim(Dim::W); + } + + Real_sub_outputdataDesc.setDim(Dim::N, 1); + Real_sub_outputdataDesc.setDim(Dim::C, Subtensors_outputdata->desc().dim(Dim::C)); - Sub_outputdataDesc.setDim(Dim::H, - ((Sub_inputdataDesc.dim(Dim::H) + 2 * pady_new - kernelSizeY) - / kernelStrideY) + 1); - Sub_outputdataDesc.setDim(Dim::W, - ((Sub_inputdataDesc.dim(Dim::W) + 2 * padx_new - kernelSizeX) - / kernelStrideX) + 1); + + Real_sub_outputdataDesc.setDim(Dim::H, Real_sub_outputdata_height); + Real_sub_outputdataDesc.setDim(Dim::W, Real_sub_outputdata_width); DataVector V_Sub_inputdata; std::vector V_Sub_inputdatasOffsets; @@ -282,17 +307,18 @@ void PassImpl::run(const Model::Ptr& model) { DataVector V_newWeights; DataVector V_newbiases; - V_Sub_inputdata.reserve(dilationX * dilationY); - V_Sub_inputdatasOffsets.reserve(dilationX * dilationY); - V_Sub_outputdata.reserve(dilationX * dilationY); - V_Sub_outputdatasOffsets.reserve(dilationX * dilationY); + V_Sub_inputdata.reserve(Sub_output_dilationX_dimenion * Sub_output_dilationY_dimenion); + V_Sub_inputdatasOffsets.reserve(Sub_output_dilationX_dimenion * Sub_output_dilationY_dimenion); + + V_newWeights.reserve(Sub_output_dilationX_dimenion * Sub_output_dilationY_dimenion); + V_newbiases.reserve(Sub_output_dilationX_dimenion * Sub_output_dilationY_dimenion); - V_newWeights.reserve(dilationX * dilationY); - V_newbiases.reserve(dilationX * dilationY); + V_Sub_outputdata.reserve(Sub_output_dilationX_dimenion * Sub_output_dilationY_dimenion); + V_Sub_outputdatasOffsets.reserve(Sub_output_dilationX_dimenion * Sub_output_dilationY_dimenion); - for (int dilationXInd = 0; dilationXInd < dilationX; ++dilationXInd) { + for (int dilationXInd = 0; dilationXInd < dilationX; dilationXInd += (dilationX / Sub_output_dilationX_dimenion)) { for (int dilationYInd = 0; dilationYInd < dilationY; - ++dilationYInd) { + dilationYInd += (dilationY / Sub_output_dilationY_dimenion)) { Data Sub_inputdata; Sub_inputdata = model->duplicateData(Permuted_inputdata, "@Sub-InputData", Sub_inputdataDesc); @@ -302,14 +328,21 @@ void PassImpl::run(const Model::Ptr& model) { Sub_inputdatasOffsets.set(Dim::W, dilationYInd * Sub_inputdataDesc.dim(Dim::W)); - Data Sub_outputdata; - Sub_outputdata = model->duplicateData(Subtensors_outputdata, - "@Sub_OutputData", Sub_outputdataDesc); + V_Sub_inputdata.emplace_back(Sub_inputdata); + V_Sub_inputdatasOffsets.emplace_back(Sub_inputdatasOffsets); + + Data Real_sub_outputdata; + Real_sub_outputdata = model->duplicateData( + Subtensors_outputdata, "@Sub_OutputData", + Real_sub_outputdataDesc); DimValues Sub_outputdatasOffsets; - Sub_outputdatasOffsets.set(Dim::N, dilationXInd); + Sub_outputdatasOffsets.set(Dim::N, dilationXInd * Sub_output_dilationX_dimenion / dilationX); Sub_outputdatasOffsets.set(Dim::W, - dilationYInd * Sub_outputdataDesc.dim(Dim::W)); + (dilationYInd * Sub_output_dilationY_dimenion / dilationY) * Real_sub_outputdataDesc.dim(Dim::W)); + + V_Sub_outputdata.emplace_back(Real_sub_outputdata); + V_Sub_outputdatasOffsets.emplace_back(Sub_outputdatasOffsets); // reuse weights and biases auto newWeights = model->duplicateData(weights, "@NewWeights", @@ -317,11 +350,6 @@ void PassImpl::run(const Model::Ptr& model) { auto newbiases = model->duplicateData(biases, "@Newbiases", biases->desc()); - V_Sub_inputdata.emplace_back(Sub_inputdata); - V_Sub_inputdatasOffsets.emplace_back(Sub_inputdatasOffsets); - V_Sub_outputdata.emplace_back(Sub_outputdata); - V_Sub_outputdatasOffsets.emplace_back(Sub_outputdatasOffsets); - V_newWeights.emplace_back(newWeights); V_newbiases.emplace_back(newbiases); } @@ -333,28 +361,73 @@ void PassImpl::run(const Model::Ptr& model) { V_Sub_inputdata); // sub tensors convolution - for (int index = 0; index < dilationX * dilationY; ++index) { + for (int Sub_output_XInd = 0; Sub_output_XInd < Sub_output_dilationX_dimenion; ++Sub_output_XInd) { + for (int Sub_output_YInd = 0; Sub_output_YInd < Sub_output_dilationY_dimenion; + ++Sub_output_YInd) { // Add SubDataConv stage + auto Sub_outputdataDesc = Real_sub_outputdataDesc; + Sub_outputdataDesc.setDim(Dim::W, Sub_outputdata_width); + + Data Sub_outputdata; + Sub_outputdata = model->duplicateData(Subtensors_outputdata, + "@Sub_OutputData", Sub_outputdataDesc); + auto newStage = model->addNewStage( stage->origLayerName() + "@SubDataConv", StageType::StubConv, stage->origLayer(), { - V_Sub_inputdata[index], V_newWeights[index], - V_newbiases[index] }, { V_Sub_outputdata[index] }); + V_Sub_inputdata[Sub_output_XInd * Sub_output_dilationY_dimenion + Sub_output_YInd], + V_newWeights[Sub_output_XInd * Sub_output_dilationY_dimenion + Sub_output_YInd], + V_newbiases[Sub_output_XInd * Sub_output_dilationY_dimenion + Sub_output_YInd] }, { Sub_outputdata }); newStage->attrs().set("kernelSizeX", kernelSizeX); newStage->attrs().set("kernelSizeY", kernelSizeY); - newStage->attrs().set("kernelStrideX", kernelStrideX); - newStage->attrs().set("kernelStrideY", kernelStrideY); - newStage->attrs().set("padLeft", padx_new); - newStage->attrs().set("padRight", padx_new); - newStage->attrs().set("padTop", pady_new); - newStage->attrs().set("padBottom", pady_new); + + newStage->attrs().set("kernelStrideX", kernelStrideX_new); + newStage->attrs().set("kernelStrideY", kernelStrideY_new); + + newStage->attrs().set("padLeft", padLeft_new); + newStage->attrs().set("padRight", padRight_new); + + newStage->attrs().set("padTop", padTop_new); + newStage->attrs().set("padBottom", padBottom_new); newStage->attrs().set("dilationX", 1); newStage->attrs().set("dilationY", 1); newStage->attrs().set("groupSize", groupSize); newStage->attrs().set("tryHW", true); + + newStage->attrs().set("scaleFactor", scaleFactor); + + if (Sub_outputdata_expand) { + _stageBuilder->addShrinkStage(model, + stage->name() + "@SubConvOutputData", + stage->origLayer(), Sub_outputdata, + V_Sub_outputdata[Sub_output_XInd * Sub_output_dilationY_dimenion + Sub_output_YInd]); + } else { + V_Sub_outputdata[Sub_output_XInd * Sub_output_dilationY_dimenion + Sub_output_YInd] = Sub_outputdata; + } + } } + auto Reshape_Permuted_outputdata_Desc = Subtensors_outputdata->desc(); + + Reshape_Permuted_outputdata_Desc.setDim(Dim::H, + Subtensors_outputdata->desc().dim(Dim::H) / Sub_output_dilationY_dimenion); + Reshape_Permuted_outputdata_Desc.setDim(Dim::W, + Subtensors_outputdata->desc().dim(Dim::W) * Sub_output_dilationY_dimenion); + + auto Reshape_Permuted_outputdata = model->duplicateData( + Subtensors_outputdata, "@Reshape-Permuted-outputdata", + Reshape_Permuted_outputdata_Desc); + + auto n = 0; + auto c = 0; + auto h = 0; + auto w = 0; + V_Sub_outputdatasOffsets[0].get(Dim::N, n); + V_Sub_outputdatasOffsets[0].get(Dim::C, c); + V_Sub_outputdatasOffsets[0].get(Dim::H, h); + V_Sub_outputdatasOffsets[0].get(Dim::W, w); + auto ConcatSubOutputDataStage = _stageBuilder->addConcatStage(model, stage->name() + "@Concat-Sub-OutputData", stage->origLayer(), std::move(V_Sub_outputdatasOffsets), V_Sub_outputdata, @@ -366,34 +439,21 @@ void PassImpl::run(const Model::Ptr& model) { // output permute DataDesc permute_outputdataDesc(DataType::FP16, DimsOrder::NCHW, - { Subtensors_outputdata->desc().dim(Dim::C), - Subtensors_outputdata->desc().dim(Dim::H), + { Subtensors_outputdata->desc().dim(Dim::N), Subtensors_outputdata->desc().dim(Dim::W), - Subtensors_outputdata->desc().dim(Dim::N) }); - - permute_outputdataDesc.setDim(Dim::N, - Subtensors_outputdata->desc().dim(Dim::C)); - permute_outputdataDesc.setDim(Dim::C, - Subtensors_outputdata->desc().dim(Dim::H)); - permute_outputdataDesc.setDim(Dim::H, - Subtensors_outputdata->desc().dim(Dim::W)); - permute_outputdataDesc.setDim(Dim::W, - Subtensors_outputdata->desc().dim(Dim::N)); + Subtensors_outputdata->desc().dim(Dim::H), + Subtensors_outputdata->desc().dim(Dim::C) }); auto permute_outputdata = model->duplicateData(Subtensors_outputdata, "@Permuted-OutputData", permute_outputdataDesc); - SmallVector ieOrder2(4, -1); - - ieOrder2[0] = 1; - ieOrder2[1] = 2; - ieOrder2[2] = 3; - ieOrder2[3] = 0; - - _stageBuilder->addPermuteStage(model, - stage->origLayerName() + "@Permute-OutputData", - stage->origLayer(), { Subtensors_outputdata }, { - permute_outputdata }, ieOrder2); + _stageBuilder->addPermuteStage( + model, + stage->origLayerName() + "@Permute-OutputData", + stage->origLayer(), + Subtensors_outputdata, + permute_outputdata, + DimValues_{{Dim::W, Dim::N}, {Dim::H, Dim::W}, {Dim::C, Dim::H}, {Dim::N, Dim::C}}); // Expand output if need if (Expand_mark) { @@ -427,7 +487,6 @@ void PassImpl::run(const Model::Ptr& model) { stage->name() + "@copy-Permute-OutputData", stage->origLayer(), permute_outputdata, output); } - model->removeStage(stage); } } diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/split_grouped_conv.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/split_grouped_conv.cpp index eea307512cc0f0..7f9d647cfdb9be 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/split_grouped_conv.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/split_grouped_conv.cpp @@ -88,7 +88,7 @@ void PassImpl::run(const Model::Ptr& model) { continue; } - model->disconnectStageDatas(stage); + model->disconnectStage(stage); auto inGroupDimC = input->desc().dim(Dim::C) / groupSize; auto outGroupDimC = output->desc().dim(Dim::C) / groupSize; @@ -191,8 +191,8 @@ void PassImpl::run(const Model::Ptr& model) { // subConvStage auto subConvStage = model->duplicateStage( - stage->name() + postfix, stage, + postfix, {subInputs[groupInd], subWeights, subBiases}, {subOutputs[groupInd]}); diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/split_hw_conv_and_pool.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/split_hw_conv_and_pool.cpp index dc38c769ff75e4..7138d9efdf6a57 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/split_hw_conv_and_pool.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/split_hw_conv_and_pool.cpp @@ -104,8 +104,8 @@ void PassImpl::run(const Model::Ptr& model) { auto numTiles = (convOutput->desc().dim(Dim::C) + tileSize - 1) / tileSize; - model->disconnectStageDatas(convStage); - model->disconnectStageDatas(poolStage); + model->disconnectStage(convStage); + model->disconnectStage(poolStage); DataVector subOutputs(numTiles); @@ -187,14 +187,14 @@ void PassImpl::run(const Model::Ptr& model) { } model->duplicateStage( - convStage->name() + postfix, convStage, + postfix, {convInput, tileWeights, tileBiases}, {convOutputTile}); model->duplicateStage( - poolStage->name() + postfix, poolStage, + postfix, {convOutputTile}, {poolOutputTile}); diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/split_hw_depth_convolution.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/split_hw_depth_convolution.cpp index d0444297153dec..e0870e942c2250 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/split_hw_depth_convolution.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/split_hw_depth_convolution.cpp @@ -199,7 +199,7 @@ void PassImpl::run(const Model::Ptr& model) { // Multiple tiles processing // - model->disconnectStageDatas(stage); + model->disconnectStage(stage); DataVector subInputs(numTiles); DataVector subOutputs(numTiles); @@ -237,8 +237,8 @@ void PassImpl::run(const Model::Ptr& model) { auto tileBiases = std::get<1>(constDatas); auto tileStage = model->duplicateStage( - stage->name() + postfix, stage, + postfix, {subInputs[tileInd], tileWeights, tileBiases}, {subOutputs[tileInd]}); diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/strided_slice.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/strided_slice.cpp new file mode 100644 index 00000000000000..98d5fc7cc40c08 --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/src/passes/strided_slice.cpp @@ -0,0 +1,317 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace vpu { + +namespace { + +struct StridedSliceParams { + DimValues begin; + DimValues end; + DimValues strides; + + DimValues begin_mask; + DimValues end_mask; +}; + +struct StridedSliceInternalParams { + DimValues begin_dms; + DimValues end_dms; + DimValues strides_dms; +}; + +class PassImpl final : public Pass { +public: + explicit PassImpl(StageBuilder::Ptr stageBuilder) : _stageBuilder(std::move(stageBuilder)) {} + + void run(const Model::Ptr& model) override; + +private: + StageBuilder::Ptr _stageBuilder; + + static StridedSliceParams parseInputParams(const Stage& stage); + static StridedSliceInternalParams computeInternalParams(const Stage& stage, StridedSliceParams params); +}; + +StridedSliceParams PassImpl::parseInputParams(const Stage& stage) { + auto beginInput = stage->input(1); + auto endInput = stage->input(2); + auto stridesInput = stage->input(3); + + IE_ASSERT(beginInput->content() != nullptr); + IE_ASSERT(endInput->content() != nullptr); + IE_ASSERT(stridesInput->content() != nullptr); + + StridedSliceParams params; + + size_t num_input_dims = stage->input(0)->desc().numDims(); + + auto vectorToDimValues = [](const std::vector& v) { + auto dims = DimsOrder::fromNumDims(v.size()).toIndices(); + int idx = v.size(); + for (auto& dim : dims) { + idx--; + dim.second = v[idx]; + } + return dims; + }; + + params.begin = vectorToDimValues( + std::vector(beginInput->content()->get(), + beginInput->content()->get() + beginInput->desc().dims().get(Dim::C, 0))); + params.end = vectorToDimValues( + std::vector(endInput->content()->get(), + endInput->content()->get() + endInput->desc().dims().get(Dim::C, 0))); + params.strides = vectorToDimValues( + std::vector(stridesInput->content()->get(), + stridesInput->content()->get() + stridesInput->desc().dims().get(Dim::C, 0))); + + IE_ASSERT(params.begin.size() == num_input_dims); + IE_ASSERT(params.end.size() == num_input_dims); + IE_ASSERT(params.strides.size() == num_input_dims); + + std::vector begin_mask_values; + std::vector end_mask_values; + + std::string begin_mask_str = stage->origLayer()->GetParamAsString("begin_mask", ""); + for (const auto& c : begin_mask_str) { + if (c == '1') begin_mask_values.push_back(1); + else if (c == '0') begin_mask_values.push_back(0); + } + begin_mask_values.insert(begin_mask_values.end(), num_input_dims - begin_mask_values.size(), 1); + + std::string end_mask_str = stage->origLayer()->GetParamAsString("end_mask", ""); + for (const auto& c : end_mask_str) { + if (c == '1') end_mask_values.push_back(1); + else if (c == '0') end_mask_values.push_back(0); + } + end_mask_values.insert(end_mask_values.end(), num_input_dims - end_mask_values.size(), 1); + + std::string ellipsis_mask_str = stage->origLayer()->GetParamAsString("ellipsis_mask", ""); + for (const auto& c : ellipsis_mask_str) { + IE_ASSERT(c != '1') << "VPU doesn't support ellipsis_mask for StridedSlice"; + } + + std::string new_axis_mask_str = stage->origLayer()->GetParamAsString("new_axis_mask", ""); + for (const auto& c : new_axis_mask_str) { + IE_ASSERT(c != '1') << "VPU doesn't support new_axis_mask for StridedSlice"; + } + + std::string shrink_axis_mask_str = stage->origLayer()->GetParamAsString("shrink_axis_mask", ""); + for (const auto& c : shrink_axis_mask_str) { + IE_ASSERT(c != '1') << "VPU doesn't support shrink_axis_mask for StridedSlice"; + } + + params.begin_mask = vectorToDimValues(begin_mask_values); + params.end_mask = vectorToDimValues(end_mask_values); + + return params; +} + +StridedSliceInternalParams PassImpl::computeInternalParams(const Stage& stage, StridedSliceParams params) { + auto input = stage->input(0); + + StridedSliceInternalParams m_params = StridedSliceInternalParams(); + size_t numDims = input->desc().numDims(); + + for (const auto& dim : input->desc().dimsOrder().toPermutation()) { + m_params.begin_dms.set(dim, 0); + m_params.end_dms.set(dim, input->desc().dim(dim)); + m_params.strides_dms.set(dim, 1); + } + + auto clip = [](int value, int min, int max) { + return std::min(std::max(min, value), max); + }; + + for (const auto& dim : input->desc().dimsOrder().toPermutation()) { + m_params.strides_dms.set(dim, params.strides[dim]); + + IE_ASSERT(params.begin_mask[dim] == 1 || params.begin_mask[dim] == 0); + IE_ASSERT(params.end_mask[dim] == 1 || params.end_mask[dim] == 0); + + m_params.begin_dms.set(dim, + params.begin_mask[dim] ? clip(params.begin[dim], 0, input->desc().dim(dim)) : 0); + m_params.end_dms.set(dim, + params.end_mask[dim] ? clip(params.end[dim], 0, input->desc().dim(dim)) : input->desc().dim(dim)); + + IE_ASSERT(dim != Dim::N || numDims < 4 || m_params.strides_dms[dim] == 1) + << "VPU doesn't support batch strides for StridedSlice"; + IE_ASSERT(m_params.begin_dms[dim] >= 0 && m_params.begin_dms[dim] < m_params.end_dms[dim]); + IE_ASSERT(m_params.end_dms[dim] <= input->desc().dim(dim)); + IE_ASSERT(m_params.strides_dms[dim] > 0); + } + + return m_params; +} + +void PassImpl::run(const Model::Ptr& model) { + VPU_PROFILE(stridedSlice); + + for (const auto& stage : model->getStages()) { + if (stage->type() != StageType::StridedSlice) { + continue; + } + IE_ASSERT(stage->numInputs() == 4); + IE_ASSERT(stage->numOutputs() == 1); + + auto input = stage->input(0); + auto output = stage->output(0); + + IE_ASSERT(input->desc().numDims() == output->desc().numDims()); + IE_ASSERT(input->desc().dimsOrder() == output->desc().dimsOrder()); + + auto params = parseInputParams(stage); + auto m_params = computeInternalParams(stage, params); + + model->disconnectStage(stage); + + auto directOrder = DimsOrder::fromNumDims(input->desc().numDims()); + auto perm = directOrder.toPermutation(); + + // + // Select a region of interest in accordance with the begin and end parameters. + // + + const bool needSelectROI = std::any_of(perm.begin(), perm.end(), [&](Dim dim) { + return m_params.begin_dms[dim] != 0 || m_params.end_dms[dim] != input->desc().dim(dim); }); + if (needSelectROI) { + auto roiDesc = input->desc(); + for (const auto &dim : perm) { + roiDesc.setDim(dim, m_params.end_dms[dim] - m_params.begin_dms[dim]); + } + auto roiData = model->duplicateData(input, "@roi", roiDesc); + auto shrinkStage = _stageBuilder->addShrinkStage( + model, + stage->name() + "@roi-selection", + stage->origLayer(), + input, + roiData); + shrinkStage->attrs().set("offset", m_params.begin_dms); + input = roiData; + } + + // + // Expand each dimension of the input tensor, if it is not completely divided by stride + // for further work. + // + + const bool needExpand = std::any_of(perm.begin(), perm.end(), [&](Dim dim) { + return input->desc().dim(dim) % m_params.strides_dms[dim] != 0; }); + if (needExpand) { + auto expandDesc = input->desc(); + for (const auto& dim : perm) { + auto alignValue = (m_params.strides_dms[dim] - expandDesc.dim(dim) % m_params.strides_dms[dim]) + % m_params.strides_dms[dim]; + expandDesc.setDim(dim, expandDesc.dim(dim) + alignValue); + } + auto expandedInputData = model->duplicateData(input, "@extended-input", expandDesc); + _stageBuilder->addExpandStage( + model, + stage->name() + "@expand-input", + stage->origLayer(), + input, + expandedInputData); + input = expandedInputData; + } + + // + // For copying with stride we do reshape in order to put data of interest at the beginning of each dimension, + // split into necessary and unnecessary data and then reverse reshape. + // + + for (const auto& dim : perm) { + if (m_params.strides_dms[dim] == 1) + continue; + + auto stride = abs(m_params.strides_dms[dim]); + auto reshapedDesc = input->desc(); + auto subtensorDesc = input->desc(); + auto intermediateOutDesc = input->desc(); + + if (input->desc().numDims() == 1) { + reshapedDesc = DataDesc({stride, input->desc().dim(dim) / stride}); + subtensorDesc = DataDesc({1, input->desc().dim(dim) / stride}); + } else if (perm.front() == dim) { + auto nextDim = perm.at(directOrder.dimInd(dim) + 1); + reshapedDesc.setDim(dim, stride); + reshapedDesc.setDim(nextDim, + input->desc().dim(dim) * input->desc().dim(nextDim) / stride); + subtensorDesc.setDim(dim, 1); + subtensorDesc.setDim(nextDim, reshapedDesc.dim(nextDim)); + } else { + auto previousDim = perm.at(directOrder.dimInd(dim) - 1); + reshapedDesc.setDim(dim, input->desc().dim(dim) / stride); + reshapedDesc.setDim(previousDim, input->desc().dim(previousDim) * stride); + + subtensorDesc.setDim(dim, reshapedDesc.dim(dim)); + subtensorDesc.setDim(previousDim, input->desc().dim(previousDim)); + } + + intermediateOutDesc.setDim(dim, input->desc().dim(dim) / stride); + + auto reshapedInputData = model->duplicateData( + input, formatString("@reshaped-input@dim%s", dim), reshapedDesc); + auto subtensorData = model->duplicateData( + input, formatString("@subtensor@dim%s", dim), subtensorDesc); + auto intermediateOutputData = model->duplicateData( + input, formatString("@intermediate-output@dim%s", dim), intermediateOutDesc); + + _stageBuilder->addReshapeStage( + model, + formatString("%s@reshape-input@dim%s", stage->name(), dim), + stage->origLayer(), + input, + reshapedInputData); + + _stageBuilder->addSplitStage( + model, + formatString("%s@split@dim%s", stage->name(), dim), + stage->origLayer(), + dim, + reshapedInputData, + {subtensorData}); + + _stageBuilder->addReshapeStage( + model, + formatString("%s@reshape-output@dim%s", stage->name(), dim), + stage->origLayer(), + subtensorData, + intermediateOutputData); + + input = intermediateOutputData; + } + + _stageBuilder->addCopyStage( + model, + formatString("%s@copy-output", stage->name()), + stage->origLayer(), + input, + output); + + model->removeStage(stage); + } +} + +} // namespace + +Pass::Ptr PassManager::stridedSlice() { + return std::make_shared(_stageBuilder); +} + +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/sw_conv_adaptation.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/sw_conv_adaptation.cpp index 3588814fd9ccbc..ba2a0b8dad3bbe 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/sw_conv_adaptation.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/sw_conv_adaptation.cpp @@ -66,17 +66,15 @@ class ConvStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector&, - ScalePropagationStep) override { + ScalePropagationStep, + StageDataInfo&) override { VPU_THROW_EXCEPTION << "Must never be called"; } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto weights = _inputEdges[1]->input(); - auto output = _outputEdges[0]->output(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); + auto weights = inputEdge(1)->input(); + auto output = outputEdge(0)->output(); auto finalOrder = input->desc().dimsOrder(); if (finalOrder.dimInd(Dim::C) == 1) { @@ -87,37 +85,31 @@ class ConvStage final : public StageNode { if (_type == StageType::Conv || _type == StageType::Im2ColConvolution) { if (finalOrder != input->desc().dimsOrder()) { - _orderInfo.setInput(_inputEdges[0], finalOrder); + orderInfo.setInput(inputEdge(0), finalOrder); } - _orderInfo.setOutput(_outputEdges[0], finalOrder); + orderInfo.setOutput(outputEdge(0), finalOrder); } else if (_type == StageType::DepthConv) { if (finalOrder != input->desc().dimsOrder()) { - _orderInfo.setInput(_inputEdges[0], finalOrder); + orderInfo.setInput(inputEdge(0), finalOrder); } - _orderInfo.setOutput(_outputEdges[0], finalOrder); + orderInfo.setOutput(outputEdge(0), finalOrder); } else { - _orderInfo.setInput(_inputEdges[0], finalOrder.createMovedDim(Dim::C, 0)); - _orderInfo.setOutput(_outputEdges[0], finalOrder.createMovedDim(Dim::C, 0)); + orderInfo.setInput(inputEdge(0), finalOrder.createMovedDim(Dim::C, 0)); + orderInfo.setOutput(outputEdge(0), finalOrder.createMovedDim(Dim::C, 0)); } } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { if (_type != StageType::DepthConv) { - _stridesInfo.setInput(_inputEdges[0], StridesRequirement::compact()); - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement::compact()); + stridesInfo.setInput(inputEdge(0), StridesRequirement::compact()); + stridesInfo.setOutput(outputEdge(0), StridesRequirement::compact()); } } void finalizeDataLayoutImpl() override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto weights = _inputEdges[1]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto weights = inputEdge(1)->input(); + auto output = outputEdge(0)->output(); auto kernelSizeX = attrs().get("kernelSizeX"); auto kernelSizeY = attrs().get("kernelSizeY"); @@ -223,18 +215,18 @@ class ConvStage final : public StageNode { IE_ASSERT(swWeights != nullptr); - _model->replaceStageInput(_inputEdges[1], swWeights); + _model->replaceStageInput(inputEdge(1), swWeights); } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } void finalCheckImpl() const override { + assertInputsOutputsTypes(this, + {{DataType::FP16}, {DataType::FP16}, {DataType::FP16}}, + {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -258,20 +250,17 @@ class ConvStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto weights = _inputEdges[1]->input(); - auto biases = _inputEdges[2]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto weights = inputEdge(1)->input(); + auto biases = inputEdge(2)->input(); + auto output = outputEdge(0)->output(); input->serializeOldBuffer(handle_from_this(), serializer); output->serializeOldBuffer(handle_from_this(), serializer); weights->serializeOldBuffer(handle_from_this(), serializer); - if (!_tempBufferEdges.empty()) { - _tempBufferEdges[0]->tempBuffer()->serializeOldBuffer(handle_from_this(), serializer); + if (numTempBuffers() == 1) { + tempBuffer(0)->serializeOldBuffer(handle_from_this(), serializer); } // TODO: remove this diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/sw_deconv_adaptation.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/sw_deconv_adaptation.cpp index 42874b2185e314..3c2dd913d8b5ee 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/sw_deconv_adaptation.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/sw_deconv_adaptation.cpp @@ -174,17 +174,15 @@ class DeconvStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector&, - ScalePropagationStep) override { + ScalePropagationStep, + StageDataInfo&) override { VPU_THROW_EXCEPTION << "Must never be called"; } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto weights = _inputEdges[1]->input(); - auto output = _outputEdges[0]->output(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); + auto weights = inputEdge(1)->input(); + auto output = outputEdge(0)->output(); auto finalOrder = input->desc().dimsOrder(); if (finalOrder.dimInd(Dim::C) == 1) { @@ -194,22 +192,19 @@ class DeconvStage final : public StageNode { if (_type == StageType::DepthDeconv) { if (finalOrder != input->desc().dimsOrder()) { - _orderInfo.setInput(_inputEdges[0], finalOrder); + orderInfo.setInput(inputEdge(0), finalOrder); } - _orderInfo.setOutput(_outputEdges[0], finalOrder); + orderInfo.setOutput(outputEdge(0), finalOrder); } else { - _orderInfo.setInput(_inputEdges[0], finalOrder.createMovedDim(Dim::C, 0)); - _orderInfo.setOutput(_outputEdges[0], finalOrder.createMovedDim(Dim::C, 0)); + orderInfo.setInput(inputEdge(0), finalOrder.createMovedDim(Dim::C, 0)); + orderInfo.setOutput(outputEdge(0), finalOrder.createMovedDim(Dim::C, 0)); } } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto weights = _inputEdges[1]->input(); - auto output = _outputEdges[0]->output(); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + auto input = inputEdge(0)->input(); + auto weights = inputEdge(1)->input(); + auto output = outputEdge(0)->output(); auto finalOrder = input->desc().dimsOrder(); if (finalOrder.dimInd(Dim::C) == 1) { @@ -220,22 +215,19 @@ class DeconvStage final : public StageNode { if (_type == StageType::DepthDeconv) { if (finalOrder.dimInd(Dim::C) == 0) { // HWC - _stridesInfo.setInput(_inputEdges[0], StridesRequirement::compact()); - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement::compact()); + stridesInfo.setInput(inputEdge(0), StridesRequirement::compact()); + stridesInfo.setOutput(outputEdge(0), StridesRequirement::compact()); } } else { - _stridesInfo.setInput(_inputEdges[0], StridesRequirement::compact()); - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement::compact()); + stridesInfo.setInput(inputEdge(0), StridesRequirement::compact()); + stridesInfo.setOutput(outputEdge(0), StridesRequirement::compact()); } } void finalizeDataLayoutImpl() override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto weights = _inputEdges[1]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto weights = inputEdge(1)->input(); + auto output = outputEdge(0)->output(); auto kernelSizeX = attrs().get("kernelSizeX"); auto kernelSizeY = attrs().get("kernelSizeY"); @@ -314,18 +306,18 @@ class DeconvStage final : public StageNode { IE_ASSERT(swWeights != nullptr); - _model->replaceStageInput(_inputEdges[1], swWeights); + _model->replaceStageInput(inputEdge(1), swWeights); } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } void finalCheckImpl() const override { + assertInputsOutputsTypes(this, + {{DataType::FP16}, {DataType::FP16}, {DataType::FP16}}, + {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -349,20 +341,17 @@ class DeconvStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto weights = _inputEdges[1]->input(); - auto biases = _inputEdges[2]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto weights = inputEdge(1)->input(); + auto biases = inputEdge(2)->input(); + auto output = outputEdge(0)->output(); input->serializeOldBuffer(handle_from_this(), serializer); output->serializeOldBuffer(handle_from_this(), serializer); weights->serializeOldBuffer(handle_from_this(), serializer); - if (!_tempBufferEdges.empty()) { - _tempBufferEdges[0]->tempBuffer()->serializeOldBuffer(handle_from_this(), serializer); + if (numTempBuffers() == 1) { + tempBuffer(0)->serializeOldBuffer(handle_from_this(), serializer); } // TODO: remove this @@ -404,7 +393,7 @@ void PassImpl::run(const Model::Ptr& model) { auto dilationY = stage->attrs().get("dilationY"); auto groupSize = stage->attrs().get("groupSize"); - model->disconnectStageDatas(stage); + model->disconnectStage(stage); if (groupSize == 0 || (groupSize > input->desc().dim(Dim::C)) || diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/sw_fc_adaptation.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/sw_fc_adaptation.cpp index e68ee519672928..7827bae1412bde 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/sw_fc_adaptation.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/sw_fc_adaptation.cpp @@ -23,34 +23,26 @@ class FullyConnectedStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector&, - ScalePropagationStep) override { + ScalePropagationStep, + StageDataInfo&) override { VPU_THROW_EXCEPTION << "Must never be called"; } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); - - _orderInfo.setInput(_inputEdges[0], input->desc().dimsOrder().createMovedDim(Dim::C, 0)); - _orderInfo.setOutput(_outputEdges[0], output->desc().dimsOrder().createMovedDim(Dim::C, 0)); + orderInfo.setInput(inputEdge(0), input->desc().dimsOrder().createMovedDim(Dim::C, 0)); + orderInfo.setOutput(outputEdge(0), output->desc().dimsOrder().createMovedDim(Dim::C, 0)); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - _stridesInfo.setInput(_inputEdges[0], StridesRequirement::compact()); - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement::compact()); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + stridesInfo.setInput(inputEdge(0), StridesRequirement::compact()); + stridesInfo.setOutput(outputEdge(0), StridesRequirement::compact()); } void finalizeDataLayoutImpl() override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - auto weights = _inputEdges[1]->input(); + auto weights = inputEdge(1)->input(); auto swWeights = weights->attrs().getOrDefault("swWeights", nullptr); if (swWeights == nullptr) { @@ -63,31 +55,28 @@ class FullyConnectedStage final : public StageNode { weights->attrs().set("swWeights", swWeights); } - _model->replaceStageInput(_inputEdges[1], swWeights); + _model->replaceStageInput(inputEdge(1), swWeights); } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } void finalCheckImpl() const override { + assertInputsOutputsTypes(this, + {{DataType::FP16}, {DataType::FP16}, {DataType::FP16}}, + {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer&) const override { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto weights = _inputEdges[1]->input(); - auto biases = _inputEdges[2]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto weights = inputEdge(1)->input(); + auto biases = inputEdge(2)->input(); + auto output = outputEdge(0)->output(); input->serializeOldBuffer(handle_from_this(), serializer); @@ -135,7 +124,7 @@ void PassImpl::run(const Model::Ptr& model) { auto biases = stage->input(2); auto output = stage->output(0); - model->disconnectStageDatas(stage); + model->disconnectStage(stage); if (biases->usage() != DataUsage::Fake) { auto tempOutput = model->duplicateData( diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/sw_pooling_adaptation.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/sw_pooling_adaptation.cpp index 25f84170e9d174..f1e883b57a6241 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/sw_pooling_adaptation.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/sw_pooling_adaptation.cpp @@ -21,15 +21,13 @@ class PoolStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector&, - ScalePropagationStep) override { + ScalePropagationStep, + StageDataInfo&) override { VPU_THROW_EXCEPTION << "Must never be called"; } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); auto finalOrder = input->desc().dimsOrder(); if (input->desc().dim(Dim::N, 1) > 1) { @@ -37,15 +35,12 @@ class PoolStage final : public StageNode { finalOrder = finalOrder.createMovedDim(Dim::C, 2); } - _orderInfo.setInput(_inputEdges[0], finalOrder); - _orderInfo.setOutput(_outputEdges[0], finalOrder); + orderInfo.setInput(inputEdge(0), finalOrder); + orderInfo.setOutput(outputEdge(0), finalOrder); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + auto input = inputEdge(0)->input(); auto dimsOrder = input->desc().dimsOrder(); @@ -56,8 +51,8 @@ class PoolStage final : public StageNode { reqs.add(dimsOrder.dimInd(Dim::N), DimStride::Compact); } - _stridesInfo.setInput(_inputEdges[0], reqs); - _stridesInfo.setOutput(_outputEdges[0], reqs); + stridesInfo.setInput(inputEdge(0), reqs); + stridesInfo.setOutput(outputEdge(0), reqs); // // * AvgPool/MaxPool support both YXZ and ZYX orders: @@ -68,7 +63,7 @@ class PoolStage final : public StageNode { if (_type == StageType::MaxPool || _type == StageType::AvgPool) { if (dimsOrder.dimInd(Dim::C) == 0) { - _stridesInfo.setInput(_inputEdges[0], StridesRequirement::compact()); + stridesInfo.setInput(inputEdge(0), StridesRequirement::compact()); } } } @@ -76,11 +71,12 @@ class PoolStage final : public StageNode { void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo&) override { // Pooling will support batch by merging it with previous dimension. } void finalCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -102,11 +98,8 @@ class PoolStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); if (_type == StageType::GlobalMaxPool || _type == StageType::GlobalAvgPool) { @@ -169,7 +162,7 @@ void PassImpl::run(const Model::Ptr& model) { auto padBottom = stage->attrs().get("padBottom"); auto excludePad = stage->attrs().get("excludePad"); - model->disconnectStageDatas(stage); + model->disconnectStage(stage); auto stageType = StageType::None; if (stage->type() == StageType::StubMaxPool) { diff --git a/inference-engine/src/vpu/graph_transformer/src/passes/swap_concat_and_hw_ops.cpp b/inference-engine/src/vpu/graph_transformer/src/passes/swap_concat_and_hw_ops.cpp index 2b2bdf604ac956..8af1d5233fe7f6 100644 --- a/inference-engine/src/vpu/graph_transformer/src/passes/swap_concat_and_hw_ops.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/passes/swap_concat_and_hw_ops.cpp @@ -106,7 +106,7 @@ void PassImpl::run(const Model::Ptr& model) { for (const auto& nextStage : nextStages) { auto nextOutput = nextStage->output(0); - model->disconnectStageDatas(nextStage); + model->disconnectStage(nextStage); DataVector newOutputs; newOutputs.reserve(lastInputs.size()); @@ -124,8 +124,8 @@ void PassImpl::run(const Model::Ptr& model) { newDesc); model->duplicateStage( - nextStage->name() + postfix, nextStage, + postfix, {curInput}, {newOutput}); diff --git a/inference-engine/src/vpu/graph_transformer/src/special_stage_processor.cpp b/inference-engine/src/vpu/graph_transformer/src/special_stage_processor.cpp new file mode 100644 index 00000000000000..b8b23e61d2ca82 --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/src/special_stage_processor.cpp @@ -0,0 +1,573 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include + +#include + +namespace vpu { + +void SpecialStageProcessor::processSplit( + const Model::Ptr& model, + const Stage& stage) { + IE_ASSERT(stage->type() == StageType::Split); + + auto input = stage->input(0); + + const auto& offsets = stage->attrs().get>("offsets"); + IE_ASSERT(offsets.size() == checked_cast(stage->numOutputs())); + + for (const auto& outEdge : stage->outputEdges()) { + IE_ASSERT(outEdge->portInd() >= 0); + IE_ASSERT(checked_cast(outEdge->portInd()) < offsets.size()); + + auto output = outEdge->output(); + const auto& offsetFromInput = offsets[checked_cast(outEdge->portInd())]; + + IE_ASSERT(input->desc().dimsOrder() == output->desc().dimsOrder()); + IE_ASSERT(offsetFromInput.size() <= checked_cast(input->desc().numDims())); + for (const auto& p : offsetFromInput) { + IE_ASSERT(input->desc().dimsOrder().hasDim(p.first)); + IE_ASSERT(p.second + output->desc().dim(p.first) <= input->desc().dim(p.first)); + } + + // + // Check if we need to insert Copy stage + // + + bool needCopy = false; + if (output->usage() != DataUsage::Intermediate) { + needCopy = true; + } else if (output->parentDataEdge() != nullptr) { + needCopy = true; + } else { + // + // Check output StridesRequirement. + // + + IE_ASSERT(output->checkStrides(output->requiredStrides())); + if (!checkStrides(output->desc(), input->strides(), output->requiredStrides())) { + needCopy = true; + } + + // + // Check consumers StridesRequirement. + // + + if (!needCopy) { + for (const auto& consumerEdge : output->consumerEdges()) { + const auto& consumerInfo = consumerEdge->consumer()->getDataStridesRequirements(); + + if (consumerInfo.hasInput(consumerEdge)) { + const auto& consumerStrideReqs = consumerInfo.getInput(consumerEdge); + IE_ASSERT(output->checkStrides(consumerStrideReqs)); + + if (!checkStrides(output->desc(), input->strides(), consumerStrideReqs)) { + needCopy = true; + break; + } + } + } + } + } + + // + // Insert Copy if needed + // + + if (needCopy) { + auto outputCopy = model->duplicateData(output, "@copy"); + outputCopy->resetRequiredStrides(); + + auto outPortInd = outEdge->portInd(); + + model->replaceStageOutput(outEdge, outputCopy); + + auto copyStage = _stageBuilder->addCopyStage( + model, + formatString("%s@output=%d@copy-for-split", stage->name(), outPortInd), + stage->origLayer(), + outputCopy, + output); + if (stage->attrs().has("batchInd")) { + copyStage->attrs().set("batchInd", stage->attrs().get("batchInd")); + } + + output = outputCopy; + } + + // + // Add Data<->Data edge + // + + model->connectDatas() + .parent(input) + .child(output) + .mode(SharedDataMode::ROI) + .order(SharedDataOrder::ParentWritesToChild) + .offset(offsetFromInput) + .done(); + } +} + +void SpecialStageProcessor::processConcat( + const Model::Ptr& model, + const Stage& stage) { + auto output = stage->output(0); + + const auto& offsets = stage->attrs().get>("offsets"); + IE_ASSERT(offsets.size() == checked_cast(stage->numInputs())); + + for (const auto& inEdge : stage->inputEdges()) { + IE_ASSERT(inEdge->portInd() >= 0); + IE_ASSERT(checked_cast(inEdge->portInd()) < offsets.size()); + + auto input = inEdge->input(); + const auto& offsetFromOutput = offsets[checked_cast(inEdge->portInd())]; + + IE_ASSERT(input->desc().dimsOrder() == output->desc().dimsOrder()); + IE_ASSERT(offsetFromOutput.size() <= checked_cast(output->desc().numDims())); + for (const auto& p : offsetFromOutput) { + IE_ASSERT(output->desc().dimsOrder().hasDim(p.first)); + IE_ASSERT(p.second + input->desc().dim(p.first) <= output->desc().dim(p.first)); + } + + // + // Check if we need to insert Copy stage + // + + bool needCopy = false; + bool optionalCopy = false; + if (input->usage() != DataUsage::Intermediate) { + needCopy = true; + optionalCopy = false; + } else if (input->parentDataEdge() != nullptr) { + needCopy = true; + optionalCopy = false; + } else { + // + // Check input StridesRequirement. + // + + IE_ASSERT(input->checkStrides(input->requiredStrides())); + if (!checkStrides(input->desc(), output->strides(), input->requiredStrides())) { + needCopy = true; + optionalCopy = false; + } + + // + // Check consumers StridesRequirement. + // + + if (!needCopy) { + for (const auto& consumerEdge : input->consumerEdges()) { + const auto& consumerInfo = consumerEdge->consumer()->getDataStridesRequirements(); + + if (consumerInfo.hasInput(consumerEdge)) { + const auto& consumerStrideReqs = consumerInfo.getInput(consumerEdge); + IE_ASSERT(input->checkStrides(consumerStrideReqs)); + + if (!checkStrides(input->desc(), output->strides(), consumerStrideReqs)) { + needCopy = true; + optionalCopy = false; + } + } + } + } + + // + // Check producer StridesRequirement. + // + + if (!needCopy) { + if (auto producerEdge = input->producerEdge()) { + const auto& producerInfo = producerEdge->producer()->getDataStridesRequirements(); + + if (producerInfo.hasOutput(producerEdge)) { + const auto& producerStrideReqs = producerInfo.getOutput(producerEdge); + IE_ASSERT(input->checkStrides(producerStrideReqs)); + + if (!checkStrides(input->desc(), output->strides(), producerStrideReqs)) { + needCopy = true; + optionalCopy = false; + } + } + + if (!needCopy) { + // + // To reduce the size of HW output (still can be optimized). + // + + if (producerEdge->producer()->category() == StageCategory::HW) { + needCopy = true; + optionalCopy = true; + } + } + } + } + } + + // + // Insert Copy if needed + // + + if (needCopy) { + Data inputCopy; + if (input->usage() == DataUsage::Const) { + inputCopy = model->addNewData( + input->name() + "@copy", + input->desc()); + } else { + inputCopy = model->duplicateData( + input, + "@copy"); + inputCopy->resetRequiredStrides(); + } + + auto copyStage = _stageBuilder->addCopyStage( + model, + formatString("%s@input=%d@copy-for-concat", stage->name(), inEdge->portInd()), + stage->origLayer(), + input, + inputCopy); + copyStage->attrs().set("optional", optionalCopy); + if (stage->attrs().has("batchInd")) { + copyStage->attrs().set("batchInd", stage->attrs().get("batchInd")); + } + + model->replaceStageInput(inEdge, inputCopy); + + input = inputCopy; + } + + // + // Add Data<->Data edge + // + + model->connectDatas() + .parent(output) + .child(input) + .mode(SharedDataMode::ROI) + .order(SharedDataOrder::ChildWritesToParent) + .offset(offsetFromOutput) + .done(); + } +} + + +void SpecialStageProcessor::processReshape( + const Model::Ptr& model, + const Stage& stage) { + auto input = stage->input(0); + auto output = stage->output(0); + + IE_ASSERT(input->desc().dimsOrder() == DimsOrder::fromNumDims(input->desc().numDims())); + IE_ASSERT(input->checkStrides(StridesRequirement::compact())); + + IE_ASSERT(output->desc().dimsOrder() == DimsOrder::fromNumDims(output->desc().numDims())); + IE_ASSERT(output->checkStrides(StridesRequirement::compact())); + + // + // Check if we need to insert Copy stage + // + + bool needCopy = false; + if (input->usage() != DataUsage::Intermediate && + output->usage() != DataUsage::Intermediate) { + needCopy = true; + } else if (input->parentDataEdge() != nullptr && + output->parentDataEdge() != nullptr) { + needCopy = true; + } + + // + // Insert Copy if needed + // + + if (needCopy) { + Data inputCopy; + if (input->usage() == DataUsage::Const) { + inputCopy = model->addNewData( + input->name() + "@copy", + input->desc()); + } else { + inputCopy = model->duplicateData( + input, + "@copy"); + } + inputCopy->updateRequiredStrides(StridesRequirement::compact()); + + auto copyStage = _stageBuilder->addCopyStage( + model, + formatString("%s@copy-for-reshape", stage->name()), + stage->origLayer(), + input, + inputCopy); + if (stage->attrs().has("batchInd")) { + copyStage->attrs().set("batchInd", stage->attrs().get("batchInd")); + } + + model->replaceStageInput(stage->inputEdge(0), inputCopy); + + input = inputCopy; + } + + // + // Add Data<->Data edge + // + + if (input->usage() == DataUsage::Intermediate && + input->parentDataEdge() == nullptr) { + model->connectDatas() + .parent(output) + .child(input) + .mode(SharedDataMode::Reshape) + .order(SharedDataOrder::ChildWritesToParent) + .done(); + } else { + IE_ASSERT(output->usage() == DataUsage::Intermediate); + IE_ASSERT(output->parentDataEdge() == nullptr); + + model->connectDatas() + .parent(input) + .child(output) + .mode(SharedDataMode::Reshape) + .order(SharedDataOrder::ParentWritesToChild) + .done(); + } +} + +void SpecialStageProcessor::processExpand( + const Model::Ptr& model, + const Stage& stage) { + auto input = stage->input(0); + auto output = stage->output(0); + + const auto& offset = stage->attrs().get("offset"); + + IE_ASSERT(input->desc().dimsOrder() == output->desc().dimsOrder()); + + IE_ASSERT(offset.size() <= checked_cast(output->desc().numDims())); + for (const auto& p : offset) { + IE_ASSERT(output->desc().dimsOrder().hasDim(p.first)); + IE_ASSERT(p.second + input->desc().dim(p.first) <= output->desc().dim(p.first)); + } + + // + // Check if we need to insert Copy stage + // + + bool needCopy = false; + bool optionalCopy = false; + if (input->usage() != DataUsage::Intermediate) { + needCopy = true; + optionalCopy = false; + } else if (input->parentDataEdge() != nullptr) { + needCopy = true; + optionalCopy = false; + } else { + // + // Check input StridesRequirement. + // + + IE_ASSERT(input->checkStrides(input->requiredStrides())); + if (!checkStrides(input->desc(), output->strides(), input->requiredStrides())) { + needCopy = true; + optionalCopy = false; + } + + // + // Check consumers StridesRequirement. + // + + if (!needCopy) { + for (const auto& consumerEdge : input->consumerEdges()) { + const auto& consumerInfo = consumerEdge->consumer()->getDataStridesRequirements(); + + if (consumerInfo.hasInput(consumerEdge)) { + const auto& consumerStrideReqs = consumerInfo.getInput(consumerEdge); + IE_ASSERT(input->checkStrides(consumerStrideReqs)); + + if (!checkStrides(input->desc(), output->strides(), consumerStrideReqs)) { + needCopy = true; + optionalCopy = false; + } + } + } + } + + // + // Check producer StridesRequirement. + // + + if (!needCopy) { + if (auto producerEdge = input->producerEdge()) { + const auto& producerInfo = producerEdge->producer()->getDataStridesRequirements(); + + if (producerInfo.hasOutput(producerEdge)) { + const auto& producerStrideReqs = producerInfo.getOutput(producerEdge); + IE_ASSERT(input->checkStrides(producerStrideReqs)); + + if (!checkStrides(input->desc(), output->strides(), producerStrideReqs)) { + needCopy = true; + optionalCopy = false; + } + } + + if (!needCopy) { + // + // To reduce the size of HW output (still can be optimized). + // + + if (producerEdge->producer()->category() == StageCategory::HW) { + needCopy = true; + optionalCopy = true; + } + } + } + } + } + + // + // Insert Copy if needed + // + + if (needCopy) { + Data inputCopy; + if (input->usage() == DataUsage::Const) { + inputCopy = model->addNewData( + input->name() + "@copy", + input->desc()); + } else { + inputCopy = model->duplicateData( + input, + "@copy"); + inputCopy->resetRequiredStrides(); + } + + auto copyStage = _stageBuilder->addCopyStage( + model, + formatString("%s@copy-for-expand", stage->name()), + stage->origLayer(), + input, + inputCopy); + copyStage->attrs().set("optional", optionalCopy); + if (stage->attrs().has("batchInd")) { + copyStage->attrs().set("batchInd", stage->attrs().get("batchInd")); + } + + model->replaceStageInput(stage->inputEdge(0), inputCopy); + + input = inputCopy; + } + + // + // Add Data<->Data edge + // + + model->connectDatas() + .parent(output) + .child(input) + .mode(SharedDataMode::ROI) + .order(SharedDataOrder::ChildWritesToParent) + .offset(offset) + .done(); +} + +void SpecialStageProcessor::processShrink( + const Model::Ptr& model, + const Stage& stage) { + auto input = stage->input(0); + auto output = stage->output(0); + + const auto& offset = stage->attrs().get("offset"); + + IE_ASSERT(input->desc().dimsOrder() == output->desc().dimsOrder()); + + IE_ASSERT(offset.size() <= checked_cast(input->desc().numDims())); + for (const auto& p : offset) { + IE_ASSERT(input->desc().dimsOrder().hasDim(p.first)); + IE_ASSERT(p.second + output->desc().dim(p.first) <= input->desc().dim(p.first)); + } + + // + // Check if we need to insert Copy for output + // + + bool needCopy = false; + if (output->usage() != DataUsage::Intermediate) { + needCopy = true; + } else if (output->parentDataEdge() != nullptr) { + needCopy = true; + } else { + // + // Check output StridesRequirement. + // + + IE_ASSERT(output->checkStrides(output->requiredStrides())); + if (!checkStrides(output->desc(), input->strides(), output->requiredStrides())) { + needCopy = true; + } + + // + // Check consumers StridesRequirement. + // + + if (!needCopy) { + for (const auto& consumerEdge : output->consumerEdges()) { + const auto& consumerInfo = consumerEdge->consumer()->getDataStridesRequirements(); + + if (consumerInfo.hasInput(consumerEdge)) { + const auto& consumerStrideReqs = consumerInfo.getInput(consumerEdge); + IE_ASSERT(output->checkStrides(consumerStrideReqs)); + + if (!checkStrides(output->desc(), input->strides(), consumerStrideReqs)) { + needCopy = true; + break; + } + } + } + } + } + + // + // Insert output Copy if needed + // + + if (needCopy) { + auto outputCopy = model->duplicateData( + output, + "@copy"); + outputCopy->resetRequiredStrides(); + + model->replaceStageOutput(stage->outputEdge(0), outputCopy); + + auto copyStage = _stageBuilder->addCopyStage( + model, + formatString("%s@copy-output-for-shrink", stage->name()), + stage->origLayer(), + outputCopy, + output); + if (stage->attrs().has("batchInd")) { + copyStage->attrs().set("batchInd", stage->attrs().get("batchInd")); + } + + output = outputCopy; + } + + // + // Add Data<->Data edge + // + + model->connectDatas() + .parent(input) + .child(output) + .mode(SharedDataMode::ROI) + .order(SharedDataOrder::ParentWritesToChild) + .offset(offset) + .done(); +} + +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/argmax.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/argmax.cpp index 7ab755e8b193bf..7dfc3eb143ecc7 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/argmax.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/argmax.cpp @@ -20,41 +20,36 @@ class ArgMaxStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); auto has_axis = attrs().get("has_axis"); if (has_axis) { - _orderInfo.setOutput(_outputEdges[0], input->desc().dimsOrder()); + orderInfo.setOutput(outputEdge(0), input->desc().dimsOrder()); } else { // axis<0 requires flatten so only NCHW layout is supported - _orderInfo.setInput(_inputEdges[0], DimsOrder::fromNumDims(input->desc().numDims())); - _orderInfo.setOutput(_outputEdges[0], DimsOrder::fromNumDims(output->desc().numDims())); + orderInfo.setInput(inputEdge(0), DimsOrder::fromNumDims(input->desc().numDims())); + orderInfo.setOutput(outputEdge(0), DimsOrder::fromNumDims(output->desc().numDims())); } } - void getDataStridesRequirementsImpl() const override { + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); auto out_max_val = attrs().get("out_max_val"); auto top_k = attrs().get("top_k"); @@ -73,11 +68,8 @@ class ArgMaxStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeNewBuffer(serializer); output->serializeNewBuffer(serializer); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/bias.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/bias.cpp index 6c29dc6396f8c8..ff9dc6d21ec35d 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/bias.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/bias.cpp @@ -40,20 +40,18 @@ class BiasStage final : public PostOpStage { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { if (step == ScalePropagationStep::Propagate) { auto inputScale = inputScales[0]; - _scaleInfo.setInput(_inputEdges[1], inputScale); - _scaleInfo.setOutput(_outputEdges[0], inputScale); + scaleInfo.setInput(inputEdge(1), inputScale); + scaleInfo.setOutput(outputEdge(0), inputScale); } else { // Bias can only propagate scaling, not generate. - _scaleInfo.setInput(_inputEdges[0], 1.0f); - _scaleInfo.setInput(_inputEdges[1], 1.0f); - _scaleInfo.setOutput(_outputEdges[0], 1.0f); + scaleInfo.setInput(inputEdge(0), 1.0f); + scaleInfo.setInput(inputEdge(1), 1.0f); + scaleInfo.setOutput(outputEdge(0), 1.0f); } } diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/clamp.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/clamp.cpp index efc01433e1abe6..11cb5c6913d045 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/clamp.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/clamp.cpp @@ -23,21 +23,19 @@ class ClampStage final : public PostOpStage { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { if (step == ScalePropagationStep::Propagate) { auto inputScale = inputScales[0]; - _scaleInfo.setOutput(_outputEdges[0], inputScale); + scaleInfo.setOutput(outputEdge(0), inputScale); attrs().get("min_value") *= inputScale; attrs().get("max_value") *= inputScale; } else { // Clamp can only propagate scaling, not generate. - _scaleInfo.setInput(_inputEdges[0], 1.0f); - _scaleInfo.setOutput(_outputEdges[0], 1.0f); + scaleInfo.setInput(inputEdge(0), 1.0f); + scaleInfo.setOutput(outputEdge(0), 1.0f); } } diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/concat.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/concat.cpp index 993ab8b9065976..dab1d971c5fd93 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/concat.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/concat.cpp @@ -27,48 +27,43 @@ class ConcatStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(!_inputEdges.empty()); - IE_ASSERT(_outputEdges.size() == 1); - - auto output = _outputEdges[0]->output(); + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { + auto output = outputEdge(0)->output(); if (step == ScalePropagationStep::Propagate) { // Keep the largest input scale factor. auto maxScale = std::numeric_limits::lowest(); - for (const auto& inEdge : _inputEdges) { + for (const auto& inEdge : inputEdges()) { maxScale = std::max(maxScale, inputScales[inEdge->portInd()]); } IE_ASSERT(maxScale > 0.0f); - for (const auto& inEdge : _inputEdges) { + for (const auto& inEdge : inputEdges()) { auto curScale = inputScales[inEdge->portInd()]; if (!isFloatEqual(curScale, maxScale)) { - _scaleInfo.setInput(inEdge, maxScale / curScale); + scaleInfo.setInput(inEdge, maxScale / curScale); } } - _scaleInfo.setOutput(_outputEdges[0], maxScale); + scaleInfo.setOutput(outputEdge(0), maxScale); } else { // Concat can only propagate scaling. - for (const auto& inEdge : _inputEdges) { - _scaleInfo.setInput(inEdge, 1.0f); + for (const auto& inEdge : inputEdges()) { + scaleInfo.setInput(inEdge, 1.0f); } - _scaleInfo.setOutput(_outputEdges[0], 1.0f); + scaleInfo.setOutput(outputEdge(0), 1.0f); } } - void propagateDataOrderImpl() const override { - IE_ASSERT(!_inputEdges.empty()); - IE_ASSERT(_outputEdges.size() == 1); - - auto output = _outputEdges[0]->output(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto output = outputEdge(0)->output(); DimsOrderMap dimsOrderVotes; - for (const auto& inEdge : _inputEdges) { + for (const auto& inEdge : inputEdges()) { dimsOrderVotes[inEdge->input()->desc().dimsOrder()]++; } @@ -96,18 +91,15 @@ class ConcatStage final : public StageNode { IE_ASSERT(finalOrder.numDims() > 0); IE_ASSERT(curVotes > 0); - for (const auto& inEdge : _inputEdges) { - _orderInfo.setInput(inEdge, finalOrder); + for (const auto& inEdge : inputEdges()) { + orderInfo.setInput(inEdge, finalOrder); } - _orderInfo.setOutput(_outputEdges[0], finalOrder); + orderInfo.setOutput(outputEdge(0), finalOrder); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(!_inputEdges.empty()); - IE_ASSERT(_outputEdges.size() == 1); - - auto output = _outputEdges[0]->output(); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + auto output = outputEdge(0)->output(); auto dimsOrder = output->desc().dimsOrder(); @@ -117,7 +109,7 @@ class ConcatStage final : public StageNode { auto minConcatDimInd = dimsOrder.numDims() - 1; - for (const auto& inEdge : _inputEdges) { + for (const auto& inEdge : inputEdges()) { auto input = inEdge->input(); for (const auto& p : output->desc().dims()) { @@ -144,7 +136,7 @@ class ConcatStage final : public StageNode { // Merge input StridesRequirement. // - for (const auto& inEdge : _inputEdges) { + for (const auto& inEdge : inputEdges()) { auto curInput = inEdge->input(); auto curInputReqs = curInput->requiredStrides(); @@ -183,19 +175,24 @@ class ConcatStage final : public StageNode { // Return merged StridesRequirement. // - for (const auto& inEdge : _inputEdges) { - _stridesInfo.setInput(inEdge, inputReqs); + for (const auto& inEdge : inputEdges()) { + stridesInfo.setInput(inEdge, inputReqs); } - _stridesInfo.setOutput(_outputEdges[0], outputReqs); + stridesInfo.setOutput(outputEdge(0), outputReqs); } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + IE_ASSERT(numInputs() > 0); + IE_ASSERT(numOutputs() == 1); + + const auto& firstInputPrecision = input(0)->desc().type(); + assertAllInputsOutputsTypes(this, {firstInputPrecision}, {firstInputPrecision}); } void serializeParamsImpl(BlobSerializer&) const override { diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/convolution.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/convolution.cpp index 16986580c2b1eb..5a6d0b6d0de957 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/convolution.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/convolution.cpp @@ -31,8 +31,10 @@ void FrontEnd::parseConvolution( auto input = inputs[0]; auto output = outputs[0]; - if (!(input->desc().numDims() == 3 || input->desc().numDims() == 4)) { - VPU_THROW_EXCEPTION << "Convolution supports only 3D or 4D input"; + bool is3D = input->desc().numDims() > 4; // i.e. == 5 + + if (input->desc().numDims() < 3 || input->desc().numDims() > 5) { + VPU_THROW_EXCEPTION << "Convolution supports only 3D or 4D or 5D input"; } if (output->desc().numDims() != input->desc().numDims()) { VPU_THROW_EXCEPTION << "Convolution supports only same num dims in input and output"; @@ -47,18 +49,23 @@ void FrontEnd::parseConvolution( int kernelSizeX = convLayer->_kernel_x; int kernelSizeY = convLayer->_kernel_y; + int kernelSizeZ = is3D ? convLayer->_kernel.at(ie::Z_AXIS) : 1; int kernelStrideX = convLayer->_stride_x; int kernelStrideY = convLayer->_stride_y; + int kernelStrideZ = is3D ? convLayer->_stride.at(ie::Z_AXIS) : 1; auto paddings = getPaddings(*convLayer); int padLeft = paddings.begin.exist(ie::X_AXIS) ? paddings.begin[ie::X_AXIS] : 0; int padRight = paddings.end.exist(ie::X_AXIS) ? paddings.end[ie::X_AXIS] : padLeft; int padTop = paddings.begin.exist(ie::Y_AXIS) ? paddings.begin[ie::Y_AXIS] : 0; int padBottom = paddings.end.exist(ie::Y_AXIS) ? paddings.end[ie::Y_AXIS] : padTop; + int padFront = paddings.begin.exist(ie::Z_AXIS) ? paddings.begin[ie::Z_AXIS] : 0; + int padBack = paddings.end.exist(ie::Z_AXIS) ? paddings.end[ie::Z_AXIS] : padFront; int dilationX = convLayer->_dilation_x; int dilationY = convLayer->_dilation_y; + int dilationZ = is3D ? convLayer->_dilation.at(ie::Z_AXIS) : 1; int groupSize = convLayer->_group; @@ -73,11 +80,11 @@ void FrontEnd::parseConvolution( } // TODO: support dilated convolution - if ((dilationX != 1 || dilationY != 1) && (!env.config.hwDilation)) { + if ((dilationX != 1 || dilationY != 1 || dilationZ != 1) && (!env.config.hwDilation)) { tryHW = false; } - if (kernelSizeX > 15 || kernelSizeY > 15 || kernelStrideX > 8) { + if (kernelSizeX > 15 || kernelSizeY > 15 || kernelSizeZ > 1 || kernelStrideX > 8) { tryHW = false; } @@ -85,7 +92,7 @@ void FrontEnd::parseConvolution( tryHW = false; } - if (output->desc().numDims() < 4) { + if (output->desc().numDims() < 4 || is3D) { tryHW = false; } @@ -97,15 +104,25 @@ void FrontEnd::parseConvolution( std::tie(weights, biases) = getWeightsAndBiases(model, layer); IE_ASSERT(weights->desc().totalDimSize() >= - kernelSizeX * kernelSizeY * (input->desc().dim(Dim::C) / groupSize) * output->desc().dim(Dim::C)); - weights = model->duplicateData( - weights, - "@conv", + kernelSizeX * kernelSizeY * kernelSizeZ * (input->desc().dim(Dim::C) / groupSize) * output->desc().dim(Dim::C)); + + auto weightsDesc = is3D ? DataDesc({ kernelSizeX, kernelSizeY, + kernelSizeZ, input->desc().dim(Dim::C) / groupSize, - output->desc().dim(Dim::C)})); + output->desc().dim(Dim::C)}) : + DataDesc({ + kernelSizeX, + kernelSizeY, + input->desc().dim(Dim::C) / groupSize, + output->desc().dim(Dim::C)}); + + weights = model->duplicateData( + weights, + "@conv", + weightsDesc); if (biases->usage() != DataUsage::Fake) { IE_ASSERT(biases->desc().totalDimSize() >= output->desc().dim(Dim::C)); @@ -140,6 +157,14 @@ void FrontEnd::parseConvolution( stage->attrs().set("dilationX", dilationX); stage->attrs().set("dilationY", dilationY); + if (is3D) { + stage->attrs().set("kernelSizeZ", kernelSizeZ); + stage->attrs().set("kernelStrideZ", kernelStrideZ); + stage->attrs().set("padFront", padFront); + stage->attrs().set("padBack", padBack); + stage->attrs().set("dilationZ", dilationZ); + } + stage->attrs().set("groupSize", groupSize); stage->attrs().set("tryHW", tryHW); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/copy.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/copy.cpp index df9fdc34461f11..e75175ac0a6fb8 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/copy.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/copy.cpp @@ -33,58 +33,48 @@ class CopyStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { if (step == ScalePropagationStep::Propagate) { - _scaleInfo.setOutput(_outputEdges[0], inputScales[0]); + scaleInfo.setOutput(outputEdge(0), inputScales[0]); } else { // Copy can only propagate scaling. - _scaleInfo.setInput(_inputEdges[0], 1.0f); - _scaleInfo.setOutput(_outputEdges[0], 1.0f); + scaleInfo.setInput(inputEdge(0), 1.0f); + scaleInfo.setOutput(outputEdge(0), 1.0f); } } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); - _orderInfo.setOutput(_outputEdges[0], input->desc().dimsOrder()); + orderInfo.setOutput(outputEdge(0), input->desc().dimsOrder()); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - _stridesInfo.setInput(_inputEdges[0], StridesRequirement().remove(0)); - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement().remove(0)); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + stridesInfo.setInput(inputEdge(0), StridesRequirement().remove(0)); + stridesInfo.setOutput(outputEdge(0), StridesRequirement().remove(0)); } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } StageSHAVEsRequirements getSHAVEsRequirementsImpl() const override { return StageSHAVEsRequirements::NotNeeded; } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer&) const override { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); if (input->desc().dimsOrder() == DimsOrder::NC) { if (!input->checkStrides(StridesRequirement().add(0, DimStride::Compact)) || diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/crop.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/crop.cpp index 030d1d7fbe63d0..04d8d672db87a4 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/crop.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/crop.cpp @@ -19,62 +19,54 @@ class CropStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(_inputEdges.size() >= 1); - IE_ASSERT(_outputEdges.size() == 1); - + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { if (step == ScalePropagationStep::Propagate) { auto inputScale = inputScales[0]; - _scaleInfo.setOutput(_outputEdges[0], inputScale); + scaleInfo.setOutput(outputEdge(0), inputScale); } else { // Crop can only propagate scaling, not generate. - for (const auto& inEdge : _inputEdges) { - _scaleInfo.setInput(inEdge, 1.0f); + for (const auto& inEdge : inputEdges()) { + scaleInfo.setInput(inEdge, 1.0f); } - _scaleInfo.setOutput(_outputEdges[0], 1.0f); + scaleInfo.setOutput(outputEdge(0), 1.0f); } } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() >= 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); auto inOrder = input->desc().dimsOrder(); // HWC only - _orderInfo.setInput(_inputEdges[0], inOrder.createMovedDim(Dim::C, 0)); - _orderInfo.setOutput(_outputEdges[0], inOrder.createMovedDim(Dim::C, 0)); + orderInfo.setInput(inputEdge(0), inOrder.createMovedDim(Dim::C, 0)); + orderInfo.setOutput(outputEdge(0), inOrder.createMovedDim(Dim::C, 0)); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() >= 1); - IE_ASSERT(_outputEdges.size() == 1); - - _stridesInfo.setInput(_inputEdges[0], StridesRequirement::compact()); - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement::compact()); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + stridesInfo.setInput(inputEdge(0), StridesRequirement::compact()); + stridesInfo.setOutput(outputEdge(0), StridesRequirement::compact()); } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() >= 1); - IE_ASSERT(_outputEdges.size() == 1); - - for (const auto& inEdge : _inputEdges) { - _batchInfo.setInput(inEdge, BatchSupport::Split); + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + for (const auto& inEdge : inputEdges()) { + batchInfo.setInput(inEdge, BatchSupport::Split); } - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } StageSHAVEsRequirements getSHAVEsRequirementsImpl() const override { return StageSHAVEsRequirements::NotNeeded; } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + IE_ASSERT(numInputs() == 1 || numInputs() == 2); + IE_ASSERT(numOutputs() == 1); + assertAllInputsOutputsTypes(this, DataType::FP16, DataType::FP16); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -86,12 +78,8 @@ class CropStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() >= 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeOldBuffer(handle_from_this(), serializer); output->serializeOldBuffer(handle_from_this(), serializer); @@ -124,12 +112,6 @@ void FrontEnd::parseCrop( << "] has invalid axis value. Expected: 0 <= axis < 4, Actual: " << cropAxis; } - if (cropAxis == 0) { - VPU_THROW_EXCEPTION - << "Layer " << layer->name << " [" << layer->type - << "] Can't crop batch channel"; - } - auto stage = model->addNewStage( layer->name, StageType::Crop, diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/ctc_decoder.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/ctc_decoder.cpp index ca9ccf90fa4740..026105e1e4fd61 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/ctc_decoder.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/ctc_decoder.cpp @@ -18,54 +18,42 @@ class CTCDecoderStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); auto cInd = input->desc().dimsOrder().dimInd(Dim::C); - _orderInfo.setOutput(_outputEdges[0], output->desc().dimsOrder().createMovedDim(Dim::C, cInd)); + orderInfo.setOutput(outputEdge(0), output->desc().dimsOrder().createMovedDim(Dim::C, cInd)); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - _stridesInfo.setInput(_inputEdges[0], StridesRequirement::compact()); - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement::compact()); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + stridesInfo.setInput(inputEdge(0), StridesRequirement::compact()); + stridesInfo.setOutput(outputEdge(0), StridesRequirement::compact()); } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } StageSHAVEsRequirements getSHAVEsRequirementsImpl() const override { return StageSHAVEsRequirements::OnlyOne; } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}, {DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer&) const override { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input0 = _inputEdges[0]->input(); - auto input1 = _inputEdges[1]->input(); - auto output = _outputEdges[0]->output(); + auto input0 = inputEdge(0)->input(); + auto input1 = inputEdge(1)->input(); + auto output = outputEdge(0)->output(); input0->serializeOldBuffer(handle_from_this(), serializer); input1->serializeOldBuffer(handle_from_this(), serializer); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/custom.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/custom.cpp index 89ac460d187e65..dd4490fc9b225f 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/custom.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/custom.cpp @@ -41,66 +41,67 @@ class CustomStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { const auto& inputOrders = attrs().get>("inputOrders"); const auto& outputOrders = attrs().get>("outputOrders"); - for (const auto& inEdge : _inputEdges) { + for (const auto& inEdge : inputEdges()) { // last input is always OpenCL binary, so use it as is. - if (inEdge->portInd() == _inputEdges.size() - 1) { + if (inEdge->portInd() == numInputs() - 1) { break; } auto it = inputOrders.find(inEdge->portInd()); if (it != inputOrders.end()) { auto requiredOrder = it->second; - _orderInfo.setInput(inEdge, requiredOrder); + orderInfo.setInput(inEdge, requiredOrder); } } - for (const auto& outEdge : _outputEdges) { + for (const auto& outEdge : outputEdges()) { auto it = outputOrders.find(outEdge->portInd()); if (it != outputOrders.end()) { auto requiredOrder = it->second; - _orderInfo.setOutput(outEdge, requiredOrder); + orderInfo.setOutput(outEdge, requiredOrder); } } } - void getDataStridesRequirementsImpl() const override { - for (const auto& inEdge : _inputEdges) { + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + for (const auto& inEdge : inputEdges()) { // last input is always OpenCL binary, so use it as is. - if (inEdge->portInd() == _inputEdges.size() - 1) { + if (inEdge->portInd() == numInputs() - 1) { break; } - _stridesInfo.setInput(inEdge, StridesRequirement::compact()); + stridesInfo.setInput(inEdge, StridesRequirement::compact()); } - for (const auto& outEdge : _outputEdges) { - _stridesInfo.setOutput(outEdge, StridesRequirement::compact()); + for (const auto& outEdge : outputEdges()) { + stridesInfo.setOutput(outEdge, StridesRequirement::compact()); } } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { - for (const auto& inEdge : _inputEdges) { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + std::vector formats = attrs().get>("formats"); + + for (const auto& inEdge : inputEdges()) { + IE_ASSERT(inEdge->portInd() < formats.size()); + // last input is always OpenCL binary, so use it as is. - if (inEdge->portInd() == _inputEdges.size() - 1) { + if ((inEdge->portInd() == numInputs() - 1) || (formats[inEdge->portInd()] == CustomDataFormat::Any)) { break; } - _batchInfo.setInput(inEdge, BatchSupport::Split); + batchInfo.setInput(inEdge, BatchSupport::Split); } - for (const auto& outEdge : _outputEdges) { - _batchInfo.setOutput(outEdge, BatchSupport::Split); + for (const auto& outEdge : outputEdges()) { + batchInfo.setOutput(outEdge, BatchSupport::Split); } } - void finalCheckImpl() const override { - } - void serializeParamsImpl(BlobSerializer& serializer) const override { const auto& customLayer = attrs().get("customLayer"); const auto& gws = attrs().get>("gws"); @@ -136,7 +137,7 @@ class CustomStage final : public StageNode { // Total number of blobs // - serializer.append(static_cast(_inputEdges.size() + _outputEdges.size())); + serializer.append(static_cast(numInputs() + numOutputs())); // // Number of kernel parameters @@ -200,15 +201,26 @@ class CustomStage final : public StageNode { auto blob = parameter.irSource.substr(0, pos); auto dim = parameter.irSource.substr(pos + 1, std::string::npos); + IE_ASSERT(dim.length() == 1) + << "Unable to deduce parameter " << parameter.argName << " for " + << _origLayer->type <<" layer. Name is: " << _origLayer->name; + char dimLetter = dim[0]; + ie::DataPtr origData; if (blob == "I") { origData = _origLayer->insData[parameter.portIndex].lock(); } else { - origData = _origLayer->outData[0]; + origData = _origLayer->outData[parameter.portIndex]; } IE_ASSERT(origData != nullptr); auto dims = origData->getDims(); + int ndims = dims.size(); + + if (ndims > 4) + VPU_THROW_EXCEPTION + << "Unable to deduce parameter " << parameter.argName << " for " + << _origLayer->type <<" layer. Name is: " << _origLayer->name; const std::map vars = { { 'b', 0 }, { 'B', 0 }, @@ -217,8 +229,9 @@ class CustomStage final : public StageNode { { 'x', 3 }, { 'X', 3 }, }; - if (vars.find(dim[0]) != vars.end()) { - auto res = dims.at(vars.at(dim[0])); + auto var = vars.find(dimLetter); + if (var != vars.end()) { + auto res = dims.at(var->second-4+ndims); serializer.append(static_cast(res)); serializer.append(static_cast(-1)); @@ -258,15 +271,19 @@ class CustomStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_tempBufferEdges.empty()); + IE_ASSERT(numTempBuffers() == 1); - for (const auto& inEdge : _inputEdges) { + for (const auto& inEdge : inputEdges()) { inEdge->input()->serializeOldBuffer(handle_from_this(), serializer); } - for (const auto& outEdge : _outputEdges) { + for (const auto& outEdge : outputEdges()) { outEdge->output()->serializeOldBuffer(handle_from_this(), serializer); } + + for (const auto& tempEdge : tempBufferEdges()) { + tempEdge->tempBuffer()->serializeOldBuffer(handle_from_this(), serializer); + } } }; @@ -362,15 +379,18 @@ void FrontEnd::parseCustom( auto customLayer = customLayersForType[stage_num]; std::map ports; + std::vector formats; // Gather inputs DataVector stageInputs; for (auto& param : customLayer->bindings()) { if (param.type == CustomParamType::Input) { ports[param.argName] = stageInputs.size(); + formats.emplace_back(param.format); stageInputs.emplace_back(inputs[param.portIndex]); } else if (param.type == CustomParamType::InputBuffer) { ports[param.argName] = stageInputs.size(); + formats.emplace_back(CustomDataFormat::BFYX); stageInputs.emplace_back(tempBuffsMap[param.portIndex]); } } @@ -386,12 +406,14 @@ void FrontEnd::parseCustom( DataDesc({origBlob->size()}), ieBlobContent(origBlob)); ports[param.argName] = stageInputs.size(); + formats.emplace_back(param.format); stageInputs.emplace_back(std::move(customBlob)); } } } customLayer->setStageNumInputs(stageInputs.size()); + formats.emplace_back(CustomDataFormat::Any); // Get kernel binary auto kernelNode = kernelNodes.find(customLayer->kernelBinary()); @@ -429,6 +451,7 @@ void FrontEnd::parseCustom( stage->attrs().set("customLayer", customLayer); stage->attrs().set("ports", ports); + stage->attrs().set("formats", formats); SmallVector gws; SmallVector lws; @@ -447,25 +470,27 @@ void FrontEnd::parseCustom( b2b[kp.argName] = kp; } - const std::map formats = { + const std::map formatsMap = { { CustomDataFormat::BYXF, DimsOrder::NHWC }, - { CustomDataFormat::BFYX, DimsOrder::NCHW } + { CustomDataFormat::BFYX, DimsOrder::NCHW }, + { CustomDataFormat::YXF, DimsOrder::HWC }, + { CustomDataFormat::FYX, DimsOrder::CHW } }; for (const auto& kp : customLayer->parameters()) { const auto& parameter = b2b[kp]; if (parameter.type == CustomParamType::Input) { - auto it = formats.find(parameter.format); - if (it != formats.end()) { + auto it = formatsMap.find(parameter.format); + if (it != formatsMap.end()) { auto requiredOrder = it->second; inputOrders[parameter.portIndex] = requiredOrder; } } if (parameter.type == CustomParamType::Output) { - auto it = formats.find(parameter.format); - if (it != formats.end()) { + auto it = formatsMap.find(parameter.format); + if (it != formatsMap.end()) { auto requiredOrder = it->second; outputOrders[parameter.portIndex] = requiredOrder; } @@ -474,6 +499,11 @@ void FrontEnd::parseCustom( stage->attrs().set("inputOrders", std::move(inputOrders)); stage->attrs().set("outputOrders", std::move(outputOrders)); + + int buffer_size = customLayer->kernelBinary().length() + 1024; + model->addTempBuffer( + stage, + DataDesc({buffer_size})); } } diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/detection_output.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/detection_output.cpp index afbf79db5a22cf..7e3e02115dbcb2 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/detection_output.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/detection_output.cpp @@ -95,28 +95,28 @@ class DetectionOutputStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 3 || _inputEdges.size() == 5); - IE_ASSERT(_outputEdges.size() == 1); - - for (const auto& inEdge : _inputEdges) { - _stridesInfo.setInput(inEdge, StridesRequirement::compact()); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + for (const auto& inEdge : inputEdges()) { + stridesInfo.setInput(inEdge, StridesRequirement::compact()); } - for (const auto& outEdge : _outputEdges) { - _stridesInfo.setOutput(outEdge, StridesRequirement::compact()); + for (const auto& outEdge : outputEdges()) { + stridesInfo.setOutput(outEdge, StridesRequirement::compact()); } } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + IE_ASSERT(numInputs() == 3 || numInputs() == 5); + IE_ASSERT(numOutputs() == 1); + assertAllInputsOutputsTypes(this, DataType::FP16, DataType::FP16); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -126,25 +126,21 @@ class DetectionOutputStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 3 || _inputEdges.size() == 5); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.size() == 1); - - auto loc = _inputEdges[0]->input(); - auto conf = _inputEdges[1]->input(); - auto priors = _inputEdges[2]->input(); - auto output = _outputEdges[0]->output(); + auto loc = inputEdge(0)->input(); + auto conf = inputEdge(1)->input(); + auto priors = inputEdge(2)->input(); + auto output = outputEdge(0)->output(); loc->serializeNewBuffer(serializer); conf->serializeNewBuffer(serializer); priors->serializeNewBuffer(serializer); - if (_inputEdges.size() == 5) { - _inputEdges[3]->input()->serializeNewBuffer(serializer); - _inputEdges[4]->input()->serializeNewBuffer(serializer); + if (numInputs() == 5) { + inputEdge(3)->input()->serializeNewBuffer(serializer); + inputEdge(4)->input()->serializeNewBuffer(serializer); } output->serializeNewBuffer(serializer); - _tempBufferEdges[0]->tempBuffer()->serializeNewBuffer(serializer); + tempBuffer(0)->serializeNewBuffer(serializer); } }; diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/eltwise.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/eltwise.cpp index c4094fb55514c1..71494a39eb8c4e 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/eltwise.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/eltwise.cpp @@ -72,7 +72,6 @@ const std::map& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - auto output = _outputEdges[0]->output(); + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { + auto output = outputEdge(0)->output(); if (_type != StageType::Prod && step == ScalePropagationStep::Propagate) { // Keep the largest input scale factor. auto maxScale = std::numeric_limits::lowest(); - for (const auto& inEdge : _inputEdges) { + for (const auto& inEdge : inputEdges()) { if (inEdge->input()->usage() == DataUsage::Fake) { continue; } @@ -101,7 +98,7 @@ class EltwiseStage final : public StageNode { maxScale = std::max(maxScale, inputScales[inEdge->portInd()]); } - for (const auto& inEdge : _inputEdges) { + for (const auto& inEdge : inputEdges()) { if (inEdge->input()->usage() == DataUsage::Fake) { continue; } @@ -109,29 +106,26 @@ class EltwiseStage final : public StageNode { auto curScale = inputScales[inEdge->portInd()]; if (!isFloatEqual(curScale, maxScale)) { - _scaleInfo.setInput(inEdge, maxScale / curScale); + scaleInfo.setInput(inEdge, maxScale / curScale); } } - _scaleInfo.setOutput(_outputEdges[0], maxScale); + scaleInfo.setOutput(outputEdge(0), maxScale); } else { // Eltwise can only propagate scaling for Sum and Max cases. - for (const auto& inEdge : _inputEdges) { - _scaleInfo.setInput(inEdge, 1.0f); + for (const auto& inEdge : inputEdges()) { + scaleInfo.setInput(inEdge, 1.0f); } - _scaleInfo.setOutput(_outputEdges[0], 1.0f); + scaleInfo.setOutput(outputEdge(0), 1.0f); } } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - auto input0 = _inputEdges[0]->input(); - auto input1 = _inputEdges[1]->input(); - auto input2 = _inputEdges[2]->input(); - auto output = _outputEdges[0]->output(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input0 = inputEdge(0)->input(); + auto input1 = inputEdge(1)->input(); + auto input2 = inputEdge(2)->input(); + auto output = outputEdge(0)->output(); auto in0Desc = input0->desc(); auto in1Desc = input1->desc(); @@ -159,26 +153,27 @@ class EltwiseStage final : public StageNode { finalOrder = outDesc.dimsOrder(); } - _orderInfo.setInput(_inputEdges[0], finalOrder.numDims() == in0Desc.numDims() ? finalOrder : in0Desc.dimsOrder()); - _orderInfo.setInput(_inputEdges[1], finalOrder.numDims() == in1Desc.numDims() ? finalOrder : in1Desc.dimsOrder()); - _orderInfo.setInput(_inputEdges[2], finalOrder.numDims() == in2Desc.numDims() ? finalOrder : in2Desc.dimsOrder()); - _orderInfo.setOutput(_outputEdges[0], finalOrder); + orderInfo.setInput(inputEdge(0), finalOrder.numDims() == in0Desc.numDims() ? finalOrder : in0Desc.dimsOrder()); + orderInfo.setInput(inputEdge(1), finalOrder.numDims() == in1Desc.numDims() ? finalOrder : in1Desc.dimsOrder()); + orderInfo.setInput(inputEdge(2), finalOrder.numDims() == in2Desc.numDims() ? finalOrder : in2Desc.dimsOrder()); + orderInfo.setOutput(outputEdge(0), finalOrder); } - void getDataStridesRequirementsImpl() const override { + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } StageSHAVEsRequirements getSHAVEsRequirementsImpl() const override { return StageSHAVEsRequirements::CanBeLimited; } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}, {DataType::FP16}, {DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -198,14 +193,10 @@ class EltwiseStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input0 = _inputEdges[0]->input(); - auto input1 = _inputEdges[1]->input(); - auto input2 = _inputEdges[2]->input(); - auto output = _outputEdges[0]->output(); + auto input0 = inputEdge(0)->input(); + auto input1 = inputEdge(1)->input(); + auto input2 = inputEdge(2)->input(); + auto output = outputEdge(0)->output(); input0->serializeNewBuffer(serializer, output->desc().dimsOrder()); output->serializeNewBuffer(serializer); @@ -255,7 +246,7 @@ void FrontEnd::parseEltwise( auto output = outputs[0]; auto tempOutput = output; - if ((stageType != StageType::Select) && (inputs.size() > 2)) { + if (inputs.size() > 2) { tempOutput = model->duplicateData( output, formatString("@temp@1/%d", inputs.size() - 2)); @@ -269,10 +260,7 @@ void FrontEnd::parseEltwise( else tempInputs[1] = inputs[1]; - if (stageType == StageType::Select) - tempInputs[2] = inputs[2]; - else - tempInputs[2] = model->addFakeData(); + tempInputs[2] = model->addFakeData(); auto stage = model->addNewStage( layer->name, @@ -298,33 +286,51 @@ void FrontEnd::parseEltwise( stage->attrs().set("min_value", 0.0f); stage->attrs().set("max_value", 1.0f); - if (stageType != StageType::Select) { - tempInputs[0] = tempOutput; - for (int ind = 2; ind < inputs.size(); ++ind) { - tempInputs[1] = inputs[ind]; - - if (ind + 1 == inputs.size()) { - tempOutput = output; - } else { - tempOutput = model->duplicateData( - output, - formatString("@temp@%d/%d", ind, inputs.size() - 2)); - } + tempInputs[0] = tempOutput; + for (int ind = 2; ind < inputs.size(); ++ind) { + tempInputs[1] = inputs[ind]; - stage = model->addNewStage( - layer->name + "@" + std::to_string(ind - 1), - stageType, - layer, - tempInputs, - {tempOutput}); + if (ind + 1 == inputs.size()) { + tempOutput = output; + } else { + tempOutput = model->duplicateData( + output, + formatString("@temp@%d/%d", ind, inputs.size() - 2)); + } - if (layer->coeff.size() > ind) { - stage->attrs().set("coeff2", layer->coeff[ind]); - } + stage = model->addNewStage( + layer->name + "@" + std::to_string(ind - 1), + stageType, + layer, + tempInputs, + {tempOutput}); - tempInputs[0] = tempOutput; + if (layer->coeff.size() > ind) { + stage->attrs().set("coeff2", layer->coeff[ind]); } + + tempInputs[0] = tempOutput; + } +} + +void FrontEnd::parseSelect( + const Model::Ptr& model, + const ie::CNNLayerPtr& _layer, + const DataVector& inputs, + const DataVector& outputs) { + auto layer = std::dynamic_pointer_cast(_layer); + IE_ASSERT(layer != nullptr); + + if (inputs.size() != 3) { + VPU_THROW_EXCEPTION << "Select supports only three inputs"; } + + auto stage = model->addNewStage( + layer->name, + StageType::Select, + layer, + inputs, + outputs); } Stage StageBuilder::addSumStage( diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/exp.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/exp.cpp new file mode 100644 index 00000000000000..9204767179f188 --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/src/stages/exp.cpp @@ -0,0 +1,46 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include +#include + +#include + +namespace vpu { + +namespace { + +class ExpStage final : public PostOpStage { +private: + StagePtr cloneImpl() const override { + return std::make_shared(*this); + } + + void serializeParamsImpl(BlobSerializer&) const override { + } +}; + +} // namespace + +void FrontEnd::parseExp( + const Model::Ptr& model, + const ie::CNNLayerPtr& layer, + const DataVector& inputs, + const DataVector& outputs) { + IE_ASSERT(inputs.size() == 1); + IE_ASSERT(outputs.size() == 1); + + model->addNewStage( + layer->name, + StageType::Exp, + layer, + inputs, + outputs); +} + +} // namespace vpu + diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/broadcast.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/expand.cpp similarity index 62% rename from inference-engine/src/vpu/graph_transformer/src/stages/broadcast.cpp rename to inference-engine/src/vpu/graph_transformer/src/stages/expand.cpp index d8fdd0fd3737b6..0fced457b27d41 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/broadcast.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/expand.cpp @@ -17,49 +17,44 @@ namespace vpu { namespace { -class BroadcastStage final : public StageNode { +class ExpandStage final : public StageNode { protected: StagePtr cloneImpl() const override { - return std::make_shared(*this); + return std::make_shared(*this); } void propagateScaleFactorsImpl( const SmallVector&, - ScalePropagationStep) override { + ScalePropagationStep, + StageDataInfo&) override { VPU_THROW_EXCEPTION << "Must never be called"; } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); - auto input = _inputEdges[0]->input(); - - _orderInfo.setOutput(_outputEdges[0], input->desc().dimsOrder()); + orderInfo.setOutput(outputEdge(0), input->desc().dimsOrder()); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); auto dimsOrder = output->desc().dimsOrder(); // - // Get smallest Dim over which Broadcast is done. + // Get smallest Dim over which Expand is done. // - auto minBroadcastDimInd = dimsOrder.numDims(); + auto minExpandDimInd = dimsOrder.numDims(); for (const auto& p : output->desc().dims()) { if (input->desc().dim(p.first) != p.second) { - minBroadcastDimInd = std::min(minBroadcastDimInd, dimsOrder.dimInd(p.first)); + minExpandDimInd = std::min(minExpandDimInd, dimsOrder.dimInd(p.first)); } } - IE_ASSERT(minBroadcastDimInd < dimsOrder.numDims()); + IE_ASSERT(minExpandDimInd < dimsOrder.numDims()); // // Initial StridesRequirement for input and output. @@ -68,7 +63,7 @@ class BroadcastStage final : public StageNode { auto outputReqs = output->requiredStrides(); auto inputReqs = outputReqs; - for (int i = minBroadcastDimInd + 1; i < dimsOrder.numDims(); ++i) { + for (int i = minExpandDimInd + 1; i < dimsOrder.numDims(); ++i) { inputReqs.remove(i); } @@ -82,7 +77,7 @@ class BroadcastStage final : public StageNode { if (consumerInfo.hasInput(consumerEdge)) { const auto& consumerReqs = consumerInfo.getInput(consumerEdge); - for (int i = 0; i < minBroadcastDimInd + 1; ++i) { + for (int i = 0; i < minExpandDimInd + 1; ++i) { if (outputReqs.get(i) == DimStride::Any) { if (consumerReqs.get(i) != DimStride::Any) { inputReqs.add(i, consumerReqs.get(i)); @@ -97,17 +92,19 @@ class BroadcastStage final : public StageNode { // Return merged StridesRequirements. // - _stridesInfo.setInput(_inputEdges[0], inputReqs); - _stridesInfo.setOutput(_outputEdges[0], outputReqs); + stridesInfo.setInput(inputEdge(0), inputReqs); + stridesInfo.setOutput(outputEdge(0), outputReqs); } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + const auto& firstInputPrecision = input(0)->desc().type(); + assertInputsOutputsTypes(this, {{firstInputPrecision}}, {{firstInputPrecision}}); } void serializeParamsImpl(BlobSerializer&) const override { @@ -121,16 +118,16 @@ class BroadcastStage final : public StageNode { } // namespace -Stage StageBuilder::addBroadcastStage( +Stage StageBuilder::addExpandStage( const Model::Ptr& model, const std::string& name, const ie::CNNLayerPtr& layer, const Data& input, const Data& output, const DimValues& offset) { - auto stage = model->addNewStage( + auto stage = model->addNewStage( name, - StageType::Broadcast, + StageType::Expand, layer, {input}, {output}); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/floor.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/floor.cpp new file mode 100644 index 00000000000000..bbc586996718cd --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/src/stages/floor.cpp @@ -0,0 +1,45 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include +#include + +#include + +namespace vpu { + +namespace { + +class FloorStage final : public PostOpStage { +private: + StagePtr cloneImpl() const override { + return std::make_shared(*this); + } + + void serializeParamsImpl(BlobSerializer&) const override { + } +}; + +} // namespace + +void FrontEnd::parseFloor( + const Model::Ptr& model, + const ie::CNNLayerPtr& layer, + const DataVector& inputs, + const DataVector& outputs) { + IE_ASSERT(inputs.size() == 1); + IE_ASSERT(outputs.size() == 1); + + model->addNewStage( + layer->name, + StageType::Floor, + layer, + inputs, + outputs); +} + +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/gather.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/gather.cpp index d5dea793664047..29e704e74039b6 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/gather.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/gather.cpp @@ -1,17 +1,5 @@ -// -// Copyright 2019 Intel Corporation. -// -// This software and the related documents are Intel copyrighted materials, -// and your use of them is governed by the express license under which they -// were provided to you (End User License Agreement for the Intel(R) Software -// Development Products (Version May 2017)). Unless the License provides -// otherwise, you may not use, modify, copy, publish, distribute, disclose or -// transmit this software or the related documents without Intel's prior -// written permission. -// -// This software and the related documents are provided as is, with no -// express or implied warranties, other than those that are expressly -// stated in the License. +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 // #include @@ -55,69 +43,60 @@ class GatherStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - if (step == ScalePropagationStep::Propagate) { - _scaleInfo.setOutput(_outputEdges[0], inputScales[0]); - } else { - // Gather can only propagate scaling. - for (const auto& inEdge : _inputEdges) { - _scaleInfo.setInput(inEdge, 1.0f); - } - _scaleInfo.setOutput(_outputEdges[0], 1.0f); - } + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { + if (step == ScalePropagationStep::Propagate) { + scaleInfo.setOutput(outputEdge(0), inputScales[0]); + } else { + // Gather can only propagate scaling. + for (const auto& inEdge : inputEdges()) { + scaleInfo.setInput(inEdge, 1.0f); + } + scaleInfo.setOutput(outputEdge(0), 1.0f); + } } - void propagateDataOrderImpl() const override { + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - for (const auto& inEdge : _inputEdges) { - _stridesInfo.setInput(inEdge, StridesRequirement::compact()); - } - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement::compact()); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + for (const auto& inEdge : inputEdges()) { + stridesInfo.setInput(inEdge, StridesRequirement::compact()); + } + stridesInfo.setOutput(outputEdge(0), StridesRequirement::compact()); } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } StageSHAVEsRequirements getSHAVEsRequirementsImpl() const override { return StageSHAVEsRequirements::NotNeeded; } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}, {DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 2); - - auto input = _inputEdges[0]->input(); + auto input = inputEdge(0)->input(); - auto axis = attrs().get("axis"); - auto axisInd = input->desc().dimsOrder().dimInd(axis); + auto axis = attrs().get("axis"); + auto axisInd = input->desc().dimsOrder().dimInd(axis); - serializer.append(static_cast(axisInd)); + serializer.append(static_cast(axisInd)); } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - auto input0 = _inputEdges[0]->input(); - auto input1 = _inputEdges[1]->input(); - auto output = _outputEdges[0]->output(); + auto input0 = inputEdge(0)->input(); + auto input1 = inputEdge(1)->input(); + auto output = outputEdge(0)->output(); - input0->serializeNewBuffer(serializer); - output->serializeNewBuffer(serializer); - input1->serializeNewBuffer(serializer); + input0->serializeNewBuffer(serializer); + output->serializeNewBuffer(serializer); + input1->serializeNewBuffer(serializer); } }; diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/gemm.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/gemm.cpp index d297d0117b5aac..fdf3c399778196 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/gemm.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/gemm.cpp @@ -1,17 +1,5 @@ -// -// Copyright (C) 2019 Intel Corporation. -// -// This software and the related documents are Intel copyrighted materials, -// and your use of them is governed by the express license under which they -// were provided to you (End User License Agreement for the Intel(R) Software -// Development Products (Version May 2017)). Unless the License provides -// otherwise, you may not use, modify, copy, publish, distribute, disclose or -// transmit this software or the related documents without Intel's prior -// written permission. -// -// This software and the related documents are provided as is, with no -// express or implied warranties, other than those that are expressly -// stated in the License. +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 // #include @@ -33,14 +21,11 @@ class GEMMStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto inputDimsOrder0 = inputEdge(0)->input()->desc().dimsOrder(); + auto inputDimsOrder1 = inputEdge(1)->input()->desc().dimsOrder(); - auto inputDimsOrder0 = _inputEdges[0]->input()->desc().dimsOrder(); - auto inputDimsOrder1 = _inputEdges[1]->input()->desc().dimsOrder(); - auto inputDimsOrder2 = _inputEdges[2]->input()->desc().dimsOrder(); - auto outputDimsOrder = _outputEdges[0]->output()->desc().dimsOrder(); + auto outputDimsOrder = outputEdge(0)->output()->desc().dimsOrder(); if (inputDimsOrder0.numDims() >= 3) { inputDimsOrder0.moveDim(Dim::C, 2); // ->...CHW @@ -48,61 +33,60 @@ class GEMMStage final : public StageNode { if (inputDimsOrder1.numDims() >= 3) { inputDimsOrder1.moveDim(Dim::C, 2); // ->...CHW } - if (inputDimsOrder2.numDims() >= 3) { - inputDimsOrder2.moveDim(Dim::C, 2); // ->...CHW - } if (outputDimsOrder.numDims() >= 3) { outputDimsOrder.moveDim(Dim::C, 2); // ->...CHW } - _orderInfo.setInput(_inputEdges[0], inputDimsOrder0); - _orderInfo.setInput(_inputEdges[1], inputDimsOrder1); - _orderInfo.setInput(_inputEdges[2], inputDimsOrder2); - _orderInfo.setOutput(_outputEdges[0], outputDimsOrder); + orderInfo.setInput(inputEdge(0), inputDimsOrder0); + orderInfo.setInput(inputEdge(1), inputDimsOrder1); + orderInfo.setOutput(outputEdge(0), outputDimsOrder); + + if (numInputs() == 3) { + auto inputDimsOrder2 = inputEdge(2)->input()->desc().dimsOrder(); + if (inputDimsOrder2.numDims() >= 3) { + inputDimsOrder2.moveDim(Dim::C, 2); // ->...CHW + } + orderInfo.setInput(inputEdge(2), inputDimsOrder2); + } } - void getDataStridesRequirementsImpl() const override { + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + IE_ASSERT(numInputs() == 2 || numInputs() == 3); + IE_ASSERT(numOutputs() == 1); + assertAllInputsOutputsTypes(this, DataType::FP16, DataType::FP16); } void serializeParamsImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - auto alpha = attrs().get("alpha"); auto beta = attrs().get("beta"); auto transposeA = attrs().get("transposeA"); auto transposeB = attrs().get("transposeB"); + auto hasThreeInputs = numInputs() == 3; serializer.append(static_cast(alpha)); serializer.append(static_cast(beta)); + serializer.append(static_cast(hasThreeInputs)); serializer.append(static_cast(transposeA)); serializer.append(static_cast(transposeB)); } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input1 = _inputEdges[0]->input(); - auto input2 = _inputEdges[1]->input(); - auto input3 = _inputEdges[2]->input(); - auto output = _outputEdges[0]->output(); - - input1->serializeNewBuffer(serializer); - input2->serializeNewBuffer(serializer); - input3->serializeNewBuffer(serializer); - output->serializeNewBuffer(serializer); + inputEdge(0)->input()->serializeNewBuffer(serializer); + inputEdge(1)->input()->serializeNewBuffer(serializer); + if (numInputs() == 3) { + inputEdge(2)->input()->serializeNewBuffer(serializer); + } + outputEdge(0)->output()->serializeNewBuffer(serializer); } }; @@ -113,7 +97,7 @@ void FrontEnd::parseGEMM( const ie::CNNLayerPtr& _layer, const DataVector& inputs, const DataVector& outputs) { - IE_ASSERT(inputs.size() == 3); + IE_ASSERT(inputs.size() == 2 || inputs.size() == 3); IE_ASSERT(outputs.size() == 1); auto layer = std::dynamic_pointer_cast(_layer); @@ -127,9 +111,7 @@ void FrontEnd::parseGEMM( layer->beta, layer->transpose_a, layer->transpose_b, - inputs[0], - inputs[1], - inputs[2], + inputs, outputs[0]); } @@ -141,15 +123,13 @@ Stage StageBuilder::addGemmStage( const float beta, const bool transposeA, const bool transposeB, - const Data& inputA, - const Data& inputB, - const Data& inputC, + const DataVector& inputs, const Data& output) { auto stage = model->addNewStage( name, StageType::GEMM, layer, - {inputA, inputB, inputC}, + inputs, {output}); stage->attrs().set("alpha", alpha); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/grn.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/grn.cpp index 633d457c5de40e..e6088954a2b89e 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/grn.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/grn.cpp @@ -19,30 +19,25 @@ class GRNStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); - auto input = _inputEdges[0]->input(); - - _orderInfo.setOutput(_outputEdges[0], input->desc().dimsOrder()); + orderInfo.setOutput(outputEdge(0), input->desc().dimsOrder()); } - void getDataStridesRequirementsImpl() const override { + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -52,12 +47,8 @@ class GRNStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeNewBuffer(serializer); output->serializeNewBuffer(serializer); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/interp.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/interp.cpp index 40e6e95917cc74..8a56b4b20b5d06 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/interp.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/interp.cpp @@ -19,30 +19,25 @@ class InterpStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); - auto input = _inputEdges[0]->input(); - - _orderInfo.setOutput(_outputEdges[0], input->desc().dimsOrder()); + orderInfo.setOutput(outputEdge(0), input->desc().dimsOrder()); } - void getDataStridesRequirementsImpl() const override { + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -52,12 +47,8 @@ class InterpStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeOldBuffer(handle_from_this(), serializer); output->serializeOldBuffer(handle_from_this(), serializer); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/log.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/log.cpp index a3f886d0388793..7363df5c87ef1e 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/log.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/log.cpp @@ -1,17 +1,5 @@ -// -// Copyright 2019 Intel Corporation. -// -// This software and the related documents are Intel copyrighted materials, -// and your use of them is governed by the express license under which they -// were provided to you (End User License Agreement for the Intel(R) Software -// Development Products (Version May 2017)). Unless the License provides -// otherwise, you may not use, modify, copy, publish, distribute, disclose or -// transmit this software or the related documents without Intel's prior -// written permission. -// -// This software and the related documents are provided as is, with no -// express or implied warranties, other than those that are expressly -// stated in the License. +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 // #include diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/mtcnn.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/mtcnn.cpp index e202b585563767..d9d69b8220747d 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/mtcnn.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/mtcnn.cpp @@ -32,37 +32,31 @@ class MTCNNStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); - - _orderInfo.setInput(_inputEdges[0], input->desc().dimsOrder().createMovedDim(Dim::C, 2)); - _orderInfo.setOutput(_outputEdges[0], output->desc().dimsOrder().createMovedDim(Dim::C, 0)); + orderInfo.setInput(inputEdge(0), input->desc().dimsOrder().createMovedDim(Dim::C, 2)); + orderInfo.setOutput(outputEdge(0), output->desc().dimsOrder().createMovedDim(Dim::C, 0)); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - _stridesInfo.setInput(_inputEdges[0], StridesRequirement::compact()); - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement::compact()); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + stridesInfo.setInput(inputEdge(0), StridesRequirement::compact()); + stridesInfo.setOutput(outputEdge(0), StridesRequirement::compact()); } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, + {{DataType::U8, DataType::FP16}, {DataType::U8, DataType::FP16}}, + {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -85,13 +79,9 @@ class MTCNNStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input0 = _inputEdges[0]->input(); - auto input1 = _inputEdges[1]->input(); - auto output = _outputEdges[0]->output(); + auto input0 = inputEdge(0)->input(); + auto input1 = inputEdge(1)->input(); + auto output = outputEdge(0)->output(); input0->serializeOldBuffer(handle_from_this(), serializer); output->serializeOldBuffer(handle_from_this(), serializer); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/mvn.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/mvn.cpp index 37736e2902d3ad..63e3e4c5ce2777 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/mvn.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/mvn.cpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace vpu { @@ -20,47 +21,40 @@ class MVNStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); - auto input = _inputEdges[0]->input(); - - _orderInfo.setOutput(_outputEdges[0], input->desc().dimsOrder()); + orderInfo.setOutput(outputEdge(0), input->desc().dimsOrder()); } - void getDataStridesRequirementsImpl() const override { + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { auto normalize = attrs().get("normalize"); auto across_channels = attrs().get("across_channels"); + auto eps = attrs().get("eps"); serializer.append(static_cast(normalize)); serializer.append(static_cast(across_channels)); + serializer.append(static_cast(eps)); } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeOldBuffer(handle_from_this(), serializer); output->serializeOldBuffer(handle_from_this(), serializer); @@ -80,15 +74,6 @@ void FrontEnd::parseMVN( auto layer = std::dynamic_pointer_cast(_layer); IE_ASSERT(layer != nullptr); - float def_eps = 1e-9f; - float eps = layer->GetParamAsFloat("eps", def_eps); - - if (eps > 1e-7f) { - VPU_THROW_EXCEPTION - << "Layer " << layer->name << " [" << layer->type - << "] in our kernel we use const value 1e-9f. Actual = " << eps; - } - auto stage = model->addNewStage( layer->name, StageType::MVN, @@ -98,6 +83,7 @@ void FrontEnd::parseMVN( stage->attrs().set("normalize", layer->normalize); stage->attrs().set("across_channels", layer->across_channels); + stage->attrs().set("eps", layer->GetParamAsFloat("eps", 0.0f)); } } // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/none.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/none.cpp index 7489d9876c8b61..819fd143b21c69 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/none.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/none.cpp @@ -21,31 +21,29 @@ class NoneStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector&, - ScalePropagationStep) override { - for (const auto& outEdge : _outputEdges) { - _scaleInfo.setOutput(outEdge, 1.0f); + ScalePropagationStep, + StageDataInfo& scaleInfo) override { + for (const auto& outEdge : outputEdges()) { + scaleInfo.setOutput(outEdge, 1.0f); } } - void propagateDataOrderImpl() const override { + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { } - void getDataStridesRequirementsImpl() const override { + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } StageSHAVEsRequirements getSHAVEsRequirementsImpl() const override { return StageSHAVEsRequirements::NotNeeded; } - void finalCheckImpl() const override { - } - void serializeParamsImpl(BlobSerializer&) const override { } diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/norm.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/norm.cpp index 9ba01e3368384c..465d9b002f09ee 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/norm.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/norm.cpp @@ -21,20 +21,14 @@ class LRNStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); - auto input = _inputEdges[0]->input(); - - _orderInfo.setOutput(_outputEdges[0], input->desc().dimsOrder()); + orderInfo.setOutput(outputEdge(0), input->desc().dimsOrder()); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + auto input = inputEdge(0)->input(); // LRN supports both HWC and CHW orders, but requires that input and output have the same stride @@ -44,22 +38,20 @@ class LRNStage final : public StageNode { reqs.add(1, DimStride::Aligned); } - _stridesInfo.setInput(_inputEdges[0], reqs); - _stridesInfo.setOutput(_outputEdges[0], reqs); + stridesInfo.setInput(inputEdge(0), reqs); + stridesInfo.setOutput(outputEdge(0), reqs); } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -76,12 +68,8 @@ class LRNStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeOldBuffer(handle_from_this(), serializer); output->serializeOldBuffer(handle_from_this(), serializer); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/normalize.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/normalize.cpp index 68cb5b168f8960..b9309bd1d9f206 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/normalize.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/normalize.cpp @@ -20,37 +20,29 @@ class NormalizeStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); - auto input = _inputEdges[0]->input(); - - _orderInfo.setOutput(_outputEdges[0], input->desc().dimsOrder()); + orderInfo.setOutput(outputEdge(0), input->desc().dimsOrder()); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - if (_inputEdges[0]->input()->desc().dimsOrder().dimInd(Dim::C) == 0) { - _stridesInfo.setInput(_inputEdges[0], StridesRequirement::compact()); - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement::compact()); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + if (input(0)->desc().dimsOrder().dimInd(Dim::C) == 0) { + stridesInfo.setInput(inputEdge(0), StridesRequirement::compact()); + stridesInfo.setOutput(outputEdge(0), StridesRequirement::compact()); } } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}, {DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -64,16 +56,11 @@ class NormalizeStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto scales = _inputEdges[1]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto scales = inputEdge(1)->input(); + auto output = outputEdge(0)->output(); auto inputDesc = input->desc(); - auto outputDesc = input->desc(); auto iDimsOrder = inputDesc.dimsOrder(); if (iDimsOrder == DimsOrder::NC || iDimsOrder == DimsOrder::C) { diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/pad.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/pad.cpp index 88d8f1ece67357..3c5f6466b5b91d 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/pad.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/pad.cpp @@ -23,54 +23,45 @@ class PadStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { if (step == ScalePropagationStep::Propagate) { - _scaleInfo.setOutput(_outputEdges[0], inputScales[0]); + scaleInfo.setOutput(outputEdge(0), inputScales[0]); } else { // Copy can only propagate scaling. - _scaleInfo.setInput(_inputEdges[0], 1.0f); - _scaleInfo.setOutput(_outputEdges[0], 1.0f); + scaleInfo.setInput(inputEdge(0), 1.0f); + scaleInfo.setOutput(outputEdge(0), 1.0f); } } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); - _orderInfo.setOutput(_outputEdges[0], input->desc().dimsOrder()); + orderInfo.setOutput(outputEdge(0), input->desc().dimsOrder()); } - void getDataStridesRequirementsImpl() const override { + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { // TODO: try merge with last dimension - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } StageSHAVEsRequirements getSHAVEsRequirementsImpl() const override { return StageSHAVEsRequirements::CanBeLimited; } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); + auto input = inputEdge(0)->input(); auto perm = input->desc().dimsOrder().toPermutation(); IE_ASSERT(perm.size() <= 4); @@ -95,12 +86,8 @@ class PadStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeOldBuffer(handle_from_this(), serializer); output->serializeOldBuffer(handle_from_this(), serializer); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/permute.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/permute.cpp index 58a79ca9d0c90f..e33d15a4231357 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/permute.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/permute.cpp @@ -15,22 +15,6 @@ namespace vpu { namespace { -template -SmallVector permuteArray(const Cont1& src, const Cont2& permutation) { - SmallVector out(permutation.size()); - - for (int i = 0; i < out.size(); i++) { - auto newInd = static_cast(permutation[i]); - - IE_ASSERT(newInd >= 0); - IE_ASSERT(newInd < src.size()); - - out[i] = src[newInd]; - } - - return out; -} - class PermuteStage final : public StageNode { private: StagePtr cloneImpl() const override { @@ -39,73 +23,55 @@ class PermuteStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { if (step == ScalePropagationStep::Propagate) { - _scaleInfo.setOutput(_outputEdges[0], inputScales[0]); + scaleInfo.setOutput(outputEdge(0), inputScales[0]); } else { // Copy can only propagate scaling. - _scaleInfo.setInput(_inputEdges[0], 1.0f); - _scaleInfo.setOutput(_outputEdges[0], 1.0f); + scaleInfo.setInput(inputEdge(0), 1.0f); + scaleInfo.setOutput(outputEdge(0), 1.0f); } } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - - _orderInfo.setOutput(_outputEdges[0], input->desc().dimsOrder()); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + orderInfo.setOutput(outputEdge(0), input(0)->desc().dimsOrder()); } - void getDataStridesRequirementsImpl() const override { + void getDataStridesRequirementsImpl(StageDataInfo&) override { } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo&) override { } StageSHAVEsRequirements getSHAVEsRequirementsImpl() const override { return StageSHAVEsRequirements::CanBeLimited; } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - - const auto& order = attrs().get>("order"); + const auto& permutation = attrs().get>("permutation"); - auto perm = input->desc().dimsOrder().toPermutation(); - auto ind = input->desc().dimsOrder().toIndices(); - - auto dimPerm = permuteArray(order, perm); - auto memoryOrderPerm = permuteArray(ind.toVector(-1), dimPerm); - - int i = 0; - for (i = 0; i < memoryOrderPerm.size(); i++) { - serializer.append(static_cast(memoryOrderPerm[i])); + for (auto dstDim : output(0)->desc().dimsOrder().toPermutation()) { + const auto srcDim = permutation[dstDim]; + const auto srcDimInd = input(0)->desc().dimsOrder().dimInd(srcDim); + serializer.append(static_cast(srcDimInd)); } - for (; i < MAX_DIMS_32; i++) { + + for (int i = output(0)->desc().numDims(); i < MAX_DIMS_32; i++) { serializer.append(static_cast(-1)); } } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeNewBuffer(serializer); output->serializeNewBuffer(serializer); @@ -122,55 +88,33 @@ void FrontEnd::parsePermute( IE_ASSERT(inputs.size() == 1); IE_ASSERT(outputs.size() == 1); - auto ieOrder = layer->GetParamAsInts("order"); + const auto ieOrder = layer->GetParamAsUInts("order"); + const auto perm = DimsOrder::fromNumDims(checked_cast(ieOrder.size())).toPermutation(); - auto maxIeOrder = *std::max_element(ieOrder.begin(), ieOrder.end()); - - SmallVector vpuOrder(MAX_DIMS_64, -1); + DimValues_ permutation; for (size_t i = 0; i < ieOrder.size(); i++) { - vpuOrder[i] = maxIeOrder - ieOrder[ieOrder.size() - 1 - i]; + const auto srcDim = perm[ieOrder.size() - ieOrder[i] - 1]; + const auto dstDim = perm[ieOrder.size() - i - 1]; + permutation.set(dstDim, srcDim); } - auto input = inputs[0]; - auto output = outputs[0]; - - auto stage = model->addNewStage( - layer->name, - StageType::Permute, - layer, - inputs, - outputs); - - stage->attrs().set>("order", vpuOrder); + _stageBuilder->addPermuteStage(model, layer->name, layer, inputs[0], outputs[0], permutation); } Stage StageBuilder::addPermuteStage( const Model::Ptr& model, const std::string& name, const ie::CNNLayerPtr& layer, - const DataVector& inputs, - const DataVector& outputs, - const SmallVector& ieOrder) { - IE_ASSERT(inputs.size() == 1); - IE_ASSERT(outputs.size() == 1); - - auto maxIeOrder = *std::max_element(ieOrder.begin(), ieOrder.end()); - - SmallVector vpuOrder(MAX_DIMS_64, -1); - for (size_t i = 0; i < ieOrder.size(); i++) { - vpuOrder[i] = maxIeOrder - ieOrder[ieOrder.size() - 1 - i]; - } - - auto input = inputs[0]; - auto output = outputs[0]; - + const Data& input, + const Data& output, + const DimValues_& permutation) { auto stage = model->addNewStage( - layer->name, + name, StageType::Permute, layer, - inputs, - outputs); - stage->attrs().set>("order", vpuOrder); + {input}, + {output}); + stage->attrs().set("permutation", permutation); return stage; } diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/power.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/power.cpp index 2f83e63d8ae2b1..9504c731084884 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/power.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/power.cpp @@ -48,21 +48,19 @@ class PowerStage final : public PostOpStage { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { auto power = attrs().get("power"); auto& scale = attrs().get("scale"); auto& bias = attrs().get("bias"); if (power != 1.0f) { - _scaleInfo.setInput(_inputEdges[0], 1.0f); - _scaleInfo.setOutput(_outputEdges[0], 1.0f); + scaleInfo.setInput(inputEdge(0), 1.0f); + scaleInfo.setOutput(outputEdge(0), 1.0f); } else { auto inputScale = inputScales[0]; - _scaleInfo.setOutput(_outputEdges[0], inputScale); + scaleInfo.setOutput(outputEdge(0), inputScale); if (step == ScalePropagationStep::ScaleInput) { scale *= inputScale; diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/proposal.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/proposal.cpp index 6f292b9bf80ef6..746ad53f8c1cf6 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/proposal.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/proposal.cpp @@ -22,34 +22,29 @@ class ProposalStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input0 = inputEdge(0)->input(); + auto input1 = inputEdge(1)->input(); - auto input0 = _inputEdges[0]->input(); - auto input1 = _inputEdges[1]->input(); - - _orderInfo.setInput(_inputEdges[0], input0->desc().dimsOrder().createMovedDim(Dim::C, 2)); - _orderInfo.setInput(_inputEdges[1], input1->desc().dimsOrder().createMovedDim(Dim::C, 2)); + orderInfo.setInput(inputEdge(0), input0->desc().dimsOrder().createMovedDim(Dim::C, 2)); + orderInfo.setInput(inputEdge(1), input1->desc().dimsOrder().createMovedDim(Dim::C, 2)); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - _stridesInfo.setInput(_inputEdges[0], StridesRequirement::compact()); - _stridesInfo.setInput(_inputEdges[1], StridesRequirement::compact()); - _stridesInfo.setInput(_inputEdges[2], StridesRequirement::compact()); - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement::compact()); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + stridesInfo.setInput(inputEdge(0), StridesRequirement::compact()); + stridesInfo.setInput(inputEdge(1), StridesRequirement::compact()); + stridesInfo.setInput(inputEdge(2), StridesRequirement::compact()); + stridesInfo.setOutput(outputEdge(0), StridesRequirement::compact()); } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}, {DataType::FP16}, {DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -104,20 +99,16 @@ class ProposalStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.size() == 1); - - auto input0 = _inputEdges[0]->input(); - auto input1 = _inputEdges[1]->input(); - auto input2 = _inputEdges[2]->input(); - auto output = _outputEdges[0]->output(); + auto input0 = inputEdge(0)->input(); + auto input1 = inputEdge(1)->input(); + auto input2 = inputEdge(2)->input(); + auto output = outputEdge(0)->output(); input0->serializeNewBuffer(serializer); output->serializeNewBuffer(serializer); input1->serializeNewBuffer(serializer); input2->serializeNewBuffer(serializer); - _tempBufferEdges[0]->tempBuffer()->serializeNewBuffer(serializer); + tempBuffer(0)->serializeNewBuffer(serializer); } }; diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/psroipooling.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/psroipooling.cpp index 67d45d7ad6e0b0..a77d536d1c9eb5 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/psroipooling.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/psroipooling.cpp @@ -19,33 +19,28 @@ class PSROIPoolingStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input0 = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); - auto input0 = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); - - _orderInfo.setInput(_inputEdges[0], input0->desc().dimsOrder().createMovedDim(Dim::C, 2)); - _orderInfo.setOutput(_outputEdges[0], output->desc().dimsOrder().createMovedDim(Dim::C, 2)); + orderInfo.setInput(inputEdge(0), input0->desc().dimsOrder().createMovedDim(Dim::C, 2)); + orderInfo.setOutput(outputEdge(0), output->desc().dimsOrder().createMovedDim(Dim::C, 2)); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - _stridesInfo.setInput(_inputEdges[0], StridesRequirement::compact()); - _stridesInfo.setInput(_inputEdges[1], StridesRequirement::compact()); - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement::compact()); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + stridesInfo.setInput(inputEdge(0), StridesRequirement::compact()); + stridesInfo.setInput(inputEdge(1), StridesRequirement::compact()); + stridesInfo.setOutput(outputEdge(0), StridesRequirement::compact()); } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}, {DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -59,13 +54,9 @@ class PSROIPoolingStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input0 = _inputEdges[0]->input(); - auto input1 = _inputEdges[1]->input(); - auto output = _outputEdges[0]->output(); + auto input0 = inputEdge(0)->input(); + auto input1 = inputEdge(1)->input(); + auto output = outputEdge(0)->output(); input0->serializeNewBuffer(serializer); output->serializeNewBuffer(serializer); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/reduce.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/reduce.cpp index d889fd9cc67129..a00fdfe593bcb4 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/reduce.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/reduce.cpp @@ -1,21 +1,10 @@ -// -// Copyright 2019 Intel Corporation. -// -// This software and the related documents are Intel copyrighted materials, -// and your use of them is governed by the express license under which they -// were provided to you (End User License Agreement for the Intel(R) Software -// Development Products (Version May 2017)). Unless the License provides -// otherwise, you may not use, modify, copy, publish, distribute, disclose or -// transmit this software or the related documents without Intel's prior -// written permission. -// -// This software and the related documents are provided as is, with no -// express or implied warranties, other than those that are expressly -// stated in the License. +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 // #include +#include #include #include @@ -29,36 +18,30 @@ class ReduceStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - auto input0 = _inputEdges[0]->input(); - auto input1 = _inputEdges[1]->input(); - auto output = _outputEdges[0]->output(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input0 = inputEdge(0)->input(); + auto input1 = inputEdge(1)->input(); + auto output = outputEdge(0)->output(); - auto in0Desc = input0->desc(); - auto in1Desc = input1->desc(); - auto outDesc = output->desc(); + auto in0Desc = input0->desc(); + auto in1Desc = input1->desc(); + auto outDesc = output->desc(); - auto in0Order = DimsOrder::fromNumDims(in0Desc.numDims()); - auto in1Order = DimsOrder::fromNumDims(in1Desc.numDims()); - auto outOrder = DimsOrder::fromNumDims(outDesc.numDims()); + auto in0Order = DimsOrder::fromNumDims(in0Desc.numDims()); + auto in1Order = DimsOrder::fromNumDims(in1Desc.numDims()); + auto outOrder = DimsOrder::fromNumDims(outDesc.numDims()); - _orderInfo.setInput(_inputEdges[0], in0Order); - _orderInfo.setInput(_inputEdges[1], in1Order); - _orderInfo.setOutput(_outputEdges[0], outOrder); + orderInfo.setInput(inputEdge(0), in0Order); + orderInfo.setInput(inputEdge(1), in1Order); + orderInfo.setOutput(outputEdge(0), outOrder); } - void getDataStridesRequirementsImpl() const override { + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { } void finalizeDataLayoutImpl() override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - auto input0 = _inputEdges[0]->input(); - auto input1 = _inputEdges[1]->input(); + auto input0 = inputEdge(0)->input(); + auto input1 = inputEdge(1)->input(); auto in0Desc = input0->desc(); auto in1Desc = input1->desc(); @@ -68,7 +51,7 @@ class ReduceStage final : public StageNode { size_t ndims = in0Desc.numDims(); IE_ASSERT(in1Desc.numDims() == 1); size_t indicesSize = in1Desc.totalDimSize(); - IE_ASSERT(indicesSize < ndims); + IE_ASSERT(indicesSize <= ndims); const auto oldIndices = input1->content()->get(); @@ -89,6 +72,7 @@ class ReduceStage final : public StageNode { index = static_cast(perm[ndims - 1 - index]); newIndices[i] = index; } + std::sort(newIndices, newIndices + indicesSize); auto newList = _model->duplicateData( input1, @@ -96,17 +80,18 @@ class ReduceStage final : public StageNode { DataDesc(), ieBlobContent(newIndicesBlob)); - _model->replaceStageInput(_inputEdges[1], newList); + _model->replaceStageInput(inputEdge(1), newList); } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } StageSHAVEsRequirements getSHAVEsRequirementsImpl() const override { return StageSHAVEsRequirements::CanBeLimited; } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}, {DataType::S32}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -116,16 +101,13 @@ class ReduceStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - auto input0 = _inputEdges[0]->input(); - auto input1 = _inputEdges[1]->input(); - auto output = _outputEdges[0]->output(); + auto input0 = inputEdge(0)->input(); + auto input1 = inputEdge(1)->input(); + auto output = outputEdge(0)->output(); - input0->serializeNewBuffer(serializer); - output->serializeNewBuffer(serializer); - input1->serializeNewBuffer(serializer); + input0->serializeNewBuffer(serializer); + output->serializeNewBuffer(serializer); + input1->serializeNewBuffer(serializer); } }; @@ -145,6 +127,8 @@ void FrontEnd::parseReduce( auto stageType = StageType::None; if (layer->type == "ReduceAnd") { stageType = StageType::ReduceAnd; + } else if (layer->type == "ReduceMin") { + stageType = StageType::ReduceMin; } else { VPU_THROW_EXCEPTION << "Reduce operation: " << layer->type << " is not supported"; } diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/region_yolo.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/region_yolo.cpp index cda718ca4d975c..956349713245c2 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/region_yolo.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/region_yolo.cpp @@ -19,39 +19,26 @@ class RegionYoloStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto output = _outputEdges[0]->output(); - - if (!attrs().get("doSoftMax")) { - _orderInfo.setOutput(_outputEdges[0], output->desc().dimsOrder().createMovedDim(Dim::C, 2)); // CHW - } + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { if (attrs().get("doSoftMax")) { // Major dimension must be compact. - _stridesInfo.setInput(_inputEdges[0], StridesRequirement().add(2, DimStride::Compact)); + stridesInfo.setInput(inputEdge(0), StridesRequirement().add(2, DimStride::Compact)); } } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -69,12 +56,8 @@ class RegionYoloStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeOldBuffer(handle_from_this(), serializer); output->serializeOldBuffer(handle_from_this(), serializer); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/relu.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/relu.cpp index ff4cd9a3fc58e2..d99c5c5f7baea8 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/relu.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/relu.cpp @@ -26,24 +26,22 @@ class ReLUStage final : public PostOpStage { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { if (step == ScalePropagationStep::Propagate) { auto inputScale = inputScales[0]; - _scaleInfo.setOutput(_outputEdges[0], inputScale); + scaleInfo.setOutput(outputEdge(0), inputScale); } else { // ReLU can only propagate scaling, not generate. - _scaleInfo.setInput(_inputEdges[0], 1.0f); - _scaleInfo.setOutput(_outputEdges[0], 1.0f); + scaleInfo.setInput(inputEdge(0), 1.0f); + scaleInfo.setOutput(outputEdge(0), 1.0f); } } void serializeParamsImpl(BlobSerializer& serializer) const override { auto negativeSlope = attrs().get("negativeSlope"); - serializer.append(static_cast(_inputEdges.size() == 2)); + serializer.append(static_cast(numInputs() == 2)); serializer.append(negativeSlope); } }; diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/reorg_yolo.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/reorg_yolo.cpp index b6176e53fd8a7d..03b67635f15da1 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/reorg_yolo.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/reorg_yolo.cpp @@ -21,54 +21,44 @@ class ReorgYoloStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { if (step == ScalePropagationStep::Propagate) { - _scaleInfo.setOutput(_outputEdges[0], inputScales[0]); + scaleInfo.setOutput(outputEdge(0), inputScales[0]); } else { // ReorgYolo can only propagate scaling. - _scaleInfo.setInput(_inputEdges[0], 1.0f); - _scaleInfo.setOutput(_outputEdges[0], 1.0f); + scaleInfo.setInput(inputEdge(0), 1.0f); + scaleInfo.setOutput(outputEdge(0), 1.0f); } } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); - _orderInfo.setOutput(_outputEdges[0], input->desc().dimsOrder()); + orderInfo.setOutput(outputEdge(0), input->desc().dimsOrder()); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + auto input = inputEdge(0)->input(); auto inOrder = input->desc().dimsOrder(); if (inOrder.dimInd(Dim::C) == 0) { - _stridesInfo.setInput(_inputEdges[0], StridesRequirement::compact()); - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement::compact()); + stridesInfo.setInput(inputEdge(0), StridesRequirement::compact()); + stridesInfo.setOutput(outputEdge(0), StridesRequirement::compact()); } } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -78,12 +68,8 @@ class ReorgYoloStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeOldBuffer(handle_from_this(), serializer); output->serializeOldBuffer(handle_from_this(), serializer); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/resample.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/resample.cpp index 2f6ab2d91a4e19..d3e6bd41956d5d 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/resample.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/resample.cpp @@ -25,30 +25,25 @@ class ResampleStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); - auto input = _inputEdges[0]->input(); - - _orderInfo.setOutput(_outputEdges[0], input->desc().dimsOrder()); + orderInfo.setOutput(outputEdge(0), input->desc().dimsOrder()); } - void getDataStridesRequirementsImpl() const override { + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -62,12 +57,8 @@ class ResampleStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeOldBuffer(handle_from_this(), serializer); output->serializeOldBuffer(handle_from_this(), serializer); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/reshape.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/reshape.cpp index a9a3c4571cfa18..7e35592327949b 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/reshape.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/reshape.cpp @@ -22,53 +22,41 @@ class ReshapeStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { if (step == ScalePropagationStep::Propagate) { - _scaleInfo.setOutput(_outputEdges[0], inputScales[0]); + scaleInfo.setOutput(outputEdge(0), inputScales[0]); } else { // Reshape can only propagate scaling. - _scaleInfo.setInput(_inputEdges[0], 1.0f); - _scaleInfo.setOutput(_outputEdges[0], 1.0f); + scaleInfo.setInput(inputEdge(0), 1.0f); + scaleInfo.setOutput(outputEdge(0), 1.0f); } } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); // Only default order is supported - _orderInfo.setInput(_inputEdges[0], DimsOrder::fromNumDims(input->desc().numDims())); - _orderInfo.setOutput(_outputEdges[0], DimsOrder::fromNumDims(output->desc().numDims())); + orderInfo.setInput(inputEdge(0), DimsOrder::fromNumDims(input->desc().numDims())); + orderInfo.setOutput(outputEdge(0), DimsOrder::fromNumDims(output->desc().numDims())); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - _stridesInfo.setInput(_inputEdges[0], StridesRequirement::compact()); - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement::compact()); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + stridesInfo.setInput(inputEdge(0), StridesRequirement::compact()); + stridesInfo.setOutput(outputEdge(0), StridesRequirement::compact()); } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } - void finalCheckImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); - - IE_ASSERT(input->desc().totalDimSize() == output->desc().totalDimSize()); + void initialCheckImpl() const override { + const auto& firstInputPrecision = input(0)->desc().type(); + assertInputsOutputsTypes(this, {{firstInputPrecision}}, {{firstInputPrecision}}); + IE_ASSERT(input(0)->desc().totalDimSize() == output(0)->desc().totalDimSize()); } void serializeParamsImpl(BlobSerializer&) const override { diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/reverse_sequence.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/reverse_sequence.cpp index 66b19a496b087b..886b7002e10e0d 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/reverse_sequence.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/reverse_sequence.cpp @@ -1,17 +1,5 @@ -// -// Copyright 2019 Intel Corporation. -// -// This software and the related documents are Intel copyrighted materials, -// and your use of them is governed by the express license under which they -// were provided to you (End User License Agreement for the Intel(R) Software -// Development Products (Version May 2017)). Unless the License provides -// otherwise, you may not use, modify, copy, publish, distribute, disclose or -// transmit this software or the related documents without Intel's prior -// written permission. -// -// This software and the related documents are provided as is, with no -// express or implied warranties, other than those that are expressly -// stated in the License. +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 // #include @@ -30,52 +18,44 @@ class ReverseSequenceStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - _orderInfo.setOutput(_outputEdges[0], input->desc().dimsOrder()); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); + orderInfo.setOutput(outputEdge(0), input->desc().dimsOrder()); } - void getDataStridesRequirementsImpl() const override { + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}, {DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto seq_lengths = _inputEdges[1]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto seq_lengths = inputEdge(1)->input(); + auto output = outputEdge(0)->output(); - auto seq_axis = input->desc().dimsOrder().dimInd(attrs().get("seq_axis")); - auto batch_axis = input->desc().dimsOrder().dimInd(attrs().get("batch_axis")); + auto seq_axis = input->desc().dimsOrder().dimInd(attrs().get("seq_axis")); + auto batch_axis = input->desc().dimsOrder().dimInd(attrs().get("batch_axis")); - serializer.append(static_cast(seq_axis)); - serializer.append(static_cast(batch_axis)); + serializer.append(static_cast(seq_axis)); + serializer.append(static_cast(batch_axis)); } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto seq_lengths = _inputEdges[1]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto seq_lengths = inputEdge(1)->input(); + auto output = outputEdge(0)->output(); - input->serializeNewBuffer(serializer); - seq_lengths->serializeNewBuffer(serializer); - output->serializeNewBuffer(serializer); + input->serializeNewBuffer(serializer); + seq_lengths->serializeNewBuffer(serializer); + output->serializeNewBuffer(serializer); } }; diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/rnn.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/rnn.cpp index 0a9e10f73d2087..78779a88a2947c 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/rnn.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/rnn.cpp @@ -21,12 +21,9 @@ class LSTMCellStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 5); - IE_ASSERT(_outputEdges.size() == 2); - - auto output = _outputEdges[0]->output(); - auto input = _inputEdges[0]->input(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto output = outputEdge(0)->output(); + auto input = inputEdge(0)->input(); auto inputDimsOrder = input->desc().dimsOrder(); auto outputDimsOrder = output->desc().dimsOrder(); @@ -38,29 +35,29 @@ class LSTMCellStage final : public StageNode { outputDimsOrder.moveDim(Dim::C, 2); // ->...CHW } - _orderInfo.setInput(_inputEdges[0], inputDimsOrder); - _orderInfo.setOutput(_outputEdges[0], outputDimsOrder); + orderInfo.setInput(inputEdge(0), inputDimsOrder); + orderInfo.setOutput(outputEdge(0), outputDimsOrder); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 5); - IE_ASSERT(_outputEdges.size() == 2); - - for (const auto& inEdge : _inputEdges) { - _stridesInfo.setInput(inEdge, StridesRequirement::compact()); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + for (const auto& inEdge : inputEdges()) { + stridesInfo.setInput(inEdge, StridesRequirement::compact()); } - for (const auto& outEdge : _outputEdges) { - _stridesInfo.setOutput(outEdge, StridesRequirement::compact()); + for (const auto& outEdge : outputEdges()) { + stridesInfo.setOutput(outEdge, StridesRequirement::compact()); } } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + IE_ASSERT(numInputs() == 5); + IE_ASSERT(numOutputs() > 0); + assertAllInputsOutputsTypes(this, DataType::FP16, DataType::FP16); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -73,23 +70,21 @@ class LSTMCellStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 5); - IE_ASSERT(_outputEdges.size() == 2); - int nCells = attrs().get("nCells"); bool useTempBuffer = (nCells > 1); - IE_ASSERT((_tempBufferEdges.size() == 1 && useTempBuffer) || !useTempBuffer); + IE_ASSERT((numTempBuffers() == 1 && useTempBuffer) || !useTempBuffer); - for (const auto& inEdge : _inputEdges) { + for (const auto& inEdge : inputEdges()) { inEdge->input()->serializeNewBuffer(serializer); } - for (const auto& outEdge : _outputEdges) { + for (const auto& outEdge : outputEdges()) { outEdge->output()->serializeNewBuffer(serializer); } - if (useTempBuffer) - _tempBufferEdges[0]->tempBuffer()->serializeNewBuffer(serializer); + if (useTempBuffer) { + tempBuffer(0)->serializeNewBuffer(serializer); + } } }; @@ -139,7 +134,7 @@ void FrontEnd::parseRNN( size_t input_size = inputs[0]->desc().dim(Dim::W); IE_ASSERT(input_size == inputs[0]->desc().totalDimSize() / nCells / nBatches); - size_t state_size = inputs[1]->desc().totalDimSize() / nBatches; + size_t state_size = outputs[0]->desc().totalDimSize() / nCells / nBatches; size_t cell_state_size = inputs[2]->desc().totalDimSize() / nBatches; IE_ASSERT(state_size == cell_state_size); @@ -201,14 +196,46 @@ void FrontEnd::parseLSTMCell( auto layer = std::dynamic_pointer_cast(_layer); IE_ASSERT(layer != nullptr); - Data weights, biases; - std::tie(weights, biases) = getWeightsAndBiases(model, layer); + DataVector stageInputs = inputs; + auto origWeights = layer->_weights; + + IE_ASSERT(origWeights != nullptr) << "weights are empty for layer: " << layer->name; + + if (lstmWeights.count(origWeights) != 0) { + stageInputs.emplace_back(lstmWeights[origWeights]); + } else { + auto weights = model->addConstData( + layer->name + "@weights", + DataDesc({origWeights->size()}), + ieBlobContent(origWeights)); + lstmWeights[origWeights] = weights; + stageInputs.emplace_back(weights); + } + + auto origBiases = layer->_biases; + + Data biases; + if (origBiases == nullptr) { + biases = model->addFakeData(); + } else { + if (lstmBiases.count(origBiases) != 0) { + biases = lstmBiases[origBiases]; + } else { + biases = model->addConstData( + layer->name + "@biases", + DataDesc({origBiases->size()}), + ieBlobContent(origBiases)); + lstmBiases[origBiases] = biases; + } + } + + stageInputs.emplace_back(biases); auto stage = model->addNewStage( layer->name, StageType::LSTMCell, layer, - {inputs[0], inputs[1], inputs[2], weights, biases}, + stageInputs, outputs); stage->attrs().set("RNNForward", true); stage->attrs().set("nCells", 1); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/roipooling.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/roipooling.cpp index 96cdc1f3a03dfe..0b40214bf6a4f9 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/roipooling.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/roipooling.cpp @@ -27,33 +27,28 @@ class ROIPoolingStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input0 = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); - auto input0 = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); - - _orderInfo.setInput(_inputEdges[0], input0->desc().dimsOrder().createMovedDim(Dim::C, 2)); - _orderInfo.setOutput(_outputEdges[0], output->desc().dimsOrder().createMovedDim(Dim::C, 2)); + orderInfo.setInput(inputEdge(0), input0->desc().dimsOrder().createMovedDim(Dim::C, 2)); + orderInfo.setOutput(outputEdge(0), output->desc().dimsOrder().createMovedDim(Dim::C, 2)); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - - _stridesInfo.setInput(_inputEdges[0], StridesRequirement::compact()); - _stridesInfo.setInput(_inputEdges[1], StridesRequirement::compact()); - _stridesInfo.setOutput(_outputEdges[0], StridesRequirement::compact()); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + stridesInfo.setInput(inputEdge(0), StridesRequirement::compact()); + stridesInfo.setInput(inputEdge(1), StridesRequirement::compact()); + stridesInfo.setOutput(outputEdge(0), StridesRequirement::compact()); } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}, {DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -69,13 +64,9 @@ class ROIPoolingStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 2); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input0 = _inputEdges[0]->input(); - auto input1 = _inputEdges[1]->input(); - auto output = _outputEdges[0]->output(); + auto input0 = inputEdge(0)->input(); + auto input1 = inputEdge(1)->input(); + auto output = outputEdge(0)->output(); input0->serializeNewBuffer(serializer); output->serializeNewBuffer(serializer); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/scale.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/scale.cpp index eec26944954d55..af07f88300d686 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/scale.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/scale.cpp @@ -23,17 +23,15 @@ class ScaleStage final : public PostOpStage { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(_inputEdges.size() == 2 || _inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { auto inputScale = inputScales[0]; - _scaleInfo.setInput(_inputEdges[1], step == ScalePropagationStep::Propagate ? 1.0f : inputScale); - if (_inputEdges.size() == 3) { - _scaleInfo.setInput(_inputEdges[2], inputScale); + scaleInfo.setInput(inputEdge(1), step == ScalePropagationStep::Propagate ? 1.0f : inputScale); + if (numInputs() == 3) { + scaleInfo.setInput(inputEdge(2), inputScale); } - _scaleInfo.setOutput(_outputEdges[0], inputScale); + scaleInfo.setOutput(outputEdge(0), inputScale); } void serializeParamsImpl(BlobSerializer&) const override { diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/shrink.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/shrink.cpp index 450842eb755678..ebb33e4346bee3 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/shrink.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/shrink.cpp @@ -25,25 +25,20 @@ class ShrinkStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector&, - ScalePropagationStep) override { + ScalePropagationStep, + StageDataInfo&) override { VPU_THROW_EXCEPTION << "Must never be called"; } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); - auto input = _inputEdges[0]->input(); - - _orderInfo.setOutput(_outputEdges[0], input->desc().dimsOrder()); + orderInfo.setOutput(outputEdge(0), input->desc().dimsOrder()); } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); auto dimsOrder = input->desc().dimsOrder(); @@ -100,17 +95,19 @@ class ShrinkStage final : public StageNode { // Return merged StridesRequirements. // - _stridesInfo.setInput(_inputEdges[0], inputReqs); - _stridesInfo.setOutput(_outputEdges[0], outputReqs); + stridesInfo.setInput(inputEdge(0), inputReqs); + stridesInfo.setOutput(outputEdge(0), outputReqs); } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + const auto& firstInputPrecision = input(0)->desc().type(); + assertInputsOutputsTypes(this, {{firstInputPrecision}}, {{firstInputPrecision}}); } void serializeParamsImpl(BlobSerializer&) const override { diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/softmax.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/softmax.cpp index 5847be14230778..01ff1c3217e43d 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/softmax.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/softmax.cpp @@ -20,31 +20,27 @@ class SoftMaxStage final : public StageNode { return std::make_shared(*this); } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); - auto input = _inputEdges[0]->input(); - - _orderInfo.setOutput(_outputEdges[0], input->desc().dimsOrder()); + orderInfo.setOutput(outputEdge(0), input->desc().dimsOrder()); } - void getDataStridesRequirementsImpl() const override { + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); + auto input = inputEdge(0)->input(); auto axis = attrs().get("axis"); auto axisInd = input->desc().dimsOrder().dimInd(axis); @@ -53,12 +49,8 @@ class SoftMaxStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeNewBuffer(serializer); output->serializeNewBuffer(serializer); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/split.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/split.cpp index fdd97061c7811d..aff1f7f73249a4 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/split.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/split.cpp @@ -23,42 +23,34 @@ class SplitStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(!_outputEdges.empty()); - + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { if (step == ScalePropagationStep::Propagate) { auto inputScale = inputScales[0]; - for (const auto& outEdge : _outputEdges) { - _scaleInfo.setOutput(outEdge, inputScale); + for (const auto& outEdge : outputEdges()) { + scaleInfo.setOutput(outEdge, inputScale); } } else { // Split can only propagate scaling. - _scaleInfo.setInput(_inputEdges[0], 1.0f); + scaleInfo.setInput(inputEdge(0), 1.0f); - for (const auto& outEdge : _outputEdges) { - _scaleInfo.setOutput(outEdge, 1.0f); + for (const auto& outEdge : outputEdges()) { + scaleInfo.setOutput(outEdge, 1.0f); } } } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(!_outputEdges.empty()); - - auto input = _inputEdges[0]->input(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); - for (const auto& outEdge : _outputEdges) { - _orderInfo.setOutput(outEdge, input->desc().dimsOrder()); + for (const auto& outEdge : outputEdges()) { + orderInfo.setOutput(outEdge, input->desc().dimsOrder()); } } - void getDataStridesRequirementsImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(!_outputEdges.empty()); - - auto input = _inputEdges[0]->input(); + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + auto input = inputEdge(0)->input(); auto dimsOrder = input->desc().dimsOrder(); @@ -68,7 +60,7 @@ class SplitStage final : public StageNode { auto minSplitDimInd = dimsOrder.numDims(); - for (const auto& outEdge : _outputEdges) { + for (const auto& outEdge : outputEdges()) { auto output = outEdge->output(); for (const auto& p : input->desc().dims()) { @@ -90,7 +82,7 @@ class SplitStage final : public StageNode { // Merge output consumers StridesRequirement. // - for (const auto& outEdge : _outputEdges) { + for (const auto& outEdge : outputEdges()) { auto curOutput = outEdge->output(); for (const auto& consumerEdge : curOutput->consumerEdges()) { @@ -123,19 +115,23 @@ class SplitStage final : public StageNode { // Return merged StridesRequirements. // - _stridesInfo.setInput(_inputEdges[0], inputReqs); - for (const auto& outEdge : _outputEdges) { - _stridesInfo.setOutput(outEdge, outputReqs); + stridesInfo.setInput(inputEdge(0), inputReqs); + for (const auto& outEdge : outputEdges()) { + stridesInfo.setOutput(outEdge, outputReqs); } } void finalizeDataLayoutImpl() override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& /*batchInfo*/) override { } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + IE_ASSERT(numInputs() == 1); + IE_ASSERT(numOutputs() > 0); + const auto& firstInputPrecision = input(0)->desc().type(); + assertAllInputsOutputsTypes(this, {firstInputPrecision}, {firstInputPrecision}); } void serializeParamsImpl(BlobSerializer&) const override { @@ -165,6 +161,15 @@ void FrontEnd::parseSplit( auto inDesc = input->desc(); auto perm = inDesc.dimsOrder().toPermutation(); + // Detect unused data + DataVector onlyUsedOutputs; + for (const auto& output : outputs) { + if (!output->origData()->getInputTo().empty()) { + onlyUsedOutputs.push_back(output); + } + } + IE_ASSERT(!onlyUsedOutputs.empty()); + // Check whether it is Split(copy) or Slice Caffe layer // and we do not trust to IE layer type value. bool isSplit = true; @@ -235,7 +240,7 @@ void FrontEnd::parseSplit( } } - _stageBuilder->addSplitStage(model, layer->name, layer, axis, input, outputs); + _stageBuilder->addSplitStage(model, layer->name, layer, axis, input, onlyUsedOutputs); } } diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/strided_slice.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/strided_slice.cpp new file mode 100644 index 00000000000000..dcb5d9cb78be9b --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/src/stages/strided_slice.cpp @@ -0,0 +1,77 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include +#include + +#include + +namespace vpu { + +namespace { + +class StridedSliceStage final : public StageNode { +private: + StagePtr cloneImpl() const override { + return std::make_shared(*this); + } + + void propagateScaleFactorsImpl( + const SmallVector& inputScales, + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { + } + + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + } + + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { + } + + void finalizeDataLayoutImpl() override { + } + + void getBatchSupportInfoImpl(StageDataInfo& /*batchInfo*/) override { + } + + void initialCheckImpl() const override { + IE_ASSERT(numInputs() == 4); + IE_ASSERT(numOutputs() == 1); + assertInputsOutputsTypes( + this, + {{DataType::FP16}, {DataType::S32}, {DataType::S32}, {DataType::S32}}, + {{DataType::FP16}}); + } + + void serializeParamsImpl(BlobSerializer&) const override { + VPU_THROW_EXCEPTION << "Must never be called"; + } + + void serializeDataImpl(BlobSerializer&) const override { + VPU_THROW_EXCEPTION << "Must never be called"; + } +}; + +} // namespace + +void FrontEnd::parseStridedSlice( + const Model::Ptr& model, + const ie::CNNLayerPtr& layer, + const DataVector& inputs, + const DataVector& outputs) { + IE_ASSERT(inputs.size() == 4); + IE_ASSERT(outputs.size() == 1); + + model->addNewStage( + layer->name, + StageType::StridedSlice, + layer, + inputs, + outputs); +} + +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/tile.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/tile.cpp index c02669266ee231..64c091bca96c80 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stages/tile.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stages/tile.cpp @@ -21,37 +21,32 @@ class TileStage final : public StageNode { void propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - + ScalePropagationStep step, + StageDataInfo& scaleInfo) override { if (step == ScalePropagationStep::Propagate) { auto inputScale = inputScales[0]; - _scaleInfo.setOutput(_outputEdges[0], inputScale); + scaleInfo.setOutput(outputEdge(0), inputScale); } else { // Tile can only propagate scaling, not generate. - _scaleInfo.setInput(_inputEdges[0], 1.0f); - _scaleInfo.setOutput(_outputEdges[0], 1.0f); + scaleInfo.setInput(inputEdge(0), 1.0f); + scaleInfo.setOutput(outputEdge(0), 1.0f); } } - void propagateDataOrderImpl() const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + auto input = inputEdge(0)->input(); auto inOrder = input->desc().dimsOrder(); auto finalOrder = inOrder; - _orderInfo.setInput(_inputEdges[0], finalOrder); - _orderInfo.setOutput(_outputEdges[0], finalOrder); + orderInfo.setInput(inputEdge(0), finalOrder); + orderInfo.setOutput(outputEdge(0), finalOrder); } - void getDataStridesRequirementsImpl() const override { + void getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) override { } - void getBatchSupportInfoImpl() const override { + void getBatchSupportInfoImpl(StageDataInfo& batchInfo) override { } void finalizeDataLayoutImpl() override { @@ -61,15 +56,13 @@ class TileStage final : public StageNode { return StageSHAVEsRequirements::OnlyOne; } - void finalCheckImpl() const override { + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); auto axis = attrs().get("axis"); auto tiles = attrs().get("tiles"); @@ -82,12 +75,8 @@ class TileStage final : public StageNode { } void serializeDataImpl(BlobSerializer& serializer) const override { - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeNewBuffer(serializer); output->serializeNewBuffer(serializer); diff --git a/inference-engine/src/vpu/graph_transformer/src/stages/topk.cpp b/inference-engine/src/vpu/graph_transformer/src/stages/topk.cpp new file mode 100644 index 00000000000000..463e630bc2e282 --- /dev/null +++ b/inference-engine/src/vpu/graph_transformer/src/stages/topk.cpp @@ -0,0 +1,147 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include + +namespace vpu { + +static TopKMode getMode(const std::shared_ptr layer) { + const auto& mode = layer->mode; + if (mode == "max") + return TopKMode::Max; + if (mode == "min") + return TopKMode::Min; + VPU_THROW_EXCEPTION << layer->name << " TopK can take only 'max' or 'min' for mode, but actually it has: " << mode; +} + +static TopKSort getSort(const std::shared_ptr layer) { + const auto& sort = layer->sort; + if (sort == "none") + return TopKSort::None; + if (sort == "value") + return TopKSort::Value; + if (sort == "index") + return TopKSort::Index; + VPU_THROW_EXCEPTION << layer->name << " TopK can take only 'value', 'index' or 'none' for sort, but actually it has: " << sort; +} + +namespace { + +class TopKStage final : public StageNode { +private: + StagePtr cloneImpl() const override { + return std::make_shared(*this); + } + + void propagateDataOrderImpl(StageDataInfo& orderInfo) override { + IE_ASSERT(_inputEdges.size() == 2); + IE_ASSERT(_outputEdges.size() == 2); + + auto inputValues = _inputEdges[0]->input(); + + auto outputOrder = inputValues->desc().dimsOrder(); + + orderInfo.setOutput(_outputEdges[0], outputOrder); + orderInfo.setOutput(_outputEdges[1], outputOrder); + } + + void getDataStridesRequirementsImpl(StageDataInfo& /*stridesInfo*/) override { + } + + void finalizeDataLayoutImpl() override { + } + + void getBatchSupportInfoImpl(StageDataInfo& /*batchInfo*/) override { + } + + StageSHAVEsRequirements getSHAVEsRequirementsImpl() const override { + return StageSHAVEsRequirements::CanBeLimited; + } + + void initialCheckImpl() const override { + assertInputsOutputsTypes(this, + {{DataType::FP16}, {DataType::S32}}, + {{DataType::FP16}, {DataType::S32}}); + } + + void serializeParamsImpl(BlobSerializer& serializer) const override { + IE_ASSERT(_inputEdges.size() == 2); + + auto inputValues = _inputEdges[0]->input(); + + auto axis = attrs().get("axis"); + auto axisInd = inputValues->desc().dimsOrder().dimInd(axis); + + auto mode = attrs().get("mode"); + auto sort = attrs().get("sort"); + + serializer.append(static_cast(axisInd)); + serializer.append(static_cast(mode)); + serializer.append(static_cast(sort)); + } + + void serializeDataImpl(BlobSerializer& serializer) const override { + IE_ASSERT(_inputEdges.size() == 2); + IE_ASSERT(_outputEdges.size() == 2); + + auto inputValues = _inputEdges[0]->input(); + auto inputK = _inputEdges[1]->input(); + auto outputValues = _outputEdges[0]->output(); + auto outputIndices = _outputEdges[1]->output(); + + inputValues->serializeNewBuffer(serializer); + outputValues->serializeNewBuffer(serializer); + inputK->serializeNewBuffer(serializer); + outputIndices->serializeNewBuffer(serializer); + } +}; + +} // namespace + +void FrontEnd::parseTopK( + const Model::Ptr& model, + const ie::CNNLayerPtr& _layer, + const DataVector& inputs, + const DataVector& outputs) { + auto layer = std::dynamic_pointer_cast(_layer); + IE_ASSERT(layer != nullptr); + + IE_ASSERT(inputs.size() == 2); + IE_ASSERT(outputs.size() == 2); + + auto inputValues = inputs[0]; + auto inputK = inputs[1]; + auto outputValues = outputs[0]; + auto outputIndices = outputs[1]; + + const auto numDims = inputValues->desc().numDims(); + + IE_ASSERT(inputK->desc().numDims() == 1); + IE_ASSERT(outputValues->desc().numDims() == numDims); + IE_ASSERT(outputIndices->desc().numDims() == numDims); + + IE_ASSERT(layer->axis < numDims); + + auto perm = DimsOrder::fromNumDims(numDims).toPermutation(); + auto axis = perm[numDims - 1 - layer->axis]; + + TopKMode mode = getMode(layer); + TopKSort sort = getSort(layer); + + auto stage = model->addNewStage( + layer->name, + StageType::TopK, + layer, + inputs, + outputs); + + stage->attrs().set("axis", axis); + stage->attrs().set("mode", mode); + stage->attrs().set("sort", sort); +} + +} // namespace vpu diff --git a/inference-engine/src/vpu/graph_transformer/src/stub_stage.cpp b/inference-engine/src/vpu/graph_transformer/src/stub_stage.cpp index 70dd0804d88105..ef3c8b4fe80bf8 100644 --- a/inference-engine/src/vpu/graph_transformer/src/stub_stage.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/stub_stage.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -18,44 +19,39 @@ StagePtr StubStage::cloneImpl() const { void StubStage::propagateScaleFactorsImpl( const SmallVector& inputScales, - ScalePropagationStep step) { + ScalePropagationStep step, + StageDataInfo& scaleInfo) { if (_type == StageType::StubConv || _type == StageType::StubFullyConnected || _type == StageType::StubDeconv) { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - auto weights = _inputEdges[1]->input(); - auto biases = _inputEdges[2]->input(); + auto weights = inputEdge(1)->input(); + auto biases = inputEdge(2)->input(); IE_ASSERT(weights->usage() == DataUsage::Const); IE_ASSERT(biases->usage() == DataUsage::Const || biases->usage() == DataUsage::Fake); auto inputScale = inputScales[0]; - _scaleInfo.setInput(_inputEdges[1], step == ScalePropagationStep::Propagate ? 1.0f : inputScale); + scaleInfo.setInput(inputEdge(1), step == ScalePropagationStep::Propagate ? 1.0f : inputScale); if (biases->usage() == DataUsage::Const) { - _scaleInfo.setInput(_inputEdges[2], inputScale); + scaleInfo.setInput(inputEdge(2), inputScale); } - _scaleInfo.setOutput(_outputEdges[0], inputScale); + scaleInfo.setOutput(outputEdge(0), inputScale); } else { IE_ASSERT(_type == StageType::StubMaxPool || _type == StageType::StubAvgPool); - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); - _scaleInfo.setOutput(_outputEdges[0], inputScales[0]); + scaleInfo.setOutput(outputEdge(0), inputScales[0]); } } -void StubStage::propagateDataOrderImpl() const { +void StubStage::propagateDataOrderImpl(StageDataInfo&) { VPU_THROW_EXCEPTION << "Must be replaced with real stage"; } -void StubStage::getDataStridesRequirementsImpl() const { +void StubStage::getDataStridesRequirementsImpl(StageDataInfo&) { VPU_THROW_EXCEPTION << "Must be replaced with real stage"; } @@ -63,31 +59,37 @@ void StubStage::finalizeDataLayoutImpl() { VPU_THROW_EXCEPTION << "Must be replaced with real stage"; } -void StubStage::getBatchSupportInfoImpl() const { +void StubStage::getBatchSupportInfoImpl(StageDataInfo& batchInfo) { if (_type == StageType::StubConv || _type == StageType::StubFullyConnected || _type == StageType::StubDeconv) { - IE_ASSERT(_inputEdges.size() == 3); - IE_ASSERT(_outputEdges.size() == 1); - - auto weights = _inputEdges[1]->input(); - auto biases = _inputEdges[2]->input(); + auto weights = inputEdge(1)->input(); + auto biases = inputEdge(2)->input(); IE_ASSERT(weights->usage() == DataUsage::Const); IE_ASSERT(biases->usage() == DataUsage::Const || biases->usage() == DataUsage::Fake); - _batchInfo.setInput(_inputEdges[0], BatchSupport::Split); - _batchInfo.setOutput(_outputEdges[0], BatchSupport::Split); + batchInfo.setInput(inputEdge(0), BatchSupport::Split); + batchInfo.setOutput(outputEdge(0), BatchSupport::Split); } else { IE_ASSERT(_type == StageType::StubMaxPool || _type == StageType::StubAvgPool); - IE_ASSERT(_inputEdges.size() == 1); - IE_ASSERT(_outputEdges.size() == 1); - // Pooling will support batch by merging it with previous dimension. } } +void StubStage::initialCheckImpl() const { + if (_type == StageType::StubConv || _type == StageType::StubFullyConnected || _type == StageType::StubDeconv) { + assertInputsOutputsTypes(this, + {{DataType::FP16}, {DataType::FP16}, {DataType::FP16}}, + {{DataType::FP16}}); + } else if (_type == StageType::StubMaxPool || _type == StageType::StubAvgPool) { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); + } else { + VPU_THROW_EXCEPTION << "unknown type"; + } +} + void StubStage::finalCheckImpl() const { VPU_THROW_EXCEPTION << "Must never be called"; } diff --git a/inference-engine/src/vpu/graph_transformer/src/sw/post_op_stage.cpp b/inference-engine/src/vpu/graph_transformer/src/sw/post_op_stage.cpp index 1c3c56718bd5e5..cf1aa25cbbb6fa 100644 --- a/inference-engine/src/vpu/graph_transformer/src/sw/post_op_stage.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/sw/post_op_stage.cpp @@ -11,36 +11,28 @@ namespace vpu { -void PostOpStage::propagateDataOrderImpl() const { - IE_ASSERT(!_inputEdges.empty()); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); +void PostOpStage::propagateDataOrderImpl(StageDataInfo& orderInfo) { + auto input = inputEdge(0)->input(); auto inDimsOrder = input->desc().dimsOrder(); - _orderInfo.setOutput(_outputEdges[0], inDimsOrder); + orderInfo.setOutput(outputEdge(0), inDimsOrder); } -void PostOpStage::getDataStridesRequirementsImpl() const { - IE_ASSERT(!_inputEdges.empty()); - IE_ASSERT(_outputEdges.size() == 1); - - auto input = _inputEdges[0]->input(); +void PostOpStage::getDataStridesRequirementsImpl(StageDataInfo& stridesInfo) { + auto input = inputEdge(0)->input(); StridesRequirement reqs; reqs.add(2, DimStride::Compact); - _stridesInfo.setInput(_inputEdges[0], reqs); - _stridesInfo.setOutput(_outputEdges[0], reqs); + stridesInfo.setInput(inputEdge(0), reqs); + stridesInfo.setOutput(outputEdge(0), reqs); } void PostOpStage::finalizeDataLayoutImpl() { } -void PostOpStage::getBatchSupportInfoImpl() const { - IE_ASSERT(!_inputEdges.empty()); - IE_ASSERT(_outputEdges.size() == 1); +void PostOpStage::getBatchSupportInfoImpl(StageDataInfo& /*batchInfo*/) { } StageSHAVEsRequirements PostOpStage::getSHAVEsRequirementsImpl() const { @@ -48,22 +40,21 @@ StageSHAVEsRequirements PostOpStage::getSHAVEsRequirementsImpl() const { return StageSHAVEsRequirements::TwoOrOne; } -void PostOpStage::finalCheckImpl() const { +void PostOpStage::initialCheckImpl() const { + IE_ASSERT(numInputs() > 0); + IE_ASSERT(numOutputs() == 1); + assertAllInputsOutputsTypes(this, DataType::FP16, DataType::FP16); } void PostOpStage::serializeDataImpl(BlobSerializer& serializer) const { - IE_ASSERT(!_inputEdges.empty()); - IE_ASSERT(_outputEdges.size() == 1); - IE_ASSERT(_tempBufferEdges.empty()); - - auto input = _inputEdges[0]->input(); - auto output = _outputEdges[0]->output(); + auto input = inputEdge(0)->input(); + auto output = outputEdge(0)->output(); input->serializeNewBuffer(serializer); output->serializeNewBuffer(serializer); - for (int i = 1; i < _inputEdges.size(); ++i) { - _inputEdges[i]->input()->serializeNewBuffer(serializer); + for (int i = 1; i < numInputs(); ++i) { + this->input(i)->serializeNewBuffer(serializer); } } diff --git a/inference-engine/src/vpu/myriad_plugin/CMakeLists.txt b/inference-engine/src/vpu/myriad_plugin/CMakeLists.txt index b51f1a6aae6edf..7023513d1117e6 100644 --- a/inference-engine/src/vpu/myriad_plugin/CMakeLists.txt +++ b/inference-engine/src/vpu/myriad_plugin/CMakeLists.txt @@ -11,6 +11,11 @@ ie_add_plugin(NAME ${TARGET_NAME} SOURCES ${SOURCES} VERSION_DEFINES_FOR api/myriad_api.cpp) +add_dependencies(${TARGET_NAME} vpu_copy_firmware) +if(TARGET vpu_compile_custom_kernels) + add_dependencies(${TARGET_NAME} vpu_compile_custom_kernels) +endif() + target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" @@ -31,8 +36,3 @@ endif() target_link_libraries(${TARGET_NAME} PRIVATE ${INTEL_ITT_LIBS} inference_engine vpu_graph_transformer mvnc) -if (LINUX) - add_custom_command(TARGET ${TARGET_NAME} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/../vpu_custom_kernels - $/vpu_custom_kernels) -endif() diff --git a/inference-engine/src/vpu/myriad_plugin/api/myriad_api.cpp b/inference-engine/src/vpu/myriad_plugin/api/myriad_api.cpp index ad09790b2b9df8..1fa79839a88e6a 100644 --- a/inference-engine/src/vpu/myriad_plugin/api/myriad_api.cpp +++ b/inference-engine/src/vpu/myriad_plugin/api/myriad_api.cpp @@ -13,7 +13,7 @@ using namespace vpu::MyriadPlugin; INFERENCE_PLUGIN_API(StatusCode) CreatePluginEngine(IInferencePlugin *&plugin, ResponseDesc *resp) noexcept { try { auto mvnc = std::make_shared(); - plugin = make_ie_compatible_plugin({2, 0, CI_BUILD_NUMBER, "myriadPlugin"}, std::make_shared(mvnc)); + plugin = make_ie_compatible_plugin({{2, 1}, CI_BUILD_NUMBER, "myriadPlugin"}, std::make_shared(mvnc)); return OK; } catch (std::exception &ex) { diff --git a/inference-engine/src/vpu/myriad_plugin/myriad_async_infer_request.cpp b/inference-engine/src/vpu/myriad_plugin/myriad_async_infer_request.cpp index 7a1733e0c25f45..0c7d472fd8d0c2 100644 --- a/inference-engine/src/vpu/myriad_plugin/myriad_async_infer_request.cpp +++ b/inference-engine/src/vpu/myriad_plugin/myriad_async_infer_request.cpp @@ -4,6 +4,7 @@ #include #include "myriad_async_infer_request.h" +#include using namespace vpu::MyriadPlugin; using namespace InferenceEngine; @@ -21,6 +22,7 @@ MyriadAsyncInferRequest::MyriadAsyncInferRequest(MyriadInferRequest::Ptr request InferenceEngine::StagedTask::Ptr MyriadAsyncInferRequest::createAsyncRequestTask() { + VPU_PROFILE(createAsyncRequestTask); return std::make_shared([this]() { auto asyncTaskCopy = _asyncTask; try { diff --git a/inference-engine/src/vpu/myriad_plugin/myriad_config.cpp b/inference-engine/src/vpu/myriad_plugin/myriad_config.cpp index 3a9fb049f7e34d..6e9edf092fad2e 100644 --- a/inference-engine/src/vpu/myriad_plugin/myriad_config.cpp +++ b/inference-engine/src/vpu/myriad_plugin/myriad_config.cpp @@ -39,11 +39,19 @@ MyriadConfig::MyriadConfig(const std::map &config, Con { CONFIG_VALUE(YES), std::chrono::milliseconds(1000) }, { CONFIG_VALUE(NO), std::chrono::milliseconds(0) } }; + static const std::unordered_map powerConfigs = { + { VPU_MYRIAD_CONFIG_VALUE(POWER_FULL), PowerConfig::FULL }, + { VPU_MYRIAD_CONFIG_VALUE(POWER_INFER), PowerConfig::INFER }, + { VPU_MYRIAD_CONFIG_VALUE(POWER_STAGE), PowerConfig::STAGE }, + { VPU_MYRIAD_CONFIG_VALUE(POWER_STAGE_SHAVES), PowerConfig::STAGE_SHAVES }, + { VPU_MYRIAD_CONFIG_VALUE(POWER_STAGE_NCES), PowerConfig::STAGE_NCES }, + }; setOption(forceReset, boolSwitches, config, VPU_MYRIAD_CONFIG_KEY(FORCE_RESET)); setOption(platform, platformSwitches, config, VPU_MYRIAD_CONFIG_KEY(PLATFORM)); setOption(protocol, protocolSwitches, config, VPU_MYRIAD_CONFIG_KEY(PROTOCOL)); setOption(watchdogInterval, watchdogSwitches, config, VPU_MYRIAD_CONFIG_KEY(WATCHDOG)); + setOption(powerConfig, powerConfigs, config, VPU_MYRIAD_CONFIG_KEY(POWER_MANAGEMENT)); IE_SUPPRESS_DEPRECATED_START static const std::unordered_map platformSwitchesDepr = { @@ -84,6 +92,10 @@ IE_SUPPRESS_DEPRECATED_START {VPU_MYRIAD_CONFIG_KEY(PROTOCOL), { VPU_MYRIAD_CONFIG_VALUE(PCIE), VPU_MYRIAD_CONFIG_VALUE(USB), std::string()}}, {VPU_MYRIAD_CONFIG_KEY(WATCHDOG), {CONFIG_VALUE(YES), CONFIG_VALUE(NO)}}, + {VPU_MYRIAD_CONFIG_KEY(POWER_MANAGEMENT), + { VPU_MYRIAD_CONFIG_VALUE(POWER_FULL), VPU_MYRIAD_CONFIG_VALUE(POWER_INFER), + VPU_MYRIAD_CONFIG_VALUE(POWER_STAGE), VPU_MYRIAD_CONFIG_VALUE(POWER_STAGE_SHAVES), + VPU_MYRIAD_CONFIG_VALUE(POWER_STAGE_NCES)}}, {VPU_CONFIG_KEY(FORCE_RESET), {CONFIG_VALUE(YES), CONFIG_VALUE(NO)}}, {VPU_CONFIG_KEY(PLATFORM), @@ -129,6 +141,7 @@ IE_SUPPRESS_DEPRECATED_START {VPU_MYRIAD_CONFIG_KEY(PROTOCOL)}, {VPU_MYRIAD_CONFIG_KEY(WATCHDOG)}, {VPU_MYRIAD_CONFIG_KEY(THROUGHPUT_STREAMS)}, + {VPU_MYRIAD_CONFIG_KEY(POWER_MANAGEMENT)}, {VPU_CONFIG_KEY(FORCE_RESET)}, {VPU_CONFIG_KEY(PLATFORM)}, diff --git a/inference-engine/src/vpu/myriad_plugin/myriad_config.h b/inference-engine/src/vpu/myriad_plugin/myriad_config.h index a4c44375559dac..61b2db4121d04c 100644 --- a/inference-engine/src/vpu/myriad_plugin/myriad_config.h +++ b/inference-engine/src/vpu/myriad_plugin/myriad_config.h @@ -16,8 +16,17 @@ namespace vpu { namespace MyriadPlugin { +VPU_DECLARE_ENUM(PowerConfig, + FULL = 0, + INFER = 1, + STAGE = 2, + STAGE_SHAVES = 3, + STAGE_NCES = 4, +) + struct MyriadConfig final : ParsedConfig { bool forceReset = false; + PowerConfig powerConfig = PowerConfig::FULL; ncDevicePlatform_t platform = NC_ANY_PLATFORM; ncDeviceProtocol_t protocol = NC_ANY_PROTOCOL; std::chrono::milliseconds watchdogInterval = std::chrono::milliseconds(1000); diff --git a/inference-engine/src/vpu/myriad_plugin/myriad_executable_network.cpp b/inference-engine/src/vpu/myriad_plugin/myriad_executable_network.cpp index 504337e89fad95..1b736d6bc66fd6 100644 --- a/inference-engine/src/vpu/myriad_plugin/myriad_executable_network.cpp +++ b/inference-engine/src/vpu/myriad_plugin/myriad_executable_network.cpp @@ -6,8 +6,11 @@ #include #include +#include "cnn_network_impl.hpp" +#include "exec_graph_info.hpp" #include #include +#include #include using namespace InferenceEngine; @@ -43,6 +46,7 @@ static void selectNumberOfExecutors(const ncDevicePlatform_t& platform, ExecutableNetwork::ExecutableNetwork(std::vector &devicePool, const std::map &config, ConfigMode mode) { + VPU_PROFILE(ExecutableNetwork); _config = std::make_shared(config, mode); _log = std::make_shared("MyriadPlugin", _config->hostLogLevel, consoleOutput()); @@ -65,6 +69,7 @@ ExecutableNetwork::ExecutableNetwork(std::vector &devicePool, ExecutableNetwork::ExecutableNetwork(ICNNNetwork &network, std::vector &devicePool, const std::map &config) : ExecutableNetwork(devicePool, config) { + VPU_PROFILE(ExecutableNetwork); bool ti_proc_ok = !NetPass::CombineRNNSeq(network) ? NetPass::UnrollTI(network) : true; if (!ti_proc_ok) THROW_IE_EXCEPTION << "Plugin doesn't support Tensor Iterator in pure form. " @@ -80,7 +85,7 @@ ExecutableNetwork::ExecutableNetwork(ICNNNetwork &network, std::vectornumShaves, compiledGraph->numSlices, _config->numExecutors); _graphBlob = std::move(compiledGraph->blob); - _stagesMetaData = std::move(compiledGraph->stagesMeta); + _graphMetaData = std::move(compiledGraph->graphMeta); _inputInfo = std::move(compiledGraph->inputInfo); _outputInfo = std::move(compiledGraph->outputInfo); @@ -109,6 +114,7 @@ ExecutableNetwork::ExecutableNetwork(const std::string &blobFilename, std::vector &devicePool, const std::map &config) : ExecutableNetwork(devicePool, config, ConfigMode::RUNTIME_MODE) { + VPU_PROFILE(ExecutableNetwork); std::ifstream blobFile(blobFilename, std::ios::binary); std::ostringstream blobContentStream; blobContentStream << blobFile.rdbuf(); @@ -140,8 +146,8 @@ ExecutableNetwork::ExecutableNetwork(const std::string &blobFilename, _executor->allocateGraph(_device, _graphDesc, _graphBlob, blobHeader, numStages, networkName, _config->numExecutors); - _stagesMetaData.resize(numStages); - for (auto &meta : _stagesMetaData) { + _graphMetaData.stagesMeta.resize(numStages); + for (auto &meta : _graphMetaData.stagesMeta) { meta.stageName = meta.stageType = meta.layerName = meta.layerType = "UNKNOWN"; meta.status = InferenceEngineProfileInfo::LayerStatus::EXECUTED; } @@ -174,5 +180,138 @@ void ExecutableNetwork::GetMetric(const std::string &name, Parameter &result, Re } } +void ExecutableNetwork::GetExecGraphInfo(InferenceEngine::ICNNNetwork::Ptr &graphPtr) { + graphPtr = buildRuntimeGraph(_graphMetaData); +} + +InferenceEngine::ICNNNetwork::Ptr ExecutableNetwork::buildRuntimeGraph(GraphMetaInfo& graphMetaInfo) { + auto net = std::make_shared(); + net->setPrecision(Precision::FP16); + net->setName(graphMetaInfo.graphName); + + std::map stageMetaIndexToLayer; + + auto createLayerFromMeta = [&](const StageMetaInfo &stageMetaInfo) -> CNNLayer::Ptr { + auto layer = std::make_shared(LayerParams{stageMetaInfo.stageName, + stageMetaInfo.layerType, + Precision::FP16}); + + layer->params[ExecGraphInfoSerialization::ORIGINAL_NAMES] = stageMetaInfo.layerName; + layer->params[ExecGraphInfoSerialization::IMPL_TYPE] = stageMetaInfo.stageType; + layer->params[ExecGraphInfoSerialization::EXECUTION_ORDER] = std::to_string(stageMetaInfo.execOrder); + + std::stringstream layoutStream; + int ind = 0; + for (auto &outLayout : stageMetaInfo.outLayouts) { + if (ind == 0) { + layoutStream << outLayout; + ind++; + continue; + } + layoutStream << ',' << outLayout; + } + layer->params[ExecGraphInfoSerialization::OUTPUT_LAYOUTS] = layoutStream.str(); + + std::string outPrecisionsStr; + ind = 0; + for (auto &outPrecision : stageMetaInfo.outPrecisions) { + if (ind == 0) { + outPrecisionsStr += outPrecision.name(); + ind++; + continue; + } + outPrecisionsStr += ',' + std::string(outPrecision.name()); + } + layer->params[ExecGraphInfoSerialization::OUTPUT_PRECISIONS] = outPrecisionsStr; + + if (stageMetaInfo.execOrder < 0) { + layer->params[ExecGraphInfoSerialization::PERF_COUNTER] = "not_executed"; + } else { + layer->params[ExecGraphInfoSerialization::PERF_COUNTER] = std::to_string(stageMetaInfo.execTime); + } + + return layer; + }; + + // + // Write performance counts + // + + auto perfInfo = _executor->getPerfTimeInfo(_graphDesc._graphHandle); + + const auto deviceTimings = perfInfo.data(); + auto deviceTimingsCount = perfInfo.size(); + + if (deviceTimingsCount > 0) { + std::size_t timeIndex = 0; + + for (auto &stageMeta : graphMetaInfo.stagesMeta) { + if (stageMeta.status == ie::InferenceEngineProfileInfo::EXECUTED && + timeIndex < deviceTimingsCount) { + stageMeta.execTime += deviceTimings[timeIndex]; + timeIndex++; + } + } + } + + // + // Add all stages to network + // + + for (std::size_t i = 0; i < graphMetaInfo.stagesMeta.size(); i++) { + const auto stageMetaData = graphMetaInfo.stagesMeta[i]; + + if (stageMetaData.status == ie::InferenceEngineProfileInfo::LayerStatus::OPTIMIZED_OUT || + stageMetaData.stageName == "" || + stageMetaData.stageName == "") { + continue; + } + + auto layer = createLayerFromMeta(stageMetaData); + stageMetaIndexToLayer.insert(std::make_pair(i, layer)); + net->addLayer(layer); + } + + // + // Add all edges to network + // + + for (const auto &dataMetaData : graphMetaInfo.datasMeta) { + DataPtr data; + + auto parent = stageMetaIndexToLayer[dataMetaData.parentIndex]; + data = std::make_shared(dataMetaData.name, dataMetaData.desc); + parent->outData.push_back(data); + data->getCreatorLayer() = parent; + + for (auto &childMetaIndex : dataMetaData.childrenIndices) { + auto child = stageMetaIndexToLayer[childMetaIndex]; + data->getInputTo()[child->name] = child; + child->insData.push_back(data); + } + } + + // + // Specify inputs data + // + + for (std::size_t i = 0; i < graphMetaInfo.stagesMeta.size(); i++) { + const auto stageMetaData = graphMetaInfo.stagesMeta[i]; + + if (stageMetaData.inputsNum != 0 || + stageMetaData.stageName == "" || + stageMetaData.stageName == "") { + continue; + } + + auto input = stageMetaIndexToLayer[i]; + auto inputInfo = std::make_shared(); + inputInfo->setInputData(input->outData[0]); + net->setInputInfo(inputInfo); + } + + return net; +} + } // namespace MyriadPlugin } // namespace vpu diff --git a/inference-engine/src/vpu/myriad_plugin/myriad_executable_network.h b/inference-engine/src/vpu/myriad_plugin/myriad_executable_network.h index 9de33ec7835154..2aba48533a3e4b 100644 --- a/inference-engine/src/vpu/myriad_plugin/myriad_executable_network.h +++ b/inference-engine/src/vpu/myriad_plugin/myriad_executable_network.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -55,7 +56,7 @@ class ExecutableNetwork : public InferenceEngine::ExecutableNetworkThreadSafeDef InferenceEngine::OutputsDataMap networkOutputs) override { return std::make_shared(_graphDesc, networkInputs, networkOutputs, _inputInfo, _outputInfo, - _stagesMetaData, _config, _log, _executor); + _graphMetaData.stagesMeta, _config, _log, _executor); } void CreateInferRequest(InferenceEngine::IInferRequest::Ptr &asyncRequest) override { @@ -66,7 +67,7 @@ class ExecutableNetwork : public InferenceEngine::ExecutableNetworkThreadSafeDef auto syncRequestImpl = std::make_shared(_graphDesc, _networkInputs, _networkOutputs, _inputInfo, _outputInfo, - _stagesMetaData, _config, _log, + _graphMetaData.stagesMeta, _config, _log, _executor); syncRequestImpl->setPointerToExecutableNetworkInternal(shared_from_this()); auto taskExecutorGetResult = getNextTaskExecutor(); @@ -90,6 +91,8 @@ class ExecutableNetwork : public InferenceEngine::ExecutableNetworkThreadSafeDef void GetMetric(const std::string &name, InferenceEngine::Parameter &result, InferenceEngine::ResponseDesc *resp) const override; + void GetExecGraphInfo(InferenceEngine::ICNNNetwork::Ptr &graphPtr) override; + void GetMappedTopology( std::map> &deployedTopology) override { THROW_IE_EXCEPTION << "GetMappedTopology is not implemented\n"; @@ -101,7 +104,7 @@ class ExecutableNetwork : public InferenceEngine::ExecutableNetworkThreadSafeDef std::vector _graphBlob; GraphDesc _graphDesc; DevicePtr _device; - std::vector _stagesMetaData; + GraphMetaInfo _graphMetaData; std::shared_ptr _config; std::vector _supportedMetrics; @@ -126,6 +129,8 @@ class ExecutableNetwork : public InferenceEngine::ExecutableNetworkThreadSafeDef return taskExecutor; } + + InferenceEngine::ICNNNetwork::Ptr buildRuntimeGraph(GraphMetaInfo& graphMetaInfo); }; } // namespace MyriadPlugin diff --git a/inference-engine/src/vpu/myriad_plugin/myriad_executor.cpp b/inference-engine/src/vpu/myriad_plugin/myriad_executor.cpp index f0d16d4a454033..0f3bfcacba4223 100644 --- a/inference-engine/src/vpu/myriad_plugin/myriad_executor.cpp +++ b/inference-engine/src/vpu/myriad_plugin/myriad_executor.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "myriad_executor.h" #include "myriad_config.h" @@ -36,6 +37,7 @@ using namespace vpu; static std::mutex device_mutex; MyriadExecutor::MyriadExecutor(bool forceReset, const LogLevel& vpuLogLevel, const Logger::Ptr& log) : _log(log) { + VPU_PROFILE(MyriadExecutor); _mvnc = std::make_shared(); int ncResetAll = forceReset; auto status = ncGlobalSetOption(NC_RW_RESET_ALL, &ncResetAll, sizeof(ncResetAll)); @@ -75,7 +77,9 @@ ncStatus_t MyriadExecutor::bootNextDevice(std::vector &devicePool, const std::string& configDevName, const ncDevicePlatform_t &configPlatform, const ncDeviceProtocol_t &configProtocol, - int watchdogInterval) { + int watchdogInterval, + PowerConfig powerConfig) { + VPU_PROFILE(bootNextDevice); // #-17972, #-16790 #if defined(NO_BOOT) if (!devicePool.empty()) { @@ -186,6 +190,14 @@ ncStatus_t MyriadExecutor::bootNextDevice(std::vector &devicePool, device._name = deviceName; } + status = ncDeviceSetOption(device._deviceHandle, NC_RW_DEVICE_POWER_CONFIG, reinterpret_cast(&powerConfig), sizeof(dataLength)); + + if (status != NC_OK) { + _log->warning("Failed to set configuration for Power Manager"); + ncDeviceClose(&device._deviceHandle); + return status; + } + /* TODO: what should we do if we do not know maximum available graphs? What if we got number <= 0? */ device._graphNum = 1; device._deviceIdx = lastDeviceIdx + 1; @@ -195,6 +207,7 @@ ncStatus_t MyriadExecutor::bootNextDevice(std::vector &devicePool, DevicePtr MyriadExecutor::openDevice(std::vector &devicePool, const std::shared_ptr &config) { + VPU_PROFILE(openDevice); std::lock_guard lock(device_mutex); auto firstBootedButEmptyDevice = std::find_if(devicePool.begin(), devicePool.end(), @@ -227,7 +240,7 @@ DevicePtr MyriadExecutor::openDevice(std::vector &devicePool, } ncStatus_t booted = bootNextDevice(devicePool, config->deviceName, - config->platform, config->protocol, config->watchdogInterval.count()); + config->platform, config->protocol, config->watchdogInterval.count(), config->powerConfig); // TODO Is any tests for this case? #-19309 // In case, then there is no another not booted device, use already booted with minimum number of executors @@ -272,6 +285,7 @@ VPU_PACKED(bin_header { };) void MyriadExecutor::closeDevices(std::vector &devicePool) { + VPU_PROFILE(closeDevices); std::lock_guard lock(device_mutex); for (auto &device : devicePool) { if (device->_deviceHandle != nullptr) { @@ -287,6 +301,7 @@ void MyriadExecutor::allocateGraph(DevicePtr &device, GraphDesc &graphDesc, const std::vector &graphFileContent, const std::pair &graphHeaderDesc, size_t numStages, const char* networkName, int executors) { + VPU_PROFILE(allocateGraph); _numStages = numStages; graphDesc._name = networkName; if (device->_deviceHandle == nullptr) { @@ -373,6 +388,7 @@ void MyriadExecutor::allocateGraph(DevicePtr &device, GraphDesc &graphDesc, void MyriadExecutor::queueInference(GraphDesc &graphDesc, void *input_data, size_t input_bytes, void *result_data, size_t result_bytes) { + VPU_PROFILE(queueInference); #ifndef NDEBUG if (auto dumpFileName = std::getenv("IE_VPU_DUMP_INPUT_FILE_NAME")) { std::ofstream file(dumpFileName, std::ios_base::binary | std::ios_base::out); @@ -410,6 +426,7 @@ void MyriadExecutor::getResult(GraphDesc &graphDesc, void *result_data, unsigned } void MyriadExecutor::deallocateGraph(DevicePtr &device, GraphDesc &graphDesc) { + VPU_PROFILE(deallocateGraph); std::lock_guard lock(device_mutex); if (graphDesc._inputFifoHandle != nullptr) { diff --git a/inference-engine/src/vpu/myriad_plugin/myriad_executor.h b/inference-engine/src/vpu/myriad_plugin/myriad_executor.h index daec8a5750e30f..af40fdaccee33e 100644 --- a/inference-engine/src/vpu/myriad_plugin/myriad_executor.h +++ b/inference-engine/src/vpu/myriad_plugin/myriad_executor.h @@ -132,7 +132,8 @@ class MyriadExecutor { const std::string& configDevName, const ncDevicePlatform_t &configPlatform, const ncDeviceProtocol_t &configProtocol, - int watchdogInterval); + int watchdogInterval, + PowerConfig powerConfig); }; typedef std::shared_ptr MyriadExecutorPtr; diff --git a/inference-engine/src/vpu/myriad_plugin/myriad_infer_request.cpp b/inference-engine/src/vpu/myriad_plugin/myriad_infer_request.cpp index 79530c85c2e52e..65af2cbbad2904 100644 --- a/inference-engine/src/vpu/myriad_plugin/myriad_infer_request.cpp +++ b/inference-engine/src/vpu/myriad_plugin/myriad_infer_request.cpp @@ -12,6 +12,7 @@ #include #include +#include #include "myriad_executable_network.h" #include "myriad_infer_request.h" @@ -35,23 +36,27 @@ MyriadInferRequest::MyriadInferRequest(GraphDesc &graphDesc, _log(log), _stagesMetaData(blobMetaData), _config(myriadConfig), _inputInfo(inputInfo), _outputInfo(outputInfo), _graphDesc(graphDesc) { - _deviceLayout = _config->compileConfig.hwOptimization ? NCHW : NHWC; - if (_config->compileConfig.forceLayout == ComputeLayout::NCHW) - _deviceLayout = NCHW; - if (_config->compileConfig.forceLayout == ComputeLayout::NHWC) - _deviceLayout = NHWC; + VPU_PROFILE(MyriadInferRequest); + _layoutPreference = _config->compileConfig.hwOptimization ? + LayoutPreference::ChannelMajor : + LayoutPreference::ChannelMinor; + if (_config->compileConfig.forceLayout == ComputeLayout::NCHW || + _config->compileConfig.forceLayout == ComputeLayout::NCDHW) + _layoutPreference = LayoutPreference::ChannelMajor; + if (_config->compileConfig.forceLayout == ComputeLayout::NHWC || + _config->compileConfig.forceLayout == ComputeLayout::NDHWC) + _layoutPreference = LayoutPreference::ChannelMinor; + + const auto& ioStrides = _config->compileConfig.ioStrides; // allocate inputs for (auto &networkInput : _networkInputs) { + IE_ASSERT(ioStrides.find(networkInput.first) == ioStrides.end()) + << " input blob with strides is not supported"; + SizeVector dims = networkInput.second->getTensorDesc().getDims(); Precision precision = networkInput.second->getTensorDesc().getPrecision(); Layout layout = networkInput.second->getTensorDesc().getLayout(); - if (precision != Precision::FP32 && - precision != Precision::FP16 && - precision != Precision::U8) { - THROW_IE_EXCEPTION << PARAMETER_MISMATCH_str << "Unsupported input precision: " - << precision << "! Supported precisions: FP32, FP16 and U8"; - } Blob::Ptr inputBlob = make_blob_with_precision(TensorDesc( precision, dims, @@ -64,15 +69,13 @@ MyriadInferRequest::MyriadInferRequest(GraphDesc &graphDesc, } // allocate outputs for (auto &networkOutput : _networkOutputs) { + IE_ASSERT(ioStrides.find(networkOutput.first) == ioStrides.end()) + << " output blob with strides is not supported"; + SizeVector dims = networkOutput.second->getTensorDesc().getDims(); Precision precision = networkOutput.second->getTensorDesc().getPrecision(); Layout layout = networkOutput.second->getTensorDesc().getLayout(); - if (precision != Precision::FP32 && - precision != Precision::FP16) { - THROW_IE_EXCEPTION << PARAMETER_MISMATCH_str << "Unsupported output precision: " - << precision << "! Supported precisions: FP32, FP16"; - } Blob::Ptr outputBlob = make_blob_with_precision(TensorDesc( precision, dims, @@ -97,19 +100,7 @@ void MyriadInferRequest::InferImpl() { } void MyriadInferRequest::InferAsync() { - for (auto input : _inputs) { - auto const inputBlobPtr = input.second; - if (inputBlobPtr->getTensorDesc().getPrecision() != Precision::FP16 - && inputBlobPtr->getTensorDesc().getPrecision() != Precision::FP32 - && inputBlobPtr->getTensorDesc().getPrecision() != Precision::U8) - THROW_IE_EXCEPTION << PARAMETER_MISMATCH_str << "Unsupported input blob precision"; - } - for (auto output : _outputs) { - auto const outputBlobPtr = output.second; - if (outputBlobPtr->getTensorDesc().getPrecision() != Precision::FP16 - && outputBlobPtr->getTensorDesc().getPrecision() != Precision::FP32) - THROW_IE_EXCEPTION << PARAMETER_MISMATCH_str << "Unsupported output blob precision"; - } + VPU_PROFILE(InferAsync); // execute input pre-processing execDataPreprocessing(_inputs, true); // "true" stands for serial preprocessing in case of OpenMP @@ -124,14 +115,14 @@ void MyriadInferRequest::InferAsync() { auto inputBlob = input.second; size_t byteSize = inputBlob->byteSize(); Layout layout = inputBlob->getTensorDesc().getLayout(); - if (layout != _deviceLayout && (layout == NCHW || layout == NHWC)) { - // TODO copyBlob allocates new memory, but we already have allocated buffer of enough size - inputBlob = copyBlob(inputBlob, _deviceLayout); + Layout vpuLayout = deviceLayout(layout, _layoutPreference); + if (layout != vpuLayout) { + inputBlob = copyBlob(inputBlob, vpuLayout); } const auto input_offset_it = _inputInfo.offset.find(input.first); if (input_offset_it != _inputInfo.offset.end()) { - size_t required_buff_size = checked_cast(input_offset_it->second) + byteSize; + size_t required_buff_size = vpu::checked_cast(input_offset_it->second) + byteSize; IE_ASSERT(required_buff_size <= inputBuffer.size()); MEMCPY(&inputBuffer[input_offset_it->second], inputBlob->buffer().as(), byteSize); } @@ -146,9 +137,9 @@ void MyriadInferRequest::InferAsync() { tmpBlob = foundInputBlob->second; Layout layout = tmpBlob->getTensorDesc().getLayout(); - if (layout != _deviceLayout && (layout == NCHW || layout == NHWC)) { - // TODO copyBlob allocates new memory, but we already have allocated buffer of enough size - tmpBlob = copyBlob(tmpBlob, _deviceLayout); + Layout vpuLayout = deviceLayout(layout, _layoutPreference); + if (layout != vpuLayout) { + tmpBlob = copyBlob(tmpBlob, vpuLayout); } inputPtr = tmpBlob->buffer(); @@ -158,13 +149,14 @@ void MyriadInferRequest::InferAsync() { } void MyriadInferRequest::GetResult() { + VPU_PROFILE(GetResult); _executor->getResult(_graphDesc, resultBuffer.data(), resultBuffer.size()); for (auto pp : _outputs) { const auto offset_it = _outputInfo.offset.find(pp.first); if (offset_it != _outputInfo.offset.end()) { - size_t resultOffset = checked_cast(offset_it->second); + size_t resultOffset = vpu::checked_cast(offset_it->second); if (resultOffset > resultBuffer.size()) { THROW_IE_EXCEPTION << "unexpected result data size"; } @@ -173,7 +165,7 @@ void MyriadInferRequest::GetResult() { auto outDesc = outputBlob->getTensorDesc(); // TODO: TensorDesc doesn't update internal BlockingDesc and strides when setLayout is called - auto vpuLayout = (outDesc.getLayout() == NCHW || outDesc.getLayout() == NHWC) ? _deviceLayout : outDesc.getLayout(); + auto vpuLayout = deviceLayout(outDesc.getLayout(), _layoutPreference); ie::TensorDesc tempTensorDesc(outDesc.getPrecision(), outDesc.getDims(), vpuLayout); auto tmpBlob = make_blob_with_precision(tempTensorDesc, resultBuffer.data() + resultOffset); diff --git a/inference-engine/src/vpu/myriad_plugin/myriad_infer_request.h b/inference-engine/src/vpu/myriad_plugin/myriad_infer_request.h index b9e56e988f93b5..9420c1b74a9cb1 100644 --- a/inference-engine/src/vpu/myriad_plugin/myriad_infer_request.h +++ b/inference-engine/src/vpu/myriad_plugin/myriad_infer_request.h @@ -14,6 +14,7 @@ #include #include +#include #include "myriad_executor.h" #include "myriad_config.h" @@ -23,7 +24,7 @@ namespace MyriadPlugin { class MyriadInferRequest : public InferenceEngine::InferRequestInternal { MyriadExecutorPtr _executor; - InferenceEngine::Layout _deviceLayout; + LayoutPreference _layoutPreference; Logger::Ptr _log; std::vector _stagesMetaData; std::shared_ptr _config; diff --git a/inference-engine/src/vpu/myriad_plugin/myriad_plugin.cpp b/inference-engine/src/vpu/myriad_plugin/myriad_plugin.cpp index 9ac80a6c2f5a54..7b86aaad85adfa 100644 --- a/inference-engine/src/vpu/myriad_plugin/myriad_plugin.cpp +++ b/inference-engine/src/vpu/myriad_plugin/myriad_plugin.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "myriad_plugin.h" @@ -23,6 +24,7 @@ using namespace vpu::MyriadPlugin; ExecutableNetworkInternal::Ptr Engine::LoadExeNetworkImpl(const ICore * /*core*/, ICNNNetwork &network, const std::map &config) { + VPU_PROFILE(LoadExeNetworkImpl); InputsDataMap networkInputs; OutputsDataMap networkOutputs; @@ -76,6 +78,7 @@ void Engine::QueryNetwork(const ICNNNetwork& network, QueryNetworkResult& res) c void Engine::QueryNetwork(const ICNNNetwork& network, const std::map& config, QueryNetworkResult& res) const { + VPU_PROFILE(QueryNetwork); auto layerNames = getSupportedLayers( network, Platform::MYRIAD_2, @@ -107,6 +110,7 @@ Engine::Engine(std::shared_ptr mvnc) : // ImportNetwork gets a config provided by an user. LoadNetwork gets the plugin config and merge it with user's config. // Need to found a common way to handle configs IExecutableNetwork::Ptr Engine::ImportNetwork(const std::string &modelFileName, const std::map &config) { + VPU_PROFILE(ImportNetwork); std::ifstream blobFile(modelFileName, std::ios::binary); if (!blobFile.is_open()) { diff --git a/inference-engine/tests/CMakeLists.txt b/inference-engine/tests/CMakeLists.txt index ab92e78a3b2448..2c6ba6a2c7fd3d 100644 --- a/inference-engine/tests/CMakeLists.txt +++ b/inference-engine/tests/CMakeLists.txt @@ -27,8 +27,6 @@ enable_testing() add_subdirectory(helpers) -disable_deprecated_warnings() - if(ENABLE_TESTS) add_subdirectory(unit) endif() diff --git a/inference-engine/tests/helpers/single_layer_common.cpp b/inference-engine/tests/helpers/single_layer_common.cpp index 9452fc33193f45..aaf1affdbd83ed 100644 --- a/inference-engine/tests/helpers/single_layer_common.cpp +++ b/inference-engine/tests/helpers/single_layer_common.cpp @@ -115,25 +115,73 @@ void BufferWrapper::insert(size_t index, float value) { } } -void CompareCommon(const Blob::Ptr& actual, const Blob::Ptr& expected, float tolerance) { +void CompareCommonAbsolute(const Blob::Ptr& actual, const Blob::Ptr& expected, float tolerance) { ASSERT_NE(actual, nullptr); ASSERT_NE(expected, nullptr); - Layout res_layout = actual->getTensorDesc().getLayout(); - Layout ref_layout = expected->getTensorDesc().getLayout(); - SizeVector res_dims = actual->getTensorDesc().getDims(); + BufferWrapper res_ptr(actual); + BufferWrapper ref_ptr(expected); + float max_abs_error = 0; + size_t actualMaxErrId = 0; + size_t expectedMaxErrId = 0; + std::function absoluteErrorUpdater = [&](size_t actualIdx, size_t expectedIdx) { + auto actual = res_ptr[actualIdx]; + auto expected = ref_ptr[expectedIdx]; + float abs_error = fabsf(actual - expected); + if (abs_error > max_abs_error) { + max_abs_error = abs_error; + actualMaxErrId = actualIdx; + expectedMaxErrId = expectedIdx; + } + }; + CompareCommon(actual, expected, tolerance, absoluteErrorUpdater); + + ASSERT_NEAR(ref_ptr[expectedMaxErrId], res_ptr[actualMaxErrId], tolerance) + << "expectedMaxErrId = " << expectedMaxErrId + << " actualMaxErrId = " << actualMaxErrId; +} + +void CompareCommonRelative(const Blob::Ptr& actual, const Blob::Ptr& expected, float tolerance) { + ASSERT_NE(actual, nullptr); + ASSERT_NE(expected, nullptr); BufferWrapper res_ptr(actual); BufferWrapper ref_ptr(expected); + float max_rel_error = 0; + size_t actualMaxErrId = 0; + size_t expectedMaxErrId = 0; + std::function relatedErrorUpdater = [&](size_t actualIdx, size_t expectedIdx) { + auto actual = res_ptr[actualIdx]; + auto expected = ref_ptr[expectedIdx]; + float abs_error = fabsf(actual - expected); + float rel_error = expected != 0.0 ? fabsf(abs_error / expected) : abs_error; + if (rel_error > max_rel_error) { + max_rel_error = rel_error; + actualMaxErrId = actualIdx; + expectedMaxErrId = expectedIdx; + } + }; + CompareCommon(actual, expected, tolerance, relatedErrorUpdater); + + float abs_threshold = fabsf(ref_ptr[expectedMaxErrId]) * tolerance; + ASSERT_NEAR(ref_ptr[expectedMaxErrId], res_ptr[actualMaxErrId], abs_threshold) + << "expectedMaxErrId = " << expectedMaxErrId + << " actualMaxErrId = " << actualMaxErrId; +} + +void CompareCommon(const Blob::Ptr& actual, const Blob::Ptr& expected, float tolerance, + const std::function& errorUpdater) { + ASSERT_NE(actual, nullptr); + ASSERT_NE(expected, nullptr); + + Layout res_layout = actual->getTensorDesc().getLayout(); + Layout ref_layout = expected->getTensorDesc().getLayout(); + SizeVector res_dims = actual->getTensorDesc().getDims(); size_t res_size = actual->size(); size_t ref_size = expected->size(); ASSERT_EQ(res_size, ref_size); - float max_error = 0; - size_t actualMaxErrId = 0; - size_t expectedMaxErrId = 0; - if (res_layout == NCHW || res_layout == NHWC) { size_t N = res_dims[0]; size_t C = res_dims[1]; @@ -150,12 +198,7 @@ void CompareCommon(const Blob::Ptr& actual, const Blob::Ptr& expected, float tol size_t expectedIdx = ref_layout == NCHW ? w + h * W + c * W * H + n * W * H * C : c + w * C + h * C * W + n * C * W * H; - float cur_diff = fabs(res_ptr[actualIdx] - ref_ptr[expectedIdx]); - if (cur_diff > max_error) { - max_error = cur_diff; - actualMaxErrId = actualIdx; - expectedMaxErrId = expectedIdx; - } + errorUpdater(actualIdx, expectedIdx); } } } @@ -168,28 +211,15 @@ void CompareCommon(const Blob::Ptr& actual, const Blob::Ptr& expected, float tol for (size_t n = 0; n < N; n++) { for (size_t c = 0; c < C; c++) { size_t actualIdx = c + n * C; - float cur_diff = fabs(res_ptr[actualIdx] - ref_ptr[actualIdx]); - if (cur_diff > max_error) { - max_error = cur_diff; - actualMaxErrId = actualIdx; - expectedMaxErrId = actualIdx; - } + errorUpdater(actualIdx, actualIdx); } } } else { for (size_t i = 0; i < ref_size; i++) { - float cur_diff = fabs(res_ptr[i] - ref_ptr[i]); - if (cur_diff > max_error) { - max_error = cur_diff; - actualMaxErrId = expectedMaxErrId = i; - } + errorUpdater(i, i); } } } - - ASSERT_NEAR(ref_ptr[expectedMaxErrId], res_ptr[actualMaxErrId], tolerance) - << "expectedMaxErrId = " << expectedMaxErrId - << " actualMaxErrId = " << actualMaxErrId; } void fill_data_common(BufferWrapper& data, size_t size, size_t duty_ratio) { diff --git a/inference-engine/tests/helpers/single_layer_common.hpp b/inference-engine/tests/helpers/single_layer_common.hpp index e7a998c3c6ce32..bb89ae1f839c7a 100644 --- a/inference-engine/tests/helpers/single_layer_common.hpp +++ b/inference-engine/tests/helpers/single_layer_common.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #ifndef USE_BOOST_RE @@ -24,13 +25,13 @@ #define FIND_STR(SRC, PATTERN) boost::regex_search(SRC, boost::regex(PATTERN)) #endif -#define REPLACE_WITH_NUM(SRC, PATTERN, NUM) REPLACE_WITH_STR(SRC, PATTERN, std::to_string(NUM)) +#define REPLACE_WITH_NUM(SRC, PATTERN, NUM) REPLACE_WITH_STR(SRC, PATTERN, to_string_c_locale(NUM)) #define REPLACE_WITH_NUM_VECTOR(SRC, PATTERN, NUMS) \ { std::string result; \ if (NUMS.size() > 0) { \ - result += std::to_string(NUMS[0]); \ + result += to_string_c_locale(NUMS[0]); \ for (int i = 1; i < NUMS.size(); i++) { \ - result += "," + std::to_string(NUMS[i]); \ + result += "," + to_string_c_locale(NUMS[i]); \ } \ } \ REPLACE_WITH_STR(SRC, PATTERN, result); } @@ -38,9 +39,9 @@ { std::string result; \ auto nums_size = NUMS.size(); \ if (nums_size > 0) { \ - result += std::to_string(NUMS[nums_size - 1]); \ + result += to_string_c_locale(NUMS[nums_size - 1]); \ for (int i = 2; i <= nums_size; i++) { \ - result += "," + std::to_string(NUMS[nums_size - i]); \ + result += "," + to_string_c_locale(NUMS[nums_size - i]); \ } \ } \ REPLACE_WITH_STR(SRC, PATTERN, result); } @@ -133,7 +134,17 @@ class BufferWrapper { void insert(size_t index, float value); }; -void -CompareCommon(const InferenceEngine::Blob::Ptr &actual, const InferenceEngine::Blob::Ptr &expected, float tolerance); +void CompareCommon(const InferenceEngine::Blob::Ptr &actual, + const InferenceEngine::Blob::Ptr &expected, + float tolerance, + const std::function &errorUpdater); + +void CompareCommonAbsolute(const InferenceEngine::Blob::Ptr &actual, + const InferenceEngine::Blob::Ptr &expected, + float tolerance); + +void CompareCommonRelative(const InferenceEngine::Blob::Ptr &actual, + const InferenceEngine::Blob::Ptr &expected, + float tolerance); void fill_data_common(BufferWrapper &data, size_t size, size_t duty_ratio = 10); diff --git a/inference-engine/tests/helpers/tests_common.hpp b/inference-engine/tests/helpers/tests_common.hpp index 8d97b07c1b77a9..2895e9ce4d33b0 100644 --- a/inference-engine/tests/helpers/tests_common.hpp +++ b/inference-engine/tests/helpers/tests_common.hpp @@ -14,24 +14,33 @@ #include #include -#ifdef WIN32 -#define UNUSED +#ifdef _WIN32 +# define UNUSED #else -#define UNUSED __attribute__((unused)) +# define UNUSED __attribute__((unused)) #endif #include "stdlib.h" #include "stdio.h" #include "string.h" #ifdef _WIN32 - #include "Psapi.h" +# include "Psapi.h" #endif +template +inline std::string to_string_c_locale(T value) { + std::stringstream val_stream; + val_stream.imbue(std::locale("C")); + val_stream << value; + return val_stream.str(); +} + class BaseTestCreator { protected: std::string _type; public: explicit BaseTestCreator(const std::string& type) : _type(type) {} + virtual ~BaseTestCreator() = default; virtual InferenceEngine::CNNLayerPtr create(const std::string& type) = 0; @@ -104,6 +113,7 @@ class TestsCommon : public ::testing::Test { std::make_shared>("Floor"), std::make_shared>("HardSigmoid"), std::make_shared>("Log"), + std::make_shared>("Exp"), std::make_shared>("Reciprocal"), std::make_shared>("Selu"), std::make_shared>("Sign"), @@ -124,7 +134,9 @@ class TestsCommon : public ::testing::Test { std::make_shared>("ReduceProd"), std::make_shared>("ReduceSum"), std::make_shared>("ReduceSumSquare"), - std::make_shared>("TopK") + std::make_shared>("TopK"), + std::make_shared>("NonMaxSuppression"), + std::make_shared>("ScatterUpdate") }; return creators; } @@ -365,23 +377,25 @@ class TestsCommon : public ::testing::Test { } std::string replace(std::string& str, const std::string& from, const int& to) { - replace(str, from, std::to_string(to)); + replace(str, from, to_string_c_locale(to)); return str; } std::string replace(std::string& str, const std::string& from, const size_t& to) { - replace(str, from, std::to_string(to)); + replace(str, from, to_string_c_locale(to)); return str; } std::string replace(std::string& str, const std::string& from, const float& to) { - replace(str, from, std::to_string(to)); + replace(str, from, to_string_c_locale(to)); return str; } // trim from both ends (in place) static inline std::string &trim(std::string &s) { - s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); - s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int c){ + return !std::isspace(c);})); + s.erase(std::find_if(s.rbegin(), s.rend(), [](int c){ + return !std::isspace(c);}).base(), s.end()); return s; } diff --git a/inference-engine/tests/helpers/tests_vpu_common.cpp b/inference-engine/tests/helpers/tests_vpu_common.cpp new file mode 100644 index 00000000000000..0fed062ec04f96 --- /dev/null +++ b/inference-engine/tests/helpers/tests_vpu_common.cpp @@ -0,0 +1,43 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include + +#include "single_layer_common.hpp" +#include "tests_vpu_common.hpp" + +using namespace InferenceEngine; + +/* this function assumes that the precision of a generated network is FP16 */ +std::shared_ptr createNetworkWithDesiredSize(std::size_t sizeInMB) { + + Builder::Network builder("network"); + Builder::FullyConnectedLayer fcBuilder("FullyConnected"); + + SizeVector inputDims = {1, 2, 16, 16}; // 1 KB + + auto generateBlob = [](Precision precision, + SizeVector dims, Layout layout) { + IE_ASSERT(precision == Precision::FP16); + Blob::Ptr blob = make_shared_blob(TensorDesc(precision, dims, layout)); + blob->allocate(); + GenRandomDataCommon(blob); + return blob; + }; + + idx_t layerId = builder.addLayer(Builder::InputLayer("input").setPort(Port(inputDims))); + + idx_t weightsId = builder.addLayer(Builder::ConstLayer("weights").setData(generateBlob(Precision::FP16, + {sizeInMB * 1024, 2, 16, 16}, Layout::OIHW))); + + layerId = builder.addLayer({{layerId}, {weightsId}}, Builder::FullyConnectedLayer("FullyConnected").setOutputNum(1024 * sizeInMB)); + + builder.addLayer({PortInfo(layerId)}, Builder::OutputLayer("output")); + + INetwork::CPtr network = builder.build(); + std::shared_ptr cnnNetwork = Builder::convertToICNNNetwork(network); + + return cnnNetwork; +} diff --git a/inference-engine/tests/helpers/tests_vpu_common.hpp b/inference-engine/tests/helpers/tests_vpu_common.hpp new file mode 100644 index 00000000000000..eee97e44770ee6 --- /dev/null +++ b/inference-engine/tests/helpers/tests_vpu_common.hpp @@ -0,0 +1,100 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include + +#include + +#include +#include + +#include "single_layer_common.hpp" +#include "vpu/vpu_plugin_config.hpp" +#include + + +using config_t = std::map; + +static constexpr char ENV_MYRIADX[] = "IE_VPU_MYRIADX"; +static constexpr char ENV_HDDL_R[] = "IE_VPU_ENABLE_PER_LAYER_TESTS_HDDL"; + +#define DISABLE_IF(expression) \ +{ \ + if (expression) { \ + SKIP() << "Disabled since " << #expression << std::endl; \ + } \ +} + +#if defined(_WIN32) || defined(WIN32) + #define DISABLE_ON_WINDOWS_IF(expr) DISABLE_IF((expr)) +#else + #define DISABLE_ON_WINDOWS_IF(expr) +#endif + +static bool hasPlatform(const std::string &environment_variable) { + auto env = std::getenv(environment_variable.c_str()); + if (!env) { + return false; + } + + int value; + try { + value = std::stoi(env); + } catch (...) { + return false; + } + + return value != 0; +} + +static bool hasMyriadX() { + return hasPlatform(ENV_MYRIADX); +} + +static bool hasMyriad2() { + /* TODO: change with environment variable for MYRIAD-2 */ + return !hasMyriadX(); +} + +static bool hasAppropriateStick(const config_t &config) { + bool suitsConfig; + + auto platform = config.find(VPU_MYRIAD_CONFIG_KEY(PLATFORM)); + if (platform == config.end() || platform->second.empty()) { + suitsConfig = hasMyriad2() || hasMyriadX(); + } else { + bool hasRequestedMyriad2 = + platform->second == VPU_MYRIAD_CONFIG_VALUE(2450) && hasMyriad2(); + bool hasRequestedMyriadX = + platform->second == VPU_MYRIAD_CONFIG_VALUE(2480) && hasMyriadX(); + suitsConfig = hasRequestedMyriad2 || hasRequestedMyriadX; + } + + bool suitsDeprecatedConfig; + // Deprecated api + IE_SUPPRESS_DEPRECATED_START + platform = config.find(VPU_CONFIG_KEY(PLATFORM)); + if (platform == config.end() || platform->second.empty()) { + suitsDeprecatedConfig = hasMyriad2() || hasMyriadX(); + } else { + bool hasRequestedMyriad2 = + platform->second == VPU_CONFIG_VALUE(2450) && hasMyriad2(); + bool hasRequestedMyriadX = + platform->second == VPU_CONFIG_VALUE(2480) && hasMyriadX(); + suitsDeprecatedConfig = hasRequestedMyriad2 || hasRequestedMyriadX; + } + IE_SUPPRESS_DEPRECATED_END + + return suitsConfig && suitsDeprecatedConfig; +} + +static bool hasHDDL_R() { + return hasPlatform(ENV_HDDL_R); +} + +/* this function assumes that the precision of a generated network is FP16 */ +std::shared_ptr createNetworkWithDesiredSize(std::size_t sizeInMB); diff --git a/inference-engine/tests/unit/CMakeLists.txt b/inference-engine/tests/unit/CMakeLists.txt index d83e0880e6bae3..8180802793a0da 100644 --- a/inference-engine/tests/unit/CMakeLists.txt +++ b/inference-engine/tests/unit/CMakeLists.txt @@ -14,14 +14,12 @@ SET (CMAKE_SKIP_RPATH OFF) file(GLOB TEST_SRC graph_tools/*.cpp + http_client/*.cpp inference_engine_tests/*.cpp inference_engine_tests/cpp_interfaces/*.cpp inference_engine_tests/normalization/*.cpp - mem_solver/*.cpp cnn_network/*.cpp builders/*.cpp - transformations/*.cpp - ie_class/*.cpp # TODO: apeskov: Please fix issue CVS # shape_infer/*.cpp shape_infer/built-in/*.cpp @@ -76,7 +74,7 @@ source_group("include" FILES ${TEST_INCLUDE}) # create target -add_executable(${TARGET_NAME} ${TEST_SRC} ${TEST_INCLUDE} ${MKLDNN_TESTS} ${MKLDNN_TESTS_INCLUDE} ${DLAI_TESTS} transformations/sub_test.cpp transformations/tranformations_test.hpp) +add_executable(${TARGET_NAME} ${TEST_SRC} ${TEST_INCLUDE} ${MKLDNN_TESTS} ${MKLDNN_TESTS_INCLUDE} ${DLIA_TESTS}) set_ie_threading_interface_for(${TARGET_NAME}) target_include_directories(${TARGET_NAME} PRIVATE @@ -93,6 +91,10 @@ set_target_properties(${TARGET_NAME} PROPERTIES COMPILE_PDB_NAME ${TARGET_NAME}) target_compile_options(${TARGET_NAME} PRIVATE $<$: -Wno-inconsistent-missing-override >) target_compile_options(${TARGET_NAME} PRIVATE $<$: -Wno-inconsistent-missing-override >) +if (ENABLE_MYRIAD) + target_link_libraries(${TARGET_NAME} PRIVATE mvnc vpu_graph_transformer_test_static) +endif () + target_link_libraries(${TARGET_NAME} PRIVATE gtest gflags @@ -112,6 +114,10 @@ if (ENABLE_MKL_DNN) mkldnn) endif () +if (ENABLE_DLIA) + target_link_libraries(${TARGET_NAME} PRIVATE dliaPluginIOTransformations) +endif () + add_test(NAME ${TARGET_NAME} COMMAND ${TARGET_NAME}) diff --git a/inference-engine/tests/unit/builders/network_builder_test.cpp b/inference-engine/tests/unit/builders/network_builder_test.cpp index f707c79cdef6a8..2c694c095c6695 100644 --- a/inference-engine/tests/unit/builders/network_builder_test.cpp +++ b/inference-engine/tests/unit/builders/network_builder_test.cpp @@ -190,12 +190,10 @@ class NetworkBuilderTest : public BuilderTestCommon { } } - void compareICNNNetworks(const ICNNNetwork& newNetwork, const ICNNNetwork& oldNetwork) { - IE_SUPPRESS_DEPRECATED_START - CNNNetwork network((ICNNNetwork*)&newNetwork); - IE_SUPPRESS_DEPRECATED_END + void compareICNNNetworks(const ICNNNetwork::Ptr newNetwork, const ICNNNetwork& oldNetwork) { + CNNNetwork network(newNetwork); - if (newNetwork.layerCount() != oldNetwork.layerCount()) + if (newNetwork->layerCount() != oldNetwork.layerCount()) THROW_IE_EXCEPTION << "ICNNNetworks have different numbers of layers!"; for (const auto& layer : network) { CNNLayerPtr oldLayer; @@ -223,8 +221,8 @@ class NetworkBuilderTest : public BuilderTestCommon { InputsDataMap newInput; OutputsDataMap newOutput; - newNetwork.getInputsInfo(newInput); - newNetwork.getOutputsInfo(newOutput); + newNetwork->getInputsInfo(newInput); + newNetwork->getOutputsInfo(newOutput); InputsDataMap oldInput; OutputsDataMap oldOutput; oldNetwork.getInputsInfo(oldInput); @@ -724,7 +722,7 @@ TEST_F(NetworkBuilderTest, convertFromICNNNetworkToICNNNetwork) { std::shared_ptr network = Builder::convertToICNNNetwork(Builder::Network(net_reader.getNetwork()).build()); try { - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } catch (InferenceEngine::details::InferenceEngineException &ex) { FAIL() << ex.what(); } @@ -1148,7 +1146,7 @@ TEST_F(NetworkBuilderTest, CreateLSTMFromBuilder) { builder.addLayer({{lstm, 2}}, Builder::OutputLayer("output2")); const auto network = Builder::convertToICNNNetwork(builder.build()); try { - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } catch (InferenceEngine::details::InferenceEngineException &ex) { FAIL() << ex.what(); } @@ -1187,6 +1185,8 @@ TEST_F(NetworkBuilderTest, CheckPreProcessAlexNet) { } TEST_F(NetworkBuilderTest, ReshapeNetworkTest) { + Builder::ReshapeLayer("WA"); + std::string model = R"V0G0N( @@ -1231,7 +1231,7 @@ TEST_F(NetworkBuilderTest, ReshapeNetworkTest) { network->getLayerByName("flatten", layer, nullptr); ASSERT_EQ(layer->outData[0]->getDims().size(), 2); try { - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } catch (InferenceEngine::details::InferenceEngineException &ex) { FAIL() << ex.what(); } diff --git a/inference-engine/tests/unit/builders/transform_network_test.cpp b/inference-engine/tests/unit/builders/transform_network_test.cpp deleted file mode 100644 index ebfce47a8c4fad..00000000000000 --- a/inference-engine/tests/unit/builders/transform_network_test.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include -#include -#include -#include - -#include "builder_test.hpp" - -using namespace testing; -using namespace InferenceEngine; - -class TransformNetworkTest: public BuilderTestCommon {}; - -TEST_F(TransformNetworkTest, AddNewLayer) { - Builder::Network builder("test"); - Transform::Network network(builder); - ASSERT_EQ(0, builder.size()); - network.addLayer(Builder::InputLayer("in1").setPort(Port({1, 3, 27, 27}))); - ASSERT_EQ(1, builder.size()); -} - -TEST_F(TransformNetworkTest, RemoveLayer) { - Builder::Network builder("test"); - Transform::Network network(builder); - ASSERT_EQ(0, builder.size()); - Transform::Layer layer = network.addLayer(Builder::InputLayer("in1").setPort(Port({1, 3, 27, 27}))); - ASSERT_EQ(1, builder.size()); - - network.removeLayer(layer); - ASSERT_EQ(0, builder.size()); -} - -TEST_F(TransformNetworkTest, GetIncorrectPort) { - Builder::Network builder("test"); - Transform::Network network(builder); - Transform::Layer layer = network.addLayer(Builder::InputLayer("in1").setPort(Port({1, 3, 27, 27}))); - ASSERT_THROW(layer.getInPort(), InferenceEngine::details::InferenceEngineException); - ASSERT_THROW(layer.getOutPort(1), InferenceEngine::details::InferenceEngineException); -} - - -TEST_F(TransformNetworkTest, GetCorrectPort) { - Builder::Network builder("test"); - Transform::Network network(builder); - Transform::Layer layer = network.addLayer(Builder::InputLayer("in1").setPort(Port({1, 3, 27, 27}))); - ASSERT_NO_THROW(layer.getOutPort()); - ASSERT_NO_THROW(layer.getOutPort(0)); -} - -TEST_F(TransformNetworkTest, GetLayerById) { - Builder::Network builder("test"); - Transform::Network network(builder); - Transform::Layer layer = network.addLayer(Builder::InputLayer("in1").setPort(Port({1, 3, 27, 27}))); - ASSERT_NO_THROW(network.getLayer(layer.getId())); -} - -TEST_F(TransformNetworkTest, GetLayerByName) { - Builder::Network builder("test"); - Transform::Network network(builder); - network.addLayer(Builder::InputLayer("in1").setPort(Port({1, 3, 27, 27}))); - ASSERT_NO_THROW(network.getLayer("in1")); -} - -TEST_F(TransformNetworkTest, ConnectTwoLayers) { - Builder::Network builder("test"); - Transform::Network network(builder); - Transform::Layer input = network.addLayer(Builder::InputLayer("in1").setPort(Port({1, 3, 27, 27}))); - Transform::Layer relu = network.addLayer(Builder::ReLULayer("relu1")); - ASSERT_EQ(2, builder.size()); - ASSERT_EQ(0, builder.getConnections().size()); - network.connect(input, relu); - ASSERT_EQ(1, builder.getConnections().size()); -} - -TEST_F(TransformNetworkTest, ConnectTwoPorts) { - Builder::Network builder("test"); - Transform::Network network(builder); - Transform::Port inputPort = network.addLayer(Builder::InputLayer("in1").setPort(Port({1, 3, 27, 27}))).getOutPort(); - Transform::Port reluPort = network.addLayer(Builder::ReLULayer("relu1")).getInPort(); - ASSERT_EQ(2, builder.size()); - ASSERT_EQ(0, builder.getConnections().size()); - network.connect(inputPort, reluPort); - ASSERT_EQ(1, builder.getConnections().size()); -} - -TEST_F(TransformNetworkTest, DisconnectTwoLayers) { - Builder::Network builder("test"); - Transform::Network network(builder); - Transform::Layer input = network.addLayer(Builder::InputLayer("in1").setPort(Port({1, 3, 27, 27}))); - Transform::Layer relu = network.addLayer(Builder::ReLULayer("relu1")); - ASSERT_EQ(2, builder.size()); - ASSERT_EQ(0, builder.getConnections().size()); - network.connect(input, relu); - ASSERT_EQ(1, builder.getConnections().size()); - network.disconnect(input, relu); - ASSERT_EQ(0, builder.getConnections().size()); -} - -TEST_F(TransformNetworkTest, DisonnectTwoPorts) { - Builder::Network builder("test"); - Transform::Network network(builder); - Transform::Port inputPort = network.addLayer(Builder::InputLayer("in1").setPort(Port({1, 3, 27, 27}))).getOutPort(); - Transform::Port reluPort = network.addLayer(Builder::ReLULayer("relu1")).getInPort(); - ASSERT_EQ(2, builder.size()); - ASSERT_EQ(0, builder.getConnections().size()); - network.connect(inputPort, reluPort); - ASSERT_EQ(1, builder.getConnections().size()); - network.disconnect(inputPort, reluPort); - ASSERT_EQ(0, builder.getConnections().size()); -} - -TEST_F(TransformNetworkTest, RemoveLayerAndConnection) { - Builder::Network builder("test"); - Transform::Network network(builder); - Transform::Layer input = network.addLayer(Builder::InputLayer("in1").setPort(Port({1, 3, 27, 27}))); - Transform::Layer relu = network.addLayer(Builder::ReLULayer("relu1")); - network.connect(input, relu); - ASSERT_EQ(1, builder.getConnections().size()); - ASSERT_EQ(2, builder.size()); - network.removeLayer(relu); - ASSERT_EQ(0, builder.getConnections().size()); - ASSERT_EQ(1, builder.size()); -} - -TEST_F(TransformNetworkTest, GetInitializedConnection) { - Builder::Network builder("test"); - Transform::Network network(builder); - Transform::Layer input = network.addLayer(Builder::InputLayer("in1").setPort(Port({1, 3, 27, 27}))); - Transform::Layer relu = network.addLayer(Builder::ReLULayer("relu1")); - network.connect(input, relu); - ASSERT_EQ(input.getOutPort(), relu.getInPort().getConnection().getSource()); -} - -TEST_F(TransformNetworkTest, GetIncorrectConnections) { - Builder::Network builder("test"); - Transform::Network network(builder); - Transform::Layer input = network.addLayer(Builder::InputLayer("in1").setPort(Port({1, 3, 27, 27}))); - Transform::Layer relu = network.addLayer(Builder::ReLULayer("relu1")); - ASSERT_THROW(relu.getInPort().getConnection().getSource(), InferenceEngine::details::InferenceEngineException); - ASSERT_THROW(input.getOutPort().getConnection().getDestination(), InferenceEngine::details::InferenceEngineException); - ASSERT_NO_THROW(input.getOutPort().getConnection().getSource()); - ASSERT_NO_THROW(relu.getInPort().getConnection().getDestination()); -} - -TEST_F(TransformNetworkTest, ConnectToSourcePortsFromConnection) { - Builder::Network builder("test"); - Transform::Network network(builder); - Transform::Port inputPort = network.addLayer(Builder::InputLayer("in1").setPort(Port({1, 3, 27, 27}))).getOutPort(); - Transform::Port reluPort = network.addLayer(Builder::ReLULayer("relu1")).getInPort(); - ASSERT_EQ(2, builder.size()); - ASSERT_EQ(0, builder.getConnections().size()); - ASSERT_NO_THROW(inputPort.getConnection().setDestination(reluPort)); - ASSERT_EQ(1, builder.getConnections().size()); -} - -TEST_F(TransformNetworkTest, ConnectWithTwoDestinations) { - Builder::Network builder("test"); - Transform::Network network(builder); - Transform::Port inputPort = network.addLayer(Builder::InputLayer("in1").setPort(Port({1, 3, 27, 27}))).getOutPort(); - Transform::Port reluPort1 = network.addLayer(Builder::ReLULayer("relu1")).getInPort(); - Transform::Port reluPort2 = network.addLayer(Builder::ReLULayer("relu2")).getInPort(); - ASSERT_EQ(3, builder.size()); - ASSERT_EQ(0, builder.getConnections().size()); - ASSERT_NO_THROW(inputPort.getConnection().setDestination(reluPort1)); - ASSERT_NO_THROW(inputPort.getConnection().addDestination(reluPort2)); - ASSERT_THROW(inputPort.getConnection().addDestination(reluPort2), InferenceEngine::details::InferenceEngineException); - ASSERT_EQ(2, builder.getConnections().size()); - ASSERT_THROW(inputPort.getConnection().setDestination(reluPort2), InferenceEngine::details::InferenceEngineException); - ASSERT_NO_THROW(inputPort.getConnection().setDestinations({reluPort2, reluPort1})); - ASSERT_EQ(2, builder.getConnections().size()); -} - -TEST_F(TransformNetworkTest, ConnectToDestinationPortsFromConnection) { - Builder::Network builder("test"); - Transform::Network network(builder); - Transform::Port inputPort = network.addLayer(Builder::InputLayer("in1").setPort(Port({1, 3, 27, 27}))).getOutPort(); - Transform::Port reluPort = network.addLayer(Builder::ReLULayer("relu1")).getInPort(); - ASSERT_EQ(2, builder.size()); - ASSERT_EQ(0, builder.getConnections().size()); - reluPort.getConnection().setSource(inputPort); - ASSERT_EQ(1, builder.getConnections().size()); -} \ No newline at end of file diff --git a/inference-engine/tests/unit/cnn_network/cnn_layer_validation_tests.cpp b/inference-engine/tests/unit/cnn_network/cnn_layer_validation_tests.cpp index fc9b8085c9a25b..4803449c268c31 100644 --- a/inference-engine/tests/unit/cnn_network/cnn_layer_validation_tests.cpp +++ b/inference-engine/tests/unit/cnn_network/cnn_layer_validation_tests.cpp @@ -10,7 +10,7 @@ #include #include <../shape_infer/built_in_shape_infer_general_test.hpp> #include -#include <../include/ie_data.h> +#include #include "layer_builder.h" #include "shapes.h" diff --git a/inference-engine/tests/unit/cnn_network/parameters.h b/inference-engine/tests/unit/cnn_network/parameters.h index 451422521f40f4..30f109ef7f3102 100644 --- a/inference-engine/tests/unit/cnn_network/parameters.h +++ b/inference-engine/tests/unit/cnn_network/parameters.h @@ -214,13 +214,13 @@ class Parameters { } case ParametersValues::FLOAT_POSITIVE: { for (int j = 0; j < magicNumber; ++j) { - paramsValues.push_back(std::to_string(distFloatPositive(gen))); + paramsValues.push_back(to_string_c_locale(distFloatPositive(gen))); } break; } case ParametersValues::FLOAT_NEGATIVE: { for (int j = 0; j < magicNumber; ++j) { - paramsValues.push_back(std::to_string(distFloatNegative(gen))); + paramsValues.push_back(to_string_c_locale(distFloatNegative(gen))); } break; } diff --git a/inference-engine/tests/unit/cnn_network/shapes.h b/inference-engine/tests/unit/cnn_network/shapes.h index a02076918a5ca1..293f0c604251bb 100644 --- a/inference-engine/tests/unit/cnn_network/shapes.h +++ b/inference-engine/tests/unit/cnn_network/shapes.h @@ -26,6 +26,8 @@ struct Maps{ std::map> mapOfUnequalShapes { // Layer name, Correct num of input, Correct num of output + { "Convolution", {3, 1}}, + { "Deconvolution", {3, 1}}, { "Crop", {2, 1}}, { "DetectionOutput", {3, 1}}, { "Interp", {2, 1}} @@ -226,4 +228,4 @@ class LayersWithNIO : public Layers{ ~LayersWithNIO() override = default; }; -#endif // SHAPES_H \ No newline at end of file +#endif // SHAPES_H diff --git a/inference-engine/tests/unit/cnn_network/v2_format_parser_test.cpp b/inference-engine/tests/unit/cnn_network/v2_format_parser_test.cpp index d3ce6e95b7140b..77e379dadce3c5 100644 --- a/inference-engine/tests/unit/cnn_network/v2_format_parser_test.cpp +++ b/inference-engine/tests/unit/cnn_network/v2_format_parser_test.cpp @@ -524,9 +524,7 @@ TEST_F(V2FormatParserTest, parsesNumberOfLayersCorrectly) { string content = MAKE_ALEXNET_FOR_MEAN_TESTS_V2(); ASSERT_NO_FATAL_FAILURE(assertParseSucceed(content)); - IE_SUPPRESS_DEPRECATED_START - CNNNetwork network(net.get()); - IE_SUPPRESS_DEPRECATED_END + CNNNetwork network(net); ASSERT_EQ(network.layerCount(), LAYER_COUNT); } diff --git a/inference-engine/tests/unit/engines/gna/configuration_test.cpp b/inference-engine/tests/unit/engines/gna/configuration_test.cpp index 95ec605943908f..6f56466fd1a94f 100644 --- a/inference-engine/tests/unit/engines/gna/configuration_test.cpp +++ b/inference-engine/tests/unit/engines/gna/configuration_test.cpp @@ -29,42 +29,10 @@ TEST_F(GNAConfigTest, reportAnErrorIfConfigNotFound) { {TargetDevice :: eCPU, {Precision::FP32}}}); EXPECT_CALL(net, getPrecision()).WillRepeatedly(Return(Precision::FP32)); - EXPECT_CALL(net, getTargetDevice()).WillRepeatedly(Return(TargetDevice::eGNA)); ASSERT_ANY_THROW(c.find_configuration(net)); } -TEST_F(GNAConfigTest, canFindConfiguration) { - - Config c ({{TargetDevice :: eGNA, {Precision::I16}}, - {TargetDevice :: eCPU, {Precision::FP32}}}); - - EXPECT_CALL(net, getPrecision()).WillRepeatedly(Return(Precision::FP32)); - EXPECT_CALL(net, getTargetDevice()).WillRepeatedly(Return(TargetDevice::eCPU)); - - auto match = c.find_configuration(net); - - EXPECT_EQ(match.device, TargetDevice::eCPU); - auto matchNetPrec = std::find(match.networkPrec.begin(), match.networkPrec.end(), Precision::FP32) != match.networkPrec.end(); - EXPECT_EQ(matchNetPrec, true); -} - -TEST_F(GNAConfigTest, canPassTroughNetworkAfterFindConfiguration) { - - Config c ({{TargetDevice :: eGNA, {Precision::I16}}, - {TargetDevice :: eCPU, {Precision::FP32}}}); - - EXPECT_CALL(net, getPrecision()).WillRepeatedly(Return(Precision::FP32)); - EXPECT_CALL(net, getTargetDevice()).WillRepeatedly(Return(TargetDevice::eCPU)); - - auto match = c.find_configuration(net); - - auto net2 = match.convert(net); - - EXPECT_EQ(net2->getTargetDevice(), TargetDevice::eCPU); - EXPECT_EQ(net2->getPrecision(), Precision::FP32); -} - TEST_F(GNAConfigTest, canNotMatchWithDefaultDevice) { Config c ({{TargetDevice :: eGNA, {Precision::I16}}, @@ -73,7 +41,6 @@ TEST_F(GNAConfigTest, canNotMatchWithDefaultDevice) { c.setDefaultDevice(TargetDevice::eGNA); EXPECT_CALL(net, getPrecision()).WillRepeatedly(Return(Precision::FP32)); - EXPECT_CALL(net, getTargetDevice()).WillRepeatedly(Return(TargetDevice::eDefault)); EXPECT_ANY_THROW(c.find_configuration(net).convert(net)); } @@ -86,12 +53,6 @@ TEST_F(GNAConfigTest, canMatchWithDefaultDevice) { c.setDefaultDevice(TargetDevice::eGNA); EXPECT_CALL(net, getPrecision()).WillRepeatedly(Return(Precision::I16)); - EXPECT_CALL(net, getTargetDevice()).WillRepeatedly(Return(TargetDevice::eDefault)); - - auto net2 = c.find_configuration(net).convert(net); - - EXPECT_EQ(net2->getTargetDevice(), TargetDevice::eDefault); - EXPECT_EQ(net2->getPrecision(), Precision::I16); } TEST_F(GNAConfigTest, canMatchWith1AsyncThread) { diff --git a/inference-engine/tests/unit/engines/gna/fp32_non_quantized_tests.cpp b/inference-engine/tests/unit/engines/gna/fp32_non_quantized_tests.cpp index bb0187238ef809..4a7caab4bdb5b2 100644 --- a/inference-engine/tests/unit/engines/gna/fp32_non_quantized_tests.cpp +++ b/inference-engine/tests/unit/engines/gna/fp32_non_quantized_tests.cpp @@ -397,3 +397,214 @@ TEST_F(FP32NonQuantizedTest, DISABLED_SplitToConcatThroughScaleShiftPropagateFor .called_with_input(input_data).equals_to(expected_result); } +TEST_F(FP32NonQuantizedTest, SplitToConcatWith2InputsNotAlignedNoFC) { + std::vector input_data = getRangeInput(20); + assert_that().onInferModel(SplitToConcatWith2InputsNotAlignedNoFC()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, DISABLED_SplitToConcatWith2By50InputsNotAlignedNoFC) { + std::vector input_data = getRangeInput(100); + assert_that().onInferModel(SplitToConcatWith2By50InputsNotAlignedNoFC()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, DISABLED_SplitToConcatWith2By50InputsNotAlignedNoFCWithInCopyWithOutCopy) { + std::vector input_data = getRangeInput(100); + assert_that().onInferModel(SplitToConcatWith2By50InputsNotAlignedNoFCWithInCopyWithOutCopy()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith3InputsNotAlignedNoFC) { + std::vector input_data = getRangeInput(30); + assert_that().onInferModel(SplitToConcatWith3InputsNotAlignedNoFC()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith4InputsNotAlignedNoFC) { + std::vector input_data = getRangeInput(40); + assert_that().onInferModel(SplitToConcatWith4InputsNotAlignedNoFC()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith4InputsNotAlignedNoFCWithOutCopy) { + std::vector input_data = getRangeInput(40); + assert_that().onInferModel(SplitToConcatWith4InputsNotAlignedNoFCWithOutCopy()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, DISABLED_SplitToConcatWith10InputsNotAlignedNoFC) { + std::vector input_data = getRangeInput(100); + assert_that().onInferModel(SplitToConcatWith10InputsNotAlignedNoFC()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, DISABLED_SplitToConcatWith10InputsNotAlignedNoFCWithOutCopy) { + std::vector input_data = getRangeInput(100); + assert_that().onInferModel(SplitToConcatWith10InputsNotAlignedNoFCWithOutCopy()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith10By1InputsNotAlignedNoFCWithOutCopy) { + std::vector input_data = getRangeInput(10); + assert_that().onInferModel(SplitToConcatWith10By1InputsNotAlignedNoFCWithOutCopy()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith2InputsNotAlignedWithFC) { + std::vector input_data = getRangeInput(20); + std::vector expected_result(10, 211.0f); + assert_that().onInferModel(SplitToConcatWith2InputsNotAlignedWithFC()) + .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(expected_result); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith3InputsNotAlignedWithFC) { + std::vector input_data = getRangeInput(30); + std::vector expected_result(10, 466.0f); + assert_that().onInferModel(SplitToConcatWith3InputsNotAlignedWithFC()) + .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(expected_result); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith3By512InputsWithOutCopy) { + std::vector input_data = getRangeInput(1536); + assert_that().onInferModel(SplitToConcatWith3By512InputsWithOutCopy()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, DISABLED_SplitToConcatWith10InputsNotAlignedWithFC) { + std::vector input_data = getRangeInput(100); + std::vector expected_result(10, 5051.0f); + assert_that().onInferModel(SplitToConcatWith10InputsNotAlignedWithFC()) + .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(expected_result); +} + +TEST_F(FP32NonQuantizedTest, DISABLED_SplitToConcatWith2InputsAlignedNoFC) { + std::vector input_data = getRangeInput(64); + assert_that().onInferModel(SplitToConcatWith2InputsAlignedNoFC()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, DISABLED_SplitToConcatWith2By64InputsAlignedNoFC) { + std::vector input_data = getRangeInput(128); + assert_that().onInferModel(SplitToConcatWith2By64InputsAlignedNoFC()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith2By64InputsAlignedNoFCWithOutCopy) { + std::vector input_data = getRangeInput(128); + assert_that().onInferModel(SplitToConcatWith2By64InputsAlignedNoFCWithOutCopy()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith2InputsAlignedNoFCWithInCopyWithOutCopy) { + std::vector input_data = getRangeInput(64); + assert_that().onInferModel(SplitToConcatWith2InputsAlignedNoFCWithInCopyWithOutCopy()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, DISABLED_SplitToConcatWith3InputsAlignedNoFC) { + std::vector input_data = getRangeInput(96); + assert_that().onInferModel(SplitToConcatWith3InputsAlignedNoFC()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith3InputsAlignedNoFCWithInCopyWithOutCopy) { + std::vector input_data = getRangeInput(96); + assert_that().onInferModel(SplitToConcatWith3InputsAlignedNoFCWithInCopyWithOutCopy()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, DISABLED_SplitToConcatWith10InputsAlignedNoFC) { + std::vector input_data = getRangeInput(320); + assert_that().onInferModel(SplitToConcatWith10InputsAlignedNoFC()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith10InputsAlignedNoFCWithInCopyWithOutCopy) { + std::vector input_data = getRangeInput(320); + assert_that().onInferModel(SplitToConcatWith10InputsAlignedNoFCWithInCopyWithOutCopy()) + .inNotCompactMode().gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(input_data); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith2InputsAlignedWithFC) { + std::vector input_data = getRangeInput(64); + std::vector expected_result(32, 2081.0f); + assert_that().onInferModel(SplitToConcatWith2InputsAlignedWithFC()) + .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(expected_result); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith2InputsAlignedWithFCWithInCopy) { + std::vector input_data = getRangeInput(64); + std::vector expected_result(32, 2081.0f); + assert_that().onInferModel(SplitToConcatWith2InputsAlignedWithFCWithInCopy()) + .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(expected_result); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith3InputsAlignedWithFC) { + std::vector input_data = getRangeInput(96); + std::vector expected_result(32, 4657.0f); + assert_that().onInferModel(SplitToConcatWith3InputsAlignedWithFC()) + .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(expected_result); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith3InputsAlignedWithFCWithInCopy) { + std::vector input_data = getRangeInput(96); + std::vector expected_result(32, 4657.0f); + assert_that().onInferModel(SplitToConcatWith3InputsAlignedWithFCWithInCopy()) + .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(expected_result); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith10InputsAlignedWithFC) { + std::vector input_data = getRangeInput(320); + std::vector expected_result(32, 51361.0f); + assert_that().onInferModel(SplitToConcatWith10InputsAlignedWithFC()) + .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(expected_result); +} + +TEST_F(FP32NonQuantizedTest, SplitToConcatWith10InputsAlignedWithFCWithInCopy) { + std::vector input_data = getRangeInput(320); + std::vector expected_result(32, 51361.0f); + assert_that().onInferModel(SplitToConcatWith10InputsAlignedWithFCWithInCopy()) + .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU() + .called_with_input(input_data).equals_to(expected_result); +} + +TEST_F(FP32NonQuantizedTest, ReshapeConvolutionLessThan48Filters) { + std::vector input_data(800, 1.f); + std::vector expected_result(1600, 8.f); + + assert_that().onInferModel(ReshapeConvolutionLessThan48Filters()) + .inNotCompactMode() + .withWeigthsPattern({1}) + .gna() + .propagate_forward() + .onCPU() + .called_with_input(input_data) + .equals_to(expected_result); +} diff --git a/inference-engine/tests/unit/engines/gna/gna_cppwraper_test.cpp b/inference-engine/tests/unit/engines/gna/gna_cppwraper_test.cpp index 3ff8aa5d80db9f..a0321fd3cc85b8 100644 --- a/inference-engine/tests/unit/engines/gna/gna_cppwraper_test.cpp +++ b/inference-engine/tests/unit/engines/gna/gna_cppwraper_test.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#ifndef _WIN32 +#if !defined _WIN32 || !defined(__INTEL_COMPILER) #include #endif #include "gna_api_wrapper.hpp" diff --git a/inference-engine/tests/unit/engines/gna/gna_graph_aot_test.cpp b/inference-engine/tests/unit/engines/gna/gna_graph_aot_test.cpp index c64d0682720a75..1bb166d1ce475f 100644 --- a/inference-engine/tests/unit/engines/gna/gna_graph_aot_test.cpp +++ b/inference-engine/tests/unit/engines/gna/gna_graph_aot_test.cpp @@ -30,7 +30,7 @@ class GNAAOTTests : public GNATest { } }; -TEST_F(GNAAOTTests, AffineWith2AffineOutputs_canbe_export_imported) { +TEST_F(GNAAOTTests, DISABLED_AffineWith2AffineOutputs_canbe_export_imported) { const std::string X = registerFileForRemove("unit_tests.bin"); @@ -44,7 +44,7 @@ TEST_F(GNAAOTTests, AffineWith2AffineOutputs_canbe_export_imported) { } -TEST_F(GNAAOTTests, AffineWith2AffineOutputs_canbe_imported_verify_structure) { +TEST_F(GNAAOTTests, DISABLED_AffineWith2AffineOutputs_canbe_imported_verify_structure) { auto & nnet_type = storage(); diff --git a/inference-engine/tests/unit/engines/gna/gna_matcher.cpp b/inference-engine/tests/unit/engines/gna/gna_matcher.cpp index e9eeba7fa1fb78..c912935cc4f9d1 100644 --- a/inference-engine/tests/unit/engines/gna/gna_matcher.cpp +++ b/inference-engine/tests/unit/engines/gna/gna_matcher.cpp @@ -156,7 +156,6 @@ void GNAPropagateMatcher :: match() { } net_reader.SetWeights(weights); - net_reader.getNetwork().setTargetDevice(_env.target_device); if (_env.cb) { auto network = net_reader.getNetwork(); @@ -256,7 +255,7 @@ void GNAPropagateMatcher :: match() { std::unique_ptr combined(new NNetComponentMatcher()); for (auto & matchWhat : _env.whatToMatch) { - switch(matchWhat) { + switch(matchWhat.type) { case GnaPluginTestEnvironment::matchPrecision : combined->add(new NNetPrecisionMatcher(_env.nnet_precision, INTEL_AFFINE)); break; @@ -265,13 +264,13 @@ void GNAPropagateMatcher :: match() { .WillOnce(Return(GNA_NOERROR)); break; case GnaPluginTestEnvironment::matchPwlInserted : - combined->add(new PWLMatcher(_env.matchInserted, _env.matchQuantity, _env.pwlsToMatchWith)); + combined->add(new PWLMatcher(_env.matchInserted, matchWhat.matchQuantity, _env.pwlsToMatchWith)); break; case GnaPluginTestEnvironment::matchConvInserted: - combined->add(new ConvoluionLayerMatcher(_env.matchInserted, _env.matchQuantity)); + combined->add(new ConvoluionLayerMatcher(_env.matchInserted, matchWhat.matchQuantity)); break; case GnaPluginTestEnvironment::matchMaxPoolingInserted: - combined->add(new PoolingLayerMatcher(_env.matchInserted, _env.matchQuantity, true)); + combined->add(new PoolingLayerMatcher(_env.matchInserted, matchWhat.matchQuantity, true)); break; case GnaPluginTestEnvironment::matchPwlQuantizeMetrics : combined->add(new PWLQuantizationMetricsMatcher(_env.type, @@ -279,10 +278,10 @@ void GNAPropagateMatcher :: match() { _env.quantization_segments_threshold)); break; case GnaPluginTestEnvironment::matchCopyInserted : - combined->add(new CopyLayerMatcher(_env.matchInserted, _env.matchQuantity)); + combined->add(new CopyLayerMatcher(_env.matchInserted, matchWhat.matchQuantity)); break; case GnaPluginTestEnvironment::matchDiagonalInserted : - combined->add(new DiagLayerMatcher(_env.matchInserted, _env.matchQuantity)); + combined->add(new DiagLayerMatcher(_env.matchInserted, matchWhat.matchQuantity)); break; case GnaPluginTestEnvironment::saveArgs : EXPECT_CALL(mockApi, GNAPropagateForward(_, _, _, _, _, _)) @@ -405,8 +404,6 @@ void GNAPluginAOTMatcher :: match() { TBlob output({ Precision::FP32, {1, 10}, Layout::NC }); output.allocate(); - net_reader.getNetwork().setTargetDevice(TargetDevice::eGNA); - if (_env.cb) { auto network = net_reader.getNetwork(); _env.cb(network); @@ -439,8 +436,6 @@ void GNADumpXNNMatcher::load(GNAPlugin & plugin) { GNATest::fillWeights(weights); net_reader.SetWeights(weights); - net_reader.getNetwork().setTargetDevice(TargetDevice::eGNA); - if (_env.cb) { auto network = net_reader.getNetwork(); _env.cb(network); @@ -516,8 +511,6 @@ void GNAQueryStateMatcher :: match() { GNATest::fillWeights(weights); net_reader.SetWeights(weights); - net_reader.getNetwork().setTargetDevice(TargetDevice::eGNA); - if (_env.cb) { auto network = net_reader.getNetwork(); _env.cb(network); diff --git a/inference-engine/tests/unit/engines/gna/gna_matcher.hpp b/inference-engine/tests/unit/engines/gna/gna_matcher.hpp index 3399937e98f763..4f0329a2c7ff2f 100644 --- a/inference-engine/tests/unit/engines/gna/gna_matcher.hpp +++ b/inference-engine/tests/unit/engines/gna/gna_matcher.hpp @@ -55,14 +55,19 @@ class GnaPluginTestEnvironment { matchAffineWeights, saveAffineWeights }; - std::vector whatToMatch; enum { kUnset = -1, kAnyNotNull= -2 }; + struct MatcherData { + MatchWhat type = matchNone; + int matchQuantity = kUnset; + }; + std::vector whatToMatch; + InferenceEngine::TargetDevice target_device = InferenceEngine::TargetDevice::eGNA; - int matchQuantity = kUnset; + int numberOfStates = kUnset; bool matchInserted = true; NnetPrecision nnet_precision; @@ -103,7 +108,7 @@ class GNATestConfigurability : public GNATestBase{ protected: bool needNextMatcher = true; GnaPluginTestEnvironment _env; - GnaPluginTestEnvironment::MatchWhat & getMatcher() { + GnaPluginTestEnvironment::MatcherData& getMatcher() { if (needNextMatcher) { needNextMatcher = false; _env.whatToMatch.push_back({}); @@ -197,7 +202,7 @@ class GNAPropagateMatcher : public GNATestConfigurability { */ GNAPropagateMatcher & filledWith(int16_t valueToFill) { _env.fillValue = valueToFill; - getMatcher() = GnaPluginTestEnvironment::fillOutputValues; + getMatcher().type = GnaPluginTestEnvironment::fillOutputValues; return *this; } @@ -238,14 +243,15 @@ class GNAPropagateMatcher : public GNATestConfigurability { GNAPropagateMatcher & once() { - IE_ASSERT(_env.matchPwlInserted && _env.pwlsToMatchWith.empty()); - _env.matchQuantity = 1; - return *this; + return times(1); } GNAPropagateMatcher & twice() { - IE_ASSERT(_env.matchPwlInserted && _env.pwlsToMatchWith.empty()); - _env.matchQuantity = 2; + return times(2); + } + + GNAPropagateMatcher & times(int n) { + getMatcher().matchQuantity = n; return *this; } @@ -255,24 +261,24 @@ class GNAPropagateMatcher : public GNATestConfigurability { GNAPropagateMatcher & exact_nnet_structure(intel_nnet_type_t * pNet) { - getMatcher() = GnaPluginTestEnvironment::exactNNetStructure; + getMatcher().type = GnaPluginTestEnvironment::exactNNetStructure; original_nnet = pNet; return *this; } GNAPropagateMatcher & pwl_inserted_into_nnet() { - getMatcher() = GnaPluginTestEnvironment::matchPwlInserted; + getMatcher().type = GnaPluginTestEnvironment::matchPwlInserted; return *this; } GNAPropagateMatcher & pwls_inserted_into_nnet(const std::vector &pwls) { - getMatcher() = GnaPluginTestEnvironment::matchPwlInserted; + getMatcher().type = GnaPluginTestEnvironment::matchPwlInserted; _env.pwlsToMatchWith = pwls; return *this; } GNAPropagateMatcher & max_pooling_inserted_into_nnet() { - getMatcher() = GnaPluginTestEnvironment::matchMaxPoolingInserted; + getMatcher().type = GnaPluginTestEnvironment::matchMaxPoolingInserted; return *this; } @@ -281,37 +287,37 @@ class GNAPropagateMatcher : public GNATestConfigurability { } GNAPropagateMatcher & convolution_inserted_into_nnet() { - getMatcher() = GnaPluginTestEnvironment::matchConvInserted; + getMatcher().type = GnaPluginTestEnvironment::matchConvInserted; return *this; } GNAPropagateMatcher & pwl_quantization_activation(uint32_t activation_type) { - getMatcher() = GnaPluginTestEnvironment::matchPwlQuantizeMetrics; + getMatcher().type = GnaPluginTestEnvironment::matchPwlQuantizeMetrics; _env.type = activation_type; return *this; } GNAPropagateMatcher & pwl_quantization_precision_threshold(float threshold) { - getMatcher() = GnaPluginTestEnvironment::matchPwlQuantizeMetrics; + getMatcher().type = GnaPluginTestEnvironment::matchPwlQuantizeMetrics; _env.quantization_presicion_threshold = threshold; return *this; } GNAPropagateMatcher & pwl_quantization_segments_threshold(uint16_t threshold) { - getMatcher() = GnaPluginTestEnvironment::matchPwlQuantizeMetrics; + getMatcher().type = GnaPluginTestEnvironment::matchPwlQuantizeMetrics; _env.quantization_segments_threshold = threshold; return *this; } GNAPropagateMatcher & diagonal_inserted_into_nnet() { - getMatcher() = GnaPluginTestEnvironment::matchDiagonalInserted; + getMatcher().type = GnaPluginTestEnvironment::matchDiagonalInserted; return *this; } GNAPropagateMatcher &preprocessed_input_data(std::vector input_init, std::vector input_processed, InferenceEngine::Precision inputPrecision) { - getMatcher() = GnaPluginTestEnvironment::matchInputData; + getMatcher().type = GnaPluginTestEnvironment::matchInputData; _env.input_processed = std::move(input_processed); _env.input_init["placeholder"] = std::move(input_init); _env.input_precision = inputPrecision; @@ -319,60 +325,60 @@ class GNAPropagateMatcher : public GNATestConfigurability { } GNAPropagateMatcher & copy_inserted_into_nnet() { - getMatcher() = GnaPluginTestEnvironment::matchCopyInserted; + getMatcher().type = GnaPluginTestEnvironment::matchCopyInserted; return *this; } GNAPropagateMatcher & affine_weights_transpozed(std::pair &&transpozedArgs) { - getMatcher() = GnaPluginTestEnvironment::saveAffineWeights; + getMatcher().type = GnaPluginTestEnvironment::saveAffineWeights; _env.transposedArgsForSaving = std::move(transpozedArgs); return *this; } GNAPropagateMatcher & affine_weights() { - getMatcher() = GnaPluginTestEnvironment::saveAffineWeights; + getMatcher().type = GnaPluginTestEnvironment::saveAffineWeights; return *this; } GNAPropagateMatcher & affine_weights_eq(std::vector & sourceWeights) { - getMatcher() = GnaPluginTestEnvironment::matchAffineWeights; + getMatcher().type = GnaPluginTestEnvironment::matchAffineWeights; _env.transposedData = &sourceWeights; return *this; } GNAPropagateMatcher & affine_weights_transposed(std::vector & sourceWeights, std::pair transposeData) { - getMatcher() = GnaPluginTestEnvironment::matchAffineWeightsTranspose; + getMatcher().type = GnaPluginTestEnvironment::matchAffineWeightsTranspose; _env.transposeArgs = transposeData; _env.transposedData = &sourceWeights; return *this; } GNAPropagateMatcher & nnet_input_precision(const InferenceEngine::Precision &precision) { - getMatcher() = GnaPluginTestEnvironment::matchPrecision; + getMatcher().type = GnaPluginTestEnvironment::matchPrecision; _env.nnet_precision.input_precision = precision; return *this; } GNAPropagateMatcher & nnet_ouput_precision(const InferenceEngine::Precision &precision) { - getMatcher() = GnaPluginTestEnvironment::matchPrecision; + getMatcher().type = GnaPluginTestEnvironment::matchPrecision; _env.nnet_precision.output_precision = precision; return *this; } GNAPropagateMatcher & nnet_weights_precision(const InferenceEngine::Precision &precision) { - getMatcher() = GnaPluginTestEnvironment::matchPrecision; + getMatcher().type = GnaPluginTestEnvironment::matchPrecision; _env.nnet_precision.weights_precision = precision; return *this; } GNAPropagateMatcher & nnet_biases_precision(const InferenceEngine::Precision &precision) { - getMatcher() = GnaPluginTestEnvironment::matchPrecision; + getMatcher().type = GnaPluginTestEnvironment::matchPrecision; _env.nnet_precision.biases_precision = precision; return *this; } GNAPropagateMatcher & proc_type(uint32_t proc_type) { - getMatcher() = GnaPluginTestEnvironment::matchProcType; + getMatcher().type = GnaPluginTestEnvironment::matchProcType; _env.proc_type = proc_type; return * this; } @@ -392,6 +398,7 @@ class GNAPropagateMatcher : public GNATestConfigurability { _env.target_device = InferenceEngine::TargetDevice::eCPU; return *this; } + protected: void match(); intel_nnet_type_t * original_nnet = nullptr; @@ -528,7 +535,7 @@ class GNATest : public ::testing::Test, public GNATestConfigurability return *this; } GNATest & save_args() { - getMatcher() = GnaPluginTestEnvironment::saveArgs; + getMatcher().type = GnaPluginTestEnvironment::saveArgs; return *this; } GNATest & save() { @@ -635,4 +642,15 @@ class GNATest : public ::testing::Test, public GNATestConfigurability } } } + + protected: + std::vector getRangeInput(std::size_t max) { + std::vector result(max); + float value = 1.0f; + for(std::size_t i = 0; i < result.size(); i++) { + result[i] = value; + value++; + } + return result; + } }; diff --git a/inference-engine/tests/unit/engines/gna/i16_quantisation_test.cpp b/inference-engine/tests/unit/engines/gna/i16_quantisation_test.cpp index 207c782169f8d4..5708d275d096e7 100644 --- a/inference-engine/tests/unit/engines/gna/i16_quantisation_test.cpp +++ b/inference-engine/tests/unit/engines/gna/i16_quantisation_test.cpp @@ -272,10 +272,10 @@ TEST_F(I16QuantisationTest, ClampFollowedByTanh_ResultInDiagonalInsertion) { .gna().propagate_forward().called_with().diagonal_inserted_into_nnet().twice(); } -TEST_F(I16QuantisationTest, EltwiseWithMemoryAndActivationInput_ResultInDiagonalInsertion) { +TEST_F(I16QuantisationTest, EltwiseWithMemoryAndActivationInput_ResultInTwoDiagonalsInsertion) { assert_that().onInferModel(eltwiseWithMemoryAndActivationInputModel()) .inNotCompactMode().withGNAConfig(GNA_CONFIG_KEY(SCALE_FACTOR), 1.0f) - .gna().propagate_forward().called_with().diagonal_inserted_into_nnet().once(); + .gna().propagate_forward().called_with().diagonal_inserted_into_nnet().twice(); } TEST_F(I16QuantisationTest, AffineWith2AffineOutputs_ResultInOnlyOneIdentityInsertion) { @@ -362,7 +362,9 @@ TEST_F(I16QuantisationTest, MultipleActivationsAfterAffine_ResultInMultipleDiago // extra identity inserted for affine assert_that().onInferModel(AffineWithReluSigmoid()) .inNotCompactMode().withGNAConfig(GNA_CONFIG_KEY(SCALE_FACTOR), 1.0f) - .gna().propagate_forward().called_with().pwls_inserted_into_nnet({kActRelu, kActSigmoid}); + .gna().propagate_forward().called_with() + // 1 diag for second activation, 1 for eltwise + .pwls_inserted_into_nnet({kActRelu, kActSigmoid}).diagonal_inserted_into_nnet().times(3); } // TODO: build a regression test on top of it using real quantisation accuracy checking @@ -411,3 +413,9 @@ TEST_F(I16QuantisationTest, PowerWithScaleFactorPropagateForward) { .inNotCompactMode().withGNAConfig(GNA_CONFIG_KEY(SCALE_FACTOR), 1.0f) .gna().propagate_forward().called_with().pwls_inserted_into_nnet({kActIdentity}).And().diagonal_inserted_into_nnet(); } + +TEST_F(I16QuantisationTest, ConcatWithDifferentInputScaleFactorsPropagateForward) { + assert_that().onInferModel(ConcatWithDiffScaleFactor()) + .inNotCompactMode().withGNAConfig(GNA_CONFIG_KEY(SCALE_FACTOR), 1.0f) + .gna().propagate_forward().called_with().pwls_inserted_into_nnet({kActIdentity}); +} diff --git a/inference-engine/tests/unit/engines/gna/matchers/diag_matcher.hpp b/inference-engine/tests/unit/engines/gna/matchers/diag_matcher.hpp index b39813df316fbd..2c178657fe05c2 100644 --- a/inference-engine/tests/unit/engines/gna/matchers/diag_matcher.hpp +++ b/inference-engine/tests/unit/engines/gna/matchers/diag_matcher.hpp @@ -9,12 +9,14 @@ class DiagLayerMatcher : public ::testing::MatcherInterface { bool matchInserted; - int matchQuantity; + int matchQuantity; + mutable int actualQuantity; public: DiagLayerMatcher(bool matchInserted, int matchQuantity) : matchInserted(matchInserted), matchQuantity(matchQuantity) {} bool MatchAndExplain(const intel_nnet_type_t *foo, ::testing::MatchResultListener *listener) const override { if (foo == nullptr) return false; + actualQuantity = 0; for(int i = 0; i < foo->nLayers; i++) { if (foo->pLayers[i].nLayerKind != INTEL_AFFINE_DIAGONAL) continue; // diagonal layer has to have 1 for weights and 0 for biases @@ -45,13 +47,25 @@ class DiagLayerMatcher : public ::testing::MatcherInterface 0 + if (matchQuantity == -1) { + if (actualQuantity > 0) + return matchInserted; + else + return !matchInserted; } - return !matchInserted; + if (actualQuantity == matchQuantity) + return matchInserted; + else + return !matchInserted; + }; void DescribeTo(::std::ostream *os) const override { - *os << "should "<< (matchInserted ? "" : "not ") << "have Identity Diagonal Primitive primitive as part of nnet structure"; + *os << "should "<< (matchInserted ? "" : "not ") << "have " + << (matchQuantity == -1 ? "any" : std::to_string(matchQuantity)) + << " Identity Diagonal Primitive primitive as part of nnet structure, but was " << actualQuantity; } }; diff --git a/inference-engine/tests/unit/engines/gna/test_irs.cpp b/inference-engine/tests/unit/engines/gna/test_irs.cpp index 4ede25fcaa8b1b..18e8554a20b906 100644 --- a/inference-engine/tests/unit/engines/gna/test_irs.cpp +++ b/inference-engine/tests/unit/engines/gna/test_irs.cpp @@ -3509,7 +3509,7 @@ std::string AffineWithReluSigmoid() { - + 1 @@ -3777,6 +3777,8 @@ std::string LSTMCellOnlyModel() { )V0G0N"; }; + + std::string TIModelWithLSTMCell1() { return R"V0G0N( @@ -5764,6 +5766,2988 @@ std::string SplitToConcatThroughScaleShift() { )V0G0N"; } +std::string ConcatWithDiffScaleFactor() { + return R"V0G0N( + + + + + + 1 + 20 + + + + + + + + 1 + 20 + + + + + 1 + 10 + + + 1 + 10 + + + + + + + + 1 + 10 + + + + + 1 + 10 + + + + + + + + 1 + 10 + + + + + 1 + 10 + + + + + + + 1 + 10 + + + 1 + 10 + + + + + 1 + 20 + + + + + + + + + + + + +)V0G0N"; + } + + std::string SplitToConcatWith2InputsNotAlignedNoFC () { + return R"V0G0N( + + + + + + + 1 + 20 + + + + + + + + 1 + 20 + + + + + 1 + 10 + + + 1 + 10 + + + + + + + + 1 + 10 + + + 1 + 10 + + + + + 1 + 20 + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith2By50InputsNotAlignedNoFC () { + return R"V0G0N( + + + + + + + 1 + 100 + + + + + + + + 1 + 100 + + + + + 1 + 50 + + + 1 + 50 + + + + + + + + 1 + 50 + + + 1 + 50 + + + + + 1 + 100 + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith2By50InputsNotAlignedNoFCWithInCopyWithOutCopy () { + return R"V0G0N( + + + + + + + 1 + 100 + + + + + + + 1 + 100 + + + + + 1 + 100 + + + + + + + + 1 + 100 + + + + + 1 + 50 + + + 1 + 50 + + + + + + + + 1 + 50 + + + 1 + 50 + + + + + 1 + 100 + + + + + + + 1 + 100 + + + + + 1 + 100 + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith2By64InputsAlignedNoFC () { + return R"V0G0N( + + + + + + + 1 + 128 + + + + + + + + 1 + 128 + + + + + 1 + 64 + + + 1 + 64 + + + + + + + + 1 + 64 + + + 1 + 64 + + + + + 1 + 128 + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith2By64InputsAlignedNoFCWithOutCopy () { + return R"V0G0N( + + + + + + + 1 + 128 + + + + + + + + 1 + 128 + + + + + 1 + 64 + + + 1 + 64 + + + + + + + + 1 + 64 + + + 1 + 64 + + + + + 1 + 128 + + + + + + + 1 + 128 + + + + + 1 + 128 + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith2InputsAlignedNoFC () { + return R"V0G0N( + + + + + + + 1 + 64 + + + + + + + + 1 + 64 + + + + + 1 + 32 + + + 1 + 32 + + + + + + + + 1 + 32 + + + 1 + 32 + + + + + 1 + 64 + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith2InputsAlignedNoFCWithInCopyWithOutCopy () { + return R"V0G0N( + + + + + + + 1 + 64 + + + + + + + 1 + 64 + + + + + 1 + 64 + + + + + + + + 1 + 64 + + + + + 1 + 32 + + + 1 + 32 + + + + + + + + 1 + 32 + + + 1 + 32 + + + + + 1 + 64 + + + + + + + 1 + 64 + + + + + 1 + 64 + + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith2InputsNotAlignedWithFC () { + return R"V0G0N( + + + + + + + 1 + 20 + + + + + + + + 1 + 20 + + + + + 1 + 10 + + + 1 + 10 + + + + + + + + 1 + 10 + + + 1 + 10 + + + + + 1 + 20 + + + + + + + + 1 + 20 + + + + + 1 + 10 + + + + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith2InputsAlignedWithFC () { + return R"V0G0N( + + + + + + + 1 + 64 + + + + + + + + 1 + 64 + + + + + 1 + 32 + + + 1 + 32 + + + + + + + + 1 + 32 + + + 1 + 32 + + + + + 1 + 64 + + + + + + + + 1 + 64 + + + + + 1 + 32 + + + + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith2InputsAlignedWithFCWithInCopy () { + return R"V0G0N( + + + + + + + 1 + 64 + + + + + + + 1 + 64 + + + + + 1 + 64 + + + + + + + + 1 + 64 + + + + + 1 + 32 + + + 1 + 32 + + + + + + + + 1 + 32 + + + 1 + 32 + + + + + 1 + 64 + + + + + + + + 1 + 64 + + + + + 1 + 32 + + + + + + + + + + + + + + + + + + + + )V0G0N"; + } + +std::string SplitToConcatWith3InputsNotAlignedNoFC () { + return R"V0G0N( + + + + + + + 1 + 30 + + + + + + + + 1 + 30 + + + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + + + + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + + + 1 + 30 + + + + + + + + + + + + + + )V0G0N"; +} + +std::string SplitToConcatWith3InputsNotAlignedWithFC () { + return R"V0G0N( + + + + + + + 1 + 30 + + + + + + + + 1 + 30 + + + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + + + + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + + + 1 + 30 + + + + + + + + 1 + 30 + + + + + 1 + 10 + + + + + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith3InputsAlignedNoFC () { + return R"V0G0N( + + + + + + + 1 + 96 + + + + + + + + 1 + 96 + + + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + + + + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + + + 1 + 96 + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith3InputsAlignedNoFCWithInCopyWithOutCopy () { + return R"V0G0N( + + + + + + + 1 + 96 + + + + + + + 1 + 96 + + + + + 1 + 96 + + + + + + + + 1 + 96 + + + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + + + + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + + + 1 + 96 + + + + + + + 1 + 96 + + + + + 1 + 96 + + + + + + + + + + + + + + + + + + )V0G0N"; +} + + std::string SplitToConcatWith3InputsAlignedWithFC () { + return R"V0G0N( + + + + + + + 1 + 96 + + + + + + + + 1 + 96 + + + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + + + + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + + + 1 + 96 + + + + + + + + 1 + 96 + + + + + 1 + 32 + + + + + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith3InputsAlignedWithFCWithInCopy () { + return R"V0G0N( + + + + + + + 1 + 96 + + + + + + + 1 + 96 + + + + + 1 + 96 + + + + + + + + 1 + 96 + + + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + + + + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + + + 1 + 96 + + + + + + + + 1 + 96 + + + + + 1 + 32 + + + + + + + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith4InputsNotAlignedNoFC () { + return R"V0G0N( + + + + + + + 1 + 40 + + + + + + + + 1 + 40 + + + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + + + + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + + + 1 + 40 + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith4InputsNotAlignedNoFCWithOutCopy () { + return R"V0G0N( + + + + + + + 1 + 40 + + + + + + + + 1 + 40 + + + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + + + + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + + + 1 + 40 + + + + + + + 1 + 40 + + + + + 1 + 40 + + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith10InputsNotAlignedNoFC () { + return R"V0G0N( + + + + + + + 1 + 100 + + + + + + + + 1 + 100 + + + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + + + + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + + + 1 + 100 + + + + + + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith10InputsNotAlignedNoFCWithOutCopy () { + return R"V0G0N( + + + + + + + 1 + 100 + + + + + + + + 1 + 100 + + + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + + + + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + + + 1 + 100 + + + + + + + 1 + 100 + + + + + 1 + 100 + + + + + + + + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith10By1InputsNotAlignedNoFCWithOutCopy () { + return R"V0G0N( + + + + + + + 1 + 10 + + + + + + + + 1 + 10 + + + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + + + + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + + + 1 + 10 + + + + + + + 1 + 10 + + + + + 1 + 10 + + + + + + + + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith10InputsAlignedNoFC () { + return R"V0G0N( + + + + + + + 1 + 320 + + + + + + + + 1 + 320 + + + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + + + + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + + + 1 + 320 + + + + + + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith10InputsAlignedNoFCWithInCopyWithOutCopy () { + return R"V0G0N( + + + + + + + 1 + 320 + + + + + + + 1 + 320 + + + + + 1 + 320 + + + + + + + + 1 + 320 + + + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + + + + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + + + 1 + 320 + + + + + + + 1 + 320 + + + + + 1 + 320 + + + + + + + + + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith10InputsNotAlignedWithFC () { + return R"V0G0N( + + + + + + + 1 + 100 + + + + + + + + 1 + 100 + + + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + + + + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + + + 1 + 100 + + + + + + + + 1 + 100 + + + + + 1 + 10 + + + + + + + + + + + + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith10InputsAlignedWithFC () { + return R"V0G0N( + + + + + + + 1 + 320 + + + + + + + + 1 + 320 + + + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + + + + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + + + 1 + 320 + + + + + + + + 1 + 320 + + + + + 1 + 32 + + + + + + + + + + + + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith10InputsAlignedWithFCWithInCopy () { + return R"V0G0N( + + + + + + + 1 + 320 + + + + + + + 1 + 320 + + + + + 1 + 320 + + + + + + + + 1 + 320 + + + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + + + + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + 1 + 32 + + + + + 1 + 320 + + + + + + + + 1 + 320 + + + + + 1 + 32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string SplitToConcatWith3By512InputsWithOutCopy () { + return R"V0G0N( + + + + + + + 1 + 1536 + + + + + + + + 1 + 1536 + + + + + 1 + 512 + + + 1 + 512 + + + 1 + 512 + + + + + + + + 1 + 512 + + + 1 + 512 + + + 1 + 512 + + + + + 1 + 1536 + + + + + + + 1 + 1536 + + + + + 1 + 1536 + + + + + + + + + + + + + + + )V0G0N"; + } + + std::string ReshapeConvolutionLessThan48Filters() { + return R"V0G0N( + + + + + + + 1 + 800 + + + + + + + + 1 + 800 + + + + + 1 + 4 + 1 + 200 + + + + + + + + 1 + 4 + 1 + 200 + + + + + 1 + 16 + 1 + 100 + + + + + + + + + + + 1 + 16 + 1 + 100 + + + + + 1 + 1600 + + + + + + + 1 + 1600 + + + + + 1 + 1600 + + + + + + + + + + + + )V0G0N"; + } } // namespace GNATestIRs diff --git a/inference-engine/tests/unit/engines/gna/test_irs.hpp b/inference-engine/tests/unit/engines/gna/test_irs.hpp index 5449607968c5b2..8383419bc2af5e 100644 --- a/inference-engine/tests/unit/engines/gna/test_irs.hpp +++ b/inference-engine/tests/unit/engines/gna/test_irs.hpp @@ -67,4 +67,39 @@ std::string InputSplitConcatReshapeModelUnaligned(); std::string LSTMCellOnlyModelUnaligned(); std::string SplitToConcatThroughScaleShift(); std::string PowerWithScaleFactor1(); +std::string ConcatWithDiffScaleFactor(); + +std::string SplitToConcatWith2InputsNotAlignedNoFC(); +std::string SplitToConcatWith2InputsAlignedNoFC(); +std::string SplitToConcatWith2InputsAlignedNoFCWithInCopyWithOutCopy(); +std::string SplitToConcatWith2InputsNotAlignedWithFC(); +std::string SplitToConcatWith2InputsAlignedWithFC(); +std::string SplitToConcatWith2InputsAlignedWithFCWithInCopy(); + +std::string SplitToConcatWith3InputsNotAlignedNoFC(); +std::string SplitToConcatWith3InputsAlignedNoFC(); +std::string SplitToConcatWith3InputsAlignedNoFCWithInCopyWithOutCopy(); +std::string SplitToConcatWith3InputsNotAlignedWithFC(); +std::string SplitToConcatWith3InputsAlignedWithFC(); +std::string SplitToConcatWith3InputsAlignedWithFCWithInCopy(); + +std::string SplitToConcatWith4InputsNotAlignedNoFC(); +std::string SplitToConcatWith4InputsNotAlignedNoFCWithOutCopy(); + +std::string SplitToConcatWith10InputsNotAlignedNoFC(); +std::string SplitToConcatWith10InputsNotAlignedNoFCWithOutCopy(); +std::string SplitToConcatWith10InputsAlignedNoFC(); +std::string SplitToConcatWith10InputsAlignedNoFCWithInCopyWithOutCopy(); +std::string SplitToConcatWith10InputsNotAlignedWithFC(); +std::string SplitToConcatWith10InputsAlignedWithFC(); +std::string SplitToConcatWith10InputsAlignedWithFCWithInCopy(); + +std::string SplitToConcatWith10By1InputsNotAlignedNoFCWithOutCopy(); +std::string SplitToConcatWith2By50InputsNotAlignedNoFC(); +std::string SplitToConcatWith2By50InputsNotAlignedNoFCWithInCopyWithOutCopy(); +std::string SplitToConcatWith2By64InputsAlignedNoFC(); +std::string SplitToConcatWith2By64InputsAlignedNoFCWithOutCopy(); +std::string SplitToConcatWith3By512InputsWithOutCopy(); + +std::string ReshapeConvolutionLessThan48Filters(); } // namespace GNATestIRs diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/broadcast_tests.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/broadcast_tests.cpp index c1e0985bbe7628..21cb455984a376 100644 --- a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/broadcast_tests.cpp +++ b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/broadcast_tests.cpp @@ -19,6 +19,7 @@ using namespace std; using namespace mkldnn; struct broadcast_test_params { + std::string shape_precision; std::string precision; InferenceEngine::SizeVector in_shape; InferenceEngine::SizeVector out_shape; @@ -33,6 +34,11 @@ void ref_broadcast(InferenceEngine::TBlob &src, InferenceEngine::TBlob - + _DIM_SIZE_ @@ -119,10 +125,11 @@ class MKLDNNCPUExtBroadcastTests : public TestsCommon, public WithParamInterface std::string getModel(broadcast_test_params p) { std::string model = model_t; - std::string in_shape; + std::string in_shape = ""; std::string out_shape; REPLACE_WITH_STR(model, "_IIDXP_", p.precision); + REPLACE_WITH_STR(model, "_ISDXP_", p.shape_precision); for (size_t i = 0; i < p.in_shape.size(); i++) { in_shape += ""; in_shape += std::to_string(p.in_shape[i]) + "\n"; @@ -166,20 +173,31 @@ class MKLDNNCPUExtBroadcastTests : public TestsCommon, public WithParamInterface // Input Data InferenceEngine::Blob::Ptr dims; InferenceEngine::SizeVector vector_dim(1, p.out_shape.size()); - dims = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::I32, vector_dim, InferenceEngine::TensorDesc::getLayoutByDims(vector_dim) }); - dims->allocate(); - for (size_t i = 0; i < p.out_shape.size(); i++) { - static_cast(dims->buffer())[i] = static_cast(p.out_shape[i]); + if (p.shape_precision == "I32") { + dims = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::I32, vector_dim, InferenceEngine::TensorDesc::getLayoutByDims(vector_dim) }); + dims->allocate(); + for (size_t i = 0; i < p.out_shape.size(); i++) { + static_cast(dims->buffer())[i] = static_cast(p.out_shape[i]); + } + auto * dimsPtr = dynamic_cast*>(dims.get()); + if (dimsPtr == nullptr) + FAIL() << "Cannot cast blob to TBlob."; + } else if (p.shape_precision == "FP32") { + dims = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, vector_dim, InferenceEngine::TensorDesc::getLayoutByDims(vector_dim) }); + dims->allocate(); + for (size_t i = 0; i < p.out_shape.size(); i++) { + static_cast(dims->buffer())[i] = static_cast(p.out_shape[i]); + } + auto * dimsPtr = dynamic_cast*>(dims.get()); + if (dimsPtr == nullptr) + FAIL() << "Cannot cast blob to TBlob."; } - auto * dimsPtr = dynamic_cast*>(dims.get()); - if (dimsPtr == nullptr) - FAIL() << "Cannot cast blob to TBlob."; InferenceEngine::BlobMap srcs; InferenceEngine::Blob::Ptr src; std::pair item = *out.begin(); if (p.precision == "I32") { - src = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::I32, p.in_shape, InferenceEngine::TensorDesc::getLayoutByDims(p.in_shape) }); + src = InferenceEngine::make_shared_blob({InferenceEngine::Precision::I32, p.in_shape, InferenceEngine::TensorDesc::getLayoutByDims(p.in_shape)}); src->allocate(); for (size_t i = 0; i < src->size(); i++) static_cast(src->buffer())[i] = static_cast(i); @@ -207,9 +225,8 @@ class MKLDNNCPUExtBroadcastTests : public TestsCommon, public WithParamInterface if (dst_ref.data()[i] != (*output).data()[i]) FAIL() << "The difference between res_ptr[i] and ref_ptr[i]"; } - } - else if (p.precision == "FP32") { - src = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, p.in_shape, InferenceEngine::TensorDesc::getLayoutByDims(p.in_shape) }); + } else if (p.precision == "FP32") { + src = InferenceEngine::make_shared_blob({InferenceEngine::Precision::FP32, p.in_shape, InferenceEngine::TensorDesc::getLayoutByDims(p.in_shape)}); src->allocate(); fill_data_dbgval(src->buffer(), src->size()); auto * srcPtr = dynamic_cast*>(src.get()); @@ -249,17 +266,18 @@ TEST_P(MKLDNNCPUExtBroadcastTests, TestsBroadcast) {} INSTANTIATE_TEST_CASE_P( TestsBroadcast, MKLDNNCPUExtBroadcastTests, ::testing::Values( - // Params: precision, in_shape, out_shape - broadcast_test_params{ "I32", { 1 }, { 2, 3, 4 } }, - broadcast_test_params{ "I32", { 4, 1, 2 }, { 4, 2, 2 } }, - broadcast_test_params{ "I32", { 4, 2, 1 }, { 4, 2, 2 } }, - broadcast_test_params{ "I32", { 4, 2 }, { 2, 4, 2 } }, - broadcast_test_params{ "I32", { 4, 1, 1 }, { 4, 2, 1 } }, - broadcast_test_params{ "I32", { 2, 1, 3, 1 },{ 2, 2, 2, 3, 1 } }, - broadcast_test_params{"FP32", { 1 }, { 2, 3, 4 } }, - broadcast_test_params{"FP32", { 4, 1, 2 }, { 4, 2, 2 } }, - broadcast_test_params{"FP32", { 4, 2, 1 }, { 4, 2, 2 } }, - broadcast_test_params{"FP32", { 4, 2 }, { 2, 4, 2 } }, - broadcast_test_params{"FP32", { 4, 1, 1 }, { 4, 2, 1 } }, - broadcast_test_params{"FP32", { 2, 1, 3, 1 },{ 2, 2, 2, 3, 1 } } + // Params: shape_precision, precision, in_shape, out_shape + broadcast_test_params{ "I32", "I32",{},{ 2, 3, 4 } }, + broadcast_test_params{ "I32", "I32",{ 4, 1, 2 },{ 4, 2, 2 } }, + broadcast_test_params{ "I32", "I32",{ 4, 2, 1 },{ 4, 2, 2 } }, + broadcast_test_params{ "I32", "I32",{ 4, 2 },{ 2, 4, 2 } }, + broadcast_test_params{ "I32", "I32",{ 4, 1, 1 },{ 4, 2, 1 } }, + broadcast_test_params{ "I32", "I32",{ 2, 1, 3, 1 },{ 2, 2, 2, 3, 1 } }, + broadcast_test_params{ "I32","FP32",{},{ 2, 3, 4 } }, + broadcast_test_params{ "I32","FP32",{ 4, 1, 2 },{ 4, 2, 2 } }, + broadcast_test_params{ "I32","FP32",{ 4, 2, 1 },{ 4, 2, 2 } }, + broadcast_test_params{ "I32","FP32",{ 4, 2 },{ 2, 4, 2 } }, + broadcast_test_params{ "I32","FP32",{ 4, 1, 1 },{ 4, 2, 1 } }, + broadcast_test_params{ "I32","FP32", { 2, 1, 3, 1 },{ 2, 2, 2, 3, 1 } }, + broadcast_test_params{"FP32","FP32",{ 2, 1, 3, 1 },{ 2, 2, 2, 3, 1 } } )); diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/fake_layer.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/fake_layer.cpp index 8c2c36906c212f..fa0af69c715fc2 100644 --- a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/fake_layer.cpp +++ b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/fake_layer.cpp @@ -44,8 +44,8 @@ class FakeExtensions : public IExtension { void GetVersion(const Version *&versionInfo) const noexcept override { static Version ExtensionDescription = { - {2, 0}, // extension API version - "2.0", + {2, 1}, // extension API version + "2.1", "ie-cpu-ext" // extension description message }; diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/gather_tests.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/gather_tests.cpp index d92a4f28d1e0a4..4976106d3faa6f 100644 --- a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/gather_tests.cpp +++ b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/gather_tests.cpp @@ -21,8 +21,9 @@ using namespace mkldnn; struct gather_test_params { std::string inIdxPrecision; - InferenceEngine::SizeVector inIdx; InferenceEngine::SizeVector inDict; + InferenceEngine::SizeVector inIdx; + int axis; InferenceEngine::SizeVector out; @@ -40,34 +41,23 @@ void ref_gather(InferenceEngine::TBlob &srcIdx, InferenceEngine::TBlob dims = srcDct.getTensorDesc().getDims(); - std::vector dims_actual; - - // Remove redundant dimensions - for (size_t i = 0; i < dims.size(); i++) { - if (dims[i] > 1) { - for (size_t j = i; j < dims.size(); j++) - dims_actual.push_back(dims[j]); - break; - } - } + std::vector dictionary_dims = srcDct.getTensorDesc().getDims(); // Find number of dictionaries, index range and data length size_t numDictionaries = 1; for (i = 0; i < axis; i++) - numDictionaries *= dims_actual[i]; - size_t indexRange = dims_actual[axis]; + numDictionaries *= dictionary_dims[i]; + size_t indexRange = dictionary_dims[axis]; size_t dataLength = 1; - for (i = axis + 1; i < dims_actual.size(); i++) - dataLength *= dims_actual[i]; + for (i = axis + 1; i < dictionary_dims.size(); i++) + dataLength *= dictionary_dims[i]; // The gathering process for (i = 0; i < src_size; i++) { unsigned int idx = static_cast(src_dataIdx[i]); // Index clipping - if (idx < indexRange) - { + if (idx < indexRange) { // Copying data to destination from Dictionary for (j = 0; j < numDictionaries; j++) { memcpy(&dst_data[dataLength * (i + j * src_size)], @@ -85,17 +75,17 @@ class MKLDNNCPUExtGatherTests: public TestsCommon, public WithParamInterface - + - _IIDX_ + _IDICT_ - + - _IDICT_ + _IIDX_ @@ -117,17 +107,17 @@ class MKLDNNCPUExtGatherTests: public TestsCommon, public WithParamInterface - - + + )V0G0N"; std::string getModel(gather_test_params p) { std::string model = model_t; - std::string inIdx; + std::string inIdx = ""; std::string inDict; - std::string out; + std::string out = ""; for (auto& idx : p.inIdx) { inIdx += ""; @@ -193,7 +183,6 @@ class MKLDNNCPUExtGatherTests: public TestsCommon, public WithParamInterfacegetSelectedPrimitiveDescriptor()->getImplementationType() & p.selectedType); } } - ASSERT_EQ(4, nodes.size()); // Input Dictionary InferenceEngine::Blob::Ptr srcDict = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, p.inDict, InferenceEngine::TensorDesc::getLayoutByDims(p.inDict) }); @@ -309,27 +298,32 @@ TEST_P(MKLDNNCPUExtGatherTests, TestsGather) {} INSTANTIATE_TEST_CASE_P( TestsGather, MKLDNNCPUExtGatherTests, ::testing::Values( - gather_test_params{ "FP32", {1, 1, 12, 256}, {1, 1, 71, 16}, 0, {1, 12, 256, 16}, 1, MKLDNNPlugin::impl_desc_type::unknown }, - gather_test_params{ "I32", {1, 1, 12, 256}, {1, 1, 71, 16}, 0, {1, 12, 256, 16}, 1, MKLDNNPlugin::impl_desc_type::unknown }, - gather_test_params{ "I32", {12, 256}, {71, 16}, 0, {12, 256, 16}, 1, MKLDNNPlugin::impl_desc_type::unknown }, - gather_test_params{ "I32", {3, 4}, {2, 5, 6}, 0, {3, 4, 5, 6}, 1, MKLDNNPlugin::impl_desc_type::unknown }, - gather_test_params{ "I32", {3, 4}, {5, 1}, 0, {3, 4, 1}, 1, MKLDNNPlugin::impl_desc_type::unknown }, - gather_test_params{ "FP32", {1, 1, 12, 256}, {1, 1, 71, 16}, 1, {1, 71, 12, 256}, 1, MKLDNNPlugin::impl_desc_type::unknown }, - gather_test_params{ "I32", {1, 1, 3, 4}, {1, 2, 5, 6}, 1, {2, 3, 4, 6}, 1, MKLDNNPlugin::impl_desc_type::unknown }, - gather_test_params{ "I32", {1, 1, 3, 4}, {1, 2, 5, 6}, 2, {2, 5, 3, 4}, 1, MKLDNNPlugin::impl_desc_type::unknown }, - gather_test_params{ "I32", {12, 4, 9, 8}, {6, 13, 10, 3}, 1, {6, 12, 4, 9, 8, 10, 3}, 1, MKLDNNPlugin::impl_desc_type::unknown } +// Params: inIdxPrecision, inDict, inIdx, axis, out, num_prim_desc, selectedType + gather_test_params{ "I32",{ 31 },{}, 0,{}, 1, MKLDNNPlugin::impl_desc_type::unknown }, + gather_test_params{ "FP32",{ 31 },{}, 0,{}, 1, MKLDNNPlugin::impl_desc_type::unknown }, + gather_test_params{ "FP32",{ 1, 31, 4 },{ 10 }, 1,{ 1, 10, 4 }, 1, MKLDNNPlugin::impl_desc_type::unknown }, + gather_test_params{ "FP32",{ 31, 7 },{ 1,12,1 }, 0,{ 1, 12, 1, 7 }, 1, MKLDNNPlugin::impl_desc_type::unknown }, + gather_test_params{ "FP32", {71, 16}, {1, 12, 256}, 0, {1, 12, 256, 16}, 1, MKLDNNPlugin::impl_desc_type::unknown }, + gather_test_params{ "I32", {71, 16}, {1, 12, 256}, 0, {1, 12, 256, 16}, 1, MKLDNNPlugin::impl_desc_type::unknown }, + gather_test_params{ "I32", {71, 16}, {12, 256}, 0, {12, 256, 16}, 1, MKLDNNPlugin::impl_desc_type::unknown }, + gather_test_params{ "I32", {2, 5, 6}, {3, 4}, 0, {3, 4, 5, 6}, 1, MKLDNNPlugin::impl_desc_type::unknown }, + gather_test_params{ "I32", {5, 1}, {3, 4}, 0, {3, 4, 1}, 1, MKLDNNPlugin::impl_desc_type::unknown }, + gather_test_params{ "FP32", {71, 16}, {1, 12, 256}, 1, {1, 71, 12, 256}, 1, MKLDNNPlugin::impl_desc_type::unknown }, + gather_test_params{ "I32", {2, 5, 6}, {1, 1, 3, 4}, 1, {2, 3, 4, 6}, 1, MKLDNNPlugin::impl_desc_type::unknown }, + gather_test_params{ "I32", {2, 5, 6}, {1, 1, 3, 4}, 2, {2, 5, 3, 4}, 1, MKLDNNPlugin::impl_desc_type::unknown }, + gather_test_params{ "I32", {6, 13, 10, 3}, {12, 4, 9, 8}, 1, {6, 12, 4, 9, 8, 10, 3}, 1, MKLDNNPlugin::impl_desc_type::unknown } )); struct gatherTF_test_params { - InferenceEngine::SizeVector in_dim; - std::vector in; - InferenceEngine::SizeVector dct_dim; std::vector dct; + InferenceEngine::SizeVector in_dim; + std::vector in; + int axis; InferenceEngine::SizeVector ref_dim; @@ -342,17 +336,17 @@ class MKLDNNCPUExtGatherTFTests : public TestsCommon, public WithParamInterface< std::string model_t = R"V0G0N( - + - _IIDX_ + _IDICT_ - + - _IDICT_ + _IIDX_ @@ -374,8 +368,8 @@ class MKLDNNCPUExtGatherTFTests : public TestsCommon, public WithParamInterface< - - + + )V0G0N"; @@ -474,8 +468,6 @@ class MKLDNNCPUExtGatherTFTests : public TestsCommon, public WithParamInterface< TEST_P(MKLDNNCPUExtGatherTFTests, TestsGather) {} // Test data vectors -std::vector in0 = { 0, 1, 1, 0 }; -std::vector in1 = { 0, 1, 2, 1 }; std::vector dict = { 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f }; std::vector ref_in0_a0_d223 = { 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f }; // 2x2x2x3 std::vector ref_in0_a2_d232 = { 1.f, 2.f, 2.f, 1.f, 3.f, 4.f, 4.f, 3.f, 5.f, 6.f, 6.f, 5.f, 7.f, 8.f, 8.f, 7.f, 9.f, 10.f, 10.f, 9.f, 11.f, 12.f, 12.f, 11.f }; // 2x3x2x2 @@ -486,34 +478,37 @@ std::vector ref_in1_a2_d223 = { 1.f, 2.f, 3.f, 2.f, 4.f, 5.f, 6.f, 5.f, 7 INSTANTIATE_TEST_CASE_P( TestsGather, MKLDNNCPUExtGatherTFTests, ::testing::Values( - gatherTF_test_params{ { 2, 2 }, in0,{ 2, 2, 3 }, dict, 0, { 2, 2, 2, 3 }, ref_in0_a0_d223 }, - gatherTF_test_params{ { 2, 2 }, in0,{ 2, 2, 3 }, dict,-3, { 2, 2, 2, 3 }, ref_in0_a0_d223 }, - gatherTF_test_params{ { 2, 2 }, in0,{ 2, 3, 2 }, dict, 2, { 2, 3, 2, 2 }, ref_in0_a2_d232 }, - gatherTF_test_params{ { 2, 2 }, in0,{ 2, 3, 2 }, dict,-1, { 2, 3, 2, 2 }, ref_in0_a2_d232 }, - gatherTF_test_params{ { 2, 2 }, in1,{ 3, 2, 2 }, dict, 0, { 2, 2, 2, 2 }, ref_in1_a0_d322 }, - gatherTF_test_params{ { 2, 2 }, in1,{ 3, 2, 2 }, dict,-3, { 2, 2, 2, 2 }, ref_in1_a0_d322 }, - gatherTF_test_params{ { 2, 2 }, in1,{ 2, 3, 2 }, dict, 1, { 2, 2, 2, 2 }, ref_in1_a1_d232 }, - gatherTF_test_params{ { 2, 2 }, in1,{ 2, 3, 2 }, dict,-2, { 2, 2, 2, 2 }, ref_in1_a1_d232 }, - gatherTF_test_params{ { 2, 2 }, in1,{ 2, 2, 3 }, dict, 2, { 2, 2, 2, 2 }, ref_in1_a2_d223 }, - gatherTF_test_params{ { 2, 2 }, in1,{ 2, 2, 3 }, dict,-1, { 2, 2, 2, 2 }, ref_in1_a2_d223 })); +// Params: dct_dim, dct, in_dim, in, axis, ref_dim, ref + gatherTF_test_params{ { 3,2 }, {1.0, 1.2, 2.3, 3.4, 4.5, 5.7 }, { 2, 2 }, { 0, 1, 1, 2 },0, { 2, 2, 2 }, {1.0, 1.2, 2.3, 3.4,2.3, 3.4,4.5, 5.7 } }, + gatherTF_test_params{ { 3,3 },{ 1.0, 1.2, 1.9,2.3, 3.4, 3.9,4.5, 5.7, 5.9 }, { 1, 2 }, { 0, 2 },1,{ 3, 2 },{ 1.0, 1.9,2.3, 3.9,4.5, 5.9 } }, + gatherTF_test_params{ { 2, 2, 3 }, dict, { 2, 2 }, { 0, 1, 1, 0 },0, { 2, 2, 2, 3 }, ref_in0_a0_d223 }, + gatherTF_test_params{ { 2, 2, 3 }, dict,{ 2, 2 }, { 0, 1, 1, 0 },-3, { 2, 2, 2, 3 }, ref_in0_a0_d223 }, + gatherTF_test_params{ { 2, 3, 2 }, dict, { 2, 2 }, { 0, 1, 1, 0 },2, { 2, 3, 2, 2 }, ref_in0_a2_d232 }, + gatherTF_test_params{ { 2, 3, 2 }, dict,{ 2, 2 }, { 0, 1, 1, 0 },-1, { 2, 3, 2, 2 }, ref_in0_a2_d232 }, + gatherTF_test_params{ { 3, 2, 2 }, dict,{ 2, 2 }, { 0, 1, 2, 1 }, 0, { 2, 2, 2, 2 }, ref_in1_a0_d322 }, + gatherTF_test_params{ { 3, 2, 2 }, dict,{ 2, 2 }, { 0, 1, 2, 1 },-3, { 2, 2, 2, 2 }, ref_in1_a0_d322 }, + gatherTF_test_params{ { 2, 3, 2 }, dict,{ 2, 2 }, { 0, 1, 2, 1 }, 1, { 2, 2, 2, 2 }, ref_in1_a1_d232 }, + gatherTF_test_params{ { 2, 3, 2 }, dict,{ 2, 2 }, { 0, 1, 2, 1 },-2, { 2, 2, 2, 2 }, ref_in1_a1_d232 }, + gatherTF_test_params{ { 2, 2, 3 }, dict,{ 2, 2 }, { 0, 1, 2, 1 }, 2, { 2, 2, 2, 2 }, ref_in1_a2_d223 }, + gatherTF_test_params{ { 2, 2, 3 }, dict,{ 2, 2 }, { 0, 1, 2, 1 },-1, { 2, 2, 2, 2 }, ref_in1_a2_d223 })); class MKLDNNCPUExtGatherHolesTests : public TestsCommon, public WithParamInterface { std::string model_t = R"V0G0N( - + + 3 2 2 - + - 3 2 2 @@ -578,8 +573,8 @@ class MKLDNNCPUExtGatherHolesTests : public TestsCommon, public WithParamInterfa - - + + @@ -686,5 +681,6 @@ TEST_P(MKLDNNCPUExtGatherHolesTests, TestsGather) {} INSTANTIATE_TEST_CASE_P( TestsGather, MKLDNNCPUExtGatherHolesTests, ::testing::Values( - gatherTF_test_params{ { 1, 5, 2, 2 }, in1,{ 1, 3, 2, 2 }, dict, 1,{ 2, 2, 2, 2 }, ref_in1_a0_d322 })); + // Params: dct_dim, dct, in_dim, in, axis, ref_dim, ref + gatherTF_test_params{ { 1, 3, 2, 2 }, dict,{ 1, 5, 2, 2 },{ 0, 1, 2, 1 }, 1,{ 2, 2, 2, 2 }, ref_in1_a0_d322 })); diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/gather_tree_tests.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/gather_tree_tests.cpp deleted file mode 100644 index bd29222af11c41..00000000000000 --- a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/gather_tree_tests.cpp +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include -#include -#include "mkldnn_plugin/mkldnn_graph.h" - -#include "test_graph.hpp" - -#include "single_layer_common.hpp" -#include -#include -#include "tests_common.hpp" -#include - -#include -#include -#include - -using namespace InferenceEngine; -using namespace ::testing; -using namespace std; -using namespace mkldnn; - -struct gather_tree_test_params { - SizeVector in_out_shape; - std::vector step_idx; - std::vector parent_idx; - std::vector max_seq_len; - std::vector end_token; - std::vector reference; - - std::vector> comp; -}; - - -template -void ref_gather_tree( - InferenceEngine::TBlob &step_idx, - InferenceEngine::TBlob &parent_idx, - InferenceEngine::TBlob &max_seq_len, - InferenceEngine::TBlob &end_token, - InferenceEngine::TBlob &dst -) { - const data_t *step_idxPtr = step_idx.data(); - const int32_t *parent_idxPtr = parent_idx.data(); - const int32_t *max_seq_lenPtr = max_seq_len.data(); - const data_t *end_tokenPtr = end_token.data(); - data_t *final_idxPtr = dst.data(); - - SizeVector step_idx_dims = step_idx.getTensorDesc().getDims(); - SizeVector parent_idx_dims = parent_idx.getTensorDesc().getDims(); - SizeVector max_seq_len_dims = max_seq_len.getTensorDesc().getDims(); - SizeVector final_idx_dims = dst.getTensorDesc().getDims(); - int32_t max_time = step_idx_dims[0]; - int32_t batch_size = step_idx_dims[1]; - int32_t beam_width = step_idx_dims[2]; - - if (max_time != parent_idx_dims[0] || max_time != final_idx_dims[0] || - batch_size != parent_idx_dims[1] || batch_size != final_idx_dims[1] || batch_size != max_seq_len_dims[0] || - beam_width != parent_idx_dims[2] || beam_width != final_idx_dims[2]) { - FAIL() << " Input/Output tensors dimensions mismatch"; - return; - } - - for (int32_t time, batch = 0; batch < batch_size; batch++) { - for (int32_t beam = 0; beam < beam_width; beam++) { - int32_t max_sequence_in_beam = (std::min)(max_time, max_seq_lenPtr[batch]); - if (max_sequence_in_beam <= 0) - continue; - - for (time = (max_time - 1); time >= max_sequence_in_beam; time--) - final_idxPtr[(time * batch_size + batch) * beam_width + beam] = (*end_tokenPtr); - - for (int32_t parent = beam; time >= 0; time--) { - if (parent < 0 || parent >= beam_width) { - FAIL() << " Wrong parent index"; - return; - } - - int32_t idx = (time * batch_size + batch) * beam_width; - final_idxPtr[idx + beam] = step_idxPtr[idx + parent]; - parent = parent_idxPtr[idx + parent]; - } - - bool finished = false; - data_t *final = &final_idxPtr[batch * beam_width + beam]; - for (time = 0; time < max_sequence_in_beam; time++, final += (batch_size * beam_width)) { - if (finished) - (*final) = (*end_tokenPtr); - else if ((*final) == (*end_tokenPtr)) - finished = true; - } - } - } -} - -class MKLDNNCPUExtGatherTreeTests : public TestsCommon, public WithParamInterface { - std::string model_t = R"V0G0N( - - - - - - _IN_OUT_ - - - - - - - _IN_OUT_ - - - - - - - _IN2_ - - - - - - - 1 - - - - - - - - _IN_OUT_ - - - _IN_OUT_ - - - _IN2_ - - - 1 - - - - - _IN_OUT_ - - - - - - - - - - - -)V0G0N"; - - std::string getModel(gather_tree_test_params p) { - std::string model = model_t; - std::string in_out_shape; - - for (auto& dct : p.in_out_shape) { - in_out_shape += ""; - in_out_shape += std::to_string(dct) + "\n"; - } - - REPLACE_WITH_STR(model, "_IN_OUT_", in_out_shape); - REPLACE_WITH_NUM(model, "_IN2_", p.in_out_shape[1]); - - return model; - } - -protected: - virtual void TearDown() { - } - - virtual void SetUp() { - try { - TestsCommon::SetUp(); - gather_tree_test_params p = ::testing::WithParamInterface::GetParam(); - std::string model = getModel(p); - //std::cout << model; - InferenceEngine::CNNNetReader net_reader; - ASSERT_NO_THROW(net_reader.ReadNetwork(model.data(), model.length())); - - InferenceEngine::Extension cpuExt(make_so_name("cpu_extension")); - MKLDNNPlugin::MKLDNNExtensionManager::Ptr extMgr(new MKLDNNPlugin::MKLDNNExtensionManager()); - extMgr->AddExtension(InferenceEngine::IExtensionPtr(&cpuExt, [](InferenceEngine::IExtension*) {})); - - MKLDNNGraphTestClass graph; - graph.CreateGraph(net_reader.getNetwork(), extMgr); - - // Output Data - InferenceEngine::OutputsDataMap out; - out = net_reader.getNetwork().getOutputsInfo(); - InferenceEngine::BlobMap outputBlobs; - - std::pair item = *out.begin(); - - InferenceEngine::TBlob::Ptr output; - output = InferenceEngine::make_shared_blob(item.second->getTensorDesc()); - output->allocate(); - outputBlobs[item.first] = output; - - // Output Reference - InferenceEngine::TBlob dst_ref(item.second->getTensorDesc()); - dst_ref.allocate(); - - // Input Data - // step_idx - InferenceEngine::Blob::Ptr step_idx; - step_idx = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::I32, p.in_out_shape, InferenceEngine::TensorDesc::getLayoutByDims(p.in_out_shape) }); - step_idx->allocate(); - memcpy(step_idx->buffer(), &p.step_idx[0], sizeof(int32_t)*p.step_idx.size()); - auto * step_idxPtr = dynamic_cast*>(step_idx.get()); - if (step_idxPtr == nullptr) - FAIL() << "Cannot cast blob to TBlob."; - - // parent_idx - InferenceEngine::Blob::Ptr parent_idx; - parent_idx = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::I32, p.in_out_shape, InferenceEngine::TensorDesc::getLayoutByDims(p.in_out_shape) }); - parent_idx->allocate(); - memcpy(parent_idx->buffer(), &p.parent_idx[0], sizeof(int32_t)*p.parent_idx.size()); - auto * parent_idxPtr = dynamic_cast*>(parent_idx.get()); - if (parent_idxPtr == nullptr) - FAIL() << "Cannot cast blob to TBlob."; - - // max_seq_len - InferenceEngine::Blob::Ptr max_seq_len; - InferenceEngine::SizeVector max_seq_len_dim(1, p.in_out_shape[1]); - max_seq_len = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::I32, max_seq_len_dim, InferenceEngine::TensorDesc::getLayoutByDims(max_seq_len_dim) }); - max_seq_len->allocate(); - memcpy(max_seq_len->buffer(), &p.max_seq_len[0], sizeof(int32_t)*p.max_seq_len.size()); - auto * max_seq_lenPtr = dynamic_cast*>(max_seq_len.get()); - if (max_seq_lenPtr == nullptr) - FAIL() << "Cannot cast blob to TBlob."; - - // end_token - InferenceEngine::Blob::Ptr end_token; - InferenceEngine::SizeVector end_token_dim(1, 1); - end_token = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::I32, end_token_dim, InferenceEngine::TensorDesc::getLayoutByDims(end_token_dim) }); - end_token->allocate(); - memcpy(static_cast(end_token->buffer()), &p.end_token[0], sizeof(int32_t)); - auto * seq_lengthsIdxPtr = dynamic_cast*>(end_token.get()); - if (seq_lengthsIdxPtr == nullptr) - FAIL() << "Cannot cast blob to TBlob."; - - InferenceEngine::BlobMap srcs; - srcs.insert(std::pair("step_idx", step_idx)); - srcs.insert(std::pair("parent_idx", parent_idx)); - srcs.insert(std::pair("max_seq_len", max_seq_len)); - srcs.insert(std::pair("end_token", end_token)); - - // Reference - ref_gather_tree(*step_idxPtr, *parent_idxPtr, *max_seq_lenPtr, *seq_lengthsIdxPtr, dst_ref); - if (p.reference.size()) - if (memcmp(dst_ref.data(), &p.reference[0], p.reference.size() * sizeof(int32_t)) != 0) - FAIL() << "Wrong result with compare reference vector!"; - - // Infer - graph.Infer(srcs, outputBlobs); - compare(*output, dst_ref); - } catch (const InferenceEngine::details::InferenceEngineException &e) { - FAIL() << e.what(); - } - } -}; - -TEST_P(MKLDNNCPUExtGatherTreeTests, TestsGatherTree) {} - - -INSTANTIATE_TEST_CASE_P( - TestsGatherTree, MKLDNNCPUExtGatherTreeTests, - ::testing::Values( -// Params: in_out_shape, step_idx, parent_idx, max_seq_len, end_token, reference - gather_tree_test_params{ { 3,2,3 },{ 1,2,3,2,3,4,4,5,6,5,6,7,7,8,9,8,9,10 },{ 0,0,0,0,0,0,0,1,1,1,2,0,2,1,2,2,1,1 },{ 3,3 },{ 11 },{ 2,2,2,2,4,4,6,5,6,7,6,6,7,8,9,8,9,10 } }, - gather_tree_test_params{ { 4,1,3 },{ 1,2,3,4,5,6,7,8,9,-1,-1,-1 },{ 0,0,0,0,1,1,2,1,2,-1,-1,-1 },{ 3 },{ 10 },{ 2,2,2,6,5,6,7,8,9,10,10,10 } }, - gather_tree_test_params{ { 4,1,3 },{ 1,2,3,4,5,6,7,8,9,10,10,10 },{ 0,0,0,0,1,1,2,1,2,1,1,1 },{ 4 },{ 10 },{ 2,2,2,5,5,5,8,8,8,10,10,10 } }, - gather_tree_test_params{ { 5,1,3 },{ 1,2,3,4,5,6,7,8,9,1,10,3,2,10,10 },{ 0,0,0,0,1,1,2,1,2,1,1,1,2,0,1 },{ 5 },{ 10 },{ 2,2,2,5,5,5,8,8,8,3,1,10,2,10,10 } }, - gather_tree_test_params{ { 4,2,3 },{ 1,2,3,2,3,4,4,5,6,5,6,7,7,8,9,8,9,10,0,0,0,11,12,0 },{ 0,0,0,0,0,0,0,1,1,1,1,0,2,1,2,2,0,1,-1,-1,-1,0,1,0 },{ 3,4 },{ 11 },{ 2,2,2,2,3,2,6,5,6,7,5,7,7,8,9,8,9,8,11,11,11,11,12,0 } } - )); diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/graph_generic_test.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/graph_generic_test.cpp index 04be8a31e1dbe6..45aab3835cf2d2 100644 --- a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/graph_generic_test.cpp +++ b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/graph_generic_test.cpp @@ -151,7 +151,7 @@ class ConstPrimitiveImpl : public InferenceEngine::ILayerExecImpl { size_t data_size = outputs[0]->size(); for (size_t i = 0; i < data_size; i++) { - dst_data[i] = (dst_data[i] + 1)*2; + dst_data[i] = 2; } return InferenceEngine::OK; } diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/math_tests.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/math_tests.cpp index 52ce9631a45f1c..564221f2015b67 100644 --- a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/math_tests.cpp +++ b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/math_tests.cpp @@ -180,7 +180,7 @@ class MKLDNNCPUExtMathTests: public TestsCommon, public WithParamInterface +#include +#include "mkldnn_plugin/mkldnn_graph.h" + +#include "test_graph.hpp" + +#include "single_layer_common.hpp" +#include +#include +#include "tests_common.hpp" + + +using namespace ::testing; +using namespace std; +using namespace mkldnn; + +struct nmsTF_test_params { + int center_point_box; + InferenceEngine::SizeVector scoresDim; + std::vector boxes; + std::vector scores; + std::vector max_output_boxes_per_class; + std::vector iou_threshold; + std::vector score_threshold; + + int num_selected_indices; + std::vector ref; + + std::vector> comp; +}; + +static float intersectionOverUnion(float* boxesI, float* boxesJ, bool center_point_box) { + float yminI, xminI, ymaxI, xmaxI, yminJ, xminJ, ymaxJ, xmaxJ; + if (center_point_box) { + // box format: x_center, y_center, width, height + yminI = boxesI[1] - boxesI[3] / 2.f; + xminI = boxesI[0] - boxesI[2] / 2.f; + ymaxI = boxesI[1] + boxesI[3] / 2.f; + xmaxI = boxesI[0] + boxesI[2] / 2.f; + yminJ = boxesJ[1] - boxesJ[3] / 2.f; + xminJ = boxesJ[0] - boxesJ[2] / 2.f; + ymaxJ = boxesJ[1] + boxesJ[3] / 2.f; + xmaxJ = boxesJ[0] + boxesJ[2] / 2.f; + } else { + // box format: y1, x1, y2, x2 + yminI = (std::min)(boxesI[0], boxesI[2]); + xminI = (std::min)(boxesI[1], boxesI[3]); + ymaxI = (std::max)(boxesI[0], boxesI[2]); + xmaxI = (std::max)(boxesI[1], boxesI[3]); + yminJ = (std::min)(boxesJ[0], boxesJ[2]); + xminJ = (std::min)(boxesJ[1], boxesJ[3]); + ymaxJ = (std::max)(boxesJ[0], boxesJ[2]); + xmaxJ = (std::max)(boxesJ[1], boxesJ[3]); + } + + float areaI = (ymaxI - yminI) * (xmaxI - xminI); + float areaJ = (ymaxJ - yminJ) * (xmaxJ - xminJ); + if (areaI <= 0.f || areaJ <= 0.f) + return 0.f; + + float intersection_area = + (std::max)((std::min)(ymaxI, ymaxJ) - (std::max)(yminI, yminJ), 0.f) * + (std::max)((std::min)(xmaxI, xmaxJ) - (std::max)(xminI, xminJ), 0.f); + return intersection_area / (areaI + areaJ - intersection_area); +} + +typedef struct { + float score; + int batch_index; + int class_index; + int box_index; +} filteredBoxes; + +static void ref_nms( + InferenceEngine::TBlob &srcBoxes, + InferenceEngine::TBlob &srcScores, + InferenceEngine::TBlob &selected_idxs, + nmsTF_test_params p +) { + float *boxes = srcBoxes.data(); + float *scores = srcScores.data(); + + InferenceEngine::SizeVector scores_dims = srcScores.getTensorDesc().getDims(); + int num_boxes = static_cast(scores_dims[2]); + int max_output_boxes_per_class = num_boxes; + if (p.max_output_boxes_per_class.size()) + max_output_boxes_per_class = (std::min)(max_output_boxes_per_class, p.max_output_boxes_per_class[0]); + + float iou_threshold = 1.f; // Value range [0, 1] + if (p.iou_threshold.size()) + iou_threshold = (std::min)(iou_threshold, p.iou_threshold[0]); + + float score_threshold = 0.f; + if (p.score_threshold.size()) + score_threshold = p.score_threshold[0]; + + int* selected_indices = selected_idxs.data(); + InferenceEngine::SizeVector selected_indices_dims = selected_idxs.getTensorDesc().getDims(); + + InferenceEngine::SizeVector boxesStrides = srcBoxes.getTensorDesc().getBlockingDesc().getStrides(); + InferenceEngine::SizeVector scoresStrides = srcScores.getTensorDesc().getBlockingDesc().getStrides(); + + // boxes shape: {num_batches, num_boxes, 4} + // scores shape: {num_batches, num_classes, num_boxes} + int num_batches = static_cast(scores_dims[0]); + int num_classes = static_cast(scores_dims[1]); + std::vector fb; + + for (int batch = 0; batch < num_batches; batch++) { + float *boxesPtr = boxes + batch * boxesStrides[0]; + for (int class_idx = 0; class_idx < num_classes; class_idx++) { + float *scoresPtr = scores + batch * scoresStrides[0] + class_idx * scoresStrides[1]; + std::vector > scores_vector; + for (int box_idx = 0; box_idx < num_boxes; box_idx++) { + if (scoresPtr[box_idx] > score_threshold) + scores_vector.push_back(std::make_pair(scoresPtr[box_idx], box_idx)); + } + + if (scores_vector.size()) { + std::sort(scores_vector.begin(), scores_vector.end(), + [](const std::pair& l, const std::pair& r) { return l.first > r.first; }); + + int io_selection_size = 1; + fb.push_back({ scores_vector[0].first, batch, class_idx, scores_vector[0].second }); + for (int box_idx = 1; (box_idx < static_cast(scores_vector.size()) && io_selection_size < max_output_boxes_per_class); box_idx++) { + bool box_is_selected = true; + for (int idx = io_selection_size - 1; idx >= 0; idx--) { + float iou = intersectionOverUnion(&boxesPtr[scores_vector[box_idx].second * 4], + &boxesPtr[scores_vector[idx].second * 4], (p.center_point_box == 1)); + if (iou > iou_threshold) { + box_is_selected = false; + break; + } + } + + if (box_is_selected) { + scores_vector[io_selection_size] = scores_vector[box_idx]; + io_selection_size++; + fb.push_back({ scores_vector[box_idx].first, batch, class_idx, scores_vector[box_idx].second }); + } + } + } + } + } + + std::sort(fb.begin(), fb.end(), [](const filteredBoxes& l, const filteredBoxes& r) { return l.score > r.score; }); + int selected_indicesStride = selected_idxs.getTensorDesc().getBlockingDesc().getStrides()[0]; + int* selected_indicesPtr = selected_indices; + size_t idx; + for (idx = 0; idx < (std::min)(selected_indices_dims[0], fb.size()); idx++) { + selected_indicesPtr[0] = fb[idx].batch_index; + selected_indicesPtr[1] = fb[idx].class_index; + selected_indicesPtr[2] = fb[idx].box_index; + selected_indicesPtr += selected_indicesStride; + } + for (; idx < selected_indices_dims[0]; idx++) { + selected_indicesPtr[0] = -1; + selected_indicesPtr[1] = -1; + selected_indicesPtr[2] = -1; + selected_indicesPtr += selected_indicesStride; + } +} + +class MKLDNNCPUExtNonMaxSuppressionTFTests : public TestsCommon, public WithParamInterface { + std::string model_t2 = R"V0G0N( + + + + + + _IBOXES_ + + + + + + + _ISCORES_ + + + + + + + + _IBOXES_ + + + _ISCORES_ + + + + + _IOUT_ + + + + + + + + + +)V0G0N"; + + std::string model_t3 = R"V0G0N( + + + + + + _IBOXES_ + + + + + + + _ISCORES_ + + + + + + + 1 + + + + + + + + _IBOXES_ + + + _ISCORES_ + + + 1 + + + + + _IOUT_ + + + + + + + + + + +)V0G0N"; + std::string model_t4 = R"V0G0N( + + + + + + _IBOXES_ + + + + + + + _ISCORES_ + + + + + + + 1 + + + + + + + 1 + + + + + + + + _IBOXES_ + + + _ISCORES_ + + + 1 + + + 1 + + + + + _IOUT_ + + + + + + + + + + + +)V0G0N"; + + std::string model_t5 = R"V0G0N( + + + + + + _IBOXES_ + + + + + + + _ISCORES_ + + + + + + + 1 + + + + + + + 1 + + + + + + + 1 + + + + + + + + _IBOXES_ + + + _ISCORES_ + + + 1 + + + 1 + + + 1 + + + + + _IOUT_ + + + + + + + + + + + + +)V0G0N"; + + std::string getModel(nmsTF_test_params p) { + std::string model; + if (!p.max_output_boxes_per_class.size()) + model = model_t2; + else if (!p.iou_threshold.size()) + model = model_t3; + else if (!p.score_threshold.size()) + model = model_t4; + else + model = model_t5; + + std::string inBoxes; + std::string inScores; + std::string out; + + inBoxes += "" + std::to_string(p.scoresDim[0]) + "\n"; + inBoxes += "" + std::to_string(p.scoresDim[2]) + "\n"; + inBoxes += "4"; + + + for (auto& scr : p.scoresDim) { + inScores += ""; + inScores += std::to_string(scr) + "\n"; + } + + out += "" + std::to_string(p.num_selected_indices) + "\n"; + out += "3"; + + REPLACE_WITH_STR(model, "_IBOXES_", inBoxes); + REPLACE_WITH_STR(model, "_ISCORES_", inScores); + REPLACE_WITH_STR(model, "_IOUT_", out); + REPLACE_WITH_NUM(model, "_CPB_", p.center_point_box); + + return model; + } + +protected: + virtual void TearDown() { + } + + virtual void SetUp() { + try { + TestsCommon::SetUp(); + nmsTF_test_params p = ::testing::WithParamInterface::GetParam(); + std::string model = getModel(p); + //std::cout << model << std::endl; + InferenceEngine::CNNNetReader net_reader; + ASSERT_NO_THROW(net_reader.ReadNetwork(model.data(), model.length())); + + InferenceEngine::Extension cpuExt(make_so_name("cpu_extension")); + MKLDNNPlugin::MKLDNNExtensionManager::Ptr extMgr(new MKLDNNPlugin::MKLDNNExtensionManager()); + extMgr->AddExtension(InferenceEngine::IExtensionPtr(&cpuExt, [](InferenceEngine::IExtension*){})); + + MKLDNNGraphTestClass graph; + graph.CreateGraph(net_reader.getNetwork(), extMgr); + + // Input + InferenceEngine::BlobMap srcs; + + // Input Boxes + InferenceEngine::SizeVector boxesDim = {p.scoresDim[0], p.scoresDim[2], 4}; + InferenceEngine::Blob::Ptr srcBoxes = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, boxesDim, InferenceEngine::TensorDesc::getLayoutByDims(boxesDim) }); + srcBoxes->allocate(); + for (size_t i = 0; i < p.boxes.size(); i++) { + static_cast(srcBoxes->buffer())[i] = static_cast(p.boxes[i]); + } + //memcpy(srcBoxes->buffer(), &p.boxes[0], sizeof(float)*boxes.size()); + auto * srcBoxesPtr = dynamic_cast*>(srcBoxes.get()); + if (srcBoxesPtr == nullptr) + FAIL() << "Cannot cast blob to TBlob."; + srcs.insert(std::pair("InputBoxes", srcBoxes)); + + // Input Scores + InferenceEngine::Blob::Ptr srcScores = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, p.scoresDim, InferenceEngine::TensorDesc::getLayoutByDims(p.scoresDim) }); + srcScores->allocate(); + for (size_t i = 0; i < p.scores.size(); i++) { + static_cast(srcScores->buffer())[i] = static_cast(p.scores[i]); + } + auto * srcScoresPtr = dynamic_cast*>(srcScores.get()); + if (srcScoresPtr == nullptr) + FAIL() << "Cannot cast blob to TBlob."; + srcs.insert(std::pair("InputScores", srcScores)); + + // Input BoxesPerClass + InferenceEngine::Blob::Ptr srcBoxesPerClass; + InferenceEngine::Blob::Ptr srcIouThr; + InferenceEngine::Blob::Ptr srcScoreThr; + if (p.max_output_boxes_per_class.size()) { + srcBoxesPerClass = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::I32, InferenceEngine::SizeVector(1,1), InferenceEngine::TensorDesc::getLayoutByDims(InferenceEngine::SizeVector(1,1)) }); + srcBoxesPerClass->allocate(); + memcpy(static_cast(srcBoxesPerClass->buffer()), &p.max_output_boxes_per_class[0], sizeof(int32_t)); + auto * srcBoxesPerClassPtr = dynamic_cast*>(srcBoxesPerClass.get()); + if (srcBoxesPerClassPtr == nullptr) + FAIL() << "Cannot cast blob to TBlob."; + srcs.insert(std::pair("InputBoxesPerClass", srcBoxesPerClass)); + } + + // Input IouThr + if (p.iou_threshold.size()) { + srcIouThr = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, InferenceEngine::SizeVector(1,1), InferenceEngine::TensorDesc::getLayoutByDims(InferenceEngine::SizeVector(1,1)) }); + srcIouThr->allocate(); + memcpy(static_cast(srcIouThr->buffer()), &p.iou_threshold[0], sizeof(float)); + auto * srcIouThrPtr = dynamic_cast*>(srcIouThr.get()); + if (srcIouThrPtr == nullptr) + FAIL() << "Cannot cast blob to TBlob."; + srcs.insert(std::pair("InputIouThr", srcIouThr)); + } + + // Input ScoreThr + if (p.score_threshold.size()) { + srcScoreThr = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, InferenceEngine::SizeVector(1,1), InferenceEngine::TensorDesc::getLayoutByDims(InferenceEngine::SizeVector(1,1)) }); + srcScoreThr->allocate(); + memcpy(static_cast(srcScoreThr->buffer()), &p.score_threshold[0], sizeof(float)); + auto * srcScoreThrPtr = dynamic_cast*>(srcScoreThr.get()); + if (srcScoreThrPtr == nullptr) + FAIL() << "Cannot cast blob to TBlob."; + srcs.insert(std::pair("InputScoreThr", srcScoreThr)); + } + + // Output Data + InferenceEngine::OutputsDataMap out; + out = net_reader.getNetwork().getOutputsInfo(); + InferenceEngine::BlobMap outputBlobs; + std::pair item = *out.begin(); + InferenceEngine::TBlob::Ptr output; + output = InferenceEngine::make_shared_blob(item.second->getTensorDesc()); + output->allocate(); + outputBlobs[item.first] = output; + + // Infer + graph.Infer(srcs, outputBlobs); + + // Output Reference + if (!p.ref.size()) { + InferenceEngine::TBlob selected_indices_ref(item.second->getTensorDesc()); + selected_indices_ref.allocate(); + ref_nms(*srcBoxesPtr, *srcScoresPtr, selected_indices_ref, p); + compare(*output, selected_indices_ref); + } else { + // Check results + if (memcmp((*output).data(), &p.ref[0], p.ref.size()) != 0) + FAIL() << "Wrong result with compare TF reference!"; + } + } catch (const InferenceEngine::details::InferenceEngineException &e) { + FAIL() << e.what(); + } + } +}; + +TEST_P(MKLDNNCPUExtNonMaxSuppressionTFTests, TestsNonMaxSuppression) {} + +static std::vector boxes = { 0.0, 0.0, 1.0, 1.0, 0.0, 0.1, 1.0, 1.1, 0.0, -0.1, 1.0, 0.9, 0.0, 10.0, 1.0, 11.0, 0.0, 10.1, 1.0, 11.1, 0.0, 100.0, 1.0, 101.0 }; +static std::vector scores = { 0.9f, 0.75f, 0.6f, 0.95f, 0.5f, 0.3f }; +static std::vector reference = { 0,0,3,0,0,0,0,0,5 }; + +INSTANTIATE_TEST_CASE_P( + TestsNonMaxSuppression, MKLDNNCPUExtNonMaxSuppressionTFTests, + ::testing::Values( +// Params: center_point_box, scoresDim, boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold, num_selected_indices, ref + + nmsTF_test_params{ 1, {1,1,6}, { 0.5f, 0.5f, 1.0f, 1.0f,0.5f, 0.6f, 1.0f, 1.0f,0.5f, 0.4f, 1.0f, 1.0f,0.5f, 10.5f, 1.0f, 1.0f, 0.5f, 10.6f, 1.0f, 1.0f, 0.5f, 100.5f, 1.0f, 1.0f }, + scores,{ 3 },{ 0.5f },{ 0.f }, 3, reference }, /*nonmaxsuppression_center_point_box_format*/ + + nmsTF_test_params{ 0, {1,1,6}, { 1.0, 1.0, 0.0, 0.0, 0.0, 0.1, 1.0, 1.1, 0.0, 0.9, 1.0, -0.1, 0.0, 10.0, 1.0, 11.0, 1.0, 10.1, 0.0, 11.1, 1.0, 101.0, 0.0, 100.0 }, + scores,{ 3 },{ 0.5 },{ 0.0 }, 3, reference }, /*nonmaxsuppression_flipped_coordinates*/ + + nmsTF_test_params{ 0, { 1,1,10 },{ 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, + 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0 }, + { 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9 },{ 3 },{ 0.5 },{ 0.0 }, 1,{ 0,0,0 } }, /*nonmaxsuppression_identical_boxes*/ + + nmsTF_test_params{ 0, { 1,1,6 }, boxes, scores,{ 2 },{ 0.5 },{ 0.0 }, 2,{ 0,0,3,0,0,0 } }, /*nonmaxsuppression_limit_output_size*/ + + nmsTF_test_params{ 0,{ 1,1,1 },{ 0.0, 0.0, 1.0, 1.0 }, { 0.9 },{ 3 },{ 0.5 },{ 0.0 }, 1, { 0,0,0 } }, /*nonmaxsuppression_single_box*/ + + nmsTF_test_params{ 0, { 1,1,6 }, boxes, scores, { 3 }, { 0.5 }, { 0.0 }, 3, reference }, /*nonmaxsuppression_suppress_by_IOU*/ + + nmsTF_test_params{ 0, { 2,1,6 },{ 0.0, 0.0, 1.0, 1.0, 0.0, 0.1, 1.0, 1.1, 0.0, -0.1, 1.0, 0.9, 0.0, 10.0, 1.0, 11.0, 0.0, 10.1, 1.0, 11.1, 0.0, 100.0, 1.0, 101.0, + 0.0, 0.0, 1.0, 1.0, 0.0, 0.1, 1.0, 1.1, 0.0, -0.1, 1.0, 0.9, 0.0, 10.0, 1.0, 11.0, 0.0, 10.1, 1.0, 11.1, 0.0, 100.0, 1.0, 101.0 }, + { 0.9, 0.75, 0.6, 0.95, 0.5, 0.3, 0.9, 0.75, 0.6, 0.95, 0.5, 0.3 },{ 2 },{ 0.5 },{ 0.0 }, 4,{ 0,0,3,0,0,0,1,0,3,1,0,0 } }, /*nonmaxsuppression_two_batches*/ + + nmsTF_test_params{ 0, { 1,2,6 }, boxes, + { 0.9, 0.75, 0.6, 0.95, 0.5, 0.3, 0.9, 0.75, 0.6, 0.95, 0.5, 0.3 },{ 2 },{ 0.5 },{ 0.0 }, 4,{ 0,0,3,0,0,0,0,1,3,0,1,0 } }, /*nonmaxsuppression_two_classes*/ + + nmsTF_test_params{ 0, { 1,1,6 }, boxes, scores, { 3 }, { 0.5 }, {}, 3, reference }, /*nonmaxsuppression_no_score_threshold*/ + + nmsTF_test_params{ 0, { 1,1,6 }, boxes, scores, { 3 }, {}, {}, 3, reference }, /*nonmaxsuppression_no_iou_threshold_and_score_threshold*/ + + nmsTF_test_params{ 0, { 1,1,6 }, boxes, scores, {}, {}, {}, 3, {} } /*nonmaxsuppression_no_max_output_boxes_per_class_and_iou_threshold_and_score_threshold*/ +)); diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/onehot_tests.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/onehot_tests.cpp index 08653b5c63b127..f4b02fc8efcf86 100644 --- a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/onehot_tests.cpp +++ b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/onehot_tests.cpp @@ -829,11 +829,6 @@ void ref_one_hot_5d(InferenceEngine::Blob &src, InferenceEngine::Blob &dst, one_ #define case_5d_3 one_hot_base_params({ {1, 3, 2, 3}, {4, 1, 3, 2, 3}, 2, 4, 1.0f, 0.0f }) #define case_5d_4 one_hot_base_params({ {1, 3, 2, 3}, {2, 1, 3, 4, 3}, 3, 4, 1.0f, 0.0f }) -std::string getTestCaseName(testing::TestParamInfo obj) { - return obj.param.libraryName + - "_" + getDeviceName(obj.param.targetDevice); -} - one_hot_test_params one_hot_only_1d_test_cases[] = { one_hot_test_params("MKLDNNPlugin", case_1d_0), one_hot_test_params("MKLDNNPlugin", case_1d_1) diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/reduce_tests.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/reduce_tests.cpp index d845a782671a4e..dc8ff4c0a3dcfa 100644 --- a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/reduce_tests.cpp +++ b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/reduce_tests.cpp @@ -81,6 +81,12 @@ void ref_reduce( InferenceEngine::SizeVector dstStrides = dst.getTensorDesc().getBlockingDesc().getStrides(); InferenceEngine::SizeVector skip_dims; + if (!dst_dims.size()) + dst_dims = InferenceEngine::SizeVector(1, 1); + + if (!dstStrides.size()) + dstStrides = InferenceEngine::SizeVector(1, 1); + if (axes_for_reduction.size() == 0) FAIL() << " Index vector should be 1 dimension"; @@ -283,7 +289,7 @@ class MKLDNNCPUExtReduceTests : public TestsCommon, public WithParamInterface\n"; - } - } else { - out_shape = "1\n"; + for (size_t i = 0; i < p.out_shape.size(); i++) { + out_shape += ""; + out_shape += std::to_string(p.out_shape[i]) + "\n"; } REPLACE_WITH_STR(model, "_OUT_", out_shape); diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/scatter_tests.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/scatter_tests.cpp new file mode 100644 index 00000000000000..0d498a779a585b --- /dev/null +++ b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/scatter_tests.cpp @@ -0,0 +1,205 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include +#include "mkldnn_plugin/mkldnn_graph.h" + +#include "test_graph.hpp" + +#include "single_layer_common.hpp" +#include +#include +#include "tests_common.hpp" + + +using namespace ::testing; +using namespace std; +using namespace mkldnn; + +struct scatterTF_test_params { + std::string inIdxPrecision; + InferenceEngine::SizeVector inDataDim; + std::vector inData; + InferenceEngine::SizeVector inIdxDim; + std::vector inIdx; + std::vector inUpd; + int axis; + + std::vector reference; + + std::vector> comp; +}; + +class MKLDNNCPUExtScatterTFTests : public TestsCommon, public WithParamInterface { + std::string model_t = R"V0G0N( + + + + + + _IDATA_ + + + + + + + _IIDX_ + + + + + + + _IIDX_ + + + + + + + + _IDATA_ + + + _IIDX_ + + + _IIDX_ + + + + + _IDATA_ + + + + + + + + + + +)V0G0N"; + + std::string getModel(scatterTF_test_params p) { + std::string model = model_t; + std::string inIdx; + std::string inData; + + for (auto& idx : p.inIdxDim) { + inIdx += ""; + inIdx += std::to_string(idx) + "\n"; + } + + for (auto& dct : p.inDataDim) { + inData += ""; + inData += std::to_string(dct) + "\n"; + } + + REPLACE_WITH_STR(model, "_IIDX_", inIdx); + REPLACE_WITH_STR(model, "_IIDXP_", p.inIdxPrecision); + REPLACE_WITH_STR(model, "_IDATA_", inData); + REPLACE_WITH_NUM(model, "_AX_", p.axis); + + return model; + } + +protected: + virtual void TearDown() { + } + + virtual void SetUp() { + try { + TestsCommon::SetUp(); + scatterTF_test_params p = ::testing::WithParamInterface::GetParam(); + std::string model = getModel(p); + //std::cout << model << std::endl; + InferenceEngine::CNNNetReader net_reader; + ASSERT_NO_THROW(net_reader.ReadNetwork(model.data(), model.length())); + + InferenceEngine::Extension cpuExt(make_so_name("cpu_extension")); + MKLDNNPlugin::MKLDNNExtensionManager::Ptr extMgr(new MKLDNNPlugin::MKLDNNExtensionManager()); + extMgr->AddExtension(InferenceEngine::IExtensionPtr(&cpuExt, [](InferenceEngine::IExtension*){})); + + MKLDNNGraphTestClass graph; + graph.CreateGraph(net_reader.getNetwork(), extMgr); + + // Input Data + InferenceEngine::Blob::Ptr srcData = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, p.inDataDim, InferenceEngine::TensorDesc::getLayoutByDims(p.inDataDim) }); + srcData->allocate(); + memcpy(srcData->buffer(), &p.inData[0], sizeof(float)*p.inData.size()); + auto * srcDataPtr = dynamic_cast*>(srcData.get()); + if (srcDataPtr == nullptr) + FAIL() << "Cannot cast blob to TBlob."; + + // Input Indexes + InferenceEngine::Blob::Ptr srcIdx; + if (p.inIdxPrecision == "I32") { + srcIdx = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::I32, p.inIdxDim, InferenceEngine::TensorDesc::getLayoutByDims(p.inIdxDim) }); + srcIdx->allocate(); + memcpy(static_cast(srcIdx->buffer()), &p.inIdx[0], sizeof(int32_t)*p.inIdx.size()); + auto * srcIdxPtr = dynamic_cast*>(srcIdx.get()); + if (srcIdxPtr == nullptr) + FAIL() << "Cannot cast blob to TBlob."; + } else { + srcIdx = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, p.inIdxDim, InferenceEngine::TensorDesc::getLayoutByDims(p.inIdxDim) }); + srcIdx->allocate(); + for (size_t i = 0; i < p.inIdx.size(); i++) { + static_cast(srcIdx->buffer())[i] = static_cast(p.inIdx[i]); + } + auto * srcIdxPtr = dynamic_cast*>(srcIdx.get()); + if (srcIdxPtr == nullptr) + FAIL() << "Cannot cast blob to TBlob."; + } + + // Input Updates + InferenceEngine::Blob::Ptr srcUpd; + srcUpd = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, p.inIdxDim, InferenceEngine::TensorDesc::getLayoutByDims(p.inIdxDim) }); + srcUpd->allocate(); + memcpy(static_cast(srcUpd->buffer()), &p.inUpd[0], sizeof(float)*p.inUpd.size()); + auto * srcUpdPtr = dynamic_cast*>(srcUpd.get()); + if (srcUpdPtr == nullptr) + FAIL() << "Cannot cast blob to TBlob."; + + // Output Data + InferenceEngine::OutputsDataMap out; + out = net_reader.getNetwork().getOutputsInfo(); + InferenceEngine::BlobMap outputBlobs; + std::pair item = *out.begin(); + InferenceEngine::TBlob::Ptr output; + output = InferenceEngine::make_shared_blob(item.second->getTensorDesc()); + output->allocate(); + outputBlobs[item.first] = output; + + // Infer + InferenceEngine::BlobMap srcs; + srcs.insert(std::pair("InputData", srcData)); + srcs.insert(std::pair("InputIndexes", srcIdx)); + srcs.insert(std::pair("InputUpdates", srcUpd)); + graph.Infer(srcs, outputBlobs); + + // Check results + if (memcmp((*output).data(), &p.reference[0], p.reference.size()) != 0) + FAIL() << "Wrong result with compare TF reference!"; + } catch (const InferenceEngine::details::InferenceEngineException &e) { + FAIL() << e.what(); + } + } +}; + +TEST_P(MKLDNNCPUExtScatterTFTests, TestsScatter) {} + +INSTANTIATE_TEST_CASE_P( + TestsScatter, MKLDNNCPUExtScatterTFTests, + ::testing::Values( +// Params: inDataDim, inData, inIdxDim, inIdx, inUpd, axis, reference + scatterTF_test_params{ "I32", { 3,3 },{ 0,0,0,0,0,0,0,0,0 },{ 2,3 },{ 1,0,2,0,2,1 },{ 1.,1.1,1.2,2,2.1,2.2 }, 0,{ 2,1.1,0,1,0,2.2,0,2.1,1.2 }}, + scatterTF_test_params{ "I32", { 3,3 },{ 0,0,0,0,0,0,0,0,0 },{ 2,3 },{ 1,0,2,0,2,1 },{ 1.,1.1,1.2,2,2.1,2.2 }, 1,{ 1.1,1,1.2,2,2.2,2.1,0,0,0 }}, + scatterTF_test_params{ "I32", { 1,5 },{ 1,2,3,4,5 },{ 1,2 },{ 1,3 },{ 1.1,2.1 }, 1,{ 1,1.1,3,2.1,5 }}, + scatterTF_test_params{"FP32", { 3,3 },{ 0,0,0,0,0,0,0,0,0 },{ 2,3 },{ 1,0,2,0,2,1 },{ 1.,1.1,1.2,2,2.1,2.2 }, 0,{ 2,1.1,0,1,0,2.2,0,2.1,1.2 }}, + scatterTF_test_params{"FP32", { 3,3 },{ 0,0,0,0,0,0,0,0,0 },{ 2,3 },{ 1,0,2,0,2,1 },{ 1.,1.1,1.2,2,2.1,2.2 }, 1,{ 1.1,1,1.2,2,2.2,2.1,0,0,0 }}, + scatterTF_test_params{"FP32", { 1,5 },{ 1,2,3,4,5 },{ 1,2 },{ 1,3 },{ 1.1,2.1 }, 1,{ 1,1.1,3,2.1,5 }})); diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/sparse_fill_empty_rows_tests.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/sparse_fill_empty_rows_tests.cpp new file mode 100644 index 00000000000000..b0a14115bfafaf --- /dev/null +++ b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/sparse_fill_empty_rows_tests.cpp @@ -0,0 +1,553 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include +#include "mkldnn_plugin/mkldnn_graph.h" + +#include "test_graph.hpp" + +#include "single_layer_common.hpp" +#include +#include +#include "tests_common.hpp" + +#include +#include +#include + +using namespace ::testing; +using namespace std; +using namespace mkldnn; + + +struct sparse_fill_empty_rows_test_params { + std::string precision; + InferenceEngine::SizeVector input_indices_shape; + std::vector input_indices_value; + + InferenceEngine::SizeVector input_values_shape; + + InferenceEngine::SizeVector input_dense_shape_shape; + std::vector input_dense_shape_value; + + InferenceEngine::SizeVector input_default_value_shape; + std::vector input_default_value_value; + + InferenceEngine::SizeVector output_indices_shape; + InferenceEngine::SizeVector output_values_shape; + InferenceEngine::SizeVector output_empty_rows_indicator_shape; + + size_t num_prim_desc; + int selectedType; + + std::vector> comp; +}; + +void ref_sparse_fill_empty_rows(InferenceEngine::TBlob &input_indices, + InferenceEngine::TBlob &input_values, + InferenceEngine::TBlob &dense_shape, + InferenceEngine::TBlob &default_value, + InferenceEngine::TBlob &output_indices, + InferenceEngine::TBlob &output_values, + InferenceEngine::TBlob &output_empty_rows_indicator) { + const float *input_indices_ptr = input_indices.data(); + const float *input_values_ptr = input_values.data(); + const float *dense_shape_ptr = dense_shape.data(); + const float *default_value_ptr = default_value.data(); + float dflt_value = default_value_ptr[0]; + + float num_rows = dense_shape_ptr[0]; + float num_cols = dense_shape_ptr[1]; + + std::vector dims = input_values.getTensorDesc().getDims(); + size_t inMaxNumValues = dims[0]; + std::vector out_dims = output_values.getTensorDesc().getDims(); + size_t outMaxNumValues = out_dims[0]; + + // compute actual number of values by searching out of range indice that serves as a marker + size_t in_actual_num_values = 0; + for (in_actual_num_values = 0; in_actual_num_values < inMaxNumValues; in_actual_num_values++) { + float indice_x = input_indices_ptr[2 * in_actual_num_values]; + float indice_y = input_indices_ptr[2 * in_actual_num_values + 1]; + if (indice_x < 0 || indice_y < 0 || indice_x >= num_rows || indice_y >= num_cols) break; + } + + // create auxiliary container for sorting + std::vector> indices_values(in_actual_num_values); // + for (size_t i = 0; i < in_actual_num_values; i++) { + float row = input_indices_ptr[2 * i]; + float col = input_indices_ptr[2 * i + 1]; + float value = input_values_ptr[i]; + std::array elem = { row, col, value }; + indices_values[i] = elem; + } + + // sort values by row + std::sort(indices_values.begin(), indices_values.end(), + [](const std::array& first, const std::array& second) { + return first[0] < second[0]; + }); + + // unsplit indices and values + std::vector indices_with_sorted_rows; + std::vector values_for_sorted_rows; + for (auto const & elem : indices_values) { + indices_with_sorted_rows.push_back(elem[0]); + indices_with_sorted_rows.push_back(elem[1]); + values_for_sorted_rows.push_back(elem[2]); + } + + // compute start indice for each row and a number of values at each row + std::vector values_at_row(num_rows); + std::fill(values_at_row.begin(), values_at_row.end(), 0); + float prev_row_with_value = -1.0; + unsigned int total_num_values = 0; + std::vector>::iterator curr_it, prev_it; + for (float row_ind = 0.0; row_ind < num_rows; row_ind = row_ind + 1.0) { + curr_it = std::find_if(indices_values.begin(), indices_values.end(), + [row_ind](std::array elem) { return elem[0] == row_ind; }); + if (curr_it != indices_values.end()) { + if (prev_row_with_value != -1.0) { + unsigned int num_values_at_prev_row = std::distance(prev_it, curr_it); + values_at_row[(int)prev_row_with_value] = num_values_at_prev_row; + total_num_values += num_values_at_prev_row; + } + prev_row_with_value = row_ind; + prev_it = curr_it; + } + else { + total_num_values++; + } + } + if (prev_row_with_value != -1.0) { + unsigned int num_values_at_prev_row = std::distance(prev_it, indices_values.end()); + values_at_row[(int)prev_row_with_value] = num_values_at_prev_row; + total_num_values += num_values_at_prev_row; + } + + // create output indices + float *output_indices_ptr = output_indices.data(); + float *output_values_ptr = output_values.data(); + float *output_empty_rows_indicator_ptr = output_empty_rows_indicator.data(); + + // zero output buffers + std::memset(output_indices_ptr, 0, outMaxNumValues * 2 * sizeof(float)); + std::memset(output_values_ptr, 0, outMaxNumValues * sizeof(float)); + std::memset(output_empty_rows_indicator_ptr, 0, num_rows * sizeof(float)); + + unsigned int curr_pos_from_copy = 0; + unsigned int curr_pos_to_copy = 0; + for (int row_ind = 0; row_ind < (int)num_rows; row_ind++) { + unsigned int num_values_at_row = values_at_row[row_ind]; + if (num_values_at_row == 0) { + output_empty_rows_indicator_ptr[row_ind] = 1.0; + output_values_ptr[curr_pos_to_copy] = dflt_value; + output_indices_ptr[curr_pos_to_copy * 2] = (float)row_ind; + output_indices_ptr[curr_pos_to_copy * 2 + 1] = 0.0; + curr_pos_to_copy++; + } + else { + output_empty_rows_indicator_ptr[row_ind] = 0.0; + std::copy(values_for_sorted_rows.begin() + curr_pos_from_copy, + values_for_sorted_rows.begin() + curr_pos_from_copy + num_values_at_row, + output_values_ptr + curr_pos_to_copy); + std::copy(indices_with_sorted_rows.begin() + 2 * curr_pos_from_copy, + indices_with_sorted_rows.begin() + 2 * curr_pos_from_copy + 2 * num_values_at_row, output_indices_ptr + 2 * curr_pos_to_copy); + curr_pos_to_copy += num_values_at_row; + curr_pos_from_copy += num_values_at_row; + } + } + + // mark the end of output using (-1, -1) indice + if (total_num_values < outMaxNumValues) { + output_indices_ptr[total_num_values * 2] = -1.0; + output_indices_ptr[total_num_values * 2 + 1] = -1.0; + } +} + +class MKLDNNCPUExtSparseFillEmptyRowsTests : public TestsCommon, public WithParamInterface { + std::string model_t = R"V0G0N( + + + + + + _IIN_ + + + + + + + _IVL_ + + + + + + + _IDS_ + + + + + + + _IDV_ + + + + + + + _IIN_ + + + _IVL_ + + + _IDS_ + + + _IDV_ + + + + + _OIN_ + + + _OVL_ + + + _ERI_ + + + + + + + + + + + +)V0G0N"; + + std::string getModel(sparse_fill_empty_rows_test_params p) { + std::string model = model_t; + std::string input_indices; + std::string input_values; + std::string dense_shape; + std::string default_value; + std::string output_indices; + std::string output_values; + std::string output_empty_rows_indicator; + + InferenceEngine::SizeVector input_dense_shape_shape = { 2 }; + + for (auto& shape : p.input_indices_shape) { + input_indices += ""; + input_indices += std::to_string(shape) + "\n"; + } + + for (auto& shape : p.input_values_shape) { + input_values += ""; + input_values += std::to_string(shape) + "\n"; + } + + for (auto& shape : input_dense_shape_shape) { + dense_shape += ""; + dense_shape += std::to_string(shape) + "\n"; + } + + for (auto& shape : p.input_default_value_shape) { + default_value += ""; + default_value += std::to_string(shape) + "\n"; + } + + for (auto& shape : p.output_indices_shape) { + output_indices += ""; + output_indices += std::to_string(shape) + "\n"; + } + + for (auto& shape : p.output_values_shape) { + output_values += ""; + output_values += std::to_string(shape) + "\n"; + } + + for (auto& shape : p.output_empty_rows_indicator_shape) { + output_empty_rows_indicator += ""; + output_empty_rows_indicator += std::to_string(shape) + "\n"; + } + + REPLACE_WITH_STR(model, "_IIN_", input_indices); + REPLACE_WITH_STR(model, "_IVL_", input_values); + REPLACE_WITH_STR(model, "_IDS_", dense_shape); + REPLACE_WITH_STR(model, "_IDV_", default_value); + REPLACE_WITH_STR(model, "_OIN_", output_indices); + REPLACE_WITH_STR(model, "_OVL_", output_values); + REPLACE_WITH_STR(model, "_ERI_", output_empty_rows_indicator); + + return model; + } + + template + static void fill_data_dbgval(data_t *data, size_t size) { + for (size_t i = 0; i < size; i++) { + data[i] = static_cast(i & (sizeof(data_t) * 8 - 1)); + } + } +protected: + virtual void TearDown() { + } + + virtual void SetUp() { + try { + TestsCommon::SetUp(); + sparse_fill_empty_rows_test_params p = ::testing::WithParamInterface::GetParam(); + std::string model = getModel(p); + + InferenceEngine::CNNNetReader net_reader; + ASSERT_NO_THROW(net_reader.ReadNetwork(model.data(), model.length())); + + InferenceEngine::Extension cpuExt(make_so_name("cpu_extension")); + MKLDNNPlugin::MKLDNNExtensionManager::Ptr extMgr(new MKLDNNPlugin::MKLDNNExtensionManager()); + extMgr->AddExtension(InferenceEngine::IExtensionPtr(&cpuExt, [](InferenceEngine::IExtension*) {})); + + MKLDNNGraphTestClass graph; + graph.CreateGraph(net_reader.getNetwork(), extMgr); + + auto& nodes = graph.getNodes(); + nodes = graph.getNodes(); + + for (auto &node : nodes) { + if (node->getName() == "SparseFillEmptyRows") { + ASSERT_EQ(p.num_prim_desc, node->getSupportedPrimitiveDescriptors().size()); + for (size_t j = 0; j < p.num_prim_desc && j < p.comp.size(); j++) { + p.comp.at(j)(node->getSupportedPrimitiveDescriptors().at(j)); + } + ASSERT_NE(nullptr, node->getSelectedPrimitiveDescriptor()); + ASSERT_EQ(p.selectedType, + node->getSelectedPrimitiveDescriptor()->getImplementationType() & p.selectedType); + } + } + // 4 inputs + 1 op + 3 outputs + ASSERT_EQ(8, nodes.size()); + + // Input Data + InferenceEngine::Blob::Ptr input_indices = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, + p.input_indices_shape, InferenceEngine::TensorDesc::getLayoutByDims(p.input_indices_shape) }); + input_indices->allocate(); + auto *input_indices_ptr = dynamic_cast*>(input_indices.get()); + std::copy(p.input_indices_value.begin(), p.input_indices_value.end(), (float *) input_indices_ptr->data()); + + InferenceEngine::Blob::Ptr input_values = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, + p.input_values_shape, InferenceEngine::TensorDesc::getLayoutByDims(p.input_values_shape) }); + input_values->allocate(); + fill_data(input_values->buffer(), input_values->size()); + + auto *input_values_ptr = dynamic_cast*>(input_values.get()); + InferenceEngine::Blob::Ptr input_dense_shape = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, + p.input_dense_shape_shape, InferenceEngine::TensorDesc::getLayoutByDims(p.input_dense_shape_shape) }); + input_dense_shape->allocate(); + auto *input_dense_shape_ptr = dynamic_cast*>(input_dense_shape.get()); + std::copy(p.input_dense_shape_value.begin(), p.input_dense_shape_value.end(), (float *) input_dense_shape_ptr->data()); + + InferenceEngine::Blob::Ptr input_default_value = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, + p.input_default_value_shape, InferenceEngine::TensorDesc::getLayoutByDims(p.input_default_value_shape) }); + input_default_value->allocate(); + auto *input_default_value_ptr = dynamic_cast*>(input_default_value.get()); + std::copy(p.input_default_value_value.begin(), p.input_default_value_value.end(), (float *) input_default_value_ptr->data()); + + // Output Data + InferenceEngine::OutputsDataMap out; + out = net_reader.getNetwork().getOutputsInfo(); + InferenceEngine::BlobMap output_blobs; + auto iter = out.begin(); + + std::pair item = *(iter++); + InferenceEngine::Blob::Ptr output_indices = InferenceEngine::make_shared_blob(item.second->getTensorDesc()); + output_indices->allocate(); + output_blobs[item.first] = output_indices; + InferenceEngine::TBlob output_indices_ref(item.second->getTensorDesc()); + output_indices_ref.allocate(); + + item = *(iter++); + InferenceEngine::Blob::Ptr output_values = InferenceEngine::make_shared_blob(item.second->getTensorDesc()); + output_values->allocate(); + output_blobs[item.first] = output_values; + InferenceEngine::TBlob output_values_ref(item.second->getTensorDesc()); + output_values_ref.allocate(); + + item = *(iter++); + InferenceEngine::Blob::Ptr output_empty_rows_indicator = InferenceEngine::make_shared_blob(item.second->getTensorDesc()); + output_empty_rows_indicator->allocate(); + output_blobs[item.first] = output_empty_rows_indicator; + InferenceEngine::TBlob output_empty_rows_indicator_ref(item.second->getTensorDesc()); + output_empty_rows_indicator_ref.allocate(); + + // Compute reference result + ref_sparse_fill_empty_rows(*input_indices_ptr, *input_values_ptr, *input_dense_shape_ptr, *input_default_value_ptr, + output_indices_ref, output_values_ref, output_empty_rows_indicator_ref); + + // Compute IE result + InferenceEngine::BlobMap inputs; + inputs.insert(std::pair("InputIndices", input_indices)); + inputs.insert(std::pair("InputValues", input_values)); + inputs.insert(std::pair("InputDenseShape", input_dense_shape)); + inputs.insert(std::pair("InputDefaultValue", input_default_value)); + + // Check the result + graph.Infer(inputs, output_blobs); + compare(*output_indices, output_indices_ref, 0.0f); + compare(*output_values, output_values_ref, 0.0f); + compare(*output_empty_rows_indicator, output_empty_rows_indicator_ref, 0.0f); + } + catch (const InferenceEngine::details::InferenceEngineException &e) { + FAIL() << e.what(); + } + } +}; + +TEST_P(MKLDNNCPUExtSparseFillEmptyRowsTests, TestsSparseFillEmptyRows) {} + + +// case 1 - empty sparse tensor with marker +InferenceEngine::SizeVector input_indices_shape_case1 = {2, 2}; +std::vector input_indices_value_case1 = {-1.f, -1.f}; +InferenceEngine::SizeVector input_values_shape_case1 = {2}; +InferenceEngine::SizeVector input_dense_shape_shape_case1 = {2}; +std::vector input_dense_shape_value_case1 = {3.f, 4.f}; +InferenceEngine::SizeVector input_default_value_shape_case1 = {1}; +std::vector input_default_value_case1 = {0.f}; +InferenceEngine::SizeVector output_indices_shape_case1 = {12, 2}; +InferenceEngine::SizeVector output_values_shape_case1 = {12}; +InferenceEngine::SizeVector output_empty_rows_indicator_shape_case1 = {3}; + +// case 2 - in one row all values absent without marker +InferenceEngine::SizeVector input_indices_shape_case2 = {6, 2}; +std::vector input_indices_value_case2 = {1.f, 0.f, 0.f, 0.f, 3.f, 1.f, 1.f, 2.f, 3.f, 4.f, 0.f, 1.f}; +InferenceEngine::SizeVector input_values_shape_case2 = {6}; +InferenceEngine::SizeVector input_dense_shape_shape_case2 = {2}; +std::vector input_dense_shape_value_case2 = {4.f, 5.f}; +InferenceEngine::SizeVector input_default_value_shape_case2 = {1}; +std::vector input_default_value_case2 = {0.f}; +InferenceEngine::SizeVector output_indices_shape_case2 = {20, 2}; +InferenceEngine::SizeVector output_values_shape_case2 = {20}; +InferenceEngine::SizeVector output_empty_rows_indicator_shape_case2 = {4}; + +// case 3 - in one row all values absent with marker +InferenceEngine::SizeVector input_indices_shape_case3 = { 6, 2 }; +std::vector input_indices_value_case3 = { 1.f, 0.f, 0.f, 0.f, 3.f, 1.f, 1.f, 2.f, 3.f, 4.f, -1.f, -1.f }; +InferenceEngine::SizeVector input_values_shape_case3 = { 6 }; +InferenceEngine::SizeVector input_dense_shape_shape_case3 = { 2 }; +std::vector input_dense_shape_value_case3 = { 4.f, 5.f }; +InferenceEngine::SizeVector input_default_value_shape_case3 = { 1 }; +std::vector input_default_value_case3 = { 0.f }; +InferenceEngine::SizeVector output_indices_shape_case3 = { 20, 2 }; +InferenceEngine::SizeVector output_values_shape_case3 = { 20 }; +InferenceEngine::SizeVector output_empty_rows_indicator_shape_case3 = { 4 }; + +// case 4 - in all rows at least one value presents without marker +InferenceEngine::SizeVector input_indices_shape_case4 = { 7, 2 }; +std::vector input_indices_value_case4 = { 1.f, 0.f, 0.f, 0.f, 3.f, 1.f, 1.f, 2.f, 3.f, 3.f, 2.f, 1.f, 4.f, 3.f }; +InferenceEngine::SizeVector input_values_shape_case4 = { 7 }; +InferenceEngine::SizeVector input_dense_shape_shape_case4 = { 2 }; +std::vector input_dense_shape_value_case4 = { 5.f, 4.f }; +InferenceEngine::SizeVector input_default_value_shape_case4 = { 1 }; +std::vector input_default_value_case4 = { 0.f }; +InferenceEngine::SizeVector output_indices_shape_case4 = { 20, 2 }; +InferenceEngine::SizeVector output_values_shape_case4 = { 20 }; +InferenceEngine::SizeVector output_empty_rows_indicator_shape_case4 = { 5 }; + +// case 5 - in all rows at least one value presents with marker +InferenceEngine::SizeVector input_indices_shape_case5 = { 8, 2 }; +std::vector input_indices_value_case5 = { 1.f, 0.f, 0.f, 0.f, 3.f, 1.f, 1.f, 2.f, 3.f, 3.f, 2.f, 1.f, 4.f, 3.f, -1.f, -1.f }; +InferenceEngine::SizeVector input_values_shape_case5 = { 8 }; +InferenceEngine::SizeVector input_dense_shape_shape_case5 = { 2 }; +std::vector input_dense_shape_value_case5 = { 5.f, 4.f }; +InferenceEngine::SizeVector input_default_value_shape_case5 = { 1 }; +std::vector input_default_value_case5 = { 0.f }; +InferenceEngine::SizeVector output_indices_shape_case5 = { 20, 2 }; +InferenceEngine::SizeVector output_values_shape_case5 = { 20 }; +InferenceEngine::SizeVector output_empty_rows_indicator_shape_case5 = { 5 }; + +// case 6 - big sparse tensor with many missed rows without marker +InferenceEngine::SizeVector input_indices_shape_case6 = { 7, 2 }; +std::vector input_indices_value_case6 = { 1.f, 0.f, 0.f, 0.f, 99.f, 19.f, 12.f, 2.f, 37.f, 13.f, 2.f, 1.f, 45.f, 3.f }; +InferenceEngine::SizeVector input_values_shape_case6 = { 7 }; +InferenceEngine::SizeVector input_dense_shape_shape_case6 = { 2 }; +std::vector input_dense_shape_value_case6 = { 100.f, 20.f }; +InferenceEngine::SizeVector input_default_value_shape_case6 = { 1 }; +std::vector input_default_value_case6 = { 0.f }; +InferenceEngine::SizeVector output_indices_shape_case6 = { 2000, 2 }; +InferenceEngine::SizeVector output_values_shape_case6 = { 2000 }; +InferenceEngine::SizeVector output_empty_rows_indicator_shape_case6 = { 100 }; + +// case 7 - big sparse tensor with many missed rows with marker +InferenceEngine::SizeVector input_indices_shape_case7 = { 8, 2 }; +std::vector input_indices_value_case7 = { 1.f, 0.f, 0.f, 0.f, 99.f, 19.f, 12.f, 2.f, 37.f, 13.f, 2.f, 1.f, 45.f, 3.f, -1.f, -1.f }; +InferenceEngine::SizeVector input_values_shape_case7 = { 8 }; +InferenceEngine::SizeVector input_dense_shape_shape_case7 = { 2 }; +std::vector input_dense_shape_value_case7 = { 100.f, 20.f }; +InferenceEngine::SizeVector input_default_value_shape_case7 = { 1 }; +std::vector input_default_value_case7 = { 0.f }; +InferenceEngine::SizeVector output_indices_shape_case7 = { 2000, 2 }; +InferenceEngine::SizeVector output_values_shape_case7 = { 2000 }; +InferenceEngine::SizeVector output_empty_rows_indicator_shape_case7 = { 100 }; + +INSTANTIATE_TEST_CASE_P( + TestsSparseFillEmptyRows, MKLDNNCPUExtSparseFillEmptyRowsTests, + ::testing::Values( + // case 1 - empty sparse tensor without marker + sparse_fill_empty_rows_test_params{ "FP32", + input_indices_shape_case1, input_indices_value_case1, input_values_shape_case1, + input_dense_shape_shape_case1, input_dense_shape_value_case1, input_default_value_shape_case1, input_default_value_case1, + output_indices_shape_case1, output_values_shape_case1, output_empty_rows_indicator_shape_case1, + 1, MKLDNNPlugin::impl_desc_type::unknown }, + + // case 2 - in one row all values absent without marker + sparse_fill_empty_rows_test_params{ "FP32", + input_indices_shape_case2, input_indices_value_case2, input_values_shape_case2, + input_dense_shape_shape_case2, input_dense_shape_value_case2, input_default_value_shape_case2, input_default_value_case2, + output_indices_shape_case2, output_values_shape_case2, output_empty_rows_indicator_shape_case2, + 1, MKLDNNPlugin::impl_desc_type::unknown }, + + // case 3 - in one row all values absent with marker + sparse_fill_empty_rows_test_params{ "FP32", + input_indices_shape_case3, input_indices_value_case3, input_values_shape_case3, + input_dense_shape_shape_case3, input_dense_shape_value_case3, input_default_value_shape_case3, input_default_value_case3, + output_indices_shape_case3, output_values_shape_case3, output_empty_rows_indicator_shape_case3, + 1, MKLDNNPlugin::impl_desc_type::unknown }, + + // case 4 - in all rows at least one value presents without marker + sparse_fill_empty_rows_test_params{ "FP32", + input_indices_shape_case4, input_indices_value_case4, input_values_shape_case4, + input_dense_shape_shape_case4, input_dense_shape_value_case4, input_default_value_shape_case4, input_default_value_case4, + output_indices_shape_case4, output_values_shape_case4, output_empty_rows_indicator_shape_case4, + 1, MKLDNNPlugin::impl_desc_type::unknown }, + + // case 5 - in all rows at least one value presents with marker + sparse_fill_empty_rows_test_params{ "FP32", + input_indices_shape_case5, input_indices_value_case5, input_values_shape_case5, + input_dense_shape_shape_case5, input_dense_shape_value_case5, input_default_value_shape_case5, input_default_value_case5, + output_indices_shape_case5, output_values_shape_case5, output_empty_rows_indicator_shape_case5, + 1, MKLDNNPlugin::impl_desc_type::unknown }, + + // case 6 - big sparse tensor with many missed rows without marker + sparse_fill_empty_rows_test_params{ "FP32", + input_indices_shape_case6, input_indices_value_case6, input_values_shape_case6, + input_dense_shape_shape_case6, input_dense_shape_value_case6, input_default_value_shape_case6, input_default_value_case6, + output_indices_shape_case6, output_values_shape_case6, output_empty_rows_indicator_shape_case6, + 1, MKLDNNPlugin::impl_desc_type::unknown }, + + // case 7 - big sparse tensor with many missed rows with marker + sparse_fill_empty_rows_test_params{ "FP32", + input_indices_shape_case7, input_indices_value_case7, input_values_shape_case7, + input_dense_shape_shape_case7, input_dense_shape_value_case7, input_default_value_shape_case7, input_default_value_case7, + output_indices_shape_case7, output_values_shape_case7, output_empty_rows_indicator_shape_case7, + 1, MKLDNNPlugin::impl_desc_type::unknown } + )); diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/topk_tests.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/topk_tests.cpp index 9f28a8174e064e..d1ec622cf73618 100644 --- a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/topk_tests.cpp +++ b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/topk_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2019 Intel Corporation +// Copyright (C) 2018-2019 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // @@ -131,7 +131,7 @@ static void ref_topk(InferenceEngine::TBlob &src, InferenceEngine::TBlob< } if (!sort_value) - std::sort(src_vector.begin(), src_vector.begin() + src_k, [&src_vector](const pair &a, const pair &b) + std::sort(src_vector.begin(), src_vector.begin() + src_k, [](const pair &a, const pair &b) { return (a.second < b.second); }); for (int j = 0; j < src_k; ++j) { @@ -367,9 +367,7 @@ class MKLDNNCPUExtTopK1OutTests : public TestsCommon, public WithParamInterface< - - 1 - + @@ -379,7 +377,6 @@ class MKLDNNCPUExtTopK1OutTests : public TestsCommon, public WithParamInterface< _IN_ - 1 @@ -445,8 +442,8 @@ class MKLDNNCPUExtTopK1OutTests : public TestsCommon, public WithParamInterface< graph.CreateGraph(net_reader.getNetwork(), extMgr); // Input Data - InferenceEngine::Blob::Ptr src; - src = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, p.in_shape, InferenceEngine::TensorDesc::getLayoutByDims(p.in_shape) }); + InferenceEngine::Blob::Ptr src = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, p.in_shape, + InferenceEngine::TensorDesc::getLayoutByDims(p.in_shape) }); src->allocate(); if (p.input_tensor.size()) memcpy(src->buffer(), &p.input_tensor[0], sizeof(float)*p.input_tensor.size()); @@ -458,10 +455,8 @@ class MKLDNNCPUExtTopK1OutTests : public TestsCommon, public WithParamInterface< InferenceEngine::BlobMap srcs; srcs.insert(std::pair("value", src)); - - InferenceEngine::Blob::Ptr seq_lengthsIdx; - InferenceEngine::SizeVector seq_lengths_dim(1, 1); - seq_lengthsIdx = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::I32, seq_lengths_dim, InferenceEngine::TensorDesc::getLayoutByDims(seq_lengths_dim) }); + InferenceEngine::Blob::Ptr seq_lengthsIdx = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::I32, {}, + InferenceEngine::TensorDesc::getLayoutByDims({})}); seq_lengthsIdx->allocate(); memcpy(static_cast(seq_lengthsIdx->buffer()), &p.src_k[0], sizeof(int32_t)); auto * seq_lengthsIdxPtr = dynamic_cast*>(seq_lengthsIdx.get()); @@ -492,7 +487,8 @@ class MKLDNNCPUExtTopK1OutTests : public TestsCommon, public WithParamInterface< } } else { InferenceEngine::TBlob::Ptr output; - output = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::I32, p.out_shape, InferenceEngine::TensorDesc::getLayoutByDims(p.out_shape) }); + output = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::I32, p.out_shape, + InferenceEngine::TensorDesc::getLayoutByDims(p.out_shape) }); output->allocate(); outputBlobs[item.first] = output; diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/unique_tests.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/unique_tests.cpp new file mode 100644 index 00000000000000..b17369c1d9c6a8 --- /dev/null +++ b/inference-engine/tests/unit/engines/mkldnn/graph/layers/extensions/unique_tests.cpp @@ -0,0 +1,378 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include +#include "mkldnn_plugin/mkldnn_graph.h" + +#include "test_graph.hpp" + +#include "single_layer_common.hpp" +#include +#include +#include "tests_common.hpp" + +#include +#include + +using namespace ::testing; +using namespace std; +using namespace mkldnn; + + +struct unique_test_params { + std::string model; + + std::string precision; + + std::string sorted; + std::string return_inverse; + std::string return_counts; + + InferenceEngine::SizeVector input_shape; + std::vector input_value; + + InferenceEngine::SizeVector output_uniques_shape; + InferenceEngine::SizeVector output_indices_shape; + InferenceEngine::SizeVector output_counts_shape; + + std::vector output_uniques_value_ref; + std::vector output_indices_value_ref; + std::vector output_counts_value_ref; + + size_t num_prim_desc; + int selectedType; + + std::vector> comp; +}; + +class MKLDNNCPUExtUniqueTests : public TestsCommon, public WithParamInterface { + std::string getModel(unique_test_params p) { + std::string model = p.model; + + std::string input_shape; + std::string output_uniques_shape; + std::string output_indices_shape; + std::string output_counts_shape; + + for (auto& shape : p.input_shape) { + input_shape += ""; + input_shape += std::to_string(shape) + "\n"; + } + + for (auto& shape : p.output_uniques_shape) { + output_uniques_shape += ""; + output_uniques_shape += std::to_string(shape) + "\n"; + } + + for (auto& shape : p.output_indices_shape) { + output_indices_shape += ""; + output_indices_shape += std::to_string(shape) + "\n"; + } + + for (auto& shape : p.output_counts_shape) { + output_counts_shape += ""; + output_counts_shape += std::to_string(shape) + "\n"; + } + + REPLACE_WITH_STR(model, "_SORTED_", p.sorted); + REPLACE_WITH_STR(model, "_INPUT_SHAPE_", input_shape); + REPLACE_WITH_STR(model, "_OUTPUT_UNIQUES_SHAPE_", output_uniques_shape); + REPLACE_WITH_STR(model, "_OUTPUT_INDICES_SHAPE_", output_indices_shape); + REPLACE_WITH_STR(model, "_OUTPUT_COUNTS_SHAPE_", output_counts_shape); + + return model; + } + +protected: + virtual void TearDown() { + } + + virtual void SetUp() { + try { + TestsCommon::SetUp(); + unique_test_params p = ::testing::WithParamInterface::GetParam(); + std::string model = getModel(p); + + InferenceEngine::CNNNetReader net_reader; + ASSERT_NO_THROW(net_reader.ReadNetwork(model.data(), model.length())); + + InferenceEngine::Extension cpuExt(make_so_name("cpu_extension")); + MKLDNNPlugin::MKLDNNExtensionManager::Ptr extMgr(new MKLDNNPlugin::MKLDNNExtensionManager()); + extMgr->AddExtension(InferenceEngine::IExtensionPtr(&cpuExt, [](InferenceEngine::IExtension*) {})); + + MKLDNNGraphTestClass graph; + graph.CreateGraph(net_reader.getNetwork(), extMgr); + + auto& nodes = graph.getNodes(); + nodes = graph.getNodes(); + + for (auto &node : nodes) { + if (node->getName() == "Unique") { + ASSERT_EQ(p.num_prim_desc, node->getSupportedPrimitiveDescriptors().size()); + for (size_t j = 0; j < p.num_prim_desc && j < p.comp.size(); j++) { + p.comp.at(j)(node->getSupportedPrimitiveDescriptors().at(j)); + } + ASSERT_NE(nullptr, node->getSelectedPrimitiveDescriptor()); + ASSERT_EQ(p.selectedType, + node->getSelectedPrimitiveDescriptor()->getImplementationType() & p.selectedType); + } + } + + // prepare input blob and input blob map + InferenceEngine::Blob::Ptr input = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, + p.input_shape, InferenceEngine::TensorDesc::getLayoutByDims(p.input_shape) }); + input->allocate(); + auto *input_ptr = dynamic_cast*>(input.get()); + std::copy(p.input_value.begin(), p.input_value.end(), (float *)input_ptr->data()); + InferenceEngine::BlobMap input_blob_map; + input_blob_map["InputValues"] = input; + + // prepare output blob map + InferenceEngine::OutputsDataMap out = net_reader.getNetwork().getOutputsInfo(); + InferenceEngine::BlobMap output_blob_map; + for (auto iter = out.begin(); iter != out.end(); iter++) { + std::pair item = *iter; + InferenceEngine::Blob::Ptr output_blob_ptr = InferenceEngine::make_shared_blob(item.second->getTensorDesc()); + output_blob_ptr->allocate(); + output_blob_map[item.first] = output_blob_ptr; + } + + // prepare blobs with reference data + InferenceEngine::Blob::Ptr output_uniques_blob_ref = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, + p.output_uniques_shape, InferenceEngine::TensorDesc::getLayoutByDims(p.output_uniques_shape) }); + output_uniques_blob_ref->allocate(); + auto *output_uniques_blob_ref_ptr = dynamic_cast*>(output_uniques_blob_ref.get()); + std::copy(p.output_uniques_value_ref.begin(), p.output_uniques_value_ref.end(), (float *)output_uniques_blob_ref_ptr->data()); + + InferenceEngine::Blob::Ptr output_indices_blob_ref = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, + p.output_indices_shape, InferenceEngine::TensorDesc::getLayoutByDims(p.output_indices_shape) }); + output_indices_blob_ref->allocate(); + auto *output_indices_blob_ref_ptr = dynamic_cast*>(output_indices_blob_ref.get()); + std::copy(p.output_indices_value_ref.begin(), p.output_indices_value_ref.end(), (float *)output_indices_blob_ref_ptr->data()); + + InferenceEngine::Blob::Ptr output_counts_blob_ref = InferenceEngine::make_shared_blob({ InferenceEngine::Precision::FP32, + p.output_counts_shape, InferenceEngine::TensorDesc::getLayoutByDims(p.output_counts_shape) }); + output_counts_blob_ref->allocate(); + auto *output_counts_blob_ref_ptr = dynamic_cast*>(output_counts_blob_ref.get()); + std::copy(p.output_counts_value_ref.begin(), p.output_counts_value_ref.end(), (float *)output_counts_blob_ref_ptr->data()); + + // infer + graph.Infer(input_blob_map, output_blob_map); + + // check the result + auto iter = out.begin(); + compare(*output_blob_map[iter->first], *output_uniques_blob_ref, 0.0f); + if (p.return_inverse == "true") { + iter++; + compare(*output_blob_map[iter->first], *output_indices_blob_ref, 0.0f); + } + if (p.return_counts == "true") { + iter++; + compare(*output_blob_map[iter->first], *output_counts_blob_ref, 0.0f); + } + } + catch (const InferenceEngine::details::InferenceEngineException &e) { + FAIL() << e.what(); + } + } +}; + +TEST_P(MKLDNNCPUExtUniqueTests, TestsUnique) {} + +// model 1 that contains one Unique layer with two outputs: unique elements, indices +std::string model1 = R"V0G0N( + + + + + + _INPUT_SHAPE_ + + + + + + + + _INPUT_SHAPE_ + + + + + _OUTPUT_UNIQUES_SHAPE_ + + + _OUTPUT_INDICES_SHAPE_ + + + + + + + + +)V0G0N"; + +// model 2 that contains one Unique layer with three outputs: unique elements, indices, counts +std::string model2 = R"V0G0N( + + + + + + _INPUT_SHAPE_ + + + + + + + + _INPUT_SHAPE_ + + + + + _OUTPUT_UNIQUES_SHAPE_ + + + _OUTPUT_INDICES_SHAPE_ + + + _OUTPUT_COUNTS_SHAPE_ + + + + + + + + +)V0G0N"; + +// case 1 - input with 10 elements where some of them repeat, non-sorted +InferenceEngine::SizeVector input_shape_case1 = { 10 }; +std::vector input_value_case1 = { 8.f, 1.f, 2.f, 1.f, 8.f, 5.f, 1.f, 5.f, 0.f, 0.f }; +InferenceEngine::SizeVector output_uniques_shape_case1 = { 10 }; +InferenceEngine::SizeVector output_indicess_shape_case1 = { 10 }; +InferenceEngine::SizeVector output_counts_shape_case1 = { 10 }; +std::vector output_uniques_value_ref_case1 = { 8.f, 1.f, 2.f, 5.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f }; +std::vector output_indices_value_ref_case1 = { 0.f, 1.f, 2.f, 1.f, 0.f, 3.f, 1.f, 3.f, 4.f, 4.f }; +std::vector output_counts_value_ref_case1 = { 2.f, 3.f, 1.f, 2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f }; + +// case 2 - input with 10 elements where all of them are unique, non-sorted +InferenceEngine::SizeVector input_shape_case2 = { 10 }; +std::vector input_value_case2 = { 8.f, 1.f, 2.f, 3.f, 10.f, 5.f, 12.f, 15.f, 0.f, 100.f }; +InferenceEngine::SizeVector output_uniques_shape_case2 = { 10 }; +InferenceEngine::SizeVector output_indicess_shape_case2 = { 10 }; +InferenceEngine::SizeVector output_counts_shape_case2 = { 10 }; +std::vector output_uniques_value_ref_case2 = { 8.f, 1.f, 2.f, 3.f, 10.f, 5.f, 12.f, 15.f, 0.f, 100.f }; +std::vector output_indices_value_ref_case2 = { 0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f }; +std::vector output_counts_value_ref_case2 = { 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f }; + +// case 3 - input with 10 elements where all of them are the same, non-sorted +InferenceEngine::SizeVector input_shape_case3 = { 10 }; +std::vector input_value_case3 = { 8.f, 8.f, 8.f, 8.f, 8.f, 8.f, 8.f, 8.f, 8.f, 8.f }; +InferenceEngine::SizeVector output_uniques_shape_case3 = { 10 }; +InferenceEngine::SizeVector output_indicess_shape_case3 = { 10 }; +InferenceEngine::SizeVector output_counts_shape_case3 = { 10 }; +std::vector output_uniques_value_ref_case3 = { 8.f, 8.f, 8.f, 8.f, 8.f, 8.f, 8.f, 8.f, 8.f, 8.f }; +std::vector output_indices_value_ref_case3 = { 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f }; +std::vector output_counts_value_ref_case3 = { 10.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f }; + +// case 4 - input with 10 elements where some of them repeat, sorted +InferenceEngine::SizeVector input_shape_case4 = { 10 }; +std::vector input_value_case4 = { 8.f, 1.f, 2.f, 1.f, 8.f, 5.f, 1.f, 5.f, 0.f, 0.f }; +InferenceEngine::SizeVector output_uniques_shape_case4 = { 10 }; +InferenceEngine::SizeVector output_indicess_shape_case4 = { 10 }; +InferenceEngine::SizeVector output_counts_shape_case4 = { 10 }; +std::vector output_uniques_value_ref_case4 = { 0.f, 1.f, 2.f, 5.f, 8.f, 8.f, 8.f, 8.f, 8.f, 8.f }; +std::vector output_indices_value_ref_case4 = { 4.f, 1.f, 2.f, 1.f, 4.f, 3.f, 1.f, 3.f, 0.f, 0.f }; +std::vector output_counts_value_ref_case4 = { 2.f, 3.f, 1.f, 2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f }; + +// case 5 - input with 10 elements where all of them are unique, sorted +InferenceEngine::SizeVector input_shape_case5 = { 10 }; +std::vector input_value_case5 = { 8.f, 1.f, 2.f, 3.f, 10.f, 5.f, 12.f, 15.f, 0.f, 100.f }; +InferenceEngine::SizeVector output_uniques_shape_case5 = { 10 }; +InferenceEngine::SizeVector output_indicess_shape_case5 = { 10 }; +InferenceEngine::SizeVector output_counts_shape_case5 = { 10 }; +std::vector output_uniques_value_ref_case5 = { 0.f, 1.f, 2.f, 3.f, 5.f, 8.f, 10.f, 12.f, 15.f, 100.f }; +std::vector output_indices_value_ref_case5 = { 5.f, 1.f, 2.f, 3.f, 6.f, 4.f, 7.f, 8.f, 0.f, 9.f }; +std::vector output_counts_value_ref_case5 = { 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f }; + +INSTANTIATE_TEST_CASE_P( + TestsUnique, MKLDNNCPUExtUniqueTests, + ::testing::Values( + // case 0 - model1, sorted="false", input with 10 elements where some of them repeat + unique_test_params { + model1, "FP32", "false", "true", "false", input_shape_case1, input_value_case1, + output_uniques_shape_case1, output_indicess_shape_case1, output_counts_shape_case1, + output_uniques_value_ref_case1, output_indices_value_ref_case1, output_counts_value_ref_case1, + 1, MKLDNNPlugin::impl_desc_type::unknown + }, + // case 1 - model1, sorted="false", input with 10 elements where all of them are unique + unique_test_params{ + model1, "FP32", "false", "true", "false", input_shape_case2, input_value_case2, + output_uniques_shape_case2, output_indicess_shape_case2, output_counts_shape_case2, + output_uniques_value_ref_case2, output_indices_value_ref_case2, output_counts_value_ref_case2, + 1, MKLDNNPlugin::impl_desc_type::unknown + }, + // case 2 - model1, sorted="false", input with 10 elements where all of them are the same + unique_test_params{ + model1, "FP32", "false", "true", "false", input_shape_case3, input_value_case3, + output_uniques_shape_case3, output_indicess_shape_case3, output_counts_shape_case3, + output_uniques_value_ref_case3, output_indices_value_ref_case3, output_counts_value_ref_case3, + 1, MKLDNNPlugin::impl_desc_type::unknown + }, + // case 3 - model1, sorted="true", input with 10 elements where some of them repeat + unique_test_params{ + model1, "FP32", "true", "true", "false", input_shape_case4, input_value_case4, + output_uniques_shape_case4, output_indicess_shape_case4, output_counts_shape_case4, + output_uniques_value_ref_case4, output_indices_value_ref_case4, output_counts_value_ref_case4, + 1, MKLDNNPlugin::impl_desc_type::unknown + }, + // case 4 - model1, sorted="true", input with 10 elements where all of them are unique + unique_test_params{ + model1, "FP32", "true", "true", "false", input_shape_case5, input_value_case5, + output_uniques_shape_case5, output_indicess_shape_case5, output_counts_shape_case5, + output_uniques_value_ref_case5, output_indices_value_ref_case5, output_counts_value_ref_case5, + 1, MKLDNNPlugin::impl_desc_type::unknown + }, + // case 5 - model2, sorted="false", input with 10 elements where some of them repeat + unique_test_params{ + model2, "FP32", "false", "true", "true", input_shape_case1, input_value_case1, + output_uniques_shape_case1, output_indicess_shape_case1, output_counts_shape_case1, + output_uniques_value_ref_case1, output_indices_value_ref_case1, output_counts_value_ref_case1, + 1, MKLDNNPlugin::impl_desc_type::unknown + }, + // case 6 - model2, sorted="false", input with 10 elements where all of them are unique + unique_test_params{ + model2, "FP32", "false", "true", "true", input_shape_case2, input_value_case2, + output_uniques_shape_case2, output_indicess_shape_case2, output_counts_shape_case2, + output_uniques_value_ref_case2, output_indices_value_ref_case2, output_counts_value_ref_case2, + 1, MKLDNNPlugin::impl_desc_type::unknown + }, + // case 7 - model2, sorted="false", input with 10 elements where all of them are the same + unique_test_params{ + model2, "FP32", "false", "true", "true", input_shape_case3, input_value_case3, + output_uniques_shape_case3, output_indicess_shape_case3, output_counts_shape_case3, + output_uniques_value_ref_case3, output_indices_value_ref_case3, output_counts_value_ref_case3, + 1, MKLDNNPlugin::impl_desc_type::unknown + }, + // case 8 - model2, sorted="true", input with 10 elements where some of them repeat + unique_test_params{ + model2, "FP32", "true", "true", "true", input_shape_case4, input_value_case4, + output_uniques_shape_case4, output_indicess_shape_case4, output_counts_shape_case4, + output_uniques_value_ref_case4, output_indices_value_ref_case4, output_counts_value_ref_case4, + 1, MKLDNNPlugin::impl_desc_type::unknown + }, + // case 9 - model2, sorted="true", input with 10 elements where all of them are unique + unique_test_params{ + model2, "FP32", "true", "true", "true", input_shape_case5, input_value_case5, + output_uniques_shape_case5, output_indicess_shape_case5, output_counts_shape_case5, + output_uniques_value_ref_case5, output_indices_value_ref_case5, output_counts_value_ref_case5, + 1, MKLDNNPlugin::impl_desc_type::unknown + } +)); diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_activation_test.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_activation_test.cpp index 8ba55b33c001f8..e5f5afe6e7db7f 100644 --- a/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_activation_test.cpp +++ b/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_activation_test.cpp @@ -165,16 +165,16 @@ class MKLDNNGraphActivationTests: public TestsCommon, string P1, P2; if (p.alg == eltwise_relu) { - P1 = string("negative_slope=\"") + to_string(p.alpha) + string("\""); - P2 = string("beta=\"") + to_string(p.beta) + string("\""); + P1 = string("negative_slope=\"") + to_string_c_locale(p.alpha) + string("\""); + P2 = string("beta=\"") + to_string_c_locale(p.beta) + string("\""); } else if (p.alg == eltwise_bounded_relu) { - P1 = string("n=\"") + to_string(p.alpha) + string("\""); - P2 = string("beta=\"") + to_string(p.beta) + string("\""); + P1 = string("n=\"") + to_string_c_locale(p.alpha) + string("\""); + P2 = string("beta=\"") + to_string_c_locale(p.beta) + string("\""); } else if (p.alg == eltwise_tanh) { P1 = string("type=\"tanh\""); } else { - P1 = string("alpha=\"") + to_string(p.alpha) + string("\""); - P2 = string("beta=\"") + to_string(p.beta) + string("\""); + P1 = string("alpha=\"") + to_string_c_locale(p.alpha) + string("\""); + P2 = string("beta=\"") + to_string_c_locale(p.beta) + string("\""); } REPLACE_WITH_STR(model, "_P1_", P1); REPLACE_WITH_STR(model, "_P2_", P2); diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_conv_test.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_conv_test.cpp index ba4596d25f355c..8018deb25ad162 100644 --- a/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_conv_test.cpp +++ b/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_conv_test.cpp @@ -59,8 +59,8 @@ void ref_conv(const TBlob &src, const data_t *weights, const size_t weig size_t IH = src_dims[dims_size - 2]; size_t IW = src_dims[dims_size - 1]; - size_t OW = (IW + 2u * prm.pads_begin[X_AXIS] - prm.kernel[X_AXIS]) / prm.strides[X_AXIS] + 1u; - size_t OH = (IH + 2u * prm.pads_begin[Y_AXIS] - prm.kernel[Y_AXIS]) / prm.strides[Y_AXIS] + 1u; + size_t OW = (IW + prm.pads_end[X_AXIS] + prm.pads_begin[X_AXIS] - prm.kernel[X_AXIS]) / prm.strides[X_AXIS] + 1u; + size_t OH = (IH + prm.pads_end[Y_AXIS] + prm.pads_begin[Y_AXIS] - prm.kernel[Y_AXIS]) / prm.strides[Y_AXIS] + 1u; size_t OD = dims_size == 5 ? (ID + 2u * prm.pads_begin[Z_AXIS] - prm.kernel[Z_AXIS]) / prm.strides[Z_AXIS] + 1u : 1u; size_t OC = prm.out_c; @@ -80,12 +80,12 @@ void ref_conv(const TBlob &src, const data_t *weights, const size_t weig size_t SC2 = SC1 * OD; size_t SC3 = OC / GC; size_t SC4 = SC2 * SC3; - + size_t IC1 = IH * IW; size_t IC2 = IC1 * ID; size_t IC3 = IC / GC; size_t IC4 = IC2 * IC3; - + size_t KC1 = KH * KW; size_t KC2 = KC1 * KD; size_t KC3 = IC3 * KC2; @@ -144,7 +144,7 @@ void ref_conv(const TBlob &src, const data_t *weights, const size_t weig class MKLDNNGraphConvolutionTests: public TestsCommon, public WithParamInterface { std::string model_t_5D = R"V0G0N( - + @@ -193,7 +193,7 @@ class MKLDNNGraphConvolutionTests: public TestsCommon, int k_len = p.kernel.size(); for (size_t i = 2; i < p.dims.size(); i++) { size_t inx = k_len - i + 1; - size_t dim = (p.dims[i] + 2lu * p.pads_begin[inx] - p.kernel[inx]) / p.strides[inx] + 1lu; + size_t dim = (p.dims[i] + p.pads_end[inx] + p.pads_begin[inx] - p.kernel[inx]) / p.strides[inx] + 1lu; s_dims += "\n "; s_dims += std::to_string(dim) + ""; } @@ -347,9 +347,9 @@ INSTANTIATE_TEST_CASE_P( TestConvolution, MKLDNNGraphConvolutionTests, ::testing::Values( /*0*/ conv_test_params{{1, 9, 16, 32}, - {1, 1}, {1, 1}, {0, 0}, {0, 0}, 17, 1, "", 6, MKLDNNPlugin::impl_desc_type::jit | MKLDNNPlugin::impl_desc_type::_1x1 }, + {1, 1}, {1, 1}, {0, 0}, {0, 0}, 17, 1, "same_upper", 6, MKLDNNPlugin::impl_desc_type::jit | MKLDNNPlugin::impl_desc_type::_1x1 }, conv_test_params{{1, 9, 32, 16}, - {2, 4}, {1, 1}, {0, 0}, {0, 0}, 17, 1, "", 5, MKLDNNPlugin::impl_desc_type::jit }, + {2, 4}, {1, 1}, {1, 1}, {0, 2}, 17, 1, "", 5, MKLDNNPlugin::impl_desc_type::jit }, conv_test_params{{1, 9, 32, 16}, {2, 4}, {2, 1}, {0, 0}, {0, 0}, 17, 1, "", 5, MKLDNNPlugin::impl_desc_type::jit }, conv_test_params{{1, 3, 40, 40}, @@ -392,13 +392,13 @@ INSTANTIATE_TEST_CASE_P( {3, 3, 3}, {1, 1, 1}, {0, 0, 0}, {0, 0, 0}, 64, 1, "", 2, MKLDNNPlugin::impl_desc_type::gemm_blas }, conv_test_params{{1, 5, 15, 20, 20}, {3, 3, 3}, {3, 2, 1}, {0, 0, 0}, {0, 0, 0}, 64, 1, "", 2, MKLDNNPlugin::impl_desc_type::gemm_blas }, - conv_test_params{{1, 5, 15, 20, 20}, - {3, 3, 3}, {1, 1, 1}, {2, 2, 2}, {1, 1, 1}, 64, 1, "", 2, MKLDNNPlugin::impl_desc_type::gemm_blas }, + // conv_test_params{{1, 5, 15, 20, 20}, + // {3, 3, 3}, {1, 1, 1}, {2, 2, 2}, {1, 1, 1}, 64, 1, "", 2, MKLDNNPlugin::impl_desc_type::gemm_blas }, conv_test_params{{1, 16, 30, 30, 10}, {5, 5, 5}, {1, 1, 1}, {2, 2, 2}, {2, 2, 2}, 16, 1, "", 2, MKLDNNPlugin::impl_desc_type::gemm_blas, {MKLDNNPlugin::impl_desc_type::gemm_blas} }, conv_test_params{{1, 4, 16, 16, 16}, - {3, 3, 3}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, 8, 1, "same_upper", 2, MKLDNNPlugin::impl_desc_type::gemm_blas }, + {3, 3, 3}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, 8, 1, "", 2, MKLDNNPlugin::impl_desc_type::gemm_blas }, #endif /*20*/ conv_test_params{{1, 16, 30, 30, 10}, {5, 5, 5}, {1, 1, 1}, {2, 2, 2}, {2, 2, 2}, 16, 1, "", 2, MKLDNNPlugin::impl_desc_type::jit }, @@ -492,7 +492,7 @@ INSTANTIATE_TEST_CASE_P( TestDynBatchConvolution, MKLDNNGraphDynBatchConvolutionTests, ::testing::Values( conv_test_params{{1, 8, 16, 32}, - {1, 1}, {1, 1}, {0, 0}, {0, 0}, 17, 1, "", 7, MKLDNNPlugin::impl_desc_type::jit | MKLDNNPlugin::impl_desc_type::_1x1, + {1, 1}, {1, 1}, {0, 0}, {0, 0}, 17, 1, "same_upper", 7, MKLDNNPlugin::impl_desc_type::jit | MKLDNNPlugin::impl_desc_type::_1x1, {MKLDNNPlugin::impl_desc_type::jit_avx512_winograd}}, conv_test_params{{1, 9, 32, 16}, {2, 4}, {1, 1}, {0, 0}, {0, 0}, 17, 1, "", 5, MKLDNNPlugin::impl_desc_type::jit, diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_eltwise_test.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_eltwise_test.cpp index ed09cecede0257..d9d98d4531fb48 100644 --- a/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_eltwise_test.cpp +++ b/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_eltwise_test.cpp @@ -47,7 +47,7 @@ void ref_eltwise(const std::vector> &src, Inferen std::istringstream stream(prm.scales); std::string str; while (getline(stream, str, ',')) { - float val = std::stof(str); + float val = InferenceEngine::CNNLayer::ie_parse_float(str); scales.push_back(val); } } else { @@ -344,7 +344,7 @@ class MKLDNNGraphEltwise3InputsTests: public TestsCommon, std::string scale; if (!p.scales.empty()) { - scale = std::string("coeff=\"") + p.scales + std::string("\""); + scale = std::string("coeff=\"") + to_string_c_locale(p.scales) + std::string("\""); } REPLACE_WITH_STR(model, "_OP_", op); REPLACE_WITH_STR(model, "_COEFF_", scale); @@ -588,14 +588,14 @@ class MKLDNNGraphEltwise2InputsTests: public TestsCommon, std::string model = model_t; std::string op = select_op(p.op); - std::string src_dims1; + std::string src_dims1 = ""; for (auto &dim : p.dims1) { src_dims1 += "\n "; src_dims1 += std::to_string(dim) + ""; } REPLACE_WITH_STR(model, "__SRC_DIMS_1__", src_dims1); - std::string src_dims2; + std::string src_dims2 = ""; for (auto &dim : p.dims2) { src_dims2 += "\n "; src_dims2 += std::to_string(dim) + ""; @@ -617,7 +617,7 @@ class MKLDNNGraphEltwise2InputsTests: public TestsCommon, std::string scale; if (!p.scales.empty()) { - scale = std::string("coeff=\"") + p.scales + std::string("\""); + scale = std::string("coeff=\"") + to_string_c_locale(p.scales) + std::string("\""); } REPLACE_WITH_STR(model, "_OP_", op); REPLACE_WITH_STR(model, "_COEFF_", scale); @@ -652,27 +652,7 @@ class MKLDNNGraphEltwise2InputsTests: public TestsCommon, } } InferenceEngine::SizeVector dims_src1 = p.dims1; - InferenceEngine::Layout layout1 = InferenceEngine::ANY; - switch (p.dims1.size()) { - case 4: - layout1 = InferenceEngine::NCHW; - break; - case 5: - layout1 = InferenceEngine::NCDHW; - break; - } - InferenceEngine::SizeVector dims_src2 = p.dims2; - InferenceEngine::Layout layout2 = InferenceEngine::ANY; - switch (p.dims2.size()) { - case 4: - layout2 = InferenceEngine::NCHW; - break; - case 5: - layout2 = InferenceEngine::NCDHW; - break; - } - - InferenceEngine::Blob::Ptr src1 = InferenceEngine::make_shared_blob({InferenceEngine::Precision::FP32, dims_src1, layout1}); + InferenceEngine::Blob::Ptr src1 = InferenceEngine::make_shared_blob({InferenceEngine::Precision::FP32, dims_src1, InferenceEngine::TensorDesc::getLayoutByDims(p.dims1) }); src1->allocate(); InferenceEngine::TBlob* srcPtr1 = dynamic_cast*>(src1.get()); @@ -681,7 +661,9 @@ class MKLDNNGraphEltwise2InputsTests: public TestsCommon, FAIL() << "Cannot cast blob to TBlob."; fill_data_sine(src1->buffer(), src1->size(), 0.1, 0.9, 1); - InferenceEngine::Blob::Ptr src2 = InferenceEngine::make_shared_blob({InferenceEngine::Precision::FP32, dims_src2, layout2}); + + InferenceEngine::SizeVector dims_src2 = p.dims2; + InferenceEngine::Blob::Ptr src2 = InferenceEngine::make_shared_blob({InferenceEngine::Precision::FP32, dims_src2, InferenceEngine::TensorDesc::getLayoutByDims(p.dims2) }); src2->allocate(); InferenceEngine::TBlob* srcPtr2 = dynamic_cast*>(src2.get()); @@ -762,22 +744,22 @@ INSTANTIATE_TEST_CASE_P( INSTANTIATE_TEST_CASE_P( TestsDiffDims, MKLDNNGraphEltwise2InputsTests, ::testing::Values( - eltwise_test_params{{1},{1, 3},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, - eltwise_test_params{{1, 3},{1},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, + eltwise_test_params{{},{1, 3},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, + eltwise_test_params{{1, 3},{},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, eltwise_test_params{{1, 3},{3},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, - eltwise_test_params{{1},{1, 3, 3},{}, eltwise_test_params::opType::Sum, "", 2, MKLDNNPlugin::impl_desc_type::ref}, - eltwise_test_params{{1, 3, 3},{1},{}, eltwise_test_params::opType::Sum, "", 2, MKLDNNPlugin::impl_desc_type::ref}, + eltwise_test_params{{},{1, 3, 3},{}, eltwise_test_params::opType::Sum, "", 2, MKLDNNPlugin::impl_desc_type::ref}, + eltwise_test_params{{1, 3, 3},{},{}, eltwise_test_params::opType::Sum, "", 2, MKLDNNPlugin::impl_desc_type::ref}, eltwise_test_params{{1, 3, 3},{3},{}, eltwise_test_params::opType::Sum, "", 2, MKLDNNPlugin::impl_desc_type::ref}, eltwise_test_params{{1, 3},{1, 3, 3},{}, eltwise_test_params::opType::Sum, "", 2, MKLDNNPlugin::impl_desc_type::ref}, eltwise_test_params{{1, 3, 3},{1, 3},{}, eltwise_test_params::opType::Sum, "", 2, MKLDNNPlugin::impl_desc_type::ref}, - eltwise_test_params{{1},{1, 3, 3, 3},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, - eltwise_test_params{{1, 3, 3, 3},{1},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, + eltwise_test_params{{},{1, 3, 3, 3},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, + eltwise_test_params{{1, 3, 3, 3},{},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, eltwise_test_params{{1, 3},{1, 3, 3, 3},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, eltwise_test_params{{1, 3, 3, 3},{1, 3},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, eltwise_test_params{{1, 3, 3},{1, 3, 3, 3},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, eltwise_test_params{{1, 3, 3, 3},{1, 3, 3},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, - eltwise_test_params{{1},{1, 3, 3, 3, 3},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, - eltwise_test_params{{1, 3, 3, 3, 3},{1},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, + eltwise_test_params{{},{1, 3, 3, 3, 3},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, + eltwise_test_params{{1, 3, 3, 3, 3},{},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, eltwise_test_params{{1, 3},{1, 3, 3, 3, 3},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, eltwise_test_params{{1, 3, 3, 3, 3},{1, 3},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, eltwise_test_params{{1, 3, 3},{1, 3, 3, 3, 3},{}, eltwise_test_params::opType::Sum, "", 1, MKLDNNPlugin::impl_desc_type::ref}, diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_leaks_test.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_leaks_test.cpp index d8ffa203984e78..e3576688fa3e48 100644 --- a/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_leaks_test.cpp +++ b/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_leaks_test.cpp @@ -5,6 +5,7 @@ #include #include #include "mkldnn_plugin/mkldnn_graph.h" +#include "mkldnn_plugin/mkldnn_exec_network.h" #include "test_graph.hpp" diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_permute_test.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_permute_test.cpp index 3b1b7d25ae9817..d91b3ff2b001f3 100644 --- a/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_permute_test.cpp +++ b/inference-engine/tests/unit/engines/mkldnn/graph/layers/internal/graph_permute_test.cpp @@ -202,7 +202,9 @@ INSTANTIATE_TEST_CASE_P( permute_test_params{{2, 3, 4, 5, 7}, {0, 2, 4, 3, 1}, 1, MKLDNNPlugin::impl_desc_type::unknown}, permute_test_params{{2, 3, 4, 5, 7}, {0, 4, 2, 3, 1}, 1, MKLDNNPlugin::impl_desc_type::unknown}, permute_test_params{{2, 3, 4, 5}, {0, 3, 1, 2}, 1, MKLDNNPlugin::impl_desc_type::unknown}, - permute_test_params{{3, 4, 7}, {1, 0, 2}, 1, MKLDNNPlugin::impl_desc_type::unknown} + permute_test_params{{3, 4, 7}, {1, 0, 2}, 1, MKLDNNPlugin::impl_desc_type::unknown}, + permute_test_params{{3, 4, 7, 8, 4}, {0, 2, 3, 4, 1}, 1, MKLDNNPlugin::impl_desc_type::unknown}, + permute_test_params{{3, 4, 7, 8, 4}, {0, 4, 1, 2, 3}, 1, MKLDNNPlugin::impl_desc_type::unknown} )); class MKLDNNGraphDynBatchPermuteTests: public MKLDNNGraphPermuteTests { @@ -288,5 +290,7 @@ INSTANTIATE_TEST_CASE_P( permute_test_params{{2, 3, 4, 5, 7}, {0, 2, 1, 3, 4}, 1, MKLDNNPlugin::impl_desc_type::unknown}, permute_test_params{{2, 3, 4, 5, 7}, {0, 2, 4, 3, 1}, 1, MKLDNNPlugin::impl_desc_type::unknown}, permute_test_params{{2, 3, 4, 5, 7}, {0, 4, 2, 3, 1}, 1, MKLDNNPlugin::impl_desc_type::unknown}, - permute_test_params{{2, 3, 4, 5}, {0, 3, 1, 2}, 1, MKLDNNPlugin::impl_desc_type::unknown} + permute_test_params{{2, 3, 4, 5}, {0, 3, 1, 2}, 1, MKLDNNPlugin::impl_desc_type::unknown}, + permute_test_params{{3, 4, 7, 8, 4}, {0, 2, 3, 4, 1}, 1, MKLDNNPlugin::impl_desc_type::unknown}, + permute_test_params{{3, 4, 7, 8, 4}, {0, 4, 1, 2, 3}, 1, MKLDNNPlugin::impl_desc_type::unknown} )); diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/structure/graph_structure_test.cpp b/inference-engine/tests/unit/engines/mkldnn/graph/structure/graph_structure_test.cpp index e8e20c0782f353..85f73ad032dc81 100644 --- a/inference-engine/tests/unit/engines/mkldnn/graph/structure/graph_structure_test.cpp +++ b/inference-engine/tests/unit/engines/mkldnn/graph/structure/graph_structure_test.cpp @@ -3,17 +3,14 @@ // #include -#include -#include "mkldnn_plugin/mkldnn_graph.h" +#include "mkldnn_plugin/mkldnn_exec_network.h" -#include "single_layer_common.hpp" #include #include "tests_common.hpp" #include "../test_graph.hpp" #include #include #include -#include using namespace ::testing; using namespace std; @@ -3817,7 +3814,7 @@ TEST_F(MKLDNNGraphStructureTests, TestNoRedundantReordersForXceptionTopology) { TEST_F(MKLDNNGraphStructureTests, TestNoRedundantReordersForGrayscaleInput) { std::string model = R"V0G0N( - + @@ -3830,7 +3827,7 @@ TEST_F(MKLDNNGraphStructureTests, TestNoRedundantReordersForGrayscaleInput) { - + 1 @@ -4505,7 +4502,7 @@ TEST_F(MKLDNNGraphStructureTests, TestFailedVNect0003) { TEST_F(MKLDNNGraphStructureTests, TestConvolutionDWConvolutionSumFusing) { std::string model = R"V0G0N( - + @@ -4528,7 +4525,7 @@ TEST_F(MKLDNNGraphStructureTests, TestConvolutionDWConvolutionSumFusing) { - + 1 @@ -4549,7 +4546,7 @@ TEST_F(MKLDNNGraphStructureTests, TestConvolutionDWConvolutionSumFusing) { - + 1 @@ -4570,7 +4567,7 @@ TEST_F(MKLDNNGraphStructureTests, TestConvolutionDWConvolutionSumFusing) { - + 1 @@ -4613,7 +4610,7 @@ TEST_F(MKLDNNGraphStructureTests, TestConvolutionDWConvolutionSumFusing) { - + 1 diff --git a/inference-engine/tests/unit/engines/mkldnn/graph/test_graph.hpp b/inference-engine/tests/unit/engines/mkldnn/graph/test_graph.hpp index 67188fba7aade7..da43e2a2d9f112 100644 --- a/inference-engine/tests/unit/engines/mkldnn/graph/test_graph.hpp +++ b/inference-engine/tests/unit/engines/mkldnn/graph/test_graph.hpp @@ -64,7 +64,11 @@ class MKLDNNGraphTestClass: public MKLDNNPlugin::MKLDNNGraph { auto input = inputNodes.find(name); if (input != inputNodes.end()) { - MKLDNNPlugin::MKLDNNDims outDims = input->second->getChildEdgeAt(0)->getDims(); + MKLDNNPlugin::MKLDNNDims outDims; + if(input->second->getChildEdgeAt(0)->getDims().ndims() == 0 ) + outDims = MKLDNNPlugin::MKLDNNDims(InferenceEngine::SizeVector(1,1)); + else + outDims = input->second->getChildEdgeAt(0)->getDims(); if (batch < 1) batch = outDims[0]; diff --git a/inference-engine/tests/unit/mem_solver/mem_solver_test.cpp b/inference-engine/tests/unit/engines/mkldnn/mem_solver_test.cpp similarity index 97% rename from inference-engine/tests/unit/mem_solver/mem_solver_test.cpp rename to inference-engine/tests/unit/engines/mkldnn/mem_solver_test.cpp index 0f430c0ac9c38c..5c7ffc5c82668f 100644 --- a/inference-engine/tests/unit/mem_solver/mem_solver_test.cpp +++ b/inference-engine/tests/unit/engines/mkldnn/mem_solver_test.cpp @@ -4,12 +4,12 @@ #include -#include "memory_solver.hpp" +#include "mkldnn_memory_solver.hpp" #include "details/ie_exception.hpp" using namespace testing; -using namespace InferenceEngine; -using Box = InferenceEngine::MemorySolver::Box; +using namespace MKLDNNPlugin; +using Box = MKLDNNPlugin::MemorySolver::Box; TEST(MemSolverTest, LinearAndEven) { int n = 0; @@ -198,7 +198,7 @@ TEST(MemSolverTest, GetOffsetThows) { MemorySolver ms(boxes); ms.solve(); - EXPECT_THROW(ms.getOffset(100), details::InferenceEngineException); + EXPECT_THROW(ms.getOffset(100), InferenceEngine::details::InferenceEngineException); } TEST(MemSolverTest, NoOverlapping) { diff --git a/inference-engine/tests/unit/graph_tools/graph_copy_tests.cpp b/inference-engine/tests/unit/graph_tools/graph_copy_tests.cpp index fce08b2dd5a9b5..035300042684f7 100644 --- a/inference-engine/tests/unit/graph_tools/graph_copy_tests.cpp +++ b/inference-engine/tests/unit/graph_tools/graph_copy_tests.cpp @@ -38,18 +38,17 @@ class GraphCopyTests : public GraphTestsBase { CONNECT(3, 5); CONNECT(5, 2); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap &maps) { + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap &maps) { prepareInputs(maps, 12); }))); - EXPECT_CALL(mockNet, getOutputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](OutputsDataMap &maps) { + EXPECT_CALL(*mockNet, getOutputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](OutputsDataMap &maps) { prepareOutputs(maps); }))); - EXPECT_CALL(mockNet, getTargetDevice()).WillRepeatedly(Return(TargetDevice::eCPU)); - EXPECT_CALL(mockNet, getPrecision()).WillRepeatedly(Return(Precision::FP16)); - EXPECT_CALL(mockNet, getBatchSize()).WillRepeatedly(Return(12)); - EXPECT_CALL(mockNet, getName(_, _)).WillRepeatedly(Invoke([](char *pName, size_t len) { + EXPECT_CALL(*mockNet, getPrecision()).WillRepeatedly(Return(Precision::FP16)); + EXPECT_CALL(*mockNet, getBatchSize()).WillRepeatedly(Return(12)); + EXPECT_CALL(*mockNet, getName(_, _)).WillRepeatedly(Invoke([](char *pName, size_t len) { memcpy(pName, "nm", 3); })); @@ -60,12 +59,10 @@ class GraphCopyTests : public GraphTestsBase { }; TEST_F(GraphCopyTests, copyNetworkPreserveBasicParams) { - - auto clone = CNNNetCopy(mockNet, mc); + auto clone = CNNNetCopy(*mockNet, mc); //network was copied not just assigned - ASSERT_NE(clone.get(), &mockNet); - ASSERT_EQ(clone->getTargetDevice(), TargetDevice::eCPU); + ASSERT_NE(clone.get(), mockNet.get()); ASSERT_EQ(clone->getPrecision(), Precision::FP16); char name[20]; @@ -74,41 +71,38 @@ TEST_F(GraphCopyTests, copyNetworkPreserveBasicParams) { } TEST_F(GraphCopyTests, canPreserveBatchWhenCopyNetwork) { - auto clone = CNNNetCopy(mockNet, mc); + auto clone = CNNNetCopy(*mockNet, mc); ASSERT_EQ(clone->getBatchSize(), 12); } TEST_F(GraphCopyTests, canPreserveInputs) { - auto clone = CNNNetCopy(mockNet, mc); + auto clone = CNNNetCopy(*mockNet, mc); InputsDataMap inputs, inputsTarget; InputsDataMap heads, headsTarget; clone->getInputsInfo(inputs); - mockNet.getInputsInfo(inputsTarget); + mockNet->getInputsInfo(inputsTarget); ASSERT_INPUTS_INFO_EQ(inputs, inputsTarget); } TEST_F(GraphCopyTests, canPreserveOutputs) { - auto clone = CNNNetCopy(mockNet, mc); + auto clone = CNNNetCopy(*mockNet, mc); OutputsDataMap outTarget, outSource; clone->getOutputsInfo(outTarget); - mockNet.getOutputsInfo(outSource); + mockNet->getOutputsInfo(outSource); ASSERT_OUTPUTS_INFO_EQ(outSource, outTarget); } TEST_F(GraphCopyTests, canPreserveAttributes) { - auto clone = CNNNetCopy(mockNet, mc); + auto clone = CNNNetCopy(*mockNet, mc); ADD_ATTR(1, "id", "r-1-2-3"); ADD_ATTR(2, "id", "r-1-2-3"); - - IE_SUPPRESS_DEPRECATED_START - CNNNetwork cloned (clone.get()); - IE_SUPPRESS_DEPRECATED_END + CNNNetwork cloned (clone); auto idMemOutput = cloned.getLayerByName("1")->GetParamAsString("id"); auto idMemInput = cloned.getLayerByName("2")->GetParamAsString("id"); @@ -117,7 +111,7 @@ TEST_F(GraphCopyTests, canPreserveAttributes) { } TEST_F(GraphCopyTests, canPreserveGetData) { - auto clone = CNNNetCopy(mockNet, mc); + auto clone = CNNNetCopy(*mockNet, mc); ASSERT_NE(clone->getData("1"), nullptr); ASSERT_NE(clone->getData("2"), nullptr); @@ -127,7 +121,7 @@ TEST_F(GraphCopyTests, canPreserveGetData) { } TEST_F(GraphCopyTests, canPreserveTopology) { - auto iclone = CNNNetCopy(mockNet, mc); + auto iclone = CNNNetCopy(*mockNet, mc); auto clone = CNNNetwork(iclone); ASSERT_EQ(clone.layerCount(), 5); @@ -159,7 +153,7 @@ using FP32_2_FP32 = GNAPluginNS::details::QuantPair<_FP32_2_FP32 , _FP32_2_FP32 TEST_F(GraphCopyTests, canQuantizeTopology) { - auto iclone = ModelQuantizer().quantize(mockNet, std::vector({1.0f, 1.0f})); + auto iclone = ModelQuantizer().quantize(*mockNet, std::vector({1.0f, 1.0f})); auto clone = CNNNetwork(iclone); CNNNetBFS(clone.getLayerByName("1"), [&](CNNLayerPtr layer) { @@ -224,9 +218,7 @@ TEST(CNNSpecificGraphCopyTests, copyNetworkWithClampLayer) { struct EmptyStruct {}; auto visitor = [&](CNNLayerPtr lp) { return injectData(lp); }; auto copied_net_ptr = CNNNetCopy(network, visitor); - IE_SUPPRESS_DEPRECATED_START - auto copied_net = CNNNetwork(copied_net_ptr.get()); - IE_SUPPRESS_DEPRECATED_END + auto copied_net = CNNNetwork(copied_net_ptr); //check that Clamp layer was properly copied auto layer = std::dynamic_pointer_cast(copied_net.getLayerByName("ClampLayer")); @@ -294,9 +286,7 @@ TEST(CNNSpecificGraphCopyTests, copyPreprocess) { struct EmptyStruct {}; auto visitor = [&](CNNLayerPtr lp) { return injectData(lp); }; auto copied_net_ptr = CNNNetCopy(network, visitor); - IE_SUPPRESS_DEPRECATED_START - auto copied_net = CNNNetwork(copied_net_ptr.get()); - IE_SUPPRESS_DEPRECATED_END + auto copied_net = CNNNetwork(copied_net_ptr); //check that pre process Info existed in copied network auto &pp = copied_net.getInputsInfo().begin()->second->getPreProcess(); @@ -359,9 +349,7 @@ TEST(CNNSpecificGraphCopyTests, copyNetworkWithDeconvolution) { struct EmptyStruct {}; auto visitor = [&](CNNLayerPtr lp) { return injectData(lp); }; auto copied_net_ptr = CNNNetCopy(network, visitor); - IE_SUPPRESS_DEPRECATED_START - auto copied_net = CNNNetwork(copied_net_ptr.get()); - IE_SUPPRESS_DEPRECATED_END + auto copied_net = CNNNetwork(copied_net_ptr); // check that Clamp layer was properly copied auto layer = std::dynamic_pointer_cast(copied_net.getLayerByName("upsample_merged")); diff --git a/inference-engine/tests/unit/graph_tools/graph_test_base.hpp b/inference-engine/tests/unit/graph_tools/graph_test_base.hpp index 5e6a683f569ec5..a4dbb2484b7981 100644 --- a/inference-engine/tests/unit/graph_tools/graph_test_base.hpp +++ b/inference-engine/tests/unit/graph_tools/graph_test_base.hpp @@ -30,7 +30,7 @@ class GraphTestsBase : public ::testing::Test { std::vector layers; std::vector> datas; - MockICNNNetwork mockNet; + std::shared_ptr mockNet; InferenceEngine::CNNNetwork wrap; /** @@ -63,7 +63,7 @@ class GraphTestsBase : public ::testing::Test { } CNNLayerPtr layerByName(std::string name) { - auto sorted = InferenceEngine::details::CNNNetSortTopologically(mockNet); + auto sorted = InferenceEngine::details::CNNNetSortTopologically(*mockNet); auto i = std::find_if(sorted.begin(), sorted.end(), [&](CNNLayerPtr l){ return l->name == name; @@ -232,9 +232,8 @@ class GraphTestsBase : public ::testing::Test { */ int _batchSize = 1; void SetUp() override { - IE_SUPPRESS_DEPRECATED_START - wrap = InferenceEngine::CNNNetwork(&mockNet); - IE_SUPPRESS_DEPRECATED_END + mockNet = std::make_shared(); + wrap = InferenceEngine::CNNNetwork(std::dynamic_pointer_cast(mockNet)); datas.resize(10); for (int i = 0; i < 10; i++) { diff --git a/inference-engine/tests/unit/graph_tools/graph_tools_functional_tests.cpp b/inference-engine/tests/unit/graph_tools/graph_tools_functional_tests.cpp new file mode 100644 index 00000000000000..a969d714ecd74e --- /dev/null +++ b/inference-engine/tests/unit/graph_tools/graph_tools_functional_tests.cpp @@ -0,0 +1,39 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include
+#include +#include +#include +#include + +using namespace testing; +using namespace InferenceEngine::details; +using namespace InferenceEngine; +using namespace std; + +class GraphToolsFncTest : public ::testing::Test { +public: + template + static void checkSort(const T &sorted) { + for (int i = 0; i < sorted.size(); i++) { + //check that all input already visited: + for (auto &inputs : sorted[i]->insData) { + auto inputName = inputs.lock()->getCreatorLayer().lock()->name; + + bool bFound = false; + for (int j = 0; j < i; j++) { + if (sorted[j]->name == inputName) { + bFound = true; + break; + } + } + ASSERT_TRUE(bFound) << "order is not correct, layer " << sorted[i]->name << " has missed input: " + << inputName; + } + } + } +}; + diff --git a/inference-engine/tests/unit/graph_tools/graph_tools_test.cpp b/inference-engine/tests/unit/graph_tools/graph_tools_test.cpp index ad6dc1711ad517..70c4b820bcc61e 100644 --- a/inference-engine/tests/unit/graph_tools/graph_tools_test.cpp +++ b/inference-engine/tests/unit/graph_tools/graph_tools_test.cpp @@ -104,10 +104,10 @@ TEST_F(GraphToolsTest, canSortTopologically) { CONNECT(2, 1); CONNECT(1, 4); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillOnce(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillOnce(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - auto sorted = CNNNetSortTopologically(mockNet); + auto sorted = CNNNetSortTopologically(*mockNet); EXPECT_EQ(sorted.size(), 4); @@ -139,10 +139,10 @@ TEST_F(GraphToolsTest, canDetectLoopsWhileSortTing) { CONNECT(4, 8); CONNECT(8, 3); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillOnce(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillOnce(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - ASSERT_ANY_THROW(CNNNetSortTopologically(mockNet)); + ASSERT_ANY_THROW(CNNNetSortTopologically(*mockNet)); } @@ -154,11 +154,11 @@ TEST_F(GraphToolsTest, canSortIfInputsPointsToLayerWithMultiInputs) { CONNECT(3, 5); CONNECT(5, 2); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillOnce(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillOnce(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - auto sorted = CNNNetSortTopologically(mockNet); + auto sorted = CNNNetSortTopologically(*mockNet); vector> expected = { {"1", "3", "4", "5", "2"}, @@ -203,10 +203,10 @@ TEST_F(GraphToolsTest, canGetAllMemoryInputsLayersFromStandardInputs) { CONNECT(5, 7); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillOnce(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillOnce(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareSomeInputs(maps, {1}); }))); - auto allInputLayers = CNNNetGetAllInputLayers(mockNet); + auto allInputLayers = CNNNetGetAllInputLayers(*mockNet); ASSERT_EQ(3, allInputLayers.size()); auto element = allInputLayers.begin(); ASSERT_STREQ("1", element->get()->name.c_str()); @@ -220,10 +220,10 @@ TEST_F(GraphToolsTest, canGetSingleInputLayer) { // 1->2 CONNECT(1, 2); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillOnce(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillOnce(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareSomeInputs(maps, {1}); }))); - auto allInputLayers = CNNNetGetAllInputLayers(mockNet); + auto allInputLayers = CNNNetGetAllInputLayers(*mockNet); ASSERT_EQ(1, allInputLayers.size()); } @@ -239,7 +239,7 @@ TEST_F(GraphToolsTest, canIterateOverCNNNetwork) { CONNECT(6, 7); CONNECT(7, 8); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); @@ -265,7 +265,7 @@ TEST_F(GraphToolsTest, canIterateOverCNNNetworkWithCycle) { CONNECT(3, 4); CONNECT(4, 2); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); @@ -285,7 +285,7 @@ TEST_F(GraphToolsTest, canCompareCNNNetworkIterators) { CONNECT(1, 2); CONNECT(1, 3); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillOnce(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillOnce(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); @@ -302,7 +302,7 @@ TEST_F(GraphToolsTest, canIterateOverEmptyNetwork) { CONNECT(1, 2); CONNECT(2, 1); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillOnce(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillOnce(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); @@ -318,11 +318,11 @@ TEST_F(GraphToolsTest, CNNNetSwapLayersSwapWithItself) { CONNECT(1, 2); CONNECT(2, 3); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -338,11 +338,11 @@ TEST_F(GraphToolsTest, CNNNetSwapLayersSwapWithItself) { TEST_F(GraphToolsTest, CNNNetSwapLayersSimpleCase_1) { CONNECT(1, 2); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -359,11 +359,11 @@ TEST_F(GraphToolsTest, CNNNetSwapLayersSimpleCase_2) { CONNECT(1, 2); CONNECT(2, 3); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -381,11 +381,11 @@ TEST_F(GraphToolsTest, CNNNetSwapLayersSimpleCase_3) { CONNECT(1, 2); CONNECT(2, 3); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -407,11 +407,11 @@ TEST_F(GraphToolsTest, CNNNetSwapLayersDoesSwapDims) { SET_DIMS(2, {20, 1}); SET_DIMS(3, {30, 1}); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -434,11 +434,11 @@ TEST_F(GraphToolsTest, CNNNetSwapLayersSimpleCase_4) { CONNECT(3, 4); CONNECT(4, 5); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -458,11 +458,11 @@ TEST_F(GraphToolsTest, CNNNetSwapLayersSplit) { CONNECT(1, 2); CONNECT(1, 3); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -479,11 +479,11 @@ TEST_F(GraphToolsTest, CNNNetSwapLayersSplit_2) { CONNECT(1, 2); CONNECT(1, 3); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -504,11 +504,11 @@ TEST_F(GraphToolsTest, CNNNetSwapLayersSplit_3) { CONNECT(2, 4); CONNECT(2, 5); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -532,11 +532,11 @@ TEST_F(GraphToolsTest, CNNNetSwapLayersSplit_4) { CONNECT(4, 2); CONNECT(4, 1); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_, _, _)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -562,11 +562,11 @@ TEST_F(GraphToolsTest, CanNotInsertLayerIntoNonAdjiacendLayers) { CONNECT(1, 2); CONNECT(2, 3); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -580,11 +580,11 @@ TEST_F(GraphToolsTest, CanNotInsertLayerIntoNonAdjiacendLayers) { TEST_F(GraphToolsTest, CNNNetworkInsertLayerSimpleCase) { CONNECT(1, 2); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -602,11 +602,11 @@ TEST_F(GraphToolsTest, CNNNetworkInsertLayerSimpleCaseWithMultipleOutputs) { CONNECT(1, 2); CONNECT(1, 3); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -626,11 +626,11 @@ TEST_F(GraphToolsTest, CNNNetworkInsertLayerSimpleCaseWithMultipleInputs) { CONNECT(1, 2); CONNECT(3, 2); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -650,11 +650,11 @@ TEST_F(GraphToolsTest, CNNNetworkInsertLayerSplitAndConcat) { CONNECT_FROM_PORT(1, 1, 2); CONNECT_FROM_PORT(1, 2, 3); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0,1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -677,11 +677,11 @@ TEST_F(GraphToolsTest, CNNNetworkInsertLayerSplitAndConcat) { TEST_F(GraphToolsTest, CNNNetworkInsertAfterLastLayer) { CONNECT(1, 2); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -698,11 +698,11 @@ TEST_F(GraphToolsTest, CNNNetworkInsertAfterAll) { CONNECT(1, 2); CONNECT(1, 3); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -719,11 +719,11 @@ TEST_F(GraphToolsTest, CNNNetworkInsertAllAfterSplit) { CONNECT_FROM_PORT(1, 0, 2); CONNECT_FROM_PORT(1, 1, 3); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -741,11 +741,11 @@ TEST_F(GraphToolsTest, CNNNetworkInsert1AfterSplit) { CONNECT_FROM_PORT(1, 1, 3); CONNECT_FROM_PORT(1, 2, 4); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -764,11 +764,11 @@ TEST_F(GraphToolsTest, CNNNetworkInsertAfter2ConnectionsToEltwise) { CONNECT(1, 2); CONNECT(1, 2); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -786,11 +786,11 @@ TEST_F(GraphToolsTest, CNNNetworkRemoveNullPointerLayer) { CONNECT_FROM_PORT(1, 1, 3); CONNECT_FROM_PORT(1, 2, 4); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -804,11 +804,11 @@ TEST_F(GraphToolsTest, CNNNetworkRemoveInputOrOutputLayer) { CONNECT_FROM_PORT(2, 0, 3); CONNECT_FROM_PORT(1, 0, 3); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -825,11 +825,11 @@ TEST_F(GraphToolsTest, CNNNetworkRemoveLayerThaHas2Outputs) { CONNECT_FROM_PORT(1, 0, 3); CONNECT_FROM_PORT(5, 0, 4); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -853,11 +853,11 @@ TEST_F(GraphToolsTest, CNNNetworkRemoveLayerSplit) { CONNECT_FROM_PORT(1, 1, 3); CONNECT_FROM_PORT(2, 0, 3); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -883,11 +883,11 @@ TEST_F(GraphToolsTest, CNNNetworkRemoveLayerSplit2) { CONNECT_FROM_PORT(2, 0, 4); CONNECT_FROM_PORT(2, 0, 5); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); @@ -911,11 +911,11 @@ TEST_F(GraphToolsTest, CNNNetworkRemoveSimpleLayer) { CONNECT_FROM_PORT(1, 0, 2); CONNECT_FROM_PORT(2, 0, 3); - EXPECT_CALL(mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ + EXPECT_CALL(*mockNet, getInputsInfo(_)).WillRepeatedly(WithArg<0>(Invoke([&](InputsDataMap & maps){ prepareInputs(maps); }))); - EXPECT_CALL(mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ + EXPECT_CALL(*mockNet, getLayerByName(_,_,_)).WillRepeatedly(WithArgs<0, 1>(Invoke([&](const char* name, InferenceEngine::CNNLayerPtr& l){ l = layerByName(name); return l== nullptr ? GENERAL_ERROR : OK; }))); diff --git a/inference-engine/tests/unit/inference_engine_tests/blob_proxy_test.cpp b/inference-engine/tests/unit/inference_engine_tests/blob_proxy_test.cpp index 2612f53806e86f..16f75642fcf1ae 100644 --- a/inference-engine/tests/unit/inference_engine_tests/blob_proxy_test.cpp +++ b/inference-engine/tests/unit/inference_engine_tests/blob_proxy_test.cpp @@ -51,7 +51,13 @@ TEST_F(BlobProxyTests, shouldNotDeAllocate) SizeVector v = {1, 2, 3}; auto allocator = createMockAllocator(); - TBlobProxy proxy(Precision::FP32, C, TBlob({ Precision::FP32, v, CHW}, dynamic_pointer_cast(allocator)), 2, {2}); + TBlob blob({ Precision::FP32, v, CHW }, dynamic_pointer_cast(allocator)); + + Blob::Ptr spBlob(&blob, [](Blob*) { + //don't delete + }); + + TBlobProxy proxy(Precision::FP32, C, spBlob, 2, {2}); EXPECT_EQ(((Blob&)proxy).deallocate(), false); } @@ -72,7 +78,11 @@ TEST_F(BlobProxyTests, canAccessProxyBlobUsingBaseMethod) TBlob blob({ Precision::FP32, v, CHW }, dynamic_pointer_cast(allocator)); blob.allocate(); - TBlobProxy proxy(Precision::FP32, C, move(blob), 2, {2}); + Blob::Ptr spBlob(&blob, [](Blob*) { + //don't delete + }); + + TBlobProxy proxy(Precision::FP32, C, spBlob, 2, {2}); auto proxyBuffer = proxy.buffer(); float *ptr = (float*)(void*)proxyBuffer; @@ -95,7 +105,11 @@ TEST_F(BlobProxyTests, canAccessProxyBlobUsingHelpers) TBlob blob({Precision::FP32, v, CHW }, dynamic_pointer_cast(allocator)); blob.allocate(); - TBlobProxy proxy(Precision::FP32, C, std::move(blob), 2, {2}); + Blob::Ptr spBlob(&blob, [](Blob*) { + //don't delete + }); + + TBlobProxy proxy(Precision::FP32, C, spBlob, 2, {2}); auto proxyData = proxy.data(); float *ptr = (float * )&proxyData[0]; diff --git a/inference-engine/tests/unit/inference_engine_tests/blob_test.cpp b/inference-engine/tests/unit/inference_engine_tests/blob_test.cpp index 19547a52288abc..f4bbeec06b9f5f 100644 --- a/inference-engine/tests/unit/inference_engine_tests/blob_test.cpp +++ b/inference-engine/tests/unit/inference_engine_tests/blob_test.cpp @@ -513,9 +513,10 @@ TEST_F(BlobTests, cannotIncreaseSizeOfPreallocated) { float input[] = {0.1f, 0.2f, 0.3f}; auto b = make_shared_blob({ Precision::FP32, {1, 2}, HW }, input); + ASSERT_NE(nullptr, b->buffer().as()); b->Resize({1,3}); - //since allocator isno't releasing, user have to be carefull that this still use old array + //since allocator isn't releasing, user have to be carefull that this still use old array ASSERT_EQ(nullptr, b->buffer().as()); b->Resize({1,1}); @@ -530,6 +531,8 @@ TEST_F(BlobTests, canAcceptpreallocatedSize) { float input[] = {0.1f, 0.2f, 0.3f}; auto b = make_shared_blob({ Precision::FP32, {1, 2}, HW }, input, 100); + ASSERT_NE(nullptr, b->buffer().as()); + b->Resize({1,101}); //since allocator isn't releasing, user have to be carefull that this still use old array ASSERT_EQ(nullptr, b->buffer().as()); diff --git a/inference-engine/tests/unit/inference_engine_tests/cnn_network_test.cpp b/inference-engine/tests/unit/inference_engine_tests/cnn_network_test.cpp index d69972d3e5ffc8..90cab63f12cf46 100644 --- a/inference-engine/tests/unit/inference_engine_tests/cnn_network_test.cpp +++ b/inference-engine/tests/unit/inference_engine_tests/cnn_network_test.cpp @@ -21,7 +21,6 @@ class CNNNetworkTests : public ::testing::Test { }; TEST_F(CNNNetworkTests, throwsOnInitWithNull) { - IE_SUPPRESS_DEPRECATED_START - ASSERT_THROW(CNNNetwork network(nullptr), InferenceEngine::details::InferenceEngineException); - IE_SUPPRESS_DEPRECATED_END + std::shared_ptr nlptr = nullptr; + ASSERT_THROW(CNNNetwork network(nlptr), InferenceEngine::details::InferenceEngineException); } diff --git a/inference-engine/tests/unit/inference_engine_tests/cpp_interfaces/executor_manager_tests.cpp b/inference-engine/tests/unit/inference_engine_tests/cpp_interfaces/executor_manager_tests.cpp index 022cc67bd7d56d..9fcb67aeda9c27 100644 --- a/inference-engine/tests/unit/inference_engine_tests/cpp_interfaces/executor_manager_tests.cpp +++ b/inference-engine/tests/unit/inference_engine_tests/cpp_interfaces/executor_manager_tests.cpp @@ -25,23 +25,18 @@ TEST_F(ExecutorManagerTests, canCreateSingleExecutorManager) { } TEST_F(ExecutorManagerTests, createDifferentExecutorsForDifferentDevices) { - auto device1 = TargetDeviceInfo::name(TargetDevice::eCPU); - auto device2 = TargetDeviceInfo::name(TargetDevice::eGPU); - - auto executor1 = _manager.getExecutor(device1); - auto executor2 = _manager.getExecutor(device2); + auto executor1 = _manager.getExecutor("CPU"); + auto executor2 = _manager.getExecutor("GPU"); ASSERT_NE(executor1, executor2); ASSERT_EQ(2, _manager.getExecutorsNumber()); } TEST_F(ExecutorManagerTests, returnTheSameExecutorForTheSameDevice) { - auto device1 = TargetDeviceInfo::name(TargetDevice::eCPU); - auto device2 = TargetDeviceInfo::name(TargetDevice::eGPU); - auto executor1 = _manager.getExecutor(device1); - auto executor2 = _manager.getExecutor(device2); + auto executor1 = _manager.getExecutor("CPU"); + auto executor2 = _manager.getExecutor("GPU"); - auto executor = _manager.getExecutor(device2); + auto executor = _manager.getExecutor("GPU"); ASSERT_EQ(executor, executor2); ASSERT_EQ(2, _manager.getExecutorsNumber()); diff --git a/inference-engine/tests/unit/inference_engine_tests/cpp_interfaces/iinference_plugin_internal_tests.cpp b/inference-engine/tests/unit/inference_engine_tests/cpp_interfaces/iinference_plugin_internal_tests.cpp index 88816b70b86d11..7e258dac8fa7c5 100644 --- a/inference-engine/tests/unit/inference_engine_tests/cpp_interfaces/iinference_plugin_internal_tests.cpp +++ b/inference-engine/tests/unit/inference_engine_tests/cpp_interfaces/iinference_plugin_internal_tests.cpp @@ -43,7 +43,7 @@ class InferenceEnginePluginInternalTest : public ::testing::Test { virtual void SetUp() { mock_plugin_impl.reset(new MockInferencePluginInternal()); - plugin = details::shared_from_irelease(make_ie_compatible_plugin({2, 0, "test", "version"}, mock_plugin_impl)); + plugin = details::shared_from_irelease(make_ie_compatible_plugin({{2, 1}, "test", "version"}, mock_plugin_impl)); mockExeNetworkInternal = make_shared(); } @@ -183,7 +183,7 @@ class InferenceEnginePluginInternal2Test : public ::testing::Test { virtual void SetUp() { mockPluginImpl = make_shared(); - plugin = details::shared_from_irelease(make_ie_compatible_plugin({2, 0, "test", "version"}, mockPluginImpl)); + plugin = details::shared_from_irelease(make_ie_compatible_plugin({{2, 1}, "test", "version"}, mockPluginImpl)); mockExeNetwork = make_shared(); } diff --git a/inference-engine/tests/unit/inference_engine_tests/cpp_interfaces/plugin_base_tests.cpp b/inference-engine/tests/unit/inference_engine_tests/cpp_interfaces/plugin_base_tests.cpp index 9bd254c452b195..08d6c10c6f5e52 100644 --- a/inference-engine/tests/unit/inference_engine_tests/cpp_interfaces/plugin_base_tests.cpp +++ b/inference-engine/tests/unit/inference_engine_tests/cpp_interfaces/plugin_base_tests.cpp @@ -22,7 +22,7 @@ class PluginBaseTests: public ::testing::Test { } virtual void SetUp() { mock_impl.reset(new MockPluginImpl()); - plugin = details::shared_from_irelease(make_ie_compatible_plugin({2, 0, "test", "version"}, mock_impl)); + plugin = details::shared_from_irelease(make_ie_compatible_plugin({{2, 1}, "test", "version"}, mock_impl)); } }; @@ -33,7 +33,7 @@ TEST_F(PluginBaseTests, canReportVersion) { EXPECT_STREQ(V->buildNumber, "test"); EXPECT_STREQ(V->description, "version"); EXPECT_EQ(V->apiVersion.major, 2); - EXPECT_EQ(V->apiVersion.minor, 0); + EXPECT_EQ(V->apiVersion.minor, 1); } diff --git a/inference-engine/tests/unit/inference_engine_tests/device_tests.cpp b/inference-engine/tests/unit/inference_engine_tests/device_tests.cpp deleted file mode 100644 index 280cc18aad3145..00000000000000 --- a/inference-engine/tests/unit/inference_engine_tests/device_tests.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include -#include "ie_device.hpp" -#include "details/ie_exception.hpp" - -using namespace InferenceEngine; - -class DeviceTests : public ::testing::Test { -protected: - virtual void TearDown() { - } - - virtual void SetUp() { - } - -public: - -}; - -TEST_F(DeviceTests, internalFindThrowsOnBadDevice) { - FindPluginRequest request = { TargetDevice::eBalanced }; - ASSERT_THROW(findPlugin(request), InferenceEngine::details::InferenceEngineException); -} - -TEST_F(DeviceTests, externalFindReturnsErrorStatus) { - FindPluginRequest request = { TargetDevice::eBalanced }; - FindPluginResponse result; - ResponseDesc desc; - StatusCode status = findPlugin(request, result, &desc); - ASSERT_EQ(status, GENERAL_ERROR); -} - -#if defined(ENABLE_MKL_DNN) -TEST_F(DeviceTests, externalFindPopulatesResult) { - FindPluginRequest request = { TargetDevice::eCPU }; - FindPluginResponse result; - ResponseDesc desc; - StatusCode status = findPlugin(request, result, &desc); - ASSERT_EQ(status, OK); - ASSERT_NE(result.names.size(), 0); -} -#endif - -TEST_F(DeviceTests, returnsProperDeviceName) { - ASSERT_STREQ(getDeviceName(TargetDevice::eDefault), "Default"); - ASSERT_STREQ(getDeviceName(TargetDevice::eBalanced), "Balanced"); - ASSERT_STREQ(getDeviceName(TargetDevice::eCPU), "CPU"); - ASSERT_STREQ(getDeviceName(TargetDevice::eGPU), "GPU"); - ASSERT_STREQ(getDeviceName(TargetDevice::eFPGA), "FPGA"); - ASSERT_STREQ(getDeviceName(TargetDevice::eMYRIAD), "MYRIAD"); - ASSERT_STREQ(getDeviceName(TargetDevice::eGNA), "GNA"); - ASSERT_STREQ(getDeviceName(TargetDevice::eHETERO), "HETERO"); - ASSERT_STREQ(getDeviceName(static_cast(-1)), "Unknown device"); - //off by one test - might not be enough - ASSERT_STREQ(getDeviceName(static_cast((uint8_t)TargetDevice::eHETERO + 1)), "Unknown device"); -} diff --git a/inference-engine/tests/unit/inference_engine_tests/local_test.cpp b/inference-engine/tests/unit/inference_engine_tests/local_test.cpp index 3c4cbf249b16e8..69205005e045a2 100644 --- a/inference-engine/tests/unit/inference_engine_tests/local_test.cpp +++ b/inference-engine/tests/unit/inference_engine_tests/local_test.cpp @@ -6,6 +6,7 @@ #include #include +#include using namespace ::testing; using namespace std; @@ -96,6 +97,103 @@ class LocaleTests : public ::testing::Test { )V0G0N"; + + std::string _model_LSTM = R"V0G0N( + + + + + + 1 + 30 + + + + + + + + 1 + 30 + + + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + + + + + + 1 + 10 + + + 1 + 10 + + + 1 + 10 + + + + + 1 + 10 + + + 1 + 10 + + + + + + + + + + + + 1 + 10 + + + 1 + 10 + + + + + 1 + 10 + + + + + + + + + + + + + +)V0G0N"; + protected: std::string getModel() const { std::string model = _model; @@ -108,28 +206,35 @@ class LocaleTests : public ::testing::Test { return model; } - void testBody() const { + void testBody(bool isLSTM = false) const { CNNNetReader reader; // This model contains layers with float attributes. // Conversion from string may be affected by locale. - auto model = getModel(); + std::string model = isLSTM ? _model_LSTM : getModel(); reader.ReadNetwork(model.data(), model.length()); auto net = reader.getNetwork(); - auto power_layer = dynamic_pointer_cast(net.getLayerByName("power")); - ASSERT_EQ(power_layer->scale, 0.75f); - ASSERT_EQ(power_layer->offset, 0.35f); - ASSERT_EQ(power_layer->power, 0.5f); + if (!isLSTM) { + auto power_layer = dynamic_pointer_cast(net.getLayerByName("power")); + ASSERT_EQ(power_layer->scale, 0.75f); + ASSERT_EQ(power_layer->offset, 0.35f); + ASSERT_EQ(power_layer->power, 0.5f); - auto sum_layer = dynamic_pointer_cast(net.getLayerByName("sum")); - std::vector ref_coeff {0.77f, 0.33f}; - ASSERT_EQ(sum_layer->coeff, ref_coeff); + auto sum_layer = dynamic_pointer_cast(net.getLayerByName("sum")); + std::vector ref_coeff{0.77f, 0.33f}; + ASSERT_EQ(sum_layer->coeff, ref_coeff); - auto info = net.getInputsInfo(); - auto preproc = info.begin()->second->getPreProcess(); - ASSERT_EQ(preproc[0]->stdScale, 0.1f); - ASSERT_EQ(preproc[0]->meanValue, 104.006f); + auto info = net.getInputsInfo(); + auto preproc = info.begin()->second->getPreProcess(); + ASSERT_EQ(preproc[0]->stdScale, 0.1f); + ASSERT_EQ(preproc[0]->meanValue, 104.006f); + } else { + InferenceEngine::NetPass::UnrollRNN_if(net, [] (const RNNCellBase& rnn) -> bool { return true; }); + auto lstmcell_layer = dynamic_pointer_cast(net.getLayerByName("LSTMCell")); + float ref_coeff(0.2f); + ASSERT_EQ(lstmcell_layer->clip, ref_coeff); + } } }; @@ -145,6 +250,18 @@ TEST_F(LocaleTests, WithUSLocale) { setlocale(LC_ALL, ""); } +TEST_F(LocaleTests, WithRULocaleOnLSTM) { + setlocale(LC_ALL, "ru_RU.UTF-8"); + testBody(true); + setlocale(LC_ALL, ""); +} + +TEST_F(LocaleTests, WithUSLocaleOnLSTM) { + setlocale(LC_ALL, "en_US.UTF-8"); + testBody(true); + setlocale(LC_ALL, ""); +} + TEST_F(LocaleTests, DISABLED_WithRULocaleCPP) { auto prev = std::locale(); std::locale::global(std::locale("ru_RU.UTF-8")); diff --git a/inference-engine/tests/unit/inference_engine_tests/ngraph_reader_tests.cpp b/inference-engine/tests/unit/inference_engine_tests/ngraph_reader_tests.cpp index 7609a809c8cfdc..4b707f7bd1d99f 100644 --- a/inference-engine/tests/unit/inference_engine_tests/ngraph_reader_tests.cpp +++ b/inference-engine/tests/unit/inference_engine_tests/ngraph_reader_tests.cpp @@ -25,7 +25,7 @@ class NGraphReaderTests : public TestsCommon { void TearDown() override {} void SetUp() override {} - void compareICNNNetworks(const ICNNNetwork& newNetwork, const ICNNNetwork& oldNetwork) { + void compareICNNNetworks(ICNNNetwork::Ptr newNetwork, const CNNNetwork& oldNetwork) { auto compareParamVal = [](const std::string& val1, const std::string& val2) -> bool { std::vector vals1, vals2; std::stringstream ss1(val1); @@ -62,10 +62,10 @@ class NGraphReaderTests : public TestsCommon { return true; }; std::vector err_log; - CNNNetwork network((ICNNNetwork*)&newNetwork); - CNNNetwork refNetwork((ICNNNetwork*)&oldNetwork); - if (newNetwork.layerCount() != oldNetwork.layerCount()) - THROW_IE_EXCEPTION << "ICNNNetworks have different numbers of layers! " + std::to_string(newNetwork.layerCount()) + " and " + std::to_string(oldNetwork.layerCount()); + CNNNetwork network(newNetwork); + CNNNetwork refNetwork(oldNetwork); + if (newNetwork->layerCount() != oldNetwork.layerCount()) + THROW_IE_EXCEPTION << "ICNNNetworks have different numbers of layers! " + std::to_string(newNetwork->layerCount()) + " and " + std::to_string(oldNetwork.layerCount()); auto newIterator = network.begin(); auto oldIterator = refNetwork.begin(); for (; newIterator != network.end() && oldIterator != refNetwork.end(); newIterator++, oldIterator++) { @@ -120,12 +120,10 @@ class NGraphReaderTests : public TestsCommon { InputsDataMap newInput; OutputsDataMap newOutput; - newNetwork.getInputsInfo(newInput); - newNetwork.getOutputsInfo(newOutput); - InputsDataMap oldInput; - OutputsDataMap oldOutput; - oldNetwork.getInputsInfo(oldInput); - oldNetwork.getOutputsInfo(oldOutput); + newNetwork->getInputsInfo(newInput); + newNetwork->getOutputsInfo(newOutput); + InputsDataMap oldInput = oldNetwork.getInputsInfo(); + OutputsDataMap oldOutput = oldNetwork.getOutputsInfo(); bool success = newInput.size() == oldInput.size(); for (const auto& it : newInput) { @@ -181,7 +179,7 @@ TEST_F(NGraphReaderTests, ReadScalarNetwork) { Blob::CPtr blob; auto nGraph = reader.read(model, blob); ICNNNetwork::Ptr network = convertFunctionToICNNNetwork(nGraph); - CNNNetwork cnetwork(network.get()); + CNNNetwork cnetwork(network); cnetwork.begin(); } @@ -499,7 +497,7 @@ std::string modelV5 = R"V0G0N( InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, DISABLED_ReadProposalNetwork) { @@ -661,7 +659,7 @@ std::string modelV5 = R"V0G0N( InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadPriorBoxNetwork) { @@ -906,7 +904,7 @@ std::string modelV5 = R"V0G0N( InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadSplitNetwork) { @@ -1030,7 +1028,7 @@ TEST_F(NGraphReaderTests, ReadSplitNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, DISABLED_ReadDetectionOutputNetwork) { @@ -1187,7 +1185,7 @@ TEST_F(NGraphReaderTests, DISABLED_ReadDetectionOutputNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadConcatNetwork) { @@ -1322,7 +1320,7 @@ TEST_F(NGraphReaderTests, ReadConcatNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, DISABLED_ReadTopKNetwork) { @@ -1431,7 +1429,7 @@ TEST_F(NGraphReaderTests, DISABLED_ReadTopKNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadMVNNetwork) { @@ -1532,7 +1530,7 @@ TEST_F(NGraphReaderTests, ReadMVNNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadLrnNetwork) { @@ -1633,7 +1631,7 @@ TEST_F(NGraphReaderTests, ReadLrnNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, DISABLED_ReadLrnNetwork2) { @@ -1774,7 +1772,7 @@ TEST_F(NGraphReaderTests, DISABLED_ReadLrnNetwork2) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } @@ -1876,7 +1874,7 @@ TEST_F(NGraphReaderTests, ReadClampNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadSigmoidNetwork) { @@ -1975,7 +1973,7 @@ TEST_F(NGraphReaderTests, ReadSigmoidNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadPReLUNetwork) { @@ -2098,7 +2096,7 @@ TEST_F(NGraphReaderTests, ReadPReLUNetwork) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadELUNetwork) { @@ -2199,7 +2197,7 @@ TEST_F(NGraphReaderTests, ReadELUNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadShapeOfNetwork) { @@ -2289,7 +2287,7 @@ TEST_F(NGraphReaderTests, ReadShapeOfNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadLeakyReLUNetwork) { @@ -2390,7 +2388,7 @@ TEST_F(NGraphReaderTests, ReadLeakyReLUNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadTanhNetwork) { @@ -2489,7 +2487,7 @@ TEST_F(NGraphReaderTests, ReadTanhNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadExpNetwork) { @@ -2588,7 +2586,7 @@ TEST_F(NGraphReaderTests, ReadExpNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadReLUNetwork) { @@ -2687,7 +2685,7 @@ TEST_F(NGraphReaderTests, ReadReLUNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadBroadcastNetwork) { @@ -2855,7 +2853,7 @@ TEST_F(NGraphReaderTests, ReadSoftMaxNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadMaxPoolNetwork) { @@ -2956,7 +2954,7 @@ TEST_F(NGraphReaderTests, ReadMaxPoolNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadAvgPoolNetwork) { std::string model = R"V0G0N( @@ -3056,7 +3054,7 @@ TEST_F(NGraphReaderTests, ReadAvgPoolNetwork) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); }; TEST_F(NGraphReaderTests, ReadReLUNetworkWithoutTopologicalOrder) { @@ -3155,7 +3153,7 @@ TEST_F(NGraphReaderTests, ReadReLUNetworkWithoutTopologicalOrder) { InferenceEngine::CNNNetReader net_reader; net_reader.ReadNetwork(modelV5.data(), modelV5.length()); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadTileNetwork) { @@ -3276,7 +3274,7 @@ TEST_F(NGraphReaderTests, ReadTileNetwork) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadTileNetwork2) { @@ -3437,7 +3435,7 @@ TEST_F(NGraphReaderTests, ReadTileNetwork2) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadTransposeNetwork) { @@ -3558,7 +3556,7 @@ TEST_F(NGraphReaderTests, ReadTransposeNetwork) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadReshapeNetwork) { @@ -3684,7 +3682,7 @@ TEST_F(NGraphReaderTests, ReadReshapeNetwork) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadSqueeze) { @@ -3812,7 +3810,7 @@ TEST_F(NGraphReaderTests, ReadSqueeze) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadInterpolateNetwork) { @@ -3932,7 +3930,7 @@ TEST_F(NGraphReaderTests, ReadInterpolateNetwork) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadMatMulNetwork) { @@ -4038,7 +4036,7 @@ TEST_F(NGraphReaderTests, ReadMatMulNetwork) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadDeconvolution3DNetwork) { @@ -4172,7 +4170,7 @@ TEST_F(NGraphReaderTests, ReadDeconvolution3DNetwork) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadDeconvolution2DNetwork) { @@ -4297,7 +4295,7 @@ TEST_F(NGraphReaderTests, ReadDeconvolution2DNetwork) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadConvolutionNetwork) { @@ -4422,7 +4420,7 @@ TEST_F(NGraphReaderTests, ReadConvolutionNetwork) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadMaximumNetwork) { @@ -4565,7 +4563,7 @@ TEST_F(NGraphReaderTests, ReadMaximumNetwork) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadDivideNetwork) { @@ -4708,7 +4706,7 @@ TEST_F(NGraphReaderTests, ReadDivideNetwork) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadPowNetwork) { @@ -4851,7 +4849,7 @@ TEST_F(NGraphReaderTests, ReadPowNetwork) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadMultiplyNetwork) { @@ -4994,7 +4992,7 @@ TEST_F(NGraphReaderTests, ReadMultiplyNetwork) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ReadAddNoBroadcastNetwork) { @@ -5137,7 +5135,7 @@ TEST_F(NGraphReaderTests, ReadAddNoBroadcastNetwork) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, DISABLED_ReadAddNetwork) { @@ -5276,7 +5274,7 @@ TEST_F(NGraphReaderTests, DISABLED_ReadAddNetwork) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ConvBiasFusion) { @@ -5478,7 +5476,7 @@ TEST_F(NGraphReaderTests, ConvBiasFusion) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ConvBiasFusionFP16) { @@ -5680,7 +5678,7 @@ TEST_F(NGraphReaderTests, ConvBiasFusionFP16) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, DISABLED_MatMulBiasFusion) { @@ -5852,7 +5850,7 @@ TEST_F(NGraphReaderTests, DISABLED_MatMulBiasFusion) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, MatMulBiasFusionNoBroadcast) { @@ -5987,7 +5985,7 @@ TEST_F(NGraphReaderTests, MatMulBiasFusionNoBroadcast) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ConvertMulAddToScaleShift) { @@ -6244,7 +6242,7 @@ TEST_F(NGraphReaderTests, ConvertMulAddToScaleShift) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ConvertMulAddToPower) { @@ -6508,7 +6506,7 @@ TEST_F(NGraphReaderTests, ConvertMulAddToPower) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ConvertMulToPower) { @@ -6687,7 +6685,7 @@ TEST_F(NGraphReaderTests, ConvertMulToPower) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ConvertAddToPower) { @@ -6866,7 +6864,7 @@ TEST_F(NGraphReaderTests, ConvertAddToPower) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ConvertMulToScaleShift) { @@ -7039,7 +7037,7 @@ TEST_F(NGraphReaderTests, ConvertMulToScaleShift) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ConvertAddToScaleShift) { @@ -7212,7 +7210,7 @@ TEST_F(NGraphReaderTests, ConvertAddToScaleShift) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ConvertMulToEltwise) { @@ -7398,7 +7396,7 @@ TEST_F(NGraphReaderTests, ConvertMulToEltwise) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ConvertAddToEltwise) { @@ -7584,7 +7582,7 @@ TEST_F(NGraphReaderTests, ConvertAddToEltwise) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ConvertBroadcastToTiles1) { @@ -7769,7 +7767,7 @@ TEST_F(NGraphReaderTests, ConvertBroadcastToTiles1) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ConvertBroadcastToTiles2) { @@ -7967,7 +7965,7 @@ TEST_F(NGraphReaderTests, ConvertBroadcastToTiles2) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, ConvertBroadcastToTiles3) { @@ -8107,7 +8105,7 @@ TEST_F(NGraphReaderTests, ConvertBroadcastToTiles3) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } TEST_F(NGraphReaderTests, DISABLED_ConvertMulAddToScaleShiftTest) { @@ -8288,5 +8286,5 @@ TEST_F(NGraphReaderTests, DISABLED_ConvertMulAddToScaleShiftTest) { net_reader.ReadNetwork(modelV5.data(), modelV5.length()); net_reader.SetWeights(tWeights); - compareICNNNetworks(*network, net_reader.getNetwork()); + compareICNNNetworks(network, net_reader.getNetwork()); } diff --git a/inference-engine/tests/unit/inference_engine_tests/plugin_dispatcher_tests.cpp b/inference-engine/tests/unit/inference_engine_tests/plugin_dispatcher_tests.cpp index e3855bfe9feda3..c280ed883bac75 100644 --- a/inference-engine/tests/unit/inference_engine_tests/plugin_dispatcher_tests.cpp +++ b/inference-engine/tests/unit/inference_engine_tests/plugin_dispatcher_tests.cpp @@ -95,27 +95,11 @@ TEST_F(PluginDispatcherTests, throwsOnUnknownPlugin) { ASSERT_THROW(dispatcher.getPluginByName(nameExt("unknown_plugin")), InferenceEngine::details::InferenceEngineException); } -TEST_F(PluginDispatcherTests, throwsOnDeviceWithoutPlugins) { - PluginDispatcher dispatcher({ "./", "./lib" }); - ASSERT_THROW(dispatcher.getSuitablePlugin(TargetDevice::eBalanced), - InferenceEngine::details::InferenceEngineException); -} - ACTION(ThrowException) { THROW_IE_EXCEPTION << "Exception!"; } -TEST_F(PluginDispatcherTests, triesToLoadEveryPluginSuitableForDevice) { - MockDispatcher disp({ "./", "./lib" }); - - ON_CALL(disp, getPluginByName(_)).WillByDefault(ThrowException()); -#ifdef ENABLE_MKL_DNN - EXPECT_CALL(disp, getPluginByName(nameExt("MKLDNNPlugin"))).Times(1); -#endif - ASSERT_THROW(disp.getSuitablePlugin(TargetDevice::eCPU), InferenceEngine::details::InferenceEngineException); -} - #if defined(ENABLE_MKL_DNN) TEST_F(PluginDispatcherTests, returnsIfLoadSuccessfull) { MockDispatcher disp({ "./", "./lib" }); @@ -123,7 +107,7 @@ TEST_F(PluginDispatcherTests, returnsIfLoadSuccessfull) { auto ptr = dispatcher.getPluginByName(nameExt("mock_engine")); EXPECT_CALL(disp, getPluginByName(_)).WillOnce(Return(ptr)); - ASSERT_NO_THROW(disp.getSuitablePlugin(TargetDevice::eCPU)); + ASSERT_NO_THROW(disp.getPluginByName(nameExt("MKLDNNPlugin"))); } #if defined ENABLE_MKL_DNN && !defined _WIN32 && !defined __CYGWIN__ && !defined __APPLE__ diff --git a/inference-engine/tests/unit/inference_engine_tests/range_iterator_tests.cpp b/inference-engine/tests/unit/inference_engine_tests/range_iterator_tests.cpp deleted file mode 100644 index 367840a5ddb449..00000000000000 --- a/inference-engine/tests/unit/inference_engine_tests/range_iterator_tests.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include -#include -#include "range_iterator.hpp" -#include - -using namespace std; -using namespace InferenceEngine; - -class RangeIteratorTests: public ::testing::Test { - protected: - virtual void TearDown() { - } - - virtual void SetUp() { - } - - public: - -}; - -TEST_F(RangeIteratorTests, canCompareSameStringsInsensitive) { - ASSERT_FALSE(std::lexicographical_compare(null_terminated_string("UPPer"), - null_terminated_string_end(), - null_terminated_string("upper"), - null_terminated_string_end(), [](char a, char b) { - std::locale loc; - return std::tolower(a, loc) > std::tolower(b, loc); - })); -} - -TEST_F(RangeIteratorTests, canCompareNotSameStringsInsensitive) { - ASSERT_TRUE(std::lexicographical_compare(null_terminated_string("UPPer"), - null_terminated_string_end(), - null_terminated_string("uppel"), - null_terminated_string_end(), [](char a, char b) { - std::locale loc; - return std::tolower(a, loc) > std::tolower(b, loc); - })); - -} - -TEST_F(RangeIteratorTests, cannotDereferenceEndIterator) { - ASSERT_ANY_THROW(*null_terminated_string_end()); - ASSERT_ANY_THROW(++null_terminated_string_end()); - ASSERT_ANY_THROW(null_terminated_string_end()++); -} diff --git a/inference-engine/tests/unit/inference_engine_tests/util_const_infer_test.cpp b/inference-engine/tests/unit/inference_engine_tests/util_const_infer_test.cpp index 81fad6340520b5..5107576a99b76e 100644 --- a/inference-engine/tests/unit/inference_engine_tests/util_const_infer_test.cpp +++ b/inference-engine/tests/unit/inference_engine_tests/util_const_infer_test.cpp @@ -19,6 +19,7 @@ #include "util_test.hpp" #include "util_const_infer_test.hpp" #include
+#include namespace IE = InferenceEngine; @@ -123,7 +124,8 @@ IE::BlobMap RemoveLayerTests::fillConstData(const std::vector& cons IE::Blob::Ptr blob = make_blob_with_precision(desc); blob->allocate(); auto* buffer = blob->buffer().as(); - for (int i = 0; i < blob->size(); i++) { + size_t buffer_length = blob->byteSize() / sizeof(float); + for (int i = 0; i < buffer_length; i++) { buffer[i] = i + 1; } constData[outData->getName()] = blob; @@ -145,6 +147,75 @@ IE::BlobMap RemoveLayerTests::initConstLayers(const std::vector& co return customBlobs; } +IE::BlobMap RemoveLayerTests::fillConstDataDiffPrec (const std::vector& constLayers) { + IE::BlobMap constData; + for (const auto& name:constLayers) { + auto layer = getLayer(name); + for (const auto& outData:layer->outData) { + IE::TensorDesc desc = outData->getTensorDesc(); + IE::Blob::Ptr blob = make_blob_with_precision(desc); + blob->allocate(); + switch(layer->precision) { + case IE::Precision::U8: { + auto *buffer = blob->buffer().as(); + for (int i = 0; i < blob->size(); i++) { + buffer[i] = i + 2; + } + break; + } + case IE::Precision::I32: { + auto *buffer = blob->buffer().as(); + for (int i = 0; i < blob->size(); i++) { + buffer[i] = i + 2; + } + break; + } + case IE::Precision::I64: { + auto *buffer = blob->buffer().as(); + for (int i = 0; i < blob->size(); i++) { + buffer[i] = i + 2; + } + break; + } + case IE::Precision::FP16: { + auto *buffer = blob->buffer().as(); + float j = 0; + for (int i = 0; i < blob->size(); i++) { + buffer[i] = j + (float)2; + buffer[i] = IE::PrecisionUtils::f32tof16(buffer[i]); + j++; + } + break; + } + case IE::Precision::FP32: { + auto *buffer = blob->buffer().as(); + for (int i = 0; i < blob->size(); i++) { + buffer[i] = i + 2; + } + break; + } + default: + THROW_IE_EXCEPTION << "Not supported data type"; + } + constData[outData->getName()] = blob; + } + } + return constData; +} + +IE::BlobMap RemoveLayerTests::initConstLayersDiffPrec(const std::vector &constLayers) { + for (const auto& name : constLayers) { + getLayer(name)->type = "Const"; + } + IE::BlobMap customBlobs = fillConstDataDiffPrec(constLayers); + for (const auto& layerName: constLayers) { + auto layer = getLayer(layerName); + layer->type = "Const"; + layer->blobs["custom"] = customBlobs[layer->outData[0]->getName()]; + } + return customBlobs; +} + TEST_F(RemoveLayerTests, canTrimL2) { auto layer1 = getLayer("layer1"); auto layer4 = getLayer("layer4"); @@ -828,3 +899,712 @@ TEST_F(AdvancedShapeInferTests, canReshapeWithScalar) { ASSERT_EQ(getData("data1")->getTensorDesc().getDims(), newInShape); ASSERT_EQ(getData("data3")->getTensorDesc().getDims(), newOutShape); } + +TEST_F(AdvancedShapeInferTests, canFoldConstWithOneHot) { + // Const-d1-OneHot-d2 + // \ + // I1-d3-Eltw(Sum)-d4 + auto testFunc = [&](IE::Precision precision) { + netBuilder = NetBuilder(); + net = netBuilder + .data("data1", IE::SizeVector{2}, precision, IE::Layout::C) + .data("data2", IE::SizeVector{2, 10}, precision, IE::Layout::NC) + .data("data3", IE::SizeVector{2, 10}, precision, IE::Layout::NC) + .data("data4", IE::SizeVector{2, 10}, precision, IE::Layout::NC) + .layer(IE::LayerParams{"const", "dummy", precision}) + .layer(IE::LayerParams{"oneHot", "OneHot", precision}) + .layer(IE::LayerParams{"input", "input", precision}) + .layer(IE::LayerParams{"eltwise", "Eltwise", precision}) + .linkToData("const", "data1") + .linkDataTo("data1", "oneHot") + .linkToData("oneHot", "data2") + .linkDataTo("data2", "eltwise") + .linkToData("input", "data3") + .linkDataTo("data3", "eltwise") + .linkToData("eltwise", "data4") + .addInput("data3") + .finalize(); + getLayer("oneHot")->params = { + {"axis", "-1"}, + {"depth", "10"}, + {"off_value", "1.0"}, + {"on_value", "1.0"} + }; + getLayer("eltwise")->params = { + {"operation", "sum"} + }; + originalLayersNum = net->allLayers().size(); + + IE::CNNNetwork cnnNetwork(net); + initConstLayers({"const"}); + IE::ConstTransformer transformator(net.get()); + transformator.fullTrim(); + + ASSERT_EQ(net->allLayers().size(), originalLayersNum - 1); + }; + + testFunc(IE::Precision::FP32); + testFunc(IE::Precision::FP16); + testFunc(IE::Precision::Q78); + testFunc(IE::Precision::I16); + testFunc(IE::Precision::U8); + testFunc(IE::Precision::I8); + testFunc(IE::Precision::U16); + testFunc(IE::Precision::I32); + testFunc(IE::Precision::I64); +} + +TEST_F(AdvancedShapeInferTests, MulWithTensorConstInferTest) { + + auto testFunc = [&](IE::Precision precisionInData1, IE::Precision precisionInData2, IE::Precision precisionOutData) { + + netBuilder = NetBuilder(); + net = netBuilder + .data("data1", IE::SizeVector{2, 2}, precisionInData1, IE::Layout::NC) + .data("data2", IE::SizeVector{2, 2}, precisionInData2, IE::Layout::NC) + .data("data3", IE::SizeVector{2, 2}, precisionOutData, IE::Layout::NC) + .layer(IE::LayerParams{"mulLayer", "Eltwise"}) + .layer(IE::LayerParams{"input1", "Const", precisionInData1}) + .layer(IE::LayerParams{"input2", "Const", precisionInData2}) + .linkToData("input1", "data1") + .linkToData("input2", "data2") + .linkDataTo("data1", "mulLayer") + .linkDataTo("data2", "mulLayer") + .linkToData("mulLayer", "data3") + .addInput("data1") + .addInput("data2") + .finalize(); + + getLayer("mulLayer")->params = { + {"operation", "mul"} + }; + + IE::CNNNetwork cnnNetwork(net); + initConstLayersDiffPrec({"input1", "input2"}); + float ref[] = {4, 9, 16, 25}; + if (precisionOutData == IE::Precision::FP16) { + for (int i = 0; i < 4; i++) + ref[i] = IE::PrecisionUtils::f32tof16(ref[i]); + } + IE::ConstTransformer transformator(net.get()); + transformator.foldConstSubgraphs(); + switch(precisionOutData) { + case IE::Precision::U8: { + auto *l = cnnNetwork.getLayerByName("mulLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::I32: { + auto *l = cnnNetwork.getLayerByName("mulLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::I64: { + auto *l = cnnNetwork.getLayerByName("mulLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::FP16: { + auto *l = cnnNetwork.getLayerByName("mulLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::FP32: { + auto *l = cnnNetwork.getLayerByName("mulLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + default: + THROW_IE_EXCEPTION << "Unsupported precision!"; + } + }; + + testFunc(IE::Precision::U8, IE::Precision::U8, IE::Precision::U8); + testFunc(IE::Precision::U8, IE::Precision::I32, IE::Precision::I32); + testFunc(IE::Precision::U8, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::U8, IE::Precision::FP16, IE::Precision::FP16); + testFunc(IE::Precision::U8, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::I32, IE::Precision::U8, IE::Precision::I32); + testFunc(IE::Precision::I32, IE::Precision::I32, IE::Precision::I32); + testFunc(IE::Precision::I32, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::I32, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::I32, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::I64, IE::Precision::U8, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::I32, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::I64, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::U8, IE::Precision::FP16); + testFunc(IE::Precision::FP16, IE::Precision::I32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::I64, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::FP16, IE::Precision::FP16); + testFunc(IE::Precision::FP16, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::U8, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::I32, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::I64, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::FP32, IE::Precision::FP16); + testFunc(IE::Precision::FP32, IE::Precision::FP16, IE::Precision::FP16); +} + + +TEST_F(AdvancedShapeInferTests, MulWithScalarConstInferTest) { + + auto testFunc = [&](IE::Precision precisionInData1, IE::Precision precisionInData2, IE::Precision precisionOutData) { + + netBuilder = NetBuilder(); + net = netBuilder + .data("data1", IE::SizeVector{2, 2}, precisionInData1, IE::Layout::NC) + .data("data2", IE::SizeVector{}, precisionInData2, IE::Layout::SCALAR) + .data("data3", IE::SizeVector{2, 2}, precisionOutData, IE::Layout::NC) + .layer(IE::LayerParams{"mulLayer", "Eltwise"}) + .layer(IE::LayerParams{"input1", "Const", precisionInData1}) + .layer(IE::LayerParams{"input2", "Const", precisionInData2}) + .linkToData("input1", "data1") + .linkToData("input2", "data2") + .linkDataTo("data1", "mulLayer") + .linkDataTo("data2", "mulLayer") + .linkToData("mulLayer", "data3") + .addInput("data1") + .addInput("data2") + .finalize(); + + getLayer("mulLayer")->params = { + {"operation", "mul"} + }; + + IE::CNNNetwork cnnNetwork(net); + initConstLayersDiffPrec({"input1", "input2"}); + float ref[] = {4, 6, 8, 10}; + if (precisionOutData == IE::Precision::FP16) { + for (int i = 0; i < 4; i++) + ref[i] = IE::PrecisionUtils::f32tof16(ref[i]); + } + IE::ConstTransformer transformator(net.get()); + transformator.foldConstSubgraphs(); + switch(precisionOutData) { + case IE::Precision::U8: { + auto *l = cnnNetwork.getLayerByName("mulLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::I32: { + auto *l = cnnNetwork.getLayerByName("mulLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::I64: { + auto *l = cnnNetwork.getLayerByName("mulLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::FP16: { + auto *l = cnnNetwork.getLayerByName("mulLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::FP32: { + auto *l = cnnNetwork.getLayerByName("mulLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + default: + THROW_IE_EXCEPTION << "Unsupported precision!"; + } + }; + + testFunc(IE::Precision::U8, IE::Precision::U8, IE::Precision::U8); + testFunc(IE::Precision::U8, IE::Precision::I32, IE::Precision::I32); + testFunc(IE::Precision::U8, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::U8, IE::Precision::FP16, IE::Precision::FP16); + testFunc(IE::Precision::U8, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::I32, IE::Precision::U8, IE::Precision::I32); + testFunc(IE::Precision::I32, IE::Precision::I32, IE::Precision::I32); + testFunc(IE::Precision::I32, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::I32, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::I32, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::I64, IE::Precision::U8, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::I32, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::I64, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::U8, IE::Precision::FP16); + testFunc(IE::Precision::FP16, IE::Precision::I32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::I64, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::FP16, IE::Precision::FP16); + testFunc(IE::Precision::FP16, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::U8, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::I32, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::I64, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::FP32, IE::Precision::FP16); + testFunc(IE::Precision::FP32, IE::Precision::FP16, IE::Precision::FP16); +} + +TEST_F(AdvancedShapeInferTests, AddWithScalarConstInferTest) { + + auto testFunc = [&](IE::Precision precisionInData1, IE::Precision precisionInData2, IE::Precision precisionOutData) { + + netBuilder = NetBuilder(); + net = netBuilder + .data("data1", IE::SizeVector{2, 2}, precisionInData1, IE::Layout::NC) + .data("data2", IE::SizeVector{}, precisionInData2, IE::Layout::SCALAR) + .data("data3", IE::SizeVector{2, 2}, precisionOutData, IE::Layout::NC) + .layer(IE::LayerParams{"addLayer", "Eltwise"}) + .layer(IE::LayerParams{"input1", "Const", precisionInData1}) + .layer(IE::LayerParams{"input2", "Const", precisionInData2}) + .linkToData("input1", "data1") + .linkToData("input2", "data2") + .linkDataTo("data1", "addLayer") + .linkDataTo("data2", "addLayer") + .linkToData("addLayer", "data3") + .addInput("data1") + .addInput("data2") + .finalize(); + + getLayer("addLayer")->params = { + {"operation", "sum"} + }; + + IE::CNNNetwork cnnNetwork(net); + initConstLayersDiffPrec({"input1", "input2"}); + float ref[] = {4, 5, 6, 7}; + if (precisionOutData == IE::Precision::FP16) { + for (int i = 0; i < 4; i++) + ref[i] = IE::PrecisionUtils::f32tof16(ref[i]); + } + IE::ConstTransformer transformator(net.get()); + transformator.foldConstSubgraphs(); + switch(precisionOutData) { + case IE::Precision::U8: { + auto *l = cnnNetwork.getLayerByName("addLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::I32: { + auto *l = cnnNetwork.getLayerByName("addLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::I64: { + auto *l = cnnNetwork.getLayerByName("addLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::FP16: { + auto *l = cnnNetwork.getLayerByName("addLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::FP32: { + auto *l = cnnNetwork.getLayerByName("addLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + default: + THROW_IE_EXCEPTION << "Unsupported precision!"; + } + }; + + testFunc(IE::Precision::U8, IE::Precision::U8, IE::Precision::U8); + testFunc(IE::Precision::U8, IE::Precision::I32, IE::Precision::I32); + testFunc(IE::Precision::U8, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::U8, IE::Precision::FP16, IE::Precision::FP16); + testFunc(IE::Precision::U8, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::I32, IE::Precision::U8, IE::Precision::I32); + testFunc(IE::Precision::I32, IE::Precision::I32, IE::Precision::I32); + testFunc(IE::Precision::I32, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::I32, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::I32, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::I64, IE::Precision::U8, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::I32, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::I64, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::U8, IE::Precision::FP16); + testFunc(IE::Precision::FP16, IE::Precision::I32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::I64, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::FP16, IE::Precision::FP16); + testFunc(IE::Precision::FP16, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::U8, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::I32, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::I64, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::FP32, IE::Precision::FP16); + testFunc(IE::Precision::FP32, IE::Precision::FP16, IE::Precision::FP16); +} + +TEST_F(AdvancedShapeInferTests, AddWithTensorConstInferTest) { + + auto testFunc = [&](IE::Precision precisionInData1, IE::Precision precisionInData2, IE::Precision precisionOutData) { + + netBuilder = NetBuilder(); + net = netBuilder + .data("data1", IE::SizeVector{2, 2}, precisionInData1, IE::Layout::NC) + .data("data2", IE::SizeVector{2,2}, precisionInData2, IE::Layout::NC) + .data("data3", IE::SizeVector{2, 2}, precisionOutData, IE::Layout::NC) + .layer(IE::LayerParams{"addLayer", "Eltwise"}) + .layer(IE::LayerParams{"input1", "Const", precisionInData1}) + .layer(IE::LayerParams{"input2", "Const", precisionInData2}) + .linkToData("input1", "data1") + .linkToData("input2", "data2") + .linkDataTo("data1", "addLayer") + .linkDataTo("data2", "addLayer") + .linkToData("addLayer", "data3") + .addInput("data1") + .addInput("data2") + .finalize(); + + getLayer("addLayer")->params = { + {"operation", "sum"} + }; + + IE::CNNNetwork cnnNetwork(net); + initConstLayersDiffPrec({"input1", "input2"}); + float ref[] = {4, 6, 8, 10}; + if (precisionOutData == IE::Precision::FP16) { + for (int i = 0; i < 4; i++) + ref[i] = IE::PrecisionUtils::f32tof16(ref[i]); + } + IE::ConstTransformer transformator(net.get()); + transformator.foldConstSubgraphs(); + switch(precisionOutData) { + case IE::Precision::U8: { + auto *l = cnnNetwork.getLayerByName("addLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::I32: { + auto *l = cnnNetwork.getLayerByName("addLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::I64: { + auto *l = cnnNetwork.getLayerByName("addLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::FP16: { + auto *l = cnnNetwork.getLayerByName("addLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::FP32: { + auto *l = cnnNetwork.getLayerByName("addLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + default: + THROW_IE_EXCEPTION << "Unsupported precision!"; + } + }; + + testFunc(IE::Precision::U8, IE::Precision::U8, IE::Precision::U8); + testFunc(IE::Precision::U8, IE::Precision::I32, IE::Precision::I32); + testFunc(IE::Precision::U8, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::U8, IE::Precision::FP16, IE::Precision::FP16); + testFunc(IE::Precision::U8, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::I32, IE::Precision::U8, IE::Precision::I32); + testFunc(IE::Precision::I32, IE::Precision::I32, IE::Precision::I32); + testFunc(IE::Precision::I32, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::I32, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::I32, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::I64, IE::Precision::U8, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::I32, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::I64, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::U8, IE::Precision::FP16); + testFunc(IE::Precision::FP16, IE::Precision::I32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::I64, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::FP16, IE::Precision::FP16); + testFunc(IE::Precision::FP16, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::U8, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::I32, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::I64, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::FP32, IE::Precision::FP16); + testFunc(IE::Precision::FP32, IE::Precision::FP16, IE::Precision::FP16); +} + +TEST_F(AdvancedShapeInferTests, AddWithBroadcastingConstInferTest) { + + auto testFunc = [&](IE::Precision precisionInData1, IE::Precision precisionInData2, IE::Precision precisionOutData) { + + netBuilder = NetBuilder(); + net = netBuilder + .data("data1", IE::SizeVector{2, 2}, precisionInData1, IE::Layout::NC) + .data("data2", IE::SizeVector{1, 2}, precisionInData2, IE::Layout::NC) + .data("data3", IE::SizeVector{2, 2}, precisionOutData, IE::Layout::NC) + .layer(IE::LayerParams{"addLayer", "Eltwise"}) + .layer(IE::LayerParams{"input1", "Const", precisionInData1}) + .layer(IE::LayerParams{"input2", "Const", precisionInData2}) + .linkToData("input1", "data1") + .linkToData("input2", "data2") + .linkDataTo("data1", "addLayer") + .linkDataTo("data2", "addLayer") + .linkToData("addLayer", "data3") + .addInput("data1") + .addInput("data2") + .finalize(); + + getLayer("addLayer")->params = { + {"operation", "sum"} + }; + + IE::CNNNetwork cnnNetwork(net); + initConstLayersDiffPrec({"input1", "input2"}); + float ref[] = {4, 5, 7, 8}; + if (precisionOutData == IE::Precision::FP16) { + for (int i = 0; i < 4; i++) + ref[i] = IE::PrecisionUtils::f32tof16(ref[i]); + } + IE::ConstTransformer transformator(net.get()); + transformator.foldConstSubgraphs(); + switch(precisionOutData) { + case IE::Precision::U8: { + auto *l = cnnNetwork.getLayerByName("addLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::I32: { + auto *l = cnnNetwork.getLayerByName("addLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::I64: { + auto *l = cnnNetwork.getLayerByName("addLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::FP16: { + auto *l = cnnNetwork.getLayerByName("addLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::FP32: { + auto *l = cnnNetwork.getLayerByName("addLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + default: + THROW_IE_EXCEPTION << "Unsupported precision!"; + } + }; + + testFunc(IE::Precision::U8, IE::Precision::U8, IE::Precision::U8); + testFunc(IE::Precision::U8, IE::Precision::I32, IE::Precision::I32); + testFunc(IE::Precision::U8, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::U8, IE::Precision::FP16, IE::Precision::FP16); + testFunc(IE::Precision::U8, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::I32, IE::Precision::U8, IE::Precision::I32); + testFunc(IE::Precision::I32, IE::Precision::I32, IE::Precision::I32); + testFunc(IE::Precision::I32, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::I32, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::I32, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::I64, IE::Precision::U8, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::I32, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::I64, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::U8, IE::Precision::FP16); + testFunc(IE::Precision::FP16, IE::Precision::I32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::I64, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::FP16, IE::Precision::FP16); + testFunc(IE::Precision::FP16, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::U8, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::I32, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::I64, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::FP32, IE::Precision::FP16); + testFunc(IE::Precision::FP32, IE::Precision::FP16, IE::Precision::FP16); +} + +TEST_F(AdvancedShapeInferTests, MulWithBroadcastingConstInferTest) { + + auto testFunc = [&](IE::Precision precisionInData1, IE::Precision precisionInData2, IE::Precision precisionOutData) { + + netBuilder = NetBuilder(); + net = netBuilder + .data("data1", IE::SizeVector{2, 2}, precisionInData1, IE::Layout::NC) + .data("data2", IE::SizeVector{1, 2}, precisionInData2, IE::Layout::NC) + .data("data3", IE::SizeVector{2, 2}, precisionOutData, IE::Layout::NC) + .layer(IE::LayerParams{"mulLayer", "Eltwise"}) + .layer(IE::LayerParams{"input1", "Const", precisionInData1}) + .layer(IE::LayerParams{"input2", "Const", precisionInData2}) + .linkToData("input1", "data1") + .linkToData("input2", "data2") + .linkDataTo("data1", "mulLayer") + .linkDataTo("data2", "mulLayer") + .linkToData("mulLayer", "data3") + .addInput("data1") + .addInput("data2") + .finalize(); + + getLayer("mulLayer")->params = { + {"operation", "mul"} + }; + + IE::CNNNetwork cnnNetwork(net); + initConstLayersDiffPrec({"input1", "input2"}); + float ref[] = {4, 6, 12, 15}; + if (precisionOutData == IE::Precision::FP16) { + for (int i = 0; i < 4; i++) + ref[i] = IE::PrecisionUtils::f32tof16(ref[i]); + } + IE::ConstTransformer transformator(net.get()); + transformator.foldConstSubgraphs(); + switch(precisionOutData) { + case IE::Precision::U8: { + auto *l = cnnNetwork.getLayerByName("mulLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::I32: { + auto *l = cnnNetwork.getLayerByName("mulLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::I64: { + auto *l = cnnNetwork.getLayerByName("mulLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::FP16: { + auto *l = cnnNetwork.getLayerByName("mulLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + case IE::Precision::FP32: { + auto *l = cnnNetwork.getLayerByName("mulLayer__data3__Const").get()->blobs.at("custom")->cbuffer().as(); + ASSERT_EQ(l[0], ref[0]); + ASSERT_EQ(l[1], ref[1]); + ASSERT_EQ(l[2], ref[2]); + ASSERT_EQ(l[3], ref[3]); + break; + } + default: + THROW_IE_EXCEPTION << "Unsupported precision!"; + } + }; + + testFunc(IE::Precision::U8, IE::Precision::U8, IE::Precision::U8); + testFunc(IE::Precision::U8, IE::Precision::I32, IE::Precision::I32); + testFunc(IE::Precision::U8, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::U8, IE::Precision::FP16, IE::Precision::FP16); + testFunc(IE::Precision::U8, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::I32, IE::Precision::U8, IE::Precision::I32); + testFunc(IE::Precision::I32, IE::Precision::I32, IE::Precision::I32); + testFunc(IE::Precision::I32, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::I32, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::I32, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::I64, IE::Precision::U8, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::I32, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::I64, IE::Precision::I64); + testFunc(IE::Precision::I64, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::I64, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::U8, IE::Precision::FP16); + testFunc(IE::Precision::FP16, IE::Precision::I32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::I64, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::FP16, IE::Precision::FP16); + testFunc(IE::Precision::FP16, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::U8, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::I32, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::I64, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::FP16, IE::Precision::FP32); + testFunc(IE::Precision::FP32, IE::Precision::FP32, IE::Precision::FP32); + testFunc(IE::Precision::FP16, IE::Precision::FP32, IE::Precision::FP16); + testFunc(IE::Precision::FP32, IE::Precision::FP16, IE::Precision::FP16); +} diff --git a/inference-engine/tests/unit/inference_engine_tests/util_const_infer_test.hpp b/inference-engine/tests/unit/inference_engine_tests/util_const_infer_test.hpp index b5fe89abe8a91b..10bdc5318b720a 100644 --- a/inference-engine/tests/unit/inference_engine_tests/util_const_infer_test.hpp +++ b/inference-engine/tests/unit/inference_engine_tests/util_const_infer_test.hpp @@ -74,6 +74,10 @@ class RemoveLayerTests : public testing::Test { IE::BlobMap initConstLayers(const std::vector& constLayers); + IE::BlobMap fillConstDataDiffPrec(const std::vector& constLayers); + + IE::BlobMap initConstLayersDiffPrec(const std::vector& constLayers); + NetBuilder netBuilder; IE::details::CNNNetworkImplPtr net; size_t originalLayersNum; diff --git a/inference-engine/tests/unit/mocks/cpp_interfaces/impl/mock_async_infer_request_thread_safe_internal.hpp b/inference-engine/tests/unit/mocks/cpp_interfaces/impl/mock_async_infer_request_thread_safe_internal.hpp index 6fdc1d0f6fa331..734b883cf3d085 100644 --- a/inference-engine/tests/unit/mocks/cpp_interfaces/impl/mock_async_infer_request_thread_safe_internal.hpp +++ b/inference-engine/tests/unit/mocks/cpp_interfaces/impl/mock_async_infer_request_thread_safe_internal.hpp @@ -20,7 +20,7 @@ class MockAsyncInferRequestThreadSafeInternal : public AsyncInferRequestThreadSa void setRequestBusy() { AsyncInferRequestThreadSafeInternal::setIsRequestBusy(true); } - + using AsyncInferRequestThreadSafeInternal::isRequestBusy; bool isRequestBusy() { return AsyncInferRequestThreadSafeInternal::isRequestBusy(); } diff --git a/inference-engine/tests/unit/mocks/mock_icnn_network.hpp b/inference-engine/tests/unit/mocks/mock_icnn_network.hpp index 1bdac7d8317103..1a15e52ffd1f76 100644 --- a/inference-engine/tests/unit/mocks/mock_icnn_network.hpp +++ b/inference-engine/tests/unit/mocks/mock_icnn_network.hpp @@ -35,7 +35,7 @@ class MockICNNNetwork : public InferenceEngine::ICNNNetwork { MOCK_QUALIFIED_METHOD1(setBatchSize, noexcept, InferenceEngine::StatusCode (const size_t size)); MOCK_QUALIFIED_METHOD2(setBatchSize, noexcept, InferenceEngine::StatusCode (const size_t size, InferenceEngine::ResponseDesc*)); MOCK_QUALIFIED_METHOD0(getBatchSize, const noexcept, size_t ()); - MOCK_QUALIFIED_METHOD0(getStats, const noexcept, InferenceEngine::ICNNNetworkStats& ()); + MOCK_QUALIFIED_METHOD2(getStats, const noexcept, InferenceEngine::StatusCode (InferenceEngine::ICNNNetworkStats** /*stats*/, InferenceEngine::ResponseDesc* /*resp*/)); MOCK_QUALIFIED_METHOD0(Release, noexcept, void ()); MOCK_QUALIFIED_METHOD1(getInputShapes, const noexcept, void (InferenceEngine::ICNNNetwork::InputShapes&)); MOCK_QUALIFIED_METHOD2(reshape, noexcept, InferenceEngine::StatusCode (const InferenceEngine::ICNNNetwork::InputShapes &, InferenceEngine::ResponseDesc *)); @@ -52,16 +52,16 @@ class MockCNNNetworkImpl: public InferenceEngine::details::CNNNetworkImpl { MOCK_QUALIFIED_METHOD0(getPrecision, const noexcept, InferenceEngine::Precision ()); MOCK_QUALIFIED_METHOD1(getOutputsInfo, const noexcept, void (InferenceEngine::OutputsDataMap& out)); MOCK_QUALIFIED_METHOD1(getInputsInfo, const noexcept, void (InferenceEngine::InputsDataMap &inputs)); - MOCK_QUALIFIED_METHOD1(getInput, noexcept, InferenceEngine::InputInfo::Ptr (const std::string &inputName)); + MOCK_QUALIFIED_METHOD1(getInput, const noexcept, InferenceEngine::InputInfo::Ptr (const std::string &inputName)); MOCK_QUALIFIED_METHOD2(getName, const noexcept, void (char* pName, size_t len)); MOCK_QUALIFIED_METHOD0(getName, const noexcept, const std::string& ()); MOCK_QUALIFIED_METHOD0(layerCount, const noexcept, size_t ()); MOCK_QUALIFIED_METHOD1(getData, noexcept, InferenceEngine::DataPtr&(const char* dname)); MOCK_QUALIFIED_METHOD1(addLayer, noexcept, void(const InferenceEngine::CNNLayerPtr& layer)); MOCK_QUALIFIED_METHOD3(addOutput, noexcept, InferenceEngine::StatusCode (const std::string &, size_t , InferenceEngine::ResponseDesc*)); - MOCK_QUALIFIED_METHOD3(getLayerByName, noexcept, InferenceEngine::StatusCode (const char* , InferenceEngine::CNNLayerPtr& , InferenceEngine::ResponseDesc* )); + MOCK_QUALIFIED_METHOD3(getLayerByName, const noexcept, InferenceEngine::StatusCode (const char* , InferenceEngine::CNNLayerPtr& , InferenceEngine::ResponseDesc* )); MOCK_QUALIFIED_METHOD1(setTargetDevice, noexcept, void (InferenceEngine::TargetDevice device)); - MOCK_QUALIFIED_METHOD0(getTargetDevice, noexcept, InferenceEngine::TargetDevice ()); + MOCK_QUALIFIED_METHOD0(getTargetDevice, const noexcept, InferenceEngine::TargetDevice ()); MOCK_QUALIFIED_METHOD1(setBatchSize, noexcept, InferenceEngine::StatusCode (const size_t size)); MOCK_QUALIFIED_METHOD2(setBatchSize, noexcept, InferenceEngine::StatusCode (const size_t size, InferenceEngine::ResponseDesc*)); MOCK_QUALIFIED_METHOD0(getBatchSize, const noexcept, size_t ()); diff --git a/inference-engine/tests/unit/mocks/mock_not_empty_icnn_network.hpp b/inference-engine/tests/unit/mocks/mock_not_empty_icnn_network.hpp index 1edefb728d2b01..563005cde76acd 100644 --- a/inference-engine/tests/unit/mocks/mock_not_empty_icnn_network.hpp +++ b/inference-engine/tests/unit/mocks/mock_not_empty_icnn_network.hpp @@ -42,7 +42,7 @@ class MockNotEmptyICNNNetwork : public ICNNNetwork { MOCK_QUALIFIED_METHOD1(setBatchSize, noexcept, StatusCode (const size_t size)); MOCK_QUALIFIED_METHOD2(setBatchSize, noexcept, StatusCode (const size_t size, ResponseDesc*)); MOCK_QUALIFIED_METHOD0(getBatchSize, const noexcept, size_t ()); - MOCK_QUALIFIED_METHOD0(getStats, const noexcept, InferenceEngine::ICNNNetworkStats& ()); + MOCK_QUALIFIED_METHOD2(getStats, const noexcept, InferenceEngine::StatusCode (InferenceEngine::ICNNNetworkStats** /*stats*/, InferenceEngine::ResponseDesc* /*resp*/)); MOCK_QUALIFIED_METHOD0(Release, noexcept, void ()); MOCK_QUALIFIED_METHOD1(getInputShapes, const noexcept, void (ICNNNetwork::InputShapes &)); MOCK_QUALIFIED_METHOD2(reshape, noexcept, StatusCode (const ICNNNetwork::InputShapes &, ResponseDesc *)); diff --git a/inference-engine/tests/unit/shape_infer/built_in_shape_infer_general_test.cpp b/inference-engine/tests/unit/shape_infer/built_in_shape_infer_general_test.cpp index 6e1f7b590b669d..cb9545e2efd7ef 100644 --- a/inference-engine/tests/unit/shape_infer/built_in_shape_infer_general_test.cpp +++ b/inference-engine/tests/unit/shape_infer/built_in_shape_infer_general_test.cpp @@ -790,6 +790,42 @@ INSTANTIATE_TEST_CASE_P( {{2, 128, 10, 10}}}), MapParams(MapStrStr(std::map{ {"levels", "2"}})), LayerDataName("data"), + CanInfer(true)), + ::testing::make_tuple(LayerType("Unique"), + InOutShapes({{{5}}, + {{5}, {5}}}), + NewInOutShapes({{{25}}, + {{25}, {25}}}), + MapParams(MapStrStr(std::map{{"sorted", "false"}, + {"return_inverse", "true"}, + {"return_counts", "false"}})), + LayerDataName("data"), + CanInfer(true)), + ::testing::make_tuple(LayerType("Unique"), + InOutShapes({{{5}}, + {{5}, {5}, {5}}}), + NewInOutShapes({{{25}}, + {{25}, {25}, {25}}}), + MapParams(MapStrStr(std::map{{"sorted", "false"}, + {"return_inverse", "true"}, + {"return_counts", "true"}})), + LayerDataName("data"), + CanInfer(true)), + ::testing::make_tuple(LayerType("Scatter"), + InOutShapes({{{3, 3}, {2, 3}}, + {{3,3}}}), + NewInOutShapes({{{4, 4}, {3, 4}}, + {{4,4}}}), + MapParams(MapStrStr(std::map{{"axis", "0"}})), + LayerDataName("data"), + CanInfer(true)), + ::testing::make_tuple(LayerType("NonMaxSuppression"), + InOutShapes({{{1, 2, 4}, {1, 3, 2}}, + {{6, 3}}}), + NewInOutShapes({{{2, 5, 4}, {2, 3, 5}}, + {{30, 3}}}), + MapParams(MapStrStr(std::map{{"center_point_box", "0"}})), + LayerDataName("data"), CanInfer(true)) ) ); diff --git a/inference-engine/tests/unit/transformations/eltwise_broadcast_test.cpp b/inference-engine/tests/unit/transformations/eltwise_broadcast_test.cpp deleted file mode 100644 index 23f27c0a396b40..00000000000000 --- a/inference-engine/tests/unit/transformations/eltwise_broadcast_test.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include -#include -#include -#include -#include - -#include "tranformations_test.hpp" - -using namespace testing; -using namespace InferenceEngine; - -class TransformNetworkTest: public TransformationTestCommon {}; - -TEST_F(TransformationTestCommon, EltwiseBroadcastOneDimension) { - Builder::Network builder("eltwiseBroadcast"); - - idx_t firstInputId = builder.addLayer(Builder::InputLayer("FirstInput").setPort(Port({1, 3, 227, 1}))); - idx_t secondInputId = builder.addLayer(Builder::InputLayer("SecondInput").setPort(Port({1, 3, 227, 227}))); - idx_t eltwiseSumId = builder.addLayer({firstInputId, secondInputId}, Builder::EltwiseLayer("Sum"). - setEltwiseType(Builder::EltwiseLayer::EltwiseType::SUM). - setOutputPort(Port({1, 3, 227, 227}))); - auto network = Transform::Network(builder); - - Transform::TransformationEltwiseBroadcast transformationEltwiseBroadcast; - transformationEltwiseBroadcast.execute(network); - auto firstInputLayer = network.getLayer(firstInputId); - auto tileLayer = network.getLayer(firstInputId).getOutPort().getConnection().getDestination().getLayer(); - ASSERT_EQ(tileLayer.getType(), "Tile"); - ASSERT_EQ(tileLayer.getParameter("axis").as(), 3); - ASSERT_EQ(tileLayer.getParameter("tiles").as(), 227); - ASSERT_EQ(firstInputLayer.getOutPort().getConnection().getDestination().getLayer().getId(), tileLayer.getId()); - ASSERT_EQ(tileLayer.getOutPort().getConnection().getDestination().getLayer().getId(), eltwiseSumId); -} - -TEST_F(TransformationTestCommon, EltwiseBroadcastTwoDimensions) { - Builder::Network builder("eltwiseBroadcast"); - - idx_t firstInputId = builder.addLayer(Builder::InputLayer("FirstInput").setPort(Port({1, 1, 227, 1}))); - idx_t secondInputId = builder.addLayer(Builder::InputLayer("SecondInput").setPort(Port({1, 3, 227, 227}))); - idx_t eltwiseSumId = builder.addLayer({firstInputId, secondInputId}, Builder::EltwiseLayer("Sum"). - setEltwiseType(Builder::EltwiseLayer::EltwiseType::SUM). - setOutputPort(Port({1, 3, 227, 227}))); - auto network = Transform::Network(builder); - - Transform::TransformationEltwiseBroadcast transformationEltwiseBroadcast; - transformationEltwiseBroadcast.execute(network); - auto firstInputLayer = network.getLayer(firstInputId); - auto tile1Layer = network.getLayer(firstInputId).getOutPort().getConnection().getDestination().getLayer(); - auto tile2Layer = tile1Layer.getOutPort().getConnection().getDestination().getLayer(); - ASSERT_EQ(tile1Layer.getType(), "Tile"); - ASSERT_EQ(tile1Layer.getParameter("axis").as(), 1); - ASSERT_EQ(tile1Layer.getParameter("tiles").as(), 3); - ASSERT_EQ(tile2Layer.getType(), "Tile"); - ASSERT_EQ(tile2Layer.getParameter("axis").as(), 3); - ASSERT_EQ(tile2Layer.getParameter("tiles").as(), 227); - ASSERT_EQ(firstInputLayer.getOutPort().getConnection().getDestination().getLayer().getId(), tile1Layer.getId()); - ASSERT_EQ(tile1Layer.getOutPort().getConnection().getDestination().getLayer().getId(), tile2Layer.getId()); - ASSERT_EQ(tile2Layer.getOutPort().getConnection().getDestination().getLayer().getId(), eltwiseSumId); -} \ No newline at end of file diff --git a/inference-engine/tests/unit/transformations/sub_test.cpp b/inference-engine/tests/unit/transformations/sub_test.cpp deleted file mode 100644 index 85b9b4aa2b59f5..00000000000000 --- a/inference-engine/tests/unit/transformations/sub_test.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include -#include -#include -#include -#include - -#include "tranformations_test.hpp" - -using namespace testing; -using namespace InferenceEngine; - -class TransformNetworkTest: public TransformationTestCommon {}; - -TEST_F(TransformationTestCommon, Sub) { - Builder::Network builder("sub"); - - idx_t firstInputId = builder.addLayer(Builder::InputLayer("FirstInput").setPort(Port({1,3, 227, 227}))); - idx_t secondInputId = builder.addLayer(Builder::InputLayer("SecondInput").setPort(Port({1,3, 227, 227}))); - idx_t eltwiseSubId = builder.addLayer({firstInputId, secondInputId}, Builder::EltwiseLayer("Sub").setEltwiseType(Builder::EltwiseLayer::EltwiseType::SUB)); - idx_t clampId = builder.addLayer({eltwiseSubId}, Builder::ClampLayer("clamp")); - auto network = Transform::Network(builder); - - Transform::TransformationSub transformationSub; - transformationSub.execute(network); - ASSERT_THROW(network.getLayer("Sub"), InferenceEngine::details::InferenceEngineException); - auto sumLayer = network.getLayer(firstInputId).getOutPort().getConnection().getDestination().getLayer(); - auto powerLayer = network.getLayer(secondInputId).getOutPort().getConnection().getDestination().getLayer(); - ASSERT_EQ(sumLayer.getType(), "Eltwise"); - ASSERT_EQ(sumLayer.getParameter("operation").as(), "sum"); - ASSERT_EQ(powerLayer.getType(), "Power"); - ASSERT_EQ(powerLayer.getParameter("power").as(), 1.0f); - ASSERT_EQ(powerLayer.getParameter("scale").as(), -1.0f); - ASSERT_EQ(powerLayer.getParameter("shift").as(), 0.0f); - ASSERT_EQ(sumLayer.getOutPort().getConnection().getDestination().getLayer().getId(), clampId); -} \ No newline at end of file diff --git a/inference-engine/tests/unit/transformations/tranformations_test.hpp b/inference-engine/tests/unit/transformations/tranformations_test.hpp deleted file mode 100644 index 797c2980d15b73..00000000000000 --- a/inference-engine/tests/unit/transformations/tranformations_test.hpp +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include -#include -#include - -#include "../builders/builder_test.hpp" - -class TransformationTestCommon : public BuilderTestCommon { -public: -}; \ No newline at end of file diff --git a/inference-engine/thirdparty/CMakeLists.txt b/inference-engine/thirdparty/CMakeLists.txt index 5a4b2595cb855c..54de20fdc78049 100644 --- a/inference-engine/thirdparty/CMakeLists.txt +++ b/inference-engine/thirdparty/CMakeLists.txt @@ -7,13 +7,6 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(CMAKE_CC_FLAGS "${CMAKE_CC_FLAGS} -Wno-unknown-warning-option -Wno-inconsistent-missing-override -Wno-pass-failed") endif() -add_subdirectory(pugixml) -export(TARGETS pugixml NAMESPACE IE:: APPEND FILE "${CMAKE_BINARY_DIR}/targets.cmake") -export(TARGETS pugixml NAMESPACE IE:: APPEND FILE "${CMAKE_BINARY_DIR}/targets_developer.cmake") - -add_subdirectory(stb_lib) -add_subdirectory(ade) - if (ENABLE_CLDNN) set(CLDNN__OUTPUT_BIN_DIR ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) set(CLDNN__OUTPUT_LIB_DIR ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) @@ -35,20 +28,35 @@ if (ENABLE_CLDNN) add_subdirectory(clDNN) endif() -if (UNIX OR APPLE AND ${CMAKE_BUILD_TYPE} STREQUAL "Release") - remove_definitions(-fvisibility=hidden) - add_definitions(-fvisibility=default) -endif() +function(build_with_lto) + if(ENABLE_LTO) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto") + set(CMAKE_AR "gcc-ar") + set(CMAKE_RANLIB "gcc-ranlib") + endif() -include(ngraph.cmake) + add_subdirectory(pugixml) + export(TARGETS pugixml NAMESPACE IE:: APPEND FILE "${CMAKE_BINARY_DIR}/targets.cmake") + export(TARGETS pugixml NAMESPACE IE:: APPEND FILE "${CMAKE_BINARY_DIR}/targets_developer.cmake") -if (UNIX OR APPLE AND ${CMAKE_BUILD_TYPE} STREQUAL "Release") - remove_definitions(-fvisibility=default) - add_definitions(-fvisibility=hidden) -endif() + if (TARGET pugixml_mt) + export(TARGETS pugixml_mt NAMESPACE IE:: APPEND FILE "${CMAKE_BINARY_DIR}/targets.cmake") + export(TARGETS pugixml_mt NAMESPACE IE:: APPEND FILE "${CMAKE_BINARY_DIR}/targets_developer.cmake") + endif() + + add_subdirectory(stb_lib) + + add_subdirectory(ade) + export(TARGETS ade NAMESPACE IE:: APPEND FILE "${CMAKE_BINARY_DIR}/targets_developer.cmake") + + include(ngraph.cmake) + + add_subdirectory(fluid/modules/gapi) + export(TARGETS fluid NAMESPACE IE:: APPEND FILE "${CMAKE_BINARY_DIR}/targets_developer.cmake") +endfunction() + +build_with_lto() if(ENABLE_MKL_DNN) include(mkldnn.cmake) endif() - -add_subdirectory("${IE_MAIN_SOURCE_DIR}/thirdparty/fluid/modules/gapi") diff --git a/inference-engine/thirdparty/ade b/inference-engine/thirdparty/ade index 562e301ccc8327..cbe2db61a659c2 160000 --- a/inference-engine/thirdparty/ade +++ b/inference-engine/thirdparty/ade @@ -1 +1 @@ -Subproject commit 562e301ccc8327e4016ccc3f1bc3a8592f50ea21 +Subproject commit cbe2db61a659c2cc304c3837406f95c39dfa938e diff --git a/inference-engine/thirdparty/clDNN/CMakeLists.txt b/inference-engine/thirdparty/clDNN/CMakeLists.txt index 624d95c85a7e24..c39fe5ca71575f 100644 --- a/inference-engine/thirdparty/clDNN/CMakeLists.txt +++ b/inference-engine/thirdparty/clDNN/CMakeLists.txt @@ -548,8 +548,10 @@ endif() # - on others: shared libraries directory. if(__CLDNN_TargetOs MATCHES "^windows$") set(CLDNN__IOCL_ICD_LIBDIRS ${CLDNN__IOCL_ICD_STLDIRS} CACHE INTERNAL "Paths to libraries to link for Intel OpenCL SDK ICD.") + set(CLDNN__IOCL_ICD_LIBPATH ${CLDNN__IOCL_ICD_LIBDIRS}/${CMAKE_STATIC_LIBRARY_PREFIX}OpenCL${CMAKE_STATIC_LIBRARY_SUFFIX} CACHE INTERNAL "") else() set(CLDNN__IOCL_ICD_LIBDIRS ${CLDNN__IOCL_ICD_SHLDIRS} CACHE INTERNAL "Paths to libraries to link for Intel OpenCL SDK ICD.") + set(CLDNN__IOCL_ICD_LIBPATH ${CLDNN__IOCL_ICD_LIBDIRS}/${CMAKE_SHARED_LIBRARY_PREFIX}OpenCL${CMAKE_SHARED_LIBRARY_SUFFIX} CACHE INTERNAL "") endif() unset(__CLDNN_IOclIcdVersions) @@ -669,7 +671,7 @@ message(STATUS "[clDNN] - Root: ${CLDNN__IOCL_ICD_ROOT}") message(STATUS "[clDNN] + Headers: ${CLDNN__IOCL_ICD_INCDIRS}") message(STATUS "[clDNN] + Static libs: ${CLDNN__IOCL_ICD_STLDIRS}") message(STATUS "[clDNN] + Shared libs: ${CLDNN__IOCL_ICD_SHLDIRS}") -message(STATUS "[clDNN] + Libs to link: ${CLDNN__IOCL_ICD_LIBDIRS}") +message(STATUS "[clDNN] + Libs to link: ${CLDNN__IOCL_ICD_LIBPATH}") message(STATUS "[clDNN] =============================================================================") unset(__CLDNN_DetectedArch_Target) @@ -680,7 +682,7 @@ unset(__CLDNN_DetectedArch_Target) # =================================== Main targets names and labels ==================================== -set(CLDNN_BUILD__PROJ__clDNN "${CLDNN_BUILD__PROJ_NAME_PREFIX}clDNN_shlib") +set(CLDNN_BUILD__PROJ__clDNN "${CLDNN_BUILD__PROJ_NAME_PREFIX}clDNN_lib") set(CLDNN_BUILD__PROJ_LABEL__clDNN "clDNN") # ================================================ Outputs ============================================= @@ -817,7 +819,9 @@ foreach(__CLDNN_CompilerFlagName IN ITEMS "CMAKE_CXX_FLAGS" "CMAKE_C_FLAGS") endif() endif() elseif(CMAKE_COMPILER_IS_INTEL) - message(FATAL_ERROR "TODO Support native ICC") + if(UNIX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -diag-warning=68,654,1125") + endif() # Adding needed settings specific to GCC. # NOTE: Following options can be needed in the future (although some not recommended: NR): # [NR] -fno-short-enums @@ -1009,17 +1013,6 @@ set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS CLDNN_CMAKE ) -if (MSVC) -# set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS -# _SCL_SECURE_NO_WARNINGS -# ) -elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS - _GLIBCXX_USE_CXX11_ABI=0 - $<$:_FORTIFY_SOURCE=2> - ) -endif() - # ===================================== Include/Link directories ======================================= include_directories( @@ -1028,10 +1021,14 @@ include_directories( "${CLDNN__KHR_CLHPP_DIR}" "${CLDNN__CODEGEN_INCDIR}" ) -link_directories( - ${CLDNN__IOCL_ICD_LIBDIRS} + +add_library(clDNN_OpenCL UNKNOWN IMPORTED) +set_target_properties(clDNN_OpenCL + PROPERTIES + IMPORTED_LOCATION ${CLDNN__IOCL_ICD_LIBPATH} ) + # =================================== Link targets and dependencies ==================================== if(CLDNN__INCLUDE_CORE) add_subdirectory(src) diff --git a/inference-engine/thirdparty/clDNN/api/C/activation.h b/inference-engine/thirdparty/clDNN/api/C/activation.h deleted file mode 100644 index 0f35d4d6b6bdc3..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/activation.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Activation using rectified linear unit or parameterized rectified linear unit. -/// @details Can get one negative slope or negative slope per channel. -/// @par Algorithm: -/// out(i,x,y) = max(0, in(i,x,y)) + slope(i) * min(0, in(i,x,y)) -/// @par Where: -/// @li out(i,x,y) : value at x, y from i-th feature map after activation. -/// @li in(i,x,y) : value at x, y from i-th feature map before activation. -/// @li slope(i) : the slope value of the i-th feature map (can be shared across channels or one slope per channel). -CLDNN_BEGIN_PRIMITIVE_DESC(activation) -/// @brief activation function. -cldnn_activation_func activation_func; -/// @brief Activation additional params. -/// activation_relu_negative_slope - additional_params.a is a negative slope -/// activation_brelu - additional_params.a is a upper bound -/// activation_linear - additional_params.a/b uses as a*val + b -cldnn_activation_additional_params additional_params; -/// @brief Activation additional params stored on a memory object -/// activation_relu_negative_slope - negative slope per feature map -/// activation_brelu - upper bound per feature map -/// activation_linear - a,b per feature map -cldnn_primitive_id additional_params_input; -CLDNN_END_PRIMITIVE_DESC(activation) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(activation); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/activation_grad.h b/inference-engine/thirdparty/clDNN/api/C/activation_grad.h deleted file mode 100644 index 7a1e53240e3281..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/activation_grad.h +++ /dev/null @@ -1,58 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Activation gradient for rectified linear unit or parameterized rectified linear unit. -/// @par Algorithm: -/// out(i,x,y) = input_gradient(i,x,y) * ((input(i,x,y) > 0) + slope(i) * (input(i,x,y) <= 0) -/// @par Where: -/// @li out(i,x,y) : value at x, y from i-th feature map after activation. -/// @li in(i,x,y) : value at x, y from i-th feature map before activation. -/// @li slope(i) : the slope value of the i-th feature map (can be shared across channels or one slope per channel). -CLDNN_BEGIN_PRIMITIVE_DESC(activation_grad) -/// @brief activation gradient function. -cldnn_activation_grad_func activation_grad_func; -/// @brief Activation additional params. -/// activation_relu_negative_slope_grad - additional_params.a is a negative slope -cldnn_activation_additional_params additional_params; -/// @brief Activation additional params stored on a memory object -/// activation_relu_negative_slope_grad - negative slope per feature map -cldnn_primitive_id additional_params_input; -CLDNN_END_PRIMITIVE_DESC(activation_grad) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(activation_grad); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/apply_adam.h b/inference-engine/thirdparty/clDNN/api/C/apply_adam.h deleted file mode 100644 index a775ef94808c24..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/apply_adam.h +++ /dev/null @@ -1,73 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Apply Adam primitive. -/// @details Updates output using Adam algorithm. The output of this primitive should be mutable_data type in case user wants to update -/// variable accross network. If output is not mutable_data then it will be initialized with 0. -/// "Adam: A Method for Stochastic Optimization" by Diederik P. Kingma, Jimmy Ba -/// @n See: https://arxiv.org/abs/1412.6980 -/// -/// Algorithm: -/// @n float lr[t] = lr * sqrt(1 - beta2^t) / (1 - beta1^t); -/// @n float m[t] = beta1 * m[t-1] + (1 - beta1) * grad[t]; -/// @n float v[t] = beta2 * v[t-1] + (1 - beta2) * grad[t] * grad[t]; -/// @n float result = result - lr[t] * m[t] / (sqrt(v[t]) + epsilon); - -CLDNN_BEGIN_PRIMITIVE_DESC(apply_adam) -/// @brief Primitive id containing m data. -cldnn_primitive_id m; -/// @brief Primitive id containing v data. -cldnn_primitive_id v; -/// @brief Primitive id containing beta1^t. -cldnn_primitive_id beta1_power; -/// @brief Primitive id containing beta2^t. -cldnn_primitive_id beta2_power; -/// @brief Learning rate parameter. -float lr; -/// @brief Beta1 parameter. -float beta1; -/// @brief Beta2 parameter. -float beta2; -/// @brief Epsilon. -float epsilon; -/// @brief Optional primitive id that need to complete before execution of this primitive. Used only for synchronization. -cldnn_primitive_id dependency_id; -CLDNN_END_PRIMITIVE_DESC(apply_adam) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(apply_adam); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/arg_max_min.h b/inference-engine/thirdparty/clDNN/api/C/arg_max_min.h deleted file mode 100644 index 65359091a8b81c..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/arg_max_min.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Enum type to specify output type - index of max or min values -typedef enum { - cldnn_arg_max, - cldnn_arg_min, -} cldnn_arg_max_min_out; - -/// @brief Enum type to specify axis to maximize/minimize along. -typedef enum { - cldnn_arg_max_min_batch, - cldnn_arg_max_min_feature, - cldnn_arg_max_min_x, - cldnn_arg_max_min_y, - cldnn_arg_max_min_xyf -} cldnn_arg_max_min_axis; - -/// @brief Finds the index of the k max/min values of input. -CLDNN_BEGIN_PRIMITIVE_DESC(arg_max_min) -/// @brief Number of indices to output. -uint32_t top_k; -/// @brief Type of output - max or mix. -cldnn_arg_max_min_out output_type; -/// @brief Axis to maximize/minimize along. If not set, maximize the flattened x, y ,f dimensions for each index of the first dimension. -cldnn_arg_max_min_axis axis; -/// @brief Indicates that the primitive has user defined axis to maximize/minimize along. -uint32_t with_axis; -/// @brief Sets output order: if True than first output contains values and second (optional) - indices. -uint32_t values_first; -/// @brief Type of sorting - by values or indices. -uint32_t sort; -CLDNN_END_PRIMITIVE_DESC(arg_max_min) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(arg_max_min); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/average_unpooling.h b/inference-engine/thirdparty/clDNN/api/C/average_unpooling.h deleted file mode 100644 index ea45ff24a85ae0..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/average_unpooling.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs "average_unpooling" operation. -/// @details Reverse operation of average pooling. -/// Each element in every pooling window is filled with output / window size value. In case of window overlap the elements are added. -CLDNN_BEGIN_PRIMITIVE_DESC(average_unpooling) -/// @brief Defines shift in output buffer. -cldnn_tensor stride; -/// @brief Pooling kernel size. -cldnn_tensor size; -/// @brief Output size of this primitive. -cldnn_tensor output_size; -CLDNN_END_PRIMITIVE_DESC(average_unpooling) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(average_unpooling); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/batch_norm.h b/inference-engine/thirdparty/clDNN/api/C/batch_norm.h deleted file mode 100644 index 58e0f0b54ae319..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/batch_norm.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Batch normalization primitive. -/// @details Performs batch normalization as described in -/// "Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift" by Ioffe, Szegedy -/// @n See: http://arxiv.org/abs/1502.03167 -/// -/// Algorithm: -/// @n global stats can be computed as: -/// @n out[i] = ( (in[i] - mean[b]) / sqrt(variance[b] + epsilon) ) * scale[b] + shift[b] - -CLDNN_BEGIN_PRIMITIVE_DESC(batch_norm) -/// @brief Primitive id containing mean data. -cldnn_primitive_id mean; -/// @brief Primitive id containing variance. -cldnn_primitive_id variance; -/// @brief Primitive id containing scale. -cldnn_primitive_id scale; -/// @brief Primitive id containing shift. -cldnn_primitive_id shift; -/// @brief Primitive id containing inverted variance used in future gradient computing. -cldnn_primitive_id inv_variance; -/// @brief Epsilon. -float epsilon; -CLDNN_END_PRIMITIVE_DESC(batch_norm) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(batch_norm); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/batch_norm_grad.h b/inference-engine/thirdparty/clDNN/api/C/batch_norm_grad.h deleted file mode 100644 index 81a69575336467..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/batch_norm_grad.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs backward batch normalization layer. -/// @details Calculates mean gradient and gradient * input for every feature in data, -/// then output is calculated as inv_variance * (input_grad - mean_grad_input * input - mean_grad) -CLDNN_BEGIN_PRIMITIVE_DESC(batch_norm_grad) -/// @brief Primitive id containing inverted variance from forward pass. -cldnn_primitive_id inv_variance; -CLDNN_END_PRIMITIVE_DESC(batch_norm_grad) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(batch_norm_grad); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/binary_convolution.h b/inference-engine/thirdparty/clDNN/api/C/binary_convolution.h deleted file mode 100644 index a819fc5f41f819..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/binary_convolution.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -// Copyright (c) 2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs forward spatial binary_convolution with weight sharing. -/// @details Parameters are defined in context of "direct" binary_convolution, but actual algorithm is not implied. -CLDNN_BEGIN_PRIMITIVE_DESC(binary_convolution) -/// @brief Defines a shift, relative to (0,0) position of the input buffer, where (0,0) point of the binary_convolution window should start calculations. -cldnn_tensor input_offset; -/// @brief Defines shift in input buffer between adjacent calculations of output values. -cldnn_tensor stride; -/// @brief Defines gaps in the input - dilation rate k=1 is normal binary_convolution, k=2 means skipping one pixel per input, k=4 means skipping 3 pixels. -/// As an example in one dimension, a filter w of size 3 would compute over input x the following: w[0]*x[0] + w[1]*x[1] + w[2]*x[2] for dilation of 1. -/// For dilation 2 the filter would instead compute w[0]*x[0] + w[1]*x[2] + w[2]*x[4]. -cldnn_tensor dilation; -/// @brief Weights groups count -uint32_t split; -/// @brief User-defined output data size of the primitive (w/o padding). -cldnn_tensor output_size; -/// @brief Number of feature groups (grouped convolution). If more than 1 then weights/bias count needs to be 1. -int groups; -/// @brief Logical value of padding. Can be one of 3 values: 1 - pad bits equal to 1; -1 -> pad bits equal to 0; 0 -> pad is not counted -float pad_value; -/// @brief Array of primitive ids containing weights data. Size of array should be equivalent to @p split. -cldnn_primitive_id_arr weights; - -CLDNN_END_PRIMITIVE_DESC(binary_convolution) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(binary_convolution); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/border.h b/inference-engine/thirdparty/clDNN/api/C/border.h deleted file mode 100644 index 29ddc2bcd0acc3..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/border.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" - -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Type of border that will be added to the input by current layer / primitive -/// ( @CLDNN_PRIMITIVE_DESC{border} ​). -typedef enum /*:int32_t*/ { - /// @brief All points in the border are set to constant value. - cldnn_border_constant, - cldnn_border_zero = cldnn_border_constant, /// keep bwd compatibilty - /// @brief Border is constructed as an mirror of image (edge is also mirrored). - /// @details Size of border in any dimension cannot be larger than size of - /// input in the same dimension. - cldnn_border_mirror, - /// @brief Border is constructed as an mirror of image (edge is NOT mirrored). - /// @details Size of border in any dimension cannot be larger than size of - /// input in the same dimension decreased by @c 1. - cldnn_border_mirror_101, - /// @brief Border is constructed as an replication of edge. - /// @details Size of border in any dimension cannot be larger than size of - /// input in the same dimension. - cldnn_border_edge -} cldnn_border_type; - -/// @brief Adds border around input. -/// -/// @details Applies border of specified type around input data. The size of output data is increased -/// by @c left_top_sizes and by @right_bottom_sizes. -/// @n -/// @n@b Requirements: -/// @n - @c left_top_sizes and @c right_bottom_sizes must be non-negative on all dimensions and compatible -/// with size of input (describe the same dimensions). -/// @n - For @c border_type equal to @c cldnn_border_mirror, @c left_top_sizes and @c right_bottom_sizes -/// must be lower than or equal to size of input on corresponding dimension (for all dimensions) -/// @n - For @c border_type equal to @c cldnn_border_mirror_101, @c left_top_sizes and @c right_bottom_sizes -/// must be lower than size of input on corresponding dimension (for all dimensions) -CLDNN_BEGIN_PRIMITIVE_DESC(border) -/// @brief Size of border that needs to be added from left (in X dimension) and from top (in Y dimension). -cldnn_tensor left_top_sizes; -/// @brief Size of border that needs to be added from right (in X dimension) and from bottom (in Y dimension). -cldnn_tensor right_bottom_sizes; -/// @brief Type of border that needs to be added to the input. -cldnn_border_type border_type; -/// @brief Border value that is used in constant mode. -float border_value; -CLDNN_END_PRIMITIVE_DESC(border) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(border); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/broadcast.h b/inference-engine/thirdparty/clDNN/api/C/broadcast.h deleted file mode 100644 index 519210fec9f1f6..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/broadcast.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" - -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Broadcasts input to defined by @p broadcast_sizes output. @p broadcast_axes are used to -/// reinterpret input (reshape) inside algorithm. -/// -/// @details Takes input, reinterpret it according to @p broadcast_axes -/// and copies it to output once or multiple times. -/// @n -/// @n Simple example with empty @p broadcast_axes. Lets assume that: -/// @n input_sizes = (in_b, in_f, in_y, in_x) -/// @n broadcast_sizes = (bs_b, bs_f, bs_y, bs_x) -/// @n broadcast_axes = () - empty -/// @n The input is broadcasted on each dimension where bs_{dim} > in_{dim} and bs_{dim} -/// is dividable by in_{dim} (input is copied bs_{dim} / in_{dim} times). -/// The dimensions where bs_{dim} is equal to in_{dim} remain unchanged. -/// @n The resulting output will have sizes equal to @p broadcast_sizes and contains values from -/// input that meet following criteria: -/// @n output[(b, f, y, x)] = input[(b % in_b, f % in_f, y % in_y, x % in_x)] -/// @n where (b, f, y, x) is a position of value in a primitive output. -/// @n -/// @n More complicated example with non empty @p broadcast_axes. Lets assume that: -/// @n broadcast_sizes = (bs_b, bs_f, bs_y, bs_x) -/// @n broadcast_axes = (2) -/// @n Taking into account broadcast_axes size (=1) primitive's input must be (4 - 1 = 3): -/// @n primitive input = (1, in_b, in_f, in_x) -/// @n Due to broadcast_axes = (2) primitive will interpret input as: -/// @n primitive input(internal representation) = (in_b, in_f, 1, in_x) -/// @n Now, you can apply broadcast rules from previous example to modified (reinterpreted) -/// input and output: -/// @n input_sizes = (in_b, in_f, 1, in_x) -/// @n output_shape = (bs_b, bs_f, bs_y, bs_x) -/// @n broadcast_axes = () - empty -/// @n -/// @n@b Requirements: -/// @n - @p broadcast_sizes must be positive on all dimensions. -/// @n - @p broadcast_axes size (dimensions count) must be within (inclusive) range -/// 0 - 4. -/// @n - @p broadcast_axes mustn't have duplicate values. -/// @n - Values of @p broadcast_axes must be within (inclusive) range 0 - 3 -/// @n - @p output_shape must be greater (dividable) than or equal to reinterpreted -/// input on all dimensions. -/// @n Breaking any of these conditions will raise an exception. -CLDNN_BEGIN_PRIMITIVE_DESC(broadcast) -/// @brief Sizes of broadcast. Output size of current primitive will match broadcast sizes (layout type -/// will not change). -cldnn_tensor broadcast_sizes; -/// @brief Array of axes positions from output shape (0-based, from left to right) -/// along which broadcast should happen. -cldnn_uint16_t_arr broadcast_axes; - -CLDNN_END_PRIMITIVE_DESC(broadcast) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(broadcast); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/cldnn.h b/inference-engine/thirdparty/clDNN/api/C/cldnn.h deleted file mode 100644 index cbee0ec18b4082..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/cldnn.h +++ /dev/null @@ -1,891 +0,0 @@ -/* -// Copyright (c) 2016-2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -// exporting symbols form dynamic library -#ifdef EXPORT_NEURAL_SYMBOLS -#if defined(_MSC_VER) -// Microsoft -#define CLDNN_API __declspec(dllexport) -#elif defined(__GNUC__) -// GCC -#define CLDNN_API __attribute__((visibility("default"))) -#else -#define CLDNN_API -#pragma warning Unknown dynamic link import / export semantics. -#endif -#else // import dll -#if defined(_MSC_VER) -// Microsoft -#define CLDNN_API __declspec(dllimport) -#elif defined(__GNUC__) -// GCC -#define CLDNN_API -#else -#define CLDNN_API -#pragma warning Unknown dynamic link import / export semantics. -#endif -#endif - -#include -#include - -/// @addtogroup c_api C API -/// @{ - -/// @defgroup c_memory Memory Management - -/// @defgroup c_topology Network Topology - -/// @defgroup c_engine Execution Engine - -/// @defgroup c_network Network Execution - -/// @defgroup c_error Error Handling - -/// @defgroup c_version Version Information - -#ifdef __cplusplus -extern "C" { -#endif - -/// @addtogroup c_error -/// @{ -#define CLDNN_SUCCESS 0 -#define CLDNN_ERROR -1 -#define CLDNN_INVALID_ARG -2 -#define CLDNN_OUT_OF_RESOURCES -3 -#define CLDNN_DEVICE_ERROR -4 -#define CLDNN_UNSUPPORTED_SIZE -5 -#define CLDNN_UNSUPPORTED_FORMAT -6 -#define CLDNN_DIMENSION_MISMATCH -7 -#define CLDNN_ALLOC_SIZE_EXCEEDED -8 -#define CLDNN_GLOBAL_SIZE_EXCEEDED -9 - -#define CLDNN_API_STRING_SIZE_MAX 256 -/// @brief Represents errors status for all API calls -typedef int32_t cldnn_status; -/// @} - -/// @addtogroup c_version -/// @{ -/// @brief Represents version information of API. -typedef struct { - int32_t major; ///< Major version component (major version of clDNN API interface). - int32_t minor; ///< Minor version component (minor version of API interface - correlated with IE API version). - int32_t build; ///< Build version component (version/revision of official Open Source drop of clDNN library). - int32_t revision; ///< Revision version component (incremental identifier of current build/compilation). -} cldnn_version; -/// @} - -/// @ingroup c_engine -/// @brief Engine object -typedef struct cldnn_engine_impl* cldnn_engine; - -/// @ingroup c_network -/// @brief Event object -typedef struct cldnn_event_impl* cldnn_event; - -/// @ingroup c_topology -/// @brief Network topology to be defined by user -typedef struct cldnn_topology_impl* cldnn_topology; - -/// @ingroup c_program -/// @brief Compiled program build from @ref cldnn_topology by @ref cldnn_engine -typedef struct cldnn_program_impl* cldnn_program; - -/// @ingroup c_network -/// @brief Executable network allocated from @ref cldnn_program -typedef struct cldnn_network_impl* cldnn_network; - -/// @ingroup c_memory -/// @brief Memory object -typedef struct cldnn_memory_impl* cldnn_memory; - -/// @addtogroup c_engine -/// @{ - -/// @brief Defines available engine types -typedef enum /*:int32_t*/ { - cldnn_engine_ocl ///< OpenCL engine -} cldnn_engine_type; - -/// @brief Priority modes. -typedef enum /*:int16_t*/ { - cldnn_priority_disabled, - cldnn_priority_low, - cldnn_priority_med, - cldnn_priority_high -} cldnn_priority_mode_type; - -/// @brief Throttle modes. -typedef enum /*:int16_t*/ { - cldnn_throttle_disabled, - cldnn_throttle_low, - cldnn_throttle_med, - cldnn_throttle_high -} cldnn_throttle_mode_type; - -/// @brief Configuration parameters for created engine. -typedef struct { - uint32_t enable_profiling; ///< Enable per-primitive profiling. - uint32_t meaningful_kernels_names; ///< Generate meaniful names fo OpenCL kernels. - uint32_t dump_custom_program; ///< dump the custom generated program to files - const char* compiler_options; ///< OpenCL compiler options string. - const char* single_kernel_name; ///< If provided, runs specific layer. - uint32_t enable_parallelisation; ///< Enables parallel execution of primitives which don't depend on each other. Disabled by default. - const char* engine_log; ///< Specifies a file to which engine log should be dumped. Null/empty values means no logging. - const char* sources_dumps_dir; ///< Specifies a directory where sources of cldnn::program objects should be dumped. - ///< Null/empty values means no loggins. - /*cldnn_priority_mode_type*/ int16_t priority_mode; ///< Priority mode (support of OpenCL priority hints in command queue). - /*cldnn_throttle_mode_type*/ int16_t throttle_mode; ///< Throttle mode (support of throttle hints in command queue). - uint32_t enable_memory_pool; ///< Enables memory usage optimization. memory objects will be reused when possible. - uint16_t n_streams; ///< Number of queues executed in parallel - void* context; - const char* tuning_cache_path; ///< Enables defining other than default path to tuning cache json -} cldnn_engine_configuration; - -/// @brief Information about the engine returned by cldnn_get_engine_info(). -typedef struct { - uint32_t cores_count; ///< Number of available HW cores. - uint32_t core_frequency; ///< Clock frequency in MHz. - - uint64_t max_work_group_size; ///< Maximum number of work-items in a work-group executing a kernel using the data parallel execution model. - uint64_t max_local_mem_size; ///< Maximum size of local memory arena in bytes. - uint64_t max_global_mem_size; ///< Maximum size of global device memory in bytes. - uint64_t max_alloc_mem_size; ///< Maximum size of memory object allocation in bytes. - - uint64_t max_image2d_width; ///< Maximum image 2d width supported by the device. - uint64_t max_image2d_height; ///< Maximum image 2d height supported by the device. - - // Flags (for layout compatibility fixed size types are used). - uint8_t supports_fp16; ///< Does engine support FP16. - uint8_t supports_fp16_denorms; ///< Does engine support denormalized FP16. - uint8_t supports_subgroups_short; ///< Does engine support cl_intel_subgroups_short. - uint8_t supports_image; ///< Does engine support images (CL_DEVICE_IMAGE_SUPPORT cap). - - uint8_t supports_imad; ///< Does engine support int8 mad. - uint8_t supports_immad; ///< Does engine support int8 multi mad. - - char ocl_device_name[CLDNN_API_STRING_SIZE_MAX]; ///< Device ID string - char ocl_driver_version[CLDNN_API_STRING_SIZE_MAX]; ///< Version of OpenCL driver -} cldnn_engine_info; -/// @} - -/// @addtogroup c_network -/// @{ - -/// @brief user-defined event handler callback. -typedef void (*cldnn_event_handler)(void*); - -/// @brief Profiling information for an executed network primitive. -/// @details Every @ref cldnn_event associated with @ref cldnn_network_output. -/// can contain one or more profiling information intervals. -typedef struct { - const char* name; ///< Profiling interval name. - uint64_t nanoseconds; -} cldnn_profiling_interval; - -/// @brief Network build option types. -typedef enum /*:int32_t*/ { - cldnn_build_option_fusing, ///< Allow primitives fusing during network build. - cldnn_build_option_optimize_data, ///< Enable implicit reordering for user input. - cldnn_build_option_debug, ///< Enable debug mode. - cldnn_build_option_outputs, ///< User selected list of network outputs. - cldnn_build_option_tuning_config, ///< Tuning config. - cldnn_build_option_graph_dumps_dir, ///< Specifies a directory to which stages of network compilation should be dumped. - cldnn_build_option_serialization, ///< Specifies a name of files to which serialization should be dumped. - cldnn_build_option_load_program, ///< Specifies a name of load_program process. - cldnn_build_option_learning_config, ///< User defined learning parameters. - cldnn_build_option_detection_output_gpu ///< Run detection output layer always on GPU, regardless performance -} cldnn_build_option_type; - -/// @brief Tuning modes. -typedef enum /*:int32_t*/ { - cldnn_tuning_disabled, ///< Tuning is disabled. - cldnn_tuning_use_cache, ///< Tuning using the cached data (no on-line tuning for non-existing data). - cldnn_tuning_tune_and_cache, ///< Tuning using the cached data if exist, tune and update cache otherwise. -} cldnn_tuning_mode_type; - -/// @brief Tuning config. -struct cldnn_tuning_config { - const int32_t mode; ///< #cldnn_tuning_mode_type. - const char* cache_file_path; ///< A path to the tuning cache file. -}; - -/// @brief Learning params. -struct cldnn_learning_params { - const float momentum; - const float weights_decay; -}; - -/// @brief Represents network build option. -typedef struct { - int32_t type; ///< #cldnn_build_option_type. - const void* data; ///< option parameter - e.g list of outputs. -} cldnn_build_option; - -/// @brief Output information for executed @a cldnn_network. -/// @details User should wait for event before accessing the memory. -typedef struct { - cldnn_event event; ///< Event to be waited. - cldnn_memory memory; ///< Output memory. - ///< User should wait for the event before access this field. -} cldnn_network_output; - -/// @} - -/// @addtogroup c_memory -/// @{ - -/// @brief Represents memory formats (orders). -/// @n In CNN most of data is describe as 4 dimensional blocks. In Intel(R) clDNN library we describe memory with 4 letters -/// - b - number of blocks in batch. For weights formats: output features - conv, neurons - inner product -/// - f - number of feature maps, features or channels. For weights formats: input features - conv, inputs, inner product -/// - x - spatial, width -/// - y - spatial, height -/// /n -/// For explanation how each format type is implemented in memory we will use naming shown bellow (b=2,f=3,y=3,x=3): -/// \image html layout_memory_representation.jpg -typedef enum /*:int32_t*/ { - // Data formats - cldnn_format_yxfb, ///< batch first, feature and than spatials \n \image html yxfb.jpg - cldnn_format_byxf, ///< used in bitmaps, input from user i.e b images of RGB format \n \image html byxf.jpg - cldnn_format_bfyx, ///< the most common format for activations in clDNN. \n \image html bfyx.jpg - cldnn_format_fyxb, ///< format not used inside clDNN, but supported in reorder as extension for user provided formats. - cldnn_format_bfyx_f16, ///< format used for blocked convolution - cldnn_format_bs_xs_xsv8_bsv8, ///< format used only for fully connected weights: bs - batch slice, xs - x slice, - ///< bsv8 - 8 values of single slice. - ///< \n \image html bs_xs_xsv8_bsv8.jpg - cldnn_format_bs_xs_xsv8_bsv16, ///< format used only for fully connected weights: bs - batch slice, xs - x slice, - ///< bsv16 - 16 values of single slice. - ///< \n \image html bs_xs_xsv8_bsv16.jpg - cldnn_format_bs_x_bsv16, ///< format used only for fully connected weights fp16 batch=1 : bs - batch slice (responses slice), - ///< bsv16 - 16 values of single batch slice, x - flattened plane of (fyx). - ///< \n \image html bs_x_bsv16.jpg - cldnn_format_bf8_xy16, ///< format used only for convolution 1x1 input, xy aligned to 16, f aligned to 8 - ///< \n \image html bf8_xy16.jpg - cldnn_format_b_fs_yx_32fp, ///< format for data for binary convolutions - cldnn_format_winograd_2x3_s1_data, ///< format used for input for winograd convolution, F(2,3) -- filter 3x3 with stride 1 - cldnn_format_byxf_af32, ///< format for input for primitives using MMAD - cldnn_format_byx8_f4, ///< format for input for MMAD convolutions - cldnn_format_fs_bs_yx_bs4_fs32, ///< format for batched input for primitives using MMAD - cldnn_format_b_fs_yx_fsv4, ///< format for input for IMAD convolutions - cldnn_format_bfzyx, ///< format for 3D convolutions - cldnn_format_bfwzyx, ///< 6D format - cldnn_format_fs_b_yx_fsv32, ///< format for fp16 convolutions using 32 features - - // Weights formats - cldnn_format_o_i_yx_i16_o16, ///< format used for blocked convolution - cldnn_format_os_iyx_osv16, ///< format used only for convolution weights: os - output feature maps slice, - ///< i - input feature maps, yx - spatials, sv16 - 16 values of single slice. - ///< \n \image html os_iyx_osv16.jpg - cldnn_format_oiyx_o16, ///< format used only for convolution weights: os - output feature maps slice, - ///< i - input feature maps, yx - spatials, sv16 - 16 values of single slice. - cldnn_format_os_iyx_osv32, ///< format used only for convolution weights: os - output feature maps slice, - ///< i - input feature maps, yx - spatials, sv32 - 32 values of single slice. - cldnn_format_os_iyx_osv64, ///< format used only for convolution weights: os - output feature maps slice, - ///< i - input feature maps, yx - spatials, sv64 - 64 values of single slice. - cldnn_format_image_2d_weights_c4_fyx_b, ///< image format for weights, image 2d, 4-channel, - ///< width size is f*y*x/4 (4-channels filled with fyx data), height is b - ///< \n \image html image_2d_weights_c4_fyx_b.jpg - cldnn_format_image_2d_weights_c1_b_fyx, ///< image format for weights, image 2d, single channel, width size is b, height is f*y*x - ///< \n \image html image_2d_weights_c1_b_fyx.jpg - cldnn_format_winograd_2x3_s1_weights, ///< format used for weights for winograd non-fused convolution, F(2,3) -- filter 3x3 with stride 1 - cldnn_format_winograd_2x3_s1_fused_weights, ///< format used for weights for winograd fused convolution, F(2,3) -- filter 3x3 with stride 1 - cldnn_format_winograd_6x3_s1_fused_weights, ///< format used for weights for winograd fused convolution, F(6,3) -- filter 3x3 with stride 1 - cldnn_format_image_2d_weights_winograd_6x3_s1_fbxyb, ///< image format used for weights for winograd fused convolution, F(6,3) -- filter 3x3 with stride 1 - cldnn_format_image_2d_weights_winograd_6x3_s1_xfbyb, ///< image format used for weights for winograd fused convolution, F(6,3) -- filter 3x3 with stride 1 - cldnn_format_os_is_yx_isa8_osv8_isv4, ///< format for weights for MMAD convolutions, - ///< stored as ((aligned_to_8(O)/8) * (aligned_to_32(I)/32) * Y * X * ( 8 ) * ( 8 ) * ( 4 ) - cldnn_format_os_is_yx_isa8_osv8_isv4_swizzled_by_4, ///< format for weights for MMAD convolutions - cldnn_format_is_o_yx_isv32, ///< format for weights for 1x1 MMAD convolutions - cldnn_format_is_o32_yx_isv32_swizzled_by_4, ///< format for weights for 1x1 MMAD convolutions - cldnn_format_os_is_y_x8_osv8_isv4, ///< format for weights for MMAD convolutions - cldnn_format_os_is_y_x8_osv8_isv4_swizzled_by_4, ///< format for weights for MMAD convolutions - cldnn_bf_lyx_yx, ///< format for local convolution weights - cldnn_format_os_is_yx_osv16_isv4, ///< format for weights for IMAD convolutions - cldnn_format_os_is_yx_osv32_isv32p, ///< format for weights for binary convolutions - cldnn_format_format_num, ///< number of format types - cldnn_format_any = -1 -} cldnn_format_type; - -#define CLDNN_FLOAT_TYPE_MASK 0x80 -#define CLDNN_UINT_TYPE_MASK 0x40 -#define CLDNN_BIN_TYPE_MASK 0x20 - -#define CLDNN_TENSOR_BATCH_DIM_MAX 1 -#define CLDNN_TENSOR_FEATURE_DIM_MAX 1 -#define CLDNN_TENSOR_SPATIAL_DIM_MAX 4 -#define CLDNN_TENSOR_LOCAL_DIM_MAX 2 -#define CLDNN_TENSOR_DIM_MAX 8 - -/// @brief N-dimensional vector. Mostly used to represent memory size. -typedef struct { - size_t batch_num; - size_t feature_num; - size_t spatial_num; - size_t local_num; - int32_t sizes[CLDNN_TENSOR_DIM_MAX]; -} cldnn_tensor; - -/// @brief Padding information. -typedef struct { - cldnn_tensor lower_size; ///< Lower padding sizes. For spatials, it means size of left (X) and top (Y) padding. - cldnn_tensor upper_size; ///< Upper padding sizes. For spatials, it means size of right (X) and bottom (Y) padding. - float filling_value; ///< Filling value for an element of padding. If data type of elements is different than float it is converted - ///< to it using round-towards-nearest-even (for floating-point data types) or round-towards-zero (for integral - ///< data types). -} cldnn_padding; - -/// @brief Data type stored in memory. -typedef enum /*:size_t*/ { - cldnn_bin = sizeof(int32_t) | CLDNN_BIN_TYPE_MASK, - cldnn_u8 = sizeof(uint8_t) | CLDNN_UINT_TYPE_MASK, - cldnn_i8 = sizeof(int8_t), - cldnn_f16 = sizeof(int16_t) | CLDNN_FLOAT_TYPE_MASK, - cldnn_f32 = sizeof(float) | CLDNN_FLOAT_TYPE_MASK, - cldnn_i32 = sizeof(int32_t), - cldnn_i64 = sizeof(int64_t) -} cldnn_data_type; - -/// @brief Memory layout description. -typedef struct { - size_t data_type; ///< data type (@ref cldnn_data_type) stored in memory. - int32_t format; ///< Memor format (@ref cldnn_format_type) - cldnn_tensor size; ///< N-dimensional vector describes size (in elements) of memory (excluding padding). - cldnn_padding padding; ///< Explicitly added padding to memory buffer. -} cldnn_layout; -/// @} - -/// @addtogroup c_topology -/// @{ - -/// @brief Represents reference to an array of floats. -typedef struct { - const float* data; ///< Pointer to float array. - size_t size; ///< Size (in floats) of the array. -} cldnn_float_arr; - -/// @brief Represents reference to an array of uint16_t. -typedef struct { - const uint16_t* data; ///< Pointer to uint16_t array. - size_t size; ///< Size (in uint16_t) of the array. -} cldnn_uint16_t_arr; - -/// @brief Represents reference to an array of uint8_t. -typedef struct { - const uint8_t* data; ///< Pointer to uint8_t array. - size_t size; ///< Size (in uint8_t) of the array. -} cldnn_uint8_t_arr; - -/// @brief Represents reference to an array of tensor. -typedef struct { - const cldnn_tensor* data; ///< Pointer to tensor array. - size_t size; ///< Size (in tensor) of the array. -} cldnn_tensor_arr; - -/// @brief Globally unique primitive's type id -typedef const struct cldnn_primitive_type* cldnn_primitive_type_id; - -/// @brief Unique @p id of a primitive within a topology. -typedef const char* cldnn_primitive_id; - -/// @brief Represents reference to an array of primitive ids. -typedef struct { - const cldnn_primitive_id* data; ///< Pointer to ids array. - size_t size; ///< Number of ids in the array. -} cldnn_primitive_id_arr; - -/// @brief Detailed information about program primitives after a graph optimization step. -typedef struct { - cldnn_primitive_id original_id; - const char* type_id; - cldnn_primitive_id_arr dependencies; - cldnn_primitive_id_arr users; - cldnn_primitive_id_arr fused_ids; - cldnn_layout output_layout; - const char* kernel_id; - const char* layout_str; - int is_cpu; - int exec_id; -} cldnn_primitive_info; - -typedef struct { - cldnn_data_type data_type; - // No bool type available... - char enabled; -} cldnn_optional_data_type; - -/// @brief Custom primitive kernel source code -typedef const char* cldnn_kernel_code; -/// @brief Custom primitive kernel source code array -typedef cldnn_kernel_code* cldnn_kernels_code; -/// @brief Custom primitive kernel entry point -typedef const char* cldnn_kernel_entry_point; -/// @brief Custom primitive kernel build options -typedef const char* cldnn_kernel_build_options; -/// @brief Custom primitive kernel workgroup sizes -typedef const size_t* cldnn_work_group_sizes; - -/// @brief Custom primitive kernel argument type -typedef enum cldnn_arg_type_t { - arg_input, - arg_output, -} cldnn_arg_type; - -/// @brief Custom primitive kernel argument index -typedef uint32_t cldnn_arg_index; - -/// @brief Custom primitive kernel argument type -typedef struct cldnn_arg_t { - cldnn_arg_type arg_type; - cldnn_arg_index index; -} cldnn_arg; - -/// @brief Custom primitive kernel argument array -typedef const cldnn_arg* cldnn_kernel_arguments; - -/// @brief activation functions -typedef enum cldnn_activation_func_t { - activation_none, // val - activation_logistic, // 1/(1 + exp(-val)) - activation_hyperbolic_tan, // tanh(val) - activation_relu, // max(0, val) - activation_relu_negative_slope, // max(0, val) + a * min(0, val) (a is additional param) - activation_clamp, // max(a, min(b, val) (a,b are additional param) - activation_softrelu, // log(1 + exp(val)) - activation_abs, // abs(val) - activation_linear, // a*val + b (a,b are additional params) - activation_square, // val*val - activation_sqrt, // sqrt(val) - activation_elu, // max(0, val) + a * (exp(min(0, val) - 1) (a is additional param) - activation_sin, // sin(val) - activation_asin, // asin(val) - activation_sinh, // sinh(val) - activation_asinh, // asinh(val) - activation_cos, // cos(val) - activation_acos, // acos(val) - activation_cosh, // cosh(val) - activation_acosh, // acosh(val) - activation_log, // log(val) - activation_log2, // log2(val) - activation_exp, // exp(val) - activation_tan, // tan(val) - activation_atan, // atan(val) - activation_atanh, // atanh(val) - activation_floor, // floor(val) - activation_ceil, // ceil(val) - activation_negative, // -val - activation_not, // !val - activation_pow, // pow(val, a) - activation_reciprocal, // (1/val) - activation_erf, // Gauss error function - activation_hard_sigmoid, // max(0, min(1, a * val + b)) (a,b are additional params) - activation_selu, // for val <= 0: b * (a * e^val - a); for val > 0: b * val (a,b are additional params) - activation_sign, // val > 0: 1; val < 0: -1; val == 0: 0 - activation_softplus, // ln(exp(val) + 1) - activation_softsign // (val/(1+|val|)) -} cldnn_activation_func; - -/// @brief activation gradient functions -typedef enum cldnn_activation_grad_func_t { - activation_grad_none, // val - activation_grad_relu, // val * (input > 0) - activation_grad_relu_negative_slope, // val * ((input > 0) + a * (input <= 0) (a is additional param) -} cldnn_activation_grad_func; - -/// @brief activation additional params -typedef struct cldnn_activation_additional_params_t { - float a, b; -} cldnn_activation_additional_params; - -/// @brief Axis which index_select primitive will index. -typedef enum index_select_axis_name_t { - along_b, - along_f, - along_y, - along_x -} index_select_axis_name; - -/// @brief Axis which index_select primitive will index array -typedef const index_select_axis_name* index_select_axis_name_arr; - -/// @brief reorder mean operation modes -typedef enum cldnn_reorder_mean_mode_t { - mean_none, // val - mean_subtract, // val - mean - mean_mul, // val * mean - mean_div, // val/mean -} cldnn_reorder_mean_mode; - -/// @brief Begin primitive description definition -/// @details Defines @p 'cldnn_primitive_type_desc' structure with first 5 fields -/// common for all primitive descriptors. Other fields should be added after this macro. -/// primitive descriptor definition should be closed by @ref CLDNN_END_PRIMITIVE_DESC. -#define CLDNN_BEGIN_PRIMITIVE_DESC(PType) \ - struct cldnn_##PType##_desc { \ - cldnn_primitive_type_id type; /**< @brief Primitive type identificator. */ \ - cldnn_primitive_id id; /**< @brief Primitive id unique within a topology. */ \ - cldnn_primitive_id_arr input; /**< @brief Input primitives ids. */ \ - cldnn_padding output_padding; /**< @brief Output padding information. */ \ - cldnn_optional_data_type output_data_type; /**< @brief If specified, describes an explicit change of the output precision of the primitive. */ - -/// @brief Close primitive descriptor definition. -#define CLDNN_END_PRIMITIVE_DESC(PType) \ - }; - -#define CLDNN_PRIMITIVE_DESC(PType) cldnn_##PType##_desc - -/// @brief Basic primitive descriptor structure. -CLDNN_BEGIN_PRIMITIVE_DESC(primitive) -CLDNN_END_PRIMITIVE_DESC(primitive) - -/// @} - -/// @addtogroup c_version -/// @{ -/// @brief Get information about version of clDNN. -CLDNN_API cldnn_version cldnn_get_version(cldnn_status* status); -/// @} - -/// @addtogroup c_topology -/// @{ - -/// @brief Create empty network topology -CLDNN_API cldnn_topology cldnn_create_topology(cldnn_status* status); - -/// @brief Add new primitive to the topology. -/// @param[in] dto The pointer to a structure defined by @ref CLDNN_BEGIN_PRIMITIVE_DESC and @ref CLDNN_END_PRIMITIVE_DESC -CLDNN_API void cldnn_add_primitive(cldnn_topology topology, const struct CLDNN_PRIMITIVE_DESC(primitive) * dto, cldnn_status* status); - -/// @brief Change input layout of the topology. -/// @param[in] id of the input layout in the topology -/// @param[in] new_layout of the input layout -CLDNN_API void cldnn_change_input_layout(cldnn_topology topology, cldnn_primitive_id id, cldnn_layout new_layout, cldnn_status* status); - -/// @brief Return all primitives id from topology. -/// @details Function fills user provided buffer by primitive ids. Each id is followed by '\0'. -/// @param[in] ids Pointer to user-allocated buffer to store names. -/// @param[in] size Size (in chars) of the buffer. -/// @param[out] size_ret Required size (in chars) to store result. -CLDNN_API void cldnn_get_primitive_ids(cldnn_topology topology, char* ids, size_t size, size_t* size_ret, cldnn_status* status); - -/// @brief Increment reference counter for the topology object. -CLDNN_API void cldnn_retain_topology(cldnn_topology topology, cldnn_status* status); - -/// @brief Decrement reference counter for the topology object. Deletes object when counter becomes zero. -CLDNN_API void cldnn_release_topology(cldnn_topology topology, cldnn_status* status); -/// @} - -/// @addtogroup c_engine -/// @{ - -/// @brief number of available engines of the particular type -CLDNN_API uint32_t cldnn_get_engine_count(/*cldnn_engine_type*/ int32_t type, cldnn_status* status); - -/// @brief Release pending memory allocated in OpenCL context. -/// @param[in] type Engine type @ref cldnn_engine_type. Only OCL engine is supported. -/// @details OpenCL does not guarantee that the memory will be released (even with cl:Buffers releaed). -/// Use this function to force releasing whole pending memory. -CLDNN_API void cldnn_release_pending_memory(cldnn_engine engine, uint16_t stream_id, cldnn_status* status); - -/// @brief Create new engine of the specified @p type, @p engine_num, and @p configuration options. -/// @param[in] type Engine type @ref cldnn_engine_type. Only OCL engine is supported. -/// @param[in] engine_num Engine index. Should be 0. -/// @param[in] configuration Pointer to engine configuration options. -CLDNN_API cldnn_engine cldnn_create_engine( - /*cldnn_engine_type*/ int32_t type, - uint32_t engine_num, - const cldnn_engine_configuration* configuration, - cldnn_status* status); - -/// @brief Increment reference counter for the engine object. -CLDNN_API void cldnn_retain_engine(cldnn_engine engine, cldnn_status* status); - -/// @brief Decrement reference counter for the engine object. Deletes object when counter becomes zero. -CLDNN_API void cldnn_release_engine(cldnn_engine engine, cldnn_status* status); - -/// @brief Returns engine information. See @ref cldnn_engine_info for details. -CLDNN_API cldnn_engine_info cldnn_get_engine_info(cldnn_engine engine, cldnn_status* status); - -/// @brief Returns the @ref cldnn_engine_type for the particular engine -CLDNN_API /*cldnn_engine_type*/ int32_t cldnn_get_engine_type(cldnn_engine engine, cldnn_status* status); - -/// @brief Returns total size of all resources allocated using given engine -CLDNN_API int64_t cldnn_get_temp_used_device_memory_size(cldnn_engine engine, cldnn_status* status); -/// @} - -/// @brief Returns max size of resources allocated using given engine -CLDNN_API int64_t cldnn_get_max_used_device_memory_size(cldnn_engine engine, cldnn_status* status); - -/// @addtogroup c_network -/// @{ - -/// @brief Creates an event which can be set by user. -CLDNN_API cldnn_event cldnn_create_user_event(cldnn_engine engine, uint16_t stream_id, cldnn_status* status); - -/// @brief Checks if an event was created by user. -CLDNN_API int32_t cldnn_is_user_event(cldnn_event event, cldnn_status* status); - -/// @brief Increment reference counter for the event object. -CLDNN_API void cldnn_retain_event(cldnn_event event, cldnn_status* status); - -/// @brief Decrement reference counter for the event object. Deletes object when counter becomes zero. -CLDNN_API void cldnn_release_event(cldnn_event event, cldnn_status* status); - -/// @brief Waits for event completion or error. -CLDNN_API void cldnn_wait_for_event(cldnn_event event, cldnn_status* status); - -/// @brief Set event status to @p completed. -CLDNN_API void cldnn_set_event(cldnn_event event, cldnn_status* status); - -/// @brief Register call back to be called on event completion. -/// @param[in] handler Pointer to @ref cldnn_event_handler call-back function. -/// @param[in] param user-defined value to be passed to the call back function. -CLDNN_API void cldnn_add_event_handler(cldnn_event event, cldnn_event_handler handler, void* param, cldnn_status* status); - -/// @brief Returns the profiling information for an network primitive associated with event. -/// @param[in] profiling Pointer to the array of @ref cldnn_profiling_interval where information to be stored. -/// @param[in] size Number of elements in the array of @ref cldnn_profiling_interval. -/// @param[out] size_ret Number of elements required to store profiling information. -CLDNN_API void cldnn_get_event_profiling_info(cldnn_event event, cldnn_profiling_interval* profiling, size_t size, size_t* size_ret, cldnn_status* status); -/// @} - -/// @addtogroup c_program -/// @{ - -/// @brief Builds executable program based on user-defined @p topology by specified @p engine. -/// @param[in] engine The engine which will be used to build the program. -/// @param[in] topology The user-defined topology on which the network will be based. -/// @param[in] options The pointer of array of @ref cldnn_build_option which define network build options. -/// @param[in] options_num Number of elements in the @p options array. -CLDNN_API cldnn_program cldnn_build_program( - cldnn_engine engine, - cldnn_topology topology, - cldnn_build_option* options, - size_t options_num, - cldnn_status* status); - -/// @brief Increment reference counter for the program object. -CLDNN_API void cldnn_retain_program(cldnn_program program, cldnn_status* status); - -/// @brief Decrement reference counter for the program object. Deletes object when counter becomes zero. -CLDNN_API void cldnn_release_program(cldnn_program program, cldnn_status* status); -/// @} - -/// @addtogroup c_network -/// @{ - -/// @brief Builds and allocates executable network based on user-defined @p topology by specified @p engine. -/// This is a shorthand for cldnn_build_program and cldnn_allocate_network. -/// @param[in] engine The engine which will be used to build the metwork. -/// @param[in] topology The user-defined topology on which the network will be based. -/// @param[in] options The pointer of array of @ref cldnn_build_option which define network build options. -/// @param[in] options_num Number of elements in the @p options array. -CLDNN_API cldnn_network cldnn_build_network( - cldnn_engine engine, - cldnn_topology topology, - cldnn_build_option* options, - size_t options_num, - cldnn_status* status); - -/// @brief Allocates memory for a new network which will be able to execute specified @p program. -/// @param[in] program The program object which holds binaries compiled from some topology and engine. Multiple network objects can share the same program. -CLDNN_API cldnn_network cldnn_allocate_network(cldnn_program program, uint16_t stream_id, cldnn_status* status); - -/// @brief Increment reference counter for the network object. -CLDNN_API void cldnn_retain_network(cldnn_network network, cldnn_status* status); - -/// @brief Decrement reference counter for the network object. Deletes object when counter becomes zero. -CLDNN_API void cldnn_release_network(cldnn_network network, cldnn_status* status); - -/// @brief Provides user input data to the network (for @p input_layout primitives). -/// @param[in] id Primitive @p id of @p input_layout primitive defined in @p topology. -/// @param[in] mem Memory object with user data which @p layout matches the @p input_layout defined in @p topology. -/// @details User should set the input data for every @p input_layout primitive defined in @p topology -/// by calling this function before call to cldnn_execute_network(). -CLDNN_API void cldnn_set_network_input(cldnn_network network, cldnn_primitive_id id, cldnn_memory mem, cldnn_status* status); - -/// @brief Sets learning rate for training primitives in network. -/// @param[in] lr Learning rate. -CLDNN_API void cldnn_set_learning_rate(cldnn_network network, float lr, cldnn_status* status); - -/// @brief Returns learning rate value. -CLDNN_API float cldnn_get_learning_rate(cldnn_network network, cldnn_status* status); - -/// @brief Returns information about particular primitive. -/// @details Function fills user provided buffer by primitive description. -/// @param[in] id Primitive @p id of @p input_layout primitive defined in @p topology. -/// @param[in] info Pointer to user-allocated buffer to store names. -/// @param[in] size Size (in chars) of the buffer. -/// @param[out] size_ret Required size (in chars) to store result. -/// @returns pointer to array of chars with detailed information about particular primitive. -CLDNN_API void cldnn_get_primitive_info(cldnn_network network, cldnn_primitive_id id, char* info, size_t size, size_t* size_ret, cldnn_status* status); - -/// @brief Returns @p engine associated with the @p network. -CLDNN_API cldnn_engine cldnn_get_network_engine(cldnn_network network, cldnn_status* status); - -/// @brief Returns @p program associated with the @p network. -CLDNN_API cldnn_program cldnn_get_network_program(cldnn_network network, cldnn_status* status); - -/// @brief Returns names of network outputs. -/// @details Function fills user provided buffer by primitive names. Each name is followed by '\0'. -/// Empty name "\0\0" means end of data. -/// @param[in] names Pointer to user-allocated buffer to store names. -/// @param[in] size Size (in chars) of the buffer. -/// @param[out] size_ret Required size (in chars) to store result. -CLDNN_API void cldnn_get_network_output_names(cldnn_network network, char* names, size_t size, size_t* size_ret, cldnn_status* status); - -/// @brief Returns names of executed primitives. -/// @details Function fills user provided buffer by primitive names. Each name is followed by '\0'. -/// Empty name "\0\0" means end of data. -/// @param[in] names Pointer to user-allocated buffer to store names. -/// @param[in] size Size (in chars) of the buffer. -/// @param[out] size_ret Required size (in chars) to store result. -CLDNN_API void cldnn_get_network_executed_primitive_names(cldnn_network network, char* names, size_t size, size_t* size_ret, cldnn_status* status); - -/// @brief Returns names of all primitives in network. -/// @details Function fills user provided buffer by primitive names. Each name is followed by '\0'. -/// Empty name "\0\0" means end of data. -/// @param[in] names Pointer to user-allocated buffer to store names. -/// @param[in] size Size (in chars) of the buffer. -/// @param[out] size_ret Required size (in chars) to store result. -CLDNN_API void cldnn_get_network_all_primitive_names(cldnn_network network, char* names, size_t size, size_t* size_ret, cldnn_status* status); - -/// @brief Returns names of all primitives in network before graph optimization. -/// @details Function fills user provided buffer by primitive names. Each name is followed by '\0'. -/// Empty name "\0\0" means end of data. -/// @param[in] names Pointer to user-allocated buffer to store names. -/// @param[in] size Size (in chars) of the buffer. -/// @param[out] size_ret Required size (in chars) to store result. -CLDNN_API void cldnn_get_network_all_primitive_org_names(cldnn_network network, char* names, size_t size, size_t* size_ret, cldnn_status* status); - -/// @brief Executes network. -/// @details User should call cldnn_set_network_input() for every @p input_layout defined in tho source @p topology. -/// Function returns immediately, even if @p dependencies are not set yet. -/// @params dependencies Pointer to an array of @ref cldnn_events to be waited for network execution. -/// @param deps_num Number of elements in the @p dependencies array. -CLDNN_API void cldnn_execute_network(cldnn_network network, cldnn_event* dependencies, size_t deps_num, cldnn_status* status); - -/// @brief Returns executed network output information. -/// @details User should call this function after cldnn_execute_network() to get result of network execution. -/// @param name Output name to get the result. -/// @returns @ref cldnn_network_output structure with the output information. -/// To work with the result of this function, user should first wait for cldnn_network_output::event -/// before getting an access to cldnn_network_output::memory. -CLDNN_API cldnn_network_output cldnn_get_network_output(cldnn_network network, const char* name, cldnn_status* status); - -/// @brief Returns @ref memory corresponding to output with @p name. -/// @details User can call this function even before calling cldnn_execute_network(), but then content of memory is uninitialized. -/// @param name Output name to get the result. -/// @returns @ref cldnn_memory structure with the output information. -CLDNN_API cldnn_memory cldnn_get_network_output_memory(cldnn_network network, const char* name, cldnn_status* status); - -/// @brief Returns @ref event corresponding to output with @p name. -/// @details User can call this function even before calling cldnn_execute_network(), but then content of memory is uninitialized. -/// @param name Output name to get the result. -/// @returns @ref cldnn_event structure with the output information. -CLDNN_API cldnn_event cldnn_get_network_output_event(cldnn_network network, const char* name, cldnn_status* status); - -/// @brief Returns description of final runtime graph -/// @details Function fills user provided buffer by primitive_info structures. -/// Should be called after network compilation. -/// @param[in] info array with the runtime information for each primitive. -/// @param[in] size Elements count in the buffer. -/// @param[out] size_ret Required size of array (in elements) to store result. -CLDNN_API void cldnn_get_primitives_info(cldnn_network event, const cldnn_primitive_info** info, size_t size, - size_t* size_ret, cldnn_status* status); - -/// @brief Returns description of all optimization stages -/// @details Function fills user provided buffer by primitive_info structures. -/// Should be called after network compilation. -/// @param[in] info array with the runtime information for each primitive after all optimization passes. -/// @param[in] pass_sizes array with descriptors count on each step. -/// @param[in] pass_names array with names on each step. -/// @param[in] total_size Elements count in the buffer. -/// @param[out] total_size_ret Required size of info array (in elements) to store result for all steps. -/// @param[out] pass_count_ret Required size of steps_size array (in elements) to store step lengths in info array. -/// @param[out] pass_names_total_size_ret Required size of step_names array (in char elements) to store step names. -CLDNN_API void cldnn_get_optimizer_passes_info(cldnn_network network, - const cldnn_primitive_info** info, int* pass_sizes, char* pass_names, - size_t total_size, - size_t* total_size_ret, size_t* pass_count_ret, size_t* pass_names_total_size_ret, - cldnn_status* status); -/// @} - -/// @addtogroup c_memory -/// @{ - -/// @brief Allocate memory on @p engine using specified @p layout. -CLDNN_API cldnn_memory cldnn_allocate_memory(cldnn_engine engine, cldnn_layout layout, uint16_t stream_id, cldnn_status* status); -/// @brief Create memory object attached to the buffer allocated by user. -/// @note User is responsible for buffer deallocation. Buffer lifetime should be bigger than lifetime of the memory object. -CLDNN_API cldnn_memory cldnn_attach_memory(cldnn_layout layout, void* pointer, size_t size, uint16_t stream_id, cldnn_status* status); -/// @brief Checks if two memory objects refer to the same underlaying buffer. -CLDNN_API int32_t cldnn_is_the_same_buffer(cldnn_memory mem1, cldnn_memory mem2, cldnn_status* status); -/// @brief Increment reference counter for the memory object. -CLDNN_API void cldnn_retain_memory(cldnn_memory memory, cldnn_status* status); -/// @brief Decrement reference counter for the memory object. Deletes object when counter becomes zero. -CLDNN_API void cldnn_release_memory(cldnn_memory memory, cldnn_status* status); -/// @brief Locks memory buffer. Provides direct access to memory data. -/// @returns Direct pointer to the memory data. -CLDNN_API void* cldnn_lock_memory(cldnn_memory memory, cldnn_status* status); -/// @brief Unlocks memory locked by cldnn_lock_memory(cldnn_memory memory, cldnn_status* status). -CLDNN_API void cldnn_unlock_memory(cldnn_memory memory, cldnn_status* status); -/// @brief Returns memory layout -/// @returns @ref cldnn_layout which describes memory. -CLDNN_API cldnn_layout cldnn_get_memory_layout(cldnn_memory memory, cldnn_status* status); -/// @brief Returns stream id of the memory object -CLDNN_API uint16_t cldnn_get_memory_stream_id(cldnn_memory memory, cldnn_status* status); -/// @brief Returns stream id of the network -CLDNN_API uint16_t cldnn_get_network_stream_id(cldnn_network network, cldnn_status* status); -/// @brief Returns reference to the engine associated with memory object. -/// @returns The engine associated with memory object. Or NULL if memory was attached to user-allocated buffer. -CLDNN_API cldnn_engine cldnn_get_memory_engine(cldnn_memory memory, cldnn_status* status); -/// @brief converts float(32 bit) to half_t(fp16 bit) -/// @returns 16bit half_t -CLDNN_API uint16_t cldnn_float_to_half(float, cldnn_status*); -/// @brief converts half_t(f16 bit) to float(32 bit) -/// @returns 32bit float -CLDNN_API float cldnn_half_to_float(uint16_t, cldnn_status*); - -/// @} - -/// @addtogroup c_error -/// @{ - -/// @brief If cldnn function returns status different than CLDNN_SUCCESS, user call this function to get more details. -/// @returns pointer to array of chars with more detailed description of last error. -/// @note If sequence of error occure, description of only last error will avaiable -CLDNN_API const char* cldnn_get_last_error_message(); -/// @} - -#ifdef __cplusplus -} -#endif - -/// @} - -// primitives -#ifdef __cplusplus -#define CLDNN_DECLARE_PRIMITIVE_TYPE_ID(PType) extern "C" CLDNN_API cldnn_primitive_type_id cldnn_##PType##_type_id(cldnn_status* status) -#else -#define CLDNN_DECLARE_PRIMITIVE_TYPE_ID(PType) CLDNN_API cldnn_primitive_type_id cldnn_##PType##_type_id(cldnn_status* status) -#endif - - diff --git a/inference-engine/thirdparty/clDNN/api/C/concatenation.h b/inference-engine/thirdparty/clDNN/api/C/concatenation.h deleted file mode 100644 index 9620f834d0d85b..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/concatenation.h +++ /dev/null @@ -1,74 +0,0 @@ -/* -// Copyright (c) 2016-2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - cldnn_concatenation_along_b = 0, - cldnn_concatenation_along_f = CLDNN_TENSOR_BATCH_DIM_MAX, - cldnn_concatenation_along_x = CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX, - cldnn_concatenation_along_y = cldnn_concatenation_along_x + 1, - cldnn_concatenation_along_z = cldnn_concatenation_along_y + 1, - cldnn_concatenation_along_w = cldnn_concatenation_along_z + 1 -} cldnn_concatenation_axis; - -/// @details Concatenation is used to concatenate multiple sources into one destination along specified dimension. -/// Note that all other dimensions (except the one along which concatenation take place) must have the same value in each source -/// and each source should have the same format. -/// @par Alogrithm: -/// \code -/// int outputIdx = 0 -/// for(i : input) -/// { -/// for(f : i.features) -/// { -/// output[outputIdx] = f -/// outputIdx += 1 -/// } -/// } -/// \endcode -/// @par Where: -/// @li input : data structure holding all source inputs for this primitive -/// @li output : data structure holding output data for this primitive -/// @li i.features : number of features in currently processed input -/// @li outputIdx : index of destination feature -CLDNN_BEGIN_PRIMITIVE_DESC(concatenation) -/// @brief Dimension along which concatenation should take place. -cldnn_concatenation_axis axis; -CLDNN_END_PRIMITIVE_DESC(concatenation) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(concatenation); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/condition.h b/inference-engine/thirdparty/clDNN/api/C/condition.h deleted file mode 100644 index 7c47ed74837c73..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/condition.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" - -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Function, which will be used during comparison. -typedef enum /*:int32_t*/ { - EQUAL, - GREATER, - LESS -} cldnn_cond_functions; - -/// @brief Adds primitive, which works like "if". -/// -/// @details -/// @n Applies comparision between 2 inputs. -/// @n Compare data - sizes of that input specifes the range of the comparison. -/// @n Offset - offset in memory, when comparing values. -CLDNN_BEGIN_PRIMITIVE_DESC(condition) -/// @brief An identifier of topology, which will be executed when comparison returns true. -cldnn_topology topology_true; -/// @brief An identifier of topology, which will be executed when comparison returns false. -cldnn_topology topology_false; -/// @brief An identifier of primitive which contains compare values. -cldnn_primitive_id compare_data; -/// @brief Used function during comparison. -cldnn_cond_functions function; -/// @brief Offset for compare data. -cldnn_tensor offset; - -CLDNN_END_PRIMITIVE_DESC(condition) -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(condition); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/contract.h b/inference-engine/thirdparty/clDNN/api/C/contract.h deleted file mode 100644 index da5ad67da52d77..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/contract.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" - -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Select reduction operation for contract layer ( @CLDNN_PRIMITIVE_DESC{contract} ?). -typedef enum /*:int32_t*/ { - /// @brief Sum reduction. - cldnn_contract_sum, - /// @brief Product reduction. - cldnn_contract_product, - /// @brief All reduction. - cldnn_contract_all, - /// @brief Any reduction. - cldnn_contract_any, - /// @brief Max reduction. - cldnn_contract_max -} cldnn_contract_mode; - -/// @brief Reduces input with an operation defined by @p mode along defined -/// by @p reduction_axes dimensions. -/// -/// @details Reduces the input using the binary operation determined by -/// @p mode. The @p reduction_axes determine the final shape of the -/// output, which is calculated based on the input shape by -/// collapsing the dimensions along which the reduction happens. -/// For example, for the input with -/// @n input_sizes = (in_b, in_f, in_y, in_x) -/// @n a reduction with -/// @n reduction_axes = (2) -/// @n would collapse the Y dimension, producing -/// @n output_shape = (1, in_b, in_f, in_x) -/// @n where every element is a @p mode reduction of the input elements with -/// @n the same B, F and X coordinates. -/// @n -/// @n@b Requirements: -/// @n - @p reduction_axes size (dimensions count) must be within (inclusive) range -/// 1 - 4. -/// @n - @p reduction_axes mustn't have duplicate values. -/// @n - Values of @p reduction_axes must be within (inclusive) range 0 - 3 -/// @n Breaking any of these conditions will raise an exception. -CLDNN_BEGIN_PRIMITIVE_DESC(contract) -/// @brief Reduction mode. See #cldnn_contract_mode. -int32_t mode; /*cldnn_contract_mode*/ -/// @brief Array of axes positions from input shape (0-based, from left to right) -/// along which reduction should happen. -cldnn_uint16_t_arr reduction_axes; - -CLDNN_END_PRIMITIVE_DESC(contract) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(contract); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/convolution.h b/inference-engine/thirdparty/clDNN/api/C/convolution.h deleted file mode 100644 index 1405060e818a15..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/convolution.h +++ /dev/null @@ -1,88 +0,0 @@ -/* -// Copyright (c) 2016-2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs forward spatial convolution with weight sharing. -/// Also supports built-in Relu @CLDNN_PRIMITIVE_DESC{activation} available by setting it in arguments. -/// @details Parameters are defined in context of "direct" convolution, but actual algorithm is not implied. -CLDNN_BEGIN_PRIMITIVE_DESC(convolution) -/// @brief Defines a shift, relative to (0,0) position of the input buffer, where (0,0) point of the convolution window should start calculations. -cldnn_tensor input_offset; -/// @brief Defines shift in input buffer between adjacent calculations of output values. -cldnn_tensor stride; -/// @brief Defines gaps in the input - dilation rate k=1 is normal convolution, k=2 means skipping one pixel per input, k=4 means skipping 3 pixels. -/// As an example in one dimension, a filter w of size 3 would compute over input x the following: w[0]*x[0] + w[1]*x[1] + w[2]*x[2] for dilation of 1. -/// For dilation 2 the filter would instead compute w[0]*x[0] + w[1]*x[2] + w[2]*x[4]. -cldnn_tensor dilation; -/// @brief Enable Relu activation. -uint32_t with_activation; -/// @brief Relu activation slope. -float activation_negative_slope; -/// @brief On how many cards split the computation to. -uint32_t split; -/// @brief Indicates that the primitive has user-defined output size (non-zero value). -uint32_t with_output_size; -/// @brief User-defined output data size of the primitive (w/o padding). -cldnn_tensor output_size; -/// @brief Array of primitive ids containing weights data. Size of array should be equivalent to @p split. -cldnn_primitive_id_arr weights; -/// @brief Array of primitive ids containing bias data. Size of array should be equivalent to @p split. -cldnn_primitive_id_arr bias; -/// @brief List of primitive ids containing weights quanitization factors per output feature map. -cldnn_primitive_id_arr weights_quantization_factors; -/// @brief List of primitive ids containing output calibration factors per output feature map. -cldnn_primitive_id_arr output_calibration_factors; -/// @brief Input quantization factor -float input_quantization_factor; -/// @brief Output quantization factor -float output_quantization_factor; -/// @brief Number of feature groups (grouped convolution). If more than 1 then weights/bias count needs to be 1. -uint32_t groups; -/// @param deformable_groups Defines a number of deformable groups that splits trans input into several parts -/// by channel dimension. -uint32_t deformable_groups; -/// @param padding_above Defines a padding added to input image on left (x axis) and top (y axis). -cldnn_tensor padding_above; -/// @param padding_below Defines a padding added to input image on right (x axis) and bottom (y axis). -cldnn_tensor padding_below; -/// @param deformable_mode. -uint8_t deformable_mode; - -CLDNN_END_PRIMITIVE_DESC(convolution) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(convolution); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/convolution_grad_input.h b/inference-engine/thirdparty/clDNN/api/C/convolution_grad_input.h deleted file mode 100644 index 90073077541fec..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/convolution_grad_input.h +++ /dev/null @@ -1,58 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs transposed convolution. -/// @details convolution_grad_input is similar to convolution layer with the weights flipped on the axis and stride -/// and input padding parameters used in opposite sense as in convolution. -CLDNN_BEGIN_PRIMITIVE_DESC(convolution_grad_input) -/// @brief Defines a shift, relative to (0,0) position of the input buffer, where (0,0) point of the convolution_grad_input window should start calculations. -cldnn_tensor input_offset; -/// @brief Defines the spatial dimensions of stride of adjacent elements in input buffer. -cldnn_tensor stride; -/// @brief On how many cards split the computation to. -uint32_t split; -/// @brief Indicates that the primitive has user-defined output size (non-zero value). -uint32_t with_output_size; -/// @brief User-defined output data size of the primitive (w/o padding). -cldnn_tensor output_size; -/// @brief Array of primitive ids containing weights data. Size of array should be equivalent to @p split. -cldnn_primitive_id_arr weights; -CLDNN_END_PRIMITIVE_DESC(convolution_grad_input) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(convolution_grad_input); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/convolution_grad_weights.h b/inference-engine/thirdparty/clDNN/api/C/convolution_grad_weights.h deleted file mode 100644 index 3fe6f40fe9fe41..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/convolution_grad_weights.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs backward convolution operation for weights and biases. -/// @details convolution_grad_weights updates weights and bias mutable data for training purposes. -/// @details Please note that this primitive was not heavily tested and currently only batch=1 is enabled for this primitive. -CLDNN_BEGIN_PRIMITIVE_DESC(convolution_grad_weights) -/// @brief Defines a shift, relative to (0,0) position of the input buffer, where (0,0) point of the convolution_grad_weights window should start calculations. -cldnn_tensor input_offset; -/// @brief Defines the spatial dimensions of stride of adjacent elements in input buffer. -cldnn_tensor stride; -/// @brief Defines gaps in the input - dilation rate k=1 is normal convolution, k=2 means skipping one pixel per input, k=4 means skipping 3 pixels. -/// As an example in one dimension, a filter w of size 3 would compute over input x the following: w[0]*x[0] + w[1]*x[1] + w[2]*x[2] for dilation of 1. -/// For dilation 2 the filter would instead compute w[0]*x[0] + w[1]*x[2] + w[2]*x[4]. -cldnn_tensor dilation; -/// @brief On how many cards split the computation to. -uint32_t split; -/// @brief Array of primitive ids containing weights data. Size of array should be equivalent to @p split. -cldnn_primitive_id_arr weights; -/// @brief Array of primitive ids containing bias data. Size of array should be equivalent to @p split or should be empty (if not using bias). -cldnn_primitive_id_arr bias; -/// @brief Primitive id containing convolution gradient data. Used for proper order of gradient calculation. Leave empty if primitive is last in backward pass. -cldnn_primitive_id conv_grad; -/// @brief Array of primitive ids containing weights gradient data calculated in previous iteration. -/// Amount of primitives and their memory sizes should be same as weights. -cldnn_primitive_id_arr prev_weights_grad; -/// @brief Array of primitive ids containing bias gradient data calculated in previous iteration. -/// Amount of primitives and their memory sizes should be same as biases. -cldnn_primitive_id_arr prev_bias_grad; -/// @brief Should primitive give weights gradient (delta) as an output -bool output_grad_w; - -CLDNN_END_PRIMITIVE_DESC(convolution_grad_weights) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(convolution_grad_weights); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/crop.h b/inference-engine/thirdparty/clDNN/api/C/crop.h deleted file mode 100644 index f1e1df3ac7bafa..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/crop.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs crop operation on input. -/// @details Crops the input to the shape of reference_input across all dimensions taking into account specified input offsets. -/// @n Borders variant calculated output shape from input shape minus the specified borders. -/// @n -/// @n\b Examples -/// @n Crop without offset example: -/// \image html crop_no_offset.jpg -/// @n Crop with offset example: -/// \image html crop_w_offset.jpg -/// @n -/// @n\b Requirements (reference size variant) -/// @n - Input size cannot be greater than reference size in any dimension -/// @n - All sizes have to have positive numbers -/// @n - Reference size plus offset cannot exceed input size -/// @n -/// @n\b Requirements (borders variant) -/// @n - Borders support batch, feature and spatial dimensions (rest of dimensions ignored). -/// @n - Input size cannot be greater than reference size in any dimension -/// @n - All sizes specified in borders have to have non-negative values (positive or @c 0). -/// @n - Sum of sizes of opposite borders must be lower than input size (on all non-ignored dimensions). -/// @n -/// @n Breaking any of this conditions will cause exception throw. -CLDNN_BEGIN_PRIMITIVE_DESC(crop) -/// @brief Reference input tensor with the required dimensions (if positive) or -/// negated value of right/bottom/upper border size (if non-positive). -cldnn_tensor reference_input; -/// @brief Input offsets (reference_input is positive) or left/top/lower border -/// size (reference_input is negative). -cldnn_tensor offsets; -CLDNN_END_PRIMITIVE_DESC(crop) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(crop); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/custom_gpu_primitive.h b/inference-engine/thirdparty/clDNN/api/C/custom_gpu_primitive.h deleted file mode 100644 index 3c3129a9060223..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/custom_gpu_primitive.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief This primitive executes a custom kernel provided by the application -/// @details The application is required to provide all relevant details for executing the custom kernel -/// such as: sources, entry point, work sizes and parameter bindings. -CLDNN_BEGIN_PRIMITIVE_DESC(custom_gpu_primitive) -/// @brief Source code for the kernel -cldnn_primitive_id_arr kernels_code; -/// @brief The name of the entry point function in the kernel -cldnn_kernel_entry_point kernel_entry_point; -/// @brief Argument bindings for the entry point function -cldnn_kernel_arguments kernel_arguments; -/// @brief The number of arguments used by the kernel -int kernel_arguments_num; -/// @brief The kernel's build options -cldnn_kernel_build_options build_options; -/// @brief The output layout declared by the primitive -cldnn_layout output_layout; -/// @brief The global working sizes -cldnn_work_group_sizes gws; -/// @brief The number of global work sizes -int gws_num; -/// @brief The local working sizes -cldnn_work_group_sizes lws; -/// @brief The number of local work sizes -int lws_num; - -CLDNN_END_PRIMITIVE_DESC(custom_gpu_primitive) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(custom_gpu_primitive); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/data.h b/inference-engine/thirdparty/clDNN/api/C/data.h deleted file mode 100644 index 6c8914181b2b08..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/data.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Provides input data to topology. -/// @details This primitive allows to pass data which is known at topology creation (constants). -/// For example, weights and biases for scoring networks. -/// @note Passing data at topology may improve network performance if data optimization is enabled. -CLDNN_BEGIN_PRIMITIVE_DESC(data) -/// @brief Memory object which contains data. -/// @note If memory is attached by ::cldnn_attach_memory(), -/// attached buffer should be valid on ::cldnn_build_network() call. -cldnn_memory mem; -CLDNN_END_PRIMITIVE_DESC(data) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(data); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/deconvolution.h b/inference-engine/thirdparty/clDNN/api/C/deconvolution.h deleted file mode 100644 index 0dd6d92167e9ab..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/deconvolution.h +++ /dev/null @@ -1,69 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs transposed convolution. -/// Also supports built-in Relu @CLDNN_PRIMITIVE_DESC{activation} available by setting it in arguments. -/// @details Deconvolution is similar to convolution layer with the weights flipped on the axis -/// and stride and input padding parameters used in opposite sense as in convolution. -CLDNN_BEGIN_PRIMITIVE_DESC(deconvolution) -/// @brief Defines a shift, relative to (0,0) position of the input buffer, where (0,0) point of the deconvolution window should start calculations. -cldnn_tensor input_offset; -/// @brief Defines the spatial dimensions of stride of adjacent elements in input buffer. -cldnn_tensor stride; -/// @brief Enables Relu activation. -uint32_t with_activation; -/// @brief Relu activation slope. -float activation_negative_slope; -/// @brief On how many cards split the computation to. -uint32_t split; -/// @brief Indicates that the primitive has user-defined output size (non-zero value). -uint32_t with_output_size; -/// @brief User-defined output data size of the primitive (w/o padding). -cldnn_tensor output_size; -/// @brief Array of primitive ids containing weights data. Size of array should be equivalent to @p split. -cldnn_primitive_id_arr weights; -/// @brief Array of primitive ids containing bias data. Size of array should be equivalent to @p split or should be empty (if not using bias). -cldnn_primitive_id_arr bias; -/// @brief Indicates that deconvolution is used for convolution backward computation (convolution_grad_input) -uint32_t gradient; -/// @brief Number of feature groups (grouped deconvolution). If more than 1 then weights/bias count needs to be 1. -uint32_t groups; -CLDNN_END_PRIMITIVE_DESC(deconvolution) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(deconvolution); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/deformable_conv.h b/inference-engine/thirdparty/clDNN/api/C/deformable_conv.h deleted file mode 100644 index 58b2d8a6ca39ae..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/deformable_conv.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -// Copyright (c) 2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs deformable convolution on a preprocessed data. Should be created after deformable_interp primitive. -CLDNN_BEGIN_PRIMITIVE_DESC(deformable_conv) -/// @brief On how many cards split the computation to. -uint32_t split; -/// @brief User-defined output data size of the primitive (w/o padding). -cldnn_tensor output_size; -/// @brief Array of primitive ids containing weights data. Size of array should be equivalent to @p split. -cldnn_primitive_id_arr weights; -/// @brief Array of primitive ids containing bias data. Size of array should be equivalent to @p split. -cldnn_primitive_id_arr bias; -/// @brief Number of feature groups (grouped convolution). If more than 1 then weights/bias count needs to be 1. -uint32_t groups; - -CLDNN_END_PRIMITIVE_DESC(deformable_conv) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(deformable_conv); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/deformable_interp.h b/inference-engine/thirdparty/clDNN/api/C/deformable_interp.h deleted file mode 100644 index c71b7163015d4b..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/deformable_interp.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -// Copyright (c) 2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs interpolation pass for deformable convolution. Output tensor has IC*KH*KW channels. -CLDNN_BEGIN_PRIMITIVE_DESC(deformable_interp) -/// @brief Defines a shift, relative to (0,0) position of the input buffer, where (0,0) point of the convolution window should start calculations. -cldnn_tensor input_offset; -/// @brief Defines shift in input buffer between adjacent calculations of output values. -cldnn_tensor stride; -/// @brief Defines gaps in the input - dilation rate k=1 is normal convolution, k=2 means skipping one pixel per input, k=4 means skipping 3 pixels. -/// As an example in one dimension, a filter w of size 3 would compute over input x the following: w[0]*x[0] + w[1]*x[1] + w[2]*x[2] for dilation of 1. -/// For dilation 2 the filter would instead compute w[0]*x[0] + w[1]*x[2] + w[2]*x[4]. -cldnn_tensor dilation; -/// @brief On how many cards split the computation to. -uint32_t split; -/// @brief User-defined output data size of the primitive (w/o padding). -cldnn_tensor output_size; -/// @brief Size of the weights tensor. -cldnn_tensor kernel_size; -uint32_t groups; -/// @param deformable_groups Defines a number of deformable groups that splits trans input into several parts -/// by channel dimension. -uint32_t deformable_groups; -/// @param padding_above Defines a padding added to input image on left (x axis) and top (y axis). -cldnn_tensor padding_above; -/// @param padding_below Defines a padding added to input image on right (x axis) and bottom (y axis). -cldnn_tensor padding_below; - -CLDNN_END_PRIMITIVE_DESC(deformable_interp) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(deformable_interp); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} diff --git a/inference-engine/thirdparty/clDNN/api/C/detection_output.h b/inference-engine/thirdparty/clDNN/api/C/detection_output.h deleted file mode 100644 index ea01203d7ac7f9..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/detection_output.h +++ /dev/null @@ -1,89 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Select method for coding the prior-boxes in Detection Output layer ( @CLDNN_PRIMITIVE_DESC{detection_output} ). -typedef enum /*:int32_t*/ { - cldnn_code_type_corner, - cldnn_code_type_center_size, - cldnn_code_type_corner_size, -} cldnn_prior_box_code_type; - -/// @brief Generates a list of detections based on location and confidence predictions by doing non maximum suppression. -/// @details Each row is a 7 dimension vector, which stores: [image_id, label, confidence, xmin, ymin, xmax, ymax]. -/// If number of detections per image is lower than keep_top_k, will write dummy results at the end with image_id=-1. -CLDNN_BEGIN_PRIMITIVE_DESC(detection_output) -/// @brief Number of classes to be predicted. -uint32_t num_classes; -/// @brief Number of total bounding boxes to be kept per image after NMS step. -uint32_t keep_top_k; -/// @brief If not 0, bounding box are shared among different classes. -uint32_t share_location; -/// @brief Background label id (-1 if there is no background class). -int background_label_id; -/// @brief Threshold for NMS step. -float nms_threshold; -/// @brief Maximum number of results to be kept in NMS. -int top_k; -/// @brief Used for adaptive NMS. -float eta; -/// @brief Type of coding method for bounding box. See #cldnn_prior_box_code_type. -int32_t code_type; -/// @brief If not 0, variance is encoded in target; otherwise we need to adjust the predicted offset accordingly. -uint32_t variance_encoded_in_target; -/// @brief Only keep detections with confidences larger than this threshold. -float confidence_threshold; -/// @brief Number of elements in a single prior description (4 if priors calculated using PriorBox layer, 5 - if Proposal) -int32_t prior_info_size; -/// @brief Offset of the box coordinates w.r.t. the beginning of a prior info record -int32_t prior_coordinates_offset; -/// @brief If true, priors are normalized to [0; 1] range. -uint32_t prior_is_normalized; -/// @brief Width of input image. -int32_t input_width; -/// @brief Height of input image. -int32_t input_height; -/// @brief Decrease label id to skip background label equal to 0. Can't be used simultaneously with background_label_id. -int32_t decrease_label_id; -/// @brief Clip decoded boxes right after decoding -int32_t clip_before_nms; -/// @brief Clip decoded boxes after nms step -int32_t clip_after_nms; -CLDNN_END_PRIMITIVE_DESC(detection_output) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(detection_output); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/detection_output_sort.h b/inference-engine/thirdparty/clDNN/api/C/detection_output_sort.h deleted file mode 100644 index 94d6b3cd30c94c..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/detection_output_sort.h +++ /dev/null @@ -1,58 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Generates a list of detections based on location and confidence predictions by doing non maximum suppression. -/// @details Each row is a 7 dimension vector, which stores: [image_id, label, confidence, xmin, ymin, xmax, ymax]. -/// If number of detections per image is lower than keep_top_k, will write dummy results at the end with image_id=-1. -CLDNN_BEGIN_PRIMITIVE_DESC(detection_output_sort) -/// @brief Number of classes to be predicted. -uint32_t num_classes; -/// @brief Number of classes to be predicted. -uint32_t num_images; -/// @brief Number of total bounding boxes to be kept per image after NMS step. -uint32_t keep_top_k; -/// @brief If true, bounding box are shared among different classes. -uint32_t share_location; -/// @brief Maximum number of results to be kept in NMS. -int top_k; -/// @brief Background label id (-1 if there is no background class). -int background_label_id; -CLDNN_END_PRIMITIVE_DESC(detection_output_sort) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(detection_output_sort); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/eltwise.h b/inference-engine/thirdparty/clDNN/api/C/eltwise.h deleted file mode 100644 index 64a26eaf59093a..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/eltwise.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) 2016-2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Select mode for eltwise layer ( @CLDNN_PRIMITIVE_DESC{eltwise} ​). -typedef enum /*:int32_t*/ { - /// @brief Eltwise sum. - cldnn_eltwise_sum, - /// @brief Eltwise subtract. - cldnn_eltwise_sub, - /// @brief Eltwise max. - cldnn_eltwise_max, - /// @brief Eltwise product (Hadamard). - cldnn_eltwise_prod, - /// @brief Eltwise div. - cldnn_eltwise_div, - /// @brief Eltwise min. - cldnn_eltwise_min, - /// @brief Eltwise pow. - cldnn_eltwise_pow, - /// @brief Eltwise mod. - cldnn_eltwise_mod, - /// @brief Eltwise equal. - cldnn_eltwise_eq, - /// @brief Eltwise not equal. - cldnn_eltwise_ne, - /// @brief Eltwise less. - cldnn_eltwise_lt, - /// @brief Eltwise less of equal. - cldnn_eltwise_le, - /// @brief Eltwise greater. - cldnn_eltwise_gt, - /// @brief Eltwise greater or equal. - cldnn_eltwise_ge, - /// @brief Eltwise and. - cldnn_eltwise_and, - /// @brief Eltwise or. - cldnn_eltwise_or, - /// @brief Eltwise xor. - cldnn_eltwise_xor, - /// @brief Eltwise squared diff. - cldnn_eltwise_squared_diff, - /// @brief Eltwise floormod. - cldnn_eltwise_floor_mod -} cldnn_eltwise_mode; - -/// @brief Performs elementwise operations (sum, subtract, max or product) on two input primitives -/// Also supports built-in Relu @CLDNN_PRIMITIVE_DESC{activation} available by setting it in arguments. -/// @notes -/// - both inputs have to have equal sizes in all dimensions or the input tensors are broadcastable -/// to the same shape in which the size of each dimention is a max. of input sizes on this dimension) -/// - format of both inputs has to be the same -/// - when using integer types, only following eltwise modes are supported: sum, sub, prod, div -CLDNN_BEGIN_PRIMITIVE_DESC(eltwise) -/// @brief Primitive id containing output quanitization factors per output feature map. -cldnn_primitive_id output_calibration_factors; -/// @brief Output quantization factor -float output_quantization_factor; -/// @brief List of primitive ids containing input quantization factors per feature map, one primitive id for each input. -cldnn_primitive_id_arr input_calibration_factors; -/// @brief List of quantization factors per input. -cldnn_float_arr input_quantization_factors; -/// @brief Eltwise mode. See #cldnn_eltwise_mode. -int32_t mode; /*cldnn_eltwise_mode*/ -/// @brief Blob-wise coefficient for SUM operation -cldnn_float_arr coefficients; -/// @brief Enables Relu activation. -uint32_t with_activation; -/// @brief Relu activation slope. -float activation_negative_slope; -/// @brief Defines shift in input buffers between adjacent calculations of output values. -cldnn_tensor_arr stride; - -CLDNN_END_PRIMITIVE_DESC(eltwise) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(eltwise); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/embed.h b/inference-engine/thirdparty/clDNN/api/C/embed.h deleted file mode 100644 index b1278227def208..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/embed.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -CLDNN_BEGIN_PRIMITIVE_DESC(embed) - -/// @brief Primitive id containing weights data. -cldnn_primitive_id weights; -/// @brief Primitive id containing bias data. -cldnn_primitive_id bias; - -CLDNN_END_PRIMITIVE_DESC(embed) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(embed); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/fully_connected.h b/inference-engine/thirdparty/clDNN/api/C/fully_connected.h deleted file mode 100644 index 2b46d9341c64aa..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/fully_connected.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs forward fully connected layer (inner product). -/// Also supports built-in Relu @CLDNN_PRIMITIVE_DESC{activation} available by setting it in arguments. -CLDNN_BEGIN_PRIMITIVE_DESC(fully_connected) -/// @brief Enable Relu activation. -uint32_t with_activation; -/// @brief Relu activation slope. -float activation_negative_slope; -/// @brief Primitive id containing weights data. -cldnn_primitive_id weights; -/// @brief Primitive id containing bias data. -cldnn_primitive_id bias; -/// @brief Primitive id containing weights quanitization factors per output feature map. -cldnn_primitive_id weights_quantization_factors; -/// @brief Primitive id containing output quanitization factors per output feature map. -cldnn_primitive_id output_calibration_factors; -/// @brief Input quantization factor -float input_quantization_factor; -/// @brief Output quantization factor -float output_quantization_factor; - -CLDNN_END_PRIMITIVE_DESC(fully_connected) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(fully_connected); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/fully_connected_grad_input.h b/inference-engine/thirdparty/clDNN/api/C/fully_connected_grad_input.h deleted file mode 100644 index a12ba53f3c6019..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/fully_connected_grad_input.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs backward fully connected layer (inner product) for input. -CLDNN_BEGIN_PRIMITIVE_DESC(fully_connected_grad_input) -/// @brief Primitive id containing weights data. -cldnn_primitive_id weights; -CLDNN_END_PRIMITIVE_DESC(fully_connected_grad_input) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(fully_connected_grad_input); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/fully_connected_grad_weights.h b/inference-engine/thirdparty/clDNN/api/C/fully_connected_grad_weights.h deleted file mode 100644 index 84ae61d8c22900..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/fully_connected_grad_weights.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs backward fully connected layer (inner product) for weights and biases. -CLDNN_BEGIN_PRIMITIVE_DESC(fully_connected_grad_weights) -/// @brief Primitive id containing weights data. -cldnn_primitive_id weights; -/// @brief Primitive id containing bias data. -cldnn_primitive_id bias; -/// @brief Primitive id containing fully connected gradient data. Used for proper order of gradient calculation. -/// Leave empty if primitive is last in backward pass. -cldnn_primitive_id fc_grad; -/// @brief Primitive id containing weight gradient calculated in previous iteration. Memory size should be same as weights. -cldnn_primitive_id prev_weights_grad; -/// @brief Primitive id containing bias gradient calculated in previous iteration. Memory size should be same as bias. -cldnn_primitive_id prev_bias_grad; -CLDNN_END_PRIMITIVE_DESC(fully_connected_grad_weights) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(fully_connected_grad_weights); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/gather.h b/inference-engine/thirdparty/clDNN/api/C/gather.h deleted file mode 100644 index 7bf2396ec82079..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/gather.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -// Copyright (c) 2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" - -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif -typedef enum { - cldnn_gather_along_b = 0, - cldnn_gather_along_f = CLDNN_TENSOR_BATCH_DIM_MAX, - cldnn_gather_along_x = CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX, - cldnn_gather_along_y = cldnn_gather_along_x + 1 -} cldnn_gather_axis; - -CLDNN_BEGIN_PRIMITIVE_DESC(gather) -/// @brief Gathering axis; -cldnn_gather_axis axis; -/// @brief Output shape -cldnn_tensor output_shape; -CLDNN_END_PRIMITIVE_DESC(gather) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(gather); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/gemm.h b/inference-engine/thirdparty/clDNN/api/C/gemm.h deleted file mode 100644 index d28a757232350e..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/gemm.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs forward attention layer. - -CLDNN_BEGIN_PRIMITIVE_DESC(gemm) -/// @brief Variable containing ALPHA parameter -float alpha; -/// @brief Variable containing BETA parameter -float beta; -/// @brief Flag for transposing first input matrix -bool transpose_input0; -/// @brief Flag for transposing second input matrix -bool transpose_input1; -CLDNN_END_PRIMITIVE_DESC(gemm) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(gemm); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/index_select.h b/inference-engine/thirdparty/clDNN/api/C/index_select.h deleted file mode 100644 index 3d687ed30f9cca..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/index_select.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" - -#include - -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Select index, which will be copied to the output.. -/// -/// @details Applies index selecting along specified dimension. The indices, which will be copied are specifed by -/// by @c indices. -/// @n -/// @n Example: -/// @n input_sizes = (1, 2, 4, 2) -/// @n input_values = (a, b, c, d) -/// @n (e, f, g, h) -/// @n indices_sizes = (1, 1, 6, 1) -/// @n indices_values = {0, 0, 1, 1, 3, 3} -/// @n For axis: along_x: -/// @n output_sizes = (1, 2, 6, 2) -/// @n output_values = (a, a, b, b, d, d) -/// @n (e, e, f, f, h, h) -/// @n -/// @n The resulting output will have sizes equal to input_size with changed concrete tensor size to inidices x size. -/// @n -/// @n@b Requirements: -/// @n - @c input must be a valid primitive_id, which output's format is bfyx/yxfb; -/// @n - @c indices must be a valid primitive_id, which output's layout is: (bfyx/yxfb, i32, {1, 1, indicies_size, 1}) -/// @n - @c axis - valid index_select_axis_name instance. -/// @n Breaking any of this conditions will cause exeption throw. -CLDNN_BEGIN_PRIMITIVE_DESC(index_select) - -/// @brief A list of axes of index selecting. -index_select_axis_name_arr axis; -/// @brief Number of axes of index selecting. -int axis_num; -/// @brief Do index_select in reverse order on axis. -bool reverse; - -CLDNN_END_PRIMITIVE_DESC(index_select) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(index_select); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/input_layout.h b/inference-engine/thirdparty/clDNN/api/C/input_layout.h deleted file mode 100644 index e750dc3ae8052d..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/input_layout.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Provides input layout for a data to be passed later to network. -/// @details This primitive allows to define the layout for input data -/// which will be passed to network before execution. -/// For example, network input images. -/// @note User should call network::set_input_data() for every @p input_layout primitive before network execution. -/// @sa network::set_input_data(), cldnn::data -CLDNN_BEGIN_PRIMITIVE_DESC(input_layout) -/// @brief Defines layout for the data will be passed to network. -cldnn_layout layout; -CLDNN_END_PRIMITIVE_DESC(input_layout) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(input_layout); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/lookup_table.h b/inference-engine/thirdparty/clDNN/api/C/lookup_table.h deleted file mode 100644 index 45fb500a376b4d..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/lookup_table.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Enum type to specify axis to return values from. -typedef enum { - cldnn_lookup_table_batch, - cldnn_lookup_table_feature, - cldnn_lookup_table_x, - cldnn_lookup_table_y, - cldnn_lookup_table_xyf -} cldnn_lookup_table_axis; - -/// @brief Returns values from data on which given indices are pointing at. -CLDNN_BEGIN_PRIMITIVE_DESC(lookup_table) -/// @brief Axis to return values from. If not set, returns data which index is pointing at in the flattened x, y, f dimensions for each batch. -cldnn_lookup_table_axis axis; -/// @brief Indicates that the primitive has user defined axis to return values from. -uint32_t with_axis; -CLDNN_END_PRIMITIVE_DESC(lookup_table) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(lookup_table); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/lrn.h b/inference-engine/thirdparty/clDNN/api/C/lrn.h deleted file mode 100644 index d57a7f01fe54df..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/lrn.h +++ /dev/null @@ -1,69 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum /*:int32_t*/ { - cldnn_lrn_norm_region_across_channel, - cldnn_lrn_norm_region_within_channel -} cldnn_lrn_norm_region; - -/// @brief Local response normalization -/// @details LRN layer as described in chapter 3.3 of "ImageNet Classification with Deep Convolutional -/// Neural Networks" by Khrizevsky, Sutskever, Hinton. @n See: http://www.cs.toronto.edu/~fritz/absps/imagenet.pdf -/// @par Alogrithm: -/// b(i,x,y) = a(i,x,y) / (k+alpha*sum(min(N-1, i+n/2); j=max(0,i-n/2); a(j,x,y)^2)) -/// @par Where: -/// @li b(i,x,y) : value at x, y from i-th feature map after normalization -/// @li a(i,x,y) : value at x, y from i-th feature map before normalization -/// @li N : number of feature maps -/// @li n : size of normalization -/// @li k, alpha, beta : hyper parameters (equal to 2, 10e-4, 0.75 in paper). -CLDNN_BEGIN_PRIMITIVE_DESC(lrn) -/// @brief Size of normalization. -uint32_t size; -/// @brief Hyper parameter "k". -float k; -/// @brief Hyper parameter "alpha". -float alpha; -/// @brief Hyper parameter "beta". -float beta; -/// @brief Normalize across or within channel -cldnn_lrn_norm_region norm_region; -CLDNN_END_PRIMITIVE_DESC(lrn) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(lrn); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/lstm.h b/inference-engine/thirdparty/clDNN/api/C/lstm.h deleted file mode 100644 index 433116216e2556..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/lstm.h +++ /dev/null @@ -1,147 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Weights orders -/// @details Specifies the order in which the weights are concatenated. -/// e.g. [i, o, f, z] : [input, output, forget, block] -/// ONNX order: iofz -/// Caffe order: ifoz -/// pyTorch order: izof -/// IE order: fizo -typedef enum /*:int32_t*/ { - cldnn_lstm_offset_order_iofz = 0, - cldnn_lstm_offset_order_ifoz, - cldnn_lstm_offset_order_izof, - cldnn_lstm_offset_order_fizo -} cldnn_lstm_offset_order; - -/// @brief LSTM Output selection -/// @details The current implementation allows the use to select the output -/// of an LSTM node by specifing any of the following options -typedef enum /*:int32_t*/ { - /// output the entire hidden sequence - cldnn_lstm_output_sequence = 0, - /// output just the last hidden value - cldnn_lstm_output_hidden, - /// output the last hidden and last cell values - cldnn_lstm_output_hidden_cell, - /// output the hidden sequence concatenated with the last cell - cldnn_lstm_output_sequence_cell -} cldnn_lstm_output; - -/// @brief Performs forward Long Short-Term Memory (LSTM) layer. -/// @details The current implementation of LSTM is described the following equations. -/// it = f(Xt*(Wi^T) + Ht-1*Ri + Wbi) -/// ft = f(Xt*(Wf^T) + Ht-1*Rf + Wbf) -/// ct = g(Xt*(Wc^T) + Ht-1*Rc + Wbc) -/// Ct = ft (.) Ct-1 + it (.) ct -/// ot = f(Xt*(Wo^T) + Ht-1*Ro + Wbo) -/// Ht = ot (.) h(Ct) -/// Where f = Sigmoid, g = Tanh, and h = Tanh. -CLDNN_BEGIN_PRIMITIVE_DESC(lstm) -/// @brief Array of primitive ids containing weight matrices for input, output, forget, and cell gates. -cldnn_primitive_id weights; -/// @brief Array of primitive ids containing recurrent weight matrices for input, output, forget, and cell gates. -cldnn_primitive_id recurrent; -/// @brief Array of primitive ids containing bias vectors for input, output, forget, and cell gates. -cldnn_primitive_id bias; -/// @brief Array of primitive ids containing the initial value of the hidden data (Ht-1). -cldnn_primitive_id initial_hidden; -/// @brief Array of primitive ids containing the initial value of the cell state data (Ct-1). -cldnn_primitive_id initial_cell; -/// @brief Array of primitive ids containing peephole weight vectors for input, output, and forget gates. -cldnn_primitive_id peepholes; -/// @brief Cell clip threshold T. It is applied to the input of activations [-T, T]. No clip is applied if it is not specified. -float clip; -/// @brief Couple the input and forget gates if input_forget is 1. Default is 0. -bool input_forget; -/// @brief A list of 3 activation functions for the input, output, forget, cell, and hidden. -cldnn_activation_func activations[3]; -/// @brief Optional scaling values used by some activation functions. The values are consumed in the order of activation functions. -cldnn_activation_additional_params activation_params[3]; -/// @brief Output selection. Default the entire hidden sequence is returned -cldnn_lstm_output output_selection; -/// @brief Weights, recurrent weights, and biases order. [iofz] : ONNX, [ifoz] : Caffe -cldnn_lstm_offset_order offset_order; -// NOT SUPPORTED YET -// uint32_t output_sequence; -CLDNN_END_PRIMITIVE_DESC(lstm) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(lstm); - -/// @brief LSTM Layer GEMM helper primitive. -/// @details The current helper primitive performs fused GEMM operations. -CLDNN_BEGIN_PRIMITIVE_DESC(lstm_gemm) -/// @brief Array of primitive ids containing weight matrices for input, output, forget, and cell gates. -cldnn_primitive_id weights; -/// @brief Array of primitive ids containing recurrent weight matrices for input, output, forget, and cell gates. -cldnn_primitive_id recurrent; -/// @brief Array of primitive ids containing bias vectors for input, output, forget, and cell gates. -cldnn_primitive_id bias; -/// @brief Array of primitive ids containing the initial value of the hidden data (Ht-1). -cldnn_primitive_id hidden; -/// @brief direction default = 0, bidirectional = 1. -uint32_t direction; -CLDNN_END_PRIMITIVE_DESC(lstm_gemm) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(lstm_gemm); - -/// @brief LSTM Layer element-wise helper primitive. -/// @details The current helper primitive performs fused element-wise operations. -CLDNN_BEGIN_PRIMITIVE_DESC(lstm_elt) -/// @brief Array of primitive ids containing the initial value of the cell state data (Ct-1). -cldnn_primitive_id cell; -/// @brief Cell clip threshold T. It is applied to the input of activations [-T, T]. No clip is applied if it is not specified. -float clip; -/// @brief Couple the input and forget gates if input_forget is 1. Default is 0. -bool input_forget; -/// @brief A list of 3 activation functions for the input, output, forget, cell, and hidden. -cldnn_activation_func activations[3]; -/// @brief Optional scaling values used by some activation functions. The values are consumed in the order of activation functions. -cldnn_activation_additional_params activation_params[3]; -/// @brief Weights, recurrent weights, and biases order. [iofz] : ONNX, [ifoz] : Caffe -cldnn_lstm_offset_order offset_order; -/// @brief direction default = 0, bidirectional = 1. -uint32_t direction; -// NOT SUPPORTED YET -// uint32_t output_sequence; -CLDNN_END_PRIMITIVE_DESC(lstm_elt) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(lstm_elt); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/lstm_dynamic.h b/inference-engine/thirdparty/clDNN/api/C/lstm_dynamic.h deleted file mode 100644 index 1de13faac26c9b..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/lstm_dynamic.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif -/// @brief Performs forward Long Short-Term Memory (LSTM_DYNAMIC) layer. -/// @details The current implementation of LSTM_DYNAMIC is described the following equations. -/// it = f(Xt*(Wi^T) + Ht-1*Ri + Wbi) -/// ft = f(Xt*(Wf^T) + Ht-1*Rf + Wbf) -/// ct = g(Xt*(Wc^T) + Ht-1*Rc + Wbc) -/// Ct = ft (.) Ct-1 + it (.) ct -/// ot = f(Xt*(Wo^T) + Ht-1*Ro + Wbo) -/// Ht = ot (.) h(Ct) -/// Where f = Sigmoid, g = Tanh, and h = Tanh. -CLDNN_BEGIN_PRIMITIVE_DESC(lstm_dynamic) -/// @brief Array of primitive ids containing weight matrices for input, output, forget, and cell gates. -cldnn_primitive_id weights; -/// @brief Array of primitive ids containing recurrent weight matrices for input, output, forget, and cell gates. -cldnn_primitive_id recurrent; -/// @brief Primitive Id of mutable data primitive pointing to buffer, which will be filled with last hidden state. -cldnn_primitive_id last_hidden_state; -/// @brief Primitive Id of mutable data primitive pointing to buffer, which will be filled with last cell state. -cldnn_primitive_id last_cell_state; -/// @brief Array of primitive ids containing bias vectors for input, output, forget, and cell gates. -cldnn_primitive_id bias; -/// @brief Array of primitive ids containing the initial value of the hidden data (Ht-1). -cldnn_primitive_id initial_hidden; -/// @brief Array of primitive ids containing the initial value of the cell state data (Ct-1). -cldnn_primitive_id initial_cell; -/// @brief Primitive id containing the dynamic sequence lengths. -cldnn_primitive_id dyn_length; -/// @brief Cell clip threshold T. It is applied to the input of activations [-T, T]. No clip is applied if it is not specified. -float clip; -/// @brief Couple the input and forget gates if input_forget is 1. Default is 0. -bool input_forget; -CLDNN_END_PRIMITIVE_DESC(lstm_dynamic) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(lstm_dynamic); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/max_unpooling.h b/inference-engine/thirdparty/clDNN/api/C/max_unpooling.h deleted file mode 100644 index a2ca824eda8040..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/max_unpooling.h +++ /dev/null @@ -1,58 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs "max_unpooling" operation. -/// @details Reverse operation of max pooling, based on the argmax data where indices of each max pooling region are stored. -CLDNN_BEGIN_PRIMITIVE_DESC(max_unpooling) -/// @brief Primitive id which contains indices of each max pooling region. Indices must be in flattened bfyx format with no padding. Needs to be fp32 data type. -cldnn_primitive_id argmax; -/// @brief Defines a shift, relative to (0,0) position of the input buffer, -/// where (0,0) point of the pooling window should start calculations. Used only for output size computation. -cldnn_tensor input_offset; -/// @brief Defines shift in input buffer between adjacent calculations of output values. Used only for output size computation. -cldnn_tensor stride; -/// @brief Pooling kernel size. Used only for output size computation. -cldnn_tensor size; -/// @brief Indicates that the primitive has user-defined output size (non-zero value). -uint32_t with_output_size; -/// @brief User-defined output data size of the primitive (w/o padding). -cldnn_tensor output_size; -CLDNN_END_PRIMITIVE_DESC(max_unpooling) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(max_unpooling); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/mutable_data.h b/inference-engine/thirdparty/clDNN/api/C/mutable_data.h deleted file mode 100644 index ac6f133500e95d..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/mutable_data.h +++ /dev/null @@ -1,60 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Enum type to specify function for weights filling. -typedef enum { - zero, - one, - xavier -} cldnn_filler_type; - -/// @brief Provides mutable data. -/// @details This primitive allows to pass data which can be written to during training. -/// For example, weights and biases for scoring networks. -/// This primitive can be also set as other primitive's output. In this case the underlying buffer will be the same in mutable_data and preceding primitive. -CLDNN_BEGIN_PRIMITIVE_DESC(mutable_data) -/// @brief Memory object which contains data. -/// @note If memory is attached by ::cldnn_attach_memory(), -/// attached buffer should be valid on ::cldnn_build_network() call. -cldnn_memory mem; -/// @brief Specifies function which will be used to fill data. -cldnn_filler_type fill_type; -CLDNN_END_PRIMITIVE_DESC(mutable_data) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(mutable_data); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/mvn.h b/inference-engine/thirdparty/clDNN/api/C/mvn.h deleted file mode 100644 index 3c7875ccfbd291..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/mvn.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Mean Variance Normalization primitive. -/// @details Normalizes the input to have 0-mean and/or unit (1) variance. - -CLDNN_BEGIN_PRIMITIVE_DESC(mvn) -/// @brief Determines if the normalization is done across or within channels. -uint32_t across_channels; -/// @brief Determines if normalize variance is applied. -uint32_t normalize_variance; -/// @brief Epsilon for not dividing by zero while normalizing. -float epsilon; -CLDNN_END_PRIMITIVE_DESC(mvn) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(mvn); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/normalize.h b/inference-engine/thirdparty/clDNN/api/C/normalize.h deleted file mode 100644 index 2e4a2a3ead2fcf..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/normalize.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Normalizes the input using an L2 norm and multiplies the output with scale value. -/// The scale can be equal for all channels or one scale per channel. -/// @details The L2 norm is computed as:
-/// Across spatial mode (across_spatial=true)-
-/// norm(i,x,y) = sqrt( Σ( in(f,w,h)^2 ) + epsilon ) where f in range (0,num_of_features), w in range (0,input_width), h in range (0,input_height).
-/// The summation is performed over all the pixels in the batch.
-/// Within spatial mode (across_spatial=false)-
-/// norm(i,x,y) = sqrt( Σ( in(f,x,y)^2 ) + epsilon ) where f in range (0,num_of_features).
-/// The summation is performed over this (x,y) position on all the features.
-/// @par Algorithm: -/// out(i,x,y) = ( in(i,x,y) / norm(i,x,y) ) * scale(i) -/// @par Where: -/// @li out(i,x,y) : value at x, y from i-th feature map after normalization. -/// @li in(i,x,y) : value at x, y from i-th feature map before normalization. -/// @li norm(i,x,y) : L2 norm as described above. -/// @li scale(i) : the scale value of the i-th feature map. -CLDNN_BEGIN_PRIMITIVE_DESC(normalize) -/// @brief Scale input primitive id with values needed for scaling after the normalization. -/// Scale x dimension should be 1 (if all channels have the same scale) or equal to input feature size (one scale per channel). -/// All other dimensions should be 1. -cldnn_primitive_id scale_input; -/// @brief Determines if the normalization is done across or within spatial (see documentation above). -uint32_t across_spatial; -/// @brief Epsilon for not dividing by zero while normalizing. -float epsilon; -CLDNN_END_PRIMITIVE_DESC(normalize) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(normalize); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/one_hot.h b/inference-engine/thirdparty/clDNN/api/C/one_hot.h deleted file mode 100644 index 479d33018a6fcb..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/one_hot.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Creates a one-hot encoding of the input. -/// @details Creates a one-hot encoding of the input, putting the new one-hot axis in the position -/// @n specified by the @p one_hot_axis input, using the @p shape tensor as size reference. -/// @n The size of @p shape must be appropriate for adding a one-hot axis to input. For example, -/// @n input_sizes = (1, in_f, in_y, in_x) -/// @n expanded with -/// @n one_hot_axis = 2 -/// @n would insert the one-hot axis in the Y dimension, requiring -/// @n shape = (in_f, in_y, one-hot_limit, in_x) -/// @n The output values would then be determined by input as -/// @n output[f, y, i, x] = (input[0, f, y, x] == i) ? 1 : 0; -/// @n Since determining whether the input is appropriate (that the one-hot axis -/// @n has enough space to fully encode all inputs) requires scanning the whole -/// @n input, the primitive doesn't check for that, instead producing all-zeros -/// @n output axes for inputs below 0 and greater than the limit set by -/// @n @p shape. -/// @n -/// @n\b Requirements -/// @n - @p one_hot_axis must be within (inclusive) range 0 - 3. -/// @n - @p shape must fit input sizes (see example above). -/// @n - input batch size must be equal to 1. -/// @n -/// @n Breaking any of this conditions will cause exception throw. -CLDNN_BEGIN_PRIMITIVE_DESC(one_hot) -/// @brief Output size reference. -cldnn_tensor shape; -/// @brief One-hot axis position in output shape (0-based, from left to right). -uint16_t one_hot_axis; -/// @brief The locations represented by indices in input take this value. -float on_value; -/// @brief The locations not represented by indices in input take this value. -float off_value; -CLDNN_END_PRIMITIVE_DESC(one_hot) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(one_hot); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/permute.h b/inference-engine/thirdparty/clDNN/api/C/permute.h deleted file mode 100644 index c531b5ea23130a..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/permute.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Permutes data in the memory, with respect to provided order. -/// @details Permute order is set as vector with positions meaning corresponding to tensor. -/// Vector values represent dimensions to be permuted in bfyx format. For example:
-/// input_dimensions = tensor{ 5, 3, 6, 3 }
-/// permute_order = { 2, 3, 1, 0 }
-/// output_dimensions = { 6, 3, 3, 5 }
-///
-/// When permute_order is { 0, 1, 2, 3 } then input_dimensions = output_dimensions -CLDNN_BEGIN_PRIMITIVE_DESC(permute) -/// @brief Array of permuted output order in bfyx format. -cldnn_uint16_t_arr permute_order; -CLDNN_END_PRIMITIVE_DESC(permute) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(permute); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/pooling.h b/inference-engine/thirdparty/clDNN/api/C/pooling.h deleted file mode 100644 index 19aaa57f0058df..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/pooling.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -// Copyright (c) 2016-2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Select method for Pooling layer ( @CLDNN_PRIMITIVE_DESC{pooling} ). -typedef enum /*:int32_t*/ { - /// @brief Maximum-pooling method. - cldnn_pooling_max, - /// @brief Average-pooling method. - cldnn_pooling_average, - /// @brief Average-pooling method without values which are outside of the input. - cldnn_pooling_average_no_padding, - /// @brief Maximum-pooling method with additional buffer to store argmax indices. - cldnn_pooling_max_with_argmax, - /// @brief Pooling with bilinear interpolation - cldnn_pooling_bilinear, - /// @brief Deformable pooling with bilinear interpolation - cldnn_pooling_deformable_bilinear -} cldnn_pooling_mode; - -/// @brief Performs "pooling" operation which is a form of non-linear down-sampling. -/// @details Pools the input image by taking the max, average, etc. within regions. -CLDNN_BEGIN_PRIMITIVE_DESC(pooling) -/// @brief Primitive id which contains indices of each max pooling region. Indices must be in flattened bfyx format with no padding. Needs to be fp32 data type. -cldnn_primitive_id argmax; -/// @brief Pooling method. See #cldnn_pooling_mode. -int32_t mode; -/// @brief Global pooling (kernel size is equal to the spatial dimension of input tensor) -int8_t global_pooling; -/// @brief Defines a shift, relative to (0,0) position of the input buffer, where (0,0) point of the pooling window should start calculations. -cldnn_tensor input_offset; -/// @brief Defines shift in input buffer between adjacent calculations of output values. -cldnn_tensor stride; -/// @brief Pooling kernel size. -cldnn_tensor size; -/// @brief Indicates that the primitive has user-defined output size (non-zero value). -uint32_t with_output_size; -/// @brief User-defined output data size of the primitive (w/o padding). -cldnn_tensor output_size; -CLDNN_END_PRIMITIVE_DESC(pooling) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(pooling); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/prior_box.h b/inference-engine/thirdparty/clDNN/api/C/prior_box.h deleted file mode 100644 index 552b0ecf2f7dba..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/prior_box.h +++ /dev/null @@ -1,69 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Generates a set of default bounding boxes with different sizes and aspect ratios. -/// @details The prior-boxes are shared across all the images in a batch (since they have the same width and height). -/// First feature stores the mean of each prior coordinate. -/// Second feature stores the variance of each prior coordinate. -CLDNN_BEGIN_PRIMITIVE_DESC(prior_box) -/// @brief Image width and height. -cldnn_tensor img_size; -/// @brief Minimum box sizes in pixels. -cldnn_float_arr min_sizes; -/// @brief Maximum box sizes in pixels. -cldnn_float_arr max_sizes; -/// @brief Various of aspect ratios. Duplicate ratios will be ignored. -cldnn_float_arr aspect_ratios; -/// @brief If not 0, will flip each aspect ratio. For example, if there is aspect ratio "r", aspect ratio "1.0/r" we will generated as well. -uint32_t flip; -/// @brief If not 0, will clip the prior so that it is within [0, 1]. -uint32_t clip; -/// @brief Variance for adjusting the prior boxes. -cldnn_float_arr variance; -/// @brief Step width. -float step_width; -/// @brief Step height. -float step_height; -/// @brief Offset to the top left corner of each cell. -float offset; -/// @broef If false, only first min_size is scaled by aspect_ratios -uint32_t scale_all_sizes; -CLDNN_END_PRIMITIVE_DESC(prior_box) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(prior_box); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/proposal.h b/inference-engine/thirdparty/clDNN/api/C/proposal.h deleted file mode 100644 index 745027fa9c28eb..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/proposal.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -// Copyright (c) 2017-2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -#define CLDNN_ROI_VECTOR_SIZE 5 - -CLDNN_BEGIN_PRIMITIVE_DESC(proposal) -int max_proposals; -float iou_threshold; -int base_bbox_size; -int min_bbox_size; -int feature_stride; -int pre_nms_topn; -int post_nms_topn; -cldnn_float_arr ratios; -cldnn_float_arr scales; -float coordinates_offset; -float box_coordinate_scale; -float box_size_scale; -uint32_t swap_xy; -uint32_t initial_clip; -uint32_t clip_before_nms; -uint32_t clip_after_nms; -uint32_t round_ratios; -uint32_t shift_anchors; -uint32_t normalize; -uint32_t for_deformable; -CLDNN_END_PRIMITIVE_DESC(proposal) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(proposal); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/reduce.h b/inference-engine/thirdparty/clDNN/api/C/reduce.h deleted file mode 100644 index 13d3436e0bd0c0..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/reduce.h +++ /dev/null @@ -1,87 +0,0 @@ -/* -// Copyright (c) 2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" - -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - cldnn_reduce_along_b = 0, - cldnn_reduce_along_f = CLDNN_TENSOR_BATCH_DIM_MAX, - cldnn_reduce_along_x = CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX, - cldnn_reduce_along_y = cldnn_reduce_along_x + 1, - cldnn_reduce_along_z = cldnn_reduce_along_y + 1, - cldnn_reduce_along_w = cldnn_reduce_along_z + 1 -} cldnn_reduce_axis; - -// @brief Select mode for reduce layer ( @CLDNN_PRIMITIVE_DESC{reduce} ​). -typedef enum { - /// @brief Reduce max - cldnn_reduce_max, - /// @brief Reduce min - cldnn_reduce_min, - /// @brief Reduce mean - cldnn_reduce_mean, - /// @brief Reduce prod - cldnn_reduce_prod, - /// @brief Reduce sum - cldnn_reduce_sum, - /// @brief Reduce and - cldnn_reduce_and, - /// @brief Reduce or - cldnn_reduce_or, - /// @brief Reduce sum square - cldnn_reduce_sum_square, - /// @brief Reduce l1 - cldnn_reduce_l1, - /// @brief Reduce l2 - cldnn_reduce_l2, - /// @brief Reduce log sum - cldnn_reduce_log_sum, - /// @brief Reduce log sum exp - cldnn_reduce_log_sum_exp -} cldnn_reduce_mode; - -CLDNN_BEGIN_PRIMITIVE_DESC(reduce) -/// @brief Keep the reduced dimension or not, 1 mean keep reduced dimension -int32_t keep_dims; -/// @brief Reduce operation type -int32_t mode; -/// @brief List of axes to reduce -cldnn_uint16_t_arr axes; -CLDNN_END_PRIMITIVE_DESC(reduce) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(reduce); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/region_yolo.h b/inference-engine/thirdparty/clDNN/api/C/region_yolo.h deleted file mode 100644 index 883f5dafe339a7..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/region_yolo.h +++ /dev/null @@ -1,59 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief region softmax specific for yolo2 topology -/// @details -/// @par Algorithm: -/// -/// @par Where: -/// -CLDNN_BEGIN_PRIMITIVE_DESC(region_yolo) -/// @brief paramter coords -uint32_t coords; -/// @brief paramter classes -uint32_t classes; -/// @brief Number of anchors -uint32_t num; -/// @brief Apply softmax after logistic -uint32_t do_softmax; -/// @brief Number of really used anchors -uint32_t mask_size; -CLDNN_END_PRIMITIVE_DESC(region_yolo) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(region_yolo); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/reorder.h b/inference-engine/thirdparty/clDNN/api/C/reorder.h deleted file mode 100644 index 63204fe6d8fddb..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/reorder.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Changes how data is ordered in memory. Value type is not changed & all information is preserved. -/// @details Corresponding values are bitwise equal before/after reorder. -/// Also merged with subtraction layer, which can subtract, multiply or divide values based on mean_mode value, while doing reordering. -/// NOTE THAT THIS WILL SUBTRACT THE SAME VALUES FROM EACH BATCH. -CLDNN_BEGIN_PRIMITIVE_DESC(reorder) -/// @brief Requested memory format. -cldnn_format_type output_format; -/// @brief Primitive id to get mean subtract values. Ignored if subtract_per_featrue is set. -cldnn_primitive_id mean_subtract; -/// @brief Array of mean subtract values. -cldnn_float_arr subtract_per_feature; -/// @brief Mode of mean execution -cldnn_reorder_mean_mode mean_mode; -CLDNN_END_PRIMITIVE_DESC(reorder) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(reorder); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/reorg_yolo.h b/inference-engine/thirdparty/clDNN/api/C/reorg_yolo.h deleted file mode 100644 index 0ec3f3687ea888..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/reorg_yolo.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief yolo2 topology specific data reorganization primitive -/// @details -/// @par Algorithm: -/// -/// @par Where: -/// -CLDNN_BEGIN_PRIMITIVE_DESC(reorg_yolo) -/// @brief paramter stride -uint32_t stride; - -CLDNN_END_PRIMITIVE_DESC(reorg_yolo) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(reorg_yolo); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/reshape.h b/inference-engine/thirdparty/clDNN/api/C/reshape.h deleted file mode 100644 index 5218654088f212..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/reshape.h +++ /dev/null @@ -1,50 +0,0 @@ -/* -// Copyright (c) 2017 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Changes information about inputs's layout effectively creating new memory which share underlaying buffer -/// but is interpreted in a different way (different shape). -/// @note reshape primitive is supposed only to reinterpret shape of the memory therefore it's not possible to change -/// neither data type nor format of the input buffer and total number of elements in input and output (excluding paddings) must match. -/// Please note that there is no guarantee that underlying data will be in proper format if primitive was explicitly added to output list. -CLDNN_BEGIN_PRIMITIVE_DESC(reshape) -/// @brief Requested memory shape. -cldnn_tensor output_shape; -CLDNN_END_PRIMITIVE_DESC(reshape) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(reshape); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/reverse_sequence.h b/inference-engine/thirdparty/clDNN/api/C/reverse_sequence.h deleted file mode 100644 index bea2d21e95a94a..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/reverse_sequence.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -// Copyright (c) 2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" - -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -CLDNN_BEGIN_PRIMITIVE_DESC(reverse_sequence) -/// @brief The axis which is partially reversed. -int32_t seq_axis; -/// @brief The axis along which reversal is performed. -int32_t batch_axis; -CLDNN_END_PRIMITIVE_DESC(reverse_sequence) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(reverse_sequence); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/roi_pooling.h b/inference-engine/thirdparty/clDNN/api/C/roi_pooling.h deleted file mode 100644 index 6d6667fa5e368e..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/roi_pooling.h +++ /dev/null @@ -1,68 +0,0 @@ -/* -// Copyright (c) 2017 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -CLDNN_BEGIN_PRIMITIVE_DESC(roi_pooling) -/// @brief Pooling method. See #cldnn_pooling_mode. -int32_t mode; -/// @brief True, if pooling is position sensitive (PSROIPoolng). -bool position_sensitive; -/// @brief Output width. -int pooled_width; -/// @brief Output height. -int pooled_height; -/// @brief Count of sub bins in x spatial dimension. -int spatial_bins_x; -/// @brief Count of sub bins in y spatial dimension. -int spatial_bins_y; -/// @brief Output features count (applied for position sensitive case only). -int output_dim; -/// @brief Transformation parameter. -float trans_std; -/// @brief False, if pooling is deformable (DeformablePSROIPoolng). -bool no_trans; -/// @brief Ratio of the coordinates used in RoIs to the width (and height) of the input data. -float spatial_scale; -/// @brief Size of pooled part. -int part_size; -/// @brief Size of pooled group. -int group_size; -CLDNN_END_PRIMITIVE_DESC(roi_pooling) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(roi_pooling); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/scale.h b/inference-engine/thirdparty/clDNN/api/C/scale.h deleted file mode 100644 index 7cc65fdc547169..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/scale.h +++ /dev/null @@ -1,58 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs elementwise product of input and scale_input. -/// @details Scale input dimension should be equal to input dimension or be 1 if it is not there.
-/// Input size : 2x3x4x5(BFYX)
-/// Possible scale inputs sizes :
-/// 2x3x4x5 - works the same as(axis == 0 == -4) in caffe
-/// 1x3x4x5 - works the same as(axis == 1 == -3) in caffe
-/// 1x1x4x5 - works the same as(axis == 2 == -2) in caffe
-/// 1x1x1x5 - works the same as(axis == 3 == -1) in caffe
-/// 1x1x1x1 - works the same as empty shape(scalar) in caffe
-/// When scale_input is the same as input, the behavior is the same as @CLDNN_PRIMITIVE_DESC{eltwise} with product operation.
-/// Performs scale over feature when the scale feature size is equal to input feature size.
-/// Performs scale over feature in batch when the scale feature and scale batch sizes are equal to input feature and input batch sizes.
-/// Optionally it can also add provided biases by setting bias_term.
-CLDNN_BEGIN_PRIMITIVE_DESC(scale) -/// @brief Primitive id containing bias data. -cldnn_primitive_id bias; -CLDNN_END_PRIMITIVE_DESC(scale) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(scale); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/scale_grad_input.h b/inference-engine/thirdparty/clDNN/api/C/scale_grad_input.h deleted file mode 100644 index 694d2eb23fb693..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/scale_grad_input.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs scale primitive backward for input. -CLDNN_BEGIN_PRIMITIVE_DESC(scale_grad_input) - -CLDNN_END_PRIMITIVE_DESC(scale_grad_input) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(scale_grad_input); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/scale_grad_weights.h b/inference-engine/thirdparty/clDNN/api/C/scale_grad_weights.h deleted file mode 100644 index 060f0951bf3994..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/scale_grad_weights.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs scale layer backward for scale_input and biases. -CLDNN_BEGIN_PRIMITIVE_DESC(scale_grad_weights) -/// @brief Scale input primitive id. -cldnn_primitive_id scale_input; -/// @brief Primitive id containing bias data. -cldnn_primitive_id bias; -/// @brief Primitive id containing scale gradient data calculated in previous iteration. -cldnn_primitive_id prev_scale_grad; -/// @brief Primitive id containing bias gradient data calculated in previous iteration. -cldnn_primitive_id prev_bias_grad; -/// @brief Primitive id which uses weights and biases updated in this primitive. -cldnn_primitive_id scale_grad; -CLDNN_END_PRIMITIVE_DESC(scale_grad_weights) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(scale_grad_weights); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/select.h b/inference-engine/thirdparty/clDNN/api/C/select.h deleted file mode 100644 index f50eacb92d4235..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/select.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs elementwise select operation on two input primitives with selector primitive (mask) -/// @notes -/// - both inputs have to have equal sizes in all dimensions -/// - format of both inputs has to be the same -/// - mask primitive input have to have equal size in all dimensions with inputs -CLDNN_BEGIN_PRIMITIVE_DESC(select) - -CLDNN_END_PRIMITIVE_DESC(select) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(select); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/shuffle_channels.h b/inference-engine/thirdparty/clDNN/api/C/shuffle_channels.h deleted file mode 100644 index bd0887be8b2e7b..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/shuffle_channels.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -// Copyright (c) 2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" - -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -CLDNN_BEGIN_PRIMITIVE_DESC(shuffle_channels) -/// @brief The number of groups to split the channel dimension. This number must evenly divide the channel dimension size. -int32_t group; -/// @brief The index of the channel dimension (default is 1). -int32_t axis; -CLDNN_END_PRIMITIVE_DESC(shuffle_channels) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(shuffle_channels); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/softmax.h b/inference-engine/thirdparty/clDNN/api/C/softmax.h deleted file mode 100644 index 042a876d33d8e2..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/softmax.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -// Copyright (c) 2016-2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Enum type to specify softmax's normalization scope (see cldnn_softmax_desc::dimension). -typedef enum { - cldnn_softmax_normalize_f, - cldnn_softmax_normalize_x, - cldnn_softmax_normalize_y, - cldnn_softmax_normalize_z, - cldnn_softmax_normalize_fyx, - cldnn_softmax_normalize_all, -} cldnn_softmax_dimension; - -/// @brief Normalizes results so they sum to 1. The scope of normalization is defined by a member @p dimension. -/// @details -/// @par Algorithm: -/// b = e^a/sum(N-1; j=0; e^j) -/// @par Where: -/// @li N : number of values to normalize -/// @li b : value after normalization -/// @li a : value before normalization -CLDNN_BEGIN_PRIMITIVE_DESC(softmax) -/// @brief Defines a scope of a single softmax normalization. -/// @details -/// Being given a 4-dimensional input, which consists of b,f,y,x dimensions, softmax normalizes data which are divided into multiple independent sets. -/// Specific behavior is determined by this parameter, as follows: -/// - when set to @link cldnn_softmax_dimension cldnn_softmax_normalize_x @endlink each input row is normalized independently, -/// - when set to @link cldnn_softmax_dimension cldnn_softmax_normalize_y @endlink each input column is normalized independently, -/// - when set to @link cldnn_softmax_dimension cldnn_softmax_normalize_f @endlink each in-depth vector of input is normalized independently, -/// - when set to @link cldnn_softmax_dimension cldnn_softmax_normalize_fyx @endlink each 3d image within input is normalized independently, -/// - when set to @link cldnn_softmax_dimension cldnn_softmax_normalize_all @endlink everything is normalized, -cldnn_softmax_dimension dimension; -CLDNN_END_PRIMITIVE_DESC(softmax) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(softmax); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/softmax_loss_grad.h b/inference-engine/thirdparty/clDNN/api/C/softmax_loss_grad.h deleted file mode 100644 index b982c62e1f1a4e..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/softmax_loss_grad.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Backward pass for Softmax log loss. -/// @details The output values are the same as input_prob, except for the correct one based on the label which is subtracted by 1. -CLDNN_BEGIN_PRIMITIVE_DESC(softmax_loss_grad) - -CLDNN_END_PRIMITIVE_DESC(softmax_loss_grad) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(softmax_loss_grad); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/split.h b/inference-engine/thirdparty/clDNN/api/C/split.h deleted file mode 100644 index 144f8fd892d61c..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/split.h +++ /dev/null @@ -1,68 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Performs split operation on input. -/// @details splits the input data into n parts, for each user provides name and offsets. -/// @n User cannot use split primitive directly. -/// @n It is needed to refer to the output ids with the name ":". -/// @n -/// @n\b Assumptions -/// @n - offsets1 < offsets2 < offsets3 < ... -/// @n - size[n] = offsets[n+1] - offsets[n]; -/// @n - last element: size[n] = split_input.size - offsets[n]; -/// @n - no buffer overlapping, as the output size is calculated using offset and input size -/// @n - split primitive id cannot be used by any other primitive (user needs to use output_ids only) -/// @n Breaking any of this conditions will cause exeption throw. -/// @n -/// @n\b Example: -/// @n Splitting output to 2 parts by the features: -/// @n input_size = { 2, 4, 3, 5 }; -/// @n split_id = "split"; -/// @n output_ids_offsets[0] = { "out0", { 0,0,0,0 } }; -/// @n output_ids_offsets[1] = { "out1", { 0,2,0,0 } }; -/// @n After split there would be 2 primitives: "split:out0" and "split:out1" which contain 2 feature maps (lower and upper) - -CLDNN_BEGIN_PRIMITIVE_DESC(split) -/// @brief List of output_ids. -cldnn_primitive_id_arr output_ids; -/// @brief Array of tensors with offsets. -cldnn_tensor_arr output_offsets; -CLDNN_END_PRIMITIVE_DESC(split) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(split); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/strided_slice.h b/inference-engine/thirdparty/clDNN/api/C/strided_slice.h deleted file mode 100644 index 33218cf2edf3ab..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/strided_slice.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -// Copyright (c) 2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" - -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -CLDNN_BEGIN_PRIMITIVE_DESC(strided_slice) -/// @brief Array of bits, that provide replace begin[i] to max possible range in that dimension. -cldnn_uint8_t_arr begin_mask; -/// @brief Array of bits, that provide replace end[i] to max possible range in that dimension. -cldnn_uint8_t_arr end_mask; -/// @brief Array of bits, that provide adding a new length 1 dimension at ith position in the output tensor. -cldnn_uint8_t_arr new_axis_mask; -/// @brief Array of bits, that provide shrinks the dimensionality by 1, taking on the value at index begin[i]. -cldnn_uint8_t_arr shrink_axis_mask; -CLDNN_END_PRIMITIVE_DESC(strided_slice) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(strided_slice); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/tile.h b/inference-engine/thirdparty/clDNN/api/C/tile.h deleted file mode 100644 index 21d3d0ed765658..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/tile.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - cldnn_tile_along_b = 0, - cldnn_tile_along_f = CLDNN_TENSOR_BATCH_DIM_MAX, - cldnn_tile_along_x = CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX, - cldnn_tile_along_y = cldnn_tile_along_x + 1, - cldnn_tile_along_z = cldnn_tile_along_y + 1 -} cldnn_tile_axis; - -CLDNN_BEGIN_PRIMITIVE_DESC(tile) -/// @brief Tiling axis -cldnn_tile_axis axis; -/// @brief Tiles number across an axis -int tiles; -CLDNN_END_PRIMITIVE_DESC(tile) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(tile); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/C/upsampling.h b/inference-engine/thirdparty/clDNN/api/C/upsampling.h deleted file mode 100644 index 87727efdf05415..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/C/upsampling.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn.h" -/// @addtogroup c_api C API -/// @{ -/// @addtogroup c_topology Network Topology -/// @{ -/// @addtogroup c_primitives Primitives -/// @{ - -#ifdef __cplusplus -extern "C" { -#endif - -/// @brief Sample mode for upsampling layer ( @CLDNN_PRIMITIVE_DESC{upsampling} ​). -typedef enum /*:int32_t*/ { - /// @brief upsampling nearest neighbor. - cldnn_upsampling_nearest, - /// @brief upsampling bilinear. - cldnn_upsampling_bilinear, -} cldnn_upsampling_sample_type; - -/// @brief Performs nearest neighbor/bilinear upsampling -/// Also supports built-in Relu @ref activation available by setting it in arguments. -CLDNN_BEGIN_PRIMITIVE_DESC(upsampling) -/// @param scale Upsampling scale. -float scale; -/// @param num_filter Input filter. Only used by bilinear sample_type. -uint32_t num_filter; -/// @param sample_type Upsampling method (nearest neighbor/bilinear). -int32_t sample_type; /*cldnn_sample_type*/ -/// @brief Enables Relu activation. -uint32_t with_activation; -/// @brief Relu activation slope. -float activation_negative_slope; -CLDNN_END_PRIMITIVE_DESC(upsampling) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(upsampling); - -#ifdef __cplusplus -} -#endif - -/// @} -/// @} -/// @} - diff --git a/inference-engine/thirdparty/clDNN/api/CPP/compounds.h b/inference-engine/thirdparty/clDNN/api/CPP/compounds.h deleted file mode 100644 index bc05b8dff07964..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/CPP/compounds.h +++ /dev/null @@ -1,231 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "meta_utils.hpp" - -namespace cldnn { - -/// @addtogroup cpp_api C++ API -/// @{ - -/// @cond CPP_HELPERS - -/// @defgroup cpp_helpers Helpers -/// @{ - -template -class mutable_array_ref { -public: - typedef size_t size_type; - - mutable_array_ref() : _data(nullptr), _size(0) {} - explicit mutable_array_ref(T& val) : _data(&val), _size(1) {} - mutable_array_ref(T* data, size_t size) : _data(data), _size(size) {} - - template - explicit mutable_array_ref(T (&arr)[N]) : _data(arr), _size(N) {} - - mutable_array_ref(const mutable_array_ref& other) : _data(other._data), _size(other._size) {} - - mutable_array_ref& operator=(const mutable_array_ref& other) { - if (this == &other) - return *this; - _data = other._data; - _size = other._size; - return *this; - } - - T* data() const { return _data; } - size_t size() const { return _size; } - bool empty() const { return _size == 0; } - -#if defined(_SECURE_SCL) && (_SECURE_SCL > 0) - typedef stdext::checked_array_iterator iterator; - typedef stdext::checked_array_iterator const_iterator; - iterator begin() const { return stdext::make_checked_array_iterator(_data, _size); } - iterator end() const { return stdext::make_checked_array_iterator(_data, _size, _size); } - const_iterator cbegin() const { return stdext::make_checked_array_iterator(_data, _size); } - const_iterator cend() const { return stdext::make_checked_array_iterator(_data, _size, _size); } -#else - typedef T* iterator; - typedef T* const_iterator; - iterator begin() const { return _data; } - iterator end() const { return _data + _size; } - const_iterator cbegin() const { return _data; } - const_iterator cend() const { return _data + _size; } -#endif - - T& operator[](size_t idx) const { - assert(idx < _size); - return _data[idx]; - } - - T& at(size_t idx) const { - if (idx >= _size) throw std::out_of_range("idx"); - return _data[idx]; - } - - std::vector vector() const { return std::vector(_data, _data + _size); } - -private: - T* _data; - size_t _size; -}; - -template -class array_ref { -public: - typedef size_t size_type; - - array_ref() : _data(nullptr), _size(0) {} - explicit array_ref(const T& val) : _data(&val), _size(1) {} - array_ref(const T* data, size_t size) : _data(data), _size(size) {} - - template - explicit array_ref(const std::vector& vec) : _data(vec.data()), _size(vec.size()) {} - - template - explicit array_ref(const T (&arr)[N]) : _data(arr), _size(N) {} - - explicit array_ref(const mutable_array_ref& other) : _data(other.data()), _size(other.size()) {} - - array_ref(const array_ref& other) : _data(other._data), _size(other._size) {} - - array_ref& operator=(const array_ref& other) { - if (this == &other) - return *this; - _data = other._data; - _size = other._size; - return *this; - } - - const T* data() const { return _data; } - size_t size() const { return _size; } - bool empty() const { return _size == 0; } - -#if defined(_SECURE_SCL) && (_SECURE_SCL > 0) - typedef stdext::checked_array_iterator iterator; - typedef stdext::checked_array_iterator const_iterator; - iterator begin() const { return stdext::make_checked_array_iterator(_data, _size); } - iterator end() const { return stdext::make_checked_array_iterator(_data, _size, _size); } - const_iterator cbegin() const { return stdext::make_checked_array_iterator(_data, _size); } - const_iterator cend() const { return stdext::make_checked_array_iterator(_data, _size, _size); } -#else - typedef const T* iterator; - typedef const T* const_iterator; - iterator begin() const { return _data; } - iterator end() const { return _data + _size; } - const_iterator cbegin() const { return _data; } - const_iterator cend() const { return _data + _size; } -#endif - - const T& operator[](size_t idx) const { - assert(idx < _size); - return _data[idx]; - } - - const T& at(size_t idx) const { - if (idx >= _size) throw std::out_of_range("idx"); - return _data[idx]; - } - - std::vector vector() const { return std::vector(_data, _data + _size); } - -private: - const T* _data; - size_t _size; -}; - -// NOTE: It seems that clang before version 3.9 has bug that treates non-member template function with deleted function -// body as non-template or non-specializable (specializations are treated as redefinitions). -// template size_t basic_strlen(const Char* str) = delete; -template -size_t basic_strlen(const Char*) { - static_assert(meta::always_false::value, "basic_strlen for selected Char type is deleted."); - return 0; -} - -template <> -inline size_t basic_strlen(const char* str) { return std::strlen(str); } - -template <> -inline size_t basic_strlen(const wchar_t* str) { return std::wcslen(str); } - -template -class basic_string_ref { -public: - typedef const Char* iterator; - typedef const Char* const_iterator; - typedef size_t size_type; - -private: - const Char* _data; - size_t _size; - -public: - basic_string_ref() : _data(nullptr), _size(0) {} - explicit basic_string_ref(const Char* str) : _data(str), _size(basic_strlen(str)) {} - - template - explicit basic_string_ref(const std::basic_string& str) : _data(str.c_str()), _size(str.size()) {} - - basic_string_ref(const basic_string_ref& other) : _data(other._data), _size(other._size) {} - - basic_string_ref& operator=(const basic_string_ref& other) { - if (this == &other) - return *this; - _data = other._data; - _size = other._size; - return *this; - } - - const Char* data() const { return _data; } - const Char* c_str() const { return _data; } - size_t size() const { return _size; } - size_t length() const { return _size; } - bool empty() const { return _size == 0; } - - iterator begin() const { return _data; } - iterator end() const { return _data + _size; } - const_iterator cbegin() const { return begin(); } - const_iterator cend() const { return end(); } - - const Char& operator[](size_t idx) { - assert(idx < _size); - return _data[idx]; - } - - std::basic_string str() const { return std::basic_string(_data, _size); } - operator std::basic_string() const { return str(); } -}; - -typedef basic_string_ref string_ref; -typedef basic_string_ref wstring_ref; - -/// @} - -/// @endcond - -/// @} -} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/event.hpp b/inference-engine/thirdparty/clDNN/api/CPP/event.hpp deleted file mode 100644 index 81b863cad657ad..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/CPP/event.hpp +++ /dev/null @@ -1,132 +0,0 @@ -/* -// Copyright (c) 2016-2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn_defs.h" -#include "engine.hpp" -#include "profiling.hpp" -#include -#include -#include -#include - -namespace cldnn { - -/// @addtogroup cpp_api C++ API -/// @{ - -/// @addtogroup cpp_event Events Support -/// @{ - -/// @brief Represents an clDNN Event object -struct event { - /// @brief Create an event which can be set to 'completed' by user. - static event create_user_event(const engine& engine, uint16_t stream_id) { - event status = (event) check_status("create user event failed", [&](status_t* status) { - return cldnn_create_user_event(engine.get(), stream_id, status); - }); - return status; - } - - /// @brief Construct from C API handler @ref ::cldnn_event. - explicit event(cldnn_event impl) : _impl(impl) { - if (_impl == nullptr) throw std::invalid_argument("implementation pointer should not be null"); - } - - event(const event& other) : _impl(other._impl) { - retain(); - } - - event& operator=(const event& other) { - if (_impl == other._impl) return *this; - release(); - _impl = other._impl; - retain(); - return *this; - } - - ~event() { - release(); - } - - friend bool operator==(const event& lhs, const event& rhs) { return lhs._impl == rhs._impl; } - friend bool operator!=(const event& lhs, const event& rhs) { return !(lhs == rhs); } - - /// @brief Wait for event completion. - void wait() const { - check_status("wait event failed", [=](status_t* status) { cldnn_wait_for_event(_impl, status); }); - } - - /// @brief Set event status to 'completed'. - void set() const { - check_status("set event failed", [=](status_t* status) { cldnn_set_event(_impl, status); }); - } - - /// @brief Register call back to be called on event completion. - void set_event_handler(cldnn_event_handler handler, void* param) const { - check_status("set event handler failed", [=](status_t* status) { cldnn_add_event_handler(_impl, handler, param, status); }); - } - - /// @brief Get profiling info for the event associated with network output. - std::vector get_profiling_info() const { - using namespace instrumentation; - wait(); - size_t size_ret = 0; - status_t err_invalid_arg = CLDNN_SUCCESS; - cldnn_get_event_profiling_info(_impl, nullptr, 0, &size_ret, &err_invalid_arg); - - if (size_ret == 0) { - return {}; - } - - std::vector profiling_info_ref(size_ret); - - check_status("get event profiling info failed", [&](status_t* status) { - cldnn_get_event_profiling_info(_impl, profiling_info_ref.data(), profiling_info_ref.size(), &size_ret, status); - }); - assert(profiling_info_ref.size() == size_ret); - - std::vector result(profiling_info_ref.size()); - std::transform( - std::begin(profiling_info_ref), - std::end(profiling_info_ref), - std::begin(result), - [](const cldnn_profiling_interval& ref) -> profiling_interval { - return { - ref.name, - std::make_shared(std::chrono::nanoseconds(ref.nanoseconds))}; - }); - return result; - } - - /// @brief Returns C API event handler. - cldnn_event get() const { return _impl; } - -private: - cldnn_event _impl; - void retain() { - check_status("retain event failed", [=](status_t* status) { cldnn_retain_event(_impl, status); }); - } - void release() { - check_status("retain event failed", [=](status_t* status) { cldnn_release_event(_impl, status); }); - } -}; -CLDNN_API_CLASS(event) - -/// @} -/// @} -} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/network.hpp b/inference-engine/thirdparty/clDNN/api/CPP/network.hpp deleted file mode 100644 index 492a98acc9b9fb..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/CPP/network.hpp +++ /dev/null @@ -1,366 +0,0 @@ -/* -// Copyright (c) 2016-2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "cldnn_defs.h" -#include "compounds.h" -#include "memory.hpp" -#include "program.hpp" -#include "event.hpp" - -#include -#include -#include -#include -#include -#include - -namespace cldnn { - -/// @addtogroup cpp_api C++ API -/// @{ - -/// @defgroup cpp_network Network Execution -/// @{ - -/// @brief Represents network output returned by @ref network::get_output(). -struct network_output { - /// @brief Returns @ref event associated with the output. - event get_event() const { return _event; } - - /// @brief Returns @ref memory object of the output. Blocked until associated @ref event is not complete. - memory get_memory() const { - _event.wait(); - return _result; - } - -private: - event _event; - memory _result; - network_output(event evt, memory mem) : _event(evt), _result(mem) {} - network_output(cldnn_event evt, cldnn_memory mem) : _event(evt), _result(mem) {} - friend struct network; -}; - -/// @brief Executable network allocated from @ref program. -struct network { - /// @brief Allocate network - /// @param program The program object which contains compiled primitives this network should allocate memory for. - network(program const& program, uint16_t stream_id) - : _impl(check_status("network allocation failed", [&](status_t* status) { - return cldnn_allocate_network(program.get(), stream_id, status); - })) {} - - /// @brief Constructs network object from implicitly created program object. This is a shorthand for network(program(engine, topology, options)) - /// @param engine - /// @param topology - /// @param options - network(const engine& engine, - const topology& topology, - const build_options& options = build_options(), - uint16_t stream_id = 0) - : network(program(engine, topology, options), stream_id) {} - - /// @brief Constructs network object from C API @ref cldnn_network. - explicit network(cldnn_network impl) : _impl(impl) { - if (_impl == nullptr) - throw std::invalid_argument("implementation pointer should not be null"); - } - - /// @brief Copy construction. - network(const network& other) : _impl(other._impl) { retain(); } - - /// @brief Copy assignment. - network& operator=(const network& other) { - if (_impl == other._impl) - return *this; - release(); - _impl = other._impl; - retain(); - return *this; - } - - /// @brief Releases wrapped C API @ref cldnn_network. - ~network() { release(); } - - friend bool operator==(const network& lhs, const network& rhs) { return lhs._impl == rhs._impl; } - friend bool operator!=(const network& lhs, const network& rhs) { return !(lhs == rhs); } - - /// @brief Returns @ref engine by which network was built. - engine get_engine() const { - engine status = (engine) check_status("get network engine failed", - [&](status_t* status) { return cldnn_get_network_engine(_impl, status); }); - return status; - } - - /// @brief Returns network internal @ref program. - program get_program() const { - program status = (program) check_status("get network program failed", - [&](status_t* status) { return cldnn_get_network_program(_impl, status); }); - return status; - } - - /// @brief Provides @ref memory for @ref input_layout primitives defined by user in source @ref topology. - void set_input_data(const primitive_id& id, const memory& mem) const { - check_status("set network input failed", - [&](status_t* status) { cldnn_set_network_input(_impl, id.c_str(), mem.get(), status); }); - } - - /// @brief Sets learning rate for training primitives. - void set_learning_rate(const float lr) { - check_status("set learning rate failed", - [&](status_t* status) { cldnn_set_learning_rate(_impl, lr, status); }); - } - - /// @brief Return learning rate. - float get_learning_rate() { - return check_status("get learning rate failed", - [&](status_t* status) { return cldnn_get_learning_rate(_impl, status); }); - } - - /// @brief Return stream id. - uint16_t get_stream_id() { - return check_status("get stream id failed", - [&](status_t* status) { return cldnn_get_network_stream_id(_impl, status); }); - } - - std::string get_primitive_info(const primitive_id& id) const { - size_t size_ret = 0; - status_t err_invalid_arg = CLDNN_SUCCESS; - - cldnn_get_primitive_info(_impl, id.c_str(), nullptr, 0, &size_ret, &err_invalid_arg); - assert(err_invalid_arg == CLDNN_INVALID_ARG); - assert(size_ret > 0); - std::vector names_buf(size_ret); - - check_status("get primitive info failed", [&](status_t* status) { - cldnn_get_primitive_info(_impl, id.c_str(), names_buf.data(), names_buf.size(), &size_ret, status); - }); - assert(names_buf.size() == size_ret); - - std::string result(names_buf.begin(), names_buf.end()); - return result; - } - - /// @brief Returns description of final runtime graph - std::vector get_primitives_info() { - size_t size_ret = 0; - status_t err_invalid_arg = CLDNN_SUCCESS; - cldnn_get_primitives_info(_impl, nullptr, 0, &size_ret, &err_invalid_arg); - assert(size_ret > 0); - std::vector buf(size_ret); - - check_status("get network primitives info extended", [&](status_t* status) { - cldnn_get_primitives_info(_impl, buf.data(), buf.size(), &size_ret, status); - }); - - std::vector res; - for (auto& pi : buf) { - res.emplace_back(pi); - } - return res; - } - - /// @brief Returns description of all optimization stages - std::vector>> get_optimization_steps_info() { - size_t total_size_ret = 0; - size_t steps_count_ret = 0; - size_t step_names_size_ret = 0; - status_t err_invalid_arg = CLDNN_SUCCESS; - cldnn_get_optimizer_passes_info(_impl, - nullptr, - nullptr, - nullptr, - 0, - &total_size_ret, - &steps_count_ret, - &step_names_size_ret, - &err_invalid_arg); - assert(total_size_ret > 0); - std::vector buf(total_size_ret); - std::vector info_size(steps_count_ret); - std::vector info_names(step_names_size_ret); - - check_status("get primitives info for each optimization step", [&](status_t* status) { - cldnn_get_optimizer_passes_info(_impl, - buf.data(), - info_size.data(), - info_names.data(), - buf.size(), - &total_size_ret, - &steps_count_ret, - &step_names_size_ret, - status); - }); - - std::vector>> res; - std::vector names; - for (auto buf_ptr = info_names.data(); *buf_ptr != 0; buf_ptr += names.back().size() + 1) { - names.emplace_back(buf_ptr); - } - - assert(names.size() == steps_count_ret); - - int j = 0; - for (size_t i = 0; i < steps_count_ret; i++) { - int sz = info_size[i]; - std::vector opt_step; - for (int k = 0; k < sz; k++) { - opt_step.emplace_back(buf[j]); - j++; - } - res.emplace_back(names[i], opt_step); - } - return res; - } - - /// @brief Returns the list of executed primitives. - std::vector get_executed_primitive_ids() const { - return get_prim_ids(cldnn_get_network_executed_primitive_names); - } - - /// @brief Returns the list of all primitives ids in network. - std::vector get_all_primitive_ids() const { - return get_prim_ids(cldnn_get_network_all_primitive_names); - } - - /// @brief Returns the list of all primitives ids in network before graph optimization. - std::vector get_all_primitive_org_ids() const { - return get_prim_ids(cldnn_get_network_all_primitive_org_names); - } - - /// @brief Returns the list of available network outputs. - std::vector get_output_ids() const { return get_prim_ids(cldnn_get_network_output_names); } - - /// @brief Returns @ref network_output object for particular @p output. Can't be called before network execution - network_output get_output(const primitive_id& output_id) const { - cldnn_network_output output = check_status( - "get network output failed", - [&](status_t* status) { return cldnn_get_network_output(_impl, output_id.c_str(), status); }); - return network_output(output.event, output.memory); - } - - /// @brief Returns @ref memory object for particular @p output. Can be called before network execution - memory get_output_memory(const primitive_id& output_id) const { - memory output = (memory) check_status("get output memory failed", [&](status_t* status) { - return cldnn_get_network_output_memory(_impl, output_id.c_str(), status); - }); - return output; - } - - /// @brief Returns @ref event object for particular @p primitive. Can't be called before network execution - event get_primitive_event(const primitive_id& output_id) const { - event output = (event) check_status("get output event failed", [&](status_t* status) { - return cldnn_get_network_output_event(_impl, output_id.c_str(), status); - }); - return output; - } - - /// @brief Returns the list of @ref event for the primitives that were executed in network. - std::map get_executed_primitives() const { - auto primitive_ids = get_executed_primitive_ids(); - auto all_primitive_ids = get_all_primitive_ids(); - auto all_primitive_org_ids = get_all_primitive_org_ids(); - // Get list of optimized prmitives - std::vector optimized_primitives; - for (decltype(all_primitive_org_ids.size()) i = 0; i < all_primitive_org_ids.size(); i++) { - if (all_primitive_ids[i] == "_optimized_") - optimized_primitives.push_back(all_primitive_org_ids[i]); - } - std::map result; - for (auto& id : primitive_ids) { - if (std::find(optimized_primitives.begin(), optimized_primitives.end(), id) == optimized_primitives.end()) - result.emplace(id, get_primitive_event(id)); - } - return result; - } - - /// @brief Returns the list of primitive ids before and after graph optimization. - /// @details If primitive was not optimized, the old and actual id will be the same. - /// @n If primitive was optimized during graph optimization, the actual id will be "_optimized_". - std::map get_all_primitives() const { - auto primitive_ids = get_all_primitive_ids(); - auto primitive_org_ids = get_all_primitive_org_ids(); - std::map result; - for (decltype(primitive_org_ids.size()) i = 0; i < primitive_org_ids.size(); i++) { - result.emplace(primitive_org_ids[i], primitive_ids[i]); - } - return result; - } - - /// @brief Executes network and returns the list of @ref network_output. - /// @param dependencies List of @ref event objects to be waited before network execution. - /// @note User should call set_input_data() for every @ref input_layout defined in source @ref topology - /// before network execution. - std::map execute(const std::vector& dependencies = {}) const { - std::vector dep_refs(dependencies.size()); - for (decltype(dependencies.size()) i = 0; i < dependencies.size(); i++) { - dep_refs[i] = dependencies[i].get(); - } - - check_status("network execute failed", [&](status_t* status) { - return cldnn_execute_network(_impl, dep_refs.data(), dep_refs.size(), status); - }); - - auto output_ids = get_output_ids(); - std::map result; - for (auto& id : output_ids) { - result.emplace(id, get_output(id)); - } - return result; - } - - /// @brief Returns wrapped C API @ref cldnn_network handler. - cldnn_network get() const { return _impl; } - -private: - cldnn_network _impl; - - typedef void ( - *get_prim_ids_func_t)(cldnn_network network, char* names, size_t size, size_t* size_ret, cldnn_status* status); - - void retain() { - check_status("retain topology failed", [=](status_t* status) { cldnn_retain_network(_impl, status); }); - } - void release() { - check_status("retain topology failed", [=](status_t* status) { cldnn_release_network(_impl, status); }); - } - - std::vector get_prim_ids(get_prim_ids_func_t func) const { - size_t size_ret = 0; - status_t err_invalid_arg = CLDNN_SUCCESS; - func(_impl, nullptr, 0, &size_ret, &err_invalid_arg); - assert(err_invalid_arg == CLDNN_INVALID_ARG); - assert(size_ret > 0); - std::vector names_buf(size_ret); - - check_status("get network output ids failed", [&](status_t* status) { - func(_impl, names_buf.data(), names_buf.size(), &size_ret, status); - }); - assert(names_buf.size() == size_ret); - - std::vector result; - for (auto buf_ptr = names_buf.data(); *buf_ptr != 0; buf_ptr += result.back().size() + 1) { - result.emplace_back(buf_ptr); - } - return result; - } -}; -CLDNN_API_CLASS(network) -/// @} -/// @} -} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/primitive.hpp b/inference-engine/thirdparty/clDNN/api/CPP/primitive.hpp deleted file mode 100644 index 22d2b214431513..00000000000000 --- a/inference-engine/thirdparty/clDNN/api/CPP/primitive.hpp +++ /dev/null @@ -1,314 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once - -#include "cldnn_defs.h" -#include "compounds.h" -#include "layout.hpp" - -#include -#include -#include -#include -#include -#include - -namespace cldnn { -/// @addtogroup cpp_api C++ API -/// @{ - -/// @addtogroup cpp_topology Network Topology -/// @{ - -/// @brief Globally unique primitive type id. -using primitive_type_id = cldnn_primitive_type_id; -/// @brief C API compatible unique @p id of a primitive within a topology. -using primitive_id_ref = cldnn_primitive_id; -/// @brief Unique @p id of a primitive within a topology. -using primitive_id = std::string; - -/// @brief Dynamic cast to specified primitive description type. -template -typename PType::dto* as_dto(CLDNN_PRIMITIVE_DESC(primitive) * dto) { - if (dto->type != PType::type_id()) - throw std::invalid_argument("type"); - return reinterpret_cast(dto); -} - -/// @brief Dynamic cast to specified primitive description type. -template -const typename PType::dto* as_dto(const CLDNN_PRIMITIVE_DESC(primitive) * dto) { - if (dto->type != PType::type_id()) - throw std::invalid_argument("type"); - return reinterpret_cast(dto); -} - -struct primitive_info; - -/// @brief Base class of network primitive description. -struct primitive { - /// @brief Initialize fields common for all primitives. - struct fixed_size_vector_ref { - private: - std::vector& vref; - - public: - explicit fixed_size_vector_ref(std::vector& ref) : vref(ref) {} - - auto size() const -> decltype(vref.size()) { return vref.size(); } - auto empty() const -> decltype(vref.empty()) { return vref.empty(); } - auto begin() const -> decltype(vref.begin()) { return vref.begin(); } - auto end() const -> decltype(vref.end()) { return vref.end(); } - auto cbegin() const -> decltype(vref.cbegin()) { return vref.cbegin(); } - auto cend() const -> decltype(vref.cend()) { return vref.cend(); } - - primitive_id& operator[](size_t idx) { return vref[idx]; } - primitive_id const& operator[](size_t idx) const { return vref[idx]; } - - primitive_id& at(size_t idx) { return vref.at(idx); } - primitive_id const& at(size_t idx) const { return vref.at(idx); } - - primitive_id* data() { return vref.data(); } - const primitive_id* data() const { return vref.data(); } - - const std::vector& ref() const { return vref; } - }; - -public: - primitive(const primitive_type_id& type, - const primitive_id& id, - const std::vector& input, - const padding& output_padding = padding(), - const optional_data_type output_data_type = optional_data_type()) - : type(type), - id(id), - input(_input.cpp_ids), - output_padding(output_padding), - output_data_type(output_data_type), - _input(input) {} - - /// @brief Constructs a copy from basic C API @CLDNN_PRIMITIVE_DESC{primitive} - explicit primitive(const CLDNN_PRIMITIVE_DESC(primitive) * dto) - : type(dto->type), - id(dto->id), - input(_input.cpp_ids), - output_padding(dto->output_padding), - output_data_type(dto->output_data_type.enabled - ? optional_data_type{static_cast(dto->output_data_type.data_type)} - : optional_data_type{}), - _input(dto->input) {} - - virtual ~primitive() = default; - - /// @brief Requested output padding. - /// @brief Requested output padding. - /// @brief Returns pointer to a C API primitive descriptor casted to @CLDNN_PRIMITIVE_DESC{primitive}. - virtual const CLDNN_PRIMITIVE_DESC(primitive) * get_dto() const = 0; - - /// @brief Returns references to all primitive ids on which this primitive depends - inputs, weights, biases, etc. - std::vector> dependencies() { - std::vector> result; - auto&& deps = get_dependencies(); - - result.reserve(_input.size() + deps.size()); - for (auto& pid : _input.cpp_ids) result.push_back(std::ref(pid)); - for (auto& pid : deps) result.push_back(std::ref(const_cast(pid.get()))); - - return result; - } - - /// @brief Returns copy of all primitive ids on which this primitive depends - inputs, weights, biases, etc. - std::vector dependencies() const { - auto result = input.ref(); - auto deps = get_dependencies(); - result.insert(result.end(), deps.begin(), deps.end()); - return result; - } - - virtual primitive_id type_string() const = 0; - - /// @brief Implicit conversion to primiitive id. - operator primitive_id() const { return id; } - - /// @brief Primitive's type id. - const primitive_type_id type; - - /// @brief Primitive's id. - const primitive_id id; - - /// @brief List of ids of input primitives. - fixed_size_vector_ref input; - - /// @brief Requested output padding. - padding output_padding; - - /// @brief Requested output precision, if any. - optional_data_type output_data_type; - -protected: - struct primitive_id_arr { - explicit primitive_id_arr(std::vector const& vec) : cpp_ids(vec) {} - - explicit primitive_id_arr(std::vector&& vec) : cpp_ids(std::move(vec)) {} - - // create from C API id array - explicit primitive_id_arr(cldnn_primitive_id_arr c_id_arr) { - cpp_ids.resize(c_id_arr.size); - for (size_t i = 0; i < c_id_arr.size; ++i) cpp_ids[i] = c_id_arr.data[i]; - } - - std::vector cpp_ids; - mutable std::vector c_ids; - // get C API id array - auto ref() const -> decltype(cldnn_primitive_id_arr{c_ids.data(), c_ids.size()}) { - c_ids.resize(cpp_ids.size()); - for (size_t i = 0; i < cpp_ids.size(); ++i) c_ids[i] = cpp_ids[i].c_str(); - - return cldnn_primitive_id_arr{c_ids.data(), c_ids.size()}; - } - - size_t size() const { return cpp_ids.size(); } - }; - - primitive_id_arr _input; - - virtual std::vector> get_dependencies() const { return {}; } - - friend primitive_info; -}; - -/// @brief base class for all primitives implementations. -template -class primitive_base : public primitive { -public: - /// @brief Returns pointer to a C API primitive descriptor casted to @CLDNN_PRIMITIVE_DESC{primitive}. - const CLDNN_PRIMITIVE_DESC(primitive) * get_dto() const override { - // update common dto fields - _dto.id = id.c_str(); - _dto.type = type; - _dto.input = _input.ref(); - _dto.output_padding = output_padding; - _dto.output_data_type.enabled = static_cast(output_data_type); - _dto.output_data_type.data_type = static_cast(*output_data_type); - - // call abstract method to update primitive-specific fields - update_dto(_dto); - return reinterpret_cast(&_dto); - } - -protected: - explicit primitive_base(const primitive_id& id, - const std::vector& input, - const padding& output_padding = padding(), - optional_data_type output_data_type = optional_data_type()) - : primitive(PType::type_id(), id, input, output_padding, output_data_type) {} - - explicit primitive_base(const DTO* dto) : primitive(reinterpret_cast(dto)) { - if (dto->type != PType::type_id()) - throw std::invalid_argument("DTO type mismatch"); - } - -private: - mutable DTO _dto; - - virtual void update_dto(DTO& dto) const = 0; -}; - -struct primitive_info { - primitive_info(const primitive_id& original_id, - const std::string& type_id, - const std::vector& dependencies, - const std::vector& users, - const std::vector& fused_ids, - const layout& output_layout, - const std::string& layout_str, - const std::string& kernel_id, - bool is_cpu, - int exec_id) - : original_id(original_id), - type_id(type_id), - c_dependencies(dependencies), - c_users(users), - c_fused_ids(fused_ids), - output_layout(output_layout), - layout_str(layout_str), - kernel_id(kernel_id), - is_cpu(is_cpu), - exec_id(exec_id) {} - - explicit primitive_info(const cldnn_primitive_info* c_info) - : original_id(c_info->original_id), - type_id(c_info->type_id), - c_dependencies(c_info->dependencies), - c_users(c_info->users), - c_fused_ids(c_info->fused_ids), - output_layout(c_info->output_layout), - layout_str(c_info->layout_str), - kernel_id(c_info->kernel_id), - is_cpu(c_info->is_cpu != 0), - exec_id(c_info->exec_id) {} - - primitive_id original_id; - std::string type_id; - primitive::primitive_id_arr c_dependencies; - primitive::primitive_id_arr c_users; - primitive::primitive_id_arr c_fused_ids; - layout output_layout; - std::string layout_str; - std::string kernel_id; - bool is_cpu; - int exec_id; - - const cldnn_primitive_info* get_dto() const { - dto.original_id = original_id.c_str(); - dto.type_id = type_id.c_str(); - dto.dependencies = c_dependencies.ref(); - dto.users = c_users.ref(); - dto.fused_ids = c_fused_ids.ref(); - dto.output_layout = output_layout; - dto.layout_str = layout_str.c_str(); - dto.kernel_id = kernel_id.c_str(); - dto.is_cpu = is_cpu; - dto.exec_id = exec_id; - - return &dto; - } - - mutable cldnn_primitive_info dto; -}; - -#define CLDNN_DEFINE_TYPE_ID(PType) \ - static primitive_type_id type_id() { \ - return check_status(#PType " type id failed", \ - [](status_t* status) { return cldnn_##PType##_type_id(status); }); \ - } - -#define CLDNN_DEFINE_TYPE_STRING(PType) \ - primitive_id type_string() const override { \ - static constexpr const char* type_str = #PType; \ - return std::string(type_str); \ - } - -#define CLDNN_DECLARE_PRIMITIVE(PType) \ - typedef CLDNN_PRIMITIVE_DESC(PType) dto; \ - CLDNN_DEFINE_TYPE_ID(PType) \ - CLDNN_DEFINE_TYPE_STRING(PType) - -/// @} -/// @} -} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/activation.hpp b/inference-engine/thirdparty/clDNN/api/activation.hpp similarity index 55% rename from inference-engine/thirdparty/clDNN/api/CPP/activation.hpp rename to inference-engine/thirdparty/clDNN/api/activation.hpp index 7fe48276e40cf9..ec035036089873 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/activation.hpp +++ b/inference-engine/thirdparty/clDNN/api/activation.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/activation.h" #include "primitive.hpp" #include @@ -28,6 +27,60 @@ namespace cldnn { /// @addtogroup cpp_primitives Primitives /// @{ +/// @brief activation functions +enum class activation_func { + none, // val + logistic, // 1/(1 + exp(-val)) + hyperbolic_tan, // tanh(val) + relu, // max(0, val) + relu_negative_slope, // max(0, val) + a * min(0, val) (a is additional param) + clamp, // max(a, min(b, val) (a,b are additional param) + softrelu, // log(1 + exp(val)) + abs, // abs(val) + linear, // a*val + b (a,b are additional params) + square, // val*val + sqrt, // sqrt(val) + elu, // max(0, val) + a * (exp(min(0, val) - 1) (a is additional param) + sin, // sin(val) + asin, // asin(val) + sinh, // sinh(val) + asinh, // asinh(val) + cos, // cos(val) + acos, // acos(val) + cosh, // cosh(val) + acosh, // acosh(val) + log, // log(val) + log2, // log2(val) + exp, // exp(val) + tan, // tan(val) + atan, // atan(val) + atanh, // atanh(val) + floor, // floor(val) + ceil, // ceil(val) + negative, // -val + negation, // !val + pow, // pow(val, a) + reciprocal, // (1/val) + erf, // Gauss error function + hard_sigmoid, // max(0, min(1, a * val + b)) (a,b are additional params) + selu, // for val <= 0: b * (a * e^val - a); for val > 0: b * val (a,b are additional params) + sign, // val > 0: 1; val < 0: -1; val == 0: 0 + softplus, // ln(exp(val) + 1) + softsign // (val/(1+|val|)) +}; + +/// @brief activation gradient functions +enum class activation_grad_func { + none, // val + relu, // val * (input > 0) + relu_negative_slope, // val * ((input > 0) + a * (input <= 0) (a is additional param) +}; + +/// @brief activation additional params +struct activation_additional_params { + float a, b; +}; + /// @brief Activation using rectified linear unit or parameterized rectified linear unit. /// @details Can get one negative slope or negative slope per channel. /// @par Algorithm: @@ -36,7 +89,7 @@ namespace cldnn { /// @li out(i,x,y) : value at x, y from i-th feature map after activation. /// @li in(i,x,y) : value at x, y from i-th feature map before activation. /// @li slope(i) : the slope value of the i-th feature map (can be shared across channels or one slope per channel). -struct activation : public primitive_base { +struct activation : public primitive_base { CLDNN_DECLARE_PRIMITIVE(activation) /// @brief Constructs Relu primitive. @@ -46,11 +99,11 @@ struct activation : public primitive_baseactivation_func), - additional_params(dto->additional_params), - additional_params_input(dto->additional_params_input) {} - /// @brief activation function. - cldnn_activation_func activation_func; + activation_func activation_function; /// @brief activation additional params. - cldnn_activation_additional_params additional_params; + activation_additional_params additional_params; /// @brief PRelu activation slope input primitive id. /// Input x dimension should be equal to input feature size (one slope per channel). @@ -94,14 +140,8 @@ struct activation : public primitive_base namespace cldnn { @@ -35,7 +35,7 @@ namespace cldnn { /// @li out(i,x,y) : value at x, y from i-th feature map after activation. /// @li in(i,x,y) : value at x, y from i-th feature map before activation. /// @li slope(i) : the slope value of the i-th feature map (can be shared across channels or one slope per channel). -struct activation_grad : public primitive_base { +struct activation_grad : public primitive_base { CLDNN_DECLARE_PRIMITIVE(activation_grad) /// @brief Constructs Relu grad primitive. @@ -47,11 +47,11 @@ struct activation_grad : public primitive_baseactivation_grad_func), - additional_params(dto->additional_params), - additional_params_input(dto->additional_params_input) {} - /// @brief activation_grad function. - cldnn_activation_grad_func activation_grad_func; + activation_grad_func activation_grad_function; /// @brief activation_grad additional params. - cldnn_activation_additional_params additional_params; + activation_additional_params additional_params; /// @brief PRelu activation slope input primitive id. /// Input x dimension should be equal to input feature size (one slope per channel). @@ -96,14 +89,8 @@ struct activation_grad : public primitive_base @@ -40,7 +39,7 @@ namespace cldnn { /// @n float v[t] = beta2 * v[t-1] + (1 - beta2) * grad[t] * grad[t]; /// @n float result = result - lr[t] * m[t] / (sqrt(v[t]) + epsilon); -struct apply_adam : public primitive_base { +struct apply_adam : public primitive_base { CLDNN_DECLARE_PRIMITIVE(apply_adam) /// @brief Constructs apply Adam primitive. @@ -78,19 +77,6 @@ struct apply_adam : public primitive_basem), - v(dto->v), - beta1_power(dto->beta1_power), - beta2_power(dto->beta2_power), - lr(dto->lr), - beta1(dto->beta1), - beta2(dto->beta2), - epsilon(dto->epsilon), - dependency_id(dto->dependency_id) {} - /// @brief Primitive id containing m data. primitive_id m; /// @brief Primitive id containing v data. @@ -118,18 +104,6 @@ struct apply_adam : public primitive_base #include @@ -34,7 +33,7 @@ namespace cldnn { /// We use f32, as bigger indices could not fit in smaller data types. /// If you want to use output as indices outside of network (inside just use lookup table primitive), /// you will need to firstly cast it to int (look into tests for example). -struct arg_max_min : public primitive_base { +struct arg_max_min : public primitive_base { CLDNN_DECLARE_PRIMITIVE(arg_max_min) /// @brief Enum type to specify axis to return values from. @@ -72,16 +71,6 @@ struct arg_max_min : public primitive_basetop_k), - output_type(static_cast(dto->output_type)), - axis(static_cast(dto->axis)), - sort(static_cast(dto->sort)), - with_axis(dto->with_axis != 0), - values_first(dto->values_first != 0) {} - /// @brief Number of indices to output. uint32_t top_k; /// @brief Type of output - max or mix. @@ -94,18 +83,8 @@ struct arg_max_min : public primitive_base(output_type); - dto.with_axis = with_axis; - dto.axis = static_cast(axis); - dto.sort = static_cast(sort); - dto.values_first = values_first; - } }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/average_unpooling.hpp b/inference-engine/thirdparty/clDNN/api/average_unpooling.hpp similarity index 81% rename from inference-engine/thirdparty/clDNN/api/CPP/average_unpooling.hpp rename to inference-engine/thirdparty/clDNN/api/average_unpooling.hpp index 74328409273033..733324bc9e9ce3 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/average_unpooling.hpp +++ b/inference-engine/thirdparty/clDNN/api/average_unpooling.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/average_unpooling.h" #include "primitive.hpp" namespace cldnn { @@ -30,7 +29,7 @@ namespace cldnn { /// @brief Performs "average_unpooling" operation. /// @details Reverse operation of average pooling. /// Each element in every pooling window is filled with output / window size value. In case of window overlap the elements are added. -struct average_unpooling : public primitive_base { +struct average_unpooling : public primitive_base { CLDNN_DECLARE_PRIMITIVE(average_unpooling) /// @brief Constructs average_unpooling primitive. @@ -48,25 +47,14 @@ struct average_unpooling : public primitive_basestride), size(dto->size), output_size(dto->output_size) {} - /// @brief Defines shift in output buffer. tensor stride; /// @brief Pooling kernel size. tensor size; /// @brief Output size of this primitive. tensor output_size; - -protected: - void update_dto(dto& dto) const override { - dto.stride = stride; - dto.size = size; - dto.output_size = output_size; - } }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/batch_norm.hpp b/inference-engine/thirdparty/clDNN/api/batch_norm.hpp similarity index 90% rename from inference-engine/thirdparty/clDNN/api/CPP/batch_norm.hpp rename to inference-engine/thirdparty/clDNN/api/batch_norm.hpp index b6061df9f3877e..29b8e69d96014e 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/batch_norm.hpp +++ b/inference-engine/thirdparty/clDNN/api/batch_norm.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/batch_norm.h" #include "primitive.hpp" #include @@ -37,7 +36,7 @@ namespace cldnn { /// @n global stats can be computed as: /// @n out[i] = ( (in[i] - mean[b]) / sqrt(variance[b] + epsilon) ) * scale[b] + shift[b] -struct batch_norm : public primitive_base { +struct batch_norm : public primitive_base { CLDNN_DECLARE_PRIMITIVE(batch_norm) /// @brief Constructs batch normalization primitive. @@ -146,16 +145,6 @@ struct batch_norm : public primitive_basemean), - variance(dto->variance), - scale(dto->scale), - shift(dto->shift), - inv_variance(dto->inv_variance), - epsilon(dto->epsilon) {} - /// @brief Primitive id containing mean data. primitive_id mean; /// @brief Primitive id containing variance. @@ -188,15 +177,6 @@ struct batch_norm : public primitive_base @@ -31,7 +30,7 @@ namespace cldnn { /// @brief Performs backward batch normalization layer. /// @details Calculates mean gradient and gradient * input for every feature in data, /// then output is calculated as inv_variance * (input_grad - mean_grad_input * input - mean_grad) -struct batch_norm_grad : public primitive_base { +struct batch_norm_grad : public primitive_base { CLDNN_DECLARE_PRIMITIVE(batch_norm_grad) /// @brief Constructs batch normalization backward layer. @@ -48,11 +47,6 @@ struct batch_norm_grad : public primitive_baseinv_variance) { - } - /// @brief Primitive id containing inverted variance from forward pass. primitive_id inv_variance; @@ -60,12 +54,8 @@ struct batch_norm_grad : public primitive_base> get_dependencies() const override { return {inv_variance}; } - - void update_dto(dto& dto) const override { - dto.inv_variance = inv_variance.c_str(); - } }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/binary_convolution.hpp b/inference-engine/thirdparty/clDNN/api/binary_convolution.hpp similarity index 82% rename from inference-engine/thirdparty/clDNN/api/CPP/binary_convolution.hpp rename to inference-engine/thirdparty/clDNN/api/binary_convolution.hpp index 3331238d9f49c1..effe04ad07083a 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/binary_convolution.hpp +++ b/inference-engine/thirdparty/clDNN/api/binary_convolution.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/binary_convolution.h" #include "primitive.hpp" #include @@ -29,7 +28,7 @@ namespace cldnn { /// @{ /// @brief Performs forward spatial binary_convolution with weight sharing. -struct binary_convolution : public primitive_base { +struct binary_convolution : public primitive_base { CLDNN_DECLARE_PRIMITIVE(binary_convolution) /// @brief Constructs binary_convolution primitive. @@ -59,29 +58,14 @@ struct binary_convolution : public primitive_baseinput_offset), - stride(dto->stride), - dilation(dto->dilation), - output_size(dto->output_size), - groups(dto->groups), - pad_value(dto->pad_value), - _weights(dto->weights) {} - - /// @brief List of primitive ids containing weights data. - fixed_size_vector_ref weights; /// @brief Defines a shift, relative to (0,0) position of the input buffer, where (0,0) point of the binary_convolution window should start calculations. tensor input_offset; /// @brief Defines shift in input buffer between adjacent calculations of output values. @@ -96,29 +80,17 @@ struct binary_convolution : public primitive_base pad bits equal to 0; 0 -> pad is not counted float pad_value; + /// @brief List of primitive ids containing weights data. + const primitive_id_arr weights; int32_t split() const { return static_cast(weights.size()); } -protected: - primitive_id_arr _weights; - std::vector> get_dependencies() const override { std::vector> ret; ret.reserve(weights.size()); - for (auto& w : weights) ret.push_back(w); + for (auto& w : weights) ret.push_back(std::ref(w)); return ret; } - - void update_dto(dto& dto) const override { - dto.weights = _weights.ref(); - dto.input_offset = input_offset; - dto.stride = stride; - dto.split = split(); - dto.dilation = dilation; - dto.output_size = output_size; - dto.groups = groups; - dto.pad_value = pad_value; - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/border.hpp b/inference-engine/thirdparty/clDNN/api/border.hpp similarity index 86% rename from inference-engine/thirdparty/clDNN/api/CPP/border.hpp rename to inference-engine/thirdparty/clDNN/api/border.hpp index 50f5544665fd87..db617f08a0981f 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/border.hpp +++ b/inference-engine/thirdparty/clDNN/api/border.hpp @@ -14,8 +14,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once - -#include "../C/border.h" #include "primitive.hpp" namespace cldnn { @@ -29,20 +27,20 @@ namespace cldnn { /// @brief Type of border that will be added to the input by border layer / primitive. enum class border_type : std::int32_t { /// @brief All points in the border are set to constant value. - constant = cldnn_border_constant, - zero = cldnn_border_zero, + constant, + zero, /// @brief Border is constructed as an mirror of image (edge is also mirrored). /// @details Size of border in any dimension cannot be larger than size of /// input in the same dimension. - mirror = cldnn_border_mirror, + mirror, /// @brief Border is constructed as an mirror of image (edge is NOT mirrored). /// @details Size of border in any dimension cannot be larger than size of /// input in the same dimension decreased by @c 1. - mirror_101 = cldnn_border_mirror_101, + mirror_101, /// @brief Border is constructed as an replication of edge. /// @details Size of border in any dimension cannot be larger than size of /// input in the same dimension. - edge = cldnn_border_edge + edge }; /// @brief Adds border around input. @@ -58,7 +56,7 @@ enum class border_type : std::int32_t { /// @n - For @c border_type equal to @c cldnn_border_mirror_101, @c left_top_sizes and @c right_bottom_sizes /// must be lower than size of input on corresponding dimension (for all dimensions) /// @n Breaking any of this conditions will cause exeption throw. -struct border : public primitive_base { +struct border : public primitive_base { CLDNN_DECLARE_PRIMITIVE(border) /// @brief Constructs border primitive / layer. @@ -104,14 +102,6 @@ struct border : public primitive_base { const padding& output_padding = padding()) : border(id, input, x_y_sizes, x_y_sizes, type, 0.0f, output_padding) {} - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{border} - border(const dto* dto) - : primitive_base(dto), - left_top_sizes(dto->left_top_sizes), - right_bottom_sizes(dto->right_bottom_sizes), - type(static_cast(dto->border_type)), - border_value(dto->border_value) {} - /// @brief Sizes of border that needs to be added from left (in X dimension) and from top (in Y dimension). tensor left_top_sizes; /// @brief Sizes of border that needs to be added from right (in X dimension) and from bottom (in Y dimension). @@ -120,14 +110,6 @@ struct border : public primitive_base { border_type type; /// @brief Border value that is used in constant mode. float border_value; - -protected: - void update_dto(dto& dto) const override { - dto.left_top_sizes = left_top_sizes; - dto.right_bottom_sizes = right_bottom_sizes; - dto.border_type = static_cast(type); - dto.border_value = border_value; - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/broadcast.hpp b/inference-engine/thirdparty/clDNN/api/broadcast.hpp similarity index 89% rename from inference-engine/thirdparty/clDNN/api/CPP/broadcast.hpp rename to inference-engine/thirdparty/clDNN/api/broadcast.hpp index 4aca4c72ce00e0..7c2ca99be0ebfd 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/broadcast.hpp +++ b/inference-engine/thirdparty/clDNN/api/broadcast.hpp @@ -15,7 +15,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/broadcast.h" #include "primitive.hpp" #include @@ -67,7 +66,7 @@ namespace cldnn { /// @n - @p output_shape must be greater (dividable) than or equal to reinterpreted /// input on all dimensions. /// @n Breaking any of these conditions will raise an exception. -struct broadcast : public primitive_base { +struct broadcast : public primitive_base { CLDNN_DECLARE_PRIMITIVE(broadcast) /// @brief Constructs broadcast primitive / layer. @@ -91,25 +90,11 @@ struct broadcast : public primitive_basebroadcast_sizes), - broadcast_axes(uint16_t_arr_to_vector(dto->broadcast_axes)) - - {} - /// @brief Expected sizes of output from broadcast primitive. tensor broadcast_sizes; /// @brief Array of axes positions from output shape (0-based, from left to right) /// along which broadcast should happen. std::vector broadcast_axes; - -protected: - void update_dto(dto& dto) const override { - dto.broadcast_sizes = broadcast_sizes; - dto.broadcast_axes = uint16_t_vector_to_arr(broadcast_axes); - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/cldnn_defs.h b/inference-engine/thirdparty/clDNN/api/cldnn.hpp similarity index 67% rename from inference-engine/thirdparty/clDNN/api/CPP/cldnn_defs.h rename to inference-engine/thirdparty/clDNN/api/cldnn.hpp index 9e17acf4f7cc4b..e7f703043fda59 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/cldnn_defs.h +++ b/inference-engine/thirdparty/clDNN/api/cldnn.hpp @@ -1,5 +1,5 @@ /* -// Copyright (c) 2016 Intel Corporation +// Copyright (c) 2016-2019 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -130,16 +130,36 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include +#include +#include +#include #include #include -#include -#include -#include - -#include "../C/cldnn.h" namespace cldnn { + +/// @addtogroup cpp_api C++ API +/// @{ + +/// @defgroup cpp_version Version Information +/// @{ + +/// @brief Represents version information of API. +struct version_t { + int32_t major; ///< Major version component (major version of clDNN API interface). + int32_t minor; ///< Minor version component (minor version of API interface - correlated with IE API version). + int32_t build; ///< Build version component (version/revision of official Open Source drop of clDNN library). + int32_t revision; ///< Revision version component (incremental identifier of current build/compilation). +}; + +/// @brief Get information about version of clDNN. +version_t get_version(); + +/// @} + +float half_to_float(uint16_t value); +uint16_t float_to_half(float value); + // There is no portable half precision floating point support. // Using wrapped integral type with the same size and alignment restrictions. class half_impl { @@ -150,99 +170,30 @@ class half_impl { operator uint16_t() const { return _data; } operator float() const { - cldnn_status status = CLDNN_SUCCESS; - auto value = cldnn_half_to_float(_data, &status); - if (status != CLDNN_SUCCESS) - throw std::runtime_error("Conversion from half failed"); - return value; - } - explicit half_impl(float value) { - cldnn_status status = CLDNN_SUCCESS; - _data = cldnn_float_to_half(value, &status); - if (status != CLDNN_SUCCESS) - throw std::runtime_error("Conversion to half failed"); + return half_to_float(_data); } + explicit half_impl(float value) + : _data(float_to_half(value)) + {} + private: uint16_t _data; }; -} // namespace cldnn + // Use complete implementation if necessary. #if defined HALF_HALF_HPP -typedef half half_t; +using half_t = half; #else -typedef cldnn::half_impl half_t; +using half_t = half_impl; #endif -namespace cldnn { -/// @addtogroup cpp_api C++ API -/// @{ - -/// @defgroup cpp_error Error Handling -/// @{ - -using status_t = ::cldnn_status; - -/// @brief clDNN specific exception type. -class error : public std::runtime_error { -public: - explicit error(const std::string& _Message, status_t status = CLDNN_ERROR) - : runtime_error(_Message), _status(status) { - } - - explicit error(const char* _Message, status_t status = CLDNN_ERROR) - : runtime_error(_Message), _status(status) { - } - - /// @brief Returns clDNN status code. - const status_t& status() const { return _status; } - -private: - status_t _status; -}; - -#define CLDNN_THROW(msg, status) throw cldnn::error(msg, status); - -template -T check_status(std::string err_msg, std::function func) { - status_t status = CLDNN_SUCCESS; - auto result = func(&status); - if (status != CLDNN_SUCCESS) - CLDNN_THROW(err_msg.append(": ").append(cldnn_get_last_error_message()), status); - return result; -} - -template <> -inline void check_status(std::string err_msg, std::function func) { - status_t status = CLDNN_SUCCESS; - func(&status); - if (status != CLDNN_SUCCESS) - CLDNN_THROW(err_msg.append(": ").append(cldnn_get_last_error_message()), status); -} - -/// @} - -/// @defgroup cpp_version Version Information -/// @{ - -using version_t = ::cldnn_version; - -/// @brief Get information about version of clDNN. -inline version_t get_version() { - return check_status("get_version: fetching version information failed", - [](status_t* status) { - return ::cldnn_get_version(status); - }); -} - -/// @} - /// @cond CPP_HELPERS /// @defgroup cpp_helpers Helpers /// @{ -#define CLDNN_API_CLASS(the_class) static_assert(std::is_standard_layout::value, #the_class " has to be 'standart layout' class"); +#define CLDNN_API_CLASS(the_class) static_assert(std::is_standard_layout::value, #the_class " has to be 'standard layout' class"); template typename std::enable_if::value, T>::type align_to(T size, size_t align) { @@ -275,8 +226,8 @@ typename std::enable_if::value, bool>::type is_aligned_to(T /// division, except each operand is converted to unsigned type if necessary. template constexpr auto ceil_div(T1 val, T2 divider) - -> typename std::enable_if::value && std::is_integral::value, - decltype(std::declval::type>() / std::declval::type>())>::type { +-> typename std::enable_if::value && std::is_integral::value, + decltype(std::declval::type>() / std::declval::type>())>::type { typedef typename std::make_unsigned::type UT1; typedef typename std::make_unsigned::type UT2; typedef decltype(std::declval() / std::declval()) RetT; @@ -299,8 +250,8 @@ constexpr auto ceil_div(T1 val, T2 divider) /// division, except each operand is converted to unsigned type if necessary. template constexpr auto round_up_to(T1 val, T2 rounding) - -> typename std::enable_if::value && std::is_integral::value, - decltype(std::declval::type>() / std::declval::type>())>::type { +-> typename std::enable_if::value && std::is_integral::value, + decltype(std::declval::type>() / std::declval::type>())>::type { typedef typename std::make_unsigned::type UT1; typedef typename std::make_unsigned::type UT2; typedef decltype(std::declval() / std::declval()) RetT; @@ -308,81 +259,7 @@ constexpr auto round_up_to(T1 val, T2 rounding) return static_cast(ceil_div(val, rounding) * static_cast(rounding)); } -/// -/// \brief Converts C API float array to std::vector -/// -inline std::vector float_arr_to_vector(const cldnn_float_arr& arr) { - std::vector result(arr.size); - for (size_t i = 0; i < arr.size; i++) { - result[i] = arr.data[i]; - } - return result; -} - -/// -/// \brief Converts C API float array to std::vector -/// -inline std::vector uint16_t_arr_to_vector(const cldnn_uint16_t_arr& arr) { - std::vector result(arr.size); - for (size_t i = 0; i < arr.size; i++) { - result[i] = arr.data[i]; - } - return result; -} - -/// -/// \brief Converts C API uint8_t array to std::vector -/// -inline std::vector uint8_t_arr_to_vector(const cldnn_uint8_t_arr& arr) { - std::vector result(arr.size); - for (size_t i = 0; i < arr.size; i++) { - result[i] = arr.data[i]; - } - return result; -} - -/// -/// \brief Converts std::vector to C API float_array -/// -inline cldnn_float_arr float_vector_to_arr(const std::vector& stor) { - return {stor.data(), stor.size()}; -} - -/// -/// \brief Converts std::vector to C API float_array -/// -inline cldnn_uint16_t_arr uint16_t_vector_to_arr(const std::vector& stor) { - return {stor.data(), stor.size()}; -} - -/// -/// \brief Converts std::vector to C API uint8_t array -/// -inline cldnn_uint8_t_arr uint8_t_vector_to_arr(const std::vector& stor) { - return {stor.data(), stor.size()}; -} - -/// -/// \brief Converts std::vector to C API tensor_array -/// -inline cldnn_tensor_arr tensor_vector_to_arr(const std::vector& stor) { - return cldnn_tensor_arr{stor.data(), stor.size()}; -} - -/// -/// \brief Converts C API tensor_array to std::vector of C API tensor -/// -inline std::vector tensor_arr_to_cldnn_vector(const cldnn_tensor_arr& arr) { - std::vector result(arr.size); - for (size_t i = 0; i < arr.size; i++) - result[i] = arr.data[i]; - - return result; -} - /// @} - /// @endcond - /// @} } // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/compounds.h b/inference-engine/thirdparty/clDNN/api/compounds.h new file mode 100644 index 00000000000000..ba6966fc7c7fe9 --- /dev/null +++ b/inference-engine/thirdparty/clDNN/api/compounds.h @@ -0,0 +1,101 @@ +/* +// Copyright (c) 2016 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "meta_utils.hpp" + +namespace cldnn { + +/// @addtogroup cpp_api C++ API +/// @{ + +/// @cond CPP_HELPERS + +/// @defgroup cpp_helpers Helpers +/// @{ + +template +class mutable_array_ref { +public: + typedef size_t size_type; + + mutable_array_ref() : _data(nullptr), _size(0) {} + explicit mutable_array_ref(T& val) : _data(&val), _size(1) {} + mutable_array_ref(T* data, size_t size) : _data(data), _size(size) {} + + template + explicit mutable_array_ref(T (&arr)[N]) : _data(arr), _size(N) {} + + mutable_array_ref(const mutable_array_ref& other) : _data(other._data), _size(other._size) {} + + mutable_array_ref& operator=(const mutable_array_ref& other) { + if (this == &other) + return *this; + _data = other._data; + _size = other._size; + return *this; + } + + T* data() const { return _data; } + size_t size() const { return _size; } + bool empty() const { return _size == 0; } + +#if defined(_SECURE_SCL) && (_SECURE_SCL > 0) + typedef stdext::checked_array_iterator iterator; + typedef stdext::checked_array_iterator const_iterator; + iterator begin() const { return stdext::make_checked_array_iterator(_data, _size); } + iterator end() const { return stdext::make_checked_array_iterator(_data, _size, _size); } + const_iterator cbegin() const { return stdext::make_checked_array_iterator(_data, _size); } + const_iterator cend() const { return stdext::make_checked_array_iterator(_data, _size, _size); } +#else + typedef T* iterator; + typedef T* const_iterator; + iterator begin() const { return _data; } + iterator end() const { return _data + _size; } + const_iterator cbegin() const { return _data; } + const_iterator cend() const { return _data + _size; } +#endif + + T& operator[](size_t idx) const { + assert(idx < _size); + return _data[idx]; + } + + T& at(size_t idx) const { + if (idx >= _size) throw std::out_of_range("idx"); + return _data[idx]; + } + + std::vector vector() const { return std::vector(_data, _data + _size); } + +private: + T* _data; + size_t _size; +}; + +/// @} + +/// @endcond + +/// @} +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/concatenation.hpp b/inference-engine/thirdparty/clDNN/api/concatenation.hpp similarity index 77% rename from inference-engine/thirdparty/clDNN/api/CPP/concatenation.hpp rename to inference-engine/thirdparty/clDNN/api/concatenation.hpp index 0bcd0a38e1a88f..9fe02502a927fc 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/concatenation.hpp +++ b/inference-engine/thirdparty/clDNN/api/concatenation.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/concatenation.h" #include "primitive.hpp" #include @@ -50,16 +49,16 @@ namespace cldnn { /// @li output : data structure holding output data for this primitive /// @li i.features : number of features in currently processed input /// @li outputIdx : index of destination feature -struct concatenation : public primitive_base { +struct concatenation : public primitive_base { CLDNN_DECLARE_PRIMITIVE(concatenation) enum concatenation_axis { - along_b = cldnn_concatenation_along_b, - along_f = cldnn_concatenation_along_f, - along_x = cldnn_concatenation_along_x, - along_y = cldnn_concatenation_along_y, - along_z = cldnn_concatenation_along_z, - along_w = cldnn_concatenation_along_w + along_b, + along_f, + along_x, + along_y, + along_z, + along_w }; /// @li Constructs concatenation primitive. @@ -73,17 +72,8 @@ struct concatenation : public primitive_base(dto->axis)) {} - /// @brief Dimension along which concatenation should take place concatenation_axis axis; - -private: - void update_dto(dto& dto) const override { - dto.axis = static_cast(axis); - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/condition.hpp b/inference-engine/thirdparty/clDNN/api/condition.hpp similarity index 81% rename from inference-engine/thirdparty/clDNN/api/CPP/condition.hpp rename to inference-engine/thirdparty/clDNN/api/condition.hpp index 68e4b9c1afff5a..19501ba2f0afbd 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/condition.hpp +++ b/inference-engine/thirdparty/clDNN/api/condition.hpp @@ -14,8 +14,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once - -#include "../C/condition.h" #include "primitive.hpp" #include "topology.hpp" #include @@ -36,7 +34,7 @@ enum cond_functions : int32_t { EQUAL, GREATER, LESS }; /// @n Applies comparision between 2 inputs. /// @n Compare data - sizes of that input specifes the range of the comparison. /// @n Offset - offset in memory, when comparing values. -struct condition : public primitive_base { +struct condition : public primitive_base { CLDNN_DECLARE_PRIMITIVE(condition) /// @brief Constructs condition primitive / layer. @@ -67,15 +65,6 @@ struct condition : public primitive_basetopology_true), - topology_false(dto->topology_false), - compare_data(dto->compare_data), - function(static_cast(dto->function)), - offset(dto->offset) {} - /// @brief An identifier of topology, which will be executed when comparison returns true. topology topology_true; /// @brief An identifier of topology, which will be executed when comparison returns false. @@ -88,14 +77,6 @@ struct condition : public primitive_base(function); - dto.offset = offset; - dto.topology_true = topology_true.get(); - dto.topology_false = topology_false.get(); - } - std::vector> get_dependencies() const override { return {compare_data}; } }; } // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/contract.hpp b/inference-engine/thirdparty/clDNN/api/contract.hpp similarity index 82% rename from inference-engine/thirdparty/clDNN/api/CPP/contract.hpp rename to inference-engine/thirdparty/clDNN/api/contract.hpp index 3db25c72dbd5de..9242b4e845e7af 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/contract.hpp +++ b/inference-engine/thirdparty/clDNN/api/contract.hpp @@ -14,8 +14,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once - -#include "../C/contract.h" #include "primitive.hpp" #include @@ -30,15 +28,15 @@ namespace cldnn { /// @brief Select mode for the @ref contract layer. enum class contract_mode : int32_t { /// @brief Sum reduction. - sum = cldnn_contract_sum, + sum, /// @brief Product reduction. - prod = cldnn_contract_product, + prod, /// @brief All reduction. - all = cldnn_contract_all, + all, /// @brief Any reduction. - any = cldnn_contract_any, + any, /// @brief Max reduction. - max = cldnn_contract_max + max }; /// @brief Reduces input with an operation defined by @p mode along defined @@ -63,7 +61,7 @@ enum class contract_mode : int32_t { /// @n - @p reduction_axes mustn't have duplicate values. /// @n - Values of @p reduction_axes must be within (inclusive) range 0 - 3 /// @n Breaking any of these conditions will raise an exception. -struct contract : public primitive_base { +struct contract : public primitive_base { CLDNN_DECLARE_PRIMITIVE(contract) /// @brief Constructs contract primitive / layer. @@ -85,25 +83,11 @@ struct contract : public primitive_base(dto->mode)), - reduction_axes(uint16_t_arr_to_vector(dto->reduction_axes)) { - } - /// @param mode Contract mode. contract_mode mode; /// @brief Array of axes positions from input shape (0-based, from left to right) /// along which reduction should happen. std::vector reduction_axes; - -protected: - void update_dto(dto& dto) const override { - dto.mode = static_cast(mode); - dto.reduction_axes = uint16_t_vector_to_arr(reduction_axes); - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/convolution.hpp b/inference-engine/thirdparty/clDNN/api/convolution.hpp similarity index 75% rename from inference-engine/thirdparty/clDNN/api/CPP/convolution.hpp rename to inference-engine/thirdparty/clDNN/api/convolution.hpp index 5143262055edbf..1d0b14e154ed1c 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/convolution.hpp +++ b/inference-engine/thirdparty/clDNN/api/convolution.hpp @@ -16,9 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/convolution.h" -#include "../C/deformable_interp.h" -#include "../C/deformable_conv.h" #include "primitive.hpp" #include @@ -33,7 +30,7 @@ namespace cldnn { /// @brief Performs forward spatial convolution with weight sharing. /// Also supports built-in Relu @CLDNN_PRIMITIVE_DESC{activation} available by setting it in arguments. /// @details Parameters are defined in context of "direct" convolution, but actual algorithm is not implied. -struct convolution : public primitive_base { +struct convolution : public primitive_base { CLDNN_DECLARE_PRIMITIVE(convolution) /// @brief Constructs convolution primitive. @@ -57,31 +54,23 @@ struct convolution : public primitive_base(0)), - _output_calibration_factors(std::vector(0)) { + weights(weights), + bias(bias), + weights_quantization_factors(std::vector(0)), + output_calibration_factors(std::vector(0)) { if ((bias.size() != 0) && (weights.size() != bias.size())) throw std::runtime_error("convolution's weights/bias count does not match"); } @@ -110,31 +99,23 @@ struct convolution : public primitive_base(0)), - _output_calibration_factors(std::vector(0)) { + weights(weights), + bias(bias), + weights_quantization_factors(std::vector(0)), + output_calibration_factors(std::vector(0)) { if ((bias.size() != 0) && (weights.size() != bias.size())) throw std::runtime_error("convolution's weights/bias count does not match"); } @@ -165,31 +146,23 @@ struct convolution : public primitive_base(0)), - _output_calibration_factors(std::vector(0)) { + weights(weights), + bias(bias), + weights_quantization_factors(std::vector(0)), + output_calibration_factors(std::vector(0)) { if ((bias.size() != 0) && (weights.size() != bias.size())) throw std::runtime_error("convolution's weights/bias count does not match"); } @@ -217,22 +190,14 @@ struct convolution : public primitive_base(0)), - _output_calibration_factors(std::vector(0)) { + weights(weights), + bias(bias), + weights_quantization_factors(std::vector(0)), + output_calibration_factors(std::vector(0)) { if ((bias.size() != 0) && (weights.size() != bias.size())) throw std::runtime_error("convolution's weights/bias count does not match"); } @@ -272,31 +237,23 @@ struct convolution : public primitive_base(0)), - _output_calibration_factors(std::vector(0)) { + weights(weights), + bias(bias), + weights_quantization_factors(std::vector(0)), + output_calibration_factors(std::vector(0)) { if ((bias.size() != 0) && (weights.size() != bias.size())) throw std::runtime_error("convolution's weights/bias count does not match"); if ((groups > 1) && ((weights.size() != 1) || ((bias.size() != 0) && (bias.size() != 1)))) @@ -331,31 +288,23 @@ struct convolution : public primitive_base(0)) { + weights(weights), + bias(bias), + weights_quantization_factors(w_quantization_factor), + output_calibration_factors(std::vector(0)) { if ((bias.size() != 0) && (weights.size() != bias.size())) throw std::runtime_error("convolution's weights/bias count does not match"); if ((weights.size() != 0) && (weights.size() != weights_quantization_factors.size())) @@ -389,30 +338,22 @@ struct convolution : public primitive_base(0)) { + weights(weights), + bias(bias), + weights_quantization_factors(quantization_factors), + output_calibration_factors(std::vector(0)) { if ((bias.size() != 0) && (weights.size() != bias.size())) throw std::runtime_error("convolution's weights/bias count does not match"); validate_quantized(); @@ -446,31 +387,23 @@ struct convolution : public primitive_base(0)), - _weights_quantization_factors(std::vector(0)), - _output_calibration_factors(std::vector(0)) {} + weights(weights), + bias(std::vector(0)), + weights_quantization_factors(std::vector(0)), + output_calibration_factors(std::vector(0)) {} /// @brief Constructs convolution primitive (w/o bias). /// @param id This primitive id. @@ -547,31 +472,23 @@ struct convolution : public primitive_base(0)), - _weights_quantization_factors(std::vector(0)), - _output_calibration_factors(std::vector(0)) {} + weights(weights), + bias(std::vector(0)), + weights_quantization_factors(std::vector(0)), + output_calibration_factors(std::vector(0)) {} /// @brief Constructs convolution primitive (w/o bias). /// @param id This primitive id. @@ -599,31 +516,23 @@ struct convolution : public primitive_base(0)), - _weights_quantization_factors(std::vector(0)), - _output_calibration_factors(std::vector(0)) {} + weights(weights), + bias(std::vector(0)), + weights_quantization_factors(std::vector(0)), + output_calibration_factors(std::vector(0)) {} /// @brief Constructs convolution primitive (w/o bias). /// @param id This primitive id. @@ -647,31 +556,23 @@ struct convolution : public primitive_base(0)), - _weights_quantization_factors(std::vector(0)), - _output_calibration_factors(std::vector(0)) {} + weights(weights), + bias(std::vector(0)), + weights_quantization_factors(std::vector(0)), + output_calibration_factors(std::vector(0)) {} /// @brief Constructs convolution primitive (computes input paddings to match output size). /// @param id This primitive id. @@ -696,22 +597,14 @@ struct convolution : public primitive_base(0)), - _output_calibration_factors(std::vector(0)) { + weights(weights), + bias(bias), + weights_quantization_factors(std::vector(0)), + output_calibration_factors(std::vector(0)) { if ((bias.size() != 0) && (weights.size() != bias.size())) throw std::runtime_error("convolution's weights/bias count does not match"); } @@ -748,22 +641,14 @@ struct convolution : public primitive_base(0)), - _weights_quantization_factors(std::vector(0)), - _output_calibration_factors(std::vector(0)) {} + weights(weights), + bias(std::vector(0)), + weights_quantization_factors(std::vector(0)), + output_calibration_factors(std::vector(0)) {} /* /// @brief Constructs convolution primitive. @@ -807,17 +692,11 @@ struct convolution : public primitive_base(0)), - _output_calibration_factors(std::vector(0)) { + weights(weights), + bias(bias), + weights_quantization_factors(std::vector(0)), + output_calibration_factors(std::vector(0)) { if ((bias.size() != 0) && (weights.size() != bias.size())) throw std::runtime_error("convolution's weights/bias count does not match"); if ((groups > 1) && ((weights.size() != 1) || ((bias.size() != 0) && (bias.size() != 1)))) throw std::runtime_error("grouped convolution's weights/bias count must be 1"); } - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{convolution} - convolution(const dto* dto) - : primitive_base(dto), - weights(_weights.cpp_ids), - bias(_bias.cpp_ids), - weights_quantization_factors(_weights_quantization_factors.cpp_ids), - output_calibration_factors(_output_calibration_factors.cpp_ids), - input_quantization_factor(dto->input_quantization_factor), - output_quantization_factor(dto->output_quantization_factor), - input_offset(dto->input_offset), - stride(dto->stride), - dilation(dto->dilation), - with_activation(dto->with_activation != 0), - activation_negative_slope(dto->activation_negative_slope), - with_output_size(dto->with_output_size != 0), - output_size(dto->output_size), - groups(dto->groups), - deformable_groups(dto->deformable_groups), - padding_above(dto->padding_above), - padding_below(dto->padding_below), - deformable_mode(dto->deformable_mode != 0), - _weights(dto->weights), - _bias(dto->bias), - _weights_quantization_factors(dto->weights_quantization_factors), - _output_calibration_factors(dto->output_calibration_factors) { - if (!dto->split || (weights.size() != bias.size() && bias.size() != 0) || dto->split != weights.size()) - throw std::invalid_argument("Invalid convolution dto: bad split value"); - } - /// @brief Constructs convolution primitive (computes input paddings to match output size). /// @param id This primitive id. /// @param input Input primitive id. @@ -889,8 +739,6 @@ struct convolution : public primitive_base(weights.size()); } -protected: - primitive_id_arr _weights; - primitive_id_arr _bias; - primitive_id_arr _weights_quantization_factors; - primitive_id_arr _output_calibration_factors; - std::vector> get_dependencies() const override { std::vector> ret; ret.reserve(weights.size() + bias.size() + weights_quantization_factors.size() + output_calibration_factors.size()); - for (auto& w : weights) ret.push_back(w); - for (auto& b : bias) ret.push_back(b); - for (auto& q : weights_quantization_factors) ret.push_back(q); - for (auto& q : output_calibration_factors) ret.push_back(q); + for (auto& w : weights) ret.push_back(std::ref(w)); + for (auto& b : bias) ret.push_back(ref(b)); + for (auto& q : weights_quantization_factors) ret.push_back(std::ref(q)); + for (auto& q : output_calibration_factors) ret.push_back(std::ref(q)); return ret; } - void update_dto(dto& dto) const override { - dto.weights = _weights.ref(); - dto.bias = _bias.ref(); - dto.weights_quantization_factors = _weights_quantization_factors.ref(); - dto.output_calibration_factors = _output_calibration_factors.ref(); - dto.input_quantization_factor = input_quantization_factor; - dto.output_quantization_factor = output_quantization_factor; - dto.input_offset = input_offset; - dto.stride = stride; - dto.split = split(); - dto.with_activation = with_activation; - dto.activation_negative_slope = activation_negative_slope; - dto.dilation = dilation; - dto.with_output_size = with_output_size; - dto.output_size = output_size; - dto.groups = groups; - dto.deformable_groups = deformable_groups; - dto.padding_above = padding_above; - dto.padding_below = padding_below; - dto.deformable_mode = deformable_mode ? 1 : 0; - } - private: // TODO: validate_quantized -> validate ? void validate_quantized() { @@ -1033,18 +843,13 @@ struct convolution : public primitive_base { +struct deformable_interp : public primitive_base { CLDNN_DECLARE_PRIMITIVE(deformable_interp) deformable_interp(const primitive_id& id, @@ -1082,31 +887,6 @@ struct deformable_interp : public primitive_baseinput_offset), - stride(dto->stride), - dilation(dto->dilation), - output_size(dto->output_size), - kernel_size(dto->kernel_size), - groups(dto->groups), - deformable_groups(dto->deformable_groups), - padding_above(dto->padding_above), - padding_below(dto->padding_below) { } - - void update_dto(dto& dto) const override { - dto.input_offset = input_offset; - dto.stride = stride; - dto.dilation = dilation; - dto.groups = groups; - dto.output_size = output_size; - dto.kernel_size = kernel_size; - dto.deformable_groups = deformable_groups; - dto.padding_above = padding_above; - dto.padding_below = padding_below; - } - /// @brief Defines a shift, relative to (0,0) position of the input buffer, where (0,0) point of the convolution window should start calculations. tensor input_offset; /// @brief Defines shift in input buffer between adjacent calculations of output values. @@ -1130,7 +910,7 @@ struct deformable_interp : public primitive_base { +struct deformable_conv : public primitive_base { CLDNN_DECLARE_PRIMITIVE(deformable_conv) deformable_conv(const primitive_id& id, @@ -1141,21 +921,19 @@ struct deformable_conv : public primitive_base(weights.size()); } @@ -1163,31 +941,10 @@ struct deformable_conv : public primitive_base> get_dependencies() const override { std::vector> ret; ret.reserve(weights.size() + bias.size()); - for (auto& w : weights) ret.push_back(w); - for (auto& b : bias) ret.push_back(b); + for (auto& w : weights) ret.push_back(std::ref(w)); + for (auto& b : bias) ret.push_back(std::ref(b)); return ret; } - - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{convolution} - deformable_conv(const dto* dto) - : primitive_base(dto), - weights(_weights.cpp_ids), - bias(_bias.cpp_ids), - output_size(dto->output_size), - groups(dto->groups), - _weights(dto->weights), - _bias(dto->bias) { - } - - void update_dto(dto& dto) const override { - dto.weights = _weights.ref(); - dto.bias = _bias.ref(); - dto.output_size = output_size; - dto.groups = groups; - } -protected: - primitive_id_arr _weights; - primitive_id_arr _bias; }; /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/convolution_grad_input.hpp b/inference-engine/thirdparty/clDNN/api/convolution_grad_input.hpp similarity index 94% rename from inference-engine/thirdparty/clDNN/api/CPP/convolution_grad_input.hpp rename to inference-engine/thirdparty/clDNN/api/convolution_grad_input.hpp index 9b691f6510df99..534aedb78d6970 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/convolution_grad_input.hpp +++ b/inference-engine/thirdparty/clDNN/api/convolution_grad_input.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/deconvolution.h" #include "deconvolution.hpp" #include "primitive.hpp" #include @@ -48,7 +47,7 @@ struct convolution_grad_input : public deconvolution { tensor stride = {1, 1, 1, 1}, tensor input_offset = {0, 0, 0, 0}, const padding& output_padding = padding()) - : deconvolution(id, input, {weights}, stride, input_offset, false, 0.0f, output_padding, true) {} + : deconvolution(id, input, {weights}, stride, input_offset, output_padding, true) {} /// @brief Constructs convolution_grad_input primitive (computes input paddings to match output size). /// @param id This primitive id. @@ -67,10 +66,7 @@ struct convolution_grad_input : public deconvolution { tensor input_offset, tensor output_size, const padding& output_padding = padding()) - : deconvolution(id, input, {weights}, stride, input_offset, false, 0.0f, output_size, output_padding, true) {} - - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{convolution_grad_input} - explicit convolution_grad_input(const dto* dto) : deconvolution(dto) {} + : deconvolution(id, input, {weights}, stride, input_offset, output_size, output_padding, true) {} /// @brief Constructs convolution_grad_input primitive (computes input paddings to match output size). /// @param id This primitive id. @@ -96,4 +92,4 @@ struct convolution_grad_input : public deconvolution { /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/convolution_grad_weights.hpp b/inference-engine/thirdparty/clDNN/api/convolution_grad_weights.hpp similarity index 75% rename from inference-engine/thirdparty/clDNN/api/CPP/convolution_grad_weights.hpp rename to inference-engine/thirdparty/clDNN/api/convolution_grad_weights.hpp index 30f6d842fe6e51..fa15fa73fca542 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/convolution_grad_weights.hpp +++ b/inference-engine/thirdparty/clDNN/api/convolution_grad_weights.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/convolution_grad_weights.h" #include "primitive.hpp" #include @@ -32,7 +31,7 @@ namespace cldnn { /// @details convolution_grad_weights updates weights and bias mutable data for training purposes. /// @details Please note that this primitive was not heavily tested and currently only batch=1 is enabled for this primitive. struct convolution_grad_weights - : public primitive_base { + : public primitive_base { CLDNN_DECLARE_PRIMITIVE(convolution_grad_weights) /// @brief Constructs convolution_grad_weights primitive. @@ -58,19 +57,15 @@ struct convolution_grad_weights const primitive_id& conv_grad = "", const padding& output_padding = padding()) : primitive_base(id, {input_grad, input}, output_padding), - weights(_weights.cpp_ids), - bias(_bias.cpp_ids), - prev_weights_grad(_prev_weights_grad.cpp_ids), - prev_bias_grad(_prev_bias_grad.cpp_ids), conv_grad(conv_grad), stride(stride), input_offset(input_offset), dilation(dilation), output_grad_w(false), - _weights(weights), - _bias(bias), - _prev_weights_grad(std::vector(0)), - _prev_bias_grad(std::vector(0)) {} + weights(weights), + bias(bias), + prev_weights_grad(std::vector(0)), + prev_bias_grad(std::vector(0)) {} /// @brief Constructs convolution_grad_weights primitive (w/o bias). /// @param id This primitive id. @@ -95,19 +90,15 @@ struct convolution_grad_weights const primitive_id& conv_grad = "", const padding& output_padding = padding()) : primitive_base(id, {input_grad, input}, output_padding), - weights(_weights.cpp_ids), - bias(_bias.cpp_ids), - prev_weights_grad(_prev_weights_grad.cpp_ids), - prev_bias_grad(_prev_bias_grad.cpp_ids), conv_grad(conv_grad), stride(stride), input_offset(input_offset), dilation(dilation), output_grad_w(output_grad_w), - _weights(weights), - _bias(std::vector(0)), - _prev_weights_grad(std::vector(0)), - _prev_bias_grad(std::vector(0)) {} + weights(weights), + bias(std::vector(0)), + prev_weights_grad(std::vector(0)), + prev_bias_grad(std::vector(0)) {} /// @brief Constructs convolution_grad_weights primitive (w/o bias). /// @param id This primitive id. @@ -130,19 +121,15 @@ struct convolution_grad_weights const primitive_id& conv_grad = "", const padding& output_padding = padding()) : primitive_base(id, {input_grad, input}, output_padding), - weights(_weights.cpp_ids), - bias(_bias.cpp_ids), - prev_weights_grad(_prev_weights_grad.cpp_ids), - prev_bias_grad(_prev_bias_grad.cpp_ids), conv_grad(conv_grad), stride(stride), input_offset(input_offset), dilation(dilation), output_grad_w(false), - _weights(weights), - _bias(std::vector(0)), - _prev_weights_grad(std::vector(0)), - _prev_bias_grad(std::vector(0)) {} + weights(weights), + bias(std::vector(0)), + prev_weights_grad(std::vector(0)), + prev_bias_grad(std::vector(0)) {} /// @brief Constructs convolution_grad_weights primitive with momentum optimizer. /// @param id This primitive id. @@ -171,50 +158,16 @@ struct convolution_grad_weights const primitive_id& conv_grad = "", const padding& output_padding = padding()) : primitive_base(id, {input_grad, input}, output_padding), - weights(_weights.cpp_ids), - bias(_bias.cpp_ids), - prev_weights_grad(_prev_weights_grad.cpp_ids), - prev_bias_grad(_prev_bias_grad.cpp_ids), conv_grad(conv_grad), stride(stride), input_offset(input_offset), dilation(dilation), output_grad_w(false), - _weights(weights), - _bias(bias), - _prev_weights_grad(prev_weights_grad), - _prev_bias_grad(prev_bias_grad) {} + weights(weights), + bias(bias), + prev_weights_grad(prev_weights_grad), + prev_bias_grad(prev_bias_grad) {} - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{convolution_grad_weights} - convolution_grad_weights(const dto* dto) - : primitive_base(dto), - weights(_weights.cpp_ids), - bias(_bias.cpp_ids), - prev_weights_grad(_prev_weights_grad.cpp_ids), - prev_bias_grad(_prev_bias_grad.cpp_ids), - conv_grad(dto->conv_grad), - stride(dto->stride), - input_offset(dto->input_offset), - dilation(dto->dilation), - output_grad_w(dto->output_grad_w), - _weights(dto->weights), - _bias(dto->bias), - _prev_weights_grad(dto->prev_weights_grad), - _prev_bias_grad(dto->prev_bias_grad) { - if (!dto->split || (weights.size() != bias.size() && bias.size() != 0) || dto->split != weights.size()) - throw std::invalid_argument("Invalid convolution_grad_weights dto: bad split value"); - } - - /// @brief List of primitive ids containing weights data. - fixed_size_vector_ref weights; - /// @brief List of primitive ids containing bias data. - fixed_size_vector_ref bias; - /// @brief Array of primitive ids containing weights gradient data calculated in previous iteration. - /// Amount of primitives and their memory sizes should be same as weights. - fixed_size_vector_ref prev_weights_grad; - /// @brief Array of primitive ids containing bias gradient data calculated in previous iteration. - /// Amount of primitives and their memory sizes should be same as biases. - fixed_size_vector_ref prev_bias_grad; /// @brief Primitive id containing convolution gradient data. primitive_id conv_grad; /// @brief Defines shift in input buffer between adjacent calculations of output values. @@ -228,45 +181,37 @@ struct convolution_grad_weights tensor dilation; /// @brief Should primitive give weights gradient (delta) as an output bool output_grad_w; + /// @brief List of primitive ids containing weights data. + const primitive_id_arr weights; + /// @brief List of primitive ids containing bias data. + const primitive_id_arr bias; + /// @brief Array of primitive ids containing weights gradient data calculated in previous iteration. + /// Amount of primitives and their memory sizes should be same as weights. + const primitive_id_arr prev_weights_grad; + /// @brief Array of primitive ids containing bias gradient data calculated in previous iteration. + /// Amount of primitives and their memory sizes should be same as biases. + const primitive_id_arr prev_bias_grad; /// @brief On how many cards split the computation to. int32_t split() const { return static_cast(weights.size()); } protected: - primitive_id_arr _weights; - primitive_id_arr _bias; - primitive_id_arr _prev_weights_grad; - primitive_id_arr _prev_bias_grad; - std::vector> get_dependencies() const override { std::vector> ret; ret.reserve(weights.size() + bias.size() + !conv_grad.empty() + prev_weights_grad.size() + prev_bias_grad.size()); - for (auto& w : weights) ret.push_back(w); - for (auto& b : bias) ret.push_back(b); + for (auto& w : weights) ret.push_back(std::ref(w)); + for (auto& b : bias) ret.push_back(std::ref(b)); - for (auto& g : prev_weights_grad) ret.push_back(g); - for (auto& g : prev_bias_grad) ret.push_back(g); + for (auto& g : prev_weights_grad) ret.push_back(std::ref(g)); + for (auto& g : prev_bias_grad) ret.push_back(std::ref(g)); if (!conv_grad.empty()) ret.push_back(conv_grad); return ret; } - - void update_dto(dto& dto) const override { - dto.weights = _weights.ref(); - dto.bias = _bias.ref(); - dto.input_offset = input_offset; - dto.dilation = dilation; - dto.split = split(); - dto.stride = stride; - dto.output_grad_w = output_grad_w; - dto.conv_grad = conv_grad.c_str(); - dto.prev_bias_grad = _prev_bias_grad.ref(); - dto.prev_weights_grad = _prev_weights_grad.ref(); - } }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/crop.hpp b/inference-engine/thirdparty/clDNN/api/crop.hpp similarity index 92% rename from inference-engine/thirdparty/clDNN/api/CPP/crop.hpp rename to inference-engine/thirdparty/clDNN/api/crop.hpp index 3a47f247ad5ca7..ef7aa6ecf2b0bb 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/crop.hpp +++ b/inference-engine/thirdparty/clDNN/api/crop.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/crop.h" #include "primitive.hpp" namespace cldnn { @@ -59,7 +58,7 @@ constexpr auto crop_borders = crop_borders_t{}; /// @n - Sum of sizes of opposite borders must be lower than input size (on all non-ignored dimensions). /// @n /// @n Breaking any of this conditions will cause exception throw. -struct crop : public primitive_base { +struct crop : public primitive_base { CLDNN_DECLARE_PRIMITIVE(crop) /// @brief Constructs crop primitive. @@ -111,19 +110,10 @@ struct crop : public primitive_base { const padding& output_padding = padding()) : primitive_base(id, {input}, output_padding), reference_input(xy_borders.negate()), offsets(xy_borders) {} - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{crop} - crop(const dto* dto) : primitive_base(dto), reference_input(dto->reference_input), offsets(dto->offsets) {} - /// @brief Reference input tensor with the required dimensions. tensor reference_input; /// @brief Input offsets. tensor offsets; - -protected: - void update_dto(dto& dto) const override { - dto.reference_input = reference_input; - dto.offsets = offsets; - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/custom_gpu_primitive.hpp b/inference-engine/thirdparty/clDNN/api/custom_gpu_primitive.hpp similarity index 67% rename from inference-engine/thirdparty/clDNN/api/CPP/custom_gpu_primitive.hpp rename to inference-engine/thirdparty/clDNN/api/custom_gpu_primitive.hpp index 26192378b6d9cb..2a729145752e31 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/custom_gpu_primitive.hpp +++ b/inference-engine/thirdparty/clDNN/api/custom_gpu_primitive.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/custom_gpu_primitive.h" #include "primitive.hpp" #include "memory.hpp" #include @@ -33,9 +32,24 @@ namespace cldnn { /// @brief This primitive executes a custom kernel provided by the application /// @details The application is required to provide all relevant details for executing the custom kernel /// such as: sources, entry point, work sizes and parameter bindings. -struct custom_gpu_primitive : public primitive_base { +struct custom_gpu_primitive : public primitive_base { CLDNN_DECLARE_PRIMITIVE(custom_gpu_primitive) + /// @brief Custom primitive kernel argument type + enum arg_type { + arg_input, + arg_output, + }; + // + /// @brief Custom primitive kernel argument index + using arg_index = uint32_t; + // + /// @brief Custom primitive kernel argument description + struct arg_desc { + arg_type type; + arg_index index; + }; + /// @brief Constructs custom_gpu_primitive primitive /// @param id This primitive id. /// @param input Input primitive ids. @@ -50,39 +64,24 @@ struct custom_gpu_primitive : public primitive_base& input, const std::vector& kernels_code, const std::string& kernel_entry_point, - const std::vector& kernel_arguments, + const std::vector& kernel_arguments, const std::string& build_options, const layout& output_layout, const std::vector& gws = {}, const std::vector& lws = {}) : primitive_base(id, {input}, output_layout.data_padding), - kernels_code(_kernels_code.cpp_ids), kernel_entry_point(kernel_entry_point), kernel_arguments(kernel_arguments), build_options(build_options), output_layout(output_layout), gws(gws.size() ? gws : std::vector{output_layout.count()}), lws(lws), - _kernels_code(kernels_code) {} - - /// @brief Constructs a copy from basic C API @CLDNN_PRIMITIVE_DESC{custom_gpu_primitive} - custom_gpu_primitive(const dto* dto) - : primitive_base(dto), - kernels_code(_kernels_code.cpp_ids), - kernel_entry_point(dto->kernel_entry_point), - kernel_arguments(dto->kernel_arguments, dto->kernel_arguments + dto->kernel_arguments_num), - build_options(dto->build_options), - output_layout(dto->output_layout), - gws(dto->gws, dto->gws + dto->gws_num), - lws(dto->lws, dto->lws + dto->lws_num), - _kernels_code(dto->kernels_code) {} + kernels_code(kernels_code) {} - /// @brief Source code for the kernel - fixed_size_vector_ref kernels_code; /// @brief The name of the entry point function in the kernel const std::string kernel_entry_point; /// @brief Argument bindings for the entry point function - const std::vector kernel_arguments; + const std::vector kernel_arguments; /// @brief The kernel's build options const std::string build_options; /// @brief The output layout declared by the primitive @@ -91,22 +90,8 @@ struct custom_gpu_primitive : public primitive_base gws; /// @brief The local working sizes const std::vector lws; - -protected: - primitive_id_arr _kernels_code; - - void update_dto(dto& dto) const override { - dto.kernels_code = _kernels_code.ref(); - dto.kernel_entry_point = kernel_entry_point.c_str(); - dto.kernel_arguments = kernel_arguments.data(); - dto.kernel_arguments_num = static_cast(kernel_arguments.size()); - dto.build_options = build_options.c_str(); - dto.output_layout = (cldnn_layout)output_layout; - dto.gws = gws.data(); - dto.gws_num = static_cast(gws.size()); - dto.lws = lws.data(); - dto.lws_num = static_cast(lws.size()); - } + /// @brief Source code for the kernel + const primitive_id_arr kernels_code; }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/data.hpp b/inference-engine/thirdparty/clDNN/api/data.hpp similarity index 83% rename from inference-engine/thirdparty/clDNN/api/CPP/data.hpp rename to inference-engine/thirdparty/clDNN/api/data.hpp index d663df975da383..133c046a9dbef0 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/data.hpp +++ b/inference-engine/thirdparty/clDNN/api/data.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/data.h" #include "primitive.hpp" #include "memory.hpp" @@ -32,7 +31,7 @@ namespace cldnn { /// @details This primitive allows to pass data which is known at topology creation. /// For example, weights and biases for scoring networks. /// @note Passing data at topology may improve network performance if data optimization is enabled. -struct data : public primitive_base { +struct data : public primitive_base { CLDNN_DECLARE_PRIMITIVE(data) /// @brief Constructs data primitive. @@ -42,20 +41,9 @@ struct data : public primitive_base { data(const primitive_id& id, const memory& mem) : primitive_base(id, {}, padding()), mem(mem) {} - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{data} - explicit data(const dto* dto) - : primitive_base(dto), mem(dto->mem) { - mem.retain(); - } - /// @brief @ref memory object which contains data. /// @note If memory is attached by memory::attach(), the attached buffer should be valid till network build. memory mem; - -protected: - void update_dto(dto& dto) const override { - dto.mem = mem.get(); - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/deconvolution.hpp b/inference-engine/thirdparty/clDNN/api/deconvolution.hpp similarity index 77% rename from inference-engine/thirdparty/clDNN/api/CPP/deconvolution.hpp rename to inference-engine/thirdparty/clDNN/api/deconvolution.hpp index a68e583465c39c..a506a0850fdaaa 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/deconvolution.hpp +++ b/inference-engine/thirdparty/clDNN/api/deconvolution.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/deconvolution.h" #include "primitive.hpp" #include @@ -32,9 +31,8 @@ namespace cldnn { /// Also supports built-in Relu @ref activation available by setting it in arguments. /// @details Deconvolution is similar to convolution layer with the weights flipped on the axis /// and stride and input padding parameters used in opposite sense as in convolution. -struct deconvolution : public primitive_base { +struct deconvolution : public primitive_base { CLDNN_DECLARE_PRIMITIVE(deconvolution) - /// @brief Constructs deconvolution primitive. /// @param id This primitive id. /// @param input Input primitive id. @@ -51,20 +49,14 @@ struct deconvolution : public primitive_base& bias, tensor stride = {1, 1, 1, 1}, tensor input_offset = {0, 0, 0, 0}, - bool with_activation = false, - float activation_slp = 0.0f, const padding& output_padding = padding()) : primitive_base(id, {input}, output_padding), - weights(_weights.cpp_ids), - bias(_bias.cpp_ids), input_offset(input_offset), stride(stride), - with_activation(with_activation), - activation_negative_slope(activation_slp), with_output_size(false), groups(1), - _weights(weights), - _bias(bias), + weights(weights), + bias(bias), _gradient(false) {} /// @brief Constructs deconvolution primitive. /// @param id This primitive id. @@ -84,20 +76,14 @@ struct deconvolution : public primitive_base& weights, tensor stride = {1, 1, 1, 1}, tensor input_offset = {0, 0, 0, 0}, - bool with_activation = false, - float activation_slp = 0.0f, const padding& output_padding = padding(), bool gradient = false) : primitive_base(id, {input}, output_padding), - weights(_weights.cpp_ids), - bias(_bias.cpp_ids), input_offset(input_offset), stride(stride), - with_activation(with_activation), - activation_negative_slope(activation_slp), with_output_size(false), groups(1), - _weights(weights), - _bias(std::vector(0)), + weights(weights), + bias(std::vector(0)), _gradient(gradient) {} /// @brief Constructs deconvolution primitive (w/o bias). @@ -147,21 +127,15 @@ struct deconvolution : public primitive_base(0)), + weights(weights), + bias(std::vector(0)), _gradient(gradient) {} /// @brief Constructs deconvolution primitive (computes input paddings to match output size). @@ -181,22 +155,16 @@ struct deconvolution : public primitive_base& bias, tensor stride, tensor input_offset, - bool with_activation, - float activation_slp, tensor output_size, const padding& output_padding = padding()) : primitive_base(id, {input}, output_padding), - weights(_weights.cpp_ids), - bias(_bias.cpp_ids), input_offset(input_offset), stride(stride), - with_activation(with_activation), - activation_negative_slope(activation_slp), with_output_size(true), output_size(output_size), groups(1), - _weights(weights), - _bias(bias), + weights(weights), + bias(bias), _gradient(false) {} /// @brief Constructs deconvolution primitive (computes input paddings to match output size). @@ -218,22 +186,16 @@ struct deconvolution : public primitive_base& weights, tensor stride, tensor input_offset, - bool with_activation, - float activation_slp, tensor output_size, const padding& output_padding = padding(), bool gradient = false) : primitive_base(id, {input}, output_padding), - weights(_weights.cpp_ids), - bias(_bias.cpp_ids), input_offset(input_offset), stride(stride), - with_activation(with_activation), - activation_negative_slope(activation_slp), with_output_size(true), output_size(output_size), groups(1), - _weights(weights), - _bias(std::vector(0)), + weights(weights), + bias(std::vector(0)), _gradient(gradient) {} - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{deconvolution} - deconvolution(const dto* dto) - : primitive_base(dto), - weights(_weights.cpp_ids), - bias(_bias.cpp_ids), - input_offset(dto->input_offset), - stride(dto->stride), - with_activation(dto->with_activation != 0), - activation_negative_slope(dto->activation_negative_slope), - with_output_size(dto->with_output_size != 0), - output_size(dto->output_size), - groups(dto->groups), - _weights(dto->weights), - _bias(dto->bias), - _gradient(dto->gradient != 0) { - if (!dto->split || (weights.size() != bias.size() && bias.size() != 0) || dto->split != weights.size()) - throw std::invalid_argument("Invalid deconvolution dto: bad split value"); - } - /// @brief Constructs deconvolution primitive (computes input paddings to match output size). /// @param id This primitive id. /// @param input Input primitive id. @@ -308,8 +245,6 @@ struct deconvolution : public primitive_base(weights.size()); } @@ -379,32 +304,16 @@ struct deconvolution : public primitive_base> get_dependencies() const override { std::vector> ret; ret.reserve(weights.size() + bias.size()); - for (auto& w : weights) ret.push_back(w); - for (auto& b : bias) ret.push_back(b); + for (auto& w : weights) ret.push_back(std::ref(w)); + for (auto& b : bias) ret.push_back(std::ref(b)); return ret; } - - void update_dto(dto& dto) const override { - dto.weights = _weights.ref(); - dto.bias = _bias.ref(); - dto.input_offset = input_offset; - dto.split = split(); - dto.stride = stride; - dto.with_activation = with_activation; - dto.activation_negative_slope = activation_negative_slope; - dto.with_output_size = with_output_size; - dto.output_size = output_size; - dto.gradient = _gradient; - dto.groups = groups; - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/depth_to_space.hpp b/inference-engine/thirdparty/clDNN/api/depth_to_space.hpp similarity index 79% rename from inference-engine/thirdparty/clDNN/api/CPP/depth_to_space.hpp rename to inference-engine/thirdparty/clDNN/api/depth_to_space.hpp index 8719d2fb190fd5..0c6257a09fef57 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/depth_to_space.hpp +++ b/inference-engine/thirdparty/clDNN/api/depth_to_space.hpp @@ -16,8 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once - -#include "../C/depth_to_space.h" #include "primitive.hpp" namespace cldnn { @@ -30,7 +28,7 @@ namespace cldnn { /// @brief /// @details -struct depth_to_space : public primitive_base { +struct depth_to_space : public primitive_base { CLDNN_DECLARE_PRIMITIVE(depth_to_space) /// @brief Constructs depth_to_space primitive. @@ -43,14 +41,8 @@ struct depth_to_space : public primitive_baseblock_size) {} - /// @brief Block size. size_t block_size; - -protected: - void update_dto(dto& dto) const override { dto.block_size = block_size; } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/detection_output.hpp b/inference-engine/thirdparty/clDNN/api/detection_output.hpp similarity index 74% rename from inference-engine/thirdparty/clDNN/api/CPP/detection_output.hpp rename to inference-engine/thirdparty/clDNN/api/detection_output.hpp index 719fa89b3674da..6df38c1cee78a7 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/detection_output.hpp +++ b/inference-engine/thirdparty/clDNN/api/detection_output.hpp @@ -17,8 +17,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once #include -#include "../C/detection_output.h" -#include "../C/detection_output_sort.h" #include "primitive.hpp" namespace cldnn { @@ -31,15 +29,15 @@ namespace cldnn { /// @brief Select method for coding the prior-boxes in the @ref detection output layer. enum class prior_box_code_type : int32_t { - corner = cldnn_code_type_corner, - center_size = cldnn_code_type_center_size, - corner_size = cldnn_code_type_corner_size + corner, + center_size, + corner_size }; /// @brief Generates a list of detections based on location and confidence predictions by doing non maximum suppression. /// @details Each row is a 7 dimension vector, which stores: [image_id, label, confidence, xmin, ymin, xmax, ymax]. /// If number of detections per image is lower than keep_top_k, will write dummy results at the end with image_id=-1. -struct detection_output : public primitive_base { +struct detection_output : public primitive_base { CLDNN_DECLARE_PRIMITIVE(detection_output) /// @brief Constructs detection output primitive. @@ -104,32 +102,6 @@ struct detection_output : public primitive_basenum_classes), - keep_top_k(dto->keep_top_k), - share_location(dto->share_location != 0), - background_label_id(dto->background_label_id), - nms_threshold(dto->nms_threshold), - top_k(dto->top_k), - eta(dto->eta), - code_type(static_cast(dto->code_type)), - variance_encoded_in_target(dto->variance_encoded_in_target != 0), - confidence_threshold(dto->confidence_threshold), - prior_info_size(dto->prior_info_size), - prior_coordinates_offset(dto->prior_coordinates_offset), - prior_is_normalized(dto->prior_is_normalized != 0), - input_width(dto->input_width), - input_height(dto->input_height), - decrease_label_id(dto->decrease_label_id != 0), - clip_before_nms(dto->clip_before_nms != 0), - clip_after_nms(dto->clip_after_nms != 0) { - if (decrease_label_id && background_label_id != 0) - throw std::invalid_argument( - "Cannot use decrease_label_id and background_label_id parameter simultaneously."); - } - /// @brief Number of classes to be predicted. const uint32_t num_classes; /// @brief Number of total bounding boxes to be kept per image after NMS step. @@ -168,33 +140,13 @@ struct detection_output : public primitive_base(code_type); - dto.variance_encoded_in_target = variance_encoded_in_target; - dto.keep_top_k = keep_top_k; - dto.confidence_threshold = confidence_threshold; - dto.prior_info_size = prior_info_size; - dto.prior_coordinates_offset = prior_coordinates_offset; - dto.prior_is_normalized = prior_is_normalized; - dto.input_width = input_width; - dto.input_height = input_height; - dto.decrease_label_id = decrease_label_id; - dto.clip_before_nms = clip_before_nms; - dto.clip_after_nms = clip_after_nms; - } }; /// @brief Generates a list of detections based on location and confidence predictions by doing non maximum suppression. /// @details Each row is a 7 dimension vector, which stores: [image_id, label, confidence, xmin, ymin, xmax, ymax]. /// If number of detections per image is lower than keep_top_k, will write dummy results at the end with image_id=-1. struct detection_output_sort - : public primitive_base { + : public primitive_base { CLDNN_DECLARE_PRIMITIVE(detection_output_sort) /// @brief Constructs detection output primitive. @@ -223,16 +175,6 @@ struct detection_output_sort top_k(top_k), background_label_id(background_label_id) {} - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{detection_output} - detection_output_sort(const dto* dto) - : primitive_base(dto), - num_images(dto->num_images), - num_classes(dto->num_classes), - keep_top_k(dto->keep_top_k), - share_location(dto->share_location != 0), - top_k(dto->top_k), - background_label_id(dto->background_label_id) {} - /// @brief Number of classes to be predicted. const uint32_t num_images; /// @brief Number of classes to be predicted. @@ -245,16 +187,6 @@ struct detection_output_sort const int top_k; /// @brief Background label id (-1 if there is no background class). const int background_label_id; - -protected: - void update_dto(dto& dto) const override { - dto.num_classes = num_classes; - dto.num_images = num_images; - dto.keep_top_k = keep_top_k; - dto.share_location = share_location; - dto.top_k = top_k; - dto.background_label_id = background_label_id; - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/eltwise.hpp b/inference-engine/thirdparty/clDNN/api/eltwise.hpp similarity index 65% rename from inference-engine/thirdparty/clDNN/api/CPP/eltwise.hpp rename to inference-engine/thirdparty/clDNN/api/eltwise.hpp index 55e72ab60fa518..93cd3fa67eb267 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/eltwise.hpp +++ b/inference-engine/thirdparty/clDNN/api/eltwise.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/eltwise.h" #include "primitive.hpp" #include @@ -31,43 +30,43 @@ namespace cldnn { /// @brief Select mode for the @ref eltwise layer. enum class eltwise_mode : int32_t { /// @brief Eltwise sum. - sum = cldnn_eltwise_sum, + sum, /// @brief Eltwise subtract. - sub = cldnn_eltwise_sub, + sub, /// @brief Eltwise max. - max = cldnn_eltwise_max, + max, /// @brief Eltwise product (Hadamard). - prod = cldnn_eltwise_prod, + prod, /// @brief Eltwise div. - div = cldnn_eltwise_div, + div, /// @brief Eltwise min. - min = cldnn_eltwise_min, + min, /// @brief Eltwise pow. - pow = cldnn_eltwise_pow, + pow, /// @brief Eltwise squared diff. - squared_diff = cldnn_eltwise_squared_diff, + squared_diff, /// @brief Eltwise mod. - mod = cldnn_eltwise_mod, + mod, /// @brief Eltwise equal. - eq = cldnn_eltwise_eq, + eq, /// @brief Eltwise not equal. - ne = cldnn_eltwise_ne, + ne, /// @brief Eltwise less. - lt = cldnn_eltwise_lt, + lt, /// @brief Eltwise less of equal. - le = cldnn_eltwise_le, + le, /// @brief Eltwise greater. - gt = cldnn_eltwise_gt, + gt, /// @brief Eltwise greater or equal. - ge = cldnn_eltwise_ge, + ge, /// @brief Eltwise and. - logic_and = cldnn_eltwise_and, + logic_and, /// @brief Eltwise or. - logic_or = cldnn_eltwise_or, + logic_or, /// @brief Eltwise XOR. - logic_xor = cldnn_eltwise_xor, + logic_xor, /// @brief Eltwise floormod. - floor_mod = cldnn_eltwise_floor_mod + floor_mod }; /// @brief Performs elementwise operations (sum, subtract, max or product) on two input primitives @@ -77,7 +76,7 @@ enum class eltwise_mode : int32_t { /// to the same shape in which the size of each dimention is a max. of input sizes on this dimension) /// - format of both inputs has to be the same /// - when using integer types, only following eltwise modes are supported: sum, sub, prod, div -struct eltwise : public primitive_base { +struct eltwise : public primitive_base { CLDNN_DECLARE_PRIMITIVE(eltwise) /// @brief Constructs eltwise primitive. @@ -91,21 +90,15 @@ struct eltwise : public primitive_base { const primitive_id& input, const primitive_id& input2, eltwise_mode mode, - bool with_activation = false, - float activation_slp = 0.0f, const padding& output_padding = padding()) : primitive_base(id, {input, input2}, output_padding), output_calibration_factors(""), output_quantization_factor(1.0f), - inputs_calibration_factors(_inputs_calibration_factors.cpp_ids), input_quantization_factors(0), mode(mode), coefficients(std::vector(0)), - with_activation(with_activation), - activation_negative_slope(activation_slp), stride(std::vector(0)), - _inputs_calibration_factors(std::vector(0)), - _stride(tensor_vector_to_cldnn_vector(stride)) {} + inputs_calibration_factors(std::vector(0)) {} /// @brief Constructs eltwise primitive. /// @param id This primitive id. @@ -120,21 +113,15 @@ struct eltwise : public primitive_base { const primitive_id& input2, std::vector stride, eltwise_mode mode, - bool with_activation = false, - float activation_slp = 0.0f, const padding& output_padding = padding()) : primitive_base(id, {input, input2}, output_padding), output_calibration_factors(""), output_quantization_factor(1.0f), - inputs_calibration_factors(_inputs_calibration_factors.cpp_ids), input_quantization_factors(0), mode(mode), coefficients(std::vector(0)), - with_activation(with_activation), - activation_negative_slope(activation_slp), stride(stride), - _inputs_calibration_factors(std::vector(0)), - _stride(tensor_vector_to_cldnn_vector(stride)) {} + inputs_calibration_factors(std::vector(0)) {} /// @brief Constructs eltwise primitive. /// @param id This primitive id. @@ -145,21 +132,15 @@ struct eltwise : public primitive_base { eltwise(const primitive_id& id, const std::vector& inputs, eltwise_mode mode, - bool with_activation = false, - float activation_slp = 0.0f, const padding& output_padding = padding()) : primitive_base(id, inputs, output_padding), output_calibration_factors(""), output_quantization_factor(1.0f), - inputs_calibration_factors(_inputs_calibration_factors.cpp_ids), input_quantization_factors(0), mode(mode), coefficients(std::vector(0)), - with_activation(with_activation), - activation_negative_slope(activation_slp), stride(std::vector(0)), - _inputs_calibration_factors(std::vector(0)), - _stride(tensor_vector_to_cldnn_vector(stride)) {} + inputs_calibration_factors(std::vector(0)) {} /// @brief Constructs eltwise primitive. /// @param id This primitive id. @@ -174,21 +155,15 @@ struct eltwise : public primitive_base { const primitive_id& input2, const primitive_id& output_calibration_factors, eltwise_mode mode, - bool with_activation = false, - float activation_slp = 0.0f, const padding& output_padding = padding()) : primitive_base(id, {input, input2}, output_padding), output_calibration_factors(output_calibration_factors), output_quantization_factor(1.0f), - inputs_calibration_factors(_inputs_calibration_factors.cpp_ids), input_quantization_factors(0), mode(mode), coefficients(std::vector(0)), - with_activation(with_activation), - activation_negative_slope(activation_slp), stride(std::vector(0)), - _inputs_calibration_factors(std::vector(0)), - _stride(tensor_vector_to_cldnn_vector(stride)) {} + inputs_calibration_factors(std::vector(0)) {} /// @brief Constructs eltwise primitive. /// @param id This primitive id. @@ -201,21 +176,15 @@ struct eltwise : public primitive_base { const std::vector& inputs, const primitive_id& output_calibration_factors, eltwise_mode mode, - bool with_activation = false, - float activation_slp = 0.0f, const padding& output_padding = padding()) : primitive_base(id, inputs, output_padding), output_calibration_factors(output_calibration_factors), output_quantization_factor(1.0f), - inputs_calibration_factors(_inputs_calibration_factors.cpp_ids), input_quantization_factors(0), mode(mode), coefficients(std::vector(0)), - with_activation(with_activation), - activation_negative_slope(activation_slp), stride(std::vector(0)), - _inputs_calibration_factors(std::vector(0)), - _stride(tensor_vector_to_cldnn_vector(stride)) {} + inputs_calibration_factors(std::vector(0)) {} /// @brief Constructs eltwise primitive. /// @param id This primitive id. @@ -230,21 +199,15 @@ struct eltwise : public primitive_base { const primitive_id& input2, const float o_quantization_factor, eltwise_mode mode, - bool with_activation = false, - float activation_slp = 0.0f, const padding& output_padding = padding()) : primitive_base(id, {input, input2}, output_padding), output_calibration_factors(""), output_quantization_factor(o_quantization_factor), - inputs_calibration_factors(_inputs_calibration_factors.cpp_ids), input_quantization_factors(0), mode(mode), coefficients(std::vector(0)), - with_activation(with_activation), - activation_negative_slope(activation_slp), stride(std::vector(0)), - _inputs_calibration_factors(std::vector(0)), - _stride(tensor_vector_to_cldnn_vector(stride)) {} + inputs_calibration_factors(std::vector(0)) {} /// @brief Constructs eltwise primitive. /// @param id This primitive id. @@ -257,21 +220,15 @@ struct eltwise : public primitive_base { const std::vector& inputs, const float o_quantization_factor, eltwise_mode mode, - bool with_activation = false, - float activation_slp = 0.0f, const padding& output_padding = padding()) : primitive_base(id, inputs, output_padding), output_calibration_factors(""), output_quantization_factor(o_quantization_factor), - inputs_calibration_factors(_inputs_calibration_factors.cpp_ids), input_quantization_factors(0), mode(mode), coefficients(std::vector(0)), - with_activation(with_activation), - activation_negative_slope(activation_slp), stride(std::vector(0)), - _inputs_calibration_factors(std::vector(0)), - _stride(tensor_vector_to_cldnn_vector(stride)) {} + inputs_calibration_factors(std::vector(0)) {} /// @brief Constructs eltwise primitive. /// @param id This primitive id. @@ -284,21 +241,15 @@ struct eltwise : public primitive_base { const std::vector& inputs, eltwise_mode mode, const std::vector& coefficients, - bool with_activation = false, - float activation_slp = 0.0f, const padding& output_padding = padding()) : primitive_base(id, inputs, output_padding), output_calibration_factors(""), output_quantization_factor(1.0f), - inputs_calibration_factors(_inputs_calibration_factors.cpp_ids), input_quantization_factors(0), mode(mode), coefficients(coefficients), - with_activation(with_activation), - activation_negative_slope(activation_slp), stride(std::vector(0)), - _inputs_calibration_factors(std::vector(0)), - _stride(tensor_vector_to_cldnn_vector(stride)) { + inputs_calibration_factors(std::vector(0)) { if (mode == eltwise_mode::sum && !coefficients.empty() && coefficients.size() != inputs.size()) { throw std::invalid_argument("Invalid eltwise sum coefficients count (should be equal to 0 or input.size)"); } @@ -307,70 +258,31 @@ struct eltwise : public primitive_base { } } - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{eltwise} - eltwise(const dto* dto) - : primitive_base(dto), - output_calibration_factors(dto->output_calibration_factors), - output_quantization_factor(dto->output_quantization_factor), - inputs_calibration_factors(_inputs_calibration_factors.cpp_ids), - input_quantization_factors(float_arr_to_vector(dto->input_quantization_factors)), - mode(static_cast(dto->mode)), - coefficients(float_arr_to_vector(dto->coefficients)), - with_activation(dto->with_activation != 0), - activation_negative_slope(dto->activation_negative_slope), - stride(tensor_arr_to_vector(dto->stride)), - _inputs_calibration_factors(dto->input_calibration_factors), - _stride(tensor_vector_to_cldnn_vector(stride)) { - if (dto->input.size < 2) - throw std::invalid_argument("eltiwise dto should containt at least two inputs"); - if (dto->coefficients.size != 0 && dto->coefficients.size != dto->input.size) - throw std::invalid_argument( - "Invalid eltwise coefficients count in dto (should be equal to 0 or input.size)"); - } - /// @brief Primitive id containing output quanitization factors per output feature map. primitive_id output_calibration_factors; /// @brief Output quantization factor float output_quantization_factor; - /// @brief List of primitive ids containing input quantization factors per feature map, one primitive id for each input. - fixed_size_vector_ref inputs_calibration_factors; /// @brief List of quantization factors per input. std::vector input_quantization_factors; /// @param mode Eltwise mode. eltwise_mode mode; /// @param coefficients Blob-wise coefficient for SUM operation. std::vector coefficients; - /// @brief Enables Relu activation. - bool with_activation; - /// @brief Relu activation slope. - float activation_negative_slope; /// @brief Defines shift in input buffers between adjacent calculations of output values. std::vector stride; + /// @brief List of primitive ids containing input quantization factors per feature map, one primitive id for each input. + const primitive_id_arr inputs_calibration_factors; protected: - primitive_id_arr _inputs_calibration_factors; - std::vector _stride; std::vector> get_dependencies() const override { std::vector> ret; if (!output_calibration_factors.empty()) ret.push_back(output_calibration_factors); - for (auto& icf : inputs_calibration_factors) ret.push_back(icf); + for (auto& icf : inputs_calibration_factors) ret.push_back(std::ref(icf)); return ret; } - - void update_dto(dto& dto) const override { - dto.output_calibration_factors = output_calibration_factors.c_str(); - dto.output_quantization_factor = output_quantization_factor; - dto.input_calibration_factors = _inputs_calibration_factors.ref(); - dto.input_quantization_factors = float_vector_to_arr(input_quantization_factors); - dto.mode = static_cast(mode); - dto.coefficients = float_vector_to_arr(coefficients); - dto.with_activation = with_activation; - dto.activation_negative_slope = activation_negative_slope; - dto.stride = tensor_vector_to_arr(_stride); - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/embed.hpp b/inference-engine/thirdparty/clDNN/api/embed.hpp similarity index 85% rename from inference-engine/thirdparty/clDNN/api/CPP/embed.hpp rename to inference-engine/thirdparty/clDNN/api/embed.hpp index 7082be18a0cac7..91a66e32b441db 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/embed.hpp +++ b/inference-engine/thirdparty/clDNN/api/embed.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/embed.h" #include "primitive.hpp" #include @@ -36,7 +35,7 @@ namespace cldnn { /// @n output_size = { 8, 75, 15, 1 }; /// @par Algorithm: /// @par Where: -struct embed : public primitive_base { +struct embed : public primitive_base { CLDNN_DECLARE_PRIMITIVE(embed) /// @brief Constructs embed primitive. @@ -60,11 +59,6 @@ struct embed : public primitive_base { const primitive_id& weights) : primitive_base(id, {input}), weights(weights), bias("") {} - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{embed} - embed(const dto* dto) - : primitive_base(dto), weights(dto->weights), bias(dto->bias) { - } - /// @brief Primitive id containing weights data. primitive_id weights; /// @brief Primitive id containing bias data. @@ -77,14 +71,9 @@ struct embed : public primitive_base { else return {weights, bias}; } - - void update_dto(dto& dto) const override { - dto.weights = weights.c_str(); - dto.bias = bias.c_str(); - } }; /// @} /// @} /// @} } // namespace cldnn -#pragma once \ No newline at end of file +#pragma once diff --git a/inference-engine/thirdparty/clDNN/api/CPP/engine.hpp b/inference-engine/thirdparty/clDNN/api/engine.hpp similarity index 61% rename from inference-engine/thirdparty/clDNN/api/CPP/engine.hpp rename to inference-engine/thirdparty/clDNN/api/engine.hpp index fea46a2a1f0448..0c70122c4715a7 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/engine.hpp +++ b/inference-engine/thirdparty/clDNN/api/engine.hpp @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "cldnn_defs.h" +#include "cldnn.hpp" #include namespace cldnn { @@ -29,23 +29,23 @@ namespace cldnn { /// @brief Defines available engine types enum class engine_types : int32_t { - ocl = cldnn_engine_ocl + ocl }; /// @brief Defines available priority mode types enum class priority_mode_types : int16_t { - disabled = cldnn_priority_disabled, - low = cldnn_priority_low, - med = cldnn_priority_med, - high = cldnn_priority_high + disabled, + low, + med, + high }; /// @brief Defines available priority mode types enum class throttle_mode_types : int16_t { - disabled = cldnn_throttle_disabled, - low = cldnn_throttle_low, - med = cldnn_throttle_med, - high = cldnn_throttle_high + disabled, + low, + med, + high }; /// @brief Configuration parameters for created engine. @@ -110,44 +110,35 @@ struct engine_configuration { throw std::invalid_argument("Invalid streams count set in engine config"); } } - - explicit engine_configuration(const cldnn_engine_configuration& c_conf): - enable_profiling(c_conf.enable_profiling != 0), - meaningful_kernels_names(c_conf.meaningful_kernels_names != 0), - dump_custom_program(c_conf.dump_custom_program != 0), - compiler_options(c_conf.compiler_options), - single_kernel_name(c_conf.single_kernel_name), - enable_parallelisation(c_conf.enable_parallelisation != 0), - engine_log(c_conf.engine_log), sources_dumps_dir(c_conf.sources_dumps_dir), - priority_mode(static_cast(c_conf.priority_mode)), - throttle_mode(static_cast(c_conf.throttle_mode)), - enable_memory_pool(c_conf.enable_memory_pool != 0), - n_streams(c_conf.n_streams), context(c_conf.context), - tuning_cache_path(c_conf.tuning_cache_path) {} - - /// @brief Implicit conversion to C API @ref ::cldnn_engine_configuration - operator ::cldnn_engine_configuration() const { - return { - enable_profiling, - meaningful_kernels_names, - dump_custom_program, - compiler_options.c_str(), - single_kernel_name.c_str(), - enable_parallelisation, - engine_log.c_str(), - sources_dumps_dir.c_str(), - static_cast(priority_mode), - static_cast(throttle_mode), - enable_memory_pool, - n_streams, - context, - tuning_cache_path.c_str()}; - } }; /// @brief Information about the engine properties and capabilities. -/// @details Look into @ref ::cldnn_engine_info for details. -using engine_info = ::cldnn_engine_info; +struct engine_info { + uint32_t cores_count; ///< Number of available HW cores. + uint32_t core_frequency; ///< Clock frequency in MHz. + + uint64_t max_work_group_size; ///< Maximum number of work-items in a work-group executing a kernel using the data parallel execution model. + uint64_t max_local_mem_size; ///< Maximum size of local memory arena in bytes. + uint64_t max_global_mem_size; ///< Maximum size of global device memory in bytes. + uint64_t max_alloc_mem_size; ///< Maximum size of memory object allocation in bytes. + + uint64_t max_image2d_width; ///< Maximum image 2d width supported by the device. + uint64_t max_image2d_height; ///< Maximum image 2d height supported by the device. + + // Flags (for layout compatibility fixed size types are used). + uint8_t supports_fp16; ///< Does engine support FP16. + uint8_t supports_fp16_denorms; ///< Does engine support denormalized FP16. + uint8_t supports_subgroups_short; ///< Does engine support cl_intel_subgroups_short. + uint8_t supports_image; ///< Does engine support images (CL_DEVICE_IMAGE_SUPPORT cap). + + uint8_t supports_imad; ///< Does engine support int8 mad. + uint8_t supports_immad; ///< Does engine support int8 multi mad. + + std::string dev_name; ///< Device ID string + std::string driver_version; ///< Version of OpenCL driver +}; + +struct engine_impl; /// @brief Represents clDNN engine object. struct engine { @@ -158,12 +149,8 @@ struct engine { /// @brief Construct engine of the specified @p type, @p engine_num, and @p configuration options. /// @param[in] type Engine type @ref cldnn_engine_type. Only OCL engine is supported. /// @param[in] engine_num Engine index. Should be 0. - /// @param[in] configuration Pointer to engine configuration options. - engine(engine_types type, uint32_t engine_num, const engine_configuration& configuration = engine_configuration()) - : _impl(check_status<::cldnn_engine>("failed to create engine", [&](status_t* status) { - cldnn_engine_configuration conf = configuration; - return cldnn_create_engine(static_cast(type), engine_num, &conf, status); - })) {} + /// @param[in] configuration Engine configuration options. + engine(engine_types type, uint32_t engine_num, const engine_configuration& configuration = engine_configuration()); // TODO add move construction/assignment engine(const engine& other) : _impl(other._impl) { @@ -186,65 +173,35 @@ struct engine { friend bool operator!=(const engine& lhs, const engine& rhs) { return !(lhs == rhs); } /// @brief Returns number of available engines of the particular @p type. - static uint32_t engine_count(engine_types type) { - return check_status("engine_count failed", [=](status_t* status) { - return cldnn_get_engine_count(static_cast(type), status); - }); - } + static uint32_t engine_count(engine_types type); /// @brief Release pending memory allocated in OpenCL context. - void release_pending_memory(uint16_t stream_id) const { - check_status("flush_memory failed", [=](status_t* status) { - return cldnn_release_pending_memory(_impl, stream_id, status); - }); - } + void release_pending_memory(uint16_t stream_id) const; /// @brief Returns information about properties and capabilities for the engine. - engine_info get_info() const { - return check_status("engine_count failed", [=](status_t* status) { - return cldnn_get_engine_info(_impl, status); - }); - } + engine_info get_info() const; /// @brief Returns total size of all resources allocated using given engine - uint64_t get_max_used_device_memory_size() const { - return check_status("get total device memory failed", [=](status_t* status) { - return cldnn_get_max_used_device_memory_size(_impl, status); - }); - } + uint64_t get_max_used_device_memory_size() const; /// @brief Returns total size of currently resources allocated using given engine - uint64_t get_temp_used_device_memory_size() const { - return check_status("get device memory failed", [=](status_t* status) { - return cldnn_get_temp_used_device_memory_size(_impl, status); - }); - } + uint64_t get_temp_used_device_memory_size() const; /// @brief Returns type of the engine. - engine_types get_type() const { - return check_status("engine_count failed", [=](status_t* status) { - return static_cast(cldnn_get_engine_type(_impl, status)); - }); - } + engine_types get_type() const; /// @brief get C API engine handler. - ::cldnn_engine get() const { return _impl; } + engine_impl* get() const { return _impl; } private: friend struct network; friend struct memory; friend struct event; - explicit engine(::cldnn_engine impl) : _impl(impl) { - if (_impl == nullptr) throw std::invalid_argument("implementation pointer should not be null"); - } - ::cldnn_engine _impl; - void retain() { - check_status("retain engine failed", [=](status_t* status) { cldnn_retain_engine(_impl, status); }); - } - void release() { - check_status("release engine failed", [=](status_t* status) { cldnn_release_engine(_impl, status); }); - } + engine_impl* _impl; + + void retain(); + void release(); }; CLDNN_API_CLASS(engine) diff --git a/inference-engine/thirdparty/clDNN/api/event.hpp b/inference-engine/thirdparty/clDNN/api/event.hpp new file mode 100644 index 00000000000000..5db6700d0cd0cb --- /dev/null +++ b/inference-engine/thirdparty/clDNN/api/event.hpp @@ -0,0 +1,94 @@ +/* +// Copyright (c) 2016-2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma once +#include "cldnn.hpp" +#include "engine.hpp" +#include "profiling.hpp" +#include +#include +#include +#include +#include + +namespace cldnn { + +/// @addtogroup cpp_api C++ API +/// @{ + +/// @addtogroup cpp_event Events Support +/// @{ + +struct event_impl; + +/// @brief user-defined event handler callback. +using event_handler = std::function; + +/// @brief Represents an clDNN Event object +struct event { + /// @brief Create an event which can be set to 'completed' by user. + static event create_user_event(const engine& engine, uint16_t stream_id); + + /// @brief Construct from C API handler @ref ::cldnn_event. + explicit event(event_impl* impl) : _impl(impl) { + if (_impl == nullptr) throw std::invalid_argument("implementation pointer should not be null"); + } + + event(const event& other) : _impl(other._impl) { + retain(); + } + + event& operator=(const event& other) { + if (_impl == other._impl) return *this; + release(); + _impl = other._impl; + retain(); + return *this; + } + + ~event() { + release(); + } + + friend bool operator==(const event& lhs, const event& rhs) { return lhs._impl == rhs._impl; } + friend bool operator!=(const event& lhs, const event& rhs) { return !(lhs == rhs); } + + /// @brief Wait for event completion. + void wait() const; + + /// @brief Set event status to 'completed'. + void set() const; + + /// @brief Register call back to be called on event completion. + void set_event_handler(event_handler handler, void* param) const; + + /// @brief Get profiling info for the event associated with network output. + std::vector get_profiling_info() const; + + /// @brief Returns C API event handler. + event_impl* get() const { return _impl; } + +private: + event_impl* _impl; + void retain(); + void release(); +}; +CLDNN_API_CLASS(event) + +/// @} +/// @} +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/fully_connected.hpp b/inference-engine/thirdparty/clDNN/api/fully_connected.hpp similarity index 76% rename from inference-engine/thirdparty/clDNN/api/CPP/fully_connected.hpp rename to inference-engine/thirdparty/clDNN/api/fully_connected.hpp index 103bf18ab49904..b91466f4e62cb4 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/fully_connected.hpp +++ b/inference-engine/thirdparty/clDNN/api/fully_connected.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/fully_connected.h" #include "primitive.hpp" #include @@ -50,7 +49,7 @@ namespace cldnn { /// yxfb bfyx /// -struct fully_connected : public primitive_base { +struct fully_connected : public primitive_base { CLDNN_DECLARE_PRIMITIVE(fully_connected) /// @brief Constructs fully connected layer. @@ -64,8 +63,6 @@ struct fully_connected : public primitive_baseweights), - bias(dto->bias), - weights_quantization_factors(dto->weights_quantization_factors), - output_calibration_factors(dto->output_calibration_factors), - input_quantization_factor(dto->input_quantization_factor), - output_quantization_factor(dto->output_quantization_factor), - with_activation(dto->with_activation != 0), - activation_negative_slope(dto->activation_negative_slope) {} + output_quantization_factor(1.0f) {} /// @brief Primitive id containing weights data. primitive_id weights; @@ -161,10 +136,6 @@ struct fully_connected : public primitive_base> get_dependencies() const override { @@ -182,19 +153,8 @@ struct fully_connected : public primitive_base @@ -29,8 +28,7 @@ namespace cldnn { /// @{ /// @brief Performs backward fully connected layer (inner product) for input. - -struct fully_connected_grad_input : public primitive_base { +struct fully_connected_grad_input : public primitive_base { CLDNN_DECLARE_PRIMITIVE(fully_connected_grad_input) /// @brief Constructs fully connected layer grad for input. @@ -47,11 +45,6 @@ struct fully_connected_grad_input : public primitive_baseweights) { - } - /// @brief Primitive id containing weights data. primitive_id weights; @@ -59,12 +52,8 @@ struct fully_connected_grad_input : public primitive_base> get_dependencies() const override { return {weights}; } - - void update_dto(dto& dto) const override { - dto.weights = weights.c_str(); - } }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/fully_connected_grad_weights.hpp b/inference-engine/thirdparty/clDNN/api/fully_connected_grad_weights.hpp similarity index 86% rename from inference-engine/thirdparty/clDNN/api/CPP/fully_connected_grad_weights.hpp rename to inference-engine/thirdparty/clDNN/api/fully_connected_grad_weights.hpp index a72596f6ffc07d..71af7a81691640 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/fully_connected_grad_weights.hpp +++ b/inference-engine/thirdparty/clDNN/api/fully_connected_grad_weights.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/fully_connected_grad_weights.h" #include "primitive.hpp" #include @@ -29,9 +28,8 @@ namespace cldnn { /// @{ /// @brief Performs backward fully connected layer (inner product) for weights and biases. - struct fully_connected_grad_weights - : public primitive_base { + : public primitive_base { CLDNN_DECLARE_PRIMITIVE(fully_connected_grad_weights) /// @brief Constructs fully connected layer for weights and biases. @@ -81,15 +79,6 @@ struct fully_connected_grad_weights prev_weights_grad(prev_weights_grad), prev_bias_grad(prev_bias_grad) {} - /// @brief Constructs a copy from basic C API @CLDNN_PRIMITIVE_DESC{fully_connected_grad_weights} - fully_connected_grad_weights(const dto* dto) - : primitive_base(dto), - weights(dto->weights), - bias(dto->bias), - fc_grad(dto->fc_grad), - prev_weights_grad(dto->prev_weights_grad), - prev_bias_grad(dto->prev_bias_grad) {} - /// @brief Primitive id containing weights data. primitive_id weights; /// @brief Primitive id containing bias data. @@ -119,16 +108,8 @@ struct fully_connected_grad_weights return ret; } - - void update_dto(dto& dto) const override { - dto.weights = weights.c_str(); - dto.bias = bias.c_str(); - dto.fc_grad = fc_grad.c_str(); - dto.prev_weights_grad = prev_weights_grad.c_str(); - dto.prev_bias_grad = prev_bias_grad.c_str(); - } }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/gather.hpp b/inference-engine/thirdparty/clDNN/api/gather.hpp similarity index 73% rename from inference-engine/thirdparty/clDNN/api/CPP/gather.hpp rename to inference-engine/thirdparty/clDNN/api/gather.hpp index de4eb5f9092c61..9b22d5cf5ffdb5 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/gather.hpp +++ b/inference-engine/thirdparty/clDNN/api/gather.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/gather.h" #include "primitive.hpp" namespace cldnn { @@ -29,14 +28,14 @@ namespace cldnn { /// @brief /// @details -struct gather : public primitive_base { +struct gather : public primitive_base { CLDNN_DECLARE_PRIMITIVE(gather) enum gather_axis { - along_b = cldnn_gather_along_b, - along_f = cldnn_gather_along_f, - along_x = cldnn_gather_along_x, - along_y = cldnn_gather_along_y + along_b, + along_f, + along_x, + along_y }; /// @brief Constructs gather primitive. @@ -53,20 +52,10 @@ struct gather : public primitive_base { const padding& output_padding = padding()) : primitive_base(id, {dict, idx}, output_padding), axis(axis), output_shape(output_shape) {} - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{gather} - gather(const dto* dto) - : primitive_base(dto), axis(static_cast(dto->axis)), output_shape(dto->output_shape) {} - /// @brief Gathering axis gather_axis axis; /// @brief Gathering input shape tensor output_shape; - -protected: - void update_dto(dto& dto) const override { - dto.axis = static_cast(axis); - dto.output_shape = output_shape; - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/gather_tree.hpp b/inference-engine/thirdparty/clDNN/api/gather_tree.hpp new file mode 100644 index 00000000000000..28980cf7288136 --- /dev/null +++ b/inference-engine/thirdparty/clDNN/api/gather_tree.hpp @@ -0,0 +1,54 @@ +// Copyright (c) 2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma once +#include "primitive.hpp" + +namespace cldnn { + /// @addtogroup cpp_api C++ API + /// @{ + /// @addtogroup cpp_topology Network Topology + /// @{ + /// @addtogroup cpp_primitives Primitives + /// @{ + + /// @brief Performs gather tree + /// + /// @details Performs gather tree +struct gather_tree : public primitive_base { + CLDNN_DECLARE_PRIMITIVE(gather_tree) + + /// @brief Constructs gather tree primitive / layer. + /// + /// @param id An identifier of new primitive. + /// @param step_input An identifier of primitive which is an step input + /// @param parent_input An identifier of primitive which is an parent input + /// @param step_seq_len_input An identifier of primitive which is an input that contains + /// lengths of step sequence (per batch) to perform + /// @param end_token An identifier of primitive which is an input that contains + /// a value of the end_token + /// @param output_padding Optional padding for output from primitive + gather_tree(const primitive_id& id, + const primitive_id& step_input, + const primitive_id& parent_input, + const primitive_id& max_seq_len_input, + const primitive_id& end_token, + const padding& output_padding = padding()) + : primitive_base(id, { step_input, parent_input, max_seq_len_input, end_token }, output_padding) {} +}; + /// @} + /// @} + /// @} +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/gemm.hpp b/inference-engine/thirdparty/clDNN/api/gemm.hpp similarity index 82% rename from inference-engine/thirdparty/clDNN/api/CPP/gemm.hpp rename to inference-engine/thirdparty/clDNN/api/gemm.hpp index 01ca36b7c3cd33..e99561f68439e5 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/gemm.hpp +++ b/inference-engine/thirdparty/clDNN/api/gemm.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/gemm.h" #include "primitive.hpp" #include @@ -41,7 +40,7 @@ namespace cldnn { /// @n - @c computations with optional params: output = alpha x (input3 x beta + input x input2) /// @n - @c transpose params tranposing second matrix <-TODO -struct gemm : public primitive_base { +struct gemm : public primitive_base { CLDNN_DECLARE_PRIMITIVE(gemm) /// @brief Constructs gemm layer. @@ -77,22 +76,6 @@ struct gemm : public primitive_base { float alpha; /// @brief Variable containing BETA parameter float beta; - - /// @brief Constructs a copy from basic C API @CLDNN_PRIMITIVE_DESC{gemm} - gemm(const dto* dto) - : primitive_base(dto), - transpose_input0(dto->transpose_input0), - transpose_input1(dto->transpose_input1), - alpha(dto->alpha), - beta(dto->beta) {} - -protected: - void update_dto(dto& dto) const override { - dto.transpose_input0 = transpose_input0; - dto.transpose_input1 = transpose_input1; - dto.alpha = alpha; - dto.beta = beta; - } }; } // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/index_select.hpp b/inference-engine/thirdparty/clDNN/api/index_select.hpp similarity index 89% rename from inference-engine/thirdparty/clDNN/api/CPP/index_select.hpp rename to inference-engine/thirdparty/clDNN/api/index_select.hpp index 1b71d8d5042ac3..0e6548eec25dbc 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/index_select.hpp +++ b/inference-engine/thirdparty/clDNN/api/index_select.hpp @@ -15,11 +15,19 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/index_select.h" #include "primitive.hpp" #include namespace cldnn { + +/// @brief Axis which index_select primitive will index. +enum class index_select_axis_name { + along_b, + along_f, + along_y, + along_x +}; + /// @brief Select index, which will be copied to the output.. /// /// @details Applies index selecting along specified dimension. The indices, which will be copied are specifed by @@ -43,7 +51,7 @@ namespace cldnn { /// @n - @c indices must be a valid primitive_id, which output's layout is: (bfyx/yxfb, i32, {1, 1, indicies_size, 1}) /// @n - @c axis - valid index_select_axis_name instance. /// @n Breaking any of this conditions will cause exeption throw. -struct index_select : public primitive_base { +struct index_select : public primitive_base { CLDNN_DECLARE_PRIMITIVE(index_select) /// @brief Constructs index_select primitive / layer. @@ -90,21 +98,10 @@ struct index_select : public primitive_baseaxis, dto->axis + dto->axis_num), reverse(dto->reverse) {} - /// @brief A list of axes of index selecting std::vector axis; /// @brief Do index_select in reverse order on axis/axes. bool reverse; - -protected: - void update_dto(dto& dto) const override { - dto.axis = axis.data(); - dto.axis_num = static_cast(axis.size()); - dto.reverse = reverse; - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/input_layout.hpp b/inference-engine/thirdparty/clDNN/api/input_layout.hpp similarity index 80% rename from inference-engine/thirdparty/clDNN/api/CPP/input_layout.hpp rename to inference-engine/thirdparty/clDNN/api/input_layout.hpp index 7f1fac5622a5e4..f994ed64cc2a0e 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/input_layout.hpp +++ b/inference-engine/thirdparty/clDNN/api/input_layout.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/input_layout.h" #include "primitive.hpp" #include "memory.hpp" @@ -35,7 +34,7 @@ namespace cldnn { /// @note User should call network::set_input_data() for every @p input_layout primitive before network execution. /// @note @p output_padding property of @p input_layout is ignored - its output layout is always equal to input layout defined during object creation. /// @sa network::set_input_data(), cldnn::data -struct input_layout : public primitive_base { +struct input_layout : public primitive_base { CLDNN_DECLARE_PRIMITIVE(input_layout) /// @brief Constructs input layout primitive. @@ -44,23 +43,12 @@ struct input_layout : public primitive_baselayout) { - output_padding = layout.data_padding; - } - /// @brief Defines layout for the data will be passed to network. mutable cldnn::layout layout; - void change_layout(cldnn::layout new_layout) { + void change_layout(const cldnn::layout& new_layout) { layout = new_layout; } - -private: - void update_dto(dto& dto) const override { - dto.layout = layout; - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/layout.hpp b/inference-engine/thirdparty/clDNN/api/layout.hpp similarity index 93% rename from inference-engine/thirdparty/clDNN/api/CPP/layout.hpp rename to inference-engine/thirdparty/clDNN/api/layout.hpp index a8c8c5ad1d0bf7..db17b8eb7b2713 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/layout.hpp +++ b/inference-engine/thirdparty/clDNN/api/layout.hpp @@ -32,15 +32,19 @@ namespace cldnn { /// @addtogroup cpp_memory Memory description and management /// @{ +constexpr size_t float_type_mask = 0x80; +constexpr size_t uint_type_mask = 0x40; +constexpr size_t bin_type_mask = 0x20; + /// @brief Possible data types could be stored in memory. enum class data_types : size_t { - bin = cldnn_bin, - i8 = cldnn_i8, /// Not supported in current HW - u8 = cldnn_u8, /// - i32 = cldnn_i32, - i64 = cldnn_i64, - f16 = cldnn_f16, - f32 = cldnn_f32, + bin = sizeof(int32_t) | bin_type_mask, + u8 = sizeof(uint8_t) | uint_type_mask, + i8 = sizeof(int8_t), + f16 = sizeof(int16_t) | float_type_mask, + f32 = sizeof(float) | float_type_mask, + i32 = sizeof(int32_t), + i64 = sizeof(int64_t) }; class optional_data_type { @@ -112,11 +116,11 @@ struct data_type_to_type { typedef float type; }; /// Helper class to identify key properties for data_types. struct data_type_traits { static size_t size_of(data_types data_type) { - return (static_cast(data_type) & ~(CLDNN_FLOAT_TYPE_MASK | CLDNN_UINT_TYPE_MASK | CLDNN_BIN_TYPE_MASK)); + return (static_cast(data_type) & ~(float_type_mask | uint_type_mask | bin_type_mask)); } static bool is_floating_point(data_types data_type) { - return (static_cast(data_type) & CLDNN_FLOAT_TYPE_MASK) != 0; + return (static_cast(data_type) & float_type_mask) != 0; } static size_t align_of(data_types data_type) { @@ -248,17 +252,6 @@ struct padding { /// @brief Constructs "zero-sized" padding. padding() : padding({0, 0, 0, 0}, 0) {} - /// @brief Copy construction. - explicit padding(const cldnn_padding& other) - : _lower_size(other.lower_size), _upper_size(other.upper_size), _filling_value(other.filling_value) {} - - /// @brief Implicit conversion to C API @ref cldnn_padding. - operator cldnn_padding() const { - return {static_cast(_lower_size), - static_cast(_upper_size), - _filling_value}; - } - /// @brief Returns true if padding size is not zero. explicit operator bool() const { return std::any_of(_lower_size.raw.begin(), _lower_size.raw.end(), [](const tensor::value_type& el) { return el != 0; }) || @@ -310,17 +303,6 @@ struct layout { layout(data_types data_type, cldnn::format fmt, tensor size, padding apadding = padding()) : data_type(data_type), format(fmt), size(size), data_padding(apadding) {} - /// Construct C++ layout based on C API @p cldnn_layout - explicit layout(const cldnn_layout& other) : - data_type(static_cast(other.data_type)), - format(static_cast(other.format)), - size(other.size), data_padding(other.padding) {} - - /// Convert to C API @p cldnn_layout - operator cldnn_layout() const { - return {static_cast(data_type), static_cast(format), size, data_padding}; - } - layout(const layout& other) = default; layout& operator=(const layout& other) { diff --git a/inference-engine/thirdparty/clDNN/api/CPP/lookup_table.hpp b/inference-engine/thirdparty/clDNN/api/lookup_table.hpp similarity index 79% rename from inference-engine/thirdparty/clDNN/api/CPP/lookup_table.hpp rename to inference-engine/thirdparty/clDNN/api/lookup_table.hpp index 887bd26cf75ebd..65349edd55cded 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/lookup_table.hpp +++ b/inference-engine/thirdparty/clDNN/api/lookup_table.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/lookup_table.h" #include "primitive.hpp" namespace cldnn { @@ -28,7 +27,7 @@ namespace cldnn { /// @{ /// @brief Returns values from data on which given indices are pointing at. -struct lookup_table : public primitive_base { +struct lookup_table : public primitive_base { CLDNN_DECLARE_PRIMITIVE(lookup_table) /// @brief Enum type to specify axis to maximize/minimize along. @@ -48,22 +47,12 @@ struct lookup_table : public primitive_base(dto->axis)), with_axis(dto->with_axis != 0) {} - /// @brief Axis to return values from. If not set, returns data which index is pointing at in the flattened x, y, f dimensions for each batch. axis_name axis; /// @brief Indicates that the primitive has user defined axis to return values from. bool with_axis; - -protected: - void update_dto(dto& dto) const override { - dto.with_axis = with_axis; - dto.axis = static_cast(axis); - } }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/lrn.hpp b/inference-engine/thirdparty/clDNN/api/lrn.hpp similarity index 79% rename from inference-engine/thirdparty/clDNN/api/CPP/lrn.hpp rename to inference-engine/thirdparty/clDNN/api/lrn.hpp index 94dce3c82d19ab..1e48dd06eba73b 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/lrn.hpp +++ b/inference-engine/thirdparty/clDNN/api/lrn.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/lrn.h" #include "primitive.hpp" namespace cldnn { @@ -27,6 +26,11 @@ namespace cldnn { /// @addtogroup cpp_primitives Primitives /// @{ +typedef enum { /*:int32_t*/ + lrn_norm_region_across_channel, + lrn_norm_region_within_channel +} lrn_norm_region; + /// @brief Local response normalization /// @details LRN layer as described in chapter 3.3 of "ImageNet Classification with Deep Convolutional /// Neural Networks" by Khrizevsky, Sutskever, Hinton. @n See: http://www.cs.toronto.edu/~fritz/absps/imagenet.pdf @@ -38,7 +42,7 @@ namespace cldnn { /// @li N : number of feature maps /// @li n : size of normalization /// @li k, alpha, beta : hyper parameters (equal to 2, 10e-4, 0.75 in paper). -struct lrn : public primitive_base { +struct lrn : public primitive_base { CLDNN_DECLARE_PRIMITIVE(lrn) /// @brief Constructs LRN primitive. @@ -55,7 +59,7 @@ struct lrn : public primitive_base { float k, float alpha, float beta, - cldnn_lrn_norm_region lrn_norm_region, + lrn_norm_region lrn_norm_region, const padding& output_padding = padding()) : primitive_base(id, {input}, output_padding), size(size), @@ -64,15 +68,6 @@ struct lrn : public primitive_base { beta(beta), norm_region(lrn_norm_region) {} - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{normalization} - lrn(const dto* dto) - : primitive_base(dto), - size(dto->size), - k(dto->k), - alpha(dto->alpha), - beta(dto->beta), - norm_region(dto->norm_region) {} - /// @brief Size of normalization. uint32_t size; /// @brief Hyper parameter "k". @@ -82,18 +77,9 @@ struct lrn : public primitive_base { /// @brief Hyper parameter "beta". float beta; /// @brief Normalize across or within channel - cldnn_lrn_norm_region norm_region; - -protected: - void update_dto(dto& dto) const override { - dto.size = size; - dto.k = k; - dto.alpha = alpha; - dto.beta = beta; - dto.norm_region = norm_region; - } + lrn_norm_region norm_region; }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/lstm.hpp b/inference-engine/thirdparty/clDNN/api/lstm.hpp similarity index 70% rename from inference-engine/thirdparty/clDNN/api/CPP/lstm.hpp rename to inference-engine/thirdparty/clDNN/api/lstm.hpp index a7a54df504abe9..97ae0f3f1ed8a8 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/lstm.hpp +++ b/inference-engine/thirdparty/clDNN/api/lstm.hpp @@ -16,8 +16,8 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/lstm.h" #include "primitive.hpp" +#include "activation.hpp" #include #include @@ -29,6 +29,34 @@ namespace cldnn { /// @addtogroup cpp_primitives Primitives /// @{ +/// @brief Weights orders +/// @details Specifies the order in which the weights are concatenated. +/// e.g. [i, o, f, z] : [input, output, forget, block] +/// ONNX order: iofz +/// Caffe order: ifoz +/// pyTorch order: izof +/// IE order: fizo +enum class lstm_weights_order { + iofz, + ifoz, + izof, + fizo +}; + +/// @brief LSTM Output selection +/// @details The current implementation allows the use to select the output +/// of an LSTM node by specifing any of the following options +enum class lstm_output_selection { + /// output the entire hidden sequence + sequence = 0, + /// output just the last hidden value + hidden, + /// output the last hidden and last cell values + hidden_cell, + /// output the hidden sequence concatenated with the last cell + sequence_cell +}; + /// @brief Performs forward Long Short-Term Memory (LSTM) layer. /// @details The current implementation of LSTM is described the following equations. /// it = f(Xt*(Wi^T) + Ht-1*Ri + Wbi) @@ -38,7 +66,7 @@ namespace cldnn { /// ot = f(Xt*(Wo^T) + Ht-1*Ro + Wbo) /// Ht = ot (.) h(Ct) /// Where f = Sigmoid, g = Tanh, and h = Tanh. -struct lstm : public primitive_base { +struct lstm : public primitive_base { CLDNN_DECLARE_PRIMITIVE(lstm) /// @brief Constructs lstm layer. @@ -65,10 +93,10 @@ struct lstm : public primitive_base { const primitive_id& peepholes = "", const float clip = 0, const bool input_forget = 0, - const std::vector& activations = {}, - const std::vector activation_params = {}, - const cldnn_lstm_output output_selection = cldnn_lstm_output_sequence, - const cldnn_lstm_offset_order offset_order = cldnn_lstm_offset_order_iofz, + const std::vector& activations = {}, + const std::vector activation_params = {}, + const lstm_output_selection output_selection = lstm_output_selection::sequence, + const lstm_weights_order offset_order = lstm_weights_order::iofz, const padding& output_padding = padding()) : primitive_base(id, input, output_padding), weights(weights), @@ -84,22 +112,6 @@ struct lstm : public primitive_base { output_selection(output_selection), offset_order(offset_order) {} - /// @brief Constructs a copy from basic C API @CLDNN_PRIMITIVE_DESC{lstm} - lstm(const dto* dto) - : primitive_base(dto), - weights(dto->weights), - recurrent(dto->recurrent), - bias(dto->bias), - initial_hidden(dto->initial_hidden), - initial_cell(dto->initial_cell), - peepholes(dto->peepholes), - clip(dto->clip), - input_forget(dto->input_forget), - activations(dto->activations, std::end(dto->activations)), - activation_params(dto->activation_params, std::end(dto->activation_params)), - output_selection(dto->output_selection), - offset_order(dto->offset_order) {} - /// @brief Primitive id containing weights data. primitive_id weights; /// @brief Primitive id containing recurrent data. @@ -117,13 +129,13 @@ struct lstm : public primitive_base { /// @brief Couple the input and forget gates if input_forget is 1. Default is 0. bool input_forget; /// @brief A list of 3 activation functions for the input, output, forget, cell, and hidden. - std::vector activations; + std::vector activations; /// @brief Optional scaling values used by some activation functions. The values are consumed in the order of activation functions. - std::vector activation_params; + std::vector activation_params; /// @brief Output selection. Default the entire hidden sequence is returned. - cldnn_lstm_output output_selection; + lstm_output_selection output_selection; /// @brief Weights, recurrent weights, and biases order. [iofz] : ONNX, [ifoz] : Caffe - cldnn_lstm_offset_order offset_order; + lstm_weights_order offset_order; // NOT SUPPORTED YET // /// @brief Optional tensor specifying lengths of the sequences in a batch. @@ -147,30 +159,10 @@ struct lstm : public primitive_base { } return ret; } - - void update_dto(dto& dto) const override { - dto.weights = weights.c_str(); - dto.recurrent = recurrent.c_str(); - dto.bias = bias.c_str(); - dto.peepholes = peepholes.c_str(); - dto.initial_hidden = initial_hidden.c_str(); - dto.initial_cell = initial_cell.c_str(); - dto.output_selection = output_selection; - dto.offset_order = offset_order; - if (activations.size() == 3) { - std::copy_n(activations.begin(), 3, dto.activations); - } - if (activation_params.size() == 3) { - std::copy_n(activation_params.begin(), 3, dto.activation_params); - } - dto.clip = clip; - dto.input_forget = input_forget; - } }; -struct lstm_gemm : public primitive_base { +struct lstm_gemm : public primitive_base { CLDNN_DECLARE_PRIMITIVE(lstm_gemm) - /// @brief Constructs lstm layer. /// @param id This primitive id. /// @param input input primitive id. @@ -194,15 +186,6 @@ struct lstm_gemm : public primitive_baseweights), - recurrent(dto->recurrent), - bias(dto->bias), - hidden(dto->hidden), - direction(dto->direction) {} - /// @brief Primitive id containing weights data. primitive_id weights; /// @brief Primitive id containing recurrent data. @@ -225,20 +208,12 @@ struct lstm_gemm : public primitive_base { +struct lstm_elt : public primitive_base { CLDNN_DECLARE_PRIMITIVE(lstm_elt) - using vec_activation = std::vector; - using vec_activation_param = std::vector; + using vec_activation = std::vector; + using vec_activation_param = std::vector; /// @brief Constructs lstm layer. /// @param id This primitive id. @@ -253,9 +228,9 @@ struct lstm_elt : public primitive_base activations = {}, - const std::vector activation_params = {}, - const cldnn_lstm_offset_order offset_order = cldnn_lstm_offset_order_iofz, + const std::vector activations = {}, + const std::vector activation_params = {}, + const lstm_weights_order offset_order = lstm_weights_order::iofz, const uint32_t direction = 0, const padding& output_padding = padding()) : primitive_base(id, {input}, output_padding), @@ -267,17 +242,6 @@ struct lstm_elt : public primitive_basecell), - clip(dto->clip), - input_forget(dto->input_forget), - activations(dto->activations, std::end(dto->activations)), - activation_params(dto->activation_params, std::end(dto->activation_params)), - offset_order(dto->offset_order), - direction(dto->direction) {} - /// @brief Primitive id containing the initial value of the cell state data. primitive_id cell; /// @brief Cell clip threshold T. It is applied to the input of activations [-T, T]. No clip is applied if it is not specified. @@ -285,11 +249,11 @@ struct lstm_elt : public primitive_base activations; + std::vector activations; /// @brief Optional scaling values used by some activation functions. The values are consumed in the order of activation functions. - std::vector activation_params; + std::vector activation_params; /// @brief Weights, recurrent weights, and biases order. [iofz] : ONNX, [ifoz] : Caffe - cldnn_lstm_offset_order offset_order; + lstm_weights_order offset_order; /// @brief direction default = 0, bidirectional = 1. uint32_t direction; @@ -300,23 +264,9 @@ struct lstm_elt : public primitive_base @@ -37,7 +36,7 @@ namespace cldnn { /// ot = f(Xt*(Wo^T) + Ht-1*Ro + Wbo) /// Ht = ot (.) h(Ct) /// Where f = Sigmoid, g = Tanh, and h = Tanh. -struct lstm_dynamic : public primitive_base { +struct lstm_dynamic : public primitive_base { CLDNN_DECLARE_PRIMITIVE(lstm_dynamic) /// @brief Constructs lstm_dynamic layer. @@ -78,20 +77,6 @@ struct lstm_dynamic : public primitive_basedyn_length), - weights(dto->weights), - recurrent(dto->recurrent), - last_hidden_state(dto->last_hidden_state), - last_cell_state(dto->last_cell_state), - bias(dto->bias), - initial_hidden(dto->initial_hidden), - initial_cell(dto->initial_cell), - clip(dto->clip), - input_forget(dto->input_forget) {} - /// @brief Primitive id containing the dynamic sequence lengths. primitive_id dyn_length; /// @brief Primitive id containing weights data. @@ -137,19 +122,6 @@ struct lstm_dynamic : public primitive_base @@ -30,7 +29,7 @@ namespace cldnn { /// @brief Performs "max_unpooling" operation. /// @details Reverse operation of max pooling, based on the argmax data where indices of each max pooling region are stored. -struct max_unpooling : public primitive_base { +struct max_unpooling : public primitive_base { CLDNN_DECLARE_PRIMITIVE(max_unpooling) /// @brief Constructs max_unpooling primitive. @@ -73,16 +72,6 @@ struct max_unpooling : public primitive_baseargmax), - input_offset(dto->input_offset), - stride(dto->stride), - size(dto->size), - with_output_size(dto->with_output_size != 0), - output_size(dto->output_size) {} - /// @brief Primitive id which contains indices of each max pooling region. /// Indices must be in flattened bfyx format with no padding. Needs to be fp32 data type. primitive_id argmax; @@ -99,17 +88,8 @@ struct max_unpooling : public primitive_base> get_dependencies() const override { return {argmax}; } - - void update_dto(dto& dto) const override { - dto.argmax = argmax.c_str(); - dto.input_offset = input_offset; - dto.stride = stride; - dto.size = size; - dto.with_output_size = with_output_size; - dto.output_size = output_size; - } }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/memory.hpp b/inference-engine/thirdparty/clDNN/api/memory.hpp similarity index 65% rename from inference-engine/thirdparty/clDNN/api/CPP/memory.hpp rename to inference-engine/thirdparty/clDNN/api/memory.hpp index 1ac388d8dda8fb..efc8b8ab2ccd57 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/memory.hpp +++ b/inference-engine/thirdparty/clDNN/api/memory.hpp @@ -17,7 +17,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once #include -#include "cldnn_defs.h" +#include "cldnn.hpp" #include "compounds.h" #include "layout.hpp" #include "engine.hpp" @@ -36,9 +36,7 @@ namespace cldnn { template struct pointer; -namespace details { -struct memory_c_to_cpp_converter; -} +struct memory_impl; /// @brief Represents buffer with particular @ref layout. /// @details Usually allocated by @ref engine except cases when attached to user-allocated buffer. @@ -47,18 +45,9 @@ struct memory { friend struct mutable_data; friend struct network; friend struct network_output; - friend struct details::memory_c_to_cpp_converter; /// Allocate memory on @p engine using specified @p layout - static memory allocate(const engine& engine, const layout& layout, uint16_t stream_id = 0) { - size_t size = layout.bytes_count(); - if (size == 0) - throw std::invalid_argument("size should be more than 0"); - memory status = (memory) check_status("memory allocation failed", [&](status_t* status) { - return cldnn_allocate_memory(engine.get(), layout, stream_id, status); - }); - return status; - } + static memory allocate(const engine& engine, const layout& layout, uint16_t stream_id = 0); /// Create memory object attached to the buffer allocated by user. /// @param ptr The pointer to user allocated buffer. @@ -75,12 +64,16 @@ struct memory { throw std::invalid_argument(err_str); } - return (memory) check_status("memory attach failed", [&](status_t* status) { - return cldnn_attach_memory(layout, ptr, data_size, stream_id, status); - }); + return attach_impl(layout, static_cast(ptr), stream_id); } - memory(const memory& other) : _impl(other._impl), _layout(other._layout), _size(other._size), _count(other._count) { + explicit memory(memory_impl* data) + : _impl(data) { + if (_impl == nullptr) + throw std::invalid_argument("implementation pointer should not be null"); + } + + memory(const memory& other) : _impl(other._impl) { retain(); } @@ -89,9 +82,6 @@ struct memory { return *this; release(); _impl = other._impl; - _layout = other._layout; - _size = other._size; - _count = other._count; retain(); return *this; } @@ -102,28 +92,19 @@ struct memory { friend bool operator!=(const memory& lhs, const memory& rhs) { return !(lhs == rhs); } /// number of elements of _layout.data_type stored in memory - size_t count() const { return _count; } + size_t count() const; /// number of bytes used by memory - size_t size() const { return _size; } + size_t size() const; /// Associated @ref layout - const layout& get_layout() const { return _layout; } - int get_stream_id() const { return get_stream_id_impl(_impl); } + const layout& get_layout() const; + int get_stream_id() const; /// Test if memory is allocated by @p engine - bool is_allocated_by(const engine& engine) const { - auto my_engine = check_status("get memory engine failed", [&](status_t* status) { - return cldnn_get_memory_engine(_impl, status); - }); - return my_engine == engine.get(); - } + bool is_allocated_by(const engine& engine) const; - bool is_the_same_buffer(const memory& other) const { - return check_status("checking if two memories refers to the same buffer failed", [&](status_t* status) { - return cldnn_is_the_same_buffer(_impl, other._impl, status) != 0; - }); - } + bool is_the_same_buffer(const memory& other) const; /// Creates the @ref pointer object to get an access memory data template @@ -132,72 +113,28 @@ struct memory { cldnn::pointer pointer() const; /// C API memory handle - cldnn_memory get() const { return _impl; } + memory_impl* get() const { return _impl; } private: friend struct engine; - cldnn_memory _impl; - layout _layout; - size_t _size; - size_t _count; - int _stream_id; - - static layout get_layout_impl(cldnn_memory mem) { - if (!mem) - throw std::invalid_argument("mem"); - - return check_status("get memory layout failed", - [=](status_t* status) { return (layout) cldnn_get_memory_layout(mem, status); }); - } - - static int get_stream_id_impl(cldnn_memory mem) { - if (!mem) - throw std::invalid_argument("mem"); - - return check_status("get memory layout failed", - [=](status_t* status) { return cldnn_get_memory_stream_id(mem, status); }); - } - - explicit memory(cldnn_memory data) - : _impl(data), - _layout(get_layout_impl(data)), - _size(_layout.bytes_count()), - _count(_layout.count()), - _stream_id(get_stream_id_impl(data)) { - if (_impl == nullptr) - throw std::invalid_argument("implementation pointer should not be null"); - } - - void retain() { - check_status("retain memory failed", [=](status_t* status) { cldnn_retain_memory(_impl, status); }); - } - void release() { - check_status("release memory failed", [=](status_t* status) { cldnn_release_memory(_impl, status); }); - } + memory_impl* _impl; template T* lock() const { - if (data_type_traits::align_of(_layout.data_type) % alignof(T) != 0) { + if (data_type_traits::align_of(get_layout().data_type) % alignof(T) != 0) { throw std::logic_error("memory data type alignment do not match"); } - return check_status("memory lock failed", - [=](status_t* status) { return static_cast(cldnn_lock_memory(_impl, status)); }); + return static_cast(lock_impl()); } - void unlock() const { - check_status("memory unlock failed", - [=](status_t* status) { return cldnn_unlock_memory(_impl, status); }); - } -}; + void unlock() const; + + void* lock_impl() const; + static memory attach_impl(const cldnn::layout& layout, void* ptr, uint16_t stream_id); -namespace details { -// we need this hackish structure as long as primitives (which are used internally) use c++ api 'memory' (see: -// cldnn::data) -struct memory_c_to_cpp_converter { - // does not retain @p c_mem - static memory convert(cldnn_memory c_mem) { return memory{c_mem}; } + void retain(); + void release(); }; -} // namespace details /// @brief Helper class to get an access @ref memory data /// @details diff --git a/inference-engine/thirdparty/clDNN/api/CPP/meta_utils.hpp b/inference-engine/thirdparty/clDNN/api/meta_utils.hpp similarity index 98% rename from inference-engine/thirdparty/clDNN/api/CPP/meta_utils.hpp rename to inference-engine/thirdparty/clDNN/api/meta_utils.hpp index 2f445d7867acda..f34cb6f0e99520 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/meta_utils.hpp +++ b/inference-engine/thirdparty/clDNN/api/meta_utils.hpp @@ -51,4 +51,4 @@ template struct all : public std::integral_constant::value> {}; } // namespace meta -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/mutable_data.hpp b/inference-engine/thirdparty/clDNN/api/mutable_data.hpp similarity index 84% rename from inference-engine/thirdparty/clDNN/api/CPP/mutable_data.hpp rename to inference-engine/thirdparty/clDNN/api/mutable_data.hpp index 190eb3824785f9..f6345d5217e1f8 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/mutable_data.hpp +++ b/inference-engine/thirdparty/clDNN/api/mutable_data.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/mutable_data.h" #include "primitive.hpp" #include "memory.hpp" #include @@ -33,7 +32,7 @@ namespace cldnn { /// @details This primitive allows to pass data which can be written to during training. /// For example, weights and biases for scoring networks. /// This primitive can be also set as other primitive's output. In this case the underlying buffer will be the same in mutable_data and preceding primitive. -struct mutable_data : public primitive_base { +struct mutable_data : public primitive_base { CLDNN_DECLARE_PRIMITIVE(mutable_data) /// @brief Enum type to specify function for data filling. @@ -59,24 +58,12 @@ struct mutable_data : public primitive_basemem), fill_type(static_cast(dto->fill_type)) { - mem.retain(); - } - /// @brief @ref memory object which contains data. /// @note If memory is attached by memory::attach(), the attached buffer should be valid till network build. memory mem; /// @brief Specifies function which will be used to fill weights. filler_type fill_type; - -protected: - void update_dto(dto& dto) const override { - dto.mem = mem.get(); - dto.fill_type = static_cast(fill_type); - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/mvn.hpp b/inference-engine/thirdparty/clDNN/api/mvn.hpp similarity index 79% rename from inference-engine/thirdparty/clDNN/api/CPP/mvn.hpp rename to inference-engine/thirdparty/clDNN/api/mvn.hpp index d649f7e74df031..f66971f2ecfa8e 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/mvn.hpp +++ b/inference-engine/thirdparty/clDNN/api/mvn.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/mvn.h" #include "primitive.hpp" namespace cldnn { @@ -29,7 +28,7 @@ namespace cldnn { /// @brief Mean Variance Normalization primitive. /// @details Normalizes the input to have 0-mean and/or unit (1) variance. -struct mvn : public primitive_base { +struct mvn : public primitive_base { CLDNN_DECLARE_PRIMITIVE(mvn) /// @brief Constructs mvn primitive. @@ -49,28 +48,14 @@ struct mvn : public primitive_base { normalize_variance(normalize_variance), epsilon(epsilon) {} - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{mvn} - mvn(const dto* dto) - : primitive_base(dto), - across_channels(dto->across_channels != 0), - normalize_variance(dto->normalize_variance != 0), - epsilon(dto->epsilon) {} - /// @brief Determines if the normalization is done across or within channels. bool across_channels; /// @brief Determines if normalize variance is applied. bool normalize_variance; /// @brief Epsilon for not dividing by zero while normalizing. float epsilon; - -protected: - void update_dto(dto& dto) const override { - dto.across_channels = across_channels; - dto.normalize_variance = normalize_variance; - dto.epsilon = epsilon; - } }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/network.hpp b/inference-engine/thirdparty/clDNN/api/network.hpp new file mode 100644 index 00000000000000..f593cd4451e0f5 --- /dev/null +++ b/inference-engine/thirdparty/clDNN/api/network.hpp @@ -0,0 +1,200 @@ +/* +// Copyright (c) 2016-2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma once +#include "cldnn.hpp" +#include "compounds.h" +#include "memory.hpp" +#include "program.hpp" +#include "event.hpp" + +#include +#include +#include +#include +#include +#include + +namespace cldnn { + +/// @addtogroup cpp_api C++ API +/// @{ + +/// @defgroup cpp_network Network Execution +/// @{ + +/// @brief Represents network output returned by @ref network::get_output(). +struct network_output { + /// @brief Returns @ref event associated with the output. + event get_event() const { return _event; } + + /// @brief Returns @ref memory object of the output. Blocked until associated @ref event is not complete. + memory get_memory() const { + _event.wait(); + return _result; + } + +private: + event _event; + memory _result; + network_output(event evt, memory mem) : _event(evt), _result(mem) {} + friend struct network; +}; + +struct network_impl; + +/// @brief Executable network allocated from @ref program. +struct network { + /// @brief Allocate network + /// @param program The program object which contains compiled primitives this network should allocate memory for. + network(program const& program, uint16_t stream_id); + + /// @brief Constructs network object from implicitly created program object. This is a shorthand for network(program(engine, topology, options)) + /// @param engine + /// @param topology + /// @param options + network(const engine& engine, + const topology& topology, + const build_options& options = build_options(), + uint16_t stream_id = 0) + : network(program(engine, topology, options), stream_id) {} + + /// @brief Constructs network object from C API @ref cldnn_network. + explicit network(network_impl* impl) : _impl(impl) { + if (_impl == nullptr) + throw std::invalid_argument("implementation pointer should not be null"); + } + + /// @brief Copy construction. + network(const network& other) : _impl(other._impl) { retain(); } + + /// @brief Copy assignment. + network& operator=(const network& other) { + if (_impl == other._impl) + return *this; + release(); + _impl = other._impl; + retain(); + return *this; + } + + /// @brief Releases wrapped C API @ref cldnn_network. + ~network() { release(); } + + friend bool operator==(const network& lhs, const network& rhs) { return lhs._impl == rhs._impl; } + friend bool operator!=(const network& lhs, const network& rhs) { return !(lhs == rhs); } + + /// @brief Returns @ref engine by which network was built. + engine get_engine() const; + + /// @brief Returns network internal @ref program. + program get_program() const; + + /// @brief Provides @ref memory for @ref input_layout primitives defined by user in source @ref topology. + void set_input_data(const primitive_id& id, const memory& mem) const; + + /// @brief Sets learning rate for training primitives. + void set_learning_rate(const float lr); + + /// @brief Return learning rate. + float get_learning_rate(); + + /// @brief Return stream id. + uint16_t get_stream_id(); + + std::string get_primitive_info(const primitive_id& id) const; + + /// @brief Returns description of final runtime graph + std::vector get_primitives_info(); + + /// @brief Returns description of all optimization stages + std::vector>> get_optimization_steps_info(); + + /// @brief Returns the list of executed primitives. + std::vector get_executed_primitive_ids() const; + + /// @brief Returns the list of all primitives ids in network. + std::vector get_all_primitive_ids() const; + + /// @brief Returns the list of all primitives ids in network before graph optimization. + std::vector get_all_primitive_org_ids() const; + + /// @brief Returns the list of available network outputs. + std::vector get_output_ids() const; + + /// @brief Returns @ref memory object for particular @p output. Can be called before network execution + memory get_output_memory(const primitive_id& output_id) const; + + /// @brief Returns @ref event object for particular @p primitive. Can't be called before network execution + event get_primitive_event(const primitive_id& output_id) const; + + /// @brief Returns @ref network_output object for particular @p output. Can't be called before network execution + network_output get_output(const primitive_id& output_id) const { + return network_output(get_primitive_event(output_id), get_output_memory(output_id)); + } + + /// @brief Returns the list of @ref event for the primitives that were executed in network. + std::map get_executed_primitives() const { + auto primitive_ids = get_executed_primitive_ids(); + auto all_primitive_ids = get_all_primitive_ids(); + auto all_primitive_org_ids = get_all_primitive_org_ids(); + // Get list of optimized prmitives + std::vector optimized_primitives; + for (decltype(all_primitive_org_ids.size()) i = 0; i < all_primitive_org_ids.size(); i++) { + if (all_primitive_ids[i] == "_optimized_") + optimized_primitives.push_back(all_primitive_org_ids[i]); + } + std::map result; + for (auto& id : primitive_ids) { + if (std::find(optimized_primitives.begin(), optimized_primitives.end(), id) == optimized_primitives.end()) + result.emplace(id, get_primitive_event(id)); + } + return result; + } + + /// @brief Returns the list of primitive ids before and after graph optimization. + /// @details If primitive was not optimized, the old and actual id will be the same. + /// @n If primitive was optimized during graph optimization, the actual id will be "_optimized_". + std::map get_all_primitives() const { + auto primitive_ids = get_all_primitive_ids(); + auto primitive_org_ids = get_all_primitive_org_ids(); + std::map result; + for (decltype(primitive_org_ids.size()) i = 0; i < primitive_org_ids.size(); i++) { + result.emplace(primitive_org_ids[i], primitive_ids[i]); + } + return result; + } + + /// @brief Executes network and returns the list of @ref network_output. + /// @param dependencies List of @ref event objects to be waited before network execution. + /// @note User should call set_input_data() for every @ref input_layout defined in source @ref topology + /// before network execution. + std::map execute(const std::vector& dependencies = {}) const; + + /// @brief Returns wrapped C API @ref cldnn_network handler. + network_impl* get() const { return _impl; } + +private: + network_impl* _impl; + + void retain(); + void release(); +}; +CLDNN_API_CLASS(network) +/// @} +/// @} +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/normalize.hpp b/inference-engine/thirdparty/clDNN/api/normalize.hpp similarity index 86% rename from inference-engine/thirdparty/clDNN/api/CPP/normalize.hpp rename to inference-engine/thirdparty/clDNN/api/normalize.hpp index 19a4ea69d502df..611cc08bd65e92 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/normalize.hpp +++ b/inference-engine/thirdparty/clDNN/api/normalize.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/normalize.h" #include "primitive.hpp" #include @@ -44,7 +43,7 @@ namespace cldnn { /// @li in(i,x,y) : value at x, y from i-th feature map before normalization. /// @li norm(i,x,y) : L2 norm as described above. /// @li scale(i) : the scale value of the i-th feature map. -struct normalize : public primitive_base { +struct normalize : public primitive_base { CLDNN_DECLARE_PRIMITIVE(normalize) /// @brief Constructs normalize primitive. @@ -66,13 +65,6 @@ struct normalize : public primitive_basescale_input), - across_spatial(dto->across_spatial != 0), - epsilon(dto->epsilon) {} - /// @brief Scale input primitive id with values needed for scaling after the normalization. /// Scale x dimension should be 1 (if all channels have the same scale) or equal to input feature size (one scale per channel). /// All other dimensions should be 1. @@ -84,14 +76,8 @@ struct normalize : public primitive_base> get_dependencies() const override { return {scale_input}; } - - void update_dto(dto& dto) const override { - dto.scale_input = scale_input.c_str(); - dto.across_spatial = across_spatial; - dto.epsilon = epsilon; - } }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/one_hot.hpp b/inference-engine/thirdparty/clDNN/api/one_hot.hpp similarity index 86% rename from inference-engine/thirdparty/clDNN/api/CPP/one_hot.hpp rename to inference-engine/thirdparty/clDNN/api/one_hot.hpp index f7475574815753..13340d03556fad 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/one_hot.hpp +++ b/inference-engine/thirdparty/clDNN/api/one_hot.hpp @@ -14,8 +14,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once - -#include "../C/one_hot.h" #include "primitive.hpp" namespace cldnn { @@ -49,7 +47,7 @@ namespace cldnn { /// @n - input batch size must be equal to 1. /// @n /// @n Breaking any of this conditions will cause exception throw. -struct one_hot : public primitive_base { +struct one_hot : public primitive_base { CLDNN_DECLARE_PRIMITIVE(one_hot) /// @brief Constructs one-hot primitive layer. @@ -68,10 +66,6 @@ struct one_hot : public primitive_base { : primitive_base(id, {input}, output_padding), shape(shape), one_hot_axis(one_hot_axis), on_value(on_value), off_value(off_value) {} - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{one_hot} - one_hot(const dto* dto) : primitive_base(dto), shape(dto->shape), one_hot_axis(dto->one_hot_axis), - on_value(dto->on_value), off_value(dto->off_value) {} - /// @brief Output size reference. tensor shape; /// @brief One-hot axis position in output shape (0-based, from left to right). @@ -80,14 +74,6 @@ struct one_hot : public primitive_base { float on_value; /// @brief all other locations take value this value. float off_value; - -protected: - void update_dto(dto& dto) const override { - dto.shape = shape; - dto.one_hot_axis = one_hot_axis; - dto.on_value = on_value; - dto.off_value = off_value; - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/permute.hpp b/inference-engine/thirdparty/clDNN/api/permute.hpp similarity index 83% rename from inference-engine/thirdparty/clDNN/api/CPP/permute.hpp rename to inference-engine/thirdparty/clDNN/api/permute.hpp index 7028a977dc56a3..24f6f03a135f32 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/permute.hpp +++ b/inference-engine/thirdparty/clDNN/api/permute.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/permute.h" #include "primitive.hpp" #include @@ -36,7 +35,7 @@ namespace cldnn { /// output_dimensions = { 6, 3, 3, 5 }
///
/// When permute_order is { 0, 1, 2, 3 } then input_dimensions = output_dimensions -struct permute : public primitive_base { +struct permute : public primitive_base { CLDNN_DECLARE_PRIMITIVE(permute) /// @brief Constructs permute primitive. @@ -49,14 +48,8 @@ struct permute : public primitive_base { const padding& output_padding = padding()) : primitive_base(id, {input}, output_padding), permute_order(permute_order) {} - /// @brief Constructs a copy from basic C API @CLDNN_PRIMITIVE_DESC{reorder} - permute(const dto* dto) : primitive_base(dto), permute_order(uint16_t_arr_to_vector(dto->permute_order)) {} - /// @brief Array of permuted output order in bfyx format. std::vector permute_order; - -protected: - void update_dto(dto& dto) const override { dto.permute_order = uint16_t_vector_to_arr(permute_order); } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/pooling.hpp b/inference-engine/thirdparty/clDNN/api/pooling.hpp similarity index 90% rename from inference-engine/thirdparty/clDNN/api/CPP/pooling.hpp rename to inference-engine/thirdparty/clDNN/api/pooling.hpp index 02a72b8d6f93ca..8adfd4dee6306c 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/pooling.hpp +++ b/inference-engine/thirdparty/clDNN/api/pooling.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/pooling.h" #include "primitive.hpp" #include @@ -31,22 +30,22 @@ namespace cldnn { /// @brief Select method for the @ref pooling layer. enum class pooling_mode : int32_t { /// @brief Maximum-pooling method. - max = cldnn_pooling_max, + max, /// @brief Average-pooling method - values. - average = cldnn_pooling_average, + average, /// @brief Average-pooling method without values which are outside of the input. - average_no_padding = cldnn_pooling_average_no_padding, + average_no_padding, /// @brief Maximum-pooling method with additional buffer to store argmax indices. - max_with_argmax = cldnn_pooling_max_with_argmax, + max_with_argmax, /// @brief Pooling with bilinear interpolation. - bilinear = cldnn_pooling_bilinear, + bilinear, /// @brief Deformable pooling with bilinear interpolation. - deformable_bilinear = cldnn_pooling_deformable_bilinear + deformable_bilinear }; /// @brief Performs "pooling" operation which is a form of non-linear down-sampling. /// @details Pools the input image by taking the max, average, etc. within regions. -struct pooling : public primitive_base { +struct pooling : public primitive_base { CLDNN_DECLARE_PRIMITIVE(pooling) /// @brief Constructs pooling primitive. @@ -171,18 +170,6 @@ struct pooling : public primitive_base { size(0, 0, 0, 0), with_output_size(false) {} - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{pooling} - pooling(const dto* dto) - : primitive_base(dto), - argmax(dto->argmax), - mode(static_cast(dto->mode)), - global_pooling(dto->global_pooling != 0), - input_offset(dto->input_offset), - stride(dto->stride), - size(dto->size), - with_output_size(dto->with_output_size != 0), - output_size(dto->output_size) {} - /// @brief Constructs pooling primitive (computes input paddings to match output size). /// @param id This primitive id. /// @param input Input primitive id. @@ -251,19 +238,8 @@ struct pooling : public primitive_base { return {}; return {argmax}; } - - void update_dto(dto& dto) const override { - dto.mode = static_cast(mode); - dto.argmax = argmax.c_str(); - dto.input_offset = input_offset; - dto.stride = stride; - dto.size = size; - dto.with_output_size = with_output_size; - dto.output_size = output_size; - dto.global_pooling = global_pooling; - } }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/primitive.hpp b/inference-engine/thirdparty/clDNN/api/primitive.hpp new file mode 100644 index 00000000000000..314c71dd0d10da --- /dev/null +++ b/inference-engine/thirdparty/clDNN/api/primitive.hpp @@ -0,0 +1,173 @@ +/* +// Copyright (c) 2016 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma once + +#include "cldnn.hpp" +#include "compounds.h" +#include "layout.hpp" + +#include +#include +#include +#include +#include +#include + +namespace cldnn { +/// @addtogroup cpp_api C++ API +/// @{ + +/// @addtogroup cpp_topology Network Topology +/// @{ + +/// @brief Globally unique primitive's type id +using primitive_type_id = struct primitive_type *; + +/// @brief Unique @p id of a primitive within a topology. +using primitive_id = std::string; + +struct primitive_info; + +/// @brief Base class of network primitive description. +struct primitive { +public: + /// @brief Initialize fields common for all primitives. + primitive(const primitive_type_id& type, + const primitive_id& id, + const std::vector& input, + const padding& output_padding = padding(), + const optional_data_type output_data_type = optional_data_type()) + : type(type), + id(id), + output_padding(output_padding), + output_data_type(output_data_type), + input(input) {} + + virtual ~primitive() = default; + + /// @brief Returns references to all primitive ids on which this primitive depends - inputs, weights, biases, etc. + std::vector> dependencies() { + std::vector> result; + auto&& deps = get_dependencies(); + + result.reserve(input.size() + deps.size()); + for (auto& pid : input) result.push_back(std::ref(pid)); + for (auto& pid : deps) result.push_back(std::ref(const_cast(pid.get()))); + + return result; + } + + /// @brief Returns copy of all primitive ids on which this primitive depends - inputs, weights, biases, etc. + std::vector dependencies() const { + auto result = input; + auto deps = get_dependencies(); + result.insert(result.end(), deps.begin(), deps.end()); + return result; + } + + virtual primitive_id type_string() const = 0; + + /// @brief Implicit conversion to primiitive id. + operator primitive_id() const { return id; } + + /// @brief Primitive's type id. + const primitive_type_id type; + + /// @brief Primitive's id. + const primitive_id id; + + /// @brief Requested output padding. + padding output_padding; + + /// @brief Requested output precision, if any. + optional_data_type output_data_type; + + size_t input_size() const { return input.size(); } + + using primitive_id_arr = std::vector; + + /// @brief List of ids of input primitives. + primitive_id_arr input; + +protected: + virtual std::vector> get_dependencies() const { return {}; } + class condition; + friend struct primitive_info; +}; + +/// @brief base class for all primitives implementations. +template +class primitive_base : public primitive { +protected: + explicit primitive_base(const primitive_id& id, + const std::vector& input, + const padding& output_padding = padding(), + optional_data_type output_data_type = optional_data_type()) + : primitive(PType::type_id(), id, input, output_padding, output_data_type) {} +}; + +struct primitive_info { + primitive_info(const primitive_id& original_id, + const std::string& type_id, + const std::vector& dependencies, + const std::vector& users, + const std::vector& fused_ids, + const layout& output_layout, + const std::string& layout_str, + const std::string& kernel_id, + bool is_cpu, + int exec_id) + : original_id(original_id), + type_id(type_id), + c_dependencies(dependencies), + c_users(users), + c_fused_ids(fused_ids), + output_layout(output_layout), + layout_str(layout_str), + kernel_id(kernel_id), + is_cpu(is_cpu), + exec_id(exec_id) {} + + primitive_id original_id; + std::string type_id; + primitive::primitive_id_arr c_dependencies; + primitive::primitive_id_arr c_users; + primitive::primitive_id_arr c_fused_ids; + layout output_layout; + std::string layout_str; + std::string kernel_id; + bool is_cpu; + int exec_id; +}; + +#define CLDNN_DEFINE_TYPE_ID(PType) \ + static primitive_type_id type_id(); + +#define CLDNN_DEFINE_TYPE_STRING(PType) \ + primitive_id type_string() const override { \ + static constexpr const char* type_str = #PType; \ + return std::string(type_str); \ + } + +#define CLDNN_DECLARE_PRIMITIVE(PType) \ + CLDNN_DEFINE_TYPE_ID(PType) \ + CLDNN_DEFINE_TYPE_STRING(PType) + +/// @} +/// @} +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/prior_box.hpp b/inference-engine/thirdparty/clDNN/api/prior_box.hpp similarity index 76% rename from inference-engine/thirdparty/clDNN/api/CPP/prior_box.hpp rename to inference-engine/thirdparty/clDNN/api/prior_box.hpp index dbdd13c6142967..5908f6a636a3ce 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/prior_box.hpp +++ b/inference-engine/thirdparty/clDNN/api/prior_box.hpp @@ -17,10 +17,9 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include - -#include "../C/prior_box.h" #include "primitive.hpp" + +#include #include #include @@ -36,7 +35,7 @@ namespace cldnn { /// @details The prior-boxes are shared across all the images in a batch (since they have the same width and height). /// First feature stores the mean of each prior coordinate. /// Second feature stores the variance of each prior coordinate. -struct prior_box : public primitive_base { +struct prior_box : public primitive_base { CLDNN_DECLARE_PRIMITIVE(prior_box) /// @brief Constructs prior-box primitive. @@ -107,21 +106,6 @@ struct prior_box : public primitive_baseimg_size), - min_sizes(float_arr_to_vector(dto->min_sizes)), - max_sizes(float_arr_to_vector(dto->max_sizes)), - aspect_ratios(float_arr_to_vector(dto->aspect_ratios)), - flip(dto->flip != 0), - clip(dto->clip != 0), - variance(float_arr_to_vector(dto->variance)), - step_width(dto->step_width), - step_height(dto->step_height), - offset(dto->offset), - scale_all_sizes(dto->scale_all_sizes != 0) {} - /// @brief Image width and height. tensor img_size; /// @brief Minimum box sizes in pixels. @@ -144,31 +128,6 @@ struct prior_box : public primitive_base& stor) { return {stor.data(), stor.size()}; } - - static std::vector float_arr_to_vector(const cldnn_float_arr& arr) { - std::vector result(arr.size); - for (size_t i = 0; i < arr.size; i++) { - result[i] = arr.data[i]; - } - return result; - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/profiling.hpp b/inference-engine/thirdparty/clDNN/api/profiling.hpp similarity index 97% rename from inference-engine/thirdparty/clDNN/api/CPP/profiling.hpp rename to inference-engine/thirdparty/clDNN/api/profiling.hpp index d2563f0ead3e65..004b9ba1cc15d2 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/profiling.hpp +++ b/inference-engine/thirdparty/clDNN/api/profiling.hpp @@ -15,7 +15,6 @@ */ #pragma once -#include "cldnn_defs.h" #include #include #include @@ -68,7 +67,6 @@ struct profiling_period_basic : profiling_period { }; /// @brief Represents prifiling interval as its name and value. -/// @sa @ref ::cldnn_profiling_interval struct profiling_interval { std::string name; ///< @brief Display name. std::shared_ptr value; ///< @brief Interval value. diff --git a/inference-engine/thirdparty/clDNN/api/CPP/program.hpp b/inference-engine/thirdparty/clDNN/api/program.hpp similarity index 58% rename from inference-engine/thirdparty/clDNN/api/CPP/program.hpp rename to inference-engine/thirdparty/clDNN/api/program.hpp index ecf00dd7204163..7cbdd54d2fb7ee 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/program.hpp +++ b/inference-engine/thirdparty/clDNN/api/program.hpp @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "cldnn_defs.h" +#include "cldnn.hpp" #include "topology.hpp" #include "engine.hpp" #include @@ -36,23 +36,23 @@ namespace cldnn { /// @brief Represents user-provided program build option type. enum class build_option_type { /// @brief Allow primitives fusing during program build (default: false). - fusing = cldnn_build_option_fusing, + fusing, /// @brief Enable implicit reordering for user inputs (default: false). - optimize_data = cldnn_build_option_optimize_data, + optimize_data, /// @brief Enable running detection output layer always on gpu, regardless performance - detection_output_gpu = cldnn_build_option_detection_output_gpu, + detection_output_gpu, /// @brief Enable debug mode (default: false). /// @details This option enforce all program primitives to be accessible as outputs. - debug = cldnn_build_option_debug, + debug, /// @brief User selected list of program outputs. - outputs = cldnn_build_option_outputs, + outputs, /// @brief User defined learning parameters. - learning_config = cldnn_build_option_learning_config, + learning_config, /// @brief Tuning config (default: Tuning is disabled). /// @details The tuner will automatically find the optimal kernel/config for each node in the graph, @@ -60,25 +60,25 @@ enum class build_option_type { /// Expect long execution time in the first run. /// After the first run a cache with the tuning results will be created in the path provided. /// This cache will be used in the next runs. - tuning_config = cldnn_build_option_tuning_config, + tuning_config, /// @brief Specifies a directory to which stages of network compilation should be dumped. (default: empty, i.e. no dumping) - graph_dumps_dir = cldnn_build_option_graph_dumps_dir, + graph_dumps_dir, /// @brief Name for serialization process - serialize_network = cldnn_build_option_serialization, - load_program = cldnn_build_option_load_program + serialize_network, + load_program }; /// @brief Tuning mode. enum class tuning_mode { /// @brief Tuning is disabled. - tuning_disabled = cldnn_tuning_disabled, + tuning_disabled, /// @brief Tuning using the cached data (no on-line tuning for non-existing data). - tuning_use_cache = cldnn_tuning_use_cache, + tuning_use_cache, /// @brief Tuning using the cached data if exist, tune and update cache otherwise. - tuning_tune_and_cache = cldnn_tuning_tune_and_cache + tuning_tune_and_cache }; /// @brief Tuning configuration. @@ -91,8 +91,8 @@ struct tuning_config_options { /// @brief Learning parameters. struct learning_params { - float momentum; - float weights_decay; + float momentum = 0.0; + float weights_decay = 0.0; learning_params() : momentum(0.9f), weights_decay(0.0005f) {} }; @@ -141,9 +141,6 @@ struct build_option { /// @brief Returns option type represented by this object. virtual build_option_type get_type() const = 0; - /// @brief Returns option @ref ::cldnn_build_option::data represented by this object. - virtual const void* get_data() const = 0; - friend class build_options; }; @@ -154,17 +151,11 @@ struct build_option_bool : build_option { /// @param value Is option enabled. explicit build_option_bool(bool value) : _value(value ? 1 : 0) {} - /// @brief Constructs from C API @ref ::cldnn_build_option. - explicit build_option_bool(const cldnn_build_option& value) : _value(reinterpret_cast(value.data)) { - assert(value.type == static_cast(OptType)); - } - /// @brief Is option enabled. bool enabled() const { return _value != 0; } private: build_option_type get_type() const override { return OptType; } - const void* get_data() const override { return reinterpret_cast(_value); } uintptr_t _value; }; @@ -176,47 +167,14 @@ struct build_option_outputs : build_option { /// @brief Constructs option. /// @param outs List of ouput ids (names) explicit build_option_outputs(const std::vector& outs) - : outputs(outs), _ref_store(to_refs(outputs)), _outputs_ref({_ref_store.data(), _ref_store.size()}) {} - - /// @brief Constructs from C API @ref ::cldnn_build_option. - explicit build_option_outputs(const cldnn_build_option& value) - : build_option_outputs(make_outputs_from_ref(value)) { - assert(value.type == static_cast(cldnn_build_option_outputs)); - } + : outputs(outs) {} private: /// @brief Returns build_option_type::outputs. build_option_type get_type() const override { return build_option_type::outputs; } - /// @brief Returns pointer to @ref cldnn_primitive_is_arr - const void* get_data() const override { return &_outputs_ref; } build_option_outputs(const build_option_outputs& other) = delete; build_option_outputs& operator=(const build_option_outputs& other) = delete; - - const std::vector _ref_store; - const cldnn_primitive_id_arr _outputs_ref; - - static std::vector to_refs(const std::vector& stor) { - std::vector result(stor.size()); - for (size_t i = 0; i < stor.size(); i++) { - result[i] = stor[i].c_str(); - } - return result; - } - - static std::vector make_outputs_from_ref(const cldnn_build_option& value) { - if (value.type != cldnn_build_option_outputs) - throw std::invalid_argument("option type does not match: should be 'output'"); - if (value.data == nullptr) - throw std::invalid_argument("output data is empty"); - auto refs = reinterpret_cast(value.data); - std::vector result; - result.reserve(refs->size); - for (decltype(refs->size) i = 0; i < refs->size; i++) { - result.push_back(refs->data[i]); - } - return result; - } }; /// @brief @ref build_option specialization for learning config. @@ -227,36 +185,14 @@ struct build_option_learning_config : build_option { /// @brief Constructs learning config build option. /// @param learning_params Parameters for learning. explicit build_option_learning_config(const learning_params& params) - : params(params), params_ref({params.momentum, params.weights_decay}) {} - - /// @brief Constructs learning config build option from C API @ref ::cldnn_build_option. - explicit build_option_learning_config(const cldnn_build_option& value) - : build_option_learning_config(make_config_from_ref(value)) { - assert(value.type == static_cast(cldnn_build_option_learning_config)); - } + : params(params) {} private: /// @brief Returns build_option_type::learning_config. build_option_type get_type() const override { return build_option_type::learning_config; } - /// @brief Returns pointer to @ref cldnn_learning_params. - const void* get_data() const override { return ¶ms_ref; } build_option_learning_config(const build_option_learning_config& other) = delete; build_option_learning_config& operator=(const build_option_learning_config& other) = delete; - - const cldnn_learning_params params_ref; - - static learning_params make_config_from_ref(const cldnn_build_option& value) { - if (value.type != cldnn_build_option_learning_config) - throw std::invalid_argument("option type does not match: should be 'learning_config'"); - if (value.data == nullptr) - throw std::invalid_argument("Learning params data is empty"); - auto refs = reinterpret_cast(value.data); - learning_params result; - result.momentum = refs->momentum; - result.weights_decay = refs->weights_decay; - return result; - } }; /// @brief @ref build_option specialization for tuning config. @@ -267,36 +203,14 @@ struct build_option_tuning_config : build_option { /// @brief Constructs tuning config build option. /// @param tuning_config Configuration for the tuning. explicit build_option_tuning_config(const tuning_config_options& tuning_config) - : config(tuning_config), config_ref({static_cast(config.mode), config.cache_file_path.c_str()}) {} - - /// @brief Constructs tuning config build option from C API @ref ::cldnn_build_option. - explicit build_option_tuning_config(const cldnn_build_option& value) - : build_option_tuning_config(make_config_from_ref(value)) { - assert(value.type == static_cast(cldnn_build_option_tuning_config)); - } + : config(tuning_config) {} private: /// @brief Returns build_option_type::tuning_config. build_option_type get_type() const override { return build_option_type::tuning_config; } - /// @brief Returns pointer to @ref cldnn_tuning_config - const void* get_data() const override { return &config_ref; } build_option_tuning_config(const build_option_tuning_config& other) = delete; build_option_tuning_config& operator=(const build_option_tuning_config& other) = delete; - - const cldnn_tuning_config config_ref; - - static tuning_config_options make_config_from_ref(const cldnn_build_option& value) { - if (value.type != cldnn_build_option_tuning_config) - throw std::invalid_argument("option type does not match: should be 'tuning_config'"); - if (value.data == nullptr) - throw std::invalid_argument("Tuning config data is empty"); - auto refs = reinterpret_cast(value.data); - tuning_config_options result; - result.mode = tuning_mode(refs->mode); - result.cache_file_path = std::string(refs->cache_file_path); - return result; - } }; /// @brief @ref build_option specialization for selecting a directory. @@ -308,26 +222,12 @@ struct build_option_directory : build_option { /// @param outs List of ouput ids (names) explicit build_option_directory(const std::string& dir_path) : directory_path(dir_path) {} - /// @brief Constructs from C API @ref ::cldnn_build_option. - explicit build_option_directory(const cldnn_build_option& value) : directory_path(from_c_value(value)) {} - private: /// @brief Returns build_option_type::graph_dumps_dir. build_option_type get_type() const override { return build_option_type::graph_dumps_dir; } - /// @brief Returns null terminated C string. - const void* get_data() const override { return (directory_path.empty() ? nullptr : directory_path.c_str()); } build_option_directory(const build_option_directory& other) = delete; build_option_directory& operator=(const build_option_directory& other) = delete; - - static std::string from_c_value(const cldnn_build_option& value) { - if (value.type != static_cast(OptType)) - throw std::invalid_argument("option type does not match"); - if (value.data == nullptr) - return {}; - - return {static_cast(value.data)}; - } }; /// @brief @ref build_option specialization for serialization process. @@ -337,27 +237,11 @@ struct build_option_serialization : build_option { explicit build_option_serialization(const std::string& name) : serialization_network_name(name) {} - explicit build_option_serialization(const cldnn_build_option& value) - : serialization_network_name(from_c_value(value)) {} - private: build_option_type get_type() const override { return build_option_type::serialize_network; } - const void* get_data() const override { - return (serialization_network_name.empty() ? nullptr : serialization_network_name.c_str()); - } - build_option_serialization(const build_option_serialization& other) = delete; build_option_serialization& operator=(const build_option_serialization& other) = delete; - - static std::string from_c_value(const cldnn_build_option& value) { - if (value.type != static_cast(OptType)) - throw std::invalid_argument("option type does not match"); - if (value.data == nullptr) - return {}; - - return {static_cast(value.data)}; - } }; /// @brief @ref build_option specialization for load_program process. @@ -367,24 +251,11 @@ struct build_option_load_program : build_option { explicit build_option_load_program(const std::string& name) : load_program_name(name) {} - explicit build_option_load_program(const cldnn_build_option& value) : load_program_name(from_c_value(value)) {} - private: build_option_type get_type() const override { return build_option_type::load_program; } - const void* get_data() const override { return (load_program_name.empty() ? nullptr : load_program_name.c_str()); } - build_option_load_program(const build_option_load_program& other) = delete; build_option_load_program& operator=(const build_option_load_program& other) = delete; - - static std::string from_c_value(const cldnn_build_option& value) { - if (value.type != static_cast(OptType)) - throw std::invalid_argument("option type does not match"); - if (value.data == nullptr) - return {}; - - return {static_cast(value.data)}; - } }; namespace detail { @@ -395,8 +266,6 @@ struct build_option_traits { typedef build_option object_type; /// @brief Make default @ref build_option corresponding @p OptType static std::shared_ptr make_default(); - /// @brief Make @ref build_option from C API @ref ::cldnn_build_option - static std::shared_ptr make_option(const cldnn_build_option& option); }; #ifndef DOXYGEN_SHOULD_SKIP_THIS @@ -404,91 +273,51 @@ template <> struct build_option_traits { typedef build_option_bool object_type; static std::shared_ptr make_default() { return build_option::fusing(); } - static std::shared_ptr make_option(const cldnn_build_option& option) { - assert(option.type == cldnn_build_option_fusing); - return std::make_shared(option); - } }; template <> struct build_option_traits { typedef build_option_bool object_type; static std::shared_ptr make_default() { return build_option::optimize_data(); } - static std::shared_ptr make_option(const cldnn_build_option& option) { - assert(option.type == cldnn_build_option_optimize_data); - return std::make_shared(option); - } }; template <> struct build_option_traits { typedef build_option_bool object_type; static std::shared_ptr make_default() { return build_option::detection_output_gpu(); } - static std::shared_ptr make_option(const cldnn_build_option& option) { - assert(option.type == cldnn_build_option_detection_output_gpu); - return std::make_shared(option); - } }; template <> struct build_option_traits { typedef build_option_bool object_type; static std::shared_ptr make_default() { return build_option::debug(); } - static std::shared_ptr make_option(const cldnn_build_option& option) { - assert(option.type == cldnn_build_option_debug); - return std::make_shared(option); - } }; template <> struct build_option_traits { typedef build_option_outputs object_type; static std::shared_ptr make_default() { return build_option::outputs({}); } - static std::shared_ptr make_option(const cldnn_build_option& option) { - assert(option.type == cldnn_build_option_outputs); - return std::make_shared(option); - } }; template <> struct build_option_traits { typedef build_option_learning_config object_type; static std::shared_ptr make_default() { return build_option::learning_config(); } - static std::shared_ptr make_option(const cldnn_build_option& option) { - assert(option.type == cldnn_build_option_learning_config); - return std::make_shared(option); - } }; template <> struct build_option_traits { typedef build_option_tuning_config object_type; static std::shared_ptr make_default() { return build_option::tuning_config(); } - static std::shared_ptr make_option(const cldnn_build_option& option) { - assert(option.type == cldnn_build_option_tuning_config); - return std::make_shared(option); - } }; template <> struct build_option_traits { typedef build_option_directory object_type; static std::shared_ptr make_default() { return build_option::graph_dumps_dir({}); } - static std::shared_ptr make_option(const cldnn_build_option& option) { - assert(option.type == cldnn_build_option_graph_dumps_dir); - return std::make_shared(option); - } }; template <> struct build_option_traits { typedef build_option_serialization object_type; static std::shared_ptr make_default() { return build_option::serialize_network({}); } - static std::shared_ptr make_option(const cldnn_build_option& option) { - assert(option.type == cldnn_build_option_serialization); - return std::make_shared(option); - } }; template <> struct build_option_traits { typedef build_option_load_program object_type; static std::shared_ptr make_default() { return build_option::load_program({}); } - static std::shared_ptr make_option(const cldnn_build_option& option) { - assert(option.type == cldnn_build_option_load_program); - return std::make_shared(option); - } }; #endif @@ -553,13 +382,6 @@ class build_options { set_option(args...); } - /// @brief Constructs build options list from C API ::cldnn_build_options. - explicit build_options(array_ref options) { - for (auto& o : options) { - _options.emplace_back(make_option(o)); - } - } - /// @brief Returns program build option for @p OptType template std::shared_ptr::object_type> get() const { @@ -576,15 +398,6 @@ class build_options { std::vector> _options; void set_option(void) {} - /// @brief Returns C API compatible list of ::cldnn_build_option - std::vector get_refs() const { - std::vector result; - for (auto& o : _options) { - result.push_back({static_cast(o->get_type()), o->get_data()}); - } - return result; - } - void add_or_replace_option(std::shared_ptr opt) { for (auto& p : _options) { if (p->get_type() == opt->get_type()) { @@ -594,35 +407,10 @@ class build_options { } _options.push_back(opt); } - - static std::shared_ptr make_option(const cldnn_build_option& option) { - switch (option.type) { - case cldnn_build_option_fusing: - return detail::build_option_traits::make_option(option); - case cldnn_build_option_learning_config: - return detail::build_option_traits::make_option(option); - case cldnn_build_option_optimize_data: - return detail::build_option_traits::make_option(option); - case cldnn_build_option_detection_output_gpu: - return detail::build_option_traits::make_option(option); - case cldnn_build_option_debug: - return detail::build_option_traits::make_option(option); - case cldnn_build_option_outputs: - return detail::build_option_traits::make_option(option); - case cldnn_build_option_tuning_config: - return detail::build_option_traits::make_option(option); - case cldnn_build_option_graph_dumps_dir: - return detail::build_option_traits::make_option(option); - case cldnn_build_option_serialization: - return detail::build_option_traits::make_option(option); - case cldnn_build_option_load_program: - return detail::build_option_traits::make_option(option); - default: - throw std::out_of_range("unsupported build option type"); - } - } }; +struct program_impl; + /// @brief Compiled program build from @ref topology by @ref engine struct program { friend struct network; @@ -632,17 +420,9 @@ struct program { /// @param[in] engine The engine which will be used to build the program. /// @param[in] topology The user-defined topology on which the network will be based. /// @param[in] options Program build options. See @ref build_option and @ref build_options for details. - program(engine const& engine, topology const& topology, build_options const& options = build_options()) - : _impl(check_status("program creation failed", [&](status_t* status) { - auto options_refs = options.get_refs(); - return cldnn_build_program(engine.get(), - topology.get(), - options_refs.data(), - options_refs.size(), - status); - })) {} - - /// @brief Retains the C API @ref cldnn_program handler stored in @p other. + program(engine const& engine, topology const& topology, build_options const& options = build_options()); + + /// @brief Copy constructor. program(program const& other) : _impl(other._impl) { retain(); } /// @brief Dereferences the counter of the underlying C API @ref cldnn_program handler. @@ -664,22 +444,18 @@ struct program { friend bool operator!=(const program& lhs, const program& rhs) { return !(lhs == rhs); } /// @brief Returns wrapped C API @ref cldnn_program handler. - ::cldnn_program get() const { return _impl; } + program_impl* get() const { return _impl; } private: - ::cldnn_program _impl; + program_impl* _impl; - explicit program(::cldnn_program impl) : _impl(impl) { + explicit program(program_impl* impl) : _impl(impl) { if (_impl == nullptr) throw std::invalid_argument("implementation pointer should not be null"); } - void retain() { - check_status("retain topology failed", [=](status_t* status) { cldnn_retain_program(_impl, status); }); - } - void release() { - check_status("retain topology failed", [=](status_t* status) { cldnn_release_program(_impl, status); }); - } + void retain(); + void release(); }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/proposal.hpp b/inference-engine/thirdparty/clDNN/api/proposal.hpp similarity index 75% rename from inference-engine/thirdparty/clDNN/api/CPP/proposal.hpp rename to inference-engine/thirdparty/clDNN/api/proposal.hpp index 4fcc5867b79a68..19fc536744bb25 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/proposal.hpp +++ b/inference-engine/thirdparty/clDNN/api/proposal.hpp @@ -17,10 +17,8 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include - -#include "../C/proposal.h" #include "primitive.hpp" +#include namespace cldnn { /// @addtogroup cpp_api C++ API @@ -30,7 +28,9 @@ namespace cldnn { /// @addtogroup cpp_primitives Primitives /// @{ -struct proposal : public primitive_base { +#define CLDNN_ROI_VECTOR_SIZE 5 + +struct proposal : public primitive_base { CLDNN_DECLARE_PRIMITIVE(proposal) proposal(const primitive_id& id, @@ -163,29 +163,6 @@ struct proposal : public primitive_basemax_proposals), - iou_threshold(dto->iou_threshold), - base_bbox_size(dto->base_bbox_size), - min_bbox_size(dto->min_bbox_size), - feature_stride(dto->feature_stride), - pre_nms_topn(dto->pre_nms_topn), - post_nms_topn(dto->post_nms_topn), - ratios(float_arr_to_vector(dto->ratios)), - scales(float_arr_to_vector(dto->scales)), - coordinates_offset(dto->coordinates_offset), - box_coordinate_scale(dto->box_coordinate_scale), - box_size_scale(dto->box_size_scale), - for_deformable(dto->for_deformable != 0), - swap_xy(dto->swap_xy != 0), - initial_clip(dto->initial_clip != 0), - clip_before_nms(dto->clip_before_nms != 0), - clip_after_nms(dto->clip_after_nms != 0), - round_ratios(dto->round_ratios != 0), - shift_anchors(dto->shift_anchors != 0), - normalize(dto->normalize != 0) {} - int max_proposals; float iou_threshold; int base_bbox_size; @@ -206,30 +183,6 @@ struct proposal : public primitive_base @@ -22,7 +21,7 @@ using namespace std; namespace cldnn { -struct pyramid_roi_align : public primitive_base { +struct pyramid_roi_align : public primitive_base { CLDNN_DECLARE_PRIMITIVE(pyramid_roi_align) pyramid_roi_align(const primitive_id &id, const primitive_id &input, const padding &output_padding = padding()) @@ -40,14 +39,5 @@ struct pyramid_roi_align : public primitive_base { +struct quantize : public primitive_base { CLDNN_DECLARE_PRIMITIVE(quantize) quantize(const primitive_id& id, @@ -46,14 +45,8 @@ struct quantize : public primitive_baselevels) {} - /// @brief levels The number of quantization levels. int levels; - -protected: - void update_dto(dto& dto) const override { dto.levels = levels; } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/reduce.hpp b/inference-engine/thirdparty/clDNN/api/reduce.hpp similarity index 65% rename from inference-engine/thirdparty/clDNN/api/CPP/reduce.hpp rename to inference-engine/thirdparty/clDNN/api/reduce.hpp index 13527e26128aa3..6bc3c5fe1e4862 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/reduce.hpp +++ b/inference-engine/thirdparty/clDNN/api/reduce.hpp @@ -17,7 +17,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/reduce.h" #include "primitive.hpp" #include @@ -32,43 +31,43 @@ namespace cldnn { /// @brief Select mode for the @ref reduce layer enum class reduce_mode : uint16_t { /// @brief Reduce max - max = cldnn_reduce_max, + max, /// @brief Reduce min - min = cldnn_reduce_min, + min, /// @brief Reduce mean - mean = cldnn_reduce_mean, + mean, /// @brief Reduce prod - prod = cldnn_reduce_prod, + prod, /// @brief Reduce sum - sum = cldnn_reduce_sum, + sum, /// @brief Reduce and - logical_and = cldnn_reduce_and, + logical_and, /// @brief Reduce or - logical_or = cldnn_reduce_or, + logical_or, /// @brief Reduce sum_square - sum_square = cldnn_reduce_sum_square, + sum_square, /// @brief Reduce l1 - l1 = cldnn_reduce_l1, + l1, /// @brief Reduce l2 - l2 = cldnn_reduce_l2, + l2, /// @brief Reduce log_sum - log_sum = cldnn_reduce_log_sum, + log_sum, /// @brief Reduce sum_exp - log_sum_exp = cldnn_reduce_log_sum_exp + log_sum_exp }; /// @brief Applies the specific reduction function along provided axes (second input) of the input tensor (first input). /// @details -struct reduce : public primitive_base { +struct reduce : public primitive_base { CLDNN_DECLARE_PRIMITIVE(reduce) enum reduce_axis { - along_b = cldnn_reduce_along_b, - along_f = cldnn_reduce_along_f, - along_x = cldnn_reduce_along_x, - along_y = cldnn_reduce_along_y, - along_z = cldnn_reduce_along_z, - along_w = cldnn_reduce_along_w + along_b, + along_f, + along_x, + along_y, + along_z, + along_w }; /// @brief Constructs reduce primitive @@ -79,22 +78,12 @@ struct reduce : public primitive_base { const int32_t keep_dims, const padding& output_padding = padding()) : primitive_base(id, {input}, output_padding), mode(mode), axes(axes), keep_dims(keep_dims) {} - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{reduce} - reduce(const dto* dto) : primitive_base(dto), mode(static_cast(dto->mode)), - axes(uint16_t_arr_to_vector(dto->axes)), keep_dims(dto->keep_dims) {} /// @brief Reduce operation type reduce_mode mode; /// @brief List of axes to reduce std::vector axes; /// @brief Keep the reduced dimension or not, 1 mean keep reduced dimension int32_t keep_dims; - -protected: - void update_dto(dto& dto) const override { - dto.mode = static_cast(mode); - dto.keep_dims = keep_dims; - dto.axes = uint16_t_vector_to_arr(axes); - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/region_yolo.hpp b/inference-engine/thirdparty/clDNN/api/region_yolo.hpp similarity index 76% rename from inference-engine/thirdparty/clDNN/api/CPP/region_yolo.hpp rename to inference-engine/thirdparty/clDNN/api/region_yolo.hpp index 044282680f022c..a1c68d5931b1dc 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/region_yolo.hpp +++ b/inference-engine/thirdparty/clDNN/api/region_yolo.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/region_yolo.h" #include "primitive.hpp" namespace cldnn { @@ -31,7 +30,7 @@ namespace cldnn { /// @details /// @par Algorithm: /// @par Where: -struct region_yolo : public primitive_base { +struct region_yolo : public primitive_base { CLDNN_DECLARE_PRIMITIVE(region_yolo) /// @brief Constructs region_yolo primitive. @@ -53,15 +52,6 @@ struct region_yolo : public primitive_basecoords), - classes(dto->classes), - num(dto->num), - mask_size(dto->mask_size), - do_softmax(dto->do_softmax != 0) {} - /// @brief Defines a scope of a region yolo normalization /// @details /// Specific behaviour is determined by these parameters, as follows: @@ -70,15 +60,6 @@ struct region_yolo : public primitive_base @@ -29,11 +28,19 @@ namespace cldnn { /// @addtogroup cpp_primitives Primitives /// @{ +/// @brief reorder mean operation modes +enum class reorder_mean_mode { + none, // val + subtract, // val - mean + mul, // val * mean + div, // val/mean +}; + /// @brief Changes how data is ordered in memory. Value type is not changed & all information is preserved. /// @details Corresponding values are bitwise equal before/after reorder. /// Also merged with subtraction layer, which can subtract, multiply or divide values based on mean_mode value, while doing reordering. /// NOTE THAT THIS WILL SUBTRACT THE SAME VALUES FROM EACH BATCH. -struct reorder : public primitive_base { +struct reorder : public primitive_base { CLDNN_DECLARE_PRIMITIVE(reorder) /// @brief Constructs reorder primitive with directly provided mean subtract values. @@ -45,7 +52,7 @@ struct reorder : public primitive_base { const primitive_id& input, const layout& output_layout, const std::vector& values_to_subtract = {}, - const cldnn_reorder_mean_mode mode = cldnn_reorder_mean_mode::mean_subtract) + const reorder_mean_mode mode = reorder_mean_mode::subtract) : primitive_base(id, {input}, output_layout.data_padding, optional_data_type {output_layout.data_type}), output_format(output_layout.format), mean(""), @@ -61,7 +68,7 @@ struct reorder : public primitive_base { const primitive_id& input, const layout& output_layout, primitive_id const& mean, - const cldnn_reorder_mean_mode mode = cldnn_reorder_mean_mode::mean_subtract) + const reorder_mean_mode mode = reorder_mean_mode::subtract) : primitive_base(id, {input}, output_layout.data_padding, optional_data_type {output_layout.data_type}), output_format(output_layout.format), mean(mean), @@ -78,7 +85,7 @@ struct reorder : public primitive_base { format output_format, data_types output_data_type, const std::vector& values_to_subtract = {}, - const cldnn_reorder_mean_mode mode = cldnn_reorder_mean_mode::mean_subtract, + const reorder_mean_mode mode = reorder_mean_mode::subtract, const padding& output_padding = padding()) : primitive_base(id, {input}, output_padding, optional_data_type{output_data_type}), output_format(output_format), @@ -96,7 +103,7 @@ struct reorder : public primitive_base { format output_format, data_types output_data_type, primitive_id const& mean, - const cldnn_reorder_mean_mode mode = cldnn_reorder_mean_mode::mean_subtract, + const reorder_mean_mode mode = reorder_mean_mode::subtract, const padding& output_padding = padding()) : primitive_base(id, {input}, output_padding, optional_data_type {output_data_type}), output_format(output_format), @@ -104,14 +111,6 @@ struct reorder : public primitive_base { subtract_per_feature(0), mean_mode(mode) {} - /// @brief Constructs a copy from basic C API @CLDNN_PRIMITIVE_DESC{reorder} - reorder(const dto* dto) - : primitive_base(dto), - output_format(dto->output_format), - mean(dto->mean_subtract), - subtract_per_feature(float_arr_to_vector(dto->subtract_per_feature)), - mean_mode(dto->mean_mode) {} - /// @brief Requested memory format. format output_format; /// @brief Primitive id to get mean subtract values. Ignored if subtract_per_featrue is set. @@ -119,7 +118,7 @@ struct reorder : public primitive_base { /// @brief Array of mean subtract values. std::vector subtract_per_feature; /// @brief Mode of mean execution - cldnn_reorder_mean_mode mean_mode; + reorder_mean_mode mean_mode; protected: std::vector> get_dependencies() const override { @@ -127,14 +126,8 @@ struct reorder : public primitive_base { return {}; return {mean}; } - - void update_dto(dto& dto) const override { - dto.output_format = static_cast(output_format.value); - dto.mean_subtract = mean.c_str(); - dto.subtract_per_feature = float_vector_to_arr(subtract_per_feature); - dto.mean_mode = mean_mode; - } }; + /// @} /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/reorg_yolo.hpp b/inference-engine/thirdparty/clDNN/api/reorg_yolo.hpp similarity index 82% rename from inference-engine/thirdparty/clDNN/api/CPP/reorg_yolo.hpp rename to inference-engine/thirdparty/clDNN/api/reorg_yolo.hpp index 993e84644b41be..e80790328c4529 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/reorg_yolo.hpp +++ b/inference-engine/thirdparty/clDNN/api/reorg_yolo.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/reorg_yolo.h" #include "primitive.hpp" namespace cldnn { @@ -31,7 +30,7 @@ namespace cldnn { /// @details /// @par Algorithm: /// @par Where: -struct reorg_yolo : public primitive_base { +struct reorg_yolo : public primitive_base { CLDNN_DECLARE_PRIMITIVE(reorg_yolo) /// @brief Constructs region_yolo primitive. @@ -44,16 +43,10 @@ struct reorg_yolo : public primitive_basestride) {} - /// @brief Defines a scope of a reorg yolo normalization /// @details /// Specific behaviour is determined by these parameters, as follows: uint32_t stride; - -private: - void update_dto(dto& dto) const override { dto.stride = stride; } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/reshape.hpp b/inference-engine/thirdparty/clDNN/api/reshape.hpp similarity index 85% rename from inference-engine/thirdparty/clDNN/api/CPP/reshape.hpp rename to inference-engine/thirdparty/clDNN/api/reshape.hpp index 05715b28edd7ae..27444d938598b8 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/reshape.hpp +++ b/inference-engine/thirdparty/clDNN/api/reshape.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/reshape.h" #include "primitive.hpp" namespace cldnn { @@ -32,7 +31,7 @@ namespace cldnn { /// @note reshape primitive is supposed only to reinterpret shape of the memory therefore it's not possible to change /// neither data type nor format of the input buffer and total number of elements in input and output (excluding paddings) must match. /// Please note that there is no guarantee that underlying data will be in proper format if primitive was explicitly added to output list. -struct reshape : public primitive_base { +struct reshape : public primitive_base { CLDNN_DECLARE_PRIMITIVE(reshape) /// @brief Constructs reshape primitive. @@ -48,17 +47,11 @@ struct reshape : public primitive_base { const padding& output_padding = padding()) : primitive_base(id, {input}, output_padding), output_shape(output_shape) {} - /// @brief Constructs a copy from basic C API @CLDNN_PRIMITIVE_DESC{reshape} - reshape(const dto* dto) : primitive_base(dto), output_shape(dto->output_shape) {} - /// @brief Requested memory shape. tensor output_shape; - -protected: - void update_dto(dto& dto) const override { dto.output_shape = output_shape; } }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/reverse_sequence.hpp b/inference-engine/thirdparty/clDNN/api/reverse_sequence.hpp similarity index 86% rename from inference-engine/thirdparty/clDNN/api/CPP/reverse_sequence.hpp rename to inference-engine/thirdparty/clDNN/api/reverse_sequence.hpp index 28e541959f0053..43dc95c93c337f 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/reverse_sequence.hpp +++ b/inference-engine/thirdparty/clDNN/api/reverse_sequence.hpp @@ -17,7 +17,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/reverse_sequence.h" #include "primitive.hpp" namespace cldnn { @@ -30,7 +29,7 @@ namespace cldnn { /// @brief /// @details -struct reverse_sequence : public primitive_base { +struct reverse_sequence : public primitive_base { CLDNN_DECLARE_PRIMITIVE(reverse_sequence) /// @brief Constructs reverse_sequence primitive. @@ -67,19 +66,10 @@ struct reverse_sequence : public primitive_baseseq_axis), batch_axis(dto->batch_axis) {} - /// @brief The axis which is partially reversed. int32_t seq_axis; /// @brief The axis along which reversal is performed. int32_t batch_axis; - -protected: - void update_dto(dto& dto) const override { - dto.seq_axis = seq_axis; - dto.batch_axis = batch_axis; - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/roi_pooling.hpp b/inference-engine/thirdparty/clDNN/api/roi_pooling.hpp similarity index 71% rename from inference-engine/thirdparty/clDNN/api/CPP/roi_pooling.hpp rename to inference-engine/thirdparty/clDNN/api/roi_pooling.hpp index 5823558a16189d..c44c231e36fcd2 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/roi_pooling.hpp +++ b/inference-engine/thirdparty/clDNN/api/roi_pooling.hpp @@ -17,7 +17,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once #include "pooling.hpp" -#include "../C/roi_pooling.h" #include "primitive.hpp" #include @@ -29,7 +28,7 @@ namespace cldnn { /// @addtogroup cpp_primitives Primitives /// @{ -struct roi_pooling : public primitive_base { +struct roi_pooling : public primitive_base { CLDNN_DECLARE_PRIMITIVE(roi_pooling) roi_pooling(const primitive_id& id, @@ -83,21 +82,6 @@ struct roi_pooling : public primitive_base(dto->mode)), - position_sensitive(dto->position_sensitive), - pooled_width(dto->pooled_width), - pooled_height(dto->pooled_height), - spatial_scale(dto->spatial_scale), - trans_std(dto->trans_std), - no_trans(dto->no_trans), - output_dim(dto->output_dim), - part_size(dto->part_size), - group_size(dto->group_size), - spatial_bins_x(dto->spatial_bins_x), - spatial_bins_y(dto->spatial_bins_y) {} - pooling_mode mode; bool position_sensitive; int pooled_width; @@ -110,22 +94,6 @@ struct roi_pooling : public primitive_base(mode); - dto.position_sensitive = position_sensitive; - dto.pooled_width = pooled_width; - dto.pooled_height = pooled_height; - dto.spatial_scale = spatial_scale; - dto.trans_std = trans_std; - dto.no_trans = no_trans; - dto.part_size = part_size; - dto.group_size = group_size; - dto.output_dim = output_dim; - dto.spatial_bins_x = spatial_bins_x; - dto.spatial_bins_y = spatial_bins_y; - } }; /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/scale.hpp b/inference-engine/thirdparty/clDNN/api/scale.hpp similarity index 89% rename from inference-engine/thirdparty/clDNN/api/CPP/scale.hpp rename to inference-engine/thirdparty/clDNN/api/scale.hpp index 7996a8432b9b69..445b750da7e895 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/scale.hpp +++ b/inference-engine/thirdparty/clDNN/api/scale.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/scale.h" #include "primitive.hpp" #include @@ -41,7 +40,7 @@ namespace cldnn { /// Performs scale over feature when the scale feature size is equal to input feature size.
/// Performs scale over feature in batch when the scale feature and scale batch sizes are equal to input feature and input batch sizes.
/// Optionally it can also add provided biases by providing bias data.
-struct scale : public primitive_base { +struct scale : public primitive_base { CLDNN_DECLARE_PRIMITIVE(scale) /// @brief Constructs scale primitive without adding bias. @@ -68,12 +67,6 @@ struct scale : public primitive_base { const padding& output_padding = padding()) : primitive_base(id, {input, scale_input}, output_padding), bias(bias) {} - /// @brief Constructs a copy from C API @CLDNN_PRIMITIVE_DESC{scale} - scale(const dto* dto) : primitive_base(dto), bias(dto->bias) { - if (dto->input.size != 2) - throw std::invalid_argument("scale dto should contains exactly 2 inputs"); - } - /// @brief Primitive id containing bias data. primitive_id bias; @@ -84,8 +77,6 @@ struct scale : public primitive_base { else return {bias}; } - - void update_dto(dto& dto) const override { dto.bias = bias.c_str(); } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/scale_grad_input.hpp b/inference-engine/thirdparty/clDNN/api/scale_grad_input.hpp similarity index 82% rename from inference-engine/thirdparty/clDNN/api/CPP/scale_grad_input.hpp rename to inference-engine/thirdparty/clDNN/api/scale_grad_input.hpp index fb31e13a94ec4b..667cf5b3d2f9f0 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/scale_grad_input.hpp +++ b/inference-engine/thirdparty/clDNN/api/scale_grad_input.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/scale_grad_input.h" #include "primitive.hpp" #include @@ -29,7 +28,7 @@ namespace cldnn { /// @{ /// @brief Performs scale primitive backward for input. -struct scale_grad_input : public primitive_base { +struct scale_grad_input : public primitive_base { CLDNN_DECLARE_PRIMITIVE(scale_grad_input) /// @brief Constructs scale_grad_input. @@ -43,16 +42,8 @@ struct scale_grad_input : public primitive_baseinput.size != 2) - throw std::invalid_argument("scale_grad_input dto should contains exactly 2 inputs"); - } - protected: std::vector> get_dependencies() const override { return {}; } - - void update_dto(dto&) const override {} }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/scale_grad_weights.hpp b/inference-engine/thirdparty/clDNN/api/scale_grad_weights.hpp similarity index 89% rename from inference-engine/thirdparty/clDNN/api/CPP/scale_grad_weights.hpp rename to inference-engine/thirdparty/clDNN/api/scale_grad_weights.hpp index cf773d20d65e10..d13b18d137ace8 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/scale_grad_weights.hpp +++ b/inference-engine/thirdparty/clDNN/api/scale_grad_weights.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/scale_grad_weights.h" #include "primitive.hpp" #include @@ -29,7 +28,7 @@ namespace cldnn { /// @{ /// @brief Performs scale layer backward for scale_input and biases. -struct scale_grad_weights : public primitive_base { +struct scale_grad_weights : public primitive_base { CLDNN_DECLARE_PRIMITIVE(scale_grad_weights) /// @brief Constructs scale_grad_weights primitive without bias. @@ -97,15 +96,6 @@ struct scale_grad_weights : public primitive_basescale_input), - bias(dto->bias), - prev_scale_grad(dto->prev_scale_grad), - prev_bias_grad(dto->prev_bias_grad), - scale_grad(dto->scale_grad) {} - /// @brief Scale input primitive id. primitive_id scale_input; /// @brief Primitive id containing bias data. @@ -134,14 +124,6 @@ struct scale_grad_weights : public primitive_base { +struct select : public primitive_base { } }; -namespace { -struct attach { - attach() { - implementation_map::add( + {{std::make_tuple(engine_types::ocl, data_types::f32, format::yxfb), select_gpu::create}, + {std::make_tuple(engine_types::ocl, data_types::f16, format::yxfb), select_gpu::create}, + {std::make_tuple(engine_types::ocl, data_types::i8, format::yxfb), select_gpu::create}, + {std::make_tuple(engine_types::ocl, data_types::u8, format::yxfb), select_gpu::create}, - {std::make_tuple(engine_types::ocl, data_types::f32, format::byxf), select_gpu::create}, - {std::make_tuple(engine_types::ocl, data_types::f16, format::byxf), select_gpu::create}, - {std::make_tuple(engine_types::ocl, data_types::i8, format::byxf), select_gpu::create}, - {std::make_tuple(engine_types::ocl, data_types::u8, format::byxf), select_gpu::create}}); - } + {std::make_tuple(engine_types::ocl, data_types::f32, format::bfyx), select_gpu::create}, + {std::make_tuple(engine_types::ocl, data_types::f16, format::bfyx), select_gpu::create}, + {std::make_tuple(engine_types::ocl, data_types::i8, format::bfyx), select_gpu::create}, + {std::make_tuple(engine_types::ocl, data_types::u8, format::bfyx), select_gpu::create}, - ~attach() {} -}; -attach attach_impl; -} // namespace + {std::make_tuple(engine_types::ocl, data_types::f32, format::byxf), select_gpu::create}, + {std::make_tuple(engine_types::ocl, data_types::f16, format::byxf), select_gpu::create}, + {std::make_tuple(engine_types::ocl, data_types::i8, format::byxf), select_gpu::create}, + {std::make_tuple(engine_types::ocl, data_types::u8, format::byxf), select_gpu::create}}); +} + +} // namespace detail } // namespace gpu } // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/src/gpu/shuffle_channels_gpu.cpp b/inference-engine/thirdparty/clDNN/src/gpu/shuffle_channels_gpu.cpp index 3d9df1417c8b00..5558106003dc82 100644 --- a/inference-engine/thirdparty/clDNN/src/gpu/shuffle_channels_gpu.cpp +++ b/inference-engine/thirdparty/clDNN/src/gpu/shuffle_channels_gpu.cpp @@ -60,18 +60,16 @@ struct shuffle_channels_gpu : typed_primitive_gpu_impl { } }; -namespace { -struct attach { - attach() { - auto val_fw = shuffle_channels_gpu::create; - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::bfyx), - val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::bfyx), - val_fw); - } - ~attach() = default; -}; -attach attach_impl; -} // namespace +namespace detail { + +attach_shuffle_channels_gpu::attach_shuffle_channels_gpu() { + auto val_fw = shuffle_channels_gpu::create; + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::bfyx), + val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::bfyx), + val_fw); +} + +} // namespace detail } // namespace gpu } // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/src/gpu/softmax_gpu.cpp b/inference-engine/thirdparty/clDNN/src/gpu/softmax_gpu.cpp index fba473906cf034..6fa7d062f15c41 100644 --- a/inference-engine/thirdparty/clDNN/src/gpu/softmax_gpu.cpp +++ b/inference-engine/thirdparty/clDNN/src/gpu/softmax_gpu.cpp @@ -88,24 +88,22 @@ struct softmax_gpu : typed_primitive_gpu_impl { } }; -namespace { -struct attach { - attach() { - auto val_fw = softmax_gpu::create; - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::yxfb), val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::yxfb), val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::bfyx), val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::bfyx), val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::byxf), val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::byxf), val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::bfzyx), val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::bfzyx), val_fw); - } - ~attach() {} -}; - -attach attach_impl; -} // namespace - +namespace detail { + +attach_softmax_gpu::attach_softmax_gpu() { + auto val_fw = softmax_gpu::create; + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::yxfb), val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::yxfb), val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::bfyx), val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::bfyx), val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::byxf), val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::byxf), val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::bfzyx), val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::bfzyx), val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::bfzyx_f16), val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::bfzyx_f16), val_fw); +} + +} // namespace detail } // namespace gpu } // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/src/gpu/softmax_loss_grad_gpu.cpp b/inference-engine/thirdparty/clDNN/src/gpu/softmax_loss_grad_gpu.cpp index 33dc60a94b9cc3..2dd03647f609d5 100644 --- a/inference-engine/thirdparty/clDNN/src/gpu/softmax_loss_grad_gpu.cpp +++ b/inference-engine/thirdparty/clDNN/src/gpu/softmax_loss_grad_gpu.cpp @@ -51,28 +51,24 @@ struct softmax_loss_grad_gpu : typed_primitive_gpu_impl { } }; -namespace { -struct attach { - attach() { - auto val_fw = softmax_loss_grad_gpu::create; - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::yxfb), - val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::yxfb), - val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::bfyx), - val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::bfyx), - val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::byxf), - val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::byxf), - val_fw); - } - ~attach() {} -}; +namespace detail { -attach attach_impl; -} // namespace +attach_softmax_loss_grad_gpu::attach_softmax_loss_grad_gpu() { + auto val_fw = softmax_loss_grad_gpu::create; + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::yxfb), + val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::yxfb), + val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::bfyx), + val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::bfyx), + val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::byxf), + val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::byxf), + val_fw); +} +} // namespace detail } // namespace gpu } // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/src/gpu/strided_slice_gpu.cpp b/inference-engine/thirdparty/clDNN/src/gpu/strided_slice_gpu.cpp index 8b7b49f3a5f1ec..8818d01eb7f70c 100644 --- a/inference-engine/thirdparty/clDNN/src/gpu/strided_slice_gpu.cpp +++ b/inference-engine/thirdparty/clDNN/src/gpu/strided_slice_gpu.cpp @@ -81,18 +81,16 @@ struct strided_slice_gpu : typed_primitive_gpu_impl { } }; -namespace { -struct attach { - attach() { - auto val_fw = strided_slice_gpu::create; - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::bfyx), - val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::bfyx), - val_fw); - } - ~attach() = default; -}; -attach attach_impl; -} // namespace +namespace detail { + +attach_strided_slice_gpu::attach_strided_slice_gpu() { + auto val_fw = strided_slice_gpu::create; + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::bfyx), + val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::bfyx), + val_fw); +} + +} // namespace detail } // namespace gpu } // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/src/gpu/tile_gpu.cpp b/inference-engine/thirdparty/clDNN/src/gpu/tile_gpu.cpp index 722ff50b94ce1f..43b2f7870b45b1 100644 --- a/inference-engine/thirdparty/clDNN/src/gpu/tile_gpu.cpp +++ b/inference-engine/thirdparty/clDNN/src/gpu/tile_gpu.cpp @@ -71,19 +71,17 @@ struct tile_gpu : typed_primitive_gpu_impl { } }; -namespace { -struct attach { - attach() { - auto val_fw = tile_gpu::create; +namespace detail { - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::bfyx), val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::bfyx), val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::bfzyx), val_fw); - implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::bfzyx), val_fw); - } - ~attach() {} -}; -attach attach_impl; -} // namespace +attach_tile_gpu::attach_tile_gpu() { + auto val_fw = tile_gpu::create; + + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::bfyx), val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::bfyx), val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f32, format::bfzyx), val_fw); + implementation_map::add(std::make_tuple(engine_types::ocl, data_types::f16, format::bfzyx), val_fw); +} + +} // namespace detail } // namespace gpu } // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/src/gpu/upsampling_gpu.cpp b/inference-engine/thirdparty/clDNN/src/gpu/upsampling_gpu.cpp index e12d758454e2d4..8b918f6fb4da44 100644 --- a/inference-engine/thirdparty/clDNN/src/gpu/upsampling_gpu.cpp +++ b/inference-engine/thirdparty/clDNN/src/gpu/upsampling_gpu.cpp @@ -49,7 +49,7 @@ struct upsampling_gpu : typed_primitive_gpu_impl { const auto& primitive = arg.get_primitive(); if (primitive->with_activation) - convert_activation_func_params(primitive, us_params.activation); + convert_activation_func_params(primitive, us_params.activations); us_params.num_filter = primitive->num_filter; us_params.sampleType = convert_to_sample_type(primitive->sample_type); @@ -68,20 +68,18 @@ struct upsampling_gpu : typed_primitive_gpu_impl { } }; -namespace { -struct attach { - attach() { - implementation_map::add( - {{std::make_tuple(engine_types::ocl, data_types::f32, format::yxfb), upsampling_gpu::create}, - {std::make_tuple(engine_types::ocl, data_types::f16, format::yxfb), upsampling_gpu::create}, - {std::make_tuple(engine_types::ocl, data_types::f32, format::bfyx), upsampling_gpu::create}, - {std::make_tuple(engine_types::ocl, data_types::f16, format::bfyx), upsampling_gpu::create}, - {std::make_tuple(engine_types::ocl, data_types::f32, format::byxf), upsampling_gpu::create}, - {std::make_tuple(engine_types::ocl, data_types::f16, format::byxf), upsampling_gpu::create}}); - } - ~attach() {} -}; -attach attach_impl; -} // namespace +namespace detail { + +attach_upsampling_gpu::attach_upsampling_gpu() { + implementation_map::add( + {{std::make_tuple(engine_types::ocl, data_types::f32, format::yxfb), upsampling_gpu::create}, + {std::make_tuple(engine_types::ocl, data_types::f16, format::yxfb), upsampling_gpu::create}, + {std::make_tuple(engine_types::ocl, data_types::f32, format::bfyx), upsampling_gpu::create}, + {std::make_tuple(engine_types::ocl, data_types::f16, format::bfyx), upsampling_gpu::create}, + {std::make_tuple(engine_types::ocl, data_types::f32, format::byxf), upsampling_gpu::create}, + {std::make_tuple(engine_types::ocl, data_types::f16, format::byxf), upsampling_gpu::create}}); +} + +} // namespace detail } // namespace gpu } // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/src/gpu/wait_for_events_gpu.cpp b/inference-engine/thirdparty/clDNN/src/gpu/wait_for_events_gpu.cpp index 2d563fd12a8afa..ea725da00611bf 100644 --- a/inference-engine/thirdparty/clDNN/src/gpu/wait_for_events_gpu.cpp +++ b/inference-engine/thirdparty/clDNN/src/gpu/wait_for_events_gpu.cpp @@ -19,6 +19,7 @@ #include "prior_box_inst.h" #include "input_layout_inst.h" #include "implementation_map.h" +#include "register_gpu.hpp" #include "network_impl.h" #include "events_waiter.h" @@ -51,19 +52,20 @@ class wait_for_events_gpu : public primitive_impl { } }; -namespace { -struct attach { - attach() { - implementation_map::add({{engine_types::ocl, wait_for_events_gpu::create_data}}); +namespace detail { - implementation_map::add({{engine_types::ocl, wait_for_events_gpu::create_input_layout}}); +attach_data_gpu::attach_data_gpu() { + implementation_map::add({ {engine_types::ocl, wait_for_events_gpu::create_data} }); +} - implementation_map::add({{engine_types::ocl, wait_for_events_gpu::create_prior_box}}); - } - ~attach() {} -}; -attach attach_impl; -} // namespace +attach_input_layout_gpu::attach_input_layout_gpu() { + implementation_map::add({{engine_types::ocl, wait_for_events_gpu::create_input_layout}}); +} + +attach_prior_box_gpu::attach_prior_box_gpu() { + implementation_map::add({{engine_types::ocl, wait_for_events_gpu::create_prior_box}}); +} +} // namespace detail } // namespace gpu } // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/add_required_reorders.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/add_required_reorders.cpp index 8d5211bb25754a..21cf01572b2c4f 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/add_required_reorders.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/add_required_reorders.cpp @@ -25,6 +25,7 @@ #include "scale_inst.h" #include "tensor_type.h" #include +#include /* This pass checks if data formats (layouts) of output/input in hidden layers match. @@ -41,12 +42,11 @@ void add_required_reorders::add_reorder(program_impl& p, program_node* node, pro // ToDo: add a method to program_impl class which adds an intermediate node given a node and its user auto it = std::find(usr->get_dependencies().begin(), usr->get_dependencies().end(), node); if (it == usr->get_dependencies().end()) { - throw error("Inconcistency in topology description: user of a node is not present among its dependecies.", - CLDNN_ERROR); + throw std::runtime_error("Inconcistency in topology description: user of a node is not present among its dependecies."); } auto idx = it - usr->get_dependencies().begin(); if (idx < 0 || (size_t)idx >= usr->get_dependencies().size()) { - throw error("Internal Error: container index out of range exception.", CLDNN_ERROR); + throw std::runtime_error("Internal Error: container index out of range exception."); } p.add_intermediate(new_reorder_node, *usr, idx); } @@ -81,9 +81,8 @@ void add_required_reorders::run(program_impl& p) { correct_layout_selected = true; break; } else { - throw error("Internal Error: no layout format available for " + usr->id() + " comaptible with " + - node->id(), - CLDNN_ERROR); + throw std::runtime_error("Internal Error: no layout format available for " + usr->id() + " comaptible with " + + node->id()); } } } @@ -110,9 +109,8 @@ void add_required_reorders::run(program_impl& p) { } if (!correct_layout_selected) { - throw error("Internal Error: no implementation for " + usr->id() + - " kernel which satisfies output format dependecies.", - CLDNN_ERROR); + throw std::runtime_error("Internal Error: no implementation for " + usr->id() + + " kernel which satisfies output format dependecies."); } } diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/calculate_prior_boxes.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/calculate_prior_boxes.cpp index f71e6d2cefae0d..ac2ace87d9fe39 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/calculate_prior_boxes.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/calculate_prior_boxes.cpp @@ -36,12 +36,10 @@ void calculate_prior_boxes::run(program_impl& p) { pb_node.calc_result(); p.remove_connection(pb_node.input(), pb_node); - auto& result = pb_node.get_result_buffer(); - result.add_ref(); // need to inc ref count since we will be assigning this memory as cldnn_memory in next line - // that is not ref_count_obj - auto cpp_mem = details::memory_c_to_cpp_converter::convert(api_cast(&result)); + auto result = pb_node.get_result_buffer(); + auto cpp_mem = memory(result.detach()); auto& data_node = p.get_or_create(std::make_shared("_cldnn_tmp_" + pb_node.id() + "_result", cpp_mem)); p.replace(pb_node, data_node); } -} \ No newline at end of file +} diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/eltwise_remove_stride.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/eltwise_remove_stride.cpp index 2011533c82c326..77df35793347f4 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/eltwise_remove_stride.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/eltwise_remove_stride.cpp @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// -#include "api/CPP/tensor.hpp" +#include "api/tensor.hpp" #include "pass_manager.h" diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/eltwise_shrinking.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/eltwise_shrinking.cpp index 42531366ff96e3..6088270aa9a30f 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/eltwise_shrinking.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/eltwise_shrinking.cpp @@ -34,6 +34,9 @@ void eltwise_shrinking::run(program_impl& p) { } } + if (node->get_output_layout().format == format::fs_b_yx_fsv32) + continue; + const auto eltw = std::static_pointer_cast(node->get_primitive()); // TODO: support cases which already have stride! if (eltw->stride.empty() && !node->get_users().empty()) { diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/graph_initializations.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/graph_initializations.cpp index 4c08450d50ea17..34b8fb4071445c 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/graph_initializations.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/graph_initializations.cpp @@ -91,7 +91,7 @@ void graph_initializations::replace_nodes(program_impl& p) { } // For all the other dimensions, copy from the split_input - for (int dimension = 0; dimension < CLDNN_TENSOR_DIM_MAX; dimension++) { + for (int dimension = 0; dimension < tensor_dim_max; dimension++) { reference_input_size.raw[dimension] = (reference_input_size.raw[dimension] == 0) ? output_layout_size.raw[dimension] : reference_input_size.raw[dimension]; @@ -131,14 +131,19 @@ void graph_initializations::replace_nodes(program_impl& p) { auto num_filter = upsampling_prim->num_filter; // setting deconvolution parameters based on upsampling input - auto scale = static_cast(upsampling_prim->scale); - tensor stride(1, 1, scale, scale); - auto offset = static_cast(std::ceil((scale - 1) / 2.f)); - tensor input_offset(0, 0, -offset, -offset); + auto upsampled_size = node->get_output_layout().size; + auto input_size = input_node.get_output_layout().size; + auto scale_x = static_cast(upsampled_size.spatial[0] / input_size.spatial[0]); + auto scale_y = static_cast(upsampled_size.spatial[1] / input_size.spatial[1]); + tensor stride(1, 1, scale_x, scale_y); + auto offset_x = static_cast(std::ceil((scale_x - 1) / 2.f)); + auto offset_y = static_cast(std::ceil((scale_y - 1) / 2.f)); + tensor input_offset(0, 0, -offset_x, -offset_y); // setting weights for deconvolution - auto kernel_size = static_cast((2 * scale) - (scale % 2)); - layout weights_layout(data_types::f32, format::bfyx, tensor(1, 1, kernel_size, kernel_size)); + auto kernel_size_x = static_cast((2 * scale_x) - (static_cast(scale_x) % 2)); + auto kernel_size_y = static_cast((2 * scale_y) - (static_cast(scale_y) % 2)); + layout weights_layout(data_types::f32, format::bfyx, tensor(1, 1, kernel_size_x, kernel_size_y)); std::vector weights_vec; for (uint32_t weights_idx = 0; weights_idx < num_filter; weights_idx++) { @@ -146,14 +151,16 @@ void graph_initializations::replace_nodes(program_impl& p) { mem_lock dst{data_to_allocate}; float* dst_data = dst.data(); // initialize with bilinear weights data - auto f = static_cast(std::ceil(kernel_size / 2.0f)); - float c = (2 * f - 1 - f % 2) / (2.f * f); + auto f_x = static_cast(std::ceil(kernel_size_x / 2.0f)); + auto f_y = static_cast(std::ceil(kernel_size_y / 2.0f)); + float c_x = (2 * f_x - 1 - f_x % 2) / (2.f * f_x); + float c_y = (2 * f_y - 1 - f_y % 2) / (2.f * f_y); float x = 0.f; float y = 0.f; for (size_t i = 0; i < weights_layout.count(); ++i) { - x = static_cast(i % kernel_size); - y = static_cast((i / kernel_size) % kernel_size); - dst_data[i] = (1 - std::abs(x / f - c)) * (1 - std::abs(y / f - c)); + x = static_cast(i % kernel_size_x); + y = static_cast((i / kernel_size_x) % kernel_size_y); + dst_data[i] = (1 - std::abs(x / f_x - c_x)) * (1 - std::abs(y / f_y - c_y)); } // create weights primitive, with dummy memory which will be replaced in firther step @@ -214,7 +221,8 @@ void graph_initializations::replace_nodes(program_impl& p) { auto& input_node = node->get_dependency(0); // disable for 5D - if (input_node.get_output_layout().format == format::bfzyx) + if (input_node.get_output_layout().format == format::bfzyx || + input_node.get_output_layout().format == format::bfzyx_f16) continue; primitive_id input_id = deconv_prim->input[0]; @@ -228,8 +236,6 @@ void graph_initializations::replace_nodes(program_impl& p) { std::vector bias_vec; for (auto& bias_id : biases) bias_vec.push_back(bias_id); auto input_offset = deconv_prim->input_offset; - auto with_activation = deconv_prim->with_activation; - auto activation_negative_slope = deconv_prim->activation_negative_slope; auto output_padding = deconv_prim->output_padding; // remove deconvolution node and its connections to weights and biases, rename it and move to the optimized @@ -267,8 +273,6 @@ void graph_initializations::replace_nodes(program_impl& p) { stride, input_offset, tensor{1, 1, 1, 1}, - with_activation, - activation_negative_slope, output_padding); p.get_or_create(conv_prim); } else { @@ -278,8 +282,6 @@ void graph_initializations::replace_nodes(program_impl& p) { stride, input_offset, tensor{1, 1, 1, 1}, - with_activation, - activation_negative_slope, output_padding); p.get_or_create(conv_prim); } @@ -456,10 +458,10 @@ void graph_initializations::handle_lstm(program_impl& p) { } } - bool emit_last_cell = lstm_prim->output_selection == cldnn_lstm_output_hidden_cell || - lstm_prim->output_selection == cldnn_lstm_output_sequence_cell; - bool emit_sequence = lstm_prim->output_selection == cldnn_lstm_output_sequence_cell || - lstm_prim->output_selection == cldnn_lstm_output_sequence; + bool emit_last_cell = lstm_prim->output_selection == lstm_output_selection::hidden_cell || + lstm_prim->output_selection == lstm_output_selection::sequence_cell; + bool emit_sequence = lstm_prim->output_selection == lstm_output_selection::sequence_cell || + lstm_prim->output_selection == lstm_output_selection::sequence; std::vector cell_list(directions * sequence_len); std::vector hidden_list(directions * sequence_len); diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/handle_reshape.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/handle_reshape.cpp index bbd5708c20729a..f8e0ad6e79e349 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/handle_reshape.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/handle_reshape.cpp @@ -42,7 +42,7 @@ void handle_reshape::run(program_impl& p) { while (node_itr != p.get_processing_order().end()) { auto& node = (*node_itr++); program_helpers::do_for_types(*node, [&p](reshape_node& node) { - if (node.is_output() || node.get_users().size() > 1 || node.get_fused_activation_func() != activation_none) + if (node.is_output() || node.get_users().size() > 1 || !node.get_fused_activations_funcs().empty()) return; auto& out_node = node.get_users().front(); diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/post_input_reorder.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/post_input_reorder.cpp index cafa9a0935f561..5e29b05e53a369 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/post_input_reorder.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/post_input_reorder.cpp @@ -20,6 +20,7 @@ #include "gpu/primitive_gpu_base.h" #include "fully_connected/fully_connected_params.h" #include +#include /* This pass checks if if primitive's input format matches implementation's input format @@ -39,12 +40,11 @@ program_node& post_input_reorder::add_reorder(program_impl& p, // ToDo: add a method to program_impl class which adds an intermediate node given a node and its user auto it = std::find(usr->get_dependencies().begin(), usr->get_dependencies().end(), node); if (it == usr->get_dependencies().end()) { - throw error("Inconcistency in topology description: user of a node is not present among its dependecies.", - CLDNN_ERROR); + throw std::runtime_error("Inconcistency in topology description: user of a node is not present among its dependecies."); } auto idx = it - usr->get_dependencies().begin(); if (idx < 0 || (size_t)idx >= usr->get_dependencies().size()) { - throw error("Internal Error: container index out of range exception.", CLDNN_ERROR); + throw std::runtime_error("Internal Error: container index out of range exception."); } p.add_intermediate(new_reorder_node, *usr, idx); return new_reorder_node; diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/post_optimize_weights.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/post_optimize_weights.cpp index ea43c18a0ede35..8e19ddd48f84be 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/post_optimize_weights.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/post_optimize_weights.cpp @@ -18,62 +18,43 @@ #include "pass_manager.h" #include "program_helpers.h" -#include "api_extension/CPP/fused_conv_eltwise.hpp" +#include "api_extension/fused_conv_eltwise.hpp" #include "include/fused_conv_eltwise_inst.h" #include "include/binary_convolution_inst.h" #include "include/deformable_convolution_inst.h" +#include "lstm_dynamic_input_inst.h" namespace cldnn { -post_optimize_weights::post_optimize_weights(layout_optimizer& lo_ref) - : base_pass("post_optimize_weights"), _lo(lo_ref) {} - -void post_optimize_weights::run(program_impl& p) { run(p, _lo); } +post_optimize_weights::post_optimize_weights(reorder_factory& rf_ref) + : base_pass("post_optimize_weights"), _rf(rf_ref) {} // function which prepares given primitive for weights optimization -template -void post_optimize_weights::optimize_weights(T& node, layout_optimizer& lo, program_impl& p) { - auto weights_offset = node.get_primitive()->input.size(); - auto bias_offset = weights_offset + program_helpers::wrap_if_single(node.get_primitive()->weights).size(); - for (auto i = weights_offset; i < bias_offset; i++) { - auto& weights = node.get_dependency(i); - auto* impl = node.get_selected_impl().get(); - auto output_layout = node.get_output_layout(); - auto& weights_node = node.get_dependency(1); - auto weights_layout = weights_node.get_output_layout(); - const auto weights_type = layout_optimizer::data_type::weights; +template post_optimize_weights::weights_bias_offset post_optimize_weights::get_weights_bias_offset(const T& node) { + return weights_bias_offset(node.get_primitive()->input.size(), program_helpers::wrap_if_single(node.get_primitive()->weights).size()); +} - auto reorders = lo.get_generic_layer(impl->_weights_reorder_params, weights.id(), weights_layout, weights_type); +template <> +post_optimize_weights::weights_bias_offset post_optimize_weights::get_weights_bias_offset(const fused_conv_eltwise_node& node) { + return weights_bias_offset(node.get_primitive()->input.size(), program_helpers::wrap_if_single(node.get_primitive()->conv.weights).size()); +} - for (auto& reorder : reorders) { - // insert new generic_layer node to topology - p.add_intermediate(reorder.first, node, i, !reorder.second); - // set generic_layer's node output layout and implementation - auto& g_node = node.get_dependency(i); - g_node.get_output_layout(false); - g_node.selected_impl = g_node.type()->choose_impl(p.get_engine(), g_node); - } - // set the old output layout and do not invalidate users as change of weights will not affect output layout - node.set_output_layout(output_layout, false); - } +template <> +post_optimize_weights::weights_bias_offset post_optimize_weights::get_weights_bias_offset(const lstm_dynamic_input_node& node) { + return weights_bias_offset(node.get_primitive()->input.size() + 1, program_helpers::wrap_if_single(node.get_primitive()->weights).size()); } // function which prepares given primitive for weights optimization -template <> -void post_optimize_weights::optimize_weights(fused_conv_eltwise_node& node, - layout_optimizer& lo, - program_impl& p) { - auto weights_offset = node.get_primitive()->input.size(); - auto bias_offset = weights_offset + program_helpers::wrap_if_single(node.get_primitive()->conv.weights).size(); - for (auto i = weights_offset; i < bias_offset; i++) { - auto& weights = node.get_dependency(i); +template +void post_optimize_weights::optimize_weights(T& node, program_impl& p) { + auto offsets = get_weights_bias_offset(node); + for (auto i = offsets.weights_offset; i < offsets.bias_offset; i++) { + auto& weights_node = node.get_dependency(i); auto* impl = node.get_selected_impl().get(); auto output_layout = node.get_output_layout(); - auto& weights_node = node.get_dependency(1); auto weights_layout = weights_node.get_output_layout(); - const auto weights_type = layout_optimizer::data_type::weights; - auto reorders = lo.get_generic_layer(impl->_weights_reorder_params, weights.id(), weights_layout, weights_type); + auto reorders = _rf.get_weights_reorder(weights_node.id(), weights_layout, impl->_weights_reorder_params); for (auto& reorder : reorders) { // insert new generic_layer node to topology @@ -88,37 +69,23 @@ void post_optimize_weights::optimize_weights(fused_conv } } -template void post_optimize_weights::optimize_weights(convolution_node& node, - layout_optimizer& lo, - program_impl& p); -template void post_optimize_weights::optimize_weights(deconvolution_node& node, - layout_optimizer& lo, - program_impl& p); -template void post_optimize_weights::optimize_weights(fully_connected_node& node, - layout_optimizer& lo, - program_impl& p); -template void post_optimize_weights::optimize_weights(binary_convolution_node& node, - layout_optimizer& lo, - program_impl& p); -template void post_optimize_weights::optimize_weights(deformable_conv_node& node, - layout_optimizer& lo, - program_impl& p); - -void post_optimize_weights::run(program_impl& p, layout_optimizer& lo) { +void post_optimize_weights::run(program_impl& p) { for (auto& node : p.get_processing_order()) { if (node->type() == convolution::type_id()) { - optimize_weights(node->as(), lo, p); + optimize_weights(node->as(), p); } if (node->type() == binary_convolution::type_id()) { - optimize_weights(node->as(), lo, p); + optimize_weights(node->as(), p); } else if (node->type() == deconvolution::type_id()) { - optimize_weights(node->as(), lo, p); + optimize_weights(node->as(), p); } else if (node->type() == deformable_conv::type_id()) { - optimize_weights(node->as(), lo, p); + optimize_weights(node->as(), p); } else if (node->type() == fully_connected::type_id()) { - optimize_weights(node->as(), lo, p); + optimize_weights(node->as(), p); } else if (node->type() == fused_conv_eltwise::type_id()) { - optimize_weights(node->as(), lo, p); + optimize_weights(node->as(), p); + } else if (node->type() == lstm_dynamic_input::type_id()) { + optimize_weights(node->as(), p); } } } diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/pre_optimize_bias.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/pre_optimize_bias.cpp index 2a62c6f6c5c327..d9d4e1afb9f758 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/pre_optimize_bias.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/pre_optimize_bias.cpp @@ -25,50 +25,57 @@ using namespace cldnn; -pre_optimize_bias::pre_optimize_bias(layout_optimizer& lo_ref) : base_pass("pre_optimize_bias"), _lo(lo_ref) {} +pre_optimize_bias::pre_optimize_bias(reorder_factory& rf_ref) : base_pass("pre_optimize_bias"), _rf(rf_ref) {} -void pre_optimize_bias::run(program_impl& p) { run(p, _lo); } +void pre_optimize_bias::run(program_impl& p) { run(p, _rf); } // function which prepares given primitive for weights optimization template -void pre_optimize_bias::optimize_bias(T& node, layout_optimizer& lo, program_impl& p) { +void pre_optimize_bias::optimize_bias(T& node, reorder_factory& rf, program_impl& p) { layout output_layout = node.get_output_layout(); size_t weights_offset = node.get_primitive()->input.size(); size_t bias_offset = weights_offset + program_helpers::wrap_if_single(node.get_primitive()->weights).size(); - for (size_t i = bias_offset; i < node.get_dependencies().size(); ++i) { + for (size_t i = bias_offset; i < node.get_dependencies().size() - node.get_fused_inputs_count(); ++i) { // find weights primitive with given pimitive_id and add it to weights_optimizer const program_node& bias = node.get_dependency(i); - const auto bias_type = layout_optimizer::data_type::bias; - auto reorder = lo.get_reorder(bias.get_output_layout(), bias.id(), bias_type, node, output_layout); + auto new_layout = layout(output_layout.data_type, + format::bfyx, + { 1, static_cast(bias.get_output_layout().count()), 1, 1 }); + if (new_layout.data_type == data_types::bin) { + new_layout.data_type = bias.get_output_layout().data_type; + } + auto reorder = rf.get_reorder(bias.id(), + bias.get_output_layout(), + new_layout); if (reorder.first) p.add_intermediate(reorder.first, node, i, !reorder.second); } } template void pre_optimize_bias::optimize_bias(convolution_node& node, - layout_optimizer& lo, + reorder_factory& rf, program_impl& p); template void pre_optimize_bias::optimize_bias(deconvolution_node& node, - layout_optimizer& lo, + reorder_factory& rf, program_impl& p); template void pre_optimize_bias::optimize_bias(fully_connected_node& node, - layout_optimizer& lo, + reorder_factory& rf, program_impl& p); -template void pre_optimize_bias::optimize_bias(embed_node& node, layout_optimizer& lo, program_impl& p); +template void pre_optimize_bias::optimize_bias(embed_node& node, reorder_factory& rf, program_impl& p); -void pre_optimize_bias::run(program_impl& p, layout_optimizer& lo) { +void pre_optimize_bias::run(program_impl& p, reorder_factory& rf) { for (auto& prim : p.get_processing_order()) { if (prim->type() == convolution::type_id()) { if (!prim->as().weights_quantization_term()) - optimize_bias(prim->as(), lo, p); + optimize_bias(prim->as(), rf, p); } else if (prim->type() == deconvolution::type_id()) { - optimize_bias(prim->as(), lo, p); + optimize_bias(prim->as(), rf, p); } else if (prim->type() == fully_connected::type_id()) { if (!prim->as().weights_quantization_term()) - optimize_bias(prim->as(), lo, p); + optimize_bias(prim->as(), rf, p); } else if (prim->type() == embed::type_id()) { - optimize_bias(prim->as(), lo, p); + optimize_bias(prim->as(), rf, p); } } -} \ No newline at end of file +} diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/prep_opt_depthwise_sep_post.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/prep_opt_depthwise_sep_post.cpp index 876947715de5d0..53f627b74dce06 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/prep_opt_depthwise_sep_post.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/prep_opt_depthwise_sep_post.cpp @@ -50,7 +50,7 @@ void prep_opt_depthwise_sep_post::optimize_depthwise_sep_pre(program_impl& p, T& if (node.get_primitive()->bias.size() != 0) { const auto& bias_layout = node.get_dependency(dependency_offset).get_output_layout(); auto target_layout = - layout(bias_layout.data_type, cldnn::format::bfyx, {1, 1, bias_layout.size.spatial[0] * split, 1}); + layout(bias_layout.data_type, cldnn::format::bfyx, {1, bias_layout.size.feature[0] * split, 1, 1}); program_helpers::merge_buffers(p.get_engine(), node, target_layout, @@ -109,4 +109,4 @@ void prep_opt_depthwise_sep_post::run(program_impl& p) { optimize_depthwise_sep_pre(p, prim->as()); } } -} \ No newline at end of file +} diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_binarization.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_binarization.cpp deleted file mode 100644 index c07ef47b1f3b21..00000000000000 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_binarization.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* -// Copyright (c) 2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include -#include "quantize_inst.h" -#include "binary_convolution_inst.h" -#include "data_inst.h" -#include "pass_manager.h" -#include "program_helpers.h" -#include - -void prepare_binarization::prepare_packed_quantize(program_impl&, program_node& node) { - auto& quantize_node = node.as(); - - if (quantize_node.get_primitive()->levels != 2 || quantize_node.get_users().size() > 1 || - quantize_node.is_output() || !(quantize_node.get_users().front()->is_type())) - return; - - auto& input_low = quantize_node.get_dependency(1).template as(); - auto& input_high = quantize_node.get_dependency(2).template as(); - - auto& mem_input_low = input_low.get_attached_memory(); - auto& mem_input_high = input_high.get_attached_memory(); - - bool is_binarization = true; - switch (mem_input_high.get_layout().data_type) { - case data_types::f32: { - float* data_input_low = static_cast(mem_input_low.lock()); - float* data_input_high = static_cast(mem_input_high.lock()); - - for (size_t i = 0; i < mem_input_high.get_layout().count(); i++) { - if (data_input_high[i] != data_input_low[i]) { - is_binarization = false; - break; - } - } - break; - } - case data_types::f16: { - uint16_t* data_input_low = static_cast(mem_input_low.lock()); - uint16_t* data_input_high = static_cast(mem_input_high.lock()); - - for (size_t i = 0; i < mem_input_high.get_layout().count(); i++) { - if (data_input_high[i] != data_input_low[i]) { - is_binarization = false; - break; - } - } - break; - } - default: - throw std::runtime_error("PrepareBinarization: Unsupported precision of quantize inputs"); - } - - mem_input_low.unlock(); - mem_input_high.unlock(); - - if (!is_binarization) - return; - - quantize_node.set_packed_binary_output(true); -} - -void prepare_binarization::prepare_fusing(program_impl& p, program_node& node) { - auto& binary_conv_node = node.as(); - - program_node* user; - - // TODO: support more than 1 fused node - bool repeat = false; - do { - if (binary_conv_node.get_users().size() > 1 || binary_conv_node.get_users().empty()) - return; - - user = binary_conv_node.get_users().front(); - - // check all primitive types that can be possibly fused - bool fuse_scale = user->is_type(); - bool fuse_quantize = user->is_type() && user->as().get_packed_binary_output() && - binary_conv_node.get_output_layout().size.feature[0] == user->get_dependency(1).get_output_layout().size.feature[0] && - binary_conv_node.get_output_layout().size.feature[0] == user->get_dependency(2).get_output_layout().size.feature[0] && - binary_conv_node.get_primitive()->dilation == tensor{1}; - if (!fuse_scale && !fuse_quantize) - return; - - cldnn::padding needed_padding = - padding::max(user->get_output_layout().data_padding, binary_conv_node.get_output_layout().data_padding); - binary_conv_node.add_fused_primitive(user); - - while (user->get_dependencies().size() > 1) { - auto& dep = user->get_dependency(user->get_dependencies().size() - 1); - p.remove_connection(dep, *user); - } - - p.add_optimized_primitive_info(user->id(), {binary_conv_node.id()}); - - binary_conv_node.merge_output_padding(needed_padding); - binary_conv_node.set_output_layout(user->get_output_layout()); - - p.extract_and_remove(*user); - } while (repeat); -} - -void prepare_binarization::run(program_impl& p) { - for (auto& prim : p.get_processing_order()) { - if (prim->type() == quantize::type_id()) { - prepare_packed_quantize(p, *prim); - } - } - - for (auto& prim : p.get_processing_order()) { - if (prim->type() == binary_convolution::type_id()) { - prepare_fusing(p, *prim); - } - } -} diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_buffer_fusing.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_buffer_fusing.cpp index a14e8b9aeb0c7a..d4f69471de0308 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_buffer_fusing.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_buffer_fusing.cpp @@ -16,9 +16,9 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// -#include "api/CPP/eltwise.hpp" -#include "api/CPP/pooling.hpp" -#include "api/CPP/upsampling.hpp" +#include "api/eltwise.hpp" +#include "api/pooling.hpp" +#include "api/upsampling.hpp" #include "primitive_inst.h" #include "activation_inst.h" #include "concatenation_inst.h" @@ -37,7 +37,6 @@ using namespace cldnn; // ToDo remove friendship relation from program_node - void prepare_buffer_fusing::run(program_impl& p) { bool is_debug = p.get_options().get()->enabled(); /* @@ -49,7 +48,7 @@ void prepare_buffer_fusing::run(program_impl& p) { If crop is before concat there can be padding mismtach, since concat changes padding. */ auto can_optimize = [](const program_node* node) { - if (node->is_output() || (node->get_fused_activation_func() != cldnn_activation_func_t::activation_none)) { + if (node->is_output() || (!node->get_fused_activations_funcs().empty())) { return false; } return true; @@ -272,123 +271,10 @@ void prepare_buffer_fusing::run(program_impl& p) { continue; program_helpers::do_for_types(*node, [&p](reshape_node& node) { node.get_output_layout(); - if (node.is_in_place() && node.get_fused_activation_func() == activation_none) + if (node.is_in_place() && node.get_fused_activations_funcs().empty()) node.can_be_optimized(true); else node.can_be_optimized(false); }); - program_helpers::do_for_types(*node, [&p](reorder_node& node) { - auto& input = node.input(); - - auto output_layout = node.get_output_layout(); - // This is WA for topologies that due to additional reorders added perform worse with conv1x1 optimization - auto remove_bf8_xy_opt = ((input.is_type() || input.is_type()) && - output_layout.format == format::bf8_xy16 && input.get_users().size() == 1); - // Remove reorder from convolution 1x1 to bfyx in some conditions - auto remove_byxf_opt = (input.is_type() && input.get_users().size() == 1 && - input.get_output_layout().format == format::byxf); - - // Work-around to propagate blocked formats to first convolution - fs_byx_fsv32, bfyx_f16 - // Pattern of convolution -> pooling -> reorder - auto blocked_conv_pool_reorder = - input.is_type() && - input.get_dependencies().front()->is_type() && // Input to pooling is convolution - input.get_dependencies().front()->get_users().size() == 1 && // Convolution has only one user (pooling) - input.get_dependencies().front()->get_output_layout().format == - format::bfyx; // Convolution outputs bfyx format - // Pattern of convolution -> reorder - auto blocked_conv_reorder = input.is_type() && input.get_users().size() == 1 && - input.get_output_layout().format == format::bfyx; - auto remove_bfyx_to_blocked = - (output_layout.format == format::fs_b_yx_fsv32 || - // For bfyx_f16 if the size is large enough it is more optimal to sink reorder into convolution - (output_layout.format == format::bfyx_f16 && - output_layout.count() > 500000 - // bfyx -> bfyx_f16 implementation can only handle 3 input features - && input.get_output_layout().size.feature[0] == 3)) && - (blocked_conv_pool_reorder || blocked_conv_reorder); - - // check if all inputs user have the same format - auto all_users_same_format = true; - auto input_user_layout_format = input.get_users().front()->get_output_layout().format; - for (auto const& user : input.get_users()) { - if (user->get_output_layout().format != input_user_layout_format) { - all_users_same_format = false; - break; - } - } - auto same_data_type = input.get_output_layout().data_type == output_layout.data_type; - // Optimization only available in case of layers that support different input and output formats. - // todo: new api needs to be created to read such caps - if (!(input.is_type() && - (output_layout.format == format::bfyx || output_layout.format == format::yxfb || - output_layout.format == format::byxf) && - input.get_output_layout().format != format::fs_b_yx_fsv32 && - input.get_output_layout().format != format::bfyx_f16 && all_users_same_format && same_data_type) && - !remove_bf8_xy_opt && - !(input.is_type() && (input.get_output_layout().format == format::bf8_xy16)) && - !(input.is_type() && - (output_layout.format == format::bfyx || output_layout.format == format::yxfb || - output_layout.format == format::byxf) && - input.get_output_layout().format != format::fs_b_yx_fsv32 && - input.get_output_layout().format != format::bfyx_f16 && all_users_same_format && same_data_type) && - !(remove_byxf_opt && - (node.get_users().front()->is_type() || node.get_users().front()->is_type()) && - output_layout.format != format::fs_b_yx_fsv32) && - !(remove_bfyx_to_blocked)) - return; - - if (remove_bf8_xy_opt) { - auto users_user_layout = node.get_users().front()->get_users().front()->get_output_layout(); - // if users_user_layout is still bf8_yx16 (stacked convolutions) then leave the reorder - if (users_user_layout.format == format::bf8_xy16) - return; - auto input_layout = input.get_output_layout(); - auto target_layout = layout(input_layout.data_type, - users_user_layout.format, - input_layout.size, - input_layout.data_padding); - input.set_output_layout(target_layout, false); - } else if (remove_byxf_opt) { - for (auto user : node.get_users()) { - auto users_users = user->get_users(); - - for (auto const& users_user : users_users) { - if (users_user->get_output_layout().format != format::byxf && !users_user->is_type()) { - remove_byxf_opt = false; - return; - } - } - } - - for (auto user : node.get_users()) { - if (remove_byxf_opt) { - auto input_layout = input.get_output_layout(); - user->set_output_layout(input_layout, false); - } - } - } else if (remove_bfyx_to_blocked) { - auto& conv_node = blocked_conv_reorder ? input : *(input.get_dependencies().front()); - auto original_layout = conv_node.get_output_layout(); - auto output_format = output_layout.format; - // Change convolution output layout since it can handle bfyx -> blocked format change - auto target_layout = layout(original_layout.data_type, - output_format, - original_layout.size, - original_layout.data_padding); - - if (blocked_conv_pool_reorder) { - input.set_output_padding(output_layout.data_padding); - } else { - target_layout.data_padding = output_layout.data_padding; - } - - conv_node.set_output_layout(target_layout); - } else { - input.set_output_layout(output_layout, false); - } - node.can_be_optimized(true); - p.extract_and_remove(node); // try to remove redundant reorders - }); } } diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_depthwise_sep_opt.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_depthwise_sep_opt.cpp index dab8ba8b317b44..9c6ec77a3db7f6 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_depthwise_sep_opt.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_depthwise_sep_opt.cpp @@ -32,7 +32,7 @@ void prepare_depthwise_sep_opt::optimize_depthwise_sep_pre(T& node) { // are not reused in other primitives as they will be overriden with concatenated ones for (size_t i = 1; i < node.get_dependencies().size(); i++) { auto& weights_or_biases = node.get_dependency(i); - if (weights_or_biases.get_users().size() > 1 || weights_or_biases.type() != data::type_id()) + if (weights_or_biases.get_users().size() > 1 || !weights_or_biases.template is_type()) return; } } else { @@ -52,9 +52,9 @@ template void prepare_depthwise_sep_opt::optimize_depthwise_sep_pretype() == convolution::type_id()) { + if (prim->is_type()) { optimize_depthwise_sep_pre(prim->as()); - } else if (prim->type() == deconvolution::type_id()) { + } else if (prim->is_type()) { optimize_depthwise_sep_pre(prim->as()); } } diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_padding.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_padding.cpp index 6f2505e999e174..458611e7a5c180 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_padding.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_padding.cpp @@ -36,6 +36,9 @@ void prepare_padding::run(program_impl& p) { if (!prim->with_output_size) continue; + if (node->get_output_layout().format == format::bfzyx_f16) + continue; + auto filter_size = prim_node.weights(0).get_output_layout().size; auto needed_padding = calc_sliding_window_needed_input_padding(prim_node.input().get_output_layout(), @@ -119,7 +122,8 @@ void prepare_padding::run(program_impl& p) { conv_layout.format != cldnn::format::byxf_af32 && conv_layout.format != cldnn::format::fs_bs_yx_bsv4_fsv32 && conv_layout.format != cldnn::format::b_fs_yx_fsv4 && - conv_layout.format != cldnn::format::fs_b_yx_fsv32) { + conv_layout.format != cldnn::format::fs_b_yx_fsv32 && + conv_layout.format != cldnn::format::b_fs_yx_32fp) { continue; } diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_primitive_fusing.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_primitive_fusing.cpp index d6c359cc5551f1..0e60acd94e1dca 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_primitive_fusing.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_primitive_fusing.cpp @@ -16,13 +16,15 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// -#include "api/CPP/pooling.hpp" -#include "api/CPP/proposal.hpp" -#include "api/CPP/roi_pooling.hpp" +#include "api/pooling.hpp" +#include "api/proposal.hpp" +#include "api/roi_pooling.hpp" #include "program_helpers.h" #include "pass_manager.h" +#include "quantize_inst.h" +#include "binary_convolution_inst.h" #include "activation_inst.h" #include "batch_norm_inst.h" #include "batch_norm_grad_inst.h" @@ -50,154 +52,274 @@ #include #include #include +#include "error_handler.h" -void prepare_primitive_fusing::fuse_skip_layers(program_impl& p, program_node* node) { - program_helpers::do_for_types(*node, [&p](eltwise_node& node) { - if (node.get_primitive()->mode != eltwise_mode::sum || node.inputs_count() != 2) - return; +void prepare_primitive_fusing::run(program_impl& p) { + fuse_reorders(p); + fuse_simple_primitives(p); + fuse_activations(p); + fuse_skip_layers(p); +} - // both inputs should be deconvolutions - if (!(node.input(0).is_type() && node.input(1).is_type())) { - return; - } +void prepare_primitive_fusing::fuse_reorders(program_impl &p) { + // This loop tries fusing several reorders one by one (if present) into one reorder + auto itr = p.get_processing_order() .begin(); + while (itr != p.get_processing_order().end()) { + auto node_itr = itr++; + auto& node = (*node_itr); - auto& to_fuse_with = node.input(0); - int to_fuse_index = 1; + if (node->is_output()) + continue; - // remove dependencies and users of elwtise that is going to be extracted - p.add_connection(node.input(to_fuse_index), to_fuse_with); - p.remove_connection(node.input(to_fuse_index), node); + program_helpers::do_for_types(*node, [&p](reorder_node& node) { + auto& input = node.input(); - p.get_processing_order().erase(&to_fuse_with); - p.get_processing_order().insert(&node, &to_fuse_with); + // Restrictions: + // - inputs cannot be padded + // - primitives input cannot be output + // - input was optimized + if (node.has_padded_dependency() || input.is_output() || + node.get_dependencies().size() != 1 || input.can_be_optimized()) + return; - if (node.get_fused_activation_func() != activation_none) - to_fuse_with.set_fused_activation(node.get_fused_activation_func(), node.get_fused_activation_params()); - to_fuse_with.set_output_padding(node.get_output_layout().data_padding); + // - check if previous node is reorder with 1 user (and if the layouts are the same - remove reorder) + // - do not fuse if current node has mean subtract + if (input.get_users().size() != 1 || !input.is_type() || + input.get_output_layout() != node.get_output_layout() || node.has_mean() || + !node.get_primitive()->subtract_per_feature.empty()) + return; - p.extract_and_remove(node); - }); -} + p.add_optimized_primitive_info(node.id()); -template -static bool node_is_type(program_node* n) { - return n->is_type(); + auto output_layout = node.get_output_layout(); + input.set_output_layout(output_layout, false); + p.extract_and_remove(node); + }); + } } -void prepare_primitive_fusing::fuse_conv_bn_scale(program_impl& p, program_node* node) { - program_helpers::do_for_types(*node, [&p](convolution_node& node) { - if (node.get_users().size() > 2) - return; +void prepare_primitive_fusing::fuse_activations(program_impl &p) { + bool is_debug = p.get_options().get()->enabled(); + auto itr = p.get_processing_order().begin(); + while (itr != p.get_processing_order().end()) { + auto node_itr = itr++; + auto& node = (*node_itr); + + program_helpers::do_for_types(*node, [&p, &is_debug](activation_node& node) { + auto& input = node.input(); - auto found_bn = std::find_if(node.get_users().begin(), node.get_users().end(), node_is_type); - auto bn_node = found_bn != node.get_users().end() ? *found_bn : nullptr; - if (bn_node != nullptr) { - if (bn_node->get_users().size() > 2) + // Restrictions: + // - inputs cannot be padded + // - primitives input cannot be output + // - no activation additional input + // - input was optimized + if (node.has_padded_dependency() || (input.is_output() && !is_debug) || node.is_output() || + node.get_dependencies().size() != 1 || input.can_be_optimized()) return; - auto found_scale = - std::find_if(bn_node->get_users().begin(), bn_node->get_users().end(), node_is_type); - auto sc_node = found_bn != node.get_users().end() ? *found_scale : nullptr; - if (sc_node != nullptr) { - int bn_index = static_cast(std::distance(node.get_users().begin(), found_bn)); - int sc_index = static_cast(std::distance(bn_node->get_users().begin(), found_scale)); - auto scale_prim = std::static_pointer_cast(sc_node->get_primitive()); - auto bn_prim = std::static_pointer_cast(bn_node->get_primitive()); - auto prim = node.get_primitive(); - bool training = false; - - if (node.get_users().size() == 2) { - training = true; - float zero = 0.0f; - layout dummy_layout(data_types::f32, format::bfyx, tensor(1, 1, 1, 1)); - - auto bn_backw = node.get_users().begin(); - std::advance(bn_backw, bn_index == 0 ? 1 : 0); - if (!(*bn_backw)->is_type()) - return; - auto sc_backw = bn_node->get_users().begin(); - std::advance(sc_backw, sc_index == 0 ? 1 : 0); - if (!(*sc_backw)->is_type()) - return; - - auto conv_out_prim = std::make_shared(prim->id + "_fused_conv_out", - memory::attach(dummy_layout, &zero, 1)); - auto& conv_out_node = p.get_or_create(conv_out_prim); - auto conv_out_mem = p.get_engine().allocate_memory(node.get_output_layout(), 0); - conv_out_node.as().attach_memory(*conv_out_mem, false); - p.add_intermediate(conv_out_node, **bn_backw, 1, true); - - auto bn_out_prim = std::make_shared(prim->id + "_fused_bn_out", - memory::attach(dummy_layout, &zero, 1)); - auto& bn_out_node = p.get_or_create(bn_out_prim); - auto bn_out_mem = p.get_engine().allocate_memory(bn_node->get_output_layout(), 0); - bn_out_node.as().attach_memory(*bn_out_mem, false); - p.add_intermediate(bn_out_node, **sc_backw, 0, true); - } + // - limit to primitives which implementations support activation fusing + if (input.get_users().size() != 1 || + // TODO: new api needs to be created to read such caps + // right now use whitelist so no new primitives will be affected in case of lack of fused activation + // support + (!input.is_type() && !input.is_type() && !input.is_type() && + !input.is_type() && !input.is_type() && !input.is_type() && + !input.is_type() && !input.is_type() && !input.is_type() && + !input.is_type() && !input.is_type() && !input.is_type() && + !input.is_type() && !input.is_type() && !input.is_type() && + !input.is_type() && !input.is_type() && !input.is_type() && + !input.is_type() && !input.is_type() && !input.is_type() && + !input.is_type() && !input.is_type() && + !input.is_type() && !input.is_type())) + return; - auto new_conv = std::make_shared(prim->id + "_fused", - prim->input[0], - prim->weights.ref(), - prim->bias.ref(), - bn_prim->epsilon, - scale_prim->input[1], - scale_prim->bias, - prim->stride, - prim->dilation, - prim->input_offset, - bn_prim->inv_variance, - prim->with_activation, - prim->activation_negative_slope, - prim->output_padding); - auto& new_node = p.get_or_create(new_conv); - p.replace(node, new_node); - - while (sc_node->get_dependencies().size() > 1) { - // ToDo: here we modify users and dependencies, - // It should be done through public methods in program_node/program_impl - // to avoid friend declarations - auto& dep = sc_node->get_dependency(sc_node->get_dependencies().size() - 1); - p.remove_connection(dep, *sc_node); - dep.users.push_back(&new_node); - if (sc_node->get_dependencies().size() == 1) - new_node.dependencies.insert(new_node.dependencies.begin() + 1, &dep); - else - new_node.dependencies.push_back(&dep); - } - p.extract_and_remove(*sc_node); - while (bn_node->get_dependencies().size() > 1) { - auto& dep = bn_node->get_dependency(bn_node->get_dependencies().size() - 1); - p.remove_connection(dep, *bn_node); - new_node.dependencies.push_back(&dep); + if (input.get_fused_primitives().empty()) { + input.add_fused_activation(node.get_primitive()->activation_function, node.get_primitive()->additional_params); + for (size_t i = 0; i < node.get_fused_activations_funcs().size(); i++) { + input.add_fused_activation(node.get_fused_activations_funcs()[i], + node.get_fused_activations_params()[i]); } - p.extract_and_remove(*bn_node); - auto inv_var_node = - std::find_if(new_node.dependencies.begin(), - new_node.dependencies.end(), - [&new_conv](const program_node* node) { - return node->id().find(new_conv->inv_variance) != std::string::npos; - }); - (*inv_var_node)->users.push_back(&new_node); - - if (training) { - auto user = std::find_if(new_node.get_users().begin(), - new_node.get_users().end(), - [](const program_node* node) { - return node->id().find("_fused_conv_out") != std::string::npos; - }); - p.reverse_connection(new_node, **user); - user = std::find_if( - new_node.get_users().begin(), - new_node.get_users().end(), - [](const program_node* node) { return node->id().find("_fused_bn_out") != std::string::npos; }); - p.reverse_connection(new_node, **user); - p.get_processing_order() - .calculate_BFS_processing_order(); // this should be avoided, why do we need recalculation of - // processing order? + input.set_output_padding(node.get_output_layout().data_padding); + } else { + // If node already has any fused node using new mechanism, + // we can just use the same way and handle any amount of activations + p.fuse_nodes(input, node); + } + + p.add_optimized_primitive_info(node.id(), {input.id()}); + + p.extract_and_remove(node); + }); + } +} + +void prepare_primitive_fusing::fuse_skip_layers(program_impl& p) { + // This loop tries fusing eltwise (sum) with deconvolution + auto itr = p.get_processing_order().begin(); + while (itr != p.get_processing_order().end()) { + auto node_itr = itr++; + auto& node = (*node_itr); + + program_helpers::do_for_types(*node, [&p](eltwise_node& node) { + if (node.get_primitive()->mode != eltwise_mode::sum || node.inputs_count() != 2) + return; + + // both inputs should be deconvolutions + if (!(node.input(0).is_type() && node.input(1).is_type())) { + return; + } + + auto& to_fuse_with = node.input(0); + int to_fuse_index = 1; + + // remove dependencies and users of elwtise that is going to be extracted + p.add_connection(node.input(to_fuse_index), to_fuse_with); + p.remove_connection(node.input(to_fuse_index), node); + + p.get_processing_order().erase(&to_fuse_with); + p.get_processing_order().insert(&node, &to_fuse_with); + + if (!node.get_fused_activations_funcs().empty()) { + for (size_t i = 0; i < node.get_fused_activations_funcs().size(); i++) { + to_fuse_with.add_fused_activation(node.get_fused_activations_funcs()[i], + node.get_fused_activations_params()[i]); } } - } - }); + to_fuse_with.set_output_padding(node.get_output_layout().data_padding); + + p.extract_and_remove(node); + }); + } +} + +void prepare_primitive_fusing::fuse_simple_primitives(program_impl &p) { + bool recalc_processing_order = false; + + auto itr = p.get_processing_order().begin(); + while (itr != p.get_processing_order().end()) { + auto node_itr = itr++; + auto& node = (*node_itr); + + auto fuse_activation_f = [&p](activation_node& activation_node) { + auto& input_data = activation_node.get_dependency(0); + if (activation_node.get_dependencies().size() >= 3 || + (!(input_data.is_type() && input_data.get_output_layout().format == format::bfyx_f16) && + !input_data.is_type())) + return; + + p.fuse_nodes(input_data, activation_node); + }; + + auto fuse_scale_f = [&p](scale_node& scale_node) { + if (scale_node.get_dependencies().empty()) + CLDNN_ERROR_MESSAGE(scale_node.id(), "scale has invalid count of dependencies"); + + auto& input_data = scale_node.get_dependency(0); + bool fuse_to_binary_conv = input_data.is_type() && + input_data.as().get_primitive()->dilation == tensor{1}; + bool fuse_to_conv = input_data.is_type() && input_data.get_output_layout().format == format::bfyx_f16; + + bool should_fuse = fuse_to_binary_conv || fuse_to_conv; + if (!should_fuse) + return; + + p.fuse_nodes(input_data, scale_node); + }; + + auto fuse_quantize_f = [&p](quantize_node& quantize_node) { + auto& input_data = quantize_node.get_dependency(0); + auto& input_lo = quantize_node.get_dependency(1); + auto& input_hi = quantize_node.get_dependency(2); + + auto out_layout = quantize_node.get_output_layout(); + auto in_layout = input_data.get_output_layout(); + + bool fuse_to_binary_conv = input_data.is_type() && + ((out_layout.data_type == data_types::bin && + quantize_node.get_dependencies().size() == 5 && + ((in_layout.size.feature[0] == input_lo.get_output_layout().size.feature[0] && + in_layout.size.feature[0] == input_hi.get_output_layout().size.feature[0]) || + (input_lo.get_output_layout().size.feature[0] == 1 && + input_hi.get_output_layout().size.feature[0] == 1)))) && + input_data.as().get_primitive()->dilation.spatial[0] == 1 && + input_data.as().get_primitive()->dilation.spatial[1] == 1; + + bool should_fuse = fuse_to_binary_conv; + + if (!should_fuse) + return; + + p.fuse_nodes(input_data, quantize_node); + }; + + auto fuse_eltwise_f = [&p, &recalc_processing_order, this](eltwise_node& node) { + std::shared_ptr prim = node.get_primitive(); + if (node.is_output() || node.inputs_count() != 2 || + prim->mode != eltwise_mode::sum || !prim->stride.empty()) + return; + + std::vector parents = node.get_dependencies(); + std::list users = node.get_users(); + + auto parent1 = parents[0]; + auto parent2 = parents[1]; + + // We should have at least one convolution node + if (!parent1->is_type() && !parent2->is_type()) + return; + + // Choose a convolution node + size_t fused_idx = parent1->is_type() ? 0 : 1; + size_t peer_idx = parent1->is_type() ? 1 : 0; + + int p1_pnum = p.get_processing_order().get_processing_number(parents[fused_idx]); + int p2_pnum = p.get_processing_order().get_processing_number(parents[peer_idx]); + + if (p1_pnum < p2_pnum && parents[peer_idx]->is_type()) { + std::swap(fused_idx, peer_idx); + } + + if (parent1->is_type() && !_lo.is_format_optimized(parent1->as(), format::bfyx_f16)) + return; + if (parent2->is_type() && !_lo.is_format_optimized(parent2->as(), format::bfyx_f16)) + return; + + auto fused_node = parents[fused_idx]; + auto peer_node = parents[peer_idx]; + + // This fusing can be extended to support peer node in any layout and with broadcast + // Fusing is allowed only if current layouts are bfyx_f16 (in tests) or bfyx_f16_network attribute is true + bool merge_allowed = fused_node->get_users().size() == 1 && + (_lo.get_optimization_attributes().bfyx_f16_network || + (fused_node->get_output_layout().format == format::bfyx_f16 && + peer_node->get_output_layout().format == format::bfyx_f16)) && + fused_node->get_output_layout().size == peer_node->get_output_layout().size; + + for (auto& parent : fused_node->get_dependencies()) + if (parent->id() == peer_node->id()) + merge_allowed = false; + + if (!merge_allowed) + return; + + if (p.get_processing_order().get_processing_number(fused_node) < + p.get_processing_order().get_processing_number(peer_node)) + recalc_processing_order = true; + + p.fuse_nodes(*fused_node, node); + }; + + program_helpers::do_for_types(*node, + fuse_activation_f, + fuse_scale_f, + fuse_quantize_f, + fuse_eltwise_f); + } + + // Need to update processing order to handle cases when peer node processing number is greater + // than fused node one + if (recalc_processing_order) + p.get_processing_order().calc_processing_order(p); } void prepare_conv_eltw_fusing::fuse_conv_eltwise(program_impl& p, program_node* node) { @@ -243,9 +365,10 @@ void prepare_conv_eltw_fusing::fuse_conv_eltwise(program_impl& p, program_node* } // TODO Allow to pass arbitrary convolution activation in constructor - if (conv_node->get_fused_activation_func() != cldnn_activation_func::activation_none && - conv_node->get_fused_activation_func() != cldnn_activation_func::activation_relu && - conv_node->get_fused_activation_func() != cldnn_activation_func::activation_relu_negative_slope) + if (!conv_node->get_fused_activations_funcs().empty() && + !(conv_node->get_fused_activations_funcs().size() == 1 && (conv_node->get_fused_activations_funcs()[0] == activation_func::relu || + conv_node->get_fused_activations_funcs()[0] == activation_func::relu_negative_slope || + conv_node->get_fused_activations_funcs()[0] == activation_func::none))) return; // make sure eltwise have only 2 inputs @@ -298,26 +421,21 @@ void prepare_conv_eltw_fusing::fuse_conv_eltwise(program_impl& p, program_node* if (eltw_node->inputs_calibration_term()) return; - // TODO Allow to pass arbitrary convolution activation in constructor - if (conv_node->get_fused_activation_func() != cldnn_activation_func::activation_none) { - conv.with_activation = true; - - if (conv_node->get_fused_activation_func() == cldnn_activation_func::activation_relu_negative_slope) { - conv.activation_negative_slope = conv_node->get_fused_activation_params().a; - } - } auto conv_id = conv_node->id(); auto eltw_id = eltw_node->id(); + bool conv_with_activation = !conv_node->get_fused_activations_funcs().empty(); + auto conv_netagive_slope = conv_with_activation ? conv_node->get_fused_activations_params().begin()->a : 0.0f; + auto fused_conv_eltw = std::make_shared(conv_id + "_fused_" + eltw_id, conv_node->input().id(), eltw_node->input(eltw_second_input_idx).id(), eltw.mode, - conv.weights.ref(), - conv.bias.ref(), - conv.weights_quantization_factors.ref(), - conv.output_calibration_factors.ref(), + conv.weights, + conv.bias, + conv.weights_quantization_factors, + conv.output_calibration_factors, conv.input_quantization_factor, eltw_scale, // eltw_scale eltw.output_calibration_factors, @@ -325,8 +443,8 @@ void prepare_conv_eltw_fusing::fuse_conv_eltwise(program_impl& p, program_node* new_conv_stride, conv.input_offset, conv.dilation, - conv.with_activation, - conv.activation_negative_slope, + conv_with_activation, + conv_netagive_slope, false, // eltw.with_activation - use fused activation 0.f); // eltw.activation_negative_slope - use fused activation @@ -335,7 +453,9 @@ void prepare_conv_eltw_fusing::fuse_conv_eltwise(program_impl& p, program_node* auto& new_node = p.get_or_create(fused_conv_eltw); - new_node.set_fused_activation(eltw_node->get_fused_activation_func(), eltw_node->get_fused_activation_params()); + for (size_t i = 0; i < eltw_node->get_fused_activations_funcs().size(); i++) + new_node.add_fused_activation(eltw_node->get_fused_activations_funcs()[i], + eltw_node->get_fused_activations_params()[i]); // Copy output calibration factors pointer as replace will remove eltwise node program_node* output_calibration_factors = nullptr; @@ -378,212 +498,26 @@ void prepare_conv_eltw_fusing::fuse_conv_eltwise(program_impl& p, program_node* p.add_optimized_primitive_info(eltw_id, {new_node.id()}); } -void prepare_primitive_fusing::run(program_impl& p) { - bool is_debug = p.get_options().get()->enabled(); - - std::list conv_nodes; - auto itr = p.get_processing_order() - .begin(); // note we need to use iterators since currently processed element can be removed - while (itr != p.get_processing_order().end()) { - auto node_itr = itr++; - if ((*node_itr)->is_type()) { - // Don't push nodes that will be executed in bfyx_f16 layout since - // these ones supports eltwise fusing inside common convolution kernel - if (!_lo.is_format_optimized((*node_itr)->as(), format::bfyx_f16)) - conv_nodes.push_back(*node_itr); - } - } - - // Disabled due to kernel being not optimized - // itr = conv_nodes.begin(); - // while (itr != conv_nodes.end()) - //{ - // auto node_itr = itr++; - // auto& node = (*node_itr); - - // fuse_conv_bn_scale(p, node); - //} - - // This loop tries fusing several reorders one by one (if present) into one reorder - itr = p.get_processing_order().begin(); - while (itr != p.get_processing_order().end()) { - auto node_itr = itr++; - auto& node = (*node_itr); - - if (node->is_output()) - continue; - - program_helpers::do_for_types(*node, [&p, is_debug](reorder_node& node) { - auto& input = node.input(); - - // Restrictions: - // - inputs cannot be padded - // - primitives input cannot be output - // - input was optimized - if (node.has_padded_dependency() || (input.is_output() && !is_debug) || - node.get_dependencies().size() != 1 || input.can_be_optimized()) - return; - - // - check if previous node is reorder with 1 user (and if the layouts are the same - remove reorder) - // - do not fuse if current node has mean subtract - if (input.get_users().size() != 1 || !input.is_type() || - input.get_output_layout() != node.get_output_layout() || node.has_mean() || - !node.get_primitive()->subtract_per_feature.empty()) - return; - - p.add_optimized_primitive_info(node.id()); - - input.set_output_layout(node.get_output_layout(), false); - p.extract_and_remove(node); - }); - } - - itr = p.get_processing_order().begin(); - while (itr != p.get_processing_order().end()) { - auto node_itr = itr++; - auto& node = (*node_itr); - - program_helpers::do_for_types(*node, [&p, is_debug](activation_node& node) { - auto& input = node.input(); - - // Restrictions: - // - inputs cannot be padded - // - primitives input cannot be output - // - no activation additional input - // - input was optimized - if (node.has_padded_dependency() || (input.is_output() && !is_debug) || node.is_output() || - node.get_dependencies().size() != 1 || input.can_be_optimized()) - return; - - // - check if there is no activation fused already - // - limit to primitives which implementations support activation fusing - if (input.get_users().size() != 1 || input.get_fused_activation_func() != activation_none || - // TODO: new api needs to be created to read such caps - // right now use whitelist so no new primitives will be affected in case of lack of fused activation - // support - (!input.is_type() && !input.is_type() && !input.is_type() && - !input.is_type() && !input.is_type() && !input.is_type() && - !input.is_type() && !input.is_type() && !input.is_type() && - !input.is_type() && !input.is_type() && !input.is_type() && - !input.is_type() && !input.is_type() && !input.is_type() && - !input.is_type() && !input.is_type() && !input.is_type() && - !input.is_type() && !input.is_type() && !input.is_type() && - !input.is_type() && !input.is_type() && - !input.is_type())) - return; - - input.set_fused_activation(node.get_primitive()->activation_func, node.get_primitive()->additional_params); - input.set_output_padding(node.get_output_layout().data_padding); - - p.add_optimized_primitive_info(node.id(), {input.id()}); - - p.extract_and_remove(node); - }); - } - - // This loop tries fusing eltwise (sum) with deconvolution - itr = p.get_processing_order().begin(); - while (itr != p.get_processing_order().end()) { - auto node_itr = itr++; - auto& node = (*node_itr); - - fuse_skip_layers(p, node); - } -} - void prepare_conv_eltw_fusing::run(program_impl& p) { - bool recalc_processing_order = false; - auto itr = p.get_processing_order().begin(); - - if (bfyx_f16_opt) { - while (itr != p.get_processing_order().end()) { - auto node_itr = itr++; - auto& node = (*node_itr); - program_helpers::do_for_types(*node, [&p, this, &recalc_processing_order](eltwise_node& node) { - std::shared_ptr prim = node.get_primitive(); - if (node.is_output() || node.inputs_count() != 2 || - prim->mode != eltwise_mode::sum || !prim->stride.empty()) - return; - - std::vector parents = node.get_dependencies(); - std::list users = node.get_users(); - - auto parent1 = parents[0]; - auto parent2 = parents[1]; - - // We should have at least one convolution node - if (!parent1->is_type() && !parent2->is_type()) - return; - - // Choose a convolution node - size_t fused_idx = parent1->is_type() ? 0 : 1; - size_t peer_idx = parent1->is_type() ? 1 : 0; - - int p1_pnum = p.get_processing_order().get_processing_number(parents[fused_idx]); - int p2_pnum = p.get_processing_order().get_processing_number(parents[peer_idx]); - - if (p1_pnum < p2_pnum && parents[peer_idx]->is_type()) { - std::swap(fused_idx, peer_idx); - } - - if (parent1->is_type() && !_lo.is_format_optimized(*parent1, format::bfyx_f16)) - return; - if (parent2->is_type() && !_lo.is_format_optimized(*parent2, format::bfyx_f16)) - return; - - auto fused_node = parents[fused_idx]; - auto peer_node = parents[peer_idx]; - - // This fusing can be extended to support peer node in any layout and with broadcast - bool merge_allowed = fused_node->get_users().size() == 1 && - fused_node->get_output_layout().format == format::bfyx_f16 && - peer_node->get_output_layout().format == format::bfyx_f16 && - fused_node->get_output_layout().size == peer_node->get_output_layout().size; - - for (auto& parent : fused_node->get_dependencies()) - if (parent->id() == peer_node->id()) - merge_allowed = false; - - if (!merge_allowed) - return; - - if (p.get_processing_order().get_processing_number(fused_node) < - p.get_processing_order().get_processing_number(peer_node)) - recalc_processing_order = true; - - fused_node->set_output_padding(node.get_output_layout().data_padding); - - p.add_optimized_primitive_info(node.id(), { fused_node->id() }); - fused_node->as().add_fused_primitive(&node); - - fused_node->users.remove(&node); - peer_node->users.remove(&node); - p.replace_all_usages(node, *fused_node); - node.dependencies.clear(); - p.remove_if_dangling(node); - }); - } - // Need to update processing order to handle cases when peer node processing number is greater - // than fused node one - if (recalc_processing_order) - p.get_processing_order().calc_processing_order(p); - } - - std::list conv_nodes; // note we need to use iterators since currently processed element can be removed - itr = p.get_processing_order().begin(); + auto itr = p.get_processing_order().begin(); while (itr != p.get_processing_order().end()) { auto node_itr = itr++; - if ((*node_itr)->is_type()) + if (node_itr != p.get_processing_order().end() && + (*node_itr)->is_type()) if (!bfyx_f16_opt || !_lo.is_format_optimized((*node_itr)->as(), format::bfyx_f16)) conv_nodes.push_back(*node_itr); } // fuse conv + eltwise after activations - itr = conv_nodes.begin(); - while (itr != conv_nodes.end()) { - auto node_itr = itr++; + auto conv_itr = conv_nodes.begin(); + while (conv_itr != conv_nodes.end()) { + auto node_itr = conv_itr++; + + if (node_itr == conv_nodes.end()) + break; + auto& node = (*node_itr); fuse_conv_eltwise(p, node); @@ -598,6 +532,13 @@ void prepare_conv_eltw_read_write_opt::conv_eltwise_read_write_opt(program_impl& return; } + // look for conflicts + auto this_node_processing_number = p.get_processing_order().get_processing_number(node); + for (auto& user : second_input_node->users) { + if (p.get_processing_order().get_processing_number(user) > this_node_processing_number) + return; + } + // buffer shared between primitives, if second input is mutable data, then we can reuse this memory auto shared_buffer_mem = second_input_node->is_type() ? second_input_node->as().get_attached_memory_ptr() @@ -656,7 +597,8 @@ void prepare_conv_eltw_read_write_opt::run(program_impl& p) { .begin(); // note we need to use iterators since currently processed element can be removed while (itr != p.get_processing_order().end()) { auto node_itr = itr++; - if ((*node_itr)->is_type()) + if (node_itr != p.get_processing_order().end() && + (*node_itr)->is_type()) fused_conv_eltw_nodes.push_back(*node_itr); } diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_quantization.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_quantization.cpp new file mode 100644 index 00000000000000..b2518af69bcc39 --- /dev/null +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/prepare_quantization.cpp @@ -0,0 +1,98 @@ +/* +// Copyright (c) 2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "api/quantize.hpp" +#include "api/binary_convolution.hpp" +#include "api/scale.hpp" +#include "api/pooling.hpp" + +#include "quantize_inst.h" +#include "binary_convolution_inst.h" +#include "data_inst.h" +#include "pass_manager.h" +#include "program_helpers.h" +#include +#include "to_string_utils.h" +#include "error_handler.h" + + +void prepare_quantization::prepare_packed_quantize(program_impl& p) { + auto itr = p.get_processing_order().begin(); + while (itr != p.get_processing_order().end()) { + auto node_itr = itr++; + auto &node = (*node_itr); + + program_helpers::do_for_types(*node, [&](quantize_node& quantize_node) { + if (quantize_node.is_output()) + return; + + auto &input_low = quantize_node.get_dependency(1).template as(); + auto &input_high = quantize_node.get_dependency(2).template as(); + + auto &mem_input_low = input_low.get_attached_memory(); + auto &mem_input_high = input_high.get_attached_memory(); + + auto output_dt = quantize_node.get_output_layout().data_type; + + if (quantize_node.get_primitive()->levels == 2) { + bool is_binarization = true; + switch (mem_input_high.get_layout().data_type) { + case data_types::f32: { + auto data_input_low = static_cast(mem_input_low.lock()); + auto data_input_high = static_cast(mem_input_high.lock()); + + for (size_t i = 0; i < mem_input_high.get_layout().count(); i++) { + if (data_input_high[i] != data_input_low[i]) { + is_binarization = false; + break; + } + } + break; + } + case data_types::f16: { + auto data_input_low = static_cast(mem_input_low.lock()); + auto data_input_high = static_cast(mem_input_high.lock()); + + for (size_t i = 0; i < mem_input_high.get_layout().count(); i++) { + if (data_input_high[i] != data_input_low[i]) { + is_binarization = false; + break; + } + } + break; + } + default: + CLDNN_ERROR_MESSAGE(node->id(), "prepare_quantization: Unsupported precision of quantize inputs"); + } + mem_input_low.unlock(); + mem_input_high.unlock(); + + if (is_binarization) { + output_dt = data_types::bin; + } + } + + quantize_node.typed_desc()->output_data_type = optional_data_type{output_dt}; + quantize_node.recalc_output_layout(); + }); + } +} + +void prepare_quantization::run(program_impl& p) { + prepare_packed_quantize(p); +} diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/propagate_constants.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/propagate_constants.cpp index 34eb7797ab201b..5c0812fa28a05d 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/propagate_constants.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/propagate_constants.cpp @@ -79,9 +79,9 @@ void propagate_constants::run(program_impl& p) { // with recomputed cldnn::data for (auto& cout : to_replace) { auto& id_to_replace = cout.first; + auto mem_impl = cout.second; - // TODO: do not use API primitives internally and get rid of this last 'cldnn::memory' internal usage - memory api_memory = details::memory_c_to_cpp_converter::convert(api_cast(cout.second.get())); + memory api_memory = memory(mem_impl.detach()); // c-cpp converter does not retain since normally it is done inside API-impl layer (cldnn.cpp) so we need to do // it manually cout.second->add_ref(); diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/remove_redundant_reorders.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/remove_redundant_reorders.cpp index 07782bc70c7dd3..01f2caa4fffefe 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/remove_redundant_reorders.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/remove_redundant_reorders.cpp @@ -18,83 +18,168 @@ #include "pass_manager.h" #include "program_helpers.h" +#include "binary_convolution_inst.h" #include #include +#include using namespace cldnn; -remove_redundant_reorders::remove_redundant_reorders(bool bfyx_to_bfyx_f16_opt) - : base_pass("remove_redundant_reorders"), bfyx_to_bfyx_f16_opt(bfyx_to_bfyx_f16_opt) {} +remove_redundant_reorders::remove_redundant_reorders(layout_optimizer& lo_ref, bool enable_reorder_fusing, bool update_implementations) + : base_pass("remove_redundant_reorders"), lo(lo_ref), enable_reorder_fusing(enable_reorder_fusing), update_implementations(update_implementations) {} void remove_redundant_reorders::run(program_impl& p) { - auto itr = p.get_processing_order() - .begin(); // note we need to use iterators since currently processed element can be removed + auto update_implementation = [&](program_node& node) { + if (!update_implementations) + return; + + auto& eng = p.get_engine(); + auto new_impl = node.type()->choose_impl(eng, node); + node.set_selected_impl(std::move(new_impl)); + }; + + // Fuse reorders into primitives + auto itr = p.get_processing_order().begin(); + if (enable_reorder_fusing) { + while (itr != p.get_processing_order().end()) { + auto node_ptr = *itr++; + if (!node_ptr->is_type()) // only care for reorders + continue; + + auto& node = node_ptr->as(); + + auto& input = node.input(); + auto output_layout = node.get_output_layout(); + + if (node.is_output()) + continue; + + if (node.has_mean() || !node.get_primitive()->subtract_per_feature.empty()) + continue; + + if (!node.get_fused_activations_funcs().empty()) + continue; + + auto same_data_type = input.get_output_layout().data_type == output_layout.data_type; + if (!same_data_type) + continue; + + bool all_users_fuse = true; + std::vector recalc_list; + + for (auto usr : node.get_users()) { + if (!lo.can_fuse_reorder(input, *usr, input.get_output_layout().format, usr->get_output_layout().format)) { + all_users_fuse = false; + break; + } + + if (usr->is_type()) + recalc_list.push_back(usr); + } + + if (!all_users_fuse) + continue; + + auto output_padded = static_cast(output_layout.data_padding); + auto can_omit_padding = output_layout.format == format::bfyx_f16 && input.get_output_layout().format == format::bfyx; + + if (output_padded && !can_omit_padding) { + if (input.get_users().size() != 1) + continue; + + if (input.is_type()) + continue; + + input.merge_output_padding(output_layout.data_padding); + } + + node.can_be_optimized(true); + p.extract_and_remove(node); + + for (auto rl : recalc_list) { + rl->recalc_output_layout(true); + } + } + } + + // Shrink reorder chains + itr = p.get_processing_order().begin(); while (itr != p.get_processing_order().end()) { - auto& node = (*itr++); // post-inc to avoid invalidation due to possible erase + auto node = *itr++; if (!node->is_type()) // only care for reorders continue; - program_node* current_node = node; - std::vector r_nodes_to_remove; - - auto optimize = true; - while (current_node) { - auto& r_node = current_node->as(); - current_node = nullptr; - - if (r_node.has_mean() || - !r_node.get_primitive()->subtract_per_feature.empty() || // do not optimize if mean of subtract are present - r_node.is_output() || // do not optimize when both reorder and layer before are outputs - r_node.get_fused_activation_func() != activation_none) { - // TODO Verify whether optimization can be performed at current sub-chain of reorders - optimize = false; - break; - } + auto& r_node = node->as(); + auto& dep_node = r_node.get_dependency(0); + + if (!dep_node.is_type()) + continue; + + auto& r_dep_node = dep_node.as(); + + bool remove_dep = r_dep_node.get_users().size() == 1 && + !r_dep_node.has_mean() && + r_dep_node.get_primitive()->subtract_per_feature.empty() && + !r_dep_node.is_output() && + r_dep_node.get_fused_activations_funcs().empty(); - r_nodes_to_remove.push_back(&r_node); + bool remove_current = + r_dep_node.get_users().size() == 1 && + !r_dep_node.is_output() && + !r_node.has_mean() && + r_node.get_primitive()->subtract_per_feature.empty() && + r_node.get_fused_activations_funcs().empty(); - if (r_node.get_dependency(0).is_type() && r_node.get_dependencies().size() == 1 && - r_node.get_users().size() == 1 && r_node.get_dependency(0).get_users().size() == 1) - current_node = &r_node.get_dependency(0); + if (remove_dep) { + r_dep_node.can_be_optimized(true); + p.add_optimized_primitive_info(r_dep_node.id()); + p.extract_and_remove(r_dep_node); + update_implementation(r_node); + } else if (remove_current) { + auto output_layout = r_node.get_output_layout(); + auto dep_prim = std::const_pointer_cast(r_dep_node.get_primitive()); + dep_prim->output_format = output_layout.format; + dep_prim->output_data_type = output_layout.data_type; + + r_node.can_be_optimized(true); + p.add_optimized_primitive_info(r_node.id()); + p.extract_and_remove(r_node); + + r_dep_node.recalc_output_layout(false); + update_implementation(r_dep_node); } - if (!optimize) + } + + // Optimize reorders not changing memory layout + itr = p.get_processing_order().begin(); + while (itr != p.get_processing_order().end()) { + auto node = *itr++; + if (!node->is_type()) // only care for reorders continue; - assert(node->get_dependencies().size() == 1 && - "reorder without mean should have exactly one dependecy (input)"); - auto& r_output = r_nodes_to_remove.front(); - auto& r_input = r_nodes_to_remove.back()->get_dependency(0); - auto o_layout = r_output->get_output_layout(); - auto i_layout = r_input.get_output_layout(); + auto& r_node = node->as(); - auto ident = program_helpers::are_layouts_identical(o_layout, i_layout); - if (!ident.second) + if (r_node.has_mean() || + !r_node.get_primitive()->subtract_per_feature.empty() || + r_node.is_output() || + !r_node.get_fused_activations_funcs().empty()) continue; - for (auto remove_reorder_node : r_nodes_to_remove) { - auto& r_node = remove_reorder_node->as(); + auto o_layout = r_node.get_output_layout(); + auto i_layout = r_node.get_dependency(0).get_output_layout(); - if (ident.first && ident.second && r_node.is_output() && - r_node.get_dependency(0).is_input()) { // do not optimize when reorder is output and layer before is input - optimize = false; - break; - } - } - if (!optimize) + auto ident = program_helpers::are_layouts_identical(o_layout, i_layout); + + if (!ident.second) continue; - auto rem_itr = r_nodes_to_remove.begin(); - while (rem_itr != r_nodes_to_remove.end()) { - auto remove_reorder_node = *rem_itr++; - auto& r_node = remove_reorder_node->as(); - // mark as optimized - r_node.can_be_optimized(true); - r_node.requires_reinterpret(!ident.first); - if (ident.first) { // no need of reshape - p.add_optimized_primitive_info(r_node.get_primitive()->id); - p.extract_and_remove( - r_node); // try to remove if possible (with respect to r_node not being marked as output) - } + // mark as optimized + r_node.can_be_optimized(true); + r_node.requires_reinterpret(!ident.first); + if (ident.first) { // no need of reshape + p.add_optimized_primitive_info(r_node.get_primitive()->id); + p.extract_and_remove( + r_node); // try to remove if possible (with respect to r_node not being marked as output) } } @@ -116,7 +201,7 @@ void remove_redundant_reorders::run(program_impl& p) { if (user->is_type() && user != node && !user->is_output() && - user->get_fused_activation_func() == cldnn_activation_func_t::activation_none) { + user->get_fused_activations_funcs().empty()) { auto l1 = node->get_output_layout(); auto l2 = user->get_output_layout(); @@ -129,6 +214,9 @@ void remove_redundant_reorders::run(program_impl& p) { if (r_nodes_to_remove.empty()) continue; + if (itr == p.get_processing_order().end()) + break; + auto rem_itr = r_nodes_to_remove.begin(); while (rem_itr != r_nodes_to_remove.end()) { auto remove_reorder_node = *rem_itr++; @@ -144,29 +232,77 @@ void remove_redundant_reorders::run(program_impl& p) { } } - if (bfyx_to_bfyx_f16_opt) { - // Removes reorder bfyx->bfyx_f16 when ic=3 and oc>=16 in order to enable specific kernel - // Needs to be done after passes that can change layouts (like prepare_padding) - itr = p.get_processing_order().begin(); - while (itr != p.get_processing_order().end()) { - auto &node = *itr++; - if (!node->is_type()) - continue; - - if (node->get_dependencies().size() != 1 || node->get_users().size() != 1) - continue; + // This pass removed reorder if previous node can store directly to required layout + itr = p.get_processing_order().begin(); + while (itr != p.get_processing_order().end()) { + auto& node = *itr++; + if (!node->is_type() || !node->is_in_data_flow() || node->get_dependencies().size() != 1) + continue; - auto &user = node->get_users().front(); - auto &dep = node->get_dependency(0); + auto& dep = node->get_dependency(0); + if (!dep.is_type() || node->get_output_layout().format != format::bfyx_f16) + continue; - if (user->is_type() && - node->get_fused_activation_func() == cldnn_activation_func_t::activation_none && - dep.get_output_layout().format == format::bfyx && - dep.get_output_layout().size.feature[0] == 3 && - node->get_output_layout().format == format::bfyx_f16 && - user->get_output_layout().size.feature[0] >= 16) { - p.extract_and_remove(*node); - } + auto output_layout = node->get_output_layout(); + dep.set_output_layout(output_layout, false); + if (dep.type()->does_possible_implementation_exist(p.get_engine(), dep)) { + p.replace_all_usages(*node, dep); + p.get_processing_order().erase(node); + p.add_optimized_primitive_info(node->id()); + p.remove_all_connections(*node); + p.remove_if_dangling(*node); } } + + // This pass removed reorder if the next node supports reorder's input format + itr = p.get_processing_order().begin(); + while (itr != p.get_processing_order().end()) { + auto& node = *itr++; + if (!node->is_type() || !node->is_in_data_flow() || node->get_users().size() != 1 || node->get_dependencies().size() != 1) + continue; + + auto& usr = node->get_users().front(); + auto& dep = node->get_dependency(0); + if (!usr->is_type() || node->get_output_layout().format != format::bfyx || + dep.get_output_layout().format != format::bfyx_f16) + continue; + + dep.merge_output_padding(node->get_output_layout().data_padding); + p.replace_all_usages(*node, dep); + p.get_processing_order().erase(node); + p.add_optimized_primitive_info(node->id()); + p.remove_all_connections(*node); + p.remove_if_dangling(*node); + } + + // Remove u8 -> fp conversion in reorder if the next layer is scale + // Scale node loads u8, converts it to fp type and performs scaling and shifting + // FIXME: scale layer sometimes works incorrectly for u8 input. Need to fix it and this pass can be enabled again. +// itr = p.get_processing_order().begin(); +// while (itr != p.get_processing_order().end()) { +// auto& node = *itr++; +// if (!node->is_type() || !node->is_in_data_flow()) +// continue; +// +// if (node->get_users().size() != 1 || node->get_dependencies().size() != 1) +// continue; +// +// auto& usr = node->get_users().front(); +// auto& dep = node->get_dependency(0); +// if (!usr->is_type() || +// !dep.is_input() || +// dep.get_output_layout().data_type != data_types::u8 || +// (node->get_output_layout().data_type != data_types::f32 && node->get_output_layout().data_type != data_types::f16) || +// dep.get_output_layout().format != node->get_output_layout().format || +// dep.get_output_layout().size != node->get_output_layout().size) +// continue; +// +// usr->merge_output_padding(node->get_output_layout().data_padding); +// +// p.replace_all_usages(*node, dep); +// p.get_processing_order().erase(node); +// p.add_optimized_primitive_info(node->id()); +// p.remove_all_connections(*node); +// p.remove_if_dangling(*node); +// } } diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/reorder_inputs.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/reorder_inputs.cpp index 5bb34bd2704535..b5d08dcdeae137 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/reorder_inputs.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/reorder_inputs.cpp @@ -16,19 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// -#include "api/CPP/proposal.hpp" -#include "api/CPP/roi_pooling.hpp" -#include "api/CPP/reorg_yolo.hpp" -#include "api/CPP/eltwise.hpp" -#include -#include -#include "api/CPP/softmax.hpp" -#include "api/CPP/permute.hpp" -#include "api/CPP/reshape.hpp" -#include "api/CPP/activation.hpp" -#include "api/CPP/scale.hpp" -#include "api/CPP/custom_gpu_primitive.hpp" -#include "upsampling_inst.h" +#include "api/binary_convolution.hpp" #include "pass_manager.h" #include "program_node.h" #include "layout_optimizer.h" @@ -36,364 +24,405 @@ #include "program_helpers.h" #include #include +#include +#include +#include using namespace cldnn; // ToDo remove friendship relation from program_impl -reorder_inputs::reorder_inputs(layout_optimizer& lo_ref) : base_pass("reorder_inputs"), _lo(lo_ref) {} +reorder_inputs::reorder_inputs(layout_optimizer& lo_ref, reorder_factory& rf_ref) : base_pass("reorder_inputs"), _lo(lo_ref), _rf(rf_ref) {} -void reorder_inputs::run(program_impl& p) { run(p, _lo); } +void reorder_inputs::run(program_impl& p) { run(p, _lo, _rf); } -void reorder_inputs::run(program_impl& p, layout_optimizer& lo) { - // first pass to set layout optimization_attributes for topology - bool can_use_fsv32 = true; - bool can_use_f16 = true; - size_t total_conv_layers = 0; - size_t total_dw_conv_layers = 0; - size_t total_grouped_conv_layers = 0; - size_t opt_conv_layers_bfyx_f16 = 0; +namespace { - for (auto& node : p.get_processing_order()) { - auto& prim = *node; - if (prim.type() == cldnn::convolution::type_id()) { - if (prim.as().get_primitive()->split() > 1) - lo.set_optimization_attribute(layout_optimizer::optimization_attributes_type::splitted_convolution, 1); +std::map get_preferred_formats(program_impl& p, layout_optimizer& lo) { + std::map fmt_map; + for (auto n : p.get_processing_order()) { + if (!n->is_in_data_flow()) + continue; - if (prim.as().get_primitive()->groups > 1) - lo.set_optimization_attribute(layout_optimizer::optimization_attributes_type::group_convolution, 1); + auto ex = lo.get_preferred_format(*n); + fmt_map[n] = ex; + } + return fmt_map; +} + +enum class direction_e { + forwards = 0, + backwards = 1 +}; - if (prim.as().get_primitive()->deformable_mode) - lo.set_optimization_attribute(layout_optimizer::optimization_attributes_type::deformable_convolution, 1); +inline constexpr direction_e reverse(direction_e dir) { + return dir == direction_e::forwards ? direction_e::backwards : direction_e::forwards; +} - uint32_t ifm = static_cast(node->get_dependency(0).get_output_layout().size.feature[0]); - if (prim.as().get_primitive()->groups == ifm) - total_dw_conv_layers++; - else if (prim.as().get_primitive()->groups > 1 || prim.as().get_primitive()->split() > 1) - total_grouped_conv_layers++; +template +struct travel_direction_wrapper { + static const std::list& next_nodes(program_node* node) { + return node->get_users(); + } - if (lo.is_format_optimized(prim.as(), format::bfyx_f16)) - opt_conv_layers_bfyx_f16++; + template + static T& first(T& current, T& /*next*/) { return current; } - total_conv_layers++; - } + template + static T& second(T& /*current*/, T& next) { return next; } +}; - // list of layers that do not support yxfb or perform worse than bfyx - if (prim.type() == cldnn::detection_output::type_id() || prim.type() == cldnn::proposal::type_id() || - prim.type() == cldnn::roi_pooling::type_id() || prim.type() == cldnn::deconvolution::type_id() || - prim.type() == cldnn::upsampling::type_id() || prim.type() == cldnn::reorg_yolo::type_id()) - lo.set_optimization_attribute(layout_optimizer::optimization_attributes_type::bfyx_only_layer, 1); - - // Check if all layers in topology support fs_byx_fsv32 format - if (prim.is_in_data_flow() && prim.type() != cldnn::convolution::type_id() && - prim.type() != cldnn::pooling::type_id() && prim.type() != cldnn::eltwise::type_id() && - prim.type() != cldnn::fully_connected::type_id() && prim.type() != cldnn::reorder::type_id() && - prim.type() != cldnn::permute::type_id() && prim.type() != cldnn::reshape::type_id() && - prim.type() != cldnn::input_layout::type_id() && prim.type() != cldnn::softmax::type_id()) - can_use_fsv32 = false; - - if (prim.is_in_data_flow() && - prim.type() != cldnn::convolution::type_id() && - prim.type() != cldnn::activation::type_id() && - prim.type() != cldnn::pooling::type_id() && - prim.type() != cldnn::eltwise::type_id() && - prim.type() != cldnn::permute::type_id() && - prim.type() != cldnn::reshape::type_id() && - prim.type() != cldnn::detection_output::type_id() && - prim.type() != cldnn::custom_gpu_primitive::type_id() && - prim.type() != cldnn::concatenation::type_id() && - prim.type() != cldnn::fully_connected::type_id() && - prim.type() != cldnn::reorder::type_id() && - prim.type() != cldnn::input_layout::type_id() && - prim.type() != cldnn::softmax::type_id() && - prim.type() != cldnn::prior_box::type_id() && - prim.type() != cldnn::scale::type_id()) - can_use_f16 = false; +template <> +struct travel_direction_wrapper { + static const std::vector& next_nodes(program_node* node) { + return node->get_dependencies(); } + template + static T& first(T& /*current*/, T& next) { return next; } + + template + static T& second(T& current, T& /*next*/) { return current; } +}; + +template +bool can_propagate_formats_rec( + const std::map& fmt_map, + layout_optimizer& lo, + program_node* prev, + program_node* node, + format::type fmt) { + + auto sel_fmt = fmt_map.at(node); + if (fmt == sel_fmt) + return true; + + auto first_node = travel_direction_wrapper::first(prev, node); + auto second_node = travel_direction_wrapper::second(prev, node); + auto first_fmt = travel_direction_wrapper::first(fmt, sel_fmt); + auto second_fmt = travel_direction_wrapper::second(fmt, sel_fmt); + + if (lo.can_fuse_reorder(*first_node, + *second_node, + first_fmt, + second_fmt)) + return true; + + if (sel_fmt != format::any) + return false; + + if (!lo.is_format_supported(*node, fmt)) + return false; + + auto reverse_reorders = std::count_if( + travel_direction_wrapper::next_nodes(node).begin(), + travel_direction_wrapper::next_nodes(node).end(), + [&](program_node* rev) { + return rev->is_in_data_flow() && fmt_map.at(rev) != fmt && rev != prev; + }); + + if (reverse_reorders > 0) + return false; + + for (auto next : travel_direction_wrapper::next_nodes(node)) { + if (!next->is_in_data_flow()) + continue; + if (!can_propagate_formats_rec(fmt_map, lo, node, next, fmt)) + return false; + } - // Due to fact that single winograd convolution is faster than bfyx_f16 and - // using them together leads do redundant reorders, whole topology switch - // will be performed if at least half of layers can use bfyx_f16. - bool should_use_bfyx_f16_conv = can_use_f16 && - ((opt_conv_layers_bfyx_f16 / static_cast(total_conv_layers)) > 0.5f) && - total_conv_layers > 11 && - total_grouped_conv_layers == 0; // conv with groups are not supported correctly yet - - if (can_use_fsv32) - lo.set_optimization_attribute(layout_optimizer::optimization_attributes_type::only_fsv32_layers, 1); - - if (should_use_bfyx_f16_conv) - lo.set_optimization_attribute(layout_optimizer::optimization_attributes_type::bfyx_f16_network, 1); - - const auto reorder_input = [&p, &lo, should_use_bfyx_f16_conv](typed_program_node& conv_node) { - auto conv_prim = conv_node.get_primitive(); - auto& input_node = conv_node.get_dependency(0); - auto&& weights_layout = conv_node.weights(0).get_output_layout(); - auto&& input_layout = input_node.get_output_layout(); - - std::shared_ptr new_input = nullptr; - - if (input_node.type() == reorder::type_id()) { // convolution's input is a reorder - auto reorder_prim = input_node.as().typed_desc(); - auto& reorder_input = input_node.get_dependency(0); - auto reorder_layout = input_node.get_output_layout(); - reorder_layout.data_type = *reorder_prim->output_data_type; - new_input = lo.get_reorder(reorder_layout, - reorder_prim->id, - layout_optimizer::data_type::input, - conv_node, - weights_layout) - .first; - - auto reorder_removed = false; - if (new_input && new_input->output_format != format::winograd_2x3_s1_data && - new_input->output_format != format::bf8_xy16 && new_input->output_format != format::byxf && - new_input->output_format != format::fs_b_yx_fsv32 && - new_input->output_format != format::bfyx_f16) { // output format is not optimal - auto reorder_input_layout = reorder_input.get_output_layout(); - - auto opt_layout = - layout(*new_input->output_data_type, new_input->output_format, reorder_input_layout.size); - if (reorder_input_layout == opt_layout) { // reorder 'breaks' optimal format - if (reorder_prim->subtract_per_feature.empty() && reorder_prim->mean.empty() && - !reorder_prim->output_padding) { // just plain reorder - conv_node.replace_dependency(0, reorder_input); - if (input_node.get_users().size() == 0 && !input_node.is_output()) { - reorder_removed = p.extract_and_remove(input_node); - } - new_input = nullptr; - } else { // change reorder's output layout - reorder_prim->output_format = opt_layout.format; - reorder_prim->output_data_type = opt_layout.data_type; - new_input = nullptr; - } - } else { // current reorder gives bad output, simply change it - reorder_prim->output_format = opt_layout.format; - reorder_prim->output_data_type = opt_layout.data_type; - new_input = nullptr; - } - } + return true; +} - if (!reorder_removed) - input_node.recalc_output_layout(); - else - conv_node.recalc_output_layout(); - } else { - new_input = lo.get_reorder(input_node.get_output_layout(), - input_node.id(), - layout_optimizer::data_type::input, - conv_node, - weights_layout) - .first; +template +void propagate_formats_rec(std::map& fmt_map, + layout_optimizer& lo, + program_node* prev, + program_node* node, + format::type fmt) { + auto sel_fmt = fmt_map.at(node); + if (sel_fmt == fmt) + return; + + auto first_node = travel_direction_wrapper::first(prev, node); + auto second_node = travel_direction_wrapper::second(prev, node); + auto first_fmt = travel_direction_wrapper::first(fmt, sel_fmt); + auto second_fmt = travel_direction_wrapper::second(fmt, sel_fmt); + + if (lo.can_fuse_reorder(*first_node, + *second_node, + first_fmt, + second_fmt)) + return; + + fmt_map.at(node) = fmt; + + for (auto next : travel_direction_wrapper::next_nodes(node)) { + if (!next->is_in_data_flow()) + continue; + propagate_formats_rec(fmt_map, lo, node, next, fmt); + } +} + +template +void propagate_formats_in_dir(std::map& fmt_map, + layout_optimizer& lo, + program_node* node) { + auto fmt = fmt_map.at(node); + + for (auto next : travel_direction_wrapper::next_nodes(node)) { + if (!next->is_in_data_flow()) + continue; + if (!can_propagate_formats_rec(fmt_map, lo, node, next, fmt)) + return; + } + + for (auto next : travel_direction_wrapper::next_nodes(node)) { + if (!next->is_in_data_flow()) + continue; + propagate_formats_rec(fmt_map, lo, node, next, fmt); + } +} + +void propagate_formats(program_impl& p, std::map& fmt_map, layout_optimizer& lo) { + auto it = p.get_processing_order().begin(); + while (it != p.get_processing_order().end()) { + auto node = *it++; + + if (fmt_map.count(node) == 0 || fmt_map.at(node) == format::any) + continue; + + propagate_formats_in_dir(fmt_map, lo, node); + propagate_formats_in_dir(fmt_map, lo, node); + } +} + +struct reorder_cnt { + size_t number; + size_t total_sizes; +}; + +template +reorder_cnt count_reorders_in_dir(const std::map& fmt_map, layout_optimizer& lo, program_node* node) { + size_t cnt = 0; + size_t size = 0; + auto sel_fmt = fmt_map.at(node); + + for (auto next : travel_direction_wrapper::next_nodes(node)) { + if (!next->is_in_data_flow()) + continue; + + auto next_fmt = fmt_map.at(next); + + if (next_fmt == format::any || + (sel_fmt != next_fmt && + !lo.can_fuse_reorder(*travel_direction_wrapper::first(node, next), + *travel_direction_wrapper::second(node, next), + travel_direction_wrapper::first(sel_fmt, next_fmt), + travel_direction_wrapper::second(sel_fmt, next_fmt)))) { + cnt += 1; + size += travel_direction_wrapper::first(node, next)->get_output_layout().count(); } + } - if (new_input && new_input->output_format == format::winograd_2x3_s1_data) { - auto lower_size = (conv_prim->input_offset.negate() + input_layout.size); - - tensor upper_input_padding = tensor{0}; - upper_input_padding.spatial[0] = - (2 - (lower_size.spatial[0] % 2)) % 2; // winograd conv requires input's x to be in form 4 + 2n, with - // restriction that x >= 3, we can shortage it to x % 2 == 0 - upper_input_padding.spatial[1] = - (8 - ((lower_size.spatial[1] - 2) % 8)) % 8; // for y, y - 2 % 8 == 0 must hold - - p.apply_needed_padding(conv_node, - input_node, - padding{conv_prim->input_offset.negate().sizes(), upper_input_padding.sizes()}); - - auto winograd_output = std::make_shared("_winograd_" + conv_node.id(), - conv_node.id(), - input_layout.format, - input_layout.data_type, - std::vector{}, - cldnn_reorder_mean_mode::mean_subtract, - conv_node.output_layout.data_padding); - conv_node.output_layout.data_padding = padding{}; - program_node& back_node = p.get_or_create(winograd_output); - p.get_processing_order().insert_next(&conv_node, &back_node); - - auto bias_term = conv_node.bias_term(); - // create additional eltwise node after reorder to compute bias - if (bias_term) { - auto& bias_node = conv_node.get_dependency(2); - std::vector inputs = {back_node.id(), bias_node.id()}; - auto winograd_output_biases = std::make_shared(back_node.id() + "_bias", - inputs, - cldnn::eltwise_mode::sum, - conv_prim->with_activation, - conv_prim->activation_negative_slope, - back_node.get_output_layout().data_padding); - back_node.get_output_layout().data_padding = padding{}; - auto& back_bias_node = p.get_or_create(winograd_output_biases); - p.get_processing_order().insert_next(&back_node, &back_bias_node); - p.replace_all_usages(back_node, back_bias_node); - p.add_connection(back_node, back_bias_node); - p.add_connection(bias_node, back_bias_node); - conv_node.invalidate_users(); - p.replace_all_usages(conv_node, back_bias_node); - } + return { cnt, size }; +} - if (conv_prim->with_activation) { - conv_node.typed_desc()->with_activation = false; - if (!bias_term) - back_node.set_fused_activation( - activation_relu_negative_slope, - cldnn_activation_additional_params_t{conv_prim->activation_negative_slope, 0.0f}); - } +reorder_cnt count_reorders(const std::map& fmt_map, layout_optimizer& lo, program_node* node) { + auto fwd = count_reorders_in_dir(fmt_map, lo, node); + auto bwd = count_reorders_in_dir(fmt_map, lo, node); - if (!bias_term) { - conv_node.invalidate_users(); - p.replace_all_usages(conv_node, back_node); - } - p.add_connection(conv_node, back_node); - - auto& r_node = p.get_or_create(new_input); - r_node.as().set_input_offset(conv_prim->input_offset); - - if (!bias_term) { - p.swap_names(conv_node, back_node); - if (conv_node.is_output()) { - conv_node.set_output(false); - back_node.set_output(true); - for (auto& output : p.get_outputs()) { - if (output == &conv_node) { - output = &back_node; - break; - } - } - } - } else { - conv_node.remove_dependency(2); - auto& back_bias_node = *(p.nodes_map.find(back_node.id() + "_bias")->second); - p.swap_names(conv_node, back_bias_node); - if (conv_node.is_output()) { - conv_node.set_output(false); - back_bias_node.set_output(true); - for (auto& output : p.get_outputs()) { - if (output == &conv_node) { - output = &back_bias_node; - break; - } - } - } + return { fwd.number + bwd.number, fwd.total_sizes + bwd.total_sizes }; +} + +void minimize_local_reorders(program_impl& p, std::map& fmt_map, layout_optimizer& lo) { + for (auto node : p.get_processing_order()) { + if (!node->is_in_data_flow()) + continue; + + if (lo.get_preferred_format(*node) != format::any) + continue; + + if (fmt_map.at(node) == format::any) { + auto out_fmt = node->get_output_layout().format; + if (lo.is_format_supported(*node, out_fmt)) { + fmt_map.at(node) = out_fmt; } } - if (new_input && (new_input->output_format == format::bf8_xy16 || new_input->output_format == format::byxf)) { - auto conv1x1_output = std::make_shared("_conv1x1_reorder_back_" + conv_node.id(), - conv_node.id(), - input_layout.format, - input_layout.data_type); - auto& back_node = p.get_or_create(conv1x1_output); - p.get_processing_order().insert_next(&conv_node, &back_node); - conv_node.invalidate_users(); - p.replace_all_usages(conv_node, back_node); - p.add_connection(conv_node, back_node); - - p.mark_if_constant(back_node); - p.mark_if_data_flow(back_node); - p.mark_if_constant(conv_node); - p.mark_if_data_flow(conv_node); - } + auto sel_fmt = fmt_map.at(node); + auto best_reorder_cnt = count_reorders(fmt_map, lo, node); + auto best_format = sel_fmt; + + if (best_reorder_cnt.number == 0) + continue; + + std::set local_formats; - if (new_input) { - auto& r_node = p.get_or_create(new_input); - p.add_intermediate(r_node, conv_node, 0, r_node.get_dependencies().empty()); - conv_node.recalc_output_layout(); + for (auto user : node->get_users()) { + auto user_fmt = fmt_map.at(user); + + if (user_fmt != format::any && + lo.is_format_supported(*node, user_fmt)) { + local_formats.insert(user_fmt); + } } - }; - const auto reorder_input_convolution_binary = [&p, &lo](typed_program_node& conv_bin_node) { - auto conv_bin_prim = conv_bin_node.get_primitive(); - auto& input_node = conv_bin_node.get_dependency(0); - auto&& weights_layout = conv_bin_node.weights(0).get_output_layout(); - - std::shared_ptr new_input = nullptr; - - if (input_node.type() == reorder::type_id()) { - auto reorder_prim = input_node.as().typed_desc(); - auto& reorder_input = input_node.get_dependency(0); - auto reorder_layout = input_node.get_output_layout(); - reorder_layout.data_type = *reorder_prim->output_data_type; - new_input = lo.get_reorder(reorder_layout, - reorder_prim->id, - layout_optimizer::data_type::input, - conv_bin_node, - weights_layout) - .first; - - auto reorder_removed = false; - if (new_input && new_input->output_format != format::b_fs_yx_32fp) { - auto reorder_input_layout = reorder_input.get_output_layout(); - - auto opt_layout = - layout(*new_input->output_data_type, new_input->output_format, reorder_input_layout.size); - if (reorder_input_layout == opt_layout) { // reorder 'breaks' optimal format - if (reorder_prim->subtract_per_feature.empty() && reorder_prim->mean.empty() && - !reorder_prim->output_padding) { // just plain reorder - conv_bin_node.replace_dependency(0, reorder_input); - if (input_node.get_users().size() == 0 && !input_node.is_output()) { - reorder_removed = p.extract_and_remove(input_node); - } - new_input = nullptr; - } else { // change reorder's output layout - reorder_prim->output_format = opt_layout.format; - reorder_prim->output_data_type = opt_layout.data_type; - new_input = nullptr; - } - } else { // current reorder gives bad output, simply change it - reorder_prim->output_format = opt_layout.format; - reorder_prim->output_data_type = opt_layout.data_type; - new_input = nullptr; - } + for (auto dep : node->get_dependencies()) { + if (!dep->is_in_data_flow()) + continue; + + auto dep_fmt = fmt_map.at(dep); + + if (dep_fmt != format::any && + lo.is_format_supported(*node, dep_fmt)) { + local_formats.insert(dep_fmt); } + } + + if (local_formats.empty()) + continue; - if (!reorder_removed) - input_node.recalc_output_layout(); - else - conv_bin_node.recalc_output_layout(); - } else { - new_input = lo.get_reorder(input_node.get_output_layout(), - input_node.id(), - layout_optimizer::data_type::input, - conv_bin_node, - weights_layout) - .first; + for (auto new_fmt : local_formats) { + fmt_map.at(node) = new_fmt; + + auto reorders_cnt = count_reorders(fmt_map, lo, node); + + if (reorders_cnt.number < best_reorder_cnt.number || + (reorders_cnt.number == best_reorder_cnt.number && reorders_cnt.total_sizes < best_reorder_cnt.total_sizes) ) { + best_reorder_cnt = reorders_cnt; + best_format = new_fmt; + } } - if (new_input) { - auto& r_node = p.get_or_create(new_input); - p.add_intermediate(r_node, conv_bin_node, 0, r_node.get_dependencies().empty()); - conv_bin_node.recalc_output_layout(); + fmt_map.at(node) = best_format; + } +} + +template +void insert_reorders_in_dir(program_impl& p, const std::map& fmt_map, reorder_factory& rf, program_node* node) { + auto fmt = fmt_map.at(node); + + auto next_cpy = travel_direction_wrapper::next_nodes(node); + for (auto next : next_cpy) { + if (!next->is_in_data_flow()) + continue; + + if (fmt_map.count(next) > 0 && fmt_map.at(next) == fmt) + continue; + + auto next_layout = next->get_output_layout(); + auto current_layout = node->get_output_layout(); + + auto first_layout = travel_direction_wrapper::first(current_layout, next_layout); + auto in_layout = first_layout; + auto out_layout = first_layout; + + travel_direction_wrapper::first(in_layout, out_layout).format = fmt; + + auto reorder_pair = rf.get_reorder(travel_direction_wrapper::first(node, next)->id(), + in_layout, + out_layout); + auto reorder = reorder_pair.first; + + if (reorder) { + auto& reorder_node = p.get_or_create(reorder); + p.add_intermediate(reorder_node, + *travel_direction_wrapper::second(node, next), + *travel_direction_wrapper::first(node, next), + !reorder_pair.second); } - }; + } +} + +void insert_reorders(program_impl& p, const std::map& fmt_map, reorder_factory& rf) { + auto it = p.get_processing_order().begin(); + while (it != p.get_processing_order().end()) { + auto node = *(it++); + + if (fmt_map.count(node) != 1) + continue; + + auto fmt = fmt_map.at(node); + if (fmt == format::any) + continue; + + insert_reorders_in_dir(p, fmt_map, rf, node); + } - const auto reorder_input_detection_output = [&p, &lo](typed_program_node& detection_output_node) { + it = p.get_processing_order().begin(); + while (it != p.get_processing_order().end()) { + auto node = *(it++); + + if (fmt_map.count(node) != 1) + continue; + + auto fmt = fmt_map.at(node); + if (fmt == format::any) + continue; + + insert_reorders_in_dir(p, fmt_map, rf, node); + } +} + +} // namespace + +void reorder_inputs::run(program_impl& p, layout_optimizer& lo, reorder_factory& rf) { + auto fmt_map = get_preferred_formats(p, lo); + propagate_formats(p, fmt_map, lo); + minimize_local_reorders(p, fmt_map, lo); + insert_reorders(p, fmt_map, rf); + + for (auto n : p.get_processing_order()) { + n->recalc_output_layout(true); + } + + const auto reorder_input_detection_output = [&p, &rf](typed_program_node& detection_output_node) { auto detection_output_prim = detection_output_node.get_primitive(); for (size_t i = 0; i < detection_output_node.get_dependencies().size(); i++) { auto& input = detection_output_node.get_dependency(i); - std::shared_ptr new_input = lo.get_reorder(input.get_output_layout(), - input.id(), - layout_optimizer::data_type::input, - detection_output_node, - layout{data_types::f32, format::bfyx, tensor{}}) - .first; - - if (new_input) { - p.add_intermediate(new_input, detection_output_node, i); + auto new_input = rf.get_reorder(input.id(), + input.get_output_layout(), + layout{ data_types::f32, format::bfyx, input.get_output_layout().size }); + + if (new_input.first) { + p.add_intermediate(new_input.first, detection_output_node, i, !new_input.second); + } + } + }; + + const auto reorder_input_binary_convolution = [&p, &rf](typed_program_node& binary_conv_node) { + auto& input = binary_conv_node.input(); + auto input_layout = input.get_output_layout(); + auto new_layout = input_layout; + new_layout.data_type = data_types::bin; + + auto reorder = rf.get_reorder(input.id(), input_layout, new_layout); + + if (reorder.first) { + p.add_intermediate(reorder.first, binary_conv_node, 0, !reorder.second); + } + }; + + const auto reorder_input_deconvolution = [&p, &lo, &rf](typed_program_node& deconv_node) { + auto& input = deconv_node.input(); + auto input_layout = input.get_output_layout(); + auto new_format = lo.get_preferred_format(deconv_node); + if (new_format == format::bfzyx_f16) { + auto reorder = rf.get_reorder(input.id(), input_layout, + layout{ input_layout.data_type, new_format, input_layout.size }); + if (reorder.first) { + p.add_intermediate(reorder.first, deconv_node, 0, !reorder.second); } } }; for (auto& prim : p.get_processing_order()) { - // there's an assumption that only convolution will take data/input_layout as input - // exception to that rule would be a convolution which takes a reorder as input - see reoder_input above - program_helpers::do_for_types( + program_helpers::do_for_types( *prim, - reorder_input, // case for convolution - reorder_input_detection_output, // case for detection-output - reorder_input_convolution_binary); // case for binary convolution + reorder_input_detection_output, + reorder_input_binary_convolution, + reorder_input_deconvolution); } } diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/reverse_optional_nodes_outputs.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/reverse_optional_nodes_outputs.cpp index 3cd101b0ca518c..7d8cd273528a57 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/reverse_optional_nodes_outputs.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/reverse_optional_nodes_outputs.cpp @@ -24,7 +24,6 @@ using namespace cldnn; - /* Pass made for nodes, which has optional outputs (and had to reverse connections so the processing order was valid). diff --git a/inference-engine/thirdparty/clDNN/src/graph_optimizer/strided_slice_optimize.cpp b/inference-engine/thirdparty/clDNN/src/graph_optimizer/strided_slice_optimize.cpp index 61cbc9ae05f4fa..e70620ed0f0c1c 100644 --- a/inference-engine/thirdparty/clDNN/src/graph_optimizer/strided_slice_optimize.cpp +++ b/inference-engine/thirdparty/clDNN/src/graph_optimizer/strided_slice_optimize.cpp @@ -77,8 +77,8 @@ void strided_slice_optimize::run(program_impl& p) { auto& reshape_prim_node = p.get_or_create(reshape_prim); - reshape_prim_node.set_output_layout( - {node_layout.data_type, node_layout.format, reshape_prim->output_shape}); + layout output_layout = { node_layout.data_type, node_layout.format, reshape_prim->output_shape }; + reshape_prim_node.set_output_layout(output_layout); p.add_intermediate(reshape_prim_node, *node, 0, true); p.extract_and_remove(*node); diff --git a/inference-engine/thirdparty/clDNN/src/half.cpp b/inference-engine/thirdparty/clDNN/src/half.cpp index 6147f68a27ad27..11a3eca7919ba1 100644 --- a/inference-engine/thirdparty/clDNN/src/half.cpp +++ b/inference-engine/thirdparty/clDNN/src/half.cpp @@ -15,10 +15,11 @@ */ /////////////////////////////////////////////////////////////////////////////////////////////////// -#include "api_impl.h" #include +#include namespace cldnn { + float half_to_float(uint16_t value) { static const uint32_t FLOAT16_EXP_SHIFT = (23 - 10); static const uint32_t FLOAT16_EXP_MASK = 0x7C00; diff --git a/inference-engine/thirdparty/clDNN/src/include/activation_grad_inst.h b/inference-engine/thirdparty/clDNN/src/include/activation_grad_inst.h index 993a7f62e67eee..adc349025ec6bc 100644 --- a/inference-engine/thirdparty/clDNN/src/include/activation_grad_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/activation_grad_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/activation_grad.hpp" +#include "api/activation_grad.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/activation_inst.h b/inference-engine/thirdparty/clDNN/src/include/activation_inst.h index 53e9f3459781ae..d5f5e4085a1650 100644 --- a/inference-engine/thirdparty/clDNN/src/include/activation_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/activation_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/activation.hpp" +#include "api/activation.hpp" #include "primitive_inst.h" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/api_impl.h b/inference-engine/thirdparty/clDNN/src/include/api_impl.h deleted file mode 100644 index 456a24b4c04af1..00000000000000 --- a/inference-engine/thirdparty/clDNN/src/include/api_impl.h +++ /dev/null @@ -1,125 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once - -#include "api/C/cldnn.h" -#include "api/CPP/cldnn_defs.h" - -#include -#include -#include - -#define API_CAST(api_type, impl_type) \ - inline api_type api_cast(impl_type* value) { return reinterpret_cast(value); } \ - inline impl_type* api_cast(api_type value) { return reinterpret_cast(value); } - -namespace cldnn { -struct last_err { - /// @breif Sets the message of last error - void set_last_error_message(const std::string& msg) { _msg = msg; } - - void set_last_exception(const std::exception& ex) { _msg = ex.what(); } - - /// @breif Gets the message of last error - const std::string& get_last_error_message() { return _msg; } - static last_err& instance(); - -private: - std::string _msg; - last_err() : _msg("Operation succeed") {} -}; - -// float <--> half convertors -float half_to_float(uint16_t value); -uint16_t float_to_half(float value); -} // namespace cldnn - -template -T exception_handler(cldnn_status default_error, - cldnn_status* status, - const T& default_result, - std::function func) { - // NOTE for implementer: status should not be modified after successful func() call - try { - if (status) - *status = CLDNN_SUCCESS; - return func(); - } catch (const cldnn::error& err) { - if (status) - *status = err.status(); - cldnn::last_err::instance().set_last_exception(err); - -#ifndef NDEBUG - static_cast(default_result); - throw; -#endif - } catch (const std::exception& err) { - if (status) - *status = default_error; - cldnn::last_err::instance().set_last_exception(err); - -#ifndef NDEBUG - static_cast(default_result); - throw; -#endif - } catch (...) { - if (status) - *status = default_error; - cldnn::last_err::instance().set_last_error_message("error unknown"); - -#ifndef NDEBUG - static_cast(default_result); - throw; -#endif - } - -#ifdef NDEBUG - return default_result; -#endif -} - -inline void exception_handler(cldnn_status default_error, cldnn_status* status, std::function func) { - // NOTE for implementer: status should not be modified after successful func() call - try { - if (status) - *status = CLDNN_SUCCESS; - func(); - } catch (const cldnn::error& err) { - if (status) - *status = err.status(); - cldnn::last_err::instance().set_last_exception(err); -#ifndef NDEBUG - throw; -#endif - } catch (const std::exception& err) { - if (status) - *status = default_error; - cldnn::last_err::instance().set_last_exception(err); - -#ifndef NDEBUG - throw; -#endif - } catch (...) { - if (status) - *status = default_error; - cldnn::last_err::instance().set_last_error_message("error unknown"); -#ifndef NDEBUG - throw; -#endif - } -} diff --git a/inference-engine/thirdparty/clDNN/src/include/apply_adam_inst.h b/inference-engine/thirdparty/clDNN/src/include/apply_adam_inst.h index 50bd523e01b657..c754b4e414d53a 100644 --- a/inference-engine/thirdparty/clDNN/src/include/apply_adam_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/apply_adam_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/apply_adam.hpp" +#include "api/apply_adam.hpp" #include "primitive_inst.h" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/arg_max_min_inst.h b/inference-engine/thirdparty/clDNN/src/include/arg_max_min_inst.h index e181bb860a8407..939d2211351614 100644 --- a/inference-engine/thirdparty/clDNN/src/include/arg_max_min_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/arg_max_min_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/arg_max_min.hpp" +#include "api/arg_max_min.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/average_unpooling_inst.h b/inference-engine/thirdparty/clDNN/src/include/average_unpooling_inst.h index 4a600b514c2f69..be91e855b7accf 100644 --- a/inference-engine/thirdparty/clDNN/src/include/average_unpooling_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/average_unpooling_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/average_unpooling.hpp" +#include "api/average_unpooling.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/batch_norm_grad_inst.h b/inference-engine/thirdparty/clDNN/src/include/batch_norm_grad_inst.h index 5eb6564c734ca2..0d2c617b00d53b 100644 --- a/inference-engine/thirdparty/clDNN/src/include/batch_norm_grad_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/batch_norm_grad_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/batch_norm_grad.hpp" +#include "api/batch_norm_grad.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/batch_norm_inst.h b/inference-engine/thirdparty/clDNN/src/include/batch_norm_inst.h index c0d265e412ade2..485131ebab70e9 100644 --- a/inference-engine/thirdparty/clDNN/src/include/batch_norm_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/batch_norm_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/batch_norm.hpp" +#include "api/batch_norm.hpp" #include "primitive_inst.h" #include "mutable_data_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/binary_convolution_inst.h b/inference-engine/thirdparty/clDNN/src/include/binary_convolution_inst.h index ad12bfd9a19176..027499c55859f9 100644 --- a/inference-engine/thirdparty/clDNN/src/include/binary_convolution_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/binary_convolution_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/binary_convolution.hpp" +#include "api/binary_convolution.hpp" #include "primitive_inst.h" #include @@ -25,14 +25,6 @@ namespace cldnn { -struct fused_primitive_desc { - std::shared_ptr prim; - size_t dep_start_idx; - std::vector deps; - cldnn_activation_func_t activation; - cldnn_activation_additional_params activation_params; -}; - template <> struct typed_program_node : public typed_program_node_base { using parent = typed_program_node_base; @@ -63,39 +55,9 @@ struct typed_program_node : public typed_program_node_baseget_primitive(); - local_desc.dep_start_idx = this->get_dependencies().size(); - local_desc.activation = cldnn_activation_func_t::activation_none; - if (p->get_fused_activation_func() != cldnn_activation_func_t::activation_none) { - local_desc.activation = p->get_fused_activation_func(); - local_desc.activation_params = p->get_fused_activation_params(); - } - - for (size_t i = 1; i < p->get_dependencies().size(); i++) { - auto& dep = p->get_dependency(i); - this->dependencies.push_back(&dep); - local_desc.deps.push_back(dep.id()); - dep.users.push_back(this); - } - fused_prims.push_back(local_desc); - } - - const std::vector& get_fused_primitives() const { return fused_prims; } - - size_t get_fused_inputs_count() const { - size_t count = 0; - for (auto& fp : get_fused_primitives()) { - count += fp.deps.size(); - } - return count; - } - private: int32_t split; bool depthwise_sep_opt; - std::vector fused_prims; }; using binary_convolution_node = typed_program_node; @@ -118,12 +80,6 @@ class typed_primitive_inst : public typed_primitive_inst_bas return dep_memory(1 + index); } - - memory_impl& fused_memory(size_t dep_id) const { return dep_memory(1 + node.get_split() + dep_id); } - - bool has_fused_primitives() const { return !node.get_fused_primitives().empty(); } - - size_t get_fused_mem_count() const { return node.get_fused_inputs_count(); } }; using binary_convolution_inst = typed_primitive_inst; diff --git a/inference-engine/thirdparty/clDNN/src/include/border_inst.h b/inference-engine/thirdparty/clDNN/src/include/border_inst.h index 4c39a4a076679e..5d750f621e1ef1 100644 --- a/inference-engine/thirdparty/clDNN/src/include/border_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/border_inst.h @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include +#include #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/broadcast_inst.h b/inference-engine/thirdparty/clDNN/src/include/broadcast_inst.h index c1cfd97a00ed21..aa9cd1cd0ef0e6 100644 --- a/inference-engine/thirdparty/clDNN/src/include/broadcast_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/broadcast_inst.h @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include +#include #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/concatenation_inst.h b/inference-engine/thirdparty/clDNN/src/include/concatenation_inst.h index 9d2c54474738b3..9899a3e12ce04c 100644 --- a/inference-engine/thirdparty/clDNN/src/include/concatenation_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/concatenation_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/concatenation.hpp" +#include "api/concatenation.hpp" #include "primitive_inst.h" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/condition_inst.h b/inference-engine/thirdparty/clDNN/src/include/condition_inst.h index 5bff4f777e5603..c03931c8f88596 100644 --- a/inference-engine/thirdparty/clDNN/src/include/condition_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/condition_inst.h @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include +#include #include "network_impl.h" #include "primitive_inst.h" @@ -49,7 +49,7 @@ struct typed_program_node : public typed_program_node_base void add_or_change_input_layout(const program_node& node) { auto layout = node.get_dependency(0).get_output_layout(); auto input_id = node.as().result_id(); - if (_program == (program_impl::ptr) nullptr) { // if first run, create input_layout + if (_topology.get_primitives().count(input_id) == 0) { _topology.add(std::make_shared(input_id, layout)); for (auto& prim : _topology.get_primitives()) { for (auto& inp : prim.second->input) { @@ -68,8 +68,8 @@ struct typed_program_node : public typed_program_node_base typed_program_node(std::shared_ptr prim, program_impl& prog) : parent(prim, prog), - _branch_true(*api_cast(this->get_primitive()->topology_true.get())), - _branch_false(*api_cast(this->get_primitive()->topology_false.get())) {} + _branch_true(*this->get_primitive()->topology_true.get()), + _branch_false(*this->get_primitive()->topology_false.get()) {} program_node& input() const { return get_dependency(0); } program_node& compare() const { return get_dependency(1); } diff --git a/inference-engine/thirdparty/clDNN/src/include/contract_inst.h b/inference-engine/thirdparty/clDNN/src/include/contract_inst.h index 29e4c793a02c60..08ff773e19447c 100644 --- a/inference-engine/thirdparty/clDNN/src/include/contract_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/contract_inst.h @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include +#include #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/convolution_grad_weights_inst.h b/inference-engine/thirdparty/clDNN/src/include/convolution_grad_weights_inst.h index bda5093cd9ded8..698ad1f8db8341 100644 --- a/inference-engine/thirdparty/clDNN/src/include/convolution_grad_weights_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/convolution_grad_weights_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/convolution_grad_weights.hpp" +#include "api/convolution_grad_weights.hpp" #include "primitive_inst.h" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/convolution_inst.h b/inference-engine/thirdparty/clDNN/src/include/convolution_inst.h index 6922b90890a2a8..d71f0a8bdffe18 100644 --- a/inference-engine/thirdparty/clDNN/src/include/convolution_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/convolution_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/convolution.hpp" +#include "api/convolution.hpp" #include "primitive_inst.h" #include @@ -30,15 +30,6 @@ struct typed_program_node : public typed_program_node_base; public: - struct fused_primitive_desc { - std::shared_ptr prim; - size_t dep_start_idx; - std::vector deps; - cldnn_activation_func_t activation; - cldnn_activation_additional_params activation_params; - }; - - typed_program_node(std::shared_ptr prim, program_impl& prog) : parent(prim, prog), split(this->get_primitive()->split()), @@ -89,7 +80,6 @@ struct typed_program_node : public typed_program_node_base(idx) >= this->get_split()) throw std::range_error("quantization factor offset too big"); - return get_dependency(1 + (1 + 1 * bias_term()) * this->get_split() + idx + get_trans_dep_offset()); } @@ -108,57 +98,12 @@ struct typed_program_node : public typed_program_node_base(idx) >= this->get_split()) - throw std::range_error("eltwise offset too big"); - - int index = 1 + this->get_split() - + (bias_term() ? this->get_split() : 0) - + (weights_quantization_term() ? this->get_split() : 0) - + (output_calibration_term() ? this->get_split() : 0); - return get_dependency(static_cast(index)); - } - - void add_fused_primitive(const program_node *p) { - fused_primitive_desc local_desc; - local_desc.prim = p->get_primitive(); - local_desc.dep_start_idx = this->get_dependencies().size(); - local_desc.activation = cldnn_activation_func_t::activation_none; - if (p->get_fused_activation_func() != cldnn_activation_func_t::activation_none) { - local_desc.activation = p->get_fused_activation_func(); - local_desc.activation_params = p->get_fused_activation_params(); - } - - for (size_t i = 0; i < p->get_dependencies().size(); i++) { - auto& dep = p->get_dependency(i); - if (dep.id() == this->id()) - continue; - - this->dependencies.push_back(&dep); - local_desc.deps.push_back(dep.id()); - dep.users.push_back(this); - } - fused_prims.push_back(local_desc); - } - - const std::vector& get_fused_primitives() const { - return fused_prims; - } - bool bias_term() const { return get_primitive()->bias.size() > 0; } bool weights_quantization_term() const { return get_primitive()->weights_quantization_factors.size() > 0; } bool output_calibration_term() const { return get_primitive()->output_calibration_factors.size() > 0; } - size_t get_fused_inputs_count() const { - size_t count = 0; - for (auto& fp : get_fused_primitives()) { - count += fp.deps.size(); - } - return count; - } - float get_input_qf() const { return input_qf; } float get_output_qf() const { return output_qf; } @@ -171,7 +116,6 @@ struct typed_program_node : public typed_program_node_base fused_prims; }; using convolution_node = typed_program_node; @@ -235,23 +179,11 @@ class typed_primitive_inst : public typed_primitive_inst_base; diff --git a/inference-engine/thirdparty/clDNN/src/include/crop_inst.h b/inference-engine/thirdparty/clDNN/src/include/crop_inst.h index f57320b3b6c322..75369a82e80edb 100644 --- a/inference-engine/thirdparty/clDNN/src/include/crop_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/crop_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/crop.hpp" +#include "api/crop.hpp" #include "primitive_inst.h" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/custom_gpu_primitive_inst.h b/inference-engine/thirdparty/clDNN/src/include/custom_gpu_primitive_inst.h index cb6d7a8adea60a..e964281a8f0027 100644 --- a/inference-engine/thirdparty/clDNN/src/include/custom_gpu_primitive_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/custom_gpu_primitive_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/custom_gpu_primitive.hpp" +#include "api/custom_gpu_primitive.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/data_inst.h b/inference-engine/thirdparty/clDNN/src/include/data_inst.h index 2aee4f1fb2c647..782e41d5e2711f 100644 --- a/inference-engine/thirdparty/clDNN/src/include/data_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/data_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/data.hpp" +#include "api/data.hpp" #include "primitive_inst.h" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/deconvolution_inst.h b/inference-engine/thirdparty/clDNN/src/include/deconvolution_inst.h index f968654b182f9e..e06c8454b1b581 100644 --- a/inference-engine/thirdparty/clDNN/src/include/deconvolution_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/deconvolution_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/deconvolution.hpp" +#include "api/deconvolution.hpp" #include "primitive_inst.h" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/deformable_convolution_inst.h b/inference-engine/thirdparty/clDNN/src/include/deformable_convolution_inst.h index ff744684f11ece..a622e519179b00 100644 --- a/inference-engine/thirdparty/clDNN/src/include/deformable_convolution_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/deformable_convolution_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/convolution.hpp" +#include "api/convolution.hpp" #include "primitive_inst.h" #include @@ -112,8 +112,6 @@ class typed_primitive_inst : public typed_primitive_inst_base; - - template <> struct typed_program_node : public typed_program_node_base { using parent = typed_program_node_base; diff --git a/inference-engine/thirdparty/clDNN/src/include/depth_to_space_inst.h b/inference-engine/thirdparty/clDNN/src/include/depth_to_space_inst.h index 297dc0327b438b..835222c9f752c1 100644 --- a/inference-engine/thirdparty/clDNN/src/include/depth_to_space_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/depth_to_space_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/depth_to_space.hpp" +#include "api/depth_to_space.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/detection_output_inst.h b/inference-engine/thirdparty/clDNN/src/include/detection_output_inst.h index 3503e0e05e10cc..9e495e0d956529 100644 --- a/inference-engine/thirdparty/clDNN/src/include/detection_output_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/detection_output_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/detection_output.hpp" +#include "api/detection_output.hpp" #include "primitive_inst.h" #include "topology_impl.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/eltwise_inst.h b/inference-engine/thirdparty/clDNN/src/include/eltwise_inst.h index 773e7e6cd0b305..c2d0204969c013 100644 --- a/inference-engine/thirdparty/clDNN/src/include/eltwise_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/eltwise_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/eltwise.hpp" +#include "api/eltwise.hpp" #include "primitive_inst.h" #include #include "topology_impl.h" diff --git a/inference-engine/thirdparty/clDNN/src/include/embed_inst.h b/inference-engine/thirdparty/clDNN/src/include/embed_inst.h index 2878ce2ebfba8c..e5ae3bb3986067 100644 --- a/inference-engine/thirdparty/clDNN/src/include/embed_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/embed_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/embed.hpp" +#include "api/embed.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/engine_impl.h b/inference-engine/thirdparty/clDNN/src/include/engine_impl.h index 039ad876743252..52df865eacabe4 100644 --- a/inference-engine/thirdparty/clDNN/src/include/engine_impl.h +++ b/inference-engine/thirdparty/clDNN/src/include/engine_impl.h @@ -16,8 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/memory.hpp" -#include "api_impl.h" +#include "api/memory.hpp" #include "event_impl.h" #include "refcounted_obj.h" #include "implementation_map.h" @@ -53,14 +52,14 @@ struct engine_impl : public refcounted_obj { explicit engine_impl(const engine_configuration& conf); ~engine_impl(); engine_types type() const { return engine_types::ocl; } - refcounted_obj_ptr allocate_memory(layout layout, uint16_t stream_id); - refcounted_obj_ptr allocate_memory(layout layout, + refcounted_obj_ptr allocate_memory(const layout& layout, uint16_t stream_id); + refcounted_obj_ptr allocate_memory(const layout& layout, primitive_id, uint32_t, std::set, uint16_t stream_id, bool reusable = true); - refcounted_obj_ptr reinterpret_buffer(const memory_impl& memory, layout new_layout); + refcounted_obj_ptr reinterpret_buffer(const memory_impl& memory, const layout& new_layout); bool is_the_same_buffer(const memory_impl& mem1, const memory_impl& mem2); refcounted_obj_ptr create_user_event(uint16_t stream_id, bool set = false); @@ -134,5 +133,3 @@ struct engine_impl : public refcounted_obj { memory_pool _memory_pool; }; } // namespace cldnn - -API_CAST(::cldnn_engine, cldnn::engine_impl) diff --git a/inference-engine/thirdparty/clDNN/src/include/error_handler.h b/inference-engine/thirdparty/clDNN/src/include/error_handler.h index e8a0a401a57b7e..517b9c0d471a68 100644 --- a/inference-engine/thirdparty/clDNN/src/include/error_handler.h +++ b/inference-engine/thirdparty/clDNN/src/include/error_handler.h @@ -21,7 +21,7 @@ #include #include #include -#include "api/CPP/layout.hpp" +#include "api/layout.hpp" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/event_impl.h b/inference-engine/thirdparty/clDNN/src/include/event_impl.h index a340a1af0bb4f9..3bfe644bce4b5e 100644 --- a/inference-engine/thirdparty/clDNN/src/include/event_impl.h +++ b/inference-engine/thirdparty/clDNN/src/include/event_impl.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api_impl.h" +#include "api/event.hpp" #include "refcounted_obj.h" #include @@ -33,18 +33,23 @@ struct event_impl : public refcounted_obj { void wait(); bool is_set(); virtual bool is_valid() const { return _attached; } - virtual void reset() { _attached = false; } + virtual void reset() { + _attached = false; + _set = false; + _profiling_captured = false; + _profiling_info.clear(); + } // returns true if handler has been successfully added - bool add_event_handler(cldnn_event_handler handler, void* data); + bool add_event_handler(event_handler handler, void* data); - const std::list& get_profiling_info(); + const std::list& get_profiling_info(); private: std::mutex _handlers_mutex; - std::list> _handlers; + std::list> _handlers; bool _profiling_captured = false; - std::list _profiling_info; + std::list _profiling_info; protected: bool _set = false; @@ -54,11 +59,11 @@ struct event_impl : public refcounted_obj { virtual void wait_impl() = 0; virtual bool is_set_impl() = 0; - virtual bool add_event_handler_impl(cldnn_event_handler, void*) { return true; } + virtual bool add_event_handler_impl(event_handler, void*) { return true; } // returns whether profiling info has been captures successfully and there's no need to call this impl a second time // when user requests to get profling info - virtual bool get_profiling_info_impl(std::list&) { return true; } + virtual bool get_profiling_info_impl(std::list&) { return true; } }; struct user_event : virtual public event_impl { @@ -78,5 +83,3 @@ struct user_event : virtual public event_impl { }; } // namespace cldnn - -API_CAST(::cldnn_event, cldnn::event_impl) diff --git a/inference-engine/thirdparty/clDNN/src/include/fully_connected_grad_input_inst.h b/inference-engine/thirdparty/clDNN/src/include/fully_connected_grad_input_inst.h index 309e7b249eb46d..e015f853bdbc79 100644 --- a/inference-engine/thirdparty/clDNN/src/include/fully_connected_grad_input_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/fully_connected_grad_input_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/fully_connected_grad_input.hpp" +#include "api/fully_connected_grad_input.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/fully_connected_grad_weights_inst.h b/inference-engine/thirdparty/clDNN/src/include/fully_connected_grad_weights_inst.h index ee4384e6436b35..9b63ea64e4e068 100644 --- a/inference-engine/thirdparty/clDNN/src/include/fully_connected_grad_weights_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/fully_connected_grad_weights_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/fully_connected_grad_weights.hpp" +#include "api/fully_connected_grad_weights.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/fully_connected_inst.h b/inference-engine/thirdparty/clDNN/src/include/fully_connected_inst.h index bfeaaab5e6ed02..8162664f862412 100644 --- a/inference-engine/thirdparty/clDNN/src/include/fully_connected_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/fully_connected_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/fully_connected.hpp" +#include "api/fully_connected.hpp" #include "primitive_inst.h" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/fused_conv_bn_scale_inst.h b/inference-engine/thirdparty/clDNN/src/include/fused_conv_bn_scale_inst.h index 473701984df451..460f02c568aa21 100644 --- a/inference-engine/thirdparty/clDNN/src/include/fused_conv_bn_scale_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/fused_conv_bn_scale_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api_extension/CPP/fused_conv_bn_scale.hpp" +#include "api_extension/fused_conv_bn_scale.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/fused_conv_eltwise_inst.h b/inference-engine/thirdparty/clDNN/src/include/fused_conv_eltwise_inst.h index ede85cb8e0c5dd..f7fe3afacb2b93 100644 --- a/inference-engine/thirdparty/clDNN/src/include/fused_conv_eltwise_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/fused_conv_eltwise_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api_extension/CPP/fused_conv_eltwise.hpp" +#include "api_extension/fused_conv_eltwise.hpp" #include "primitive_inst.h" #include @@ -39,9 +39,9 @@ struct typed_program_node : public typed_program_node_baseeltw.with_activation) { auto slope = get_primitive()->eltw.activation_negative_slope; if (slope == 0.f) { - this->set_fused_activation(activation_relu, {}); + this->add_fused_activation(activation_func::relu, {}); } else { - this->set_fused_activation(activation_relu_negative_slope, { slope, 0.f }); + this->add_fused_activation(activation_func::relu_negative_slope, { slope, 0.f }); } } } diff --git a/inference-engine/thirdparty/clDNN/src/include/gather_inst.h b/inference-engine/thirdparty/clDNN/src/include/gather_inst.h index 8b7c6fb49c591e..e54a0a6a5387fc 100644 --- a/inference-engine/thirdparty/clDNN/src/include/gather_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/gather_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/gather.hpp" +#include "api/gather.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/gather_tree_inst.h b/inference-engine/thirdparty/clDNN/src/include/gather_tree_inst.h new file mode 100644 index 00000000000000..64caaac2e01e18 --- /dev/null +++ b/inference-engine/thirdparty/clDNN/src/include/gather_tree_inst.h @@ -0,0 +1,49 @@ +// Copyright (c) 2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +#include "primitive_inst.h" +#include +#include + +namespace cldnn { +template <> +struct typed_program_node : typed_program_node_base { +private: + using parent = typed_program_node_base; +public: + using parent::parent; + typed_program_node(const std::shared_ptr prim, program_impl& prog) : parent(prim, prog) { + } + program_node& input() const { return get_dependency(0); } +}; + +using gather_tree_node = typed_program_node; + +template <> +class typed_primitive_inst : public typed_primitive_inst_base { + using parent = typed_primitive_inst_base; + +public: + static layout calc_output_layout(gather_tree_node const& node); + static std::string to_string(gather_tree_node const& node); + typed_primitive_inst(network_impl& network, gather_tree_node const& node); +}; + +using gather_tree_inst = typed_primitive_inst; + +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/src/include/gemm_inst.h b/inference-engine/thirdparty/clDNN/src/include/gemm_inst.h index ff2aea2833c5c3..1db6ee319b7e58 100644 --- a/inference-engine/thirdparty/clDNN/src/include/gemm_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/gemm_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/gemm.hpp" +#include "api/gemm.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/generic_layer.h b/inference-engine/thirdparty/clDNN/src/include/generic_layer.h deleted file mode 100644 index cadb4797e55326..00000000000000 --- a/inference-engine/thirdparty/clDNN/src/include/generic_layer.h +++ /dev/null @@ -1,35 +0,0 @@ -/* -// Copyright (c) 2016 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once - -#include "api/C/cldnn.h" - -namespace cldnn { -/// @brief Changes how data is ordered in memory. Value type is not changed & all information is preserved. -/// @details Corresponding values are bitwise equal before/after reorder. -/// Also merged with subtraction layer, which can subtract values while doing reordering. -CLDNN_BEGIN_PRIMITIVE_DESC(generic_layer) -/// @brief Requested memory layout. -cldnn_layout output_layout; -const void* generic_params; - -CLDNN_END_PRIMITIVE_DESC(generic_layer) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(generic_layer); - -} // namespace cldnn \ No newline at end of file diff --git a/inference-engine/thirdparty/clDNN/src/include/generic_layer.hpp b/inference-engine/thirdparty/clDNN/src/include/generic_layer.hpp index dbd92a9b993094..47f8305315c663 100644 --- a/inference-engine/thirdparty/clDNN/src/include/generic_layer.hpp +++ b/inference-engine/thirdparty/clDNN/src/include/generic_layer.hpp @@ -16,9 +16,8 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "generic_layer.h" -#include "api/CPP/primitive.hpp" -#include "api/CPP/memory.hpp" +#include "api/primitive.hpp" +#include "api/memory.hpp" #include "kernel_selector_helper.h" #include @@ -35,7 +34,7 @@ namespace cldnn { /// @details Corresponding values are bitwise equal before/after reorder. /// Also merged with subtraction layer, which can subtract values while doing reordering. /// NOTE THAT THIS WILL SUBTRACT THE SAME VALUES FROM EACH BATCH. -struct generic_layer : public primitive_base { +struct generic_layer : public primitive_base { CLDNN_DECLARE_PRIMITIVE(generic_layer) /// @brief Constructs generic_layer primitive which takes mean subtract values from another primitive. @@ -50,23 +49,12 @@ struct generic_layer : public primitive_baseoutput_layout), - generic_params(*static_cast(dto->generic_params)) {} - /// @brief Requested memory layout. layout output_layout; const kernel_selector::generic_kernel_params generic_params; protected: std::vector> get_dependencies() const override { return {}; } - - void update_dto(dto& dto) const override { - dto.output_layout = output_layout; - dto.generic_params = &generic_params; - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/src/include/index_select_inst.h b/inference-engine/thirdparty/clDNN/src/include/index_select_inst.h index 42b796c414379b..dbb2b615769fb9 100644 --- a/inference-engine/thirdparty/clDNN/src/include/index_select_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/index_select_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/index_select.hpp" +#include "api/index_select.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/input_layout_inst.h b/inference-engine/thirdparty/clDNN/src/include/input_layout_inst.h index 324a1a6474cc47..bf33de6f5924b1 100644 --- a/inference-engine/thirdparty/clDNN/src/include/input_layout_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/input_layout_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/input_layout.hpp" +#include "api/input_layout.hpp" #include "primitive_inst.h" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/internal_primitive.h b/inference-engine/thirdparty/clDNN/src/include/internal_primitive.h index 36dff33b8faa7d..f815e06284f900 100644 --- a/inference-engine/thirdparty/clDNN/src/include/internal_primitive.h +++ b/inference-engine/thirdparty/clDNN/src/include/internal_primitive.h @@ -15,7 +15,7 @@ */ #pragma once -#include "api/CPP/primitive.hpp" +#include "api/primitive.hpp" #include "primitive_type.h" diff --git a/inference-engine/thirdparty/clDNN/src/include/internal_primitive_type_base.h b/inference-engine/thirdparty/clDNN/src/include/internal_primitive_type_base.h index cd799ecd4f3341..605ab5bf7f3cfc 100644 --- a/inference-engine/thirdparty/clDNN/src/include/internal_primitive_type_base.h +++ b/inference-engine/thirdparty/clDNN/src/include/internal_primitive_type_base.h @@ -26,15 +26,10 @@ namespace cldnn { template -struct internal_primitive_type_base : public ::cldnn_primitive_type { +struct internal_primitive_type_base : public primitive_type { static_assert(meta::is_internal_primitive::value, "Primitive type passed to internal_primitive_type_base should derive from internal_primitive"); - [[noreturn]] std::shared_ptr from_dto(const CLDNN_PRIMITIVE_DESC(primitive) *) const override { - throw std::runtime_error( - "Trying to create an internal primitive from dto - internal primitives are intransferable by design"); - } - [[noreturn]] std::shared_ptr create_node(program_impl&, const std::shared_ptr) const override { throw std::runtime_error( diff --git a/inference-engine/thirdparty/clDNN/src/include/kernel_selector_helper.h b/inference-engine/thirdparty/clDNN/src/include/kernel_selector_helper.h index 5dfadf6a5eac52..785fdce7269843 100644 --- a/inference-engine/thirdparty/clDNN/src/include/kernel_selector_helper.h +++ b/inference-engine/thirdparty/clDNN/src/include/kernel_selector_helper.h @@ -14,8 +14,12 @@ #pragma once -#include "api/C/cldnn.h" -#include "api/CPP/tensor.hpp" +#include "api/cldnn.hpp" +#include "api/tensor.hpp" +#include "api/eltwise.hpp" +#include "api/scale.hpp" +#include "api/quantize.hpp" +#include "api/activation.hpp" #include "kernel_selector_params.h" #include "kernel_selector_common.h" @@ -23,6 +27,8 @@ #include #include +#include +#include using namespace cldnn; @@ -101,9 +107,9 @@ std::string to_host_version(const cldnn::version_t& version); kernel_selector::data_tensor convert_data_tensor(const layout& l, uint32_t split = 1, const tensor view_offset = tensor {}); kernel_selector::weights_tensor convert_weights_tensor(const layout& l); layout from_weights_tensor(const kernel_selector::weights_tensor& t); -kernel_selector::activation_function get_kernel_selector_activation_param(cldnn_activation_func activation_func); +kernel_selector::activation_function get_kernel_selector_activation_param(activation_func activation_func); kernel_selector::activation_function get_kernel_selector_activation_grad_param( - cldnn_activation_grad_func activation_grad_func); + activation_grad_func activation_grad_func); template kernel_selector::dim_tensor convert_dim_vector(const tensor& t) { @@ -117,28 +123,37 @@ kernel_selector::dim_tensor convert_dim_vector(const tensor& t) { } template -inline void convert_activation_func_params(const p_type primitive, kernel_selector::base_activation_params& params) { +inline void convert_activation_func_params(const p_type primitive, std::vector& params) { const float negative_slope = primitive->activation_negative_slope; if (negative_slope != 0.0f) { - params.m = negative_slope; - params.function = kernel_selector::activation_function::RELU_NEGATIVE_SLOPE; + params.emplace_back(kernel_selector::activation_function::RELU_NEGATIVE_SLOPE, negative_slope, 0.0f); } else { - params.function = kernel_selector::activation_function::RELU; + params.emplace_back(kernel_selector::activation_function::RELU, 0.0f, 0.0f); } } template -inline void convert_fused_activation_func_params(const arg_t& arg, kernel_selector::base_activation_params& params) { - params.m = arg.get_fused_activation_params().a; - params.n = arg.get_fused_activation_params().b; - params.function = get_kernel_selector_activation_param(arg.get_fused_activation_func()); +inline void convert_fused_activation_func_params(const arg_t& arg, std::vector& params) { + for (size_t i = 0; i < arg.get_fused_activations_funcs().size(); i++) { + params.emplace_back(get_kernel_selector_activation_param(arg.get_fused_activations_funcs()[i]), + arg.get_fused_activations_params()[i].a, + arg.get_fused_activations_params()[i].b); + } +} + +template +inline void convert_new_activation_func(const p_type primitive, std::vector& params) { + params.insert(params.begin(), {get_kernel_selector_activation_param(primitive->activation_function), + primitive->additional_params.a, + primitive->additional_params.b}); } template -inline void convert_new_activation_func(const p_type primitive, kernel_selector::base_activation_params& params) { - params.function = get_kernel_selector_activation_param(primitive->activation_func); - params.m = primitive->additional_params.a; - params.n = primitive->additional_params.b; +inline void convert_new_activation_grad_func(const p_type primitive, std::vector& params) { + params.insert(params.begin(), {get_kernel_selector_activation_grad_param(primitive->activation_grad_function), + primitive->additional_params.a, + primitive->additional_params.b, + true}); } void set_params(const program_node& node, kernel_selector::params& params); @@ -157,7 +172,44 @@ inline params_t get_default_params(const arg_t& arg, uint32_t split = 1) { params.layerID = arg.id(); - convert_fused_activation_func_params(arg, params.activation); + convert_fused_activation_func_params(arg, params.activations); + size_t op_id = 0; + for (auto& fused_prim : arg.get_fused_primitives()) { + using op_type = kernel_selector::base_params::fused_operation_desc::Type; + kernel_selector::base_params::fused_operation_desc desc; + if (fused_prim.prim->type == eltwise::type_id()) { + desc.type = op_type::ELTWISE; + } else if (fused_prim.prim->type == scale::type_id()) { + desc.type = op_type::SCALE; + } else if (fused_prim.prim->type == quantize::type_id()) { + desc.type = op_type::QUANTIZE; + } else if (fused_prim.prim->type == activation::type_id()) { + desc.type = op_type::ACTIVATION; + std::shared_ptr p = fused_prim.prim; + auto activation_prim = std::static_pointer_cast(p); + desc.activation.m = activation_prim->additional_params.a; + desc.activation.n = activation_prim->additional_params.b; + desc.activation.function = get_kernel_selector_activation_param(activation_prim->activation_function); + } else { + throw std::runtime_error("Invalid fused primitive type in " + arg.id() + " node"); + } + + desc.dep_idx_start = fused_prim.dep_start_idx; + desc.dep_size = fused_prim.deps.size(); + desc.op_id = op_id++; + desc.output_tensor = convert_data_tensor(fused_prim.output_layout); + + for (size_t i = desc.dep_idx_start; i < desc.dep_idx_start + desc.dep_size; i++) { + desc.tensors.push_back(convert_data_tensor(arg.get_dependency(i).get_output_layout())); + } + + if (fused_prim.activation != activation_func::none) { + desc.activation.m = fused_prim.activation_params.a; + desc.activation.n = fused_prim.activation_params.b; + desc.activation.function = get_kernel_selector_activation_param(fused_prim.activation); + } + params.fused_ops.push_back(desc); + } return params; } @@ -186,8 +238,8 @@ inline params_t get_weights_bias_default_params(const arg_t& arg, uint32_t split params.bias.push_back(convert_data_tensor(layout(bias_layout.data_type, bias_layout.format, {bias_layout.size.batch[0], - bias_layout.size.feature[0], - bias_layout.size.spatial[0] / static_cast(groups), + bias_layout.size.feature[0] / static_cast(groups), + bias_layout.size.spatial[0], bias_layout.size.spatial[1]})) .FlattenFeatureAndSpatials()); } diff --git a/inference-engine/thirdparty/clDNN/src/include/layout_optimizer.h b/inference-engine/thirdparty/clDNN/src/include/layout_optimizer.h index 27f8ab5bafc65d..b381e68eb60676 100644 --- a/inference-engine/thirdparty/clDNN/src/include/layout_optimizer.h +++ b/inference-engine/thirdparty/clDNN/src/include/layout_optimizer.h @@ -53,20 +53,51 @@ class primitive_inst; // it's programmers responsiblity to choose between 'get_reorder', which creates reorder to best format // for given primitive (or nullptr if it's already optimal) and user shall insert it into it's own topology. // (note: layout_optimizer has internal caching mechanism, so if there's already reorder added for given (mem,format) -// pair during 'get_reorder' call, it will be reused); -// or 'add_weights_for_optimization' which, beside creating the reorder, adds both primitives (data and reorder) to its -// internal network which allows later to call 'optimize' and get already reordered data to be exchanged in target -// topology. +// pair during 'get_reorder' call, it will be reused). + +class reorder_factory { +public: + // pair.first is reorder (may be nullptr if reorder is not needed), pair.second tells if returned reorder was cached + // (no need to add it to 'ouputs' etc.) for pair.first == nullptr, pair.second == true + std::pair, bool> get_reorder(primitive_id src_id, layout in_layout, layout out_layout); + + std::vector, bool>> get_weights_reorder( + primitive_id input_id, + const layout& old_layout, + const kernel_selector::weights_reorder_params& reorder_params); + +private: + struct cache_key { + primitive_id data_source; + layout expected_layout; + + friend bool operator==(cache_key const& lhs, cache_key const& rhs) { + return lhs.data_source == rhs.data_source && lhs.expected_layout == rhs.expected_layout; + } + + friend bool operator!=(cache_key const& lhs, cache_key const& rhs) { return !(lhs == rhs); } + + friend bool operator<(cache_key const& lhs, cache_key const& rhs) { + if (lhs.data_source != rhs.data_source) + return (lhs.data_source < rhs.data_source); + return lhs.expected_layout < rhs.expected_layout; + } + }; + + std::map> _cached_reorders; + std::map> _cached_generic_reorders; +}; + class layout_optimizer { public: - enum class data_type { weights, bias, input }; enum class optimization_attributes_type { splitted_convolution, group_convolution, deformable_convolution, bfyx_only_layer, only_fsv32_layers, - bfyx_f16_network + bfyx_f16_network, + bfzyx_f16_network }; struct optimization_attributes { @@ -76,6 +107,7 @@ class layout_optimizer { int32_t bfyx_only_layer = 0; int32_t only_fsv32_layers = 0; int32_t bfyx_f16_network = 0; + int32_t bfzyx_f16_network = 0; }; private: @@ -83,142 +115,79 @@ class layout_optimizer { // TODO: Remove once we will get full support for input/output padding in all primitive implementations. bool _output_size_handling_enabled; - struct cache_key { - primitive_id data_source; - layout expected_layout; - - friend bool operator==(cache_key const& lhs, cache_key const& rhs) { - return lhs.data_source == rhs.data_source && lhs.expected_layout == rhs.expected_layout; - } - - friend bool operator!=(cache_key const& lhs, cache_key const& rhs) { return !(lhs == rhs); } - - friend bool operator<(cache_key const& lhs, cache_key const& rhs) { - if (lhs.data_source != rhs.data_source) - return (lhs.data_source < rhs.data_source); - return lhs.expected_layout < rhs.expected_layout; - } - }; - - std::map> _cached_reorders; - std::map> _cached_generic_layers; - layout get_expected_layout(layout const& current_layout, - data_type type, convolution_node const& node, layout const& output_or_weights_layout); layout get_expected_layout(layout const& current_layout, - data_type type, deconvolution_node const& node, layout const& output_or_weights_layout); layout get_expected_layout(layout const& current_layout, - data_type type, - fully_connected_node const& node, - layout const& output_or_weights_layout); - layout get_expected_layout(layout const& current_layout, - data_type type, detection_output_node const& node, layout const& output_or_weights_layout); layout get_expected_layout(layout const& current_layout, - data_type type, - embed_node const& node, - layout const& output_or_weights_layout); - layout get_expected_layout(layout const& current_layout, - data_type type, - lstm_gemm_node const& node, - layout const& output_or_weights_layout); - layout get_expected_layout(layout const& current_layout, - data_type type, binary_convolution_node const& node, layout const& output_or_weights_layout); bool convolution_bfyx_opt(const layout& output_layout, const layout& weights_layout, std::shared_ptr conv); - bool convolution_byxf_opt(const layout& output_layout, + bool convolution_byxf_opt(const layout& input_layout, + const layout& output_layout, const layout& weights_layout, std::shared_ptr conv); bool convolution_bfyx_f16_opt(const layout& output_layout, const layout& weights_layout, std::shared_ptr conv); + bool convolution_bfzyx_f16_opt(const layout& output_layout, + const layout& weights_layout, + std::shared_ptr conv); + bool deconvolution_bfzyx_f16_opt(const layout& output_layout, + const layout& weights_layout, + std::shared_ptr conv); bool users_for_convolution_byxf_opt(program_node const& node, uint32_t depth); - bool deps_depth_in_same_format(program_node const& node, const cldnn::format format, uint32_t depth); - - // pair.first is reorder (may be nullptr if reorder is not needed), pair.second tells if returned reorder was cached - // (no need to add it to 'ouputs' etc.) for pair.first == nullptr, pair.second == true - std::pair, bool> create_reorder_if_needed(const layout& current_layout, - const cldnn::primitive_id& memid, - layout const& expected_layout); - - std::pair, bool> create_reorder_from_given_source( - const cldnn::primitive_id& memid, - layout const& expected_layout, - const kernel_selector::weights_reorder_params& reorder_params); + bool deps_for_convolution_byxf_opt(program_node const& node, uint32_t depth); public: explicit layout_optimizer(bool output_size_handling_enabled = true); - // this method creates reorder for data, which is currently in 'data_layout' format, to best format in context of - // 'user' primitive. data is used by 'user' in a way described by 'type' (i.e. weights/bias/input). id shall be - // primitive_id of data's source (used as reorder's input and for cache checks). user_layout is optional parameter - // (required for weights and bias, optional for input) which tells what kind of output 'user' - // is supposed to compute - it's used for example to decide if weights shall be converted to fp16. - // - // if 'data_layout' is already optimal, nullptr is returned - // currently optimizations are supported only for convolution and fully-connected. - // - // returns a pair - where pair.first is a pointer to the reorder primitive and pair.second tells if - // it's been reused from cache, pair.second == false means this is a newly created primitive and probably needs to be - // added to topology etc. - template - auto get_reorder(layout const& data_layout, - primitive_id const& id, - data_type type, - T& node, - layout const& user_layout) -> - typename std::enable_if::value, - meta::deduce_ret_type_t>::type { - auto expected_layout = get_expected_layout(data_layout, type, node, user_layout); - return create_reorder_if_needed(data_layout, id, expected_layout); - } + format get_preferred_format(program_node& node) { + format expected = format::any; + auto output_layout = node.get_output_layout(); + + if (node.is_type()) { + auto& conv_node = node.as(); + auto weights_layout = conv_node.weights(0).get_output_layout(); + expected = get_expected_layout(output_layout, conv_node, weights_layout).format; + } else if (node.is_type()) { + auto& bconv_node = node.as(); + auto weights_layout = bconv_node.weights(0).get_output_layout(); + expected = get_expected_layout(output_layout, bconv_node, weights_layout).format; + } else if (node.is_type()) { + expected = get_expected_layout( + output_layout, + node.as(), + layout{ data_types::f32, format::bfyx, tensor{} }).format; + } else if (node.is_type() || node.is_type()) { + expected = node.get_output_layout().format; + } else if (node.is_type()) { + auto& deconv_node = node.as(); + auto weights_layout = deconv_node.weights(0).get_output_layout(); + expected = get_expected_layout(output_layout, deconv_node, weights_layout).format; + } - // case for unsupported 'user' primitives - template - auto get_reorder(layout const& data_layout, - primitive_id const& id, - data_type type, - T& node, - layout const& user_layout) -> - typename std::enable_if::value, - meta::deduce_ret_type_t>::type { - static_assert(meta::always_false::value, - "Layout optimization for given primitive type is currently unsupported!"); - return meta::deduce_ret_type_t(); + return expected; } - std::vector, bool>> get_generic_layer( - const kernel_selector::weights_reorder_params& reorder_params, - primitive_id input_id, - const layout& old_layout, - data_type type); + bool is_format_supported(program_node& node, format::type fmt); + + // Returns whether reorder between "prev" with format fmt_prev and "next" with format fmt_next + // can be fused into next. + bool can_fuse_reorder(program_node& prev, program_node& next, format fmt_prev, format fmt_next); void set_optimization_attribute(optimization_attributes_type attribute, int32_t val); optimization_attributes get_optimization_attributes() { return _optimization_attributes; } bool is_format_optimized(const convolution_node& node, const format& format); + bool is_format_optimized(const deconvolution_node& node, const format& format); }; } // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/src/include/lookup_table_inst.h b/inference-engine/thirdparty/clDNN/src/include/lookup_table_inst.h index 486fa3071fbcdd..6e07af20ae0f0a 100644 --- a/inference-engine/thirdparty/clDNN/src/include/lookup_table_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/lookup_table_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/lookup_table.hpp" +#include "api/lookup_table.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/lrn_inst.h b/inference-engine/thirdparty/clDNN/src/include/lrn_inst.h index 35e082b3af0c49..f43315535e1c56 100644 --- a/inference-engine/thirdparty/clDNN/src/include/lrn_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/lrn_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/lrn.hpp" +#include "api/lrn.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/lstm_dynamic_input_inst.h b/inference-engine/thirdparty/clDNN/src/include/lstm_dynamic_input_inst.h index 4ed70cc98956ff..6b8cddb1f012b2 100644 --- a/inference-engine/thirdparty/clDNN/src/include/lstm_dynamic_input_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/lstm_dynamic_input_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api_extension/CPP/lstm_dynamic_input.hpp" +#include "api_extension/lstm_dynamic_input.hpp" #include "primitive_inst.h" #include "error_handler.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/lstm_dynamic_inst.h b/inference-engine/thirdparty/clDNN/src/include/lstm_dynamic_inst.h index 9c2445f95b33a0..fac24bcbcb5ee1 100644 --- a/inference-engine/thirdparty/clDNN/src/include/lstm_dynamic_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/lstm_dynamic_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/lstm_dynamic.hpp" +#include "api/lstm_dynamic.hpp" #include "primitive_inst.h" #include "error_handler.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/lstm_dynamic_timeloop_inst.h b/inference-engine/thirdparty/clDNN/src/include/lstm_dynamic_timeloop_inst.h index 22a4ce6b3dac09..f5d13e3090c799 100644 --- a/inference-engine/thirdparty/clDNN/src/include/lstm_dynamic_timeloop_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/lstm_dynamic_timeloop_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api_extension/CPP/lstm_dynamic_timeloop.hpp" +#include "api_extension/lstm_dynamic_timeloop.hpp" #include "primitive_inst.h" #include "error_handler.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/lstm_elt_inst.h b/inference-engine/thirdparty/clDNN/src/include/lstm_elt_inst.h index 20aed4649ac106..5be00727c3612a 100644 --- a/inference-engine/thirdparty/clDNN/src/include/lstm_elt_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/lstm_elt_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/lstm.hpp" +#include "api/lstm.hpp" #include "primitive_inst.h" #include @@ -31,7 +31,7 @@ struct typed_program_node : public typed_program_node_base { program_node& input() const { return get_dependency(0); } program_node& cell() const { return get_dependency(1); } bool cell_term() const { return !get_primitive()->cell.empty(); } - int32_t offset_order() const { return get_primitive()->offset_order; } + lstm_weights_order offset_order() const { return get_primitive()->offset_order; } float clip() const { float clip_val = get_primitive()->clip; if (clip_val < 0) @@ -57,7 +57,7 @@ class typed_primitive_inst : public typed_primitive_inst_base diff --git a/inference-engine/thirdparty/clDNN/src/include/lstm_inst.h b/inference-engine/thirdparty/clDNN/src/include/lstm_inst.h index 36a4bf4a40d746..95f9f348b35950 100644 --- a/inference-engine/thirdparty/clDNN/src/include/lstm_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/lstm_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/lstm.hpp" +#include "api/lstm.hpp" #include "primitive_inst.h" #include #include @@ -43,8 +43,8 @@ struct typed_program_node : public typed_program_node_base { bool peepholes_term() const { return !get_primitive()->peepholes.empty(); } bool initial_hidden_term() const { return !get_primitive()->initial_hidden.empty(); } bool initial_cell_term() const { return !get_primitive()->initial_cell.empty(); } - std::vector activations() const { return get_primitive()->activations; } - std::vector activation_params() const { + std::vector activations() const { return get_primitive()->activations; } + std::vector activation_params() const { return get_primitive()->activation_params; } size_t sequence_len() const { return get_primitive()->input.size(); } @@ -75,8 +75,8 @@ class typed_primitive_inst : public typed_primitive_inst_base { bool peepholes_term() const { return !argument.peepholes.empty(); } bool initial_hidden_term() const { return !argument.initial_hidden.empty(); } bool initial_cell_term() const { return !argument.initial_cell.empty(); } - std::vector activations() const { return argument.activations; } - std::vector activation_params() const { return argument.activation_params; } + std::vector activations() const { return argument.activations; } + std::vector activation_params() const { return argument.activation_params; } }; using lstm_inst = typed_primitive_inst; diff --git a/inference-engine/thirdparty/clDNN/src/include/max_unpooling_inst.h b/inference-engine/thirdparty/clDNN/src/include/max_unpooling_inst.h index 5b34910d232521..4e06c0dd0c0274 100644 --- a/inference-engine/thirdparty/clDNN/src/include/max_unpooling_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/max_unpooling_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/max_unpooling.hpp" +#include "api/max_unpooling.hpp" #include "primitive_inst.h" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/memory_impl.h b/inference-engine/thirdparty/clDNN/src/include/memory_impl.h index e2020016611f64..5ff6499ff42eb6 100644 --- a/inference-engine/thirdparty/clDNN/src/include/memory_impl.h +++ b/inference-engine/thirdparty/clDNN/src/include/memory_impl.h @@ -16,27 +16,26 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/memory.hpp" +#include "api/memory.hpp" -#include "api_impl.h" #include "engine_impl.h" #include "refcounted_obj.h" namespace cldnn { struct memory_impl : refcounted_obj { - memory_impl(const engine_impl::ptr& engine, layout layout, uint16_t stream_id, bool reused = false) - : _engine(engine), _layout(layout), _stream_id(stream_id), _reused(reused) {} + memory_impl(const engine_impl::ptr& engine, const layout& layout, uint16_t stream_id, bool reused = false) + : _engine(engine), _layout(layout), _stream_id(stream_id), _reused(reused), _bytes_count(_layout.bytes_count()) {} virtual ~memory_impl() { if (_engine != (engine_impl::ptr) nullptr && !_reused) { - _engine->get_memory_pool().subtract_memory_used(_layout.bytes_count()); + _engine->get_memory_pool().subtract_memory_used(_bytes_count); } } virtual void* lock() = 0; virtual void unlock() = 0; virtual void fill(unsigned char pattern, event_impl::ptr ev) = 0; - size_t size() const { return _layout.bytes_count(); } + size_t size() const { return _bytes_count; } virtual bool is_allocated_by(const engine_impl& engine) const { return &engine == _engine.get(); } const refcounted_obj_ptr& get_engine() const { return _engine; } const layout& get_layout() const { return _layout; } @@ -49,10 +48,13 @@ struct memory_impl : refcounted_obj { private: bool _reused; + // layout bytes count, needed because of traits static map destruction + // before run of memory_impl destructor, when engine is static + size_t _bytes_count; }; struct simple_attached_memory : memory_impl { - simple_attached_memory(layout layout, void* pointer, uint16_t stream_id) + simple_attached_memory(const layout& layout, void* pointer, uint16_t stream_id) : memory_impl((engine_impl::ptr) nullptr, layout, stream_id), _pointer(pointer) {} void* lock() override { return _pointer; } @@ -92,5 +94,3 @@ struct mem_lock { }; } // namespace cldnn - -API_CAST(::cldnn_memory, cldnn::memory_impl) diff --git a/inference-engine/thirdparty/clDNN/src/include/memory_pool.h b/inference-engine/thirdparty/clDNN/src/include/memory_pool.h index 890a5a5ceddc7a..d915b378046dae 100644 --- a/inference-engine/thirdparty/clDNN/src/include/memory_pool.h +++ b/inference-engine/thirdparty/clDNN/src/include/memory_pool.h @@ -16,9 +16,8 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/layout.hpp" -#include "api/CPP/primitive.hpp" -#include "api_impl.h" +#include "api/layout.hpp" +#include "api/primitive.hpp" #include "refcounted_obj.h" diff --git a/inference-engine/thirdparty/clDNN/src/include/meta_utils.h b/inference-engine/thirdparty/clDNN/src/include/meta_utils.h index 3eb0efdff720f5..8de3f6fd2ce649 100644 --- a/inference-engine/thirdparty/clDNN/src/include/meta_utils.h +++ b/inference-engine/thirdparty/clDNN/src/include/meta_utils.h @@ -16,7 +16,7 @@ #pragma once #include -#include "api/CPP/meta_utils.hpp" +#include "api/meta_utils.hpp" #include "internal_primitive.h" namespace cldnn { diff --git a/inference-engine/thirdparty/clDNN/src/include/mutable_data_inst.h b/inference-engine/thirdparty/clDNN/src/include/mutable_data_inst.h index 46f1f939d8181f..6b57b1a8dd408b 100644 --- a/inference-engine/thirdparty/clDNN/src/include/mutable_data_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/mutable_data_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/mutable_data.hpp" +#include "api/mutable_data.hpp" #include "primitive_inst.h" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/mvn_inst.h b/inference-engine/thirdparty/clDNN/src/include/mvn_inst.h index c197f565e325ad..6c8ec374763211 100644 --- a/inference-engine/thirdparty/clDNN/src/include/mvn_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/mvn_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/mvn.hpp" +#include "api/mvn.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/network_impl.h b/inference-engine/thirdparty/clDNN/src/include/network_impl.h index 4206431474b604..0b99503bed1ad8 100644 --- a/inference-engine/thirdparty/clDNN/src/include/network_impl.h +++ b/inference-engine/thirdparty/clDNN/src/include/network_impl.h @@ -17,9 +17,8 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/network.hpp" +#include "api/network.hpp" -#include "api_impl.h" #include "engine_impl.h" #include "event_impl.h" #include "program_impl.h" @@ -111,5 +110,3 @@ struct network_impl : public refcounted_obj { void check_names(); }; } // namespace cldnn - -API_CAST(::cldnn_network, cldnn::network_impl) diff --git a/inference-engine/thirdparty/clDNN/src/include/normalize_inst.h b/inference-engine/thirdparty/clDNN/src/include/normalize_inst.h index 6135b42749154e..4e64f4b2b0177d 100644 --- a/inference-engine/thirdparty/clDNN/src/include/normalize_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/normalize_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/normalize.hpp" +#include "api/normalize.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/one_hot_inst.h b/inference-engine/thirdparty/clDNN/src/include/one_hot_inst.h index 9c4daed2abecec..49fc93d1824a21 100644 --- a/inference-engine/thirdparty/clDNN/src/include/one_hot_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/one_hot_inst.h @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include +#include #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/pass_manager.h b/inference-engine/thirdparty/clDNN/src/include/pass_manager.h index e404ec9b0a0068..2bdf5f1d6a63fd 100644 --- a/inference-engine/thirdparty/clDNN/src/include/pass_manager.h +++ b/inference-engine/thirdparty/clDNN/src/include/pass_manager.h @@ -162,14 +162,13 @@ class prepare_buffer_fusing : public base_pass { void run(program_impl& p) override; }; -class prepare_binarization : public base_pass { +class prepare_quantization : public base_pass { public: - prepare_binarization() : base_pass("prepare_binarization") {} + prepare_quantization() : base_pass("prepare_quantization") {} private: void run(program_impl& p) override; - void prepare_packed_quantize(program_impl& p, program_node& node); - void prepare_fusing(program_impl& p, program_node& node); + void prepare_packed_quantize(program_impl& p); }; class prepare_conv_eltw_fusing : public base_pass { @@ -220,21 +219,23 @@ class prepare_primitive_fusing : public base_pass { private: void run(program_impl& p) override; - void fuse_skip_layers(program_impl& p, program_node* node); - void fuse_conv_bn_scale(program_impl& p, program_node* node); + void fuse_reorders(program_impl& p); + void fuse_activations(program_impl& p); + void fuse_skip_layers(program_impl& p); + void fuse_simple_primitives(program_impl &p); layout_optimizer& _lo; }; class pre_optimize_bias : public base_pass { public: - explicit pre_optimize_bias(layout_optimizer& lo_ref); + explicit pre_optimize_bias(reorder_factory& rf_ref); private: void run(program_impl& p) override; - virtual void run(program_impl& p, layout_optimizer& lo); + virtual void run(program_impl& p, reorder_factory& rf); template - void optimize_bias(T& node, layout_optimizer& lo, program_impl& p); - layout_optimizer& _lo; + void optimize_bias(T& node, reorder_factory& rf, program_impl& p); + reorder_factory& _rf; }; class prepare_padding : public base_pass { @@ -258,14 +259,26 @@ class post_input_reorder : public base_pass { class post_optimize_weights : public base_pass { public: - explicit post_optimize_weights(layout_optimizer& lo_ref); + explicit post_optimize_weights(reorder_factory& rf_ref); private: + struct weights_bias_offset { + size_t weights_offset; + size_t bias_offset; + + // When using this ctor weights offset is added to the bias_offset + weights_bias_offset(const size_t w_offset, const size_t b_offset) + : weights_offset(w_offset) + , bias_offset(weights_offset + b_offset) + {} + }; + void run(program_impl& p) override; - virtual void run(program_impl& p, layout_optimizer& lo); - template - void optimize_weights(T& node, layout_optimizer& lo, program_impl& p); - layout_optimizer& _lo; + template + weights_bias_offset get_weights_bias_offset(const T& node); + template + void optimize_weights(T& node, program_impl& p); + reorder_factory& _rf; }; class propagate_constants : public base_pass { @@ -288,21 +301,24 @@ class propagate_constants : public base_pass { class remove_redundant_reorders : public base_pass { public: - explicit remove_redundant_reorders(bool bfyx_to_bfyx_f16_opt = false); + explicit remove_redundant_reorders(layout_optimizer& lo_ref, bool enable_reorder_fusing = false, bool update_implementations = false); void run(program_impl& p) override; private: - bool bfyx_to_bfyx_f16_opt; + layout_optimizer& lo; + bool enable_reorder_fusing; + bool update_implementations; }; class reorder_inputs : public base_pass { public: - explicit reorder_inputs(layout_optimizer& lo_ref); + reorder_inputs(layout_optimizer& lo_ref, reorder_factory& rf_ref); private: void run(program_impl& p) override; - virtual void run(program_impl& p, layout_optimizer& lo); + virtual void run(program_impl& p, layout_optimizer& lo, reorder_factory& rf); layout_optimizer& _lo; + reorder_factory& _rf; }; class trim_to_outputs : public base_pass { diff --git a/inference-engine/thirdparty/clDNN/src/include/permute_inst.h b/inference-engine/thirdparty/clDNN/src/include/permute_inst.h index 064f4d9f03198b..8ad0b371b835f0 100644 --- a/inference-engine/thirdparty/clDNN/src/include/permute_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/permute_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/permute.hpp" +#include "api/permute.hpp" #include "primitive_inst.h" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/pooling_inst.h b/inference-engine/thirdparty/clDNN/src/include/pooling_inst.h index 7796978f99e35f..39fb338a039f7d 100644 --- a/inference-engine/thirdparty/clDNN/src/include/pooling_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/pooling_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/pooling.hpp" +#include "api/pooling.hpp" #include "primitive_inst.h" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/primitive_inst.h b/inference-engine/thirdparty/clDNN/src/include/primitive_inst.h index d91a9104dafff6..2e518520819457 100644 --- a/inference-engine/thirdparty/clDNN/src/include/primitive_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/primitive_inst.h @@ -17,8 +17,8 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/primitive.hpp" -#include "api/CPP/concatenation.hpp" +#include "api/primitive.hpp" +#include "api/concatenation.hpp" #include "event_impl.h" #include "memory_impl.h" @@ -86,7 +86,7 @@ class primitive_inst { memory_impl& dep_memory(size_t index) const { return dependencies().at(index)->output_memory(); } memory_impl& output_memory() const { return *_output; } - size_t inputs_memory_count() const { return _node.get_primitive()->input.size(); } + size_t inputs_memory_count() const { return _node.get_primitive()->input_size(); } primitive_type_id type() const { return _node.type(); } primitive_id id() const { return _node.id(); } primitive_id org_id() const { return _node.get_org_primitive_id(); } @@ -115,6 +115,14 @@ class primitive_inst { void build_deps(); + memory_impl& fused_memory(size_t dep_id) const { + return dep_memory(get_fused_mem_offset() + dep_id); + } + + bool has_fused_primitives() const { return !_node.get_fused_primitives().empty(); } + size_t get_fused_mem_count() const { return _node.get_fused_inputs_count(); } + size_t get_fused_mem_offset() const { return _node.get_fused_primitives()[0].dep_start_idx; } + protected: primitive_inst(network_impl& network, program_node const& node, bool allocate_memory); diff --git a/inference-engine/thirdparty/clDNN/src/include/primitive_type.h b/inference-engine/thirdparty/clDNN/src/include/primitive_type.h index 3089c5cda504c6..072b157f0ff736 100644 --- a/inference-engine/thirdparty/clDNN/src/include/primitive_type.h +++ b/inference-engine/thirdparty/clDNN/src/include/primitive_type.h @@ -16,9 +16,9 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/memory.hpp" -#include "api/CPP/primitive.hpp" -#include "api/CPP/program.hpp" +#include "api/memory.hpp" +#include "api/primitive.hpp" +#include "api/program.hpp" #include "topology_impl.h" @@ -32,22 +32,22 @@ struct program_node; struct primitive_impl; class primitive_inst; struct program_impl; -} // namespace cldnn -struct cldnn_primitive_type { - virtual ~cldnn_primitive_type() = default; - virtual std::shared_ptr from_dto(const CLDNN_PRIMITIVE_DESC(primitive) * dto) const = 0; - virtual std::shared_ptr create_node(cldnn::program_impl& program, - const std::shared_ptr prim) const = 0; - virtual std::shared_ptr create_instance(cldnn::network_impl& network, - const cldnn::program_node& node) const = 0; - virtual std::unique_ptr choose_impl(cldnn::engine_impl& engine, - const cldnn::program_node& node) const = 0; - virtual bool does_an_implementation_exist(cldnn::engine_impl& engine, const cldnn::program_node& node) const = 0; - virtual bool does_possible_implementation_exist(cldnn::engine_impl& engine, - const cldnn::program_node& node) const = 0; - virtual cldnn::layout calc_output_layout(const cldnn::program_node& node) const = 0; - virtual std::string to_string(const cldnn::program_node& node) const = 0; +struct primitive_type { + virtual ~primitive_type() = default; + + virtual std::shared_ptr create_node(program_impl& program, + const std::shared_ptr prim) const = 0; + virtual std::shared_ptr create_instance(network_impl& network, + const program_node& node) const = 0; + virtual std::unique_ptr choose_impl(engine_impl& engine, + const program_node& node) const = 0; + virtual bool does_an_implementation_exist(engine_impl& engine, const program_node& node) const = 0; + virtual bool does_possible_implementation_exist(engine_impl& engine, + const program_node& node) const = 0; + virtual layout calc_output_layout(const program_node& node) const = 0; + virtual std::string to_string(const program_node& node) const = 0; virtual bool is_internal_type() const { return false; } }; +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/src/include/primitive_type_base.h b/inference-engine/thirdparty/clDNN/src/include/primitive_type_base.h index d7e464d7c18a68..0b9033a7755b57 100644 --- a/inference-engine/thirdparty/clDNN/src/include/primitive_type_base.h +++ b/inference-engine/thirdparty/clDNN/src/include/primitive_type_base.h @@ -27,17 +27,10 @@ namespace cldnn { template -struct primitive_type_base : ::cldnn_primitive_type { +struct primitive_type_base : primitive_type { static_assert(meta::is_api_primitive::value, "Primitive type passed to primitive_type_base should derive from cldnn::primitive"); - std::shared_ptr from_dto(const CLDNN_PRIMITIVE_DESC(primitive) * dto) const override { - if (dto->type != this) - throw std::invalid_argument("primitive_type_base::from_dto: primitive type mismatch"); - - return std::make_shared(as_dto(dto)); - } - std::shared_ptr create_node(program_impl& program, const std::shared_ptr prim) const override { if (prim->type != this) diff --git a/inference-engine/thirdparty/clDNN/src/include/prior_box_inst.h b/inference-engine/thirdparty/clDNN/src/include/prior_box_inst.h index 9adf088a80af79..d77b11d70beff4 100644 --- a/inference-engine/thirdparty/clDNN/src/include/prior_box_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/prior_box_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/prior_box.hpp" +#include "api/prior_box.hpp" #include "primitive_inst.h" #include #include @@ -32,7 +32,7 @@ struct typed_program_node : typed_program_node_base { program_node& input() const { return get_dependency(0); } void calc_result(); - memory_impl& get_result_buffer() const { return *result; } + memory_impl::ptr get_result_buffer() const { return result; } private: memory_impl::ptr result; diff --git a/inference-engine/thirdparty/clDNN/src/include/program_helpers.h b/inference-engine/thirdparty/clDNN/src/include/program_helpers.h index ac38579aa4c993..92775139f3b4c0 100644 --- a/inference-engine/thirdparty/clDNN/src/include/program_helpers.h +++ b/inference-engine/thirdparty/clDNN/src/include/program_helpers.h @@ -65,8 +65,14 @@ struct program_helpers { // helper function which creates single-element array if it's given anything // other than std::vector. - // std::vector case -> does not wrap, returns t as-is - static const primitive::fixed_size_vector_ref& wrap_if_single(primitive::fixed_size_vector_ref const& t) { + // std::vector case -> does not wrap + template + static std::vector& wrap_if_single(std::vector& t) { + return t; + } + + template + static const std::vector& wrap_if_single(const std::vector& t) { return t; } @@ -103,10 +109,10 @@ struct program_helpers { } static void merge_buffers(engine_impl& engine, program_node& node, - layout target_layout, + const layout& target_layout, size_t begin_offset, size_t end_offset); static layout get_weights_layout(typed_program_node& data_node, int32_t split); static std::pair are_layouts_identical(layout const& l1, layout const& l2); }; -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/src/include/program_impl.h b/inference-engine/thirdparty/clDNN/src/include/program_impl.h index 9af2a2aa8c6cc2..e19547d96e00ab 100644 --- a/inference-engine/thirdparty/clDNN/src/include/program_impl.h +++ b/inference-engine/thirdparty/clDNN/src/include/program_impl.h @@ -18,7 +18,7 @@ #pragma once -#include "api/CPP/program.hpp" +#include "api/program.hpp" #include "refcounted_obj.h" #include "engine_impl.h" @@ -49,7 +49,7 @@ struct program_impl : public refcounted_obj { friend class prepare_padding; // to be removed when possible friend class propagate_constants; // to be removed when possible friend class prepare_primitive_fusing; // to be removed when possible - friend class prepare_binarization; // to be removed when possible + friend class prepare_quantization; // to be removed when possible friend class prepare_conv_eltw_fusing; // to be removed when possible friend class reorder_inputs; // to be removed when possible friend class remove_redundant_reorders; // to be removed when possible @@ -60,9 +60,13 @@ struct program_impl : public refcounted_obj { public: typedef std::list list_of_nodes; typedef list_of_nodes::const_iterator const_iterator; + typedef list_of_nodes::const_reverse_iterator const_reverse_iterator; typedef list_of_nodes::iterator node_iterator; + typedef list_of_nodes::reverse_iterator node_reverse_iterator; const_iterator begin() const { return _processing_order.begin(); } const_iterator end() const { return _processing_order.end(); } + const_reverse_iterator rbegin() const { return _processing_order.rbegin(); } + const_reverse_iterator rend() const { return _processing_order.rend(); } void calc_processing_order_visit(program_node* node); void calc_processing_order(program_impl& p); @@ -191,6 +195,9 @@ struct program_impl : public refcounted_obj { // returns if 'node' has been extracted and removed successfully bool extract_and_remove(program_node& node); + // Fuses two nodes into fused_node and removes peer_node from graph + void fuse_nodes(program_node& fused_node, program_node& peer_node); + // returns if 'node' has been removed bool remove_if_dangling(program_node& node); @@ -242,6 +249,7 @@ struct program_impl : public refcounted_obj { void build_program(bool is_internal); void init_graph(); void set_options(); + void set_layout_optimizer_attributes(layout_optimizer& lo); void apply_opt_pass(base_pass& pass); @@ -301,5 +309,3 @@ struct program_impl : public refcounted_obj { }; } // namespace cldnn - -API_CAST(::cldnn_program, cldnn::program_impl) diff --git a/inference-engine/thirdparty/clDNN/src/include/program_node.h b/inference-engine/thirdparty/clDNN/src/include/program_node.h index f87dfa0069763c..43cbb770d930f5 100644 --- a/inference-engine/thirdparty/clDNN/src/include/program_node.h +++ b/inference-engine/thirdparty/clDNN/src/include/program_node.h @@ -18,7 +18,8 @@ #include #include -#include "api/CPP/primitive.hpp" +#include "api/primitive.hpp" +#include "api/activation.hpp" #include "internal_primitive.h" #include "meta_utils.h" @@ -32,6 +33,7 @@ namespace cldnn { struct program_impl; class reorder_inputs; class graph_initializations; +class prepare_quantization; template struct typed_program_node; @@ -42,6 +44,16 @@ struct internal_primitive_type_base; class json_composite; class xml_composite; + +struct fused_primitive_desc { + std::shared_ptr prim; + size_t dep_start_idx; + std::vector deps; + activation_func activation; + activation_additional_params activation_params; + layout output_layout = layout(data_types::f32, format::bfyx, tensor()); +}; + /* Base class for all primitives which wraps API class and extends it to be used in graph context. @@ -58,7 +70,7 @@ struct program_node { friend class compile_graph; // to be removed when possible friend class graph_initializations; // to be removed when possible friend class prepare_primitive_fusing; // to be removed when possible - friend class prepare_binarization; // to be removed when possible + friend class prepare_quantization; // to be removed when possible friend class prepare_conv_eltw_fusing; // to be removed when possible friend class prepare_conv_eltw_read_write_opt; // to be removed when possible friend class propagate_constants; // to be removed when possible @@ -153,7 +165,7 @@ struct program_node { // sets cached output layout to an arbitrary value, invalidates users if new layout differs from previous one and @p // invalidate_users_if_changed is set to true returns whether output layout has changed - bool set_output_layout(layout new_layout, bool invalidate_users_if_changed = true); + bool set_output_layout(layout& new_layout, bool invalidate_users_if_changed = true); // forces recalculation of cached output layout, invalidates users if new layout is different than previous one and // @p invalidate_users_if_changed is set to true returns whether output layout has changed @@ -182,16 +194,31 @@ struct program_node { bool is_marked(uint8_t val) const { return user_mark == val; } uint8_t get_user_mark() const { return user_mark; } - void set_fused_activation(cldnn_activation_func activation_func, - cldnn_activation_additional_params additional_params) { - fused_activation.activation_func = activation_func; - fused_activation.additional_params = additional_params; + void add_fused_activation(activation_func activation_func, + activation_additional_params additional_params) { + fused_activations.emplace_back(activation_func, additional_params); } - cldnn_activation_func get_fused_activation_func() const { return fused_activation.activation_func; } + std::vector get_fused_activations_funcs() const { + std::vector funcs; + std::transform(fused_activations.begin(), + fused_activations.end(), + std::back_inserter(funcs), + [](fused_activation_params const& p) { return p.func; }); + return funcs; + } - cldnn_activation_additional_params get_fused_activation_params() const { - return fused_activation.additional_params; + std::vector get_fused_activations_params() const { + std::vector params; + std::transform(fused_activations.begin(), + fused_activations.end(), + std::back_inserter(params), + [](fused_activation_params const& p) { return p.params; }); + return params; + } + + void copy_fused_activation(const program_node& rhs) { + fused_activations = rhs.fused_activations; } // check/set if the node can be optimized out (removed from the network) @@ -257,6 +284,33 @@ struct program_node { return reused_memory_color; } + virtual void add_fused_primitive(fused_primitive_desc& desc) { + fused_prims.push_back(desc); + } + + virtual void add_fused_primitives(std::vector descs) { + fused_prims.insert(fused_prims.end(), descs.begin(), descs.end()); + } + + const std::vector& get_fused_primitives() const { return fused_prims; } + + size_t get_fused_inputs_count() const { + size_t count = 0; + for (auto& fp : get_fused_primitives()) { + count += fp.deps.size(); + } + return count; + } + + bool has_fused_primitives() const { return !get_fused_primitives().empty(); } + + layout get_fused_output_layout() const { + auto fused_prims = get_fused_primitives(); + if (fused_prims.empty()) + return layout(data_types::f32, format::bfyx, tensor()); + return fused_prims.back().output_layout; + } + protected: std::shared_ptr desc; program_impl& myprog; @@ -279,7 +333,7 @@ struct program_node { uint8_t user_mark = 0; bool optimized = false; bool share_buffer = true; - std::array _support_padding_in_axis = {}; // zero-initialization + std::array _support_padding_in_axis = {}; // zero-initialization mutable bool has_reused_memory = false; mutable uint32_t reused_memory_color = 0; @@ -287,12 +341,18 @@ struct program_node { const primitive_id org_id; struct fused_activation_params { - cldnn_activation_func activation_func = activation_none; - cldnn_activation_additional_params additional_params = {0.0f, 0.0f}; - }; + activation_func func = activation_func::none; + activation_additional_params params = {0.0f, 0.0f}; - fused_activation_params fused_activation; + fused_activation_params() {} + + fused_activation_params(activation_func _func, activation_additional_params _params) : + func(_func), + params(_params) {} + }; + std::vector fused_activations; + std::vector fused_prims; void invalidate_users() const; }; @@ -303,6 +363,7 @@ struct api_typed_program_node_base : public program_node { "PType should name a non-const, non-volatile type derived from cldnn::primitive but not from " "cldnn::internal_primitive"); friend class cldnn::graph_initializations; + friend class cldnn::prepare_quantization; friend struct cldnn::program_impl; friend class cldnn::reorder_inputs; diff --git a/inference-engine/thirdparty/clDNN/src/include/proposal_inst.h b/inference-engine/thirdparty/clDNN/src/include/proposal_inst.h index a8c8d3b629e668..0ffb3ac7399011 100644 --- a/inference-engine/thirdparty/clDNN/src/include/proposal_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/proposal_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/proposal.hpp" +#include "api/proposal.hpp" #include "primitive_inst.h" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/pyramid_roi_align_inst.h b/inference-engine/thirdparty/clDNN/src/include/pyramid_roi_align_inst.h index 9da44c2a793e8e..66b602028f0afa 100644 --- a/inference-engine/thirdparty/clDNN/src/include/pyramid_roi_align_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/pyramid_roi_align_inst.h @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -#include "api/CPP/pyramid_roi_align.hpp" +#include "api/pyramid_roi_align.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/quantize_inst.h b/inference-engine/thirdparty/clDNN/src/include/quantize_inst.h index 75522ececc2d5f..250b6eeec14a82 100644 --- a/inference-engine/thirdparty/clDNN/src/include/quantize_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/quantize_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/quantize.hpp" +#include "api/quantize.hpp" #include "primitive_inst.h" #include @@ -31,11 +31,13 @@ struct typed_program_node : public typed_program_node_base { program_node& input(size_t index = 0) const { return get_dependency(index); } size_t inputs_count() const { return get_dependencies().size(); } - void set_packed_binary_output(bool _packed_binary_output) { packed_binary_output = _packed_binary_output; } - bool get_packed_binary_output() const { return packed_binary_output; } + void set_output_data_type(data_types dt) { out_dt = dt; dt_changed = true; } + data_types get_output_data_type() const { return out_dt; } + bool has_custom_out_dt() const { return dt_changed; } private: - bool packed_binary_output = false; + data_types out_dt; + bool dt_changed = false; }; using quantize_node = typed_program_node; diff --git a/inference-engine/thirdparty/clDNN/src/include/reduce_inst.h b/inference-engine/thirdparty/clDNN/src/include/reduce_inst.h index 7aa4ad613b081f..9963505fb36b46 100644 --- a/inference-engine/thirdparty/clDNN/src/include/reduce_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/reduce_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/reduce.hpp" +#include "api/reduce.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/region_yolo_inst.h b/inference-engine/thirdparty/clDNN/src/include/region_yolo_inst.h index ff94ccae93aae5..0a285b2f134cbe 100644 --- a/inference-engine/thirdparty/clDNN/src/include/region_yolo_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/region_yolo_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/region_yolo.hpp" +#include "api/region_yolo.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/reorder_inst.h b/inference-engine/thirdparty/clDNN/src/include/reorder_inst.h index fa5caabc383f2b..e4824d0ce57c44 100644 --- a/inference-engine/thirdparty/clDNN/src/include/reorder_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/reorder_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/reorder.hpp" +#include "api/reorder.hpp" #include "primitive_inst.h" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/reorg_yolo_inst.h b/inference-engine/thirdparty/clDNN/src/include/reorg_yolo_inst.h index 1fd9e98f8891cd..4f8528645b5991 100644 --- a/inference-engine/thirdparty/clDNN/src/include/reorg_yolo_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/reorg_yolo_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/reorg_yolo.hpp" +#include "api/reorg_yolo.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/reshape_inst.h b/inference-engine/thirdparty/clDNN/src/include/reshape_inst.h index 751276dc6fa789..2ee1e45c50ca95 100644 --- a/inference-engine/thirdparty/clDNN/src/include/reshape_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/reshape_inst.h @@ -16,8 +16,9 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/reshape.hpp" +#include "api/reshape.hpp" #include "primitive_inst.h" +#include "error_handler.h" #include #include @@ -33,10 +34,13 @@ struct typed_program_node : public typed_program_node_base { public: using parent::parent; - program_node& input() const { return get_dependency(0); } + program_node& input() const { + CLDNN_ERROR_LESS_THAN(id(), "the number of dependencies", dependencies.size(), "1", 1, "ERROR: the node has no input"); + return get_dependency(0); + } bool is_in_place() const { - if (this->is_output() || this->get_fused_activation_func() != activation_none) + if (this->is_output() || !this->get_fused_activations_funcs().empty()) return false; return (!this->get_output_layout().data_padding && !input().get_output_layout(false).data_padding); } diff --git a/inference-engine/thirdparty/clDNN/src/include/reverse_sequence_inst.h b/inference-engine/thirdparty/clDNN/src/include/reverse_sequence_inst.h index 01411468deaf32..631b5916517527 100644 --- a/inference-engine/thirdparty/clDNN/src/include/reverse_sequence_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/reverse_sequence_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/reverse_sequence.hpp" +#include "api/reverse_sequence.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/roi_pooling_inst.h b/inference-engine/thirdparty/clDNN/src/include/roi_pooling_inst.h index 70d3d7e00f6974..b323d37100aa43 100644 --- a/inference-engine/thirdparty/clDNN/src/include/roi_pooling_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/roi_pooling_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/roi_pooling.hpp" +#include "api/roi_pooling.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/scale_grad_input_inst.h b/inference-engine/thirdparty/clDNN/src/include/scale_grad_input_inst.h index 0d15828ed9893a..29815debbcd66d 100644 --- a/inference-engine/thirdparty/clDNN/src/include/scale_grad_input_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/scale_grad_input_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/scale_grad_input.hpp" +#include "api/scale_grad_input.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/scale_grad_weights_inst.h b/inference-engine/thirdparty/clDNN/src/include/scale_grad_weights_inst.h index b7f22f8f281cd2..ecef6d1d5a9f74 100644 --- a/inference-engine/thirdparty/clDNN/src/include/scale_grad_weights_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/scale_grad_weights_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/scale_grad_weights.hpp" +#include "api/scale_grad_weights.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/scale_inst.h b/inference-engine/thirdparty/clDNN/src/include/scale_inst.h index fa12b18c4f75d2..6b7f28c6ca2ce2 100644 --- a/inference-engine/thirdparty/clDNN/src/include/scale_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/scale_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/scale.hpp" +#include "api/scale.hpp" #include "primitive_inst.h" #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/select_inst.h b/inference-engine/thirdparty/clDNN/src/include/select_inst.h index 2046c8ac9d53ec..ffddcffedb0fdb 100644 --- a/inference-engine/thirdparty/clDNN/src/include/select_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/select_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include +#include #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/shuffle_channels_inst.h b/inference-engine/thirdparty/clDNN/src/include/shuffle_channels_inst.h index 0fd82fad01eb1e..168c4ec38fb06c 100644 --- a/inference-engine/thirdparty/clDNN/src/include/shuffle_channels_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/shuffle_channels_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/shuffle_channels.hpp" +#include "api/shuffle_channels.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/sliding_window_utils.h b/inference-engine/thirdparty/clDNN/src/include/sliding_window_utils.h index bb89fc2d104f67..8c93cc99f2a284 100644 --- a/inference-engine/thirdparty/clDNN/src/include/sliding_window_utils.h +++ b/inference-engine/thirdparty/clDNN/src/include/sliding_window_utils.h @@ -14,8 +14,8 @@ #pragma once -#include -#include +#include +#include #include #include diff --git a/inference-engine/thirdparty/clDNN/src/include/softmax_inst.h b/inference-engine/thirdparty/clDNN/src/include/softmax_inst.h index 1b4fc39d6367c4..1b362936d4a780 100644 --- a/inference-engine/thirdparty/clDNN/src/include/softmax_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/softmax_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/softmax.hpp" +#include "api/softmax.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/softmax_loss_grad_inst.h b/inference-engine/thirdparty/clDNN/src/include/softmax_loss_grad_inst.h index a764efca26e839..88cc8d17254bca 100644 --- a/inference-engine/thirdparty/clDNN/src/include/softmax_loss_grad_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/softmax_loss_grad_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/softmax_loss_grad.hpp" +#include "api/softmax_loss_grad.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/split_inst.h b/inference-engine/thirdparty/clDNN/src/include/split_inst.h index 0d60123095534c..97add445bf3ce8 100644 --- a/inference-engine/thirdparty/clDNN/src/include/split_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/split_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/split.hpp" +#include "api/split.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/strided_slice_inst.h b/inference-engine/thirdparty/clDNN/src/include/strided_slice_inst.h index 1aedff1a21538a..16a2243c0145b6 100644 --- a/inference-engine/thirdparty/clDNN/src/include/strided_slice_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/strided_slice_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/strided_slice.hpp" +#include "api/strided_slice.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/tile_inst.h b/inference-engine/thirdparty/clDNN/src/include/tile_inst.h index 7751c23cfbcd96..5bc64ff4839cd9 100644 --- a/inference-engine/thirdparty/clDNN/src/include/tile_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/tile_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/tile.hpp" +#include "api/tile.hpp" #include "primitive_inst.h" #include diff --git a/inference-engine/thirdparty/clDNN/src/include/to_string_utils.h b/inference-engine/thirdparty/clDNN/src/include/to_string_utils.h index 52a5752528c767..f865a0dd45ff64 100644 --- a/inference-engine/thirdparty/clDNN/src/include/to_string_utils.h +++ b/inference-engine/thirdparty/clDNN/src/include/to_string_utils.h @@ -15,9 +15,9 @@ */ #pragma once #include -#include "api/CPP/tensor.hpp" -#include "api/CPP/layout.hpp" -#include "api/CPP/primitive.hpp" +#include "api/tensor.hpp" +#include "api/layout.hpp" +#include "api/primitive.hpp" #include namespace cldnn { @@ -93,6 +93,8 @@ inline std::string fmt_to_str(format fmt) { return "bfwzyx"; case format::fs_b_yx_fsv32: return "fs_b_yx_fsv32"; + case format::bfzyx_f16: + return "bfzyx_f16"; case format::winograd_2x3_s1_weights: return "winograd_2x3_s1_weights"; @@ -134,7 +136,10 @@ inline std::string fmt_to_str(format fmt) { return "os_is_y_x8_osv8_isv4"; case format::os_is_yx_osv32_isv32p: return "os_is_yx_osv32_isv32p"; - + case format::o_i_zyx_i16_o16: + return "o_i_zyx_i16_o16"; + case format::i_o_zyx_o16_i16: + return "i_o_zyx_o16_i16"; default: return "unknown (" + std::to_string(fmt.value) + ")"; } diff --git a/inference-engine/thirdparty/clDNN/src/include/topology_impl.h b/inference-engine/thirdparty/clDNN/src/include/topology_impl.h index 41ca28bb34cbf6..c8b465cc460551 100644 --- a/inference-engine/thirdparty/clDNN/src/include/topology_impl.h +++ b/inference-engine/thirdparty/clDNN/src/include/topology_impl.h @@ -16,9 +16,8 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/primitive.hpp" -#include "api/CPP/input_layout.hpp" -#include "api_impl.h" +#include "api/primitive.hpp" +#include "api/input_layout.hpp" #include "refcounted_obj.h" #include @@ -55,7 +54,7 @@ struct topology_impl : public refcounted_obj { } } - void change_input_layout(const primitive_id& id, layout new_layout) { + void change_input_layout(const primitive_id& id, const layout& new_layout) { auto& inp_layout = this->at(id); if (inp_layout->type != input_layout::type_id()) { throw std::runtime_error("Primitive: " + id + " is not input_layout."); @@ -76,5 +75,3 @@ struct topology_impl : public refcounted_obj { topology_map _primitives; }; } // namespace cldnn - -API_CAST(::cldnn_topology, cldnn::topology_impl) diff --git a/inference-engine/thirdparty/clDNN/src/include/upsampling_inst.h b/inference-engine/thirdparty/clDNN/src/include/upsampling_inst.h index 86ea8af4358fae..bc19b5c932c830 100644 --- a/inference-engine/thirdparty/clDNN/src/include/upsampling_inst.h +++ b/inference-engine/thirdparty/clDNN/src/include/upsampling_inst.h @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "api/CPP/upsampling.hpp" +#include "api/upsampling.hpp" #include "primitive_inst.h" #include #include "topology_impl.h" diff --git a/inference-engine/thirdparty/clDNN/src/index_select.cpp b/inference-engine/thirdparty/clDNN/src/index_select.cpp index a9590719d7d045..9fc03be030b4c2 100644 --- a/inference-engine/thirdparty/clDNN/src/index_select.cpp +++ b/inference-engine/thirdparty/clDNN/src/index_select.cpp @@ -22,7 +22,7 @@ #include namespace cldnn { -primitive_type_id index_select_type_id() { +primitive_type_id index_select::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/input_layout.cpp b/inference-engine/thirdparty/clDNN/src/input_layout.cpp index b1bcf8a7239b0c..3e8f2c98d3fa84 100644 --- a/inference-engine/thirdparty/clDNN/src/input_layout.cpp +++ b/inference-engine/thirdparty/clDNN/src/input_layout.cpp @@ -25,7 +25,7 @@ #include namespace cldnn { -primitive_type_id input_layout_type_id() { +primitive_type_id input_layout::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/kernel_selector_helper.cpp b/inference-engine/thirdparty/clDNN/src/kernel_selector_helper.cpp index a79cfee7de49e2..72b9881575712d 100644 --- a/inference-engine/thirdparty/clDNN/src/kernel_selector_helper.cpp +++ b/inference-engine/thirdparty/clDNN/src/kernel_selector_helper.cpp @@ -143,6 +143,8 @@ kernel_selector::data_layout to_data_layout(format f) { return kernel_selector::data_layout::fs_b_yx_fsv32; case format::bfwzyx: return kernel_selector::data_layout::bfwzyx; + case format::bfzyx_f16: + return kernel_selector::data_layout::bfzyx_f16; default: return kernel_selector::data_layout::bfyx; } @@ -252,6 +254,10 @@ kernel_selector::weights_layout to_weights_layout(format f) { return kernel_selector::weights_layout::bf_lyx_yx; case format::oiyx_o16: return kernel_selector::weights_layout::oiyx_o16; + case format::o_i_zyx_i16_o16: + return kernel_selector::weights_layout::o_i_zyx_i16_o16; + case format::i_o_zyx_o16_i16: + return kernel_selector::weights_layout::i_o_zyx_o16_i16; default: throw std::invalid_argument("Unable to convert tensor layout " + fmt_to_str(f) + " to weights layout"); } @@ -319,6 +325,10 @@ cldnn::format::type from_weights_layout(kernel_selector::weights_layout l) { return cldnn::format::os_is_yx_osv32_isv32p; case kernel_selector::weights_layout::oizyx: return cldnn::format::bfzyx; + case kernel_selector::weights_layout::o_i_zyx_i16_o16: + return cldnn::format::o_i_zyx_i16_o16; + case kernel_selector::weights_layout::i_o_zyx_o16_i16: + return cldnn::format::i_o_zyx_o16_i16; default: return cldnn::format::bfyx; } @@ -424,83 +434,83 @@ layout from_weights_tensor(const kernel_selector::weights_tensor& l) { return layout(type, format, t); } -kernel_selector::activation_function get_kernel_selector_activation_param(cldnn_activation_func activation_func) { - switch (activation_func) { - case activation_none: +kernel_selector::activation_function get_kernel_selector_activation_param(activation_func activation) { + switch (activation) { + case cldnn::activation_func::none: return kernel_selector::activation_function::NONE; - case activation_logistic: + case cldnn::activation_func::logistic: return kernel_selector::activation_function::LOGISTIC; - case activation_hyperbolic_tan: + case cldnn::activation_func::hyperbolic_tan: return kernel_selector::activation_function::HYPERBOLIC_TAN; - case activation_relu: + case cldnn::activation_func::relu: return kernel_selector::activation_function::RELU; - case activation_relu_negative_slope: + case cldnn::activation_func::relu_negative_slope: return kernel_selector::activation_function::RELU_NEGATIVE_SLOPE; - case activation_clamp: + case cldnn::activation_func::clamp: return kernel_selector::activation_function::CLAMP; - case activation_softrelu: + case cldnn::activation_func::softrelu: return kernel_selector::activation_function::SOFTRELU; - case activation_abs: + case cldnn::activation_func::abs: return kernel_selector::activation_function::ABS; - case activation_linear: + case cldnn::activation_func::linear: return kernel_selector::activation_function::LINEAR; - case activation_square: + case cldnn::activation_func::square: return kernel_selector::activation_function::SQUARE; - case activation_sqrt: + case cldnn::activation_func::sqrt: return kernel_selector::activation_function::SQRT; - case activation_elu: + case cldnn::activation_func::elu: return kernel_selector::activation_function::ELU; - case activation_sin: + case cldnn::activation_func::sin: return kernel_selector::activation_function::SIN; - case activation_asin: + case cldnn::activation_func::asin: return kernel_selector::activation_function::ASIN; - case activation_sinh: + case cldnn::activation_func::sinh: return kernel_selector::activation_function::SINH; - case activation_asinh: + case cldnn::activation_func::asinh: return kernel_selector::activation_function::ASINH; - case activation_cos: + case cldnn::activation_func::cos: return kernel_selector::activation_function::COS; - case activation_acos: + case cldnn::activation_func::acos: return kernel_selector::activation_function::ACOS; - case activation_cosh: + case cldnn::activation_func::cosh: return kernel_selector::activation_function::COSH; - case activation_acosh: + case cldnn::activation_func::acosh: return kernel_selector::activation_function::ACOSH; - case activation_log: + case cldnn::activation_func::log: return kernel_selector::activation_function::LOG; - case activation_log2: + case cldnn::activation_func::log2: return kernel_selector::activation_function::LOG2; - case activation_exp: + case cldnn::activation_func::exp: return kernel_selector::activation_function::EXP; - case activation_tan: + case cldnn::activation_func::tan: return kernel_selector::activation_function::TAN; - case activation_atan: + case cldnn::activation_func::atan: return kernel_selector::activation_function::ATAN; - case activation_atanh: + case cldnn::activation_func::atanh: return kernel_selector::activation_function::ATANH; - case activation_floor: + case cldnn::activation_func::floor: return kernel_selector::activation_function::FLOOR; - case activation_ceil: + case cldnn::activation_func::ceil: return kernel_selector::activation_function::CEIL; - case activation_negative: + case cldnn::activation_func::negative: return kernel_selector::activation_function::NEGATIVE; - case activation_not: + case cldnn::activation_func::negation: return kernel_selector::activation_function::NOT; - case activation_pow: + case cldnn::activation_func::pow: return kernel_selector::activation_function::POW; - case activation_erf: + case cldnn::activation_func::erf: return kernel_selector::activation_function::ERF; - case activation_reciprocal: + case cldnn::activation_func::reciprocal: return kernel_selector::activation_function::RECIPROCAL; - case activation_selu: + case cldnn::activation_func::selu: return kernel_selector::activation_function::SELU; - case activation_sign: + case cldnn::activation_func::sign: return kernel_selector::activation_function::SIGN; - case activation_softplus: + case cldnn::activation_func::softplus: return kernel_selector::activation_function::SOFTPLUS; - case activation_softsign: + case cldnn::activation_func::softsign: return kernel_selector::activation_function::SOFTSIGN; - case activation_hard_sigmoid: + case cldnn::activation_func::hard_sigmoid: return kernel_selector::activation_function::HARD_SIGMOID; default: throw std::runtime_error("Unknown activation function"); @@ -509,13 +519,13 @@ kernel_selector::activation_function get_kernel_selector_activation_param(cldnn_ } kernel_selector::activation_function get_kernel_selector_activation_grad_param( - cldnn_activation_grad_func activation_grad_func) { + activation_grad_func activation_grad_func) { switch (activation_grad_func) { - case activation_grad_none: + case cldnn::activation_grad_func::none: return kernel_selector::activation_function::NONE_GRAD; - case activation_grad_relu: + case cldnn::activation_grad_func::relu: return kernel_selector::activation_function::RELU_GRAD; - case activation_grad_relu_negative_slope: + case cldnn::activation_grad_func::relu_negative_slope: return kernel_selector::activation_function::RELU_NEGATIVE_SLOPE_GRAD; default: throw std::runtime_error("Unknown activation_grad function"); diff --git a/inference-engine/thirdparty/clDNN/src/layout_optimizer.cpp b/inference-engine/thirdparty/clDNN/src/layout_optimizer.cpp index 60f5c545354594..4136d97ab3e19b 100644 --- a/inference-engine/thirdparty/clDNN/src/layout_optimizer.cpp +++ b/inference-engine/thirdparty/clDNN/src/layout_optimizer.cpp @@ -33,6 +33,138 @@ using namespace cldnn; +std::pair, bool> reorder_factory::get_reorder(primitive_id src_id, + layout in_layout, + layout out_layout +) { + if (in_layout == out_layout) + return std::make_pair(nullptr, true); + + cache_key ckey{ src_id, out_layout }; + auto itr = _cached_reorders.find(ckey); + if (itr != _cached_reorders.end()) + return std::make_pair(itr->second, true); + + auto count = _cached_reorders.size(); + std::stringstream ss; + ss << src_id << "_reorder_" << count; + + auto reorder = std::make_shared(ss.str(), src_id, out_layout); + _cached_reorders[ckey] = reorder; + + return std::make_pair(reorder, false); +} + +std::vector, bool>> reorder_factory::get_weights_reorder( + primitive_id input_id, + const layout& old_layout, + const kernel_selector::weights_reorder_params& reorder_params) { + + if (reorder_params.engine == kernel_selector::weights_reorder_params::Engine::NONE) + return {}; + + std::vector, bool>> ret; + + if (reorder_params.engine == kernel_selector::weights_reorder_params::Engine::CPU && + reorder_params.cpuKernel != nullptr) { + const auto intermediate_format = from_weights_layout(reorder_params.cpuKernel->GetExpectedInputLayout()); + const auto intermediate_type = from_weights_type(reorder_params.cpuKernel->GetExpectedInputType()); + if (intermediate_format != old_layout.format || intermediate_type != old_layout.data_type) { + const layout intermediate_layout = { intermediate_type, + intermediate_format, + old_layout.size.transform(intermediate_format, 1) }; + + auto reorder = get_reorder(input_id, old_layout, intermediate_layout); + if (reorder.first) { + ret.push_back(reorder); + input_id = reorder.first->id; + } + } + } + + // TODO: Add conversion of WeightsTensor to cldnn::tensor to have not flattened shape + // layout expected_layout = from_weights_tensor(reorder_params.dest); + + auto new_dtype = from_weights_type(reorder_params.dest.GetDType()); + const auto bpp = data_type_traits::size_of(new_dtype); + tensor expected_size = { 1, 1, 1, (tensor::value_type)(reorder_params.dest.PhysicalSizeInBytes() / bpp) }; + + bool toImageType = IsImageType(reorder_params.dest.GetLayout()); + bool toDynamicLSTMType = IsDynamicLSTMType(reorder_params.dest.GetLayout()); + if (toImageType || toDynamicLSTMType) + expected_size = old_layout.size; + + layout expected_layout = { new_dtype, + toImageType ? from_weights_layout(reorder_params.dest.GetLayout()) + : format::bfyx, // simple linear format (flatten to x channel) + expected_size }; + + cache_key ckey{ input_id, expected_layout }; + auto itr = _cached_generic_reorders.find(ckey); + if (itr != _cached_generic_reorders.end()) { + ret.push_back(std::make_pair(itr->second, true)); + } else { + auto count = _cached_generic_reorders.size(); + std::stringstream ss; + ss << input_id << "_generic_layer_" << count; + + auto reorder = std::make_shared(ss.str(), input_id, expected_layout, reorder_params); + _cached_generic_reorders[ckey] = reorder; + ret.push_back(std::make_pair(reorder, false)); + } + + return ret; +} + +bool layout_optimizer::is_format_supported(program_node& node, format::type fmt) { + if (node.is_type() && fmt == format::byxf) + return false; + + if (node.is_type()) + return node.get_output_layout().format == fmt; + + auto& engine = node.get_program().get_engine(); + auto prev_layout = node.get_output_layout(); + auto new_layout = prev_layout; + new_layout.format = fmt; + node.set_output_layout(new_layout, false); + + auto supported = node.type()->does_possible_implementation_exist(engine, node); + + node.set_output_layout(prev_layout, false); + + return supported; +} + +bool layout_optimizer::can_fuse_reorder(program_node& prev, program_node& next, format fmt_prev, format fmt_next) { + auto prev_simple = fmt_prev == format::bfyx || fmt_prev == format::byxf || fmt_prev == format::yxfb; + auto next_simple = fmt_next == format::bfyx || fmt_next == format::byxf || fmt_next == format::yxfb; + auto prev_output_layout = prev.get_output_layout(); + auto next_output_layout = next.get_output_layout(); + + if (next.is_type()) + return true; + + if (next.is_type() && prev_simple && next_simple) + return true; + + if (next.is_type() && prev_simple && next_simple) + return true; + + if (next.is_type() && + (fmt_prev == format::bfyx || fmt_prev == format::yxfb || fmt_prev == format::bfyx_f16 || fmt_prev == format::fs_b_yx_fsv32)) + return true; + + if (next.is_type() && + fmt_prev == format::bfyx && + (fmt_next == format::fs_b_yx_fsv32 || + (fmt_next == format::bfyx_f16 && next_output_layout.size.feature[0] >= 16 && prev_output_layout.size.feature[0] == 3))) + return true; + + return false; +} + + namespace { bool should_use_winograd_2x3_s1(std::shared_ptr const& prim, layout const& input_layout, @@ -94,7 +226,8 @@ bool layout_optimizer::convolution_bfyx_opt(layout const& output_layout, return false; } -bool layout_optimizer::convolution_byxf_opt(layout const& output_layout, +bool layout_optimizer::convolution_byxf_opt(const layout& input_layout, + layout const& output_layout, const layout& weights_layout, std::shared_ptr conv) { // A set of rules that define when byxf mem format has better performance @@ -103,7 +236,7 @@ bool layout_optimizer::convolution_byxf_opt(layout const& output_layout, weights_layout.size.batch[0] % 64 == 0 && conv->stride.spatial[0] == 1 && conv->stride.spatial[1] == 1 && conv->input_offset.spatial[0] == 0 && conv->input_offset.spatial[1] == 0) || // Winograd - should_use_winograd_2x3_s1(conv, output_layout, weights_layout, _output_size_handling_enabled)) + should_use_winograd_2x3_s1(conv, input_layout, weights_layout, _output_size_handling_enabled)) return true; return false; @@ -130,74 +263,97 @@ bool layout_optimizer::convolution_bfyx_f16_opt(layout const& input_layout, return false; } +bool layout_optimizer::convolution_bfzyx_f16_opt(layout const& input_layout, + const layout& weights_layout, + std::shared_ptr conv) { + // A set of rules that define when bfzyx_f16 mem format can be used + if ((input_layout.format == format::bfzyx || + input_layout.format == format::bfzyx_f16) && + (input_layout.data_type == data_types::f32 || + input_layout.data_type == data_types::f16) && + input_layout.size.batch[0] == 1 && + weights_layout.size.batch[0] % 16 == 0 && + (input_layout.size.feature[0] / conv->split()) % 16 == 0 && + conv->dilation == tensor(1) && conv->groups == 1) + return true; + return false; +} + +bool layout_optimizer::deconvolution_bfzyx_f16_opt(layout const& input_layout, + const layout& weights_layout, + std::shared_ptr deconv) { + // A set of rules that define when bfzyx_f16 mem format can be used + if ((input_layout.format == format::bfzyx || + input_layout.format == format::bfzyx_f16) && + (input_layout.data_type == data_types::f32 || + input_layout.data_type == data_types::f16) && + input_layout.size.batch[0] == 1 && + weights_layout.size.batch[0] % 16 == 0 && input_layout.size.feature[0] % 16 == 0 && + deconv->split() == 1) + return true; + return false; +} + bool layout_optimizer::users_for_convolution_byxf_opt(program_node const& node, uint32_t depth) { // This function checks if byxf optimization can be applied to the required depth of node's users. // Setting depth to 1 will check only node's users, depth = 2 are user's users etc. if (depth == 0) return true; - bool use_byxf = false; for (auto& user : node.get_users()) { // primitives that support transitions byxf->other format and other format->byxf are valid for byxf opt if (user->type() == cldnn::eltwise::type_id() || user->type() == cldnn::pooling::type_id()) { - use_byxf = users_for_convolution_byxf_opt(*user, depth - 1); + if (!users_for_convolution_byxf_opt(*user, depth - 1)) + return false; // convolution that is capable to use byxf and is performant is also valid for byxf opt } else if (user->type() == cldnn::convolution::type_id()) { auto conv_prim = user->as().get_primitive(); - if (convolution_byxf_opt(user->calc_output_layout(), + if (convolution_byxf_opt(node.get_output_layout(), + user->calc_output_layout(), user->get_dependency(1).get_output_layout(), conv_prim)) { - use_byxf = users_for_convolution_byxf_opt(*user, depth - 1); + if (!users_for_convolution_byxf_opt(*user, depth - 1)) + return false; } else { - use_byxf = false; - break; + return false; } } else { - use_byxf = false; - break; + return false; } } - return use_byxf; + return true; } -bool layout_optimizer::deps_depth_in_same_format(program_node const& node, const cldnn::format format, uint32_t depth) { +bool layout_optimizer::deps_for_convolution_byxf_opt(program_node const& node, uint32_t depth) { // This function checks if requested format is the same for node's users in the required depth. // Setting depth to 1 will check only node's dependencies, depth = 2 are dep's dependencies etc. if (depth == 0) return true; - bool same_format = false; for (auto& dep : node.get_dependencies()) { // skip data and generic_layers - if (dep->type() == cldnn::data::type_id() || dep->type() == cldnn::generic_layer::type_id()) + if (dep->is_type() || dep->is_type()) continue; - // if dependency is of type reorder and format is different then skip it and move to its dependency - // further in graph such reorders could be optimized out - if (dep->type() == cldnn::reorder::type_id() && dep->get_dependencies().size() == 1 && - dep->get_output_layout().format != format) { - same_format = deps_depth_in_same_format(dep->get_dependency(0), format, depth); - } else if (dep->get_output_layout().format == format) { - // if dependency is of type reorder and format is the same, check if its users are primitives with support - // for different input and output formats if that is true then graph optimizer will optimize such reorder and - // layout for its dependency will be changed - if (dep->type() == cldnn::reorder::type_id() && - (dep->get_dependency(0).type() == cldnn::eltwise::type_id() || - dep->get_dependency(0).type() == cldnn::pooling::type_id()) && - dep->get_dependencies().size() == 1) - same_format = deps_depth_in_same_format(dep->get_dependency(0), format, depth - 1); - else - same_format = deps_depth_in_same_format(*dep, format, depth - 1); - } else { - same_format = false; - break; + if (dep->is_type()) { + auto& conv_dep = dep->as(); + if (!convolution_byxf_opt(conv_dep.input().get_output_layout(), + conv_dep.get_output_layout(), + conv_dep.weights().get_output_layout(), + conv_dep.get_primitive())) { + return false; + } + } else if (!dep->is_type() && !dep->is_type()) { + return false; } + + if (!deps_for_convolution_byxf_opt(*dep, depth - 1)) + return false; } - return same_format; + return true; } layout layout_optimizer::get_expected_layout(layout const& current_layout, - data_type type, convolution_node const& node, layout const& output_or_weights_layout) { auto prim = node.get_primitive(); @@ -210,150 +366,85 @@ layout layout_optimizer::get_expected_layout(layout const& current_layout, if (conv_node->get_groups() > 1) dw_prev_check = true; } - if (type == data_type::weights || type == data_type::bias) { - expected_data_type = output_or_weights_layout.data_type; + if (_optimization_attributes.bfzyx_f16_network && + convolution_bfzyx_f16_opt(node.get_dependency(0).get_output_layout(), + output_or_weights_layout, prim)) { + expected_tensor = current_layout.size; + expected_format = cldnn::format::bfzyx_f16; + } else if (current_layout.format == format::bfzyx) { + expected_tensor = current_layout.size; + expected_format = cldnn::format::bfzyx; + } else if ((_optimization_attributes.bfyx_f16_network && + convolution_bfyx_f16_opt(node.get_dependency(0).get_output_layout(), output_or_weights_layout, prim)) || + node.get_dependency(0).get_output_layout().format == format::bfyx_f16) { + expected_tensor = current_layout.size; + expected_format = cldnn::format::bfyx_f16; + } else if (current_layout.data_type == data_types::f16 && + layout_optimizer::convolution_byxf_opt(node.input().get_output_layout(), current_layout, output_or_weights_layout, prim) && + (users_for_convolution_byxf_opt(node, 2) || + deps_for_convolution_byxf_opt(node, 2)) && + // todo: remove this condition when yxfb optimizations will be disabled + current_layout.format != cldnn::format::yxfb && current_layout.size.batch[0] == 1 && + prim->dilation == tensor {1} && !node.get_transposed() && + !dw_prev_check && node.get_groups() == 1) { + expected_tensor = current_layout.size; + expected_format = cldnn::format::byxf; + } else if ((current_layout.data_type == data_types::f16 && current_layout.format == format::fs_b_yx_fsv32) || + (current_layout.data_type == data_types::f16 && prim->split() == 1 && prim->groups == 1 && + current_layout.size.feature[0] % 32 == 0 && current_layout.size.batch[0] != 1 && + current_layout.size.batch[0] <= 16 && _optimization_attributes.splitted_convolution == 0 && + _optimization_attributes.group_convolution == 0 && + _optimization_attributes.deformable_convolution == 0 && + _optimization_attributes.only_fsv32_layers == 1)) { + // fp16 32 features things + if (prim->split() != 1 || + current_layout.size.batch[0] == 1) // escape to bfyx format for unsupported node + expected_format = format::bfyx; + else + expected_format = format::fs_b_yx_fsv32; + } else if (current_layout.format == format::b_fs_yx_fsv4 || + current_layout.format == format::os_is_yx_osv16_isv4) { + // imad case + // nothing to do, just go out from here. + } else if (current_layout.data_type == data_types::i8) { // mmad case + expected_tensor = current_layout.size; + expected_format = current_layout.format; // cldnn::format::byxf_af32; + } else if (layout_optimizer::convolution_bfyx_opt(current_layout, output_or_weights_layout, prim) || + (_output_size_handling_enabled && prim->with_output_size) || node.get_transposed()) { + // commented out due to performance reasons, maybe enable in future + /*if (current_layout.data_type == data_types::f32 && + current_layout.size.batch[0] % 16 == 0 && + current_layout.format == format::bfyx && + output_or_weights_layout.size.spatial[0] == 1 && output_or_weights_layout.size.spatial[1] == 1 && + prim->stride.spatial[0] == 1 && prim->stride.spatial[1] == 1 && + prim->input_offset.spatial[0] == 0 && prim->input_offset.spatial[1] == 0 && + !node.get_transposed()) + { + if (!((current_layout.size.feature[0] % 8) == 0 && (current_layout.size.spatial[0] * + current_layout.size.spatial[1]) == 16 && current_layout.data_padding == padding{ { 0,0,0,0 }, 0 })) + { + expected_tensor = current_layout.size.transform(cldnn::format::bf8_xy16, 1); + expected_format = cldnn::format::bf8_xy16; + } } - - switch (type) { - case data_type::bias: // convolution bias - expected_tensor = cldnn::tensor(1, 1, static_cast(current_layout.count()), 1); - expected_format = cldnn::format::bfyx; - break; - - case data_type::input: // convolution input - if (current_layout.format == format::bfzyx) { - expected_tensor = current_layout.size; + else*/ + { + expected_tensor = current_layout.size; + if (current_layout.format == format::bfzyx_f16) expected_format = cldnn::format::bfzyx; - } else if (_optimization_attributes.bfyx_f16_network && - convolution_bfyx_f16_opt(current_layout, output_or_weights_layout, prim)) { - expected_tensor = current_layout.size; - expected_format = cldnn::format::bfyx_f16; - } else if (current_layout.data_type == data_types::f16 && - layout_optimizer::convolution_byxf_opt(current_layout, output_or_weights_layout, prim) && - (users_for_convolution_byxf_opt(node, 2) || - deps_depth_in_same_format(node, cldnn::format::byxf, 2)) && - // todo: remove this condition when yxfb optimizations will be disabled - current_layout.format != cldnn::format::yxfb && current_layout.size.batch[0] == 1 && - prim->dilation == tensor {1} && !node.get_transposed() && - !dw_prev_check && node.get_groups() == 1) { - expected_tensor = current_layout.size; - expected_format = cldnn::format::byxf; - } else if ((current_layout.data_type == data_types::f16 && current_layout.format == format::fs_b_yx_fsv32) || - (current_layout.data_type == data_types::f16 && prim->split() == 1 && prim->groups == 1 && - current_layout.size.feature[0] % 32 == 0 && current_layout.size.batch[0] != 1 && - current_layout.size.batch[0] <= 16 && _optimization_attributes.splitted_convolution == 0 && - _optimization_attributes.group_convolution == 0 && - _optimization_attributes.deformable_convolution == 0 && - _optimization_attributes.only_fsv32_layers == 1)) { - // fp16 32 features things - if (output_or_weights_layout.size.feature[0] == 3 || // use bfyx -> fs_byx_fsv32 convolution - prim->split() != 1 || - current_layout.size.batch[0] == 1) // escape to bfyx format for unsupported node - expected_format = format::bfyx; - else - expected_format = format::fs_b_yx_fsv32; - } else if (current_layout.format == format::b_fs_yx_fsv4 || - current_layout.format == format::os_is_yx_osv16_isv4) { - // imad case - // nothing to do, just go out from here. - } else if (current_layout.data_type == data_types::i8) { // mmad case - expected_tensor = current_layout.size; - expected_format = current_layout.format; // cldnn::format::byxf_af32; - } else if (layout_optimizer::convolution_bfyx_opt(current_layout, output_or_weights_layout, prim) || - (_output_size_handling_enabled && prim->with_output_size) || node.get_transposed()) { - // commented out due to performance reasons, maybe enable in future - /*if (current_layout.data_type == data_types::f32 && - current_layout.size.batch[0] % 16 == 0 && - current_layout.format == format::bfyx && - output_or_weights_layout.size.spatial[0] == 1 && output_or_weights_layout.size.spatial[1] == 1 && - prim->stride.spatial[0] == 1 && prim->stride.spatial[1] == 1 && - prim->input_offset.spatial[0] == 0 && prim->input_offset.spatial[1] == 0 && - !node.get_transposed()) - { - if (!((current_layout.size.feature[0] % 8) == 0 && (current_layout.size.spatial[0] * - current_layout.size.spatial[1]) == 16 && current_layout.data_padding == padding{ { 0,0,0,0 }, 0 })) - { - expected_tensor = current_layout.size.transform(cldnn::format::bf8_xy16, 1); - expected_format = cldnn::format::bf8_xy16; - } - } - else*/ - { - expected_tensor = current_layout.size; - expected_format = cldnn::format::bfyx; - } - - } else { - expected_tensor = current_layout.size; - expected_format = cldnn::format::yxfb; - } - - break; - - default: - throw std::runtime_error( - "Unsupported data type in layout_optimizer::get_expected_layout for convolution primitive"); - } - - return layout(expected_data_type, expected_format, expected_tensor); -} - -layout layout_optimizer::get_expected_layout(layout const& current_layout, - data_type type, - fully_connected_node const& node, - layout const& output_or_weights_layout) { - auto prim = node.get_primitive(); - auto expected_tensor = current_layout.size; - auto expected_data_type = current_layout.data_type; - auto expected_format = current_layout.format; - - if (type == data_type::weights || type == data_type::bias) { - expected_data_type = output_or_weights_layout.data_type; - } - - switch (type) { - case data_type::bias: // fc bias - expected_tensor = cldnn::tensor(1, 1, static_cast(current_layout.count()), 1); - expected_format = cldnn::format::bfyx; - break; - - default: - throw std::runtime_error( - "Unsupported data type in layout_optimizer::get_expected_layout for fully-connected primitive"); - } - - return layout(expected_data_type, expected_format, expected_tensor); -} - -layout layout_optimizer::get_expected_layout(layout const& current_layout, - data_type type, - lstm_gemm_node const& node, - layout const& output_or_weights_layout) { - auto prim = node.get_primitive(); - auto expected_tensor = current_layout.size; - auto expected_data_type = current_layout.data_type; - auto expected_format = current_layout.format; - - if (type == data_type::weights || type == data_type::bias) { - expected_data_type = output_or_weights_layout.data_type; - } - - switch (type) { - case data_type::bias: - expected_tensor = cldnn::tensor(1, 1, static_cast(current_layout.count()), 1); - expected_format = cldnn::format::bfyx; - break; + else + expected_format = cldnn::format::bfyx; + } - default: - throw std::runtime_error( - "Unsupported data type in layout_optimizer::get_expected_layout for fully-connected primitive"); + } else { + expected_tensor = current_layout.size; + expected_format = cldnn::format::yxfb; } return layout(expected_data_type, expected_format, expected_tensor); } layout layout_optimizer::get_expected_layout(layout const& current_layout, - data_type type, deconvolution_node const& node, layout const& output_or_weights_layout) { auto prim = node.get_primitive(); @@ -361,26 +452,15 @@ layout layout_optimizer::get_expected_layout(layout const& current_layout, auto expected_data_type = current_layout.data_type; auto expected_format = current_layout.format; - if (type == data_type::weights || type == data_type::bias) { - expected_data_type = output_or_weights_layout.data_type; - } - - switch (type) { - case data_type::bias: // convolution bias - expected_tensor = cldnn::tensor(1, 1, static_cast(current_layout.count()), 1); - expected_format = cldnn::format::bfyx; - break; - - default: - throw std::runtime_error( - "Unsupported data type in layout_optimizer::get_expected_layout for deconvolution primitive"); + if (_optimization_attributes.bfzyx_f16_network && + deconvolution_bfzyx_f16_opt(current_layout, output_or_weights_layout, prim)) { + expected_tensor = current_layout.size; + expected_format = cldnn::format::bfzyx_f16; } - return layout(expected_data_type, expected_format, expected_tensor); } layout layout_optimizer::get_expected_layout(layout const& current_layout, - data_type type, detection_output_node const& node, layout const& output_or_weights_layout) { auto prim = node.get_primitive(); @@ -388,161 +468,20 @@ layout layout_optimizer::get_expected_layout(layout const& current_layout, auto expected_data_type = data_types::f32; auto expected_format = output_or_weights_layout.format; - if (type != data_type::input) - CLDNN_ERROR_MESSAGE(prim->id, "detection_output only supports optimization of its output (no weights/biases)"); - - return layout(expected_data_type, expected_format, expected_tensor); -} - -layout layout_optimizer::get_expected_layout(layout const& current_layout, - data_type type, - embed_node const& node, - layout const& output_or_weights_layout) { - auto prim = node.get_primitive(); - auto expected_tensor = current_layout.size; - auto expected_data_type = current_layout.data_type; - auto expected_format = current_layout.format; - - if (type == data_type::weights || type == data_type::bias) { - expected_data_type = output_or_weights_layout.data_type; - } - - switch (type) { - case data_type::bias: - expected_tensor = cldnn::tensor(1, 1, static_cast(current_layout.count()), 1); - expected_format = cldnn::format::bfyx; - break; - - default: - throw std::runtime_error( - "Unsupported data type in layout_optimizer::get_expected_layout for embed primitive"); - } - return layout(expected_data_type, expected_format, expected_tensor); } layout layout_optimizer::get_expected_layout(layout const& current_layout, - data_type type, binary_convolution_node const& node, layout const& output_or_weights_layout) { auto prim = node.get_primitive(); auto expected_tensor = current_layout.size; auto expected_data_type = data_types::bin; - auto expected_format = current_layout.format; - - if (type == data_type::weights || type == data_type::bias) { - expected_data_type = output_or_weights_layout.data_type; - } - - switch (type) { - case data_type::bias: // convolution bias - expected_tensor = cldnn::tensor(1, 1, static_cast(current_layout.count()), 1); - expected_format = cldnn::format::bfyx; - break; - - case data_type::input: // convolution input - expected_tensor = current_layout.size; - expected_format = cldnn::format::b_fs_yx_32fp; - break; - - default: - throw std::runtime_error( - "Unsupported data type in layout_optimizer::get_expected_layout for binary_convolution primitive"); - } + auto expected_format = cldnn::format::b_fs_yx_32fp; return layout(expected_data_type, expected_format, expected_tensor); } -std::pair, bool> layout_optimizer::create_reorder_if_needed( - const layout& current_layout, - const cldnn::primitive_id& memid, - layout const& expected_layout) { - if (current_layout != expected_layout) { - cache_key ckey{memid, expected_layout}; - auto itr = _cached_reorders.find(ckey); - if (itr != _cached_reorders.end()) - return std::make_pair(itr->second, true); - - auto count = _cached_reorders.size(); - std::stringstream ss; - ss << "reorder_" << count << "_" << memid; - - auto reorder = std::make_shared(ss.str(), memid, expected_layout); - _cached_reorders[ckey] = reorder; - return std::make_pair(reorder, false); - } - - return std::make_pair(nullptr, true); -} - -std::pair, bool> layout_optimizer::create_reorder_from_given_source( - const cldnn::primitive_id& memid, - layout const& expected_layout, - const kernel_selector::weights_reorder_params& reorder_params) { - cache_key ckey{memid, expected_layout}; - auto itr = _cached_generic_layers.find(ckey); - if (itr != _cached_generic_layers.end()) - return std::make_pair(itr->second, true); - - auto count = _cached_generic_layers.size(); - std::stringstream ss; - ss << memid << "_generic_layer_" << count; - - auto reorder = std::make_shared(ss.str(), memid, expected_layout, reorder_params); - _cached_generic_layers[ckey] = reorder; - return std::make_pair(reorder, false); -} - -std::vector, bool>> layout_optimizer::get_generic_layer( - const kernel_selector::weights_reorder_params& reorder_params, - primitive_id input_id, - const layout& old_layout, - data_type type) { - if (reorder_params.engine == kernel_selector::weights_reorder_params::Engine::NONE || type != data_type::weights) - return {}; - - std::vector, bool>> ret; - - if (reorder_params.engine == kernel_selector::weights_reorder_params::Engine::CPU && - reorder_params.cpuKernel != nullptr) { - const auto intermediate_format = from_weights_layout(reorder_params.cpuKernel->GetExpectedInputLayout()); - const auto intermediate_type = from_weights_type(reorder_params.cpuKernel->GetExpectedInputType()); - if (intermediate_format != old_layout.format || intermediate_type != old_layout.data_type) { - const layout intermediate_layout = {intermediate_type, - intermediate_format, - old_layout.size.transform(intermediate_format, 1)}; - - auto reorder = create_reorder_if_needed(old_layout, input_id, intermediate_layout); - if (reorder.first) { - ret.push_back(reorder); - input_id = reorder.first->id; - } - } - } - - // TODO: Add conversion of WeightsTensor to cldnn::tensor to have not flattened shape - // layout expected_layout = from_weights_tensor(reorder_params.dest); - - auto new_dtype = from_weights_type(reorder_params.dest.GetDType()); - const auto bpp = data_type_traits::size_of(new_dtype); - tensor expected_size = {1, 1, 1, (tensor::value_type)(reorder_params.dest.PhysicalSizeInBytes() / bpp)}; - - bool toImageType = IsImageType(reorder_params.dest.GetLayout()); - if (toImageType) - expected_size = old_layout.size; - - layout expected_layout = {new_dtype, - toImageType ? from_weights_layout(reorder_params.dest.GetLayout()) - : format::bfyx, // simple linear format (flatten to x channel) - expected_size}; - - auto reorder = create_reorder_from_given_source(input_id, expected_layout, reorder_params); - if (reorder.first) - ret.push_back(reorder); - - return ret; -} - void layout_optimizer::set_optimization_attribute(optimization_attributes_type attribute, int32_t val) { switch (attribute) { case optimization_attributes_type::splitted_convolution: @@ -563,6 +502,9 @@ void layout_optimizer::set_optimization_attribute(optimization_attributes_type a case optimization_attributes_type::bfyx_f16_network: _optimization_attributes.bfyx_f16_network = val; break; + case optimization_attributes_type::bfzyx_f16_network: + _optimization_attributes.bfzyx_f16_network = val; + break; default: throw std::out_of_range("unsupported layout optimization attribute"); } @@ -578,8 +520,25 @@ bool layout_optimizer::is_format_optimized(const convolution_node& node, const f return convolution_bfyx_f16_opt(input_layout, weights_layout, prim) && // Work-around for inability to use bfyx_f16 and winograd together !should_use_winograd_2x3_s1(prim, input_layout, weights_layout, _output_size_handling_enabled); + case format::bfzyx_f16: + return convolution_bfzyx_f16_opt(input_layout, weights_layout, prim); default: throw std::invalid_argument( "[Layout optimizer] Other formats in is_format_optimized(...) method are not implemented!"); } } + +bool layout_optimizer::is_format_optimized(const deconvolution_node& node, const format& format) { + auto input_layout = node.input().get_output_layout(); + auto weights_layout = node.weights().get_output_layout(); + auto prim = node.get_primitive(); + + switch (format) { + case format::bfzyx_f16: + return deconvolution_bfzyx_f16_opt(input_layout, weights_layout, prim); + default: + throw std::invalid_argument( + "[Layout optimizer] Other formats in is_format_optimized(...) method are not implemented!"); + } +} + diff --git a/inference-engine/thirdparty/clDNN/src/lookup_table.cpp b/inference-engine/thirdparty/clDNN/src/lookup_table.cpp index 61a422be1529bb..8f2ba63be8f400 100644 --- a/inference-engine/thirdparty/clDNN/src/lookup_table.cpp +++ b/inference-engine/thirdparty/clDNN/src/lookup_table.cpp @@ -23,7 +23,7 @@ #include namespace cldnn { -primitive_type_id lookup_table_type_id() { +primitive_type_id lookup_table::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/lrn.cpp b/inference-engine/thirdparty/clDNN/src/lrn.cpp index 6f9e0d394257cc..dba1fb3d64d136 100644 --- a/inference-engine/thirdparty/clDNN/src/lrn.cpp +++ b/inference-engine/thirdparty/clDNN/src/lrn.cpp @@ -21,7 +21,7 @@ #include namespace cldnn { -primitive_type_id lrn_type_id() { +primitive_type_id lrn::type_id() { static primitive_type_base instance; return &instance; } @@ -39,7 +39,7 @@ std::string lrn_inst::to_string(lrn_node const& node) { auto k = desc->k; auto alpha = desc->alpha; auto beta = desc->beta; - auto norm_region = desc->norm_region == cldnn_lrn_norm_region::cldnn_lrn_norm_region_across_channel + auto norm_region = desc->norm_region == lrn_norm_region::lrn_norm_region_across_channel ? "across channel" : "within channel"; auto& input = node.input(); diff --git a/inference-engine/thirdparty/clDNN/src/lstm.cpp b/inference-engine/thirdparty/clDNN/src/lstm.cpp index 061ab9b2da0889..5eb99dfdeba756 100644 --- a/inference-engine/thirdparty/clDNN/src/lstm.cpp +++ b/inference-engine/thirdparty/clDNN/src/lstm.cpp @@ -22,7 +22,7 @@ #include namespace cldnn { -primitive_type_id lstm_type_id() { +primitive_type_id lstm::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/lstm_dynamic.cpp b/inference-engine/thirdparty/clDNN/src/lstm_dynamic.cpp index 86e0c7248f5179..657d1e22bf2ddc 100644 --- a/inference-engine/thirdparty/clDNN/src/lstm_dynamic.cpp +++ b/inference-engine/thirdparty/clDNN/src/lstm_dynamic.cpp @@ -22,7 +22,7 @@ #include namespace cldnn { -primitive_type_id lstm_dynamic_type_id() { +primitive_type_id lstm_dynamic::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/lstm_dynamic_input.cpp b/inference-engine/thirdparty/clDNN/src/lstm_dynamic_input.cpp index fc23642d4fc3a6..4d11734f5ee7d3 100644 --- a/inference-engine/thirdparty/clDNN/src/lstm_dynamic_input.cpp +++ b/inference-engine/thirdparty/clDNN/src/lstm_dynamic_input.cpp @@ -23,7 +23,7 @@ #include namespace cldnn { -primitive_type_id lstm_dynamic_input_type_id() { +primitive_type_id lstm_dynamic_input::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/lstm_dynamic_timeloop.cpp b/inference-engine/thirdparty/clDNN/src/lstm_dynamic_timeloop.cpp index 6dd38d30dfada8..32f6276e7fcf7e 100644 --- a/inference-engine/thirdparty/clDNN/src/lstm_dynamic_timeloop.cpp +++ b/inference-engine/thirdparty/clDNN/src/lstm_dynamic_timeloop.cpp @@ -23,7 +23,7 @@ #include namespace cldnn { -primitive_type_id lstm_dynamic_timeloop_type_id() { +primitive_type_id lstm_dynamic_timeloop::type_id() { static primitive_type_base instance; return &instance; } @@ -79,7 +79,6 @@ void lstm_dynamic_timeloop_node::reverse_optional_outputs_connections() { } } - size_t lstm_dynamic_timeloop_node::get_dependency_idx(std::string val) const { auto ret = get_param_list_index(val); CLDNN_ERROR_EQUAL(id(), diff --git a/inference-engine/thirdparty/clDNN/src/lstm_elt.cpp b/inference-engine/thirdparty/clDNN/src/lstm_elt.cpp index 9981e7270bc6d5..2672148ac5c26c 100644 --- a/inference-engine/thirdparty/clDNN/src/lstm_elt.cpp +++ b/inference-engine/thirdparty/clDNN/src/lstm_elt.cpp @@ -22,7 +22,7 @@ #include namespace cldnn { -primitive_type_id lstm_elt_type_id() { +primitive_type_id lstm_elt::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/lstm_gemm.cpp b/inference-engine/thirdparty/clDNN/src/lstm_gemm.cpp index 15abf73e97f65d..580ab10a659e71 100644 --- a/inference-engine/thirdparty/clDNN/src/lstm_gemm.cpp +++ b/inference-engine/thirdparty/clDNN/src/lstm_gemm.cpp @@ -22,7 +22,7 @@ #include namespace cldnn { -primitive_type_id lstm_gemm_type_id() { +primitive_type_id lstm_gemm::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/max_unpooling.cpp b/inference-engine/thirdparty/clDNN/src/max_unpooling.cpp index f400419bf56cc7..b3a3c9d78e95ff 100644 --- a/inference-engine/thirdparty/clDNN/src/max_unpooling.cpp +++ b/inference-engine/thirdparty/clDNN/src/max_unpooling.cpp @@ -23,7 +23,7 @@ #include namespace cldnn { -primitive_type_id max_unpooling_type_id() { +primitive_type_id max_unpooling::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/memory.cpp b/inference-engine/thirdparty/clDNN/src/memory.cpp new file mode 100644 index 00000000000000..39161c04b171c3 --- /dev/null +++ b/inference-engine/thirdparty/clDNN/src/memory.cpp @@ -0,0 +1,86 @@ +/* +// Copyright (c) 2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#include "api/memory.hpp" +#include "memory_impl.h" +#include "engine_impl.h" + +namespace cldnn { + +memory memory::allocate(const engine& engine, const layout& layout, uint16_t stream_id) { + size_t size = layout.bytes_count(); + if (size == 0) + throw std::invalid_argument("size should be more than 0"); + + return memory(engine.get()->allocate_memory(layout, stream_id).detach()); +} + +size_t memory::count() const { + return get_layout().count(); +} + +size_t memory::size() const { + return _impl->size(); +} + +const layout& memory::get_layout() const { + return _impl->get_layout(); +} + +int memory::get_stream_id() const { + return _impl->get_stream_id(); +} + +bool memory::is_allocated_by(const engine& engine) const { + return _impl->is_allocated_by(*engine.get()); +} + +bool memory::is_the_same_buffer(const memory& other) const { + if (_impl == other.get()) + return true; + + if (_impl->get_engine() != other.get()->get_engine()) + return false; + + // User memory, check te pointers + if (!_impl->get_engine()) + return lock_impl() == other.lock_impl(); + + // Engine memory, let it decide + return _impl->get_engine()->is_the_same_buffer(*_impl, *other.get()); +} + +memory memory::attach_impl(const cldnn::layout& layout, void* ptr, uint16_t stream_id) { + return memory(new simple_attached_memory(layout, ptr, stream_id)); +} + +void* memory::lock_impl() const { + return _impl->lock(); +} + +void memory::unlock() const { + _impl->unlock(); +} + +void memory::retain() { + _impl->add_ref(); +} +void memory::release() { + _impl->release(); +} + +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/src/memory_pool.cpp b/inference-engine/thirdparty/clDNN/src/memory_pool.cpp index 7798437053ba08..85d07e8e9390b3 100644 --- a/inference-engine/thirdparty/clDNN/src/memory_pool.cpp +++ b/inference-engine/thirdparty/clDNN/src/memory_pool.cpp @@ -32,6 +32,7 @@ #include #include #include +#include namespace cldnn { memory_record::memory_record(memory_set users, @@ -43,13 +44,13 @@ memory_record::memory_record(memory_set users, memory_impl::ptr memory_pool::alloc_memory(const layout& layout, uint16_t stream_id) { auto context = _engine->get_context(); if (layout.bytes_count() > context->get_engine_info().max_alloc_mem_size) { - throw error("exceeded max size of memory object allocation", CLDNN_ALLOC_SIZE_EXCEEDED); + throw std::runtime_error("exceeded max size of memory object allocation"); } add_memory_used(layout.bytes_count()); if (_max_peak_memory_used > context->get_engine_info().max_global_mem_size) { - throw error("exceeded global device memory", CLDNN_GLOBAL_SIZE_EXCEEDED); + throw std::runtime_error("exceeded global device memory"); } try { @@ -66,9 +67,9 @@ memory_impl::ptr memory_pool::alloc_memory(const layout& layout, uint16_t stream case CL_OUT_OF_RESOURCES: case CL_OUT_OF_HOST_MEMORY: case CL_INVALID_BUFFER_SIZE: - throw error("out of GPU resources", CLDNN_OUT_OF_RESOURCES); + throw std::runtime_error("out of GPU resources"); default: - throw error("GPU buffer allocation failed", CLDNN_ERROR); + throw std::runtime_error("GPU buffer allocation failed"); } } } diff --git a/inference-engine/thirdparty/clDNN/src/mutable_data.cpp b/inference-engine/thirdparty/clDNN/src/mutable_data.cpp index 53222ffb064d09..cfece38c3ae5ac 100644 --- a/inference-engine/thirdparty/clDNN/src/mutable_data.cpp +++ b/inference-engine/thirdparty/clDNN/src/mutable_data.cpp @@ -26,7 +26,7 @@ #include namespace cldnn { -primitive_type_id mutable_data_type_id() { +primitive_type_id mutable_data::type_id() { static primitive_type_base instance; return &instance; } @@ -46,7 +46,7 @@ memory_impl::ptr attach_or_copy_data(network_impl& network, memory_impl& mem) { } // namespace mutable_data_node::typed_program_node(const std::shared_ptr dprim, program_impl& prog) - : parent(dprim, prog), mem(api_cast(dprim->mem.get())) { + : parent(dprim, prog), mem(dprim->mem.get()) { recalc_output_layout(false); can_share_buffer(false); fill_memory(); diff --git a/inference-engine/thirdparty/clDNN/src/mvn.cpp b/inference-engine/thirdparty/clDNN/src/mvn.cpp index b264a19ecd6fe1..cd35de55663cd8 100644 --- a/inference-engine/thirdparty/clDNN/src/mvn.cpp +++ b/inference-engine/thirdparty/clDNN/src/mvn.cpp @@ -20,7 +20,7 @@ #include namespace cldnn { -primitive_type_id mvn_type_id() { +primitive_type_id mvn::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/network.cpp b/inference-engine/thirdparty/clDNN/src/network.cpp index f1e147b0984e96..49ef8aafa2a613 100644 --- a/inference-engine/thirdparty/clDNN/src/network.cpp +++ b/inference-engine/thirdparty/clDNN/src/network.cpp @@ -19,9 +19,9 @@ #include "engine_impl.h" #include "event_impl.h" #include "program_impl.h" -#include "api/CPP/data.hpp" -#include "api/CPP/mutable_data.hpp" -#include "api/CPP/input_layout.hpp" +#include "api/data.hpp" +#include "api/mutable_data.hpp" +#include "api/input_layout.hpp" #include "error_handler.h" #include "primitive_inst.h" @@ -36,6 +36,8 @@ #include #include #include +#include +#include // #define DEBUG_DUMP_PATH "cldnn_dump/" @@ -49,6 +51,103 @@ #endif namespace cldnn { + +network::network(program const& program, uint16_t stream_id) + : _impl(program.get()->get_engine().allocate_network(*program.get(), stream_id).detach()) {} + +engine network::get_engine() const { + auto impl = engine_impl::ptr(&_impl->get_engine()); + return engine(impl.detach()); +} + +program network::get_program() const { + auto impl = program_impl::cptr(&_impl->get_program()); + return program(const_cast(impl.detach())); +} + +void network::set_input_data(const primitive_id& id, const memory& mem) const { + _impl->set_input_data(id, *mem.get()); +} + +void network::set_learning_rate(const float lr) { + _impl->set_learning_rate(lr); +} + +float network::get_learning_rate() { + return _impl->get_learning_rate(); +} + +uint16_t network::get_stream_id() { + return _impl->get_stream_id(); +} + +std::string network::get_primitive_info(const primitive_id& id) const { + return _impl->get_primitive_info(id); +} + +std::vector network::get_primitives_info() { + return _impl->get_primitives_info(); +} + +std::vector>> network::get_optimization_steps_info() { + return _impl->get_optimizer_passes_info(); +} + +std::vector network::get_executed_primitive_ids() const { + return _impl->get_executed_primitive_ids(); +} + +std::vector network::get_all_primitive_ids() const { + return _impl->get_all_primitive_ids(); +} + +std::vector network::get_all_primitive_org_ids() const { + return _impl->get_all_primitive_org_ids(); +} + +std::vector network::get_output_ids() const { + return _impl->get_output_ids(); +} + +memory network::get_output_memory(const primitive_id& output_id) const { + auto out_mem = memory_impl::ptr(&_impl->get_primitive(output_id)->output_memory()); + return memory(out_mem.detach()); +} + +event network::get_primitive_event(const primitive_id& output_id) const { + auto out_event = _impl->get_primitive_event(output_id); + return event(out_event.detach()); +} + +std::map network::execute(const std::vector& dependencies) const { + std::vector> dep_impls(dependencies.size()); + + std::transform( + dependencies.begin(), + dependencies.end(), + dep_impls.begin(), + [](const event& ev) { + return event_impl::ptr(ev.get()); + }); + + _impl->execute(dep_impls); + + auto output_ids = get_output_ids(); + std::map result; + for (auto& id : output_ids) { + result.emplace(id, get_output(id)); + } + return result; +} + +void network::retain() { + _impl->add_ref(); +} + +void network::release() { + _impl->release(); +} + #ifdef DEBUG_DUMP_PATH static float convert_half_to_float(half_t val, bool flush_denorm_to_zero = false) { #if defined HALF_HALF_HPP @@ -142,11 +241,13 @@ void dump(memory_impl& mem, std::ofstream& file_stream) { for (cldnn::tensor::value_type b = 0; b < size.batch[0]; ++b) { for (cldnn::tensor::value_type f = 0; f < (cldnn::tensor::value_type)ceil_div(size.feature[0], 32); ++f) { - for (cldnn::tensor::value_type y = 0; y < size.spatial[1]; ++y) { - for (cldnn::tensor::value_type x = 0; x < size.spatial[0]; ++x) { - cldnn::tensor t(cldnn::batch(b), cldnn::feature(f), cldnn::spatial(x, y, 0, 0)); - size_t input_it = mem.get_layout().get_linear_offset(t); - file_stream << mem_ptr[input_it] << std::endl; + for (cldnn::tensor::value_type z = 0; z < size.spatial[2]; ++z) { + for (cldnn::tensor::value_type y = 0; y < size.spatial[1]; ++y) { + for (cldnn::tensor::value_type x = 0; x < size.spatial[0]; ++x) { + cldnn::tensor t(cldnn::batch(b), cldnn::feature(f), cldnn::spatial(x, y, z, 0)); + size_t input_it = mem.get_layout().get_linear_offset(t); + file_stream << mem_ptr[input_it] << std::endl; + } } } } @@ -364,6 +465,7 @@ void network_impl::execute(const std::vector>& ev { log_memory_to_file(get_primitive(inst->id())->output_memory(), layer_name + "_dst_0"); } + get_engine().flush_network(_stream_id); #endif } diff --git a/inference-engine/thirdparty/clDNN/src/normalize.cpp b/inference-engine/thirdparty/clDNN/src/normalize.cpp index ea3e7519fd41db..4baf057adb988a 100644 --- a/inference-engine/thirdparty/clDNN/src/normalize.cpp +++ b/inference-engine/thirdparty/clDNN/src/normalize.cpp @@ -21,7 +21,7 @@ #include namespace cldnn { -primitive_type_id normalize_type_id() { +primitive_type_id normalize::type_id() { static primitive_type_base instance; return &instance; } @@ -55,10 +55,10 @@ std::string normalize_inst::to_string(normalize_node const& node) { } normalize_inst::typed_primitive_inst(network_impl& network, normalize_node const& node) : parent(network, node) { - /// Scale x dimension should be 1 (if all channels have the same scale) or equal to input feature size (one scale per channel). + /// Scale f dimension should be 1 (if all channels have the same scale) or equal to input feature size (one scale per channel). auto scale_layout = node.scale().get_output_layout(); auto scale_size = scale_layout.size; - auto scale_feature_size = scale_size.spatial[0]; + auto scale_feature_size = scale_size.feature[0]; auto input_layout = node.input().get_output_layout(); auto input_feature_size = input_layout.size.feature[0]; diff --git a/inference-engine/thirdparty/clDNN/src/one_hot.cpp b/inference-engine/thirdparty/clDNN/src/one_hot.cpp index c779beacc6b44f..9345b353fad326 100644 --- a/inference-engine/thirdparty/clDNN/src/one_hot.cpp +++ b/inference-engine/thirdparty/clDNN/src/one_hot.cpp @@ -21,12 +21,12 @@ #include namespace cldnn { -primitive_type_id one_hot_type_id() { +primitive_type_id one_hot::type_id() { static primitive_type_base instance; return &instance; } -static bool is_output_bfzyx(layout input, int32_t axis) { +static bool is_output_bfzyx(const layout& input, int32_t axis) { if (input.format == format::bfzyx) return true; if (axis == 4) diff --git a/inference-engine/thirdparty/clDNN/src/permute.cpp b/inference-engine/thirdparty/clDNN/src/permute.cpp index 9a60303f52c67c..c40a7132ec333e 100644 --- a/inference-engine/thirdparty/clDNN/src/permute.cpp +++ b/inference-engine/thirdparty/clDNN/src/permute.cpp @@ -26,7 +26,7 @@ namespace cldnn { -primitive_type_id permute_type_id() { +primitive_type_id permute::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/pooling.cpp b/inference-engine/thirdparty/clDNN/src/pooling.cpp index 51b01e847829c7..c07b18d6e68165 100644 --- a/inference-engine/thirdparty/clDNN/src/pooling.cpp +++ b/inference-engine/thirdparty/clDNN/src/pooling.cpp @@ -22,7 +22,7 @@ #include namespace cldnn { -primitive_type_id pooling_type_id() { +primitive_type_id pooling::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/prior_box.cpp b/inference-engine/thirdparty/clDNN/src/prior_box.cpp index c4ceb89350cb69..134a1fe489cca2 100644 --- a/inference-engine/thirdparty/clDNN/src/prior_box.cpp +++ b/inference-engine/thirdparty/clDNN/src/prior_box.cpp @@ -26,7 +26,7 @@ #include namespace cldnn { -primitive_type_id prior_box_type_id() { +primitive_type_id prior_box::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/program.cpp b/inference-engine/thirdparty/clDNN/src/program.cpp index 5c349aaa552efa..24496dc9072003 100644 --- a/inference-engine/thirdparty/clDNN/src/program.cpp +++ b/inference-engine/thirdparty/clDNN/src/program.cpp @@ -27,6 +27,17 @@ #include "program_impl.h" #include "sliding_window_utils.h" +#include "roi_pooling_inst.h" +#include "reorg_yolo_inst.h" +#include "eltwise_inst.h" +#include "softmax_inst.h" +#include "permute_inst.h" +#include "custom_gpu_primitive_inst.h" +#include "binary_convolution_inst.h" +#include "upsampling_inst.h" +#include "reshape_inst.h" +#include "activation_inst.h" +#include "scale_inst.h" #include "convolution_inst.h" #include "concatenation_inst.h" #include "crop_inst.h" @@ -61,6 +72,18 @@ #include #include #include +#include + +program::program(engine const& engine, topology const& topology, build_options const& options) + : _impl(engine.get()->build_program(*topology.get(), options).detach()) {} + +void program::retain() { + _impl->add_ref(); +} + +void program::release() { + _impl->release(); +} program_impl::program_impl(engine_impl& engine_ref, topology_impl const& topology, @@ -228,12 +251,12 @@ void program_impl::prepare_nodes(std::set> const& for (const auto& node : nodes_map) { auto node_ptr = node.second; if (node_ptr == nullptr) - throw error("NULL pointer in nodes_map.", CLDNN_ERROR); + throw std::runtime_error("NULL pointer in nodes_map."); // ToDo: avoid O(n^2) run time here (pass map instead of set?) bool found = false; for (const auto& src_node : nodes) { if (src_node == nullptr) - throw error("NULL pointer in nodes_map.", CLDNN_ERROR); + throw std::runtime_error("NULL pointer in nodes_map."); if (node.first == src_node->get_primitive()->id) { copy_node_dependencies(node_ptr.get(), src_node.get()); found = true; @@ -258,7 +281,7 @@ void program_impl::prepare_nodes(topology_impl const& topology) { for (const auto& node : nodes_map) { auto node_ptr = node.second.get(); if (node_ptr == nullptr) - throw error("NULL pointer in nodes_map.", CLDNN_ERROR); + throw std::runtime_error("NULL pointer in nodes_map."); add_node_dependencies(node_ptr); if (node_ptr->dependencies.size() == 0) { inputs.push_back(node_ptr); @@ -365,13 +388,18 @@ void program_impl::pre_optimize_graph(bool is_internal) { } layout_optimizer lo(output_size_handling_enabled); + set_layout_optimizer_attributes(lo); + + reorder_factory rf; if (options.get()->enabled()) { + apply_opt_pass(); + apply_opt_pass(lo); - apply_opt_pass(lo); + apply_opt_pass(lo, rf); - // this code should be moved to post compilation after kernel selector will support handling reorder bias - apply_opt_pass(lo); + // TODO this code should be moved to post compilation after kernel selector will support handling reorder bias + apply_opt_pass(rf); // passes regarding conv + eltwise optimizations @@ -388,7 +416,7 @@ void program_impl::pre_optimize_graph(bool is_internal) { apply_opt_pass(output_size_handling_enabled); - apply_opt_pass(lo.get_optimization_attributes().bfyx_f16_network); + apply_opt_pass(lo, options.get()->enabled()); if (options.get()->enabled()) { // Fuse conv + eltw after padding preparations @@ -404,8 +432,6 @@ void program_impl::pre_optimize_graph(bool is_internal) { apply_opt_pass(); } - apply_opt_pass(); - // try to fuse buffers (i.e. depth_concat in bfyx format) after padding calculations if (options.get()->enabled()) { apply_opt_pass(); @@ -419,10 +445,11 @@ void program_impl::post_optimize_graph(bool is_internal) { // input reorder for fully connected if necessary apply_opt_pass(); + reorder_factory rf; layout_optimizer lo; - apply_opt_pass(lo); + apply_opt_pass(rf); - apply_opt_pass(); // TODO: do we need it at this place also? + apply_opt_pass(lo, false, true); // TODO: do we need it at this place also? if (!is_internal) { // ToDo remove hidden dependencies from propagate_constants pass @@ -748,9 +775,8 @@ void program_impl::add_intermediate(program_node& node, } } if (!node_found) { - throw error("Trying to add intermediate node in between " + next.id() + " and dependecy " + prev.id() + - " but they are not connected in this way.", - CLDNN_ERROR); + throw std::runtime_error("Trying to add intermediate node in between " + next.id() + " and dependecy " + prev.id() + + " but they are not connected in this way."); } add_intermediate(node, next, idx, connect_int_node_with_old_dep, move_usrs_of_prev_to_node); } @@ -810,10 +836,10 @@ void program_impl::swap_names(program_node& node1, program_node& node2) { void program_impl::replace_all_usages(program_node& old_node, program_node& new_node) { auto itr = old_node.users.begin(); - bool end = (itr == old_node.users.end()); - while (!end) { + auto cnt = old_node.users.size(); + while (cnt != 0) { + cnt--; auto& usage = (*itr++); - end = (itr == old_node.users.end()); usage->replace_dependency(old_node, new_node); } } @@ -927,6 +953,53 @@ bool program_impl::extract_and_remove(program_node& node) { return true; } +void program_impl::fuse_nodes(program_node &fused_node, program_node &peer_node) { + auto peer_layout = peer_node.get_output_layout(); + fused_primitive_desc local_desc; + local_desc.prim = peer_node.get_primitive(); + local_desc.dep_start_idx = fused_node.get_dependencies().size(); + local_desc.output_layout = peer_layout; + local_desc.activation = activation_func::none; + if (!peer_node.get_fused_activations_funcs().empty()) { + if (peer_node.get_fused_activations_funcs().size() > 1) + CLDNN_ERROR_MESSAGE(peer_node.id(), "Fused primitive descriptor doesn't support > 1 activation functions in a peer node"); + + local_desc.activation = peer_node.get_fused_activations_funcs()[0]; + local_desc.activation_params = peer_node.get_fused_activations_params()[0]; + } + + cldnn::padding needed_padding = padding::max(peer_layout.data_padding, + fused_node.get_output_layout().data_padding); + + // Add new dependencies to the fused_node + for (size_t i = 0; i < peer_node.get_dependencies().size(); i++) { + auto& dep = peer_node.get_dependency(i); + if (dep.id() == fused_node.id()) + continue; + fused_node.dependencies.push_back(&dep); + local_desc.deps.push_back(dep.id()); + dep.users.push_back(&fused_node); + } + fused_node.add_fused_primitive(local_desc); + // This shouldn't happen, but who knows... + if (peer_node.has_fused_primitives()) { + fused_node.add_fused_primitives(peer_node.get_fused_primitives()); + } + add_optimized_primitive_info(peer_node.id(), { fused_node.id() }); + + // Remove all edges connected with peer node + while (peer_node.get_dependencies().size() > 0) { + auto& dep = peer_node.get_dependency(peer_node.get_dependencies().size() - 1); + remove_connection(dep, peer_node); + } + replace_all_usages(peer_node, fused_node); + + // Update output layout. Recalculation is not needed. + fused_node.merge_output_padding(needed_padding); + fused_node.set_output_layout(peer_layout, false); + fused_node.recalc_output_layout(true); +} + void program_impl::remove_nodes(std::list& to_remove) { for (auto const& node : to_remove) { if (node->is_input()) { @@ -968,17 +1041,13 @@ void program_impl::dump_program(const char* stage, bool with_full_info, std::function const& filter) const { std::string path = get_dir_path(options); - if (path.empty()) { + if (path.empty() || !with_full_info) { return; } std::ofstream graph(path + "cldnn_program_" + std::to_string(prog_id) + "_" + stage + ".graph"); dump_graph_init(graph, *this, filter); - if (!with_full_info) { - return; - } - graph.open(path + "cldnn_program_" + std::to_string(prog_id) + "_" + stage + ".info"); dump_graph_info(graph, *this, filter); @@ -1042,3 +1111,105 @@ const program_impl::graph_optimizer_info& program_impl::get_optimizer_passes_inf const program_impl::primitives_info& program_impl::get_primitives_info() const { return prim_info; } void program_impl::apply_opt_pass(base_pass& pass) { pm->run(*this, pass); } + +void program_impl::set_layout_optimizer_attributes(layout_optimizer& lo) { + // first pass to set layout optimization_attributes for topology + bool can_use_fsv32 = true; + bool can_use_f16 = true; + size_t total_conv_layers = 0; + size_t total_dw_conv_layers = 0; + size_t total_grouped_conv_layers = 0; + size_t opt_conv_layers_bfyx_f16 = 0; + size_t opt_conv_layers_bfzyx_f16 = 0; + size_t opt_deconv_layers_bfzyx_f16 = 0; + + for (auto& node : get_processing_order()) { + auto& prim = *node; + if (prim.type() == cldnn::convolution::type_id()) { + if (prim.as().get_primitive()->split() > 1) + lo.set_optimization_attribute(layout_optimizer::optimization_attributes_type::splitted_convolution, 1); + + if (prim.as().get_primitive()->groups > 1) + lo.set_optimization_attribute(layout_optimizer::optimization_attributes_type::group_convolution, 1); + + if (prim.as().get_primitive()->deformable_mode) + lo.set_optimization_attribute(layout_optimizer::optimization_attributes_type::deformable_convolution, 1); + + uint32_t ifm = static_cast(node->get_dependency(0).get_output_layout().size.feature[0]); + if (prim.as().get_primitive()->groups == ifm) + total_dw_conv_layers++; + else if (prim.as().get_primitive()->groups > 1 || prim.as().get_primitive()->split() > 1) + total_grouped_conv_layers++; + + if (lo.is_format_optimized(prim.as(), format::bfyx_f16)) + opt_conv_layers_bfyx_f16++; + + if (lo.is_format_optimized(prim.as(), format::bfzyx_f16)) + opt_conv_layers_bfzyx_f16++; + + total_conv_layers++; + } + if (prim.type() == cldnn::deconvolution::type_id()) { + if (lo.is_format_optimized(prim.as(), format::bfzyx_f16)) + opt_deconv_layers_bfzyx_f16 += 1; + } + + // list of layers that do not support yxfb or perform worse than bfyx + if (prim.type() == cldnn::detection_output::type_id() || prim.type() == cldnn::proposal::type_id() || + prim.type() == cldnn::roi_pooling::type_id() || prim.type() == cldnn::deconvolution::type_id() || + prim.type() == cldnn::upsampling::type_id() || prim.type() == cldnn::reorg_yolo::type_id()) + lo.set_optimization_attribute(layout_optimizer::optimization_attributes_type::bfyx_only_layer, 1); + + // Check if all layers in topology support fs_byx_fsv32 format + if (prim.is_in_data_flow() && + prim.type() != cldnn::convolution::type_id() && + prim.type() != cldnn::pooling::type_id() && + prim.type() != cldnn::eltwise::type_id() && + prim.type() != cldnn::fully_connected::type_id() && + prim.type() != cldnn::reorder::type_id() && + prim.type() != cldnn::permute::type_id() && + prim.type() != cldnn::reshape::type_id() && + prim.type() != cldnn::input_layout::type_id() && + prim.type() != cldnn::activation::type_id() && + prim.type() != cldnn::softmax::type_id()) { + can_use_fsv32 = false; + } + + if (prim.is_in_data_flow() && + prim.type() != cldnn::convolution::type_id() && + prim.type() != cldnn::activation::type_id() && + prim.type() != cldnn::pooling::type_id() && + prim.type() != cldnn::eltwise::type_id() && + prim.type() != cldnn::permute::type_id() && + prim.type() != cldnn::reshape::type_id() && + prim.type() != cldnn::detection_output::type_id() && + prim.type() != cldnn::binary_convolution::type_id() && + prim.type() != cldnn::quantize::type_id() && + prim.type() != cldnn::custom_gpu_primitive::type_id() && + prim.type() != cldnn::concatenation::type_id() && + prim.type() != cldnn::fully_connected::type_id() && + prim.type() != cldnn::reorder::type_id() && + prim.type() != cldnn::input_layout::type_id() && + prim.type() != cldnn::softmax::type_id() && + prim.type() != cldnn::prior_box::type_id() && + prim.type() != cldnn::scale::type_id()) + can_use_f16 = false; + } + + // Due to fact that single winograd convolution is faster than bfyx_f16 and + // using them together leads do redundant reorders, whole topology switch + // will be performed if at least half of layers can use bfyx_f16. + bool should_use_bfyx_f16_conv = can_use_f16 && + ((opt_conv_layers_bfyx_f16 / static_cast(total_conv_layers)) > 0.5f) && + total_conv_layers > 11 && + total_grouped_conv_layers == 0; // conv with groups are not supported correctly yet + + if (can_use_fsv32) + lo.set_optimization_attribute(layout_optimizer::optimization_attributes_type::only_fsv32_layers, 1); + + if (should_use_bfyx_f16_conv) + lo.set_optimization_attribute(layout_optimizer::optimization_attributes_type::bfyx_f16_network, 1); + + if (opt_conv_layers_bfzyx_f16 >= 1 || opt_deconv_layers_bfzyx_f16 >= 1) + lo.set_optimization_attribute(layout_optimizer::optimization_attributes_type::bfzyx_f16_network, 1); +} diff --git a/inference-engine/thirdparty/clDNN/src/program_helpers.cpp b/inference-engine/thirdparty/clDNN/src/program_helpers.cpp index 07d1bb2ccbf3b3..a765fa1cc3e38f 100644 --- a/inference-engine/thirdparty/clDNN/src/program_helpers.cpp +++ b/inference-engine/thirdparty/clDNN/src/program_helpers.cpp @@ -26,7 +26,7 @@ namespace cldnn { // helper function for merging the weights/biases buffers on cpu side for depthwise separable convolution optimization void program_helpers::merge_buffers(engine_impl& engine, program_node& node, - layout target_layout, + const layout& target_layout, size_t begin_offset, size_t end_offset) { memory_impl::ptr data_to_allocate = engine.allocate_memory(target_layout, 0); @@ -87,17 +87,19 @@ std::pair program_helpers::are_layouts_identical(layout const& l1, l (l1.format == format::fs_b_yx_fsv32 && l2.format != format::fs_b_yx_fsv32) || (l2.format == format::fs_b_yx_fsv32 && l1.format != format::fs_b_yx_fsv32) || (l1.format == format::bfyx_f16 && l2.format != format::bfyx_f16) || - (l2.format == format::bfyx_f16 && l1.format != format::bfyx_f16)) + (l2.format == format::bfyx_f16 && l1.format != format::bfyx_f16) || + (l1.format == format::bfzyx_f16 && l2.format != format::bfzyx_f16) || + (l2.format == format::bfzyx_f16 && l1.format != format::bfzyx_f16)) return {false, false}; auto l1_pitch = l1.get_pitches(); auto l2_pitch = l2.get_pitches(); // ignore pitches which will never be used (for dims with size == 1) - for (size_t i = 0; i < CLDNN_TENSOR_DIM_MAX; ++i) + for (size_t i = 0; i < tensor_dim_max; ++i) if (l1.size.raw[i] == 1) l1_pitch.raw[i] = 0; - for (size_t i = 0; i < CLDNN_TENSOR_DIM_MAX; ++i) + for (size_t i = 0; i < tensor_dim_max; ++i) if (l2.size.raw[i] == 1) l2_pitch.raw[i] = 0; diff --git a/inference-engine/thirdparty/clDNN/src/program_node.cpp b/inference-engine/thirdparty/clDNN/src/program_node.cpp index 1805d830f2409d..28e551f8a6d212 100644 --- a/inference-engine/thirdparty/clDNN/src/program_node.cpp +++ b/inference-engine/thirdparty/clDNN/src/program_node.cpp @@ -103,6 +103,23 @@ std::unique_ptr program_node::desc_to_json() const { node_info->add("in data flow", bool_to_str(data_flow)); node_info->add("output", bool_to_str(output)); + + json_composite fused_nodes_info; + size_t index = 0; + for (auto& fused_desc : get_fused_primitives()) { + json_composite fused_node_info; + fused_node_info.add("id", fused_desc.prim->id); + fused_node_info.add("dependencies", fused_desc.deps); + fused_node_info.add("dep start_idx", fused_desc.dep_start_idx); + json_composite output_layout_info; + output_layout_info.add("data type", dt_to_str(fused_desc.output_layout.data_type)); + output_layout_info.add("format", fmt_to_str(output_layout.format)); + output_layout_info.add("size", output_layout.size.to_string()); + fused_node_info.add("output layout", output_layout_info); + fused_nodes_info.add("fused primitive idx " + std::to_string(index++), fused_node_info); + } + node_info->add("fused primitives", fused_nodes_info); + std::vector deps_ptrs; { bool empty = true; @@ -165,7 +182,9 @@ bool program_node::is_detached(bool whole_branch) { return true; } -layout program_node::calc_output_layout() const { return type()->calc_output_layout(*this); } +layout program_node::calc_output_layout() const { + return type()->calc_output_layout(*this); +} layout program_node::get_output_layout(bool invalidate_users_if_changed) { if (valid_output_layout) @@ -189,7 +208,7 @@ layout program_node::get_non_padded_output_layout(bool invalidate_users_if_chang return result; } -bool program_node::set_output_layout(layout new_layout, bool invalidate_users_if_changed) { +bool program_node::set_output_layout(layout& new_layout, bool invalidate_users_if_changed) { merge_output_padding(new_layout.data_padding); new_layout.data_padding = output_layout.data_padding; bool changed = (new_layout != output_layout); @@ -202,7 +221,8 @@ bool program_node::set_output_layout(layout new_layout, bool invalidate_users_if } bool program_node::recalc_output_layout(bool invalidate_users_if_changed) { - return set_output_layout(calc_output_layout(), invalidate_users_if_changed); + auto output_layout = calc_output_layout(); + return set_output_layout(output_layout, invalidate_users_if_changed); } bool program_node::has_padded_dependency() { diff --git a/inference-engine/thirdparty/clDNN/src/proposal.cpp b/inference-engine/thirdparty/clDNN/src/proposal.cpp index d7cd57a65821f8..b30f5544e04877 100644 --- a/inference-engine/thirdparty/clDNN/src/proposal.cpp +++ b/inference-engine/thirdparty/clDNN/src/proposal.cpp @@ -32,7 +32,7 @@ static void generate_anchors(unsigned base_size, bool shift_anchors, bool round_ratios); -primitive_type_id proposal_type_id() { +primitive_type_id proposal::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/pyramid_roi_align.cpp b/inference-engine/thirdparty/clDNN/src/pyramid_roi_align.cpp index 471128adee78f5..fe4fdd8105b789 100644 --- a/inference-engine/thirdparty/clDNN/src/pyramid_roi_align.cpp +++ b/inference-engine/thirdparty/clDNN/src/pyramid_roi_align.cpp @@ -20,7 +20,7 @@ #include namespace cldnn { -primitive_type_id pyramid_roi_align_type_id() { +primitive_type_id pyramid_roi_align::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/quantize.cpp b/inference-engine/thirdparty/clDNN/src/quantize.cpp index 47d5ba2bbdf5e2..c32016890c8067 100644 --- a/inference-engine/thirdparty/clDNN/src/quantize.cpp +++ b/inference-engine/thirdparty/clDNN/src/quantize.cpp @@ -24,7 +24,7 @@ #include namespace cldnn { -primitive_type_id quantize_type_id() { +primitive_type_id quantize::type_id() { static primitive_type_base instance; return &instance; } @@ -33,16 +33,16 @@ layout quantize_inst::calc_output_layout(quantize_node const& node) { auto desc = node.get_primitive(); auto input_layout = node.input().get_output_layout(); - auto input_format = input_layout.format; + auto output_format = input_layout.format; + auto out_dt = input_layout.data_type; + if (node.get_primitive()->output_data_type) + out_dt = *node.get_primitive()->output_data_type; - bool is_packed_binarization = desc->levels == 2 && - node.get_users().size() == 1 && - node.get_users().front()->is_type(); + if (out_dt == data_types::bin) { + output_format = format::b_fs_yx_32fp; + } - if (is_packed_binarization) - return layout{data_types::bin, format::b_fs_yx_32fp, input_layout.size}; - else - return layout{input_layout.data_type, input_format, input_layout.size}; + return layout{out_dt, output_format, input_layout.size}; } std::string quantize_inst::to_string(quantize_node const& node) { diff --git a/inference-engine/thirdparty/clDNN/src/reduce.cpp b/inference-engine/thirdparty/clDNN/src/reduce.cpp index 640f227befd286..d650948ad7cc01 100644 --- a/inference-engine/thirdparty/clDNN/src/reduce.cpp +++ b/inference-engine/thirdparty/clDNN/src/reduce.cpp @@ -24,7 +24,7 @@ #include namespace cldnn { -primitive_type_id reduce_type_id() { +primitive_type_id reduce::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/region_yolo.cpp b/inference-engine/thirdparty/clDNN/src/region_yolo.cpp index d6e273582a559f..c8b347d5a7a355 100644 --- a/inference-engine/thirdparty/clDNN/src/region_yolo.cpp +++ b/inference-engine/thirdparty/clDNN/src/region_yolo.cpp @@ -20,7 +20,7 @@ #include namespace cldnn { -primitive_type_id region_yolo_type_id() { +primitive_type_id region_yolo::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/reorder.cpp b/inference-engine/thirdparty/clDNN/src/reorder.cpp index cbdee7dff7d30e..3c9e9ec74a12c1 100644 --- a/inference-engine/thirdparty/clDNN/src/reorder.cpp +++ b/inference-engine/thirdparty/clDNN/src/reorder.cpp @@ -25,7 +25,7 @@ namespace cldnn { -primitive_type_id reorder_type_id() { +primitive_type_id reorder::type_id() { static primitive_type_base instance; return &instance; } @@ -38,6 +38,10 @@ layout reorder_inst::calc_output_layout(reorder_node const& node) { auto ofmt = node.get_primitive()->output_format; auto op = node.get_primitive()->output_padding; + if (ofmt == format::any) { + ofmt = ifmt; + } + if (ofmt.is_winograd() && ifmt.is_winograd()) { if (ofmt == ifmt) return layout(odt, ofmt, input_layout.size, op); @@ -151,7 +155,7 @@ layout reorder_inst::calc_output_layout(reorder_node const& node) { } if (ofmt == format::bs_xs_xsv8_bsv8 || ofmt == format::bs_xs_xsv8_bsv16 || ofmt == format::bs_x_bsv16 || - ofmt == format::bfzyx || ifmt == format::bfzyx) { + ofmt == format::bfzyx || ifmt == format::bfzyx || ofmt == format::bfzyx_f16 || ifmt == format::bfzyx_f16) { return layout(odt, ofmt, input_layout.size.transform(ofmt, 1), op); } else if (ofmt != ifmt && (ofmt == format::bfwzyx || ifmt == format::bfwzyx)) { // TODO Shouldn't transform be called every time ifmt != ofmt? diff --git a/inference-engine/thirdparty/clDNN/src/reorg_yolo.cpp b/inference-engine/thirdparty/clDNN/src/reorg_yolo.cpp index 9c07234d87894e..bd28abaa59204f 100644 --- a/inference-engine/thirdparty/clDNN/src/reorg_yolo.cpp +++ b/inference-engine/thirdparty/clDNN/src/reorg_yolo.cpp @@ -20,7 +20,7 @@ #include namespace cldnn { -primitive_type_id reorg_yolo_type_id() { +primitive_type_id reorg_yolo::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/reshape.cpp b/inference-engine/thirdparty/clDNN/src/reshape.cpp index ea43f4259a0335..a7e07f492ba283 100644 --- a/inference-engine/thirdparty/clDNN/src/reshape.cpp +++ b/inference-engine/thirdparty/clDNN/src/reshape.cpp @@ -24,7 +24,7 @@ namespace cldnn { -primitive_type_id reshape_type_id() { +primitive_type_id reshape::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/reverse_sequence.cpp b/inference-engine/thirdparty/clDNN/src/reverse_sequence.cpp index afa6bdf78ff625..df6c68fb3f3145 100644 --- a/inference-engine/thirdparty/clDNN/src/reverse_sequence.cpp +++ b/inference-engine/thirdparty/clDNN/src/reverse_sequence.cpp @@ -22,7 +22,7 @@ #include namespace cldnn { -primitive_type_id reverse_sequence_type_id() { +primitive_type_id reverse_sequence::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/roi_pooling.cpp b/inference-engine/thirdparty/clDNN/src/roi_pooling.cpp index 3967e0fd341e5a..6cbaf400317f8f 100644 --- a/inference-engine/thirdparty/clDNN/src/roi_pooling.cpp +++ b/inference-engine/thirdparty/clDNN/src/roi_pooling.cpp @@ -21,7 +21,7 @@ #include namespace cldnn { -primitive_type_id roi_pooling_type_id() { +primitive_type_id roi_pooling::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/scale.cpp b/inference-engine/thirdparty/clDNN/src/scale.cpp index eef446d2aa004b..ab62bf898ddda0 100644 --- a/inference-engine/thirdparty/clDNN/src/scale.cpp +++ b/inference-engine/thirdparty/clDNN/src/scale.cpp @@ -21,7 +21,7 @@ #include namespace cldnn { -primitive_type_id scale_type_id() { +primitive_type_id scale::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/scale_grad_input.cpp b/inference-engine/thirdparty/clDNN/src/scale_grad_input.cpp index 3620b55c14a7c6..471e526556cb5f 100644 --- a/inference-engine/thirdparty/clDNN/src/scale_grad_input.cpp +++ b/inference-engine/thirdparty/clDNN/src/scale_grad_input.cpp @@ -21,7 +21,7 @@ #include namespace cldnn { -primitive_type_id scale_grad_input_type_id() { +primitive_type_id scale_grad_input::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/scale_grad_weights.cpp b/inference-engine/thirdparty/clDNN/src/scale_grad_weights.cpp index 4a65ba54db21f7..e9063358c53bd2 100644 --- a/inference-engine/thirdparty/clDNN/src/scale_grad_weights.cpp +++ b/inference-engine/thirdparty/clDNN/src/scale_grad_weights.cpp @@ -21,7 +21,7 @@ #include namespace cldnn { -primitive_type_id scale_grad_weights_type_id() { +primitive_type_id scale_grad_weights::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/select.cpp b/inference-engine/thirdparty/clDNN/src/select.cpp index 14032c0cf2d91b..4264eef76e100e 100644 --- a/inference-engine/thirdparty/clDNN/src/select.cpp +++ b/inference-engine/thirdparty/clDNN/src/select.cpp @@ -22,7 +22,7 @@ #include namespace cldnn { -primitive_type_id select_type_id() { +primitive_type_id select::type_id() { static primitive_type_base().port; const int in_buf = m_gm.metadata(eh->srcNode()).get().rc; - m_agents.back()->in_buffer_ids[in_port] = in_buf; + agents_data.back().in_buffer_ids[in_port] = in_buf; grab_mat_nh(eh->srcNode()); } } // FIXME: Assumption that all operation outputs MUST be connected - m_agents.back()->out_buffer_ids.resize(nh->outEdges().size(), -1); + agents_data.back().out_buffer_ids.resize(nh->outEdges().size(), -1); for (auto eh : nh->outEdges()) { const auto& data = m_gm.metadata(eh->dstNode()).get(); const auto out_port = m_gm.metadata(eh).get().port; const int out_buf = data.rc; - m_agents.back()->out_buffer_ids[out_port] = out_buf; + agents_data.back().out_buffer_ids[out_port] = out_buf; if (data.shape == GShape::GMAT) grab_mat_nh(eh->dstNode()); } if (fu.k.m_scratch) - m_scratch_users.push_back(last_agent); + scratch_users.push_back(last_agent); last_agent++; break; } @@ -776,12 +789,50 @@ cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g, } // Check that IDs form a continiuos set (important for further indexing) - GAPI_Assert(m_id_map.size() > 0); - GAPI_Assert(m_id_map.size() == static_cast(mat_count)); + GAPI_Assert(id_map.size() > 0); + GAPI_Assert(id_map.size() == static_cast(mat_count)); + + return FluidGraphInputData {std::move(agents_data), std::move(scratch_users), std::move(id_map), std::move(all_gmat_ids), mat_count}; +} + +cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g, + const cv::gimpl::FluidGraphInputData &traverse_res, + const std::vector &outputRois) + : m_g(g), m_gm(m_g) +{ + GConstFluidModel fg(m_g); + + auto tie_traverse_res = [&traverse_res](){ + auto& r = traverse_res; + return std::tie(r.m_scratch_users, r.m_id_map, r.m_all_gmat_ids, r.m_mat_count); + }; + + auto tie_this = [this](){ + return std::tie(m_scratch_users, m_id_map, m_all_gmat_ids, m_num_int_buffers); + }; + + tie_this() = tie_traverse_res(); + + auto create_fluid_agent = [&g](agent_data_t const& agent_data) -> std::unique_ptr { + std::unique_ptr agent_ptr; + switch (agent_data.kind) + { + case GFluidKernel::Kind::Filter: agent_ptr.reset(new FluidFilterAgent(g, agent_data.nh)); break; + case GFluidKernel::Kind::Resize: agent_ptr.reset(new FluidResizeAgent(g, agent_data.nh)); break; + case GFluidKernel::Kind::NV12toRGB: agent_ptr.reset(new FluidNV12toRGBAgent(g, agent_data.nh)); break; + default: GAPI_Assert(false); + } + std::tie(agent_ptr->in_buffer_ids, agent_ptr->out_buffer_ids) = std::tie(agent_data.in_buffer_ids, agent_data.out_buffer_ids); + return agent_ptr; + }; + + for (auto const& agent_data : traverse_res.m_agents_data){ + m_agents.push_back(create_fluid_agent(agent_data)); + } // Actually initialize Fluid buffers - GAPI_LOG_INFO(NULL, "Initializing " << mat_count << " fluid buffer(s)" << std::endl); - m_num_int_buffers = mat_count; + GAPI_LOG_INFO(NULL, "Initializing " << m_num_int_buffers << " fluid buffer(s)" << std::endl); + const std::size_t num_scratch = m_scratch_users.size(); m_buffers.resize(m_num_int_buffers + num_scratch); @@ -847,6 +898,12 @@ cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g, makeReshape(outputRois); + GAPI_LOG_INFO(NULL, "Internal buffers: " << std::fixed << std::setprecision(2) << static_cast(total_buffers_size())/1024 << " KB\n"); +} + +std::size_t cv::gimpl::GFluidExecutable::total_buffers_size() const +{ + GConstFluidModel fg(m_g); std::size_t total_size = 0; for (const auto &i : ade::util::indexed(m_buffers)) { @@ -854,7 +911,7 @@ cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g, const auto idx = ade::util::index(i); const auto b = ade::util::value(i); if (idx >= m_num_int_buffers || - fg.metadata(m_all_gmat_ids[idx]).get().internal == true) + fg.metadata(m_all_gmat_ids.at(idx)).get().internal == true) { GAPI_Assert(b.priv().size() > 0); } @@ -863,7 +920,7 @@ cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g, // (There can be non-zero sized const border buffer allocated in such buffers) total_size += b.priv().size(); } - GAPI_LOG_INFO(NULL, "Internal buffers: " << std::fixed << std::setprecision(2) << static_cast(total_size)/1024 << " KB\n"); + return total_size; } namespace @@ -1207,6 +1264,11 @@ void cv::gimpl::GFluidExecutable::packArg(cv::GArg &in_arg, const cv::GArg &op_a void cv::gimpl::GFluidExecutable::run(std::vector &&input_objs, std::vector &&output_objs) +{ + run(input_objs, output_objs); +} +void cv::gimpl::GFluidExecutable::run(std::vector &input_objs, + std::vector &output_objs) { // Bind input buffers from parameters for (auto& it : input_objs) bindInArg(it.first, it.second); @@ -1280,6 +1342,34 @@ void cv::gimpl::GFluidExecutable::run(std::vector &&input_objs, } } +cv::gimpl::GParallelFluidExecutable::GParallelFluidExecutable(const ade::Graph &g, + const FluidGraphInputData &graph_data, + const std::vector ¶llelOutputRois, + const decltype(parallel_for) &pfor) +: parallel_for(pfor) +{ + for (auto&& rois : parallelOutputRois){ + tiles.emplace_back(new GFluidExecutable(g, graph_data, rois.rois)); + } +} + + +void cv::gimpl::GParallelFluidExecutable::reshape(ade::Graph&, const GCompileArgs& ) +{ + //TODO: implement ? + GAPI_Assert(false && "Not Implemented;"); +} + +void cv::gimpl::GParallelFluidExecutable::run(std::vector &&input_objs, + std::vector &&output_objs) +{ + parallel_for(tiles.size(), [&, this](std::size_t index){ + GAPI_Assert((bool)tiles[index]); + tiles[index]->run(input_objs, output_objs); + }); +} + + // FIXME: these passes operate on graph global level!!! // Need to fix this for heterogeneous (island-based) processing void GFluidBackendImpl::addBackendPasses(ade::ExecutionEngineSetupContext &ectx) diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbackend.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbackend.hpp index d540999783f098..7923f0c0037486 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbackend.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbackend.hpp @@ -10,10 +10,10 @@ // FIXME? Actually gfluidbackend.hpp is not included anywhere // and can be placed in gfluidbackend.cpp -#include "opencv2/gapi/garg.hpp" -#include "opencv2/gapi/gproto.hpp" -#include "opencv2/gapi/fluid/gfluidkernel.hpp" -#include "opencv2/gapi/fluid/gfluidbuffer.hpp" +#include +#include +#include +#include // PRIVATE STUFF! #include "backends/common/gbackend.hpp" @@ -51,6 +51,13 @@ struct FluidData gapi::fluid::BorderOpt border; }; +struct agent_data_t { + GFluidKernel::Kind kind; + ade::NodeHandle nh; + std::vector in_buffer_ids; + std::vector out_buffer_ids; + }; + struct FluidAgent { public: @@ -96,8 +103,23 @@ struct FluidAgent virtual std::pair linesReadAndnextWindow(std::size_t inPort) const = 0; }; +//helper data structure for accumulating graph traversal/analysis data +struct FluidGraphInputData { + + std::vector m_agents_data; + std::vector m_scratch_users; + std::unordered_map m_id_map; // GMat id -> buffer idx map + std::map m_all_gmat_ids; + + std::size_t m_mat_count; +}; +//local helper function to traverse the graph once and pass the results to multiple instances of GFluidExecutable +FluidGraphInputData fluidExtractInputDataFromGraph(const ade::Graph &m_g, const std::vector &nodes); + class GFluidExecutable final: public GIslandExecutable { + GFluidExecutable(const GFluidExecutable&) = delete; // due std::unique_ptr in members list + const ade::Graph &m_g; GModel::ConstGraph m_gm; @@ -121,15 +143,40 @@ class GFluidExecutable final: public GIslandExecutable void initBufferRois(std::vector& readStarts, std::vector& rois, const std::vector &out_rois); void makeReshape(const std::vector& out_rois); + std::size_t total_buffers_size() const; public: - GFluidExecutable(const ade::Graph &g, - const std::vector &nodes, - const std::vector &outputRois); - virtual inline bool canReshape() const override { return true; } virtual void reshape(ade::Graph& g, const GCompileArgs& args) override; + virtual void run(std::vector &&input_objs, + std::vector &&output_objs) override; + + void run(std::vector &input_objs, + std::vector &output_objs); + + + GFluidExecutable(const ade::Graph &g, + const FluidGraphInputData &graph_data, + const std::vector &outputRois); +}; + + +class GParallelFluidExecutable final: public GIslandExecutable { + GParallelFluidExecutable(const GParallelFluidExecutable&) = delete; // due std::unique_ptr in members list + + std::vector> tiles; + decltype(GFluidParallelFor::parallel_for) parallel_for; +public: + GParallelFluidExecutable(const ade::Graph &g, + const FluidGraphInputData &graph_data, + const std::vector ¶llelOutputRois, + const decltype(parallel_for) &pfor); + + + virtual inline bool canReshape() const override { return false; } + virtual void reshape(ade::Graph& g, const GCompileArgs& args) override; + virtual void run(std::vector &&input_objs, std::vector &&output_objs) override; }; diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbuffer.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbuffer.cpp index 0bfdd66349678d..f0dc6edb7810fa 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbuffer.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbuffer.cpp @@ -9,12 +9,12 @@ #include // hex, dec (debug) -#include "opencv2/gapi/own/convert.hpp" -#include "opencv2/gapi/own/types.hpp" +#include +#include -#include "opencv2/gapi/fluid/gfluidbuffer.hpp" +#include #include "backends/fluid/gfluidbuffer_priv.hpp" -#include "opencv2/gapi/opencv_includes.hpp" +#include #include "backends/fluid/gfluidutils.hpp" // saturate diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidcore.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidcore.cpp index b58ec0765ccbcb..49d6d2e233c0a3 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidcore.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidcore.cpp @@ -8,16 +8,16 @@ #include "precomp.hpp" -#include "opencv2/gapi/own/assert.hpp" -#include "opencv2/core/traits.hpp" -#include "opencv2/core/hal/hal.hpp" -#include "opencv2/core/hal/intrin.hpp" +#include +#include +#include +#include -#include "opencv2/gapi/core.hpp" +#include -#include "opencv2/gapi/fluid/gfluidbuffer.hpp" -#include "opencv2/gapi/fluid/gfluidkernel.hpp" -#include "opencv2/gapi/fluid/core.hpp" +#include +#include +#include #include "gfluidbuffer_priv.hpp" #include "gfluidbackend.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc.cpp index 49f1824a9d4783..dfbce1e9fca3ff 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc.cpp @@ -8,18 +8,18 @@ #include "precomp.hpp" -#include "opencv2/gapi/own/assert.hpp" -#include "opencv2/core/traits.hpp" -#include "opencv2/imgproc/types_c.h" +#include +#include +#include -#include "opencv2/gapi/core.hpp" -#include "opencv2/gapi/imgproc.hpp" +#include +#include -#include "opencv2/gapi/own/types.hpp" +#include -#include "opencv2/gapi/fluid/gfluidbuffer.hpp" -#include "opencv2/gapi/fluid/gfluidkernel.hpp" -#include "opencv2/gapi/fluid/imgproc.hpp" +#include +#include +#include #include "gfluidbuffer_priv.hpp" #include "gfluidbackend.hpp" @@ -27,8 +27,8 @@ #include "gfluidimgproc_func.hpp" -#include "opencv2/imgproc/hal/hal.hpp" -#include "opencv2/core/hal/intrin.hpp" +#include +#include #include #include @@ -1683,6 +1683,121 @@ GAPI_FLUID_KERNEL(GFluidMedianBlur, cv::gapi::imgproc::GMedianBlur, false) } }; +GAPI_FLUID_KERNEL(GFluidRGB2YUV422, cv::gapi::imgproc::GRGB2YUV422, false) +{ + static const int Window = 1; + static const auto Kind = cv::GFluidKernel::Kind::Filter; + + static void run(const cv::gapi::fluid::View& in, + cv::gapi::fluid::Buffer& out) + { + const auto *src = in.InLine(0); + auto *dst = out.OutLine(); + + run_rgb2yuv422_impl(dst, src, in.length()); + } +}; + +GAPI_FLUID_KERNEL(GFluidRGB2HSV, cv::gapi::imgproc::GRGB2HSV, true) +{ + static const int Window = 1; + static const auto Kind = cv::GFluidKernel::Kind::Filter; + + static void run(const cv::gapi::fluid::View& in, + cv::gapi::fluid::Buffer& out, + cv::gapi::fluid::Buffer& scratch) + { + const auto *src = in.InLine(0); + auto *dst = out.OutLine(); + + auto* sdiv_table = scratch.OutLine(0); + auto* hdiv_table = sdiv_table + 256; + + run_rgb2hsv_impl(dst, src, sdiv_table, hdiv_table, in.length()); + } + + static void initScratch(const cv::GMatDesc& /* in */, + cv::gapi::fluid::Buffer& scratch) + { + const int hsv_shift = 12; + + cv::GMatDesc desc; + desc.chan = 1; + desc.depth = CV_32S; + desc.size = cv::gapi::own::Size(512, 1); + + cv::gapi::fluid::Buffer buffer(desc); + scratch = std::move(buffer); + + auto* sdiv_table = scratch.OutLine(0); + auto* hdiv_table = sdiv_table + 256; + + sdiv_table[0] = hdiv_table[0] = 0; + for(int i = 1; i < 256; i++ ) + { + sdiv_table[i] = cv::saturate_cast((255 << hsv_shift)/(1.*i)); + hdiv_table[i] = cv::saturate_cast((180 << hsv_shift)/(6.*i)); + } + + } + + static void resetScratch(cv::gapi::fluid::Buffer& /* scratch */) + { + } +}; + +GAPI_FLUID_KERNEL(GFluidBayerGR2RGB, cv::gapi::imgproc::GBayerGR2RGB, false) +{ + static const int Window = 3; + static const int LPI = 2; + + static void run(const cv::gapi::fluid::View& in, + cv::gapi::fluid::Buffer& out) + { + const int height = in.meta().size.height; + const int border_size = 1; + const int width = in.length(); + + constexpr int num_lines = LPI + 2 * border_size; + const uchar* src[num_lines]; + uchar* dst[LPI]; + + for (int i = 0; i < LPI; ++i) + { + dst[i] = out.OutLine(i); + } + + for (int i = 0; i < num_lines; ++i) + { + src[i] = in.InLine(i - 1); + } + + if (in.y() == -1) + { + run_bayergr2rgb_bg_impl(dst[1], src + border_size, width); + std::memcpy(dst[0], dst[1], width * 3); + } + else if (in.y() == height - LPI - 2 * border_size + 1) + { + run_bayergr2rgb_gr_impl(dst[0], src, width); + std::memcpy(dst[1], dst[0], width * 3); + } + else + { + run_bayergr2rgb_gr_impl(dst[0], src, width); + run_bayergr2rgb_bg_impl(dst[1], src + border_size, width); + } + } + + static cv::gapi::fluid::Border getBorder(const cv::GMatDesc&) + { + int borderType = cv::BORDER_CONSTANT; + auto borderValue = cv::Scalar(); + + return { borderType, borderValue }; + } +}; + } // namespace fliud } // namespace gapi } // namespace cv @@ -1709,6 +1824,9 @@ cv::gapi::GKernelPackage cv::gapi::imgproc::fluid::kernels() , GFluidGaussBlur , GFluidSobel , GFluidSobelXY + , GFluidRGB2YUV422 + , GFluidRGB2HSV + , GFluidBayerGR2RGB #if 0 , GFluidCanny -- not fluid (?) , GFluidEqualizeHist -- not fluid diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.dispatch.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.dispatch.cpp index 835fb8203ce9fb..3ea4676dde6213 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.dispatch.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.dispatch.cpp @@ -12,8 +12,8 @@ #include "gfluidutils.hpp" -#include "opencv2/core/cvdef.h" -#include "opencv2/core/hal/intrin.hpp" +#include +#include #include #include @@ -43,7 +43,35 @@ void run_rgb2gray_impl(uchar out[], const uchar in[], int width, //-------------------------------------- // -// Fluid kernels: RGB-to-YUV, YUV-to-RGB +// Fluid kernels: RGB-to-HSV +// +//-------------------------------------- + +void run_rgb2hsv_impl(uchar out[], const uchar in[], const int sdiv_table[], + const int hdiv_table[], int width) +{ + CV_CPU_DISPATCH(run_rgb2hsv_impl, (out, in, sdiv_table, hdiv_table, width), CV_CPU_DISPATCH_MODES_ALL); +} + +//-------------------------------------- +// +// Fluid kernels: RGB-to-BayerGR +// +//-------------------------------------- + +void run_bayergr2rgb_bg_impl(uchar out[], const uchar **in, int width) +{ + CV_CPU_DISPATCH(run_bayergr2rgb_bg_impl, (out, in, width), CV_CPU_DISPATCH_MODES_ALL); +} + +void run_bayergr2rgb_gr_impl(uchar out[], const uchar **in, int width) +{ + CV_CPU_DISPATCH(run_bayergr2rgb_gr_impl, (out, in, width), CV_CPU_DISPATCH_MODES_ALL); +} + +//-------------------------------------- +// +// Fluid kernels: RGB-to-YUV, RGB-to-YUV422, YUV-to-RGB // //-------------------------------------- @@ -57,6 +85,11 @@ void run_yuv2rgb_impl(uchar out[], const uchar in[], int width, const float coef CV_CPU_DISPATCH(run_yuv2rgb_impl, (out, in, width, coef), CV_CPU_DISPATCH_MODES_ALL); } +void run_rgb2yuv422_impl(uchar out[], const uchar in[], int width) +{ + CV_CPU_DISPATCH(run_rgb2yuv422_impl, (out, in, width), CV_CPU_DISPATCH_MODES_ALL); +} + //------------------------- // // Fluid kernels: sepFilter diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.hpp index 191ac0834449f8..b89ccd89882813 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.hpp @@ -8,7 +8,7 @@ #if !defined(GAPI_STANDALONE) -#include "opencv2/core.hpp" +#include namespace cv { namespace gapi { @@ -25,7 +25,26 @@ void run_rgb2gray_impl(uchar out[], const uchar in[], int width, //-------------------------------------- // -// Fluid kernels: RGB-to-YUV, YUV-to-RGB +// Fluid kernels: RGB-to-HSV +// +//-------------------------------------- + +void run_rgb2hsv_impl(uchar out[], const uchar in[], const int sdiv_table[], + const int hdiv_table[], int width); + +//-------------------------------------- +// +// Fluid kernels: RGB-to-BayerGR +// +//-------------------------------------- + +void run_bayergr2rgb_bg_impl(uchar out[], const uchar **in, int width); + +void run_bayergr2rgb_gr_impl(uchar out[], const uchar **in, int width); + +//-------------------------------------- +// +// Fluid kernels: RGB-to-YUV,RGB-to-YUV422, YUV-to-RGB // //-------------------------------------- @@ -33,6 +52,8 @@ void run_rgb2yuv_impl(uchar out[], const uchar in[], int width, const float coef void run_yuv2rgb_impl(uchar out[], const uchar in[], int width, const float coef[4]); +void run_rgb2yuv422_impl(uchar out[], const uchar in[], int width); + //------------------------- // // Fluid kernels: sepFilter diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.simd.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.simd.hpp index 397d3b0df0353b..b5c5147ff1ff32 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.simd.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidimgproc_func.simd.hpp @@ -47,7 +47,26 @@ void run_rgb2gray_impl(uchar out[], const uchar in[], int width, //-------------------------------------- // -// Fluid kernels: RGB-to-YUV, YUV-to-RGB +// Fluid kernels: RGB-to-HSV +// +//-------------------------------------- + +void run_rgb2hsv_impl(uchar out[], const uchar in[], const int sdiv_table[], + const int hdiv_table[], int width); + +//-------------------------------------- +// +// Fluid kernels: RGB-to-BayerGR +// +//-------------------------------------- + +void run_bayergr2rgb_bg_impl(uchar out[], const uchar **in, int width); + +void run_bayergr2rgb_gr_impl(uchar out[], const uchar **in, int width); + +//-------------------------------------- +// +// Fluid kernels: RGB-to-YUV, RGB-to-YUV422, YUV-to-RGB // //-------------------------------------- @@ -55,6 +74,8 @@ void run_rgb2yuv_impl(uchar out[], const uchar in[], int width, const float coef void run_yuv2rgb_impl(uchar out[], const uchar in[], int width, const float coef[4]); +void run_rgb2yuv422_impl(uchar out[], const uchar in[], int width); + //------------------------- // // Fluid kernels: sepFilter @@ -247,6 +268,454 @@ void run_rgb2gray_impl(uchar out[], const uchar in[], int width, } } +//-------------------------------------- +// +// Fluid kernels: RGB-to-HSV +// +//-------------------------------------- +// +void run_rgb2hsv_impl(uchar out[], const uchar in[], const int sdiv_table[], + const int hdiv_table[], int width) +{ + const int hsv_shift = 12; + const int hr = 180; + + int j = 0; + + #if CV_SIMD128 + const int vectorStep = 16; + + uint8_t ff = 0xff; + v_uint8x16 mask1(ff, 0, 0, 0, ff, 0, 0, 0, ff, 0, 0, 0, ff, 0, 0, 0); + v_uint8x16 mask2(0, ff, 0, 0, 0, ff, 0, 0, 0, ff, 0, 0, 0, ff, 0, 0); + v_uint8x16 mask3(0, 0, ff, 0, 0, 0, ff, 0, 0, 0, ff, 0, 0, 0, ff, 0); + v_uint8x16 mask4(0, 0, 0, ff, 0, 0, 0, ff, 0, 0, 0, ff, 0, 0, 0, ff); + + for (int w = 0; w <= 3 * (width - vectorStep); w += 3 * vectorStep) + { + v_uint8x16 r, g, b; + v_load_deinterleave(in + w, r, g, b); + + v_uint8x16 v_min_rgb = v_min(v_min(r, g), b); + v_uint8x16 v_max_rgb = v_max(v_max(r, g), b); + + v_uint8x16 v_diff = v_max_rgb - v_min_rgb; + + v_uint8x16 v_r_eq_max = (r == v_max_rgb); + v_uint8x16 v_g_eq_max = (g == v_max_rgb); + + v_uint8x16 v; + // get V-ch + v = v_max_rgb; + + // divide v into 4x4 vectors because later int32 required + v_uint32x4 v_idx[4]; + v_idx[0] = v_reinterpret_as_u32(v & mask1); + v_idx[1] = v_reinterpret_as_u32(v & mask2) >> 8; + v_idx[2] = v_reinterpret_as_u32(v & mask3) >> 16; + v_idx[3] = v_reinterpret_as_u32(v & mask4) >> 24; + + v_uint32x4 sv_elems_32[4]; + sv_elems_32[0] = v_reinterpret_as_u32(v_lut(sdiv_table, v_reinterpret_as_s32(v_idx[0]))); + sv_elems_32[1] = v_reinterpret_as_u32(v_lut(sdiv_table, v_reinterpret_as_s32(v_idx[1]))); + sv_elems_32[2] = v_reinterpret_as_u32(v_lut(sdiv_table, v_reinterpret_as_s32(v_idx[2]))); + sv_elems_32[3] = v_reinterpret_as_u32(v_lut(sdiv_table, v_reinterpret_as_s32(v_idx[3]))); + + // divide and calculate s according to above feature + v_uint32x4 ss[4]; + + v_uint32x4 v_add = v_setall_u32(1) << (hsv_shift - 1); + + v_uint32x4 v_diff_exp[4]; + v_diff_exp[0] = v_reinterpret_as_u32(v_reinterpret_as_u8(v_diff) & mask1); + v_diff_exp[1] = v_reinterpret_as_u32(v_reinterpret_as_u8(v_diff) & mask2) >> 8; + v_diff_exp[2] = v_reinterpret_as_u32(v_reinterpret_as_u8(v_diff) & mask3) >> 16; + v_diff_exp[3] = v_reinterpret_as_u32(v_reinterpret_as_u8(v_diff) & mask4) >> 24; + + // s = (diff * sdiv_table[v] + (1 << (hsv_shift-1))) >> hsv_shift; + ss[0] = (v_diff_exp[0] * sv_elems_32[0] + v_add) >> hsv_shift; + ss[1] = (v_diff_exp[1] * sv_elems_32[1] + v_add) >> hsv_shift; + ss[2] = (v_diff_exp[2] * sv_elems_32[2] + v_add) >> hsv_shift; + ss[3] = (v_diff_exp[3] * sv_elems_32[3] + v_add) >> hsv_shift; + + // reconstruct order of S-ch + v_uint32x4 zip[8]; + v_zip(ss[0], ss[2], zip[0], zip[1]); + v_zip(ss[1], ss[3], zip[2], zip[3]); + + v_zip(zip[0], zip[2], zip[4], zip[5]); + v_zip(zip[1], zip[3], zip[6], zip[7]); + + v_uint8x16 s = v_pack(v_pack(zip[4], zip[5]), v_pack(zip[6], zip[7])); + + // the same divination for H-ch + // FIXME: REALLY UGLY and slow + v_uint32x4 gg[4]; + v_uint16x8 tmp_exp[2]; + v_expand(g, tmp_exp[0], tmp_exp[1]); + v_expand(tmp_exp[0], gg[0], gg[1]); + v_expand(tmp_exp[1], gg[2], gg[3]); + + v_uint32x4 rr[4]; + v_expand(r, tmp_exp[0], tmp_exp[1]); + v_expand(tmp_exp[0], rr[0], rr[1]); + v_expand(tmp_exp[1], rr[2], rr[3]); + + v_uint32x4 bb[4]; + v_expand(b, tmp_exp[0], tmp_exp[1]); + v_expand(tmp_exp[0], bb[0], bb[1]); + v_expand(tmp_exp[1], bb[2], bb[3]); + + v_int32x4 e[4]; + v_int16x8 sig_exp[2]; + v_expand(v_reinterpret_as_s8(v_r_eq_max), sig_exp[0], sig_exp[1]); + v_expand(sig_exp[0], e[0], e[1]); + v_expand(sig_exp[1], e[2], e[3]); + + v_int32x4 p[4]; + v_expand(v_reinterpret_as_s8(v_g_eq_max), sig_exp[0], sig_exp[1]); + v_expand(sig_exp[0], p[0], p[1]); + v_expand(sig_exp[1], p[2], p[3]); + + // reconstruct order of v_diff + v_zip(v_diff_exp[0], v_diff_exp[2], zip[0], zip[1]); + v_zip(v_diff_exp[1], v_diff_exp[3], zip[2], zip[3]); + + v_zip(zip[0], zip[2], zip[4], zip[5]); + v_zip(zip[1], zip[3], zip[6], zip[7]); + + v_uint8x16 vd = v_pack(v_pack(zip[4], zip[5]), v_pack(zip[6], zip[7])); + + v_uint32x4 vdd[4]; + v_uint16x8 vvdd[2]; + v_expand(vd, vvdd[0], vvdd[1]); + v_expand(vvdd[0], vdd[0], vdd[1]); + v_expand(vvdd[1], vdd[2], vdd[3]); + + // start computing H-ch + //h = (_vr & (g - b)) + (~_vr & ((_vg & (b - r + 2 * diff)) + ((~_vg) & (r - g + 4 * diff)))); + v_int32x4 hh[4]; + hh[0] = v_reinterpret_as_s32(v_select(e[0], v_reinterpret_as_s32(gg[0] - bb[0]), + v_select(p[0], v_reinterpret_as_s32(bb[0] - rr[0] + v_setall_u32(2) * vdd[0]), + v_reinterpret_as_s32(rr[0] - gg[0] + v_setall_u32(4) * vdd[0])))); + hh[1] = v_reinterpret_as_s32(v_select(e[1], v_reinterpret_as_s32(gg[1] - bb[1]), + v_select(p[1], v_reinterpret_as_s32(bb[1] - rr[1] + v_setall_u32(2) * vdd[1]), + v_reinterpret_as_s32(rr[1] - gg[1] + v_setall_u32(4) * vdd[1])))); + hh[2] = v_reinterpret_as_s32(v_select(e[2], v_reinterpret_as_s32(gg[2] - bb[2]), + v_select(p[2], v_reinterpret_as_s32(bb[2] - rr[2] + v_setall_u32(2) * vdd[2]), + v_reinterpret_as_s32(rr[2] - gg[2] + v_setall_u32(4) * vdd[2])))); + hh[3] = v_reinterpret_as_s32(v_select(e[3], v_reinterpret_as_s32(gg[3] - bb[3]), + v_select(p[3], v_reinterpret_as_s32(bb[3] - rr[3] + v_setall_u32(2) * vdd[3]), + v_reinterpret_as_s32(rr[3] - gg[3] + v_setall_u32(4) * vdd[3])))); + + //h = (h * hdiv_table[diff] + (1 << (hsv_shift-1))) >> hsv_shift; + v_uint32x4 h_elems_32[4]; + h_elems_32[0] = v_reinterpret_as_u32(v_lut(hdiv_table, v_reinterpret_as_s32(vdd[0]))); + h_elems_32[1] = v_reinterpret_as_u32(v_lut(hdiv_table, v_reinterpret_as_s32(vdd[1]))); + h_elems_32[2] = v_reinterpret_as_u32(v_lut(hdiv_table, v_reinterpret_as_s32(vdd[2]))); + h_elems_32[3] = v_reinterpret_as_u32(v_lut(hdiv_table, v_reinterpret_as_s32(vdd[3]))); + + hh[0] = (hh[0] * v_reinterpret_as_s32(h_elems_32[0]) + v_reinterpret_as_s32(v_add)) >> hsv_shift; + hh[1] = (hh[1] * v_reinterpret_as_s32(h_elems_32[1]) + v_reinterpret_as_s32(v_add)) >> hsv_shift; + hh[2] = (hh[2] * v_reinterpret_as_s32(h_elems_32[2]) + v_reinterpret_as_s32(v_add)) >> hsv_shift; + hh[3] = (hh[3] * v_reinterpret_as_s32(h_elems_32[3]) + v_reinterpret_as_s32(v_add)) >> hsv_shift; + + // check for negative H + v_int32x4 v_h_less_0[4]; + v_h_less_0[0] = (hh[0] < v_setall_s32(0)); + v_h_less_0[1] = (hh[1] < v_setall_s32(0)); + v_h_less_0[2] = (hh[2] < v_setall_s32(0)); + v_h_less_0[3] = (hh[3] < v_setall_s32(0)); + + v_int32x4 v_h_180[4]; + v_h_180[0] = hh[0] + v_setall_s32(180); + v_h_180[1] = hh[1] + v_setall_s32(180); + v_h_180[2] = hh[2] + v_setall_s32(180); + v_h_180[3] = hh[3] + v_setall_s32(180); + + hh[0] = v_select(v_h_less_0[0], v_h_180[0], hh[0]); + hh[1] = v_select(v_h_less_0[1], v_h_180[1], hh[1]); + hh[2] = v_select(v_h_less_0[2], v_h_180[2], hh[2]); + hh[3] = v_select(v_h_less_0[3], v_h_180[3], hh[3]); + + // pack H-ch + v_uint16x8 hh_16_1 = v_pack(v_reinterpret_as_u32(hh[0]), v_reinterpret_as_u32(hh[1])); + v_uint16x8 hh_16_2 = v_pack(v_reinterpret_as_u32(hh[2]), v_reinterpret_as_u32(hh[3])); + + v_uint8x16 h = v_pack(hh_16_1, hh_16_2); + + v_store_interleave(out + w, h, s, v); + + // output offset + j += vectorStep; + } + v_cleanup(); + #endif + + for (; j < width; ++j) + { + int r = in[j * 3 ], + g = in[j * 3 + 1], + b = in[j * 3 + 2]; + + int h, s, v = b; + int vmin = std::min({r, g, b}); + v = std::max({r, g, b}); + int _vr, _vg; + + uchar diff = cv::saturate_cast(v - vmin); + _vr = v == r ? -1 : 0; + _vg = v == g ? -1 : 0; + + s = (diff * sdiv_table[v] + (1 << (hsv_shift-1))) >> hsv_shift; + + h = (_vr & (g - b)) + + (~_vr & ((_vg & (b - r + 2 * diff)) + ((~_vg) & (r - g + 4 * diff)))); + + h = (h * hdiv_table[diff] + (1 << (hsv_shift-1))) >> hsv_shift; + h += h < 0 ? hr : 0; + + out[j * 3 ] = cv::saturate_cast(h); + out[j * 3 + 1] = (uchar)(s); + out[j * 3 + 2] = (uchar)(v); + } +} + +//-------------------------------------- +// +// Fluid kernels: RGB-to-BayerGR +// +//-------------------------------------- + +void run_bayergr2rgb_bg_impl(uchar out[], const uchar **in, int width) +{ + + int j = 0; + + #if CV_SIMD128 + const int vectorStep = 16; + + v_uint16x8 l_1, r_1, l_2, r_2; + v_uint16x8 l_3, r_3, l_4, r_4; + + for (int w = 0; w <= width - 2 * vectorStep - 2; w += 2 * vectorStep) // -2 for offset vectors + { + v_uint8x16 g1, r1, g1_offset, r1_offset; // 1 line + v_uint8x16 b2, g2, b2_offset, g2_offset; // 2 line + v_uint8x16 g3, r3, g3_offset, r3_offset; // 3 line + + v_load_deinterleave(in[0] + w + 1, r1, g1); + v_load_deinterleave(in[0] + w + 2 + 1, r1_offset, g1_offset); + + v_load_deinterleave(in[1] + w, b2, g2); + v_load_deinterleave(in[1] + w + 2, b2_offset, g2_offset); + + v_load_deinterleave(in[2] + w + 1, r3, g3); + v_load_deinterleave(in[2] + w + 2 + 1, r3_offset, g3_offset); + + + // calculate b-channel + v_expand(b2, l_1, r_1); + v_expand(b2_offset, l_2, r_2); + v_uint8x16 b2_sum = v_rshr_pack<1>(l_1 + l_2, r_1 + r_2); + + v_uint8x16 b_low, b_high; + v_zip(b2_sum, b2_offset, b_low, b_high); + + + // calculate r-channel + v_expand(r1, l_1, r_1); + v_expand(r1_offset, l_2, r_2); + v_expand(r3, l_3, r_3); + v_expand(r3_offset, l_4, r_4); + + v_uint8x16 r13offset_sum, r13_sum; + r13offset_sum = v_rshr_pack<2>(l_1 + l_2 + l_3 + l_4, + r_1 + r_2 + r_3 + r_4); + r13_sum = v_rshr_pack<1>(l_1 + l_3, r_1 + r_3); + + v_uint8x16 r_low, r_high; + v_zip(r13_sum, r13offset_sum, r_low, r_high); + + + // calculate g-channel + v_expand(g1, l_1, r_1); + v_expand(g3, l_2, r_2); + v_expand(g2, l_3, r_3); + v_expand(g2_offset, l_4, r_4); + + v_uint8x16 g_out_sum = v_rshr_pack<2>(l_1 + l_2 + l_3 + l_4, + r_1 + r_2 + r_3 + r_4); + + v_uint8x16 g_low, g_high; + v_zip(g2, g_out_sum, g_low, g_high); + + + v_store_interleave(out + w * 3 + 3, b_low, g_low, r_low); + v_store_interleave(out + w * 3 + vectorStep * 3 + 3, b_high, g_high, r_high); + + // output offset for scalar code + j += vectorStep * 2; + } + #endif + + bool curr_red = true; + int t0, t1, t2; + + int i = 1; + + for (; j < width - 1; ++j, curr_red = !curr_red) + { + if (!curr_red) + { + t0 = (in[i][j - 1] + in[i][j + 1] + 1) >> 1; + t1 = in[i][j]; + t2 = (in[i - 1][j] + in[i + 1][j] + 1) >> 1; + + + out[j * 3 + 0] = (uchar)t0; + out[j * 3 + 1] = (uchar)t1; + out[j * 3 + 2] = (uchar)t2; + } + else + { + t2 = (in[i - 1][j - 1] + in[i - 1][j + 1] + + in[i + 1][j - 1] + in[i + 1][j + 1] + 2) >> 2; + t1 = (in[i][j - 1] + in[i][j + 1] + + in[i - 1][j] + in[i + 1][j] + 2) >> 2; + t0 = in[i][j]; + + out[j * 3 + 0] = (uchar)t0; + out[j * 3 + 1] = (uchar)t1; + out[j * 3 + 2] = (uchar)t2; + } + } + + out[0] = out[3]; + out[1] = out[4]; + out[2] = out[5]; + + out[3 * (width - 1) ] = out[3 * (width - 2) ]; + out[3 * (width - 1) + 1] = out[3 * (width - 2) + 1]; + out[3 * (width - 1) + 2] = out[3 * (width - 2) + 2]; +} + +void run_bayergr2rgb_gr_impl(uchar out[], const uchar **in, int width) +{ + + int j = 0; + + #if CV_SIMD128 + const int vectorStep = 16; + + v_uint16x8 l_1, r_1, l_2, r_2; + v_uint16x8 l_3, r_3, l_4, r_4; + + for (int w = 0; w <= width - 2 * vectorStep - 2; w += 2 * vectorStep) // -2 for offset vectors + { + v_uint8x16 b1, g1, b1_offset, g1_offset; // 1 line + v_uint8x16 g2, r2, g2_offset, r2_offset; // 2 line + v_uint8x16 b3, g3, b3_offset, g3_offset; // 3 line + + v_load_deinterleave(in[0] + w, b1, g1); + v_load_deinterleave(in[0] + w + 2, b1_offset, g1_offset); + + v_load_deinterleave(in[1] + w, g2, r2); + v_load_deinterleave(in[1] + w + 2, g2_offset, r2_offset); + + v_load_deinterleave(in[2] + w, b3, g3); + v_load_deinterleave(in[2] + w + 2, b3_offset, g3_offset); + + // calculate r-channel + v_expand(r2, l_1, r_1); + v_expand(r2_offset, l_2, r_2); + v_uint8x16 r2_sum = v_rshr_pack<1>(l_1 + l_2, r_1 + r_2); + + v_uint8x16 r_low, r_high; + v_zip(r2, r2_sum, r_low, r_high); + + + // calculate b-channel + v_expand(b1, l_1, r_1); + v_expand(b1_offset, l_2, r_2); + v_expand(b3, l_3, r_3); + v_expand(b3_offset, l_4, r_4); + + v_uint8x16 b13offset_sum, b13_sum; + b13offset_sum = v_rshr_pack<2>(l_1 + l_2 + l_3 + l_4, + r_1 + r_2 + r_3 + r_4); + b13_sum = v_rshr_pack<1>(l_2 + l_4, r_2 + r_4); + + v_uint8x16 b_low, b_high; + v_zip(b13offset_sum, b13_sum, b_low, b_high); + + + // calculate g-channel + v_expand(g1, l_1, r_1); + v_expand(g3, l_2, r_2); + v_expand(g2, l_3, r_3); + v_expand(g2_offset, l_4, r_4); + + v_uint8x16 g_out_sum = v_rshr_pack<2>(l_1 + l_2 + l_3 + l_4, + r_1 + r_2 + r_3 + r_4); + + v_uint8x16 g_low, g_high; + v_zip(g_out_sum, g2_offset, g_low, g_high); + + + v_store_interleave(out + w * 3 + 3, b_low, g_low, r_low); + v_store_interleave(out + w * 3 + vectorStep * 3 + 3, b_high, g_high, r_high); + + // output offset for scalar code + j += vectorStep * 2; + } + #endif + + bool curr_blue = false; + int t0, t1, t2; + + int i = 1; + + for (; j < width - 1; ++j, curr_blue = !curr_blue) + { + if (!curr_blue) + { + // pixel at green at bgbg line + t2 = (in[i][j - 1] + in[i][j + 1] + 1) >> 1; + t1 = in[i][j]; + t0 = (in[i - 1][j] + in[i + 1][j] + 1) >> 1; + + out[j * 3 + 0] = (uchar)t0; + out[j * 3 + 1] = (uchar)t1; + out[j * 3 + 2] = (uchar)t2; + } + else + { + // pixel at red at grgr line + t2 = in[i][j]; + + t1 = (in[i][j - 1] + in[i][j + 1] + + in[i - 1][j] + in[i + 1][j] + 2) >> 2; + + t0 = (in[i - 1][j - 1] + in[i - 1][j + 1] + + in[i + 1][j - 1] + in[i + 1][j + 1] + 2) >> 2; + + out[j * 3 + 0] = (uchar)t0; + out[j * 3 + 1] = (uchar)t1; + out[j * 3 + 2] = (uchar)t2; + + } + } + + out[0] = out[3]; + out[1] = out[4]; + out[2] = out[5]; + + out[3 * (width - 1) ] = out[3 * (width - 2) ]; + out[3 * (width - 1) + 1] = out[3 * (width - 2) + 1]; + out[3 * (width - 1) + 2] = out[3 * (width - 2) + 2]; +} + //-------------------------------------- // // Fluid kernels: RGB-to-YUV, YUV-to-RGB @@ -402,6 +871,112 @@ void run_yuv2rgb_impl(uchar out[], const uchar in[], int width, const float coef } } +// Y' = 0.299*R' + 0.587*G' + 0.114*B' +// U' = (B' - Y')*0.492 +// V' = (R' - Y')*0.877 +static const float coef[5] = {0.299f, 0.587f, 0.114f, 0.492f, 0.877f}; + +static const ushort c0 = static_cast(coef[0]*(1 << 16) + 0.5f); +static const ushort c1 = static_cast(coef[1]*(1 << 16) + 0.5f); +static const ushort c2 = static_cast(coef[2]*(1 << 16) + 0.5f); +static const short c3 = static_cast(coef[3]*(1 << 12) + 0.5f); +static const short c4 = static_cast(coef[4]*(1 << 12) + 0.5f); + +void run_rgb2yuv422_impl(uchar out[], const uchar in[], int width) +{ + int w = 0, j = 0; + + #if CV_SIMD128 + const int vectorStep = 16; + + for (; w <= 3 * (width - vectorStep); w += 3 * vectorStep) + { + v_uint8x16 r, g, b; + v_load_deinterleave(in + w, r, g, b); + + // TODO: compute u and v x2 less times + v_uint8x16 y, u, v; + + v_uint16x8 rr1, gg1, bb1, rr2, gg2, bb2; + v_expand(r, rr1, rr2); + v_expand(g, gg1, gg2); + v_expand(b, bb1, bb2); + + rr1 = rr1 << 7; + rr2 = rr2 << 7; + gg1 = gg1 << 7; + gg2 = gg2 << 7; + bb1 = bb1 << 7; + bb2 = bb2 << 7; + + v_uint16x8 yy1, yy2; + + yy1 = v_mul_hi(v_setall_u16(c0), rr1) + + v_mul_hi(v_setall_u16(c1), gg1) + + v_mul_hi(v_setall_u16(c2), bb1); + + yy2 = v_mul_hi(v_setall_u16(c0), rr2) + + v_mul_hi(v_setall_u16(c1), gg2) + + v_mul_hi(v_setall_u16(c2), bb2); + + v_int16x8 u1, u2, v1, v2; + + u1 = v_mul_hi(v_setall_s16(c3), v_reinterpret_as_s16(bb1) - v_reinterpret_as_s16(yy1)); + u2 = v_mul_hi(v_setall_s16(c3), v_reinterpret_as_s16(bb2) - v_reinterpret_as_s16(yy2)); + v1 = v_mul_hi(v_setall_s16(c4), v_reinterpret_as_s16(rr1) - v_reinterpret_as_s16(yy1)); + v2 = v_mul_hi(v_setall_s16(c4), v_reinterpret_as_s16(rr2) - v_reinterpret_as_s16(yy2)); + + y = v_pack((yy1 + v_setall_u16(1 << 6)) >> 7, + (yy2 + v_setall_u16(1 << 6)) >> 7); + u = v_pack_u((u1 + v_setall_s16(257 << 2)) >> 3, + (u2 + v_setall_s16(257 << 2)) >> 3); + v = v_pack_u((v1 + v_setall_s16(257 << 2)) >> 3, + (v2 + v_setall_s16(257 << 2)) >> 3); + + uint8_t ff = 0xff; + v_uint8x16 mask(ff, 0, ff, 0, ff, 0, ff, 0, ff, 0, ff, 0, ff, 0, ff, 0); + v_uint8x16 uu = u & mask; + v_uint8x16 vv = v & mask; + // extract even u and v + v_uint8x16 u_low = v_pack(v_reinterpret_as_u16(uu), v_reinterpret_as_u16(uu)); + v_uint8x16 v_low = v_pack(v_reinterpret_as_u16(vv), v_reinterpret_as_u16(vv)); + + v_uint8x16 out1, out2; + v_zip(u_low, v_low, out1, out2); + + v_store_interleave(out + j, out1, y); + + // offset for output buffer + j += vectorStep * 2; + } + v_cleanup(); + #endif + + for (; w < width * 3; w += 6) + { + short r = in[w] << 7; + short g = in[w + 1] << 7; + short b = in[w + 2] << 7; + short y1 = (c0 * r + c1 * g + c2 * b) >> 16; + short u = c3*(b - y1) >> 16; + short v = c4*(r - y1) >> 16; + + out[j] = cv::saturate_cast((u + (128 << 3) + (1 << 2)) >> 3); // u + out[j + 1] = cv::saturate_cast((y1 + (1 << 6)) >> 7); // y1 + out[j + 2] = cv::saturate_cast((v + (128 << 3) + (1 << 2)) >> 3); // v + + r = in[w + 3] << 7; + g = in[w + 4] << 7; + b = in[w + 5] << 7; + short y2 = (c0 * r + c1 * g + c2 * b) >> 16; + + out[j + 3] = cv::saturate_cast((y2 + (1 << 6)) >> 7); // y2 + + // offset for output buffer + j += 4; + } +} + //------------------------- // // Fluid kernels: sepFilter diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclbackend.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclbackend.cpp index 6bfa74e35d292e..ab2c42056489cd 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclbackend.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclbackend.cpp @@ -18,9 +18,9 @@ #include -#include "opencv2/gapi/gcommon.hpp" -#include "opencv2/gapi/util/any.hpp" -#include "opencv2/gapi/gtype_traits.hpp" +#include +#include +#include #include "compiler/gobjref.hpp" #include "compiler/gmodel.hpp" @@ -150,6 +150,26 @@ void cv::gimpl::GOCLExecutable::run(std::vector &&input_objs, // has received from user (or from another Island, or mix...) // FIXME: Check input/output objects against GIsland protocol + // NB: We must clean-up m_res before this function returns because internally (bindInArg, + // bindOutArg) we work with cv::UMats, not cv::Mats that were originally placed into the + // input/output objects. If this is not done and cv::UMat "leaves" the local function scope, + // certain problems may occur. + // + // For example, if the original output (cv::Mat) is re-initialized by the user but we still + // hold cv::UMat -> we get cv::UMat that has a parent that was already destroyed. Also, + // since we don't own the data (the user does), there's no point holding it after we're done + const auto clean_up = [&input_objs, &output_objs] (cv::gimpl::Mag* p) + { + // Only clean-up UMat entries from current scope, we know that inputs and outputs are stored + // as UMats from the context below, so the following procedure is safe + auto& umats = p->slot(); + // NB: avoid clearing the whole magazine, there's also pre-allocated internal data + for (auto& it : input_objs) umats.erase(it.first.id); + for (auto& it : output_objs) umats.erase(it.first.id); + }; + // RAII wrapper to clean-up m_res + std::unique_ptr cleaner(&m_res, clean_up); + for (auto& it : input_objs) magazine::bindInArg (m_res, it.first, it.second, true); for (auto& it : output_objs) magazine::bindOutArg(m_res, it.first, it.second, true); diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclbackend.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclbackend.hpp index b57c66209d1cf1..52cf6d263614f4 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclbackend.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclbackend.hpp @@ -13,9 +13,9 @@ #include // tuple #include // type_list_index -#include "opencv2/gapi/garg.hpp" -#include "opencv2/gapi/gproto.hpp" -#include "opencv2/gapi/ocl/goclkernel.hpp" +#include +#include +#include #include "api/gorigin.hpp" #include "backends/common/gbackend.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclcore.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclcore.cpp index f55f7bb2f9d61f..974110021950aa 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclcore.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclcore.cpp @@ -7,8 +7,8 @@ #include "precomp.hpp" -#include "opencv2/gapi/core.hpp" -#include "opencv2/gapi/ocl/core.hpp" +#include +#include #include "backends/ocl/goclcore.hpp" GAPI_OCL_KERNEL(GOCLAdd, cv::gapi::core::GAdd) diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclcore.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclcore.hpp index fb6b78ad12d68d..1ed9c06fae3ae0 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclcore.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclcore.hpp @@ -11,7 +11,7 @@ #include #include -#include "opencv2/gapi/ocl/goclkernel.hpp" +#include namespace cv { namespace gimpl { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclimgproc.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclimgproc.cpp index 6e99d00d95fa6a..5795f440234b4f 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclimgproc.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclimgproc.cpp @@ -7,8 +7,8 @@ #include "precomp.hpp" -#include "opencv2/gapi/imgproc.hpp" -#include "opencv2/gapi/ocl/imgproc.hpp" +#include +#include #include "backends/ocl/goclimgproc.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclimgproc.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclimgproc.hpp index 7bb18f0a768aca..864f5fef70114f 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclimgproc.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclimgproc.hpp @@ -11,7 +11,7 @@ #include #include -#include "opencv2/gapi/ocl/goclkernel.hpp" +#include namespace cv { namespace gimpl { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclkernel.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclkernel.cpp index d01aae837315bf..11ca51be454e05 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclkernel.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/ocl/goclkernel.cpp @@ -7,7 +7,7 @@ #include -#include "opencv2/gapi/ocl/goclkernel.hpp" +#include const cv::UMat& cv::GOCLContext::inMat(int input) { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiled.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiled.cpp index 00de6997ed47b3..b27725751388cc 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiled.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiled.cpp @@ -9,8 +9,8 @@ #include -#include "opencv2/gapi/gproto.hpp" // can_describe -#include "opencv2/gapi/gcompiled.hpp" +#include // can_describe +#include #include "compiler/gcompiled_priv.hpp" #include "backends/common/gbackend.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiler.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiler.cpp index 83d4da1b775167..9925c196add905 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiler.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiler.cpp @@ -35,12 +35,12 @@ // #if !defined(GAPI_STANDALONE) -#include "opencv2/gapi/cpu/core.hpp" // Also directly refer to Core -#include "opencv2/gapi/cpu/imgproc.hpp" // ...and Imgproc kernel implementations +#include // Also directly refer to Core +#include // ...and Imgproc kernel implementations #endif // !defined(GAPI_STANDALONE) // -#include "opencv2/gapi/gcompoundkernel.hpp" // compound::backend() +#include // compound::backend() #include "logger.hpp" @@ -48,16 +48,28 @@ namespace { cv::gapi::GKernelPackage getKernelPackage(cv::GCompileArgs &args) { + auto withAuxKernels = [](const cv::gapi::GKernelPackage& pkg) { + cv::gapi::GKernelPackage aux_pkg; + for (const auto &b : pkg.backends()) { + aux_pkg = combine(aux_pkg, b.priv().auxiliaryKernels()); + } + return combine(pkg, aux_pkg); + }; + + auto has_use_only = cv::gimpl::getCompileArg(args); + if (has_use_only) + return withAuxKernels(has_use_only.value().pkg); + static auto ocv_pkg = #if !defined(GAPI_STANDALONE) combine(cv::gapi::core::cpu::kernels(), - cv::gapi::imgproc::cpu::kernels(), - cv::unite_policy::KEEP); + cv::gapi::imgproc::cpu::kernels()); #else cv::gapi::GKernelPackage(); #endif // !defined(GAPI_STANDALONE) auto user_pkg = cv::gimpl::getCompileArg(args); - return combine(ocv_pkg, user_pkg.value_or(cv::gapi::GKernelPackage{}), cv::unite_policy::REPLACE); + auto user_pkg_with_aux = withAuxKernels(user_pkg.value_or(cv::gapi::GKernelPackage{})); + return combine(ocv_pkg, user_pkg_with_aux); } cv::util::optional getGraphDumpDirectory(cv::GCompileArgs& args) @@ -87,7 +99,6 @@ cv::gimpl::GCompiler::GCompiler(const cv::GComputation &c, { using namespace std::placeholders; m_all_kernels = getKernelPackage(m_args); - auto lookup_order = getCompileArg(m_args).value_or(gapi::GLookupOrder()); auto dump_path = getGraphDumpDirectory(m_args); m_e.addPassStage("init"); @@ -107,8 +118,7 @@ cv::gimpl::GCompiler::GCompiler(const cv::GComputation &c, m_e.addPassStage("kernels"); m_e.addPass("kernels", "resolve_kernels", std::bind(passes::resolveKernels, _1, - std::ref(m_all_kernels), // NB: and not copied here - lookup_order)); + std::ref(m_all_kernels))); // NB: and not copied here m_e.addPass("kernels", "check_islands_content", passes::checkIslandsContent); m_e.addPassStage("meta"); diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiler.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiler.hpp index b369c14d12029f..38484340593eba 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiler.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gcompiler.hpp @@ -9,9 +9,9 @@ #define OPENCV_GAPI_GCOMPILER_HPP -#include "opencv2/gapi/gcommon.hpp" -#include "opencv2/gapi/gkernel.hpp" -#include "opencv2/gapi/gcomputation.hpp" +#include +#include +#include #include diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gislandmodel.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gislandmodel.hpp index 03b42ff381087b..d25db58300fe26 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gislandmodel.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gislandmodel.hpp @@ -15,8 +15,8 @@ #include #include -#include "opencv2/gapi/util/optional.hpp" -#include "opencv2/gapi/gkernel.hpp" +#include +#include #include "compiler/gobjref.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodel.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodel.cpp index 8a3cfdeae7d6b3..53464cbfe82ac9 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodel.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodel.cpp @@ -14,7 +14,7 @@ #include // util::indexed #include -#include "opencv2/gapi/gproto.hpp" +#include #include "api/gnode_priv.hpp" #include "compiler/gobjref.hpp" #include "compiler/gmodel.hpp" @@ -114,7 +114,7 @@ void GModel::linkOut(Graph &g, ade::NodeHandle opH, ade::NodeHandle objH, std::s op.outs[out_port] = RcDesc{gm.rc, gm.shape, {}}; } -std::vector GModel::orderedInputs(Graph &g, ade::NodeHandle nh) +std::vector GModel::orderedInputs(ConstGraph &g, ade::NodeHandle nh) { std::vector sorted_in_nhs(nh->inEdges().size()); for (const auto& in_eh : nh->inEdges()) @@ -126,7 +126,7 @@ std::vector GModel::orderedInputs(Graph &g, ade::NodeHandle nh) return sorted_in_nhs; } -std::vector GModel::orderedOutputs(Graph &g, ade::NodeHandle nh) +std::vector GModel::orderedOutputs(ConstGraph &g, ade::NodeHandle nh) { std::vector sorted_out_nhs(nh->outEdges().size()); for (const auto& out_eh : nh->outEdges()) diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodel.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodel.hpp index 2e98fa123c07db..98ab20878567e8 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodel.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodel.hpp @@ -22,8 +22,8 @@ // This part of the system is API-unaware by its design. // -#include "opencv2/gapi/garg.hpp" -#include "opencv2/gapi/gkernel.hpp" +#include +#include #include "compiler/gobjref.hpp" #include "compiler/gislandmodel.hpp" @@ -203,8 +203,8 @@ namespace GModel GAPI_EXPORTS void redirectReaders(Graph &g, ade::NodeHandle from, ade::NodeHandle to); GAPI_EXPORTS void redirectWriter (Graph &g, ade::NodeHandle from, ade::NodeHandle to); - GAPI_EXPORTS std::vector orderedInputs (Graph &g, ade::NodeHandle nh); - GAPI_EXPORTS std::vector orderedOutputs(Graph &g, ade::NodeHandle nh); + GAPI_EXPORTS std::vector orderedInputs (ConstGraph &g, ade::NodeHandle nh); + GAPI_EXPORTS std::vector orderedOutputs(ConstGraph &g, ade::NodeHandle nh); // Returns input meta array for given op node // Array is sparse, as metadata for non-gapi input objects is empty diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodelbuilder.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodelbuilder.hpp index 2d06000617df34..abe1c79d38c49b 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodelbuilder.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/gmodelbuilder.hpp @@ -11,8 +11,8 @@ #include #include -#include "opencv2/gapi/gproto.hpp" -#include "opencv2/gapi/gcall.hpp" +#include +#include #include "api/gorigin.hpp" #include "api/gnode.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/dump_dot.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/dump_dot.cpp index 89020d19b52668..1eefe723b8e92b 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/dump_dot.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/dump_dot.cpp @@ -14,7 +14,7 @@ #include -#include "opencv2/gapi/gproto.hpp" +#include #include "compiler/gmodel.hpp" #include "compiler/gislandmodel.hpp" #include "compiler/passes/passes.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/exec.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/exec.cpp index 8b20d60eecbaa6..73743a4ad9f778 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/exec.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/exec.cpp @@ -17,7 +17,7 @@ #include // contains #include // chain -#include "opencv2/gapi/util/optional.hpp" // util::optional +#include // util::optional #include "logger.hpp" // GAPI_LOG #include "compiler/gmodel.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/helpers.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/helpers.cpp index 60bf36afdc39d1..12267a38d13ccc 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/helpers.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/helpers.cpp @@ -13,7 +13,7 @@ #include -#include "opencv2/gapi/own/assert.hpp" // GAPI_Assert +#include // GAPI_Assert #include "compiler/passes/helpers.hpp" namespace { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/kernels.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/kernels.cpp index 0feb7b1b15d1bb..f5f0098cae07ee 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/kernels.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/kernels.cpp @@ -11,7 +11,7 @@ #include #include -#include "opencv2/gapi/gcompoundkernel.hpp" // compound::backend() +#include // compound::backend() #include "compiler/gmodel.hpp" #include "compiler/passes/passes.hpp" @@ -35,7 +35,7 @@ namespace // 1. Get GCompoundKernel implementation // 2. Create GCompoundContext // 3. Run GCompoundKernel with GCompoundContext - // 4. Build subgraph from imputs/outputs GCompoundKernel + // 4. Build subgraph from inputs/outputs GCompoundKernel // 5. Replace compound node to subgraph void expand(ade::Graph& g, ade::NodeHandle nh, const ImplInfo& impl_info) @@ -101,8 +101,7 @@ namespace // This pass, given the kernel package, selects a kernel implementation // for every operation in the graph void cv::gimpl::passes::resolveKernels(ade::passes::PassContext &ctx, - const gapi::GKernelPackage &kernels, - const gapi::GLookupOrder &order) + const gapi::GKernelPackage &kernels) { std::unordered_set active_backends; @@ -114,8 +113,7 @@ void cv::gimpl::passes::resolveKernels(ade::passes::PassContext &ctx, auto &op = gr.metadata(nh).get(); cv::gapi::GBackend selected_backend; cv::GKernelImpl selected_impl; - std::tie(selected_backend, selected_impl) - = kernels.lookup(op.k.name, order); + std::tie(selected_backend, selected_impl) = kernels.lookup(op.k.name); selected_backend.priv().unpackKernel(ctx.graph, nh, selected_impl); op.backend = selected_backend; diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/meta.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/meta.cpp index 528d84ce80853d..1577a861753652 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/meta.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/meta.cpp @@ -75,7 +75,7 @@ void cv::gimpl::passes::inferMeta(ade::passes::PassContext &ctx, bool meta_is_in // Now ask kernel for it's output meta. // Resulting out_args may have a larger size than op.outs, since some // outputs could stay unused (unconnected) - const auto& out_metas = op.k.outMeta(input_meta_args, op.args); + const auto out_metas = op.k.outMeta(input_meta_args, op.args); // Walk through operation's outputs, update meta of output objects // appropriately diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/passes.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/passes.hpp index 14f6acdc0c5337..4daddab451e54d 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/passes.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/compiler/passes/passes.hpp @@ -44,9 +44,8 @@ void storeResultingMeta(ade::passes::PassContext &ctx); void expandKernels(ade::passes::PassContext &ctx, const gapi::GKernelPackage& kernels); -void resolveKernels(ade::passes::PassContext &ctx, - const gapi::GKernelPackage &kernels, - const gapi::GLookupOrder &order); +void resolveKernels(ade::passes::PassContext &ctx, + const gapi::GKernelPackage &kernels); void fuseIslands(ade::passes::PassContext &ctx); void syncIslandTags(ade::passes::PassContext &ctx); diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/executor/gasync.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/executor/gasync.cpp index eba09516d4629d..b92dbdcec44ebb 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/executor/gasync.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/executor/gasync.cpp @@ -4,10 +4,12 @@ // // Copyright (C) 2019 Intel Corporation -#include "opencv2/gapi/gcomputation_async.hpp" -#include "opencv2/gapi/gcomputation.hpp" -#include "opencv2/gapi/gcompiled_async.hpp" -#include "opencv2/gapi/gcompiled.hpp" + +#include +#include +#include +#include +#include #include @@ -19,11 +21,11 @@ namespace { //This is a tool to move initialize captures of a lambda in C++11 template - struct move_through_copy{ + struct copy_through_move{ T value; - move_through_copy(T&& g) : value(std::move(g)) {} - move_through_copy(move_through_copy&&) = default; - move_through_copy(move_through_copy const& lhs) : move_through_copy(std::move(const_cast(lhs))) {} + copy_through_move(T&& g) : value(std::move(g)) {} + copy_through_move(copy_through_move&&) = default; + copy_through_move(copy_through_move const& lhs) : copy_through_move(std::move(const_cast(lhs))) {} }; } @@ -80,6 +82,7 @@ class async_service { }}; } } + std::unique_lock lck{mtx}; bool first_task = q.empty(); q.push(std::move(t)); @@ -108,8 +111,12 @@ async_service the_ctx; } namespace { -template -std::exception_ptr call_and_catch(f_t&& f){ +template +std::exception_ptr call_and_catch(f_t&& f, context_t&& ctx){ + if (std::forward(ctx).isCanceled()){ + return std::make_exception_ptr(GAsyncCanceled{}); + } + std::exception_ptr eptr; try { std::forward(f)(); @@ -120,15 +127,21 @@ std::exception_ptr call_and_catch(f_t&& f){ return eptr; } -template -void call_with_callback(f_t&& f, callback_t&& cb){ - auto eptr = call_and_catch(std::forward(f)); +struct DummyContext { + bool isCanceled() const { + return false; + } +}; + +template +void call_with_callback(f_t&& f, callback_t&& cb, context_t&& ctx){ + auto eptr = call_and_catch(std::forward(f), std::forward(ctx)); std::forward(cb)(eptr); } -template -void call_with_futute(f_t&& f, std::promise& p){ - auto eptr = call_and_catch(std::forward(f)); +template +void call_with_future(f_t&& f, std::promise& p, context_t&& ctx){ + auto eptr = call_and_catch(std::forward(f), std::forward(ctx)); if (eptr){ p.set_exception(eptr); } @@ -138,56 +151,126 @@ void call_with_futute(f_t&& f, std::promise& p){ } }//namespace +bool GAsyncContext::cancel(){ + bool expected = false; + bool updated = cancelation_requested.compare_exchange_strong(expected, true); + return updated; +} + +bool GAsyncContext::isCanceled() const { + return cancelation_requested.load(); +} + +const char* GAsyncCanceled::what() const noexcept { + return "GAPI asynchronous operation was canceled"; +} + //For now these async functions are simply wrapping serial version of apply/operator() into a functor. //These functors are then serialized into single queue, which is processed by a devoted background thread. void async_apply(GComputation& gcomp, std::function&& callback, GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args){ - //TODO: use move_through_copy for all args except gcomp + //TODO: use copy_through_move for all args except gcomp + //TODO: avoid code duplication between versions of "async" functions auto l = [=]() mutable { auto apply_l = [&](){ gcomp.apply(std::move(ins), std::move(outs), std::move(args)); }; - call_with_callback(apply_l,std::move(callback)); + call_with_callback(apply_l,std::move(callback), DummyContext{}); }; impl::the_ctx.add_task(l); } std::future async_apply(GComputation& gcomp, GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args){ - move_through_copy> prms{{}}; + copy_through_move> prms{{}}; auto f = prms.value.get_future(); auto l = [=]() mutable { auto apply_l = [&](){ gcomp.apply(std::move(ins), std::move(outs), std::move(args)); }; - call_with_futute(apply_l, prms.value); + call_with_future(apply_l, prms.value, DummyContext{}); }; impl::the_ctx.add_task(l); return f; } +void async_apply(GComputation& gcomp, std::function&& callback, GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args, GAsyncContext& ctx){ + //TODO: use copy_through_move for all args except gcomp + auto l = [=, &ctx]() mutable { + auto apply_l = [&](){ + gcomp.apply(std::move(ins), std::move(outs), std::move(args)); + }; + + call_with_callback(apply_l,std::move(callback), ctx); + }; + impl::the_ctx.add_task(l); +} + +std::future async_apply(GComputation& gcomp, GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args, GAsyncContext& ctx){ + copy_through_move> prms{{}}; + auto f = prms.value.get_future(); + auto l = [=, &ctx]() mutable { + auto apply_l = [&](){ + gcomp.apply(std::move(ins), std::move(outs), std::move(args)); + }; + + call_with_future(apply_l, prms.value, ctx); + }; + + impl::the_ctx.add_task(l); + return f; + +} + void async(GCompiled& gcmpld, std::function&& callback, GRunArgs &&ins, GRunArgsP &&outs){ auto l = [=]() mutable { auto apply_l = [&](){ gcmpld(std::move(ins), std::move(outs)); }; - call_with_callback(apply_l,std::move(callback)); + call_with_callback(apply_l,std::move(callback), DummyContext{}); + }; + + impl::the_ctx.add_task(l); +} + +void async(GCompiled& gcmpld, std::function&& callback, GRunArgs &&ins, GRunArgsP &&outs, GAsyncContext& ctx){ + auto l = [=, &ctx]() mutable { + auto apply_l = [&](){ + gcmpld(std::move(ins), std::move(outs)); + }; + + call_with_callback(apply_l,std::move(callback), ctx); }; impl::the_ctx.add_task(l); } std::future async(GCompiled& gcmpld, GRunArgs &&ins, GRunArgsP &&outs){ - move_through_copy> prms{{}}; + copy_through_move> prms{{}}; auto f = prms.value.get_future(); auto l = [=]() mutable { auto apply_l = [&](){ gcmpld(std::move(ins), std::move(outs)); }; - call_with_futute(apply_l, prms.value); + call_with_future(apply_l, prms.value, DummyContext{}); + }; + + impl::the_ctx.add_task(l); + return f; + +} +std::future async(GCompiled& gcmpld, GRunArgs &&ins, GRunArgsP &&outs, GAsyncContext& ctx){ + copy_through_move> prms{{}}; + auto f = prms.value.get_future(); + auto l = [=, &ctx]() mutable { + auto apply_l = [&](){ + gcmpld(std::move(ins), std::move(outs)); + }; + + call_with_future(apply_l, prms.value, ctx); }; impl::the_ctx.add_task(l); diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/executor/gexecutor.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/executor/gexecutor.cpp index 2594cde4444959..aacc4d12224915 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/executor/gexecutor.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/executor/gexecutor.cpp @@ -11,7 +11,7 @@ #include -#include "opencv2/gapi/opencv_includes.hpp" +#include #include "executor/gexecutor.hpp" #include "compiler/passes/passes.hpp" @@ -152,17 +152,31 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args) { using cv::util::get; const auto desc = get(d.meta); + + auto check_own_mat = [&desc, &args, &index]() + { + auto& out_mat = *get(args.outObjs.at(index)); + GAPI_Assert(out_mat.data != nullptr && + desc.canDescribe(out_mat)); + }; + #if !defined(GAPI_STANDALONE) // Building as part of OpenCV - follow OpenCV behavior - // if output buffer is not enough to hold the result, reallocate it - auto& out_mat = *get(args.outObjs.at(index)); - createMat(desc, out_mat); + // In the case of cv::Mat if output buffer is not enough to hold the result, reallocate it + if (cv::util::holds_alternative(args.outObjs.at(index))) + { + auto& out_mat = *get(args.outObjs.at(index)); + createMat(desc, out_mat); + } + // In the case of own::Mat never reallocated, checked to perfectly fit required meta + else + { + check_own_mat(); + } #else // Building standalone - output buffer should always exist, // and _exact_ match our inferred metadata - auto& out_mat = *get(args.outObjs.at(index)); - GAPI_Assert(out_mat.data != nullptr && - desc.canDescribe(out_mat)) + check_own_mat(); #endif // !defined(GAPI_STANDALONE) } } diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/precomp.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/precomp.hpp index eebe9d8968fb04..6106cd9bce6eb6 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/precomp.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/precomp.hpp @@ -9,13 +9,13 @@ #define __OPENCV_GAPI_PRECOMP_HPP__ #if !defined(GAPI_STANDALONE) -# include "opencv2/core.hpp" -# include "opencv2/imgproc.hpp" -# include "opencv2/gapi/core.hpp" -# include "opencv2/gapi/imgproc.hpp" +# include +# include +# include +# include #endif // !defined(GAPI_STANDALONE) -#include "opencv2/gapi.hpp" -#include "opencv2/gapi/gkernel.hpp" +#include +#include #endif // __OPENCV_GAPI_PRECOMP_HPP__ diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_compoundkernel_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_compoundkernel_tests.cpp index d1d8793da3bbfb..a0237599bc8fcb 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_compoundkernel_tests.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_compoundkernel_tests.cpp @@ -8,7 +8,7 @@ // FIXME: move out from Common #include "../test_precomp.hpp" -#include "opencv2/gapi/cpu/core.hpp" +#include #include @@ -235,7 +235,7 @@ TEST(GCompoundKernel, ReplaceDefaultKernel) cv::GMat in1, in2; auto out = cv::gapi::add(in1, in2); const auto custom_pkg = cv::gapi::kernels(); - const auto full_pkg = cv::gapi::combine(cv::gapi::core::cpu::kernels(), custom_pkg, cv::unite_policy::REPLACE); + const auto full_pkg = cv::gapi::combine(cv::gapi::core::cpu::kernels(), custom_pkg); cv::GComputation comp(cv::GIn(in1, in2), cv::GOut(out)); cv::Mat in_mat1 = cv::Mat::eye(3, 3, CV_8UC1), in_mat2 = cv::Mat::eye(3, 3, CV_8UC1), @@ -257,7 +257,7 @@ TEST(GCompoundKernel, DoubleAddC) auto out = cv::gapi::addC(super, s); const auto custom_pkg = cv::gapi::kernels(); - const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP); + const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels()); cv::GComputation comp(cv::GIn(in1, in2, s), cv::GOut(out)); cv::Mat in_mat1 = cv::Mat::eye(3, 3, CV_8UC1), @@ -282,7 +282,7 @@ TEST(GCompoundKernel, AddC) auto out = cv::gapi::addC(super, s); const auto custom_pkg = cv::gapi::kernels(); - const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP); + const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels()); cv::GComputation comp(cv::GIn(in1, in2, s), cv::GOut(out)); cv::Mat in_mat1 = cv::Mat::eye(3, 3, CV_8UC1), @@ -308,7 +308,7 @@ TEST(GCompoundKernel, MergeWithSplit) auto out = cv::gapi::merge3(a2, b2, c2); const auto custom_pkg = cv::gapi::kernels(); - const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP); + const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels()); cv::GComputation comp(cv::GIn(in), cv::GOut(out)); cv::Mat in_mat = cv::Mat::eye(3, 3, CV_8UC3), out_mat, ref_mat; @@ -325,7 +325,7 @@ TEST(GCompoundKernel, AddWithAddC) auto out = GCompoundAddWithAddC::on(in1, in2, s); const auto custom_pkg = cv::gapi::kernels(); - const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP); + const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels()); cv::GComputation comp(cv::GIn(in1, in2, s), cv::GOut(out)); cv::Mat in_mat1 = cv::Mat::eye(3, 3, CV_8UC1), @@ -347,7 +347,7 @@ TEST(GCompoundKernel, SplitWithAdd) std::tie(out1, out2) = GCompoundSplitWithAdd::on(in); const auto custom_pkg = cv::gapi::kernels(); - const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP); + const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels()); cv::GComputation comp(cv::GIn(in), cv::GOut(out1, out2)); cv::Mat in_mat = cv::Mat::eye(3, 3, CV_8UC3), @@ -375,7 +375,7 @@ TEST(GCompoundKernel, ParallelAddC) std::tie(out1, out2) = GCompoundParallelAddC::on(in1, in2); const auto custom_pkg = cv::gapi::kernels(); - const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP); + const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels()); cv::GComputation comp(cv::GIn(in1, in2), cv::GOut(out1, out2)); cv::Mat in_mat = cv::Mat::eye(3, 3, CV_8UC1), @@ -402,7 +402,7 @@ TEST(GCompoundKernel, GCompundKernelAndDefaultUseOneData) auto out = cv::gapi::add(GCompoundAddWithAddC::on(in1, in2, s), cv::gapi::addC(in2, s)); const auto custom_pkg = cv::gapi::kernels(); - const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP); + const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels()); cv::GComputation comp(cv::GIn(in1, in2, s), cv::GOut(out)); cv::Mat in_mat1 = cv::Mat::eye(3, 3, CV_8UC1), @@ -428,7 +428,7 @@ TEST(GCompoundKernel, CompoundExpandedToCompound) GCompoundAddWithAddCImpl, GCompoundDoubleAddCImpl>(); - const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP); + const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels()); cv::GComputation comp(cv::GIn(in1, in2, s), cv::GOut(out)); cv::Mat in_mat1 = cv::Mat::eye(3, 3, CV_8UC1), @@ -449,7 +449,7 @@ TEST(GCompoundKernel, MaxInArray) GDoubleArray in; auto out = GCompoundMaxInArray::on(in); const auto custom_pkg = cv::gapi::kernels(); - const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP); + const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels()); cv::GComputation comp(cv::GIn(in), cv::GOut(out)); std::vector v = { 1, 5, -2, 3, 10, 2}; cv::Scalar out_scl; @@ -465,7 +465,7 @@ TEST(GCompoundKernel, NegateArray) GDoubleArray in; GDoubleArray out = GCompoundNegateArray::on(in); const auto custom_pkg = cv::gapi::kernels(); - const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP); + const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels()); cv::GComputation comp(cv::GIn(in), cv::GOut(out)); std::vector in_v = {1, 5, -2, -10, 3}; std::vector out_v; @@ -483,7 +483,7 @@ TEST(GCompoundKernel, RightGArrayHandle) GDoubleArray a; cv::GMat out = GCompoundGMatGArrayGMat::on(in[0], a, in[1]); const auto custom_pkg = cv::gapi::kernels(); - const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels(), cv::unite_policy::KEEP); + const auto full_pkg = cv::gapi::combine(custom_pkg, cv::gapi::core::cpu::kernels()); cv::GComputation comp(cv::GIn(in[0], a, in[1]), cv::GOut(out)); std::vector in_v(3, 1.0); cv::Mat in_mat1 = cv::Mat::eye(cv::Size(3, 3), CV_8UC1), diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests.hpp index 6b5babc754e89e..5644c19e3dc6fc 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests.hpp @@ -2,7 +2,7 @@ // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // -// Copyright (C) 2018 Intel Corporation +// Copyright (C) 2018-2019 Intel Corporation #ifndef OPENCV_GAPI_CORE_TESTS_HPP @@ -30,125 +30,100 @@ enum bitwiseOp NOT = 3 }; -namespace +// Note: namespace must match the namespace of the type of the printed object +inline std::ostream& operator<<(std::ostream& os, mathOp op) { -const char *MathOperations[] = {"ADD", "SUB", "MUL", "DIV"}; -const char *BitwiseOperations[] = {"And", "Or", "Xor"}; -const char *CompareOperations[] = {"CMP_EQ", "CMP_GT", "CMP_GE", "CMP_LT", "CMP_LE", "CMP_NE"}; -//corresponds to OpenCV -const char *NormOperations[] = {"", "NORM_INF", "NORM_L1", "","NORM_L2"}; -} - - -struct PrintMathOpCoreParams -{ - template - std::string operator()(const ::testing::TestParamInfo& info) const +#define CASE(v) case mathOp::v: os << #v; break + switch (op) { - std::stringstream ss; - cv::Size sz = std::get<4>(info.param); - ss<(info.param)] - <<"_"<(info.param) - <<"_"<(info.param) - <<"_"<<(int)std::get<3>(info.param) - <<"_"<(info.param)+1) - <<"_"<(info.param) - <<"_"<(info.param); - return ss.str(); - } -}; - -struct PrintCmpCoreParams -{ - template - std::string operator()(const ::testing::TestParamInfo& info) const - { - std::stringstream ss; - cv::Size sz = std::get<3>(info.param); - ss<(info.param)] - <<"_"<(info.param) - <<"_"<(info.param) - <<"_"<(info.param); - return ss.str(); - } -}; + CASE(ADD); + CASE(SUB); + CASE(MUL); + CASE(DIV); + default: GAPI_Assert(false && "unknown mathOp value"); + } +#undef CASE + return os; +} -struct PrintBWCoreParams +// Note: namespace must match the namespace of the type of the printed object +inline std::ostream& operator<<(std::ostream& os, bitwiseOp op) { - template - std::string operator()(const ::testing::TestParamInfo& info) const +#define CASE(v) case bitwiseOp::v: os << #v; break + switch (op) { - std::stringstream ss; - cv::Size sz = std::get<2>(info.param); - ss<(info.param)] - <<"_"<(info.param) - <<"_"<(info.param); - return ss.str(); - } -}; + CASE(AND); + CASE(OR); + CASE(XOR); + CASE(NOT); + default: GAPI_Assert(false && "unknown bitwiseOp value"); + } +#undef CASE + return os; +} -struct PrintNormCoreParams +GAPI_TEST_FIXTURE(MathOpTest, initMatsRandU, FIXTURE_API(mathOp,bool,double,bool), 4, + opType, testWithScalar, scale, doReverseOp) +GAPI_TEST_FIXTURE(MulDoubleTest, initMatrixRandU, <>, 0) +GAPI_TEST_FIXTURE(DivTest, initMatrixRandU, <>, 0) +GAPI_TEST_FIXTURE(DivCTest, initMatrixRandU, <>, 0) +GAPI_TEST_FIXTURE(MeanTest, initMatrixRandU, <>, 0) +GAPI_TEST_FIXTURE(MaskTest, initMatrixRandU, <>, 0) +GAPI_TEST_FIXTURE(Polar2CartTest, initMatsRandU, <>, 0) +GAPI_TEST_FIXTURE(Cart2PolarTest, initMatsRandU, <>, 0) +GAPI_TEST_FIXTURE(CmpTest, initMatsRandU, FIXTURE_API(CmpTypes,bool), 2, opType, testWithScalar) +GAPI_TEST_FIXTURE(BitwiseTest, initMatsRandU, FIXTURE_API(bitwiseOp), 1, opType) +GAPI_TEST_FIXTURE(NotTest, initMatrixRandU, <>, 0) +GAPI_TEST_FIXTURE(SelectTest, initMatsRandU, <>, 0) +GAPI_TEST_FIXTURE(MinTest, initMatsRandU, <>, 0) +GAPI_TEST_FIXTURE(MaxTest, initMatsRandU, <>, 0) +GAPI_TEST_FIXTURE(AbsDiffTest, initMatsRandU, <>, 0) +GAPI_TEST_FIXTURE(AbsDiffCTest, initMatsRandU, <>, 0) +GAPI_TEST_FIXTURE(SumTest, initMatrixRandU, FIXTURE_API(CompareScalars), 1, cmpF) +GAPI_TEST_FIXTURE(AddWeightedTest, initMatsRandU, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(NormTest, initMatrixRandU, FIXTURE_API(CompareScalars,NormTypes), 2, + cmpF, opType) +GAPI_TEST_FIXTURE(IntegralTest, initNothing, <>, 0) +GAPI_TEST_FIXTURE(ThresholdTest, initMatrixRandU, FIXTURE_API(int), 1, tt) +GAPI_TEST_FIXTURE(ThresholdOTTest, initMatrixRandU, FIXTURE_API(int), 1, tt) +GAPI_TEST_FIXTURE(InRangeTest, initMatrixRandU, <>, 0) +GAPI_TEST_FIXTURE(Split3Test, initMatrixRandU, <>, 0) +GAPI_TEST_FIXTURE(Split4Test, initMatrixRandU, <>, 0) +GAPI_TEST_FIXTURE(ResizeTest, initNothing, FIXTURE_API(CompareMats,int,cv::Size), 3, + cmpF, interp, sz_out) +GAPI_TEST_FIXTURE(ResizePTest, initNothing, FIXTURE_API(CompareMats,int,cv::Size), 3, + cmpF, interp, sz_out) +GAPI_TEST_FIXTURE(ResizeTestFxFy, initNothing, FIXTURE_API(CompareMats,int,double,double), 4, + cmpF, interp, fx, fy) +GAPI_TEST_FIXTURE(Merge3Test, initMatsRandU, <>, 0) +GAPI_TEST_FIXTURE(Merge4Test, initMatsRandU, <>, 0) +GAPI_TEST_FIXTURE(RemapTest, initMatrixRandU, <>, 0) +GAPI_TEST_FIXTURE(FlipTest, initMatrixRandU, FIXTURE_API(int), 1, flipCode) +GAPI_TEST_FIXTURE(CropTest, initMatrixRandU, FIXTURE_API(cv::Rect), 1, rect_to) +GAPI_TEST_FIXTURE(ConcatHorTest, initNothing, <>, 0) +GAPI_TEST_FIXTURE(ConcatVertTest, initNothing, <>, 0) +GAPI_TEST_FIXTURE(ConcatVertVecTest, initNothing, <>, 0) +GAPI_TEST_FIXTURE(ConcatHorVecTest, initNothing, <>, 0) +GAPI_TEST_FIXTURE(LUTTest, initNothing, <>, 0) +GAPI_TEST_FIXTURE(ConvertToTest, initNothing, FIXTURE_API(CompareMats, double, double), 3, + cmpF, alpha, beta) +GAPI_TEST_FIXTURE(PhaseTest, initMatsRandU, FIXTURE_API(bool), 1, angle_in_degrees) +GAPI_TEST_FIXTURE(SqrtTest, initMatrixRandU, <>, 0) +GAPI_TEST_FIXTURE(NormalizeTest, initNothing, FIXTURE_API(CompareMats,double,double,int,MatType2), 5, + cmpF, a, b, norm_type, ddepth) +struct BackendOutputAllocationTest : TestWithParamBase<> { - template - std::string operator()(const ::testing::TestParamInfo& info) const + BackendOutputAllocationTest() { - std::stringstream ss; - cv::Size sz = std::get<2>(info.param); - ss<(info.param)] - <<"_"<(info.param) - <<"_"<>{}; -struct MulDoubleTest : public TestParams>{}; -struct DivTest : public TestParams>{}; -struct DivCTest : public TestParams>{}; -struct MeanTest : public TestParams> {}; -struct MaskTest : public TestParams> {}; -struct Polar2CartTest : public TestParams> {}; -struct Cart2PolarTest : public TestParams> {}; -struct CmpTest : public TestParams>{}; -struct BitwiseTest : public TestParams>{}; -struct NotTest : public TestParams> {}; -struct SelectTest : public TestParams> {}; -struct MinTest : public TestParams>{}; -struct MaxTest : public TestParams>{}; -struct AbsDiffTest : public TestParams>{}; -struct AbsDiffCTest : public TestParams> {}; -struct SumTest : public TestParams> {}; -struct AddWeightedTest : public TestParams>{}; -struct NormTest : public TestParams>{}; -struct IntegralTest : public TestWithParam> {}; -struct ThresholdTest : public TestParams> {}; -struct ThresholdOTTest : public TestParams> {}; -struct InRangeTest : public TestParams> {}; -struct Split3Test : public TestParams> {}; -struct Split4Test : public TestParams> {}; -struct ResizeTest : public TestWithParam> {}; -struct ResizeTestFxFy : public TestWithParam> {}; -struct Merge3Test : public TestParams> {}; -struct Merge4Test : public TestParams> {}; -struct RemapTest : public TestParams> {}; -struct FlipTest : public TestParams> {}; -struct CropTest : public TestParams> {}; -struct ConcatHorTest : public TestWithParam> {}; -struct ConcatVertTest : public TestWithParam> {}; -struct ConcatVertVecTest : public TestWithParam> {}; -struct ConcatHorVecTest : public TestWithParam> {}; -struct LUTTest : public TestParams> {}; -struct ConvertToTest : public TestParams> {}; -struct PhaseTest : public TestParams> {}; -struct SqrtTest : public TestParams> {}; -struct NormalizeTest : public TestParams> {}; +// FIXME: move all tests from this fixture to the base class once all issues are resolved +struct BackendOutputAllocationLargeSizeWithCorrectSubmatrixTest : BackendOutputAllocationTest {}; +GAPI_TEST_FIXTURE(ReInitOutTest, initNothing, , 1, out_sz) } // opencv_test #endif //OPENCV_GAPI_CORE_TESTS_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests_inl.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests_inl.hpp index bf0ac9895469be..5b22ef98b5a058 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests_inl.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_core_tests_inl.hpp @@ -2,13 +2,13 @@ // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // -// Copyright (C) 2018 Intel Corporation +// Copyright (C) 2018-2019 Intel Corporation #ifndef OPENCV_GAPI_CORE_TESTS_INL_HPP #define OPENCV_GAPI_CORE_TESTS_INL_HPP -#include "opencv2/gapi/core.hpp" +#include #include "gapi_core_tests.hpp" namespace opencv_test @@ -16,15 +16,6 @@ namespace opencv_test TEST_P(MathOpTest, MatricesAccuracyTest ) { - mathOp opType = ADD; - int type = 0, dtype = 0; - cv::Size sz; - double scale = 1; // mul, div - bool testWithScalar = false, initOutMatr = false, doReverseOp = false; - cv::GCompileArgs compile_args; - std::tie(opType, testWithScalar, type, scale, sz, dtype, initOutMatr, doReverseOp, compile_args) = GetParam(); - initMatsRandU(type, sz, dtype, initOutMatr); - // G-API code & corresponding OpenCV code //////////////////////////////// cv::GMat in1, in2, out; if( testWithScalar ) @@ -82,7 +73,7 @@ TEST_P(MathOpTest, MatricesAccuracyTest ) } } cv::GComputation c(GIn(in1, sc1), GOut(out)); - c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, sc), gout(out_mat_gapi), getCompileArgs()); } else { @@ -118,7 +109,7 @@ TEST_P(MathOpTest, MatricesAccuracyTest ) FAIL() << "no such math operation type for matrix and matrix!"; }} cv::GComputation c(GIn(in1, in2), GOut(out)); - c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), getCompileArgs()); } // Comparison ////////////////////////////////////////////////////////////// @@ -148,22 +139,14 @@ TEST_P(MathOpTest, MatricesAccuracyTest ) TEST_P(MulDoubleTest, AccuracyTest) { - auto param = GetParam(); - int type = std::get<0>(param); - int dtype = std::get<2>(param); - cv::Size sz_in = std::get<1>(param); - bool initOut = std::get<3>(param); - auto& rng = cv::theRNG(); double d = rng.uniform(0.0, 10.0); - auto compile_args = std::get<4>(param); - initMatrixRandU(type, sz_in, dtype, initOut); // G-API code //////////////////////////////////////////////////////////// cv::GMat in1, out; out = cv::gapi::mulC(in1, d, dtype); cv::GComputation c(in1, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code /////////////////////////////////////////////////////////// cv::multiply(in_mat1, d, out_mat_ocv, 1, dtype); @@ -187,26 +170,19 @@ TEST_P(MulDoubleTest, AccuracyTest) #else EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); #endif - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(out_mat_gapi.size(), sz); } TEST_P(DivTest, DISABLED_DivByZeroTest) // https://github.com/opencv/opencv/pull/12826 { - int type = 0, dtype = 0; - cv::Size sz_in; - bool initOut = false; - cv::GCompileArgs compile_args; - std::tie(type, sz_in, dtype, initOut, compile_args) = GetParam(); - - initMatrixRandU(type, sz_in, dtype, initOut); - in_mat2 = cv::Mat(sz_in, type); + in_mat2 = cv::Mat(sz, type); in_mat2.setTo(cv::Scalar::all(0)); // G-API code ////////////////////////////////////////////////////////////// cv::GMat in1, in2; auto out = cv::gapi::div(in1, in2, 1.0, dtype); cv::GComputation c(GIn(in1, in2), GOut(out)); - c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -216,19 +192,12 @@ TEST_P(DivTest, DISABLED_DivByZeroTest) // https://github.com/opencv/opencv/pul // Comparison ////////////////////////////////////////////////////////////// { EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(DivCTest, DISABLED_DivByZeroTest) // https://github.com/opencv/opencv/pull/12826 { - int type = 0, dtype = 0; - cv::Size sz_in; - bool initOut = false; - cv::GCompileArgs compile_args; - std::tie(type, sz_in, dtype, initOut, compile_args) = GetParam(); - - initMatrixRandU(type, sz_in, dtype, initOut); sc = cv::Scalar::all(0); // G-API code ////////////////////////////////////////////////////////////// @@ -237,7 +206,7 @@ TEST_P(DivCTest, DISABLED_DivByZeroTest) // https://github.com/opencv/opencv/pu auto out = cv::gapi::divC(in1, sc1, dtype); cv::GComputation c(GIn(in1, sc1), GOut(out)); - c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, sc), gout(out_mat_gapi), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -247,19 +216,13 @@ TEST_P(DivCTest, DISABLED_DivByZeroTest) // https://github.com/opencv/opencv/pu // Comparison ////////////////////////////////////////////////////////////// { EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi)); - cv::Mat zeros = cv::Mat::zeros(sz_in, type); + cv::Mat zeros = cv::Mat::zeros(sz, type); EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != zeros)); } } TEST_P(MeanTest, AccuracyTest) { - int type = 0; - bool initOut = false; - cv::Size sz_in; - cv::GCompileArgs compile_args; - std::tie(type, sz_in, initOut, compile_args) = GetParam(); - initMatrixRandU(type, sz_in, initOut); cv::Scalar out_norm; cv::Scalar out_norm_ocv; @@ -268,7 +231,7 @@ TEST_P(MeanTest, AccuracyTest) auto out = cv::gapi::mean(in); cv::GComputation c(cv::GIn(in), cv::GOut(out)); - c.apply(cv::gin(in_mat1), cv::gout(out_norm), std::move(compile_args)); + c.apply(cv::gin(in_mat1), cv::gout(out_norm), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { out_norm_ocv = cv::mean(in_mat1); @@ -281,14 +244,7 @@ TEST_P(MeanTest, AccuracyTest) TEST_P(MaskTest, AccuracyTest) { - int type = 0; - bool initOut = false; - cv::Size sz_in; - cv::GCompileArgs compile_args; - std::tie(type, sz_in, initOut, compile_args) = GetParam(); - initMatrixRandU(type, sz_in, type, initOut); - - in_mat2 = cv::Mat(sz_in, CV_8UC1); + in_mat2 = cv::Mat(sz, CV_8UC1); cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255)); in_mat2 = in_mat2 > 128; @@ -297,7 +253,7 @@ TEST_P(MaskTest, AccuracyTest) auto out = cv::gapi::mask(in, m); cv::GComputation c(cv::GIn(in, m), cv::GOut(out)); - c.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat_gapi), std::move(compile_args)); + c.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat_gapi), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { out_mat_ocv = cv::Mat::zeros(in_mat1.size(), in_mat1.type()); @@ -311,17 +267,12 @@ TEST_P(MaskTest, AccuracyTest) TEST_P(Polar2CartTest, AccuracyTest) { - auto param = GetParam(); - cv::Size sz_in = std::get<0>(param); - auto compile_args = std::get<2>(param); - initMatsRandU(CV_32FC1, sz_in, CV_32FC1, std::get<1>(param)); - cv::Mat out_mat2; cv::Mat out_mat_ocv2; - if(std::get<1>(param) == true) + if (dtype != -1) { - out_mat2 = cv::Mat(sz_in, CV_32FC1); - out_mat_ocv2 = cv::Mat(sz_in, CV_32FC1); + out_mat2 = cv::Mat(sz, dtype); + out_mat_ocv2 = cv::Mat(sz, dtype); } // G-API code ////////////////////////////////////////////////////////////// @@ -329,7 +280,7 @@ TEST_P(Polar2CartTest, AccuracyTest) std::tie(out1, out2) = cv::gapi::polarToCart(in1, in2); cv::GComputation c(GIn(in1, in2), GOut(out1, out2)); - c.apply(gin(in_mat1,in_mat2), gout(out_mat_gapi, out_mat2), std::move(compile_args)); + c.apply(gin(in_mat1,in_mat2), gout(out_mat_gapi, out_mat2), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::polarToCart(in_mat1, in_mat2, out_mat_ocv, out_mat_ocv2); @@ -361,19 +312,14 @@ TEST_P(Polar2CartTest, AccuracyTest) EXPECT_EQ(0, cv::countNonZero(difx > 1e-6*absx)); EXPECT_EQ(0, cv::countNonZero(dify > 1e-6*absy)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(Cart2PolarTest, AccuracyTest) { - auto param = GetParam(); - cv::Size sz_in = std::get<0>(param); - auto compile_args = std::get<2>(param); - initMatsRandU(CV_32FC1, sz_in, CV_32FC1, std::get<1>(param)); - - cv::Mat out_mat2(sz_in, CV_32FC1); - cv::Mat out_mat_ocv2(sz_in, CV_32FC1); + cv::Mat out_mat2(sz, dtype); + cv::Mat out_mat_ocv2(sz, dtype); // G-API code ////////////////////////////////////////////////////////////// cv::GMat in1, in2, out1, out2; @@ -414,20 +360,12 @@ TEST_P(Cart2PolarTest, AccuracyTest) // (expected relative accuracy like 1e-6) EXPECT_EQ(0, cv::countNonZero(difm > 1e-6*absm)); EXPECT_EQ(0, cv::countNonZero(difa > 1e-3*absa)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(CmpTest, AccuracyTest) { - CmpTypes opType = CMP_EQ; - int type = 0; - cv::Size sz; - bool testWithScalar = false, initOutMatr = false; - cv::GCompileArgs compile_args; - std::tie(opType, testWithScalar, type, sz, initOutMatr, compile_args) = GetParam(); - initMatsRandU(type, sz, CV_8U, initOutMatr); - // G-API code & corresponding OpenCV code //////////////////////////////// cv::GMat in1, out; if( testWithScalar ) @@ -447,7 +385,7 @@ TEST_P(CmpTest, AccuracyTest) cv::compare(in_mat1, sc, out_mat_ocv, opType); cv::GComputation c(GIn(in1, in2), GOut(out)); - c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, sc), gout(out_mat_gapi), getCompileArgs()); } else { @@ -466,7 +404,7 @@ TEST_P(CmpTest, AccuracyTest) cv::compare(in_mat1, in_mat2, out_mat_ocv, opType); cv::GComputation c(GIn(in1, in2), GOut(out)); - c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), getCompileArgs()); } // Comparison ////////////////////////////////////////////////////////////// @@ -478,14 +416,6 @@ TEST_P(CmpTest, AccuracyTest) TEST_P(BitwiseTest, AccuracyTest) { - bitwiseOp opType = AND; - int type = 0; - cv::Size sz; - bool initOutMatr = false; - cv::GCompileArgs compile_args; - std::tie(opType, type, sz, initOutMatr, compile_args) = GetParam(); - initMatsRandU(type, sz, type, initOutMatr); - // G-API code & corresponding OpenCV code //////////////////////////////// cv::GMat in1, in2, out; switch(opType) @@ -514,7 +444,7 @@ TEST_P(BitwiseTest, AccuracyTest) } } cv::GComputation c(GIn(in1, in2), GOut(out)); - c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), getCompileArgs()); // Comparison ////////////////////////////////////////////////////////////// { @@ -525,17 +455,12 @@ TEST_P(BitwiseTest, AccuracyTest) TEST_P(NotTest, AccuracyTest) { - auto param = GetParam(); - cv::Size sz_in = std::get<1>(param); - auto compile_args = std::get<3>(param); - initMatrixRandU(std::get<0>(param), sz_in, std::get<0>(param), std::get<2>(param)); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::bitwise_not(in); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -544,18 +469,13 @@ TEST_P(NotTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(SelectTest, AccuracyTest) { - auto param = GetParam(); - int type = std::get<0>(param); - cv::Size sz_in = std::get<1>(param); - auto compile_args = std::get<3>(param); - initMatsRandU(type, sz_in, type, std::get<2>(param)); - cv::Mat in_mask(sz_in, CV_8UC1); + cv::Mat in_mask(sz, CV_8UC1); cv::randu(in_mask, cv::Scalar::all(0), cv::Scalar::all(255)); // G-API code ////////////////////////////////////////////////////////////// @@ -563,7 +483,7 @@ TEST_P(SelectTest, AccuracyTest) auto out = cv::gapi::select(in1, in2, in3); cv::GComputation c(GIn(in1, in2, in3), GOut(out)); - c.apply(gin(in_mat1, in_mat2, in_mask), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, in_mat2, in_mask), gout(out_mat_gapi), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -573,23 +493,18 @@ TEST_P(SelectTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(MinTest, AccuracyTest) { - auto param = GetParam(); - cv::Size sz_in = std::get<1>(param); - auto compile_args = std::get<3>(param); - initMatsRandU(std::get<0>(param), sz_in, std::get<0>(param), std::get<2>(param)); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in1, in2; auto out = cv::gapi::min(in1, in2); cv::GComputation c(GIn(in1, in2), GOut(out)); - c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -598,23 +513,18 @@ TEST_P(MinTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(MaxTest, AccuracyTest) { - auto param = GetParam(); - cv::Size sz_in = std::get<1>(param); - auto compile_args = std::get<3>(param); - initMatsRandU(std::get<0>(param), sz_in, std::get<0>(param), std::get<2>(param)); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in1, in2; auto out = cv::gapi::max(in1, in2); cv::GComputation c(GIn(in1, in2), GOut(out)); - c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -623,23 +533,18 @@ TEST_P(MaxTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(AbsDiffTest, AccuracyTest) { - auto param = GetParam(); - cv::Size sz_in = std::get<1>(param); - auto compile_args = std::get<3>(param); - initMatsRandU(std::get<0>(param), sz_in, std::get<0>(param), std::get<2>(param)); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in1, in2; auto out = cv::gapi::absDiff(in1, in2); cv::GComputation c(GIn(in1, in2), GOut(out)); - c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -648,24 +553,19 @@ TEST_P(AbsDiffTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(AbsDiffCTest, AccuracyTest) { - auto param = GetParam(); - cv::Size sz_in = std::get<1>(param); - auto compile_args = std::get<3>(param); - initMatsRandU(std::get<0>(param), sz_in, std::get<0>(param), std::get<2>(param)); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in1; cv::GScalar sc1; auto out = cv::gapi::absDiffC(in1, sc1); cv::GComputation c(cv::GIn(in1, sc1), cv::GOut(out)); - c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, sc), gout(out_mat_gapi), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -674,20 +574,12 @@ TEST_P(AbsDiffCTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(SumTest, AccuracyTest) { - auto param = GetParam(); - compare_scalar_f cmpF = get<3>(GetParam()); - MatType type = std::get<0>(param); - cv::Size sz_in = std::get<1>(param); - auto compile_args = std::get<4>(param); - initMatrixRandU(type, sz_in, type, std::get<2>(param)); - - cv::Scalar out_sum; cv::Scalar out_sum_ocv; @@ -696,7 +588,7 @@ TEST_P(SumTest, AccuracyTest) auto out = cv::gapi::sum(in); cv::GComputation c(cv::GIn(in), cv::GOut(out)); - c.apply(cv::gin(in_mat1), cv::gout(out_sum), std::move(compile_args)); + c.apply(cv::gin(in_mat1), cv::gout(out_sum), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { out_sum_ocv = cv::sum(in_mat1); @@ -709,25 +601,17 @@ TEST_P(SumTest, AccuracyTest) TEST_P(AddWeightedTest, AccuracyTest) { - int type = 0, dtype = 0; - cv::Size sz_in; - bool initOut = false; - cv::GCompileArgs compile_args; - compare_f cmpF; - std::tie(type, sz_in, dtype, initOut, cmpF, compile_args) = GetParam(); - auto& rng = cv::theRNG(); double alpha = rng.uniform(0.0, 1.0); double beta = rng.uniform(0.0, 1.0); double gamma = rng.uniform(0.0, 1.0); - initMatsRandU(type, sz_in, dtype, initOut); // G-API code ////////////////////////////////////////////////////////////// cv::GMat in1, in2; auto out = cv::gapi::addWeighted(in1, alpha, in2, beta, gamma, dtype); cv::GComputation c(GIn(in1, in2), GOut(out)); - c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -735,20 +619,11 @@ TEST_P(AddWeightedTest, AccuracyTest) } // Comparison ////////////////////////////////////////////////////////////// EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); - + EXPECT_EQ(out_mat_gapi.size(), sz); } TEST_P(NormTest, AccuracyTest) { - compare_scalar_f cmpF; - NormTypes opType = NORM_INF; - int type = 0; - cv::Size sz; - cv::GCompileArgs compile_args; - std::tie(opType, type, sz, cmpF, compile_args) = GetParam(); - initMatrixRandU(type, sz, type, false); - cv::Scalar out_norm; cv::Scalar out_norm_ocv; @@ -764,7 +639,7 @@ TEST_P(NormTest, AccuracyTest) } out_norm_ocv = cv::norm(in_mat1, opType); cv::GComputation c(GIn(in1), GOut(out)); - c.apply(gin(in_mat1), gout(out_norm), std::move(compile_args)); + c.apply(gin(in_mat1), gout(out_norm), getCompileArgs()); // Comparison ////////////////////////////////////////////////////////////// { @@ -774,16 +649,12 @@ TEST_P(NormTest, AccuracyTest) TEST_P(IntegralTest, AccuracyTest) { - int type = std::get<0>(GetParam()); - cv::Size sz_in = std::get<1>(GetParam()); - auto compile_args = std::get<2>(GetParam()); - int type_out = (type == CV_8U) ? CV_32SC1 : CV_64FC1; - cv::Mat in_mat1(sz_in, type); + in_mat1 = cv::Mat(sz, type); cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255)); - cv::Size sz_out = cv::Size(sz_in.width + 1, sz_in.height + 1); + cv::Size sz_out = cv::Size(sz.width + 1, sz.height + 1); cv::Mat out_mat1(sz_out, type_out); cv::Mat out_mat_ocv1(sz_out, type_out); @@ -795,7 +666,7 @@ TEST_P(IntegralTest, AccuracyTest) std::tie(out1, out2) = cv::gapi::integral(in1, type_out, CV_64FC1); cv::GComputation c(cv::GIn(in1), cv::GOut(out1, out2)); - c.apply(cv::gin(in_mat1), cv::gout(out_mat1, out_mat2), std::move(compile_args)); + c.apply(cv::gin(in_mat1), cv::gout(out_mat1, out_mat2), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -810,15 +681,8 @@ TEST_P(IntegralTest, AccuracyTest) TEST_P(ThresholdTest, AccuracyTestBinary) { - auto param = GetParam(); - int type = std::get<0>(param); - cv::Size sz_in = std::get<1>(param); - int tt = std::get<2>(param); - - auto compile_args = std::get<4>(param); cv::Scalar thr = initScalarRandU(50); cv::Scalar maxval = initScalarRandU(50) + cv::Scalar(50, 50, 50, 50); - initMatrixRandU(type, sz_in, type, std::get<3>(param)); cv::Scalar out_scalar; // G-API code ////////////////////////////////////////////////////////////// @@ -827,7 +691,7 @@ TEST_P(ThresholdTest, AccuracyTestBinary) out = cv::gapi::threshold(in1, th1, mv1, tt); cv::GComputation c(GIn(in1, th1, mv1), GOut(out)); - c.apply(gin(in_mat1, thr, maxval), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, thr, maxval), gout(out_mat_gapi), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -835,21 +699,14 @@ TEST_P(ThresholdTest, AccuracyTestBinary) } // Comparison ////////////////////////////////////////////////////////////// { - ASSERT_EQ(out_mat_gapi.size(), sz_in); + ASSERT_EQ(out_mat_gapi.size(), sz); EXPECT_EQ(0, cv::norm(out_mat_ocv, out_mat_gapi, NORM_L1)); } } TEST_P(ThresholdOTTest, AccuracyTestOtsu) { - auto param = GetParam(); - int type = std::get<0>(param); - cv::Size sz_in = std::get<1>(param); - int tt = std::get<2>(param); - - auto compile_args = std::get<4>(param); cv::Scalar maxval = initScalarRandU(50) + cv::Scalar(50, 50, 50, 50); - initMatrixRandU(type, sz_in, type, std::get<3>(param)); cv::Scalar out_gapi_scalar; double ocv_res; @@ -859,7 +716,7 @@ TEST_P(ThresholdOTTest, AccuracyTestOtsu) std::tie(out, scout) = cv::gapi::threshold(in1, mv1, tt); cv::GComputation c(cv::GIn(in1, mv1), cv::GOut(out, scout)); - c.apply(gin(in_mat1, maxval), gout(out_mat_gapi, out_gapi_scalar), std::move(compile_args)); + c.apply(gin(in_mat1, maxval), gout(out_mat_gapi, out_gapi_scalar), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -868,21 +725,15 @@ TEST_P(ThresholdOTTest, AccuracyTestOtsu) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(out_mat_gapi.size(), sz); EXPECT_EQ(ocv_res, out_gapi_scalar.val[0]); } } TEST_P(InRangeTest, AccuracyTest) { - auto param = GetParam(); - int type = std::get<0>(param); - cv::Size sz_in = std::get<1>(param); - - auto compile_args = std::get<3>(param); cv::Scalar thrLow = initScalarRandU(100); cv::Scalar thrUp = initScalarRandU(100) + cv::Scalar(100, 100, 100, 100); - initMatrixRandU(type, sz_in, type, std::get<2>(param)); // G-API code ////////////////////////////////////////////////////////////// cv::GMat in1; @@ -890,7 +741,7 @@ TEST_P(InRangeTest, AccuracyTest) auto out = cv::gapi::inRange(in1, th1, mv1); cv::GComputation c(GIn(in1, th1, mv1), GOut(out)); - c.apply(gin(in_mat1, thrLow, thrUp), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, thrLow, thrUp), gout(out_mat_gapi), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -899,26 +750,22 @@ TEST_P(InRangeTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(Split3Test, AccuracyTest) { - cv::Size sz_in = std::get<0>(GetParam()); - auto compile_args = std::get<1>(GetParam()); - initMatrixRandU(CV_8UC3, sz_in, CV_8UC1); - - cv::Mat out_mat2 = cv::Mat(sz_in, CV_8UC1); - cv::Mat out_mat3 = cv::Mat(sz_in, CV_8UC1); - cv::Mat out_mat_ocv2 = cv::Mat(sz_in, CV_8UC1); - cv::Mat out_mat_ocv3 = cv::Mat(sz_in, CV_8UC1); + cv::Mat out_mat2 = cv::Mat(sz, dtype); + cv::Mat out_mat3 = cv::Mat(sz, dtype); + cv::Mat out_mat_ocv2 = cv::Mat(sz, dtype); + cv::Mat out_mat_ocv3 = cv::Mat(sz, dtype); // G-API code ////////////////////////////////////////////////////////////// cv::GMat in1, out1, out2, out3; std::tie(out1, out2, out3) = cv::gapi::split3(in1); cv::GComputation c(cv::GIn(in1), cv::GOut(out1, out2, out3)); - c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat2, out_mat3), std::move(compile_args)); + c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat2, out_mat3), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { std::vector out_mats_ocv = {out_mat_ocv, out_mat_ocv2, out_mat_ocv3}; @@ -934,22 +781,19 @@ TEST_P(Split3Test, AccuracyTest) TEST_P(Split4Test, AccuracyTest) { - cv::Size sz_in = std::get<0>(GetParam()); - auto compile_args = std::get<1>(GetParam()); - initMatrixRandU(CV_8UC4, sz_in, CV_8UC1); - cv::Mat out_mat2 = cv::Mat(sz_in, CV_8UC1); - cv::Mat out_mat3 = cv::Mat(sz_in, CV_8UC1); - cv::Mat out_mat4 = cv::Mat(sz_in, CV_8UC1); - cv::Mat out_mat_ocv2 = cv::Mat(sz_in, CV_8UC1); - cv::Mat out_mat_ocv3 = cv::Mat(sz_in, CV_8UC1); - cv::Mat out_mat_ocv4 = cv::Mat(sz_in, CV_8UC1); + cv::Mat out_mat2 = cv::Mat(sz, dtype); + cv::Mat out_mat3 = cv::Mat(sz, dtype); + cv::Mat out_mat4 = cv::Mat(sz, dtype); + cv::Mat out_mat_ocv2 = cv::Mat(sz, dtype); + cv::Mat out_mat_ocv3 = cv::Mat(sz, dtype); + cv::Mat out_mat_ocv4 = cv::Mat(sz, dtype); // G-API code ////////////////////////////////////////////////////////////// cv::GMat in1, out1, out2, out3, out4; std::tie(out1, out2, out3, out4) = cv::gapi::split4(in1); cv::GComputation c(cv::GIn(in1), cv::GOut(out1, out2, out3, out4)); - c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat2, out_mat3, out_mat4), std::move(compile_args)); + c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat2, out_mat3, out_mat4), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { std::vector out_mats_ocv = {out_mat_ocv, out_mat_ocv2, out_mat_ocv3, out_mat_ocv4}; @@ -964,7 +808,8 @@ TEST_P(Split4Test, AccuracyTest) } } -static void ResizeAccuracyTest(compare_f cmpF, int type, int interp, cv::Size sz_in, cv::Size sz_out, double fx, double fy, cv::GCompileArgs&& compile_args) +static void ResizeAccuracyTest(const CompareMats& cmpF, int type, int interp, cv::Size sz_in, + cv::Size sz_out, double fx, double fy, cv::GCompileArgs&& compile_args) { cv::Mat in_mat1 (sz_in, type ); cv::Scalar mean = cv::Scalar::all(127); @@ -996,31 +841,48 @@ static void ResizeAccuracyTest(compare_f cmpF, int type, int interp, cv::Size sz TEST_P(ResizeTest, AccuracyTest) { - compare_f cmpF; - int type = 0, interp = 0; - cv::Size sz_in, sz_out; - cv::GCompileArgs compile_args; - std::tie(cmpF, type, interp, sz_in, sz_out, compile_args) = GetParam(); - ResizeAccuracyTest(cmpF, type, interp, sz_in, sz_out, 0.0, 0.0, std::move(compile_args)); + ResizeAccuracyTest(cmpF, type, interp, sz, sz_out, 0.0, 0.0, getCompileArgs()); } TEST_P(ResizeTestFxFy, AccuracyTest) { - compare_f cmpF; - int type = 0, interp = 0; - cv::Size sz_in; - double fx = 0.0, fy = 0.0; - cv::GCompileArgs compile_args; - std::tie(cmpF, type, interp, sz_in, fx, fy, compile_args) = GetParam(); - ResizeAccuracyTest(cmpF, type, interp, sz_in, cv::Size{0, 0}, fx, fy, std::move(compile_args)); + ResizeAccuracyTest(cmpF, type, interp, sz, cv::Size{0, 0}, fx, fy, getCompileArgs()); +} + +TEST_P(ResizePTest, AccuracyTest) +{ + constexpr int planeNum = 3; + cv::Size sz_in_p {sz.width, sz.height*planeNum}; + cv::Size sz_out_p{sz_out.width, sz_out.height*planeNum}; + + cv::Mat in_mat(sz_in_p, CV_8UC1); + cv::randn(in_mat, cv::Scalar::all(127.0f), cv::Scalar::all(40.f)); + + cv::Mat out_mat (sz_out_p, CV_8UC1); + cv::Mat out_mat_ocv_p(sz_out_p, CV_8UC1); + + cv::GMatP in; + auto out = cv::gapi::resizeP(in, sz_out, interp); + cv::GComputation c(cv::GIn(in), cv::GOut(out)); + + c.compile(cv::descr_of(in_mat).asPlanar(planeNum), getCompileArgs()) + (cv::gin(in_mat), cv::gout(out_mat)); + + for (int i = 0; i < planeNum; i++) { + const cv::Mat in_mat_roi = in_mat(cv::Rect(0, i*sz.height, sz.width, sz.height)); + cv::Mat out_mat_roi = out_mat_ocv_p(cv::Rect(0, i*sz_out.height, sz_out.width, sz_out.height)); + cv::resize(in_mat_roi, out_mat_roi, sz_out, 0, 0, interp); + } + + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat, out_mat_ocv_p)); + } } TEST_P(Merge3Test, AccuracyTest) { - cv::Size sz_in = std::get<0>(GetParam()); - initMatsRandU(CV_8UC1, sz_in, CV_8UC3); - auto compile_args = std::get<1>(GetParam()); - cv::Mat in_mat3(sz_in, CV_8UC1); + cv::Mat in_mat3(sz, type); cv::Scalar mean = cv::Scalar::all(127); cv::Scalar stddev = cv::Scalar::all(40.f); @@ -1031,7 +893,7 @@ TEST_P(Merge3Test, AccuracyTest) auto out = cv::gapi::merge3(in1, in2, in3); cv::GComputation c(cv::GIn(in1, in2, in3), cv::GOut(out)); - c.apply(cv::gin(in_mat1, in_mat2, in_mat3), cv::gout(out_mat_gapi), std::move(compile_args)); + c.apply(cv::gin(in_mat1, in_mat2, in_mat3), cv::gout(out_mat_gapi), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { std::vector in_mats_ocv = {in_mat1, in_mat2, in_mat3}; @@ -1045,11 +907,8 @@ TEST_P(Merge3Test, AccuracyTest) TEST_P(Merge4Test, AccuracyTest) { - cv::Size sz_in = std::get<0>(GetParam()); - initMatsRandU(CV_8UC1, sz_in, CV_8UC4); - auto compile_args = std::get<1>(GetParam()); - cv::Mat in_mat3(sz_in, CV_8UC1); - cv::Mat in_mat4(sz_in, CV_8UC1); + cv::Mat in_mat3(sz, type); + cv::Mat in_mat4(sz, type); cv::Scalar mean = cv::Scalar::all(127); cv::Scalar stddev = cv::Scalar::all(40.f); @@ -1061,7 +920,7 @@ TEST_P(Merge4Test, AccuracyTest) auto out = cv::gapi::merge4(in1, in2, in3, in4); cv::GComputation c(cv::GIn(in1, in2, in3, in4), cv::GOut(out)); - c.apply(cv::gin(in_mat1, in_mat2, in_mat3, in_mat4), cv::gout(out_mat_gapi), std::move(compile_args)); + c.apply(cv::gin(in_mat1, in_mat2, in_mat3, in_mat4), cv::gout(out_mat_gapi), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { std::vector in_mats_ocv = {in_mat1, in_mat2, in_mat3, in_mat4}; @@ -1075,12 +934,7 @@ TEST_P(Merge4Test, AccuracyTest) TEST_P(RemapTest, AccuracyTest) { - auto param = GetParam(); - int type = std::get<0>(param); - cv::Size sz_in = std::get<1>(param); - auto compile_args = std::get<3>(param); - initMatrixRandU(type, sz_in, type, std::get<2>(param)); - cv::Mat in_map1(sz_in, CV_16SC2); + cv::Mat in_map1(sz, CV_16SC2); cv::Mat in_map2 = cv::Mat(); cv::randu(in_map1, cv::Scalar::all(0), cv::Scalar::all(255)); cv::Scalar bv = cv::Scalar(); @@ -1090,7 +944,7 @@ TEST_P(RemapTest, AccuracyTest) auto out = cv::gapi::remap(in1, in_map1, in_map2, cv::INTER_NEAREST, cv::BORDER_REPLICATE, bv); cv::GComputation c(in1, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -1099,25 +953,18 @@ TEST_P(RemapTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(FlipTest, AccuracyTest) { - auto param = GetParam(); - int type = std::get<0>(param); - int flipCode = std::get<1>(param); - cv::Size sz_in = std::get<2>(param); - initMatrixRandU(type, sz_in, type, false); - auto compile_args = std::get<4>(GetParam()); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::flip(in, flipCode); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::flip(in_mat1, out_mat_ocv, flipCode); @@ -1125,24 +972,17 @@ TEST_P(FlipTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(CropTest, AccuracyTest) { - auto param = GetParam(); - int type = std::get<0>(param); - cv::Rect rect_to = std::get<1>(param); - cv::Size sz_in = std::get<2>(param); - auto compile_args = std::get<4>(param); - - initMatrixRandU(type, sz_in, type, false); cv::Size sz_out = cv::Size(rect_to.width, rect_to.height); - if( std::get<3>(param) == true ) + if (dtype != -1) { - out_mat_gapi = cv::Mat(sz_out, type); - out_mat_ocv = cv::Mat(sz_out, type); + out_mat_gapi = cv::Mat(sz_out, dtype); + out_mat_ocv = cv::Mat(sz_out, dtype); } // G-API code ////////////////////////////////////////////////////////////// @@ -1150,7 +990,7 @@ TEST_P(CropTest, AccuracyTest) auto out = cv::gapi::crop(in, rect_to); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::Mat(in_mat1, rect_to).copyTo(out_mat_ocv); @@ -1164,17 +1004,14 @@ TEST_P(CropTest, AccuracyTest) TEST_P(ConcatHorTest, AccuracyTest) { - auto param = GetParam(); - int type = std::get<0>(param); - cv::Size sz_out = std::get<1>(param); - auto compile_args = std::get<2>(param); + cv::Size sz_out = sz; int wpart = sz_out.width / 4; cv::Size sz_in1 = cv::Size(wpart, sz_out.height); cv::Size sz_in2 = cv::Size(sz_out.width - wpart, sz_out.height); - cv::Mat in_mat1 (sz_in1, type ); - cv::Mat in_mat2 (sz_in2, type); + in_mat1 = cv::Mat(sz_in1, type ); + in_mat2 = cv::Mat(sz_in2, type); cv::Scalar mean = cv::Scalar::all(127); cv::Scalar stddev = cv::Scalar::all(40.f); @@ -1182,17 +1019,17 @@ TEST_P(ConcatHorTest, AccuracyTest) cv::randn(in_mat2, mean, stddev); cv::Mat out_mat(sz_out, type); - cv::Mat out_mat_ocv(sz_out, type); + out_mat_ocv = cv::Mat(sz_out, type); // G-API code ////////////////////////////////////////////////////////////// cv::GMat in1, in2; auto out = cv::gapi::concatHor(in1, in2); cv::GComputation c(GIn(in1, in2), GOut(out)); - c.apply(gin(in_mat1, in_mat2), gout(out_mat), std::move(compile_args)); + c.apply(gin(in_mat1, in_mat2), gout(out_mat), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { - cv::hconcat(in_mat1, in_mat2, out_mat_ocv ); + cv::hconcat(in_mat1, in_mat2, out_mat_ocv); } // Comparison ////////////////////////////////////////////////////////////// { @@ -1202,17 +1039,14 @@ TEST_P(ConcatHorTest, AccuracyTest) TEST_P(ConcatVertTest, AccuracyTest) { - auto param = GetParam(); - int type = std::get<0>(param); - cv::Size sz_out = std::get<1>(param); - auto compile_args = std::get<2>(param); + cv::Size sz_out = sz; int hpart = sz_out.height * 2/3; cv::Size sz_in1 = cv::Size(sz_out.width, hpart); cv::Size sz_in2 = cv::Size(sz_out.width, sz_out.height - hpart); - cv::Mat in_mat1 (sz_in1, type); - cv::Mat in_mat2 (sz_in2, type); + in_mat1 = cv::Mat(sz_in1, type); + in_mat2 = cv::Mat(sz_in2, type); cv::Scalar mean = cv::Scalar::all(127); cv::Scalar stddev = cv::Scalar::all(40.f); @@ -1220,14 +1054,14 @@ TEST_P(ConcatVertTest, AccuracyTest) cv::randn(in_mat2, mean, stddev); cv::Mat out_mat(sz_out, type); - cv::Mat out_mat_ocv(sz_out, type); + out_mat_ocv = cv::Mat(sz_out, type); // G-API code ////////////////////////////////////////////////////////////// cv::GMat in1, in2; auto out = cv::gapi::concatVert(in1, in2); cv::GComputation c(GIn(in1, in2), GOut(out)); - c.apply(gin(in_mat1, in_mat2), gout(out_mat), std::move(compile_args)); + c.apply(gin(in_mat1, in_mat2), gout(out_mat), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::vconcat(in_mat1, in_mat2, out_mat_ocv ); @@ -1240,10 +1074,7 @@ TEST_P(ConcatVertTest, AccuracyTest) TEST_P(ConcatVertVecTest, AccuracyTest) { - auto param = GetParam(); - int type = std::get<0>(param); - cv::Size sz_out = std::get<1>(param); - auto compile_args = std::get<2>(param); + cv::Size sz_out = sz; int hpart1 = sz_out.height * 2/5; int hpart2 = sz_out.height / 5; @@ -1251,9 +1082,9 @@ TEST_P(ConcatVertVecTest, AccuracyTest) cv::Size sz_in2 = cv::Size(sz_out.width, hpart2); cv::Size sz_in3 = cv::Size(sz_out.width, sz_out.height - hpart1 - hpart2); - cv::Mat in_mat1 (sz_in1, type); - cv::Mat in_mat2 (sz_in2, type); - cv::Mat in_mat3 (sz_in3, type); + in_mat1 = cv::Mat(sz_in1, type); + in_mat2 = cv::Mat(sz_in2, type); + cv::Mat in_mat3(sz_in3, type); cv::Scalar mean = cv::Scalar::all(127); cv::Scalar stddev = cv::Scalar::all(40.f); @@ -1262,7 +1093,7 @@ TEST_P(ConcatVertVecTest, AccuracyTest) cv::randn(in_mat3, mean, stddev); cv::Mat out_mat(sz_out, type); - cv::Mat out_mat_ocv(sz_out, type); + out_mat_ocv = cv::Mat(sz_out, type); // G-API code ////////////////////////////////////////////////////////////// std::vector mats(3); @@ -1271,7 +1102,7 @@ TEST_P(ConcatVertVecTest, AccuracyTest) std::vector cvmats = {in_mat1, in_mat2, in_mat3}; cv::GComputation c({mats[0], mats[1], mats[2]}, {out}); - c.apply(gin(in_mat1, in_mat2, in_mat3), gout(out_mat), std::move(compile_args)); + c.apply(gin(in_mat1, in_mat2, in_mat3), gout(out_mat), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -1285,10 +1116,7 @@ TEST_P(ConcatVertVecTest, AccuracyTest) TEST_P(ConcatHorVecTest, AccuracyTest) { - auto param = GetParam(); - int type = std::get<0>(param); - cv::Size sz_out = std::get<1>(param); - auto compile_args = std::get<2>(param); + cv::Size sz_out = sz; int wpart1 = sz_out.width / 3; int wpart2 = sz_out.width / 4; @@ -1296,8 +1124,8 @@ TEST_P(ConcatHorVecTest, AccuracyTest) cv::Size sz_in2 = cv::Size(wpart2, sz_out.height); cv::Size sz_in3 = cv::Size(sz_out.width - wpart1 - wpart2, sz_out.height); - cv::Mat in_mat1 (sz_in1, type); - cv::Mat in_mat2 (sz_in2, type); + in_mat1 = cv::Mat(sz_in1, type); + in_mat2 = cv::Mat(sz_in2, type); cv::Mat in_mat3 (sz_in3, type); cv::Scalar mean = cv::Scalar::all(127); cv::Scalar stddev = cv::Scalar::all(40.f); @@ -1307,7 +1135,7 @@ TEST_P(ConcatHorVecTest, AccuracyTest) cv::randn(in_mat3, mean, stddev); cv::Mat out_mat(sz_out, type); - cv::Mat out_mat_ocv(sz_out, type); + out_mat_ocv = cv::Mat(sz_out, type); // G-API code ////////////////////////////////////////////////////////////// std::vector mats(3); @@ -1316,7 +1144,7 @@ TEST_P(ConcatHorVecTest, AccuracyTest) std::vector cvmats = {in_mat1, in_mat2, in_mat3}; cv::GComputation c({mats[0], mats[1], mats[2]}, {out}); - c.apply(gin(in_mat1, in_mat2, in_mat3), gout(out_mat), std::move(compile_args)); + c.apply(gin(in_mat1, in_mat2, in_mat3), gout(out_mat), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -1330,14 +1158,11 @@ TEST_P(ConcatHorVecTest, AccuracyTest) TEST_P(LUTTest, AccuracyTest) { - auto param = GetParam(); - int type_mat = std::get<0>(param); - int type_lut = std::get<1>(param); + int type_mat = type; + int type_lut = dtype; int type_out = CV_MAKETYPE(CV_MAT_DEPTH(type_lut), CV_MAT_CN(type_mat)); - cv::Size sz_in = std::get<2>(param); - auto compile_args = std::get<4>(GetParam()); - initMatrixRandU(type_mat, sz_in, type_out); + initMatrixRandU(type_mat, sz, type_out); cv::Size sz_lut = cv::Size(1, 256); cv::Mat in_lut(sz_lut, type_lut); cv::randu(in_lut, cv::Scalar::all(0), cv::Scalar::all(255)); @@ -1347,7 +1172,7 @@ TEST_P(LUTTest, AccuracyTest) auto out = cv::gapi::LUT(in, in_lut); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::LUT(in_mat1, in_lut, out_mat_ocv); @@ -1355,52 +1180,42 @@ TEST_P(LUTTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(ConvertToTest, AccuracyTest) { - auto param = GetParam(); - int type_mat = std::get<0>(param); - int depth_to = std::get<1>(param); - cv::Size sz_in = std::get<2>(param); + int type_mat = type; + int depth_to = dtype; int type_out = CV_MAKETYPE(depth_to, CV_MAT_CN(type_mat)); - initMatrixRandU(type_mat, sz_in, type_out); - auto compile_args = std::get<3>(GetParam()); + initMatrixRandU(type_mat, sz, type_out); // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; - auto out = cv::gapi::convertTo(in, depth_to); + auto out = cv::gapi::convertTo(in, depth_to, alpha, beta); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { - in_mat1.convertTo(out_mat_ocv, depth_to); + in_mat1.convertTo(out_mat_ocv, depth_to, alpha, beta); } // Comparison ////////////////////////////////////////////////////////////// { - EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(PhaseTest, AccuracyTest) { - int img_type = -1; - cv::Size img_size; - bool angle_in_degrees = false; - cv::GCompileArgs compile_args; - std::tie(img_type, img_size, angle_in_degrees, compile_args) = GetParam(); - initMatsRandU(img_type, img_size, img_type); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in_x, in_y; auto out = cv::gapi::phase(in_x, in_y, angle_in_degrees); cv::GComputation c(in_x, in_y, out); - c.apply(in_mat1, in_mat2, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, in_mat2, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// cv::phase(in_mat1, in_mat2, out_mat_ocv, angle_in_degrees); @@ -1414,18 +1229,12 @@ TEST_P(PhaseTest, AccuracyTest) TEST_P(SqrtTest, AccuracyTest) { - int img_type = -1; - cv::Size img_size; - cv::GCompileArgs compile_args; - std::tie(img_type, img_size, compile_args) = GetParam(); - initMatrixRandU(img_type, img_size, img_type); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::sqrt(in); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// cv::sqrt(in_mat1, out_mat_ocv); @@ -1439,27 +1248,14 @@ TEST_P(SqrtTest, AccuracyTest) TEST_P(NormalizeTest, Test) { - auto param = GetParam(); - - compare_f cmpF; - MatType type, ddepth; - cv::Size sz; - double a = 0 , b = 0; - int norm_type = 0; - bool createOut = 0; - cv::GCompileArgs compile_args; - - std::tie(cmpF, type, sz, a, b, norm_type, ddepth, createOut, compile_args) = GetParam(); - int dtype = CV_MAKETYPE(ddepth, CV_MAT_CN(type)); - - initMatsRandN(type, sz, dtype, createOut); + initMatrixRandN(type, sz, CV_MAKETYPE(ddepth, CV_MAT_CN(type))); // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::normalize(in, a, b, norm_type, ddepth); cv::GComputation c(cv::GIn(in), cv::GOut(out)); - c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi), std::move(compile_args)); + c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -1472,6 +1268,249 @@ TEST_P(NormalizeTest, Test) } } +TEST_P(BackendOutputAllocationTest, EmptyOutput) +{ + // G-API code ////////////////////////////////////////////////////////////// + cv::GMat in1, in2, out; + out = cv::gapi::mul(in1, in2); + cv::GComputation c(cv::GIn(in1, in2), cv::GOut(out)); + + EXPECT_TRUE(out_mat_gapi.empty()); + c.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat_gapi), getCompileArgs()); + EXPECT_FALSE(out_mat_gapi.empty()); + + // OpenCV code ///////////////////////////////////////////////////////////// + cv::multiply(in_mat1, in_mat2, out_mat_ocv); + + // Comparison ////////////////////////////////////////////////////////////// + // Expected: output is allocated to the needed size + EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(sz, out_mat_gapi.size()); +} + +TEST_P(BackendOutputAllocationTest, CorrectlyPreallocatedOutput) +{ + out_mat_gapi = cv::Mat(sz, type); + auto out_mat_gapi_ref = out_mat_gapi; // shallow copy to ensure previous data is not deleted + + // G-API code ////////////////////////////////////////////////////////////// + cv::GMat in1, in2, out; + out = cv::gapi::add(in1, in2); + cv::GComputation c(cv::GIn(in1, in2), cv::GOut(out)); + c.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat_gapi), getCompileArgs()); + + // OpenCV code ///////////////////////////////////////////////////////////// + cv::add(in_mat1, in_mat2, out_mat_ocv); + + // Comparison ////////////////////////////////////////////////////////////// + // Expected: output is not reallocated + EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(sz, out_mat_gapi.size()); + + EXPECT_EQ(out_mat_gapi_ref.data, out_mat_gapi.data); +} + +TEST_P(BackendOutputAllocationTest, IncorrectOutputMeta) +{ + // G-API code ////////////////////////////////////////////////////////////// + cv::GMat in1, in2, out; + out = cv::gapi::add(in1, in2); + cv::GComputation c(cv::GIn(in1, in2), cv::GOut(out)); + + const auto run_and_compare = [&c, this] () + { + auto out_mat_gapi_ref = out_mat_gapi; // shallow copy to ensure previous data is not deleted + + // G-API code ////////////////////////////////////////////////////////////// + c.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat_gapi), getCompileArgs()); + + // OpenCV code ///////////////////////////////////////////////////////////// + cv::add(in_mat1, in_mat2, out_mat_ocv, cv::noArray()); + + // Comparison ////////////////////////////////////////////////////////////// + // Expected: size is changed, type is changed, output is reallocated + EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(sz, out_mat_gapi.size()); + EXPECT_EQ(type, out_mat_gapi.type()); + + EXPECT_NE(out_mat_gapi_ref.data, out_mat_gapi.data); + }; + + const auto chan = CV_MAT_CN(type); + + out_mat_gapi = cv::Mat(sz, CV_MAKE_TYPE(CV_64F, chan)); + run_and_compare(); + + out_mat_gapi = cv::Mat(sz, CV_MAKE_TYPE(CV_MAT_DEPTH(type), chan + 1)); + run_and_compare(); +} + +TEST_P(BackendOutputAllocationTest, SmallerPreallocatedSize) +{ + out_mat_gapi = cv::Mat(sz / 2, type); + auto out_mat_gapi_ref = out_mat_gapi; // shallow copy to ensure previous data is not deleted + + // G-API code ////////////////////////////////////////////////////////////// + cv::GMat in1, in2, out; + out = cv::gapi::mul(in1, in2); + cv::GComputation c(cv::GIn(in1, in2), cv::GOut(out)); + c.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat_gapi), getCompileArgs()); + + // OpenCV code ///////////////////////////////////////////////////////////// + cv::multiply(in_mat1, in_mat2, out_mat_ocv); + + // Comparison ////////////////////////////////////////////////////////////// + // Expected: size is changed, output is reallocated due to original size < curr size + EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(sz, out_mat_gapi.size()); + + EXPECT_NE(out_mat_gapi_ref.data, out_mat_gapi.data); +} + +TEST_P(BackendOutputAllocationTest, SmallerPreallocatedSizeWithSubmatrix) +{ + out_mat_gapi = cv::Mat(sz / 2, type); + + cv::Mat out_mat_gapi_submat = out_mat_gapi(cv::Rect({10, 0}, sz / 5)); + EXPECT_EQ(out_mat_gapi.data, out_mat_gapi_submat.datastart); + + auto out_mat_gapi_submat_ref = out_mat_gapi_submat; // shallow copy to ensure previous data is not deleted + + // G-API code ////////////////////////////////////////////////////////////// + cv::GMat in1, in2, out; + out = cv::gapi::mul(in1, in2); + cv::GComputation c(cv::GIn(in1, in2), cv::GOut(out)); + c.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat_gapi_submat), getCompileArgs()); + + // OpenCV code ///////////////////////////////////////////////////////////// + cv::multiply(in_mat1, in_mat2, out_mat_ocv); + + // Comparison ////////////////////////////////////////////////////////////// + // Expected: submatrix is reallocated and is "detached", original matrix is unchanged + EXPECT_EQ(0, cv::countNonZero(out_mat_gapi_submat != out_mat_ocv)); + EXPECT_EQ(sz, out_mat_gapi_submat.size()); + EXPECT_EQ(sz / 2, out_mat_gapi.size()); + + EXPECT_NE(out_mat_gapi_submat_ref.data, out_mat_gapi_submat.data); + EXPECT_NE(out_mat_gapi.data, out_mat_gapi_submat.datastart); +} + +TEST_P(BackendOutputAllocationTest, LargerPreallocatedSize) +{ + out_mat_gapi = cv::Mat(sz * 2, type); + auto out_mat_gapi_ref = out_mat_gapi; // shallow copy to ensure previous data is not deleted + + // G-API code ////////////////////////////////////////////////////////////// + cv::GMat in1, in2, out; + out = cv::gapi::mul(in1, in2); + cv::GComputation c(cv::GIn(in1, in2), cv::GOut(out)); + c.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat_gapi), getCompileArgs()); + + // OpenCV code ///////////////////////////////////////////////////////////// + cv::multiply(in_mat1, in_mat2, out_mat_ocv); + + // Comparison ////////////////////////////////////////////////////////////// + // Expected: size is changed, output is reallocated + EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(sz, out_mat_gapi.size()); + + EXPECT_NE(out_mat_gapi_ref.data, out_mat_gapi.data); +} + +TEST_P(BackendOutputAllocationLargeSizeWithCorrectSubmatrixTest, + LargerPreallocatedSizeWithCorrectSubmatrix) +{ + out_mat_gapi = cv::Mat(sz * 2, type); + auto out_mat_gapi_ref = out_mat_gapi; // shallow copy to ensure previous data is not deleted + + cv::Mat out_mat_gapi_submat = out_mat_gapi(cv::Rect({5, 8}, sz)); + EXPECT_EQ(out_mat_gapi.data, out_mat_gapi_submat.datastart); + + auto out_mat_gapi_submat_ref = out_mat_gapi_submat; + + // G-API code ////////////////////////////////////////////////////////////// + cv::GMat in1, in2, out; + out = cv::gapi::mul(in1, in2); + cv::GComputation c(cv::GIn(in1, in2), cv::GOut(out)); + c.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat_gapi_submat), getCompileArgs()); + + // OpenCV code ///////////////////////////////////////////////////////////// + cv::multiply(in_mat1, in_mat2, out_mat_ocv); + + // Comparison ////////////////////////////////////////////////////////////// + // Expected: submatrix is not reallocated, original matrix is not reallocated + EXPECT_EQ(0, cv::countNonZero(out_mat_gapi_submat != out_mat_ocv)); + EXPECT_EQ(sz, out_mat_gapi_submat.size()); + EXPECT_EQ(sz * 2, out_mat_gapi.size()); + + EXPECT_EQ(out_mat_gapi_ref.data, out_mat_gapi.data); + EXPECT_EQ(out_mat_gapi_submat_ref.data, out_mat_gapi_submat.data); + EXPECT_EQ(out_mat_gapi.data, out_mat_gapi_submat.datastart); +} + +TEST_P(BackendOutputAllocationTest, LargerPreallocatedSizeWithSmallSubmatrix) +{ + out_mat_gapi = cv::Mat(sz * 2, type); + auto out_mat_gapi_ref = out_mat_gapi; // shallow copy to ensure previous data is not deleted + + cv::Mat out_mat_gapi_submat = out_mat_gapi(cv::Rect({5, 8}, sz / 2)); + EXPECT_EQ(out_mat_gapi.data, out_mat_gapi_submat.datastart); + + auto out_mat_gapi_submat_ref = out_mat_gapi_submat; + + // G-API code ////////////////////////////////////////////////////////////// + cv::GMat in1, in2, out; + out = cv::gapi::mul(in1, in2); + cv::GComputation c(cv::GIn(in1, in2), cv::GOut(out)); + c.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat_gapi_submat), getCompileArgs()); + + // OpenCV code ///////////////////////////////////////////////////////////// + cv::multiply(in_mat1, in_mat2, out_mat_ocv); + + // Comparison ////////////////////////////////////////////////////////////// + // Expected: submatrix is reallocated and is "detached", original matrix is unchanged + EXPECT_EQ(0, cv::countNonZero(out_mat_gapi_submat != out_mat_ocv)); + EXPECT_EQ(sz, out_mat_gapi_submat.size()); + EXPECT_EQ(sz * 2, out_mat_gapi.size()); + + EXPECT_EQ(out_mat_gapi_ref.data, out_mat_gapi.data); + EXPECT_NE(out_mat_gapi_submat_ref.data, out_mat_gapi_submat.data); + EXPECT_NE(out_mat_gapi.data, out_mat_gapi_submat.datastart); +} + +TEST_P(ReInitOutTest, TestWithAdd) +{ + in_mat1 = cv::Mat(sz, type); + in_mat2 = cv::Mat(sz, type); + cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(100)); + cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(100)); + + // G-API code ////////////////////////////////////////////////////////////// + cv::GMat in1, in2, out; + out = cv::gapi::add(in1, in2, dtype); + cv::GComputation c(cv::GIn(in1, in2), cv::GOut(out)); + + const auto run_and_compare = [&c, this] () + { + // G-API code ////////////////////////////////////////////////////////////// + c.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat_gapi), getCompileArgs()); + + // OpenCV code ///////////////////////////////////////////////////////////// + cv::add(in_mat1, in_mat2, out_mat_ocv, cv::noArray()); + + // Comparison ////////////////////////////////////////////////////////////// + EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + }; + + // run for uninitialized output + run_and_compare(); + + // run for initialized output (can be initialized with a different size) + initOutMats(out_sz, type); + run_and_compare(); +} + } // opencv_test #endif //OPENCV_GAPI_CORE_TESTS_INL_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests.hpp index 14aac47f62884f..bb75f820e69b9b 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests.hpp @@ -2,7 +2,7 @@ // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // -// Copyright (C) 2018 Intel Corporation +// Copyright (C) 2018-2019 Intel Corporation #ifndef OPENCV_GAPI_IMGPROC_TESTS_HPP @@ -14,32 +14,45 @@ namespace opencv_test { - -struct Filter2DTest : public TestParams > {}; -struct BoxFilterTest : public TestParams > {}; -struct SepFilterTest : public TestParams > {}; -struct BlurTest : public TestParams > {}; -struct GaussianBlurTest : public TestParams > {}; -struct MedianBlurTest : public TestParams > {}; -struct ErodeTest : public TestParams > {}; -struct Erode3x3Test : public TestParams > {}; -struct DilateTest : public TestParams > {}; -struct Dilate3x3Test : public TestParams > {}; -struct SobelTest : public TestParams > {}; -struct SobelXYTest : public TestParams > {}; -struct EqHistTest : public TestParams > {}; -struct CannyTest : public TestParams > {}; -struct RGB2GrayTest : public TestParams> {}; -struct BGR2GrayTest : public TestParams> {}; -struct RGB2YUVTest : public TestParams> {}; -struct YUV2RGBTest : public TestParams> {}; -struct NV12toRGBTest : public TestParams> {}; -struct NV12toBGRTest : public TestParams> {}; -struct RGB2LabTest : public TestParams> {}; -struct BGR2LUVTest : public TestParams> {}; -struct LUV2BGRTest : public TestParams> {}; -struct BGR2YUVTest : public TestParams> {}; -struct YUV2BGRTest : public TestParams> {}; +GAPI_TEST_FIXTURE(Filter2DTest, initMatrixRandN, FIXTURE_API(CompareMats,int,int), 3, + cmpF, kernSize, borderType) +GAPI_TEST_FIXTURE(BoxFilterTest, initMatrixRandN, FIXTURE_API(CompareMats,int,int), 3, + cmpF, filterSize, borderType) +GAPI_TEST_FIXTURE(SepFilterTest, initMatrixRandN, FIXTURE_API(CompareMats,int), 2, cmpF, kernSize) +GAPI_TEST_FIXTURE(BlurTest, initMatrixRandN, FIXTURE_API(CompareMats,int,int), 3, + cmpF, filterSize, borderType) +GAPI_TEST_FIXTURE(GaussianBlurTest, initMatrixRandN, FIXTURE_API(CompareMats,int), 2, cmpF, kernSize) +GAPI_TEST_FIXTURE(MedianBlurTest, initMatrixRandN, FIXTURE_API(CompareMats,int), 2, cmpF, kernSize) +GAPI_TEST_FIXTURE(ErodeTest, initMatrixRandN, FIXTURE_API(CompareMats,int,int), 3, + cmpF, kernSize, kernType) +GAPI_TEST_FIXTURE(Erode3x3Test, initMatrixRandN, FIXTURE_API(CompareMats,int), 2, + cmpF, numIters) +GAPI_TEST_FIXTURE(DilateTest, initMatrixRandN, FIXTURE_API(CompareMats,int,int), 3, + cmpF, kernSize, kernType) +GAPI_TEST_FIXTURE(Dilate3x3Test, initMatrixRandN, FIXTURE_API(CompareMats,int), 2, cmpF, numIters) +GAPI_TEST_FIXTURE(SobelTest, initMatrixRandN, FIXTURE_API(CompareMats,int,int,int), 4, + cmpF, kernSize, dx, dy) +GAPI_TEST_FIXTURE(SobelXYTest, initMatrixRandN, FIXTURE_API(CompareMats,int,int,int,int), 5, + cmpF, kernSize, order, border_type, border_val) +GAPI_TEST_FIXTURE(EqHistTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(CannyTest, initMatrixRandN, FIXTURE_API(CompareMats,double,double,int,bool), 5, + cmpF, thrLow, thrUp, apSize, l2gr) +GAPI_TEST_FIXTURE(RGB2GrayTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(BGR2GrayTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(RGB2YUVTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(YUV2RGBTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(NV12toRGBTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(NV12toBGRpTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(NV12toRGBpTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(NV12toBGRTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(RGB2LabTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(BGR2LUVTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(LUV2BGRTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(BGR2YUVTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(YUV2BGRTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(RGB2HSVTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(BayerGR2RGBTest, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) +GAPI_TEST_FIXTURE(RGB2YUV422Test, initMatrixRandN, FIXTURE_API(CompareMats), 1, cmpF) } // opencv_test #endif //OPENCV_GAPI_IMGPROC_TESTS_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests_inl.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests_inl.hpp index 08a317a5c5699c..396f50c1e235d1 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests_inl.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_imgproc_tests_inl.hpp @@ -2,32 +2,62 @@ // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // -// Copyright (C) 2018 Intel Corporation +// Copyright (C) 2018-2019 Intel Corporation #ifndef OPENCV_GAPI_IMGPROC_TESTS_INL_HPP #define OPENCV_GAPI_IMGPROC_TESTS_INL_HPP -#include "opencv2/gapi/imgproc.hpp" +#include #include "gapi_imgproc_tests.hpp" namespace opencv_test { -TEST_P(Filter2DTest, AccuracyTest) + +// FIXME avoid this code duplicate in perf tests +namespace { - compare_f cmpF; - MatType type = 0; - int kernSize = 0, borderType = 0, dtype = 0; - cv::Size sz; - bool initOut = false; - cv::GCompileArgs compile_args; - std::tie(cmpF, type, kernSize, sz, borderType, dtype, initOut, compile_args) = GetParam(); - initMatsRandN(type, sz, dtype, initOut); + void rgb2yuyv(const uchar* rgb_line, uchar* yuv422_line, int width) + { + CV_Assert(width % 2 == 0); + + for (int i = 0; i < width; i += 2) + { + uchar r = rgb_line[i * 3 ]; + uchar g = rgb_line[i * 3 + 1]; + uchar b = rgb_line[i * 3 + 2]; + + yuv422_line[i * 2 ] = cv::saturate_cast(-0.14713 * r - 0.28886 * g + 0.436 * b + 128.f); // U0 + yuv422_line[i * 2 + 1] = cv::saturate_cast( 0.299 * r + 0.587 * g + 0.114 * b ); // Y0 + yuv422_line[i * 2 + 2] = cv::saturate_cast( 0.615 * r - 0.51499 * g - 0.10001 * b + 128.f); // V0 + + r = rgb_line[i * 3 + 3]; + g = rgb_line[i * 3 + 4]; + b = rgb_line[i * 3 + 5]; + + yuv422_line[i * 2 + 3] = cv::saturate_cast(0.299 * r + 0.587 * g + 0.114 * b); // Y1 + } + } + void convertRGB2YUV422Ref(const cv::Mat& in, cv::Mat &out) + { + out.create(in.size(), CV_8UC2); + + for (int i = 0; i < in.rows; ++i) + { + const uchar* in_line_p = in.ptr(i); + uchar* out_line_p = out.ptr(i); + rgb2yuyv(in_line_p, out_line_p, in.cols); + } + } +} + +TEST_P(Filter2DTest, AccuracyTest) +{ cv::Point anchor = {-1, -1}; double delta = 0; - cv::Mat kernel = cv::Mat(kernSize, kernSize, CV_32FC1 ); + cv::Mat kernel = cv::Mat(kernSize, kernSize, CV_32FC1); cv::Scalar kernMean = cv::Scalar(1.0); cv::Scalar kernStddev = cv::Scalar(2.0/3); randn(kernel, kernMean, kernStddev); @@ -37,7 +67,7 @@ TEST_P(Filter2DTest, AccuracyTest) auto out = cv::gapi::filter2D(in, dtype, kernel, anchor, delta, borderType); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::filter2D(in_mat1, out_mat_ocv, dtype, kernel, anchor, delta, borderType); @@ -51,27 +81,20 @@ TEST_P(Filter2DTest, AccuracyTest) TEST_P(BoxFilterTest, AccuracyTest) { - compare_f cmpF; - MatType type = 0; - int filterSize = 0, borderType = 0, dtype = 0; - cv::Size sz; - bool initOut = false; - cv::GCompileArgs compile_args; - std::tie(cmpF, type, filterSize, sz, borderType, dtype, initOut, compile_args) = GetParam(); - initMatsRandN(type, sz, dtype, initOut); - cv::Point anchor = {-1, -1}; bool normalize = true; // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; - auto out = cv::gapi::boxFilter(in, dtype, cv::Size(filterSize, filterSize), anchor, normalize, borderType); + auto out = cv::gapi::boxFilter(in, dtype, cv::Size(filterSize, filterSize), anchor, normalize, + borderType); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { - cv::boxFilter(in_mat1, out_mat_ocv, dtype, cv::Size(filterSize, filterSize), anchor, normalize, borderType); + cv::boxFilter(in_mat1, out_mat_ocv, dtype, cv::Size(filterSize, filterSize), anchor, + normalize, borderType); } // Comparison ////////////////////////////////////////////////////////////// { @@ -82,19 +105,10 @@ TEST_P(BoxFilterTest, AccuracyTest) TEST_P(SepFilterTest, AccuracyTest) { - compare_f cmpF; - MatType type = 0; - int kernSize = 0, dtype = 0; - cv::Size sz; - bool initOut = false; - cv::GCompileArgs compile_args; - std::tie(cmpF, type, kernSize, sz, dtype, initOut, compile_args) = GetParam(); - cv::Mat kernelX(kernSize, 1, CV_32F); cv::Mat kernelY(kernSize, 1, CV_32F); randu(kernelX, -1, 1); randu(kernelY, -1, 1); - initMatsRandN(type, sz, dtype, initOut); cv::Point anchor = cv::Point(-1, -1); @@ -103,7 +117,7 @@ TEST_P(SepFilterTest, AccuracyTest) auto out = cv::gapi::sepFilter(in, dtype, kernelX, kernelY, anchor, cv::Scalar() ); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::sepFilter2D(in_mat1, out_mat_ocv, dtype, kernelX, kernelY ); @@ -117,15 +131,6 @@ TEST_P(SepFilterTest, AccuracyTest) TEST_P(BlurTest, AccuracyTest) { - compare_f cmpF; - MatType type = 0; - int filterSize = 0, borderType = 0; - cv::Size sz; - bool initOut = false; - cv::GCompileArgs compile_args; - std::tie(cmpF, type, filterSize, sz, borderType, initOut, compile_args) = GetParam(); - initMatsRandN(type, sz, type, initOut); - cv::Point anchor = {-1, -1}; // G-API code ////////////////////////////////////////////////////////////// @@ -133,7 +138,7 @@ TEST_P(BlurTest, AccuracyTest) auto out = cv::gapi::blur(in, cv::Size(filterSize, filterSize), anchor, borderType); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::blur(in_mat1, out_mat_ocv, cv::Size(filterSize, filterSize), anchor, borderType); @@ -147,15 +152,6 @@ TEST_P(BlurTest, AccuracyTest) TEST_P(GaussianBlurTest, AccuracyTest) { - compare_f cmpF; - MatType type = 0; - int kernSize = 0; - cv::Size sz; - bool initOut = false; - cv::GCompileArgs compile_args; - std::tie(cmpF,type, kernSize, sz, initOut, compile_args) = GetParam(); - initMatsRandN(type, sz, type, initOut); - cv::Size kSize = cv::Size(kernSize, kernSize); double sigmaX = rand(); @@ -164,7 +160,7 @@ TEST_P(GaussianBlurTest, AccuracyTest) auto out = cv::gapi::gaussianBlur(in, kSize, sigmaX); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::GaussianBlur(in_mat1, out_mat_ocv, kSize, sigmaX); @@ -178,21 +174,12 @@ TEST_P(GaussianBlurTest, AccuracyTest) TEST_P(MedianBlurTest, AccuracyTest) { - compare_f cmpF; - MatType type = 0; - int kernSize = 0; - cv::Size sz; - bool initOut = false; - cv::GCompileArgs compile_args; - std::tie(cmpF, type, kernSize, sz, initOut, compile_args) = GetParam(); - initMatsRandN(type, sz, type, initOut); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::medianBlur(in, kernSize); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::medianBlur(in_mat1, out_mat_ocv, kernSize); @@ -206,15 +193,6 @@ TEST_P(MedianBlurTest, AccuracyTest) TEST_P(ErodeTest, AccuracyTest) { - compare_f cmpF; - MatType type = 0; - int kernSize = 0, kernType = 0; - cv::Size sz; - bool initOut = false; - cv::GCompileArgs compile_args; - std::tie(cmpF, type, kernSize, sz, kernType, initOut, compile_args) = GetParam(); - initMatsRandN(type, sz, type, initOut); - cv::Mat kernel = cv::getStructuringElement(kernType, cv::Size(kernSize, kernSize)); // G-API code ////////////////////////////////////////////////////////////// @@ -222,7 +200,7 @@ TEST_P(ErodeTest, AccuracyTest) auto out = cv::gapi::erode(in, kernel); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::erode(in_mat1, out_mat_ocv, kernel); @@ -236,15 +214,6 @@ TEST_P(ErodeTest, AccuracyTest) TEST_P(Erode3x3Test, AccuracyTest) { - compare_f cmpF; - MatType type = 0; - int numIters = 0; - cv::Size sz; - bool initOut = false; - cv::GCompileArgs compile_args; - std::tie(cmpF, type, sz, initOut, numIters, compile_args) = GetParam(); - initMatsRandN(type, sz, type, initOut); - cv::Mat kernel = cv::getStructuringElement(cv::MorphShapes::MORPH_RECT, cv::Size(3,3)); // G-API code ////////////////////////////////////////////////////////////// @@ -252,7 +221,7 @@ TEST_P(Erode3x3Test, AccuracyTest) auto out = cv::gapi::erode3x3(in, numIters); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::erode(in_mat1, out_mat_ocv, kernel, cv::Point(-1, -1), numIters); @@ -266,15 +235,6 @@ TEST_P(Erode3x3Test, AccuracyTest) TEST_P(DilateTest, AccuracyTest) { - compare_f cmpF; - MatType type = 0; - int kernSize = 0, kernType = 0; - cv::Size sz; - bool initOut = false; - cv::GCompileArgs compile_args; - std::tie(cmpF, type, kernSize, sz, kernType, initOut, compile_args) = GetParam(); - initMatsRandN(type, sz, type, initOut); - cv::Mat kernel = cv::getStructuringElement(kernType, cv::Size(kernSize, kernSize)); // G-API code ////////////////////////////////////////////////////////////// @@ -282,7 +242,7 @@ TEST_P(DilateTest, AccuracyTest) auto out = cv::gapi::dilate(in, kernel); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::dilate(in_mat1, out_mat_ocv, kernel); @@ -296,15 +256,6 @@ TEST_P(DilateTest, AccuracyTest) TEST_P(Dilate3x3Test, AccuracyTest) { - compare_f cmpF; - MatType type = 0; - int numIters = 0; - cv::Size sz; - bool initOut = false; - cv::GCompileArgs compile_args; - std::tie(cmpF, type, sz, initOut, numIters, compile_args) = GetParam(); - initMatsRandN(type, sz, type, initOut); - cv::Mat kernel = cv::getStructuringElement(cv::MorphShapes::MORPH_RECT, cv::Size(3,3)); // G-API code ////////////////////////////////////////////////////////////// @@ -312,7 +263,7 @@ TEST_P(Dilate3x3Test, AccuracyTest) auto out = cv::gapi::dilate3x3(in, numIters); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::dilate(in_mat1, out_mat_ocv, kernel, cv::Point(-1,-1), numIters); @@ -324,24 +275,14 @@ TEST_P(Dilate3x3Test, AccuracyTest) } } - TEST_P(SobelTest, AccuracyTest) { - compare_f cmpF; - MatType type = 0; - int kernSize = 0, dtype = 0, dx = 0, dy = 0; - cv::Size sz; - bool initOut = false; - cv::GCompileArgs compile_args; - std::tie(cmpF, type, kernSize, sz, dtype, dx, dy, initOut, compile_args) = GetParam(); - initMatsRandN(type, sz, dtype, initOut); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::Sobel(in, dtype, dx, dy, kernSize ); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::Sobel(in_mat1, out_mat_ocv, dtype, dx, dy, kernSize); @@ -355,24 +296,15 @@ TEST_P(SobelTest, AccuracyTest) TEST_P(SobelXYTest, AccuracyTest) { - compare_f cmpF; - MatType type = 0; - int kernSize = 0, dtype = 0, order = 0, border_type = 0, border_val = 0; - cv::Size sz; - cv::GCompileArgs compile_args; - std::tie(cmpF, type, kernSize, sz, dtype, order, border_type, border_val, compile_args) = GetParam(); - cv::Mat out_mat_ocv2; cv::Mat out_mat_gapi2; - initMatsRandN(type, sz, dtype); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::SobelXY(in, dtype, order, kernSize, 1, 0, border_type, border_val); cv::GComputation c(cv::GIn(in), cv::GOut(std::get<0>(out), std::get<1>(out))); - c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat_gapi2), std::move(compile_args)); + c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi, out_mat_gapi2), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { // workaround for cv::Sobel @@ -397,19 +329,12 @@ TEST_P(SobelXYTest, AccuracyTest) TEST_P(EqHistTest, AccuracyTest) { - compare_f cmpF; - cv::Size sz; - bool initOut = false; - cv::GCompileArgs compile_args; - std::tie(cmpF, sz, initOut, compile_args) = GetParam(); - initMatsRandN(CV_8UC1, sz, CV_8UC1, initOut); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::equalizeHist(in); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::equalizeHist(in_mat1, out_mat_ocv); @@ -417,29 +342,18 @@ TEST_P(EqHistTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), std::get<1>(GetParam())); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(CannyTest, AccuracyTest) { - compare_f cmpF; - MatType type; - int apSize = 0; - double thrLow = 0.0, thrUp = 0.0; - cv::Size sz; - bool l2gr = false, initOut = false; - cv::GCompileArgs compile_args; - std::tie(cmpF, type, sz, thrLow, thrUp, apSize, l2gr, initOut, compile_args) = GetParam(); - - initMatsRandN(type, sz, CV_8UC1, initOut); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::Canny(in, thrLow, thrUp, apSize, l2gr); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::Canny(in_mat1, out_mat_ocv, thrLow, thrUp, apSize, l2gr); @@ -453,17 +367,12 @@ TEST_P(CannyTest, AccuracyTest) TEST_P(RGB2GrayTest, AccuracyTest) { - auto param = GetParam(); - auto compile_args = std::get<3>(param); - compare_f cmpF = std::get<0>(param); - initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC1, std::get<2>(param)); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::RGB2Gray(in); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_RGB2GRAY); @@ -471,23 +380,18 @@ TEST_P(RGB2GrayTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(BGR2GrayTest, AccuracyTest) { - auto param = GetParam(); - auto compile_args = std::get<3>(param); - compare_f cmpF = std::get<0>(param); - initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC1, std::get<2>(param)); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::BGR2Gray(in); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_BGR2GRAY); @@ -495,23 +399,18 @@ TEST_P(BGR2GrayTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(RGB2YUVTest, AccuracyTest) { - auto param = GetParam(); - auto compile_args = std::get<3>(param); - compare_f cmpF = std::get<0>(param); - initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC3, std::get<2>(param)); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::RGB2YUV(in); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_RGB2YUV); @@ -519,24 +418,18 @@ TEST_P(RGB2YUVTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(YUV2RGBTest, AccuracyTest) { - auto param = GetParam(); - auto compile_args = std::get<3>(param); - compare_f cmpF = std::get<0>(param); - initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC3, std::get<2>(param)); - - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::YUV2RGB(in); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_YUV2RGB); @@ -544,19 +437,12 @@ TEST_P(YUV2RGBTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(NV12toRGBTest, AccuracyTest) { - compare_f cmpF; - cv::Size sz; - cv::GCompileArgs compile_args; - std::tie(cmpF, sz, compile_args) = GetParam(); - - initMatsRandN(CV_8UC1, sz, CV_8UC3); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in_y; cv::GMat in_uv; @@ -567,7 +453,7 @@ TEST_P(NV12toRGBTest, AccuracyTest) cv::randn(in_mat_uv, cv::Scalar::all(127), cv::Scalar::all(40.f)); cv::GComputation c(cv::GIn(in_y, in_uv), cv::GOut(out)); - c.apply(cv::gin(in_mat1, in_mat_uv), cv::gout(out_mat_gapi), std::move(compile_args)); + c.apply(cv::gin(in_mat1, in_mat_uv), cv::gout(out_mat_gapi), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::cvtColorTwoPlane(in_mat1, in_mat_uv, out_mat_ocv, cv::COLOR_YUV2RGB_NV12); @@ -581,13 +467,6 @@ TEST_P(NV12toRGBTest, AccuracyTest) TEST_P(NV12toBGRTest, AccuracyTest) { - compare_f cmpF; - cv::Size sz; - cv::GCompileArgs compile_args; - std::tie(cmpF, sz, compile_args) = GetParam(); - - initMatsRandN(CV_8UC1, sz, CV_8UC3); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in_y; cv::GMat in_uv; @@ -598,7 +477,7 @@ TEST_P(NV12toBGRTest, AccuracyTest) cv::randn(in_mat_uv, cv::Scalar::all(127), cv::Scalar::all(40.f)); cv::GComputation c(cv::GIn(in_y, in_uv), cv::GOut(out)); - c.apply(cv::gin(in_mat1, in_mat_uv), cv::gout(out_mat_gapi), std::move(compile_args)); + c.apply(cv::gin(in_mat1, in_mat_uv), cv::gout(out_mat_gapi), getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::cvtColorTwoPlane(in_mat1, in_mat_uv, out_mat_ocv, cv::COLOR_YUV2BGR_NV12); @@ -610,19 +489,88 @@ TEST_P(NV12toBGRTest, AccuracyTest) } } -TEST_P(RGB2LabTest, AccuracyTest) + +static void toPlanar(const cv::Mat& in, cv::Mat& out) +{ + GAPI_Assert(out.depth() == in.depth()); + GAPI_Assert(out.channels() == 1); + GAPI_Assert(in.channels() == 3); + GAPI_Assert(out.cols == in.cols); + GAPI_Assert(out.rows == 3*in.rows); + + std::vector outs(3); + for (int i = 0; i < 3; i++) { + outs[i] = out(cv::Rect(0, i*in.rows, in.cols, in.rows)); + } + cv::split(in, outs); +} + +TEST_P(NV12toRGBpTest, AccuracyTest) +{ + cv::Size sz_p = cv::Size(sz.width, sz.height * 3); + // G-API code ////////////////////////////////////////////////////////////// + cv::GMat in_y; + cv::GMat in_uv; + auto out = cv::gapi::NV12toRGBp(in_y, in_uv); + + // Additional mat for uv + cv::Mat in_mat_uv(cv::Size(sz.width / 2, sz.height / 2), CV_8UC2); + cv::randn(in_mat_uv, cv::Scalar::all(127), cv::Scalar::all(40.f)); + + cv::GComputation c(cv::GIn(in_y, in_uv), cv::GOut(out)); + cv::Mat out_mat_gapi_planar(cv::Size(sz.width, sz.height * 3), CV_8UC1); + c.apply(cv::gin(in_mat1, in_mat_uv), cv::gout(out_mat_gapi_planar), getCompileArgs()); + // OpenCV code ///////////////////////////////////////////////////////////// + cv::Mat out_mat_ocv_planar(cv::Size(sz.width, sz.height * 3), CV_8UC1); + { + cv::cvtColorTwoPlane(in_mat1, in_mat_uv, out_mat_ocv, cv::COLOR_YUV2RGB_NV12); + toPlanar(out_mat_ocv, out_mat_ocv_planar); + } + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi_planar, out_mat_ocv_planar)); + EXPECT_EQ(out_mat_gapi_planar.size(), sz_p); + } +} + + +TEST_P(NV12toBGRpTest, AccuracyTest) { - auto param = GetParam(); - auto compile_args = std::get<3>(param); - compare_f cmpF = std::get<0>(param); - initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC3, std::get<2>(param)); + cv::Size sz_p = cv::Size(sz.width, sz.height * 3); + + // G-API code ////////////////////////////////////////////////////////////// + cv::GMat in_y; + cv::GMat in_uv; + auto out = cv::gapi::NV12toBGRp(in_y, in_uv); + + // Additional mat for uv + cv::Mat in_mat_uv(cv::Size(sz.width / 2, sz.height / 2), CV_8UC2); + cv::randn(in_mat_uv, cv::Scalar::all(127), cv::Scalar::all(40.f)); + + cv::GComputation c(cv::GIn(in_y, in_uv), cv::GOut(out)); + cv::Mat out_mat_gapi_planar(cv::Size(sz.width, sz.height * 3), CV_8UC1); + c.apply(cv::gin(in_mat1, in_mat_uv), cv::gout(out_mat_gapi_planar), getCompileArgs()); + // OpenCV code ///////////////////////////////////////////////////////////// + cv::Mat out_mat_ocv_planar(cv::Size(sz.width, sz.height * 3), CV_8UC1); + { + cv::cvtColorTwoPlane(in_mat1, in_mat_uv, out_mat_ocv, cv::COLOR_YUV2BGR_NV12); + toPlanar(out_mat_ocv, out_mat_ocv_planar); + } + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi_planar, out_mat_ocv_planar)); + EXPECT_EQ(out_mat_gapi_planar.size(), sz_p); + } +} +TEST_P(RGB2LabTest, AccuracyTest) +{ // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::RGB2Lab(in); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_RGB2Lab); @@ -630,23 +578,18 @@ TEST_P(RGB2LabTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(BGR2LUVTest, AccuracyTest) { - auto param = GetParam(); - auto compile_args = std::get<3>(param); - compare_f cmpF = std::get<0>(param); - initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC3, std::get<2>(param)); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::BGR2LUV(in); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_BGR2Luv); @@ -654,23 +597,18 @@ TEST_P(BGR2LUVTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(LUV2BGRTest, AccuracyTest) { - auto param = GetParam(); - auto compile_args = std::get<3>(param); - compare_f cmpF = std::get<0>(param); - initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC3, std::get<2>(param)); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::LUV2BGR(in); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_Luv2BGR); @@ -678,23 +616,18 @@ TEST_P(LUV2BGRTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(BGR2YUVTest, AccuracyTest) { - auto param = GetParam(); - auto compile_args = std::get<3>(param); - compare_f cmpF = std::get<0>(param); - initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC3, std::get<2>(param)); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::BGR2YUV(in); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_BGR2YUV); @@ -702,23 +635,18 @@ TEST_P(BGR2YUVTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + EXPECT_EQ(out_mat_gapi.size(), sz); } } TEST_P(YUV2BGRTest, AccuracyTest) { - auto param = GetParam(); - auto compile_args = std::get<3>(param); - compare_f cmpF = std::get<0>(param); - initMatsRandN(CV_8UC3, std::get<1>(param), CV_8UC3, std::get<2>(param)); - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = cv::gapi::YUV2BGR(in); cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_YUV2BGR); @@ -726,7 +654,64 @@ TEST_P(YUV2BGRTest, AccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); - EXPECT_EQ(out_mat_gapi.size(), std::get<1>(param)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } +} + +TEST_P(RGB2HSVTest, AccuracyTest) +{ + // G-API code ////////////////////////////////////////////////////////////// + cv::GMat in; + auto out = cv::gapi::RGB2HSV(in); + + cv::GComputation c(in, out); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); + // OpenCV code ///////////////////////////////////////////////////////////// + { + cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_RGB2HSV); + } + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } +} + +TEST_P(BayerGR2RGBTest, AccuracyTest) +{ + // G-API code ////////////////////////////////////////////////////////////// + cv::GMat in; + auto out = cv::gapi::BayerGR2RGB(in); + + cv::GComputation c(in, out); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); + // OpenCV code ///////////////////////////////////////////////////////////// + { + cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_BayerGR2RGB); + } + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + } +} + +TEST_P(RGB2YUV422Test, AccuracyTest) +{ + // G-API code ////////////////////////////////////////////////////////////// + cv::GMat in; + auto out = cv::gapi::RGB2YUV422(in); + + cv::GComputation c(in, out); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); + // OpenCV code ///////////////////////////////////////////////////////////// + { + convertRGB2YUV422Ref(in_mat1, out_mat_ocv); + } + // Comparison ////////////////////////////////////////////////////////////// + { + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); } } diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests.hpp index 9f53d368570ce1..6cf4e5ed29ee32 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests.hpp @@ -61,7 +61,6 @@ struct g_api_ocv_pair_mat_mat { namespace { - //declare test cases for matrix and scalar operators g_api_ocv_pair_mat_scalar opPlus = {std::string{"operator+"}, [](cv::GMat in,cv::GScalar c){return in+c;}, @@ -184,9 +183,12 @@ g_api_ocv_pair_mat_mat opXor = {std::string{"operator^"}, [](const cv::Mat& in1, const cv::Mat& in2, cv::Mat& out){cv::bitwise_xor(in1, in2, out);}}; } // anonymous namespace -struct MathOperatorMatScalarTest : public TestParams>{}; -struct MathOperatorMatMatTest : public TestParams>{}; -struct NotOperatorTest : public TestParams> {}; + +GAPI_TEST_FIXTURE(MathOperatorMatScalarTest, initMatsRandU, + FIXTURE_API(CompareMats, g_api_ocv_pair_mat_scalar), 2, cmpF, op) +GAPI_TEST_FIXTURE(MathOperatorMatMatTest, initMatsRandU, + FIXTURE_API(CompareMats, g_api_ocv_pair_mat_mat), 2, cmpF, op) +GAPI_TEST_FIXTURE(NotOperatorTest, initMatrixRandU, <>, 0) } // opencv_test #endif // OPENCV_GAPI_OPERATOR_TESTS_COMMON_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests_inl.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests_inl.hpp index 7ec702ae9b7d43..44bcc9bc1b0554 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests_inl.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_operators_tests_inl.hpp @@ -14,15 +14,6 @@ namespace opencv_test { TEST_P(MathOperatorMatScalarTest, OperatorAccuracyTest ) { - compare_f cmpF; - g_api_ocv_pair_mat_scalar op; - int type = 0, dtype = 0; - cv::Size sz; - bool initOutMatr = false; - cv::GCompileArgs compile_args; - std::tie(cmpF, op, type, sz, dtype, initOutMatr, compile_args) = GetParam(); - initMatsRandU(type, sz, dtype, initOutMatr); - auto fun_gapi = op.g_api_function; auto fun_ocv = op.ocv_function ; @@ -33,7 +24,7 @@ TEST_P(MathOperatorMatScalarTest, OperatorAccuracyTest ) auto out = fun_gapi(in1, in2); cv::GComputation c(GIn(in1, in2), GOut(out)); - c.apply(gin(in_mat1, sc), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, sc), gout(out_mat_gapi), getCompileArgs()); fun_ocv(in_mat1, sc, out_mat_ocv); @@ -46,15 +37,6 @@ TEST_P(MathOperatorMatScalarTest, OperatorAccuracyTest ) TEST_P(MathOperatorMatMatTest, OperatorAccuracyTest ) { - compare_f cmpF; - g_api_ocv_pair_mat_mat op; - int type = 0, dtype = 0; - cv::Size sz; - bool initOutMatr = false; - cv::GCompileArgs compile_args; - std::tie(cmpF, op, type, sz, dtype, initOutMatr, compile_args) = GetParam(); - initMatsRandU(type, sz, dtype, initOutMatr); - auto fun_gapi = op.g_api_function; auto fun_ocv = op.ocv_function ; @@ -65,7 +47,7 @@ TEST_P(MathOperatorMatMatTest, OperatorAccuracyTest ) auto out = fun_gapi(in1, in2); cv::GComputation c(GIn(in1, in2), GOut(out)); - c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), std::move(compile_args)); + c.apply(gin(in_mat1, in_mat2), gout(out_mat_gapi), getCompileArgs()); fun_ocv(in_mat1, in_mat2, out_mat_ocv); @@ -78,16 +60,12 @@ TEST_P(MathOperatorMatMatTest, OperatorAccuracyTest ) TEST_P(NotOperatorTest, OperatorAccuracyTest) { - cv::Size sz_in = std::get<1>(GetParam()); - initMatrixRandU(std::get<0>(GetParam()), sz_in, std::get<0>(GetParam()), std::get<2>(GetParam())); - cv::GCompileArgs compile_args; - // G-API code ////////////////////////////////////////////////////////////// cv::GMat in; auto out = ~in; cv::GComputation c(in, out); - c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + c.apply(in_mat1, out_mat_gapi, getCompileArgs()); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -96,7 +74,7 @@ TEST_P(NotOperatorTest, OperatorAccuracyTest) // Comparison ////////////////////////////////////////////////////////////// { EXPECT_EQ(0, cv::countNonZero(out_mat_ocv != out_mat_gapi)); - EXPECT_EQ(out_mat_gapi.size(), sz_in); + EXPECT_EQ(out_mat_gapi.size(), sz); } } } // opencv_test diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_render_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_render_tests.cpp new file mode 100644 index 00000000000000..8e845e6f5ae794 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_render_tests.cpp @@ -0,0 +1,9 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#include "../test_precomp.hpp" +#include "gapi_render_tests_inl.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_render_tests.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_render_tests.hpp new file mode 100644 index 00000000000000..84df8c11f74a2c --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_render_tests.hpp @@ -0,0 +1,73 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_RENDER_TESTS_HPP +#define OPENCV_GAPI_RENDER_TESTS_HPP + +#include "gapi_tests_common.hpp" +#include "api/render_priv.hpp" + +namespace opencv_test +{ + +using Points = std::vector; +using Rects = std::vector; +using PairOfPoints = std::pair; +using VecOfPairOfPoints = std::vector; + +template +class RenderWithParam : public TestWithParam +{ +protected: + void Init() + { + MatType type = CV_8UC3; + out_mat_ocv = cv::Mat(sz, type, cv::Scalar(255)); + out_mat_gapi = cv::Mat(sz, type, cv::Scalar(255)); + + if (isNV12Format) { + /* NB: When converting data from BGR to NV12, data loss occurs, + * so the reference data is subjected to the same transformation + * for correct comparison of the test results */ + cv::gapi::wip::draw::BGR2NV12(out_mat_ocv, y, uv); + cv::cvtColorTwoPlane(y, uv, out_mat_ocv, cv::COLOR_YUV2BGR_NV12); + } + } + + void Run() + { + if (isNV12Format) { + cv::gapi::wip::draw::BGR2NV12(out_mat_gapi, y, uv); + cv::gapi::wip::draw::render(y, uv, prims); + cv::cvtColorTwoPlane(y, uv, out_mat_gapi, cv::COLOR_YUV2BGR_NV12); + + // NB: Also due to data loss + cv::gapi::wip::draw::BGR2NV12(out_mat_ocv, y, uv); + cv::cvtColorTwoPlane(y, uv, out_mat_ocv, cv::COLOR_YUV2BGR_NV12); + } else { + cv::gapi::wip::draw::render(out_mat_gapi, prims); + } + } + + cv::Size sz; + cv::Scalar color; + int thick; + int lt; + bool isNV12Format; + std::vector prims; + cv::Mat y, uv; + cv::Mat out_mat_ocv, out_mat_gapi; +}; + +struct RenderTextTest : public RenderWithParam > {}; +struct RenderRectTest : public RenderWithParam > {}; +struct RenderCircleTest : public RenderWithParam > {}; +struct RenderLineTest : public RenderWithParam > {}; + +} // opencv_test + +#endif //OPENCV_GAPI_RENDER_TESTS_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_render_tests_inl.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_render_tests_inl.hpp new file mode 100644 index 00000000000000..aded1ad4cb0aa8 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_render_tests_inl.hpp @@ -0,0 +1,96 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_RENDER_TESTS_INL_HPP +#define OPENCV_GAPI_RENDER_TESTS_INL_HPP + +#include "gapi_render_tests.hpp" + +#include + +namespace opencv_test +{ + +TEST_P(RenderTextTest, AccuracyTest) +{ + std::vector points; + std::string text; + int ff; + double fs; + bool blo; + + std::tie(sz, text, points, ff, fs, color, thick, lt, blo, isNV12Format) = GetParam(); + Init(); + + for (const auto& p : points) { + cv::putText(out_mat_ocv, text, p, ff, fs, color, thick, lt, blo); + prims.emplace_back(cv::gapi::wip::draw::Text{text, p, ff, fs, color, thick, lt, blo}); + } + + Run(); + + EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); +} + +TEST_P(RenderRectTest, AccuracyTest) +{ + std::vector rects; + int shift; + + std::tie(sz, rects, color, thick, lt, shift, isNV12Format) = GetParam(); + Init(); + + for (const auto& r : rects) { + cv::rectangle(out_mat_ocv, r, color, thick, lt, shift); + prims.emplace_back(cv::gapi::wip::draw::Rect{r, color, thick, lt, shift}); + } + + Run(); + + EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); +} + +TEST_P(RenderCircleTest, AccuracyTest) +{ + std::vector points; + int radius; + int shift; + + std::tie(sz, points, radius, color, thick, lt, shift, isNV12Format) = GetParam(); + Init(); + + for (const auto& p : points) { + cv::circle(out_mat_ocv, p, radius, color, thick, lt, shift); + prims.emplace_back(cv::gapi::wip::draw::Circle{p, radius, color, thick, lt, shift}); + } + + Run(); + + EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); +} + +TEST_P(RenderLineTest, AccuracyTest) +{ + std::vector> points; + int shift; + + std::tie(sz, points, color, thick, lt, shift, isNV12Format) = GetParam(); + Init(); + + for (const auto& p : points) { + cv::line(out_mat_ocv, p.first, p.second, color, thick, lt, shift); + prims.emplace_back(cv::gapi::wip::draw::Line{p.first, p.second, color, thick, lt, shift}); + } + + Run(); + + EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)); +} + +} // opencv_test + +#endif //OPENCV_GAPI_RENDER_TESTS_INL_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_tests_common.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_tests_common.hpp index 937587c0bd5ed5..3d3141feda7aba 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_tests_common.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_tests_common.hpp @@ -2,13 +2,20 @@ // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // -// Copyright (C) 2018 Intel Corporation +// Copyright (C) 2018-2019 Intel Corporation +#ifndef OPENCV_GAPI_TESTS_COMMON_HPP +#define OPENCV_GAPI_TESTS_COMMON_HPP #include +#include +#include -#include "opencv2/ts.hpp" -#include "opencv2/gapi.hpp" +#include +#include +#include + +#include "gapi_tests_helpers.hpp" namespace { @@ -41,6 +48,15 @@ class TestFunctional return cv::Scalar(s1, s2, s3, s4); } + void initOutMats(cv::Size sz_in, int dtype) + { + if (dtype != -1) + { + out_mat_gapi = cv::Mat(sz_in, dtype); + out_mat_ocv = cv::Mat(sz_in, dtype); + } + } + void initMatsRandU(int type, cv::Size sz_in, int dtype, bool createOutputMatrices = true) { in_mat1 = cv::Mat(sz_in, type); @@ -50,10 +66,9 @@ class TestFunctional cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255)); cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255)); - if (createOutputMatrices && dtype != -1) + if (createOutputMatrices) { - out_mat_gapi = cv::Mat (sz_in, dtype); - out_mat_ocv = cv::Mat (sz_in, dtype); + initOutMats(sz_in, dtype); } } @@ -62,28 +77,28 @@ class TestFunctional in_mat1 = cv::Mat(sz_in, type); sc = initScalarRandU(100); - cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255)); - if (createOutputMatrices && dtype != -1) + if (createOutputMatrices) { - out_mat_gapi = cv::Mat (sz_in, dtype); - out_mat_ocv = cv::Mat (sz_in, dtype); + initOutMats(sz_in, dtype); } } - void initMatsRandN(int type, cv::Size sz_in, int dtype, bool createOutputMatrices = true) + void initMatrixRandN(int type, cv::Size sz_in, int dtype, bool createOutputMatrices = true) { - in_mat1 = cv::Mat(sz_in, type); + in_mat1 = cv::Mat(sz_in, type); cv::randn(in_mat1, cv::Scalar::all(127), cv::Scalar::all(40.f)); - if (createOutputMatrices && dtype != -1) + if (createOutputMatrices) { - out_mat_gapi = cv::Mat(sz_in, dtype); - out_mat_ocv = cv::Mat(sz_in, dtype); + initOutMats(sz_in, dtype); } } + // empty function intended to show that nothing is to be initialized via TestFunctional methods + void initNothing(int, cv::Size, int, bool = true) {} + static cv::Mat nonZeroPixels(const cv::Mat& mat) { int channels = mat.channels(); @@ -117,6 +132,134 @@ using compare_f = std::function; using compare_scalar_f = std::function; +// FIXME: re-use MatType. current problem: "special values" interpreted incorrectly (-1 is printed +// as 16FC512) +struct MatType2 +{ +public: + MatType2(int val = 0) : _value(val) {} + operator int() const { return _value; } + friend std::ostream& operator<<(std::ostream& os, const MatType2& t) + { + switch (t) + { + case -1: return os << "SAME_TYPE"; + default: PrintTo(MatType(t), &os); return os; + } + } +private: + int _value; +}; + +// Universal parameter wrapper for common (pre-defined) and specific (user-defined) parameters +template +struct Params +{ + using gcomp_args_function_t = cv::GCompileArgs(*)(); + using common_params_t = std::tuple; + using specific_params_t = std::tuple; + using params_t = std::tuple; + static constexpr const size_t common_params_size = std::tuple_size::value; + static constexpr const size_t specific_params_size = std::tuple_size::value; + + template + static const typename std::tuple_element::type& + getCommon(const params_t& t) + { + static_assert(I < common_params_size, "Index out of range"); + return std::get(t); + } + + template + static const typename std::tuple_element::type& + getSpecific(const params_t& t) + { + static_assert(specific_params_size > 0, + "Impossible to call this function: no specific parameters specified"); + static_assert(I < specific_params_size, "Index out of range"); + return std::get(t); + } +}; + +// Base class for test fixtures +template +struct TestWithParamBase : TestFunctional, + TestWithParam::params_t> +{ + using AllParams = Params; + + MatType2 type = getCommonParam<0>(); + cv::Size sz = getCommonParam<1>(); + MatType2 dtype = getCommonParam<2>(); + + // Get common (pre-defined) parameter value by index + template + inline auto getCommonParam() const + -> decltype(AllParams::template getCommon(this->GetParam())) + { + return AllParams::template getCommon(this->GetParam()); + } + + // Get specific (user-defined) parameter value by index + template + inline auto getSpecificParam() const + -> decltype(AllParams::template getSpecific(this->GetParam())) + { + return AllParams::template getSpecific(this->GetParam()); + } + + // Return G-API compile arguments specified for test fixture + inline cv::GCompileArgs getCompileArgs() const + { + return getCommonParam<3>()(); + } +}; + +/** + * @private + * @brief Create G-API test fixture with TestWithParamBase base class + * @param Fixture test fixture name + * @param InitF callable that will initialize default available members (from TestFunctional) + * @param API base class API. Specifies types of user-defined parameters. If there are no such + * parameters, empty angle brackets ("<>") must be specified. + * @param Number number of user-defined parameters (corresponds to the number of types in API). + * if there are no such parameters, 0 must be specified. + * @param ... list of names of user-defined parameters. if there are no parameters, the list + * must be empty. + */ +#define GAPI_TEST_FIXTURE(Fixture, InitF, API, Number, ...) \ + struct Fixture : public TestWithParamBase API { \ + static_assert(Number == AllParams::specific_params_size, \ + "Number of user-defined parameters doesn't match size of __VA_ARGS__"); \ + __WRAP_VAARGS(DEFINE_SPECIFIC_PARAMS_##Number(__VA_ARGS__)) \ + Fixture() { InitF(type, sz, dtype); } \ + }; + +// Wrapper for test fixture API. Use to specify multiple types. +// Example: FIXTURE_API(int, bool) expands to +#define FIXTURE_API(...) <__VA_ARGS__> + +template +struct CompareF +{ + using callable_t = std::function; + CompareF(callable_t&& cmp, std::string&& cmp_name) : + _comparator(std::move(cmp)), _name(std::move(cmp_name)) {} + bool operator()(const T1& a, const T2& b) const + { + return _comparator(a, b); + } + friend std::ostream& operator<<(std::ostream& os, const CompareF& obj) + { + return os << obj._name; + } +private: + callable_t _comparator; + std::string _name; +}; + +using CompareMats = CompareF; +using CompareScalars = CompareF; template struct Wrappable @@ -129,6 +272,14 @@ struct Wrappable return t(a, b); }; } + + CompareMats to_compare_obj() + { + T t = *static_cast(this); + std::stringstream ss; + ss << t; + return CompareMats(to_compare_f(), ss.str()); + } }; template @@ -142,6 +293,14 @@ struct WrappableScalar return t(a, b); }; } + + CompareScalars to_compare_obj() + { + T t = *static_cast(this); + std::stringstream ss; + ss << t; + return CompareScalars(to_compare_f(), ss.str()); + } }; @@ -161,7 +320,10 @@ class AbsExact : public Wrappable return true; } } -private: + friend std::ostream& operator<<(std::ostream& os, const AbsExact&) + { + return os << "AbsExact()"; + } }; class AbsTolerance : public Wrappable @@ -181,6 +343,10 @@ class AbsTolerance : public Wrappable return true; } } + friend std::ostream& operator<<(std::ostream& os, const AbsTolerance& obj) + { + return os << "AbsTolerance(" << std::to_string(obj._tol) << ")"; + } private: double _tol; }; @@ -209,6 +375,10 @@ class Tolerance_FloatRel_IntAbs : public Wrappable } } } + friend std::ostream& operator<<(std::ostream& os, const Tolerance_FloatRel_IntAbs& obj) + { + return os << "Tolerance_FloatRel_IntAbs(" << obj._tol << ", " << obj._tol8u << ")"; + } private: double _tol; double _tol8u; @@ -238,6 +408,10 @@ class AbsSimilarPoints : public Wrappable return true; } } + friend std::ostream& operator<<(std::ostream& os, const AbsSimilarPoints& obj) + { + return os << "AbsSimilarPoints(" << obj._tol << ", " << obj._percent << ")"; + } private: double _tol; double _percent; @@ -270,6 +444,11 @@ class ToleranceFilter : public Wrappable } return true; } + friend std::ostream& operator<<(std::ostream& os, const ToleranceFilter& obj) + { + return os << "ToleranceFilter(" << obj._tol << ", " << obj._tol8u << ", " + << obj._inf_tol << ")"; + } private: double _tol; double _tol8u; @@ -298,6 +477,10 @@ class ToleranceColor : public Wrappable } return true; } + friend std::ostream& operator<<(std::ostream& os, const ToleranceColor& obj) + { + return os << "ToleranceColor(" << obj._tol << ", " << obj._inf_tol << ")"; + } private: double _tol; double _inf_tol; @@ -320,24 +503,66 @@ class AbsToleranceScalar : public WrappableScalar return true; } } + friend std::ostream& operator<<(std::ostream& os, const AbsToleranceScalar& obj) + { + return os << "AbsToleranceScalar(" << std::to_string(obj._tol) << ")"; + } private: double _tol; }; - } // namespace opencv_test namespace { - inline std::ostream& operator<<(std::ostream& os, const opencv_test::compare_f&) +inline std::ostream& operator<<(std::ostream& os, const opencv_test::compare_f&) +{ + return os << "compare_f"; +} + +inline std::ostream& operator<<(std::ostream& os, const opencv_test::compare_scalar_f&) +{ + return os << "compare_scalar_f"; +} +} // anonymous namespace + +// Note: namespace must match the namespace of the type of the printed object +namespace cv +{ +inline std::ostream& operator<<(std::ostream& os, CmpTypes op) +{ +#define CASE(v) case CmpTypes::v: os << #v; break + switch (op) { - return os << "compare_f"; + CASE(CMP_EQ); + CASE(CMP_GT); + CASE(CMP_GE); + CASE(CMP_LT); + CASE(CMP_LE); + CASE(CMP_NE); + default: GAPI_Assert(false && "unknown CmpTypes value"); } +#undef CASE + return os; } -namespace +inline std::ostream& operator<<(std::ostream& os, NormTypes op) { - inline std::ostream& operator<<(std::ostream& os, const opencv_test::compare_scalar_f&) +#define CASE(v) case NormTypes::v: os << #v; break + switch (op) { - return os << "compare_scalar_f"; + CASE(NORM_INF); + CASE(NORM_L1); + CASE(NORM_L2); + CASE(NORM_L2SQR); + CASE(NORM_HAMMING); + CASE(NORM_HAMMING2); + CASE(NORM_RELATIVE); + CASE(NORM_MINMAX); + default: GAPI_Assert(false && "unknown NormTypes value"); } +#undef CASE + return os; } +} // namespace cv + +#endif //OPENCV_GAPI_TESTS_COMMON_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_tests_helpers.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_tests_helpers.hpp new file mode 100644 index 00000000000000..db1083df0ce251 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/common/gapi_tests_helpers.hpp @@ -0,0 +1,67 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019 Intel Corporation + +#ifndef OPENCV_GAPI_TESTS_HELPERS_HPP +#define OPENCV_GAPI_TESTS_HELPERS_HPP + +#include +#include + +namespace opencv_test +{ + +// Ensure correct __VA_ARGS__ expansion on Windows +#define __WRAP_VAARGS(x) x + +#define __TUPLE_PARAM_TYPE(i) std::tuple_element::type + +// implementation of recursive in-class declaration and initialization of member variables +#define __DEFINE_PARAMS_IMPL1(index, param_name) \ + __TUPLE_PARAM_TYPE(index) param_name = getSpecificParam(); + +#define __DEFINE_PARAMS_IMPL2(index, param_name, ...) \ + __TUPLE_PARAM_TYPE(index) param_name = getSpecificParam(); \ + __WRAP_VAARGS(__DEFINE_PARAMS_IMPL1(index+1, __VA_ARGS__)) + +#define __DEFINE_PARAMS_IMPL3(index, param_name, ...) \ + __TUPLE_PARAM_TYPE(index) param_name = getSpecificParam(); \ + __WRAP_VAARGS(__DEFINE_PARAMS_IMPL2(index+1, __VA_ARGS__)) + +#define __DEFINE_PARAMS_IMPL4(index, param_name, ...) \ + __TUPLE_PARAM_TYPE(index) param_name = getSpecificParam(); \ + __WRAP_VAARGS(__DEFINE_PARAMS_IMPL3(index+1, __VA_ARGS__)) + +#define __DEFINE_PARAMS_IMPL5(index, param_name, ...) \ + __TUPLE_PARAM_TYPE(index) param_name = getSpecificParam(); \ + __WRAP_VAARGS(__DEFINE_PARAMS_IMPL4(index+1, __VA_ARGS__)) + +#define __DEFINE_PARAMS_IMPL6(index, param_name, ...) \ + __TUPLE_PARAM_TYPE(index) param_name = getSpecificParam(); \ + __WRAP_VAARGS(__DEFINE_PARAMS_IMPL5(index+1, __VA_ARGS__)) + +// user interface to define member variables of specified names +#define DEFINE_SPECIFIC_PARAMS_0() + +#define DEFINE_SPECIFIC_PARAMS_1(...) \ + __WRAP_VAARGS(__DEFINE_PARAMS_IMPL1(0, __VA_ARGS__)) + +#define DEFINE_SPECIFIC_PARAMS_2(...) \ + __WRAP_VAARGS(__DEFINE_PARAMS_IMPL2(0, __VA_ARGS__)) + +#define DEFINE_SPECIFIC_PARAMS_3(...) \ + __WRAP_VAARGS(__DEFINE_PARAMS_IMPL3(0, __VA_ARGS__)) + +#define DEFINE_SPECIFIC_PARAMS_4(...) \ + __WRAP_VAARGS(__DEFINE_PARAMS_IMPL4(0, __VA_ARGS__)) + +#define DEFINE_SPECIFIC_PARAMS_5(...) \ + __WRAP_VAARGS(__DEFINE_PARAMS_IMPL5(0, __VA_ARGS__)) + +#define DEFINE_SPECIFIC_PARAMS_6(...) \ + __WRAP_VAARGS(__DEFINE_PARAMS_IMPL6(0, __VA_ARGS__)) +} // namespace opencv_test + +#endif //OPENCV_GAPI_TESTS_HELPERS_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_core_tests_cpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_core_tests_cpu.cpp index 5414263fd6fd3d..665d525bc12de2 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_core_tests_cpu.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_core_tests_cpu.cpp @@ -2,75 +2,69 @@ // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // -// Copyright (C) 2018 Intel Corporation +// Copyright (C) 2018-2019 Intel Corporation #include "../test_precomp.hpp" #include "../common/gapi_core_tests.hpp" -#include "opencv2/gapi/cpu/core.hpp" +#include -#define CORE_CPU cv::gapi::core::cpu::kernels() +namespace +{ +#define CORE_CPU [] () { return cv::compile_args(cv::gapi::core::cpu::kernels()); } +} // anonymous namespace namespace opencv_test { - // FIXME: Wut? See MulTestCPU/MathOpTest below (duplicate?) INSTANTIATE_TEST_CASE_P(AddTestCPU, MathOpTest, - Combine(Values(ADD, MUL), - testing::Bool(), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values(1.0), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), Values( -1, CV_8U, CV_16U, CV_32F ), - /*init output matrices or not*/ testing::Bool(), - Values(false), - Values(cv::compile_args(CORE_CPU))), - opencv_test::PrintMathOpCoreParams()); + Values(CORE_CPU), + Values(ADD, MUL), + testing::Bool(), + Values(1.0), + Values(false))); INSTANTIATE_TEST_CASE_P(MulTestCPU, MathOpTest, - Combine(Values(MUL), - testing::Bool(), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values(1.0, 0.5, 2.0), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), Values( -1, CV_8U, CV_16U, CV_32F ), - /*init output matrices or not*/ testing::Bool(), - Values(false), - Values(cv::compile_args(CORE_CPU))), - opencv_test::PrintMathOpCoreParams()); + Values(CORE_CPU), + Values(MUL), + testing::Bool(), + Values(1.0, 0.5, 2.0), + Values(false))); INSTANTIATE_TEST_CASE_P(SubTestCPU, MathOpTest, - Combine(Values(SUB), - testing::Bool(), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values (1.0), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), Values( -1, CV_8U, CV_16U, CV_32F ), - /*init output matrices or not*/ testing::Bool(), + Values(CORE_CPU), + Values(SUB), testing::Bool(), - Values(cv::compile_args(CORE_CPU))), - opencv_test::PrintMathOpCoreParams()); + Values (1.0), + testing::Bool())); INSTANTIATE_TEST_CASE_P(DivTestCPU, MathOpTest, - Combine(Values(DIV), - testing::Bool(), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values (1.0, 0.5, 2.0), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), Values( -1, CV_8U, CV_16U, CV_32F ), - /*init output matrices or not*/ testing::Bool(), + Values(CORE_CPU), + Values(DIV), testing::Bool(), - Values(cv::compile_args(CORE_CPU))), - opencv_test::PrintMathOpCoreParams()); + Values (1.0, 0.5, 2.0), + testing::Bool())); INSTANTIATE_TEST_CASE_P(MulTestCPU, MulDoubleTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), @@ -78,8 +72,7 @@ INSTANTIATE_TEST_CASE_P(MulTestCPU, MulDoubleTest, cv::Size(640, 480), cv::Size(128, 128)), Values( -1, CV_8U, CV_16U, CV_32F ), - /*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(DivTestCPU, DivTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), @@ -87,8 +80,7 @@ INSTANTIATE_TEST_CASE_P(DivTestCPU, DivTest, cv::Size(640, 480), cv::Size(128, 128)), Values( -1, CV_8U, CV_16U, CV_32F ), - /*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(DivCTestCPU, DivCTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), @@ -96,132 +88,133 @@ INSTANTIATE_TEST_CASE_P(DivCTestCPU, DivCTest, cv::Size(640, 480), cv::Size(128, 128)), Values( -1, CV_8U, CV_16U, CV_32F ), - /*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(MeanTestCPU, MeanTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - /*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(MaskTestCPU, MaskTest, Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(SelectTestCPU, SelectTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(Polar2CartCPU, Polar2CartTest, - Combine(Values(cv::Size(1280, 720), + Combine(Values(CV_32FC1), + Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(CV_32FC1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(Cart2PolarCPU, Cart2PolarTest, - Combine(Values(cv::Size(1280, 720), + Combine(Values(CV_32FC1), + Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(CV_32FC1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(PhaseCPU, PhaseTest, Combine(Values(CV_32F, CV_32FC3), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU), + /* angle_in_degrees */ testing::Bool())); INSTANTIATE_TEST_CASE_P(SqrtCPU, SqrtTest, Combine(Values(CV_32F, CV_32FC3), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(CompareTestCPU, CmpTest, - Combine(Values(CMP_EQ, CMP_GE, CMP_NE, CMP_GT, CMP_LT, CMP_LE), - testing::Bool(), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU))), - opencv_test::PrintCmpCoreParams()); + Values(CV_8U), + Values(CORE_CPU), + Values(CMP_EQ, CMP_GE, CMP_NE, CMP_GT, CMP_LT, CMP_LE), + testing::Bool())); INSTANTIATE_TEST_CASE_P(BitwiseTestCPU, BitwiseTest, - Combine(Values(AND, OR, XOR), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU))), - opencv_test::PrintBWCoreParams()); + Values(-1), + Values(CORE_CPU), + Values(AND, OR, XOR))); INSTANTIATE_TEST_CASE_P(BitwiseNotTestCPU, NotTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - /*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(MinTestCPU, MinTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(MaxTestCPU, MaxTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(SumTestCPU, SumTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), + Values(-1), //Values(1e-5), - Values(AbsToleranceScalar(1e-5).to_compare_f()), - Values(cv::compile_args(CORE_CPU)))); + Values(CORE_CPU), + Values(AbsToleranceScalar(1e-5).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(AbsDiffTestCPU, AbsDiffTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(AbsDiffCTestCPU, AbsDiffCTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(AddWeightedTestCPU, AddWeightedTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), @@ -229,45 +222,45 @@ INSTANTIATE_TEST_CASE_P(AddWeightedTestCPU, AddWeightedTest, cv::Size(640, 480), cv::Size(128, 128)), Values( -1, CV_8U, CV_16U, CV_32F ), -/*init output matrices or not*/ testing::Bool(), - Values(Tolerance_FloatRel_IntAbs(1e-6, 1).to_compare_f()), - Values(cv::compile_args(CORE_CPU)))); + Values(CORE_CPU), + Values(Tolerance_FloatRel_IntAbs(1e-6, 1).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(NormTestCPU, NormTest, - Combine(Values(NORM_INF, NORM_L1, NORM_L2), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - //Values(1e-5), - Values(AbsToleranceScalar(1e-5).to_compare_f()), - Values(cv::compile_args(CORE_CPU))), - opencv_test::PrintNormCoreParams()); + Values(-1), + Values(CORE_CPU), + Values(AbsToleranceScalar(1e-5).to_compare_obj()), + Values(NORM_INF, NORM_L1, NORM_L2))); INSTANTIATE_TEST_CASE_P(IntegralTestCPU, IntegralTest, Combine(Values( CV_8UC1, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(ThresholdTestCPU, ThresholdTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::THRESH_BINARY, cv::THRESH_BINARY_INV, cv::THRESH_TRUNC, cv::THRESH_TOZERO, cv::THRESH_TOZERO_INV), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU), + Values(cv::THRESH_BINARY, cv::THRESH_BINARY_INV, cv::THRESH_TRUNC, + cv::THRESH_TOZERO, cv::THRESH_TOZERO_INV))); INSTANTIATE_TEST_CASE_P(ThresholdTestCPU, ThresholdOTTest, Combine(Values(CV_8UC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::THRESH_OTSU, cv::THRESH_TRIANGLE), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU), + Values(cv::THRESH_OTSU, cv::THRESH_TRIANGLE))); INSTANTIATE_TEST_CASE_P(InRangeTestCPU, InRangeTest, @@ -275,144 +268,192 @@ INSTANTIATE_TEST_CASE_P(InRangeTestCPU, InRangeTest, Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(Split3TestCPU, Split3Test, - Combine(Values(cv::Size(1280, 720), + Combine(Values(CV_8UC3), + Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_CPU)))); + Values(CV_8UC1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(Split4TestCPU, Split4Test, - Combine(Values(cv::Size(1280, 720), + Combine(Values(CV_8UC4), + Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_CPU)))); + Values(CV_8UC1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(ResizeTestCPU, ResizeTest, - Combine(Values(AbsSimilarPoints(2, 0.05).to_compare_f()), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Values(cv::Size(1280, 720), + cv::Size(640, 480), + cv::Size(128, 128)), + Values(-1), + Values(CORE_CPU), + Values(AbsSimilarPoints(2, 0.05).to_compare_obj()), Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA), + Values(cv::Size(64,64), + cv::Size(30,30)))); + +INSTANTIATE_TEST_CASE_P(ResizePTestCPU, ResizePTest, + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), + Values(-1), + Values(CORE_CPU), + Values(AbsSimilarPoints(2, 0.05).to_compare_obj()), + Values(cv::INTER_LINEAR), Values(cv::Size(64,64), - cv::Size(30,30)), - Values(cv::compile_args(CORE_CPU)))); + cv::Size(30,30)))); INSTANTIATE_TEST_CASE_P(ResizeTestCPU, ResizeTestFxFy, - Combine(Values(AbsSimilarPoints(2, 0.05).to_compare_f()), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), + Values(-1), + Values(CORE_CPU), + Values(AbsSimilarPoints(2, 0.05).to_compare_obj()), + Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA), Values(0.5, 0.1), - Values(0.5, 0.1), - Values(cv::compile_args(CORE_CPU)))); + Values(0.5, 0.1))); INSTANTIATE_TEST_CASE_P(Merge3TestCPU, Merge3Test, - Combine(Values(cv::Size(1280, 720), + Combine(Values(CV_8UC1), + Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_CPU)))); + Values(CV_8UC3), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(Merge4TestCPU, Merge4Test, - Combine(Values(cv::Size(1280, 720), + Combine(Values(CV_8UC1), + Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_CPU)))); + Values(CV_8UC4), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(RemapTestCPU, RemapTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(FlipTestCPU, FlipTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values(0,1,-1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU), + Values(0,1,-1))); INSTANTIATE_TEST_CASE_P(CropTestCPU, CropTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values(cv::Rect(10, 8, 20, 35), cv::Rect(4, 10, 37, 50)), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU), + Values(cv::Rect(10, 8, 20, 35), cv::Rect(4, 10, 37, 50)))); INSTANTIATE_TEST_CASE_P(LUTTestCPU, LUTTest, Combine(Values(CV_8UC1, CV_8UC3), - Values(CV_8UC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(CV_8UC1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(LUTTestCustomCPU, LUTTest, Combine(Values(CV_8UC3), - Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(CV_8UC3), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(ConvertToCPU, ConvertToTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values(CV_8U, CV_16U, CV_16S, CV_32F), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_CPU)))); + Values(CV_8U, CV_16U, CV_16S, CV_32F), + Values(CORE_CPU), + Values(AbsExact().to_compare_obj()), + Values(2.5, 1.0, -1.0), + Values(250.0, 0.0, -128.0))); INSTANTIATE_TEST_CASE_P(ConcatHorTestCPU, ConcatHorTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(ConcatVertTestCPU, ConcatVertTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(ConcatVertVecTestCPU, ConcatVertVecTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(ConcatHorVecTestCPU, ConcatHorVecTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); INSTANTIATE_TEST_CASE_P(NormalizeTestCPU, NormalizeTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), + Values(-1), + Values(CORE_CPU), + Values(AbsExact().to_compare_obj()), Values(0.0, 15.0), Values(1.0, 120.0, 255.0), Values(NORM_MINMAX, NORM_INF, NORM_L1, NORM_L2), - Values(-1, CV_8U, CV_16U, CV_16S, CV_32F), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1, CV_8U, CV_16U, CV_16S, CV_32F))); + +INSTANTIATE_TEST_CASE_P(BackendOutputAllocationTestCPU, BackendOutputAllocationTest, + Combine(Values(CV_8UC3, CV_16SC2, CV_32FC1), + Values(cv::Size(50, 50)), + Values(-1), + Values(CORE_CPU))); + +INSTANTIATE_TEST_CASE_P(BackendOutputAllocationLargeSizeWithCorrectSubmatrixTestCPU, + BackendOutputAllocationLargeSizeWithCorrectSubmatrixTest, + Combine(Values(CV_8UC3, CV_16SC2, CV_32FC1), + Values(cv::Size(50, 50)), + Values(-1), + Values(CORE_CPU))); + +INSTANTIATE_TEST_CASE_P(ReInitOutTestCPU, ReInitOutTest, + Combine(Values(CV_8UC3, CV_16SC4, CV_32FC1), + Values(cv::Size(640, 480)), + Values(-1), + Values(CORE_CPU), + Values(cv::Size(640, 400), + cv::Size(10, 480)))); } diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_core_tests_fluid.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_core_tests_fluid.cpp index ccf864660aef58..be158d029fa06c 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_core_tests_fluid.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_core_tests_fluid.cpp @@ -2,33 +2,33 @@ // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // -// Copyright (C) 2018 Intel Corporation +// Copyright (C) 2018-2019 Intel Corporation #include "../test_precomp.hpp" #include "../common/gapi_core_tests.hpp" -namespace opencv_test +namespace { +#define CORE_FLUID [] () { return cv::compile_args(cv::gapi::core::fluid::kernels()); } +} // anonymous namespace -#define CORE_FLUID cv::gapi::core::fluid::kernels() - +namespace opencv_test +{ // FIXME: Windows accuracy problems after recent update! INSTANTIATE_TEST_CASE_P(MathOpTestFluid, MathOpTest, - Combine(Values(ADD, SUB, DIV, MUL), - testing::Bool(), - Values(CV_8UC3, CV_8UC1, CV_16SC1, CV_32FC1), - Values(1.0), + Combine(Values(CV_8UC3, CV_8UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1920, 1080), cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), Values(-1, CV_8U, CV_32F), + Values(CORE_FLUID), + Values(ADD, SUB, DIV, MUL), testing::Bool(), - testing::Bool(), - Values(cv::compile_args(CORE_FLUID))), - opencv_test::PrintMathOpCoreParams()); + Values(1.0), + testing::Bool())); INSTANTIATE_TEST_CASE_P(MulSTestFluid, MulDoubleTest, Combine(Values(CV_8UC1, CV_16SC1, CV_32FC1), @@ -36,8 +36,7 @@ INSTANTIATE_TEST_CASE_P(MulSTestFluid, MulDoubleTest, cv::Size(640, 480), cv::Size(128, 128)), Values(-1), // FIXME: extend with more types - testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(CORE_FLUID))); INSTANTIATE_TEST_CASE_P(DivCTestFluid, DivCTest, Combine(Values(CV_8UC1, CV_16SC1, CV_32FC1), @@ -45,35 +44,33 @@ INSTANTIATE_TEST_CASE_P(DivCTestFluid, DivCTest, cv::Size(640, 480), cv::Size(128, 128)), Values(CV_8U, CV_32F), - testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(CORE_FLUID))); INSTANTIATE_TEST_CASE_P(AbsDiffTestFluid, AbsDiffTest, Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(-1), + Values(CORE_FLUID))); INSTANTIATE_TEST_CASE_P(AbsDiffCTestFluid, AbsDiffCTest, Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(-1), + Values(CORE_FLUID))); INSTANTIATE_TEST_CASE_P(BitwiseTestFluid, BitwiseTest, - Combine(Values(AND, OR, XOR), - Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_16SC1), + Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1920, 1080), cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - testing::Bool(), - Values(cv::compile_args(CORE_FLUID))), - opencv_test::PrintBWCoreParams()); + Values(-1), + Values(CORE_FLUID), + Values(AND, OR, XOR))); INSTANTIATE_TEST_CASE_P(BitwiseNotTestFluid, NotTest, Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_16SC1), @@ -81,8 +78,8 @@ INSTANTIATE_TEST_CASE_P(BitwiseNotTestFluid, NotTest, cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(-1), + Values(CORE_FLUID))); INSTANTIATE_TEST_CASE_P(MinTestFluid, MinTest, Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_16SC1, CV_32FC1), @@ -90,8 +87,8 @@ INSTANTIATE_TEST_CASE_P(MinTestFluid, MinTest, cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(-1), + Values(CORE_FLUID))); INSTANTIATE_TEST_CASE_P(MaxTestFluid, MaxTest, Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_16SC1, CV_32FC1), @@ -99,20 +96,19 @@ INSTANTIATE_TEST_CASE_P(MaxTestFluid, MaxTest, cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(-1), + Values(CORE_FLUID))); INSTANTIATE_TEST_CASE_P(CompareTestFluid, CmpTest, - Combine(Values(CMP_EQ, CMP_GE, CMP_NE, CMP_GT, CMP_LT, CMP_LE), - testing::Bool(), - Values(CV_8UC3, CV_8UC1, CV_16SC1, CV_32FC1), + Combine(Values(CV_8UC3, CV_8UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1920, 1080), cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - testing::Bool(), - Values(cv::compile_args(CORE_FLUID))), - opencv_test::PrintCmpCoreParams()); + Values(CV_8U), + Values(CORE_FLUID), + Values(CMP_EQ, CMP_GE, CMP_NE, CMP_GT, CMP_LT, CMP_LE), + testing::Bool())); INSTANTIATE_TEST_CASE_P(AddWeightedTestFluid, AddWeightedTest, Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), @@ -120,55 +116,63 @@ INSTANTIATE_TEST_CASE_P(AddWeightedTestFluid, AddWeightedTest, cv::Size(640, 480), cv::Size(128, 128)), Values(-1, CV_8U, CV_32F), - testing::Bool(), - Values(Tolerance_FloatRel_IntAbs(1e-5, 2).to_compare_f()), - //Values(0.5000005), - Values(cv::compile_args(CORE_FLUID)))); + Values(CORE_FLUID), + Values(Tolerance_FloatRel_IntAbs(1e-5, 2).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(LUTTestFluid, LUTTest, Combine(Values(CV_8UC1, CV_8UC3), - Values(CV_8UC1), Values(cv::Size(1920, 1080), cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(CV_8UC1), + Values(CORE_FLUID))); INSTANTIATE_TEST_CASE_P(ConvertToFluid, ConvertToTest, Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_32FC1), - Values(CV_8U, CV_16U, CV_32F), Values(cv::Size(1920, 1080), cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_FLUID)))); + Values(CV_8U, CV_16U, CV_32F), + Values(CORE_FLUID), + Values(Tolerance_FloatRel_IntAbs(1e-5, 2).to_compare_obj()), + Values(2.5, 1.0, -1.0), + Values(250.0, 0.0, -128.0))); INSTANTIATE_TEST_CASE_P(Split3TestFluid, Split3Test, - Combine(Values(cv::Size(1280, 720), + Combine(Values(CV_8UC3), + Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_FLUID)))); + Values(CV_8UC1), + Values(CORE_FLUID))); INSTANTIATE_TEST_CASE_P(Split4TestFluid, Split4Test, - Combine(Values(cv::Size(1280, 720), + Combine(Values(CV_8UC4), + Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_FLUID)))); + Values(CV_8UC1), + Values(CORE_FLUID))); INSTANTIATE_TEST_CASE_P(Merge3TestFluid, Merge3Test, - Combine(Values(cv::Size(1920, 1080), + Combine(Values(CV_8UC1), + Values(cv::Size(1920, 1080), cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_FLUID)))); + Values(CV_8UC3), + Values(CORE_FLUID))); INSTANTIATE_TEST_CASE_P(Merge4TestFluid, Merge4Test, - Combine(Values(cv::Size(1920, 1080), + Combine(Values(CV_8UC1), + Values(cv::Size(1920, 1080), cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_FLUID)))); + Values(CV_8UC4), + Values(CORE_FLUID))); INSTANTIATE_TEST_CASE_P(SelectTestFluid, SelectTest, Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_16SC1), @@ -176,37 +180,41 @@ INSTANTIATE_TEST_CASE_P(SelectTestFluid, SelectTest, cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(-1), + Values(CORE_FLUID))); INSTANTIATE_TEST_CASE_P(Polar2CartFluid, Polar2CartTest, - Combine(Values(cv::Size(1280, 720), + Combine(Values(CV_32FC1), + Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(CV_32FC1), + Values(CORE_FLUID))); INSTANTIATE_TEST_CASE_P(Cart2PolarFluid, Cart2PolarTest, - Combine(Values(cv::Size(1280, 720), + Combine(Values(CV_32FC1), + Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(CV_32FC1), + Values(CORE_FLUID))); INSTANTIATE_TEST_CASE_P(PhaseFluid, PhaseTest, Combine(Values(CV_32F, CV_32FC3), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(-1), + Values(CORE_FLUID), + /* angle_in_degrees */ testing::Bool())); INSTANTIATE_TEST_CASE_P(SqrtFluid, SqrtTest, Combine(Values(CV_32F, CV_32FC3), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_FLUID)))); + Values(-1), + Values(CORE_FLUID))); INSTANTIATE_TEST_CASE_P(ThresholdTestFluid, ThresholdTest, Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_16SC1), @@ -214,11 +222,11 @@ INSTANTIATE_TEST_CASE_P(ThresholdTestFluid, ThresholdTest, cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), + Values(-1), + Values(CORE_FLUID), Values(cv::THRESH_BINARY, cv::THRESH_BINARY_INV, cv::THRESH_TRUNC, - cv::THRESH_TOZERO, cv::THRESH_TOZERO_INV), - testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + cv::THRESH_TOZERO, cv::THRESH_TOZERO_INV))); INSTANTIATE_TEST_CASE_P(InRangeTestFluid, InRangeTest, Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_16SC1, CV_32FC1), @@ -226,25 +234,46 @@ INSTANTIATE_TEST_CASE_P(InRangeTestFluid, InRangeTest, cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(-1), + Values(CORE_FLUID))); -INSTANTIATE_TEST_CASE_P( - ResizeTestFluid, ResizeTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC3/*CV_8UC1, CV_16UC1, CV_16SC1*/), - Values(/*cv::INTER_NEAREST,*/ cv::INTER_LINEAR/*, cv::INTER_AREA*/), +INSTANTIATE_TEST_CASE_P(ResizeTestFluid, ResizeTest, + Combine(Values(CV_8UC3/*CV_8UC1, CV_16UC1, CV_16SC1*/), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128), cv::Size(64, 64), cv::Size(30, 30)), + Values(-1), + Values(CORE_FLUID), + Values(AbsExact().to_compare_obj()), + Values(/*cv::INTER_NEAREST,*/ cv::INTER_LINEAR/*, cv::INTER_AREA*/), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128), cv::Size(64, 64), - cv::Size(30, 30)), - Values(cv::compile_args(CORE_FLUID)))); + cv::Size(30, 30)))); + +INSTANTIATE_TEST_CASE_P(BackendOutputAllocationTestFluid, BackendOutputAllocationTest, + Combine(Values(CV_8UC3, CV_16SC2, CV_32FC1), + Values(cv::Size(50, 50)), + Values(-1), + Values(CORE_FLUID))); + +INSTANTIATE_TEST_CASE_P(BackendOutputAllocationLargeSizeWithCorrectSubmatrixTestFluid, + BackendOutputAllocationLargeSizeWithCorrectSubmatrixTest, + Combine(Values(CV_8UC3, CV_16SC2, CV_32FC1), + Values(cv::Size(50, 50)), + Values(-1), + Values(CORE_FLUID))); + +INSTANTIATE_TEST_CASE_P(ReInitOutTestFluid, ReInitOutTest, + Combine(Values(CV_8UC3, CV_16SC4, CV_32FC1), + Values(cv::Size(640, 480)), + Values(-1), + Values(CORE_FLUID), + Values(cv::Size(640, 400), + cv::Size(10, 480)))); //---------------------------------------------------------------------- // FIXME: Clean-up test configurations which are enabled already @@ -258,8 +287,7 @@ INSTANTIATE_TEST_CASE_P(MathOpTestCPU, MathOpTest, cv::Size(128, 128)), Values(-1, CV_8U, CV_32F), /*init output matrices or not*/ testing::Bool(), - Values(false)), - opencv_test::PrintMathOpCoreParams()); + Values(false))); INSTANTIATE_TEST_CASE_P(SubTestCPU, MathOpTest, Combine(Values(SUB), @@ -270,8 +298,7 @@ INSTANTIATE_TEST_CASE_P(SubTestCPU, MathOpTest, cv::Size(128, 128)), Values(-1, CV_8U, CV_32F), /*init output matrices or not*/ testing::Bool(), - testing::Bool()), - opencv_test::PrintMathOpCoreParams()); + testing::Bool())); INSTANTIATE_TEST_CASE_P(MulSTestCPU, MulSTest, Combine(Values(CV_8UC1, CV_16SC1, CV_32FC1), @@ -321,8 +348,7 @@ INSTANTIATE_TEST_CASE_P(CompareTestCPU, CmpTest, Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool()), - opencv_test::PrintCmpCoreParams()); +/*init output matrices or not*/ testing::Bool())); INSTANTIATE_TEST_CASE_P(BitwiseTestCPU, BitwiseTest, Combine(Values(AND, OR, XOR), @@ -330,8 +356,7 @@ INSTANTIATE_TEST_CASE_P(BitwiseTestCPU, BitwiseTest, Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool()), - opencv_test::PrintBWCoreParams()); +/*init output matrices or not*/ testing::Bool())); INSTANTIATE_TEST_CASE_P(BitwiseNotTestCPU, NotTest, Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), @@ -391,8 +416,7 @@ INSTANTIATE_TEST_CASE_P(NormTestCPU, NormTest, Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128))), - Values(0.0), - opencv_test::PrintNormCoreParams()); + Values(0.0)); INSTANTIATE_TEST_CASE_P(IntegralTestCPU, IntegralTest, Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), @@ -473,7 +497,7 @@ INSTANTIATE_TEST_CASE_P(LUTTestCPU, LUTTest, Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool())); +/*init output matrices or not*/ Values(true))); INSTANTIATE_TEST_CASE_P(LUTTestCustomCPU, LUTTest, Combine(Values(CV_8UC3), @@ -481,7 +505,7 @@ INSTANTIATE_TEST_CASE_P(LUTTestCustomCPU, LUTTest, Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool())); +/*init output matrices or not*/ Values(true))); INSTANTIATE_TEST_CASE_P(ConvertToCPU, ConvertToTest, Combine(Values(CV_8UC3, CV_8UC1, CV_16UC1, CV_32FC1), diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_imgproc_tests_cpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_imgproc_tests_cpu.cpp index 58b8bab8ac8ce8..77622b87cee546 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_imgproc_tests_cpu.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_imgproc_tests_cpu.cpp @@ -2,273 +2,322 @@ // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // -// Copyright (C) 2018 Intel Corporation +// Copyright (C) 2018-2019 Intel Corporation #include "../test_precomp.hpp" #include "../common/gapi_imgproc_tests.hpp" -#include "opencv2/gapi/cpu/imgproc.hpp" +#include -#define IMGPROC_CPU cv::gapi::imgproc::cpu::kernels() +namespace +{ +#define IMGPROC_CPU [] () { return cv::compile_args(cv::gapi::imgproc::cpu::kernels()); } +} // anonymous namespace namespace opencv_test { - INSTANTIATE_TEST_CASE_P(Filter2DTestCPU, Filter2DTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), - Values(3, 4, 5, 7), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), - cv::Size(640, 480), - cv::Size(128, 128)), - Values(cv::BORDER_DEFAULT), + cv::Size(640, 480), + cv::Size(128, 128)), Values(-1, CV_32F), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()), + Values(3, 4, 5, 7), + Values(cv::BORDER_DEFAULT))); INSTANTIATE_TEST_CASE_P(BoxFilterTestCPU, BoxFilterTest, - Combine(Values(AbsTolerance(0).to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), - Values(3,5), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), - Values(cv::BORDER_DEFAULT), Values(-1, CV_32F), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(IMGPROC_CPU), + Values(AbsTolerance(0).to_compare_obj()), + Values(3,5), + Values(cv::BORDER_DEFAULT))); INSTANTIATE_TEST_CASE_P(SepFilterTestCPU_8U, SepFilterTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3), - Values(3), + Combine(Values(CV_8UC1, CV_8UC3), Values(cv::Size(1280, 720), - cv::Size(640, 480)), + cv::Size(640, 480)), Values(-1, CV_16S, CV_32F), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()), + Values(3))); INSTANTIATE_TEST_CASE_P(SepFilterTestCPU_other, SepFilterTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_16UC1, CV_16SC1, CV_32FC1), - Values(3), + Combine(Values(CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), Values(-1, CV_32F), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()), + Values(3))); INSTANTIATE_TEST_CASE_P(BlurTestCPU, BlurTest, - Combine(Values(AbsTolerance(0.0).to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), - Values(3,5), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), - Values(cv::BORDER_DEFAULT), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(-1), + Values(IMGPROC_CPU), + Values(AbsTolerance(0.0).to_compare_obj()), + Values(3,5), + Values(cv::BORDER_DEFAULT))); INSTANTIATE_TEST_CASE_P(gaussBlurTestCPU, GaussianBlurTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), - Values(3, 5), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(-1), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()), + Values(3, 5))); INSTANTIATE_TEST_CASE_P(MedianBlurTestCPU, MedianBlurTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), - Values(3, 5), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(-1), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()), + Values(3, 5))); INSTANTIATE_TEST_CASE_P(ErodeTestCPU, ErodeTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), - Values(3, 5), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), + Values(-1), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()), + Values(3, 5), Values(cv::MorphShapes::MORPH_RECT, cv::MorphShapes::MORPH_CROSS, - cv::MorphShapes::MORPH_ELLIPSE), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + cv::MorphShapes::MORPH_ELLIPSE))); INSTANTIATE_TEST_CASE_P(Erode3x3TestCPU, Erode3x3Test, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(1,2,4), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(-1), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()), + Values(1,2,4))); INSTANTIATE_TEST_CASE_P(DilateTestCPU, DilateTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), - Values(3, 5), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), + Values(-1), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()), + Values(3, 5), Values(cv::MorphShapes::MORPH_RECT, cv::MorphShapes::MORPH_CROSS, - cv::MorphShapes::MORPH_ELLIPSE), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + cv::MorphShapes::MORPH_ELLIPSE))); INSTANTIATE_TEST_CASE_P(Dilate3x3TestCPU, Dilate3x3Test, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(1,2,4), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(-1), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()), + Values(1,2,4))); INSTANTIATE_TEST_CASE_P(SobelTestCPU, SobelTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), - Values(3, 5), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), Values(-1, CV_16S, CV_32F), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()), + Values(3, 5), Values(0, 1), - Values(1, 2), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(1, 2))); INSTANTIATE_TEST_CASE_P(SobelTestCPU32F, SobelTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_32FC1), - Values(3, 5), + Combine(Values(CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), Values(CV_32F), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()), + Values(3, 5), Values(0, 1), - Values(1, 2), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(1, 2))); INSTANTIATE_TEST_CASE_P(SobelXYTestCPU, SobelXYTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), - Values(3, 5), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), Values(-1, CV_16S, CV_32F), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()), + Values(3, 5), Values(1, 2), Values(BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT), - Values(0, 1, 255), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(0, 1, 255))); INSTANTIATE_TEST_CASE_P(SobelXYTestCPU32F, SobelXYTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_32FC1), - Values(3, 5), + Combine(Values(CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), Values(CV_32F), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()), + Values(3, 5), Values(1, 2), Values(BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT), - Values(0, 1, 255), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(0, 1, 255))); INSTANTIATE_TEST_CASE_P(EqHistTestCPU, EqHistTest, - Combine(Values(AbsExact().to_compare_f()), + Combine(Values(CV_8UC1), Values(cv::Size(1280, 720), - cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + cv::Size(640, 480)), + Values(CV_8UC1), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()))); INSTANTIATE_TEST_CASE_P(CannyTestCPU, CannyTest, - Combine(Values(AbsSimilarPoints(0, 0.05).to_compare_f()), - Values(CV_8UC1, CV_8UC3), + Combine(Values(CV_8UC1, CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), + Values(CV_8UC1), + Values(IMGPROC_CPU), + Values(AbsSimilarPoints(0, 0.05).to_compare_obj()), Values(3.0, 120.0), Values(125.0, 240.0), Values(3, 5), - testing::Bool(), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + testing::Bool())); INSTANTIATE_TEST_CASE_P(RGB2GrayTestCPU, RGB2GrayTest, - Combine(Values(AbsExact().to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), - cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + cv::Size(640, 480)), + Values(CV_8UC1), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()))); INSTANTIATE_TEST_CASE_P(BGR2GrayTestCPU, BGR2GrayTest, - Combine(Values(AbsExact().to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(CV_8UC1), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()))); INSTANTIATE_TEST_CASE_P(RGB2YUVTestCPU, RGB2YUVTest, - Combine(Values(AbsExact().to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(CV_8UC3), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()))); INSTANTIATE_TEST_CASE_P(YUV2RGBTestCPU, YUV2RGBTest, - Combine(Values(AbsExact().to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), - /*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(CV_8UC3), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()))); INSTANTIATE_TEST_CASE_P(NV12toRGBTestCPU, NV12toRGBTest, - Combine(Values(AbsExact().to_compare_f()), + Combine(Values(CV_8UC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(CV_8UC3), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()))); INSTANTIATE_TEST_CASE_P(NV12toBGRTestCPU, NV12toBGRTest, - Combine(Values(AbsExact().to_compare_f()), + Combine(Values(CV_8UC1), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), + Values(CV_8UC3), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()))); + +INSTANTIATE_TEST_CASE_P(NV12toRGBpTestCPU, NV12toRGBpTest, + Combine(Values(CV_8UC1), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), + Values(CV_8UC3), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()))); + +INSTANTIATE_TEST_CASE_P(NV12toBGRpTestCPU, NV12toBGRpTest, + Combine(Values(CV_8UC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(CV_8UC3), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()))); INSTANTIATE_TEST_CASE_P(RGB2LabTestCPU, RGB2LabTest, - Combine(Values(AbsExact().to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(CV_8UC3), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()))); INSTANTIATE_TEST_CASE_P(BGR2LUVTestCPU, BGR2LUVTest, - Combine(Values(AbsExact().to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(CV_8UC3), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()))); INSTANTIATE_TEST_CASE_P(LUV2BGRTestCPU, LUV2BGRTest, - Combine(Values(AbsExact().to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(CV_8UC3), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()))); INSTANTIATE_TEST_CASE_P(BGR2YUVTestCPU, BGR2YUVTest, - Combine(Values(AbsExact().to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(CV_8UC3), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()))); INSTANTIATE_TEST_CASE_P(YUV2BGRTestCPU, YUV2BGRTest, - Combine(Values(AbsExact().to_compare_f()), + Combine(Values(CV_8UC3), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), + Values(CV_8UC3), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()))); + +INSTANTIATE_TEST_CASE_P(RGB2HSVTestCPU, RGB2HSVTest, + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_CPU)))); + Values(CV_8UC3), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()))); +INSTANTIATE_TEST_CASE_P(BayerGR2RGBTestCPU, BayerGR2RGBTest, + Combine(Values(CV_8UC1), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), + Values(CV_8UC3), + Values(IMGPROC_CPU), + Values(AbsExact().to_compare_obj()))); + +INSTANTIATE_TEST_CASE_P(RGB2YUV422TestCPU, RGB2YUV422Test, + Combine(Values(CV_8UC3), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), + Values(CV_8UC2), + Values(IMGPROC_CPU), + Values(AbsTolerance(1).to_compare_obj()))); } // opencv_test diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_imgproc_tests_fluid.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_imgproc_tests_fluid.cpp index f053565111ad8d..99c36c4661a33d 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_imgproc_tests_fluid.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_imgproc_tests_fluid.cpp @@ -2,191 +2,219 @@ // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // -// Copyright (C) 2018 Intel Corporation +// Copyright (C) 2018-2019 Intel Corporation #include "../test_precomp.hpp" #include "../common/gapi_imgproc_tests.hpp" -#define IMGPROC_FLUID cv::gapi::imgproc::fluid::kernels() +namespace +{ +#define IMGPROC_FLUID [] () { return cv::compile_args(cv::gapi::imgproc::fluid::kernels()); } +} // anonymous namespace namespace opencv_test { INSTANTIATE_TEST_CASE_P(RGB2GrayTestFluid, RGB2GrayTest, - Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), - cv::Size(640, 480)), - Values(true, false), - Values(cv::compile_args(IMGPROC_FLUID)))); + cv::Size(640, 480)), + Values(CV_8UC1), + Values(IMGPROC_FLUID), + Values(ToleranceColor(1e-3).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(BGR2GrayTestFluid, BGR2GrayTest, - Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), - Values(true, false), - Values(cv::compile_args(IMGPROC_FLUID)))); + Values(CV_8UC1), + Values(IMGPROC_FLUID), + Values(ToleranceColor(1e-3).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(RGB2YUVTestFluid, RGB2YUVTest, - Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), - Values(true, false), - Values(cv::compile_args(IMGPROC_FLUID)))); + Values(CV_8UC3), + Values(IMGPROC_FLUID), + Values(ToleranceColor(1e-3).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(YUV2RGBTestFluid, YUV2RGBTest, - Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), - Values(true, false), - Values(cv::compile_args(IMGPROC_FLUID)))); + Values(CV_8UC3), + Values(IMGPROC_FLUID), + Values(ToleranceColor(1e-3).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(RGB2LabTestFluid, RGB2LabTest, - Combine(Values(AbsSimilarPoints(1, 0.05).to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), - Values(true, false), - Values(cv::compile_args(IMGPROC_FLUID)))); + Values(CV_8UC3), + Values(IMGPROC_FLUID), + Values(AbsSimilarPoints(1, 0.05).to_compare_obj()))); // FIXME: Not supported by Fluid yet (no kernel implemented) INSTANTIATE_TEST_CASE_P(BGR2LUVTestFluid, BGR2LUVTest, - Combine(Values(ToleranceColor(5e-3, 6).to_compare_f()), + Combine(Values(CV_8UC3), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), + Values(CV_8UC3), + Values(IMGPROC_FLUID), + Values(ToleranceColor(5e-3, 6).to_compare_obj()))); + +INSTANTIATE_TEST_CASE_P(RGB2HSVTestFluid, RGB2HSVTest, + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), - Values(true, false), - Values(cv::compile_args(IMGPROC_FLUID)))); + Values(CV_8UC3), + Values(IMGPROC_FLUID), + Values(ToleranceColor(1e-3).to_compare_obj()))); + +INSTANTIATE_TEST_CASE_P(BayerGR2RGBTestFluid, BayerGR2RGBTest, + Combine(Values(CV_8UC1), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), + Values(CV_8UC3), + Values(IMGPROC_FLUID), + Values(ToleranceColor(1e-3).to_compare_obj()))); + +INSTANTIATE_TEST_CASE_P(RGB2YUV422TestFluid, RGB2YUV422Test, + Combine(Values(CV_8UC3), + Values(cv::Size(1280, 720), + cv::Size(640, 480)), + Values(CV_8UC2), + Values(IMGPROC_FLUID), + Values(AbsTolerance(1).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(blurTestFluid, BlurTest, - Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), - Values(CV_8UC1, CV_16UC1, CV_16SC1), - Values(3), // add kernel size=5 when implementation is ready + Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), - Values(cv::BORDER_DEFAULT), - Values(true, false), - Values(cv::compile_args(IMGPROC_FLUID)))); + Values(-1), + Values(IMGPROC_FLUID), + Values(ToleranceFilter(1e-4f, 0.01).to_compare_obj()), + Values(3), // add kernel size=5 when implementation is ready + Values(cv::BORDER_DEFAULT))); INSTANTIATE_TEST_CASE_P(gaussBlurTestFluid, GaussianBlurTest, - Combine(Values(ToleranceFilter(1e-3f, 0.01).to_compare_f()), - Values(CV_8UC1, CV_16UC1, CV_16SC1), - Values(3), // add kernel size=5 when implementation is ready + Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), - Values(true, false), - Values(cv::compile_args(IMGPROC_FLUID)))); + Values(-1), + Values(IMGPROC_FLUID), + Values(ToleranceFilter(1e-3f, 0.01).to_compare_obj()), + Values(3))); // add kernel size=5 when implementation is ready INSTANTIATE_TEST_CASE_P(medianBlurTestFluid, MedianBlurTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_16UC1, CV_16SC1), - Values(3), // add kernel size=5 when implementation is ready + Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), - Values(true, false), - Values(cv::compile_args(IMGPROC_FLUID)))); + Values(-1), + Values(IMGPROC_FLUID), + Values(AbsExact().to_compare_obj()), + Values(3))); // add kernel size=5 when implementation is ready INSTANTIATE_TEST_CASE_P(erodeTestFluid, ErodeTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_16UC1, CV_16SC1), - Values(3), // add kernel size=5 when implementation is ready + Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), + Values(-1), + Values(IMGPROC_FLUID), + Values(AbsExact().to_compare_obj()), + Values(3), // add kernel size=5 when implementation is ready Values(cv::MorphShapes::MORPH_RECT, cv::MorphShapes::MORPH_CROSS, - cv::MorphShapes::MORPH_ELLIPSE), - Values(true, false), - Values(cv::compile_args(IMGPROC_FLUID)))); + cv::MorphShapes::MORPH_ELLIPSE))); INSTANTIATE_TEST_CASE_P(dilateTestFluid, DilateTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_16UC1, CV_16SC1), - Values(3), // add kernel size=5 when implementation is ready + Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), + Values(-1), + Values(IMGPROC_FLUID), + Values(AbsExact().to_compare_obj()), + Values(3), // add kernel size=5 when implementation is ready Values(cv::MorphShapes::MORPH_RECT, cv::MorphShapes::MORPH_CROSS, - cv::MorphShapes::MORPH_ELLIPSE), - Values(true, false), - Values(cv::compile_args(IMGPROC_FLUID)))); + cv::MorphShapes::MORPH_ELLIPSE))); INSTANTIATE_TEST_CASE_P(SobelTestFluid, SobelTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_16UC1, CV_16SC1), - Values(3), // add kernel size=5 when implementation is ready + Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), Values(-1, CV_16S, CV_32F), + Values(IMGPROC_FLUID), + Values(AbsExact().to_compare_obj()), + Values(3), // add kernel size=5 when implementation is ready Values(0, 1), - Values(1, 2), - Values(true, false), - Values(cv::compile_args(IMGPROC_FLUID)))); + Values(1, 2))); INSTANTIATE_TEST_CASE_P(SobelTestFluid32F, SobelTest, - Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), - Values(CV_32FC1), - Values(3), // add kernel size=5 when implementation is ready + Combine(Values(CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), Values(CV_32F), + Values(IMGPROC_FLUID), + Values(ToleranceFilter(1e-4f, 0.01).to_compare_obj()), + Values(3), // add kernel size=5 when implementation is ready Values(0, 1), - Values(1, 2), - Values(true, false), - Values(cv::compile_args(IMGPROC_FLUID)))); + Values(1, 2))); INSTANTIATE_TEST_CASE_P(SobelXYTestFluid, SobelXYTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), - Values(3), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), Values(-1, CV_16S, CV_32F), + Values(IMGPROC_FLUID), + Values(AbsExact().to_compare_obj()), + Values(3), Values(1, 2), Values(BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT_101), - Values(0, 1, 255), - Values(cv::compile_args(IMGPROC_FLUID)))); + Values(0, 1, 255))); INSTANTIATE_TEST_CASE_P(SobelXYTestFluid32F, SobelXYTest, - Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), - Values(CV_32FC1), - Values(3), + Combine(Values(CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), Values(CV_32F), + Values(IMGPROC_FLUID), + Values(ToleranceFilter(1e-4f, 0.01).to_compare_obj()), + Values(3), Values(1, 2), Values(BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT_101), - Values(0, 1, 255), - Values(cv::compile_args(IMGPROC_FLUID)))); + Values(0, 1, 255))); INSTANTIATE_TEST_CASE_P(boxFilterTestFluid32, BoxFilterTest, - Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), - Values(CV_8UC1, CV_16UC1, CV_16SC1), - Values(3), // add kernel size=5 when implementation is ready + Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), - Values(cv::BORDER_DEFAULT), Values(-1, CV_32F), - Values(true, false), - Values(cv::compile_args(IMGPROC_FLUID)))); + Values(IMGPROC_FLUID), + Values(ToleranceFilter(1e-4f, 0.01).to_compare_obj()), + Values(3), // add kernel size=5 when implementation is ready + Values(cv::BORDER_DEFAULT))); INSTANTIATE_TEST_CASE_P(sepFilterTestFluid, SepFilterTest, - Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), - Values(CV_32FC1), - Values(3), // add kernel size=5 when implementation is ready + Combine(Values(CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), Values(-1, CV_32F), - Values(true, false), - Values(cv::compile_args(IMGPROC_FLUID)))); + Values(IMGPROC_FLUID), + Values(ToleranceFilter(1e-4f, 0.01).to_compare_obj()), + Values(3))); // add kernel size=5 when implementation is ready INSTANTIATE_TEST_CASE_P(filter2DTestFluid, Filter2DTest, - Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), - Values(CV_8UC1, CV_16UC1, CV_16SC1), - Values(3), // add kernel size=4,5,7 when implementation ready + Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::BORDER_DEFAULT), Values(-1, CV_32F), - Values(true, false), - Values(cv::compile_args(IMGPROC_FLUID)))); + Values(IMGPROC_FLUID), + Values(ToleranceFilter(1e-4f, 0.01).to_compare_obj()), + Values(3), // add kernel size=4,5,7 when implementation ready + Values(cv::BORDER_DEFAULT))); } // opencv_test diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_operators_tests_cpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_operators_tests_cpu.cpp index 435c798c688fc9..14817557b49e98 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_operators_tests_cpu.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_operators_tests_cpu.cpp @@ -7,67 +7,65 @@ #include "../test_precomp.hpp" #include "../common/gapi_operators_tests.hpp" -#include "opencv2/gapi/cpu/core.hpp" +#include -#define CORE_CPU cv::gapi::core::cpu::kernels() +namespace +{ +#define CORE_CPU [] () { return cv::compile_args(cv::gapi::core::cpu::kernels()); } +} // anonymous namespace namespace opencv_test { - // FIXME: CPU test runs are disabled since Fluid is an exclusive plugin now! INSTANTIATE_TEST_CASE_P(MathOperatorTestCPU, MathOperatorMatMatTest, - Combine(Values(AbsExact().to_compare_f()), - Values( opPlusM, opMinusM, opDivM, - opGreater, opLess, opGreaterEq, opLessEq, opEq, opNotEq), - Values(CV_8UC1, CV_16SC1, CV_32FC1), + Combine(Values(CV_8UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), - cv::Size(640, 480), - cv::Size(128, 128)), + cv::Size(640, 480), + cv::Size(128, 128)), Values(-1, CV_8U, CV_32F), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(CORE_CPU), + Values(AbsExact().to_compare_obj()), + Values( opPlusM, opMinusM, opDivM, + opGreater, opLess, opGreaterEq, opLessEq, opEq, opNotEq))); INSTANTIATE_TEST_CASE_P(MathOperatorTestCPU, MathOperatorMatScalarTest, - Combine(Values(AbsExact().to_compare_f()), - Values( opPlus, opPlusR, opMinus, opMinusR, opMul, opMulR, // FIXIT avoid division by values near zero: opDiv, opDivR, - opGT, opLT, opGE, opLE, opEQ, opNE, - opGTR, opLTR, opGER, opLER, opEQR, opNER), - Values(CV_8UC1, CV_16SC1, CV_32FC1), + Combine(Values(CV_8UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), Values(-1, CV_8U, CV_32F), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(CORE_CPU), + Values(AbsExact().to_compare_obj()), + Values( opPlus, opPlusR, opMinus, opMinusR, opMul, opMulR, // FIXIT avoid division by values near zero: opDiv, opDivR, + opGT, opLT, opGE, opLE, opEQ, opNE, + opGTR, opLTR, opGER, opLER, opEQR, opNER))); INSTANTIATE_TEST_CASE_P(BitwiseOperatorTestCPU, MathOperatorMatMatTest, - Combine(Values(AbsExact().to_compare_f()), - Values( opAnd, opOr, opXor ), - Values(CV_8UC1, CV_16UC1, CV_16SC1), + Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), - cv::Size(640, 480), - cv::Size(128, 128)), + cv::Size(640, 480), + cv::Size(128, 128)), Values(-1), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(CORE_CPU), + Values(AbsExact().to_compare_obj()), + Values( opAnd, opOr, opXor ))); INSTANTIATE_TEST_CASE_P(BitwiseOperatorTestCPU, MathOperatorMatScalarTest, - Combine(Values(AbsExact().to_compare_f()), - Values( opAND, opOR, opXOR, opANDR, opORR, opXORR ), - Values(CV_8UC1, CV_16UC1, CV_16SC1), + Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), Values(-1), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(CORE_CPU), + Values(AbsExact().to_compare_obj()), + Values( opAND, opOR, opXOR, opANDR, opORR, opXORR ))); INSTANTIATE_TEST_CASE_P(BitwiseNotOperatorTestCPU, NotOperatorTest, Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_CPU)))); + Values(-1), + Values(CORE_CPU))); } diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_operators_tests_fluid.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_operators_tests_fluid.cpp index b3e54bb85a48cf..45c8e1889025c2 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_operators_tests_fluid.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_operators_tests_fluid.cpp @@ -8,65 +8,64 @@ #include "../test_precomp.hpp" #include "../common/gapi_operators_tests.hpp" -#define CORE_FLUID cv::gapi::core::fluid::kernels() +namespace +{ +#define CORE_FLUID [] () { return cv::compile_args(cv::gapi::core::fluid::kernels()); } +} // anonymous namespace namespace opencv_test { INSTANTIATE_TEST_CASE_P(MathOperatorTestFluid, MathOperatorMatMatTest, - Combine(Values(AbsExact().to_compare_f()), - Values( opPlusM, opMinusM, opDivM, - opGreater, opLess, opGreaterEq, opLessEq, opEq, opNotEq), - Values(CV_8UC1, CV_16SC1, CV_32FC1), + Combine(Values(CV_8UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), - cv::Size(640, 480), - cv::Size(128, 128)), + cv::Size(640, 480), + cv::Size(128, 128)), Values(-1, CV_8U, CV_32F), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(CORE_FLUID), + Values(AbsExact().to_compare_obj()), + Values( opPlusM, opMinusM, opDivM, + opGreater, opLess, opGreaterEq, opLessEq, opEq, opNotEq))); //FIXME: Some Mat/Scalar Fluid kernels are not there yet! INSTANTIATE_TEST_CASE_P(DISABLED_MathOperatorTestFluid, MathOperatorMatScalarTest, - Combine(Values(AbsExact().to_compare_f()), - Values( opPlus, opPlusR, opMinus, opMinusR, opMul, opMulR, // FIXIT avoid division by values near zero: opDiv, opDivR, - opGT, opLT, opGE, opLE, opEQ, opNE, - opGTR, opLTR, opGER, opLER, opEQR, opNER), - Values(CV_8UC1, CV_16SC1, CV_32FC1), + Combine(Values(CV_8UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), Values(-1, CV_8U, CV_32F), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(CORE_FLUID), + Values(AbsExact().to_compare_obj()), + Values( opPlus, opPlusR, opMinus, opMinusR, opMul, opMulR, // FIXIT avoid division by values near zero: opDiv, opDivR, + opGT, opLT, opGE, opLE, opEQ, opNE, + opGTR, opLTR, opGER, opLER, opEQR, opNER))); INSTANTIATE_TEST_CASE_P(BitwiseOperatorTestFluid, MathOperatorMatMatTest, - Combine(Values(AbsExact().to_compare_f()), - Values( opAnd, opOr, opXor ), - Values(CV_8UC1, CV_16UC1, CV_16SC1), + Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), - cv::Size(640, 480), - cv::Size(128, 128)), + cv::Size(640, 480), + cv::Size(128, 128)), Values(-1), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(CORE_FLUID), + Values(AbsExact().to_compare_obj()), + Values( opAnd, opOr, opXor ))); //FIXME: Some Mat/Scalar Fluid kernels are not there yet! INSTANTIATE_TEST_CASE_P(DISABLED_BitwiseOperatorTestFluid, MathOperatorMatScalarTest, - Combine(Values(AbsExact().to_compare_f()), - Values( opAND, opOR, opXOR, opANDR, opORR, opXORR ), - Values(CV_8UC1, CV_16UC1, CV_16SC1), + Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), Values(-1), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(CORE_FLUID), + Values(AbsExact().to_compare_obj()), + Values( opAND, opOR, opXOR, opANDR, opORR, opXORR ))); INSTANTIATE_TEST_CASE_P(BitwiseNotOperatorTestFluid, NotOperatorTest, Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_FLUID)))); + Values(-1), + Values(CORE_FLUID))); } diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_render_tests_cpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_render_tests_cpu.cpp new file mode 100644 index 00000000000000..334a9e5e1339cc --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/cpu/gapi_render_tests_cpu.cpp @@ -0,0 +1,66 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#include "../test_precomp.hpp" +#include "../common/gapi_render_tests.hpp" + +namespace opencv_test +{ + +INSTANTIATE_TEST_CASE_P(RenderTextTestCPU, RenderTextTest, + Combine(Values(cv::Size(1280, 720), + cv::Size(640, 480), + cv::Size(128, 128)), + Values("text"), + Values(Points{Point(5, 30), Point(40, 70), Point(-1, -1)}), +/* Font face */ Values(FONT_HERSHEY_SIMPLEX), +/* Font scale */ Values(2), +/* Color */ Values(cv::Scalar(255, 0, 0)), +/* Thickness */ Values(1), +/* Line type */ Values(LINE_8), +/* Bottom left origin */ testing::Bool(), +/* NV12 format or not */ testing::Bool())); + +INSTANTIATE_TEST_CASE_P(RenderRectTestCPU, RenderRectTest, + Combine(Values(cv::Size(1280, 720), + cv::Size(640, 480), + cv::Size(128, 128)), + Values(Rects{Rect(5, 30, 40, 50), + Rect(40, 70, 40, 50), +/* Edge case, rectangle will not be drawn */ Rect(75, 110, -40, 50), +/* Edge case, rectangle will not be drawn */ Rect(70, 100, 0, 50)}), +/* Color */ Values(cv::Scalar(255, 0, 0)), +/* Thickness */ Values(1), +/* Line type */ Values(LINE_8), +/* Shift */ Values(0), +/* NV12 format or not */ testing::Bool())); + +INSTANTIATE_TEST_CASE_P(RenderCircleTestCPU, RenderCircleTest, + Combine(Values(cv::Size(1280, 720), + cv::Size(640, 480), + cv::Size(128, 128)), + Values(Points{Point(5, 30), Point(40, 70), Point(75, 110)}), +/* Radius */ Values(5), +/* Color */ Values(cv::Scalar(255, 0, 0)), +/* Thickness */ Values(1), +/* Line type */ Values(LINE_8), +/* Shift */ Values(0), +/* NV12 format or not */ testing::Bool())); + +INSTANTIATE_TEST_CASE_P(RenderLineTestCPU, RenderLineTest, + Combine(Values(cv::Size(1280, 720), + cv::Size(640, 480), + cv::Size(128, 128)), + Values(VecOfPairOfPoints{ {Point(5, 30) , Point(5, 40) }, + {Point(40, 70) , Point(50, 70) }, + {Point(75, 110), Point(100, 115)} }), +/* Color */ Values(cv::Scalar(255, 0, 0)), +/* Thickness */ Values(1), +/* Line type */ Values(LINE_8), +/* Shift */ Values(0), +/* NV12 format or not */ testing::Bool())); +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_async_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_async_test.cpp index ebf7a7d3e21823..9702119b528d80 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_async_test.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_async_test.cpp @@ -6,8 +6,10 @@ #include "test_precomp.hpp" -#include "opencv2/gapi/gcomputation_async.hpp" -#include "opencv2/gapi/gcompiled_async.hpp" +#include +#include +#include + #include #include @@ -78,6 +80,32 @@ namespace { } } }; + + + //TODO: unify with callback helper code + struct cancel_struct { + std::atomic num_tasks_to_spawn; + + cv::gapi::wip::GAsyncContext ctx; + + cancel_struct(int tasks_to_spawn) : num_tasks_to_spawn(tasks_to_spawn) {} + }; + + G_TYPED_KERNEL(GCancelationAdHoc, , "org.opencv.test.cancel_ad_hoc") + { + static GMatDesc outMeta(GMatDesc in, cancel_struct* ) { return in; } + + }; + + GAPI_OCV_KERNEL(GCancelationAdHocImpl, GCancelationAdHoc) + { + static void run(const cv::Mat& , cancel_struct* cancel_struct_p, cv::Mat&) { + auto& cancel_struct_ = * cancel_struct_p; + auto num_tasks_to_spawn = -- cancel_struct_.num_tasks_to_spawn; + cancel_struct_.ctx.cancel(); + EXPECT_GT(num_tasks_to_spawn, 0)<<"Incorrect Test setup - to small number of tasks to feed the queue \n"; + } + }; } struct ExceptionOnExecution { @@ -117,6 +145,41 @@ struct ExceptionOnExecution { }; +struct SelfCanceling { + cv::GComputation self_cancel; + SelfCanceling(cancel_struct* cancel_struct_p) : self_cancel([cancel_struct_p]{ + cv::GMat in; + cv::GMat out = GCancelationAdHoc::on(in, cancel_struct_p); + return GComputation{in, out}; + }) + {} + + const cv::Size sz{2, 2}; + cv::Mat in_mat{sz, CV_8U, cv::Scalar(1)}; + cv::Mat out_mat; + + cv::GCompiled compile(){ + return self_cancel.compile(descr_of(in_mat), compile_args()); + } + + cv::GComputation& computation(){ + return self_cancel; + } + + cv::GRunArgs in_args(){ + return cv::gin(in_mat); + } + + cv::GRunArgsP out_args(){ + return cv::gout(out_mat); + } + + cv::GCompileArgs compile_args(){ + auto pkg = cv::gapi::kernels(); + return cv::compile_args(pkg); + } +}; + template struct crtp_cast { template @@ -150,6 +213,11 @@ struct CallBack: crtp_cast { this->crtp_cast_(this)->async(callback(), std::forward(args)...); } + template + void start_async(cv::gapi::wip::GAsyncContext& ctx, Args&&... args){ + this->crtp_cast_(this)->async(ctx, callback(), std::forward(args)...); + } + void wait_for_result() { std::unique_lock lck{mtx}; @@ -186,6 +254,14 @@ struct AsyncCompiled : crtp_cast{ auto gcmpld = this->crtp_cast_(this)->compile(); return cv::gapi::wip::async(gcmpld, std::forward(args)...); } + + template + auto async(cv::gapi::wip::GAsyncContext& ctx, Args&&... args) -> + decltype(cv::gapi::wip::async(std::declval(), std::forward(args)..., std::declval())) + { + auto gcmpld = this->crtp_cast_(this)->compile(); + return cv::gapi::wip::async(gcmpld, std::forward(args)..., ctx); + } }; //Test Mixin, hiding details of calling apply (async_apply) on GAPI Computation object @@ -193,9 +269,23 @@ template struct AsyncApply : crtp_cast { template - auto async(Args&&... args) ->decltype(cv::gapi::wip::async_apply(std::declval(), std::forward(args)...)) { - return cv::gapi::wip::async_apply(this->crtp_cast_(this)->computation(), std::forward(args)..., this->crtp_cast_(this)->compile_args()); + auto async(Args&&... args) -> + decltype(cv::gapi::wip::async_apply(std::declval(), std::forward(args)..., std::declval())) + { + return cv::gapi::wip::async_apply( + this->crtp_cast_(this)->computation(), std::forward(args)..., this->crtp_cast_(this)->compile_args() + ); + } + + template + auto async(cv::gapi::wip::GAsyncContext& ctx, Args&&... args) -> + decltype(cv::gapi::wip::async_apply(std::declval(), std::forward(args)... , std::declval(), std::declval())) + { + return cv::gapi::wip::async_apply( + this->crtp_cast_(this)->computation(), std::forward(args)..., this->crtp_cast_(this)->compile_args(), ctx + ); } + }; @@ -240,7 +330,7 @@ TYPED_TEST_P(stress, test){ const std::size_t number_of_threads = 4; auto thread_body = [&](){ - std::vector requests{request_per_thread}; + std::vector requests(request_per_thread); for (auto&& r : requests){ r.start_async(r.in_args(), r.out_args()); } @@ -262,13 +352,151 @@ TYPED_TEST_P(stress, test){ } REGISTER_TYPED_TEST_CASE_P(stress, test); +template +struct cancel : ::testing::Test{}; +TYPED_TEST_CASE_P(cancel); + +TYPED_TEST_P(cancel, basic){ + constexpr int num_tasks = 100; + cancel_struct cancel_struct_ {num_tasks}; + std::vector requests; requests.reserve(num_tasks); + + for (auto i = num_tasks; i>0; i--){ + requests.emplace_back(&cancel_struct_); + } + for (auto&& r : requests){ + //first request will cancel other on it's execution + r.start_async(cancel_struct_.ctx, r.in_args(), r.out_args()); + } + + unsigned int canceled = 0 ; + for (auto&& r : requests){ + try { + r.wait_for_result(); + }catch (cv::gapi::wip::GAsyncCanceled&){ + ++canceled; + } + } + ASSERT_GT(canceled, 0u); +} + +namespace { + GRunArgs deep_copy_out_args(const GRunArgsP& args ){ + GRunArgs result; result.reserve(args.size()); + for (auto&& arg : args){ + //FIXME: replace this switch with use of visit() on variant, when it will be available + switch (arg.index()){ + #if !defined(GAPI_STANDALONE) + case GRunArgP::index_of() : result.emplace_back(*util::get(arg)); break; + case GRunArgP::index_of() : result.emplace_back(*util::get(arg)); break; + case GRunArgP::index_of() : result.emplace_back(*util::get(arg)); break; + #endif // !defined(GAPI_STANDALONE) + case GRunArgP::index_of() : result.emplace_back(*util::get (arg)); break; + case GRunArgP::index_of() : result.emplace_back(*util::get(arg)); break; + case GRunArgP::index_of() : result.emplace_back(util::get (arg)); break; + default : ; + } + } + return result; + } + + GRunArgsP args_p_from_args(GRunArgs& args){ + GRunArgsP result; result.reserve(args.size()); + for (auto&& arg : args){ + switch (arg.index()){ + #if !defined(GAPI_STANDALONE) + case GRunArg::index_of() : result.emplace_back(&util::get(arg)); break; + case GRunArg::index_of() : result.emplace_back(&util::get(arg)); break; + case GRunArg::index_of() : result.emplace_back(&util::get(arg)); break; + #endif // !defined(GAPI_STANDALONE) + case GRunArg::index_of() : result.emplace_back(&util::get (arg)); break; + case GRunArg::index_of() : result.emplace_back(&util::get(arg)); break; + case GRunArg::index_of() : result.emplace_back(util::get (arg)); break; + default : ; + } + } + return result; + } +} + +REGISTER_TYPED_TEST_CASE_P(cancel, basic); + +template +struct output_args_lifetime : ::testing::Test{ + static constexpr const int num_of_requests = 20; +}; +TYPED_TEST_CASE_P(output_args_lifetime); +//There are intentionaly no actual checks (asserts and verify) in output_args_lifetime tests. +//They are more of example use-cases than real tests. (ASAN/valgrind can still catch issues here) +TYPED_TEST_P(output_args_lifetime, callback){ + + std::atomic active_requests = {0}; + + for (int i=0; inum_of_requests; i++) + { + TypeParam r; + + //As output arguments are __captured by reference__ calling code + //__must__ ensure they live long enough to complete asynchronous activity. + //(i.e. live at least until callback is called) + auto out_args_ptr = std::make_shared(deep_copy_out_args(r.out_args())); + + //Extend lifetime of out_args_ptr content by capturing it into a callback + auto cb = [&active_requests, out_args_ptr](std::exception_ptr ){ + --active_requests; + }; + + ++active_requests; + + r.async(cb, r.in_args(), args_p_from_args(*out_args_ptr)); + } + + + while(active_requests){ + std::this_thread::sleep_for(std::chrono::milliseconds{2}); + } +} + + +TYPED_TEST_P(output_args_lifetime, future){ + + std::vector> fs(this->num_of_requests); + std::vector> out_ptrs(this->num_of_requests); + + for (int i=0; inum_of_requests; i++) + { + TypeParam r; + + //As output arguments are __captured by reference__ calling code + //__must__ ensure they live long enough to complete asynchronous activity. + //(i.e. live at least until future.get()/wait() is returned) + auto out_args_ptr = std::make_shared(deep_copy_out_args(r.out_args())); + + //Extend lifetime of out_args_ptr content + out_ptrs[i] = out_args_ptr; + + fs[i] = r.async(r.in_args(), args_p_from_args(*out_args_ptr)); + } + + for (auto const& ftr : fs ){ + ftr.wait(); + } +} +REGISTER_TYPED_TEST_CASE_P(output_args_lifetime, callback, future); + //little helpers to match up all combinations of setups -template class callback_or_future_t, template class compiled_or_apply_t> +template class... args_t> struct Case : compute_fixture_t, - callback_or_future_t>, - compiled_or_apply_t > -{}; + args_t> ... +{ + template + Case(Args&&... args) : compute_fixture_t(std::forward(args)...) { } + Case(Case const & ) = default; + Case(Case && ) = default; + + Case() = default; +}; template using cases = ::testing::Types< @@ -277,23 +505,22 @@ using cases = ::testing::Types< Case, Case >; + INSTANTIATE_TYPED_TEST_CASE_P(AsyncAPINormalFlow_, normal, cases); INSTANTIATE_TYPED_TEST_CASE_P(AsyncAPIExceptionHandling_, exception, cases); INSTANTIATE_TYPED_TEST_CASE_P(AsyncAPIStress, stress, cases); -TEST(AsyncAPI, Sample){ - cv::GComputation self_mul([]{ - cv::GMat in; - cv::GMat out = cv::gapi::mul(in, in); - return GComputation{in, out}; - }); +INSTANTIATE_TYPED_TEST_CASE_P(AsyncAPICancelation, cancel, cases); - const cv::Size sz{2, 2}; - cv::Mat in_mat{sz, CV_8U, cv::Scalar(1)}; - cv::Mat out; +template +using explicit_wait_cases = ::testing::Types< + Case, + Case, + Case, + Case + >; + +INSTANTIATE_TYPED_TEST_CASE_P(AsyncAPIOutArgsLifetTime, output_args_lifetime, explicit_wait_cases); - auto f = cv::gapi::wip::async_apply(self_mul,cv::gin(in_mat), cv::gout(out)); - f.wait(); -} } // namespace opencv_test diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_basic_hetero_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_basic_hetero_tests.cpp index 62069d86506f33..4f0ac188124936 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_basic_hetero_tests.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_basic_hetero_tests.cpp @@ -8,7 +8,7 @@ #include "test_precomp.hpp" #include "gapi_mock_kernels.hpp" -#include "opencv2/gapi/fluid/gfluidkernel.hpp" +#include namespace opencv_test { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_desc_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_desc_tests.cpp index d0c551ad3e036a..fa79230ff8074a 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_desc_tests.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_desc_tests.cpp @@ -7,7 +7,7 @@ #include "test_precomp.hpp" -#include "opencv2/gapi/cpu/gcpukernel.hpp" +#include namespace opencv_test { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_parallel_rois_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_parallel_rois_test.cpp new file mode 100644 index 00000000000000..2275dba0da7adb --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_parallel_rois_test.cpp @@ -0,0 +1,315 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019 Intel Corporation + + +#include "test_precomp.hpp" + +#include "gapi_fluid_test_kernels.hpp" + +namespace opencv_test +{ + +namespace { + cv::Mat randomMat(cv::Size img_sz, int type = CV_8UC1, cv::Scalar mean = cv::Scalar(127.0f), cv::Scalar stddev = cv::Scalar(40.f)){ + cv::Mat mat(img_sz, type); + cv::randn(mat, mean, stddev); + return mat; + } + + cv::GFluidParallelOutputRois asGFluidParallelOutputRois(const std::vector& rois){ + cv::GFluidParallelOutputRois parallel_rois; + for (auto const& roi : rois) { + parallel_rois.parallel_rois.emplace_back(GFluidOutputRois{{to_own(roi)}}); + } + return parallel_rois; + } + + void adjust_empty_roi(cv::Rect& roi, cv::Size size){ + if (roi.empty()) roi = cv::Rect{{0,0}, size}; + } + + cv::GCompileArgs combine(cv::GCompileArgs&& lhs, cv::GCompileArgs const& rhs){ + lhs.insert(lhs.end(), rhs.begin(), rhs.end()); + return std::move(lhs); + } +} +using namespace cv::gapi_test_kernels; + +//As GTest can not simultaneously parameterize test with both types and values - lets use type-erasure and virtual interfaces +//to use different computation pipelines +struct ComputationPair { + void run_with_gapi(const cv::Mat& in_mat, cv::GCompileArgs const& compile_args, cv::Mat& out_mat){ + run_with_gapi_impl(in_mat, combine(cv::compile_args(fluidTestPackage), compile_args), out_mat); + } + void run_with_gapi(const cv::Mat& in_mat, cv::GFluidParallelOutputRois const& parallel_rois, cv::Mat& out_mat){ + run_with_gapi_impl(in_mat, cv::compile_args(fluidTestPackage, parallel_rois), out_mat); + } + + virtual void run_with_ocv (const cv::Mat& in_mat, const std::vector& rois, cv::Mat& out_mat) = 0; + + virtual std::string name() const { return {}; } + + virtual ~ComputationPair () = default; + + friend std::ostream& operator<<(std::ostream& o, ComputationPair const* cp){ + std::string custom_name = cp->name(); + return o << (custom_name.empty() ? typeid(cp).name() : custom_name ); + } + +private: + virtual void run_with_gapi_impl(const cv::Mat& in_mat, cv::GCompileArgs const& comp_args, cv::Mat& out_mat) = 0; +}; + +struct Blur3x3CP : ComputationPair{ + static constexpr int borderType = BORDER_REPLICATE; + static constexpr int kernelSize = 3; + + std::string name() const override { return "Blur3x3"; } + void run_with_gapi_impl(const cv::Mat& in_mat, cv::GCompileArgs const& comp_args, cv::Mat& out_mat_gapi) override { + cv::GMat in; + cv::GMat out = TBlur3x3::on(in, borderType, {}); + cv::GComputation c(cv::GIn(in), cv::GOut(out)); + + // Run G-API + auto cc = c.compile(cv::descr_of(in_mat), comp_args); + cc(cv::gin(in_mat), cv::gout(out_mat_gapi)); + } + + void run_with_ocv(const cv::Mat& in_mat, const std::vector& rois, cv::Mat& out_mat_ocv) override { + cv::Point anchor = {-1, -1}; + // Check with OpenCV + for (auto roi : rois) { + adjust_empty_roi(roi, in_mat.size()); + cv::blur(in_mat(roi), out_mat_ocv(roi), {kernelSize, kernelSize}, anchor, borderType); + } + } +}; + +struct AddCCP : ComputationPair{ + std::string name() const override { return "AddC"; } + void run_with_gapi_impl(const cv::Mat& in_mat, cv::GCompileArgs const& comp_args, cv::Mat& out_mat_gapi) override { + cv::GMat in; + cv::GMat out = TAddCSimple::on(in, 1); + cv::GComputation c(cv::GIn(in), cv::GOut(out)); + + // Run G-API + auto cc = c.compile(cv::descr_of(in_mat), comp_args); + cc(cv::gin(in_mat), cv::gout(out_mat_gapi)); + } + + void run_with_ocv(const cv::Mat& in_mat, const std::vector& rois, cv::Mat& out_mat_ocv) override { + // Check with OpenCV + for (auto roi : rois) { + adjust_empty_roi(roi, in_mat.size()); + out_mat_ocv(roi) = in_mat(roi) + 1u; + } + } +}; + +template +struct SequenceOfBlursCP : ComputationPair{ + BorderTypes borderType = _borderType; + + std::string name() const override { return "SequenceOfBlurs, border type: " + std::to_string(static_cast(borderType)); } + void run_with_gapi_impl(const cv::Mat& in_mat, cv::GCompileArgs const& comp_args, cv::Mat& out_mat) override { + cv::Scalar borderValue(0); + + GMat in; + auto mid = TBlur3x3::on(in, borderType, borderValue); + auto out = TBlur5x5::on(mid, borderType, borderValue); + + GComputation c(GIn(in), GOut(out)); + auto cc = c.compile(descr_of(in_mat), comp_args); + cc(cv::gin(in_mat), cv::gout(out_mat)); + } + void run_with_ocv(const cv::Mat& in_mat, const std::vector& rois, cv::Mat& out_mat) override { + cv::Mat mid_mat_ocv = Mat::zeros(in_mat.size(), in_mat.type()); + cv::Point anchor = {-1, -1}; + + for (auto roi : rois) { + adjust_empty_roi(roi, in_mat.size()); + cv::blur(in_mat, mid_mat_ocv, {3,3}, anchor, borderType); + cv::blur(mid_mat_ocv(roi), out_mat(roi), {5,5}, anchor, borderType); + } + } +}; + +struct TiledComputation : public TestWithParam , decltype(cv::GFluidParallelFor::parallel_for)>> {}; +TEST_P(TiledComputation, Test) +{ + ComputationPair* cp; + cv::Size img_sz; + std::vector rois ; + decltype(cv::GFluidParallelFor::parallel_for) pfor; + auto mat_type = CV_8UC1; + + std::tie(cp, img_sz, rois, pfor) = GetParam(); + + cv::Mat in_mat = randomMat(img_sz, mat_type); + cv::Mat out_mat_gapi = cv::Mat::zeros(img_sz, mat_type); + cv::Mat out_mat_ocv = cv::Mat::zeros(img_sz, mat_type); + + auto comp_args = combine(cv::compile_args(asGFluidParallelOutputRois(rois)), pfor ? cv::compile_args(cv::GFluidParallelFor{pfor}) : cv::GCompileArgs{}); + cp->run_with_gapi(in_mat, comp_args, out_mat_gapi); + cp->run_with_ocv (in_mat, rois, out_mat_ocv); + + EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv)) + << "in_mat : \n" << in_mat << std::endl + << "diff matrix :\n " << (out_mat_gapi != out_mat_ocv) << std::endl + << "out_mat_gapi: \n" << out_mat_gapi << std::endl + << "out_mat_ocv: \n" << out_mat_ocv << std::endl;; +} + + +namespace { + //this is ugly but other variants (like using shared_ptr) are IMHO even more ugly :) + template + T* addr_of_static(Arg... arg) { + static T obj(std::forward(arg)...); + return &obj; + } +} + +auto single_arg_computations = [](){ + return Values( addr_of_static(), + addr_of_static(), + addr_of_static>(), + addr_of_static>(), + addr_of_static>() + ); + +}; + +auto tilesets_8x10 = [](){ + return Values(std::vector{cv::Rect{}}, + std::vector{cv::Rect{0,0,8,5}, cv::Rect{0,5,8,5}}, + std::vector{cv::Rect{0,1,8,3}, cv::Rect{0,4,8,3}}, + std::vector{cv::Rect{0,2,8,3}, cv::Rect{0,5,8,2}}, + std::vector{cv::Rect{0,3,8,4}, cv::Rect{0,9,8,1}}); +}; + +auto tilesets_20x15 = [](){ + return Values(std::vector{cv::Rect{}}, + std::vector{cv::Rect{{0,0},cv::Size{20,7}}, + cv::Rect{{0,7},cv::Size{20,8}}}); +}; + +auto tilesets_320x240 = [](){ + return Values(std::vector{cv::Rect{{0,0}, cv::Size{320,120}}, + cv::Rect{{0,120}, cv::Size{320,120}}}, + + std::vector{cv::Rect{{0,0}, cv::Size{320,120}}, + cv::Rect{{0,120}, cv::Size{320,120}}}, + + std::vector{cv::Rect{{0,0}, cv::Size{320,60}}, + cv::Rect{{0,60}, cv::Size{320,60}}, + cv::Rect{{0,120},cv::Size{320,120}}}); +}; + +namespace{ + auto no_custom_pfor = decltype(cv::GFluidParallelFor::parallel_for){}; +} + +INSTANTIATE_TEST_CASE_P(FluidTiledSerial8x10, TiledComputation, + Combine( + single_arg_computations(), + Values(cv::Size(8, 10)), + tilesets_8x10(), + Values(no_custom_pfor)) +); + +INSTANTIATE_TEST_CASE_P(FluidTiledSerial20x15, TiledComputation, + Combine( + single_arg_computations(), + Values(cv::Size(20, 15)), + tilesets_20x15(), + Values(no_custom_pfor)) +); + +INSTANTIATE_TEST_CASE_P(FluidTiledSerial320x240, TiledComputation, + Combine( + single_arg_computations(), + Values(cv::Size(320, 240)), + tilesets_320x240(), + Values(no_custom_pfor)) +); + +//FIXME: add multiple outputs tests + +TEST(FluidTiledParallelFor, basic) +{ + cv::Size img_sz{8,20}; + auto mat_type = CV_8UC1; + + cv::GMat in; + cv::GMat out = TAddCSimple::on(in, 1); + cv::GComputation c(cv::GIn(in), cv::GOut(out)); + + cv::Mat in_mat = randomMat(img_sz, mat_type); + cv::Mat out_mat_gapi = cv::Mat::zeros(img_sz, mat_type); + + auto parallel_rois = asGFluidParallelOutputRois( std::vector{cv::Rect{0,0,8,5}, cv::Rect{0,5,8,5}}); + + std::size_t items_count = 0; + auto pfor = [&items_count](std::size_t count, std::function ){ + items_count = count; + }; + + // Run G-API + auto cc = c.compile(cv::descr_of(in_mat), cv::compile_args(fluidTestPackage, parallel_rois, GFluidParallelFor{pfor})); + cc(cv::gin(in_mat), cv::gout(out_mat_gapi)); + ASSERT_EQ(parallel_rois.parallel_rois.size(), items_count); +} + +namespace { + auto serial_for = [](std::size_t count, std::function f){ + for (std::size_t i = 0; i < count; ++i){ + f(i); + } + }; + + auto cv_parallel_for = [](std::size_t count, std::function f){ + cv::parallel_for_(cv::Range(0, static_cast(count)), [f](const cv::Range& r){ + for (auto i = r.start; i < r.end; ++i){ + f(i); + } }); + }; +} + +INSTANTIATE_TEST_CASE_P(FluidTiledParallel8x10, TiledComputation, + Combine( + single_arg_computations(), + Values(cv::Size(8, 10)), + tilesets_8x10(), + Values(serial_for, cv_parallel_for)) +); +} // namespace opencv_test + +//define custom printer for "parallel_for" test parameter +namespace std { + void PrintTo(decltype(cv::GFluidParallelFor::parallel_for) const& f, std::ostream* o); +} + +//separate declaration and definition are needed to please the compiler +void std::PrintTo(decltype(cv::GFluidParallelFor::parallel_for) const& f, std::ostream* o){ + if (f) { + using namespace opencv_test; + if (f.target()){ + *o <<"serial_for"; + } + else if (f.target()){ + *o <<"cv_parallel_for"; + } + else { + *o <<"parallel_for of type: " << f.target_type().name(); + } + } + else + { + *o << "default parallel_for"; + } + +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_resize_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_resize_test.cpp index cbe3237d337676..2798b85b95902f 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_resize_test.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_resize_test.cpp @@ -381,7 +381,7 @@ static auto fluidResizeTestPackage = [](int interpolation, cv::Size szIn, cv::Si }break; default: CV_Assert(false); } - return combine(pkg, fluidTestPackage, unite_policy::KEEP); + return combine(pkg, fluidTestPackage); #undef RESIZE_SWITCH #undef RESIZE_CASE @@ -743,7 +743,7 @@ TEST_P(NV12PlusResizeTest, Test) auto out = cv::gapi::resize(rgb, out_sz, 0, 0, interp); cv::GComputation c(cv::GIn(y, uv), cv::GOut(out)); - auto pkg = cv::gapi::combine(fluidTestPackage, cv::gapi::core::fluid::kernels(), cv::unite_policy::KEEP); + auto pkg = cv::gapi::combine(fluidTestPackage, cv::gapi::core::fluid::kernels()); c.apply(cv::gin(y_mat, uv_mat), cv::gout(out_mat) ,cv::compile_args(pkg, cv::GFluidOutputRois{{to_own(roi)}})); @@ -822,8 +822,7 @@ TEST_P(Preproc4lpiTest, Test) cv::GComputation c(cv::GIn(y, uv), cv::GOut(out)); auto pkg = cv::gapi::combine(cv::gapi::core::fluid::kernels(), - fluidResizeTestPackage(interp, in_sz, out_sz, 4), - cv::unite_policy::REPLACE); + fluidResizeTestPackage(interp, in_sz, out_sz, 4)); c.apply(cv::gin(y_mat, uv_mat), cv::gout(out_mat) ,cv::compile_args(pkg, cv::GFluidOutputRois{{to_own(roi)}})); diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test.cpp index 131f96aaa0b938..b919d991147ce9 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test.cpp @@ -7,10 +7,10 @@ #include "test_precomp.hpp" -#include "opencv2/gapi/core.hpp" +#include -#include "opencv2/gapi/fluid/gfluidbuffer.hpp" -#include "opencv2/gapi/fluid/gfluidkernel.hpp" +#include +#include // FIXME: move these tests with priv() to internal suite #include "backends/fluid/gfluidbuffer_priv.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test_kernels.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test_kernels.cpp index fcc8d9b058b337..7c4904cc5ebc20 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test_kernels.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test_kernels.cpp @@ -9,6 +9,7 @@ #include #include "gapi_fluid_test_kernels.hpp" #include +#include namespace cv { @@ -72,7 +73,8 @@ GAPI_FLUID_KERNEL(FAddCSimple, TAddCSimple, false) for (int i = 0, w = in.length(); i < w; i++) { //std::cout << std::setw(4) << int(in_row[i]); - out_row[i] = static_cast(in_row[i] + cval); + //FIXME: it seems that over kernels might need it as well + out_row[i] = cv::gapi::own::saturate(in_row[i] + cval); } //std::cout << std::endl; } diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test_kernels.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test_kernels.hpp index 567dddd5d379e3..dfb88225bb9a27 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test_kernels.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_fluid_test_kernels.hpp @@ -8,7 +8,7 @@ #ifndef GAPI_FLUID_TEST_KERNELS_HPP #define GAPI_FLUID_TEST_KERNELS_HPP -#include "opencv2/gapi/fluid/gfluidkernel.hpp" +#include namespace cv { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_gcomputation_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_gcomputation_tests.cpp index 070cea6927b9fb..0e38e05705d3f1 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_gcomputation_tests.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_gcomputation_tests.cpp @@ -6,7 +6,8 @@ #include "test_precomp.hpp" -#include "opencv2/gapi/cpu/gcpukernel.hpp" +#include +#include namespace opencv_test { @@ -51,6 +52,41 @@ namespace opencv_test { } }; + + struct GComputationVectorMatsAsOutput: public ::testing::Test + { + cv::Mat in_mat; + cv::GComputation m_c; + std::vector ref_mats; + + GComputationVectorMatsAsOutput() : in_mat(300, 300, CV_8UC3), + m_c([&](){ + cv::GMat in; + cv::GMat out[3]; + std::tie(out[0], out[1], out[2]) = cv::gapi::split3(in); + return cv::GComputation({in}, {out[0], out[1], out[2]}); + }) + { + cv::randu(in_mat, cv::Scalar::all(0), cv::Scalar::all(255)); + cv::split(in_mat, ref_mats); + } + + void run(std::vector& out_mats) + { + m_c.apply({in_mat}, out_mats); + } + + void check(const std::vector& out_mats) + { + for (const auto& it : ade::util::zip(ref_mats, out_mats)) + { + const auto& ref_mat = std::get<0>(it); + const auto& out_mat = std::get<1>(it); + + EXPECT_EQ(0, cv::countNonZero(ref_mat != out_mat)); + } + } + }; } TEST_F(GComputationApplyTest, ThrowDontPassCustomKernel) @@ -65,4 +101,37 @@ namespace opencv_test ASSERT_NO_THROW(m_c.apply(in_mat, out_mat, cv::compile_args(pkg))); } + TEST_F(GComputationVectorMatsAsOutput, OutputAllocated) + { + std::vector out_mats(3); + for (auto& out_mat : out_mats) + { + out_mat.create(in_mat.size(), CV_8UC1); + } + + run(out_mats); + check(out_mats); + } + + TEST_F(GComputationVectorMatsAsOutput, OutputNotAllocated) + { + std::vector out_mats(3); + + run(out_mats); + check(out_mats); + } + + TEST_F(GComputationVectorMatsAsOutput, OutputAllocatedWithInvalidMeta) + { + std::vector out_mats(3); + + for (auto& out_mat : out_mats) + { + out_mat.create(in_mat.size() / 2, CV_8UC1); + } + + run(out_mats); + check(out_mats); + } + } // namespace opencv_test diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_gpu_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_gpu_test.cpp index 7cb6f9f66d2b10..6c4e10a1399a25 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_gpu_test.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_gpu_test.cpp @@ -10,7 +10,7 @@ #include "logger.hpp" #include "common/gapi_tests_common.hpp" -#include "opencv2/gapi/gpu/ggpukernel.hpp" +#include #include "opencl_kernels_test_gapi.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_kernel_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_kernel_tests.cpp index aeb47628e01ee8..7a33b0de9e9bc0 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_kernel_tests.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_kernel_tests.cpp @@ -6,28 +6,129 @@ #include "test_precomp.hpp" -#include "opencv2/gapi/cpu/gcpukernel.hpp" +#include #include "gapi_mock_kernels.hpp" +#include // cpu::backend +#include // fluid::backend + namespace opencv_test { namespace { - G_TYPED_KERNEL(GClone, , "org.opencv.test.clone") + namespace I { - static GMatDesc outMeta(GMatDesc in) { return in; } + G_TYPED_KERNEL(GClone, , "org.opencv.test.clone") + { + static GMatDesc outMeta(GMatDesc in) { return in; } + }; + } + enum class KernelTags + { + CPU_CUSTOM_BGR2GRAY, + CPU_CUSTOM_CLONE, + CPU_CUSTOM_ADD, + FLUID_CUSTOM_BGR2GRAY, + FLUID_CUSTOM_CLONE, + FLUID_CUSTOM_ADD }; - GAPI_OCV_KERNEL(GCloneImpl, GClone) + class HeteroGraph: public ::testing::Test { - static void run(const cv::Mat& in, cv::Mat &out) + public: + HeteroGraph() { - out = in.clone(); + auto tmp = I::GClone::on(cv::gapi::add(in[0], in[1])); + out = cv::gapi::imgproc::GBGR2Gray::on(tmp); + } + + static void registerCallKernel(KernelTags kernel_tag) { + kernel_calls.insert(kernel_tag); + } + + bool checkCallKernel(KernelTags kernel_tag) { + return ade::util::contains(kernel_calls, kernel_tag); } + + protected: + void SetUp() override + { + if (!kernel_calls.empty()) + cv::util::throw_error(std::logic_error("Kernel call log has not been cleared!!!")); + } + + void TearDown() override + { + kernel_calls.clear(); + } + + protected: + cv::GMat in[2], out; + static std::set kernel_calls; }; -} + + namespace cpu + { + GAPI_OCV_KERNEL(GClone, I::GClone) + { + static void run(const cv::Mat&, cv::Mat) + { + HeteroGraph::registerCallKernel(KernelTags::CPU_CUSTOM_CLONE); + } + }; + + GAPI_OCV_KERNEL(BGR2Gray, cv::gapi::imgproc::GBGR2Gray) + { + static void run(const cv::Mat&, cv::Mat&) + { + HeteroGraph::registerCallKernel(KernelTags::CPU_CUSTOM_BGR2GRAY); + } + }; + + GAPI_OCV_KERNEL(GAdd, cv::gapi::core::GAdd) + { + static void run(const cv::Mat&, const cv::Mat&, int, cv::Mat&) + { + HeteroGraph::registerCallKernel(KernelTags::CPU_CUSTOM_ADD); + } + }; + } + + namespace fluid + { + GAPI_FLUID_KERNEL(GClone, I::GClone, false) + { + static const int Window = 1; + static void run(const cv::gapi::fluid::View&, cv::gapi::fluid::Buffer) + { + HeteroGraph::registerCallKernel(KernelTags::FLUID_CUSTOM_CLONE); + } + }; + + GAPI_FLUID_KERNEL(BGR2Gray, cv::gapi::imgproc::GBGR2Gray, false) + { + static const int Window = 1; + static void run(const cv::gapi::fluid::View&, cv::gapi::fluid::Buffer&) + { + HeteroGraph::registerCallKernel(KernelTags::FLUID_CUSTOM_BGR2GRAY); + } + }; + + GAPI_FLUID_KERNEL(GAdd, cv::gapi::core::GAdd, false) + { + static const int Window = 1; + static void run(const cv::gapi::fluid::View&, const cv::gapi::fluid::View&, + int, cv::gapi::fluid::Buffer&) + { + HeteroGraph::registerCallKernel(KernelTags::FLUID_CUSTOM_ADD); + } + }; + } + + std::set HeteroGraph::kernel_calls; +} // anonymous namespace TEST(KernelPackage, Create) { @@ -57,17 +158,6 @@ TEST(KernelPackage, IncludesAPI) EXPECT_FALSE(pkg.includesAPI()); } -TEST(KernelPackage, IncludesAPI_Overlapping) -{ - namespace J = Jupiter; - namespace S = Saturn; - auto pkg = cv::gapi::kernels(); - EXPECT_TRUE (pkg.includesAPI()); - EXPECT_TRUE (pkg.includesAPI()); - EXPECT_FALSE(pkg.includesAPI()); - EXPECT_FALSE(pkg.includesAPI()); -} - TEST(KernelPackage, Include_Add) { namespace J = Jupiter; @@ -78,23 +168,6 @@ TEST(KernelPackage, Include_Add) EXPECT_TRUE(pkg.includes()); } -TEST(KernelPackage, Include_KEEP) -{ - namespace J = Jupiter; - namespace S = Saturn; - auto pkg = cv::gapi::kernels(); - EXPECT_FALSE(pkg.includes()); - EXPECT_FALSE(pkg.includes()); - - pkg.include(); // default (KEEP) - EXPECT_TRUE(pkg.includes()); - EXPECT_TRUE(pkg.includes()); - - pkg.include(cv::unite_policy::KEEP); // explicit (KEEP) - EXPECT_TRUE(pkg.includes()); - EXPECT_TRUE(pkg.includes()); -} - TEST(KernelPackage, Include_REPLACE) { namespace J = Jupiter; @@ -102,7 +175,7 @@ TEST(KernelPackage, Include_REPLACE) auto pkg = cv::gapi::kernels(); EXPECT_FALSE(pkg.includes()); - pkg.include(cv::unite_policy::REPLACE); + pkg.include(); EXPECT_FALSE(pkg.includes()); EXPECT_TRUE(pkg.includes()); } @@ -111,31 +184,27 @@ TEST(KernelPackage, RemoveBackend) { namespace J = Jupiter; namespace S = Saturn; - auto pkg = cv::gapi::kernels(); + auto pkg = cv::gapi::kernels(); EXPECT_TRUE(pkg.includes()); EXPECT_TRUE(pkg.includes()); - EXPECT_TRUE(pkg.includes()); pkg.remove(J::backend()); EXPECT_FALSE(pkg.includes()); EXPECT_FALSE(pkg.includes()); - EXPECT_TRUE(pkg.includes()); + EXPECT_TRUE(pkg.includes()); }; TEST(KernelPackage, RemoveAPI) { namespace J = Jupiter; namespace S = Saturn; - auto pkg = cv::gapi::kernels(); + auto pkg = cv::gapi::kernels(); EXPECT_TRUE(pkg.includes()); EXPECT_TRUE(pkg.includes()); - EXPECT_TRUE(pkg.includes()); pkg.remove(); EXPECT_TRUE(pkg.includes()); - EXPECT_TRUE(pkg.includes()); EXPECT_FALSE(pkg.includes()); - EXPECT_FALSE(pkg.includes()); }; TEST(KernelPackage, CreateHetero) @@ -177,7 +246,7 @@ TEST(KernelPackage, Combine_REPLACE_Full) namespace S = Saturn; auto j_pkg = cv::gapi::kernels(); auto s_pkg = cv::gapi::kernels(); - auto u_pkg = cv::gapi::combine(j_pkg, s_pkg, cv::unite_policy::REPLACE); + auto u_pkg = cv::gapi::combine(j_pkg, s_pkg); EXPECT_EQ(3u, u_pkg.size()); EXPECT_FALSE(u_pkg.includes()); @@ -194,7 +263,7 @@ TEST(KernelPackage, Combine_REPLACE_Partial) namespace S = Saturn; auto j_pkg = cv::gapi::kernels(); auto s_pkg = cv::gapi::kernels(); - auto u_pkg = cv::gapi::combine(j_pkg, s_pkg, cv::unite_policy::REPLACE); + auto u_pkg = cv::gapi::combine(j_pkg, s_pkg); EXPECT_EQ(2u, u_pkg.size()); EXPECT_TRUE (u_pkg.includes()); @@ -208,38 +277,7 @@ TEST(KernelPackage, Combine_REPLACE_Append) namespace S = Saturn; auto j_pkg = cv::gapi::kernels(); auto s_pkg = cv::gapi::kernels(); - auto u_pkg = cv::gapi::combine(j_pkg, s_pkg, cv::unite_policy::REPLACE); - - EXPECT_EQ(3u, u_pkg.size()); - EXPECT_TRUE(u_pkg.includes()); - EXPECT_TRUE(u_pkg.includes()); - EXPECT_TRUE(u_pkg.includes()); -} - -TEST(KernelPackage, Combine_KEEP_AllDups) -{ - namespace J = Jupiter; - namespace S = Saturn; - auto j_pkg = cv::gapi::kernels(); - auto s_pkg = cv::gapi::kernels(); - auto u_pkg = cv::gapi::combine(j_pkg ,s_pkg, cv::unite_policy::KEEP); - - EXPECT_EQ(6u, u_pkg.size()); - EXPECT_TRUE(u_pkg.includes()); - EXPECT_TRUE(u_pkg.includes()); - EXPECT_TRUE(u_pkg.includes()); - EXPECT_TRUE(u_pkg.includes()); - EXPECT_TRUE(u_pkg.includes()); - EXPECT_TRUE(u_pkg.includes()); -} - -TEST(KernelPackage, Combine_KEEP_Append_NoDups) -{ - namespace J = Jupiter; - namespace S = Saturn; - auto j_pkg = cv::gapi::kernels(); - auto s_pkg = cv::gapi::kernels(); - auto u_pkg = cv::gapi::combine(j_pkg, s_pkg, cv::unite_policy::KEEP); + auto u_pkg = cv::gapi::combine(j_pkg, s_pkg); EXPECT_EQ(3u, u_pkg.size()); EXPECT_TRUE(u_pkg.includes()); @@ -252,7 +290,7 @@ TEST(KernelPackage, TestWithEmptyLHS) namespace J = Jupiter; auto lhs = cv::gapi::kernels<>(); auto rhs = cv::gapi::kernels(); - auto pkg = cv::gapi::combine(lhs, rhs, cv::unite_policy::KEEP); + auto pkg = cv::gapi::combine(lhs, rhs); EXPECT_EQ(1u, pkg.size()); EXPECT_TRUE(pkg.includes()); @@ -263,22 +301,211 @@ TEST(KernelPackage, TestWithEmptyRHS) namespace J = Jupiter; auto lhs = cv::gapi::kernels(); auto rhs = cv::gapi::kernels<>(); - auto pkg = cv::gapi::combine(lhs, rhs, cv::unite_policy::KEEP); + auto pkg = cv::gapi::combine(lhs, rhs); EXPECT_EQ(1u, pkg.size()); EXPECT_TRUE(pkg.includes()); } +TEST(KernelPackage, Return_Unique_Backends) +{ + auto pkg = cv::gapi::kernels(); + EXPECT_EQ(2u, pkg.backends().size()); +} + TEST(KernelPackage, Can_Use_Custom_Kernel) { cv::GMat in[2]; - auto out = GClone::on(cv::gapi::add(in[0], in[1])); + auto out = I::GClone::on(cv::gapi::add(in[0], in[1])); const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)}); - auto pkg = cv::gapi::kernels(); + auto pkg = cv::gapi::kernels(); EXPECT_NO_THROW(cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out)). compile({in_meta, in_meta}, cv::compile_args(pkg))); } +TEST_F(HeteroGraph, Call_Custom_Kernel_Default_Backend) +{ + // in0 -> GCPUAdd -> tmp -> cpu::GClone -> GCPUBGR2Gray -> out + // ^ + // | + // in1 -------` + + cv::Mat in_mat1 = cv::Mat::eye(3, 3, CV_8UC3), + in_mat2 = cv::Mat::eye(3, 3, CV_8UC3), + out_mat; + + auto pkg = cv::gapi::kernels(); + cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out)). + apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat), cv::compile_args(pkg)); + + EXPECT_TRUE(checkCallKernel(KernelTags::CPU_CUSTOM_CLONE)); +} + +TEST_F(HeteroGraph, Call_Custom_Kernel_Not_Default_Backend) +{ + // in0 -> GCPUAdd -> tmp -> fluid::GClone -> GCPUBGR2Gray -> out + // ^ + // | + // in1 -------` + + cv::Mat in_mat1 = cv::Mat::eye(3, 3, CV_8UC3), + in_mat2 = cv::Mat::eye(3, 3, CV_8UC3), + out_mat; + + auto pkg = cv::gapi::kernels(); + cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out)). + apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat), cv::compile_args(pkg)); + + EXPECT_TRUE(checkCallKernel(KernelTags::FLUID_CUSTOM_CLONE)); +} + +TEST_F(HeteroGraph, Replace_Default_To_Same_Backend) +{ + // in0 -> GCPUAdd -> tmp -> cpu::GClone -> cpu::BGR2Gray -> out + // ^ + // | + // in1 -------` + + cv::Mat in_mat1 = cv::Mat::eye(3, 3, CV_8UC3), + in_mat2 = cv::Mat::eye(3, 3, CV_8UC3), + out_mat; + + auto pkg = cv::gapi::kernels(); + cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out)). + apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat), cv::compile_args(pkg)); + + EXPECT_TRUE(checkCallKernel(KernelTags::CPU_CUSTOM_BGR2GRAY)); +} + +TEST_F(HeteroGraph, Replace_Default_To_Another_Backend) +{ + //in0 -> GCPUAdd -> tmp -> cpu::GClone -> fluid::BGR2Gray -> out + // ^ + // | + //in1 --------` + + cv::Mat in_mat1(300, 300, CV_8UC3), + in_mat2(300, 300, CV_8UC3), + out_mat; + + auto pkg = cv::gapi::kernels(); + cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out)). + apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat), cv::compile_args(pkg)); + + EXPECT_TRUE(checkCallKernel(KernelTags::FLUID_CUSTOM_BGR2GRAY)); +} + +TEST_F(HeteroGraph, Use_Only_Same_Backend) +{ + //in0 -> cpu::GAdd -> tmp -> cpu::GClone -> cpu::BGR2Gray -> out + // ^ + // | + //in1 --------` + + cv::Mat in_mat1(300, 300, CV_8UC3), + in_mat2(300, 300, CV_8UC3), + out_mat; + + auto pkg = cv::gapi::kernels(); + cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out)). + apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat), cv::compile_args(cv::gapi::use_only{pkg})); + + EXPECT_TRUE(checkCallKernel(KernelTags::CPU_CUSTOM_ADD)); + EXPECT_TRUE(checkCallKernel(KernelTags::CPU_CUSTOM_CLONE)); + EXPECT_TRUE(checkCallKernel(KernelTags::CPU_CUSTOM_BGR2GRAY)); +} + +TEST_F(HeteroGraph, Use_Only_Another_Backend) +{ + //in0 -> fluid::GAdd -> tmp -> fluid::GClone -> fluid::BGR2Gray -> out + // ^ + // | + //in1 --------` + + cv::Mat in_mat1(300, 300, CV_8UC3), + in_mat2(300, 300, CV_8UC3), + out_mat; + + auto pkg = cv::gapi::kernels(); + cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out)). + apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat), cv::compile_args(cv::gapi::use_only{pkg})); + + EXPECT_TRUE(checkCallKernel(KernelTags::FLUID_CUSTOM_ADD)); + EXPECT_TRUE(checkCallKernel(KernelTags::FLUID_CUSTOM_CLONE)); + EXPECT_TRUE(checkCallKernel(KernelTags::FLUID_CUSTOM_BGR2GRAY)); +} + +TEST_F(HeteroGraph, Use_Only_Hetero_Backend) +{ + //in0 -> cpu::GAdd -> tmp -> fluid::GClone -> fluid::BGR2Gray -> out + // ^ + // | + //in1 --------` + + cv::Mat in_mat1(300, 300, CV_8UC3), + in_mat2(300, 300, CV_8UC3), + out_mat; + + auto pkg = cv::gapi::kernels(); + cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out)). + apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat), cv::compile_args(cv::gapi::use_only{pkg})); + + EXPECT_TRUE(checkCallKernel(KernelTags::CPU_CUSTOM_ADD)); + EXPECT_TRUE(checkCallKernel(KernelTags::FLUID_CUSTOM_CLONE)); + EXPECT_TRUE(checkCallKernel(KernelTags::FLUID_CUSTOM_BGR2GRAY)); +} + +TEST_F(HeteroGraph, Use_Only_Not_Found_Default) +{ + //in0 -> GCPUAdd -> tmp -> fluid::GClone -> fluid::BGR2Gray -> out + // ^ + // | + //in1 --------` + + cv::Mat in_mat1(300, 300, CV_8UC3), + in_mat2(300, 300, CV_8UC3), + out_mat; + + auto pkg = cv::gapi::kernels(); + EXPECT_ANY_THROW(cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out)). + apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat), cv::compile_args(cv::gapi::use_only{pkg}))); +} + +TEST_F(HeteroGraph, Use_Only_Not_Found_Custom) +{ + //in0 -> cpu::GAdd -> tmp -> fluid::GClone -> fluid::BGR2Gray -> out + // ^ + // | + //in1 --------` + + cv::Mat in_mat1(300, 300, CV_8UC3), + in_mat2(300, 300, CV_8UC3), + out_mat; + + auto pkg = cv::gapi::kernels(); + EXPECT_ANY_THROW(cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out)). + apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat), cv::compile_args(cv::gapi::use_only{pkg}))); +} + +TEST_F(HeteroGraph, Use_Only_Other_Package_Ignored) +{ + //in0 -> cpu::GAdd -> tmp -> fluid::GClone -> fluid::BGR2Gray -> out + // ^ + // | + //in1 --------` + + cv::Mat in_mat1(300, 300, CV_8UC3), + in_mat2(300, 300, CV_8UC3), + out_mat; + + auto pkg = cv::gapi::kernels(); + auto clone_pkg = cv::gapi::kernels(); + + EXPECT_ANY_THROW(cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out)). + apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat), + cv::compile_args(clone_pkg, cv::gapi::use_only{pkg}))); +} + } // namespace opencv_test diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_mock_kernels.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_mock_kernels.hpp index cd876efdbe8b2f..9163281d25672a 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_mock_kernels.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_mock_kernels.hpp @@ -5,7 +5,7 @@ // Copyright (C) 2018 Intel Corporation -#include "opencv2/gapi/cpu/gcpukernel.hpp" +#include #include "api/gbackend_priv.hpp" // directly instantiate GBackend::Priv diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_sample_pipelines.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_sample_pipelines.cpp index 815aa0d8722fb1..0bfb4f6548f169 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_sample_pipelines.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_sample_pipelines.cpp @@ -298,4 +298,20 @@ TEST(GAPI_Pipeline, PipelineAllocatingKernel) EXPECT_THROW(comp.apply(in_mat, out_mat, cv::compile_args(pkg)), std::logic_error); } + +TEST(GAPI_Pipeline, CanUseOwnMatAsOutput) +{ + cv::GMat in; + cv::GComputation comp(in, cv::gapi::bitwise_not(in)); + + cv::Mat in_mat(3, 3, CV_8UC1); + cv::Mat out_mat(3, 3, CV_8UC1); + + cv::gapi::own::Mat in_own_mat(in_mat.rows, in_mat.cols, CV_8UC1, in_mat.data); + cv::gapi::own::Mat out_own_mat(out_mat.rows, out_mat.cols, CV_8UC1, out_mat.data); + + // FIXME add overload for apply(cv::gapi::own::Mat in, cv::gapi::own::Mat& out) + EXPECT_NO_THROW(comp.apply({in_own_mat}, {out_own_mat})); +} + } // namespace opencv_test diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_transform_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_transform_tests.cpp new file mode 100644 index 00000000000000..c18e930e06c563 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_transform_tests.cpp @@ -0,0 +1,189 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019 Intel Corporation + +#include + +#include "test_precomp.hpp" +#include "opencv2/gapi/gtransform.hpp" +#include "opencv2/gapi/gtype_traits.hpp" +// explicit include to use GComputation::Priv +#include "api/gcomputation_priv.hpp" + +namespace opencv_test +{ + +namespace +{ +using GMat = cv::GMat; +using GMat2 = std::tuple; +using GMat3 = std::tuple; +using GScalar = cv::GScalar; +template using GArray = cv::GArray; + +GAPI_TRANSFORM(gmat_in_gmat_out, , "gmat_in_gmat_out") +{ + static GMat pattern(GMat) { return {}; } + static GMat substitute(GMat) { return {}; } +}; + +GAPI_TRANSFORM(gmat2_in_gmat_out, , "gmat2_in_gmat_out") +{ + static GMat pattern(GMat, GMat) { return {}; } + static GMat substitute(GMat, GMat) { return {}; } +}; + +GAPI_TRANSFORM(gmat2_in_gmat3_out, , "gmat2_in_gmat3_out") +{ + static GMat3 pattern(GMat, GMat) { return {}; } + static GMat3 substitute(GMat, GMat) { return {}; } +}; + +GAPI_TRANSFORM(gmatp_in_gmatp_out, , "gmatp_in_gmatp_out") +{ + static GMatP pattern(GMatP) { return {}; } + static GMatP substitute(GMatP) { return {}; } +}; + +GAPI_TRANSFORM(gsc_in_gmat_out, , "gsc_in_gmat_out") +{ + static GMat pattern(GScalar) { return {}; } + static GMat substitute(GScalar) { return {}; } +}; + +GAPI_TRANSFORM(gmat_in_gsc_out, , "gmat_in_gsc_out") +{ + static GScalar pattern(GMat) { return {}; } + static GScalar substitute(GMat) { return {}; } +}; + +GAPI_TRANSFORM(garr_in_gmat_out, )>, "garr_in_gmat_out") +{ + static GMat pattern(GArray) { return {}; } + static GMat substitute(GArray) { return {}; } +}; + +GAPI_TRANSFORM(gmat_in_garr_out, (GMat)>, "gmat_in_garr_out") +{ + static GArray pattern(GMat) { return {}; } + static GArray substitute(GMat) { return {}; } +}; + +GAPI_TRANSFORM(gmat_gsc_garray_in_gmat2_out, )>, "gmat_gsc_garray_in_gmat2_out") +{ + static GMat2 pattern(GMat, GScalar, GArray) { return {}; } + static GMat2 substitute(GMat, GScalar, GArray) { return {}; } +}; + +} // anonymous namespace + +TEST(KernelPackageTransform, CreatePackage) +{ + auto pkg = cv::gapi::kernels + < gmat_in_gmat_out + , gmat2_in_gmat_out + , gmat2_in_gmat3_out + , gmatp_in_gmatp_out + , gsc_in_gmat_out + , gmat_in_gsc_out + , garr_in_gmat_out + , gmat_in_garr_out + , gmat_gsc_garray_in_gmat2_out + >(); + + auto tr = pkg.get_transformations(); + EXPECT_EQ(9u, tr.size()); +} + +TEST(KernelPackageTransform, Include) +{ + cv::gapi::GKernelPackage pkg; + pkg.include(); + pkg.include(); + pkg.include(); + auto tr = pkg.get_transformations(); + EXPECT_EQ(3u, tr.size()); +} + +TEST(KernelPackageTransform, Combine) +{ + auto pkg1 = cv::gapi::kernels(); + auto pkg2 = cv::gapi::kernels(); + auto pkg_comb = cv::gapi::combine(pkg1, pkg2); + auto tr = pkg_comb.get_transformations(); + EXPECT_EQ(2u, tr.size()); +} + +namespace { + template + inline bool ProtoContainsT(const cv::GProtoArg &arg) { + return cv::GProtoArg::index_of() == arg.index(); + } +} // anonymous namespace + +TEST(KernelPackageTransform, gmat_gsc_in_gmat_out) +{ + auto tr = gmat_gsc_garray_in_gmat2_out::transformation(); + + auto check = [](const cv::GComputation &comp){ + const auto &p = comp.priv(); + EXPECT_EQ(3u, p.m_ins.size()); + EXPECT_EQ(2u, p.m_outs.size()); + + EXPECT_TRUE(ProtoContainsT(p.m_ins[0])); + EXPECT_TRUE(ProtoContainsT(p.m_ins[1])); + EXPECT_TRUE(ProtoContainsT(p.m_ins[2])); + EXPECT_TRUE(cv::util::get(p.m_ins[2]).holds()); + EXPECT_FALSE(cv::util::get(p.m_ins[2]).holds()); + + EXPECT_TRUE(ProtoContainsT(p.m_outs[0])); + EXPECT_TRUE(ProtoContainsT(p.m_outs[1])); + }; + + check(tr.pattern()); + check(tr.substitute()); +} + +TEST(KernelPackageTransform, gmat_in_garr_out) +{ + auto tr = gmat_in_garr_out::transformation(); + + auto check = [](const cv::GComputation &comp){ + const auto &p = comp.priv(); + EXPECT_EQ(1u, p.m_ins.size()); + EXPECT_EQ(1u, p.m_outs.size()); + + EXPECT_TRUE(ProtoContainsT(p.m_ins[0])); + + EXPECT_TRUE(ProtoContainsT(p.m_outs[0])); + EXPECT_TRUE(cv::util::get(p.m_outs[0]).holds()); + EXPECT_FALSE(cv::util::get(p.m_outs[0]).holds()); + }; + + check(tr.pattern()); + check(tr.substitute()); +} + +TEST(KernelPackageTransform, garr_in_gmat_out) +{ + auto tr = garr_in_gmat_out::transformation(); + + auto check = [](const cv::GComputation &comp){ + const auto &p = comp.priv(); + EXPECT_EQ(1u, p.m_ins.size()); + EXPECT_EQ(1u, p.m_outs.size()); + + EXPECT_TRUE(ProtoContainsT(p.m_ins[0])); + EXPECT_TRUE(cv::util::get(p.m_ins[0]).holds()); + EXPECT_FALSE(cv::util::get(p.m_ins[0]).holds()); + + EXPECT_TRUE(ProtoContainsT(p.m_outs[0])); + }; + + check(tr.pattern()); + check(tr.substitute()); +} + +} // namespace opencv_test diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_util_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_util_tests.cpp index 574c0ab5426ae7..e080f544f9033e 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_util_tests.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gapi_util_tests.cpp @@ -9,7 +9,7 @@ #include -#include "opencv2/gapi/util/util.hpp" +#include namespace opencv_test { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_core_tests_gpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_core_tests_gpu.cpp index 5ee3f650affaf1..0f67688f834ebe 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_core_tests_gpu.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_core_tests_gpu.cpp @@ -2,73 +2,68 @@ // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // -// Copyright (C) 2018 Intel Corporation +// Copyright (C) 2018-2019 Intel Corporation #include "../test_precomp.hpp" #include "../common/gapi_core_tests.hpp" -#define CORE_GPU cv::gapi::core::gpu::kernels() +namespace +{ +#define CORE_GPU [] () { return cv::compile_args(cv::gapi::core::gpu::kernels()); } +} // anonymous namespace namespace opencv_test { // FIXME: Wut? See MulTestGPU/MathOpTest below (duplicate?) INSTANTIATE_TEST_CASE_P(AddTestGPU, MathOpTest, - Combine(Values(ADD, MUL), - testing::Bool(), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values(1.0), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), Values( -1, CV_8U, CV_16U, CV_32F ), - /*init output matrices or not*/ testing::Bool(), - Values(false), - Values(cv::compile_args(CORE_GPU))), - opencv_test::PrintMathOpCoreParams()); + Values(CORE_GPU), + Values(ADD, MUL), + testing::Bool(), + Values(1.0), + Values(false))); INSTANTIATE_TEST_CASE_P(MulTestGPU, MathOpTest, - Combine(Values(MUL), - testing::Bool(), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values(1.0, 0.5, 2.0), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), Values( -1, CV_8U, CV_16U, CV_32F ), - /*init output matrices or not*/ testing::Bool(), - Values(false), - Values(cv::compile_args(CORE_GPU))), - opencv_test::PrintMathOpCoreParams()); + Values(CORE_GPU), + Values(MUL), + testing::Bool(), + Values(1.0, 0.5, 2.0), + Values(false))); INSTANTIATE_TEST_CASE_P(SubTestGPU, MathOpTest, - Combine(Values(SUB), - testing::Bool(), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values (1.0), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), Values( -1, CV_8U, CV_16U, CV_32F ), - /*init output matrices or not*/ testing::Bool(), + Values(CORE_GPU), + Values(SUB), testing::Bool(), - Values(cv::compile_args(CORE_GPU))), - opencv_test::PrintMathOpCoreParams()); + Values (1.0), + testing::Bool())); INSTANTIATE_TEST_CASE_P(DivTestGPU, MathOpTest, - Combine(Values(DIV), - testing::Bool(), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values (1.0, 0.5, 2.0), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), Values( -1, CV_8U, CV_16U, CV_32F ), - /*init output matrices or not*/ testing::Bool(), + Values(CORE_GPU), + Values(DIV), testing::Bool(), - Values(cv::compile_args(CORE_GPU))), - opencv_test::PrintMathOpCoreParams()); + Values (1.0, 0.5, 2.0), + testing::Bool())); INSTANTIATE_TEST_CASE_P(MulTestGPU, MulDoubleTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), @@ -76,8 +71,7 @@ INSTANTIATE_TEST_CASE_P(MulTestGPU, MulDoubleTest, cv::Size(640, 480), cv::Size(128, 128)), Values( -1, CV_8U, CV_16U, CV_32F ), - /*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(DivTestGPU, DivTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), @@ -85,8 +79,7 @@ INSTANTIATE_TEST_CASE_P(DivTestGPU, DivTest, cv::Size(640, 480), cv::Size(128, 128)), Values( -1, CV_8U, CV_16U, CV_32F ), - /*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(DivCTestGPU, DivCTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), @@ -94,16 +87,15 @@ INSTANTIATE_TEST_CASE_P(DivCTestGPU, DivCTest, cv::Size(640, 480), cv::Size(128, 128)), Values( -1, CV_8U, CV_16U, CV_32F ), - /*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(MeanTestGPU, MeanTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - /*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU))); //TODO: mask test doesn't work #if 0 @@ -112,8 +104,7 @@ INSTANTIATE_TEST_CASE_P(MaskTestGPU, MaskTest, Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(CORE_GPU))); #endif INSTANTIATE_TEST_CASE_P(SelectTestGPU, SelectTest, @@ -121,92 +112,92 @@ INSTANTIATE_TEST_CASE_P(SelectTestGPU, SelectTest, Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(Polar2CartGPU, Polar2CartTest, - Combine(Values(cv::Size(1280, 720), + Combine(Values(CV_32FC1), + Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(CV_32FC1), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(Cart2PolarGPU, Cart2PolarTest, - Combine(Values(cv::Size(1280, 720), + Combine(Values(CV_32FC1), + Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(CV_32FC1), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(CompareTestGPU, CmpTest, - Combine(Values(CMP_EQ, CMP_GE, CMP_NE, CMP_GT, CMP_LT, CMP_LE), - testing::Bool(), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU))), - opencv_test::PrintCmpCoreParams()); + Values(CV_8U), + Values(CORE_GPU), + Values(CMP_EQ, CMP_GE, CMP_NE, CMP_GT, CMP_LT, CMP_LE), + testing::Bool())); INSTANTIATE_TEST_CASE_P(BitwiseTestGPU, BitwiseTest, - Combine(Values(AND, OR, XOR), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU))), - opencv_test::PrintBWCoreParams()); + Values(-1), + Values(CORE_GPU), + Values(AND, OR, XOR))); INSTANTIATE_TEST_CASE_P(BitwiseNotTestGPU, NotTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - /*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(MinTestGPU, MinTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(MaxTestGPU, MaxTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(SumTestGPU, SumTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(AbsToleranceScalar(1e-3).to_compare_f()),//TODO: too relaxed? - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU), + Values(AbsToleranceScalar(1e-3).to_compare_obj())));//TODO: too relaxed? INSTANTIATE_TEST_CASE_P(AbsDiffTestGPU, AbsDiffTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(AbsDiffCTestGPU, AbsDiffCTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(AddWeightedTestGPU, AddWeightedTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), @@ -214,44 +205,45 @@ INSTANTIATE_TEST_CASE_P(AddWeightedTestGPU, AddWeightedTest, cv::Size(640, 480), cv::Size(128, 128)), Values( -1, CV_8U, CV_16U, CV_32F ), -/*init output matrices or not*/ testing::Bool(), - Values(Tolerance_FloatRel_IntAbs(1e-6, 1).to_compare_f()), - Values(cv::compile_args(CORE_GPU)))); + Values(CORE_GPU), + Values(Tolerance_FloatRel_IntAbs(1e-6, 1).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(NormTestGPU, NormTest, - Combine(Values(NORM_INF, NORM_L1, NORM_L2), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(AbsToleranceScalar(1e-3).to_compare_f()), //TODO: too relaxed? - Values(cv::compile_args(CORE_GPU))), - opencv_test::PrintNormCoreParams()); + Values(-1), + Values(CORE_GPU), + Values(AbsToleranceScalar(1e-3).to_compare_obj()), //TODO: too relaxed? + Values(NORM_INF, NORM_L1, NORM_L2))); INSTANTIATE_TEST_CASE_P(IntegralTestGPU, IntegralTest, Combine(Values( CV_8UC1, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(ThresholdTestGPU, ThresholdTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::THRESH_BINARY, cv::THRESH_BINARY_INV, cv::THRESH_TRUNC, cv::THRESH_TOZERO, cv::THRESH_TOZERO_INV), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU), + Values(cv::THRESH_BINARY, cv::THRESH_BINARY_INV, cv::THRESH_TRUNC, + cv::THRESH_TOZERO, cv::THRESH_TOZERO_INV))); INSTANTIATE_TEST_CASE_P(ThresholdTestGPU, ThresholdOTTest, Combine(Values(CV_8UC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::THRESH_OTSU, cv::THRESH_TRIANGLE), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU), + Values(cv::THRESH_OTSU, cv::THRESH_TRIANGLE))); INSTANTIATE_TEST_CASE_P(InRangeTestGPU, InRangeTest, @@ -259,120 +251,155 @@ INSTANTIATE_TEST_CASE_P(InRangeTestGPU, InRangeTest, Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(Split3TestGPU, Split3Test, - Combine(Values(cv::Size(1280, 720), + Combine(Values(CV_8UC3), + Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_GPU)))); + Values(CV_8UC1), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(Split4TestGPU, Split4Test, - Combine(Values(cv::Size(1280, 720), + Combine(Values(CV_8UC4), + Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_GPU)))); + Values(CV_8UC1), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(ResizeTestGPU, ResizeTest, - Combine(Values(AbsSimilarPoints(2, 0.05).to_compare_f()), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), + Values(-1), + Values(CORE_GPU), + Values(AbsSimilarPoints(2, 0.05).to_compare_obj()), + Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA), Values(cv::Size(64,64), - cv::Size(30,30)), - Values(cv::compile_args(CORE_GPU)))); + cv::Size(30,30)))); INSTANTIATE_TEST_CASE_P(ResizeTestGPU, ResizeTestFxFy, - Combine(Values(AbsSimilarPoints(2, 0.05).to_compare_f()), - Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA), + Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), + Values(-1), + Values(CORE_GPU), + Values(AbsSimilarPoints(2, 0.05).to_compare_obj()), + Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA), Values(0.5, 0.1), - Values(0.5, 0.1), - Values(cv::compile_args(CORE_GPU)))); + Values(0.5, 0.1))); INSTANTIATE_TEST_CASE_P(Merge3TestGPU, Merge3Test, - Combine(Values(cv::Size(1280, 720), + Combine(Values(CV_8UC1), + Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_GPU)))); + Values(CV_8UC3), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(Merge4TestGPU, Merge4Test, - Combine(Values(cv::Size(1280, 720), + Combine(Values(CV_8UC1), + Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_GPU)))); + Values(CV_8UC4), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(RemapTestGPU, RemapTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(FlipTestGPU, FlipTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values(0,1,-1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU), + Values(0,1,-1))); INSTANTIATE_TEST_CASE_P(CropTestGPU, CropTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values(cv::Rect(10, 8, 20, 35), cv::Rect(4, 10, 37, 50)), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU), + Values(cv::Rect(10, 8, 20, 35), cv::Rect(4, 10, 37, 50)))); INSTANTIATE_TEST_CASE_P(LUTTestGPU, LUTTest, Combine(Values(CV_8UC1, CV_8UC3), - Values(CV_8UC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(CV_8UC1), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(LUTTestCustomGPU, LUTTest, Combine(Values(CV_8UC3), - Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(CV_8UC3), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(ConvertToGPU, ConvertToTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), - Values(CV_8U, CV_16U, CV_16S, CV_32F), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_GPU)))); + Values(CV_8U, CV_16U, CV_16S, CV_32F), + Values(CORE_GPU), + Values(AbsExact().to_compare_obj()), + Values(2.5, 1.0, -1.0), + Values(250.0, 0.0, -128.0))); INSTANTIATE_TEST_CASE_P(ConcatHorTestGPU, ConcatHorTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(ConcatVertTestGPU, ConcatVertTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU))); + +INSTANTIATE_TEST_CASE_P(BackendOutputAllocationTestGPU, BackendOutputAllocationTest, + Combine(Values(CV_8UC3, CV_16SC2, CV_32FC1), + Values(cv::Size(50, 50)), + Values(-1), + Values(CORE_GPU))); + +// FIXME: there's an issue in OCL backend with matrix reallocation that shouldn't happen +INSTANTIATE_TEST_CASE_P(DISABLED_BackendOutputAllocationLargeSizeWithCorrectSubmatrixTestGPU, + BackendOutputAllocationLargeSizeWithCorrectSubmatrixTest, + Combine(Values(CV_8UC3, CV_16SC2, CV_32FC1), + Values(cv::Size(50, 50)), + Values(-1), + Values(CORE_GPU))); + +INSTANTIATE_TEST_CASE_P(ReInitOutTestGPU, ReInitOutTest, + Combine(Values(CV_8UC3, CV_16SC4, CV_32FC1), + Values(cv::Size(640, 480)), + Values(-1), + Values(CORE_GPU), + Values(cv::Size(640, 400), + cv::Size(10, 480)))); //TODO: fix this backend to allow ConcatVertVec ConcatHorVec #if 0 @@ -381,13 +408,13 @@ INSTANTIATE_TEST_CASE_P(ConcatVertVecTestGPU, ConcatVertVecTest, Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_GPU)))); + Values(CORE_GPU))); INSTANTIATE_TEST_CASE_P(ConcatHorVecTestGPU, ConcatHorVecTest, Combine(Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::compile_args(CORE_GPU)))); + Values(CORE_GPU))); #endif } diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_imgproc_tests_gpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_imgproc_tests_gpu.cpp index 92e23e8ba8bf4e..e745bbeed2ba4e 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_imgproc_tests_gpu.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_imgproc_tests_gpu.cpp @@ -2,237 +2,243 @@ // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // -// Copyright (C) 2018 Intel Corporation +// Copyright (C) 2018-2019 Intel Corporation #include "../test_precomp.hpp" #include "../common/gapi_imgproc_tests.hpp" -#define IMGPROC_GPU cv::gapi::imgproc::gpu::kernels() +namespace +{ +#define IMGPROC_GPU [] () { return cv::compile_args(cv::gapi::imgproc::gpu::kernels()); } +} // anonymous namespace namespace opencv_test { - INSTANTIATE_TEST_CASE_P(Filter2DTestGPU, Filter2DTest, - Combine(Values(Tolerance_FloatRel_IntAbs(1e-5, 2).to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), - Values(3, 4, 5, 7), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), - Values(cv::BORDER_DEFAULT), Values(-1, CV_32F), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(IMGPROC_GPU), + Values(Tolerance_FloatRel_IntAbs(1e-5, 2).to_compare_obj()), + Values(3, 4, 5, 7), + Values(cv::BORDER_DEFAULT))); -INSTANTIATE_TEST_CASE_P(BoxFilterTestGPU, BoxFilterTest, - Combine(Values(Tolerance_FloatRel_IntAbs(1e-5, 2).to_compare_f()), - Values(/*CV_8UC1,*/ CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), - Values(3,5), +INSTANTIATE_TEST_CASE_P(BoxFilterTestCPU, BoxFilterTest, + Combine(Values(/*CV_8UC1,*/ CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), - Values(cv::BORDER_DEFAULT), Values(-1, CV_32F), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); //TODO: 8UC1 doesn't work + Values(IMGPROC_GPU), + Values(Tolerance_FloatRel_IntAbs(1e-5, 2).to_compare_obj()), + Values(3,5), + Values(cv::BORDER_DEFAULT))); //TODO: 8UC1 doesn't work + INSTANTIATE_TEST_CASE_P(SepFilterTestGPU_8U, SepFilterTest, - Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), - Values(CV_8UC1, CV_8UC3), - Values(3), + Combine(Values(CV_8UC1, CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), Values(-1, CV_16S, CV_32F), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(IMGPROC_GPU), + Values(ToleranceFilter(1e-4f, 0.01).to_compare_obj()), + Values(3))); INSTANTIATE_TEST_CASE_P(SepFilterTestGPU_other, SepFilterTest, - Combine(Values(ToleranceFilter(1e-4f, 0.01).to_compare_f()), - Values(CV_16UC1, CV_16SC1, CV_32FC1), - Values(3), + Combine(Values(CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), Values(-1, CV_32F), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(IMGPROC_GPU), + Values(ToleranceFilter(1e-4f, 0.01).to_compare_obj()), + Values(3))); INSTANTIATE_TEST_CASE_P(BlurTestGPU, BlurTest, - Combine(Values(Tolerance_FloatRel_IntAbs(1e-4, 2).to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), - Values(3,5), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), - Values(cv::BORDER_DEFAULT), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(-1), + Values(IMGPROC_GPU), + Values(Tolerance_FloatRel_IntAbs(1e-4, 2).to_compare_obj()), + Values(3,5), + Values(cv::BORDER_DEFAULT))); INSTANTIATE_TEST_CASE_P(gaussBlurTestGPU, GaussianBlurTest, - Combine(Values(ToleranceFilter(1e-5f, 0.01).to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), - Values(3), // FIXIT 5 + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(-1), + Values(IMGPROC_GPU), + Values(ToleranceFilter(1e-5f, 0.01).to_compare_obj()), + Values(3))); // FIXIT 5 INSTANTIATE_TEST_CASE_P(MedianBlurTestGPU, MedianBlurTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), - Values(3, 5), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(-1), + Values(IMGPROC_GPU), + Values(AbsExact().to_compare_obj()), + Values(3, 5))); INSTANTIATE_TEST_CASE_P(ErodeTestGPU, ErodeTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), - Values(3, 5), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), + Values(-1), + Values(IMGPROC_GPU), + Values(AbsExact().to_compare_obj()), + Values(3, 5), Values(cv::MorphShapes::MORPH_RECT, cv::MorphShapes::MORPH_CROSS, - cv::MorphShapes::MORPH_ELLIPSE), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + cv::MorphShapes::MORPH_ELLIPSE))); INSTANTIATE_TEST_CASE_P(Erode3x3TestGPU, Erode3x3Test, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(1,2,4), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(-1), + Values(IMGPROC_GPU), + Values(AbsExact().to_compare_obj()), + Values(1,2,4))); INSTANTIATE_TEST_CASE_P(DilateTestGPU, DilateTest, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), - Values(3, 5), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), + Values(-1), + Values(IMGPROC_GPU), + Values(AbsExact().to_compare_obj()), + Values(3, 5), Values(cv::MorphShapes::MORPH_RECT, cv::MorphShapes::MORPH_CROSS, - cv::MorphShapes::MORPH_ELLIPSE), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + cv::MorphShapes::MORPH_ELLIPSE))); INSTANTIATE_TEST_CASE_P(Dilate3x3TestGPU, Dilate3x3Test, - Combine(Values(AbsExact().to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(1,2,4), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(-1), + Values(IMGPROC_GPU), + Values(AbsExact().to_compare_obj()), + Values(1,2,4))); INSTANTIATE_TEST_CASE_P(SobelTestGPU, SobelTest, - Combine(Values(Tolerance_FloatRel_IntAbs(1e-4, 2).to_compare_f()), - Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), - Values(3, 5), + Combine(Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), Values(-1, CV_16S, CV_32F), + Values(IMGPROC_GPU), + Values(Tolerance_FloatRel_IntAbs(1e-4, 2).to_compare_obj()), + Values(3, 5), Values(0, 1), - Values(1, 2), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(1, 2))); INSTANTIATE_TEST_CASE_P(SobelTestGPU32F, SobelTest, - Combine(Values(Tolerance_FloatRel_IntAbs(1e-4, 2).to_compare_f()), - Values(CV_32FC1), - Values(3, 5), + Combine(Values(CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480)), Values(CV_32F), + Values(IMGPROC_GPU), + Values(Tolerance_FloatRel_IntAbs(1e-4, 2).to_compare_obj()), + Values(3, 5), Values(0, 1), - Values(1, 2), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(1, 2))); INSTANTIATE_TEST_CASE_P(EqHistTestGPU, EqHistTest, - Combine(Values(AbsExact().to_compare_f()), // FIXIT Non reliable check + Combine(Values(CV_8UC1), Values(cv::Size(1280, 720), - cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + cv::Size(640, 480)), + Values(-1), + Values(IMGPROC_GPU), + Values(AbsExact().to_compare_obj()))); // FIXIT Non reliable check INSTANTIATE_TEST_CASE_P(CannyTestGPU, CannyTest, - Combine(Values(AbsSimilarPoints(0, 0.05).to_compare_f()), - Values(CV_8UC1, CV_8UC3), + Combine(Values(CV_8UC1, CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), + Values(CV_8UC1), + Values(IMGPROC_GPU), + Values(AbsSimilarPoints(0, 0.05).to_compare_obj()), Values(3.0, 120.0), Values(125.0, 240.0), Values(3, 5), - testing::Bool(), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + testing::Bool())); INSTANTIATE_TEST_CASE_P(RGB2GrayTestGPU, RGB2GrayTest, - Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), - cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + cv::Size(640, 480)), + Values(CV_8UC1), + Values(IMGPROC_GPU), + Values(ToleranceColor(1e-3).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(BGR2GrayTestGPU, BGR2GrayTest, - Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(CV_8UC1), + Values(IMGPROC_GPU), + Values(ToleranceColor(1e-3).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(RGB2YUVTestGPU, RGB2YUVTest, - Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(CV_8UC3), + Values(IMGPROC_GPU), + Values(ToleranceColor(1e-3).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(YUV2RGBTestGPU, YUV2RGBTest, - Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(CV_8UC3), + Values(IMGPROC_GPU), + Values(ToleranceColor(1e-3).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(RGB2LabTestGPU, RGB2LabTest, - Combine(Values(AbsSimilarPoints(1, 0.05).to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(CV_8UC3), + Values(IMGPROC_GPU), + Values(AbsSimilarPoints(1, 0.05).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(BGR2LUVTestGPU, BGR2LUVTest, - Combine(Values(ToleranceColor(5e-3, 6).to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(CV_8UC3), + Values(IMGPROC_GPU), + Values(ToleranceColor(5e-3, 6).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(LUV2BGRTestGPU, LUV2BGRTest, - Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(CV_8UC3), + Values(IMGPROC_GPU), + Values(ToleranceColor(1e-3).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(BGR2YUVTestGPU, BGR2YUVTest, - Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); + Values(CV_8UC3), + Values(IMGPROC_GPU), + Values(ToleranceColor(1e-3).to_compare_obj()))); INSTANTIATE_TEST_CASE_P(YUV2BGRTestGPU, YUV2BGRTest, - Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Combine(Values(CV_8UC3), Values(cv::Size(1280, 720), cv::Size(640, 480)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(IMGPROC_GPU)))); - + Values(CV_8UC3), + Values(IMGPROC_GPU), + Values(ToleranceColor(1e-3).to_compare_obj()))); } // opencv_test diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_operators_tests_gpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_operators_tests_gpu.cpp index 73b1c78f34bd0f..a939d3208c09a8 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_operators_tests_gpu.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/gpu/gapi_operators_tests_gpu.cpp @@ -8,64 +8,62 @@ #include "../test_precomp.hpp" #include "../common/gapi_operators_tests.hpp" -#define CORE_GPU cv::gapi::core::gpu::kernels() +namespace +{ +#define CORE_GPU [] () { return cv::compile_args(cv::gapi::core::gpu::kernels()); } +} // anonymous namespace namespace opencv_test { - INSTANTIATE_TEST_CASE_P(MathOperatorTestGPU, MathOperatorMatMatTest, - Combine(Values(Tolerance_FloatRel_IntAbs(1e-5, 2).to_compare_f()), - Values( opPlusM, opMinusM, opDivM, - opGreater, opLess, opGreaterEq, opLessEq, opEq, opNotEq), - Values(CV_8UC1, CV_16SC1, CV_32FC1), + Combine(Values(CV_8UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), - cv::Size(640, 480), - cv::Size(128, 128)), + cv::Size(640, 480), + cv::Size(128, 128)), Values(-1, CV_8U, CV_32F), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(CORE_GPU), + Values(Tolerance_FloatRel_IntAbs(1e-5, 2).to_compare_obj()), + Values( opPlusM, opMinusM, opDivM, + opGreater, opLess, opGreaterEq, opLessEq, opEq, opNotEq))); INSTANTIATE_TEST_CASE_P(MathOperatorTestGPU, MathOperatorMatScalarTest, - Combine(Values(Tolerance_FloatRel_IntAbs(1e-4, 2).to_compare_f()), - Values( opPlus, opPlusR, opMinus, opMinusR, opMul, opMulR, // FIXIT avoid division by values near zero: opDiv, opDivR, - opGT, opLT, opGE, opLE, opEQ, opNE, - opGTR, opLTR, opGER, opLER, opEQR, opNER), - Values(CV_8UC1, CV_16SC1, CV_32FC1), + Combine(Values(CV_8UC1, CV_16SC1, CV_32FC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), Values(-1, CV_8U, CV_32F), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(CORE_GPU), + Values(Tolerance_FloatRel_IntAbs(1e-4, 2).to_compare_obj()), + Values( opPlus, opPlusR, opMinus, opMinusR, opMul, opMulR, // FIXIT avoid division by values near zero: opDiv, opDivR, + opGT, opLT, opGE, opLE, opEQ, opNE, + opGTR, opLTR, opGER, opLER, opEQR, opNER))); INSTANTIATE_TEST_CASE_P(BitwiseOperatorTestGPU, MathOperatorMatMatTest, - Combine(Values(AbsExact().to_compare_f()), - Values( opAnd, opOr, opXor ), - Values(CV_8UC1, CV_16UC1, CV_16SC1), + Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), - cv::Size(640, 480), - cv::Size(128, 128)), + cv::Size(640, 480), + cv::Size(128, 128)), Values(-1), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(CORE_GPU), + Values(AbsExact().to_compare_obj()), + Values( opAnd, opOr, opXor ))); INSTANTIATE_TEST_CASE_P(BitwiseOperatorTestGPU, MathOperatorMatScalarTest, - Combine(Values(AbsExact().to_compare_f()), - Values( opAND, opOR, opXOR, opANDR, opORR, opXORR ), - Values(CV_8UC1, CV_16UC1, CV_16SC1), + Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), Values(-1), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(CORE_GPU), + Values(AbsExact().to_compare_obj()), + Values( opAND, opOR, opXOR, opANDR, opORR, opXORR ))); INSTANTIATE_TEST_CASE_P(BitwiseNotOperatorTestGPU, NotOperatorTest, Combine(Values(CV_8UC1, CV_16UC1, CV_16SC1), Values(cv::Size(1280, 720), cv::Size(640, 480), cv::Size(128, 128)), -/*init output matrices or not*/ testing::Bool(), - Values(cv::compile_args(CORE_GPU)))); + Values(-1), + Values(CORE_GPU))); } diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_gmodel_builder_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_gmodel_builder_test.cpp index f5de11486336ab..e166b87a7a81e2 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_gmodel_builder_test.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_gmodel_builder_test.cpp @@ -9,7 +9,7 @@ #include // util::indexed -#include "opencv2/gapi/gkernel.hpp" +#include #include "compiler/gmodelbuilder.hpp" #include "compiler/gmodel.hpp" // RcDesc, GModel::init diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_recompilation_test.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_recompilation_test.cpp index 833ea177895525..c433025aa1bf16 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_recompilation_test.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/internal/gapi_int_recompilation_test.cpp @@ -8,9 +8,9 @@ #include "../test_precomp.hpp" #include "api/gcomputation_priv.hpp" -#include "opencv2/gapi/fluid/gfluidkernel.hpp" -#include "opencv2/gapi/fluid/core.hpp" -#include "opencv2/gapi/fluid/imgproc.hpp" +#include +#include +#include namespace opencv_test { @@ -197,8 +197,7 @@ TEST(GComputationCompile, ReshapeRois) cv::randn(first_in_mat, cv::Scalar::all(127), cv::Scalar::all(40.f)); cv::Mat first_out_mat; auto fluidKernels = cv::gapi::combine(gapi::imgproc::fluid::kernels(), - gapi::core::fluid::kernels(), - cv::unite_policy::REPLACE); + gapi::core::fluid::kernels()); cc.apply(first_in_mat, first_out_mat, cv::compile_args(fluidKernels)); auto first_comp = cc.priv().m_lastCompiled; diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/opencl_kernels_test_gapi.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/opencl_kernels_test_gapi.hpp index 1164165c4be800..e71985f6e3c3ba 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/opencl_kernels_test_gapi.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/opencl_kernels_test_gapi.hpp @@ -4,9 +4,9 @@ // // Copyright (C) 2018 Intel Corporation -#include "opencv2/core/ocl.hpp" -#include "opencv2/core/ocl_genbase.hpp" -#include "opencv2/core/opencl/ocl_defs.hpp" +#include +#include +#include #ifdef HAVE_OPENCL const char* opencl_symm7x7_src = diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/own/gapi_types_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/own/gapi_types_tests.cpp index d84319079e278c..b40bb1df8821d8 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/own/gapi_types_tests.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/own/gapi_types_tests.cpp @@ -6,7 +6,7 @@ #include "../test_precomp.hpp" -#include "opencv2/gapi/own/types.hpp" +#include namespace opencv_test { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/own/mat_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/own/mat_tests.cpp index a6453c62c01bfb..42245a53f113e2 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/own/mat_tests.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/own/mat_tests.cpp @@ -6,7 +6,7 @@ #include "../test_precomp.hpp" -#include "opencv2/gapi/own/mat.hpp" +#include #include //suppress_unused_warning namespace opencv_test diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/own/scalar_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/own/scalar_tests.cpp index 0ee626cc73b07c..09fec67ab99496 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/own/scalar_tests.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/own/scalar_tests.cpp @@ -6,7 +6,7 @@ #include "../test_precomp.hpp" -#include "opencv2/gapi/own/scalar.hpp" +#include namespace opencv_test { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/test_precomp.hpp b/inference-engine/thirdparty/fluid/modules/gapi/test/test_precomp.hpp index 9fb0e4d4f1285d..a2f8f3fc529678 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/test_precomp.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/test_precomp.hpp @@ -13,17 +13,17 @@ #include #include -#include "opencv2/ts.hpp" -#include "opencv2/gapi.hpp" -#include "opencv2/gapi/imgproc.hpp" -#include "opencv2/gapi/core.hpp" -#include "opencv2/gapi/cpu/gcpukernel.hpp" -#include "opencv2/gapi/gpu/ggpukernel.hpp" -#include "opencv2/gapi/gpu/imgproc.hpp" -#include "opencv2/gapi/gpu/core.hpp" -#include "opencv2/gapi/gcompoundkernel.hpp" -#include "opencv2/gapi/operators.hpp" -#include "opencv2/gapi/fluid/imgproc.hpp" -#include "opencv2/gapi/fluid/core.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #endif // __OPENCV_GAPI_TEST_PRECOMP_HPP__ diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/util/any_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/util/any_tests.cpp index 1c6c9cc0a7cbbd..9d3e9c92a4d41b 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/util/any_tests.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/util/any_tests.cpp @@ -6,7 +6,7 @@ #include "../test_precomp.hpp" -#include "opencv2/gapi/util/any.hpp" +#include namespace opencv_test { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/util/optional_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/util/optional_tests.cpp index 7b6cdb1f217055..7dde9fc63805a7 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/util/optional_tests.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/util/optional_tests.cpp @@ -6,7 +6,7 @@ #include "../test_precomp.hpp" -#include "opencv2/gapi/util/optional.hpp" +#include #include //suppress_unused_warning namespace opencv_test diff --git a/inference-engine/thirdparty/fluid/modules/gapi/test/util/variant_tests.cpp b/inference-engine/thirdparty/fluid/modules/gapi/test/util/variant_tests.cpp index 328afe7c9f74d8..bdeea9445f3bb0 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/test/util/variant_tests.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/test/util/variant_tests.cpp @@ -6,7 +6,7 @@ #include "../test_precomp.hpp" -#include "opencv2/gapi/util/variant.hpp" +#include #include //std::max_align_t namespace opencv_test diff --git a/inference-engine/thirdparty/fluid/revision.txt b/inference-engine/thirdparty/fluid/revision.txt index fc8eec54b89ade..3d69bd33473dec 100644 --- a/inference-engine/thirdparty/fluid/revision.txt +++ b/inference-engine/thirdparty/fluid/revision.txt @@ -1 +1 @@ -master / 2019-05-20 +master / 2019-07-29 diff --git a/inference-engine/thirdparty/mkl-dnn/include/mkldnn.h b/inference-engine/thirdparty/mkl-dnn/include/mkldnn.h index 03d58405970068..069981e6ebbce1 100644 --- a/inference-engine/thirdparty/mkl-dnn/include/mkldnn.h +++ b/inference-engine/thirdparty/mkl-dnn/include/mkldnn.h @@ -649,6 +649,9 @@ mkldnn_status_t MKLDNN_API mkldnn_memory_get_data_handle( mkldnn_status_t MKLDNN_API mkldnn_memory_set_data_handle( mkldnn_primitive_t memory, void *handle); +mkldnn_status_t MKLDNN_API mkldnn_memory_set_data_handle_no_pads_proc( + mkldnn_primitive_t memory, void *handle); + /** @} */ /** @addtogroup c_api_reorder Reorder diff --git a/inference-engine/thirdparty/mkl-dnn/include/mkldnn.hpp b/inference-engine/thirdparty/mkl-dnn/include/mkldnn.hpp index 07a4b04cb348bd..41dcb27f48e5c4 100644 --- a/inference-engine/thirdparty/mkl-dnn/include/mkldnn.hpp +++ b/inference-engine/thirdparty/mkl-dnn/include/mkldnn.hpp @@ -965,6 +965,11 @@ struct memory: public primitive { "could not set native handle"); } + inline void set_data_handle_no_pads_proc(void *handle) const { + error::wrap_c_api(mkldnn_memory_set_data_handle_no_pads_proc(get(), handle), + "could not set native handle"); + } + // Must go away or be private: static mkldnn_data_type_t convert_to_c(data_type adata_type) { return static_cast(adata_type); diff --git a/inference-engine/thirdparty/mkl-dnn/src/common/memory.cpp b/inference-engine/thirdparty/mkl-dnn/src/common/memory.cpp index 4c6656bc8ff8b6..fe6fdc0d90db7a 100644 --- a/inference-engine/thirdparty/mkl-dnn/src/common/memory.cpp +++ b/inference-engine/thirdparty/mkl-dnn/src/common/memory.cpp @@ -163,7 +163,13 @@ status_t mkldnn_memory_get_data_handle(const primitive_t *memory, status_t mkldnn_memory_set_data_handle(primitive_t *memory, void *handle) { if (any_null(memory) || memory->kind() != primitive_kind::memory) return invalid_arguments; - return memory->set_data_handle(handle); + return memory->set_data_handle(handle, true); +} + +status_t mkldnn_memory_set_data_handle_no_pads_proc(primitive_t *memory, void *handle) { + if (any_null(memory) || memory->kind() != primitive_kind::memory) + return invalid_arguments; + return memory->set_data_handle(handle, false); } status_t mkldnn_concat_primitive_desc_create_v2(primitive_desc_t **concat_pd, diff --git a/inference-engine/thirdparty/mkl-dnn/src/common/primitive.hpp b/inference-engine/thirdparty/mkl-dnn/src/common/primitive.hpp index e91a627b762fbb..1568c6b8238c1d 100644 --- a/inference-engine/thirdparty/mkl-dnn/src/common/primitive.hpp +++ b/inference-engine/thirdparty/mkl-dnn/src/common/primitive.hpp @@ -88,8 +88,9 @@ struct mkldnn_primitive: public mkldnn::impl::c_compatible { return mkldnn::impl::status::invalid_arguments; } /** sets data handle. Applicable for memory primitives only. */ - virtual mkldnn::impl::status_t set_data_handle(void *handle) { + virtual mkldnn::impl::status_t set_data_handle(void *handle, bool pads_zeroing) { UNUSED(handle); + UNUSED(pads_zeroing); assert(this->kind() == mkldnn::impl::primitive_kind::memory); return mkldnn::impl::status::invalid_arguments; } diff --git a/inference-engine/thirdparty/mkl-dnn/src/cpu/cpu_isa_traits.hpp b/inference-engine/thirdparty/mkl-dnn/src/cpu/cpu_isa_traits.hpp index 4ac2f87521e495..7db5ddc503a884 100644 --- a/inference-engine/thirdparty/mkl-dnn/src/cpu/cpu_isa_traits.hpp +++ b/inference-engine/thirdparty/mkl-dnn/src/cpu/cpu_isa_traits.hpp @@ -49,6 +49,7 @@ typedef enum { avx512_mic, avx512_mic_4ops, avx512_core_bf16, + avx512_vpopcnt, } cpu_isa_t; template struct cpu_isa_traits {}; /* ::vlen -> 32 (for avx2) */ @@ -129,6 +130,9 @@ static inline bool mayiuse(const cpu_isa_t cpu_isa) { return true && mayiuse(avx512_core_vnni) && cpu.has(Cpu::tAVX512_BF); + case avx512_vpopcnt: + return true + && cpu.has(Cpu::tAVX512_VPOPCNTDQ); case isa_any: return true; } diff --git a/inference-engine/thirdparty/mkl-dnn/src/cpu/cpu_memory.hpp b/inference-engine/thirdparty/mkl-dnn/src/cpu/cpu_memory.hpp index 830adcc630fe8f..02ba03e3719804 100644 --- a/inference-engine/thirdparty/mkl-dnn/src/cpu/cpu_memory.hpp +++ b/inference-engine/thirdparty/mkl-dnn/src/cpu/cpu_memory.hpp @@ -61,9 +61,9 @@ struct cpu_memory_t: public cpu_primitive_t { *handle = static_cast(data_); return success; } - virtual mkldnn::impl::status_t set_data_handle(void *handle) { + virtual mkldnn::impl::status_t set_data_handle(void *handle, bool pads_zeroing) { data_ = static_cast(handle); - return zero_pad(); + return pads_zeroing ? zero_pad() : success; } virtual char *memory(size_t output_index = 0) const diff --git a/inference-engine/thirdparty/mkl-dnn/src/cpu/jit_uni_bin_conv_kernel.cpp b/inference-engine/thirdparty/mkl-dnn/src/cpu/jit_uni_bin_conv_kernel.cpp index 189bd11d4f8d6e..04bca4d073ab61 100644 --- a/inference-engine/thirdparty/mkl-dnn/src/cpu/jit_uni_bin_conv_kernel.cpp +++ b/inference-engine/thirdparty/mkl-dnn/src/cpu/jit_uni_bin_conv_kernel.cpp @@ -174,35 +174,41 @@ void jit_uni_bin_conv_fwd_kernel::apply_filter(int ur_w, int pad_l, int pad if (jcp.ic_padded != jcp.ic && last_icb && ifm2 == (ic_blocks - 1)) uni_vandps(vmm_tmp, vmm_tmp, ptr[reg_table + 7 * vlen]); - if (isa == sse42) { - movups(vmm_tmp1, vmm_tmp); - pand(vmm_tmp1, vmm_mask); + if (mayiuse(avx512_vpopcnt)) { + vpopcntd(vmm_tmp, vmm_tmp); + uni_vpaddd(Vmm(1 + r * jcp.ur_w * jcp.nb_oc_blocking + ur_w * ii + jj), + Vmm(1 + r * jcp.ur_w * jcp.nb_oc_blocking + ur_w * ii + jj), vmm_tmp); } else { - uni_vandps(vmm_tmp1, vmm_mask, vmm_tmp); - } + if (isa == sse42) { + movups(vmm_tmp1, vmm_tmp); + pand(vmm_tmp1, vmm_mask); + } else { + uni_vandps(vmm_tmp1, vmm_mask, vmm_tmp); + } - uni_vpsrld(vmm_tmp, vmm_tmp, 4); - uni_vandps(vmm_tmp, vmm_tmp, vmm_mask); + uni_vpsrld(vmm_tmp, vmm_tmp, 4); + uni_vandps(vmm_tmp, vmm_tmp, vmm_mask); - if (isa == sse42) { - movups(vmm_tmp2, vmm_lookup); - pshufb(vmm_tmp2, vmm_tmp); - movups(vmm_tmp, vmm_lookup); - pshufb(vmm_tmp, vmm_tmp1); - paddb(vmm_tmp, vmm_tmp2); - } else { - uni_vpshufb(vmm_tmp, vmm_lookup, vmm_tmp); - uni_vpshufb(vmm_tmp1, vmm_lookup, vmm_tmp1); - uni_vpaddb(vmm_tmp, vmm_tmp, vmm_tmp1); - } + if (isa == sse42) { + movups(vmm_tmp2, vmm_lookup); + pshufb(vmm_tmp2, vmm_tmp); + movups(vmm_tmp, vmm_lookup); + pshufb(vmm_tmp, vmm_tmp1); + paddb(vmm_tmp, vmm_tmp2); + } else { + uni_vpshufb(vmm_tmp, vmm_lookup, vmm_tmp); + uni_vpshufb(vmm_tmp1, vmm_lookup, vmm_tmp1); + uni_vpaddb(vmm_tmp, vmm_tmp, vmm_tmp1); + } - if (mayiuse(avx512_core_vnni)) { - vpdpbusd(Vmm(1 + r * jcp.ur_w * jcp.nb_oc_blocking + ur_w * ii + jj), vmm_tmp, vmm_one_u8); - } else { - uni_vpmaddubsw(vmm_tmp, vmm_tmp, vmm_one_u8); - uni_vpmaddwd(vmm_tmp, vmm_tmp, vmm_one_s16); - uni_vpaddd(Vmm(1 + r * jcp.ur_w * jcp.nb_oc_blocking + ur_w * ii + jj), - Vmm(1 + r * jcp.ur_w * jcp.nb_oc_blocking + ur_w * ii + jj), vmm_tmp); + if (mayiuse(avx512_core_vnni)) { + vpdpbusd(Vmm(1 + r * jcp.ur_w * jcp.nb_oc_blocking + ur_w * ii + jj), vmm_tmp, vmm_one_u8); + } else { + uni_vpmaddubsw(vmm_tmp, vmm_tmp, vmm_one_u8); + uni_vpmaddwd(vmm_tmp, vmm_tmp, vmm_one_s16); + uni_vpaddd(Vmm(1 + r * jcp.ur_w * jcp.nb_oc_blocking + ur_w * ii + jj), + Vmm(1 + r * jcp.ur_w * jcp.nb_oc_blocking + ur_w * ii + jj), vmm_tmp); + } } } } diff --git a/inference-engine/thirdparty/mkl-dnn/src/cpu/ref_depthwise.cpp b/inference-engine/thirdparty/mkl-dnn/src/cpu/ref_depthwise.cpp index 4e954742efa58f..ec99c625c1e706 100644 --- a/inference-engine/thirdparty/mkl-dnn/src/cpu/ref_depthwise.cpp +++ b/inference-engine/thirdparty/mkl-dnn/src/cpu/ref_depthwise.cpp @@ -74,7 +74,9 @@ void ref_depthwise_fwd_t::execute_forward() const { parallel_nd(MB, C, D, H, W, [&](int n, int c, int d, int h, int w) { - size_t data_off = data_d.ndims() == 4 + size_t data_off = data_d.ndims() == 3 + ? data_d.off(n, c, d) + : data_d.ndims() == 4 ? data_d.off(n, c, h, w) : data_d.ndims() == 5 ? data_d.off(n, c, d, h, w) diff --git a/inference-engine/thirdparty/movidius/CMakeLists.txt b/inference-engine/thirdparty/movidius/CMakeLists.txt index 7c15e3b5dea9f8..6821e5aa4932eb 100644 --- a/inference-engine/thirdparty/movidius/CMakeLists.txt +++ b/inference-engine/thirdparty/movidius/CMakeLists.txt @@ -3,12 +3,11 @@ # if (ENABLE_MYRIAD) - add_subdirectory( - "${IE_MAIN_SOURCE_DIR}/thirdparty/movidius/XLink" - "${CMAKE_BINARY_DIR}/thirdparty/movidius/XLink") + set(XLINK_DIR "${IE_MAIN_SOURCE_DIR}/thirdparty/movidius/XLink" CACHE PATH "path to Xlink") + add_subdirectory("${XLINK_DIR}" "${CMAKE_BINARY_DIR}/thirdparty/movidius/XLink") add_subdirectory( "${IE_MAIN_SOURCE_DIR}/thirdparty/movidius/mvnc" "${CMAKE_BINARY_DIR}/thirdparty/movidius/mvnc") + endif() - \ No newline at end of file diff --git a/inference-engine/thirdparty/movidius/WinPthread/pthread_semaphore.c b/inference-engine/thirdparty/movidius/WinPthread/pthread_semaphore.c new file mode 100644 index 00000000000000..06d63262a5a914 --- /dev/null +++ b/inference-engine/thirdparty/movidius/WinPthread/pthread_semaphore.c @@ -0,0 +1,265 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "pthread_semaphore.h" + +#include +#include +#include +#include + +#ifndef SEM_VALUE_MAX +# define SEM_VALUE_MAX INT_MAX +#endif + +struct pthread_sem_private_t { + pthread_mutex_t access; + pthread_cond_t conditional; + volatile int counter; // >= 0 no waiters, == -1 some waiters +}; + +int pthread_sem_init(pthread_sem_t *psem, int pshared, unsigned int value) { + int result = 0; + if (NULL == psem) { + errno = EINVAL; + return -1; + } + if (value > SEM_VALUE_MAX){ + errno = EINVAL; + return -1; + } + if (pshared != 0) { + errno = ENOSYS; + return -1; + } + struct pthread_sem_private_t *psem_private = malloc(sizeof(struct pthread_sem_private_t)); + if (NULL == psem_private) { + return -1; + } + + result = pthread_mutex_init(&psem_private->access, NULL); + if (result) { + free(psem_private); + errno = result; + return -1; + } + + result = pthread_cond_init(&psem_private->conditional, NULL); + if (result) { + pthread_mutex_destroy(&psem_private->access); + free(psem_private); + errno = result; + return -1; + } + + psem_private->counter = value; + + *psem = (pthread_sem_t)psem_private; + errno = 0; + return 0; +} + +int pthread_sem_destroy(pthread_sem_t *psem) { + int result = 0; + + if (NULL == psem) { + errno = EINVAL; + return -1; + } + if (0 == *psem) { + errno = EINVAL; + return -1; + } + + struct pthread_sem_private_t *psem_private = (struct pthread_sem_private_t *)*psem; + + result = pthread_mutex_lock(&psem_private->access); + if (result) { + errno = result; + return -1; + } + + if (psem_private->counter == -1) { + pthread_mutex_unlock(&psem_private->access); + errno = EBUSY; + return -1; + } + + // conditional variable might not be deleted due to wait queue - lets notify users + result = pthread_cond_destroy(&psem_private->conditional); + if (result) { + pthread_mutex_unlock(&psem_private->access); + errno = result; + return -1; + } + + result = pthread_mutex_unlock(&psem_private->access); + if (result) { + errno = result; + return -1; + } + + // UB - untested if mutex object corrupted + result = pthread_mutex_destroy(&psem_private->access); + if (result) { + errno = result; + return -1; + } + + free(psem_private); + *psem = 0; + + errno = 0; + return 0; +} +static int pthread_sem_post_signal_or_broadcast(pthread_sem_t *psem, int broadcast) { + int result; + if (NULL == psem) { + errno = EINVAL; + return -1; + } + if (0 == *psem) { + errno = EINVAL; + return -1; + } + + struct pthread_sem_private_t *psem_private = (struct pthread_sem_private_t *)*psem; + result = pthread_mutex_lock(&psem_private->access); + if (result) { + errno = result; + return -1; + } + + // right now value == 0 not usually means that there is a waiter queue + if (broadcast) { + result = pthread_cond_broadcast(&psem_private->conditional); + } else { + result = pthread_cond_signal(&psem_private->conditional); + } + if (result) { + pthread_mutex_unlock(&psem_private->access); + errno = result; + return -1; + } + + // up counter + if (psem_private->counter == INT_MAX) { + pthread_mutex_unlock(&psem_private->access); + errno = EOVERFLOW; + return -1; + } + if (psem_private->counter == -1) { + psem_private->counter = 1; + } else { + psem_private->counter ++; + } + + result = pthread_mutex_unlock(&psem_private->access); + if (result) { + errno = result; + return -1; + } + + errno = 0; + return 0; +} + +int pthread_sem_post_broadcast(pthread_sem_t *psem) { + return pthread_sem_post_signal_or_broadcast(psem, 1); +} + +int pthread_sem_post(pthread_sem_t *psem) { + return pthread_sem_post_signal_or_broadcast(psem, 0); +} + +static int pthread_sem_timed_or_blocked_wait(pthread_sem_t *psem, const struct timespec *abstime) { + int result = 0; + if (NULL == psem) { + errno = EINVAL; + return -1; + } + if (0 == *psem) { + errno = EINVAL; + return -1; + } + struct pthread_sem_private_t *psem_private = (struct pthread_sem_private_t *)*psem; + result = pthread_mutex_lock(&psem_private->access); + if (result) { + errno = result; + return -1; + } + + for (;psem_private->counter < 1;) { + // indicate that we will be waiting this counter + psem_private->counter = -1; + if (abstime == NULL) { + result = pthread_cond_wait(&psem_private->conditional, &psem_private->access); + } else { + result = pthread_cond_timedwait(&psem_private->conditional, &psem_private->access, abstime); + } + if (result != 0) { + break; + } + } + + // printf("cond_wait=%d\n", result); + if (result) { + // sema not obtained - resetting counter back + if (psem_private->counter == -1) { + psem_private->counter = 0; + } + pthread_mutex_unlock(&psem_private->access); + errno = result; + return -1; + } + + // acquire semaphore + psem_private->counter --; + + result = pthread_mutex_unlock(&psem_private->access); + if (result) { + errno = result; + return -1; + } + + errno = 0; + return 0; +} + +int pthread_sem_wait(pthread_sem_t *psem) { + return pthread_sem_timed_or_blocked_wait(psem, NULL); +} + +int pthread_sem_timedwait(pthread_sem_t *psem, const struct timespec *abstime) { + if (NULL == abstime) { + errno = EINVAL; + return -1; + } + if (abstime->tv_sec < 0 || abstime->tv_nsec < 0) { + errno = EINVAL; + return -1; + } + return pthread_sem_timed_or_blocked_wait(psem, abstime); +} + + +# ifdef __APPLE__ + +int sem_init(sem_t *psem, int pshared, unsigned int value) { + return pthread_sem_init(psem, pshared, value); +} +int sem_destroy(sem_t *psem) { + return pthread_sem_destroy(psem); +} +int sem_post(sem_t *psem) { + return pthread_sem_post(psem); +} +int sem_wait(sem_t *psem) { + return pthread_sem_wait(psem); +} +int sem_timedwait(sem_t *psem, const struct timespec *abstime) { + return pthread_sem_timedwait(psem, abstime); +} + +#endif diff --git a/inference-engine/thirdparty/movidius/WinPthread/pthread_semaphore.h b/inference-engine/thirdparty/movidius/WinPthread/pthread_semaphore.h new file mode 100644 index 00000000000000..7cde51039a6f14 --- /dev/null +++ b/inference-engine/thirdparty/movidius/WinPthread/pthread_semaphore.h @@ -0,0 +1,48 @@ +// Copyright (C) 2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef PTHREAD_SEMAPHORE_H +#define PTHREAD_SEMAPHORE_H + +# include +# include +typedef intptr_t pthread_sem_t; + +# ifdef __cplusplus +extern "C" { +# endif +int pthread_sem_init(pthread_sem_t *psem, int pshared, unsigned int value); +int pthread_sem_destroy(pthread_sem_t *psem); +int pthread_sem_post(pthread_sem_t *psem); +int pthread_sem_post_broadcast(pthread_sem_t *psem); +int pthread_sem_wait(pthread_sem_t *psem); +int pthread_sem_timedwait(pthread_sem_t *psem, const struct timespec *abstime); +# ifdef __cplusplus +} +# endif + +# ifdef __APPLE__ + +typedef pthread_sem_t sem_t; + +# ifdef __cplusplus +extern "C" { +# endif + +int sem_init(sem_t *psem, int pshared, unsigned int value); +int sem_destroy(sem_t *psem); +int sem_post(sem_t *psem); +int sem_wait(sem_t *psem); +int sem_timedwait(sem_t *psem, const struct timespec *abstime); + +# ifdef __cplusplus +} +# endif + +# elif defined(_WIN32) +# error "pthread based semaphores not implemented for WIN32" +# else +# include +# endif // linux case +#endif // PTHREAD_SEMAPHORE_H diff --git a/inference-engine/thirdparty/movidius/XLink/CMakeLists.txt b/inference-engine/thirdparty/movidius/XLink/CMakeLists.txt index a28791de4f61fe..658081f4816622 100644 --- a/inference-engine/thirdparty/movidius/XLink/CMakeLists.txt +++ b/inference-engine/thirdparty/movidius/XLink/CMakeLists.txt @@ -4,29 +4,22 @@ set(TARGET_NAME "XLink") -if(NOT WIN32) - find_package(Threads REQUIRED) - - find_path(LIBUSB_INCLUDE_DIR NAMES libusb.h PATH_SUFFIXES "include" "libusb" "libusb-1.0") - find_library(LIBUSB_LIBRARY NAMES usb-1.0 PATH_SUFFIXES "lib") - - if(NOT LIBUSB_INCLUDE_DIR OR NOT LIBUSB_LIBRARY) - message(FATAL_ERROR "libusb is required") - endif() -endif() - -file(GLOB_RECURSE SOURCES *.c *.h) -file(GLOB_RECURSE SHARED "../shared/*") +include(XLink.cmake) -# FIXME: WIN_PTHREAD also should be built as a library +# Windows threads sources if(WIN32) file(GLOB USB_WIN_SOURCES "../USB_WIN/*") - file(GLOB WIN_PTHREAD_SOURCES "../WinPthread/*") - list(APPEND SOURCES ${USB_WIN_SOURCES} ${WIN_PTHREAD_SOURCES}) + set(WIN_PTHREAD_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/../WinPthread/win_semaphore.c" + "${CMAKE_CURRENT_SOURCE_DIR}/../WinPthread/win_pthread.c") + list(APPEND XLINK_SOURCES ${USB_WIN_SOURCES} ${WIN_PTHREAD_SOURCES}) +else() + list(APPEND XLINK_SOURCES "../WinPthread/pthread_semaphore.c") endif() -add_library(${TARGET_NAME} STATIC ${SOURCES} ${SHARED}) +add_library(${TARGET_NAME} STATIC ${XLINK_SOURCES}) +# Threads and usb include if(WIN32) target_include_directories(${TARGET_NAME} PRIVATE @@ -40,36 +33,30 @@ endif() target_include_directories(${TARGET_NAME} PUBLIC - "shared" - "../shared/include" - "pc") + ${XLINK_INCLUDE} + "../WinPthread") if(NOT WIN32) target_link_libraries(${TARGET_NAME} PUBLIC - Threads::Threads - ${LIBUSB_LIBRARY}) + Threads::Threads + ${LIBUSB_LIBRARY}) endif() target_compile_definitions(${TARGET_NAME} PRIVATE - __PC__ - HAVE_STRUCT_TIMESPEC - _CRT_SECURE_NO_WARNINGS + __PC__ + HAVE_STRUCT_TIMESPEC + _CRT_SECURE_NO_WARNINGS + USE_USB_VSC ) if (ENABLE_MYRIAD_NO_BOOT) target_compile_definitions(${TARGET_NAME} - PRIVATE - NO_BOOT - USE_USB_VSC) -else() - target_compile_definitions(${TARGET_NAME} - PRIVATE - USE_USB_VSC) + PRIVATE + NO_BOOT) endif() +add_dependencies(${TARGET_NAME} vpu_copy_firmware) -if(ENABLE_TESTS) - add_subdirectory(tests) -endif() \ No newline at end of file +set_property(TARGET ${TARGET_NAME} PROPERTY C_STANDARD 99) diff --git a/inference-engine/thirdparty/movidius/XLink/XLink.cmake b/inference-engine/thirdparty/movidius/XLink/XLink.cmake new file mode 100644 index 00000000000000..2cd1380e70e0ac --- /dev/null +++ b/inference-engine/thirdparty/movidius/XLink/XLink.cmake @@ -0,0 +1,36 @@ +if(EXISTS "$ENV{MV_COMMON_BASE}") + set(MV_COMMON_BASE $ENV{MV_COMMON_BASE}) +else() + set(MV_COMMON_BASE ${CMAKE_CURRENT_LIST_DIR}/..) +endif(EXISTS "$ENV{MV_COMMON_BASE}") + +if(NOT WIN32) + find_package(Threads REQUIRED) + + find_path(LIBUSB_INCLUDE_DIR NAMES libusb.h PATH_SUFFIXES "include" "libusb" "libusb-1.0") + find_library(LIBUSB_LIBRARY NAMES usb-1.0 PATH_SUFFIXES "lib") + + if(NOT LIBUSB_INCLUDE_DIR OR NOT LIBUSB_LIBRARY) + message(FATAL_ERROR "libusb is required") + endif() +endif() + +set(XLINK_INCLUDE + ${MV_COMMON_BASE}/XLink/pc + ${MV_COMMON_BASE}/XLink/shared + ${MV_COMMON_BASE}/shared/include + ) + +set(XLINK_INCLUDE_DIRECTORIES + ${XLINK_INCLUDE} + ${LIBUSB_INCLUDE_DIR} + ) + +set(XLINK_SOURCES + ${MV_COMMON_BASE}/XLink/pc/XLinkPlatform.c + ${MV_COMMON_BASE}/XLink/pc/usb_boot.c + ${MV_COMMON_BASE}/XLink/pc/pcie_host.c + ${MV_COMMON_BASE}/XLink/shared/XLink.c + ${MV_COMMON_BASE}/XLink/shared/XLinkDispatcher.c + ${MV_COMMON_BASE}/shared/src/mvStringUtils.c + ) diff --git a/inference-engine/thirdparty/movidius/XLink/pc/XLinkPlatform.c b/inference-engine/thirdparty/movidius/XLink/pc/XLinkPlatform.c index e3f4961104f560..c15145ac8c1645 100644 --- a/inference-engine/thirdparty/movidius/XLink/pc/XLinkPlatform.c +++ b/inference-engine/thirdparty/movidius/XLink/pc/XLinkPlatform.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "XLinkPlatform.h" #include "usb_boot.h" @@ -89,6 +90,26 @@ extern void initialize_usb_boot(); #define OPEN_DEV_ERROR_MESSAGE_LENGTH 128 #endif +static char* XLinkPlatformErrorToStr(const xLinkPlatformErrorCode_t errorCode) { + switch (errorCode) { + case X_LINK_PLATFORM_SUCCESS: return "X_LINK_PLATFORM_SUCCESS"; + case X_LINK_PLATFORM_DEVICE_NOT_FOUND: return "X_LINK_PLATFORM_DEVICE_NOT_FOUND"; + case X_LINK_PLATFORM_ERROR: return "X_LINK_PLATFORM_ERROR"; + case X_LINK_PLATFORM_TIMEOUT: return "X_LINK_PLATFORM_TIMEOUT"; + case X_LINK_PLATFORM_DRIVER_NOT_LOADED: return "X_LINK_PLATFORM_DRIVER_NOT_LOADED"; + default: return ""; + } +} + +static char* pciePlatformStateToStr(const pciePlatformState_t platformState) { + switch (platformState) { + case PCIE_PLATFORM_ANY_STATE: return "PCIE_PLATFORM_ANY_STATE"; + case PCIE_PLATFORM_BOOTED: return "PCIE_PLATFORM_BOOTED"; + case PCIE_PLATFORM_UNBOOTED: return "PCIE_PLATFORM_UNBOOTED"; + default: return ""; + } +} + static xLinkPlatformErrorCode_t parseUsbBootError(usbBootError_t rc) { switch (rc) { case USB_BOOT_SUCCESS: @@ -102,13 +123,31 @@ static xLinkPlatformErrorCode_t parseUsbBootError(usbBootError_t rc) { } } +static xLinkPlatformErrorCode_t parsePCIeHostError(pcieHostError_t rc) { + switch (rc) { + case PCIE_HOST_SUCCESS: + return X_LINK_PLATFORM_SUCCESS; + case PCIE_HOST_DEVICE_NOT_FOUND: + return X_LINK_PLATFORM_DEVICE_NOT_FOUND; + case PCIE_HOST_ERROR: + return X_LINK_PLATFORM_ERROR; + case PCIE_HOST_TIMEOUT: + return X_LINK_PLATFORM_TIMEOUT; + case PCIE_HOST_DRIVER_NOT_LOADED: + return X_LINK_PLATFORM_DRIVER_NOT_LOADED; + default: + return X_LINK_PLATFORM_ERROR; + } +} + static int usb_write(libusb_device_handle *f, const void *data, size_t size, unsigned int timeout) { + const int chunk_size = DEFAULT_CHUNKSZ; while(size > 0) { int bt, ss = size; - if(ss > 1024*1024*5) - ss = 1024*1024*5; + if(ss > chunk_size) + ss = chunk_size; #if (defined(_WIN32) || defined(_WIN64) ) int rc = usb_bulk_write(f, USB_ENDPOINT_OUT, (unsigned char *)data, ss, &bt, timeout); #else @@ -125,11 +164,12 @@ static int usb_write(libusb_device_handle *f, const void *data, size_t size, uns static int usb_read(libusb_device_handle *f, void *data, size_t size, unsigned int timeout) { + const int chunk_size = DEFAULT_CHUNKSZ; while(size > 0) { int bt, ss = size; - if(ss > 1024*1024*5) - ss = 1024*1024*5; + if(ss > chunk_size) + ss = chunk_size; #if (defined(_WIN32) || defined(_WIN64)) int rc = usb_bulk_read(f, USB_ENDPOINT_IN, (unsigned char *)data, ss, &bt, timeout); #else @@ -170,9 +210,9 @@ libusb_device_handle *usblink_open(const char *path) #if (!defined(_WIN32) && !defined(_WIN64)) uint16_t bcdusb = -1; - rc = usb_find_device_with_bcd(0, (char *)path, size, (void **)&dev, DEFAULT_OPENVID, DEFAULT_OPENPID, &bcdusb, 0); + rc = usb_find_device_with_bcd(0, (char *)path, size, (void **)&dev, DEFAULT_OPENVID, DEFAULT_OPENPID, &bcdusb); #else - rc = usb_find_device(0, (char *)path, size, (void **)&dev, DEFAULT_OPENVID, DEFAULT_OPENPID, 0); + rc = usb_find_device(0, (char *)path, size, (void **)&dev, DEFAULT_OPENVID, DEFAULT_OPENPID); #endif if(rc == USB_BOOT_SUCCESS) break; @@ -181,7 +221,7 @@ libusb_device_handle *usblink_open(const char *path) if (rc == USB_BOOT_TIMEOUT || rc == USB_BOOT_DEVICE_NOT_FOUND) // Timeout return 0; #if (defined(_WIN32) || defined(_WIN64) ) - h = usb_open_device(dev, NULL, 0, stderr, OPEN_DEV_ERROR_MESSAGE_LENGTH); + h = usb_open_device(dev, NULL, 0, stderr, OPEN_DEV_ERROR_MESSAGE_LENGTH); int libusb_rc = ((h != NULL) ? (0) : (-1)); if (libusb_rc < 0) { @@ -267,7 +307,7 @@ int USBLinkWrite(void* fd, void* data, int size, unsigned int timeout) return rc; } - int USBLinkRead(void* fd, void* data, int size, unsigned int timeout) +int USBLinkRead(void* fd, void* data, int size, unsigned int timeout) { int rc = 0; #ifndef USE_USB_VSC @@ -290,7 +330,7 @@ int USBLinkWrite(void* fd, void* data, int size, unsigned int timeout) while(toRead > 0) { - rc = read(usbFdRead, &((char*)gl_protocoldata)[nread], toRead); + rc = read(usbFdRead, &((char*)data)[nread], toRead); if ( rc < 0) { return -2; @@ -336,7 +376,7 @@ int USBLinkPlatformResetRemote(void* fd) int UsbLinkPlatformConnect(const char* devPathRead, const char* devPathWrite, void** fd) { - #if (!defined(USE_USB_VSC)) +#if (!defined(USE_USB_VSC)) #ifdef USE_LINK_JTAG struct sockaddr_in serv_addr; usbFdWrite = socket(AF_INET, SOCK_STREAM, 0); @@ -361,7 +401,7 @@ int UsbLinkPlatformConnect(const char* devPathRead, const char* devPathWrite, vo usbFdRead= open(devPathRead, O_RDWR); if(usbFdRead < 0) { - return -1; + return X_LINK_PLATFORM_DEVICE_NOT_FOUND; } // set tty to raw mode struct termios tty; @@ -369,8 +409,9 @@ int UsbLinkPlatformConnect(const char* devPathRead, const char* devPathWrite, vo int rc; rc = tcgetattr(usbFdRead, &tty); if (rc < 0) { + close(usbFdRead); usbFdRead = -1; - return -2; + return X_LINK_PLATFORM_ERROR; } spd = B115200; @@ -381,21 +422,25 @@ int UsbLinkPlatformConnect(const char* devPathRead, const char* devPathWrite, vo rc = tcsetattr(usbFdRead, TCSANOW, &tty); if (rc < 0) { + close(usbFdRead); usbFdRead = -1; - return -2; + return X_LINK_PLATFORM_ERROR; } usbFdWrite= open(devPathWrite, O_RDWR); if(usbFdWrite < 0) { + close(usbFdRead); usbFdWrite = -1; - return -2; + return X_LINK_PLATFORM_ERROR; } // set tty to raw mode rc = tcgetattr(usbFdWrite, &tty); if (rc < 0) { + close(usbFdRead); + close(usbFdWrite); usbFdWrite = -1; - return -2; + return X_LINK_PLATFORM_ERROR; } spd = B115200; @@ -406,8 +451,10 @@ int UsbLinkPlatformConnect(const char* devPathRead, const char* devPathWrite, vo rc = tcsetattr(usbFdWrite, TCSANOW, &tty); if (rc < 0) { + close(usbFdRead); + close(usbFdWrite); usbFdWrite = -1; - return -2; + return X_LINK_PLATFORM_ERROR; } return 0; #endif /*USE_LINK_JTAG*/ @@ -415,8 +462,8 @@ int UsbLinkPlatformConnect(const char* devPathRead, const char* devPathWrite, vo *fd = usblink_open(devPathWrite); if (*fd == 0) { - /* could fail due to port name change */ - return -1; + /* could fail due to port name change */ + return -1; } if(*fd) @@ -465,7 +512,7 @@ static int pcie_host_write(void *f, unsigned int timeout) { #if (defined(_WIN32) || defined(_WIN64)) -#define CHUNK_SIZE_BYTES (5ULL * 1024ULL * 1024ULL) + #define CHUNK_SIZE_BYTES (5ULL * 1024ULL * 1024ULL) while (size) { @@ -573,13 +620,24 @@ static int pcie_host_open(UNUSED const char* devPathRead, static int pcie_host_close(void *f) { -#if (!defined(_WIN32) && !defined(_WIN64)) + int rc; /** For PCIe device reset is called on host side */ - pcie_reset_device(*(int*)f); +#if (defined(_WIN32) && defined(_WIN64)) + rc = pcie_reset_device((HANDLE)f); +#else + rc = pcie_reset_device(*(int*)f); #endif - - pcie_close(f); - return 0; + if (rc) { + mvLog(MVLOG_ERROR, "Device resetting failed with error %d", rc); + pciePlatformState_t state = PCIE_PLATFORM_ANY_STATE; + pcie_get_device_state(f, &state); + mvLog(MVLOG_INFO, "Device state is %s", pciePlatformStateToStr(state)); + } + rc = pcie_close(f); + if (rc) { + mvLog(MVLOG_ERROR, "Device closing failed with error %d", rc); + } + return rc; } /*############################### FUNCTION ARRAYS #################################*/ @@ -619,7 +677,7 @@ int XLinkRead(xLinkDeviceHandle_t* deviceHandle, void* data, int size, unsigned int XLinkPlatformCloseRemote(xLinkDeviceHandle_t* deviceHandle) { if(deviceHandle->protocol == X_LINK_ANY_PROTOCOL || - deviceHandle->protocol == X_LINK_NMB_OF_PROTOCOLS) { + deviceHandle->protocol == X_LINK_NMB_OF_PROTOCOLS) { perror("No method for closing handler with protocol value equals to X_LINK_ANY_PROTOCOL and X_LINK_NMB_OF_PROTOCOLS\n"); return X_LINK_PLATFORM_ERROR; } @@ -634,72 +692,187 @@ void XLinkPlatformInit() #endif } -static int getDeviceName(int index, XLinkDeviceState_t state, deviceDesc_t* out_deviceDesc, - XLinkProtocol_t protocol, XLinkPlatform_t platform, int searchByName) -{ - if (index < 0) { - perror("Incorrect index value\n"); - return X_LINK_PLATFORM_ERROR; - } +pciePlatformState_t XLinkStateToPciePlatformState(const XLinkDeviceState_t state); - if(protocol == X_LINK_ANY_PROTOCOL || - protocol == X_LINK_USB_VSC) { - // At the moment there is no situation where you may need a non standard vid - int vid = AUTO_VID; +static xLinkPlatformErrorCode_t getUSBDeviceName(int index, + XLinkDeviceState_t state, + const deviceDesc_t in_deviceRequirements, + deviceDesc_t* out_foundDevice) { + ASSERT_X_LINK_PLATFORM(index >= 0); + ASSERT_X_LINK_PLATFORM(out_foundDevice); - int pid = AUTO_PID; - if(state == X_LINK_UNBOOTED) { - pid = XLinkPlatformToPid(platform); - if(searchByName) { - pid = get_pid_by_name(out_deviceDesc->name); - } - } else if(state == X_LINK_BOOTED) { - pid = DEFAULT_OPENPID; + int vid = AUTO_VID; + int pid = AUTO_PID; + + char name[XLINK_MAX_NAME_SIZE] = {}; + + int searchByName = 0; + if (strlen(in_deviceRequirements.name) > 0) { + searchByName = 1; + mv_strcpy(name, XLINK_MAX_NAME_SIZE, in_deviceRequirements.name); + } + + // Set PID + if (state == X_LINK_BOOTED) { + if (in_deviceRequirements.platform != X_LINK_ANY_PLATFORM) { + mvLog(MVLOG_WARN, "Search specific platform for booted device unavailable"); + return X_LINK_PLATFORM_ERROR; + } + pid = DEFAULT_OPENPID; + } else { + if (searchByName) { + pid = get_pid_by_name(in_deviceRequirements.name); + } else { + pid = XLinkPlatformToPid(in_deviceRequirements.platform, state); } + } #if (!defined(_WIN32) && !defined(_WIN64)) - uint16_t bcdusb = -1; - usbBootError_t rc = usb_find_device_with_bcd(index, out_deviceDesc->name, XLINK_MAX_NAME_SIZE, 0, vid, pid, &bcdusb, searchByName); + uint16_t bcdusb = -1; + usbBootError_t rc = usb_find_device_with_bcd( + index, name, XLINK_MAX_NAME_SIZE, 0, vid, pid, &bcdusb); #else - usbBootError_t rc = usb_find_device(index, out_deviceDesc->name, XLINK_MAX_NAME_SIZE, 0, vid, pid, searchByName); + usbBootError_t rc = usb_find_device( + index, name, XLINK_MAX_NAME_SIZE, 0, vid, pid); #endif + xLinkPlatformErrorCode_t xLinkRc = parseUsbBootError(rc); + if(xLinkRc == X_LINK_PLATFORM_SUCCESS) + { + mv_strcpy(out_foundDevice->name, XLINK_MAX_NAME_SIZE, name); + out_foundDevice->protocol = X_LINK_USB_VSC; + out_foundDevice->platform = XLinkPlatformPidToPlatform(get_pid_by_name(name)); + } + return xLinkRc; +} - xLinkPlatformErrorCode_t xLinkRc = parseUsbBootError(rc); - if(xLinkRc == X_LINK_PLATFORM_SUCCESS) - { - out_deviceDesc->protocol = X_LINK_USB_VSC; - out_deviceDesc->platform = XLinkPlatformPidToPlatform( - get_pid_by_name(out_deviceDesc->name)); - return xLinkRc; - } +static xLinkPlatformErrorCode_t getPCIeDeviceName(int index, + XLinkDeviceState_t state, + const deviceDesc_t in_deviceRequirements, + deviceDesc_t* out_foundDevice) { + ASSERT_X_LINK_PLATFORM(index >= 0); + ASSERT_X_LINK_PLATFORM(out_foundDevice); + ASSERT_X_LINK_PLATFORM(in_deviceRequirements.platform != X_LINK_MYRIAD_2); + + char name[XLINK_MAX_NAME_SIZE] = {}; + + if (strlen(in_deviceRequirements.name) > 0) { + mv_strcpy(name, XLINK_MAX_NAME_SIZE, in_deviceRequirements.name); } - if((protocol == X_LINK_ANY_PROTOCOL || - protocol == X_LINK_PCIE) && !searchByName) { - out_deviceDesc->protocol = X_LINK_PCIE; - out_deviceDesc->platform = platform; + pcieHostError_t rc = pcie_find_device_port( + index, name, XLINK_MAX_NAME_SIZE, XLinkStateToPciePlatformState(state)); - // #-18686 - return pcie_find_device_port(0, out_deviceDesc->name, XLINK_MAX_NAME_SIZE); + xLinkPlatformErrorCode_t xLinkRc = parsePCIeHostError(rc); + if(xLinkRc == X_LINK_PLATFORM_SUCCESS) + { + mv_strcpy(out_foundDevice->name, XLINK_MAX_NAME_SIZE, name); + out_foundDevice->protocol = X_LINK_PCIE; + out_foundDevice->platform = X_LINK_MYRIAD_X; } + return xLinkRc; +} + +xLinkPlatformErrorCode_t XLinkPlatformFindDeviceName(XLinkDeviceState_t state, + const deviceDesc_t in_deviceRequirements, + deviceDesc_t* out_foundDevice) { + memset(out_foundDevice, 0, sizeof(deviceDesc_t)); + xLinkPlatformErrorCode_t USB_rc; + xLinkPlatformErrorCode_t PCIe_rc; + + switch (in_deviceRequirements.protocol){ + case X_LINK_USB_CDC: + case X_LINK_USB_VSC: + return getUSBDeviceName(0, state, in_deviceRequirements, out_foundDevice); + + case X_LINK_PCIE: + return getPCIeDeviceName(0, state, in_deviceRequirements, out_foundDevice); + + case X_LINK_ANY_PROTOCOL: + USB_rc = getUSBDeviceName(0, state, in_deviceRequirements, out_foundDevice); + if (USB_rc == X_LINK_PLATFORM_SUCCESS) { // Found USB device, return it + return X_LINK_PLATFORM_SUCCESS; + } + if (USB_rc != X_LINK_PLATFORM_DEVICE_NOT_FOUND) { // Issue happen, log it + mvLog(MVLOG_DEBUG, "USB find device failed with rc: %s", + XLinkPlatformErrorToStr(USB_rc)); + } + + // Try to find PCIe device + memset(out_foundDevice, 0, sizeof(deviceDesc_t)); + PCIe_rc = getPCIeDeviceName(0, state, in_deviceRequirements, out_foundDevice); + if (PCIe_rc == X_LINK_PLATFORM_SUCCESS) { // Found PCIe device, return it + return X_LINK_PLATFORM_SUCCESS; + } + if (PCIe_rc != X_LINK_PLATFORM_DEVICE_NOT_FOUND) { // Issue happen, log it + mvLog(MVLOG_DEBUG, "PCIe find device failed with rc: %s", + XLinkPlatformErrorToStr(PCIe_rc)); + } + return X_LINK_PLATFORM_DEVICE_NOT_FOUND; - memset(out_deviceDesc, 0, sizeof(deviceDesc_t)); - return X_LINK_PLATFORM_DEVICE_NOT_FOUND; + default: + mvLog(MVLOG_WARN, "Unknown protocol"); + return X_LINK_PLATFORM_DEVICE_NOT_FOUND; + } } -int XLinkPlatformFindDeviceName(int index, +xLinkPlatformErrorCode_t XLinkPlatformFindArrayOfDevicesNames( XLinkDeviceState_t state, - deviceDesc_t* in_deviceRequirements, - deviceDesc_t* out_foundDevice) -{ - if(strnlen(in_deviceRequirements->name, XLINK_MAX_NAME_SIZE)) { - mv_strcpy(out_foundDevice->name, XLINK_MAX_NAME_SIZE, in_deviceRequirements->name); + const deviceDesc_t in_deviceRequirements, + deviceDesc_t* out_foundDevice, + const unsigned int devicesArraySize, + unsigned int *out_amountOfFoundDevices) { + + memset(out_foundDevice, 0, sizeof(deviceDesc_t) * devicesArraySize); + + unsigned int usb_index = 0; + unsigned int pcie_index = 0; + unsigned int both_protocol_index = 0; + + // TODO Handle possible errors + switch (in_deviceRequirements.protocol){ + case X_LINK_USB_CDC: + case X_LINK_USB_VSC: + while(getUSBDeviceName( + usb_index, state, in_deviceRequirements, &out_foundDevice[usb_index]) == + X_LINK_PLATFORM_SUCCESS) { + ++usb_index; + } - return getDeviceName(index, state, out_foundDevice, in_deviceRequirements->protocol, in_deviceRequirements->platform, 1); - } + *out_amountOfFoundDevices = usb_index; + return X_LINK_PLATFORM_SUCCESS; + + case X_LINK_PCIE: + while(getPCIeDeviceName( + pcie_index, state, in_deviceRequirements, &out_foundDevice[pcie_index]) == + X_LINK_PLATFORM_SUCCESS) { + ++pcie_index; + } + + *out_amountOfFoundDevices = pcie_index; + return X_LINK_PLATFORM_SUCCESS; + + case X_LINK_ANY_PROTOCOL: + while(getUSBDeviceName( + usb_index, state, in_deviceRequirements, + &out_foundDevice[both_protocol_index]) == + X_LINK_PLATFORM_SUCCESS) { + ++usb_index; + ++both_protocol_index; + } + while(getPCIeDeviceName( + pcie_index, state, in_deviceRequirements, + &out_foundDevice[both_protocol_index]) == + X_LINK_PLATFORM_SUCCESS) { + ++pcie_index; + ++both_protocol_index; + } + *out_amountOfFoundDevices = both_protocol_index; + return X_LINK_PLATFORM_SUCCESS; - memset(out_foundDevice->name, 0, XLINK_MAX_NAME_SIZE); - return getDeviceName(index, state, out_foundDevice, in_deviceRequirements->protocol, in_deviceRequirements->platform, 0); + default: + mvLog(MVLOG_WARN, "Unknown protocol"); + return X_LINK_PLATFORM_DEVICE_NOT_FOUND; + } } int XLinkPlatformIsDescriptionValid(deviceDesc_t *in_deviceDesc) { @@ -713,7 +886,7 @@ int XLinkPlatformIsDescriptionValid(deviceDesc_t *in_deviceDesc) { if(in_deviceDesc->platform != X_LINK_ANY_PLATFORM) { int namePid = get_pid_by_name(in_deviceDesc->name); - int platformPid = XLinkPlatformToPid(in_deviceDesc->platform); + int platformPid = XLinkPlatformToPid(in_deviceDesc->platform, X_LINK_UNBOOTED); return namePid == platformPid; } @@ -721,12 +894,24 @@ int XLinkPlatformIsDescriptionValid(deviceDesc_t *in_deviceDesc) { return 1; } -int XLinkPlatformToPid(const XLinkPlatform_t platform) { - switch (platform) { - case X_LINK_MYRIAD_2: return DEFAULT_UNBOOTPID_2150; - case X_LINK_MYRIAD_X: return DEFAULT_UNBOOTPID_2485; - default: return AUTO_UNBOOTED_PID; +int XLinkPlatformToPid(const XLinkPlatform_t platform, const XLinkDeviceState_t state) { + if (state == X_LINK_UNBOOTED) { + switch (platform) { + case X_LINK_MYRIAD_2: return DEFAULT_UNBOOTPID_2150; + case X_LINK_MYRIAD_X: return DEFAULT_UNBOOTPID_2485; + default: return AUTO_UNBOOTED_PID; + } + } else if (state == X_LINK_BOOTED) { + return DEFAULT_OPENPID; + } else if (state == X_LINK_ANY_STATE) { + switch (platform) { + case X_LINK_MYRIAD_2: return DEFAULT_UNBOOTPID_2150; + case X_LINK_MYRIAD_X: return DEFAULT_UNBOOTPID_2485; + default: return AUTO_PID; + } } + + return AUTO_PID; } XLinkPlatform_t XLinkPlatformPidToPlatform(const int pid) { @@ -737,6 +922,25 @@ XLinkPlatform_t XLinkPlatformPidToPlatform(const int pid) { } } +XLinkDeviceState_t XLinkPlatformPidToState(const int pid) { + switch (pid) { + case DEFAULT_OPENPID: return X_LINK_BOOTED; + case AUTO_PID: return X_LINK_ANY_STATE; + default: return X_LINK_UNBOOTED; + } +} + +pciePlatformState_t XLinkStateToPciePlatformState(const XLinkDeviceState_t state) { + switch (state) { + case X_LINK_ANY_STATE: return PCIE_PLATFORM_ANY_STATE; + case X_LINK_BOOTED: return PCIE_PLATFORM_BOOTED; + case X_LINK_UNBOOTED: return PCIE_PLATFORM_UNBOOTED; + default: + return PCIE_PLATFORM_ANY_STATE; + } +} + +#if (!defined(_WIN32) && !defined(_WIN64)) int XLinkPlatformBootRemote(deviceDesc_t* deviceDesc, const char* binaryPath) { int rc = 0; @@ -775,7 +979,7 @@ int XLinkPlatformBootRemote(deviceDesc_t* deviceDesc, const char* binaryPath) fclose(file); if (deviceDesc->protocol == X_LINK_PCIE) { - // FIXME Temporary open fd to boot device and then close it. But it can cause some problem + // Temporary open fd to boot device and then close it int* pcieFd = NULL; rc = pcie_init(deviceDesc->name, (void**)&pcieFd); if (rc) { @@ -809,3 +1013,72 @@ int XLinkPlatformBootRemote(deviceDesc_t* deviceDesc, const char* binaryPath) return -1; } } +#else +int XLinkPlatformBootRemote(deviceDesc_t* deviceDesc, const char* binaryPath) +{ + int rc = 0; + FILE *file; + long file_size; + + void *image_buffer; + + if (deviceDesc->protocol == X_LINK_PCIE) { + // FIXME Temporary open fd to boot device and then close it. But it can cause some problem + int* pcieFd = NULL; + + rc = pcie_init(deviceDesc->name, (void**)&pcieFd); + if (rc) { + printf("pcie_init failed with %s \n",deviceDesc->name); + return rc; + } + rc = pcie_boot_device(pcieFd); + pcie_close(pcieFd); // Will not check result for now + return rc; + } else if (deviceDesc->protocol == X_LINK_USB_VSC) { + /* Open the mvcmd file */ + file = fopen(binaryPath, "rb"); + if(file == NULL) { + printf("fw file open failed with %s \n",binaryPath); + if(usb_loglevel) + perror(binaryPath); + return -7; + } + fseek(file, 0, SEEK_END); + file_size = ftell(file); + rewind(file); + if(file_size <= 0 || !(image_buffer = (char*)malloc(file_size))) + { + if(usb_loglevel) + perror("buffer"); + fclose(file); + return -3; + } + if(fread(image_buffer, 1, file_size, file) != file_size) + { + if(usb_loglevel) + perror(binaryPath); + fclose(file); + free(image_buffer); + return -7; + } + fclose(file); + + char subaddr[28+2]; + // This will be the string to search for in /sys/dev/char links + int chars_to_write = snprintf(subaddr, 28, "-%s:", deviceDesc->name); + if(chars_to_write >= 28) { + printf("Path to your boot util is too long for the char array here!\n"); + } + // Boot it + rc = usb_boot(deviceDesc->name, image_buffer, file_size); + + if(!rc && usb_loglevel > 1) { + fprintf(stderr, "Boot successful, device address %s\n", deviceDesc->name); + } + return rc; + } else { + printf("Selected protocol not supported\n"); + return -1; + } +} +#endif diff --git a/inference-engine/thirdparty/movidius/XLink/pc/pcie_host.c b/inference-engine/thirdparty/movidius/XLink/pc/pcie_host.c index e2af8dd9333864..e8f8a53b7bf0fc 100644 --- a/inference-engine/thirdparty/movidius/XLink/pc/pcie_host.c +++ b/inference-engine/thirdparty/movidius/XLink/pc/pcie_host.c @@ -33,6 +33,8 @@ #define MVLOG_UNIT_NAME PCIe #include "mvLog.h" #include "mvStringUtils.h" +#include "pcie_host.h" + #define PCIE_DEVICE_ID 0x6200 #define PCIE_VENDOR_ID 0x8086 @@ -42,14 +44,23 @@ static HANDLE global_pcie_lock_fd = NULL; static OVERLAPPED global_pcie_lock_overlap = { 0 }; #define GLOBAL_PCIE_LOCK() LockFileEx(global_pcie_lock_fd, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, MAXDWORD, MAXDWORD, &global_pcie_lock_overlap) #define GLOBAL_PCIE_UNLOCK() UnlockFileEx(global_pcie_lock_fd, 0, MAXDWORD, MAXDWORD, &global_pcie_lock_overlap) +/* IOCTL commands IDs. for Windows*/ +#define MXLK_DEVICE_TYPE 40001 + +#define MXLK_STATUS_DEV CTL_CODE(MXLK_DEVICE_TYPE, 0xA08, METHOD_IN_DIRECT, FILE_ANY_ACCESS) +#define MXLK_RESET_DEV CTL_CODE(MXLK_DEVICE_TYPE, 0xA09, METHOD_IN_DIRECT, FILE_ANY_ACCESS) +#define MXLK_BOOT_DEV CTL_CODE(MXLK_DEVICE_TYPE, 0xA0A, METHOD_IN_DIRECT, FILE_ANY_ACCESS) + #endif -#if (!defined(_WIN32) || !defined(_WIN64)) +#if (!defined(_WIN32) && !defined(_WIN64)) /** MXLK data */ /* IOCTL commands IDs. */ #define IOC_MAGIC 'Z' -#define MXLK_RESET_DEV _IO(IOC_MAGIC, 0x80) -#define MXLK_BOOT_DEV _IOW(IOC_MAGIC, 0x81, struct mxlk_boot_param) +#define MXLK_RESET_DEV _IO(IOC_MAGIC, 0x80) +#define MXLK_BOOT_DEV _IOW(IOC_MAGIC, 0x81, struct mxlk_boot_param) +#define MXLK_STATUS_DEV _IOR(IOC_MAGIC, 0x82, enum mx_fw_status) +#endif struct mxlk_boot_param { /* Buffer containing the MX application image (MVCMD format) */ @@ -57,8 +68,17 @@ struct mxlk_boot_param { /* Size of the image in bytes. */ size_t length; }; + +/* State of Myriad X device. */ +enum mx_fw_status { + /* MX waiting for FW to be loaded from host */ + MX_FW_STATE_BOOTLOADER, + /* MX running FW loaded from host. */ + MX_FW_STATUS_USER_APP, + /* MX context is not restored or device is lost*/ + MX_FW_STATUS_UNKNOWN_STATE, +}; /** MXLK data end */ -#endif #if !(defined(_WIN32) || defined(_WIN64)) static inline void timeout_to_timeval(unsigned int timeout_ms, @@ -77,14 +97,14 @@ int pcie_write(HANDLE fd, void * buf, size_t bufSize, unsigned int timeout) HANDLE dev = fd; BOOL ret = WriteFile(dev, buf, bufSize, &bytesWritten, 0); - + mvLog(MVLOG_DEBUG, "pcie_write windows return fd %d buff %p bytesWritten %d errno %d", dev,buf, bytesWritten, errno); if (ret == FALSE) return -errno; return bytesWritten; } #else -int pcie_write(void *fd, void * buf, size_t bufSize, unsigned int timeout_ms) +pcieHostError_t pcie_write(void *fd, void * buf, size_t bufSize, unsigned int timeout_ms) { fd_set wrfds; struct timeval timeval; @@ -107,17 +127,17 @@ int pcie_write(void *fd, void * buf, size_t bufSize, unsigned int timeout_ms) ret = select(*((int*)fd) + 1, NULL, &wrfds, NULL, select_timeout); if (ret < 0) { - return X_LINK_PLATFORM_ERROR; + return PCIE_HOST_ERROR; } if (!FD_ISSET(*((int*)fd), &wrfds)) { - return X_LINK_PLATFORM_TIMEOUT; + return PCIE_HOST_TIMEOUT; } ret = write(*((int*)fd), buf, bufSize); if (ret < 0) { - return X_LINK_PLATFORM_ERROR; + return PCIE_HOST_ERROR; } return ret; @@ -125,7 +145,7 @@ int pcie_write(void *fd, void * buf, size_t bufSize, unsigned int timeout_ms) #endif // (defined(_WIN32) || defined(_WIN64)) #if (defined(_WIN32) || defined(_WIN64)) -int pcie_read(HANDLE fd, void * buf, size_t bufSize, int timeout) +int pcie_read(HANDLE fd, void * buf, size_t bufSize, unsigned int timeout) { int bytesRead; HANDLE dev = fd; @@ -138,7 +158,7 @@ int pcie_read(HANDLE fd, void * buf, size_t bufSize, int timeout) return bytesRead; } #else -int pcie_read(void *fd, void *buf, size_t bufSize, int timeout_ms) +pcieHostError_t pcie_read(void *fd, void *buf, size_t bufSize, unsigned int timeout_ms) { fd_set rdfds; struct timeval timeval; @@ -157,15 +177,15 @@ int pcie_read(void *fd, void *buf, size_t bufSize, int timeout_ms) ret = select(*((int*)fd) + 1, &rdfds, NULL, NULL, select_timeout); if (ret < 0) { - return X_LINK_PLATFORM_ERROR; + return PCIE_HOST_ERROR; } if (!FD_ISSET(*((int*)fd), &rdfds)) { - return X_LINK_PLATFORM_TIMEOUT; + return PCIE_HOST_TIMEOUT; } ret = read(*((int*)fd), buf, bufSize); if (ret < 0) { - return X_LINK_PLATFORM_ERROR; + return PCIE_HOST_ERROR; } return ret; @@ -175,6 +195,9 @@ int pcie_read(void *fd, void *buf, size_t bufSize, int timeout_ms) #if (defined(_WIN32) || defined(_WIN64)) int pcie_init(const char *slot, HANDLE *fd) { + +// Commented out to re-run when the execution is aborted +/* const char* tempPath = getenv("TEMP"); const char pcieMutexName[] = "\\pcie.mutex"; if (tempPath) { @@ -198,7 +221,7 @@ int pcie_init(const char *slot, HANDLE *fd) mvLog(MVLOG_ERROR, "Only one device supported."); return -1; } - +*/ HANDLE hDevice = CreateFile(slot, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, @@ -214,13 +237,14 @@ int pcie_init(const char *slot, HANDLE *fd) *fd = hDevice; + mvLog(MVLOG_DEBUG, "pcie_init windows new fd %d", *fd); return 0; } #else int pcie_init(const char *slot, void **fd) { if (!fd) - return -1; + return -1; int mx_fd = open(slot, O_RDWR); @@ -246,7 +270,8 @@ int pcie_init(const char *slot, void **fd) int pcie_close(void *fd) { #if (defined(_WIN32) || defined(_WIN64)) - GLOBAL_PCIE_UNLOCK(); + // Commented out to re-run when the execution is aborted + //GLOBAL_PCIE_UNLOCK(); HANDLE hDevice = (HANDLE)fd; if (hDevice == INVALID_HANDLE_VALUE) { @@ -309,32 +334,61 @@ int pci_count_devices(uint16_t vid, uint16_t pid) } #endif // (defined(_WIN32) || defined(_WIN64)) -xLinkPlatformErrorCode_t pcie_find_device_port(int index, char* port_name, int size) { +pcieHostError_t pcie_find_device_port( + int index, char* port_name, int name_length, const pciePlatformState_t requiredState) { + ASSERT_X_LINK_PLATFORM(port_name); + ASSERT_X_LINK_PLATFORM(index >= 0); + ASSERT_X_LINK_PLATFORM(name_length > 0); + + pcieHostError_t rc = PCIE_HOST_DEVICE_NOT_FOUND; + + char found_device[XLINK_MAX_NAME_SIZE] = { 0 }; + pciePlatformState_t platformState; + #if (defined(_WIN32) || defined(_WIN64)) - snprintf(port_name, size, "%s%d", "\\\\.\\mxlink", index); + int amoutOfMyriadPCIeDevices = pci_count_devices(PCIE_VENDOR_ID, PCIE_DEVICE_ID); + if (amoutOfMyriadPCIeDevices == 0) + return PCIE_HOST_DEVICE_NOT_FOUND; - if (pci_count_devices(PCIE_VENDOR_ID, PCIE_DEVICE_ID) == 0) { - mvLog(MVLOG_DEBUG, "No PCIe device(s) with Vendor ID: 0x%hX and Device ID: 0x%hX found", - PCIE_VENDOR_ID, PCIE_DEVICE_ID); - return X_LINK_PLATFORM_DEVICE_NOT_FOUND; - } + int amountOfSuitableDevices = 0; + int deviceCount = 0; - if (index > pci_count_devices(PCIE_VENDOR_ID, PCIE_DEVICE_ID)) { - return X_LINK_PLATFORM_DEVICE_NOT_FOUND; - } + while (deviceCount < amoutOfMyriadPCIeDevices) { + snprintf(found_device, XLINK_MAX_NAME_SIZE, "%s%d", "\\\\.\\mxlink", deviceCount); - return X_LINK_PLATFORM_SUCCESS; + // Get state of device + if (pcie_get_device_state(found_device, &platformState) != 0) { + return PCIE_HOST_ERROR; // Get device state step failed + } + + // Found device suits requested state + if (platformState == requiredState || requiredState == PCIE_PLATFORM_ANY_STATE) { + // If port_name is specified, we search for specific device + if (strnlen(port_name, name_length) > 1 && + strncmp(port_name, found_device, name_length) == 0) { + rc = PCIE_HOST_SUCCESS; + break; + // Trying to find device which suits requirements and index + } + else if (amountOfSuitableDevices == index) { + mv_strncpy(port_name, name_length, + found_device, XLINK_MAX_NAME_SIZE - 1); + rc = PCIE_HOST_SUCCESS; + break; + } + ++amountOfSuitableDevices; + } + ++deviceCount; + } + return rc; #else - xLinkPlatformErrorCode_t rc = X_LINK_PLATFORM_DEVICE_NOT_FOUND; struct dirent *entry; DIR *dp; - if (port_name == NULL) - return X_LINK_PLATFORM_ERROR; dp = opendir("/sys/class/mxlk/"); if (dp == NULL) { - return X_LINK_PLATFORM_DRIVER_NOT_LOADED; + return PCIE_HOST_DRIVER_NOT_LOADED; } // All entries in this (virtual) directory are generated when the driver @@ -344,13 +398,30 @@ xLinkPlatformErrorCode_t pcie_find_device_port(int index, char* port_name, int s // Compare the beginning of the name to make sure it is a device name if (strncmp(entry->d_name, "mxlk", 4) == 0) { - if (device_cnt == index) - { - snprintf(port_name, size, "/dev/%s", entry->d_name); - rc = X_LINK_PLATFORM_SUCCESS; - break; + // Save name + snprintf(found_device, name_length, "/dev/%s", entry->d_name); + // Get state of device + if (pcie_get_device_state(found_device, &platformState) != 0) { + closedir(dp); + return PCIE_HOST_ERROR; // Get device state step failed + } + + // Found device suits requested state + if (platformState == requiredState || requiredState == PCIE_PLATFORM_ANY_STATE) { + // If port_name is specified, we search for specific device + if (strnlen(port_name, name_length) > 1 && + strncmp(port_name, found_device, name_length) == 0) { + rc = PCIE_HOST_SUCCESS; + break; + // Trying to find device which suits requirements and index + } else if (device_cnt == index){ + mv_strncpy(port_name, name_length, + found_device, XLINK_MAX_NAME_SIZE - 1); + rc = PCIE_HOST_SUCCESS; + break; + } + ++device_cnt; } - device_cnt++; } } closedir(dp); @@ -359,24 +430,154 @@ xLinkPlatformErrorCode_t pcie_find_device_port(int index, char* port_name, int s #endif // (!defined(_WIN32) && !defined(_WIN64)) } +#if (!defined(_WIN32) && !defined(_WIN64)) int pcie_reset_device(int fd) { -#if (!defined(_WIN32) || !defined(_WIN64)) return ioctl(fd, MXLK_RESET_DEV); +} #else - return -1; -#endif +int pcie_reset_device(HANDLE fd) +{ + BOOL bResult = FALSE; + DWORD junk = 0; // discard results + int output_buffer; + + mvLog(MVLOG_DEBUG, "calling Windows RESET DeviceIoControl fd %d", fd); + if (fd == 0) { + return PCIE_HOST_ERROR; + } + + bResult = DeviceIoControl(fd, // device to be queried + MXLK_RESET_DEV, // operation to perform + NULL, 0, // no input buffer + &output_buffer, sizeof(output_buffer), // output buffer + &junk, // # bytes returned + (LPOVERLAPPED) NULL); // synchronous I/O + + if (!bResult) { + mvLog(MVLOG_ERROR, "RESET failed(status = %d).", GetLastError()); + return PCIE_HOST_ERROR; + } else { + return PCIE_HOST_SUCCESS; + } } +#endif +#if (!defined(_WIN32) && !defined(_WIN64)) int pcie_boot_device(int fd, void *buffer, size_t length) { -#if (!defined(_WIN32) || !defined(_WIN64)) + int rc = pcie_reset_device(fd); + if (rc) { + mvLog(MVLOG_INFO, "Device resetting failed with error: %d\n", rc); + return rc; + } struct mxlk_boot_param boot_param; boot_param.buffer = buffer; boot_param.length = length; return ioctl(fd, MXLK_BOOT_DEV, &boot_param); +} #else - return -1; + int pcie_boot_device(HANDLE fd) + { + int rc = pcie_reset_device(fd); + if (rc) { + mvLog(MVLOG_INFO, "Device resetting failed with error: %d\n", rc); + return rc; + } + + BOOL bResult = FALSE; + DWORD junk = 0; // discard results + int output_buffer; + struct mxlk_boot_param boot_param; + + mvLog(MVLOG_DEBUG, "calling Windows BOOT DeviceIoControl %d",fd); + if (fd == 0) { + return PCIE_HOST_ERROR; + } + bResult = DeviceIoControl(fd, // device to be queried + MXLK_BOOT_DEV, // operation to perform + NULL, 0, // no input buffer + &output_buffer, sizeof(output_buffer), // output buffer + &junk, // # bytes returned + (LPOVERLAPPED) NULL); // synchronous I/O + if (!bResult) { + mvLog(MVLOG_ERROR, "BOOT failed(status = %d)", GetLastError()); + return PCIE_HOST_ERROR; + } else { + return PCIE_HOST_SUCCESS; + } +} +#endif + + +pcieHostError_t pcie_get_device_state(const char *port_name, pciePlatformState_t *platformState) { + ASSERT_X_LINK_PLATFORM(port_name); + ASSERT_X_LINK_PLATFORM(platformState); + pcieHostError_t retCode = PCIE_HOST_SUCCESS; + +#if (!defined(_WIN32) && !defined(_WIN64)) // Linux implementation + int mx_fd = open(port_name, O_RDONLY); + + if (mx_fd == -1) { + // driver returns EACCESS in case it instance already used. + *platformState = PCIE_PLATFORM_BOOTED; + } else { + enum mx_fw_status fw_status= MX_FW_STATUS_UNKNOWN_STATE; + int ret = ioctl(mx_fd, MXLK_STATUS_DEV, &fw_status); + if(ret){ + *platformState = PCIE_PLATFORM_ANY_STATE; + mvLog(MVLOG_WARN, "Failed to get device status: %d. Errno %d", ret, errno); + retCode = PCIE_HOST_DEVICE_NOT_FOUND; + } else if(fw_status == MX_FW_STATUS_USER_APP) { + *platformState = PCIE_PLATFORM_BOOTED; + } else { + *platformState = PCIE_PLATFORM_UNBOOTED; + } + close(mx_fd); + } +#else // Windows implementation + HANDLE hDevice = INVALID_HANDLE_VALUE; // handle to the drive to be examined + BOOL bResult = FALSE; // results flag + DWORD junk = 0; // discard results + + hDevice = CreateFile(port_name, // drive to open + 0, // no access to the drive + FILE_SHARE_READ | // share mode + FILE_SHARE_WRITE, + NULL, // default security attributes + OPEN_EXISTING, // disposition + 0, // file attributes + NULL); // do not copy file attributes + + if (hDevice == INVALID_HANDLE_VALUE){ // cannot open the drive + mvLog(MVLOG_ERROR, "Failed to open device: %s. Error %d", port_name, GetLastError()); + *platformState = PCIE_PLATFORM_ANY_STATE; + return PCIE_HOST_DEVICE_NOT_FOUND; + } + enum mx_fw_status fw_status = MX_FW_STATUS_USER_APP; + + bResult = DeviceIoControl(hDevice, // device to be queried + MXLK_STATUS_DEV, // operation to perform + NULL, 0, // no input buffer + &fw_status, sizeof(fw_status), // output buffer + &junk, // # bytes returned + (LPOVERLAPPED) NULL); // synchronous I/O + + if (!bResult) { + mvLog(MVLOG_ERROR, "Failed to get device status. Error %d", GetLastError()); + *platformState = PCIE_PLATFORM_ANY_STATE; + retCode = PCIE_HOST_DEVICE_NOT_FOUND; + mvLog(MVLOG_DEBUG, "PCIE_PLATFORM_ANY_STATE"); + } else if (fw_status == MX_FW_STATUS_USER_APP) { + *platformState = PCIE_PLATFORM_BOOTED; + mvLog(MVLOG_DEBUG, "PCIE_PLATFORM_BOOTED"); + } else { + *platformState = PCIE_PLATFORM_UNBOOTED; + mvLog(MVLOG_DEBUG, "PCIE_PLATFORM_UNBOOTED"); + } + + CloseHandle(hDevice); #endif + return retCode; } diff --git a/inference-engine/thirdparty/movidius/XLink/pc/pcie_host.h b/inference-engine/thirdparty/movidius/XLink/pc/pcie_host.h index 9ce2273bd18fb4..4a54b1d5a8f5e9 100644 --- a/inference-engine/thirdparty/movidius/XLink/pc/pcie_host.h +++ b/inference-engine/thirdparty/movidius/XLink/pc/pcie_host.h @@ -6,13 +6,49 @@ #define PCIE_HOST_H #include "XLinkPlatform.h" +#include "XLinkPlatform_tool.h" + +typedef enum { + /* PCIE_PLATFORM_ANY_STATE intended for use in the device requirement, + / but also means an unknown state if we cannot get the device status */ + PCIE_PLATFORM_ANY_STATE = 0, + PCIE_PLATFORM_BOOTED = 1, + PCIE_PLATFORM_UNBOOTED = 2, +} pciePlatformState_t; + +typedef enum { + PCIE_HOST_SUCCESS = 0, + PCIE_HOST_DEVICE_NOT_FOUND, + PCIE_HOST_ERROR, + PCIE_HOST_TIMEOUT, + PCIE_HOST_DRIVER_NOT_LOADED +} pcieHostError_t; int pcie_init(const char *slot, void **fd); -int pcie_write(void *fd, void * buf, size_t bufSize, unsigned int timeout_ms); -int pcie_read(void *fd, void *buf, size_t bufSize, unsigned int timeout_ms); +pcieHostError_t pcie_write(void *fd, void * buf, size_t bufSize, unsigned int timeout_ms); +pcieHostError_t pcie_read(void *fd, void *buf, size_t bufSize, unsigned int timeout_ms); int pcie_close(void *fd); -xLinkPlatformErrorCode_t pcie_find_device_port(int index, char* port_name, int size); + +/** + * @brief Get device name on index + * @param port_name Port on which device is located. + * If not empty, function will search for device with this name + */ +pcieHostError_t pcie_find_device_port( + int index, char* port_name, int name_length, pciePlatformState_t requiredState); + +/** + * @brief Get state for pcie device on specified port + */ +pcieHostError_t pcie_get_device_state( + const char * port_name, pciePlatformState_t* platformState); + + +#if (!defined(_WIN32) && !defined(_WIN64)) int pcie_reset_device(int fd); int pcie_boot_device(int fd, void *buffer, size_t length); - +#else +int pcie_reset_device(HANDLE fd); +int pcie_boot_device(HANDLE fd); +#endif #endif // PCIE_HOST_H diff --git a/inference-engine/thirdparty/movidius/XLink/pc/usb_boot.c b/inference-engine/thirdparty/movidius/XLink/pc/usb_boot.c index 7e770c89a79d42..95dcdfe48949cf 100644 --- a/inference-engine/thirdparty/movidius/XLink/pc/usb_boot.c +++ b/inference-engine/thirdparty/movidius/XLink/pc/usb_boot.c @@ -2,11 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // -// USB utility for use with Myriad2v2 ROM -// Very heavily modified from Sabre version of usb_boot -// Copyright(C) 2015 Movidius Ltd. - - #include #include #include @@ -35,7 +30,6 @@ #define DEFAULT_WRITE_TIMEOUT 2000 #define DEFAULT_CONNECT_TIMEOUT 20000 #define DEFAULT_SEND_FILE_TIMEOUT 10000 -#define DEFAULT_CHUNKSZ 1024*1024 #define USB1_CHUNKSZ 64 /* @@ -192,7 +186,7 @@ static int isBootedMyriadDevice(const int idVendor, const int idProduct) { static int isNotBootedMyriadDevice(const int idVendor, const int idProduct) { // Device is Myriad, pid supported and it's is not booted device if (idVendor == DEFAULT_VID && is_pid_supported(idProduct) == 1 - && idProduct != DEFAULT_OPENPID) { + && idProduct != DEFAULT_OPENPID) { return 1; } return 0; @@ -214,8 +208,10 @@ static const char *gen_addr(libusb_device *dev, int pid) } p = buff; +#ifdef XLINK_USE_BUS uint8_t bus = libusb_get_bus_number(dev); p += snprintf(p, sizeof(buff), "%u.", bus); +#endif for (i = 0; i < pnum_cnt - 1; i++) p += snprintf(p, sizeof(buff),"%u.", pnums[i]); @@ -237,8 +233,8 @@ static pthread_mutex_t globalMutex = PTHREAD_MUTEX_INITIALIZER; /** * @brief Find usb device address - * @param addr Device name (address) which would be returned - * @param searchByName Means that need to find device with name which contains in addr parameter + * @param input_addr Device name (address) which would be returned. If not empty, we will try to + * find device with this name * * @details * Find any device (device = 0): @@ -253,13 +249,13 @@ static pthread_mutex_t globalMutex = PTHREAD_MUTEX_INITIALIZER; * Index can be used to iterate through all connected myriad devices and save their names. * It will loop only over suitable devices specified by vid and pid */ -usbBootError_t usb_find_device_with_bcd(unsigned idx, char *addr, - unsigned addrsize, void **device, int vid, int pid, uint16_t* bcdusb, int searchByName) { +usbBootError_t usb_find_device_with_bcd(unsigned idx, char *input_addr, + unsigned addrsize, void **device, int vid, int pid, uint16_t* bcdusb) { if (pthread_mutex_lock(&globalMutex)) { fprintf(stderr, "Mutex lock failed\n"); return USB_BOOT_ERROR; } - + int searchByName = 0; static libusb_device **devs = NULL; libusb_device *dev = NULL; struct libusb_device_descriptor desc; @@ -276,6 +272,10 @@ usbBootError_t usb_find_device_with_bcd(unsigned idx, char *addr, return USB_BOOT_ERROR; } + if (strlen(input_addr) > 1) { + searchByName = 1; + } + // Update device list if empty or if indx 0 if (!devs || idx == 0) { if (devs) { @@ -303,45 +303,46 @@ usbBootError_t usb_find_device_with_bcd(unsigned idx, char *addr, // If found device have the same id and vid as input if ( (desc.idVendor == vid && desc.idProduct == pid) - // Any myriad device - || (vid == AUTO_VID && pid == AUTO_PID - && isMyriadDevice(desc.idVendor, desc.idProduct)) - // Any not booted myriad device - || (vid == AUTO_VID && (pid == AUTO_UNBOOTED_PID) - && isNotBootedMyriadDevice(desc.idVendor, desc.idProduct)) - // Any not booted with specific pid - || (vid == AUTO_VID && pid == desc.idProduct - && isNotBootedMyriadDevice(desc.idVendor, desc.idProduct)) - // Any booted device - || (vid == AUTO_VID && pid == DEFAULT_OPENPID - && isBootedMyriadDevice(desc.idVendor, desc.idProduct)) ) + // Any myriad device + || (vid == AUTO_VID && pid == AUTO_PID + && isMyriadDevice(desc.idVendor, desc.idProduct)) + // Any not booted myriad device + || (vid == AUTO_VID && (pid == AUTO_UNBOOTED_PID) + && isNotBootedMyriadDevice(desc.idVendor, desc.idProduct)) + // Any not booted with specific pid + || (vid == AUTO_VID && pid == desc.idProduct + && isNotBootedMyriadDevice(desc.idVendor, desc.idProduct)) + // Any booted device + || (vid == AUTO_VID && pid == DEFAULT_OPENPID + && isBootedMyriadDevice(desc.idVendor, desc.idProduct)) ) { if (device) { - const char *caddr = gen_addr(dev, get_pid_by_name(addr)); - // If the same add as input - if (!strcmp(caddr, addr)) { + const char *dev_addr = gen_addr(dev, get_pid_by_name(input_addr)); + if (!strcmp(dev_addr, input_addr)) { if (usb_loglevel > 1) { fprintf(stderr, "Found Address: %s - VID/PID %04x:%04x\n", - addr, desc.idVendor, desc.idProduct); + input_addr, desc.idVendor, desc.idProduct); } + libusb_ref_device(dev); libusb_free_device_list(devs, 1); if (bcdusb) *bcdusb = desc.bcdUSB; *device = dev; devs = 0; + if (pthread_mutex_unlock(&globalMutex)) { fprintf(stderr, "Mutex unlock failed\n"); } return USB_BOOT_SUCCESS; } } else if (searchByName) { - const char *caddr = gen_addr(dev, get_pid_by_name(addr)); + const char *dev_addr = gen_addr(dev, desc.idProduct); // If the same add as input - if (!strcmp(caddr, addr)) { + if (!strcmp(dev_addr, input_addr)) { if (usb_loglevel > 1) { fprintf(stderr, "Found Address: %s - VID/PID %04x:%04x\n", - addr, desc.idVendor, desc.idProduct); + input_addr, desc.idVendor, desc.idProduct); } if (pthread_mutex_unlock(&globalMutex)) { @@ -354,7 +355,7 @@ usbBootError_t usb_find_device_with_bcd(unsigned idx, char *addr, if (usb_loglevel > 1) fprintf(stderr, "Device %d Address: %s - VID/PID %04x:%04x\n", idx, caddr, desc.idVendor, desc.idProduct); - mv_strncpy(addr, addrsize, caddr, addrsize - 1); + mv_strncpy(input_addr, addrsize, caddr, addrsize - 1); if (pthread_mutex_unlock(&globalMutex)) { fprintf(stderr, "Mutex unlock failed\n"); } @@ -373,8 +374,14 @@ usbBootError_t usb_find_device_with_bcd(unsigned idx, char *addr, #endif #if (defined(_WIN32) || defined(_WIN64) ) -usbBootError_t usb_find_device(unsigned idx, char *addr, unsigned addrsize, void **device, int vid, int pid, int specificDevice) +usbBootError_t usb_find_device(unsigned idx, char *addr, unsigned addrsize, void **device, int vid, int pid) { + if (!addr) + return USB_BOOT_ERROR; + int specificDevice = 0; + if (strlen(addr) > 1) + specificDevice = 1; + // TODO There is no global mutex as in linux version int res; // 2 => vid @@ -497,7 +504,7 @@ static libusb_device_handle *usb_open_device(libusb_device *dev, uint8_t *endpoi { if(usb_loglevel > 1) fprintf(stderr, "Found EP 0x%02x : max packet size is %u bytes\n", - ifdesc->endpoint[i].bEndpointAddress, ifdesc->endpoint[i].wMaxPacketSize); + ifdesc->endpoint[i].bEndpointAddress, ifdesc->endpoint[i].wMaxPacketSize); if((ifdesc->endpoint[i].bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) != LIBUSB_TRANSFER_TYPE_BULK) continue; if( !(ifdesc->endpoint[i].bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) ) @@ -510,7 +517,7 @@ static libusb_device_handle *usb_open_device(libusb_device *dev, uint8_t *endpoi } libusb_free_config_descriptor(cdesc); mv_strcpy(err_string_buff, OPEN_DEV_ERROR_MESSAGE_LENGTH, - "Unable to find BULK OUT endpoint\n"); + "Unable to find BULK OUT endpoint\n"); libusb_close(h); return 0; } @@ -533,66 +540,66 @@ static int wait_findopen(const char *device_address, int timeout, libusb_device return USB_BOOT_ERROR; } - usleep(100000); - if(usb_loglevel > 1) - { - if(timeout == -1) - fprintf(stderr, "Starting wait for connect, no timeout\n"); - else if(timeout == 0) - fprintf(stderr, "Trying to connect\n"); - else fprintf(stderr, "Starting wait for connect with %ums timeout\n", timeout); - } - last_open_dev_err[0] = 0; - i = 0; - for(;;) - { - highres_gettime(&t1); - int addr_size = strlen(device_address); + usleep(100000); + if(usb_loglevel > 1) + { + if(timeout == -1) + fprintf(stderr, "Starting wait for connect, no timeout\n"); + else if(timeout == 0) + fprintf(stderr, "Trying to connect\n"); + else fprintf(stderr, "Starting wait for connect with %ums timeout\n", timeout); + } + last_open_dev_err[0] = 0; + i = 0; + for(;;) + { + highres_gettime(&t1); + int addr_size = strlen(device_address); #if (!defined(_WIN32) && !defined(_WIN64) ) rc = usb_find_device_with_bcd(0, (char*)device_address, addr_size, (void**)dev, - DEFAULT_VID, get_pid_by_name(device_address), bcdusb, 0); + DEFAULT_VID, get_pid_by_name(device_address), bcdusb); #else rc = usb_find_device(0, (char *)device_address, addr_size, (void **)dev, - DEFAULT_VID, get_pid_by_name(device_address), 0); + DEFAULT_VID, get_pid_by_name(device_address)); #endif - if(rc < 0) - return USB_BOOT_ERROR; - if(!rc) - { + if(rc < 0) + return USB_BOOT_ERROR; + if(!rc) + { #if (!defined(_WIN32) && !defined(_WIN64) ) *devh = usb_open_device(*dev, endpoint, last_open_dev_err, OPEN_DEV_ERROR_MESSAGE_LENGTH); #else *devh = usb_open_device(*dev, endpoint, 0, last_open_dev_err, OPEN_DEV_ERROR_MESSAGE_LENGTH); #endif if(*devh != NULL) - { - if(usb_loglevel > 1) - fprintf(stderr, "Found and opened device\n"); - return 0; - } + { + if(usb_loglevel > 1) + fprintf(stderr, "Found and opened device\n"); + return 0; + } #if (!defined(_WIN32) && !defined(_WIN64) ) - libusb_unref_device(*dev); + libusb_unref_device(*dev); #endif - } + } highres_gettime(&t2); elapsedTime += highres_elapsed_ms(&t1, &t2); - if(timeout != -1) - { - if(usb_loglevel) - { - if(last_open_dev_err[0]) - fprintf(stderr, "%s", last_open_dev_err); - fprintf(stderr, "error: device not found!\n"); - } - return rc ? USB_BOOT_DEVICE_NOT_FOUND : USB_BOOT_TIMEOUT; + if(timeout != -1) + { + if(usb_loglevel) + { + if(last_open_dev_err[0]) + fprintf(stderr, "%s", last_open_dev_err); + fprintf(stderr, "error: device not found!\n"); + } + return rc ? USB_BOOT_DEVICE_NOT_FOUND : USB_BOOT_TIMEOUT; } else if (elapsedTime > (double)timeout) { return rc ? USB_BOOT_DEVICE_NOT_FOUND : USB_BOOT_TIMEOUT; - } - i++; - usleep(100000); - } - return 0; + } + i++; + usleep(100000); + } + return 0; } #if (!defined(_WIN32) && !defined(_WIN64) ) @@ -601,11 +608,11 @@ static int send_file(libusb_device_handle* h, uint8_t endpoint, const uint8_t* t static int send_file(libusb_device_handle *h, uint8_t endpoint, const uint8_t *tx_buf, unsigned filesize) #endif { - const uint8_t *p; - int rc; - int wb, twb, wbr; - double elapsedTime; - highres_time_t t1, t2; + const uint8_t *p; + int rc; + int wb, twb, wbr; + double elapsedTime; + highres_time_t t1, t2; unsigned int bulk_chunklen=DEFAULT_CHUNKSZ; elapsedTime = 0; twb = 0; @@ -616,44 +623,44 @@ static int send_file(libusb_device_handle *h, uint8_t endpoint, const uint8_t *t bulk_chunklen = USB1_CHUNKSZ; } #endif - if(usb_loglevel > 1) - fprintf(stderr, "Performing bulk write of %u bytes...\n", filesize); - while(twb < filesize) - { - highres_gettime(&t1); - wb = filesize - twb; - if(wb > bulk_chunklen) - wb = bulk_chunklen; - wbr = 0; + if(usb_loglevel > 1) + fprintf(stderr, "Performing bulk write of %u bytes...\n", filesize); + while(twb < filesize) + { + highres_gettime(&t1); + wb = filesize - twb; + if(wb > bulk_chunklen) + wb = bulk_chunklen; + wbr = 0; #if (!defined(_WIN32) && !defined(_WIN64) ) rc = libusb_bulk_transfer(h, endpoint, (void *)p, wb, &wbr, write_timeout); #else rc = usb_bulk_write(h, endpoint, (void *)p, wb, &wbr, write_timeout); #endif - if(rc || (wb != wbr)) - { - if(rc == LIBUSB_ERROR_NO_DEVICE) - break; - if(usb_loglevel) - fprintf(stderr, "bulk write: %s (%d bytes written, %d bytes to write)\n", libusb_strerror(rc), wbr, wb); - if(rc == LIBUSB_ERROR_TIMEOUT) - return USB_BOOT_TIMEOUT; - else return USB_BOOT_ERROR; - } - highres_gettime(&t2); - elapsedTime += highres_elapsed_ms(&t1, &t2); - if (elapsedTime > DEFAULT_SEND_FILE_TIMEOUT) { - return USB_BOOT_TIMEOUT; - } - twb += wbr; - p += wbr; - } - if(usb_loglevel > 1) - { - double MBpS = ((double)filesize / 1048576.) / (elapsedTime * 0.001); - fprintf(stderr, "Successfully sent %u bytes of data in %lf ms (%lf MB/s)\n", filesize, elapsedTime, MBpS); - } - return 0; + if(rc || (wb != wbr)) + { + if(rc == LIBUSB_ERROR_NO_DEVICE) + break; + if(usb_loglevel) + fprintf(stderr, "bulk write: %s (%d bytes written, %d bytes to write)\n", libusb_strerror(rc), wbr, wb); + if(rc == LIBUSB_ERROR_TIMEOUT) + return USB_BOOT_TIMEOUT; + else return USB_BOOT_ERROR; + } + highres_gettime(&t2); + elapsedTime += highres_elapsed_ms(&t1, &t2); + if (elapsedTime > DEFAULT_SEND_FILE_TIMEOUT) { + return USB_BOOT_TIMEOUT; + } + twb += wbr; + p += wbr; + } + if(usb_loglevel > 1) + { + double MBpS = ((double)filesize / 1048576.) / (elapsedTime * 0.001); + fprintf(stderr, "Successfully sent %u bytes of data in %lf ms (%lf MB/s)\n", filesize, elapsedTime, MBpS); + } + return 0; } int usb_boot(const char *addr, const void *mvcmd, unsigned size) @@ -685,9 +692,9 @@ int usb_boot(const char *addr, const void *mvcmd, unsigned size) return rc; } rc = send_file(h, endpoint, mvcmd, size,bcdusb); - libusb_release_interface(h, 0); - libusb_close(h); - libusb_unref_device(dev); + libusb_release_interface(h, 0); + libusb_close(h); + libusb_unref_device(dev); #endif return rc; } diff --git a/inference-engine/thirdparty/movidius/XLink/pc/usb_boot.h b/inference-engine/thirdparty/movidius/XLink/pc/usb_boot.h index 91fe89f42d3443..7deef99933afe7 100644 --- a/inference-engine/thirdparty/movidius/XLink/pc/usb_boot.h +++ b/inference-engine/thirdparty/movidius/XLink/pc/usb_boot.h @@ -18,6 +18,7 @@ extern int usb_loglevel; #define DEFAULT_UNBOOTVID 0x03E7 #define DEFAULT_UNBOOTPID_2485 0x2485 #define DEFAULT_UNBOOTPID_2150 0x2150 +#define DEFAULT_CHUNKSZ 1024*1024 typedef enum usbBootError { @@ -28,9 +29,11 @@ typedef enum usbBootError { } usbBootError_t; #if (!defined(_WIN32) && !defined(_WIN64)) -usbBootError_t usb_find_device_with_bcd(unsigned idx, char *addr, unsigned addrsize, void **device, int vid, int pid,unsigned short* bcdusb, int searchByName); +usbBootError_t usb_find_device_with_bcd(unsigned idx, char *input_addr, + unsigned addrsize, void **device, int vid, int pid,unsigned short* bcdusb); #else -usbBootError_t usb_find_device(unsigned idx, char *addr, unsigned addrsize, void **device, int vid, int pid, int specificDevice); +usbBootError_t usb_find_device(unsigned idx, char *addr, unsigned addrsize, + void **device, int vid, int pid); #endif int usb_boot(const char *addr, const void *mvcmd, unsigned size); int get_pid_by_name(const char* name); diff --git a/inference-engine/thirdparty/movidius/XLink/shared/XLink.c b/inference-engine/thirdparty/movidius/XLink/shared/XLink.c index 470b11dbe31863..5da79add2e4df4 100644 --- a/inference-engine/thirdparty/movidius/XLink/shared/XLink.c +++ b/inference-engine/thirdparty/movidius/XLink/shared/XLink.c @@ -9,6 +9,7 @@ /// #include "XLink.h" +#include "XLink_tool.h" #include "stdio.h" #include "stdint.h" @@ -17,59 +18,50 @@ #include #include + + #if (defined(_WIN32) || defined(_WIN64)) +#include "gettime.h" #include "win_pthread.h" #include "win_semaphore.h" -#include "gettime.h" #else -#include -#include -#endif -#if (defined(_WIN32) || defined(_WIN64)) -#include "gettime.h" +# ifdef __APPLE__ +# include "pthread_semaphore.h" +# else +# include +# endif #endif + #include "mvMacros.h" #include "XLinkPlatform.h" #include "XLinkDispatcher.h" +#include "XLinkPublicDefines.h" + #define _XLINK_ENABLE_PRIVATE_INCLUDE_ #include "XLinkPrivateDefines.h" +#ifdef MVLOG_UNIT_NAME +#undef MVLOG_UNIT_NAME #define MVLOG_UNIT_NAME xLink +#endif #include "mvLog.h" #include "mvStringUtils.h" -#define USB_DATA_TIMEOUT 10000 -#define CIRCULAR_INCREMENT(x,maxVal) \ - { \ - x++; \ - if (x == maxVal) \ - x = 0; \ - } -//avoid problems with unsigned. first compare and then give the nuw value -#define CIRCULAR_DECREMENT(x,maxVal) \ -{ \ - if (x == 0) \ - x = maxVal; \ - else \ - x--; \ -} -#define EXTRACT_IDS(streamId, linkId) \ -{ \ - linkId = (streamId >> 24) & 0XFF; \ - streamId = streamId & 0xFFFFFF; \ -} +#ifndef XLINK_USB_DATA_TIMEOUT +#define XLINK_USB_DATA_TIMEOUT 0 +#endif -#define COMBIN_IDS(streamId, linkid) \ - streamId = streamId | ((linkid & 0xFF) << 24); +#ifndef XLINK_COMMON_TIMEOUT_MSEC +#define XLINK_COMMON_TIMEOUT_MSEC (1*60*1000) +#endif #define DEFAULT_TIMEOUT ((unsigned int)-1) #define MAX_PATH_LENGTH (255) -static unsigned int glCommonTimeOutMsec = 1000; +static unsigned int glCommonTimeOutMsec = XLINK_COMMON_TIMEOUT_MSEC; static unsigned int glDeviceOpenTimeOutMsec = 5000; static unsigned int glAllocateGraphTimeOutMsec = 12000; - XLinkError_t XLinkSetCommonTimeOutMsec(unsigned int msec) { glCommonTimeOutMsec = msec; return X_LINK_SUCCESS; @@ -87,6 +79,7 @@ XLinkError_t XLinkSetAllocateGraphTimeOutMsec(unsigned int msec) { int XLinkWaitSem(sem_t* sem) { +#ifdef __PC__ ASSERT_X_LINK_R(sem != NULL, -1); if (glCommonTimeOutMsec == 0) @@ -109,6 +102,9 @@ int XLinkWaitSem(sem_t* sem) return sem_timedwait(sem, &ts); } +#else + return sem_wait(sem); +#endif } int XLinkWaitSemUserMode(sem_t* sem, unsigned int timeout) @@ -141,6 +137,10 @@ int XLinkWaitSemUserMode(sem_t* sem, unsigned int timeout) } } +static int is_semaphore_initialized(const streamDesc_t *stream) { + return stream && strnlen(stream->name, MAX_STREAM_NAME_LENGTH) != 0; +} + int dispatcherLocalEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response); int dispatcherRemoteEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response); //adds a new event with parameters and returns event id @@ -148,18 +148,24 @@ int dispatcherEventSend(xLinkEvent_t* event); streamDesc_t* getStreamById(void* fd, streamId_t id); void releaseStream(streamDesc_t*); int addNewPacketToStream(streamDesc_t* stream, void* buffer, uint32_t size); +xLinkDesc_t* getLink(void* fd); +#ifdef __PC__ static XLinkError_t checkEventHeader(xLinkEventHeader_t header); +#endif struct dispatcherControlFunctions controlFunctionTbl; XLinkGlobalHandler_t* glHandler; //TODO need to either protect this with semaphor - // or make profiling data per device + //or make profiling data per device linkId_t nextUniqueLinkId = 0; //incremental number, doesn't get decremented. xLinkDesc_t availableXLinks[MAX_LINKS]; +xLinkDesc_t* getLinkById(linkId_t id); +xLinkDesc_t* getLink(void* fd); sem_t pingSem; //to b used by myriad + char* TypeToStr(int type) { switch(type) @@ -181,7 +187,7 @@ char* TypeToStr(int type) case XLINK_RESET_RESP: return "XLINK_RESET_RESP"; case XLINK_RESP_LAST: return "XLINK_RESP_LAST"; default: - break; + break; } return ""; } @@ -246,67 +252,69 @@ int handleIncomingEvent(xLinkEvent_t* event){ mvLog(MVLOG_DEBUG, "%s, size %u, streamId %u.\n", TypeToStr(event->header.type), event->header.size, event->header.streamId); void* buffer ; streamDesc_t* stream ; + int sc = 0 ; switch (event->header.type){ - case XLINK_WRITE_REQ: - /*If we got here, we will read the data no matter what happens. - If we encounter any problems we will still read the data to keep - the communication working but send a NACK.*/ - stream = getStreamById(event->deviceHandle.xLinkFD, event->header.streamId); - ASSERT_X_LINK(stream); - - stream->localFillLevel += event->header.size; - mvLog(MVLOG_DEBUG,"Got write, current local fill level is %u out of %u %u\n", stream->localFillLevel, stream->readSize, stream->writeSize); - - buffer = allocateData(ALIGN_UP(event->header.size, __CACHE_LINE_SIZE), __CACHE_LINE_SIZE); - if (buffer == NULL){ - mvLog(MVLOG_FATAL,"out of memory\n"); - ASSERT_X_LINK(0); - } - int sc = XLinkRead(&event->deviceHandle, buffer, event->header.size, USB_DATA_TIMEOUT); - if(sc < 0){ - mvLog(MVLOG_ERROR,"%s() Read failed (err %d)\n", __func__, (int)sc); - deallocateData(buffer, ALIGN_UP(event->header.size, __CACHE_LINE_SIZE), __CACHE_LINE_SIZE); - ASSERT_X_LINK(0); - } + case XLINK_WRITE_REQ: + /*If we got here, we will read the data no matter what happens. + If we encounter any problems we will still read the data to keep + the communication working but send a NACK.*/ + stream = getStreamById(event->deviceHandle.xLinkFD, event->header.streamId); + ASSERT_X_LINK(stream); - event->data = buffer; - if (addNewPacketToStream(stream, buffer, event->header.size)){ - mvLog(MVLOG_WARN,"No more place in stream. release packet\n"); - deallocateData(buffer, ALIGN_UP(event->header.size, __CACHE_LINE_SIZE), __CACHE_LINE_SIZE); - event->header.flags.bitField.ack = 0; - event->header.flags.bitField.nack = 1; - assert(0); - } - releaseStream(stream); - break; - case XLINK_READ_REQ: - break; - case XLINK_READ_REL_REQ: - break; - case XLINK_CREATE_STREAM_REQ: - break; - case XLINK_CLOSE_STREAM_REQ: - break; - case XLINK_PING_REQ: - break; - case XLINK_RESET_REQ: - break; - case XLINK_WRITE_RESP: - break; - case XLINK_READ_RESP: - break; - case XLINK_READ_REL_RESP: - break; - case XLINK_CREATE_STREAM_RESP: - break; - case XLINK_CLOSE_STREAM_RESP: - break; - case XLINK_PING_RESP: - break; - case XLINK_RESET_RESP: - break; - default: - ASSERT_X_LINK(0); + stream->localFillLevel += event->header.size; + mvLog(MVLOG_DEBUG,"S%d: Got write of %ld, current local fill level is %ld out of %ld %ld\n", + event->header.streamId, event->header.size, stream->localFillLevel, stream->readSize, stream->writeSize); + + buffer = allocateData(ALIGN_UP(event->header.size, __CACHE_LINE_SIZE), __CACHE_LINE_SIZE); + if (buffer == NULL){ + mvLog(MVLOG_FATAL,"out of memory\n"); + ASSERT_X_LINK(0); + } + sc = XLinkRead(&event->deviceHandle, buffer, event->header.size, XLINK_USB_DATA_TIMEOUT); + if(sc < 0){ + mvLog(MVLOG_ERROR,"%s() Read failed %d\n", __func__, (int)sc); + deallocateData(buffer, ALIGN_UP(event->header.size, __CACHE_LINE_SIZE), __CACHE_LINE_SIZE); + ASSERT_X_LINK(0); + } + + event->data = buffer; + if (addNewPacketToStream(stream, buffer, event->header.size)){ + mvLog(MVLOG_WARN,"No more place in stream. release packet\n"); + deallocateData(buffer, ALIGN_UP(event->header.size, __CACHE_LINE_SIZE), __CACHE_LINE_SIZE); + event->header.flags.bitField.ack = 0; + event->header.flags.bitField.nack = 1; + assert(0); + } + releaseStream(stream); + break; + case XLINK_READ_REQ: + break; + case XLINK_READ_REL_REQ: + break; + case XLINK_CREATE_STREAM_REQ: + break; + case XLINK_CLOSE_STREAM_REQ: + break; + case XLINK_PING_REQ: + break; + case XLINK_RESET_REQ: + break; + case XLINK_WRITE_RESP: + break; + case XLINK_READ_RESP: + break; + case XLINK_READ_REL_RESP: + break; + case XLINK_CREATE_STREAM_RESP: + break; + case XLINK_CLOSE_STREAM_RESP: + break; + case XLINK_PING_RESP: + break; + case XLINK_RESET_RESP: + break; + default: + ASSERT_X_LINK(0); } //adding event for the scheduler. We let it know that this is a remote event dispatcherAddEvent(EVENT_REMOTE, event); @@ -315,31 +323,41 @@ int handleIncomingEvent(xLinkEvent_t* event){ int dispatcherEventReceive(xLinkEvent_t* event){ static xLinkEvent_t prevEvent = {0}; - - const unsigned int unlimitedUsbTimeout = 0; - int sc = XLinkRead(&event->deviceHandle, &event->header, sizeof(event->header), unlimitedUsbTimeout); +#ifdef __PC__ + int sc = XLinkRead(&event->deviceHandle, &event->header, sizeof(event->header), 0); +#else + int sc = XLinkRead(&event->deviceHandle, &event->header, sizeof(event->header), XLINK_USB_DATA_TIMEOUT); +#endif mvLog(MVLOG_DEBUG,"Incoming event %p: %s %d %p prevEvent: %s %d %p\n", - event, - TypeToStr(event->header.type), - (int)event->header.id, - event->deviceHandle.xLinkFD, - TypeToStr(prevEvent.header.type), - (int)prevEvent.header.id, - prevEvent.deviceHandle.xLinkFD); - - if(sc < 0 && event->header.type == XLINK_RESET_RESP) { - return sc; + event, + TypeToStr(event->header.type), + (int)event->header.id, + event->deviceHandle.xLinkFD, + TypeToStr(prevEvent.header.type), + (int)prevEvent.header.id, + prevEvent.deviceHandle.xLinkFD); + + + if(sc < 0) { + xLinkDesc_t* link = getLink(&event->deviceHandle.xLinkFD); + if (event->header.type == XLINK_RESET_RESP || link == NULL) { + return sc; + } else if (link->hostClosedFD) { + //host intentionally closed usb, finish normally + event->header.type = XLINK_RESET_RESP; + return 0; + } } - if(sc < 0){ - mvLog(MVLOG_ERROR,"%s() Read failed (err %d) | event %p %s\n", __func__, (int)sc, event, TypeToStr(event->header.type)); + if(sc < 0) { + mvLog(MVLOG_ERROR,"%s() Read failed %d\n", __func__, (int)sc); return sc; } if (prevEvent.header.id == event->header.id && - prevEvent.header.type == event->header.type && - prevEvent.deviceHandle.xLinkFD == event->deviceHandle.xLinkFD) + prevEvent.header.type == event->header.type && + prevEvent.deviceHandle.xLinkFD == event->deviceHandle.xLinkFD) { mvLog(MVLOG_FATAL,"Duplicate id detected. \n"); } @@ -349,7 +367,8 @@ int dispatcherEventReceive(xLinkEvent_t* event){ mvLog(MVLOG_WARN,"Failed to handle incoming event"); } - if(event->header.type == XLINK_RESET_REQ ) { + if(event->header.type == XLINK_RESET_REQ) + { if(event->deviceHandle.protocol == X_LINK_PCIE) { mvLog(MVLOG_DEBUG,"XLINK_RESET_REQ received - doing nothing, we dont want to reset device"); } @@ -393,11 +412,6 @@ static linkId_t getNextAvailableLinkUniqueId() do { int i; - nextUniqueLinkId++; - if (nextUniqueLinkId == INVALID_LINK_ID) - { - nextUniqueLinkId = 0; - } for (i = 0; i < MAX_LINKS; i++) { if (availableXLinks[i].id != INVALID_LINK_ID && @@ -408,12 +422,17 @@ static linkId_t getNextAvailableLinkUniqueId() { return nextUniqueLinkId; } + nextUniqueLinkId++; + if (nextUniqueLinkId == INVALID_LINK_ID) + { + nextUniqueLinkId = 0; + } } while (start != nextUniqueLinkId); mvLog(MVLOG_ERROR, "%s():- no next available link!\n", __func__); return INVALID_LINK_ID; } -static int getNextAvailableLinkIndex() +int getNextAvailableLinkIndex() { int i; for (i = 0; i < MAX_LINKS; i++) @@ -446,8 +465,11 @@ streamDesc_t* getStreamById(void* fd, streamId_t id) int stream; for (stream = 0; stream < XLINK_MAX_STREAMS; stream++) { if (link->availableStreams[stream].id == id) { - if (XLinkWaitSem(&link->availableStreams[stream].sem)) + if (XLinkWaitSem(&link->availableStreams[stream].sem)) { +#ifdef __PC__ return NULL; +#endif + } return &link->availableStreams[stream]; } } @@ -461,9 +483,12 @@ streamDesc_t* getStreamByName(xLinkDesc_t* link, const char* name) for (stream = 0; stream < XLINK_MAX_STREAMS; stream++) { if (link->availableStreams[stream].id != INVALID_STREAM_ID && strcmp(link->availableStreams[stream].name, name) == 0) { - if (XLinkWaitSem(&link->availableStreams[stream].sem)) - return NULL; - return &link->availableStreams[stream]; + if (XLinkWaitSem(&link->availableStreams[stream].sem)) { +#ifdef __PC__ + return NULL; +#endif + } + return &link->availableStreams[stream]; } } return NULL; @@ -500,7 +525,7 @@ streamPacketDesc_t* getPacketFromStream(streamDesc_t* stream) ret = &stream->packets[stream->firstPacketUnused]; stream->availablePackets--; CIRCULAR_INCREMENT(stream->firstPacketUnused, - XLINK_MAX_PACKETS_PER_STREAM); + XLINK_MAX_PACKETS_PER_STREAM); stream->blockedPackets++; } return ret; @@ -515,10 +540,16 @@ void deallocateStream(streamDesc_t* stream) stream->readSize = 0; stream->closeStreamInitiated = 0; } + +#ifndef __PC__ + if (is_semaphore_initialized(stream)) { + if(sem_destroy(&stream->sem)) + perror("Can't destroy semaphore"); + } +#endif } } - int releasePacketFromStream(streamDesc_t* stream, uint32_t* releasedSize) { streamPacketDesc_t* currPack = &stream->packets[stream->firstPacket]; @@ -526,22 +557,28 @@ int releasePacketFromStream(streamDesc_t* stream, uint32_t* releasedSize) mvLog(MVLOG_ERROR,"There is no packet to release\n"); return 0; // ignore this, although this is a big problem on application side } + stream->localFillLevel -= currPack->length; - mvLog(MVLOG_DEBUG,"Got release, current local fill level is %u out of %u %u\n", stream->localFillLevel, stream->readSize, stream->writeSize); + mvLog(MVLOG_DEBUG, "S%d: Got release of %ld , current local fill level is %ld out of %ld %ld\n", + stream->id, currPack->length, stream->localFillLevel, stream->readSize, stream->writeSize); + + deallocateData(currPack->data, + ALIGN_UP_INT32((int32_t)currPack->length, __CACHE_LINE_SIZE), __CACHE_LINE_SIZE); - deallocateData(currPack->data, ALIGN_UP_INT32((int32_t)currPack->length, __CACHE_LINE_SIZE), __CACHE_LINE_SIZE); CIRCULAR_INCREMENT(stream->firstPacket, XLINK_MAX_PACKETS_PER_STREAM); stream->blockedPackets--; - *releasedSize = currPack->length; + if (releasedSize) { + *releasedSize = currPack->length; + } return 0; } int isStreamSpaceEnoughFor(streamDesc_t* stream, uint32_t size) { if(stream->remoteFillPacketLevel >= XLINK_MAX_PACKETS_PER_STREAM || - stream->remoteFillLevel + size > stream->writeSize){ - mvLog(MVLOG_DEBUG, "S%d: Not enough space in stream for %u: PKT %u, FILL %u SIZE %u\n", - stream->id, size, stream->remoteFillPacketLevel, stream->remoteFillLevel, stream->writeSize); + stream->remoteFillLevel + size > stream->writeSize){ + mvLog(MVLOG_DEBUG, "S%d: Not enough space in stream '%s' for %ld: PKT %ld, FILL %ld SIZE %ld\n", + stream->id, stream->name, size, stream->remoteFillPacketLevel, stream->remoteFillLevel, stream->writeSize); return 0; } else @@ -561,10 +598,10 @@ int addNewPacketToStream(streamDesc_t* stream, void* buffer, uint32_t size){ } streamId_t allocateNewStream(void* fd, - const char* name, - uint32_t writeSize, - uint32_t readSize, - streamId_t forcedId) + const char* name, + uint32_t writeSize, + uint32_t readSize, + streamId_t forcedId) { streamId_t streamId; streamDesc_t* stream; @@ -597,9 +634,18 @@ streamId_t allocateNewStream(void* fd, else stream->id = forcedId; link->nextUniqueStreamId++; //even if we didnt use a new one, we need to align with total number of unique streams - int sem_initiated = strlen(stream->name) != 0; + if (!is_semaphore_initialized(stream)) //if sem_init is called for already initiated sem, behavior is undefined + { + if(sem_init(&stream->sem, 0, 0)) + perror("Can't create semaphore\n"); + } + else + { + mvLog(MVLOG_INFO, "is_semaphore_initialized\n"); + } + mv_strncpy(stream->name, MAX_STREAM_NAME_LENGTH, - name, MAX_STREAM_NAME_LENGTH - 1); + name, MAX_STREAM_NAME_LENGTH - 1); stream->readSize = 0; stream->writeSize = 0; stream->remoteFillLevel = 0; @@ -607,28 +653,44 @@ streamId_t allocateNewStream(void* fd, stream->localFillLevel = 0; stream->closeStreamInitiated = 0; - if (!sem_initiated) //if sem_init is called for already initiated sem, behavior is undefined - sem_init(&stream->sem, 0, 0); } if (readSize && !stream->readSize) { stream->readSize = readSize; + +#ifndef __PC__ + // FIXME: not the best solution but the simplest for now: + // it is just for a check; real allocation will be done during receiving an usb package + void *buffer = allocateData(ALIGN_UP(readSize, __CACHE_LINE_SIZE), __CACHE_LINE_SIZE); + if (buffer == NULL) { + mvLog(MVLOG_ERROR,"Cannot create stream. Requested memory = %u", stream->readSize); + return INVALID_STREAM_ID; + } else { + deallocateData(buffer, ALIGN_UP(readSize, __CACHE_LINE_SIZE), __CACHE_LINE_SIZE); + } +#endif } if (writeSize && !stream->writeSize) { stream->writeSize = writeSize; } + + mvLog(MVLOG_DEBUG, "The stream \"%s\" created, id = %u, readSize = %d, writeSize = %d\n", + stream->name, stream->id, stream->readSize, stream->writeSize); + streamId = stream->id; releaseStream(stream); return streamId; } +#ifdef __PC__ static void setEventFailed(xLinkEvent_t * event ) { event->header.flags.bitField.localServe = 1; event->header.flags.bitField.ack = 0; event->header.flags.bitField.nack = 1; } +#endif //this function should be called only for remote requests int dispatcherLocalEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response) @@ -637,101 +699,114 @@ int dispatcherLocalEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response) response->header.id = event->header.id; mvLog(MVLOG_DEBUG, "%s\n",TypeToStr(event->header.type)); switch (event->header.type){ - case XLINK_WRITE_REQ: - //in case local tries to write after it issues close (writeSize is zero) - stream = getStreamById(event->deviceHandle.xLinkFD, event->header.streamId); - if(!stream){ - mvLog(MVLOG_DEBUG, "stream %d has been closed!\n", event->header.streamId); - setEventFailed(event); - break; - } - if (stream->writeSize == 0) - { - event->header.flags.bitField.nack = 1; - event->header.flags.bitField.ack = 0; - // return -1 to don't even send it to the remote - releaseStream(stream); - return -1; - } - event->header.flags.bitField.ack = 1; - event->header.flags.bitField.nack = 0; - event->header.flags.bitField.localServe = 0; + case XLINK_WRITE_REQ: + //in case local tries to write after it issues close (writeSize is zero) + stream = getStreamById(event->deviceHandle.xLinkFD, event->header.streamId); - if(!isStreamSpaceEnoughFor(stream, event->header.size)){ - mvLog(MVLOG_DEBUG,"local NACK RTS. stream is full\n"); - event->header.flags.bitField.block = 1; - event->header.flags.bitField.localServe = 1; - }else{ - event->header.flags.bitField.block = 0; - stream->remoteFillLevel += event->header.size; - stream->remoteFillPacketLevel++; +#ifdef __PC__ + if(!stream){ + mvLog(MVLOG_DEBUG, "stream %d has been closed!\n", event->header.streamId); + setEventFailed(event); + break; + } +#else + ASSERT_X_LINK(stream); +#endif - mvLog(MVLOG_DEBUG,"Got local write remote fill level %u out of %u %u\n", stream->remoteFillLevel, stream->writeSize, stream->readSize); - } - releaseStream(stream); - break; - case XLINK_READ_REQ: - stream = getStreamById(event->deviceHandle.xLinkFD, event->header.streamId); - if(!stream){ - mvLog(MVLOG_DEBUG, "stream %d has been closed!\n", event->header.streamId); - setEventFailed(event); - break; - } - streamPacketDesc_t* packet = getPacketFromStream(stream); - if (packet){ - //the read can be served with this packet - event->data = packet; + if (stream->writeSize == 0) + { + event->header.flags.bitField.nack = 1; + event->header.flags.bitField.ack = 0; + // return -1 to don't even send it to the remote + releaseStream(stream); + return -1; + } event->header.flags.bitField.ack = 1; event->header.flags.bitField.nack = 0; - event->header.flags.bitField.block = 0; - } - else{ - event->header.flags.bitField.block = 1; - } - releaseStream(stream); - event->header.flags.bitField.localServe = 1; - break; - case XLINK_READ_REL_REQ: - stream = getStreamById(event->deviceHandle.xLinkFD, event->header.streamId); - ASSERT_X_LINK(stream); - uint32_t releasedSize = 0; - releasePacketFromStream(stream, &releasedSize); - event->header.size = releasedSize; - releaseStream(stream); - break; - case XLINK_CREATE_STREAM_REQ: - break; - case XLINK_CLOSE_STREAM_REQ: - stream = getStreamById(event->deviceHandle.xLinkFD, event->header.streamId); - - ASSERT_X_LINK(stream); - if (stream->remoteFillLevel != 0){ - stream->closeStreamInitiated = 1; - event->header.flags.bitField.block = 1; - event->header.flags.bitField.localServe = 1; - }else{ - event->header.flags.bitField.block = 0; event->header.flags.bitField.localServe = 0; - } - releaseStream(stream); - break; - case XLINK_RESET_REQ: - mvLog(MVLOG_DEBUG,"XLINK_RESET_REQ - do nothing\n"); - break; - case XLINK_PING_REQ: - case XLINK_WRITE_RESP: - case XLINK_READ_RESP: - case XLINK_READ_REL_RESP: - case XLINK_CREATE_STREAM_RESP: - case XLINK_CLOSE_STREAM_RESP: - case XLINK_PING_RESP: - break; - case XLINK_RESET_RESP: - //should not happen - event->header.flags.bitField.localServe = 1; - break; - default: - ASSERT_X_LINK(0); + + if(!isStreamSpaceEnoughFor(stream, event->header.size)){ + mvLog(MVLOG_FATAL,"local NACK RTS. stream '%s' is full (event %d)\n", stream->name, event->header.id); + event->header.flags.bitField.block = 1; + event->header.flags.bitField.localServe = 1; + // TODO: easy to implement non-blocking read here, just return nack + mvLog(MVLOG_WARN, "Blocked event would cause dispatching thread to wait on semaphore infinitely\n"); + }else{ + event->header.flags.bitField.block = 0; + stream->remoteFillLevel += event->header.size; + stream->remoteFillPacketLevel++; + mvLog(MVLOG_DEBUG,"S%d: Got local write of %ld , remote fill level %ld out of %ld %ld\n", + event->header.streamId, event->header.size, stream->remoteFillLevel, stream->writeSize, stream->readSize); + } + releaseStream(stream); + break; + case XLINK_READ_REQ: + stream = getStreamById(event->deviceHandle.xLinkFD, event->header.streamId); +#ifdef __PC__ + if(!stream){ + mvLog(MVLOG_DEBUG, "stream %d has been closed!\n", event->header.streamId); + setEventFailed(event); + break; + } +#else + ASSERT_X_LINK(stream); +#endif + streamPacketDesc_t* packet = getPacketFromStream(stream); + if (packet){ + //the read can be served with this packet + event->data = packet; + event->header.flags.bitField.ack = 1; + event->header.flags.bitField.nack = 0; + event->header.flags.bitField.block = 0; + } + else{ + event->header.flags.bitField.block = 1; + // TODO: easy to implement non-blocking read here, just return nack + } + event->header.flags.bitField.localServe = 1; + releaseStream(stream); + break; + case XLINK_READ_REL_REQ: + stream = getStreamById(event->deviceHandle.xLinkFD, event->header.streamId); + ASSERT_X_LINK(stream); + uint32_t releasedSize = 0; + releasePacketFromStream(stream, &releasedSize); + event->header.size = releasedSize; + releaseStream(stream); + break; + case XLINK_CREATE_STREAM_REQ: + break; + case XLINK_CLOSE_STREAM_REQ: + stream = getStreamById(event->deviceHandle.xLinkFD, event->header.streamId); + + ASSERT_X_LINK(stream); + if (stream->remoteFillLevel != 0){ + stream->closeStreamInitiated = 1; + event->header.flags.bitField.block = 1; + event->header.flags.bitField.localServe = 1; + }else{ + event->header.flags.bitField.block = 0; + event->header.flags.bitField.localServe = 0; + } + releaseStream(stream); + break; + case XLINK_RESET_REQ: + mvLog(MVLOG_DEBUG,"XLINK_RESET_REQ - do nothing\n"); + break; + case XLINK_PING_REQ: + case XLINK_WRITE_RESP: + case XLINK_READ_RESP: + case XLINK_READ_REL_RESP: + case XLINK_CREATE_STREAM_RESP: + case XLINK_CLOSE_STREAM_RESP: + case XLINK_PING_RESP: + break; + case XLINK_RESET_RESP: + //should not happen + event->header.flags.bitField.localServe = 1; + break; + default: + ASSERT_X_LINK(0); } return 0; } @@ -776,12 +851,12 @@ int dispatcherRemoteEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response stream->remoteFillLevel -= event->header.size; stream->remoteFillPacketLevel--; - mvLog(MVLOG_DEBUG,"Got remote release %u, remote fill level %u out of %u %u\n", - event->header.size, stream->remoteFillLevel, stream->writeSize, stream->readSize); + mvLog(MVLOG_DEBUG,"S%d: Got remote release of %ld, remote fill level %ld out of %ld %ld\n", + event->header.streamId, event->header.size, stream->remoteFillLevel, stream->writeSize, stream->readSize); releaseStream(stream); dispatcherUnblockEvent(-1, XLINK_WRITE_REQ, event->header.streamId, - event->deviceHandle.xLinkFD); + event->deviceHandle.xLinkFD); //with every released packet check if the stream is already marked for close if (stream->closeStreamInitiated && stream->localFillLevel == 0) { @@ -801,48 +876,56 @@ int dispatcherRemoteEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response event->header.streamName, 0, event->header.size, INVALID_STREAM_ID); + + if (response->header.streamId == INVALID_STREAM_ID) { + response->header.flags.bitField.ack = 0; + response->header.flags.bitField.sizeTooBig = 1; + break; + } + response->deviceHandle = event->deviceHandle; mv_strncpy(response->header.streamName, MAX_STREAM_NAME_LENGTH, - event->header.streamName, MAX_STREAM_NAME_LENGTH - 1); + event->header.streamName, MAX_STREAM_NAME_LENGTH - 1); response->header.size = event->header.size; mvLog(MVLOG_DEBUG,"creating stream %x\n", (int)response->header.streamId); break; case XLINK_CLOSE_STREAM_REQ: - { - response->header.type = XLINK_CLOSE_STREAM_RESP; - response->header.streamId = event->header.streamId; - response->deviceHandle = event->deviceHandle; - - streamDesc_t* stream = getStreamById(event->deviceHandle.xLinkFD, - event->header.streamId); - if (!stream) { - //if we have sent a NACK before, when the event gets unblocked - //the stream might already be unavailable - response->header.flags.bitField.ack = 1; //All is good, we are done + { + response->header.type = XLINK_CLOSE_STREAM_RESP; + response->header.streamId = event->header.streamId; + response->deviceHandle = event->deviceHandle; + + streamDesc_t* stream = getStreamById(event->deviceHandle.xLinkFD, + event->header.streamId); + if (!stream) { + //if we have sent a NACK before, when the event gets unblocked + //the stream might already be unavailable + response->header.flags.bitField.ack = 1; //All is good, we are done + response->header.flags.bitField.nack = 0; + mvLog(MVLOG_DEBUG,"%s() got a close stream on aready closed stream\n", __func__); + } else { + if (stream->localFillLevel == 0) + { + response->header.flags.bitField.ack = 1; response->header.flags.bitField.nack = 0; - mvLog(MVLOG_DEBUG,"%s() got a close stream on aready closed stream\n", __func__); - } else { - if (stream->localFillLevel == 0) - { - response->header.flags.bitField.ack = 1; - response->header.flags.bitField.nack = 0; - - deallocateStream(stream); - if (!stream->writeSize) { - stream->id = INVALID_STREAM_ID; - } - } - else - { - mvLog(MVLOG_DEBUG,"%s():fifo is NOT empty returning NACK \n", __func__); - response->header.flags.bitField.nack = 1; - stream->closeStreamInitiated = 1; - } - releaseStream(stream); + deallocateStream(stream); + if (!stream->writeSize) { + stream->id = INVALID_STREAM_ID; + stream->name[0] = '\0'; + } } - break; + else + { + mvLog(MVLOG_DEBUG,"%s():fifo is NOT empty returning NACK \n", __func__); + response->header.flags.bitField.nack = 1; + stream->closeStreamInitiated = 1; + } + + releaseStream(stream); } + break; + } case XLINK_PING_REQ: response->header.type = XLINK_PING_RESP; response->header.flags.bitField.ack = 1; @@ -850,7 +933,7 @@ int dispatcherRemoteEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response sem_post(&pingSem); break; case XLINK_RESET_REQ: - mvLog(MVLOG_DEBUG,"reset request\n"); + mvLog(MVLOG_DEBUG,"reset request - received! Sending ACK *****\n"); response->header.flags.bitField.ack = 1; response->header.flags.bitField.nack = 0; response->header.type = XLINK_RESET_RESP; @@ -870,6 +953,9 @@ int dispatcherRemoteEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response event->header.streamName, event->header.size,0, event->header.streamId); +#ifndef __PC__ + ASSERT_X_LINK_R(response->header.streamId != INVALID_STREAM_ID, X_LINK_ERROR); +#endif response->deviceHandle = event->deviceHandle; break; } @@ -888,6 +974,7 @@ int dispatcherRemoteEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response response->header.flags.bitField.nack = 1; response->header.flags.bitField.ack = 0; stream->id = INVALID_STREAM_ID; + stream->name[0] = '\0'; break; } releaseStream(stream); @@ -902,12 +989,16 @@ int dispatcherRemoteEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response } return 0; } - //adds a new event with parameters and returns event id int dispatcherEventSend(xLinkEvent_t *event) { mvLog(MVLOG_DEBUG, "%s, size %d, streamId %d.\n", TypeToStr(event->header.type), event->header.size, event->header.streamId); - int rc = XLinkWrite(&event->deviceHandle, &event->header, sizeof(event->header), USB_DATA_TIMEOUT); +#ifdef __PC__ + int rc = XLinkWrite(&event->deviceHandle, &event->header, sizeof(event->header), XLINK_USB_DATA_TIMEOUT); +#else + int rc = XLinkWrite(&event->deviceHandle, &event->header, sizeof(event->header), 0); +#endif + if(rc < 0) { mvLog(MVLOG_ERROR,"Write failed (header) (err %d) | event %s\n", rc, TypeToStr(event->header.type)); @@ -917,10 +1008,12 @@ int dispatcherEventSend(xLinkEvent_t *event) { //write requested data rc = XLinkWrite(&event->deviceHandle, event->data, - event->header.size, USB_DATA_TIMEOUT); - + event->header.size, XLINK_USB_DATA_TIMEOUT); if(rc < 0) { - mvLog(MVLOG_ERROR,"Write failed (event) (err %d)\n", rc); + mvLog(MVLOG_ERROR,"Write failed %d\n", rc); +#ifndef __PC__ + return rc; +#endif } } // this function will send events to the remote node @@ -934,7 +1027,7 @@ static xLinkState_t getXLinkState(xLinkDesc_t* link) return link->peerState; } -void dispatcherCloseLink(void*fd) +void dispatcherCloseLink(void* fd, int fullClose) { xLinkDesc_t* link = getLink(fd); @@ -943,30 +1036,36 @@ void dispatcherCloseLink(void*fd) return; } + if (!fullClose) { + link->peerState = XLINK_DOWN; + return; + } + +#ifndef __PC__ + link->peerState = X_LINK_COMMUNICATION_NOT_OPEN; +#else link->peerState = XLINK_NOT_INIT; +#endif + link->id = INVALID_LINK_ID; link->deviceHandle.xLinkFD = NULL; link->nextUniqueStreamId = 0; - int index; - uint32_t release_size = 0; - streamDesc_t* stream; - for (index = 0; index < XLINK_MAX_STREAMS; index++) - { - stream = &link->availableStreams[index]; - while (NULL != getPacketFromStream(stream)) - { - releasePacketFromStream(stream, &release_size); + for (int index = 0; index < XLINK_MAX_STREAMS; index++) { + streamDesc_t* stream = &link->availableStreams[index]; + if (!stream) { + continue; } - while (stream->blockedPackets != 0) - { - releasePacketFromStream(stream, &release_size); + + while (getPacketFromStream(stream) || stream->blockedPackets) { + releasePacketFromStream(stream, NULL); } - if (stream->name[0] != '\0') - { - sem_destroy(&stream->sem); // ignore the error for some unused semaphore + + if (is_semaphore_initialized(stream)) { + sem_destroy(&stream->sem); stream->name[0] = '\0'; } + stream->id = INVALID_STREAM_ID; } } @@ -976,10 +1075,10 @@ void dispatcherCloseDeviceFd(xLinkDeviceHandle_t* deviceHandle) XLinkPlatformCloseRemote(deviceHandle); } - /*################################################################################# ###################################### EXTERNAL ################################### ##################################################################################*/ + //Called only from app - per device XLinkError_t XLinkConnect(XLinkHandler_t* handler) { @@ -997,7 +1096,7 @@ XLinkError_t XLinkConnect(XLinkHandler_t* handler) link->deviceHandle.protocol = handler->protocol; if (XLinkPlatformConnect(handler->devicePath2, handler->devicePath, - link->deviceHandle.protocol, &link->deviceHandle.xLinkFD) < 0) { + link->deviceHandle.protocol, &link->deviceHandle.xLinkFD) < 0) { return X_LINK_ERROR; } @@ -1005,6 +1104,7 @@ XLinkError_t XLinkConnect(XLinkHandler_t* handler) return X_LINK_TIMEOUT; xLinkEvent_t event = {0}; + event.header.type = XLINK_PING_REQ; event.deviceHandle = link->deviceHandle; dispatcherAddEvent(EVENT_LOCAL, &event); @@ -1016,21 +1116,40 @@ XLinkError_t XLinkConnect(XLinkHandler_t* handler) link->id = getNextAvailableLinkUniqueId(); link->peerState = XLINK_UP; + link->hostClosedFD = 0; handler->linkId = link->id; - return X_LINK_SUCCESS; } XLinkError_t XLinkInitialize(XLinkGlobalHandler_t* handler) { +#ifndef __PC__ + mvLogLevelSet(MVLOG_FATAL); + mvLogDefaultLevelSet(MVLOG_FATAL); +#endif + + ASSERT_X_LINK(handler); ASSERT_X_LINK(XLINK_MAX_STREAMS <= MAX_POOLS_ALLOC); glHandler = handler; - sem_init(&pingSem,0,0); + if (sem_init(&pingSem,0,0)) { + mvLog(MVLOG_ERROR, "Can't create semaphore\n"); + } int i; XLinkPlatformInit(); + + //Using deprecated fields. Begin. + int loglevel = handler->loglevel; + int protocol = handler->protocol; + //Using deprecated fields. End. + memset((void*)handler, 0, sizeof(XLinkGlobalHandler_t)); + //Using deprecated fields. Begin. + handler->loglevel = loglevel; + handler->protocol = protocol; + //Using deprecated fields. End. + //initialize availableStreams xLinkDesc_t* link; for (i = 0; i < MAX_LINKS; i++) { @@ -1051,12 +1170,27 @@ XLinkError_t XLinkInitialize(XLinkGlobalHandler_t* handler) controlFunctionTbl.closeDeviceFd = &dispatcherCloseDeviceFd; if (dispatcherInitialize(&controlFunctionTbl)) + { +#ifdef __PC__ return X_LINK_TIMEOUT; +#endif + } + +#ifndef __PC__ + int index = getNextAvailableLinkIndex(); + if (index == -1) + return X_LINK_COMMUNICATION_NOT_OPEN; + link = &availableXLinks[index]; + link->deviceHandle.xLinkFD = NULL; + link->id = nextUniqueLinkId++; + link->peerState = XLINK_UP; + + sem_wait(&pingSem); +#endif return X_LINK_SUCCESS; } - XLinkError_t XLinkGetFillLevel(streamId_t streamId, int isRemote, int* fillLevel) { linkId_t id; @@ -1082,6 +1216,11 @@ XLinkError_t XLinkGetFillLevel(streamId_t streamId, int isRemote, int* fillLevel streamId_t XLinkOpenStream(linkId_t id, const char* name, int stream_write_size) { + ASSERT_X_LINK(name); + if (stream_write_size < 0) { + return X_LINK_ERROR; + } + xLinkEvent_t event = {0}; xLinkDesc_t* link = getLinkById(id); mvLog(MVLOG_DEBUG,"%s() id %d link %p\n", __func__, id, link); @@ -1092,8 +1231,7 @@ streamId_t XLinkOpenStream(linkId_t id, const char* name, int stream_write_size) return INVALID_STREAM_ID; } - if(strlen(name) > MAX_STREAM_NAME_LENGTH) - { + if(strlen(name) > MAX_STREAM_NAME_LENGTH) { mvLog(MVLOG_WARN,"name too long\n"); return INVALID_STREAM_ID; } @@ -1103,7 +1241,7 @@ streamId_t XLinkOpenStream(linkId_t id, const char* name, int stream_write_size) stream_write_size = ALIGN_UP(stream_write_size, __CACHE_LINE_SIZE); event.header.type = XLINK_CREATE_STREAM_REQ; mv_strncpy(event.header.streamName, MAX_STREAM_NAME_LENGTH, - name, MAX_STREAM_NAME_LENGTH - 1); + name, MAX_STREAM_NAME_LENGTH - 1); event.header.size = stream_write_size; event.header.streamId = INVALID_STREAM_ID; event.deviceHandle = link->deviceHandle; @@ -1112,6 +1250,7 @@ streamId_t XLinkOpenStream(linkId_t id, const char* name, int stream_write_size) if (dispatcherWaitEventComplete(&link->deviceHandle, DEFAULT_TIMEOUT)) return INVALID_STREAM_ID; +#ifdef __PC__ XLinkError_t eventStatus = checkEventHeader(event.header); if (eventStatus != X_LINK_SUCCESS) { mvLog(MVLOG_ERROR, "Got wrong package from device, error code = %s", XLinkErrorToStr(eventStatus)); @@ -1122,14 +1261,23 @@ streamId_t XLinkOpenStream(linkId_t id, const char* name, int stream_write_size) return INVALID_STREAM_ID; } } +#endif } streamId_t streamId = getStreamIdByName(link, name); +#ifdef __PC__ if (streamId > 0x0FFFFFFF) { mvLog(MVLOG_ERROR, "Cannot find stream id by the \"%s\" name", name); mvLog(MVLOG_ERROR,"Max streamId reached!"); return INVALID_STREAM_ID; } +#else + if (streamId == INVALID_STREAM_ID) { + mvLog(MVLOG_ERROR,"Max streamId reached %x!", streamId); + return INVALID_STREAM_ID; + } +#endif + COMBIN_IDS(streamId, id); return streamId; } @@ -1157,7 +1305,6 @@ XLinkError_t checkEventHeader(xLinkEventHeader_t header) { } } - // Just like open stream, when closeStream is called // on the local size we are resetting the writeSize // and on the remote side we are freeing the read buffer @@ -1176,7 +1323,8 @@ XLinkError_t XLinkCloseStream(streamId_t streamId) event.header.type = XLINK_CLOSE_STREAM_REQ; event.header.streamId = streamId; event.deviceHandle = link->deviceHandle; - if (dispatcherAddEvent(EVENT_LOCAL, &event) == NULL) { + xLinkEvent_t* ev = dispatcherAddEvent(EVENT_LOCAL, &event); + if (ev == NULL) { mvLog(MVLOG_ERROR, "Dispatcher failed on adding event"); return X_LINK_ERROR; } @@ -1184,9 +1332,7 @@ XLinkError_t XLinkCloseStream(streamId_t streamId) if (dispatcherWaitEventComplete(&link->deviceHandle, DEFAULT_TIMEOUT)) return X_LINK_TIMEOUT; - if (event.header.flags.bitField.ack == 1) - return X_LINK_SUCCESS; - else + if (event.header.flags.bitField.ack != 1) return X_LINK_COMMUNICATION_FAIL; return X_LINK_SUCCESS; @@ -1201,22 +1347,43 @@ XLinkError_t XLinkGetAvailableStreams(linkId_t id) { return X_LINK_COMMUNICATION_NOT_OPEN; } + /*...get other statuses*/ return X_LINK_SUCCESS; } -XLinkError_t XLinkFindDevice(int index, XLinkDeviceState_t state, - deviceDesc_t* in_deviceRequirements, deviceDesc_t* out_foundDevice) +XLinkError_t XLinkFindFirstSuitableDevice(XLinkDeviceState_t state, + const deviceDesc_t in_deviceRequirements, + deviceDesc_t *out_foundDevice) { - memset(out_foundDevice, 0, sizeof(struct deviceDesc_t)); + ASSERT_X_LINK(out_foundDevice); xLinkPlatformErrorCode_t rc; - rc = XLinkPlatformFindDeviceName(index, state, in_deviceRequirements, out_foundDevice); + rc = XLinkPlatformFindDeviceName(state, in_deviceRequirements, out_foundDevice); + return parseUsbLinkPlatformError(rc); +} + +XLinkError_t XLinkFindAllSuitableDevices(XLinkDeviceState_t state, + deviceDesc_t in_deviceRequirements, + deviceDesc_t *out_foundDevicesPtr, + const unsigned int devicesArraySize, + unsigned int* out_amountOfFoundDevices) { + ASSERT_X_LINK(out_foundDevicesPtr); + ASSERT_X_LINK(devicesArraySize > 0); + ASSERT_X_LINK(out_amountOfFoundDevices); + + xLinkPlatformErrorCode_t rc; + rc = XLinkPlatformFindArrayOfDevicesNames( + state, in_deviceRequirements, + out_foundDevicesPtr, devicesArraySize, out_amountOfFoundDevices); + return parseUsbLinkPlatformError(rc); } static XLinkError_t writeData(streamId_t streamId, const uint8_t* buffer, - int size, unsigned int timeout) + int size, unsigned int timeout) { + ASSERT_X_LINK(buffer); + linkId_t id; EXTRACT_IDS(streamId,id); xLinkDesc_t* link = getLinkById(id); @@ -1225,8 +1392,6 @@ static XLinkError_t writeData(streamId_t streamId, const uint8_t* buffer, { return X_LINK_COMMUNICATION_NOT_OPEN; } - struct timespec start, end; - clock_gettime(CLOCK_REALTIME, &start); xLinkEvent_t event = {0}; event.header.type = XLINK_WRITE_REQ; @@ -1235,10 +1400,15 @@ static XLinkError_t writeData(streamId_t streamId, const uint8_t* buffer, event.deviceHandle = link->deviceHandle; event.data = (void*)buffer; - if (dispatcherAddEvent(EVENT_LOCAL, &event) == NULL) { + struct timespec start, end; + clock_gettime(CLOCK_REALTIME, &start); + + xLinkEvent_t* ev = dispatcherAddEvent(EVENT_LOCAL, &event); + if (ev == NULL) { mvLog(MVLOG_ERROR, "Dispatcher failed on adding event"); return X_LINK_ERROR; } + if (dispatcherWaitEventComplete(&link->deviceHandle, timeout)) return X_LINK_TIMEOUT; @@ -1246,7 +1416,7 @@ static XLinkError_t writeData(streamId_t streamId, const uint8_t* buffer, if (event.header.flags.bitField.ack == 1) { - //profile only on success + //profile only on success if( glHandler->profEnable) { glHandler->profilingData.totalWriteBytes += size; @@ -1265,17 +1435,17 @@ XLinkError_t XLinkWriteData(streamId_t streamId, const uint8_t* buffer, } XLinkError_t XLinkWriteDataWithTimeout(streamId_t streamId, const uint8_t* buffer, - int size, unsigned int timeout) + int size, unsigned int timeout) { return writeData(streamId, buffer, size, timeout); } - XLinkError_t XLinkWriteGraphData(streamId_t streamId, const uint8_t* buffer, int size) { return writeData(streamId, buffer, size, glAllocateGraphTimeOutMsec); } + XLinkError_t XLinkAsyncWriteData() { if (getXLinkState(NULL) != XLINK_UP) @@ -1302,20 +1472,21 @@ XLinkError_t XLinkReadDataWithTimeOut(streamId_t streamId, streamPacketDesc_t** } xLinkEvent_t event = {0}; - struct timespec start, end; - event.header.type = XLINK_READ_REQ; event.header.size = 0; event.header.streamId = streamId; event.deviceHandle = link->deviceHandle; event.data = NULL; + struct timespec start, end; clock_gettime(CLOCK_REALTIME, &start); - if (dispatcherAddEvent(EVENT_LOCAL, &event) == NULL) { + xLinkEvent_t* ev = dispatcherAddEvent(EVENT_LOCAL, &event); + if (ev == NULL) { mvLog(MVLOG_ERROR, "Dispatcher failed on adding event"); return X_LINK_ERROR; } + if (dispatcherWaitEventComplete(&link->deviceHandle, timeout)) return X_LINK_TIMEOUT; @@ -1327,17 +1498,16 @@ XLinkError_t XLinkReadDataWithTimeOut(streamId_t streamId, streamPacketDesc_t** *packet = (streamPacketDesc_t *)event.data; clock_gettime(CLOCK_REALTIME, &end); - if (event.header.flags.bitField.ack == 1) + if (event.header.flags.bitField.ack != 1) + return X_LINK_COMMUNICATION_FAIL; + + if( glHandler->profEnable) { - if( glHandler->profEnable) - { - glHandler->profilingData.totalReadBytes += (*packet)->length; - glHandler->profilingData.totalReadTime += timespec_diff(&start, &end); - } - return X_LINK_SUCCESS; + glHandler->profilingData.totalReadBytes += (*packet)->length; + glHandler->profilingData.totalReadTime += timespec_diff(&start, &end); } - else - return X_LINK_COMMUNICATION_FAIL; + + return X_LINK_SUCCESS; } XLinkError_t XLinkReleaseData(streamId_t streamId) @@ -1356,10 +1526,12 @@ XLinkError_t XLinkReleaseData(streamId_t streamId) event.header.streamId = streamId; event.deviceHandle = link->deviceHandle; - if (dispatcherAddEvent(EVENT_LOCAL, &event) == NULL) { + xLinkEvent_t* ev = dispatcherAddEvent(EVENT_LOCAL, &event); + if (ev == NULL) { mvLog(MVLOG_ERROR, "Dispatcher failed on adding event"); return X_LINK_ERROR; } + if (dispatcherWaitEventComplete(&link->deviceHandle, DEFAULT_TIMEOUT)) return X_LINK_TIMEOUT; @@ -1369,7 +1541,7 @@ XLinkError_t XLinkReleaseData(streamId_t streamId) return X_LINK_COMMUNICATION_FAIL; } -XLinkError_t XLinkBootRemote(deviceDesc_t* deviceDesc, const char* binaryPath) +XLinkError_t XLinkBoot(deviceDesc_t* deviceDesc, const char* binaryPath) { if (XLinkPlatformBootRemote(deviceDesc, binaryPath) == 0) return X_LINK_SUCCESS; @@ -1392,9 +1564,9 @@ XLinkError_t XLinkResetRemote(linkId_t id) xLinkEvent_t event = {0}; event.header.type = XLINK_RESET_REQ; event.deviceHandle = link->deviceHandle; - mvLog(MVLOG_DEBUG,"sending reset remote event\n"); + mvLog(MVLOG_DEBUG, "sending reset remote event\n"); dispatcherAddEvent(EVENT_LOCAL, &event); - if (dispatcherWaitEventComplete(&link->deviceHandle, DEFAULT_TIMEOUT)) + if (dispatcherWaitEventComplete(&link->deviceHandle, glDeviceOpenTimeOutMsec)) return X_LINK_TIMEOUT; return X_LINK_SUCCESS; @@ -1407,8 +1579,7 @@ XLinkError_t XLinkResetAll() #else int i; for (i = 0; i < MAX_LINKS; i++) { - if (availableXLinks[i].id != INVALID_LINK_ID && - availableXLinks[i].deviceHandle.protocol != X_LINK_PCIE) { + if (availableXLinks[i].id != INVALID_LINK_ID) { xLinkDesc_t* link = &availableXLinks[i]; int stream; for (stream = 0; stream < XLINK_MAX_STREAMS; stream++) { @@ -1477,4 +1648,90 @@ XLinkError_t XLinkProfPrint() } return X_LINK_SUCCESS; } + +// ------------------------------------ +// Deprecated API. Begin. +// ------------------------------------ + +XLinkError_t getDeviceName(int index, char* name, int nameSize, XLinkPlatform_t platform, XLinkDeviceState_t state) +{ + ASSERT_X_LINK(name != NULL); + ASSERT_X_LINK(index >= 0); + ASSERT_X_LINK(nameSize >= 0 && nameSize <= XLINK_MAX_NAME_SIZE); + + deviceDesc_t in_deviceRequirements = {}; + in_deviceRequirements.protocol = glHandler != NULL ? glHandler->protocol : USB_VSC; + in_deviceRequirements.platform = platform; + memset(name, 0, nameSize); + + if(index == 0) + { + deviceDesc_t deviceToBoot = {}; + XLinkError_t rc = + XLinkFindFirstSuitableDevice(state, in_deviceRequirements, &deviceToBoot); + if(rc != X_LINK_SUCCESS) + { + return rc; + } + + return mv_strcpy(name, nameSize, deviceToBoot.name) == EOK ? X_LINK_SUCCESS : X_LINK_ERROR; + } + else + { + deviceDesc_t deviceDescArray[XLINK_MAX_DEVICES] = {}; + unsigned int numberOfDevices = 0; + XLinkError_t rc = + XLinkFindAllSuitableDevices(state, in_deviceRequirements, + deviceDescArray, XLINK_MAX_DEVICES, &numberOfDevices); + if(rc != X_LINK_SUCCESS) + { + return rc; + } + + if((unsigned int)index >= numberOfDevices) + { + return X_LINK_DEVICE_NOT_FOUND; + } + + return mv_strcpy(name, nameSize, deviceDescArray[index].name) == EOK ? X_LINK_SUCCESS : X_LINK_ERROR; + } +} + +XLinkError_t XLinkGetDeviceName(int index, char* name, int nameSize) +{ + return getDeviceName(index, name, nameSize, X_LINK_ANY_PLATFORM, X_LINK_ANY_STATE); +} +XLinkError_t XLinkGetDeviceNameExtended(int index, char* name, int nameSize, int pid) +{ + XLinkDeviceState_t state = XLinkPlatformPidToState(pid); + XLinkPlatform_t platform = XLinkPlatformPidToPlatform(pid); + + return getDeviceName(index, name, nameSize, platform, state); +} + +XLinkError_t XLinkBootRemote(const char* deviceName, const char* binaryPath) +{ + ASSERT_X_LINK(deviceName != NULL); + ASSERT_X_LINK(binaryPath != NULL); + + deviceDesc_t deviceDesc = {}; + deviceDesc.protocol = glHandler != NULL ? glHandler->protocol : USB_VSC; + mv_strcpy(deviceDesc.name, XLINK_MAX_NAME_SIZE, deviceName); + + return XLinkBoot(&deviceDesc, binaryPath); +} + +XLinkError_t XLinkDisconnect(linkId_t id) +{ + xLinkDesc_t* link = getLinkById(id); + ASSERT_X_LINK(link != NULL); + + link->hostClosedFD = 1; + return XLinkPlatformCloseRemote(&link->deviceHandle); +} + +// ------------------------------------ +// Deprecated API. End. +// ------------------------------------ + /* end of file */ diff --git a/inference-engine/thirdparty/movidius/XLink/shared/XLink.h b/inference-engine/thirdparty/movidius/XLink/shared/XLink.h index f996cac014ad72..42f99a9aaf3dc5 100644 --- a/inference-engine/thirdparty/movidius/XLink/shared/XLink.h +++ b/inference-engine/thirdparty/movidius/XLink/shared/XLink.h @@ -6,6 +6,7 @@ /// @file /// @brief Application configuration Leon header /// + #ifndef _XLINK_H #define _XLINK_H #include "XLinkPublicDefines.h" @@ -43,11 +44,19 @@ XLinkError_t XLinkGetAvailableStreams(linkId_t id); /** * @brief Return Myriad device description which meets the requirements - * @param index a set of parameters that the device must comply with - * @param index Return device on index from suitable devices list */ -XLinkError_t XLinkFindDevice(int index, XLinkDeviceState_t state, - deviceDesc_t* in_deviceRequirements, deviceDesc_t* out_foundDevice); +XLinkError_t XLinkFindFirstSuitableDevice(XLinkDeviceState_t state, + const deviceDesc_t in_deviceRequirements, + deviceDesc_t *out_foundDevice); + +/** + * @brief Return Myriad device description which meets the requirements + */ +XLinkError_t XLinkFindAllSuitableDevices(XLinkDeviceState_t state, + const deviceDesc_t in_deviceRequirements, + deviceDesc_t *out_foundDevicesPtr, + const unsigned int devicesArraySize, + unsigned int *out_amountOfFoundDevices); // Send a package to initiate the writing of data to a remote stream // Note that the actual size of the written data is ALIGN_UP(size, 64) @@ -72,7 +81,7 @@ XLinkError_t XLinkReleaseData(streamId_t streamId); XLinkError_t XLinkGetFillLevel(streamId_t streamId, int isRemote, int* fillLevel); // Boot the remote (This is intended as an interface to boot the Myriad from PC) -XLinkError_t XLinkBootRemote(deviceDesc_t* deviceDesc, const char* binaryPath); +XLinkError_t XLinkBoot(deviceDesc_t* deviceDesc, const char* binaryPath); // Reset the remote XLinkError_t XLinkResetRemote(linkId_t id); @@ -87,6 +96,21 @@ XLinkError_t XLinkProfPrint(); XLinkError_t XLinkWriteGraphData(streamId_t streamId, const uint8_t* buffer, int size); +// ------------------------------------ +// Deprecated API. Begin. +// ------------------------------------ + +XLinkError_t XLinkGetDeviceName(int index, char* name, int nameSize); +XLinkError_t XLinkGetDeviceNameExtended(int index, char* name, int nameSize, int pid); + +XLinkError_t XLinkBootRemote(const char* deviceName, const char* binaryPath); + +XLinkError_t XLinkDisconnect(linkId_t id); + +// ------------------------------------ +// Deprecated API. End. +// ------------------------------------ + #ifdef __cplusplus } #endif diff --git a/inference-engine/thirdparty/movidius/XLink/shared/XLinkDispatcher.c b/inference-engine/thirdparty/movidius/XLink/shared/XLinkDispatcher.c index 252755e9c7f475..9927ee4fb2a342 100644 --- a/inference-engine/thirdparty/movidius/XLink/shared/XLinkDispatcher.c +++ b/inference-engine/thirdparty/movidius/XLink/shared/XLinkDispatcher.c @@ -7,27 +7,31 @@ /// /// @brief Application configuration Leon header /// - #ifndef _GNU_SOURCE #define _GNU_SOURCE // fix for warning: implicit declaration of function ‘pthread_setname_np’ #endif + #include "stdio.h" #include "stdint.h" #include "stdlib.h" #include "string.h" - #include #include + #if (defined(_WIN32) || defined(_WIN64)) -#include "win_pthread.h" -#include "win_semaphore.h" +# include "win_pthread.h" +# include "win_semaphore.h" #else -#include -#include +# include +# ifndef __APPLE__ +# include +# endif #endif + #include "XLinkDispatcher.h" #include "XLinkPrivateDefines.h" #include "XLink.h" +#include "XLink_tool.h" #define MVLOG_UNIT_NAME xLink #include "mvLog.h" @@ -42,11 +46,11 @@ typedef enum { typedef struct xLinkEventPriv_t { xLinkEvent_t packet; + xLinkEvent_t *retEv; xLinkEventState_t isServed; xLinkEventOrigin_t origin; sem_t* sem; void* data; - xLinkEvent_t * retEv; uint32_t pad; } xLinkEventPriv_t; @@ -72,6 +76,8 @@ typedef struct { xLinkDeviceHandle_t deviceHandle; //will be device handler int schedulerId; + int queueProcPriority; + sem_t addEventSem; sem_t notifyDispatcherSem; volatile uint32_t resetXLink; @@ -83,22 +89,6 @@ typedef struct { localSem_t eventSemaphores[MAXIMUM_SEMAPHORES]; } xLinkSchedulerState_t; - -#define CIRCULAR_INCREMENT(x, maxVal, base) \ - { \ - x++; \ - if (x == maxVal) \ - x = base; \ - } -//avoid problems with unsigned. first compare and then give the nuw value -#define CIRCULAR_DECREMENT(x, maxVal, base) \ -{ \ - if (x == base) \ - x = maxVal - 1; \ - else \ - x--; \ -} - extern char* TypeToStr(int type); #if (defined(_WIN32) || defined(_WIN64)) @@ -112,10 +102,11 @@ int numSchedulers; xLinkSchedulerState_t schedulerState[MAX_SCHEDULERS]; sem_t addSchedulerSem; +//below workaround for "C2088 '==': illegal for struct" error int pthread_t_compare(pthread_t a, pthread_t b) { #if (defined(_WIN32) || defined(_WIN64) ) - return ((a.tid == b.tid)); + return ((a.tid == b.tid)); #else return (a == b); #endif @@ -186,7 +177,7 @@ static sem_t* createSem(xLinkSchedulerState_t* curr) } else return NULL; - return sem; + return sem; } } @@ -199,7 +190,7 @@ static void* eventReader(void* ctx) xLinkSchedulerState_t *curr = (xLinkSchedulerState_t*)ctx; ASSERT_X_LINK_R(curr, NULL); - xLinkEvent_t event = { 0 }; + xLinkEvent_t event = { 0 };// to fix error C4700 in win event.header.id = -1; event.deviceHandle = curr->deviceHandle; @@ -208,28 +199,30 @@ static void* eventReader(void* ctx) while (!curr->resetXLink) { int sc = glControlFunc->eventReceive(&event); mvLog(MVLOG_DEBUG,"Reading %s (scheduler %d, fd %p, event id %d, event stream_id %u, event size %u)\n", - TypeToStr(event.header.type), curr->schedulerId, event.deviceHandle.xLinkFD, event.header.id, event.header.streamId, event.header.size); + TypeToStr(event.header.type), curr->schedulerId, event.deviceHandle.xLinkFD, event.header.id, event.header.streamId, event.header.size); +#ifdef __PC__ if (event.header.type == XLINK_RESET_RESP) { curr->resetXLink = 1; mvLog(MVLOG_INFO,"eventReader thread stopped: reset"); break; } +#endif if (sc) { + // Only run this logic on the host side, the FW does not need this logic +#ifdef __PC__ if (sem_post(&curr->notifyDispatcherSem)) { mvLog(MVLOG_ERROR,"can't post semaphore\n"); // stop eventSchedulerRun thread } mvLog(MVLOG_ERROR,"eventReader thread stopped (err %d)", sc); +#endif break; } } - return 0; } - - static int isEventTypeRequest(xLinkEventPriv_t* event) { if (event->packet.header.type < XLINK_REQUEST_LAST) @@ -248,9 +241,9 @@ static void markEventReady(xLinkEventPriv_t* event) event->isServed = EVENT_READY; } -static void markEventServed(xLinkEventPriv_t* event) +static void eventPost(xLinkEventPriv_t* event) { - if(event->retEv){ + if (event->retEv){ // the xLinkEventPriv_t slot pointed by "event" will be // re-cycled as soon as we mark it as EVENT_SERVED, // so before that, we copy the result event into XLink API layer @@ -261,9 +254,13 @@ static void markEventServed(xLinkEventPriv_t* event) mvLog(MVLOG_ERROR,"can't post semaphore\n"); } } - event->isServed = EVENT_SERVED; } +static void markEventServed(xLinkEventPriv_t* event) +{ + eventPost(event); + event->isServed = EVENT_SERVED; +} static int dispatcherRequestServe(xLinkEventPriv_t * event, xLinkSchedulerState_t* curr){ ASSERT_X_LINK(curr != NULL); @@ -271,10 +268,15 @@ static int dispatcherRequestServe(xLinkEventPriv_t * event, xLinkSchedulerState_ xLinkEventHeader_t *header = &event->packet.header; if (header->flags.bitField.block){ //block is requested markEventBlocked(event); - }else if(header->flags.bitField.localServe == 1 || - (header->flags.bitField.ack == 0 - && header->flags.bitField.nack == 1)){ //this event is served locally, or it is failed + } else if(header->flags.bitField.localServe == 1 || + (header->flags.bitField.ack == 0 + && header->flags.bitField.nack == 1)){ //this event is served locally, or it is failed +#ifdef __PC__ markEventServed(event); +#else + eventPost(event); + return 1; +#endif }else if (header->flags.bitField.ack == 1 && header->flags.bitField.nack == 0){ event->isServed = EVENT_PENDING; @@ -298,11 +300,11 @@ static int dispatcherResponseServe(xLinkEventPriv_t * event, xLinkSchedulerState xLinkEventHeader_t *evHeader = &event->packet.header; if (curr->lQueue.q[i].isServed == EVENT_PENDING && - header->id == evHeader->id && - header->type == evHeader->type - XLINK_REQUEST_LAST -1) + header->id == evHeader->id && + header->type == evHeader->type - XLINK_REQUEST_LAST -1) { mvLog(MVLOG_DEBUG,"----------------------ISserved %s\n", - TypeToStr(header->type)); + TypeToStr(header->type)); //propagate back flags header->flags = evHeader->flags; markEventServed(&curr->lQueue.q[i]); @@ -310,14 +312,14 @@ static int dispatcherResponseServe(xLinkEventPriv_t * event, xLinkSchedulerState } } if (i == MAX_EVENTS) { - mvLog(MVLOG_FATAL,"no request for this response: %s %d %d\n", TypeToStr(event->packet.header.type), event->origin, event->packet.header.id); + mvLog(MVLOG_FATAL,"no request for this response: %s %d\n", TypeToStr(event->packet.header.type), event->origin); printf("#### (i == MAX_EVENTS) %s %d %d\n", TypeToStr(event->packet.header.type), event->origin, (int)event->packet.header.id); for (i = 0; i < MAX_EVENTS; i++) { xLinkEventHeader_t *header = &curr->lQueue.q[i].packet.header; printf("%d) header->id %i, header->type %s(%i), curr->lQueue.q[i].isServed %i, EVENT_PENDING %i\n", i, (int)header->id - , TypeToStr(header->type), header->type, curr->lQueue.q[i].isServed, EVENT_PENDING); + , TypeToStr(header->type), header->type, curr->lQueue.q[i].isServed, EVENT_PENDING); } ASSERT_X_LINK(0); @@ -329,7 +331,7 @@ static inline xLinkEventPriv_t* getNextElementWithState(xLinkEventPriv_t* base, xLinkEventPriv_t* start, xLinkEventState_t state){ xLinkEventPriv_t* tmp = start; while (start->isServed != state){ - CIRCULAR_INCREMENT(start, end, base); + CIRCULAR_INCREMENT_BASE(start, end, base); if(tmp == start){ break; } @@ -357,10 +359,10 @@ static xLinkEventPriv_t* searchForReadyEvent(xLinkSchedulerState_t* curr) static xLinkEventPriv_t* getNextQueueElemToProc(eventQueueHandler_t *q ){ xLinkEventPriv_t* event = NULL; - event = getNextElementWithState(q->base, q->end, q->curProc, EVENT_ALLOCATED); - if(event != NULL) { + if (q->cur != q->curProc) { + event = getNextElementWithState(q->base, q->end, q->curProc, EVENT_ALLOCATED); q->curProc = event; - CIRCULAR_INCREMENT(q->curProc, q->end, q->base); + CIRCULAR_INCREMENT_BASE(q->curProc, q->end, q->base); } return event; } @@ -375,7 +377,7 @@ static xLinkEvent_t* addNextQueueElemToProc(xLinkSchedulerState_t* curr, xLinkEvent_t* ev; xLinkEventPriv_t* eventP = getNextElementWithState(q->base, q->end, q->cur, EVENT_SERVED); if (eventP == NULL) { - mvLog(MVLOG_ERROR, "Can not get next element"); + mvLog(MVLOG_ERROR, "getNextElementWithState returned NULL"); return NULL; } mvLog(MVLOG_DEBUG, "Received event %s %d", TypeToStr(event->header.type), o); @@ -385,7 +387,6 @@ static xLinkEvent_t* addNextQueueElemToProc(xLinkSchedulerState_t* curr, mvLog(MVLOG_WARN, "Failed to unref sem"); } } - eventP->sem = sem; eventP->packet = *event; eventP->origin = o; @@ -395,10 +396,9 @@ static xLinkEvent_t* addNextQueueElemToProc(xLinkSchedulerState_t* curr, }else{ eventP->retEv = NULL; } - // Mark eventP as ALLOCATED to prevent it from being allocated again - eventP->isServed = EVENT_ALLOCATED; q->cur = eventP; - CIRCULAR_INCREMENT(q->cur, q->end, q->base); + eventP->isServed = EVENT_ALLOCATED; + CIRCULAR_INCREMENT_BASE(q->cur, q->end, q->base); return ev; } @@ -406,20 +406,26 @@ static xLinkEventPriv_t* dispatcherGetNextEvent(xLinkSchedulerState_t* curr) { ASSERT_X_LINK_R(curr != NULL, NULL); + if (XLinkWaitSem(&curr->notifyDispatcherSem)) { + mvLog(MVLOG_ERROR,"can't post semaphore\n"); + } + xLinkEventPriv_t* event = NULL; event = searchForReadyEvent(curr); if (event) { return event; } - if (XLinkWaitSem(&curr->notifyDispatcherSem)) { - mvLog(MVLOG_ERROR,"can't post semaphore\n"); - return NULL; - } - event = getNextQueueElemToProc(&curr->lQueue); + + eventQueueHandler_t* hPriorityQueue = curr->queueProcPriority ? &curr->lQueue : &curr->rQueue; + eventQueueHandler_t* lPriorityQueue = curr->queueProcPriority ? &curr->rQueue : &curr->lQueue; + curr->queueProcPriority = curr->queueProcPriority ? 0 : 1; + + event = getNextQueueElemToProc(hPriorityQueue); if (event) { return event; } - event = getNextQueueElemToProc(&curr->rQueue); + event = getNextQueueElemToProc(lPriorityQueue); + return event; } @@ -436,7 +442,6 @@ static int isAvailableScheduler(xLinkSchedulerState_t* curr) static void closeDeviceFdAndResetScheduler(xLinkSchedulerState_t* curr) { - mvLog(MVLOG_INFO, "Dispatcher Cleaning..."); glControlFunc->closeDeviceFd(&curr->deviceHandle); curr->schedulerId = -1; @@ -453,36 +458,34 @@ static void closeDeviceFdAndResetScheduler(xLinkSchedulerState_t* curr) } numSchedulers--; mvLog(MVLOG_INFO,"Cleaning Successfully\n"); - } - static int dispatcherReset(xLinkSchedulerState_t* curr) { ASSERT_X_LINK(curr != NULL); +#ifdef __PC__ CHECK_MUTEX_SUCCESS_RC(pthread_mutex_lock(&reset_mutex), 1); if(!isAvailableScheduler(curr)) { CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&reset_mutex)); return 1; } +#endif mvLog(MVLOG_INFO, "Resetting..."); - glControlFunc->closeLink(curr->deviceHandle.xLinkFD); - - //notifyDispatcherSem +1 for NULL event, avoid dispatcher blocking. + glControlFunc->closeLink(curr->deviceHandle.xLinkFD, 1); if (sem_post(&curr->notifyDispatcherSem)) { mvLog(MVLOG_ERROR,"can't post semaphore\n"); //to allow us to get a NULL event } - xLinkEventPriv_t* event = dispatcherGetNextEvent(curr); while (event != NULL) { mvLog(MVLOG_INFO, "dropped event is %s, status %d\n", TypeToStr(event->packet.header.type), event->isServed); - // although there is no no execution for this event, also mark it as being served without success - // caller will be informed and internal event memory slot will be de-allocated + +#ifdef __PC__ markEventServed(event); +#endif event = dispatcherGetNextEvent(curr); } @@ -492,10 +495,20 @@ static int dispatcherReset(xLinkSchedulerState_t* curr) markEventServed(event); event = getNextElementWithState(curr->lQueue.base, curr->lQueue.end, curr->lQueue.base, EVENT_PENDING); } + +#ifdef __PC__ closeDeviceFdAndResetScheduler(curr); CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&reset_mutex)); +#else + glControlFunc->closeDeviceFd(&curr->deviceHandle); + curr->schedulerId = -1; + numSchedulers--; +#endif + + mvLog(MVLOG_DEBUG,"Reset Successfully\n"); return 0; } + #if (defined(_WIN32) || defined(_WIN64)) static void* __cdecl eventSchedulerRun(void* ctx) #else @@ -512,11 +525,22 @@ static void* eventSchedulerRun(void* ctx) pthread_attr_t attr; int sc; int res; - if (pthread_attr_init(&attr) !=0) { + if (pthread_attr_init(&attr) != 0) { mvLog(MVLOG_ERROR,"pthread_attr_init error"); return NULL; } - +#ifndef __PC__ + if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) != 0) { + pthread_attr_destroy(&attr); + mvLog(MVLOG_ERROR,"pthread_attr_setinheritsched error"); + return NULL; + } + if (pthread_attr_setschedpolicy(&attr, SCHED_RR) != 0) { + pthread_attr_destroy(&attr); + mvLog(MVLOG_ERROR,"pthread_attr_setschedpolicy error"); + return NULL; + } +#endif sc = pthread_create(&readerThreadId, &attr, eventReader, curr); if (sc) { mvLog(MVLOG_ERROR, "Thread creation failed"); @@ -525,16 +549,21 @@ static void* eventSchedulerRun(void* ctx) } return NULL; } - char eventReaderThreadName[20]; +#ifndef __APPLE__ + char eventReaderThreadName[MVLOG_MAXIMUM_THREAD_NAME_SIZE]; snprintf(eventReaderThreadName, sizeof(eventReaderThreadName), "EventRead%.2dThr", schedulerId); sc = pthread_setname_np(readerThreadId, eventReaderThreadName); if (sc != 0) { perror("Setting name for event reader thread failed"); } +#endif +#ifdef __PC__ sc = pthread_attr_destroy(&attr); if (sc) { mvLog(MVLOG_WARN, "Thread attr destroy failed"); } +#endif + xLinkEventPriv_t* event; xLinkEventPriv_t response; @@ -542,10 +571,17 @@ static void* eventSchedulerRun(void* ctx) while (!curr->resetXLink) { event = dispatcherGetNextEvent(curr); - if (event == NULL) { + if(event == NULL) + { + mvLog(MVLOG_ERROR,"Dispatcher received NULL event!"); + /// Skip the event instead of asserting, so only + /// the particular xlink chan will crash +#ifdef __PC__ break; +#else + continue; +#endif } - ASSERT_X_LINK_R(event->packet.deviceHandle.xLinkFD == curr->deviceHandle.xLinkFD, NULL); getRespFunction getResp; xLinkEvent_t* toSend; @@ -560,26 +596,28 @@ static void* eventSchedulerRun(void* ctx) res = getResp(&event->packet, &response.packet); if (isEventTypeRequest(event)){ + int served = 0; if (event->origin == EVENT_LOCAL){ //we need to do this for locals only - dispatcherRequestServe(event, curr); + served = dispatcherRequestServe(event, curr); } - // For PCIE and in with Connect to booted option don't send reset request - - if (res == 0 && event->packet.header.flags.bitField.localServe == 0){ - // FIXME We shouldn't send reset request for PCIE and with turned on "NO_BOOT" cmake option - // Also, we can't just close evenReader thread, as WinPthread don't have suitable function for this emergency exit, - // so, let's pretend that would be ping request, and then we can correctly close eventReader thread - + if (res == 0 && event->packet.header.flags.bitField.localServe == 0) { +#ifndef __PC__ + /* + * Device part: reset device if sending failed + */ + ASSERT_X_LINK_R(glControlFunc->eventSend(toSend) == 0, NULL); +#else + (void)served; if (toSend->header.type == XLINK_RESET_REQ) { if(toSend->deviceHandle.protocol == X_LINK_PCIE) { toSend->header.type = XLINK_PING_REQ; curr->resetXLink = 1; - mvLog(MVLOG_INFO, "Request for reboot not sent"); + mvLog(MVLOG_DEBUG, "Request for reboot not sent, only ping event"); } else { #if defined(NO_BOOT) toSend->header.type = XLINK_PING_REQ; curr->resetXLink = 1; - mvLog(MVLOG_INFO, "Request for reboot not sent"); + mvLog(MVLOG_INFO, "Request for reboot not sent, only ping event"); #endif } } @@ -587,14 +625,30 @@ static void* eventSchedulerRun(void* ctx) if (glControlFunc->eventSend(toSend) != 0) { mvLog(MVLOG_ERROR, "Event sending failed"); } +#endif + } +#ifndef __PC__ + if (event->origin == EVENT_REMOTE || served) { + event->isServed = EVENT_SERVED; } +#endif } else { if (event->origin == EVENT_REMOTE){ // match remote response with the local request dispatcherResponseServe(event, curr); } +#ifndef __PC__ + event->isServed = EVENT_SERVED; +#endif } //TODO: dispatcher shouldn't know about this packet. Seems to be easily move-able to protocol +#ifndef __PC__ + if (event->origin == EVENT_REMOTE) { + if (event->packet.header.type == XLINK_RESET_REQ) { + curr->resetXLink = 1; + } + } +#else if (event->packet.header.type == XLINK_RESET_REQ) { curr->resetXLink = 1; } @@ -603,13 +657,20 @@ static void* eventSchedulerRun(void* ctx) if (event->origin == EVENT_REMOTE){ event->isServed = EVENT_SERVED; } +#endif } - sc = pthread_join(readerThreadId, NULL); if (sc) { mvLog(MVLOG_ERROR, "Waiting for thread failed"); } +#ifndef __PC__ + if (pthread_attr_destroy(&attr) != 0) { + mvLog(MVLOG_ERROR,"pthread_attr_destroy error"); + return NULL; + } +#endif + if (dispatcherReset(curr) != 0) { mvLog(MVLOG_WARN, "Failed to reset"); } @@ -655,7 +716,6 @@ xLinkEvent_t* dispatcherAddEvent(xLinkEventOrigin_t origin, xLinkEvent_t *event) if(curr->resetXLink) { return NULL; } - mvLog(MVLOG_DEBUG, "Receiving event %s %d\n", TypeToStr(event->header.type), origin); if (XLinkWaitSem(&curr->addEventSem)) { mvLog(MVLOG_ERROR,"can't wait semaphore\n"); @@ -675,6 +735,7 @@ xLinkEvent_t* dispatcherAddEvent(xLinkEventOrigin_t origin, xLinkEvent_t *event) if (sem_post(&curr->addEventSem)) { mvLog(MVLOG_ERROR,"can't post semaphore\n"); } + return NULL; } event->header.flags.raw = 0; @@ -701,9 +762,12 @@ int dispatcherWaitEventComplete(xLinkDeviceHandle_t* deviceHandle, unsigned int if (id == NULL) { return -1; } - +#ifndef __PC__ + (void)timeout; + return XLinkWaitSem(id); +#else int rc = XLinkWaitSemUserMode(id, timeout); - if (rc && deviceHandle->protocol != X_LINK_PCIE) { + if (rc) { xLinkEvent_t event = {0}; event.header.type = XLINK_RESET_REQ; event.deviceHandle = *deviceHandle; @@ -716,6 +780,7 @@ int dispatcherWaitEventComplete(xLinkDeviceHandle_t* deviceHandle, unsigned int } return rc; +#endif } int dispatcherUnblockEvent(eventId_t id, xLinkEventType_t type, streamId_t stream, void* xLinkFD) @@ -731,13 +796,16 @@ int dispatcherUnblockEvent(eventId_t id, xLinkEventType_t type, streamId_t strea { if (blockedEvent->isServed == EVENT_BLOCKED && ((blockedEvent->packet.header.id == id || id == -1) - && blockedEvent->packet.header.type == type - && blockedEvent->packet.header.streamId == stream)) + && blockedEvent->packet.header.type == type + && blockedEvent->packet.header.streamId == stream)) { mvLog(MVLOG_DEBUG,"unblocked**************** %d %s\n", (int)blockedEvent->packet.header.id, TypeToStr((int)blockedEvent->packet.header.type)); markEventReady(blockedEvent); + if (sem_post(&curr->notifyDispatcherSem)){ + mvLog(MVLOG_ERROR, "can't post semaphore\n"); + } return 1; } else { mvLog(MVLOG_DEBUG,"%d %s\n", @@ -762,10 +830,12 @@ int findAvailableScheduler() */ int dispatcherStart(xLinkDeviceHandle_t* deviceHandle) { +#ifdef __PC__ if (deviceHandle->xLinkFD == NULL) { mvLog(MVLOG_ERROR, "Invalid device filedescriptor"); return -1; } +#endif pthread_attr_t attr; int eventIdx; @@ -774,16 +844,16 @@ int dispatcherStart(xLinkDeviceHandle_t* deviceHandle) mvLog(MVLOG_ERROR,"Max number Schedulers reached!\n"); return -1; } - int idx = findAvailableScheduler(); - if (idx < 0) { - mvLog(MVLOG_ERROR,"Available sheduler not found"); + if (idx == -1) { + mvLog(MVLOG_ERROR,"Max number Schedulers reached!\n"); return -1; } memset(&schedulerState[idx], 0, sizeof(xLinkSchedulerState_t)); schedulerState[idx].semaphores = 0; + schedulerState[idx].queueProcPriority = 0; schedulerState[idx].resetXLink = 0; schedulerState[idx].deviceHandle = *deviceHandle; @@ -811,7 +881,6 @@ int dispatcherStart(xLinkDeviceHandle_t* deviceHandle) } if (sem_init(&schedulerState[idx].notifyDispatcherSem, 0, 0)) { perror("Can't create semaphore\n"); - return -1; } localSem_t* temp = schedulerState[idx].eventSemaphores; while (temp < schedulerState[idx].eventSemaphores + MAXIMUM_SEMAPHORES) { @@ -820,9 +889,22 @@ int dispatcherStart(xLinkDeviceHandle_t* deviceHandle) } if (pthread_attr_init(&attr) != 0) { mvLog(MVLOG_ERROR,"pthread_attr_init error"); +#ifdef __PC__ return -1; +#endif } +#ifndef __PC__ + if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) != 0) { + mvLog(MVLOG_ERROR,"pthread_attr_setinheritsched error"); + pthread_attr_destroy(&attr); + } + if (pthread_attr_setschedpolicy(&attr, SCHED_RR) != 0) { + mvLog(MVLOG_ERROR,"pthread_attr_setschedpolicy error"); + pthread_attr_destroy(&attr); + } +#endif + XLinkWaitSem(&addSchedulerSem); mvLog(MVLOG_DEBUG,"%s() starting a new thread - schedulerId %d \n", __func__, idx); int sc = pthread_create(&schedulerState[idx].xLinkThreadId, @@ -834,22 +916,25 @@ int dispatcherStart(xLinkDeviceHandle_t* deviceHandle) if (pthread_attr_destroy(&attr) != 0) { perror("Thread attr destroy failed\n"); } +#ifdef __PC__ return -1; +#endif } - - char schedulerThreadName[20]; +#ifndef __APPLE__ + char schedulerThreadName[MVLOG_MAXIMUM_THREAD_NAME_SIZE]; snprintf(schedulerThreadName, sizeof(schedulerThreadName), "Scheduler%.2dThr", schedulerState[idx].schedulerId); sc = pthread_setname_np(schedulerState[idx].xLinkThreadId, schedulerThreadName); if (sc != 0) { perror("Setting name for indexed scheduler thread failed"); } - +#endif +#ifdef __PC__ pthread_detach(schedulerState[idx].xLinkThreadId); - numSchedulers++; +#endif - sc = pthread_attr_destroy(&attr); - if (sc) { - perror("Thread attr destroy failed"); + numSchedulers++; + if (pthread_attr_destroy(&attr) != 0) { + mvLog(MVLOG_ERROR,"pthread_attr_destroy error"); } sem_post(&addSchedulerSem); @@ -857,10 +942,9 @@ int dispatcherStart(xLinkDeviceHandle_t* deviceHandle) return 0; } -/** - * @brief Initialize dispatcher functions and reset all schedulers - */ int dispatcherInitialize(struct dispatcherControlFunctions* controlFunc) { + // create thread which will communicate with the pc + int i; if (!controlFunc || !controlFunc->eventReceive || @@ -879,7 +963,14 @@ int dispatcherInitialize(struct dispatcherControlFunctions* controlFunc) { for (i = 0; i < MAX_SCHEDULERS; i++){ schedulerState[i].schedulerId = -1; } + +#ifndef __PC__ + xLinkDeviceHandle_t temp = {0}; + temp.protocol = X_LINK_ANY_PROTOCOL; + return dispatcherStart(&temp); //myriad has one +#else return 0; +#endif } int dispatcherClean(void* xLinkFD) @@ -899,6 +990,4 @@ int dispatcherClean(void* xLinkFD) return 0; } - - /* end of file */ diff --git a/inference-engine/thirdparty/movidius/XLink/shared/XLinkDispatcher.h b/inference-engine/thirdparty/movidius/XLink/shared/XLinkDispatcher.h index df01d116064cc8..0b2bf15d7eb3bf 100644 --- a/inference-engine/thirdparty/movidius/XLink/shared/XLinkDispatcher.h +++ b/inference-engine/thirdparty/movidius/XLink/shared/XLinkDispatcher.h @@ -33,7 +33,7 @@ struct dispatcherControlFunctions { int (*eventReceive) (xLinkEvent_t*); getRespFunction localGetResponse; getRespFunction remoteGetResponse; - void (*closeLink) (void* fd); + void (*closeLink) (void* fd, int fullClose); void (*closeDeviceFd) (xLinkDeviceHandle_t* deviceHandle); }; @@ -45,4 +45,4 @@ int dispatcherClean(void* xLinkFD); } #endif -#endif +#endif \ No newline at end of file diff --git a/inference-engine/thirdparty/movidius/XLink/shared/XLinkPlatform.h b/inference-engine/thirdparty/movidius/XLink/shared/XLinkPlatform.h index 77acc3d0cb5a87..1de0b16fbd8d24 100644 --- a/inference-engine/thirdparty/movidius/XLink/shared/XLinkPlatform.h +++ b/inference-engine/thirdparty/movidius/XLink/shared/XLinkPlatform.h @@ -2,6 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 // +/// +/// @brief Application configuration Leon header +/// + #ifndef _XLINK_LINKPLATFORM_H #define _XLINK_LINKPLATFORM_H @@ -17,24 +21,40 @@ extern "C" #define MAX_POOLS_ALLOC 32 #define PACKET_LENGTH (64*1024) +typedef enum { + X_LINK_PLATFORM_SUCCESS = 0, + X_LINK_PLATFORM_DEVICE_NOT_FOUND = -1, + X_LINK_PLATFORM_ERROR = -2, + X_LINK_PLATFORM_TIMEOUT = -3, + X_LINK_PLATFORM_DRIVER_NOT_LOADED = -4 +} xLinkPlatformErrorCode_t; + + int XLinkWrite(xLinkDeviceHandle_t* deviceHandle, void* data, int size, unsigned int timeout); int XLinkRead(xLinkDeviceHandle_t* deviceHandle, void* data, int size, unsigned int timeout); int XLinkPlatformConnect(const char* devPathRead, const char* devPathWrite, - XLinkProtocol_t protocol, void** fd); + XLinkProtocol_t protocol, void** fd); void XLinkPlatformInit(); /** - * @brief Return Myriad device name on index + * @brief Return Myriad device description which meets the requirements */ -int XLinkPlatformFindDeviceName(int index, +xLinkPlatformErrorCode_t XLinkPlatformFindDeviceName(XLinkDeviceState_t state, + const deviceDesc_t in_deviceRequirements, + deviceDesc_t* out_foundDevice); + +xLinkPlatformErrorCode_t XLinkPlatformFindArrayOfDevicesNames( XLinkDeviceState_t state, - deviceDesc_t* in_deviceRequirements, - deviceDesc_t* out_foundDevice); + const deviceDesc_t in_deviceRequirements, + deviceDesc_t* out_foundDevicePtr, + const unsigned int devicesArraySize, + unsigned int *out_amountOfFoundDevices); int XLinkPlatformIsDescriptionValid(deviceDesc_t *in_deviceDesc); -int XLinkPlatformToPid(const XLinkPlatform_t platform); +int XLinkPlatformToPid(const XLinkPlatform_t platform, const XLinkDeviceState_t state); XLinkPlatform_t XLinkPlatformPidToPlatform(const int pid); +XLinkDeviceState_t XLinkPlatformPidToState(const int pid); int XLinkPlatformBootRemote(deviceDesc_t* deviceDesc, const char* binaryPath); @@ -43,14 +63,6 @@ int XLinkPlatformCloseRemote(xLinkDeviceHandle_t* deviceHandle); void* allocateData(uint32_t size, uint32_t alignment); void deallocateData(void* ptr,uint32_t size, uint32_t alignment); -typedef enum xLinkPlatformErrorCode { - X_LINK_PLATFORM_SUCCESS = 0, - X_LINK_PLATFORM_DEVICE_NOT_FOUND = -1, - X_LINK_PLATFORM_ERROR = -2, - X_LINK_PLATFORM_TIMEOUT = -3, - X_LINK_PLATFORM_DRIVER_NOT_LOADED = -4 -} xLinkPlatformErrorCode_t; - #ifdef __cplusplus } #endif diff --git a/inference-engine/thirdparty/movidius/XLink/shared/XLinkPlatform_tool.h b/inference-engine/thirdparty/movidius/XLink/shared/XLinkPlatform_tool.h new file mode 100644 index 00000000000000..52fb6e3557d21a --- /dev/null +++ b/inference-engine/thirdparty/movidius/XLink/shared/XLinkPlatform_tool.h @@ -0,0 +1,41 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef _XLINKPLATFORM_TOOL_H +#define _XLINKPLATFORM_TOOL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef NDEBUG // Release configuration +#ifndef __PC__ + #define ASSERT_X_LINK_PLATFORM(x) if(!(x)) { exit(EXIT_FAILURE); } + #define ASSERT_X_LINK_PLATFORM_R(x, r) ASSERT_X_LINK_PLATFORM(x) + #else + #define ASSERT_X_LINK_PLATFORM(x) if(!(x)) { return X_LINK_PLATFORM_ERROR; } + #define ASSERT_X_LINK_PLATFORM_R(x, r) if(!(x)) { return r; } + #endif +#else // Debug configuration +#ifndef __PC__ +#define ASSERT_X_LINK_PLATFORM(x) if(!(x)) { fprintf(stderr, "%s:%d:\n Assertion Failed: %s\n", __FILE__, __LINE__, #x); exit(EXIT_FAILURE); } +#define ASSERT_X_LINK_PLATFORM_R(x, r) ASSERT_X_LINK_PLATFORM(x) +#else +#define ASSERT_X_LINK_PLATFORM(x) if(!(x)) { \ + mvLog(MVLOG_ERROR, "%s:%d\n\t Assertion Failed: %s", __FILE__, __LINE__, #x); \ + return X_LINK_PLATFORM_ERROR; \ + } +#define ASSERT_X_LINK_PLATFORM_R(x, r) if(!(x)) { \ + mvLog(MVLOG_ERROR, "%s:%d\n\t Assertion Failed: %s", __FILE__, __LINE__, #x); \ + return r; \ + } +#endif +#endif // NDEBUG + +#ifdef __cplusplus +} +#endif + +#endif //_XLINKPLATFORM_TOOL_H diff --git a/inference-engine/thirdparty/movidius/XLink/shared/XLinkPrivateDefines.h b/inference-engine/thirdparty/movidius/XLink/shared/XLinkPrivateDefines.h index 419136a49b1b2b..635bba30bbe253 100644 --- a/inference-engine/thirdparty/movidius/XLink/shared/XLinkPrivateDefines.h +++ b/inference-engine/thirdparty/movidius/XLink/shared/XLinkPrivateDefines.h @@ -11,13 +11,15 @@ #define _XLINKPRIVATEDEFINES_H #ifdef _XLINK_ENABLE_PRIVATE_INCLUDE_ - -#include -#if (defined(_WIN32) || defined(_WIN64)) -#include "win_semaphore.h" -#else -#include -#endif +# if (defined(_WIN32) || defined(_WIN64)) +# include "win_semaphore.h" +# else +# ifdef __APPLE__ +# include "pthread_semaphore.h" +# else +# include +# endif +# endif #include #ifdef __cplusplus @@ -25,52 +27,11 @@ extern "C" { #endif -#ifdef USE_USB_VSC -#define HEADER_SIZE (64-12 -8) -#else #define HEADER_SIZE (64-12 -8) -#endif #define MAXIMUM_SEMAPHORES 32 #define __CACHE_LINE_SIZE 64 -#ifdef NDEBUG // Release configuration - #ifndef __PC__ - #define ASSERT_X_LINK(x) if(!(x)) { exit(EXIT_FAILURE); } - #define ASSERT_X_LINK_R(x, r) ASSERT_X_LINK(x) - #else - #define ASSERT_X_LINK(x) if(!(x)) { return X_LINK_ERROR; } - #define ASSERT_X_LINK_R(x, r) if(!(x)) { return r; } - #endif -#else // Debug configuration - #ifndef __PC__ - #define ASSERT_X_LINK(x) if(!(x)) { fprintf(stderr, "%s:%d:\n Assertion Failed: %s\n", __FILE__, __LINE__, #x); exit(EXIT_FAILURE); } - #define ASSERT_X_LINK_R(x, r) ASSERT_X_LINK(x) - #else - #define ASSERT_X_LINK(x) if(!(x)) { fprintf(stderr, "%s:%d:\n Assertion Failed: %s\n", __FILE__, __LINE__, #x); return X_LINK_ERROR; } - #define ASSERT_X_LINK_R(x, r) if(!(x)) { fprintf(stderr, "%s:%d:\n Assertion Failed: %s\n", __FILE__, __LINE__, #x); return r; } - #endif -#endif // NDEBUG - -#ifndef CHECK_MUTEX_SUCCESS -#define CHECK_MUTEX_SUCCESS(call) { \ - int error; \ - if ((error = (call))) { \ - mvLog(MVLOG_ERROR, "%s failed with error: %d", #call, error); \ - } \ -} -#endif // CHECK_MUTEX_SUCCESS - -#ifndef CHECK_MUTEX_SUCCESS_RC -#define CHECK_MUTEX_SUCCESS_RC(call, rc) { \ - int error; \ - if ((error = (call))) { \ - mvLog(MVLOG_ERROR, "%s failed with error: %d", #call, error); \ - return rc; \ - } \ -} -#endif // CHECK_MUTEX_SUCCESS_RC - typedef int32_t eventId_t; /** @@ -80,7 +41,7 @@ typedef enum { XLINK_NOT_INIT, XLINK_UP, XLINK_DOWN, -} xLinkState_t; +}xLinkState_t; /** * @brief Device description @@ -114,7 +75,7 @@ typedef struct{ uint32_t closeStreamInitiated; sem_t sem; -} streamDesc_t; +}streamDesc_t; /** * @brief XLink primitive for each device @@ -126,13 +87,18 @@ typedef struct xLinkDesc_t { xLinkState_t peerState; xLinkDeviceHandle_t deviceHandle; linkId_t id; + + //Deprecated fields. Begin. + int hostClosedFD; + //Deprecated fields. End. + } xLinkDesc_t; //events which are coming from remote typedef enum { - /*USB-PCIE related events*/ + /*USB-X_LINK_PCIE related events*/ XLINK_WRITE_REQ, XLINK_READ_REQ, XLINK_READ_REL_REQ, @@ -151,7 +117,7 @@ typedef enum XLINK_RESET_RESP, XLINK_RESP_LAST, - /*IPC related events*/ + /*X_LINK_IPC related events*/ IPC_WRITE_REQ, IPC_READ_REQ, IPC_CREATE_STREAM_REQ, @@ -169,9 +135,15 @@ typedef enum EVENT_REMOTE, } xLinkEventOrigin_t; -#define MAX_EVENTS 64 +#ifdef __PC__ #define MAX_LINKS 32 +#else +#define MAX_LINKS 1 +#endif + +#define MAX_EVENTS 64 #define MAX_SCHEDULERS MAX_LINKS +#define XLINK_MAX_DEVICES MAX_LINKS typedef struct xLinkEventHeader_t{ eventId_t id; @@ -201,7 +173,6 @@ typedef struct xLinkEvent_t { }xLinkEvent_t; int XLinkWaitSem(sem_t* sem); - int XLinkWaitSemUserMode(sem_t* sem, unsigned int timeout); const char* XLinkErrorToStr(XLinkError_t rc); diff --git a/inference-engine/thirdparty/movidius/XLink/shared/XLinkPublicDefines.h b/inference-engine/thirdparty/movidius/XLink/shared/XLinkPublicDefines.h index a6abf2171a48d7..1f0b3e51b2ff59 100644 --- a/inference-engine/thirdparty/movidius/XLink/shared/XLinkPublicDefines.h +++ b/inference-engine/thirdparty/movidius/XLink/shared/XLinkPublicDefines.h @@ -62,8 +62,7 @@ typedef enum{ typedef uint32_t streamId_t; typedef uint8_t linkId_t; -typedef struct deviceDesc_t -{ +typedef struct { XLinkProtocol_t protocol; XLinkPlatform_t platform; char name[XLINK_MAX_NAME_SIZE]; @@ -73,7 +72,6 @@ typedef struct streamPacketDesc_t { uint8_t* data; uint32_t length; - } streamPacketDesc_t; typedef struct XLinkProf_t @@ -90,16 +88,34 @@ typedef struct XLinkGlobalHandler_t { int profEnable; XLinkProf_t profilingData; + + //Deprecated fields. Begin. + int loglevel; + int protocol; + //Deprecated fields. End. } XLinkGlobalHandler_t; typedef struct { char* devicePath; char* devicePath2; - linkId_t linkId; + int linkId; XLinkProtocol_t protocol; } XLinkHandler_t; + +//Deprecated defines. Begin. + +typedef enum{ + USB_VSC = 0, + USB_CDC, + PCIE, + IPC, + NMB_OF_PROTOCOLS +} XLinkProtocol_deprecated_t; + +//Deprecated defines. End. + #ifdef __cplusplus } #endif diff --git a/inference-engine/thirdparty/movidius/XLink/shared/XLinkVersion.h b/inference-engine/thirdparty/movidius/XLink/shared/XLinkVersion.h index 4832466789841c..1dce1739e3e63f 100644 --- a/inference-engine/thirdparty/movidius/XLink/shared/XLinkVersion.h +++ b/inference-engine/thirdparty/movidius/XLink/shared/XLinkVersion.h @@ -4,7 +4,6 @@ /// /// @file -/// /// @brief Application configuration Leon header /// diff --git a/inference-engine/thirdparty/movidius/XLink/shared/XLink_tool.h b/inference-engine/thirdparty/movidius/XLink/shared/XLink_tool.h new file mode 100644 index 00000000000000..6752d0549a0adb --- /dev/null +++ b/inference-engine/thirdparty/movidius/XLink/shared/XLink_tool.h @@ -0,0 +1,100 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef _XLINK_TOOL_H +#define _XLINK_TOOL_H + +#ifdef __cplusplus +extern "C" +{ +#endif +#ifdef NDEBUG // Release configuration +#ifndef __PC__ + #define ASSERT_X_LINK(x) if(!(x)) { exit(EXIT_FAILURE); } + #define ASSERT_X_LINK_R(x, r) ASSERT_X_LINK(x) + #else + #define ASSERT_X_LINK(x) if(!(x)) { return X_LINK_ERROR; } + #define ASSERT_X_LINK_R(x, r) if(!(x)) { return r; } + #endif +#else // Debug configuration + +#ifndef __PC__ +#define ASSERT_X_LINK(x) if(!(x)) { fprintf(stderr, "%s:%d:\n Assertion Failed: %s\n", __FILE__, __LINE__, #x); exit(EXIT_FAILURE); } +#define ASSERT_X_LINK_R(x, r) ASSERT_X_LINK(x) +#else +#define ASSERT_X_LINK(x) if(!(x)) { \ + mvLog(MVLOG_ERROR, "%s:%d\n\t Assertion Failed: %s", __FILE__, __LINE__, #x); \ + return X_LINK_ERROR; \ + } +#define ASSERT_X_LINK_R(x, r) if(!(x)) { \ + mvLog(MVLOG_ERROR, "%s:%d\n\t Assertion Failed: %s", __FILE__, __LINE__, #x); \ + return r; \ + } +#endif +#endif // NDEBUG + +#ifndef CHECK_MUTEX_SUCCESS +#define CHECK_MUTEX_SUCCESS(call) { \ + int error; \ + if ((error = (call))) { \ + mvLog(MVLOG_ERROR, "%s failed with error: %d", #call, error); \ + } \ + } +#endif // CHECK_MUTEX_SUCCESS + +#ifndef CHECK_MUTEX_SUCCESS_RC +#define CHECK_MUTEX_SUCCESS_RC(call, rc) { \ + int error; \ + if ((error = (call))) { \ + mvLog(MVLOG_ERROR, "%s failed with error: %d", #call, error); \ + return rc; \ + } \ + } +#endif // CHECK_MUTEX_SUCCESS_RC + +#define CIRCULAR_INCREMENT(x, maxVal) \ + { \ + x++; \ + if (x == maxVal) \ + x = 0; \ + } + +//avoid problems with unsigned. first compare and then give the nuw value +#define CIRCULAR_DECREMENT(x, maxVal) \ + { \ + if (x == 0) \ + x = maxVal; \ + else \ + x--; \ + } + +#define CIRCULAR_INCREMENT_BASE(x, maxVal, base) \ + { \ + x++; \ + if (x == maxVal) \ + x = base; \ + } +//avoid problems with unsigned. first compare and then give the nuw value +#define CIRCULAR_DECREMENT_BASE(x, maxVal, base) \ + { \ + if (x == base) \ + x = maxVal - 1; \ + else \ + x--; \ + } + +#define EXTRACT_IDS(streamId, linkId) \ + { \ + linkId = (streamId >> 24) & 0XFF; \ + streamId = streamId & 0xFFFFFF; \ + } + +#define COMBIN_IDS(streamId, linkid) \ + streamId = streamId | ((linkid & 0xFF) << 24); + +#ifdef __cplusplus +} +#endif + +#endif //_XLINK_TOOL_H diff --git a/inference-engine/thirdparty/movidius/XLink/tests/CMakeLists.txt b/inference-engine/thirdparty/movidius/XLink/tests/CMakeLists.txt deleted file mode 100644 index 49fc39a3b9c9ca..00000000000000 --- a/inference-engine/thirdparty/movidius/XLink/tests/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (C) 2018-2019 Intel Corporation -# SPDX-License-Identifier: Apache-2.0 -# - -set(TARGET_NAME "XLinkTests") -set(CMAKE_CXX_STANDARD 11) - -add_executable(${TARGET_NAME} XLink_tests.cpp) - -target_include_directories(${TARGET_NAME} - PRIVATE - ${IE_MAIN_SOURCE_DIR}/tests/libs/gtest/googletest/include - ${IE_MAIN_SOURCE_DIR}/tests/libs/gtest/googletest/ - ../shared - ../pc) - -target_link_libraries(${TARGET_NAME} - PRIVATE - XLink gtest gtest_main) - -set_target_properties(${TARGET_NAME} PROPERTIES - POSITION_INDEPENDENT_CODE TRUE - COMPILE_PDB_NAME ${TARGET_NAME}) diff --git a/inference-engine/thirdparty/movidius/XLink/tests/XLink_tests.cpp b/inference-engine/thirdparty/movidius/XLink/tests/XLink_tests.cpp deleted file mode 100644 index aa4622b279054c..00000000000000 --- a/inference-engine/thirdparty/movidius/XLink/tests/XLink_tests.cpp +++ /dev/null @@ -1,390 +0,0 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include -#include -#include -#include -#include -#include "XLink.h" - -#define MAX_NAME_LENGTH 16 -#define MAX_DEVICES 32 -#define MAX_PATH 255 - -#define MYRIADX 0x2485 -#define MYRIAD2 0x2150 -#define MYRIAD_BOOTED 0xf63b -#define MYRIAD_UNBOOTED -1 - -static XLinkGlobalHandler_t globalHandler; - -class XLinkTests : public ::testing::Test { -public: - static void SetUpTestCase() { - ASSERT_EQ(X_LINK_SUCCESS, XLinkInitialize(&globalHandler)); - // Waiting for initialization - std::this_thread::sleep_for(std::chrono::seconds(1)); - } -protected: - virtual ~XLinkTests() {} - - void getFirmwarePath(char* devAddr, char* firmwarePath) { - char* p = strchr(devAddr, '-'); - if (p == nullptr) { - EXPECT_TRUE(false) << "Invalid device address"; - } -#if (!defined(_WIN32) && !defined(_WIN64)) - snprintf(firmwarePath, 40, "./lib/MvNCAPI%s.mvcmd", p); -#else - snprintf(firmwarePath, 40, "./MvNCAPI%s.mvcmd", p); -#endif // #if (!defined(_WIN32) && !defined(_WIN64)) - } - - void bootAnyDevice() { - char firmwarePath[MAX_PATH]; - deviceDesc_t deviceDesc = {}; - deviceDesc_t in_deviceDesc = {}; - in_deviceDesc.protocol = X_LINK_USB_VSC; - in_deviceDesc.platform = X_LINK_ANY_PLATFORM; - - // Get device name - ASSERT_EQ(X_LINK_SUCCESS, XLinkFindDevice(0, X_LINK_UNBOOTED, &in_deviceDesc, &deviceDesc)); - getFirmwarePath(deviceDesc.name, firmwarePath); - - printf("Would boot (%s) device with firmware (%s) \n", deviceDesc.name, firmwarePath); - - // Boot device - ASSERT_EQ(X_LINK_SUCCESS, XLinkBootRemote(&deviceDesc, firmwarePath)); - // FIXME: need to find a way to avoid this sleep - std::this_thread::sleep_for(std::chrono::seconds(2)); - - // Check, that device booted - deviceDesc_t bootedDeviceDesc = {}; - ASSERT_EQ(X_LINK_SUCCESS, XLinkFindDevice(0, X_LINK_BOOTED, &in_deviceDesc, &bootedDeviceDesc)); - } - - void closeDevice(char* bootedName) { - XLinkHandler_t *handler = (XLinkHandler_t *)malloc(sizeof(XLinkHandler_t)); - handler->devicePath = bootedName; - ASSERT_EQ(X_LINK_SUCCESS, XLinkConnect(handler)); - // FIXME: need to find a way to avoid this sleep - std::this_thread::sleep_for(std::chrono::seconds(1)); - - ASSERT_EQ(X_LINK_SUCCESS, XLinkResetRemote(handler->linkId)); - free(handler); - - std::this_thread::sleep_for(std::chrono::seconds(2)); - } - - void closeDeviceWithHandler(XLinkHandler_t* handler) { - ASSERT_EQ(X_LINK_SUCCESS, XLinkResetRemote(handler->linkId)); - free(handler); - std::this_thread::sleep_for(std::chrono::seconds(2)); - } - -}; - -TEST_F(XLinkTests, CanBootConnectAndResetDevice) { - char firmwarePath[MAX_PATH]; - deviceDesc_t deviceDesc = {}; - deviceDesc_t in_deviceDesc = {}; - in_deviceDesc.protocol = X_LINK_USB_VSC; - in_deviceDesc.platform = X_LINK_ANY_PLATFORM; - - ASSERT_EQ(X_LINK_SUCCESS, XLinkFindDevice(0, X_LINK_UNBOOTED, &in_deviceDesc, &deviceDesc)); - getFirmwarePath(deviceDesc.name, firmwarePath); - - ASSERT_EQ(X_LINK_SUCCESS, XLinkBootRemote(&deviceDesc, firmwarePath)); - // FIXME: need to find a way to avoid this sleep - std::this_thread::sleep_for(std::chrono::seconds(2)); - - deviceDesc_t bootedDesc = {}; - for (int i = 0; i < MAX_DEVICES; i++) { - if (X_LINK_SUCCESS == XLinkFindDevice(i, X_LINK_BOOTED, &in_deviceDesc, &bootedDesc)) { - break; - } - } - - XLinkHandler_t *handler = (XLinkHandler_t *)malloc(sizeof(XLinkHandler_t)); - handler->protocol = bootedDesc.protocol; - handler->devicePath = bootedDesc.name; - ASSERT_EQ(X_LINK_SUCCESS, XLinkConnect(handler)); - // FIXME: need to find a way to avoid this sleep - std::this_thread::sleep_for(std::chrono::seconds(1)); - - ASSERT_EQ(X_LINK_SUCCESS, XLinkResetRemote(handler->linkId)); - free(handler); - // FIXME: need to find a way to avoid this sleep - std::this_thread::sleep_for(std::chrono::seconds(2)); -} - -class XLinkOpenStreamTests : public XLinkTests { -protected: - virtual ~XLinkOpenStreamTests() { - - } - void SetUp() override { - deviceDesc_t deviceDesc = {}; - deviceDesc_t in_deviceDesc = {}; - in_deviceDesc.protocol = X_LINK_USB_VSC; - in_deviceDesc.platform = X_LINK_MYRIAD_X; - - ASSERT_EQ(X_LINK_SUCCESS, XLinkFindDevice(0, X_LINK_ANY_STATE, &in_deviceDesc, &deviceDesc)); - ASSERT_EQ(X_LINK_SUCCESS, XLinkBootRemote(&deviceDesc, "./lib/MvNCAPI-ma2480.mvcmd")); - - std::this_thread::sleep_for(std::chrono::seconds(1)); - - deviceDesc_t bootedDesc = {}; - for (int i = 0; i < MAX_DEVICES; i++) { - if (X_LINK_SUCCESS == XLinkFindDevice(i, X_LINK_BOOTED, &in_deviceDesc, &bootedDesc)) { - break; - } - } - - handler = (XLinkHandler_t *)malloc(sizeof(XLinkHandler_t)); - handler->protocol = bootedDesc.protocol; - handler->devicePath = bootedDesc.name; - ASSERT_EQ(X_LINK_SUCCESS, XLinkConnect(handler)); - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - - void TearDown() override { - ASSERT_EQ(X_LINK_SUCCESS, XLinkResetRemote(handler->linkId)); - free(handler); - // FIXME: need to find a way to avoid this sleep - std::this_thread::sleep_for(std::chrono::seconds(2)); - } - - XLinkHandler_t *handler; -}; - -TEST_F(XLinkOpenStreamTests, CanOpenAndCloseStream) { - streamId_t stream = XLinkOpenStream(handler->linkId, "mySuperStream", 1024); - ASSERT_NE(INVALID_STREAM_ID, stream); - ASSERT_NE(INVALID_STREAM_ID_OUT_OF_MEMORY, stream); - ASSERT_EQ(X_LINK_SUCCESS, XLinkCloseStream(stream)); -} - -TEST_F(XLinkOpenStreamTests, CannotOpenStreamMoreThanMemoryOnDevice) { - const int _512MB = 512 * 1024 * 1024; - streamId_t stream = XLinkOpenStream(handler->linkId, "mySuperStream", _512MB); - ASSERT_EQ(INVALID_STREAM_ID_OUT_OF_MEMORY, stream); -} - -// FIXME: the test doesn't work -// TODO: is it correct behavior, should we accept the same names -TEST_F(XLinkOpenStreamTests, DISABLED_CannotOpenTwoStreamsWithTheSameName) { - const int _1KB = 1 * 1024; - const char streamName[] = "mySuperStream"; - streamId_t stream0 = XLinkOpenStream(handler->linkId, streamName, _1KB); - ASSERT_NE(INVALID_STREAM_ID, stream0); - - streamId_t stream1 = XLinkOpenStream(handler->linkId, streamName, _1KB); - ASSERT_EQ(INVALID_STREAM_ID, stream1); - - ASSERT_EQ(X_LINK_SUCCESS, XLinkCloseStream(stream0)); -} - -// FIXME: XLinkOpenStream doesn't allocate any memory on device -TEST_F(XLinkOpenStreamTests, DISABLED_CannotOpenStreamsMoreThanMemoryOnDevice) { - const int _256MB = 256 * 1024 * 1024; - streamId_t stream0 = XLinkOpenStream(handler->linkId, "mySuperStream0", _256MB); - ASSERT_NE(INVALID_STREAM_ID, stream0); - - streamId_t stream1 = XLinkOpenStream(handler->linkId, "mySuperStream1", _256MB); - ASSERT_EQ(INVALID_STREAM_ID, stream1); - - ASSERT_EQ(X_LINK_SUCCESS, XLinkCloseStream(stream0)); - ASSERT_EQ(X_LINK_SUCCESS, XLinkCloseStream(stream1)); -} - -/** - * @brief XLinkGetDeviceName function tests - */ -class XLinkGetDeviceNameTests : public XLinkTests { -protected: - ~XLinkGetDeviceNameTests() override = default; -}; - -// TODO Can compose list of all devices - -/** - * @brief XLinkGetDeviceName should return error if index argument is invalid - */ -TEST_F(XLinkGetDeviceNameTests, ReturnErrorOnIncorrectIndex) { - deviceDesc_t deviceDesc = {}; - deviceDesc_t in_deviceDesc = {}; - in_deviceDesc.protocol = X_LINK_USB_VSC; - in_deviceDesc.platform = X_LINK_ANY_PLATFORM; - - ASSERT_EQ(X_LINK_ERROR, XLinkFindDevice(-1, X_LINK_ANY_STATE, &in_deviceDesc, &deviceDesc)); - ASSERT_TRUE(strlen(deviceDesc.name) == 0); -} - -/** - * @brief XLinkGetDeviceName should return device name in AUTO_PID mode (pid = 0) - */ -TEST_F(XLinkGetDeviceNameTests, ReturnAnyDeviceName) { - deviceDesc_t deviceDesc = {}; - deviceDesc_t in_deviceDesc = {}; - in_deviceDesc.protocol = X_LINK_USB_VSC; - in_deviceDesc.platform = X_LINK_ANY_PLATFORM; - - ASSERT_EQ(X_LINK_SUCCESS, XLinkFindDevice(0, X_LINK_ANY_STATE, &in_deviceDesc, &deviceDesc)); - ASSERT_TRUE(strlen(deviceDesc.name) > 2); -} - -/** - * @brief XLinkGetDeviceName should return M2 device name if pid = MYRIAD2 (0x2150) - */ -TEST_F(XLinkGetDeviceNameTests, ReturnCorrectM2DeviceName) { - deviceDesc_t deviceDesc = {}; - deviceDesc_t in_deviceDesc = {}; - in_deviceDesc.protocol = X_LINK_USB_VSC; - in_deviceDesc.platform = X_LINK_MYRIAD_2; - - ASSERT_EQ(X_LINK_SUCCESS, XLinkFindDevice(0, X_LINK_ANY_STATE, &in_deviceDesc, &deviceDesc)); - ASSERT_TRUE(strstr(deviceDesc.name, "ma2450") != nullptr); -} - -/** - * @brief XLinkGetDeviceName should return MX device name if pid = MYRIADX (0x2485) - */ -TEST_F(XLinkGetDeviceNameTests, ReturnCorrectMXDeviceName) { - deviceDesc_t deviceDesc = {}; - deviceDesc_t in_deviceDesc = {}; - in_deviceDesc.protocol = X_LINK_USB_VSC; - in_deviceDesc.platform = X_LINK_MYRIAD_X; - - ASSERT_EQ(X_LINK_SUCCESS,XLinkFindDevice(0, X_LINK_ANY_STATE, &in_deviceDesc, &deviceDesc)); - ASSERT_TRUE(strstr(deviceDesc.name, "ma2480") != nullptr); -} - -/** - * @brief XLinkGetDeviceName should return booted MX device name if pid = MYRIAD_BOOTED (0xf63b) - */ -TEST_F(XLinkGetDeviceNameTests, ReturnCorrectBootedDeviceName) { - bootAnyDevice(); - - deviceDesc_t bootedDeviceDescr = {}; - deviceDesc_t in_deviceDesc = {}; - in_deviceDesc.protocol = X_LINK_USB_VSC; - in_deviceDesc.platform = X_LINK_ANY_PLATFORM; - - ASSERT_EQ(X_LINK_SUCCESS, XLinkFindDevice(0, X_LINK_BOOTED, &in_deviceDesc, &bootedDeviceDescr)); - ASSERT_TRUE(strstr(bootedDeviceDescr.name, "ma2480") == nullptr); - ASSERT_TRUE(strstr(bootedDeviceDescr.name, "ma2450") == nullptr); - - closeDevice(bootedDeviceDescr.name); -} - -/** - * @brief XLinkResetAll function tests - */ -class XLinkResetAllTests : public XLinkTests { -protected: - ~XLinkResetAllTests() override = default; -}; - -/** - * @brief XLinkResetAll function should reset all booted devices - */ -TEST_F(XLinkResetAllTests, ResetBootedDevice) { - // TODO Boot all available devices - bootAnyDevice(); - - // Without connection to device XLinkResetAll doesn't work - // Connect to device - deviceDesc_t bootedDeviceDescr = {}; - deviceDesc_t in_deviceDesc = {}; - in_deviceDesc.protocol = X_LINK_USB_VSC; - in_deviceDesc.platform = X_LINK_ANY_PLATFORM; - - ASSERT_EQ(X_LINK_SUCCESS, XLinkFindDevice(0, X_LINK_BOOTED, &in_deviceDesc, &bootedDeviceDescr)); - - XLinkHandler_t *handler = (XLinkHandler_t *)malloc(sizeof(XLinkHandler_t)); - handler->protocol = bootedDeviceDescr.protocol; - handler->devicePath = bootedDeviceDescr.name; - ASSERT_EQ(X_LINK_SUCCESS, XLinkConnect(handler)); - - // Try to reset device - ASSERT_EQ(X_LINK_SUCCESS, XLinkResetAll()); - std::this_thread::sleep_for(std::chrono::seconds(2)); - - // No one booted device should be found - deviceDesc_t afterResetBootedDescr = {}; - ASSERT_EQ(X_LINK_DEVICE_NOT_FOUND, XLinkFindDevice(0, X_LINK_BOOTED, &in_deviceDesc, &afterResetBootedDescr)); -} - -/** - * @brief XLinkConnect function tests - */ -class XLinkConnectTests : public XLinkTests { -protected: - ~XLinkConnectTests() override = default; -}; - -TEST_F(XLinkConnectTests, InvalidHanler) { - ASSERT_EQ(X_LINK_ERROR, XLinkConnect(nullptr)); -} - -TEST_F(XLinkConnectTests, ConnectToDevice) { - bootAnyDevice(); - - deviceDesc_t bootedDeviceDescr = {}; - deviceDesc_t in_deviceDesc = {}; - in_deviceDesc.protocol = X_LINK_USB_VSC; - in_deviceDesc.platform = X_LINK_ANY_PLATFORM; - - ASSERT_EQ(X_LINK_SUCCESS, XLinkFindDevice(0, X_LINK_BOOTED, &in_deviceDesc, &bootedDeviceDescr)); - - XLinkHandler_t *handler = (XLinkHandler_t *)malloc(sizeof(XLinkHandler_t)); - handler->protocol = bootedDeviceDescr.protocol; - handler->devicePath = bootedDeviceDescr.name; - ASSERT_EQ(X_LINK_SUCCESS, XLinkConnect(handler)); - - closeDeviceWithHandler(handler); -} - -class XLinkBootRemoteTests: public XLinkTests { -public: - ~XLinkBootRemoteTests() override = default; -}; - -TEST_F(XLinkBootRemoteTests, USBDeviceNameChangedAfterBoot) { - deviceDesc_t unbootedDeviceDescr = {}; - deviceDesc_t in_deviceDesc = {}; - in_deviceDesc.protocol = X_LINK_USB_VSC; - in_deviceDesc.platform = X_LINK_ANY_PLATFORM; - char firmwarePath[MAX_PATH]; - - // Get device name - ASSERT_EQ(X_LINK_SUCCESS, XLinkFindDevice(0, X_LINK_UNBOOTED, &in_deviceDesc, &unbootedDeviceDescr)); - getFirmwarePath(unbootedDeviceDescr.name, firmwarePath); - - // Boot device - ASSERT_EQ(X_LINK_SUCCESS, XLinkBootRemote(&unbootedDeviceDescr, firmwarePath)); - std::this_thread::sleep_for(std::chrono::seconds(2)); - - // Booted device appear - deviceDesc_t bootedDeviceDesc = {}; - ASSERT_EQ(X_LINK_SUCCESS, XLinkFindDevice(0, X_LINK_BOOTED, &in_deviceDesc, &bootedDeviceDesc)); - - // Previous device don't disappear - bool before_booted_found = false; - deviceDesc_t deviceDesc = {}; - for (int i = 0; i < MAX_DEVICES; i++) { - if (X_LINK_SUCCESS == XLinkFindDevice(i, X_LINK_UNBOOTED, &in_deviceDesc, &deviceDesc)) { - if (strcmp(deviceDesc.name, unbootedDeviceDescr.name) == 0) { - before_booted_found = true; - } - break; - } - } - - ASSERT_FALSE(before_booted_found); - - closeDevice(bootedDeviceDesc.name); -} \ No newline at end of file diff --git a/inference-engine/thirdparty/movidius/mvnc/CMakeLists.txt b/inference-engine/thirdparty/movidius/mvnc/CMakeLists.txt index 8d61aae0a1e1c3..9fb2e998cc78db 100644 --- a/inference-engine/thirdparty/movidius/mvnc/CMakeLists.txt +++ b/inference-engine/thirdparty/movidius/mvnc/CMakeLists.txt @@ -18,11 +18,12 @@ endif() file(GLOB MVNC_SOURCES "include/*" "src/*") file(GLOB WATCHDOG_SOURCES "../watchdog/*") -# FIXME: WIN_PTHREAD also should be built as a library if(WIN32) file(GLOB USB_WIN_SOURCES "../USB_WIN/*") file(GLOB WIN_PTHREAD_SOURCES "../WinPthread/*") list(APPEND ${MVNC_SOURCES} ${USB_WIN_SOURCES} ${WIN_PTHREAD_SOURCES}) +else() + list(APPEND ${MVNC_SOURCES} "../WinPthread/pthread_semaphore.c") endif() add_library(${TARGET_NAME} STATIC ${MVNC_SOURCES} ${WATCHDOG_SOURCES}) @@ -43,6 +44,7 @@ endif() if(UNIX) target_include_directories(${TARGET_NAME} PRIVATE + "../WinPthread" "${LIBUSB_INCLUDE_DIR}") endif() diff --git a/inference-engine/thirdparty/movidius/mvnc/include/mvnc.h b/inference-engine/thirdparty/movidius/mvnc/include/mvnc.h index 5dc90c7bb1a928..0b0ca7530c9810 100644 --- a/inference-engine/thirdparty/movidius/mvnc/include/mvnc.h +++ b/inference-engine/thirdparty/movidius/mvnc/include/mvnc.h @@ -135,6 +135,8 @@ typedef enum { NC_RO_DEVICE_ID = 2016, // returns device id NC_RO_DEVICE_PLATFORM = 2017, // returns device platform (MyriadX, Myriad2) NC_RO_DEVICE_PROTOCOL = 2018, // returns device protocol (USB, PCIe) + NC_RW_DEVICE_POWER_CONFIG = 2400, // writes config for the power manager to device + NC_RW_DEVICE_POWER_CONFIG_RESET = 2401, // resets power manager config on device } ncDeviceOption_t; typedef enum { diff --git a/inference-engine/thirdparty/movidius/mvnc/include/mvnc_data.h b/inference-engine/thirdparty/movidius/mvnc/include/mvnc_data.h index 07c8990c4790c6..7b8838c9b8cbcd 100644 --- a/inference-engine/thirdparty/movidius/mvnc/include/mvnc_data.h +++ b/inference-engine/thirdparty/movidius/mvnc/include/mvnc_data.h @@ -1,3 +1,7 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + #ifndef _MVNC_DATA_H #define _MVNC_DATA_H diff --git a/inference-engine/thirdparty/movidius/mvnc/include/mvnc_tool.h b/inference-engine/thirdparty/movidius/mvnc/include/mvnc_tool.h index 431eec990e2e8f..f83a12b72962cc 100644 --- a/inference-engine/thirdparty/movidius/mvnc/include/mvnc_tool.h +++ b/inference-engine/thirdparty/movidius/mvnc/include/mvnc_tool.h @@ -1,3 +1,7 @@ +// Copyright (C) 2018-2019 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + #ifndef _MVNC_TOOL_H #define _MVNC_TOOL_H diff --git a/inference-engine/thirdparty/movidius/mvnc/include/ncCommPrivate.h b/inference-engine/thirdparty/movidius/mvnc/include/ncCommPrivate.h index e2c8de82fbe6e3..46e46c1e180ec5 100644 --- a/inference-engine/thirdparty/movidius/mvnc/include/ncCommPrivate.h +++ b/inference-engine/thirdparty/movidius/mvnc/include/ncCommPrivate.h @@ -155,12 +155,19 @@ typedef enum { CLASS3_SET_LOG_LEVEL_XLINK, } deviceOptionClass3; +typedef enum { + CLASS4_SET_POWER_CONFIG = 0, + CLASS4_GET_POWER_CONFIG, + CLASS4_RESET_POWER_CONFIG, +} deviceOptionClass4; + typedef struct { union { deviceOptionClass0 c0; deviceOptionClass1 c1; deviceOptionClass2 c2; deviceOptionClass3 c3; + deviceOptionClass4 c4; } type; uint32_t optionClass; uint32_t data; diff --git a/inference-engine/thirdparty/movidius/mvnc/include/ncPrivateTypes.h b/inference-engine/thirdparty/movidius/mvnc/include/ncPrivateTypes.h index a91a0c8e57bb13..a7c4380980d469 100644 --- a/inference-engine/thirdparty/movidius/mvnc/include/ncPrivateTypes.h +++ b/inference-engine/thirdparty/movidius/mvnc/include/ncPrivateTypes.h @@ -19,10 +19,13 @@ #include "watchdog.h" typedef enum { - NC_OPTION_CLASS0 = 0, - NC_OPTION_CLASS1 = 1, - NC_OPTION_CLASS2 = 2, - NC_OPTION_CLASS3 = 3, + NC_OPTION_CLASS0 = 0, + NC_OPTION_CLASS1 = 1, + NC_OPTION_CLASS2 = 2, + NC_OPTION_CLASS3 = 3, + NC_OPTION_CLASS4 = 4, + NC_OPTION_LAST = 4, // Last configuration option available + NC_OPTION_GRAPH_LAST = 2, // Last configuration option available for graph } ncOptionClass_t; typedef enum { diff --git a/inference-engine/thirdparty/movidius/mvnc/src/mvnc_api.c b/inference-engine/thirdparty/movidius/mvnc/src/mvnc_api.c index af6f9ce85c4909..94638c8f01417a 100644 --- a/inference-engine/thirdparty/movidius/mvnc/src/mvnc_api.c +++ b/inference-engine/thirdparty/movidius/mvnc/src/mvnc_api.c @@ -53,9 +53,10 @@ #define MAX_RELATED_PATH_LENGTH 100 // Timeouts -#define STATUS_WAIT_TIMEOUT 15 -#define DEVICE_APPEAR_TIMEOUT_ON_OPEN (2) -#define DEVICE_APPEAR_TIMEOUT_ON_CLOSE (10) +#define DEVICE_CONNECT_TIMEOUT (2) +#define PCIE_DEVICE_CONNECT_TIMEOUT (10) +#define DEVICE_APPEAR_TIMEOUT_ON_OPEN (2) +#define DEVICE_APPEAR_TIMEOUT_ON_CLOSE (10) #define SLEEP_MS 250 #define MAX_ITERATIONS 20 @@ -94,19 +95,20 @@ static int global_lock_fd = -1; * @param errorMsg Message to be written in case of error. It is a format string */ #ifndef CHECK_STREAM_ID -#define CHECK_STREAM_ID(id, callReleasingResources, errorMsg) { \ - char errorMsgWithReason[255]; \ - if (id == INVALID_STREAM_ID_OUT_OF_MEMORY) { \ - snprintf(errorMsgWithReason, 255, "%s %s", errorMsg, "due to not enough memory on device"); \ - mvLog(MVLOG_ERROR, errorMsgWithReason); \ - callReleasingResources; \ - return NC_OUT_OF_MEMORY; \ - } else if (id == INVALID_STREAM_ID) { \ - snprintf(errorMsgWithReason, 255, "%s %s", errorMsg, "due to unknown error"); \ - callReleasingResources; \ - return NC_ERROR; \ - } \ - mvLog(MVLOG_DEBUG, "Stream opened"); \ +#define CHECK_STREAM_ID(id, callReleasingResources, errorMsg) { \ + char errorMsgWithReason[255]; \ + if (id == INVALID_STREAM_ID_OUT_OF_MEMORY) { \ + snprintf(errorMsgWithReason, 255, "%s %s", errorMsg, "due to not enough memory on device"); \ + mvLog(MVLOG_ERROR, errorMsgWithReason); \ + callReleasingResources; \ + return NC_OUT_OF_MEMORY; \ + } else if (id == INVALID_STREAM_ID) { \ + snprintf(errorMsgWithReason, 255, "%s %s", errorMsg, "due to unknown error"); \ + mvLog(MVLOG_ERROR, errorMsgWithReason); \ + callReleasingResources; \ + return NC_ERROR; \ + } \ + mvLog(MVLOG_DEBUG, "Stream opened"); \ } #endif // CHECK_STREAM_ID @@ -206,7 +208,7 @@ static void sleepForSeconds(const unsigned int seconds) { static char* getProductName(const char* name) { -#if (defined(_WIN32) || defined(_WIN64)) && !defined(PCIE_NAME_STR) +#if (defined(_WIN32) || defined(_WIN64)) const char PCIeName[] = "mxlink"; #else const char PCIeName[] = "mxlk"; @@ -284,80 +286,71 @@ static void resetAll() #if defined(NO_BOOT) mvLog(MVLOG_INFO, "Devices will not be restarted for this configuration (NO_BOOT)"); #else - int index = 0; - int stalled_count = 0; - int iters = 0; - int bootrom_count = 0; - int after_reset_count = 0; - XLinkError_t rc; - deviceDesc_t out_deviceDesc; + // Reset only USB devices deviceDesc_t in_deviceDesc = { .protocol = X_LINK_USB_VSC, - .platform = NC_ANY_PLATFORM + .platform = X_LINK_ANY_PLATFORM }; - double waittm = timeInSeconds() + STATUS_WAIT_TIMEOUT; - while (timeInSeconds() < waittm) { - rc = XLinkFindDevice(index, X_LINK_ANY_STATE, &in_deviceDesc, &out_deviceDesc); - if (rc != X_LINK_SUCCESS) - break; //no more devices found + unsigned int stalled_count = 0; + deviceDesc_t stalledDevices[NC_MAX_DEVICES] = {}; - if (strlen(getProductName(out_deviceDesc.name)) == 1 && - out_deviceDesc.protocol != X_LINK_PCIE) { //name doesn't have product number - //device is already booted, need to reset - mvLog(MVLOG_DEBUG,"Found stalled device %s\n", out_deviceDesc.name); - XLinkHandler_t* handler = calloc(1, sizeof(XLinkHandler_t)); + unsigned int stalled_count_after_reboot = 0; + + + double waittm = timeInSeconds() + DEVICE_APPEAR_TIMEOUT_ON_OPEN; + do { + // Find stalled devices + stalled_count = 0; + XLinkFindAllSuitableDevices( + X_LINK_BOOTED, in_deviceDesc, stalledDevices, NC_MAX_DEVICES, &stalled_count); + + if (stalled_count) { + mvLog(MVLOG_INFO, "%d stalled devices found, Resetting...", stalled_count); + } else { + mvLog(MVLOG_DEBUG, "Stalled devices not found"); + return; + } + + // Try to reboot them + int i; + for (i = 0; i < stalled_count; ++i) { + mvLog(MVLOG_DEBUG, "Found stalled device %s", stalledDevices[i].name); + XLinkHandler_t* handler = calloc(1, sizeof(XLinkHandler_t)); if (!handler){ mvLog(MVLOG_ERROR, "Memory allocation failed"); - break; + return; } - handler->protocol = out_deviceDesc.protocol; - handler->devicePath = (char*)out_deviceDesc.name; - rc = XLinkConnect(handler); + + handler->protocol = stalledDevices[i].protocol; + handler->devicePath = (char*)stalledDevices[i].name; + XLinkError_t rc = XLinkConnect(handler); if (rc) { mvLog(MVLOG_ERROR," Failed to connect to stalled device, rc: %s", XLinkErrorToStr(rc)); + } else { + } - stalled_count++; free(handler); - - } else { - bootrom_count++; } - index++; - } - if (stalled_count) { - mvLog(MVLOG_INFO,"Stalled devices found, Reseting..."); - rc = XLinkResetAll(); + // This command will reset all previously connected devices + XLinkError_t rc = XLinkResetAll(); if (rc) { mvLog(MVLOG_WARN,"Failed to reset all device, rc: %s", XLinkErrorToStr(rc)); } - iters = 0; - - while ((after_reset_count < bootrom_count + stalled_count) && - iters < MAX_ITERATIONS) { - usleep(SLEEP_MS*1000); - after_reset_count = 0; - index = 0; - waittm = timeInSeconds() + STATUS_WAIT_TIMEOUT; - while (timeInSeconds() < waittm) { - XLinkError_t rc = XLinkFindDevice(index, X_LINK_ANY_STATE, &in_deviceDesc, &out_deviceDesc); - if (rc != X_LINK_SUCCESS) - break; //no more devices found + // Check that all devices are rebooted + stalled_count_after_reboot = 0; + deviceDesc_t stalledDevicesAfterReboot[NC_MAX_DEVICES] = {}; + XLinkFindAllSuitableDevices( + X_LINK_BOOTED, in_deviceDesc, + stalledDevicesAfterReboot, NC_MAX_DEVICES, &stalled_count_after_reboot); - if (strlen(getProductName(out_deviceDesc.name)) > 1 && - out_deviceDesc.protocol != X_LINK_PCIE) { //name has product number - after_reset_count++; - } - index++; - } - iters++; - mvLog(MVLOG_INFO,"..."); - } + mvLog(MVLOG_INFO,"..."); usleep(SLEEP_MS*1000); - } + + } while (stalled_count_after_reboot > 0 && timeInSeconds() < waittm); #endif } @@ -380,7 +373,7 @@ ncStatus_t ncDeviceResetAll() { static ncStatus_t initializeXLink() { - XLinkSetCommonTimeOutMsec(3 * 60 * 10000); + XLinkSetCommonTimeOutMsec(60 * 1000); // We sanitize the situation by trying to reset the devices that have been left open initialized = 1; devices = NULL; @@ -399,17 +392,6 @@ static ncStatus_t initializeXLink() return NC_OK; } -static int isDeviceOpened(const char *name) -{ - struct _devicePrivate_t *d = devices; - while (d) { - if (strcmp(d->dev_addr, name) == 0) - return 0; - d = d->next; - } - return -1; -} - /** * @brief Check is path exists (directory or file) */ @@ -564,7 +546,11 @@ static ncStatus_t destroyDeviceHandle(struct ncDeviceHandle_t **deviceHandlePtr) ncStatus_t ncDeviceOpen(struct ncDeviceHandle_t **deviceHandlePtr, struct ncDeviceDescr_t in_ncDeviceDesc, int watchdogInterval, const char* customFirmwareDirectory) { - deviceDesc_t out_deviceDesc = {0}; + + //---------------------------------------------------------- + // Check input + + deviceDesc_t deviceDescToBoot = {0}; deviceDesc_t in_deviceDesc = {0}; copyNcDeviceDescrToXLink(&in_ncDeviceDesc, &in_deviceDesc); @@ -595,7 +581,8 @@ ncStatus_t ncDeviceOpen(struct ncDeviceHandle_t **deviceHandlePtr, return NC_OK; } - // Initialize handler + //-------------------------------------------------------- + // Initialize global mutex and mutex for deviceOpen if (!initialized) { #if (defined(_WIN32) || defined(_WIN64)) @@ -626,7 +613,13 @@ ncStatus_t ncDeviceOpen(struct ncDeviceHandle_t **deviceHandlePtr, } GLOBAL_LOCK(); - CHECK_MUTEX_SUCCESS_RC(pthread_mutex_lock(&deviceOpenMutex), NC_ERROR); + int error = pthread_mutex_lock(&deviceOpenMutex); + if (error) { + GLOBAL_UNLOCK(); + mvLog(MVLOG_ERROR, "pthread_mutex_lock(&deviceOpenMutex) failed with error: %d", error); + return NC_ERROR; + } + if (!initialized) { ncStatus_t sc; if ((sc = initializeXLink()) != 0) { @@ -636,18 +629,19 @@ ncStatus_t ncDeviceOpen(struct ncDeviceHandle_t **deviceHandlePtr, } } + //-------------------------------------------------------- + // Search for device + #if defined(NO_BOOT) XLinkDeviceState_t state = X_LINK_BOOTED; #else XLinkDeviceState_t state = X_LINK_UNBOOTED; #endif - // Find any unbooted device or booted device and create deviceHandle - // TODO: PCIE could be found at once. Otherwise, it would cause a lot of errors about the opening file error. XLinkError_t rc = X_LINK_ERROR; double waittm = timeInSeconds() + DEVICE_APPEAR_TIMEOUT_ON_OPEN; while ((rc != X_LINK_SUCCESS) && (timeInSeconds() < waittm)) { - rc = XLinkFindDevice(0, state, &in_deviceDesc, &out_deviceDesc); + rc = XLinkFindFirstSuitableDevice(state, in_deviceDesc, &deviceDescToBoot); } if (rc != X_LINK_SUCCESS) { @@ -665,15 +659,16 @@ ncStatus_t ncDeviceOpen(struct ncDeviceHandle_t **deviceHandlePtr, return parseXLinkError(NC_ERROR); } - // Allocate handler + //-------------------------------------------------------- + // Allocate device handler struct ncDeviceHandle_t *dH = calloc(1, sizeof(*dH)); struct _devicePrivate_t *d = calloc(1, sizeof(*d)); if (dH && d) { dH->private_data = d; - d->protocol = out_deviceDesc.protocol; - d->dev_addr = strdup(out_deviceDesc.name); + d->protocol = deviceDescToBoot.protocol; + d->dev_addr = strdup(deviceDescToBoot.name); d->device_mon_stream_id = INVALID_LINK_ID; d->graph_monitor_stream_id = INVALID_LINK_ID; d->wd_interval = watchdogInterval; @@ -694,7 +689,9 @@ ncStatus_t ncDeviceOpen(struct ncDeviceHandle_t **deviceHandlePtr, return NC_OUT_OF_MEMORY; } - // Boot device + //-------------------------------------------------------- + // Boot device + XLinkHandler_t* handler = calloc(1, sizeof(XLinkHandler_t)); if (!handler) { mvLog(MVLOG_ERROR, "Memory allocation failed"); @@ -716,9 +713,9 @@ ncStatus_t ncDeviceOpen(struct ncDeviceHandle_t **deviceHandlePtr, rc = XLinkConnect(handler); #else if (handler->protocol == X_LINK_PCIE) { // PCIe -#if (!defined(_WIN32) && !defined(_WIN64)) ncStatus_t sc; char mv_cmd_file_path[MAX_PATH_LENGTH] = {}; +#if (!defined(_WIN32) && !defined(_WIN64)) if (customFirmwareDirectory && strnlen(customFirmwareDirectory, MAX_PATH_LENGTH) > 1) { mv_strncpy(mv_cmd_file_path, MAX_PATH_LENGTH, customFirmwareDirectory, MAX_PATH_LENGTH - 1); addEndPathSeparator(mv_cmd_file_path); @@ -733,7 +730,8 @@ ncStatus_t ncDeviceOpen(struct ncDeviceHandle_t **deviceHandlePtr, GLOBAL_UNLOCK(); return NC_MVCMD_NOT_FOUND; } - rc = XLinkBootRemote(&out_deviceDesc, mv_cmd_file_path); +#endif + rc = XLinkBoot(&deviceDescToBoot, mv_cmd_file_path); if (rc) { mvLog(MVLOG_WARN, "%s() XLinkBootRemote returned error %s for %s", __func__, XLinkErrorToStr(rc), d->dev_addr); @@ -741,12 +739,36 @@ ncStatus_t ncDeviceOpen(struct ncDeviceHandle_t **deviceHandlePtr, mvLog(MVLOG_INFO, "%s() XLinkBootRemote returned success %s for %s", __func__, XLinkErrorToStr(rc), d->dev_addr); } -#endif + // Search for booted device + deviceDesc_t tempDeviceDesc = {}; + waittm = timeInSeconds() + DEVICE_APPEAR_TIMEOUT_ON_CLOSE; + do { + rc = XLinkFindFirstSuitableDevice(X_LINK_BOOTED, deviceDescToBoot, &tempDeviceDesc); + } while (rc != X_LINK_SUCCESS && timeInSeconds() < waittm); + + if (rc != X_LINK_SUCCESS) { + mvLog(MVLOG_ERROR, "Device doesn't appear after boot"); + free(handler); + destroyDeviceHandle(deviceHandlePtr); + CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&deviceOpenMutex)); + GLOBAL_UNLOCK(); + return NC_ERROR; + } + d->protocol_booted = d->protocol; d->dev_addr_booted = strdup(d->dev_addr); handler->protocol = d->protocol_booted; handler->devicePath = d->dev_addr_booted; - rc = XLinkConnect(handler); + + // FIXME BSOD +#if (defined(_WIN32) || defined(_WIN64)) + sleepForSeconds(5); +#endif + // Connect to booted + waittm = timeInSeconds() + PCIE_DEVICE_CONNECT_TIMEOUT; + do { + rc = XLinkConnect(handler); + } while(rc != X_LINK_SUCCESS && timeInSeconds() < waittm); } else { // USB // Find firmware and boot device with it char mv_cmd_file_path[MAX_PATH_LENGTH] = {}; @@ -770,20 +792,19 @@ ncStatus_t ncDeviceOpen(struct ncDeviceHandle_t **deviceHandlePtr, mvLog(MVLOG_INFO, "%s() XLinkBootRemote is running for %s...\n", __func__, d->dev_addr); - // remember all currently available devices + // Remember all currently available devices deviceDesc_t beforeBootDevices[NC_MAX_DEVICES] = {{0}}; - deviceDesc_t simpleDeviceDesc = { - .platform = NC_ANY_PLATFORM, - .protocol = convertProtocolToXlink(in_ncDeviceDesc.protocol) + unsigned int numberOfDevicesBeforeBoot = 0; + deviceDesc_t deviceDesc = { + .platform = X_LINK_ANY_PLATFORM, + .protocol = X_LINK_USB_VSC }; - int n = 0; - for (; n < NC_MAX_DEVICES; ++n) { - if (XLinkFindDevice(n, X_LINK_ANY_STATE, &simpleDeviceDesc, &beforeBootDevices[n])) - break; - } + XLinkFindAllSuitableDevices(X_LINK_ANY_STATE, deviceDesc, beforeBootDevices, + NC_MAX_DEVICES, &numberOfDevicesBeforeBoot); - rc = XLinkBootRemote(&out_deviceDesc, mv_cmd_file_path); + // Boot device + rc = XLinkBoot(&deviceDescToBoot, mv_cmd_file_path); if (rc) { mvLog(MVLOG_WARN, "%s() XLinkBootRemote returned error %s for %s", __func__, XLinkErrorToStr(rc), d->dev_addr); @@ -792,99 +813,139 @@ ncStatus_t ncDeviceOpen(struct ncDeviceHandle_t **deviceHandlePtr, __func__, XLinkErrorToStr(rc), d->dev_addr); } - deviceDesc_t booted_device = {0}; + // After boot name should change. Find + deviceDesc_t foundBootedDevice = {0}; + int found_new_booted_device = 0; - // After boot name should change - double waittm = timeInSeconds() + STATUS_WAIT_TIMEOUT; - int deviceBooted = 0; - while ((timeInSeconds() < waittm) && !deviceBooted) { - int dev_indx = 0; - for (; dev_indx < NC_MAX_DEVICES; ++dev_indx) { - rc = XLinkFindDevice(dev_indx, X_LINK_ANY_STATE, &simpleDeviceDesc, &booted_device); - booted_device.name[NC_MAX_NAME_SIZE - 1] = 0; - if (rc != X_LINK_SUCCESS) - break; + deviceDesc_t afterBootDevices[NC_MAX_DEVICES] = {{0}}; + unsigned int numberOfDevicesAfterBoot = 0; + + waittm = timeInSeconds() + DEVICE_APPEAR_TIMEOUT_ON_OPEN; + do { + XLinkFindAllSuitableDevices(X_LINK_ANY_STATE, deviceDesc, afterBootDevices, + NC_MAX_DEVICES, &numberOfDevicesAfterBoot); + if (numberOfDevicesAfterBoot != numberOfDevicesBeforeBoot) { + continue; + } + deviceDesc_t tempDevicDescr = {}; - // if beforeBootDevices contains booted_name this is not a device we are looking for - int not_found = 0; - n = 0; - for (; n < NC_MAX_DEVICES; ++n) { - if (strcmp(booted_device.name, beforeBootDevices[n].name) == 0 || - booted_device.protocol == X_LINK_PCIE) { - not_found = 1; - break; + // Device should disappear from unbooted list + if (X_LINK_DEVICE_NOT_FOUND != XLinkFindFirstSuitableDevice( + X_LINK_ANY_STATE, + deviceDescToBoot, + &tempDevicDescr)) { + continue; + } + int i, j; + for (i = 0; i < numberOfDevicesAfterBoot; ++i) { + int found_in_before_boot_list = 0; + for (j = 0; j < numberOfDevicesBeforeBoot; ++j) { + if(strcmp(afterBootDevices[i].name, beforeBootDevices[j].name) == 0) { + found_in_before_boot_list = 1; } } - - if (not_found) - continue; - handler->protocol = booted_device.protocol; - handler->devicePath = (char *) booted_device.name; - - rc = XLinkConnect(handler); - // Device mustn't be in devices pool - if (isDeviceOpened(booted_device.name) < 0 && rc == X_LINK_SUCCESS) { - deviceBooted = 1; - d->protocol_booted = booted_device.protocol; - d->dev_addr_booted = strdup(booted_device.name); + if (!found_in_before_boot_list) { + mv_strcpy(foundBootedDevice.name, XLINK_MAX_NAME_SIZE, + afterBootDevices[i].name); + foundBootedDevice.platform = afterBootDevices[i].platform; + foundBootedDevice.protocol = afterBootDevices[i].protocol; + found_new_booted_device = 1; break; } } + } while (!found_new_booted_device && timeInSeconds() < waittm); + + if (!found_new_booted_device) { + mvLog(MVLOG_ERROR, "Device doesn't appear after boot"); + free(handler); + destroyDeviceHandle(deviceHandlePtr); + CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&deviceOpenMutex)); + GLOBAL_UNLOCK(); + return NC_ERROR; + } + + // Connect to booted + waittm = timeInSeconds() + DEVICE_CONNECT_TIMEOUT; + + d->protocol_booted = d->protocol; + d->dev_addr_booted = strdup(foundBootedDevice.name); + + handler->protocol = foundBootedDevice.protocol; + handler->devicePath = (char *) foundBootedDevice.name; + do { + rc = XLinkConnect(handler); + } while(rc != X_LINK_SUCCESS && timeInSeconds() < waittm); + + if (rc != X_LINK_SUCCESS) { + mvLog(MVLOG_ERROR, "Device doesn't appear after boot"); + free(handler); + destroyDeviceHandle(deviceHandlePtr); + CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&deviceOpenMutex)); + GLOBAL_UNLOCK(); + return NC_ERROR; } } #endif if (rc != X_LINK_SUCCESS) { - // If PCIE device was booted then we will find it but can not connect. - mvLog_t logLevel = MVLOG_ERROR; - if(in_deviceDesc.protocol == X_LINK_PCIE) { - logLevel = MVLOG_WARN; - } - - mvLog(logLevel, "Failed connection to device (%s) with error %d", d->dev_addr, rc); + mvLog(MVLOG_ERROR, "Failed connection to device (%s) with error %d", d->dev_addr, rc); free(handler); destroyDeviceHandle(deviceHandlePtr); CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&deviceOpenMutex)); GLOBAL_UNLOCK(); return parseXLinkError(rc); } + + // After this line calling free(handler) and destroyDeviceHandle after each other is double-free corruption + d->xlink = handler; + d->next = devices; + + // Check device handle + if (d->dev_addr == NULL || d->dev_addr_booted == NULL || d->xlink == NULL) { + mvLog(MVLOG_ERROR, "device is invalid"); + destroyDeviceHandle(deviceHandlePtr); + CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&deviceOpenMutex)); + GLOBAL_UNLOCK(); + return NC_INVALID_HANDLE; + } + + devices = d; + mvLog(MVLOG_INFO, "XLinkConnect done - link Id %d\n", handler->linkId); - int error = 0; if ((error = pthread_mutex_init(&d->dev_data_m, NULL)) != 0) { mvLog(MVLOG_ERROR, "pthread_mutex_init (dev_data_m) failed with error: %d", error); - free(handler); destroyDeviceHandle(deviceHandlePtr); + CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&deviceOpenMutex)); + GLOBAL_UNLOCK(); return NC_ERROR; } // If current mutex initialization failed, destroy previous if ((error = pthread_mutex_init(&d->dev_stream_m, NULL)) != 0) { mvLog(MVLOG_ERROR, "pthread_mutex_init (dev_stream_m) failed with error: %d", error); CHECK_MUTEX_SUCCESS(pthread_mutex_destroy(&d->dev_data_m)); - free(handler); destroyDeviceHandle(deviceHandlePtr); + CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&deviceOpenMutex)); + GLOBAL_UNLOCK(); return NC_ERROR; } if ((error = pthread_mutex_init(&d->graph_stream_m, NULL)) != 0) { mvLog(MVLOG_ERROR, "pthread_mutex_init (graph_stream_m) failed with error: %d", error); CHECK_MUTEX_SUCCESS(pthread_mutex_destroy(&d->dev_data_m)); CHECK_MUTEX_SUCCESS(pthread_mutex_destroy(&d->dev_stream_m)); - free(handler); destroyDeviceHandle(deviceHandlePtr); + CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&deviceOpenMutex)); + GLOBAL_UNLOCK(); return NC_ERROR; } - d->xlink = handler; - d->next = devices; - devices = d; - if (handler->protocol != X_LINK_PCIE) { mvLog(MVLOG_INFO, "Booted %s (%s) -> %s\n", d->dev_addr, d->dev_addr_booted, d->dev_file ? d->dev_file : "VSC"); } else { mvLog(MVLOG_INFO, "Booted %s -> %s\n", - d->dev_addr, d->dev_file ? d->dev_file : "X_LINK_PCIE"); + d->dev_addr, d->dev_file ? d->dev_file : "PCIe"); } sleepForSeconds(1); @@ -892,17 +953,22 @@ ncStatus_t ncDeviceOpen(struct ncDeviceHandle_t **deviceHandlePtr, CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&deviceOpenMutex)); GLOBAL_UNLOCK(); - streamId_t streamId = XLinkOpenStream(d->xlink->linkId, "deviceMonitor", CONFIG_STREAM_SIZE); - CHECK_STREAM_ID(streamId, {}, "can't open deviceMonitor stream"); + streamId_t deviceMonitorStreamId = XLinkOpenStream(d->xlink->linkId, "deviceMonitor", CONFIG_STREAM_SIZE); + CHECK_STREAM_ID( + deviceMonitorStreamId, + { + CHECK_MUTEX_SUCCESS(pthread_mutex_destroy(&d->graph_stream_m)); + CHECK_MUTEX_SUCCESS(pthread_mutex_destroy(&d->dev_stream_m)); + CHECK_MUTEX_SUCCESS(pthread_mutex_destroy(&d->dev_data_m)); + destroyDeviceHandle(deviceHandlePtr); + }, + "can't open deviceMonitor stream"); - d->device_mon_stream_id = streamId; + d->device_mon_stream_id = deviceMonitorStreamId; #if !(defined(NO_BOOT)) - if(d->protocol != X_LINK_PCIE) - { - watchdog_init_context(&d->watchdog_ctx); - watchdog_register_device(&d->watchdog_ctx, d); - } + watchdog_init_context(&d->watchdog_ctx); + watchdog_register_device(&d->watchdog_ctx, d); #endif getDevAttributes(d); @@ -911,18 +977,40 @@ ncStatus_t ncDeviceOpen(struct ncDeviceHandle_t **deviceHandlePtr, printfOverXLinkOpen(d); #endif - streamId = XLinkOpenStream(d->xlink->linkId, "graphMonitor", - BLOB_STREAM_SIZE); + streamId_t graphMonitorStreamId = XLinkOpenStream(d->xlink->linkId, "graphMonitor", BLOB_STREAM_SIZE); #if (!defined(_WIN32) && !defined(_WIN64)) - CHECK_STREAM_ID(streamId, { - printfOverXLinkClose(d); + CHECK_STREAM_ID(graphMonitorStreamId, { + printfOverXLinkClose(d); + // TODO NO_BOOT case + watchdog_unregister_device(&d->watchdog_ctx); + CHECK_MUTEX_SUCCESS(pthread_mutex_destroy(&d->dev_data_m)); + CHECK_MUTEX_SUCCESS(pthread_mutex_destroy(&d->dev_stream_m)); + CHECK_MUTEX_SUCCESS(pthread_mutex_destroy(&d->graph_stream_m)); + XLinkError_t closed = XLinkCloseStream(deviceMonitorStreamId); + if (closed != X_LINK_SUCCESS) { + mvLog(MVLOG_ERROR, "Failed to close deviceMonitor stream"); + } + + destroyDeviceHandle(deviceHandlePtr); }, "can't open graphMonitor stream"); #else - CHECK_STREAM_ID(streamId, {}, "can't open graphMonitor stream"); + CHECK_STREAM_ID(graphMonitorStreamId, { + // TODO NO_BOOT case + watchdog_unregister_device(&d->watchdog_ctx); + CHECK_MUTEX_SUCCESS(pthread_mutex_destroy(&d->dev_data_m)); + CHECK_MUTEX_SUCCESS(pthread_mutex_destroy(&d->dev_stream_m)); + CHECK_MUTEX_SUCCESS(pthread_mutex_destroy(&d->graph_stream_m)); + XLinkError_t closed = XLinkCloseStream(deviceMonitorStreamId); + if (closed != X_LINK_SUCCESS) { + mvLog(MVLOG_ERROR, "Failed to close deviceMonitor stream"); + } + + destroyDeviceHandle(deviceHandlePtr); + }, "can't open graphMonitor stream"); #endif - d->graph_monitor_stream_id = streamId; + d->graph_monitor_stream_id = graphMonitorStreamId; d->state = NC_DEVICE_OPENED; return NC_OK; @@ -930,7 +1018,6 @@ ncStatus_t ncDeviceOpen(struct ncDeviceHandle_t **deviceHandlePtr, ncStatus_t ncAvailableDevices(struct ncDeviceDescr_t *deviceDescrPtr, int maxDevices, int* out_countDevices) { - //TODO: PCIe device support can be performed after #-17972 is completed CHECK_HANDLE_CORRECT(deviceDescrPtr); CHECK_HANDLE_CORRECT(out_countDevices); @@ -938,20 +1025,20 @@ ncStatus_t ncAvailableDevices(struct ncDeviceDescr_t *deviceDescrPtr, memset(deviceDescrPtr, 0, maxDevices * sizeof(struct ncDeviceDescr_t)); deviceDesc_t in_deviceDsc = { - .platform = NC_ANY_PLATFORM, - .protocol = X_LINK_USB_VSC + .platform = X_LINK_ANY_PLATFORM, + .protocol = X_LINK_ANY_PROTOCOL }; - int n = 0; - for (; n < maxDevices; ++n) { - deviceDesc_t deviceDsc = {0}; - if (XLinkFindDevice(n, X_LINK_UNBOOTED, &in_deviceDsc, &deviceDsc)) - break; - - copyXLinkDeviceDescrToNc(&deviceDsc, &deviceDescrPtr[n]); + deviceDesc_t deviceDescArray[NC_MAX_DEVICES] = {}; + unsigned int amountOfFoundDevices = 0; + XLinkFindAllSuitableDevices( + X_LINK_UNBOOTED, in_deviceDsc, deviceDescArray, NC_MAX_DEVICES, &amountOfFoundDevices); + int i; + for (i = 0; i < amountOfFoundDevices; ++i) { + copyXLinkDeviceDescrToNc(&deviceDescArray[i], &deviceDescrPtr[i]); } - *out_countDevices = n; + *out_countDevices = amountOfFoundDevices; return NC_OK; } @@ -963,11 +1050,11 @@ ncStatus_t ncDeviceLoadFirmware(const ncDevicePlatform_t devicePlatform, const c // Find device with specific platform deviceDesc_t deviceDesc = {0}; deviceDesc_t in_deviceDesc = { - .platform = devicePlatform, + .platform = convertPlatformToXlink(devicePlatform), .protocol = X_LINK_USB_VSC }; - rc = XLinkFindDevice(0, X_LINK_UNBOOTED, &in_deviceDesc, &deviceDesc); + rc = XLinkFindFirstSuitableDevice(X_LINK_UNBOOTED, in_deviceDesc, &deviceDesc); if (rc) { mvLog(MVLOG_WARN, "Failed to find (%s) platform device", ncPlatformToStr(devicePlatform)); return NC_DEVICE_NOT_FOUND; @@ -995,12 +1082,12 @@ ncStatus_t ncDeviceLoadFirmware(const ncDevicePlatform_t devicePlatform, const c } mvLog(MVLOG_INFO, "Trying to boot %s device", deviceDesc.name); - rc = XLinkBootRemote(&deviceDesc, mv_cmd_file_path); + rc = XLinkBoot(&deviceDesc, mv_cmd_file_path); if (rc) { mvLog(MVLOG_WARN, "%s() XLinkBootRemote returned error %s\n", __func__, XLinkErrorToStr(rc)); } else { mvLog(MVLOG_INFO, "%s() XLinkBootRemote returned success %s\n", __func__, XLinkErrorToStr(rc)); - sleepForSeconds(DEVICE_APPEAR_TIMEOUT_ON_OPEN); + sleepForSeconds(DEVICE_APPEAR_TIMEOUT_ON_OPEN); } return parseXLinkError(rc); @@ -1087,78 +1174,6 @@ static ncStatus_t getThermalStats(struct _devicePrivate_t *d){ return NC_OK; } -static ncStatus_t getDeviceFrequency(struct _devicePrivate_t *d){ - deviceCommand_t config; - config.type.c0 = CLASS0_DEVICE_QUERY_CLOCKS; - config.optionClass = NC_OPTION_CLASS0; - CHECK_MUTEX_SUCCESS_RC(pthread_mutex_lock(&d->dev_stream_m), NC_ERROR); - XLinkError_t rc = X_LINK_SUCCESS; - rc = XLinkWriteData(d->device_mon_stream_id, (const uint8_t*)&config, sizeof(config)); - if (rc != X_LINK_SUCCESS) { - mvLog(MVLOG_ERROR, "Failed to write data, rc: %s", XLinkErrorToStr(rc)); - CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&d->dev_stream_m)); - return parseXLinkError(rc); - } - streamPacketDesc_t* packet = 0; - - rc = XLinkReadData(d->device_mon_stream_id, &packet); - if (rc != X_LINK_SUCCESS || !packet) { - mvLog(MVLOG_ERROR, "Failed to read data, rc: %s", XLinkErrorToStr(rc)); - CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&d->dev_stream_m)); - return parseXLinkError(rc); - } - - if( packet->length != sizeof(uint32_t)) { - CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&d->dev_stream_m)); - return NC_ERROR; - } - mvnc_memcpy(&d->deviceFreq, sizeof(d->deviceFreq), packet->data, packet->length); - rc = XLinkReleaseData(d->device_mon_stream_id); - CHECK_MUTEX_SUCCESS_RC(pthread_mutex_unlock(&d->dev_stream_m), NC_ERROR); - if (rc != X_LINK_SUCCESS) { - mvLog(MVLOG_WARN,"Failed to release data, rc: %s", XLinkErrorToStr(rc)); - } - return NC_OK; -} - -static ncStatus_t getDeviceProfilingData(struct _devicePrivate_t *d){ - deviceCommand_t config; - config.type.c0 = CLASS0_DEVICE_PROFILING_DATA; - config.optionClass = NC_OPTION_CLASS0; - CHECK_MUTEX_SUCCESS_RC(pthread_mutex_lock(&d->dev_stream_m), NC_ERROR); - XLinkError_t rc = X_LINK_SUCCESS; - rc = XLinkWriteData(d->device_mon_stream_id, (const uint8_t*)&config, sizeof(config)); - if (rc != X_LINK_SUCCESS) { - mvLog(MVLOG_ERROR, "Failed to write data, rc: %s", XLinkErrorToStr(rc)); - CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&d->dev_stream_m)); - return parseXLinkError(rc); - } - streamPacketDesc_t* packet = 0; - - rc = XLinkReadData(d->device_mon_stream_id, &packet); - if (rc != X_LINK_SUCCESS || !packet) { - mvLog(MVLOG_ERROR, "Failed to read data, rc: %s", XLinkErrorToStr(rc)); - CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&d->dev_stream_m)); - return parseXLinkError(rc); - } - - d->receivedData = packet->length; - if (d->profilingBuffer == 0) { - d->profilingBuffer = (uint8_t*) malloc(profUpperBound); - } - - if( packet->length > profUpperBound) { - d->receivedData = profUpperBound; - } - mvnc_memcpy(d->profilingBuffer, profUpperBound, packet->data, d->receivedData); - rc = XLinkReleaseData(d->device_mon_stream_id); - CHECK_MUTEX_SUCCESS_RC(pthread_mutex_unlock(&d->dev_stream_m), NC_ERROR); - if (rc != X_LINK_SUCCESS) { - mvLog(MVLOG_WARN,"Failed to release data, rc: %s", XLinkErrorToStr(rc)); - } - return NC_OK; -} - static ncStatus_t deviceGetDeviceMemory(struct _devicePrivate_t *d, uint32_t * mem) { @@ -1236,7 +1251,9 @@ static void fprintfsock( int s, const char* fmt, ... ) { } if(s < 0) { - (void) write( 1, ptext, len ); + if(write( 1, ptext, len) != len) { + fprintf(stderr, "Error in fprintfsock: write failed\n"); + } } else { if(send( s, ptext, len, 0 ) < 0) { @@ -1566,7 +1583,9 @@ ncStatus_t ncDeviceClose(struct ncDeviceHandle_t **deviceHandlePtr) { int wasConnectedToBooted = 0; if (d->dev_addr != NULL && d->dev_addr_booted != NULL && strncmp(d->dev_addr, d->dev_addr_booted, NC_MAX_NAME_SIZE) == 0) { - wasConnectedToBooted = 1; // For PCIE that also would work + // PCIe device have same booted and unbooted addr + if (d->protocol != X_LINK_PCIE) + wasConnectedToBooted = 1; } GLOBAL_LOCK(); @@ -1619,17 +1638,29 @@ ncStatus_t ncDeviceClose(struct ncDeviceHandle_t **deviceHandlePtr) { printfOverXLinkClose(d); #endif +#if !defined(NO_BOOT) + watchdog_unregister_device(&d->watchdog_ctx); +#endif + + // Save all devices before reset + deviceDesc_t in_deviceDesc = { + .platform = X_LINK_ANY_PLATFORM, + .protocol = d->protocol + }; + deviceDesc_t beforeResetDevices[NC_MAX_DEVICES] = {{0}}; + unsigned int foundDevicesBeforeReset = 0; + XLinkFindAllSuitableDevices(X_LINK_ANY_STATE, in_deviceDesc, beforeResetDevices, + NC_MAX_DEVICES, &foundDevicesBeforeReset); + if (d->state != NC_DEVICE_FAILED) { // #17801 #if !defined(NO_BOOT) - if (d->device_mon_stream_id != INVALID_LINK_ID && - d->protocol != X_LINK_PCIE) { - rc = XLinkCloseStream(d->device_mon_stream_id); - if (rc) - mvLog(MVLOG_WARN,"Failed to close stream, rc: %s", XLinkErrorToStr(rc)); - } - if (d->graph_monitor_stream_id != INVALID_LINK_ID && - d->protocol != X_LINK_PCIE) { + if (d->graph_monitor_stream_id != INVALID_LINK_ID) { + if (d->device_mon_stream_id != INVALID_LINK_ID) { + rc = XLinkCloseStream(d->device_mon_stream_id); + if (rc) + mvLog(MVLOG_WARN,"Failed to close stream, rc: %s", XLinkErrorToStr(rc)); + } rc = XLinkCloseStream(d->graph_monitor_stream_id); if (rc) mvLog(MVLOG_WARN,"Failed to close stream, rc: %s", XLinkErrorToStr(rc)); @@ -1648,12 +1679,6 @@ ncStatus_t ncDeviceClose(struct ncDeviceHandle_t **deviceHandlePtr) { } } -#if !defined(NO_BOOT) - if(d->protocol != X_LINK_PCIE) { - watchdog_unregister_device(&d->watchdog_ctx); - } -#endif - d->state = NC_DEVICE_CLOSED; CHECK_MUTEX_SUCCESS(pthread_mutex_destroy(&d->graph_stream_m)); @@ -1662,48 +1687,53 @@ ncStatus_t ncDeviceClose(struct ncDeviceHandle_t **deviceHandlePtr) { CHECK_MUTEX_SUCCESS(pthread_mutex_unlock(&d->dev_data_m)); CHECK_MUTEX_SUCCESS(pthread_mutex_destroy(&d->dev_data_m)); - if (!wasConnectedToBooted) { - int device_appear_after_reboot = 0; + + if (!wasConnectedToBooted && d->protocol != X_LINK_PCIE) { + deviceDesc_t bootedDeviceDesc = { + .protocol = d->protocol, + .platform = X_LINK_ANY_PLATFORM + }; + mv_strcpy(bootedDeviceDesc.name, XLINK_MAX_NAME_SIZE, d->dev_addr_booted); + + int booted_disappeared = 0; + int unbooted_appeared = 0; // Wait for unbooted device appear in usb list double waittm = timeInSeconds() + DEVICE_APPEAR_TIMEOUT_ON_CLOSE; - while (timeInSeconds() < waittm) { - // check current devices - // wait for booted name to disappear - // wait for unbooted name to appear - // sometimes both names can be present in the list of usb devices - deviceDesc_t device_desc = {0}; - deviceDesc_t in_deviceDesc = { - .platform = NC_ANY_PLATFORM, - .protocol = d->protocol - }; - - int booted_disappeared = 1; - int unbooted_appeared = 0; - - int n = 0; - while (XLinkFindDevice(n++, X_LINK_ANY_STATE, &in_deviceDesc, &device_desc) == X_LINK_SUCCESS) { - if (d->dev_addr_booted != NULL && - strcmp(device_desc.name, d->dev_addr_booted) == 0) { - booted_disappeared = 0; - break; - } - if (d->dev_addr != NULL && - strcmp(device_desc.name, d->dev_addr) == 0) { - unbooted_appeared = 1; - } + deviceDesc_t afterResetDevices[NC_MAX_DEVICES] = {{0}}; + unsigned int foundDevicesAfterReset = 0; + do { + XLinkFindAllSuitableDevices(X_LINK_ANY_STATE, in_deviceDesc, afterResetDevices, + NC_MAX_DEVICES, &foundDevicesAfterReset); + if (foundDevicesAfterReset != foundDevicesBeforeReset) { + continue; } - if (!(booted_disappeared && unbooted_appeared)) { + deviceDesc_t deviceDesc = {}; + rc = XLinkFindFirstSuitableDevice(X_LINK_BOOTED, bootedDeviceDesc, &deviceDesc); + if (rc == X_LINK_SUCCESS) { continue; } else { - device_appear_after_reboot = 1; - break; + booted_disappeared = 1; } - } + int i, j; + for (i = 0; i < foundDevicesAfterReset; ++i) { + int found_in_before_reset_list = 0; + for (j = 0; j < foundDevicesBeforeReset; ++j) { + if(strcmp(beforeResetDevices[i].name, afterResetDevices[j].name) == 0) { + found_in_before_reset_list = 1; + } + } + if (!found_in_before_reset_list) { + unbooted_appeared = 1; + } + } + - if (device_appear_after_reboot == 0) { + } while (!(booted_disappeared && unbooted_appeared) && timeInSeconds() < waittm); + + if (!booted_disappeared || !unbooted_appeared) { mvLog(MVLOG_ERROR, "Device didn't appear after reboot"); } } else { @@ -2106,7 +2136,7 @@ ncStatus_t ncGraphSetOption(struct ncGraphHandle_t * graphHandle, return NC_INVALID_PARAMETERS; } if (option < GRAPH_CLASS0_BASE || - option > (GRAPH_CLASS0_BASE + OPTION_CLASS_SIZE * NC_OPTION_CLASS3)) { + option > (GRAPH_CLASS0_BASE + OPTION_CLASS_SIZE * NC_OPTION_GRAPH_LAST)) { mvLog(MVLOG_ERROR, "Option %d is invalid", option); return NC_INVALID_PARAMETERS; } @@ -2415,7 +2445,7 @@ ncStatus_t ncGraphGetOption(struct ncGraphHandle_t * graphHandle, } if (option < GRAPH_CLASS0_BASE || - option > (GRAPH_CLASS0_BASE + OPTION_CLASS_SIZE * NC_OPTION_CLASS3)) { + option > (GRAPH_CLASS0_BASE + OPTION_CLASS_SIZE * NC_OPTION_GRAPH_LAST)) { mvLog(MVLOG_ERROR, "Option %d is invalid", option); return NC_INVALID_PARAMETERS; } @@ -2775,9 +2805,36 @@ static ncStatus_t getDeviceOptionClass0(struct _devicePrivate_t *d, return rc; } +static ncStatus_t setDeviceOptionClass4(struct _devicePrivate_t *d, + ncDeviceOption_t option, + const void *data, unsigned int dataLength){ + XLinkError_t rc = X_LINK_SUCCESS; + deviceCommand_t config; + + if (option != NC_RW_DEVICE_POWER_CONFIG_RESET && option != NC_RW_DEVICE_POWER_CONFIG) { + mvLog(MVLOG_ERROR, "No such option"); + return NC_INVALID_PARAMETERS; + } + + config.type.c4 = (option == NC_RW_DEVICE_POWER_CONFIG ? CLASS4_SET_POWER_CONFIG : CLASS4_RESET_POWER_CONFIG); + config.optionClass = NC_OPTION_CLASS4; + config.data = *(uint32_t*)data; + + rc = XLinkWriteData(d->device_mon_stream_id, (const uint8_t *)&config, sizeof(config)); + + if (rc != X_LINK_SUCCESS) + { + mvLog(MVLOG_ERROR, "Failed to write data, rc: %s", XLinkErrorToStr(rc)); + return parseXLinkError(rc); + } + + return NC_OK; +} + ncStatus_t ncDeviceSetOption(struct ncDeviceHandle_t *deviceHandle, - ncDeviceOption_t option, - const void *data, unsigned int dataLength){ + ncDeviceOption_t option, + const void *data, unsigned int dataLength){ + ncStatus_t rc = NC_OK; if (!deviceHandle || !data){ mvLog(MVLOG_ERROR, "Some of the parameters are NULL"); return NC_INVALID_PARAMETERS; @@ -2788,7 +2845,7 @@ ncStatus_t ncDeviceSetOption(struct ncDeviceHandle_t *deviceHandle, } if (option < DEVICE_CLASS0_BASE || - option > (DEVICE_CLASS0_BASE + OPTION_CLASS_SIZE * NC_OPTION_CLASS3)) { + option > (DEVICE_CLASS0_BASE + OPTION_CLASS_SIZE * NC_OPTION_LAST)) { mvLog(MVLOG_ERROR, "Option %d is invalid", option); return NC_INVALID_PARAMETERS; } @@ -2809,14 +2866,22 @@ ncStatus_t ncDeviceSetOption(struct ncDeviceHandle_t *deviceHandle, return NC_INVALID_HANDLE; } - GLOBAL_UNLOCK(); + if (opClass > d->dev_attr.max_device_opt_class) { mvLog(MVLOG_ERROR, "This device FW does not support NC_OPTION_CLASS%d", opClass); return NC_UNAUTHORIZED; } - return NC_INVALID_PARAMETERS; + switch (opClass) { + case NC_OPTION_CLASS4: + rc = setDeviceOptionClass4(d, option, data, dataLength); + break; + default: + rc = NC_INVALID_PARAMETERS; + } + GLOBAL_UNLOCK(); + return rc; } //static options can be read before device is open @@ -2844,7 +2909,7 @@ ncStatus_t ncDeviceGetOption(struct ncDeviceHandle_t * deviceHandle, } if (option < DEVICE_CLASS0_BASE || - option > (DEVICE_CLASS0_BASE + OPTION_CLASS_SIZE * NC_OPTION_CLASS3)) { + option > (DEVICE_CLASS0_BASE + OPTION_CLASS_SIZE * NC_OPTION_LAST)) { mvLog(MVLOG_ERROR, "Option %d is invalid", option); return NC_INVALID_PARAMETERS; } diff --git a/inference-engine/thirdparty/movidius/mvnc/src/mvnc_data.c b/inference-engine/thirdparty/movidius/mvnc/src/mvnc_data.c index befa347e98966a..56bf3aa79e1f81 100644 --- a/inference-engine/thirdparty/movidius/mvnc/src/mvnc_data.c +++ b/inference-engine/thirdparty/movidius/mvnc/src/mvnc_data.c @@ -1,6 +1,20 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// +/* +* Copyright 2017-2019 Intel Corporation. +* The source code, information and material ("Material") contained herein is +* owned by Intel Corporation or its suppliers or licensors, and title to such +* Material remains with Intel Corporation or its suppliers or licensors. +* The Material contains proprietary information of Intel or its suppliers and +* licensors. The Material is protected by worldwide copyright laws and treaty +* provisions. +* No part of the Material may be used, copied, reproduced, modified, published, +* uploaded, posted, transmitted, distributed or disclosed in any way without +* Intel's prior express written permission. No license under any patent, +* copyright or other intellectual property rights in the Material is granted to +* or conferred upon you, either expressly, by implication, inducement, estoppel +* or otherwise. +* Any license under such intellectual property rights must be express and +* approved by Intel in writing. +*/ #include #include "mvnc_data.h" diff --git a/inference-engine/thirdparty/movidius/mvnc/tests/mvnc_tests_common.cpp b/inference-engine/thirdparty/movidius/mvnc/tests/mvnc_tests_common.cpp index 4a9ba73dafde82..6b3168fd5c0f8d 100644 --- a/inference-engine/thirdparty/movidius/mvnc/tests/mvnc_tests_common.cpp +++ b/inference-engine/thirdparty/movidius/mvnc/tests/mvnc_tests_common.cpp @@ -39,6 +39,28 @@ TEST_F(MvncTestsCommon, AvailableDevicesSholdReturnErrorIfCountPtrIsNULL) { ASSERT_ERROR(ncAvailableDevices(act_devices, NC_MAX_DEVICES, NULL)); } +TEST_F(MvncTestsCommon, CanGetPCIeAndUSB) { + if (!(getAmountOfUSBDevices() > 0 && getAmountOfPCIeDevices())) + GTEST_SKIP_("USB and PCIe not available"); + + struct ncDeviceDescr_t act_devices[NC_MAX_DEVICES] = {}; + int act_devicesCount = 0; + ASSERT_NO_ERROR(ncAvailableDevices(act_devices, NC_MAX_DEVICES, &act_devicesCount)); + + bool usb_device_found = false; + bool pcie_device_found = false; + + for (int i = 0; i < act_devicesCount; ++i) { + if (isMyriadUSBDevice(act_devices[i].name)) { + usb_device_found = true; + } else if (isMyriadPCIeDevice(act_devices[i].name)) { + pcie_device_found = true; + } + } + + EXPECT_TRUE(usb_device_found); + EXPECT_TRUE(pcie_device_found); +} // *********************************************** // // Tests using both platforms // @@ -47,6 +69,11 @@ TEST_F(MvncTestsCommon, AvailableDevicesSholdReturnErrorIfCountPtrIsNULL) { * @brief Test that USB and PCIe works at the same time. USB first */ TEST_F(MvncTestsCommon, OpenUSBThenPCIEAndClose) { + if (getAmountOfPCIeDevices() == 0) + GTEST_SKIP() << "PCIe devices not found"; + if (getAmountOfUSBDevices() == 0) + GTEST_SKIP() << "USB devices not found"; + ncDeviceHandle_t *deviceHandle_USB = nullptr; ncDeviceHandle_t *deviceHandle_PCIe = nullptr; std::string actDeviceName; @@ -54,9 +81,6 @@ TEST_F(MvncTestsCommon, OpenUSBThenPCIEAndClose) { deviceDesc.protocol = NC_USB; deviceDesc.platform = NC_ANY_PLATFORM; - ASSERT_TRUE(getAmountOfPCIeDevices() > 0) << "PCIe devices not found"; - ASSERT_TRUE(getAmountOfUSBDevices() > 0) << "USB devices not found"; - ASSERT_NO_ERROR(ncDeviceOpen(&deviceHandle_USB, deviceDesc, watchdogInterval, firmwarePath)); actDeviceName = deviceHandle_USB->private_data->dev_addr; @@ -80,6 +104,11 @@ TEST_F(MvncTestsCommon, OpenUSBThenPCIEAndClose) { * @brief Test that USB and PCIe works at the same time. PCIe first */ TEST_F(MvncTestsCommon, OpenPCIEThenUSBAndClose) { + if (getAmountOfPCIeDevices() == 0) + GTEST_SKIP() << "PCIe devices not found"; + if (getAmountOfUSBDevices() == 0) + GTEST_SKIP() << "USB devices not found"; + ncDeviceHandle_t *deviceHandle_USB = nullptr; ncDeviceHandle_t *deviceHandle_PCIe = nullptr; std::string actDeviceName; @@ -87,9 +116,6 @@ TEST_F(MvncTestsCommon, OpenPCIEThenUSBAndClose) { deviceDesc.protocol = NC_PCIE; deviceDesc.platform = NC_ANY_PLATFORM; - ASSERT_TRUE(getAmountOfPCIeDevices() > 0) << "PCIe devices not found"; - ASSERT_TRUE(getAmountOfUSBDevices() > 0) <<"USB devices not found"; - // Open PCIe device ASSERT_NO_ERROR(ncDeviceOpen(&deviceHandle_PCIe, deviceDesc, watchdogInterval, firmwarePath)); @@ -126,8 +152,6 @@ class MvncOpenDevice : public MvncTestsCommon, _deviceProtocol = GetParam(); available_devices = getAmountOfDevices(_deviceProtocol); - ASSERT_TRUE(available_devices > 0) << ncProtocolToStr(_deviceProtocol) - << " devices not found"; } ncDeviceProtocol_t _deviceProtocol = NC_ANY_PROTOCOL; @@ -137,14 +161,16 @@ class MvncOpenDevice : public MvncTestsCommon, * @brief Open any device and close it */ TEST_P(MvncOpenDevice, OpenAndClose) { + if (available_devices == 0) + GTEST_SKIP() << ncProtocolToStr(_deviceProtocol) << " devices not found"; + ncDeviceHandle_t* deviceHandle = nullptr; std::string deviceName; ncDeviceDescr_t deviceDesc = {}; deviceDesc.protocol = _deviceProtocol; deviceDesc.platform = NC_ANY_PLATFORM; - ASSERT_NO_ERROR(ncDeviceOpen(&deviceHandle, deviceDesc, - watchdogInterval, firmwarePath)); + ASSERT_NO_ERROR(ncDeviceOpen(&deviceHandle, deviceDesc, watchdogInterval, firmwarePath)); ASSERT_TRUE(deviceHandle != nullptr); ASSERT_TRUE(deviceHandle->private_data != nullptr); @@ -162,6 +188,9 @@ TEST_P(MvncOpenDevice, OpenAndClose) { * @brief Check that all field of deviceHandle would be initialized */ TEST_P(MvncOpenDevice, AllHandleFieldsInitialized) { + if (available_devices == 0) + GTEST_SKIP() << ncProtocolToStr(_deviceProtocol) << " devices not found"; + ncDeviceHandle_t* deviceHandle = nullptr; ncDeviceDescr_t deviceDesc = {}; deviceDesc.protocol = _deviceProtocol; @@ -187,6 +216,9 @@ TEST_P(MvncOpenDevice, AllHandleFieldsInitialized) { * already has allocated device */ TEST_P(MvncOpenDevice, OpenTwiceSameHandler) { + if (available_devices == 0) + GTEST_SKIP() << ncProtocolToStr(_deviceProtocol) << " devices not found"; + ncDeviceHandle_t *deviceHandle = nullptr; ncDeviceDescr_t deviceDesc = {}; deviceDesc.protocol = _deviceProtocol; @@ -219,6 +251,8 @@ TEST_P(MvncOpenDevice, OpenTwiceSameHandler) { */ // Fixme Test only for one device TEST_P(MvncOpenDevice, DISABLED_OpenSameDeviceTwiceDifferentHandlers) { + if (available_devices == 0) + GTEST_SKIP() << ncProtocolToStr(_deviceProtocol) << " devices not found"; ncDeviceHandle_t *deviceHandle1 = nullptr; ncDeviceHandle_t *deviceHandle2 = nullptr; @@ -243,6 +277,9 @@ TEST_P(MvncOpenDevice, DISABLED_OpenSameDeviceTwiceDifferentHandlers) { * @note Mostly this test important for PCIe and connect to booted option, as in that cases XLinkReset have another behavior */ TEST_P(MvncOpenDevice, OpenTwiceWithOneXLinkInitializion) { + if (available_devices == 0) + GTEST_SKIP() << ncProtocolToStr(_deviceProtocol) << " devices not found"; + ncDeviceHandle_t *deviceHandle = nullptr; std::string actDeviceName; @@ -301,6 +338,9 @@ class MvncLoggingTests : public MvncOpenDevice { }; TEST_P(MvncLoggingTests, ShouldNotPrintErrorMessagesIfCanNotOpenDevice) { + if (available_devices == 0) + GTEST_SKIP() << ncProtocolToStr(_deviceProtocol) << " devices not found"; + setLogLevel(MVLOG_ERROR); ncDeviceHandle_t * deviceHandle = nullptr; @@ -312,6 +352,9 @@ TEST_P(MvncLoggingTests, ShouldNotPrintErrorMessagesIfCanNotOpenDevice) { } TEST_P(MvncLoggingTests, ShouldPrintWarningMessagesIfCanNotOpenDeviceAndMvLogLevelIsInfo) { + if (available_devices == 0) + GTEST_SKIP() << ncProtocolToStr(_deviceProtocol) << " devices not found"; + setLogLevel(MVLOG_INFO); ncDeviceHandle_t * deviceHandle = nullptr; @@ -367,7 +410,7 @@ class MvncGraphAllocations: public MvncOpenDevice { /** * @brief Allocate graph for one device */ -TEST_P(MvncGraphAllocations, OneGraph) { +TEST_P(MvncGraphAllocations, DISABLED_OneGraph) { if (!blobLoaded) GTEST_SKIP_("Blob for test is not loaded\n"); openDevices(1, _deviceHandle, _bootedDevices); @@ -385,8 +428,9 @@ TEST_P(MvncGraphAllocations, OneGraph) { /** * @brief Allocate graphs for 2 device (serial) */ -TEST_P(MvncGraphAllocations, AllocateGraphsOn2DevicesSerial) { - if (!blobLoaded) GTEST_SKIP_("Blob for test is not loaded\n"); +TEST_P(MvncGraphAllocations, DISABLED_AllocateGraphsOn2DevicesSerial) { + if (!blobLoaded) + GTEST_SKIP_("Blob for test is not loaded\n"); openDevices(2, _deviceHandle, _bootedDevices); // Create graphs handlers @@ -418,7 +462,7 @@ TEST_P(MvncGraphAllocations, AllocateGraphsOn2DevicesSerial) { * @warning It's depend on USBLINK_TRANSFER_SIZE constant from UsbLinkPlatform.c file * @warning Need blob to use this tests */ -TEST_P(MvncGraphAllocations, AllocateGraphsOn2DevicesParallel) { +TEST_P(MvncGraphAllocations, DISABLED_AllocateGraphsOn2DevicesParallel) { if (!blobLoaded) GTEST_SKIP_("Blob for test is not loaded\n"); openDevices(2, _deviceHandle, _bootedDevices); @@ -485,12 +529,39 @@ TEST_F(MvncCloseDevice, EmptyFieldsOfDeviceHandle) { ASSERT_EQ(ncDeviceClose(&deviceHandlePtr), NC_INVALID_PARAMETERS); } +#if (!(defined(_WIN32) || defined(_WIN64))) +TEST_F(MvncCloseDevice, USBDeviceWillBeAvailableRightAfterClosing) { + + ncDeviceHandle_t* deviceHandle = nullptr; + ncDeviceDescr_t deviceDesc = {}; + deviceDesc.protocol = NC_USB; + deviceDesc.platform = NC_ANY_PLATFORM; + + ASSERT_NO_ERROR(ncDeviceOpen( + &deviceHandle, deviceDesc, watchdogInterval, firmwarePath)); + + ASSERT_TRUE(deviceHandle); + deviceDesc_t toFindDeviceDescr = { + .protocol = X_LINK_USB_VSC, + .platform = X_LINK_ANY_PLATFORM + }; + strcpy(deviceDesc.name, deviceHandle->private_data->dev_addr); + + ASSERT_NO_ERROR(ncDeviceClose(&deviceHandle)); + + deviceDesc_t foundDevice = {}; + XLinkError_t rc = XLinkFindFirstSuitableDevice( + X_LINK_UNBOOTED, toFindDeviceDescr, &foundDevice); + ASSERT_EQ(X_LINK_SUCCESS, rc); +} +#endif + // *************************************************** // // TESTS WITH INFERENCE // using MvncInference = MvncGraphAllocations; -TEST_P(MvncInference, DoOneIterationOfInference) { +TEST_P(MvncInference, DISABLED_DoOneIterationOfInference) { if (!blobLoaded) GTEST_SKIP_("Blob for test is not loaded\n"); openDevices(1, _deviceHandle, _bootedDevices); diff --git a/inference-engine/thirdparty/movidius/mvnc/tests/mvnc_tests_common.hpp b/inference-engine/thirdparty/movidius/mvnc/tests/mvnc_tests_common.hpp index 176373daa75d8b..87311862562cc5 100644 --- a/inference-engine/thirdparty/movidius/mvnc/tests/mvnc_tests_common.hpp +++ b/inference-engine/thirdparty/movidius/mvnc/tests/mvnc_tests_common.hpp @@ -97,26 +97,19 @@ class MvncTestsCommon : public ::testing::Test { * @brief Get amount of all currently connected Myriad devices * @param[in] deviceProtocol Count only platform specific devices */ - static int getAmountOfDevices(const ncDeviceProtocol_t deviceProtocol = NC_ANY_PROTOCOL) { - int amount = 0; - deviceDesc_t deviceDesc = {}; - deviceDesc_t in_deviceDesc = {}; - in_deviceDesc.protocol = convertProtocolToXlink(deviceProtocol); - in_deviceDesc.platform = X_LINK_ANY_PLATFORM; - - if(in_deviceDesc.protocol == X_LINK_USB_VSC) { - for (; amount < MAX_DEVICES; ++amount) { - if (XLinkFindDevice(amount, X_LINK_ANY_STATE, &in_deviceDesc, &deviceDesc)) - break; - } - return amount; - } - - if (XLinkFindDevice(amount, X_LINK_ANY_STATE, &in_deviceDesc, &deviceDesc) == X_LINK_SUCCESS) { - return ++amount; - } - - return amount; + static int getAmountOfDevices(const ncDeviceProtocol_t deviceProtocol = NC_ANY_PROTOCOL, + const ncDevicePlatform_t devicePlatform = NC_ANY_PLATFORM, + const XLinkDeviceState_t state = X_LINK_ANY_STATE) { + deviceDesc_t req_deviceDesc = {}; + req_deviceDesc.protocol = convertProtocolToXlink(deviceProtocol); + req_deviceDesc.platform = convertPlatformToXlink(devicePlatform); + + deviceDesc_t deviceDescArray[NC_MAX_DEVICES] = {}; + unsigned int foundDevices = 0; + XLinkFindAllSuitableDevices( + state, req_deviceDesc, deviceDescArray, NC_MAX_DEVICES, &foundDevices); + + return foundDevices; } /** @@ -161,25 +154,26 @@ class MvncTestsCommon : public ::testing::Test { /** * @brief Get list of all currently connected Myriad devices */ - static std::vector getDevicesList() { - std::vector < std::string > devName; - deviceDesc_t tempDeviceDesc = {}; - deviceDesc_t in_deviceDesc = {}; - in_deviceDesc.protocol = X_LINK_USB_VSC; - in_deviceDesc.platform = X_LINK_ANY_PLATFORM; - - for (int i = 0; i < MAX_DEVICES; ++i) { - if (XLinkFindDevice(i, X_LINK_ANY_STATE, &in_deviceDesc, &tempDeviceDesc)) - break; - devName.emplace_back(tempDeviceDesc.name); + static std::vector getDevicesList( + const ncDeviceProtocol_t deviceProtocol = NC_ANY_PROTOCOL, + const ncDevicePlatform_t devicePlatform = NC_ANY_PLATFORM, + const XLinkDeviceState_t state = X_LINK_ANY_STATE) { + + deviceDesc_t req_deviceDesc = {}; + req_deviceDesc.protocol = convertProtocolToXlink(deviceProtocol); + req_deviceDesc.platform = convertPlatformToXlink(devicePlatform); + + deviceDesc_t deviceDescArray[NC_MAX_DEVICES] = {}; + unsigned int foundDevices = 0; + XLinkFindAllSuitableDevices( + state, req_deviceDesc, deviceDescArray, NC_MAX_DEVICES, &foundDevices); + + std::vector < std::string > devNames; + for (int i = 0; i < foundDevices; ++i) { + devNames.emplace_back(deviceDescArray[i].name); } - in_deviceDesc.protocol = X_LINK_PCIE; - /// PCIe don't use indexes and always return same device - if (XLinkFindDevice(0, X_LINK_ANY_STATE, &in_deviceDesc, &tempDeviceDesc) == 0) - devName.emplace_back(tempDeviceDesc.name); - - return devName; + return devNames; } static bool isMyriadXUSBDevice(const std::string &deviceName) { @@ -228,8 +222,8 @@ class MvncTestsCommon : public ::testing::Test { /** * @brief Check that device matches the specified protocol */ - static bool isSamePlatformDevice(const std::string &deviceName, - const ncDevicePlatform_t expectedPlatform) { + static bool isSamePlatformUSBDevice(const std::string &deviceName, + const ncDevicePlatform_t expectedPlatform) { switch (expectedPlatform) { case NC_MYRIAD_2: return isMyriad2USBDevice(deviceName); case NC_MYRIAD_X: return isMyriadXUSBDevice(deviceName); @@ -242,22 +236,19 @@ class MvncTestsCommon : public ::testing::Test { } static long getAmountOfMyriadXDevices() { - auto devName = getDevicesList(); - return count_if(devName.begin(), devName.end(), isMyriadXUSBDevice); + return getAmountOfDevices(NC_ANY_PROTOCOL, NC_MYRIAD_X); } static long getAmountOfMyriad2Devices() { - auto devName = getDevicesList(); - return count_if(devName.begin(), devName.end(), isMyriad2USBDevice); + return getAmountOfDevices(NC_ANY_PROTOCOL, NC_MYRIAD_2); } - static long getAmountOfBootedDevices() { - auto devName = getDevicesList(); - return count_if(devName.begin(), devName.end(), isMyriadBootedUSBDevice); + static long getAmountOfBootedDevices(ncDeviceProtocol_t deviceProtocol = NC_ANY_PROTOCOL) { + return getAmountOfDevices(deviceProtocol, NC_ANY_PLATFORM, X_LINK_BOOTED); } - static long getAmountOfNotBootedDevices() { - return (getAmountOfMyriadXDevices() + getAmountOfMyriad2Devices()); + static long getAmountOfNotBootedDevices(ncDeviceProtocol_t deviceProtocol = NC_ANY_PROTOCOL) { + return getAmountOfDevices(deviceProtocol, NC_ANY_PLATFORM, X_LINK_UNBOOTED); } static long getAmountOfPCIeDevices() { @@ -276,7 +267,7 @@ class MvncTestsCommon : public ::testing::Test { */ bool readBINFile(const std::string& fileName, std::vector& buf) { std::ifstream file(fileName, std::ios_base::binary | std::ios_base::ate); - if (!file.is_open()) { + if (file.fail()) { std::cout << "Can't open file!" << std::endl; return false; } diff --git a/inference-engine/thirdparty/movidius/mvnc/tests/mvnc_tests_usb.cpp b/inference-engine/thirdparty/movidius/mvnc/tests/mvnc_tests_usb.cpp index b3be2ffd8a768a..e08953b46a64fe 100644 --- a/inference-engine/thirdparty/movidius/mvnc/tests/mvnc_tests_usb.cpp +++ b/inference-engine/thirdparty/movidius/mvnc/tests/mvnc_tests_usb.cpp @@ -14,8 +14,9 @@ class MvncOpenUSBDevice : public MvncTestsCommon { protected: ~MvncOpenUSBDevice() override = default; void SetUp() override { + ncDeviceResetAll(); MvncTestsCommon::SetUp(); - available_devices = getAmountOfNotBootedDevices(); + available_devices = getAmountOfNotBootedDevices(NC_USB); ASSERT_TRUE(available_devices > 0); } }; @@ -201,8 +202,6 @@ class MvncDevicePlatform : public MvncOpenUSBDevice, available_myriadX = getAmountOfMyriadXDevices(); available_myriad2 = getAmountOfMyriad2Devices(); - ASSERT_TRUE(available_myriadX > 0); - ASSERT_TRUE(available_myriad2 > 0); devicePlatform = GetParam(); } }; @@ -211,6 +210,9 @@ class MvncDevicePlatform : public MvncOpenUSBDevice, * @brief Open specified device and close it */ TEST_P(MvncDevicePlatform, OpenAndClose) { + if (available_myriad2 == 0 || available_myriadX == 0) + GTEST_SKIP(); + ncDeviceHandle_t *deviceHandle = nullptr; ncDeviceDescr_t deviceDesc = {}; deviceDesc.protocol = NC_USB; @@ -222,7 +224,7 @@ TEST_P(MvncDevicePlatform, OpenAndClose) { unsigned int size = MAX_DEV_NAME; ASSERT_NO_ERROR(ncDeviceGetOption(deviceHandle, NC_RO_DEVICE_NAME, deviceName, &size)); - EXPECT_TRUE(isSamePlatformDevice(deviceName, devicePlatform)); + EXPECT_TRUE(isSamePlatformUSBDevice(deviceName, devicePlatform)); ASSERT_NO_ERROR(ncDeviceClose(&deviceHandle)); diff --git a/inference-engine/thirdparty/movidius/shared/include/mvLog.h b/inference-engine/thirdparty/movidius/shared/include/mvLog.h index 4ecb5da7f8911a..0a85158852567a 100644 --- a/inference-engine/thirdparty/movidius/shared/include/mvLog.h +++ b/inference-engine/thirdparty/movidius/shared/include/mvLog.h @@ -99,6 +99,10 @@ extern int pthread_getname_np (pthread_t , char *, size_t); #define MVLOG_FATAL_COLOR ANSI_COLOR_RED #endif +#ifndef MVLOG_MAXIMUM_THREAD_NAME_SIZE +#define MVLOG_MAXIMUM_THREAD_NAME_SIZE 20 +#endif + typedef enum mvLog_t{ MVLOG_DEBUG = 0, MVLOG_INFO, diff --git a/inference-engine/thirdparty/movidius/shared/include/mvStringUtils.h b/inference-engine/thirdparty/movidius/shared/include/mvStringUtils.h index 51b0d0eabec4cb..d9a632a65e3bb4 100644 --- a/inference-engine/thirdparty/movidius/shared/include/mvStringUtils.h +++ b/inference-engine/thirdparty/movidius/shared/include/mvStringUtils.h @@ -1,6 +1,20 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// +/* +* Copyright 2017-2019 Intel Corporation. +* The source code, information and material ("Material") contained herein is +* owned by Intel Corporation or its suppliers or licensors, and title to such +* Material remains with Intel Corporation or its suppliers or licensors. +* The Material contains proprietary information of Intel or its suppliers and +* licensors. The Material is protected by worldwide copyright laws and treaty +* provisions. +* No part of the Material may be used, copied, reproduced, modified, published, +* uploaded, posted, transmitted, distributed or disclosed in any way without +* Intel's prior express written permission. No license under any patent, +* copyright or other intellectual property rights in the Material is granted to +* or conferred upon you, either expressly, by implication, inducement, estoppel +* or otherwise. +* Any license under such intellectual property rights must be express and +* approved by Intel in writing. +*/ #ifndef MVSTRINGUTILS_H__ #define MVSTRINGUTILS_H__ diff --git a/inference-engine/thirdparty/movidius/shared/src/mvStringUtils.c b/inference-engine/thirdparty/movidius/shared/src/mvStringUtils.c index 4cefaf65219a8e..8008b0219a14a6 100644 --- a/inference-engine/thirdparty/movidius/shared/src/mvStringUtils.c +++ b/inference-engine/thirdparty/movidius/shared/src/mvStringUtils.c @@ -1,6 +1,20 @@ -// Copyright (C) 2018-2019 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// +/* +* Copyright 2017-2019 Intel Corporation. +* The source code, information and material ("Material") contained herein is +* owned by Intel Corporation or its suppliers or licensors, and title to such +* Material remains with Intel Corporation or its suppliers or licensors. +* The Material contains proprietary information of Intel or its suppliers and +* licensors. The Material is protected by worldwide copyright laws and treaty +* provisions. +* No part of the Material may be used, copied, reproduced, modified, published, +* uploaded, posted, transmitted, distributed or disclosed in any way without +* Intel's prior express written permission. No license under any patent, +* copyright or other intellectual property rights in the Material is granted to +* or conferred upon you, either expressly, by implication, inducement, estoppel +* or otherwise. +* Any license under such intellectual property rights must be express and +* approved by Intel in writing. +*/ #include "mvStringUtils.h" diff --git a/inference-engine/thirdparty/movidius/watchdog/watchdog.cpp b/inference-engine/thirdparty/movidius/watchdog/watchdog.cpp index e8d348546343e6..87db12cb0c6ca5 100644 --- a/inference-engine/thirdparty/movidius/watchdog/watchdog.cpp +++ b/inference-engine/thirdparty/movidius/watchdog/watchdog.cpp @@ -24,6 +24,7 @@ #include #define _XLINK_ENABLE_PRIVATE_INCLUDE_ #include +#include "XLink_tool.h" namespace { @@ -230,7 +231,11 @@ class WatchdogImpl { threadRunning = true; poolThread = std::thread([this]() { - if (pthread_setname_np(pthread_self(), "WatchdogThread") != 0) { + if (pthread_setname_np( +#ifndef __APPLE__ + pthread_self(), +#endif + "WatchdogThread") != 0) { perror("Setting name for watchdog thread failed"); } watchdog_routine(); @@ -273,7 +278,10 @@ class WatchdogImpl { return std::get<0>(item)->getHandle() == ptr->actual->getHandle(); }); bool bFound = idx != std::end(watchedDevices); - watchedDevices.erase(idx); + if(bFound) { + watchedDevices.erase(idx); + delete ptr; + } // wake up thread since we might select removed device as nex to be ping, and there is no more devices available notificationReason = WAKE_UP_THREAD; @@ -283,17 +291,6 @@ class WatchdogImpl { return bFound; } - void clear() { - { - mvLog(MVLOG_INFO, "clear\n"); - auto __locker = lock(); - watchedDevices.clear(); - notificationReason = WAKE_UP_THREAD; - } - // wake up thread - wakeUpPingThread.notify_one(); - } - private: std::unique_lock lock() { return std::unique_lock(devicesListAcc); diff --git a/inference-engine/thirdparty/movidius/watchdog/watchdog.h b/inference-engine/thirdparty/movidius/watchdog/watchdog.h index 026f409a60405f..f4c49940b601ea 100644 --- a/inference-engine/thirdparty/movidius/watchdog/watchdog.h +++ b/inference-engine/thirdparty/movidius/watchdog/watchdog.h @@ -35,7 +35,8 @@ typedef enum { WD_API wd_error_t watchdog_init_context(wd_context *ctx); /** - * @brief creates watchdog thread, if not created, and registers new watchee device, and initialise opaque handle to it + * @brief Creates watchdog thread, if not created, and registers new watchee device, and initialise opaque handle to it. + * To avoid a memory leak, the registered device must be unregister with watchdog_unregister_device(). * @param d - newly connected device descriptor * @return */ diff --git a/inference-engine/thirdparty/ngraph.cmake b/inference-engine/thirdparty/ngraph.cmake index a930d121cb8769..673f80673a93bb 100644 --- a/inference-engine/thirdparty/ngraph.cmake +++ b/inference-engine/thirdparty/ngraph.cmake @@ -26,8 +26,11 @@ if (WIN32) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4308") endif() -set(NGRAPH_TOOLS_ENABLE FALSE) -set(NGRAPH_STATIC_LIB_ENABLE TRUE) +set(NGRAPH_TOOLS_ENABLE OFF) +set(NGRAPH_STATIC_LIB_ENABLE ON) +set(NGRAPH_JSON_ENABLE OFF) +set(NGRAPH_ADDRESS_SANITIZER OFF) + set(NGRAPH_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ngraph/src/ngraph") include_directories("${CMAKE_CURRENT_SOURCE_DIR}/ngraph/src" "${NGRAPH_SOURCE_DIR}") @@ -46,6 +49,11 @@ if (HAS_MAYBE_UNINITIALIZED) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-maybe-uninitialized -Wno-return-type") endif() endif() + +if(UNIX AND CMAKE_CXX_COMPILER_ID MATCHES Intel) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -diag-warning=1011") +endif() + # WA for GCC 7.0 if (UNIX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-return-type") diff --git a/inference-engine/thirdparty/stb_lib/CMakeLists.txt b/inference-engine/thirdparty/stb_lib/CMakeLists.txt index 46cb6cb591550c..e476f67b7de21a 100644 --- a/inference-engine/thirdparty/stb_lib/CMakeLists.txt +++ b/inference-engine/thirdparty/stb_lib/CMakeLists.txt @@ -1,6 +1,18 @@ -# Copyright (C) 2018 Intel Corporation -# SPDX-License-Identifier: Apache-2.0 +#=============================================================================== +# Copyright (C) 2018-2019 Intel Corporation # +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#=============================================================================== set(TARGET stb_image) diff --git a/inference-engine/tools/CMakeLists.txt b/inference-engine/tools/CMakeLists.txt index fddfa28a460df9..3d80df361f51cb 100644 --- a/inference-engine/tools/CMakeLists.txt +++ b/inference-engine/tools/CMakeLists.txt @@ -32,4 +32,3 @@ if (ENABLE_OPENCV) endif() add_subdirectory(vpu) - diff --git a/inference-engine/tools/accuracy_checker_tool/accuracy_check.py b/inference-engine/tools/accuracy_checker_tool/accuracy_check.py index 3d4fc2bfb5d114..6ce972183a5c10 100644 --- a/inference-engine/tools/accuracy_checker_tool/accuracy_check.py +++ b/inference-engine/tools/accuracy_checker_tool/accuracy_check.py @@ -14,6 +14,6 @@ limitations under the License. """ -from openvino.tools.accuracy_checker.accuracy_checker.main import main +from accuracy_checker.main import main main() diff --git a/inference-engine/tools/accuracy_checker_tool/convert_annotation.py b/inference-engine/tools/accuracy_checker_tool/convert_annotation.py index 5313d71c001489..7e024e1698af53 100644 --- a/inference-engine/tools/accuracy_checker_tool/convert_annotation.py +++ b/inference-engine/tools/accuracy_checker_tool/convert_annotation.py @@ -14,7 +14,7 @@ limitations under the License. """ -from openvino.tools.accuracy_checker.accuracy_checker.annotation_converters.convert import main +from accuracy_checker.annotation_converters.convert import main if __name__ == '__main__': main() diff --git a/inference-engine/tools/benchmark_tool/README.md b/inference-engine/tools/benchmark_tool/README.md index bf11be24c398fd..af4c2e36cc6b84 100644 --- a/inference-engine/tools/benchmark_tool/README.md +++ b/inference-engine/tools/benchmark_tool/README.md @@ -1,16 +1,192 @@ -# OpenVINOâ„¢ Benchmark Tool -Inference Engine Benchmark Tool is a Python\* command-line tool, which measures latency for synchronous mode. +# Benchmark Python* Tool -Please, refer to https://docs.openvinotoolkit.org for details. +This topic demonstrates how to run the Benchmark Python* Tool, which performs inference using convolutional networks. Performance can be measured for two inference modes: synchronous (latency-oriented) and asynchronous (throughput-oriented). -## Usage +> **NOTE:** This topic describes usage of Python implementation of the Benchmark Tool. For the C++ implementation, refer to [Benchmark C++ Tool](./inference-engine/samples/benchmark_app/README.md). -In general, the Benchmark Tool is configured in the same way as the Accuracy Checker. You can also use additional command line arguments to define benchmark-specific parameters: +## How It Works -| Argument | Type | Description | -| -------------------------------------------- | ------ | -------------------------------------------------------- | -| -c, --config | string | Required. Path to the YML file with local configuration | -| -ic, --benchmark_iterations_count | string | Optional. Benchmark itertations count. (1000 is default) | +Upon start-up, the application reads command-line parameters and loads a network and images/binary files to the Inference Engine plugin, which is chosen depending on a specified device. The number of infer requests and execution approach depend on the mode defined with the `-api` command-line parameter. -## Hardware requirements -Hardware requirements depend on a model. Typically for public models RAM memory size has to be not less then 16Gb independently on operation system. \ No newline at end of file +> **NOTE**: By default, Inference Engine samples, tools and demos expect input with BGR channels order. If you trained your model to work with RGB order, you need to manually rearrange the default channels order in the sample or demo application or reconvert your model using the Model Optimizer tool with `--reverse_input_channels` argument specified. For more information about the argument, refer to **When to Reverse Input Channels** section of [Converting a Model Using General Conversion Parameters](./docs/MO_DG/prepare_model/convert_model/Converting_Model_General.md). + +### Synchronous API + +For synchronous mode, the primary metric is latency. The application creates one infer request and executes the `Infer` method. A number of executions is defined by one of the two values: +* Number of iterations defined with the `-niter` command-line argument +* Time duration specified with the `-t` command-line argument +* Both of them (execution will continue until both conditions are met) +* Predefined duration if `-niter` and `-t` are not specified. Predefined duration value depends on device. + +During the execution, the application collects two types of metrics: +* Latency for each infer request executed with `Infer` method +* Duration of all executions + +Reported latency value is calculated as mean value of all collected latencies. Reported throughput value is a derivative from reported latency and additionally depends on batch size. + +### Asynchronous API +For asynchronous mode, the primary metric is throughput in frames per second (FPS). The application creates a certain number of infer requests and executes the `StartAsync` method. A number of executions is defined by one of the two values: +* Number of iterations defined with the `-niter` command-line argument +* Time duration specified with the `-t` command-line argument +* Both of them (execution will continue until both conditions are met) +* Predefined duration if `-niter` and `-t` are not specified. Predefined duration value depends on device. + +The infer requests are executed asynchronously. Callback is used to wait for previous execution to complete. The application measures all infer requests executions and reports the throughput metric based on batch size and total execution duration. + +## Run the Tool +Notice that the benchmark_app usually produces optimal performance for any device out of the box. + +**So in most cases you don't need to play the app options explicitly and the plain device name is enough**, for example, for CPU: +```sh +python3 benchmark_app.py -m -i -d CPU +``` + +But it is still may be non-optimal for some cases, especially for very small networks. More details can read in [Introduction to Performance Topics](./docs/IE_DG/Intro_to_Performance.md). + +Running the application with the `-h` or `--help`' option yields the following usage message: + +``` +usage: benchmark_app.py [-h] [-i PATH_TO_INPUT] -m PATH_TO_MODEL + [-d TARGET_DEVICE] + [-l PATH_TO_EXTENSION] [-c PATH_TO_CLDNN_CONFIG] + [-api {sync,async}] [-niter NUMBER_ITERATIONS] + [-b BATCH_SIZE] + [-stream_output [STREAM_OUTPUT]] [-t TIME] + [-progress [PROGRESS]] [-nstreams NUMBER_STREAMS] + [-nthreads NUMBER_THREADS] [-pin {YES,NO}] + [--exec_graph_path EXEC_GRAPH_PATH] + [-pc [PERF_COUNTS]] + +Options: + -h, --help Show this help message and exit. + -i PATH_TO_INPUT, --path_to_input PATH_TO_INPUT + Optional. Path to a folder with images and/or binaries + or to specific image or binary file. + -m PATH_TO_MODEL, --path_to_model PATH_TO_MODEL + Required. Path to an .xml file with a trained model. + -d TARGET_DEVICE, --target_device TARGET_DEVICE + Optional. Specify a target device to infer on: CPU, + GPU, FPGA, HDDL or MYRIAD. + Use "-d HETERO:" format to specify HETERO plugin. + Use "-d MULTI:" format to specify MULTI plugin. + The application looks for a suitable plugin for the specified device. + -l PATH_TO_EXTENSION, --path_to_extension PATH_TO_EXTENSION + Optional. Required for CPU custom layers. Absolute + path to a shared library with the kernels + implementations. + -c PATH_TO_CLDNN_CONFIG, --path_to_cldnn_config PATH_TO_CLDNN_CONFIG + Optional. Required for GPU custom kernels. Absolute + path to an .xml file with the kernels description. + -api {sync,async}, --api_type {sync,async} + Optional. Enable using sync/async API. Default value + is async. + -niter NUMBER_ITERATIONS, --number_iterations NUMBER_ITERATIONS + Optional. Number of iterations. If not specified, the + number of iterations is calculated depending on a + device. + -b BATCH_SIZE, --batch_size BATCH_SIZE + Optional. Batch size value. If not specified, the + batch size value is determined from IR + -stream_output [STREAM_OUTPUT] + Optional. Print progress as a plain text. When + specified, an interactive progress bar is replaced + with a multiline output. + -t TIME, --time TIME Optional. Time in seconds to execute topology. + -progress [PROGRESS] Optional. Show progress bar (can affect performance + measurement). Default values is "False". + -nstreams NUMBER_STREAMS, --number_streams NUMBER_STREAMS + Optional. Number of streams to use for inference on the CPU/GPU in throughput mode + (for HETERO and MULTI device cases use format :,: or just ). + Default value is determined automatically for a device. + Please note that although the automatic selection usually provides a reasonable performance, + it still may be non-optimal for some cases, especially for very small networks. + -nthreads NUMBER_THREADS, --number_threads NUMBER_THREADS + Number of threads to use for inference on the CPU + (including HETERO and MULTI cases). + -pin {YES,NO}, --infer_threads_pinning {YES,NO} + Optional. Enable ("YES" is default value) or disable + ("NO")CPU threads pinning for CPU-involved inference. + --exec_graph_path EXEC_GRAPH_PATH + Optional. Path to a file where to store executable + graph information serialized. + -pc [PERF_COUNTS], --perf_counts [PERF_COUNTS] + Optional. Report performance counters. + +``` + +Running the application with the empty list of options yields the usage message given above and an error message. + +Application supports topologies with one or more inputs. If a topology is not data sensitive, you can skip the input parameter. In this case, inputs are filled with random values. +If a model has only image input(s), please a provide folder with images or a path to an image as input. +If a model has some specific input(s) (not images), please prepare a binary file(s), which is filled with data of appropriate precision and provide a path to them as input. +If a model has mixed input types, input folder should contain all required files. Image inputs are filled with image files one by one. Binary inputs are filled with binary inputs one by one. + +To run the tool, you can use public or Intel's pre-trained models. To download the models, use the OpenVINO [Model Downloader](./tools/downloader/README.md) or go to [https://download.01.org/opencv/](https://download.01.org/opencv/). + +> **NOTE**: Before running the tool with a trained model, make sure the model is converted to the Inference Engine format (\*.xml + \*.bin) using the [Model Optimizer tool](./docs/MO_DG/Deep_Learning_Model_Optimizer_DevGuide.md). + +## Examples of Running the Tool + +This section provides step-by-step instructions on how to run the Benchmark Tool with the `googlenet-v1` public model on CPU or FPGA devices. As an input, the `car.png` file from the `/deployment_tools/demo/` directory is used. + +> **NOTE:** The Internet access is required to execute the following steps successfully. If you have access to the Internet through the proxy server only, please make sure that it is configured in your OS environment. + +1. Download the model. Go to the the Model Downloader directory and run the `downloader.py` script with specifying the model name and directory to download the model to: + ```sh + cd /deployment_tools/open_model_zoo/tools/downloader + ``` + ```sh + python3 downloader.py --name googlenet-v1 -o + ``` +2. Convert the model to the Inference Engine IR format. Go to the Model Optimizer directory and run the `mo.py` script with specifying the path to the model, model format (which must be FP32 for CPU and FPG) and output directory to generate the IR files: + ```sh + cd /deployment_tools/model_optimizer + ``` + ```sh + python3 mo.py --input_model /public/googlenet-v1/googlenet-v1.caffemodel --data_type FP32 --output_dir + ``` +3. Run the tool with specifying the `/deployment_tools/demo/car.png` file as an input image, the IR of the `googlenet-v1` model and a device to perform inference on. The following commands demonstrate running the Benchmark Tool in the asynchronous mode on CPU and FPGA devices: + + * On CPU: + ```sh + python3 benchmark_app.py -m /googlenet-v1.xml -d CPU -api async -i /deployment_tools/demo/car.png --progress true -b 1 + ``` + * On FPGA: + ```sh + python3 benchmark_app.py -m /googlenet-v1.xml -d HETERO:FPGA,CPU -api async -i /deployment_tools/demo/car.png --progress true -b 1 + ``` + +The application outputs number of executed iterations, total duration of execution, latency and throughput. +Additionally, if you set the `-pc` parameter, the application outputs performance counters. +If you set `-exec_graph_path`, the application reports executable graph information serialized. + +Below are fragments of sample output for CPU and FPGA devices: +* For CPU: + ``` + [Step 8/9] Measuring performance (Start inference asyncronously, 60000 ms duration, 4 inference requests in parallel using 4 streams) + Progress: |................................| 100.00% + + [Step 9/9] Dumping statistics report + Progress: |................................| 100.00% + + Count: 4408 iterations + Duration: 60153.52 ms + Latency: 51.8244 ms + Throughput: 73.28 FPS + ``` +* For FPGA: + ``` + [Step 10/11] Measuring performance (Start inference asyncronously, 5 inference requests using 1 streams for CPU, limits: 120000 ms duration) + Progress: |................................| 100% + + [Step 11/11] Dumping statistics report + Count: 98075 iterations + Duration: 120011.03 ms + Latency: 5.65 ms + Throughput: 817.22 FPS + ``` + +## See Also +* [Using Inference Engine Samples](./docs/IE_DG/Samples_Overview.md) +* [Model Optimizer](./docs/MO_DG/Deep_Learning_Model_Optimizer_DevGuide.md) +* [Model Downloader](./tools/downloader/README.md) \ No newline at end of file diff --git a/inference-engine/tools/benchmark_tool/benchmark.py b/inference-engine/tools/benchmark_tool/benchmark.py deleted file mode 100644 index 0e5280f214d2ac..00000000000000 --- a/inference-engine/tools/benchmark_tool/benchmark.py +++ /dev/null @@ -1,22 +0,0 @@ -""" -Copyright (C) 2018-2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import openvino.tools.benchmark as benchmark - -if __name__ == '__main__': - config = benchmark.CommandLineReader.read() - result = benchmark.Benchmark(config).run() - print("{0}: {1:.4} ms".format(config.model, result.latency * 1000.0)) \ No newline at end of file diff --git a/inference-engine/tools/benchmark_tool/benchmark_app.py b/inference-engine/tools/benchmark_tool/benchmark_app.py new file mode 100644 index 00000000000000..8c91b98e02f41e --- /dev/null +++ b/inference-engine/tools/benchmark_tool/benchmark_app.py @@ -0,0 +1,200 @@ +import os +import sys +from datetime import datetime + +from parameters import parse_args +from openvino.tools.benchmark.benchmark import Benchmark +from openvino.tools.benchmark.utils.constants import MULTI_DEVICE_NAME +from openvino.tools.benchmark.utils.infer_request_wrap import InferRequestsQueue +from openvino.tools.benchmark.utils.inputs_filling import get_inputs +from openvino.tools.benchmark.utils.logging import logger +from openvino.tools.benchmark.utils.progress_bar import ProgressBar +from openvino.tools.benchmark.utils.utils import next_step, read_network, config_network_inputs, get_number_iterations, \ + process_help_inference_string, print_perf_counters, dump_exec_graph, get_duration_in_milliseconds, \ + get_command_line_arguments +from openvino.tools.benchmark.utils.statistics_report import StatisticsReport, averageCntReport, detailedCntReport + +def main(args): + statistics = None + try: + if args.number_streams is None: + logger.warn(" -nstreams default value is determined automatically for a device. " + "Although the automatic selection usually provides a reasonable performance, " + "but it still may be non-optimal for some cases, for more information look at README. ") + + if args.report_type: + statistics = StatisticsReport(StatisticsReport.Config(args.report_type, args.report_folder)) + statistics.add_parameters(StatisticsReport.Category.COMMAND_LINE_PARAMETERS, get_command_line_arguments(sys.argv)) + + + # ------------------------------ 2. Loading Inference Engine --------------------------------------------------- + next_step() + + device_name = args.target_device.upper() + + benchmark = Benchmark(args.target_device, args.number_infer_requests, + args.number_iterations, args.time, args.api_type) + + benchmark.add_extension(args.path_to_extension, args.path_to_cldnn_config) + + version = benchmark.get_version_info() + + logger.info(version) + + # --------------------- 3. Read the Intermediate Representation of the network --------------------------------- + next_step() + + start_time = datetime.now() + ie_network = read_network(args.path_to_model) + duration_ms = "{:.2f}".format((datetime.now() - start_time).total_seconds() * 1000) + if statistics: + logger.info("Read network took {} ms".format(duration_ms)) + statistics.add_parameters(StatisticsReport.Category.EXECUTION_RESULTS, + [ + ('read network time (ms)', duration_ms) + ]) + + # --------------------- 4. Resizing network to match image sizes and given batch ------------------------------- + + next_step() + if args.batch_size and args.batch_size != ie_network.batch_size: + benchmark.reshape(ie_network, args.batch_size) + batch_size = ie_network.batch_size + logger.info('Network batch size: {}, precision: {}'.format(ie_network.batch_size, ie_network.precision)) + + # --------------------- 5. Configuring input of the model ------------------------------------------------------ + next_step() + + config_network_inputs(ie_network) + + # --------------------- 6. Setting device configuration -------------------------------------------------------- + next_step() + benchmark.set_config(args.number_streams, args.api_type, args.number_threads, + args.infer_threads_pinning) + + # --------------------- 7. Loading the model to the device ----------------------------------------------------- + next_step() + + start_time = datetime.now() + perf_counts = True if args.perf_counts or \ + args.report_type in [ averageCntReport, detailedCntReport ] or \ + args.exec_graph_path else False + exe_network = benchmark.load_network(ie_network, perf_counts, args.number_infer_requests) + duration_ms = "{:.2f}".format((datetime.now() - start_time).total_seconds() * 1000) + if statistics: + logger.info("Load network took {} ms".format(duration_ms)) + statistics.add_parameters(StatisticsReport.Category.EXECUTION_RESULTS, + [ + ('load network time (ms)', duration_ms) + ]) + + # --------------------- 8. Setting optimal runtime parameters -------------------------------------------------- + next_step() + + # Number of requests + infer_requests = exe_network.requests + benchmark.nireq = len(infer_requests) + + # Iteration limit + benchmark.niter = get_number_iterations(benchmark.niter, len(exe_network.requests), args.api_type) + + # ------------------------------------ 9. Creating infer requests and filling input blobs ---------------------- + next_step() + + request_queue = InferRequestsQueue(infer_requests) + + path_to_input = os.path.abspath(args.path_to_input) if args.path_to_input else None + requests_input_data = get_inputs(path_to_input, batch_size, ie_network.inputs, infer_requests) + + if statistics: + statistics.add_parameters(StatisticsReport.Category.RUNTIME_CONFIG, + [ + ('topology', ie_network.name), + ('target device', device_name), + ('API', args.api_type), + ('precision', str(ie_network.precision)), + ('batch size', str(ie_network.batch_size)), + ('number of iterations', str(benchmark.niter) if benchmark.niter else "0"), + ('number of parallel infer requests', str(benchmark.nireq)), + ('duration (ms)', str(get_duration_in_milliseconds(benchmark.duration_seconds))), + ]) + + for nstreams in benchmark.device_number_streams.items(): + statistics.add_parameters(StatisticsReport.Category.RUNTIME_CONFIG, + [ + ("number of {} streams".format(nstreams[0]), str(nstreams[1])), + ]) + + # ------------------------------------ 10. Measuring performance ----------------------------------------------- + + output_string = process_help_inference_string(benchmark) + + next_step(output_string) + progress_bar_total_count = 10000 + if benchmark.niter and not benchmark.duration_seconds: + progress_bar_total_count = benchmark.niter + + progress_bar = ProgressBar(progress_bar_total_count, args.stream_output, args.progress) + + fps, latency_ms, total_duration_sec, iteration = benchmark.infer(request_queue, requests_input_data, + batch_size, progress_bar) + + # ------------------------------------ 11. Dumping statistics report ------------------------------------------- + next_step() + + if args.exec_graph_path: + dump_exec_graph(exe_network, args.exec_graph_path) + + if perf_counts: + perfs_count_list = [] + for ni in range(int(benchmark.nireq)): + perfs_count_list.append(exe_network.requests[ni].get_perf_counts()) + if args.perf_counts: + print_perf_counters(perfs_count_list) + if statistics: + statistics.dump_performance_counters(perfs_count_list) + + if statistics: + statistics.add_parameters(StatisticsReport.Category.EXECUTION_RESULTS, + [ + ('total execution time (ms)', '{:.2f}'.format(get_duration_in_milliseconds(total_duration_sec))), + ('total number of iterations', str(iteration)), + ]) + if MULTI_DEVICE_NAME not in device_name: + statistics.add_parameters(StatisticsReport.Category.EXECUTION_RESULTS, + [ + ('latency (ms)', '{:.2f}'.format(latency_ms)), + ]) + + statistics.add_parameters(StatisticsReport.Category.EXECUTION_RESULTS, + [ + ('throughput', '{:.2f}'.format(fps)), + ]) + + if statistics: + statistics.dump() + + print('Count: {} iterations'.format(iteration)) + print('Duration: {:.2f} ms'.format(get_duration_in_milliseconds(total_duration_sec))) + if MULTI_DEVICE_NAME not in device_name: + print('Latency: {:.2f} ms'.format(latency_ms)) + print('Throughput: {:.2f} FPS'.format(fps)) + + del exe_network + + next_step.step_id = 0 + except Exception as e: + logger.exception(e) + + if statistics: + statistics.add_parameters(StatisticsReport.Category.EXECUTION_RESULTS, + [ + ('error', str(e)), + ]) + statistics.dump() + +if __name__ == "__main__": + # ------------------------------ 1. Parsing and validating input arguments ------------------------------------- + next_step() + + main(parse_args()) diff --git a/inference-engine/tools/benchmark_tool/parameters.py b/inference-engine/tools/benchmark_tool/parameters.py new file mode 100644 index 00000000000000..cf92b5bda65038 --- /dev/null +++ b/inference-engine/tools/benchmark_tool/parameters.py @@ -0,0 +1,98 @@ +import argparse +from fnmatch import fnmatch + +from openvino.tools.benchmark.utils.constants import XML_EXTENSION_PATTERN + + +def str2bool(v): + if v.lower() in ('yes', 'true', 't', 'y', '1'): + return True + elif v.lower() in ('no', 'false', 'f', 'n', '0'): + return False + else: + raise argparse.ArgumentTypeError('Boolean value expected.') + + +def validate_args(args): + if args.number_iterations is not None and args.number_iterations < 0: + raise Exception("Number of iterations should be positive (invalid -niter option value)") + if args.number_infer_requests and args.number_infer_requests < 0: + raise Exception("Number of inference requests should be positive (invalid -nireq option value)") + if not fnmatch(args.path_to_model, XML_EXTENSION_PATTERN): + raise Exception('Path {} is not xml file.') + + +def parse_args(): + parser = argparse.ArgumentParser(add_help=False) + args = parser.add_argument_group('Options') + args.add_argument('-h', '--help', action='help', default=argparse.SUPPRESS, + help='Show this help message and exit.') + args.add_argument('-i', '--path_to_input', type=str, required=False, + help='Optional. ' + 'Path to a folder with images and/or binaries or to specific image or binary file.') + args.add_argument('-m', '--path_to_model', type=str, required=True, + help='Required. Path to an .xml file with a trained model.') + args.add_argument('-d', '--target_device', type=str, required=False, default='CPU', + help='Optional. Specify a target device to infer on: CPU, GPU, FPGA, HDDL or MYRIAD. ' + 'Use \'-d HETERO:\' format to specify HETERO plugin. ' + 'Use \'-d MULTI:\' format to specify MULTI plugin. ' + 'The application looks for a suitable plugin for the specified device.') + args.add_argument('-l', '--path_to_extension', type=str, required=False, default=None, + help='Optional. Required for CPU custom layers. ' + 'Absolute path to a shared library with the kernels implementations.') + args.add_argument('-c', '--path_to_cldnn_config', type=str, required=False, + help='Optional. Required for GPU custom kernels. Absolute path to an .xml file with the ' + 'kernels description.') + args.add_argument('-api', '--api_type', type=str, required=False, default='async', choices=['sync', 'async'], + help='Optional. Enable using sync/async API. Default value is async.') + args.add_argument('-niter', '--number_iterations', type=int, required=False, default=None, + help='Optional. Number of iterations. ' + 'If not specified, the number of iterations is calculated depending on a device.') + args.add_argument('-nireq', '--number_infer_requests', type=int, required=False, default=None, + help='Optional. Number of infer requests. Default value is determined automatically for device.') + args.add_argument('-b', '--batch_size', type=int, required=False, default=None, + help='Optional. ' + + 'Batch size value. ' + + 'If not specified, the batch size value is determined from Intermediate Representation') + args.add_argument('-stream_output', type=str2bool, required=False, default=False, nargs='?', const=True, + help='Optional. ' + 'Print progress as a plain text. ' + 'When specified, an interactive progress bar is replaced with a multi-line output.') + args.add_argument('-t', '--time', type=int, required=False, default=None, + help='Optional. Time in seconds to execute topology.') + args.add_argument('-progress', type=str2bool, required=False, default=False, nargs='?', const=True, + help='Optional. ' + 'Show progress bar (can affect performance measurement). Default values is \'False\'.') + args.add_argument('-nstreams', '--number_streams', type=str, required=False, default=None, + help='Optional. Number of streams to use for inference on the CPU/GPU in throughput mode ' + '(for HETERO and MULTI device cases use format :,: ' + 'or just ). ' + 'Default value is determined automatically for a device. Please note that although the automatic selection ' + 'usually provides a reasonable performance, it still may be non - optimal for some cases, especially for very small networks. ' + 'See samples README for more details.') + + args.add_argument('-nthreads', '--number_threads', type=int, required=False, default=None, + help='Number of threads to use for inference on the CPU ' + '(including HETERO and MULTI cases).') + args.add_argument('-pin', '--infer_threads_pinning', type=str, required=False, default='YES', choices=['YES', 'NO'], + help='Optional. Enable (\'YES\' is default value) or disable (\'NO\')' + 'CPU threads pinning for CPU-involved inference.') + args.add_argument('--exec_graph_path', type=str, required=False, + help='Optional. Path to a file where to store executable graph information serialized.') + args.add_argument('-pc', '--perf_counts', type=str2bool, required=False, default=False, nargs='?', const=True, + help='Optional. Report performance counters.', ) + args.add_argument('--report_type', type=str, required=False, + choices=['no_counters', 'average_counters', 'detailed_counters'], + help="Optional. Enable collecting statistics report. \"no_counters\" report contains " + "configuration options specified, resulting FPS and latency. \"average_counters\" " + "report extends \"no_counters\" report and additionally includes average PM " + "counters values for each layer from the network. \"detailed_counters\" report " + "extends \"average_counters\" report and additionally includes per-layer PM " + "counters and latency for each executed infer request.") + args.add_argument('--report_folder', type=str, required=False, default='', + help="Optional. Path to a folder where statistics report is stored.") + parsed_args = parser.parse_args() + + validate_args(parsed_args) + + return parsed_args diff --git a/inference-engine/tools/benchmark_tool/requirements.txt b/inference-engine/tools/benchmark_tool/requirements.txt index 9b6a06127421c7..7042cb2a066171 100644 --- a/inference-engine/tools/benchmark_tool/requirements.txt +++ b/inference-engine/tools/benchmark_tool/requirements.txt @@ -1,17 +1,4 @@ -joblib==0.13.2 -nibabel==2.4.1 -numpy==1.16.4 -opencv-python==4.1.0.25 -Pillow==6.0.0 -pkg-resources==0.0.0 -progress==1.5 -py-cpuinfo==5.0.0 -PyYAML==5.1.1 -scikit-learn==0.21.2 -scipy==1.3.0 -Shapely==1.6.4.post2 -six==1.12.0 -sklearn==0.0 -tqdm==4.32.2 -xmltodict==0.12.0 -yamlloader==0.5.5 +py-cpuinfo +numpy +progress +opencv-python \ No newline at end of file diff --git a/inference-engine/tools/calibration_tool/README.md b/inference-engine/tools/calibration_tool/README.md index 219bc0b240ad32..06193dbb6dc179 100644 --- a/inference-engine/tools/calibration_tool/README.md +++ b/inference-engine/tools/calibration_tool/README.md @@ -1,89 +1,116 @@ # Python* Calibration Tool -The Python* Calibration Tool calibrates a given FP32 model so that you can run calibrated model in low-precision 8-bit integer mode while keeping the input data of this model in the original precision. -The Calibration Tool is a Python\* command-line tool, which imports Python types from the `openvino.tools.calibration` package. +## Introduction + +The Calibration Tool quantizes a given FP16 or FP32 model and produces a low-precision 8-bit integer (INT8) model with keeping model inputs in the original precision. To learn more about benefits of inference in INT8 precision, refer to [Using Low-Precision 8-bit Integer Inference](./docs/IE_DG/Int8Inference.md). > **NOTE**: INT8 models are currently supported only by the CPU plugin. For the full list of supported configurations, see the [Supported Devices](./docs/IE_DG/supported_plugins/Supported_Devices.md) topic. -## Hardware requirements -Hardware requirements depend on a model. Typically for public models RAM memory size has to be not less then 16Gb, drive has to have not less then 30 GB free space independently on operation system. Temporary directory is used to cache layers output during calibration. +You can run the Calibration Tool in two modes: + +* The **standard mode** performs quantization with the minimal accuracy drop within specified threshold compared to accuracy of the original model. This mode utilizes the [Accuracy Checker tool](./tools/accuracy_checker/README.md) to measure accuracy during the calibration process. Use this mode to obtain an INT8 IR that can be directly used in your application + +* The **simplified mode** produces the IR that contains plain statistics for each layer which is collected without any accuracy check, meaning that the accuracy of the new IR with statistics might be dramatically low. Therefore, all layers are considered to be executed in INT8. Use this mode to understand a potential performance gain of model conversion to INT8 precision and make a conclusion about running the standard mode routine. + +The Calibration Tool is a Python\* command-line tool, which imports Python types from the `openvino.tools.calibration` package. + +## System Requirements +Hardware requirements depend on a model. Typically for public models RAM memory size has to be not less then 16GB, drive has to have not less then 30 GB free space independently on operation system. Temporary directory is used to cache layers output during calibration. ## Usage -The Calibration Tool is configured in the same way as the Accuracy Checker. You can also use additional command-line arguments to define calibration-specific parameters. +You can run the Calibration Tool in either standard or simplified mode with an appropriate set of configuration parameters. + +### Standard Mode +In the standard mode, the Calibration Tool is configured in the same way as the Accuracy Checker. + +> **NOTE**: For consistency reasons, a part of arguments have the same name and meaning as in the Accuracy Checker and can be reused for running the Accuracy Checker. + +For configuring the tool, you can use the following command-line arguments: + +**Command-Line arguments common for the Calibration Tool and Accuracy Checker** -### Command-Line Arguments for the Accuracy Checker Tool reused in Calibration Tool | Argument | Type | Description | | -------------------------------------------- | ------ | ------------------------------------------------------- | -| -c, --config | string | Optional. Path to the YML file with local configuration | -| -d, --definitions | string | Optional. Path to the YML file with definitions | -| -m, --models | string | Optional. Prefix path to the models and weights. In the simplified mode, it is the path to IR .xml file | -| -s, --source | string | Optional. Prefix path to the data source. In the simplified mode, it is the path to a folder with images | -| -a, --annotations | string | Optional. Prefix path to the converted annotations and datasets meta data | -| -e, --extensions | string | Optional. Prefix path to extensions folder. In simplified mode is a path to extensions library | -| --cpu_extensions_mode, --cpu-extensions-mode | string | Optional. specified preferable set of processor instruction for automatic searching the CPU extension lib: `avx2` or `sse4` | -| -C, --converted_models, --converted-models | string | Optional. Directory to store Model Optimizer converted models. Used for DLSDK launcher only | -| -M, --model_optimizer, --model-optimizer | string | Optional. Path to model optimizer Caffe* directory | -| --tf_custom_op_config_dir, --tf-custom-op-config-dir | string | Optional. Path to directory with TensorFlow* custom operation configuration files for model optimizer | -| --tf_obj_detection_api_pipeline_config_path, --tf-obj-detection-api-pipeline-config-path | string | Optional. Path to directory with TensorFlow object detection API pipeline configuration files for the Model Optimizer | -| --progress | string | Optional. Progress reporter: `bar`, `print` or `None` | -| -td, --target_devices, --target-devices | string | Optional. Space-separated list of devices for infer | -| -tt, --target_tags, --target-tags | string | Optional. Space-separated list of launcher tags for infer | - -### Specific Command Line Arguments for Calibration Tool +| `-c`, `--config` | string | Required. Path to the YML file with local configuration. | +| `-d`, `--definitions` | string | Optional. Path to the YML file with definitions. | +| `-m`, `--models` | string | Optional. Prefix path to the models and weights. | +| `-s`, `--source` | string | Optional. Prefix path to the data source. | +| `-a`, `--annotations` | string | Optional. Prefix path to the converted annotations and datasets meta data. | +| `-e`, `--extensions` | string | Optional. Prefix path to extensions folder. | +| `--cpu_extensions_mode`, `--cpu-extensions-mode` | string | Optional. Preferable set of processor instruction for automatic searching the CPU extension lib: `avx2` or `sse4`. | +| `-C`, `--converted_models`, `--converted-models` | string | Optional. Directory to store Model Optimizer converted models.| +| `-M`, `--model_optimizer`, `--model-optimizer` | string | Optional. Path to model optimizer Caffe* directory. | +| `--tf_custom_op_config_dir`, `--tf-custom-op-config-dir` | string | Optional. Path to directory with TensorFlow* custom operation configuration files for model optimizer. | +| `--tf_obj_detection_api_pipeline_config_path`, `--tf-obj-detection-api-pipeline-config-path` | string | Optional. Path to directory with TensorFlow object detection API pipeline configuration files for the Model Optimizer. | +| `--progress` | string | Optional. Progress reporter: `bar`, `print` or `None` | +| `-td`, `--target_devices`, `--target-devices` | string | Optional. Space-separated list of devices for infer | +| `-tt`, `--target_tags`, `--target-tags` | string | Optional. Space-separated list of launcher tags for infer | + +**Command Line Arguments specific for Calibration Tool** + | Argument | Type | Description | | --------------------------------- | ------ | --------------------------------------------------------- | -| -p, --precision | string | Optional. Precision to calibrate. Default value is INT8. In the simplified mode, determines output IR precision | -| --ignore_layer_types, --ignore-layer-types | string | Optional. Layer types list which will be skipped during quantization | -| --ignore_layer_types_path, --ignore-layer-types-path | string | Optional. Ignore layer types file path | -| --ignore_layer_names, --ignore-layer-names | string | Optional. Layer names list which will be skipped during quantization | -| --ignore_layer_names_path, --ignore-layer-names-path | string | Optional. Ignore layer names file path | -| --batch_size, --batch-size | integer| Optional. Batch size value. If not specified, the batch size value is determined from IR | -| -th, --threshold | float | Optional. Accuracy drop of quantized model should not exceed this threshold. Should be pointer in percents without percent sign. (1% is default) | -| -ic, --benchmark_iterations_count, --benchmark-iterations-count | integer | Optional. Benchmark iterations count (1 is default). | -| -mn, --metric_name, --metric-name | string | Optional. Metric name used during calibration | -| -mt, --metric_type, --metric-type | string | Optional. Metric type used during calibration | -| -o, --output_dir, --output-dir | string | Optional. Directory to store converted models. Original model directory is used if not defined | - -### Simplified mode +| `-p`, `--precision` | string | Optional. Precision to calibrate. Default value is INT8. In the simplified mode, determines output IR precision. | +| `--ignore_layer_types`, `--ignore-layer-types` | string | Optional. Layer types list which will be skipped during quantization. | +| `--ignore_layer_types_path`, `--ignore-layer-types-path` | string | Optional. Ignore layer types file path. | +| `--ignore_layer_names`, `--ignore-layer-names` | string | Optional. Layer names list which will be skipped during quantization. | +| `--ignore_layer_names_path`, `--ignore-layer-names-path` | string | Optional. Ignore layer names file path. | +| `--batch_size`, `--batch-size` | integer| Optional. Batch size value. If not specified, the batch size value is determined from IR. | +| `-th`, `--threshold` | float | Optional. Accuracy drop of quantized model should not exceed this threshold. Should be pointer in percents without percent sign. (1% is default). | +| `-ic`, `--benchmark_iterations_count`, `--benchmark-iterations-count` | integer | Optional. Benchmark iterations count (1 is default). | +| `-mn`, `--metric_name`, `--metric-name` | string | Optional. Metric name used during calibration. | +| `-mt`, `--metric_type`, `--metric-type` | string | Optional. Metric type used during calibration. | +| `-o`, `--output_dir`, `--output-dir` | string | Optional. Directory to store converted models. Original model directory is used if not defined. | + +### Simplified Mode + +The tool in this mode does not use the Accuracy Checker, configuration and annotation files, but you are required to specify paths to an IR .xml file and a dataset folder. Optionally, you can specify a prefix path to an extensions folder and the number of images from the dataset folder: + | Argument | Type | Description | | --------------------------------- | ------ | --------------------------------------------------------- | -| -sm, --simplified_mode, --simplified-mode | | Optional. If specified, the Calibration Tool collects statistics without searching for optimal data thresholds. | -| -ss, --subset | integer | Optional. This option is used only with --simplified_mode. Specifies a number of images from a folder that is set using `-s` option. | +| `-sm`, `--simplified_mode`, `--simplified-mode` | | Required. If specified, the Calibration Tool runs in the simplified mode to collects statistics without searching for optimal data thresholds. | +| `-m` | string | Required. Path to the IR .xml file. | +| `-s`, `--source` | string | Optional. Path to a folder with images. | +| `-ss`, `--subset` | integer | Optional. This option is used only with `--simplified_mode`. Specifies a number of images from a folder that is set using `-s` option. | +| `-e`, `--extensions` | string | Optional. Prefix path to extensions folder. | +| `-td`, `--target_devices`, `--target-devices` | string | Optional. Space-separated list of devices for infer. | +| `-p`, `--precision` | string | Optional. Precision to calibrate. Default value is INT8. In the simplified mode, determines output IR precision. | +| `-o`, `--output_dir`, `--output-dir` | string | Optional. Directory to store converted models. Original model directory is used if not defined. | -## Model Calibration Flow +## Typical Workflow Samples (Standard Mode) ### Introduction -The calibration tool read original FP32 model, calibration dataset and create low precision model. Low precision model has two differences from original model: +The calibration tool reads original FP16 or FP32 models, calibration dataset and creates a low precision model. The low precision model has two differences from the original model: 1. Per channel statistics are defined. Statistics have minimum and maximum values for each layer and each channel. Model statistics are stored in Inference Engine intermediate representation file (IR) in XML format. 2. `quantization_level` layer attribute is defined. The attribute defines precision which is used during inference. ### Prerequisites -* Model: Tensorflow\* Inception v1. You can download the model from here: https://github.com/tensorflow/models/tree/master/research/slim +* Model: TensorFlow* Inception v1. You can download the model from here: https://github.com/tensorflow/models/tree/master/research/slim * Dataset: ImageNet. You can download ImageNet from here: http://www.image-net.org/download.php * YML configuration files: you can find YML configuration files and YML definition file which are used below in `configs` directory: - `definitions.yml` - definition file - - `inception_v1.yml` - configuration file for Tensorflow\* Inception v1 model - - `ncf_config.yml` - configuration file for NCF model in OpenVINO\* Inference Engine Intermediate Representation format - - `ssd_mobilenet_v1_coco.yml` - configuration file for Tensorflow\* SSD Mobilenet v1 model - - `unet2d.yml` - configuration file for Unet2D mode in in OpenVINO\* Inference Engine Intermediate Representation format + - `inception_v1.yml` - configuration file for TensorFlow* Inception v1 model + - `ncf_config.yml` - configuration file for NCF model in OpenVINO Inference Engine Intermediate Representation format + - `ssd_mobilenet_v1_coco.yml` - configuration file for TensorFlow* SSD Mobilenet v1 model + - `unet2d.yml` - configuration file for Unet2D mode in in OpenVINO Inference Engine Intermediate Representation format -If you have custom topology with not supported accuracy metric or not suported custom dataset then you should add some components implementation in `openvino.tools.accuracy_checker` Python\* package yourself. Refer to `openvino.tools.accuracy_checker` documentation how to implement metric and dataset support. +If your custom topology does not support accuracy metric or a custom dataset, add some components implementation in `openvino.tools.accuracy_checker` Python\* package yourself. For more information about metric implementation and dataset support, go to the [Accuracy Checker documentation](./tools/accuracy_checker/README.md). There are steps to calibrate and evaluate result model: -- Step #1. Convert data annotation files -- Optional step for low precision model performance estimation. -- Step #2. Calibration -- Step #3. Result model evaluation +1. Convert data annotation files. +2. (Optional) Estimate low precision model performance. +3. Calibrate the model. +4. Evaluate the resulting model. Additional optional step before calibration is available to rough estimate possible INT8 performance. -### Step #1. Convert Data Annotation Files +### Convert Data Annotation Files Calibration dataset is subset of training dataset. Use Convert Annotation Tool to convert ImageNet\* dataset to Calibration Tool readable data annotation files. Data annotation files describe subset of images which are used during calibration. Command line: ```sh python convert_annotation.py imagenet --annotation_file /datasets/ImageNet/val.txt --labels_file /datasets/ImageNet/synset_words.txt -ss 2000 -o ~/annotations -a imagenet.pickle -m imagenet.json ``` -> **NOTE:** For simplicity all command line tools in below steps use the same command line arguments. In practice [Collect Statistics Tool](./inference-engine/tools/collect_statistics_tool/README.md) uses calibration dataset, but [Accuracy Checker Tool](./inference-engine/tools/accuracy_checker_tool/README.md) has to use whole validation dataset. +> **NOTE:** For simplicity, all command line tools in the steps below use the same command line arguments. In practice [Collect Statistics Tool](./inference-engine/tools/collect_statistics_tool/README.md) uses calibration dataset, but [Accuracy Checker Tool](./tools/accuracy_checker/README.md) has to use the whole validation dataset. | Argument | Type | Description | @@ -93,11 +120,11 @@ python convert_annotation.py imagenet --annotation_file /datasets/ImageNet/val.t | -M | string | Path to model optimizer directory | | --models | string | Prefix path to the models and weights | | --source | string | Prefix path to the data source | -| --annotations | string | Pefix path to the converted annotations and datasets meta data | -| --converted_models | string | Directory to store Model Optimizer converted models. Used for DLSDK launcher only | +| --annotations | string | Prefix path to the converted annotations and datasets meta data | +| --converted_models | string | Directory to store Model Optimizer converted models | -### Optional Step for Low Precision Model Performance Estimation +### (Optional) Estimate Low-Precision Model Performance Before calibration, you can roughly estimate low precision performance with [Collect Statistics Tool](./inference-engine/tools/collect_statistics_tool/README.md). @@ -109,14 +136,14 @@ Command line: python collect_statistics.py --config ~/inception_v1.yml -d ~/defenitions.yml -M /home/user/intel/openvino/deployment_tools/model_optimizer --models ~/models --source /media/user/calibration/datasets --annotations ~/annotations --converted_models ~/models ``` -Result model has statistics which allow you to infer this model in INT8 precision. To measure performance you can use [Benchmark Tool](./inference-engine/tools/benchmark_tool/README.md). +Result model has statistics which allow you to infer this model in INT8 precision. To measure performance, you can use the [Benchmark App](./inference-engine/ie_bridges/python/sample/benchmark_app/README.md). -### Step #2. Calibration -During calibration process, the model is ajusted for efficient quantization and minimization of accuracy drop on calibration dataset. Calibration tool produces calibrated model which will be executed in low precision 8 bit quantzed mode after loading into CPU plugin. +### Calibrate the Model +During calibration process, the model is adjusted for efficient quantization and minimization of accuracy drop on calibration dataset. Calibration tool produces calibrated model which will be executed in low precision 8-bit quantized mode after loading into CPU plugin. [Calibration Tool](./inference-engine/tools/calibration_tool/README.md) has flexible and extensible mechanism of enabling new data set and metrics. Each network has its own dedicated network metric and dataset where network was trained. Dataset description and network metrics can be reused for different network. -To plug new dataset you need to develop YML file. To develop new metric you need to develop Python\* module implementing metric and describe in YML. Please, refer to [Accuracy Checker Tool](./inference-engine/tools/accuracy_checker_tool/README.md) for details. +To plug new dataset you need to develop YML file. To develop new metric you need to develop Python\* module implementing metric and describe in YML. Please, refer to [Accuracy Checker Tool](./tools/accuracy_checker/README.md) for details. Command line example: @@ -124,17 +151,17 @@ Command line example: python calibrate.py --config ~/inception_v1.yml --definition ~/defenitions.yml -M /home/user/intel/openvino/deployment_tools/model_optimizer --tf_custom_op_config_dir ~/tf_custom_op_configs --models ~/models --source /media/user/calibration/datasets --annotations ~/annotations ``` -### Step #3. Result model evaluation -After calibration of the model it worse to evaluate network accuracy on whole validation set using [Accuracy Checker Tool](./inference-engine/tools/accuracy_checker_tool/README.md). +### Evaluate the Resulting Model +After calibration of the model it worse to evaluate network accuracy on whole validation set using [Accuracy Checker Tool](./tools/accuracy_checker/README.md). -#### Step #3.1 Check accuracy +#### Check accuracy Command line: ```sh python accuracy_check.py --config ~/inception_v1.yml -d ~/defenitions.yml -M /home/user/intel/openvino/deployment_tools/model_optimizer --tf_custom_op_config_dir ~/tf_custom_op_configs --models ~/models --source /media/user/calibration/datasets --annotations ~/annotations -tf dlsdk -td CPU ``` -#### Step #3.2 Check performance -Use `benchmark_app` command line tool to measure latency and throughput for synchronous and asynchronous modes. Note, please, `benchmark_app` command line tool uses converted OpenVINO\* Intermediate Representation model. +#### Check performance +Use the [Benchmark App](./inference-engine/samples/benchmark_app/README.md) command line tool to measure latency and throughput for synchronous and asynchronous modes. Note, the Benchmark App command line tool uses converted OpenVINO* Intermediate Representation model. Command line for synchronous mode: @@ -147,21 +174,10 @@ Command line for the asynchronous mode: ./benchmark_app -i /inputImage.bmp -m /inception_v1.xml -d CPU -api async ``` -#### Optional step to check performance -You can use Python\* [Benchmark Tool](./inference-engine/tools/benchmark_tool/README.md) command line tool to quickly check performance with the same command line arguments and configuration YML files as for [Calibration Tool](./inference-engine/tools/calibration_tool/README.md). - -Command line: -```sh -python benchmark.py --config ~/inception_v1.yml -d ~/defenitions.yml -M /home/user/intel/openvino/deployment_tools/model_optimizer --tf_custom_op_config_dir ~/tf_custom_op_configs --models ~/models --source /media/user/calibration/datasets --annotations ~/annotations --converted_models ~/models -``` - -## Simplified Mode Flow - -The Calibration Tool in the simplified mode helps to quickly estimate performance of a model. It converts all possible layers into INT8 and collects statistics without achieving needed accuracy. The tool generates new IR, which is used in performance tests. Therefore, the tool in this mode does not use Accuracy Checker, configuration and annotation files, but you should specify paths to an IR .xml file and a dataset folder. Optionally, you can specify a path to an extensions library and the number of images from the dataset folder. In simplified mode path to an extensions is a path to extensions library. +## Typical Workflow Samples (Simplified Mode) To run the Calibration Tool in the simplified mode, use the following command: ```sh -python3 calibrate.py -sm -m -s -ss img_num -e -td target_device +python3 calibrate.py -sm -m -s -ss -e -td -precision --output-dir ``` -It accepts models with FP32, FP16 precisions and images as dataset. - +It accepts models with FP32, FP16 precisions and image files as the dataset. \ No newline at end of file diff --git a/inference-engine/tools/calibration_tool/statistics_collector/CMakeLists.txt b/inference-engine/tools/calibration_tool/statistics_collector/CMakeLists.txt index ca082f54d3c69b..a1c9168154cc83 100644 --- a/inference-engine/tools/calibration_tool/statistics_collector/CMakeLists.txt +++ b/inference-engine/tools/calibration_tool/statistics_collector/CMakeLists.txt @@ -53,4 +53,8 @@ target_link_libraries(${TARGET_NAME} ${TARGET_NAME_LIB} gflags) if(UNIX) target_link_libraries(${TARGET_NAME} ${LIB_DL} pthread) endif() -set_ie_threading_interface_for(${TARGET_NAME_LIB}) \ No newline at end of file +set_ie_threading_interface_for(${TARGET_NAME_LIB}) + +# export for python + +export(TARGETS ${TARGET_NAME_LIB} NAMESPACE IE:: APPEND FILE "${CMAKE_BINARY_DIR}/targets_developer.cmake") diff --git a/inference-engine/tools/calibration_tool/statistics_collector/data_stats.cpp b/inference-engine/tools/calibration_tool/statistics_collector/data_stats.cpp index 0b5ca96bf4050d..31c7ebc72402aa 100644 --- a/inference-engine/tools/calibration_tool/statistics_collector/data_stats.cpp +++ b/inference-engine/tools/calibration_tool/statistics_collector/data_stats.cpp @@ -2,11 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 // +#include #include #include #include #include -#include #include #include #include diff --git a/inference-engine/tools/calibration_tool/statistics_collector/data_stats.hpp b/inference-engine/tools/calibration_tool/statistics_collector/data_stats.hpp index 8f770c5350dbc1..85f91a81ca2ec9 100644 --- a/inference-engine/tools/calibration_tool/statistics_collector/data_stats.hpp +++ b/inference-engine/tools/calibration_tool/statistics_collector/data_stats.hpp @@ -4,13 +4,13 @@ #pragma once -#include #include #include #include #include #include #include +#include struct TensorStatistic { TensorStatistic(float* data, size_t count, size_t nbuckets = 1000); @@ -51,7 +51,7 @@ class simpleDataStats : public dataStats { void getDataMinMax(const std::string& name, size_t channel, float& min, float& max, float threshold = 100.f); protected: struct statsPair { - float _min = std::numeric_limits::max();; + float _min = std::numeric_limits::max(); float _max = std::numeric_limits::min(); }; std::unordered_map> _data; diff --git a/inference-engine/tools/calibration_tool/statistics_collector/main.cpp b/inference-engine/tools/calibration_tool/statistics_collector/main.cpp index 40c21b462ca5a6..8a0cbdada3e420 100644 --- a/inference-engine/tools/calibration_tool/statistics_collector/main.cpp +++ b/inference-engine/tools/calibration_tool/statistics_collector/main.cpp @@ -35,7 +35,7 @@ static const char model_message[] = "Required. Path to an .xml file with a train static const char plugin_message[] = "Plugin name. For example, CPU. If this parameter is passed, " "the sample looks for a specified plugin only."; /// @brief Message for assigning cnn calculation to device -static const char target_device_message[] = "Target device to infer on: CPU (default), GPU, FPGA or MYRIAD." +static const char target_device_message[] = "Target device to infer on: CPU (default), GPU, FPGA, HDDL or MYRIAD." " The application looks for a suitable plugin for the specified device."; /// @brief Message for batch argument type static const char batch_message[] = "Batch size value. If not specified, the batch size value is taken from IR"; @@ -178,15 +178,10 @@ int main(int argc, char *argv[]) { showUsage(); return ex.exitCode(); } catch (const UserExceptions& ex) { - if (ex.list().size() == 1) { - slog::err << "Input problem: " << ex.what() << slog::endl; - showUsage(); - return ex.list().begin()->exitCode(); - } else { - slog::err << "Input problems: \n" << ex.what() << slog::endl; - showUsage(); + slog::err << "Input problems: \n" << ex.what() << slog::endl; + showUsage(); + if (!ex.list().empty()) return ex.list().begin()->exitCode(); - } } catch (const std::exception& ex) { slog::err << ex.what() << slog::endl; return 1; diff --git a/inference-engine/tools/calibration_tool/statistics_collector/statistics_processor.cpp b/inference-engine/tools/calibration_tool/statistics_collector/statistics_processor.cpp index 4f4f48e6fe68fa..7390c212fc9c33 100644 --- a/inference-engine/tools/calibration_tool/statistics_collector/statistics_processor.cpp +++ b/inference-engine/tools/calibration_tool/statistics_collector/statistics_processor.cpp @@ -295,7 +295,10 @@ void StatisticsCollector::fillBlobs(StatisticsCollector* collectorInstance) { progress_step = 100lu; collectorInstance->_consoleProgress = std::make_shared(img_number); - TensorDesc inputDesc = collectorInstance->_cnn_network->getInputsInfo().begin()->second->getTensorDesc(); + auto inpuInfo = collectorInstance->_cnn_network->getInputsInfo(); + if (inpuInfo.empty()) + THROW_IE_EXCEPTION << "Input info is empty"; + TensorDesc inputDesc = inpuInfo.begin()->second->getTensorDesc(); const Precision::ePrecision inputPrecision = inputDesc.getPrecision(); PreprocessingOptions preprocessingOptions; diff --git a/inference-engine/tools/vpu/CMakeLists.txt b/inference-engine/tools/vpu/CMakeLists.txt index a3b33331ac111c..738b5a84926b34 100644 --- a/inference-engine/tools/vpu/CMakeLists.txt +++ b/inference-engine/tools/vpu/CMakeLists.txt @@ -13,6 +13,7 @@ # limitations under the License. if(ENABLE_MYRIAD) + add_subdirectory(vpu_perfcheck) add_subdirectory(vpu_profile) endif() diff --git a/inference-engine/tools/vpu/common/vpu_tools_common.cpp b/inference-engine/tools/vpu/common/vpu_tools_common.cpp index 7d5fe0cb5e3b8d..1ffccb9192075b 100644 --- a/inference-engine/tools/vpu/common/vpu_tools_common.cpp +++ b/inference-engine/tools/vpu/common/vpu_tools_common.cpp @@ -31,7 +31,7 @@ #include #include "vpu_tools_common.hpp" -#include "vpu/utils/string.hpp" +#include #include "samples/common.hpp" #include "precision_utils.h" diff --git a/inference-engine/tools/vpu/vpu_compile/CMakeLists.txt b/inference-engine/tools/vpu/vpu_compile/CMakeLists.txt index 94830bae7b36e3..e0930297661c6d 100644 --- a/inference-engine/tools/vpu/vpu_compile/CMakeLists.txt +++ b/inference-engine/tools/vpu/vpu_compile/CMakeLists.txt @@ -43,7 +43,7 @@ target_link_libraries(${TARGET_NAME} PRIVATE gflags ) -add_dependencies(${TARGET_NAME} myriadPlugin vpu_copy_firmware) +add_dependencies(${TARGET_NAME} myriadPlugin) set_target_properties(${TARGET_NAME} PROPERTIES COMPILE_PDB_NAME diff --git a/inference-engine/tools/vpu/vpu_compile/main.cpp b/inference-engine/tools/vpu/vpu_compile/main.cpp index f447330a4d54dd..631c369eecb10b 100644 --- a/inference-engine/tools/vpu/vpu_compile/main.cpp +++ b/inference-engine/tools/vpu/vpu_compile/main.cpp @@ -28,7 +28,7 @@ #include "inference_engine.hpp" #include #include "samples/common.hpp" -#include "vpu/utils/string.hpp" +#include #include "vpu_tools_common.hpp" diff --git a/inference-engine/tools/vpu/vpu_perfcheck/CMakeLists.txt b/inference-engine/tools/vpu/vpu_perfcheck/CMakeLists.txt new file mode 100644 index 00000000000000..34345fd2cbef8b --- /dev/null +++ b/inference-engine/tools/vpu/vpu_perfcheck/CMakeLists.txt @@ -0,0 +1,69 @@ +# +# Copyright (c) 2018-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +function(add_perfcheck_target TARGET_NAME PLUGIN_NAME) + find_package(Threads REQUIRED) + + file(GLOB SOURCES *.cpp) + + if(WIN32) + file(GLOB WIN_PTHREAD_SOURCES "${IE_MAIN_SOURCE_DIR}/thirdparty/movidius/WinPthread/win_pthread.c") + file(GLOB_RECURSE SHARED "${IE_MAIN_SOURCE_DIR}/thirdparty/movidius/shared/*") + list(APPEND SOURCES ${WIN_PTHREAD_SOURCES} ${SHARED}) + endif() + + add_executable(${TARGET_NAME} ${SOURCES}) + + # TODO: enable some day and fix all warnings +# if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") +# target_compile_options(${TARGET_NAME} +# PRIVATE +# "-Wall") +# endif() + + target_include_directories(${TARGET_NAME} + SYSTEM PRIVATE + "${IE_MAIN_SOURCE_DIR}/include" + "${IE_MAIN_SOURCE_DIR}/src/inference_engine" + "${IE_MAIN_SOURCE_DIR}/src/vpu/graph_transformer/include" + "${IE_MAIN_SOURCE_DIR}/samples/common/samples" + "${IE_MAIN_SOURCE_DIR}/samples/common/format_reader") + + if(WIN32) + target_include_directories(${TARGET_NAME} + PRIVATE + "${IE_MAIN_SOURCE_DIR}/thirdparty/movidius/WinPthread" + "${IE_MAIN_SOURCE_DIR}/thirdparty/movidius/shared/include") + endif() + + target_link_libraries(${TARGET_NAME} + PRIVATE + inference_engine format_reader + ${CMAKE_DL_LIBS} + Threads::Threads) + + add_dependencies(${TARGET_NAME} + ${PLUGIN_NAME} ${ARGN}) + + set_target_properties(${TARGET_NAME} PROPERTIES + COMPILE_PDB_NAME ${TARGET_NAME}) + + add_cpplint_target(${TARGET_NAME}_cpplint FOR_TARGETS ${TARGET_NAME}) +endfunction() + +if(ENABLE_MYRIAD) + add_perfcheck_target(myriad_perfcheck myriadPlugin) +endif() diff --git a/inference-engine/tools/vpu/vpu_perfcheck/main.cpp b/inference-engine/tools/vpu/vpu_perfcheck/main.cpp new file mode 100644 index 00000000000000..5130c8765e1812 --- /dev/null +++ b/inference-engine/tools/vpu/vpu_perfcheck/main.cpp @@ -0,0 +1,788 @@ +/* +// Copyright (C) 2018-2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#if defined(_WIN32) +#define NOMINMAX +#endif +#if (defined(_WIN32) || defined(_WIN64)) +#define WIN32_LEAN_AND_MEAN +#include "win_pthread.h" +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static char* m_exename = nullptr; + +#if defined(WIN32) || defined(__APPLE__) +typedef std::chrono::time_point time_point; +#else +typedef std::chrono::time_point time_point; +#endif +typedef std::chrono::high_resolution_clock Time; +typedef std::chrono::duration> ms; +typedef std::chrono::duration fsec; + +#define TIMEDIFF(start, end) ((std::chrono::duration_cast((end) - (start))).count()) + +class BitMap { +private: + typedef struct { + unsigned short type = 0u; /* Magic identifier */ + unsigned int size = 0u; /* File size in bytes */ + unsigned int reserved = 0u; + unsigned int offset = 0u; /* Offset to image data, bytes */ + } BmpHeader; + + typedef struct { + unsigned int size = 0u; /* Header size in bytes */ + int width = 0, height = 0; /* Width and height of image */ + unsigned short planes = 0u; /* Number of colour planes */ + unsigned short bits = 0u; /* Bits per pixel */ + unsigned int compression = 0u; /* Compression type */ + unsigned int imagesize = 0u; /* Image size in bytes */ + int xresolution = 0, yresolution = 0; /* Pixels per meter */ + unsigned int ncolours = 0u; /* Number of colours */ + unsigned int importantcolours = 0u; /* Important colours */ + } BmpInfoHeader; + +public: + explicit BitMap(const std::string &filename) { + BmpHeader header; + BmpInfoHeader infoHeader; + + std::ifstream input(filename, std::ios::binary); + if (!input) { + return; + } + + input.read(reinterpret_cast(&header.type), 2); + + if (header.type != 'M'*256+'B') { + std::cerr << "[BMP] file is not bmp type\n"; + return; + } + + input.read(reinterpret_cast(&header.size), 4); + input.read(reinterpret_cast(&header.reserved), 4); + input.read(reinterpret_cast(&header.offset), 4); + + input.read(reinterpret_cast(&infoHeader), sizeof(BmpInfoHeader)); + + bool rowsReversed = infoHeader.height < 0; + _width = infoHeader.width; + _height = abs(infoHeader.height); + + if (infoHeader.bits != 24) { + std::cerr << "[BMP] 24bpp only supported. But input has:" << infoHeader.bits << "\n"; + return; + } + + if (infoHeader.compression != 0) { + std::cerr << "[BMP] compression not supported\n"; + } + + int padSize = _width & 3; + char pad[3]; + size_t size = _width * _height * 3; + + _data.reset(new unsigned char[size], std::default_delete()); + + input.seekg(header.offset, std::ios::beg); + + // reading by rows in invert vertically + for (uint32_t i = 0; i < _height; i++) { + uint32_t storeAt = rowsReversed ? i : (uint32_t)_height - 1 - i; + input.read(reinterpret_cast(_data.get()) + _width * 3 * storeAt, _width * 3); + input.read(pad, padSize); + } + } + + ~BitMap() = default; + + size_t _height = 0; + size_t _width = 0; + std::shared_ptr _data; + +public: + size_t size() const { return _width * _height * 3; } + size_t width() const { return _width; } + size_t height() const { return _height; } + + std::shared_ptr getData() { + return _data; + } +}; + +#define IECALL(call) \ +{ \ + if (InferenceEngine::OK != (call)) { \ + std::cout << #call " failed: " << resp.msg << std::endl; \ + return 1; \ + } \ +} + +static short f32tof16(float x); +static float f16tof32(short x); +static bool loadImage(const std::string &imageFilename, InferenceEngine::Blob::Ptr &blob); +static bool loadBinaryTensor(const std::string &binaryFilename, InferenceEngine::Blob::Ptr &blob); + + +static void setConfig(std::map& config, + const std::string& file_config_cl) { + config[VPU_CONFIG_KEY(LOG_LEVEL)] = CONFIG_VALUE(LOG_WARNING); + config[CONFIG_KEY(LOG_LEVEL)] = CONFIG_VALUE(LOG_WARNING); + config[VPU_CONFIG_KEY(PRINT_RECEIVE_TENSOR_TIME)] = CONFIG_VALUE(YES); + config[VPU_CONFIG_KEY(CUSTOM_LAYERS)] = file_config_cl; +} + +static void printPerformanceCounts(const std::map& perfMap) { + std::vector> perfVec(perfMap.begin(), + perfMap.end()); + std::sort(perfVec.begin(), perfVec.end(), + [=](const std::pair &pair1, + const std::pair &pair2) -> bool { + return pair1.second.execution_index < pair2.second.execution_index; + }); + + size_t maxLayerName = 0u, maxExecType = 0u; + for (auto & it : perfVec) { + maxLayerName = std::max(maxLayerName, it.first.length()); + maxExecType = std::max(maxExecType, std::strlen(it.second.exec_type)); + } + + size_t indexWidth = 7, nameWidth = maxLayerName + 5, typeWidth = maxExecType + 5, timeWidth = 10; + size_t totalWidth = indexWidth + nameWidth + typeWidth + timeWidth; + + std::cout << std::endl << "Detailed Per Stage Profile" << std::endl; + for (size_t i = 0; i < totalWidth; i++) + std::cout << "="; + std::cout << std::endl; + std::cout << std::setw(indexWidth) << std::left << "Index" + << std::setw(nameWidth) << std::left << "Name" + << std::setw(typeWidth) << std::left << "Type" + << std::setw(timeWidth) << std::right << "Time (ms)" + << std::endl; + for (size_t i = 0; i < totalWidth; i++) + std::cout << "-"; + std::cout << std::endl; + + long long totalTime = 0; + for (const auto& p : perfVec) { + const auto& stageName = p.first; + const auto& info = p.second; + if (info.status == InferenceEngine::InferenceEngineProfileInfo::EXECUTED) { + std::cout << std::setw(indexWidth) << std::left << info.execution_index + << std::setw(nameWidth) << std::left << stageName + << std::setw(typeWidth) << std::left << info.exec_type + << std::setw(timeWidth) << std::right << info.realTime_uSec / 1000.0 + << std::endl; + + totalTime += info.realTime_uSec; + } + } + + for (int i = 0; i < totalWidth; i++) + std::cout << "-"; + std::cout << std::endl; + std::cout << std::setw(totalWidth / 2) << std::right << "Total inference time:" + << std::setw(totalWidth / 2 + 1) << std::right << totalTime / 1000.0 + << std::endl; + for (int i = 0; i < totalWidth; i++) + std::cout << "-"; + std::cout << std::endl; +} + +static std::string getAppRealName(const char* name) { + std::string filename(name); + size_t splitpos = filename.find_last_of('\\'); + if (std::string::npos == splitpos) { + splitpos = filename.find_last_of('/'); + if (std::string::npos == splitpos) { + return filename; + } + } + return filename.substr(splitpos + 1); +} + +static void print_usage() { + std::cout << "Usage:" << std::endl << getAppRealName(m_exename) << " [number of iterations >= 1000]" + << " [batch >= 1, default=1] [num_networks, default=1] [config_file_custom_layer, default='']" << std::endl; +} + +static void getBMPFiles(std::vector &out, const std::string &directory) { + const std::string ext = ".bmp"; + DIR *dir; + dirent *ent; + dir = opendir(directory.c_str()); + if (!dir) + return; + while ((ent = readdir(dir)) != nullptr) { + const std::string file_name = ent->d_name; + const std::string full_file_name = directory + "/" + file_name; + if ((file_name.length() >= ext.length()) + && (0 == file_name.compare(file_name.length() - ext.length(), ext.length(), ext))) { + // proceed + } else { + continue; + } + struct stat st; + if (stat(full_file_name.c_str(), &st) == -1) + continue; + const bool is_directory = (st.st_mode & S_IFDIR) != 0; + if (is_directory) + continue; + out.push_back(full_file_name); + } + closedir(dir); +} + +static void getBINFiles(std::vector &out, const std::string &directory) { + const std::string ext = ".bin"; + DIR *dir; + dirent *ent; + dir = opendir(directory.c_str()); + if (!dir) + return; + while ((ent = readdir(dir)) != nullptr) { + const std::string file_name = ent->d_name; + const std::string full_file_name = directory + "/" + file_name; + if ((file_name.length() >= ext.length()) + && (0 == file_name.compare(file_name.length() - ext.length(), ext.length(), ext))) { + // proceed + } else { + continue; + } + struct stat st; + if (stat(full_file_name.c_str(), &st) == -1) + continue; + const bool is_directory = (st.st_mode & S_IFDIR) != 0; + if (is_directory) + continue; + out.push_back(full_file_name); + } + closedir(dir); +} + +int num_requests = 4; + +#define MIN_ITER 1000 + +#define USE_CALLBACK + +int niter; +std::atomic iterations_to_run; +std::mutex done_mutex; +std::condition_variable alldone; +int reallydone = 0; + +std::vector iter_start; +std::vector iter_end; +std::vector iter_time; + +const int profile = 0; +std::map perfMap; + +int process(const std::string& modelFileName, const std::string& inputsDir, + std::string& file_config_cl, int nBatch, int num_networks) { + InferenceEngine::ResponseDesc resp; + + niter /= nBatch; + num_requests = num_requests * num_networks; + + // add some more requests. they'll be excluded on performance measurement + niter += 2 * 2 * num_requests; + + if (pthread_setname_np( +#ifndef __APPLE__ + pthread_self(), +#endif + "MainThread") != 0) { + perror("Setting name for main thread failed"); + } + + InferenceEngine::PluginDispatcher disp; + InferenceEngine::InferenceEnginePluginPtr plugin( + disp.getPluginByName(std::string("myriadPlugin") + IE_BUILD_POSTFIX)); + + std::cout << "InferenceEngine: " << std::endl; + + const InferenceEngine::Version *pluginVersion = nullptr; + plugin->GetVersion(pluginVersion); + std::cout << pluginVersion << std::endl << std::endl; + + InferenceEngine::CNNNetReader netReader; + netReader.ReadNetwork(modelFileName); + + std::string binFileName = fileNameNoExt(modelFileName) + ".bin"; + netReader.ReadWeights(binFileName); + + std::ifstream file(file_config_cl); + if (!file.is_open()) { + file_config_cl.clear(); + } + + std::vector pictures; + getBMPFiles(pictures, inputsDir); + int numPictures = pictures.size(); + + std::vector binaries; + getBINFiles(binaries, inputsDir); + int numBinaries = binaries.size(); + + if (pictures.empty() && binaries.empty()) { + std::cout << inputsDir << " directory doesn't contain input files" << std::endl; + return 1; + } + + InferenceEngine::CNNNetwork cnnNetwork = netReader.getNetwork(); + + if (nBatch != 1) { + std::cout << "Setting batch to : "<< nBatch << "\n"; + cnnNetwork.setBatchSize(nBatch); + } + + InferenceEngine::InputsDataMap networkInputs; + networkInputs = cnnNetwork.getInputsInfo(); + InferenceEngine::OutputsDataMap networkOutputs; + networkOutputs = cnnNetwork.getOutputsInfo(); + + for (auto &input : networkInputs) { + input.second->setPrecision(InferenceEngine::Precision::FP16); + } + + for (auto &output : networkOutputs) { + output.second->setPrecision(InferenceEngine::Precision::FP16); + } + + std::vector exeNetwork(num_networks); + std::map networkConfig; + setConfig(networkConfig, file_config_cl); + + for (int n = 0; n < num_networks; ++n) { + if (num_networks > 1) + printf("Load network %d...\n", n); + else + printf("Load network... \n"); + fflush(stdout); + IECALL(plugin->LoadNetwork(exeNetwork[n], cnnNetwork, networkConfig, &resp)); + } + + std::vector request(num_requests); + iter_start.resize(niter); + iter_end.resize(niter); + iter_time.resize(niter); + + iterations_to_run = niter - num_requests; + + for (int r = 0, idxPic = 0; r < num_requests; ++r) { + int n = r % num_networks; + IECALL(exeNetwork[n]->CreateInferRequest(request[r], &resp)); + + for (auto &input : networkInputs) { + InferenceEngine::Blob::Ptr inputBlob; + IECALL(request[r]->GetBlob(input.first.c_str(), inputBlob, &resp)); + + const auto& dims = inputBlob->getTensorDesc().getDims(); + auto layout = inputBlob->getTensorDesc().getLayout(); + + // number of channels is 3 for Image, dims order is always NCHW + const bool isImage = ((layout == InferenceEngine::NHWC || layout == InferenceEngine::NCHW) && dims[1] == 3); + + if (isImage && (numPictures > 0)) { + if (!loadImage(pictures[(idxPic++) % numPictures], inputBlob)) + return 1; + } else if (numBinaries > 0) { + if (!loadBinaryTensor(binaries[(idxPic++) % numBinaries], inputBlob)) + return 1; + } else { + std::cout << inputsDir << " directory doesn't contain correct input files" << std::endl; + return 1; + } + } + + IECALL(request[r]->SetCompletionCallback( + [](InferenceEngine::IInferRequest::Ptr request, InferenceEngine::StatusCode code) { + if (code != InferenceEngine::OK) { + std::cout << "Infer failed: " << code << std::endl; + exit(1); + } + + int iter = --iterations_to_run; + int reqIdx = (niter - iter - 1) - num_requests; + + iter_end[reqIdx] = Time::now(); + + InferenceEngine::ResponseDesc resp; + if (profile && (reqIdx == niter / 2)) { + request->GetPerformanceCounts(perfMap, &resp); + } + + if (iter >= 0) { + iter_start[reqIdx + (num_requests)] = Time::now(); + if (InferenceEngine::OK != request->StartAsync(&resp)) { + std::cout << "StartAsync failed: " << resp.msg << std::endl; + exit(1); + } + } + + iter_time[reqIdx] = TIMEDIFF(iter_start[reqIdx], iter_end[reqIdx]); + // printf("request#%d %fms\n", reqIdx, iter_time[reqIdx]); + + if (iter == -num_requests) { + reallydone = 1; + alldone.notify_all(); + } + })); + } + + printf("Inference started. Running %d iterations...\n", niter - 2 * 2 * num_requests); + fflush(stdout); + for (int r = 0; r < num_requests; ++r) { + iter_start[r] = Time::now(); + IECALL(request[r]->StartAsync(&resp)); + } + + { + std::unique_lock lock(done_mutex); + alldone.wait(lock, [&](){return reallydone;}); + } + + // check 10 time intervals to get min/max fps values + const int fps_checks = 10; + // exclude (2 * num_requests) first and last iterations + int num_exclude = 2 * num_requests; + time_point cstart = iter_end[num_exclude - 1]; + time_point cend = iter_end[niter - num_exclude - 1]; + + double totalTime = (std::chrono::duration_cast(cend - cstart)).count(); + std::cout << std::endl << "Total time: " << (totalTime) << " ms" << std::endl; + + std::cout << "Average fps on " << (niter - 2 * num_exclude) << " iterations" + << (nBatch == 1 ? ": " : (" of " + std::to_string(nBatch) + " frames: ")) + << static_cast(niter - 2 * num_exclude) * 1000.0 * nBatch / (totalTime) << " fps" << std::endl; + + double check_time = totalTime / fps_checks; + + double min_fps = 100000; + double max_fps = -100000; + int citer = num_exclude; + for (int f = 0; f < fps_checks; ++f) { + int fiter = 0; + auto fend = (f < fps_checks - 1) ? cstart + std::chrono::microseconds((unsigned int)(check_time * 1000)) : cend; + while ((citer + fiter < niter - num_exclude) && iter_end[citer + fiter] <= fend) { + fiter++; + } + + double ffps = 1000 * fiter * nBatch / (check_time); + min_fps = std::min(min_fps, ffps); + max_fps = std::max(max_fps, ffps); + citer += fiter; + cstart = fend; + } + + std::cout << "Min fps: " << min_fps << std::endl; + std::cout << "Max fps: " << max_fps << std::endl; + + if (profile) { + printPerformanceCounts(perfMap); + } + + return 0; +} + +int main(int argc, char *argv[]) { + niter = MIN_ITER; + int num_networks = 1; + int nBatch = 1; + std::string file_config_cl; + + m_exename = argv[0]; + + if (argc < 3) { + print_usage(); + return 0; + } + + auto parse = [](const std::string& src) { + try { + return std::stol(src, nullptr, 0); + } catch (const std::invalid_argument& exception) { + std::cout << "Cannot perform conversion for " << src << ": " << exception.what() << std::endl; + print_usage(); + std::abort(); + } catch (const std::out_of_range& exception) { + std::cout << src << " is out of range: " << exception.what() << std::endl; + print_usage(); + std::abort(); + } catch (...) { + std::cout << "Unexpected exception" << std::endl; + print_usage(); + std::abort(); + } + }; + + if (argc > 3) { + niter = static_cast(parse(argv[3])); + } + + if (argc > 4) { + nBatch = static_cast(parse(argv[4])); + } + + if (argc > 5) { + num_networks = static_cast(parse(argv[5])); + } + + if (argc > 6) { + file_config_cl = std::string(argv[6]); + } + + if (niter < MIN_ITER) { + print_usage(); + return 0; + } + + if (num_networks < 1 || num_networks > 16) { + print_usage(); + return 0; + } + + if (nBatch < 1) { + print_usage(); + return 0; + } + + try { + std::string modelFileName(argv[1]); + std::string inputsDir(argv[2]); + return process(modelFileName, inputsDir, file_config_cl, nBatch, num_networks); + } + catch (const std::exception& ex) { + std::cout << ex.what(); + } + + return -1; +} + +inline float asfloat(uint32_t v) { + return *reinterpret_cast(&v); +} + +#define EXP_MASK_F32 0x7F800000U +#define EXP_MASK_F16 0x7C00U + +static short f32tof16(float x) { + static float min16 = asfloat((127 - 14) << 23); + + static float max16 = asfloat(((127 + 15) << 23) | 0x007FE000); + static uint32_t max16f16 = ((15 + 15) << 10) | 0x3FF; + + union { + float f; + uint32_t u; + } v{}; + v.f = x; + + uint32_t s = (v.u >> 16) & 0x8000; + + v.u &= 0x7FFFFFFF; + + if ((v.u & EXP_MASK_F32) == EXP_MASK_F32) { + if (v.u & 0x007FFFFF) { + return s | (v.u >> (23 - 10)) | 0x0200; + } else { + return s | (v.u >> (23 - 10)); + } + } + + float halfULP = asfloat(v.u & EXP_MASK_F32) * asfloat((127 - 11) << 23); + v.f += halfULP; + + if (v.f < min16 * 0.5F) { + return s; + } + + if (v.f < min16) { + return s | (1 << 10); + } + + if (v.f >= max16) { + return max16f16 | s; + } + + v.u -= ((127 - 15) << 23); + + v.u >>= (23 - 10); + + return v.u | s; +} + +static float f16tof32(short x) { + // this is storage for output result + uint32_t u = x; + + // get sign in 32bit format + uint32_t s = ((u & 0x8000) << 16); + + // check for NAN and INF + if ((u & EXP_MASK_F16) == EXP_MASK_F16) { + // keep mantissa only + u &= 0x03FF; + + // check if it is NAN and raise 10 bit to be align with intrin + if (u) { + u |= 0x0200; + } + + u <<= (23 - 10); + u |= EXP_MASK_F32; + u |= s; + } else if ((x & EXP_MASK_F16) == 0) { // check for zero and denormals. both are converted to zero + u = s; + } else { + // abs + u = (u & 0x7FFF); + + // shift mantissa and exp from f16 to f32 position + u <<= (23 - 10); + + // new bias for exp (f16 bias is 15 and f32 bias is 127) + u += ((127 - 15) << 23); + + // add sign + u |= s; + } + + // finaly represent result as float and return + return *reinterpret_cast(&u); +} + +static bool loadImage(const std::string &imageFilename, InferenceEngine::Blob::Ptr &blob) { + InferenceEngine::TensorDesc tensDesc = blob->getTensorDesc(); + if (tensDesc.getPrecision() != InferenceEngine::Precision::FP16) { + std::cout << "loadImage error: Input must have FP16 precision" << std::endl; + return false; + } + + if (tensDesc.getLayout() != InferenceEngine::NHWC && tensDesc.getLayout() != InferenceEngine::NCHW) { + std::cout << "loadImage error: Input must have NCHW or NHWC layout" << std::endl; + return false; + } + + BitMap reader(imageFilename); + + const auto dims = tensDesc.getDims(); + auto numBlobChannels = dims[1]; + size_t batch = dims[0]; + size_t w = dims[3]; + size_t h = dims[2]; + size_t img_w = reader.width(); + size_t img_h = reader.height(); + + size_t numImageChannels = reader.size() / (reader.width() * reader.height()); + if (numBlobChannels != numImageChannels && numBlobChannels != 1) { + std::cout << "loadImage error: Input channels mismatch: image channels " << numImageChannels << ", " + << "network channels " << numBlobChannels << ", expecting count of image channels are equal " + << "to count if network channels or count of network channels are equal to 1" << std::endl; + return false; + } + + int16_t *blobDataPtr = std::dynamic_pointer_cast>(blob)->data(); + auto nPixels = w * h; + unsigned char *RGB8 = reader.getData().get(); + float xscale = 1.0f * img_w / w; + float yscale = 1.0f * img_h / h; + + for (int n = 0; n != batch; n++) { + for (int i = 0; i < h; ++i) { + int y = static_cast(std::floor((i + 0.5f) * yscale)); + for (int j = 0; j < w; ++j) { + int x = static_cast(std::floor((j + 0.5f) * xscale)); + for (int k = 0; k < numBlobChannels; k++) { + if (tensDesc.getLayout() == InferenceEngine::NHWC) { + blobDataPtr[n * h * w * numBlobChannels + (i * w + j) * numBlobChannels + k] = + f32tof16(1.0 * RGB8[(y * img_w + x) * numImageChannels + k]); + } else { + blobDataPtr[n * h * w * numBlobChannels + (i * w + j) + k * nPixels] = + f32tof16(1.0 * RGB8[(y * img_w + x) * numImageChannels + k]); + } + } + } + } + } + + return true; +} + +bool loadBinaryTensor(const std::string &binaryFilename, InferenceEngine::Blob::Ptr &blob) { + InferenceEngine::TensorDesc tensDesc = blob->getTensorDesc(); + if (tensDesc.getPrecision() != InferenceEngine::Precision::FP16) { + std::cout << "loadBinaryTensor error: Input must have FP16 precision" << std::endl; + return false; + } + + std::ifstream binaryFile(binaryFilename, std::ios_base::binary | std::ios_base::ate); + + if (!binaryFile) { + std::cout << "loadBinaryTensor error: While opening a file an error is encountered" << std::endl; + return false; + } + + int fileSize = binaryFile.tellg(); + binaryFile.seekg(0, std::ios_base::beg); + size_t count = blob->size(); + if (fileSize != count * sizeof(float)) { + std::cout << "loadBinaryTensor error: File contains insufficient items" << std::endl; + return false; + } + + if (binaryFile.good()) { + int16_t *blobDataPtr = std::dynamic_pointer_cast>(blob)->data(); + for (size_t i = 0; i < count; i++) { + float tmp = 0.f; + binaryFile.read(reinterpret_cast(&tmp), sizeof(float)); + blobDataPtr[i] = f32tof16(tmp); + } + } else { + std::cout << "loadBinaryTensor error: While reading a file an error is encountered" << std::endl; + return false; + } + return true; +} diff --git a/inference-engine/tools/vpu/vpu_profile/README.md b/inference-engine/tools/vpu/vpu_profile/README.md index a7d818f7826220..f5e57fb40850e9 100644 --- a/inference-engine/tools/vpu/vpu_profile/README.md +++ b/inference-engine/tools/vpu/vpu_profile/README.md @@ -25,7 +25,7 @@ vpu_profile [OPTIONS] -inputs_dir Path to folder with images, only bitmap(.bmp) supported. Default: ".". -config Path to the configuration file. Default value: "config". -iterations Specifies number of iterations. Default value: 16. - -plugin Specifies plugin. Supported values: myriad. + -plugin Specifies plugin. Supported values: myriad, hddl. Default value: "myriad". -report Specifies report type. Supported values: per_layer, per_stage. Overrides value in configuration file if provided. Default value: "per_layer" @@ -40,6 +40,15 @@ $./vpu_profile -model /model_name.xml ``` > **NOTE**: Models should be first converted to the Inference Engine format (\*.xml + \*.bin) using the [Model Optimizer tool](https://software.intel.com/en-us/articles/OpenVINO-ModelOptimizer). +## Plugin Option + +You have to select between Myriad and HDDL plugin manually, by default vpu_profile will try to use myriad plugin +If you need to run HDDL, need to set it explicitly + +```sh +$./vpu_profile -model /model_name.xml -plugin hddl +``` + ## Iterations Option Sets amount of Infer requests to be executed, will affect overall inference time, performance counts will be reported for last iteration diff --git a/inference-engine/tools/vpu/vpu_profile/main.cpp b/inference-engine/tools/vpu/vpu_profile/main.cpp index 592a4e99f41fa7..8403611ae196d7 100644 --- a/inference-engine/tools/vpu/vpu_profile/main.cpp +++ b/inference-engine/tools/vpu/vpu_profile/main.cpp @@ -43,7 +43,7 @@ static constexpr char model_message[] = "Path to xml model."; static constexpr char inputs_dir_message[] = "Path to folder with images, only bitmap(.bmp) supported. Default: \".\"."; static constexpr char config_message[] = "Path to the configuration file. Default value: \"config\"."; static constexpr char iterations_message[] = "Specifies number of iterations. Default value: 16."; -static constexpr char plugin_message[] = "Specifies plugin. Supported values: myriad.\n" +static constexpr char plugin_message[] = "Specifies plugin. Supported values: myriad, hddl.\n" "\t \t \tDefault value: \"myriad\"."; static constexpr char report_message[] = "Specifies report type. Supported values: per_layer, per_stage.\n" "\t \t \tOverrides value in configuration file if provided. Default value: \"per_stage\""; diff --git a/model-optimizer/README.md b/model-optimizer/README.md index 8bfe2176c79f53..9a111d9d9d68f7 100644 --- a/model-optimizer/README.md +++ b/model-optimizer/README.md @@ -4,17 +4,15 @@ Project structure:
     |-- root
         |-- extensions
-            |-- front/ - graph transformations during front phase
-            |-- middle/ - graph transformations during middle phase (after partial inference)
-            |-- end/  - graph transformations during back phase (before IR generation) 
-            |-- ops/ - Model Optimizer operation classes
+            |-- front/caffe
+                |-- CustomLayersMapping.xml.example - example of file for registering custom Caffe layers in 2017R3 public
+                manner
         |-- mo
             |-- back - Back-End logic: contains IR emitting logic
-            |-- front - Front-End logic: contains matching between Framework-specific layers and IR specific, 
-                        calculation of output shapes for each registered layer
+            |-- front - Front-End logic: contains matching between Framework-specific layers and IR specific, calculation
+            of output shapes for each registered layer
             |-- graph - Graph utilities to work with internal IR representation
             |-- middle - Graph transformations - optimizations of the model
-            |-- ops - Model Optimizer operation classes
             |-- pipeline - Sequence of steps required to create IR for each framework
             |-- utils - Utility functions
         |-- tf_call_ie_layer - Sources for TensorFlow fallback in Inference Engine during model inference
@@ -22,18 +20,25 @@ Project structure:
         |-- mo_caffe.py - Entry point particularly for Caffe
         |-- mo_mxnet.py - Entry point particularly for MXNet
         |-- mo_tf.py - Entry point particularly for TensorFlow
-
+        |-- ModelOptimizer - Entry point particularly for Caffe that contains same CLI as 2017R3 publicly released
+        Model Optimizer
 
## Prerequisites Model Optimizer requires: -1. Python 3.4 or newer +1. Python 3 or newer + +2. [Optional] Please read about use cases that require Caffe available on the machine (:doc:`caffe_dependency`). + Please follow the steps described (:doc:`caffe_build`). ## Installation instructions -1. Go to the Model Optimizer folder +1. Go to the Model Optimizer folder: +
+    cd PATH_TO_INSTALL_DIR/deployment_tools/model_optimizer/model_optimizer_tensorflow
+
2. Create virtual environment and activate it. This option is strongly recommended as it creates a Python sandbox and dependencies for Model Optimizer do not influence global Python configuration, installed libraries etc. At the same @@ -41,9 +46,13 @@ Model Optimizer requires: step only if you do want to install all Model Optimizer dependencies globally: * Create environment: -
virtualenv -p /usr/bin/python3.6 .env3 --system-site-packages
+
+          virtualenv -p /usr/bin/python3.6 .env3 --system-site-packages
+        
* Activate it: -
. .env3/bin/activate
+
+        . .env3/bin/activate
+      
3. Install dependencies. If you want to convert models only from particular framework, you should use one of available requirements_*.txt files corresponding to the framework of choice. For example, for Caffe use requirements_caffe.txt and so on. When you decide to switch later to other frameworks, please install dependencies diff --git a/model-optimizer/extensions/back/CutMemory.py b/model-optimizer/extensions/back/CutMemory.py new file mode 100644 index 00000000000000..65488fc5c253fa --- /dev/null +++ b/model-optimizer/extensions/back/CutMemory.py @@ -0,0 +1,65 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import numpy as np + +from extensions.back.ParameterToPlaceholder import ParameterToInput +from extensions.ops.parameter import Parameter +from mo.back.replacement import BackReplacementPattern +from mo.graph.graph import Graph +from mo.ops.crop import Crop +from mo.utils.logger import log + + +class CutMemory(BackReplacementPattern): + """ + Cut Memory layers and have inputs/outputs in graph instead of them + """ + enabled = False + + def run_before(self): + return [ParameterToInput] + + @staticmethod + def pattern(): + return dict( + nodes=[ + ('op', dict(kind='op', op='Memory'))], + edges=[] + ) + + @staticmethod + def replace_pattern(graph: Graph, match: dict): + node = match['op'] + node_id = node['id'] + + if node.in_port(0).disconnected(): + i = 0 + for dest in node.out_port(0).get_destinations(): + new_in = Parameter(graph, {'name': "Parameter_"+str(i)+"_for_"+node_id, + 'shape': dest.data.get_shape()}).create_node() + i += 1 + dest.disconnect() + new_in.out_port(0).connect(dest) + log.error("Add input/output mapped {} -> {} ".format(new_in.name, "Result_for_"+node_id), + extra={'is_warning': True}) + else: + out_node_port = node.out_port(0).get_destination() + in_node_port = node.in_port(0).get_source() + node.in_port(0).disconnect() + node.out_port(0).disconnect() + crop = Crop(graph, {'name': 'Result_for_'+node_id, 'dim': np.array([1]), 'offset': np.array([0]), 'axis': np.array([0])}).create_node() + in_node_port.connect(crop.in_port(0)) + crop.out_port(0).connect(out_node_port) diff --git a/model-optimizer/extensions/back/CutMemory_test.py b/model-optimizer/extensions/back/CutMemory_test.py new file mode 100644 index 00000000000000..911acaa5d31c86 --- /dev/null +++ b/model-optimizer/extensions/back/CutMemory_test.py @@ -0,0 +1,71 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import unittest +from extensions.back.CutMemory import CutMemory +from mo.utils.unittest.graph import compare_graphs, build_graph + + +class CutMemoryTest(unittest.TestCase): + def test_remove_memory(self): + """Memory should be replaced by input and output""" + graph = build_graph( + nodes_attrs={ + 'input': {'kind': 'op'}, + 'data_in': {'kind': 'data', 'shape': None, 'value': None}, + 'memory_in': {'kind': 'op', 'op': 'Memory', 'index': 1, 'id': 'memory_', 'in_ports_count': 1}, + 'data_mem': {'kind': 'data', 'shape': None, 'value': None}, + 'concat': {'kind': 'op', 'op': 'Concat', 'axis': 0}, + 'concat_data': {'kind': 'data', 'shape': None, 'value': None}, + 'some_op': {'kind': 'op'}, + 'some_op_data': {'kind': 'data', 'shape': None, 'value': None}, + 'memory_out': {'kind': 'op', 'op': 'Memory', 'index': 0, 'id': 'memory_'}, + 'data_mem_out': {'kind': 'data', 'shape': None, 'value': None}, + 'mem_out_result': {'kind': 'op', 'op': 'Result'} + }, + edges=[ + ('input', 'data_in'), ('memory_in', 'data_mem'), + ('data_in', 'concat', {'in': 0}), ('data_mem', 'concat', {'in': 1}), + ('concat', 'concat_data'), ('concat_data', 'some_op'), + ('some_op', 'some_op_data'), ('some_op_data', 'memory_out'), + ('memory_out', 'data_mem_out'), ('data_mem_out', 'mem_out_result') + ] + ) + graph_ref = build_graph( + nodes_attrs={ + 'input': {'kind': 'op'}, + 'data_in': {'kind': 'data', 'shape': None, 'value': None}, + 'new_input': {'kind': 'op', 'op': 'Parameter'}, + 'new_in_data': {'kind': 'data', 'shape': None, 'value': None}, + 'concat': {'kind': 'op', 'op': 'Concat', 'axis': 0}, + 'concat_data': {'kind': 'data', 'shape': None, 'value': None}, + 'some_op': {'kind': 'op'}, + 'some_op_data': {'kind': 'data', 'shape': None, 'value': None}, + 'crop': {'kind': 'op', 'op': 'Crop', 'axis': 0}, + 'crop_data': {'kind': 'data', 'shape': None, 'value': None}, + 'mem_out_result': {'kind': 'op', 'op': 'Result'}, + }, + edges=[ + ('input', 'data_in'), ('new_input', 'new_in_data'), + ('data_in', 'concat', {'in': 0}), ('new_in_data', 'concat', {'in': 1}), + ('concat', 'concat_data'), ('concat_data', 'some_op'), + ('some_op', 'some_op_data'), ('some_op_data', 'crop'), + ('crop', 'crop_data'), ('crop_data', 'mem_out_result') + ], + ) + CutMemory().find_and_replace_pattern(graph) + + (flag, resp) = compare_graphs(graph, graph_ref, last_node='mem_out_result', check_op_attrs=True) + self.assertTrue(flag, resp) diff --git a/model-optimizer/extensions/back/FuseReshapesSequence.py b/model-optimizer/extensions/back/FuseReshapesSequence.py index 2f2459f7df7d7a..27ca9485f6210f 100644 --- a/model-optimizer/extensions/back/FuseReshapesSequence.py +++ b/model-optimizer/extensions/back/FuseReshapesSequence.py @@ -46,6 +46,11 @@ def find_and_replace_pattern(self, graph: Graph): next_op = get_next_operation(node)[0] log.debug('second node: id={}, type={}'.format(next_op.soft_get('id'), next_op.soft_get('type'))) if next_op.has_valid('type') and next_op.type == 'Reshape': + dim_value = next_op.in_port(1).data.get_value() + if dim_value is None or 0 in dim_value or -1 in dim_value: + # we do not fuse reshape sequences with special symbols: 0, -1 + continue + # Detected Reshape1 --> data --> Reshape2 pattern without side edges. Remove Reshape1 log.debug('Second phase for Reshape: {}'.format(node.soft_get('name'))) remove_op_node_with_data_node(graph, node) diff --git a/model-optimizer/extensions/back/Gather0D.py b/model-optimizer/extensions/back/Gather0D.py new file mode 100644 index 00000000000000..e896bcdb03b8e9 --- /dev/null +++ b/model-optimizer/extensions/back/Gather0D.py @@ -0,0 +1,61 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import logging as log + +import numpy as np + +from mo.back.replacement import BackReplacementPattern +from mo.front.common.partial_infer.utils import int64_array +from mo.graph.graph import Graph +from mo.ops.const import Const +from mo.ops.squeeze import Squeeze + + +class Gather0D(BackReplacementPattern): + """ + This is a workaround until InferenceEngine starts support 0D. + The pass finds Gather with 0D constant input with indices to gather and converts it to 1D with 1 element and + then add Squeeze to restore initial number of dimension. + """ + + enabled = True + force_shape_inference = True + + def find_and_replace_pattern(self, graph: Graph): + for gather in graph.get_op_nodes(type='Gather'): + indices = gather.in_port(1).get_source().node + indices_value = gather.in_port(1).data.get_value() + if indices.op == 'Const' and indices_value is not None and indices_value.ndim == 0: + log.debug('The Gather node {} has constant 0D input with indices'.format(gather.id)) + + new_indices = Const(graph, {'value': np.array([indices_value.item()])}).create_node() + + # the input shape is changed so need to disconnect port first + gather.in_port(1).disconnect() + gather.in_port(1).connect(new_indices.out_port(0)) + + # the output of Gather is changed so need to run shape inference for it and override the existing shape + gather['override_output_shape'] = True + gather['need_shape_inference'] = True + + # insert Squeeze to remove the dimension 'axis' which become equal to 1 after change of the Gather + # indices constant + squeeze = Squeeze(graph, {'name': gather.id + '/Squeeze'}).create_node() + squeeze_axis = Const(graph, {'name': squeeze.id + '/axis', + 'value': int64_array([gather.axis])}).create_node() + + gather.out_port(0).get_connection().insert_node(squeeze) + squeeze.in_port(1).connect(squeeze_axis.out_port(0)) diff --git a/model-optimizer/extensions/back/ReduceToPooling.py b/model-optimizer/extensions/back/ReduceToPooling.py index 9646ade8da18c6..2ed90225bcf6d4 100644 --- a/model-optimizer/extensions/back/ReduceToPooling.py +++ b/model-optimizer/extensions/back/ReduceToPooling.py @@ -78,6 +78,8 @@ def replace_pattern(self, graph: Graph, match: dict): axis_data_value = node.in_port(1).data.get_value() axis = int64_array([axis_data_value.item()]) if axis_data_value.size == 1 else axis_data_value axis = [get_canonical_axis_index(input_shape, a) for a in axis] + assert 0 not in axis, 'The node "{}" is a Reduce operation for batch dimension which is not supported'.format( + node.name) # Check that values in axis list are consecutive for idx in range(1, len(axis)): @@ -94,10 +96,11 @@ def replace_pattern(self, graph: Graph, match: dict): # 2. Create reshape with appropriate shape if len(begin_dims) > 2: - begin_dims = np.array([np.prod(begin_dims[0:-1]), begin_dims[-1]], dtype=np.int64) + begin_dims = int64_array([begin_dims[0], np.prod(begin_dims[1:])]) else: # Expand begin_dims to 2 - begin_dims = np.array(np.append(begin_dims, [1] * (2 - len(begin_dims))), dtype=np.int64) + begin_dims = int64_array(np.append(begin_dims, [1] * (2 - len(begin_dims)))) + reshape_shape = np.array([*begin_dims, reduction_dim, end_dim], dtype=np.int64) pool_window = np.array([1, 1, reduction_dim, 1], dtype=np.int64) @@ -105,7 +108,8 @@ def replace_pattern(self, graph: Graph, match: dict): reshape_op = Reshape(graph, {'name': node.id + '/Reshape'}) reshape_dim_const_data = Const(graph, {'name': node.id + '/Reshape/Dim', 'value': reshape_shape}).create_node_with_data() - final_reshape_op = Reshape(graph, {'name': node.id + '/FinalReshape', 'dim': output_shape}) + + final_reshape_op = Reshape(graph, {'name': node.id + '/FinalReshape'}) final_reshape_dim_const_data = Const(graph, {'name': node.id + '/FinalReshape/Dim', 'value': output_shape}).create_node_with_data() pooling_op = Pooling(graph, @@ -127,6 +131,10 @@ def replace_pattern(self, graph: Graph, match: dict): ), final_reshape_dim_const_data], data_nodes=output_data) + # convert batch dimension to 0 to produce reshape-able IR over the batch dimension + reshape_dim_const_data.in_node(0).value[0] = 0 + final_reshape_dim_const_data.in_node(0).value[0] = 0 + # 4. If it is reduction with summation, we need to multiply by size of the reduction slice with Mul op if reduce_type == 'ReduceSum': output_data.in_node().insert_node_with_data_after( diff --git a/model-optimizer/extensions/back/ReduceToPooling_test.py b/model-optimizer/extensions/back/ReduceToPooling_test.py index 8861dab03c1313..9d7eec9dd33ced 100644 --- a/model-optimizer/extensions/back/ReduceToPooling_test.py +++ b/model-optimizer/extensions/back/ReduceToPooling_test.py @@ -98,14 +98,14 @@ def test1(self): ('reshape_2_data', 'concat'), ], {'placeholder_1_data': {'shape': int64_array([1, 64, 1])}, - 'reshape_1_const': {'value': int64_array([1, 1, 64, 1]), 'shape': int64_array([4])}, - 'reshape_1_const_data': {'value': int64_array([1, 1, 64, 1]), + 'reshape_1_const': {'value': int64_array([0, 1, 64, 1]), 'shape': int64_array([4])}, + 'reshape_1_const_data': {'value': int64_array([0, 1, 64, 1]), 'shape': int64_array([4])}, 'reshape_1_data': {'shape': int64_array([1, 1, 64, 1])}, 'pooling': {'window': int64_array([1, 1, 64, 1])}, 'pooling_data': {'shape': int64_array([1, 1, 1, 1])}, - 'reshape_2_const': {'value': int64_array([1, 1, 1]), 'shape': int64_array([3])}, - 'reshape_2_const_data': {'value': int64_array([1, 1, 1]), 'shape': int64_array([3])}, + 'reshape_2_const': {'value': int64_array([0, 1, 1]), 'shape': int64_array([3])}, + 'reshape_2_const_data': {'value': int64_array([0, 1, 1]), 'shape': int64_array([3])}, 'reshape_2_data': {'shape': int64_array([1, 1, 1])}, }, nodes_with_edges_only=True) @@ -155,14 +155,14 @@ def test2(self): ], {'placeholder_1': {'shape': int64_array([1, 3, 64, 64])}, 'placeholder_1_data': {'shape': int64_array([1, 3, 64, 64])}, - 'reshape_1_const': {'value': int64_array([1, 3, 64, 64]), 'shape': int64_array([4])}, - 'reshape_1_const_data': {'value': int64_array([1, 3, 64, 64]), + 'reshape_1_const': {'value': int64_array([0, 3, 64, 64]), 'shape': int64_array([4])}, + 'reshape_1_const_data': {'value': int64_array([0, 3, 64, 64]), 'shape': int64_array([4])}, 'reshape_1_data': {'shape': int64_array([1, 3, 64, 64])}, 'pooling': {'window': int64_array([1, 1, 64, 1])}, 'pooling_data': {'shape': int64_array([1, 3, 1, 64])}, - 'reshape_2_const': {'value': int64_array([1, 3, 1, 64]), 'shape': int64_array([4])}, - 'reshape_2_const_data': {'value': int64_array([1, 3, 1, 64]), + 'reshape_2_const': {'value': int64_array([0, 3, 1, 64]), 'shape': int64_array([4])}, + 'reshape_2_const_data': {'value': int64_array([0, 3, 1, 64]), 'shape': int64_array([4])}, 'reshape_2_data': {'shape': int64_array([1, 3, 1, 64])}, }, nodes_with_edges_only=True) @@ -213,15 +213,15 @@ def test3(self): ], {'placeholder_1': {'shape': int64_array([1, 3, 64, 64])}, 'placeholder_1_data': {'shape': int64_array([1, 3, 64, 64])}, - 'reshape_1_const': {'value': int64_array([1, 3, 64 * 64, 1]), + 'reshape_1_const': {'value': int64_array([0, 3, 64 * 64, 1]), 'shape': int64_array([4])}, - 'reshape_1_const_data': {'value': int64_array([1, 3, 64 * 64, 1]), + 'reshape_1_const_data': {'value': int64_array([0, 3, 64 * 64, 1]), 'shape': int64_array([4])}, 'reshape_1_data': {'shape': int64_array([1, 3, 64 * 64, 1])}, 'pooling': {'window': int64_array([1, 1, 64 * 64, 1])}, 'pooling_data': {'shape': int64_array([1, 3, 1, 1])}, - 'reshape_2_const': {'value': int64_array([1, 3, 1, 1]), 'shape': int64_array([4])}, - 'reshape_2_const_data': {'value': int64_array([1, 3, 1, 1]), + 'reshape_2_const': {'value': int64_array([0, 3, 1, 1]), 'shape': int64_array([4])}, + 'reshape_2_const_data': {'value': int64_array([0, 3, 1, 1]), 'shape': int64_array([4])}, 'reshape_2_data': {'shape': int64_array([1, 3, 1, 1])}, }, nodes_with_edges_only=True) @@ -272,15 +272,15 @@ def test4(self): ], {'placeholder_1': {'shape': int64_array([2, 3, 64, 64])}, 'placeholder_1_data': {'shape': int64_array([2, 3, 64, 64])}, - 'reshape_1_const': {'value': int64_array([2, 1, 3 * 64 * 64, 1]), + 'reshape_1_const': {'value': int64_array([0, 1, 3 * 64 * 64, 1]), 'shape': int64_array([4])}, - 'reshape_1_const_data': {'value': int64_array([2, 1, 3 * 64 * 64, 1]), + 'reshape_1_const_data': {'value': int64_array([0, 1, 3 * 64 * 64, 1]), 'shape': int64_array([4])}, 'reshape_1_data': {'shape': int64_array([2, 1, 3 * 64 * 64, 1])}, 'pooling': {'window': int64_array([1, 1, 3 * 64 * 64, 1])}, 'pooling_data': {'shape': int64_array([2, 1, 1, 1])}, - 'reshape_2_const': {'value': int64_array([2]), 'shape': int64_array([1])}, - 'reshape_2_const_data': {'value': int64_array([2]), 'shape': int64_array([1])}, + 'reshape_2_const': {'value': int64_array([0]), 'shape': int64_array([1])}, + 'reshape_2_const_data': {'value': int64_array([0]), 'shape': int64_array([1])}, 'reshape_2_data': {'shape': int64_array([2])}, }, nodes_with_edges_only=True) @@ -330,16 +330,16 @@ def test5(self): ], {'placeholder_1': {'shape': int64_array([1, 16, 64, 64, 64, 4])}, 'placeholder_1_data': {'shape': int64_array([1, 16, 64, 64, 64, 4])}, - 'reshape_1_const': {'value': int64_array([65536, 64, 4, 1]), + 'reshape_1_const': {'value': int64_array([0, 4194304, 4, 1]), 'shape': int64_array([4])}, - 'reshape_1_const_data': {'value': int64_array([65536, 64, 4, 1]), + 'reshape_1_const_data': {'value': int64_array([0, 4194304, 4, 1]), 'shape': int64_array([4])}, - 'reshape_1_data': {'shape': int64_array([65536, 64, 4, 1])}, + 'reshape_1_data': {'shape': int64_array([1, 4194304, 4, 1])}, 'pooling': {'window': int64_array([1, 1, 4, 1])}, - 'pooling_data': {'shape': int64_array([65536, 64, 1, 1])}, - 'reshape_2_const': {'value': int64_array([1, 16, 64, 64, 64]), + 'pooling_data': {'shape': int64_array([1, 4194304, 1, 1])}, + 'reshape_2_const': {'value': int64_array([0, 16, 64, 64, 64]), 'shape': int64_array([5])}, - 'reshape_2_const_data': {'value': int64_array([1, 16, 64, 64, 64]), + 'reshape_2_const_data': {'value': int64_array([0, 16, 64, 64, 64]), 'shape': int64_array([5])}, 'reshape_2_data': {'shape': int64_array([1, 16, 64, 64, 64])}, }, nodes_with_edges_only=True) @@ -392,14 +392,14 @@ def test6(self): ], {'placeholder_1': {'shape': int64_array([1, 64, 1])}, 'placeholder_1_data': {'shape': int64_array([1, 64, 1])}, - 'reshape_1_const': {'value': int64_array([1, 1, 64, 1]), 'shape': int64_array([4])}, - 'reshape_1_const_data': {'value': int64_array([1, 1, 64, 1]), + 'reshape_1_const': {'value': int64_array([0, 1, 64, 1]), 'shape': int64_array([4])}, + 'reshape_1_const_data': {'value': int64_array([0, 1, 64, 1]), 'shape': int64_array([4])}, 'reshape_1_data': {'shape': int64_array([1, 1, 64, 1])}, 'pooling': {'window': int64_array([1, 1, 64, 1])}, 'pooling_data': {'shape': int64_array([1, 1, 1, 1])}, - 'reshape_2_const': {'value': int64_array([1, 1, 1]), 'shape': int64_array([3])}, - 'reshape_2_const_data': {'value': int64_array([1, 1, 1]), 'shape': int64_array([3])}, + 'reshape_2_const': {'value': int64_array([0, 1, 1]), 'shape': int64_array([3])}, + 'reshape_2_const_data': {'value': int64_array([0, 1, 1]), 'shape': int64_array([3])}, 'reshape_2_data': {'shape': int64_array([1, 1, 1])}, 'power': {'scale': 64.0}, 'power_data': {'shape': int64_array([1, 1, 1])}, diff --git a/model-optimizer/extensions/back/ScalarConstNormalize.py b/model-optimizer/extensions/back/ScalarConstNormalize.py index e0a08569828e8b..add5033d4ea922 100644 --- a/model-optimizer/extensions/back/ScalarConstNormalize.py +++ b/model-optimizer/extensions/back/ScalarConstNormalize.py @@ -27,6 +27,9 @@ # Temporary nGraph workaround. TODO: REMOVE +from mo.ops.unsqueeze import Unsqueeze + + class ScalarNormalize(BackReplacementPattern): enabled = True graph_condition = [lambda graph: graph.graph['cmd_params'].generate_experimental_IR_V10] @@ -73,6 +76,7 @@ def find_and_replace_pattern(self, graph: Graph): 'Unsqueeze': [1], 'Squeeze': [1], 'Eltwise': [1], + 'Range': [0, 1, 2], } for node in graph.get_op_nodes(): if node.has_and_set('type') and node.type in rules: @@ -88,3 +92,33 @@ def find_and_replace_pattern(self, graph: Graph): src_node.out_port(0).connect(reshape.in_port(0)) reshape.infer(reshape) graph.strict_mode = True + + +class RangeInputNormalize(BackReplacementPattern): + enabled = True + graph_condition = [lambda graph: not graph.graph['cmd_params'].generate_experimental_IR_V10] + force_clean_up = True + + def run_after(self): + return [ScalarNormalizeForSpecificOps] + + def find_and_replace_pattern(self, graph: Graph): + graph.strict_mode = False + # key is the type of the operation. The value is list of ports to convert from 0D to 1D + rules = { + 'Range': [0, 1, 2], + } + for node in graph.get_op_nodes(): + if node.has_and_set('type') and node.type in rules: + for port in rules[node.type]: + if port in node.in_ports() and not node.in_port(port).disconnected(): + src_node = node.in_port(port).get_connection().get_source().node + shape = node.in_port(port).data.get_shape() + assert shape is not None + if shape is not None and shape.size == 0: + reshape = create_op_node_with_second_input(graph, Unsqueeze, int64_array([0]), + {'name': src_node.id + '/Dims'}) + src_node.out_port(0).get_connection().set_source(reshape.out_port(0)) + src_node.out_port(0).connect(reshape.in_port(0)) + reshape.infer(reshape) + graph.strict_mode = True \ No newline at end of file diff --git a/model-optimizer/extensions/front/mxnet/conv_ext.py b/model-optimizer/extensions/front/mxnet/conv_ext.py index 1792ff8d193baa..f871d1e4937fed 100644 --- a/model-optimizer/extensions/front/mxnet/conv_ext.py +++ b/model-optimizer/extensions/front/mxnet/conv_ext.py @@ -124,6 +124,10 @@ def extract(node): 'get_pad': DeconvFrontExtractor.get_pad, } + output_padding = attr.tuple("adj", int, None) + if target_shape is None and output_padding: + node_attrs["output_padding"] = np.array([0, 0, *[s for s in output_padding]], dtype=np.int64) + # update the attributes of the node Convolution.update_node_stat(node, node_attrs) return __class__.enabled diff --git a/model-optimizer/extensions/front/mxnet/conv_ext_test.py b/model-optimizer/extensions/front/mxnet/conv_ext_test.py index 2a75fcea7f84f5..35eb0fe9f27047 100644 --- a/model-optimizer/extensions/front/mxnet/conv_ext_test.py +++ b/model-optimizer/extensions/front/mxnet/conv_ext_test.py @@ -150,3 +150,70 @@ def test_deconv_ext_target_shape(self): np.testing.assert_equal(node[key], exp_res[key]) else: self.assertEqual(node[key], exp_res[key]) + + def test_deconv_ext_output_pad(self): + params = {'attrs': { + "kernel": "(4, 4)", + "no_bias": "True", + "num_filter": "21", + "num_group": "14", + "pad": "(4, 4)", + "stride": "(2, 2)", + "dilate": "(3, 3)", + "workspace": "1536", + "adj": "(1, 1)" + }} + node = PB({'symbol_dict': params}) + DeconvFrontExtractor.extract(node) + exp_res = { + 'op': 'Deconvolution', + 'pad': np.array([[0, 0], [0, 0], [4, 4], [4, 4]]), + 'pad_spatial_shape': np.array([[4, 4], [4, 4]]), + 'stride': np.array([1, 1, 2, 2]), + 'kernel_spatial': np.array([4, 4]), + 'dilation': np.array([1, 1, 3, 3]), + 'group': 14, + 'output': 21, + 'bias_addable': True, + 'bias_term': False, + 'output_padding': np.array([0, 0, 1, 1]), + } + for key in exp_res.keys(): + if key in ('pad', 'pad_spatial_shape', 'stride', 'kernel_spatial', 'dilation', 'output_spatial_shape', 'output_padding'): + np.testing.assert_equal(node[key], exp_res[key]) + else: + self.assertEqual(node[key], exp_res[key]) + + def test_deconv_ext_target_shape_with_output_pad(self): + params = {'attrs': { + "kernel": "(4, 4)", + "no_bias": "True", + "num_filter": "21", + "num_group": "14", + "pad": "(4, 4)", + "stride": "(2, 2)", + "dilate": "(3, 3)", + "workspace": "1536", + "target_shape": "(120, 120)", + "adj": "(1, 1)" + }} + node = PB({'symbol_dict': params}) + DeconvFrontExtractor.extract(node) + exp_res = { + 'op': 'Deconvolution', + 'pad': np.array([[0, 0], [0, 0], [4, 4], [4, 4]]), + 'pad_spatial_shape': np.array([[4, 4], [4, 4]]), + 'stride': np.array([1, 1, 2, 2]), + 'kernel_spatial': np.array([4, 4]), + 'dilation': np.array([1, 1, 3, 3]), + 'group': 14, + 'output': 21, + 'bias_addable': True, + 'bias_term': False, + 'output_spatial_shape': np.array([120, 120]), + } + for key in exp_res.keys(): + if key in ('pad', 'pad_spatial_shape', 'stride', 'kernel_spatial', 'dilation', 'output_spatial_shape'): + np.testing.assert_equal(node[key], exp_res[key]) + else: + self.assertEqual(node[key], exp_res[key]) diff --git a/model-optimizer/extensions/front/mxnet/elementwise_ext.py b/model-optimizer/extensions/front/mxnet/elementwise_ext.py index 3a68da7a96225a..03475c6efa93ff 100644 --- a/model-optimizer/extensions/front/mxnet/elementwise_ext.py +++ b/model-optimizer/extensions/front/mxnet/elementwise_ext.py @@ -15,7 +15,7 @@ """ import numpy as np -from extensions.ops.elementwise import Mul, Sub, Add, Maximum, Minimum +from extensions.ops.elementwise import Mul, Sub, Add, Maximum, Minimum, Div, Greater, GreaterEqual, Equal, Less, LessEqual, Pow, NotEqual, LogicalAnd, LogicalOr from mo.front.extractor import FrontExtractorOp from mo.front.mxnet.extractors.utils import get_mxnet_layer_attrs from mo.graph.graph import Node @@ -43,6 +43,26 @@ def extract(node): return __class__.enabled +class BroadcastDivFrontExtractor(FrontExtractorOp): + op = 'broadcast_div' + enabled = True + + @staticmethod + def extract(node): + Div.update_node_stat(node) + return __class__.enabled + + +class BroadcastSubFrontExtractor(FrontExtractorOp): + op = 'broadcast_sub' + enabled = True + + @staticmethod + def extract(node): + Sub.update_node_stat(node) + return __class__.enabled + + class ElementwiseAddExtractor(FrontExtractorOp): op = 'elemwise_add' enabled = True @@ -103,6 +123,126 @@ def extract(node): return __class__.enabled +class ElemwiseDivFrontExtractor(FrontExtractorOp): + op = 'elemwise_div' + enabled = True + + @staticmethod + def extract(node): + Div.update_node_stat(node, {}) + return __class__.enabled + + +class BroadcastMaximumFrontExtractor(FrontExtractorOp): + op = 'broadcast_maximum' + enabled = True + + @staticmethod + def extract(node): + Maximum.update_node_stat(node) + return __class__.enabled + + +class BroadcastMinimumFrontExtractor(FrontExtractorOp): + op = 'broadcast_minimum' + enabled = True + + @staticmethod + def extract(node): + Minimum.update_node_stat(node) + return __class__.enabled + + +class BroadcastGreaterFrontExtractor(FrontExtractorOp): + op = 'broadcast_greater' + enabled = True + + @staticmethod + def extract(node): + Greater.update_node_stat(node) + return __class__.enabled + + +class BroadcastGreaterEqualFrontExtractor(FrontExtractorOp): + op = 'broadcast_greater_equal' + enabled = True + + @staticmethod + def extract(node): + GreaterEqual.update_node_stat(node) + return __class__.enabled + + +class BroadcastEqualFrontExtractor(FrontExtractorOp): + op = 'broadcast_equal' + enabled = True + + @staticmethod + def extract(node): + Equal.update_node_stat(node) + return __class__.enabled + + +class BroadcastNotEqualFrontExtractor(FrontExtractorOp): + op = 'broadcast_not_equal' + enabled = True + + @staticmethod + def extract(node): + NotEqual.update_node_stat(node) + return __class__.enabled + + +class BroadcastLesserFrontExtractor(FrontExtractorOp): + op = 'broadcast_lesser' + enabled = True + + @staticmethod + def extract(node): + Less.update_node_stat(node) + return __class__.enabled + + +class BroadcastLesserEqualFrontExtractor(FrontExtractorOp): + op = 'broadcast_lesser_equal' + enabled = True + + @staticmethod + def extract(node): + LessEqual.update_node_stat(node) + return __class__.enabled + + +class BroadcastPowerFrontExtractor(FrontExtractorOp): + op = 'broadcast_power' + enabled = True + + @staticmethod + def extract(node): + Pow.update_node_stat(node) + return __class__.enabled + + +class BroadcastLogicalAndFrontExtractor(FrontExtractorOp): + op = 'broadcast_logical_and' + enabled = True + + @staticmethod + def extract(node): + LogicalAnd.update_node_stat(node) + return __class__.enabled + + +class BroadcastLogicalOrFrontExtractor(FrontExtractorOp): + op = 'broadcast_logical_or' + enabled = True + + @staticmethod + def extract(node): + LogicalOr.update_node_stat(node) + return __class__.enabled + + class MaximumFrontExtractor(FrontExtractorOp): op = '_maximum' enabled = True @@ -178,6 +318,83 @@ def extract(node): return __class__.enabled +class GreaterEqualScalarFrontExtractor(FrontExtractorOp): + op = '_greater_equal_scalar' + enabled = True + + @staticmethod + def extract(node): + attrs = get_mxnet_layer_attrs(node.symbol_dict) + node['scalar'] = np.array([attrs.float('scalar', 1.0)]) + return __class__.enabled + + +class EqualScalarFrontExtractor(FrontExtractorOp): + op = '_equal_scalar' + enabled = True + + @staticmethod + def extract(node): + attrs = get_mxnet_layer_attrs(node.symbol_dict) + node['scalar'] = np.array([attrs.float('scalar', 1.0)]) + return __class__.enabled + + +class NotEqualScalarFrontExtractor(FrontExtractorOp): + op = '_not_equal_scalar' + enabled = True + + @staticmethod + def extract(node): + attrs = get_mxnet_layer_attrs(node.symbol_dict) + node['scalar'] = np.array([attrs.float('scalar', 1.0)]) + return __class__.enabled + + +class LesserScalarFrontExtractor(FrontExtractorOp): + op = '_lesser_scalar' + enabled = True + + @staticmethod + def extract(node): + attrs = get_mxnet_layer_attrs(node.symbol_dict) + node['scalar'] = np.array([attrs.float('scalar', 1.0)]) + return __class__.enabled + + +class LesserEqualScalarFrontExtractor(FrontExtractorOp): + op = '_lesser_equal_scalar' + enabled = True + + @staticmethod + def extract(node): + attrs = get_mxnet_layer_attrs(node.symbol_dict) + node['scalar'] = np.array([attrs.float('scalar', 1.0)]) + return __class__.enabled + + +class MinimumScalarFrontExtractor(FrontExtractorOp): + op = '_minimum_scalar' + enabled = True + + @staticmethod + def extract(node): + attrs = get_mxnet_layer_attrs(node.symbol_dict) + node['scalar'] = attrs.float('scalar', 1.0) + return __class__.enabled + + +class MaximumScalarFrontExtractor(FrontExtractorOp): + op = '_maximum_scalar' + enabled = True + + @staticmethod + def extract(node): + attrs = get_mxnet_layer_attrs(node.symbol_dict) + node['scalar'] = attrs.float('scalar', 1.0) + return __class__.enabled + + class ZerosFrontExtractor(FrontExtractorOp): op = 'zeros_like' enabled = True diff --git a/model-optimizer/extensions/front/mxnet/eltwise_scalar_replacers.py b/model-optimizer/extensions/front/mxnet/eltwise_scalar_replacers.py index 983845ccca2e69..6f44354ae0c4b2 100644 --- a/model-optimizer/extensions/front/mxnet/eltwise_scalar_replacers.py +++ b/model-optimizer/extensions/front/mxnet/eltwise_scalar_replacers.py @@ -14,7 +14,7 @@ limitations under the License. """ -from extensions.ops.elementwise import Div, Greater, Sub, Mul, Add +from extensions.ops.elementwise import Div, Greater, GreaterEqual, Equal, NotEqual, Sub, Mul, Add, Less, LessEqual, Minimum, Maximum from mo.front.common.replacement import FrontReplacementOp from mo.front.mxnet.extractors.utils import scalar_ops_replacer from mo.graph.graph import Node, Graph @@ -42,6 +42,51 @@ def replace_op(self, graph: Graph, node: Node): return [greater_node.id] +class GreaterEqualScalarFrontReplacer(FrontReplacementOp): + op = '_greater_equal_scalar' + enabled = True + + def replace_op(self, graph: Graph, node: Node): + greater_node = scalar_ops_replacer(graph, node, GreaterEqual) + return [greater_node.id] + + +class EqualScalarFrontReplacer(FrontReplacementOp): + op = '_equal_scalar' + enabled = True + + def replace_op(self, graph: Graph, node: Node): + equal_scalar_node = scalar_ops_replacer(graph, node, Equal) + return [equal_scalar_node.id] + + +class NotEqualScalarFrontReplacer(FrontReplacementOp): + op = '_not_equal_scalar' + enabled = True + + def replace_op(self, graph: Graph, node: Node): + not_equal_scalar_node = scalar_ops_replacer(graph, node, NotEqual) + return [not_equal_scalar_node.id] + + +class LesserScalarFrontReplacer(FrontReplacementOp): + op = '_lesser_scalar' + enabled = True + + def replace_op(self, graph: Graph, node: Node): + lesser_scalar_node = scalar_ops_replacer(graph, node, Less) + return [lesser_scalar_node.id] + + +class LesserEqualScalarFrontReplacer(FrontReplacementOp): + op = '_lesser_equal_scalar' + enabled = True + + def replace_op(self, graph: Graph, node: Node): + lesser_equal_scalar_node = scalar_ops_replacer(graph, node, LessEqual) + return [lesser_equal_scalar_node.id] + + class MinusScalarFrontReplacer(FrontReplacementOp): op = '_minus_scalar' enabled = True @@ -71,3 +116,21 @@ class PlusScalarFrontReplacer(FrontReplacementOp): def replace_op(self, graph: Graph, node: Node): add_node = scalar_ops_replacer(graph, node, Add) return [add_node.id] + + +class MinimumScalarFrontReplacer(FrontReplacementOp): + op = '_minimum_scalar' + enabled = True + + def replace_op(self, graph: Graph, node: Node): + minimum_scalar_node = scalar_ops_replacer(graph, node, Minimum) + return [minimum_scalar_node.id] + + +class MaximumScalarFrontReplacer(FrontReplacementOp): + op = '_maximum_scalar' + enabled = True + + def replace_op(self, graph: Graph, node: Node): + maximum_scalar_node = scalar_ops_replacer(graph, node, Maximum) + return [maximum_scalar_node.id] diff --git a/model-optimizer/extensions/front/mxnet/expand_dims_ext.py b/model-optimizer/extensions/front/mxnet/expand_dims_ext.py new file mode 100644 index 00000000000000..ae86c2dbb7082b --- /dev/null +++ b/model-optimizer/extensions/front/mxnet/expand_dims_ext.py @@ -0,0 +1,31 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from mo.front.extractor import FrontExtractorOp +from mo.front.mxnet.extractors.utils import get_mxnet_layer_attrs +from mo.ops.expand_dims import ExpandDims + + +class ExpandDimsExtractor(FrontExtractorOp): + op = 'expand_dims' + enabled = True + + @staticmethod + def extract(node): + attrs = get_mxnet_layer_attrs(node.symbol_dict) + expand_axis = attrs.int('axis', None) + ExpandDims.update_node_stat(node, {'expand_axis': expand_axis}) + return __class__.enabled diff --git a/model-optimizer/extensions/front/onnx/constant_of_shape_ext.py b/model-optimizer/extensions/front/onnx/constant_of_shape_ext.py new file mode 100644 index 00000000000000..62ced0ae4f8af7 --- /dev/null +++ b/model-optimizer/extensions/front/onnx/constant_of_shape_ext.py @@ -0,0 +1,33 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import numpy as np +from onnx import numpy_helper + +from mo.front.extractor import FrontExtractorOp +from mo.front.onnx.extractors.utils import onnx_attr +from mo.ops.constant_of_shape import ConstantOfShape + + +class ConstantOfShapeExtractor(FrontExtractorOp): + op = 'ConstantOfShape' + enabled = True + + @staticmethod + def extract(node): + fill_value = onnx_attr(node, 'value', 't', default=np.array([0.0]), dst_type=lambda x: numpy_helper.to_array(x)) + + ConstantOfShape.update_node_stat(node, {'fill_value': fill_value}) + return __class__.enabled diff --git a/model-optimizer/extensions/front/onnx/constant_of_shape_to_broadcast.py b/model-optimizer/extensions/front/onnx/constant_of_shape_to_broadcast.py new file mode 100644 index 00000000000000..81e607edda4901 --- /dev/null +++ b/model-optimizer/extensions/front/onnx/constant_of_shape_to_broadcast.py @@ -0,0 +1,41 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from mo.front.common.replacement import FrontReplacementPattern +from mo.graph.graph import Graph +from mo.ops.broadcast import Broadcast +from mo.ops.const import Const + + +class ConstantOfShapeToBroadcast(FrontReplacementPattern): + """ + Converts the 'ConstantOfShape' layer to 'Broadcast'. + + The 'ConstantOfShape' has one 1D input defining the output constant shape. The value to be filled is defined by the + 'value' attribute. The transformation creates constant node with value equal to 'value' attribute and connects it to + the first input of a newly created 'Broadcast' node which defines value to broadcast. Then the input of the + 'ConstantOfShape' is connected to the second input of the 'Broadcast'. + """ + enabled = True + + def find_and_replace_pattern(self, graph: Graph): + for const_of_shape_node in graph.get_op_nodes(op='ConstantOfShape'): + broadcast_node = Broadcast(graph, {'name': const_of_shape_node.name + '/Broadcast'}).create_node() + const_of_shape_node.in_port(0).get_connection().set_destination(broadcast_node.in_port(1)) + broadcast_node.in_port(0).connect(Const(graph, {'name': broadcast_node.name + '/FillValue', + 'value': const_of_shape_node.fill_value} + ).create_node().out_port(0)) + const_of_shape_node.out_port(0).get_connection().set_source(broadcast_node.out_port(0)) diff --git a/model-optimizer/extensions/front/onnx/elementwise_ext.py b/model-optimizer/extensions/front/onnx/elementwise_ext.py index d33e4a575f4482..09fad5a7085638 100644 --- a/model-optimizer/extensions/front/onnx/elementwise_ext.py +++ b/model-optimizer/extensions/front/onnx/elementwise_ext.py @@ -15,7 +15,7 @@ """ import numpy as np -from extensions.ops.elementwise import Add, Mul, Pow +from extensions.ops.elementwise import Add, Mul, Pow, Less, Equal, Greater, LogicalAnd, LogicalOr from mo.front.extractor import FrontExtractorOp from mo.front.onnx.extractors.utils import onnx_attr from mo.graph.graph import Node @@ -76,6 +76,16 @@ def extract(node: Node): return __class__.enabled +class SqrtExtractor(FrontExtractorOp): + op = 'Sqrt' + enabled = True + + @staticmethod + def extract(node): + Power.update_node_stat(node, {'power': 0.5}) + return __class__.enabled + + class ScaleFrontExtractor(FrontExtractorOp): op = 'Scale' enabled = True @@ -95,3 +105,53 @@ class MaxExtractor(FrontExtractorOp): def extract(node: Node): EltwiseNMax.update_node_stat(node) return __class__.enabled + + +class EqualExtractor(FrontExtractorOp): + op = 'Equal' + enabled = True + + @staticmethod + def extract(node): + Equal.update_node_stat(node) + return __class__.enabled + + +class LessExtractor(FrontExtractorOp): + op = 'Less' + enabled = True + + @staticmethod + def extract(node): + Less.update_node_stat(node) + return __class__.enabled + + +class GreaterExtractor(FrontExtractorOp): + op = 'Greater' + enabled = True + + @staticmethod + def extract(node): + Greater.update_node_stat(node) + return __class__.enabled + + +class AndExtractor(FrontExtractorOp): + op = 'And' + enabled = True + + @staticmethod + def extract(node): + LogicalAnd.update_node_stat(node) + return __class__.enabled + + +class OrExtractor(FrontExtractorOp): + op = 'Or' + enabled = True + + @staticmethod + def extract(node): + LogicalOr.update_node_stat(node) + return __class__.enabled diff --git a/model-optimizer/extensions/front/onnx/expand_ext.py b/model-optimizer/extensions/front/onnx/expand_ext.py new file mode 100644 index 00000000000000..e0db7aa9b423d4 --- /dev/null +++ b/model-optimizer/extensions/front/onnx/expand_ext.py @@ -0,0 +1,28 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from mo.front.extractor import FrontExtractorOp +from mo.ops.broadcast import Broadcast + + +class ExpandExtractor(FrontExtractorOp): + op = 'Expand' + enabled = True + + @staticmethod + def extract(node): + Broadcast.update_node_stat(node) + return __class__.enabled diff --git a/model-optimizer/extensions/front/onnx/floor_ext.py b/model-optimizer/extensions/front/onnx/floor_ext.py new file mode 100644 index 00000000000000..5ce07defa0458d --- /dev/null +++ b/model-optimizer/extensions/front/onnx/floor_ext.py @@ -0,0 +1,28 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from extensions.ops.activation_ops import Floor +from mo.front.extractor import FrontExtractorOp + + +class FloorExtractor(FrontExtractorOp): + op = 'Floor' + enabled = True + + @staticmethod + def extract(node): + Floor.update_node_stat(node) + return __class__.enabled diff --git a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/__init__.py b/model-optimizer/extensions/front/onnx/not_ext.py similarity index 63% rename from inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/__init__.py rename to model-optimizer/extensions/front/onnx/not_ext.py index 3d35f203c67aa3..21744b4a52405b 100644 --- a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/__init__.py +++ b/model-optimizer/extensions/front/onnx/not_ext.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2018-2019 Intel Corporation + Copyright (c) 2019 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,4 +14,15 @@ limitations under the License. """ -from .benchmark import main +from extensions.ops.activation_ops import Not +from mo.front.extractor import FrontExtractorOp + + +class NotExtractor(FrontExtractorOp): + op = 'Not' + enabled = True + + @staticmethod + def extract(node): + Not.update_node_stat(node) + return __class__.enabled diff --git a/model-optimizer/extensions/front/onnx/reduce_min_ext.py b/model-optimizer/extensions/front/onnx/reduce_min_ext.py new file mode 100644 index 00000000000000..bf5bbe9ec17415 --- /dev/null +++ b/model-optimizer/extensions/front/onnx/reduce_min_ext.py @@ -0,0 +1,33 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from extensions.ops.ReduceOps import ReduceMin +from mo.front.common.partial_infer.utils import int64_array +from mo.front.extractor import FrontExtractorOp +from mo.front.onnx.extractors.utils import onnx_attr +from mo.graph.graph import Node + + +class ReduceMinFrontExtractor(FrontExtractorOp): + op = 'ReduceMin' + enabled = True + + @staticmethod + def extract(node: Node): + axis = onnx_attr(node, 'axes', 'ints', default=None, dst_type=lambda x: int64_array(x)) + keep_dims = onnx_attr(node, 'keepdims', 'i', default=True) + ReduceMin.update_node_stat(node, {'axis': axis, 'keep_dims': keep_dims}) + return __class__.enabled diff --git a/model-optimizer/extensions/front/onnx/slice_ext.py b/model-optimizer/extensions/front/onnx/slice_ext.py index 93affa04710052..8d67c1bc090036 100644 --- a/model-optimizer/extensions/front/onnx/slice_ext.py +++ b/model-optimizer/extensions/front/onnx/slice_ext.py @@ -15,10 +15,7 @@ """ import numpy as np -import logging as log -from mo.ops.op import Op -from mo.graph.graph import Node from mo.front.extractor import FrontExtractorOp from mo.front.onnx.extractors.utils import onnx_attr from mo.ops.slice import Slice @@ -38,6 +35,7 @@ def extract(node): 'axis': axis if len(axis) != 0 else None, 'start': start if len(start) != 0 else None, 'end': end if len(end) != 0 else None, + 'format': 'onnx' } # update the attributes of the node diff --git a/model-optimizer/extensions/front/onnx/top_k_ext.py b/model-optimizer/extensions/front/onnx/top_k_ext.py new file mode 100644 index 00000000000000..20688b787b078d --- /dev/null +++ b/model-optimizer/extensions/front/onnx/top_k_ext.py @@ -0,0 +1,30 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from extensions.ops.topk import TopK +from mo.front.extractor import FrontExtractorOp +from mo.front.onnx.extractors.utils import onnx_attr + + +class TopKExtractor(FrontExtractorOp): + op = 'TopK' + enabled = True + + @staticmethod + def extract(node): + axis = onnx_attr(node, 'axis', 'i', default=-1) + TopK.update_node_stat(node, {'axis': axis, 'sort': 'value'}) + return __class__.enabled diff --git a/model-optimizer/extensions/front/reduce_axis_normalizer.py b/model-optimizer/extensions/front/reduce_axis_normalizer.py index 739f9e05361f99..0ad8b57b1849f2 100644 --- a/model-optimizer/extensions/front/reduce_axis_normalizer.py +++ b/model-optimizer/extensions/front/reduce_axis_normalizer.py @@ -45,6 +45,8 @@ def replace_sub_graph(self, graph: Graph, match: [dict, SubgraphMatch]): node = match['reduce'] connected_in_ports = [port for port in node.in_ports().values() if not port.disconnected()] if len(connected_in_ports) == 1: + # if the 'axis' is None then we still add a second input to the layer with a 1D array with 1 element equal + # to None. The infer function handles this case because the input shape is known at this stage only if node.has('axis'): const = Const(graph, {'value': node.axis}).create_node() node.add_input_port(1, skip_if_exist=True) diff --git a/model-optimizer/extensions/front/tf/BatchToSpaceNDToUpsample.py b/model-optimizer/extensions/front/tf/BatchToSpaceNDToUpsample.py new file mode 100644 index 00000000000000..4a96c57f74a41f --- /dev/null +++ b/model-optimizer/extensions/front/tf/BatchToSpaceNDToUpsample.py @@ -0,0 +1,108 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import logging as log + +import numpy as np + +from extensions.ops.upsample import UpsampleOp +from mo.front.common.partial_infer.utils import int64_array +from mo.front.common.replacement import FrontReplacementSubgraph +from mo.graph.graph import Graph, Node + + +class BatchToSpaceNDToUpsample(FrontReplacementSubgraph): + """ + The transformation looks for pattern that performs NX upscale of the input image specified in the NHWC layout. + """ + enabled = True + + @staticmethod + def pattern(**kwargs): + return dict( + nodes=[ + ('transpose', dict(op='Transpose')), + ('expand_dims', dict(op='Unsqueeze')), + ('tile', dict(op='Tile')), + ('batch_to_space_nd', dict(op='BatchToSpaceND')), + ('strided_slice', dict(op='StridedSlice')), + ('transpose_back', dict(op='Transpose')), + ], + edges=[ + ('transpose', 'expand_dims', {'out': 0}), + ('expand_dims', 'tile', {'out': 0}), + ('tile', 'batch_to_space_nd', {'out': 0}), + ('batch_to_space_nd', 'strided_slice', {'out': 0}), + ('strided_slice', 'transpose_back', {'out': 0}) + ] + ) + + @staticmethod + def replace_sub_graph(graph: Graph, match: dict, **kwargs): + def _input_node_value(node: Node, port_ind: int): + input_node = node.in_port(port_ind).get_source().node + return input_node.value if input_node.op == 'Const' else None + + transpose = match['transpose'] + transpose_order = _input_node_value(transpose, 1) + if transpose_order is None or not np.all(np.equal(transpose_order, int64_array([1, 2, 3, 0]))): + log.debug('The transpose order {} for node {} is not equal to [1, 2, 3, 0]. Cannot apply ' + 'BatchToSpaceNDToUpsample transformation.'.format(transpose_order, transpose.name)) + return + + expand_axis = match['expand_dims'] + expand_axis_value = _input_node_value(expand_axis, 1) + if expand_axis_value != 0: + log.debug('The expand axis {} for node {} is not equal to 0. Cannot apply BatchToSpaceNDToUpsample ' + 'transformation.'.format(expand_axis_value, expand_axis.name)) + return + + tile = match['tile'] + tile_value = _input_node_value(tile, 1) + if tile_value is None: + log.debug('The tile value is not defined for node {}. Cannot apply BatchToSpaceNDToUpsample ' + 'transformation.'.format(tile.name)) + return + + if len(np.where(tile_value != 1)) != 1: + log.debug('The number of tiles not equal to 1 not equal to 1. Cannot apply BatchToSpaceNDToUpsample ' + 'transformation.') + return + tile_batch = tile_value[0] + + batch_to_space_nd = match['batch_to_space_nd'] + block_shape = _input_node_value(batch_to_space_nd, 1) + if block_shape is None or tile_batch != np.prod(block_shape): + log.debug('The block shape {} for node {} is not defined or inconsistent with the tile size. Cannot apply ' + 'BatchToSpaceNDToUpsample transformation.'.format(block_shape, batch_to_space_nd.name)) + return + if len(block_shape) != 2: + log.debug('The block shape len is not equal to 2 for node {}. Cannot apply BatchToSpaceNDToUpsample ' + 'transformation.'.format(batch_to_space_nd.name)) + return + + transpose_back = match['transpose_back'] + transpose_back_order = _input_node_value(transpose_back, 1) + if transpose_back_order is None or not np.all(np.equal(transpose_back_order, int64_array([3, 0, 1, 2]))): + log.debug('The transpose order {} for node {} is not equal to [3, 0, 1, 2]. Cannot apply ' + 'BatchToSpaceNDToUpsample transformation.'.format(transpose_back_order, transpose_back.name)) + return + + upsample_node = UpsampleOp(graph, {'height_scale': block_shape[0], 'width_scale': block_shape[1], + 'mode': 'nearest', + 'name': transpose.name + '/upsample'}).create_node() + + match['transpose'].in_port(0).get_connection().set_destination(upsample_node.in_port(0)) + match['transpose_back'].out_port(0).get_connection().set_source(upsample_node.out_port(0)) diff --git a/model-optimizer/extensions/front/tf/InterpolateTransposes.py b/model-optimizer/extensions/front/tf/InterpolateTransposes.py new file mode 100644 index 00000000000000..b764eac11d76e5 --- /dev/null +++ b/model-optimizer/extensions/front/tf/InterpolateTransposes.py @@ -0,0 +1,58 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import numpy as np + +from mo.front.tf.replacement import FrontReplacementFromConfigFileGeneral +from mo.graph.graph import Graph, Node +from mo.middle.pattern_match import find_pattern_matches, inverse_dict + + +class InterpolateTranspose(FrontReplacementFromConfigFileGeneral): + """ + Delete useless transposes around ResizeNearestNeighbor op. In TF this op is working in NHWC layout, + Resample in OpenVINO working in NCHW layout. If all graph has NCHW layout we should delete transposes around + Resample: (NCHW->NHWC) -> Resample -> (NHWC -> NCHW) to run this op in NCHW without changes of layout. + """ + enabled = True + replacement_id = 'InterpolateTranspose' + + pattern_nodes = [ + ('interpolate', {'kind': 'op', 'op': 'Interpolate'}), + ('transpose_1', {'kind': 'op', 'op': 'Transpose'}), + ('transpose_2', {'kind': 'op', 'op': 'Transpose'}), + ] + pattern_edges = [ + ('transpose_1', 'interpolate'), + ('interpolate', 'transpose_2'), + ] + + def transform_graph(self, graph: Graph, replacement_descriptions: dict): + matches = find_pattern_matches(graph, self.pattern_nodes, self.pattern_edges) + for match in list(matches): + inverse_match = inverse_dict(match) + interpolate = Node(graph, inverse_match['interpolate']) + transpose_1 = Node(graph, inverse_match['transpose_1']) + transpose_2 = Node(graph, inverse_match['transpose_2']) + + # Check for data layout and transposes orders + if graph.graph['layout'] != 'NCHW' or np.array_equal(transpose_1.in_port(1).data.get_value(), [0, 2, 3, 1]) or \ + np.array_equal(transpose_2.in_port(1).data.get_value(), [0, 3, 1, 2]): + return + + transpose_1.in_port(0).get_connection().set_destination(interpolate.in_port(0)) + transpose_2.out_port(0).get_connection().set_source(interpolate.out_port(0)) + + graph.remove_nodes_from([transpose_1.id, transpose_2.id]) diff --git a/model-optimizer/extensions/front/tf/ObjectDetectionAPI.py b/model-optimizer/extensions/front/tf/ObjectDetectionAPI.py index c225868c18d929..b8916e95dcf229 100644 --- a/model-optimizer/extensions/front/tf/ObjectDetectionAPI.py +++ b/model-optimizer/extensions/front/tf/ObjectDetectionAPI.py @@ -160,7 +160,7 @@ def _relax_reshape_nodes(graph: Graph, pipeline_config: PipelineConfig): for ssd_head_ind in range(num_layers): input_node = _find_ssd_head_node(graph, ssd_head_ind, 'box') assert (input_node is not None) - old_reshape_node = _skip_node_of_type(input_node.out_node(), ['Identity']) + old_reshape_node = _skip_node_of_type(input_node.out_node(), ['Identity', 'FakeQuantWithMinMaxVars']) assert old_reshape_node.op == 'Reshape' reshape_size_node = Const(graph, {'value': int64_array([0, -1, 1, 4])}).create_node([]) new_reshape_op = Reshape(graph, {'name': input_node.id + '/Reshape'}) @@ -170,7 +170,7 @@ def _relax_reshape_nodes(graph: Graph, pipeline_config: PipelineConfig): # fix hard-coded value for the number of items in tensor produced by the convolution to make topology reshapable input_node = _find_ssd_head_node(graph, ssd_head_ind, 'class') assert (input_node is not None) - old_reshape_node = _skip_node_of_type(input_node.out_node(), ['Identity']) + old_reshape_node = _skip_node_of_type(input_node.out_node(), ['Identity', 'FakeQuantWithMinMaxVars']) assert old_reshape_node.op == 'Reshape' reshape_size_node_2 = Const(graph, {'value': int64_array([0, -1, num_classes + 1])}).create_node([]) new_reshape_op_2 = Reshape(graph, {'name': input_node.id + '/Reshape'}) @@ -191,6 +191,9 @@ def _create_prior_boxes_node(graph: Graph, pipeline_config: PipelineConfig): max_scale = pipeline_config.get_param('ssd_anchor_generator_max_scale') num_layers = pipeline_config.get_param('ssd_anchor_generator_num_layers') aspect_ratios = pipeline_config.get_param('ssd_anchor_generator_aspect_ratios') + if not isinstance(aspect_ratios, list): + aspect_ratios = [aspect_ratios] + # prior boxes have to be generated using the image size used for training image_height = pipeline_config.get_param('resizer_image_height') image_width = pipeline_config.get_param('resizer_image_width') @@ -203,7 +206,11 @@ def _create_prior_boxes_node(graph: Graph, pipeline_config: PipelineConfig): if pipeline_config.get_param('ssd_anchor_generator_reduce_lowest') is not None: reduce_boxes_in_lowest_layer = pipeline_config.get_param('ssd_anchor_generator_reduce_lowest') - scales = [min_scale + (max_scale - min_scale) * i / (num_layers - 1) for i in range(num_layers)] + [1.0] + if pipeline_config.get_param('ssd_anchor_generator_scales') is not None: + scales = pipeline_config.get_param('ssd_anchor_generator_scales') + [1.0] + else: + scales = [min_scale + (max_scale - min_scale) * i / (num_layers - 1) for i in range(num_layers)] + [1.0] + prior_box_nodes = [] for ssd_head_ind in range(num_layers): ssd_head_node = _find_ssd_head_node(graph, ssd_head_ind, 'box') @@ -216,8 +223,10 @@ def _create_prior_boxes_node(graph: Graph, pipeline_config: PipelineConfig): widths = [scales[ssd_head_ind] * sqrt(ar) for ar in aspect_ratios] heights = [scales[ssd_head_ind] / sqrt(ar) for ar in aspect_ratios] - widths += [sqrt(scales[ssd_head_ind] * scales[ssd_head_ind + 1])] - heights += [sqrt(scales[ssd_head_ind] * scales[ssd_head_ind + 1])] + interpolated_scale_ar = pipeline_config.get_param('ssd_anchor_generator_interpolated_scale_aspect_ratio') + if interpolated_scale_ar > 0.0: + widths += [sqrt(scales[ssd_head_ind] * scales[ssd_head_ind + 1]) * interpolated_scale_ar] + heights += [sqrt(scales[ssd_head_ind] * scales[ssd_head_ind + 1]) / interpolated_scale_ar] widths = [w * image_width * base_anchor_size[1] for w in widths] heights = [h * image_height * base_anchor_size[0] for h in heights] @@ -944,8 +953,11 @@ def generate_sub_graph(self, graph: Graph, match: SubgraphMatch): {'name': 'do_reshape_conf'}, activation_conf_node) mark_as_correct_data_layout(reshape_conf_node) - if pipeline_config.get_param('ssd_anchor_generator_num_layers') is not None or \ - pipeline_config.get_param('multiscale_anchor_generator_min_level') is not None: + custom_attributes = match.custom_replacement_desc.custom_attributes + if ('disable_prior_boxes_layers_generator' not in custom_attributes or + not custom_attributes['disable_prior_boxes_layers_generator']) and \ + (pipeline_config.get_param('ssd_anchor_generator_num_layers') is not None or + pipeline_config.get_param('multiscale_anchor_generator_min_level') is not None): # change the Reshape operations with hardcoded number of output elements of the convolution nodes to be # reshapable _relax_reshape_nodes(graph, pipeline_config) diff --git a/model-optimizer/extensions/front/tf/elementwise_ext.py b/model-optimizer/extensions/front/tf/elementwise_ext.py index e9f42ce94cc8e4..04a618daa6b1a1 100644 --- a/model-optimizer/extensions/front/tf/elementwise_ext.py +++ b/model-optimizer/extensions/front/tf/elementwise_ext.py @@ -16,7 +16,7 @@ import logging as log from extensions.ops.elementwise import Add, Mul, Sub, Div, Maximum, Minimum, Pow, LogicalAnd, LogicalOr, Equal, \ - GreaterEqual, Greater, Less, LessEqual, NotEqual + GreaterEqual, Greater, Less, LessEqual, NotEqual, BiasAdd from mo.front.extractor import FrontExtractorOp from mo.front.tf.extractors.utils import tf_dtype_extractor from mo.ops.eltwise_n import EltwiseNAdd @@ -33,6 +33,16 @@ def extract(node): return __class__.enabled +class AddV2Extractor(FrontExtractorOp): + op = 'AddV2' + enabled = True + + @staticmethod + def extract(node): + Add.update_node_stat(node, {'data_type': tf_dtype_extractor(node.pb.attr["T"].type)}) + return __class__.enabled + + class AddNExtractor(FrontExtractorOp): op = 'AddN' enabled = True @@ -49,13 +59,9 @@ class BiasAddExtractor(FrontExtractorOp): @staticmethod def extract(node): - data_format = node.pb.attr['data_format'].s.decode("utf-8") - if data_format == "NHWC": - Add.update_node_stat(node, {'data_type': tf_dtype_extractor(node.pb.attr["T"].type)}) - return __class__.enabled - else: - log.error('BiasAdd operation has unsupported `data_format`={}'.format(data_format)) - return False + BiasAdd.update_node_stat(node, {'data_type': tf_dtype_extractor(node.pb.attr["T"].type), + 'data_format': node.pb.attr["data_format"].s.decode()}) + return __class__.enabled class MulExtractor(FrontExtractorOp): diff --git a/model-optimizer/extensions/front/tf/sparse_fill_empty_rows_ext.py b/model-optimizer/extensions/front/tf/sparse_fill_empty_rows_ext.py new file mode 100644 index 00000000000000..c0038c28af8c04 --- /dev/null +++ b/model-optimizer/extensions/front/tf/sparse_fill_empty_rows_ext.py @@ -0,0 +1,33 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import numpy as np + +from extensions.ops.sparse_fill_empty_rows import SparseFillEmptyRows +from mo.front.extractor import FrontExtractorOp + + +class SparseFillEmptyRowsFrontExtractor(FrontExtractorOp): + op = 'SparseFillEmptyRows' + enabled = True + + @staticmethod + def extract(node): + attrs = {} + + SparseFillEmptyRows.update_node_stat(node, attrs) + + return __class__.enabled diff --git a/model-optimizer/extensions/front/tf/swish.py b/model-optimizer/extensions/front/tf/swish.py new file mode 100644 index 00000000000000..eb27db6e18da65 --- /dev/null +++ b/model-optimizer/extensions/front/tf/swish.py @@ -0,0 +1,37 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from extensions.ops.activation_ops import Sigmoid +from extensions.ops.elementwise import Mul +from mo.front.common.replacement import FrontReplacementOp +from mo.graph.graph import Node, Graph + + +class Swish(FrontReplacementOp): + op = "swish_f32" + enabled = True + + def replace_op(self, graph: Graph, node: Node): + mul_node = Mul(graph, {'name': node.name + '/mul_'}).create_node() + sigmoid_node = Sigmoid(graph, {'name': node.name + '/sigmoid_'}).create_node() + + # Connect nodes + node.in_port(0).get_connection().get_source().connect(mul_node.in_port(0)) + node.in_port(0).get_connection().get_source().connect(sigmoid_node.in_port(0)) + sigmoid_node.out_port(0).connect(mul_node.in_port(1)) + + # The "explicit" version of the return value is: [(out_node.id, 0)]) + return [mul_node.id] diff --git a/model-optimizer/extensions/front/tf/swish_test.py b/model-optimizer/extensions/front/tf/swish_test.py new file mode 100644 index 00000000000000..bd526352a52ec6 --- /dev/null +++ b/model-optimizer/extensions/front/tf/swish_test.py @@ -0,0 +1,56 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import unittest + +import numpy as np + +from extensions.front.tf.swish import Swish +from mo.utils.unittest.graph import build_graph, compare_graphs + +nodes_attributes = { + 'placeholder_1': {'shape': np.array([1, 227, 227, 3]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'}, + 'placeholder_2': {'shape': np.array([1, 227, 227, 3]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'}, + # swish operation + 'swish': {'kind': 'op', 'op': 'swish_f32'}, + # Test operation + 'last': {'type': None, 'value': None, 'kind': 'op', 'op': None}, + # Add and Mul operations + 'mul': {'type': 'Multiply', 'kind': 'op', 'op': 'Mul'}, + 'sigmoid': {'value': None, 'type': 'Sigmoid', 'kind': 'op', 'op': 'Sigmoid'}, +} + + +class TestSwish(unittest.TestCase): + def test_swish_test_1(self): + # Test with two different inputs from two placeholders + graph = build_graph(nodes_attributes, + [('placeholder_1', 'swish'), + ('swish', 'last') + ], nodes_with_edges_only=True) + + graph_ref = build_graph(nodes_attributes, + [('placeholder_1', 'sigmoid', {'out': 0}), + ('placeholder_1', 'mul', {'in': 0, 'out': 0}), + ('sigmoid', 'mul', {'in': 1}), + ('mul', 'last'), + ], nodes_with_edges_only=True) + + graph.stage = 'front' + Swish().find_and_replace_pattern(graph) + + (flag, resp) = compare_graphs(graph, graph_ref, 'last', check_op_attrs=True) + self.assertTrue(flag, resp) diff --git a/model-optimizer/extensions/front/tf/unique_ext.py b/model-optimizer/extensions/front/tf/unique_ext.py new file mode 100644 index 00000000000000..0e56287f327d2d --- /dev/null +++ b/model-optimizer/extensions/front/tf/unique_ext.py @@ -0,0 +1,39 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import numpy as np + +from extensions.ops.unique import Unique +from mo.front.extractor import FrontExtractorOp + + +class UniqueFrontExtractor(FrontExtractorOp): + op = 'Unique' + enabled = True + + @staticmethod + def extract(node): + # TensorFlow Unique operation always returns two outputs: unique elements and indices + # The unique elements in the output are not sorted + attrs = { + 'sorted': 'false', + 'return_inverse': 'true', + 'return_counts': 'false' + } + + Unique.update_node_stat(node, attrs) + + return __class__.enabled diff --git a/model-optimizer/extensions/middle/BiasAddBroadcasting.py b/model-optimizer/extensions/middle/BiasAddBroadcasting.py new file mode 100644 index 00000000000000..8353cd914552bc --- /dev/null +++ b/model-optimizer/extensions/middle/BiasAddBroadcasting.py @@ -0,0 +1,75 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from extensions.middle.EltwiseChecker import EltwiseChecker +from extensions.ops.elementwise import Add +from mo.front.common.layout import get_features_dim +from mo.graph.graph import Graph +from mo.middle.replacement import MiddleReplacementPattern +from mo.ops.const import Const +from mo.ops.unsqueeze import Unsqueeze +import numpy as np + + +class BiasAddInputBroadcasting(MiddleReplacementPattern): + """ + In TF BiasAdd op have 2 inputs: data tensor and bias tensor. Bias always has 1D shape and should be broadcasted + to data tensor by features dimension. + + Also replacing BiasAdd by usual Add op after broadcasting. + """ + enabled = True + force_shape_inference = True + + def run_before(self): + return [EltwiseChecker] + + @staticmethod + def pattern(): + return dict( + nodes=[ + ('BiasAdd', dict(kind='op', op='Add', type='BiasAdd')) + ], + edges=[]) + + def replace_pattern(self, graph: Graph, match: dict): + bias_add = match['BiasAdd'] + + # Replace BiasAdd by Add operation + new_add = Add(graph, {'name': bias_add.id + '/Add'}).create_node() + + bias_add.in_port(0).get_connection().set_destination(new_add.in_port(0)) + bias_add.in_port(1).get_connection().set_destination(new_add.in_port(1)) + bias_add.out_port(0).get_connection().set_source(new_add.out_port(0)) + + if bias_add.data_format != 'NCHW': + return + + input_shape = new_add.in_port(0).data.get_shape() + bias_shape = new_add.in_port(1).data.get_shape() + assert len(bias_shape) == 1 + + unsqueeze_dims = np.arange(len(input_shape)) + channel_dim = get_features_dim('NCHW', len(input_shape)) + unsqueeze_dims = np.delete(unsqueeze_dims, channel_dim, 0) + + unsqueeze_node = Unsqueeze(graph, {'name': new_add.id + '/BiasUnsqueeze'}).create_node() + unsqueeze_dims_node = Const(graph, {'name': new_add.id + '/Dims', + 'value': unsqueeze_dims}).create_node() + # Reconnecting nodes + unsqueeze_node.in_port(1).connect(unsqueeze_dims_node.out_port(0)) + unsqueeze_node['override_output_shape'] = True + + new_add.in_port(1).get_connection().insert_node(unsqueeze_node) diff --git a/model-optimizer/extensions/middle/Cast.py b/model-optimizer/extensions/middle/Cast.py index ba9f2ed2e6601c..19a115c5b6feab 100644 --- a/model-optimizer/extensions/middle/Cast.py +++ b/model-optimizer/extensions/middle/Cast.py @@ -32,7 +32,7 @@ def run_after(self): from extensions.middle.pass_separator import PreMiddleStart return [PreMiddleStart] - identity_list = [np.float32, np.double, np.int32, np.int64] + identity_list = [np.float32, np.double, np.int32, np.int64, np.uint8, np.bool] def pattern(self): return dict( @@ -41,8 +41,13 @@ def pattern(self): def replace_pattern(self, graph: Graph, match: dict): # resulting network is fully floating point, so casts to float are useless - if match['op'].dst_type in [np.int32, np.int64]: - log.warning('Deleting Cast node {} to {} from network since Cast operation isn\'t supported yet. Inference results can be' - ' incorrect'.format(match['op'].name, match['op'].dst_type)) + node = match['op'] + name = node.soft_get('name', node.id) + dst_type = node.dst_type - match['op']['identity'] = True + if node.out_port(0).data.get_value() is None: + if dst_type in [np.int32, np.int64]: + log.warning('Deleting Cast node {} to {} from network since Cast operation isn\'t supported yet. ' + 'Inference results can be incorrect'.format(name, dst_type)) + + match['op']['identity'] = True diff --git a/model-optimizer/extensions/middle/EltwiseInputReshape.py b/model-optimizer/extensions/middle/EltwiseInputReshape.py index 520621d4c45b1a..23ce86e8d81740 100644 --- a/model-optimizer/extensions/middle/EltwiseInputReshape.py +++ b/model-optimizer/extensions/middle/EltwiseInputReshape.py @@ -14,15 +14,10 @@ limitations under the License. """ -from copy import deepcopy - import numpy as np from mo.front.common.layout import get_features_dim, shape_for_layout -from mo.graph.graph import Node, Graph -from mo.middle.passes.eliminate import graph_clean_up, graph_clean_up_tf, graph_clean_up_onnx -from mo.middle.passes.fusing.helpers import get_value_id -from mo.middle.pattern_match import for_graph_and_each_sub_graph_recursively +from mo.graph.graph import Graph from mo.middle.replacement import MiddleReplacementPattern from mo.ops.const import Const from mo.ops.op import Op @@ -45,7 +40,7 @@ class Eltwise1DInputReshape(MiddleReplacementPattern): change of graph.graph['layout'] may cause an issue change in re-layout function: convert_nhwc_to_nchw(graph) may cause an issue """ - enabled = True + enabled = False def run_after(self): return [EltwiseInputReshape] @@ -53,19 +48,20 @@ def run_after(self): def find_and_replace_pattern(self, graph: Graph): layout = graph.graph['layout'] for eltwise_op_node in graph.get_op_nodes(is_eltwise=True): - if get_value_id(eltwise_op_node) is None: - out_shape = eltwise_op_node.out_node().shape + out_shape = eltwise_op_node.out_port().data.get_shape() if 4 <= len(out_shape) <= 5: out_features = out_shape[get_features_dim(layout, len(out_shape))] for port, node in eltwise_op_node.in_nodes().items(): if len(node.shape) != len(out_shape) and len(node.shape) == 1 and out_features == node.shape[0]: - in_atts = deepcopy(graph.get_edge_data(node.id, eltwise_op_node.id)[0]) - graph.remove_edge(node.id, eltwise_op_node.id) new_shape = shape_for_layout(layout, batch=1, features=out_features, height=1, width=1, depth=1 if len(out_shape) == 5 else None) - reshape_data_op = Reshape(graph, attrs={'dim': new_shape, 'name': node.id + '/Broadcast'}) - reshape_data_node = reshape_data_op.create_node_with_data([node]) - graph.add_edge(reshape_data_node.id, eltwise_op_node.id, **in_atts) + dim_const = Const(graph, {'value': new_shape, 'name': node.id + '/Dim'}).create_node() + reshape_op = Reshape(graph, attrs={'dim': new_shape, 'name': node.id + '/Broadcast'}).create_node() + + eltwise_op_node.in_port(port).get_source().node.out_port(0).get_connection().set_destination(reshape_op.in_port(0)) + reshape_op.in_port(1).connect(dim_const.out_port(0)) + + reshape_op.out_port(0).connect(eltwise_op_node.in_port(port)) class EltwiseInputReshape(MiddleReplacementPattern): diff --git a/model-optimizer/extensions/middle/InsertSelect.py b/model-optimizer/extensions/middle/InsertSelect.py new file mode 100644 index 00000000000000..ece6e981a1d222 --- /dev/null +++ b/model-optimizer/extensions/middle/InsertSelect.py @@ -0,0 +1,146 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import numpy as np + +from extensions.ops.select import Select +from mo.front.common.partial_infer.utils import int64_array +from mo.graph.graph import Graph, Node +from mo.middle.pattern_match import find_pattern_matches, inverse_dict +from mo.middle.replacement import MiddleReplacementPattern +from mo.ops.concat import Concat +from mo.ops.const import Const +from mo.ops.crop import Crop +from mo.ops.memory import Memory +from mo.ops.result import Result +from mo.utils.error import Error +from mo.utils.graph import invert_sub_graph_between_nodes + + +class AddSelectBeforeMemoryNodePattern(MiddleReplacementPattern): + """ + Add Select before saving state with Memory to avoid garbage saving + """ + enabled = False + + @staticmethod + def pattern(): + return dict( + nodes=[('op', dict(op='Memory', index=0))], + edges=[]) + + @staticmethod + def replace_pattern(graph: Graph, match: dict): + node = match['op'] + + if node.name == 'iteration_number_out': + return + + # calculate length of context when state of inference becomes meaningful + inputs = [] + for n in graph.get_op_nodes(**{'op': 'Parameter'}): + inputs.append(n) + + in_nodes = [] + for inp in inputs: + for ins in inp.out_port(0).get_destinations(): + in_nodes.append(ins.node.name) + + context_len = 1 + try: + subgraph = invert_sub_graph_between_nodes(graph, [node.in_port(0).get_source().node.name], in_nodes) + except Error: + return + + for n in subgraph: + n_node = Node(graph, n) + if n_node.kind == 'op' and n_node.op == 'Splice': + context_len += len(n_node.context) - 1 + + if context_len == 1: + return + + in_node_port = node.in_port(0).get_source() + in_node_shape = node.in_port(0).data.get_shape() + node.in_port(0).disconnect() + + # add Select before saving state to avoid saving garbage + select_node = Select(graph, {'name': 'select_' + node.name}).create_node() + zero_else = Const(graph, {'name': 'zero_else', 'value': np.zeros(in_node_shape)}).create_node() + select_node.in_port(1).connect(in_node_port) + select_node.in_port(2).connect(zero_else.out_port(0)) + + # check if we have already appropriate iteration counter + existing_counters = find_pattern_matches(graph, nodes=[('mem_in', dict(op='Memory', index=1, + shape=int64_array([context_len]))), + ('mem_in_data', dict()), + ('crop_mem_in', dict(op='Crop', axis=int64_array([1]), + offset=int64_array([1]), + dim=int64_array([context_len-1]))), + ('crop_mem_in_data', dict()), + ('concat', dict(op='Concat', axis=1)), + ('concat_data', dict()), + ('const_1', dict(op='Const')), + ('const_1_data', dict()), + ('mem_out', dict(op='Memory', index=0, + shape=int64_array([context_len]))), + ('crop_out', dict(op='Crop', axis=int64_array([1]), + offset=int64_array([0]), + dim=int64_array([1]))), + ('crop_out_data', dict()), + ('select', dict(op='Select')) + ], + edges=[('mem_in', 'mem_in_data'), ('mem_in_data', 'crop_mem_in'), + ('crop_mem_in', 'crop_mem_in_data'), + ('crop_mem_in_data', 'concat', {'in': 0}), + ('const_1', 'const_1_data'), + ('const_1_data', 'concat', {'in': 1}), + ('concat', 'concat_data'), ('concat_data', 'mem_out'), + ('concat_data', 'crop_out'), ('crop_out', 'crop_out_data'), + ('crop_out_data', 'select')]) + counter_match = next(existing_counters, None) + if counter_match is not None: + input_port = Node(graph, inverse_dict(counter_match)['crop_out']).out_port(0) + else: + mem_out = Memory(graph, {'name': 'iteration_number', 'size': 2, + 'index': 1, 'id': 'iteration_'+node.name, + 'shape': int64_array([context_len]), + 'force_precision': 'I32'}).create_node() + cut_first = Crop(graph, {'name': 'cut_first', 'axis': int64_array([1]), + 'offset': int64_array([1]), 'dim': int64_array([context_len-1]), + 'force_precision': 'I32'}).create_node() + cut_first.in_port(0).connect(mem_out.out_port(0)) + ones = Const(graph, {'name': 'ones', 'value': np.ones([1, 1], dtype=np.int64), + 'force_precision': 'I32'}).create_node() + concat = Concat(graph, {'name': 'concat_ones', 'in_ports_count': 2, 'axis': 1, + 'force_precision': 'I32'}).create_node() + concat.in_port(0).connect(cut_first.out_port(0)) + concat.in_port(1).connect(ones.out_port(0)) + mem_in = Memory(graph, {'name': 'iteration_number_out', 'size': 2, + 'index': 0, 'id': 'iteration_'+node.name, + 'shape': int64_array([context_len]), + 'force_precision': 'I32'}).create_node() + mem_in.in_port(0).connect(concat.out_port(0)) + res = Result(graph, {}).create_node() + mem_in.out_port(0).connect(res.in_port(0)) + cut_last = Crop(graph, {'name': 'cut_last', 'axis': int64_array([1]), + 'offset': int64_array([0]), 'dim': int64_array([1]), + 'force_precision': 'I32'}).create_node() + cut_last.in_port(0).connect(concat.out_port(0)) + input_port = cut_last.out_port(0) + + select_node.in_port(0).connect(input_port) + select_node.out_port(0).connect(node.in_port(0)) + select_node.out_port(0).data.set_shape(in_node_shape) diff --git a/model-optimizer/extensions/middle/InsertSelect_test.py b/model-optimizer/extensions/middle/InsertSelect_test.py new file mode 100644 index 00000000000000..2b0e97203a0564 --- /dev/null +++ b/model-optimizer/extensions/middle/InsertSelect_test.py @@ -0,0 +1,271 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import numpy as np +import unittest + +from extensions.middle.InsertSelect import AddSelectBeforeMemoryNodePattern +from mo.front.common.partial_infer.utils import int64_array +from mo.utils.unittest.graph import build_graph, compare_graphs + + +class InsertSelectTests(unittest.TestCase): + + # graph have no splices - selects should not be inserted + def test_insert_select_0(self): + graph = build_graph({'in_node': {'kind': 'data', 'shape': [1, 13]}, + 'placeholder_1': {'kind': 'op', 'op': None}, + 'placeholder_data_1': {'kind': 'data', 'shape': [1, 13]}, + 'memory': {'kind': 'op', 'op': 'Memory', 'index': 0}, + }, + [('in_node', 'placeholder_1'), ('placeholder_1', 'placeholder_data_1'), + ('placeholder_data_1', 'memory') + ], + nodes_with_edges_only=True) + AddSelectBeforeMemoryNodePattern().find_and_replace_pattern(graph) + ref_graph = build_graph({'in_node': {'kind': 'data', 'shape': [1, 13]}, + 'placeholder_1': {'kind': 'op', 'op': None}, + 'placeholder_data_1': {'kind': 'data', 'shape': [1, 13]}, + 'memory': {'kind': 'op', 'op': 'Memory', 'index': 0}, + }, + [('in_node', 'placeholder_1'), ('placeholder_1', 'placeholder_data_1'), + ('placeholder_data_1', 'memory') + ], + nodes_with_edges_only=True + ) + + (flag, resp) = compare_graphs(graph, ref_graph, 'memory') + self.assertTrue(flag, resp) + + # graph contains 1 splice with context length 5, should be inserted select with memory as counter with length 5 + def test_insert_select_1(self): + graph = build_graph({'in_node': {'kind': 'data', 'shape': [1, 13]}, + 'placeholder_1': {'kind': 'op', 'op': None}, + 'placeholder_data_1': {'kind': 'data', 'shape': [1, 13]}, + 'splice_1': {'kind': 'op', 'op': 'Splice', 'context': np.array([-2, -1, 0, 1, 2])}, + 'splice_data_1': {'kind': 'data', 'shape': [1, 13]}, + 'placeholder_2': {'kind': 'op', 'op': None}, + 'placeholder_data_2': {'kind': 'data', 'shape': [1, 26]}, + 'memory': {'kind': 'op', 'op': 'Memory', 'index': 0}, + }, + [('in_node', 'placeholder_1'), ('placeholder_1', 'placeholder_data_1'), + ('placeholder_data_1', 'splice_1'), ('splice_1', 'splice_data_1'), + ('splice_data_1', 'placeholder_2'), ('placeholder_2', 'placeholder_data_2'), + ('placeholder_data_2', 'memory') + ], + nodes_with_edges_only=True) + AddSelectBeforeMemoryNodePattern().find_and_replace_pattern(graph) + ref_graph = build_graph({'in_node': {'kind': 'data', 'shape': [1, 13]}, + 'placeholder_1': {'kind': 'op', 'op': None}, + 'placeholder_data_1': {'kind': 'data', 'shape': [1, 13]}, + 'splice_1': {'kind': 'op', 'op': 'Splice', 'context': np.array([-2, -1, 0, 1, 2])}, + 'splice_data_1': {'kind': 'data', 'shape': [1, 13]}, + 'placeholder_2': {'kind': 'op', 'op': None}, + + 'memory_in': {'kind': 'op', 'op': 'Memory', 'shape': int64_array([5])}, + 'memory_in_data': {'kind': 'data'}, + 'memory_out': {'kind': 'op', 'op': 'Memory', 'shape': int64_array([5])}, + 'memory_out_data': {'kind': 'data'}, + 'result': {'kind': 'op', 'op': 'Result'}, + 'crop_in': {'kind': 'op', 'op': 'Crop', 'axis': 1, 'offset': 1, 'dim': 4}, + 'crop_in_data': {'kind': 'data'}, + 'crop_out': {'kind': 'op', 'op': 'Crop', 'axis': 1, 'offset': 0, 'dim': 1}, + 'crop_out_data': {'kind': 'data'}, + 'select': {'kind': 'op', 'op': 'Select'}, + 'select_out_data': {'kind': 'data', 'shape': [1, 26]}, + 'const_0': {'kind': 'op', 'op': 'Const'}, + 'const_0_data': {'kind': 'data'}, + 'const_1': {'kind': 'op', 'op': 'Const'}, + 'const_1_data': {'kind': 'data'}, + 'concat': {'kind': 'op', 'op': 'Concat'}, + 'concat_data': {'kind': 'data'}, + + 'placeholder_data_2': {'kind': 'data', 'shape': [1, 26]}, + 'memory': {'kind': 'op', 'op': 'Memory', 'index': 0}, + }, + [('in_node', 'placeholder_1'), ('placeholder_1', 'placeholder_data_1'), + ('placeholder_data_1', 'splice_1'), ('splice_1', 'splice_data_1'), + ('splice_data_1', 'placeholder_2'), ('placeholder_2', 'placeholder_data_2'), + ('placeholder_data_2', 'select', {'in': 1}), + + ('memory_in', 'memory_in_data'), ('memory_in_data', 'crop_in'), + ('crop_in', 'crop_in_data'), ('crop_in_data', 'concat', {'in': 0}), + ('const_1', 'const_1_data'), ('const_1_data', 'concat', {'in': 1}), + ('concat', 'concat_data'), ('concat_data', 'memory_out'), + ('memory_out', 'memory_out_data'), ('memory_out_data', 'result'), + ('concat_data', 'crop_out'), ('crop_out', 'crop_out_data'), + ('crop_out_data', 'select', {'in': 0}), + ('const_0', 'const_0_data'), ('const_0_data', 'select', {'in': 2}), + + ('select', 'select_out_data'), + ('select_out_data', 'memory') + ], + nodes_with_edges_only=True + ) + + (flag, resp) = compare_graphs(graph, ref_graph, 'memory') + self.assertTrue(flag, resp) + + # graph contains 1 splice with context length 5 on the path to memory and 1 out of path, + # should be inserted select with memory as counter with length 5 + def test_insert_select_2(self): + graph = build_graph({'in_node': {'kind': 'data', 'shape': [1, 13]}, + 'placeholder_1': {'kind': 'op', 'op': None}, + 'placeholder_data_1': {'kind': 'data', 'shape': [1, 13]}, + 'splice_1': {'kind': 'op', 'op': 'Splice', 'context': np.array([-2, -1, 0, 1, 2])}, + 'splice_data_1': {'kind': 'data', 'shape': [1, 65]}, + 'splice_2': {'kind': 'op', 'op': 'Splice', 'context': np.array([-1, 0, 1])}, + 'splice_data_2': {'kind': 'data', 'shape': [1, 39]}, + 'placeholder_2': {'kind': 'op', 'op': None}, + 'placeholder_data_2': {'kind': 'data', 'shape': [1, 26]}, + 'memory': {'kind': 'op', 'op': 'Memory', 'index': 0}, + }, + [('in_node', 'placeholder_1'), ('placeholder_1', 'placeholder_data_1'), + ('placeholder_data_1', 'splice_1'), ('splice_1', 'splice_data_1'), + ('placeholder_data_1', 'splice_2'), ('splice_2', 'splice_data_2'), + ('splice_data_1', 'placeholder_2'), ('placeholder_2', 'placeholder_data_2'), + ('placeholder_data_2', 'memory') + ], + nodes_with_edges_only=True) + AddSelectBeforeMemoryNodePattern().find_and_replace_pattern(graph) + ref_graph = build_graph({'in_node': {'kind': 'data', 'shape': [1, 13]}, + 'placeholder_1': {'kind': 'op', 'op': None}, + 'placeholder_data_1': {'kind': 'data', 'shape': [1, 13]}, + 'splice_1': {'kind': 'op', 'op': 'Splice', 'context': np.array([-2, -1, 0, 1, 2])}, + 'splice_data_1': {'kind': 'data', 'shape': [1, 65]}, + 'splice_2': {'kind': 'op', 'op': 'Splice', 'context': np.array([-1, 0, 1])}, + 'splice_data_2': {'kind': 'data', 'shape': [1, 39]}, + 'placeholder_2': {'kind': 'op', 'op': None}, + + 'memory_in': {'kind': 'op', 'op': 'Memory', 'shape': int64_array([5])}, + 'memory_in_data': {'kind': 'data'}, + 'memory_out': {'kind': 'op', 'op': 'Memory', 'shape': int64_array([5])}, + 'memory_out_data': {'kind': 'data'}, + 'result': {'kind': 'op', 'op': 'Result'}, + 'crop_in': {'kind': 'op', 'op': 'Crop', 'axis': 1, 'offset': 1, 'dim': 4}, + 'crop_in_data': {'kind': 'data'}, + 'crop_out': {'kind': 'op', 'op': 'Crop', 'axis': 1, 'offset': 0, 'dim': 1}, + 'crop_out_data': {'kind': 'data'}, + 'select': {'kind': 'op', 'op': 'Select'}, + 'select_out_data': {'kind': 'data', 'shape': [1, 26]}, + 'const_0': {'kind': 'op', 'op': 'Const'}, + 'const_0_data': {'kind': 'data'}, + 'const_1': {'kind': 'op', 'op': 'Const'}, + 'const_1_data': {'kind': 'data'}, + 'concat': {'kind': 'op', 'op': 'Concat'}, + 'concat_data': {'kind': 'data'}, + + 'placeholder_data_2': {'kind': 'data', 'shape': [1, 26]}, + 'memory': {'kind': 'op', 'op': 'Memory', 'index': 0}, + }, + [('in_node', 'placeholder_1'), ('placeholder_1', 'placeholder_data_1'), + ('placeholder_data_1', 'splice_1'), ('splice_1', 'splice_data_1'), + ('placeholder_data_1', 'splice_2'), ('splice_2', 'splice_data_2'), + ('splice_data_1', 'placeholder_2'), ('placeholder_2', 'placeholder_data_2'), + ('placeholder_data_2', 'select', {'in': 1}), + + ('memory_in', 'memory_in_data'), ('memory_in_data', 'crop_in'), + ('crop_in', 'crop_in_data'), ('crop_in_data', 'concat', {'in': 0}), + ('const_1', 'const_1_data'), ('const_1_data', 'concat', {'in': 1}), + ('concat', 'concat_data'), ('concat_data', 'memory_out'), + ('memory_out', 'memory_out_data'), ('memory_out_data', 'result'), + ('concat_data', 'crop_out'), ('crop_out', 'crop_out_data'), + ('crop_out_data', 'select', {'in': 0}), + ('const_0', 'const_0_data'), ('const_0_data', 'select', {'in': 2}), + + ('select', 'select_out_data'), + ('select_out_data', 'memory') + ], + nodes_with_edges_only=True + ) + + (flag, resp) = compare_graphs(graph, ref_graph, 'memory') + self.assertTrue(flag, resp) + + # graph contains 2 splices with sum context length 8 on the path to memory, + # should be inserted select with memory as counter with length 7 + def test_insert_select_3(self): + graph = build_graph({'in_node': {'kind': 'data', 'shape': [1, 13]}, + 'placeholder_1': {'kind': 'op', 'op': None}, + 'placeholder_data_1': {'kind': 'data', 'shape': [1, 13]}, + 'splice_1': {'kind': 'op', 'op': 'Splice', 'context': np.array([-2, -1, 0, 1, 2])}, + 'splice_data_1': {'kind': 'data', 'shape': [1, 65]}, + 'splice_2': {'kind': 'op', 'op': 'Splice', 'context': np.array([-1, 0, 1])}, + 'splice_data_2': {'kind': 'data', 'shape': [1, 39]}, + 'placeholder_2': {'kind': 'op', 'op': None}, + 'placeholder_data_2': {'kind': 'data', 'shape': [1, 26]}, + 'memory': {'kind': 'op', 'op': 'Memory', 'index': 0}, + }, + [('in_node', 'placeholder_1'), ('placeholder_1', 'placeholder_data_1'), + ('placeholder_data_1', 'splice_1'), ('splice_1', 'splice_data_1'), + ('splice_data_1', 'splice_2'), ('splice_2', 'splice_data_2'), + ('splice_data_2', 'placeholder_2'), ('placeholder_2', 'placeholder_data_2'), + ('placeholder_data_2', 'memory') + ], + nodes_with_edges_only=True) + AddSelectBeforeMemoryNodePattern().find_and_replace_pattern(graph) + ref_graph = build_graph({'in_node': {'kind': 'data', 'shape': [1, 13]}, + 'placeholder_1': {'kind': 'op', 'op': None}, + 'placeholder_data_1': {'kind': 'data', 'shape': [1, 13]}, + 'splice_1': {'kind': 'op', 'op': 'Splice', 'context': np.array([-2, -1, 0, 1, 2])}, + 'splice_data_1': {'kind': 'data', 'shape': [1, 65]}, + 'splice_2': {'kind': 'op', 'op': 'Splice', 'context': np.array([-1, 0, 1])}, + 'splice_data_2': {'kind': 'data', 'shape': [1, 39]}, + 'placeholder_2': {'kind': 'op', 'op': None}, + + 'memory_in': {'kind': 'op', 'op': 'Memory', 'shape': int64_array([7])}, + 'memory_in_data': {'kind': 'data'}, + 'memory_out': {'kind': 'op', 'op': 'Memory', 'shape': int64_array([7])}, + 'memory_out_data': {'kind': 'data'}, + 'result': {'kind': 'op', 'op': 'Result'}, + 'crop_in': {'kind': 'op', 'op': 'Crop', 'axis': 1, 'offset': 1, 'dim': 6}, + 'crop_in_data': {'kind': 'data'}, + 'crop_out': {'kind': 'op', 'op': 'Crop', 'axis': 1, 'offset': 0, 'dim': 1}, + 'crop_out_data': {'kind': 'data'}, + 'select': {'kind': 'op', 'op': 'Select'}, + 'select_out_data': {'kind': 'data', 'shape': [1, 26]}, + 'const_0': {'kind': 'op', 'op': 'Const'}, + 'const_0_data': {'kind': 'data'}, + 'const_1': {'kind': 'op', 'op': 'Const'}, + 'const_1_data': {'kind': 'data'}, + 'concat': {'kind': 'op', 'op': 'Concat'}, + 'concat_data': {'kind': 'data'}, + + 'placeholder_data_2': {'kind': 'data', 'shape': [1, 26]}, + 'memory': {'kind': 'op', 'op': 'Memory', 'index': 0}, + }, + [('in_node', 'placeholder_1'), ('placeholder_1', 'placeholder_data_1'), + ('placeholder_data_1', 'splice_1'), ('splice_1', 'splice_data_1'), + ('splice_data_1', 'splice_2'), ('splice_2', 'splice_data_2'), + ('splice_data_2', 'placeholder_2'), ('placeholder_2', 'placeholder_data_2'), + ('placeholder_data_2', 'select', {'in': 1}), + + ('memory_in', 'memory_in_data'), ('memory_in_data', 'crop_in'), + ('crop_in', 'crop_in_data'), ('crop_in_data', 'concat', {'in': 0}), + ('const_1', 'const_1_data'), ('const_1_data', 'concat', {'in': 1}), + ('concat', 'concat_data'), ('concat_data', 'memory_out'), + ('memory_out', 'memory_out_data'), ('memory_out_data', 'result'), + ('concat_data', 'crop_out'), ('crop_out', 'crop_out_data'), + ('crop_out_data', 'select', {'in': 0}), + ('const_0', 'const_0_data'), ('const_0_data', 'select', {'in': 2}), + + ('select', 'select_out_data'), + ('select_out_data', 'memory') + ], + nodes_with_edges_only=True + ) + + (flag, resp) = compare_graphs(graph, ref_graph, 'memory') + self.assertTrue(flag, resp) diff --git a/model-optimizer/extensions/middle/RemoveDuplicationMemory.py b/model-optimizer/extensions/middle/RemoveDuplicationMemory.py index 1562360cb76dbe..a182c13fd02a2e 100644 --- a/model-optimizer/extensions/middle/RemoveDuplicationMemory.py +++ b/model-optimizer/extensions/middle/RemoveDuplicationMemory.py @@ -15,7 +15,7 @@ """ import numpy as np -from mo.graph.graph import Graph, Node +from mo.graph.graph import Graph from mo.middle.replacement import MiddleReplacementPattern from mo.ops.crop import Crop @@ -34,41 +34,118 @@ def pattern(): @staticmethod def replace_pattern(graph: Graph, match: dict): - if len(match['op'].in_nodes()) == 0: - return - mem = match['op'] - in_mem = mem.in_node(0) + mem_shape = mem.in_port(0).data.get_shape() + mem_parent = mem.in_port(0).get_source() context = mem['context'] - outs = in_mem.out_nodes() - for out in outs: - if out['op'] == 'Splice' and out.id != mem.id and set(out['context']).issubset(set(context)): - left_cont_out = out['context'][0] + for child_port in mem_parent.get_destinations(): + child = child_port.node + # check if we find Splice containing context 'context' + if child['op'] == 'Splice' and child.id != mem.id and set(child['context']).issubset(set(context)): + left_cont_out = child['context'][0] left_cont = context[0] - out_node = out.out_node() - for out_name, out_edge in out_node.get_outputs(): - out_transfer = Node(graph, out_name) + for child_of_child in child.out_port(0).get_destinations(): + out_transfer = child_of_child.node + out_transfer_port = child_of_child if out_transfer['op'] == 'Crop': # modify existing Crop to get right data from larger Splice - out_transfer['offset'] = out_transfer['offset'] + (left_cont_out - left_cont) * in_mem.shape[-1] + out_transfer['offset'] = out_transfer['offset'] + (left_cont_out - left_cont) * mem_shape[-1] else: # insert Crop if we have not one - out_transfer.in_port(out_edge['in']).disconnect() + child_of_child.disconnect() crop_node = Crop(graph, {'name': graph.unique_id(prefix='Splice_crop_'), - 'offset': (left_cont_out - left_cont) * in_mem.shape[-1], - 'dim': np.array([len(out['context']) * in_mem.shape[-1]]), + 'offset': (left_cont_out - left_cont) * mem_shape[-1], + 'dim': np.array([len(child['context']) * mem_shape[-1]]), 'axis': np.array([-1])}).create_node() - out.out_port(0).connect(crop_node.in_port(0)) - crop_node.out_port(0).connect(out_transfer.in_port(out_edge['in'])) - crop_node.out_node(0).shape = out_node.shape + child.out_port(0).connect(crop_node.in_port(0)) + crop_node.out_port(0).connect(child_of_child) + crop_node.out_port(0).data.set_shape(child.out_port(0).data.get_shape()) + + out_transfer_port = crop_node.in_port(0) + + # move edge to child from old Splice to larger + out_transfer_port.disconnect() + mem.out_port(0).connect(out_transfer_port) + + graph.remove_node(child.id) + + +class MergeNeighborSplicePattern(MiddleReplacementPattern): + """ + Merge Splices with neighbor contexts, for example: [-5, 0] and [0, 3] to context [-5, 3] + """ + enabled = False - out_transfer = crop_node + @staticmethod + def pattern(): + return dict( + nodes=[('op', dict(op='Splice'))], + edges=[]) + + @staticmethod + def replace_pattern(graph: Graph, match: dict): + mem = match['op'] + mem_shape = mem.in_port(0).data.get_shape() + mem_parent = mem.in_port(0).get_source() + context = mem['context'] + + for child_port in mem_parent.get_destinations(): + child = child_port.node + if child['op'] == 'Splice' and child.id != mem.id and \ + (child['context'][0] == context[-1] or child['context'][0] == context[-1]): + + new_context = list(context) + new_context.extend(list(child['context'])) + new_context = list(set(new_context)) + new_context.sort() + if child['context'][0] == context[-1]: + new_node = mem + rem_node = child + else: + new_node = child + rem_node = mem + + # reset edges from rem_node to new_node + for out_port_rem in rem_node.out_port(0).get_destinations(): + out_transfer = out_port_rem.node + out_transfer_shape = out_port_rem.data.get_shape().copy() + + out_port_rem.disconnect() + + if out_transfer['op'] == 'Crop': + # modify existing Crop to get right data from larger Splice + out_transfer['offset'] = out_transfer['offset'] + (len(new_context) - len(rem_node.context)) * mem_shape[-1] + out_port_rem.connect(new_node.out_port(0)) + else: + # insert Crop if we have not one + crop_node = Crop(graph, {'name': graph.unique_id(prefix='Splice_crop_'), + 'offset': (len(new_context) - len(rem_node.context)) * mem_shape[-1], + 'dim': np.array([len(rem_node['context']) * mem_shape[-1]]), + 'axis': np.array([-1])}).create_node() + new_node.out_port(0).connect(crop_node.in_port(0)) + crop_node.out_port(0).connect(out_port_rem) + crop_node.out_port(0).data.set_shape(out_transfer_shape) + + for out_port_rem in new_node.out_port(0).get_destinations(): + out_transfer = out_port_rem.node + out_transfer_shape = out_port_rem.data.get_shape().copy() + + if out_transfer['op'] != 'Crop': + # insert Crop if we have not one + crop_node = Crop(graph, {'name': graph.unique_id(prefix='Splice_crop_'), + 'offset': np.array([0]), + 'dim': np.array([len(new_node['context']) * mem_shape[-1]]), + 'axis': np.array([-1])}).create_node() + new_node.out_port(0).connect(crop_node.in_port(0)) + out_port_rem.disconnect() + crop_node.out_port(0).connect(out_port_rem) + crop_node.out_port(0).data.set_shape(out_transfer_shape) - # move edge from old Splice to larger - in_port = graph.get_edge_data(out_node.id, out_transfer.id)[0]['in'] - out_transfer.in_port(0).disconnect() - mem.out_port(0).connect(out_transfer.in_port(in_port)) + new_shape = new_node.out_port(0).data.get_shape() + new_shape[1] += rem_node.out_port(0).data.get_shape()[1] - rem_node.in_port(0).data.get_shape()[1] + new_node.out_port(0).data.set_shape(new_shape) + new_node.context = new_context - graph.remove_node(out.id) + graph.remove_node(rem_node.id) diff --git a/model-optimizer/extensions/middle/RemoveDuplicationMemory_test.py b/model-optimizer/extensions/middle/RemoveDuplicationMemory_test.py index bbef79f7a0a241..db31f6ab79a0ef 100644 --- a/model-optimizer/extensions/middle/RemoveDuplicationMemory_test.py +++ b/model-optimizer/extensions/middle/RemoveDuplicationMemory_test.py @@ -15,14 +15,15 @@ """ import unittest -from extensions.middle.RemoveDuplicationMemory import RemoveMemoryDuplicationPattern +from extensions.middle.RemoveDuplicationMemory import RemoveMemoryDuplicationPattern, MergeNeighborSplicePattern from mo.utils.unittest.graph import build_graph, compare_graphs class RemoveMemoryDuplicationPatternTests(unittest.TestCase): def test_remove_duplication(self): - graph = build_graph({'in_node': {'kind': 'data', 'shape': [1, 13]}, + graph = build_graph({'input': {'kind': 'op', 'op': 'Parameter'}, + 'in_node': {'kind': 'data', 'shape': [1, 13]}, 'splice_1': {'kind': 'op', 'op': 'Splice', 'context': range(-5, 6)}, 'splice_data_1': {'kind': 'data', 'shape': [1, 143]}, 'placeholder_1': {'kind': 'op', 'op': None}, @@ -30,12 +31,14 @@ def test_remove_duplication(self): 'splice_data_2': {'kind': 'data', 'shape': [1, 39]}, 'placeholder_2': {'kind': 'op', 'op': None}, }, - [('in_node', 'splice_1'), ('splice_1', 'splice_data_1'), ('splice_data_1', 'placeholder_1'), + [('input', 'in_node'), ('in_node', 'splice_1'), + ('splice_1', 'splice_data_1'), ('splice_data_1', 'placeholder_1'), ('in_node', 'splice_2'), ('splice_2', 'splice_data_2'), ('splice_data_2', 'placeholder_2'), ], nodes_with_edges_only=True) RemoveMemoryDuplicationPattern().find_and_replace_pattern(graph) - ref_graph = build_graph({'in_node': {'kind': 'data', 'shape': [1, 13]}, + ref_graph = build_graph({'input': {'kind': 'op', 'op': 'Parameter'}, + 'in_node': {'kind': 'data', 'shape': [1, 13]}, 'splice_1': {'kind': 'op', 'op': 'Splice', 'context': range(-5, 6)}, 'splice_data_1': {'kind': 'data', 'shape': [1, 143]}, 'placeholder_1': {'kind': 'op'}, @@ -44,8 +47,8 @@ def test_remove_duplication(self): 'placeholder_2': {'kind': 'op'}, }, [ - ('in_node', 'splice_1'), ('splice_1', 'splice_data_1'), - ('splice_data_1', 'placeholder_1'), + ('input', 'in_node'), ('in_node', 'splice_1'), + ('splice_1', 'splice_data_1'), ('splice_data_1', 'placeholder_1'), ('splice_data_1', 'crop_2'), ('crop_2', 'splice_data_2'), ('splice_data_2', 'placeholder_2'), ], @@ -56,7 +59,8 @@ def test_remove_duplication(self): self.assertTrue(flag, resp) def test_remove_duplication_with_crops(self): - graph = build_graph({'in_node': {'kind': 'data', 'shape': [1, 13]}, + graph = build_graph({'input': {'kind': 'op', 'op': 'Parameter'}, + 'in_node': {'kind': 'data', 'shape': [1, 13]}, 'splice_1': {'kind': 'op', 'op': 'Splice', 'context': range(-5, 6)}, 'splice_data_1': {'kind': 'data', 'shape': [1, 143]}, 'crop_1': {'kind': 'op', 'op': 'Crop', 'offset': 13, 'dim': 13, 'axis': -1}, @@ -64,24 +68,66 @@ def test_remove_duplication_with_crops(self): 'splice_data_2': {'kind': 'data', 'shape': [1, 39]}, 'crop_2': {'kind': 'op', 'op': 'Crop', 'offset': 13, 'dim': 13, 'axis': -1}, }, - [('in_node', 'splice_1'), ('splice_1', 'splice_data_1'), ('splice_data_1', 'crop_1'), + [('input', 'in_node'), ('in_node', 'splice_1'), + ('splice_1', 'splice_data_1'), ('splice_data_1', 'crop_1'), ('in_node', 'splice_2'), ('splice_2', 'splice_data_2'), ('splice_data_2', 'crop_2'), ], nodes_with_edges_only=True) RemoveMemoryDuplicationPattern().find_and_replace_pattern(graph) - ref_graph = build_graph({'in_node': {'kind': 'data', 'shape': [1, 13]}, + ref_graph = build_graph({'input': {'kind': 'op', 'op': 'Parameter'}, + 'in_node': {'kind': 'data', 'shape': [1, 13]}, 'splice_1': {'kind': 'op', 'op': 'Splice', 'context': range(-5, 6)}, 'splice_data_1': {'kind': 'data', 'shape': [1, 143]}, 'crop_1': {'kind': 'op', 'op': 'Crop', 'offset': 13, 'dim': 13}, 'crop_2': {'kind': 'op', 'op': 'Crop', 'offset': 65, 'dim': 13, 'axis': -1}, }, [ - ('in_node', 'splice_1'), ('splice_1', 'splice_data_1'), - ('splice_data_1', 'crop_1'), - ('splice_data_1', 'crop_2'), + ('input', 'in_node'), ('in_node', 'splice_1'), + ('splice_1', 'splice_data_1'), + ('splice_data_1', 'crop_1'), ('splice_data_1', 'crop_2'), ], nodes_with_edges_only=True ) (flag, resp) = compare_graphs(graph, ref_graph, 'crop_2') self.assertTrue(flag, resp) + + def test_remove_duplication_neibor(self): + graph = build_graph({'input': {'kind': 'op', 'op': 'Parameter'}, + 'in_node': {'kind': 'data', 'shape': [1, 13]}, + 'splice_1': {'kind': 'op', 'op': 'Splice', 'context': range(-5, 1)}, + 'splice_data_1': {'kind': 'data', 'shape': [1, 78], 'value': None}, + 'placeholder_1': {'kind': 'op', 'op': None}, + 'splice_2': {'kind': 'op', 'op': 'Splice', 'context': range(0, 2)}, + 'splice_data_2': {'kind': 'data', 'shape': [1, 26], 'value': None}, + 'placeholder_2': {'kind': 'op', 'op': None}, + }, + [('input', 'in_node'), ('in_node', 'splice_1'), + ('splice_1', 'splice_data_1'), ('splice_data_1', 'placeholder_1'), + ('in_node', 'splice_2'), ('splice_2', 'splice_data_2'), ('splice_data_2', 'placeholder_2'), + ], + nodes_with_edges_only=True) + MergeNeighborSplicePattern().find_and_replace_pattern(graph) + ref_graph = build_graph({'input': {'kind': 'op', 'op': 'Parameter'}, + 'in_node': {'kind': 'data', 'shape': [1, 13]}, + 'splice_1': {'kind': 'op', 'op': 'Splice', 'context': range(-5, 2)}, + 'splice_data_1': {'kind': 'data', 'shape': [1, 91], 'value': None}, + 'crop_1': {'kind': 'op', 'op': 'Crop', 'offset': 0, 'dim': 78, 'axis': -1}, + 'crop_1_data': {'kind': 'data', 'shape': [1, 78]}, + 'placeholder_1': {'kind': 'op'}, + 'crop_2': {'kind': 'op', 'op': 'Crop', 'offset': 65, 'dim': 26, 'axis': -1}, + 'splice_data_2': {'kind': 'data', 'shape': [1, 26], 'value': None}, + 'placeholder_2': {'kind': 'op'}, + }, + [ + ('input', 'in_node'), ('in_node', 'splice_1'), + ('splice_1', 'splice_data_1'), ('splice_data_1', 'crop_1'), + ('crop_1', 'crop_1_data'), ('crop_1_data', 'placeholder_1'), + ('splice_data_1', 'crop_2'), ('crop_2', 'splice_data_2'), + ('splice_data_2', 'placeholder_2'), + ], + nodes_with_edges_only=True + ) + + (flag, resp) = compare_graphs(graph, ref_graph, 'placeholder_2') + self.assertTrue(flag, resp) diff --git a/model-optimizer/extensions/middle/RemoveUselessCrops.py b/model-optimizer/extensions/middle/RemoveUselessCrops.py index 5bee3c38ccbc4f..433304693263c5 100644 --- a/model-optimizer/extensions/middle/RemoveUselessCrops.py +++ b/model-optimizer/extensions/middle/RemoveUselessCrops.py @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. """ -from mo.graph.graph import Graph, Node +from mo.graph.graph import Graph from mo.middle.replacement import MiddleReplacementPattern @@ -40,22 +40,23 @@ def pattern(): @staticmethod def replace_pattern(graph: Graph, match: dict): crop_node = match['crop'] - in_crop_node = crop_node.in_node(0) + crop_node_parent_port = crop_node.in_port(0).get_source() concat_node = match['concat'] - data = match['data'] - if len(data.out_nodes()) != 1: + if len(crop_node.out_port(0).get_destinations()) != 1: return - outs = in_crop_node.out_nodes() + outs = crop_node_parent_port.get_destinations() offsets_dims = list([]) crop_list = list([]) axis = crop_node['axis'] - for out in outs: + for in_port in outs: + out = in_port.node if out['op'] == 'Crop' and out['axis'] == axis and \ - len(out.out_node().out_nodes()) == 1 and out.out_node().out_node(0).id == concat_node.id: + len(out.out_port(0).get_destinations()) == 1 and \ + out.out_port(0).get_destination().node == concat_node: offsets_dims.append((out['offset'], out['dim'])) - crop_list.append(out.id) + crop_list.append(out) offsets_dims.sort(key=lambda off_dim: off_dim[0]) size = 0 @@ -64,21 +65,24 @@ def replace_pattern(graph: Graph, match: dict): return size = size + off_d[1] - if size != in_crop_node.shape[axis]: + if size != crop_node_parent_port.data.get_shape()[axis]: return remove_concat = True - for inp, attrs in concat_node.get_inputs(): - in_node_id, a = Node(graph, inp).get_inputs()[0] - if in_node_id not in crop_list: - remove_concat = False - else: - Node(graph, in_node_id).out_port(0).disconnect() + free_port = None + for inp in concat_node.in_ports(): + if not concat_node.in_port(inp).disconnected(): + in_node = concat_node.in_port(inp).get_source().node + if in_node not in crop_list: + remove_concat = False + else: + in_node.out_port(0).disconnect() + free_port = inp if remove_concat: - for crop in crop_list: - Node(graph, crop).in_port(0).disconnect() - - concat_out = concat_node.out_node(0).out_node(0) - concat_out.in_port(0).disconnect() - in_crop_node.in_node(0).out_port(0).connect(concat_out.in_port(0)) + concat_outs = concat_node.out_port(0).get_destinations() + for out in concat_outs: + out.disconnect() + crop_node_parent_port.connect(out) + else: + crop_node_parent_port.connect(concat_node.in_port(free_port)) diff --git a/model-optimizer/extensions/middle/RemoveUselessCrops_test.py b/model-optimizer/extensions/middle/RemoveUselessCrops_test.py index 976ad133f8e1e1..c3f9e0d4148f7d 100644 --- a/model-optimizer/extensions/middle/RemoveUselessCrops_test.py +++ b/model-optimizer/extensions/middle/RemoveUselessCrops_test.py @@ -54,10 +54,28 @@ def test_useless_crops(self): RemoveUselessCropsPattern().find_and_replace_pattern(graph) ref_graph = build_graph({'placeholder_in': {'kind': 'op', 'op': 'Parameter'}, 'in_node': {'kind': 'data', 'shape': [1, 130]}, + 'crop1': {'kind': 'op', 'op': 'Crop', 'offset': 0, 'dim': 26, 'axis': -1}, + 'crop_data_1': {'kind': 'data', 'shape': [1, 26]}, + 'crop2': {'kind': 'op', 'op': 'Crop', 'offset': 26, 'dim': 26, 'axis': -1}, + 'crop_data_2': {'kind': 'data', 'shape': [1, 26]}, + 'crop3': {'kind': 'op', 'op': 'Crop', 'offset': 52, 'dim': 26, 'axis': -1}, + 'crop_data_3': {'kind': 'data', 'shape': [1, 26]}, + 'crop4': {'kind': 'op', 'op': 'Crop', 'offset': 78, 'dim': 26, 'axis': -1}, + 'crop_data_4': {'kind': 'data', 'shape': [1, 26]}, + 'crop5': {'kind': 'op', 'op': 'Crop', 'offset': 104, 'dim': 26, 'axis': -1}, + 'crop_data_5': {'kind': 'data', 'shape': [1, 26]}, + 'concat': {'kind': 'op', 'op': 'Concat'}, + 'concat_data': {'kind': 'data', 'shape': [1, 130]}, 'placeholder': {'kind': 'op', 'op': 'Parameter'}, }, [ ('placeholder_in', 'in_node'), + ('in_node', 'crop1'), ('crop1', 'crop_data_1'), + ('in_node', 'crop2'), ('crop2', 'crop_data_2'), + ('in_node', 'crop3'), ('crop3', 'crop_data_3'), + ('in_node', 'crop4'), ('crop4', 'crop_data_4'), + ('in_node', 'crop5'), ('crop5', 'crop_data_5'), + ('concat', 'concat_data'), ('in_node', 'placeholder') ] ) @@ -121,3 +139,72 @@ def test_useful_crops(self): ) (flag, resp) = compare_graphs(graph, ref_graph, 'placeholder') self.assertTrue(flag, resp) + + def test_useless_crops_without_concat(self): + graph = build_graph({'placeholder_in': {'kind': 'op', 'op': 'Parameter'}, + 'in_node': {'kind': 'data', 'shape': [1, 130]}, + 'crop1': {'kind': 'op', 'op': 'Crop', 'offset': 0, 'dim': 26, 'axis': -1}, + 'crop_data_1': {'kind': 'data', 'shape': [1, 26]}, + 'crop2': {'kind': 'op', 'op': 'Crop', 'offset': 26, 'dim': 26, 'axis': -1}, + 'crop_data_2': {'kind': 'data', 'shape': [1, 26]}, + 'crop3': {'kind': 'op', 'op': 'Crop', 'offset': 52, 'dim': 26, 'axis': -1}, + 'crop_data_3': {'kind': 'data', 'shape': [1, 26]}, + 'crop4': {'kind': 'op', 'op': 'Crop', 'offset': 78, 'dim': 26, 'axis': -1}, + 'crop_data_4': {'kind': 'data', 'shape': [1, 26]}, + 'crop5': {'kind': 'op', 'op': 'Crop', 'offset': 104, 'dim': 26, 'axis': -1}, + 'crop_data_5': {'kind': 'data', 'shape': [1, 26]}, + 'placeholder_concat': {'kind': 'op', 'op': None}, + 'placeholder_concat_data': {'kind': 'data', 'shape': [1, 100]}, + 'concat': {'kind': 'op', 'op': 'Concat'}, + 'concat_data': {'kind': 'data', 'shape': [1, 230]}, + 'placeholder': {'kind': 'op', 'op': None}, + }, + [('placeholder_in', 'in_node'), + ('in_node', 'crop1'), ('crop1', 'crop_data_1'), + ('in_node', 'crop2'), ('crop2', 'crop_data_2'), + ('in_node', 'crop3'), ('crop3', 'crop_data_3'), + ('in_node', 'crop4'), ('crop4', 'crop_data_4'), + ('in_node', 'crop5'), ('crop5', 'crop_data_5'), + ('placeholder_concat', 'placeholder_concat_data'), + ('crop_data_1', 'concat', {'in': 0}), + ('crop_data_2', 'concat', {'in': 1}), + ('crop_data_3', 'concat', {'in': 2}), + ('crop_data_4', 'concat', {'in': 3}), + ('crop_data_5', 'concat', {'in': 4}), + ('placeholder_concat_data', 'concat', {'in': 5}), + ('concat', 'concat_data'), + ('concat_data', 'placeholder')]) + RemoveUselessCropsPattern().find_and_replace_pattern(graph) + ref_graph = build_graph({'placeholder_in': {'kind': 'op', 'op': 'Parameter'}, + 'in_node': {'kind': 'data', 'shape': [1, 130]}, + 'crop1': {'kind': 'op', 'op': 'Crop', 'offset': 0, 'dim': 26, 'axis': -1}, + 'crop_data_1': {'kind': 'data', 'shape': [1, 26]}, + 'crop2': {'kind': 'op', 'op': 'Crop', 'offset': 26, 'dim': 26, 'axis': -1}, + 'crop_data_2': {'kind': 'data', 'shape': [1, 26]}, + 'crop3': {'kind': 'op', 'op': 'Crop', 'offset': 52, 'dim': 26, 'axis': -1}, + 'crop_data_3': {'kind': 'data', 'shape': [1, 26]}, + 'crop4': {'kind': 'op', 'op': 'Crop', 'offset': 78, 'dim': 26, 'axis': -1}, + 'crop_data_4': {'kind': 'data', 'shape': [1, 26]}, + 'crop5': {'kind': 'op', 'op': 'Crop', 'offset': 104, 'dim': 26, 'axis': -1}, + 'crop_data_5': {'kind': 'data', 'shape': [1, 26]}, + 'placeholder_concat': {'kind': 'op', 'op': None}, + 'placeholder_concat_data': {'kind': 'data', 'shape': [1, 100]}, + 'concat': {'kind': 'op', 'op': 'Concat'}, + 'concat_data': {'kind': 'data', 'shape': [1, 230]}, + 'placeholder': {'kind': 'op', 'op': 'Parameter'}, + }, + [ + ('placeholder_in', 'in_node'), + ('in_node', 'crop1'), ('crop1', 'crop_data_1'), + ('in_node', 'crop2'), ('crop2', 'crop_data_2'), + ('in_node', 'crop3'), ('crop3', 'crop_data_3'), + ('in_node', 'crop4'), ('crop4', 'crop_data_4'), + ('in_node', 'crop5'), ('crop5', 'crop_data_5'), + ('placeholder_concat', 'placeholder_concat_data'), + ('in_node', 'concat', {'in': 4}), + ('placeholder_concat_data', 'concat', {'in': 5}), + ('concat', 'concat_data'), + ('concat_data', 'placeholder')]) + + (flag, resp) = compare_graphs(graph, ref_graph, 'placeholder') + self.assertTrue(flag, resp) diff --git a/model-optimizer/extensions/middle/ReplaceMemoryOffsetWithSplice.py b/model-optimizer/extensions/middle/ReplaceMemoryOffsetWithSplice.py index 043a2e6ed1eb10..dfddc337138736 100644 --- a/model-optimizer/extensions/middle/ReplaceMemoryOffsetWithSplice.py +++ b/model-optimizer/extensions/middle/ReplaceMemoryOffsetWithSplice.py @@ -46,53 +46,55 @@ def replace_pattern(graph: Graph, match: dict): if pair_node.has_default: return - if len(node.in_nodes()) != 0: - input_node = node.in_node(0) - op_output = node.out_node().out_node() - out_node = pair_node.out_node(0) + if node.in_port(0).get_source() is not None: + input_node_out_port = node.in_port(0).get_source() + op_output_id = node.out_port(0).get_destination().node.id + out_node_in_ports = pair_node.out_port(0).get_destinations() else: - input_node = pair_node.in_node(0) - op_output = pair_node.out_node().out_node() - out_node = node.out_node(0) + input_node_out_port = pair_node.in_port(0).get_source() + op_output_id = pair_node.out_port(0).get_destination().node.id + out_node_in_ports = node.out_port(0).get_destinations() - in_shape = input_node.shape + in_shape = input_node_out_port.data.get_shape().copy() node_id = node.id node_name = node.name node_t = node.t - graph.remove_node(op_output.id) - graph.remove_node(node.id) - graph.remove_node(pair_node.id) - splice = Splice(graph, {'name': node_name, 'id': node_id, - 'context': int64_array(range(-abs(node_t), abs(node_t) + 1))}).create_node([input_node]) + 'context': int64_array(range(node_t, 1)) if node_t < 0 else int64_array(range(0, node_t+1))}).create_node() + splice.in_port(0).connect(input_node_out_port) - # offset of Crop will be 0 (first element) if node_t < 0 and in_shape[1]*2*node_t (last element) if node_t > 0 + # offset of Crop will be 0 (first element) if node_t < 0 and in_shape[1]*node_t (last element) if node_t > 0 crop = Crop(graph, {'name': 'Splice_Crop', 'axis': int64_array([1]), - 'offset': int64_array([max(0, in_shape[1] * 2 * node_t)]), + 'offset': int64_array([max(0, in_shape[1] * node_t)]), 'dim': int64_array([in_shape[1]])}).create_node() splice.out_port(0).connect(crop.in_port(0)) - splice.out_node(0).shape = int64_array([in_shape[0], (2 * abs(node_t) + 1) * in_shape[1]]) + splice.out_port(0).data.set_shape(int64_array([in_shape[0], (abs(node_t) + 1) * in_shape[1]])) - outs = input_node.out_nodes() - for out_ in outs: + outs = input_node_out_port.get_destinations() + for in_port in outs: + out_ = in_port.node if out_['op'] != 'MemoryOffset' and out_['op'] != 'Splice': crop_input = Crop(graph, {'name': 'Splice_Crop', 'axis': int64_array([1]), - 'offset': int64_array([input_node.shape[1] * abs(node_t)]), - 'dim': int64_array([input_node.shape[1]])}).create_node() + 'offset': int64_array([-min(0, in_shape[1] * node_t)]), + 'dim': int64_array([in_shape[1]])}).create_node() splice.out_port(0).connect(crop_input.in_port(0)) - in_port = graph.get_edge_data(input_node.id, out_.id)[0]['in'] - graph.remove_edge(input_node.id, out_.id) - crop_input.out_port(0).connect(out_.in_port(in_port)) - crop_input.out_node(0).shape = input_node.shape + in_port.disconnect() + crop_input.out_port(0).connect(in_port) + crop_input.out_port(0).data.set_shape(in_shape) - graph.add_edge(crop.id, out_node.id, **{'in': 0, 'out': 0}) + for dest_port in out_node_in_ports: + dest_port.connect(crop.out_port(0)) + + graph.remove_node(op_output_id) + graph.remove_node(node.id) + graph.remove_node(pair_node.id) class ReplaceMemoryOffsetWithMemoryNodePattern(MiddleReplacementPattern): @@ -115,15 +117,15 @@ def replace_pattern(graph: Graph, match: dict): if node.t >= 0: raise Error('Does not support IfDefined with t > 0') - if len(node.in_nodes()) != 0: + if node.in_port(0).get_source() is not None: input_port = node.in_port(0).get_source() - op_output = node.out_node().out_node() + op_output_id = node.out_port(0).get_destination().node.id out_port = pair_node.out_port(0) node_name = node.name pair_name = pair_node.name else: input_port = pair_node.in_port(0).get_source() - op_output = pair_node.out_node().out_node() + op_output_id = pair_node.out_port(0).get_destination().node.id out_port = node.out_port(0) node_name = pair_node.name pair_name = node.name @@ -169,6 +171,6 @@ def replace_pattern(graph: Graph, match: dict): out_port.get_connection().set_source(memory_out.out_port(0)) memory_out.out_port(0).data.set_shape(np.array([in_shape[0], memory_out.shape[0]])) - graph.remove_node(op_output.id) + graph.remove_node(op_output_id) graph.remove_node(node.id) graph.remove_node(pair_node.id) diff --git a/model-optimizer/extensions/middle/ReplaceMemoryOffsetWithSplice_test.py b/model-optimizer/extensions/middle/ReplaceMemoryOffsetWithSplice_test.py index 57d34b658a97e0..9d3c406724566f 100644 --- a/model-optimizer/extensions/middle/ReplaceMemoryOffsetWithSplice_test.py +++ b/model-optimizer/extensions/middle/ReplaceMemoryOffsetWithSplice_test.py @@ -30,8 +30,10 @@ def setUpClass(cls): 'pair_name': 'memoryoffset_2', 'has_default': False}, 'memoryoffset_data': {'kind': 'data', 'shape': [1, 13]}, 'memoryoffset_2': {'kind': 'op', 'op': 'MemoryOffset', 't': -5, - 'pair_name': 'memoryoffset', 'has_default': False}, + 'pair_name': 'memoryoffset', 'has_default': False, + 'in_ports_count': 1}, 'memoryoffset_2_data': {'kind': 'data', 'shape': [1, 13]}, + 'crop_data': {'kind': 'data', 'shape': [1, 13]}, 'out_placeholder': {'kind': 'op', 'op': 'placeholder'}, 'opoutput': {'kind': 'op', 'op': 'OpOutput'}, } @@ -49,10 +51,10 @@ def test_memoryoffset_pos(self): ReplaceMemoryOffsetNodePattern().find_and_replace_pattern(graph) ref_graph = build_graph({'in_placeholder': {'kind': 'op', 'op': 'placeholder'}, 'in_node': {'kind': 'data', 'shape': [1, 13]}, - 'splice': {'kind': 'op', 'op': 'Splice', 'context': range(-5, 6)}, - 'splice_data': {'kind': 'data', 'shape': [1, 143]}, + 'splice': {'kind': 'op', 'op': 'Splice', 'context': range(0, 6)}, + 'splice_data': {'kind': 'data', 'shape': [1, 78]}, 'crop': {'kind': 'op', 'op': 'Crop', 'offset': 130, 'dim': 13}, - 'memoryoffset_2_data': {'kind': 'data', 'shape': [1, 13]}, + 'crop_data': {'kind': 'data', 'shape': [1, 13]}, 'out_placeholder': {'kind': 'op', 'op': 'placeholder'}, }, [ @@ -60,12 +62,12 @@ def test_memoryoffset_pos(self): ('in_node', 'splice'), ('splice', 'splice_data'), ('splice_data', 'crop'), - ('crop', 'memoryoffset_2_data'), - ('memoryoffset_2_data', 'out_placeholder') + ('crop', 'crop_data'), + ('crop_data', 'out_placeholder') ] ) - (flag, resp) = compare_graphs(graph, ref_graph, 'memoryoffset_2_data') + (flag, resp) = compare_graphs(graph, ref_graph, 'out_placeholder') self.assertTrue(flag, resp) def test_memoryoffset_neg(self): @@ -81,8 +83,8 @@ def test_memoryoffset_neg(self): ReplaceMemoryOffsetNodePattern().find_and_replace_pattern(graph) ref_graph = build_graph({'in_placeholder': {'kind': 'op', 'op': 'placeholder'}, 'in_node': {'kind': 'data', 'shape': [1, 13]}, - 'splice': {'kind': 'op', 'op': 'Splice', 'context': range(-5, 6)}, - 'splice_data': {'kind': 'data', 'shape': [1, 143]}, + 'splice': {'kind': 'op', 'op': 'Splice', 'context': range(-5, 1)}, + 'splice_data': {'kind': 'data', 'shape': [1, 78]}, 'crop': {'kind': 'op', 'op': 'Crop', 'offset': 0, 'dim': 13}, 'memoryoffset_2_data': {'kind': 'data', 'shape': [1, 13]}, 'out_placeholder': {'kind': 'op', 'op': 'placeholder'}, @@ -113,8 +115,8 @@ def test_memoryoffset_neg_0(self): ReplaceMemoryOffsetNodePattern().find_and_replace_pattern(graph) ref_graph = build_graph({'in_placeholder': {'kind': 'op', 'op': 'placeholder'}, 'in_node': {'kind': 'data', 'shape': [1, 13]}, - 'splice': {'kind': 'op', 'op': 'Splice', 'context': range(-5, 6)}, - 'splice_data': {'kind': 'data', 'shape': [1, 143]}, + 'splice': {'kind': 'op', 'op': 'Splice', 'context': range(-5, 1)}, + 'splice_data': {'kind': 'data', 'shape': [1, 78]}, 'crop': {'kind': 'op', 'op': 'Crop', 'offset': 0, 'dim': 13}, 'crop_input': {'kind': 'op', 'op': 'Crop', 'offset': 65, 'dim': 13}, 'crop_input_data': {'kind': 'data', 'shape': [1, 13]}, diff --git a/model-optimizer/extensions/middle/ReplacePNorm.py b/model-optimizer/extensions/middle/ReplacePNorm.py new file mode 100644 index 00000000000000..065f0855980c90 --- /dev/null +++ b/model-optimizer/extensions/middle/ReplacePNorm.py @@ -0,0 +1,62 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from extensions.ops.ReduceOps import ReduceSum +from mo.front.common.partial_infer.utils import int64_array +from mo.front.tf.graph_utils import create_op_node_with_second_input +from mo.graph.graph import Graph +from mo.middle.replacement import MiddleReplacementPattern +from mo.ops.power import Power +from mo.ops.reshape import Reshape + + +class ReplacePNormNodePattern(MiddleReplacementPattern): + """ + PNorm operation should be replaced by operations: Power(P) -> Reshape(n,c*g->n,g,c)-> ReduceSum(axis=1)-> Power(1/P) + """ + enabled = False + + @staticmethod + def pattern(): + return dict( + nodes=[('op', dict(op='pnorm'))], + edges=[]) + + @staticmethod + def replace_pattern(graph: Graph, match: dict): + node = match['op'] + shape = node.in_port(0).data.get_shape().copy() + + assert shape[1] % node.group == 0 + + power_node = Power(graph, attrs={'name': node.id + '_power', + 'power': node.p}).create_node() + + reshape_node = create_op_node_with_second_input(graph, Reshape, + int64_array([shape[0], shape[1] / node.group, node.group]), + {'name': node.id + '_reshape'}) + reshape_node.in_port(0).connect(power_node.out_port(0)) + + reducesum_node = create_op_node_with_second_input(graph, ReduceSum, + int64_array([2]), + {'name': node.id + '_sum', 'keep_dims': False}) + reducesum_node.in_port(0).connect(reshape_node.out_port(0)) + + invpower_node = Power(graph, attrs={'name': node.id + '_invpower', + 'power': 1.0 / node.p}).create_node() + invpower_node.in_port(0).connect(reducesum_node.out_port(0)) + + node.in_port(0).get_connection().set_destination(power_node.in_port(0)) + node.out_port(0).get_connection().set_source(invpower_node.out_port(0)) diff --git a/model-optimizer/extensions/middle/ReplacePNormNodePattern_test.py b/model-optimizer/extensions/middle/ReplacePNormNodePattern_test.py new file mode 100644 index 00000000000000..c5d1a45131c309 --- /dev/null +++ b/model-optimizer/extensions/middle/ReplacePNormNodePattern_test.py @@ -0,0 +1,76 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import unittest + +from extensions.middle.ReplacePNorm import ReplacePNormNodePattern +from mo.utils.unittest.graph import build_graph, compare_graphs + + +class ReplacePNormNodePatternTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.nodes_attributes = { + 'placeholder': {'kind': 'op', 'op': None}, + 'in_node': {'kind': 'data', 'shape': [1, 3500]}, + 'pnorm': {'kind': 'op', 'op': 'pnorm', 'group': 10, 'p': 2.0}, + 'pnorm_data': {'kind': 'data', 'shape': [1, 350]}, + 'out_placeholder': {'kind': 'op', 'op': 'placeholder'}, + } + + def test_pnorm(self): + graph = build_graph(self.nodes_attributes, + [('placeholder', 'in_node'), + ('in_node', 'pnorm'), + ('pnorm', 'pnorm_data'), + ('pnorm_data', 'out_placeholder')]) + ReplacePNormNodePattern().find_and_replace_pattern(graph) + + ref_graph = build_graph({'in_placeholder': {'kind': 'op', 'op': None}, + 'in_node': {'kind': 'data', 'shape': [1, 3500]}, + 'power': {'kind': 'op', 'op': 'Power', 'power': 2.0}, + 'power_data': {'kind': 'data'}, + 'reshape': {'kind': 'op', 'op': 'Reshape'}, + 'reshape_data': {'kind': 'data'}, + 'const': {'kind': 'op', 'op': 'Const', 'value': [1, 350, 10]}, + 'const_data': {'kind': 'data'}, + 'reduce': {'kind': 'op', 'op': 'ReduceSum'}, + 'reduce_data': {'kind': 'data'}, + 'const_1': {'kind': 'op', 'op': 'Const', 'value': 2}, + 'const_data_1': {'kind': 'data'}, + 'invpower': {'kind': 'op', 'op': 'Power', 'power': 0.5}, + 'invpower_data': {'kind': 'data'}, + 'out_placeholder': {'kind': 'op', 'op': 'placeholder'}, + }, + [ + ('in_placeholder', 'in_node'), + ('in_node', 'power'), + ('power', 'power_data'), + ('power_data', 'reshape', {'in': 0}), + ('reshape', 'reshape_data'), + ('const', 'const_data'), + ('const_data', 'reshape', {'in': 1}), + ('reshape_data', 'reduce', {'in': 0}), + ('const_1', 'const_data_1'), + ('const_data_1', 'reduce', {'in': 1}), + ('reduce', 'reduce_data'), + ('reduce_data', 'invpower'), + ('invpower', 'invpower_data'), + ('invpower_data', 'out_placeholder'), + ] + ) + + (flag, resp) = compare_graphs(graph, ref_graph, 'out_placeholder') + self.assertTrue(flag, resp) diff --git a/model-optimizer/extensions/middle/ReplaceSpliceNodePattern.py b/model-optimizer/extensions/middle/ReplaceSpliceNodePattern.py index 935174ac6b1060..39dbe4af3c36a6 100644 --- a/model-optimizer/extensions/middle/ReplaceSpliceNodePattern.py +++ b/model-optimizer/extensions/middle/ReplaceSpliceNodePattern.py @@ -13,9 +13,9 @@ See the License for the specific language governing permissions and limitations under the License. """ -import numpy as np - from extensions.front.kaldi.replace_lstm_node_pattern import unique_id +from extensions.ops.splitv import SplitV +from mo.front.common.partial_infer.utils import int64_array from mo.graph.graph import Graph from mo.middle.replacement import MiddleReplacementPattern from mo.ops.concat import Concat @@ -55,11 +55,9 @@ def pattern(): @staticmethod def replace_pattern(graph: Graph, match: dict): node = match['op'] - input_node = node.in_nodes()[0] - out_node = node.out_node(0) - - graph.remove_edge(input_node.id, node.id) - graph.remove_edge(node.id, out_node.id) + in_shape = node.in_port(0).data.get_shape().copy() + memory_element = in_shape[1] - node.const_dim + memory_size = memory_element * len(node.context) memory_pair_id = unique_id('id') # Memory(in) @@ -67,33 +65,81 @@ def replace_pattern(graph: Graph, match: dict): 'id': memory_pair_id, 'index': 1, 'size': 2, - 'shape': np.array(([input_node.shape[1] * len(node.context)]), - dtype=np.int64)}).create_node_with_data() + 'shape': int64_array([memory_size])}).create_node() # Memory(in) \ # Crop # Input(temp) / crop = Crop(graph, {'name': 'Splice_Crop', - 'axis': np.array([1], dtype=np.int64), - 'offset': np.array([input_node.shape[1]], dtype=np.int64), - 'dim': np.array([input_node.shape[1] * (len(node.context) - 1)], - dtype=np.int64)}).create_node_with_data([input_memory]) + 'axis': int64_array([1]), + 'offset': int64_array([memory_element]), + 'dim': int64_array([memory_size - memory_element])}).create_node() + crop.in_port(0).connect(input_memory.out_port(0)) # Crop \ # Concat # Input / concat_node = Concat(graph, {'name': 'Splice_Concat', 'in_ports_count': 2, - 'axis': 1}).create_node([crop, input_node]) + 'axis': 1}).create_node() + concat_node.in_port(0).connect(crop.out_port(0)) # Concat -> Memory(out) mem_out = Memory(graph, {'name': 'out_splice_memory', 'id': memory_pair_id, 'index': 0, 'size': 2, - 'shape': np.array([input_node.shape[1] * len(node.context)], dtype=np.int64)}).create_node_with_data() - - Result(graph).create_node([mem_out]) - - graph.add_edge(concat_node.id, out_node.id, **{'in': 0, 'out': 0}) - out_node.add_output_port(1) - graph.add_edge(out_node.id, mem_out.in_node(0).id, **{'in': 0, 'out': 1}) + 'shape': int64_array([memory_size])}).create_node() + mem_out.in_port(0).connect(concat_node.out_port(0)) + Result(graph).create_node().in_port(0).connect(mem_out.out_port(0)) + + if node.const_dim != 0: + memory_element_constdim = node.const_dim + memory_size_constdim = memory_element_constdim * len(node.context) + split = SplitV(graph, {'name': node.id + '_split_const', 'axis': 1, 'out_ports_count': 2, + 'size_splits': int64_array([memory_element, memory_element_constdim])}).create_node() + split.out_port(0).connect(concat_node.in_port(1)) + + # create separate splice construction for const_dim + memory_pair_id = unique_id('memory_for_const_dim') + input_memory_const_dim = Memory(graph, {'name': 'const_dim_in_memory', + 'id': memory_pair_id, + 'index': 1, + 'size': 2, + 'shape': int64_array([memory_size_constdim])}).create_node() + crop_const_dim = Crop(graph, {'name': 'const_dim_crop', + 'axis': int64_array([1]), + 'offset': int64_array([memory_element_constdim]), + 'dim': int64_array([memory_size_constdim - memory_element_constdim])}).create_node() + crop_const_dim.in_port(0).connect(input_memory_const_dim.out_port(0)) + + concat_node_const_dim = Concat(graph, {'name': 'const_dim_concat', + 'in_ports_count': 2, + 'axis': 1}).create_node() + concat_node_const_dim.in_port(0).connect(crop_const_dim.out_port(0)) + + mem_out_const_dim = Memory(graph, {'name': 'const_dim_out_memory', + 'id': memory_pair_id, + 'index': 0, + 'size': 2, + 'shape': int64_array([memory_size_constdim])}).create_node() + mem_out_const_dim.in_port(0).connect(concat_node_const_dim.out_port(0)) + Result(graph).create_node().in_port(0).connect(mem_out_const_dim.out_port(0)) + + # connect splice to Split as begin and Concat as the end + split.out_port(1).connect(concat_node_const_dim.in_port(1)) + crop_first = Crop(graph, {'name': 'const_dim_crop_first', + 'axis': int64_array([1]), + 'offset': int64_array([0]), + 'dim': int64_array([memory_element_constdim])}).create_node() + crop_first.in_port(0).connect(concat_node_const_dim.out_port(0)) + + concat_const = Concat(graph, {'name': node.id+'_concat_const', 'axis': 1, + 'in_ports_count': 2}).create_node() + concat_const.in_port(1).connect(crop_first.out_port(0)) + concat_const.in_port(0).connect(concat_node.out_port(0)) + + node.in_port(0).get_connection().set_destination(split.in_port(0)) + node.out_port(0).get_connection().set_source(concat_const.out_port(0)) + else: + node.in_port(0).get_connection().set_destination(concat_node.in_port(1)) + node.out_port(0).get_connection().set_source(concat_node.out_port(0)) diff --git a/model-optimizer/extensions/middle/ReplaceSpliceNodePattern_test.py b/model-optimizer/extensions/middle/ReplaceSpliceNodePattern_test.py index ca403364997e95..4689784d945484 100644 --- a/model-optimizer/extensions/middle/ReplaceSpliceNodePattern_test.py +++ b/model-optimizer/extensions/middle/ReplaceSpliceNodePattern_test.py @@ -17,43 +17,129 @@ from extensions.middle.ReplaceSpliceNodePattern import ReplaceSpliceNodePattern from mo.graph.graph import Node -from mo.utils.unittest.graph import build_graph +from mo.utils.unittest.graph import build_graph, compare_graphs class ReplaceSpliceNodePatternTests(unittest.TestCase): @classmethod def setUpClass(cls): cls.nodes_attributes = { + 'placeholder': {'kind': 'op', 'op': None}, 'in_node': {'kind': 'data', 'shape': [1, 13]}, - 'slice': {'kind': 'op', 'op': 'Splice', 'context': range(-5, 5)}, + 'splice': {'kind': 'op', 'op': 'Splice', 'context': range(-5, 6), 'const_dim': 0}, 'splice_data': {'kind': 'data', 'shape': [1, 143]}, + 'out_placeholder': {'kind': 'op', 'op': 'placeholder'}, } - cls.graph = build_graph(cls.nodes_attributes, - [('in_node', 'slice'), - ('slice', 'splice_data')]) - - ReplaceSpliceNodePattern().find_and_replace_pattern(cls.graph) - - def test_memory(self): - memory_nodes = [node for node in self.graph.nodes(data=True) if node[1]['kind'] == 'op' and node[1]['op'] == 'Memory'] - self.assertEqual(len(memory_nodes), 2) - for memory_node in memory_nodes: - node = Node(self.graph, memory_node[0]) - if len(node.in_nodes()): - self.assertEqual(node.index, 0) - elif len(node.out_nodes()): - self.assertEqual(node.index, 1) - self.assertEqual(memory_nodes[0][1]['id'], memory_nodes[1][1]['id']) - - def test_crop(self): - crop_node = [node for node in self.graph.nodes(data=True) if node[1]['kind'] == 'op' and node[1]['op'] == 'Crop'] - self.assertEqual(len(crop_node), 1) - crop_node = Node(self.graph, crop_node[0][0]) - self.assertEqual(crop_node.offset, [13]) - self.assertEqual(crop_node.dim, [13 * 9]) - - def test_concat(self): - concat_node = [node for node in self.graph.nodes(data=True) if node[1]['kind'] == 'op' and node[1]['op'] == 'Concat'] - self.assertEqual(len(concat_node), 1) - crop_node = Node(self.graph, concat_node[0][0]) - self.assertEqual(crop_node.axis, 1) + + def test_splice(self): + graph = build_graph(self.nodes_attributes, + [('placeholder', 'in_node'), + ('in_node', 'splice'), + ('splice', 'splice_data'), + ('splice_data', 'out_placeholder')]) + ReplaceSpliceNodePattern().find_and_replace_pattern(graph) + + ref_graph = build_graph({'in_placeholder': {'kind': 'op', 'op': None}, + 'in_node': {'kind': 'data', 'shape': [1, 13]}, + 'memory_in': {'kind': 'op', 'op': 'Memory'}, + 'memory_in_data': {'kind': 'data'}, + 'crop_mem': {'kind': 'op', 'op': 'Crop', 'offset': 13, 'dim': 130}, + 'crop_mem_data': {'kind': 'data'}, + 'concat': {'kind': 'op', 'op': 'Concat'}, + 'concat_data': {'kind': 'data', 'shape': [1, 143]}, + 'memory_out': {'kind': 'op', 'op': 'Memory'}, + 'memory_out_data': {'kind': 'data'}, + 'result': {'kind': 'op', 'op': 'Result'}, + 'out_placeholder': {'kind': 'op', 'op': 'placeholder'}, + }, + [ + ('in_placeholder', 'in_node'), + ('memory_in', 'memory_in_data'), + ('memory_in_data', 'crop_mem'), + ('crop_mem', 'crop_mem_data'), + ('crop_mem_data', 'concat', {'in': 0}), + ('in_node', 'concat', {'in': 1}), + ('concat', 'concat_data'), + ('concat_data', 'memory_out'), + ('memory_out', 'memory_out_data'), + ('memory_out_data', 'result'), + ('concat_data', 'out_placeholder'), + ] + ) + + (flag, resp) = compare_graphs(graph, ref_graph, 'out_placeholder') + self.assertTrue(flag, resp) + + def test_splice_with_constdim(self): + graph = build_graph(self.nodes_attributes, + [('placeholder', 'in_node'), + ('in_node', 'splice'), + ('splice', 'splice_data'), + ('splice_data', 'out_placeholder')]) + Node(graph, 'splice')['const_dim'] = 10 + Node(graph, 'splice_data')['shape'] = [1, 43] + ReplaceSpliceNodePattern().find_and_replace_pattern(graph) + + ref_graph = build_graph({'in_placeholder': {'kind': 'op', 'op': None}, + 'in_node': {'kind': 'data', 'shape': [1, 13]}, + 'split': {'kind': 'op', 'op': 'Split'}, + 'split_data_0': {'kind': 'data'}, + 'split_data_1': {'kind': 'data'}, + 'memory_in': {'kind': 'op', 'op': 'Memory'}, + 'memory_in_data': {'kind': 'data'}, + 'crop_mem': {'kind': 'op', 'op': 'Crop', 'offset': 3, 'dim': 30}, + 'crop_mem_data': {'kind': 'data'}, + 'concat': {'kind': 'op', 'op': 'Concat'}, + 'concat_data': {'kind': 'data'}, + 'memory_out': {'kind': 'op', 'op': 'Memory'}, + 'memory_out_data': {'kind': 'data'}, + 'result': {'kind': 'op', 'op': 'Result'}, + 'memory_in_constdims': {'kind': 'op', 'op': 'Memory'}, + 'memory_in_constdims_data': {'kind': 'data'}, + 'crop_mem_constdims': {'kind': 'op', 'op': 'Crop', 'offset': 10, 'dim': 100}, + 'crop_mem_constdims_data': {'kind': 'data'}, + 'concat_constdims': {'kind': 'op', 'op': 'Concat'}, + 'concat_constdims_data': {'kind': 'data'}, + 'memory_out_constdims': {'kind': 'op', 'op': 'Memory'}, + 'memory_out_constdims_data': {'kind': 'data'}, + 'result_constdims': {'kind': 'op', 'op': 'Result'}, + 'crop_first_constdims': {'kind': 'op', 'op': 'Crop', 'offset': 0, 'dim': 10}, + 'crop_first_constdims_data': {'kind': 'data'}, + 'concat_all': {'kind': 'op', 'op': 'Concat'}, + 'concat_all_data': {'kind': 'data', 'shape': [1, 43]}, + 'out_placeholder': {'kind': 'op', 'op': 'placeholder'}, + }, + [ + ('in_placeholder', 'in_node'), + ('in_node', 'split'), + ('split', 'split_data_0', {'out': 0}), + ('split', 'split_data_1', {'out': 1}), + ('memory_in', 'memory_in_data'), + ('memory_in_data', 'crop_mem'), + ('crop_mem', 'crop_mem_data'), + ('crop_mem_data', 'concat', {'in': 0}), + ('split_data_0', 'concat', {'in': 1}), + ('concat', 'concat_data'), + ('concat_data', 'memory_out'), + ('memory_out', 'memory_out_data'), + ('memory_out_data', 'result'), + ('memory_in_constdims', 'memory_in_constdims_data'), + ('memory_in_constdims_data', 'crop_mem_constdims'), + ('crop_mem_constdims', 'crop_mem_constdims_data'), + ('crop_mem_constdims_data', 'concat_constdims', {'in': 0}), + ('split_data_1', 'concat_constdims', {'in': 1}), + ('concat_constdims', 'concat_constdims_data'), + ('concat_constdims_data', 'memory_out_constdims'), + ('memory_out_constdims', 'memory_out_constdims_data'), + ('memory_out_constdims_data', 'result_constdims'), + ('concat_constdims_data', 'crop_first_constdims'), + ('crop_first_constdims', 'crop_first_constdims_data'), + ('crop_first_constdims_data', 'concat_all', {'in': 1}), + ('concat_data', 'concat_all', {'in': 0}), + ('concat_all', 'concat_all_data'), + ('concat_all_data', 'out_placeholder'), + ] + ) + + (flag, resp) = compare_graphs(graph, ref_graph, 'out_placeholder') + self.assertTrue(flag, resp) diff --git a/model-optimizer/extensions/middle/SliceConvert_test.py b/model-optimizer/extensions/middle/SliceConvert_test.py index 7d660f73b1a830..112995f1b3130d 100644 --- a/model-optimizer/extensions/middle/SliceConvert_test.py +++ b/model-optimizer/extensions/middle/SliceConvert_test.py @@ -18,6 +18,7 @@ import numpy as np from extensions.middle.SliceConverter import ConvertSlice +from mo.front.common.partial_infer.utils import int64_array from mo.graph.graph import Node from mo.utils.unittest.graph import build_graph, compare_graphs from mo.ops.slice import Slice @@ -176,3 +177,204 @@ def test_3(self): (flag, resp) = compare_graphs(graph, graph_ref, 'output_op', check_op_attrs=True) self.assertTrue(flag, resp) + + +class ConvertSliceONNXOpset10Tests(unittest.TestCase): + nodes_attributes = { + # input data + 'placeholder_1': {'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'}, + 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None}, + # Slice layer inputs + 'starts': {'type': 'Const', 'kind': 'op', 'op': 'Const'}, + 'starts_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None}, + 'ends': {'type': 'Const', 'kind': 'op', 'op': 'Const'}, + 'ends_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None}, + 'strides': {'type': 'Const', 'kind': 'op', 'op': 'Const'}, + 'strides_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None}, + 'axes': {'type': 'Const', 'kind': 'op', 'op': 'Const'}, + 'axes_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None}, + 'steps': {'type': 'Const', 'kind': 'op', 'op': 'Const'}, + 'steps_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None}, + # Slice layer + 'slice': {'type': 'Slice', 'kind': 'op', 'op': 'Slice', 'format': 'onnx', 'end': None}, + 'slice_data': {'value': None, 'shape': None, 'kind': 'data'}, + # Output operation + 'output_op': {'type': 'Const', 'kind': 'op', 'op': 'Const'}, + 'output_data': {'shape': None, 'kind': 'data', 'data_type': None}, + 'op_output': {'kind': 'op', 'op': 'Result'}, + # StridedSlice layer + 'strided_slice': {'kind': 'op', 'op': 'StridedSlice', 'slices': None, 'shrink_axis_mask': None} + } + + def test_no_steps_no_axes(self): + input_shape = int64_array([5, 10, 20]) + starts_value = int64_array([3, 2, 7]) + ends_value = int64_array([5, 8, 15]) + steps_value = int64_array([1, 1, 1]) + masks_value = np.zeros([len(input_shape)], dtype=np.int64) + graph = build_graph(self.nodes_attributes, + [('placeholder_1', 'placeholder_1_data'), + ('placeholder_1_data', 'slice', {'in': 0}), + ('starts', 'starts_data'), + ('starts_data', 'slice', {'in': 1}), + ('ends', 'ends_data'), + ('ends_data', 'slice', {'in': 2}), + ('slice', 'slice_data'), + ('slice_data', 'output_op'), + ('output_op', 'output_data'), + ('output_data', 'op_output') + ], + {'placeholder_1_data': {'shape': input_shape}, + 'starts': {'shape': starts_value.shape, 'value': starts_value}, + 'starts_data': {'shape': starts_value.shape, 'value': starts_value}, + 'ends': {'shape': ends_value.shape, 'value': ends_value}, + 'ends_data': {'shape': ends_value.shape, 'value': ends_value}, + }, nodes_with_edges_only=True + ) + slice_node = Node(graph, 'slice') + Slice.infer(slice_node) + + pattern = ConvertSlice() + pattern.find_and_replace_pattern(graph) + + graph_ref = build_graph(self.nodes_attributes, + [('placeholder_1', 'placeholder_1_data'), + ('placeholder_1_data', 'strided_slice', {'in': 0}), + ('starts', 'starts_data'), + ('starts_data', 'strided_slice', {'in': 1}), + ('ends', 'ends_data'), + ('ends_data', 'strided_slice', {'in': 2}), + ('strides', 'strides_data'), + ('strides_data', 'strided_slice', {'in': 3}), + ('strided_slice', 'slice_data'), + ('slice_data', 'output_op'), + ('output_op', 'output_data'), + ('output_data', 'op_output') + ], + {'placeholder_1_data': {'shape': input_shape}, + 'strided_slice': {'new_axis_mask': masks_value, 'shrink_axis_mask': masks_value, + 'ellipsis_mask': masks_value, 'begin_mask': np.ones([3]), + 'end_mask': np.ones([3])}, + 'slice_data': {'shape': int64_array([2, 6, 8])} + }, nodes_with_edges_only=True + ) + (flag, resp) = compare_graphs(graph, graph_ref, 'output_op', check_op_attrs=True) + self.assertTrue(flag, resp) + + def test_no_axes(self): + input_shape = int64_array([5, 10, 20]) + starts_value = int64_array([3, 2, 7]) + ends_value = int64_array([5, 8, 15]) + steps_value = int64_array([2, 3, 1]) + masks_value = np.zeros([len(input_shape)], dtype=np.int64) + graph = build_graph(self.nodes_attributes, + [('placeholder_1', 'placeholder_1_data'), + ('placeholder_1_data', 'slice', {'in': 0}), + ('starts', 'starts_data'), + ('starts_data', 'slice', {'in': 1}), + ('ends', 'ends_data'), + ('ends_data', 'slice', {'in': 2}), + ('steps', 'steps_data'), + ('steps_data', 'slice', {'in': 4}), + ('slice', 'slice_data'), + ('slice_data', 'output_op'), + ('output_op', 'output_data'), + ('output_data', 'op_output') + ], + {'placeholder_1_data': {'shape': input_shape}, + 'starts': {'shape': starts_value.shape, 'value': starts_value}, + 'starts_data': {'shape': starts_value.shape, 'value': starts_value}, + 'ends': {'shape': ends_value.shape, 'value': ends_value}, + 'ends_data': {'shape': ends_value.shape, 'value': ends_value}, + 'steps': {'shape': steps_value.shape, 'value': steps_value}, + 'steps_data': {'shape': steps_value.shape, 'value': steps_value}, + }, nodes_with_edges_only=True + ) + slice_node = Node(graph, 'slice') + Slice.infer(slice_node) + + pattern = ConvertSlice() + pattern.find_and_replace_pattern(graph) + + graph_ref = build_graph(self.nodes_attributes, + [('placeholder_1', 'placeholder_1_data'), + ('placeholder_1_data', 'strided_slice', {'in': 0}), + ('starts', 'starts_data'), + ('starts_data', 'strided_slice', {'in': 1}), + ('ends', 'ends_data'), + ('ends_data', 'strided_slice', {'in': 2}), + ('strides', 'strides_data'), + ('strides_data', 'strided_slice', {'in': 3}), + ('strided_slice', 'slice_data'), + ('slice_data', 'output_op'), + ('output_op', 'output_data'), + ('output_data', 'op_output') + ], + {'placeholder_1_data': {'shape': input_shape}, + 'strided_slice': {'new_axis_mask': masks_value, 'shrink_axis_mask': masks_value, + 'ellipsis_mask': masks_value, 'begin_mask': np.ones([3]), + 'end_mask': np.ones([3])}, + 'slice_data': {'shape': int64_array([1, 2, 8])} + }, nodes_with_edges_only=True + ) + (flag, resp) = compare_graphs(graph, graph_ref, 'output_op', check_op_attrs=True) + self.assertTrue(flag, resp) + + def test_no_steps(self): + input_shape = int64_array([5, 10, 20]) + starts_value = int64_array([4, 2]) + ends_value = int64_array([15, 8]) + axes_value = int64_array([2, 1]) + masks_value = np.zeros([len(input_shape)], dtype=np.int64) + graph = build_graph(self.nodes_attributes, + [('placeholder_1', 'placeholder_1_data'), + ('placeholder_1_data', 'slice', {'in': 0}), + ('starts', 'starts_data'), + ('starts_data', 'slice', {'in': 1}), + ('ends', 'ends_data'), + ('ends_data', 'slice', {'in': 2}), + ('axes', 'axes_data'), + ('axes_data', 'slice', {'in': 3}), + ('slice', 'slice_data'), + ('slice_data', 'output_op'), + ('output_op', 'output_data'), + ('output_data', 'op_output') + ], + {'placeholder_1_data': {'shape': input_shape}, + 'starts': {'shape': starts_value.shape, 'value': starts_value}, + 'starts_data': {'shape': starts_value.shape, 'value': starts_value}, + 'ends': {'shape': ends_value.shape, 'value': ends_value}, + 'ends_data': {'shape': ends_value.shape, 'value': ends_value}, + 'axes': {'shape': axes_value.shape, 'value': axes_value}, + 'axes_data': {'shape': axes_value.shape, 'value': axes_value}, + }, nodes_with_edges_only=True + ) + slice_node = Node(graph, 'slice') + Slice.infer(slice_node) + + pattern = ConvertSlice() + pattern.find_and_replace_pattern(graph) + + graph_ref = build_graph(self.nodes_attributes, + [('placeholder_1', 'placeholder_1_data'), + ('placeholder_1_data', 'strided_slice', {'in': 0}), + ('starts', 'starts_data'), + ('starts_data', 'strided_slice', {'in': 1}), + ('ends', 'ends_data'), + ('ends_data', 'strided_slice', {'in': 2}), + ('strides', 'strides_data'), + ('strides_data', 'strided_slice', {'in': 3}), + ('strided_slice', 'slice_data'), + ('slice_data', 'output_op'), + ('output_op', 'output_data'), + ('output_data', 'op_output') + ], + {'placeholder_1_data': {'shape': input_shape}, + 'strided_slice': {'new_axis_mask': masks_value, 'shrink_axis_mask': masks_value, + 'ellipsis_mask': masks_value, 'begin_mask': int64_array([0, 1, 1]), + 'end_mask': int64_array([0, 1, 1])}, + 'slice_data': {'shape': int64_array([5, 6, 11])} + }, nodes_with_edges_only=True + ) + (flag, resp) = compare_graphs(graph, graph_ref, 'output_op', check_op_attrs=True) + self.assertTrue(flag, resp) diff --git a/model-optimizer/extensions/middle/SliceConverter.py b/model-optimizer/extensions/middle/SliceConverter.py index 241afb0d08318f..5eba1450895a6e 100644 --- a/model-optimizer/extensions/middle/SliceConverter.py +++ b/model-optimizer/extensions/middle/SliceConverter.py @@ -16,11 +16,13 @@ import numpy as np -from mo.graph.graph import Graph +from mo.front.common.partial_infer.utils import int64_array +from mo.graph.graph import Graph, Node from mo.middle.replacement import MiddleReplacementPattern from mo.ops.const import Const from mo.ops.crop import Crop from mo.ops.strided_slice import StridedSlice +from mo.utils.error import Error def convert_negative_indices(indices: np.array, shape: np.array): @@ -31,11 +33,12 @@ def convert_negative_indices(indices: np.array, shape: np.array): class ConvertSlice(MiddleReplacementPattern): """ - This class convert Slice operation to Crop or Split depends on parameters + This class convert Slice operation to Crop, Split or StridedSlice depends on parameters """ enabled = True op = "Slice" + force_clean_up = True def run_after(self): from extensions.middle.pass_separator import MiddleStart @@ -49,8 +52,83 @@ def pattern(self): edges=[] ) + @staticmethod + def convert_onnx_slice_opset10(node: Node): + """ + Converts the Slice node from ONNX opset10 to StridedSlice. + :param node: Slice node + :return: None + """ + graph = node.graph + + input_shape = node.in_port(0).data.get_shape() + output_shape = node.out_port(0).data.get_shape() + starts = node.in_port(1).data.get_value() + ends = node.in_port(2).data.get_value() + if starts is None or ends is None: + raise Error('The input with starts or end is not constant for node {}'.format(node.id)) + + # in ONNX the value for 'ends' is usually -1 which is translated to maximum possible value of int64. This + # value must be converted to maximum of int32 because such big values do not fit into the int32 which is + # supported by the StridedSlice layer + ends = int64_array([np.iinfo(np.int32).max if item > np.iinfo(np.int32).max else item for item in ends]) + if node.is_in_port_connected(3): + axes = node.in_port(3).data.get_value() + if axes is None: + raise Error('The input with axes is not constant for node {}'.format(node.id)) + else: + axes = int64_array(list(range(starts.size))) + + if node.is_in_port_connected(4): + steps = node.in_port(4).data.get_value() + if steps is None: + raise Error('The input with steps is not constant for node {}'.format(node.id)) + else: + steps = np.ones([starts.size]) + + ss_begin_mask = np.zeros(len(input_shape), dtype=np.int32) + ss_end_mask = np.zeros(len(input_shape), dtype=np.int32) + ss_begin = np.zeros(len(input_shape), dtype=np.int32) + ss_end = np.zeros(len(input_shape), dtype=np.int32) + ss_steps = np.ones(len(input_shape), dtype=np.int32) + + # prepare inputs and attributes for the StridedSlice layer + for i, axis in enumerate(axes): + if starts[i] != 0: + ss_begin_mask[axis] = 1 + ss_begin[axis] = starts[i] + + ss_end_mask[axis] = 1 + ss_end[axis] = ends[i] + + ss_steps[axis] = steps[i] + + begin_node = Const(graph, {'value': ss_begin, 'force_precision': 'I32'}).create_node() + end_node = Const(graph, {'value': ss_end, 'force_precision': 'I32'}).create_node() + strides_node = Const(graph, {'value': ss_steps, 'force_precision': 'I32'}).create_node() + + ss = StridedSlice(graph, dict(new_axis_mask=np.zeros(len(output_shape), dtype=np.int32), + shrink_axis_mask=np.zeros(len(output_shape), dtype=np.int32), + ellipsis_mask=np.zeros(len(output_shape), dtype=np.int32), + begin_mask=ss_begin_mask, + end_mask=ss_end_mask)).create_node() + node.in_port(0).get_connection().set_destination(ss.in_port(0)) + begin_node.out_port(0).connect(ss.in_port(1)) + end_node.out_port(0).connect(ss.in_port(2)) + strides_node.out_port(0).connect(ss.in_port(3)) + node.out_port(0).get_connection().set_source(ss.out_port(0)) + def replace_pattern(self, graph: Graph, match: dict): node = match['slice'] + + input = node.in_node(0) + output_data = node.out_node() + + # ONNX 10 opset case + if len(node.in_nodes()) >= 3 and node.has_valid('format') and node['format'] == 'onnx': + self.convert_onnx_slice_opset10(node) + return + # Caffe case if not node.has_valid('start') or not node.has_valid('end'): return @@ -58,16 +136,12 @@ def replace_pattern(self, graph: Graph, match: dict): begin = node.start end = node.end axis = node.axis if node.has_valid('axis') else np.arange(begin.size) - - - input = node.in_node(0) - output_data = node.out_node() # Check whether operation use only one axis or not axes_begin = np.zeros(len(input.shape), dtype=np.int32) axes_end = np.zeros(len(input.shape), dtype=np.int32) - begin_ext = np.zeros(len(input.shape), dtype=np.int32) - end_ext = np.zeros(len(input.shape), dtype=np.int32) + ss_begin = np.zeros(len(input.shape), dtype=np.int32) + ss_end = np.zeros(len(input.shape), dtype=np.int32) dims = 0 axes = np.zeros(begin.size) for i in range(len(axis)): @@ -76,10 +150,10 @@ def replace_pattern(self, graph: Graph, match: dict): axes[i] = 1 if begin[i] != 0: axes_begin[axis[i]] = 1 - begin_ext[axis[i]] = begin[i] + ss_begin[axis[i]] = begin[i] if end[i] < input.shape[axis[i]]: axes_end[axis[i]] = 1 - end_ext[axis[i]] = end[i] + ss_end[axis[i]] = end[i] axes = np.array(axes, dtype=bool) if dims == 1 or dims == 0: @@ -91,11 +165,11 @@ def replace_pattern(self, graph: Graph, match: dict): begin_mask=axes_begin, end_mask=axes_end)) - convert_negative_indices(begin_ext, input.shape) - convert_negative_indices(end_ext, input.shape) + convert_negative_indices(ss_begin, input.shape) + convert_negative_indices(ss_end, input.shape) - begin_node = Const(graph, {'name': 'begin', 'value': begin_ext, 'force_precision': 'I32'}).create_node_with_data() - end_node = Const(graph, {'name': 'end', 'value': end_ext, 'force_precision': 'I32'}).create_node_with_data() + begin_node = Const(graph, {'value': ss_begin, 'force_precision': 'I32'}).create_node_with_data() + end_node = Const(graph, {'value': ss_end, 'force_precision': 'I32'}).create_node_with_data() ss.create_node_with_data(inputs=[input, begin_node, end_node], data_nodes=[output_data]) # Remove unnecessary edges from and to to Slice vertex diff --git a/model-optimizer/extensions/middle/UpsampleToResample.py b/model-optimizer/extensions/middle/UpsampleToResample.py index 77227535eef2e3..98bbe2d1135348 100644 --- a/model-optimizer/extensions/middle/UpsampleToResample.py +++ b/model-optimizer/extensions/middle/UpsampleToResample.py @@ -22,6 +22,7 @@ from extensions.ops.elementwise import Mul from extensions.ops.interpolate import Interpolate +from mo.front.common.layout import get_height_dim, get_width_dim from mo.front.common.partial_infer.utils import int64_array from mo.graph.graph import Graph, Node from mo.middle.replacement import MiddleReplacementPattern @@ -33,7 +34,6 @@ class UpsampleToResample(MiddleReplacementPattern): enabled = True force_clean_up = True - graph_condition = [lambda graph: graph.graph['fw'] == 'onnx'] def run_after(self): from extensions.middle.pass_separator import MiddleStart @@ -54,6 +54,7 @@ def pattern(self): def replace_pattern(self, graph: Graph, match: Dict[str, Node]): log.debug('UpsampleToResample is triggered') upsample = match['upsample'] + input_shape = upsample.in_port(0).data.get_shape() if len(upsample.in_nodes()) == 2: if upsample.in_node(1).value is None: @@ -79,13 +80,15 @@ def replace_pattern(self, graph: Graph, match: Dict[str, Node]): shape = Shape(graph, {'name': upsample.name + '/0_port'}).create_node() - begin = Const(graph, {'value': np.array([2])}).create_node() - end = Const(graph, {'value': np.array([4])}).create_node() - stride = Const(graph, {'value': np.array([1])}).create_node() + begin = Const(graph, {'value': int64_array([get_height_dim(graph.graph['layout'], + len(input_shape))])}).create_node() + end = Const(graph, {'value': int64_array([get_width_dim(graph.graph['layout'], + len(input_shape)) + 1])}).create_node() + stride = Const(graph, {'value': int64_array([1])}).create_node() ss = StridedSlice(graph, {'name': upsample.name + '/ss_0_port', 'begin_mask': np.array([1]), 'end_mask': np.array([0]), 'new_axis_mask': np.array([0]), - 'shrink_axis_mask': np.array([0]), - 'ellipsis_mask': np.array([0])}).create_node() + 'shrink_axis_mask': int64_array([0]), + 'ellipsis_mask': int64_array([0])}).create_node() mul = Mul(graph, {'name': upsample.name + '/factor_mul_'}).create_node() @@ -99,7 +102,8 @@ def replace_pattern(self, graph: Graph, match: Dict[str, Node]): factor.out_port(0).connect(mul.in_port(1)) # Create Interpolate operation - axes = int64_array([2, 3]) if graph.graph['layout'] == 'NCHW' else int64_array([1, 2]) + axes = int64_array([get_height_dim(graph.graph['layout'], len(input_shape)), + get_width_dim(graph.graph['layout'], len(input_shape))]) resample_op = Interpolate(graph, dict(name='Interpolate/{}'.format(upsample.name), factor=factor_value, axes=axes, mode=upsample.attrs()['mode'], diff --git a/model-optimizer/extensions/ops/Cast.py b/model-optimizer/extensions/ops/Cast.py index 51769940222fb7..2c31644401b818 100644 --- a/model-optimizer/extensions/ops/Cast.py +++ b/model-optimizer/extensions/ops/Cast.py @@ -18,6 +18,7 @@ from mo.front.common.partial_infer.elemental import copy_shape_infer from mo.graph.graph import Node, Graph +from mo.middle.passes.convert_data_type import np_data_type_to_precision from mo.ops.op import Op @@ -27,13 +28,23 @@ class Cast(Op): def __init__(self, graph: Graph, attrs: dict): mandatory_props = { 'op': __class__.op, + 'type': 'Convert', 'infer': __class__.infer, + 'type_infer': __class__.type_infer, 'dst_type': None, 'in_ports_count': 1, 'out_ports_count': 1, } super().__init__(graph, mandatory_props, attrs) + def backend_attrs(self): + return [('precision', lambda node: np_data_type_to_precision(node.dst_type))] + + @staticmethod + def type_infer(node: Node): + assert node.has_valid('dst_type'), 'Destination type of "Cast" operation should be extracted earlier' + node.out_port(0).set_data_type(node.dst_type) + @staticmethod def infer(node: Node): assert node.has_valid('dst_type'), 'Destination type of "Cast" operation should be extracted earlier' diff --git a/model-optimizer/extensions/ops/ReduceOps.py b/model-optimizer/extensions/ops/ReduceOps.py index 5faadf4702f347..457e7b35187dc1 100644 --- a/model-optimizer/extensions/ops/ReduceOps.py +++ b/model-optimizer/extensions/ops/ReduceOps.py @@ -25,26 +25,28 @@ 'ReduceSum': np.sum, 'ReduceProd': np.prod, 'ReduceMax': np.max, + 'ReduceMin': np.min, 'ReduceMean': np.mean, 'ReduceAnd': np.all, } def reduce_infer(node: Node): - in_ports = node.in_ports() - assert len(in_ports) == 2 and 0 in in_ports and 1 in in_ports, \ + connected_in_ports = [port for port in node.in_ports().values() if not port.disconnected()] + assert len(connected_in_ports) == 2, \ "{} node `{}` should have 2 input ports, where 0-input is data input and 1-input represent " \ "`reduction_indices`".format(node.op, node.id) - axis = node.in_port(1).data.get_value() - in_data = node.in_port(0).data in_shape = in_data.get_shape() - assert in_shape is not None, "Can not infer {} node `{}`: shape of 0-input unknown".format(node.op, node.id) + axis = node.in_port(1).data.get_value() - # The default axis == None is to reduce over all the dimensions of the input tensor - if axis is None: + # If the axis is None then reduce over all the dimensions of the input tensor + if axis.size == 1 and axis.item() is None: axis = int64_array(list(range(len(in_shape)))) + node.in_port(1).data.set_value(axis) + + assert in_shape is not None, "Can not infer {} node `{}`: shape of 0-input unknown".format(node.op, node.id) axis = axis.copy() if axis.size == 1: @@ -102,6 +104,11 @@ class ReduceProd(ReduceOp): enabled = True +class ReduceMin(ReduceOp): + op = 'ReduceMin' + enabled = True + + class ReduceMax(ReduceOp): op = 'ReduceMax' enabled = True diff --git a/model-optimizer/extensions/ops/activation_ops.py b/model-optimizer/extensions/ops/activation_ops.py index 4646b764a3298c..6da7eeca2902c1 100644 --- a/model-optimizer/extensions/ops/activation_ops.py +++ b/model-optimizer/extensions/ops/activation_ops.py @@ -23,7 +23,7 @@ from mo.ops.clamp import Clamp from mo.ops.op import Op -activation_ops = ['Sigmoid', 'Tanh', 'ReLU6', 'Exp', 'Elu', 'Not'] +activation_ops = ['Sigmoid', 'Tanh', 'ReLU6', 'Exp', 'Elu', 'Not', 'Floor'] class Activation(Op): @@ -80,6 +80,11 @@ class Erf(Activation): operation = None +class Floor(Activation): + op = 'Floor' + operation = staticmethod(lambda x: np.floor(x)) + + class Elu(Activation): op = 'Elu' diff --git a/model-optimizer/extensions/ops/detectionoutput_onnx.py b/model-optimizer/extensions/ops/detectionoutput_onnx.py index 8566e8a251bc88..5e5cf2c40eb12e 100644 --- a/model-optimizer/extensions/ops/detectionoutput_onnx.py +++ b/model-optimizer/extensions/ops/detectionoutput_onnx.py @@ -16,6 +16,7 @@ import numpy as np +from mo.front.common.partial_infer.utils import int64_array from mo.ops.op import Op @@ -27,7 +28,9 @@ def __init__(self, graph, attrs): mandatory_props = dict( type=__class__.op, op=__class__.op, - infer=__class__.infer + infer=__class__.infer, + in_ports_count=4, + out_ports_count=4, ) super().__init__(graph, mandatory_props, attrs) @@ -48,12 +51,7 @@ def infer(node): rois_num = node.max_detections_per_image # boxes node.out_node(0).shape = np.array([rois_num, 4], dtype=np.int64) - try: - # classes - node.out_node(1).shape = np.array([rois_num], dtype=np.int64) - # scores - node.out_node(2).shape = np.array([rois_num], dtype=np.int64) - # batch_ids - node.out_node(3).shape = np.array([rois_num], dtype=np.int64) - except Exception as ex: - print(ex) + # classes, scores, batch indices + for port_ind in range(1, 4): + if not node.out_port(port_ind).disconnected(): + node.out_port(port_ind).data.set_shape(int64_array([rois_num])) diff --git a/model-optimizer/extensions/ops/elementwise.py b/model-optimizer/extensions/ops/elementwise.py index 758f1e37d7ae2b..234b2181476ef9 100644 --- a/model-optimizer/extensions/ops/elementwise.py +++ b/model-optimizer/extensions/ops/elementwise.py @@ -16,9 +16,11 @@ import numpy as np -from mo.front.common.partial_infer.eltwise import eltwise_infer +from mo.front.common.partial_infer.eltwise import eltwise_infer, bias_add_infer from mo.graph.graph import Graph +from mo.middle.passes.convert_data_type import data_type_str_to_np from mo.ops.op import Op +from mo.utils.error import Error class Elementwise(Op): @@ -32,13 +34,23 @@ def __init__(self, graph: Graph, attrs: dict): 'op': self.op, 'type': self.op_type, 'infer': lambda node: eltwise_infer(node, self.operation), + 'type_infer': self.type_infer, 'can_be_bias': True, 'can_be_fused': True, 'in_ports_count': 2, 'out_ports_count': 1, - 'is_eltwise': True + 'is_eltwise': True, }, attrs) + @staticmethod + def type_infer(node): + in_type_0 = node.in_port(0).get_data_type() + in_type_1 = node.in_port(1).get_data_type() + if in_type_0 != in_type_1: + raise Error('Elementwise operation {} has inputs of different data types: {} and {}'.format( + node.soft_get('name'), in_type_0, in_type_1)) + node.out_port(0).set_data_type(in_type_0) + class Add(Elementwise): enabled = False @@ -47,6 +59,14 @@ class Add(Elementwise): operation = staticmethod(lambda a, b: a + b) +class BiasAdd(Add): + op_type = 'BiasAdd' + + def __init__(self, graph: Graph, attrs: dict): + attrs.update({'infer': lambda node: bias_add_infer(node, self.operation)}) + super().__init__(graph, attrs) + + class Sub(Elementwise): enabled = False op = 'Sub' @@ -72,7 +92,22 @@ class Pow(Elementwise): enabled = False op = 'Pow' op_type = 'Pow' - operation = staticmethod(lambda a, b: a ** b) + + @staticmethod + def operation(a, b): + if np.any(b < 0) and np.issubdtype(a.dtype, np.signedinteger): + return np.array(a.astype(np.float32) ** b, dtype=np.float32) + return a ** b + + @staticmethod + def type_infer(node): + # dynamic power output data type is complicate to predict, so we set float data type by default, + # if we haven't got actual value + value = node.out_port(0).data.get_value() + if value is not None: + node.out_port(0).set_data_type(value.dtype) + else: + node.out_port(0).set_data_type(data_type_str_to_np(node.graph.graph['cmd_params'].data_type)) class Greater(Elementwise): diff --git a/model-optimizer/extensions/ops/gather.py b/model-optimizer/extensions/ops/gather.py index eddbd9daeeb46c..2622d6a609b032 100644 --- a/model-optimizer/extensions/ops/gather.py +++ b/model-optimizer/extensions/ops/gather.py @@ -61,7 +61,7 @@ def infer(node: Node): # both inputs are constant if data.value is not None and indices.value is not None: indices.value = np.array(indices.value, dtype=np.int64) - node.out_node(0).value = np.take(data.value, indices.value, axis) + node.out_node(0).value = np.array(np.take(data.value, indices.value, axis), dtype=data.value.dtype) node.out_node(0).shape = np.array(node.out_node(0).value.shape, dtype=np.int64) return diff --git a/model-optimizer/extensions/ops/non_max_suppression.py b/model-optimizer/extensions/ops/non_max_suppression.py new file mode 100644 index 00000000000000..1ade624832e22e --- /dev/null +++ b/model-optimizer/extensions/ops/non_max_suppression.py @@ -0,0 +1,55 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from mo.front.common.partial_infer.utils import int64_array +from mo.graph.graph import Node, Graph +from mo.ops.op import Op + + +class NonMaxSuppression(Op): + op = 'NonMaxSuppression' + + def __init__(self, graph: Graph, attrs: dict): + mandatory_props = { + 'type': __class__.op, + 'op': __class__.op, + 'infer': __class__.infer, + 'center_point_box': 0, + 'in_ports_count': 5, + 'out_ports_count': 1, + 'force_precision_in_ports': {2: 'int32'}, + } + super().__init__(graph, mandatory_props, attrs) + + def supported_attrs(self): + return [ + 'center_point_box', + ] + + @staticmethod + def infer(node: Node): + boxes_shape = node.in_port(0).data.get_shape() + assert boxes_shape is not None, 'The shape of tensor with boxes is not defined' + scores_shape = node.in_port(1).data.get_shape() + assert scores_shape is not None, 'The shape of tensor with scores is not defined' + assert len(boxes_shape) == 3, 'Length of tensors with boxes must be equal to 3' + assert len(scores_shape) == 3, 'Length of tensors with scores must be equal to 3' + + num_classes = scores_shape[1] + num_input_boxes = boxes_shape[1] + assert scores_shape[2] == num_input_boxes, 'Number of boxes mismatch' + + node.out_port(0).data.set_shape(int64_array([num_input_boxes * num_classes, 3])) diff --git a/model-optimizer/extensions/ops/pnorm.py b/model-optimizer/extensions/ops/pnorm.py new file mode 100644 index 00000000000000..8f1a2267daf68d --- /dev/null +++ b/model-optimizer/extensions/ops/pnorm.py @@ -0,0 +1,42 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from mo.graph.graph import Graph, Node +from mo.ops.op import Op + + +class PNormOp(Op): + """ + PNorm operation should be replaced by operations: + Power(P) -> Reshape(n,c*g->n,g,c)-> ReduceSum(axis=1)-> Power(1/P) + """ + op = 'pnorm' + + def __init__(self, graph: Graph, attrs: dict): + mandatory_props = { + 'type': None, + 'op': __class__.op, + 'in_ports_count': 1, + 'out_ports_count': 1, + 'infer': __class__.infer + } + super().__init__(graph, mandatory_props, attrs) + + @staticmethod + def infer(node: Node): + shape = node.in_port(0).data.get_shape().copy() + shape[1] = shape[1] / node.group + node.out_port(0).data.set_shape(shape) diff --git a/model-optimizer/extensions/ops/range.py b/model-optimizer/extensions/ops/range.py index 2b02ce1ff6cf7d..4e23c3b38864ba 100644 --- a/model-optimizer/extensions/ops/range.py +++ b/model-optimizer/extensions/ops/range.py @@ -45,7 +45,7 @@ def infer(node: Node): if not start.has_valid('value') or not limit.has_valid('value') or not delta.has_valid('value'): log.error("Range operation is supported with constant inputs only") return - if 'type' in node.pb.attr: + if node.has_valid('pb') and 'type' in node.pb.attr: from mo.front.tf.extractors.utils import tf_dtype_extractor result_data_type = tf_dtype_extractor(node.pb.attr["type"].type) else: diff --git a/model-optimizer/extensions/ops/roifeatureextractor_onnx.py b/model-optimizer/extensions/ops/roifeatureextractor_onnx.py index 5477d9b868a8da..5a00a0582dff75 100644 --- a/model-optimizer/extensions/ops/roifeatureextractor_onnx.py +++ b/model-optimizer/extensions/ops/roifeatureextractor_onnx.py @@ -26,7 +26,9 @@ def __init__(self, graph, attrs): mandatory_props = dict( type=__class__.op, op=__class__.op, - infer=__class__.infer + infer=__class__.infer, + in_ports_count=5, + out_ports_count=2, ) super().__init__(graph, mandatory_props, attrs) @@ -47,7 +49,5 @@ def infer(node): input_features_level_0_shape = node.in_node(1).shape channels_num = input_features_level_0_shape[1] node.out_node(0).shape = np.array([rois_num, channels_num, node.output_size, node.output_size], dtype=np.int64) - try: + if not node.out_port(1).disconnected(): node.out_node(1).shape = np.array([rois_num, 4], dtype=np.int64) - except Exception as ex: - print(ex) diff --git a/model-optimizer/extensions/ops/sparse_fill_empty_rows.py b/model-optimizer/extensions/ops/sparse_fill_empty_rows.py new file mode 100644 index 00000000000000..18d076e8438319 --- /dev/null +++ b/model-optimizer/extensions/ops/sparse_fill_empty_rows.py @@ -0,0 +1,84 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import logging as log + +import networkx as nx +import numpy as np + +from mo.graph.graph import Node, Graph +from mo.ops.op import Op + + +class SparseFillEmptyRows(Op): + ''' The operation fills empty rows in the input 2-D sparse tensor with a default value. + For more details see https://www.tensorflow.org/api_docs/cc/class/tensorflow/ops/sparse-fill-empty-rows + + 4 inputs: + - [0, required] input indices of the sparse tensor (2D), + - [1, required] input values of the sparse tensor (1D), + - [2, required] shape of the sparse tensor. Value of this input is required for the Model Optimizer (1D), + - [3, required] default value to insert at rows missing from the input sparse tensor (0D), + + 3 outputs: + - [0, optional] indices of the filled sparse tensor (2D) + - [1, optional] values of the filled sparse tensor (1D) + - [2, optional] indicator of whether the dense row was missing in the input sparse tensor (1D) + ''' + op = 'SparseFillEmptyRows' + + def __init__(self, graph: Graph, attrs: dict): + mandatory_props = { + 'type': __class__.op, + 'op': __class__.op, + 'infer': __class__.infer, + 'in_ports_count': 4, + 'out_ports_count': 3 + } + super().__init__(graph, mandatory_props, attrs) + + def supported_attrs(self): + return [] + + @staticmethod + def infer(node: Node): + assert len(node.in_nodes()) == 4 + + # check that shape value is defined that is needed for shape inference + shape = node.in_node(2) + assert shape.value is not None and shape.value.size == 2, \ + "SparseFillEmptyRows is supported only with constant shape value" + + shape_value = np.array(shape.value, dtype=np.int64) + + # check that default value is scalar + default_value = node.in_node(3) + assert default_value.shape is not None and len(default_value.shape) == 0, \ + "Default value for SparseFillEmptyRows must be scalar" + + for out_node_ind in node.out_nodes(): + if out_node_ind == 0: # set a shape for output indices + node.out_node(0).shape = np.array([np.prod(shape_value), 2], dtype=np.int64) + continue + elif out_node_ind == 1: # set a shape for output values + node.out_node(1).shape = np.array([np.prod(shape_value)], dtype=np.int64) + continue + elif out_node_ind == 2: # set a shape for empty row indicator + node.out_node(2).shape = np.array([shape_value[0]], dtype=np.int64) + continue + else: + log.error("SparseFillEmptyRows has only three outputs") + return diff --git a/model-optimizer/extensions/ops/sparse_fill_empty_rows_test.py b/model-optimizer/extensions/ops/sparse_fill_empty_rows_test.py new file mode 100644 index 00000000000000..e5cecb0e1f25ef --- /dev/null +++ b/model-optimizer/extensions/ops/sparse_fill_empty_rows_test.py @@ -0,0 +1,128 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import unittest + +import numpy as np + +from extensions.ops.sparse_fill_empty_rows import SparseFillEmptyRows +from mo.front.common.partial_infer.utils import int64_array +from mo.graph.graph import Node +from mo.utils.unittest.graph import build_graph + + +nodes_attributes = {'input_indices': {'shape': None, 'value': None, 'kind': 'data'}, + 'input_values': {'shape': None, 'value': None, 'kind': 'data'}, + 'dense_shape': {'shape': None, 'value': None, 'kind': 'data'}, + 'default_value': {'shape': None, 'value': None, 'kind': 'data'}, + 'sparse_fill_empty_rows_node': {'op': 'SparseFillEmptyRows', 'kind': 'op'}, + 'output_indices': {'shape': None, 'value': None, 'kind': 'data'}, + 'output_values': {'shape': None, 'value': None, 'kind': 'data'}, + 'empty_row_indicator': {'shape': None, 'value': None, 'kind': 'data'}, + } + +# graph 1 +edges1 = [('input_indices', 'sparse_fill_empty_rows_node', {'in': 0}), + ('input_values', 'sparse_fill_empty_rows_node', {'in': 1}), + ('dense_shape', 'sparse_fill_empty_rows_node', {'in': 2}), + ('default_value', 'sparse_fill_empty_rows_node', {'in': 3}), + ('sparse_fill_empty_rows_node', 'output_indices', {'out': 0}), + ('sparse_fill_empty_rows_node', 'output_values', {'out': 1}), + ('sparse_fill_empty_rows_node', 'empty_row_indicator', {'out': 2})] + +inputs1 = {'input_indices': {'shape': int64_array([20, 2]), 'value': None}, + 'input_values': {'shape': int64_array([20]), 'value': None}, + 'dense_shape': {'shape': int64_array([2]), 'value': np.array([4, 5])}, + 'default_value': {'shape': int64_array([]), 'value': None}} + +class TestSparseFillEmptyRows(unittest.TestCase): + def test_partial_infer(self): + graph = build_graph(nodes_attributes, edges1, inputs1) + + sparse_fill_empty_rows_node = Node(graph, 'sparse_fill_empty_rows_node') + SparseFillEmptyRows.infer(sparse_fill_empty_rows_node) + + # prepare reference results + ref_output_indices_shape = int64_array([20, 2]) + ref_output_values_shape = int64_array([20]) + ref_empty_row_indicator_shape = int64_array([4]) + + # get resulted shapes + res_output_indices_shape = graph.node['output_indices']['shape'] + res_output_values_shape = graph.node['output_values']['shape'] + res_empty_row_indicator_shape = graph.node['empty_row_indicator']['shape'] + + self.assertTrue(np.array_equal(ref_output_indices_shape, res_output_indices_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_output_indices_shape, res_output_indices_shape)) + + self.assertTrue(np.array_equal(ref_output_values_shape, res_output_values_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_output_values_shape, res_output_values_shape)) + + self.assertTrue(np.array_equal(ref_empty_row_indicator_shape, res_empty_row_indicator_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_empty_row_indicator_shape, res_empty_row_indicator_shape)) + + def test_partial_infer_for_some_out_ports(self): + edges = [('input_indices', 'sparse_fill_empty_rows_node', {'in': 0}), + ('input_values', 'sparse_fill_empty_rows_node', {'in': 1}), + ('dense_shape', 'sparse_fill_empty_rows_node', {'in': 2}), + ('default_value', 'sparse_fill_empty_rows_node', {'in': 3}), + ('sparse_fill_empty_rows_node', 'output_indices', {'out': 0}), + ('sparse_fill_empty_rows_node', 'empty_row_indicator', {'out': 2})] + graph = build_graph(nodes_attributes, edges, inputs1) + + sparse_fill_empty_rows_node = Node(graph, 'sparse_fill_empty_rows_node') + SparseFillEmptyRows.infer(sparse_fill_empty_rows_node) + + # prepare reference results + ref_output_indices_shape = int64_array([20, 2]) + ref_empty_row_indicator_shape = int64_array([4]) + + # get resulted shapes + res_output_indices_shape = graph.node['output_indices']['shape'] + res_empty_row_indicator_shape = graph.node['empty_row_indicator']['shape'] + + self.assertTrue(np.array_equal(ref_output_indices_shape, res_output_indices_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_output_indices_shape, res_output_indices_shape)) + + self.assertTrue(np.array_equal(ref_empty_row_indicator_shape, res_empty_row_indicator_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_empty_row_indicator_shape, res_empty_row_indicator_shape)) + + def test_incorrect_shape_of_default_value(self): + inputs = {'input_indices': {'shape': int64_array([20, 2]), 'value': None}, + 'input_values': {'shape': int64_array([20]), 'value': None}, + 'dense_shape': {'shape': int64_array([2]), 'value': np.array([4, 5])}, + 'default_value': {'shape': int64_array([3]), 'value': None}} + graph = build_graph(nodes_attributes, edges1, inputs) + sparse_fill_empty_rows_node = Node(graph, 'sparse_fill_empty_rows_node') + self.assertRaises(AssertionError, SparseFillEmptyRows.infer, sparse_fill_empty_rows_node) + + def test_no_value_of_dense_shape(self): + inputs = {'input_indices': {'shape': int64_array([20, 2]), 'value': None}, + 'input_values': {'shape': int64_array([20]), 'value': None}, + 'dense_shape': {'shape': int64_array([2]), 'value': None}, + 'default_value': {'shape': int64_array([]), 'value': None}} + graph = build_graph(nodes_attributes, edges1, inputs) + sparse_fill_empty_rows_node = Node(graph, 'sparse_fill_empty_rows_node') + self.assertRaises(AssertionError, SparseFillEmptyRows.infer, sparse_fill_empty_rows_node) + + def test_incorrect_shape_of_dense_shape(self): + inputs = {'input_indices': {'shape': int64_array([20, 2]), 'value': None}, + 'input_values': {'shape': int64_array([20]), 'value': None}, + 'dense_shape': {'shape': int64_array([2, 2]), 'value': np.array([[4, 5],[1, 2]])}, + 'default_value': {'shape': int64_array([]), 'value': None}} + graph = build_graph(nodes_attributes, edges1, inputs) + sparse_fill_empty_rows_node = Node(graph, 'sparse_fill_empty_rows_node') + self.assertRaises(AssertionError, SparseFillEmptyRows.infer, sparse_fill_empty_rows_node) diff --git a/model-optimizer/extensions/ops/splice.py b/model-optimizer/extensions/ops/splice.py index 330f885e0ad58c..9062feb66b09d3 100644 --- a/model-optimizer/extensions/ops/splice.py +++ b/model-optimizer/extensions/ops/splice.py @@ -25,6 +25,7 @@ def __init__(self, graph: Graph, attrs: dict): mandatory_props = { 'type': None, 'op': __class__.op, + 'const_dim': 0, 'in_ports_count': 1, 'out_ports_count': 1, 'infer': __class__.infer, @@ -35,4 +36,4 @@ def __init__(self, graph: Graph, attrs: dict): def infer(node: Node): out_node = node.out_node() out_node.shape = node.in_node().shape.copy() - out_node.shape[1] = node.in_node().shape[1] * len(node.context) + out_node.shape[1] = node.const_dim + (node.in_node().shape[1] - node.const_dim) * len(node.context) diff --git a/model-optimizer/extensions/ops/unique.py b/model-optimizer/extensions/ops/unique.py new file mode 100644 index 00000000000000..23b51f278dbc15 --- /dev/null +++ b/model-optimizer/extensions/ops/unique.py @@ -0,0 +1,171 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import logging as log + +import networkx as nx +import numpy as np + +from mo.graph.graph import Node, Graph +from mo.ops.op import Op + + +class Unique(Op): + ''' The operation finds unique elements in 1-D tensor. + For more details see https://www.tensorflow.org/api_docs/python/tf/unique + + attributes: + - sorted, indicates whether to sort the unique elements in ascending order or + to return in the same order as they occur in the input + - return_inverse, indicates whether to output indices + - return_counts, indicates whether to output the counts of each unique element + + 1 input: + - [0, required] input tensor (1D) + + 2 outputs: + - [0, required] tensor containing all of the unique elements of the input + and sorted in the same order as in the input (1D) + - [1, optional] tensor of indices for each value of the input + in the tensor of unique elements (1D) + - [2, optional] tensor with a number of occurences for each unique element + in the input (1D) + ''' + op = 'Unique' + + def __init__(self, graph: Graph, attrs: dict): + mandatory_props = { + 'type': __class__.op, + 'op': __class__.op, + 'infer': __class__.infer, + 'in_ports_count': 1, + 'out_ports_count': 3 + } + super().__init__(graph, mandatory_props, attrs) + + def supported_attrs(self): + return [ + 'sorted', + 'return_inverse', + 'return_counts', + ] + + @staticmethod + def infer(node: Node): + # check that all required attributes are set + assert node.has('sorted') and node.sorted in ['true', 'false'], \ + "Unique does not have valid sorted attribute" + assert node.has('return_inverse') and node.return_inverse in ['true', 'false'], \ + "Unique does not have valid return_inverse attribute" + assert node.has('return_counts') and node.return_counts in ['true', 'false'], \ + "Unique does not have valid return_counts attribute" + + # check a number of input and output nodes + assert len(node.in_nodes()) == 1, "Unique must have one input" + assert len(node.out_nodes()) <= 3, "Unique must have less or equal to 3 outputs" + + # compute maximum number of outputs if no output port is pruned + max_num_outputs = 1 + if node.return_inverse == 'true': + max_num_outputs += 1 + if node.return_counts == 'true': + max_num_outputs += 1 + + # check a number of outputs + assert len(node.out_nodes()) <= max_num_outputs, \ + "The number of outputs in IR Unique layer must be less or equal to framework graph one" + + # check that the output with unique elements remains in a graph after pruning + # since this is required output + assert 0 in node.out_nodes(), \ + "The output with unique elements must remain in a graph" + + # check if outputs with indices and counts remain in a graph after pruning + # and update attributes + if len(node.out_nodes()) == 1: + node.return_inverse = 'false' + node.return_counts = 'false' + if len(node.out_nodes()) == 2 and 1 in node.out_nodes() \ + and node.return_inverse == 'true' and node.return_counts == 'true': + node.return_counts = 'false' + if len(node.out_nodes()) == 2 and 2 in node.out_nodes() \ + and node.return_inverse == 'true' and node.return_counts == 'true': + node.return_inverse = 'false' + + # check that input is 1-D tensor + input_shape = node.in_node(0).shape + assert input_shape is not None and input_shape.size == 1, \ + "Unique accepts only 1-D input" + + # determine a shape for each output + for out_node_ind in node.out_nodes(): + assert (out_node_ind < max_num_outputs), "Unique has three outputs at most" + # all outputs have the same shape equal to the input shape + node.out_node(out_node_ind).shape = input_shape + + input_value = node.in_node(0).value + if input_value is None: + return + + # check that input value is 1-D + assert len(input_value.shape) == 1, \ + "Unique accepts only 1-D input" + + is_sorted = (node.sorted == 'true') + return_inverse = (node.return_inverse == 'true') + return_counts = (node.return_counts == 'true') + + # infer if the input is constant + if is_sorted: + unique_output = np.unique(input_value, return_inverse = return_inverse, + return_counts = return_counts, return_index = False) + if not return_inverse and not return_counts: + unique_output = [unique_output] + else: + # np.unique can only return unique elements in sorted order + # so this case should be handled separately + sorted_uniques, sorted_index, sorted_inverse, sorted_counts = np.unique(input_value, return_index = True, + return_inverse = True, return_counts = True) + # compute uniques that are in the same order as they occur in the input, + # indices of input values in uniques, counts for each unique element + uniques = [] + inverse = [] + counts = [] + old_ind_by_elem = dict(zip(sorted_uniques, range(len(sorted_index)))) + new_ind_by_elem = dict() + new_ind = 0 + for ind in np.sort(sorted_index): + uniques.append(input_value[ind]) + old_ind = old_ind_by_elem[input_value[ind]] + counts.append(sorted_counts[old_ind]) + new_ind_by_elem[input_value[ind]] = new_ind + new_ind += 1 + inverse = [new_ind_by_elem[input_value[ind]] for ind in range(len(input_value))] + + # pack unique_output + unique_output = [] + unique_output.append(uniques) + if return_inverse: + unique_output.append(inverse) + if return_counts: + unique_output.append(counts) + + # write result to output nodes + j = 0 + for out_node_ind in node.out_nodes(): + node.out_node(out_node_ind).value = np.array(unique_output[j], dtype=np.float) + node.out_node(out_node_ind).shape = np.array(node.out_node(out_node_ind).value.shape, dtype=np.int64) + j += 1 diff --git a/model-optimizer/extensions/ops/unique_test.py b/model-optimizer/extensions/ops/unique_test.py new file mode 100644 index 00000000000000..9475900f8e5124 --- /dev/null +++ b/model-optimizer/extensions/ops/unique_test.py @@ -0,0 +1,273 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import unittest + +import numpy as np + +from extensions.ops.unique import Unique +from mo.front.common.partial_infer.utils import int64_array +from mo.graph.graph import Node +from mo.utils.unittest.graph import build_graph + + +# graph 1 with two outputs: uniques and indices +nodes_attributes = {'input': {'shape': None, 'value': None, 'kind': 'data'}, + 'unique_node': {'op': 'Unique', 'kind': 'op'}, + 'output_uniques': {'shape': None, 'value': None, 'kind': 'data'}, + 'output_indices': {'shape': None, 'value': None, 'kind': 'data'}, + } +edges1 = [('input', 'unique_node', {'in': 0}), + ('unique_node', 'output_uniques', {'out': 0}), + ('unique_node', 'output_indices', {'out': 1})] +inputs1 = {'input': {'shape': int64_array([20]), 'value': None}, + 'unique_node': { + 'sorted': 'false', + 'return_inverse': 'true', + 'return_counts': 'false' + } + } + +# graph 2 with three outputs: uniques, indices and counts +nodes_attributes2 = {'input': {'shape': None, 'value': None, 'kind': 'data'}, + 'unique_node': {'op': 'Unique', 'kind': 'op'}, + 'output_uniques': {'shape': None, 'value': None, 'kind': 'data'}, + 'output_indices': {'shape': None, 'value': None, 'kind': 'data'}, + 'output_counts': {'shape': None, 'value': None, 'kind': 'data'} + } +edges2 = [('input', 'unique_node', {'in': 0}), + ('unique_node', 'output_uniques', {'out': 0}), + ('unique_node', 'output_indices', {'out': 1}), + ('unique_node', 'output_counts', {'out': 2})] +inputs2 = {'input': {'shape': int64_array([20]), 'value': None}, + 'unique_node': { + 'sorted': 'false', + 'return_inverse': 'true', + 'return_counts': 'true' + } + } + + +class TestUnique(unittest.TestCase): + # case 1: a graph with two outputs: uniques and indices + def test_partial_infer1(self): + graph = build_graph(nodes_attributes, edges1, inputs1) + + unique_node = Node(graph, 'unique_node') + Unique.infer(unique_node) + + # prepare reference results + ref_output_uniques_shape = int64_array([20]) + ref_output_indices_shape = int64_array([20]) + + # get resulted shapes + res_output_uniques_shape = graph.node['output_uniques']['shape'] + res_output_indices_shape = graph.node['output_indices']['shape'] + + self.assertTrue(np.array_equal(ref_output_uniques_shape, res_output_uniques_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_output_uniques_shape, res_output_uniques_shape)) + + self.assertTrue(np.array_equal(ref_output_indices_shape, res_output_indices_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_output_indices_shape, res_output_indices_shape)) + + # case 2: a graph with three outputs: uniques, indices and counts + def test_partial_infer2(self): + graph = build_graph(nodes_attributes2, edges2, inputs2) + + unique_node = Node(graph, 'unique_node') + Unique.infer(unique_node) + + # prepare reference results + ref_output_uniques_shape = int64_array([20]) + ref_output_indices_shape = int64_array([20]) + ref_output_counts_shape = int64_array([20]) + + # get resulted shapes + res_output_uniques_shape = graph.node['output_uniques']['shape'] + res_output_indices_shape = graph.node['output_indices']['shape'] + res_output_counts_shape = graph.node['output_counts']['shape'] + + self.assertTrue(np.array_equal(ref_output_uniques_shape, res_output_uniques_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_output_uniques_shape, res_output_uniques_shape)) + + self.assertTrue(np.array_equal(ref_output_indices_shape, res_output_indices_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_output_indices_shape, res_output_indices_shape)) + + self.assertTrue(np.array_equal(ref_output_counts_shape, res_output_counts_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_output_counts_shape, res_output_counts_shape)) + + # case 3: a graph with just unique output + def test_partial_infer_just_unique(self): + edges = [('input', 'unique_node', {'in': 0}), + ('unique_node', 'output_uniques', {'out': 0})] + graph = build_graph(nodes_attributes, edges, inputs1) + + unique_node = Node(graph, 'unique_node') + Unique.infer(unique_node) + + # prepare reference results + ref_output_uniques_shape = int64_array([20]) + + # get resulted shapes + res_output_uniques_shape = graph.node['output_uniques']['shape'] + + self.assertTrue(np.array_equal(ref_output_uniques_shape, res_output_uniques_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_output_uniques_shape, res_output_uniques_shape)) + + # case 4: an invalid graph with 2D input + def test_incorrect_input_shape(self): + inputs = {'input': {'shape': int64_array([20, 2]), 'value': None}} + + graph = build_graph(nodes_attributes, edges1, inputs) + + unique_node = Node(graph, 'unique_node') + self.assertRaises(AssertionError, Unique.infer, unique_node) + + # case 5: an invalid graph with return_counts = false and three outputs + def test_more_output_ports(self): + nodes_attributes1 = {'input': {'shape': None, 'value': None, 'kind': 'data'}, + 'unique_node': {'op': 'Unique', 'kind': 'op'}, + 'output_uniques': {'shape': None, 'value': None, 'kind': 'data'}, + 'output_indices': {'shape': None, 'value': None, 'kind': 'data'}, + 'output3': {'shape': None, 'value': None, 'kind': 'data'}, + } + edges = [('input', 'unique_node', {'in': 0}), + ('unique_node', 'output_uniques', {'out': 0}), + ('unique_node', 'output_indices', {'out': 1}), + ('unique_node', 'output3', {'out': 2})] + graph = build_graph(nodes_attributes1, edges, inputs1) + + unique_node = Node(graph, 'unique_node') + self.assertRaises(AssertionError, Unique.infer, unique_node) + + # case 6: an invalid graph without unique output + def test_no_uniques_output(self): + edges = [('input', 'unique_node', {'in': 0}), + ('unique_node', 'output_indices', {'out': 1})] + graph = build_graph(nodes_attributes, edges, inputs1) + + unique_node = Node(graph, 'unique_node') + self.assertRaises(AssertionError, Unique.infer, unique_node) + + # case 7: infer for constant input + # graph with a constant input, three outputs, sorted = 'false' + def test_constant_input(self): + nodes_attributes_ = {'input': {'shape': None, 'value': None, 'kind': 'data'}, + 'unique_node': {'op': 'Unique', 'kind': 'op'}, + 'output_uniques': {'shape': None, 'value': None, 'kind': 'data'}, + 'output_indices': {'shape': None, 'value': None, 'kind': 'data'}, + 'output_counts': {'shape': None, 'value': None, 'kind': 'data'} + } + edges_ = [('input', 'unique_node', {'in': 0}), + ('unique_node', 'output_uniques', {'out': 0}), + ('unique_node', 'output_indices', {'out': 1}), + ('unique_node', 'output_counts', {'out': 2})] + inputs_ = {'input': {'shape': int64_array([10]), + 'value': np.array([8.0, 1.0, 2.0, 1.0, 8.0, 5.0, 1.0, 5.0, 0.0, 0.0], dtype=np.float)}, + 'unique_node': { + 'sorted': 'false', + 'return_inverse': 'true', + 'return_counts': 'true' + } + } + graph = build_graph(nodes_attributes_, edges_, inputs_) + unique_node = Node(graph, 'unique_node') + Unique.infer(unique_node) + + # prepare reference results + ref_output_uniques_shape = int64_array([5]) + ref_output_uniques_value = np.array([8.0, 1.0, 2.0, 5.0, 0.0], dtype=np.float) + ref_output_indices_shape = int64_array([10]) + ref_output_indices_value = np.array([0.0, 1.0, 2.0, 1.0, 0.0, 3.0, 1.0, 3.0, 4.0, 4.0], dtype=np.float) + ref_output_counts_shape = int64_array([5]) + ref_output_counts_value = np.array([2.0, 3.0, 1.0, 2.0, 2.0], dtype=np.float) + + # get resulted shapes + res_output_uniques_shape = graph.node['output_uniques']['shape'] + res_output_uniques_value = graph.node['output_uniques']['value'] + res_output_indices_shape = graph.node['output_indices']['shape'] + res_output_indices_value = graph.node['output_indices']['value'] + res_output_counts_shape = graph.node['output_counts']['shape'] + res_output_counts_value = graph.node['output_counts']['value'] + + # verify the results + self.assertTrue(np.array_equal(ref_output_uniques_shape, res_output_uniques_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_output_uniques_shape, res_output_uniques_shape)) + self.assertTrue(np.array_equal(ref_output_uniques_value, res_output_uniques_value), + 'values do not match expected: {} and given: {}'.format(ref_output_uniques_value, res_output_uniques_value)) + self.assertTrue(np.array_equal(ref_output_indices_shape, res_output_indices_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_output_indices_shape, res_output_indices_shape)) + self.assertTrue(np.array_equal(ref_output_indices_value, res_output_indices_value), + 'values do not match expected: {} and given: {}'.format(ref_output_indices_value, res_output_indices_value)) + self.assertTrue(np.array_equal(ref_output_counts_shape, res_output_counts_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_output_counts_shape, res_output_counts_shape)) + self.assertTrue(np.array_equal(ref_output_counts_value, res_output_counts_value), + 'values do not match expected: {} and given: {}'.format(ref_output_counts_value, res_output_counts_value)) + + # case 8: infer for constant input + # graph with a constant input, three outputs, sorted = 'true' + def test_constant_input(self): + nodes_attributes_ = {'input': {'shape': None, 'value': None, 'kind': 'data'}, + 'unique_node': {'op': 'Unique', 'kind': 'op'}, + 'output_uniques': {'shape': None, 'value': None, 'kind': 'data'}, + 'output_indices': {'shape': None, 'value': None, 'kind': 'data'}, + 'output_counts': {'shape': None, 'value': None, 'kind': 'data'} + } + edges_ = [('input', 'unique_node', {'in': 0}), + ('unique_node', 'output_uniques', {'out': 0}), + ('unique_node', 'output_indices', {'out': 1}), + ('unique_node', 'output_counts', {'out': 2})] + inputs_ = {'input': {'shape': int64_array([10]), + 'value': np.array([8.0, 1.0, 2.0, 1.0, 8.0, 5.0, 1.0, 5.0, 0.0, 0.0], dtype=np.float)}, + 'unique_node': { + 'sorted': 'true', + 'return_inverse': 'true', + 'return_counts': 'true' + } + } + graph = build_graph(nodes_attributes_, edges_, inputs_) + unique_node = Node(graph, 'unique_node') + Unique.infer(unique_node) + + # prepare reference results + ref_output_uniques_shape = int64_array([5]) + ref_output_uniques_value = np.array([0.0, 1.0, 2.0, 5.0, 8.0], dtype=np.float) + ref_output_indices_shape = int64_array([10]) + ref_output_indices_value = np.array([4.0, 1.0, 2.0, 1.0, 4.0, 3.0, 1.0, 3.0, 0.0, 0.0], dtype=np.float) + ref_output_counts_shape = int64_array([5]) + ref_output_counts_value = np.array([2.0, 3.0, 1.0, 2.0, 2.0], dtype=np.float) + + # get resulted shapes + res_output_uniques_shape = graph.node['output_uniques']['shape'] + res_output_uniques_value = graph.node['output_uniques']['value'] + res_output_indices_shape = graph.node['output_indices']['shape'] + res_output_indices_value = graph.node['output_indices']['value'] + res_output_counts_shape = graph.node['output_counts']['shape'] + res_output_counts_value = graph.node['output_counts']['value'] + + # verify the results + self.assertTrue(np.array_equal(ref_output_uniques_shape, res_output_uniques_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_output_uniques_shape, res_output_uniques_shape)) + self.assertTrue(np.array_equal(ref_output_uniques_value, res_output_uniques_value), + 'values do not match expected: {} and given: {}'.format(ref_output_uniques_value, res_output_uniques_value)) + self.assertTrue(np.array_equal(ref_output_indices_shape, res_output_indices_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_output_indices_shape, res_output_indices_shape)) + self.assertTrue(np.array_equal(ref_output_indices_value, res_output_indices_value), + 'values do not match expected: {} and given: {}'.format(ref_output_indices_value, res_output_indices_value)) + self.assertTrue(np.array_equal(ref_output_counts_shape, res_output_counts_shape), + 'shapes do not match expected: {} and given: {}'.format(ref_output_counts_shape, res_output_counts_shape)) + self.assertTrue(np.array_equal(ref_output_counts_value, res_output_counts_value), + 'values do not match expected: {} and given: {}'.format(ref_output_counts_value, res_output_counts_value)) diff --git a/model-optimizer/mo/back/ie_ir_ver_2/emitter.py b/model-optimizer/mo/back/ie_ir_ver_2/emitter.py index 2d318eed95365c..0d68c9de5da968 100644 --- a/model-optimizer/mo/back/ie_ir_ver_2/emitter.py +++ b/model-optimizer/mo/back/ie_ir_ver_2/emitter.py @@ -402,8 +402,8 @@ def generate_ie_ir(graph: Graph, file_name: str, input_names: tuple = (), mean_o unsupported.report(log.error, "List of operations that cannot be converted to Inference Engine IR:") raise Error('Part of the nodes was not converted to IR. Stopped. ' + refer_to_faq_msg(24)) - with open(file_name, 'w') as file: - file.write(pretty_xml_as_string) + with open(file_name, 'wb') as file: + file.write(bytes(pretty_xml_as_string, "UTF-8")) def port_renumber(graph: Graph): diff --git a/model-optimizer/mo/front/common/partial_infer/concat.py b/model-optimizer/mo/front/common/partial_infer/concat.py index 372a124c207fea..d6ada30d2340d5 100644 --- a/model-optimizer/mo/front/common/partial_infer/concat.py +++ b/model-optimizer/mo/front/common/partial_infer/concat.py @@ -67,7 +67,7 @@ def concat_infer(node): if any(v is None for v in values): return - node.out_node(0).value = np.concatenate(values, axis=node.axis) + node.out_node(0).value = np.array(np.concatenate(values, axis=node.axis), dtype=values[0].dtype) node.out_node(0).shape = np.array(node.out_node(0).value.shape, dtype=np.int64) diff --git a/model-optimizer/mo/front/common/partial_infer/eltwise.py b/model-optimizer/mo/front/common/partial_infer/eltwise.py index 12d4b800adfcdb..7cfdb157070041 100644 --- a/model-optimizer/mo/front/common/partial_infer/eltwise.py +++ b/model-optimizer/mo/front/common/partial_infer/eltwise.py @@ -14,9 +14,8 @@ limitations under the License. """ -import numpy as np -import logging as log import networkx as nx +import numpy as np from mo.front.common.partial_infer.utils import int64_array from mo.graph.graph import Node @@ -90,3 +89,10 @@ def eltwise_infer(node, op=None, **kwargs): node.out_node().value = values[0] for i in range(len(values) - 1): node.out_node().value = op(node.out_node().value, values[i + 1]) + + +def bias_add_infer(node, op): + if node.in_port(0).data.get_value() is not None and node.in_port(1).data.get_value() is not None and op is not None: + node.out_port(0).data.set_value(op(node.in_port(0).data.get_value(), node.in_port(1).data.get_value())) + else: + node.out_port(0).data.set_shape(node.in_port(0).data.get_shape()) diff --git a/model-optimizer/mo/front/common/partial_infer/multi_box_detection.py b/model-optimizer/mo/front/common/partial_infer/multi_box_detection.py index 755451a041eca0..0efc2804f0e85c 100644 --- a/model-optimizer/mo/front/common/partial_infer/multi_box_detection.py +++ b/model-optimizer/mo/front/common/partial_infer/multi_box_detection.py @@ -26,6 +26,10 @@ def multi_box_detection_infer(node: Node): conf_shape = node.in_node(1).shape prior_boxes_shape = node.in_node(2).shape + if loc_shape is None or conf_shape is None or prior_boxes_shape is None: + log.warning('Shapes for the Detection Output are not defined') + return + prior_size = 4 if node.has('normalized') and not node.normalized: prior_size = 5 @@ -42,10 +46,6 @@ def multi_box_detection_infer(node: Node): if node.has_and_set('share_location') and node.share_location: num_loc_classes = 1 - if loc_shape is None or conf_shape is None or prior_boxes_shape is None: - log.warning('Shapes for the Detection Output are not defined') - return - if num_priors * num_loc_classes * 4 != loc_shape[-1]: log.warning('Locations and prior boxes shapes mismatch: "{}" vs "{}"'.format(loc_shape, prior_boxes_shape)) return diff --git a/model-optimizer/mo/front/common/partial_infer/space_to_batch.py b/model-optimizer/mo/front/common/partial_infer/space_to_batch.py index d1573641b5a7c5..00f33003903d1c 100644 --- a/model-optimizer/mo/front/common/partial_infer/space_to_batch.py +++ b/model-optimizer/mo/front/common/partial_infer/space_to_batch.py @@ -16,6 +16,8 @@ import numpy as np +from mo.front.common.partial_infer.utils import int64_array + def space_to_batch_infer(node): """ @@ -36,8 +38,9 @@ def space_to_batch_infer(node): pads = pad[:, 0] + input_shape[1:len(block_size)+1] + pad[:, 1] - output_shape = [input_shape[0] * np.prod(block_size), *[int(x) for x in (pads / block_size)], input_shape[-1]] - node.out_node().shape = np.array(output_shape) + node.out_node().shape = int64_array([input_shape[0] * np.prod(block_size), + *[int(x) for x in (pads / block_size)], + *input_shape[len(block_size) + 1:]]) def batch_to_space_infer(node): @@ -62,5 +65,4 @@ def batch_to_space_infer(node): sizes = pads - crop[:, 0] - crop[:, 1] batch = int(input_shape[0] / (np.prod(block_size))) - output_shape = [batch, *sizes, input_shape[-1]] - node.out_node().shape = np.array(output_shape) + node.out_node().shape = int64_array([batch, *sizes, *input_shape[len(block_size) + 1:]]) diff --git a/model-optimizer/mo/front/common/partial_infer/split.py b/model-optimizer/mo/front/common/partial_infer/split.py index 6e081471f62344..0dbb3cd0efddf9 100644 --- a/model-optimizer/mo/front/common/partial_infer/split.py +++ b/model-optimizer/mo/front/common/partial_infer/split.py @@ -88,8 +88,10 @@ def split(input_data_node: Node, node: Node, axis: int, part_sizes: list): return splitted = None - if input_data_node.value is not None: - splitted = np.split(input_data_node.value, part_sizes_to_indices(part_sizes), axis) + input_value = input_data_node.value + if input_value is not None: + splitted = [np.array(part, dtype=input_value.dtype) + for part in np.split(input_value, part_sizes_to_indices(part_sizes), axis)] # not all outputs from the split could be used so it is necessary to iterate over output edges and infer shape for # necessary nodes only @@ -104,7 +106,6 @@ def split(input_data_node: Node, node: Node, axis: int, part_sizes: list): out_node.value = splitted[out_port] assert all(out_node.value.shape == out_node.shape) - assert not node.has_valid('axis') or node.axis == axis node.axis = axis # WARNING: != 4 is supposed to work for NHWC to NCHW translation only. # if other global permutations happen this will fail diff --git a/model-optimizer/mo/front/kaldi/extractors/pnorm_component_ext.py b/model-optimizer/mo/front/kaldi/extractors/pnorm_component_ext.py new file mode 100644 index 00000000000000..80b69073bd4794 --- /dev/null +++ b/model-optimizer/mo/front/kaldi/extractors/pnorm_component_ext.py @@ -0,0 +1,58 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from mo.front.extractor import FrontExtractorOp +from mo.front.kaldi.loader.utils import collect_until_token, read_binary_integer32_token, read_binary_float_token +from extensions.ops.pnorm import PNormOp +from mo.utils.error import Error + + +class PNormComponentFrontExtractor(FrontExtractorOp): + op = 'pnormcomponent' + enabled = True + + @staticmethod + def extract(node): + pb = node.parameters + try: + collect_until_token(pb, b'') + except Error: + raise Error(" was not found") + in_dim = read_binary_integer32_token(pb) + + try: + collect_until_token(pb, b'') + except Error: + raise Error(" was not found") + out_dim = read_binary_integer32_token(pb) + + assert in_dim % out_dim == 0 + + group = in_dim / out_dim + + try: + collect_until_token(pb, b'

') + except Error: + raise Error("

was not found") + p = read_binary_float_token(pb) + + attrs = { + 'group': group, + 'p': p, + } + + PNormOp.update_node_stat(node, attrs) + return __class__.enabled diff --git a/model-optimizer/mo/front/kaldi/extractors/pnorm_component_ext_test.py b/model-optimizer/mo/front/kaldi/extractors/pnorm_component_ext_test.py new file mode 100644 index 00000000000000..5e725f5769de16 --- /dev/null +++ b/model-optimizer/mo/front/kaldi/extractors/pnorm_component_ext_test.py @@ -0,0 +1,41 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import numpy as np + +from extensions.ops.pnorm import PNormOp +from mo.front.kaldi.extractors.pnorm_component_ext import PNormComponentFrontExtractor +from mo.front.kaldi.extractors.common_ext_test import KaldiFrontExtractorTest +from mo.front.kaldi.loader.utils_test import TestKaldiUtilsLoading +from mo.ops.op import Op + + +class PNormComponentFrontExtractorTest(KaldiFrontExtractorTest): + @classmethod + def register_op(cls): + Op.registered_ops['pnorm'] = PNormOp + + @classmethod + def create_pb_for_test_node(cls): + pb = KaldiFrontExtractorTest.write_tag_with_value('', 3500) + pb += KaldiFrontExtractorTest.write_tag_with_value('', 350) + pb += KaldiFrontExtractorTest.write_tag_with_value('

', 2, np.float32) + cls.test_node['parameters'] = TestKaldiUtilsLoading.bytesio_from(pb) + + def test_extract(self): + PNormComponentFrontExtractor.extract(self.test_node) + self.assertEqual(self.test_node['p'], 2) + self.assertEqual(self.test_node['group'], 10) diff --git a/model-optimizer/mo/front/kaldi/extractors/splice_component_ext.py b/model-optimizer/mo/front/kaldi/extractors/splice_component_ext.py index da39914381a6c5..4a0b95f53edbf0 100644 --- a/model-optimizer/mo/front/kaldi/extractors/splice_component_ext.py +++ b/model-optimizer/mo/front/kaldi/extractors/splice_component_ext.py @@ -49,5 +49,12 @@ def extract(node): mapping_rule['context'] = read_binary_vector(pb, False, dtype=np.int32) else: raise Error('Unknown token {} in SpliceComponent node {}'.format(tag, node.id)) + + tag = find_next_tag(pb) + if tag == '': + read_placeholder(pb, 1) + const_dim = read_binary_integer32_token(pb) + mapping_rule['const_dim'] = const_dim + Splice.update_node_stat(node, mapping_rule) return __class__.enabled diff --git a/model-optimizer/mo/front/kaldi/loader/loader.py b/model-optimizer/mo/front/kaldi/loader/loader.py index 288a4dc199a719..232e280dead425 100644 --- a/model-optimizer/mo/front/kaldi/loader/loader.py +++ b/model-optimizer/mo/front/kaldi/loader/loader.py @@ -81,8 +81,9 @@ def load_parallel_component(file_descr, graph: Graph, prev_layer_id): for i in range(nnet_count): read_token_value(file_descr, b'') collect_until_token(file_descr, b'') - g, shape = load_kalid_nnet1_model(file_descr, 'Nested_net_{}'.format(i)) + g = load_kalid_nnet1_model(file_descr, 'Nested_net_{}'.format(i)) input_nodes = [n for n in graph.nodes(data=True) if n[1]['op'] == 'Parameter'] + shape = input_nodes[0][1]['shape'] if i != nnet_count - 1: slices_points.append(shape[1]) g.remove_node(input_nodes[0][0]) @@ -157,7 +158,6 @@ def load_kalid_nnet1_model(file_descr, name): prev_layer_id = 'Parameter' graph.add_node(prev_layer_id, name=prev_layer_id, kind='op', op='Parameter', parameters=None) - input_shape = np.array([]) while True: component_type = find_next_component(file_descr) @@ -185,13 +185,13 @@ def load_kalid_nnet1_model(file_descr, name): prev_node = Node(graph, prev_layer_id) if prev_node.op == 'Parameter': prev_node['shape'] = np.array([1, layer_i], dtype=np.int64) - input_shape = np.array([1, layer_i], dtype=np.int64) + prev_node.add_output_port(0) Node(graph, layer_id).add_input_port(0) graph.create_edge(prev_node, Node(graph, layer_id), 0, 0) prev_layer_id = layer_id log.debug('{} (type is {}) was loaded'.format(prev_layer_id, component_type)) - return graph, input_shape + return graph def load_kalid_nnet2_model(file_descr, nnet_name): @@ -203,38 +203,35 @@ def load_kalid_nnet2_model(file_descr, nnet_name): all_components = load_components(file_descr, graph) - input_shape = np.array([]) - for layer_id in all_components: prev_node = Node(graph, prev_layer_id) if prev_node.op == 'Parameter': parameters = Node(graph, layer_id).parameters input_dim = read_token_value(parameters, b'') prev_node['shape'] = np.array([1, input_dim], dtype=np.int64) - input_shape = np.array([1, input_dim], dtype=np.int64) prev_node.add_output_port(0) Node(graph, layer_id).add_input_port(0) graph.create_edge(prev_node, Node(graph, layer_id), 0, 0) prev_layer_id = layer_id log.debug('{} and {} were connected'.format(prev_layer_id, layer_id)) - return graph, input_shape + return graph def load_kaldi_nnet3_model(file_descr, nnet_name): graph = Graph(name=nnet_name) file_descr.read(1) - component_layer_map, input_shape, input_name = load_topology_map(file_descr, graph) + component_layer_map = load_topology_map(file_descr, graph) # add information for shape calculation for MemoryOffset # shape calculation for MemoryOffset can't be done through shape of previous layer because # it is separated in 2 parts to remove cycle from graph - node = Node(graph, input_name) - for o_n_name, params in node.get_outputs(): - o_n = Node(graph, o_n_name) - if o_n['op'] == 'MemoryOffset': - o_n['parameters']['element_size'] = input_shape[1] + for node in graph.get_op_nodes(**{'op': 'Parameter'}): + for o_n_name, params in node.get_outputs(): + o_n = Node(graph, o_n_name) + if o_n['op'] == 'MemoryOffset': + o_n['parameters']['element_size'] = node['shape'][1] load_components(file_descr, graph, component_layer_map) - return graph, input_shape + return graph def load_components(file_descr, graph, component_layer_map=None): @@ -308,18 +305,15 @@ def load_topology_map(file_descr, graph): not_finished = True component_layer_map = {} layer_node_map = {} - input_shape = np.array([], dtype=np.int64) - input_name = "" while not_finished: - not_finished, input_shape, input_name = read_node(file_descr, graph, component_layer_map, layer_node_map, - input_shape, input_name) - return component_layer_map, input_shape, input_name + not_finished = read_node(file_descr, graph, component_layer_map, layer_node_map) + return component_layer_map -def read_node(file_descr, graph, component_layer_map, layer_node_map, input_shape, input_name): +def read_node(file_descr, graph, component_layer_map, layer_node_map): s = file_descr.readline() if s == b'\n': - return False, input_shape, input_name + return False tokens = s.split(b' ') if tokens[0] == b'input-node': in_name = s[s.find(b'name=')+len(b'name='):].split(b' ')[0] @@ -332,9 +326,6 @@ def read_node(file_descr, graph, component_layer_map, layer_node_map, input_shap else: Node(graph, in_name)['op'] = 'Parameter' Node(graph, in_name)['shape'] = in_shape - - input_shape = in_shape - input_name = in_name elif tokens[0] == b'component-node': layer_name = s[s.find(b'name=')+len(b'name='):].split(b' ')[0] layer_name = str(layer_name).strip('b').replace('\'', "") @@ -430,7 +421,7 @@ def read_node(file_descr, graph, component_layer_map, layer_node_map, input_shap o_n['parameters']['element_size'] = dim else: raise Error("Unsupported node specifier {}".format(tokens[0])) - return True, input_shape, input_name + return True def parse_input_for_node(string, graph, component_layer_map): @@ -536,11 +527,5 @@ def parse_specifier(string, graph, layer_node_map): node['parameters']['has_default'] = True return node_id elif spec == b'ReplaceIndex': - spec_name = graph.unique_id(prefix='ReplaceIndex_') - graph.add_node(spec_name, - parameters=dict(), - op='ReplaceIndex', - kind='op') node = parse_specifier(args[0], graph, layer_node_map) - graph.add_edge(node, spec_name, **create_edge_attrs(node, spec_name)) - return spec_name + return node diff --git a/model-optimizer/mo/front/kaldi/loader/loader_test.py b/model-optimizer/mo/front/kaldi/loader/loader_test.py index d9b7a2243dd740..b3f1b269ba3dfe 100644 --- a/model-optimizer/mo/front/kaldi/loader/loader_test.py +++ b/model-optimizer/mo/front/kaldi/loader/loader_test.py @@ -19,7 +19,7 @@ import unittest from mo.front.kaldi.loader.loader import load_topology_map, load_components -from mo.graph.graph import Graph +from mo.graph.graph import Graph, Node from mo.utils.unittest.graph import build_graph, compare_graphs @@ -33,15 +33,15 @@ def test_component_map_loading_sequence(self): "component-node name=tdnn1.batchnorm component=tdnn1.batchnorm input=tdnn1.relu \n\n" graph = Graph(name="test_graph_component_map_loading_sequence") - test_top_map, input_shape, input_name = load_topology_map(io.BytesIO(bytes(test_map, 'ascii')), graph) + test_top_map = load_topology_map(io.BytesIO(bytes(test_map, 'ascii')), graph) ref_map = {b"lda": ["lda"], b"tdnn1.affine": ["tdnn1.affine"], b"tdnn1.relu": ["tdnn1.relu"], b"tdnn1.batchnorm": ["tdnn1.batchnorm"]} self.assertEqual(test_top_map, ref_map) - self.assertListEqual(list(input_shape), [1, 16]) - self.assertEquals(input_name, "input") + self.assertTrue("input" in graph.nodes()) + self.assertListEqual(list(Node(graph, 'input')['shape']), [1, 16]) ref_graph = build_graph({'input': {'shape': np.array([1, 16]), 'kind': 'op', 'op': 'Parameter'}, 'lda': {'kind': 'op'}, @@ -70,15 +70,15 @@ def test_component_map_loading_swap(self): "\n" graph = Graph(name="test_graph_component_map_loading_swap") - test_top_map, input_shape, input_name = load_topology_map(io.BytesIO(bytes(test_map, 'ascii')), graph) + test_top_map = load_topology_map(io.BytesIO(bytes(test_map, 'ascii')), graph) ref_map = {b"lda": ["lda"], b"tdnn1.affine": ["tdnn1.affine"], b"tdnn1.relu": ["tdnn1.relu"], b"tdnn1.batchnorm": ["tdnn1.batchnorm"]} self.assertEqual(test_top_map, ref_map) - self.assertListEqual(list(input_shape), [1, 16]) - self.assertEquals(input_name, "input") + self.assertTrue("input" in graph.nodes()) + self.assertListEqual(list(Node(graph, 'input')['shape']), [1, 16]) ref_graph = build_graph({'input': {'shape': np.array([1, 16]), 'kind': 'op', 'op': 'Parameter'}, 'lda': {'kind': 'op'}, @@ -104,14 +104,14 @@ def test_component_map_loading_append(self): "\n" graph = Graph(name="test_graph_component_map_loading_append") - test_top_map, input_shape, input_name = load_topology_map(io.BytesIO(bytes(test_map, 'ascii')), graph) + test_top_map= load_topology_map(io.BytesIO(bytes(test_map, 'ascii')), graph) ref_map = {b"lda": ["lda"], b"tdnn1.affine": ["tdnn1.affine"], b"tdnn1.relu": ["tdnn1.relu"]} self.assertEqual(test_top_map, ref_map) - self.assertListEqual(list(input_shape), [1, 16]) - self.assertEqual(input_name, "input") + self.assertTrue("input" in graph.nodes()) + self.assertListEqual(list(Node(graph, 'input')['shape']), [1, 16]) ref_graph = build_graph({'input': {'shape': np.array([1, 16]), 'kind': 'op', 'op': 'Parameter'}, 'lda': {'kind': 'op'}, @@ -143,14 +143,14 @@ def test_component_map_loading_offset(self): "\n" graph = Graph(name="test_graph_component_map_loading_offset") - test_top_map, input_shape, input_name = load_topology_map(io.BytesIO(bytes(test_map, 'ascii')), graph) + test_top_map= load_topology_map(io.BytesIO(bytes(test_map, 'ascii')), graph) ref_map = {b"lda": ["lda"], b"tdnn1.affine": ["tdnn1.affine"], b"tdnn1.relu": ["tdnn1.relu"]} self.assertEqual(test_top_map, ref_map) - self.assertListEqual(list(input_shape), [1, 16]) - self.assertEqual(input_name, "input") + self.assertTrue("input" in graph.nodes()) + self.assertListEqual(list(Node(graph, 'input')['shape']), [1, 16]) ref_graph = build_graph({'input': {'shape': np.array([1, 16]), 'kind': 'op', 'op': 'Parameter'}, 'lda': {'kind': 'op'}, diff --git a/model-optimizer/mo/front/kaldi/loader/utils.py b/model-optimizer/mo/front/kaldi/loader/utils.py index c7ac3ec280a316..550236802522d3 100644 --- a/model-optimizer/mo/front/kaldi/loader/utils.py +++ b/model-optimizer/mo/front/kaldi/loader/utils.py @@ -28,35 +28,37 @@ supported_components = [ 'addshift', 'affinecomponent', + 'affinecomponentpreconditionedonline', 'affinetransform', + 'backproptruncationcomponent', + 'batchnormcomponent', + 'clipgradientcomponent', 'convolutional1dcomponent', 'convolutionalcomponent', 'copy', + 'elementwiseproductcomponent', 'fixedaffinecomponent', + 'linearcomponent', + 'logsoftmaxcomponent', + 'lstmnonlinearitycomponent', 'lstmprojected', 'lstmprojectedstreams', 'maxpoolingcomponent', + 'naturalgradientaffinecomponent', + 'naturalgradientperelementscalecomponent', + 'noopcomponent', + 'normalizecomponent', 'parallelcomponent', + 'pnormcomponent', + 'rectifiedlinearcomponent', 'rescale', 'sigmoid', + 'sigmoidcomponent', 'softmax', 'softmaxcomponent', 'splicecomponent', + 'sumgroupcomponent', 'tanhcomponent', - 'normalizecomponent', - 'affinecomponentpreconditionedonline', - 'rectifiedlinearcomponent', - 'batchnormcomponent', - 'naturalgradientaffinecomponent', - 'logsoftmaxcomponent', - 'naturalgradientperelementscalecomponent', - 'sigmoidcomponent', - 'tanhcomponent', - 'elementwiseproductcomponent', - 'clipgradientcomponent', - 'noopcomponent', - 'lstmnonlinearitycomponent', - 'backproptruncationcomponent', ] @@ -191,6 +193,7 @@ def find_next_component(file_desc: io.BufferedReader) -> str: :param file_desc:file descriptor :return: string like '' """ + is_start = True while True: tag = find_next_tag(file_desc) # Tag is . But we want get without '<' and '>' @@ -201,6 +204,9 @@ def find_next_component(file_desc: io.BufferedReader) -> str: return component_name elif tag == '': raise Error('Component has unsupported or not specified type') + elif not (is_start and tag == end_of_component_tag) and tag.find('Component') != -1: + raise Error('Component has unsupported type {}'.format(tag)) + is_start = False def get_name_from_path(path: str) -> str: diff --git a/model-optimizer/mo/front/kaldi/loader/utils_test.py b/model-optimizer/mo/front/kaldi/loader/utils_test.py index 47e625849e3649..1b221c0d3a8457 100644 --- a/model-optimizer/mo/front/kaldi/loader/utils_test.py +++ b/model-optimizer/mo/front/kaldi/loader/utils_test.py @@ -88,9 +88,14 @@ def test_find_next_component(self): test_file = b'somefakeinfoinfo' + component + b'' self.assertEqual(find_next_component(self.bytesio_from(test_file)), component.decode('ascii').lower()[1:-1]) + def test_find_next_component_eoc(self): + component = b'' + test_file = b'' + component + b'' + self.assertEqual(find_next_component(self.bytesio_from(test_file)), component.decode('ascii').lower()[1:-1]) + def test_find_next_component_end_of_nnet(self): test_file = b'somefakeinfoinfo' - self.assertEqual(find_next_component(self.bytesio_from(test_file)), end_of_nnet_tag.lower()[1:-1]) + self.assertRaises(Error, find_next_component, self.bytesio_from(test_file)) def test_find_end_of_component(self): component = '' diff --git a/model-optimizer/mo/front/mxnet/extractor.py b/model-optimizer/mo/front/mxnet/extractor.py index 86a0c171a7958b..12c0888adf04ba 100644 --- a/model-optimizer/mo/front/mxnet/extractor.py +++ b/model-optimizer/mo/front/mxnet/extractor.py @@ -37,7 +37,6 @@ def extractor_wrapper(mxnet_extractor): mxnet_op_extractors = { 'BatchNorm': extractor_wrapper(batch_norm_ext), - 'Crop': extractor_wrapper(crop_ext), 'ScaleShift': extractor_wrapper(scale_shift_ext), 'slice_axis': extractor_wrapper(slice_axis_ext), 'null': lambda node: null_ext(node.symbol_dict), diff --git a/model-optimizer/mo/front/tf/extractor.py b/model-optimizer/mo/front/tf/extractor.py index 006b07eaaef2f2..ab17ee131dd5cc 100644 --- a/model-optimizer/mo/front/tf/extractor.py +++ b/model-optimizer/mo/front/tf/extractor.py @@ -73,6 +73,7 @@ def node_pb_arg(pb_extractor: callable): 'ConcatV2': node_pb_arg(tf_concat_ext), 'MatMul': node_pb_arg(tf_matmul_ext), 'BatchMatMul': node_pb_arg(tf_batchmatmul_ext), + 'BatchMatMulV2': node_pb_arg(tf_batchmatmul_ext), 'Pack': node_pb_arg(tf_pack_ext), 'Unpack': node_pb_arg(tf_unpack_ext), 'Const': node_pb_arg(tf_const_ext), diff --git a/model-optimizer/mo/graph/graph.py b/model-optimizer/mo/graph/graph.py index adbd902a0cb0ec..6f5d1a1672a5b9 100644 --- a/model-optimizer/mo/graph/graph.py +++ b/model-optimizer/mo/graph/graph.py @@ -145,6 +145,12 @@ def has_port(self, port_type, idx, control_flow=False): else: return self.has_valid('_out_ports') and idx in self.out_ports(control_flow=control_flow) + def is_in_port_connected(self, idx, control_flow=False): + return self.has_port('in', idx, control_flow) and not self.in_port(idx, control_flow).disconnected() + + def is_out_port_connected(self, idx, control_flow=False): + return self.has_port('out', idx, control_flow) and not self.out_port(idx, control_flow).disconnected() + def attrs(self): return self.graph.node[self.node] @@ -240,8 +246,8 @@ def get_sorted_outputs(self, control_flow: bool = False): return sorted([x for x in self.get_outputs(control_flow=control_flow) if 'out' in x[1]], key=lambda x: x[1]['out']) - def soft_get(self, k): - return self[k] if self.has_valid(k) else '' + def soft_get(self, k, default=''): + return self[k] if self.has_valid(k) else default def edges(self, attrs: dict = None): """ Get a single edge with specified set of attributes. @@ -911,7 +917,8 @@ def dict_includes_compare_attrs(attr, attr_probe): if callable(attr_probe) and not isinstance(attr_probe, type): return attr_probe(attr) else: - return attr == attr_probe + res = (attr == attr_probe) + return res if isinstance(res, bool) else all(res) def dict_includes(big: dict, sub_dict: dict, skip_attr_names=[]): diff --git a/model-optimizer/mo/graph/port.py b/model-optimizer/mo/graph/port.py index e24ad442bcfb68..8512bd33ab1947 100644 --- a/model-optimizer/mo/graph/port.py +++ b/model-optimizer/mo/graph/port.py @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. """ +import numpy as np from copy import deepcopy from mo.front.common.partial_infer.utils import int64_array @@ -107,7 +108,8 @@ def _set_shape(self, shape): assert self.node.in_node(self.idx, control_flow=self.control_flow).value is None self.node.in_node(self.idx, control_flow=self.control_flow).shape = int64_array(shape) else: - assert self.node.out_node(self.idx, control_flow=self.control_flow).value is None + data_node = self.node.out_node(self.idx, control_flow=self.control_flow) + assert data_node.value is None or np.array_equal(data_node.shape, int64_array(shape)) self.node.out_node(self.idx, control_flow=self.control_flow).shape = int64_array(shape) def _get_value(self): diff --git a/model-optimizer/mo/middle/passes/convert_data_type.py b/model-optimizer/mo/middle/passes/convert_data_type.py index 3f28bd9bceb3de..ce785dbf448dc1 100644 --- a/model-optimizer/mo/middle/passes/convert_data_type.py +++ b/model-optimizer/mo/middle/passes/convert_data_type.py @@ -41,6 +41,13 @@ def data_type_str_to_precision(data_type_str: str): return SUPPORTED_DATA_TYPES[data_type_str][1] if data_type_str in SUPPORTED_DATA_TYPES else None +def np_data_type_to_precision(np_data_type): + for np_t, precision in SUPPORTED_DATA_TYPES.values(): + if np_t == np_data_type: + return precision + raise Error('Data type "{}" is not supported'.format(np_data_type)) + + def convert_blob(graph: Graph, node: Node, data_type: type, force_precision: str): out_edges = graph.out_edges(node.node, data=True) diff --git a/model-optimizer/mo/middle/passes/eliminate.py b/model-optimizer/mo/middle/passes/eliminate.py index cc90cff5482311..ab860df649a4f9 100644 --- a/model-optimizer/mo/middle/passes/eliminate.py +++ b/model-optimizer/mo/middle/passes/eliminate.py @@ -158,10 +158,13 @@ def shape_inference(graph: Graph): old_out_shapes = [port.data.get_shape() for port in node.out_ports().values() if not port.disconnected()] node.infer(node) new_out_shapes = [port.data.get_shape() for port in node.out_ports().values() if not port.disconnected()] - for shape1, shape2 in zip(old_out_shapes, new_out_shapes): - if shape1 is not None and not np.array_equal(shape1, shape2): - raise Error("After partial shape inference were found shape collision for node {} (old shape: {}, " - "new shape: {})".format(node.name, shape1, shape2)) + if not node.has_and_set('override_output_shape'): + for shape1, shape2 in zip(old_out_shapes, new_out_shapes): + if shape1 is not None and not np.array_equal(shape1, shape2): + raise Error("After partial shape inference were found shape collision for node {} (old shape: " + "{}, new shape: {})".format(node.name, shape1, shape2)) + else: + del node['override_output_shape'] node.need_shape_inference = False diff --git a/model-optimizer/mo/ops/broadcast.py b/model-optimizer/mo/ops/broadcast.py index b3894a50d7aaf6..d2d8070b0a87d3 100644 --- a/model-optimizer/mo/ops/broadcast.py +++ b/model-optimizer/mo/ops/broadcast.py @@ -49,7 +49,13 @@ def __init__(self, graph: Graph, attrs: dict): @staticmethod def infer(node: Node): # TODO Add necessary checks and asserts - node.out_node().shape = node.in_node(1).value + b_value = node.in_port(0).data.get_value() + b_shape = node.in_port(1).data.get_value() + assert b_shape is not None + node.out_port(0).data.set_shape(b_shape) + PermuteInputs().set_input_permutation(node.in_node(1), node, 'output:0', 'shape') - if node.in_node(0).value is not None and node.in_node(1).value is not None: - node.out_node().value = np.broadcast_to(node.in_node(0).value, node.in_node(1).value) + if b_value is not None: + new_value = np.broadcast_to(b_value, b_shape) + node.out_port(0).data.set_value(new_value) + diff --git a/model-optimizer/mo/ops/constant_of_shape.py b/model-optimizer/mo/ops/constant_of_shape.py new file mode 100644 index 00000000000000..ef17fa8e52ec6e --- /dev/null +++ b/model-optimizer/mo/ops/constant_of_shape.py @@ -0,0 +1,41 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from mo.graph.graph import Graph +from mo.ops.op import Op + + +class ConstantOfShape(Op): + """ Create a tensor of the shape specified in the first input with all values equal to attribute 'value'. + The operation is converted to Broadcast operation + """ + + op = 'ConstantOfShape' + enabled = True + + def __init__(self, graph: Graph, attrs: dict): + super().__init__(graph, { + 'kind': 'op', + 'type': None, + 'op': __class__.op, + 'in_ports_count': 1, + 'out_ports_count': 1, + 'fill_value': 0, + 'infer': None, + }, attrs) + + def supported_attrs(self): + return ['fill_value'] diff --git a/model-optimizer/mo/ops/crop.py b/model-optimizer/mo/ops/crop.py index d314ba7fd9608d..8539b168573bf1 100644 --- a/model-optimizer/mo/ops/crop.py +++ b/model-optimizer/mo/ops/crop.py @@ -90,7 +90,11 @@ def _one_input_infer(node: Node): if len(node.crop_begin) != len(node.axis) or len(node.crop_end) != len(node.axis): log.error('number of crop_begin/crop_end should match number of axis') return - output_shape[node.axis] = output_shape[node.axis] - node.crop_begin - node.crop_end + if type(node.axis) in [list, tuple]: + for i in range(len(node.axis)): + output_shape[node.axis[i]] = output_shape[node.axis[i]] - node.crop_begin[i] - node.crop_end[i] + else: + output_shape[node.axis] = output_shape[node.axis] - node.crop_begin - node.crop_end else: log.error('Crop node {} should have either dim or crop_begin and crop_end attributes'.format(node.name)) return diff --git a/model-optimizer/mo/ops/expand_dims.py b/model-optimizer/mo/ops/expand_dims.py index 22215bac24695e..5ad5f4601f9fe9 100644 --- a/model-optimizer/mo/ops/expand_dims.py +++ b/model-optimizer/mo/ops/expand_dims.py @@ -18,7 +18,7 @@ from mo.front.common.partial_infer.utils import int64_array from mo.graph.graph import Node -from mo.ops.op import Op, PermuteAttrs +from mo.ops.op import Op from mo.utils.error import Error @@ -75,4 +75,4 @@ def infer(node: Node): # convert data type of the shape to int64 explicitly output_node.shape = output_node.shape.astype(np.int64) if input_node.value is not None: - output_node.value = np.array(np.reshape(input_node.value, output_node.shape)) + output_node.value = input_node.value.reshape(output_node.shape) diff --git a/model-optimizer/mo/ops/memoryoffset.py b/model-optimizer/mo/ops/memoryoffset.py index 515bb5cbd15f9f..da9f5268fecba2 100644 --- a/model-optimizer/mo/ops/memoryoffset.py +++ b/model-optimizer/mo/ops/memoryoffset.py @@ -43,33 +43,24 @@ def infer(node: Node): # MemoryOffset is splitted in 2 parts to avoid cycle in graph # Calculate shape from shape of previous layer where possible # In other cases information about shapes from initial Kaldi model used - if len(node.in_nodes()) > 0: + if not node.in_port(0).disconnected(): copy_shape_infer(node) pair_node = Node(node.graph, node.pair_name) - for out_node_name, params in pair_node.get_outputs(): - out_node = Node(node.graph, out_node_name) - out_node.shape = node.out_node().shape + pair_node.out_port(0).data.set_shape(node.out_port(0).data.get_shape()) else: pair_node = Node(node.graph, node.pair_name) - if pair_node.in_node().shape is not None: - for out_node_name, params in node.get_outputs(): - out_node = Node(node.graph, out_node_name) - out_node.shape = pair_node.in_node().shape + if pair_node.in_port(0).data.get_shape() is not None: + node.out_port(0).data.set_shape(pair_node.in_port(0).data.get_shape()) copy_shape_infer(pair_node) elif pair_node.has_valid('element_size'): # TODO Add here real batch - for out_node_name, params in node.get_outputs(): - out_node = Node(node.graph, out_node_name) - out_node.shape = np.array([1, pair_node['element_size']]) - elif pair_node.in_node().in_node().op == 'FullyConnected': - out_size = pair_node.in_node().in_node()['out-size'] - for out_node_name, params in node.get_outputs(): - out_node = Node(node.graph, out_node_name) - out_node.shape = np.array([1, out_size]) - elif pair_node.in_node().in_node().op == 'Normalize': - out_size = pair_node.in_node().in_node()['in_dim'] - for out_node_name, params in node.get_outputs(): - out_node = Node(node.graph, out_node_name) - out_node.shape = np.array([1, out_size]) + node.out_port(0).data.set_shape(np.array([1, pair_node['element_size']])) + elif pair_node.in_port(0).get_source().node.has_valid('out-size'): + out_size = pair_node.in_port(0).get_source().node['out-size'] + node.out_port(0).data.set_shape(np.array([1, out_size])) + elif pair_node.in_port(0).get_source().node.has_valid('in_dim'): + out_size = pair_node.in_port(0).get_source().node['in_dim'] + node.out_port(0).data.set_shape(np.array([1, out_size])) else: - raise Error("Can't calculate MemoryOffset shape for node {}. Possibly you need to add shape for it through --input_shape".format(node.id)) + raise Error("Can't calculate MemoryOffset shape for node {}. ".format(node.id) + + "Possibly you need to add shape for it through --input_shape") diff --git a/model-optimizer/mo/ops/slice.py b/model-optimizer/mo/ops/slice.py index fda2acd3352ba1..f146aaacc560a1 100644 --- a/model-optimizer/mo/ops/slice.py +++ b/model-optimizer/mo/ops/slice.py @@ -40,8 +40,10 @@ def supported_attrs(self): @staticmethod def infer(node: Node): + axis = None + steps = None if len(node.in_nodes()) == 1: - # Caffe or ONNX + # Caffe or ONNX before 10 opset if node.has('start') and node.has('end') and node.has('axis'): # ONNX case if node.has_valid('start') and node.has_valid('end') and node.has('axis'): @@ -55,26 +57,49 @@ def infer(node: Node): # Caffe case from mo.front.common.partial_infer.slice import caffe_slice_infer caffe_slice_infer(node) - elif len(node.in_nodes()) == 3: - # TF case - start_node = node.in_node(1) - size_node = node.in_node(2) - if start_node.has_valid('value') and size_node.has_valid('value'): - start = np.array(node.in_node(1).value, dtype=np.int64) - size = np.array(node.in_node(2).value, dtype=np.int64) - end = start + size - axis = None - - # Delete edges to start, size nodes - node.graph.remove_edge(node.in_node(1).id, node.id) - node.graph.remove_edge(node.in_node(2).id, node.id) - - node['start'] = start - node['end'] = end - node['axis'] = None + elif len(node.in_nodes()) >= 3: + if node.has('format') and node['format'] == 'onnx': + # ONNX 10 opset case + starts_node = node.in_node(1) + ends_node = node.in_node(2) + if starts_node.has_valid('value') and ends_node.has_valid('value'): + start = np.array(node.in_node(1).value, dtype=np.int64) + end = np.array(node.in_node(2).value, dtype=np.int64) + if 3 in node.in_nodes(): + if node.in_node(3).has_valid('value'): + axis = np.array(node.in_node(3).value, dtype=np.int64) + else: + log.warning('Incorrect slice operation: axes should be const') + return + if 4 in node.in_nodes(): + if node.in_node(4).has_valid('value'): + steps = np.array(node.in_node(4).value, dtype=np.int64) + else: + log.warning('Incorrect slice operation: steps should be const') + return + else: + log.warning('Incorrect slice operation: no starts or ends attr') + return else: - log.warning('Incorrect slice operation: no starts or end attr') - return + # TF case + start_node = node.in_node(1) + size_node = node.in_node(2) + if start_node.has_valid('value') and size_node.has_valid('value'): + start = np.array(node.in_node(1).value, dtype=np.int64) + size = np.array(node.in_node(2).value, dtype=np.int64) + end = start + size + axis = None + + # Delete edges to start, size nodes + node.graph.remove_edge(node.in_node(1).id, node.id) + node.graph.remove_edge(node.in_node(2).id, node.id) + + node['start'] = start + node['end'] = end + node['axis'] = None + else: + log.warning('Incorrect slice operation: no starts or end attr') + return else: log.warning('Incorrect number of input nodes in slice operation') return @@ -96,12 +121,15 @@ def infer(node: Node): if axis is None: axis = [x for x in range(len(start))] + if steps is None: + steps = np.ones(start.size, dtype=np.int64) + # Calculate output value for slice operation slice_idx = [None for x in range(len(node.in_node().shape))] shrink_axis_mask = [False for x in range(len(node.in_node().shape))] for id in range(len(axis)): # Ranged for output value for specified axis - slice_idx[axis[id]] = slice(start[id], end[id], 1) + slice_idx[axis[id]] = slice(start[id], end[id], steps[id]) # TODO: check whether this check is really important for axis, s in enumerate(slice_idx): @@ -113,5 +141,5 @@ def infer(node: Node): node['shrink_axis_mask'] = np.array(shrink_axis_mask) value = value[tuple(slice_idx)] - node.out_node().value = np.array(value) if node.in_node(0).value is not None else None + node.out_node().value = value.copy() if node.in_node(0).value is not None else None node.out_node().shape = np.array(value.shape) diff --git a/model-optimizer/mo/ops/squeeze.py b/model-optimizer/mo/ops/squeeze.py index 9e513b75ea9452..02444721343025 100644 --- a/model-optimizer/mo/ops/squeeze.py +++ b/model-optimizer/mo/ops/squeeze.py @@ -69,8 +69,8 @@ def infer(node: Node): if node.in_port(1).get_source().node.op == 'Const': node.in_port(1).data.set_value(real_squeeze_dims) - if node.in_node().value is not None: - node.out_node().value = np.array(np.reshape(node.in_node().value, output_shape)) + if node.in_port(0).data.get_value() is not None: + node.out_port(0).data.set_value(node.in_port(0).data.get_value().reshape(output_shape)) # the squeeze_dim attribute will be converted to the second input in the end of the Middle phase PermuteInputs().set_input_permutation(node.in_node(1), node, 'input:0', 'axis') diff --git a/model-optimizer/mo/ops/strided_slice.py b/model-optimizer/mo/ops/strided_slice.py index 3cb314bcd0d9a9..ee4a788a21d92a 100644 --- a/model-optimizer/mo/ops/strided_slice.py +++ b/model-optimizer/mo/ops/strided_slice.py @@ -89,6 +89,7 @@ def backend_attrs(self): def convert(attr): return lambda node: array_to_str(node, attr) + for a in list(['new_axis_mask', 'shrink_axis_mask', 'ellipsis_mask', 'begin_mask', 'end_mask']): al.append((a, convert(a))) return al @@ -97,7 +98,7 @@ def convert(attr): def infer(node: Node): tf_strided_slice_infer(node) - if node.graph.graph['layout'] == 'NHWC': + if node.graph.graph['layout'] == 'NHWC' and node.out_port(0).data.get_value() is None: PermuteAttrs.create_permute_attrs(node, attrs=[('shrink_axis_mask', 'input:0', permute_masks), ('new_axis_mask', 'input:0', permute_masks), ('ellipsis_mask', 'input:0', permute_masks), diff --git a/model-optimizer/mo/ops/unsqueeze.py b/model-optimizer/mo/ops/unsqueeze.py index dd3c13f9f268e8..4923ff9ac8a3d4 100644 --- a/model-optimizer/mo/ops/unsqueeze.py +++ b/model-optimizer/mo/ops/unsqueeze.py @@ -68,9 +68,9 @@ def infer(node): for dim in unsqueeze_dims: output_shape = np.insert(output_shape, dim, 1) - node.out_port(0).data.set_shape(int64_array(output_shape)) - if input_value is not None: - node.out_port(0).data.set_value(np.reshape(input_value, output_shape)) + node.out_port(0).data.set_value(input_value.reshape(output_shape)) + else: + node.out_port(0).data.set_shape(int64_array(output_shape)) PermuteInputs().set_input_permutation(node.in_node(1), node, 'input:0', 'axis') diff --git a/model-optimizer/mo/pipeline/caffe.py b/model-optimizer/mo/pipeline/caffe.py index f98c5ec1981795..98f49b87d10c48 100644 --- a/model-optimizer/mo/pipeline/caffe.py +++ b/model-optimizer/mo/pipeline/caffe.py @@ -41,12 +41,14 @@ from mo.utils.cli_parser import get_meta_info from mo.utils.error import Error from mo.utils.find_inputs import find_inputs +from mo.utils.logger import log_step from mo.utils.utils import refer_to_faq_msg def driver(argv: argparse.Namespace, proto_file_name: str, model_file_name: str, output_model_name: str, output_dir: str, caffe_proto_path: str, mean_file: str = "", - mean_file_offsets: tuple = None, custom_layers_mapping_path: str = None): + mean_file_offsets: tuple = None, custom_layers_mapping_path:str = None): + log_step(argv.steps, 'LOAD') meta_info = get_meta_info(argv) caffe_pb2 = loader.import_caffe_pb2(caffe_proto_path) @@ -91,7 +93,9 @@ def driver(argv: argparse.Namespace, proto_file_name: str, model_file_name: str, extract_node_attrs(graph, lambda node: caffe_extractor(node, check_for_duplicates(caffe_type_extractors))) # --------------------------------- LOAD END ------------------------------------------------------ + log_step(argv.steps, 'FRONT') class_registration.apply_replacements(graph, class_registration.ClassType.FRONT_REPLACER) + log_step(argv.steps, 'MIDDLE') class_registration.apply_replacements(graph, class_registration.ClassType.MIDDLE_REPLACER) # Mark nodes with attr 'can_be_fused': False to disable fusing for specified nodes @@ -150,13 +154,14 @@ def driver(argv: argparse.Namespace, proto_file_name: str, model_file_name: str, permute_op_nodes_attrs(graph) graph_clean_up(graph) + log_step(argv.steps, 'BACK') class_registration.apply_replacements(graph, class_registration.ClassType.BACK_REPLACER) remove_const_ops(graph) CreateConstNodesReplacement().find_and_replace_pattern(graph) remove_output_ops(graph) - + log_step(argv.steps, 'EMIT') prepare_emit_ir(graph=graph, data_type=argv.data_type, output_dir=output_dir, output_model_name=output_model_name, mean_data=mf, input_names=input_names, diff --git a/model-optimizer/mo/pipeline/kaldi.py b/model-optimizer/mo/pipeline/kaldi.py index 8d833ff1964682..cc91fda0a8362e 100644 --- a/model-optimizer/mo/pipeline/kaldi.py +++ b/model-optimizer/mo/pipeline/kaldi.py @@ -18,7 +18,9 @@ import numpy as np from extensions.back.CreateConstNodes import CreateConstNodesReplacement +from extensions.back.CutMemory import CutMemory from extensions.back.ElementwiseOpsToEltwiseOps import DivideToEltwises, SubtractToEltwises, SimpleEltwiseToEltwiseOp +from extensions.back.ForceStrictPrecision import ForceStrictPrecision from extensions.back.LeakyReluToReluWithNegativeSlope import LeakyReluToReluWithNegativeSlope from extensions.back.ParameterToPlaceholder import ParameterToInput from extensions.back.TransposeToPermute import TransposeToPermute @@ -28,10 +30,13 @@ from extensions.front.kaldi.fuse_repeated_reshape import FuseRepeatedReshapes from extensions.front.kaldi.replace_lstm_node_pattern import ReplaceLSTMNodePattern from extensions.middle.EltwiseChecker import EltwiseChecker -from extensions.middle.RemoveDuplicationMemory import RemoveMemoryDuplicationPattern +from extensions.middle.InsertSelect import AddSelectBeforeMemoryNodePattern +from extensions.middle.RemoveDuplicationMemory import RemoveMemoryDuplicationPattern, MergeNeighborSplicePattern from extensions.middle.RemoveIdentity import RemoveIdentity from extensions.middle.RemoveUselessCrops import RemoveUselessCropsPattern -from extensions.middle.ReplaceMemoryOffsetWithSplice import ReplaceMemoryOffsetNodePattern, ReplaceMemoryOffsetWithMemoryNodePattern +from extensions.middle.ReplaceMemoryOffsetWithSplice import ReplaceMemoryOffsetNodePattern, \ + ReplaceMemoryOffsetWithMemoryNodePattern +from extensions.middle.ReplacePNorm import ReplacePNormNodePattern from extensions.middle.ReplaceSpliceNodePattern import ReplaceSpliceNodePattern from mo.front.common.register_custom_ops import update_extractors_with_extensions from mo.front.extractor import extract_node_attrs, remove_output_ops @@ -47,6 +52,7 @@ from mo.utils.cli_parser import get_meta_info from mo.utils.error import Error from mo.utils.find_inputs import find_outputs +from mo.utils.logger import log_step from mo.utils.utils import refer_to_faq_msg @@ -113,14 +119,15 @@ def apply_biases_to_last_layer(graph, counts): def driver(argv, input_model, output_model_name, output_dir): + log_step(argv.steps, 'LOAD') meta_info = get_meta_info(argv) EltwiseChecker.enabled = False try: - graph, input_shapes = load_kaldi_model(input_model) + graph = load_kaldi_model(input_model) except Exception as e: - raise Error('Model Optimizer is not able to read Kaldi model {}. '.format(input_model) + + raise Error('Model Optimizer is not able to parse Kaldi model {}. '.format(input_model) + refer_to_faq_msg(91)) from e graph.check_empty_graph('load_kaldi_nnet_model') graph.graph['cmd_params'] = argv @@ -136,18 +143,23 @@ def driver(argv, input_model, output_model_name, output_dir): extract_node_attrs(graph, lambda node: kaldi_extractor(node)) # --------------------------------- LOAD END ------------------------------------------------------ + log_step(argv.steps, 'FRONT') ReplaceLSTMNodePattern().find_and_replace_pattern(graph) class_registration.apply_replacements(graph, class_registration.ClassType.FRONT_REPLACER) - + log_step(argv.steps, 'MIDDLE') graph = partial_infer(graph) + ReplacePNormNodePattern().find_and_replace_pattern(graph) ReplaceMemoryOffsetNodePattern().find_and_replace_pattern(graph) ReplaceMemoryOffsetWithMemoryNodePattern().find_and_replace_pattern(graph) RemoveMemoryDuplicationPattern().find_and_replace_pattern(graph) + MergeNeighborSplicePattern().find_and_replace_pattern(graph) RemoveUselessCropsPattern().find_and_replace_pattern(graph) RemoveIdentity().find_and_replace_pattern(graph) graph_clean_up(graph) + AddSelectBeforeMemoryNodePattern().find_and_replace_pattern(graph) + ReplaceSpliceNodePattern().find_and_replace_pattern(graph) graph_clean_up(graph) @@ -171,7 +183,7 @@ def driver(argv, input_model, output_model_name, output_dir): log.debug("After removing softmax") graph.print_graph_stat() - ParameterToInput().find_and_replace_pattern(graph) + log_step(argv.steps, 'BACK') LeakyReluToReluWithNegativeSlope().find_and_replace_pattern(graph) TransposeToPermute().find_and_replace_pattern(graph) DivideToEltwises().find_and_replace_pattern(graph) @@ -180,10 +192,17 @@ def driver(argv, input_model, output_model_name, output_dir): for_graph_and_each_sub_graph_recursively(graph, convert_matmul_to_fully_connected) # Intentionally after all transformations + if argv.remove_memory: + CutMemory().find_and_replace_pattern(graph) + graph_clean_up(graph) + ParameterToInput().find_and_replace_pattern(graph) + KaldiRemoveMemoryOutputBackReplacementPattern().find_and_replace_pattern(graph) + ForceStrictPrecision().find_and_replace_pattern(graph) remove_const_ops(graph) CreateConstNodesReplacement().find_and_replace_pattern(graph) remove_output_ops(graph) + log_step(argv.steps, 'EMIT') prepare_emit_ir(graph, argv.data_type, output_dir, output_model_name, meta_info=meta_info) return 0 diff --git a/model-optimizer/mo/pipeline/mx.py b/model-optimizer/mo/pipeline/mx.py index e9f6ac8ba1d8ef..db0bcbcc98b268 100644 --- a/model-optimizer/mo/pipeline/mx.py +++ b/model-optimizer/mo/pipeline/mx.py @@ -16,6 +16,7 @@ from extensions.back.CreateConstNodes import CreateConstNodesReplacement from mo.middle.pattern_match import for_graph_and_each_sub_graph_recursively from mo.utils.error import Error, FrameworkError +from mo.utils.logger import log_step from mo.utils.utils import refer_to_faq_msg try: @@ -50,6 +51,7 @@ def driver(argv: argparse.Namespace, input_model: str, output_model_name: str, output_dir: str): + log_step(argv.steps, 'LOAD') meta_info = get_meta_info(argv) try: @@ -88,7 +90,9 @@ def driver(argv: argparse.Namespace, input_model: str, output_model_name: str, o extract_node_attrs(graph, mxnet_op_extractor) # --------------------------------- LOAD END ------------------------------------------------------ + log_step(argv.steps, 'FRONT') class_registration.apply_replacements(graph, class_registration.ClassType.FRONT_REPLACER) + log_step(argv.steps, 'MIDDLE') class_registration.apply_replacements(graph, class_registration.ClassType.MIDDLE_REPLACER) fuse_pad(graph) @@ -142,6 +146,7 @@ def driver(argv: argparse.Namespace, input_model: str, output_model_name: str, o permute_op_nodes_attrs(graph) graph_clean_up(graph) + log_step(argv.steps, 'BACK') class_registration.apply_replacements(graph, class_registration.ClassType.BACK_REPLACER) for_graph_and_each_sub_graph_recursively(graph, remove_const_ops) @@ -149,6 +154,7 @@ def driver(argv: argparse.Namespace, input_model: str, output_model_name: str, o for_graph_and_each_sub_graph_recursively(graph, remove_output_ops) + log_step(argv.steps, 'EMIT') prepare_emit_ir(graph=graph, data_type=argv.data_type, output_dir=output_dir, output_model_name=output_model_name, meta_info=meta_info) return 0 diff --git a/model-optimizer/mo/pipeline/onnx.py b/model-optimizer/mo/pipeline/onnx.py index 9a4af959745760..4ef9ea69ad802d 100644 --- a/model-optimizer/mo/pipeline/onnx.py +++ b/model-optimizer/mo/pipeline/onnx.py @@ -48,10 +48,12 @@ from mo.utils import class_registration from mo.utils.cli_parser import get_meta_info from mo.utils.error import Error +from mo.utils.logger import log_step from mo.utils.utils import refer_to_faq_msg def driver(argv: argparse.Namespace, model_file_name: str, output_model_name: str, output_dir: str): + log_step(argv.steps, 'LOAD') meta_info = get_meta_info(argv) model_proto = load_onnx_model(model_file_name) @@ -92,7 +94,9 @@ def driver(argv: argparse.Namespace, model_file_name: str, output_model_name: st extract_node_attrs(graph, lambda node: onnx_op_extractor(node, check_for_duplicates(onnx_op_extractors))) # --------------------------------- LOAD END ------------------------------------------------------ + log_step(argv.steps, 'FRONT') class_registration.apply_replacements(graph, class_registration.ClassType.FRONT_REPLACER) + log_step(argv.steps, 'MIDDLE') class_registration.apply_replacements(graph, class_registration.ClassType.MIDDLE_REPLACER) fuse_pad(graph) @@ -164,6 +168,8 @@ def driver(argv: argparse.Namespace, model_file_name: str, output_model_name: st permute_op_nodes_attrs(graph) graph_clean_up_onnx(graph) + + log_step(argv.steps, 'BACK') class_registration.apply_replacements(graph, class_registration.ClassType.BACK_REPLACER) for_graph_and_each_sub_graph_recursively(graph, remove_const_ops) @@ -172,6 +178,7 @@ def driver(argv: argparse.Namespace, model_file_name: str, output_model_name: st for_graph_and_each_sub_graph_recursively(graph, remove_output_ops) + log_step(argv.steps, 'EMIT') prepare_emit_ir(graph=graph, data_type=argv.data_type, output_dir=output_dir, output_model_name=output_model_name, meta_info=meta_info) diff --git a/model-optimizer/mo/pipeline/tf.py b/model-optimizer/mo/pipeline/tf.py index e024bf756aea72..65bab2a425e264 100644 --- a/model-optimizer/mo/pipeline/tf.py +++ b/model-optimizer/mo/pipeline/tf.py @@ -51,6 +51,7 @@ from mo.utils import class_registration, tensorboard from mo.utils.cli_parser import get_meta_info from mo.utils.error import Error +from mo.utils.logger import log_step from mo.utils.utils import refer_to_faq_msg try: @@ -66,6 +67,7 @@ def tf2nx(argv: argparse.Namespace, model_file_name: str, output_model_name: str The specific TF structure assumes each GraphDef node is converted to a single NetworkX node, node id is an original TF node name, and edges go directly from one op to another op. """ + log_step(argv.steps, 'LOAD') meta_info = get_meta_info(argv) if argv.tensorflow_custom_layer_libraries: @@ -130,7 +132,9 @@ def tf2nx(argv: argparse.Namespace, model_file_name: str, output_model_name: str extract_node_attrs(graph, lambda node: tf_op_extractor(node, check_for_duplicates(tf_op_extractors))) # --------------------------------- LOAD END ------------------------------------------------------ + log_step(argv.steps, 'FRONT') class_registration.apply_replacements(graph, class_registration.ClassType.FRONT_REPLACER) + log_step(argv.steps, 'MIDDLE') class_registration.apply_replacements(graph, class_registration.ClassType.MIDDLE_REPLACER) fuse_pad(graph) @@ -218,6 +222,8 @@ def tf2nx(argv: argparse.Namespace, model_file_name: str, output_model_name: str for_graph_and_each_sub_graph_recursively(graph, graph_clean_up_tf) graph.graph['layout'] = 'NCHW' + + log_step(argv.steps, 'BACK') class_registration.apply_replacements(graph, class_registration.ClassType.BACK_REPLACER) for_graph_and_each_sub_graph_recursively(graph, graph_clean_up_tf) @@ -226,6 +232,7 @@ def tf2nx(argv: argparse.Namespace, model_file_name: str, output_model_name: str for_graph_and_each_sub_graph_recursively(graph, remove_output_ops) + log_step(argv.steps, 'EMIT') prepare_emit_ir(graph=graph, data_type=argv.data_type, output_dir=output_dir, output_model_name=output_model_name, meta_info=meta_info) diff --git a/model-optimizer/mo/utils/cli_parser.py b/model-optimizer/mo/utils/cli_parser.py index 3e353190d4d1af..bbe78b3da3b6be 100644 --- a/model-optimizer/mo/utils/cli_parser.py +++ b/model-optimizer/mo/utils/cli_parser.py @@ -210,13 +210,13 @@ def get_common_cli_parser(parser: argparse.ArgumentParser = None): 'DEBUG', 'NOTSET'], default='ERROR') common_group.add_argument('--input', - help='Comma-separated list of input nodes names with shapes ' + - 'and values for freezing. '+ - 'For example, use the following format to set input port ' + - 'of the node with the shape as an input node and ' + - 'freeze output port of the node with the value ' + - 'and the shape : ' + - 'port1:node_name1[shape1], node_name2:port2[shape2]->value2.') + help='Quoted list of comma-separated input nodes names with shapes ' + + 'and values for freezing. The shape and value are specified as space-separated lists. '+ + 'For example, use the following format to set input port 0 ' + + 'of the node `node_name1` with the shape [3 4] as an input node and ' + + 'freeze output port 1 of the node `node_name2` with the value [20 15] ' + + 'and the shape [2]: ' + + '"0:node_name1[3 4],node_name2:1[2]->[20 15]".') common_group.add_argument('--output', help='The name of the output operation of the model. ' + 'For TensorFlow*, do not add :0 to this name.') @@ -301,6 +301,10 @@ def get_common_cli_parser(parser: argparse.ArgumentParser = None): help='[ Experimental feature ] Enables `Shape` operation with all children keeping. ' 'This feature makes model reshapable in Inference Engine', action='store_true', default=False) + common_group.add_argument('--steps', + help='Enables model conversion steps display', + action='store_true', default=False) + return parser @@ -369,7 +373,8 @@ def get_mxnet_cli_options(): def get_kaldi_cli_options(): d = { 'counts': '- A file name with full path to the counts file', - 'remove_output_softmax': '- Removes the SoftMax layer that is the output layer' + 'remove_output_softmax': '- Removes the SoftMax layer that is the output layer', + 'remove_memory': '- Removes the Memory layer and use additional inputs and outputs instead' } return OrderedDict(sorted(d.items(), key=lambda t: t[0])) @@ -564,6 +569,11 @@ def get_kaldi_cli_parser(parser: argparse.ArgumentParser = None): kaldi_group.add_argument("--remove_output_softmax", help="Removes the SoftMax layer that is the output layer", action='store_true') + + kaldi_group.add_argument("--remove_memory", + help="Removes the Memory layer and use additional inputs outputs instead", + action='store_true', + default=False) return parser diff --git a/model-optimizer/mo/utils/error.py b/model-optimizer/mo/utils/error.py index d7d28e7bc0b483..3ff74fb408a776 100644 --- a/model-optimizer/mo/utils/error.py +++ b/model-optimizer/mo/utils/error.py @@ -25,9 +25,12 @@ class BasicError(Exception): """ def __str__(self): + cause = "" + if self.__cause__: + cause = self.__cause__.__str__() + '\n' if len(self.args) <= 1: - return Exception.__str__(self) - return self.args[0].format(*self.args[1:]) # pylint: disable=unsubscriptable-object + return cause + Exception.__str__(self) + return cause + self.args[0].format(*self.args[1:]) # pylint: disable=unsubscriptable-object class FrameworkError(BasicError): diff --git a/model-optimizer/mo/utils/graph.py b/model-optimizer/mo/utils/graph.py index dfaaf9c3ee7beb..51cfdd3703c16b 100644 --- a/model-optimizer/mo/utils/graph.py +++ b/model-optimizer/mo/utils/graph.py @@ -199,7 +199,8 @@ def invert_sub_graph_between_nodes(graph: Graph, start_nodes: list, end_nodes: l while len(d) != 0: cur_node_name = d.popleft() sub_graph_nodes.append(cur_node_name) - if cur_node_name not in start_nodes and detect_extra_start_node(Node(graph, cur_node_name)): + if cur_node_name not in start_nodes and \ + detect_extra_start_node is not None and detect_extra_start_node(Node(graph, cur_node_name)): extra_start_nodes.append(cur_node_name) else: if cur_node_name not in end_nodes: # do not add output nodes of the end_nodes diff --git a/model-optimizer/mo/utils/logger.py b/model-optimizer/mo/utils/logger.py index 51bc3900e0ce5e..ded44a13c6171f 100644 --- a/model-optimizer/mo/utils/logger.py +++ b/model-optimizer/mo/utils/logger.py @@ -13,11 +13,17 @@ See the License for the specific language governing permissions and limitations under the License. """ - +import importlib import logging as log import os import re +# WA for abseil bug that affects logging while importing TF starting 1.14 version +# Link to original issue: https://github.com/abseil/abseil-py/issues/99 +if importlib.util.find_spec('absl') is not None: + import absl.logging + log.root.removeHandler(absl.logging._absl_handler) + handler_num = 0 @@ -77,3 +83,16 @@ def init_logger(lvl: str, silent: bool): if handler_num == 0: logger.addHandler(handler) handler_num += 1 + + +def log_step(flag, step): + messages = { + 'LOAD': 'Model loading step', + 'FRONT': 'Front phase execution step', + 'MIDDLE': 'Middle phase execution step', + 'BACK': 'Back phase execution step', + 'EMIT': 'IR emitting step', + } + if flag: + assert step in messages.keys() + print('[ INFO ] {}'.format(messages[step])) diff --git a/model-optimizer/mo/utils/pipeline_config.py b/model-optimizer/mo/utils/pipeline_config.py index 5352db3bf6e41d..0ebd8a1ab57951 100644 --- a/model-optimizer/mo/utils/pipeline_config.py +++ b/model-optimizer/mo/utils/pipeline_config.py @@ -46,10 +46,13 @@ ('multiscale_anchor_generator_aspect_ratios', 'anchor_generator/multiscale_anchor_generator/aspect_ratios'), ('multiscale_anchor_generator_scales_per_octave', 'anchor_generator/multiscale_anchor_generator/scales_per_octave'), # SSD anchor generator attributes - ('ssd_anchor_generator_min_scale', 'anchor_generator/ssd_anchor_generator/min_scale'), - ('ssd_anchor_generator_max_scale', 'anchor_generator/ssd_anchor_generator/max_scale'), + ('ssd_anchor_generator_min_scale', 'anchor_generator/ssd_anchor_generator/min_scale', 0.2), + ('ssd_anchor_generator_max_scale', 'anchor_generator/ssd_anchor_generator/max_scale', 0.95), ('ssd_anchor_generator_num_layers', 'anchor_generator/ssd_anchor_generator/num_layers'), ('ssd_anchor_generator_aspect_ratios', 'anchor_generator/ssd_anchor_generator/aspect_ratios'), + ('ssd_anchor_generator_scales', 'anchor_generator/ssd_anchor_generator/scales'), + ('ssd_anchor_generator_interpolated_scale_aspect_ratio', + 'anchor_generator/ssd_anchor_generator/interpolated_scale_aspect_ratio', 1.0), ('ssd_anchor_generator_reduce_lowest', 'anchor_generator/ssd_anchor_generator/reduce_boxes_in_lowest_layer'), ('ssd_anchor_generator_base_anchor_height', 'anchor_generator/ssd_anchor_generator/base_anchor_height', 1.0), ('ssd_anchor_generator_base_anchor_width', 'anchor_generator/ssd_anchor_generator/base_anchor_width', 1.0), diff --git a/model-optimizer/mo/utils/pipeline_config_test.py b/model-optimizer/mo/utils/pipeline_config_test.py index 6c8e19bbf7dc5b..747a5ce3fb23ec 100644 --- a/model-optimizer/mo/utils/pipeline_config_test.py +++ b/model-optimizer/mo/utils/pipeline_config_test.py @@ -145,6 +145,9 @@ def test_pipeline_config_existing_file(self): 'anchor_generator_width': 256, 'anchor_generator_height_stride': 16, 'anchor_generator_width_stride': 16, + 'ssd_anchor_generator_min_scale': 0.2, + 'ssd_anchor_generator_max_scale': 0.95, + 'ssd_anchor_generator_interpolated_scale_aspect_ratio': 1.0, } os.unlink(file_name) self.assertDictEqual(pipeline_config._model_params, expected_result) diff --git a/model-optimizer/mo/utils/unittest/graph.py b/model-optimizer/mo/utils/unittest/graph.py index fd5a3050745f14..0d2cd539cb79c6 100644 --- a/model-optimizer/mo/utils/unittest/graph.py +++ b/model-optimizer/mo/utils/unittest/graph.py @@ -234,9 +234,14 @@ def build_graph_with_edge_attrs(nodes_attrs: dict, edges: list, update_attribute def compare_graphs(graph: Graph, graph_ref: Graph, last_node: str, last_node_ref=None, check_op_attrs=False): + from mo.utils.unittest.ir_engine import IREngine + stderr = [] if last_node_ref is None: last_node_ref = last_node + if 'statistics' in graph.graph and 'statistics' in graph_ref.graph: + assert graph.graph['statistics'] == graph_ref.graph['statistics'], "int8 statistics comparison failed" + q = deque([last_node]) q_ref = deque([last_node_ref]) @@ -245,7 +250,8 @@ def compare_graphs(graph: Graph, graph_ref: Graph, last_node: str, last_node_ref while len(q_ref) != 0: if len(q) == 0: - return False, 'Graphs have different number of nodes' + stderr.append('Graphs have different number of nodes') + return False, stderr node = Node(graph, q.popleft()) node_ref = Node(graph_ref, q_ref.popleft()) @@ -254,25 +260,28 @@ def compare_graphs(graph: Graph, graph_ref: Graph, last_node: str, last_node_ref # Check that nodes has same amount of output nodes if len(node_ref.out_nodes()) != len(node.out_nodes()): - return False, 'Current node "{}" and reference node "{}" have different amount of output nodes: {} vs {}'.\ - format(node.id, node_ref.id, len(node_ref.out_nodes()), len(node.out_nodes())) + stderr.append('Current node "{}" and reference node "{}" have different amount of output nodes: {} vs {}'.\ + format(node.id, node_ref.id, len(node_ref.out_nodes()), len(node.out_nodes()))) + return False, stderr # Check that nodes has same amount of input nodes if len(node_ref.in_nodes()) != len(node.in_nodes()): - return False, 'Current node "{}" and reference node "{}" have different amount of input nodes: {} vs {}'.\ - format(node.id, node_ref.id, len(node_ref.in_nodes()), len(node.in_nodes())) + stderr.append('Current node "{}" and reference node "{}" have different amount of input nodes: {} vs {}'.\ + format(node.id, node_ref.id, len(node_ref.in_nodes()), len(node.in_nodes()))) + return False, stderr # Check that nodes has same 'kind' if node_ref.kind != node.kind: - return False, 'Current node "{}" and reference node "{}" have different kind parameter'.\ - format(node.id, node_ref.id) + stderr.append('Current node "{}" and reference node "{}" have different kind parameter'.\ + format(node.id, node_ref.id)) + return False, stderr # Check can_be_fused attr if node_ref.has_valid('can_be_fused'): if node_ref.soft_get('can_be_fused') != node.soft_get('can_be_fused'): - return False, 'Current node "{}" and reference node "{}" have different "can_be_fused" parameter ' \ + stderr.append('Current node "{}" and reference node "{}" have different "can_be_fused" parameter ' \ '{} and {}'.format(node.id, node_ref.id, node.soft_get('can_be_fused'), - node_ref.soft_get('can_be_fused')) + node_ref.soft_get('can_be_fused'))) if node_ref.kind == 'op': # Check that nodes has same operation @@ -282,41 +291,53 @@ def compare_graphs(graph: Graph, graph_ref: Graph, last_node: str, last_node_ref 'infer', 'IE']: continue if attr not in graph.node[node.id]: - return False, 'Current node "{}" has missing attribute {}'.format(node.id, attr) + stderr.append('Current node "{}" has missing attribute {}'.format(node.id, attr)) + continue if type(graph_ref.node[node_ref.id][attr]) in [np.ndarray, list]: if not np.array_equal(graph.node[node.id][attr], graph_ref.node[node_ref.id][attr]): - return False, 'Current node "{}" and reference node "{}" have different attr "{}" : ' \ + stderr.append('Current node "{}" and reference node "{}" have different attr "{}" : ' \ '{} and {}'.format(node.id, node_ref.id, attr, graph.node[node.id][attr], - graph_ref.node[node_ref.id][attr]) + graph_ref.node[node_ref.id][attr])) elif isinstance(graph.node[node.id][attr], Number): eps = 5e-2 if node.has('precision') and node['precision'] == 'FP16' else 1e-4 if abs(graph.node[node.id][attr] - graph_ref.node[node_ref.id][attr]) > eps: - return False, '{} and {} has different attr {} : {} and {}'.format( - node.id, node_ref.id, attr, graph.node[node.id][attr], - graph_ref.node[node_ref.id][attr]) + stderr.append('{} and {} has different attr {} : {} and {}'.format( + node.id, node_ref.id, attr, graph.node[node.id][attr], + graph_ref.node[node_ref.id][attr])) + elif isinstance(graph.node[node.id][attr], IREngine): + resp, err_log = graph.node[node.id][attr].compare(graph_ref.node[node_ref.id][attr]) + if not resp: + stderr.extend(err_log) elif graph.node[node.id][attr] != graph_ref.node[node_ref.id][attr]: - return False, 'Current node "{}" and reference node "{}" have different attr "{}" : {} and {}'.format( - node.id, node_ref.id, attr, graph.node[node.id][attr], - graph_ref.node[node_ref.id][attr]) + stderr.append('Current node "{}" and reference node "{}" have different attr "{}" : {} and {}'.format( + node.id, node_ref.id, attr, graph.node[node.id][attr], + graph_ref.node[node_ref.id][attr])) else: if node_ref.has_valid('shape') and not node.has_valid('shape'): - return False, '{} has None shape'.format(node.id) + stderr.append('{} has None shape'.format(node.id)) if node_ref.has_valid('value') and not node.has_valid('value'): - return False, '{} has None value'.format(node.id) + stderr.append('{} has None value'.format(node.id)) # Check that nodes has same shape and value if node_ref.has_valid('shape') and node_ref.shape is not None and not np.array_equal(node_ref.shape, node.shape): - return False, 'Current node "{}" and reference node "{}" have different shapes {} and {}'.\ - format(node.id, node_ref.id, node.shape, node_ref.shape) + stderr.append('Current node "{}" and reference node "{}" have different shapes {} and {}'.\ + format(node.id, node_ref.id, node.shape, node_ref.shape)) if node_ref.has_valid('value') and node_ref.value is not None: - eps = 5e-2 if np.asarray(node.value).dtype == 'float16' else 1e-4 + dtype = np.asarray(node.value).dtype + if dtype == 'uint8': + eps = 0 + elif dtype == 'float16': + eps = 5e-2 + else: + eps = 1e-4 + if not np.allclose(node_ref.value, node.value, rtol=eps, atol=eps): - return False, 'Current node "{}" and reference node "{}" have different values \n{} \nand \n{}'.\ - format(node.id, node_ref.id, node.value, node_ref.value) + stderr.append('Current node "{}" and reference node "{}" have different values \n{} \nand \n{}'.\ + format(node.id, node_ref.id, node.value, node_ref.value)) ports = sorted(node.in_nodes().keys()) if node.kind == 'op' else None in_nodes = [node.in_node(k) for k in ports] if node.kind == 'op' else node.in_nodes() for in_node in in_nodes: @@ -325,7 +346,8 @@ def compare_graphs(graph: Graph, graph_ref: Graph, last_node: str, last_node_ref ports_ref = sorted(node_ref.in_nodes().keys()) if node_ref.kind == 'op' else None if ports != ports_ref: - return False, 'Current node "{}" and reference node "{}" have different ports'.format(node.id, node_ref.id) + stderr.append('Current node "{}" and reference node "{}" have different ports'.format(node.id, node_ref.id)) + return False, stderr in_nodes = [node_ref.in_node(k) for k in ports] if node_ref.kind == 'op' else node_ref.in_nodes() for in_node in in_nodes: @@ -342,14 +364,26 @@ def compare_graphs(graph: Graph, graph_ref: Graph, last_node: str, last_node_ref if out_node.id not in checked_nodes_ref and out_node.id not in q_ref: q_ref.append(out_node.id) - return True, '' + return (False, '\n'.join(stderr)) if stderr else (True, []) + + +class FakeAttr: + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + def __setitem__(self, key, value): + setattr(self, key, value) + + def __getitem__(self, item): + return getattr(self, item) class FakeNode: def __init__(self, pl, ml): self.pb = pl self.model_pb = ml - self.graph = None + self.graph = FakeAttr() + self.graph.graph = {} self.update_node = lambda: None def __setitem__(self, key, value): diff --git a/model-optimizer/mo/utils/unittest/ir_engine.py b/model-optimizer/mo/utils/unittest/ir_engine.py new file mode 100644 index 00000000000000..99347d41f3403e --- /dev/null +++ b/model-optimizer/mo/utils/unittest/ir_engine.py @@ -0,0 +1,332 @@ +""" + Copyright (c) 2018-2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import hashlib +import os +import xml.etree.ElementTree as ET +from collections import namedtuple, defaultdict +from pathlib import Path + +import networkx as nx +import numpy as np +import logging as log +import sys + +from mo.graph.graph import Node, Graph +from mo.utils.unittest.graph import compare_graphs + +log.basicConfig(format="[ %(levelname)s ] %(message)s", level=log.DEBUG, stream=sys.stdout) + +class IREngine(object): + def __init__(self, path_to_xml: str, path_to_bin=None, precision="FP32", xml_tree=None): + if not xml_tree and not os.path.exists(path_to_xml): + raise AttributeError("File {} do not exists!".format(path_to_xml)) + + if path_to_bin and not os.path.exists(path_to_bin): + raise AttributeError("File {} do not exists!".format(path_to_bin)) + + self.path_to_xml = str(path_to_xml) + self.path_to_bin = str(path_to_bin) if path_to_bin else None + self.xml_tree = xml_tree + self.input_node = None + + if precision.upper() not in ['FP32', 'FP16']: + raise AttributeError("Precision {} is not supported!".format(precision)) + self.__load_ir() + + def __load_xml(self): + xml_tree = self.xml_tree or ET.parse(self.path_to_xml) + xml_root = xml_tree.getroot() + xml_layers = {} + xml_edges = [] + statistics = {} + + Edge = namedtuple('edge', ['from_layer', 'from_port', 'to_layer', 'to_port']) + + # Create graph with operations only + self.graph = Graph() + self.graph.graph['hashes'] = {} + + # Parse XML + for child in xml_root: + if child.tag == 'layers': + for layer in child: + layer_id, layer_attrs = self.__load_layer(layer) + xml_layers.update({layer_id: layer_attrs}) + elif child.tag == 'edges': + for edge in child: + xml_edges.append(Edge(edge.attrib['from-layer'], int(edge.attrib['from-port']), + edge.attrib['to-layer'], int(edge.attrib['to-port']))) + elif child.tag == 'statistics': + layers = child.findall('layer') + for layer in layers: + statistics[layer.find('name').text] = {'min': layer.find('min').text, 'max': layer.find('max').text} + + self.graph.graph['statistics'] = statistics + + for layer in xml_layers.keys(): + self.graph.add_node(layer, **xml_layers[layer]) + + for edge in xml_edges: + self.graph.add_edges_from( + [(edge.from_layer, edge.to_layer, {'from_port': edge.from_port, 'to_port': edge.to_port})]) + + # Insert data nodes between op nodes and insert data nodes with weights + nodes = list(self.graph.nodes()) + for node in nodes: + out_edges = Node(self.graph, node).get_outputs() + data_nodes = {} + for port in self.graph.node[node]['ports']: + data = self.graph.unique_id(prefix='data_') + self.graph.add_node(data, **{'kind': 'data', 'shape': self.graph.node[node]['ports'][port], + 'value': None}) + self.graph.add_edges_from([(node, data, {'out': port})]) + data_nodes.update({port: data}) + + for out_node, edge_attrs in out_edges: + self.graph.remove_edge(node, out_node) + if edge_attrs['from_port'] in data_nodes: + data = data_nodes[edge_attrs['from_port']] + else: + raise RuntimeError("SMTH wrong with IR! There is an edge from not existing port") + self.graph.add_edges_from([(data, out_node, {'in': edge_attrs['to_port']})]) + + def __load_bin(self): + bin_buff = np.fromfile(file=self.path_to_bin, dtype=np.uint8) + graph = self.graph + nodes = [node for node in graph.nodes()] + hashes = defaultdict(dict) + for node in nodes: + for w in ['weights', 'biases', 'custom']: + if w in graph.node[node]: + data = graph.unique_id(prefix='data_') + offset, size, in_port, precision = graph.node[node][w] + if Node(graph, node).soft_get('type') == 'BinaryConvolution': + precision = np.uint8 + value = np.frombuffer(buffer=bin_buff, dtype=precision, count=size, offset=offset) + hashes[graph.node[node]['name']][w] = hashlib.sha512(value.tobytes()).hexdigest() + graph.add_node(data, **{'kind': 'data', 'value': value, 'shape': value.shape}) + graph.add_edges_from([(data, node, {'in': in_port})]) + self.graph.graph['hashes'].update(hashes) + + def __load_bin_hashes(self): + graph = self.graph + bin_hash_map = {name: blob_map.item(0) for name, blob_map in dict(np.load(self.path_to_bin, + allow_pickle=True)).items()} + + for node in graph.nodes(): + for w in ['weights', 'biases', 'custom']: + if w in graph.node[node]: + assert Node(graph, node).has_valid('name') + node_name = Node(graph, node).name + assert node_name in bin_hash_map and w in bin_hash_map[node_name] + graph.node[node]['hashes'] = bin_hash_map[node_name][w] + + + def __load_ir(self): + self.__load_xml() + if not self.path_to_bin: + return + + if self.path_to_bin.endswith('.bin.hashes.npz'): + self.__load_bin_hashes() + else: + self.__load_bin() + + def __load_layer(self, layer): + """ + Layer example + + + + + + 1 + 3 + 32 + 32 + + + + + 1 + 32 + 32 + 32 + + + + + + + + + """ + + layer_id = layer.attrib['id'] + + layer_attrs = layer.attrib + layer_attrs.update({'ports': {}, 'kind': 'op'}) + + inputs_counter = 0 + + for attr in layer: + if attr.tag == 'data': + layer_attrs.update(IREngine.__normalize_attrs(attr.attrib)) + elif attr.tag == 'input': + inputs_counter = len(attr) + elif attr.tag == 'output': + output = attr + for port in output: + port_id = int(port.attrib['id']) + output_shape = [] + for dim in port: + output_shape.append(int(dim.text)) + + layer_attrs['ports'].update({port_id: output_shape}) + elif attr.tag == 'blobs': + in_port = inputs_counter + precision = layer.attrib['precision'] + precision_map = { + 'FP32': (4, np.float32), + 'FP16': (2, np.float16), + 'I64': (8, np.int64), + 'I32': (4, np.int32), + } + type_size, dtype = precision_map[precision] + for blob_attr in attr: + layer_attrs.update({blob_attr.tag: (int(blob_attr.attrib['offset']), + int(blob_attr.attrib['size']) // type_size, + in_port, + dtype)}) + in_port += 1 + elif attr.tag == 'body': + xml_body_child = list(layer.iterfind('body')) + assert len(xml_body_child) == 1 + + body_ir = IREngine(path_to_xml=None, + path_to_bin=self.path_to_bin, + xml_tree=ET.ElementTree(xml_body_child[0])) + self.graph.graph['hashes'].update(body_ir.graph.graph['hashes']) + + # Find port_map section and take an input with axis specified - this will be out input_layer for body + xml_port_map = list(layer.iterfind('port_map')) + if not len(xml_port_map) == 1: + log.warning("TensorIterator body won\'t be compared due to missing port_map section!") + continue + xml_port_map = xml_port_map[0] + + input_layers = [] + for input in xml_port_map: + if input.tag == 'input' and 'axis' in input.attrib: + if 'internal_layer_id' not in input.attrib: + log.warning("internal_layer_id attrib not found in input section") + else: + input_layers.append(Node(body_ir.graph, input.attrib['internal_layer_id'])) + + if len(input_layers) != 1: + log.warning("TensorIterator body won\'t be compared due to the number of inputs in body != 1 " + "({})".format(len(input_layers))) + else: + body_ir.input_node = input_layers[0] + layer_attrs.update({'body': body_ir}) + + return layer_id, layer_attrs + + @staticmethod + def __normalize_attrs(attrs: dict): + """ + Normalize attributes for type 'data'. + Replace " from values (not used right now) and make list of value with int, float or other types values. + Example: {'order': '1,0,2'} -> {'order': [1, 0, 2]} + {'order': '1'} -> {'order': 1} + """ + normalized_attrs = {} + for attr, value in attrs.items(): + value = value.replace('\"', '') + value = value.split(',') + n_value = [] + for val in value: + if val.isdigit(): + n_value.append(int(val)) + elif IREngine.__isfloat(val): + n_value.append(float(val)) + else: + n_value.append(val) + + if len(n_value) == 1: + normalized_attrs.update({attr: n_value[0]}) + else: + normalized_attrs.update({attr: n_value}) + + return normalized_attrs + + @staticmethod + def __isfloat(value): + try: + float(value) + return True + except ValueError: + return False + + @staticmethod + def __find_input(graph): + inputs = [] + for node in sorted(graph.nodes()): + node = Node(graph, node) + if node.has_valid('type') and node.type == 'Input': + inputs.append(node) + + if len(inputs) < 1: + raise RuntimeError("Graph {} has less than one input node") + + return inputs + + def compare(self, ref_net): + if not isinstance(ref_net, IREngine): + ir_input = self.__find_input(self.graph)[0] + ref_input = self.__find_input(ref_net)[0] + ref_graph = ref_net + else: + ir_input = self.input_node or self.__find_input(self.graph)[0] + ref_input = ref_net.input_node or ref_net.__find_input(ref_net.graph)[0] + ref_graph = ref_net.graph + # TODO check that ir_input[0].id and ref_input[0].id are the same + result, stderr = compare_graphs(graph=self.graph, graph_ref=ref_graph, last_node=ir_input.id, + last_node_ref=ref_input.id, check_op_attrs=True) + return result, stderr + + def generate_bin_hashes_file(self, path_for_file=None): + # This function creates file with extension '.bin.hashes.npz' where hashes of bin exists. + # For creating this file in custom filder use attribute path_for_file. + # Where directory for file should be existed + graph = self.graph + if path_for_file is None: + path_for_file = str(Path(self.path_to_xml).with_suffix('.bin.hashes.npz')) + assert 'hashes' in graph.graph, "Loaded IR graph doesn't contain `hashes`: {}".format(self.path_to_xml) + np.savez_compressed(path_for_file, **graph.graph['hashes']) + return path_for_file + + def get_inputs(self): + # Function return input nodes in dictionary: {input_node_name: input_node_shape, ...} + input_nodes = self.__find_input(self.graph) + return {input_node.name: input_node.out_node().shape for input_node in input_nodes} + + def __eq__(self, other): + # To call this function create two IREngine objects (IR1, IR2) and compare them IR1 == IR2 + if not isinstance(other, IREngine): + raise AttributeError("IREngine can be compared only with IREngine object type") + return self.compare(other)[0] \ No newline at end of file diff --git a/model-optimizer/mo/utils/unittest/ir_engine_test.py b/model-optimizer/mo/utils/unittest/ir_engine_test.py new file mode 100644 index 00000000000000..fd87373d7f6f50 --- /dev/null +++ b/model-optimizer/mo/utils/unittest/ir_engine_test.py @@ -0,0 +1,136 @@ +""" + Copyright (c) 2018-2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import unittest +import logging as log +import sys +from generator import generator, generate +import os + +from mo.utils.unittest.ir_engine import IREngine +from mo.graph.graph import Graph, Node + +log.basicConfig(format="[ %(levelname)s ] %(message)s", level=log.DEBUG, stream=sys.stdout) + + +@generator +class TestFunction (unittest.TestCase): + def setUp(self): + self.xml = os.path.join(os.path.dirname(__file__), + "./test_data/mxnet_synthetic_gru_bidirectional_FP16_1_v6.xml") + self.xml_negative = os.path.join(os.path.dirname(__file__), + "./test_data/mxnet_synthetic_gru_bidirectional_FP16_1_v6_negative.xml") + self.bin = os.path.splitext(self.xml)[0] + '.bin' + self.assertTrue(os.path.exists(self.xml), 'XML file not found: {}'.format(self.xml)) + self.assertTrue(os.path.exists(self.bin), 'BIN file not found: {}'.format(self.bin)) + + self.IR = IREngine(path_to_xml=str(self.xml), path_to_bin=str(self.bin)) + self.IR_ref = IREngine(path_to_xml=str(self.xml), path_to_bin=str(self.bin)) + self.IR_negative = IREngine(path_to_xml=str(self.xml_negative), path_to_bin=str(self.bin)) + + @generate(*[(4.4, True), ('aaaa', False)]) + def test_is_float(self, test_data, result): + test_data = test_data + self.assertEqual(IREngine._IREngine__isfloat(test_data), result, + "Function __isfloat is not working with value: {}".format(test_data)) + log.info('Test for function __is_float passed wit value: {}, expected result: {}'.format(test_data, result)) + + # TODO add comparison not for type IREngine + def test_compare(self): + flag, msg = self.IR.compare(self.IR_ref) + self.assertTrue(flag, 'Comparing false, test compare function failed') + log.info('Test for function compare passed') + + def test_comare_negative(self): + # Reference data for test: + reference_msg = 'Current node "2" and reference node "2" have different attr "type" : Const and Input' + # Check function: + flag, msg = self.IR.compare(self.IR_negative) + self.assertFalse(flag, 'Comparing flag failed, test compare function failed') + self.assertEqual(msg, reference_msg, 'Comparing message failes, test compare negative failed') + + log.info('Test for function compare passed') + + def test_find_input(self): + # Create references for this test: + ref_nodes = [Node(self.IR.graph, '0')] + # Check function: + a = IREngine._IREngine__find_input(self.IR.graph) + self.assertTrue(a == ref_nodes, 'Error') + + def test_get_inputs(self): + # Reference data for test: + ref_input_dict = {'data': [1, 10, 16]} + # Check function: + inputs_dict = self.IR.get_inputs() + # is_equal = compare_dictionaries(ref_input_dict, inputs_dict) + self.assertTrue(ref_input_dict == inputs_dict, 'Test on function get_inputs failed') + log.info('Test for function get_inputs passed') + + def test_eq_function(self): + self.assertTrue(self.IR == self.IR_ref, 'Comparing false, test eq function failed') + log.info('Test for function eq passed') + + def test_generate_bin_hashes_file(self): + # Generate bin_hashes file in default directory + path_for_file = self.IR.generate_bin_hashes_file() + self.assertTrue(os.path.exists(path_for_file), + 'File with hashes not exists: {}. ' + 'Test for function generate_bin_hashes_file failed'.format(path_for_file)) + log.info('Test for function generate_bin_hashes_file with default folder passed') + + def test_generate_bin_hashes_file_custom_directory(self): + # Generate bin_hashes file in custom directory + directory_for_file = os.path.join(os.path.dirname(__file__), 'test_data/bin_hash/') + if not os.path.exists(directory_for_file): + os.mkdir(directory_for_file) + path_for_file_2 = self.IR.generate_bin_hashes_file(path_for_file=directory_for_file) + self.assertTrue(os.path.exists(path_for_file_2), + 'File with hashes not exists: {}. ' + 'Test for function generate_bin_hashes_file failed'.format(path_for_file_2)) + log.info('Test for function generate_bin_hashes_file with custom folder passed') + + @generate(*[({'order': '1,0,2'}, {'order': [1, 0, 2]}), + ({'order': '1'}, {'order': 1})]) + def test_normalize_attr(self, test_data, reference): + result_dict = IREngine._IREngine__normalize_attrs(attrs=test_data) + self.assertTrue(reference == result_dict, 'Test on function normalize_attr failed') + log.info('Test for function normalize_attr passed') + + def test_load_bin_hashes(self): + if not os.path.exists(os.path.join(os.path.splitext(self.bin)[0], '.bin.hashes.npz')): + path_for_file = self.IR.generate_bin_hashes_file() + IR = IREngine(path_to_xml=str(self.xml), path_to_bin=str(path_for_file)) + is_ok = True + # Check for constant nodes + const_nodes = IR.graph.get_op_nodes(type='Const') + for node in const_nodes: + if not node.has_valid('hashes'): + log.error('Constant node {} do not include hashes'.format(node.name)) + is_ok = False + + # Check for TensorIterator Body + ti_nodes = IR.graph.get_op_nodes(type='TensorIterator') + for ti in ti_nodes: + if not ti.has_valid('body'): + log.error('TensorIterator has not body attrubite for node: {}'.format(ti.name)) + else: + const_ti_nodes = ti.body.graph.get_op_nodes(type='Const') + for node in const_ti_nodes: + if not node.has_valid('hashes'): + log.error('Constant node {} do not include hashes'.format(node.name)) + is_ok = False + + self.assertTrue(is_ok, 'Test for function load_bin_hashes failed') \ No newline at end of file diff --git a/model-optimizer/mo/utils/unittest/test_data/mxnet_synthetic_gru_bidirectional_FP16_1_v6.bin b/model-optimizer/mo/utils/unittest/test_data/mxnet_synthetic_gru_bidirectional_FP16_1_v6.bin new file mode 100644 index 0000000000000000000000000000000000000000..00fa110928d10baa3300d75df13b7773c60c7bee GIT binary patch literal 222768 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ⅇ!4RPoiFB4W^YV)Du8}KYZMHWP8X1{s~wa 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 z%0Aa_ei7bEa*Vv=d6xw^J1g;5EL;)iBk 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♷{*2#3dvT{)=`L3gSU*nddk<;kX3q z!yaPE2i@7YA!=vu20q+a=PaU}Le=7Ckv{ZC*3XmZ z4$>%+MMd)2Sc@9Uf?3d*LVhN3=%x4<3PAr$Bl=pIkdV#i`CrPTq8s4Ni389%l*?au zo6&;8cU;VDME2r7>`Clb-Lf|5+r&Y^Ve8AHR2CHe0}e_>)Ej;9jllP$o6fqRGJcOq zf=M2n&FZg0F?xfoWaAS%fa`cQKWObS|MT`!>L=9(HmDF7PDk?x4AaqKIW*kfmDA8J z8Ay_QD6P<6(iS?F_O+E@GtP|^bljY)ELESl{;>`NDRn76r~E=T+r`-)sU(oJ{tZSj zU&cyQ)ZfLHNH=Elv&KAek5!R{OKxERx=g%|W!ePZ#q>)W_koU6U0)Q{8od#SUI)+f z9w52V)#!PB6YT*>^GNqwR>@zSt%V74xY5=9(c2CD3}>aTw23{;rSjjgfXch~8VArB z_w}Hk4B4=R8VURO z5v7>^S*a;pL)RnQqo9?_e^>evRh;Ji5&tGX)V9eD(T89Ixa5tLfY!?PdE9tE+Q4#S zf57Q18SUd*@FBjUe5V#2MK?;9s3!Fh_V{YZJ&f7v51ECd9=9i>I|c+!N>pK(q2cer5p5B)2DmUEJltqSl2l{C)sOk>>uEs{!EVt<8Wtr zsd9oo(0SlE`m|!2+(bUi5@{VWgw6^yrX}#MsQjcqBqz6-i={KXD8C1J%fIkJrIp%6 zzp71Vn_NkF1$iv$_!!bb!|H^gZyF5>M(81Vp`)q(O|0jQ4^(w7M-TW}DHk;BWx&(x z4>{A1W;mZpR$)fg$)4Z?I+D)yL^}eZRw$XpDOFgQ8B5nmC8RE)8mxqQL3PS2QA2yQ zTa37X4bODtqx8c617u7mD78tHu|=9IIFxi?-Th5F*ge@}_dRz}|HzDMPTT!XfjT+0 zmbOywg~kW-*?-a6uFV|sxO@Xp*30m1>AA$IR+7TtwlWja42P_d(mFObXSLtdy+OCN z(N59(G1L41OiF9k1?O1@*BNsKq{F_kC)R%TwX}g9b&s~@*qe!p&ezy;2}wPtAlrx)gR@T|1qnPep-gShwKQnW^;XKPzdsqH}pd!hBTGmIImg_!FM0y&XTJb zeTfgIqbhuS>S*=8$0VE4m$m=0Yie1iB=16ZvozWu7{QAuZNXh3*-M3G(m>~BG$!@3 zejnVC;NGAToe-EwkI{V4Me>s}#4HE8?PhBnzQ6LN^%579o3kDKJ4&QeXaQrsQBs5s z88R7H){2qSkn?LNjU)%?am_8BuDFdJ4U{vp%$eTiQl|eQO0cro6ziTVpEXX(aR%eI z^B>}lc*@B6%*&vzH|B>R)3iOw;;WM`S|>s?c*mq+#3Y}ULgob2PdTH_gKu{ebCNcJ z_-LzywE?(u0Ac1g@T&YPdU-3S02#_+`fq3paBtwP4bxxGgrde$zn)%5dCa3c%`FZj zl{WhSUDHwEt3x=|3yE+>7)@;w7-7_>mj2XQ&Znbd*$vhFVnJ6&QXaBWx9D~IiL#3= z+3*6mEd_XlaDm>J4ruk&8nh-otF+~Ljj67Q*clZgzhgz@O92bzPdw&SP+0mA&k6Dn zbig(ESH2MPu<>d;V&K%FXdrKdrv+C>2XXGXGsa8amNi8*d-cU;-qK3IszJMhvI#5bf7^Xi6_NSn7DTs zef_=M;lB9^ccl;;$On2#;5Wv0c{P3t^XyKu5Nu;T*ckIn#tNXq*)bQS8Brs%7D39P zn^X*^x_4WzXb2CJuk%v0m08y88@H211&de<(L;Y-Q-LhnQu0?IlQf_uNE=UK|19zW zmxBC*gO2ykrUSr9KT(aRdtIYwDe{ZmL7I;%qshJ*Y!8WBEhVNz#nFz+6{7meSX1ys zqX@ajzFD=L<89=(OX3cO3GzQ}S4H%tv=# z_whAnf#I?ig$V}oS+}jfNtnDbF$vTF!Cl=fMSB^eXgL0ahFEK?7;rh?TlX)TmL5QY zdmkPVY`}VQ)%pqamWuu*R{nJAFGN;K?UhY&QJ~q6L7jz@YISpeuupbh{2Y&l`(?lK zEm#ANd6ps3EBNo=^Nh*v#;ECrG}HjUv}B_I`;*Va!?Ui*o0!9BMY>`)%T7FFp4N7g z6v(B0p{b;@@&InTlVheCwmb*CI}3mky4AHb&^hY}8Rg1>E5S=GM|dJwIo;+>vIcN=#TIxjRP7!qdD2GnI1i~4N*2u+;HkuJ2m!qV(+!DgtjI?vyN z<_6xtT<>@Zi_F_-0V)}BG@nZwiy6|$8BXXboYvOJGKpc zO#`iE>NtLrZujnFwOy-{_PT3Y^QD^TRq%n?FEk^mrFz}EfXB)RSJ9#z(Y%q;SFbH> z@@K|WMr(ji8g6$0_rVs_D>#f@r*+(|?OvIcvf=%|cC_7)1u$RV(Orm(nk;tCmvXC)4M&^?K|8G{;Thhvqw(FI! zRfr&?G0VAP`J5VBhU?&}$~gJ|Z0=-F25Ux-z}>I6T$SY2DhdJGj4xty$O+>isjE)4 zFQJnBoUjab->INCbTz7bhsFb6E6~+S0hZf$^P60U1>&lre$a$>o^)5MNY{;dYE7k! zvDo!RxbAjZ?Sh3Rul+fhiejP|(BI$rQO{G+xET92M6Gee6}Mdhja!nXr8k=LRh z`CH|N2l%aGrk!Sek`}NgMl;lcRHrFG;i(4B&>Fy1c}i^iwQGRmu3|l#*A6^9>*J4@@j*yS0kHdA;r`b%+!tHT0XJ6PR#)w1MS=e~P4cG!u zQDw5(hzBTemWU!(%bp1-)_0%=5b$KCfIon_nn*zsJz7PJ=c2D)}o{_HOf8SZP2fw13u%f_is zXlut;;hJUZ3$3z9;|GF~#t7(XErOS#7U+T#ph|ku|GNmx%1$a5)CxKO6}!4C;` zbVw@_j>2|Pl4r7&+C2EG<|@~f9ihHr80}%dhIXJkQeBpuFg>u<|Cnr0YU1}sOSFj< zw-dR_Uhq+@JFgdfE@UZNd0slu?hihIa6X;(Ri0#Tx8mJ}HKzj^*=36cKe1e#M$XYv zfh=@_R+f+XI%u9?b<{1UKmURTB>pasS4&_cGg%y~MA4V%8+w~Ki{*j~cu;~9SUQ)@ zjVQv^07z>?Sr_pT*@@PfWu^Dl8#Nz%Icv#|gnF(ykalV#@5!!AcC!IM(iq7iRl~8K z|E+?L5&8jMsDo*q;1VqkRUmZ(2f@3Zs+RHn05jSvxgzR>`nqfR`Z-6sR)LdtYhWd5 zY(GOAPzl#pSKFi-PL3;w${4et8TY-j0{S&+FRKxXNje$=k`yaSJJ4_TTslFk>Yi)0 zCs$ZUsepGQ?Mv1fQ^b9Ng~oC+m0f1X$p^@0TnW@5R}74b(7xe!oj-tno2>SgD!Esn z`}i3-ZA9LBZEEl1w5=i8~<6-Cm=3f;))5oAu2KiEFuo{(wKCFIjELFSsB$ zXR}$Fy49FZc6mmDGo+*(2K4&6awLD4|Forf76zKisWd<8V60Mi;-!!du-xh9IJmyd zK)>KG`e8f2uOuQUjh*ma;5WoM!F?!)^mM*A9|gN9!`=CWF*H zxtuxfu=pcVBNR(Y3Z3P7(lDfw4nc;ek(Ojq=x6J_cMMJ8I31Mi%xG_S=BJa)YTz)= zP#f`@tSv1?qy3Fi?jgvKa8N&WSPt~mK;@|xyYn)N9&hP1;k z-MfRA5)vRwGY{BeYxpT68B4BhpaDwpE2xi5r1RjZC@c44YsECX1Zl}@N%he#t}*Vep9DPx`B4c{EU`5H#XB=vc7J90zzdkp zXVD_$KXGP-eco>F`B-B=k!xtYGRnTJU3dO&l~PJcx8UzIkDOrZAb^3i6~}QzxfjHfR*AM!Up}u$G4Q@I#&@N$1HFbGW7irju;^H(J47Oa0;h zb6aUOX$$-dr`^wT@+r2Cjs}*p)$|Gqp?l=HQco*S!t9y+kUN+CD3`}v8p5~pNBkxq zudbjOzVNh8;9gH5ZKD(Hv7nq?A`=t+xDKyu9#Wc0ZP@E5FcFyTK@Hg<`Q%d>9nBPp zpnMtI=^o%rx_N=@@@glx$@pew3M1W&)R10h9P}sS!PzZ%6+2{|x3+0fX0)RUHLd>e z-s*$u;>NfEF6{Nj7D~kQK3ZUlFcX(2t$BE`2>t=Dj;f|K6dt*0Vl7t+zw5qjwDA5$ z4xkal6-xL-=xY*X+Gc_2=f7xKMUIak-5rsS2}Iyz0h zX*L%3c{^d|{8NsCG~B<51(`}JDUH#6p!q7Q(~)0V#UGO4cBE^k#|YHGDRx<-2vBn; zqh{s-_ej@bbQ+~gx5;7B34J#n@e4FfuV#F=Qe}~pgalhSEyl`YlU4#+?rgCsX#@GI zMD~l(AE=}Y*i_RbHE^ENo8>E61?4%3-JM<4C}EuR5x1tAtCdm3c?BGQ2egxrIGSfP zj=Q3ULCb3$)|;+C6V$xY6Ma0L$KKHOEJg}|U;m;ku(##_=!bbB@0AWKjp-ygnWPFb z>W;hmZrcNr&V`yMMFpR-L(p&6BUqGIVQzBBY%HANz}SVGI_JN81>=xZI3f=1kXBj4 zX$Sp@Ra!lz{^pD^^909RL39d1H?vs_&V9*LG?DS$Kg#HeMYA7uyy6C%ISx7FkRxL>c))T&-PpBtnFE5Vzu)g?6_F!lzJH}oR zsy_yvT>|Z|YPbh_B@KXE*;nB_&6^Zy7t`y52dkZTv)KnL_=p_ImL*o>%kb39PUa5R z(44)}A|*&x(j4+2^N=`}+>EVgw5N&qth}Gqq9?Snl#nZ~igXWJ8T%RSx0~Rzt`-3g z{%-BfI0;zWX3@@O4YbhmX~WnT@fOjQirKqx&xCcfZqR8pqI2G?6~hx@!uZJgsaHv~bTHyMKc!8^1zD0}pk`<@^aBnt z>%!ajo1AFOP0eFILy?7BIX+r_*a12v`vCjk`E2OEPxf5i*xrT(dju%vCm{#)Hgh%H zV^$^&0B>s)$x%14hSoDy87Ias*Z$T0Orvq?F0ULppWgHggDb=a6vMp~c$Yk%bVeGa zpNrb!%YvNQDDo#thTHcBTo>*wYpkhgKW~U1(Q!b=5b!qqSh>w>2HzXGKzV#8_Y-s? z5}#Zh=EeNlH>(|b<$P)E11g4utYBA?S3H7q&GYMK=#AvJY!cd^{f;g{Hv^UQfy!@i z#raKH9<0ZG>%IZ&K8e*}v%R;}^QgY~oW*GS@m&<_Sce;_V}u3SmFQaBFSt4Q*8X6Z z%x;VFDR1bfGRbOtvzBxPe%@I^A-FIc*KPufwTUlEaa#tzCf+kM1Cwwm5*_Hp&Z!4j z7JAGfFOhQq9YOtAao15i5&Pgxj8V@}Dd#xa7dW9$(4D9ru{*2>{I>MU{}F97=c2Q6 zD!L@Da@B;~L3E&)XyX~dM>xr7k~mavv7w-GT=;|z*g~KgsJ#QRC$X}7JINz06|2J> zl#l)gceXufol;KUB&8dWm@rN<2|qSs?Q>>h6st^NL3N;*f_rIYq%}r9pg$Mmbz;vc z7reVNqa_LMnB#J$LJL@ymW!kDbhv6e0wY5L-HohBo47OK0xChgQ;u6qF zx6oN;ad$QKZ15QI1!vMUV3GClw6>u!UaIFkgd7Pgg*Eov!sTmlNA^NnU3g*L?D~*h(#k$|2xc!!$pIHmB$057Hgn+jpNG zkbvkTPx4LR%Y-p@KGacQ*k=Hbn=B-G^|pL~ARwPJ#eSFhgE=TT3ww1leyP#IGd8`v zfaFvBIOP6+GpnWrf>tmk(9Ctlmy)a9C8>>k$%Bj+)lf*zMGGhZo^(&mxr^e zIN9o=&NO?`vi3wNNq9mtxF@*U_hV9d_;s~$R8~D8owu{}wcXU}gc`XqCC~@dPMf3F zl8WNUghR$O_B$<=8R8?+5hcd%CCtP(a5C_6{)FbOASwW^(kLO>-G=w4PvwJ-J$#bu zd{zt8DfYh0YrKrj*7XRCLc|n)SKCP`xG(O7d}KMyxSRAc_=0mCG&tpn-G+O}i;X6z zFuO0!3Kk&UT>nU?fz^&_IZqEA8y4e{;46(gn%wR{gKVz5oibURj87?(K*^rGR;GQ7_u^BzrS~g#14Cvn-H$#;P}*1j zRjO}XmsNHs{uoOmyXY*rMp}iybd`!e_C#%`7UA=~0j9BBy_*uR{u#F!MJNp%2OLY4 zDsnB?TJN8MG5%d{ciDxGeW`^^U3esBav2h3Ubf6XBRUQ_wd<33X8vKnhCZP?u+q^_~zgC){c923m z2Fd+d;!trV8|)ga{l%sQYez+}t0>Iu={{{K>LUsW$j)zMO`H14TvkqRUwkg@W=Xb1lrCHq=I)+$2&51k5sfD#f;p))d8M_69y zgYYvErf+8WbVV+1b@RWK6!rJuVEG5y*l22H*}Z8x>A}nLGNdp#VusTN<}PqOR={tA z`S?M#9(hLhlfgVMZ^uh2uB?y2Kg8y`#dmuh=x2<{I1i)@nICrrQtxoQiyr37*jDiM zZkGl|Y@X|?T$^ohjjym8*I_BKe~C?K5%B`N-FhqC)NO30 zHPd(lTZvq}r(-gktJV)-+8;N`9>xCA^2?Qs-Rgn$RrzMQF`D8K$O$>W)P@%DY_gY2 zm5TRfTSS0OPP}!bgU)8!=?eHmXML)0u<#%R3r5Ei6>@j^~-7!l@tt80|;9mM=4z564 zBu5mQ!CVm{2pVxJqIk4}Hb`j2?y;}tIi;2zZr?=lDmeOAU8Vbv|PBI0s3J$B>i$#YVwU1BZoY z+qc=Hz;5gkdxi=EStTfpPnt~n(TzYzoaiZp+hyc)V@ml4*rvjwFG5untp|xfo_XV;nYbZS{%wn}zTW2ru3Qusn5~JKU z?(2M**@-U&s_GlG${qxH%SHY@33;vc23#=VF10SYf#qSV&^vlV7$7}2W6>GkIc1Qt zUVRoUWwlUKs@driYG|w*uv}lR6bh_KDJC zYauv-GqPKwD#}!{3r%omut@S&*obnZFBSqlsV_MtL+?&}$;8cB&G}1uLB9ySJcZac z?LL1`&)DDS29Vu8maorlin4K8U1#osPM*)fu{0~TM|dqG(ySA}B%5qS6Sb%4B72?K zfE`~sO8G4((w-({NiWq$%8kG>;RmY<8z){z+gu&dN_y28Aoh@YS*o=NXIOL8n1nLY zV_^K&lN+$N)@E9beDQS^PmuR;2`hk_Qw2+`p4EnxuxpurDkJH9v#bASxT*H{hZ$mK z5?PVCm`uQ1S!r z-=xfnd^DY|SCEq&)6g|CGqA&2VR^+c{;m8@S3G_Q$)P`4QMZojbC-M#wWWu=3-LVH zeY2`GOv=m4lKxhhYXxwg%A42KhROutJKvC`+h>jI?lWS7o$ac|YoS-}Em*aBY94Ps zc9Py_VOkbsRa#k2pb>m95AjciMn6(P_$KTU!;}dqoKzA1Q%}lhViwZsVZv8BfQRq2gmibFh5J45>rP zO3pI4jsY!Z?5lkS=ZmRmjTT1Ae~UH!MQLV0kvdx|&DpdLrNPtQ`3Xc5)Oygea*yt& z$#fNNj5^~nayoxw{$n7uGtuce;egt-^gr+~lA4&`;OqbzjRu+hc?`Ri=;5n8X}ntK zItdS=r15mM(`_0VABB3n6)!^}iRP})F4OxYKfWx>&I!VGB|kk$i@A@Y3iyJwN`EEw zfJVn1ynyo$Tre0V^>Gb$91wYU9!D>yaok5wsPVcbhNeWEDiO5hI$y3O2+3oK`W_Zra%L6v*9*8n0z~0#i z4JY~KVstbrPEHC_fxPer*K*~-&CKrXGIx>%(&&m$?1#|qJD0vQuW^Utkz5@&0(wM0 z=`Z-2y-8dm{=nC52!cC&FB5P+t1B&#HJ*im=kqmmIh9_yUhZubGHNU9*H=Q1V}n-r zb<xE3 zQw%z*vofDMQdqQcibv>oW4q$Z(E0q)tY(){fo&gH>R2SSv+^jL=SPyBOqL4DRg!un zU5hUqI*2k@-wh8?3>t+#xlrh0_7-*Zh9`6iyqz~Yj=6t83aB;h(E>f$otOTYa2Mse zo3m@#`+1_$lKon|zNMLqz*)4&UP_LTZ>|%pGB_z(u*-P1dq0t^A2~Irv)Oip`p~Rm zyar}oD>_};iaXge>E-yM<_^9sI2>ldYxqgxWZYj*m9~q$wedne_S!r|?%?M+`{@pF zTI`^A@h!fP%@k+(TLi}8{_^wGQnpCa*RMh?fn7I5V$!w@0t!n(wn#Vzzi&^t_w?u2 zIk*kT_P9YXAw(tKX4kCYphnk*M%Mv$-QXv>%i8H)75oJ5lO!}e$ATNw$v`11AMTKC zxc%Bj&=3q8-a+zSHr&i(>egAdJLy;DK>G0PC8!&uU$@x<5K1(SQeut7FTkVvS=>Z_ z=H;wiN~y@Be4<=iUk6V>57b*s4eWNOvH^i1}o)1}7N-=gInjtq2;pu~wp&q>8s#qaVF@IL=0hC|EDP&St?Bn#*b zw9_6-lGE>#+W24G-<-K2Q3nF1F@SC(8UE);$;!}vl4|qC;CTDh_0ipc)v@>R&stvo zJAa(%bsv(wiVq#brEsHQCL1r!;XBR7=~v+=)kj;bbp~%tVcOO7+o#YTmaCLa@8_Dx zQ!Q)`a+C>OMqlApI0k&ndl+=cL3a9yamZ{)Hqo+lmgw84H*TENo96rHOQswlL^u{V<|D*)yMij{{J;;wcSE*RMrv0N{avo-{*#qOH zJXtA)FG>B~4cxoqw#&CkB3wWI|IWXvx4`FOejy>Ml1pq%@U+ie5SoTO;UvE*$?syP zk&m${!O^HAn=j>e%%;QmA67Y8*Tdv(pkWpVr{qgD!kX!=5P0D_>%SxYwP8Tc>xed_ zuvLvd50;ZTxr|P8+Zus}v!7I#x!OJo3D}Ubg0GAz?^ufyw zxw0TEMlQLG*vgO~MbT$NMF##={v}Y)9xsempF3_ODDvd^I$>K-Id({z6Q4#>t#JGS zZRLB+9`%w} z&4l^7lX>m|KoE?GF74<|w;0V)4Re0Z3gets6GfXFtigei^bT&~4P$?Z(^wF_w^}%p z=u_CoqTm+U34Vh2RZVSNLT81nw#p{bSlHk$Ag_{>1ESUxpCB4vjE5<&%qg^C#Vbxu zrqN=-Val$s=d>mhV=r4{Tn!YNCMFaTizOCOJD>qh9i>8Qv92%|XMj7#((lk{$5KZ{ zrPGoVT9Ujj>0(ktb0pmYJ+M;f7uHJKLodkH*>m%AV6t^WUn4fdC!}5ei@{d(5&uDI zj%v}Gq#M6MLVSdA*WSr3+SU0Vy&d(Q#;9e~P%sN^r=_!J5d=P!CrC>07^`>)CBuo9RyVE&2(CDFIK4^eN$g6rFW= zQ%M`e(ZvcBt58cCxi&M!9f~i`;_h19-QB%~dh8}wW+wRJPI1@8-C5jW`QGnOo`3Qg4HUCtv*bd!bf6#V>B>hdvd_S{xq+8zz5IL3;&>0sjmCIJlf!7Ky`S?> znrZGsm1qDcaCrkYl~TaQjIzZ=Of(AM<=RbgB>5Zdu*>>FDIZ-e77?42y~ppOT4GPV zGfrlg`C(@XT+DibPj#-9XK>({vXMr%z8&olfVvsjNxI1=Fpy4p7F{GBM;p-$^ey$d zu!P+R+z|u#iJH828J=MtG0JNh+4mhMSaWxzNZmCM1+82qxuK8`9cAD2akK(Zrw-tX zTs3-GWU>gIVta&WzS3KiZvk*&cEUKrt`EWE{k!;4vc>2N`@I#k@mfV}K^o#eSSWsh z&RSo2pJrAMh-E90Ar>Fosg4nkp*?6a@!{7(d3Kh+HE;4FB+2Z{3j{F@p(7|zX_CGw?}kEd(lz65Noh!E@y29YVg0TY1%L&BydMMt>3XvN8e+QirWHD z$U1$f{Rnc?sc07T4x`KgxCR@}@_P!(ZRllw$jRv+*|mK&QAbkJ4?{~-pWURj%GSZH z&4T(G@qufte|DKaQ86;{>G3wKl3F04etl7jAtx>-F^3D|@Jk&UjOW_RGjs8XtjiQi|*dqNC8^^k;*DM+K zMwU+c9%U~th>OPL&6JEy0M}(P@G1|p+uAr#$J^AnL7LMw!d9a_I>win zpYW%Q9r&oXKG_>Q{SL*`WH5YRkHWdl1K+}ItlnriG!Snu-7?6oR(fjiuNTL z6-c*0(TKT9SO0LOlWi}M3k!m7<_=8ftlo_ovbho$f()$?GfSHj^}&Bsya{J|jrcbF z2$=&J*9p9y{>!(97Ebu+oq+<(oe^UMf{pM~zAq#Sk7qyBc03U{jw?aaQp^}-9PxD5 zdngb1J|!5JqKBA?B)7C(%gaZiwdOAEK7)K7c8V8JVsUr5BivX3W`m>brpRD4CgmtRT>BKH#ywxyA%~0)=EgW-YK~U2l}0c1%Am zJ=Us9OVUfh&SSD;wO_P#=GTason)3#R-tiHxJuDj^Ddgtp7{r$JUjy#_QIs0C!lmT zRLH-(jdL3oZMHc;PbJ4Wci#~@ME!R6)9YI5qrs4?(j+iZFMuoaw$?5z-N=b)tsO10 zoqz%#pCl!u+`;|{i=lVCZA2z~Af5z7mg***!5VT!Nw!x2P4HmDi5FNa(mX~fBaSVN z+~{kZTv^XDCG8Ex@FeUYTfL>&efV~*m*KNF%Ygsoi1Eo%Qt!{U7-6K1QkZ2balSZL z&^Zt-(<|~O(qg?HYb##jGAb$t1<)&DQFo)s+F9Ei;k3~TpNuKWm&z@`XP`=p1O<&! zlITu$jWG6142iQV%V;S6ljpFBye@3-cHo2Pv@=TEswaVC*oF$lZ{`)^R>X`I`$=nU z<&Ay#ow$Ikkv*W3eSty?z13&pMgs-Vj{mI6BxvZm-VIh6=e|(b&2NwKpUJ`Sm9lRo^e%~0+_R{|D zNYl$L+QIB^&d$ai=@L&ONo=%pb>?i+#NUrRfO*a@?Ns(-O;Nn|D`=fyckaViN;gBC z6~nDl-%u}37o3L7X7H)v^wiP3fwR0?LapEoSKi98c(zyqZPt$9eLQy{+2}8Y1m415 z)ih3Pe+9N`og=JEBgw7GVe#co$q#TXRpiy)Pp~; zl$ZysI@i%5bva(lOfw~{7d@(>Ktt^cYR3L$I%{G|{xRqSCIh_DHjb zFit3#RZeLYc3%6Jt>hFpBGGbncmDY2+5lHuQcq2!!-Y=LL{`7lpNxuM^r57R(kikg zco`PbIgX+3rgB3L-eJ>gJaG)xa?w8ch+)ZSW!AJ!u%{7OFAH_$Kz=oD7yoRIbzN3} z;{L)G`91RoDoZyNs2rrDDKppv{?(p>ztitfsn-syryWsywY*Y_$VMG>MJ^6hEw7%g z%=f)>l(9U;8}L(IG3L=k^S3zFBGO^pDM&&WDM-VF1Yk;j0#?su8ORmZ-&$2~j`pJX z4O~UH2oKPn_-s0oAnBzxMsNk!;1&UgW3B5h{pt(T?y+jtFgD2Ej^)d4g=W+3$`U>m zsHF{91&**cU0+#94{A84zpZ!ON_#2w3{_9!?+cRG)<%G6SZs)shw>m@YgZ1j#Ke`UvILbOe&lAkcct>S%KhZOshV7<%0pkB|5h<@CT| zzR5C*9|;y96N2G-H~UBZ0nICpHa_{|wADZZ55?21ok;?8-A~r{FZ;r|(N_ktf41Wa zZa4WUw?UOOfz_i;{rA{WdO}+zOk_apW8!e&x1m#Qv!unW z4qBgn!Z_ip;l86?!!3*)Pa^DZYPwI8LfKF4HgT|cgK`;Eh7n8oXRR#F)?PbBNj19j zC`+ufg>5Lm%RbZEsb%0@uYkLGp|Mhn1#Qd={5yMv(T2(1GnNr1IOz_lO*F?7+RDs}K4uIRd(xr$DCZSwINz~o z&u|HHi<~3*2f?jy;|hJlJg%2`AlfL+Ghm}g^=z{)3<96~Nn>=qlzIV04#;qq?L}Y$=u`gc)^MI$nm)^QNov z6G40FNWqizQOaRu>gw70p^O{A)2Xh_$D!m!phO@`c`VMS;Gu-Gs)H<@Gu_re_ z0@I#LTGeQ$qrPJnxDD@-EObu$;Ja=~VsNgqYOGb}^YjUHu0MoKCl8cAaar-Z`PP<= zYe1(lk`+{^vTgp8)^Yq#|7=p3r+^Z(jcF4PYZ@M8w(zzJOjjPFT=<37N4X}drbViQ z>&O?EfjLh-O(guxo5)%P)^nh-QU$%{7uj%W7Pv@Gh->U2EV1lZoL9=LyLl(z6nM}A zb=#`pzTd0`*(Yzo0a{$|qlKcj`XyZ>%|Nj<*ImWjYJZ;m9`C~8q&}WX4ig_TP&d4U zmNGGo%G|2?<Xqx_y%K(pCnRKpQ;l@d!v_z#I)X$ibBcA_Ol zsO~w*--0x(dLRc)Ar0l}q?!1VoWZcqF)Yv_nDQuGk*52z@D{X$jt7rkLskvNlT}g= zd%R;_W)YOBO^l_U{=6gX4{D(%bQSafnR-oi2T+^ZrCsyQa2H|C951pK#V7kR%*7~* zon=M+v6l1ry63C-hBjdm7HC-fR9so=rW{9eP5-(Is;!dmdI*kvu&BS=0z zpQM@d$wG24_(jjmKD24TAH8xC-`)bzVIYIv3(Y7Ep%um2AIo2goHsv09;4m=m961(xy){iGDnR;t> z1a#;oJ%dj0_c4RCG#kF0T7{QD##*Ocm)2Gq>#0XJzV; zcJR=jB)yl$nhTXGe1I%z6%7f+s5Olo_CuUbn_IxY!*<(lrS`XO1+D8vGm9m%#{P0L zavt-iCHNfo{B6i?>hjb}XiT;WpW^>PQ}CpO+?F1^fzg?D=iAw9X4tz)34!-YQO6xE zj<%Otc`@l8_)lqpbXDm5zCdRXm9g8NuE((vxISNQ{cQB%ItdepX=fe%B*~o^UqJfLWMmv_D^4=Y z5RtB9x24VE4Rl7ak;`HQy#p@^o5u3yG}sU2^ZX~x(;MQ9^?Bp3B_Fk~A@!A|^a=jp zka-g`$Yy3{fQ~apPqHK^t@M9rj`mQ$ZU*@(`JjG64u_no$HY!%#CfG*%58d$cbDtA z+9L4wGmBBtU&N`9oA`+SS3*Cn86E8!j!Hr@+p9oJ^S33%sK(Bao>E>)OJrF(@$sPe zDJj0!Qu%fNeZCXsbbF*W+HIDM_9@jMb+2P&YqHTe;UB~1@$#yqOmwd&o#lM~A?cHB zxrIzVABD-C%a(VK$b1MhyqV&DqpNVz`YigeZ@2%0)X!6bpOJf$zr@x^GL9=$!dW@s zycwjcF$Ezucr9OtGc=E$$`!~JZh+(YLi`>7$LhxHvh<-9m1#;J-q4#98>cLkfUM6p z1G)SzC$3uz(!Cvf$y{k7)NT&apH5|6GIM-GEM2vk?1@;}SgfhqHe&{F6PrjXI2y52 z20P6l)Z*wf<<^l`Vzp+e&uj2@Dk2Lt`sP3#N$ z%XaBP2SO5*=Y&H@YCt>0BJj?C7r5s3P zn2S!J=HPu20$0V=oTB66*yyG4m==*1;Ai3^a?5p6uI+GY->uDUGzK~wZm|zSGtJ%p zot_>J!`P>_#GCkNr3#C4c+wUs$IMwa2YbwVp=OWwY54Wl@&7P2Tm^O}H=SWY@uXfg&vIy~`Ufq8a7l=NFzUmHq98IHp zVJ@-8m_e4A#eiJVP5!lZl)rv#Uepk1uGH);Mx)_MbHz!z(?c?sf8a-vi!s*16JCo8 z=r;3sLZlWd<*+{S`%2{otw6=#I1&&Wv4+^;d!(;H`5iZ%c|@823ts;E&J*&m@a=|| z9z#j|BwvB4{+m{iOX>UYbVZBz2}iT@^4#cRY7*_G+;eUgP6E$)y4f7{fL?YnsY#Er zb@;agw<#Z~UojAS0*1~GWOB?f$8?7s`L)OLYo3UmkdSysyY7E4A0#D6N7PX#<^#E` zb&~dgH$^|uUGo@zjmpah19=_Y^+bCzYlY8PjyjquKhR;8LL*QfsWcf51kZ2mEBKj< z38BIXegFqo&DFcnBeaRwhV+)QM1=>Whrq{?@(*M&)QXY|-AuwBG9oY)~Tod`bTx|UXRfmN6JM_AD5pE4+k+)jz zQjN7P?jGoxwY}Vt-k@oC9{(lEG+O=BF_{yw2W+leNoQCi0R{fQ`74w*z`GW2^PM1f zN{kcp;`5**e}q@@zJ4jar;-fW3ugnf<$Ay?i9|y!BaK8{G7gz4&|=&3RQ@4roLNN~ z$m;kz(s1Pp8zzmX6@kd@bRM%8Bbxe)b`=l6`BGBK0&@OVd6?)Hi?ONV9jUii-@jiD zaUDZ*ly0_}Mh5vyc?T}YC)yT&GVDTCxdtoX&QRN-IqaRf2?gJF*M9$TH+T3%6@{JQ|1(rP|GQdaPE~o26y(6(m{7O=krKPYI(GJqdSB zVSEDBvb`Y@3F*N$#@2+gdYaxucUkZ8TzZ0*!&aJY#EI+5D4U2MPmZ4vp6m<(y;u|R z1>8~Tk+<~Iyt4Nn>z0f;XfqSd(b`J+x83DjnS8hOY}-g$4p|L39-;QeQsayJ|2dgu zwSHo8aT!{o6y`rcU)aW}xwGk1X9vhBxCN8NuSSb#um20KlX}D$Pxp{PT4BduppMGx z9g9ZtYj}V#QMzPq^^Z{?UsM}JI^f@)EvyLtfFr?Uy9wPe=E)zOC(R1x9ot>usC8Ow zBbw>#Z*GF@!zw%hD3J5mzf{Ek7)7wmlW7h<%G1dfSF-&Eza=*pN+~YOa$}tPqPA6S zoUofdc9#vl;Zgby+SX{{s3G;GKeUeGLg}&MHy=okPy}wp5pN%OqD9Ih(L{9B9|r2_ z7OYO-2-|2p%_waIpi*cJbCz;+7CbXC;$XHM)kaRTFYp!q|1WC>9VU;2d*U%I=P#^w z)W6Vuuru1passVv*;*r|2wes_A3wF-Y$n_2TNXH@+yhmvDSyF#iVVlIN8F5@spz~F zIR-QPI7e^bzf>ef^xA0H$Le=ckJzz}VPubelW%;C&*zt(vkz)%;UErSeT)fvjC)%T2d4it;;iC>sA(0884JfOXl zZ&>~cZbI|H@%*3ZWZM&->K~OyC`rnV!?op(!Q94XOTA_b=85@+M#!WWcI zdK*7!W&IhN5+6t%g5xnGZ`CH`kBlz9BvD=>lMwl}R?&W*BuAv;ZFGIqb!w0#JXU#W ze;3{Bl376G5B$L}Cx=m6;t zAowiN`mr2OK;Mhb1wY8kQEOsBqj*&G1v9;jCDsoPhPTnTg%M(R@J`1{hrq#56F*ma z`1+GQj*`4BIQa75fVhRVM=7was_v@@IhL~XDgG~JpM1!^lvu6h$YS3C^vm+fwG1|> zaqClf8EGm_jGw@JXje!A`>EZ;w~&KUT3WbIik7)!kJ749@7k$a9^X1%MJpt~ zwhbV+4WDrwk{$X;_v9(&NpciUp&M9WO_JLak>}N(rH7+mD2G-Coo-dV0sA|1Ee@ie zfq&J2o@qaW>d?CUjWz@2xSn&!O{FpV4aZ*?vnR6a*a(ga7IG0i z#g-XFj(avq-$genpV%7s^grSPz!E@uYyV~Ql)EjNEfl7!%zoNZnYVI&bygTcz;WY?0F{jMV)*;p;?TbqQD@3tr$$p0be6Z92Q+Q zFb)ay2mP0?L}@;7bgHoVXU9n;qUp#P&91o%qq`zFyxbP&7qgtQlD$$G-m5ss4mma^ zhrC6@qIbabZcq>zGl6!oLYl*lqFnTmXlHHYw{ktQOgqTK%#%LNs0%w&$R~6O;r&d3 z4pNT5sZ@^!)zwm%z8jS0>B=nmCLb#IV4qNbmZbb=G$HByl-^Rlh(>C%Rvz3^Cb;U{ zWN=L5xOl>h$4rHt$rY_!Hn4K_JVKcNZK?+)(0}Eov3~Mr;GuX;X^;OfE}DI{5-cm) z6yNEQ;&gc;r>p@k$`bKiw17O7H2oZI!d@t~EbD14G)k%jJ%2aJBb}Apfv)stw&~ppy!s+rak!pjI#^S^C(!;#Tp8*XOb2umcI#!GBXeVU1R~ zM0vh8cynDnvp?;c&>QIB*~YHSPdX$K%H4d!mD<`4dAQt7pAJf~VW29Er!7Fem97L} zE3}gBC2f@}SXh6L#1b1?30sKQq#WtvFAlS}$Fu{yiznbYdyQ|#9T$^@2ENg3M|L+L zlP^%e_#PNzV!N1bqioq`*E(Z?Q-FzRfZ0riy^&GW?jl@SsAH@d(1MuXyZfZ-sI1uj`u9}1> z?I1@bvu2{^vr(cSnMY{C0fiWl)hNF(d@1?DPVEaI?rffB_>J5C8Nz{`>)E)sk2 z3?Rqlr`46)u}5fiTp4yHD@Z0^nRSS+We9dqyTk@`y8XPoKI5<_58H1pHM3F_cOlT| zU)JyAdwk^vkqxA;e0kYV$UHU7J4n+-siZLmzx7UuuE?;sQFK7&<|jGA-=81Od==k} zVtH)TQ{@xghli=@reT-#H82(1?K_RKExY90?iT*eFmp3`3(sclsQSxhQy)2|`BTvf zyaODbYvqomJiN7|3$98F+Rw^@QHdv`yn*#4-r*h^qJQ(6w5a)uYT{#DFT+af@bA)b z8cwE2Inr1PCcCPp)Dcm^;mBBy#d+BMM$xelikW4Li0#K^?>R^AoIKgGV zhcZz0g!;r6tn55vOiWmVF49Kw9l5Sjhn{zZJNohN{*gwG9EI#sHCopH7wrQ(n2UTY z9w@$3kuii-h8aW)=Q=)$m+*JtJ?rpxlFgKEwJS3 zvx=l7712BQNc$q8tS*X0*={XVoFa#+5u^=$oK=L^P)qSf(mAN@e}O8vW!MvZ#jI)n z>VQ04nx+8555IC(RbE2pTnrb{2Eyri-RLRisAt9Z&bjOgc>&a*`7nd+oK}W4Vl7~% z{)Zz73ZnHqpE8t`2|SR#<06o_dQ%@IB$G~-TIr>Y;k>yNiyKo-I~}~m_GtE)W!}d0 zKYA>9Bb>2wM&*RCghuY4`bltZ1;kyMDN0kbt65igNjF*Zuwv=}&{_27&FFW$6CLJf zlu_a+I)`N7Dda7)74$FUZZCGdspqIQzcboL?lvm?k0~23p0-N%imoIR+B^8X2d-P0-Dr*Eg@M$sal{s>h`RlEr)_PT`ZJd-$MsBn^9W z2ObEY?89kyF%flLc}yy7Bxvcpker8fN4-IP*_r3_?{gF+OZc#uFG^5|LnHWAyf3D* zK9v82x>ib_DBdE)#P5ML&t+wvv0GcjGu-2}ENwqhZEvJx`UADj@`($~+r}?Foy`u$ zEIDt?X6wyKyhZ!KTUlk6li$!w@= zN{VB+>Dvmmt4jWf-(<~kEI>;fM~us)RbU-nZu19h`uFffe5bqudY5|0fVur3yvCj` zr0b33Av9f0Ms_A?dFWcbs?o!L5A7#c-3KX5eY94zH~o{2348!e@nPhYx7t6k_F@ql zz`xvmC?rJLKC!#b?m(97N_Um~%!){JGd4+!fL7Uu1{4Llo{!jT>&^cbZ5s|?uO$!4 zG77OD=6zP)GtK{4Yp+j-`S)4dFQK310P86-%WrX){S~>1s&WaB$WFHlq2e1|N^vNSg+lN59V6i(sY(nt{Xdt4R?WG_Ff8#CF;hII{}El&+iBQw|kA zu&wF`m}rd!j&xzOfwL-TTRMYgZA)r?Wde=o+wo-McjRrMH@bpfi5~4LH=x$&C6DxM z)ORRDGY^Zot@HG3R$Hl$7RdjyLCOhpukAQ#CK&RVK$sTqd7tr}jVH0DjyGlBb|(6Q ztO+RnoADkrmW@O!xWm%M-Zi@?|14jZ7MlZUDBma?^KW;A>CI^j%@LnyZ`l)k*V>Eu z_+arIb-ctwVV~6lMm6D_EJ4y=d zXG?s`$O_V3TxFgWm*A=9Kwf}`K`Mh3SVa`}l^L>(N8&}`e&`~da81D@If?%vrjV%2 zh1xXw%eTac$A(-^oMV<{q0(MDosWR(agZ-aOM#N=OJKavkrBCrJsdPe*VVVmRrEU$ zN;)}g8STkhA)GG5C(KJqRoI+<_4eiKtdpeo>o?1#HaO7=F;446vbh#@#M0PVivOeK zbNZx{>JhoOZA`*qX>o9;Z=vJ^CLMUh$r~EYYCDN7N2_m5Caru+kR5gBLwr58c)2K3 zows;3y`4RYUhwxbt}0_}ZN<-c5gHypng-c8eGt%O!;HDw6XlWo+FqA@7wlPUxfA!7 z{Yb^_jJMVV($gr+qRn2Yv|~5TMc)h2a$_^bJR7K{uI5R;2hK($lMKP9*-O+4wq38~ zPSNB2XIWud6?ktyg!x(h)Ye=SL{d}7_HmGcvmVb!5o`}SLQj%{_KKEiWU+h=F}OXD zy*Z)o2@V^u6<4|POSjD|`~>_f+xah=6!qI#4E|Ng%<|Vr7-jlNBB;!Jx{kBo@@DnA zJ}hBK!V4|PZ{iJ>O!>Z%?YkHKRm&^iM4OGD_$oUK)bs<<#nA+Ffwd)1a>lC(dNiIT zUy7{5GD)_XC{MSwh3UXH^OJKj^vAIX;Ri5<$EZez3v>q?9Y159YdgJX4N1)9tF6rg zH%x>Y?HI1C&&JK=Pbvj4+oI=obU6;*O@TVP`V`WNz}eSp;P}%cM?F3g9`&jS z?IGl)QbgYjQ=s-jUtb?vakA7pM6AZs@QJJ@@)7!~bT$1I0yhFr@ky_?C+oNLSWX@&7+bOUdDpmNsS+ zUPk;WG}hXYzsz2NI&v@JyCDe=%&I`|cw+~D8GqwzkG9f>q`Da^b@g0m8K8I+`#*9G=t6ex`>`ZQO?z;NN0IT{x*3iD4=g13$N z-BDO)iPcGtJx<=>85a|<3>TNXVvIAaoY24;rLRwLsTXM}e1l#@ zhg~a6_h&~Fw!-K0+vkv?a9isVO0nOM+|@!D{~-?^ep8pGH>`YxUS9wm`q`7)~%f;=4fxI$xCCt!Uy0vzSp? zn(n+A|56^}*asVmv7VReU1PdqJs(aay9LM0__$|JMH`6twEp3A)3J;0b zoyRd(8EXz8gR(anJ^0Gl@~*RTHjAQHwSJlB*$Dq$X-n!wl1O_wcj6eit=2nkv`@sJ z1Pc&~u-hWNGH2kAEW$WoHgNTmj*$@%yj_T|7dsor(JWkz*zqxXRLr3Ffi>@gF7Xq} zYjpM6%(r-huQYD$oJPJYpYSQM5iSOKRvqazs9&}tRjY~T8jbauXp(G2Ur=dNr8C9z z@)*{gnQ$5kfrH95qX`qFcH%PIF_sUslsowT*f-i^GBoC+67clXn}a(l8$qI8U?e-I z*Y!<n3oITIo<728Pv1KZ@%`E=a*w)-{D%x)*nI8H z2A@=0hUG8jVDiJap7&CgOHFxMI&@8GGh4^BgFO_FCiVEF^hxO(ff(~jABz3}-*pq^ z3V2h)AuG|K{gG)r)=OIp$UCGYoUMnUmQr2qo-#l`$|ym;@xcQu(Y2cw%h(uq8Q0W8 zv=%Vy>F2Kx=eeouh+C}dF!`;I*Lu$4rt~bN%8W#>safWi|3k@Jq<;Kt-oTs)RG7{B zzq${4-9JfwR}pbDKf{}gZ_zHCVoW2;S$_Ks=Mt?wxv4Jud|Il@w;i7sQ<~MHF9&dfxKIXbokaoA~;1ikE{%O-Fw^2l}0O^M|&QwXw{$fA{<> zRbLu_J8IMTGCqtK_l3xZgvE-;n)!G6FVec+XI_&g7!O&AU=jVGdlG2(CZiROHvDhr zI&vnh96LwSve?Q#rtVvvQj^7w8P!u^ExDZ z)(>Ej=rRAI+VOIGBQex?n)OC_4{VyHVlFL#Hf7W*0L(tl=%yE7NsywICgzYq_=#~h z^NzX}S5i)Z{$Zi{1BaB4Z{_+Px7zWO$0K&=o51h`_sB4wKbeQozX#QK>R8H za&xPEgKda@z(3N$kRUZn9F2ZyWgPcm5BD6Dj+cdkN`Bsj-U6rLJ6tNhn_`&dGLoHl zcyZV?OimaEw~>Rb$m9t}*nV2jzf;REPF6n9-`Xo_g4_TZtf_NTdZM_^2(xdM?wcFA z8|9Y^08f6ly9;h$y+HQ7s1Zx1gk*S$u-mm(mGPg@jBB^a?_&Te5^Nb zplz35u*tdvdZS+8za1$F_O>jNOu|c~5keGsMn7gX^$j)Jn|Gult7bVb@y_}aZ5!$p zSm9aV&p~~(&E|q-i}7<{RPT#(gEIOFoy&hnTWAzNBeoYiWHd6Dqu1Fhy*w=B11U&d$DOfMwoVm%FC?JrUHOFZtiD(T)fVTFJk&=9h`KMCk1m~q8{uwjs`r&+pw$OliOcA>d53$@q$#PmxBB|s$trnP?aDvXGD|CC{wo;QXM0esWpr(Tf7uilfc&F0K$^mi_ zN0Vc##-SW?gq@`M#g<|na6xV4TSy0>b1m1J)8=d@X(S~;md9p9mA;-uqzJGO4`=bt(O~J|Q+i9YZqE_|Qd4 zw(oDV4(#5>(hae%uxgxO+t7FC7dk%hO&RaM%9$LtY2pSl((wS~OJY;k21jd|iLt+?FVA3;;?P4xpnvz^D!@`32QnQmLB z{7uW@O?bSS$?gK>ZPT$kUQ z-;{>rl`|QgOHHODjNfRsw>tF`88>o<+G}E$uMK@cm!U=&iG!780YU!u(AD4_f>x}d-55ZefL06GyjQ*lf40I~DXgvsc= zZ7iM#bC!7PC^?RO(-yMp;9@+1ckqX?W6)agJhdk4=wZ}b?`w@AM~p+x(&S{IpY{~{ zlm&DHus&*#?syvTPaD&z=D&C-;^xXgNj*3FOM&FqQg7K!?Ks8a7SDZaRlOb>fIH)V z)c-r+kHQUTQ9@ZA@vc${Hp82Q>Xx&-93*XSH>#m0@&Yr>qQTGZY$j81tH4&KnODcy z8^om@gW?afNIlfv+1Udx7Ai}-o;Xk9vplc)3LMPMECTmfJnN&JB@#PKt00-57yQYm$J>^yZRT4QfRU8HnyhW3mG z5_%_WqI;DZbUbWw^3o}?Lt)}S|My%-iwU@PSYF{CuZ_mwX3`vzqpk4#fh&5(vS_V^ zwTNTAlAp9gc};7`U%-*HFqOSq+z2PzU)siqs(u@Y4IPYlASootHr7R$tdGP6*m4%Z zs-V_Zx4AtqSRZenX>QTCSa+IB^}f#dsOs(pnn`A|5wHb3Yrm6~?C5L{QO?Bo@bqJ? zmBI3O65<}BRdEfmpEg{|D7KE&7mK0;m`Kkuu7V<8KnM9HRFP+!AI05lG^zy}qIh(k zR-#pKOW=Ii;p_!Sp{dRDXe<`*K90bSoQWt}tHu zKKnKY2lC>^G2d!b%^hQO0Ht6uqO$2z@5&ukR#9iCg-iyU-G|kGPz6H|Mhf{y9FI zvx{EE-pjcy;vW5v)$^y(Bi1D}#j!l~r+-9db*&n?s}{;sHLupi*Ff$>Vk0ZYUsWs1 zAC)lq3(zc&=ov8Eaic0|o!Q4Zm4`$-)N1*c`HyRl_zu?u_c3Fl@t>gtsyIT$3V|xd z7dx_za^$j21pd@u%7sDXqtC>-fly~U3j6Akdgk9oK{88v?W{-&;l4x^ABjiEaOojW z(~bw1kdpj&jEu%Z&e&ZlLz3V?+*JF6{?sFMhcd<+Yb?bUx)XH?EK_pxp`b2rB!r+M z`gpX5hP&*@3CSJL$zJ(H+G=YqzMS<`V&$#IG~R z=VN~`2OICJPg+I3kcP8FvVk~hWxEf0BWxXo9b`4x%9eRfTHstlren8?=xf)dgf~Vk z{S+TdK9FnVPbn`dX(lCnRK7?tw%_!Y)K*dbllUmxU=+?Xm2BHu5*;@$q zut3MG_xenAl5rdL5Do+$v)LBSA5!)+ed%)0DgJ1^gLxUP)b0t{c#`o|f5Fd44A;>c zjlQ#Qg2N!i%n|EWX7vX*@l}red44UD?TNYMZn-${T6C_zLeQ z8f2HBjJf5MNO>)fzqIQsE=Gyc7k#CJSt_q?b}|!%xhSuk2(|7D@wwv%+UFTbW?&QM zbF<~^X)0ep>Y!EHXxfDz<$;(->?t<_lfoigxp61pIp?D` z>1GLql$TaPQdos~^>|9=%hY&%0srPGA_m3&ybAq;CkZL2thaUgM0ugJqrVsSh~X?1 z(nLp~3g`klD_zq&$cN&$u&UY;I*hI7I4fH^#2$MGYB!DP&Zbir$%Pe!p6{1dffWnr|6 zU4~Z^`8YD(wj-DhcivO@1{DQ@-r=YZh8G<|&v4N5r)6kFtqnKCKUlW;Z(JMufBx-C zDDm49`Ej<<{)3*DS1#X#F57R)6JQ23KRerO24PNb&*UndmVzwn~0z z9Zu@P6lpU|cUMXq1#od{Cs1{5g#CcCD!C2m$X{W#Sc z$7&ealAzp#*D=W8@z=x*lCHhMe_LzGlfgGLkA0ypggl`9?Zf`Y@3D>br!n!Z(;}Vy z#bAO@J4Pl48>(r%Ihz;orZ&bsa0*S4j*!WoWVL=)h;WS0a-7wGe3o(xZ3FuDXEaDL z`2Cp9fd%%aG>g8nv_-r8`*5C&_4+_pG#v*X)rF=>2cmMo3V0=@7hjB5BAfjH-7CcU zPnGGAQCZE!52G}c}F&#AS^dE=K*aNS&`241Bf6^hA4tUHqn>o?5o zGH-D|oFi{h`o@Pz&E<_aGr*;HXtkrAQWl0lncp!KCW|MP9RWYnq~2^{Yy@fnjHAK4A-bd_>&M9N^g-?r z(uf}hFX98|C?(TAiER*n(C2a#UJn#=i4O+uq9L^dC(LQ+ZU(q=i-j$xNp9X-Y|BPV zE0kS&P1kKB2DGVP<*D8!#&$N^^+Y>F^XY%BxXx}V4m^~vL1T^bWTduOE{pGm&&ZpC zsMZTl0V2aGHi0aKOousis~v6u|0&NN&@&cw7Q~;~9zI(b&9C_?m=)D2+BuvXe^pYb zNAaUu_T_TA`*~n2+o&C&7s)RC0^bx5I!%9P%jAq!!n9TIXtWdptj-kUIqYjI0dr~` zq@SNAQ#L>m=Y2sSlKo9IXPPIH%pemqhh9)TjSeU|{uZRI zHcu^z=7Ga_wX=A}Q^84VXb;e7sHz7z!sQUBtkst$((&Mk>!WDwwqrx!t~?AXiK@o9 zn2o6u&@5oxY~%fmP;DsQ17!Dl*s7<|w=m`p8mo_5dNqPX?S1F?gIYobp@BJ6VD$)s{Y@LNn zg52~H+96IhFO!zMq25qgj})nkK1&XeQ_5=nIeZfr!ZxD{8Dh;bH=}dwpc6F?@lk0V zq{DE6-_r7vw*H0YDrv28Q6HO88Z?Nv#W-3NeMS4odU-3(gTKS}R6zChiRfK)RmTSO zK`N-_<4v>r`fk!@-d2u5_>%JlaM3P`n_@Re_x%BPb8?4%)%vi0dWK`USSw)*?Sf1v zw}_}_usZ{85?!L5gr_MnA*k}WM2XgaG zRjw;JNGc@x8RW+SPl*)TL+jW;6DcE{|Y7Xc6N5_q`r#gG-cc^8rHA`uC(IPZHR5>dQyu{mJ zG<|@Z;-1WHeSkg-_&_&CzqtWx!mV;XH@65u#+Z7qHSkq}CFP&;HSQw+8~u#ExQT1N_}$nW+bPtU+;k5i zt0V%hvMuWymZP#*q0&IWtE;J>6br3^5MEc_85@DBZV?ya(`FT>w48(6$Xhb=!xt>s z)XC=!HfEFP?#eAbzuA(h0Z#i1dtCl*p3Pa?S(m_QqS=o#70>zXu;2moNu5rrlJT*_ z$$8KzY!Vy?pR$B#Vy3|O32x`xAw0(4a~Fu%BZP2NjS?zT*J8UWqbl@ z2tI&L%-wc`B?lHDQBISoy?VhxOp|_&b!=|;pP;$YL-#02+A-yu3R$k;ZmU=D7#5W= z#t24J??msbWAQ21F*BSTv?9?W$7#7MDs6k*WA#n;a;7bsL{$77ZI_<73mbn1MzYk` zFs9v`2~U(;hPG+_=_}^qpDwEQFLoo@zdjr~Qa*xZWRda2o&ZjgS_sf}R1_PQ+s0n~ z4mw49)5DC8B#`%LqNhUkir@!Sg06Oa3O!?Y?@KF(Z09o7`58(6F2+-?nz1G26v=0_ z#pj6+Krd+)K`2L9DOEz0`IO-Lv2p|biPRE&b*=&T;YOx5 zOa+8?huyewR{FE=gs#2Jej=v*B=5dc>mUzcF9>{}2g@SL@ z?r5(46|`ie_z_lVl)wx68a7=%7MdmW^REU=Nlu^+EG~-dzoqIv_FZ%A!zc@&k7R=epe9}AV{TY5Xf zh0@!st)bRxKd_Z7MMwC?_zYQPateYLr$fvV%pUXU;-X!2>7Jb7qnsi;2y?A+#RNg zGnm6W$DCu;rkPS<{x4i3SUZ#>P09X|@yS(=c^&(ChG(o{y4jyv3vA*fUlTakypKLA zBdp%)AaY4KOAiMht1DxN8VB@$)xK~I>~HKt<TAZT{K;a!sqENvV_hcnqJ9B@RyBlir6!P zm(XUnlj~v=a?m_VQov}Y172yiA>GLqNwC&pFI;ELk_QMsVjroIWGTqwSF~JVv0cx< zE`@^AsHTC;K}`R+4K4#8e3OE;=qCL~sEYhM%26WlM{6Kcv7HW`sL&2vmO6=#jsNiG zn6t(Rx(d`6F0ak8c8EJ!KBb0TFjsSsr+QP6;~7XASkKf+xV}}864H(37db&W^pK0p zd?9^!-&<7Yd~F30_g3$eQ^4ps;K=v{RK z_!E_epDQZvFuw#NrME$Yftv@>SvX04&rQzUtIg7Mwtcg}0sX02-zsO-#IejrRnl0g zHN~aOQ_Ma7%~~56=Sr{wo>=R)P>X8>erVOR=Vdn~2Akc77`34T%&`5=WO5D~B!Cvc zdZ16VTe*b?qZ;6ff|alQNOi2Er7_QN2YV>(P)x-<)^onFF@#;I1{USpM%{oDT^)_r zW^28z-4T42e<%mYBU97l_}*p(!=Ob#j}0flCgY3J2QG-u0|8froEEx6K`O1CrX7qG zY;!h_l+XIEzIV2Y{l&<_6|y=7J@NPWq0q252s_0_{6`XjW0NkCc;zA(D9kXo(F5>< zV*wuUzjG3mW>s4<(=vsjB-lggMQR1SU;^z-iW}qY@oavZAbe)0{_l)c-Am|fRf6rI zMhu}hPR;(CtU=D~vuL}2c=ix9(j#bn#LL-vWT$bKk$bMeIY!-#Loi+6NfJX7kcKCt z*KioC_gBcH<@)R#tH`t*@6>%M?W7wf=Pb&2{sq}jFz^JpPz?MV?)3hpLunAoU$Cb6 z!3rZyGdYr9kC66z+kqa^EU@01W`C9T(ZtLaY8~?y8jEM}OHhg6FUnvLY0pz#__MD$ zn$KJ_i^yrK5#z~D*Ji61+2$u%c_VHh1>qK$gnF5Ez;RjzT~^Y}Yh*IpgtnpEB3_y^ zgImOVWUU#`G}I$qPf0a>EKunR7OQ+n$gxCYIm{40;M(}J`(A90{iow2d}_6jqPf1Q zr6ji9l{~Z~h5SfbkeqZL@EpqAueQk^V{Zo2eIG+NxwY!A=oluh`lv`sS&(AI!P5L) zj9u*=k;r3gkU!e5Xm^sVe^W<`X)FY8irrWlt8AC5!*tfSe}d0QIO;=_)R9RR+g~0O zPl$_2B|I1pRX2(4VQZ%M$dwO$YUT1EiV2OBO4oy|av!tRC89 zhOr&N43-2_2y|3fb_C05Y^Ur~Upt2&r#Uj?TWUYp2DJbO6er3f^ykmPtDrf}ggvC0 zhG;Lc;z(sS^Gg(Yb?reO~kpV84FO@A$}qM;)`}(yRf*Gl$1}$^fF>% zI>E)@8Tq1A$S%!q!eutTRg>`rxl<<7;7K9W$ln5A)1b$qKq~Ay4jRB5S6_OWO~}ef z(aM295!94*Ui+Cd{{uM262e;1&yJsZ5ymCJh~q}EvsyI{ns@o~jHOr6jwdrgs{Aa~ z<cL{z*DL_!JK`*QE4!-?yTz z-kH1=?W2s3LUC%CtRRUk>pCpTBZGjnOKTo2SGSFi|VErLI< z`1w`j_xP5|hWO8!JI&|Vz>Ozm>_ykXRPw>-=3Xt#61uSdES=Pp$7R}J39ZYe zsuSX>`q~=b!EiVWjbW*#Gf;E+8DkAJi6{hQT$=UMZrBfbzzaEA=xeutaq7aTFSv?) z&w5*NtWnDBM9z>M?i}=9E(At7{sWg`wDE#AQo9>B$Q}JxxtH@14w65Nedaj%L7WCA zplkS!f2{oq4HLZ4#mZM>W@JpF{Skcea}Y~ygCVz55Uw%&_9=mhZr zE})$B9)=N~Yf3ZwlJx}ryYZbIn>~NyL~FcalF6P_bCh%g<)`g%G5Hcc!}1-8{1Ff1 zS8>yD`E|vl1T!=AMQTA#(iAe7%yF5%L%24pnOm@3-lb4$ZzOyzPO)03i-g^FUm4Rf z88_rZWRNx6b>4ppODHbxjns%ur~Gjtuuy3Q7(v}uX%W&EHKzrI&vq52hpd8jW`0%k zf~t}0;Y)co+)Of*>Y@J}i)5cPkM*v%nZNxtn88%~nIyt-*qi05x^Z+wIJEW3>3-uG z*>5GFb*dM?g%5-qNDwUbm=w9eH96?vp z&0>btANR<R{$mby8#Bzrd_D-H5?Gy9PmF_Ss!UqG$|nr2)x z+eZ!uLAn?pvV!1?RR*5~v-vOPYB-y(Lg#p=GK%KLlwPzbtjyIx%%@F$`2Mx>GJnKc zl@+%1clRmza^Q7ffwdA)`i}&d1|rq|3uLOzeTC6V^Sw2|dZBLurODQe6H0p_$=iX9 zi(3I+s}$bij_FTb8NtqQs@OR0uKW>pmBvVG&0lF$Xl5wJKNB8BtsJ@JpnS~#ixg*U z5o?3?{wC%a9AnLp#^bJ;ar7qmNdE9n4xWyy@rOs7GV#=o@sC0DK_DE)h>^3+Yb#M%n?n#wACfE$Bk@#RO@*EY#7u0#$ zdDLO;b^H^&v9ocPxWZ^8sVotLrNF?O^rw1HS_BgPiE55H%>4qrLg&a_+0w7TTGFGK z0#c{o7T63u*Q(oMaq0-kG4NsZH zdlpDg03U%$r~h3T;+X@M|GD*oStCyrR0`bmJaAuP|=2TGi1_O%7J0 zjX@b{x4g;N!FZyVjPrIl7JP5amf9X^u9Tu|X&D=1e{m(IE4S%Bb?vFWzy16!T?HGq?KflV8p8&Hj+caFXkD>5D7g)f{u}@~M(ik11 zs{B!|g8MRGV@GKb<CX!LsCHz7k=DQlqpp&hw0)#fX9QcI>d0wv0 zdZ{{??e6WyBEZGTPf`I!@XU=NP6Jo)X;Pg(BiUeLRdb5lv zp#t8$_=4$Uuh<}P!m4CXSC+DE2?zeSfpy>&Qb9J6Spu>YN$!m<;+f=#e2Z}xUV!r; zirs^K)B~()E5aQW2wsQ7W6m?>+(EJ+u#9xD&MIxqG(uDRIdej-uHaeSx&s)-U%qP3nSm0bwF@^ntR0 zbkS(w@;nsg)`D9|P3q?};W48Q>LNdMWtekld9;OXhzFb1 zL!H0^?kTDX%isxy23JX9R&(h9XPS8fLufx%Ll!`ZS?f%y_obOyIwRO=YiAn0y(7pi z^v&f|LR@EKKXXGgC&yDaY4fp792w>0<@#;9UYM=^tLSD~*Q%JmA)}rsQ{V|l1JePD zvmEIOWI4+7e_#K11^({}{C|4|PJtq!kI;iA@=@YrX^p(jI!5w@&RaLh@a%aw%f0}+ z(H`i!`h#1Aiw5@ur^7k68sA5&1GB8%^qu(_jD#Mz0#24k+W}HBzD0C*@)u}KTZqYW z3(^l!b1U;)|C96_Z&Sl@DZWBzDqRjoko8$Kb`G5q-8OK?hDx8{O3QD|Hz&r%xjE3+ z80RJCPq;R7meJl^WV8Z<Y8d~VykLq;oqPhLXmK!8Iu8qFeFMw5MB`A} zc2qJmiT3Abh@b7>a8%SHxhNz2`p_7=fZ)a^TBuB9IbyB(f2;~jD?HU2!>ZrK(ln_Q zUe2$xH_23=g>UO=Mjhue#-*D?uS-AGD@ z^RBKpBBhXVPkVzefHlFvc4^}%nk%hUR)+SY8s;Fz^9`3GVQJdeA0>S@T3PqCX~q!s zAK?|gVBd6k$=%?4wY=Jx?hc)SK{6D2@D%eg(^?h_zBe8+0%bk=+DcbS1b2~qmLE^D z7g&)XTYVQgkaPih%sFT)pJRS9M=_7%6?(x~4W<~?n7d7J$En-pqi}VoS?E_>l29DM zs_@64f;_^GH@XSW$#Cr+{0sg;CgI$859fhXJj;Z)jBC5q9v_DlLm3CaW6LGK1wRvWv$ghnzBCF)6s23jFJM+`2JCy#@qC7 z_?t8umsRfips+hgHyl;c7?))ziUO19cZ+4%W;f&BA)iz>CYr8MCo|STf6o!mT}I={ z5yFrZ(wx63hWe1Xqhs)3Z7uWqEZ}vS8iS+Wg(ji1AV4Odh|DkG5!t79wyRt1r4OL8 zoe=UHy{yaVt{IL>(fO#H{UMYZlWYA_&J7F8r_s60uJSqNV5}nhPzp1ipJ58i98@OA zgTWapT!tW&>0k0VrXx!5SHdN{Aa_X&4CwLR=KR!k{^I_0VljB1kFaqcW?ec znmt#^BTv@vf}Wb64z-t>8@PI?eZ@WareB2BG=HcQBZSs1FL9q(s?B3#HT6Irh=M1X z>#=>HSg17Vi?^DC!KIWxU=pcDYKLmT%iuk1nHH3m!`A7#(UPtszI7K#xPO^c0=m3m zW-j@G8J;BTHkA!U;GU~x>N^LgQ=0FVxL-LP@3M~W(p^S~}uXYOA<|6-J;Ep+oc9!SS0bncZ6iX^HOGSUCO{Z7!E5(D( zray-BX=Atu+Wf`f1;&kCh9bcNv$=}7z2u?z7?e?+W{CNXeJDmvw3qNh!y;*YdeZJ> zY*z20$9x4d8%AavR-Xx_eFK5px7qAVyOJR>AHh(36YeB2@Hor^Rx52{8p`P|8UD(@ z7O;9F&?=%W^($>)1m30`cli`*wJhHt*c?{0_65fVZ^~hGyHFMWXH9Va0mGy+uFp~? zSU?wsOpb@Y2*t(CrsnEFQqg}xb#ogYU}fQC_f&f($X^knM|L}SOB#upx{xZ&?Ryk< zga?EHpr)}6?MDH~D}&W$7&)h+%fXHrCs@BTh18AzODWHG`f=*F*fV5~nkR5EP{wJ1 zu`C&OoUxwX06&B`u(i^ZTmr9!x~Pd+RKLqqKSSgxxI5*=cy|KIjaPY zNNZ(ICI7|m;HENK@L=M0`(Kikq66+!rV79o63SnDG>4$h%fI%9?0=w5zDstj9`o zuvkBw_skXiS$ml{CE|tjiaNyRXc&LpDCncincK=$4DL7gBAcY?le0Uc zL8$|Ag!CxuvprgQg<0l1y+e_;ndx-juvJs-3}?z5;wK&+N>wZnT~=ygrH~ZgAX%|1 z(n!2q4N66QF`%5;N>7z~D=%d#gh%I+8n};L57ki*hIY|hu8cATRbYd=v*0CjmsTL1 zP^$LRxfDd8LRzd(bPoa>qWb$QF;~Ffb~{i_t#6cPSL{9c9lI*?`0gtc~Kp>SUv%o8lusM#>#x=W` z-6n1uX$XBN(lX&xONPhfA8IPqxMlo7Y8wE}H4{V48OvghT#@Nx&M5=QU7DcYro9|C zUX;Ei#C(myH1}OsFZ9kQqx~$o{egN$t;+pSzG~kYrz6PN0&f`Yp#!}cbrBVm8)GEK z8GiRpnnYFHQG1N4vNVfU#sCmNKe=$}JE#Z8;2muDlq3HJ@1W1t8=Qd-OPR_Ma76H- z#bmNxoJ+(DPzzyAnTPZlqr5h^uA!c2c(6{zFi#zpE>WT2328PQ>F?y}E$~!biH&j^(dUi1?7!25>6wnm;-&D>2dUtvy9c;xImduPY7Mt zmHKu$8q0DvmOCTuHn^sFRqVli#~(pHuo%>^yOApRiunfB^7kRb<4(#sj@3dTqdk}4 z?j^mEnu37R$&M7xqAQSqW5Eq>%j_Wav0IZp@(X*o@Ih{4T~fnnLu;&DR&UJd{B=4X z-i~O3=E>j0;_7Q@Gs4U}djQn*-=hPvb$~qEqW91yAj!Ndp3JU^Dv>Z&!KL~w8uTWH zvT-i{DaE4y(syv57khL%Fcg49!5`7m@nl8 zIL1;}FUW>mQu)*QT3N$LN+r-?qZVDx@;n6e+D;cQ3aPLYXlqZUEAWM2U414?LkjS} zp`q3UBStFj*9Wd2|NAuh!3DyS@w z7SmtkF{W)Sh8@Xe?IjrQ>XbPPZ#8~!j7EeSv(%#c0=6b9rHo3RSxMvgF`=8JYxZH5 zIMLt8OO}|)FeqJ7+Nlz5P0y1x>Il$XIb}7m%Z56l^;}c`0N93X1Beu0Q_DHr5#OQa4UX-E-<=CN!nU%D;0E{kan;+8W(w)9%H(!%Cs{7 zi0!<4o0Hv%_^LHUtsFCrKTme(_d`ADuS}8H9QLG50}@NaS@q`&k@nT#Z%ZbA@dhK(q80w8~-;~Ap4X$&~At)^RM7t zR7$nJxLJ+HvhY=p@K@p!iQPQre36Fdlq zcyy9sHqNZO?hmF*d|*1Hfl4MG3#*yg^q@RB?KB-2tYaMFW+2lT9~w_aGs0mLhlncU zm)1L=(HsyR2bBl3hKAB=S|c3;NJI2oDj2hn`Eb9xmRmKH1z;4@4@<1NjA1_3g=D#Y z*hm8Zgf7wb>I{EX+7KPfYy|!y8dxBuk-ssC3s)*xQ0G@+9 zdU1ZH$e|y>`$}F*aLf$Wu@8|F;ygOgY(U?DLFPky4VEJ((Q0TJ?yt_XdI@`w?Gnn) zWlrV^YGrjgn90A038G=E$}pbj9Z+2KcuK=GbE7d@&c#RMG4!Wg8;$hs^VdVqxpAIE z=`^F1y2-TEfAxd5W}Qy&0InMOKqrd_3A_oz9*htUq8-i#@V$5}XkUbT& zWcsT~p{{U~m?!1|c>qP>m^PU}$8PHo`5{HXF3vL6To5JI;qr*j@kbIaJU5S#dF++K z7~^j_ca4@;;Pq`MISa~@#tze-Zxpl}NcmtgzDNC1EjuT=r?lU! zrZympuo+w$TZxu+?8Xxut?^+v4*$0?KZ4R;w8gk$Zj`^+RRZUX9gMD&X5Q9kgo-PX zC|Vd4>f-7xZSm%JKNe0T*ThZMUJVztm2xk^pvn9L{V?cDW|4==JbRR}jLtGzLYD5K zO*L|)mDWRgl)DK-Y2~a(XsrD^e#$%$HB1ZS2XW{zE+i%x&+uBZ441%tP$h0hpeW^G zBfMD}ZTrY<@e^!IDwQ1I_JQV1jdO+!4~-L!9cLz3&_y$(P$@WiqC1#{}*oP>!#kdUN|b5GSeGI zNjuS6Y!U%)_w9656&m7>aEulnXk>RJPjEG)qbukZx{JOuNq{Qmyrp!8*EAb0dQ}1Zmb}g$h^DjJ>ih_~GOEk%Ul2O{*(tgSp-+8-| z86ho}Z__VAl#z#JJ0t{5(~ZA^Y06vsJ9F70@&VUjQ`!h5F<;QnTlbai-H_TaPrerAG{kj^%UU<{RlE67!&tF_$R>bPYN$GV)Cza3mm zBlJJ{5Xpz$Mt_O9C~7A$3k`H}o~yx?%I_@wXO=HF@J>)Ne%iTos?x}aPb~`u z>nQkI8E?IGmQT$i#Nno{r{-yNJ;tF#fHbj*IT3coBbYn4H#y2hds|6SFwvR-B|2Yd zjdsR8Ajg6WXqfnfoIp>+x{+NnF8Zgkx-iW54#&$&^%6=w>qdMod9O?*i#C>nBCbVw z*H?3bsiu~JI|VClytr9A8yt@KtW_jR8G>{2_-o~budzD+6liB>| ztl5pLX}%(*qv)@ohSx}jit7Ed4T52D;68^nHAF@jx#Hbau^%L2hJso0B>*@3$XzKmfoS4ny zT}lno#%v)oE}6U{zwoBN+mGdX12s6=$!dtpL_RwLn=M}qRRX0MEqR3bh@O_WhQd)$3c;QD9Aj8# zsB@)od1ulXP?>&%-nRaK(w!ZWO8Y*MknxXQ@y zepbN;gD2#b89|}HQI(V<3CvgDAIV}JUV+KV(WC}+1n(q^jUwQoaNPCWY|SV#gO%Q3 z1Bj1zhRO%7F)eRh^PtvSUZ%_r{4Fgn^B0?Zma->@?zr!BGvq5N{b=*d`=l0|7oGv5 z#OkEi`U&V9O~f1Y`Ds2_QMqQ;)fh6Vp&ngGwWNaqva?$yUH$cWzlzc1pw!Zw zo%KY_lhGXn$v?hk@T9bxMk^}*9$E<3GJVJR*uKesO4mVA^^MV2xyUs$s*_zpK2|rE zR-(aOz^aD805cn;;uqF6EtZ}{6`1m82i@)1PM*ng(LH*Y9*fy*oi8^+smwGxMT9K) z3KSsCrI*}3);&$4KTMiF-t0v_hQgRnWvtnNxprdlbTSt@JWtuSX9{=*Mxpz04^V;V z!>ULp=u7GCz(G2J4pJ9_J4QSi&YtZ*gUbP?KaC#vS9zVXU2d*Tv$|Wu)Hfs!ap`sC zSpPk!a`*X}N=9&&)L8Ar^i3rxm$cJSip|s#&3F*bl_zy@e*Y{yR6f91;BNGl%}IHB z9=p9mvpBK1`3;s2C0G&0-z2}ZEM6u-Wsy7>X2auFE%+6j(1)uz+)OpfZfZ5NPNQX} zgT$izX6@kH&r%Fh z5KiM~CEAlS@>bN$U&!o^QpI;HBR1Mz?;*y7brsc7I)ad(WY@th zL$O*q9VMNS*U{(l6ZNZofjLFTf^-s3o5Mx{8NHB?1{PtqIVANmmyl7)=inct7h~Sl zqqD(IbUOSKzM_2+ep6495==*3H;`*QN}B;1;%uX77F41QmMxSfn_Yt;wUp__`IWNq zwO}=|likl+%xGIx;iZ_6aTV?gY`0di-(LYoNfFE?aRsxDK6Zyu1GJL?&?1689E7@=wZooP^R>!aUM2u7Zg0kTjL!+)U1|$3DkzY(8xeL zFjH?qH|wM6QW`^73eSSwQ<~t;IA~09)k9usI~w$7bc-B7i*}+;}dpJ`2+lJ#8^c$cgOaGy-Z}bqxV9;p)0rqkV$h#2oA@U0=LwM zjLI5l+Ys2Fhf&A)CF?d{v@iKRRFbp1XDS zdOLzUA73@HIOD^V5B2a~!I$W%z+!JnFpo&VLS!n|OJSHJ7IJdpGExDQR%TNl`X$ud zEUoHRb#tLqMf!oGGmn#8@OP{&#R%8yjrJyi6x?F<+{7nVm5W z7yPdY*KP8E z;#^?|UCZLJI%=)dc@=BYJNkZiLy${t(2Ktf`y-9!cb8X3$=w)7Xp(`Mr|qyYO-qx9 zk_~ud-1_*!tk1g&4F5m&5xXlM?>@|!S|!O@wHZlAZDAt52I}}?xNEQna}6Hi)=GPV zcXg+FojL1o*^{(Fc8{2bzH7`&noNVC zBT{=c3%&<`u#ROmUTz57!Qdr0Q)w61(pnyOnYm&QlAqwX^@l47W0W3BRQsDnaAj+k znoB0JYwCtxeFW;Beu}Cr?M{a6&5Pb{ZohAd_m&W5gu@r|d;M>{K0XcdI47>H82lYn z1Z(W?N?osPT_CMl=0%L7KOX2F2adUa=YQfw&N}jfl-!sUkZLV7)0JATcAkgAiR?~f zG}Z~L58Y?uR|F@(zsW9&#kwoEN$sT`*!KIiCZxB6x0iy+!O0}r=qSF&wOz?%I6Y#o zmEvGKw8@MLM%XW`BKVZ`ADjbblM48NHPv5X<2SMdmf#oZIp#~Xy>eM<0+MM69@d8Y zI|jzsTX7#fNgQTu_g?^cQ8fT{WX*)V=u3Q5YMzp!+=s~sf<4+*@CI&>BI$d)9d;&l zBkzzbI7?T{KErpcQ@)}!uhEzM78szYEETDs@iEwxc3|4&AiW{nVO8iV@EDbaTLY`% z9k?1#vqy2$D%KHpDWk|v5+PK?!Ssf%;__W@Ba}zWsSIz7< zcBWAbc4Hh49j?;vnz6Jm>TKUr50UAe%fMrdtuxXoS28c#>sneH)mE4afBp-Z4$T1_$8l^W{G{6Yo6 zCH1;`0?(J`1Qq9bQYg@jRTQEZNT=;P5qG%Wsx0Y7O)1kHiYL0pI=k49u+xeMZH3?D ziO?0G@Yq_1v;nkYWXcTebKgf3t@4?5{W+n0v|H9O@-Fm>JAoFdVuhW`dpU{uNcxy2 zUz&FCcM*n=`+O~!9q@xcr5}O6jP9@))udREN4*S2!2TWyE@u{6P!gf8c zR2CLzB#wHjw_dO)%Gu1cfZ~naQkPPPbuJW?|(|&FzSraqq>VLqW_5wC_wYArv4VqEDRp>(Kt@Jwm72`=}l1a{8qyXuq zcU6DFdj3nsCvhZtBD7~IUl-FHI0??;-f9Q1=$Or|2wev;eErOXP%-ijXW?aps1Ge( zGw3uvL-`q4PmvH5_tK?im#kZn4S5PT;^Co!*`N5s{1#jCT+6n^3`WK{5-KXrU367? z5GdmdL!(G(G(IJ1X}G;!&z6caqKl=!#6Qg5IL4e{PX{+qb+9KwMZb|xR<4yCnj4zK zPWTYjFm&Ey%8UJIisK?m_Jy$;x{UW1p#|B=TrOo)iu-ZUn1Y{^HZW4`XD$n!k`k1= zW`m$yVG?Qv|ADip&pkvcCQQXu;=D|WokJTk%3J_9jTxf8Bt6yq#yY4F7dj9)<^P55 zK)1n<;7Sk)<}h#0ZToVd96hX-$Xv^en;C4w>oS($U1TWJ>}jyMWC&iv$8cEvNNZdqq=} zxk^`GP!D6f*VJ&JWD&^xn#Fouqvob7-0hY#;ttz37{Z zMwn-LCoE!*CWG;R>M?n|bXhM9N7)6neo}|vd3+atjy#HLN?xNOx1YwMrO=JaW{sD> zng?l`m>4L5V(}Gxk1Vjh+IL9Db<~Z*NPt%g(ZG0)?nZp7TDssLB=pH_d zTE~wPB=9?~ZXMve!eSPc?zua1JJ2N5hO3T~Q4rKu2b6v&ObuxGKl^mi;>H8C8!V*d zymQQwDYjLfX&OJvlh}M?16nK30j0g&X*^Zkb4WvZVPqe;&(VP~umyN5Ge3TA%mJaa`t4Zgf)1du?nmvRR zwnauN2ZfHTFvwv$hK7z9ewKYgE`j68DAbB$mtEo1rm ztBhK@1tsG&^xo`7Kfq?7fmt5^5|tr!jdR8~_O8^C*K38cph;q@TWihy_V9a;`8j_oG}XakZfIB0v;`xlQ{0%NF# zx?~)}Z@FhSK&$LUsFpg+I-xzW<3m;PV5=)Rh&H1!tWWAF?dLxF@)>*3D!Kw-t~j0x zmzA!d&BomX8-61fh{cxx$K6ljGEz#A@#$U3M8`Mz5FEuGFbnWyDoPz*IP&6Dss*qE#1Z2VmB(wwP-p%J3Sy!!u)F0PzwoVUN+w` zx`9*Kjt2;f_(kTNP%U~?GK~mza_H#VVd!t=Io^V{kbc&hU`z27ti&i4AB~OT!r(%A z0%$-l;j+eKyx-BxIF2~scji-_sFqS6m>TS7FAH=>0zSdLvc4ECu&z%Dg{k+Y9@K*+ z(4A#jE(bf*VsLU`nf$BdbaW<>3yZ+pf#S3?-eZU9HMCpCNV>}}_@VT-xRwzvTdSO0 zAi6AQp}5j_;j6|VbX_W5{t$NPJ78^m4L1v|hDGozV9;~y6H^)$W`5#d!3g{@)P|Pl zj@kWUO3CeGrkLZ5X0~Sdh02UwHcA;py7^bJ?6nZNYp)Qpf*0`OQ4dTK(68vW+8iFx`>;{KjR;I+lEGf zfAt@(w@M`=$Gy8qZRu>lmS4JTe2w+h;Y_c2KB+gDZ&%5BFLmOslSa(d=0RoBepTD! zQQ}E^f%K2ECpc3x$#d(1P{&H7FX%+{l8F3j^CdqIRFS=EEqc*ftX|}P2SC2k&f$C3ZlRSqk=#SmnD;D)DY1rI9_F;Z0w?;8vg`T^{}L1t z+jFptwRI#(KInVEQs4ThXXPa53Ux5ADLeCyXP(`LS`KUxbJG@-iExp01pO(P@;1B| z%jP?+ef)NJesv9F61`V{<*TY)N+nta*%WgTEX-r+8B?MZ=O>EqB#YbXYH3&C#^UL6 zZ7Yr`aW?To(mz`>6*s#dzu{__x}MTOaxtqRXy>gJIts3k-geiJVMe$g(DsaP)Kr>L z;iLHvEJ!xUZMm!3Y*;Bj=v_?fg=?*#|DpP?hZE<$zYm}Yu6G|$*>4nVP#x0_<0v`)L0 z{U69O3>a^{$D{n$Sd}~kU*Z1+6Qvp?ps@TF-EXHk1NL#XvNF=!USC0e@cL9d<3KxYope@O!4xmAXkBG7e$IC3D{y7j zIsKIDY4yQbtq>h=|BV|f3(Z(mAnPsv5B|$h)mIsg#rxE%@*2b{4A{jQf#zk*5JQ5O zwr167B$VuY@S1-T%`qRrs@znR%zqAdJ4?|)=(u%|Ov9rXWoLEiSLmgg%1nyeq+Z z*p&=(^`kqiEilbW2^?qqwHP>2yiUf$JBE)`H^bC^W(Vg4_(*6@p34VV>epcK2Ju=C z;LV$cubRD3eXyFEnC;4ou{NpLW9;t|#YbXI#8IACqlo|wl%?tzEQv#?M}WOJGe=3hE8$H7wc zvNm0OEG)%^bh`>^Fl~xzqVHM9S>Dk~@d*Hd7249A{q}IT2en}7D$!n_+Da7oi+t_C zd{R;Byo+<@LV_CYMJA&dD(LYO|yxdyp^cl);cES(P za_J6V)KkZ3#nc)NWQX|;HziKvz0sD_;Q;luwu566F7QT(XIte%&L&2nVpj zcla?`X?(;T9WNN2cMF@27N$}7Ci^TIBuOcPn(D_YX5(MrA3*p_>Lu6Z3RYvY2q~=( zcQ34T8PqbuXjA)smhTEsMZL4S*&6KGK@PL|2@tDcQU5^8kbk+oS+j)lv1{cgv@O#< z+}Bru_u^pi1IKEEk2f39m4QY#&BRDUH&EQi13E3?eX|(@?n} z>McDb7qUh9JwC^>fcn6_nR&Rh-kWRYiUpzE3tnU-ga~=L`;F@{H$qP|yW(5qmw6FP z0I$Ujk}3VLxRjCs6(tI8DN$avp&>LG3n@2H2Nyvj7}t7^@DIz!NC7vq)8IJJ636kG ztlRtvi-2ON3pgguC#H2)+(QeJIHqr?hA)vPT7tPpz9q~+`9MGOrP147oo+VE!BdQF zz(>}mB^;+n4!SBk#Y}QiDG6oqZ)ZO%FMAt0&-AKg%yW4mu&vc7uc1^SFGPt$*jObk z#0&5}qYHCuZNLX$YpHugJM(MSYMZqyt=uCmLr3U~ z?WvY)jK?nxzp}+TCg|>6D9b$_xA7b_Ti{8ly`qHDxH8Lva9!VLHiy`JZz z13*yB2CzrGPV0d)#7LbZGyaL(6Ygf!RTzjT#icgtF0nrTDh&`y1}a2u&bf~EsiUyL zHovRDNX3xrlSjm3TO62e^x)%Iio^Hx(ZU=d(~}ptW&LAR5LbbQ+#_R#Zz5Bq#)1^m zkJhs;xqoR#X`SF9>V9;M{>gR}tv3{)mDR8EGJH)t2I@=e@Oy5NQW+Ew62NAuC4Pbr z${W=$%qRDsu-$drbrzJAdg{B8T^K3$hTkd*%uSY(bBSe8h0;gXT3;#rJ9s;7w0=ObxMeCdM!H%p4 zMgpsu9>SmSo-qq4a3FV~n9V!}A@lRUQ@7DlW~5S(9))W`DW}~$Y&`;>SnBFsb%i(& z6!7kqIH^3?4x5l;0;TWtBKj`$E^w8db~bCzhJ((=ToV2&jNW)p_Rvxp%WQuCT%%3K zGYTXZY!zIctBxuP2f6=@UW}!1pFY#qOKG?Se&g&>{v7%O51=?Xzp#@$!;CiMnCiOa z8t!ZmxQsi~0#Xfi9pcDvl!F?GQSg84>2uI-`=pODQ{gmwPxx7=8pt9)q%WYYSQUPa zk~6!T3DRU&ycuwn1aWFJwOtO$++uu$;l_Bk75GzrDhwgRg)A}xtj8PhCfeV3JmZZN zf+wej%JVWxdGF!b(hyQdZ^T%CX`ukl_rB0h@>%E_BjXGNj~U_YwwR8_Ga~#}_}gAr zSwsE<4ym!R3^w(ykO*3@j6GJ}x+Q0^6LEx(N6bFF{%o|xR zkf^Y&k;|i3L&fx~a#f)mc;=4NxlH^H}HJ2A=~v z7?m-D#^~K*8^d#oD>jn-{kvfo+E@Ar&j?T9GM44W?mPIx-BED*=87{IEA4{SK$}Z1 zN*3LX+-Q^ngf*rdIZchCKLSliYFHiMBUiK;AiNH zW-$lI8~R@Ai`JIz(7Q31RSt1%K-J!9k3T8L(MVn(=CLjRoAB2bI?SPC}tSpQa;!p zhotQZKT2l`(ZUYlER}O6lOWwsIF)CWcT;_a>!!Sb_70s!lkjM^qZ*U?PM%>jWNCqQ zr9RRF#-JnoL-$8r^Nl9mO}F!_lpRQuJjCI7q=$%Iq8SZ^tVrK1)g~RSy(Gx$i?5># zxD>a6rMuLDW972KeQ-f8q{nOL*(B&6MnZXpfV7Xzl5XiYj3umkPt>MFPm-6@`~RcH zq1Ad2jS}ug?PGiYE^;rfYuG7Qeye5RKlO*)6?7Mqt)c1%Uo3v2OvQi{mVMd?Jc+P8 znaJn3wKG({DGcC-Df>w=+E*OsSq;jg?3h8)e+8S!ZmE|UK!SJgz2Ngx}R#)&5-<2cq zkmzw%C36C5pM$wd>^+>rCNv8JOwR*dyoEQ1KV_4}`tJJ?W!yi7tarE>qzdaP+WNikdiR{%@Bjr3&p z>A!#*IES3$3ejDlGT6t6I@!pP_0c6NTWML~R$9gs(}VCE{uU@;j19}*mULa03wd9%dDc_b%P_JREg~r7HB2JY z%`)yUSy#>PQD)Y1utVQ1LpC4(#I(M*@ObulH__3)?Jmlkd>1nc$L`8Ih07}oq(OoH zWC3`ZwHQv4L%^7I?Suj7KQ4;>Rh7sYxo}v#Fc)?qe($AKEI%jms4OS9uV z&Nu>UYMbGHVHH1}ZUrlh8~PNc>pcP9>W%dV#s+^ax&#yw_WG8X%mc!-;!frkDZo7J zJ^YoUCQ9uPHNP2EXfM5(Siz`{m%-z~iDD-@4u!dHsa2Q;ri7dos6_-AXAedt)oIdD z7;4L*w{Vu_b)8~7(JT65kf0WI|78`8dd_tY>xpKgu`Vw1DyoDknYD2k^jW)7}Fq__9PRS8+^YwmD_5L5@ykEU^cwt?Q3Ffm~zHuAb8BIDYph<34$&Wjer)eIP8uqG%5s5kAd2L7B`fQRNcNzOv@4s6*HYGj zw{#2Ggr3k%D9G~opKHDKpbwW!M3(L? zN$3GwLsP9HsHL#jdjk2?X>gzn;1yUwJ#9V|FZvWcT%H6b>676M`5vikO=A0qO5_7K zKlXHBBRL*OkiST`HNSX7*U?z3yoSgEaTfd!^b@k#lZu4>?mhfva9lhBuh9kW?q*eK zF*s^GCATvLcWLxPI%rKa5@B_;)N#Q24>Y9d5`?+tW3VN#9f#)jLDS?ObQfK~d@Bp| zqVP{*N?W7?YF`>=d6?lqo>UyQ8c>o$Z7s(jRaO zcxy}oZ;X>{vakfdz$3-wuGO@W5G!o5dC>vP(oOkWfkbqCbvf~p(VUT@Znz7xoaiXN zk5LUg)Pph`h%Ze$>LYtp2+t^Puz))wO-D1}HY*(p>eKAk{AOsOQsR)mR#1}V zUFM6em)8K?_g!$$wTh^-Y^AxTN{@{TrONU?bvZl=M}UI#Sk_18X-U-E8t2hJxB=D- zPu6sZtd?2d$}MG8SyjCcw+)4}J$)w20{v5kAmXmc&TuP|Rpp3tb1M5CF3lKQph!rh0qe(dwSG$k;E+ zPTkO6&nBu7jJZJ2CdEJ_?ndBjNnwbme*2BXI(aS zyUT+^s5puMg+MKF6nK?WNbKMYMR&vmUrk}U@JV<>nxc`kFxUdy!MEVGQAp0z^H4q3 zzmgJ6nHqV47TI6*08pX0iMn#*x~L2?Q)zdt_2 zvUMk+I7XQ*S8!Ku5IpR73;$!gF{dvS&5>_0XLLh!I`5c}&n@Yl;7qfbBMSMjK&p>wkQMcb)VA)>19A0xw{qWc8imuSr(yOj_$N7 zk-qR3{KN%C%=J3`%^f4;a?*Bd0Lm{vq(7OTp{VZ=+JGhw9-@v&qm_nijG18^BejQCw-b%}?S+dI3Fi>>$swLrkBvTK*yL7gA|alAhNyuPWQ3 z*Uz(PEorh^&eh-i%)d073hT`>*cATIc%x{b0vQ(QF8I+Snj)7+i)7^6o*RjR$ocRw z+Dvd69yKqJ6YTD-0UiizSvGG;P!(LTcIUkF{DY5LRotWb@@TN6f==2@_&2U1Zt#7K zoRB*T{4K;9-L3C%2$}#I36G!`=bD+It^O8FmAZPKTa#%5*CVE(7^|;}%v@DwA{Ov$KYY;=zZT!5Fo1%c*L5ta(^W623;ignPX#WsuFEBz7J%cIx> zs5#!Grs3209=s@Dbn)adBpD1e3+%8xOQ@}5&m%=t(-JnbY! zVVP^-1-}_JcUMELat4rQy{N*P2o0{0@n1|jON6p~?p+(-Zp2smaL(hucm9?~| z-K1@#_S`&N6OUjl6h^iQZjbJ0GHQ#8!U<~ozyad*_oh5!@VmuIw4qFW-_0XdSDrAP z;v+L&sOd_O60Ad9VWvd9#U}V&)5eikLV{5Z4#9_QZ&w_WZ-IE{7VVkUh?E68%v*YX zyk2qp!keNW!m_k|psC!JR_A6om(xSBLvc}g08@K^Vot6` zxEcD5`^RRGNzOuOzu68v?=ybQ7#a`HjzDoDdt_ zN9*UEBea(iZIh584`8X(wy5&zIPnPGW`2`9(E+n-DmUdt=tzhsX5nr<;-L@6c> z=iw9NDVpZ|hWoN4i8y~#`U0&-9_nT4D-41Gi?tqgvlK1*1{$I{?IHd&N z3jlLJb;@1EDBQ}dhtq^+^dvswKczPH{Q}G2Mq@hqEMJD4-W(22YX?HW1wBDs2g}5= z+y&;{%>ZWtmi$sH>7EJSu>|8iS`e&H_QOYHp|MA+Y<*oBFaMUV;8{ErV$zO?PR(V` z%7_Ck*c@&j?SVGnPIL&kNgrnMPz6k6$!@>{D9zkQ8&Yg9O!w&}tx~v>+)%0plg$Y0 zB}*-vN1l@#ImJ;nW5lmx^duikW9#C+D=detaUUGx-E8)Q)w!FZjvq#^HTr>Da(VNm z+|)Znp28BfK61CMd|C-n3e*IfT!>GJUSaq+0DhC*EM>72Y6)85oMlg83p`#qLz;>m zaI=gXw7b$rtcfnMo_?WkHFNnKg)5l)t~1`Gu)>?0s1)+mV+uh7j6(0}RiUtJQr=hn z6SvRUi$_K!YOOt@^^8l0x6QG}2R3zqxIIv5KhR#e;oat~WsGsXMdOf4e}Xfxso0&A zfv?P(I-k=LNAlwt9io_YQ!PmH=zi%gn?*gbcH2*SEO?jB0zG^IMCd&DjD9Z_&3$vd zhN?{j-{4I=0gtx6fcm&WU?h4Oc~t&weIm7tn<8To!2a3{FoaR|Yjr!v5fC5gTRoG)ppcmmvX|=nzqZ8R8R*`y9 z6T0+kXuXtT4nsFlU4Dtu7!FZ)!Zj|yl9(!kA=IO+Ui%9#LQn7&;V9diG8dV@vrw5v z!1~_jpc}12((pazlDpw9hv(~Fv7zijOLPo7Yx`-guL61nTDhBnYplAuEluHFT8OnC zGtDslE36C+z(`oyRd)T~o_%b#HcKB%81-754Lh0l!WJ7idR3qXP>c(KOyh6IG*|~- z=GN(J;d6Kc{ih`33+g52Gd#$u`?tz@d12rhUoQO;zD{bom&r*U#O;LN$RYW-w1N-N zt0;PLLjZ%PX8i{*oAuBR`K>($4Yb;uqpb^^7e?t(;4nF4Rz!=+#(^!uLAJx|Abxdr z)spb<$Z~RTsfb$=HhWr_@#G3DMjygl%1gQjkGatMiH>vDsI&RGowbyBb^_(d-?-qimMYk=^ZI01)}sq zQOoV6v@OCjt{f_cQusA!107`5q!nlxydzn_!`W0P;43cIXQ_*I`5$;NF5<}W)I#~( z>qvyNvbzJl%(=xGj!D`idt-SmU^y&mDH!kC4wypGK8xhJD%1C(YMYLxh{Mey+HgFF z+!xkHh2zSgpLCse(Ir~Xeisyj1!*Nmd+UI*klY40P~*t0wq}AY{~cOJ83o(o+ccXd z+3#>F+a~oBIle5b`!8kB@Fl8^aRb1f9b>8X3SJ{y*=exX$#cQj&-hVm@bZWS_$#d! zyIzI3Iu{6q7lc|JQt9t|~o{S;$oo zZgd3F40XSL8_$rdnJKWTK7zSUKIW`8ccdH!B|vj>P&!QA(pNrst>66eKMtK&apibM zi_>W_DOfMXJ;!OzR}_L4R(EuPmJ+&|E3HIDkueug#}^cL3P2W1E5CD1ZCeN5!$ zV7RLy$zW$}Q0xY=4xMLZF}>!O?~SI*%k|c1MD7wRR{H5vq!Xl{=%y~cFRjBA;?KE0jQDjd^euW5=!&0v z-hfrw8f&wCuAjYu(iYW?^|Hg2Gp_pZ2V+6E3&c6*xW7voavhQg&gl!1z1|yEPqQ@p z{>OoT-2+`|SmSCL6U0H#X*SiHDP2jNFV0rO+K!ru4`3l20sE;%nHO_2nPb)ziZHrN zd#e+iY4rpfKqp&Qwjn=Eo2mnhxu~yHL21L(IU5`=@Iq}lN2K+6U6>l~6-0TB*bF;E zFG|$ddc;i&p-NVzz$EV+!m+H9a<0?TW#-J?g@?g4th>x}PvZl|A+;^u;hYSXfL_8C zR4Mw5?-pUyXerfy)V4mvY<{=gGlzrM$&D1n)}gHdGO=bint2){z*ULCcHn|VOQqiy2#Ei&s(qD3tO#wd8 zN)e=OLJOvEkAeFrn<9{|z6m8H*h3!ZXyKnM?uTPGZnZ91S>jVLkegLzfd7&0I%ut~ zhc~3=<~8_M_XLWP(Xbh-PHyWRy)*g6^a!${Cc+AS0KYk`0jMp#ms0T%DZ-x{8;Nq5 zD>^UKN4ArXYyvY&s_!VpwNFhIpVKPl4EQ!ZQVb5%XSe)xQS`pF1wgtq7ao&t&`-D} z_TgOgJn%@_P@#>r85t%f8F(X_Z0ewyQrecsJ2529oPJ!>BIjVza$>(Vge^*&n9nl#^mbuhjk?U)I$xb9`(p635E0G`MBI6EK zlE&IiR9U{pc-_xk>&OK1OKT}-7$HU*HpA=*F&V;T!+1s@6wyg@o><-=&(-y{v4vxs zxhPN`Rl`}vMdg4{P8q8`3E-_RSD$SpZk8INw5VyA6p@BDhU|t zVjmfUl=j@jnAQASttH&0?Gax1&$xc8&#gMbKDjT9#qQDx>j0lYk9wEjd+3bi0+)Oq(UyKu zxAEu59Xh1KCz2%oZFC_}jW+ILH+q9g`_CHh@CBw(K7krZ9X*j7YJ*K|b{P*Q+w3rl zuOQYV5o}IA3pHeQ#cb(;xi7{k=5zGatLmFsuQZ(X|FLkV*#te|IuRW&^nb%ujdKA- zyTpE@wEPE50#9)lI))64z9pZG>5ap1XGMZjNK9@ldTut5uA#kFd$pl2A-fT3C-==e z%?Oc=;7FDSQ$Uzcr;)hW#;Ck+N`Pi7?Cd@ec^MQW%V`fR;n$Ikl&-j!r-|59ct$_S zyNsPmn%*A_Kv(UlO37eN3OD~&YKX(oGrn6ykT?!pW5GT$kaCCE>nGeZ<2y&RDIORH$8hlX&|j3e=!=?1GBz(Q|x0-ks5(?VJ!ZvEoJ-i zVPKL}Jw~L*_y}%@wJyxp-OXyo)JH9ZXXGbt7#Irg zY9A#S8-xeS&4`>=Pt4A1uXEtGR!{oiFDum)8geIK5wZs!HU9KBmaqIo+lbmI&qd}bP|pWAmb%g^kZm@LoC}VEI5aZiJ*&b_XAO=` zQtYt>z!30}&G3)YPO2QxK^qUCJnARc29neagyP$H3R9|VN!@Gp!TXsG5qJv|&KgKR z%WtS8v}SbQ=H_bt5iD=a*N%~~dOdv;xCjR7j4P_OH4@+!@=A_j>NTCjg`Abbkc*up zZH#ARBUd#b2>ED#I?=t{d|}Uk;dCKa7*s~Zf)6l*#S&6J5QknfqW^1BQdZUFM%56Wiud^8VpninzD`a%ABcaFC3G*I1!{|-+Ct_lya8=uNK^)} zTiqQ~1m3YLdKVbWHK3KOOL&l6%TvuAoc&M~jX#7SbWK_bGuh5=ZJvkLBCEA6STxJx zMW&bjYqpR(=hdOxrQXUYBb;RGZ;g2|n$`x7H15)w9L7$e8hc9*WNL;RU)FZPS4_jO zfXk782yM-4Mn+mGJR63(>d~&CKASy6DZzB2)y~<;{Y;x{y%y75yB!I7av*_`2G-#t z@RBeY+?6IX7lnaSzzEdCly%dMKtc8t&(HQSJ=l8dESv>vI*z-Rmq=zDouaUsdQ*If zN(xbgpr&Md)K;49h{rx{0`mthcJ6|w;ZClfIo%ndebi5)V>rY83EUSPzFWD^gl*)S zo`)OaI}0 zfri{!wdTe-zB*)unFnI^iRd(q6X&Z1g{N9mR)17L3x$?+PPu1PXFKub`gG{pWG#F09lybhq*la>lK*((@R!(M`CE6Z7j46gf7o7e zyp_P7*0;89Qbkmo4$ww|TU;p%Phb`3l$?`A8@!T_siU zUe^HeCQG7SWO1>D+ zy(F9|jA4Y5)8HUTBGc$uIl`<7U&h>WpJ7@mguBQkFrZP4w)_HQh#uQE>UN%FdhD_8 zzxBh`?C2;kOmxZ>Q39)euE4Wmb6CP1!ac#Jzp}iUsW=-6MPdv-oc?0H$5`MnzO$(6;$pfK)k z6mi{QDH_iLA+(kniFjj!GFEMC{0*|#UeGMpBk~~_E7RD`Qrk7u#oX2Sh_n=)hntlH z?yJ(|N(!xo55j-UwL-E{NBoO4vWB2MI*U&CtrWsx8Sod}3&-G9s5CZROW{Qcxe^19 z+{Z);C)0Xncc_sr!au?L;ZNa%TEZ$QT_*$Fy@?Lyqx@t8yz8lKT@?1%#_L7(<76yL z4jRflfDf3Ps!#DXmxM}z z-tzODZt`Y48P)f6MwfMnipY`DDLMz1&aH`t%fH;$1gCsS-o)PdPVz;(1`p<-wF(G~ z*M2!ub)5By%)`rPt#S8NE2}s356&|DaD4-LYNj%;gC*`DZy1qf3%P>x#jK%cTz`^- z`UA?kaMQ(=$Aye7aIG6SW}-Z6t+5^T;h)n5`W$dlm=NB^@k1<5R|zj@Rl3WhbRHoF z4?e;^I+Ztxk+>vZZcOzm?nE@YcN1N!L&X9!iwf0$(7Yxi9zfQbjRqxY{2Fl z=RM^)A06cxg4fGlWu@Fk{NhQ43qc7w)Ck2+w9^-Pe6mVDYD}ExJ0}{21(k1;$z!R7#Z9=;knLAk@r+(v0qG`067)d(8d9elT zuk?9D<36Az^d9}~yu{S5qg^)nAe{j=@$WZo3B2+@70bd%u97uVDW7~1wIzLEqTVu4 z-Hi5br*lbdxh@ScH7_>2D9U!5IFt&|@Q=Yig>>_^Fa!6^&J`b`R{ARefUVFCed;#d zL~rDaFx7+YZH!1*!m0=7lM0|K|F~iXd`uz(sic5#3~dI3;J;W^mawY!Ht@j+kjv!< z*KrbVWERH5ST^n++#L@#m*gg(9kf5m_O`&E^h{#QU2E$=CyNbj%V|+@5>u0$1hIIW zJCtswHB31thuvYZ@G8kiJ4CjjxBMV5$bOmm;Ua_{_KU(-FoRaX7aT{;UTToVvgnBw zTiKrMsT$jypB}m^FpT+!T1M@Ky>p^?m6~{(TZ8YI^K~%eYiMG}>~QRNo1n6lA@?Jf z7(1Lei=l(^WlwMHV@k(`V6yN4Rx|%{Pquo>)r8r2j5#%T2(6tpjwvq=YfXf?vj~UpM!;u`0ksEp#6#g4+?7NdL*Z!QAnQ6Uvj`poUs9)V3I)TB%-I|xsPrf; zZL^!4m-qrS0HORQd_Siub3#@Dra44b zu_1Ml$GdNm^Kwt2`o=J@oont4t(2&_#e7N?GQ!A4c1B+vkM`-s+@ZLn(1Uu&DOwTv zkVhyW=z3xEKNv|hmXCKsjdSFe#*5GO=JYk(k00>oqP=u)AWfIKZPWqMd?WOuCY%0A zpLq_HmZHf9slBhenF{V(b+!w#xV2Zvl#ZhdniIsMNvJpLJBQ+jVkI*f1Vyj$u14vxchsA7lhD<0;iOnB zWm?^(Tz3VMEPWtPXl>caect#L48<2bzu^~fPRtQC=U6Zetdh4113WW9cWo50sq4(G z=z#dfERQti(nzPN@?AD1`7Ra)9pGQmL1F`4$w8Wcdl)qwEtBg7Ot6K!EA9Zhk%YtH zTv8(3m$4biL5~9;&DiwLN(pHvX(|-Ts%;j97inL;pfs4aq)*K$Vh1`TrZ4N=s^Ob( zS75!>k~vY;1|0lw(oKCyR?0uU_ZW{m zNyyM&(_Um9=^@l572yNpCrQh`LWg4)J)ox=pW!2Y2CXi3#)+UaDJSi~Bb?cw9)2sl zqFFH3cqo6hWh;Idt@fmL^E{B)**^t2n3|*oJ*ysPKii7->(opK(T*$fi|BEAyHc@K zF;?dv@S8vtB(k);r>;R!b<9}LNc5I#L+>%dZFI&Y@KLxNox~KHr>!{iq59scZ$5{6 z+)rG)X({-X#8{JzP+_@6l-3a8I(&@v6Lj>~lpW*<-cV^4j6}0cKiuQ_pZcJw|G4A3 zzdl))-beMI`a)HYn=IFgWYm(cpohkBYpx@YDYk9_huBW-E?rXZ*sd!(`E&Nm;T5e* zVh#P5`z`Ga{z;`Qi}DIJg5u#JYn1a3=8w}zb5~*cq;)%Vn01Fe$)<4A%BZxEW#rVC zE)kZBOT)QwR#)E)Fpt*cqrnb%gbd8cKugu5_%S&jT37L!SNv~u7hb?6#qM%{k_XvV zm`~|4b9ZcU`9EnD8P5o6p#+&lP&+i3@h-;b4W)@O56tr51pJ#c$rYKF)DC8`?^QxY z-M^VX$!YrxD@JSbMdgKjmX=c5y8_}6?Hb-I9$}rua8z4Qgtx4}Tp>6acsy_Eb=pd* zsiuP+T8w;RVG`HXw8W;;J<#7gEUbqGeEVS!DFQ{~uSSY7H98iogNZ;itKu|mj<+8j zF4keX_9jq+>q#ZnX_XX9i=V9Wt{kx3w^V%Ys_&_!GvA~ZPmak8&`2#Fe~0h&9#$uo z`+S1d^f$p-sHGX_rQ`?vZLV|Xx@V%o@C-Z6y9htD(ZTuE8hNd0OxQr~ly-sT)>Po0 zOX+fesV!Bw2)-sM_BY-lY&R~@by9w%i#kBbhg;Kn>|`5G%8?WLYF5q8WQw^4bQQRw zYT7Alv2~J;TXR=rpF;+oGNw&97T60@X*c+eA4fC8hbRvNO;BBO3N}C^y}^NL*%Q_gaY<*d{e%-8Fhb--dEMV0}rK^>_RmW2iGKIVM$sx@4gf^I3@QGCvCd{a+w z9R?A6xYST9PCMG#<9*6mI*%^JPst2pC5+-*`rgu7@IUD})#+q+xc=4j;F|hVX&_@L zw*+@#F8soh5vz0Sls#fO@lQpB7loVBD=k+=tYdatt^Fm;LNf1tNE_fTdBwR4bhof5 zb~vnx?7$|KG2(1D_=Cbl?IDaI3&Z~Pe}d2SZ{A_X9d=$9hXv?;eyd&qWy8VzLt~n8 zL|iCNB#SaEbBq-ZI)SU=P5*dz0|#Q>*!+3}GzM-0een)yp{2|B+bmQT4Rx16i`25xXIBsDEdC+H>Y3sJ zx5j$(8=5Q7mgRk{7Ox;%=oM5EUNhISDyMRE2kthiL0i!~lqp4xH^O@K)?CdxuLa;s zxu*IPYj9pou0X6EuEXckbp017LwAV*rLW=>R9FaR!!)xpODW5g!f3d4nYkNZ!v*qR zsIc%z`L!{TIf=?icO%F9sx#6QvN5Wrr(8@~ zx7S*#td4D&=QZy7p7MW5yR*lEOL%;YpYO$#xU&OGg(Lb)u?BrXTta^Si`tp&;F?8t zrCwutN=GaN94srm6K&2#1((!)%7Q?Y6~x74L;%G*3T;9zVKm#j{{dc5A?Lg`j|9?= zNJ$V$d8i3#08BE5Ua~Hs`<_`i5eKJFg-iUA`Y>=tNC8i1XU|Jghde_UEEHA^#i~ER zEhyMZYeVHq=rafwfASvk(D@z==flZMHs9Zgf8uy6L3pgRL=OW8nHFp~4s~{7o}{~M z?q%Q>W{Kzt)^_ivz)G+vaJt+jU>Re~F?0vx6a1m=BHvw^s2r$IzS}~~0;ILT8Bg$z zynC=^tU;6Q)7(qN7-@3stDHJU4XGvMrSuIwq>^YJtpWZOw6qk%jY{O)v!65Dpw4)l z#<{bYE+D`kFrVRZ@_0~F@Z{K;&ZVIz1}BOCDGl`mN0roYGJra0Ghb{VOqsxL?>^)_ zx-NaT3|o-5f-RUENCMmf*KMXAa)2Pjn{V(USZRor?Rp zo~_UH)iJw(EPbqJ8>vKky4u3xfjej&sZIU1V{%Qe(~TJa6)U}9cV6ymuE6#;QdG+=ewIAkf#!Nmzg-Q=Bu$;H~0AxEW8)tpJBHzu;_jjWJgrjSA;TYy!5xGZpKM zWv~%a=@XsAX=YG`qu?hAfrDvBc#11vO>-5D=z~rfVOkfMj7Q78vbvb#QLgRG`W1{i zmPD`cRS~2kg$$#(_yre>{3`Y(O`=L?G}f8pDbN%5PyIuR@ZLea$?E7wpoG7Z-kzrV zhjLO#X^@&T4GxvFtoopZ=%@XeszEYmF~@&v5@seT-77YP`6|8SS3?YD8y|$TV5jjx z{v5U2J&fkiPo_(5X_R1^p~cD`^SW3`yGR##cj4dgf^o%V!@W{l*$crd=np!_x@jwG znJ8FT9dp=R#@&Rgt&QZfT33FnxAce6vbH4OYTAOh#VqTYkqBI%b=ofZrPQBv6#wA% z^9rF8w)|c%T?AIpAU%Oj2Gew(@H+iTpaEQv{-5kKV@Mwwh4T?1^?`nw zv|x49G{*l7GCR;@c{!_LhRD~2;-H;!7bZ%j;6hkYxgR--+G(UH(KLxbb+~{vq?45AyAFYtW$w*d;m`K1N@M^R(Rmg z7r2{sleEV-#m7%rN)f(UtVwIny=nL zk5~#$OSyC84Y3V4N1DQOV!SaAeH2VK=lo@Tuw1?&eg z)F?`HxJ{h}4^R{I13kzO^MW*F=^NApHb>L+eqzNytZ|H9vriyp^gVVLR7?Eub%X*$ z$^~C07hZtPT9GQ<0QG`r8~=dj&VisAQt&L7vxG8KWfPZZ<$J~nc{5+7P$KY*(hhL*tm2AIDi3h4)B)Ab8i$r>W| zjT~=|agWnAeUA9hIzc1!5GBJhn8T5z4=40AU$PMfpw;zE@OMgLa-5OOegDYScb=BHJ13p#k!m;xIEl9O4RT?*l30QrmT%x2?3mn}g>aVi0;U!7Y4Pk;s4d!;;1%y> zf5LNx=lTw|2sj_@c^TMp)R*2HE5yUFarl5DMJq2FnuK;+O@C+DCPt(?IgQ)r&jl(+ zUG9N9>bFUntBSX=GmAu%+3`Q%4eOwGPMjoOOzf4ofNe7uXvx98+Go&4N12=XB6?nU zB!4njxof%Jn&HfB{7<3V{1u8ZXPA>hb7nnbLtG``Za74%Am)d=)^F}E31iSXgIUiC zFO)QT6-O=KCfW5PY8$o#KcuUb8+bA=q#dD)`7qZ@#;I1uqg&Fu6w=7!aGln(zf_4IJ&IZsQ{4TbnQfR8Tv%OiUT zA^gg|&bFsTo8jJ&y}p}w(wFixpnM3)lZn+CDST9h;GwjQm|H$9=Fqon zf_s=W2E2?T9do=P){(B|oA55Wz}&UIHSLamrafj(7VW;K%y%x4inGGt2rsPIL0Q*B z8>{bIn<@Cc*GU_qu_f9-y|vO=YOD_e!em}D3e|D#30;SiZKl~({Ky8cp5oJ_9!8?0 z7yeV-zJ3E=q8E{WNYm*?nV3$lu}b9YN)I?467+@gJ+cB=n|DhOGmgdNG9;*5eH^u< z8p>(ZL2sDTiZ&I~l$Y2C%(zt4lU6|Y;D&olE3ZG5yUAbNOHhU_5T6{{Zu>;fnAgxj zU+>XbR`16h z35!W-?w2)RLq5r~%>M-5@%Cmcb}AW^x#B{r*esFiX?+G?f9tk}}5B7ZMHU4m&| zHTodFiOIyqC^vMg?L%g`1MJO)yE=w>PA}RSZ!(MGW=0WzLDYifC;y@Z&}Wsxy-|_$ zy4nJ`ml^7Hyf&j2+bv#Zg8wl)8Av1zQ6|l;%;!wmN^gkk*~NInMfgA2yHGFvXW%ck zB-F|Af-RJXuzN5aXsaJ4)%9djoL>}ud@!9JaNt99n>K?w;q(1Xw+los4eF_-WSz2w zyrGJ=P!E#b%x({#uJ5WB~I#8r{oXzP2AyW$8tXjWN_F^Ml%213Q0 zpKzS-ux>#&@e96$CS@jtBJ|U|q5LiLW9qrc3~84Q*io>LEk?R~L!_YC6Ftb9!0foQ zIWU~1h-MtyW&cx)qU}9Lz|Y=+a{N+!7Yd@s_`6U~T?HzOC!~AkY(1nm5IvSSU@RrE zT6%lFQEULZ;p@&M`MS~*_GyjGzkM=~mC|Ft*G;+@_cX5hVT+TXwPyoh-zwrs%1^1U zb2e4Yx_G#ymDEeW9hcuR-1mS@boC?c+|`*0oGR?!fokOVIp{pWS22yty3^MxDngip z_6H~ES-7Y*-l(g87bEFFX(cJbYCH3i!l;*&8*Px&wF-X37KQ{NgAQrD>X5YRdvVBj-IRu7@b6Y-89D zt($on&iicGi0>iC94pKrLQButKm@F`DQk4YBT0vVm+6v=pQJrmEi}qI$^6Ax z-WC?i2CCCS++u9hZv?zFm=R4lQ8a>&1Fh#3a3Y&p@2~@6ysHl=Ga4(5I5Q51o2iwV z%zL}*uyk>={GXUWblZOME*+BCo;9Oqje~&-W^MTVlcd))9=)U0%*rt3r=ackyQ0Z! zaz$;75KjRLK^~e<7|hCR2ZT~=scRu*Cd`XF;I09)(GzH+>xS_i-q4V{p4m%zinyz^ zve;gXtKt1i!KB8dM>`uSzKVb+rCqe_jID& z!Yh4~^kv?+A)k9RY0BGE=oHx*AikKir0CN{*U2~FVb~3Ik8-!SBDes0x$vyQy zT&vLwzgOv_bTog2jxuBx^xkvl!mC(*?!z_p`0{7*Wcd$XO+3QKnyo~)dKnLlE+vgg ztcff7rLyDXF_4+fQ%J|ZDeg?V@Xwd8(2q&!E?HTE@Q9V*QBw+z^u{6YUt%*Lm*!= z*2^_`CsN;NOYQ`FLaxXcF2*yL=51n@Fe265>!+Jj%# zM`p$1tHO}9R&sqQKLfV|sh!zesDKNAW8xtA&rhQQT905uZ2_r_p1J1XV!Mc zyVL{twX-w%J>>*nWw@ghq6iYprb(Y)rO7CR0oPkzT?VVWPG&uBbMWx08p` z1jw7KhKr*PT1RJb&=b^9hcZ7nXX5D!v`Ky^#tYwo9yq>sm^ZTG{HZKM(akEZNHUyv2kvcY^?(GdIweZ(OV_gZ?2kYsO=Ck{ zzw>{j2kaMfp<}eOq9TUYnu{!XNK|mMTA4f)wwv3vSyDSK%dDV258QS6pnq!`=X1H( zdaIhnk1e86bm$ou(t@EJmu<@8pi_`7vwO_&t(nrdl7 zyyyxEf-|T9_o@9tr${xukG@ZSjGGa6%0==MS%TDj1iwH%cEPhH$U`+SWR=nOc(mjq zDelGcG2kCA<$GN-_-)(^GR4Z!ilP6^QS?RX0(3*$$frSW;0ew_=RVZ?L2dqGb%oMJ4b zb7*z_H<&-{)25(k-{ineUzLOisK~lf&(Tibhc>$taBk~(;2%pYAz_&x1ya5*msGs30VuMCpXm<65luFHHE=xPpO>y?#KEFEav@XyfnWh<2g zT93T(UUnbg%@mJ}vNg*?{2Mt$AJAsvN?N7NGju>|CJ%NMfDQBndWs&mzgJWvLH=ya zU{To6uj+%uB=Hw{t@V>DY(BRx=X-=w#!NJU%{P5SrMDbYSvwLXmOwSpHKkEp9D9i7 zpsTC_+;)z#nL!o)S`oUH3NEC#l)vhobvLP_*KjPu-}FRa;;&;L(t0J2L)s#$Um_gPy{zDwEaxMGaw!{P#Rf4ED&l*?&h60{^q2l20@xpleJ z-l**^$nNNoG}EP(yPh5()9~N=YUFZm3~XH97cbD7fD++^b0~b~@=71_SWdM*5F^Bc zN>!nc+!cjzbu!CQ5+(`>c(R7M;)mP#( z@vGWa|4RQx=U_s$jp$@W;1z5;AKP+cn|@j7>du?+L$>LwtuuqY>{0B>YL~U7eTAo) zzb*fsFcA+_Y9L9d&(899Pz7VrR#p_9MBP{wf8ERh@=v9gIS)>X9=H&ZlS(QR(HL^6zpwYNa`^GS18O~7qw6sYbxhgJf|t%Lh*)IqrkDCiC=myF+CQ?=@R zAVzuyS<7;}p5Z36P3m#FD0@CFtqsznZIj)-_?^%qW3+xWxY=2Wlr;yzykj~~px>hZ zl(#zC21oLN!Nb}h1!=>w7n+xVLO;UQL43s8KnGXXQ6HSXJ9w%&)IWer36L`hRN*tE zj`bUD8Ggyv1MiVQt$9LDxr(4>iCy_Ztpr;tb;Wb({+v1Ni*X-qW zxCPYMeE6n)JL?>}g5&io!ACe2o`z}`N~?sL^Za_)+=!=w$E&-)2KoOSs2-fGi9CzG zU=?uDnB1zu_XW?<1j|A5p0#;!ky0YNGu}eiY1JXEIH(>1uEZ2v1XSgnLiNQ{xiMLx z?_&j(0BuV1gJLaKuM+tM=km?5Ul!~Pv??KQ)&q7v7(seSC*2zD9@r6mP_`jidKQ=* zH{HFCT$O5tI!R$UtPIT<1bLvd(LZKEaJAIbhVTvaH$AtOM54@_kWjK7k|%HCzs$|) zmD%5}E!rU16%OI+^rf(MJ%Fb<=a2)SgQ^x92HkR=(tQI(*+#sOW}wOJW7#M(g^m-; zf^OEUO%gG^f_s>4$idKb)FV`qbzsw#2uSz3!q3PKsJ_W!SKKop$~Y|FGVIE8UoyOJ zmBpT{iy`wuBn?mVO%M{Y3lJ?};yQR1%N-SzcdY#>-`6_9Y<#3smXPD~XQI+B_4IPD3U%={2#al%4(f<=|r_N^1(KF&5G{=N9-Cs|1!f-)6Nod&qz4m*hfxAq^!i zboF&c@;N>YW(ySqDd5(v5tpRC!n5>Aka2hr3C3DF+;IS5r6XujX39+iziK=9AAy&? z|37c<#T&uFY=&zJU#zB?z-H243z*MBoy;br6fG6gMLA6tuveD7p*s8w-;KA)?_K+} zNbSGStjygjj+hQympl#(VB6FetUjIt6qAN>Z+K3bZJBtOKBE(vpoD?%Gjweb&g3!H z*8GKIOYrZUW2`iur=5bQy|mO-y({dY+xVbmRoHIOcdyObhGT@WnO(qrS;)+-<+6sQ z5X_;+x_jr07fRsm!V7*^T#En{JD0&NC2(!@kp z_zo}Oyw8$QirOP^g$-xX^aXz&SOgy4!uB?D7dkC?OY01}v(CIh;IeaULOnE6UyxHQ zR0u5tm)F(Ma`PoPMY?FmJZ<%4{7?DUvYl|{&+{lfl= zAMPJ#JcaLNJsCtNiMeQNb2+|3+w$}5y6df4nZI`xH2)Mk(z(39kq?w*C$-kh;_XEH zi&gYu#sV}3kCa;A=UKhb0aB8bG&Vax2`Lsr9`K*c7oPZMkUf#J#CD`UP1h!op7=Z4 z$9u#aQQx~G%;%*)>z|>%adH~loO|WsXge1~7hWv>0#3{=$_KnrKa_qu_94@N|1eIR z5Deq5N?6Dv*9&b94C2r9dxAl9v7t0OCNts|BrOzn-VYwZ!?jr+aM{s5XruAo`Ah0T za@YDidvlzLQu!Fj9n_)g&!LUPG0yAc3*M`w3iX`l{89V~ZJu7pl}o>{ma#_sS2U3Y zn2qb1QJ~k}Cwy?7kVc!0$ZE94?{ZbtlY~YI<>_5*j{ZRWqMy(7qb+(#91UEly~d8{ z5k?rllPcN1OL<)DfiRe0-^*XIm6^p{`{)L?i2u~*qu=B|fq7IKvfJX(Vus^-$aSPk z^d_H9^Y9c>RoLi#PUbt;(RlHY{veP+UXe?o4gP=eYQ3a%E%-Y(r@N) zeUr;3_7z&Oe8NdkdUlUH?tX(`kvk@2>ya4wFw7s`$jPWKYRZ2lf8z?CsfOT-q#E1r zw+p9fmRz6jVi$~0{z0j&g8l6pO(Q?_{nQLT)?)bx^-}g+zKQPub@&um-|1(omS>c9w6oA?E8-!42yFySizjh@;)*a8_ba9rn%oyC(= zsZT^zdZg}To8?PNKf2es+TByTjv9L#Srgpj$P1@py!G|Oi+DcUH@n7mDO*;htLND; zBg{8vo}ew1wOT^CF3_RRi;S^Va*rU{MqN}IGoWX$6ugj{?kJu;KopwHxAK|ry zwx}enpg@1gizy#i8JuF}YgZc{K15x~57AD1ytN#=3y!Jd(h}!Qv&gAvXS%J59#5vo!pF(x}==2ziq;ck4HYDM&TATEh z#FRtoEUWHp8$Kr{Cmt2E^j2z5T8(&^pta=%)b)ujGS56AG!f@yM4&*~`Qop^#_rPS zEY#d4jMMvcrSu4mA~mAob6qnB0QI1s7O#(XY%ubv%~4)|3C&P!IZqQB8ZC$&J+~&K zQ>+RZ1RU18Xj@FBz;1IHzb5A~*Zcotz3~lVA@fO>QW8p&07o|$(I@+78!_Cks zlomJ)?@oxFQ0t4ImBIGc5)EbY95YrYfzS5G*$?IZMj7`}J&tblG!*|sbHo!)C(dQG zh1&a1u#opLi=;Ia2QA8L#O#6zNN1^O#(!QlfNg`M-!SroALmlygPgHmb@@oGEd?KzhcyMo@f3ptAGhtJ1(j1g=k-$Un_d2P9Y z$uXRIg=l?30GopNBDg(}EZlQ5bFh+|jitXjp9bT!84>OrBwYzzF^;oR_M66A_pf{^ z{)p26 z!Zqj)yi9JvP*gnubVsZg8jp9v{9<{aqcgxR1=@uV@>Pk)jIYXmF(^-mnOy;3DwHHG zajZ5TT`_v=g92yet8%4aRe4wTFQ8UGL8gSJd*9&yf+OYgQnY!J{VVzcF?^2l9(7XA zt^c9dOlU=~&|cZq(ORvId$#=1XhTl~xTf2Wq6+q2?i04o7T@Gv?nvrMP~ z$(vPC8yTu(gwZ?i747*&Vr|(zpOl4y{zsf zK3ocopk1`Eyb#`vd}Ye2SKVWUWU;J;89USlftTDb2H95MIqAGIz!EQwl2*uV$;Q;u zQVUX%pP@;*U3|hYePaydy12Op)8T&UvPblieGm)`{b?vweTS z?Xw%*EXJD)ofwxzUfhg}%2;Pz?I|nj*+#AI>i*Q#m0XW9zf+fg0aPC!=}14}%F;Gb^Lb6CR}7@KmXY zhR_&3(b%h8K#@YB&^mQ9ex@8y7N{j0Gt|xOH|-frM&7gA#K5|iN6)e|wnF86TPG|7$%w$ZjU#TYg)}5l~%!@10mhV z;@DVp0DL!t#9_e}a5pXCKSaKUZg{T1n|_-<0sZ3u*XF=F91!$`QlVU~r@V+f7i+F> z?7VGrWDRm6XCJsir)%rdtC&U9IHU%O@s50Us4D%;s%PcaAtQ<1;1~4!@Eg4rv+)s9 z0`1PeWuJydxNCA2Is`0)l}0Il78xg}h$ZdvCqvlQhhR8UaRFIElT_m zumCqSI^+y5AOatTTG5sQmz#vAJFbgr_#E5D#~K|Hf7jM&gSm%XHea~+kve1%9ir3) zFGLX=4s=!rcyqbu1oEOgLPL28-^CZ}NxnUh(pR|p)7h&+dF|=BgF!FR8d$nqHBqYp`ptBBSku|#+jUank~kMan95Dx*)=??iUB&&^+ z&XdMcYwu=~M4O8iEs1`TdZOo{oKQ91i^u2>$V>85e5n5+*Y->j=gX%wH}DQ;(mwR7 z*~}ZocAJH1p1JqP8!5jz95mD+93L^9-Sza9XUT(X+=v#Cm%20VDFs6KymR0Z=LbWf@ENKT~TIp zaNP?J=y&fgd@UKN?a*GB)oEWPMk?U3YR~XoWvW&bO=2~C@pzEY52f*IrX7-h*Rb>M z)^c0xOwUF<%!p#o>A2V~{4+jbOoX>_YQj#VJUEDlhpX7n;EH@bvq@ZR=39!Qayqyt zio4+3885GZ4eC)|I51x+W%foy`APGj4$keM+BmNaGb7+sd#raf+9;W%n)9)Fl79|W zS15QxC%ZkuWYYubDtUsYGrxU=dcb{F`XnueO4jBcToXpH#>Uy8~E@9Cd?jmet0ep>sKesKxxHr_@~@cg`7sK4`!GR1j2B8kL; zFD}D8#yXL~;Mvm?SF$qU5s{TC(r1XRSsONxp2bb^2>Mc;vV1c5cDHaXy`Q!v z`&V?qylRew3cDb5lt}zIFQ7bCbj=|_g^OvDIl$PatrGJHT*;^Q&w6GYBE{JW+i~`{ zZ5(c(oenO7ZEMilO{>b+YjaUFy(tN_9T$yzObwjmi`h-mpU!oC$D^nr&kL0Y^*|(^ z=e-(QNPYy1u_G{>7x-163Kb$H#bV?EJta0!B0>Ay(~^dDZ#~EX_PYN;RnP!Xf&V6F z(JAC{v9(H}@ELKpumNo`x`v;lI`p7zh`cO3k*9>ahcA&qhHU=FRP={(Q?8ad$G1n@ z#CAkY3@$U4sP*+ON+Cg^J?!;)LH)9LB@p6UjX1Qz`AynDsXhwk81v9@<1j7EX2{W@ zhydi&E3LJvXgGPNRHGKxG{`SH!Xm^eW`eqf<^xjogksO|5PlxAQ2z8V_kQbZ>x95n zvpN#dB5O0*pTyicXx`c6n^AK>~)$H|id<&3drPgg$I!-!;D^8a25p$6)sCZGk7 zXH%7wHx+5Bb|>g{jUxHOdGL8YjEmth+M3|va5Jf~Zx8)F{0GOjiL{7V3724}4kF|XgkuuOR`TNV1G=<({HqbnM8Uy*xJcPks)lECF04aR{~8%HbCHLLy>Y66cvc*ijTgHY9vvE+su7WnSap zZM0PLxtp1J@np+Lvxq)4&{=uvUTu7!aZ*jTEjv@)=4@?m=#D2-P@#ZESIQ~EU2+2* z!&mfYye3}=+?sy)LTG{XVf|WG)L+`UQW=S`aKQc{%b}I9U9zV68{$Y$S9U>~!3Xip zVqW)sPvwZqdX&7=GRVEkJ`m6>4bBiv z{SBKQU6F5QbI5HWLyThIQD3Er^De6g^X^abcd=NYT$JQ$C3f=auJVD>at`0Dt_bW2 zw27aM_VFiDx@R?O%=63LbAExU|DTpmzswbRD?X*HFutPyGoQM%6}&G7>We6whTh9r zQpyeWbRNO1Bs<`!ZWIX=A)D+m!b?;Qt!EuzFE}x@64D@V;_r9{nP=4CU(I$H2n)(j zRt@}E)4{RRl`5bQDu|nsZ(>1Iig%%}jn2`EJUD`piHTcvPb$A#Dfwp4D#O45m>9nN-^iV3n*eJ|eyJG%Nnr(5GOC5<%- zE;lwyZ=LtBF5cHCqZsLlYgXJ#vljG_5AY`Ac|Eb z%QoRjtS+egEpj0v83!E+$~rPsOXhLP^FWAQwqDaWibVsT^;fJXy6x?fwF?d9FX?$p zvQiGXzG~Vn(d&p|P_@CA)Sdsg+R~rIz%c|0>@OsjMF|?}2wZ9X2s@^c`eMOfpNy^Wn@z_&NcG zu)UR;k9sCDhtp1k@_@FnKH4uANBv>`UzMfFO9TDU zD_aAeoHf<|3Ma#FwQ=g-vhO>iAKB0Kpi zZH>{wRfxacM@*PlS5Y_4WNd5kx@s_?YX6Omu@IX--QK_3per9 z5^js4xQ|&N8T6U-Z(M%R-5sEuq7qcC>JTQ~vjTC-{ zB^Ln_m8z~iFOSyU+6>)bm-#Pbkn=ZNad}3(GZ1G|dPK@c3%RN&d+m=xzH*I%XK7*O zZ&n^lp4ajty-m0?c`vRF_qJ9iHKZ$Ly7?;$c5@dio>(YHRH}cS22OX#wan-7)-^pcL z92b92q*_l)F|7^1${uD`w$EffT9q+$siEZkEcqZDT6NNZ4VF-Y^SfX1UUxo1F!^LQ*6Dy57=rGjHQLKqMo`)qJQUvtua|}#mAquMA)O$N2o8u{TsJ}dhv^KfxQz_$U=PzDrtVkF}5G(uW-}ss-@_yg{eFh zeRSv1Hds5FznO_>7CqpaECtt%mUEFYT3zu`{AaOHc6EJ_+K4@-C+G#Om$KKjBzi`6 zL(fKMZlyT*K&FwG@;N-$UYF!z8Z!fxf$kLT+>~e|VSZTO#l9#O_+e*TVwHJR;Jq~& zJtwEcA!c6d8eTxUPP>rt8NG3FZKl3o=s_l`Cr~F!X`FVNJjhrD-_3t!-@su0!1t9Z zxG}Cm3ow^ljsFyn(@6Sb;c+&`C_zI!4(9>)c77!wmNFIuPD;1gJbAo1nmg$^c}%Fj zJIOUS)RVt1HVZY?jtN``4HT=3AY(a@jUIyDa%)akK3^l`WtOe-kd8Q$^cN>Pw-_m8ntsi>2q5%}v}su_lvci8?p|^k zHBo3x3t=B$pu90^M^f90=*`7HC zuT%}TK?hBug9>Xy9obvsJ{{w81+rXR-KDifapG1-in<1$)d$L;4<{F43VfK3_Wi_W z>|tv~T2nkD9}qu*$8MmukN>A%LRZYn{`%s7nX}N}((TpTQ4Q9erQn+GLdH(BfZ3Zr zQOX*Rqj%`5{AHQnOXRb19KDrM2%SJ!-VDB(JLH7fQA;+5@FA?QHHvurtE?xXrVNCP z*rRkWPYpKJ3W#^nX8M#*47K(>v%EyJq<-cda5sKXiqiJ-E@0kXYI|W~3i<>r>%ZdFg(J|II28Tt>?WV$lj(#vR2n>F}uPe28Xxj(hfNFSJUw zH^R`IH*miyZPcYJoCC9EyiCc1%DOI?BbEAaPB>(Oi)l+)cTZQi!w<0)Fi>bdQg{V8 zZ9dXHEXvreuC*K&mg8b3cR51sq!vm6!%Hg}o85VTy|c!^S{x(3DlG>uz;yTro;)vmHI?Og@L~Lmp+d zw>-HJn2cZI;jScbO`O-Zu=9?pG&>HM2;{nU458p{xsx%#3TZ&jzd~ck;H+6_g}|tX z>Bye|K zClwPG%I)>?z6-9M_Mq}wEfH!V*K(u;YVz%3VLz1uN*VVXKFT@+m7(oG+cyVCqnWYi z^;q;AzL^$@ZCztQu3yoZro`et;arlHc@p{~qb9=N`=2I5|r~tR|WZYK}3+ zKQWeQ3ojSA1#`MLESWy*ZCS!}^cIegGev*!l=01cVz8X8WD)-)UIexDcWV*eRjJ`ikV{e(z1EhBE^)KF zjO!S=m_1QGCSD`O=qM#QTsO4T7Dwu8N1OtYaToKXdl*Rx4tDqU7r?56vok>aDvwWy z4-)*O9%!QcEQnRv%BL#jl!CC)Zx?seXwBAA8>&GK-#B*1lNkrpYtDE#@n2%@8H!rkJi&kUw!`(bPU3uIBL~qb z*`|#%-eC>qG`B6~5-QntLoQ8MI)|1}c7zt0-=Q|TgCkk(>}zZ$>F$3?>uBv`nuTof zv1ow)7+kw2NKe;dG=ojl7dbd-6%5M<*-QDpy@3=XRx+lGGHp%Ma7%I=2rcRA6E=`& z;X^#P`IFQzo*8-Bf9MqN7VeEo;u^s;`8l=nTxg~^m|H?gZm;hWox&G^`gWBuLSCc{ zW>55v>=$t;sY^!FGnixts1MK7)DUM5%1`Yv{(CO+=yr86{$MoMFNXM3ek^!gz% z?dj*N8#MrT<2Pf^%k5$6I1_x>7l<2w3_M4l=qaq?KiFa5;bsUDI!z$oiX3#kMnA-B z_%crw`;xbTf=Y_>2|gX1fxBfcRCp+!<WixPm(_JS93Q0ViCh( z^Fg_JIE6Z3pSx37EDlW^hn8!T)UmRY*7a=Q|KOr}g}_y@tW?1e#WHlVs|%zX4UtPo z&q0-Po>)*3ve@4O{;MOPvYihajK4gU*#-S5K5o_s&X>aa`nXB%9)>L^0CTuzWE0v% z#?e1xSDFXO2(hN-&HRL3vVfYKHQ+tjD*iU}LHs1^7_A7dCEk@Tv&L#)sU~(Q27ipV zc=Es}-GyP2&3}P+Xtz9pT||jwE=^{gjBCbq`~}{PDrOn|wtt)Y0R>^NU4@3lN_40C z7@bn;NPnWatSdRIVQHfCIXldc>6guobe6PKuWz*SUJ70!$-IK}jAhVvG*X(s@?f zUQrtr+Ct}v7}X}{$fUU8;eT+@JWH$7sm2p&Jn4h>kfGTHq#WaKy3Uv6E=YR?^JGWS zG3Ie|6P+txH;bVy)D7FdSarX9EQgGERu!LP3FHuNz+M@x@qK?9%VoQ!xP;%u(I^R* z)Ek0wDw+E&^@K+FrWDWfdq2}dqU^Dtuvm>ghXjN=PKP#^YCM3hhdSBEK~~IpTs-rd zaoa>@=TwgOWKhMgp7i%}-oRU<4oO?dpKJ+wr~L;V&OZBmVWrmJJ;Z2&jz_K(c4=db zdaO(EoSdW`fXSoo3>Y=hNwF8bf<~F~G?UL$x{?&6;AHC?Ryx#)SZKU=Y+}9ivgQc0 z0=p6a0;hs4l6(8S_!6K)Fz^;DhWz zay$CQdVwBRU%&<+#dD4K8XO!o(vGY=bgWmR^2eQZ|T;Jn_E!a46Mr`7+ zz!vN%)6is`tbb72c$bPpvmT?F?2WYuP%fuPEv)zK6U+GsG8&dK#iGH%vLSYs=4-QD ziMT4=Muv!IaBICItrK#vzx9^NYYY`v`IB807C-{dCB91Z1S43ax@WD#hTu-F%B%rH z(wX38^p(`MztkGC5puJz=DB4=hyE&CJzxt-QF3UohSa7;H|Iiefom-eTT9X-XoU8U zeu11|&DZr6{eeAZCEpBmF&GZKbKenfS#=!4%1URcg>=P>$T_i{^AC2C>_TGfa+nMZ07Y7h4Y&ch{`+XQ66PBnuaXqt| zeL&isgq^5`Z?u?;Rdp32SG}sRl-4Fhso>3!oWU}nzU_>Uh?g9#l(46P-dNhIuLMP` z&;PehfecZO+`$j%AZ@$2Ogf~V5T>AxyrB`6KL!JQxw}1DA>}oWq4&N`zAMO{;0PDR z+rpj2&e{g3!_CE+>`LO4@PxR^@q_d<^1O&CE8|8057?CF7C$~baQTW8nlQ*DL&o^${R%dZue{uZA=VU(K; z0RrG1+aq|oq62@?KZ7;053^!YOS!saDce~7KSwp4dT5dA^7pco!dL21(J5 z>nFT#0;ZN5`B13O3rCO)aJ^FJ< z7LS5Tu!Pms9+MTGAY!h$cp$4D7#sM8(u{@5L{}jc#<|R)(Ch3Y-K2`rLvgen?%rf9 z-y>4-tm~%GAYk!Swyq^pNfc3dma!e5fvrF?%r$?aC;TEgh3AUXP+qCMI8M(ECrF3z z9nV~KCVPvY%TKdfx&&>AT8c)8``|3`ptzMh3O+=fe^AS5UG2T}HMqVy9N!N}H0W3t zQk+%MRd;Rtr%^|iL8|aYBJ=z+sca# z&{upd_>BF7`-Vp9DcT3M5&mID8lTk7kOdV7J!W6n96dJ+6N~q>aK((^^^MD(S8S?O zlROqvSslJfi2+t-N9;wj`5n?8{l!MINvsLcT!UQemAmn?%_2a?iwu_ph52ok;P}(I zG(llYZTZ5j3?XMXZV6N1r>GvUow+UNnr0yms2W$)4WU}TnY^nwLN6({r!0d2751Q- zSChy!GJqqYBZ+VkX6QHBUb~0x3$`Mu{^7oO4&^-O!Wu$*4ch)Kl08tELSlJ8@synYWkw0H5QC zvjq>q^j*L>bZ*@qX=bpop#?q}naUuTVB}_ped|c3eu1}xEy;dVL@TJnIUGMP@Cf~v z{n>Nb+n(#VQ0TF+9#=IF!*@22-ST($=a@drWm+fThIexUnoC=G7<;5l;bV|jb!esK zlI(Hx0drgyXwJ3cL7mh=18uqFdUz|(Pj5zlOg|&e30kDjLh1BBlxHMIKO0JN3=F-D zEXo$iTQ#e&ie^eX(0To)>m2*&X`h)XS&R#$n`^D`P+&Zn|3}Aa1x1nF-kN3o4n$=B#9LcHZj|>_3}Px8a<`9&U)a<@o3sk z>SaUu2G22G2gUj7(Pr|&oZXIQ)F2PoLtz?^f~|kG<-3jYuCHuz#%o-JDz=h*3ZcQ- zSfoXQyObTl1!83}*8Lu}$luntO>2^{+(SWMY_V4hAvW5F$tH1EcpSNJ%$I}KTlli- zuzyeJm$*P_gbtIIj`8{#8TfH#6|zQF&{(Y#CBfr#r8xy=lM{@>q2Zx>+6>aN)Tq#M z?8L?XiTX}+6N@;9hU?%GI~ zM=PW?!?&4(%%=&VarCTpiF!gBhhM~I8!wT>#~XDeiLVo0iJM#(-St=@{MmeMq(OgE zA*-HvmfcTy8w%11u>Tn(N3fP`k-H0@V&>O(iJOoDnaIs7cUV72O}PMMTymG zS{v25&wbdr2aO<`Md03uHKZ@fJlH*#1TWi4@gZvtXY~g99G#ZW7~7;;iM{dc#ARZ0 zvx3nfEZ{MTxq*z*Pd+4!<*$LJ^eZozuns?#k@FJd_I+nV^+sujnLlB$yPfj_St@Sh zy?v+F=4XV*QosAKSp#2Dwu*-(ActF`u%9H6@x~VLFaiQC#BiOAML|`G1E+?9v&ACd z&&}g1Dh{RqXvRCSy=cGSSF@nwL6#kegl=?&n8JN+5}z%9(zlcSY!Ir9hr3&m_uk*+ zrqWt2xFeOZ<_Nq?pJOzGK6Z&SA&{89MNQT!(47gp$v%$tg34H02znErmB3{EVd<`F zshmUE`xU2=%h)dtXE#A_e$En0K2X(F(wR#vD}Gu~gpZJqx@TGEv&o@Lfg+H|Sd2vJ z>$nHDy?wj~r5Wgm$E);Y4tA6MBjlF*llO5& zG)qGlw$OEkmqa;sTiMBUyRp?kaKFni8;}=L(a=vz+c?A)!xLIJP}FEmZUAdT!5`2n zypsIHPlJnCv}6&>;I2|fxw*VLkOcaW5qvG1jJ`)VGl0lOtI%BHXxKjcwa@^1a{RN#-}{d%NnzInU9f(-zSgI8*6uZlU3h z2Sy22-kv|O*>M&!(chGc1V>!7JXpD@9Jc+yYg~4#b!MUV8Sn#rcdVLrl_!`N)p z#b2U3xE~9B_)@$A9|5J)Shp_j7M;q>*r&1Ccu}QGY6-Qgb;v$9b}pUGe9~p=mnxAj zz?{mW-(rO#$DwVO!S;iir@e2cs|j05&+$*n7A-3GPQz zlzrS%P1v?~GW#2lwSj*5OK3mYC06L-U^ve?|7mn2QraJ9GzI%PynVRAxc?UM)#YmuCl`uAihr0@0U@=bU^j;h1xykHhq z&$^^^(Ywfn6jjU`*gh-4dO;oliKG^e$A@rJ`y4q+OOeisQ%Pa^6LgzB#O(TcL$K$A zEwEGRXxtWWtxwR}2fI5uB=0ewQor`iypCrA6C?*Xq+Gyvx##F>AEkV7bz-TsK!yS7 z%Kfp6-{pB3UR;1Yu&U@ojmpL?m<5B5goZ#Hk^!@fy6o<!^#2g z4blU8;RdXOlf&O=t+wMO*|#`8b)WEN1nsr@S`X?`nwL30LOgww~77IGl|xn=d(6lhJAz z>^Pd!JR7na>(F1kw^7yl1~cNYRHxBHBg$8;pI4{=q?qw7K$p}*1V(Ww1sMur;sH|W1d@^qGu-q*)mbH%~JC7!nA^KFP~jx z7@q7qiwrUr4M{DB>KpUZY8j;hc3`qq#=VU@R%O!!Z$wLFo>|Vr4VgC*R?zQ24gCl5 zMK&9THH;S42H~lsy!jM+fIkvaiWu=0QJ(n|bT4Va|Irg^Q%D$mUpmp(%Kg=rXx)Z7 z?vE&l-T6T)N$$geC~MCPWQ>vKAT%47RNAwA{9qY>o-`rR&rLe!_go zj+&Ejb?Xut$0E@{IJ3rJH;)7bpoi}8PomxVbe0=>>y@l0w`&!wv&d$j0=Y*q>P7NV zDr`iPOux+$MH+HowK&QMb&Y&Rh8`B^M0SgVP)=mP^#3pIx=}oklgl{MY{*bdA{kM- zAy610ZIUn6b#pZMrt7jqSR)mdlf@{Wsg{r@l3(bpxzBe24dRVh4b#wP@H*_VzMidA zGx>ZOKjY%Se#kB;_?bqGKCgx z3=BS0SL?lk8|e>qAS(;Bo_oe>{j@xm%%Iy~24ofR8}rDp@MO;g({g@^ouz-H4n9UTpDOgYY2T~LBj7Qe^W^xNb!+D|UX9(Zoi=5skjzDTdMD9G-sYWqta zLk7WKP4H|5o%|nF@Qz~B&_lUUptWt&hWRmhcxMqq{&_0>$-bIR(lcp&cXeYLT7?>f zAjKs(L(RffWwx<|i+JO@+DQQ;Cylf~QDxr^2`~kbW|-=E@h!|26xuPENA<|__(6S| zIV)5Eucp1wNUNA0w{D|0ip)d);P(`Fu&F$k3iL(Vi{NYOG$Yj;dRw%_@tFQb=@@z+ zoWpaMC#643bMgApdP|4^pqdayYeigjlYm5M*SOV&QZHs%Gbv+w-UaN{#pI65Jk+G&zd4YAR3qUa6KnQRmW`g8Lh=q>F*h_OF(4re2! z%~!5^mP@y#_TdY_*M1Kbq|RU>Bt&GluyGZG_DZPb+NQMNDe_2C#@$nyOP7N(kMjh@ zCLWaQaJzh;6j3L$UvkB@{}{8#5=d2egBF4ctpWIYfvJ+QFC){KD$gS2;2p>+4vYOD zI%zHO2zzYSH;Vh>_$+kC+#n^gDJW|&tJw`t@MO|i6!}~5XDC_r>u2@$FxF6lx78Q= zaJWIngPLfKE~NFx`F&aa_0#|0Cv23~Nx5y!j@}e z3C2L9g7h2|=-H)vsHFAXHr$-3?Poi@_eg&05?>K42sFkGp`!k=XbRQX59;-gN-KuT z20PQvY=yiW|6VyBCDZln1UBe$vki4>l~7Y0>%D2-aKAyfLz`Jke8AhCZ^Ht3E2FI{ zq^Y$orkYVsx&x%z4erM}2spz^$!Sbu)%lCm2gVcoUVamAM?v_;cJd#m8G%J~I?4!* zu)>4)%}{hvNQXF!rqOxEY~m)VG=$Hk-D3-*Sx7~F(zlbLG{0)PVcr>$m9Mg{F(H`2 z2bkx)&xx0=gS|ji{W?5%11-YeUF}KKhEu8s_}%T)3(5q3LWwnF?Tu;sP&(T#9SQx8 zK5j;uL%5EM(f!7E@cQG_E$ljLiaJ^I^~*d8vO~VI6ZRFbncAwn!^eZBt%A{+SEX}f zN|D3Th@gg4$q$)QH_V!3I+;SIrQfHE&9Z2+y%Ika+$ToS+ptk72-SEW%ur4`*4M`T zrFa9kVb*hrO)T}>&x~u5(@X+4TBbs2J~Ac4g1WtUU<32ZOZkh?hG#qBIx z#$=Z0T@q{<7{&*B2g4-KBtb2L2knD#La?qdJ$8rMo>Y?J=^~*Ckg#&&ob)04Xq$z` zYdU%cYNt2G5q=-l;p@nYP5f6LNAFXfF7W}^{>=|KNrsxe@1Tx zk2_ti+`PPmjF0q+)hpn!o`-(O4ah^Ky-vMD)xV^k1|pseXzZ08gOl`+i-sO=Ydqkwh=UZGte39 zDXl?Appm3E&Eo&*kmU;CLg}SclLqpAzU6#YTs5m8+A4R>xNScUjb|lS-8h23nxiyK zhm$0}j`m>J-T&8l#XzF&1!=bHy(=8%2wu?oCL6uQ2wx+)nvuikM;@{E0j^BeUZHKp zh8o#|b1l(4Vubi!t&1%Cv*KSY)~v_EN!L()wgD0oZZccgb7g=#j8#>?#&$#{LNh|~ zN(TI{+j$4~;Lpy}&yKin=o z!PAQtB_?ag?mFY?Ow=ZIKe$x~$h%{8X3$FHtDeO2xi0uxI`T==O2%t~{5o}fN|b3w z-^>>FHEa{OV$#|An3oKpIi#~uRY(S%_fR|;wWZU6=-$v6$qM>|bfI;W&m!lwWi$u7 zYb^FIl52)+Mk3pwjW=(Y&#k7uE@(ce*G7a;`a^R#bR!d_;o1ZgPAZs#*y5NYbSS-v z`i9bIujoRot?MXw3|f&sJX4=8b$}<`3;r!sSh3@S)ii}{!268SFk!s!`QX=V8l7kD zqN&bRyd-T(qm*EYtfX1OSW*ln#(-xTuW+A?4N4iV<^F3*6x&D6qT{-tl~rB@^O1P) zqzzVn*m9D1sKnctdxWL98~-PC7DbXt`YO?5UXcT66Wf|rK>e;i4=rH5*)dwuH(iZH z1K9V{)oDKK244_*#99Xpc`Ln7Ux#J{AF}1N6|G}`ZjRJjW>hDetz@aG@|qVzL*312 z0j8Oc<$I9VJH(h7C_*A|N67JP>pmRa%qyT@IFZeUv#hZAm;5-q8#p#=TvqIhl^UVtCKC;2<4eyJt!6tia_*$yl zrkgi;Eb!S*f`afKt_X~kt}YqaQ;)>!VtRobQf@SXY>BIE*W;dsz~h(EFHXg$w9jZp zMgm{OI|eJDpVDTujFzGQ!87a0C+mw{J)kekB%e%+ZwT1bSIP)qLC7VYj0eD+ISudU z<7rjxy)l5+Gm9cedM&aQ-Q^jj=iw4^SKKqOk6Z|TL-F*ian&&zx3Z2Vo+O@xuO1J4 zt5<0uYpOh$%nY2x8_^rBhO>%Q$4s+7h1{?lth+iutg8ExLPotnb{o*P`7@aI&k5|1 zbHHA>EP04d;p?n3lJ)UiLoU#k)g(oY+Nh!>rECvPBG*uopXoV`fU%ix5(mHxrwm_Z z%J45H@?PKyRb97jN#Wz@uHa}$RJ%bZ+IU9V&$7Bg zG2Yfb9^PJOWM`=k8p7<69a(DV#OgQbbg)*mkCalLDSJtA4vG?d7@luD(0-O%C+Q5c zmb@C(Lc8z+Rtz{;HKZ*u?YL!LqeEkR2PzoFrAM|uf@sy`>1E9weCMwx|^xRM@6 z^Q{!|c6(XFfv1qk^hWG|Rxa8pwK06!ZA{3A6xygfNp#=&!}irX))XWQM) zta{XTdqu9rsX8AgQLirl;j6i?lkBoRWeXTTPYs!7QW;*~x2Y z``A>i0qm@wk?(Y#xu5#|H?1FZGx&aUu$#gsTq$td-ifWy+v9K)Zgn!ttjtN%c@muu zoT&O_2_0?RbWD=cg2cGO$A*?>Z(UdkJS&EhnGHh8R;INg|8(Se_r+kP^)=(a$<0A}D#~ z59m0CPh4*88^?RWp0o#YsJ9Wt<7qZ|jPgwjW&bI4P#wx|WrNv{4k9k`Ih&xpPk(1o zV-vY1e^DxF)gAjZ?(1iLW{JRTSZojQn>?8oMkUEr&*VTM=A~tXQ+gvZD^MD!zK@jS zqyx^){!`<*5#5*8k|RU&NEX`(+*53<&Pem>g{{k=puO&DO8T%fN&&SvIQIv5KR|6- znhoHC#9eZjxteyQ*KjI*0M+y}?>)+Ohnz3)lE0&8lz0*nPm%4yN|Og}v5vU7xkR4~ z6jLmxXh9(xObz6uxoDl-3G&pg23A=EjC1M`ZG?V}Ens727w3k5X)udAT&X3OB8&B0 zscrPu$~#j=w@PoGsF2}?EV4LIN5^R+0wZFp@;2-# zpTVxtTw-J6Ul-*ojYiTGZJM$c-*9fEUGY2rPI?sT>Bjo3sHBXxs2(XM*N~>rK5t7^|lW>&>&;eDk3(oNi~aTqAwa zBIQ5#0pY89#(1Zl!+H59xxTwwfx`xJ-A9&^)p#ufDHKaaKVX_2N2y$=#c3J;EGdh$ zU8!26r7_6$1K!ymQgvetI!>O`)*|O=I1s#V4Do+SI;CX>-=MbShGNqczK>OL9n@RP zcKVF{?Rv?cgI1tDEoz;i-&endY$!FX2kA!TRpSiVNsv&S6~f=6 z%SLvKMP+jOz$8fi_5(vfF)r#U0a76(w7^u&y=U%w2R)Y=kchD<*z%HAmx8bCdDpqk4x9 z<11KY6f$-|KG5CRgy=3nZXWH+uEnASN=C%r7%1^FA(v1*9>rSn$M%__Anw5y>%D+b zf0kWM6>)CU^v$D5>rT>W|4mlj7>G;w->TqDQ)E&pVs4?A>378S&bE-%RK)mq>>lLHqlvpb;NIK9acSMKC+>0u3Y9HTW?=C{Uje@4Av9=lEd}}$Y+kZfv$n<;T+TR`=ul$S-S&F4Cd~<(nBvxU zHj_4WcMpxW7`dvwF-8OeBv;9MIKFt9V7wY|ZMN-=8<+80%%c1Q{^a&R$aea(C}&w^ zxx?Du_A>Oi{Q{0-+e7or9dv78J#39ypsf6=RTs&|b;Y9Bd6>1@=%_8{|L}>*cy(Fx^DoldvF_v?)tTu! z8>r(?6fdzYw3OJH{s{E9xU|UE7&o+RG+7?vdZuE(%W?rvnFG^cM`9y&G(Qvi>^Jat zG@9=T{FFs&xUC?uaA9pUx=x%z2lhkTLCg47DD_#(m^dI6_7%T-rx{Zfj7vsVW7ic= z>OteSD?*QFO$-f}RS)=rv>lye4Gvb1TSxzqyDC>n-pIk^6@Tn&WIst7iWAjU&gRw} zxftVm8+lH2P5&;woxG-<18MYxSya7YwL*tc5z<6?E5*_`c%QP?or&E1H2ldkbvJig z+ZDxnjjQ80WXEG*3;Y3x(erYC`dcaDyF%tVZ-Wo8m3c9$u7#CMAj4M`>C?D>E3M#qrQE9j5gKsJu8HmFtC^s(R@~8>B zOT(-!^bX`TcMtB?q9C!mF}kUs((eNm?K8!=r0nc4siXX%BiLZPONm8Kw5CcicR1?+ z(~mqr5m`g?Bz48btv5oHYFE2}uc9pasfAgk*h9P`@X;tM#RV(+LqPVM7@QMbjl6L^ z(WVNsfw(YU*+IUmIY|$`9;nCDVRm;-Te|jus!J1v>heV2UL2C=lV;X&PZv+B0;x={ zNyM~|*Y?BRy+uAFrXgTYTHnoPv?P#Pt6H;yXEHXU%49Q;P9|#E49af!YuP&}r5I5h z)){LPu7Y!hcG8a#kHfE!JhWxxp-@G2JL?W^;1t(QaO~EK47!UeBQ$VV@WoOi&+A}G zIKRWwK1nlx?#Jbw^dI+RC66bUS1Lt4-g5dOhl|}nx22UzV{?=5ySN~ufYQY(S?XTNYVsUO;7f3^xP_!uMkUgkpX2Y? zIpYQG4m8o*p-A<)K-|}gK`u9H!*XehaXR9xr?a29L@tE(Tg&M;+?G@mYb(nFHQ6)A z#n4T;Da;~wllrig&o7-riFV+ zs4>A$Yw_%%+iVtHp}unx!)t6(@{lLu8GUNPL12aSkjvmcLB*ak;u`8o+IdT$VMY@) zjVvSbs&nQO^wz56d|)~7Z(|zi&d*_3vZz~?hT45#mu(7iZ4(<880PLBwY11cd>Eem zu}x?XZLK{=Xr@s) zD2oRT>bymB;-~V^B!6g~)lcb$$`-Ex74}iJOZLjNn-N1=th<(GvRHj2d5aY7xmKMo zM|W8gE{~Q3rKz{WTYwals`?`Vbyn7k9DN9&W^)_ju50dxO(#jEhNLj9}mWn7@fCDf2kK-k1 z75ZYk?#wV|N=bR2;-j<{N)z@f>8?}QjW3XQMiuc3JLISn(=cuzIBu$vAAy(a!=3TP z=bH`F=D}XD9^uZzypd8i%L1!|;17)94rwRPwXUSOo90DTAcf^CtpT-jNqK^Io}R^M z7Rs(ywFW5xYadz3v!Mw}dsjGZk5}RU6IxcmZ?J+eRE1m#T#<{(7Ylwidjh?R@ZmC3 zb9f}!rncgfd`(!4c~BwXNFL2Th-SEFNRx2vrsxCHW z&?c;~J2UMb`X%M%`GHtjK|Ko^xiL7=Pn>P(ne-?!%3n$UfL7~E$R0!j6Qr4J6dj`$ z@eQPZO%4i+; zoH2E!`G$_BRc&)TTf_+EpgpgaYt1M&mLs7>scl9Q~_Nv3$l@##SWu-*prq4UF&0CJS_-h>u&hH<&^Kp zwMb?3SjmI8MPyn_&>^${Wv3^^tSl>StvB^Xm%3s;g1ye(n7rDy(ESpL1kOMb7JEW3 zh#KZa{u6;Fw0}x%>y{Fr8^p=_UFQYowa9Ri%lkvU?_ZdL*&)(cX$wTl?chQCiKnBz zD2KO(!@y|)LmvUR{tkNCHx>;SYXQquAk9>0WUe_7LMd*;*{r&J{C)#|vV^$HaU%`(>G!7Mr8##zntSSRcKH}E{(De#c@ zPnisqmbUC7J&)?+#Ncz%rpQ&Hfx8p+=}ATr|1_p?54%qG`Th$)ZoCEgPqqwg0O)G+ z2^4r=eOc#V5?-N|V^8>2lJ4wl4WU&*hZ~R9quMMvu^@>A^=5>7wr8~JHr+sRNi;^A zBJ5zkYLfo9On7@9hs lw>Q1=PRT$a>q?MtI;mTZv;bcT%&z=%_V3S=nXTW{`f%0 z@*A|Mt6|}>+9J{pJrFzKt@>kMB292s$HP!d#4-*dOSnu&L-yiBvc^_V%bwOaE?q87 zAJdZbzIUXvx965s(j~#x>9V)II*txX`6)fb|3rfy&y1Ho`-s#MrXSboMDzlxq&TUG zJ{z0TEpRS8B|4cOzKy=Xmr*bIZ*y_VE!F_lbj2Ds!)|B5xtMaS_&1+Hm6eWaq*_Zh5DE$(;@HN3$TtIEE{m}-h zU9}_{uVwSb@effRP(A+l|%kd z+6`fbywo_U&ULlQu95LP#`wm3t_45_c(2V;O5&;BHjbS}7B+#R;2Jy~9G0Nfgbw6_ zaMkhC04*W-vSy+(&P!HX{L3o3#h%fx{!FPMBwPJg>_R|NX30k-yL!=TYu}z;nQY}i-~->sxi!!% zL*<$ywjO$Kj(}a!GhWd973#6IBVN;W;!7Gn5(|4 z1(_y@@)&KlDmb@T2i3os!@EGb2rRz!NcHAtlkL^DniR4Itv0ACt?fwyN9;)UO6WtT z+KO60mXCf!N(aX}M(~zAS-fLgDz{Z^TEweq;O%- zNoG@0J;&g?|Yy%uOR=FPXQZc5&6!J`xMbz@81 zY^4D^W4r}LZ6ZlQ?@HI!%klN4yWkgT4zdZ@Q87Y&oL#Tuc^z8Mud;7ybv9X#Qg`sm z{9j44a(YkFvC)|jt3eMB4%wV}((Gl&;*G%$G(mpdM zJ_?)YE~y`3K5{y_4bTbZlEjFwp^{MlM+QIOx{)q6)&=uEJjAM^1bHVs9a3Qzx@zEN zko~?SeH__utVV?bB6#aB(ofoXv5NnhoBP+s)nMh~4lC^xXcI}{;>YxbvR(GE`&0`a z)Yrs@$@h#~>R-5>T8dRehhY+4g#C8BO{%G;Xs4K)QKXG?Tt16x)KSE)t6O0`6saaL4Yt5C1q3Lu9OdZ;(AkzcI zeGhXR5DpRhDm_M(%oMtel;Q3ygRI}iN^o%ev6j&8)_PV(eVJGZPhwsD=j1KoVBg(P zB`rxwgyidkLRX_LOE$8pJ3+}-++W6;prX{;#$V882`W&P4)h z3`)RXP+!uO%?u@y*yR0dB=I~yW7bnsA?K}+*`EBt{po2ooLq&I>~rX3Fv8gtb&-mp zBYXmB%Wtv+JK4jeNIyGawxY7L2Ynd))+I@m1W9M@Qa@V+gQV+C+E5Uy_fYW#-?sFxjN$^*wa9Qa`h4 zJcSjPK2V+JlpnJ@!p60a`P$%qEmEk7|Fa&D|HwTOM$V`CAt7@q&2Nmr!|AC2MZ^kN z2f>|CO1>CeMT_a9=vZzP4Fc1}gN zeSNe)X6`~6lxn4wg=S9t4_`^ny0YO{YKS~{hr7a9W7N!j&HvCBiIL~8P+fVIJ~wWT z|D!UGR@Z;HdxApZrdiW-5+6l3)MudkK12Ly6fNhhCT*9;f?l{B(6p#~tna0|)7DuX zXeI-XAv>>TWUz+fF17;~)W)ju`h=wQ##%Pr_*dRemWP_gt_g&D%UR>K{=9)FH#&nS zrVS_SbeHoh%^_cMrPHbWoU52RBDR&;$!=*YSQ}%$8AGedla2erYP{9TVReIC;NfCc z6o%fR1?CpgiA(@q_W^MAFXV%*{pb|z3BKwMsYOI>vMar&{F3SXxiCc37p{OztGWp&L@V`(KUE@gf+S;z-zU|;7X=JjHWli#DsxD`3r9M7HRHYiT8_)gu zSjc_aYRn_hH*u-7i%#G*&5?XK)H*rjceWo^8zq7C4jXDl7#CbU3~=QI_c^=%8? zt?mV8O)g2$_UMn+HX}o&H>esdp?CN9jhyPbrKq;J;82a(OOSf{@AMC95lATNrJtZB zQ?io&F!kB07FYJL?mm^ip`H2p*z2eu--~v{xi;nVD=V-}88bs`9# zq*YMi?KHE(o-Ye(sEwsN?AzgH4_b%iv%CTQuCC@gt?ur>dAj17qCsO1OpVtYpLasM;Vacwre z;!aYl>yMQ7!f^f(chjn&DeREE0>7b+trbxnMYmW;=&=42t%~DAQQ$b5fdq8gcw;V- zE0cM=n;Hirg%7C&YQgn%lT}#U;qPTD@86@$wN=t4lEG{SaQMEns_eJ^Np~A(Q8dNw z{`ikkf(K!P@hf(Sf(!kV zu@MLwMZkqMA<#%YDvy<7grV${+%i-&rmLQ!ACLR!&&;StV(2g9U-u$aL0z4L;@qJ4 zbY@I*MOi=HbwIlsPl4M_bAo>&VCx&YVtw#m4pml|8HHd5WEr?Cxl3mxt-;&KYsJnq z^$*m+b-7~3E0x(__<>PI&#RVCoeh(?-ewQw4tORy>mQ9n9+#eopVM=}a6J=uBL}W`jP=az3nf42o7_HtMTSVw3;>Nhrl_tDv%FtjaE z&kOoTe`7utTe1W#oh)${qUA{+dd_s43+27?Q1m#^B%`)}dH6Sl`Cg}eQySym=roJS zs1$4Q8cpt;iXmx?I z6DDYfNH>akDtSece0}Kh;8A~URC{(>>Qf>wYXLN^!)yz)h{bM}x}sGyy0BC}OYYC> z;;PP>M2mU4IJdEbDL^qA8X9V@(<*CCErrej$MzcWDALt>WC|N-mX#XQ$>7jE<(?B* z5O<#S#ra528p)n0F4Br@qZ`m!ZAfZW+g1`0m`7TYNUPSGy7Xj;y0LZXPL>qY+0(>R zgck)3_Oj4ghe=-fvxFLGy(#oq=o=^`%95pykG9_An?E&tL5-9`>S({ByDE0RnssHn^*uP0qHDk9;eofN!_1-{_f-rAwQBSsXe=i|7Sa?T zLWYX(BY)eHSo5^Pt`zXFRs`buYu}0BpukC#lU-1oqpR$tb%Fp(*T@^1Y?jfNlVBi? zCCZt&3u#*V1Z(Cj!XMds+8?3Wdxk0X zGTsXLlzOQ*_1s#yVE>R^eH1YjPNwG8d*i41M_WSg(O23p`I-5z!xb1qdV33KKLQc_ znq1xZ=}mHWBe~#vbGC$>`y zrO&_@OD_hZXd7*#v!nlzxf1tPhLgXok+>-$Y$N196p;n=RjDsVu->6CG{p4N-s&UQ z0k)pkfCS&K)jsKp@MoS4_obgaCM?g;8 zJ0Q}yQ7LvA$CL9wG4;vUe5I9(?2j=g2%KdsVU?lh>xyo%@6>V@ zLz|5nR=8vEjf zBvKO2p$THLrx;sjEDDU|xv4BoQ|*C&VH4LA4Oa`r22pR`54Yw5kTgr7n`WAEmEBMu z%lX;e65sTDY=+)R$m_>ySM*#xh|<;HfjY9+kmWZ5ev%t>^77&BC2n zD;;x_w%}CAsqdGkJFf>TS(`(TN?h_Z5{u%z=wGo9s-k#=bR#>R*@oVrdLP50^4I-ui$IXE;R` z-ABUK=)|>2KZ)I_KTzU};h3mfuHE8EwL9^`leWi=7K`!nIJc*7#%VN2WmaiO8(0v$ z!cNn3G8JZcy%D+8y|zut5p>Kw*?^n?ZLQG^$tmMdEi09LkYsB3BS-;S&apD}5zXKq zjGEA0kF_50wZ1Z>E1sHGL200Bfj0D=@t$4LT0~4ixr91IG>?TkflBYR+y~s0Yw0%n zOMNblRQ?5J`e9yP9aK66EheHqP^|#_n$N5N?PR~E)I+V9XaXl_w!%(I<&`#M6L1A8 z>yrpb!}L|sYPqd9Ma~r&rgL_d^cA+)o_pStq<4m(>?7PrknnKwB8iBLh^P?s`PUgq)g*dVbZm3CTSomac%oJx{G(+ytX2vE=`g>V z3wM<-vEMFDdnRr%2GTV#FZ8`e6M7&x6hFlal)kVJu0vahH_QT_bTU{fjGuxoifF4s z`CPBPYsnRMnXi+Z(2C4wj7Q~ZJL}ShIygOMD?UUeo{smeo*YP*3wTRPjrd7pi?$Hm zV7hj~I>N63clL60gd7bUjdHXB!%?|#Svik2+mu2L-M>BaInq6`lURnU3>p|+f&Yz) zhZk43v57buR9hdd@0OjU(>3yzHJ#{HbuN1l8!61tnq}mI_ozk2X8kF;XVdY{jEVlR z414G#DIcgnDXyp{TF=25i@ zZiYFTCQXr@a!I+IKMy(sop%+V;Qs6nlA6FDdx}@sz1S(-l}DAzt<+=;zDfT?i5}wV zOR5CE2R_NqSawL#dWdz@D>^H99qZE1j#=VHyw@6n^T==I2677}kzWupS@R&$hm&3W zDXT~e8xx@O0>%yrlEy3;oWW)oYtoA$51AqE5Gnv^rmUre+R94?e(`m3i7w5|z=)2oD^!zs41na{hU7Gk~JnSlA^Lqleib zvxRk+-?d(ewdqOa3UGrDSOt`$=1i~Yc_)weT}HLM(MkhrX8Jv}5A{$xYoDz&;-a;H z@be(G4s*k1qYANH_h}{b7(FS>2ER=c=vr^5uAxoPv0y*e$#GiTTWnG2VK5RGChg6a z`W#Rj^h6!VWBxbu@hdig{)aaPkFq^T!Kb9X!cCV;GN)o~^s%g3yr+&J*vxalY}|SNVYFq#h8S!;GZ2eK90|EnE9k{gk}Rbz6Rd|22vOPdANg?2ph- znM(dvS8F$DQ|m5n#z&JFcPs0DsFvB5)&|0iLyFZ~(SsyXlSAE%33w%X4t-5YpiXtv zYgms-jdjbyBaBrffNRlAy*yFYP$ir}3kh?179&pT%_foT!k$o!`Z$=A?RDj5KeaWakUCd8F7@#> zk!xB-ozpCux)4=MjZ?;06`)3g_b{!$wuU{Y{`8qFhq+Hm7GHb1@~iR-`V1j<7A>~b zGCXP_BS}Q4hP9Np;hTtL62wXBJB7x!Ve@EJ4Q7^@$K*=$4UA4taS844<4 zW<$Qizk`P3jsAIRZ0bW_L1h5W#%l%^hQjb6v%5S`)bzvlCUMJXIc*CQHP}Ct>IEeH zlhO0|7g9Z#E4CGW5%9VsZK1g_{IFgEpD)_Z*I3-9t|a;3OjrWUktr&or!7TTp+@0Y zX_BwG&*IOM|H!XUZ_jJJ4<2bO!K>D;b@d6HLQ8u&4LM7Lf$#$xp9nzi=$mnwP`44bberWxcpVP zMDiP!=Zv&m7KN>z$NEVrf+wr5z-{>$jo@X2vTG~sTbZ`N8eyMr-$Ksn6>%|bleNop z*s7~67vqc&(AAqX`zj13n?PrvgQR| z>NiMdzKkZVj*sb;@>O0Q>;SCfA^c9jF$<;KphqiZbb2SGQ z;(Pxz`5(Eubv@JwI2{E^lB2hAS^i5;VMokDXgV39Rx?l0&vd+0P~M|-aV`!`QUtRg zP7wDAZ9L=Xk${)l&~(;N$?9K;D-p@;VqD(P&x|6s5Ouvu?~5i0IgD}S6nbmK2kp4I zd6@0@KZ9+|WNRfI#VSN~BTYz>URjqN!-Do(8k=Syu`^y}7|KAHFw~(V^>gfm>}C$9tak&2&<2`K2_pk}7V(%-%s$H4 zrfdPlJ*9U-1IcQVpyXx8lxk=Ws20Nwk87T>h$`eTSjU&Et(DG3U$Ps$P~x@SEN|L= zd^5QuEk}kL|FI;wJou{X>+xua^+4T@cL}Ba&(->A-BWwu>$C{lYfZ_BLCerzabI~1 zIu;i&#&}kv-k?W#t1RZVk_Yh;+C#k-TEJeaD`Njr@&s?Vyi$;Tr(>bZGkjTe>G^elF8;;*qA5Kn#Kh_RBLI7#%ZY{ z=J+4I39*9f^$)sC9IoeBXX3-c3E-^lrWO2P#23(*e{sSdz(j7U;*- zl>^Gq^v&V_IpTL(C9gqSw+gG}M=h+X}Yd!(j3mtN!+v5iJon`v zU4%YC$9!=tfVvQX{PtY7-F8i!N?wV1St?%O>p^S#zv8-lrFqs<|^J9&D9Cet$L5IMJQL*^iHH19;_nWOwA&{#a%byWFB{KL2nthH=_v~ zO|OB*Q3P3>E^h&J0^inF;Czq+IL!-M=A$N=rR6=^G+ITw8Iy+llCk_e=t-BLQFj&W zau)Ieh7`CLd}28uPJk>1w)NMoJP|a{K4f;(fB2k!NBL!&U@Kt><@HG=*BmlZ$};Qf z)%j4PE*9i3w&CW{oWj6ftfkjN)chfa0BijY__@1DtxzkW3G$;I#%fK+GjSc)PaqFe z5ALHb|6Jn)zfYfe5^+$QNUJIREt^dTyNMUEDfB4B?C zW$@O;iPge_S{hqVJieuoPhxZMD)li+)S}$KT=no)QiE1jC*hLj8OR(KF?;ZZ#w6_z z-dTG}dqa!Z>G;(!)3l(yoq3`*2Ik?K;8-iFy*{kWM6QiWjRmw`5G`DbnY=X3aHxjeuurlSe`^t}uT4+Y*SMYeewQr(t!SVJUm&FAPzu}RNk}Jk! zaSz-$M3#agT^8CwsjEMLymVny5q6Xdl(LpGLILM;w!ulPBiA32uKPiPmZf7;u2=6TO1|DT@rH6ypfJNPiop!#`BXv97A9oa2Qu14E zW=U2Hy^&hj_xYwF6}&B#tS!`|js-2edr2!M%iOmU6VdZxIlB z=c6FqCj1t6u{UJ37C@zf{es2$9mnBdFV_yVOjPP=p$Rx=81ZX9CSmL=i@#z+W~~NfNSA zro%!;DsvSz{sKP2PSH~Q4e2ar($-W_rnx_PO@3NjA8`n+MNa=EvR-T){Z9J9qU3sP zINWCMDjCXHyck!bOX(oBrgDzFC|&rZ|2Xf$!7y3tD%9PGoZ zn!9j6IRh8+^b-4N9n~~o)zuB`6L**^aFU$Io=3kx4v?+-Tw!-$Ji4chmEU78n~z$F zzu>MoNh;vZH7~$EECn2>)wKbco0$VGCg=4g%r11)y6Ye44U&glLx0klgRX^9!`hzW~Rcf$5!{$5PB<{$u()SbDvx$PUmAK!oP_Gx5tAcve-u1 zsyLLn>^*5>oC;PzPe^C`-puy0+ni4-x?Y*(gx@f$cG12`^YuDxEnRDt!HrQd<(U4^ z-&8+oHigd8FXA*Yflal);G?wN>NRi>T-JUW_ubjz06Y)4g4ku!QpRyLMbD3h1U?0C z8q?T2s{y^&_t*f?ec}Uc=uU{Gw;oFKiFL zrWEq&{!~R^ZuvbLlwF7Y@_jYtvevQ!>K#lD;>pwqG#4$_DhEf?u4J!iLv?bh2gCK9 zT*AeJJ9!7Y!^pLbWvl2{Uu!-$tEagoxPp!f^s&6+ls91yw68dh1+}))D5Hiz#m6j$ z?KPnBgyl6mIu3e2de_jPK7tz>%oUnHn1Rw*PjNgQ$mX-o61cC``{KVq()y*v((?YI zbb}GjmXkJNA6)l&8+kP_ja;-9FK!NI=PWl^@svz7Xww%xQLkbAr`4m6vK#R^ws2@Q zeok`eN72Bq^cLuiQC%HQT8lf~t+ZaiI{(xB>N|m>%yXbE-lGp`Gv`OKt;Mv3r`nvg z=|$@_+ckX=I**0{EzQh8`eyU71p4B?b$u7#b$J_;~a@wl_Gy zHbYZc2JK-!qm}e$?2NJ$x6oJGYr+5T3Vo|g$Gym6V6~McZ%U8UO9NS>zt+c0;9=+) zDG3ueG_#_zWE|Sd+Di9~^KvzEE?5@blU|W{#ibPoVlxwd~;@hlWq{hZ`TUIy+z3`V>991_in4wxZenIUZ9dmp}KYY7m+TqDGSNFI- zTi%&F*dXvX-9ls07y4a039WSv^!dVkr3j1Uw}Dgm4MhaD1h49|a8pQsJm*33jb6ny z=}Ourrvr}>E5&!AC;2b&1>H{ziR*)h9Q&2ctd^X?i<1fXg}ydxw~=N}&||>;QHt%* z*Eqh}pVnJXTCv)}DEbR+kiN3R z+6Yfqx-76#SxU0mB)yyaFfA|L6v{c;a1S_3D~snmO?XjqFuJ|c-O|{*mk(td6+zO7 z#mK8~4)o!P{^Ige$VhC$=Pa!Y){sw-mlDFCD=aq4Xf=J4^@*$vYcG5ft3>JILIsI) zmG@|8(z}RE)LDOoR;mYl6mnXH$twLH=g{D5`L+MC5SAwD4*yi>KWe0ZLz}|es=K6x z@!kB;Kdy(00*UgyrxS8csU5_S;1B6EYoIH}8PYdnP)Zq~ynI#P(uuy&=)By__u2W~ zRD>hG40#4>%ZkenNKYfupXx}VQ}FHR(KN~bm|Pcsfd73TdKkQidO|X3C_CZWL$=_f zf(86hEFu#=CGGiTy@!B7C84tJR~GEHvY9Vp6X|pGh-{>*X>DhOZ>!vmVy6mhktxBpaNj=VxdL~k{iKP$8;!H| zb9~3gVj|cVd{Xw7Zby4)L-|_1fu4Z`a(%4~e}s})akFe;0tIwD07%tDz*ok`x2^oo)bd}5RpCZkqps&A7^^jEeQ7dq#JN!^0G z#pS@It_7Wl$ynB=;L-7K_*q~K|B@a^9mrZ6gv&;{HiCVI~vytmc5}%ngSd{L;Wz=EHQOA3ym7T_U=y6xNT*O*Z z8gH8b-hi&mFMSR;c#GAc>`ykGW+_odSM#Q$6gfod6sQM&yF^?Zm9dZJsf=YTcAblh z;D&3eV>x!=o|1^7#Js$%7S79(<=AKWB4nDH*a;eSCpe0WFL-wjHy2lXl4AHsVR@4A zgzS-3V?55M7m|i%{WfFeLzAQKdDA|2cV@QM~DLq`N zBbSij?QE{3=YrL_)8R!; zf^qUJ>ln-J?C*4*{eili{WKbS#G#-)bJs ztRO@CB|n!@UreFSjJ-f5{L><%R5?kmgA>pYix#}jZiBddA&-Ln0gCbdy__UixxPz#V(Pk@kW(&IZs8e*T{T=n9#!4xDEGnh{&uoX%aWm6mdt*6l&Gwxl zeP}fFE7hSv`9JM9&ZD0(tJwPRC86b_(iE#SS??uL{F6N**IZ7wM^)rMSOx8zd?qCl z9T$_N!0Cj3ccc3d`*tyX>yLwP2V}@IF_w1n}Ib$g+$~UP3 zA!I)(;O;Ev^Np3K02d<~-St~&h{5%d@O~6go6_`*F}RZa7<%d@|3v93F2u3|cCsBE z;j6T#mZDk}GC+AvlC`{!dc^0NAa>)^NfUa|aaehyRe%{}JlSu=1~1XoYE|PgT8gFv z9jdeg-CpT-COXG`@=CPg#%vX15qim=7j9JMB1RGMf!o(#d~WtwMGUy2YdBm75eD4`4gaD zRiy>9Kj2E%lIRQDY@1wO(c_dd;F4PH>&EKHm!Pq8HV`p;qpr>y*~2J`UCNh`?)W-O zS6p1xKkFf=DefPLxrRgBE1LSjDtBo>4{j!oP*`~ z9`T#v6D$LaYh<1W1ngK4M)_8HB%$%zWCL1V6?qZt4WQwyAos>HqphkhrTBvI{> z+!4OnXTb%y$s9{^dBM=~tR{J)A?H$~hdP+-QqG!Z{FSr%O8My_dPmF$9g`PGg1AZ( z{d-Aw=!CqX?g(P<3DK(OYR$nv_}V$?|LJyO(7zveu7#BbK;Y{P1evlq3xu_-EA12v z@I6kqo+#xpPDpcr?J=5`^w)J#INi3hlfkBRA^6s6DXp1{o^Xhv(|8MKvfP$blDeuf zlHI)O%eIwwn8H#tgvCi)StfDwp!64dDlCS~-c?5(p@3~8oJGS;5e*PC*56RuD`TyT z>}~xE*exoGqveh0cvu{M9eAp@4;1yJ%m3nz;tFX^oaw418sa0ZcHp2=NE#@5SYukr z{S%(N&}(3ya!{R_t%!^G0Z@3io2#X4-iO}R?%=uPo=m{GFiOl(-r&AG zL`g7P85_YD`${HkFq2RU=phfu@T}(Oa9Tg@i*qbE@OMeNr3bOejnN^zN*k`Z<3|Su zk-yEhwqqz<9%-L1wZxq=7nU=1QTmbbDXWjX4qwYU3;y8ia{cT@d>Bg$Rt8U5OLog_ zF0KuX!yEZ$oFi^_bjE!lH&=%Y2pqyNOq@D9C8#BlKk z?uAF8!peNx;y6VcN>YLH^&L1>O^s%HrSQ`HrDY{Bx%PmMwmC~Ry5P7>yC%8n(FwE* zb8D@X5$u9Czud@RVe&Qeyt@c;ONo(F%nSa{po}jjiagdBiyrvwXbQ(!=RvXg703a` z#}rbDf&!{_LEFTg`bP2vmBY_7S8L-}=QUky3H_7&c2-ksqtgE7;-AcKFJvA=%eg|D zLbBzm5su2^Pq=Jw40wUATWSU4T`O>yHV#b%U%5(Np=00<=#evwl%UPxAH=4*gYvO# zyO zpEfqZ=4qZ>UpAFS_-SfkmyDei{sP5%80si>X1iEIY}JS$kAl-KF?bCZbDonQz-iDS zz71Oz-%wo0>Z4=k`wCHHkX+x=&OHJZ&&Ezm4Noli<|hrC&wMT*g;X;CK2 zu7-h{9GQk*@rv4gM@3pq8mC#bB_uXusC-Qy?m8YQLpDlh*%osFaCNuI-ED5?GG(na z(x@QbgZ9fhY@K(xG}rTkU6UG-g4!6o&M0M8GXAFkaos=ESCy{>;>+N`pYm9n!s1DJ zm};B(zeo?I=eVW%N>|Jjlwj`97^trBjWN<(dBAJ-ll=_4j+5ce-;tLfHPd<+3GA9y zLTV2Ld`*963&ZVEkJLEqF-MWo;zPdJTw}~sdy^N=4X~e|DAvi`hA$&mU}nx=G{JQY zo~8}aDr<4EOip>o66TV|ypp>y9{?>CbF#(Dg<`l9uX*-Nizhw)BU z$;@^tXgKX2s3t8&nbK;+T<|zlZ=ipJ>zwC>GbMk~WKSktgEQ0jTP!4mZ{ZcmQ2a5w zfwvlGv=iML7^Uwg-HkuxC3rP~yPM50CK;;zc$iy0h`!2CJqu7}=Of2pJT6l(ifTI; z^bxVG$XD)P#OXhjWXv~uD>c^Dkldi>wlEXr4yeBJ1&t>+*c6AAw~uab9-PqI$iM!cae4XDrN4n>7km1YM$Pg_?LHnM=n8M#L6{dqGHCPvV4)^4QS7(RP|& zkB3d;3OyWpL*DRH`Xs-E%1WKQ57Sb^-+C9bs#1~cfzapk9eSes;-KV=&C&|`TcdS6 zo$Ry?Vl$2M=KAzI!YyYsnns3b$AZKB3xR9+2Pp;Il1BcLwhyQPT`X1PU-5nOzW4a z&YGiFOquST}f5>a2-$cJVvP5NV9olO0OmgFAqaH_p1oUkB$ee~ZcB z+?+$YXZNt3;L|EpH4{)Qc-e;suF_MW?2R^EJ&E$SzO0Q=#&ue@{)+zWT{N(rIiMq^_0zqg!+ZBak= z0z80r#|7gxc#9BCkP2u?(7JljdR4yVIYh?sHfT4x%<}>{Ws^kM;oy6v4_j(hQGuK6 zpG79nTi{H1>NzQ!!CWm$*=JsXGkY)|PRg<~B+NP;?rEh=!B|C0nh&+P!PWY5*{xT@ zh&2)q$os{vW?IhWjA-0LRB3;n$$rxDDW8-uai7(xwRApB7x@Qz(Y+hg)?UG)-qT^L z<-2j)lyQY+dP06+t}xcau56_|hEx_R*^8lf@^dk4}l&;e26K zH!@j%R}345dXZoHOtW{yOJlqCh^FavfOqR*?Tvj>HGdU(qHx2XB^ojF&;@)8G}1$6 zKYfVN()y=%h5U~0r-c{9pd|nJ>*n;08iFd%STv!xKNUMzHqDjC@CM@K`jIv!c8NGfJ0QtXdJe8*TR-PcRwf&Ty4kl5=yj-Cy90F*t@J^i!A8OTZCIjTgWo?4RP<(ury z{4MBbJe$YOF9d{E85&POjYxfC z5-yc$H=lrWV})1^HBUakymDWwrkHLiraw2bP=2M0R+mrF=SIHszYwR|Ey4P7uD_;qBm0*yUk~9+gppbz`Xqiy z-DdmXcq4!ZQA|?1>ql4E*ZmM?*axtMkef@UM`N6*s@99{BTLLu^o20RrQjj7CXdk8 zRoKApDe>fC;EOvN+#hMuQO7md#+t!FG#?sbEg-JZDO+Hz9kgo0ApbmAOm^nTJI&Li z8BVABj9mO1&jZDKC{w_n^b~Ff1)U_Ph*FrXfXu^U=H--tN673(RMn}UD++RQ?6*Sq-)V-$cS0YZFqOw4(TLo4k!C~rK&EIwQMDL ziE_CiR%B6`l}#9&!d7R^rmNDX!hHVT7!qdKn54x3xpg`~o`G0uwY zua6>yTsvHggN0p*MoXSeHj~eWA8ukDNNPqQ^E{ryZiMU7LAjv*DOJ|`8($%L7$THJ zW90t4Gku0S#GV4a&?ck4qqR`fn-Lq3T5?e=ZuDS3jONCw=r#PH9u||!9>ad29o11x z@TX%Luz>!RF420py)eM@!U|k!a7CQw&y7W5U!eici(ZqknEpyCD&@;(R`E}vH~D=Q z$uh(O((&wy3Pm4~FXtvY1Ls^p?G>aLuSuPwR`|1Acj-aY$0#VgA!_yp^fRM9pBDFw zg`j9OoXpqvKrTo|U!*pk!6d`>M%`jUt^l=?*3vN&4sdy6@TM<`wbH)Q1!@#869hMt z^cg#i7QU-^l-^8q1P}1`Y`va}e~BvTLREZ%MR8xb30yYHlfQ*xt`RI=+Cf=h0&l2n z@wYZA$&vW1+>326BZNcZLcJ;MFk?k4drn!@Tb|@kF*aE*;&`?|N<^^vhLqw2`WN2k z2xZN*^?0MwM=ePonQD45+wH7}@TVGYNHcvNNY-ZKE@~szpFJV1@hfGu@)sVB%2Fo% zyQv1xi$hUaTmup3Q$CnZh}lmcxw-&TWsZ{Ih_~&Du-V_bN}{LgReTou<|^x##B42{ zb%T6vZDF9A6g?*J7aM>aK|jt5?(PS{y^+P(0pLx2#qAV{Y|j2HtWy@bVvU&&H(Bbz zXqGrb-HWzcWXND2(5B06`U2k!Ib0%r64=`>BrRcCghm`SRUi#hoUA%16HpQqlx%Fs<{0g^m^7s)1#}BGOq3Hy!=c1tj)~$fE!z1;i}NU z`&m0F&IF&s0aVv8ggLHI=V+I#C$SuMB2dpLurYxQCKCexy7~%F4KLe8pE=t2FVNx9 zwRk{zDn|?T=~Cu^b210|mNv-Q+%2yY9}1Zr=DsVe)5#%g)|A3TfXWZcv}g~&97UgnqG3Yy`*s@25Z zj83kv_Q&oCYEZYbgFzc8!5vJu|11z6uNO;zZjr4dIyRQ~gYKoU;+F$P+{LhqyqP0< zZ6BjPt%v?LSDE>pB}kN;`9A3tc=JqES%3;;O|c|D)5fd zJlIT)=N;5D{=8b6BLkR$e~VwhjZ(?Dt}RO2K~4uZFwXj+si1?$`cdGVnFtQ`mEI5P zX2YtLketBTswpqU?B zLZ{JL^Q(r`EBKR=zoyiDN$9XLFqT$6=_+0TbyGq95PDUDT#Y>%;#tW7#L00kuhkxmQKDIA+6mWE$_YdH2M1Da_>#5F zXq#L@&*D$KZPkrr3GOb;Q+lCNT3b0Te1SflE#bMcGh&zZkT6h>lfx}B%6Mqiea`PB zLIZ$QTYrZqvErnTy)`}SS)eTC`}0-RS{Y}vCKy)irXI7ccRO1j%)^VK-H`O`%HCjx zAF)gNOzP5_%a?hEM)-1C3;m8-r-x}H(E)D}Aq$s=PllnSBHWZT=GC@}WobHibZ4XEYno#~zq$g-o^H&yIs=={LS>UtB;QP>AvzdH3ShqsZJHuRxi)bl$Ij%?qyax!U zb3H4`GEnJ{=%3X7!l*#dY%h#P56Lt8YWo=bAV-35gZI}CqkQCx8RW0sR1WiONSX`V zo%4=NsZ~y8yq6wDZbSvxRece>1~sJ9`XBaX#3?qy z9Z^r)GJeP0797sMd%&3}uH{wMM=4*-Ck{>7$<~lJVjpECnaRCqrd(V(RleEAwPYNb zjcf9s+8RK8$^o2YtvJe8vCfb!ZKKK%54<@OsJwd=3&DL(mzxL41+= z#rQL0y*e&jP7gD2^qIJ%NI|oW{$`8V()l0Z8Gibh~>NS z5?cxV7~W2D@pYIIJlZDXZ1|HnS6U}yZuUczPtSvw>y_vxZ6^pwbSR45ZadXcQjB22q(y))a^=hJPBOi+vx*Cmur%?D4X3*x@BAnJj5?p z6JTvu*NOp=eTfk#gs9Zf$Xnl2g#o*iHz(E5M5Q&Is~5qPzRgNDbMRPTN{q5!!P^{V z$dK4O!PDSU*+UXB7Mi#QpwE^+cuo3jMH2bL8E>9;{7>y}^s=dFJ>6}tWVbBE$r?lC zMfjYY12X7S|pH z>8vH4twjj+cqLyLiih5zAZ$Y6S~npuIB(f;BIN)hLE zEk7z{EMVJEORXUT_L;ujEN(j{UPHxUKAq{f9UjS_z$XB@KzT7%M#*qABgkKpb$8W< zRCoz-Br6S^xdNaVEu@ky4rj+zKznUf_ysb^F+?g)+DI>P58lhN%+o9|MocuiZTM{& zjf?YCa~7&2QaVd+;Cp3;;(F3KPjga?JQiD{gJ`1O!7|iQlvE*oeTa7z3h^D8>B0PL zPV9Kz+h2*~UssA`N>ino#%OR9uF|#x10g{l9@U$a!JUA@RExaDNAVhILfq1nvE+t~ zeIvjfd4Sb|8*VHesMXf%$d_$HNn~h2AhZ9&BE{|SxxABkS8Oe3>V4QqEt{Swd!J2n z96&$y1m`cMmOch}gnpN`cusxvr z7~C#!f1wukx^nps=|f6sYlC1bXE<5z+O1_ny4)x4bR@9$a#PgBS5ev`=kO!?wE(dA z$a&WY+Y0?M-5Xc}`^WtH2jJQ~l4_>>&5jCLarwyy*5C3}S`aXUE%_XtKP!*+NGmAx zV#`=#(in8nMz}0W&+eiO5eJt2n10Yx0bCh#oFnOE?LXR8tA&~=FKIF0%EhETGNu7< zq?_>^f0VZHCgfkGEqaAMt4^4#g7T6;3$bzfV`#XaLh}MMWw-VN+tF?Pwc6a?pVS}< z-U!Zu9-88-muw&tv@yxdpg+a`LH@NSDI)K(rpj@W8`_x%Kwr-Plqc+wru%1!MZxWo z7y0mc^E6MD#^CWe7Dsfj38*J=+be$)m=x@H2J(t6A52+H#%~+#Z zBD2(&(PzwUY^L}N$3)AHR*-+Vn6=nAmQ^ijfVSGD*?XhWxCL6_zbxI$xGE*m1#o^% z%g7G=viPWj6qRo|=Beva*06i}LjPD?0ENKxz6Q0WrQCT)dp?ktWOJi8(wpRfc?j=i z*lfX%xt8$+a?bl5{@obd(v_D^6F<5B#bJ6z>UFFO)o4HJWiNmT^~&>~xQ2w8Qg$n3 z4MfRrm61G7E$cX9hVm}@I`fS_hCZRU^!;)}U{|NO4`?%;Ww=JBlcVZn-W?S6t@tDm zP#t)MS{pWr`2riza@%=ZhM>jwjQ@%&K_|gF88V=O)wYT5QelD6c}nfH&7rO3KAK4< zV=5KOrsTPP6fu@3@(A1k^WzihVaHVE0sZJGjhfJI&PLu;OGo%A+VBRrgqjS$$3mmN z9u3qFW|U=-R5hCGgcg^oJN#xs_F$r%}?l%~y>7kdHhI#^`lrOb+5L^fB}bHKqm%!!I?vc$A0WzaU#X(R`rg zN{7iV<5)~7%N0~s&<%^}JIo5Ga3~G4< zY!1Yk##({cL!=#2LsH8&QvCtvbp&0`=drK!gTEI^1g}Pa3eHkpb&l5en<|jZ<{0mx z_wae}B$-4$!!!1PGbFT){s>1Jo6Rk#zWagJ7te6TF(>Tfze1b(FxP*m5e--R!%gOp z^^B`DJ%Go#N;yN+_xe1e8Leyd;5*%mF-DJ>gXc9%1^T;=koa=R;E}4x#wmNDU+$nZ zFfb!~8R`n%z5}5RSHUNg!@_&@1D&tzHD4;j(QbIJbEPZ%78m$x)(*wc#)RcmU02H5 z6(8pHq@MHvjzx#rFGRJtELxJVo_yaw>5W7Fo$^ugCcNRSSw_x?>z5qzi2*s71}`mmR{3#o2pTdzAOE` zvkT3~ZlrZ&0|~ec%}p`+H4EmfGv3A8(_Z$E@^HP578^X^D(M;n$`W+dgDMb37nDz+ zD$ORv;wCz#5-zty4&8vxHH~Zy6laLlch%&Blw@muaRjPh6sG@U<>>$H`CQ|nukxty zPx+EKlsvXg*Sq=hp<%JFX<^%+&c(riv^=@6YY*Rqp2K}-Lf~nIDE(A)3E8DN;lBZ$D+>kAg)J-(P~0%HW*xcvbKUx zaM9TQJi?dPwNlColnd_lKDN$Qw(GY%362sWm$aUxR#bzD~WT|Db~y8X&rh^^{#j@zkt68!^JPI{Zg`a0SFOP>>#@6 zqu^S)QWu~*>>nC}i=jzKwE}pRah_O_WuryiztWgX}bYpt`=1YC#ffl2Vf;G%rSotK}-XN57I+q4d{s8J}_%+J!a zmvj)`ZqF9muqEUGPh#8AGHBnqN`Q1gBGM21t*#DH3_MV~5y;sz56-iSxn^M6}%Fjis*lAV?pF}qx`;ntZd*+%A z*j#!T^+xN&5vBzGp?blRW*hMmsZ9D&AvmAYz$|tN$YjUW`qCFvO(|5;#CxPqW+K`z z4tJE(aABjD`(6FP`FMoo#o_##e-ut3`^C3fK3B`&Y3ErZ zP5jPQ;!A9%rvxix%_sb)pNqRIu4l3KcsJ16rSgL5Jd2}O*j&HCE7gjhAT@9?ZB2S1 zR|mbIJd~B@OT?>|N+b~|8O>4Uv=!tneZaPmyV_XVTzCk*X0`c7*JBdFx0nnyD}R90 zb{@$&h)y^|)#Ui*o((XicJ*T<=CmURgIJy~m6Te97Nl>aUE+0;4Z2rByiC2OO&~?= zr_48S+dE{K)KEsde>pc;#{?G3E9o8gDtl4jmqnYUrD$7TQV=y4UkGo_X0*H60x~F_ zz{v@HUAUq#Fs+}fw7oE`NL#oAnKkrdEHC&N1|ZGRUzskS@O%#as1RIONB5?30HpiE{0@<_=;j=01fF$gz+bT*puOIG+!H@j5*PvY&>+eio_?k7A^FbP=w8mDPWv%xR53i9sr+Z zjqF563%ZCtus6iT(Hph4Z96H6n4r6!3K`}Q8t!^z`5zskXlOgz4;hm4v6FqvNzv$k z^me2*v-(CDS;{*9E^(P!i&9qvp37s%;$Q>)u-PN7IqC@KP9_g{58(IgAIQbl5YkW$ z=Uwzz+?v(JT!z~-Alz*zmr;&SQe2t5Q|q{Y&GAojFZI^*g|DbCBW_l;(-JKAE)E1qIM9aZoa zHVePQE}<%jIW6fhwi;ihwa8?9+t?T00lYTksn(GJsI{dzGzH&ff1+o~0oOp=D5G5F z0A-PUG*Gm{K=401^bE2`G~$N>S$sP!?eeNW=;^YH@hj{JAjdTOCf);AcH-bqV~>`H z<~Z)aZFzWLrGFGzNw!!fvS0G8s3KApDX%9h%>&tv{lO^Kja^i$d%dC>m?u7PwM3Mz zMi2qCypZOKA5m4gi)FWbkhZipAoYwb?0(Ebc&9StZ*r!h23jkBpu4P$bAW`*8}g&T zOkiWo6uvsk8a18NgiIz~psYu!{Fib@SqXj?Ra{30XFO5Pu1y!0%B@&C(u7ShCPnmD z%k$CU0qGg_INk~6*e6`l6DnpZee|J5$CxDYkexy{c_A*VYUm*AD2?I0>0tZx;4>uf zHRffot-sP3RIttE37Uu(K_Aiv za~Hz2vewYi*SVHofQ(|tMH`+>#q$kd4bV8sZI|04Wk~~@a zsdQq)@D*1z^j0nse9cLq4D>97kTvQ8DMH&UTw-m?Ut$^dpWvpZQdosDmS5&~ywc&feigrn8h;OU6 zkUjp7`2FM+IsJbeopo4M%iqQA_L`uQCwI;airtCb-QC@V-7N-4b87d@9J{+)?7DWh z*KXhWz5l|)^T6ipiS=FUv)tWy$-E>=!C0{zqohXSLM4cFR84gMPF>~rwB{yRA7Pm* zjAVp-J<(0n#k+&nG4itx?0lqh<^njHo?`Blc3C0@y$#0CI(NoaF08QR+D+;beP696*>xqGfZ3&#arj3 z`M!y0eax1$rECIt67RxoVoKO22U78sU~@e?t}BmEeyPQ^nq-;p2PQp6h>+EG&;!q zFXI@It{;@FHuR``fRW}a`l9e!@WXgw_>&ikx1yH09?&1=JY|Xy9_S^^(F@Q5_&h&Z z3E@i8ZFp~q322PzK>4{%p)Wu?t+CV!{(+6zjWFydnhC93B6J(DOh3w>>Igo!LwDNyZFz#7!4{2>52|L5RWRb4Gl2RI~pB~3w zHsS&`9TngU)C$-1tWbyY!&vI!pjpAw*cc)1CNKG%v)-VW+*`HfuCVADyt zy>&}oYRnP_6PBtKzo`-}ipQcw+9(MJK3S^$8T?;D_BTH4RkWSP1N{)rLwkb|5SwX8 zWu#=LyD!5d@p#am6O{kVB+pdwxvRQRRi4TyRad-yaZ%dVUIoL18M>k_rq7rgd=~Db z&B2l0Aio6G6`wHn?lb8#o8$Sw3bUWN)l4_em04uH^AY;q_Qd{z28=ewQ|2*kpg%VI zljB*9(R1F$Ci6q})3iqLmXt)V@!L2?|6pgy+^A8MZf5xAxVO+kQhN@OVI)%QtIua0 z8nPfz)|bhpSs!pSBSN)j{$U*7&O$kqO-pzSrEM~7T3R4Wsp+c4?n5qpm(&fWqSuVm z-Z9WCqZ%Hf&Bi-e`urcxLmSx7zz%|kc0td=GrAJ2B`USBrohQKzj>a<@T>!3B7T!8%PEjv`k#I(pY)e>F9ROt?r9{qX6M1-o)ne=vI{lgM6}eQMpj{y zkSTicK$_e^SsKV;uEEW1pXKk&+ZK?UDc$Hjb-lr<-RNU;oFk5pH+yn1Tq&V2YQeh9 z5~U(wxEjygGbi1l;EZ&iW*Ntf+~^9)#Wbkh;%iS8Wfh~6R28bgCF6aOmb)WhqJjBiLc<(WGuQX#()>*>kUcpx4D<53T2~TfLx5P zH;=iyXDdT&6~xO@&0tA3*UN?trFZ^Cp;}B9Ovd@qXD+9lUz&*(sf#`YmgYv$GNi8K zT*M<<76E~Z!wcRH1Pl*3N?!THQ8=Nl5{&%Q0d3&6qeZYJYAsp(OLLxiJup-}9VtlP z$VRS}OR$C}OccJW?{Io(KF2TNU$F(F!mJZBLA-teBIbi?Ykn|WyF~pRd*4RlUg7mPDFJwC&>RP$BHiuW{~{)XwuL9gWM9{7;k-dg!NK1 z>l!;oH@P~f7r_@HPCnp{2)s1<`9Dgn!AFjQoivN*$+#w!dD_lkHUwM(9F1|0Wf_ODOtb@KNaxj~E9;V-jD#r+KlxX>`G(NyQj(8xd zk0h;?ITjRv7R${sA|=^{ntRI%N%r035@sX@vxC?UZnL)sjUzUZ^-zavNL~PNZ`SKt z$Yl>KaX*21q?Jrju26Q7up_1#?gl)15Db%|VwONX=ApcnC8KMob7_pXAGL!buEw}4 z+NiX4Ut`Yo#nx&#NB`)W2)=49r6|u}nqtPQt#vPa;;v+{{z*Cp#-S!m&&lFic&oDW z!)0Y9_+c(p8;f-qPjS62s&7D1=`1;`F4P3DkCwnM$zzs!dI8(JMp?|4qR+rhxtA=n zLPfSrQC(1)hxSjeO;ej(zDeG3?Z9SHY z=e9E!+*sE_oh9yBaxX9Js}Say57M{rsu2x37#aK%HjydVFMX0_K)S z+|9TMolyxwQyOn=kT)r%U=UKcm1TUl5uLf-*Rb^J6G})^5KL*xm13XuU6xClPtLLY zE^EIOoWd}$o$g0v#X@*bMq_0qqt9Mb-EdZ9G-?Qk;~i)$Nv4LYojV-v)vCJ6xQ?P^ zz?)^6s@4sJ(nabr@Yi#e?qetJy0~&;d#(a0p#Os(iXHF~rh**zakvG@XS~PP@SPHy zgm|Q``;Obt`FvV14tt|-8_}iLl99spnDRzn{6^nNmNGxtBfS)!nA(uGP)-|PZGx+h zxeLdd4*8{HBy9|J)5G~uakzu=sNUmHVW-(xY|3VHyN$JhUto*rb*902;!L@n@C0`? zU(<%5KFdWEfy;r-`&n-X>Vhqpio?W9*v`*%^wjeSE6e2zBtptOF}$^cb`~}& z>sXgvJNOd$#J?f8NgpW}VF@-mN1r750*CznLA$<^uUER9JdA3jmhwfoNT$)DzAtID z=^TQQ>D*-$GDAr_`y+9TIFQyxJ&j5#qY>dd^dre)leG(qsWoJJ$pc{99kV z&)m%XWur+B|JK)Y zDwUB76cCrfNlY`!vbV6x2V@s(p?7;hH?y>>uW(a72Lfk6c@l`h_u*qI+(4LsiM3sOeG|iq} z<^lTTu1`BiFSVkP%Z%dYacKq1C6p$U6F1^;?H_X}U&(q3hO!)_&)<^|)nC!w;sWxX z=?{CPqwW%DW5RqmFpw?h3BN>(dvdW8fht#mog90#+vJg4pLsQkgMDCg`eM)&cC^}| zN&*7IaeL4Mf5S@Z05mgV10CjfQ$Oh-RdQ7eP`VJ-0E@sN))VA+CMHMg_l$M$oViBW z=&DT5k;2Bhz!-kH(hbcPcG3mnaG7yB?GIe@_3?U3bVvww?q2hYb(WsezWMLDK59+S zc2GQ66NS6Rb0bk5>}AcacC#(MBJ|N@+}KQ2O6gN0S4ddjtfnbDzz#lDEDf5GZ&D1? zZnx4U@+>iwB~$N7zhoDx$$XzI_Xi^Q!Wdg2pny=%v0am_AomboL0hFe^c2dLSK)Jk zkpiYo=x=b!J&&*k&$2$gf@BA`!PoT5Aj-TXb&?jq#@=(f06fMYGK3T`!$x=yk32WeFngTzfYz8b%Ux%o;GHo2lCH?1O{R!4kS{gMLO8Ps=t$p)^YbMhp z>18aNtKnyIUM=8gBVXY9F(sy>5RL1R)^a{?C}WtMAm_aa!fO7ySct}PwcuxCc1V4a zfD-9fM!9YdpX-r&A?EBoiRWal)_sOd=Bz`?1ntuE3OrqiCL{b@0&OR=u7PKG=E;1UX& zJC5r#tFek{SF;0%)xRm5a2=eJ`1L}rIx1xO&0)@NpoPehUf%siN3vAvOg?xfk#Y8| z{7N{>wCRkkNAkPdIjYMS#m+_%d`@NmNp39|XoX?sEfY`j;eiUqnN-dk=DI5hXd9LW6%>h%)l^GRrPwha|jZuL0%7Q#5Z76v@qOA4 zEkb{9zs|I>1rZ@MKPcijE&egpz@iPk0N`S?Iy)Ud%Zy*33^z`DXDRYg`+M!BdyDm69HE?`OrJz0Kx2}HY8w3j%km0^v)W;wdjJ^Y z$twbRf%)G23$tw<^*>S~+83ydUq}!5Y&e)clV+1$c)StxHX=_*U%wBQWx3WG3ZOH! zEp&d&K=Z0PTN=U$m|V=Sg#W-XbV`5VX^Ny^oO2_bZq?As7-3qLln@vZ7{}b=1s)dV79OQdSLCR# zgeIgf84`Y9j>cu9T~dHi8mLg52xzIO1|IQ~Q5ihlKh8RjSx=7lyz!IG zheyC7bQ<}{95dl?6udzzfo4g6-A%zk@XF4IC?|}u)M(+ zb41{tkOQ}L6;=y~19YDlAVq~rd;<96@53nNn>?%7%6dm%7q)=;`n_@walBGkNoTzt zg~j&Vs+Gghb>TiL3U@h&F`88XJ}~TZJrK{PtHWUrTEWm&PH1WRgqC6w(+xh8Y%*7w z4rdULoFi}ry|v=ee+j!hcYJ@u!GfUtVJU;h_z?X-8fKJZv-g{v9Mwz>A+|s`)9VVd z?5jz-n`_h_)_KH%<)CA9D={b)(T0@$4VKA?q?&CoiZ-sR4YT^dBANS9KiouM>}A_R zaSiB0E(Egm`f2lNEmtr1NmPzsZMKJ^xrM}%d@z@NjW~)b_^2Y$cjS=u3O#hcvzpyMK9G~S`G&&nd~dSNSo`OQieM(L zhy3O*Im}uvJ+dwZGlIN$gBuPo40Wuuey8ok8}SVa(M!2sY6a&d*F%0Yc>tFnue+r- zkv5j!F!Ee+&=Z|U@9_w|ko&EepL~O(Oaea2U3D)%(lvl}p!|bcfgy4lnku~jow$Q= z5l9O}#&jS}%ujR`xgPih2g}RNlNk|K3u!9Jq1}YJ)9M*(+@;xUbewS$zB22gkE@hG?CJi%>Fd%#`RbTr;YNQB;{@{9A4e z9H?H!t+V#B`ITj>Z_Z71B&_s*whjZ{)d>5@J@^y;6|_J%)+A9-4r&)8OG)FRzhG!w zlR5(_vJW~-FF7C38sQaOuZ79dhKSexm-GZ_Cb*1-`W@Wa9k2C)^~|x5^HkIe!_JN< z!Wy~JJ8yn`O8wv(uAf3Fs1O+vY-b6$JlMb#@TNqoe75JO1(SCqjR)2g2%##eVUKqm~PTiKEP$k0Lh~^k;!C*SdH51*S zoupa8YK~Y?jJ5+jm+-$dY|LE4F7(FNWe;hI+DkkxPW|FQs}{?I0P!EV0>1|NC>6TU ze{AQJ9%6P@OR^YmRa&6g@Rt7!VSNv%FPoGUHkUIMxJRZ0S;HtceG|2|)B_EnwlrI+ ziZ%)_)pb6Aw?}_Bo?}Ta&+QjO(KopcuEi2#Hqh2Ki-anFEY)b|S{HQ_UNTk-Yq@GT z%)Q>dh?@fqP@1{!6bGSIr~&_TF9MNdCTWGz;jz#TLP5&<&rk@m%iU=S=@qb9Ke;BZ z(NarH>3q1-Tht#%j>Gx;C7+2ao+cy-lc0Y2FuH&>(XCkgzkX#{DNFc*y6^dsy!b|R}7UZFskoxWhM7P zuT6d$Pa>Z2Wz7QCUAhR{0tp}?w3GQo3WbcMnm$!*fbN>jZ9T+N?)nkOaZp%`Pb86u zir6a;hhv4jxD>d6_H$V*_rhpk+(N+~&$6m>i&LqLcr1p2XDPYLcctw-#_MzYV4JWoVSs^~4~ zG?>Iac6Zm~9AmiQ?&>wM|K`v5S_^D43M&h%=ZFMj|M5t1Bx4pFy;k-hx zIo23SR#?m&@o2_Au-ZBpj5l-8TiQoondplsm*q7Mv%Gjg)=kw#x(|xOcvH|{qD*3Q zwLursfYz|=G;xGjpZ3$w)1kDCvC|WRraA2THQ_+$K<3TdY99|@aTQz+#t@oreKRBB z1KbU_GkfU;!7fl&AArheRTy0(JgZaS2I!$I_w{0X{ak#Tv<1aTF{7oo0_-PNma~;g zQh)2C^&;X3aY;?BX`qWTp4(4zN;g3X?yB-+Lk)j_nw=a2f0?6LR?#oKkdfJfXzoWKSkpCSh(1pnelx>Vc7rW#Aq6NDV% zQlUo79vp$+lKWCx@Pl;>7Gmr30oq9E8&QlB_XYW#Gz~X%t<>gYr|=So%BAQgSf9BP z-q_Qk^59F>7S|4V7Ge6tb5lN|In~bif>@5-(wC_`cxQGFbQU)UaEUEqy4|AFjlcM% z($qc6Y-KF~nr{yrXC9Tua|1kOtr=({xv$s9V^HbLno7_&3>mbr>#tZuSRkL)4jD#7 zLc&XP6g@6~g(pD`xhcKmj}EL+W6^Z;hG!CdB+LchT<+*5jO$mwoZUbBjQfTE_} zM~9Kj^pA}!Cm#T(g{Jy2I>h`Tq;AOa?xa8Dr?4nDk12oMq^7Rl@@f4|#w*uMdsTMs z>utO5xl1RJ-gtqL2Xw+5u-J3)qs!`4=wmID{b7!bW}v(t!ukx#@v)$+BikX7PVkU+ zlfC=jcX~_w2fGu<$DD3RhWr)8VC6wS||TJQz!38BsY#V!ZpAX{2%cFv5U6B+M%a` zS@0Npf}3?#V zQ6znqv>iplg<`lhLNA6^G9J`o6!bSBL)_iC5-iu=SC5ZcK)*XTNXx{h-q-RS*09(G zP4zU5=?bf7jm9Ow#5^*X*mAKWHwLjNas{?Si&H<8sz1*UP|6qRDl+ zP1X-jA^&@(kS4Pf{d8q7%&Q)f3@uwvF>7h*cx7ONU&c98x1mE}O;e@=0VNX~t_CV; zmqahBAl((F={wlxts1>#^`Z~qU9SXhGA3s?P1knNrQ)^_P*9j7&!)%mRM^J5Q9nhU zY*MbIE(EjC5ABIno$>M-(1YND+A6v%tSJt%xA*@*$fyDDxMbd_jgy$13kTvuNX_J0j z&S#s<8lDvWfXsvNh@a9)IvGabqc}JFOKY0*^(ttJ(21NAs_?On-RL=Us0J)fjul<- z4fC?i0q=yjB;Hw4EW>(be>rDkr!iXcSxc-a=^KAoSfE!TbvInofB32pFXL!04b%g@ z)BE#T%mZXoI>M30TakBf@XtW&nR2=o8G_~IwcL(!#m}(WLvLdl&h$OhCbBk&w&agh z+u9pZ9JFGzp2y@NXas%=J#EvB^77`#SKt2-d;#)9E;Uo-@5mX25{!k9n@ z`H=FQHF4G#^N?Jur7s7bfG0?-R~|dTj=&gN5>zEu!CS~&MDE47l~6|RZf<}ZL<_GK z-}?JUe^Bk<4=ySep;7K&`g8n1bcXDtgCOfQhx_mnW2yYvH;XorE|YUs^M6+1&mcb; zO}F5c?8GpNjKay%dh4xu+}LV8qTjt`8F`?;F$s^-JnBqTRL|{7GB*m7GId;%hR~Vl zZ`n=AWx0H#!*#11sv?dg&A5M&B3^epEuT=1x$i8PDRe@*8sQnlbp}n`SA^=URV9bD zPq-_Oq0=1`a1j~{hfBGM-7$xH+Wua&>B$E z%sny zs86EJzy)I~KNhz2|I64vY2rq_n#LQAL5s+9IrmV2*M0Ft8sFFHspE2pO2NU&|Nqh@EQt zlKz1qLN8kneu8Zq-<*$NNs!uLJ!*u;fV$#-cq63`&T>y+Wb3lvlvO!(Hg1Bp!R=@O zyrUi$C&MLRmKhoC28wlu52wq7q8Sb3bp4I1ICHg0!Y2Hlm(4-zN03<9#lbRlxNe{~ z{VC4TPT`y3+4Qc^9sMSM#bJz;-We3bUG!6OO?(%Ii)-c6c&z$UObi$}H*Yb=)NtGg zPoz=uDDE(Ef|4;2!H?jN_nxUU*Z5jne(*b^gV0P`312adO=kKoXEcysvQxQ?f0%DH zkDxnek>zXOM80CD%m0|KuZNch%gt5lSl=>|Kz{rB;sdl3>S8n_5a$d;1wyPuc?|mm z)B!&JKQ&%D47Pz-zrqMaQzI4evmgRzmoD(JB>d9WEX2LtM znL1vbMi!Y36KiF9cma1>M!9?{FKM zNd^fAU5-E}cvvZhU(55!GuqO(4}Zc(m=nos{c9B>BVa|83x&{@NT4U2r{ry*jJ^R) z!wW%c_j;OauZ?1STyTxD9yW?8A05SV$tT&3=ZqOa@>#480iD#V-~!BBd@7(Q9@b>N zN$P9u&1~jz(nQ=Yb2y}8H^u?2M<0lL@MRKe%@9kI0pufW>5dG&fJHxQgOsV6U$XYo^R^y^yP+HD*oJ4&4QPk}gQ`xCNc= zx+fN-kSsTw3OO?$d27**;ugM$*qAwShWhfk1FBDt59oS%u{C`rwhe3(Hf4pGrR56v zec+_?wH7O-)BNZho+K|dfz%sL0F{M4!e*SR^x#)IB3QoRNAy;qHF*x=U?$q(o@+EQ zz5tK%kaZzHgiG;Rv9UJO(*h>2j;mN)IEJTljbE4+XzwmA4wQa-u4n?jVNL@IY|Pvn zF1fW}n%>M#bT7!LmnetWtIYAGV~FZ%qi@gfi_F19a} zI|{FrMS7g>X3bMqaYwVB`bmhE+pru`cU(i9&3s)6Y`!2SHsx7QQDzT!PCif3g6JBm zN(PJlm=|}VUIKk0ZMml6e%4lhT+PqVG?$P^^gZ`n%cI<3H__%UH`hzH>w6;W>Ry&Y z4S{E2RU_BDv5Kr;g`My|D-hYk`&W8v{)ScX3}q8=Atcw8chec}+t|ZYhn?v|#igVG z_0c9)b=cKgnv{t7Zrew;(&j7;q>@Q`yl@1UVV?Frvc+247K7nJAyNlrC|%_7yaS~e zn}xKk^A()Ys;gZ=Hxh4Dhou}#q_9#W{0^fnE-&Zw zWn=}}IbpC~D?QcvA}vMLFk8H?d`xK$-@8Y!{k$sZ0`E!-=pCtsI05gq_P{8;JX2h+;8o>1 zfUk~oq%EIE>Veakrub6MQQ!-oLLe?3@(LnA8 zZoGB?+2P-$PsW$odpufd2SSu_M3b|ICz<|CtcY2)vnAPXMe|jIH#D zFRh=&#iV#p!;?ESoz9Rtml~!Y6!T^t#bN%uw7E6ad}=+VdBk^WaU+%N^KZvdVNobf z`6Rp+7N}1GcWl^dF1Law)g|IZlXZsDJSxH?Q4(BiwM8}Q2cw8d(j;8hH{zI6*p&eqAQI` z_#N{_bJhq}nVbjOwVbe~Hj2hr=K;(5>rEIt#|tizB;3aA9q6u~7k_KZ?FZRUEwx6g z6#m-yrabGMYt_WJ4O zAa=`zf`5ej2UJyMcSw zE8Cyw`~GoY3w?&l3jLX9=Nj&!)xa^vP{B@@fj4jp-V8^h1@IHO;ZC9D;5F?YjMdLY z-*ts}he-YXPvvkrCv{lFqBPAKYQ+QIT^H;WMlzLZH6!enax@eVCwPr|xV~}F$YvR# z&Duz^*704Q5bi^J*XK>Y@2)M*6T9gCRXxPM=m5@?`&zlYS#o}WWh5KE!?1ZsifcGL zCXVCY%DuB3!Z~j;qsN^DS<-QI(!EZK6t0sYo(TAi+py*+b$QycZqwIdS+rc*3m$-j zu)exL?eBdJZh|Gg6nTeuoUY{Z@U4+ZE5l!b3NS3ti#*c%80FrTUbp z{$>NlVKeYiVFJI-zE$5(j^Wv!^I{^IgYOA#)(hrR=`PNRI-(f;Fgg}e(D>lrCRBv= z;hgkF;sPto57pK7zj9q;XUP#_C%!A)PIg(52y&4KfiG(%cKe?NMv5==?&O-~;tP|q zU?$6h=QPGh9ZC-l+%~(c3&nfgoy4`)M5Bdc4!UYrlt;|vT1#of)*-tN=zgn@wM*zo z-Z}-Ei~f~J?fHS=W5Mqz+H0*o~4*$c>- zfh=LKB*PjSVJG2tl1JXHpJ0i3fre_E5#er7Utp zxb0$nl<-4lMQNAwgzX$_RPD+#U{>G*n*=VB#+tu`VNt2j?yK&cMs~PA7^m!YM3Zr~ z*XTKo?_9UYiNHqIyMPqyT!LWK7xJ&_$>hcBaJ-P@sR>V!@r*RkA4!6gRY{-8mBtyk zsgWgZHNyikyk*Hu*{}mW?J?;OD!~%LH-TQ#K0X`gvCND(HtqaQR@(E?J>-{I!*t_U z?zvWZFvd~Be>6%IB8?>XYaxKn2>0nRy215ANHqJ?2%{CtvK_{qyfzd??fMh5uDVld zA~ngJYvx6>!9J$o-@=2$zSafxHJ)mf($`ZFcjsTBN8l^p+%GUcfvL_n>f%NEUyu?V zZ)&>0ojW|i1`Y=&eB|_mL9I9m~c_TK0>M0dmJ&frzjHEHOV_)Dky~ey9VeWN- zxo#>>0uPld8G?BN)l-~&k(e1VIrPq?pGA~V*cmkfF7t#O>S>B)<(fuZGsG;nH?j!+ zS2nFCCa3%`TH$PQ1i6aNi<9XEV~J3h)`7V|ZuAs9v2}1aC;8B9#7^-_S9%VQ6s|b4 z%$e}AR7%<--WFL`8fHf_cnRN@=d<3*>5>C2g)>nmDlDJ--*ezf`FmU~{AIn{}yo>aIkc_sd zr=rKQe$E3d&p#eLu_W%8-jro4-mq@lJ|x0eY;GW|*_9CyP6SqI^{hSaXU_LhPyZzo zQ^{(?`WZ}j9pk1ct1{^gFS|>D$@nCW)4N){{zFVAr)eoy1>aFQpHd5qkw%bz(Fj}t zr=c|{h(X-$XW24w@Et~ zH_Tw%)<1GK?H|2A>VOcQRYboXxGNTxEV?UcEU1FZs;zwYv%=tMuob^%&hS3A8aRxb zi3j2UFzH~kG1&mJjrWiz*Olg!NAqZb%zr`a-~w0v7`Na-Gp&w71Mwiti?5hnle6)4 z{4FX={b9|P6Qxx75+2MA2^7Gih&OdLD6Ch*HyDX5(V9Bz6GwtI!uN-K24$H`_dli?z624B z6!a+Vb3_RLh)?nvLWIMBXB7}w?&s*z^)hRl8AA5zJBweYJH4q=9dS9oGtf|;sv}#n za))bfwGPcAt%f<20akN9mAho+WX`cNLT}wKREbHa-GY^qd*bdfjbf(w+n|%>0&tp@ z-(7?`(3hIsin;I#8YG8u3F4{nr(9FA({-M%GkX}lj4ARR>$nRWpYgcBJne`)P0E5( zr9Y8zMgc2Ud`ixzGp}N_^-fO42VphyAQ}zg=pph8KSIM%q;QGA&ncjxh0) zhm10~o6e@^<$WZDwjrgBx_nNd7On`Um}@-+y~C9&AX^F=QEGcWuhAO66MT4@brb!T z`$!AS6gWg5#%5hh^gnPjDNM)uJ4ngkjH~U2yf~;jSPpitKPG$$-`_V4E>Txm?Ma%+ zbY6C&FMuyty4e~wz}NVQ0%zfTIu$iB#t9KtE72;=Xj$vD&UA{|BQopOaU> zp~PvC`Ve&kp;6f|7x#*~)i|rKFNDi$ELO2A7Br({!I|`fbT0DX&SKugi@3dhLbr3D zNEv@TDvNpWQpobI6pwLJtVri4BOk3M+_AoJ%avG~>3Jmya1$DgI+Xhke=y?mD5r^T zkm=?xxXi3213VIzMk7qa8VXuKOsU)c7>1OwGa*mS7if|QiDhn<5|Ash~Gf9 zPS363Z%iJW^`*&VkI!j!asAcjZ~}NmdXZ9~xY{Q;i93Y?<`V0<847m#Z_@tsCN9GC z@HfUTeit|q`4>M_w=;dO37ZEjW-8k#0%$F%Z3UkMQ>G) zg{-2XFx>N&G_nfWx6-Qe|MGdmy>pn(zZz)ZK4@S~L641m(bMTmP!LX44w%t60}Zg+ zkj2c49m@8z{8kQe8ZO0rWOl(q`;21V5@;Ijg{Nt;j9$OLbHun3*Dzxy{BBQgjG^C<1p5wmoSmm z^d-_GtoN=cSzj=n<)@G1H0`=xz=`!^bP$a)3JT#?33k3dV2ls`(o4B!cuT{_xUX7_ zkpq$ToVBJ$C|x5)ljFu3>?U0^F47MAP*=nBulTMWj;r!JeLTD<4S*BD3Rn|1poL&d zX|35vp!`cnqE^z1zBu`XXHw)V`J|b-u0M^|`_fROIX&U7&D9B?;ndpmFd5FX{9mu zjZ7f5sYh>xR<~_MLnN2%lJ@x8_!Q{D+Pvm?G*hNdB;!X!C z#bt^I+5QmWj<<}MYxoM;7_1XS@GUvW6zmbn*O^~3f=-F)MH^VXjU!+W@LFrU%aJaY z6KmThIaGYopA~UH-mk`qJ>=2C99oKwk}FD4&ST;MJ}tVn@Q@VqjTOsFr}(FVl6YUi zSELJLY(@*u<#NVDX;$P9+0Hb}+3aLrH}j^wE^1{=76v-!6C%{q^1?m(V=b3367&q4 z;E4)MRA0+Yy?fQ;fF&UGzUg=2B()kS##7Q#D8Se-X~H@&TA^l&R?_&Tl|ha1d0)@S zIphRBs8xjpNnd>hnosA1B(t{kN|Hk6dpNdFRUpmi6|&sDPj-OOxGi~zdI{BKgbx@# zaf|7bA~VO!)a)L~x$B}0(t+T5(2+hCZs}c_{+llLi>VePy2kTU;0vuiWH~CPwg!C) z>TRsF8SF1OK{~tFxaPaw3e{i>vYM{~!=(DwZ)qu)khxqf%;=d#-1E>GF&WqJ=C``> zgS809QdblzOVgOfwVwQ^Ph|SZC&sv1PPW(!8vn5D(Q)gBd<#S{1tw0M8mJ&1#s$F+p1#}p!I6S!pSNwJj9pv(Vl;WkRXSOxwFigq&>Q?6P~r=I5fSHT*Kg4+UuF` z2C1cs^p`iM-d@zfXKbfgnt;xLXgoA!Fu2B&{`1^*+-YJ_mUk#p`mg>H_DtOm3|k6* zLEA9*Ty0m-Y6uG9Ps(U>6LT*w0^`+B<}HPb+`YhUMVfK zM6xjBH0%#Id4AK8z#Rd)!WKb?umpU7d4s!H&qpt9WYj$OQ)v#%cVEPVjH9+m zAX@ou)KLCGF`|PMO05Ei!6a)3z8J8nImB07Ua^UhftP|#&<4Nh!$F?ZMX*}rJl179%Ctz?I?;Dhae@`f54;g@a}Jk5XF~4U{O&N_i~fM|dL8G`fWZi@PLUy1+&k4|rhgZ8 z&&>S8zs1MQja+)Ld`$D;Ls~cJvNp07q2lnK(A2od? zK{QBUcb-^zhO-#D$0}*IdSAmK>?`l3oss3re1~HQdwv>EMv$9QfAc>!IcrRJ(Py6Q z6&~XpS+94JV-kR`A2tR_uxL#9g(=79Eqw3izr2+p`0FqdbO%wse^OCnWQ64@H@ zJbVi4YAk}Tq8iph>lo<_v3)kqX0+JkbZ1ObkG#q9FGX`!$WLJ{V?S|oJ1Q5ePu^VdAT{R z3ipAkd>Pi6a3}N!jAM+xA*j;2=Jw_8yWl$cO_KNlQZDy!(nl@Nl)N6Pm%*d7r{rEB z#ZiY%v8yv}d<8pM&V$uq17jemc@BmDsx%*<|TmN4aC3Xj!oY+7>(r{NB87<2vY#fzfblE21c(u9-=8zQUXDQyS- z&wdBiQi`kd7{z3Pkc*{dj<0(r6&CNv%VUNFO9U3v`{bHBO^QVYX=UEA>_{M&XB2zq zbKuKj2QeRW#*Y*ZdGcYOM4+)5hQ?6 zpY~1f95#hNE3`9z7(c8Wj1;wxP0NSjr*Jx4&5cl-p*PW0LLIJSq&It77tv-s7VM|J z(9?{3ba+;7{k(qA8YkS9j)HP%oIXO%rLaC@d3MqW`oQAxFUzM+^E6XrHczd`=J(UH z@;PVHE=q$y6u%8UBxS7U=h9fSa$E;y=7+B*t@Yru%X6YVh{G1 zSYj`+D^@^3>bBcwW{FWzRK(sj8hazg*jsEd`Ptri{{i;1?7e5sobx>22a3&@pKXlo z1H8^Ogb~UrAPC#|%lKgaXstwkPHZXdsQ_Z}Bw-K#hna*$A;o#axQvd3cH&{4LvC3e z$@0ZhXh*5K^d=_K+HL%!yg_ZyAT|f}fwoNLw--LQT98k;P)pF0)h>P?9SADLZqq&5 zXxAB$bpq02>|OT)d?DRIakP$_BedgR!7k_@wNUq&9q}x3J$@wdG2d=Qyu>_31xKtg zhN~W|=lTYQyEN{x;ia#(o)f=i4)H?L&0OUutItO)T}Vu_)@XaBX-aRh&b;mX56*V} zt+yus1DW)ObsKqeehJFvIiZfQjXm8bQKsRKHSA$Dkz`r}v?5#}zLjpe6SH=!HKcQJ z0}3PYNXX8>ZOz{9?R*KDtEUG?yVI0Ybc)wZWqQUs)m_Qpf!*z z3-k$Ae{+ER1zm;1{O8;YJ(c4I>BV|itQv{+`pcqlTtRp>>a8?HO@q_X8@v!U10}g7 zY+m0YqgeS4RgjX@FN6QTbIPU_<1F%?ATG>`kbW|+SwVh>9zo`VI7ze>GS!sKy=Sh& z)A}B1iTeWjk?#v1tM}si%dWb8;UbXE&DHOqqehg}%eucR1PWMsWjLuJbi?a%2U{z6)7oOeH;iy%Ezk^x%w$jzw{W@`BR7>?%bH?-$Q!2Tu=!#n zUyN$ADfvKBiySdFTMfu=8VCQ0--nm5cYK{*6xmEDL~8;FC~%m5oExOx;nU#**igzd z=8~7jJnJ#O5cue5AfyRP&A!Hp*yqrtYNbCyW*ajBFOI4#*lqHPohySB$SR|hDSSSs!oM?R7BJTNWuDkp|w_$_p|q=-ks zPP2#SiMg5%L=)Jz{fKKSzgsopud`DlTbdKUP=Ahpglk1hpG{a^I`b&*5ghz&u?Y^w zcFrmfURv$lCj&P@N6^?Buesfi;%Ctoq!&=lLAV=?<$DGDn*RY}mIkuGJ=Xx^nEK4s zP#uN31zSnC@FAwT8EvL8rdUsO4TNA_#b&K>t)b&x6+twk{NA7sg%Q>Ve4l)FV(Cz+<2q=Lj-U}}m=J~bt4U}_{#UGj zZljk&{ly-zth5`HcSSPdz!6&{Bz5f^n`1Y<{+o>5AA|PM<>h;nwm>|ER3) z!RP2PdBbK&ETe#F-?n*DEKwa`&eUDBENU+!&ZIM}F*M!&I5S7S4O)U2^i(_@*sDwu zkHUzJGwDCVTU;!(v4yOqY_>9!bynMqtN4H;8=PmPi+P|AK2a}BXvH!nLS%=9VosCF z0tvnV^SNA@620EN+G<92>R0355$1zt$#Wb&t{o;NV2kl7Fy9#_a6YC)hi_azs7)O0 zG=c7izskQWE3`c35~?m-;QJ^gaFj5~I&Z`gJM(c&6T293aA^L@z)X1=xo>h1YK7ok zzQGL=FH1K-&v*eG;XBLA7zOrHa2II=76@*{^iIGoUnc)?Gji+TMIn_@KVpQ(_8YGC z@=;jPb1-O&LDYewZAGDuWO{xL@&M&2-LxcHk8hw9)34*#>(}+0x%GwnC<%Q95IwHf z*BhIC@nfa}_Oj_?2#<7BvIqHR>3i`TSm6Cc>II6$09j7{a}0Eqz_O?&e;>4Ql$z(b z-r{rj6*+;ak`GZ6b&39usg#?;#@@%jI>1G(rB{QX7G6CPy)7G5^kL4@YIkP>V=^xCvt$Bej{jub# z*#nj#ql5yOfo!PA+ygy01vQ3kKohxd%xIX&N5;29A%+ui&qYzB3!{ z&2w7^;EsqUMkuxuBdxEl*_ZfzL&P&Q4-9~wtg&dka#?S{ zo+jcLwAWq6U4j>}ntsgO!)FpGE=2oeyMCC{QC{sa^e_CM(M9$sOy#FXJC^2&+&+>@ zdlJ@#aP5S8@m})(Z1MnVN?K9`cbNwBlr9q=(Dkig&aUC<-G{5ATq`FVX+9jffZiMQYpa(tOGVTf#AH=9XZ6 z1|5P4_A#!r=2q}Y8%NCC1bbEd=l7_lajvS>I@k&0dW*0DS;93sEUQABN)*R?XmksK6TH z16H3hr3hNdDzB%bAHemvpw!Q-MH?AT*cP@2v2>E%r+VRC@>igb9HCtC9RQp#F?XWg zEqx_CN-Kjy_8TEqiDt{Zi8ONw@mPc!Bd=!#o?5ORk<@%l_-M!IVq%&F?EUn**xecf4 z^NdoIfggaAV81jSr^O~%XNXJRY|G=ia-C6P{u`@`gSes8S6Od+%s%O`+1iS+DDpCX zng~WA{g$_&||YQqqBNIHNmew7wR)^!d+fv+pOu&8s2v4nKsiW<#aJU_h)9>>B*jwSwF*LR_?)C7M~y=q>g; zYN#_FjBZk1O2;FOROvE=QaPZ@J%rIhe{X%}G`l7bu#jHS>?Sv{V!$TrlmXFgz7FbQ z&674;=N;$JUYtW&sv_Pi5xkO694@XxCpanL94qNivtj?nxbl=ID@dlb%P})^+m>(#q(I z*0{$RQJ}vg8{H8Iy~$3o-?c>hKJh&HjuEx`JBM?VVKHW%6&}quXP(gqzt;G3cBjWl)!uSFz&=nMiZ&Q_!Up+Q8vq-%rEr*9XN!#8#f)%V!E1@ z*VA&qp;DM7;BXwPRnfd8e&e~=E_xOEL>dE(&{-dwd+Cet8T%ZtH?&b4!hF^lFc0jH zdCQMwHPMc$(woW{vs7OX+t}txOSx!ajPC-fNK#1!eKhyPT8CX=D-GeEGz`{~isL&< zT@g_mGym^1TEM)wc6O`oi7S9k#%rlCXtS!h_r#Z`i^_a!J-KYIWt=S0T*(re6X`)% z=$Q`)jV`Wi{|0zzizP*REjU##Vk8)6{Bp5r)><%HT94|2 zSKdr3iEv69Ty0blJ{g@sfw2tsm!bsO*&9X3RZ$r$f$(~5qd#9)zl6?-$4E;Pd$nNCax;`2|TW;tQ zdXU|ORD}&#p$Rw^83mv+Stk@RcF7BpBo)(f@K=xlZrhF;nlO^-x~8dJgN6PY;vgy9 z`co-2r=k+jT@DD%-IQs{CS`3Bi`CCuQ}Hsq>0bmXNs-QyQ*5I*2DNtIMK)1pJNadB z4{QzEX}9qcIR~u4El{fZd;V0s8{K0YkJaQFJ_b7L9Y_J_lJCZy-3pEZ8+FS{5<9V3 zegM3XW=Z9|*QM^HvvGw@q7S)e=?lT0_#Q?r_-*zS@jZCVs`G9uBc^^#1o%ts23EsC z{A+Bd^OUi2caoapx0MjP@ z18gq=r}tt6O^^Ltd^<Qbp&6!Bcuh0bwbiPeA9JS`yg8A6#tsC=7%U#|FqJt{i&?dK&p?nJ`XzPYQ){^nh)8U<_%= zzWoHW6LK_#ZC}$6K>M_Xu#WOBRw65eGHL?qK!?yRX@;BL}fn+Sdf7R+>0^_;#1tfafGeK5k`Oo-U%!W!Jj zonorrxr|41xWS(M#e5N&#Qs8GdoU&XLOP3fj!Ox?@^^|mAzu{E$KNsk#5eW- z;x956(ob-n(U^YVhq9f<$6zN$@BLY?Y7O=(ur~~|`sL1H%E?kq!)MwV~C7W5E<75snu# za7F1abE2>XR$xh_KOmF;rOGa=CvBxwx8iskK8GvGxu}!5i0#3n=?k^gETXbA$Eo1s zMm1XGV*totfVk*f+hp%zEt`4W{O%MuM80j5XMM+Zvcx>2^^y>_a7*2=@2Wql z?X-&VX|&$o&~{HsWGsUFqF;#92P-fD7HSJ#xw5x@5~p(uLj7~?UQWG~O( zqC^_wZYi<6A}J&z zeXt2jPJ{SSu$B7)UX+DUMfqE;xll)%DD?1eWS)skErGtqd$MbGPGHq2aArcK{cf~K|BBpvFd@X5l$QOEHiT3xd?>z^%IAC~) zgSY7mc{3wqRVD@GPqIxfHr9Z7j#Dhlt|_UhWSNYV%_s*?P%iH=4ikvxOP|dH=5V}1 zdV(@Y88A}|_fkng`^W{8`Rf^VVi&z=t;DCmGSbiZ!-|#f1}CfG#7owQWA&n1HE742 zruGO7W$8#@BKS;opWFv_GHr^iM` zSxa0cpbyQ7`vNzEIYxhQ5Jp87xsX^z(5z=Lgy#YYv?t|V?X7>nS^YP$F>DCOd1r%O z!Ln9IaX8CenPT<acCoJy#6B8?cFu_JI5iwkj*7 zX|7|e3m$-XgX1)pZROM59wEvsMswUllzH~mu1@l2u8Qx7(osGe(@o09Ew$6E-#l)V zF}^~-xhuG?2C~`ow+g)=b&s8B8%Nf${bROOkrHdUa@ZQjPR(?sl^(5hf)n&ru3^Zl zSD=;s$-*fm-H`RYLS>wo*&p_0eOn_mo+azBO%g2J447)^u z<8`dJhRn6w9zO;STVv5XyvjT%J<#8RT7<*w_rTx7Bw7=0GkR)K;J(mB zK8znCgWmzm=}knDv4mTh&5T&k4R?f$y$hm@ApE$bAuS~feHq*fxE(bEljOO|D9SQh z1Nrci+eT8+EmT%E>-`dUoGS)Dp#f1M?OUlgUCTP)t~A+EPFurQdu#t4Nj?hn<8>YszLXGfYl58ADokEpE@iAk`33`;?(Cg4w=5`o? zT7%QpX*`r>It$qSo5**Rj_wyAQmP|Y%+DnIh0ny!&XsP`cp6WZ%Z>2hfRAn9ui|h{ zk6BNDT011p$Lhv=;gh~ZnuL>Rdp%4ak8;6cv>yH?EDy{^XW8`V692btYkm(NkOo}O zz}L(@cu4(2i;`>OLfnRyN)c)?$&oR>7#vKJ+z*gOPT9IauNHzk>4mzl;!nyQz*=jK zv{lSfC#knlT-<4TixIBF&?7cU>co;cpDTx?#e7Y28$QIZ$wjgxctQ!s4zEQhbF4mq z--sdjS%?BIp|+M8pNFn`hoXwsbNm`-2TKCkA_>KXRD2uMqa(K7r(Z%KB?#4ACt?uZ zMNiVhEc%Sq(^zp8z(-nwEBxwNuiEu^HAvH@bzx`PAGg z?x$;avRT+reU-EeFQ9MPM&VF!F}alcGgu5}g9)`qM%P6v@n&afP9-UewiIPX^R8snwKjk>3D81VM(9L$z+5W;cd75eOIQ>+ zO^I+m)t}HkZrxExBV1kS5&buF74>^`krC&jUy2QBF`g$6@_dbKB!ixzv&3TIl`B`= z2k-hjtoh1bNgqKv3g5YAiNB&j}7N&Zpho*rL&zkLG}9Gx)V>M8OM+E_4*-Vz4E7_)_S-L*SsySByk z62Amx9bL>CvQm!*8M#6~!p>ol>aG-bCN&L^+X$ApKEkb9ivA1O$LCr7qKA8Dy2}{} zP}SN=hU*JwjaUFidaL;NtC#dJYqskMozGYVjAXApg2zRgKh!?e??u$O=T5WIrT%QH zJkg&(rkN$OifVDlf7M@rpD`~q)DPo0^q3rCxg_u8%HXV+1tx2AkpXSiK5LJjT=gTd z$;EUJ?&9iPzPkI4KGb;O`wjgjYZH25J<(p1-Ql;$vq&wrxiAJa zbdMkh?2bCSrQvFv(w2GX|Ft}xXitf;RG6rLjdmOBL%ZYw?$g<&q*1+=be^XY?k_xK z?xsI!W4XW@;5@2cRa+S`V%Y`*-41PRa3vTPoKGy5L(4OhrT@em+6HccybEf^NioDc z=8;hHRph4W0~y^nMSkmEu0()v(v3gn+M_=YG^V@wKK@{4B&Z3h;y=+7ak&3^%^Cbi zaV0w9it|=es^j}~FI_Fx@~7iCVTUs27+B(!p2y zV|RD5Gtf&^V(V%11Fsl?zO3&Lyp`q5$KdK_7-q0ubleyu4g=drW1|)=%y~jG;U9Pd z_X~c6nwnz~Bp}#ME{$&}1mHA%9$#eba*3B3^h zhWf;6{AZ&fIj)xj_Zc}*%`1?u!L~-F`faHNUi+`26cFaHj1MGF`jS2rpMja~AnmzpTvhpr z-dd_nSBUMz5?Vz`(D$hQxVGjg&%xSLvEfVCntE5l7l4L_&}Uo|k1u%8_$l-m4dKH< zRcCUrYve=s8fhXdM!o2CWeW2sC0M^2`vKI(nLl$cKnrr1`3WB3wRnxDB1cxytSSINWf{bU|o&3zadC^ePFG+2T%W5@nG|O;T7Y~Dr`B!LHxI1S) zR?${?fc5I7LL)Sk7Sq!Fr?7z_XLJb4z`&1fx6lY!!}^`^e*Xu08kOU(DKB6HBN?iC zH`oF`i;X0=a+A{wbPxAIEMYvn>KSvP%{!8H&T8fc^f*`nbWmS2mDOo*lj-ZbN~81d z812MrbS0R9`YNXa*X4fbv)oxcjRIti(9@`5E)egL+6GH95)P0)B$Ls-Rcns>s5pL$ zL6Sba8cFGZ8_J#GXpbjMla#74NMCZR;QpRn42UmDwmqu_JA2BOq6;7P<7d7*EL zEjNxm{P9mXB3{G-`YJD*@tDrMmF65nzMWR&bXGu1{~;! z@2z|YLCtBN0dpg(!1A=bG>=TQely#I3YjmGs!-=I>x_#o_Uquxqk#_&4bY%^Rhe>>)cU&xm3zL5YzBzGXp2e z>49s(*6e?nYBjfBvkmT->RELjdJV4X8ERT?JNyU?arE^H!S^hkw{6x*#x~A_4y8Qp zP3n^Gj6ookrog|=lHjR`I6}le@CwWRnIcrv(#(})Rcj~VF*bJ_{!$3H YvdLpML0Fmp0CXe6z<=IU|JVBe0o&Q6@&Et; literal 0 HcmV?d00001 diff --git a/model-optimizer/mo/utils/unittest/test_data/mxnet_synthetic_gru_bidirectional_FP16_1_v6.xml b/model-optimizer/mo/utils/unittest/test_data/mxnet_synthetic_gru_bidirectional_FP16_1_v6.xml new file mode 100644 index 00000000000000..1c572cb268ddc1 --- /dev/null +++ b/model-optimizer/mo/utils/unittest/test_data/mxnet_synthetic_gru_bidirectional_FP16_1_v6.xml @@ -0,0 +1,626 @@ + + + + + + + 1 + 10 + 16 + + + + + + + + 1 + 10 + 16 + + + + + 10 + 1 + 16 + + + + + + + 2 + 1 + 128 + + + + + + + + + + + 2 + 1 + 128 + + + + + 1 + 1 + 128 + + + 1 + 1 + 128 + + + + + + + 2 + + + + + + + + + + 1 + 1 + 128 + + + 2 + + + + + 1 + 128 + + + + + + + 10 + 1 + 16 + + + 1 + 128 + + + + + 10 + 1 + 128 + + + 1 + 128 + + + + + + + + + + + + + + + + + + 1 + 1 + 16 + + + + + 1 + 16 + + + + + + + + 1 + 16 + + + 1 + 128 + + + + + 1 + 128 + + + + + + + + + + + + 1 + 128 + + + + + 1 + 1 + 128 + + + + + + + + + + + + + + 3 + + + + + + + + + + 1 + 128 + + + 3 + + + + + 1 + 1 + 128 + + + + + + + 2 + + + + + + + + + + 1 + 1 + 128 + + + 2 + + + + + 1 + 128 + + + + + + + 10 + 1 + 16 + + + 1 + 128 + + + + + 10 + 1 + 128 + + + 1 + 128 + + + + + + + + + + + + + + + + + + 1 + 1 + 16 + + + + + 1 + 16 + + + + + + + + 1 + 16 + + + 1 + 128 + + + + + 1 + 128 + + + + + + + + + + + + 1 + 128 + + + + + 1 + 1 + 128 + + + + + + + + + + + + + + 3 + + + + + + + + + + 1 + 128 + + + 3 + + + + + 1 + 1 + 128 + + + + + + + + 1 + 1 + 128 + + + 1 + 1 + 128 + + + + + 2 + 1 + 128 + + + + + + + 4 + + + + + + + + + + 10 + 1 + 128 + + + 4 + + + + + 10 + 1 + 1 + 128 + + + + + + + 4 + + + + + + + + + + 10 + 1 + 128 + + + 4 + + + + + 10 + 1 + 1 + 128 + + + + + + + + 10 + 1 + 1 + 128 + + + 10 + 1 + 1 + 128 + + + + + 10 + 2 + 1 + 128 + + + + + + + + 10 + 2 + 1 + 128 + + + + + 10 + 1 + 2 + 128 + + + + + + + 3 + + + + + + + + + + 10 + 1 + 2 + 128 + + + 3 + + + + + 10 + 1 + 256 + + + + + + + + 10 + 1 + 256 + + + + + 1 + 10 + 256 + + + + + + + 1 + 10 + 256 + + + + + 1 + 10 + 256 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/model-optimizer/mo/utils/unittest/test_data/mxnet_synthetic_gru_bidirectional_FP16_1_v6_negative.xml b/model-optimizer/mo/utils/unittest/test_data/mxnet_synthetic_gru_bidirectional_FP16_1_v6_negative.xml new file mode 100644 index 00000000000000..62e8422a27f772 --- /dev/null +++ b/model-optimizer/mo/utils/unittest/test_data/mxnet_synthetic_gru_bidirectional_FP16_1_v6_negative.xml @@ -0,0 +1,626 @@ + + + + + + + 1 + 10 + 16 + + + + + + + + 1 + 10 + 16 + + + + + 10 + 1 + 16 + + + + + + + 2 + 1 + 128 + + + + + + + + + + + 2 + 1 + 128 + + + + + 1 + 1 + 128 + + + 1 + 1 + 128 + + + + + + + 2 + + + + + + + + + + 1 + 1 + 128 + + + 2 + + + + + 1 + 128 + + + + + + + 10 + 1 + 16 + + + 1 + 128 + + + + + 10 + 1 + 128 + + + 1 + 128 + + + + + + + + + + + + + + + + + + 1 + 1 + 16 + + + + + 1 + 16 + + + + + + + + 1 + 16 + + + 1 + 128 + + + + + 1 + 128 + + + + + + + + + + + + 1 + 128 + + + + + 1 + 1 + 128 + + + + + + + + + + + + + + 3 + + + + + + + + + + 1 + 128 + + + 3 + + + + + 1 + 1 + 128 + + + + + + + 2 + + + + + + + + + + 1 + 1 + 128 + + + 2 + + + + + 1 + 128 + + + + + + + 10 + 1 + 16 + + + 1 + 128 + + + + + 10 + 1 + 128 + + + 1 + 128 + + + + + + + + + + + + + + + + + + 1 + 1 + 16 + + + + + 1 + 16 + + + + + + + + 1 + 16 + + + 1 + 128 + + + + + 1 + 128 + + + + + + + + + + + + 1 + 128 + + + + + 1 + 1 + 128 + + + + + + + + + + + + + + 3 + + + + + + + + + + 1 + 128 + + + 3 + + + + + 1 + 1 + 128 + + + + + + + + 1 + 1 + 128 + + + 1 + 1 + 128 + + + + + 2 + 1 + 128 + + + + + + + 4 + + + + + + + + + + 10 + 1 + 128 + + + 4 + + + + + 10 + 1 + 1 + 128 + + + + + + + 4 + + + + + + + + + + 10 + 1 + 128 + + + 4 + + + + + 10 + 1 + 1 + 128 + + + + + + + + 10 + 1 + 1 + 128 + + + 10 + 1 + 1 + 128 + + + + + 10 + 2 + 1 + 128 + + + + + + + + 10 + 2 + 1 + 128 + + + + + 10 + 1 + 2 + 128 + + + + + + + 3 + + + + + + + + + + 10 + 1 + 2 + 128 + + + 3 + + + + + 10 + 1 + 256 + + + + + + + + 10 + 1 + 256 + + + + + 1 + 10 + 256 + + + + + + + 1 + 10 + 256 + + + + + 1 + 10 + 256 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/model-optimizer/mo/utils/versions_checker.py b/model-optimizer/mo/utils/versions_checker.py index e8408ecf256144..ff576d9d024813 100644 --- a/model-optimizer/mo/utils/versions_checker.py +++ b/model-optimizer/mo/utils/versions_checker.py @@ -44,7 +44,40 @@ def check_python_version(): return 1 -def get_module_version_list_from_file(file_name): +def parse_versions_list(required_fw_versions: str, version_list: list()): + """ + Parsing requirements versions + :param required_fw_versions: String with fw versions from requirements file + :param version_list: List for append + :return: list of tuples of strings like (name_of_module, sign, version) + + Returned object is: + [('tensorflow', '>=', '1.2.0'), ('networkx', '==', '2.1'), ('numpy', None, None)] + """ + + line = required_fw_versions.strip('\n') + line = line.strip(' ') + if line == '': + return [] + splited_versions_by_conditions = re.split(r"==|>=|<=|>|<", line) + splited_versions_by_conditions = [l.strip(',') for l in splited_versions_by_conditions] + + if len(splited_versions_by_conditions) == 0: + return [] + if len(splited_versions_by_conditions) == 1: + version_list.append((splited_versions_by_conditions[0], None, None)) + else: + splited_required_versions= re.split(r",", line) + for i, l in enumerate(splited_required_versions): + comparisons = ['==', '>=', '<=', '<', '>'] + for comparison in comparisons: + if comparison in l: + version_list.append((splited_versions_by_conditions[0], comparison, splited_versions_by_conditions[i + 1])) + break + return version_list + + +def get_module_version_list_from_file(file_name: str): """ Please do not add parameter type annotations (param:type). Because we import this file while checking Python version. @@ -65,25 +98,7 @@ def get_module_version_list_from_file(file_name): req_dict = list() with open(file_name) as f: for line in f: - line = line.strip('\n') - line = line.strip(' ') - if line == '': - continue - splited_line = re.split(r"==|>=|<=|>|<", line) - splited_line = [l.strip(',') for l in splited_line] - if len(splited_line) == 1: - req_dict.append((splited_line[0], None, None)) - else: - if '==' in line: - req_dict.append((splited_line[0], '==', splited_line[1])) - elif '>=' in line: - req_dict.append((splited_line[0], '>=', splited_line[1])) - elif '<=' in line: - req_dict.append((splited_line[0], '<=', splited_line[1])) - elif '<' in line: - req_dict.append((splited_line[0], '<', splited_line[1])) - elif '>' in line: - req_dict.append((splited_line[0], '>', splited_line[1])) + req_dict = parse_versions_list(line, req_dict) return req_dict diff --git a/model-optimizer/mo/utils/versions_checker_test.py b/model-optimizer/mo/utils/versions_checker_test.py new file mode 100644 index 00000000000000..27a645532f07f7 --- /dev/null +++ b/model-optimizer/mo/utils/versions_checker_test.py @@ -0,0 +1,55 @@ +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import unittest +import unittest.mock as mock + +from unittest.mock import mock_open +from mo.utils.versions_checker import get_module_version_list_from_file, parse_versions_list + +class TestingVersionsChecker(unittest.TestCase): + @mock.patch('builtins.open', new_callable=mock_open, create=True) + def test_get_module_version_list_from_file(self, mock_open): + mock_open.return_value.__enter__ = mock_open + mock_open.return_value.__iter__ = mock.Mock( + return_value=iter(['mxnet>=1.0.0,<=1.3.1', 'networkx>=1.11', 'numpy==1.12.0', 'defusedxml<=0.5.0'])) + ref_list =[('mxnet', '>=', '1.0.0'), ('mxnet', '<=', '1.3.1'), + ('networkx', '>=', '1.11'), + ('numpy', '==', '1.12.0'), ('defusedxml', '<=', '0.5.0')] + version_list = get_module_version_list_from_file('mock_file') + self.assertEquals(len(version_list), 5) + for i, version_dict in enumerate(version_list): + self.assertTupleEqual(ref_list[i], version_dict) + + @mock.patch('builtins.open', new_callable=mock_open, create=True) + def test_get_module_version_list_from_file_with_fw_name(self, mock_open): + mock_open.return_value.__enter__ = mock_open + mock_open.return_value.__iter__ = mock.Mock( + return_value=iter(['mxnet'])) + ref_list = [('mxnet', None, None)] + version_list = get_module_version_list_from_file('mock_file') + self.assertEquals(len(version_list), 1) + for i, version_dict in enumerate(version_list): + self.assertTupleEqual(ref_list[i], version_dict) + + def test_append_version_list(self): + v1 = 'mxnet>=1.0.0,<=1.3.1' + req_list = list() + parse_versions_list(v1, req_list) + ref_list = [('mxnet', '>=', '1.0.0'), + ('mxnet', '<=', '1.3.1')] + for i, v in enumerate(req_list): + self.assertEquals(v, ref_list[i]) diff --git a/model-optimizer/requirements.txt b/model-optimizer/requirements.txt index 5b46a2c40d931c..2dba5b90b848c8 100644 --- a/model-optimizer/requirements.txt +++ b/model-optimizer/requirements.txt @@ -4,5 +4,4 @@ networkx>=1.11 numpy>=1.12.0 protobuf==3.6.1 onnx>=1.1.2 -test-generator==0.1.1 defusedxml>=0.5.0 diff --git a/model-optimizer/requirements_caffe.txt b/model-optimizer/requirements_caffe.txt index eb748925efd2f0..a032f83c870a9b 100644 --- a/model-optimizer/requirements_caffe.txt +++ b/model-optimizer/requirements_caffe.txt @@ -1,5 +1,4 @@ networkx>=1.11 numpy>=1.12.0 protobuf==3.6.1 -test-generator==0.1.1 -defusedxml>=0.5.0 \ No newline at end of file +defusedxml>=0.5.0 diff --git a/model-optimizer/requirements_dev.txt b/model-optimizer/requirements_dev.txt new file mode 100644 index 00000000000000..d325751cde2a23 --- /dev/null +++ b/model-optimizer/requirements_dev.txt @@ -0,0 +1,8 @@ +coverage==4.4.2 +m2r==0.1.12 +pyenchant==1.6.11 +pylint==2.1.1 +Sphinx==1.6.5 +safety==1.8.5 +test-generator==0.1.1 +defusedxml>=0.5.0 diff --git a/model-optimizer/requirements_kaldi.txt b/model-optimizer/requirements_kaldi.txt index 24caaf4d23497a..acd2c87d4736b3 100644 --- a/model-optimizer/requirements_kaldi.txt +++ b/model-optimizer/requirements_kaldi.txt @@ -1,4 +1,3 @@ networkx>=1.11 numpy==1.13.0 -test-generator==0.1.1 defusedxml>=0.5.0 diff --git a/model-optimizer/requirements_mxnet.txt b/model-optimizer/requirements_mxnet.txt index 1e2f5571e5c250..883ec69b3be8d9 100644 --- a/model-optimizer/requirements_mxnet.txt +++ b/model-optimizer/requirements_mxnet.txt @@ -1,5 +1,4 @@ mxnet>=1.0.0,<=1.3.1 networkx>=1.11 numpy>=1.12.0 -test-generator==0.1.1 -defusedxml>=0.5.0 \ No newline at end of file +defusedxml>=0.5.0 diff --git a/model-optimizer/requirements_onnx.txt b/model-optimizer/requirements_onnx.txt index e196da4f06e4b6..e0ed76ec1ee96b 100644 --- a/model-optimizer/requirements_onnx.txt +++ b/model-optimizer/requirements_onnx.txt @@ -1,5 +1,4 @@ onnx>=1.1.2 networkx>=1.11 numpy>=1.12.0 -test-generator==0.1.1 -defusedxml>=0.5.0 \ No newline at end of file +defusedxml>=0.5.0 diff --git a/model-optimizer/requirements_tf.txt b/model-optimizer/requirements_tf.txt index 209381ce2c54db..2accfb75a8cbe8 100644 --- a/model-optimizer/requirements_tf.txt +++ b/model-optimizer/requirements_tf.txt @@ -1,5 +1,4 @@ tensorflow>=1.2.0,<2.0.0 networkx>=1.11 numpy>=1.12.0 -test-generator==0.1.1 -defusedxml>=0.5.0 \ No newline at end of file +defusedxml>=0.5.0 diff --git a/tools/.gitignore b/tools/.gitignore index 8ff67851095c18..63a5f5d687e81c 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -1,3 +1,4 @@ +accuracy_checker accuracy_checker.log i8_normalized.dot openvino.tools.benchmark.log diff --git a/tools/accuracy_checker/.pylintrc b/tools/accuracy_checker/.pylintrc deleted file mode 100644 index 7c903acc155574..00000000000000 --- a/tools/accuracy_checker/.pylintrc +++ /dev/null @@ -1,31 +0,0 @@ -[MASTER] -disable = C0103, - C0111, - too-many-locals, - too-many-arguments, - unused-argument, - too-many-instance-attributes, - too-few-public-methods, - unsubscriptable-object, - unbalanced-tuple-unpacking, - arguments-differ, - E1101, - E1111, - C0204, - W0201, - W0107, - R0401 - -max-line-length = 120 -ignore-docstrings = yes -extension-pkg-whitelist=inference_engine,cv2,numpy -ignored-modules = numpy,cv2,openvino.inference_engine,caffe -load-plugins = pylint_checkers -ignored-classes = pathlib.PurePath -jobs=0 - -[SIMILARITIES] -ignore-imports = yes - -[BASIC] -bad-functions=print,as_posix,absolute diff --git a/tools/accuracy_checker/README.md b/tools/accuracy_checker/README.md deleted file mode 100644 index ceee153609a3cd..00000000000000 --- a/tools/accuracy_checker/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# Deep Learning accuracy validation framework - -## Installation - -### Prerequisites - -Install prerequisites first: - -#### 1. Python - -**accuracy checker** uses **Python 3**. Install it first: - -- [Python3][python3], [setuptools][setuptools]: - -```bash -sudo apt-get install python3 python3-dev python3-setuptools python3-pip -``` - -Python setuptools and python package manager (pip) install packages into system directory by default. Installation of accuracy checker tested only via [virtual environment][virtualenv]. - -In order to use virtual environment you should install it first: - -```bash -python3 -m pip install virtualenv -python3 -m virtualenv -p `which python3` -``` - -Before starting to work inside virtual environment, it should be activated: - -```bash -source /bin/activate -``` - -Virtual environment can be deactivated using command - -```bash -deactivate -``` - -#### 2. Frameworks - -The next step is installing backend frameworks for Accuracy Checker. - -In order to evaluate some models required frameworks have to be installed. Accuracy-Checker supports these frameworks: - -- [OpenVINO][openvino-get-started]. -- [Caffe][caffe-get-started]. - -You can use any of them or several at a time. - -#### 3. Requirements installation -```bash -pip3 install -r requirements.txt - -[python3]: https://www.python.org/downloads/ -[setuptools]: https://pypi.python.org/pypi/setuptools -[caffe-get-started]: accuracy_checker/launcher/caffe_installation_readme.md -[virtual-environment]: https://docs.python.org/3/tutorial/venv.html -[virtualenv]: https://virtualenv.pypa.io/en/stable -[openvino-get-started]: https://software.intel.com/en-us/openvino-toolkit/documentation/get-started \ No newline at end of file diff --git a/tools/accuracy_checker/accuracy_checker/__init__.py b/tools/accuracy_checker/accuracy_checker/__init__.py deleted file mode 100644 index c73671b0cf1e2e..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -__version__ = "0.6.9" diff --git a/tools/accuracy_checker/accuracy_checker/adapters/README.md b/tools/accuracy_checker/accuracy_checker/adapters/README.md deleted file mode 100644 index 40cec313bd58b6..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/adapters/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# Adapters - -Adapter is a function for conversion network infer output to metric specific format. -You can use 2 ways to set adapter for topology: -* Define adapter as a string. - -```yml -adapter: classification -``` - -* Define adapter as a dictionary, using `type:` for setting adapter name. This approach gives opportunity to set additional parameters for adapter if it is required. - -```yml -adapter: - type: reid - grn_workaround: False -``` - -AccuracyChecker supports following set of adapters: -* `classification` - converting output of classification model to `ClassificationPrediction` representation. -* `segmentation` - converting output of semantic segmentation model to `SeegmentationPrediction` representation. -* `tiny_yolo_v1` - converting output of Tiny YOLO v1 model to `DetectionPrediction` representation. -* `reid` - converting output of reidentification model to `ReIdentificationPrediction` representation. - * `grn_workaround` - enabling processing output with adding Global Region Normalization layer. -* `yolo_v2` - converting output of YOLO v2 family models to `DetectionPrediction` representation. - * `classes` - number of detection classes (default 20). - * `anchors` - anchor values provided as comma-separated list or one of precomputed: `yolo_v2` and `tiny_yolo_v2`. - * `coords` - number of bbox coordinates (default 4). - * `num` - num parameter from DarkNet configuration file (default 5). -* `yolo_v3` - converting output of YOLO v3 family models to `DetectionPrediction` representation. - * `classes` - number of detection classes (default 80). - * `anchors` - anchor values provided as comma-separited list or precomputed: `yolo_v3`. - * `coords` - number of bbox coordinates (default 4). - * `num` - num parameter from DarkNet configuration file (default 3). - * `threshold` - minimal objectness score value for valid detections (default 0.001). - * `input_width` and `input_height` - network input width and height correspondingly (default 416). - * `outputs` - the list of output layers names (optional), if specified there should be exactly 3 output layers provided. -* `lpr` - converting output of license plate recognition model to `CharacterRecognitionPrediction` representation. -* `ssd` - converting output of SSD model to `DetectionPrediction` representation. -* `face_person_detection` - converting face person detection model output with 2 detection outputs to `ContainerPredition`, where value of parameters `face_out`and `person_out` are used for identification `DetectionPrediction` in container. - * `face_out` - face detection output layer name. - * `person_out` - person detection output layer name. -* `attributes_recognition` - converting vehicle attributes recognition model output to `ContainerPrediction` where value of parameters `color_out`and `type_out` are used for identification `ClassificationPrediction` in container. - * `color_out` - vehicle color attribute output layer name. - * `type_out`- vehicle type attribute output layer name. -* `head_pose` - converting head pose estimation model output to `ContainerPrediction` where names of parameters `angle_pitch`, `angle_yaw` and `angle_roll` are used for identification `RegressionPrediction` in container. - * `angle_pitch` - output layer name for pitch angle. - * `angle_yaw`- output layer name for yaw angle. - * `angle_roll` - output layer name for roll angle. -* `age_gender` - converting age gender recognition model output to `ContainerPrediction` with `ClassificationPrediction` named `gender` for gender recognition, `ClassificationPrediction` named `age_classification` and `RegressionPrediction` named `age_error` for age recognition. - * `age_out` - output layer name for age recognition. - * `gender_out` - output layer name for gender recognition. -* `action_detection` - converting output of model for person detection and action recognition tasks to `ContainerPrediction` with `DetectionPrdiction` for class agnostic metric calculation and `DetectionPrediction` for action recognition. The representations in container have names `class_agnostic_prediction` and `action_prediction` respectively. - * `priorbox_out` - name of layer containing prior boxes in SSD format. - * `loc_out` - name of layer containing box coordinates in SSD format. - * `main_conf_out` - name of layer containing detection confidences. - * `add_conf_out_prefix` - prefix for generation name of layers containing action confidences if topology has several following layers or layer name. - * `add_conf_out_count` - number of layers with action confidences (optional, you can not provide this argument if action confidences contained in one layer). - * `num_action_classes` - number classes for action recognition. - * `detection_threshold` - minimal detection confidences level for valid detections. -* `super_resolution` - converting output of single image super resolution network to `SuperResolutionPrediction`. -* `landmarks_regression` - converting output of model for landmarks regression to `FacialLandmarksPrediction`. -* `text_detection` - converting output of model for text detection to `TextDetectionPrediction`. - * `pixel_class_out` - name of layer containing information related to text/no-text classification for each pixel. - * `pixel_link_out` - name of layer containing information related to linkage between pixels and their neighbors. -* `human_pose_estimation` - converting output of model for human pose estimation to `PoseEstimationPrediction`. - * `part_affinity_fields_out` - name of output layer with keypoints pairwise relations (part affinity fields). - * `keypoints_heatmap_out` - name of output layer with keypoints heatmaps. -* `beam_search_decoder` - realization CTC Beam Search decoder for symbol sequence recognition, converting model output to `CharacterRecognitionPrediction`. - * `beam_size` - size of the beam to use during decoding (default 10). - * `blank_label` - index of the CTC blank label. - * `softmaxed_probabilities` - indicator that model uses softmax for output layer (default False). -* `gaze_estimation` - converting output of gaze estimation model to `GazeVectorPrediction`. diff --git a/tools/accuracy_checker/accuracy_checker/adapters/__init__.py b/tools/accuracy_checker/accuracy_checker/adapters/__init__.py deleted file mode 100644 index 221d8ed93b437a..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/adapters/__init__.py +++ /dev/null @@ -1,80 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from .adapter import Adapter, AdapterField, create_adapter - -from .action_recognition import ActionDetection -from .text_detection import TextDetectionAdapter, LPRAdapter, BeamSearchDecoder -from .image_processing import SuperResolutionAdapter -from .attributes_recognition import ( - HeadPoseEstimatorAdapter, - VehicleAttributesRecognitionAdapter, - PersonAttributesAdapter, - AgeGenderAdapter, - LandmarksRegressionAdapter, - GazeEstimationAdapter -) - -from .reidentification import ReidAdapter -from .detection import TinyYOLOv1Adapter, SSDAdapter, FacePersonAdapter, YoloV2Adapter, YoloV3Adapter -from .classification import ClassificationAdapter -from .segmentation import SegmentationAdapter, BrainTumorSegmentationAdapter -from .pose_estimation import HumanPoseAdapter - -from .dummy_adapters import XML2DetectionAdapter - -from .hit_ratio import HitRatioAdapter - -__all__ = [ - 'Adapter', - 'AdapterField', - 'create_adapter', - - 'XML2DetectionAdapter', - - 'ClassificationAdapter', - - 'SSDAdapter', - 'TinyYOLOv1Adapter', - 'YoloV2Adapter', - 'YoloV3Adapter', - 'FacePersonAdapter', - - 'SegmentationAdapter', - 'BrainTumorSegmentationAdapter', - - 'ReidAdapter', - - 'SuperResolutionAdapter', - - 'HeadPoseEstimatorAdapter', - 'VehicleAttributesRecognitionAdapter', - 'PersonAttributesAdapter', - 'AgeGenderAdapter', - 'LandmarksRegressionAdapter', - 'GazeEstimationAdapter', - - 'TextDetectionAdapter', - - 'BeamSearchDecoder', - 'LPRAdapter', - - 'HumanPoseAdapter', - - 'ActionDetection', - - 'HitRatioAdapter' -] diff --git a/tools/accuracy_checker/accuracy_checker/adapters/action_recognition.py b/tools/accuracy_checker/accuracy_checker/adapters/action_recognition.py deleted file mode 100644 index 113eb9d6db9b6d..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/adapters/action_recognition.py +++ /dev/null @@ -1,119 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from ..adapters import Adapter -from ..config import ConfigValidator, StringField, NumberField -from ..representation import DetectionPrediction, ContainerPrediction - - -class ActionDetectorConfig(ConfigValidator): - type = StringField() - priorbox_out = StringField() - loc_out = StringField() - main_conf_out = StringField() - add_conf_out_prefix = StringField() - add_conf_out_count = NumberField(optional=True, min_value=1) - num_action_classes = NumberField() - detection_threshold = NumberField(optional=True, floats=True, min_value=0, max_value=1) - - -class ActionDetection(Adapter): - __provider__ = 'action_detection' - - def validate_config(self): - action_detector_adapter_config = ActionDetectorConfig('ActionDetector_Config') - action_detector_adapter_config.validate(self.launcher_config) - - def configure(self): - self.priorbox_out = self.launcher_config['priorbox_out'] - self.loc_out = self.launcher_config['loc_out'] - self.main_conf_out = self.launcher_config['main_conf_out'] - self.num_action_classes = self.launcher_config['num_action_classes'] - self.detection_threshold = self.launcher_config.get('detection_threshold', 0) - add_conf_out_count = self.launcher_config.get('add_conf_out_count') - add_conf_out_prefix = self.launcher_config['add_conf_out_prefix'] - if add_conf_out_count is None: - self.add_conf_outs = [add_conf_out_prefix] - else: - self.add_conf_outs = [] - for num in np.arange(start=1, stop=add_conf_out_count + 1): - self.add_conf_outs.append('{}{}'.format(add_conf_out_prefix, num)) - - def process(self, raw, identifiers=None, frame_meta=None): - result = [] - raw_outputs = self._extract_predictions(raw, frame_meta) - prior_boxes = raw_outputs[self.priorbox_out][0][0].reshape(-1, 4) - prior_variances = raw_outputs[self.priorbox_out][0][1].reshape(-1, 4) - for batch_id, identifier in enumerate(identifiers): - labels, class_scores, x_mins, y_mins, x_maxs, y_maxs, main_scores = self.prepare_detection_for_id( - batch_id, raw_outputs, prior_boxes, prior_variances - ) - action_prediction = DetectionPrediction(identifier, labels, class_scores, x_mins, y_mins, x_maxs, y_maxs) - person_prediction = DetectionPrediction( - identifier, [1] * len(labels), main_scores, x_mins, y_mins, x_maxs, y_maxs - ) - result.append(ContainerPrediction({ - 'action_prediction': action_prediction, 'class_agnostic_prediction': person_prediction - })) - - return result - - def prepare_detection_for_id(self, batch_id, raw_outputs, prior_boxes, prior_variances): - num_detections = raw_outputs[self.loc_out][batch_id].size // 4 - locs = raw_outputs[self.loc_out][batch_id].reshape(-1, 4) - main_conf = raw_outputs[self.main_conf_out][batch_id].reshape(num_detections, -1) - add_confs = list(map( - lambda layer: raw_outputs[layer][batch_id].reshape(-1, self.num_action_classes), self.add_conf_outs - )) - anchors_num = len(add_confs) - labels, class_scores, x_mins, y_mins, x_maxs, y_maxs, main_scores = [], [], [], [], [], [], [] - for index in range(num_detections): - if main_conf[index, 1] < self.detection_threshold: - continue - - x_min, y_min, x_max, y_max = self.decode_box(prior_boxes[index], prior_variances[index], locs[index]) - action_confs = add_confs[index % anchors_num][index // anchors_num] - action_label = np.argmax(action_confs) - labels.append(action_label) - class_scores.append(action_confs[action_label]) - x_mins.append(x_min) - y_mins.append(y_min) - x_maxs.append(x_max) - y_maxs.append(y_max) - main_scores.append(main_conf[index, 1]) - - return labels, class_scores, x_mins, y_mins, x_maxs, y_maxs, main_scores - - @staticmethod - def decode_box(prior, var, deltas): - prior_width = prior[2] - prior[0] - prior_height = prior[3] - prior[1] - prior_center_x = (prior[0] + prior[2]) / 2 - prior_center_y = (prior[1] + prior[3]) / 2 - - decoded_box_center_x = var[0] * deltas[0] * prior_width + prior_center_x - decoded_box_center_y = var[1] * deltas[1] * prior_height + prior_center_y - decoded_box_width = np.exp(var[2] * deltas[2]) * prior_width - decoded_box_height = np.exp(var[3] * deltas[3]) * prior_height - - decoded_xmin = decoded_box_center_x - decoded_box_width / 2 - decoded_ymin = decoded_box_center_y - decoded_box_height / 2 - decoded_xmax = decoded_box_center_x + decoded_box_width / 2 - decoded_ymax = decoded_box_center_y + decoded_box_height / 2 - - return decoded_xmin, decoded_ymin, decoded_xmax, decoded_ymax diff --git a/tools/accuracy_checker/accuracy_checker/adapters/adapter.py b/tools/accuracy_checker/accuracy_checker/adapters/adapter.py deleted file mode 100644 index 76d5ef655a83c0..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/adapters/adapter.py +++ /dev/null @@ -1,91 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from ..config import BaseField, ConfigValidator, StringField, ConfigError -from ..dependency import ClassProvider - - -class Adapter(ClassProvider): - """ - Interface that describes converting raw output to appropriate representation. - """ - - __provider_type__ = 'adapter' - - def __init__(self, launcher_config, label_map=None, output_blob=None): - self.launcher_config = launcher_config - self.output_blob = output_blob - self.label_map = label_map - - self.validate_config() - self.configure() - - def __call__(self, context=None, outputs=None, **kwargs): - if outputs is not None: - return self.process(outputs, **kwargs) - predictions = self.process(context.prediction_batch, context.identifiers_batch, **kwargs) - context.prediction_batch = predictions - return context - - def process(self, raw, identifiers=None, frame_meta=None): - raise NotImplementedError - - def configure(self): - pass - - def validate_config(self): - pass - - @staticmethod - def _extract_predictions(outputs_list, meta): - return outputs_list[0] - - -class AdapterField(BaseField): - def validate(self, entry, field_uri_=None): - super().validate(entry, field_uri_) - - if entry is None: - return - - field_uri_ = field_uri_ or self.field_uri - if isinstance(entry, str): - StringField(choices=Adapter.providers).validate(entry, 'adapter') - elif isinstance(entry, dict): - class DictAdapterValidator(ConfigValidator): - type = StringField(choices=Adapter.providers) - dict_adapter_validator = DictAdapterValidator( - 'adapter', on_extra_argument=DictAdapterValidator.IGNORE_ON_EXTRA_ARGUMENT - ) - dict_adapter_validator.validate(entry) - else: - self.raise_error(entry, field_uri_, 'adapter must be either string or dictionary') - - -def create_adapter(adapter_config, launcher, dataset=None): - if isinstance(adapter_config, str): - adapter = Adapter.provide(adapter_config, launcher.config) - elif isinstance(adapter_config, dict): - adapter = Adapter.provide(adapter_config['type'], adapter_config) - else: - raise ConfigError('Unknown type for adapter configuration') - adapter.output_blob = launcher.output_blob - if dataset: - metadata = dataset.metadata - if metadata: - adapter.label_map = dataset.metadata.get('label_map') - - return adapter diff --git a/tools/accuracy_checker/accuracy_checker/adapters/attributes_recognition.py b/tools/accuracy_checker/accuracy_checker/adapters/attributes_recognition.py deleted file mode 100644 index a166b23111147f..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/adapters/attributes_recognition.py +++ /dev/null @@ -1,211 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from ..adapters import Adapter -from ..config import ConfigValidator, StringField -from ..representation import ( - ContainerPrediction, - RegressionPrediction, - ClassificationPrediction, - FacialLandmarksPrediction, - MultiLabelRecognitionPrediction, - GazeVectorPrediction -) - - -class HeadPoseEstimatorAdapterConfig(ConfigValidator): - type = StringField() - angle_yaw = StringField() - angle_pitch = StringField() - angle_roll = StringField() - - -class HeadPoseEstimatorAdapter(Adapter): - """ - Class for converting output of HeadPoseEstimator to HeadPosePrediction representation - """ - __provider__ = 'head_pose' - - def validate_config(self): - head_pose_estimator_adapter_config = HeadPoseEstimatorAdapterConfig( - 'HeadPoseEstimator_Config', on_extra_argument=HeadPoseEstimatorAdapterConfig.ERROR_ON_EXTRA_ARGUMENT) - head_pose_estimator_adapter_config.validate(self.launcher_config) - - def configure(self): - """ - Specifies parameters of config entry - """ - self.angle_yaw = self.launcher_config['angle_yaw'] - self.angle_pitch = self.launcher_config['angle_pitch'] - self.angle_roll = self.launcher_config['angle_roll'] - - def process(self, raw, identifiers=None, frame_meta=None): - """ - Args: - identifiers: list of input data identifiers - raw: output of model - frame_meta: list of meta information about each frame - Returns: - list of ContainerPrediction objects - """ - result = [] - raw_output = self._extract_predictions(raw, frame_meta) - for identifier, yaw, pitch, roll in zip( - identifiers, - raw_output[self.angle_yaw], - raw_output[self.angle_pitch], - raw_output[self.angle_roll] - ): - prediction = ContainerPrediction({'angle_yaw': RegressionPrediction(identifier, yaw[0]), - 'angle_pitch': RegressionPrediction(identifier, pitch[0]), - 'angle_roll': RegressionPrediction(identifier, roll[0])}) - result.append(prediction) - - return result - - -class VehicleAttributesRecognitionAdapterConfig(ConfigValidator): - type = StringField() - color_out = StringField() - type_out = StringField() - - -class VehicleAttributesRecognitionAdapter(Adapter): - __provider__ = 'vehicle_attributes' - - def validate_config(self): - attributes_recognition_adapter_config = VehicleAttributesRecognitionAdapterConfig( - 'VehicleAttributesRecognition_Config', - on_extra_argument=VehicleAttributesRecognitionAdapterConfig.ERROR_ON_EXTRA_ARGUMENT) - attributes_recognition_adapter_config.validate(self.launcher_config) - - def configure(self): - """ - Specifies parameters of config entry - """ - self.color_out = self.launcher_config['color_out'] - self.type_out = self.launcher_config['type_out'] - - def process(self, raw, identifiers=None, frame_meta=None): - res = [] - raw_output = self._extract_predictions(raw, frame_meta) - for identifier, colors, types in zip(identifiers, raw_output[self.color_out], raw_output[self.type_out]): - res.append(ContainerPrediction({'color': ClassificationPrediction(identifier, colors.reshape(-1)), - 'type': ClassificationPrediction(identifier, types.reshape(-1))})) - return res - - -class AgeGenderAdapterConfig(ConfigValidator): - type = StringField() - age_out = StringField() - gender_out = StringField() - - -class AgeGenderAdapter(Adapter): - __provider__ = 'age_gender' - - def configure(self): - self.age_out = self.launcher_config['age_out'] - self.gender_out = self.launcher_config['gender_out'] - - def validate_config(self): - age_gender_adapter_config = AgeGenderAdapterConfig( - 'AgeGender_Config', on_extra_argument=AgeGenderAdapterConfig.ERROR_ON_EXTRA_ARGUMENT) - age_gender_adapter_config.validate(self.launcher_config) - - @staticmethod - def get_age_scores(age): - age_scores = np.zeros(4) - if age < 19: - age_scores[0] = 1 - return age_scores - if age < 36: - age_scores[1] = 1 - return age_scores - if age < 66: - age_scores[2] = 1 - return age_scores - age_scores[3] = 1 - return age_scores - - def process(self, raw, identifiers=None, frame_meta=None): - result = [] - raw_output = self._extract_predictions(raw, frame_meta) - for identifier, age, gender in zip(identifiers, raw_output[self.age_out], raw_output[self.gender_out]): - gender = gender.reshape(-1) - age = age.reshape(-1)[0]*100 - gender_rep = ClassificationPrediction(identifier, gender) - age_class_rep = ClassificationPrediction(identifier, self.get_age_scores(age)) - age_error_rep = RegressionPrediction(identifier, age) - result.append(ContainerPrediction({'gender': gender_rep, 'age_classification': age_class_rep, - 'age_error': age_error_rep})) - return result - - -class LandmarksRegressionAdapter(Adapter): - __provider__ = 'landmarks_regression' - - def process(self, raw, identifiers=None, frame_meta=None): - res = [] - raw_output = self._extract_predictions(raw, frame_meta) - for identifier, values in zip(identifiers, raw_output[self.output_blob]): - x_values, y_values = values[::2], values[1::2] - res.append(FacialLandmarksPrediction(identifier, x_values.reshape(-1), y_values.reshape(-1))) - return res - - -class PersonAttributesConfig(ConfigValidator): - attributes_recognition_out = StringField(optional=True) - - -class PersonAttributesAdapter(Adapter): - __provider__ = 'person_attributes' - - def validate_config(self): - person_attributes_adapter_config = PersonAttributesConfig( - 'PersonAttributes_Config', - PersonAttributesConfig.IGNORE_ON_EXTRA_ARGUMENT - ) - person_attributes_adapter_config.validate(self.launcher_config) - - def configure(self): - self.attributes_recognition_out = self.launcher_config.get('attributes_recognition_out', self.output_blob) - - def process(self, raw, identifiers=None, frame_meta=None): - result = [] - raw_output = self._extract_predictions(raw, frame_meta) - self.attributes_recognition_out = self.attributes_recognition_out or self.output_blob - for identifier, multi_label in zip(identifiers, raw_output[self.attributes_recognition_out]): - multi_label[multi_label > 0.5] = 1. - multi_label[multi_label <= 0.5] = 0. - - result.append(MultiLabelRecognitionPrediction(identifier, multi_label.reshape(-1))) - - return result - - -class GazeEstimationAdapter(Adapter): - __provider__ = 'gaze_estimation' - - def process(self, raw, identifiers=None, frame_meta=None): - result = [] - raw_output = self._extract_predictions(raw, frame_meta) - for identifier, output in zip(identifiers, raw_output[self.output_blob]): - result.append(GazeVectorPrediction(identifier, output)) - - return result diff --git a/tools/accuracy_checker/accuracy_checker/adapters/classification.py b/tools/accuracy_checker/accuracy_checker/adapters/classification.py deleted file mode 100644 index ddcf267b61b41f..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/adapters/classification.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from ..adapters import Adapter -from ..representation import ClassificationPrediction - - -class ClassificationAdapter(Adapter): - """ - Class for converting output of classification model to ClassificationPrediction representation - """ - __provider__ = 'classification' - - def process(self, raw, identifiers=None, frame_meta=None): - """ - Args: - identifiers: list of input data identifiers - raw: output of model - frame_meta: list of meta information about each frame - Returns: - list of ClassificationPrediction objects - """ - prediction = self._extract_predictions(raw, frame_meta)[self.output_blob] - prediction = np.reshape(prediction, (prediction.shape[0], -1)) - - result = [] - for identifier, output in zip(identifiers, prediction): - result.append(ClassificationPrediction(identifier, output)) - - return result diff --git a/tools/accuracy_checker/accuracy_checker/adapters/detection.py b/tools/accuracy_checker/accuracy_checker/adapters/detection.py deleted file mode 100644 index 8f36d13ff33a5c..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/adapters/detection.py +++ /dev/null @@ -1,501 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import itertools -import math - -import numpy as np - -from ..adapters import Adapter -from ..config import ConfigValidator, NumberField, StringField, ListField -from ..postprocessor.nms import NMS -from ..representation import DetectionPrediction, ContainerPrediction -from ..utils import get_or_parse_value - - -class TinyYOLOv1Adapter(Adapter): - """ - Class for converting output of Tiny YOLO v1 model to DetectionPrediction representation - """ - __provider__ = 'tiny_yolo_v1' - - def process(self, raw, identifiers=None, frame_meta=None): - """ - Args: - identifiers: list of input data identifiers - raw: output of model - Returns: - list of DetectionPrediction objects - """ - prediction = self._extract_predictions(raw, frame_meta)[self.output_blob] - - PROBABILITY_SIZE = 980 - CONFIDENCE_SIZE = 98 - BOXES_SIZE = 392 - - CELLS_X, CELLS_Y = 7, 7 - CLASSES = 20 - OBJECTS_PER_CELL = 2 - - result = [] - for identifier, output in zip(identifiers, prediction): - assert PROBABILITY_SIZE + CONFIDENCE_SIZE + BOXES_SIZE == output.shape[0] - - probability, scale, boxes = np.split(output, [PROBABILITY_SIZE, PROBABILITY_SIZE + CONFIDENCE_SIZE]) - - probability = np.reshape(probability, (CELLS_Y, CELLS_X, CLASSES)) - scale = np.reshape(scale, (CELLS_Y, CELLS_X, OBJECTS_PER_CELL)) - boxes = np.reshape(boxes, (CELLS_Y, CELLS_X, OBJECTS_PER_CELL, 4)) - - confidence = np.zeros((CELLS_Y, CELLS_X, OBJECTS_PER_CELL, CLASSES + 4)) - for cls in range(CLASSES): - confidence[:, :, 0, cls] = np.multiply(probability[:, :, cls], scale[:, :, 0]) - confidence[:, :, 1, cls] = np.multiply(probability[:, :, cls], scale[:, :, 1]) - - labels, scores, x_mins, y_mins, x_maxs, y_maxs = [], [], [], [], [], [] - for i, j, k in np.ndindex((CELLS_X, CELLS_Y, OBJECTS_PER_CELL)): - box = boxes[j, i, k] - box = [(box[0] + i) / float(CELLS_X), (box[1] + j) / float(CELLS_Y), box[2] ** 2, box[3] ** 2] - - label = np.argmax(confidence[j, i, k, :CLASSES]) - score = confidence[j, i, k, label] - - labels.append(label) - scores.append(score) - x_mins.append(box[0] - box[2] / 2.0) - y_mins.append(box[1] - box[3] / 2.0) - x_maxs.append(box[0] + box[2] / 2.0) - y_maxs.append(box[1] + box[3] / 2.0) - - result.append(DetectionPrediction(identifier, labels, scores, x_mins, y_mins, x_maxs, y_maxs)) - - return result - - -PRECOMPUTED_ANCHORS = { - 'yolo_v2': [1.3221, 1.73145, 3.19275, 4.00944, 5.05587, 8.09892, 9.47112, 4.84053, 11.2364, 10.0071], - 'tiny_yolo_v2': [1.08, 1.19, 3.42, 4.41, 6.63, 11.38, 9.42, 5.11, 16.62, 10.52], - 'yolo_v3': [ - 10.0, 13.0, 16.0, 30.0, 33.0, 23.0, 30.0, 61.0, 62.0, 45.0, 59.0, 119.0, 116.0, 90.0, 156.0, 198.0, 373.0, 326.0 - ], - 'tiny_yolo_v3': [10.0, 14.0, 23.0, 27.0, 37.0, 58.0, 81.0, 82.0, 135.0, 169.0, 344.0, 319.0] -} - - -def entry_index(w, h, n_coords, n_classes, pos, entry): - row = pos // (w * h) - col = pos % (w * h) - return row * w * h * (n_classes + n_coords + 1) + entry * w * h + col - - -class BaseYoloAdapterConfig(ConfigValidator): - classes = NumberField(floats=False, optional=True, min_value=1) - coords = NumberField(floats=False, optional=True, min_value=1) - num = NumberField(floats=False, optional=True, min_value=1) - anchors = StringField(optional=True) - - -class YoloV2Adapter(Adapter): - """ - Class for converting output of YOLO v2 family models to DetectionPrediction representation - """ - __provider__ = 'yolo_v2' - - def validate_config(self): - yolo_v2_adapter_config = BaseYoloAdapterConfig('BaseYoloAdapter_Config') - yolo_v2_adapter_config.validate(self.launcher_config) - - def configure(self): - self.classes = self.launcher_config.get('classes', 20) - self.coords = self.launcher_config.get('coords', 4) - self.num = self.launcher_config.get('num', 5) - self.anchors = get_or_parse_value(self.launcher_config.get('anchors', 'yolo_v2'), PRECOMPUTED_ANCHORS) - - def process(self, raw, identifiers=None, frame_meta=None): - """ - Args: - identifiers: list of input data identifiers - raw: output of model - Returns: - list of DetectionPrediction objects - """ - predictions = self._extract_predictions(raw, frame_meta)[self.output_blob] - - cells_x, cells_y = 13, 13 - - result = [] - for identifier, prediction in zip(identifiers, predictions): - labels, scores, x_mins, y_mins, x_maxs, y_maxs = [], [], [], [], [], [] - for y, x, n in np.ndindex((cells_y, cells_x, self.num)): - index = n * cells_y * cells_x + y * cells_x + x - - box_index = entry_index(cells_x, cells_y, self.coords, self.classes, index, 0) - obj_index = entry_index(cells_x, cells_y, self.coords, self.classes, index, self.coords) - - scale = prediction[obj_index] - - box = [ - (x + prediction[box_index + 0 * (cells_y * cells_x)]) / cells_x, - (y + prediction[box_index + 1 * (cells_y * cells_x)]) / cells_y, - np.exp(prediction[box_index + 2 * (cells_y * cells_x)]) * self.anchors[2 * n + 0] / cells_x, - np.exp(prediction[box_index + 3 * (cells_y * cells_x)]) * self.anchors[2 * n + 1] / cells_y - ] - - classes_prob = np.empty(self.classes) - for cls in range(self.classes): - cls_index = entry_index(cells_x, cells_y, self.coords, self.classes, index, self.coords + 1 + cls) - classes_prob[cls] = prediction[cls_index] - - classes_prob = classes_prob * scale - - label = np.argmax(classes_prob) - - labels.append(label) - scores.append(classes_prob[label]) - x_mins.append(box[0] - box[2] / 2.0) - y_mins.append(box[1] - box[3] / 2.0) - x_maxs.append(box[0] + box[2] / 2.0) - y_maxs.append(box[1] + box[3] / 2.0) - - result.append(DetectionPrediction(identifier, labels, scores, x_mins, y_mins, x_maxs, y_maxs)) - - return result - - -class YoloV3AdapterConfig(BaseYoloAdapterConfig): - threshold = NumberField(floats=True, optional=True, min_value=0) - outputs = ListField(optional=True) - - -class YoloV3Adapter(Adapter): - """ - Class for converting output of YOLO v3 family models to DetectionPrediction representation - """ - __provider__ = 'yolo_v3' - - def validate_config(self): - yolo_v3_adapter_config = YoloV3AdapterConfig('YoloV3Adapter_Config') - yolo_v3_adapter_config.validate(self.launcher_config) - - def configure(self): - self.classes = self.launcher_config.get('classes', 80) - self.coords = self.launcher_config.get('coords', 4) - self.num = self.launcher_config.get('num', 3) - self.anchors = get_or_parse_value(self.launcher_config.get('anchors', 'yolo_v3'), PRECOMPUTED_ANCHORS) - self.threshold = self.launcher_config.get('threshold', 0.001) - self.outputs = self.launcher_config.get('outputs', []) - - def process(self, raw, identifiers=None, frame_meta=None): - """ - Args: - identifiers: list of input data identifiers - raw: output of model - Returns: - list of DetectionPrediction objects - """ - - def get_anchors_offset(x): - return int((self.num * 2) * (len(self.anchors) / (self.num * 2) - 1 - math.log2(x / 13))) - - def parse_yolo_v3_results(prediction, threshold, w, h, det): - cells_x, cells_y = prediction.shape[1:] - prediction = prediction.flatten() - for y, x, n in np.ndindex((cells_y, cells_x, self.num)): - index = n * cells_y * cells_x + y * cells_x + x - anchors_offset = get_anchors_offset(cells_x) - - box_index = entry_index(cells_x, cells_y, self.coords, self.classes, index, 0) - obj_index = entry_index(cells_x, cells_y, self.coords, self.classes, index, self.coords) - - scale = prediction[obj_index] - if scale < threshold: - continue - - box = [ - (x + prediction[box_index + 0 * (cells_y * cells_x)]) / cells_x, - (y + prediction[box_index + 1 * (cells_y * cells_x)]) / cells_y, - np.exp(prediction[box_index + 2 * (cells_y * cells_x)]) * self.anchors[ - anchors_offset + 2 * n + 0] / w, - np.exp(prediction[box_index + 3 * (cells_y * cells_x)]) * self.anchors[ - anchors_offset + 2 * n + 1] / h - ] - - classes_prob = np.empty(self.classes) - for cls in range(self.classes): - cls_index = entry_index(cells_x, cells_y, self.coords, self.classes, index, - self.coords + 1 + cls) - classes_prob[cls] = prediction[cls_index] * scale - - det['labels'].append(cls) - det['scores'].append(classes_prob[cls]) - det['x_mins'].append(box[0] - box[2] / 2.0) - det['y_mins'].append(box[1] - box[3] / 2.0) - det['x_maxs'].append(box[0] + box[2] / 2.0) - det['y_maxs'].append(box[1] + box[3] / 2.0) - - return det - - result = [] - - raw_outputs = self._extract_predictions(raw, frame_meta) - - if self.outputs: - outputs = self.outputs - else: - outputs = raw_outputs.keys() - - batch = len(identifiers) - predictions = [[] for _ in range(batch)] - for blob in outputs: - for b in range(batch): - predictions[b].append(raw_outputs[blob][b]) - - for identifier, prediction, meta in zip(identifiers, predictions, frame_meta): - detections = {'labels': [], 'scores': [], 'x_mins': [], 'y_mins': [], 'x_maxs': [], 'y_maxs': []} - input_shape = list(meta.get('input_shape', {'data': (3, 416, 416)}).values())[0] - self.input_width = input_shape[2] - self.input_height = input_shape[1] - - for p in prediction: - parse_yolo_v3_results(p, self.threshold, self.input_width, self.input_height, detections) - - result.append(DetectionPrediction( - identifier, detections['labels'], detections['scores'], detections['x_mins'], detections['y_mins'], - detections['x_maxs'], detections['y_maxs'] - )) - - return result - - -class SSDAdapter(Adapter): - """ - Class for converting output of SSD model to DetectionPrediction representation - """ - __provider__ = 'ssd' - - def process(self, raw, identifiers=None, frame_meta=None): - """ - Args: - identifiers: list of input data identifiers - raw: output of model - Returns: - list of DetectionPrediction objects - """ - raw_outputs = self._extract_predictions(raw, frame_meta) - prediction_batch = raw_outputs[self.output_blob] - prediction_count = prediction_batch.shape[2] - prediction_batch = prediction_batch.reshape(prediction_count, -1) - prediction_batch = self.remove_empty_detections(prediction_batch) - - result = [] - for batch_index, identifier in enumerate(identifiers): - prediction_mask = np.where(prediction_batch[:, 0] == batch_index) - detections = prediction_batch[prediction_mask] - detections = detections[:, 1::] - result.append(DetectionPrediction(identifier, *zip(*detections))) - - return result - - @staticmethod - def remove_empty_detections(prediction_blob): - ind = prediction_blob[:, 0] - ind_ = np.where(ind == -1)[0] - m = ind_[0] if ind_.size else prediction_blob.shape[0] - return prediction_blob[:m, :] - - -class PyTorchSSDDecoderConfig(ConfigValidator): - type = StringField() - scores_out = StringField() - boxes_out = StringField() - confidence_threshold = NumberField(optional=True) - nms_threshold = NumberField(optional=True) - keep_top_k = NumberField(optional=True, floats=False) - - -class PyTorchSSDDecoder(Adapter): - """ - Class for converting output of PyTorch SSD models to DetectionPrediction representation - """ - __provider__ = 'pytorch_ssd_decoder' - - def validate_config(self): - config_validator = PyTorchSSDDecoderConfig( - 'PyTorchSSD_decoder_config', PyTorchSSDDecoderConfig.ERROR_ON_EXTRA_ARGUMENT - ) - - config_validator.validate(self.launcher_config) - - def configure(self): - self.scores_out = self.launcher_config['scores_out'] - self.boxes_out = self.launcher_config['boxes_out'] - self.confidence_threshold = self.launcher_config.get('confidence_threshold', 0.05) - self.nms_threshold = self.launcher_config.get('nms_threshold', 0.5) - self.keep_top_k = self.launcher_config.get('keep_top_k', 200) - - # Set default values according to: - # https://github.com/mlperf/inference/tree/master/cloud/single_stage_detector - self.aspect_ratios = [[2], [2, 3], [2, 3], [2, 3], [2], [2]] - self.feat_size = [[50, 50], [25, 25], [13, 13], [7, 7], [3, 3], [3, 3]] - self.scales = [21, 45, 99, 153, 207, 261, 315] - self.strides = [3, 3, 2, 2, 2, 2] - self.scale_xy = 0.1 - self.scale_wh = 0.2 - - @staticmethod - def softmax(x, axis=0): - return np.transpose(np.transpose(np.exp(x)) * np.reciprocal(np.sum(np.exp(x), axis=axis))) - - @staticmethod - def default_boxes(fig_size, feat_size, scales, aspect_ratios): - - fig_size_w, fig_size_h = fig_size - scales = [(int(s * fig_size_w / 300), int(s * fig_size_h / 300)) for s in scales] - fkw, fkh = np.transpose(feat_size) - - default_boxes = [] - for idx, sfeat in enumerate(feat_size): - sfeat_w, sfeat_h = sfeat - sk1 = scales[idx][0] / fig_size_w - sk2 = scales[idx + 1][1] / fig_size_h - sk3 = math.sqrt(sk1 * sk2) - all_sizes = [(sk1, sk1), (sk3, sk3)] - for alpha in aspect_ratios[idx]: - w, h = sk1 * math.sqrt(alpha), sk1 / math.sqrt(alpha) - all_sizes.append((w, h)) - all_sizes.append((h, w)) - for w, h in all_sizes: - for i, j in itertools.product(range(sfeat_w), range(sfeat_h)): - cx, cy = (j + 0.5) / fkh[idx], (i + 0.5) / fkw[idx] - default_boxes.append((cx, cy, w, h)) - default_boxes = np.clip(default_boxes, 0, 1) - - return default_boxes - - def process(self, raw, identifiers=None, frame_meta=None): - """ - Args: - identifiers: list of input data identifiers - raw: output of model - Returns: - list of DetectionPrediction objects - """ - raw_outputs = self._extract_predictions(raw, frame_meta) - - batch_scores = raw_outputs[self.scores_out] - batch_boxes = raw_outputs[self.boxes_out] - - result = [] - for identifier, scores, boxes, meta in zip(identifiers, batch_scores, batch_boxes, frame_meta): - detections = {'labels': [], 'scores': [], 'x_mins': [], 'y_mins': [], 'x_maxs': [], 'y_maxs': []} - image_info = meta.get("image_size")[0:2] - - # Default boxes - dboxes = self.default_boxes(image_info, self.feat_size, self.scales, self.aspect_ratios) - - # Scores - scores = np.transpose(scores) - scores = self.softmax(scores, axis=1) - - # Boxes - boxes = np.transpose(boxes) - boxes[:, :2] = self.scale_xy * boxes[:, :2] - boxes[:, 2:] = self.scale_wh * boxes[:, 2:] - boxes[:, :2] = boxes[:, :2] * dboxes[:, 2:] + dboxes[:, :2] - boxes[:, 2:] = np.exp(boxes[:, 2:]) * dboxes[:, 2:] - - for label, score in enumerate(np.transpose(scores)): - - # Skip background label - if label == 0: - continue - - # Filter out detections with score < confidence_threshold - mask = score > self.confidence_threshold - filtered_boxes, filtered_score = boxes[mask, :], score[mask] - if filtered_score.size == 0: - continue - - # Transform to format (x_min, y_min, x_max, y_max) - x_mins = (filtered_boxes[:, 0] - 0.5 * filtered_boxes[:, 2]) - y_mins = (filtered_boxes[:, 1] - 0.5 * filtered_boxes[:, 3]) - x_maxs = (filtered_boxes[:, 0] + 0.5 * filtered_boxes[:, 2]) - y_maxs = (filtered_boxes[:, 1] + 0.5 * filtered_boxes[:, 3]) - - # Apply NMS - keep = NMS.nms(x_mins, y_mins, x_maxs, y_maxs, filtered_score, self.nms_threshold, - include_boundaries=False, keep_top_k=self.keep_top_k) - - filtered_score = filtered_score[keep] - x_mins = x_mins[keep] - y_mins = y_mins[keep] - x_maxs = x_maxs[keep] - y_maxs = y_maxs[keep] - - # Keep topK - # Applied just after NMS - no additional sorting is required for filtered_score array - filtered_score = filtered_score[:self.keep_top_k] - x_mins = x_mins[:self.keep_top_k] - y_mins = y_mins[:self.keep_top_k] - x_maxs = x_maxs[:self.keep_top_k] - y_maxs = y_maxs[:self.keep_top_k] - - # Save detections - labels = np.full_like(filtered_score, label) - detections['labels'].extend(labels) - detections['scores'].extend(filtered_score) - detections['x_mins'].extend(x_mins) - detections['y_mins'].extend(y_mins) - detections['x_maxs'].extend(x_maxs) - detections['y_maxs'].extend(y_maxs) - - result.append( - DetectionPrediction( - identifier, detections['labels'], detections['scores'], detections['x_mins'], - detections['y_mins'], detections['x_maxs'], detections['y_maxs'] - ) - ) - - return result - - -class FacePersonDetectionAdapterConfig(ConfigValidator): - type = StringField() - face_out = StringField() - person_out = StringField() - - -class FacePersonAdapter(Adapter): - __provider__ = 'face_person_detection' - - def validate_config(self): - face_person_detection_adapter_config = FacePersonDetectionAdapterConfig( - 'FacePersonDetection_Config', on_extra_argument=FacePersonDetectionAdapterConfig.ERROR_ON_EXTRA_ARGUMENT) - face_person_detection_adapter_config.validate(self.launcher_config) - - def configure(self): - self.face_detection_out = self.launcher_config['face_out'] - self.person_detection_out = self.launcher_config['person_out'] - self.face_adapter = SSDAdapter(self.launcher_config, self.label_map, self.face_detection_out) - self.person_adapter = SSDAdapter(self.launcher_config, self.label_map, self.person_detection_out) - - def process(self, raw, identifiers=None, frame_meta=None): - face_batch_result = self.face_adapter.process(raw, identifiers) - person_batch_result = self.person_adapter.process(raw, identifiers) - result = [ContainerPrediction({self.face_detection_out: face_result, self.person_detection_out: person_result}) - for face_result, person_result in zip(face_batch_result, person_batch_result)] - - return result diff --git a/tools/accuracy_checker/accuracy_checker/adapters/dummy_adapters.py b/tools/accuracy_checker/accuracy_checker/adapters/dummy_adapters.py deleted file mode 100644 index 300dec93fe1311..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/adapters/dummy_adapters.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from ..representation import DetectionPrediction -from ..adapters import Adapter - - -class XML2DetectionAdapter(Adapter): - """ - Class for converting xml detection results in OpenCV FileStorage format to DetectionPrediction representation. - """ - - __provider__ = 'xml_detection' - - def process(self, tree, identifiers=None, frame_meta=None): - class_to_ind = dict(zip(self.label_map.values(), range(len(self.label_map.values())))) - - result = {} - for frames in tree.getroot(): - for frame in frames: - identifier = frame.tag + '.png' - labels, scores, x_mins, y_mins, x_maxs, y_maxs = [], [], [], [], [], [] - for prediction in frame: - if prediction.find('is_ignored'): - continue - - label = prediction.find('type') - if not label: - raise ValueError('Detection predictions contains detection without "{}"'.format('type')) - label = class_to_ind[label.text] - - confidence = prediction.find('confidence') - if confidence is None: - raise ValueError('Detection predictions contains detection without "{}"'.format('confidence')) - confidence = float(confidence.text) - - box = prediction.find('roi') - if not box: - raise ValueError('Detection predictions contains detection without "{}"'.format('roi')) - box = list(map(float, box.text.split())) - - labels.append(label) - scores.append(confidence) - x_mins.append(box[0]) - y_mins.append(box[1]) - x_maxs.append(box[0] + box[2]) - y_maxs.append(box[1] + box[3]) - - result[identifier] = DetectionPrediction(identifier, labels, scores, x_mins, y_mins, x_maxs, y_maxs) - - return result diff --git a/tools/accuracy_checker/accuracy_checker/adapters/hit_ratio.py b/tools/accuracy_checker/accuracy_checker/adapters/hit_ratio.py deleted file mode 100644 index f28b84f7813f2d..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/adapters/hit_ratio.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from ..adapters import Adapter -from ..representation import HitRatioPrediction - - -class HitRatioAdapter(Adapter): - """ - Class for converting output of NCF model to HitRatioPrediction representation. - """ - - __provider__ = 'hit_ratio_adapter' - - def process(self, raw, identifiers=None, frame_meta=None): - """ - Args: - raw: output of model. - identifiers: list of input data identifiers. - frame_meta: metadata for frame. - Returns: - list of HitRatioPrediction objects. - """ - - prediction = self._extract_predictions(raw, frame_meta)[self.output_blob] - prediction = np.reshape(prediction, -1) - - result = [] - for identifier, output in zip(identifiers, prediction): - result.append(HitRatioPrediction(identifier, output)) - - return result diff --git a/tools/accuracy_checker/accuracy_checker/adapters/image_processing.py b/tools/accuracy_checker/accuracy_checker/adapters/image_processing.py deleted file mode 100644 index 21ecec305faedd..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/adapters/image_processing.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from ..adapters import Adapter -from ..representation import SuperResolutionPrediction - - -class SuperResolutionAdapter(Adapter): - __provider__ = 'super_resolution' - - def process(self, raw, identifiers=None, frame_meta=None): - result = [] - raw_outputs = self._extract_predictions(raw, frame_meta) - for identifier, img_sr in zip(identifiers, raw_outputs[self.output_blob]): - img_sr *= 255 - img_sr = np.clip(img_sr, 0., 255.) - img_sr = img_sr.transpose((1, 2, 0)).astype(np.uint8) - result.append(SuperResolutionPrediction(identifier, img_sr)) - - return result diff --git a/tools/accuracy_checker/accuracy_checker/adapters/pose_estimation.py b/tools/accuracy_checker/accuracy_checker/adapters/pose_estimation.py deleted file mode 100644 index 25350f5557d7c8..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/adapters/pose_estimation.py +++ /dev/null @@ -1,331 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import math -from operator import itemgetter - -import cv2 -import numpy as np - -from ..adapters import Adapter -from ..config import ConfigValidator, StringField -from ..representation import PoseEstimationPrediction - - -class HumanPoseAdapterConfig(ConfigValidator): - type = StringField() - part_affinity_fields_out = StringField() - keypoints_heatmap_out = StringField() - - -class HumanPoseAdapter(Adapter): - __provider__ = 'human_pose_estimation' - - limb_seq = [ - [2, 3], [2, 6], [3, 4], [4, 5], [6, 7], [7, 8], [2, 9], [9, 10], [10, 11], [2, 12], [12, 13], - [13, 14], [2, 1], [1, 15], [15, 17], [1, 16], [16, 18], [3, 17], [6, 18] - ] - map_idx = [ - [31, 32], [39, 40], [33, 34], [35, 36], [41, 42], [43, 44], [19, 20], [21, 22], [23, 24], [25, 26], - [27, 28], [29, 30], [47, 48], [49, 50], [53, 54], [51, 52], [55, 56], [37, 38], [45, 46] - ] - - def validate_config(self): - human_pose_estimation_config = HumanPoseAdapterConfig('HumanPose_Config') - human_pose_estimation_config.validate(self.launcher_config) - - def configure(self): - self.part_affinity_fields = self.launcher_config['part_affinity_fields_out'] - self.keypoints_heatmap = self.launcher_config['keypoints_heatmap_out'] - - def process(self, raw, identifiers=None, frame_meta=None): - result = [] - raw_outputs = self._extract_predictions(raw, frame_meta) - raw_output = zip( - identifiers, raw_outputs[self.keypoints_heatmap], - raw_outputs[self.part_affinity_fields], frame_meta - ) - for identifier, heatmap, paf, meta in raw_output: - height, width, _ = meta['image_size'] - heatmap_avg = np.zeros((height, width, 19), dtype=np.float32) - paf_avg = np.zeros((height, width, 38), dtype=np.float32) - pad = meta.get('padding', [0, 0, 0, 0]) - heatmap = np.transpose(np.squeeze(heatmap), (1, 2, 0)) - heatmap = cv2.resize(heatmap, (0, 0), fx=8, fy=8, interpolation=cv2.INTER_CUBIC) - heatmap = heatmap[pad[0]:heatmap.shape[0] - pad[2], pad[1]:heatmap.shape[1] - pad[3]:, :] - heatmap = cv2.resize(heatmap, (width, height), interpolation=cv2.INTER_CUBIC) - heatmap_avg = heatmap_avg + heatmap - - paf = np.transpose(np.squeeze(paf), (1, 2, 0)) - paf = cv2.resize(paf, (0, 0), fx=8, fy=8, interpolation=cv2.INTER_CUBIC) - paf = paf[pad[0]:paf.shape[0] - pad[2], pad[1]:paf.shape[1] - pad[3], :] - paf = cv2.resize(paf, (width, height), interpolation=cv2.INTER_CUBIC) - paf_avg = paf_avg + paf - - peak_counter = 0 - all_peaks = [] - for part in range(0, 18): # 19th for bg - peak_counter += self.find_peaks(heatmap_avg[:, :, part], all_peaks, peak_counter) - - subset, candidate = self.group_peaks(all_peaks, paf_avg) - result.append(PoseEstimationPrediction(identifier, *self.get_poses(subset, candidate))) - - return result - - @staticmethod - def find_peaks(heatmap, all_peaks, prev_peak_counter): - heatmap[heatmap < 0.1] = 0 - map_aug = np.zeros((heatmap.shape[0] + 2, heatmap.shape[1] + 2)) - map_left = np.zeros(map_aug.shape) - map_right = np.zeros(map_aug.shape) - map_up = np.zeros(map_aug.shape) - map_down = np.zeros(map_aug.shape) - - map_aug[1:map_aug.shape[0] - 1, 1:map_aug.shape[1] - 1] = heatmap - map_left[1:map_aug.shape[0] - 1, :map_aug.shape[1] - 2] = heatmap - map_right[1:map_aug.shape[0] - 1, 2:map_aug.shape[1]] = heatmap - map_up[:map_aug.shape[0] - 2, 1:map_aug.shape[1] - 1] = heatmap - map_down[2:map_aug.shape[0], 1:map_aug.shape[1] - 1] = heatmap - - peaks_binary = (map_aug > map_left) & (map_aug > map_right) & (map_aug > map_up) & (map_aug > map_down) - peaks_binary = peaks_binary[1:map_aug.shape[0] - 1, 1:map_aug.shape[1] - 1] - peaks = list(zip(np.nonzero(peaks_binary)[1], np.nonzero(peaks_binary)[0])) - peaks = sorted(peaks, key=itemgetter(0)) # same order with matlab - - flag = np.ones(len(peaks), np.uint8) - peaks_with_score_and_id = [] - peak_counter = 0 - for i, _ in enumerate(peaks): - if flag[i] != 1: - continue - for j in range(i + 1, len(peaks)): - if math.sqrt((peaks[i][0] - peaks[j][0]) ** 2 + (peaks[i][1] - peaks[j][1]) ** 2) < 6: - flag[j] = 0 - peak_id = peak_counter + prev_peak_counter - peak_counter += 1 - peaks_with_score_and_id.append([peaks[i][0], peaks[i][1], heatmap[peaks[i][1], peaks[i][0]], peak_id]) - all_peaks.append(peaks_with_score_and_id) - - return peak_counter - - @staticmethod - def _add_pose_single_candidate(subset, candidate, idx_joint, kpt_num=20): - for joint in candidate: - num = 0 - for subset_j in subset: # check if already in some pose, was added as a part of another limb - if subset_j[idx_joint] == joint[3]: - num += 1 - continue - if num == 0: - person_keypoints = np.ones(kpt_num) * -1 - person_keypoints[idx_joint] = joint[3] # joint idx - person_keypoints[-1] = 1 # n joints in pose - person_keypoints[-2] = joint[2] # pose score - subset.append(person_keypoints) - - return subset - - @staticmethod - def _filter_subset(subset): - filtered_subset = [] - for subset_element in subset: - if subset_element[-1] < 3 or (subset_element[-2] / subset_element[-1] < 0.2): - continue - filtered_subset.append(subset_element) - - return np.asarray(filtered_subset) - - @staticmethod - def _add_pose_both_candidates(subset, temp, index_a, index_b, candidates, kpt_num=20): - for i, temp_i in enumerate(temp): - num = 0 - for j, subset_j in enumerate(subset): - if subset_j[index_a] == temp_i[0]: - subset[j][index_b] = temp[i][1] - num += 1 - subset[j][-1] += 1 - subset[j][-2] += candidates[temp_i[1], 2] + temp_i[2] - if num == 0: - person_keypoints = np.ones(kpt_num) * -1 - person_keypoints[index_a] = temp[i][0] - person_keypoints[index_b] = temp[i][1] - person_keypoints[-1] = 2 - person_keypoints[-2] = np.sum(candidates[temp_i[0:2], 2]) + temp_i[2] - subset.append(person_keypoints) - - return subset - - @staticmethod - def _copy_temperature_to_subset(subset, temp, index_a, index_b): - for _, temp_i in enumerate(temp): - for j, subset_j in enumerate(subset): - check_subset_a = subset_j[index_a] == temp_i[0] and subset_j[index_b] == -1 - check_subset_b = subset_j[index_b] == temp_i[1] and subset_j[index_a] == -1 - if check_subset_a: - subset[j][index_b] = temp_i[1] - continue - if check_subset_b: - subset[j][index_a] = temp_i[0] - - return subset - - @staticmethod - def _get_temperature(cand_a_, cand_b_, score_mid, pafs, threshold=0.05): - temp_ = [] - for index_a_, cand_a_element in enumerate(cand_a_): - for index_b_, cand_b_element in enumerate(cand_b_): - mid_point = [( - int(round((cand_a_element[0] + cand_b_element[0]) * 0.5)), - int(round((cand_a_element[1] + cand_b_element[1]) * 0.5)) - )] * 2 - vec = [cand_b_element[0] - cand_a_element[0], cand_b_element[1] - cand_a_element[1]] - norm_vec = math.sqrt(vec[0] ** 2 + vec[1] ** 2) - if norm_vec == 0: - continue - vec[0] /= norm_vec - vec[1] /= norm_vec - score_mid_a = score_mid[mid_point[0][1], mid_point[0][0], 0] - score_mid_b = score_mid[mid_point[1][1], mid_point[1][0], 1] - score = vec[0] * score_mid_a + vec[1] * score_mid_b - - height_n = pafs.shape[0] // 2 - suc_ratio = 0 - mid_score = 0 - mid_num = 10 # n points for integral over paf - - if score > -100: - p_sum = 0 - p_count = 0 - - x = np.linspace(cand_a_element[0], cand_b_element[0], mid_num) - y = np.linspace(cand_a_element[1], cand_b_element[1], mid_num) - for point_idx in range(0, mid_num): - px = int(round(x[point_idx])) - py = int(round(y[point_idx])) - pred = score_mid[py, px, 0:2] - score = vec[0] * pred[0] + vec[1] * pred[1] - if score > threshold: - p_sum += score - p_count += 1 - suc_ratio = p_count / mid_num - ratio = 0 - if p_count > 0: - ratio = p_sum / p_count - mid_score = ratio + min(height_n / norm_vec - 1, 0) - if mid_score > 0 and suc_ratio > 0.8: - score = mid_score - score_all = score + cand_a_element[2] + cand_b_element[2] - temp_.append([index_a_, index_b_, score, score_all]) - if temp_: - temp_ = sorted(temp_, key=itemgetter(2), reverse=True) - - return temp_ - - def _get_connections(self, cand_a, cand_b, score_mid, pafs, thresh): - temp_ = self._get_temperature(cand_a, cand_b, score_mid, pafs, thresh) - num_limbs = min(len(cand_a), len(cand_b)) - cnt = 0 - occur_a = np.zeros(len(cand_a), dtype=np.int32) - occur_b = np.zeros(len(cand_b), dtype=np.int32) - connections = [] - for row_temp in temp_: - if cnt == num_limbs: - break - i, j, score = row_temp[0:3] - if occur_a[i] == 0 and occur_b[j] == 0: - connections.append([cand_a[i][3], cand_b[j][3], score]) - cnt += 1 - occur_a[i] = 1 - occur_b[j] = 1 - return connections - - def group_peaks(self, peaks, pafs, kpt_num=20, threshold=0.05): - subset = [] - candidates = np.array([item for sublist in peaks for item in sublist]) - for keypoint_id, maped_keypoints in enumerate(self.map_idx): - score_mid = pafs[:, :, [x - 19 for x in maped_keypoints]] - candidate_a = peaks[self.limb_seq[keypoint_id][0] - 1] - candidate_b = peaks[self.limb_seq[keypoint_id][1] - 1] - idx_joint_a = self.limb_seq[keypoint_id][0] - 1 - idx_joint_b = self.limb_seq[keypoint_id][1] - 1 - - if not candidate_a and not candidate_b: # no such limb - continue - if not candidate_a: # limb has just B joint - subset = self._add_pose_single_candidate(subset, candidate_b, idx_joint_b, kpt_num) - continue - if not candidate_b: # limb has just A joint - subset = self._add_pose_single_candidate(subset, candidate_a, idx_joint_a, kpt_num) - continue - - temp = self._get_connections(candidate_a, candidate_b, score_mid, pafs, threshold) - if not temp: - continue - - if keypoint_id == 0: - subset = [np.ones(kpt_num) * -1 for _ in temp] - for i, temp_i in enumerate(temp): - subset[i][self.limb_seq[0][0] - 1] = temp_i[0] - subset[i][self.limb_seq[0][1] - 1] = temp_i[1] - subset[i][-1] = 2 - subset[i][-2] = np.sum(candidates[temp_i[0:2], 2]) + temp_i[2] - else: - index_a = self.limb_seq[keypoint_id][0] - 1 - index_b = self.limb_seq[keypoint_id][1] - 1 - if keypoint_id in (17, 18): - subset = self._copy_temperature_to_subset(subset, temp, index_a, index_b) - continue - subset = self._add_pose_both_candidates(subset, temp, index_a, index_b, candidates, kpt_num) - - return self._filter_subset(subset), candidates - - @staticmethod - def get_poses(subset, candidate): - persons_keypoints_x, persons_keypoints_y, persons_keypoints_v = [], [], [] - scores = [] - for subset_element in subset: - if subset_element.size == 0: - continue - keypoints_x, keypoints_y, keypoints_v = [0] * 17, [0] * 17, [0] * 17 - to_coco_map = [0, -1, 6, 8, 10, 5, 7, 9, 12, 14, 16, 11, 13, 15, 2, 1, 4, 3] - person_score = subset_element[-2] - position_id = -1 - for keypoint_id in subset_element[:-2]: - position_id += 1 - if position_id == 1: # No 'Neck' in COCO - continue - - cx, cy, visibility = 0, 0, 0 # Keypoint not found - if keypoint_id != -1: - cx, cy = candidate[keypoint_id.astype(int), 0:2] - cx = cx - 0.5 + 1 # +1 for matlab consistency, coords start from 1 - cy = cy - 0.5 + 1 - visibility = 1 - keypoints_x[to_coco_map[position_id]] = cx - keypoints_y[to_coco_map[position_id]] = cy - keypoints_v[to_coco_map[position_id]] = visibility - - scores.append(person_score * max(0, (subset_element[-1] - 1))) # -1 for Neck - persons_keypoints_x.append(keypoints_x) - persons_keypoints_y.append(keypoints_y) - persons_keypoints_v.append(keypoints_v) - - persons_keypoints_x = np.array(persons_keypoints_x) - persons_keypoints_y = np.array(persons_keypoints_y) - persons_keypoints_v = np.array(persons_keypoints_v) - scores = np.array(scores) - - return persons_keypoints_x, persons_keypoints_y, persons_keypoints_v, scores diff --git a/tools/accuracy_checker/accuracy_checker/adapters/reidentification.py b/tools/accuracy_checker/accuracy_checker/adapters/reidentification.py deleted file mode 100644 index f2fed251a5ce88..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/adapters/reidentification.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from ..adapters import Adapter -from ..representation import ReIdentificationPrediction - - -class ReidAdapter(Adapter): - """ - Class for converting output of Reid model to ReIdentificationPrediction representation - """ - __provider__ = 'reid' - - def configure(self): - """ - Specifies parameters of config entry - """ - self.grn_workaround = self.launcher_config.get("grn_workaround", True) - - def process(self, raw, identifiers=None, frame_meta=None): - """ - Args: - identifiers: list of input data identifiers - raw: output of model - Returns: - list of ReIdentificationPrediction objects - """ - prediction = self._extract_predictions(raw, frame_meta)[self.output_blob] - - if self.grn_workaround: - # workaround: GRN layer - prediction = self._grn_layer(prediction) - - return [ReIdentificationPrediction(identifier, embedding.reshape(-1)) - for identifier, embedding in zip(identifiers, prediction)] - - @staticmethod - def _grn_layer(prediction): - GRN_BIAS = 0.000001 - sum_ = np.sum(prediction ** 2, axis=1) - prediction = prediction / np.sqrt(sum_[:, np.newaxis] + GRN_BIAS) - - return prediction diff --git a/tools/accuracy_checker/accuracy_checker/adapters/segmentation.py b/tools/accuracy_checker/accuracy_checker/adapters/segmentation.py deleted file mode 100644 index fcb26c32eac232..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/adapters/segmentation.py +++ /dev/null @@ -1,74 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -import numpy as np -from ..adapters import Adapter -from ..representation import SegmentationPrediction, BrainTumorSegmentationPrediction - - -class SegmentationAdapter(Adapter): - __provider__ = 'segmentation' - - def process(self, raw, identifiers=None, frame_meta=None): - result = [] - frame_meta = frame_meta or [] * len(identifiers) - raw_outputs = self._extract_predictions(raw, frame_meta) - for identifier, output in zip(identifiers, raw_outputs[self.output_blob]): - result.append(SegmentationPrediction(identifier, output)) - - return result - - def _extract_predictions(self, outputs_list, meta): - if not 'tiles_shape' in (meta[-1] or {}): - return outputs_list[0] - tiles_shapes = [meta['tiles_shape'] for meta in meta] - restore_output = [] - offset = 0 - for _, image_tiles_shape in enumerate(tiles_shapes): - next_offset = offset + image_tiles_shape[0] * image_tiles_shape[1] - image_tiles = [network_output[self.output_blob] for network_output in outputs_list[offset:next_offset]] - tiles_columns = image_tiles[::image_tiles_shape[0]] - image = tiles_columns[0] - for tile_column in tiles_columns[1:]: - image = np.concatenate((image, tile_column), axis=3) - restore_output.append(image.squeeze()) - offset = next_offset - - return {self.output_blob: restore_output} - - -class BrainTumorSegmentationAdapter(Adapter): - __provider__ = 'brain_tumor_segmentation' - - def process(self, raw, identifiers=None, frame_meta=None): - result = [] - frame_meta = frame_meta or [] * len(identifiers) - raw_outputs = self._extract_predictions(raw, frame_meta) - for identifier, output in zip(identifiers, raw_outputs[self.output_blob]): - result.append(BrainTumorSegmentationPrediction(identifier, output)) - - return result - - def _extract_predictions(self, outputs_list, meta): - if not (meta[-1] or {}).get('multi_infer', False): - return outputs_list[0] - - output_keys = list(outputs_list[0].keys()) - output_map = {} - for output_key in output_keys: - output_data = [[output[output_key] for output in outputs_list]] - output_map[output_key] = output_data - - return output_map diff --git a/tools/accuracy_checker/accuracy_checker/adapters/text_detection.py b/tools/accuracy_checker/accuracy_checker/adapters/text_detection.py deleted file mode 100644 index d90ebfc30f18c1..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/adapters/text_detection.py +++ /dev/null @@ -1,309 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from collections import defaultdict - -import cv2 -import numpy as np - - -from ..adapters import Adapter -from ..config import ConfigValidator, StringField, NumberField, BoolField, ConfigError -from ..representation import TextDetectionPrediction, CharacterRecognitionPrediction - - -class TextDetectionAdapterConfig(ConfigValidator): - type = StringField() - pixel_link_out = StringField() - pixel_class_out = StringField() - - -class TextDetectionAdapter(Adapter): - __provider__ = 'text_detection' - - def validate_config(self): - text_detection_adapter_config = TextDetectionAdapterConfig('TextDetectionAdapter_Config') - text_detection_adapter_config.validate(self.launcher_config) - - def configure(self): - self.pixel_link_out = self.launcher_config['pixel_link_out'] - self.pixel_class_out = self.launcher_config['pixel_class_out'] - - def process(self, raw, identifiers=None, frame_meta=None): - results = [] - predictions = self._extract_predictions(raw, frame_meta) - raw_output = zip(identifiers, frame_meta, predictions[self.pixel_link_out], predictions[self.pixel_class_out]) - for identifier, current_frame_meta, link_data, cls_data in raw_output: - link_data = link_data.reshape((1, *link_data.shape)) - cls_data = cls_data.reshape((1, *cls_data.shape)) - link_data_shape = link_data.shape - new_link_data_shape = (link_data_shape[0], link_data_shape[2], link_data_shape[3], link_data_shape[1] / 2) - cls_data_shape = cls_data.shape - new_cls_data_shape = (cls_data_shape[0], cls_data_shape[2], cls_data_shape[3], cls_data_shape[1] / 2) - link_data = self.softmax(link_data.transpose((0, 2, 3, 1)).reshape(-1))[1::2] - cls_data = self.softmax(cls_data.transpose((0, 2, 3, 1)).reshape(-1))[1::2] - mask = self.decode_image_by_join(cls_data, new_cls_data_shape, link_data, new_link_data_shape) - rects = self.mask_to_boxes(mask, current_frame_meta['image_size']) - results.append(TextDetectionPrediction(identifier, rects)) - - return results - - @staticmethod - def softmax(data): - for i in np.arange(start=0, stop=data.size, step=2, dtype=int): - maximum = max(data[i], data[i + 1]) - data[i] = np.exp(data[i] - maximum) - data[i + 1] = np.exp(data[i + 1] - maximum) - sum_data = data[i] + data[i + 1] - data[i] /= sum_data - data[i + 1] /= sum_data - - return data - - def decode_image_by_join(self, cls_data, cls_data_shape, link_data, link_data_shape): - k_cls_conf_threshold = 0.7 - k_link_conf_threshold = 0.7 - height = cls_data_shape[1] - width = cls_data_shape[2] - id_pixel_mask = np.argwhere(cls_data >= k_cls_conf_threshold).reshape(-1) - pixel_mask = cls_data >= k_cls_conf_threshold - group_mask = {} - pixel_mask[id_pixel_mask] = True - points = [] - for i in id_pixel_mask: - points.append((i % width, i // width)) - group_mask[i] = -1 - link_mask = link_data >= k_link_conf_threshold - neighbours = link_data_shape[3] - for point in points: - neighbour = 0 - point_x, point_y = point - x_neighbours = [point_x - 1, point_x, point_x + 1] - y_neighbours = [point_y - 1, point_y, point_y + 1] - for neighbour_y in y_neighbours: - for neighbour_x in x_neighbours: - if neighbour_x == point_x and neighbour_y == point_y: - continue - - if neighbour_x < 0 or neighbour_x >= width or neighbour_y < 0 or neighbour_y >= height: - continue - - pixel_value = np.uint8(pixel_mask[neighbour_y * width + neighbour_x]) - link_value = np.uint8( - link_mask[int(point_y * width * neighbours + point_x * neighbours + neighbour)] - ) - - if pixel_value and link_value: - group_mask = self.join(point_x + point_y * width, neighbour_x + neighbour_y * width, group_mask) - - neighbour += 1 - - return self.get_all(points, width, height, group_mask) - - def join(self, point1, point2, group_mask): - root1 = self.find_root(point1, group_mask) - root2 = self.find_root(point2, group_mask) - if root1 != root2: - group_mask[root1] = root2 - - return group_mask - - def get_all(self, points, width, height, group_mask): - root_map = {} - mask = np.zeros((height, width)) - - for point in points: - point_x, point_y = point - point_root = self.find_root(point_x + point_y * width, group_mask) - if not root_map.get(point_root): - root_map[point_root] = int(len(root_map) + 1) - mask[point_y, point_x] = root_map[point_root] - - return mask - - @staticmethod - def find_root(point, group_mask): - root = point - update_parent = False - while group_mask[root] != -1: - root = group_mask[root] - update_parent = True - - if update_parent: - group_mask[point] = root - - return root - - @staticmethod - def mask_to_boxes(mask, image_size): - max_val = np.max(mask).astype(int) - resized_mask = cv2.resize( - mask.astype(np.float32), (image_size[1], image_size[0]), interpolation=cv2.INTER_NEAREST - ) - bboxes = [] - for i in range(int(max_val + 1)): - bbox_mask = resized_mask == i - contours_tuple = cv2.findContours(bbox_mask.astype(np.uint8), cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) - contours = contours_tuple[1] if len(contours_tuple) > 2 else contours_tuple[0] - if not contours: - continue - rect = cv2.minAreaRect(contours[0]) - _, hw, _ = rect - ignored_height = hw[0] >= image_size[0] - 1 - ignored_width = hw[1] >= image_size[1] - 1 - if ignored_height or ignored_width: - continue - box = cv2.boxPoints(rect) - bboxes.append(box) - - return bboxes - - -class LPRAdapter(Adapter): - __provider__ = 'lpr' - - def configure(self): - if not self.label_map: - raise ConfigError('LPR adapter requires dataset label map for correct decoding.') - - def process(self, raw, identifiers=None, frame_meta=None): - raw_output = self._extract_predictions(raw, frame_meta) - predictions = raw_output[self.output_blob] - result = [] - for identifier, output in zip(identifiers, predictions): - decoded_out = self.decode(output.reshape(-1)) - result.append(CharacterRecognitionPrediction(identifier, decoded_out)) - - return result - - def decode(self, outputs): - decode_out = str() - for output in outputs: - if output == -1: - break - decode_out += str(self.label_map[output]) - - return decode_out - - -class BeamSearchDecoderConfig(ConfigValidator): - beam_size = NumberField(optional=True, floats=False, min_value=1) - blank_label = NumberField(optional=True, floats=False, min_value=0) - softmaxed_probabilities = BoolField(optional=True) - - -class BeamSearchDecoder(Adapter): - __provider__ = 'beam_search_decoder' - - def validate_config(self): - beam_search_decoder_config = BeamSearchDecoderConfig( - 'BeamSearchDecoder_Config', - BeamSearchDecoderConfig.IGNORE_ON_EXTRA_ARGUMENT - ) - beam_search_decoder_config.validate(self.launcher_config) - - def configure(self): - if not self.label_map: - raise ConfigError('Beam Search Decoder requires dataset label map for correct decoding.') - - self.beam_size = self.launcher_config.get('beam_size', 10) - self.blank_label = self.launcher_config.get('blank_label', len(self.label_map)) - self.softmaxed_probabilities = self.launcher_config.get('softmaxed_probabilities', False) - - def process(self, raw, identifiers=None, frame_meta=None): - raw_output = self._extract_predictions(raw, frame_meta) - output = raw_output[self.output_blob] - output = np.swapaxes(output, 0, 1) - - result = [] - for identifier, data in zip(identifiers, output): - if self.softmaxed_probabilities: - data = np.log(data) - seq = self.decode(data, self.beam_size, self.blank_label) - decoded = ''.join(str(self.label_map[char]) for char in seq) - result.append(CharacterRecognitionPrediction(identifier, decoded)) - return result - - @staticmethod - def decode(probabilities, beam_size=10, blank_id=None): - """ - Decode given output probabilities to sequence of labels. - Arguments: - probabilities: The output log probabilities for each time step. - Should be an array of shape (time x output dim). - beam_size (int): Size of the beam to use during decoding. - blank_id (int): Index of the CTC blank label. - Returns the output label sequence. - """ - def make_new_beam(): - return defaultdict(lambda: (-np.inf, -np.inf)) - - def log_sum_exp(*args): - if all(a == -np.inf for a in args): - return -np.inf - a_max = np.max(args) - lsp = np.log(np.sum(np.exp(a - a_max) for a in args)) - - return a_max + lsp - - times, symbols = probabilities.shape - # Initialize the beam with the empty sequence, a probability of 1 for ending in blank - # and zero for ending in non-blank (in log space). - beam = [(tuple(), (0.0, -np.inf))] - - for time in range(times): - # A default dictionary to store the next step candidates. - next_beam = make_new_beam() - - for symbol_id in range(symbols): - current_prob = probabilities[time, symbol_id] - - for prefix, (prob_blank, prob_non_blank) in beam: - # If propose a blank the prefix doesn't change. - # Only the probability of ending in blank gets updated. - if symbol_id == blank_id: - next_prob_blank, next_prob_non_blank = next_beam[prefix] - next_prob_blank = log_sum_exp( - next_prob_blank, prob_blank + current_prob, prob_non_blank + current_prob - ) - next_beam[prefix] = (next_prob_blank, next_prob_non_blank) - continue - # Extend the prefix by the new character symbol and add it to the beam. - # Only the probability of not ending in blank gets updated. - end_t = prefix[-1] if prefix else None - next_prefix = prefix + (symbol_id,) - next_prob_blank, next_prob_non_blank = next_beam[next_prefix] - if symbol_id != end_t: - next_prob_non_blank = log_sum_exp( - next_prob_non_blank, prob_blank + current_prob, prob_non_blank + current_prob - ) - else: - # Don't include the previous probability of not ending in blank (prob_non_blank) if symbol - # is repeated at the end. The CTC algorithm merges characters not separated by a blank. - next_prob_non_blank = log_sum_exp(next_prob_non_blank, prob_blank + current_prob) - - next_beam[next_prefix] = (next_prob_blank, next_prob_non_blank) - # If symbol is repeated at the end also update the unchanged prefix. This is the merging case. - if symbol_id == end_t: - next_prob_blank, next_prob_non_blank = next_beam[prefix] - next_prob_non_blank = log_sum_exp(next_prob_non_blank, prob_non_blank + current_prob) - next_beam[prefix] = (next_prob_blank, next_prob_non_blank) - - beam = sorted(next_beam.items(), key=lambda x: log_sum_exp(*x[1]), reverse=True)[:beam_size] - - best = beam[0] - - return best[0] diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/README.md b/tools/accuracy_checker/accuracy_checker/annotation_converters/README.md deleted file mode 100644 index ee13679c974b64..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/README.md +++ /dev/null @@ -1,108 +0,0 @@ -# Annotation Converters - -Annotation converter is a function which converts annotation file to suitable for metric evaluation format. -Each annotation converter expects specific annotation file format or data structure, which depends on original dataset. -If converter for your data format is not supported by Accuracy Checker, you can provide your own annotation converter. -Each annotation converter has parameters available for configuration. - -Process of conversion can be implemented in two ways: -* via configuration file -* via command line - -### Describing annotation conversion in configuration file. - -Annotation conversion can be provided in `dataset` section your configuration file to convert annotation inplace before every evaluation. -Each conversion configuration should contain `converter` field filled selected converter name and provide converter specific parameters (more details in supported converters section). All paths can be prefixed via command line with `-s, --source` argument. - -You can additionally use optional parameters like: -* `subsample_size` - Dataset subsample size. You can specify the number of ground truth objects or dataset ration in percentage. Please, be careful to use this option, some datasets does not support subsampling. -* `annotation` - path to store converted annotation pickle file. You can use this parameter if you need to reuse converted annotation to avoid subsequent conversions. -* `meta` - path to store mata information about converted annotation if it is provided. - -Example of usage: -```yaml - annotation_conversion: - converter: sample - data_dir: sample/sample_dataset -``` - - -### Conversing process via command line. - -The command line for annotation conversion looks like: - -```bash -python3 convert_annotation.py -``` -All converter specific options should have format `-- ` -You may refer to `-h, --help` to full list of command line options. Some optional arguments are: - -* `-o, --output_dir` - directory to save converted annotation and meta info. -* `-a, --annotation_name` - annotation file name. -* `-m, --meta_name` - meta info file name. - -### Supported converters - -Accuracy Checker supports following list of annotation converters and specific for them parameters: -* `wider` - converts from Wider Face dataset to `DetectionAnnotation`. - * `annotation_file` - path to txt file, which contains ground truth data in WiderFace dataset format. - * `label_start` - specifies face label index in label map. Default value is 1. You can provide another value, if you want to use this dataset for separate label validation, - in case when your network predicts other class for faces. -* `sample` - converts annotation for SampleNet to `ClassificationAnnotation`. - * `data_dir` - path to sample dataset root directory. -* `voc07` - converts Pascal VOC 2007 annotation for detection task to `DetectionAnnotation`. - * `image_set_file` - path to file with validation image list (for example VOCdevkit/ImageSets/Main/val.txt). - * `annotations_dir` - path to directory with annotation files. - * `images_dir` - path to directory with images related to devkit root (default JPEGImages). - * `has_background` - allows convert dataset with/without adding background_label. Accepted values are True or False. (default is True) -* `voc_segmentation` - converts Pascal VOC annotation for semantic segmentation task to `SegmentationAnnotation`. - * `image_set_file` - path to file with validation image list (for example VOCdevkit/ImageSets/Segmentation/val.txt). - * `images_dir` - path to directory with images related to devkit root (default JPEGImages). - * `mask_dir` - path to directory with ground truth segmentation masks related to devkit root (default SegmentationClass). -* `mars` - converts MARS person reidentification dataset to `ReidentificationAnnotation`. - * `data_dir` - path to data directory, where gallery (`bbox_test`) and `query` subdirectories are located. -* `market1501` - converts Market1501 person reidentification dataset to `ReidentificationAnnotation`. - * `data_dir` - path to data directory, where gallery (`bounding_box_test`) and `query` subdirectories are located. -* `detection_opencv_storage` - converts detection annotation stored in Detection OpenCV storage format to `DetectionAnnotation`. - * `annotation_file` - path to annotation in xml format. - * `image_names_file` - path to txt file, which contains image name list for dataset. - * `label_start` - specifies label index start in label map. Default value is 1. You can provide another value, if you want to use this dataset for separate label validation. - * `background_label` - specifies which index will be used for background label. You can not provide this parameter if your dataset has not background label -* `face_reid_pairwise` - converts Labeled Faces in the Wild dataset for face reidentification to `ReidentificationClassificationAnnotation`. - * `pairs_file` - path to file with annotation positive and negative pairs. - * `train_file` - path to file with annotation positive and negative pairs used for network train (optional parameter). - * `landmarks_file` - path to file with facial landmarks coordinates for annotation images (optional parameter). -* `landmarks_regression` - converts VGG Face 2 dataset for facial landmarks regression task to `FacialLandmarksAnnotation`. - * `landmarks_csv_file` - path to csv file with coordinates of landmarks points. - * `bbox_csv_file` - path to cvs file which contains bounding box coordinates for faces (optional parameter). -* `mapillary_20` - converts Mapillary dataset contained 20 classes to `SegmentationAnnotation`. - * `data_dir` - path to dataset root folder. Relative paths to images and masks directory determine as `imgs` and `masks` respectively. In way when images and masks are located in non default directories, you can use parameters described below. - * `images_dir` - path to images folder. - * `mask_dir` - path to ground truth mask folder. -* `mighty` - converts Mighty AI dataset for road segmentation task to `SegmentationAnnotation`. - * `annotation_file` - txt file with paths to images and masks. -* `cityscapes` - converts CityScapes Dataset to `SegmentationAnnotation`. - * `dataset_root_dir` - path to dataset root. - * `images_subfolder` - path from dataset root to directory with validation images (Optional, default `imgsFine/leftImg8bit/val`). - * `masks_subfolder` - path from dataset root to directory with ground truth masks (Optional, `gtFine/val`). - * `masks_suffix` - suffix for mask file names (Optional, default `_gtFine_labelTrainIds`). - * `images_suffix` - suffix for image file names (Optional, default `_leftImg8bit`). - * `use_full_label_map` - allows to use full label map with 33 classes instead train label map with 18 classes (Optional, default `False`). -* `super_resolution` - converts dataset for super resolution task to `SuperResolutionAnnotation`. - * `data_dir` - path to folder, where images in low and high resolution are located. - * `lr_suffix` - low resolution file name's suffix (default lr). - * `hr_suffix` - high resolution file name's suffix (default hr). -* `icdar15_detection` - converts ICDAR15 dataset for text detection task to `TextDetectionAnnotation`. - * `data_dir` - path to folder with annotations on txt format. -* `icdar13_recognition` - converts ICDAR13 dataset for text recognition task to `CharecterRecognitionAnnotation`. - * `annotation_file` - path to annotation file in txt format. -* `mscoco_detection` - converts MS COCO dataset for object detection task to `DetectionAnnotation`. - * `annotation_file` - path ot annotation file in json format. - * `has_background` - allows convert dataset with/without adding background_label. Accepted values are True or False. (default is False). - * `use_full_label_map` - allows to use original label map (with 91 object categories) from paper instead public available(80 categories). -* `mscoco_keypoints` - converts MS COCO dataset for keypoints localization task to `PoseEstimationAnnotation`. - * `annotation_file` - path ot annotation file in json format. -* `imagenet` - convert ImageNet dataset for image classification task to `ClassificationAnnotation`. - * `annotation_file` - path to annotation in txt format. - * `labels_file` - path to file with word description of labels (synset words). - * `has_background` - allows to add background label to original labels and convert dataset for 1001 classes instead 1000 (default value is False). diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/__init__.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/__init__.py deleted file mode 100644 index d14cb62f15c271..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/__init__.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -Copyright (c) 2018 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -from .format_converter import BaseFormatConverter -from .convert import make_subset, save_annotation -from .market1501 import Market1501Converter -from .mars import MARSConverter -from .pascal_voc import PascalVOCDetectionConverter -from .sample_converter import SampleConverter -from .wider import WiderFormatConverter -from .detection_opencv_storage import DetectionOpenCVStorageFormatConverter -from .bitvehicle import BITVehicle, BITVehicleJSON -from .lfw import FaceReidPairwiseConverter -from .vgg_face_regression import LandmarksRegression -from .mighty import MightyFormatConverter -from .super_resolution_converter import SRConverter -from .mapillary_20 import Mapillary20Converters -from .imagenet import ImageNetFormatConverter -from .icdar import ICDAR13RecognitionDatasetConverter, ICDAR15DetectionDatasetConverter -from .ms_coco import MSCocoDetectionConverter, MSCocoKeypointsConverter -from .cityscapes import CityscapesConverter -from .ncf_converter import NCFConverter -from .brats import BratsConverter - -__all__ = [ - 'BaseFormatConverter', - 'make_subset', - 'save_annotation', - - 'ImageNetFormatConverter', - 'Market1501Converter', - 'SampleConverter', - 'PascalVOCDetectionConverter', - 'WiderFormatConverter', - 'MARSConverter', - 'DetectionOpenCVStorageFormatConverter', - 'BITVehicle', - 'BITVehicleJSON', - 'FaceReidPairwiseConverter', - 'LandmarksRegression', - 'MightyFormatConverter', - 'SRConverter', - 'Mapillary20Converters', - 'ICDAR13RecognitionDatasetConverter', - 'ICDAR15DetectionDatasetConverter', - 'MSCocoKeypointsConverter', - 'MSCocoDetectionConverter', - 'CityscapesConverter', - 'NCFConverter', - 'BratsConverter', -] diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/_reid_common.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/_reid_common.py deleted file mode 100644 index 8bcce97e806904..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/_reid_common.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from pathlib import Path - -from ..representation import ReIdentificationAnnotation - - -def read_directory(directory, query, image_pattern): - pids = set() - images = [] - for image in directory.glob("*.jpg"): - pid, camid = map(int, image_pattern.search(image.name).groups()) - if pid == -1: - continue - - camid -= 1 - pids.add(pid) - - identifier = str(Path(directory.name) / image.name) - images.append(ReIdentificationAnnotation(identifier, camid, pid, query)) - - return images, pids - - -def check_dirs(dirs, parent_dir, arg_name='data_dir'): - for directory in dirs: - if directory.is_dir(): - continue - - message_pattern = "{directory} not found in {parent_dir}. Check {arg_name} is pointed to a correct directory" - raise FileNotFoundError(message_pattern.format(directory=directory, parent_dir=parent_dir, arg_name=arg_name)) diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/bitvehicle.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/bitvehicle.py deleted file mode 100644 index 45f61e2db8561f..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/bitvehicle.py +++ /dev/null @@ -1,115 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from pathlib import Path - -from ..representation import DetectionAnnotation -from ..utils import get_key_by_value, read_json, read_xml - -from .format_converter import FileBasedAnnotationConverter - - -class BITVehicleJSON(FileBasedAnnotationConverter): - __provider__ = 'bitvehicle_json' - - def convert(self): - annotations = [] - for annotation_image in read_json(self.annotation_file): - labels, x_mins, y_mins, x_maxs, y_maxs, is_ignored, occluded = [], [], [], [], [], [], [] - for detection in annotation_image['objects']: - x_min, y_min, x_max, y_max = detection['bbox'] - label = detection['label'] - - if label == 'ignored': - for class_ in _CLASS_TO_IND.values(): - is_ignored.append(len(labels)) - labels.append(class_) - x_mins.append(x_min) - y_mins.append(y_min) - x_maxs.append(x_max) - y_maxs.append(y_max) - else: - is_occluded = detection.get('is_occluded', False) or detection.get('occluded', False) - is_difficult = detection.get('difficult', False) - if is_occluded or is_difficult: - occluded.append(len(labels)) - - labels.append(_CLASS_TO_IND[label]) - x_mins.append(x_min) - y_mins.append(y_min) - x_maxs.append(x_max) - y_maxs.append(y_max) - - identifier = Path(annotation_image['image']).name - annotation = DetectionAnnotation(identifier, labels, x_mins, y_mins, x_maxs, y_maxs) - annotation.metadata['is_occluded'] = occluded - annotation.metadata['difficult_boxes'] = is_ignored - - annotations.append(annotation) - - return annotations, get_meta() - - -class BITVehicle(FileBasedAnnotationConverter): - __provider__ = 'bitvehicle' - - def convert(self): - annotations = [] - for annotation_image in read_xml(self.annotation_file): - if annotation_image.tag != 'image': - continue - - identifier = annotation_image.get('name') - labels, x_mins, y_mins, x_maxs, y_maxs, occluded = [], [], [], [], [], [] - for roi in annotation_image.findall('box'): - label = roi.get("label") - x_left = int(roi.get('xtl')) - x_right = int(roi.get('xbr')) - y_top = int(roi.get('ytl')) - y_bottom = int(roi.get('ybr')) - x_min, y_min, x_max, y_max = x_left, y_top, x_right - x_left, y_bottom - y_top - is_occluded = bool(int(roi.get('occluded'))) - - labels.append(_CLASS_TO_IND[label]) - x_mins.append(x_min) - y_mins.append(y_min) - x_maxs.append(x_max) - y_maxs.append(y_max) - if is_occluded: - occluded.append(len(labels) - 1) - - annotation = DetectionAnnotation(identifier, labels, x_mins, y_mins, x_maxs, y_maxs) - annotation.metadata['is_occluded'] = occluded - - annotations.append(annotation) - - return annotations, get_meta() - - -_CLASSES = ( - '__background__', # always index 0 - 'vehicle', - 'plate' -) - -_CLASS_TO_IND = dict(zip(_CLASSES, list(range(len(_CLASSES))))) - - -def get_meta(): - labels = dict(enumerate(_CLASSES)) - labels[-1] = 'ignored' - - return {'label_map': labels, 'background_label': get_key_by_value(labels, '__background__')} diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/brats.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/brats.py deleted file mode 100644 index e91033d2f71ddf..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/brats.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -from pathlib import Path -import warnings - -from ..representation import BrainTumorSegmentationAnnotation -from ..utils import get_path -from ..config import StringField -from .format_converter import BaseFormatConverter, DirectoryBasedAnnotationConverterConfig - - -class BratsConverterConfig(DirectoryBasedAnnotationConverterConfig): - image_folder = StringField(optional=True) - mask_folder = StringField(optional=True) - - -class BratsConverter(BaseFormatConverter): - __provider__ = 'brats' - - _config_validator_type = BratsConverterConfig - - def configure(self): - self.data_dir = self.config['data_dir'] - self.image_folder = self.config.get('image_folder', 'imagesTr') - self.mask_folder = self.config.get('mask_folder', 'labelsTr') - - def convert(self): - mask_folder = Path(self.mask_folder) - image_folder = Path(self.image_folder) - image_dir = get_path(self.data_dir / image_folder, is_directory=True) - mask_dir = get_path(self.data_dir / mask_folder, is_directory=True) - - annotations = [] - for file_in_dir in image_dir.iterdir(): - file_name = file_in_dir.parts[-1] - mask = mask_dir / file_name - if not mask.exists(): - warnings.warn('Annotation mask for {} does not exists. File will be ignored.'.format(file_name)) - continue - annotation = BrainTumorSegmentationAnnotation( - str(image_folder / file_name), - str(mask_folder / file_name), - ) - - annotations.append(annotation) - - return annotations, None diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/cityscapes.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/cityscapes.py deleted file mode 100644 index 3bda89a0c78ca8..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/cityscapes.py +++ /dev/null @@ -1,73 +0,0 @@ -from pathlib import Path -from ..representation import SegmentationAnnotation -from ..representation.segmentation_representation import GTMaskLoader -from ..config import PathField, StringField, BoolField -from .format_converter import BaseFormatConverter, BaseFormatConverterConfig - - -train_meta = { - 'label_map': { - 0: 'road', 1: 'sidewalk', 2: 'building', 3: 'wall', 4: 'fence', 5: 'pole', 6: 'traffic light', - 7: 'traffic sign', 8: 'vegetation', 9: 'terrain', 10: 'sky', 11: 'person', 12: 'rider', 13: 'car', - 14: 'truck', 15: 'bus', 16: 'train', 17: 'motorcycle', 18: 'bicycle' - }, - 'segmentation_colors': ( - (128, 64, 128), (244, 35, 232), (70, 70, 70), (102, 102, 156), (190, 153, 153), (153, 153, 153), - (250, 170, 30), (220, 220, 0), (107, 142, 35), (152, 251, 152), (70, 130, 180), (220, 20, 60), (255, 0, 0), - (0, 0, 142), (0, 0, 70), (0, 60, 100), (0, 80, 100), (0, 0, 230), (119, 11, 32) - ), -} - -full_dataset_meta = { - 'segmentation_colors' : ( - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (111, 74, 0), (81, 0, 81), (128, 64, 128), - (244, 35, 232), (250, 170, 160), (230, 150, 140), (70, 70, 70), (102, 102, 156), (190, 153, 153), - (180, 165, 180), (150, 100, 100), (150, 120, 90), (153, 153, 153), (153, 153, 153), (250, 170, 30), - (220, 220, 0), (107, 142, 35), (152, 251, 152), (70, 130, 180), (220, 20, 60), (255, 0, 0), (0, 0, 142), - (0, 0, 70), (0, 60, 100), (0, 0, 90), (0, 0, 110), (0, 80, 100), (0, 0, 230), (119, 11, 32) - ), - 'label_map': { - 0: 'unlabeled', 1: 'ego vehicle', 2: 'rectification border', 3: 'out of roi', 4: 'static', 5: 'dynamic', - 6: 'ground', 7: 'road', 8: 'sidewalk', 9: 'parking', 10: 'rail track', 11: 'building', 12: 'wall', - 13: 'fence', 14: 'guard rail', 15: 'bridge', 16: 'tunnel', 17: 'pole', 18: 'polegroup', 19: 'traffic light', - 20: 'traffic sign', 21: 'vegetation', 22: 'terrain', 23: 'sky', 24: 'person', 25: 'rider', 26: 'car', - 27: 'truck', 28: 'bus', 29: 'caravan', 30: 'trailer', 31: 'train', 32: 'motorcycle', 33: 'bicycle', - -1: 'license plate' - } -} - - -class CityscapesConverterConfig(BaseFormatConverterConfig): - dataset_root_dir = PathField(is_directory=True) - images_subfolder = StringField(optional=True) - masks_subfolder = StringField(optional=True) - masks_suffix = StringField(optional=True) - images_suffix = StringField(optional=True) - use_full_label_map = BoolField(optional=True) - - -class CityscapesConverter(BaseFormatConverter): - __provider__ = 'cityscapes' - - _config_validator_type = CityscapesConverterConfig - - def configure(self): - self.dataset_root = self.config['dataset_root_dir'] - self.images_dir = self.config.get('images_subfolder', 'imgsFine/leftImg8bit/val') - self.masks_dir = self.config.get('masks_subfolder', 'gtFine/val') - self.masks_suffix = self.config.get('masks_suffix', '_gtFine_labelTrainIds') - self.images_suffix = self.config.get('images_suffix', '_leftImg8bit') - self.use_full_label_map = self.config.get('use_full_label_map', False) - - - def convert(self): - images = list(self.dataset_root.rglob(r'{}/*/*{}.png'.format(self.images_dir, self.images_suffix))) - annotations = [] - for image in images: - identifier = str(Path(self.images_dir).joinpath(*image.parts[-2:])) - mask = Path(self.masks_dir) / image.parts[-2] / self.masks_suffix.join( - str(image.name).split(self.images_suffix) - ) - annotations.append(SegmentationAnnotation(identifier, mask, mask_loader=GTMaskLoader.PILLOW)) - - return annotations, full_dataset_meta if self.use_full_label_map else train_meta diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/convert.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/convert.py deleted file mode 100644 index 830f73a0b26293..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/convert.py +++ /dev/null @@ -1,155 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -import warnings -import json -from pathlib import Path -from argparse import ArgumentParser -from functools import partial - -import numpy as np - -from ..utils import get_path -from ..representation import ReIdentificationClassificationAnnotation -from .format_converter import BaseFormatConverter - - -def build_argparser(): - parser = ArgumentParser( - description="Converts annotation form a arbitrary format to accuracy-checker specific format", add_help=False - ) - parser.add_argument( - "converter", - help="Specific converter to run", - choices=list(BaseFormatConverter.providers.keys()) - ) - parser.add_argument( - "-o", "--output_dir", - help="Directory to save converted annotation and meta info", - required=False, - type=partial(get_path, is_directory=True) - ) - parser.add_argument("-m", "--meta_name", help="Meta info file name", required=False) - parser.add_argument("-a", "--annotation_name", help="Annotation file name", required=False) - parser.add_argument("-ss", "--subsample", help="Dataset subsample size", required=False) - parser.add_argument("--subsample_seed", help="Seed for generation dataset subsample", type=int, required=False) - - return parser - - -def make_subset(annotation, size, seed=666): - def make_subset_pairwise(annotation, size): - def get_pairs(pairs_list): - pairs_set = set() - for identifier in pairs_list: - next_annotation = next( - pair_annotation for pair_annotation in annotation if pair_annotation.identifier == identifier - ) - positive_pairs = get_pairs(next_annotation.positive_pairs) - negative_pairs = get_pairs(next_annotation.negative_pairs) - pairs_set.add(next_annotation) - pairs_set.update(positive_pairs) - pairs_set.update(negative_pairs) - return pairs_set - - subsample_set = set() - while len(subsample_set) < size: - ann_ind = np.random.choice(len(annotation), 1) - annotation_for_subset = annotation[ann_ind[0]] - positive_pairs = annotation_for_subset.positive_pairs - negative_pairs = annotation_for_subset.negative_pairs - if len(positive_pairs) + len(negative_pairs) == 0: - continue - updated_pairs = set() - updated_pairs.add(annotation_for_subset) - updated_pairs.update(get_pairs(positive_pairs)) - updated_pairs.update(get_pairs(negative_pairs)) - subsample_set.update(updated_pairs) - return list(subsample_set) - - np.random.seed(seed) - dataset_size = len(annotation) - if dataset_size < size: - warnings.warn('Dataset size {} less than subset size {}'.format(dataset_size, size)) - return annotation - if isinstance(annotation[-1], ReIdentificationClassificationAnnotation): - return make_subset_pairwise(annotation, size) - - - return list(np.random.choice(annotation, size=size, replace=False)) - - -def main(): - main_argparser = build_argparser() - args, _ = main_argparser.parse_known_args() - converter, converter_argparser, converter_args = get_converter_arguments(args) - - main_argparser = ArgumentParser(parents=[main_argparser, converter_argparser]) - args = main_argparser.parse_args() - - converter = configure_converter(converter_args, args, converter) - out_dir = args.output_dir or Path.cwd() - - result, meta = converter.convert() - - subsample = args.subsample - if subsample: - if subsample.endswith('%'): - subsample_ratio = float(subsample[:-1]) / 100 - subsample_size = int(len(result) * subsample_ratio) - else: - subsample_size = int(args.subsample) - - result = make_subset(result, subsample_size) - - converter_name = converter.get_name() - annotation_name = args.annotation_name or "{}.pickle".format(converter_name) - meta_name = args.meta_name or "{}.json".format(converter_name) - - annotation_file = out_dir / annotation_name - meta_file = out_dir / meta_name - - save_annotation(result, meta, annotation_file, meta_file) - - -def save_annotation(annotation, meta, annotation_file, meta_file): - if annotation_file: - with annotation_file.open('wb') as file: - for representation in annotation: - representation.dump(file) - if meta_file and meta: - with meta_file.open('wt') as file: - json.dump(meta, file) - - -def configure_converter(converter_options, args, converter): - args_dict, converter_options_dict = vars(args), vars(converter_options) - converter_config = { - option_name: option_value for option_name, option_value in args_dict.items() - if option_name in converter_options_dict and option_value is not None - } - converter_config['converter'] = args.converter - converter.config = converter_config - converter.validate_config() - converter.configure() - - return converter - - -def get_converter_arguments(arguments): - converter = BaseFormatConverter.provide(arguments.converter) - converter_argparser = converter.get_argparser() - converter_options, _ = converter_argparser.parse_known_args() - return converter, converter_argparser, converter_options diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/detection_opencv_storage.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/detection_opencv_storage.py deleted file mode 100644 index dfe461a54c4a4d..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/detection_opencv_storage.py +++ /dev/null @@ -1,114 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -from ..config import PathField, NumberField -from ..representation import DetectionAnnotation -from ..utils import convert_bboxes_xywh_to_x1y1x2y2, read_xml, read_txt - -from .format_converter import BaseFormatConverter, BaseFormatConverterConfig - - -class DetectionOpenCVConverterConfig(BaseFormatConverterConfig): - annotation_file = PathField() - image_names_file = PathField(optional=True) - label_start = NumberField(floats=False, optional=True) - background_label = NumberField(floats=False, optional=True) - - -class DetectionOpenCVStorageFormatConverter(BaseFormatConverter): - __provider__ = 'detection_opencv_storage' - - _config_validator_type = DetectionOpenCVConverterConfig - - def configure(self): - self.annotation_file = self.config['annotation_file'] - self.image_names_file = self.config.get('image_names_file') - self.label_start = self.config.get('label_start', 1) - self.background_label = self.config.get('background_label') - - def convert(self): - root = read_xml(self.annotation_file) - - labels_set = self.get_label_set(root) - - labels_set = sorted(labels_set) - class_to_ind = dict(zip(labels_set, list(range(self.label_start, len(labels_set) + self.label_start + 1)))) - label_map = {} - for class_label, ind in class_to_ind.items(): - label_map[ind] = class_label - - annotations = [] - for frames in root: - for frame in frames: - identifier = '{}.png'.format(frame.tag) - labels, x_mins, y_mins, x_maxs, y_maxs = [], [], [], [], [] - difficult_indices = [] - for annotation in frame: - label = annotation.findtext('type') - if not label: - raise ValueError('"{}" contains detection without "{}"'.format(self.annotation_file, 'type')) - - box = annotation.findtext('roi') - if not box: - raise ValueError('"{}" contains detection without "{}"'.format(self.annotation_file, 'roi')) - box = list(map(float, box.split())) - - is_ignored = annotation.findtext('is_ignored', 0) - if int(is_ignored) == 1: - difficult_indices.append(len(labels)) - - labels.append(class_to_ind[label]) - x_min, y_min, x_max, y_max = convert_bboxes_xywh_to_x1y1x2y2(*box) - x_mins.append(x_min) - y_mins.append(y_min) - x_maxs.append(x_max) - y_maxs.append(y_max) - - detection_annotation = DetectionAnnotation(identifier, labels, x_mins, y_mins, x_maxs, y_maxs) - detection_annotation.metadata['difficult_boxes'] = difficult_indices - annotations.append(detection_annotation) - - if self.image_names_file: - self.rename_identifiers(annotations, self.image_names_file) - - meta = {} - if self.background_label: - label_map[self.background_label] = '__background__' - meta['background_label'] = self.background_label - meta['label_map'] = label_map - - return annotations, meta - - @staticmethod - def rename_identifiers(annotation_list, images_file): - for annotation, image in zip(annotation_list, read_txt(images_file)): - annotation.identifier = image - - return annotation_list - - - @staticmethod - def get_label_set(xml_root): - labels_set = set() - for frames in xml_root: - for frame in frames: - for annotation in frame: - label = annotation.findtext('type') - if not label: - raise ValueError('annotation contains detection without label') - - labels_set.add(label) - - return labels_set diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/format_converter.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/format_converter.py deleted file mode 100644 index 20d33811822950..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/format_converter.py +++ /dev/null @@ -1,108 +0,0 @@ -""" -Copyright (c) 2018 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -from argparse import ArgumentParser - -from ..config import ConfigValidator, StringField, PathField -from ..dependency import ClassProvider -from ..utils import format_key - - -class BaseFormatConverterConfig(ConfigValidator): - converter = StringField() - - -class BaseFormatConverter(ClassProvider): - __provider_type__ = 'converter' - - _config_validator_type = BaseFormatConverterConfig - - @property - def config_validator(self): - return self._config_validator_type( - '{}_converter_config'.format(self.get_name()), - on_extra_argument=self._config_validator_type.ERROR_ON_EXTRA_ARGUMENT - ) - - def __init__(self, config=None): - self.config = config - if config: - self.validate_config() - self.configure() - - def convert(self, *args, **kwargs): - """ - Converts specific annotation format to the ResultRepresentation specific for current dataset/task. - - Returns: - annotation: list of ResultRepresentations. - meta: meta-data map for the current dataset. - """ - raise NotImplementedError - - @classmethod - def get_name(cls): - return cls.__provider__ - - def get_argparser(self): - parser = ArgumentParser(add_help=False) - config_validator = self.config_validator - fields = config_validator.fields - for field_name, field in fields.items(): - if field_name == 'converter': - # it is base argument. Main argparser already use it to get argparser from specific converter. - # Converter argparser should contain only converter specific arguments. - continue - - required = not field.optional - parser.add_argument( - format_key(field_name), required=required, type=field.type - ) - - return parser - - def validate_config(self): - self.config_validator.validate(self.config) - - def configure(self): - pass - - -class FileBasedAnnotationConverterConfig(BaseFormatConverterConfig): - annotation_file = PathField() - - -class FileBasedAnnotationConverter(BaseFormatConverter): - _config_validator_type = FileBasedAnnotationConverterConfig - - def configure(self): - self.annotation_file = self.config['annotation_file'] - - def convert(self, *args, **kwargs): - pass - - -class DirectoryBasedAnnotationConverterConfig(BaseFormatConverterConfig): - data_dir = PathField(is_directory=True) - - -class DirectoryBasedAnnotationConverter(BaseFormatConverter): - _config_validator_type = DirectoryBasedAnnotationConverterConfig - - def configure(self): - self.data_dir = self.config['data_dir'] - - def convert(self, *args, **kwargs): - pass diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/icdar.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/icdar.py deleted file mode 100644 index 184ade3b04af77..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/icdar.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np -from ..representation import TextDetectionAnnotation, CharacterRecognitionAnnotation -from ..utils import read_txt -from .format_converter import FileBasedAnnotationConverter, DirectoryBasedAnnotationConverter - - -class ICDAR15DetectionDatasetConverter(DirectoryBasedAnnotationConverter): - __provider__ = 'icdar15_detection' - - def convert(self): - annotations = [] - - for gt_file in self.data_dir.iterdir(): - gt_file_name = str(gt_file.parts[-1]) - identifier = '{}.jpg'.format(gt_file_name.split('gt_')[-1].split('.txt')[0]) - all_points, transcriptions, difficult = [], [], [] - - for text_area in read_txt(gt_file): - text_annotation = text_area.split(',') - transcription = text_annotation[-1] - points = np.reshape(list(map(float, text_annotation[:8])), (-1, 2)) - if transcription == '###': - difficult.append(len(transcriptions)) - all_points.append(points) - transcriptions.append(transcription) - annotation = TextDetectionAnnotation(identifier, all_points, transcriptions) - annotation.metadata['difficult_boxes'] = difficult - annotations.append(annotation) - - return annotations, None - - -class ICDAR13RecognitionDatasetConverter(FileBasedAnnotationConverter): - __provider__ = 'icdar13_recognition' - - supported_symbols = '0123456789abcdefghijklmnopqrstuvwxyz' - - def convert(self): - annotations = [] - - for line in read_txt(self.annotation_file): - identifier, text = line.strip().split(' ') - annotations.append(CharacterRecognitionAnnotation(identifier, text)) - - label_map = {ind: str(key) for ind, key in enumerate(self.supported_symbols)} - - return annotations, {'label_map': label_map, 'blank_label': len(label_map)} diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/imagenet.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/imagenet.py deleted file mode 100644 index 88df08adece5d7..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/imagenet.py +++ /dev/null @@ -1,52 +0,0 @@ -import numpy as np - -from ..config import PathField, BoolField -from ..representation import ClassificationAnnotation -from ..utils import read_txt, get_path - -from .format_converter import BaseFormatConverter, BaseFormatConverterConfig - - -class ImageNetFormatConverterConfig(BaseFormatConverterConfig): - annotation_file = PathField() - labels_file = PathField(optional=True) - has_background = BoolField(optional=True) - - -class ImageNetFormatConverter(BaseFormatConverter): - __provider__ = 'imagenet' - - _config_validator_type = ImageNetFormatConverterConfig - - def configure(self): - self.annotation_file = self.config['annotation_file'] - self.labels_file = self.config.get('labels_file') - self.has_background = self.config.get('has_background', False) - - def convert(self): - annotation = [] - for image in read_txt(get_path(self.annotation_file)): - image_name, label = image.split() - label = np.int64(label) if not self.has_background else np.int64(label) + 1 - annotation.append(ClassificationAnnotation(image_name, label)) - meta = self._create_meta(self.labels_file, self.has_background) if self.labels_file else None - - return annotation, meta - - @staticmethod - def _create_meta(labels_file, has_background=False): - meta = {} - labels = {} - for i, line in enumerate(read_txt(get_path(labels_file))): - index_for_label = i if not has_background else i + 1 - line = line.strip() - label = line[line.find(' ') + 1:] - labels[index_for_label] = label - - if has_background: - labels[0] = 'background' - meta['backgound_label'] = 0 - - meta['label_map'] = labels - - return meta diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/lfw.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/lfw.py deleted file mode 100644 index 1002daf8e93572..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/lfw.py +++ /dev/null @@ -1,111 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from collections import defaultdict -from pathlib import Path - -from ..config import PathField -from ..representation import ReIdentificationClassificationAnnotation -from ..utils import read_txt - -from .format_converter import BaseFormatConverter, BaseFormatConverterConfig - - -class FaceReidPairwiseConverterConfig(BaseFormatConverterConfig): - pairs_file = PathField() - train_file = PathField(optional=True) - landmarks_file = PathField(optional=True) - - -class FaceReidPairwiseConverter(BaseFormatConverter): - __provider__ = 'face_reid_pairwise' - - _config_validator_type = FaceReidPairwiseConverterConfig - - def configure(self): - self.pairs_file = self.config['pairs_file'] - self.train_file = self.config.get('train_file') - self.landmarks_file = self.config.get('landmarks_file') - - def convert(self): - landmarks_map = {} - if self.landmarks_file: - for landmark_line in read_txt(self.landmarks_file): - landmark_line = landmark_line.split('\t') - landmarks_map[landmark_line[0]] = [int(point) for point in landmark_line[1:]] - - test_annotations = self.prepare_annotation(self.pairs_file, True, landmarks_map) - if self.train_file: - train_annotations = self.prepare_annotation(self.train_file, True, landmarks_map) - test_annotations += train_annotations - - return test_annotations, None - - @staticmethod - def get_image_name(person, image_id): - image_path_pattern = '{}/{}_{}{}.jpg' - return image_path_pattern.format(person, person, '0' * (4 - len(image_id)), image_id) - - def convert_positive(self, pairs, all_images): - positives = defaultdict(set) - for data in pairs: - image1 = self.get_image_name(data[0], data[1]) - image2 = self.get_image_name(data[0], data[2]) - positives[image1].add(image2) - all_images.add(image1) - all_images.add(image2) - - return positives, all_images - - def convert_negative(self, pairs, all_images): - negatives = defaultdict(set) - for data in pairs: - image1 = self.get_image_name(data[0], data[1]) - image2 = self.get_image_name(data[2], data[3]) - negatives[image1].add(image2) - all_images.add(image1) - all_images.add(image2) - - return negatives, all_images - - def prepare_annotation(self, ann_file: Path, train=False, landmarks_map=None): - positive_pairs, negative_pairs = [], [] - ann_lines = read_txt(ann_file) - for line in ann_lines[1:]: # skip header - pair = line.strip().split() - if len(pair) == 3: - positive_pairs.append(pair) - elif len(pair) == 4: - negative_pairs.append(pair) - - all_images = set() - positive_data, all_images = self.convert_positive(positive_pairs, all_images) - negative_data, all_images = self.convert_negative(negative_pairs, all_images) - - annotations = [] - for image in all_images: - annotation = ReIdentificationClassificationAnnotation(image, positive_data[image], negative_data[image]) - - if landmarks_map: - image_landmarks = landmarks_map.get(image) - annotation.metadata['keypoints'] = image_landmarks - - if train: - annotation.metadata['train'] = True - - annotations.append(annotation) - - return annotations diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/mapillary_20.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/mapillary_20.py deleted file mode 100644 index a089b5adf68a1f..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/mapillary_20.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -from pathlib import Path -from ..config import PathField -from ..representation import SegmentationAnnotation -from ..representation.segmentation_representation import GTMaskLoader -from ..utils import get_path -from .format_converter import BaseFormatConverter, BaseFormatConverterConfig - - -class Mapillary20ConverterConfig(BaseFormatConverterConfig): - data_dir = PathField(is_directory=True, optional=True) - images_dir = PathField(optional=True, is_directory=True) - mask_dir = PathField(optional=True, is_directory=True) - - -class Mapillary20Converters(BaseFormatConverter): - __provider__ = 'mapillary_20' - - label_map = { - 0: 'Road', - 1: 'Sidewalk', - 2: 'Building', - 3: 'Wall', - 4: 'Fence', - 5: 'Pole', - 6: 'Traffic Light', - 7: 'Traffic Sign', - 8: 'Vegetation', - 9: 'Terrain', - 10: 'Sky', - 11: 'Person', - 12: 'Rider', - 13: 'Car', - 14: 'Truck', - 15: 'Bus', - 16: 'Train', - 17: 'Motorcycle', - 18: 'Bicycle', - 19: 'Ego-Vehicle' - } - - _config_validator_type = Mapillary20ConverterConfig - - def configure(self): - data_dir = self.config.get('data_dir') - image_folder = self.config.get('images_dir', 'imgs') - mask_folder = self.config.get('mask_dir', 'masks') - if data_dir: - image_folder = data_dir / image_folder - mask_folder = data_dir / mask_folder - self.images_dir = get_path(image_folder, is_directory=True) - self.mask_dir = get_path(mask_folder, is_directory=True) - - def convert(self): - annotations = [] - for file_in_dir in self.images_dir.iterdir(): - annotation = SegmentationAnnotation( - str(Path(self.images_dir.name) / file_in_dir.name), - str(Path(self.mask_dir.name) / file_in_dir.name), - mask_loader=GTMaskLoader.PILLOW - ) - - annotations.append(annotation) - - return annotations, {'label_map': self.label_map} diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/market1501.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/market1501.py deleted file mode 100644 index 3d45cc2fbc733e..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/market1501.py +++ /dev/null @@ -1,41 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from __future__ import absolute_import, print_function - -import re - -from ._reid_common import check_dirs, read_directory -from .format_converter import DirectoryBasedAnnotationConverter - -MARKET_IMAGE_PATTERN = re.compile(r'([-\d]+)_c(\d)') - - -class Market1501Converter(DirectoryBasedAnnotationConverter): - __provider__ = 'market1501' - - def convert(self): - gallery = self.data_dir / 'bounding_box_test' - query = self.data_dir / 'query' - - check_dirs((gallery, query), self.data_dir) - gallery_images, gallery_pids = read_directory(gallery, query=False, image_pattern=MARKET_IMAGE_PATTERN) - query_images, query_pids = read_directory(query, query=True, image_pattern=MARKET_IMAGE_PATTERN) - annotation = gallery_images + query_images - - meta = {'num_identities': len(gallery_pids | query_pids)} - - return annotation, meta diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/mars.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/mars.py deleted file mode 100644 index bb8de49a1d4500..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/mars.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from __future__ import absolute_import, print_function - -import re - -from ._reid_common import check_dirs, read_directory -from .format_converter import DirectoryBasedAnnotationConverter - -MARS_IMAGE_PATTERN = re.compile(r'([\d]+)C(\d)') - - -class MARSConverter(DirectoryBasedAnnotationConverter): - __provider__ = 'mars' - - def convert(self): - gallery = self.data_dir / 'bbox_test' - query = self.data_dir / 'query' - - check_dirs((gallery, query), self.data_dir) - gallery_images, gallery_pids = read_directory(gallery, query=False, image_pattern=MARS_IMAGE_PATTERN) - query_images, query_pids = read_directory(query, query=True, image_pattern=MARS_IMAGE_PATTERN) - - return gallery_images + query_images, {'num_identities': len(gallery_pids | query_pids)} diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/mighty.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/mighty.py deleted file mode 100644 index c0ae9f211e9f20..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/mighty.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from ..representation import SegmentationAnnotation -from ..representation.segmentation_representation import GTMaskLoader -from ..utils import read_txt -from .format_converter import FileBasedAnnotationConverter - - -class MightyFormatConverter(FileBasedAnnotationConverter): - __provider__ = 'mighty' - - label_map = {0: 'BG', 1: 'road', 2: 'curbs', 3: 'marks'} - - def convert(self): - annotations = [] - for line in read_txt(self.annotation_file): - identifier, mask = line.split() - annotations.append(SegmentationAnnotation(identifier, mask, mask_loader=GTMaskLoader.PILLOW)) - - return annotations, {'label_map': self.label_map} diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/ms_coco.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/ms_coco.py deleted file mode 100644 index f1e41beaba180c..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/ms_coco.py +++ /dev/null @@ -1,129 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from tqdm import tqdm -import numpy as np - -from ..config import BoolField -from ..utils import read_json, convert_bboxes_xywh_to_x1y1x2y2 -from ..representation import DetectionAnnotation, PoseEstimationAnnotation -from .format_converter import BaseFormatConverter, FileBasedAnnotationConverter, FileBasedAnnotationConverterConfig - - -def get_image_annotation(image_id, annotations_): - return list(filter(lambda x: x['image_id'] == image_id, annotations_)) - - -def get_label_map(full_annotation, use_full_label_map=False, has_background=False): - labels = full_annotation['categories'] - - if not use_full_label_map: - label_offset = 1 if has_background else 0 - label_id_to_label = {label['id']: label_id + label_offset for label_id, label in enumerate(labels)} - label_map = {label_id + label_offset: label['name'] for label_id, label in enumerate(labels)} - else: - label_id_to_label = {label['id']: label['id'] for label in labels} - label_map = {label['id']: label['name'] for label in labels} - - return label_map, label_id_to_label - - -class MSCocoDetectionConverterConfig(FileBasedAnnotationConverterConfig): - has_background = BoolField(optional=True) - use_full_label_map = BoolField(optional=True) - - -class MSCocoDetectionConverter(BaseFormatConverter): - __provider__ = 'mscoco_detection' - - _config_validator_type = MSCocoDetectionConverterConfig - - def configure(self): - self.annotation_file = self.config['annotation_file'] - self.has_background = self.config.get('has_background', False) - self.use_full_label_map = self.config.get('use_full_label_map', False) - - def convert(self): - detection_annotations = [] - full_annotation = read_json(self.annotation_file) - image_info = full_annotation['images'] - annotations = full_annotation['annotations'] - - label_map, label_id_to_label = get_label_map(full_annotation, self.use_full_label_map, self.has_background) - - meta = {} - if self.has_background: - label_map[0] = 'background' - meta['background_label'] = 0 - - meta.update({'label_map': label_map}) - - for image in tqdm(image_info): - identifier = image['file_name'] - image_annotation = get_image_annotation(image['id'], annotations) - image_labels = [label_id_to_label[annotation['category_id']] for annotation in image_annotation] - xmins = [annotation['bbox'][0] for annotation in image_annotation] - ymins = [annotation['bbox'][1] for annotation in image_annotation] - widths = [annotation['bbox'][2] for annotation in image_annotation] - heights = [annotation['bbox'][3] for annotation in image_annotation] - xmaxs = np.add(xmins, widths) - ymaxs = np.add(ymins, heights) - is_crowd = [annotation['iscrowd'] for annotation in image_annotation] - detection_annotation = DetectionAnnotation(identifier, image_labels, xmins, ymins, xmaxs, ymaxs) - detection_annotation.metadata['iscrowd'] = is_crowd - detection_annotations.append(detection_annotation) - - return detection_annotations, meta - - -class MSCocoKeypointsConverter(FileBasedAnnotationConverter): - __provider__ = 'mscoco_keypoints' - - def convert(self): - keypoints_annotations = [] - - full_annotation = read_json(self.annotation_file) - image_info = full_annotation['images'] - annotations = full_annotation['annotations'] - label_map, _ = get_label_map(full_annotation, True) - for image in image_info: - identifier = image['file_name'] - image_annotation = get_image_annotation(image['id'], annotations) - if not image_annotation: - continue - x_vals, y_vals, visibility, labels, areas, is_crowd, bboxes, difficult = [], [], [], [], [], [], [], [] - for target in image_annotation: - if target['num_keypoints'] == 0: - difficult.append(len(x_vals)) - labels.append(target['category_id']) - keypoints = target['keypoints'] - x_vals.append(keypoints[::3]) - y_vals.append(keypoints[1::3]) - visibility.append(keypoints[2::3]) - areas.append(target['area']) - bboxes.append(convert_bboxes_xywh_to_x1y1x2y2(*target['bbox'])) - is_crowd.append(target['iscrowd']) - keypoints_annotation = PoseEstimationAnnotation( - identifier, np.array(x_vals), np.array(y_vals), np.array(visibility), np.array(labels) - ) - keypoints_annotation.metadata['areas'] = areas - keypoints_annotation.metadata['rects'] = bboxes - keypoints_annotation.metadata['iscrowd'] = is_crowd - keypoints_annotation.metadata['difficult_boxes'] = difficult - - keypoints_annotations.append(keypoints_annotation) - - return keypoints_annotations, {'label_map': label_map} diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/ncf_converter.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/ncf_converter.py deleted file mode 100644 index 86d4cb139d0964..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/ncf_converter.py +++ /dev/null @@ -1,75 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - - -from ..representation import HitRatioAnnotation -from ..utils import read_txt, get_path -from ..config import PathField, NumberField - -from .format_converter import BaseFormatConverter, BaseFormatConverterConfig - - -class NCFDatasetConverterConfig(BaseFormatConverterConfig): - raiting_file = PathField() - negative_file = PathField() - users_max_number = NumberField(optional=True) - - -class NCFConverter(BaseFormatConverter): - __provider__ = "ncf_converter" - - _config_validator_type = NCFDatasetConverterConfig - - def configure(self): - self.raiting_file = self.config['raiting_file'] - self.negative_file = self.config['negative_file'] - if 'users_max_number' in self.config: - self.users_max_number = self.config['users_max_number'] - else: - self.users_max_number = -1 - - def convert(self): - annotations = [] - users = [] - - for file_row in read_txt(self.raiting_file): - user_id, item_id, _ = file_row.split() - users.append(user_id) - identifier = ['u:'+user_id, 'i:' + item_id] - annotations.append(HitRatioAnnotation(identifier)) - if self.users_max_number > 0 and len(users) >= self.users_max_number: - break; - - item_numbers = 1 - - items_neg = [] - with get_path(self.negative_file).open() as content: - for file_row in content: - items = file_row.split() - items_neg.append(items) - if self.users_max_number > 0 and len(items_neg) >= self.users_max_number: - break; - - if items_neg: - iterations = len(items_neg[0]) - item_numbers += iterations - for i in range(iterations): - for user in users: - item = items_neg[int(user)][i] - identifier = ['u:' + user, 'i:' + item] - annotations.append(HitRatioAnnotation(identifier, False)) - - return annotations, {'users_number': len(users), 'item_numbers': item_numbers} diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/pascal_voc.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/pascal_voc.py deleted file mode 100644 index b30a72a6bd09fe..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/pascal_voc.py +++ /dev/null @@ -1,158 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from tqdm import tqdm -from pathlib import Path - -from ..config import PathField, BoolField -from ..representation import DetectionAnnotation, SegmentationAnnotation -from ..representation.segmentation_representation import GTMaskLoader -from ..utils import get_path, read_txt, read_xml -from .format_converter import BaseFormatConverter, BaseFormatConverterConfig - -_VOC_CLASSES_DETECTION = ( - 'aeroplane', 'bicycle', 'bird', 'boat', - 'bottle', 'bus', 'car', 'cat', 'chair', - 'cow', 'diningtable', 'dog', 'horse', - 'motorbike', 'person', 'pottedplant', - 'sheep', 'sofa', 'train', 'tvmonitor' -) - -_VOC_CLASSES_SEGMENTATION = tuple(['__background__']) + _VOC_CLASSES_DETECTION -_SEGMENTATION_COLORS = (( - (0, 0, 0), (128, 0, 0), (0, 128, 0), (128, 128, 0), - (0, 0, 128), (128, 0, 128), (0, 128, 128), (128, 128, 128), - (64, 0, 0), (192, 0, 0), (64, 128, 0), (192, 128, 0), - (64, 0, 128), (192, 0, 128), (64, 128, 128), (192, 128, 128), - (0, 64, 0), (128, 64, 0), (0, 192, 0), (128, 192, 0), - (0, 64, 128) -)) - - -def prepare_detection_labels(has_background=True): - num_classes = len(_VOC_CLASSES_DETECTION) - labels_shift = 1 if has_background else 0 - reversed_label_map = dict(zip(_VOC_CLASSES_DETECTION, list(range(labels_shift, num_classes + labels_shift)))) - if has_background: - reversed_label_map['__background__'] = 0 - - return reversed_label_map - - -def reverse_label_map(label_map): - return {value: key for key, value in label_map.items()} - - -class PascalVOCSegmentationConverterConfig(BaseFormatConverterConfig): - image_set_file = PathField() - images_dir = PathField(optional=True, is_directory=True) - mask_dir = PathField(optional=True, is_directory=True) - - -class PascalVOCSegmentationConverter(BaseFormatConverter): - __provider__ = 'voc_segmentation' - - _config_validator_type = PascalVOCSegmentationConverterConfig - - def configure(self): - self.image_set_file = self.config['image_set_file'] - self.image_dir = self.config.get('images_dir') - if not self.image_dir: - self.image_dir = get_path(self.image_set_file.parents[-2] / 'JPEGImages', is_directory=True) - - self.mask_dir = self.config.get('mask_dir') - if not self.mask_dir: - self.mask_dir = get_path(self.image_set_file.parents[-2] / 'SegmentationClass', is_directory=True) - - def convert(self): - - annotations = [] - for image in read_txt(self.image_set_file): - annotation = SegmentationAnnotation( - str(Path(self.image_dir.name) / '{}.jpg'.format(image)), - str(Path(self.mask_dir.name) / '{}.png'.format(image)), - mask_loader=GTMaskLoader.SCIPY - ) - - annotations.append(annotation) - - meta = { - 'label_map': dict(enumerate(_VOC_CLASSES_SEGMENTATION)), - 'background_label': 0, - 'segmentation_colors': _SEGMENTATION_COLORS - } - - return annotations, meta - - -class PascalVOCDetectionConverterConfig(BaseFormatConverterConfig): - image_set_file = PathField() - annotations_dir = PathField(is_directory=True) - images_dir = PathField(optional=True, is_directory=True) - has_background = BoolField(optional=True) - - -class PascalVOCDetectionConverter(BaseFormatConverter): - __provider__ = 'voc07' - - _config_validator_type = PascalVOCDetectionConverterConfig - - def configure(self): - self.image_set_file = self.config['image_set_file'] - self.image_dir = self.config.get('images_dir') - if not self.image_dir: - self.image_dir = get_path(self.image_set_file.parents[-2] / 'JPEGImages') - self.annotations_dir = self.config['annotations_dir'] - self.has_background = self.config.get('has_background', True) - - def convert(self): - class_to_ind = prepare_detection_labels(self.has_background) - - detections = [] - for image in tqdm(read_txt(self.image_set_file, sep=None)): - root = read_xml(self.annotations_dir / '{}.xml'.format(image)) - - identifier = root.find('.//filename').text - get_path(self.image_dir / identifier) - - labels, x_mins, y_mins, x_maxs, y_maxs = [], [], [], [], [] - difficult_indices = [] - for entry in root: - if not entry.tag.startswith('object'): - continue - - bbox = entry.find('bndbox') - difficult = int(entry.find('difficult').text) - - if difficult == 1: - difficult_indices.append(len(labels)) - - labels.append(class_to_ind[entry.find('name').text]) - x_mins.append(float(bbox.find('xmin').text) - 1) - y_mins.append(float(bbox.find('ymin').text) - 1) - x_maxs.append(float(bbox.find('xmax').text) - 1) - y_maxs.append(float(bbox.find('ymax').text) - 1) - - image_annotation = DetectionAnnotation(identifier, labels, x_mins, y_mins, x_maxs, y_maxs) - image_annotation.metadata['difficult_boxes'] = difficult_indices - - detections.append(image_annotation) - - meta = {'label_map': reverse_label_map(class_to_ind)} - if self.has_background: - meta['background_label'] = 0 - - return detections, meta diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/sample_converter.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/sample_converter.py deleted file mode 100644 index 88fb713ee40d26..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/sample_converter.py +++ /dev/null @@ -1,100 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import re - -from ..config import PathField -from ..representation import ClassificationAnnotation -from ..utils import get_path, read_txt - -from .format_converter import BaseFormatConverter, BaseFormatConverterConfig - - -class SampleConverterConfig(BaseFormatConverterConfig): - data_dir = PathField(is_directory=True) - - -class SampleConverter(BaseFormatConverter): - """ - Sample dataset converter. All annotation converters should be derived from BaseFormatConverter class. - """ - - # register name for this converter - # this name will be used for converter class look up - __provider__ = 'sample' - - _config_validator_type = SampleConverterConfig - - def configure(self): - self.data_dir = self.config['data_dir'] - - def convert(self): - """ - This method is executed automatically when convert.py is started. - All arguments are automatically forwarded from command line arguments. - - Returns: - annotations: list of annotation representation objects. - meta: dictionary with additional dataset level metadata. - """ - - dataset_directory = get_path(self.data_dir, is_directory=True) - - # read and convert annotation - labels = self._read_labels(dataset_directory / 'labels.txt') - annotations = self._convert_annotations(dataset_directory / 'test', labels) - - # convert label list to label map - label_map = {i: labels[i] for i in range(len(labels))} - metadata = {'label_map': label_map} - - return annotations, metadata - - @staticmethod - def _read_labels(labels_file): - """ - Extract label names from labels.txt file. - """ - - return read_txt(labels_file) - - @staticmethod - def _convert_annotations(test_dir, labels): - """ - Create annotation representations list. - """ - - # test directory contains files with names XXXX_class.png - # we use regular expression to extract class names - file_pattern_regex = re.compile(r'\d+_(\w+)\.png') - - annotations = [] - # iterate over all png images in test directory - for image in test_dir.glob('*.png'): - # get file name (e.g. from /foo/bar/image.png we get image.png) - image_base = str(image.parts[-1]) - - # extract class name from file name - regex_match = re.match(file_pattern_regex, image_base) - image_label = regex_match.group(1) - - # look up class index in label list - class_id = labels.index(image_label) - - # create annotation representation object - annotations.append(ClassificationAnnotation(image_base, class_id)) - - return annotations diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/super_resolution_converter.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/super_resolution_converter.py deleted file mode 100644 index 4c053f9b58de6f..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/super_resolution_converter.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -from ..config import PathField, StringField, BoolField -from ..representation import SuperResolutionAnnotation -from .format_converter import BaseFormatConverter, BaseFormatConverterConfig - - -class SRConverterConfig(BaseFormatConverterConfig): - data_dir = PathField(is_directory=True) - lr_suffix = StringField(optional=True) - hr_suffix = StringField(optional=True) - two_streams = BoolField(optional=True) - - -class SRConverter(BaseFormatConverter): - __provider__ = 'super_resolution' - - _config_validator_type = SRConverterConfig - - def configure(self): - self.data_dir = self.config['data_dir'] - self.lr_suffix = self.config.get('lr_suffix', 'lr') - self.hr_suffix = self.config.get('hr_suffix', 'hr') - self.two_streams = self.config.get('two_streams', False) - - def convert(self): - file_list_lr = [] - for file_in_dir in self.data_dir.iterdir(): - if self.lr_suffix in file_in_dir.parts[-1]: - file_list_lr.append(file_in_dir) - - annotation = [] - for lr_file in file_list_lr: - lr_file_name = lr_file.parts[-1] - hr_file_name = self.hr_suffix.join(lr_file_name.split(self.lr_suffix)) - identifier = [lr_file_name, hr_file_name] if self.two_streams else lr_file_name - annotation.append(SuperResolutionAnnotation(identifier, hr_file_name)) - - return annotation, None diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/vgg_face_regression.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/vgg_face_regression.py deleted file mode 100644 index 53c7c5784f31a6..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/vgg_face_regression.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from ..config import PathField -from ..representation import FacialLandmarksAnnotation -from ..utils import convert_bboxes_xywh_to_x1y1x2y2, read_csv -from .format_converter import BaseFormatConverter, BaseFormatConverterConfig - - -class LandmarksRegressionConfig(BaseFormatConverterConfig): - landmarks_csv_file = PathField() - bbox_csv_file = PathField(optional=True) - - -class LandmarksRegression(BaseFormatConverter): - __provider__ = 'landmarks_regression' - - _config_validator_type = LandmarksRegressionConfig - - def configure(self): - self.landmarks_csv = self.config['landmarks_csv_file'] - self.bbox_csv = self.config.get('bbox_csv_file') - - def convert(self): - annotations = [] - for row in read_csv(self.landmarks_csv): - identifier = row['NAME_ID'] + '.jpg' - x_values = np.array( - [float(row["P1X"]), float(row["P2X"]), float(row["P3X"]), float(row["P4X"]), float(row["P5X"])] - ) - y_values = np.array( - [float(row["P1Y"]), float(row["P2Y"]), float(row["P3Y"]), float(row["P4Y"]), float(row["P5Y"])] - ) - - annotation = FacialLandmarksAnnotation(identifier, x_values, y_values) - annotation.metadata['left_eye'] = 0 - annotation.metadata['right_eye'] = 1 - annotations.append(annotation) - - if self.bbox_csv: - for index, row in enumerate(read_csv(self.bbox_csv)): - annotations[index].metadata['rect'] = convert_bboxes_xywh_to_x1y1x2y2( - int(row["X"]), int(row["Y"]), int(row["W"]), int(row["H"]) - ) - - meta = { - 'label_map': {0: 'Left Eye', 1: 'Right Eye', 2: 'Nose', 3: 'Left Mouth Corner', 4: 'Right Mouth Corner'} - } - return annotations, meta diff --git a/tools/accuracy_checker/accuracy_checker/annotation_converters/wider.py b/tools/accuracy_checker/accuracy_checker/annotation_converters/wider.py deleted file mode 100644 index 672aa3a00793d2..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/annotation_converters/wider.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from ..config import NumberField -from ..representation import DetectionAnnotation -from ..utils import convert_bboxes_xywh_to_x1y1x2y2, read_txt - -from .format_converter import BaseFormatConverter, FileBasedAnnotationConverterConfig - - -class WiderConverterConfig(FileBasedAnnotationConverterConfig): - label_start = NumberField(floats=False, optional=True) - - -class WiderFormatConverter(BaseFormatConverter): - __provider__ = 'wider' - - _config_validator_type = WiderConverterConfig - - def configure(self): - self.annotation_file = self.config['annotation_file'] - self.label_start = self.config.get('label_start', 1) - - def convert(self): - image_annotations = read_txt(self.annotation_file) - image_ids = [] - for image_id, line in enumerate(image_annotations): - if '.jpg' in line: - image_ids.append(image_id) - - annotations = [] - for image_id in image_ids: - identifier = image_annotations[image_id] - bbox_count = image_annotations[image_id + 1] - bbox_lines = image_annotations[image_id + 2:image_id + 2 + int(bbox_count)] - - x_mins, y_mins, x_maxs, y_maxs = [], [], [], [] - for bbox in bbox_lines: - x_min, y_min, x_max, y_max = convert_bboxes_xywh_to_x1y1x2y2(*(map(float, (bbox.split(' ')[0:4])))) - x_mins.append(x_min) - y_mins.append(y_min) - x_maxs.append(x_max) - y_maxs.append(y_max) - - annotations.append(DetectionAnnotation( - identifier, [self.label_start] * len(x_mins), - x_mins, y_mins, x_maxs, y_maxs - )) - - return annotations, {'label_map': {0: '__background__', self.label_start: 'face'}, 'background_label': 0} diff --git a/tools/accuracy_checker/accuracy_checker/config/__init__.py b/tools/accuracy_checker/accuracy_checker/config/__init__.py deleted file mode 100644 index a32b29a4cecd86..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/config/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from .config_validator import ( - BaseField, - StringField, - ListField, - BoolField, - PathField, - NumberField, - DictField, - - BaseValidator, - ConfigError, - ConfigValidator -) - - -from .config_reader import ConfigReader - -__all__ = [ - 'BaseField', - 'StringField', - 'ListField', - 'BoolField', - 'PathField', - 'NumberField', - 'DictField', - - 'BaseValidator', - 'ConfigError', - 'ConfigValidator', - - 'ConfigReader' -] diff --git a/tools/accuracy_checker/accuracy_checker/config/config_reader.py b/tools/accuracy_checker/accuracy_checker/config/config_reader.py deleted file mode 100644 index a37686f16eff3d..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/config/config_reader.py +++ /dev/null @@ -1,414 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import copy -from pathlib import Path - -import warnings - -from ..utils import read_yaml, to_lower_register, contains_any -from .config_validator import ConfigError - - -class ConfigReader: - """ - Class for parsing input config. - """ - - @staticmethod - def merge(arguments): - """ - Args: - arguments: command-line arguments. - Returns: - dictionary containing configuration. - """ - - global_config, local_config = ConfigReader._read_configs(arguments) - if not local_config: - raise ConfigError('Missing local config') - - mode = ConfigReader._check_local_config(local_config) - ConfigReader._prepare_global_configs(global_config) - - config = ConfigReader._merge_configs(global_config, local_config, mode) - - ConfigReader._provide_cmd_arguments(arguments, config, mode) - ConfigReader._merge_paths_with_prefixes(arguments, config, mode) - ConfigReader._filter_launchers(config, arguments, mode) - - return config, mode - - @staticmethod - def _read_configs(arguments): - global_config = read_yaml(arguments.definitions) if arguments.definitions else None - local_config = read_yaml(arguments.config) - - return global_config, local_config - - @staticmethod - def _check_local_config(config): - def _is_requirements_missed(target, requirements): - return list(filter(lambda entry: not target.get(entry), requirements)) - - def _check_models_config(config): - models = config.get('models') - if not models: - raise ConfigError('Missed "{}" in local config'.format('models')) - - required_model_entries = ['name', 'launchers', 'datasets'] - required_dataset_entries = ['name'] - required_dataset_error = 'Model {} must specify {} for each dataset' - for model in models: - if _is_requirements_missed(model, required_model_entries): - raise ConfigError('Each model must specify {}'.format(', '.join(required_model_entries))) - - if list(filter(lambda entry: _is_requirements_missed(entry, required_dataset_entries), - model['datasets'])): - raise ConfigError(required_dataset_error.format(model['name'], ', '.join(required_dataset_entries))) - - def _check_pipelines_config(config): - def _count_entry(stages, entry): - count = 0 - for stage in stages: - if entry in stage: - count += 1 - return count - required_pipeline_entries = ['name', 'device_info', 'stages'] - pipelines = config['pipelines'] - if not pipelines: - raise ConfigError('Missed "{}" in local config'.format('pipelines')) - for pipeline in pipelines: - if _is_requirements_missed(pipeline, required_pipeline_entries): - raise ConfigError('Each pipeline must specify {}'.format(', '.join(required_pipeline_entries))) - stages = pipeline['stages'] - first_stage = stages[0] - dataset = first_stage.get('dataset') - if not dataset: - raise ConfigError('First stage should contain dataset') - count_datasets = _count_entry(stages, 'dataset') - if count_datasets != 1: - raise ConfigError('Exactly one dataset per pipeline is supported') - count_launchers = _count_entry(stages, 'launcher') - if not count_launchers: - raise ConfigError('Launchers are not specified') - count_metrics = _count_entry(stages, 'metrics') - if not count_metrics: - raise ConfigError('Metrics are not specified') - - if 'pipelines' in config: - _check_pipelines_config(config) - return 'pipelines' - - _check_models_config(config) - return 'models' - - @staticmethod - def _prepare_global_configs(global_configs): - if not global_configs or 'datasets' not in global_configs: - return - - datasets = global_configs['datasets'] - - def merge(local_entries, global_entries, identifier): - if not local_entries or not global_entries: - return - - for i, local in enumerate(local_entries): - local_identifier = local.get(identifier) - if not local_identifier: - continue - - local_entries[i] = ConfigReader._merge_configs_by_identifier(global_entries, local, identifier) - - for dataset in datasets: - merge(dataset.get('preprocessing'), global_configs.get('preprocessing'), 'type') - merge(dataset.get('metrics'), global_configs.get('metrics'), 'type') - merge(dataset.get('postprocessing'), global_configs.get('postprocessing'), 'type') - - @staticmethod - def _merge_configs(global_configs, local_config, mode='models'): - def _merge_models_config(global_configs, local_config): - config = copy.deepcopy(local_config) - if not global_configs: - return config - - models = config.get('models') - for model in models: - for i, launcher_entry in enumerate(model['launchers']): - model['launchers'][i] = ConfigReader._merge_configs_by_identifier( - global_configs['launchers'], launcher_entry, 'framework' - ) - - for i, dataset in enumerate(model['datasets']): - model['datasets'][i] = ConfigReader._merge_configs_by_identifier( - global_configs['datasets'], dataset, 'name' - ) - - return config - - def _merge_pipelines_config(global_config, local_config): - config = copy.deepcopy(local_config) - pipelines = [] - raw_pipelines = local_config['pipelines'] - for pipeline in raw_pipelines: - device_infos = pipeline['device_info'] - per_device_pipelines = [] - for device_info in device_infos: - copy_pipeline = copy.deepcopy(pipeline) - for stage in copy_pipeline['stages']: - if 'launcher' in stage: - stage['launcher'].update(device_info) - per_device_pipelines.append(copy_pipeline) - pipelines.extend(per_device_pipelines) - config['pipelines'] = pipelines - - return config - - functors_by_mode = { - 'models': _merge_models_config, - 'pipelines': _merge_pipelines_config - } - - return functors_by_mode[mode](global_configs, local_config) - - @staticmethod - def _merge_configs_by_identifier(global_config, local_config, identifier): - local_identifier = local_config.get(identifier) - if local_identifier is None: - return local_config - - matched = [] - for config in global_config: - global_identifier = config.get(identifier) - if global_identifier is None: - continue - - if global_identifier != local_identifier: - continue - - matched.append(config) - - config = copy.deepcopy(matched[0] if matched else {}) - for key, value in local_config.items(): - config[key] = value - - return config - - @staticmethod - def _merge_paths_with_prefixes(arguments, config, mode='models'): - args = arguments if isinstance(arguments, dict) else vars(arguments) - entries_paths = { - 'launchers': { - 'model': 'models', - 'weights': 'models', - 'caffe_model': 'models', - 'caffe_weights': 'models', - 'tf_model': 'models', - 'tf_meta': 'models', - 'mxnet_weights': 'models', - 'onnx_model': 'models', - 'kaldi_model': 'models', - 'cpu_extensions': 'extensions', - 'gpu_extensions': 'extensions', - 'bitstream': 'bitstreams', - 'affinity_map' : 'affinity_map' - }, - 'datasets': { - 'segmentation_masks_source': 'source', - 'annotation': 'annotations', - 'dataset_meta': 'annotations', - 'data_source': 'source', - }, - } - - def merge_entry_paths(keys, value): - for field, argument in keys.items(): - if field not in value: - continue - - config_path = Path(value[field]) - if config_path.is_absolute(): - value[field] = Path(value[field]) - continue - - if not argument in args or not args[argument]: - continue - - value[field] = args[argument] / config_path - - def create_command_line_for_conversion(config): - mapping = {} - value = 'source' - for key in config: - if key.endswith('file') or key.endswith('dir'): - mapping[key] = value - return mapping - - def process_config(config_item, entries_paths, dataset_identifier='datasets', identifers_mapping=None): - for entry, command_line_arg in entries_paths.items(): - entry_id = entry if not identifers_mapping else identifers_mapping[entry] - if entry_id not in config_item: - continue - - if entry_id == dataset_identifier: - datasets_configs = config_item[entry_id] - if not isinstance(datasets_configs, list): - datasets_configs = [datasets_configs] - for datasets_config in datasets_configs: - annotation_conversion_config = datasets_config.get('annotation_conversion') - if annotation_conversion_config: - command_line_conversion = (create_command_line_for_conversion(annotation_conversion_config)) - merge_entry_paths(command_line_conversion, annotation_conversion_config) - - config_entires = config_item[entry_id] - if not isinstance(config_entires, list): - config_entires = [config_entires] - for config_entry in config_entires: - merge_entry_paths(command_line_arg, config_entry) - - def process_models(config, entries_paths): - for model in config['models']: - process_config(model, entries_paths) - - def process_pipelines(config, entries_paths): - identifiers_mapping = {'datasets': 'dataset', 'launchers': 'launcher', 'reader': 'reader'} - entries_paths.update({'reader': {'data_source': 'source'}}) - for pipeline in config['pipelines']: - for stage in pipeline['stages']: - process_config(stage, entries_paths, 'dataset', identifiers_mapping) - - functors_by_mode = { - 'models': process_models, - 'pipelines': process_pipelines - } - - processing_func = functors_by_mode[mode] - processing_func(config, entries_paths) - - @staticmethod - def _provide_cmd_arguments(arguments, config, mode): - def merge_converted_model_path(converted_models_dir, mo_output_dir): - if mo_output_dir: - mo_output_dir = Path(mo_output_dir) - if mo_output_dir.is_absolute(): - return mo_output_dir - return converted_models_dir / mo_output_dir - return converted_models_dir - - def merge_dlsdk_launcher_args(arguments, launcher_entry, update_launcher_entry): - if launcher_entry['framework'].lower() != 'dlsdk': - return launcher_entry - - launcher_entry.update(update_launcher_entry) - models_prefix = arguments.models - if models_prefix: - launcher_entry['_models_prefix'] = models_prefix - - if not arguments.converted_models: - return launcher_entry - - mo_params = launcher_entry.get('mo_params', {}) - - mo_params.update({ - 'output_dir': merge_converted_model_path(arguments.converted_models, mo_params.get('output_dir')) - }) - - launcher_entry['mo_params'] = mo_params - - if arguments.aocl: - launcher_entry['_aocl'] = arguments.aocl - - return launcher_entry - - def merge_models(config, arguments, update_launcher_entry): - for model in config['models']: - for launcher_entry in model['launchers']: - merge_dlsdk_launcher_args(arguments, launcher_entry, update_launcher_entry) - - def merge_pipelines(config, arguments, update_launcher_entry): - for pipeline in config['pipelines']: - for stage in pipeline['stages']: - if 'launcher' in stage: - merge_dlsdk_launcher_args(arguments, stage['launcher'], update_launcher_entry) - functors_by_mode = { - 'models': merge_models, - 'pipelines': merge_pipelines - } - - additional_keys = [ - 'model_optimizer', 'tf_custom_op_config_dir', - 'tf_obj_detection_api_pipeline_config_path', - 'cpu_extensions_mode', 'vpu_log_level' - ] - arguments_dict = arguments if isinstance(arguments, dict) else vars(arguments) - update_launcher_entry = {} - - for key in additional_keys: - value = arguments_dict.get(key) - if value: - update_launcher_entry['_{}'.format(key)] = value - - return functors_by_mode[mode](config, arguments, update_launcher_entry) - - @staticmethod - def _filter_launchers(config, arguments, mode='models'): - def filtered(launcher, targets): - target_tags = args.get('target_tags') or [] - if target_tags: - if not contains_any(target_tags, launcher.get('tags', [])): - return True - - config_framework = launcher['framework'].lower() - target_framework = (args.get('target_framework') or config_framework).lower() - if config_framework != target_framework: - return True - - return targets and launcher.get('device', '').lower() not in targets - - def filter_models(config, target_devices): - for model in config['models']: - launchers = model['launchers'] - launchers = [launcher for launcher in launchers if not filtered(launcher, target_devices)] - - if not launchers: - warnings.warn('Model "{}" has no launchers'.format(model['name'])) - - model['launchers'] = launchers - - def filter_pipelines(config, target_devices): - saved_pipelines = [] - for pipeline in config['pipelines']: - filtered_pipeline = False - for stage in pipeline: - if 'launcher' in stage: - if filtered(stage['launcher'], target_devices): - filtered_pipeline = True - break - if filtered_pipeline: - continue - saved_pipelines.append(pipeline) - config['pipelines'] = saved_pipelines - - functors_by_mode = { - 'models': filter_models, - 'pipelines': filter_pipelines - } - - args = arguments if isinstance(arguments, dict) else vars(arguments) - target_devices = to_lower_register(args.get('target_devices') or []) - filtering_mode = functors_by_mode[mode] - filtering_mode(config, target_devices) diff --git a/tools/accuracy_checker/accuracy_checker/config/config_validator.py b/tools/accuracy_checker/accuracy_checker/config/config_validator.py deleted file mode 100644 index 5853a5f3cf8225..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/config/config_validator.py +++ /dev/null @@ -1,341 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import enum -import math -import re -import warnings -from collections import OrderedDict -from copy import copy -from functools import partial -from pathlib import Path - -from ..utils import get_path, string_to_bool - - -class ConfigError(ValueError): - pass - - -class BaseValidator: - def __init__(self, on_error=None, additional_validator=None): - self.on_error = on_error - self.additional_validator = additional_validator - - self.field_uri = None - - def validate(self, entry, field_uri=None): - field_uri = field_uri or self.field_uri - if self.additional_validator and not self.additional_validator(entry, field_uri): - self.raise_error(entry, field_uri) - - def raise_error(self, value, field_uri, reason=None): - if self.on_error: - self.on_error(value, field_uri, reason) - - error_message = 'Invalid value "{value}" for {field_uri}'.format(value=value, field_uri=field_uri) - if reason: - error_message = '{error_message}: {reason}'.format(error_message=error_message, reason=reason) - - raise ConfigError(error_message.format(value, field_uri)) - - -class _ExtraArgumentBehaviour(enum.Enum): - WARN = 'warn' - IGNORE = 'ignore' - ERROR = 'error' - - -def _is_dict_like(entry): - return hasattr(entry, '__iter__') and hasattr(entry, '__getitem__') - - -class ConfigValidator(BaseValidator): - WARN_ON_EXTRA_ARGUMENT = _ExtraArgumentBehaviour.WARN - ERROR_ON_EXTRA_ARGUMENT = _ExtraArgumentBehaviour.ERROR - IGNORE_ON_EXTRA_ARGUMENT = _ExtraArgumentBehaviour.IGNORE - - def __init__(self, config_uri, on_extra_argument=WARN_ON_EXTRA_ARGUMENT, **kwargs): - super().__init__(**kwargs) - self.on_extra_argument = on_extra_argument - - self.fields = OrderedDict() - self.field_uri = config_uri - for name in dir(self): - value = getattr(self, name) - if not isinstance(value, BaseField): - continue - - field_copy = copy(value) - field_copy.field_uri = "{}.{}".format(config_uri, name) - self.fields[name] = field_copy - - def validate(self, entry, field_uri=None): - super().validate(entry, field_uri) - field_uri = field_uri or self.field_uri - if not _is_dict_like(entry): - raise ConfigError("{} is expected to be dict-like".format(field_uri)) - - extra_arguments = [] - for key in entry: - if key not in self.fields: - extra_arguments.append(key) - continue - - self.fields[key].validate(entry[key]) - - required_fields = set(name for name, value in self.fields.items() if not value.optional) - missing_arguments = required_fields.difference(entry) - - if missing_arguments: - arguments = ', '.join(map(str, missing_arguments)) - self.raise_error( - entry, field_uri, "Invalid config for {}: missing required fields: {}".format(field_uri, arguments) - ) - - if extra_arguments: - unknown_options_error = "specifies unknown options: {}".format(extra_arguments) - message = "{} {}".format(field_uri, unknown_options_error) - - if self.on_extra_argument == _ExtraArgumentBehaviour.WARN: - warnings.warn(message) - if self.on_extra_argument == _ExtraArgumentBehaviour.ERROR: - self.raise_error(entry, field_uri, message) - - @property - def known_fields(self): - return set(self.fields) - - def raise_error(self, value, field_uri, reason=None): - if self.on_error: - self.on_error(value, field_uri, reason) - else: - raise ConfigError(reason) - - -class BaseField(BaseValidator): - def __init__(self, optional=False, allow_none=False, description=None, **kwargs): - super().__init__(**kwargs) - self.optional = optional - self.allow_none = allow_none - self.description = description - - def validate(self, entry, field_uri=None): - super().validate(entry, field_uri) - field_uri = field_uri or self.field_uri - if not self.allow_none and entry is None: - raise ConfigError("{} is not allowed to be None".format(field_uri)) - - @property - def type(self): - return str - - -class StringField(BaseField): - def __init__(self, choices=None, regex=None, case_sensitive=False, **kwargs): - super().__init__(**kwargs) - self.choices = choices if case_sensitive or not choices else list(map(str.lower, choices)) - self.regex = re.compile(regex, flags=re.IGNORECASE if not case_sensitive else 0) if regex else None - self.case_sensitive = case_sensitive - - def validate(self, entry, field_uri=None): - super().validate(entry, field_uri) - if entry is None: - return - - field_uri = field_uri or self.field_uri - source_entry = entry - - if not isinstance(entry, str): - raise ConfigError("{} is expected to be str".format(source_entry)) - - if not self.case_sensitive: - entry = entry.lower() - - if self.choices and entry not in self.choices: - reason = "unsupported option, expected one of: {}".format(', '.join(map(str, self.choices))) - self.raise_error(source_entry, field_uri, reason) - - if self.regex and not self.regex.match(entry): - self.raise_error(source_entry, field_uri, reason=None) - - @property - def type(self): - return str - - -class DictField(BaseField): - def __init__(self, key_type=None, value_type=None, validate_keys=True, validate_values=True, allow_empty=True, - **kwargs): - super().__init__(**kwargs) - self.validate_keys = validate_keys if key_type else False - self.validate_values = validate_values if value_type else False - self.key_type = _get_field_type(key_type) - self.value_type = _get_field_type(value_type) - - self.allow_empty = allow_empty - - def validate(self, entry, field_uri=None): - super().validate(entry, field_uri) - if entry is None: - return - - field_uri = field_uri or self.field_uri - if not isinstance(entry, dict): - raise ConfigError("{} is expected to be dict".format(field_uri)) - - if not entry and not self.allow_empty: - self.raise_error(entry, field_uri, "value is empty") - - for k, v in entry.items(): - if self.validate_keys: - uri = "{}.keys.{}".format(field_uri, k) - self.key_type.validate(k, uri) - - if self.validate_values: - uri = "{}.{}".format(field_uri, k) - - self.value_type.validate(v, uri) - @property - def type(self): - return dict - - -class ListField(BaseField): - def __init__(self, value_type=None, validate_values=True, allow_empty=True, **kwargs): - super().__init__(**kwargs) - self.validate_values = validate_values if value_type else False - self.value_type = _get_field_type(value_type) - self.allow_empty = allow_empty - - def validate(self, entry, field_uri=None): - super().validate(entry, field_uri) - if entry is None: - return - - if not isinstance(entry, list): - raise ConfigError("{} is expected to be list".format(field_uri)) - - if not entry and not self.allow_empty: - self.raise_error(entry, field_uri, "value is empty") - - if self.validate_values: - for i, val in enumerate(entry): - self.value_type.validate(val, "{}[{}]".format(val, i)) - - @property - def type(self): - return list - - -class NumberField(BaseField): - def __init__(self, floats=True, min_value=None, max_value=None, allow_inf=False, allow_nan=False, **kwargs): - super().__init__(**kwargs) - self.floats = floats - self.min = min_value - self.max = max_value - self.allow_inf = allow_inf - self.allow_nan = allow_nan - - def validate(self, entry, field_uri=None): - super().validate(entry, field_uri) - if entry is None: - return - - field_uri = field_uri or self.field_uri - if not self.floats and isinstance(entry, float): - raise ConfigError("{} is expected to be int".format(field_uri)) - if not isinstance(entry, int) and not isinstance(entry, float): - raise ConfigError("{} is expected to be number".format(field_uri)) - - if self.min is not None and entry < self.min: - reason = "value is less than minimal allowed - {}".format(self.min) - self.raise_error(entry, field_uri, reason) - if self.max is not None and entry > self.max: - reason = "value is greater than maximal allowed - {}".format(self.max) - self.raise_error(entry, field_uri, reason) - - if math.isinf(entry) and not self.allow_inf: - self.raise_error(entry, field_uri, "value is infinity") - if math.isnan(entry) and not self.allow_nan: - self.raise_error(entry, field_uri, "value is NaN") - - @property - def type(self): - return float if self.floats else int - - -class PathField(BaseField): - def __init__(self, is_directory=False, check_exists=True, **kwargs): - super().__init__(**kwargs) - self.is_directory = is_directory - self.check_exists = check_exists - - def validate(self, entry, field_uri=None): - super().validate(entry, field_uri) - if entry is None: - return - - field_uri = field_uri or self.field_uri - try: - get_path(entry, self.is_directory, self.check_exists) - except TypeError: - self.raise_error(entry, field_uri, "values is expected to be path-like") - except FileNotFoundError: - self.raise_error(entry, field_uri, "path does not exist") - except NotADirectoryError: - self.raise_error(entry, field_uri, "path is not a directory") - except IsADirectoryError: - self.raise_error(entry, field_uri, "path is a directory, regular file expected") - - @property - def type(self): - return Path - - -class BoolField(BaseField): - def validate(self, entry, field_uri=None): - super().validate(entry, field_uri) - if entry is None: - return - - field_uri = field_uri or self.field_uri - if not isinstance(entry, bool): - raise ConfigError("{} is expected to be bool".format(field_uri)) - - @property - def type(self): - return string_to_bool - - -def _get_field_type(key_type): - if not isinstance(key_type, BaseField): - type_ = _TYPE_TO_FIELD_CLASS.get(key_type) - if callable(type_): - return type_() - - return key_type - - -_TYPE_TO_FIELD_CLASS = { - int: partial(NumberField, floats=False), - float: partial(NumberField, floats=True), - dict: partial(DictField, validate_keys=False, validate_values=False), - list: partial(ListField, validate_values=False), - Path: PathField, - str: StringField, - bool: BoolField, -} diff --git a/tools/accuracy_checker/accuracy_checker/data_readers/__init__.py b/tools/accuracy_checker/accuracy_checker/data_readers/__init__.py deleted file mode 100644 index 8906529aa5e3ca..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/data_readers/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from .data_reader import ( - BaseReader, - DataReaderField, - ReaderCombiner, - JSONReaderConfig, - OpenCVFrameReader, - OpenCVImageReader, - PillowImageReader, - ScipyImageReader, - NiftiImageReader, - - DataRepresentation, - ClipIdentifier, - create_reader -) - -__all__ = [ - 'BaseReader', - 'DataReaderField', - 'DataRepresentation', - 'ReaderCombiner', - 'JSONReaderConfig', - 'OpenCVFrameReader', - 'OpenCVImageReader', - 'PillowImageReader', - 'ScipyImageReader', - 'NiftiImageReader', - - 'DataRepresentation', - 'ClipIdentifier', - 'create_reader' -] diff --git a/tools/accuracy_checker/accuracy_checker/data_readers/data_reader.py b/tools/accuracy_checker/accuracy_checker/data_readers/data_reader.py deleted file mode 100644 index 66c3c4d6b895eb..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/data_readers/data_reader.py +++ /dev/null @@ -1,267 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -from pathlib import Path -from functools import singledispatch -from collections import OrderedDict, namedtuple -import re -import cv2 -from PIL import Image -import scipy.misc -import numpy as np -import nibabel as nib - -from ..utils import get_path, read_json, zipped_transform, set_image_metadata -from ..dependency import ClassProvider -from ..config import BaseField, StringField, ConfigValidator, ConfigError, DictField - - -class DataRepresentation: - def __init__(self, data, meta=None, identifier=''): - self.identifier = identifier - self.data = data - self.metadata = meta or {} - if np.isscalar(data): - self.metadata['image_size'] = 1 - elif isinstance(data, list) and np.isscalar(data[0]): - self.metadata['image_size'] = len(data) - else: - self.metadata['image_size'] = data.shape if not isinstance(data, list) else data[0].shape - - -ClipIdentifier = namedtuple('ClipIdentifier', ['video', 'clip_id', 'frames']) - - -def create_reader(config): - return BaseReader.provide(config.get('type', 'opencv_imread'), config.get('data_source'), config=config) - - -class DataReaderField(BaseField): - def validate(self, entry_, field_uri=None): - super().validate(entry_, field_uri) - - if entry_ is None: - return - - field_uri = field_uri or self.field_uri - if isinstance(entry_, str): - StringField(choices=BaseReader.providers).validate(entry_, 'reader') - elif isinstance(entry_, dict): - class DictReaderValidator(ConfigValidator): - type = StringField(choices=BaseReader.providers) - dict_reader_validator = DictReaderValidator( - 'reader', on_extra_argument=DictReaderValidator.IGNORE_ON_EXTRA_ARGUMENT - ) - dict_reader_validator.validate(entry_) - else: - self.raise_error(entry_, field_uri, 'reader must be either string or dictionary') - - -class BaseReader(ClassProvider): - __provider_type__ = 'reader' - - def __init__(self, data_source, config=None): - self.config = config - self.data_source = data_source - self.read_dispatcher = singledispatch(self.read) - self.read_dispatcher.register(list, self._read_list) - self.read_dispatcher.register(ClipIdentifier, self._read_clip) - - self.validate_config() - self.configure() - - def __call__(self, context=None, identifier=None, **kwargs): - if identifier is not None: - return self.read_item(identifier) - - if not context: - raise ValueError('identifier or context should be specified') - - read_data = [self.read_item(identifier) for identifier in context.identifiers_batch] - context.data_batch = read_data - context.annotation_batch, context.data_batch = zipped_transform( - set_image_metadata, - context.annotation_batch, - context.data_batch - ) - return context - - def configure(self): - self.data_source = get_path(self.data_source, is_directory=True) - - def validate_config(self): - pass - - def read(self, data_id): - raise NotImplementedError - - def _read_list(self, data_id): - return [self.read(identifier) for identifier in data_id] - - def _read_clip(self, data_id): - video = Path(data_id.video) - frames_identifiers = [video / frame for frame in data_id.frames] - return self.read_dispatcher(frames_identifiers) - - def read_item(self, data_id): - return DataRepresentation(self.read_dispatcher(data_id), identifier=data_id) - - - -class ReaderCombinerConfig(ConfigValidator): - type = StringField() - scheme = DictField( - value_type=DataReaderField(), key_type=StringField(), allow_empty=False - ) - - -class ReaderCombiner(BaseReader): - __provider__ = 'combine_reader' - - def validate_config(self): - config_validator = ReaderCombinerConfig('reader_combiner_config') - config_validator.validate(self.config) - - def configure(self): - scheme = self.config['scheme'] - reading_scheme = OrderedDict() - for pattern, reader_config in scheme.items(): - reader = BaseReader.provide( - reader_config['type'] if isinstance(reader_config, dict) else reader_config, - self.data_source, reader_config - ) - pattern = re.compile(pattern) - reading_scheme[pattern] = reader - - self.reading_scheme = reading_scheme - - def read(self, data_id): - for pattern, reader in self.reading_scheme.items(): - if pattern.match(str(data_id)): - return reader.read(data_id) - - raise ConfigError('suitable data reader for {} not found'.format(data_id)) - - -class OpenCVImageReader(BaseReader): - __provider__ = 'opencv_imread' - - def read(self, data_id): - return cv2.imread(str(get_path(self.data_source / data_id))) - - -class PillowImageReader(BaseReader): - __provider__ = 'pillow_imread' - - def __init__(self, config=None): - super().__init__(config) - self.convert_to_rgb = True - - def read(self, data_id): - with open(str(self.data_source / data_id), 'rb') as f: - img = Image.open(f) - - return np.array(img.convert('RGB') if self.convert_to_rgb else img) - - -class ScipyImageReader(BaseReader): - __provider__ = 'scipy_imread' - - def read(self, data_id): - return np.array(scipy.misc.imread(str(get_path(self.data_source / data_id)))) - - -class OpenCVFrameReader(BaseReader): - __provider__ = 'opencv_capture' - - def __init__(self, data_source, config=None): - super().__init__(data_source, config) - self.current = -1 - - def read(self, data_id): - if data_id < 0: - raise IndexError('frame with {} index can not be grabbed, non-negative index is expected') - if data_id < self.current: - self.videocap.set(cv2.CAP_PROP_POS_FRAMES, data_id) - self.current = data_id - 1 - - return self._read_sequence(data_id) - - def _read_sequence(self, data_id): - frame = None - while self.current != data_id: - success, frame = self.videocap.read() - self.current += 1 - if not success: - raise EOFError('frame with {} index does not exists in {}'.format(self.current, self.data_source)) - - return frame - - def configure(self): - self.data_source = get_path(self.data_source) - self.videocap = cv2.VideoCapture(str(self.data_source)) - - -class JSONReaderConfig(ConfigValidator): - type = StringField() - key = StringField(optional=True, case_sensitive=True) - - -class JSONReader(BaseReader): - __provider__ = 'json_reader' - - def validate_config(self): - config_validator = JSONReaderConfig('json_reader_config') - config_validator.validate(self.config) - - def configure(self): - self.key = self.config.get('key') - - def read(self, data_id): - data = read_json(str(self.data_source / data_id)) - if self.key: - data = data.get(self.key) - - if not data: - raise ConfigError('{} does not contain {}'.format(data_id, self.key)) - - return np.array(data).astype(np.float32) - - - -class NCF_DataReader(BaseReader): - __provider__ = 'ncf_data_reader' - - def configure(self): - pass - - def read(self, data_id): - if not isinstance(data_id, str): - raise IndexError('Data identifier must be a string') - - return float(data_id.split(":")[1]) - - -class NiftiImageReader(BaseReader): - __provider__ = 'nifti_reader' - - def read(self, data_id): - nib_image = nib.load(str(get_path(self.data_source / data_id))) - image = np.array(nib_image.dataobj) - if len(image.shape) != 4: # Make sure 4D - image = np.expand_dims(image, -1) - image = np.swapaxes(np.array(image), 0, -2) - - return image diff --git a/tools/accuracy_checker/accuracy_checker/dataset.py b/tools/accuracy_checker/accuracy_checker/dataset.py deleted file mode 100644 index f02add18dfac0b..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/dataset.py +++ /dev/null @@ -1,155 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from pathlib import Path - -from .annotation_converters import BaseFormatConverter, save_annotation, make_subset -from .config import ConfigValidator, StringField, PathField, ListField, DictField, BaseField, NumberField, ConfigError -from .utils import JSONDecoderWithAutoConversion, read_json, get_path, contains_all -from .representation import BaseRepresentation -from .data_readers import DataReaderField - - -class DatasetConfig(ConfigValidator): - """ - Specifies configuration structure for dataset - """ - name = StringField() - annotation = PathField(optional=True, check_exists=False) - data_source = PathField(optional=True, check_exists=False) - dataset_meta = PathField(optional=True, check_exists=False) - metrics = ListField(allow_empty=False, optional=True) - postprocessing = ListField(allow_empty=False, optional=True) - preprocessing = ListField(allow_empty=False, optional=True) - reader = DataReaderField(optional=True) - annotation_conversion = DictField(optional=True) - subsample_size = BaseField(optional=True) - subsample_seed = NumberField(floats=False, min_value=0, optional=True) - - -class Dataset: - def __init__(self, config_entry): - self._config = config_entry - self.batch = 1 - self.iteration = 0 - dataset_config = DatasetConfig('Dataset') - dataset_config.validate(self._config) - annotation, meta = None, None - use_converted_annotation = True - self._images_dir = Path(self._config.get('data_source', '')) - if 'annotation' in self._config: - annotation_file = Path(self._config['annotation']) - if annotation_file.exists(): - annotation = read_annotation(get_path(annotation_file)) - meta = self._load_meta() - use_converted_annotation = False - if not annotation and 'annotation_conversion' in self._config: - annotation, meta = self._convert_annotation() - - if not annotation: - raise ConfigError('path to converted annotation or data for conversion should be specified') - - subsample_size = self._config.get('subsample_size') - if subsample_size: - subsample_seed = self._config.get('subsample_seed', 666) - if isinstance(subsample_size, str): - if subsample_size.endswith('%'): - subsample_size = float(subsample_size[:-1]) / 100 * len(annotation) - subsample_size = int(subsample_size) - annotation = make_subset(annotation, subsample_size, subsample_seed) - - if use_converted_annotation and contains_all(self._config, ['annotation', 'annotation_conversion']): - annotation_name = self._config['annotation'] - meta_name = self._config.get('dataset_meta') - if meta_name: - meta_name = Path(meta_name) - save_annotation(annotation, meta, Path(annotation_name), meta_name) - - self._annotation = annotation - self._meta = meta - self.size = len(self._annotation) - self.name = self._config.get('name') - - @property - def annotation(self): - return self._annotation - - def __len__(self): - return self.size - - @property - def metadata(self): - return self._meta - - @property - def labels(self): - return self._meta.get('label_map', {}) - - def __call__(self, context, *args, **kwargs): - batch_annotation = self.__getitem__(self.iteration) - self.iteration += 1 - context.annotation_batch = batch_annotation - context.identifiers_batch = [annotation.identifier for annotation in batch_annotation] - - def __getitem__(self, item): - if self.size <= item * self.batch: - raise IndexError - - batch_start = item * self.batch - batch_end = min(self.size, batch_start + self.batch) - batch_annotation = self._annotation[batch_start:batch_end] - - return batch_annotation - - @staticmethod - def set_image_metadata(annotation, images): - image_sizes = [] - data = images.data - if not isinstance(data, list): - data = [data] - for image in data: - image_sizes.append(image.shape) - annotation.set_image_size(image_sizes) - - def set_annotation_metadata(self, annotation, image, data_source): - self.set_image_metadata(annotation, image.data) - annotation.set_data_source(data_source) - - def _load_meta(self): - meta_data_file = self._config.get('dataset_meta') - return read_json(meta_data_file, cls=JSONDecoderWithAutoConversion) if meta_data_file else None - - def _convert_annotation(self): - conversion_params = self._config.get('annotation_conversion') - converter = conversion_params['converter'] - annotation_converter = BaseFormatConverter.provide(converter, conversion_params) - annotation, meta = annotation_converter.convert() - - return annotation, meta - - -def read_annotation(annotation_file: Path): - annotation_file = get_path(annotation_file) - - result = [] - with annotation_file.open('rb') as file: - while True: - try: - result.append(BaseRepresentation.load(file)) - except EOFError: - break - - return result diff --git a/tools/accuracy_checker/accuracy_checker/dependency.py b/tools/accuracy_checker/accuracy_checker/dependency.py deleted file mode 100644 index 947a3ec08aaa31..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/dependency.py +++ /dev/null @@ -1,108 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -# pylint: disable=protected-access - - -class ProvidedWrapper: - def __init__(self, provided): - self.provided = provided - - -class UnresolvedDependencyException(ValueError): - - def __init__(self, provider, missing_dependencies) -> None: - super().__init__() - self.provider = provider - self.missing_dependencies = missing_dependencies - self.message = "Unresolved dependencies ({}) for provider {}".format( - ", ".join(self.missing_dependencies), self.provider - ) - - -def get_opts(options): - """ - Args: - options: options object. - Returns: - args (tuple): positional options. - kwargs (map): keyword arguments. - """ - - if isinstance(options, tuple): - if len(options) == 2 and isinstance(options[-1], dict): - args, kwargs = options - else: - args = options - kwargs = {} - elif isinstance(options, dict): - args, kwargs = (), options - else: - raise ValueError("Options object expected to be either pair of (args, kwargs) or only args/kwargs") - - return args, kwargs - - -class BaseProvider: - providers = {} - __provider_type__ = None - __provider__ = None - - @classmethod - def provide(cls, provider, *args, **kwargs): - root_provider = cls.resolve(provider) - return root_provider(*args, **kwargs) - - @classmethod - def resolve(cls, name): - if name not in cls.providers: - raise ValueError("Requested provider not registered") - return cls.providers[name] - - -class ClassProviderMeta(type): - def __new__(mcs, name, bases, attrs, **kwargs): - cls = super().__new__(mcs, name, bases, attrs) - # do not create container for abstract provider - if '_is_base_provider' in attrs: - return cls - - assert issubclass(cls, ClassProvider), "Do not use metaclass directly" - if '__provider_type__' in attrs: - cls.providers = {} - else: - cls.register_provider(cls) - - return cls - - -class ClassProvider(BaseProvider, metaclass=ClassProviderMeta): - _is_base_provider = True - - @classmethod - def get_provider_name(cls): - return getattr(cls, '__provider__', cls.__name__) - - @classmethod - def register_provider(cls, provider): - provider_name = cls.get_provider_name() - if not provider_name: - return - cls.providers[provider_name] = provider - - -def provide(service): - return ProvidedWrapper(service) diff --git a/tools/accuracy_checker/accuracy_checker/evaluators/__init__.py b/tools/accuracy_checker/accuracy_checker/evaluators/__init__.py deleted file mode 100644 index 278615cfb78d59..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/evaluators/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .model_evaluator import ModelEvaluator -from .pipeline_evaluator import PipeLineEvaluator, get_processing_info - -__all__ = [ - 'ModelEvaluator', - 'PipeLineEvaluator', - 'get_processing_info' -] diff --git a/tools/accuracy_checker/accuracy_checker/evaluators/model_evaluator.py b/tools/accuracy_checker/accuracy_checker/evaluators/model_evaluator.py deleted file mode 100644 index 21620b6060ccb3..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/evaluators/model_evaluator.py +++ /dev/null @@ -1,162 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import copy -import pickle - -from ..utils import get_path, set_image_metadata, extract_image_representations -from ..dataset import Dataset -from ..launcher import create_launcher, DummyLauncher, InputFeeder -from ..launcher.loaders import PickleLoader -from ..logging import print_info -from ..metrics import MetricsExecutor -from ..postprocessor import PostprocessingExecutor -from ..preprocessor import PreprocessingExecutor -from ..adapters import create_adapter -from ..config import ConfigError -from ..data_readers import BaseReader - - -class ModelEvaluator: - def __init__(self, launcher, input_feeder, adapter, reader, preprocessor, postprocessor, dataset, metric): - self.launcher = launcher - self.input_feeder = input_feeder - self.adapter = adapter - self.reader = reader - self.preprocessor = preprocessor - self.postprocessor = postprocessor - self.dataset = dataset - self.metric_executor = metric - - self._annotations = [] - self._predictions = [] - - @classmethod - def from_configs(cls, launcher_config, dataset_config): - dataset_name = dataset_config['name'] - data_reader_config = dataset_config.get('reader', 'opencv_imread') - data_source = dataset_config.get('data_source') - if isinstance(data_reader_config, str): - data_reader = BaseReader.provide(data_reader_config, data_source) - elif isinstance(data_reader_config, dict): - data_reader = BaseReader.provide(data_reader_config['type'], data_source, data_reader_config) - else: - raise ConfigError('reader should be dict or string') - - preprocessor = PreprocessingExecutor(dataset_config.get('preprocessing'), dataset_name) - dataset = Dataset(dataset_config) - launcher = create_launcher(launcher_config) - config_adapter = launcher_config.get('adapter') - adapter = None if not config_adapter else create_adapter(config_adapter, launcher, dataset) - input_feeder = InputFeeder(launcher.config.get('inputs') or [], launcher.get_all_inputs()) - launcher.const_inputs = input_feeder.const_inputs - postprocessor = PostprocessingExecutor(dataset_config.get('postprocessing'), dataset_name, dataset.metadata) - metric_dispatcher = MetricsExecutor(dataset_config.get('metrics', []), dataset) - - return cls( - launcher, input_feeder, adapter, data_reader, preprocessor, postprocessor, dataset, metric_dispatcher - ) - - def process_dataset(self, stored_predictions, progress_reporter, *args, **kwargs): - if self._is_stored(stored_predictions) or isinstance(self.launcher, DummyLauncher): - self._annotations, self._predictions = self.load(stored_predictions, progress_reporter) - self._annotations, self._predictions = self.postprocessor.full_process(self._annotations, self._predictions) - - self.metric_executor.update_metrics_on_batch(self._annotations, self._predictions) - return self._annotations, self._predictions - - self.dataset.batch = self.launcher.batch - predictions_to_store = [] - for batch_id, batch_annotation in enumerate(self.dataset): - batch_identifiers = [annotation.identifier for annotation in batch_annotation] - batch_input = [self.reader(identifier=identifier) for identifier in batch_identifiers] - for annotation, input_data in zip(batch_annotation, batch_input): - set_image_metadata(annotation, input_data) - annotation.metadata['data_source'] = self.reader.data_source - batch_input = self.preprocessor.process(batch_input, batch_annotation) - _, batch_meta = extract_image_representations(batch_input) - filled_inputs = self.input_feeder.fill_non_constant_inputs(batch_input) - batch_predictions = self.launcher.predict(filled_inputs, batch_meta, *args, **kwargs) - if self.adapter: - self.adapter.output_blob = self.adapter.output_blob or self.launcher.output_blob - batch_predictions = self.adapter.process(batch_predictions, batch_identifiers, batch_meta) - - if stored_predictions: - predictions_to_store.extend(copy.deepcopy(batch_predictions)) - - annotations, predictions = self.postprocessor.process_batch(batch_annotation, batch_predictions) - if not self.postprocessor.has_dataset_processors: - self.metric_executor.update_metrics_on_batch(annotations, predictions) - - self._annotations.extend(annotations) - self._predictions.extend(predictions) - - if progress_reporter: - progress_reporter.update(batch_id, len(batch_predictions)) - - if progress_reporter: - progress_reporter.finish() - - if stored_predictions: - self.store_predictions(stored_predictions, predictions_to_store) - - if self.postprocessor.has_dataset_processors: - self.metric_executor.update_metrics_on_batch(self._annotations, self._predictions) - - return self.postprocessor.process_dataset(self._annotations, self._predictions) - - @staticmethod - def _is_stored(stored_predictions=None): - if not stored_predictions: - return False - - try: - get_path(stored_predictions) - return True - except OSError: - return False - - def compute_metrics(self, output_callback=None, ignore_results_formatting=False): - for result_presenter, evaluated_metric in self.metric_executor.iterate_metrics( - self._annotations, self._predictions): - result_presenter.write_result(evaluated_metric, output_callback, ignore_results_formatting) - - def load(self, stored_predictions, progress_reporter): - self._annotations = self.dataset.annotation - launcher = self.launcher - if not isinstance(launcher, DummyLauncher): - launcher = DummyLauncher({ - 'framework': 'dummy', - 'loader': PickleLoader.__provider__, - 'data_path': stored_predictions - }, adapter=None) - - predictions = launcher.predict([annotation.identifier for annotation in self._annotations]) - - if progress_reporter: - progress_reporter.finish(False) - - return self._annotations, predictions - - @staticmethod - def store_predictions(stored_predictions, predictions): - # since at the first time file does not exist and then created we can not use it as a pathlib.Path object - with open(stored_predictions, "wb") as content: - pickle.dump(predictions, content) - print_info("prediction objects are save to {}".format(stored_predictions)) - - def release(self): - self.launcher.release() diff --git a/tools/accuracy_checker/accuracy_checker/evaluators/pipeline_evaluator.py b/tools/accuracy_checker/accuracy_checker/evaluators/pipeline_evaluator.py deleted file mode 100644 index e34e70104b8720..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/evaluators/pipeline_evaluator.py +++ /dev/null @@ -1,229 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from collections import OrderedDict -import numpy as np - -from ..pipeline_connectors import create_connection_description -from ..utils import get_indexs, find_nearest -from ..adapters import create_adapter -from ..data_readers import create_reader -from ..dataset import Dataset -from ..launcher import create_launcher, InputFeeder -from ..metrics import MetricsExecutor -from ..pipeline_connectors import StageConnectionDescription, Connection -from ..postprocessor import PostprocessingExecutor -from..preprocessor import PreprocessingExecutor - - -def get_processing_info(pipeline_config): - name = pipeline_config['name'] - stages = pipeline_config['stages'] - dataset_name = stages[0]['dataset']['name'] - launcher = {} - for stage in stages: - if 'launcher' in stage: - launcher = stage['launcher'] - break - framework = launcher.get('framework') - device = launcher.get('device') - tags = launcher.get('tags') - - return name, framework, device, tags, dataset_name - - -def create_launcher_attribution(launchers_ids, launchers, datasets_ids, datasets, executors, executor_types): - launchers_ids = np.array(launchers_ids) - datasets_ids = np.array(datasets_ids) - for launcher_id_info, launcher in zip(enumerate(launchers_ids), launchers): - iteration, launcher_id = launcher_id_info - input_feeder = InputFeeder( - launcher.config.get('inputs', []), launcher.get_all_inputs(), launcher.fit_to_input - ) - launchers_ids[iteration:] += 1 - executors.insert(launcher_id, input_feeder) - executor_types.insert(launcher_id, 'input_feeder') - adapter_config = launcher.config.get('adapter') - dataset_id = find_nearest(datasets_ids, launcher_id, 'less') - datasets_ids[dataset_id + 1:] += 1 - dataset = datasets[dataset_id] if dataset_id != -1 else None - launcher_id += 1 - if adapter_config: - adapter = create_adapter(adapter_config, launcher, dataset) - executors.insert(launcher_id + 1, adapter) - executor_types.insert(launcher_id + 1, 'adapter') - if dataset_id != datasets_ids.size - 1: - datasets_ids[dataset_id + 1:] += 1 - if iteration != launchers_ids.size - 1: - launchers_ids[iteration + 1:] += 1 - - -def set_metrics_dataset(metrics_ids, metrics_executors, datasets_ids, datasets): - for metrics_id, metric_executor in zip(metrics_ids, metrics_executors): - dataset_id = find_nearest(datasets_ids, metrics_id, 'less') - if dataset_id != -1: - metric_executor.dataset = datasets[dataset_id].metadata - - -class PipeLineStage: - def __init__(self, evaluation_context, executors): - self._evaluation_context = evaluation_context - self.executors = executors - - def run(self): - for executor in self.executors: - executor(self.evaluation_context) - - @classmethod - def from_configs(cls, stage_name, stage_config): - config_mapping = { - 'dataset': Dataset, - 'preprocessing': PreprocessingExecutor, - 'launcher': create_launcher, - 'postprocessing': PostprocessingExecutor, - 'metrics': MetricsExecutor, - 'reader': create_reader, - } - - executor_types = [] - executors = [] - for key, config in stage_config.items(): - if key in config_mapping: - connection = create_connection_description(config, stage_name) - if connection: - executors.append(connection) - executor_types.append('connection') - executor_creator = config_mapping[key] - executor = executor_creator(config) - executor_types.append(key) - executors.append(executor) - - dataset_ids = get_indexs(executor_types, 'dataset') - datasets = [executors[idx] for idx in dataset_ids] - launcher_ids = get_indexs(executor_types, 'launcher') - launchers = [executors[idx] for idx in launcher_ids] - create_launcher_attribution(launcher_ids, launchers, dataset_ids, datasets, executors, executor_types) - - metrics_executors_id = get_indexs(executor_types, 'metrics') - dataset_ids = get_indexs(executor_types, 'dataset') - metrics_executors = [executors[idx] for idx in metrics_executors_id] - set_metrics_dataset(metrics_executors_id, metrics_executors, dataset_ids, datasets) - dataset = datasets[0] if datasets else None - eval_context = EvaluationContext(dataset, metrics_executors, launchers) - - return cls(eval_context, executors) - - @property - def evaluation_context(self): - return self._evaluation_context - - @evaluation_context.setter - def evaluation_context(self, new_context): - _shared_context = new_context.shared_context - for field, value in _shared_context.items(): - if value: - setattr(self._evaluation_context, field, value) - - -class EvaluationContext: - def __init__(self, dataset, metric_executor=None, launcher=None): - self.annotations = [] - self.predictions = [] - self.annotation_batch = [] - self.prediction_batch = [] - self.data_batch = [] - self.metrics_results = [] - self.identifiers_batch = [] - self.metrics_executor = metric_executor - self.dataset_size = dataset.size if dataset else 0 - self.launcher = launcher - self.dataset = dataset - - @property - def shared_context(self): - _shared_context = { - 'annotations': self.annotations, - 'predictions': self.predictions, - 'annotation_batch': self.annotation_batch, - 'prediction_batch': self.prediction_batch, - 'data_batch': self.data_batch, - 'identifiers_batch': self.identifiers_batch - } - return _shared_context - - -class PipeLineEvaluator: - def __init__(self, stages): - self.stages = stages - self.create_connectors() - self.context = next(iter(stages.values())).evaluation_context - - @classmethod - def from_configs(cls, pipeline_config): - stages = OrderedDict() - for stage_config in pipeline_config: - stage_name = stage_config['stage'] - evaluation_stage = PipeLineStage.from_configs(stage_name, stage_config) - stages[stage_name] = evaluation_stage - return cls(stages) - - def create_connectors(self): - def make_connection(stages, connection_template): - return Connection(stages, connection_template) - - def replace_connections(stage, all_stages): - for executor_id, executor in enumerate(stage.executors): - if isinstance(executor, StageConnectionDescription): - connector = make_connection(all_stages, executor) - stage.executors[executor_id] = connector - - for _, stage in self.stages.items(): - replace_connections(stage, self.stages) - - def process_dataset(self, stored_predictions, progress_reporter, *args, **kwargs): - self.progress_reporter = progress_reporter - dataset_size = self.context.dataset_size - dataset_size = dataset_size if dataset_size else 0 - self.progress_reporter.reset(dataset_size) - iteration = 0 - previous_context = self.context - while self.progress_reporter.progress != 100: - for _, stage in self.stages.items(): - stage.evaluation_context = previous_context - stage.run() - previous_context = stage.evaluation_context - iteration += 1 - progress_reporter.update(iteration, len(previous_context.data_batch)) - self.context = previous_context - - if progress_reporter: - progress_reporter.finish() - - def compute_metrics(self, output_callback=None, ignore_results_formatting=False): - def eval_metrics(metrics_executor, annotations, predictions): - for result_presenter, evaluated_metric in metrics_executor.iterate_metrics(annotations, predictions): - result_presenter.write_result(evaluated_metric, output_callback, ignore_results_formatting) - - for _, stage in self.stages.items(): - metrics_executors = stage.evaluation_context.metrics_executor - for metrics_executor in metrics_executors: - eval_context = stage.evaluation_context - eval_metrics(metrics_executor, eval_context.annotations, eval_context.predictions) - - def release(self): - for _, stage in self.stages.items(): - for launcher in stage.evaluation_context.launcher: - launcher.release() diff --git a/tools/accuracy_checker/accuracy_checker/launcher/__init__.py b/tools/accuracy_checker/accuracy_checker/launcher/__init__.py deleted file mode 100644 index 5ab09b085a200d..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/launcher/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from .dummy_launcher import DummyLauncher -from .launcher import Launcher, create_launcher, unsupported_launcher -from .input_feeder import InputFeeder - -try: - from .caffe_launcher import CaffeLauncher -except ImportError as import_error: - CaffeLauncher = unsupported_launcher( - 'caffe', "Caffe isn't installed. Please, install it before using. \n{}".format(import_error.msg) - ) - -try: - from .dlsdk_launcher import DLSDKLauncher -except ImportError as import_error: - DLSDKLauncher = unsupported_launcher( - 'dlsdk', "IE Python isn't installed. Please, install it before using. \n{}".format(import_error.msg) - ) - -__all__ = ['create_launcher', 'Launcher', 'CaffeLauncher', 'DLSDKLauncher', 'DummyLauncher', 'InputFeeder'] diff --git a/tools/accuracy_checker/accuracy_checker/launcher/caffe_installation_readme.md b/tools/accuracy_checker/accuracy_checker/launcher/caffe_installation_readme.md deleted file mode 100644 index 8118dcd557e876..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/launcher/caffe_installation_readme.md +++ /dev/null @@ -1,56 +0,0 @@ -# Caffe Installation Tips - -## Install OpenCV 3.3 or later with Python3 bindings - -Accuracy Checker uses OpenCV library for image processing. You can miss this step if you are using OpenCV from [OpenVINO toolkit][openvino-get-started]. - -```bash -sudo apt-get install libopencv-dev -pip install opencv-python -``` - -## Install Caffe with Python3 bindings - -* Clone repository: - -```bash -git clone https://github.com/BVLC/caffe.git -cd caffe -``` - -* Install Caffe dependencies: - -```bash -sudo apt-get install libprotobuf-dev libleveldb-dev libsnappy-dev libhdf5-serial-dev protobuf-compiler libgflags-dev libgoogle-glog-dev liblmdb-dev -sudo apt-get install --no-install-recommends libboost-all-dev -pip install -r python/requirements.txt -pip install matplotlib -``` - -* Build - -If you need CPU only version of caffe add `-DCPU_ONLY=ON` to cmake command. - -```bash -mkdir build && cd build -cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX= -Dpython_version=3 -DBLAS=open .. -make -sudo make install -``` - -* Copy Python library to your python installation. - -```bash -cp -r ../python/caffe $VIRTUAL_ENV/lib/python3.5/site-packages -cp --remove-destination lib/_caffe.so $VIRTUAL_ENV/lib/python3.5/site-packages/caffe -``` - -## Check your installation - -You can test prerequisites with the following command. If it does not fail, then you are installed prerequisites correctly: - -```bash -python3 -c 'import caffe, cv2' -``` - -[openvino-get-started]: https://software.intel.com/en-us/openvino-toolkit/documentation/get-started diff --git a/tools/accuracy_checker/accuracy_checker/launcher/caffe_launcher.py b/tools/accuracy_checker/accuracy_checker/launcher/caffe_launcher.py deleted file mode 100644 index b9c28c50394a5d..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/launcher/caffe_launcher.py +++ /dev/null @@ -1,135 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import re - -import caffe - -from ..config import PathField, StringField, NumberField, BoolField -from .launcher import Launcher, LauncherConfig - -DEVICE_REGEX = r'(?Pcpu$|gpu)(_(?P\d+))?' - - -class CaffeLauncherConfig(LauncherConfig): - """ - Specifies configuration structure for Caffe launcher. - """ - - model = PathField() - weights = PathField() - device = StringField(regex=DEVICE_REGEX) - batch = NumberField(floats=False, min_value=1, optional=True) - output_name = StringField(optional=True) - allow_reshape_input = BoolField(optional=True) - - -class CaffeLauncher(Launcher): - """ - Class for infer model using Caffe framework. - """ - - __provider__ = 'caffe' - - def __init__(self, config_entry: dict, *args, **kwargs): - super().__init__(config_entry, *args, **kwargs) - - caffe_launcher_config = CaffeLauncherConfig('Caffe_Launcher') - caffe_launcher_config.validate(self.config) - - self.model = str(self.config['model']) - self.weights = str(self.config['weights']) - - self.network = caffe.Net(self.model, self.weights, caffe.TEST) - self.allow_reshape_input = self.config.get('allow_reshape_input', False) - - match = re.match(DEVICE_REGEX, self.config['device'].lower()) - if match.group('device') == 'gpu': - caffe.set_mode_gpu() - identifier = match.group('identifier') or 0 - caffe.set_device(int(identifier)) - elif match.group('device') == 'cpu': - caffe.set_mode_cpu() - - self._batch = self.config.get('batch', 1) - self.const_inputs = self.config.get('_list_const_inputs', []) - - @property - def inputs(self): - """ - Returns: - inputs in NCHW format. - """ - self._inputs_shapes = {} - - for input_blob in self.network.inputs: - if input_blob in self.const_inputs: - continue - channels, height, width = self.network.blobs[input_blob].data.shape[1:] - self.network.blobs[input_blob].reshape(self._batch, channels, height, width) - self._inputs_shapes[input_blob] = channels, height, width - - return self._inputs_shapes - - @property - def batch(self): - return self._batch - - @property - def output_blob(self): - return next(iter(self.network.outputs)) - - def predict(self, inputs, metadata, *args, **kwargs): - """ - Args: - inputs: dictionary where keys are input layers names and values are data for them. - metadata: metadata of input representations - Returns: - raw data from network. - """ - results = [] - for infer_input in inputs: - for input_blob in self.network.inputs: - if input_blob in self.const_inputs: - continue - - data = infer_input[input_blob] - if self.allow_reshape_input: - self.network.blobs[input_blob].reshape(*data.shape) - - if data.shape[0] != self._batch: - self.network.blobs[input_blob].reshape( - data.shape[0], *self.network.blobs[input_blob].data.shape[1:] - ) - - results.append(self.network.forward(**infer_input)) - for image_meta in metadata: - self._provide_inputs_info_to_meta(image_meta) - - return results - - def get_all_inputs(self): - inputs_map = {} - for input_blob in self.network.inputs: - inputs_map[input_blob] = self.network.blobs[input_blob].data.shape - - return inputs_map - - def release(self): - """ - Releases launcher. - """ - del self.network diff --git a/tools/accuracy_checker/accuracy_checker/launcher/caffe_launcher_readme.md b/tools/accuracy_checker/accuracy_checker/launcher/caffe_launcher_readme.md deleted file mode 100644 index 2ff60137288bd3..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/launcher/caffe_launcher_readme.md +++ /dev/null @@ -1,24 +0,0 @@ -# How to configure Caffe launcher - -For enabling Caffe launcher you need to add `framework: caffe` in launchers section of your configuration file and provide following parameters: - -* `device` - specifies which device will be used for infer (`cpu`, `gpu_0` and so on). -* `model` - path to prototxt file with Caffe model for your topology. -* `weights` - path to caffemodel file with weights for your topology. -* `adapter` - approach how raw output will be converted to representation of dataset problem, some adapters can be specific to framework. You can find detailed instruction how to use adapters [here][adapters]. - -You also can specify batch size for your model using `batch` and allow to reshape input layer to data shape, using specific parameter: `allow_reshape_input` (default value is False). - -Caffe launcher config example: - -```yml -launchers: - - framework: caffe - device: CPU - model: path_to_model/alexnet.prototxt - weights: path_to_weights/alexnet.caffemodel - adapter: classification - batch: 4 -``` - -[adapters]: ./tools/accuracy_checker/accuracy_checker/adapters/README.md diff --git a/tools/accuracy_checker/accuracy_checker/launcher/dlsdk_launcher.py b/tools/accuracy_checker/accuracy_checker/launcher/dlsdk_launcher.py deleted file mode 100644 index ab7a1083ed2458..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/launcher/dlsdk_launcher.py +++ /dev/null @@ -1,474 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import subprocess -from pathlib import Path -import os -import platform -import numpy as np -from cpuinfo import get_cpu_info -import openvino.inference_engine as ie - -from ..config import ConfigError, NumberField, PathField, StringField, DictField, ListField, BoolField -from ..logging import warning -from ..utils import read_yaml, contains_all, get_path, contains_any -from .launcher import Launcher, LauncherConfig -from .model_conversion import convert_model, FrameworkParameters -from ..logging import print_info - -HETERO_KEYWORD = 'HETERO:' -FPGA_COMPILER_MODE_VAR = 'CL_CONTEXT_COMPILER_MODE_INTELFPGA' -DEVICE_REGEX = r"(?:^{hetero}(?P(?:{devices})(?:,(?:{devices}))*)$)|(?:^(?P{devices})$)".format( - hetero=HETERO_KEYWORD, devices="|".join(plugin for plugin in ie.known_plugins) -) -VPU_PLUGINS = ('HDDL', "MYRIAD") -VPU_LOG_LEVELS = ('LOG_NONE', 'LOG_WARNING', 'LOG_INFO', 'LOG_DEBUG') - - -class CPUExtensionPathField(PathField): - def __init__(self, **kwargs): - super().__init__(is_directory=False, **kwargs) - - def validate(self, entry, field_uri=None): - if entry is None: - return - - field_uri = field_uri or self.field_uri - validation_entry = '' - try: - validation_entry = Path(entry) - except TypeError: - self.raise_error(entry, field_uri, "values is expected to be path-like") - is_directory = False - if validation_entry.parts[-1] == 'AUTO': - validation_entry = validation_entry.parent - is_directory = True - try: - get_path(validation_entry, is_directory) - except FileNotFoundError: - self.raise_error(validation_entry, field_uri, "path does not exist") - except NotADirectoryError: - self.raise_error(validation_entry, field_uri, "path is not a directory") - except IsADirectoryError: - self.raise_error(validation_entry, field_uri, "path is a directory, regular file expected") - - -class DLSDKLauncherConfig(LauncherConfig): - """ - Specifies configuration structure for DLSDK launcher. - """ - - device = StringField(regex=DEVICE_REGEX) - model = PathField(optional=True) - weights = PathField(optional=True) - caffe_model = PathField(optional=True) - caffe_weights = PathField(optional=True) - mxnet_weights = PathField(optional=True) - tf_model = PathField(optional=True) - tf_meta = PathField(optional=True) - onnx_model = PathField(optional=True) - kaldi_model = PathField(optional=True) - cpu_extensions = CPUExtensionPathField(optional=True) - gpu_extensions = PathField(optional=True) - bitstream = PathField(optional=True) - mo_params = DictField(optional=True) - mo_flags = ListField(optional=True) - outputs = ListField(optional=True) - allow_reshape_input = BoolField(optional=True) - affinity_map = PathField(optional=True) - batch = NumberField(floats=False, min_value=1, optional=True) - - _models_prefix = PathField(is_directory=True, optional=True) - _model_optimizer = PathField(optional=True, allow_none=True, is_directory=True) - _tf_obj_detection_api_config_dir = PathField(optional=True, allow_none=True, is_directory=True) - _tf_custom_op_config_dir = PathField(optional=True, allow_none=True, is_directory=True) - _cpu_extensions_mode = StringField(optional=True, allow_none=True) - _aocl = PathField(optional=True) - _vpu_log_level = StringField(optional=True, choices=VPU_LOG_LEVELS) - - def __init__(self, config_uri, **kwargs): - super().__init__(config_uri, **kwargs) - self.need_conversion = None - - def validate(self, entry, field_uri=None): - """ - Validate that launcher entry meets all configuration structure requirements. - - Args: - entry: launcher configuration file entry. - field_uri: id of launcher entry. - """ - - dlsdk_model_options = ['model', 'weights'] - caffe_model_options = ['caffe_model', 'caffe_weights'] - mxnet_model_options = ['mxnet_weights'] - tf_model_options = ['tf_model'] - tf_meta_options = ['tf_meta'] - onnx_model_options = ['onnx_model'] - kaldi_model_options = ['kaldi_model'] - - multiple_model_sources_err = ( - 'Either model and weights or caffe_model and caffe_weights ' - 'or mxnet_weights or tf_model or tf_meta should be specified.' - ) - sources = { - FrameworkParameters('dlsdk', False): dlsdk_model_options, - FrameworkParameters('caffe', False): caffe_model_options, - FrameworkParameters('tf', False): tf_model_options, - FrameworkParameters('mxnet', False): mxnet_model_options, - FrameworkParameters('onnx', False): onnx_model_options, - FrameworkParameters('kaldi', False): kaldi_model_options, - FrameworkParameters('tf', True): tf_meta_options - } - - specified = [] - for mo_source_option in sources: - if contains_all(entry, sources[mo_source_option]): - specified.append(mo_source_option) - - if not specified: - raise ConfigError('{} None provided'.format(multiple_model_sources_err)) - if len(specified) > 1: - raise ConfigError('{} Several provided'.format(multiple_model_sources_err)) - - self._set_model_source(specified[0]) - super().validate(entry, field_uri) - - def _set_model_source(self, framework): - self.need_conversion = framework.name != 'dlsdk' - self.framework = framework - self.fields['model'].optional = self.need_conversion - self.fields['weights'].optional = self.need_conversion - self.fields['caffe_model'].optional = framework.name != 'caffe' - self.fields['caffe_weights'].optional = framework.name != 'caffe' - self.fields['mxnet_weights'].optional = framework.name != 'mxnet' - self.fields['tf_model'].optional = framework != FrameworkParameters('tf', False) - self.fields['tf_meta'].optional = framework != FrameworkParameters('tf', True) - self.fields['onnx_model'].optional = framework.name != 'onnx' - self.fields['kaldi_model'].optional = framework.name != 'kaldi' - - -class DLSDKLauncher(Launcher): - """ - Class for infer model using DLSDK framework. - """ - - __provider__ = 'dlsdk' - - def __init__(self, config_entry): - super().__init__(config_entry) - - dlsdk_launcher_config = DLSDKLauncherConfig('DLSDK_Launcher') - dlsdk_launcher_config.validate(self.config) - - self._device = self.config['device'].upper() - self._set_variable = False - self._prepare_bitstream_firmware(self.config) - - if dlsdk_launcher_config.need_conversion: - self._model, self._weights = DLSDKLauncher.convert_model(self.config, dlsdk_launcher_config.framework) - else: - self._model = self.config['model'] - self._weights = self.config['weights'] - - self._create_ie_plugin() - self.network = ie.IENetwork(model=str(self._model), weights=str(self._weights)) - self.original_outputs = self.network.outputs - outputs = self.config.get('outputs') - if outputs: - self.network.add_outputs(outputs) - self.const_inputs = self.config.get('_list_const_inputs', []) - self._batch = self.config.get('batch', self.network.batch_size) - if self._batch != self.network.batch_size: - self._set_batch_size(self._batch) - affinity_map_path = self.config.get('affinity_map') - if affinity_map_path and self._is_hetero(): - self._set_affinity(affinity_map_path) - elif affinity_map_path: - warning('affinity_map config is applicable only for HETERO device') - self.exec_network = self.plugin.load(network=self.network) - self.allow_reshape_input = self.config.get('allow_reshape_input', False) - - @property - def inputs(self): - """ - Returns: - inputs in NCHW format. - """ - - # reverse and omit N - return {k: v.shape[1:] for k, v in self.network.inputs.items() if k not in self.const_inputs} - - @property - def batch(self): - return self._batch - - @property - def output_blob(self): - return next(iter(self.original_outputs)) - - def predict(self, inputs, metadata, *args, **kwargs): - """ - Args: - inputs: dictionary where keys are input layers names and values are data for them. - metadata: metadata of input representations - Returns: - raw data from network. - """ - results = [] - for infer_inputs in inputs: - input_shapes = {} - do_reshape = False - for input_blob in self.network.inputs: - if input_blob in self.const_inputs: - input_shapes[input_blob] = self.network.inputs[input_blob].shape - continue - - data = infer_inputs[input_blob] - input_shapes[input_blob] = data.shape - if self.allow_reshape_input: - if tuple(self.network.inputs[input_blob].shape) != data.shape: - do_reshape = True - - if do_reshape: - self._reshape_input(input_shapes) - - for input_blob, data in infer_inputs.items(): - if input_blob in self.const_inputs: - continue - infer_inputs[input_blob] = self._align_data_shape(data, input_blob) - - network_inputs_data = {**infer_inputs} - - benchmark = kwargs.get('benchmark') - if benchmark: - benchmark(network_inputs_data) - - result = self.exec_network.infer(network_inputs_data) - - raw_outputs_callback = kwargs.get('output_callback') - if raw_outputs_callback: - raw_outputs_callback(result) - - results.append(result) - for meta_ in metadata: - self._provide_inputs_info_to_meta(meta_) - - for meta in metadata: - self._provide_inputs_info_to_meta(meta) - - return results - - def _is_hetero(self): - return self._device.startswith(HETERO_KEYWORD) - - def _devices_list(self): - device = self._device - if HETERO_KEYWORD in self._device: - device = self._device[len(HETERO_KEYWORD):] - - return [platform_.upper().strip() for platform_ in device.split(',')] - - def _set_affinity(self, affinity_map_path): - self.plugin.set_initial_affinity(self.network) - layers = self.network.layers - for layer, device in read_yaml(affinity_map_path).items(): - if layer not in layers: - raise ConfigError('Layer \'{layer}\' is not present in network'.format(layer=layer)) - if device not in self._devices_list(): - raise ConfigError( - 'Device \'{device}\' set for \'{layer}\' layer is not present in ' - 'provided configuration \'{configuration}\''.format( - device=device, layer=layer, configuration=self._device - ) - ) - layers[layer].affinity = device - - def _is_fpga(self): - return 'FPGA' in self._devices_list() - - def _is_vpu(self): - return contains_any(self._devices_list(), VPU_PLUGINS) - - def _prepare_bitstream_firmware(self, config): - if not self._is_fpga(): - return - - compiler_mode = os.environ.get(FPGA_COMPILER_MODE_VAR) - if compiler_mode == '3': - return - - bitstream = config.get('bitstream') - if bitstream: - print_info('programming bitstream: {}'.format(bitstream.name)) - aocl_executable = config.get('_aocl') - if aocl_executable: - subprocess.run([str(aocl_executable), 'program', 'acl0', str(bitstream)], check=True) - os.environ[FPGA_COMPILER_MODE_VAR] = '3' - self._set_variable = True - else: - aocx_variable = 'DLA_AOCX' - previous_bitstream = os.environ.get(aocx_variable) - if previous_bitstream == str(bitstream): - return - os.environ[aocx_variable] = str(bitstream) - if not os.environ.get(aocx_variable): - warning('Warning: {} has not been set'.format(aocx_variable)) - - @staticmethod - def get_cpu_extension(cpu_extensions, selection_mode): - cpu_extensions_name = cpu_extensions.parts[-1] - if cpu_extensions_name != 'AUTO': - return cpu_extensions - extensions_path = cpu_extensions.parent - file_format = '{}.dll' if platform.system() == 'Windows' else 'lib{}.so' - if not selection_mode: - default_cpu_extension = file_format.format('cpu_extension') - extension_list = list(extensions_path.glob(default_cpu_extension)) - - if extension_list: - return extension_list[0] - - cpu_info_flags = get_cpu_info()['flags'] - supported_flags = ['avx512', 'avx2', 'sse4'] - for flag in supported_flags: - selection_mode = flag - if selection_mode in cpu_info_flags: - break - extension_list = list(extensions_path.glob(file_format.format('cpu_extension_{}'.format(selection_mode)))) - - if not extension_list: - raise ConfigError('suitable CPU extension lib not found in {}'.format(extensions_path)) - - return extension_list[0] - - @staticmethod - def convert_model(config, framework=FrameworkParameters('caffe', False)): - config_model = config.get('{}_model'.format(framework.name), '') - config_weights = config.get('{}_weights'.format(framework.name), '') - config_meta = config.get('{}_meta'.format(framework.name), '') - - mo_search_paths = [] - model_optimizer = config.get('_model_optimizer') - if model_optimizer: - mo_search_paths.append(model_optimizer) - - model_optimizer_directory_env = os.environ.get('MO_DIR') - if model_optimizer_directory_env: - mo_search_paths.append(model_optimizer_directory_env) - - model_name = ( - Path(config_model).name.rsplit('.', 1)[0] or - Path(config_weights).name.rsplit('.', 1)[0] or - Path(config_meta).name.rsplit('.', 1)[0] - ) - - return convert_model( - model_name, - config_model, config_weights, config_meta, framework, - mo_search_paths, config.get('mo_params'), - config.get('mo_flags'), - config.get('_tf_custom_op_config_dir'), - config.get('_tf_obj_detection_api_pipeline_config_path') - ) - - def get_all_inputs(self): - return self.network.inputs - - def _reshape_input(self, shapes): - self.network.reshape(shapes) - del self.exec_network - self.exec_network = self.plugin.load(network=self.network) - - def _set_batch_size(self, batch_size): - # in some cases we can not use explicit property for setting batch size, so we need to use reshape instead - # save const inputs without changes - const_inputs_shapes = { - input_name: self.network.inputs[input_name].shape for input_name in self.const_inputs - } - new_non_const_input_shapes = {} - for layer_name, layer in self.network.inputs.items(): - if layer_name in const_inputs_shapes: - continue - layer_shape = layer.shape - ind_batch = layer.layout.find('N') - if ind_batch != -1: - layer_shape[ind_batch] = batch_size - new_non_const_input_shapes[layer_name] = layer_shape - - self.network.reshape({**const_inputs_shapes, **new_non_const_input_shapes}) - - def _align_data_shape(self, data, input_blob): - input_shape = self.network.inputs[input_blob].shape - data_batch_size = data.shape[0] - input_batch_size = input_shape[0] - - if data_batch_size < input_batch_size: - warning_message = 'data batch {} is not equal model input batch_size {}. '.format( - data_batch_size, input_batch_size - ) - warning(warning_message) - diff_number = input_batch_size - data_batch_size - filled_part = [data[-1]] * diff_number - data = np.concatenate([data, filled_part]) - - if len(data.shape) > 1 and len(input_shape) > 1 and data.shape[1] != input_shape[1]: - data = data[:, :input_shape[1]] - - return data.reshape(input_shape) - - def _create_ie_plugin(self, log=True): - if hasattr(self, 'plugin'): - del self.plugin - self.plugin = ie.IEPlugin(self._device) - if log: - print_info('IE version: {}'.format(ie.get_version())) - print_info('Loaded {} plugin version: {}'.format(self.plugin.device, self.plugin.version)) - - cpu_extensions = self.config.get('cpu_extensions') - if cpu_extensions and 'CPU' in self._devices_list(): - selection_mode = self.config.get('_cpu_extensions_mode') - cpu_extensions = DLSDKLauncher.get_cpu_extension(cpu_extensions, selection_mode) - self.plugin.add_cpu_extension(str(cpu_extensions)) - gpu_extensions = self.config.get('gpu_extensions') - if gpu_extensions and 'GPU' in self._devices_list(): - self.plugin.set_config('CONFIG_FILE', str(gpu_extensions)) - if self._is_vpu(): - log_level = self.config.get('_vpu_log_level') - if log_level: - self.plugin.set_config({'VPU_LOG_LEVEL': log_level}) - - @staticmethod - def fit_to_input(data, input_layer): - shape_len = len(input_layer.shape) - if shape_len == 4: - if len(np.shape(data)) == 5: - data = data[0] - return np.transpose(data, [0, 3, 1, 2]) - if shape_len == 2: - if len(np.shape(data)) == 1: - return np.transpose([data]) - return np.array(data) - - def release(self): - if 'network' in self.__dict__: - del self.network - if 'exec_network' in self.__dict__: - del self.exec_network - if 'plugin' in self.__dict__: - del self.plugin - if self._set_variable: - del os.environ[FPGA_COMPILER_MODE_VAR] diff --git a/tools/accuracy_checker/accuracy_checker/launcher/dlsdk_launcher_readme.md b/tools/accuracy_checker/accuracy_checker/launcher/dlsdk_launcher_readme.md deleted file mode 100644 index 2a060f49fd6d70..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/launcher/dlsdk_launcher_readme.md +++ /dev/null @@ -1,55 +0,0 @@ -# How to configure OpenVINOâ„¢ launcher - -For enabling OpenVINOâ„¢ launcher you need to add `framework: dlsdk` in launchers section of your configuration file and provide following parameters: - -* `device` - specifies which device will be used for infer. Supported: `CPU`, `GPU`, `FPGA`, `MYRIAD` and Heterogeneous plugin as `HETERO:target_device,fallback_device`. -* `model` - path to xml file with Caffe model for your topology. -* `weights` - path to bin file with weights for your topology. - -launcher may optionally provide model parameters in source framework format which will be converted to Inference Engine IR using Model Optimizer. -If you want to use Model Optimizer for model conversion, please view [Model Optimizer Developer Guide][openvino-mo]. -You can provide: - -* `caffe_model` and `caffe_weights` for Caffe model and weights (*.prototxt and *.caffemodel). -* `tf_model` for TensorFlow model (*.pb, *.pb.frozen, *.pbtxt). -* `tf_meta` for TensorFlow MetaGraph (*.meta). -* `mxnet_weights` for MXNet params (*.params). -* `onnx_model` for ONNX model (*.onnx). -* `kaldi_model` for Kaldi model (*.nnet). - -In case when you want to determine additional parameters for model conversion (data_type, input_shape and so on), you can use `mo_params` for arguments with values and `mo_flags` for positional arguments like `legacy_mxnet_model` . -Full list of supported parameters you can find in Model Optimizer Developer Guide. - -Model will be converted before every evaluation. -You can provide `converted_model_dir` for saving converted model in specific folder, otherwise, converted models will be saved in path provided via `-C` command line argument or source model directory. - -* `adapter` - approach how raw output will be converted to representation of dataset problem, some adapters can be specific to framework. You can find detailed instruction how to use adapters [here][adapters]. - -Launcher understands which batch size will be used from model intermediate representation (IR). If you want to use batch for infer, please, provide model with required batch or convert it using specific parameter in `mo_params`. - -* `allow_reshape_input` - parameter, which allows to reshape input layer to data shape (default value is False). - -Additionally you can provide device specific parameters: - -* `cpu_extensions` (path to extension *.so file with custom layers for cpu). -* `gpu_extensions` (path to extension *.xml file with OpenCL kernel description for gpu). -* `bitstream` for running on FPGA. - -OpenVINOâ„¢ launcher config example: - -```yml -launchers: - - framework: dlsdk - device: HETERO:FPGA,CPU - caffe_model: path_to_model/alexnet.prototxt - caffe_weights: path_to_weights/alexnet.caffemodel - adapter: classification - mo_params: - batch: 4 - mo_flags: - - reverse_input_channels - cpu_extensions: cpu_extentions_avx512.so -``` - -[adapters]: ./tools/accuracy_checker/accuracy_checker/adapters/README.md -[openvino-mo]: https://software.intel.com/en-us/articles/OpenVINO-ModelOptimizer diff --git a/tools/accuracy_checker/accuracy_checker/launcher/dummy_launcher.py b/tools/accuracy_checker/accuracy_checker/launcher/dummy_launcher.py deleted file mode 100644 index ce004afdd97579..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/launcher/dummy_launcher.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from ..utils import get_path -from ..logging import print_info -from ..adapters import Adapter -from ..config import PathField, StringField -from .loaders import Loader -from .launcher import Launcher, LauncherConfig - - -class DummyLauncherConfig(LauncherConfig): - """ - Specifies configuration structure for Dummy launcher. - """ - - loader = StringField(choices=Loader.providers) - data_path = PathField() - adapter = StringField(choices=Adapter.providers, optional=True) - - -class DummyLauncher(Launcher): - """ - Class for using predictions from another tool. - """ - - __provider__ = 'dummy' - - def __init__(self, config_entry: dict, *args, **kwargs): - super().__init__(config_entry, *args, **kwargs) - - dummy_launcher_config = DummyLauncherConfig('Dummy_Launcher') - dummy_launcher_config.validate(self.config) - - self.data_path = get_path(self.config['data_path']) - - self._loader = Loader.provide(self.config['loader'], self.data_path) - - print_info("{} predictions objects loaded from {}".format(len(self._loader), self.data_path)) - - def predict(self, identifiers, *args, **kwargs): - return [self._loader[identifier] for identifier in identifiers] - - def release(self): - pass - - @property - def batch(self): - return 1 - - @property - def inputs(self): - return None - - def get_all_inputs(self): - return self.inputs - - @property - def output_blob(self): - return self.data_path diff --git a/tools/accuracy_checker/accuracy_checker/launcher/input_feeder.py b/tools/accuracy_checker/accuracy_checker/launcher/input_feeder.py deleted file mode 100644 index 5a5d8fc8df4442..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/launcher/input_feeder.py +++ /dev/null @@ -1,151 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import re -import numpy as np - -from ..config import ConfigError -from ..utils import extract_image_representations - - -class InputFeeder: - def __init__(self, inputs_config, network_inputs, prepare_input_data=None): - def fit_to_input(data, input_layer): - if len(np.shape(data)) == 4: - return np.transpose(data, [0, 3, 1, 2]) - return np.array(data) - - self.input_transform_func = prepare_input_data or fit_to_input - self.network_inputs = network_inputs - self.configure(inputs_config) - - def configure(self, inputs_config): - self.const_inputs, self.non_constant_inputs, self.inputs_mapping = self._parse_inputs_config(inputs_config) - if not self.non_constant_inputs: - raise ConfigError('Network should contain at least one layer for setting variable data.') - - def fill_non_constant_inputs(self, data_representation_batch): - filled_inputs = {} - for input_layer in self.non_constant_inputs: - input_regex = None - input_batch = [] - if self.inputs_mapping: - input_regex = self.inputs_mapping[input_layer] - for data_representation in data_representation_batch: - input_data = None - identifiers = data_representation.identifier - data = data_representation.data - if not isinstance(identifiers, list) and not input_regex: - input_data = data - input_batch.append(input_data) - continue - - if not input_regex: - raise ConfigError('Impossible to choose correct data for layer {}.' - 'Please provide regular expression for matching in config.'.format(input_layer)) - data = [data] if np.isscalar(identifiers) else data - identifiers = [identifiers] if np.isscalar(identifiers) else identifiers - for identifier, data_value in zip(identifiers, data): - if input_regex.match(identifier): - input_data = data_value - break - if input_data is None: - raise ConfigError('Suitable data for filling layer {} not found'.format(input_layer)) - input_batch.append(input_data) - - filled_inputs[input_layer] = input_batch - - return self._transform_batch(filled_inputs, extract_image_representations(data_representation_batch)[1]) - - def fill_inputs(self, data_representation_batch): - inputs = self.fill_non_constant_inputs(data_representation_batch) - for infer_inputs in inputs: - infer_inputs.update(self.const_inputs) - return inputs - - def __call__(self, context, *args, **kwargs): - data_batch = context.data_batch - _, meta = extract_image_representations(data_batch) - context.input_blobs = self.fill_inputs(data_batch) - context.batch_meta = meta - - def _parse_inputs_config(self, inputs_entry): - constant_inputs = {} - non_constant_inputs_mapping = {} - non_constant_inputs = [] - for input_ in inputs_entry: - name = input_['name'] - if not name in self.network_inputs: - raise ConfigError('network does not contain input "{}"'.format(name)) - value = input_['value'] - - if input_['type'] == 'CONST_INPUT': - if isinstance(value, list): - value = np.array(value) - constant_inputs[name] = value - else: - value = re.compile(value) - non_constant_inputs_mapping[name] = value - - non_constant_inputs = list(non_constant_inputs_mapping.keys()) - not_config_inputs = list(filter( - lambda input_layer: not input_layer in non_constant_inputs + list(constant_inputs.keys()), - self.network_inputs.keys() - )) - if non_constant_inputs and not_config_inputs: - raise ConfigError('input value for {} are not presented in config.'.format(','.join(not_config_inputs))) - non_constant_inputs += not_config_inputs - - return constant_inputs, non_constant_inputs, non_constant_inputs_mapping or None - - def _transform_batch(self, batch_data, meta): - def calculate_num_splits(layers_data, batch_size): - max_split_num = 1 - for _, data in layers_data.items(): - total_tiles_num = 0 - for tiles in data: - total_tiles_num += len(tiles) - - offset = 0 if total_tiles_num % batch_size == 0 else 1 - splits_for_layer = (total_tiles_num // batch_size) + offset - if max_split_num < splits_for_layer: - max_split_num = splits_for_layer - - return max_split_num - - def separate_data(data, num_splits): - grouped_data = [[] for _ in range(num_splits)] - for data_part in data: - for split_id, data_split in enumerate(data_part): - grouped_data[split_id % num_splits].append(data_split) - return grouped_data - - batch_size = len(meta) - if meta[-1].get('multi_infer', False): - num_splits = calculate_num_splits(batch_data, batch_size) - infers_data = [{} for _ in range(num_splits)] - for layer_name, layer_data in batch_data.items(): - batch_for_all_infers = separate_data(layer_data, num_splits) - for infer_id, on_infer_batch in enumerate(batch_for_all_infers): - infers_data[infer_id][layer_name] = self.input_transform_func( - on_infer_batch, self.network_inputs[layer_name] - ) - return infers_data - - for layer_name, layer_data in batch_data.items(): - batch_data[layer_name] = self.input_transform_func(layer_data, self.network_inputs[layer_name]) - - return [batch_data] diff --git a/tools/accuracy_checker/accuracy_checker/launcher/launcher.py b/tools/accuracy_checker/accuracy_checker/launcher/launcher.py deleted file mode 100644 index 2ed64db18c573f..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/launcher/launcher.py +++ /dev/null @@ -1,164 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -import numpy as np -from ..config import BaseField -from ..adapters import AdapterField -from ..config import ConfigValidator, StringField, ListField -from ..dependency import ClassProvider - - -class Launcher(ClassProvider): - """ - Interface for inferring model. - """ - - __provider_type__ = 'launcher' - - def __init__(self, config_entry, *args, **kwargs): - self.config = config_entry - - def predict(self, inputs, metadata, *args, **kwargs): - """ - Args: - inputs: dictionary where keys are input layers names and values are data for them. - metadata: metadata of input representations - Returns: - raw data from network. - """ - - raise NotImplementedError - - def __call__(self, context, *args, **kwargs): - context.prediction_batch = self.predict(context.input_blobs, context.batch_meta) - - - def get_all_inputs(self): - raise NotImplementedError - - def release(self): - raise NotImplementedError - - @property - def batch(self): - raise NotImplementedError - - @property - def output_blob(self): - raise NotImplementedError - - @property - def inputs(self): - raise NotImplementedError - - def _provide_inputs_info_to_meta(self, meta): - meta['input_shape'] = self.inputs - - return meta - - @staticmethod - def fit_to_input(data, input_layer): - if len(np.shape(data)) == 4: - return np.transpose(data, [0, 3, 1, 2]) - return np.array(data) - -INPUTS_TYPES = ('CONST_INPUT', 'INPUT') - -class InputValidator(ConfigValidator): - name = StringField() - type = StringField(choices=INPUTS_TYPES) - value = BaseField() - - -class ListInputsField(ListField): - def __init__(self, **kwargs): - super().__init__(allow_empty=False, value_type=InputValidator('Inputs'), **kwargs) - - def validate(self, entry, field_uri=None): - super().validate(entry, field_uri) - names_set = set() - for input_layer in entry: - input_name = input_layer['name'] - if input_name not in names_set: - names_set.add(input_name) - else: - self.raise_error(entry, field_uri, '{} repeated name'.format(input_name)) - - -class LauncherConfig(ConfigValidator): - """ - Specifies common part of configuration structure for launchers. - """ - - framework = StringField(choices=Launcher.providers) - tags = ListField(allow_empty=False, optional=True) - inputs = ListInputsField(optional=True) - adapter = AdapterField(optional=True) - - def validate(self, entry, field_uri=None): - super().validate(entry, field_uri) - inputs = entry.get('inputs') - if inputs: - inputs_by_type = {input_type: [] for input_type in INPUTS_TYPES} - for input_layer in inputs: - input_type = input_layer['type'] - inputs_by_type[input_type].append(input_layer['name']) - - additional_attributes = { - '_list_{}s'.format(input_type.lower()): inputs for input_type, inputs in inputs_by_type.items() - } - for additional_attribute, values in additional_attributes.items(): - self.fields[additional_attribute] = values - - -def unsupported_launcher(name, error_message=None): - class UnsupportedLauncher(Launcher): - __provider__ = name - - def __init__(self, config_entry, *args, **kwargs): - super().__init__(config_entry, *args, **kwargs) - - msg = "{launcher} launcher is disabled. Please install {launcher} to enable it.".format(launcher=name) - raise ValueError(error_message or msg) - - def predict(self, identifiers, data, *args, **kwargs): - raise NotImplementedError - - def release(self): - raise NotImplementedError - - @property - def batch(self): - raise NotImplementedError - - return UnsupportedLauncher - - -def create_launcher(launcher_config): - """ - Args: - launcher_config: launcher configuration file entry. - Returns: - framework-specific launcher object. - """ - - launcher_config_validator = LauncherConfig( - 'Launcher_validator', - on_extra_argument=ConfigValidator.IGNORE_ON_EXTRA_ARGUMENT - ) - launcher_config_validator.validate(launcher_config) - config_framework = launcher_config['framework'] - - return Launcher.provide(config_framework, launcher_config) diff --git a/tools/accuracy_checker/accuracy_checker/launcher/loaders/__init__.py b/tools/accuracy_checker/accuracy_checker/launcher/loaders/__init__.py deleted file mode 100644 index 98217dd2b3d299..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/launcher/loaders/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from .loader import Loader - -from .pickle_loader import PickleLoader -from .xml_loader import XMLLoader - -__all__ = [ - 'Loader', - 'PickleLoader', - 'XMLLoader', -] diff --git a/tools/accuracy_checker/accuracy_checker/launcher/loaders/loader.py b/tools/accuracy_checker/accuracy_checker/launcher/loaders/loader.py deleted file mode 100644 index 7c07394af8adad..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/launcher/loaders/loader.py +++ /dev/null @@ -1,54 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from pathlib import Path - -from ...dependency import ClassProvider - - -class Loader(ClassProvider): - """ - Interface that describes loading output from another tool. - """ - - __provider_type__ = 'loader' - - def __init__(self, data_path: Path): - self._data_path = data_path - - def __len__(self): - raise NotImplementedError - - def __getitem__(self, item): - raise NotImplementedError - - -class DictLoaderMixin: - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.data = self.load() - - def __len__(self): - return len(self.data) - - def __getitem__(self, item): - if item not in self.data: - raise IndexError('There is no prediction object for "{}" input data'.format(item)) - - return self.data[item] - - def load(self): - raise NotImplementedError diff --git a/tools/accuracy_checker/accuracy_checker/launcher/loaders/pickle_loader.py b/tools/accuracy_checker/accuracy_checker/launcher/loaders/pickle_loader.py deleted file mode 100644 index ba3578b688d20e..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/launcher/loaders/pickle_loader.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from ...utils import read_pickle -from .loader import Loader, DictLoaderMixin - - -class PickleLoader(DictLoaderMixin, Loader): - """ - Class for loading output from another tool in .pickle format. - """ - - __provider__ = 'pickle' - - def load(self): - data = read_pickle(self._data_path) - - if isinstance(data, list) and all(hasattr(entry, 'identifier') for entry in data): - return dict(zip([representation.identifier for representation in data], data)) - - return data diff --git a/tools/accuracy_checker/accuracy_checker/launcher/loaders/xml_loader.py b/tools/accuracy_checker/accuracy_checker/launcher/loaders/xml_loader.py deleted file mode 100644 index 13c0de9c18c17c..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/launcher/loaders/xml_loader.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from ...utils import read_xml -from .loader import Loader, DictLoaderMixin - - -class XMLLoader(DictLoaderMixin, Loader): - """ - Class for loading output from another tool in .xml format. - """ - - __provider__ = 'xml' - - def load(self): - return read_xml(self._data_path) diff --git a/tools/accuracy_checker/accuracy_checker/launcher/model_conversion.py b/tools/accuracy_checker/accuracy_checker/launcher/model_conversion.py deleted file mode 100644 index fa223606987010..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/launcher/model_conversion.py +++ /dev/null @@ -1,201 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import sys -import subprocess -from pathlib import Path -from typing import Union -from collections import namedtuple -from ..utils import get_path, format_key - -FrameworkParameters = namedtuple('FrameworkParameters', ['name', 'meta']) - -def convert_model(topology_name, model=None, weights=None, meta=None, - framework=FrameworkParameters('caffe', False), mo_search_paths=None, mo_params=None, mo_flags=None, - tf_custom_op_config_dir=None, tf_object_detection_api_config_dir=None): - """ - Args: - topology_name: name for converted model files. - model: path to the topology file. - weights: path to the weights file. - meta: path to the meta file - framework: framework name for original model. - mo_search_paths: paths where ModelOptimizer may be found. If None only default paths is used. - mo_params: value parameters for ModelOptimizer execution. - mo_flags: flags parameters for ModelOptimizer execution. - tf_custom_op_config_dir: path to Tensor Flow custom operations directory. - tf_object_detection_api_config_dir: path to Tensor Flow directory with config for object detection API. - Returns: - paths to converted to IE IR model and weights. - """ - - mo_params = mo_params or {} - mo_flags = mo_flags or [] - - set_topology_name(mo_params, topology_name) - - model_optimizer_executable = find_mo(mo_search_paths) - if not model_optimizer_executable: - raise EnvironmentError( - 'Model optimizer not found. Please set MO_DIR environment variable to model optimizer folder ' - 'installation or refer to help for command line options for providing Model optimizer' - ) - - framework_specific_options = { - FrameworkParameters('caffe', False): {'input_model': weights, 'input_proto': model}, - FrameworkParameters('mxnet', False): {'input_model': weights}, - FrameworkParameters('tf', False): {'input_model': model}, - FrameworkParameters('tf', True): {'input_meta_graph': meta}, - FrameworkParameters('onnx', False): {'input_model': model}, - FrameworkParameters('kaldi', False): {'input_model': model} - } - - mo_params['framework'] = framework.name - mo_params.update(framework_specific_options.get(framework, {})) - - set_path_to_custom_operation_configs(mo_params, framework, tf_custom_op_config_dir, model_optimizer_executable) - set_path_to_object_detection_api_pipeline_config(mo_params, framework, tf_object_detection_api_config_dir) - args = prepare_args(str(model_optimizer_executable), flag_options=mo_flags, value_options=mo_params) - - code = exec_mo_binary(args) - - if code.returncode != 0: - raise RuntimeError("Model optimizer conversion failed: ModelOptimizer returned non-zero code") - - model_file, bin_file = find_dlsdk_ir( - get_path(mo_params.get('output_dir', Path.cwd()), is_directory=True), mo_params['model_name'] - ) - if not bin_file or not model_file: - raise RuntimeError("Model optimizer finished correctly, but converted model is not found.") - - return model_file, bin_file - - -def find_dlsdk_ir(search_path: Path, model_name): - """ - Args: - search_path: path with IE IR of model. - model_name: name of the model. - Returns: - paths to IE IR of model. - """ - - xml_file = search_path / '{}.xml'.format(model_name) - bin_file = search_path / '{}.bin'.format(model_name) - - return get_path(xml_file), get_path(bin_file) - - -def find_mo(search_paths=None) -> Union[Path, None]: - """ - Args: - search_paths: paths where ModelOptimizer may be found. If None only default paths is used. - Returns: - path to the ModelOptimizer or None if it wasn't found. - """ - - default_mo_path = ('intel', 'openvino', 'deployment_tools', 'model_optimizer') - default_paths = [Path.home().joinpath(*default_mo_path), Path('/opt').joinpath(*default_mo_path)] - - executable = 'mo.py' - for path in search_paths or default_paths: - path = Path(path) - if not path.is_dir(): - continue - - mo = path / executable - if not mo.is_file(): - continue - - return mo - - return None - - -def prepare_args(executable, flag_options=None, value_options=None): - """ - Args: - executable: path to the executable. - flag_options: positional arguments for executable. - value_options: keyword arguments for executable. - Returns: - list with command-line entries. - """ - - result = [sys.executable, executable] - - for flag_option in flag_options or []: - result.append(str(format_key(flag_option))) - - for key, value in (value_options or {}).items(): - result.append(str(format_key(key))) - result.append(str(value)) - - return result - - -def exec_mo_binary(args, timeout=None): - """ - Args: - args: command-line entries. - timeout: timeout for execution. - Returns: - result of execution. - """ - - return subprocess.run(args, check=False, timeout=timeout) - - -def set_path_to_custom_operation_configs(mo_params, framework, tf_custom_op_config_dir, mo_path): - if framework.name != 'tf': - return mo_params - - config_path = mo_params.get('tensorflow_use_custom_operations_config') - if not config_path: - return mo_params - - if tf_custom_op_config_dir: - tf_custom_op_config_dir = Path(tf_custom_op_config_dir) - else: - tf_custom_op_config_dir = Path('/').joinpath(*mo_path.parts[:-1]) / 'extensions' / 'front' / 'tf' - - config_path = Path(config_path) - if not config_path.is_absolute(): - config_path = tf_custom_op_config_dir / config_path - - mo_params['tensorflow_use_custom_operations_config'] = str(get_path(config_path)) - - return mo_params - - -def set_path_to_object_detection_api_pipeline_config(mo_params, framework, object_detection_api_config_dir=None): - object_detection_api_config = mo_params.get('tensorflow_object_detection_api_pipeline_config') - if framework.name != 'tf' or not object_detection_api_config: - return mo_params - model_path = mo_params.get('input_model') or mo_params.get('input_meta_graph') - - object_detection_api_config_dir = Path(object_detection_api_config_dir or get_path(model_path).parent) - config_path = object_detection_api_config_dir / object_detection_api_config - mo_params['tensorflow_object_detection_api_pipeline_config'] = str(get_path(config_path)) - - return mo_params - - -def set_topology_name(mo_params, topology_name): - if not mo_params.get('model_name'): - mo_params['model_name'] = topology_name - - return mo_params diff --git a/tools/accuracy_checker/accuracy_checker/logging.py b/tools/accuracy_checker/accuracy_checker/logging.py deleted file mode 100644 index 742097cf955c31..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/logging.py +++ /dev/null @@ -1,134 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import logging -import logging.config -import os -import sys -import warnings - -_DEFAULT_LOGGER_NAME = 'accuracy_checker' -_DEFAULT_LOG_FILE = 'accuracy_checker.log' - -PRINT_INFO = logging.INFO + 5 -logging.addLevelName(PRINT_INFO, "PRINT_INFO") - -_LOG_LEVEL_ENVIRON = "ACCURACY_CHECKER_LOG_LEVEL" -_LOGGING_LEVEL = logging.getLevelName(os.environ.get(_LOG_LEVEL_ENVIRON, PRINT_INFO)) - - -class LoggingFormatter(logging.Formatter): - def format(self, record: logging.LogRecord): - if record.levelno == PRINT_INFO: - return record.msg - return super().format(record) - - -class ConsoleHandler(logging.StreamHandler): - def __init__(self, default_stream=sys.stdout): - super().__init__(default_stream) - self.default_stream = default_stream - self.err_stream = sys.stderr - - def emit(self, record): - if record.levelno >= logging.WARNING: - self.stream = self.err_stream - else: - self.stream = self.default_stream - super().emit(record) - - -_LOGGING_CONFIGURATION = { - 'loggers': { - _DEFAULT_LOGGER_NAME: { - 'handlers': ['console'], - 'level': _LOGGING_LEVEL, - 'propagate': False - } - }, - 'version': 1, - 'disable_existing_loggers': False, - 'formatters': { - 'default': { - '()': LoggingFormatter, - 'format': '%(asctime)s %(name)s %(levelname)s: %(message)s', - 'datefmt': '%H:%M:%S' - }, - 'detailed': { - 'format': '%(asctime)s %(name)s %(levelname)s: %(message)s' - } - }, - 'handlers': { - 'console': { - 'level': 'DEBUG', - '()': ConsoleHandler, - 'formatter': 'default', - } - } -} - -logging.config.dictConfig(_LOGGING_CONFIGURATION) - -_default_logger = logging.getLogger(_DEFAULT_LOGGER_NAME) - - -def _warning_handler(message, category, filename, line_number, *args, **kwargs): - s = warnings.formatwarning(message, category, filename, line_number) - _default_logger.warning(s) - - -warnings.showwarning = _warning_handler - - -def get_logger(logger_name: str): - if logger_name.startswith(_DEFAULT_LOGGER_NAME): - return _default_logger.getChild(logger_name) - return logging.getLogger(logger_name) - - -def error(msg, *args, **kwargs): - _default_logger.error(msg, *args, **kwargs) - - -def warning(msg, *args, raise_warning=True, **kwargs): - if raise_warning: - warnings.warn(msg) - else: - _default_logger.warning(msg, *args, **kwargs) - - -def info(msg, *args, **kwargs): - _default_logger.info(msg, *args, **kwargs) - - -def debug(msg, *args, **kwargs): - _default_logger.debug(msg, *args, **kwargs) - - -def print_info(msg, *args, **kwargs): - _default_logger.log(PRINT_INFO, msg, *args, **kwargs) - - -def add_file_handler(file_name): - file_info_handler_config = { - 'level': 'PRINT_INFO', - 'class': 'logging.handlers.WatchedFileHandler', - 'formatter': 'default', - 'filename': file_name - } - _LOGGING_CONFIGURATION['handlers']['file_info'] = file_info_handler_config - _LOGGING_CONFIGURATION['loggers'][_DEFAULT_LOGGER_NAME]['handlers'].append('file_info') - logging.config.dictConfig(_LOGGING_CONFIGURATION) diff --git a/tools/accuracy_checker/accuracy_checker/main.py b/tools/accuracy_checker/accuracy_checker/main.py deleted file mode 100644 index c573c7da1413f1..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/main.py +++ /dev/null @@ -1,239 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from pathlib import Path -from argparse import ArgumentParser -from functools import partial - -from .config import ConfigReader -from .logging import print_info, add_file_handler -from .evaluators import ModelEvaluator, PipeLineEvaluator, get_processing_info -from .progress_reporters import ProgressReporter -from .utils import get_path - - -def build_arguments_parser(): - parser = ArgumentParser(description='NN Validation on Caffe and IE', allow_abbrev=False) - parser.add_argument( - '-d', '--definitions', - help='path to the yml file with definitions', - type=get_path, - required=False - ) - parser.add_argument( - '-c', '--config', - help='path to the yml file with local configuration', - type=get_path, - required=True - ) - parser.add_argument( - '-m', '--models', - help='prefix path to the models and weights', - type=partial(get_path, is_directory=True), - default=Path.cwd(), - required=False - ) - parser.add_argument( - '-s', '--source', - help='prefix path to the data source', - type=partial(get_path, is_directory=True), - default=Path.cwd(), - required=False - ) - parser.add_argument( - '-a', '--annotations', - help='prefix path to the converted annotations and datasets meta data', - type=partial(get_path, is_directory=True), - default=Path.cwd(), - required=False - ) - parser.add_argument( - '-e', '--extensions', - help='prefix path to extensions folder', - type=partial(get_path, is_directory=True), - default=Path.cwd(), - required=False - ) - parser.add_argument( - '--cpu_extensions_mode', - help='specified preferable set of processor instruction for automatic searching cpu extension lib', - required=False, - choices=['avx512', 'avx2', 'sse4'] - ) - parser.add_argument( - '-b', '--bitstreams', - help='prefix path to bitstreams folder', - type=partial(get_path, is_directory=True), - default=Path.cwd(), - required=False - ) - parser.add_argument( - '--stored_predictions', - help='path to file with saved predictions. Used for development', - # since at the first time file does not exist and then created we can not always check existence - required=False - ) - parser.add_argument( - '-C', '--converted_models', - help='directory to store Model Optimizer converted models. Used for DLSDK launcher only', - type=partial(get_path, is_directory=True), - default=Path.cwd(), - required=False - ) - parser.add_argument( - '-M', '--model_optimizer', - help='path to model optimizer directory', - type=partial(get_path, is_directory=True), - # there is no default value because if user did not specify it we use specific locations - # defined in model_conversion.py - required=False - ) - parser.add_argument( - '--tf_custom_op_config_dir', - help='path to directory with tensorflow custom operation configuration files for model optimizer', - type=partial(get_path, is_directory=True), - # there is no default value because if user did not specify it we use specific location - # defined in model_conversion.py - required=False - ) - parser.add_argument( - '--tf_obj_detection_api_pipeline_config_path', - help='path to directory with tensorflow object detection api pipeline configuration files for model optimizer', - type=partial(get_path, is_directory=True), - # there is no default value because if user did not specify it we use specific location - # defined in model_conversion.py - required=False - ) - parser.add_argument( - '--progress', - help='progress reporter', - required=False, - default='bar' - ) - parser.add_argument( - '-tf', '--target_framework', - help='framework for infer', - required=False - ) - parser.add_argument( - '-td', '--target_devices', - help='Space separated list of devices for infer', - required=False, - nargs='+' - ) - - parser.add_argument( - '-tt', '--target_tags', - help='Space separated list of launcher tags for infer', - required=False, - nargs='+' - ) - - parser.add_argument( - '-l', '--log_file', - help='file for additional logging results', - required=False - ) - - parser.add_argument( - '--ignore_result_formatting', - help='allow to get raw metrics results without data formatting', - required=False, - default=False - ) - - parser.add_argument( - '-am', '--affinity_map', - help='prefix path to the affinity maps', - type=partial(get_path, is_directory=True), - default=Path.cwd(), - required=False - ) - - parser.add_argument( - '--aocl', - help='path to aocl executable for FPGA bitstream programming', - type=get_path, - required=False - ) - parser.add_argument( - '--vpu_log_level', - help='log level for VPU devices', - required=False, - choices=['LOG_NONE', 'LOG_WARNING', 'LOG_INFO', 'LOG_DEBUG'], - default='LOG_WARNING' - ) - - return parser - - -def main(): - args = build_arguments_parser().parse_args() - progress_reporter = ProgressReporter.provide(( - args.progress if ':' not in args.progress - else args.progress.split(':')[0] - )) - if args.log_file: - add_file_handler(args.log_file) - - config, mode = ConfigReader.merge(args) - if mode == 'models': - model_evaluation_mode(config, progress_reporter, args) - else: - pipeline_evaluation_mode(config, progress_reporter, args) - - -def model_evaluation_mode(config, progress_reporter, args): - for model in config['models']: - for launcher_config in model['launchers']: - for dataset_config in model['datasets']: - print_processing_info( - model['name'], - launcher_config['framework'], - launcher_config['device'], - launcher_config.get('tags'), - dataset_config['name'] - ) - model_evaluator = ModelEvaluator.from_configs(launcher_config, dataset_config) - progress_reporter.reset(model_evaluator.dataset.size) - model_evaluator.process_dataset(args.stored_predictions, progress_reporter=progress_reporter) - model_evaluator.compute_metrics(ignore_results_formatting=args.ignore_result_formatting) - - model_evaluator.release() - - -def pipeline_evaluation_mode(config, progress_reporter, args): - for pipeline_config in config['pipelines']: - print_processing_info(*get_processing_info(pipeline_config)) - evaluator = PipeLineEvaluator.from_configs(pipeline_config['stages']) - evaluator.process_dataset(args.stored_predictions, progress_reporter=progress_reporter) - evaluator.compute_metrics(ignore_results_formatting=args.ignore_result_formatting) - - evaluator.release() - - -def print_processing_info(model, launcher, device, tags, dataset): - print_info('Processing info:') - print_info('model: {}'.format(model)) - print_info('launcher: {}'.format(launcher)) - if tags: - print_info('launcher tags: {}'.format(' '.join(tags))) - print_info('device: {}'.format(device)) - print_info('dataset: {}'.format(dataset)) - - -if __name__ == '__main__': - main() diff --git a/tools/accuracy_checker/accuracy_checker/metrics/README.md b/tools/accuracy_checker/accuracy_checker/metrics/README.md deleted file mode 100644 index c1381b253ad619..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/metrics/README.md +++ /dev/null @@ -1,127 +0,0 @@ -# Metrics - -For correct work metrics require specific representation format. -(e. g. map expects detection annotation and detection prediction for evaluation). - -In case when you use complicated representation located in representation container, you need to add options `annotation_source` and `prediction_source` in configuration file to -select specific representation, another way metric calculation possible only if container has only one suitable representation and will be resolved automatically. -`annotation_source` and `prediction_source` should contain only one annotation identifier and output layer name respectively. -You may optionally provide `reference` field for metric, if you want calculated metric tested against specific value (i.e. reported in canonical paper) and acceptable `threshold` for metric deviation from reference value. - -Every metric has parameters available for configuration. - -Accuracy Checker supports following set of metrics: - -* `accuracy` - classification accuracy metric, defined as the number of correct predictions divided by the total number of predictions. -Supported representation: `ClassificationAnnotation`, `ClassificationPrediction` - * `top_k` - the number of classes with the highest probability, which will be used to decide if prediction is correct. -* `accuracy_per_class` - classification accuracy metric which represents results for each class. Supported representation: `ClassificationAnnotation`, `ClassificationPrediction`. - * `top_k` - the number of classes with the highest probability, which will be used to decide if prediction is correct. - * `label_map` - the field in annotation metadata, which contains dataset label map. -* `character_recognition_accuracy` - accuracy metric for character recognition task. Supported representation: `CharacterRecognitionAnnotation`, `CharacterRecognitionPrediction`. -* `map` - mean average precision. Supported representations: `DetectionAnnotation`, `DetectionPrediction`. - * `overlap_threshold` - minimal value for intersection over union that allows to make decision that prediction bounding box is true positive. - * `overlap_method` - method for calculation bbox overlap. You can choose between intersection over union (`iou`), defined as area of intersection divided by union of annotation and prediction boxes areas, and intersection over area (`ioa`), defined as area of intersection divided by ara of prediction box. - * `include_boundaries` - allows include boundaries in overlap calculation process. If it is True then width and height of box is calculated by max - min + 1. - * `ignore_difficult` - allows to ignore difficult annotation boxes in metric calculation. In this case, difficult boxes are filtered annotations from postprocessing stage. - * `distinct_conf` - select only values for distinct confidences. - * `allow_multiple_matches_per_ignored` - allows multiple matches per ignored. - * `label_map` - the field in annotation metadata, which contains dataset label map. - * `integral` - integral type for average precision calculation. Pascal VOC `11point` and `max` approaches are available. -* `miss_rate` - miss rate metric of detection models. Supported representations: `DetectionAnnotation`, `DetectionPrediction`. - * `overlap_threshold` - minimal value for intersection over union that allows to make decision that prediction bounding box is true positive. - * `overlap_method` - method for calculation bbox overlap. You can choose between intersection over union (`iou`), defined as area of intersection divided by union of annotation and prediction boxes areas, and intersection over area (`ioa`), defined as area of intersection divided by ara of prediction box. - * `include_boundaries` - allows include boundaries in overlap calculation process. If it is True then width and height of box is calculated by max - min + 1. - * `ignore_difficult` - allows to ignore difficult annotation boxes in metric calculation. In this case, difficult boxes are filtered annotations from postprocessing stage. - * `distinct_conf` - select only values for distinct confidences. - * `allow_multiple_matches_per_ignored` - allows multiple matches per ignored. - * `label_map` - the field in annotation metadata, which contains dataset label map. - * `fppi_level` - false positive per image level. -* `recall` - recall metric of detection models. Supported representations: `DetectionAnnotation`, `DetectionPrediction`. - * `overlap_threshold` - minimal value for intersection over union that allows to make decision that prediction bounding box is true positive. - * `overlap_method` - method for calculation bbox overlap. You can choose between intersection over union (`iou`), defined as area of intersection divided by union of annotation and prediction boxes areas, and intersection over area (`ioa`), defined as area of intersection divided by ara of prediction box. - * `include_boundaries` - allows include boundaries in overlap calculation process. If it is True then width and height of box is calculated by max - min + 1. - * `ignore_difficult` - allows to ignore difficult annotation boxes in metric calculation. In this case, difficult boxes are filtered annotations from postprocessing stage. - * `distinct_conf` - select only values for distinct confidences. - * `allow_multiple_matches_per_ignored` - allows multiple matches per ignored. - * `label_map` - the field in annotation metadata, which contains dataset label map. -* `detection_accuracy` - accuracy for detection models. Supported representations: `DetectionAnnotation`, `DetectionPrediction`. - * `overlap_threshold` - minimal value for intersection over union that allows to make decision that prediction bounding box is true positive. - * `overlap_method` - method for calculation bbox overlap. You can choose between intersection over union (`iou`), defined as area of intersection divided by union of annotation and prediction boxes areas, and intersection over area (`ioa`), defined as area of intersection divided by ara of prediction box. - * `include_boundaries` - allows include boundaries in overlap calculation process. If it is True then width and height of box is calculated by max - min + 1. - * `label_map` - the field in annotation metadata, which contains dataset label map. - * `use_normalization` - allows to normalize confusion_matrix for metric calculation. -* `segmentation_accuracy` - pixel accuracy for semantic segmentation models. Supported representations: `SegmentationAnnotation`, `SegmentationPrediction`. - * `use_argmax` - allows to use argmax for prediction mask. -* `mean_iou` - mean intersection over union for semantic segmentation models. Supported representations: `SegmentationAnnotation`, `SegmentationPrediction`. - * `use_argmax` - allows to use argmax for prediction mask. -* `mean_accuracy` - mean accuracy for semantic segmentation models. Supported representations: `SegmentationAnnotation`, `SegmentationPrediction`. - * `use_argmax` - allows to use argmax for prediction mask. -* `frequency_weighted_accuracy` - frequency weighted accuracy for semantic segmentation models. Supported representations: `SegmentationAnnotation`, `SegmentationPrediction`. - * `use_argmax` - allows to use argmax for prediction mask. -More detailed information about calculation segmentation metrics you can find [here][segmentation_article]. -* `cmc` - Cumulative Matching Characteristics (CMC) score. Supported representations: `ReIdentificationAnnotation`, `ReIdentificationPrediction`. - * `top_k` - number of k highest ranked samples to consider when matching. - * `separate_camera_set` - should identities from the same camera view be filtered out. - * `single_gallery_shot` - each identity has only one instance in the gallery. - * `number_single_shot_repeats` - number of repeats for single_gallery_shot setting (required for CUHK). - * `first_match_break` - break on first matched gallery sample. -* `reid_map` - Mean Average Precision score for object reidentification. Supported representations: `ReIdentificationAnnotation`, `ReIdentificationPrediction`. - * `uninterpolated_auc` - should area under precision recall curve be computed using trapezoidal rule or directly. -* `pairwise_accuracy` - pairwise accuracy for object reidentification. Supported representations: `ReIdentificationClassificationAnnotation`, `ReIdentificationPrediction`. - * `min_score` - min score for determining that objects are different. You can provide value or use `train_median` value which will be calculated if annotations has training subset. -* `pairwise_accuracy_subsets` - object reidentification pairwise accuracy with division dataset on test and train subsets for calculation mean score. Supported representations: `ReIdentificationClassificationAnnotation`, `ReIdentificationPrediction`. - * `subset_number` - number of subsets for separating. -* `mae` - [Mean Absolute Error][mae]. Supported representations: `RegressionAnnotation`, `RegressionPrediction`. -* `mae_on_intervals` - Mean Absolute Error estimated magnitude for specific value range. Supported representations: `RegressionAnnotation`, `RegressionPrediction`. - * `intervals` - comma-separated list of interval boundaries. - * `ignore_values_not_in_interval` - allows create additional intervals for values less than minimal value in interval and greater than maximal. - * `start` , `step`, `end` - way to generate range of intervals from `start` to `end` with length `step`. -* `mse` - [Mean Squared Error][mse]. Supported representations: `RegressionAnnotation`, `RegressionPrediction`. -* `mse_on_intervals` - Mean Squared Error estimated magnitude for specific value range. Supported representations: `RegressionAnnotation`, `RegressionPrediction`. - * `intervals` - comma-separated list of interval boundaries. - * `ignore_values_not_in_interval` - allows create additional intervals for values less than minimal value in interval and greater than maximal. - * `start`, `step`, `end` - generate range of intervals from `start` to `end` with length `step`. -* `rmse` - [Root Mean Squared Error][rmse]. Supported representations: `RegressionAnnotation`, `RegressionPrediction`. -* `rmse_on_intervals` - Root Mean Squared Error estimated magnitude for specific value range. Supported representations: `RegressionAnnotation`, `RegressionPrediction`. - * `intervals` - comma-separated list of interval boundaries. - * `ignore_values_not_in_interval` - allows create additional intervals for values less than minimal value in interval and greater than maximal. - * `start`, `step`, `end` - generate range of intervals from `start` to `end` with length `step`. -* `per_point_normed_error` - Normed Error for measurement the quality of landmarks' positions. Estimated results for each point independently. Supported representations: `FacialLandmarksAnnotation`, `FacialLandmarksPrediction`. -* `normed_error` - Normed Error for measurement the quality of landmarks' positions. Supported representations: `FacialLandmarksAnnotation`, `FacialLandmarksPrediction`. - * `calculate_std` - allows calculation of standard deviation (default value: `False`) - * `percentile` - calculate error rate for given percentile. -* `per_point_regression` - Root Mean Squared Error for 2D points estimated results for each point independently. Supported representations: `PointRegressionAnnotation`, `PointRegressionPrediction`. - * `scaling_distance` - comma-separated list of 2 point indexes, distance between which will be used for scaling regression distances. -* `average point error` - Root Mean Squared Error for 2D points estimated average results for all points. Supported representations: `PointRegressionAnnotation`, `PointRegressionPrediction`. - * `scaling_distance` - comma-separated list of 2 point indexes, distance between which will be used for scaling regression distances. -* `multi_accuracy` - accuracy for multilabel recognition task. Supported representations: `MultiLabelRecognitionAnnotation`, `MultiLabelRecognitionPrediction`. - * `label_map` - the field in annotation metadata, which contains dataset label map. - * `calculate_average` - allows calculation of average accuracy (default value: `True`). -* `multi_precision` - precision metric for multilabel recognition. Supported representations: `MultiLabelRecognitionAnnotation`, `MultiLabelRecognitionPrediction`. - * `label_map` - the field in annotation metadata, which contains dataset label map. - * `calculate_average` - allows calculation of average precision (default value: `True`). -* `multi_recall` - recall metric for multilabel recognition. Supported representations: `MultiLabelRecognitionAnnotation`, `MultiLabelRecognitionPrediction`. - * `label_map` - the field in annotation metadata, which contains dataset label map. - * `calculate_average` - allows calculation of average recall (default value: `True`). -* `f1_score` - [F score][f_score] metric for multilabel recognition. Supported representations: `MultiLabelRecognitionAnnotation`, `MultiLabelRecognitionPrediction`. - * `label_map` - the field in annotation metadata, which contains dataset label map. - * `calculate_average` - allows calculation of average f-score (default value: `True`). -* `text_detection` - Harmonic mean of precision and recall for text detection task. Supported representations: `TextDetectionAnnotation`, `TextDetectionPrediction`. - * `iou_constrain` - minimal value for intersection over union that allows to make decision that prediction polygon is true positive. - * `ignore_difficult` - allows to ignore difficult ground truth text polygons in metric calculation. - * `area_precision_constrain` - minimal value for intersection over union that allows to make decision that prediction polygon matched with ignored annotation. -* `coco_precision` - MS COCO Average Precision metric for keypoints recognition and object detection tasks. Supported representations: `PoseEstimationAnnotation`, `PoseEstimationPrediction`, `DetectionAnnotation`, `DetectionPrediction`. - * `max_detections` - max number of predicted results per image. If you have more predictions,the results with minimal confidence will be ignored. - * `threshold` - intersection over union threshold. You can specify one value or comma separated range of values. This parameter supports precomputed values for standard COCO thresholds (`.5`, `.75`, `.5:.05:.95`). -* `coco_recall` - MS COCO Average Recall metric for keypoints recognition and object detection tasks. Supported representations: `PoseEstimationAnnotation`, `PoseEstimationPrediction`, `DetectionAnnotation`, `DetectionPrediction`. - * `max_detections` - max number of predicted results per image. If you have more predictions,the results with minimal confidence will be ignored. - * `threshold` - intersection over union threshold. You can specify one value or comma separated range of values. This parameter supports precomputed values for standard COCO thresholds (`.5`, `.75`, `.5:.05:.95`). -* `angle_error` - Mean angle error and Standard deviation of angle error for gaze estimation. Supported representations: `GazeVectorAnnotation`, `GazeVectorPrediction`. - -[segmentation_article]: https://arxiv.org/pdf/1411.4038v2.pdf -[mae]: https://en.wikipedia.org/wiki/Mean_absolute_error -[mse]: https://en.wikipedia.org/wiki/Mean_squared_error -[rmse]: https://en.wikipedia.org/wiki/Root-mean-square_deviation -[f_score]: https://en.wikipedia.org/wiki/F1_score -[psnr]: https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio diff --git a/tools/accuracy_checker/accuracy_checker/metrics/__init__.py b/tools/accuracy_checker/accuracy_checker/metrics/__init__.py deleted file mode 100644 index f5bc37963626f6..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/metrics/__init__.py +++ /dev/null @@ -1,93 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from .metric_executor import MetricsExecutor - -from .classification import ClassificationAccuracy, ClassificationAccuracyClasses, ClipAccuracy -from .detection import (DetectionMAP, MissRate, Recall, DetectionAccuracyMetric) -from .reid import CMCScore, ReidMAP, PairwiseAccuracy, PairwiseAccuracySubsets -from .semantic_segmentation import SegmentationAccuracy, SegmentationIOU, SegmentationMeanAccuracy, SegmentationFWAcc -from .character_recognition import CharacterRecognitionAccuracy -from .regression import ( - MeanAbsoluteErrorOnInterval, - MeanSquaredErrorOnInterval, - - MeanAbsoluteError, - MeanSquaredError, - - RootMeanSquaredErrorOnInterval, - RootMeanSquaredError, - - FacialLandmarksPerPointNormedError, - FacialLandmarksNormedError, - - PeakSignalToNoiseRatio, - - AngleError -) -from .multilabel_recognition import MultiLabelRecall, MultiLabelPrecision, MultiLabelAccuracy, F1Score -from .text_detection import TextDetectionMetric -from .coco_metrics import MSCOCOAveragePresicion -from .hit_ratio import HitRatioMetric, NDSGMetric - - -__all__ = [ - 'MetricsExecutor', - - 'ClassificationAccuracy', - 'ClassificationAccuracyClasses', - 'ClipAccuracy', - - 'DetectionMAP', - 'MissRate', - 'Recall', - 'DetectionAccuracyMetric', - - 'CMCScore', - 'ReidMAP', - 'PairwiseAccuracy', - 'PairwiseAccuracySubsets', - - 'SegmentationAccuracy', - 'SegmentationIOU', - 'SegmentationMeanAccuracy', - 'SegmentationFWAcc', - - 'CharacterRecognitionAccuracy', - - 'MeanAbsoluteError', - 'MeanSquaredError', - 'MeanAbsoluteErrorOnInterval', - 'MeanSquaredErrorOnInterval', - 'RootMeanSquaredError', - 'RootMeanSquaredErrorOnInterval', - 'FacialLandmarksPerPointNormedError', - 'FacialLandmarksNormedError', - 'PeakSignalToNoiseRatio', - 'AngleError', - - 'MultiLabelAccuracy', - 'MultiLabelRecall', - 'MultiLabelPrecision', - 'F1Score', - - 'TextDetectionMetric', - - 'MSCOCOAveragePresicion', - - 'HitRatioMetric', - 'NDSGMetric' -] diff --git a/tools/accuracy_checker/accuracy_checker/metrics/average_meter.py b/tools/accuracy_checker/accuracy_checker/metrics/average_meter.py deleted file mode 100644 index 3c2e37a935c517..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/metrics/average_meter.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - - -class AverageMeter: - def __init__(self, loss=None, counter=None): - self.loss = loss or (lambda x, y: int(x == y)) - self.counter = counter or (lambda x: 1) - self.accumulator = None - self.total_count = None - - def update(self, annotation_val, prediction_val): - loss = self.loss(annotation_val, prediction_val) - increment = self.counter(annotation_val) - - if self.accumulator is None and self.total_count is None: - # wrap in array for using numpy.divide with where attribute - # and support cases when loss function returns list-like object - self.accumulator = np.array(loss, dtype=float) - self.total_count = np.array(increment, dtype=float) - else: - self.accumulator += loss - self.total_count += increment - - def evaluate(self): - if self.total_count is None: - return 0.0 - - return np.divide( - self.accumulator, self.total_count, out=np.zeros_like(self.accumulator), where=self.total_count != 0 - ) diff --git a/tools/accuracy_checker/accuracy_checker/metrics/character_recognition.py b/tools/accuracy_checker/accuracy_checker/metrics/character_recognition.py deleted file mode 100644 index 1b7530abfca45c..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/metrics/character_recognition.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from ..representation import CharacterRecognitionAnnotation, CharacterRecognitionPrediction -from .metric import PerImageEvaluationMetric -from .average_meter import AverageMeter - - -class CharacterRecognitionAccuracy(PerImageEvaluationMetric): - __provider__ = 'character_recognition_accuracy' - - annotation_types = (CharacterRecognitionAnnotation, ) - prediction_types = (CharacterRecognitionPrediction, ) - - def configure(self): - self.accuracy = AverageMeter(lambda annotation, prediction: int(annotation == prediction)) - - def update(self, annotation, prediction): - self.accuracy.update(annotation.label, prediction.label) - - def evaluate(self, annotations, predictions): - return self.accuracy.evaluate() diff --git a/tools/accuracy_checker/accuracy_checker/metrics/classification.py b/tools/accuracy_checker/accuracy_checker/metrics/classification.py deleted file mode 100644 index 1b8e953683c2d8..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/metrics/classification.py +++ /dev/null @@ -1,136 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from ..representation import ClassificationAnnotation, ClassificationPrediction -from ..config import NumberField, StringField -from .metric import BaseMetricConfig, PerImageEvaluationMetric -from .average_meter import AverageMeter - - -class AccuracyConfig(BaseMetricConfig): - top_k = NumberField(floats=False, min_value=1, optional=True) - - -class PerClassAccuracyConfig(AccuracyConfig): - abel_map = StringField(optional=True) - - -class ClassificationAccuracy(PerImageEvaluationMetric): - """ - Class for evaluating accuracy metric of classification models. - """ - - __provider__ = 'accuracy' - - annotation_types = (ClassificationAnnotation, ) - prediction_types = (ClassificationPrediction, ) - _config_validator_type = AccuracyConfig - - def configure(self): - self.top_k = self.config.get('top_k', 1) - - def loss(annotation_label, prediction_top_k_labels): - return int(annotation_label in prediction_top_k_labels) - - self.accuracy = AverageMeter(loss) - - def update(self, annotation, prediction): - self.accuracy.update(annotation.label, prediction.top_k(self.top_k)) - - def evaluate(self, annotations, predictions): - return self.accuracy.evaluate() - - -class ClassificationAccuracyClasses(PerImageEvaluationMetric): - """ - Class for evaluating accuracy for each class of classification models. - """ - - __provider__ = 'accuracy_per_class' - - annotation_types = (ClassificationAnnotation, ) - prediction_types = (ClassificationPrediction, ) - - _config_validator_type = PerClassAccuracyConfig - - def configure(self): - self.top_k = self.config.get('top_k', 1) - label_map = self.config.get('label_map', 'label_map') - self.labels = self.dataset.metadata.get(label_map) - self.meta['names'] = list(self.labels.values()) - - def loss(annotation_label, prediction_top_k_labels): - result = np.zeros_like(list(self.labels.keys())) - if annotation_label in prediction_top_k_labels: - result[annotation_label] = 1 - - return result - - def counter(annotation_label): - result = np.zeros_like(list(self.labels.keys())) - result[annotation_label] = 1 - return result - - self.accuracy = AverageMeter(loss, counter) - - def update(self, annotation, prediction): - self.accuracy.update(annotation.label, prediction.top_k(self.top_k)) - - def evaluate(self, annotations, predictions): - return self.accuracy.evaluate() - - -class AverageProbMeter(AverageMeter): - def __init__(self): - def loss(annotation_label, prediction_scores): - return prediction_scores - super().__init__(loss=loss) - - -class ClipAccuracy(PerImageEvaluationMetric): - __provider__ = 'clip_accuracy' - - annotation_types = (ClassificationAnnotation, ) - prediction_types = (ClassificationPrediction, ) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.clip_accuracy = AverageMeter() - self.video_accuracy = AverageMeter() - self.video_avg_prob = AverageProbMeter() - self.previous_video_id = None - self.previous_video_label = None - - def update(self, annotation, prediction): - video_id = annotation.identifier.video - - if self.previous_video_id is not None and video_id != self.previous_video_id: - video_top_label = np.argmax(self.video_avg_prob.evaluate()) - self.video_accuracy.update(video_top_label, self.previous_video_label) - self.video_avg_prob = AverageProbMeter() - - self.video_avg_prob.update(annotation.label, prediction.scores) - - self.clip_accuracy.update(annotation.label, prediction.label) - - self.previous_video_id = video_id - self.previous_video_label = annotation.label - - def evaluate(self, annotations, predictions): - self.meta['names'] = ['clip_accuracy', 'video_accuracy'] - return [self.clip_accuracy.evaluate(), self.video_accuracy.evaluate()] diff --git a/tools/accuracy_checker/accuracy_checker/metrics/coco_metrics.py b/tools/accuracy_checker/accuracy_checker/metrics/coco_metrics.py deleted file mode 100644 index e9c1ea2b83974f..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/metrics/coco_metrics.py +++ /dev/null @@ -1,317 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from functools import singledispatch -from typing import Union -import numpy as np -from ..config import NumberField, BaseField -from ..representation import ( - DetectionPrediction, - DetectionAnnotation, - PoseEstimationPrediction, - PoseEstimationAnnotation -) -from ..utils import get_or_parse_value -from .overlap import Overlap -from .metric import BaseMetricConfig, PerImageEvaluationMetric - -COCO_THRESHOLDS = { - '.50': [0.5], - '.75': [0.75], - '.50:.05:.95': np.linspace(.5, 0.95, np.round((0.95 - .5) / .05).astype(int) + 1, endpoint=True) -} - - -class MSCOCOMetricConfig(BaseMetricConfig): - max_detections = NumberField(optional=True) - threshold = BaseField(optional=True) - - -class MSCOCOBaseMetric(PerImageEvaluationMetric): - annotation_types = (PoseEstimationAnnotation, DetectionAnnotation) - prediction_types = (PoseEstimationPrediction, DetectionPrediction) - _config_validator_type = MSCOCOMetricConfig - - def configure(self): - self.max_detections = self.config.get('max_detections', 20) - self.thresholds = get_or_parse_value(self.config.get('threshold', '.50:.05:.95'), COCO_THRESHOLDS) - label_map = self.dataset.metadata.get('label_map', []) - self.labels = [ - label for label in label_map - if label != self.dataset.metadata.get('background_label') - ] - self.meta['names'] = [label_map[label] for label in self.labels] - self.matching_results = [[] for _ in self.labels] - - def update(self, annotation, prediction): - compute_iou, create_boxes = select_specific_parameters(annotation) - - for label_id, label in enumerate(self.labels): - detections, scores, dt_difficult = prepare_predictions(prediction, label, self.max_detections) - ground_truth, gt_difficult, iscrowd, boxes, areas = prepare_annotations(annotation, label, create_boxes) - iou = compute_iou(ground_truth, detections, boxes, areas) - self.matching_results[label_id].append( - evaluate_image( - ground_truth, - gt_difficult, - iscrowd, - detections, - dt_difficult, - scores, - iou, - self.thresholds - )) - - def evaluate(self, annotations, predictions): - pass - - -class MSCOCOAveragePresicion(MSCOCOBaseMetric): - __provider__ = 'coco_precision' - - def evaluate(self, annotations, predictions): - precision = [ - compute_precision_recall(self.thresholds, self.matching_results[i])[0] - for i, _ in enumerate(self.labels) - ] - - return precision - - -class MSCOCORecall(MSCOCOBaseMetric): - __provider__ = 'coco_recall' - - def evaluate(self, annotations, predictions): - recalls = [ - compute_precision_recall(self.thresholds, self.matching_results[i])[1] - for i, _ in enumerate(self.labels) - ] - - return recalls -@singledispatch -def select_specific_parameters(annotation): - return compute_iou_boxes, False - -@select_specific_parameters.register(PoseEstimationAnnotation) -def pose_estimation_params(annotation): - return compute_oks, True - -@singledispatch -def prepare(entry, order): - return np.c_[entry.x_mins[order], entry.y_mins[order], entry.x_maxs[order], entry.y_maxs[order]] - - -@prepare.register(Union[PoseEstimationPrediction, PoseEstimationAnnotation]) -def prepare_keypoints(entry, order): - if entry.size == 0: - return [] - - if np.size(entry.x_values[order]) == 0: - return [] - - return np.concatenate((entry.x_values[order], entry.y_values[order], entry.visibility[order]), axis=-1) - - -def prepare_predictions(prediction, label, max_detections): - if prediction.size == 0: - return [], [], [] - prediction_ids = prediction.labels == label - scores = prediction.scores[prediction_ids] - if np.size(scores) == 0: - return [], [], [] - scores_ids = np.argsort(- scores, kind='mergesort') - difficult_box_mask = np.full(prediction.size, False) - difficult_box_mask[prediction.metadata.get('difficult_boxes', [])] = True - difficult_for_label = difficult_box_mask[prediction_ids] - if len(scores_ids) > max_detections: - scores_ids = scores_ids[:max_detections] - detections = prepare(prediction, prediction_ids) - detections = detections[scores_ids] - - return detections, scores[scores_ids], difficult_for_label[scores_ids] - - -def prepare_annotations(annotation, label, create_boxes=False): - annotation_ids = annotation.labels == label - difficult_box_mask = np.full(annotation.size, False) - difficult_box_indices = annotation.metadata.get("difficult_boxes", []) - iscrowd = np.array(annotation.metadata.get('iscrowd', [0]*annotation.size)) - difficult_box_mask[difficult_box_indices] = True - difficult_box_mask[iscrowd > 0] = True - difficult_label = difficult_box_mask[annotation_ids] - not_difficult_box_indices = np.argwhere(~difficult_label).reshape(-1) - difficult_box_indices = np.argwhere(difficult_label).reshape(-1) - iscrowd_label = iscrowd[annotation_ids] - order = np.hstack((not_difficult_box_indices, difficult_box_indices)).astype(int) - boxes = None - areas = None - if create_boxes: - boxes = np.array(annotation.bboxes) - boxes = boxes[annotation_ids] - areas = np.array(annotation.areas) - areas = areas[annotation_ids] if np.size(areas) > 0 else np.array([]) - boxes = boxes[order] - areas = areas[order] - - return prepare(annotation, annotation_ids)[order], difficult_label[order], iscrowd_label[order], boxes, areas - - -def compute_precision_recall(thresholds, matching_results): - num_thresholds = len(thresholds) - rectangle_thresholds = np.linspace(.0, 1.00, np.round((1.00 - .0) / .01) + 1, endpoint=True) - num_rec_thresholds = len(rectangle_thresholds) - precision = -np.ones((num_thresholds, num_rec_thresholds)) # -1 for the precision of absent categories - recall = -np.ones(num_thresholds) - dt_scores = np.concatenate([e['scores'] for e in matching_results]) - inds = np.argsort(-dt_scores, kind='mergesort') - dtm = np.concatenate([e['dt_matches'] for e in matching_results], axis=1)[:, inds] - dt_ignored = np.concatenate([e['dt_ignore'] for e in matching_results], axis=1)[:, inds] - gt_ignored = np.concatenate([e['gt_ignore'] for e in matching_results]) - npig = np.count_nonzero(gt_ignored == 0) - tps = np.logical_and(dtm, np.logical_not(dt_ignored)) - fps = np.logical_and(np.logical_not(dtm), np.logical_not(dt_ignored)) - tp_sum = np.cumsum(tps, axis=1).astype(dtype=np.float) - fp_sum = np.cumsum(fps, axis=1).astype(dtype=np.float) - for t, (tp, fp) in enumerate(zip(tp_sum, fp_sum)): - tp = np.array(tp) - fp = np.array(fp) - num_detections = len(tp) - rc = tp / npig - pr = tp / (fp + tp + np.spacing(1)) - q = np.zeros(num_rec_thresholds) - - if num_detections: - recall[t] = rc[-1] - else: - recall[t] = 0 - - # numpy is slow without cython optimization for accessing elements - # use python array gets significant speed improvement - pr = pr.tolist() - q = q.tolist() - - for i in range(num_detections - 1, 0, -1): - if pr[i] > pr[i - 1]: - pr[i - 1] = pr[i] - - inds = np.searchsorted(rc, rectangle_thresholds, side='left') - try: - for ri, pi in enumerate(inds): - q[ri] = pr[pi] - except IndexError: - pass - precision[t] = np.array(q) - - mean_precision = 0 if np.size(precision[precision > -1]) == 0 else np.mean(precision[precision > -1]) - mean_recall = 0 if np.size(recall[recall > -1]) == 0 else np.mean(recall[recall > -1]) - - return mean_precision, mean_recall - - -def compute_iou_boxes(annotation, prediction, *args, **kwargs): - if np.size(annotation) == 0 or np.size(prediction) == 0: - return [] - overlap = Overlap.provide('iou') - iou = np.zeros((prediction.size // 4, annotation.size // 4), dtype=np.float32) - for i, box_a in enumerate(annotation): - for j, box_b in enumerate(prediction): - iou[j, i] = overlap(box_a, box_b) - - return iou - - -def compute_oks(annotation_points, prediction_points, annotation_boxes, annotation_areas): - if np.size(prediction_points) == 0 or np.size(annotation_points) == 0: - return [] - oks = np.zeros((len(prediction_points), len(annotation_points))) - sigmas = np.array([.26, .25, .25, .35, .35, .79, .79, .72, .72, .62, .62, 1.07, 1.07, .87, .87, .89, .89])/10.0 - variance = (sigmas * 2)**2 - # compute oks between each detection and ground truth object - for gt_idx, gt_points in enumerate(annotation_points): - # create bounds for ignore regions(double the gt bbox) - xgt = gt_points[:17] - ygt = gt_points[17:34] - vgt = gt_points[34:] - k1 = np.count_nonzero(vgt > 0) - x0_bbox, y0_bbox, x1_bbox, y1_bbox = annotation_boxes[gt_idx] - area_gt = annotation_areas[gt_idx] - w_bbox = x1_bbox - x0_bbox - h_bbox = y1_bbox - y0_bbox - x0 = x0_bbox - w_bbox - x1 = x0_bbox + w_bbox * 2 - y0 = y0_bbox - h_bbox - y1 = y0_bbox + h_bbox * 2 - for dt_idx, dt_points in enumerate(prediction_points): - xdt = dt_points[:17] - ydt = dt_points[17:34] - if k1 > 0: - # measure the per-keypoint distance if keypoints visible - x_diff = xdt - xgt - y_diff = ydt - ygt - else: - # measure minimum distance to keypoints in (x0,y0) & (x1,y1) - zeros = np.zeros(len(sigmas)) - x_diff = np.max((zeros, x0 - xdt), axis=0) + np.max((zeros, xdt - x1), axis=0) - y_diff = np.max((zeros, y0 - ydt), axis=0) + np.max((zeros, ydt - y1), axis=0) - evaluation = (x_diff ** 2 + y_diff ** 2) / variance / (area_gt + np.spacing(1)) / 2 - if k1 > 0: - evaluation = evaluation[vgt > 0] - oks[dt_idx, gt_idx] = np.sum(np.exp(- evaluation)) / evaluation.shape[0] - - return oks - - -def evaluate_image(ground_truth, gt_difficult, iscrowd, detections, dt_difficult, scores, iou, thresholds): - thresholds_num = len(thresholds) - gt_num = len(ground_truth) - dt_num = len(detections) - gt_matched = np.zeros((thresholds_num, gt_num)) - dt_matched = np.zeros((thresholds_num, dt_num)) - gt_ignored = gt_difficult - dt_ignored = np.zeros((thresholds_num, dt_num)) - if np.size(iou): - for tind, t in enumerate(thresholds): - for dtind, _ in enumerate(detections): - # information about best match so far (matched_id = -1 -> unmatched) - iou_current = min([t, 1-1e-10]) - matched_id = -1 - for gtind, _ in enumerate(ground_truth): - # if this gt already matched, and not a crowd, continue - if gt_matched[tind, gtind] > 0 and not iscrowd[gtind]: - continue - # if dt matched to reg gt, and on ignore gt, stop - if matched_id > -1 and not gt_ignored[matched_id] and gt_ignored[gtind]: - break - # continue to next gt unless better match made - if iou[dtind, gtind] < iou_current: - continue - # if match successful and best so far, store appropriately - iou_current = iou[dtind, gtind] - matched_id = gtind - # if match made store id of match for both dt and gt - if matched_id == -1: - continue - dt_ignored[tind, dtind] = gt_ignored[matched_id] - dt_matched[tind, dtind] = 1 - gt_matched[tind, matched_id] = dtind - # store results for given image - return { - 'dt_matches': dt_matched, - 'gt_matches': gt_matched, - 'gt_ignore': gt_ignored, - 'dt_ignore': np.logical_or(dt_ignored, dt_difficult), - 'scores': scores - } diff --git a/tools/accuracy_checker/accuracy_checker/metrics/detection.py b/tools/accuracy_checker/accuracy_checker/metrics/detection.py deleted file mode 100644 index 7a5c29c7c0d81f..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/metrics/detection.py +++ /dev/null @@ -1,473 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import bisect -import enum -import warnings -from typing import List - -import numpy as np - -from ..utils import finalize_metric_result -from .overlap import Overlap, IOA -from ..config import BoolField, NumberField, StringField -from ..representation import DetectionAnnotation, DetectionPrediction -from .metric import BaseMetricConfig, FullDatasetEvaluationMetric - - -class APIntegralType(enum.Enum): - voc_11_point = '11point' - voc_max = 'max' - - -class BaseDetectionMetricConfig(BaseMetricConfig): - overlap_threshold = NumberField(min_value=0, max_value=1, optional=True) - ignore_difficult = BoolField(optional=True) - include_boundaries = BoolField(optional=True) - distinct_conf = BoolField(optional=True) - allow_multiple_matches_per_ignored = BoolField(optional=True) - overlap_method = StringField(optional=True, choices=Overlap.providers) - use_filtered_tp = BoolField(optional=True) - - -class MAPConfigValidator(BaseDetectionMetricConfig): - integral = StringField(choices=[e.value for e in APIntegralType], optional=True) - - -class MRConfigValidator(BaseDetectionMetricConfig): - fppi_level = NumberField(min_value=0, max_value=1) - - -class DAConfigValidator(BaseDetectionMetricConfig): - use_normalization = BoolField(optional=True) - - -class BaseDetectionMetricMixin: - def configure(self): - self.overlap_threshold = self.config.get('overlap_threshold', 0.5) - self.ignore_difficult = self.config.get('ignore_difficult', True) - self.include_boundaries = self.config.get('include_boundaries', True) - self.distinct_conf = self.config.get('distinct_conf', False) - self.allow_multiple_matches_per_ignored = self.config.get('allow_multiple_matches_per_ignored', False) - self.overlap_method = Overlap.provide(self.config.get('overlap', 'iou'), self.include_boundaries) - self.use_filtered_tp = self.config.get('use_filtered_tp', False) - - label_map = self.config.get('label_map', 'label_map') - labels = self.dataset.metadata.get(label_map, {}) - self.labels = labels.keys() - valid_labels = list(filter(lambda x: x != self.dataset.metadata.get('background_label'), self.labels)) - self.meta['names'] = [labels[name] for name in valid_labels] - - def per_class_detection_statistics(self, annotations, predictions, labels): - labels_stat = {} - for label in labels: - tp, fp, conf, n = bbox_match( - annotations, predictions, int(label), - self.overlap_method, self.overlap_threshold, - self.ignore_difficult, self.allow_multiple_matches_per_ignored, self.include_boundaries, - self.use_filtered_tp - ) - - if not tp.size: - labels_stat[label] = { - 'precision': np.array([]), - 'recall': np.array([]), - 'thresholds': conf, - 'fppi': np.array([]) - } - continue - - # select only values for distinct confidences - if self.distinct_conf: - distinct_value_indices = np.where(np.diff(conf))[0] - threshold_indexes = np.r_[distinct_value_indices, tp.size - 1] - else: - threshold_indexes = np.arange(conf.size) - - tp, fp = np.cumsum(tp)[threshold_indexes], np.cumsum(fp)[threshold_indexes] - - labels_stat[label] = { - 'precision': tp / np.maximum(tp + fp, np.finfo(np.float64).eps), - 'recall': tp / np.maximum(n, np.finfo(np.float64).eps), - 'thresholds': conf[threshold_indexes], - 'fppi': fp / len(annotations) - } - - return labels_stat - - -class DetectionMAP(BaseDetectionMetricMixin, FullDatasetEvaluationMetric): - """ - Class for evaluating mAP metric of detection models. - """ - - __provider__ = 'map' - - annotation_types = (DetectionAnnotation, ) - prediction_types = (DetectionPrediction, ) - - _config_validator_type = MAPConfigValidator - - def configure(self): - super().configure() - self.integral = APIntegralType(self.config.get('integral', APIntegralType.voc_max)) - - def evaluate(self, annotations, predictions): - valid_labels = get_valid_labels(self.labels, self.dataset.metadata.get('background_label')) - labels_stat = self.per_class_detection_statistics(annotations, predictions, valid_labels) - - average_precisions = [] - for label in labels_stat: - label_precision = labels_stat[label]['precision'] - label_recall = labels_stat[label]['recall'] - if label_recall.size: - ap = average_precision(label_precision, label_recall, self.integral) - average_precisions.append(ap) - else: - average_precisions.append(np.nan) - - average_precisions, self.meta['names'] = finalize_metric_result(average_precisions, self.meta['names']) - if not average_precisions: - warnings.warn("No detections to compute mAP") - average_precisions.append(0) - - return average_precisions - - -class MissRate(BaseDetectionMetricMixin, FullDatasetEvaluationMetric): - """ - Class for evaluating Miss Rate metric of detection models. - """ - - __provider__ = 'miss_rate' - - annotation_types = (DetectionAnnotation, ) - prediction_types = (DetectionPrediction, ) - - _config_validator_type = MRConfigValidator - - def configure(self): - super().configure() - self.fppi_level = self.config.get('fppi_level') - - def evaluate(self, annotations, predictions): - valid_labels = get_valid_labels(self.labels, self.dataset.metadata.get('background_label')) - labels_stat = self.per_class_detection_statistics(annotations, predictions, valid_labels) - - miss_rates = [] - for label in labels_stat: - label_miss_rate = 1.0 - labels_stat[label]['recall'] - label_fppi = labels_stat[label]['fppi'] - - position = bisect.bisect_left(label_fppi, self.fppi_level) - m0 = max(0, position - 1) - m1 = position if position < len(label_miss_rate) else m0 - miss_rates.append(0.5 * (label_miss_rate[m0] + label_miss_rate[m1])) - - return miss_rates - - -class Recall(BaseDetectionMetricMixin, FullDatasetEvaluationMetric): - """ - Class for evaluating recall metric of detection models. - """ - - __provider__ = 'recall' - - annotation_types = (DetectionAnnotation, ) - prediction_types = (DetectionPrediction, ) - - _config_validator_type = BaseDetectionMetricConfig - - def evaluate(self, annotations, predictions): - valid_labels = get_valid_labels(self.labels, self.dataset.metadata.get('background_label')) - labels_stat = self.per_class_detection_statistics(annotations, predictions, valid_labels) - - recalls = [] - for label in labels_stat: - label_recall = labels_stat[label]['recall'] - if label_recall.size: - max_recall = label_recall[-1] - recalls.append(max_recall) - else: - recalls.append(np.nan) - - recalls, self.meta['names'] = finalize_metric_result(recalls, self.meta['names']) - if not recalls: - warnings.warn("No detections to compute mAP") - recalls.append(0) - - return recalls - - -class DetectionAccuracyMetric(BaseDetectionMetricMixin, FullDatasetEvaluationMetric): - __provider__ = 'detection_accuracy' - - annotation_types = (DetectionAnnotation, ) - prediction_types = (DetectionPrediction, ) - _config_validator_type = DAConfigValidator - - def configure(self): - super().configure() - self.use_normalization = self.config.get('use_normalization', False) - - def evaluate(self, annotations, predictions): - all_matches, _, _ = match_detections_class_agnostic( - predictions, annotations, self.overlap_threshold, self.overlap_method - ) - cm = confusion_matrix(all_matches, predictions, annotations, len(self.labels)) - if self.use_normalization: - return np.mean(normalize_confusion_matrix(cm).diagonal()) - - return float(np.sum(cm.diagonal())) / float(np.maximum(1, np.sum(cm))) - - -def confusion_matrix(all_matched_ids, predicted_data, gt_data, num_classes): - out_cm = np.zeros([num_classes, num_classes], dtype=np.int32) - for gt, prediction in zip(gt_data, predicted_data): - for match_pair in all_matched_ids[gt.identifier]: - gt_label = int(gt.labels[match_pair[0]]) - pred_label = int(prediction.labels[match_pair[1]]) - out_cm[gt_label, pred_label] += 1 - - return out_cm - - -def normalize_confusion_matrix(cm): - row_sums = np.maximum(1, np.sum(cm, axis=1, keepdims=True)).astype(np.float32) - return cm.astype(np.float32) / row_sums - - -def match_detections_class_agnostic(predicted_data, gt_data, min_iou, overlap_method): - all_matches = {} - total_gt_bbox_num = 0 - matched_gt_bbox_num = 0 - - for gt, prediction in zip(gt_data, predicted_data): - gt_bboxes = np.stack((gt.x_mins, gt.y_mins, gt.x_maxs, gt.y_maxs), axis=-1) - predicted_bboxes = np.stack( - (prediction.x_mins, prediction.y_mins, prediction.x_maxs, prediction.y_maxs), axis=-1 - ) - - total_gt_bbox_num += len(gt_bboxes) - - similarity_matrix = calculate_similarity_matrix(gt_bboxes, predicted_bboxes, overlap_method) - - matches = [] - for _ in gt_bboxes: - best_match_pos = np.unravel_index(similarity_matrix.argmax(), similarity_matrix.shape) - best_match_value = similarity_matrix[best_match_pos] - - if best_match_value <= min_iou: - break - - gt_id = best_match_pos[0] - predicted_id = best_match_pos[1] - - similarity_matrix[gt_id, :] = 0.0 - similarity_matrix[:, predicted_id] = 0.0 - - matches.append((gt_id, predicted_id)) - matched_gt_bbox_num += 1 - - all_matches[gt.identifier] = matches - - return all_matches, total_gt_bbox_num, matched_gt_bbox_num - - -def calculate_similarity_matrix(set_a, set_b, overlap): - similarity = np.zeros([len(set_a), len(set_b)], dtype=np.float32) - for i, box_a in enumerate(set_a): - for j, box_b in enumerate(set_b): - similarity[i, j] = overlap(box_a, box_b) - - return similarity - - -def average_precision(precision, recall, integral): - if integral == APIntegralType.voc_11_point: - result = 0. - for point in np.arange(0., 1.1, 0.1): - accumulator = 0 if np.sum(recall >= point) == 0 else np.max(precision[recall >= point]) - result = result + accumulator / 11. - - return result - - if integral != APIntegralType.voc_max: - raise NotImplementedError("Integral type not implemented") - - # first append sentinel values at the end - recall = np.concatenate(([0.], recall, [1.])) - precision = np.concatenate(([0.], precision, [0.])) - - # compute the precision envelope - for i in range(precision.size - 1, 0, -1): - precision[i - 1] = np.maximum(precision[i - 1], precision[i]) - - # to calculate area under PR curve, look for points - # where X axis (recall) changes value - change_point = np.where(recall[1:] != recall[:-1])[0] - # and sum (\Delta recall) * recall - return np.sum((recall[change_point + 1] - recall[change_point]) * precision[change_point + 1]) - - -def bbox_match(annotation: List[DetectionAnnotation], prediction: List[DetectionPrediction], label, overlap_evaluator, - overlap_thresh=0.5, ignore_difficult=True, allow_multiple_matches_per_ignored=True, - include_boundaries=True, use_filtered_tp=False): - """ - Args: - annotation: ground truth bounding boxes. - prediction: predicted bounding boxes. - label: class for which bounding boxes are matched. - overlap_evaluator: evaluator of overlap. - overlap_thresh: bounding box IoU threshold. - ignore_difficult: ignores difficult bounding boxes (see Pascal VOC). - allow_multiple_matches_per_ignored: allows multiple matches per ignored. - include_boundaries: if is True then width and height of box is calculated by max - min + 1. - use_filtered_tp: if is True then ignored object are counted during evaluation. - Returns: - tp: tp[i] == 1 if detection with i-th highest score is true positive. - fp: fp[i] == 1 if detection with i-th highest score is false positive. - thresholds: array of confidence thresholds. - number_ground_truth = number of true positives. - """ - - used_boxes, number_ground_truth, difficult_boxes_annotation = _prepare_annotation_boxes( - annotation, ignore_difficult, label - ) - prediction_boxes, prediction_images, difficult_boxes_prediction = _prepare_prediction_boxes( - label, prediction, ignore_difficult - ) - - tp = np.zeros_like(prediction_images) - fp = np.zeros_like(prediction_images) - - for image in range(prediction_images.shape[0]): - gt_img = annotation[prediction_images[image]] - annotation_difficult = difficult_boxes_annotation[gt_img.identifier] - used = used_boxes[gt_img.identifier] - - idx = gt_img.labels == label - if not np.array(idx).any(): - fp[image] = 1 - continue - - prediction_box = prediction_boxes[image][1:] - annotation_boxes = gt_img.x_mins[idx], gt_img.y_mins[idx], gt_img.x_maxs[idx], gt_img.y_maxs[idx] - - overlaps = overlap_evaluator(prediction_box, annotation_boxes) - if ignore_difficult and allow_multiple_matches_per_ignored: - ioa = IOA(include_boundaries) - ignored = np.where(annotation_difficult == 1)[0] - ignored_annotation_boxes = ( - annotation_boxes[0][ignored], annotation_boxes[1][ignored], - annotation_boxes[2][ignored], annotation_boxes[3][ignored] - ) - overlaps[ignored] = ioa.evaluate(prediction_box, ignored_annotation_boxes) - - max_overlap = -np.inf - - not_ignored_overlaps = overlaps[np.where(annotation_difficult == 0)[0]] - ignored_overlaps = overlaps[np.where(annotation_difficult == 1)[0]] - if not_ignored_overlaps.size: - max_overlap = np.max(not_ignored_overlaps) - - if max_overlap < overlap_thresh and ignored_overlaps.size: - max_overlap = np.max(ignored_overlaps) - max_overlapped = np.where(overlaps == max_overlap)[0] - - def set_false_positive(box_index): - is_box_difficult = difficult_boxes_prediction[box_index].any() - return int(not ignore_difficult or not is_box_difficult) - - if max_overlap < overlap_thresh: - fp[image] = set_false_positive(image) - continue - - if not annotation_difficult[max_overlapped].any(): - if not used[max_overlapped].any(): - if not ignore_difficult or use_filtered_tp or not difficult_boxes_prediction[image].any(): - tp[image] = 1 - used[max_overlapped] = True - else: - fp[image] = set_false_positive(image) - elif not allow_multiple_matches_per_ignored: - if used[max_overlapped].any(): - fp[image] = set_false_positive(image) - used[max_overlapped] = True - - return tp, fp, prediction_boxes[:, 0], number_ground_truth - - -def _prepare_annotation_boxes(annotation, ignore_difficult, label): - used_boxes = {} - difficult_boxes = {} - num_ground_truth = 0 - - for ground_truth in annotation: - idx_for_label = ground_truth.labels == label - filtered_label = ground_truth.labels[idx_for_label] - used_ = np.zeros_like(filtered_label) - used_boxes[ground_truth.identifier] = used_ - num_ground_truth += used_.shape[0] - - difficult_box_mask = np.full_like(ground_truth.labels, False) - difficult_box_indices = ground_truth.metadata.get("difficult_boxes", []) - if ignore_difficult: - difficult_box_mask[difficult_box_indices] = True - difficult_box_mask = difficult_box_mask[idx_for_label] - - difficult_boxes[ground_truth.identifier] = difficult_box_mask - if ignore_difficult: - num_ground_truth -= np.sum(difficult_box_mask) - - return used_boxes, num_ground_truth, difficult_boxes - - -def _prepare_prediction_boxes(label, predictions, ignore_difficult): - prediction_images = [] - prediction_boxes = [] - indexes = [] - difficult_boxes = [] - for i, prediction in enumerate(predictions): - idx = prediction.labels == label - - prediction_images.append(np.full(prediction.labels[idx].shape, i)) - prediction_boxes.append(np.c_[ - prediction.scores[idx], - prediction.x_mins[idx], prediction.y_mins[idx], prediction.x_maxs[idx], prediction.y_maxs[idx] - ]) - - difficult_box_mask = np.full_like(prediction.labels, False) - difficult_box_indices = prediction.metadata.get("difficult_boxes", []) - if ignore_difficult: - difficult_box_mask[difficult_box_indices] = True - - difficult_boxes.append(difficult_box_mask) - indexes.append(np.argwhere(idx)) - - prediction_boxes = np.concatenate(prediction_boxes) - difficult_boxes = np.concatenate(difficult_boxes) - sorted_order = np.argsort(-prediction_boxes[:, 0]) - prediction_boxes = prediction_boxes[sorted_order] - prediction_images = np.concatenate(prediction_images)[sorted_order] - difficult_boxes = difficult_boxes[sorted_order] - - return prediction_boxes, prediction_images, difficult_boxes - - -def get_valid_labels(labels, background): - return list(filter(lambda label: label != background, labels)) diff --git a/tools/accuracy_checker/accuracy_checker/metrics/hit_ratio.py b/tools/accuracy_checker/accuracy_checker/metrics/hit_ratio.py deleted file mode 100644 index f5ce2c73c935e0..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/metrics/hit_ratio.py +++ /dev/null @@ -1,97 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import heapq -import math - -import numpy as np - -from ..representation import HitRatioAnnotation, HitRatioPrediction -from .metric import FullDatasetEvaluationMetric, BaseMetricConfig -from ..config import NumberField - - -class RecommenderConfigValidator(BaseMetricConfig): - top_k = NumberField(floats=False, min_value=1, optional=True) - - -class BaseRecommenderMetric(FullDatasetEvaluationMetric): - annotation_types = (HitRatioAnnotation, ) - prediction_types = (HitRatioPrediction, ) - _config_validator_type = RecommenderConfigValidator - - def __init__(self, discounter, *args, **kwargs): - super().__init__(*args, **kwargs) - self.discounter = discounter or (lambda item, rank: int(item in rank)) - - def configure(self): - self.top_k = self.config.get('top_k', 10) - self.users_num = self.dataset.metadata.get('users_number') - self.pred_per_user = {i: [] for i in range(self.users_num)} - self.gt_items = {} - - def update(self, annotation, prediction): - self.pred_per_user[prediction.user].append((prediction.item, prediction.scores)) - if annotation.positive: - self.gt_items[annotation.user] = annotation.item - - def evaluate(self, annotations, predictions): - iter_num = len(self.pred_per_user[0]) - - measure = [] - for user in range(self.users_num): - map_item_score = {} - for j in range(iter_num): - item = self.pred_per_user[user][j][0] - score = self.pred_per_user[user][j][1] - map_item_score[item] = score - ranklist = heapq.nlargest(10, map_item_score, key=map_item_score.get) - measure.append(self.discounter(self.gt_items[user], ranklist)) - - return np.mean(measure) - - -def hit_ratio_discounter(item, rank): - return int(item in rank) - - -def ndcg_discounter(item, rank): - if item in rank: - return math.log(2) / math.log(rank.index(item) + 2) - - return 0 - - -class HitRatioMetric(BaseRecommenderMetric): - """ - Class for evaluating Hit Ratio metric - """ - - __provider__ = 'hit_ratio' - - def __init__(self, *args, **kwargs): - super().__init__(hit_ratio_discounter, *args, **kwargs) - - -class NDSGMetric(BaseRecommenderMetric): - """ - Class for evaluating Normalized Discounted Cumulative Gain metric - """ - - __provider__ = 'ndcg' - - def __init__(self, *args, **kwargs): - super().__init__(ndcg_discounter, *args, **kwargs) diff --git a/tools/accuracy_checker/accuracy_checker/metrics/metric.py b/tools/accuracy_checker/accuracy_checker/metrics/metric.py deleted file mode 100644 index cb229dc7ddd83e..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/metrics/metric.py +++ /dev/null @@ -1,171 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from ..representation import ContainerRepresentation -from ..config import ConfigError -from ..utils import is_single_metric_source, get_supported_representations -from ..presenters import BasePresenter -from ..config import ConfigValidator, NumberField, StringField -from ..dependency import ClassProvider -from ..utils import zipped_transform - - -class BaseMetricConfig(ConfigValidator): - type = StringField() - name = StringField(optional=True) - reference = NumberField(optional=True) - threshold = NumberField(min_value=0, optional=True) - presenter = StringField(choices=BasePresenter.providers, optional=True) - label_map = StringField(optional=True) - prediction_source = StringField(optional=True) - annotation_source = StringField(optional=True) - - -class Metric(ClassProvider): - """ - Interface for evaluating metrics. - """ - - __provider_type__ = 'metric' - - annotation_types = () - prediction_types = () - - _config_validator_type = BaseMetricConfig - - def __init__(self, config, dataset, name=None, state=None): - self.config = config - self.name = name - self.dataset = dataset - self.state = state - self._update_iter = 0 - self.meta = {} - - self.validate_config() - self.configure() - message_unsupported_multi_source = 'metric {} does not support several {} sources' - self.annotation_source = self.config.get('annotation_source') - - if self.annotation_source and not is_single_metric_source(self.annotation_source): - raise ConfigError(message_unsupported_multi_source.format(self.name, 'annotation')) - - self.prediction_source = self.config.get('prediction_source') - if self.prediction_source and not is_single_metric_source(self.prediction_source): - raise ConfigError(message_unsupported_multi_source.format(self.name, 'prediction')) - - def __call__(self, *args, **kwargs): - return self.submit_all(*args, **kwargs) - - def submit(self, annotation, prediction): - self.update(annotation, prediction) - - def submit_all(self, annotations, predictions): - return self.evaluate(annotations, predictions) - - def update(self, annotation, prediction): - pass - - def evaluate(self, annotations, predictions): - raise NotImplementedError - - def configure(self): - """ - Specifies configuration structure for metric entry. - """ - - pass - - def validate_config(self): - """ - Validate that metric entry meets all configuration structure requirements. - """ - - self._config_validator_type( - self.name, on_extra_argument=BaseMetricConfig.ERROR_ON_EXTRA_ARGUMENT - ).validate(self.config) - - def _update_state(self, fn, state_key, default_factory=None): - iter_key = "{}_global_it".format(state_key) - if state_key not in self.state: - default = default_factory() if default_factory else None - self.state[state_key] = default - self.state[iter_key] = 0 - - self._update_iter += 1 - if self.state[iter_key] < self._update_iter: - self.state[iter_key] += 1 - self.state[state_key] = fn(self.state[state_key]) - - def _resolve_representation_containers(self, annotation, prediction): - def get_resolve_subject(representation, source=None): - def is_container(representation): - if isinstance(representation, ContainerRepresentation): - return True - representation_parents = type(representation).__bases__ - representation_parents_names = [parent.__name__ for parent in representation_parents] - - return ContainerRepresentation.__name__ in representation_parents_names - - if not is_container(representation): - return representation - - if not source: - return representation.values() - - representation = representation.get(source) - if not representation: - raise ConfigError('{} not found'.format(source)) - - return representation - - annotation = get_resolve_subject(annotation, self.annotation_source) - prediction = get_resolve_subject(prediction, self.prediction_source) - - def resolve(representation, supported_types, representation_name): - message_not_found = 'suitable {} for metric {} not found' - message_need_source = 'you need specify {} source for metric {}' - - representation = get_supported_representations(representation, supported_types) - if not representation: - raise ConfigError(message_not_found.format(representation_name, self.name)) - - if len(representation) > 1: - raise ConfigError(message_need_source.format(representation_name, self.name)) - - return representation[0] - - resolved_annotation = resolve(annotation, self.annotation_types, 'annotation') - resolved_prediction = resolve(prediction, self.prediction_types, 'prediction') - - return resolved_annotation, resolved_prediction - - -class PerImageEvaluationMetric(Metric): - def submit(self, annotation, prediction): - annotation_, prediction_ = self._resolve_representation_containers(annotation, prediction) - self.update(annotation_, prediction_) - - def evaluate(self, annotations, predictions): - raise NotImplementedError - - -class FullDatasetEvaluationMetric(Metric): - def submit_all(self, annotations, predictions): - annotations_, predictions_ = zipped_transform(self._resolve_representation_containers, annotations, predictions) - return self.evaluate(annotations_, predictions_) - - def evaluate(self, annotations, predictions): - raise NotImplementedError diff --git a/tools/accuracy_checker/accuracy_checker/metrics/metric_executor.py b/tools/accuracy_checker/accuracy_checker/metrics/metric_executor.py deleted file mode 100644 index ff16cd78fe1080..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/metrics/metric_executor.py +++ /dev/null @@ -1,120 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from collections import namedtuple - -from ..presenters import BasePresenter, EvaluationResult -from ..config import StringField -from ..utils import zipped_transform -from .metric import BaseMetricConfig, Metric -from ..config import ConfigError - -MetricInstance = namedtuple( - 'MetricInstance', ['name', 'metric_type', 'metric_fn', 'reference', 'threshold', 'presenter'] -) - - -class MetricConfig(BaseMetricConfig): - type = StringField(choices=Metric.providers) - - -class MetricsExecutor: - """ - Class for evaluating metrics according to dataset configuration entry. - """ - - def __init__(self, metrics_config, dataset=None, state=None): - self.state = state or {} - dataset_name = dataset.name if dataset else '' - message_prefix = '{}'.format(dataset_name) - if not metrics_config: - raise ConfigError('{} dataset config must specify "{}"'.format(message_prefix, 'metrics')) - - self._dataset = dataset - - self.metrics = [] - type_ = 'type' - identifier = 'name' - reference = 'reference' - threshold = 'threshold' - presenter = 'presenter' - for metric_config_entry in metrics_config: - metric_config = MetricConfig( - "metrics", on_extra_argument=MetricConfig.IGNORE_ON_EXTRA_ARGUMENT - ) - metric_type = metric_config_entry.get(type_) - metric_config.validate(metric_config_entry, type_) - - metric_identifier = metric_config_entry.get(identifier, metric_type) - - metric_fn = Metric.provide( - metric_type, metric_config_entry, self.dataset, metric_identifier, state=self.state - ) - metric_presenter = BasePresenter.provide(metric_config_entry.get(presenter, 'print_scalar')) - - self.metrics.append(MetricInstance( - metric_identifier, - metric_type, - metric_fn, - metric_config_entry.get(reference), - metric_config_entry.get(threshold), - metric_presenter - )) - - @property - def dataset(self): - return self._dataset - - @dataset.setter - def _set_dataset(self, dataset): - self._dataset = dataset - for metric in self.metrics: - metric.metric_fn.dataset = dataset - - def __call__(self, context, *args, **kwargs): - self.update_metrics_on_batch(context.annotation_batch, context.prediction_batch) - context.annotations.extend(context.annotation_batch) - context.predictions.extend(context.prediction_batch) - - def update_metrics_on_object(self, annotation, prediction): - """ - Updates metric value corresponding given annotation and prediction objects. - """ - - for metric in self.metrics: - metric.metric_fn.submit(annotation, prediction) - - def update_metrics_on_batch(self, annotation, prediction): - """ - Updates metric value corresponding given batch. - - Args: - annotation: list of batch number of annotation objects. - prediction: list of batch number of prediction objects. - """ - - zipped_transform(self.update_metrics_on_object, annotation, prediction) - - def iterate_metrics(self, annotations, predictions): - for name, metric_type, functor, reference, threshold, presenter in self.metrics: - yield presenter, EvaluationResult( - name=name, - metric_type=metric_type, - evaluated_value=functor(annotations, predictions), - reference_value=reference, - threshold=threshold, - meta=functor.meta, - ) diff --git a/tools/accuracy_checker/accuracy_checker/metrics/multilabel_recognition.py b/tools/accuracy_checker/accuracy_checker/metrics/multilabel_recognition.py deleted file mode 100644 index 9b24ce1976d52e..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/metrics/multilabel_recognition.py +++ /dev/null @@ -1,185 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np -from .metric import PerImageEvaluationMetric, BaseMetricConfig -from ..representation import MultiLabelRecognitionAnnotation, MultiLabelRecognitionPrediction -from ..config import StringField, BoolField - - -class MultiLabelConfigValidator(BaseMetricConfig): - label_map = StringField(optional=True) - calculate_average = BoolField(optional=True) - - -class MultiLabelMetric(PerImageEvaluationMetric): - annotation_types = (MultiLabelRecognitionAnnotation,) - prediction_types = (MultiLabelRecognitionPrediction,) - _config_validator_type = MultiLabelConfigValidator - - def configure(self): - label_map = self.config.get('label_map', 'label_map') - self.labels = self.dataset.metadata.get(label_map) - self.calculate_average = self.config.get('calculate_average', True) - - self.meta['scale'] = 1 - self.meta['postfix'] = '' - self.meta['calculate_mean'] = False - self.meta['names'] = list(self.labels.values()) - if self.calculate_average: - self.meta['names'].append('average') - self.tp = np.zeros_like(list(self.labels.keys()), dtype=np.float) - self.fp = np.zeros_like(list(self.labels.keys()), dtype=np.float) - self.tn = np.zeros_like(list(self.labels.keys()), dtype=np.float) - self.fn = np.zeros_like(list(self.labels.keys()), dtype=np.float) - - self.counter = np.zeros_like(list(self.labels.keys()), dtype=np.float) - - def update(self, annotation, prediction): - def loss(annotation_labels, prediction_labels): - tp_result = np.zeros_like(list(self.labels.keys()), dtype=np.float) - fp_results = np.zeros_like(list(self.labels.keys()), dtype=np.float) - tn_results = np.zeros_like(list(self.labels.keys()), dtype=np.float) - fn_results = np.zeros_like(list(self.labels.keys()), dtype=np.float) - - for index, label in enumerate(annotation_labels): - if label == 1 and label == prediction_labels[index]: - tp_result[index] = 1. - continue - - if label == 1 and label != prediction_labels[index]: - fn_results[index] = 1. - continue - - if label == 0 and label == prediction_labels[index]: - tn_results[index] = 1. - continue - - if label == 0 and label != prediction_labels[index]: - fp_results[index] = 1. - continue - - return tp_result, fp_results, tn_results, fn_results - - def counter(annotation_label): - count = np.zeros_like(annotation_label, dtype=float) - cond = np.where(np.array(annotation_label) != -1) - count[cond] = 1. - return count - - tp_upd, fp_upd, tn_upd, fn_upd = loss(annotation.multi_label, prediction.multi_label) - self.tp = np.add(self.tp, tp_upd) - self.fp = np.add(self.fp, fp_upd) - self.tn = np.add(self.tn, tn_upd) - self.fn = np.add(self.fn, fn_upd) - - self.counter = np.add(self.counter, counter(annotation.multi_label)) - - def evaluate(self, annotations, predictions): - pass - - -class MultiLabelAccuracy(MultiLabelMetric): - __provider__ = 'multi_accuracy' - - def evaluate(self, annotations, predictions): - tp_tn = np.add(self.tp, self.tn, dtype=float) - per_class = np.divide(tp_tn, self.counter, out=np.zeros_like(tp_tn, dtype=float), where=self.counter != 0) - average = np.sum(tp_tn) / np.sum(self.counter) - - return [*per_class, average] - - -class MultiLabelPrecision(MultiLabelMetric): - __provider__ = 'multi_precision' - - def evaluate(self, annotations, predictions): - tp_fp = np.add(self.tp, self.fp, dtype=float) - per_class = np.divide(self.tp, tp_fp, out=np.zeros_like(self.tp, dtype=float), where=tp_fp != 0) - if not self.calculate_average: - return per_class - average = np.sum(self.tp) / np.sum(tp_fp) - - return [*per_class, average] - - -class MultiLabelRecall(MultiLabelMetric): - __provider__ = 'multi_recall' - - def evaluate(self, annotations, predictions): - tp_fn = np.add(self.tp, self.fn, dtype=float) - per_class = np.divide(self.tp, tp_fn, out=np.zeros_like(self.tp, dtype=float), where=tp_fn != 0) - if not self.calculate_average: - return per_class - average = np.sum(self.tp) / np.sum(tp_fn) - - return [*per_class, average] - - -class F1Score(PerImageEvaluationMetric): - __provider__ = 'f1-score' - annotation_types = (MultiLabelRecognitionAnnotation,) - prediction_types = (MultiLabelRecognitionPrediction,) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.precision = MultiLabelPrecision(self.config, self.dataset) - self.recall = MultiLabelRecall(self.config, self.dataset) - - def validate_config(self): - class _F1ScoreValidator(BaseMetricConfig): - label_map = StringField(optional=True) - calculate_average = BoolField(optional=True) - - f1_score_config_validator = _F1ScoreValidator( - 'f1_score', on_extra_argument=_F1ScoreValidator.ERROR_ON_EXTRA_ARGUMENT - ) - f1_score_config_validator.validate(self.config) - - def configure(self): - label_map = self.config.get('label_map', 'label_map') - self.labels = self.dataset.metadata.get(label_map) - self.calculate_average = self.config.get('calculate_average', True) - self.meta['names'] = list(self.labels.values()) - if self.calculate_average: - self.meta['names'].append('average') - - self.meta['scale'] = 1 - self.meta['postfix'] = '' - self.meta['calculate_mean'] = False - self.meta['names'] = list(self.labels.values()) + ['average'] - - def update(self, annotation, prediction): - self.precision.update(annotation, prediction) - self.recall.update(annotation, prediction) - - def evaluate(self, annotations, predictions): - precisions = self.precision.evaluate(annotations, predictions) - recalls = self.recall.evaluate(annotations, predictions) - - precision_add = np.add(precisions[:-1], recalls[:-1], dtype=float) - precision_multiply = np.multiply(precisions[:-1], recalls[:-1], dtype=float) - - per_class = 2 * np.divide( - precision_multiply, precision_add, out=np.zeros_like(precision_multiply, dtype=float), - where=precision_add != 0 - ) - if not self.calculate_average: - return per_class - - average = 2 * (precisions[-1] * recalls[-1]) / (precisions[-1] + recalls[-1]) - - return [*per_class, average] diff --git a/tools/accuracy_checker/accuracy_checker/metrics/overlap.py b/tools/accuracy_checker/accuracy_checker/metrics/overlap.py deleted file mode 100644 index d9fffc77a4f987..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/metrics/overlap.py +++ /dev/null @@ -1,71 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from ..dependency import ClassProvider - - -class Overlap(ClassProvider): - __provider_type__ = 'overlap' - - @staticmethod - def intersections(prediction_box, annotation_boxes): - px_min, py_min, px_max, py_max = prediction_box - ax_mins, ay_mins, ax_maxs, ay_maxs = annotation_boxes - - x_mins = np.maximum(ax_mins, px_min) - y_mins = np.maximum(ay_mins, py_min) - x_maxs = np.minimum(ax_maxs, px_max) - y_maxs = np.minimum(ay_maxs, py_max) - - return x_mins, y_mins, np.maximum(x_mins, x_maxs), np.maximum(y_mins, y_maxs) - - def __init__(self, include_boundaries=None): - self.boundary = 1 if include_boundaries else 0 - - def __call__(self, *args, **kwargs): - return self.evaluate(*args, **kwargs) - - def evaluate(self, prediction_box, annotation_boxes): - raise NotImplementedError - - def area(self, box): - x0, y0, x1, y1 = box - return (x1 - x0 + self.boundary) * (y1 - y0 + self.boundary) - - -class IOU(Overlap): - __provider__ = 'iou' - - def evaluate(self, prediction_box, annotation_boxes): - intersections_area = self.area(self.intersections(prediction_box, annotation_boxes)) - unions = self.area(prediction_box) + self.area(annotation_boxes) - intersections_area - return np.divide( - intersections_area, unions, out=np.zeros_like(intersections_area, dtype=float), where=unions != 0 - ) - - -class IOA(Overlap): - __provider__ = 'ioa' - - def evaluate(self, prediction_box, annotation_boxes): - intersections_area = self.area(self.intersections(prediction_box, annotation_boxes)) - prediction_area = self.area(prediction_box) - return np.divide( - intersections_area, prediction_area, out=np.zeros_like(intersections_area, dtype=float), - where=prediction_area != 0 - ) diff --git a/tools/accuracy_checker/accuracy_checker/metrics/regression.py b/tools/accuracy_checker/accuracy_checker/metrics/regression.py deleted file mode 100644 index c70866f3be7839..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/metrics/regression.py +++ /dev/null @@ -1,357 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import warnings -import math -import numpy as np - -from ..representation import ( - RegressionAnnotation, - RegressionPrediction, - FacialLandmarksAnnotation, - FacialLandmarksPrediction, - SuperResolutionAnnotation, - SuperResolutionPrediction, - GazeVectorAnnotation, - GazeVectorPrediction -) - -from .metric import PerImageEvaluationMetric, BaseMetricConfig -from ..config import BaseField, NumberField, BoolField, ConfigError, StringField -from ..utils import string_to_tuple, finalize_metric_result - - -class BaseIntervalRegressionMetricConfig(BaseMetricConfig): - intervals = BaseField(optional=True) - start = NumberField(optional=True) - end = NumberField(optional=True) - step = NumberField(optional=True) - ignore_values_not_in_interval = BoolField(optional=True) - - -class BaseRegressionMetric(PerImageEvaluationMetric): - annotation_types = (RegressionAnnotation, ) - prediction_types = (RegressionPrediction, ) - - def __init__(self, value_differ, *args, **kwargs): - super().__init__(*args, **kwargs) - self.value_differ = value_differ - - def configure(self): - self.meta.update({'names': ['mean', 'std'], 'scale': 1, 'postfix': ' ', 'calculate_mean': False}) - self.magnitude = [] - - def update(self, annotation, prediction): - self.magnitude.append(self.value_differ(annotation.value, prediction.value)) - - def evaluate(self, annotations, predictions): - return np.mean(self.magnitude), np.std(self.magnitude) - - -class BaseRegressionOnIntervals(PerImageEvaluationMetric): - annotation_types = (RegressionAnnotation, ) - prediction_types = (RegressionPrediction, ) - _config_validator_type = BaseIntervalRegressionMetricConfig - - def __init__(self, value_differ, *args, **kwargs): - super().__init__(*args, **kwargs) - self.value_differ = value_differ - - def configure(self): - self.meta.update({'scale': 1, 'postfix': ' ', 'calculate_mean': False}) - self.ignore_out_of_range = self.config.get('ignore_values_not_in_interval', True) - - self.intervals = self.config.get('intervals') - if not self.intervals: - stop = self.config.get('end') - if not stop: - raise ConfigError('intervals or start-step-end of interval should be specified for metric') - - start = self.config.get('start', 0.0) - step = self.config.get('step', 1.0) - self.intervals = np.arange(start, stop + step, step) - - if not isinstance(self.intervals, (list, np.ndarray)): - self.intervals = string_to_tuple(self.intervals) - - self.intervals = np.unique(self.intervals) - self.magnitude = [[] for _ in range(len(self.intervals) + 1)] - - self.meta['names'] = ([]) - if not self.ignore_out_of_range: - self.meta['names'] = (['mean: < ' + str(self.intervals[0]), 'std: < ' + str(self.intervals[0])]) - - for index in range(len(self.intervals) - 1): - self.meta['names'].append('mean: <= ' + str(self.intervals[index]) + ' < ' + str(self.intervals[index + 1])) - self.meta['names'].append('std: <= ' + str(self.intervals[index]) + ' < ' + str(self.intervals[index + 1])) - - if not self.ignore_out_of_range: - self.meta['names'].append('mean: > ' + str(self.intervals[-1])) - self.meta['names'].append('std: > ' + str(self.intervals[-1])) - - def update(self, annotation, prediction): - index = find_interval(annotation.value, self.intervals) - self.magnitude[index].append(self.value_differ(annotation.value, prediction.value)) - - def evaluate(self, annotations, predictions): - if self.ignore_out_of_range: - self.magnitude = self.magnitude[1:-1] - - result = [[np.mean(values), np.std(values)] if values else [np.nan, np.nan] for values in self.magnitude] - result, self.meta['names'] = finalize_metric_result(np.reshape(result, -1), self.meta['names']) - - if not result: - warnings.warn("No values in given interval") - result.append(0) - - return result - - -class MeanAbsoluteError(BaseRegressionMetric): - __provider__ = 'mae' - - def __init__(self, *args, **kwargs): - super().__init__(mae_differ, *args, **kwargs) - - -class MeanSquaredError(BaseRegressionMetric): - __provider__ = 'mse' - - def __init__(self, *args, **kwargs): - super().__init__(mse_differ, *args, **kwargs) - - -class RootMeanSquaredError(BaseRegressionMetric): - __provider__ = 'rmse' - - def __init__(self, *args, **kwargs): - super().__init__(mse_differ, *args, **kwargs) - - def evaluate(self, annotations, predictions): - return np.sqrt(np.mean(self.magnitude)), np.sqrt(np.std(self.magnitude)) - - -class MeanAbsoluteErrorOnInterval(BaseRegressionOnIntervals): - __provider__ = 'mae_on_interval' - - def __init__(self, *args, **kwargs): - super().__init__(mae_differ, *args, **kwargs) - - -class MeanSquaredErrorOnInterval(BaseRegressionOnIntervals): - __provider__ = 'mse_on_interval' - - def __init__(self, *args, **kwargs): - super().__init__(mse_differ, *args, **kwargs) - - -class RootMeanSquaredErrorOnInterval(BaseRegressionOnIntervals): - __provider__ = 'rmse_on_interval' - - def __init__(self, *args, **kwargs): - super().__init__(mse_differ, *args, **kwargs) - - def evaluate(self, annotations, predictions): - if self.ignore_out_of_range: - self.magnitude = self.magnitude[1:-1] - - result = [] - for values in self.magnitude: - error = [np.sqrt(np.mean(values)), np.sqrt(np.std(values))] if values else [np.nan, np.nan] - result.append(error) - - result, self.meta['names'] = finalize_metric_result(np.reshape(result, -1), self.meta['names']) - - if not result: - warnings.warn("No values in given interval") - result.append(0) - - return result - - -class FacialLandmarksPerPointNormedError(PerImageEvaluationMetric): - __provider__ = 'per_point_normed_error' - - annotation_types = (FacialLandmarksAnnotation, ) - prediction_types = (FacialLandmarksPrediction, ) - - def configure(self): - self.meta.update({'scale': 1, 'postfix': ' ', 'calculate_mean': True, 'data_format': '{:.4f}'}) - self.magnitude = [] - - def update(self, annotation, prediction): - result = point_regression_differ( - annotation.x_values, annotation.y_values, prediction.x_values, prediction.y_values - ) - result /= np.maximum(annotation.interocular_distance, np.finfo(np.float64).eps) - self.magnitude.append(result) - - def evaluate(self, annotations, predictions): - num_points = np.shape(self.magnitude)[1] - point_result_name_pattern = 'point_{}_normed_error' - self.meta['names'] = [point_result_name_pattern.format(point_id) for point_id in range(num_points)] - per_point_rmse = np.mean(self.magnitude, axis=1) - per_point_rmse, self.meta['names'] = finalize_metric_result(per_point_rmse, self.meta['names']) - - return per_point_rmse - - -class NormedErrorMetricConfig(BaseMetricConfig): - calculate_std = BoolField(optional=True) - percentile = NumberField(optional=True, floats=False, min_value=0, max_value=100) - - -class FacialLandmarksNormedError(PerImageEvaluationMetric): - __provider__ = 'normed_error' - - annotation_types = (FacialLandmarksAnnotation, ) - prediction_types = (FacialLandmarksPrediction, ) - _config_validator_type = NormedErrorMetricConfig - - def configure(self): - self.calculate_std = self.config.get('calculate_std', False) - self.percentile = self.config.get('percentile') - self.meta.update({ - 'scale': 1, - 'postfix': ' ', - 'calculate_mean': not self.calculate_std or not self.percentile, - 'data_format': '{:.4f}', - 'names': ['mean'] - }) - self.magnitude = [] - - def update(self, annotation, prediction): - per_point_result = point_regression_differ( - annotation.x_values, annotation.y_values, prediction.x_values, prediction.y_values - ) - avg_result = np.sum(per_point_result) / len(per_point_result) - avg_result /= np.maximum(annotation.interocular_distance, np.finfo(np.float64).eps) - self.magnitude.append(avg_result) - - def evaluate(self, annotations, predictions): - result = [np.mean(self.magnitude)] - - if self.calculate_std: - result.append(np.std(self.magnitude)) - self.meta['names'].append('std') - - if self.percentile: - sorted_magnitude = np.sort(self.magnitude) - index = len(self.magnitude) / 100 * self.percentile - result.append(sorted_magnitude[int(index)]) - self.meta['names'].append('{}th percentile'.format(self.percentile)) - - return result - - -def calculate_distance(x_coords, y_coords, selected_points): - first_point = [x_coords[selected_points[0]], y_coords[selected_points[0]]] - second_point = [x_coords[selected_points[1]], y_coords[selected_points[1]]] - return np.linalg.norm(np.subtract(first_point, second_point)) - - -def mae_differ(annotation_val, prediction_val): - return np.abs(annotation_val - prediction_val) - - -def mse_differ(annotation_val, prediction_val): - return (annotation_val - prediction_val)**2 - - -def find_interval(value, intervals): - for index, point in enumerate(intervals): - if value < point: - return index - - return len(intervals) - - -def point_regression_differ(annotation_val_x, annotation_val_y, prediction_val_x, prediction_val_y): - loss = np.subtract(list(zip(annotation_val_x, annotation_val_y)), list(zip(prediction_val_x, prediction_val_y))) - return np.linalg.norm(loss, 2, axis=1) - - -class PeakSignalToNoiseRatio(BaseRegressionMetric): - __provider__ = 'psnr' - - annotation_types = (SuperResolutionAnnotation, ) - prediction_types = (SuperResolutionPrediction, ) - - def __init__(self, *args, **kwargs): - super().__init__(self._psnr_differ, *args, **kwargs) - - def validate_config(self): - class _PSNRConfig(BaseMetricConfig): - scale_border = NumberField(optional=True, min_value=0) - color_order = StringField(optional=True, choices=['BGR', 'RGB']) - - config_validator = _PSNRConfig('psnr', on_extra_argument=_PSNRConfig.ERROR_ON_EXTRA_ARGUMENT) - config_validator.validate(self.config) - - def configure(self): - super().configure() - self.scale_border = self.config.get('scale_border', 4) - color_order = self.config.get('color_order', 'RGB') - channel_order = { - 'BGR': [2, 1, 0], - 'RGB': [0, 1, 2] - } - self.meta['postfix'] = 'Db' - self.channel_order = channel_order[color_order] - - def _psnr_differ(self, annotation_image, prediction_image): - prediction = np.asarray(prediction_image).astype(np.float) - ground_truth = np.asarray(annotation_image).astype(np.float) - - height, width = prediction.shape[:2] - prediction = prediction[ - self.scale_border:height - self.scale_border, - self.scale_border:width - self.scale_border - ] - ground_truth = ground_truth[ - self.scale_border:height - self.scale_border, - self.scale_border:width - self.scale_border - ] - image_difference = (prediction - ground_truth) / 255. # rgb color space - - r_channel_diff = image_difference[:, :, self.channel_order[0]] - g_channel_diff = image_difference[:, :, self.channel_order[1]] - b_channel_diff = image_difference[:, :, self.channel_order[2]] - - channels_diff = (r_channel_diff * 65.738 + g_channel_diff * 129.057 + b_channel_diff * 25.064) / 256 - - mse = np.mean(channels_diff ** 2) - if mse == 0: - return np.Infinity - - return -10 * math.log10(mse) - - -def angle_differ(gt_gaze_vector, predicted_gaze_vector): - return np.arccos( - gt_gaze_vector.dot(predicted_gaze_vector) / np.linalg.norm(gt_gaze_vector) - / np.linalg.norm(predicted_gaze_vector) - ) * 180 / np.pi - - -class AngleError(BaseRegressionMetric): - __provider__ = 'angle_error' - - annotation_types = (GazeVectorAnnotation, ) - prediction_types = (GazeVectorPrediction, ) - - def __init__(self, *args, **kwargs): - super().__init__(angle_differ, *args, **kwargs) diff --git a/tools/accuracy_checker/accuracy_checker/metrics/reid.py b/tools/accuracy_checker/accuracy_checker/metrics/reid.py deleted file mode 100644 index 37920f22587346..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/metrics/reid.py +++ /dev/null @@ -1,369 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from collections import defaultdict, namedtuple -from sklearn.metrics import auc, precision_recall_curve -# noinspection PyProtectedMember -from sklearn.metrics.base import _average_binary_score -import numpy as np - -from ..representation import ( - ReIdentificationClassificationAnnotation, - ReIdentificationAnnotation, - ReIdentificationPrediction -) -from ..config import BaseField, BoolField, NumberField -from .metric import BaseMetricConfig, FullDatasetEvaluationMetric - -PairDesc = namedtuple('PairDesc', 'image1 image2 same') - - -class CMCConfigValidator(BaseMetricConfig): - top_k = NumberField(floats=False, min_value=1, optional=True) - separate_camera_set = BoolField(optional=True) - single_gallery_shot = BoolField(optional=True) - first_match_break = BoolField(optional=True) - number_single_shot_repeats = NumberField(floats=False, optional=True) - - -class ReidMapConfig(BaseMetricConfig): - interpolated_auc = BoolField(optional=True) - - -class PWAccConfig(BaseMetricConfig): - min_score = BaseField(optional=True) - - -class PWAccSubsetConfig(BaseMetricConfig): - subset_number = NumberField(optional=True, min_value=1, floats=False) - - -class CMCScore(FullDatasetEvaluationMetric): - """ - Cumulative Matching Characteristics (CMC) score. - - Config: - annotation: reid annotation. - prediction: predicted embeddings. - top_k: number of k highest ranked samples to consider when matching. - separate_camera_set: should identities from the same camera view be filtered out. - single_gallery_shot: each identity has only one instance in the gallery. - number_single_shot_repeats: number of repeats for single_gallery_shot setting. - first_match_break: break on first matched gallery sample. - """ - - __provider__ = 'cmc' - - annotation_types = (ReIdentificationAnnotation, ) - prediction_types = (ReIdentificationPrediction, ) - _config_validator_type = CMCConfigValidator - - def configure(self): - self.top_k = self.config.get('top_k', 1) - self.separate_camera_set = self.config.get('separate_camera_set', False) - self.single_gallery_shot = self.config.get('single_gallery_shot', False) - self.first_match_break = self.config.get('first_match_break', True) - self.number_single_shot_repeats = self.config.get('number_single_shot_repeats', 10) - - def evaluate(self, annotations, predictions): - dist_matrix = distance_matrix(annotations, predictions) - gallery_cameras, gallery_pids, query_cameras, query_pids = get_gallery_query_pids(annotations) - - _cmc_score = eval_cmc( - dist_matrix, query_pids, gallery_pids, query_cameras, gallery_cameras, self.separate_camera_set, - self.single_gallery_shot, self.first_match_break, self.number_single_shot_repeats - ) - - return _cmc_score[self.top_k - 1] - - -class ReidMAP(FullDatasetEvaluationMetric): - """ - Mean Average Precision score. - - Config: - annotation: reid annotation. - prediction: predicted embeddings. - interpolated_auc: should area under precision recall curve be computed using trapezoidal rule or directly. - """ - - __provider__ = 'reid_map' - - annotation_types = (ReIdentificationAnnotation, ) - prediction_types = (ReIdentificationPrediction, ) - _config_validator_type = ReidMapConfig - - def configure(self): - self.interpolated_auc = self.config.get('interpolated_auc', True) - - def evaluate(self, annotations, predictions): - dist_matrix = distance_matrix(annotations, predictions) - gallery_cameras, gallery_pids, query_cameras, query_pids = get_gallery_query_pids(annotations) - - return eval_map( - dist_matrix, query_pids, gallery_pids, query_cameras, gallery_cameras, self.interpolated_auc - ) - - -class PairwiseAccuracy(FullDatasetEvaluationMetric): - __provider__ = 'pairwise_accuracy' - - annotation_types = (ReIdentificationClassificationAnnotation, ) - prediction_types = (ReIdentificationPrediction, ) - _config_validator_type = PWAccConfig - - def configure(self): - self.min_score = self.config.get('min_score', 'train_median') - - def evaluate(self, annotations, predictions): - embed_distances, pairs = get_embedding_distances(annotations, predictions) - - min_score = self.min_score - if min_score == 'train_median': - train_distances, _train_pairs = get_embedding_distances(annotations, predictions, train=True) - min_score = np.median(train_distances) - - embed_same_class = embed_distances < min_score - - accuracy = 0 - for i, pair in enumerate(pairs): - same_label = pair.same - out_same = embed_same_class[i] - - correct_prediction = same_label and out_same or (not same_label and not out_same) - - if correct_prediction: - accuracy += 1 - - return float(accuracy) / len(pairs) - - -class PairwiseAccuracySubsets(FullDatasetEvaluationMetric): - __provider__ = 'pairwise_accuracy_subsets' - - annotation_types = (ReIdentificationClassificationAnnotation, ) - prediction_types = (ReIdentificationPrediction, ) - _config_validator_type = PWAccSubsetConfig - - def configure(self): - self.subset_num = self.config.get('subset_number', 10) - self.accuracy_metric = PairwiseAccuracy(self.config, self.dataset) - - def evaluate(self, annotations, predictions): - subset_results = [] - first_images_annotations = list(filter( - lambda annotation: (len(annotation.negative_pairs) > 0 or len(annotation.positive_pairs) > 0), annotations - )) - - idx_subsets = self.make_subsets(self.subset_num, len(first_images_annotations)) - for subset in range(self.subset_num): - test_subset = self.get_subset(first_images_annotations, idx_subsets[subset]['test']) - test_subset = self.mark_subset(test_subset, False) - - train_subset = self.get_subset(first_images_annotations, idx_subsets[subset]['train']) - train_subset = self.mark_subset(train_subset) - - subset_result = self.accuracy_metric.evaluate(test_subset+train_subset, predictions) - subset_results.append(subset_result) - - return np.mean(subset_results) - - @staticmethod - def make_subsets(subset_num, dataset_size): - subsets = [] - if subset_num > dataset_size: - raise ValueError('It is impossible to divide dataset on more than number of annotations subsets.') - - for subset in range(subset_num): - lower_bnd = subset * dataset_size // subset_num - upper_bnd = (subset + 1) * dataset_size // subset_num - subset_test = [(lower_bnd, upper_bnd)] - - subset_train = [(0, lower_bnd), (upper_bnd, dataset_size)] - subsets.append({'test': subset_test, 'train': subset_train}) - - return subsets - - @staticmethod - def mark_subset(subset_annotations, train=True): - for annotation in subset_annotations: - annotation.metadata['train'] = train - - return subset_annotations - - @staticmethod - def get_subset(container, subset_bounds): - subset = [] - for bound in subset_bounds: - subset += container[bound[0]: bound[1]] - - return subset - - -def extract_embeddings(annotation, prediction, query): - return np.stack([pred.embedding for pred, ann in zip(prediction, annotation) if ann.query == query]) - - -def get_gallery_query_pids(annotation): - gallery_pids = np.asarray([ann.person_id for ann in annotation if not ann.query]) - query_pids = np.asarray([ann.person_id for ann in annotation if ann.query]) - gallery_cameras = np.asarray([ann.camera_id for ann in annotation if not ann.query]) - query_cameras = np.asarray([ann.camera_id for ann in annotation if ann.query]) - - return gallery_cameras, gallery_pids, query_cameras, query_pids - - -def distance_matrix(annotation, prediction): - gallery_embeddings = extract_embeddings(annotation, prediction, query=False) - query_embeddings = extract_embeddings(annotation, prediction, query=True) - - return 1. - np.matmul(gallery_embeddings, np.transpose(query_embeddings)).T - - -def unique_sample(ids_dict, num): - mask = np.zeros(num, dtype=np.bool) - for indices in ids_dict.values(): - mask[np.random.choice(indices)] = True - - return mask - - -def eval_map(distance_mat, query_ids, gallery_ids, query_cams, gallery_cams, interpolated_auc=False): - number_queries, _number_gallery = distance_mat.shape - # Sort and find correct matches - indices = np.argsort(distance_mat, axis=1) - matches = (gallery_ids[indices] == query_ids[:, np.newaxis]) # type: np.ndarray - - # Compute AP for each query - average_precisions = [] - for query in range(number_queries): - # Filter out the same id and same camera - valid = (gallery_ids[indices[query]] != query_ids[query]) | (gallery_cams[indices[query]] != query_cams[query]) - - y_true = matches[query, valid] - y_score = -distance_mat[query][indices[query]][valid] - if not np.any(y_true): - continue - - average_precisions.append(binary_average_precision(y_true, y_score, interpolated_auc=interpolated_auc)) - - if not average_precisions: - raise RuntimeError("No valid query") - - return np.mean(average_precisions) - - -def eval_cmc(distance_mat, query_ids, gallery_ids, query_cams, gallery_cams, separate_camera_set=False, - single_gallery_shot=False, first_match_break=False, number_single_shot_repeats=10, top_k=100): - number_queries, _number_gallery = distance_mat.shape - - if not single_gallery_shot: - number_single_shot_repeats = 1 - - # Sort and find correct matches - indices = np.argsort(distance_mat, axis=1) - matches = gallery_ids[indices] == query_ids[:, np.newaxis] # type: np.ndarray - - # Compute CMC for each query - ret = np.zeros(top_k) - num_valid_queries = 0 - for query in range(number_queries): - valid = get_valid_subset( - gallery_cams, gallery_ids, query, indices, query_cams, query_ids, separate_camera_set - ) # type: np.ndarray - - if not np.any(matches[query, valid]): - continue - - ids_dict = defaultdict(list) - if single_gallery_shot: - gallery_indexes = gallery_ids[indices[query][valid]] - for j, x in zip(np.where(valid)[0], gallery_indexes): - ids_dict[x].append(j) - - for _ in range(number_single_shot_repeats): - if single_gallery_shot: - # Randomly choose one instance for each id - # required for correct validation on CUHK datasets - # http://www.ee.cuhk.edu.hk/~xgwang/CUHK_identification.html - sampled = (valid & unique_sample(ids_dict, len(valid))) - index = np.nonzero(matches[query, sampled])[0] - else: - index = np.nonzero(matches[query, valid])[0] - - delta = 1. / (len(index) * number_single_shot_repeats) - for j, k in enumerate(index): - if k - j >= top_k: - break - if first_match_break: - ret[k - j] += 1 - break - ret[k - j] += delta - - num_valid_queries += 1 - - if num_valid_queries == 0: - raise RuntimeError("No valid query") - - return ret.cumsum() / num_valid_queries - - -def get_valid_subset(gallery_cams, gallery_ids, query_index, indices, query_cams, query_ids, separate_camera_set): - # Filter out the same id and same camera - valid = ( - (gallery_ids[indices[query_index]] != query_ids[query_index]) | - (gallery_cams[indices[query_index]] != query_cams[query_index]) - ) - if separate_camera_set: - # Filter out samples from same camera - valid &= (gallery_cams[indices[query_index]] != query_cams[query_index]) - - return valid - - -def get_embedding_distances(annotation, prediction, train=False): - image_indexes = {} - for i, pred in enumerate(prediction): - image_indexes[pred.identifier] = i - - pairs = [] - for image1 in annotation: - if train != image1.metadata.get("train", False): - continue - - for image2 in image1.positive_pairs: - pairs.append(PairDesc(image_indexes[image1.identifier], image_indexes[image2], True)) - for image2 in image1.negative_pairs: - pairs.append(PairDesc(image_indexes[image1.identifier], image_indexes[image2], False)) - - embed1 = np.asarray([prediction[idx].embedding for idx, _, _ in pairs]) - embed2 = np.asarray([prediction[idx].embedding for _, idx, _ in pairs]) - - return 0.5 * (1 - np.sum(embed1 * embed2, axis=1)), pairs - - -def binary_average_precision(y_true, y_score, interpolated_auc=True): - def _average_precision(y_true_, y_score_, sample_weight=None): - precision, recall, _ = precision_recall_curve(y_true_, y_score_, sample_weight) - if not interpolated_auc: - # Return the step function integral - # The following works because the last entry of precision is - # guaranteed to be 1, as returned by precision_recall_curve - return -1 * np.sum(np.diff(recall) * np.array(precision)[:-1]) - - return auc(recall, precision) - - return _average_binary_score(_average_precision, y_true, y_score, average="macro") diff --git a/tools/accuracy_checker/accuracy_checker/metrics/semantic_segmentation.py b/tools/accuracy_checker/accuracy_checker/metrics/semantic_segmentation.py deleted file mode 100644 index a6138ff886d1d1..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/metrics/semantic_segmentation.py +++ /dev/null @@ -1,134 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from ..config import BoolField -from ..representation import ( - SegmentationAnnotation, - SegmentationPrediction, - BrainTumorSegmentationAnnotation, - BrainTumorSegmentationPrediction -) -from .metric import PerImageEvaluationMetric, BaseMetricConfig -from ..utils import finalize_metric_result - - -class SegmentationMetricConfig(BaseMetricConfig): - use_argmax = BoolField(optional=True) - - -class SegmentationMetric(PerImageEvaluationMetric): - annotation_types = (SegmentationAnnotation, ) - prediction_types = (SegmentationPrediction, ) - _config_validator_type = SegmentationMetricConfig - - CONFUSION_MATRIX_KEY = 'segmentation_confusion_matrix' - - def evaluate(self, annotations, predictions): - raise NotImplementedError - - def configure(self): - self.use_argmax = self.config.get('use_argmax', True) - - def update(self, annotation, prediction): - n_classes = len(self.dataset.labels) - prediction_mask = np.argmax(prediction.mask, axis=0) if self.use_argmax else prediction.mask.astype('int64') - - def update_confusion_matrix(confusion_matrix): - label_true = annotation.mask.flatten() - label_pred = prediction_mask.flatten() - - mask = (label_true >= 0) & (label_true < n_classes) - hist = np.bincount(n_classes * label_true[mask].astype(int) + label_pred[mask], minlength=n_classes ** 2) - hist = hist.reshape(n_classes, n_classes) - confusion_matrix += hist - - return confusion_matrix - - self._update_state(update_confusion_matrix, self.CONFUSION_MATRIX_KEY, lambda: np.zeros((n_classes, n_classes))) - - -class SegmentationAccuracy(SegmentationMetric): - __provider__ = 'segmentation_accuracy' - - def evaluate(self, annotations, predictions): - confusion_matrix = self.state[self.CONFUSION_MATRIX_KEY] - return np.diag(confusion_matrix).sum() / confusion_matrix.sum() - - -class SegmentationIOU(SegmentationMetric): - __provider__ = 'mean_iou' - - def evaluate(self, annotations, predictions): - confusion_matrix = self.state[self.CONFUSION_MATRIX_KEY] - union = confusion_matrix.sum(axis=1) + confusion_matrix.sum(axis=0) - np.diag(confusion_matrix) - diagonal = np.diag(confusion_matrix) - iou = np.divide(diagonal, union, out=np.zeros_like(diagonal), where=union != 0) - - values, names = finalize_metric_result(iou, list(self.dataset.labels.values())) - self.meta['names'] = names - - return values - - -class SegmentationMeanAccuracy(SegmentationMetric): - __provider__ = 'mean_accuracy' - - def evaluate(self, annotations, predictions): - confusion_matrix = self.state[self.CONFUSION_MATRIX_KEY] - diagonal = np.diag(confusion_matrix) - per_class_count = confusion_matrix.sum(axis=1) - acc_cls = np.divide(diagonal, per_class_count, out=np.zeros_like(diagonal), where=per_class_count != 0) - - values, names = finalize_metric_result(acc_cls, list(self.dataset.labels.values())) - self.meta['names'] = names - - return values - - -class SegmentationFWAcc(SegmentationMetric): - __provider__ = 'frequency_weighted_accuracy' - - def evaluate(self, annotations, predictions): - confusion_matrix = self.state[self.CONFUSION_MATRIX_KEY] - - union = (confusion_matrix.sum(axis=1) + confusion_matrix.sum(axis=0) - np.diag(confusion_matrix)) - diagonal = np.diag(confusion_matrix) - iou = np.divide(diagonal, union, out=np.zeros_like(diagonal), where=union != 0) - freq = confusion_matrix.sum(axis=1) / confusion_matrix.sum() - - return (freq[freq > 0] * iou[freq > 0]).sum() - - -class SegmentationDSCAcc(PerImageEvaluationMetric): - __provider__ = 'dice' - annotation_types = (BrainTumorSegmentationAnnotation,) - prediction_types = (BrainTumorSegmentationPrediction,) - overall_metric = [] - - def update(self, annotation, prediction): - cnt = 0 - for prediction_mask, annotation_mask in zip(prediction.mask, annotation.mask): - annotation_mask = np.transpose(annotation_mask, (2, 0, 1)) - annotation_mask = np.expand_dims(annotation_mask, 0) - numerator = np.sum(prediction_mask * annotation_mask) * 2.0 + 1.0 - denominator = np.sum(annotation_mask) + np.sum(prediction_mask) + 1.0 - self.overall_metric.append(numerator / denominator) - cnt += 1 - - def evaluate(self, annotations, predictions): - return sum(self.overall_metric) / len(self.overall_metric) diff --git a/tools/accuracy_checker/accuracy_checker/metrics/text_detection.py b/tools/accuracy_checker/accuracy_checker/metrics/text_detection.py deleted file mode 100644 index fec5b3cb042da9..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/metrics/text_detection.py +++ /dev/null @@ -1,119 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np -from .metric import PerImageEvaluationMetric, BaseMetricConfig -from ..config import BoolField, NumberField -from ..representation import TextDetectionPrediction, TextDetectionAnnotation -from ..utils import polygon_from_points - - -def get_union(detection_polygon, annotation_polygon): - area_prediction = detection_polygon.area - area_annotation = annotation_polygon.area - return area_prediction + area_annotation - get_intersection_area(detection_polygon, annotation_polygon) - - -def get_intersection_over_union(detection_polygon, annotation_polygon): - union = get_union(detection_polygon, annotation_polygon) - intersection = get_intersection_area(detection_polygon, annotation_polygon) - return intersection / union if union != 0 else 0.0 - - -def get_intersection_area(detection_polygon, annotation_polygon): - return detection_polygon.intersection(annotation_polygon).area - - -class TextDetectionMetricConfig(BaseMetricConfig): - iou_constrain = NumberField(min_value=0, max_value=1, optional=True) - ignore_difficult = BoolField(optional=True) - area_precision_constrain = NumberField(min_value=0, max_value=1, optional=True) - - -class TextDetectionMetric(PerImageEvaluationMetric): - __provider__ = 'text_detection' - - annotation_types = (TextDetectionAnnotation, ) - prediction_types = (TextDetectionPrediction, ) - _config_validator_type = TextDetectionMetricConfig - - def configure(self): - self.iou_constrain = self.config.get('iou_constrain', 0.5) - self.area_precision_constrain = self.config.get('area_precision_constrain', 0.5) - self.ignore_difficult = self.config.get('ignore_difficult', False) - self.number_matched_detections = 0 - self.number_valid_annotations = 0 - self.number_valid_detections = 0 - - def update(self, annotation, prediction): - gt_polygons = list(map(polygon_from_points, annotation.points)) - prediction_polygons = list(map(polygon_from_points, prediction.points)) - num_gt = len(gt_polygons) - num_det = len(prediction_polygons) - gt_difficult_mask = np.full(num_gt, False) - prediction_difficult_mask = np.full(num_det, False) - num_det_matched = 0 - if self.ignore_difficult: - gt_difficult_inds = annotation.metadata.get('difficult_boxes', []) - prediction_difficult_inds = prediction.metadata.get('difficult_boxes', []) - gt_difficult_mask[gt_difficult_inds] = True - prediction_difficult_mask[prediction_difficult_inds] = True - for det_id, detection_polygon in enumerate(prediction_polygons): - for gt_difficult_id in gt_difficult_inds: - gt_difficult_polygon = gt_polygons[gt_difficult_id] - intersected_area = get_intersection_area(gt_difficult_polygon, detection_polygon) - pd_dimensions = detection_polygon.area - precision = 0 if pd_dimensions == 0 else intersected_area / pd_dimensions - - if precision >= self.area_precision_constrain: - prediction_difficult_mask[det_id] = True - - if num_gt > 0 and num_det > 0: - iou_matrix = np.empty((num_gt, num_det)) - gt_matched = np.zeros(num_gt, np.int8) - det_matched = np.zeros(num_det, np.int8) - - for gt_id, gt_polygon in enumerate(gt_polygons): - for pred_id, pred_polygon in enumerate(prediction_polygons): - iou_matrix[gt_id, pred_id] = get_intersection_over_union(pred_polygon, gt_polygon) - not_matched_before = gt_matched[gt_id] == 0 and det_matched[pred_id] == 0 - not_difficult = not gt_difficult_mask[gt_id] and not prediction_difficult_mask[pred_id] - if not_matched_before and not_difficult: - if iou_matrix[gt_id, pred_id] >= self.iou_constrain: - gt_matched[gt_id] = 1 - det_matched[pred_id] = 1 - num_det_matched += 1 - - num_ignored_gt = np.sum(gt_difficult_mask) - num_ignored_pred = np.sum(prediction_difficult_mask) - num_valid_gt = num_gt - num_ignored_gt - num_valid_pred = num_det - num_ignored_pred - - self.number_matched_detections += num_det_matched - self.number_valid_annotations += num_valid_gt - self.number_valid_detections += num_valid_pred - - def evaluate(self, annotations, predictions): - recall = ( - 0 if self.number_valid_annotations == 0 - else float(self.number_matched_detections) / self.number_valid_annotations - ) - precision = ( - 0 if self.number_valid_detections == 0 - else float(self.number_matched_detections) / self.number_valid_detections - ) - - return 0 if recall + precision == 0 else 2 * recall * precision / (recall + precision) diff --git a/tools/accuracy_checker/accuracy_checker/pipeline_connectors/__init__.py b/tools/accuracy_checker/accuracy_checker/pipeline_connectors/__init__.py deleted file mode 100644 index 1e22b658c2cc77..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/pipeline_connectors/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .connectors import Connection, StageConnectionDescription, create_connection_description - -__all__ = [ - 'Connection', - 'StageConnectionDescription', - 'create_connection_description' -] diff --git a/tools/accuracy_checker/accuracy_checker/pipeline_connectors/connectors.py b/tools/accuracy_checker/accuracy_checker/pipeline_connectors/connectors.py deleted file mode 100644 index 318a3b8fcc3fc8..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/pipeline_connectors/connectors.py +++ /dev/null @@ -1,69 +0,0 @@ -from collections import namedtuple -from ..dependency import ClassProvider -from ..data_readers import DataRepresentation - - -StageConnectionDescription = namedtuple('StageConnection', ['from_stage', 'to_stage', 'replace', 'connector']) - - -class Connection: - def __init__(self, stages, description: StageConnectionDescription): - from_stage = description.from_stage - if from_stage is None: - for stage_index, stage in enumerate(stages): - if stage == description.to_stage: - from_stage = list(stages.keys())[stage_index - 1] - self.from_stage_context = stages[from_stage].evaluation_context - self.to_stage_context = stages[description.to_stage].evaluation_context - self.replace_container = description.replace - if description.connector: - self.connector = BaseConnector.provide(description.connector) - self.replace_container = self.connector.replace_container - - def __call__(self, *args, **kwargs): - shared_data = ( - self.connector(self.from_stage_context) - if self.connector else getattr(self.from_stage_context, self.replace_container) - ) - setattr(self.to_stage_context, self.replace_container, shared_data) - - -class BaseConnector(ClassProvider): - __provider_type__ = 'connector' - - def connect(self, context): - raise NotImplementedError - - def __call__(self, context, *args, **kwargs): - return self.connect(context) - - -class PredictionToDataConnector(BaseConnector): - __provider__ = 'prediction_to_data' - - replace_container = 'data_batch' - - def connect(self, context): - batch_predictions = context.prediction_batch - batch_identifiers = context.identifiers_batch - data_batch = [] - for prediction_item, identifier in zip(batch_predictions, batch_identifiers): - prediction_key = list(prediction_item.keys())[0] - data_batch.append(DataRepresentation(prediction_item[prediction_key], identifier=identifier)) - - return data_batch - - -def create_connection_description(configuration, stage_name): - config = configuration - if not isinstance(configuration, list): - config = [configuration] - for config_item in config: - connector = config_item.get('connector') - if connector: - connected_stage = config_item.get('stage') - return StageConnectionDescription( - from_stage=connected_stage, to_stage=stage_name, replace=None, connector=connector - ) - - return None diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/README.md b/tools/accuracy_checker/accuracy_checker/postprocessor/README.md deleted file mode 100644 index 752276a7fa710d..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Postprocessors - -Postprocessor is function which processes prediction and/or annotation data after model infer and before metric calculation. For correct work postprocessors require specific representation format. -(e. g. clip boxes postprocessor expects detection annotation and detection prediction for processing). - -In case when you use complicated representation located in representation container, you can add options `annotation_source` and `prediction_source` in configuration file, -if you want process only specific representations, another way postprocessor will be used for all suitable representations. `annotation_source` and `prediction_source` should contain -comma separated list of annotation identifiers and output layer names respectively. - -Every postprocessor has parameters available for configuration. - -Accuracy Checker supports following set of postprocessors: - -* `cast_to_int` - casting detection bounding box coordinates given in floating point format to integer. Supported representations: `DetectionAnotation`, `DetectionPrediction`, `TextDetectionAnnotation`, `TextDetectionPrediction`. - * `round_policy` - method for rounding: `nearest`, `greater`, `lower`, `nearest_to_zero`. -* `clip_boxes` - clipping detection bounding box sizes. Supported representations: `DetectionAnotation`, `DetectionPrediction`. - * `dst_width` and `dst_height` - destination width and height for box clipping respectively. You can also use `size` instead in case when destination sizes are equal. - * `apply_to` - option which determines target boxes for processing (`annotation` for ground truth boxes and `prediction` for detection results, `all` for both). - * `bboxes_normalized` is flag which says that target bounding boxes are in normalized format. -* `correct_yolo_v2_boxes` - resizing detection prediction bbox coordinates using specific for Yolo v2 approach. Supported representations: `DetectionAnotation`, `DetectionPrediction`. - * `dst_width` and `dst_height` - destination width and height respectively. You can also use `size` instead in case when destination sizes are equal. -* `encode_segmentation_mask` - encoding segmentation label image as segmentation mask. Supported representations: `SegmentationAnotation`, `SegmentationPrediction`. -* `resize_prediction_boxes` - resizing normalized detection prediction boxes according to image size. Supported representations: `DetectionAnotation`, `DetectionPrediction`. -* `resize_segmentation_mask` - resizing segmentation mask. Supported representations: `SegmentationAnotation`, `SegmentationPrediction`. - * `dst_width` and `dst_height` - destination width and height for box clipping respectively. You can also use `size` instead in case when destination sizes are equal. - If any of these parameters are not specified, image size will be used as default. - * `apply_to` - determines target boxes for processing (`annotation` for ground truth boxes and `prediction` for detection results, `all` for both). -* `nms` - non-maximum suppression. Supported representations: `DetectionAnotation`, `DetectionPrediction`. - * `overlap` - overlap threshold for merging detections. -* `filter` - filtering data using different parameters. Supported representations: `DetectionAnotation`, `DetectionPrediction`. - * `apply_to` - determines target boxes for processing (`annotation` for ground truth boxes and `prediction` for detection results, `all` for both). - * `remove_filtered` - removing filtered data. Annotations support ignoring filtered data without removing as default, in other cases filtered data will be removed automatically. - * Supported parameters for filtering: `labels`, `min_confidence`, `height_range`, `width_range`, `is_empty`, `min_visibility`, `aspect_ratio`, `area_ratio`, `area_range`. - Filtering by `height_range`, `width_range` are also available for `TextDetectionAnnotation`, `TextDetectionPrediction`, `area_range` - for `PoseEstimationAnnotation`, `PoseEstimationPrediction` and `TextDetectionAnnotation`, `TextDetectionPrediction`. -* `normalize_landmarks_points` - normalizing ground truth landmarks points. Supported representations: `FacialLandmarksAnnotation`, `FacialLandmarksPrediction`. - * `use_annotation_rect` - allows to use size of rectangle saved in annotation metadata for point scaling instead source image size. -* `extend_segmentation_mask` - extending annotation segmentation mask to predicted mask size making border filled by specific value. Supported representations: `SegmentationAnotation`, `SegmentationPrediction`. - * `filling_label` - value for filling border (default 255). -* `zoom_segmentation_mask` - zooming segmentation mask. Supported representations: `SegmentationAnotation`, `SegmentationPrediction`. - * `zoom` - size for zoom operation. diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/__init__.py b/tools/accuracy_checker/accuracy_checker/postprocessor/__init__.py deleted file mode 100644 index c3a93bd93ea0c1..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/__init__.py +++ /dev/null @@ -1,69 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from .postprocessing_executor import PostprocessingExecutor - -from .filter import ( - FilterPostprocessor, - - FilterByHeightRange, - FilterByLabels, - FilterByMinConfidence, - FilterEmpty, - FilterByVisibility, - FilterByAspectRatio -) - -from .cast_to_int import CastToInt -from .clip_boxes import ClipBoxes -from .nms import NMS -from .resize_prediction_boxes import ResizePredictionBoxes -from .correct_yolo_v2_boxes import CorrectYoloV2Boxes -from .resize_segmentation_mask import ResizeSegmentationMask -from .encode_segmentation_mask import EncodeSegMask -from .normalize_landmarks_points import NormalizeLandmarksPoints -from .clip_points import ClipPoints -from .extend_segmentation_mask import ExtendSegmentationMask -from .zoom_segmentation_mask import ZoomSegMask -from .crop_segmentation_mask import CropSegmentationMask -from .clip_segmentation_mask import ClipSegmentationMask - -__all__ = [ - 'PostprocessingExecutor', - - 'FilterPostprocessor', - 'FilterByHeightRange', - 'FilterByLabels', - 'FilterByMinConfidence', - 'FilterEmpty', - 'FilterByVisibility', - 'FilterByAspectRatio', - - 'CastToInt', - 'ClipBoxes', - 'NMS', - 'ResizePredictionBoxes', - 'CorrectYoloV2Boxes', - - 'ResizeSegmentationMask', - 'EncodeSegMask', - 'ExtendSegmentationMask', - 'ZoomSegMask', - 'CropSegmentationMask', - 'ClipSegmentationMask', - - 'NormalizeLandmarksPoints' -] diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/cast_to_int.py b/tools/accuracy_checker/accuracy_checker/postprocessor/cast_to_int.py deleted file mode 100644 index 26468d428dd054..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/cast_to_int.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -from functools import singledispatch -from typing import Union -import numpy as np -from ..config import StringField -from ..representation import DetectionAnnotation, DetectionPrediction, TextDetectionPrediction, TextDetectionAnnotation -from .postprocessor import Postprocessor, BasePostprocessorConfig - -round_policies_func = { - 'nearest': np.rint, - 'nearest_to_zero': np.trunc, - 'lower': np.floor, - 'greater': np.ceil -} - - -class CastToIntConfigValidator(BasePostprocessorConfig): - round_policy = StringField(optional=True, choices=round_policies_func.keys()) - - -class CastToInt(Postprocessor): - __provider__ = 'cast_to_int' - annotation_types = (DetectionAnnotation, TextDetectionAnnotation) - prediction_types = (DetectionPrediction, TextDetectionPrediction) - _config_validator_type = CastToIntConfigValidator - - def configure(self): - self.round_func = round_policies_func[self.config.get('round_policy', 'nearest')] - - def process_image(self, annotation, prediction): - @singledispatch - def cast(entry): - pass - - @cast.register(Union[DetectionAnnotation, DetectionPrediction]) - def _(entry): - entry.x_mins = self.round_func(entry.x_mins) - entry.x_maxs = self.round_func(entry.x_maxs) - entry.y_mins = self.round_func(entry.y_mins) - entry.y_maxs = self.round_func(entry.y_maxs) - - @cast.register(Union[TextDetectionAnnotation, TextDetectionPrediction]) - def _(entry): - entry.points = self.round_func(entry.points) - - - for annotation_ in annotation: - cast(annotation_) - - for prediction_ in prediction: - cast(prediction_) - - return annotation, prediction diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/clip_boxes.py b/tools/accuracy_checker/accuracy_checker/postprocessor/clip_boxes.py deleted file mode 100644 index 0d3175055a2e99..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/clip_boxes.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from ..config import BoolField, NumberField -from ..representation import DetectionPrediction, DetectionAnnotation -from .postprocessor import PostprocessorWithSpecificTargets, PostprocessorWithTargetsConfigValidator - - -class ClipConfigValidator(PostprocessorWithTargetsConfigValidator): - dst_width = NumberField(floats=False, optional=True, min_value=1) - dst_height = NumberField(floats=False, optional=True, min_value=1) - size = NumberField(floats=False, optional=True, min_value=1) - boxes_normalized = BoolField(optional=True) - - -class ClipBoxes(PostprocessorWithSpecificTargets): - __provider__ = 'clip_boxes' - - annotation_types = (DetectionAnnotation, ) - prediction_types = (DetectionPrediction, ) - _config_validator_type = ClipConfigValidator - - def configure(self): - size = self.config.get('size') - self.dst_height = size or self.config.get('dst_height') - self.dst_width = size or self.config.get('dst_width') - self.boxes_normalized = self.config.get('boxes_normalized', False) - - def process_image(self, annotation, prediction): - target_height = self.dst_height or self.image_size[0] - target_width = self.dst_width or self.image_size[1] - - max_width = target_width if not self.boxes_normalized else 1 - max_height = target_height if not self.boxes_normalized else 1 - - for target in annotation: - self._clip_boxes(target, (0, max_width), (0, max_height)) - for target in prediction: - self._clip_boxes(target, (0, max_width), (0, max_height)) - - return annotation, prediction - - @staticmethod - def _clip_boxes(entry, width_range, height_range): - entry.x_mins = entry.x_mins.clip(width_range[0], width_range[1]) - entry.x_maxs = entry.x_maxs.clip(width_range[0], width_range[1]) - entry.y_mins = entry.y_mins.clip(height_range[0], height_range[1]) - entry.y_maxs = entry.y_maxs.clip(height_range[0], height_range[1]) - - return entry diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/clip_points.py b/tools/accuracy_checker/accuracy_checker/postprocessor/clip_points.py deleted file mode 100644 index fdef034c46cb28..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/clip_points.py +++ /dev/null @@ -1,63 +0,0 @@ -"""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np -from ..config import BoolField, NumberField -from ..representation import TextDetectionAnnotation, TextDetectionPrediction -from ..utils import get_size_from_config -from .postprocessor import PostprocessorWithSpecificTargets, PostprocessorWithTargetsConfigValidator - - -class ClipPointsConfigValidator(PostprocessorWithTargetsConfigValidator): - dst_width = NumberField(floats=False, optional=True, min_value=1) - dst_height = NumberField(floats=False, optional=True, min_value=1) - size = NumberField(floats=False, optional=True, min_value=1) - points_normalized = BoolField(optional=True) - - -class ClipPoints(PostprocessorWithSpecificTargets): - __provider__ = 'clip_points' - - annotation_types = (TextDetectionAnnotation, ) - prediction_types = (TextDetectionPrediction, ) - _config_validator_type = ClipPointsConfigValidator - - def configure(self): - self.dst_height, self.dst_width = get_size_from_config(self.config, allow_none=True) - self.points_normalized = self.config.get('points_normalized', False) - - def process_image(self, annotation, prediction): - target_width = self.dst_width or self.image_size[1] - 1 - target_height = self.dst_height or self.image_size[0] - 1 - - max_width = target_width if not self.points_normalized else 1 - max_height = target_height if not self.points_normalized else 1 - for target in annotation: - points = [] - for polygon in target.points: - polygon[:, 0] = np.clip(polygon[:, 0], 0, max_width) - polygon[:, 1] = np.clip(polygon[:, 1], 0, max_height) - points.append(polygon) - target.points = points - for target in prediction: - points = [] - for polygon in target.points: - polygon[:, 0] = np.clip(polygon[:, 0], 0, max_width) - polygon[:, 1] = np.clip(polygon[:, 1], 0, max_height) - points.append(polygon) - target.points = points - - return annotation, prediction diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/clip_segmentation_mask.py b/tools/accuracy_checker/accuracy_checker/postprocessor/clip_segmentation_mask.py deleted file mode 100644 index f5e097cb756c7a..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/clip_segmentation_mask.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -import numpy as np -from .postprocessor import PostprocessorWithSpecificTargets, PostprocessorWithTargetsConfigValidator -from ..representation import BrainTumorSegmentationAnnotation, BrainTumorSegmentationPrediction -from ..config import NumberField, ConfigError - - -class ClipMaskConfigValidator(PostprocessorWithTargetsConfigValidator): - min_value = NumberField(floats=False, min_value=0, optional=True) - max_value = NumberField(floats=False) - - -class ClipSegmentationMask(PostprocessorWithSpecificTargets): - __provider__ = 'clip_segmentation_mask' - - annotation_types = (BrainTumorSegmentationAnnotation, ) - prediction_types = (BrainTumorSegmentationPrediction, ) - _config_validator_type = ClipMaskConfigValidator - - def configure(self): - self.min_value = self.config.get('min_value', 0) - self.max_value = self.config['max_value'] - if self.max_value < self.min_value: - raise ConfigError('max_value should be greater than min_value') - - def process_image(self, annotation, prediction): - for target in annotation: - target.mask = np.clip(target.mask, a_min=self.min_value, a_max=self.max_value) - - for target in prediction: - target.mask = np.clip(target.mask, a_min=self.min_value, a_max=self.max_value) - - return annotation, prediction diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/correct_yolo_v2_boxes.py b/tools/accuracy_checker/accuracy_checker/postprocessor/correct_yolo_v2_boxes.py deleted file mode 100644 index 7ed247a66b2664..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/correct_yolo_v2_boxes.py +++ /dev/null @@ -1,71 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from ..config import NumberField -from .postprocessor import BasePostprocessorConfig, Postprocessor -from ..representation import DetectionPrediction, DetectionAnnotation -from ..utils import get_size_from_config - - -class CorrectYoloV2BoxesConfigValidator(BasePostprocessorConfig): - dst_width = NumberField(floats=False, optional=True, min_value=1) - dst_height = NumberField(floats=False, optional=True, min_value=1) - size = NumberField(floats=False, optional=True, min_value=1) - - -class CorrectYoloV2Boxes(Postprocessor): - __provider__ = 'correct_yolo_v2_boxes' - - prediction_types = (DetectionPrediction, ) - annotation_types = (DetectionAnnotation, ) - _config_validator_type = CorrectYoloV2BoxesConfigValidator - - def configure(self): - self.dst_height, self.dst_width = get_size_from_config(self.config) - - def process_image(self, annotation, prediction): - dst_h, dst_w = self.dst_height, self.dst_width - # postprocessor always expects lists of annotations and predictions for the same image - # we do not need to get image sizes in cycle, because they are equal - img_h, img_w, _ = self.image_size - - if (dst_w / img_w) < (dst_h / img_h): - new_w = dst_w - new_h = (img_h * dst_w) // img_w - else: - new_h = dst_h - new_w = (img_w * dst_h) // img_h - - for prediction_ in prediction: - coordinates = zip(prediction_.x_mins, prediction_.y_mins, prediction_.x_maxs, prediction_.y_maxs) - for i, (x0, y0, x1, y1) in enumerate(coordinates): - box = [(x0 + x1) / 2.0, (y0 + y1) / 2.0, x1 - x0, y1 - y0] - box[0] = (box[0] - (dst_w - new_w) / (2.0 * dst_w)) * (dst_w / new_w) - box[1] = (box[1] - (dst_h - new_h) / (2.0 * dst_h)) * (dst_h / new_h) - box[2] *= dst_w / new_w - box[3] *= dst_h / new_h - - box[0] *= img_w - box[1] *= img_h - box[2] *= img_w - box[3] *= img_h - - prediction_.x_mins[i] = box[0] - box[2] / 2.0 + 1 - prediction_.y_mins[i] = box[1] - box[3] / 2.0 + 1 - prediction_.x_maxs[i] = box[0] + box[2] / 2.0 + 1 - prediction_.y_maxs[i] = box[1] + box[3] / 2.0 + 1 - - return annotation, prediction diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/crop_segmentation_mask.py b/tools/accuracy_checker/accuracy_checker/postprocessor/crop_segmentation_mask.py deleted file mode 100644 index 9eb4341da20582..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/crop_segmentation_mask.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from .postprocessor import PostprocessorWithSpecificTargets, PostprocessorWithTargetsConfigValidator -from ..representation import BrainTumorSegmentationAnnotation, BrainTumorSegmentationPrediction -from ..config import NumberField -from ..preprocessor import Crop3D -from ..utils import get_size_3d_from_config - - -class CropMaskConfigValidator(PostprocessorWithTargetsConfigValidator): - size = NumberField(floats=False, min_value=1) - dst_width = NumberField(floats=False, optional=True, min_value=1) - dst_height = NumberField(floats=False, optional=True, min_value=1) - dst_volume = NumberField(floats=False, optional=True, min_value=1) - - -class CropSegmentationMask(PostprocessorWithSpecificTargets): - __provider__ = 'crop_segmentation_mask' - - annotation_types = (BrainTumorSegmentationAnnotation,) - prediction_types = (BrainTumorSegmentationPrediction,) - _config_validator_type = CropMaskConfigValidator - - def configure(self): - self.dst_height, self.dst_width, self.dst_volume = get_size_3d_from_config(self.config) - - def process_image(self, annotation, prediction): - for target in annotation: - target.mask = Crop3D.crop_center(target.mask, self.dst_height, self.dst_width, self.dst_volume) - - for target in prediction: - target.mask = Crop3D.crop_center(target.mask, self.dst_height, self.dst_width, self.dst_volume) - - return annotation, prediction diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/encode_segmentation_mask.py b/tools/accuracy_checker/accuracy_checker/postprocessor/encode_segmentation_mask.py deleted file mode 100644 index 736eb0e125eff8..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/encode_segmentation_mask.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from .postprocessor import Postprocessor -from ..representation import SegmentationAnnotation, SegmentationPrediction - - -class EncodeSegMask(Postprocessor): - """ - Encode segmentation label image as segmentation mask. - """ - - __provider__ = 'encode_segmentation_mask' - - annotation_types = (SegmentationAnnotation, ) - prediction_types = (SegmentationPrediction, ) - - def process_image(self, annotation, prediction): - segmentation_colors = self.meta.get("segmentation_colors") - - if not segmentation_colors: - raise ValueError("No 'segmentation_colors' in dataset metadata.") - - for annotation_ in annotation: - mask = annotation_.mask.astype(int) - encoded_mask = np.zeros((mask.shape[0], mask.shape[1]), dtype=np.int16) - for label, color in enumerate(segmentation_colors): - encoded_mask[np.where(np.all(mask == color, axis=-1))[:2]] = label - annotation_.mask = encoded_mask - - return annotation, prediction diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/extend_segmentation_mask.py b/tools/accuracy_checker/accuracy_checker/postprocessor/extend_segmentation_mask.py deleted file mode 100644 index d8dad9d5418daf..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/extend_segmentation_mask.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -import math -import cv2 - -from .postprocessor import Postprocessor, BasePostprocessorConfig -from ..representation import SegmentationAnnotation, SegmentationPrediction -from ..config import NumberField, ConfigError - - -class ExtendSegmentationMaskConfigValidator(BasePostprocessorConfig): - filling_label = NumberField(optional=True, floats=False) - - -class ExtendSegmentationMask(Postprocessor): - """ - Extend annotation segmentation mask to prediction size filling border with specific label. - """ - - __provider__ = 'extend_segmentation_mask' - - annotation_types = (SegmentationAnnotation, ) - prediction_types = (SegmentationPrediction, ) - _config_validator_type = ExtendSegmentationMaskConfigValidator - - def configure(self): - self.filling_label = self.config.get('filling_label', 255) - - def process_image(self, annotation, prediction): - for annotation_, prediction_ in zip(annotation, prediction): - annotation_mask = annotation_.mask - dst_height, dst_width = prediction_.mask.shape[-2:] - height, width = annotation_mask.shape[-2:] - if dst_width < width or dst_height < height: - raise ConfigError('size for extending should be not less current mask size') - pad = [] - pad.append(int(math.floor((dst_height - height) / 2.0))) - pad.append(int(math.floor((dst_width - width) / 2.0))) - pad.append(int(dst_height - height - pad[0])) - pad.append(int(dst_width - width - pad[1])) - - extended_mask = cv2.copyMakeBorder( - annotation_mask, pad[0], pad[2], pad[1], pad[3], cv2.BORDER_CONSTANT, value=self.filling_label - ) - annotation_.mask = extended_mask - - return annotation, prediction diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/filter.py b/tools/accuracy_checker/accuracy_checker/postprocessor/filter.py deleted file mode 100644 index f0122b27683a3e..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/filter.py +++ /dev/null @@ -1,316 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -from functools import singledispatch -from typing import Union -import numpy as np - -from ..config import BaseField, BoolField -from ..dependency import ClassProvider -from ..postprocessor.postprocessor import PostprocessorWithSpecificTargets, PostprocessorWithTargetsConfigValidator -from ..representation import (DetectionAnnotation, DetectionPrediction, TextDetectionAnnotation, - TextDetectionPrediction, PoseEstimationPrediction, PoseEstimationAnnotation) -from ..utils import in_interval, polygon_from_points, convert_to_range - - -class FilterConfig(PostprocessorWithTargetsConfigValidator): - remove_filtered = BoolField(optional=True) - - def __init__(self, config_uri, **kwargs): - super().__init__(config_uri, **kwargs) - for functor in BaseFilter.providers: - self.fields[functor] = BaseField(optional=True) - - -class FilterPostprocessor(PostprocessorWithSpecificTargets): - __provider__ = 'filter' - - annotation_types = (DetectionAnnotation, TextDetectionAnnotation) - prediction_types = (DetectionPrediction, TextDetectionPrediction) - _config_validator_type = FilterConfig - - def __init__(self, *args, **kwargs): - self._filters = [] - self.remove_filtered = False - super().__init__(*args, **kwargs) - - def configure(self): - config = self.config.copy() - config.pop('type') - self.remove_filtered = config.pop('remove_filtered', False) - config.pop('annotation_source', None) - config.pop('prediction_source', None) - config.pop('apply_to', None) - - for key, value in config.items(): - self._filters.append(BaseFilter.provide(key, value)) - - def process_image(self, annotation, prediction): - for functor in self._filters: - for target in annotation: - self._filter_entry_by(target, functor) - - for target in prediction: - self._filter_entry_by(target, functor) - - return annotation, prediction - - def _filter_entry_by(self, entry, functor): - ignored_key = 'difficult_boxes' - - if not self.remove_filtered and isinstance(entry, (DetectionAnnotation, DetectionPrediction, - TextDetectionAnnotation, TextDetectionPrediction, - PoseEstimationAnnotation, PoseEstimationPrediction)): - ignored = entry.metadata.setdefault(ignored_key, []) - ignored.extend(functor(entry)) - else: - entry.remove(functor(entry)) - - return entry - - -class BaseFilter(ClassProvider): - __provider_type__ = 'filter' - - def __init__(self, filter_arg): - self.filter_arg = filter_arg - - def __call__(self, entry): - return self.apply_filter(entry, self.filter_arg) - - def apply_filter(self, entry, filter_arg): - raise NotImplementedError - - -class FilterByLabels(BaseFilter): - __provider__ = 'labels' - - def apply_filter(self, entry, labels): - filtered = [] - for index, label in enumerate(entry.labels): - if label in labels: - filtered.append(index) - - return filtered - - -class FilterByMinConfidence(BaseFilter): - __provider__ = 'min_confidence' - - def apply_filter(self, entry, min_confidence): - filtered = [] - - if isinstance(entry, DetectionAnnotation): - return filtered - - for index, score in enumerate(entry.scores): - if score < min_confidence: - filtered.append(index) - - return filtered - - -class FilterByHeightRange(BaseFilter): - __provider__ = 'height_range' - - annotation_types = (DetectionAnnotation, TextDetectionAnnotation) - prediction_types = (DetectionPrediction, TextDetectionPrediction) - - def apply_filter(self, entry, height_range): - @singledispatch - def filtering(entry_value, height_range_): - return [] - - @filtering.register(Union[DetectionAnnotation, DetectionPrediction]) - def _(entry_value, height_range_): - filtered = [] - for index, (y_min, y_max) in enumerate(zip(entry_value.y_mins, entry_value.y_maxs)): - height = y_max - y_min - if not in_interval(height, height_range_): - filtered.append(index) - - return filtered - - @filtering.register(Union[TextDetectionAnnotation, TextDetectionPrediction]) - def _(entry_values, height_range_): - filtered = [] - for index, polygon_points in enumerate(entry_values.points): - left_bottom_point, left_top_point, right_top_point, right_bottom_point = polygon_points - left_side_height = np.linalg.norm(left_bottom_point - left_top_point) - right_side_height = np.linalg.norm(right_bottom_point - right_top_point) - if not in_interval(np.mean([left_side_height, right_side_height]), height_range_): - filtered.append(index) - - return filtered - - return filtering(entry, convert_to_range(height_range)) - - -class FilterByWidthRange(BaseFilter): - __provider__ = 'width_range' - - annotation_types = (DetectionAnnotation, TextDetectionAnnotation) - prediction_types = (DetectionPrediction, TextDetectionPrediction) - - def apply_filter(self, entry, width_range): - @singledispatch - def filtering(entry_value, width_range_): - return [] - - @filtering.register(Union[DetectionAnnotation, DetectionPrediction]) - def _(entry_value, width_range_): - filtered = [] - for index, (x_min, x_max) in enumerate(zip(entry_value.x_mins, entry_value.x_maxs)): - width = x_max - x_min - if not in_interval(width, width_range_): - filtered.append(index) - - return filtered - - @filtering.register(Union[TextDetectionAnnotation, TextDetectionPrediction]) - def _(entry_values, width_range_): - filtered = [] - for index, polygon_points in enumerate(entry_values.points): - left_bottom_point, left_top_point, right_top_point, right_bottom_point = polygon_points - top_width = np.linalg.norm(right_top_point - left_top_point) - bottom_width = np.linalg.norm(right_bottom_point - left_bottom_point) - if not in_interval(top_width, width_range_) or not in_interval(bottom_width, width_range_): - filtered.append(index) - - return filtered - - return filtering(entry, convert_to_range(width_range)) - - -class FilterByAreaRange(BaseFilter): - __provider__ = 'area_range' - - annotation_types = (TextDetectionAnnotation, PoseEstimationAnnotation) - prediction_types = (TextDetectionPrediction, ) - - def apply_filter(self, entry, area_range): - area_range = convert_to_range(area_range) - - @singledispatch - def filtering(entry, area_range): - return [] - - @filtering.register - def _(entry: Union[PoseEstimationAnnotation, PoseEstimationPrediction], area_range): - filtered = [] - areas = entry.areas - for area_id, area in enumerate(areas): - if not in_interval(area, area_range): - filtered.append(area_id) - return filtered - - @filtering.register - def _(entry: Union[TextDetectionAnnotation, TextDetectionPrediction]): - filtered = [] - for index, polygon_points in enumerate(entry.points): - if not in_interval(polygon_from_points(polygon_points).area, area_range): - filtered.append(index) - return filtered - - return filtering(entry, area_range) - - -class FilterEmpty(BaseFilter): - __provider__ = 'is_empty' - - def apply_filter(self, entry: DetectionAnnotation, is_empty): - return np.where(np.bitwise_or(entry.x_maxs - entry.x_mins <= 0, entry.y_maxs - entry.y_mins <= 0))[0] - - -class FilterByVisibility(BaseFilter): - __provider__ = 'min_visibility' - - _VISIBILITY_LEVELS = { - 'heavy occluded': 0, - 'partially occluded': 1, - 'visible': 2 - } - - def apply_filter(self, entry, min_visibility): - filtered = [] - min_visibility_level = self.visibility_level(min_visibility) - for index, visibility in enumerate(entry.metadata.get('visibilities', [])): - if self.visibility_level(visibility) < min_visibility_level: - filtered.append(index) - - return filtered - - def visibility_level(self, visibility): - level = self._VISIBILITY_LEVELS.get(visibility) - if level is None: - message = 'Unknown visibility level "{}". Supported only "{}"' - raise ValueError(message.format(visibility, ','.join(self._VISIBILITY_LEVELS.keys()))) - - return level - - -class FilterByAspectRatio(BaseFilter): - __provider__ = 'aspect_ratio' - - def apply_filter(self, entry, aspect_ratio): - aspect_ratio = convert_to_range(aspect_ratio) - - filtered = [] - coordinates = zip(entry.x_mins, entry.y_mins, entry.x_maxs, entry.y_maxs) - for index, (x_min, y_min, x_max, y_max) in enumerate(coordinates): - ratio = (y_max - y_min) / np.maximum(x_max - x_min, np.finfo(np.float64).eps) - if not in_interval(ratio, aspect_ratio): - filtered.append(index) - - return filtered - - -class FilterByAreaRatio(BaseFilter): - __provider__ = 'area_ratio' - - def apply_filter(self, entry, area_ratio): - area_ratio = convert_to_range(area_ratio) - - filtered = [] - if not isinstance(entry, DetectionAnnotation): - return filtered - - image_size = entry.metadata.get('image_size') - if not image_size: - return filtered - image_size = image_size[0] - - image_area = image_size[0] * image_size[1] - - occluded_indices = entry.metadata.get('is_occluded', []) - coordinates = zip(entry.x_mins, entry.y_mins, entry.x_maxs, entry.y_maxs) - for index, (x_min, y_min, x_max, y_max) in enumerate(coordinates): - width, height = x_max - x_min, y_max - y_min - area = np.sqrt(float(width * height) / np.maximum(image_area, np.finfo(np.float64).eps)) - if not in_interval(area, area_ratio) or index in occluded_indices: - filtered.append(index) - - return filtered - - -class FilterInvalidBoxes(BaseFilter): - __provider__ = 'invalid_boxes' - - def apply_filter(self, entry, invalid_boxes): - infinite_mask_x = np.logical_or(~np.isfinite(entry.x_mins), ~np.isfinite(entry.x_maxs)) - infinite_mask_y = np.logical_or(~np.isfinite(entry.y_mins), ~np.isfinite(entry.y_maxs)) - infinite_mask = np.logical_or(infinite_mask_x, infinite_mask_y) - - return np.argwhere(infinite_mask).reshape(-1).tolist() diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/nms.py b/tools/accuracy_checker/accuracy_checker/postprocessor/nms.py deleted file mode 100644 index 3edc2e0c6171b5..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/nms.py +++ /dev/null @@ -1,85 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from ..config import BoolField, NumberField -from .postprocessor import BasePostprocessorConfig, Postprocessor -from ..representation import DetectionPrediction, DetectionAnnotation - - -class NMSConfigValidator(BasePostprocessorConfig): - overlap = NumberField(min_value=0, max_value=1, optional=True) - include_boundaries = BoolField(optional=True) - keep_top_k = NumberField(min_value=0, optional=True) - - -class NMS(Postprocessor): - __provider__ = 'nms' - - prediction_types = (DetectionPrediction, ) - annotation_types = (DetectionAnnotation, ) - _config_validator_type = NMSConfigValidator - - def configure(self): - self.overlap = self.config.get('overlap', 0.5) - self.include_boundaries = self.config.get('include_boundaries', True) - self.keep_top_k = self.config.get('keep_top_k') - - def process_image(self, annotations, predictions): - for prediction in predictions: - keep = self.nms( - prediction.x_mins, prediction.y_mins, prediction.x_maxs, prediction.y_maxs, prediction.scores, - self.overlap, self.include_boundaries, self.keep_top_k - ) - prediction.remove([box for box in range(len(prediction.x_mins)) if box not in keep]) - - return annotations, predictions - - @staticmethod - def nms(x1, y1, x2, y2, scores, thresh, include_boundaries=True, keep_top_k=None): - """ - Pure Python NMS baseline. - """ - - b = 1 if include_boundaries else 0 - - areas = (x2 - x1 + b) * (y2 - y1 + b) - order = scores.argsort()[::-1] - - if keep_top_k: - order = order[:keep_top_k] - - keep = [] - while order.size > 0: - i = order[0] - keep.append(i) - - xx1 = np.maximum(x1[i], x1[order[1:]]) - yy1 = np.maximum(y1[i], y1[order[1:]]) - xx2 = np.minimum(x2[i], x2[order[1:]]) - yy2 = np.minimum(y2[i], y2[order[1:]]) - - w = np.maximum(0.0, xx2 - xx1 + b) - h = np.maximum(0.0, yy2 - yy1 + b) - intersection = w * h - - union = (areas[i] + areas[order[1:]] - intersection) - overlap = np.divide(intersection, union, out=np.zeros_like(intersection, dtype=float), where=union != 0) - - order = order[np.where(overlap <= thresh)[0] + 1] - - return keep diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/normalize_landmarks_points.py b/tools/accuracy_checker/accuracy_checker/postprocessor/normalize_landmarks_points.py deleted file mode 100644 index 323fed37921021..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/normalize_landmarks_points.py +++ /dev/null @@ -1,55 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from ..config import BoolField -from ..postprocessor.postprocessor import Postprocessor, BasePostprocessorConfig -from ..representation import FacialLandmarksAnnotation, FacialLandmarksPrediction - - -class NormalizeConfigValidator(BasePostprocessorConfig): - use_annotation_rect = BoolField(optional=True) - - -class NormalizeLandmarksPoints(Postprocessor): - __provider__ = 'normalize_landmarks_points' - - annotation_types = (FacialLandmarksAnnotation, ) - prediction_types = (FacialLandmarksPrediction, ) - _config_validator_type = NormalizeConfigValidator - - def configure(self): - self.use_annotation_rect = self.config.get('use_annotation_rect', False) - - def process_image(self, annotation, prediction): - for target in annotation: - height, width, _ = self.image_size - x_start, y_start = 0, 0 - if self.use_annotation_rect: - resized_box = annotation[0].metadata.get('rect') - x_start, y_start, x_max, y_max = resized_box - width = x_max - x_start - height = y_max - y_start - - target.x_values = ( - (np.array(target.x_values, dtype=float) - x_start) / np.maximum(width, np.finfo(np.float64).eps) - ) - target.y_values = ( - (np.array(target.y_values, dtype=float) - y_start) / np.maximum(height, np.finfo(np.float64).eps) - ) - - return annotation, prediction diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/postprocessing_executor.py b/tools/accuracy_checker/accuracy_checker/postprocessor/postprocessing_executor.py deleted file mode 100644 index 29bf85437e6ff0..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/postprocessing_executor.py +++ /dev/null @@ -1,84 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from ..config import ConfigValidator, StringField -from ..utils import overrides, zipped_transform -from .postprocessor import Postprocessor - - -class PostprocessingExecutor: - def __init__(self, processors=None, dataset_name='custom', dataset_meta=None, state=None): - self._processors = [] - self._image_processors = [] - self._dataset_processors = [] - self.dataset_meta = dataset_meta - - self.state = state or {} - - if not processors: - return - - for config in processors: - postprocessor_config = PostprocessorConfig( - "{}.postprocessing".format(dataset_name), - on_extra_argument=ConfigValidator.IGNORE_ON_EXTRA_ARGUMENT - ) - postprocessor_config.validate(config) - postprocessor = Postprocessor.provide(config['type'], config, config['type'], self.dataset_meta, state) - self._processors.append(postprocessor) - - allow_image_postprocessor = True - for processor in self._processors: - if overrides(processor, 'process_all', Postprocessor): - allow_image_postprocessor = False - self._dataset_processors.append(processor) - else: - if allow_image_postprocessor: - self._image_processors.append(processor) - else: - self._dataset_processors.append(processor) - - def process_dataset(self, annotations, predictions): - for method in self._dataset_processors: - annotations, predictions = method.process_all(annotations, predictions) - - return annotations, predictions - - def process_image(self, annotation, prediction): - for method in self._image_processors: - annotation_entries, prediction_entries = method.get_entries(annotation, prediction) - method.process(annotation_entries, prediction_entries) - - return annotation, prediction - - def process_batch(self, annotations, predictions): - return zipped_transform(self.process_image, annotations, predictions) - - def full_process(self, annotations, predictions): - return self.process_dataset(*self.process_batch(annotations, predictions)) - - @property - def has_dataset_processors(self): - return len(self._dataset_processors) != 0 - - def __call__(self, context, *args, **kwargs): - batch_annotation = context.annotation_batch - batch_prediction = context.prediction_batch - context.batch_annotation, context.batch_prediction = self.process_batch(batch_annotation, batch_prediction) - - -class PostprocessorConfig(ConfigValidator): - type = StringField(choices=Postprocessor.providers) diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/postprocessor.py b/tools/accuracy_checker/accuracy_checker/postprocessor/postprocessor.py deleted file mode 100644 index 0e9fa73dbc2f1c..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/postprocessor.py +++ /dev/null @@ -1,184 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import warnings -from enum import Enum -from ..representation import ContainerRepresentation -from ..config import ConfigValidator, StringField, ConfigError, BaseField -from ..dependency import ClassProvider -from ..utils import ( - zipped_transform, - string_to_list, - check_representation_type, - get_supported_representations, - enum_values -) - - -class BasePostprocessorConfig(ConfigValidator): - type = StringField() - annotation_source = BaseField(optional=True) - prediction_source = BaseField(optional=True) - - -class Postprocessor(ClassProvider): - __provider_type__ = 'postprocessor' - - annotation_types = () - prediction_types = () - _config_validator_type = BasePostprocessorConfig - - def __init__(self, config, name=None, meta=None, state=None): - self.config = config - self.name = name - self.meta = meta - self.state = state - self.image_size = None - - self.annotation_source = self.config.get('annotation_source') - if self.annotation_source and not isinstance(self.annotation_source, list): - self.annotation_source = string_to_list(self.annotation_source) - - self.prediction_source = self.config.get('prediction_source') - if self.prediction_source and not isinstance(self.prediction_source, list): - self.prediction_source = string_to_list(self.prediction_source) - - self.validate_config() - self.setup() - - def __call__(self, *args, **kwargs): - return self.process_all(*args, **kwargs) - - def setup(self): - self.configure() - - def process_image(self, annotation, prediction): - raise NotImplementedError - - def process(self, annotation, prediction): - image_size = annotation[0].metadata.get('image_size') if not None in annotation else None - self.image_size = None - if image_size: - self.image_size = image_size[0] - self.process_image(annotation, prediction) - - return annotation, prediction - - def process_all(self, annotations, predictions): - zipped_transform(self.process, zipped_transform(self.get_entries, annotations, predictions)) - return annotations, predictions - - def configure(self): - pass - - def validate_config(self): - config_validator = self._config_validator_type( - self.name, on_extra_argument=BasePostprocessorConfig.ERROR_ON_EXTRA_ARGUMENT - ) - config_validator.validate(self.config) - - def get_entries(self, annotation, prediction): - message_not_found = '{}: {} is not found in container' - message_incorrect_type = "Incorrect type of {}. Postprocessor {} can work only with {}" - - def resolve_container(container, supported_types, entry_name, sources=None): - if not isinstance(container, ContainerRepresentation): - if sources: - message = 'Warning: {}_source can be applied only to container. Default value will be used' - warnings.warn(message.format(entry_name)) - - return [container] - - if not sources: - return get_supported_representations(container.values(), supported_types) - - entries = [] - for source in sources: - representation = container.get(source) - if not representation: - raise ConfigError(message_not_found.format(entry_name, source)) - - if supported_types and not check_representation_type(representation, supported_types): - raise TypeError(message_incorrect_type.format(entry_name, self.name, ','.join(supported_types))) - - entries.append(representation) - - return entries - - annotation_entries = resolve_container(annotation, self.annotation_types, 'annotation', self.annotation_source) - prediction_entries = resolve_container(prediction, self.prediction_types, 'prediction', self.prediction_source) - - return annotation_entries, prediction_entries - - -class ApplyToOption(Enum): - ANNOTATION = 'annotation' - PREDICTION = 'prediction' - ALL = 'all' - - -class PostprocessorWithTargetsConfigValidator(BasePostprocessorConfig): - apply_to = StringField(optional=True, choices=enum_values(ApplyToOption)) - - -class PostprocessorWithSpecificTargets(Postprocessor): - def setup(self): - apply_to = self.config.get('apply_to') - self.apply_to = ApplyToOption(apply_to) if apply_to else None - - if (self.annotation_source or self.prediction_source) and self.apply_to: - raise ConfigError("apply_to and sources both provided. You need specify only one from them") - - if not self.annotation_source and not self.prediction_source and not self.apply_to: - raise ConfigError("apply_to or annotation_source or prediction_source required for {}".format(self.name)) - - self.configure() - - def process(self, annotation, prediction): - image_size = annotation[0].metadata.get('image_size') if not None in annotation else None - self.image_size = None - if image_size: - self.image_size = image_size[0] - target_annotations, target_predictions = None, None - if self.annotation_source or self.prediction_source: - target_annotations, target_predictions = self._choose_targets_using_sources(annotation, prediction) - - if self.apply_to: - target_annotations, target_predictions = self._choose_targets_using_apply_to(annotation, prediction) - - if not target_annotations and not target_predictions: - raise ValueError("Suitable targets for {} not found".format(self.name)) - - self.process_image(target_annotations, target_predictions) - return annotation, prediction - - def _choose_targets_using_sources(self, annotations, predictions): - target_annotations = annotations if self.annotation_source else [] - target_predictions = predictions if self.prediction_source else [] - - return target_annotations, target_predictions - - def _choose_targets_using_apply_to(self, annotations, predictions): - targets_specification = { - ApplyToOption.ANNOTATION: (annotations, []), - ApplyToOption.PREDICTION: ([], predictions), - ApplyToOption.ALL: (annotations, predictions) - } - - return targets_specification[self.apply_to] - - def process_image(self, annotation, prediction): - raise NotImplementedError diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/resize_prediction_boxes.py b/tools/accuracy_checker/accuracy_checker/postprocessor/resize_prediction_boxes.py deleted file mode 100644 index 2ce7b85aab3abd..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/resize_prediction_boxes.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from ..representation import DetectionPrediction, DetectionAnnotation -from ..postprocessor.postprocessor import Postprocessor - - -class ResizePredictionBoxes(Postprocessor): - """ - Resize normalized predicted bounding boxes coordinates (i.e. from [0, 1] range) to input image shape. - """ - - __provider__ = 'resize_prediction_boxes' - - prediction_types = (DetectionPrediction, ) - annotation_types = (DetectionAnnotation, ) - - def process_image(self, annotations, predictions): - h, w, _ = self.image_size - - for prediction in predictions: - prediction.x_mins *= w - prediction.x_maxs *= w - prediction.y_mins *= h - prediction.y_maxs *= h - - return annotations, predictions diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/resize_segmentation_mask.py b/tools/accuracy_checker/accuracy_checker/postprocessor/resize_segmentation_mask.py deleted file mode 100644 index 0369342b3aa0e7..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/resize_segmentation_mask.py +++ /dev/null @@ -1,70 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -from functools import singledispatch -import scipy.misc -import numpy as np - -from ..config import NumberField -from ..utils import get_size_from_config -from .postprocessor import PostprocessorWithSpecificTargets, PostprocessorWithTargetsConfigValidator -from ..representation import SegmentationPrediction, SegmentationAnnotation - - -class ResizeMaskConfigValidator(PostprocessorWithTargetsConfigValidator): - size = NumberField(floats=False, optional=True, min_value=1) - dst_width = NumberField(floats=False, optional=True, min_value=1) - dst_height = NumberField(floats=False, optional=True, min_value=1) - -class ResizeSegmentationMask(PostprocessorWithSpecificTargets): - __provider__ = 'resize_segmentation_mask' - - annotation_types = (SegmentationAnnotation, ) - prediction_types = (SegmentationPrediction, ) - _config_validator_type = ResizeMaskConfigValidator - - def configure(self): - self.dst_height, self.dst_width = get_size_from_config(self.config, allow_none=True) - - def process_image(self, annotation, prediction): - target_height = self.dst_height or self.image_size[0] - target_width = self.dst_width or self.image_size[1] - - @singledispatch - def resize_segmentation_mask(entry, height, width): - return entry - - @resize_segmentation_mask.register(SegmentationPrediction) - def _(entry, height, width): - entry_mask = [] - for class_mask in entry.mask: - resized_mask = scipy.misc.imresize(class_mask, (height, width), 'nearest') - entry_mask.append(resized_mask) - entry.mask = np.array(entry_mask) - - return entry - - @resize_segmentation_mask.register(SegmentationAnnotation) - def _(entry, height, width): - entry.mask = scipy.misc.imresize(entry.mask, (height, width), 'nearest') - return entry - - for target in annotation: - resize_segmentation_mask(target, target_height, target_width) - - for target in prediction: - resize_segmentation_mask(target, target_height, target_width) - - return annotation, prediction diff --git a/tools/accuracy_checker/accuracy_checker/postprocessor/zoom_segmentation_mask.py b/tools/accuracy_checker/accuracy_checker/postprocessor/zoom_segmentation_mask.py deleted file mode 100644 index d7d76f41ae21e0..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/postprocessor/zoom_segmentation_mask.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Copyright (c) 2018 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from .postprocessor import Postprocessor, BasePostprocessorConfig -from ..representation import SegmentationAnnotation, SegmentationPrediction -from ..config import NumberField - - -class ZoomSegMaskConfigValidator(BasePostprocessorConfig): - zoom = NumberField(floats=False, min_value=1) - - -class ZoomSegMask(Postprocessor): - """ - Zoom probabilities of segmentation prediction. - """ - - __provider__ = 'zoom_segmentation_mask' - - annotation_types = (SegmentationAnnotation, ) - prediction_types = (SegmentationPrediction, ) - _config_validator_type = ZoomSegMaskConfigValidator - - def configure(self): - self.zoom = self.config['zoom'] - - def process_image(self, annotation, prediction): - for annotation_, prediction_ in zip(annotation, prediction): - height, width = annotation_.mask.shape[:2] - prob = prediction_.mask - zoom_prob = np.zeros((prob.shape[0], height, width), dtype=np.float32) - for c in range(prob.shape[0]): - for h in range(height): - for w in range(width): - r0 = h // self.zoom - r1 = r0 + 1 - c0 = w // self.zoom - c1 = c0 + 1 - rt = float(h) / self.zoom - r0 - ct = float(w) / self.zoom - c0 - v0 = rt * prob[c, r1, c0] + (1 - rt) * prob[c, r0, c0] - v1 = rt * prob[c, r1, c1] + (1 - rt) * prob[c, r0, c1] - zoom_prob[c, h, w] = (1 - ct) * v0 + ct * v1 - prediction_.mask = zoom_prob - - return annotation, prediction diff --git a/tools/accuracy_checker/accuracy_checker/preprocessor/README.md b/tools/accuracy_checker/accuracy_checker/preprocessor/README.md deleted file mode 100644 index 28fe73f55b85fe..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/preprocessor/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Preprocessors - -Preprocessor is function which processes input data before model inference. -Every preprocessor has parameters available for configuration. -Accuracy Checker supports following set of preprocessors: - -* `resize` - resizing the image to a new width and height. - * `dst_width` and `dst_height` are destination width and height for image resizing respectively. - You can also use `size` instead in case when destination sizes are equal for both dimensions. - * `use_pillow` parameter specifies usage of Pillow library for resizing. - Accuracy Checker uses OpenCV as default image reader. - * `interpolation` specifies method that will be used. - Possible values depend on image processing library: - * **OpenCV**: Nearest, Linear, Cubic, Area, Max, Lanczos4, Bits, Bits32 - * **Pillow**: None, Nearest, Cubic, Bicubic, Box, Bilinear, Lanczos, Antialias, Hamming - * `aspect_ratio_scale` allows save image aspect ratio using one of these ways: - - `width` - rescale width. - - `height` - rescale height. - - `greater` - rescale greater from image sizes. - - `fit_to_window` - adaptive resize for fit image into window with fixed size [dst_height x dst_width] - -* `normalization` - changing the range of pixel intensity values. - * `mean` values which will be subtracted from image channels. - You can specify one value for all channels or list of comma separated channel-wise values. - * `std` specifies values, on which pixels will be divided. - You can specify one value for all channels or list of comma separated channel-wise values. - - These parameters support work with precomputed values of frequently used datasets (e.g. `cifar10` or `imagenet`). - -* `bgr_to_rgb` - reversing image channels. Convert image in BGR format to RGB. -* `bgr_to_gray` - converting image in BGR to grayscale color space. -* `flip` - image mirroring around specified axis. - * `mode` specifies the axis for flipping (`vertical` or `horizontal`). -* `crop` - central cropping for image. - * `dst_width` and `dst_height` are destination width and height for image resizing respectively. You can also use `size` instead in case when destination sizes are equal. - * `use_pillow` parameter specifies usage of Pillow library for cropping. -* `crop_rectangle` - cropping region of interest using coordinates given as annotation metadata. -* `extend_around_rect` - scaling region of interest using annotation metadata. - * `augmentation_param` is scale factor for augmentation. -* `point_aligment` - aligning keypoints stored in annotation metadata. - * `draw_points` - allows visualize points. - * `normalize` - allows to use normalization for keypoints. - * `dst_width` and `dst_height` are destination width and height for keypoints resizing respectively. You can also use `size` instead in case when destination sizes are equal. -* `padding` - padding for image. - * `stride` - stride for padding. - * `pad_value` - value for filling space around original image. - * `dst_width` and `dst_height` are destination width and height for padded image respectively. - You can also use `size` instead in case when destination sizes are equal for both dimensions. - * `pad_type` - padding space location. Supported: `center`, `left_top`, `right_bottom` (Default is `center`). - * `use_numpy` - allow to use numpy for padding instead default OpenCV. -* `tiling` - image tiling. - * `margin` - margin for tiled fragment of image. - * `dst_width` and `dst_height` are destination width and height of tiled fragment respectively. - You can also use `size` instead in case when destination sizes are equal for both dimensions. - diff --git a/tools/accuracy_checker/accuracy_checker/preprocessor/__init__.py b/tools/accuracy_checker/accuracy_checker/preprocessor/__init__.py deleted file mode 100644 index 3999b41333da76..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/preprocessor/__init__.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from .preprocessing_executor import PreprocessingExecutor -from .preprocessors import ( - Preprocessor, - - Resize, - Flip, - Normalize, - Crop, - BgrToRgb, - BgrToGray, - CropRect, - ExtendAroundRect, - PointAligner, - Tiling, - Crop3D, - Normalize3d -) - -__all__ = [ - 'PreprocessingExecutor', - - 'Preprocessor', - 'Resize', - 'Flip', - 'Normalize', - 'Crop', - 'BgrToRgb', - 'BgrToGray', - 'CropRect', - 'ExtendAroundRect', - 'PointAligner', - 'Tiling', - 'Crop3D', - 'Normalize3d' -] diff --git a/tools/accuracy_checker/accuracy_checker/preprocessor/preprocessing_executor.py b/tools/accuracy_checker/accuracy_checker/preprocessor/preprocessing_executor.py deleted file mode 100644 index 5f5b7401925700..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/preprocessor/preprocessing_executor.py +++ /dev/null @@ -1,57 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from ..config import ConfigValidator, StringField -from ..preprocessor.preprocessors import Preprocessor - - -class PreprocessingExecutor: - def __init__(self, processors=None, dataset_name='custom', dataset_meta=None): - self.processors = [] - self.dataset_meta = dataset_meta - - if not processors: - return - - identifier = 'type' - for processor in processors: - preprocessor_config = PreprocessorConfig( - "{}.preprocessors".format(dataset_name), on_extra_argument=ConfigValidator.IGNORE_ON_EXTRA_ARGUMENT - ) - - type_ = processor.get(identifier) - preprocessor_config.validate(processor, type_) - preprocessor = Preprocessor.provide(processor[identifier], config=processor, name=type_) - - self.processors.append(preprocessor) - - def __call__(self, context, *args, **kwargs): - batch_data = context.data_batch - batch_annotation = context.annotation_batch - context.data_batch = self.process(batch_data, batch_annotation) - - def process(self, images, batch_annotation=None): - for i, _ in enumerate(images): - for processor in self.processors: - images[i] = processor( - image=images[i], annotation_meta=batch_annotation[i].metadata if batch_annotation else None - ) - - return images - - -class PreprocessorConfig(ConfigValidator): - type = StringField(choices=Preprocessor.providers) diff --git a/tools/accuracy_checker/accuracy_checker/preprocessor/preprocessors.py b/tools/accuracy_checker/accuracy_checker/preprocessor/preprocessors.py deleted file mode 100644 index 675741eb486b62..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/preprocessor/preprocessors.py +++ /dev/null @@ -1,642 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -import math -import cv2 -import numpy as np -from PIL import Image - -from ..config import BaseField, BoolField, ConfigValidator, NumberField, StringField, ConfigError -from ..dependency import ClassProvider -from ..utils import get_size_from_config, get_or_parse_value, string_to_tuple, get_size_3d_from_config - - -class BasePreprocessorConfig(ConfigValidator): - type = StringField() - - -class Preprocessor(ClassProvider): - __provider_type__ = 'preprocessor' - _config_validator_type = BasePreprocessorConfig - - def __init__(self, config, name=None): - self.config = config - self.name = name - - self.validate_config() - self.configure() - - def __call__(self, *args, **kwargs): - return self.process(*args, **kwargs) - - def process(self, image, annotation_meta=None): - raise NotImplementedError - - def configure(self): - pass - - def validate_config(self): - self._config_validator_type( - self.name, on_extra_argument=self._config_validator_type.ERROR_ON_EXTRA_ARGUMENT - ).validate(self.config) - - -def scale_width(dst_width, dst_height, image_width, image_height,): - return int(dst_width * image_width / image_height), dst_height - - -def scale_height(dst_width, dst_height, image_width, image_height): - return dst_width, int(dst_height * image_height / image_width) - - -def scale_greater(dst_width, dst_height, image_width, image_height): - if image_height > image_width: - return scale_height(dst_width, dst_height, image_width, image_height) - return scale_width(dst_width, dst_height, image_width, image_height) - - -def scale_fit_to_window(dst_width, dst_height, image_width, image_height): - im_scale = min(dst_height / image_height, dst_width / image_width) - return int(im_scale * image_width), int(im_scale * image_height) - - -PILLOW_INTERPOLATION = { - 'NEAREST': Image.NEAREST, - 'NONE': Image.NONE, - 'BOX': Image.BOX, - 'BILINEAR': Image.BILINEAR, - 'LINEAR': Image.LINEAR, - 'HAMMING': Image.HAMMING, - 'BICUBIC': Image.BICUBIC, - 'CUBIC': Image.CUBIC, - 'LANCZOS': Image.LANCZOS, - 'ANTIALIAS': Image.ANTIALIAS, -} - -OPENCV_INTERPOLATION = { - 'NEAREST': cv2.INTER_NEAREST, - 'LINEAR': cv2.INTER_LINEAR, - 'CUBIC': cv2.INTER_CUBIC, - 'AREA': cv2.INTER_AREA, - 'MAX': cv2.INTER_MAX, - 'BITS': cv2.INTER_BITS, - 'BITS2': cv2.INTER_BITS2, - 'LANCZOS4': cv2.INTER_LANCZOS4, -} - -ASPECT_RATIO_SCALE = { - 'width': scale_width, - 'height': scale_height, - 'greater': scale_greater, - 'fit_to_window': scale_fit_to_window -} - -class ResizeConfigValidator(BasePreprocessorConfig): - size = NumberField(floats=False, optional=True, min_value=1) - dst_width = NumberField(floats=False, optional=True, min_value=1) - dst_height = NumberField(floats=False, optional=True, min_value=1) - aspect_ratio_scale = StringField(choices=ASPECT_RATIO_SCALE.keys(), optional=True) - interpolation = StringField( - choices=set(PILLOW_INTERPOLATION) | set(OPENCV_INTERPOLATION), optional=True - ) - use_pillow = BoolField(optional=True) - - -class Resize(Preprocessor): - __provider__ = 'resize' - _config_validator_type = ResizeConfigValidator - - def configure(self): - self.dst_height, self.dst_width = get_size_from_config(self.config) - self.use_pil = self.config.get('use_pillow', False) - - interpolation = self.config.get('interpolation', 'LINEAR') - - self.scaling_func = ASPECT_RATIO_SCALE.get(self.config.get('aspect_ratio_scale')) - - if self.use_pil and interpolation.upper() not in PILLOW_INTERPOLATION: - raise ValueError("Incorrect interpolation option: {} for resize preprocessing".format(interpolation)) - if not self.use_pil and interpolation.upper() not in OPENCV_INTERPOLATION: - raise ValueError("Incorrect interpolation option: {} for resize preprocessing".format(interpolation)) - - if self.use_pil: - self.interpolation = PILLOW_INTERPOLATION[interpolation] - else: - self.interpolation = OPENCV_INTERPOLATION[interpolation] - - def process(self, image, annotation_meta=None): - data = image.data - new_height, new_width = self.dst_height, self.dst_width - - def process_data(data, new_height, new_width, scale_func, use_pil, interpolation): - if scale_func: - image_h, image_w = data.shape[:2] - new_width, new_height = self.scaling_func(self.dst_width, self.dst_height, image_w, image_h) - - image.metadata['preferable_width'] = max(new_width, self.dst_width) - image.metadata['preferable_height'] = max(new_height, self.dst_height) - - if use_pil: - data = Image.fromarray(data) - data = data.resize((new_width, new_height), interpolation) - data = np.array(data) - return data - - data = cv2.resize(data, (new_width, new_height), interpolation=interpolation).astype(np.float32) - if len(data.shape) == 2: - data = np.expand_dims(data, axis=-1) - - return data - - image.data = ( - process_data(data, new_height, new_width, self.scaling_func, self.use_pil, self.interpolation) - if not isinstance(data, list) else [ - process_data(data_fragment, new_height, new_width, self.scaling_func, self.use_pil, self.interpolation) - for data_fragment in data - ] - ) - - return image - - -class NormalizeConfigValidator(BasePreprocessorConfig): - mean = BaseField(optional=True) - std = BaseField(optional=True) - -class Normalize(Preprocessor): - __provider__ = 'normalization' - - PRECOMPUTED_MEANS = { - 'imagenet': (104.00698793, 116.66876762, 122.67891434), - 'cifar10': (125.307, 122.961, 113.8575), - } - - PRECOMPUTED_STDS = { - 'imagenet': (104.00698793, 116.66876762, 122.67891434), - 'cifar10': (125.307, 122.961, 113.8575), - } - - _config_validator_type = NormalizeConfigValidator - - def configure(self): - self.mean = get_or_parse_value(self.config.get('mean'), Normalize.PRECOMPUTED_MEANS) - self.std = get_or_parse_value(self.config.get('std'), Normalize.PRECOMPUTED_STDS) - if not self.mean and not self.std: - raise ConfigError('mean or std value should be provided') - - if self.std and 0 in self.std: - raise ConfigError('std value should not contain 0') - - if self.mean and not (len(self.mean) == 3 or len(self.mean) == 1): - raise ConfigError('mean should be one value or comma-separated list channel-wise values') - - if self.std and not (len(self.std) == 3 or len(self.std) == 1): - raise ConfigError('std should be one value or comma-separated list channel-wise values') - - def process(self, image, annotation_meta=None): - def process_data(data, mean, std): - if self.mean: - data = data - mean - if self.std: - data = data / std - - return data - - image.data = process_data(image.data, self.mean, self.std) if not isinstance(image.data, list) else [ - process_data(data_fragment, self.mean, self.std) for data_fragment in image.data - ] - - return image - - -class BgrToRgb(Preprocessor): - __provider__ = 'bgr_to_rgb' - - def process(self, image, annotation_meta=None): - def process_data(data): - return cv2.cvtColor(data, cv2.COLOR_BGR2RGB) - image.data = process_data(image.data) if not isinstance(image.data, list) else [ - process_data(fragment) for fragment in image.data - ] - return image - - -class BgrToGray(Preprocessor): - __provider__ = 'bgr_to_gray' - - def process(self, image, annotation_meta=None): - image.data = np.expand_dims(cv2.cvtColor(image.data, cv2.COLOR_BGR2GRAY).astype(np.float32), -1) - return image - -FLIP_MODES = { - 'horizontal': 0, - 'vertical': 1 -} - - -class FlipConfigValidator(BasePreprocessorConfig): - mode = StringField(choices=FLIP_MODES.keys()) - - -class Flip(Preprocessor): - __provider__ = 'flip' - - _config_validator_type = FlipConfigValidator - - def configure(self): - mode = self.config.get('mode', 'horizontal') - if isinstance(mode, str): - self.mode = FLIP_MODES[mode] - - def process(self, image, annotation_meta=None): - image.data = cv2.flip(image.data, self.mode) - return image - - -class CropConfigValidator(BasePreprocessorConfig): - size = NumberField(floats=False, optional=True, min_value=1) - dst_width = NumberField(floats=False, optional=True, min_value=1) - dst_height = NumberField(floats=False, optional=True, min_value=1) - use_pillow = BoolField(optional=True) - - -class Crop(Preprocessor): - __provider__ = 'crop' - _config_validator_type = CropConfigValidator - - def configure(self): - self.use_pillow = self.config.get('use_pillow', False) - self.dst_height, self.dst_width = get_size_from_config(self.config) - - def process(self, image, annotation_meta=None): - data = image.data - - def process_data(data, dst_height, dst_width, use_pillow): - height, width = data.shape[:2] - if use_pillow: - i = int(round((height - self.dst_height) / 2.)) - j = int(round((width - self.dst_width) / 2.)) - croped_data = Image.fromarray(data).crop((j, i, j + self.dst_width, i + self.dst_height)) - data = np.array(croped_data) - return data - - if width < dst_width or height < dst_height: - resized = np.array([width, height]) - if resized[0] < dst_width: - resized = resized * dst_width / resized[0] - if resized[1] < dst_height: - resized = resized * dst_height / resized[1] - - data = cv2.resize(data, tuple(np.ceil(resized).astype(int))) - - height, width, _ = data.shape - start_height = (height - dst_height) // 2 - start_width = (width - dst_width) // 2 - - return data[start_height:start_height + dst_height, start_width:start_width + dst_width] - - image.data = process_data( - data, self.dst_height, self.dst_width, self.use_pillow - ) if not isinstance(data, list) else [ - process_data(fragment, self.dst_height, self.dst_width, self.use_pillow) for fragment in image.data - ] - return image - - -class CropRect(Preprocessor): - __provider__ = 'crop_rect' - - def process(self, image, annotation_meta=None): - rect = annotation_meta.get('rect') - if not rect: - return image - - rows, cols = image.data.shape[:2] - rect_x_min, rect_y_min, rect_x_max, rect_y_max = rect - start_width, start_height = max(0, rect_x_min), max(0, rect_y_min) - - width = min(start_width + (rect_x_max - rect_x_min), cols) - height = min(start_height + (rect_y_max - rect_y_min), rows) - - image.data = image.data[start_height:height, start_width:width] - return image - - -class ExtendAroundRectConfigValidator(BasePreprocessorConfig): - augmentation_param = NumberField(floats=True, optional=True) - - -class ExtendAroundRect(Preprocessor): - __provider__ = 'extend_around_rect' - _config_validator_type = ExtendAroundRectConfigValidator - - def configure(self): - self.augmentation_param = self.config.get('augmentation_param', 0) - - def process(self, image, annotation_meta=None): - rect = annotation_meta.get('rect') - rows, cols = image.data.shape[:2] - - rect_x_left, rect_y_top, rect_x_right, rect_y_bottom = rect or (0, 0, cols, rows) - rect_x_left = max(0, rect_x_left) - rect_y_top = max(0, rect_y_top) - rect_x_right = min(rect_x_right, cols) - rect_y_bottom = min(rect_y_bottom, rows) - - rect_w = rect_x_right - rect_x_left - rect_h = rect_y_bottom - rect_y_top - - width_extent = (rect_x_right - rect_x_left + 1) * self.augmentation_param - height_extent = (rect_y_bottom - rect_y_top + 1) * self.augmentation_param - rect_x_left = rect_x_left - width_extent - border_left = abs(min(0, rect_x_left)) - rect_x_left = int(max(0, rect_x_left)) - - rect_y_top = rect_y_top - height_extent - border_top = abs(min(0, rect_y_top)) - rect_y_top = int(max(0, rect_y_top)) - - rect_y_bottom += border_top - rect_y_bottom = int(rect_y_bottom + height_extent + 0.5) - border_bottom = abs(max(0, rect_y_bottom - rows)) - - rect_x_right += border_left - rect_x_right = int(rect_x_right + width_extent + 0.5) - border_right = abs(max(0, rect_x_right - cols)) - - image.data = cv2.copyMakeBorder( - image.data, int(border_top), int(border_bottom), int(border_left), int(border_right), cv2.BORDER_REPLICATE - ) - - rect = ( - int(rect_x_left), int(rect_y_top), - int(rect_x_left) + int(rect_w + width_extent * 2), int(rect_y_top) + int(rect_h + height_extent * 2) - ) - annotation_meta['rect'] = rect - - return image - - -class PointAlignerConfigValidator(BasePreprocessorConfig): - draw_points = BoolField(optional=True) - normalize = BoolField(optional=True) - size = NumberField(floats=False, optional=True, min_value=1) - dst_width = NumberField(floats=False, optional=True, min_value=1) - dst_height = NumberField(floats=False, optional=True, min_value=1) - -class PointAligner(Preprocessor): - __provider__ = 'point_alignment' - _config_validator_type = PointAlignerConfigValidator - - ref_landmarks = np.array([ - 30.2946 / 96, 51.6963 / 112, - 65.5318 / 96, 51.5014 / 112, - 48.0252 / 96, 71.7366 / 112, - 33.5493 / 96, 92.3655 / 112, - 62.7299 / 96, 92.2041 / 112 - ], dtype=np.float64).reshape(5, 2) - - def configure(self): - self.draw_points = self.config.get('draw_points', False) - self.normalize = self.config.get('normalize', True) - self.dst_height, self.dst_width = get_size_from_config(self.config) - - def process(self, image, annotation_meta=None): - keypoints = annotation_meta.get('keypoints') - image.data = self.align(image.data, keypoints) - return image - - def align(self, img, points): - if not points: - return img - - points_number = len(points) // 2 - points = np.array(points).reshape(points_number, 2) - - inp_shape = [1., 1.] - if self.normalize: - inp_shape = img.shape - - keypoints = points.copy().astype(np.float64) - keypoints[:, 0] *= (float(self.dst_width) / inp_shape[1]) - keypoints[:, 1] *= (float(self.dst_height) / inp_shape[0]) - - keypoints_ref = np.zeros((points_number, 2), dtype=np.float64) - keypoints_ref[:, 0] = self.ref_landmarks[:, 0] * self.dst_width - keypoints_ref[:, 1] = self.ref_landmarks[:, 1] * self.dst_height - - transformation_matrix = self.transformation_from_points(np.array(keypoints_ref), np.array(keypoints)) - img = cv2.resize(img, (self.dst_width, self.dst_height)) - if self.draw_points: - for point in keypoints: - cv2.circle(img, (int(point[0]), int(point[1])), 5, (255, 0, 0), -1) - - return cv2.warpAffine(img, transformation_matrix, (self.dst_width, self.dst_height), flags=cv2.WARP_INVERSE_MAP) - - @staticmethod - def transformation_from_points(points1, points2): - points1 = np.matrix(points1.astype(np.float64)) - points2 = np.matrix(points2.astype(np.float64)) - - c1 = np.mean(points1, axis=0) - c2 = np.mean(points2, axis=0) - points1 -= c1 - points2 -= c2 - s1 = np.std(points1) - s2 = np.std(points2) - points1 /= np.maximum(s1, np.finfo(np.float64).eps) - points2 /= np.maximum(s1, np.finfo(np.float64).eps) - points_std_ratio = s2 / np.maximum(s1, np.finfo(np.float64).eps) - - u, _, vt = np.linalg.svd(points1.T * points2) - r = (u * vt).T - - return np.hstack((points_std_ratio * r, c2.T - points_std_ratio * r * c1.T)) - - -def center_padding(dst_width, dst_height, width, height): - pad = [int(math.floor((dst_height - height) / 2.0)), int(math.floor((dst_width - width) / 2.0))] - pad.extend([dst_height - height - pad[0], dst_width - width - pad[1]]) - - return pad - - -def right_bottom_padding(dst_width, dst_height, width, height): - return [0, 0, dst_height - height, dst_width - width] - - -def left_top_padding(dst_width, dst_height, width, height): - return [dst_height - height, dst_width - width, 0, 0] - - -padding_func = { - 'center': center_padding, - 'left_top': left_top_padding, - 'right_bottom': right_bottom_padding -} - - -class PaddingConfigValidator(BasePreprocessorConfig): - stride = NumberField(floats=False, min_value=1, optional=True) - pad_value = StringField(optional=True) - size = NumberField(floats=False, optional=True, min_value=1) - dst_width = NumberField(floats=False, optional=True, min_value=1) - dst_height = NumberField(floats=False, optional=True, min_value=1) - pad_type = StringField(choices=padding_func.keys(), optional=True) - use_numpy = BoolField(optional=True) - - -class Padding(Preprocessor): - __provider__ = 'padding' - _config_validator_type = PaddingConfigValidator - - def configure(self): - self.stride = self.config.get('stride', 1) - pad_val = self.config.get('pad_value', '0,0,0') - if isinstance(pad_val, int): - self.pad_value = (pad_val, pad_val, pad_val) - if isinstance(pad_val, str): - self.pad_value = string_to_tuple(pad_val, int) - self.dst_height, self.dst_width = get_size_from_config(self.config, allow_none=True) - self.pad_func = padding_func[self.config.get('pad_type', 'center')] - self.use_numpy = self.config.get('use_numpy', False) - - def process(self, image, annotation_meta=None): - height, width, _ = image.data.shape - pref_height = self.dst_height or image.metadata.get('preferable_height', height) - pref_width = self.dst_width or image.metadata.get('preferable_width', width) - height = min(height, pref_height) - pref_height = math.ceil(pref_height / float(self.stride)) * self.stride - pref_width = max(pref_width, width) - pref_width = math.ceil(pref_width / float(self.stride)) * self.stride - pad = self.pad_func(pref_width, pref_height, width, height) - image.metadata['padding'] = pad - padding_realization_func = self._opencv_padding if not self.use_numpy else self._numpy_padding - image.data = padding_realization_func(image.data, pad) - - return image - - def _opencv_padding(self, image, pad): - return cv2.copyMakeBorder( - image, pad[0], pad[2], pad[1], pad[3], cv2.BORDER_CONSTANT, value=self.pad_value - ) - - def _numpy_padding(self, image, pad): - pad_values = ( - (self.pad_value[0], self.pad_value[0]), - (self.pad_value[1], self.pad_value[1]), - (self.pad_value[2], self.pad_value[2]) - ) - return np.pad( - image, ((pad[0], pad[2]), (pad[1], pad[3]), (0, 0)), - mode='constant', constant_values=pad_values - ) - - -class TillingConfigValidator(BasePreprocessorConfig): - margin = NumberField(floats=False, min_value=1) - size = NumberField(floats=False, optional=True, min_value=1) - dst_width = NumberField(floats=False, optional=True, min_value=1) - dst_height = NumberField(floats=False, optional=True, min_value=1) - - -class Tiling(Preprocessor): - __provider__ = 'tiling' - _config_validator_type = TillingConfigValidator - - def configure(self): - self.dst_height, self.dst_width = get_size_from_config(self.config) - self.margin = self.config['margin'] - - def process(self, image, annotation_meta=None): - data = image.data - image_size = data.shape - output_height = self.dst_height - 2 * self.margin - output_width = self.dst_width - 2 * self.margin - data = cv2.copyMakeBorder(data, *np.full(4, self.margin), cv2.BORDER_REFLECT_101) - num_tiles_h = image_size[0] // output_height + (1 if image_size[0] % output_height else 0) - num_tiles_w = image_size[1] // output_width + (1 if image_size[1] % output_width else 0) - tiled_data = [] - for height in range(num_tiles_h): - for width in range(num_tiles_w): - offset = [output_height * height, output_width * width] - tile = data[offset[0]:offset[0] + self.dst_height, offset[1]:offset[1] + self.dst_width, :] - margin = [0, self.dst_height - tile.shape[0], 0, self.dst_width - tile.shape[1]] - tile = cv2.copyMakeBorder(tile, *margin, cv2.BORDER_REFLECT_101) - tiled_data.append(tile) - image.data = tiled_data - image.metadata['tiles_shape'] = (num_tiles_h, num_tiles_w) - image.metadata['multi_infer'] = True - - return image - - -class Crop3DConfigValidator(BasePreprocessorConfig): - size = NumberField(floats=False, min_value=1) - dst_width = NumberField(floats=False, optional=True, min_value=1) - dst_height = NumberField(floats=False, optional=True, min_value=1) - dst_volume = NumberField(floats=False, optional=True, min_value=1) - - -class Crop3D(Preprocessor): - __provider__ = 'crop3d' - _config_validator_type = Crop3DConfigValidator - - def configure(self): - self.dst_height, self.dst_width, self.dst_volume = get_size_3d_from_config(self.config) - - def process(self, image, annotation_meta=None): - image.data = self.crop_center(image.data, self.dst_height, self.dst_width, self.dst_volume) - return image - - @staticmethod - def crop_center(img, cropx, cropy, cropz): - - z, y, x, _ = img.shape - - # Make sure starting index is >= 0 - startx = max(x // 2 - (cropx // 2), 0) - starty = max(y // 2 - (cropy // 2), 0) - startz = max(z // 2 - (cropz // 2), 0) - - # Make sure ending index is <= size - endx = min(startx + cropx, x) - endy = min(starty + cropy, y) - endz = min(startz + cropz, z) - - return img[startz:endz, starty:endy, startx:endx, :] - - -class Normalize3d(Preprocessor): - __provider__ = "normalize3d" - - def process(self, image, annotation_meta=None): - data = self.normalize_img(image.data) - image_list = [] - for img in data: - image_list.append(img) - image.data = image_list - image.metadata['multi_infer'] = True - - return image - - @staticmethod - def normalize_img(img): - for channel in range(img.shape[3]): - channel_val = img[:, :, :, channel] - np.mean(img[:, :, :, channel]) - channel_val /= np.std(img[:, :, :, channel]) - img[:, :, :, channel] = channel_val - - return img diff --git a/tools/accuracy_checker/accuracy_checker/presenters.py b/tools/accuracy_checker/accuracy_checker/presenters.py deleted file mode 100644 index 33c1346e355190..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/presenters.py +++ /dev/null @@ -1,159 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from collections import namedtuple -from enum import Enum -import numpy as np - -from .dependency import ClassProvider -from .logging import print_info - -EvaluationResult = namedtuple( - 'EvaluationResult', [ - 'evaluated_value', 'reference_value', 'name', 'metric_type', 'threshold', 'meta' - ] -) - - -class Color(Enum): - PASSED = 0 - FAILED = 1 - - -def color_format(s, color=Color.PASSED): - if color == Color.PASSED: - return "\x1b[0;32m{}\x1b[0m".format(s) - return "\x1b[0;31m{}\x1b[0m".format(s) - - -class BasePresenter(ClassProvider): - __provider_type__ = "presenter" - - def write_result(self, evaluation_result, output_callback=None, ignore_results_formatting=False): - raise NotImplementedError - - -class ScalarPrintPresenter(BasePresenter): - __provider__ = "print_scalar" - - def write_result(self, evaluation_result: EvaluationResult, output_callback=None, ignore_results_formatting=False): - value, reference, name, _, threshold, meta = evaluation_result - value = np.mean(value) - postfix, scale, result_format = get_result_format_parameters(meta, ignore_results_formatting) - difference = None - if reference: - _, original_scale, _ = get_result_format_parameters(meta, False) - difference = compare_with_ref(reference, value, original_scale) - write_scalar_result( - value, name, threshold, difference, postfix=postfix, scale=scale, result_format=result_format - ) - - -class VectorPrintPresenter(BasePresenter): - __provider__ = "print_vector" - - def write_result(self, evaluation_result: EvaluationResult, output_callback=None, ignore_results_formatting=False): - value, reference, name, _, threshold, meta = evaluation_result - if threshold: - threshold = float(threshold) - - value_names = meta.get('names') - postfix, scale, result_format = get_result_format_parameters(meta, ignore_results_formatting) - if np.isscalar(value) or np.size(value) == 1: - if not np.isscalar(value): - value = value[0] - difference = None - if reference: - _, original_scale, _ = get_result_format_parameters(meta, False) - difference = compare_with_ref(reference, value, original_scale) - write_scalar_result( - value, name, threshold, difference, - value_name=value_names[0] if value_names else None, - postfix=postfix[0] if not np.isscalar(postfix) else postfix, - scale=scale[0] if not np.isscalar(scale) else scale, - result_format=result_format - ) - return - - for index, res in enumerate(value): - cur_postfix = '%' - if not np.isscalar(postfix): - if index < len(postfix): - cur_postfix = postfix[index] - else: - cur_postfix = postfix - write_scalar_result( - res, name, - value_name=value_names[index] if value_names else None, - postfix=cur_postfix, - scale=scale[index] if not np.isscalar(scale) else scale, - result_format=result_format - ) - - if len(value) > 1 and meta.get('calculate_mean', True): - mean_value = np.mean(np.multiply(value, scale)) - difference = None - if reference: - original_scale = get_result_format_parameters(meta, False)[1] if ignore_results_formatting else 1 - difference = compare_with_ref(reference, mean_value, original_scale) - write_scalar_result( - mean_value, name, threshold, difference, value_name='mean', - postfix=postfix[-1] if not np.isscalar(postfix) else postfix, scale=1, - result_format=result_format - ) - - -def write_scalar_result( - res_value, name, threshold=None, diff_with_ref=None, value_name=None, - postfix='%', scale=100, result_format='{:.2f}' -): - display_name = "{}@{}".format(name, value_name) if value_name else name - display_result = result_format.format(res_value * scale) - message = '{}: {}{}'.format(display_name, display_result, postfix) - - if diff_with_ref: - threshold = threshold or 0 - if threshold <= diff_with_ref: - fail_message = "[FAILED: error = {:.4}]".format(diff_with_ref) - message = "{} {}".format(message, color_format(fail_message, Color.FAILED)) - else: - message = "{} {}".format(message, color_format("[OK]", Color.PASSED)) - - print_info(message) - - -def compare_with_ref(reference, res_value, scale): - return abs(reference - (res_value * scale)) - - -class ReturnValuePresenter(BasePresenter): - __provider__ = "return_value" - - def write_result(self, evaluation_result: EvaluationResult, output_callback=None, ignore_results_formatting=False): - if output_callback: - output_callback(evaluation_result) - - -def get_result_format_parameters(meta, use_default_formatting): - postfix = ' ' - scale = 1 - result_format = '{}' - if not use_default_formatting: - postfix = meta.get('postfix', '%') - scale = meta.get('scale', 100) - result_format = meta.get('data_format', '{:.2f}') - - return postfix, scale, result_format diff --git a/tools/accuracy_checker/accuracy_checker/progress_reporters.py b/tools/accuracy_checker/accuracy_checker/progress_reporters.py deleted file mode 100644 index df1e04f57c6978..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/progress_reporters.py +++ /dev/null @@ -1,100 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import time - -from tqdm import tqdm - -from .dependency import ClassProvider -from .logging import print_info - - -class ProgressReporter(ClassProvider): - __provider_type__ = 'progress_reporter' - - def __init__(self, dataset_size=None): - self.finished = True - self.dataset_size = None - self.start_time = None - self.prev_time = None - if dataset_size is not None: - self.reset(dataset_size) - self.current = 0 - - def finish(self, objects_processed=True): - self.finished = True - if not objects_processed: - return - - process_time = time.time() - self.start_time - print_info('{} objects processed in {:.3f} seconds'.format(self.dataset_size, process_time)) - - @property - def progress(self): - return (self.current / self.dataset_size) * 100 if self.dataset_size else 0 - - def reset(self, dataset_size): - if not self.finished: - self.finish(objects_processed=False) - self.current = 0 - - self.dataset_size = dataset_size - self.start_time = time.time() - self.finished = False - - -class PrintProgressReporter(ProgressReporter): - __provider__ = 'print' - - def __init__(self, dataset_size=None, print_interval=1000): - super().__init__(dataset_size) - self.print_interval = print_interval - - def reset(self, dataset_size): - self.dataset_size = dataset_size - print_info('Total dataset size: {}'.format(dataset_size)) - self.start_time = time.time() - self.prev_time = self.start_time - - def update(self, batch_id, batch_size): - self.current += batch_size - if (batch_id + 1) % self.print_interval != 0: - return - - now = time.time() - batch_time = now - self.prev_time - self.prev_time = now - - print_info('{} / {} processed in {:.3f}s'.format((batch_id + 1) * batch_size, self.dataset_size, batch_time)) - - -class TQDMReporter(ProgressReporter): - __provider__ = 'bar' - - def update(self, _batch_id, batch_size): - self.current += batch_size - self.tqdm.update(batch_size) - - def finish(self, objects_processed=True): - self.tqdm.close() - super().finish(objects_processed) - - def reset(self, dataset_size): - super().reset(dataset_size) - self.tqdm = tqdm( - total=self.dataset_size, unit='frames', leave=False, - bar_format='{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]' - ) diff --git a/tools/accuracy_checker/accuracy_checker/representation/__init__.py b/tools/accuracy_checker/accuracy_checker/representation/__init__.py deleted file mode 100644 index 0ceabc30b966e9..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/representation/__init__.py +++ /dev/null @@ -1,103 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from .base_representation import BaseRepresentation -from .classification_representation import Classification, ClassificationAnnotation, ClassificationPrediction -from .detection_representation import Detection, DetectionAnnotation, DetectionPrediction -from .reid_representation import ( - ReIdentificationAnnotation, - ReIdentificationClassificationAnnotation, - ReIdentificationPrediction -) -from .segmentation_representation import ( - SegmentationRepresentation, - SegmentationAnnotation, - SegmentationPrediction, - BrainTumorSegmentationAnnotation, - BrainTumorSegmentationPrediction -) -from .character_recognition_representation import ( - CharacterRecognition, - CharacterRecognitionAnnotation, - CharacterRecognitionPrediction -) -from .representaton_container import ContainerRepresentation, ContainerAnnotation, ContainerPrediction -from .regression_representation import ( - RegressionAnnotation, - RegressionPrediction, - FacialLandmarksAnnotation, - FacialLandmarksPrediction, - GazeVectorAnnotation, - GazeVectorPrediction -) -from .multilabel_recognition import MultiLabelRecognitionAnnotation, MultiLabelRecognitionPrediction -from .super_resolution_representation import SuperResolutionAnnotation, SuperResolutionPrediction -from .text_detection_representation import TextDetectionAnnotation, TextDetectionPrediction -from .pose_estimation_representation import PoseEstimationAnnotation, PoseEstimationPrediction -from .hit_ratio_representation import HitRatio, HitRatioAnnotation, HitRatioPrediction - -__all__ = [ - 'BaseRepresentation', - - 'Classification', - 'ClassificationAnnotation', - 'ClassificationPrediction', - - 'Detection', - 'DetectionAnnotation', - 'DetectionPrediction', - - 'ReIdentificationAnnotation', - 'ReIdentificationClassificationAnnotation', - 'ReIdentificationPrediction', - - 'SegmentationRepresentation', - 'SegmentationAnnotation', - 'SegmentationPrediction', - 'BrainTumorSegmentationAnnotation', - 'BrainTumorSegmentationPrediction', - - 'CharacterRecognition', - 'CharacterRecognitionAnnotation', - 'CharacterRecognitionPrediction', - - 'ContainerRepresentation', - 'ContainerAnnotation', - 'ContainerPrediction', - - 'RegressionAnnotation', - 'RegressionPrediction', - 'FacialLandmarksAnnotation', - 'FacialLandmarksPrediction', - 'GazeVectorAnnotation', - 'GazeVectorPrediction', - - 'MultiLabelRecognitionAnnotation', - 'MultiLabelRecognitionPrediction', - - 'SuperResolutionAnnotation', - 'SuperResolutionPrediction', - - 'TextDetectionAnnotation', - 'TextDetectionPrediction', - - 'PoseEstimationAnnotation', - 'PoseEstimationPrediction', - - 'HitRatio', - 'HitRatioAnnotation', - 'HitRatioPrediction' -] diff --git a/tools/accuracy_checker/accuracy_checker/representation/base_representation.py b/tools/accuracy_checker/accuracy_checker/representation/base_representation.py deleted file mode 100644 index 05d53b57875065..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/representation/base_representation.py +++ /dev/null @@ -1,42 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import abc -import pickle - - -class BaseRepresentation(abc.ABC): - def __init__(self, identifier, metadata=None): - self.identifier = identifier - self.metadata = metadata or {} - - @classmethod - def load(cls, file): - obj = pickle.load(file) - - if cls != BaseRepresentation: - assert isinstance(obj, cls) - - return obj - - def dump(self, file): - pickle.dump(self, file) - - def set_image_size(self, image_sizes): - self.metadata['image_size'] = image_sizes - - def set_data_source(self, data_source): - self.metadata['data_source'] = data_source diff --git a/tools/accuracy_checker/accuracy_checker/representation/character_recognition_representation.py b/tools/accuracy_checker/accuracy_checker/representation/character_recognition_representation.py deleted file mode 100644 index df6a2418ad4a11..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/representation/character_recognition_representation.py +++ /dev/null @@ -1,31 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from .base_representation import BaseRepresentation - - -class CharacterRecognition(BaseRepresentation): - def __init__(self, identifier='', label=None): - super().__init__(identifier) - self.label = label - - -class CharacterRecognitionAnnotation(CharacterRecognition): - pass - - -class CharacterRecognitionPrediction(CharacterRecognition): - pass diff --git a/tools/accuracy_checker/accuracy_checker/representation/classification_representation.py b/tools/accuracy_checker/accuracy_checker/representation/classification_representation.py deleted file mode 100644 index 67f72f6824bb98..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/representation/classification_representation.py +++ /dev/null @@ -1,44 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from .base_representation import BaseRepresentation - - -class Classification(BaseRepresentation): - pass - - -class ClassificationAnnotation(Classification): - def __init__(self, identifier='', label=None): - super().__init__(identifier) - - self.label = label - - -class ClassificationPrediction(Classification): - def __init__(self, identifier='', scores=None): - super().__init__(identifier) - - self.scores = np.array(scores) if scores is not None else np.array([]) - - @property - def label(self): - return np.argmax(self.scores) - - def top_k(self, k): - return np.argpartition(self.scores, -k)[-k:] diff --git a/tools/accuracy_checker/accuracy_checker/representation/detection_representation.py b/tools/accuracy_checker/accuracy_checker/representation/detection_representation.py deleted file mode 100644 index 1fc2c8b8d8319f..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/representation/detection_representation.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from ..utils import remove_difficult -from .base_representation import BaseRepresentation - - -class Detection(BaseRepresentation): - def __init__(self, identifier='', labels=None, x_mins=None, y_mins=None, x_maxs=None, y_maxs=None, metadata=None): - super().__init__(identifier, metadata) - - self.labels = np.array(labels) if labels is not None else np.array([]) - self.x_mins = np.array(x_mins) if x_mins is not None else np.array([]) - self.y_mins = np.array(y_mins) if y_mins is not None else np.array([]) - self.x_maxs = np.array(x_maxs) if x_maxs is not None else np.array([]) - self.y_maxs = np.array(y_maxs) if y_maxs is not None else np.array([]) - - def remove(self, indexes): - self.labels = np.delete(self.labels, indexes) - self.x_mins = np.delete(self.x_mins, indexes) - self.y_mins = np.delete(self.y_mins, indexes) - self.x_maxs = np.delete(self.x_maxs, indexes) - self.y_maxs = np.delete(self.y_maxs, indexes) - - difficult_boxes = self.metadata.get('difficult_boxes') - if not difficult_boxes: - return - - new_difficult_boxes = remove_difficult(difficult_boxes, indexes) - - self.metadata['difficult_boxes'] = new_difficult_boxes - - @property - def size(self): - return len(self.x_mins) - - def __eq__(self, other): - if not isinstance(other, type(self)): - return False - - def are_bounding_boxes_equal(): - if not np.array_equal(self.labels, other.labels): - return False - if not np.array_equal(self.x_mins, other.x_mins): - return False - if not np.array_equal(self.y_mins, other.y_mins): - return False - if not np.array_equal(self.x_maxs, other.x_maxs): - return False - if not np.array_equal(self.y_maxs, other.y_maxs): - return False - return True - - return self.identifier == other.identifier and are_bounding_boxes_equal() and self.metadata == other.metadata - - -class DetectionAnnotation(Detection): - pass - - -class DetectionPrediction(Detection): - def __init__(self, identifier='', labels=None, scores=None, x_mins=None, y_mins=None, x_maxs=None, y_maxs=None, - metadata=None): - super().__init__(identifier, labels, x_mins, y_mins, x_maxs, y_maxs, metadata) - self.scores = np.array(scores) if scores is not None else np.array([]) - - def remove(self, indexes): - super().remove(indexes) - self.scores = np.delete(self.scores, indexes) - - def __eq__(self, other): - return np.array_equal(self.scores, other.scores) if super().__eq__(other) else False diff --git a/tools/accuracy_checker/accuracy_checker/representation/hit_ratio_representation.py b/tools/accuracy_checker/accuracy_checker/representation/hit_ratio_representation.py deleted file mode 100644 index f6cb6c7a18ab9f..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/representation/hit_ratio_representation.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np - -from .base_representation import BaseRepresentation - - -class HitRatio(BaseRepresentation): - def __init__(self, identifier=''): - super().__init__(identifier) - self.user = int(identifier[0].split('u:')[-1]) - self.item = int(identifier[1].split('i:')[-1]) - - - -class HitRatioAnnotation(HitRatio): - def __init__(self, identifier='', positive=True): - super().__init__(identifier) - self.positive = positive - - -class HitRatioPrediction(HitRatio): - def __init__(self, identifier='', scores=None): - super().__init__(identifier) - - self.scores = np.array(scores) if scores is not None else np.array([]) diff --git a/tools/accuracy_checker/accuracy_checker/representation/multilabel_recognition.py b/tools/accuracy_checker/accuracy_checker/representation/multilabel_recognition.py deleted file mode 100644 index d5af464a903a3b..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/representation/multilabel_recognition.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np -from .base_representation import BaseRepresentation - - -class MultiLabelRecognitionRepresentation(BaseRepresentation): - def __init__(self, identifier='', multi_label=None): - super().__init__(identifier) - self.multi_label = np.array(multi_label) if isinstance(multi_label, list) else multi_label - - -class MultiLabelRecognitionAnnotation(MultiLabelRecognitionRepresentation): - pass - - -class MultiLabelRecognitionPrediction(MultiLabelRecognitionRepresentation): - pass diff --git a/tools/accuracy_checker/accuracy_checker/representation/pose_estimation_representation.py b/tools/accuracy_checker/accuracy_checker/representation/pose_estimation_representation.py deleted file mode 100644 index f765dd8890d0c5..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/representation/pose_estimation_representation.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np -from .base_representation import BaseRepresentation - - -class PoseEstimationRepresentation(BaseRepresentation): - def __init__(self, identifier='', x_values=None, y_values=None, visibility=None, labels=None): - super().__init__(identifier) - self.x_values = x_values if np.size(x_values) > 0 else [] - self.y_values = y_values if np.size(y_values) > 0 else [] - self.visibility = visibility if np.size(visibility) > 0 else [2] * len(x_values) - self.labels = labels if labels is not None else np.array([1]*len(x_values)) - - @property - def areas(self): - areas = self.metadata.get('areas') - if areas: - return areas - x_mins = np.min(self.x_values, axis=1) - x_maxs = np.max(self.x_values, axis=1) - y_mins = np.min(self.y_values, axis=1) - y_maxs = np.max(self.y_values, axis=1) - return (x_maxs - x_mins) * (y_maxs - y_mins) - - @property - def bboxes(self): - rects = self.metadata.get('rects') - if rects: - return rects - x_mins = np.min(self.x_values, axis=1) - x_maxs = np.max(self.x_values, axis=1) - y_mins = np.min(self.y_values, axis=1) - y_maxs = np.max(self.y_values, axis=1) - return [[x_min, y_min, x_max, y_max] for x_min, y_min, x_max, y_max in zip(x_mins, y_mins, x_maxs, y_maxs)] - - @property - def size(self): - return len(self.x_values) - - -class PoseEstimationAnnotation(PoseEstimationRepresentation): - pass - - -class PoseEstimationPrediction(PoseEstimationRepresentation): - def __init__(self, identifier='', x_values=None, y_values=None, visibility=None, scores=None, labels=None): - super().__init__(identifier, x_values, y_values, visibility, labels) - self.scores = scores if scores.any() else [] diff --git a/tools/accuracy_checker/accuracy_checker/representation/regression_representation.py b/tools/accuracy_checker/accuracy_checker/representation/regression_representation.py deleted file mode 100644 index 99800d36233ec1..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/representation/regression_representation.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np -from .base_representation import BaseRepresentation - - -class RegressionRepresentation(BaseRepresentation): - def __init__(self, identifier='', value=None): - super().__init__(identifier) - self.value = value - - -class RegressionAnnotation(RegressionRepresentation): - pass - - -class RegressionPrediction(RegressionRepresentation): - pass - - -class GazeVectorRepresentation(RegressionRepresentation): - def __init__(self, identifier='', value=None): - if value is None: - value = np.array([]) - super().__init__(identifier, value) - -class GazeVectorAnnotation(GazeVectorRepresentation): - pass - -class GazeVectorPrediction(GazeVectorRepresentation): - pass - - - -class FacialLandmarksRepresentation(BaseRepresentation): - def __init__(self, identifier='', x_values=None, y_values=None): - super().__init__(identifier) - self.x_values = x_values if x_values.any() else [] - self.y_values = y_values if y_values.any() else [] - - -class FacialLandmarksAnnotation(FacialLandmarksRepresentation): - @property - def interocular_distance(self): - left_eye = [ - np.mean(self.x_values[self.metadata['left_eye']]), - np.mean(self.y_values[self.metadata['left_eye']]) - ] - right_eye = [ - np.mean(self.x_values[self.metadata['right_eye']]), - np.mean(self.y_values[self.metadata['right_eye']]) - ] - - return np.linalg.norm((np.subtract(left_eye, right_eye))) - - -class FacialLandmarksPrediction(FacialLandmarksRepresentation): - pass diff --git a/tools/accuracy_checker/accuracy_checker/representation/reid_representation.py b/tools/accuracy_checker/accuracy_checker/representation/reid_representation.py deleted file mode 100644 index d212eb747c116c..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/representation/reid_representation.py +++ /dev/null @@ -1,42 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from .base_representation import BaseRepresentation - - -class ReIdentification(BaseRepresentation): - pass - - -class ReIdentificationAnnotation(ReIdentification): - def __init__(self, identifier, camera_id, person_id, query): - super().__init__(identifier) - self.camera_id = camera_id - self.person_id = person_id - self.query = query - - -class ReIdentificationClassificationAnnotation(ReIdentification): - def __init__(self, identifier, positive_pairs=None, negative_pairs=None): - super().__init__(identifier) - self.positive_pairs = set(positive_pairs) - self.negative_pairs = set(negative_pairs) - - -class ReIdentificationPrediction(ReIdentification): - def __init__(self, identifiers, embedding): - super().__init__(identifiers) - self.embedding = embedding.copy() diff --git a/tools/accuracy_checker/accuracy_checker/representation/representaton_container.py b/tools/accuracy_checker/accuracy_checker/representation/representaton_container.py deleted file mode 100644 index add7c6991eaaa5..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/representation/representaton_container.py +++ /dev/null @@ -1,78 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np -from ..representation import BaseRepresentation - - -class ContainerRepresentation(BaseRepresentation): - def __init__(self, representation_map=None): - super().__init__('') - self.representations = representation_map or {} - - def __eq__(self, other): - if not isinstance(other, type(self)): - return False - - if self.identifier != other.identifier: - return False - - if self.metadata != other.metadata: - return False - - if self.representations != other.representations: - return False - - return True - - def __getitem__(self, item): - return self.representations[item] - - def get(self, key): - return self.representations.get(key) - - def values(self): - return list(self.representations.values()) - - @property - def identifier(self): - if self._identifier: - return self._identifier - - values = self.values() - if np.size(values) == 0: - raise ValueError('representation container is empty') - - self._identifier = values[0].identifier - return self._identifier - - @identifier.setter - def identifier(self, identifier): - self._identifier = identifier - - -class ContainerAnnotation(ContainerRepresentation): - def set_image_size(self, image_sizes): - for key in self.representations.keys(): - self.representations[key].metadata['image_size'] = image_sizes - - def set_data_source(self, data_source): - for key in self.representations.keys(): - self.representations[key].metadata['data_source'] = data_source - - -class ContainerPrediction(ContainerRepresentation): - pass diff --git a/tools/accuracy_checker/accuracy_checker/representation/segmentation_representation.py b/tools/accuracy_checker/accuracy_checker/representation/segmentation_representation.py deleted file mode 100644 index 45f6b014a0ac32..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/representation/segmentation_representation.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from enum import Enum - -import numpy as np - -from .base_representation import BaseRepresentation -from ..data_readers import BaseReader - - -class GTMaskLoader(Enum): - PILLOW = 0 - OPENCV = 1 - SCIPY = 2 - NIFTI = 3 - - -class SegmentationRepresentation(BaseRepresentation): - pass - - -class SegmentationAnnotation(SegmentationRepresentation): - LOADERS = { - GTMaskLoader.PILLOW: 'pillow_imread', - GTMaskLoader.OPENCV: 'opencv_imread', - GTMaskLoader.SCIPY: 'scipy_imread', - GTMaskLoader.NIFTI: 'nifti_reader' - } - - def __init__(self, identifier, path_to_mask, mask_loader=GTMaskLoader.PILLOW): - """ - Args: - identifier: object identifier (e.g. image name). - path_to_mask: path where segmentation mask should be loaded from. The path is relative to data source. - mask_loader: back-end, used to load segmentation masks. - """ - - super().__init__(identifier) - self._mask_path = path_to_mask - self._mask_loader = mask_loader - self._mask = None - - @property - def mask(self): - return self._mask if self._mask is not None else self._load_mask() - - @mask.setter - def mask(self, value): - self._mask = value - - def _load_mask(self): - if self._mask is None: - loader = BaseReader.provide(self.LOADERS.get(self._mask_loader), self.metadata['data_source']) - if self._mask_loader == GTMaskLoader.PILLOW: - loader.convert_to_rgb = False - mask = loader.read(self._mask_path) - return mask.astype(np.uint8) - - return self._mask - - -class SegmentationPrediction(SegmentationRepresentation): - def __init__(self, identifiers, mask): - """ - Args: - identifiers: object identifier (e.g. image name). - mask: array with shape (n_classes, height, width) of probabilities at each location. - """ - - super().__init__(identifiers) - self.mask = mask - - -class BrainTumorSegmentationAnnotation(SegmentationAnnotation): - def __init__(self, identifier, path_to_mask): - super().__init__(identifier, path_to_mask, GTMaskLoader.NIFTI) - - -class BrainTumorSegmentationPrediction(SegmentationPrediction): - pass diff --git a/tools/accuracy_checker/accuracy_checker/representation/super_resolution_representation.py b/tools/accuracy_checker/accuracy_checker/representation/super_resolution_representation.py deleted file mode 100644 index 7d2b660e859d3e..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/representation/super_resolution_representation.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from enum import Enum -import numpy as np - -from .base_representation import BaseRepresentation -from ..data_readers import BaseReader - - -class GTLoader(Enum): - PILLOW = 0 - OPENCV = 1 - - -class SuperResolutionRepresentation(BaseRepresentation): - pass - - -class SuperResolutionAnnotation(SuperResolutionRepresentation): - LOADERS = { - GTLoader.PILLOW: 'pillow_imread', - GTLoader.OPENCV: 'opencv_imread' - } - - def __init__(self, identifier, path_to_hr, gt_loader=GTLoader.PILLOW): - """ - Args: - identifier: object identifier (e.g. image name). - path_to_hr: path where height resolution image should be loaded from. The path is relative to data source. - gt_loader: back-end, used to load segmentation masks. - """ - - super().__init__(identifier) - self._image_path = path_to_hr - self._gt_loader = self.LOADERS.get(gt_loader) - - @property - def value(self): - loader = BaseReader.provide(self._gt_loader, self.metadata['data_source']) - gt = loader.read(self._image_path) - return gt.astype(np.uint8) - - -class SuperResolutionPrediction(SuperResolutionRepresentation): - def __init__(self, identifiers, prediction): - """ - Args: - identifiers: object identifier (e.g. image name). - prediction: array with shape (height, width) contained result image. - """ - - super().__init__(identifiers) - self.value = prediction diff --git a/tools/accuracy_checker/accuracy_checker/representation/text_detection_representation.py b/tools/accuracy_checker/accuracy_checker/representation/text_detection_representation.py deleted file mode 100644 index 38e7a9c60c844b..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/representation/text_detection_representation.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np -from ..utils import remove_difficult -from .base_representation import BaseRepresentation - - -class TextDetectionRepresentation(BaseRepresentation): - def __init__(self, identifier='', points=None): - super().__init__(identifier) - self.points = points or [] - - def remove(self, indexes): - self.points = np.delete(self.points, indexes, axis=0) - difficult = self.metadata.get('difficult_boxes') - if not difficult: - return - self.metadata['difficult_boxes'] = remove_difficult(difficult, indexes) - - -class TextDetectionAnnotation(TextDetectionRepresentation): - def __init__(self, identifier='', points=None, description=''): - super().__init__(identifier, points) - self.description = description - - def remove(self, indexes): - super().remove(indexes) - self.description = np.delete(self.description, indexes) - - -class TextDetectionPrediction(TextDetectionRepresentation): - pass diff --git a/tools/accuracy_checker/accuracy_checker/utils.py b/tools/accuracy_checker/accuracy_checker/utils.py deleted file mode 100644 index ca3b268fc3914f..00000000000000 --- a/tools/accuracy_checker/accuracy_checker/utils.py +++ /dev/null @@ -1,385 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import csv -import errno -import itertools -import json -import os -import pickle - -from pathlib import Path -from typing import Union -from warnings import warn - -from shapely.geometry.polygon import Polygon -import numpy as np -import yaml -import yamlloader - -try: - import lxml.etree as et -except ImportError: - import xml.etree.cElementTree as et - - -def concat_lists(*lists): - return list(itertools.chain(*lists)) - - -def get_path(entry: Union[str, Path], is_directory=False, check_exists=True): - try: - path = Path(entry) - except TypeError: - raise TypeError('"{}" is expected to be a path-like'.format(entry)) - - if not check_exists: - return path - - # pathlib.Path.exists throws an exception in case of broken symlink - if not os.path.exists(str(path)): - raise FileNotFoundError('{}: {}'.format(os.strerror(errno.ENOENT), path)) - - if is_directory and not path.is_dir(): - raise NotADirectoryError('{}: {}'.format(os.strerror(errno.ENOTDIR), path)) - - # if it exists it is either file (or valid symlink to file) or directory (or valid symlink to directory) - if not is_directory and not path.is_file(): - raise IsADirectoryError('{}: {}'.format(os.strerror(errno.EISDIR), path)) - - return path - - -def contains_all(container, *args): - sequence = set(container) - - for arg in args: - if len(sequence.intersection(arg)) != len(arg): - return False - - return True - - -def contains_any(container, *args): - sequence = set(container) - - for arg in args: - if sequence.intersection(arg): - return True - - return False - - -def string_to_tuple(string, casting_type=float): - processed = string.replace(' ', '') - processed = processed.replace('(', '') - processed = processed.replace(')', '') - processed = processed.split(',') - - return tuple([casting_type(entry) for entry in processed]) - - -def string_to_list(string): - processed = string.replace(' ', '') - processed = processed.replace('[', '') - processed = processed.replace(']', '') - processed = processed.split(',') - - return list(entry for entry in processed) - - -class JSONDecoderWithAutoConversion(json.JSONDecoder): - """ - Custom json decoder to convert all strings into numbers (int, float) during reading json file. - """ - - def decode(self, s, _w=json.decoder.WHITESPACE.match): - decoded = super().decode(s, _w) - return self._decode(decoded) - - def _decode(self, entry): - if isinstance(entry, str): - try: - return int(entry) - except ValueError: - pass - try: - return float(entry) - except ValueError: - pass - elif isinstance(entry, dict): - return {self._decode(key): self._decode(value) for key, value in entry.items()} - elif isinstance(entry, list): - return [self._decode(value) for value in entry] - - return entry - - -def dict_subset(dict_, key_subset): - return {key: value for key, value in dict_.items() if key in key_subset} - - -def zipped_transform(fn, *iterables, inplace=False): - result = (iterables if inplace else tuple([] for _ in range(len(iterables)))) - updater = (list.__setitem__ if inplace else lambda container, _, entry: container.append(entry)) - - for idx, values in enumerate(zip(*iterables)): - iter_res = fn(*values) - if not iter_res: - continue - - for dst, res in zip(result, iter_res): - updater(dst, idx, res) - - return result - - -def overrides(obj, attribute_name, base=None): - cls = obj if isinstance(obj, type) else obj.__class__ - - base = base or cls.__bases__[0] - obj_attr = getattr(cls, attribute_name, None) - base_attr = getattr(base, attribute_name, None) - - return obj_attr and obj_attr != base_attr - - -def enum_values(enum): - return [member.value for member in enum] - - -def get_size_from_config(config, allow_none=False): - if contains_all(config, ('size', 'dst_width', 'dst_height')): - warn('All parameters: size, dst_width, dst_height are provided. Size will be used. ' - 'You should specify only size or pair values des_width, dst_height in config.') - if 'size' in config: - return config['size'], config['size'] - if contains_all(config, ('dst_width', 'dst_height')): - return config['dst_height'], config['dst_width'] - if not allow_none: - raise ValueError('Either size or dst_width and dst_height required') - - return None, None - - -def get_size_3d_from_config(config, allow_none=False): - if contains_all(config, ('size', 'dst_width', 'dst_height', 'dst_volume')): - warn('All parameters: size, dst_width, dst_height, dst_volume are provided. Size will be used. ' - 'You should specify only size or three values des_width, dst_height, dst_volume in config.') - if 'size' in config: - return config['size'], config['size'], config['size'] - if contains_all(config, ('dst_width', 'dst_height', 'dst_volume')): - return config['dst_height'], config['dst_width'], config['dst_volume'] - if not allow_none: - raise ValueError('Either size or dst_width and dst_height required') - - return config.get('dst_height'), config.get('dst_width'), config.get('dst_volume') - - -def in_interval(value, interval): - minimum = interval[0] - maximum = interval[1] if len(interval) >= 2 else None - - if not maximum: - return minimum <= value - - return minimum <= value < maximum - - -def finalize_metric_result(values, names): - result_values, result_names = [], [] - for value, name in zip(values, names): - if np.isnan(value): - continue - - result_values.append(value) - result_names.append(name) - - return result_values, result_names - - -def get_representations(values, representation_source): - return np.reshape([value.get(representation_source) for value in values], -1) - - -def get_supported_representations(container, supported_types): - if np.shape(container) == (): - container = [container] - - return list(filter(lambda rep: check_representation_type(rep, supported_types), container)) - - -def check_representation_type(representation, representation_types): - for representation_type in representation_types: - if type(representation).__name__ == representation_type.__name__: - return True - return False - - -def is_single_metric_source(source): - if not source: - return False - - return np.size(source.split(',')) == 1 - - -def read_txt(file: Union[str, Path], sep='\n', **kwargs): - def is_empty(string): - return not string or string.isspace() - - with get_path(file).open() as content: - content = content.read(**kwargs).split(sep) - content = list(filter(lambda string: not is_empty(string), content)) - - return list(map(str.strip, content)) - - -def read_xml(file: Union[str, Path], *args, **kwargs): - return et.parse(str(get_path(file)), *args, **kwargs).getroot() - - -def read_json(file: Union[str, Path], *args, **kwargs): - with get_path(file).open() as content: - return json.load(content, *args, **kwargs) - - -def read_pickle(file: Union[str, Path], *args, **kwargs): - with get_path(file).open('rb') as content: - return pickle.load(content, *args, **kwargs) - - -def read_yaml(file: Union[str, Path], *args, **kwargs): - with get_path(file).open() as content: - return yaml.load(content, *args, Loader=yamlloader.ordereddict.Loader, **kwargs) - - -def read_csv(file: Union[str, Path], *args, **kwargs): - with get_path(file).open() as content: - return list(csv.DictReader(content, *args, **kwargs)) - - -def extract_image_representations(image_representations): - images = [rep.data for rep in image_representations] - meta = [rep.metadata for rep in image_representations] - - return images, meta - - -def convert_bboxes_xywh_to_x1y1x2y2(x_coord, y_coord, width, height): - return x_coord, y_coord, x_coord + width, y_coord + height - - -def get_or_parse_value(item, supported_values, default=None): - if isinstance(item, str): - item = item.lower() - if item in supported_values: - return supported_values[item] - - try: - return string_to_tuple(item) - except ValueError: - message = 'Invalid value "{}", expected one of precomputed: ({}) or list of values'.format( - item, ', '.join(supported_values.keys()) - ) - raise ValueError(message) - - if isinstance(item, (float, int)): - return (item, ) - - return default - - -def string_to_bool(string): - return string.lower() in ['yes', 'true', 't', '1'] - - -def get_key_by_value(container, target): - for key, value in container.items(): - if value == target: - return key - - return None - - -def format_key(key): - return '--{}'.format(key) - - -def to_lower_register(str_list): - return list(map(lambda item: item.lower() if item else None, str_list)) - - -def polygon_from_points(points): - return Polygon(points) - - -def remove_difficult(difficult, indexes): - new_difficult = [] - decrementor = 0 - id_difficult = 0 - id_removed = 0 - while id_difficult < len(difficult) and id_removed < len(indexes): - if difficult[id_difficult] < indexes[id_removed]: - new_difficult.append(difficult[id_difficult] - decrementor) - id_difficult += 1 - else: - decrementor += 1 - id_removed += 1 - - return new_difficult - - -def convert_to_range(entry): - entry_range = entry - if isinstance(entry, str): - entry_range = string_to_tuple(entry_range) - elif not isinstance(entry_range, tuple) and not isinstance(entry_range, list): - entry_range = [entry_range] - - return entry_range - - -def add_input_shape_to_meta(meta, shape): - meta['input_shape'] = shape - return meta - - -def set_image_metadata(annotation, images): - image_sizes = [] - data = images.data - if not isinstance(data, list): - data = [data] - for image in data: - image_sizes.append(image.shape) - annotation.set_image_size(image_sizes) - - return annotation, images - - -def get_indexs(container, element): - return [index for index, container_element in enumerate(container) if container_element == element] - - -def find_nearest(array, value, mode=None): - if not array: - return -1 - array = np.asarray(array) - idx = (np.abs(array - value)).argmin() - if mode == 'less': - return idx - 1 if array[idx] > value else idx - if mode == 'more': - return idx + 1 if array[idx] < value else idx - return idx diff --git a/tools/accuracy_checker/configs/face-detection-adas-0001.yml b/tools/accuracy_checker/configs/face-detection-adas-0001.yml deleted file mode 100644 index 952fbb5ce2ac80..00000000000000 --- a/tools/accuracy_checker/configs/face-detection-adas-0001.yml +++ /dev/null @@ -1,106 +0,0 @@ -models: - - name: face-detection-adas-0001 - - - framework: dlsdk - device: CPU - model: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001.xml - weights: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001.bin - adapter: ssd - cpu_extensions: AUTO - - - framework: dlsdk - tags: - - GPU32 - device: GPU - model: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001.xml - weights: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001.bin - adapter: ssd - - - framework: dlsdk - tags: - - GPU16 - device: GPU - model: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001-fp16.xml - weights: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001-fp16.bin - adapter: ssd - - - framework: dlsdk - device: MYRIAD - model: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001-fp16.xml - weights: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001-fp16.bin - adapter: ssd - - - framework: dlsdk - device: HDDL - model: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001-fp16.xml - weights: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001-fp16.bin - adapter: ssd - - - framework: dlsdk - tags: - - A10_devkit - - FP16 - device: HETERO:FPGA,CPU - model: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001.xml - weights: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001.bin - adapter: ssd - cpu_extensions: AUTO - bitstream: 2019R1_A10DK_FP16_MobileNet_Clamp.aocx - - - framework: dlsdk - tags: - - A10_devkit - - FP11 - device: HETERO:FPGA,CPU - model: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001.xml - weights: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001.bin - adapter: ssd - cpu_extensions: AUTO - bitstream: 2019R1_A10DK_FP11_ELU.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP16 - device: HETERO:FPGA,CPU - model: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001.xml - weights: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001.bin - adapter: ssd - cpu_extensions: AUTO - bitstream: 2019R1_PL1_FP16_ResNet_SqueezeNet_VGG_ELU.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP11 - device: HETERO:FPGA,CPU - model: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001.xml - weights: Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001.bin - adapter: ssd - cpu_extensions: AUTO - bitstream: 2019R1_PL1_FP11_MobileNetCaffe.aocx - - datasets: - - name: wider - data_source: WIDER_val/images - annotation_conversion: - converter: wider - annotation_file: wider_face_split/wider_face_val_bbx_gt.txt - - preprocessing: - - type: resize - dst_width: 672 - dst_height: 384 - - postprocessing: - - type: resize_prediction_boxes - - type: filter - height_range: 100 - apply_to: annotation - - metrics: - - type: map - ignore_difficult: True - include_boundaries: False - allow_multiple_matches_per_ignored: True - use_filtered_tp: True diff --git a/tools/accuracy_checker/configs/face-detection-retail-0004.yml b/tools/accuracy_checker/configs/face-detection-retail-0004.yml deleted file mode 100644 index affe6398cf0b9e..00000000000000 --- a/tools/accuracy_checker/configs/face-detection-retail-0004.yml +++ /dev/null @@ -1,113 +0,0 @@ -models: - - name: face-detection-retail-0004 - - launchers: - - framework: dlsdk - tags: - - FP32 - device: CPU - model: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004.xml - weights: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004.bin - adapter: ssd - cpu_extensions: AUTO - - - framework: dlsdk - tags: - - FP32 - device: GPU - model: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004.xml - weights: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004.bin - adapter: ssd - - - framework: dlsdk - tags: - - FP16 - device: GPU - model: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004-fp16.xml - weights: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004-fp16.bin - adapter: ssd - - - framework: dlsdk - device: MYRIAD - model: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004-fp16.xml - weights: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004-fp16.bin - adapter: ssd - - - framework: dlsdk - device: HDDL - model: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004-fp16.xml - weights: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004-fp16.bin - adapter: ssd - - - framework: dlsdk - tags: - - A10_devkit - - FP16 - device: HETERO:FPGA,CPU - model: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004.xml - weights: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004.bin - adapter: ssd - cpu_extensions: AUTO - bitstream: 2019R1_A10DK_FP16_TinyYolo.aocx - - - framework: dlsdk - tags: - - A10_devkit - - FP11 - device: HETERO:FPGA,CPU - model: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004.xml - weights: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004.bin - adapter: ssd - cpu_extensions: AUTO - bitstream: 2019R1_A10DK_FP11_CaffeMobileNet.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP16 - device: HETERO:FPGA,CPU - model: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004.xml - weights: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004.bin - adapter: ssd - cpu_extensions: AUTO - bitstream: 2019R1_PL1_FP16_ResNet_SqueezeNet_VGG_ELU.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP11 - device: HETERO:FPGA,CPU - model: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004.xml - weights: Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004.bin - adapter: ssd - cpu_extensions: AUTO - bitstream: 2019R1_PL1_FP11_ResNet_SqueezeNet_VGG.aocx - - datasets: - - name: wider - data_source: WIDER_val/images - annotation_conversion: - converter: wider - annotation_file: wider_face_split/wider_face_val_bbx_gt.txt - - preprocessing: - - type: resize - size: 300 - - postprocessing: - - type: resize_prediction_boxes - - type: cast_to_int - - type: filter - apply_to: annotation - height_range: 60 - is_empty: True - - type: filter - min_confidence: 0.0 - apply_to: prediction - - metrics: - - type: map - ignore_difficult: True - include_boundaries: False - allow_multiple_matches_per_ignored: False - distinct_conf: False diff --git a/tools/accuracy_checker/configs/face-reidentification-retail-0095.yml b/tools/accuracy_checker/configs/face-reidentification-retail-0095.yml deleted file mode 100644 index 50c0a55504f92a..00000000000000 --- a/tools/accuracy_checker/configs/face-reidentification-retail-0095.yml +++ /dev/null @@ -1,96 +0,0 @@ -models: - - name: face-reidentification-retail-0095 - - launchers: - - framework: dlsdk - tags: - - FP32 - device: CPU - model: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095.xml - weights: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095.bin - adapter: reid - - - framework: dlsdk - tags: - - FP32 - device: GPU - model: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095.xml - weights: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095.bin - adapter: reid - - - framework: dlsdk - tags: - - FP16 - device: GPU - model: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095-fp16.xml - weights: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095-fp16.bin - adapter: reid - - - framework: dlsdk - device: MYRIAD - model: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095-fp16.xml - weights: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095-fp16.bin - adapter: reid - - - framework: dlsdk - device: HDDL - model: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095-fp16.xml - weights: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095-fp16.bin - adapter: reid - - - framework: dlsdk - tags: - - A10_devkit - - FP16 - device: HETERO:FPGA,CPU - model: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095.xml - weights: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095.bin - adapter: reid - bitstream: 2019R1_A10DK_FP16_SSD300.aocx - - - framework: dlsdk - tags: - - A10_devkit - - FPGA11 - device: HETERO:FPGA,CPU - model: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095.xml - weights: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095.bin - adapter: reid - bitstream: 2019R1_A10DK_FP11_CaffeMobileNet.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP16 - device: HETERO:FPGA,CPU - model: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095.xml - weights: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095.bin - adapter: reid - bitstream: 2019R1_PL1_FP16_MobileNet_Clamp.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP11 - device: HETERO:FPGA,CPU - model: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095.xml - weights: Retail/object_reidentification/face/mobilenet_based/dldt/face-reidentification-retail-0095.bin - adapter: reid - bitstream: 2019R1_PL1_FP11_MobileNetCaffe.aocx - - datasets: - - name: lfw - data_source: LFW/lfw - annotation_conversion: - converter: face_reid_pairwise - pairs_file: LFW/annotation/pairs.txt - landmarks_file: LFW/annotation/lfw_landmark.txt - - preprocessing: - - type: point_alignment - size: 400 - - type: resize - size: 128 - - metrics: - - type: pairwise_accuracy_subsets diff --git a/tools/accuracy_checker/configs/human-pose-estimation-0001.yml b/tools/accuracy_checker/configs/human-pose-estimation-0001.yml deleted file mode 100644 index efe6105f7b6d03..00000000000000 --- a/tools/accuracy_checker/configs/human-pose-estimation-0001.yml +++ /dev/null @@ -1,155 +0,0 @@ -models: - - name: human-pose-estimation-0001 - - launchers: - - framework: dlsdk - tags: - - FP32 - device: CPU - model: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001.xml - weights: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001.bin - allow_reshape_input: True - adapter: - type: human_pose_estimation - part_affinity_fields_out: Mconv7_stage2_L1 - keypoints_heatmap_out: Mconv7_stage2_L2 - - - framework: dlsdk - tags: - - INT8 - device: CPU - model: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001-int8.xml - weights: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001-int8.bin - allow_reshape_input: True - adapter: - type: human_pose_estimation - part_affinity_fields_out: Mconv7_stage2_L1 - keypoints_heatmap_out: Mconv7_stage2_L2 - - - framework: dlsdk - tags: - - FP32 - device: GPU - model: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001.xml - weights: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001.bin - allow_reshape_input: True - adapter: - type: human_pose_estimation - part_affinity_fields_out: Mconv7_stage2_L1 - keypoints_heatmap_out: Mconv7_stage2_L2 - - - framework: dlsdk - tags: - - FP16 - device: GPU - model: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001-fp16.xml - weights: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001-fp16.bin - allow_reshape_input: True - adapter: - type: human_pose_estimation - part_affinity_fields_out: Mconv7_stage2_L1 - keypoints_heatmap_out: Mconv7_stage2_L2 - - - framework: dlsdk - device: MYRIAD - model: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001-fp16.xml - weights: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001-fp16.bin - allow_reshape_input: True - adapter: - type: human_pose_estimation - part_affinity_fields_out: Mconv7_stage2_L1 - keypoints_heatmap_out: Mconv7_stage2_L2 - - - framework: dlsdk - device: HDDL - model: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001-fp16.xml - weights: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001-fp16.bin - allow_reshape_input: True - adapter: - type: human_pose_estimation - part_affinity_fields_out: Mconv7_stage2_L1 - keypoints_heatmap_out: Mconv7_stage2_L2 - - - framework: dlsdk - tags: - - A10_devkit - - FP16 - device: HETERO:FPGA,CPU - model: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001.xml - weights: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001.bin - allow_reshape_input: True - adapter: - type: human_pose_estimation - part_affinity_fields_out: Mconv7_stage2_L1 - keypoints_heatmap_out: Mconv7_stage2_L2 - bitstream: 2019R1_A10DK_FP16_ELU.aocx - - - framework: dlsdk - tags: - - A10_devkit - - FP11 - device: HETERO:FPGA,CPU - model: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001.xml - weights: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001.bin - allow_reshape_input: True - adapter: - type: human_pose_estimation - part_affinity_fields_out: Mconv7_stage2_L1 - keypoints_heatmap_out: Mconv7_stage2_L2 - bitstream: 2019R1_A10DK_FP11_ELU.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP16 - device: HETERO:FPGA,CPU - model: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001.xml - weights: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001.bin - allow_reshape_input: True - adapter: - type: human_pose_estimation - part_affinity_fields_out: Mconv7_stage2_L1 - keypoints_heatmap_out: Mconv7_stage2_L2 - bitstream: 2019R1_PL1_FP16_ResNet_SqueezeNet_VGG_ELU.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP11 - device: HETERO:FPGA,CPU - model: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001.xml - weights: Transportation/human_pose_estimation/mobilenet-v1/dldt/human-pose-estimation-0001.bin - allow_reshape_input: True - adapter: - type: human_pose_estimation - part_affinity_fields_out: Mconv7_stage2_L1 - keypoints_heatmap_out: Mconv7_stage2_L2 - bitstream: 2019R1_PL1_FP11_ELU.aocx - - datasets: - - name: ms_coco_keypoints - data_source: val2017 - annotation_conversion: - converter: mscoco_keypoints - annotation_file: person_keypoints_val2017.json - - preprocessing: - - type: resize - size: 368 - interpolation: CUBIC - aspect_ratio_scale: width - - type: padding - stride: 8 - - postprocessing: - - type: filter - apply_to: annotation - area_range: 1, 10000000000 - - type: filter - apply_to: prediction - area_range: 1, 10000000000 - - metrics: - - name: AP - type: coco_precision - max_detections: 20 diff --git a/tools/accuracy_checker/configs/landmarks-regression-retail-0009.yml b/tools/accuracy_checker/configs/landmarks-regression-retail-0009.yml deleted file mode 100644 index 533398fd6fc15e..00000000000000 --- a/tools/accuracy_checker/configs/landmarks-regression-retail-0009.yml +++ /dev/null @@ -1,106 +0,0 @@ -models: - - name: landmarks-regression-retail-0009 - - launchers: - - framework: dlsdk - tags: - - FP32 - device: CPU - model: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009.xml - weights: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009.bin - adapter: landmarks_regression - cpu_extensions: AUTO - - - framework: dlsdk - tags: - - FP32 - device: GPU - model: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009.xml - weights: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009.bin - adapter: landmarks_regression - - - framework: dlsdk - tags: - - FP16 - device: GPU - model: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009-fp16.xml - weights: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009-fp16.bin - adapter: landmarks_regression - - - framework: dlsdk - device: MYRIAD - model: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009-fp16.xml - weights: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009-fp16.bin - adapter: landmarks_regression - - - framework: dlsdk - device: HDDL - model: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009-fp16.xml - weights: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009-fp16.bin - adapter: landmarks_regression - - - framework: dlsdk - tags: - - A10_devkit - - FP16 - device: HETERO:FPGA,CPU - model: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009.xml - weights: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009.bin - adapter: landmarks_regression - cpu_extensions: AUTO - bitstream: 2019R1_A10DK_FP16_AlexNet_GoogleNet.aocx - - - framework: dlsdk - tags: - - A10_devkit - - FP11 - device: HETERO:FPGA,CPU - model: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009.xml - weights: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009.bin - adapter: landmarks_regression - cpu_extensions: AUTO - bitstream: 2019R1_A10DK_FP11_RMNet.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP16 - device: HETERO:FPGA,CPU - model: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009.xml - weights: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009.bin - adapter: landmarks_regression - cpu_extensions: AUTO - bitstream: 2019R1_PL1_FP16_ResNet_SqueezeNet_VGG_ELU.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP11 - device: HETERO:FPGA,CPU - model: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009.xml - weights: Retail/object_attributes/landmarks_regression/0009/dldt/landmarks-regression-retail-0009.bin - adapter: landmarks_regression - cpu_extensions: AUTO - bitstream: 2019R1_PL1_FP11_RMNet.aocx - - datasets: - - name: vgg2face - data_source: VGGFaces2/test - annotation_conversion: - converter: landmarks_regression - landmarks_csv_file: VGGFaces2/bb_landmark/loose_landmark_test.csv - bbox_csv_file: VGGFaces2/bb_landmark/loose_bb_test.csv - - preprocessing: - - type: crop_rect - - type: resize - size: 48 - - postprocessing: - - type: normalize_landmarks_points - use_annotation_rect: True - - metrics: - - type: per_point_normed_error - presenter: print_vector - - type: normed_error diff --git a/tools/accuracy_checker/configs/person-reidentification-retail-0031.yml b/tools/accuracy_checker/configs/person-reidentification-retail-0031.yml deleted file mode 100644 index 090f69c6ce3d3d..00000000000000 --- a/tools/accuracy_checker/configs/person-reidentification-retail-0031.yml +++ /dev/null @@ -1,110 +0,0 @@ -models: - - name: person-reidentification-retail-0031 - - launchers: - - framework: dlsdk - tags: - - FP32 - device: CPU - model: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031.xml - weights: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031.bin - adapter: reid - - - framework: dlsdk - tags: - - INT8 - device: CPU - model: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031-int8.xml - weights: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031-int8.bin - adapter: reid - - - framework: dlsdk - tags: - - FP32 - device: GPU - model: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031.xml - weights: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031.bin - adapter: reid - - - framework: dlsdk - tags: - - FP16 - device: GPU - model: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031-fp16.xml - weights: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031-fp16.bin - adapter: reid - - - framework: dlsdk - device: MYRIAD - model: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031-fp16.xml - weights: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031-fp16.bin - adapter: reid - - - framework: dlsdk - device: HDDL - model: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031-fp16.xml - weights: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031-fp16.bin - adapter: reid - - - framework: dlsdk - tags: - - A10_devkit - - FP16 - device: HETERO:FPGA,CPU - model: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031.xml - weights: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031.bin - adapter: reid - bitstream: 2019R1_A10DK_FP16_ELU.aocx - - - framework: dlsdk - tags: - - A10_devkit - - FP11 - device: HETERO:FPGA,CPU - model: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031.xml - weights: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031.bin - adapter: reid - bitstream: 2019R1_A10DK_FP11_ELU.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP16 - device: HETERO:FPGA,CPU - model: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031.xml - weights: Security/object_attributes/pedestrian/person-attributes-recognition-crossroad-0031/dldt/person-attributes-recognition-crossroad-0031.bin - adapter: reid - bitstream: 2019R1_PL1_FP16_ResNet_SqueezeNet_VGG_ELU.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP11 - device: HETERO:FPGA,CPU - model: person-reidentification-retail-0031/FP32/person-reidentification-retail-0031.xml - weights: person-reidentification-retail-0031/FP32/person-reidentification-retail-0031.bin - adapter: reid - bitstream: 2019R1_PL1_FP11_ELU.aocx - - datasets: - - name: market1501 - reader: pillow_imread - data_source: Market-1501-v15.09.15 - annoation_conversion: - converter: market1501 - data_dir: Market-1501-v15.09.15 - - preprocessing: - - type: bgr_to_rgb - - type: resize - dst_width: 48 - dst_height: 96 - use_pillow: True - interpolation: ANTIALIAS - - metrics: - - name: rank@1 - type: cmc - top_k: 1 - - - type: reid_map diff --git a/tools/accuracy_checker/configs/person-reidentification-retail-0076.yml b/tools/accuracy_checker/configs/person-reidentification-retail-0076.yml deleted file mode 100644 index 94398185a27b28..00000000000000 --- a/tools/accuracy_checker/configs/person-reidentification-retail-0076.yml +++ /dev/null @@ -1,106 +0,0 @@ -models: - - name: person-reidentification-retail-0076 - - launchers: - - framework: dlsdk - tags: - - FP32 - device: CPU - model: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076.xml - weights: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076.bin - adapter: reid - - - framework: dlsdk - tags: - - INT8 - device: CPU - model: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076-int8.xml - weights: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076-int8.bin - adapter: reid - - - framework: dlsdk - tags: - - FP32 - device: GPU - model: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076.xml - weights: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076.bin - adapter: reid - - - framework: dlsdk - tags: - - FP16 - device: GPU - model: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076-fp16.xml - weights: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076-fp16.bin - adapter: reid - - - framework: dlsdk - device: MYRIAD - model: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076-fp16.xml - weights: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076-fp16.bin - adapter: reid - - - framework: dlsdk - device: HDDL - model: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076-fp16.xml - weights: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076-fp16.bin - adapter: reid - - - framework: dlsdk - tags: - - A10_devkit - - FP16 - device: HETERO:FPGA,CPU - model: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076.xml - weights: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076.bin - adapter: reid - bitstream: 2019R1_A10DK_FP16_ELU.aocx - - - framework: dlsdk - tags: - - A10_devkit - - FP11 - device: HETERO:FPGA,CPU - model: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076.xml - weights: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076.bin - adapter: reid - bitstream: 2019R1_A10DK_FP11_ELU.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP16 - device: HETERO:FPGA,CPU - model: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076.xml - weights: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076.bin - adapter: reid - bitstream: 2019R1_PL1_FP16_ResNet_SqueezeNet_VGG_ELU.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP11 - device: HETERO:FPGA,CPU - model: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076.xml - weights: Retail/object_redidentification/pedestrian/rmnet_based/0076/dldt/person-reidentification-retail-0076.bin - adapter: reid - bitstream: 2019R1_PL1_FP11_ELU.aocx - - datasets: - - name: market1501 - data_source: Market-1501-v15.09.15 - annoation_conversion: - converter: market1501 - data_dir: Market-1501-v15.09.15 - - preprocessing: - - type: resize - dst_width: 128 - dst_height: 384 - - metrics: - - name: rank@1 - type: cmc - top_k: 1 - - - type: reid_map diff --git a/tools/accuracy_checker/configs/person-reidentification-retail-0079.yml b/tools/accuracy_checker/configs/person-reidentification-retail-0079.yml deleted file mode 100644 index bd07fa092748b2..00000000000000 --- a/tools/accuracy_checker/configs/person-reidentification-retail-0079.yml +++ /dev/null @@ -1,106 +0,0 @@ -models: - - name: person-reidentification-retail-0079 - - launchers: - - framework: dlsdk - tags: - - FP32 - device: CPU - model: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079.xml - weights: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079.bin - adapter: reid - - - framework: dlsdk - tags: - - INT8 - device: CPU - model: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079-int8.xml - weights: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079-int8.bin - adapter: reid - - - framework: dlsdk - tags: - - FP32 - device: GPU - model: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079.xml - weights: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079.bin - adapter: reid - - - framework: dlsdk - tags: - - FP16 - device: GPU - model: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079-fp16.xml - weights: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079-fp16.bin - adapter: reid - - - framework: dlsdk - device: MYRIAD - model: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079-fp16.xml - weights: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079-fp16.bin - adapter: reid - - - framework: dlsdk - device: HDDL - model: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079-fp16.xml - weights: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079-fp16.bin - adapter: reid - - - framework: dlsdk - tags: - - A10_devkit - - FP16 - device: HETERO:FPGA,CPU - model: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079.xml - weights: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079.bin - adapter: reid - bitstream: 2019R1_A10DK_FP16_RMNet.aocx - - - framework: dlsdk - tags: - - A10_devkit - - FP11 - device: HETERO:FPGA,CPU - model: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079.xml - weights: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079.bin - adapter: reid - bitstream: 2019R1_A10DK_FP11_ELU.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP16 - device: HETERO:FPGA,CPU - model: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079.xml - weights: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079.bin - adapter: reid - bitstream: 2019R1_PL1_FP16_RMNet.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP11 - device: HETERO:FPGA,CPU - model: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079.xml - weights: Retail/object_reidentification/pedestrian/rmnet_based/0079/dldt/person-reidentification-retail-0079.bin - adapter: reid - bitstream: 2019R1_PL1_FP11_ELU.aocx - - datasets: - - name: market1501 - data_source: Market-1501-v15.09.15 - annoation_conversion: - converter: market1501 - data_dir: Market-1501-v15.09.15 - - preprocessing: - - type: resize - dst_width: 64 - dst_height: 160 - - metrics: - - name: rank@1 - type: cmc - top_k: 1 - - - type: reid_map diff --git a/tools/accuracy_checker/configs/resnet50-binary-0001.yml b/tools/accuracy_checker/configs/resnet50-binary-0001.yml deleted file mode 100644 index 22914748b8b1a0..00000000000000 --- a/tools/accuracy_checker/configs/resnet50-binary-0001.yml +++ /dev/null @@ -1,40 +0,0 @@ -models: - - name: resnet50-binary-0001 - - launchers: - - framework: dlsdk - tags: - - INT1 - device: CPU - model: PublicCompressed/classification/resnet50_binary/dldt/resnet50-binary-0001.xml - weights: PublicCompressed/classification/resnet50_binary/dldt/resnet50-binary-0001.bin - adapter: classification - - - datasets: - - name: imagenet - data_source: ImageNet - annotation_conversion: - converter: imagenet - annotation_file: val.txt - annotation: imagenet.pickle - reader: pillow_imread - - preprocessing: - - type: resize - size: 256 - aspect_ratio_scale: greater - use_pillow: True - interpolation: BILINEAR - - type: crop - size: 224 - use_pillow: True - - type: bgr_to_rgb - - metrics: - - name: accuracy@top1 - type: accuracy - top_k: 1 - - name: accuracy@top5 - type: accuracy - top_k: 5 diff --git a/tools/accuracy_checker/configs/text-detection-0002.yml b/tools/accuracy_checker/configs/text-detection-0002.yml deleted file mode 100644 index d1ebd9b8d72ae0..00000000000000 --- a/tools/accuracy_checker/configs/text-detection-0002.yml +++ /dev/null @@ -1,140 +0,0 @@ -models: - - name: text-detection-0002 - - launchers: - - framework: dlsdk - tags: - - FP32 - device: CPU - model: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002.xml - weights: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002.bin - adapter: - type: text_detection - pixel_link_out: pixel_link/add_2 - pixel_class_out: pixel_cls/add_2 - cpu_extensions: AUTO - - - framework: dlsdk - tags: - - FP32 - device: GPU - model: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002.xml - weights: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002.bin - adapter: - type: text_detection - pixel_link_out: pixel_link/add_2 - pixel_class_out: pixel_cls/add_2 - - - framework: dlsdk - tags: - - FP16 - device: GPU - model: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002-fp16.xml - weights: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002-fp16.bin - adapter: - type: text_detection - pixel_link_out: pixel_link/add_2 - pixel_class_out: pixel_cls/add_2 - - - framework: dlsdk - device: MYRIAD - model: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002-fp16.xml - weights: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002-fp16.bin - adapter: - type: text_detection - pixel_link_out: pixel_link/add_2 - pixel_class_out: pixel_cls/add_2 - - - framework: dlsdk - device: HDDL - model: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002-fp16.xml - weights: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002-fp16.bin - adapter: - type: text_detection - pixel_link_out: pixel_link/add_2 - pixel_class_out: pixel_cls/add_2 - - - framework: dlsdk - tags: - - A10_devkit - - FP16 - device: HETERO:FPGA.CPU - model: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002.xml - weights: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002.bin - adapter: - type: text_detection - pixel_link_out: pixel_link/add_2 - pixel_class_out: pixel_cls/add_2 - cpu_extensions: AUTO - bitstream: 2019R1_A10DK_FP16_MobileNet_Clamp.aocx - - - framework: dlsdk - tags: - - A10_devkit - - FP11 - device: HETERO:FPGA.CPU - model: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002.xml - weights: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002.bin - adapter: - type: text_detection - pixel_link_out: pixel_link/add_2 - pixel_class_out: pixel_cls/add_2 - cpu_extensions: AUTO - bitstream: 2019R1_A10DK_FP11_MobileNet_Clamp.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP16 - device: HETERO:FPGA.CPU - model: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002.xml - weights: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002.bin - adapter: - type: text_detection - pixel_link_out: pixel_link/add_2 - pixel_class_out: pixel_cls/add_2 - cpu_extensions: AUTO - bitstream: 2019R1_PL1_FP16_MobileNet_Clamp.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP11 - device: HETERO:FPGA.CPU - model: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002.xml - weights: Retail/object_detection/text/pixel_link_mobilenet_v2/0001/text-detection-0002.bin - adapter: - type: text_detection - pixel_link_out: pixel_link/add_2 - pixel_class_out: pixel_cls/add_2 - cpu_extensions: AUTO - bitstream: 2019R1_PL1_FP11_MobileNet_Clamp.aocx - - datasets: - - name: ICDAR2015 - - data_source: ICDAR15_DET_validation/ch4_test_images - annotation_conversion: - converter: icdar15_detection - data_dir: ICDAR15_DET_validation/gt - - preprocessing: - - type: resize - dst_width: 1280 - dst_height: 768 - - postprocessing: - - type: cast_to_int - - type: filter - area_range: 300, 980993 - height_range: 10 - width_range: 10 - apply_to: prediction - remove_filtered: True - - type: clip_points - apply_to: prediction - - metrics: - - type: text_detection - name: f-measure - ignore_difficult: True diff --git a/tools/accuracy_checker/configs/text-recognition-0012.yml b/tools/accuracy_checker/configs/text-recognition-0012.yml deleted file mode 100644 index f3045175b8d2b9..00000000000000 --- a/tools/accuracy_checker/configs/text-recognition-0012.yml +++ /dev/null @@ -1,100 +0,0 @@ -models: - - name: text-recognition-0012 - - launchers: - - framework: dlsdk - tags: - - FP32 - device: CPU - model: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012.xml - weights: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012.bin - adapter: beam_search_decoder - cpu_extensions: AUTO - - - framework: dlsdk - tags: - - FP32 - device: GPU - model: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012.xml - weights: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012.bin - adapter: beam_search_decoder - - - framework: dlsdk - tags: - - FP16 - device: GPU - model: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012-fp16.xml - weights: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012-fp16.bin - adapter: beam_search_decoder - - - framework: dlsdk - device: MYRIAD - model: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012-fp16.xml - weights: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012-fp16.bin - adapter: beam_search_decoder - - - framework: dlsdk - device: HDDL - model: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012-fp16.xml - weights: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012-fp16.bin - adapter: beam_search_decoder - - - framework: dlsdk - tags: - - A10_devkit - - FP16 - device: HETERO:FPGA,CPU - model: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012.xml - weights: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012.bin - adapter: beam_search_decoder - cpu_extensions: AUTO - bitstream: 2019R1_A10DK_FP16_AlexNet_GoogleNet.aocx - - - framework: dlsdk - tags: - - A10_devkit - - FP11 - device: HETERO:FPGA,CPU - model: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012.xml - weights: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012.bin - adapter: beam_search_decoder - cpu_extensions: AUTO - bitstream: 2019R1_A10DK_FP11_AlexNet_GoogleNet_SqueezeNet.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP16 - device: HETERO:FPGA,CPU - model: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012.xml - weights: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012.bin - adapter: beam_search_decoder - cpu_extensions: AUTO - bitstream: 2019R1_PL1_FP16_ResNet_SqueezeNet_VGG_ELU.aocx - - - framework: dlsdk - tags: - - HDDL-F - - FP11 - device: HETERO:FPGA,CPU - model: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012.xml - weights: Retail/text_recognition/bilstm_crnn_bilstm_decoder/0012/dldt/text-recognition-0012.bin - adapter: beam_search_decoder - cpu_extensions: AUTO - bitstream: 2019R1_PL1_FP11_AlexNet_GoogleNet.aocx - - datasets: - - name: ICDAR2013 - data_source: ICDAR13_REC_validation/Challenge2_Test_Task3_Images - annotation_conversion: - converter: icdar13_recognition - annotation_file: ICDAR13_REC_validation/gt/gt.txt.fixed.alfanumeric - - preprocessing: - - type: bgr_to_gray - - type: resize - dst_width: 120 - dst_height: 32 - - metrics: - - type: character_recognition_accuracy diff --git a/tools/accuracy_checker/data/test_data/1.jpg b/tools/accuracy_checker/data/test_data/1.jpg deleted file mode 100644 index 20edaaee81f192e33998ce97f651f97c9c115093..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 147595 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

sAn+@?{3-ijcz5E{Q&25) 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̋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

SLi4V4x@zVzrf?XWXEAt~WZ0V! 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 z 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 K6hjLLy@o>CM9z zBp1(~CALjVVbW^#`tquI*m^z{Pjv_-ayQzD@;8<%dTc~B%LXLDm1hZmTZ5!N^(9dl zI75QZuyewcXx@(o&DvR(DPqshD0Y2tL);YLC3ZYBj{Es@m&2qlI@~9{bGf^54Y%cY zsz`0l9C6XWEr*@#5m>sZu14kcMu&+G3BuI5cSStOQ<0h4IC0+_Gp@Mu7+3!JOX0D@ zQR4kKUBpo_^~{FG0D<)sk1E$&s*J412u`I~Q7~_bnP5WQc}`60C_(Q)p>_SmS=M{c zO%pgL9Aj2p(Xu}LI^6oUc?CzEr(@%#GRU-^jbsjnDGS#B)e@XI_LEU6zgM;9+(jnJ z%u29+ixVnww&Cc+Hk>!78OJH?z{XKau@<9By3V^kdpMd@UBWF|>^Up%nuF(j4Td$&JM7ZQ0J4k;Rv>1o&s>K;*TGG9~dZ@Q`7;QOTDJdFN zifL>oiu9N%|Ad<@4k)qnxRQy|8Q<4Q^CsVNZs8f@%28a5d%O*8R;uE@S&4M!vhCC@ z?I3Zr%_6-lQ@c6L1i2?Y$>YP>(D~6FWN#+%+$U|quKC8Kv3I5OmtXs-zV5~P_QW$d z_TL9ygTF6|#z@Fw8#l0#{a`ozHD5F;lP|jWpg>r$yIM#}BkjNOw~1DzFrx12U7|J_ zUD35=YaN_ULcqZ;?BdAGMb<_$(f^FFfOdbZ(FcoB|~w=#|Jaqc|q;En|M zsyb5s{tP|3bs zlNY?9$*)#Ie#l|oeV&8_XGKdae*EUmYI_Jqwe~O&?@y07-jbM|-Gh6+{iW&WF49ol zNKA4`#5+q_r_Z)gC_Q9VZ&uty-el%u+wnpwj%Qt?s(+l%{ZN91${~2{dM3$Rwi{+G zA#fvaHLU9F$Mo5qEF*c5_qQq?rbS5HiYj$AqG2i&Q*NKK6!G>(rX=eWN}((fN+Z{A-j*=_^raWx>$RpYnp z;DY%*L)frfo!;qA!shyT44e7{N65v1g{3pmEdNe_raR*HOQ*3mK^-UDvZQ_!9H^6% zCQfuL#F*s^@l;kZ_4M0=9`5nzD*ubl$nVD1l|Qf{{v$pZvk_O=>G6-=khPimp_=oh zeWoBNkSl2W93fb!q-~R1zl5{aAc*5rCog#Keo$cESIx**j<5+V+$s3#IYUsryspZ; zP{QmoFcyrlm>{^P!x4P=e43-TESWRk>IFwUW4>V7+LhKDW_nl;MjpijQ{_>_W`)Mq z6od5PC(v%okc>7?AkFS;$p0uh6MrbbHVzBfqbx}X5t40&F~f7tSkh*xC|jh`CMv14 zXtS@C5*3w%Y!NfeSe|oFDt@R$DM~5rrF{{Ryz~AK&*wbn-1ql;U5*!z;I!q(@M2?a z&|aqwWn=MVmvtJst>;f&XH=rS&KvRe^g2rK-!|g8zLLC6Q|H>h;Nsw&uF$&YGUy%t z4Z7z0?V3**vws|TVaHx1&(fFvY`68(IQ!M3boS4xtLz|V%y2M9B;h$5Km7}pE;No#mn?_VJ?bNWU9u?!flym zWY6hAu4c~kmd%+EELJmIit<947B5is&v(dA?-3C=79#cwYZNqJd`9;A<34UH>U(Dc zJFLDD4W(StO_b336&`WHu}(qHtw=UayF{)H@gAR!orUt#iG>a&>iti z$zU3{Vooz&Cw8_y*r?vvuv~`x|6g}*GYdXD_k*viH~xItA4}~Bqa>s1h_>!=5L5&q zYT7&WEcXKH&;3rq1qSrUO?f=8EQzeoKi|k2JxbhCmRnzL0&N|srPEOE9R8f%3e3ORn=;>jZIm^y8W)!rfd4? z+QZV{ncjyaSmRkGEI$p?+Vr3EYgeTt+srJO&hmBRvg~)q*B04z+VmDE+3q*pVXNDk zg->m2BPcQng6_w_c2`HDSYLwWxeQ2~v=@R(D%uN*_jP8&q%LT8vC+eQn_b_i6i2W6Ys=Kp0&oXXr>9|1s1@O^O5jc<~EdQ7Lx2aXUKD( z+2ncZQnIKcHfSlmVfHMt%ZtuJ=^_$At%J=+Vqn#-ISxN>WKshcn56EW5tMBb!N9vrSka&Z)qOp5Wvds&6y5-r zgwqh;&Vx8}0~qVEq5Iq=;Dp9@I9G8Eh(R69Ul9YPdZwVdIG?yrWkAD7UE@qn9y+q| zC-IFH^VH5?AiG6B_Z_jy8REPq$nca*J7#zZ{FI)giWGZ%rMUs2( z7NV8Tt1;`x6jALbGg6zCgKj<6pnav5fx*`S>s9lo^FxE5@-8(!W=QJaW^~7-^In_{ zwDu@1<#p)~^8#aUGa@nzujrWntZtlrw(7^S0ba+sg^Zsm0gQLIAMz6Sbn==UjjVO7 z`y2RV~{3y@qZXa*LwFi1_?XB~~wD=|{6RPk&;9}0GU`zN^MDI;8+_Cc`7qC_CAH(9toaa=%7&JemcX$z$v zKNom2-wBol4+shp7(!`mA)GA35E|}w5?*v!Ak;bTEFAZugz+zS36r8jg(?&F!Y60U zgpp?{VO;(m;qBQE`1vF5{9URi`FDP$@g;}Y{FCbz@||1i_}5nx{>z~he*Ez)zE79| zf2%3*Wj)gPy-A(?@9`S^u8mjtkB`3N-M4Ar1-VvLD@_~b4easY8`Vc!pPaOs-}k$Q zKVR`O-*nMJIM<&EdkY&NN$Di)df|t>?Q&69ub5LEJWkzosX-g>?ZBS;Q6JyFAxB=_MSsMdiJnKk+-ad+nCgu}XZu{BrcQ;sfH-kotwTY>@gLZ{ zl@qN!oP$DIm!PYu|AANa2e7$5RTPo@mb&oc9Bf|t5ta-ML!X*8)ok|&&G=ys+gfyp z^NcjCL?`3ReTnqAL;S$$+z2QAZ>RSKC!YS?z392O|#o4anbJLtXX#3>&sbc-{!J{ z8S$)P&M~{o#wvEy)Ay`rgOYaK_;yyat)Cs!H-#m+cA(~Q@0Hq@kA(u43#;w4CR?z4 ztT(V;e-j86OxF?I6FPF2+nm5TA+5MF_&Hu4--TqUk}V}+>1}f0bZK=42LQvp(?Aj zxWzRQkL-~o|EyNPRwUk69DWRUrZ_;z?(Kj)X2F-}N%X)|4Us0-mo7cR1Ll`}a^`C* z<*cLtH`Ue9nX;qMr@b0QHs|4T{$+f^c0LqO%z#f%`1oUbJ+?@a#@^BmBw(#F_V1Cw z%kt0C|5XJv&PvHfSw1&NnB;S6`O12$2rhG+82`3wmy_!Tv*>6OP4hxX6nLlaM zxlJx!iy*u|B@QpQe4vesccK$vImk47F}{(woBUVl4VMq?hr3_rLa+F)B6OMyM@puG zt9bzaK4~X@yu1w`^xcAQzNWBBh*PBFb}k*!J}s@!>% zJ~w2H^XA^PmlQ`kl?N+fbb#HOz}) ze_^~IEnpn_Ajvdp_`%?vljcv|#k2mE>&aK$Jc}Q6rI^ul)0AoMs!GmJeNEKEG{~c@ zy~xV{55cid$+b1wWYYUBaOy`e((0NCd*k`2D=CeN^y1)F!+5lG(>iqfy8$eEpM!eL zW?HfEN#WJIIdg?FiY`+JykDl7jmfKpyezP{5?cK4At+wSRJFVpuJ3P>aeM|oh zdpy;MGym!)PN*4!Q+>*S^XZ8(XIfMtXZgDA)@7czczS1x7(-t3_!Ccf)%5&zjQi)i zd9O3uc~?J{@N_rpGoL6OXV~CaM!@IGJSX!bRV}AW8CE};{H5Kqc(A69*VFcix5Qwr zmBaE$49g?Sd9I@hjOQUwc+Fu|j2HW5xhvh|xe_Hq*wnokUp1G(*~k-@i_vo3Gqt#j zS1WO6WL(GpG)s~Gdp6d}mE+>5d0eXvl3c%yt&OECSE1_NxY^)Nm>i! zocsj=sq$cy+l|6=_9CL3jg#;BqPR!xs3^M&EuPwn9@bt$dB3;ea3^tQmb(?}p8JPR z-+hb5AH`yK>9ttmn+@(+&c=pI8nI#12{^j*8M!kY0|j~q(QC(Ed?Wb?X?dRto1bdxCVcumxfc9FB+`l&q@mBk7ogKt2E=im-sG;Pp zY(S5q<`A!Cv*Dh`RX99q4wDa@qaXj1h4rHox&sdcpszRJ#`J+xBbfs+Z@eDEN7+@(o* zpgX8zhb?#=ai`s07J;YMVc&pgI40) z|4SG)7=%BjVy{N$5^zbK0V%8sa9VX8>>@fL+Sv@AEE3Ovhlb!QiiR0AZV<|$?7s&) zfb6@spqv^H<9D0Lr}Zb$_Jv>3v4`$hc36+huM8p*Vz#_P!%k@1ITw#VVdBA*N|fYv zg?w8tB$v*#Bcloh%$_6eQ#@EhbF8kToTXfBqId$mSoVRcRCI+^CI``#jb7L@U$HSV zMh(1M0?2z-D*3Cu5t)d6BXY|x5a}QP2;&rjxQ{g?++Y^!>8+ss9Z$hERYa5$#;KP^ zI%H_<62-{93>(&|5SLLI*im`cL5#zrQacF@t&+s7#&_svr!tXn2l^@HPgYzOclq5 zcTsc1PU9s59_Ukr7wTUX*SJ<c~@Q=0fID2suby3V= zT&=IhjZJH^OgaCHVU^C|+lP7bc77S=jWU(_o(|6$SvCW_0t0itS4#)a&{CecBHNSk zgv;g&(ysG{>z*=7*UR$l78&sW>u~2;HD)txZiO;Z*maCf-_hz`D>Z)IMHQZFjXmG; zW&wI*(}{{Eri$-0Q=#Sh7MRb-CWq}u(S`vTd^ma%d4I|T$b&1?`Kl;fW_0Xc! zV&}mkmtb^gbp}fEnTD5FmZ98blc2vh3Fq#Zg^${Az=z8pA@%qmTHj+U$4!>vJU2Jy zEcvR&`8CgxvwHI^&Qt9Qc5&qucEv+4_OhxP_DPpPc9*&j`>e3o&XXHs*WM;#yB)dB zp4`N@J6)_TWGRfZ@+BUzV^7TGO!H1d4GN~z9?m|&R#cGUtEM>d9d1_g*Wxlh zOUwgvOey4P3EX(0qFnx%O%1=ZxQ@4R=n~I7Ig&4{I*DJuDThCP=@4J4wUu|j_BgL% z?nB( z49+S4qyIhmgbZ&Ep$~s$(0SQr^q=T0npvTR)x+oE4R4mBSQ8a|BvT1{Fm-WUs|c;q zc!B109H*2rwvqGy{Xt)#fwG(#fX*kS*vrjtCH-Yeu)kv|nY6N(R{x~}B|~xK*VSTj z<&q|3`;<1`xkA%kAH^PLgQ=)2^#W-qF(wU=hSwDLVdiZ$ZgNQyetlyp{;g7g#~Qlu zSdRjCQePLci%g|wZkvq_-kPB?FIm`a_5cp1yoQC{ir8^SJT=GJ4bJLCz>Sj;@FKbf zd0kV7wS{wF`LQJO(&GbpQksH3y<9-P4ZAp;V~Tyy%Qhg1?_DHtYc%~ZBb&Ic{DSno zTv5pPRlw58g2-bxLGM65gs)GAu%sXK6Uj5Mr0N>Xm~x+d<18R9vz*DH3x&YS9t4wp zQ$bOA23DCMWbwXixN!71yiY9vowj2{b9NXuo(RXv&*Jcg+g|jMzYZ1?g2 z$NMd0wYP{G6mEr#75@lnJP2NOp5RV>C%26J;K#aJxEb09U!yDFsf`B&JxKxO7)z?{ zT_t+-N&)h!1w_~BKhl4DAI@#c#4DcK;Z4ccupYIXqD5j3pz{y1f14k2?BU??txDv| zeE~7tR!G*Zi=|p#m7$k0Gl*iSD~$DefL=~4Z041c3l~CAg2GC2SvVcKTS7o`Wi6Sf zQ0SofV>)~v45GtyIMnp9T~Os{ih5jD5XXXR|_+6z@Yb0t$T!v7_O;$@emu*iHVe&#EQzbXge;&K|>T&cuG2l+TKVH>Vtigy|# zV~AnM#=c^I-Ne@(>atP=DPDUB@6YeUih7%{Xn!#}F;klUb$=6bGBZKhK_WVkfg47T z6%!Xq9X8v9gA}(4o&;vW@0Kg{g-!w37`%n}WiMcd*w?XNA_s==UV}1!B}mV^@57y^cpF9wX??J2XqzooU1L=c-@wDgR zU5(#&%mBA1;_S7-Y$B~1j|v|Zp&7e-=sW9H5}m0PMEP9~l6`DO9k&yZJX#TMQ8`4n zy3pYb=MTAl#vFEEWq{?=DBe|LOa9$VO+F`Ci{HCniWz+-n8)24zz7$VGr}9P8LNGl z@V86n@j@G<`6%&JmEF!aJi`_LtV;zhyuQbh%nLCF{B0|*@X`vT869~>{7jZ9f8FYL zyipy0Yo)GF46k=PNlc9z7&GjM=Qla{(GWllm~n|86=DBv)_ICqs0|m-o*>5kVhlp& zYU<*JYov(l09|bp4%Yb$vf|xr(j4%MIDE?nJNYGjYS$71l;&nh_5JRIj{ zEo6s==h(foueIBKQIX8u_=Yts<>$_`0RNp2atUgasz zBvRGtkVUPvUh*f#_5n4%lBpH{6J^4jX5Yip-(=4JH0>hKZ_OYtpwgN@*J>5x<=r}l zvD78&q}UFg`Qjfuy%`~l>)ZG68rD~;rIQ|U0i2Vd_7W0hyI(YbxvliE+(~8^aLX+^%PaSe( z4^cHYqVdDnPJAHB0ZQBXh{%}W2$lV~cf%i?BJqfjqys2zrYBW4u#diSdmMk!;z65+ zHrTxkBymgY$Z<(~uFlau^18*=-h7n=-Pv+Z%pRT(^0t1ohs-h3I1){!$-N^j3%K}Q zRXllH-AvBRn+Km+=_Ebt3|+V=pT7Tm0h!0WO$Qv?Kx&?iQ0zVp5~(&5E#G~WzHl;; z)Ei$W`#<=z@&;W5e*%(NoUr9A^wfyeRnf{?vhj?ddAggxx7u>exeOh<4-yLm z2J6NI*I2h|`xE}M($%%>>R4@p!@1Uvk-$Wm_y6t*b>FLvr9^M7eT)~!$IgY9C(NIF0pjt&y3@z3aK+aytxXdUc* zSV|1r4B`8aA);q<3%0E)LX#8HF{i1Iy8ZK}eTYOMygDw{3h$8mhaAr!|L zuR+phP7s469+hPA1C@Ps!;?}HQOrOkF>7-MO^x{^Wc(7Ya&|!<_k@D>k#y*p_=+Z% zU&TTFivZn_5Utq#5^CM$5tF~6F=1s84*r&kmMe&JLu!WbOw6>;Q4hxvIa5*Q#%?-2 z<}?^mHPAOTyrBCub~0r@I3 zaP3<}Iqxw>FP_%YHdExO3)|bN^8K^1XOz(4_Nv7Yxmg?K8c%|c($8s|u@S01W;Cd>*W$}zOR48)Mu^gPZOA+>3G2G=;FiqOm@z{ZyIr0H@(Fw4dX_OhB3h5T zuQgEjRk)B@Ukb=<6*B%6hOb;n!BZo5z`?dEI5t&}&-9}B(YrP?UIhj4gy(&E7Ey~C z+Lu}w5#$o@{?@I$+X8u}w9NDB$@-Z0_}@ncBV!t`P)(n)Dq)y0@cRkxY4gk~Yn5D{ zJ>ze6-kH-36X#w=!Ps%evu7)LKfe0#vW{%VD&JVx+GaU!Yk7$}EO*g1<&9{z=1-K} z6^`R`?6KwU2sGo5A>JRur~FEv>JK& zolN?@{jt0}BvzI4(TZ$KJGKX0; z9KrjhXv|ztI*lJLyN>tjWuxWnY2}P^*V)YV+a&ls&u#cCzWk`#==+h^6F!4Us~a!^ znUneU5dpkRD=8@6aTh)N7=bsvs6kUbS)hJ$2F}Qr#wLra(eB_s=>4oQ>XK9%sy!M- z&)FFX&%&g@Z>bh!x#yzLHxJNfUn5#wTZ1fr@Pf9h55y5G%CM4i4xa3khHvS0V7Vko zSYxsY;wxlCv3@4t)~O3J9{qTkdlEjE@DPm`>7sJYZhT8anp>4@%#AnS#H|^e!+p5W zlsjGK68+yY57KC(L7p;$Ny8I;G+{Cu9<1+%_tIt}h4M!*S`|S~dKdt=y%;uCm%)-5 zE1|BU1%#0j#P3@U?W$HqSW>Fc;nEIf5l_kGy?01oW39bf!Cr@Jtux`$yJm;v%aKTB z%M_d@l%&RYJs@6E)SjbgL`?+0+=3FRO#;y~2z1eUAP2KbHAJuK+6!}a@1vpN5)x#p3^L{wRTZ93 z>ca~9Cb(X-5tpk!L#5O2<2ae}Nj zc}tJq^#jKUS@gVBA5`X_0P}q>sp=QU!1`;5cvocwm-M%SiQ6j338^6mKleNQzBWSM zJpV~DLsemATAV}U6Y&|fKmvzX)zUqIxL8lA^8YZE_#d{Dh22O*AS0{rQtKGy||^g6n;TPS!W3k-EQg9H#zmM%kua zXv^Ma5|^q40k0asCv8~lzR`rAAr7!7x&n6(UZXu(GBDjY5vDxJM0bNd@qfYch(6s( zhCSV2q33)o|HK!Lo)1yn9WI>8NSE586y&tncP2j85Z{ft-Z<|akt%Jtm)>DBa~Ed z7dsQnaMHl(NG8a)q(bO@1osDK(czIoT2b8l3eG+R!4|F1ZFCtt4yR$=DtXLJwZy># zaoAnb583>z!;f1QAys^U?$S>{N2~4e>4ge)7VGrtCcnNcjO$xhN2Qe1tryw~f0arK z?gzaVSVrC!ZeCYX2PxrpC23RH#s}}%efXFs6rGi-`!3FE1tkpI8Gc{FzGb?FE%~E| z9rZPpJyLa>9r#zD^Zk)8XT{27j%KwipZ&*|&$w{AYFdLk!{@^to?Gpk>W86W)yE!t z@&8jW=fBu+f^n(df}gQ5llQxE8^eiy&inSC6aQyoq4nGp4ns#m+j>EJChyNT1AeiM z4c|BQG4J&Ax%}P_PJG{m6V*#x-h;*QBoK9+!KN?Eu#V9l!nAo0^P)sxzn}*dC@#hh z;->w5>QZq}e3J<`-Chod>?pxo zk{4syy=TBp#sl%cOOt~OY|x@-63Fw!4%oPD2Y!4cym8x;&y9Bt=OU(lGW|pEA6|qN zVXwjws92XspWMbo6YiDxk@kHE3#x;#2N@(9qTrTP1-_d74FU`^P;-wfmKf9&we3qJ zuMa4~gTNdZV`riDFAbosn+0dIXQH|`ZOF6N#8HfNbn?x4IG&e^96FCc)SNEFi2j9U z?#O}(elPZapic&l-M~IivT@QM7g)A^8h7W*3dqsTp$qjZ@a(I-*k?EcLStv+!0j}? z(o;pcH*&emN&wlM#!5Cy9Vr6ROg3r@rd;G6-vCe9a37)@vv3GgE6HN-8y?%=;O zi)6jKM62>Q(a-xU9S$ZQ#|foco7BW|6EeSl=4E5LnJIo+B;CD1gyQdM>!Bh~ZS@5~3`M8L` z8nT|n!Ml~3FgnmneSc}z8uq!McFoUppz{l|>&85MCPoF$jSmoQ!^t>q_zb=J z)ni=fY>C=$ErZn30Aw87LGjB>xHeAp_~utD5Vp4>$KFrWq%BOb6NC#@WwKbjEJt7G zUBki`4X}D?JA5)-gTMCQ#Wm(?-1d6~FzfpyQ9xl6b~*YH`&j64A8v?&$5R?WxmSr> zoG8nEc(WASh_j1lS1LfDp_m1IruBvn)_WxhP(08O_JKw2;UFf0tcsU7^=c? z$GIjb*wzQnyvrcb>@W^pYf4@%`$ufA_kgU_9nw2JiuRki4AOII(1Pi$pw6no8~54Z z8@7s~$ggbl@?{n<)})h+zOO_rqKvxe7z=h>fVtM^@NNwy*mSxD-VVg0BkKadRx1=w zUGR$-JuL?PRed;Rdnu`}Xu%)*1E6qHI2?ZL&ix)>4@p*cu;gM#QONH^@R}6Q-FQff zd8f635#RIPI#@n{$8&qa^9v1QIKCU;O}k&iD4nOzWd6RwJ0L5^^!3@s2(;V77(OP! ze^6}1*I)OEv7%)mW7;iE{;G9WJjjS+>I7UDT!6_kLi9lVZhONeXX7*F6svGf&`h4Vi z>Jyn6c2(@CGO; z8B<8-z?WiezB-@+gXL*4Wx~-5y6Aw=G>67f#YDtdA(K^nG^O>AGuXQ=s zBYoJn4~Mh$FK{`-9hU4n-;(X%eKI>_gvWl-QOX!TEXP;)5XFeuBgxR}zf~P{q>1M- zHitj`asf{PTk`i5=P*1z81j}a>18DNoAS+VYZ+H`-!f)>Xkw&50&gI4s9M%=d9~J# zeY|XmUdHxii40vu%vWnlVN85)U~HLWBjU8nigsqT!=ID0MHlO4iKy-j6g;MYC(G;M zh=d{3yzU)}GBBZi1PE6@4Z_L&p;*Q}1!p8Wk@nLwaESX3Y#A<#JHE=|R)HV+#k@s0 z1%bdp^I@`w4b^G%3Sws-BWjYmfD61yr|tmS)iZ(q9{5S`dU*p)vG&JhYK7SU zw5sUN2Y`B&Y%u(Ci>}$!2d+LIT*tL?-2EDW>En~Ri{rjihi_2aNm{$H`7j5cq+D@E zUJ|)Q_2QFefl$+xO)}RXCFLH~ux7<|6l{5sNCnuz(2)(KQqKs?i$Y2EjzPkaK=zLp zm6KJaCy7V1qS(>-i$*7=QlTpwP;^2)(pr;2Wc;-0ZQ?!PmM&xBp^yz7#wuWLz=U>- zY$*CVS=`_1gRwV1v0920H$eLlwz+r}cT97{0oRV%^}Wru+c^2Woy%NbJ0+7^*7J{- zSi7dS4z?Q z{rN=Z>@jpgemfPbD(*^cw}u9K5v*G~OlSUGOOLethNF7nxFr5IT5Yb5-%erR3!D3i zlW`kfIrS#aj-+w%^hE61k&gSVRJg)_Sy*#(HC{Ps8*Z^E#DDB=aDu#cVr$6>tee5Z zZ;2B3&!yY&?olSFE>su$4>dt~Z98G6ZHI5V^T4opGbo+NCqZk!l0QOSsQl{!-?Lbd zS6Bjbrgg&KxAU+!h`(XQtB8}o0PMAEz$6`ml>!TWsO5>@IyTTR*#hU|M(~!QTR7=^ zIu59&#s2NDn9SwVd2(~`<-a$P$BKBIwR{>9EHwb%)OMmjOY9e`*M;j3%Hj3HtxzC- z7ce!R5sOoC;FVbhz9rh+dsUL$L^l;~c;F|zT1kTo&b=t{i5Q6DcNqQ*XMvAmAI&f> zMQyXhy}~{&q@OJyeSK$0E#8P@cv*1poCJ(oOd=ZJeBgD*MI5=@78ZQ71O5JwsC{)T zP$5$mA+y{vEWOZ9R7Ky1`pE!ih1X!EA{Q@NDJHlz{~(4? z3+J{ZK<@1dcyHi|8>jD}l*Jsb;Hr;}L#$#4;W;<#J4FhQZcC-k-oHaV?bX7kKID?q z)k_`bS4@TTld8yQvmyB!q6wjURd9Uq4OE}@g3P|!480-Q#2|AX6o#rpx^5A4DCmka z``Ol(<0A~|`4Y@~I}Mo04%xi*!Ey{8VLW5;=on+%#G7HJ=gzAc|6w&VcUJX#=>^s4 zjvK7g>e$Rx7lwFe3)zh5!4ZaKB4X+tF=m!t`Nl|?`HS&rFo8GK&z5>tva+CW`qMbnAf zH{cwlOuEVQ5NujDolMxyL6QwL(%-B|ex*plB=MePhe;l^nN4QbhL*Aqo_NdFoc)|_ z{O&k=&ceF7FUJnodCom2tWf`9Te<&{aBA+ux(x>Rgj&V*cCvft*zNhET31waw~iWo zXgB5YKKAtcUYXI>HptM6%iwv{ z%kymRon#o?R^>O00^{3}i#+LGX}-im1!h>&G#+n07n((9P-d_Z{xMkrVrBE;!-i() zo_tVNZO|=|6mHi38rKKS^|R{$`kbZxgxX-iLOl zAIA}&e}ir88)B(>h}zl|hnl~N8Q_i6MY~+Tqq6Y|tW7<@HQW91tKEme{L3d|SY!<+ z9x37{+B{sXBxbpXu0t<6&o_RF5zxvD3$dGXI_aIrB9zo2av*jiTKF;-TeYZwala9< zR*688g$a27zbY&rnoL&R*h_MkO$Ozq_3(7E725N%0K9JukgqplLGU6G{13EXaT*3U zn9snjCTcLaa10VBXNVb^B9u3xgj>W?G`ra;P|sWdPn5Lrg_=`j=GCJlu>Uwwd*uqw zxrSVq(qSx)P($u{CsE10owTU+mqYQh1k4vZcKxt`NF7y&FRrCzE=d9-$wySDVL4d` zV%PU9cU&&(kN)H6!TWjy1+zEOTYoRa%$m>W9mgE!7+)Z~Kqpe!ddzNXQUa^bU^7eO zs;%9D4jG%CpU3PvP+V=?fHA9d@>`Y(UtXA&=xpbBWSwwrV!hx@-89zKqx%H+qcsIp zCW^vEet`o04UsI1?wx{tkDu2bd9aM7r?{oo?-ydVAJ3@u-ee8eoS&ldMa$5X=|$w5 zegZ_QJHhAbipGoEb;KQCIqF4x4k#TqB#Dydbo2Ey_-OuGZ16k}CWjnA3+{fPu6K1} zOEiHRQ+GqqT4xgApaPn&a?l~;Uno39iKKZhCM;*Z*p+pM9$nXk&u-1cBde6KLcJuU zbc$z8v5S%E0bR&{I~C9Jmqmf=7a(TdQHNW`55Qw+4%QKkqg=})$i=$S;mt=65N19^ zFSQP%UGl%s>9HKz>#iqQd}8BjWi9-?$P{b8m;^aHmC+kYfv7F3qFweXB7waxzMh$k zr^nC6wlf~1Z5dAJ-FGh}J+8yuJDW?non1@{tEb}6;bq9^)DT{~iG_1#G}3%WeP|AB zMy125+?3^)aeI_AR$fs^tlS@yDZQDfgHeEV#q*l3zWdPZ&LV>UmXb)-3``A$kIix z-7%(QU%DR-ym|ruZdpwvB2_`6au`O{=7^?tWI;lY9r8~KAbJW67~N|Rn-(jG7VBOk zF(doPw0$DtC*@3k^1KX(pU#7o)J&Q&Q$RGE^}*ai@dJ^4bw9&??lOqF5d%8X-hi^i_x#XQywYn8 zKKv^I=)>7y=~D@@{I2Rh(=3_I*RNX-8tCy4m2>!V-tCNk2~3_toB{9JTMyou3#llc(`_ht1=A$NjASvP_Z5dOwT#sK1D@eD5WO?dCy-OAmv8ZBGv`VQn`f zvv>x7Jcs6)br0eOBQbNh3Q+9w7Y^Tl--j((DYzhuiI-0k_u$r7)?eQ<25OCwc+>c8 zx*~(3mgkz{tc91svCXj2!7c#hIL0#O7oSum?=$b2<+?aSF?=Ig1+AI8LEq z9BaALoUw1poRvSnvz;D2V^8DCan3Gq;*`lhVh5>IvJ--%*l|zN*oPAu*_vE<|_>6HKXPzv34dYi%5^us-z-yYF$hbL2f-$?aq&i1060YWHV!>=C zK5ur(LB*dBwzucPxerQ|-lY)nyR8p7yq5sef-+(hkc2``V|>gv7scz8k!urMz~(5A z$W<59ZZ%F2;(Qynn7)Hk6?=h}HGKc=sBYXDcW5JgcZM`B zQP!sRdo0G1!)LL^kz72o-w*SI8r)jOO1wYf5}yA%4A)mL#ch76IR0TXIXv*5Zi<k{o;#?q?K^_x4Y=oc~@$#3c5VJ$P100hi71mqn5;0RLrKt??U1iwy_eI<74UiK|Q9i~Rui3~t|0&pRNs5M@da#V(=>c<=%D`n-`EColS0N`U z_&Pus;!K0`Pl^l}wvg7y6`<-B4o|{QgLF?a82;D}Mv+YDlFo*R)lpD>bunbOpQYRO zwo-;=Ux|{}E_lD^E2%43iwCs$*z}|x_V!vLc6psck2gwlzstDbQ-{p3a_oFm^7%cT zSmPpgch=ylggaQ3U|y~O`<(~Y?FJ|NGJhl!huFDhAeifo-`i2sRMC%;xLqy8@T#~I^*mU!Fa z?tQ9|Jg1h#9ndAikJiB7`)A<9kT!NV48@=Mj#%|zIP7>ajxzh@@usXG+`7Y(GVY#A z5??+eE*Y1owtg|6A!eV$%K83{ekTlwuwgMNchE%B@*g-{x%LLFUvrk&k0pb6F$E7! z`H43TozyPWOk(4WKw zx&gKynptt5gw7>J-#;}ZHctn=&6~-_=g09j%eh47_fOI<-fM3tPe2bi zUN~|in?7c*PN;qA4L*zz6kfXpFMCyunb*p2`~1^55BFfd^|jbdI|w|J+f*XfNqa<-mv?cyh-9Y=idWUW|x1fI7Q_NId zfQU!~k_D~AhI0mUCgr1;iY0ir(?Zn!Dhm&m4>AmX65bk-4S)WDL%f{PDn`UN;3-(n zWbQqq#Fx6M&U}1QhxsAqDuX$*(3-j5h`;8~Ag?L=5^w+aJ&c%b^^A%IlldkII~iJ#^K2vZ z>@fxFg_A}6l`eSV?1lQlG$lBB;0VdTT`lgC^-;rmnlSa3H{4RHB6dI8;Gu^tHeECi z$mK*>-rGZtZO$WiGB?*XZ22M_%U&nDtL01ZpE@Jn_VGo0eZQ@|(hX94v+FOd)z_5pO3KH1lT>P~6uN|r znK!TVxMx3A4;FMYRKCUVE~Am3T+A_|v=il1sIV3`TD z$Al9}+JvOadlJd@y=dyfmyP?n6dHGUo8g~{6R7!LJNBuLg!#+{xRmEsrj=`@+T#>s~E+zCVL3edLz)lFDowOdkIM9JZv|eFP>7TfHND)?+a?p663ohUW zr2g$KGHXjCM#^KLYdQ_g|nw@5b~~s2=_f#7yiDs zUiew*m~g&*vatWz3qi7Tf#B6ofnZtOD?tJi3pHlF6x?|t5PVFG7K)z!kD~Jqr1Jmb zxKU&zDIyXYMk!=JpYudnr6@&7sie{uEe)w;lt`hhjEtgTg>bpg=iDNp(lAOTQAE-% z?eTkl|K9&x&wW1UbI$v`UV2p{bEB*BUE`|;|1?!Dzxx$t__Z^?z8B!DyPq&@)N4F% z0r0q?mxl4RzWT#zOq_WMr>0qA_Of=I{VosV7Hq-jNx6(#TO#TdPsY|h*4o$MCHHqU zk^^@}h<3=Qavp|D;Qbg`P@gM7uQ=(!zni@8{Fw{m{b~;^%6m)JY7bjS9sxR5uZzsk zbp`k8baFSNn7rTa&Lm7vBk|d_v}K$uxtx?k_s+a)z3iAY#5((NZJh)mN&NOs=_duRS(qRm#2&aD|_Y{pI)wI>=L z4aI|&dbYKl-WKdW_K+BzswR^bePKN7u5)SGm1y*36%I~G#?JTe(e11x{akRJ_!ZV?>3N=cH7!OmG^Kgx1phgXNL22k@hA7a`sFhu{{z@7l$O_$soY5 z&)%CyPV%Qp+Do}Y@h*%`J5KfVM>1}2JBhAhH&iO#gzAsyA^!yjaaL`RKE@YHCcgqh zl`y#WI+y%(-vOt*u9CZXH^AX$p|zvhO*&4~k8o4o&_{(Ap!cyYIT0L+bAG4O`CUix z*rwyC=l=#Ty)Hq!FJ|~4PZ3pDiQrwg6YLL*s#&&U94y_NO1yp65#`DH==#Rw>UgJy$TgO^C0W%eF#;xfR>8`!mAVSQ7JtiXlq-@Gfstg zf7?NfoqZ8i+%if#jKxeM*NT^C``6X6&a{XtX;*tZb>3Vfj+3WgM12 zseEl{K-t-pKs?~1kBJvevCVxSE`2Uf?!`aB#_k$iXHkH|(~r@AI{7ec+kua=Gw@7E z8~u{gO@3aGXC-U;s6}B2snqKwTaU+q@jjM4>z77Xl#PM~B?b^`r3iVKd%;%1t2(gI zzgn)yyILmFr23-%2SL=YW&v4VCrC;x5~Ren3M|j%3s!yz5FC^<6)e@N7i^oMQoZMq zVfBc?U~~PfNrF#-rv&y==LB*wV%4^b##9|Y;3sgpGTwZp=5+J42`} zmy2oj8hy5~D)SVmm+qJwZtP+2{QsRolg;Nsjb-mQmsPI$T_#G|X ze?S+0I72O^YYM~F!6Q&Ko6i$2 zOQhL+Kk$`|5*pT4(`Mg(92u{Ghws{xv)iUZPF^7#?ytv6$9)*_KoWfys=|dyMbNN$ zhDbj&7wzw-QN!qR2%g&wWBzKhi4y7}E1{F9o9A4;e^zIezHB=b`q`uPjSo1?lR`Nk zCA^<%N!)xZNV?aXypC1E{^K409NCakFUJMIPyTQ1{x5>e$%IM6$ zS3L?2L&n2wvg8|&lx`YD#x(2W)6GZm&Cetv(f3Md-dRlVKGdV~qsP$Uhz24`5`)zD zvmyD85?I@`G37}kN#M#HvVBb=dC1)(G98Pd>01Piy4TAjWk+$PM=EICUw-%Pz~`zF z`|YX}G)t-`UT~{g^ftHZp!9(%mxsG6Q?pB|6xoWZ;R3@dwa%8RLan^2@B9qC{Yy=i zo$I11B@a!3YMgylMPp=D!{^UcHuFzcEju+?aPe_k<>ji$RTCWMR(*9jSs818ipl3@ z;6S<`RlOETn@oSw>!B<0-<2o0d?YfyK{ud?DwFO85A<=VMfWEjv{uU=#K!E!!+KxH z)kpVg#`{^3O~zp)>v{ny&z=gu#hZm@kx67}fDJiRdI6_qMuL)sIh(a)8awX$VY=es zYy7%LosIoDoy{ODJE{Ew>{h5nU)_4B3!BEyx%Ufhd)xAgo>!PD_(i20mDr;#LSlD2 zm8*WV2A_Yqfd6SpL+XklS|#5^pN7kd_Pbod(HE4^Al;H4n0=NiDNi9Cp=St+z2au1 zSm8AtZ@i=|&5S*K1}5Fw2pgAr(w+9*RJ?qUc$n+Lmumy$vi(HR8$E$3Wdcco!%b>+ zyPFeVwgHVpno;#uFK%gCPc;Vu;kW7=;y-yN-TOQZS4Py+{8n)oX=XucEyPguw+S}h zkE27ydBk+tMWT895g9~l*j~IA!zBE0rv7;1{ptx5H}W0%KK?k`syfgZekNN}Q3yKt z5}fEbY}A*+p;;5SlC`NYX7_a{=y-(}xBtOJYd_)Qpak%{DZ%VAxx>(wUUKk8F|iu1r@;*Y=(150d*^(|(@tYVkqX{i>vJWxqC%Y= zw@F3xrfZ7mNd9wXy@4ESt+I$+F#S5(EEV7c->*!a@=Gv|eF!;SzSwdt8pk);5rJX~ z$ZqlmtLKJf*@i(fSiBPrT{fZ1+faTtX>UpE*yo*5$#Adf@2FB6zedpZtiOhqu{8JlHTBhIrS&-%qjR zoc9=1?%a>E`&y_`eFR=<873DmP2pYfTS=hiOl)XZ=V#~I5cF<{UhWOWJ4MlqZP*pY zIXfQKx6P)%-`+yo%TAV-@26Of3$?Vo_t)0)djC4hM$aM((c5(Mr5PX0m-oG~D4nik z=_AOrs8q`}pDi)Xd^ufeQMC4(h3@Yy7CuYGETTg0njc${Yd-%+gGGDtJJWZ9krtEI zE-@=wy4%7c(^o7zB*sSmY8#9v- zH{7vGd^?hza^xtf4StKt89`LLuM}n}A*>$B@7T7~p`~dBxnMMeUax+G*kUnM0B$9xJbJu zMZS}WEe*x<3Ce87R!X+ASMjV_3+$a9jf!%-@8e`OID8Bf-Pn@CIl0efnd(3^%N`{< zN=}m#KIxb+{R7-It)V9G!s)$`G2juK2I)Rk%%6J-EGD%>s@o43n^;A3(`!h~eP3L4 z_ci?_+Xj)lClJxgVZ6fg>$8=YqF}}woHB5XB!ny=>o&aOB1iuRxle-d$2B4C`m_t2 z>NAN#`dr$ZD~*Lewqt_Q4C}g2t(Y3oXl*h&pCs}5lTlL+pu?pLIQR5w(r39EgXgpC z)uG93+4XR4>~TqUS-=(W-K51Ho+ZZ(_4r`s0aIMd=zy)CG`4DnGrhr1*kN{&s`f8t zTXVKUu4^W?>15NzZ<9$~(0q2P#dOikv&Xpk?&C@3Zhns>%bp%;JWM+F^0oiXVc7U? zHcs?=LdFjo;EJjaB$wnxSw@#ZSJ1&tQRcCxLijRsS^rA7aX9ytAV>|XhjoNO@n!RPQj0q+sp%x%is)43xB$5iB4CXsd z(Q^s_OS}AV-Gdn6+)&_B`kTQlb~Jp{xr+^FpTn-`ay0oVBKF&cg;VEsK-Z6bc&&LZ zdU;(TLTx!3aG#+|rXR;O_cRe?KS5=D2S%1=SWPVi8m}q^z2T=woNgsbhvgB6gb{3= zXg5yMvBig0EZB?p@LIn{Zb11XYF(I$zdlyNk@aS5Q~fG3rTHYCd~OrSx5%p zi4)i}iMO~H<$Ksnwkvij>z>zIE5_5`yKU|R~ai*+dUR`C8*R5ejx#9!a=aP@NbH|EqO^-mu z8KGn|oa1(n>8F2--r?yz(HOty9|k1zVoN#}D&Roy#F- z-y|}hJH~xtPYerbpO_3t z>`%fkliIlNiLrP?^**k;;>?cUFbC95aB*a7>-gjyUWg<41qNX*(PsxT_sq zf=7$oEOohSa{V;QIvE~Ja-?!sP3hUr6=bEk0d5qWLHmzOVMWw0YB%W*gxwlR4_S=D zjZ18a``?uqbx;C?3Mu%vbqB{3*x}8S`pm-SZcJ54piu^=@c81M_N-+D4A18SuhdvxWg4Ve|ILT=p^y}AJFdn+c zvzxBtv$ZYaU_?%a$oKi;OLwc(f~bs3&T{GfgQ+wtFjbMdQ=3Ke{)Bz;}R5X2{W zRpk%hEYo+)hjARvw&u{Q>meh3`7^%tzJk32yo+^#m$m=bzryT!<(S5^!9D#0aMVWL z`E*4}lzU_<9x(jKB;0hx>qci$Y5RKk5_cS_>N{q6%6xo!MFtcPrqDfaqiNI5H6UI) z1I)~nSTC7-bX24mZgEx?y*Cx(w(nJ^S$*}Sx=R8*Sq1Wr@El+xR~*s4lO&rB(?fg@ zLW#d;jXV?s+YV;nA+=~+xc>ncIPNk2Q8(l=Zmq|Q;~hZN>JTP8NI~u006U7_;&HX( z_;=Gj)E{)FyT{d&o;X<|xiS+KKCl>pdO0c3=i^NUVg(XTpbdO~%IkI^_k!dv(Rr{)9*^bj# z7daaKl}L%EDeb2N#UB|dLnWO3ghMHtix|G^7}?J=OGhoz5ShQ)&1$@?;-(dx#^KgD zG|$SR;-Ag&hTm#*tycoSPsg!tLmOA!avf4TMqx-RLyP8h(zQHGFyV;}JA0)$u4)W} zfgSPKcrY7>>gJLY>J;;zjN;5r*oa@gx?i4>13xX$%p?#OeU=j~YaNH7+e-06=y_1jIu8aKH<2hsg5>vV?t&SgIk7*7 z%()S;E?R+2ko$)bTlK=)p^N$Dh{Uyt) z>DOcMPu`JZ@`Y#k5-?gU#FZM!DE6%s-@P*;uiLA@?Gx`X{8A4uosQFThdtP|Sr*-X zxxjvfN}_x;0B3coGD6@zAtDDfSe-?6*OlSFCk7DRG6#>gPlQPoOQ3n~FDQ%R_lPZ? zkwpvYgtbTAG2z%*jEKmk(^nMZ$v=g-_pTEOSQyQ-!fwEWc}7^_Go9HH@d?gPm;qRtW^;uPIIIuipNkp%QR-(pcoxE1enl&iu^g^&NGs3aZ^-m z>6Gj&sgMI(uhOZm2>VYQSRE@;^sOLCi!BJ{=vlz~&cfrtLB-lg*2?O5-lk1)V z*nQ_LeXG9J9@{Zg=O~OL?j&*qBWFJ?ReuSA{FcTi-INm&`bkx{Wue~z74W?vOUl+B!5{ZBz(2zk zC0u{eDSnP{+rpZjHChU_I}*7+4%={7%|E(wKn6W)8tAG_Ka8DdghAKxplXpivhjCe z`jhXRhwU>|;WI$&vkd$`{~Bm_l%TGV(r?FAS>3ziM4BP9xJBWcu=@}1bC?!MMZF6d z{>~#(GgZf3m(pNVxe@pBWty;Q<5)Uk&Ph&d=V!|lEy`W)xXUe!y}}u4r8Cq%l?(FT zLviykm%dKOh#h^!#7}4F`+*bREXFN*o5PvT zKg|TyuHjE96fj{$NJ&gZ9?Vyqv-bL z+f4Iq2chUu7sn-@wst%!Njq!z(wRZis9BE;_t(XliX3kXzwJI@Jyv%+qv}-1X(>*i zI}UykuAWsP^ss2>COm28Dx^!e5k!p(U}d;Un`Y}DAp_R-OL~Mmb-lUZ_CoGfQaqig zWlS`8mvY8Rs?HaMjfoaN`JTGTzC&4&1hf3Wacc0Uma{W>!O!Iv8K92#W2SQAayIQwC7Cm+gm$_l;hYcG zss4pDk{bJfS{Ht#S<}Sn$;1;B#DsJ)*w*kcPVCv@N|uPWGjBhHu|q;tOtaKNdv-Zp zJZwOYo)>Xdrm;9RQUZ5<&OwV1e!n#A54B34N=Gy&k(*Y>aeYlAj#zhsE=k=Fdes@U zpm;Vso^Xf$ckus4r35VLDkpk=CU|Gl6AQ8 z@;f?MAI;J3K1#RlCX3al;q8&HSgQ{MB)-rPXG|W2yFC1PM;q^NKC%u|SO2kIRj~yN z8qy&vKn(A$7>Ua#>7zFZ#BpO5ARAapZq9#7!^>ZxNp2aMO$mqJKIxpZ>oGi@HoC|8hym+xn=SUw+hr#4{s7YDjnshIeF?*;Ekx#aIJ zB{aDfixmbU(!DVTwlv$)%H^>%wRpU$qq-FF1)Jr`J<|-5X?9O#u{sUqs{MlemoJ^>Fgn zbo{WA!*=UwO+t4Qv&(n?tWWInt!Y-gUf ze54vSJ8`#qFx{jlqCNNLTlWkXFwsFyu&~P%W{ggS|L$bd=DD%d=kg$S_$iAV7dU|O zwqUq;?>kAj_Lcjw{G5;%5s>xahse!=#WlBeh4?1i6vJy&=+gHIcp%jt*2EY=Ql%~Y zw$x;mrALT51HW@4Y(q%z_+Z?a@RY8v2mvGi|6t{4-d7-|0=v)G)2}PakV|+4()wP+ zXhH{B>)--XZ$nUS%Od#A1VWqU7b^7KhOTP;T+h{Ga9a5;WVJL_a}(CWmCy*78QhOg zvL8ZxN+LZH+)qP4h0{)bf6$r}OEVO|(+?80_%hvr(Tw3a1C1lt(gUC1a%~0;rhr zcPu`1p!hdc8XT|I;}>4B(3z0P{Wa%MyJ;4&$Tz{fSub$pk5rI#`UHFL$ilq^4fNfZ z7^s}pPyCggLG!Y zjTTI?~3))jaDu6 zcUd+~TUdlqwTIDfrxuZZv5l~QFQC85EF5!vD@l-N$%)a=h)S0(+qTOHY8LJxhE-{F zrzioTTHX{tzn z+e!Q@(MY#>74vh{U$kz|CVJv<0ffXU3EZ#!6kLz$tvdYdnT1qgr{IkI+mVnNA= zSAxZL%LH?SCJMUkiG`q6$(Nn>x;ehgU za!vv@>FBCJSMh3>n?ZuJi#}G3tk4o1UaVfFKijg}oA8~8Iq$2Y7OxU4*%(y$+q**W z?qZo>$6L1gQ#$WEEtn(NwR}KuxU5}ZX#Y}xi_NM9^Np&fs~i&StK2Gx|6w3_)@@b& zYL$4ERKO8I#PY?0ZTHNprMoW*Ru}xNlISTEXos0pCumz&8$Nwel``3?`pEX9g78s> zf)DqH1!uEU1jktoL3Z!V>ZBQ-g1zfRf@kA>1P`XauR3pXThOQHYCg+hWc7cw>jX6> z^Ud~@-mhwMelFNQuQOF}$Y_aRRAG)F^3GMkm2nEyr`{c}3fwCuI3AH! zeR#QjmCD}Pf`{6H)$XtJ1>b_+3B-E}1a||~t9RV|B(S??P*pj?MsV9ky4u12ksyM& zm}$RH6uhauBXDUj75H6#D98&5uj<(SM<9N^Mo=5b2_{Z3t{QF=6C4RmfIiIwFnM|l zdPHS1@19(tR=Nu4X!xK?{<$RNX(_Sw8gdvJQipNd^l(A>YxuE013lkQq*^zNY4u}C zc%a4i!) z`JW%fJ>ng*={^5(-@K>cRu>i2*{*?8&v(;A^TWvOxIt#C{K5ycjHJGj!x4M);44(& zfddgF+3h%L?QSEBE5Fl{)`#5jml2}mDeCNSh%XpcgksBUK5J#+h?xOiG;*a3&pAuN z^KU<+x_2?-Z!L~Xr>a5yP#(R!E0{iZd`8@P=id~Yc+~H2gqSmmusL%p9$wbMT=-W& zH;g^NcHLTr#SId;#<81LZ@5A3%ow7LCPh?ni!OCaoy%5z8Hw(xk$Ac;9^X_5=&E^< zY(sDX^VTdHy=5B_m|*;~oxh77O@>tEXzH6E1I3}c*`?)nkQB2B>Y6HWpXE#*d0xWx zO|+vf2j)c3sU_aN{%j^V7}}ThFJsPrs78eE2C`x}itZ(&flg?I7I2vr7Zo zLa;~cDL#n&hW(yzaNOH&c;kE%7H1uY&m(SN)8lctZ2fjfewx8J$2AF6+;)Rz@E&*{ zRRkB_Uqs!KW$?M64!^lg#6QwK-0l(yCQzE9wCzl2PYB}N4NPb@)qr~j z`S>sM0JY6i6bW9s(Z16jK;;iW{%Arh{06B*Up?w)Q|fN*j*nc;ESvX_lgIM=PfF`h z{1I?B;^QFSX9{!RnGp2P&jG6~8szW((_SrIJ%b$+&CzlYpD)@dz>LW^P6z}o7rr5>rUADP}}+zB*4{s#VD(_0)BVhq8fai zR@?D`C`>AY$?+F4W%*|iU%3U0qyb~&58MA1quF}iHD^7SXzag16B2pW zdRQ`S9=JkdPE1EFoB;9Kjp*`dD!x$oK}THqOMK_VlK|CU){iBBaMe}^;Xv(3blxC| z{hvoupZ;$24g_fJx1^Puy+G=a8ZFpZhR*_3z@;h_g_V4^CN>?T*)A~XPlu#06HpWz zizTPO!jZTZyz_o4J#qODJ;>LF#usBz(xaM+S(#JiX_1hkHi38@+s+x!aRO)kX58X$ zgEAU3iOei1%#V!cW}PU4gLmabwu$<%`(_tL`E)?WBQfiw=rG9FJPJbA2QFTVrhfF-A-_^G&C{uuo5wl^RcP}3OPny+rO+$@!6XB&%1YYf)OY6Iy z!+7n@_=Bkir!A3;mvk1-KuZNlZ&?^xv<&`RH-+A6U515$VHmf67Y2RcL2{8{)-%JK zG2=x#Gw=Rb5&7Z<8-_=~m}?anCe-0(ByWRxe`m9nS0vGA*(B0_<^{T4D+gT{aZ$R) zWXyMy#A{oMfyn$LeoK3(S=w_HW%Iqn@5}Iee;@NbEFTsnE90iP{n)FT$~B$pq~&K~ z;E!A<3ZqYf%9u2adhSf$m+;KI!G-W%EC{uZg+sG!Jn7U+qi@TsQJ&A=i{_q(|CC3I zGA)+kanFn7=+@Qv@#hR^x%~#WbX&m2cvsA<%qE#S1t95^4P&QBi;NwH$dI>~=v@Ikt-oaydYU{zlhr7|9-2Q^IQf&2V;L zKC0hcKqEbO67K`WPt9;c`G4T;DhbRQ=N6G7-Sh5tGTygROLwmm}6*EG1tQ zTgkP#D{#NpY4k1AgT_WVmYu1?I;Z2DyT|zXT{=UyosKW4Zk3xwJdq8vAco z;n^VvJgBgdi}-GeI!8LW-P^P9^rH2!We*aCuuIfs8UND_j$urv$pY*zp#N;+tnGG| zqg?DA?5^vDhIdoQ^#9gl(T$bVXW}C|cy15Y8HBJ`8&2cwO{#d<{UaHxmIsgbyPTibN*cV1!^6T5mrLt9HvU6M|kqvk~8GN$|FwtUUo;9xr7Z#iDT1B?borhH3ZA zW_aHkfJc-D=^CjVdcGu@Xm{k&!uerXkPwFhx;N>YJ%;#XvKwBSWd?^Io+oQez1Vy1 znrzu%8$Ees1E-cP4O2#Kz_30cBMb_IdV7wV?TjKp8eeHoz6To2?4*|*bLlvve0;cx zWrb>+F(|)_K9KI_@>a=;*rWVx$<>NxDX%1#n~rd|W;dXL)la&*Y68fgG{>k9-t=(6 zGDzO8N{=LK(T|Hf;Ml54{Fmd2{_;<0vg`>OZug0-y=w#7AFhL0`5Ss7RSDB42;oP- z2n^6w2Lq}>SH9v|-#KRN^O&79V735!i?%b;3DLy;eG;bRJfT(_mQbl?PptTMgW8TI z^Z?(>vX#-p-goNkN!LhRBVmdLPDzl-^L?K<25_(Dr$JX!Dgc_38$&-B=X5*NY4Zk# zX90DlM!>Is%2emgMbub7g8ecmS@Y|98$6x#kYtbL^Rho1s7?NH)LEwue_!!4x@ ztqWQrEuDPKAXAY3m(FP{z%MPWhwEZqNuK*FAnPOne=bvgQzI zxQ+%*c}5hcf2Mziwh{IIKyaQk2ezDTrvZ-^e3>mp)1R+F9jBw%>6b}g%?*bg zVhS{{Ko1R4yl7zkYutUAcQSh=&2t#k6t!oFlB2l zGsagPCspZTYnXsY34?IDbq}UrogzA_{+qtLwHPuCPGH{3K$tQ4B}#|L;EmNS#CdBv zm$9N0$6G$3FXFuEO4W8I$kvGl9PgFq3V|%P;4oW)$as2=iGfV@v$Dwc(DOLjNgUqk|%I$ z{xEJq-p_ET52U;cVe+dcOr4mC#@Zvv%Vi3pS>HN2X>VYB+YEQmzQ{6XTk zY6DFCC@HFr)xpxS&*9g?r??}EXaC{PD(Eax{k>{Q=&(_(1%9B5&yGfY_j!VP%kWbd^@8kIN$uZXM5#`=5Zh%YG z!_4ezXYBi^ND8<9!u-5KQs+=i>vGF!Uj26%n|hFFH)N7wv&nqcavUZ<=kwQhj>C#C zYOG!9I2iVC#Qic8F(+y+E)$kPj8O&s+rYn{+nsS!>Hsv$-=M9Qa_obhx)6Fq9U`Oa zai_RG#OXUw-FcfaBg+?`O|2vssur=U^OI?6`ArzyMd07Yhn&{1HokeS!2a2}6*6j9 zlB>mgaMxx#68(J;!|y)8wV@{HEeOXME`>Ba{RAj=6cP63BVnw$4;psOM1xk|QMo}0 z))>2?it~ExY8ZPlLVM}R5O0PHP9B2jjB>D^nGX|Xx{0=?wqI0 z1~D}%>+D6l*PEevlQ+uF=_W$co%r#@W!$}+_xF-a`g5k9$i-|d8{qvOijGTTMbH=o z#~$$H7+5nQ4^`&qvimA*$==LSqQQ`G-cNFgPoLUZ_niJiyOmAAEoLdS@%(^pi?LW3 z(FLnM50dOOUmP4!Xf0!Y75BD!;7-(+%?ApUGzjLiXeJPqM6v zp*tElf2U`*=wj*MPmG8;gD?F&@l^i>+>q)CFYcYjJJZ}TCMyC@O)SGDzOyj##2PG8 z(HBYhU!k#wzmQLB#^R{pP_URY9{vA50qe#r=1qPQX7e-tfVDZeb740Yt^Q5!UH5^0 zeOX#y|CfZt%wUiD^E;8VK9Z9B*<>5pinV51c)5??x%qAf|5fj|ZmCEEl^`SBIAbH` zwUnZI!Bo0kP)SlIc7c1~CI z{>``sR#>n9?m+TKE#Oo>Cg7oEK_utMSGwYX2(~=u9p3V1amhw8_VS%5>u1ZZ;|r&$ z5VTnWJ=F5y=f1l*Gua)zM^A(#PgPNhZw#Dz^4jWwi!W69w8O(!TfyQI!b=FjlpmRRM(qYjHt-Im&y?(PX&};5pQFU>GbrY>3Ox^xpfj$V<-Os2 zoqvqO$Cn0>wl zUQdoDJKk<4r_65AlG}EyYtD8$C^;T8*6D%RzVUcp5Jp6YB-z%eX=w6D8Mlcp;)O5O z_<13e*q z4n6-~VT^u;V8QRzIBClfklTKm$(C%Y^tC#tOO^{m8u&pr4@ z<^^R|SdrSRqakdH1A8;wgH;>0!4c=~bCbHaamP0DEZgcCm|v#C-kvRnVFyYuf4G`P zNnXOns%oZVdKs?c?{fdn-sd=y%jtwP(G~y9;2iI_5L>N=+jDM_LpGl9(?N{A-D80D zJA`;?dm_#;k;55bZ@A2dFNNNl(y?Y;6Lm>(1RP_?Is~nNpW(Sc5=Vp4yjZ-W@eFGs z_tMbZXzSpjX^dZ6I{BnoK$X|DA7V*G$xj03{qMYp+Rj|JSCV~zB* z{a&irF3#M#>QC2m2dxX$;;F&G;c69wca~^3DEyT%l`1>vQn!`AIQh5s^kd9Hn&%ry z8#`uEw`m?6Ka!`*FZ$5kqZPS!`q}zz#!b#nX*{(acv!QT>t#Y$|Kq-Hoj_mQJ4e|^ z)0oDOYw7KUpE=*oV&?XX7;Cvr2{a_gMtDq0owgZ!Q%75M`pd?FzCSaYyVd=btCo#r z-mN#c+SI8<*LR)g#yp@ZDT)R?i)7&Gc2Y^YT(_;npkk@Incy9^uR#UQ#G* z9O&m9({nAE-M8ud3Q4{O8RVutT0r|o9%p*~sMAOG8W`r%OI~r2PEIsBbq;kes^@ZaG`K4! z#hlKAK~74sj7GXG|W$(FzMXO(s7p>-~Z?b?b`I1MKDo*2%?j|lzS6(#Y zW)$4I#nBcEd*<}ucr5NCFY3s^H74^{bMj7$dFK9|9BL1xUQZxhH-%gHDo zr-CP&-Lc<_uv;r?acFZoeUq^O^B!c;JN!r~u`z_)y&6X)qD&#_lq44G$Ktv+7JoO1 z=$!U*DF6Bs-j{z#Pw;NDwtL^WcJp$o8MO|7n*`vN&K+pE?IUd5vnMod+`C{C2c{)Q^iEm^GD;+&aXInFqYo$OUJ!8I;i0lO-60T7+Ub! z8~hix!~DB_p3226}_v@y=g0(T$oGR9^FocvO_Zk|U;=FP;XIuSk+wza$KIjKkuP zt8}+t1RmsP43i3=`O68i)YYC?E~oVVm|@)0+r@=n+y?9K zW(Z#%>%hI%&&ld|F%;$>hZ#z@a6-~|y7{IKTppz=+Sn(}8qQjPqvnTlSM{=qve*Zb zdqN+c&YvzytlACBb z&Fj}jEk4KTcl06s9$Sp-c}w`SKfJHHs}65R-G+AI1gQJy2Zo=DYi{jd&K=&<4M)_& zFlNsKY}v}7xiZhhxSIl<{idK~n1Sy_$;_Fv1`w5b2+JQF#_T{VoV+pwt5oaAr)5`w zv3N~y1*l+csvLQEf@e5<8^H6+Dw%uwmx0`ULKdyQg{t~d=$w)O7R^OCcfuPgl_krb z?T^N!@k;Q<<_`29+zTn^)(F42JZDV$?6K%-2CD5{hq86=@X_XMBB6ExKCk>mH~f2q z;)@z-`>JSYHJMB*-4W(27>ivkS5W7o1``%)jDfcbaO(UhJb7*^{{1RL&v;al)_b=} z+wN@4(9*>v4DaT&=qJJ6Cm?Kr0qvnruzmL`oZ$DG29Hp}W_>T}vh*fK?41a!dv~&N zV@z?eT@iJ7Zj0MHBQU~QmyX+RPc(n)W5mmPaztS_zWA#^v15eDtSTO@yAH5N*TrGT zX-h4k+uPUa=lTk8@|+)#ky zi=@aMmq^@l^#hWl1jB=7vXVE)GRLp}Bex>s;aS^8dhC(`R!%O!%(@7Ymug4u{OyI7 zk7ii3bQRPbe8UCF-#~ANNmRKZ1Z$-KqtVASM4!ydu_s)S9ldq}1ZN{=eJ;UPmkJCC z*2Viho6(kDpg*6Ru;Tm-@YzOP);jJz-E=q&%r2+Xt2r|8L5kwkD|)ERyEop=siXG? zWavOl0sQLI!$0Ac)><3yLydAej(X}qcEpvUyj~~qeXl~F4RxdP?`>fDQi{&toeEP- z&ceVvW!N*y408g`;HAb5Yy!;0!h@qk3JWKr+~tvshWmQ<_s9WiYd;HbO;P7Ag|?!N zYZAtkPlY?zLLp1mi#-ze5OY6$#wWkOl2Vbf=yco}(vuQGdfr=l<@kq#! zI|#a?_Yvm&KbpZffwv9cn?BV_q{?^Vh>~m2zswnDZ&=T%C41neATgS|&Krg28~#Vp zc{peOfQmH5@J0qlsWMw3zL^95Ef0NOW(Xfh2 zsg%`Jn%?vN2gVuCx$o=qxghzhHM4$m0Inz!g&Vi>QF==*#%bxmdgdW9Bnu@PHT5)%^4E{IK6ykg;&|YmmxgdU(8nzXq z*6%8GOOodfM}I)$gE>(3Cehq0vX5!=^+NwCHNcGHf8RbaJkPCU+ho)6)s7s@36ExW zuZ@86=(~74H=I1|7stSxnP@JTL84zqard6JGm)v9H1)A2HqRBn_2&~`M#O-PwOY3TOU~vC>aCkXZkel|v{QdSfT;Ahk z=xyVDHLlK>^lu(H@#QRtoR6h7y5nfcf@zo#7z<*h9r!RWmXTgpjOJG3LF!gD7q#|0 z%8%0*>~+(`Ke|`AIV1a7^|D%0`Yantv&t~-v?Ru`bpbB58nJz!`SN9 z(fZ#8c($#E%I)q#FO4ZomNlQX&YH^T@vO)Kow1$negFLTszU}yu9D5^!yBEfiiyP%}YNrN$oD+w7)3d3E zVihg?!(v(fDaAdJRbk?s$*Q8#?hMHpXzk8(BaxTWr4Mpdh4FvD4 zChjdKVa)Ydls$hGj?_fr+w)3-vu0g*cisk~`=^q1FAx$K8XKVPLvy^kunQgITFCJv z3-r>g!ER|0oWsn3?6%AF-p?Y)Gzh2B&VlfA>q{ z`oU*n8EwI~oxjX2skH;6IqCd+Y!=&V8H3~Vcfs#XVYp+TJ$!H9k3E@(Q2b;D_)q5D z|IH>iQ&^GaBwYic_f_!5X(6^xmIo203*JvF0@HbJApft2{OmgguZ8a5H!LP%Oq01@ zr3&4Y%+HIgQbFHvCih|O0-QKM6rX@T>1a9)7lW>o36V;IcR?Z4Q1k$Cdsa&IFK;8O z3!`AA)mB1&hzgGF-HDslX~LPW^I)ptJiIDWPo0ZJ1*gO9>0G(*cx>)h;!$FPsca4N z^-nJLDhlC2o|#MLe`e!IA;FJ*M`>i{Y~t-R5!wn@3Tpck$ewm%oZ=*eb}uGiK(7|o zD@@?rL=KSX|LmxuRV7|dv_UDSD)^RnjJ3FvO7g{`vE@++Hcbt~Gld7B(jpyWwweoI zx*CowS%BA$gn_=*Db&@zPHjV{fbO5$uzspOOL*>K#qS^V`C=zLC>>3Ew-{0X<4&lb zdJR|nsRa|9gct4|fNG|gJb5Pq=f{=Mzez5j#(RBiJYSKbAAARIK^eK1K8fsgTgk#F zEBv!D7z(G1aFub7$@0i9^0_b%pC5`t*Plvc&gBwvpXY@l+Q!hLLwxR0F$Sfp|8mb$ zN4VN~PdNFbc_<?lhRbyL`4!b$YPYrtfjMlJ|oW3 zQTSM01*#lnkX%`Wf~h9(^{6ziX?4PyW%ks_cMkd-3_+KSlkoXfASNw7j!mhF%tUi# zxVHKk)|?QjHh5;k$@C_E+swb>g@Wr9SYqd%2zqMQ5qwCU@ljAC9!TPOm6$%PUqeX$ z+*X?3m<&;xY}{oyfeZ9MiaNJH(4@!={1&nk#A^>jtZxJc=Iy1G<1%ohCm!Fg&w$YZEy1&G zYOv&hKbqyUvk~FK8<( zCb(RA16&=?LgTLEI6hI0J(~Rmx^9Oec^X-%aOxP&S$CVc-#bcA+X_g+g$7dXcm!Q% z9fzY_Gp;(4M8&U!qkKU$&i(p?Xw|ggoZiLQJ295-ox=dgh~slPX*PKI8c4mAN0w}P zK-Fx#So@$W^n2t6e8=zDiak!iB+IFAM{fy!89N3QEv=c+RVC2V*nm666yw%tM2oH# z;%;Qc$v3?szpk`^G5+LK-28UUUi|0uUum-ir?L5;t5sPeP} zD@z`u!6ik3Z$mGLT#}&HEvfO&1C72%c;2?W~m>{P>8J>)0<2B<+f=0t`Sl`X?euV*| zba)z^Wc`VgSpmLGdxTyW9%9-y50Y;^h;KI?#Jl^};bhGNOivR!Vz>GM^*9yAFC=ZM4T>Sb{AqY`ZTwvFC%+6`E`2lKNI;oR5Cz`I!? zdaNG0e3OLoIm$$?suNCJuz>#ZV3^Fm%e&{^hdT)=m~iY04(s2*Exq$Wnk}h}?7V`f zG6T_7hR*=x?#6Q)W60;1wM6cL5)QQ3a8>qytvTpIT^oA{;`O178la_KD_D?jgGu(J9nNy(o~=oS(D z@^y+pPv<{S^E-kDf=;ksTZ{rZQMi&f8^bdNSgo6i#mr<}bugGlXL_P6w-~IY=He{x z9JB3HJPg^$Yq*oa-r&b~}UG zr%FL&Uq1O7RKk6IAVXRdkFsv!JK?FFF>B+k17nXwgWMhG%HdoiSo^pi6UEzbnbrc( z9RD9_)YCxYDig@t7zve{8}Mv=9W6}ZGZMZcf(Oy>@UD$HvZ=-7{>n?3KGq$~vO;Kk z3h&hj48`@+W!PTdtyH8~2u4$EASz#-Zu}~X>u)KbaPJ1%v*Zz3CWrv{8Ye2>yn^hq zzsC0~gu%P0m-ue*6!q=k&q79$5G<*J_n(H4grf%dRrMV{5;YDtY8w7tOAqW z84Ru7!};8i#`fY~5jY7g$w7ZGgA z7tpk#Tw?U!T2edumCG=6LYo)kz@_&sNuH-oY?}aHhW2n1yT)NrT?0HeaDifY$v7n% zV$0;GaJ%m$Dji;4xwU2wzRunZ7jDhs#w^%Rv>(a|x_bu5bp}HB^VkbM|9u%uVXE-UJ8PMEH2BkI$FRBRiDu;!56s zB_k;zFnp*5;n<8OQFl4Bi#IS`JD+^Y-h`iZg>mzcJJ*)-g7(#F!nFQvum%0#RPb6f zFWLyPN^|j1V+YzMf1oeVr9*x0c?{kij8&WM*rz(86}~PHXy(>QU^O(toHw)vueY|C zse6&GQ)s45j0s52b%341wK%4H4C-v+cZxoX1q~(tq+~Gp#2@6HBSDY&w+h^F>uV zWqcqJit3e{aJkT7oS-EG%xOQmP*WO;z5?!c$cOu(3m{~T4XT$_fcyhRcy??SD>d&O zO?z8~WTp;Wd8H(f*WqV1b6(J0v-sM=&y;jM9IX`A%%EPoUeXpkM78{!(7*5?A+4*q zle4d)?UEprq7Se~xe@F1h~WE{G3|R^jm8@ zr1q3-it`%qd@2BdPx8cIrQy6!IpLI@{NyckdkuiBUm|Z)p z@mR%kb1KV0{NL4Z_SO|Fmr((i{U@P5^aJtyWDUX{;{**dZD`nj6Xy+P;6|P&&rU4B z9nU<`Bt#ANPjqJj<5t7w0$rZPYQdmqykDwv9=mn#VQ6hhgD0sU@xQY{*thfyERFrb z_g!wr202euSjNwVEoI=yxBYx?hZ~NI(m;deALJI#(?8hbj4BIH!J(GlT%mtAwo93^ z_O0qH{^NZVacRWkUKYN}X@Ogdw6WbR2;8!&QSTCizr2LdZJ83b&M;?^cFu%JPn^i< z{p--q{WT*KGmhPL+64#fmOx=(3ea7V^xXbfI1JhZ&e1*q?W>~<+JJI-g1yrNCrjzEO`Ea6WTRT z#lLxJR7dO@#`|=kp4ndXzO5&479OEKUIwg}v;=P9`*bCxdD^quma89(L50=3xHPLU z^lFeLlie?a%Jgj1yCXy{#rxyNdm6m6W<1Vt*T;OB#i%QE0fxHPku|px@l1dR`?SOy zEfp3K6YF~nxh#vB(xD_WEehS#Ir8b>IKkuh64>G(j-w3;^m*87tPZ?Syl+dPV*M-f z_lqrx|Ne!;4pq3D{^0_DE#T(vT!x#NC|DS^nHUYK(GK$ubno6z=C2d(k&lMIVfv#@ zcwj7$XICpYW65}stu>=deLpj)-ib7Tzn5N=MB;z3!|==RCUFf8#ATCyQgt^mHr7jz zbMKe}WmY?A(pDeL$k>LCjgq)Rd?(&{I1yI`Mc`D|AQY+z0&-;^>ePLrdtV)<4@|z( zgMYH{Q&bIR>ttbLp&3k^riC-6T43O7Z`{MXOO+lDVQudojFc?o{ni>(QR*}_#`@x- zQ~@!Wy9c#e{-U4XX>4L-nER`?;guIR&>&TB!IbegX&wK)WLPMRp<2)B_MLpzU+xIz z^87|FI|UPsLzu06)>v*o!JAP9Y}MN#a>05$%sxcv_x3|nEdDm#H2o+E-?c*Ea8Lv{ zZVsW(5`Sa+qX4?klxOa>jzCMwY{>h52puGTQiaWX@PKmWVRq07Y&2Lxz7*;T z3Wmh+g^V>>DHKU9npK(Xd*cQ18y(R@vVnBn^(NjHNQXvk@N3^X?!%OoG~7&@8;G!k zzMN;IS3eF-+TPKFow8VqeZ(g^9GB+YL4k_HY;w{WA1aK1=)br^Bhr1cZw zMuW#Q-%KsQVR0T^FeMMf9hBKzowwM@yZ*?)SNb>PCaD$s2W2{9ps~IjOq@gTvG`VS z&ymL!nsxB-ZZ?gu@d4NUB{1u-EvRm|L+@TB=ruVIaMy2YJ@6VM1;M2F;t0LIg!hNo zYY~@e=Cu4v6RGzPz~En>!BV*dDt_<4vN}Vk=e?vWC9_B~|7|6=#1rR)+tOC;SacSN zWlH=0VIwm}(6yx#ulV|Kb966N_SmF=*0D}1!OxGRvhz8WHdk6RB1}(=PQ>h#tK^PT zHoZ469-g?k!aYaCtOuLu$0%j&xVegZn!EsiCCRV~QVDdP!5zr4DTADrf0z*a0^M6} zK;pz{=7)G9r0kC17Q4+PZX*XkY`h8vir%Gh5fq`Kg?ShqkNNK(5#^z0ba2Tzvanqa zk2lN^6a-IX_6!*b;@-am(~P@R;@xtbI#|em*M~Csw;#|`PM4TP(=*5h;|R`pPCSuo zt)pi;E}(PrbFfNkL`CTh=+Q0>a?iB`ao@h*5B0rP99emSAA!!eNxWMjCGQO#6r8~E5!9zyvFsf+F8Z~HOb@E-j;K-2^ z(*khkx1)DzWEdbe!oQ7{(HBeOm;jfuKqc&JyS%WuCov~*v{b${e!sS zYDcojxtczemlj<4n@#ury8!kMkML^k2I6pH8G5RJ!p94Dp~Igg@X4|Pd*3HLG-kvXwhx}zVX*# zi>D+zKV~i++nG-MR$1U>ey+WEMlxK>SHK7ZDb{eg3HwapHjPWz&wJ}{VXK-XTq+I1 z$udu4$T``U^OyIZGNv|t6sfjj%~k366-#YM=JcDamNn47G+9qTH1gYpA~hM{|-AZ zf1%5)qR~n66@)e2z-RL`(e2tOcf`h*WIi-OcDe?2Yh6npHjHDB$|Zp8eQ*5rwhkx! z_9gLIX7rqJGp>wV%WBX<^j^?`MJe*2yHy(VZrGv6=>T-M%7yA}L$LA@-$ULrnO%{# z8Op~6pvA{9T&uptd&=g8snM4&B$3&7?-G1l6-lt$=$g3_Vubh7t# zl4NND+2?IpQ|0OS#{UC7`ALNS)vkaOH3xA&`>(PjC<1aaodoG({8+=xA0G+~@za1c zuIuQdg+GIs6U$v_PnsNliA&)2oZ-Lm4rh=TTYl5uy7E}MZ-!v&tGOVW`i+E}?!~q( zVYo=dA2eQ1M34p)buebB!$?laXY876o&^ z6-IFB!41^ohqT~T*kO7gP@K*W^+B&c96l^~N`gLDlG;BZ?5@`HD0X=bCVraDj2?Q7 zUQ*NX#0}n0&SsNF)eCIsbX5%J|JR*D!JsR-0N1_#{D1ElS^jN0>9sgXBwwwfcCBm4 zT-S117#~gjG!D^52NTTR!;@T&PdK*v7;0*)fI`_DWK!g6D*YpoG>-|zLajwOaiukt z*pd&V`XLc3A~Yo=7H|3+v5DnZq2RfWAjraui#$<|9#^KKgwA5#(Y_6}>xE#2IMPR- zr{m8zf2s6=M&jA31m^p-K=E4*4e-){U7f2*xr`J(EdSKeS7;aqg zB{-L|ud?`_C9X>wpw%DwTeK;Yv|X>I{&EIv_xm&S_HS8i^ycTcaV7$ZBc@o<8;1?P zV&EN?LLAZyAj;q!iFkDpmbA>kf7`|3_F06FqQ$77y%?ut{f3x6D?F>d59yy-sIcAx zHws;$8hdu*KyU@;xP2)zvhkB!3#veQ$PvI(@;;?deIBs{{g^kI@75df;f`c^7 zTJVv6XcIteP8{ex{6+o?N=K9Ds?b#OoFqN1vJB^BpZpWp@Doh!^KzYduOz>}KW<*6*26h!tq5dD}eA=I$nf3y@cZp4xe zGR7#n#TJ8>t;OQ2z1U`X1Wh)?@oY#PB<0&;W!Wm~nVJZnlV-5-W2#B7tt~DaRKaWV zWmrDx7H({m!cr9}!Q2#Ky6&|m=ocBVSF5d>I7*vFn^9Skb2?0b+ zGZRv6noz$ci&!75rpJ^y&@fQJnkEjt=?*j)T0#EPf5{#GegO@$2f?V!5S{Oj0t$~o zoArfoY}$2B27Do_x&X^9PNJ6Mc3Adg9&l_vc{{WkMy5qT==Ugec-X^v#*Pt8RBVQ@ ztJg@FdN!^5asd-es}R=eV1KwatGCCRQJ5zO9VUHTx_OgOV`I+P6^+qJ8B^w=g zK0x!Ui?L7CtI~D(82Ef~2y+a@=!wO>WY{qt7(;D=f1xk^nXZF^38ALg=t&KoAPCJ+P)~-YVs29^|+&*Z7Fj|`#0CytReVx z{t;)iBNv#d*_`>qa=Ib!B3JtB0vxcD2D7AWy3X|$S|6~&&e`D@`LY6QjQ64GiSEj% z{fkj-djfN|d7g1z zaS31OYoJ(K59Vs!1>>#KFkR{qZ0>f2KXsL;aN!V^yWGd`2W14wOgo8~6@>FIiwRD| zjncOdzY?7t3-Cd4485CsnyE2d$abZ^p##&j*?pBdcx+ZS-s!G@#V?id$!Hb*R<6(V zTv*DuaN|f=mvOm@k0wrbRD#^6J8*2>D)9Vz2gjQn#~zb?B;u+iX4a2KrQW&p*HR5U z(ZpwyT$iEi=XE$kQJghhbDEB3XA#Q-kI0KT+UVXV#=bUq$F+y?e(gWn#LxM%`Du1N zRLM_ZyC$z@I8k*vD3rjNNbbSvHogz^sxI|=+e=MeL}BH-KxU`zLo^iokM7An!miMi zsJz9fr=8n+ho<_{P7J-7S894v(N;Y}Z7~0f0ksF$8g88Cf zn8WINct*9GdmCcMpV{+xt5OT!6X8y!dm>PKuRK>J+X_!6x&O6Fw!X%_ z2I*v2@E5gdDy#Rskh8Iwf@?I#v$3~4c!x3Xu(gxqME1?Y(Y~W}Y>E!a+iVY+%6m!3 zMn5t)Q3B42RYSAGZ!X<)kQiFNfVD?9G27%5K#5KjsM|D?dnqIEvU#>Z!~Qq^osdN4 zyk3nZDY}?c7LWhVtR&sLlt}X7m2fdIkQ*Ix#~}Wi_w=$N|BjtOd*3sdtNjUBv_iEC zZ)W&GKRsi{vxGzDWZ+IL7q5NG{CZOq?XZ<+556LpD_H@{_&J;y?|gq&=Y_vpgvq8) zgK#@A9g=4@5!OZt>!W)(p(b&``_f5pXL2sJbvez9-Y-H@nSt>?U%{QCdV1555a|b} z@Ys$PlJ7PHS16~0`6ey=xzC#zf8leOXLYfB+*r(*e~Jq}cLEk@1VKfk1!!>TRAMKP z!KKr1vuiE<^QuAh@J5_wwjJt^e#Y6Jyx+=P1I*rkWj4v?!iIuqGTfEQIJB3LwP(f5 z@19FyjCAJ1xi#(BZInz6qrBlwQ#4*4>rMW&Ji{UJ^Nh6qLo#XN9q0)aVoy#L#$Q)e zaX7lCvf{xDj5PW{grS-^yEe-u(5U38fHXG7E-{wO9fE(B- zPh)#x6)+~B=Mx&#$(%Wl@ElhQO*4DBgRy5JjwC_T9yL}?DVk;q!eHjUYB0=vkMa&z z(V{;K?=Q3f7bh=9Z;d1*q?bU5T?u4umV#Rc2OwMW9PDyWhm`xS5cSNSe#!fcj&>8k zOMDJ|+LedqxgnJq8k=d{xF6^rAV=~IVw5ZNCRDMx$YDYR!t!-2|PoxFAL|I)j+yM9?E||PNkk`aKf*~2>i^$=(E@`44Lu>{`-9s z1nenBDNT(Pe;_RIn`;LV=f|RBYd(FZbDN`A&vUjF^Ua56C&G{Cr|>a%9X&2fu^-2& z2!^A@1#|m;5%u}A*yIBn$ryuhRPZl@(veKkbtr}uoSDe}a;Y)*PR#_Tw*72+dNl6k zdGVEokKyjQ5nP8?K_piLk8Qt<4*v6CB>F#4wsWCDE}h_z-HqAoecU?Z3L4(G7bL$e zCEvf#hX5@ZR&up6xbF}We7JWW4xbcS!0Z&ouTxq;=h9hfVZ8$5mhL3?@hb0+mNfN| zJ3w4duf(VQ4x}gZ6fX1dCQsG|Vx_wtvmp8#oxA=rp0J-n^_I?HI6)O%erlrNc5fD` zwLJ~JwWZubAAMqBbeZ0$wZ*B;+i^t8*?h{Dha}qDNRU}EnWjYwp{%6Rg3z^F@cqV6 zXb^U$KbGjD&7c_j?TSbF>aDw(X4yd8^tXxblX*o;M^|D>qN`30SNfy{TlOZ=lnNqXzicKW>!t~$=9rQ6B>{LuX*}-nsHV07^?1U_ zpMTy5lgDa%uwvjdtV%P*fdM_iK*v!W@D*cqzfp898b+_<*HCACD$YH?vd#L2aA**4 zqPP}YbiM&z#Z19&O5lf)H3nOVu+ws4&~(mexGXhB;CdvC=0=_3J)~NKpwm7uR%J2B z?z@K-og!F0>lh`vIVh4+%oTO6g`kQpto+H@u(9?IoEy8BY}WC^ZC@qf!-iSd_GKj* z3YQXSOuWOaRO(~`*YjDunJ0<5^|T}03)Q-*zOw~<_z7Glcp`MbG$v`epppYeoZ_QYfwIBE@R z6F!j|l}Iw>NiT-w>7e0%ylZu55NyZ|#0xKHg5ZcZ`I%e@f9n%LcZw77sIG)q`6uMw z>FxAY?PWasAQN7{`40Vy5>eW8r66Nh6qdOtgUp4msI+^8&(F%i*%BT0jOZi0%YXbk zwRwWSR5U4*8E<2rZ-2 zT(gOa>;%Eaj$2fRXToo(&cXoMe`HQBgP!6~uw`c}HwP7Qan}tLGQCE-*IMG{Zwq+$ z?Ml>JJ_2mf6v4hZp7^#+M^MjIqUfJzc=jV4b6d7zQq@F3z-M9l>SP&l zJNOdIh0YM%b^_KM`v;R+9>bfFLg2TZe9xsBTP)p;q*|D%5`RJ$WW=Hb7eFeTgxNJ+ z`*5t3Eh_KMMAhhhWK5bD7MA^`<6kE*T_*;K`0OC?z9UQ*^D{QlC}DQu+yIj5wjTzp zQ#h-p?=<82CF*&u5oVzh`(5n|`MqO0aoi&S-!Xg+ODO|X2KS)nYH3(uorv}ygK)qx z0z}NVQTZr2EUGlXi`VW!Ax`IpZTQTEVj#Ei)lcpdL_)h8BwgsBoLY_os@m zMcgJ-Up|X1RCNXU{yQYEGaJjRI^b-61bHs{1k<0sg_%|*M1Ow>IqH{3x_g6BlQW?U zr%8j1$9qU}IKpgQXekh-E!jOZuoWRHPI5p_&8wH-&=sO z4iKgzC5XEcL}krBlibEl=vw}ie32W_-EzXcm6p2p+r`P}C&MCo%A*|DBOxb#abO!=@8Huz}^X5GxB=dztqNJb3{ zb{6BZpm2UazlQ|gYsVYEu0XNbdpb1UjMb59fy+CEF(mvV=JZdcYEu@`#=8u8tq_M+ z6N<2x-N12HFZsUW0*v_-2$Ab3*za=T=O2r3jqej0EPIAoVbnq1q@|L(hA+V*YB!eb ze?hbtnPL;yR2gS1CHQApg7d{ySmU)3G@{LjUQb`g-y2bwdhigIM7HACfk>S4@GkwE zatD@-isI{<8F+JfJe6*2;3PbP;n47HI8%|2UJ)+j7Vo{b{GNsrRF9&F^AxB{Pa;qD z)?vHkB-ZDkE0FEW;HY6JZM5Hwhnv1~<#YA0ZryUUh#v>K*OtS31m~YAOu6{S}XC)i@r(}qHHxL>WrX_g_(h#Yld-&e70P(ZQok3s z$u9jRaDDYz`gxWJD;Gp??V}qQYNkP#@=Po7k;C;_wj@6NBlkY$2r4}*!JI!ubk$B5 zkn&x@UiUBurGrxJ`=%Uns`E5>^dhc{aG@<_w@_kXvbq1SK^XH#6_kSSV1nLp^6mG0 z!DlIc7BcToMU~1q+Vkonm1_9|56;Wrq|jviy(E;bE73tZ(n*g^*-FbN)|yv|Z{wW< zw&*CBiA(j*lV^VnL4+;G)C@`1WaS|Jw``bDkwn~lEsr+c;QJG{9XIcfh`<=40oOkX zLBwZCnEK%e&RV8R&&91|*X{4be^W2vbSu&A2MWgXwE#oqim@UR$Mm~l<*nZM!Rc}G_wr9r*>jdS^>UYK7o}#U5Z<`Wiidy zw_>%Q6B8(&jtAA=k{!HbDXC-(yLg-f5giue3fEQQ@Z2A?uecOn@7+a~vYLYA@24T+ z`gI~Sb`Nuo8wxwqcx=He{I|!p(nW=$ALk$(!kcoW$KKI^IHy z9l~SGi`~w2&*2aB0lia0e^dCe4doxyN-O&ID_ z1MP)~4VeLu6er|=^yGzm1p@hbkw&6)^C#Ds>2vfvxdIi6m{dy0p zVyYo4GJ?3?ya}tP`@w6CCVIFht8$WS2v>Md5g!Pxz|X4+@$ab_qR!>6nuZ_;WfR+wf_{w>?#m5VHsy&VE9*j)(LX z|9xY$yBS5p#jyW%1nsp;$9McbpkG&rbDL$ywt1?skb^u)VZ{PLT3b;b?DtzS9tF{TTz-m=8E z2Nn4as&BA1S&_`Lmt|XKj}^E?zooTj8<=s+GI3wnbIkR+LJF@4!pX1JVG-K<$W^PKR z5?c{$EZASg&oG!O-f1aLm;Y{sw2VDaS}=w^Chvm}7H-1hyInv`cO{pi62cnAZsH#A z6vKy3S$Je|D_jm7!?|mU2xbO0(-ONwFiU2T#*i0)soU{IuN=Fy;37R3u?5Q{I_Q6? z#;De(g(4RiVd7_t*|OG&BAP`+bgF1Nk*k= zv@|wM%b|xlgwVH*&v(o_i!YwUqs%}#f4)!`ENBabQ-0}?daoYWHIHYNbtAcNJcoMJ za1ptEKM-RIwxNn8?|1#d;Peg)WYO`4sus`2YU6-W2(bN zX#Qhk@Cv=a&q`7-L_CN*R&>V>Uw=>%xy;OuPOH4MTbFcR&4dJ{t1$InDju8Pgz@wv zc~F0V=E(%(N#5fy(MbjNo3VJ5w3C5`AM|s~F!!Ln2v^P8jD3Y;(0Cw?gx5u5P(l`+ zJ);+V9fQ$8>LAhF9#0>-DYECwufbK{aP)VxqbxTSqStPsKJs6v#PqTB$w6KEfOoZ+ zX-%Z!sx$3CEq|u0qtG9U$gC9S2WPjIsVsRGues+7egcxkfM?d~zH7e8&n( zZ0FGRZ>B@(B|psbJC15&?vqQr6W(W}jk@t20@YWpGgC$~(SFHT{4-chlB^fOysiMQ z-JlC32R@*nGMH;pSwgRVTtE&wpI^{@a+Lcd>5r?N3h;wu4BlrGU;$~TZ;V9*8=huB zL5Mlr?)gR==1+wwpOSF$6&)Jd`jyX&1(ApRjHkuS0}Tz1kvPKvIxE;0+lHdRGVClo znfZx(9Jm7Q*9yVX{|xX^M-NE3cQRjFpTfc~A0T}FH?sVv5==Iog?E47fW1xzXf_}u zsGQEg8s;7biQVE-Lv&bod2x)nK4@O)XH7La-_p674AwpSKpNJh;V#~%qi2|bwEYQ+ zx`?thV*=^?B~lRiC7ilPIgo%dRa*Zc0o~@_#geDnq0dkgM&H$w=LNx_)b|5s*Pp^O zQa9m-t|V(CBv0Rq-C(ZOOoi=xm*e$REvUUdVE$gn4PA6H@yEJtxK^o=*7b~Gw@T!o z;-won<5(I^^Yx*N#>L{pzi(-4^=A51S_wYJhQf1+czC?D4E~PYgb82c@W%P56K#;bMTPxx$_~``+LNgJm$((ld2r*jA=cavgoPGW zoVtXvz<=8>yyn?TFDWVGL)ZxFM|5ygv%Fwg#%c2@*Ah4}?;$$#$|bVIaIg6v&1>is zdY|^U?ZC<01^D!V&m;%*V(7ABI^w1ym>eO`Hoj06{EOL#ho@u{yA73O<$6)Nv-ky_ zcU~D+XW2pFS8u*2ej;@_mrXRvF3|ZM6HwOJ6m!=+Azsof_mKA~>6yr)QvpXW|MKC! zuMffH#%pmh<-mnLMGZmQh^09f|opaDPAqThUABGIcBr;U=4uVr`QE~k)h)Hu~f}%s2TkbJv zJ=%s#U#z9;yjZ-UcLvt%NP%OIq;Y~qGG&C+z&kY=XMVT__U6~|sr@$`j1a*KR<%UI z@h$e$%kUjQhv3rHa!6fa4^u3KG5)|zTohA8^*t6rqC_2|C3J_p<=Gk^2V3Y~(F!Tb z*I~C-0sXFMW8S*BnQHx*#It+Z7s0a1? z&xtmQ$qSYzxq<4D5AbRGT71p-f$4k2<3;au5P>PwYR+}&_IboiuIBf6U&f(I|7LtM z6vUCD2AEd;5~TX3(b2O~(DkznTRWr$%KQw^p*@~-9D6`TlZNs88!cATdKs>I<%y$? zLBwFuH~u{Y%%K!RysV*scdM3gcCTL&sed9wrT_mdghfRK-vKe{(qf!+A{jf@^PZ{i zcd5*AIUK3H%oBC8g57~DaZF4qjI{ryQ>(9GY4}ZaaLfadV=VhoHVNMyUj`4aaj;o! z8TqD=j+@zBoFO3!J@QkjwCa7zR{n-^%UOcOB3f*hf-y19DaCdDanwK17N3VFle6tJ zNZ@_G3+#6cZa)+bZo`2@$W0N|8%`ecw z`|f{DkkpVX{^)8^U6qfh_1dtVv60l+yhpX5vZmnW~=Vv(MYqadT)SWVG^5-tJnEeY6NyNxxts zyLqRVihyp7n2+PmhhrI^%NiD5h+4vnQ1w<7S#f1C^jq z2U_TcFXr&z$y(lX>rDRY@h+IssrdC=FwJ+mVE$`JTmUO`aLlw~uvl`=Y+|4Bu8?(@v+?WVVjte&BATyo;h#llR7ZQI8t+NeFSMg?U)#~6B#atm#Z=CN zBiz-h@ih5|EKNVJ#hp;uOxcl_oaACv`Z5gY$!W&aXLej=)!=u|&r^gxU4D`S$LTc0 zB#o24FqVGExLctte3X0tWqRdf=LYVw@eOWe*EsTi;mY#ud*++3Eq=o-kyoM8xzlMz zQ7ornk!bGobOR+x3f#*vjhu&^2Ys&nfph43&l!dGR}OzxrRjQ)xVkkGbk{8rT2#@> zbrxKHXVBToCUm+%23NSI ziYc3IOMAp-(xN@x+~kAG^ex|;L~f^X)>1;$(&rpkH^GL=4GwbGZ_Clu^|4%M5;Cgp z_qj=3TezpkMJoND+0yCJtLO#&PHtw=VLCE#3qXB_Tqb-i z`GA`-JRenOiPOn8g-& zb$$4_Tr%YUaD!t`kHM>TAI=47aLYf4O3YNi@Nzzytxtk@+QG&d3 zECJ~U>Cj~iQnYKSO8NdS13rqh*A@^DAlZ@R8d0aq4@fzVSC z)COJXT5$__X3+~W$p!F?J|id+K}` zy*nJvJaD2TGv6ZTVoJYOUIz2@DfoHn19~qbiRbP5Q&lZ(L7N?=Nn1~&4}*r}mgHu# zDbmwo)dy>V#hp~x<}ibVY<59g0xtsf4aK%j7AHME$L)Ig4y}?XLP3*~(5C?_ z6ri^WWk_1k=<1W$HC`28zOe`^t}ucim`k(XeT9DycERjsW0<&NB@)}cAL4C>1*KlW zuzYqe(iz%Bxr^IQr^7mq-sInMM?fye|8wUugH$F!1OHI*|$aYkg zH43(g8^Xe90e-4213s2g)R=#6d~5-EPb#KKI0{XQOF;4&8Xz^@g-oA0jXw`eK=Y)= z&`&Ied;4_`z5o7)u>Fe@F8@1%DjOejfq61;Z}vS4$4BbiqXSZKHA0#@wDKHXYib0G zN*-~kdaAr%_MhO#j5#!zx1a5~wOzT`(Q!7!XZ=JC z>XA@V9LW_)7!lWs?YIzqp^jg}xXt|y^}@v);kReDV0NAo`Z01G)m!({37Rs*E9ocN zm6eEdbrWc##&zKyJ0a~WQliy!lPw+=Y=BkM)#$MD4%nIbhz`qdpsND|u$iVT_@TLi znywy!y@5OW?_rz8ky}>Zxp5Hp+XU#st&pxegYId~;7XoOgjCoK=I{8-q{k{)cVr3p zjm^S3;uoQPwKaa?F^0S`|0ZnapQA$`ZqRc~k-dq#fg@J`<$2=k!Q~>~ZS9MpcO3ce zrcp^y-BBpa3rdBda{*LOvxu_pQFuYj0F+#t49DvAX>Yt4c)aVtIX`~W2Uj-ZowM|* zrOI4Vl;?uq$W-GXHCx!Q|252776(TgJxL!@$0M42-lyp_*fyL;l^F-o^LQonv`d^E zZ?M5LkLK`9j1a!y&F^uRD$yi5kIzm#!;UKA(6#KgMJzQSqtnh|o#RL7Vyn@3VU|4k zZU2}GGOr>Lk0BIb$KRFXyCCnJB6(Swj4x*1!sq@;5f$DaXE<9Cwx!gglHjwDAJ!mn z*H8zY*df}K5uJG~9PEd)FCJPSeq0)=}NVjG*J?Ex|x>b_F z)>)eFU$d3p6;?vm*f^Tj83~^nVsTn@Hq=aQLOFV`=ycgUIHD*6e5;f$arwc0y&DHP z%fh(May!^w;s%~?Gwa{{tc_TZ*eTdHQ-2&|-wPSb6Ni|aOnX>kigI`ACm;{{xDi3)pQGy!M7JAo>t zJ+a4NH?px?Lg&Yo!bi_1RMIMz(^=^Xx|c2rXiqih_$;C4BmY6R-Z9Wiaf9+l8L%{4 z9Wv!3X#qdyoe;DeUntL{7Sen16S3_eIz5R*o#r`>HF0pm%8iDOd;)E!Q{ebxHE5g? z{lC)^B&&A8#`|vAb@dr^_s$}c_`Cs%fADO(zT-mO%D;Gtjxo+z!t)sU+(@YZDwuxD zkb5F33m>g{SK*nlEI+FStCmP`XY6Fa^29m3qf-N2erpJIBO)|Y=MCs+IKu2&%JrML zL4}$&4wOBK+iP|Sem$CtpY=Jx>a{!2tOX1oNjJit2Y#R$@i`EF3#+h=Tp^U@=+ z^!gX{Mw>nCddJVTL?l?lwIImuQNzD)Ve&j69#2X-f!3l~So}={IQzAM^3Q(m{qA0r z6Z91lMPH(v$x%YP)@U&MBMRLzndZfoS$r<@1a)2Q4^bwvBv5oWsgporBum40qQdc~AXZird~-#KY4KvbUJn7&m`9WT904qu z&9jW$U`neLS)g~0@0R{R`y#>wc_&80k2~44=zJG$9^*!JyVO8k^(EXnZcnfVJlYh2rOW;CgrnWu+4c7Dr@<3l@AHM`kjq$T zV2I0Lt#ujC+&I=pS`dI8urG4kM9yJ_i!e**DnK$&4*B^ zofFi53q?M+M-!R-`=PqG4p-eX!1dBRpnjDU23B0f&44qd9yJJ3S~L+<$%+xE>K@6AnM7>Pf4y%I*Yw zT((glYCoR(TfY_bnMEn$C(Ub-|?%agj|&>2TOHT<~iPY7Lx1F9n;y zoUdace}W>_X}JW;&Mv}^Rqg_10q--}+k{8zchEA+BKUV%gp^rl!Pja72q-g!aa+!z z{G~_XS!e=1t&M0_6rVC$vkb-#y+I}KUr-zSES|l&7~8(Pjx372VeOlJFh^o4K5aA+ z_PqFlmdfgY{DUEpa*|g@)k&^NgoEoe z(cjBUv3RRK=59OzSIhCZ1rHGutIjgVWm2hrr?SbEfN$S&T>THAtg+{@YU({?HE zy3~Vk`Uj?`H$r{rBXoIeBEFma9`ZU?;u?JskKJHInXyJO}FR0KGQuFKnK)1_lf-(Gr-7uP+iO<3*H++?a#VZXZe&n?K{S z8RfXTs*lSVHyvcM1_Un3r55h;`(Tl~0*s{e@OR`sn!z(r%U-oW34gENW37R64Ua%d zLkm(LTuj4wc9-5M1(q$LjSZ4V(diE*u^vAgmd|bHtn+JO`RbGKbl-JQvmJ|9ZCV4t zvzKuWi_hZ8#}D9~h+3=|`2ZbqI0GSec4&Er8g8~0;7dxO`0ja0B4_v;8`=Jcw+Hci z8~sWsOdFzl$6I+mr8X5$sfK0ipU}*_hd8ru8w7NW!U7u+T-qzf+^*dd9y1QWi*_rJ zEt6Z|bi6CH-CxL*26SP@!d$qaCj#MjHxW2&fHy@-knA-sTz>aYq;xhBMv_-R-L)$a z;+z13qg>5WKHsNm))VN`tkLYKNi2xwG4Na2PBrFkgKn!`^qHRvT-fph){X$TM|V2f zvP%=?ivA~qC#--CIzf!2rg*05cKOFy>W&|;p zCnB55*O+e<(EYP!f_?He&LOUqx)>kAHg-p8M{O2ZJIdg%*N)=+xq5K^KNXO;|Clb) zA4@KZT%%&Sd>&0q93IKefyDf!&{FJ9m2aMdd}T*iFQr2iPSy$|^!c;6u{F*=>ml&7 zn#uR9Z19^t4LqazI4KHNW{OV{b(6d;SUkH3-kdpyjm`2QvHLe|GQUbsuak#(kraF( z1HoQfQ_wtG2#?g~;4!~{qjdHTKdw$i#wzPr!~NZ4+=vm`I-^U_NCbFZj}&pzoJyws zxJSbS(@?{juiV7&F!=1W74KXkhi@n9;ipT7An|QJZZ}AQ1d~gsUi=Q->+=}yMb_GU`UZpebZ;X5af0Xd zIA?KB#%t2Mar?k?NSD2F*oB7lhv8eyNhFnE%bcaB;(RRdc<#KRaXa=A*u-Uhg?vwNl1AHXp&; zQV*cjx_9XD<0(|?&=yX5oinafi-i({gJ?y39hS8##|7K}fy3t%Y{qAc;B+ySnms5K z3>QM)TuJ=Lek!p|$T8HuD0M-WykXk77JQdjpda{8C+K4NlIcjVrP@-VE?+4 zwp5pK4?8X4+jo7M-kO0XZ74Fea#$tQPSF-BAIT6lC{Hwh(imRMz1o*n;UOj~oRodO-Xx<|7$<*T*e-TdDBF=! zA0ujDQL=5j@WJMXLK<9XDt{!n{?FCEdc}w`p=_tBuy^jDaKviJOv9*J*tbBp{=X&N z=6kLy3bl!zFsk8(@aB^+q2Q;6aQ_`2;l4MQgcV9Fgl;cB)cf^h3QI0W*E>vz7Y;O? z7WN-36H0zovbb$tChThP7GA#|D-3w}Nx0=ts4&n=RJf#PsW9+Vfq74^m{97bRNcH| z2|{zJe4%8djZkfqp+$N1F`@ZrQK9HO5#ia#MHbN?28EsX;)Irmrzy0UdJ>>c*3<4Uw#*XLKSav3%oYt6w4@@{DpV9 z1PNiqRSe^5f1(3+Q^5SH5-Vt|#yzLJ;MN*z2>wydvxAy&2Yw45M0X<3rdm|Svvxn2 z+CpJS5}F%zAGzlB(E}%DU{8l5oJDvLtUH)Yr!Jd|f+Pbi>L30_QCf23tU@)Q2~B9t zv`sj3lQOu66mv;7&UDnw37{vm!z=3v%;WpaS%=K6I}&2h^nscK^5!%vHc%{xZUL4|Fhxj|+wcT~KB2 zfG&(&1?D0JZ)`g8SV?uzJ>Y|s4>*FFWF>N&EXu;Zd2pAW-sE#LFY%$7x!AaKH0KpJ zp4eRf$JzSZ;aadqEs2eKKe$_MZIea``bY~CVxuUFowbe^J_hUHh2s6Wd!v8=* zO0uqhifU9nXAe6P^9Pz_=%DYxjt>t?HtpzZz~!26;IcHXxGcUlJEVx<(SH}@>}uP7d# zoxgxQHmfnyplm_V=}CC#ggoAbcs9&H8Vb-YhMXJCFc6ah1DB-OQd}cA>}v(L`Q22S z*e!UWngx^ZAB4oRaC+!)32b;pNbb9X!o7ndoR_E_sF+>ChaQ~}=s(&?PsU#W^Q{ud z{ct{&i%g=o7Mju;qs_QKonUK?oA9X78=Y``PfeZe=zov4(_Mw*sh3?a?6`Oxw}0CU zZuhRivUgb!qcD}{oQ|RfkxJ0sD@Jnb67fE>R)N)1aXiIg4MaGPL04pCSZeur{A|yD z()%D4NjNINwdR>LCv-0^E*E1d^LOIZ8Jh5*Pz!u+$&x{9G0uuVxAoNLQN5D6^*`nH zsW_1*W#v9pZR|9j*R>Vb=hWbBcdWoFA0Y013^csyq`hs&h_c^gb~CXE%YJ?WhLc3` z*aH@j-MRsS%~k;s6dK-RS{EQE-nzapI%-fGfwOvFR z6;5QW)*uz}55tGwox$sM#9-zkNjmduD!R$f!(HUP=vY(?E|#YF(pm-hS#w)($@CJ8 z5wSza*$JjhKSfjqR^eF}c;DCF4%%hFut4@TJl}kU8?s#to)O;gW|9d6ZZSlObCMuu zl{N^Qs$gs<=DBq`WNu6%9lv&%-U*R|$xqWPYW5sKg_RSbI`#})c0K~hs>k49dl>#I zW5osC-vJLzHRypY+3@exFC3SjN*WBsX?sis?K_=`V%sFZW7De2btHkVN_c_C+U-VK zS6Z-R%L1f-Ee+jH7Le!46H%5$Io=t34xX-lz!g|)gT!S5Yj?lL`u1Wp?bsA-yj%$v z@Z5G0m(zH~`z=^L(UjX~tcEru{Q-gKD*pSY5*}HmgTcFh(BfkbPp+lHtS?*f_-`q6 z`tk#Gs#6SH-=YDlzw&u>S4^7s=h7|iDaiGJ95za*1c`)E@M~QnMiZ9dWQDWvYP=oY z{An!iP?R7p@5U3O-Eqh!N1jyF4dIRB{m5m0K6LTHL-ae}2!?*wKt_-$99~sLZ_UYs z&xQBs)#5%Hm6`y(AN#q2>1)x@x`*Zor^=z+QyKT1Yyzj?0$l$!5~5z^Aot;Oe7F1{ zt?Q}B@{V!X{mf@U>7tp4#}J?;LlLmSY7M$R)GKJ8u@)}!u7l9y^^oVsAX7pd>)(A% z=c{Bw^)VlA-pf0%r+kg@UECVHhx3()?JieMPBlKLzgvarPx#Jlsf zKqTxlE^=3b!5hbr!J}I6+~NZUtqVxkCvQ5(zXDb0zZN*RC!rl}?ey;ck6@TPAWVoX z;oeSO3!akI7L{`>c;2u(&(e>EwPmvG-U1^ibK3-Nu>@wXRX{h-s|vP$Z^L_|RN0Qs zHb^t}$K0{$Y*y$=yw3>0eDOG%(PD^?$$LXl)GHWu^agO-*T9oK+fi!2Wc~Wz@sJXE zTo|=c7fhFH;rYX7>67CpAnd0*(S0L{&s*1lzQtd-r4dOrSB6nz`};8N$SBfcr3`n? zv!UU|1#~ze2&b1+2)`x&N9&JiaT4Oo@%z$v_1yASE&kLxO6$?s+; z2rdJg8%N{YarGLGBTjKDP{Nr61A@ z(eLp1k!-kqpU+(mx6mu@F8nh!lPEf0L~3F0(3mA6zW?Y#p$u>)@QAa2U z4@;n%QJL5!yAi5~YM{6J6WwCjftRcBU6znmswtPMPs`U-W?P&wy zwhGQYA%kA5d5Z^&pFr>R3|tqb$mG^P!ZRCRL-W86H1~5eh#wTgi5=H?kH9uaEz8H2 zSqf~0Y>2=_T}WT)xWRKTQQmc43!B%t!qe;^m@Rk%)i*ccJ-&&YaY(I2+7x@?+o&+C z_2Um`JW>vjuoN22f}nXzJuOjKgvnD?m?vI^4b7~m(f+Xz%Xi1FZFvSs+xuyLZaHLm z>tU_L22i<{0(%s@smPv0&~KTXNEkyJdZQb<3HgkiZzPsmc_c~Pba2N?4FlotWu`_urkmpQ_jQ$1Ys-co?7 zV|ek$0DOSYokzC~(-U`X!2j2Btmz&NIhVHMl2^xIhDtabeFyZdp9J==N#^SGWAJB% zI=+AMk#2YxOYL9Pg7xuO?A|pR?{`S0#csCXYuN&Cr7zIGcVkhvK!`2VCy>Au54m)+ zGf-ft1v!zO+`JYmu;2O``WLuEOq>JSs~LfR>{S<7yr_UN>4mUR=Lo*?W)z#w=N(+r zJmCP(UYQxV3RMpT<8hn*BJ2HwsPg@9I!oRXgeVff`;&-jv=6}iiN`qEQEue>R5u!Y zMiXY+-Qf4XC7^Xc4i5iNCTitzwAnQgei|ntk+yvN`8_9aGZ~Au>@v_QE|=CR`{2Th zg`Co(Yj9wtDzz6c#PzYt(1`C^-1xQ?7rJNR{;pxzzmeZXIUa|niz?uT7Kd%wDcZN~ zD)KIkg`pQFP|rr_?Exbo%E!>6nhAo8DY;O*S_Hh>?}5nrA4pA88$}qWf@TrVmOPmZ zo9-UsLW(xhfj|W|26f@aTiG<(!^R@$-2rT_{egcE3g9ta2p-el!)I@C*w}mlD~uH< z$;}n`k)bt=&E;o&Z6+WUUqiQ!jK@an55bSs&fqb;9J=jaK#0Ul{&T;LQhY~aC^i>Lv6d0wgZ zL+M27*b_;%j~c?%a;#;&=MX8VeT7xSJIUTpy{O~96`AL~lcch*$ww}*{Ls@%o;Hf& zo!nczexnͻ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 diff --git a/tools/accuracy_checker/data/test_models/SampLeNet.caffemodel b/tools/accuracy_checker/data/test_models/SampLeNet.caffemodel deleted file mode 100644 index 274a07282bef5e365ece93fd4380bb71cba9f317..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 248617 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

9@dO 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 z�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 z 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

)%&VF4)6UWYwS=V@YYN-TVzQW}eRcv5);V+&t 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 I1x@z<0BZ1F)c^nh diff --git a/tools/accuracy_checker/data/test_models/SampLeNet.prototxt b/tools/accuracy_checker/data/test_models/SampLeNet.prototxt deleted file mode 100644 index d6b158f67ee447..00000000000000 --- a/tools/accuracy_checker/data/test_models/SampLeNet.prototxt +++ /dev/null @@ -1,116 +0,0 @@ -name: "SampLeNet" - -layer { - name: "data" - type: "Input" - top: "data" - input_param { shape: { dim: 1 dim: 3 dim: 32 dim: 32 } } -} - -layer { - name: "conv1" - type: "Convolution" - bottom: "data" - top: "conv1" - - convolution_param { - num_output: 6 - kernel_size: 5 - stride: 1 - } -} -layer { - name: "relu_conv1" - type: "ReLU" - bottom: "conv1" - top: "conv1" -} -layer { - name: "pool1" - type: "Pooling" - bottom: "conv1" - top: "pool1" - pooling_param { - pool: MAX - kernel_size: 2 - stride: 2 - } -} - -layer { - name: "conv2" - type: "Convolution" - bottom: "pool1" - top: "conv2" - - convolution_param { - num_output: 16 - kernel_size: 5 - stride: 1 - } -} - -layer { - name: "relu_conv2" - type: "ReLU" - bottom: "conv2" - top: "conv2" -} -layer { - name: "pool2" - type: "Pooling" - bottom: "conv2" - top: "pool2" - - pooling_param { - pool: MAX - kernel_size: 2 - stride: 2 - } -} - -layer { - name: "fc1" - type: "InnerProduct" - bottom: "pool2" - top: "fc1" - - inner_product_param { - num_output: 120 - } -} -layer { - name: "relu_fc1" - type: "ReLU" - bottom: "fc1" - top: "fc1" -} - -layer { - name: "fc2" - type: "InnerProduct" - bottom: "fc1" - top: "fc2" - - inner_product_param { - num_output: 84 - } -} - -layer { - name: "relu_fc2" - type: "ReLU" - bottom: "fc2" - top: "fc2" -} - -layer { - name: "fc3" - type: "InnerProduct" - bottom: "fc2" - top: "fc3" - - inner_product_param { - num_output: 10 - } -} diff --git a/tools/accuracy_checker/data/test_models/SampLeNet.xml b/tools/accuracy_checker/data/test_models/SampLeNet.xml deleted file mode 100644 index f3d55eebc36afd..00000000000000 --- a/tools/accuracy_checker/data/test_models/SampLeNet.xml +++ /dev/null @@ -1,239 +0,0 @@ - - - - - - - 1 - 3 - 32 - 32 - - - - - - - - 1 - 3 - 32 - 32 - - - - - 1 - 6 - 28 - 28 - - - - - - - - - - - - 1 - 6 - 28 - 28 - - - - - 1 - 6 - 28 - 28 - - - - - - - - 1 - 6 - 28 - 28 - - - - - 1 - 6 - 14 - 14 - - - - - - - - 1 - 6 - 14 - 14 - - - - - 1 - 16 - 10 - 10 - - - - - - - - - - - - 1 - 16 - 10 - 10 - - - - - 1 - 16 - 10 - 10 - - - - - - - - 1 - 16 - 10 - 10 - - - - - 1 - 16 - 5 - 5 - - - - - - - - 1 - 16 - 5 - 5 - - - - - 1 - 120 - - - - - - - - - - - - 1 - 120 - - - - - 1 - 120 - - - - - - - - 1 - 120 - - - - - 1 - 84 - - - - - - - - - - - - 1 - 84 - - - - - 1 - 84 - - - - - - - - 1 - 84 - - - - - 1 - 10 - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/accuracy_checker/pylint_checkers.py b/tools/accuracy_checker/pylint_checkers.py deleted file mode 100644 index a42ccd65908600..00000000000000 --- a/tools/accuracy_checker/pylint_checkers.py +++ /dev/null @@ -1,144 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import astroid -from pylint.checkers import BaseChecker -from pylint.interfaces import IAstroidChecker, IRawChecker - - -class BackslashChecker(BaseChecker): - """ - Checks for line continuations with '\' instead of using triple quoted string or parenthesis. - """ - - __implements__ = IRawChecker - - name = 'backslash' - msgs = { - 'W9901': ( - 'use of \\ for line continuation', 'backslash-line-continuation', - 'Used when a \\ is used for a line continuation instead of using triple quoted string or parenthesis.' - ), - } - options = () - - def process_module(self, node): - with node.stream() as stream: - for (line_number, line) in enumerate(stream): - if not line.decode().rstrip().endswith('\\'): - continue - - self.add_message('backslash-line-continuation', line=line_number) - - -class AbsoluteImportsChecker(BaseChecker): - """ - Check for absolute import from the same package. - """ - - __implements__ = IAstroidChecker - - name = 'absolute-imports' - priority = -1 - msgs = { - 'W9902': ( - 'absolute import from same package', 'package-absolute-imports', - 'Used when module of same package imported using absolute import' - ) - } - - def visit_importfrom(self, node): - node_package = self._node_package(node) - import_name = node.modname - if import_name.startswith(node_package): - self.add_message('package-absolute-imports', node=node) - - @staticmethod - def _node_package(node): - return node.scope().name.split('.')[0] - - -class StringFormatChecker(BaseChecker): - """ - Check for absolute import from the same package. - """ - - __implements__ = IAstroidChecker - - name = 'string-format' - priority = -1 - msgs = { - 'W9903': ( - 'use of "%" for string formatting', 'deprecated-string-format', - '"%" operator is used for string formatting instead of str.format method' - ) - } - - def visit_binop(self, node): - if node.op != '%': - return - - left = node.left - if not (isinstance(left, astroid.Const) and isinstance(left.value, str)): - return - - self.add_message('deprecated-string-format', node=node) - - -class BadFunctionChecker(BaseChecker): - """ - Check for absolute import from the same package. - """ - - __implements__ = IAstroidChecker - - name = 'bad-function' - priority = -1 - msgs = {'W9904': ('using prohibited function', 'bad-function-call', '')} - - options = ( - ( - 'bad-functions', - { - 'default': '', - 'help': 'List of prohibited functions', - }, - ), - ) - - def visit_call(self, node): - bad_functions = set(f.strip() for f in self.config.bad_functions.split(',')) - if self._function_name(node) in bad_functions: - self.add_message('bad-function-call', node=node) - - @staticmethod - def _function_name(node): - func = node.func - if hasattr(func, 'attrname'): - return func.attrname - elif hasattr(func, 'name'): - return func.name - - -def register(linter): - """ - Required method to auto register this checker. - """ - - linter.register_checker(BackslashChecker(linter)) - linter.register_checker(AbsoluteImportsChecker(linter)) - linter.register_checker(StringFormatChecker(linter)) - linter.register_checker(BadFunctionChecker(linter)) diff --git a/tools/accuracy_checker/requirements.txt b/tools/accuracy_checker/requirements.txt deleted file mode 100644 index 3775f8b06c2b5b..00000000000000 --- a/tools/accuracy_checker/requirements.txt +++ /dev/null @@ -1,11 +0,0 @@ -numpy -cython -tqdm -PyYAML -yamlloader -pillow -scikit-learn -scipy<=0.19 -py-cpuinfo -shapely -nibabel diff --git a/tools/accuracy_checker/sample/README.md b/tools/accuracy_checker/sample/README.md deleted file mode 100644 index 60fc9f4b0da546..00000000000000 --- a/tools/accuracy_checker/sample/README.md +++ /dev/null @@ -1,30 +0,0 @@ -Sample -=========== - -In this sample we will go through typical steps required to evaluate DL topologies. - -We will try to evaluate **SampLeNet** topology as an example - -### 1. Extract dataset - -In this sample we will use toy dataset which we refer to as *sample dataset*, which contains 10k images -of 10 different classes (classification problem), which is actually CIFAR10 dataset converted to png. - -```bash -tar xvf sample/sample_dataset.tar.gz -C sample -``` - -### 2. Evaluate sample topology - -Typically you need to write configuration file, describing evaluation process of your topology. -There is already config file for evaluating SampLeNet using OpenVINO framework, read it carefully. - -```bash -accuracy_check -c sample/sample_config.yml -m data/test_models -s sample -``` - -Used options: `-c` path to evaluation config, `-m` directory where models are stored, `-s` directory where source data (datasets). - -If everything worked correctly, you should be able to get `75.02%` accuracy. - -Now try edit config, to run SampLeNet on other plugin of Inference Engine, or go directly to your topology! diff --git a/tools/accuracy_checker/sample/sample_config.yml b/tools/accuracy_checker/sample/sample_config.yml deleted file mode 100644 index b7b79559686b9b..00000000000000 --- a/tools/accuracy_checker/sample/sample_config.yml +++ /dev/null @@ -1,66 +0,0 @@ -models: - - name: SampLeNet_example - - # list of launchers for your topology. - launchers: - # launcher framework (e.g. caffe, dlsdk) - - framework: dlsdk - # device for infer (e.g. for dlsdk cpu, gpu, hetero:cpu,gpu ...) - device: CPU - # topology IR (*.prototxt for caffe, *.xml for InferenceEngine, etc) - # path to topology is prefixed with directory, specified in "-m/--models" option - caffe_model: SampLeNet.prototxt - # topology weights binary (*.caffemodel for caffe, *.bin for InferenceEngine) - caffe_weights: SampLeNet.caffemodel - # launcher returns raw result, so it should be converted - # to an appropriate representation with adapter - adapter: classification - # batch size - batch: 1 - - # metrics, preprocessing and postprocessing are typically dataset specific, so dataset field - # specifies data and all other steps required to validate topology - # there is typically definitions file, which contains options for common datasets and which is merged - # during evaluation, but since "sample_dataset" is not used anywhere else, this config contains full definition - datasets: - # uniquely distinguishable name for dataset - # note that all other steps are specific for this dataset only - # if you need to test topology on multiple datasets, you need to specify - # every step explicitly for each dataset - - name: sample_dataset - # directory where input images are searched. - # prefixed with directory specified in "-s/--source" option - data_source: sample_dataset/test - # parameters for annotation conversion to a common annotation representation format. - annotation_conversion: - # specified which annotation converter will be used - # In order to do this you need to provide your own annotation converter, - # i.e. implement BaseFormatConverter interface. - # All annotation converters are stored in accuracy_checker/annotation_converters directory. - converter: sample - # converter specific parameters. - # Full range available options you can find in accuracy_checker/annotation_converters/README.md - # relative paths will be merged with "-s/--source" option - data_dir: sample_dataset - - # list of preprocessing, applied to each image during validation - # order of entries matters - preprocessing: - # resize input image to topology input size - # you may specify size to which image should be resized - # via dst_width, dst_height fields - - type: resize - size: 32 - # topology is trained on RGB images, but OpenCV reads in BGR - # thence it must be converted to RGB - - type: bgr_to_rgb - # dataset mean and standard deviation - - type: normalization - # you may specify precomputed statistics manually or use precomputed values, such as ImageNet as well - mean: (125.307, 122.961, 113.8575) - std: (51.5865, 50.847, 51.255) - - # list of metrics, calculated on dataset - metrics: - - type: accuracy - top_k: 1 diff --git a/tools/accuracy_checker/setup.cfg b/tools/accuracy_checker/setup.cfg deleted file mode 100644 index 5d5a13c44f578b..00000000000000 --- a/tools/accuracy_checker/setup.cfg +++ /dev/null @@ -1,8 +0,0 @@ -[flake8] -max-line-length = 120 -ignore = F401 - -[isort] -line_length = 120 -use_parentheses = True -known_third_party = openvino.inference_engine,caffe,cv2 diff --git a/tools/accuracy_checker/tests/__init__.py b/tools/accuracy_checker/tests/__init__.py deleted file mode 100644 index 43d061dfd3773c..00000000000000 --- a/tools/accuracy_checker/tests/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - diff --git a/tools/accuracy_checker/tests/common.py b/tools/accuracy_checker/tests/common.py deleted file mode 100644 index 063a6cd7399d03..00000000000000 --- a/tools/accuracy_checker/tests/common.py +++ /dev/null @@ -1,132 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -from contextlib import contextmanager -from pathlib import Path -from tempfile import TemporaryDirectory -from typing import List - -import numpy as np - -from accuracy_checker.representation import DetectionAnnotation, DetectionPrediction, SegmentationPrediction, SegmentationAnnotation -from accuracy_checker.utils import get_path - - -@contextmanager -# since it seems not possible to create pathlib.Path from str with '/' at the end we accept strings -# expect paths in posix format -def mock_filesystem(hierarchy: List[str]): - with TemporaryDirectory() as prefix: - for entry in hierarchy: - path = Path(prefix) / entry - if entry.endswith("/"): - path.mkdir(parents=True, exist_ok=True) - else: - parent = path.parent - if parent != Path("."): - parent.mkdir(parents=True, exist_ok=True) - # create file - path.open('w').close() - - yield get_path(prefix, is_directory=True) - - -def make_representation(bounding_boxes, is_ground_truth=False, score=None, meta=None): - """ - Args: - bounding_boxes: string or list of strings `score label x0 y0 x1 y1; label score x0 y0 x1 y1; ...`. - is_ground_truth: True if bbs are annotation boxes. - score: value in [0, 1], if not None, all prediction boxes are considered with the given score. - meta: metadata for representation - """ - - if not isinstance(bounding_boxes, list): - bounding_boxes = [bounding_boxes] - - result = [] - for idx, box in enumerate(bounding_boxes): - arr = np.array(np.mat(box)) - - if box == "": - arr = np.array([]).reshape((0, 5)) - - if is_ground_truth or score: - assert arr.shape[1] == 5 - elif not is_ground_truth and not score: - assert arr.shape[1] == 6 - - if not is_ground_truth and score: - score_ = score - if np.isscalar(score_) or len(score_) == 1: - score_ = np.full(arr.shape[0], score_) - arr = np.c_[score_, arr] - - if is_ground_truth: - detection = DetectionAnnotation(str(idx), arr[:, 0], arr[:, 1], arr[:, 2], arr[:, 3], arr[:, 4]) - else: - detection = DetectionPrediction(str(idx), arr[:, 1], arr[:, 0], arr[:, 2], arr[:, 3], arr[:, 4], arr[:, 5]) - - if meta: - detection.metadata = meta[idx] - - result.append(detection) - - return result - - -def make_segmentation_representation(mask, ground_truth=False): - if ground_truth: - representation = SegmentationAnnotation('identifier', None) - representation.mask = mask - return [representation] - - return [SegmentationPrediction('identifier', mask)] - - -def update_dict(dictionary, **kwargs): - copied = dictionary.copy() - copied.update(**kwargs) - - return copied - - -class DummyDataset: - def __init__(self, label_map, bg=-1): - self.label_map = label_map - self.background = bg - self.name = 'dummy' - - @property - def metadata(self): - return {"label_map": self.label_map, "background_label": self.background} - - @property - def labels(self): - return self.metadata['label_map'] - - -def multi_class_dataset(): - labels = {0: 'dog', 1: 'cat', 2: 'human', -1: 'background'} - return DummyDataset(label_map=labels, bg=-1) - - -def multi_class_dataset_without_background(): - labels = {0: 'dog', 1: 'cat', 2: 'human'} - return DummyDataset(label_map=labels) - - -def single_class_dataset(): - labels = {0: 'dog', -1: 'background'} - return DummyDataset(label_map=labels, bg=-1) diff --git a/tools/accuracy_checker/tests/conftest.py b/tools/accuracy_checker/tests/conftest.py deleted file mode 100644 index 7657240d8cc689..00000000000000 --- a/tools/accuracy_checker/tests/conftest.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import os -from pathlib import Path - -import pytest - -test_root = Path(__file__).parent -project_root = test_root.parent - - -def pytest_addoption(parser): - parser.addoption( - "--caffe_logging", action="store_true", default=False, help="Enable Google log" - ) - - -def pytest_configure(config): - if not config.getoption('caffe_logging'): - os.environ['GLOG_minloglevel'] = '2' - - -@pytest.fixture -def data_dir(): - return project_root / 'data' / 'test_data' - - -@pytest.fixture -def models_dir(): - return project_root / 'data' / 'test_models' - - -@pytest.fixture -def mock_path_exists(mocker): - mocker.patch('pathlib.Path.exists', return_value=True) - mocker.patch('pathlib.Path.is_dir', return_value=True) - mocker.patch('pathlib.Path.is_file', return_value=True) - mocker.patch('os.path.exists', return_value=True) diff --git a/tools/accuracy_checker/tests/test_adapters.py b/tools/accuracy_checker/tests/test_adapters.py deleted file mode 100644 index 3e363132e8a1ea..00000000000000 --- a/tools/accuracy_checker/tests/test_adapters.py +++ /dev/null @@ -1,121 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np -import pytest - -from accuracy_checker.adapters import SSDAdapter, Adapter -from accuracy_checker.config import ConfigError -from .common import make_representation - - -def test_detection_adapter(): - raw = { - 'detection_out': np.array([[[[0, 3, 0.2, 0, 0, 1, 1], [0, 2, 0.5, 4, 4, 7, 7], [0, 5, 0.7, 3, 3, 9, 8]]]]) - } - expected = make_representation('0.2,3,0,0,1,1;0.5,2,4,4,7,7;0.7,5,3,3,9,8') - - actual = SSDAdapter({}, output_blob='detection_out').process([raw], ['0'], [{}]) - - assert np.array_equal(actual, expected) - - -def test_detection_adapter_partially_filling_output_blob(): - raw = { - 'detection_out': np.array( - [[[[0, 3, 0.2, 0, 0, 1, 1], [0, 2, 0.5, 4, 4, 7, 7], [0, 5, 0.7, 3, 3, 9, 8], [-1, 0, 0, 0, 0, 0, 0]]]] - ) - } - expected = make_representation('0.2,3,0,0,1,1;0.5,2,4,4,7,7;0.7,5,3,3,9,8') - - actual = SSDAdapter({}, output_blob='detection_out').process([raw], ['0']) - - assert np.array_equal(actual, expected) - - -def test_detection_adapter_partially_filling_output_blob_with_zeros_at_the_end(): - raw = { - 'detection_out': np.array([[[ - [0, 3, 0.2, 0, 0, 1, 1], - [0, 2, 0.5, 4, 4, 7, 7], - [0, 5, 0.7, 3, 3, 9, 8], - [-1, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0] - ]]]) - } - expected = make_representation('0.2,3,0,0,1,1;0.5,2,4,4,7,7;0.7,5,3,3,9,8') - - actual = SSDAdapter({}, output_blob='detection_out').process([raw], ['0']) - - assert np.array_equal(actual, expected) - - -def test_detection_adapter_batch_2(): - raw = { - 'detection_out': np.array([[[[0, 3, 0.2, 0, 0, 1, 1], [0, 2, 0.5, 4, 4, 7, 7], [1, 5, 0.7, 3, 3, 9, 8]]]]) - } - expected = make_representation(['0.2,3,0,0,1,1;0.5,2,4,4,7,7', '0.7,5,3,3,9,8']) - - actual = SSDAdapter({}, output_blob='detection_out').process([raw], ['0', '1']) - - assert np.array_equal(actual, expected) - - -def test_dictionary_adapter_no_raise_warning_on_specific_args(): - adapter_config = {'type': 'age_gender', 'gender_out': 'gender', 'age_out': 'age'} - with pytest.warns(None) as record: - Adapter.provide('age_gender', adapter_config) - assert len(record) == 0 - - -def test_age_gender_adapter_raise_config_error_on_extra_args(): - adapter_config = {'type': 'age_gender', 'gender_out': 'gender', 'age_out': 'age', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Adapter.provide('age_gender', adapter_config) - - -def test_face_person_detection_adapter_raise_config_error_on_extra_args(): - adapter_config = { - 'type': 'face_person_detection', - 'face_detection_out': 'face', - 'person_detection_out': 'person', - 'something_extra': 'extra' - } - with pytest.raises(ConfigError): - Adapter.provide('face_person_detection', adapter_config) - - -def test_head_pose_adapter_raise_config_error_on_extra_args(): - adapter_config = { - 'type': 'head_pose', - 'angle_yaw': 'yaw', - 'angle_pitch': 'pitch', - 'angle_roll': 'roll', - 'something_extra': 'extra' - } - with pytest.raises(ConfigError): - Adapter.provide('head_pose', adapter_config) - - -def test_vehicle_attributes_adapter_raise_config_error_on_extra_args(): - adapter_config = { - 'type': 'vehicle_attributes', - 'color_out': 'color', - 'type_out': 'type', - 'something_extra': 'extra' - } - with pytest.raises(ConfigError): - Adapter.provide('vehicle_attributes', adapter_config) diff --git a/tools/accuracy_checker/tests/test_caffe_launcher.py b/tools/accuracy_checker/tests/test_caffe_launcher.py deleted file mode 100644 index 77a5cdf3d9f72e..00000000000000 --- a/tools/accuracy_checker/tests/test_caffe_launcher.py +++ /dev/null @@ -1,77 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import pytest -pytest.importorskip('accuracy_checker.launcher.caffe_launcher') - -import cv2 -import numpy as np - -from accuracy_checker.launcher.launcher import create_launcher -from accuracy_checker.config import ConfigError -from accuracy_checker.data_readers import DataRepresentation - - -def get_caffe_test_model(models_dir): - config = { - "framework": "caffe", - "weights": str(models_dir / "SampLeNet.caffemodel"), - "model": str(models_dir / "SampLeNet.prototxt"), - "adapter": 'classification', - "device": "cpu" - } - - return create_launcher(config) - - -class TestCaffeLauncher: - def test_launcher_creates(self, models_dir): - assert get_caffe_test_model(models_dir).inputs['data'] == (3, 32, 32) - - def test_infer(self, data_dir, models_dir): - caffe_test_model = get_caffe_test_model(models_dir) - c, h, w = caffe_test_model.inputs['data'] - img_raw = cv2.imread(str(data_dir / '1.jpg')) - img_resized = cv2.resize(img_raw, (w, h)) - input_blob = np.transpose([img_resized], (0, 3, 1, 2)) - res = caffe_test_model.predict([{'data': input_blob.astype(np.float32)}], [{}]) - - assert np.argmax(res[0]['fc3']) == 6 - - def test_caffe_launcher_provide_input_shape_to_adapter(self, mocker, models_dir): - mocker.patch('caffe.Net.forward', return_value={'fc3': 0}) - launcher = get_caffe_test_model(models_dir) - zeros = DataRepresentation(np.zeros((1, 3, 32, 32))) - launcher.predict([{'data': zeros.data}], [zeros.metadata]) - assert zeros.metadata['input_shape'] == {'data': (3, 32, 32)} - - -def test_missed_model_in_create_caffe_launcher_raises_config_error_exception(): - launcher = {'framework': 'caffe', 'weights': 'custom', 'adapter': 'classification'} - - with pytest.raises(ConfigError): - create_launcher(launcher) - - -def test_missed_weights_in_create_caffe_launcher_raises_config_error_exception(): - launcher = {'framework': 'caffe', 'model': 'custom', 'adapter': 'ssd'} - - with pytest.raises(ConfigError): - create_launcher(launcher) - - -def dummy_adapter(): - pass diff --git a/tools/accuracy_checker/tests/test_config_reader.py b/tools/accuracy_checker/tests/test_config_reader.py deleted file mode 100644 index b03e23a74e57bf..00000000000000 --- a/tools/accuracy_checker/tests/test_config_reader.py +++ /dev/null @@ -1,1295 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import copy -from pathlib import Path -from argparse import Namespace - -import pytest -from accuracy_checker.config import ConfigReader, ConfigError - - -class TestConfigReader: - def setup_method(self): - self.global_launchers = [ - { - 'framework': 'dlsdk', - 'device': 'fpga', - 'cpu_extensions': 'dlsdk_shared.so', - 'bitstream': 'bitstream' - }, - { - 'framework': 'caffe', - 'device': 'gpu_0' - } - ] - - self.global_datasets = [ - { - 'name': 'global_dataset', - 'annotation': Path('/pascal_voc_2007_annotation.pickle'), - 'data_source': Path('/VOCdevkit/VOC2007/JPEGImages'), - 'preprocessing': [ - { - 'type': 'resize', - 'interpolation': 'mean_image', - }, - { - 'type': 'normalization', - 'mean': 'voc', - } - ], - 'metrics': [{ - 'type': 'fppi', - 'mr_rates': [0.0, 0.1] - }], - 'postprocessing': [ - { - 'type': 'filter', - 'labels': ['dog', 'airplane'], - 'min_confidence': 0.05, - 'min_box_size': 60, - }, - { - 'type': 'nms', - 'overlap': 0.5 - } - ] - } - ] - - self.global_config = { - 'launchers': self.global_launchers, - 'datasets': self.global_datasets - } - - self.module = 'accuracy_checker.config.ConfigReader' - self.arguments = Namespace(**{ - 'models': Path('models'), - 'extensions': Path('extensions'), - 'source': Path('source'), - 'annotations': Path('annotations'), - 'converted_models': Path('converted_models'), - 'model_optimizer': Path('model_optimizer'), - 'bitstreams': Path('bitstreams'), - 'definitions': None, - 'stored_predictions': None, - 'tf_custom_op_config': None, - 'tf_obj_detection_api_pipeline_config_path': None, - 'progress': 'bar', - 'target_framework': None, - 'target_devices': None, - 'log_file': None, - 'target_tags': None, - 'cpu_extensions_mode': None, - 'aocl': None - }) - - def test_read_configs_without_global_config(self, mocker): - config = {'models': [{ - 'name': 'model', - 'launchers': [{'framework': 'dlsdk', 'model': Path('/absolute_path'), 'weights': Path('/absolute_path')}], - 'datasets': [{'name': 'global_dataset'}] - }]} - empty_args = Namespace(**{ - 'models': None, 'extensions': None, 'source': None, 'annotations': None, - 'converted_models': None, 'model_optimizer': None, 'bitstreams': None, - 'definitions': None, 'config': None, 'stored_predictions': None, 'tf_custom_op_config': None, - 'progress': 'bar', 'target_framework': None, 'target_devices': None, 'log_file': None, - 'tf_obj_detection_api_pipeline_config_path': None, 'target_tags': None, 'cpu_extensions_mode': None, - 'aocl': None - }) - mocker.patch('accuracy_checker.utils.get_path', return_value=Path.cwd()) - mocker.patch('yaml.load', return_value=config) - mocker.patch('pathlib.Path.open') - - result = ConfigReader.merge(empty_args) - - assert 'models' == result[1] - assert config == result[0] - - def test_empty_local_config_raises_value_error_exception(self, mocker): - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, {} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Missing local config' - - def test_missed_models_in_local_config_raises_value_error_exception(self, mocker): - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, {'not_models': 'custom'} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Missed "{}" in local config'.format('models') - - def test_empty_models_in_local_config_raises_value_error_exception(self, mocker): - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, {'models': []} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Missed "{}" in local config'.format('models') - - def test_missed_name_in_model_raises_value_error_exception(self, mocker): - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, {'models': [{'launchers': None, 'datasets': None}]} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Each model must specify {}'.format(', '.join(['name', 'launchers', 'datasets'])) - - def test_missed_launchers_in_model_raises_value_error_exception(self, mocker): - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, {'models': [{'name': None, 'datasets': None}]} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Each model must specify {}'.format(', '.join(['name', 'launchers', 'datasets'])) - - def test_missed_datasets_in_model_raises_value_error_exception(self, mocker): - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, {'models': [{'name': None, 'launchers': None}]} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Each model must specify {}'.format(', '.join(['name', 'launchers', 'datasets'])) - - def test_invalid_model_raises_value_error_exception(self, mocker): - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, {'models': [{'name': None, 'launchers': None, 'datasets': None}]} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Each model must specify {}'.format(', '.join(['name', 'launchers', 'datasets'])) - - def test_empty_pipeline_in_local_config_raises_value_error_exception(self, mocker): - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, {'pipelines': []} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Missed "{}" in local config'.format('pipelines') - - def test_missed_name_in_pipeline_raises_value_error_exception(self, mocker): - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, {'pipelines': [{'device_info': None, 'stages': None}]} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Each pipeline must specify {}'.format(', '.join(['name', 'device_info', 'stages'])) - - def test_missed_device_info_in_pipeline_raises_value_error_exception(self, mocker): - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, {'pipelines': [{'name': None, 'stages': None}]} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Each pipeline must specify {}'.format(', '.join(['name', 'device_info', 'stages'])) - - def test_missed_stages_in_pipeline_raises_value_error_exception(self, mocker): - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, {'pipelines': [{'name': None, 'device_info': None}]} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Each pipeline must specify {}'.format(', '.join(['name', 'device_info', 'stages'])) - - def test_invalid_pipeline_raises_value_error_exception(self, mocker): - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, {'pipelines': [{'name': None, 'device_info': None, 'stages': None}]} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Each pipeline must specify {}'.format(', '.join(['name', 'device_info', 'stages'])) - - def test_pipeline_empty_stages_raises_value_error_exception(self, mocker): - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, {'pipelines': [{'name': 'stage1', 'device_info': [{'framework': 'caffe', 'device': 'CPU'}], 'stages': []}]} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Each pipeline must specify {}'.format(', '.join(['name', 'device_info', 'stages'])) - - def test_pipeline_empty_device_info_raises_value_error_exception(self, mocker): - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, {'pipelines': [{'name': 'stage1', 'device_info': [], 'stages': [{'stage1': {}}]}]} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Each pipeline must specify {}'.format(', '.join(['name', 'device_info', 'stages'])) - - def test_pipeline_stage_does_not_contain_dataset_raises_value_error_exception(self, mocker): - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, { - 'pipelines': [{'name': 'stage1', 'device_info': [{'framework': 'caffe', 'device': 'CPU'}], - 'stages': [{'stage': 'stage1'}]}]} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'First stage should contain dataset' - - def test_pipeline_contains_several_datasets_raises_value_error_exception(self, mocker): - dataset_config = { - 'name': 'global_dataset', - 'dataset_meta': 'relative_annotation_path', - 'data_source': 'relative_source_path', - 'segmentation_masks_source': 'relative_source_path', - 'annotation': 'relative_annotation_path' - } - launcher_config = {'framework': 'dlsdk', 'model': '/absolute_path', 'weights': '/absolute_path'} - pipelines_config = [ - {'name': 'pipeline', 'device_info': [{'framework': 'caffe', 'device': 'CPU'}], - 'stages': [{'stage': 'stage1', 'dataset': dataset_config}, - {'stage': 'stage2', 'dataset': dataset_config, 'launcher': launcher_config, 'metrics': {}} - ] - } - ] - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, { - 'pipelines': pipelines_config} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Exactly one dataset per pipeline is supported' - - def test_pipeline_without_launchers_raises_value_error_exception(self, mocker): - dataset_config = { - 'name': 'global_dataset', - 'dataset_meta': 'relative_annotation_path', - 'data_source': 'relative_source_path', - 'segmentation_masks_source': 'relative_source_path', - 'annotation': 'relative_annotation_path' - } - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, { - 'pipelines': [{'name': 'stage1', 'device_info': [{'framework': 'caffe', 'device': 'CPU'}], - 'stages': [{'stage': 'stage1', 'dataset': dataset_config}]}]} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Launchers are not specified' - - def test_pipeline_without_metrics_raises_value_error_exception(self, mocker): - dataset_config = { - 'name': 'global_dataset', - 'dataset_meta': 'relative_annotation_path', - 'annotation': 'relative_annotation_path' - } - launcher_config = {'framework': 'dlsdk', 'model': '/absolute_path', 'weights': '/absolute_path'} - mocker.patch(self.module + '._read_configs', return_value=( - None, { - 'pipelines': [{'name': 'stage1', 'device_info': [{'framework': 'caffe', 'device': 'CPU'}], - 'stages': [{'stage': 'stage1', 'dataset': dataset_config, 'launcher': launcher_config}]}]} - )) - - with pytest.raises(ConfigError) as exception: - ConfigReader.merge(self.arguments) - - error_message = str(exception).split(sep=': ')[-1] - assert error_message == 'Metrics are not specified' - - def test_merge_datasets_with_definitions(self, mocker): - local_config = {'models': [{ - 'name': 'model', - 'launchers': [{'framework': 'dlsdk', 'model': '/absolute_path', 'weights': '/absolute_path'}], - 'datasets': [{'name': 'global_dataset'}] - }]} - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, local_config - )) - arguments = copy.deepcopy(self.arguments) - arguments.model_optimizer = None - - config = ConfigReader.merge(arguments)[0] - - assert config['models'][0]['datasets'][0] == self.global_datasets[0] - - def test_merge_datasets_with_definitions_and_meta_is_not_modified(self, mocker): - local_config = {'models': [{ - 'name': 'model', - 'launchers': [{'framework': 'dlsdk', 'model': '/absolute_path', 'weights': '/absolute_path'}], - 'datasets': [{'name': 'global_dataset', 'dataset_meta': '/absolute_path'}] - }]} - expected = self.global_datasets[0] - expected['dataset_meta'] = Path('/absolute_path') - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, local_config - )) - - config = ConfigReader.merge(self.arguments)[0] - - assert config['models'][0]['datasets'][0] == expected - - def test_expand_relative_paths_in_datasets_config_using_command_line(self, mocker): - local_config = {'models': [{ - 'name': 'model', - 'launchers': [{'framework': 'caffe'}], - 'datasets': [{ - 'name': 'global_dataset', - 'dataset_meta': 'relative_annotation_path', - 'data_source': 'relative_source_path', - 'segmentation_masks_source': 'relative_source_path', - 'annotation': 'relative_annotation_path' - }] - }]} - - mocker.patch(self.module + '._read_configs', return_value=( - None, local_config - )) - expected = copy.deepcopy(local_config['models'][0]['datasets'][0]) - expected['annotation'] = self.arguments.annotations / 'relative_annotation_path' - expected['dataset_meta'] = self.arguments.annotations / 'relative_annotation_path' - expected['segmentation_masks_source'] = self.arguments.source / 'relative_source_path' - expected['data_source'] = self.arguments.source / 'relative_source_path' - - config = ConfigReader.merge(self.arguments)[0] - - assert config['models'][0]['datasets'][0] == expected - - def test_not_modify_absolute_paths_in_datasets_config_using_command_line(self): - local_config = {'models': [{ - 'name': 'model', - 'datasets': [{ - 'name': 'global_dataset', - 'dataset_meta': '/absolute_annotation_meta_path', - 'data_source': '/absolute_source_path', - 'annotation': '/absolute_annotation_path', - }] - }]} - - expected = copy.deepcopy(local_config['models'][0]['datasets'][0]) - expected['annotation'] = Path('/absolute_annotation_path') - expected['dataset_meta'] = Path('/absolute_annotation_meta_path') - expected['data_source'] = Path('/absolute_source_path') - - ConfigReader._merge_paths_with_prefixes(self.arguments, local_config) - - assert local_config['models'][0]['datasets'][0] == expected - - def test_expand_relative_paths_in_pipeline_stage_dataset_config_using_command_line(self, mocker): - dataset_config = { - 'name': 'global_dataset', - 'dataset_meta': 'relative_annotation_path', - 'data_source': 'relative_source_path', - 'segmentation_masks_source': 'relative_source_path', - 'annotation': 'relative_annotation_path' - } - launcher_config = {'framework': 'dlsdk', 'model': '/absolute_path', 'weights': '/absolute_path'} - pipelines_config = [ - { - 'name': 'pipeline', 'device_info': [{'framework': 'caffe', 'device': 'CPU'}], - 'stages': [ - {'stage': 'stage1', 'dataset': dataset_config}, - {'stage': 'stage2', 'launcher': launcher_config, 'metrics': {}} - ] - } - ] - mocker.patch(self.module + '._read_configs', return_value=( - None, { - 'pipelines': pipelines_config} - )) - - expected = copy.deepcopy(dataset_config) - expected['annotation'] = self.arguments.annotations / 'relative_annotation_path' - expected['dataset_meta'] = self.arguments.annotations / 'relative_annotation_path' - expected['segmentation_masks_source'] = self.arguments.source / 'relative_source_path' - expected['data_source'] = self.arguments.source / 'relative_source_path' - - config = ConfigReader.merge(self.arguments)[0] - - assert config['pipelines'][0]['stages'][0]['dataset'] == expected - - def test_not_modify_absolute_paths_in_pipeline_stage_dataset_config_using_command_line(self, mocker): - dataset_config = { - 'name': 'global_dataset', - 'dataset_meta': '/absolute_annotation_meta_path', - 'data_source': '/absolute_source_path', - 'annotation': '/absolute_annotation_path' - } - launcher_config = {'framework': 'dlsdk', 'model': '/absolute_path', 'weights': '/absolute_path'} - pipelines_config = [ - { - 'name': 'pipeline', 'device_info': [{'device': 'CPU'}], - 'stages': [ - {'stage': 'stage1', 'dataset': dataset_config}, - {'stage': 'stage2', 'launcher': launcher_config, 'metrics': {}} - ] - } - ] - mocker.patch(self.module + '._read_configs', return_value=( - None, { - 'pipelines': pipelines_config} - )) - - expected = copy.deepcopy(dataset_config) - expected['annotation'] = Path('/absolute_annotation_path') - expected['dataset_meta'] = Path('/absolute_annotation_meta_path') - expected['data_source'] = Path('/absolute_source_path') - - config = ConfigReader.merge(self.arguments)[0] - - assert config['pipelines'][0]['stages'][0]['dataset'] == expected - - def test_merge_launcher_with_device_info(self, mocker): - dataset_config = { - 'name': 'global_dataset', - 'dataset_meta': '/absolute_annotation_meta_path', - 'data_source': '/absolute_source_path', - 'annotation': '/absolute_annotation_path' - } - launcher_config = {'framework': 'caffe', 'model': Path('/absolute_path'), 'weights': Path('/absolute_path')} - device_info = {'device': 'CPU'} - expected = copy.deepcopy(launcher_config) - expected.update(device_info) - pipelines_config = [ - { - 'name': 'pipeline', 'device_info': [device_info], - 'stages': [ - {'stage': 'stage1', 'dataset': dataset_config}, - {'stage': 'stage2', 'launcher': launcher_config, 'metrics': {}} - ] - } - ] - mocker.patch(self.module + '._read_configs', return_value=( - None, { - 'pipelines': pipelines_config} - )) - - config = ConfigReader.merge(self.arguments)[0] - - assert config['pipelines'][0]['stages'][1]['launcher'] == expected - - def test_merge_launcher_with_2_device_info(self, mocker): - dataset_config = { - 'name': 'global_dataset', - 'dataset_meta': '/absolute_annotation_meta_path', - 'data_source': '/absolute_source_path', - 'annotation': '/absolute_annotation_path' - } - launcher_config = {'framework': 'caffe', 'model': Path('/absolute_path'), 'weights': Path('/absolute_path')} - device_info = [{'device': 'CPU'}, {'device': 'GPU'}] - expected = [copy.deepcopy(launcher_config), copy.deepcopy(launcher_config)] - expected[0].update(device_info[0]) - expected[1].update(device_info[1]) - pipelines_config = [ - { - 'name': 'pipeline', 'device_info': device_info, - 'stages': [ - {'stage': 'stage1', 'dataset': dataset_config}, - {'stage': 'stage2', 'launcher': launcher_config, 'metrics': {}} - ] - } - ] - mocker.patch(self.module + '._read_configs', return_value=( - None, { - 'pipelines': pipelines_config} - )) - - config = ConfigReader.merge(self.arguments)[0] - assert len(config['pipelines']) == 2 - assert config['pipelines'][0]['stages'][1]['launcher'] == expected[0] - assert config['pipelines'][1]['stages'][1]['launcher'] == expected[1] - - def test_merge_launchers_with_definitions(self, mocker): - local_config = {'models': [{ - 'name': 'model', - 'launchers': [{'framework': 'dlsdk'}], - 'datasets': [{'name': 'global_dataset'}] - }]} - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, local_config - )) - expected = copy.deepcopy(self.get_global_launcher('dlsdk')) - expected['bitstream'] = self.arguments.bitstreams / expected['bitstream'] - expected['cpu_extensions'] = self.arguments.extensions / expected['cpu_extensions'] - args = copy.deepcopy(self.arguments) - args.model_optimizer = None - args.converted_models = None - args.models = None - - config = ConfigReader.merge(args)[0] - - assert config['models'][0]['launchers'][0] == expected - - def test_merge_launchers_with_model_is_not_modified(self, mocker): - local_config = {'models': [{ - 'name': 'model', - 'launchers': [{'framework': 'dlsdk', 'model': 'custom'}], - 'datasets': [{'name': 'global_dataset'}] - }]} - expected = copy.deepcopy(self.get_global_launcher('dlsdk')) - expected['model'] = 'custom' - expected['bitstream'] = self.arguments.bitstreams / expected['bitstream'] - expected['cpu_extensions'] = self.arguments.extensions / expected['cpu_extensions'] - mocker.patch(self.module + '._read_configs', return_value=( - self.global_config, local_config - )) - args = copy.deepcopy(self.arguments) - args.model_optimizer = None - args.models = None - args.converted_models = None - config = ConfigReader.merge(args)[0] - - assert config['models'][0]['launchers'][0] == expected - - def test_expand_relative_paths_in_launchers_config_using_command_line(self, mocker): - local_config = {'models': [{ - 'name': 'model', - 'launchers': [{ - 'framework': 'dlsdk', - 'model': 'relative_model_path', - 'weights': 'relative_weights_path', - 'cpu_extensions': 'relative_extensions_path', - 'gpu_extensions': 'relative_extensions_path', - 'caffe_model': 'relative_model_path', - 'caffe_weights': 'relative_weights_path', - 'tf_model': 'relative_model_path', - 'mxnet_weights': 'relative_weights_path', - 'bitstream': 'relative_bitstreams_path' - }], - 'datasets': [{'name': 'dataset'}] - }]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - - expected = copy.deepcopy(local_config['models'][0]['launchers'][0]) - expected['model'] = self.arguments.models / 'relative_model_path' - expected['caffe_model'] = self.arguments.models / 'relative_model_path' - expected['tf_model'] = self.arguments.models / 'relative_model_path' - expected['weights'] = self.arguments.models / 'relative_weights_path' - expected['caffe_weights'] = self.arguments.models / 'relative_weights_path' - expected['mxnet_weights'] = self.arguments.models / 'relative_weights_path' - expected['cpu_extensions'] = self.arguments.extensions / 'relative_extensions_path' - expected['gpu_extensions'] = self.arguments.extensions / 'relative_extensions_path' - expected['bitstream'] = self.arguments.bitstreams / 'relative_bitstreams_path' - expected['_models_prefix'] = self.arguments.models - args = copy.deepcopy(self.arguments) - args.model_optimizer = None - args.converted_models = None - config = ConfigReader.merge(args)[0] - - assert config['models'][0]['launchers'][0] == expected - - def test_both_launchers_are_filtered_by_target_tags_if_tags_not_provided_in_config(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'model': '/absolute_path1', - 'weights': '/absolute_path1', - 'adapter': 'classification', - 'device': 'CPU', - }, - { - 'framework': 'dlsdk', - 'model': '/absolute_path2', - 'weights': '/absolute_path2', - 'adapter': 'classification', - 'device': 'GPU', - } - ] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - self.arguments.target_tags = ['some_tag'] - - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - - with pytest.warns(Warning): - config = ConfigReader.merge(self.arguments)[0] - - launchers = config['models'][0]['launchers'] - assert len(launchers) == 0 - - def test_launcher_is_not_filtered_by_the_same_tag(self, mocker): - config_launchers = [{ - 'framework': 'dlsdk', - 'tags': ['some_tag'], - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - args = copy.deepcopy(self.arguments) - args.model_optimizer = None - args.converted_models = None - args.target_tags = ['some_tag'] - - config = ConfigReader.merge(args)[0] - - launchers = config['models'][0]['launchers'] - assert launchers[0] == config_launchers[0] - - def test_both_launchers_are_not_filtered_by_the_same_tag(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'tags': ['some_tag'], - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }, - { - 'framework': 'dlsdk', - 'tags': ['some_tag'], - 'model': Path('/absolute_path2'), - 'weights': Path('/absolute_path2'), - 'adapter': 'classification', - 'device': 'GPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - } - ] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - args = copy.deepcopy(self.arguments) - args.model_optimizer = None - args.converted_models = None - args.target_tags = ['some_tag'] - - config = ConfigReader.merge(args)[0] - - launchers = config['models'][0]['launchers'] - assert launchers == config_launchers - - def test_both_launchers_are_filtered_by_another_tag(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'tags': ['some_tag'], - 'model': '/absolute_path1', - 'weights': '/absolute_path1', - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }, - { - 'framework': 'dlsdk', - 'tags': ['some_tag'], - 'model': '/absolute_path2', - 'weights': '/absolute_path2', - 'adapter': 'classification', - 'device': 'GPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - } - ] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - args = copy.deepcopy(self.arguments) - args.model_optimizer = None - args.converted_models = None - args.target_tags = ['other_tag'] - - with pytest.warns(Warning): - config = ConfigReader.merge(args)[0] - - launchers = config['models'][0]['launchers'] - assert len(launchers) == 0 - - def test_only_appropriate_launcher_is_filtered_by_another_tag(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'tags': ['tag1'], - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }, - { - 'framework': 'caffe', - 'tags': ['tag2'], - 'model': Path('/absolute_path2'), - 'weights': Path('/absolute_path2'), - 'adapter': 'classification', - 'device': 'GPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - } - ] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - self.arguments.target_tags = ['tag2'] - - config = ConfigReader.merge(self.arguments)[0] - - launchers = config['models'][0]['launchers'] - assert len(launchers) == 1 - assert launchers[0] == config_launchers[1] - - def test_only_appropriate_launcher_is_filtered_by_another_tag_if_provided_several_target_tags(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'tags': ['tag1'], - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }, - { - 'framework': 'caffe', - 'tags': ['tag2'], - 'model': Path('/absolute_path2'), - 'weights': Path('/absolute_path2'), - 'adapter': 'classification', - 'device': 'GPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - } - ] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - self.arguments.target_tags = ['tag2', 'tag3'] - - config = ConfigReader.merge(self.arguments)[0] - - launchers = config['models'][0]['launchers'] - assert len(launchers) == 1 - assert launchers[0] == config_launchers[1] - - def test_launcher_with_several_tags_contained_at_least_one_from_target_tegs_is_not_filtered(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'tags': ['tag1', 'tag2'], - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - } - ] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - args = copy.deepcopy(self.arguments) - args.model_optimizer = None - args.converted_models = None - args.target_tags = ['tag2'] - - config = ConfigReader.merge(args)[0] - - launchers = config['models'][0]['launchers'] - assert len(launchers) == 1 - assert launchers[0] == config_launchers[0] - - def test_both_launchers_with_different_tags_are_not_filtered_by_the_same_tags(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'tags': ['tag1'], - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }, - { - 'framework': 'dlsdk', - 'tags': ['tag2'], - 'model': Path('/absolute_path2'), - 'weights': Path('/absolute_path2'), - 'adapter': 'classification', - 'device': 'GPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - } - ] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - args = copy.deepcopy(self.arguments) - args.model_optimizer = None - args.converted_models = None - args.target_tags = ['tag1', 'tag2'] - - config = ConfigReader.merge(args)[0] - - launchers = config['models'][0]['launchers'] - assert launchers == config_launchers - - def test_launcher_is_not_filtered_by_the_same_framework(self, mocker): - config_launchers = [{ - 'framework': 'dlsdk', - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - args = copy.deepcopy(self.arguments) - args.model_optimizer = None - args.converted_models = None - args.target_framework = 'dlsdk' - - config = ConfigReader.merge(args)[0] - - launchers = config['models'][0]['launchers'] - assert launchers == config_launchers - - def test_both_launchers_are_not_filtered_by_the_same_framework(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }, - { - 'framework': 'dlsdk', - 'model': Path('/absolute_path2'), - 'weights': Path('/absolute_path2'), - 'adapter': 'classification', - 'device': 'GPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - } - ] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - args = copy.deepcopy(self.arguments) - args.model_optimizer = None - args.converted_models = None - args.target_framework = 'dlsdk' - - config = ConfigReader.merge(args)[0] - - launchers = config['models'][0]['launchers'] - assert launchers == config_launchers - - def test_launcher_is_filtered_by_another_framework(self, mocker): - config_launchers = [{ - 'framework': 'dlsdk', - 'model': Path('/absolute_path'), - 'weights': Path('/absolute_path'), - 'adapter': 'classification', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - self.arguments.target_framework = 'caffe' - - with pytest.warns(Warning): - config = ConfigReader.merge(self.arguments)[0] - - launchers = config['models'][0]['launchers'] - assert len(launchers) == 0 - - def test_both_launchers_are_filtered_by_another_framework(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'model': '/absolute_path1', - 'weights': '/absolute_path1', - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }, - { - 'framework': 'dlsdk', - 'model': '/absolute_path2', - 'weights': '/absolute_path2', - 'adapter': 'classification', - 'device': 'GPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - } - ] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - self.arguments.target_framework = 'caffe' - - with pytest.warns(Warning): - config = ConfigReader.merge(self.arguments)[0] - - launchers = config['models'][0]['launchers'] - assert len(launchers) == 0 - - def test_only_appropriate_launcher_is_filtered_by_another_framework(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }, - { - 'framework': 'caffe', - 'model': Path('/absolute_path2'), - 'weights': Path('/absolute_path2'), - 'adapter': 'classification', - 'device': 'GPU' - } - ] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - self.arguments.target_framework = 'caffe' - - config = ConfigReader.merge(self.arguments)[0] - - launchers = config['models'][0]['launchers'] - assert len(launchers) == 1 - assert launchers[0] == config_launchers[1] - - def test_launcher_is_not_filtered_by_the_same_device(self, mocker): - config_launchers = [{ - 'framework': 'dlsdk', - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - args = copy.deepcopy(self.arguments) - args.model_optimizer = None - args.converted_models = None - args.target_devices = ['CPU'] - - config = ConfigReader.merge(args)[0] - - launchers = config['models'][0]['launchers'] - assert launchers == config_launchers - - def test_both_launchers_are_not_filtered_by_the_same_device(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }, - { - 'framework': 'caffe', - 'model': Path('/absolute_path2'), - 'weights': Path('/absolute_path2'), - 'adapter': 'classification', - 'device': 'CPU' - } - ] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - args = copy.deepcopy(self.arguments) - args.converted_models = None - args.target_devices = ['CPU'] - - config = ConfigReader.merge(args)[0] - - launchers = config['models'][0]['launchers'] - assert launchers == config_launchers - - def test_launcher_is_filtered_by_another_device(self, mocker): - config_launchers = [{ - 'framework': 'dlsdk', - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - args = copy.deepcopy(self.arguments) - args.converted_models = None - args.target_devices = ['GPU'] - - with pytest.warns(Warning): - config = ConfigReader.merge(args)[0] - - launchers = config['models'][0]['launchers'] - assert len(launchers) == 0 - - def test_both_launchers_are_filtered_by_another_device(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }, - { - 'framework': 'caffe', - 'model': Path('/absolute_path2'), - 'weights': Path('/absolute_path2'), - 'adapter': 'classification', - 'device': 'CPU' - } - ] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - self.arguments.target_devices = ['GPU'] - - with pytest.warns(Warning): - config = ConfigReader.merge(self.arguments)[0] - - launchers = config['models'][0]['launchers'] - assert len(launchers) == 0 - - def test_only_appropriate_launcher_is_filtered_by_another_device(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }, - { - 'framework': 'caffe', - 'model': Path('/absolute_path2'), - 'weights': Path('/absolute_path2'), - 'adapter': 'classification', - 'device': 'GPU' - } - ] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - args = copy.deepcopy(self.arguments) - args.converted_models = None - args.target_devices = ['GPU'] - - config = ConfigReader.merge(args)[0] - - launchers = config['models'][0]['launchers'] - assert len(launchers) == 1 - assert launchers[0] == config_launchers[1] - - def test_only_appropriate_launcher_is_filtered_by_user_input_devices(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }, - { - 'framework': 'dlsdk', - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'HETERO:CPU,GPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }, - { - 'framework': 'caffe', - 'model': Path('/absolute_path2'), - 'weights': Path('/absolute_path2'), - 'adapter': 'classification', - 'device': 'GPU', - } - ] - - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - args = copy.deepcopy(self.arguments) - args.converted_models = None - args.target_devices = ['GPU', 'CPU'] - - config = ConfigReader.merge(args)[0] - - launchers = config['models'][0]['launchers'] - assert launchers == [config_launchers[0], config_launchers[2]] - - def test_both_launchers_are_filtered_by_other_devices(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'model': '/absolute_path1', - 'weights': '/absolute_path1', - 'adapter': 'classification', - 'device': 'CPU', - }, - { - 'framework': 'caffe', - 'model': '/absolute_path2', - 'weights': '/absolute_path2', - 'adapter': 'classification', - 'device': 'CPU' - } - ] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - self.arguments.target_devices = ['FPGA', 'MYRIAD'] - - with pytest.warns(Warning): - config = ConfigReader.merge(self.arguments)[0] - - launchers = config['models'][0]['launchers'] - assert len(launchers) == 0 - - def test_both_launchers_are_not_filtered_by_same_devices(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }, - { - 'framework': 'caffe', - 'model': Path('/absolute_path2'), - 'weights': Path('/absolute_path2'), - 'adapter': 'classification', - 'device': 'GPU' - } - ] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - args = copy.deepcopy(self.arguments) - args.converted_models = None - args.target_devices = ['GPU', 'CPU'] - - config = ConfigReader.merge(args)[0] - - launchers = config['models'][0]['launchers'] - assert launchers == config_launchers - - def test_launcher_is_not_filtered_by_device_with_tail(self, mocker): - config_launchers = [ - { - 'framework': 'dlsdk', - 'model': Path('/absolute_path1'), - 'weights': Path('/absolute_path1'), - 'adapter': 'classification', - 'device': 'CPU', - '_model_optimizer': self.arguments.model_optimizer, - '_models_prefix': self.arguments.models - }, - { - 'framework': 'caffe', - 'model': Path('/absolute_path2'), - 'weights': Path('/absolute_path2'), - 'adapter': 'classification', - 'device': 'GPU' - } - ] - local_config = {'models': [{'name': 'name', 'launchers': config_launchers, 'datasets': [{'name': 'dataset'}]}]} - mocker.patch(self.module + '._read_configs', return_value=(None, local_config)) - args = copy.deepcopy(self.arguments) - args.converted_models = None - args.target_devices = ['CPU', 'GPU_unexpected_tail'] - - config = ConfigReader.merge(args)[0] - - launchers = config['models'][0]['launchers'] - assert len(launchers) == 1 - assert launchers[0] == config_launchers[0] - - def get_global_launcher(self, framework): - for launcher in self.global_launchers: - if launcher['framework'] == framework: - return launcher - - raise ValueError('Undefined global launcher with framework = "{}"'.format(framework)) - - def get_global_dataset(self, name): - for dataset in self.global_datasets: - if dataset['name'] == name: - return dataset - - raise ValueError('Undefined global dataset with name = "{}"'.format(name)) diff --git a/tools/accuracy_checker/tests/test_config_validator.py b/tools/accuracy_checker/tests/test_config_validator.py deleted file mode 100644 index eae65762f63899..00000000000000 --- a/tools/accuracy_checker/tests/test_config_validator.py +++ /dev/null @@ -1,385 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from math import inf, nan -from pathlib import Path -from unittest.mock import ANY - -import pytest -from accuracy_checker.config.config_validator import ( - ConfigError, - ConfigValidator, - DictField, - ListField, - NumberField, - PathField, - StringField -) -from tests.common import mock_filesystem - - -class TestStringField: - def test_expects_string(self): - string_field = StringField() - - with pytest.raises(ConfigError): - string_field.validate(b"foo") - with pytest.raises(ConfigError): - string_field.validate({}) - with pytest.raises(ConfigError): - string_field.validate(42) - - string_field.validate("foo") - - def test_choices(self): - string_field = StringField(choices=['foo', 'bar']) - - with pytest.raises(ConfigError): - string_field.validate('baz') - - string_field.validate('bar') - - def test_case_sensitive(self): - string_field = StringField(choices=['foo', 'bar'], case_sensitive=False) - - string_field.validate('foo') - string_field.validate('FOO') - - string_field = StringField(choices=['foo', 'bar'], case_sensitive=True) - - string_field.validate('foo') - with pytest.raises(ConfigError): - string_field.validate('FOO') - - def test_regex(self): - string_field = StringField(regex=r'foo\d*') - - string_field.validate('foo') - string_field.validate('foo42') - - with pytest.raises(ConfigError): - string_field.validate('baz') - - def test_custom_exception(self, mocker): - stub = mocker.stub(name='custom_on_error') - string_field = StringField(choices=['foo'], on_error=stub) - - with pytest.raises(ConfigError): - string_field.validate('bar', 'foo') - stub.assert_called_once_with('bar', 'foo', ANY) - - def test_custom_validator(self, mocker): - stub = mocker.stub(name='custom_validator') - string_field = StringField(choices=['foo'], additional_validator=stub) - - string_field.validate('foo', 'baz') - stub.assert_called_once_with('foo', 'baz') - - -class TestNumberField: - def test_expects_number(self): - number_field = NumberField(floats=True) - - number_field.validate(1.0) - with pytest.raises(ConfigError): - number_field.validate("foo") - with pytest.raises(ConfigError): - number_field.validate({}) - with pytest.raises(ConfigError): - number_field.validate([]) - - number_field = NumberField(floats=False) - number_field.validate(1) - with pytest.raises(ConfigError): - number_field.validate(1.0) - - def test_nans(self): - number_field = NumberField(allow_nan=True) - number_field.validate(nan) - - number_field = NumberField(allow_nan=False) - with pytest.raises(ConfigError): - number_field.validate(nan) - - def test_infinity(self): - number_field = NumberField(allow_inf=True) - number_field.validate(inf) - - number_field = NumberField(allow_inf=False) - with pytest.raises(ConfigError): - number_field.validate(inf) - - def test_ranges(self): - number_field = NumberField(min_value=0, max_value=5) - - number_field.validate(0) - number_field.validate(1) - number_field.validate(2) - - with pytest.raises(ConfigError): - number_field.validate(-1) - with pytest.raises(ConfigError): - number_field.validate(7) - - -class TestDictField: - def test_expects_dict(self): - dict_field = DictField() - - dict_field.validate({}) - with pytest.raises(ConfigError): - dict_field.validate("foo") - with pytest.raises(ConfigError): - dict_field.validate(42) - with pytest.raises(ConfigError): - dict_field.validate([]) - - def test_validates_keys(self): - dict_field = DictField() - dict_field.validate({'foo': 42, 1: 'bar'}) - - dict_field = DictField(key_type=str) - dict_field.validate({'foo': 42, 'bar': 'bar'}) - with pytest.raises(ConfigError): - dict_field.validate({'foo': 42, 1: 'bar'}) - - dict_field = DictField(key_type=StringField(choices=['foo', 'bar'])) - dict_field.validate({'foo': 42, 'bar': 42}) - with pytest.raises(ConfigError): - dict_field.validate({'foo': 42, 1: 'bar'}) - with pytest.raises(ConfigError): - dict_field.validate({'foo': 42, 'baz': 42}) - - def test_validates_values(self): - dict_field = DictField() - dict_field.validate({'foo': 42, 1: 'bar'}) - - dict_field = DictField(value_type=str) - dict_field.validate({'foo': 'foo', 1: 'bar'}) - with pytest.raises(ConfigError): - dict_field.validate({'foo': 42, 1: 2}) - - dict_field = DictField(value_type=StringField(choices=['foo', 'bar'])) - dict_field.validate({1: 'foo', 'bar': 'bar'}) - with pytest.raises(ConfigError): - dict_field.validate({1: 'foo', 2: 3}) - with pytest.raises(ConfigError): - dict_field.validate({1: 'foo', 2: 'baz'}) - - def test_converts_basic_types(self): - dict_field = DictField(value_type=str) - assert isinstance(dict_field.value_type, StringField) - - dict_field = DictField(value_type=int) - assert isinstance(dict_field.value_type, NumberField) - assert dict_field.value_type.floats is False - - dict_field = DictField(value_type=float) - assert isinstance(dict_field.value_type, NumberField) - assert dict_field.value_type.floats is True - - dict_field = DictField(value_type=list) - assert isinstance(dict_field.value_type, ListField) - - dict_field = DictField(value_type=dict) - assert isinstance(dict_field.value_type, DictField) - - dict_field = DictField(value_type=Path) - assert isinstance(dict_field.value_type, PathField) - - def test_empty(self): - dict_field = DictField() - dict_field.validate({}) - - dict_field = DictField(allow_empty=False) - with pytest.raises(ConfigError): - dict_field.validate({}) - - -class TestListField: - def test_expects_list(self): - list_field = ListField() - - list_field.validate([]) - with pytest.raises(ConfigError): - list_field.validate("foo") - with pytest.raises(ConfigError): - list_field.validate(42) - with pytest.raises(ConfigError): - list_field.validate({}) - - def test_validates_values(self): - list_field = ListField() - list_field.validate(['foo', 42]) - - list_field = ListField(value_type=str) - list_field.validate(['foo', 'bar']) - with pytest.raises(ConfigError): - list_field.validate(['foo', 42]) - - list_field = ListField(value_type=StringField(choices=['foo', 'bar'])) - list_field.validate(['foo', 'bar']) - with pytest.raises(ConfigError): - list_field.validate(['foo', 42]) - with pytest.raises(ConfigError): - list_field.validate(['foo', 'bar', 'baz']) - - def test_empty(self): - list_field = ListField() - list_field.validate([]) - - list_field = ListField(allow_empty=False) - with pytest.raises(ConfigError): - list_field.validate([]) - - -class TestPathField: - @pytest.mark.usefixtures('mock_path_exists') - def test_expects_path_like(self): - path_field = PathField() - path_field.validate('foo/bar') - path_field.validate('/home/user') - path_field.validate(Path('foo/bar')) - - with pytest.raises(ConfigError): - path_field.validate(42) - with pytest.raises(ConfigError): - path_field.validate({}) - with pytest.raises(ConfigError): - path_field.validate([]) - - def test_path_is_checked(self): - with mock_filesystem(['foo/bar']) as prefix: - prefix_path = Path(prefix) - file_field = PathField(is_directory=False) - with pytest.raises(ConfigError): - file_field.validate(prefix_path / 'foo') - file_field.validate(prefix_path / 'foo' / 'bar') - - dir_field = PathField(is_directory=True) - dir_field.validate(prefix_path / 'foo') - - with pytest.raises(ConfigError): - dir_field.validate(prefix_path / 'foo' / 'bar') - - def test_path_not_checked(self): - with mock_filesystem(['foo/bar']) as prefix: - prefix_path = Path(prefix) - file_field = PathField(is_directory=False, check_exists=False) - file_field.validate(prefix_path / 'foo' / 'bar') - - -class TestConfigValidator: - def test_compound(self): - class SampleValidator(ConfigValidator): - foo = StringField(choices=['foo']) - bar = NumberField() - - sample_validator = SampleValidator('Sample') - sample_validator.validate({'foo': 'foo', 'bar': 1}) - - with pytest.raises(ConfigError): - sample_validator.validate({'foo': 'foo'}) - with pytest.raises(ConfigError): - sample_validator.validate({'foo': 'bar', 'bar': 1}) - - def test_optional_fields(self): - class SampleValidatorNoOptionals(ConfigValidator): - foo = StringField(choices=['foo']) - bar = NumberField(optional=False) - - sample_validator = SampleValidatorNoOptionals('Sample') - sample_validator.validate({'foo': 'foo', 'bar': 1}) - with pytest.raises(ConfigError): - sample_validator.validate({'foo': 'bar'}) - - class SampleValidatorWithOptionals(ConfigValidator): - foo = StringField(choices=['foo']) - bar = NumberField(optional=True) - - sample_validator = SampleValidatorWithOptionals('Sample') - sample_validator.validate({'foo': 'foo', 'bar': 1}) - sample_validator.validate({'foo': 'foo'}) - - def test_extra_fields__warn_on_extra(self): - class SampleValidatorWarnOnExtra(ConfigValidator): - foo = StringField(choices=['foo']) - - sample_validator = SampleValidatorWarnOnExtra( - 'Sample', on_extra_argument=ConfigValidator.WARN_ON_EXTRA_ARGUMENT - ) - - with pytest.warns(UserWarning): - sample_validator.validate({'foo': 'foo', 'bar': 'bar'}) - - def test_extra_fields__error_on_extra(self): - class SampleValidatorErrorOnExtra(ConfigValidator): - foo = StringField(choices=['foo']) - - sample_validator = SampleValidatorErrorOnExtra( - 'Sample', on_extra_argument=ConfigValidator.ERROR_ON_EXTRA_ARGUMENT) - - with pytest.raises(ConfigError): - sample_validator.validate({'foo': 'bar', 'bar': 'bar'}) - - def test_extra_fields__ignore_extra(self): - class SampleValidatorIgnoresExtra(ConfigValidator): - foo = StringField(choices=['foo']) - - sample_validator = SampleValidatorIgnoresExtra( - 'Sample', on_extra_argument=ConfigValidator.IGNORE_ON_EXTRA_ARGUMENT) - - sample_validator.validate({'foo': 'foo', 'bar': 'bar'}) - - def test_custom_exception(self, mocker): - class SampleValidator(ConfigValidator): - foo = StringField(choices=['foo']) - - stub = mocker.stub(name='custom_on_error') - sample_validator = SampleValidator('Sample', on_error=stub) - sample_validator.validate({}) - stub.assert_called_once_with(ANY, 'Sample', ANY) - - def test_custom_validator(self, mocker): - class SampleValidator(ConfigValidator): - foo = StringField(choices=['foo']) - - stub = mocker.stub(name='custom_validator') - sample_validator = SampleValidator('Sample', additional_validator=stub) - entry = {'foo': 'foo'} - sample_validator.validate(entry) - stub.assert_called_once_with(entry, 'Sample') - - def test_nested(self): - class InnerValidator(ConfigValidator): - foo = StringField(choices=['foo']) - - class OuterValidator(ConfigValidator): - bar = ListField(InnerValidator('Inner')) - - outer_validator = OuterValidator('Outer', on_extra_argument=ConfigValidator.ERROR_ON_EXTRA_ARGUMENT) - - outer_validator.validate({'bar': [{'foo': 'foo'}, {'foo': 'foo'}]}) - - def test_inheritance(self): - class ParentValidator(ConfigValidator): - foo = StringField(choices=['foo']) - - class DerivedValidator(ParentValidator): - bar = StringField(choices=['bar']) - - derived_validator = DerivedValidator('Derived', on_extra_argument=ConfigValidator.ERROR_ON_EXTRA_ARGUMENT) - derived_validator.validate({'foo': 'foo', 'bar': 'bar'}) diff --git a/tools/accuracy_checker/tests/test_dataset.py b/tools/accuracy_checker/tests/test_dataset.py deleted file mode 100644 index 4d8d15f40e52e6..00000000000000 --- a/tools/accuracy_checker/tests/test_dataset.py +++ /dev/null @@ -1,220 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -import copy -from pathlib import Path -import pytest -from .common import make_representation -from accuracy_checker.config import ConfigError - -from accuracy_checker.dataset import Dataset - -def copy_dataset_config(config): - new_config = copy.deepcopy(config) - - return new_config - -class MockPreprocessor: - @staticmethod - def process(images): - return images - - -class TestDataset: - dataset_config = { - 'name': 'custom', - 'annotation': 'custom', - 'data_source': 'custom', - 'metrics': [{'type': 'map'}] - } - - def test_missed_name_raises_config_error_exception(self): - local_dataset = copy_dataset_config(self.dataset_config) - local_dataset.pop('name') - - with pytest.raises(ConfigError): - Dataset(local_dataset) - - def test_setting_custom_dataset_with_missed_annotation_raises_config_error_exception(self): - local_dataset = copy_dataset_config(self.dataset_config) - local_dataset.pop('annotation') - with pytest.raises(ConfigError): - Dataset(local_dataset) - - -@pytest.mark.usefixtures('mock_path_exists') -class TestAnnotationConversion: - dataset_config = { - 'name': 'custom', - 'data_source': 'custom', - 'metrics': [{'type': 'map'}] - } - - def test_annotation_conversion_unknown_converter_raise_config_error(self): - addition_options = {'annotation_conversion': {'converter': 'unknown'}} - config = copy_dataset_config(self.dataset_config) - config.update(addition_options) - with pytest.raises(ValueError): - Dataset(config) - - def test_annotation_conversion_converter_without_required_options_raise_config_error(self): - addition_options = {'annotation_conversion': {'converter': 'wider'}} - config = copy_dataset_config(self.dataset_config) - config.update(addition_options) - with pytest.raises(ConfigError): - Dataset(config) - - def test_annotation_conversion_raise_config_error_on_extra_args(self): - addition_options = {'annotation_conversion': {'converter': 'wider', 'annotation_file': 'file', 'something_extra': 'extra'}} - config = copy_dataset_config(self.dataset_config) - config.update(addition_options) - with pytest.raises(ConfigError): - Dataset(config) - - def test_sucessful_annotation_conversion(self, mocker): - addition_options = {'annotation_conversion': {'converter': 'wider', 'annotation_file': Path('file')}} - config = copy_dataset_config(self.dataset_config) - config.update(addition_options) - annotation_converter_mock = mocker.patch( - 'accuracy_checker.annotation_converters.WiderFormatConverter.convert', - return_value=(make_representation("0 0 0 5 5", True), None) - ) - Dataset(config) - annotation_converter_mock.assert_called_once_with() - - def test_annotation_conversion_not_convert_twice(self, mocker): - addition_options = { - 'annotation_conversion': {'converter': 'wider', 'annotation_file': Path('file')}, - 'annotation': Path('custom') - } - config = copy_dataset_config(self.dataset_config) - config.update(addition_options) - converted_annotation = make_representation('0 0 0 5 5', True) - annotation_reader_mock = mocker.patch( - 'accuracy_checker.dataset.read_annotation', - return_value=(converted_annotation, None) - ) - Dataset(config) - - annotation_reader_mock.assert_called_once_with(Path('custom')) - - def test_annotation_conversion_with_store_annotation(self, mocker): - addition_options = { - 'annotation_conversion': {'converter': 'wider', 'annotation_file':'file'}, - 'annotation': Path('custom') - } - config = copy_dataset_config(self.dataset_config) - config.update(addition_options) - converted_annotation = make_representation('0 0 0 5 5', True) - mocker.patch( - 'accuracy_checker.annotation_converters.WiderFormatConverter.convert', - return_value=(converted_annotation, None) - ) - mocker.patch('pathlib.Path.exists', return_value=False) - annotation_saver_mock = mocker.patch( - 'accuracy_checker.dataset.save_annotation' - ) - Dataset(config) - - annotation_saver_mock.assert_called_once_with(converted_annotation, None, Path('custom'), None) - - def test_annotation_conversion_subset_size(self, mocker): - addition_options = { - 'annotation_conversion': {'converter': 'wider', 'annotation_file': 'file'}, - 'subsample_size': 1 - } - config = copy_dataset_config(self.dataset_config) - config.update(addition_options) - converted_annotation = make_representation(['0 0 0 5 5', '0 1 1 10 10'], True) - mocker.patch( - 'accuracy_checker.annotation_converters.WiderFormatConverter.convert', - return_value=(converted_annotation, None) - ) - dataset = Dataset(config) - assert dataset.annotation == [converted_annotation[1]] - - def test_annotation_conversion_subset_ratio(self, mocker): - addition_options = { - 'annotation_conversion': {'converter': 'wider', 'annotation_file': 'file'}, - 'subsample_size': '50%' - } - config = copy_dataset_config(self.dataset_config) - config.update(addition_options) - converted_annotation = make_representation(['0 0 0 5 5', '0 1 1 10 10'], True) - mocker.patch( - 'accuracy_checker.annotation_converters.WiderFormatConverter.convert', - return_value=(converted_annotation, None) - ) - subset_maker_mock = mocker.patch( - 'accuracy_checker.dataset.make_subset' - ) - Dataset(config) - subset_maker_mock.assert_called_once_with(converted_annotation, 1, 666) - - def test_annoation_conversion_subset_more_than_dataset_size(self, mocker): - addition_options = { - 'annotation_conversion': {'converter': 'wider', 'annotation_file': Path('file')}, - 'subsample_size': 3, - 'subsample_seed': 1 - } - config = copy_dataset_config(self.dataset_config) - config.update(addition_options) - converted_annotation = make_representation(['0 0 0 5 5', '0 1 1 10 10'], True) - mocker.patch( - 'accuracy_checker.annotation_converters.WiderFormatConverter.convert', - return_value=(converted_annotation, None) - ) - with pytest.warns(UserWarning): - dataset = Dataset(config) - annotation = dataset.annotation - assert annotation == converted_annotation - - def test_annotation_conversion_subset_with_seed(self, mocker): - addition_options = { - 'annotation_conversion': {'converter': 'wider', 'annotation_file': Path('file')}, - 'subsample_size': 1, - 'subsample_seed': 1 - } - config = copy_dataset_config(self.dataset_config) - config.update(addition_options) - converted_annotation = make_representation(['0 0 0 5 5', '0 1 1 10 10'], True) - mocker.patch( - 'accuracy_checker.annotation_converters.WiderFormatConverter.convert', - return_value=(converted_annotation, None) - ) - dataset = Dataset(config) - annotation = dataset.annotation - assert annotation == [converted_annotation[0]] - - def test_annotation_conversion_save_subset(self, mocker): - addition_options = { - 'annotation_conversion': {'converter': 'wider', 'annotation_file': 'file'}, - 'annotation': Path('custom'), - 'subsample_size': 1, - } - config = copy_dataset_config(self.dataset_config) - config.update(addition_options) - converted_annotation = make_representation(['0 0 0 5 5', '0 1 1 10 10'], True) - mocker.patch( - 'accuracy_checker.annotation_converters.WiderFormatConverter.convert', - return_value=(converted_annotation, None) - ) - annotation_saver_mock = mocker.patch( - 'accuracy_checker.dataset.save_annotation' - ) - mocker.patch('pathlib.Path.exists', return_value=False) - Dataset(config) - annotation_saver_mock.assert_called_once_with([converted_annotation[1]], None, Path('custom'), None) - diff --git a/tools/accuracy_checker/tests/test_dependency.py b/tools/accuracy_checker/tests/test_dependency.py deleted file mode 100644 index 0f98842aa39c16..00000000000000 --- a/tools/accuracy_checker/tests/test_dependency.py +++ /dev/null @@ -1,89 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from accuracy_checker.dependency import ClassProvider, get_opts - - -def test_get_opts_positional_and_kwargs(): - opts = {'o': ((1,), {'a': 1})} - args, kwargs = get_opts(opts['o']) - - assert args == (1,) - assert kwargs == {'a': 1} - - -def test_get_opts_kwargs_only(): - opts = {'o': {'a': 1}} - args, kwargs = get_opts(opts['o']) - - assert args == () - assert kwargs == {'a': 1} - - -def test_get_opts_positional_only(): - opts = {'o': (1, 2, 3)} - args, kwargs = get_opts(opts['o']) - - assert args == (1, 2, 3) - assert kwargs == {} - - -def test_class_provider(): - class BaseService(ClassProvider): - __provider_type__ = 'Service' - - class ServiceA(BaseService): - __provider__ = 'service_a' - - class ServiceB(BaseService): - __provider__ = 'service_b' - - assert issubclass(ServiceA, BaseService) - assert issubclass(ServiceB, BaseService) - - assert 'service_a' in BaseService.providers - assert 'service_b' in BaseService.providers - - -def test_provide(): - class BaseService(ClassProvider): - __provider_type__ = 'service' - - def __init__(self): - pass - - class ServiceA(BaseService): - __provider__ = 'service_a' - - provided = BaseService.provide('service_a') - - assert isinstance(provided, ServiceA) - - -def test_provide_with_args(): - class BaseService(ClassProvider): - __provider_type__ = 'service' - - def __init__(self, bar): - self.bar = bar - - class ServiceA(BaseService): - __provider__ = 'service_a' - - provided = BaseService.provide('service_a', bar=42) - - assert isinstance(provided, ServiceA) - assert provided.bar == 42 diff --git a/tools/accuracy_checker/tests/test_detection_metrics.py b/tools/accuracy_checker/tests/test_detection_metrics.py deleted file mode 100644 index def13549991c98..00000000000000 --- a/tools/accuracy_checker/tests/test_detection_metrics.py +++ /dev/null @@ -1,459 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import pytest -import numpy as np -from accuracy_checker.metrics import DetectionMAP -from accuracy_checker.metrics.detection import Recall, bbox_match -from accuracy_checker.metrics.overlap import IOU, IOA -from tests.common import (make_representation, single_class_dataset, multi_class_dataset, - multi_class_dataset_without_background) - - -def _test_metric_wrapper(metric_cls, dataset, **kwargs): - provider = metric_cls.__provider__ - config = {'type': provider, 'name': provider} - config.update(**kwargs) - return metric_cls(config, dataset, provider) - - -class TestBoxMatch: - def test_single(self): - gt = "0 0 0 5 5" - pred = "0 0 0 5 5" - - gt = make_representation(gt, is_ground_truth=True) - pred = make_representation(pred, score=1) - overlap_evaluator = IOU({}) - - tp, fp, _, _ = bbox_match(gt, pred, 0, overlap_evaluator) - assert tp[0] == 1 - assert fp[0] == 0 - - def test_single_with_ignored_tp(self): - gt = "0 0 0 5 5" - pred = "0 0 0 5 5" - - gt = make_representation(gt, is_ground_truth=True) - pred = make_representation(pred, score=1) - pred[0].metadata['difficult_boxes'] = [0] - overlap_evaluator = IOU({}) - - tp, fp, _, _ = bbox_match(gt, pred, 0, overlap_evaluator) - assert tp[0] == 0 - assert fp[0] == 0 - - def test_single_with_use_filtered_tp(self): - gt = "0 0 0 5 5" - pred = "0 0 0 5 5" - - gt = make_representation(gt, is_ground_truth=True) - pred = make_representation(pred, score=1) - pred[0].metadata['difficult_boxes'] = [0] - overlap_evaluator = IOU({}) - - tp, fp, _, _ = bbox_match(gt, pred, 0, overlap_evaluator, use_filtered_tp=True) - assert tp[0] == 1 - assert fp[0] == 0 - - def test_single_non_overlap(self): - gt = make_representation("0 5 5 10 10", is_ground_truth=True) - pred = make_representation("0 0 0 5 5", score=1) - overlap_evaluator = IOU({}) - - tp, fp, _, _ = bbox_match(gt, pred, 0, overlap_evaluator) - assert tp[0] == 0 - assert fp[0] == 1 - - def test_single_non_overlap_ignored(self): - gt = make_representation("0 5 5 10 10", is_ground_truth=True) - pred = make_representation("0 0 0 5 5", score=1) - pred[0].metadata['difficult_boxes'] = [0] - overlap_evaluator = IOU({}) - - tp, fp, _, _ = bbox_match(gt, pred, 0, overlap_evaluator) - assert tp[0] == 0 - assert fp[0] == 0 - - def test_multiple(self): - gt = make_representation("0 0 0 5 5; 0 7 7 8 8", is_ground_truth=True) - pred = make_representation("0 0 0 5 5; 0 7 7 8 8", score=1) - overlap_evaluator = IOU({}) - - tp, fp, _, _ = bbox_match(gt, pred, 0, overlap_evaluator) - assert tp[0] == 1 - assert tp[1] == 1 - assert fp[0] == 0 - assert fp[0] == 0 - - def test_multiple_2(self): - gt = make_representation("0 0 0 5 5; 0 9 9 10 10", is_ground_truth=True) - pred = make_representation("1 0 0 0 5 5; 0.8 0 7 7 8 8") - overlap_evaluator = IOU({}) - - tp, fp, _, _ = bbox_match(gt, pred, 0, overlap_evaluator) - assert tp[0] == 1 - assert tp[1] == 0 - assert fp[0] == 0 - assert fp[1] == 1 - - def test_multi_label(self): - gt = make_representation("1 0 0 5 5; 0 9 9 10 10", is_ground_truth=True) - pred = make_representation("1 1 0 0 5 5; 0.8 0 7 7 8 8") - overlap_evaluator = IOU({}) - - tp, fp, _, _ = bbox_match(gt, pred, 1, overlap_evaluator) - assert tp.shape[0] == 1 - assert tp[0] == 1 - assert fp[0] == 0 - - tp, fp, _, _ = bbox_match(gt, pred, 0, overlap_evaluator) - assert tp.shape[0] == 1 - assert tp[0] == 0 - assert fp[0] == 1 - - def test_multi_image(self): - gt = make_representation(["0 0 0 5 5", "0 0 0 5 5"], is_ground_truth=True) - pred = make_representation(["0 0 0 5 5", "0 0 0 5 5"], score=1) - overlap_evaluator = IOU({}) - - tp, fp, _, _ = bbox_match(gt, pred, 0, overlap_evaluator) - assert tp[0] == 1 - assert tp[1] == 1 - assert fp[0] == 0 - assert fp[1] == 0 - - def test_false_negative(self): - gt = make_representation("0 0 0 5 5; 0 1 1 6 6", is_ground_truth=True) - pred = make_representation("0 0 0 5 5", score=1) - overlap_evaluator = IOU({}) - - tp, fp, _, ngt = bbox_match(gt, pred, 0, overlap_evaluator) - assert tp[0] == 1 - assert tp.shape[0] == 1 - assert ngt == 2 - - def test_multiple_detections(self): - gt = make_representation("0 0 0 5 5", is_ground_truth=True) - pred = make_representation("1 0 0 0 5 5; 0.9 0 0 0 5 5") - overlap_evaluator = IOU({}) - - tp, fp, _, _ = bbox_match(gt, pred, 0, overlap_evaluator) - assert tp[0] == 1 - assert tp[1] == 0 - - def test_no_annotations(self): - gt = "1 0 0 5 5" - pred = "0 0 0 5 5" - - gt = make_representation(gt, is_ground_truth=True) - pred = make_representation(pred, score=1) - overlap_evaluator = IOU({}) - - tp, fp, _, _ = bbox_match(gt, pred, 0, overlap_evaluator) - assert tp[0] == 0 - assert fp[0] == 1 - - def test_no_predictions(self): - gt = "0 0 0 5 5" - pred = "1 0 0 5 5" - - gt = make_representation(gt, is_ground_truth=True) - pred = make_representation(pred, score=1) - overlap_evaluator = IOU({}) - - tp, fp, _, n = bbox_match(gt, pred, 0, overlap_evaluator) - assert n == 1 - assert len(tp) == 0 - assert len(fp) == 0 - - def test_iou_empty_prediction_box(self): - gt = "0 0 0 5 5" - pred = "0 0 0 0 0" - - gt = make_representation(gt, is_ground_truth=True) - pred = make_representation(pred, score=1) - overlap_evaluator = IOU({}) - - with pytest.warns(None) as warnings: - tp, fp, _, n = bbox_match(gt, pred, 0, overlap_evaluator) - assert len(warnings) == 0 - assert n == 1 - assert tp[0] == 0 - assert fp[0] == 1 - - def test_ioa_empty_prediction_box(self): - gt = "0 0 0 5 5" - pred = "0 0 0 0 0" - - gt = make_representation(gt, is_ground_truth=True) - pred = make_representation(pred, score=1) - overlap_evaluator = IOA({}) - - with pytest.warns(None) as warnings: - tp, fp, _, n = bbox_match(gt, pred, 0, overlap_evaluator) - assert len(warnings) == 0 - assert n == 1 - assert tp[0] == 0 - assert fp[0] == 1 - - def test_iou_zero_union(self): - gt = "0 0 0 0 0" - pred = "0 0 0 0 0" - - gt = make_representation(gt, is_ground_truth=True) - pred = make_representation(pred, score=1) - overlap_evaluator = IOA({}) - - with pytest.warns(None) as warnings: - tp, fp, _, n = bbox_match(gt, pred, 0, overlap_evaluator) - assert len(warnings) == 0 - assert n == 1 - assert tp[0] == 0 - assert fp[0] == 1 - - def test_single_difficult(self): - gt = "0 0 0 5 5" - pred = "0 0 0 5 5" - - gt = make_representation(gt, is_ground_truth=True) - pred = make_representation(pred, score=1) - gt[0].metadata['difficult_boxes'] = [0] - overlap_evaluator = IOU({}) - - tp, fp, _, n = bbox_match(gt, pred, 0, overlap_evaluator, ignore_difficult=True) - assert n == 0 - assert tp[0] == 0 - assert fp[0] == 0 - - def test_single_with_not_ignore_difficult(self): - gt = "0 0 0 5 5" - pred = "0 0 0 5 5" - - gt = make_representation(gt, is_ground_truth=True) - pred = make_representation(pred, score=1) - gt[0].metadata['difficult_boxes'] = [0] - overlap_evaluator = IOU({}) - - tp, fp, _, n = bbox_match(gt, pred, 0, overlap_evaluator, ignore_difficult=False) - assert n == 1 - assert tp[0] == 1 - assert fp[0] == 0 - - def test_single_difficult_non_overlap(self): - gt = make_representation("0 5 5 10 10", is_ground_truth=True) - gt[0].metadata['difficult_boxes'] = [0] - pred = make_representation("0 0 0 5 5", score=1) - overlap_evaluator = IOU({}) - - tp, fp, _, n = bbox_match(gt, pred, 0, overlap_evaluator) - assert n == 0 - assert tp[0] == 0 - assert fp[0] == 1 - - def test_single_difficult_non_overlap_not_ignore_difficult(self): - gt = make_representation("0 5 5 10 10", is_ground_truth=True) - gt[0].metadata['difficult_boxes'] = [0] - pred = make_representation("0 0 0 5 5", score=1) - overlap_evaluator = IOU({}) - - tp, fp, _, n = bbox_match(gt, pred, 0, overlap_evaluator, ignore_difficult=False) - assert n == 1 - assert tp[0] == 0 - assert fp[0] == 1 - - def test_multiple_detections_with_ignore_difficult(self): - gt = make_representation("0 0 0 5 5", is_ground_truth=True) - pred = make_representation("1 0 0 0 5 5; 0.9 0 0 0 5 5") - gt[0].metadata['difficult_boxes'] = [0] - overlap_evaluator = IOU({}) - - tp, fp, _, n = bbox_match(gt, pred, 0, overlap_evaluator, ignore_difficult=True) - assert n == 0 - assert tp[0] == 0 - assert tp[1] == 0 - assert fp[0] == 0 - assert fp[1] == 0 - - def test_multiple_detections_with_not_ignore_difficult(self): - gt = make_representation("0 0 0 5 5", is_ground_truth=True) - pred = make_representation("1 0 0 0 5 5; 0.9 0 0 0 5 5") - gt[0].metadata['difficult_boxes'] = [0] - overlap_evaluator = IOU({}) - - tp, fp, _, n = bbox_match(gt, pred, 0, overlap_evaluator, ignore_difficult=False) - assert n == 1 - assert tp[0] == 1 - assert tp[1] == 0 - assert fp[0] == 0 - assert fp[1] == 1 - - def test_multiple_detections_with_ignore_difficult_and_not_allow_multiple_matches_per_ignored(self): - gt = make_representation("0 0 0 5 5", is_ground_truth=True) - pred = make_representation("1 0 0 0 5 5; 0.9 0 0 0 5 5") - gt[0].metadata['difficult_boxes'] = [0] - overlap_evaluator = IOU({}) - - tp, fp, _, n = bbox_match( - gt, pred, 0, overlap_evaluator, - ignore_difficult=True, allow_multiple_matches_per_ignored=False - ) - - assert n == 0 - assert tp[0] == 0 - assert tp[1] == 0 - assert fp[0] == 0 - assert fp[1] == 1 - - -class TestRecall: - def test_one_object(self): - gt = make_representation(["0 0 0 5 5"], is_ground_truth=True) - pred = make_representation(["0 0 0 5 5"], score=1) - metric = _test_metric_wrapper(Recall, single_class_dataset()) - assert 1 == metric(gt, pred)[0] - assert metric.meta.get('names') == ['dog'] - - def test_two_objects(self): - gt = make_representation(["0 0 0 5 5; 0 10 10 20 20"], is_ground_truth=True) - pred = make_representation(["0 0 0 5 5; 0 10 10 20 20"], score=1) - assert 1 == _test_metric_wrapper(Recall, single_class_dataset())(gt, pred)[0] - - def test_false_positive(self): - gt2 = make_representation(["0 10 10 20 20"], is_ground_truth=True) - pred2 = make_representation(["0 0 0 5 5"], score=1) - metric = _test_metric_wrapper(Recall, single_class_dataset()) - assert 0 == metric(gt2, pred2)[0] - assert metric.meta.get('names') == ['dog'] - - gt1 = make_representation(["0 0 0 5 5"], is_ground_truth=True) - pred1 = make_representation(["0 0 0 5 5; 0 10 10 20 20"], score=1) - assert 1 == metric(gt1, pred1)[0] - assert metric.meta.get('names') == ['dog'] - - def test_false_negative(self): - gt = make_representation(["0 10 10 20 20; 0 0 0 5 5"], is_ground_truth=True) - pred = make_representation(["0 0 0 5 5"], score=1) - metric = _test_metric_wrapper(Recall, single_class_dataset()) - assert 0.5 == metric(gt, pred)[0] - assert metric.meta.get('names') == ['dog'] - - def test_duplicate_detections(self): - gt = make_representation(["0 0 0 5 5"], is_ground_truth=True) - pred = make_representation(["0 0 0 5 5; 0 0 0 5 5"], score=1) - - metric = _test_metric_wrapper(Recall, single_class_dataset()) - assert 1 == metric(gt, pred)[0] - assert metric.meta.get('names') == ['dog'] - - def test_no_warnings_in_recall_calculation(self): - gt = make_representation(["0 0 0 5 5; 1 10 10 20 20", "1 0 0 5 5"], is_ground_truth=True) - pred = make_representation(["0 0 0 5 5; 1 10 10 20 20", "1 0 0 5 5"], score=1) - - with pytest.warns(None) as warnings: - _test_metric_wrapper(Recall, multi_class_dataset())(gt, pred) - assert len(warnings) == 0 - - def test_on_dataset_without_background(self): - gt = make_representation(["0 0 0 5 5; 1 10 10 20 20", "1 0 0 5 5"], is_ground_truth=True) - pred = make_representation(["0 0 0 5 5; 1 10 10 20 20", "1 0 0 5 5"], score=1) - - with pytest.warns(None) as warnings: - _test_metric_wrapper(Recall, multi_class_dataset_without_background())(gt, pred) - assert len(warnings) == 0 - - def test_not_gt_boxes_for_matching(self): - gt = make_representation(["0 0 0 5 5"], is_ground_truth=True) - pred = make_representation(["1 0 0 5 5"], score=1) - - metric = _test_metric_wrapper(Recall, multi_class_dataset_without_background()) - assert 0 == metric(gt, pred)[0] - assert metric.meta.get('names') == ['cat'] - - -class TestMAP: - def test_selects_all_detections(self): - gt = make_representation(["0 0 0 5 5"], is_ground_truth=True) - pred = make_representation(["0 0 0 5 5; 0 0 0 5 5"], score=1) - - metric = _test_metric_wrapper(DetectionMAP, single_class_dataset()) - metric(gt, pred) - - assert not metric.distinct_conf - assert metric.overlap_threshold == 0.5 - assert metric.ignore_difficult - assert metric.meta.get('names') == ['dog'] - - def test_no_warnings_in_map_calculation(self): - gt = make_representation(["0 0 0 5 5; 1 10 10 20 20", "1 0 0 5 5"], is_ground_truth=True) - pred = make_representation(["0 0 0 5 5; 1 10 10 20 20", "1 0 0 5 5"], score=1) - - with pytest.warns(None) as warnings: - _test_metric_wrapper(DetectionMAP, multi_class_dataset())(gt, pred) - assert len(warnings) == 0 - - def test_perfect_detection(self): - gt = make_representation(["0 0 0 5 5; 1 10 10 20 20", "1 0 0 5 5"], is_ground_truth=True) - pred = make_representation(["0 0 0 5 5; 1 10 10 20 20", "1 0 0 5 5"], score=1) - - metric = _test_metric_wrapper(DetectionMAP, multi_class_dataset()) - assert metric(gt, pred) == [1.0, 1.0] - assert metric.meta.get('names') == ['dog', 'cat'] - - def test_one_false_alarm(self): - gt = make_representation(["0 0 0 5 5", "1 0 0 5 5"], is_ground_truth=True) - pred = make_representation(["1 10 10 20 20; 0 0 0 5 5", "1 0 0 5 5"], score=1) - metric = _test_metric_wrapper(DetectionMAP, multi_class_dataset()) - values = metric(gt, pred) - assert values == [1.0, 0.5] - map_ = np.mean(values) - assert 0.75 == map_ - assert metric.meta.get('names') == ['dog', 'cat'] - - def test_zero_detection(self): - gt = make_representation(["0 0 0 5 5; 1 10 10 20 20"], is_ground_truth=True) - pred = make_representation(["0 30 30 40 40"], score=1) - - metric = _test_metric_wrapper(DetectionMAP, multi_class_dataset()) - assert metric(gt, pred) == [0.0] - assert metric.meta.get('names') == ['dog'] - - def test_no_detections_warn_user_warning(self): - gt = make_representation(["0 0 0 5 5; 1 10 10 20 20"], is_ground_truth=True) - pred = make_representation("", score=1) - with pytest.warns(UserWarning) as warnings: - map_ = _test_metric_wrapper(DetectionMAP, multi_class_dataset())(gt, pred)[0] - assert len(warnings) == 1 - - assert map_ == 0 - - def test_detection_on_dataset_without_background(self): - gt = make_representation(["0 0 0 5 5; 1 10 10 20 20", "1 0 0 5 5"], is_ground_truth=True) - pred = make_representation(["0 0 0 5 5; 1 10 10 20 20", "1 0 0 5 5"], score=1) - - with pytest.warns(None) as warnings: - map_ = _test_metric_wrapper(DetectionMAP, multi_class_dataset_without_background())(gt, pred) - mean = np.mean(map_) - assert 1.0 == mean - assert len(warnings) == 0 - - def test_not_gt_boxes_for_box_matching(self): - gt = make_representation(["0 0 0 5 5"], is_ground_truth=True) - pred = make_representation(["1 0 0 5 5"], score=1) - - metric = _test_metric_wrapper(Recall, multi_class_dataset_without_background()) - assert 0 == metric(gt, pred)[0] - assert metric.meta.get('names') == ['cat'] diff --git a/tools/accuracy_checker/tests/test_dlsdk_launcher.py b/tools/accuracy_checker/tests/test_dlsdk_launcher.py deleted file mode 100644 index 3e772fe34486bc..00000000000000 --- a/tools/accuracy_checker/tests/test_dlsdk_launcher.py +++ /dev/null @@ -1,1121 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import subprocess - -import pytest - -pytest.importorskip('accuracy_checker.launcher.dlsdk_launcher') -import os -import cv2 -import numpy as np - -from pathlib import Path -from unittest.mock import PropertyMock -from accuracy_checker.config import ConfigError -from accuracy_checker.launcher import DLSDKLauncher -from accuracy_checker.launcher.dlsdk_launcher import DLSDKLauncherConfig -from accuracy_checker.launcher.launcher import create_launcher -from accuracy_checker.launcher.model_conversion import FrameworkParameters -from tests.common import update_dict -from accuracy_checker.data_readers import DataRepresentation -from accuracy_checker.utils import contains_all - - -@pytest.fixture() -def mock_inference_engine(mocker): - try: - mocker.patch('openvino.inference_engine.IEPlugin') - mocker.patch('openvino.inference_engine.IENetwork') - except ImportError: - mocker.patch('inference_engine.IEPlugin') - mocker.patch('inference_engine.IENetwork') - - -@pytest.fixture() -def mock_inputs(mocker): - mocker.patch( - 'accuracy_checker.launcher.input_feeder.InputFeeder._parse_inputs_config', return_value=({}, ['data'], None) - ) - - -def get_dlsdk_test_model(models_dir, config_update=None): - config = { - 'framework': 'dlsdk', - 'weights': str(models_dir / 'SampLeNet.bin'), - 'model': str(models_dir / 'SampLeNet.xml'), - 'device': 'CPU', - 'adapter': 'classification', - '_models_prefix': str(models_dir) - } - if config_update: - config.update(config_update) - - return create_launcher(config) - - -def get_image(image_path, input_shape): - _, h, w = input_shape - img_raw = cv2.imread(str(image_path)) - - return DataRepresentation(cv2.resize(img_raw, (w, h))) - - -class TestDLSDKLauncherInfer: - def test_infer(self, data_dir, models_dir): - dlsdk_test_model = get_dlsdk_test_model(models_dir) - image = get_image(data_dir / '1.jpg', dlsdk_test_model.inputs['data']) - input_blob = np.transpose([image.data], (0, 3, 1, 2)) - result = dlsdk_test_model.predict([{'data': input_blob.astype(np.float32)}], [image.metadata]) - assert dlsdk_test_model.output_blob == 'fc3' - - assert np.argmax(result[0][dlsdk_test_model.output_blob]) == 6 - assert image.metadata['input_shape'] == {'data': [3, 32, 32]} - - def test_launcher_creates(self, models_dir): - assert get_dlsdk_test_model(models_dir).inputs['data'] == [3, 32, 32] - - def test_infer_with_additional_outputs(self, data_dir, models_dir): - dlsdk_test_model = get_dlsdk_test_model(models_dir, {'outputs': ['fc1', 'fc2']}) - outputs = list(dlsdk_test_model.network.outputs.keys()) - - assert contains_all(outputs, ['fc1', 'fc2', 'fc3']) - assert dlsdk_test_model.output_blob == 'fc3' - - def test_dlsd_launcher_set_batch_size(self, models_dir): - dlsdk_test_model = get_dlsdk_test_model(models_dir, {'batch': 2}) - assert dlsdk_test_model.batch == 2 - - -@pytest.mark.usefixtures('mock_path_exists') -class TestDLSDKLauncherAffinity: - def test_dlsdk_launcher_valid_affinity_map(self, mocker, models_dir): - affinity_map = {'conv1': 'GPU'} - - mocker.patch( - 'accuracy_checker.launcher.dlsdk_launcher.read_yaml', return_value=affinity_map - ) - - dlsdk_test_model = get_dlsdk_test_model(models_dir, {'device' : 'HETERO:CPU,GPU', 'affinity_map' : './affinity_map.yml'}) - layers = dlsdk_test_model.network.layers - for key, value in affinity_map.items(): - assert layers[key].affinity == value - - def test_dlsdk_launcher_affinity_map_invalid_device(self, mocker, models_dir): - affinity_map = {'conv1': 'GPU'} - - mocker.patch( - 'accuracy_checker.launcher.dlsdk_launcher.read_yaml', return_value=affinity_map - ) - - with pytest.raises(ConfigError): - get_dlsdk_test_model(models_dir, {'device' : 'HETERO:CPU,CPU', 'affinity_map' : './affinity_map.yml'}) - - def test_dlsdk_launcher_affinity_map_invalid_layer(self, mocker, models_dir): - affinity_map = {'none-existing-layer' : 'CPU'} - - mocker.patch( - 'accuracy_checker.launcher.dlsdk_launcher.read_yaml', return_value=affinity_map - ) - - with pytest.raises(ConfigError): - get_dlsdk_test_model(models_dir, {'device' : 'HETERO:CPU,CPU', 'affinity_map' : './affinity_map.yml'}) - - -@pytest.mark.usefixtures('mock_path_exists', 'mock_inference_engine', 'mock_inputs') -class TestDLSDKLauncher: - def test_program_bitsream_when_device_is_fpga(self, mocker): - subprocess_mock = mocker.patch('subprocess.run') - config = { - 'framework': 'dlsdk', - 'weights': 'custom_weights', - 'model': 'custom_model', - 'device': 'fpga', - 'bitstream': Path('custom_bitstream'), - 'adapter': 'classification', - '_models_prefix': 'prefix', - '_aocl': Path('aocl') - } - launcher = create_launcher(config) - subprocess_mock.assert_called_once_with(['aocl', 'program', 'acl0', 'custom_bitstream'], check=True) - launcher.release() - - def test_program_bitsream_when_fpga_in_hetero_device(self, mocker): - subprocess_mock = mocker.patch('subprocess.run') - config = { - 'framework': 'dlsdk', - 'weights': 'custom_weights', - 'model': 'custom_model', - 'device': 'hetero:fpga,cpu', - 'bitstream': Path('custom_bitstream'), - 'adapter': 'classification', - '_models_prefix': 'prefix', - '_aocl': Path('aocl') - } - launcher = create_launcher(config) - subprocess_mock.assert_called_once_with(['aocl', 'program', 'acl0', 'custom_bitstream'], check=True) - launcher.release() - - def test_does_not_program_bitsream_when_device_is_not_fpga(self, mocker): - subprocess_mock = mocker.patch('subprocess.run') - config = { - 'framework': 'dlsdk', - 'weights': 'custom_weights', - 'model': 'custom_model', - 'device': 'cpu', - 'bitstream': Path('custom_bitstream'), - 'adapter': 'classification', - '_models_prefix': 'prefix', - '_aocl': Path('aocl') - } - create_launcher(config) - subprocess_mock.assert_not_called() - - def test_does_not_program_bitsream_when_hetero_without_fpga(self, mocker): - subprocess_mock = mocker.patch('subprocess.run') - - config = { - 'framework': 'dlsdk', - 'weights': 'custom_weights', - 'model': 'custom_model', - 'device': 'hetero:cpu,cpu', - 'bitstream': Path('custom_bitstream'), - 'adapter': 'classification', - '_models_prefix': 'prefix', - '_aocl': Path('aocl') - } - create_launcher(config) - subprocess_mock.assert_not_called() - - def test_does_not_program_bitstream_if_compiler_mode_3_in_env_when_fpga_in_hetero_device(self, mocker): - subprocess_mock = mocker.patch('subprocess.run') - mocker.patch('os.environ.get', return_value='3') - - config = { - 'framework': 'dlsdk', - 'weights': 'custom_weights', - 'model': 'custom_model', - 'device': 'hetero:fpga,cpu', - 'bitstream': Path('custom_bitstream'), - 'adapter': 'classification', - '_models_prefix': 'prefix', - '_aocl': Path('aocl') - } - create_launcher(config) - - subprocess_mock.assert_not_called() - - def test_does_not_program_bitstream_if_compiler_mode_3_in_env_when_fpga_in_device(self, mocker): - subprocess_mock = mocker.patch('subprocess.run') - mocker.patch('os.environ.get', return_value='3') - - config = { - 'framework': 'dlsdk', - 'weights': 'custom_weights', - 'model': 'custom_model', - 'device': 'fpga', - 'bitstream': Path('custom_bitstream'), - 'adapter': 'classification', - '_models_prefix': 'prefix', - '_aocl': Path('aocl') - } - create_launcher(config) - - subprocess_mock.assert_not_called() - - def test_sets_dla_aocx_when_device_is_fpga(self, mocker): - mocker.patch('os.environ') - - config = { - 'framework': 'dlsdk', - 'weights': 'custom_weights', - 'model': 'custom_model', - 'device': 'fpga', - 'bitstream': Path('custom_bitstream'), - 'adapter': 'classification', - '_models_prefix': 'prefix' - } - create_launcher(config) - - os.environ.__setitem__.assert_called_once_with('DLA_AOCX', 'custom_bitstream') - - def test_sets_dla_aocx_when_fpga_in_hetero_device(self, mocker): - mocker.patch('os.environ') - - config = { - 'framework': 'dlsdk', - 'weights': 'custom_weights', - 'model': 'custom_model', - 'device': 'hetero:fpga,cpu', - 'bitstream': Path('custom_bitstream'), - 'adapter': 'classification', - '_models_prefix': 'prefix' - } - create_launcher(config) - os.environ.__setitem__.assert_called_once_with('DLA_AOCX', 'custom_bitstream') - - def test_does_not_set_dla_aocx_when_device_is_not_fpga(self, mocker): - mocker.patch('os.environ') - - config = { - 'framework': 'dlsdk', - 'weights': 'custom_weights', - 'model': 'custom_model', - 'device': 'cpu', - 'bitstream': 'custom_bitstream', - 'adapter': 'classification', - '_models_prefix': 'prefix' - } - create_launcher(config) - - os.environ.__setitem__.assert_not_called() - - def test_does_not_set_dla_aocx_when_hetero_without_fpga(self, mocker): - mocker.patch('os.environ') - - config = { - 'framework': 'dlsdk', - 'weights': 'custom_weights', - 'model': 'custom_model', - 'device': 'hetero:cpu,cpu', - 'bitstream': 'custom_bitstream', - 'adapter': 'classification', - '_models_prefix': 'prefix' - } - create_launcher(config) - - os.environ.__setitem__.assert_not_called() - - def test_does_not_set_dla_aocx_if_compiler_mode_3_in_env_when_fpga_in_hetero_device(self, mocker): - mocker.patch('os.environ') - mocker.patch('os.environ.get', return_value='3') - - config = { - 'framework': 'dlsdk', - 'weights': 'custom_weights', - 'model': 'custom_model', - 'device': 'hetero:fpga,cpu', - 'bitstream': 'custom_bitstream', - 'adapter': 'classification', - '_models_prefix': 'prefix' - } - create_launcher(config) - - os.environ.__setitem__.assert_not_called() - - def test_does_not_set_dla_aocx_if_compiler_mode_3_in_env_when_fpga_in_device(self, mocker): - mocker.patch('os.environ') - mocker.patch('os.environ.get', return_value='3') - - config = { - 'framework': 'dlsdk', - 'weights': 'custom_weights', - 'model': 'custom_model', - 'device': 'fpga', - 'bitstream': 'custom_bitstream', - 'adapter': 'classification', - '_models_prefix': 'prefix' - } - create_launcher(config) - - os.environ.__setitem__.assert_not_called() - - def test_model_converted_from_caffe(self, mocker): - mock = mocker.patch( - 'accuracy_checker.launcher.dlsdk_launcher.convert_model', - return_value=('converted_model', 'converted_weights') - ) - - config = { - 'framework': 'dlsdk', - 'caffe_model': '/path/to/source_models/custom_model', - 'caffe_weights': '/path/to/source_models/custom_weights', - "device": 'cpu', - 'bitstream': Path('custom_bitstream'), - '_models_prefix': '/path/to/source_models', - 'adapter': 'classification' - } - DLSDKLauncher(config) - - mock.assert_called_once_with( - 'custom_model', '/path/to/source_models/custom_model', '/path/to/source_models/custom_weights', '', - FrameworkParameters('caffe', False), - [], None, None, None, None - ) - - def test_model_converted_with_mo_params(self, mocker): - mock = mocker.patch( - 'accuracy_checker.launcher.dlsdk_launcher.convert_model', - return_value=('converted_model', 'converted_weights') - ) - - config = { - 'framework': "dlsdk", - 'caffe_model': '/path/to/source_models/custom_model', - 'caffe_weights': '/path/to/source_models/custom_weights', - 'device': 'cpu', - 'bitstream': Path('custom_bitstream'), - '_models_prefix': '/path/to/source_models', - 'mo_params': {'data_type': 'FP16'}, - 'adapter': 'classification' - } - DLSDKLauncher(config) - - mock.assert_called_once_with( - 'custom_model', '/path/to/source_models/custom_model', '/path/to/source_models/custom_weights', '', - FrameworkParameters('caffe', False), - [], {'data_type': 'FP16'}, None, None, None - ) - - def test_model_converted_with_mo_flags(self, mocker): - mock = mocker.patch( - 'accuracy_checker.launcher.dlsdk_launcher.convert_model', - return_value=('converted_model', 'converted_weights') - ) - - config = { - 'framework': 'dlsdk', - 'caffe_model': '/path/to/source_models/custom_model', - 'caffe_weights': '/path/to/source_models/custom_weights', - 'device': 'cpu', - 'bitstream': Path('custom_bitstream'), - '_models_prefix': '/path/to/source_models', - 'mo_flags': ['reverse_input_channels'], - 'adapter': 'classification' - } - - DLSDKLauncher(config) - - mock.assert_called_once_with( - 'custom_model', '/path/to/source_models/custom_model', '/path/to/source_models/custom_weights', '', - FrameworkParameters('caffe', False), - [], None, ['reverse_input_channels'], None, None - ) - - def test_model_converted_to_output_dir_in_mo_params(self, mocker): - config = { - 'framework': 'dlsdk', - 'tf_model': '/path/to/source_models/custom_model', - 'device': 'cpu', - '_models_prefix': '/path/to', - 'adapter': 'classification', - 'mo_params': {'output_dir': '/path/to/output/models'} - } - mocker.patch('accuracy_checker.launcher.model_conversion.find_mo', return_value='ModelOptimizer') - prepare_args_patch = mocker.patch('accuracy_checker.launcher.model_conversion.prepare_args') - args = { - 'input_model': '/path/to/source_models/custom_model', - 'model_name': 'custom_model', - 'output_dir': '/path/to/output/models', - 'framework': 'tf' - } - - mocker.patch( - 'accuracy_checker.launcher.model_conversion.exec_mo_binary', - return_value=subprocess.CompletedProcess(args, returncode=0) - ) - DLSDKLauncher(config) - prepare_args_patch.assert_called_once_with('ModelOptimizer', flag_options=[], value_options=args) - - def test_model_converted_from_tf(self, mocker): - mock = mocker.patch( - 'accuracy_checker.launcher.dlsdk_launcher.convert_model', - return_value=('converted_model', 'converted_weights') - ) - - config = { - 'framework': 'dlsdk', - 'tf_model': '/path/to/source_models/custom_model', - 'device': 'cpu', - '_models_prefix': '/path/to/source_models', - 'adapter': 'classification' - } - DLSDKLauncher(config) - - mock.assert_called_once_with( - 'custom_model', '/path/to/source_models/custom_model', '', '', - FrameworkParameters('tf', False), [], None, None, None, None - ) - - def test_model_converted_from_tf_checkpoint(self, mocker): - mock = mocker.patch( - 'accuracy_checker.launcher.dlsdk_launcher.convert_model', - return_value=('converted_model', 'converted_weights') - ) - - config = { - 'framework': 'dlsdk', - 'tf_meta': '/path/to/source_models/custom_model', - 'device': 'cpu', - '_models_prefix': '/path/to/source_models', - 'adapter': 'classification' - } - DLSDKLauncher(config) - - mock.assert_called_once_with( - 'custom_model', '', '', '/path/to/source_models/custom_model', - FrameworkParameters('tf', True), [], None, None, None, None - ) - - def test_model_converted_from_tf_with_arg_path_to_custom_tf_config(self, mocker): - config = { - 'framework': 'dlsdk', - 'tf_model': '/path/to/source_models/custom_model', - 'device': 'cpu', - '_models_prefix': '/path/to', - 'adapter': 'classification', - 'mo_params': {'tensorflow_use_custom_operations_config': 'ssd_v2_support.json'}, - '_tf_custom_op_config_dir': 'config/dir' - } - mocker.patch('accuracy_checker.launcher.model_conversion.find_mo', return_value=Path('/path/ModelOptimizer')) - prepare_args_patch = mocker.patch('accuracy_checker.launcher.model_conversion.prepare_args') - - args = { - 'input_model': '/path/to/source_models/custom_model', - 'model_name': 'custom_model', - 'framework': 'tf', - 'tensorflow_use_custom_operations_config': 'config/dir/ssd_v2_support.json' - } - - mocker.patch( - 'accuracy_checker.launcher.model_conversion.exec_mo_binary', - return_value=subprocess.CompletedProcess(args, returncode=0) - ) - DLSDKLauncher(config) - prepare_args_patch.assert_called_once_with('/path/ModelOptimizer', flag_options=[], value_options=args) - - def test_model_converted_from_tf_with_default_path_to_custom_tf_config(self, mocker): - config = { - 'framework': 'dlsdk', - 'tf_model': '/path/to/source_models/custom_model', - 'device': 'cpu', - '_models_prefix': '/path/to', - 'adapter': 'classification', - 'mo_params': {'tensorflow_use_custom_operations_config': 'config.json'} - } - mocker.patch('accuracy_checker.launcher.model_conversion.find_mo', return_value=Path('/path/ModelOptimizer')) - prepare_args_patch = mocker.patch('accuracy_checker.launcher.model_conversion.prepare_args') - - args = { - 'input_model': '/path/to/source_models/custom_model', - 'model_name': 'custom_model', - 'framework': 'tf', - 'tensorflow_use_custom_operations_config': '/path/extensions/front/tf/config.json' - } - - mocker.patch( - 'accuracy_checker.launcher.model_conversion.exec_mo_binary', - return_value=subprocess.CompletedProcess(args, returncode=0) - ) - DLSDKLauncher(config) - prepare_args_patch.assert_called_once_with('/path/ModelOptimizer', flag_options=[], value_options=args) - - def test_model_converted_from_tf_with_default_path_to_obj_detection_api_config(self, mocker): - config = { - 'framework': 'dlsdk', - 'tf_model': '/path/to/source_models/custom_model', - 'device': 'cpu', - '_models_prefix': '/path/to', - 'adapter': 'classification', - 'mo_params': {'tensorflow_object_detection_api_pipeline_config': 'operations.config'}, - '_tf_obj_detection_api_pipeline_config_path': None - } - mocker.patch('accuracy_checker.launcher.model_conversion.find_mo', return_value=Path('/path/ModelOptimizer')) - prepare_args_patch = mocker.patch('accuracy_checker.launcher.model_conversion.prepare_args') - - args = { - 'input_model': '/path/to/source_models/custom_model', - 'model_name': 'custom_model', - 'framework': 'tf', - 'tensorflow_object_detection_api_pipeline_config': '/path/to/source_models/operations.config' - } - - mocker.patch( - 'accuracy_checker.launcher.model_conversion.exec_mo_binary', - return_value=subprocess.CompletedProcess(args, returncode=0) - ) - DLSDKLauncher(config) - prepare_args_patch.assert_called_once_with('/path/ModelOptimizer', flag_options=[], value_options=args) - - def test_model_converted_from_tf_with_arg_path_to_obj_detection_api_config(self, mocker): - config = { - 'framework': 'dlsdk', - 'tf_model': '/path/to/source_models/custom_model', - 'device': 'cpu', - '_models_prefix': '/path/to', - 'adapter': 'classification', - 'mo_params': {'tensorflow_object_detection_api_pipeline_config': 'operations.config'}, - '_tf_custom_op_config_dir': 'config/dir', - '_tf_obj_detection_api_pipeline_config_path': 'od_api' - } - mocker.patch('accuracy_checker.launcher.model_conversion.find_mo', return_value=Path('/path/ModelOptimizer')) - prepare_args_patch = mocker.patch('accuracy_checker.launcher.model_conversion.prepare_args') - - args = { - 'input_model': '/path/to/source_models/custom_model', - 'model_name': 'custom_model', - 'framework': 'tf', - 'tensorflow_object_detection_api_pipeline_config': 'od_api/operations.config' - } - - mocker.patch( - 'accuracy_checker.launcher.model_conversion.exec_mo_binary', - return_value=subprocess.CompletedProcess(args, returncode=0) - ) - DLSDKLauncher(config) - prepare_args_patch.assert_called_once_with('/path/ModelOptimizer', flag_options=[], value_options=args) - - def test_model_converted_from_tf_checkpoint_with_arg_path_to_custom_tf_config(self, mocker): - config = { - 'framework': 'dlsdk', - 'tf_meta': '/path/to/source_models/custom_model', - 'device': 'cpu', - '_models_prefix': '/path/to', - 'adapter': 'classification', - 'mo_params': {'tensorflow_use_custom_operations_config': 'ssd_v2_support.json'}, - '_tf_custom_op_config_dir': 'config/dir' - } - mocker.patch('accuracy_checker.launcher.model_conversion.find_mo', return_value=Path('/path/ModelOptimizer')) - prepare_args_patch = mocker.patch('accuracy_checker.launcher.model_conversion.prepare_args') - - args = { - 'input_meta_graph': '/path/to/source_models/custom_model', - 'model_name': 'custom_model', - 'framework': 'tf', - 'tensorflow_use_custom_operations_config': 'config/dir/ssd_v2_support.json' - } - - mocker.patch( - 'accuracy_checker.launcher.model_conversion.exec_mo_binary', - return_value=subprocess.CompletedProcess(args, returncode=0) - ) - DLSDKLauncher(config) - prepare_args_patch.assert_called_once_with('/path/ModelOptimizer', flag_options=[], value_options=args) - - def test_model_converted_from_tf_checkoint_with_default_path_to_custom_tf_config(self, mocker): - config = { - 'framework': 'dlsdk', - 'tf_meta': '/path/to/source_models/custom_model', - 'device': 'cpu', - '_models_prefix': '/path/to', - 'adapter': 'classification', - 'mo_params': {'tensorflow_use_custom_operations_config': 'config.json'} - } - mocker.patch('accuracy_checker.launcher.model_conversion.find_mo', return_value=Path('/path/ModelOptimizer')) - prepare_args_patch = mocker.patch('accuracy_checker.launcher.model_conversion.prepare_args') - - args = { - 'input_meta_graph': '/path/to/source_models/custom_model', - 'model_name': 'custom_model', - 'framework': 'tf', - 'tensorflow_use_custom_operations_config': '/path/extensions/front/tf/config.json' - } - - mocker.patch( - 'accuracy_checker.launcher.model_conversion.exec_mo_binary', - return_value=subprocess.CompletedProcess(args, returncode=0) - ) - DLSDKLauncher(config) - prepare_args_patch.assert_called_once_with('/path/ModelOptimizer', flag_options=[], value_options=args) - - def test_model_converted_from_tf_checkoint_with_default_path_to_obj_detection_api_config(self, mocker): - config = { - 'framework': 'dlsdk', - 'tf_meta': '/path/to/source_models/custom_model', - 'device': 'cpu', - '_models_prefix': '/path/to', - 'adapter': 'classification', - 'mo_params': {'tensorflow_object_detection_api_pipeline_config': 'operations.config'}, - '_tf_obj_detection_api_pipeline_config_path': None - } - mocker.patch('accuracy_checker.launcher.model_conversion.find_mo', return_value=Path('/path/ModelOptimizer')) - prepare_args_patch = mocker.patch('accuracy_checker.launcher.model_conversion.prepare_args') - - args = { - 'input_meta_graph': '/path/to/source_models/custom_model', - 'model_name': 'custom_model', - 'framework': 'tf', - 'tensorflow_object_detection_api_pipeline_config': '/path/to/source_models/operations.config' - } - - mocker.patch( - 'accuracy_checker.launcher.model_conversion.exec_mo_binary', - return_value=subprocess.CompletedProcess(args, returncode=0) - ) - DLSDKLauncher(config) - prepare_args_patch.assert_called_once_with('/path/ModelOptimizer', flag_options=[], value_options=args) - - def test_model_converted_from_tf_checkpoint_with_arg_path_to_obj_detection_api_config(self, mocker): - config = { - 'framework': 'dlsdk', - 'tf_meta': '/path/to/source_models/custom_model', - 'device': 'cpu', - '_models_prefix': '/path/to', - 'adapter': 'classification', - 'mo_params': {'tensorflow_object_detection_api_pipeline_config': 'operations.config'}, - '_tf_custom_op_config_dir': 'config/dir', - '_tf_obj_detection_api_pipeline_config_path': 'od_api' - } - mocker.patch('accuracy_checker.launcher.model_conversion.find_mo', return_value=Path('/path/ModelOptimizer')) - prepare_args_patch = mocker.patch('accuracy_checker.launcher.model_conversion.prepare_args') - - args = { - 'input_meta_graph': '/path/to/source_models/custom_model', - 'model_name': 'custom_model', - 'framework': 'tf', - 'tensorflow_object_detection_api_pipeline_config': 'od_api/operations.config' - } - - mocker.patch( - 'accuracy_checker.launcher.model_conversion.exec_mo_binary', - return_value=subprocess.CompletedProcess(args, returncode=0) - ) - DLSDKLauncher(config) - prepare_args_patch.assert_called_once_with('/path/ModelOptimizer', flag_options=[], value_options=args) - - def test_model_converted_from_mxnet(self, mocker): - mock = mocker.patch( - 'accuracy_checker.launcher.dlsdk_launcher.convert_model', - return_value=('converted_model', 'converted_weights') - ) - - config = { - 'framework': 'dlsdk', - 'mxnet_weights': '/path/to/source_models/custom_weights', - 'device': 'cpu', - '_models_prefix': '/path/to/source_models', - 'adapter': 'classification' - } - DLSDKLauncher(config) - - mock.assert_called_once_with( - 'custom_weights', '', '/path/to/source_models/custom_weights', '', - FrameworkParameters('mxnet', False), [], None, None, None, None - ) - - def test_model_converted_from_onnx(self, mocker): - mock = mocker.patch( - 'accuracy_checker.launcher.dlsdk_launcher.convert_model', - return_value=('converted_model', 'converted_weights') - ) - - config = { - 'framework': 'dlsdk', - 'onnx_model': '/path/to/source_models/custom_model', - 'device': 'cpu', - '_models_prefix': '/path/to/source_models', - 'adapter': 'classification' - } - DLSDKLauncher(config) - - mock.assert_called_once_with( - 'custom_model', '/path/to/source_models/custom_model', '', '', - FrameworkParameters('onnx', False), [], None, None, None, None - ) - - def test_model_converted_from_kaldi(self, mocker): - mock = mocker.patch( - 'accuracy_checker.launcher.dlsdk_launcher.convert_model', - return_value=('converted_model', 'converted_weights') - ) - - config = { - 'framework': 'dlsdk', - 'kaldi_model': '/path/to/source_models/custom_model', - 'device': 'cpu', - '_models_prefix': '/path/to/source_models', - 'adapter': 'classification' - } - DLSDKLauncher(config) - - mock.assert_called_once_with( - 'custom_model', '/path/to/source_models/custom_model', '', '', - FrameworkParameters('kaldi', False), [], None, None, None, None - ) - - def test_raises_with_multiple_models_caffe_dlsdk(self): - config = { - 'framework': 'dlsdk', - 'caffe_model': 'caffe_model', - 'caffe_weights': 'caffe_weights', - 'model': 'custom_model', - 'weights': 'custom_weights', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_tf_dlsdk(self): - config = { - 'framework': 'dlsdk', - 'tf_model': 'tf_model', - 'model': 'custom_model', - 'weights': 'custom_weights', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_mxnet_dlsdk(self): - config = { - 'framework': 'dlsdk', - 'mxnet_weights': 'mxnet_weights', - 'model': 'custom_model', - 'weights': 'custom_weights', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_onnx_dlsdk(self): - config = { - 'framework': 'dlsdk', - 'onnx_model': 'onnx_model', - 'model': 'custom_model', - 'weights': 'custom_weights', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_kaldi_dlsdk(self): - config = { - 'framework': 'dlsdk', - 'onnx_model': 'kaldi_model', - 'model': 'custom_model', - 'weights': 'custom_weights', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_mxnet_caffe(self): - config = { - 'framework': 'dlsdk', - 'mxnet_weights': 'mxnet_weights', - 'caffe_model': 'caffe_model', - 'caffe_weights': 'caffe_weights', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_tf_caffe(self): - - config = { - 'framework': 'dlsdk', - 'tf_model': 'tf_model', - 'caffe_model': 'caffe_model', - 'caffe_weights': 'caffe_weights', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_onnx_caffe(self): - - config = { - 'framework': 'dlsdk', - 'onnx_model': 'onnx_model', - 'caffe_model': 'caffe_model', - 'caffe_weights': 'caffe_weights', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_mxnet_tf(self): - config = { - 'framework': 'dlsdk', - 'mxnet_weights': 'mxnet_weights', - 'tf_model': 'tf_model', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_onnx_tf(self): - config = { - 'framework': 'dlsdk', - 'onnx_model': 'onnx_model', - 'tf_model': 'tf_model', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_mxnet_caffe_tf(self): - config = { - 'framework': 'dlsdk', - 'mxnet_weights': 'mxnet_weights', - 'caffe_model': 'caffe_model', - 'caffe_weights': 'caffe_weights', - 'tf_model': 'tf_model', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_dlsdk_caffe_tf(self): - config = { - 'framework': 'dlsdk', - 'model': 'custom_model', - 'weights': 'custom_weights', - 'caffe_model': 'caffe_model', - 'caffe_weights': 'caffe_weights', - 'tf_model': 'tf_model', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_dlsdk_caffe_onnx(self): - config = { - 'framework': 'dlsdk', - 'model': 'custom_model', - 'weights': 'custom_weights', - 'caffe_model': 'caffe_model', - 'caffe_weights': 'caffe_weights', - 'onnx_model': 'onnx_model', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_dlsdk_caffe_mxnet(self): - config = { - 'framework': 'dlsdk', - 'model': 'custom_model', - 'weights': 'custom_weights', - 'caffe_model': 'caffe_model', - 'caffe_weights': 'caffe_weights', - 'mxnet_weights': 'mxnet_weights', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_dlsdk_tf_mxnet(self): - config = { - 'framework': "dlsdk", - 'model': 'custom_model', - 'weights': 'custom_weights', - 'mxnet_weights': 'mxnet_weights', - 'tf_model': 'tf_model', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_dlsdk_tf_onnx(self): - config = { - 'framework': 'dlsdk', - 'model': 'custom_model', - 'weights': 'custom_weights', - 'onnx_model': 'onnx_model', - 'tf_model': 'tf_model', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_dlsdk_tf_mxnet_caffe(self): - config = { - 'framework': 'dlsdk', - 'model': 'custom_model', - 'weights': 'custom_weights', - 'caffe_model': 'caffe_model', - 'caffe_weights': 'caffe_weights', - 'mxnet_weights': 'mxnet_weights', - 'onnx_model': 'onnx_model', - 'tf_model': 'tf_model', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_multiple_models_dlsdk_tf_mxnet_caffe_onnx(self): - config = { - 'framework': 'dlsdk', - 'model': 'custom_model', - 'weights': 'custom_weights', - 'caffe_model': 'caffe_model', - 'caffe_weights': 'caffe_weights', - 'mxnet_weights': 'mxnet_weights', - 'tf_model': 'tf_model', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - def test_raises_with_tf_model_and_tf_meta_both_provided(self): - config = { - 'framework': 'dlsdk', - 'model': 'custom_model', - 'weights': 'custom_weights', - 'caffe_model': 'caffe_model', - 'caffe_weights': 'caffe_weights', - 'mxnet_weights': 'mxnet_weights', - 'tf_model': 'tf_model', - 'tf_meta': 'tf_meta', - 'device': 'cpu', - '_models_prefix': 'prefix' - } - - with pytest.raises(ConfigError): - DLSDKLauncher(config) - - -@pytest.mark.usefixtures('mock_path_exists', 'mock_inputs', 'mock_inference_engine') -class TestDLSDKLauncherConfig: - def setup(self): - self.launcher = { - 'model': 'foo.xml', - 'weights': 'foo.bin', - 'device': 'CPU', - 'framework': 'dlsdk', - 'adapter': 'classification', - '_models_prefix': 'prefix' - } - self.config = DLSDKLauncherConfig('dlsdk_launcher') - - def test_hetero_correct(self): - self.config.validate(update_dict(self.launcher, device='HETERO:CPU')) - self.config.validate(update_dict(self.launcher, device='HETERO:CPU,FPGA')) - - def test_hetero_endswith_comma(self): - with pytest.raises(ConfigError): - self.config.validate(update_dict(self.launcher, device='HETERO:CPU,FPGA,')) - - def test_normal_multiple_devices(self): - with pytest.raises(ConfigError): - self.config.validate(update_dict(self.launcher, device='CPU,FPGA')) - - def test_hetero_empty(self): - with pytest.raises(ConfigError): - self.config.validate(update_dict(self.launcher, device='HETERO:')) - - def test_normal(self): - self.config.validate(update_dict(self.launcher, device='CPU')) - - def test_missed_model_in_create_dlsdk_launcher_raises_config_error_exception(self): - config = {'framework': 'dlsdk', 'weights': 'custom', 'adapter': 'classification', 'device': 'cpu'} - - with pytest.raises(ConfigError): - create_launcher(config) - - def test_missed_weights_in_create_dlsdk_launcher_raises_config_error_exception(self): - launcher = {'framework': 'dlsdk', 'model': 'custom', 'adapter': 'ssd', 'device': 'cpu'} - - with pytest.raises(ConfigError): - create_launcher(launcher) - - def test_missed_adapter_in_create_dlsdk_launcher_raises_config_error_exception(self): - launcher_config = {'framework': 'dlsdk', 'model': 'custom', 'weights': 'custom'} - - with pytest.raises(ConfigError): - create_launcher(launcher_config) - - def test_undefined_str_adapter_in_create_dlsdk_launcher_raises_config_error_exception(self): - launcher_config = {'framework': 'dlsdk', 'model': 'custom', 'weights': 'custom', 'adapter': 'undefined_str'} - - with pytest.raises(ConfigError): - create_launcher(launcher_config) - - def test_empty_dir_adapter_in_create_dlsdk_launcher_raises_config_error_exception(self): - launcher_config = {'framework': 'dlsdk', 'model': 'custom', 'weights': 'custom', 'adapter': {}} - - with pytest.raises(ConfigError): - create_launcher(launcher_config) - - def test_missed_type_in_dir_adapter_in_create_dlsdk_launcher_raises_config_error_exception(self): - launcher_config = {'framework': 'dlsdk', 'model': 'custom', 'weights': 'custom', 'adapter': {'key': 'val'}} - - with pytest.raises(ConfigError): - create_launcher(launcher_config) - - def test_undefined_type_in_dir_adapter_in_create_dlsdk_launcher_raises_config_error_exception(self): - launcher_config = { - 'framework': 'dlsdk', - 'model': 'custom', - 'weights': 'custom', - 'adapter': {'type': 'undefined'} - } - - with pytest.raises(ConfigError): - create_launcher(launcher_config) - - def test_dlsdk_launcher(self): - launcher = { - 'framework': 'dlsdk', 'model': 'custom', 'weights': 'custom', 'adapter': 'ssd', 'device': 'cpu', - '_models_prefix': 'models' - } - create_launcher(launcher) - - def test_dlsdk_launcher_model_with_several_image_inputs_raise_value_error(self, mocker): - launcher_config = {'framework': 'dlsdk', 'model': 'custom', 'weights': 'custom', 'adapter': {'key': 'val'}} - - with pytest.raises(ValueError): - mocker.patch( - 'accuracy_checker.launcher.dlsdk_launcher.DLSDKLauncher.inputs', - new_callable=PropertyMock(return_value={'data1': [3, 227, 227], 'data2': [3, 227, 227]}) - ) - create_launcher(launcher_config) - - def test_dlsdk_launcher_model_no_image_inputs_raise_value_error(self): - launcher_config = {'framework': 'dlsdk', 'model': 'custom', 'weights': 'custom', 'adapter': {'key': 'val'}} - - with pytest.raises(ValueError): - create_launcher(launcher_config) - - -def dummy_adapter(): - pass diff --git a/tools/accuracy_checker/tests/test_input_feeder.py b/tools/accuracy_checker/tests/test_input_feeder.py deleted file mode 100644 index 6a6f88290b52b5..00000000000000 --- a/tools/accuracy_checker/tests/test_input_feeder.py +++ /dev/null @@ -1,255 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import pytest -import re -import numpy as np -from accuracy_checker.config import ConfigError -from accuracy_checker.launcher.input_feeder import InputFeeder -from accuracy_checker.data_readers import DataRepresentation - -# InputInfo from openvino is needed here, but there is no appropriate API -# to create InputInfo with specific shape, therefore lets use analog -class InputInfo_test: - layout = '' - precision = '' - shape = [] - def __init__(self, layout = '', precision = '', shape = []): - self.layout = layout - self.precision = precision - self.shape = shape - -class TestInputFeeder: - def test_create_input_feeder_without_inputs_raise_config_error(self): - with pytest.raises(ConfigError): - InputFeeder([], {}) - - def test_create_input_feeder_with_config_inputs_and_empty_network_inputs_raise_config_error(self): - with pytest.raises(ConfigError): - InputFeeder([{'name': 'const_data', 'type': 'CONST_INPUT', 'value': '[1, 1, 1, 1]'}], {}) - - def test_create_input_feeder_with_config_const_inputs_not_in_network_inputs_raise_config_error(self): - with pytest.raises(ConfigError): - InputFeeder([{'name': 'const_data', 'type': 'CONST_INPUT', 'value': '[1, 1, 1, 1]'}], {'data': (1, 3, 10, 10)}) - - def test_create_input_feeder_with_config_inputs_not_in_network_inputs_raise_config_error(self): - with pytest.raises(ConfigError): - InputFeeder([{'name': 'data2', 'type': 'INPUT', 'value': '.'}], {'data': (1, 3, 10, 10)}) - - def test_create_input_feeder_without_config_inputs(self): - input_feeder = InputFeeder([], {'data': (1, 3, 10, 10)}) - assert not input_feeder.const_inputs - assert not input_feeder.inputs_mapping - assert input_feeder.non_constant_inputs == ['data'] - - def test_create_input_feeder_config_inputs_fully_match_to_network_inputs(self): - input_feeder = InputFeeder([{'name': 'data', 'type': 'INPUT', 'value': '.'}], {'data': (1, 3, 10, 10)}) - assert not input_feeder.const_inputs - assert input_feeder.inputs_mapping == {'data': re.compile('.')} - assert input_feeder.non_constant_inputs == ['data'] - - def test_create_input_feeder_config_inputs_contain_only_const_inputs_with_list_value(self): - input_feeder = InputFeeder([{'name': 'const_data', 'type': 'CONST_INPUT', 'value': [1, 1, 1, 1]}], {'data': (1, 3, 10, 10), 'const_data': (1, 4)}) - assert np.array_equal(input_feeder.const_inputs['const_data'], np.ones(4)) - assert not input_feeder.inputs_mapping - assert input_feeder.non_constant_inputs == ['data'] - - def test_create_input_feeder_config_inputs_contain_only_const_inputs_with_not_list_value(self): - input_feeder = InputFeeder( - [{'name': 'const_data', 'type': 'CONST_INPUT', 'value': 'value'}], - {'data': (1, 3, 10, 10), 'const_data': (1, 4)} - ) - assert input_feeder.const_inputs['const_data'] == 'value' - assert not input_feeder.inputs_mapping - assert input_feeder.non_constant_inputs == ['data'] - - def test_create_input_feeder_not_all_non_constant_inputs_in_config_raise_config_error(self): - with pytest.raises(ConfigError): - InputFeeder( - [{'name': '0', 'type': 'INPUT', 'value': '.'}], - {'0': (1, 3, 10, 10), '1': (1, 3, 10, 10)} - ) - - def test_fill_non_constant_input_with_one_input_without_specific_mapping_batch_1(self): - input_feeder = InputFeeder([], { 'input': InputInfo_test(shape=(1, 3, 10, 10)) }) - result = input_feeder.fill_non_constant_inputs([DataRepresentation(np.zeros((10, 10, 3)), identifier='0')])[0] - expected_data = np.zeros((1, 3, 10, 10)) - assert 'input' in result - assert np.array_equal(result['input'], expected_data) - - def test_fill_non_constant_input_without_specific_mapping_batch_2(self): - input_feeder = InputFeeder([], { 'input': InputInfo_test(shape=(1, 3, 10, 10))}) - result = input_feeder.fill_non_constant_inputs([ - DataRepresentation(np.zeros((10, 10, 3)), identifier='0'), - DataRepresentation(np.zeros((10, 10, 3)), identifier='1') - ])[0] - expected_data = np.zeros((2, 3, 10, 10)) - assert 'input' in result - assert np.array_equal(result['input'], expected_data) - - def test_fill_non_constant_input_with_specific_mapping_batch_1(self): - input_feeder = InputFeeder([{'name': 'input', 'type': 'INPUT', 'value': '.'}], {'input': InputInfo_test(shape=(1, 3, 10, 10))}) - result = input_feeder.fill_non_constant_inputs([DataRepresentation(np.zeros((10, 10, 3)), identifier='0')])[0] - expected_data = np.zeros((1, 3, 10, 10)) - assert 'input' in result - assert np.array_equal(result['input'], expected_data) - - def test_fill_non_constant_input_with_specific_mapping_sevaral_image_matched(self): - input_feeder = InputFeeder([{'name': 'input', 'type': 'INPUT', 'value': '.'}], {'input': InputInfo_test(shape=(1, 3, 10, 10))}) - result = input_feeder.fill_non_constant_inputs([DataRepresentation([np.zeros((10, 10, 3)), np.ones((10, 10, 3))], identifier=['0', '1'])])[0] - expected_data = np.zeros((1, 3, 10, 10)) - assert 'input' in result - assert np.array_equal(result['input'], expected_data) - - def test_fill_non_constant_input_with_specific_mapping_not_match_raise_config_error(self): - input_feeder = InputFeeder([{'name': 'input', 'type': 'INPUT', 'value': '1.'}], {'input': InputInfo_test(shape=(1, 3, 10, 10))}) - with pytest.raises(ConfigError): - input_feeder.fill_non_constant_inputs([DataRepresentation(np.zeros((10, 10, 3)), identifier='0')]) - - def test_fill_non_constant_input_with_specific_mapping_batch_2(self): - input_feeder = InputFeeder([{'name': 'input', 'type': 'INPUT', 'value': '.'}], {'input': InputInfo_test(shape=(1, 3, 10, 10))}) - result = input_feeder.fill_non_constant_inputs([ - DataRepresentation(np.zeros((10, 10, 3)), identifier='0'), - DataRepresentation(np.zeros((10, 10, 3)), identifier='1') - ])[0] - expected_data = np.zeros((2, 3, 10, 10)) - assert 'input' in result - assert np.array_equal(result['input'], expected_data) - - def test_fill_non_constant_input_with_specific_mapping_not_all_image_in_batch_matched_raise_config_error(self): - input_feeder = InputFeeder([{'name': 'input', 'type': 'INPUT', 'value': '0+'}], {'input': InputInfo_test(shape=(1, 3, 10, 10))}) - with pytest.raises(ConfigError): - input_feeder.fill_non_constant_inputs([ - DataRepresentation(np.zeros((10, 10, 3)), identifier='0'), - DataRepresentation(np.zeros((10, 10, 3)), identifier='1') - ]) - - def test_fill_non_constant_inputs_without_specific_mapping_batch_1(self): - input_feeder = InputFeeder([], { 'input1': InputInfo_test(shape=(1, 3, 10, 10)), 'input2': InputInfo_test(shape=(1, 3, 10, 10))}) - result = input_feeder.fill_non_constant_inputs([DataRepresentation(np.zeros((10, 10, 3)), identifier='0')])[0] - expected_data = np.zeros((1, 3, 10, 10)) - assert 'input1' in result - assert np.array_equal(result['input1'], expected_data) - assert 'input2' in result - assert np.array_equal(result['input2'], expected_data) - - def test_fill_non_constant_inputs_without_specific_mapping_batch_2(self): - input_feeder = InputFeeder([], {'input1': InputInfo_test(shape=(1, 3, 10, 10)), 'input2': InputInfo_test(shape = (1, 3, 10, 10))}) - result = input_feeder.fill_non_constant_inputs([ - DataRepresentation(np.zeros((10, 10, 3)), identifier='0'), - DataRepresentation(np.zeros((10, 10, 3)), identifier='1') - ])[0] - expected_data = np.zeros((2, 3, 10, 10)) - assert 'input1' in result - assert np.array_equal(result['input1'], expected_data) - assert 'input2' in result - assert np.array_equal(result['input2'], expected_data) - - def test_fill_non_constant_inputs_with_specific_mapping_batch_1(self): - input_feeder = InputFeeder( - [{'name': 'input1', 'type': 'INPUT', 'value': '0'}, {'name': 'input2', 'type': 'INPUT', 'value': '1'}], - {'input1': InputInfo_test(shape=(1, 3, 10, 10)), 'input2': InputInfo_test(shape=(1, 3, 10, 10))} - ) - result = input_feeder.fill_non_constant_inputs( - [DataRepresentation([np.zeros((10, 10, 3)), np.ones((10, 10, 3))],identifier=['0', '1'])] - )[0] - expected_data = [np.zeros((1, 3, 10, 10)), np.ones((1, 3, 10, 10))] - assert 'input1' in result - assert np.array_equal(result['input1'], expected_data[0]) - assert 'input2' in result - assert np.array_equal(result['input2'], expected_data[1]) - - def test_fill_non_constant_inputs_with_specific_mapping_not_match_raise_config_error(self): - input_feeder = InputFeeder( - [{'name': 'input1', 'type': 'INPUT', 'value': '0'}, {'name': 'input2', 'type': 'INPUT', 'value': '1'}], - {'input1': InputInfo_test(shape=(1, 3, 10, 10)), 'input2': InputInfo_test(shape=(1, 3, 10, 10))} - ) - with pytest.raises(ConfigError): - input_feeder.fill_non_constant_inputs([DataRepresentation([np.zeros((10, 10, 3)), np.ones((10, 10, 3))], identifier=['0', '2'])]) - - def test_fill_non_constant_inputs_with_specific_mapping_batch_2(self): - input_feeder = InputFeeder( - [{'name': 'input1', 'type': 'INPUT', 'value': '0'}, {'name': 'input2', 'type': 'INPUT', 'value': '1'}], - { 'input1': InputInfo_test(shape = (1, 3, 10, 10)), 'input2': InputInfo_test(shape=(1, 3, 10, 10))} - ) - result = input_feeder.fill_non_constant_inputs([ - DataRepresentation([np.zeros((10, 10, 3)), np.ones((10, 10, 3))], identifier=['0', '1']), - DataRepresentation([np.zeros((10, 10, 3)), np.ones((10, 10, 3))], identifier=['0', '1']) - ])[0] - expected_data = [np.zeros((2, 3, 10, 10)), np.ones((2, 3, 10, 10))] - assert 'input1' in result - assert np.array_equal(result['input1'], expected_data[0]) - assert 'input2' in result - assert np.array_equal(result['input2'], expected_data[1]) - - def test_fill_non_constant_inputs_with_specific_mapping_not_all_image_in_batch_matched_raise_config_error(self): - input_feeder = InputFeeder( - [{'name': 'input1', 'type': 'INPUT', 'value': '0'}, {'name': 'input2', 'type': 'INPUT', 'value': '1'}], - {'input1': (1, 3, 10, 10), 'input2': (1, 3, 10, 10)} - ) - with pytest.raises(ConfigError): - input_feeder.fill_non_constant_inputs([ - DataRepresentation([np.zeros((10, 10, 3)), np.ones((10, 10, 3))], identifier=['0', '1']), - DataRepresentation([np.zeros((10, 10, 3)), np.ones((10, 10, 3))], identifier=['0', '2']) - ]) - - def test_fill_non_const_input_with_multi_infer_data_batch_1(self): - input_feeder = InputFeeder({}, {'input': (1, 3, 10, 10)}) - result = input_feeder.fill_non_constant_inputs([ - DataRepresentation([np.zeros((10, 10, 3)), np.ones((10, 10, 3))], {'multi_infer': True}, identifier='0') - ]) - expected = [{'input': np.zeros((1, 3, 10, 10))}, {'input': np.ones((1, 3, 10, 10))}] - assert len(result) == len(expected) - assert np.array_equal(result[0]['input'], expected[0]['input']) - assert np.array_equal(result[1]['input'], expected[1]['input']) - - def test_fill_non_const_input_with_multi_infer_data_batch_2(self): - input_feeder = InputFeeder({}, {'input': (2, 3, 10, 10)}) - result = input_feeder.fill_non_constant_inputs([ - DataRepresentation( - [np.zeros((10, 10, 3)), np.ones((10, 10, 3))], - {'multi_infer': True}, - identifier='0' - ), - DataRepresentation( - [np.zeros((10, 10, 3)), np.ones((10, 10, 3))], - {'multi_infer': True}, - identifier='1' - ), - ]) - expected = [{'input': np.zeros((2, 3, 10, 10))}, {'input': np.ones((2, 3, 10, 10))}] - assert len(result) == len(expected) - assert np.array_equal(result[0]['input'], expected[0]['input']) - assert np.array_equal(result[1]['input'], expected[1]['input']) - - def test_fill_non_const_input_with_multi_infer_not_consistent_data_batch_2(self): - input_feeder = InputFeeder({}, {'input': (2, 3, 10, 10)}) - result = input_feeder.fill_non_constant_inputs([ - DataRepresentation( - [np.zeros((10, 10, 3))], - {'multi_infer': True}, - identifier='0' - ), - DataRepresentation( - [np.zeros((10, 10, 3)), np.ones((10, 10, 3))], - {'multi_infer': True}, - identifier='1' - ), - ]) - expected = [{'input': np.zeros((2, 3, 10, 10))}, {'input': np.ones((1, 3, 10, 10))}] - assert len(result) == len(expected) - assert np.array_equal(result[0]['input'], expected[0]['input']) - assert np.array_equal(result[1]['input'], expected[1]['input']) diff --git a/tools/accuracy_checker/tests/test_metric_evaluator.py b/tools/accuracy_checker/tests/test_metric_evaluator.py deleted file mode 100644 index fc0c4d28f857bc..00000000000000 --- a/tools/accuracy_checker/tests/test_metric_evaluator.py +++ /dev/null @@ -1,482 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import pytest -from accuracy_checker.config import ConfigError -from accuracy_checker.metrics import ClassificationAccuracy, MetricsExecutor -from accuracy_checker.metrics.metric import Metric -from accuracy_checker.representation import ( - ClassificationAnnotation, - ClassificationPrediction, - ContainerAnnotation, - ContainerPrediction, - DetectionAnnotation, - DetectionPrediction -) -from .common import DummyDataset - - -class TestMetric: - def setup_method(self): - self.module = 'accuracy_checker.metrics.metric_evaluator' - - def test_missed_metrics_raises_config_error_exception(self): - with pytest.raises(ConfigError): - MetricsExecutor([], None) - - def test_metrics_with_empty_entry_raises_config_error_exception(self): - with pytest.raises(ConfigError): - MetricsExecutor([{}], None) - - def test_missed_metric_type_raises_config_error_exception(self): - with pytest.raises(ConfigError): - MetricsExecutor([{'undefined': ''}], None) - - def test_undefined_metric_type_raises_config_error_exception(self): - with pytest.raises(ConfigError): - MetricsExecutor([{'type': ''}], None) - - def test_accuracy_arguments(self): - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 1}], None) - assert len(dispatcher.metrics) == 1 - _, _, accuracy_metric, _, _, _ = dispatcher.metrics[0] - assert isinstance(accuracy_metric, ClassificationAccuracy) - assert accuracy_metric.top_k == 1 - - def test_accuracy_with_several_annotation_source_raises_config_error_exception(self): - with pytest.raises(ConfigError): - MetricsExecutor([{'type': 'accuracy', 'top_k': 1, 'annotation_source': 'annotation1, annotation2'}], None) - - def test_accuracy_with_several_prediction_source_raises_value_error_exception(self): - with pytest.raises(ConfigError): - MetricsExecutor([{'type': 'accuracy', 'top_k': 1, 'prediction_source': 'prediction1, prediction2'}], None) - - def test_accuracy_on_container_with_wrong_annotation_source_name_raise_config_error_exception(self): - annotations = [ContainerAnnotation({'annotation': ClassificationAnnotation('identifier', 3)})] - predictions = [ClassificationPrediction('identifier', [1.0, 1.0, 1.0, 4.0])] - - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 1, 'annotation_source': 'a'}], None) - with pytest.raises(ConfigError): - dispatcher.update_metrics_on_batch(annotations, predictions) - - def test_accuracy_with_wrong_annotation_type_raise_config_error_exception(self): - annotations = [DetectionAnnotation('identifier', 3)] - predictions = [ClassificationPrediction('identifier', [1.0, 1.0, 1.0, 4.0])] - - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 1}], None) - with pytest.raises(ConfigError): - dispatcher.update_metrics_on_batch(annotations, predictions) - - def test_accuracy_with_unsupported_annotations_in_container_raise_config_error_exception(self): - annotations = [ContainerAnnotation({'annotation': DetectionAnnotation('identifier', 3)})] - predictions = [ClassificationPrediction('identifier', [1.0, 1.0, 1.0, 4.0])] - - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 1}], None) - with pytest.raises(ConfigError): - dispatcher.update_metrics_on_batch(annotations, predictions) - - def test_accuracy_with_unsupported_annotation_type_as_annotation_source_for_container_raises_config_error(self): - annotations = [ContainerAnnotation({'annotation': DetectionAnnotation('identifier', 3)})] - predictions = [ClassificationPrediction('identifier', [1.0, 1.0, 1.0, 4.0])] - - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 1, 'annotation_source': 'annotation'}], None) - with pytest.raises(ConfigError): - dispatcher.update_metrics_on_batch(annotations, predictions) - - def test_accuracy_on_annotation_container_with_several_suitable_representations_config_value_error_exception(self): - annotations = [ContainerAnnotation({ - 'annotation1': ClassificationAnnotation('identifier', 3), - 'annotation2': ClassificationAnnotation('identifier', 3) - })] - predictions = [ClassificationPrediction('identifier', [1.0, 1.0, 1.0, 4.0])] - - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 1}], None) - with pytest.raises(ConfigError): - dispatcher.update_metrics_on_batch(annotations, predictions) - - def test_accuracy_with_wrong_prediction_type_raise_config_error_exception(self): - annotations = [ClassificationAnnotation('identifier', 3)] - predictions = [DetectionPrediction('identifier', [1.0, 1.0, 1.0, 4.0])] - - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 1}], None) - with pytest.raises(ConfigError): - dispatcher.update_metrics_on_batch(annotations, predictions) - - def test_accuracy_with_unsupported_prediction_in_container_raise_config_error_exception(self): - annotations = [ClassificationAnnotation('identifier', 3)] - predictions = [ContainerPrediction({'prediction': DetectionPrediction('identifier', [1.0, 1.0, 1.0, 4.0])})] - - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 1}], None) - with pytest.raises(ConfigError): - dispatcher.update_metrics_on_batch(annotations, predictions) - - def test_accuracy_with_unsupported_prediction_type_as_prediction_source_for_container_raises_config_error(self): - annotations = [ClassificationAnnotation('identifier', 3)] - predictions = [ContainerPrediction({'prediction': DetectionPrediction('identifier', [1.0, 1.0, 1.0, 4.0])})] - - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 1, 'prediction_source': 'prediction'}], None) - with pytest.raises(ConfigError): - dispatcher.update_metrics_on_batch(annotations, predictions) - - def test_accuracy_on_prediction_container_with_several_suitable_representations_raise_config_error_exception(self): - annotations = [ClassificationAnnotation('identifier', 3)] - predictions = [ContainerPrediction({ - 'prediction1': ClassificationPrediction('identifier', [1.0, 1.0, 1.0, 4.0]), - 'prediction2': ClassificationPrediction('identifier', [1.0, 1.0, 1.0, 4.0]) - })] - - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 1}], None) - with pytest.raises(ConfigError): - dispatcher.update_metrics_on_batch(annotations, predictions) - - def test_complete_accuracy(self): - annotations = [ClassificationAnnotation('identifier', 3)] - predictions = [ClassificationPrediction('identifier', [1.0, 1.0, 1.0, 4.0])] - - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 1}], None) - dispatcher.update_metrics_on_batch(annotations, predictions) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result.name == 'accuracy' - assert evaluation_result.evaluated_value == pytest.approx(1.0) - assert evaluation_result.reference_value is None - assert evaluation_result.threshold is None - - def test_complete_accuracy_with_container_default_sources(self): - annotations = [ContainerAnnotation({'a': ClassificationAnnotation('identifier', 3)})] - predictions = [ContainerPrediction({'p': ClassificationPrediction('identifier', [1.0, 1.0, 1.0, 4.0])})] - - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 1}], None) - dispatcher.update_metrics_on_batch(annotations, predictions) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result.name == 'accuracy' - assert evaluation_result.evaluated_value == pytest.approx(1.0) - assert evaluation_result.reference_value is None - assert evaluation_result.threshold is None - - def test_complete_accuracy_with_container_sources(self): - annotations = [ContainerAnnotation({'a': ClassificationAnnotation('identifier', 3)})] - predictions = [ContainerPrediction({'p': ClassificationPrediction('identifier', [1.0, 1.0, 1.0, 4.0])})] - config = [{'type': 'accuracy', 'top_k': 1, 'annotation_source': 'a', 'prediction_source': 'p'}] - - dispatcher = MetricsExecutor(config, None) - dispatcher.update_metrics_on_batch(annotations, predictions) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result.name == 'accuracy' - assert evaluation_result.evaluated_value == pytest.approx(1.0) - assert evaluation_result.reference_value is None - assert evaluation_result.threshold is None - - def test_zero_accuracy(self): - annotation = [ClassificationAnnotation('identifier', 2)] - prediction = [ClassificationPrediction('identifier', [1.0, 1.0, 1.0, 4.0])] - - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 1}], None) - - for _, evaluation_result in dispatcher.iterate_metrics([annotation], [prediction]): - assert evaluation_result.name == 'accuracy' - assert evaluation_result.evaluated_value == 0.0 - assert evaluation_result.reference_value is None - assert evaluation_result.threshold is None - - def test_complete_accuracy_top_3(self): - annotations = [ClassificationAnnotation('identifier', 3)] - predictions = [ClassificationPrediction('identifier', [1.0, 3.0, 4.0, 2.0])] - - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 3}], None) - dispatcher.update_metrics_on_batch(annotations, predictions) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result.name == 'accuracy' - assert evaluation_result.evaluated_value == pytest.approx(1.0) - assert evaluation_result.reference_value is None - assert evaluation_result.threshold is None - - def test_zero_accuracy_top_3(self): - annotations = [ClassificationAnnotation('identifier', 3)] - predictions = [ClassificationPrediction('identifier', [5.0, 3.0, 4.0, 1.0])] - - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 3}], None) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result.name == 'accuracy' - assert evaluation_result.evaluated_value == 0.0 - assert evaluation_result.reference_value is None - assert evaluation_result.threshold is None - - def test_reference_is_10_by_config(self): - annotations = [ClassificationAnnotation('identifier', 3)] - predictions = [ClassificationPrediction('identifier', [5.0, 3.0, 4.0, 1.0])] - - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 3, 'reference': 10}], None) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result.name == 'accuracy' - assert evaluation_result.evaluated_value == 0.0 - assert evaluation_result.reference_value == 10 - assert evaluation_result.threshold is None - - def test_threshold_is_10_by_config(self): - annotations = [ClassificationAnnotation('identifier', 3)] - predictions = [ClassificationPrediction('identifier', [5.0, 3.0, 4.0, 1.0])] - - dispatcher = MetricsExecutor([{'type': 'accuracy', 'top_k': 3, 'threshold': 10}], None) - - for _, evaluation_result in dispatcher.iterate_metrics([annotations], [predictions]): - assert evaluation_result.name == 'accuracy' - assert evaluation_result.evaluated_value == 0.0 - assert evaluation_result.reference_value is None - assert evaluation_result.threshold == 10 - - def test_classification_per_class_accuracy_fully_zero_prediction(self): - annotation = ClassificationAnnotation('identifier', 0) - prediction = ClassificationPrediction('identifier', [1.0, 2.0]) - dataset = DummyDataset(label_map={0: '0', 1: '1'}) - dispatcher = MetricsExecutor([{'type': 'accuracy_per_class', 'top_k': 1}], dataset) - dispatcher.update_metrics_on_batch([annotation], [prediction]) - for _, evaluation_result in dispatcher.iterate_metrics([annotation], [prediction]): - assert evaluation_result.name == 'accuracy_per_class' - assert len(evaluation_result.evaluated_value) == 2 - assert evaluation_result.evaluated_value[0] == pytest.approx(0.0) - assert evaluation_result.evaluated_value[1] == pytest.approx(0.0) - assert evaluation_result.reference_value is None - assert evaluation_result.threshold is None - - def test_classification_per_class_accuracy_partially_zero_prediction(self): - annotation = [ClassificationAnnotation('identifier', 1)] - prediction = [ClassificationPrediction('identifier', [1.0, 2.0])] - dataset = DummyDataset(label_map={0: '0', 1: '1'}) - dispatcher = MetricsExecutor([{'type': 'accuracy_per_class', 'top_k': 1}], dataset) - - dispatcher.update_metrics_on_batch(annotation, prediction) - - for _, evaluation_result in dispatcher.iterate_metrics(annotation, prediction): - assert evaluation_result.name == 'accuracy_per_class' - assert len(evaluation_result.evaluated_value) == 2 - assert evaluation_result.evaluated_value[0] == pytest.approx(0.0) - assert evaluation_result.evaluated_value[1] == pytest.approx(1.0) - assert evaluation_result.reference_value is None - assert evaluation_result.threshold is None - - def test_classification_per_class_accuracy_complete_prediction(self): - annotation = [ClassificationAnnotation('identifier_1', 1), ClassificationAnnotation('identifier_2', 0)] - prediction = [ - ClassificationPrediction('identifier_1', [1.0, 2.0]), - ClassificationPrediction('identifier_2', [2.0, 1.0]) - ] - dataset = DummyDataset(label_map={0: '0', 1: '1'}) - dispatcher = MetricsExecutor([{'type': 'accuracy_per_class', 'top_k': 1}], dataset) - - dispatcher.update_metrics_on_batch(annotation, prediction) - - for _, evaluation_result in dispatcher.iterate_metrics(annotation, prediction): - assert evaluation_result.name == 'accuracy_per_class' - assert len(evaluation_result.evaluated_value) == 2 - assert evaluation_result.evaluated_value[0] == pytest.approx(1.0) - assert evaluation_result.evaluated_value[1] == pytest.approx(1.0) - assert evaluation_result.reference_value is None - assert evaluation_result.threshold is None - - def test_classification_per_class_accuracy_partially_prediction(self): - annotation = [ - ClassificationAnnotation('identifier_1', 1), - ClassificationAnnotation('identifier_2', 0), - ClassificationAnnotation('identifier_3', 0) - ] - prediction = [ - ClassificationPrediction('identifier_1', [1.0, 2.0]), - ClassificationPrediction('identifier_2', [2.0, 1.0]), - ClassificationPrediction('identifier_3', [1.0, 5.0]) - ] - dataset = DummyDataset(label_map={0: '0', 1: '1'}) - dispatcher = MetricsExecutor([{'type': 'accuracy_per_class', 'top_k': 1}], dataset) - - dispatcher.update_metrics_on_batch(annotation, prediction) - - for _, evaluation_result in dispatcher.iterate_metrics(annotation, prediction): - assert evaluation_result.name == 'accuracy_per_class' - assert len(evaluation_result.evaluated_value) == 2 - assert evaluation_result.evaluated_value[0] == pytest.approx(0.5) - assert evaluation_result.evaluated_value[1] == pytest.approx(1.0) - assert evaluation_result.reference_value is None - assert evaluation_result.threshold is None - - def test_classification_per_class_accuracy_prediction_top3_zero(self): - annotation = [ClassificationAnnotation('identifier_1', 0), ClassificationAnnotation('identifier_2', 1)] - prediction = [ - ClassificationPrediction('identifier_1', [1.0, 2.0, 3.0, 4.0]), - ClassificationPrediction('identifier_2', [2.0, 1.0, 3.0, 4.0]) - ] - dataset = DummyDataset(label_map={0: '0', 1: '1', 2: '2', 3: '3'}) - dispatcher = MetricsExecutor([{'type': 'accuracy_per_class', 'top_k': 3}], dataset) - - dispatcher.update_metrics_on_batch(annotation, prediction) - - for _, evaluation_result in dispatcher.iterate_metrics(annotation, prediction): - assert evaluation_result.name == 'accuracy_per_class' - assert len(evaluation_result.evaluated_value) == 4 - assert evaluation_result.evaluated_value[0] == pytest.approx(0.0) - assert evaluation_result.evaluated_value[1] == pytest.approx(0.0) - assert evaluation_result.evaluated_value[2] == pytest.approx(0.0) - assert evaluation_result.evaluated_value[3] == pytest.approx(0.0) - assert evaluation_result.reference_value is None - assert evaluation_result.threshold is None - - def test_classification_per_class_accuracy_prediction_top3(self): - annotation = [ClassificationAnnotation('identifier_1', 1), ClassificationAnnotation('identifier_2', 1)] - prediction = [ - ClassificationPrediction('identifier_1', [1.0, 2.0, 3.0, 4.0]), - ClassificationPrediction('identifier_2', [2.0, 1.0, 3.0, 4.0]) - ] - dataset = DummyDataset(label_map={0: '0', 1: '1', 2: '2', 3: '3'}) - dispatcher = MetricsExecutor([{'type': 'accuracy_per_class', 'top_k': 3}], dataset) - - dispatcher.update_metrics_on_batch(annotation, prediction) - - for _, evaluation_result in dispatcher.iterate_metrics(annotation, prediction): - assert evaluation_result.name == 'accuracy_per_class' - assert len(evaluation_result.evaluated_value) == 4 - assert evaluation_result.evaluated_value[0] == pytest.approx(0.0) - assert evaluation_result.evaluated_value[1] == pytest.approx(0.5) - assert evaluation_result.evaluated_value[2] == pytest.approx(0.0) - assert evaluation_result.evaluated_value[3] == pytest.approx(0.0) - assert evaluation_result.reference_value is None - assert evaluation_result.threshold is None - - -class TestMetricExtraArgs: - def test_all_metrics_raise_config_error_on_extra_args(self): - for provider in Metric.providers: - adapter_config = {'type': provider, 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide(provider, adapter_config, None) - - def test_detection_recall_raise_config_error_on_extra_args(self): - adapter_config = {'type': 'recall', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('recall', adapter_config, None) - - def test_detection_miss_rate_raise_config_error_on_extra_args(self): - adapter_config = {'type': 'miss_rate', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('miss_rate', adapter_config, None) - - def test_accuracy_raise_config_error_on_extra_args(self): - adapter_config = {'type': 'accuracy', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('accuracy', adapter_config, None) - - def test_per_class_accuracy_raise_config_error_on_extra_args(self): - adapter_config = {'type': 'accuracy_per_class', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('accuracy_per_class', adapter_config, None) - - def test_character_recognition_accuracy_raise_config_error_on_extra_args(self): - adapter_config = {'type': 'character_recognition_accuracy', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('character_recognition_accuracy', adapter_config, None) - - def test_multi_accuracy_raise_config_error_on_extra_args(self): - metric_config = {'type': 'multi_accuracy', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('multi_accuracy', metric_config, None) - - def test_multi_precision_raise_config_error_on_extra_args(self): - metric_config = {'type': 'multi_precision', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('multi_precision', metric_config, None) - - def test_f1_score_raise_config_error_on_extra_args(self): - metric_config = {'type': 'f1-score', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('f1-score', metric_config, None) - - def test_mae_raise_config_error_on_extra_args(self): - metric_config = {'type': 'mae', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('mae', metric_config, None) - - def test_mse_raise_config_error_on_extra_args(self): - metric_config = {'type': 'mse', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('mse', metric_config, None) - - def test_rmse_raise_config_error_on_extra_args(self): - metric_config = {'type': 'rmse', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('rmse', metric_config, None) - - def test_mae_on_interval_raise_config_error_on_extra_args(self): - metric_config = {'type': 'mae_on_interval', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('mae_on_interval', metric_config, None) - - def test_mse_on_interval_raise_config_error_on_extra_args(self): - metric_config = {'type': 'mse_on_interval', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('mse_on_interval', metric_config, None) - - def test_rmse_on_interval_raise_config_error_on_extra_args(self): - metric_config = {'type': 'rmse_on_interval', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('rmse_on_interval', metric_config, None) - - def test_per_point_normed_error_raise_config_error_on_extra_args(self): - metric_config = {'type': 'per_point_normed_error', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('per_point_normed_error', metric_config, None) - - def test_average_point_error_raise_config_error_on_extra_args(self): - metric_config = {'type': 'normed_error', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('normed_error', metric_config, None) - - def test_reid_cmc_raise_config_error_on_extra_args(self): - metric_config = {'type': 'cmc', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('cmc', metric_config, None) - - def test_reid_map_raise_config_error_on_extra_args(self): - adapter_config = {'type': 'reid_map', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('reid_map', adapter_config, None) - - def test_pairwise_accuracy_raise_config_error_on_extra_args(self): - metric_config = {'type': 'pairwise_accuracy', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('pairwise_accuracy', metric_config, None) - - def test_segmentation_accuracy_raise_config_error_on_extra_args(self): - metric_config = {'type': 'segmentation_accuracy', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('segmentation_accuracy', metric_config, None) - - def test_mean_iou_raise_config_error_on_extra_args(self): - metric_config = {'type': 'mean_iou', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('mean_iou', metric_config, None) - - def test_mean_accuracy_raise_config_error_on_extra_args(self): - metric_config = {'type': 'mean_accuracy', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('mean_accuracy', metric_config, None) - - def test_frequency_weighted_accuracy_raise_config_error_on_extra_args(self): - metric_config = {'type': 'frequency_weighted_accuracy', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - Metric.provide('frequency_weighted_accuracy', metric_config, None) diff --git a/tools/accuracy_checker/tests/test_model_conversion.py b/tools/accuracy_checker/tests/test_model_conversion.py deleted file mode 100644 index a5a8c7742d5b44..00000000000000 --- a/tools/accuracy_checker/tests/test_model_conversion.py +++ /dev/null @@ -1,80 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import sys -import pytest - -from accuracy_checker.launcher.model_conversion import (exec_mo_binary, find_dlsdk_ir, find_mo, prepare_args) -from tests.common import mock_filesystem - - -def test_mock_file_system(): - with mock_filesystem(['foo/bar', 'foo/baz/']) as prefix: - assert (prefix / 'foo' / 'bar').is_file() - assert (prefix / 'foo' / 'baz').is_dir() - - -def test_find_mo(): - with mock_filesystem(['deployment_tools/model_optimizer/mo.py']) as prefix: - assert find_mo([prefix / 'deployment_tools' / 'model_optimizer']) - - -def test_find_mo_is_none_when_not_exist(): - with mock_filesystem(['deployment_tools/model_optimizer/mo.py']) as prefix: - assert find_mo([prefix / 'deployment_tools']) is None - - -def test_find_mo_list_not_corrupted(): - with mock_filesystem(['deployment_tools/model_optimizer/mo.py']) as prefix: - search_paths = [prefix] - find_mo(search_paths) - assert len(search_paths) == 1 - - -def test_find_ir__in_root(): - with mock_filesystem(['model.xml', 'model.bin']) as root: - model, weights = find_dlsdk_ir(root, 'model') - assert model == root / 'model.xml' - assert weights == root / 'model.bin' - - -def test_find_ir_raises_file_not_found_error_when_ir_not_found(): - with mock_filesystem(['foo/']) as root: - with pytest.raises(FileNotFoundError): - find_dlsdk_ir(root, 'model') - - -def test_prepare_args(): - args = prepare_args('foo', ['a', 'b'], {'bar': 123, 'x': 'baz'}) - assert args[0] == sys.executable - assert args[1] == 'foo' - assert '--a' in args - assert '--b' in args - assert '--bar' in args - assert '--x' in args - - assert args[args.index('--bar') + 1] == '123' - assert args[args.index('--x') + 1] == 'baz' - - -def test_exec_mo_binary(mocker): - subprocess_run = mocker.patch('subprocess.run') - mocker.patch('os.chdir') - - args = prepare_args('ModelOptimizer', value_options={'--foo': 'bar'}) - exec_mo_binary(args) - - subprocess_run.assert_called_once_with(args, check=False, timeout=None) diff --git a/tools/accuracy_checker/tests/test_model_evaluator.py b/tools/accuracy_checker/tests/test_model_evaluator.py deleted file mode 100644 index 8540b269788419..00000000000000 --- a/tools/accuracy_checker/tests/test_model_evaluator.py +++ /dev/null @@ -1,147 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from unittest.mock import Mock, MagicMock - -from accuracy_checker.evaluators import ModelEvaluator - - -class TestModelEvaluator: - def setup_method(self): - self.launcher = Mock() - self.launcher.predict.return_value = [] - data = MagicMock(data=MagicMock(), metadata=MagicMock(), identifier=0) - self.preprocessor = Mock() - self.preprocessor.process = Mock(return_value=data) - self.postprocessor = Mock() - self.adapter = MagicMock(return_value=[]) - self.input_feeder = Mock() - self.data_reader = Mock(return_value=data) - self.data_reader.data_source = 'source' - - annotation_0 = MagicMock() - annotation_0.identifier = 0 - annotation_0.metadata = {'data_source': MagicMock()} - annotation_1 = MagicMock() - annotation_1.identifier = 1 - annotation_1.metadata = {'data_source': MagicMock()} - annotation_container_0 = MagicMock() - annotation_container_0.values = MagicMock(return_value=[annotation_0]) - annotation_container_1 = MagicMock() - annotation_container_1.values = MagicMock(return_value=([annotation_1])) - self.annotations = [[annotation_container_0], [annotation_container_1]] - - self.dataset = MagicMock() - self.dataset.__iter__.return_value = self.annotations - - self.postprocessor.process_batch = Mock(side_effect=[ - ([annotation_container_0], [annotation_container_0]), ([annotation_container_1], [annotation_container_1]) - ]) - self.postprocessor.process_dataset = Mock(return_value=( - ([annotation_container_0], [annotation_container_0]), ([annotation_container_1], [annotation_container_1]) - )) - self.postprocessor.full_process = Mock(return_value=( - ([annotation_container_0], [annotation_container_0]), ([annotation_container_1], [annotation_container_1]) - )) - - self.metric = Mock() - self.metric.update_metrics_on_batch = Mock() - - self.evaluator = ModelEvaluator(self.launcher, self.input_feeder, self.adapter, self.data_reader, self.preprocessor, self.postprocessor, self.dataset, self.metric) - self.evaluator.store_predictions = Mock() - self.evaluator.load = Mock(return_value=( - ([annotation_container_0], [annotation_container_0]), ([annotation_container_1], [annotation_container_1]) - )) - - def test_process_dataset_without_storing_predictions_and_dataset_processors(self): - self.postprocessor.has_dataset_processors = False - - self.evaluator.process_dataset(None, None) - - assert not self.evaluator.store_predictions.called - assert not self.evaluator.load.called - assert self.launcher.predict.called - assert self.postprocessor.process_batch.called - assert self.metric.update_metrics_on_batch.call_count == len(self.annotations) - assert self.postprocessor.process_dataset.called - assert not self.postprocessor.full_process.called - - def test_process_dataset_without_storing_predictions_and_with_dataset_processors(self): - self.postprocessor.has_dataset_processors = True - - self.evaluator.process_dataset(None, None) - - assert not self.evaluator.store_predictions.called - assert not self.evaluator.load.called - assert self.launcher.predict.called - assert self.postprocessor.process_batch.called - assert self.metric.update_metrics_on_batch.call_count == 1 - assert self.postprocessor.process_dataset.called - assert not self.postprocessor.full_process.called - - def test_process_dataset_with_storing_predictions_and_without_dataset_processors(self): - self.postprocessor.has_dataset_processors = False - - self.evaluator.process_dataset('path', None) - - assert self.evaluator.store_predictions.called - assert not self.evaluator.load.called - assert self.launcher.predict.called - assert self.postprocessor.process_batch.called - assert self.metric.update_metrics_on_batch.call_count == len(self.annotations) - assert self.postprocessor.process_dataset.called - assert not self.postprocessor.full_process.called - - def test_process_dataset_with_storing_predictions_and_with_dataset_processors(self): - self.postprocessor.has_dataset_processors = True - - self.evaluator.process_dataset('path', None) - - assert self.evaluator.store_predictions.called - assert not self.evaluator.load.called - assert self.launcher.predict.called - assert self.postprocessor.process_batch.called - assert self.metric.update_metrics_on_batch.call_count == 1 - assert self.postprocessor.process_dataset.called - assert not self.postprocessor.full_process.called - - def test_process_dataset_with_loading_predictions_and_without_dataset_processors(self, mocker): - mocker.patch('accuracy_checker.evaluators.model_evaluator.get_path') - self.postprocessor.has_dataset_processors = False - - self.evaluator.process_dataset('path', None) - - assert not self.evaluator.store_predictions.called - assert self.evaluator.load.called - assert not self.launcher.predict.called - assert not self.postprocessor.process_batch.called - assert self.metric.update_metrics_on_batch.call_count == 1 - assert not self.postprocessor.process_dataset.called - assert self.postprocessor.full_process.called - - def test_process_dataset_with_loading_predictions_and_with_dataset_processors(self, mocker): - mocker.patch('accuracy_checker.evaluators.model_evaluator.get_path') - self.postprocessor.has_dataset_processors = True - - self.evaluator.process_dataset('path', None) - - assert not self.evaluator.store_predictions.called - assert self.evaluator.load.called - assert not self.launcher.predict.called - assert not self.postprocessor.process_batch.called - assert self.metric.update_metrics_on_batch.call_count == 1 - assert not self.postprocessor.process_dataset.called - assert self.postprocessor.full_process.called diff --git a/tools/accuracy_checker/tests/test_postprocessor.py b/tools/accuracy_checker/tests/test_postprocessor.py deleted file mode 100644 index 81c14c3a7ab5d0..00000000000000 --- a/tools/accuracy_checker/tests/test_postprocessor.py +++ /dev/null @@ -1,1070 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np -import pytest - -from accuracy_checker.config import ConfigError -from accuracy_checker.postprocessor import PostprocessingExecutor - -from accuracy_checker.representation import ( - DetectionAnnotation, - DetectionPrediction, - ContainerAnnotation, - ContainerPrediction, - ClassificationAnnotation -) - -from .common import make_representation, make_segmentation_representation - - -def postprocess_data(executor, annotations, predictions): - return executor.full_process(annotations, predictions) - - -class TestPostprocessor: - def test_without_apply_to_and_sources_filter_raise_config_error_exception(self): - config = [{'type': 'filter', 'labels': [1]}] - - with pytest.raises(ConfigError): - PostprocessingExecutor(config) - - def test_both_provided_apply_to_and_sources_filter_raise_config_error_exception(self): - config = [{ - 'type': 'filter', - 'apply_to': 'prediction', - 'annotation_source': 'annotation', - 'labels': [1] - }] - - with pytest.raises(ConfigError): - PostprocessingExecutor(config) - - def test_filter_annotations_unsupported_source_type_in_container_raise_type_error_exception(self): - config = [{'type': 'filter', 'annotation_source': 'annotation', 'labels': [1]}] - annotation = ContainerAnnotation({'annotation': ClassificationAnnotation()}) - executor = PostprocessingExecutor(config) - - with pytest.raises(TypeError): - postprocess_data(executor, [annotation], [None]) - - def test_filter_annotations_source_not_found_raise_config_error_exception(self): - config = [{'type': 'filter', 'annotation_source': 'ann', 'labels': [1]}] - annotation = ContainerAnnotation({ - 'annotation': make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - }) - executor = PostprocessingExecutor(config) - - with pytest.raises(ConfigError): - postprocess_data(executor, [annotation], [None]) - - def test_filter_predictions_unsupported_source_type_raise_type_error_exception(self): - config = [{ - 'type': 'filter', - 'prediction_source': 'detection_out', - 'labels': [1], - 'remove_filtered': False - }] - prediction = ContainerPrediction({'detection_out': ClassificationAnnotation()}) - executor = PostprocessingExecutor(config) - - with pytest.raises(TypeError): - postprocess_data(executor, [None], [prediction]) - - def test_filter_predictions_source_not_found_raise_config_error_exception(self): - config = [{ - 'type': 'filter', 'prediction_source': 'undefined', 'labels': [1] - }] - prediction = ContainerPrediction({'detection_out': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0]}) - executor = PostprocessingExecutor(config) - - with pytest.raises(ConfigError): - postprocess_data(executor, [None], [prediction]) - - def test_filter_container_annotations_by_labels_with_ignore_using_source(self): - config = [{ - 'type': 'filter', 'annotation_source': 'annotation', 'labels': [1], 'remove_filtered': False - }] - annotation = ContainerAnnotation({ - 'annotation': make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - }) - expected = ContainerAnnotation({ - 'annotation': make_representation( - '0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True, meta=[{'difficult_boxes': [1]}] - )[0] - }) - - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_filter_container_annotations_by_labels_with_ignore_using_apply_to(self): - config = [{ - 'type': 'filter', - 'apply_to': 'annotation', - 'labels': [1], - 'remove_filtered': False - }] - annotation = ContainerAnnotation({ - 'annotation': make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - }) - expected = ContainerAnnotation({ - 'annotation': make_representation( - '0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True, meta=[{'difficult_boxes': [1]}] - )[0] - }) - - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_filter_regular_annotations_by_labels_with_ignore(self): - config = [{'type': 'filter', 'apply_to': 'annotation', 'labels': [1], 'remove_filtered': False}] - annotation = make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - expected = make_representation( - '0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True, meta=[{'difficult_boxes': [1]}] - )[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_filter_multi_source_annotations_by_labels_with_ignore(self): - config = [{ - 'type': 'filter', - 'annotation_source': ['annotation1', 'annotation2'], - 'labels': [1], - 'remove_filtered': False - }] - annotation = ContainerAnnotation({ - 'annotation1': make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0], - 'annotation2': make_representation('1 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - }) - expected = ContainerAnnotation({ - 'annotation1': make_representation( - '0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True, meta=[{'difficult_boxes': [1]}] - )[0], - 'annotation2': make_representation( - '1 0 0 10 10; 1 0 0 11 11', is_ground_truth=True, meta=[{'difficult_boxes': [0, 1]}] - )[0] - }) - - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_filter_multi_source_annotations_by_labels_with_ignore_using_apply_to(self): - config = [{ - 'type': 'filter', - 'apply_to': 'annotation', - 'labels': [1], - 'remove_filtered': False - }] - annotation = ContainerAnnotation({ - 'annotation1': make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0], - 'annotation2': make_representation('1 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - }) - expected = ContainerAnnotation({ - 'annotation1': make_representation( - '0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True, meta=[{'difficult_boxes': [1]}] - )[0], - 'annotation2': make_representation( - '1 0 0 10 10; 1 0 0 11 11', is_ground_truth=True, meta=[{'difficult_boxes': [0, 1]}] - )[0] - }) - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_filter_regular_annotations_by_labels_with_remove_using_annotation_source_warm_user_warning(self): - config = [{ - 'type': 'filter', - 'annotation_source': 'annotation', - 'labels': [1], - 'remove_filtered': True - }] - annotation = make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - expected = make_representation('0 0 0 10 10', is_ground_truth=True)[0] - - with pytest.warns(UserWarning): - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_filter_regular_annotations_by_labels_with_remove_using_apply_to(self): - config = [{'type': 'filter', 'apply_to': 'annotation', 'labels': [1], 'remove_filtered': True}] - annotation = make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - expected = make_representation('0 0 0 10 10', is_ground_truth=True)[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_filter_annotations_by_labels_with_remove_on_container(self): - config = [{ - 'type': 'filter', - 'annotation_source': 'annotation', - 'labels': [1], - 'remove_filtered': True - }] - annotation = ContainerAnnotation({ - 'annotation': make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - }) - expected = ContainerAnnotation({ - 'annotation': make_representation('0 0 0 10 10', is_ground_truth=True)[0] - }) - - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_filter_annotations_by_labels_with_remove_on_container_using_apply_to(self): - config = [{'type': 'filter', 'apply_to': 'annotation', 'labels': [1], 'remove_filtered': True}] - annotation = ContainerAnnotation({ - 'annotation': make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - }) - expected = ContainerAnnotation({ - 'annotation': make_representation('0 0 0 10 10', is_ground_truth=True)[0] - }) - - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_filter_multi_source_annotations_by_labels_with_remove(self): - config = [{ - 'type': 'filter', - 'annotation_source': ['annotation1', 'annotation2'], - 'labels': [1], 'remove_filtered': True - }] - annotation = ContainerAnnotation({ - 'annotation1': make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0], - 'annotation2': make_representation('0 0 0 10 10', is_ground_truth=True)[0] - }) - expected = ContainerAnnotation({ - 'annotation1': make_representation('0 0 0 10 10', is_ground_truth=True)[0], - 'annotation2': make_representation('0 0 0 10 10', is_ground_truth=True)[0] - }) - - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_filter_multi_source_by_labels_with_remove_on_container_using_apply_to(self): - config = [{'type': 'filter', 'apply_to': 'annotation', 'labels': [1], 'remove_filtered': True}] - annotation = ContainerAnnotation({ - 'annotation1': make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0], - 'annotation2': make_representation('0 0 0 10 10', is_ground_truth=True)[0] - }) - expected = ContainerAnnotation({ - 'annotation1': make_representation('0 0 0 10 10', is_ground_truth=True)[0], - 'annotation2': make_representation('0 0 0 10 10', is_ground_truth=True)[0] - }) - - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_filter_predictions_by_labels_with_ignore(self): - config = [{'type': 'filter', 'apply_to': 'prediction', 'labels': ['to_be_filtered'], 'remove_filtered': False}] - prediction = DetectionPrediction(labels=['some_label', 'to_be_filtered']) - expected = DetectionPrediction(labels=['some_label', 'to_be_filtered'], metadata={'difficult_boxes': [1]}) - - postprocess_data(PostprocessingExecutor(config), [None], [prediction]) - - assert prediction == expected - - def test_filter_predictions_by_labels_with_ignore_on_container(self): - config = [{ - 'type': 'filter', - 'prediction_source': 'detection_out', - 'labels': [1], - 'remove_filtered': False - }] - prediction = ContainerPrediction({ - 'detection_out': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0] - }) - expected = ContainerPrediction({'detection_out': make_representation( - '0 0 0 10 10; 1 0 0 11 11', score=1, meta=[{'difficult_boxes': [1]}] - )[0]}) - - postprocess_data(PostprocessingExecutor(config), [None], [prediction]) - - assert prediction == expected - - def test_filter_predictions_by_labels_with_ignore_on_container_using_apply_to(self): - config = [{'type': 'filter', 'apply_to': 'prediction', 'labels': [1], 'remove_filtered': False}] - prediction = ContainerPrediction({ - 'detection_out': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0] - }) - expected = ContainerPrediction({'detection_out': make_representation( - '0 0 0 10 10; 1 0 0 11 11', score=1, meta=[{'difficult_boxes': [1]}] - )[0]}) - - postprocess_data(PostprocessingExecutor(config), [None], [prediction]) - - assert prediction == expected - - def test_filter_multi_source_predictions_by_labels_with_ignore(self): - config = [{ - 'type': 'filter', 'prediction_source': ['detection_out1', 'detection_out2'], 'labels': [1], - 'remove_filtered': False - }] - prediction = ContainerPrediction({ - 'detection_out1': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0], - 'detection_out2': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0] - }) - expected = ContainerPrediction({ - 'detection_out1': make_representation( - '0 0 0 10 10; 1 0 0 11 11', score=1, meta=[{'difficult_boxes': [1]}] - )[0], - 'detection_out2': make_representation( - '0 0 0 10 10; 1 0 0 11 11', score=1, meta=[{'difficult_boxes': [1]}] - )[0] - }) - - postprocess_data(PostprocessingExecutor(config), [None], [prediction]) - - assert prediction == expected - - def test_filter_multi_source_predictions_by_labels_with_ignore_using_apply_to(self): - config = [{ - 'type': 'filter', 'apply_to': 'prediction', 'labels': [1], 'remove_filtered': False - }] - prediction = ContainerPrediction({ - 'detection_out1': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0], - 'detection_out2': make_representation('1 0 0 10 10; 1 0 0 11 11', score=1)[0] - }) - expected = ContainerPrediction({ - 'detection_out1': make_representation( - '0 0 0 10 10; 1 0 0 11 11', score=1, meta=[{'difficult_boxes': [1]}] - )[0], - 'detection_out2': make_representation( - '1 0 0 10 10; 1 0 0 11 11', score=1, meta=[{'difficult_boxes': [0, 1]}] - )[0] - }) - - postprocess_data(PostprocessingExecutor(config), [None], [prediction]) - - assert prediction == expected - - def test_filter_predictions_by_labels_with_remove(self): - config = [{'type': 'filter', 'apply_to': 'prediction', 'labels': [1], 'remove_filtered': True}] - prediction = make_representation('0 0 0 10 10; 1 0 0 11 11', score=1) - expected = make_representation('0 0 0 10 10', score=1) - - postprocess_data(PostprocessingExecutor(config), [None], prediction) - - assert prediction == expected - - def test_filter_predictions_by_labels_with_remove_on_container(self): - config = [{ - 'type': 'filter', 'prediction_source': 'detection_out', 'labels': [0], 'remove_filtered': True - }] - prediction = ContainerPrediction({ - 'detection_out': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0] - }) - expected = ContainerPrediction({'detection_out': make_representation('1 0 0 11 11', score=1)[0]}) - - postprocess_data(PostprocessingExecutor(config), [None], [prediction]) - - assert prediction == expected - - def test_filter_predictions_by_labels_with_remove_on_container_using_apply_to(self): - config = [{'type': 'filter', 'apply_to': 'prediction', 'labels': [0], 'remove_filtered': True}] - prediction = ContainerPrediction({ - 'detection_out': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0] - }) - expected = ContainerPrediction({'detection_out': make_representation('1 0 0 11 11', score=1)[0]}) - - postprocess_data(PostprocessingExecutor(config), [None], [prediction]) - - assert prediction == expected - - def test_filter_multi_source_predictions_by_labels_with_remove(self): - config = [{ - 'type': 'filter', - 'prediction_source': ['detection_out1', 'detection_out2'], - 'labels': [1], - 'remove_filtered': True - }] - prediction = ContainerPrediction({ - 'detection_out1': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0], - 'detection_out2': make_representation('0 0 0 10 10', score=1)[0] - }) - expected = ContainerPrediction({ - 'detection_out1': make_representation('0 0 0 10 10', score=1)[0], - 'detection_out2': make_representation('0 0 0 10 10', score=1)[0] - }) - - postprocess_data(PostprocessingExecutor(config), [None], [prediction]) - - assert prediction == expected - - def test_filter_multi_source_predictions_by_labels_with_remove_using_apply_to(self): - config = [{'type': 'filter', 'apply_to': 'prediction', 'labels': [1], 'remove_filtered': True}] - prediction = ContainerPrediction({ - 'detection_out1': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0], - 'detection_out2': make_representation('0 0 0 10 10', score=1)[0] - }) - expected = ContainerPrediction({ - 'detection_out1': make_representation('0 0 0 10 10', score=1)[0], - 'detection_out2': make_representation('0 0 0 10 10', score=1)[0] - }) - - postprocess_data(PostprocessingExecutor(config), [None], [prediction]) - - assert prediction == expected - - def test_filter_regular_annotations_and_regular_predictions_by_labels_with_ignore_using_apply_to(self): - config = [{'type': 'filter', 'apply_to': 'all', 'labels': [1], 'remove_filtered': False}] - prediction = make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0] - expected_prediction = make_representation( - '0 0 0 10 10; 1 0 0 11 11', score=1, meta=[{'difficult_boxes': [1]}] - )[0] - annotation = make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - expected_annotation = make_representation( - '0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True, meta=[{'difficult_boxes': [1]}] - )[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected_prediction and annotation == expected_annotation - - def test_filter_regular_annotations_and_regular_predictions_by_labels_with_remove_using_apply_to(self): - config = [{'type': 'filter', 'apply_to': 'all', 'labels': [1], 'remove_filtered': True}] - prediction = make_representation('0 0 0 10 10; 1 0 0 11 11', score=1) - expected_prediction = make_representation('0 0 0 10 10', score=1) - annotation = make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True) - expected_annotation = make_representation('0 0 0 10 10', is_ground_truth=True) - - postprocess_data(PostprocessingExecutor(config), annotation, prediction) - - assert prediction == expected_prediction and annotation == expected_annotation - - def test_filter_container_annotations_and_regular_predictions_by_labels_with_ignore_using_apply_to(self): - config = [{'type': 'filter', 'apply_to': 'all', 'labels': [1], 'remove_filtered': False}] - prediction = make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0] - expected_prediction = make_representation( - '0 0 0 10 10; 1 0 0 11 11', score=1, meta=[{'difficult_boxes': [1]}] - )[0] - annotation = make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - expected_annotation = make_representation( - '0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True, meta=[{'difficult_boxes': [1]}] - )[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected_prediction and annotation == expected_annotation - - def test_filter_container_annotations_and_regular_predictions_by_labels_with_remove_using_apply_to(self): - config = [{'type': 'filter', 'apply_to': 'all', 'labels': [1], 'remove_filtered': True}] - prediction = make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0] - expected_prediction = make_representation('0 0 0 10 10', score=1)[0] - annotation = ContainerAnnotation({ - 'annotation': make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - }) - expected_annotation = ContainerAnnotation({ - 'annotation': make_representation('0 0 0 10 10', is_ground_truth=True)[0] - }) - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected_prediction and annotation == expected_annotation - - def test_filter_regular_annotations_and_container_predictions_by_labels_with_ignore_using_apply_to(self): - config = [{'type': 'filter', 'apply_to': 'all', 'labels': [1], 'remove_filtered': False}] - prediction = ContainerPrediction({ - 'detection_out': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0] - }) - expected_prediction = ContainerPrediction({ - 'detection_out': make_representation( - '0 0 0 10 10; 1 0 0 11 11', score=1, meta=[{'difficult_boxes': [1]}] - )[0] - }) - annotation = make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - expected_annotation = make_representation( - '0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True, meta=[{'difficult_boxes': [1]}] - )[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected_prediction and annotation == expected_annotation - - def test_filter_regular_annotations_and_container_predictions_by_labels_with_remove_using_apply_to(self): - config = [{'type': 'filter', 'apply_to': 'all', 'labels': [1], 'remove_filtered': True}] - prediction = ContainerPrediction({ - 'detection_out': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0] - }) - expected_prediction = ContainerPrediction({'detection_out': make_representation('0 0 0 10 10', score=1)[0]}) - annotation = make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - expected_annotation = make_representation('0 0 0 10 10', is_ground_truth=True)[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected_prediction and annotation == expected_annotation - - def test_filter_container_annotations_and_container_predictions_by_labels_with_ignore_using_apply_to(self): - config = [{'type': 'filter', 'apply_to': 'all', 'labels': [1], 'remove_filtered': False}] - prediction = ContainerPrediction({ - 'detection_out': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0] - }) - expected_prediction = ContainerPrediction({ - 'detection_out': make_representation( - '0 0 0 10 10; 1 0 0 11 11', score=1, meta=[{'difficult_boxes': [1]}] - )[0] - }) - annotation = make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - expected_annotation = make_representation( - '0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True, meta=[{'difficult_boxes': [1]}] - )[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected_prediction and annotation == expected_annotation - - def test_filter_container_annotations_and_container_predictions_by_labels_with_remove_using_apply_to(self): - config = [{'type': 'filter', 'apply_to': 'all', 'labels': [1], 'remove_filtered': True}] - prediction = ContainerPrediction({ - 'prediction': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0] - }) - expected_prediction = ContainerPrediction({'prediction': make_representation('0 0 0 10 10', score=1)[0]}) - annotation = ContainerAnnotation({ - 'annotation': make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - }) - expected_annotation = ContainerAnnotation({ - 'annotation': make_representation('0 0 0 10 10', is_ground_truth=True)[0] - }) - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected_prediction and annotation == expected_annotation - - def test_filter_container_annotations_and_container_predictions_by_labels_with_ignore_using_sources(self): - config = [{'type': 'filter', 'apply_to': 'all', 'labels': [1], 'remove_filtered': False}] - prediction = ContainerPrediction({'prediction': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0]}) - expected_prediction = ContainerPrediction({ - 'prediction': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1, meta=[{'difficult_boxes': [1]}])[0] - }) - annotation = ContainerAnnotation({ - 'annotation': make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0] - }) - expected_annotation = ContainerAnnotation({ - 'annotation': make_representation( - '0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True, meta=[{'difficult_boxes': [1]}] - )[0] - }) - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected_prediction and annotation == expected_annotation - - def test_filter_container_annotations_and_container_predictions_by_labels_with_remove_using_sources(self): - config = [{'type': 'filter', 'annotation_source': 'annotation', 'prediction_source': 'prediction', - 'labels': [1], 'remove_filtered': True}] - prediction = ContainerPrediction({'prediction': make_representation('0 0 0 10 10; 1 0 0 11 11', score=1)[0]}) - expected_prediction = ContainerPrediction({'prediction': make_representation('0 0 0 10 10', score=1)[0]}) - annotation = ContainerAnnotation( - {'annotation': make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True)[0]}) - expected_annotation = ContainerAnnotation( - {'annotation': make_representation('0 0 0 10 10', is_ground_truth=True)[0]}) - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected_prediction and annotation == expected_annotation - - def test_filter_annotations_by_min_confidence_do_nothing(self): - config = [{'type': 'filter', 'apply_to': 'annotation', 'min_confidence': 0.5, 'remove_filtered': True}] - annotations = make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True) - expected_annotations = make_representation('0 0 0 10 10; 1 0 0 11 11', is_ground_truth=True) - - postprocess_data(PostprocessingExecutor(config), annotations, [None]) - - assert np.array_equal(annotations, expected_annotations) - - def test_filter_predictions_by_min_confidence_with_ignore(self): - config = [{'type': 'filter', 'apply_to': 'prediction', 'min_confidence': 0.5, 'remove_filtered': False}] - predictions = [ - make_representation('0 0 0 10 10; 1 0 0 11 11', score=[0.3, 0.8])[0], - make_representation('0 0 0 10 10; 1 0 0 11 11', score=[0.5, 0.4])[0] - ] - expected_predictions = [ - make_representation('0 0 0 10 10; 1 0 0 11 11', score=[0.3, 0.8], meta=[{'difficult_boxes': [0]}])[0], - make_representation('0 0 0 10 10; 1 0 0 11 11', score=[0.5, 0.4], meta=[{'difficult_boxes': [1]}])[0] - ] - - executor = PostprocessingExecutor(config) - postprocess_data(executor, [None, None], predictions) - - assert np.array_equal(predictions, expected_predictions) - - def test_filter_predictions_by_min_confidence_with_remove(self): - config = [{'type': 'filter', 'apply_to': 'prediction', 'min_confidence': 0.5, 'remove_filtered': True}] - predictions = [ - make_representation('0 0 0 10 10; 1 0 0 11 11', score=[0.3, 0.8])[0], - make_representation('0 0 0 10 10; 1 0 0 11 11', score=[0.5, 0.4])[0] - ] - expected_predictions = [ - make_representation('1 0 0 11 11', score=0.8)[0], - make_representation('0 0 0 10 10', score=0.5)[0] - ] - - postprocess_data(PostprocessingExecutor(config), [None, None], predictions) - - assert np.array_equal(predictions, expected_predictions) - - def test_filter_annotations_by_height_range_with_ignored(self): - config = [{ - 'type': 'filter', - 'apply_to': 'annotation', - 'height_range': '(10.0, 20.0)', - 'remove_filtered': False - }] - annotations = [ - make_representation('0 0 5 0 15; 1 0 10 0 15', is_ground_truth=True)[0], - make_representation('0 0 5 0 35; 1 0 10 0 40', is_ground_truth=True)[0] - ] - expected = [ - make_representation('0 0 5 0 15; 1 0 10 0 15', is_ground_truth=True, meta=[{'difficult_boxes': [1]}])[0], - make_representation('0 0 5 0 35; 1 0 10 0 40', is_ground_truth=True, meta=[{'difficult_boxes': [0, 1]}])[0] - ] - - postprocess_data(PostprocessingExecutor(config), annotations, [None, None]) - - assert np.array_equal(annotations, expected) - - def test_filter_annotations_by_height_range_with_remove(self): - config = [{'type': 'filter', 'apply_to': 'annotation', 'height_range': '(10.0, 20.0)', 'remove_filtered': True}] - annotations = [ - make_representation('0 0 5 0 15; 1 0 10 0 15', is_ground_truth=True)[0], - make_representation('0 0 5 0 35; 1 0 10 0 40', is_ground_truth=True)[0] - ] - expected = [ - make_representation('0 0 5 0 15', is_ground_truth=True)[0], - make_representation('', is_ground_truth=True)[0] - ] - - postprocess_data(PostprocessingExecutor(config), annotations, [None, None]) - - assert np.array_equal(annotations, expected) - - def test_filter_predictions_by_height_range_with_ignored(self): - config = [{ - 'type': 'filter', - 'apply_to': 'prediction', - 'height_range': '(10.0, 20.0)', - 'remove_filtered': False - }] - predictions = [ - make_representation('0 0 5 0 15; 1 0 10 0 15', score=1)[0], - make_representation('0 0 5 0 35; 1 0 10 0 40', score=1)[0] - ] - expected = [ - make_representation('0 0 5 0 15; 1 0 10 0 15', score=1, meta=[{'difficult_boxes': [1]}])[0], - make_representation('0 0 5 0 35; 1 0 10 0 40', score=1, meta=[{'difficult_boxes': [0, 1]}])[0] - ] - - postprocess_data(PostprocessingExecutor(config), [None, None], predictions) - - assert np.array_equal(predictions, expected) - - def test_filter_predictions_by_height_range_with_remove(self): - config = [{'type': 'filter', 'apply_to': 'prediction', 'height_range': '(10.0, 20.0)', 'remove_filtered': True}] - predictions = [ - make_representation('0 0 5 0 15; 1 0 10 0 15', score=1)[0], - make_representation('0 0 5 0 35; 1 0 10 0 40', score=1)[0] - ] - expected = [ - make_representation('0 0 5 0 15', score=1)[0], - make_representation('', score=1)[0] - ] - - postprocess_data(PostprocessingExecutor(config), [None, None], predictions) - - assert np.array_equal(predictions, expected) - - def test_filter_predictions_by_unknown_min_visibility_raises_value_error_exception(self): - config = [{'type': 'filter', 'apply_to': 'prediction', 'min_visibility': 'unknown'}] - predictions = [ - make_representation('0 0 5 0 15; 1 0 10 0 15', score=1)[0], - make_representation('0 0 5 0 35; 1 0 10 0 40', score=1)[0] - ] - - with pytest.raises(ValueError): - postprocess_data(PostprocessingExecutor(config), [None], predictions) - - def test_filter_annotations_by_unknown_min_visibility_raises_value_error_exception(self): - config = [{'type': 'filter', 'apply_to': 'annotation', 'min_visibility': 'unknown'}] - annotations = [DetectionPrediction(y_mins=[5.0, 10.0], y_maxs=[15.0, 40.0])] - - with pytest.raises(ValueError): - postprocess_data(PostprocessingExecutor(config), annotations, [None]) - - def test_filter_predictions_by_visibility_raises_value_error_with_unknown_visibility(self): - config = [{'type': 'filter', 'apply_to': 'prediction', 'min_visibility': 'heavy occluded'}] - predictions = [DetectionPrediction( - y_mins=[5.0, 10.0], y_maxs=[15.0, 40.0], metadata={'visibilities': ['unknown']} - )] - - with pytest.raises(ValueError): - postprocess_data(PostprocessingExecutor(config), [None], predictions) - - def test_filter_annotations_by_visibility_raises_value_error_with_unknown_visibility(self): - config = [{'type': 'filter', 'apply_to': 'annotation', 'min_visibility': 'heavy occluded'}] - annotations = [DetectionAnnotation( - y_mins=[5.0, 10.0], y_maxs=[15.0, 40.0], metadata={'visibilities': ['unknown']} - )] - - with pytest.raises(ValueError): - postprocess_data(PostprocessingExecutor(config), annotations, [None]) - - def test_filter_by_visibility_does_nothing_with_annotations_without_visibility(self): - config = [{'type': 'filter', 'apply_to': 'annotation', 'min_visibility': 'heavy occluded'}] - annotations = [ - make_representation('0 0 5 0 15; 1 0 10 0 15', is_ground_truth=True)[0], - make_representation('0 0 5 0 35; 1 0 10 0 40', is_ground_truth=True)[0] - ] - expected = [ - make_representation('0 0 5 0 15; 1 0 10 0 15', is_ground_truth=True, meta=[{'difficult_boxes': []}])[0], - make_representation('0 0 5 0 35; 1 0 10 0 40', is_ground_truth=True, meta=[{'difficult_boxes': []}])[0] - ] - - postprocess_data(PostprocessingExecutor(config), annotations, [None, None]) - - assert np.array_equal(annotations, expected) - - def test_filter_by_visibility_does_nothing_with_predictions_without_visibility(self): - config = [{'type': 'filter', 'apply_to': 'prediction', 'min_visibility': 'heavy occluded'}] - predictions = [ - DetectionPrediction(y_mins=[5.0, 10.0], y_maxs=[15.0, 40.0]), - DetectionPrediction(y_mins=[5.0, 10.0], y_maxs=[35.0, 50.0]) - ] - expected = [ - DetectionPrediction(y_mins=[5.0, 10.0], y_maxs=[15.0, 40.0], metadata={'difficult_boxes': []}), - DetectionPrediction(y_mins=[5.0, 10.0], y_maxs=[35.0, 50.0], metadata={'difficult_boxes': []}) - ] - - postprocess_data(PostprocessingExecutor(config), [None, None], predictions) - - assert np.array_equal(predictions, expected) - - def test_filter_by_visibility_does_nothing_with_default_visibility_level_and_heavy_occluded(self): - config = [{'type': 'filter', 'apply_to': 'annotation', 'min_visibility': 'heavy occluded'}] - annotation = make_representation('0 0 5 0 15; 1 0 10 0 15', is_ground_truth=True)[0] - expected = make_representation( - '0 0 5 0 15; 1 0 10 0 15', is_ground_truth=True, meta=[{'difficult_boxes': []}] - )[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_filter_by_visibility_does_nothing_with_default_visibility_level_and_partially_occluded(self): - config = [{'type': 'filter', 'apply_to': 'annotation', 'min_visibility': 'partially occluded'}] - annotation = make_representation('0 0 5 0 15; 1 0 10 0 15', is_ground_truth=True)[0] - expected = make_representation( - '0 0 5 0 15; 1 0 10 0 15', is_ground_truth=True, meta=[{'difficult_boxes': []}] - )[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_filter_by_visibility_filters_partially_occluded_remove_filtered(self): - config = [{'type': 'filter', 'apply_to': 'annotation', 'min_visibility': 'partially occluded', - 'remove_filtered': True}] - annotation = make_representation( - '0 0 5 0 15; 1 0 10 0 15', is_ground_truth=True, - meta=[{'visibilities': ['heavy occluded', 'partially occluded']}] - )[0] - expected = make_representation( - '1 0 10 0 15', is_ground_truth=True, meta=[{'visibilities': ['heavy occluded', 'partially occluded']}] - )[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_nms(self, mocker): - mock = mocker.patch('accuracy_checker.postprocessor.nms.NMS.process_all', return_value=([], [])) - config = [{'type': 'nms', 'overlap': 0.4}] - postprocess_data(PostprocessingExecutor(config), [], []) - mock.assert_called_once_with([], []) - - def test_resize_prediction_boxes(self): - config = [{'type': 'resize_prediction_boxes'}] - annotation = DetectionAnnotation(metadata={'image_size': [(100, 100, 3)]}) - prediction = make_representation('0 0 0 5 5; 1 7 7 8 8', score=1)[0] - expected = make_representation('0 0 0 500 500; 1 700 700 800 800', score=1)[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected - - def test_clip_annotation_denormalized_boxes(self): - config = [{'type': 'clip_boxes', 'apply_to': 'annotation', 'boxes_normalized': False}] - meta = {'image_size': [(10, 10, 3)]} - annotation = make_representation('0 -1 0 5 5; 1 9 11 10 10', is_ground_truth=True, meta=[meta])[0] - expected = make_representation('0 0 0 5 5; 1 9 10 10 10', is_ground_truth=True, meta=[meta])[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_clip_annotation_normalized_boxes(self): - config = [{'type': 'clip_boxes', 'apply_to': 'annotation', 'boxes_normalized': True}] - meta = {'image_size': [(10, 10, 3)]} - annotation = make_representation('0 -1 0 5 5; 1 9 11 10 10', is_ground_truth=True, meta=[meta])[0] - expected = make_representation('0 0 0 1 1; 1 1 1 1 1', is_ground_truth=True, meta=[meta])[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_clip_annotation_denormalized_boxes_with_size(self): - config = [{'type': 'clip_boxes', 'apply_to': 'annotation', 'boxes_normalized': False, 'size': 10}] - meta = {'image_size': [(10, 10, 3)]} - annotation = make_representation('0 -1 0 5 5; 1 9 11 10 10', is_ground_truth=True, meta=[meta])[0] - expected = make_representation('0 0 0 5 5; 1 9 10 10 10', is_ground_truth=True, meta=[meta])[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_clip_annotation_normalized_boxes_with_size_as_normalized(self): - config = [{'type': 'clip_boxes', 'apply_to': 'annotation', 'boxes_normalized': True, 'size': 10}] - meta = {'image_size': [(10, 10, 3)]} - annotation = make_representation('0 -1 0 5 5; 1 9 11 10 10', is_ground_truth=True, meta=[meta])[0] - expected = make_representation('0 0 0 1 1; 1 1 1 1 1', is_ground_truth=True, meta=[meta])[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [None]) - - assert annotation == expected - - def test_clip_prediction_denormalized_boxes(self): - config = [{'type': 'clip_boxes', 'apply_to': 'prediction', 'boxes_normalized': False}] - annotation = DetectionAnnotation(metadata={'image_size': [(10, 10, 3)]}) - prediction = make_representation('0 -1 0 5 5; 1 9 11 10 10', score=1)[0] - expected = make_representation('0 0 0 5 5; 1 9 10 10 10', score=1)[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected - - def test_clip_prediction_normalized_boxes(self): - config = [{'type': 'clip_boxes', 'apply_to': 'prediction', 'boxes_normalized': True}] - annotation = DetectionAnnotation(metadata={'image_size': [(10, 10, 3)]}) - prediction = make_representation('0 -1 0 5 5; 1 9 11 10 10', score=1)[0] - expected = make_representation('0 0 0 1 1; 1 1 1 1 1', score=1)[0] - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected - - def test_clip_predictions_denormalized_boxes_with_size(self): - config = [{'type': 'clip_boxes', 'apply_to': 'prediction', 'boxes_normalized': False, 'size': 10}] - annotation = DetectionAnnotation(metadata={'image_size': [(10, 10, 3)]}) - prediction = make_representation('0 -1 0 5 5; 1 9 11 10 10', score=1)[0] - expected = make_representation('0 0 0 5 5; 1 9 10 10 10', score=1)[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected - - def test_clip_predictions_normalized_boxes_with_size_as_normalized(self): - config = [{'type': 'clip_boxes', 'apply_to': 'prediction', 'boxes_normalized': True, 'size': 10}] - annotation = DetectionAnnotation(metadata={'image_size': [(10, 10, 3)]}) - prediction = make_representation('0 -1 0 5 5; 1 9 11 10 10', score=1)[0] - expected = make_representation('0 0 0 1 1; 1 1 1 1 1', score=1)[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected - - def test_cast_to_int_default(self): - config = [{'type': 'cast_to_int'}] - annotation = make_representation('0 -1 0 5 5; 1 9 11 10 10', is_ground_truth=True)[0] - prediction = make_representation('0 -1.1 0.5 5.9 5.1; 1 -9.9 11.5 10.9 10.1', score=1)[0] - expected_annotation = make_representation('0 -1 0 5 5; 1 9 11 10 10', is_ground_truth=True)[0] - expected_prediction = make_representation('0 -1 0 6 5; 1 -10 12 11 10', score=1)[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected_prediction and annotation == expected_annotation - - def test_cast_to_int_to_nearest(self): - config = [{'type': 'cast_to_int', 'round_policy': 'nearest'}] - annotation = make_representation('0 -1 0 5 5; 1 9 11 10 10', is_ground_truth=True)[0] - prediction = make_representation('0 -1.1 0.5 5.9 5.1; 1 -9.9 11.5 10.9 10.1', score=1)[0] - expected_annotation = make_representation('0 -1 0 5 5; 1 9 11 10 10', is_ground_truth=True)[0] - expected_prediction = make_representation('0 -1 0 6 5; 1 -10 12 11 10', score=1)[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected_prediction and annotation == expected_annotation - - def test_cast_to_int_to_nearest_to_zero(self): - config = [{'type': 'cast_to_int', 'round_policy': 'nearest_to_zero'}] - annotation = make_representation('0 -1 0 5 5; 1 9 11 10 10', is_ground_truth=True)[0] - prediction = make_representation('0 -1.1 0.5 5.9 5.1; 1 -9.9 11.5 10.9 10.1', score=1)[0] - expected_annotation = make_representation('0 -1 0 5 5; 1 9 11 10 10', is_ground_truth=True)[0] - expected_prediction = make_representation('0 -1 0 5 5; 1 -9 11 10 10', score=1)[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected_prediction and annotation == expected_annotation - - def test_cast_to_int_to_lower(self): - config = [{'type': 'cast_to_int', 'round_policy': 'lower'}] - annotation = make_representation('0 -1 0 5 5; 1 9 11 10 10', is_ground_truth=True)[0] - prediction = make_representation('0 -1.1 0.5 5.9 5.1; 1 -9.9 11.5 10.9 10.1', score=1)[0] - expected_annotation = make_representation('0 -1 0 5 5; 1 9 11 10 10', is_ground_truth=True)[0] - expected_prediction = make_representation('0 -2 0 5 5; 1 -10 11 10 10', score=1)[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected_prediction and annotation == expected_annotation - - def test_cast_to_int_to_greater(self): - config = [{'type': 'cast_to_int', 'round_policy': 'greater'}] - annotation = make_representation('0 -1 0 5 5; 1 9 11 10 10', is_ground_truth=True)[0] - prediction = make_representation('0 -1.1 0.5 5.9 5.1; 1 -9.9 11.5 10.9 10.1', score=1)[0] - expected_annotation = make_representation('0 -1 0 5 5; 1 9 11 10 10', is_ground_truth=True)[0] - expected_prediction = make_representation('0 -1 1 6 6; 1 -9 12 11 11', score=1)[0] - - postprocess_data(PostprocessingExecutor(config), [annotation], [prediction]) - - assert prediction == expected_prediction and annotation == expected_annotation - - def test_cast_to_int_to_unknown_raise_config_error(self): - config = [{'type': 'cast_to_int', 'round_policy': 'unknown'}] - - with pytest.raises(ConfigError): - postprocess_data(PostprocessingExecutor(config), [None], [None]) - - def test_extend_segmentation_mask_with_float_filling_raise_config_error(self): - config = [{'type': 'extend_segmentation_mask', 'filling_label': 0.5}] - - with pytest.raises(ConfigError): - postprocess_data(PostprocessingExecutor(config), [None], [None]) - - def test_extend_segmentation_mask_default(self): - config = [{'type': 'extend_segmentation_mask'}] - annotation = make_segmentation_representation(np.zeros((5, 5)), ground_truth=True) - prediction = make_segmentation_representation(np.zeros((7, 7)), ground_truth=False) - expected_annotation_mask = np.zeros((7, 7)) - expected_annotation_mask[0, :] = 255 - expected_annotation_mask[:, 0] = 255 - expected_annotation_mask[-1, :] = 255 - expected_annotation_mask[:, -1] = 255 - expected_prediction_mask = np.zeros((7, 7)) - postprocess_data(PostprocessingExecutor(config), annotation, prediction) - assert np.array_equal(prediction[0].mask, expected_prediction_mask) - assert np.array_equal(annotation[0].mask, expected_annotation_mask) - - def test_extend_segmentation_mask_do_nothing(self): - config = [{'type': 'extend_segmentation_mask'}] - annotation = make_segmentation_representation(np.zeros((5, 5)), ground_truth=True) - prediction = make_segmentation_representation(np.zeros((5, 5)), ground_truth=False) - expected_mask = np.zeros((5, 5)) - postprocess_data(PostprocessingExecutor(config), annotation, prediction) - assert np.array_equal(prediction[0].mask, expected_mask) - assert np.array_equal(annotation[0].mask, expected_mask) - - def test_extend_segmentation_mask_asymmetrical(self): - config = [{'type': 'extend_segmentation_mask'}] - annotation = make_segmentation_representation(np.zeros((5, 5)), ground_truth=True) - prediction = make_segmentation_representation(np.zeros((6, 7)), ground_truth=False) - expected_annotation_mask = np.zeros((6, 7)) - expected_annotation_mask[:, 0] = 255 - expected_annotation_mask[-1, :] = 255 - expected_annotation_mask[:, -1] = 255 - expected_prediction_mask = np.zeros((6, 7)) - postprocess_data(PostprocessingExecutor(config), annotation, prediction) - assert np.array_equal(prediction[0].mask, expected_prediction_mask) - assert np.array_equal(annotation[0].mask, expected_annotation_mask) - - def test_extend_segmentation_mask_raise_config_error_if_prediction_less_annotation(self): - config = [{'type': 'extend_segmentation_mask'}] - annotation = make_segmentation_representation(np.zeros((5, 5)), ground_truth=True) - prediction = make_segmentation_representation(np.zeros((4, 4)), ground_truth=False) - with pytest.raises(ConfigError): - postprocess_data(PostprocessingExecutor(config), annotation, prediction) - - def test_extend_segmentation_mask_with_filling_label(self): - config = [{'type': 'extend_segmentation_mask', 'filling_label': 1}] - annotation = make_segmentation_representation(np.zeros((5, 5)), ground_truth=True) - prediction = make_segmentation_representation(np.zeros((7, 7)), ground_truth=False) - expected_annotation_mask = np.zeros((7, 7)) - expected_annotation_mask[0, :] = 1 - expected_annotation_mask[:, 0] = 1 - expected_annotation_mask[-1, :] = 1 - expected_annotation_mask[:, -1] = 1 - expected_prediction_mask = np.zeros((7, 7)) - postprocess_data(PostprocessingExecutor(config), annotation, prediction) - assert np.array_equal(prediction[0].mask, expected_prediction_mask) - assert np.array_equal(annotation[0].mask, expected_annotation_mask) - - -class TestPostprocessorExtraArgs: - def test_cast_to_int_raise_config_error_on_extra_args(self): - config = {'type': 'cast_to_int', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - postprocess_data(PostprocessingExecutor(config), [None], [None]) - - def test_clip_boxes_raise_config_error_on_extra_args(self): - config = {'type': 'clip_boxes', 'size': 1, 'something_extra': 'extra'} - with pytest.raises(ConfigError): - postprocess_data(PostprocessingExecutor(config), [None], [None]) - - def test_correct_yolo_v2_boxes_raise_config_error_on_extra_args(self): - config = {'type': 'correct_yolo_v2_boxes', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - postprocess_data(PostprocessingExecutor(config), [None], [None]) - - def test_encode_segmentation_mask_raise_config_error_on_extra_args(self): - config = {'type': 'encode_segmentation_mask', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - postprocess_data(PostprocessingExecutor(config), [None], [None]) - - def test_filter_raise_config_error_on_extra_args(self): - config = {'type': 'filter', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - postprocess_data(PostprocessingExecutor(config), [None], [None]) - - def test_nms_raise_config_error_on_extra_args(self): - config = {'type': 'nms', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - postprocess_data(PostprocessingExecutor(config), [None], [None]) - - def test_normalize_landmarks_points_raise_config_error_on_extra_args(self): - config = {'type': 'normalize_landmarks_points', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - postprocess_data(PostprocessingExecutor(config), [None], [None]) - - def test_resize_prediction_boxes_raise_config_error_on_extra_args(self): - config = {'type': 'resize_prediction_boxes', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - postprocess_data(PostprocessingExecutor(config), [None], [None]) - - def test_resize_segmentation_mask_raise_config_error_on_extra_args(self): - config = {'type': 'resize_segmentation_mask', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - postprocess_data(PostprocessingExecutor(config), [None], [None]) - - def test_extend_segmentation_mask_raise_config_error_on_extra_args(self): - config = {'type': 'resize_segmentation_mask', 'something_extra': 'extra'} - with pytest.raises(ConfigError): - postprocess_data(PostprocessingExecutor(config), [None], [None]) diff --git a/tools/accuracy_checker/tests/test_preprocessor.py b/tools/accuracy_checker/tests/test_preprocessor.py deleted file mode 100644 index afc8d70f1cc396..00000000000000 --- a/tools/accuracy_checker/tests/test_preprocessor.py +++ /dev/null @@ -1,611 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import cv2 -import numpy as np -import pytest - -from accuracy_checker.config import ConfigError -from accuracy_checker.preprocessor import ( - Crop, - Normalize, - Preprocessor, - Resize, - Flip, - BgrToRgb, - CropRect, - ExtendAroundRect, - PointAligner -) -from accuracy_checker.preprocessor.preprocessing_executor import PreprocessingExecutor -from accuracy_checker.preprocessor.preprocessors import OPENCV_INTERPOLATION -from accuracy_checker.data_readers import DataRepresentation - - -class TestResize: - def test_default_resize(self, mocker): - cv2_resize_mock = mocker.patch('accuracy_checker.preprocessor.preprocessors.cv2.resize') - resize = Preprocessor.provide('resize', {'type': 'resize', 'size': 200}) - - input_mock = mocker.Mock() - resize(DataRepresentation(input_mock)) - - assert not resize.use_pil - assert resize.dst_width == 200 - assert resize.dst_height == 200 - cv2_resize_mock.assert_called_once_with( - input_mock, (200, 200), interpolation=OPENCV_INTERPOLATION['LINEAR'] - ) - - def test_custom_resize(self, mocker): - cv2_resize_mock = mocker.patch('accuracy_checker.preprocessor.preprocessors.cv2.resize') - - resize = Preprocessor.provide( - 'resize', {'type': 'resize', 'dst_width': 126, 'dst_height': 128, 'interpolation': 'CUBIC'} - ) - - input_mock = mocker.Mock() - resize(DataRepresentation(input_mock)) - - assert not resize.use_pil - assert resize.dst_width == 126 - assert resize.dst_height == 128 - cv2_resize_mock.assert_called_once_with( - input_mock, (126, 128), - interpolation=OPENCV_INTERPOLATION['CUBIC'] - ) - - def test_resize_without_save_aspect_ratio(self): - name = 'mock_preprocessor' - config = {'type': 'resize', 'dst_width': 150, 'dst_height': 150} - input_image = np.ones((100, 50, 3)) - resize = Preprocessor.provide('resize', config, name) - - result = resize(DataRepresentation(input_image)).data - - assert result.shape == (150, 150, 3) - - def test_resize_save_aspect_ratio_unknown_raise_config_error(self): - with pytest.raises(ConfigError): - Preprocessor.provide( - 'resize', {'type': 'resize', 'dst_width': 100, 'dst_height': 150, 'aspect_ratio_scale': 'unknown'} - ) - - def test_resize_save_aspect_ratio_height(self): - input_image = np.ones((100, 50, 3)) - resize = Preprocessor.provide('resize', { - 'type': 'resize', 'dst_width': 100, 'dst_height': 150, - 'interpolation': 'CUBIC', 'aspect_ratio_scale': 'height' - }) - result = resize(DataRepresentation(input_image)).data - - assert result.shape == (300, 100, 3) - - def test_resize_save_aspect_ratio_width(self): - input_image = np.ones((100, 50, 3)) - resize = Preprocessor.provide('resize', { - 'type': 'resize', 'dst_width': 150, 'dst_height': 150, 'aspect_ratio_scale': 'width' - }) - result = resize(DataRepresentation(input_image)).data - - assert result.shape == (150, 75, 3) - - def test_resize_save_aspect_ratio_for_greater_dim(self): - input_image = np.ones((100, 50, 3)) - resize = Preprocessor.provide('resize', { - 'type': 'resize', - 'dst_width': 100, - 'dst_height': 150, - 'aspect_ratio_scale': 'greater' - }) - result = resize(DataRepresentation(input_image)).data - - assert result.shape == (300, 100, 3) - - def test_resize_to_negative_size_raise_config_error(self): - with pytest.raises(ConfigError): - Preprocessor.provide('resize', {'type': 'resize', 'size': -100}) - - def test_resize_to_negative_destination_width_raise_config_error(self): - with pytest.raises(ConfigError): - Preprocessor.provide('resize', {'type': 'resize', 'dst_width': -100, 'dst_height': 100}) - - def test_resize_to_negative_destination_height_raise_config_error(self): - with pytest.raises(ConfigError): - Preprocessor.provide('resize', {'type': 'resize', 'dst_width': 100, 'dst_height': -100}) - - def test_resize_with_both_provided_size_and_dst_height_dst_width_warn(self): - input_image = np.ones((100, 50, 3)) - - with pytest.warns(None) as warnings: - resize = Preprocessor.provide( - 'resize', {'type': 'resize', 'dst_width': 100, 'dst_height': 100, 'size': 200} - ) - assert len(warnings) == 1 - result = resize(DataRepresentation(input_image)).data - assert result.shape == (200, 200, 3) - - def test_resize_provided_only_dst_height_raise_config_error(self): - with pytest.raises(ValueError): - Preprocessor.provide('resize', {'type': 'resize', 'dst_height': 100}) - - def test_resize_provided_only_dst_width_raise_config_error(self): - with pytest.raises(ValueError): - Preprocessor.provide('resize', {'type': 'resize', 'dst_width': 100}) - - -class TestNormalization: - def test_normalization_without_mean_and_std_raise_config_error(self): - with pytest.raises(ConfigError): - Preprocessor.provide('normalization', {'type': 'normalization'}) - - def test_custom_normalization_with_mean(self): - normalization = Preprocessor.provide('normalization', {'type': 'normalization', 'mean': '(1, 2, 3)'}) - source = np.full_like((3, 300, 300), 100) - input_ref = source.copy() - (1, 2, 3) - result = normalization(DataRepresentation(source)) - - assert normalization.mean == (1, 2, 3) - assert normalization.std is None - assert np.all(input_ref == result.data) - assert result.metadata == {'image_size': (3,)} - - def test_custom_normalization_with_precomputed_mean(self): - normalization = Preprocessor.provide('normalization', {'type': 'normalization', 'mean': 'cifar10'}) - - source = np.full_like((3, 300, 300), 100) - input_ref = source.copy() - normalization.PRECOMPUTED_MEANS['cifar10'] - result = normalization(DataRepresentation(source)) - - assert normalization.mean == normalization.PRECOMPUTED_MEANS['cifar10'] - assert normalization.std is None - assert np.all(input_ref == result.data) - assert result.metadata == {'image_size': (3,)} - - def test_custom_normalization_with_mean_as_scalar(self): - normalization = Preprocessor.provide('normalization', {'type': 'normalization', 'mean': '1'}) - - source = np.full_like((3, 300, 300), 100) - input_ref = source.copy() - 1 - result = normalization(DataRepresentation(source)) - - assert normalization.mean == (1.0, ) - assert normalization.std is None - assert np.all(input_ref == result.data) - assert result.metadata == {'image_size': (3,)} - - def test_custom_normalization_with_std(self): - normalization = Preprocessor.provide('normalization', {'type': 'normalization', 'std': '(1, 2, 3)'}) - - source = np.full_like((3, 300, 300), 100) - input_ref = source.copy() / (1, 2, 3) - result = normalization(DataRepresentation(source)) - - assert normalization.mean is None - assert normalization.std == (1, 2, 3) - assert np.all(input_ref == result.data) - assert result.metadata == {'image_size': (3,)} - - def test_custom_normalization_with_precomputed_std(self): - normalization = Preprocessor.provide('normalization', {'type': 'normalization', 'std': 'cifar10'}) - - source = np.full_like((3, 300, 300), 100) - input_ref = source.copy() / normalization.PRECOMPUTED_STDS['cifar10'] - result = normalization(DataRepresentation(source)) - - assert normalization.mean is None - assert normalization.std == normalization.PRECOMPUTED_STDS['cifar10'] - assert np.all(input_ref == result.data) - assert result.metadata == {'image_size': (3,)} - - def test_custom_normalization_with_std_as_scalar(self): - normalization = Preprocessor.provide('normalization', {'type': 'normalization', 'std': '2'}) - source = np.full_like((3, 300, 300), 100) - input_ref = source.copy() / 2 - result = normalization(DataRepresentation(source)) - - assert normalization.mean is None - assert normalization.std == (2.0, ) - assert np.all(input_ref == result.data) - assert result.metadata == {'image_size': (3,)} - - def test_custom_normalization_with_mean_and_std(self): - normalization = Preprocessor.provide( - 'normalization', {'type': 'normalization', 'mean': '(1, 2, 3)', 'std': '(4, 5, 6)'} - ) - - input_ = np.full_like((3, 300, 300), 100) - input_ref = (input_ - (1, 2, 3)) / (4, 5, 6) - result = normalization(DataRepresentation(input_)) - - assert normalization.mean == (1, 2, 3) - assert normalization.std == (4, 5, 6) - assert np.all(input_ref == result.data) - assert result.metadata == {'image_size': (3,)} - - def test_custom_normalization_with_mean_and_std_as_scalars(self): - normalization = Preprocessor.provide('normalization', {'type': 'normalization', 'mean': '2', 'std': '5'}) - - input_ = np.full_like((3, 300, 300), 100) - input_ref = (input_ - (2, )) / (5, ) - result = normalization(DataRepresentation(input_)) - - assert normalization.mean == (2, ) - assert normalization.std == (5, ) - assert np.all(input_ref == result.data) - assert result.metadata == {'image_size': (3,)} - - def test_normalization_with_zero_in_std_values_raise_config_error(self): - with pytest.raises(ConfigError): - Preprocessor.provide('normalization', {'type': 'normalization', 'std': '(4, 0, 6)'}) - - def test_normalization_with_zero_as_std_value_raise_config_error(self): - with pytest.raises(ConfigError): - Preprocessor.provide('normalization', {'type': 'normalization', 'std': '0'}) - - def test_normalization_with_not_channel_wise_mean_list_raise_config_error(self): - with pytest.raises(ConfigError): - Preprocessor.provide('normalization', {'type': 'normalization', 'mean': '3, 2'}) - - def test_normalization_with_not_channel_wise_std_list_raise_config_error(self): - with pytest.raises(ConfigError): - Preprocessor.provide('normalization', {'type': 'normalization', 'std': '3, 2'}) - - def test_normalization_with_unknown_precomputed_mean_raise_config_error(self): - with pytest.raises(ValueError): - Preprocessor.provide('normalization', {'type': 'normalization', 'mean': 'unknown'}) - - def test_normalization_with_unknown_precomputed_std_raise_config_error(self): - with pytest.raises(ValueError): - Preprocessor.provide('normalization', {'type': 'normalization', 'std': 'unknown'}) - - -class TestPreprocessingEvaluator: - def test_preprocessing_evaluator(self): - config = [{'type': 'normalization', 'mean': '(1, 2, 3)'}, {'type': 'resize', 'size': 200}] - preprocessor = PreprocessingExecutor(config) - - assert 2 == len(preprocessor.processors) - assert isinstance(preprocessor.processors[0], Normalize) - assert isinstance(preprocessor.processors[1], Resize) - assert preprocessor.processors[0].mean == (1, 2, 3) - assert preprocessor.processors[1].dst_width == 200 - - -class TestCrop: - def test_crop_higher(self): - crop = Crop({'dst_width': 50, 'dst_height': 33, 'type': 'crop'}) - image = np.zeros((100, 100, 3)) - image_rep = crop(DataRepresentation(image)) - - assert image_rep.data.shape == (33, 50, 3) - assert image_rep.metadata == {'image_size': (100, 100, 3)} - - def test_crop_to_size(self): - crop = Crop({'size': 50, 'type': 'crop'}) - image = np.zeros((100, 100, 3)) - image_rep = crop(DataRepresentation(image)) - - assert image_rep.data.shape == (50, 50, 3) - assert image_rep.metadata == {'image_size': (100, 100, 3)} - - def test_crop_higher_non_symmetric(self): - crop = Crop({'dst_width': 50, 'dst_height': 12, 'type': 'crop'}) - image = np.zeros((70, 50, 3)) - image_rep = crop(DataRepresentation(image)) - - assert image_rep.data.shape == (12, 50, 3) - assert image_rep.metadata == {'image_size': (70, 50, 3)} - - def test_crop_less(self): - crop = Crop({'dst_width': 151, 'dst_height': 42, 'type': 'crop'}) - image = np.zeros((30, 30, 3)) - image_rep = crop(DataRepresentation(image)) - - assert image_rep.data.shape == (42, 151, 3) - assert image_rep.metadata == {'image_size': (30, 30, 3)} - - def test_crop_less_non_symmetric(self): - crop = Crop({'dst_width': 42, 'dst_height': 151, 'type': 'crop'}) - image = np.zeros((30, 40, 3)) - image_rep = crop(DataRepresentation(image)) - - assert image_rep.data.shape == (151, 42, 3) - assert image_rep.metadata == {'image_size': (30, 40, 3)} - - def test_crop_to_negative_size_raise_config_error(self): - with pytest.raises(ConfigError): - Crop({'size': -151, 'type': 'crop'}) - - def test_crop_to_negative_destination_width_raise_config_error(self): - with pytest.raises(ConfigError): - Crop({'dst_width': -100, 'dst_height': 100, 'type': 'crop'}) - - def test_crop_to_negative_destination_height_raise_config_error(self): - with pytest.raises(ConfigError): - Crop({'dst_width': 100, 'dst_height': -100, 'type': 'crop'}) - - def test_crop_with_both_provided_size_and_dst_height_dst_width_warn(self): - image = np.zeros((30, 40, 3)) - with pytest.warns(None) as warnings: - crop = Crop({'dst_width': 100, 'dst_height': 100, 'size': 200, 'type': 'crop'}) - assert len(warnings) == 1 - result = crop.process(DataRepresentation(image)) - assert result.data.shape == (200, 200, 3) - assert result.metadata == {'image_size': (30, 40, 3)} - - -class TestFlip: - def test_horizontal_flip(self): - image = np.random.randint(0, 255, (30, 40, 3)) - expected_image = cv2.flip(image, 0) - flip = Flip({'type': 'flip', 'mode': 'horizontal'}) - assert np.array_equal(expected_image, flip.process(DataRepresentation(image)).data) - - def test_vertical_flip(self): - image = np.random.randint(0, 255, (30, 40, 3)) - expected_image = cv2.flip(image, 1) - flip = Flip({'type': 'flip', 'mode': 'vertical'}) - assert np.array_equal(expected_image, flip.process(DataRepresentation(image)).data) - - def test_flip_raise_config_error_if_mode_not_provided(self): - with pytest.raises(ConfigError): - Flip({'type': 'flip'}) - - def test_flip_raise_config_error_if_mode_unknown(self): - with pytest.raises(ConfigError): - Flip({'type': 'flip', 'mode': 'unknown'}) - - -class TestBGRtoRGB: - def test_bgr_to_rgb(self): - image = np.random.randint(0, 255, (30, 40, 3)).astype(np.uint8) - expected_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) - bgr_to_rgb = BgrToRgb({'type': 'bgr_to_rgb'}) - assert np.array_equal(expected_image, bgr_to_rgb.process(DataRepresentation(image)).data) - - -class TestCropRect: - def test_crop_rect_if_rect_not_provided(self): - image = np.zeros((30, 40, 3)) - crop_rect = CropRect({'type': 'crop_rect'}) - assert np.array_equal(image, crop_rect(image, {})) - - def test_crop_rect_if_rect_equal_image(self): - image = np.zeros((30, 40, 3)) - crop_rect = CropRect({'type': 'crop_rect'}) - assert np.array_equal(image, crop_rect(DataRepresentation(image), {'rect': [0, 0, 40, 30]}).data) - - def test_crop_rect(self): - image = np.zeros((30, 40, 3)) - image[:, 20:, :] = 1 - expected_image = np.ones((30, 20, 3)) - crop_rect = CropRect({'type': 'crop_rect'}) - assert np.array_equal(expected_image, crop_rect(DataRepresentation(image), {'rect': [20, 0, 40, 30]}).data) - - def test_crop_rect_negative_coordinates_of_rect(self): - image = np.zeros((30, 40, 3)) - image[:, 20:, :] = 1 - expected_image = image - crop_rect = CropRect({'type': 'crop_rect'}) - assert np.array_equal(expected_image, crop_rect(DataRepresentation(image), {'rect': [-20, 0, 40, 30]}).data) - - def test_crop_rect_more_image_size_coordinates_of_rect(self): - image = np.zeros((30, 40, 3)) - image[:, 20:, :] = 1 - expected_image = np.ones((30, 20, 3)) - crop_rect = CropRect({'type': 'crop_rect'}) - assert np.array_equal(expected_image, crop_rect(DataRepresentation(image), {'rect': [20, 0, 40, 50]}).data) - - -class TestExtendAroundRect: - def test_default_extend_around_rect_without_rect(self): - image = np.random.randint(0, 255, (30, 40, 3)).astype(np.uint8) - expected_image = image - extend_image_around_rect = ExtendAroundRect({'type': 'extend_around_rect'}) - assert np.array_equal(expected_image, extend_image_around_rect(DataRepresentation(image), {}).data) - - def test_default_extend_around_rect(self): - image = np.random.randint(0, 255, (30, 40, 3)).astype(np.uint8) - expected_image = image - extend_image_around_rect = ExtendAroundRect({'type': 'extend_around_rect'}) - assert np.array_equal( - expected_image, extend_image_around_rect(DataRepresentation(image), {'rect': [20, 0, 40, 30]}).data - ) - - def test_extend_around_rect_with_positive_augmentation(self): - image = np.random.randint(0, 255, (30, 40, 3)).astype(np.uint8) - expected_image = cv2.copyMakeBorder(image, int(15.5), int(31), int(0), int(11), cv2.BORDER_REPLICATE) - extend_image_around_rect = ExtendAroundRect({'type': 'extend_around_rect', 'augmentation_param': 0.5}) - assert np.array_equal( - expected_image, extend_image_around_rect(DataRepresentation(image), {'rect': [20, 0, 40, 30]}).data - ) - - def test_extend_around_rect_with_negative_augmentation(self): - image = np.random.randint(0, 255, (30, 40, 3)).astype(np.uint8) - expected_image = image - extend_image_around_rect = ExtendAroundRect({'type': 'extend_around_rect', 'augmentation_param': -0.5}) - assert np.array_equal( - expected_image, extend_image_around_rect(DataRepresentation(image), {'rect': [20, 0, 40, 30]}).data - ) - - def test_extend_around_rect_with_rect_equal_image(self): - image = np.random.randint(0, 255, (30, 40, 3)).astype(np.uint8) - expected_image = cv2.copyMakeBorder(image, int(15.5), int(31), int(20.5), int(41), cv2.BORDER_REPLICATE) - extend_image_around_rect = ExtendAroundRect({'type': 'extend_around_rect', 'augmentation_param': 0.5}) - assert np.array_equal( - expected_image, extend_image_around_rect(DataRepresentation(image), {'rect': [0, 0, 40, 30]}).data - ) - - def test_extend_around_rect_negative_coordinates_of_rect(self): - image = np.random.randint(0, 255, (30, 40, 3)).astype(np.uint8) - expected_image = cv2.copyMakeBorder(image, int(15.5), int(31), int(20.5), int(41), cv2.BORDER_REPLICATE) - extend_image_around_rect = ExtendAroundRect({'type': 'extend_around_rect', 'augmentation_param': 0.5}) - assert np.array_equal( - expected_image, extend_image_around_rect(DataRepresentation(image), {'rect': [-20, 0, 40, 30]}).data - ) - - def test_extend_around_rect_more_image_size_coordinates_of_rect(self): - image = np.random.randint(0, 255, (30, 40, 3)).astype(np.uint8) - expected_image = cv2.copyMakeBorder(image, int(15.5), int(31), int(0), int(11), cv2.BORDER_REPLICATE) - extend_image_around_rect = ExtendAroundRect({'type': 'extend_around_rect', 'augmentation_param': 0.5}) - assert np.array_equal( - expected_image, extend_image_around_rect(DataRepresentation(image), {'rect': [20, 0, 40, 50]}).data - ) - - -class TestPointAlignment: - def test_point_alignment_width_negative_size_raise_config_error(self): - with pytest.raises(ConfigError): - PointAligner({'type': 'point_alignment', 'size': -100}) - - def test_point_alignment_negative_destination_width_raise_config_error(self): - with pytest.raises(ConfigError): - PointAligner({'type': 'point_alignment', 'dst_width': -100, 'dst_height': 100}) - - def test_point_alignment_to_negative_destination_height_raise_config_error(self): - with pytest.raises(ValueError): - PointAligner({'type': 'point_alignment', 'dst_width': 100, 'dst_height': -100}) - - def test_point_alignment_provided_only_dst_height_raise_config_error(self): - with pytest.raises(ValueError): - PointAligner({'type': 'point_alignment', 'dst_height': 100}) - - def test_point_alignment_provided_only_dst_width_raise_config_error(self): - with pytest.raises(ValueError): - PointAligner({'type': 'point_alignment', 'dst_width': 100}) - - def test_point_alignment_both_provided_size_and_dst_height_dst_width_warn(self): - input_image = np.ones((100, 50, 3)) - - with pytest.warns(None) as warnings: - point_aligner = PointAligner({'type': 'point_alignment', 'dst_width': 100, 'dst_height': 100, 'size': 200}) - assert len(warnings) == 1 - result = point_aligner(DataRepresentation(input_image), {}).data - assert result.shape == (100, 50, 3) - - def test_point_alignment_not_provided_points_im_meta(self): - input_image = np.ones((100, 50, 3)) - - point_aligner = PointAligner({'type': 'point_alignment', 'dst_width': 100, 'dst_height': 100}) - result = point_aligner(DataRepresentation(input_image), {}).data - assert result.shape == (100, 50, 3) - - def test_point_alignment_default_use_normalization(self): - image = np.random.randint(0, 255, (40, 40, 3)).astype(np.uint8) - - point_aligner = PointAligner({'type': 'point_alignment', 'dst_width': 40, 'dst_height': 40}) - result = point_aligner( - DataRepresentation(image), {'keypoints': PointAligner.ref_landmarks.reshape(-1).tolist()} - ).data - transformation_matrix = point_aligner.transformation_from_points( - point_aligner.ref_landmarks * 40, point_aligner.ref_landmarks - ) - expected_result = cv2.warpAffine(image, transformation_matrix, (40, 40), flags=cv2.WARP_INVERSE_MAP) - - assert np.array_equal(result, expected_result) - - def test_point_alignment_use_normalization(self): - image = np.random.randint(0, 255, (40, 40, 3)).astype(np.uint8) - - point_aligner = PointAligner({'type': 'point_alignment', 'dst_width': 40, 'dst_height': 40, 'normalize': True}) - result = point_aligner( - DataRepresentation(image), {'keypoints': PointAligner.ref_landmarks.reshape(-1).tolist()} - ).data - transformation_matrix = point_aligner.transformation_from_points( - point_aligner.ref_landmarks * 40, point_aligner.ref_landmarks - ) - expected_result = cv2.warpAffine(image, transformation_matrix, (40, 40), flags=cv2.WARP_INVERSE_MAP) - - assert np.array_equal(result, expected_result) - - def test_point_alignment_without_normalization(self): - image = np.random.randint(0, 255, (40, 40, 3)).astype(np.uint8) - - point_aligner = PointAligner({'type': 'point_alignment', 'dst_width': 40, 'dst_height': 40, 'normalize': False}) - result = point_aligner( - DataRepresentation(image), {'keypoints': PointAligner.ref_landmarks.reshape(-1).tolist()} - ).data - transformation_matrix = point_aligner.transformation_from_points( - point_aligner.ref_landmarks * 40, point_aligner.ref_landmarks * 40 - ) - expected_result = cv2.warpAffine(image, transformation_matrix, (40, 40), flags=cv2.WARP_INVERSE_MAP) - - assert np.array_equal(result, expected_result) - - def test_point_alignment_with_drawing_points(self): - image = np.random.randint(0, 255, (40, 40, 3)).astype(np.uint8) - - point_aligner = PointAligner({ - 'type': 'point_alignment', 'dst_width': 40, 'dst_height': 40, 'draw_points': True - }) - result = point_aligner( - DataRepresentation(image), {'keypoints': PointAligner.ref_landmarks.reshape(-1).tolist()} - ).data - transformation_matrix = point_aligner.transformation_from_points( - point_aligner.ref_landmarks * 40, point_aligner.ref_landmarks - ) - expected_result = image - for point in PointAligner.ref_landmarks: - cv2.circle(expected_result, (int(point[0]), int(point[1])), 5, (255, 0, 0), -1) - expected_result = cv2.warpAffine(expected_result, transformation_matrix, (40, 40), flags=cv2.WARP_INVERSE_MAP) - - assert np.array_equal(result, expected_result) - - def test_point_alignment_with_resizing(self): - image = np.random.randint(0, 255, (80, 80, 3)).astype(np.uint8) - - point_aligner = PointAligner({'type': 'point_alignment', 'size': 40}) - result = point_aligner( - DataRepresentation(image), {'keypoints': PointAligner.ref_landmarks.reshape(-1).tolist()} - ).data - transformation_matrix = point_aligner.transformation_from_points( - point_aligner.ref_landmarks * 40, point_aligner.ref_landmarks * 0.5 - ) - expected_result = cv2.resize(image, (40, 40)) - expected_result = cv2.warpAffine(expected_result, transformation_matrix, (40, 40), flags=cv2.WARP_INVERSE_MAP) - - assert np.array_equal(result, expected_result) - - -class TestPreprocessorExtraArgs: - def test_resize_raise_config_error_on_extra_args(self): - with pytest.raises(ConfigError): - Preprocessor.provide('resize', {'type': 'resize', 'size': 1, 'something_extra': 'extra'}) - - def test_normalization_raise_config_error_on_extra_args(self): - with pytest.raises(ConfigError): - Preprocessor.provide('normalization', {'type': 'normalization', 'mean': 0, 'something_extra': 'extra'}) - - def test_bgr_to_rgb_raise_config_error_on_extra_args(self): - with pytest.raises(ConfigError): - Preprocessor.provide('bgr_to_rgb', {'type': 'bgr_to_rgb', 'something_extra': 'extra'}) - - def test_flip_raise_config_error_on_extra_args(self): - with pytest.raises(ConfigError): - Preprocessor.provide('flip', {'type': 'flip', 'something_extra': 'extra'}) - - def test_crop_accuracy_raise_config_error_on_extra_args(self): - with pytest.raises(ConfigError): - Preprocessor.provide('crop', {'type': 'crop', 'size': 1, 'something_extra': 'extra'}) - - def test_extend_around_rect_raise_config_error_on_extra_args(self): - with pytest.raises(ConfigError): - Preprocessor.provide('extend_around_rect', {'type': 'extend_around_rect', 'something_extra': 'extra'}) - - def test_point_alignment_raise_config_error_on_extra_args(self): - with pytest.raises(ConfigError): - Preprocessor.provide('point_alignment', {'type': 'point_alignment', 'something_extra': 'extra'}) diff --git a/tools/accuracy_checker/tests/test_presenter.py b/tools/accuracy_checker/tests/test_presenter.py deleted file mode 100644 index 4d2b5d4f501a6b..00000000000000 --- a/tools/accuracy_checker/tests/test_presenter.py +++ /dev/null @@ -1,552 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np -import pytest -from unittest.mock import MagicMock, call -from accuracy_checker.metrics import MetricsExecutor -from accuracy_checker.presenters import ScalarPrintPresenter, VectorPrintPresenter, EvaluationResult -from accuracy_checker.representation import ClassificationAnnotation, ClassificationPrediction - - -class TestPresenter: - def test_config_default_presenter(self): - annotations = [ClassificationAnnotation('identifier', 3)] - predictions = [ClassificationPrediction('identifier', [1.0, 1.0, 1.0, 4.0])] - config = [{'type': 'accuracy', 'top_k': 1}] - dispatcher = MetricsExecutor(config, None) - dispatcher.update_metrics_on_batch(annotations, predictions) - - for presenter, _ in dispatcher.iterate_metrics(annotations, predictions): - assert isinstance(presenter, ScalarPrintPresenter) - - def test_config_scalar_presenter(self): - annotations = [ClassificationAnnotation('identifier', 3)] - predictions = [ClassificationPrediction('identifier', [1.0, 1.0, 1.0, 4.0])] - config = [{'type': 'accuracy', 'top_k': 1, 'presenter': 'print_scalar'}] - dispatcher = MetricsExecutor(config, None) - dispatcher.update_metrics_on_batch(annotations, predictions) - - for presenter, _ in dispatcher.iterate_metrics(annotations, predictions): - assert isinstance(presenter, ScalarPrintPresenter) - - def test_config_vector_presenter(self): - annotations = [ClassificationAnnotation('identifier', 3)] - predictions = [ClassificationPrediction('identifier', [1.0, 1.0, 1.0, 4.0])] - config = [{'type': 'accuracy', 'top_k': 1, 'presenter': 'print_vector'}] - dispatcher = MetricsExecutor(config, None) - dispatcher.update_metrics_on_batch(annotations, predictions) - - for presenter, _ in dispatcher.iterate_metrics(annotations, predictions): - assert isinstance(presenter, VectorPrintPresenter) - - def test_config_unknown_presenter(self): - config = [{'type': 'accuracy', 'top_k': 1, 'presenter': 'print_somehow'}] - with pytest.raises(ValueError): - MetricsExecutor(config, None) - - def test_scalar_presenter_with_scalar_data(self, mocker): - mock_write_scalar_result = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='scalar_metric', - metric_type='metric', - evaluated_value=0.1, - reference_value=None, - threshold=None, - meta={}, - ) - presenter = ScalarPrintPresenter() - presenter.write_result(result) - mock_write_scalar_result.assert_called_once_with( - result.evaluated_value, - result.name, - result.threshold, - None, - postfix='%', - scale=100, - result_format='{:.2f}' - ) - - def test_scalar_presenter_with_vector_data(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='vector_metric', - metric_type='metric', - evaluated_value=[0.4, 0.6], - reference_value=None, - threshold=None, - meta={}, - ) - presenter = ScalarPrintPresenter() - presenter.write_result(result) - mock_write_scalar_res.assert_called_once_with( - np.mean(result.evaluated_value), - result.name, - result.threshold, - None, - postfix='%', - scale=100, - result_format='{:.2f}' - ) - - def test_default_format_for_scalar_presenter_with_ignore_formatting(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='vector_metric', - metric_type='metric', - evaluated_value=[0.456], - reference_value=None, - threshold=None, - meta={}, - ) - presenter = ScalarPrintPresenter() - presenter.write_result(result, ignore_results_formatting=True) - mock_write_scalar_res.assert_called_once_with( - np.mean(result.evaluated_value), - result.name, - result.threshold, - None, - postfix=' ', - scale=1, - result_format='{}' - ) - - def test_reference_value_for_scalar_presenter(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='vector_metric', - metric_type='metric', - evaluated_value=[0.456], - reference_value=45.6, - threshold=None, - meta={}, - ) - presenter = ScalarPrintPresenter() - presenter.write_result(result) - mock_write_scalar_res.assert_called_once_with( - np.mean(result.evaluated_value), - result.name, - result.threshold, - 0.0, - postfix='%', - scale=100, - result_format='{:.2f}' - ) - - def test_reference_value_for_scalar_presenter_with_ignore_results_formatting(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='vector_metric', - metric_type='metric', - evaluated_value=[0.456], - reference_value=45.6, - threshold=None, - meta={}, - ) - presenter = ScalarPrintPresenter() - presenter.write_result(result, ignore_results_formatting=True) - mock_write_scalar_res.assert_called_once_with( - np.mean(result.evaluated_value), - result.name, - result.threshold, - 0.0, - postfix=' ', - scale=1, - result_format='{}' - ) - - def test_specific_format_for_scalar_presenter_with_ignore_formatting(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='vector_metric', - metric_type='metric', - evaluated_value=[0.456], - reference_value=None, - threshold=None, - meta={'scale': 0.5, 'postfix': 'km/h', 'data_format': '{:.4f}'}, - ) - presenter = ScalarPrintPresenter() - presenter.write_result(result, ignore_results_formatting=True) - mock_write_scalar_res.assert_called_once_with( - np.mean(result.evaluated_value), - result.name, - result.reference_value, - result.threshold, - postfix=' ', - scale=1, - result_format='{}' - ) - - def test_vector_presenter_with_scaler_data(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='scalar_metric', - metric_type='metric', - evaluated_value=0.4, - reference_value=None, - threshold=None, - meta={}, - ) - presenter = VectorPrintPresenter() - presenter.write_result(result) - mock_write_scalar_res.assert_called_once_with( - result.evaluated_value, - result.name, - None, - result.threshold, - postfix='%', - scale=100, - value_name=None, - result_format='{:.2f}' - ) - - def test_vector_presenter_with_scaler_data_compare_with_reference(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='scalar_metric', - metric_type='metric', - evaluated_value=0.4, - reference_value=42, - threshold=None, - meta={}, - ) - presenter = VectorPrintPresenter() - presenter.write_result(result) - mock_write_scalar_res.assert_called_once_with( - result.evaluated_value, - result.name, - result.threshold, - 2, - postfix='%', - scale=100, - value_name=None, - result_format='{:.2f}' - ) - - def test_vector_presenter_with_scaler_data_compare_with_reference_ignore_formatting(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='scalar_metric', - metric_type='metric', - evaluated_value=0.4, - reference_value=42, - threshold=None, - meta={}, - ) - presenter = VectorPrintPresenter() - presenter.write_result(result, ignore_results_formatting=True) - mock_write_scalar_res.assert_called_once_with( - result.evaluated_value, - result.name, - result.threshold, - 2, - postfix=' ', - scale=1, - value_name=None, - result_format='{}' - ) - - def test_vector_presenter_with_vector_data_contain_one_element(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='scalar_metric', - metric_type='metric', - evaluated_value=[0.4], - reference_value=None, - threshold=None, - meta={'names': ['prediction']} - ) - presenter = VectorPrintPresenter() - presenter.write_result(result) - mock_write_scalar_res.assert_called_once_with( - result.evaluated_value[0], - result.name, - None, - result.threshold, - postfix='%', - scale=100, - value_name=result.meta['names'][0], - result_format='{:.2f}' - ) - - def test_vector_presenter_with_vector_data_contain_one_element_compare_with_reference(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='scalar_metric', - metric_type='metric', - evaluated_value=[0.4], - reference_value=42, - threshold=None, - meta={}, - ) - presenter = VectorPrintPresenter() - presenter.write_result(result) - mock_write_scalar_res.assert_called_once_with( - result.evaluated_value[0], - result.name, - result.threshold, - 2, - postfix='%', - scale=100, - value_name=None, - result_format='{:.2f}' - ) - - def test_vector_presenter__with_vector_data_contain_one_element_compare_with_reference_ignore_formatting(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='vector_metric', - metric_type='metric', - evaluated_value=[0.4], - reference_value=42, - threshold=None, - meta={}, - ) - presenter = VectorPrintPresenter() - presenter.write_result(result, ignore_results_formatting=True) - mock_write_scalar_res.assert_called_once_with( - result.evaluated_value[0], - result.name, - result.threshold, - 2, - postfix=' ', - scale=1, - value_name=None, - result_format='{}' - ) - - def test_vector_presenter_with_vector_data_with_default_postfix_and_scale(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='vector_metric', - metric_type='metric', - evaluated_value=[0.4, 0.6], - reference_value=None, - threshold=None, - meta={'names': ['class1', 'class2']} - ) - presenter = VectorPrintPresenter() - presenter.write_result(result) - calls = [ - call( - result.evaluated_value[0], result.name, - postfix='%', scale=100, value_name=result.meta['names'][0], result_format='{:.2f}' - ), - call( - result.evaluated_value[1], result.name, - postfix='%', scale=100, value_name=result.meta['names'][1], result_format='{:.2f}' - ), - call( - np.mean(np.multiply(result.evaluated_value, 100)), result.name, result.threshold, - None, value_name='mean', postfix='%', scale=1, result_format='{:.2f}' - ) - ] - mock_write_scalar_res.assert_has_calls(calls) - - def test_vector_presenter_with_vector_data_has_default_format_with_ignore_formatting(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='vector_metric', - metric_type='metric', - evaluated_value=[0.4, 0.6], - reference_value=None, - threshold=None, - meta={'names': ['class1', 'class2']} - ) - presenter = VectorPrintPresenter() - presenter.write_result(result, ignore_results_formatting=True) - calls = [ - call( - result.evaluated_value[0], result.name, - postfix=' ', scale=1, value_name=result.meta['names'][0], result_format='{}' - ), - call( - result.evaluated_value[1], result.name, - postfix=' ', scale=1, value_name=result.meta['names'][1], result_format='{}' - ), - call( - np.mean(np.multiply(result.evaluated_value, 1)), result.name, result.threshold, None, - value_name='mean', postfix=' ', scale=1, result_format='{}' - ) - ] - mock_write_scalar_res.assert_has_calls(calls) - - def test_vector_presenter_with_vector_data_with_default_formating_compare_with_ref(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='vector_metric', - metric_type='metric', - evaluated_value=[0.4, 0.6], - reference_value=49, - threshold=None, - meta={'names': ['class1', 'class2']} - ) - presenter = VectorPrintPresenter() - presenter.write_result(result) - calls = [ - call( - result.evaluated_value[0], result.name, - postfix='%', scale=100, value_name=result.meta['names'][0], result_format='{:.2f}' - ), - call( - result.evaluated_value[1], result.name, - postfix='%', scale=100, value_name=result.meta['names'][1], result_format='{:.2f}' - ), - call( - np.mean(np.multiply(result.evaluated_value, 100)), result.name, result.threshold, - 1, value_name='mean', postfix='%', scale=1, result_format='{:.2f}' - ) - ] - mock_write_scalar_res.assert_has_calls(calls) - - def test_vector_presenter_with_vector_data_has_default_format_with_ignore_formatting_compare_with_ref(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='vector_metric', - metric_type='metric', - evaluated_value=[0.4, 0.6], - reference_value=49, - threshold=None, - meta={'names': ['class1', 'class2']} - ) - presenter = VectorPrintPresenter() - presenter.write_result(result, ignore_results_formatting=True) - calls = [ - call( - result.evaluated_value[0], result.name, - postfix=' ', scale=1, value_name=result.meta['names'][0], result_format='{}' - ), - call( - result.evaluated_value[1], result.name, - postfix=' ', scale=1, value_name=result.meta['names'][1], result_format='{}' - ), - call( - np.mean(np.multiply(result.evaluated_value, 1)), result.name, result.threshold, 1, - value_name='mean', postfix=' ', scale=1, result_format='{}' - ) - ] - mock_write_scalar_res.assert_has_calls(calls) - - def test_vector_presenter_with_vector_data_has_specific_format_with_ignore_formatting(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='scalar_metric', - metric_type='metric', - evaluated_value=[0.4, 0.6], - reference_value=None, - threshold=None, - meta={'names': ['class1', 'class2'], 'scale': 0.5, 'postfix': 'km/h', 'data_format': '{:.4f}'} - ) - presenter = VectorPrintPresenter() - presenter.write_result(result, ignore_results_formatting=True) - calls = [ - call( - result.evaluated_value[0], result.name, - postfix=' ', scale=1, value_name=result.meta['names'][0], result_format='{}' - ), - call( - result.evaluated_value[1], result.name, - postfix=' ', scale=1, value_name=result.meta['names'][1], result_format='{}' - ), - call( - np.mean(np.multiply(result.evaluated_value, 1)), result.name, result.reference_value, result.threshold, - value_name='mean', postfix=' ', scale=1, result_format='{}' - ) - ] - mock_write_scalar_res.assert_has_calls(calls) - - def test_vector_presenter_with_vector_data_with_scalar_postfix(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='scalar_metric', - metric_type='metric', - evaluated_value=[0.4, 0.6], - reference_value=None, - threshold=None, - meta={'names': ['class1', 'class2'], 'postfix': '_'} - ) - presenter = VectorPrintPresenter() - presenter.write_result(result) - calls = [ - call(result.evaluated_value[0], result.name, - postfix=result.meta['postfix'], scale=100, value_name=result.meta['names'][0], result_format='{:.2f}' - ), - call( - result.evaluated_value[1], result.name, - postfix=result.meta['postfix'], scale=100, value_name=result.meta['names'][1], result_format='{:.2f}' - ), - call( - np.mean(np.multiply(result.evaluated_value, 100)), result.name, - result.threshold, None, value_name='mean', postfix=result.meta['postfix'], scale=1, result_format='{:.2f}' - ) - ] - mock_write_scalar_res.assert_has_calls(calls) - - def test_vector_presenter_with_vector_data_with_scalar_scale(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='scalar_metric', - metric_type='metric', - evaluated_value=[0.4, 0.6], - reference_value=None, - threshold=None, - meta={'names': ['class1', 'class2'], 'scale': 10} - ) - presenter = VectorPrintPresenter() - presenter.write_result(result) - calls = [ - call( - result.evaluated_value[0], result.name, - postfix='%', scale=result.meta['scale'], value_name=result.meta['names'][0], result_format='{:.2f}' - ), - call( - result.evaluated_value[1], result.name, - postfix='%', scale=result.meta['scale'], value_name=result.meta['names'][1], result_format='{:.2f}' - ), - call( - np.mean(np.multiply(result.evaluated_value, result.meta['scale'])), result.name, None, result.threshold, - value_name='mean', postfix='%', scale=1, result_format='{:.2f}' - ) - ] - mock_write_scalar_res.assert_has_calls(calls) - - def test_vector_presenter_with_vector_data_with_vector_scale(self, mocker): - mock_write_scalar_res = mocker.patch('accuracy_checker.presenters.write_scalar_result') # type: MagicMock - result = EvaluationResult( - name='scalar_metric', - metric_type='metric', - evaluated_value=[0.4, 0.6], - reference_value=None, - threshold=None, - meta={'names': ['class1', 'class2'], 'scale': [1, 2]} - ) - presenter = VectorPrintPresenter() - presenter.write_result(result) - calls = [ - call( - result.evaluated_value[0], result.name, - postfix='%', scale=result.meta['scale'][0], result_format='{:.2f}', value_name=result.meta['names'][0] - ), - call( - result.evaluated_value[1], result.name, postfix='%', - scale=result.meta['scale'][1], result_format='{:.2f}', value_name=result.meta['names'][1] - ), - call( - np.mean(np.multiply(result.evaluated_value, result.meta['scale'])), result.name, result.threshold, - None, result_format='{:.2f}', value_name='mean', postfix='%', scale=1 - ) - ] - mock_write_scalar_res.assert_has_calls(calls) diff --git a/tools/accuracy_checker/tests/test_regression_metrics.py b/tools/accuracy_checker/tests/test_regression_metrics.py deleted file mode 100644 index 5e478043497d8a..00000000000000 --- a/tools/accuracy_checker/tests/test_regression_metrics.py +++ /dev/null @@ -1,342 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import pytest -from accuracy_checker.metrics import MetricsExecutor -from accuracy_checker.representation import RegressionPrediction, RegressionAnnotation -from accuracy_checker.presenters import EvaluationResult - - -class TestRegressionMetric: - def setup_method(self): - self.module = 'accuracy_checker.metrics.metric_evaluator' - - def test_mae_with_zero_diff_between_annotation_and_prediction(self): - annotations = [RegressionAnnotation('identifier', 3)] - predictions = [RegressionPrediction('identifier', 3)] - config = [{'type': 'mae'}] - expected = EvaluationResult( - pytest.approx([0.0, 0.0]), - None, - 'mae', - 'mae', - None, - {'postfix': ' ', 'scale': 1, 'names': ['mean', 'std'], 'calculate_mean': False} - ) - dispatcher = MetricsExecutor(config, None) - - dispatcher.update_metrics_on_batch(annotations, predictions) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_mae_with_negative_diff_between_annotation_and_prediction(self): - annotations = [RegressionAnnotation('identifier', 3), RegressionAnnotation('identifier2', 1)] - predictions = [RegressionPrediction('identifier', 5), RegressionPrediction('identifier2', 5)] - config = [{'type': 'mae'}] - expected = EvaluationResult( - pytest.approx([3.0, 1.0]), - None, - 'mae', - 'mae', - None, - {'postfix': ' ', 'scale': 1, 'names': ['mean', 'std'], 'calculate_mean': False} - ) - dispatcher = MetricsExecutor(config, None) - - dispatcher.update_metrics_on_batch(annotations, predictions) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_mae_with_positive_diff_between_annotation_and_prediction(self): - annotations = [RegressionAnnotation('identifier', 3), RegressionAnnotation('identifier2', 1)] - predictions = [RegressionPrediction('identifier', 1), RegressionPrediction('identifier2', -3)] - config = [{'type': 'mae'}] - expected = EvaluationResult( - pytest.approx([3.0, 1.0]), - None, - 'mae', - 'mae', - None, - {'postfix': ' ', 'scale': 1, 'names': ['mean', 'std'], 'calculate_mean': False} - ) - dispatcher = MetricsExecutor(config, None) - - dispatcher.update_metrics_on_batch(annotations, predictions) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_mse_with_zero_diff_between_annotation_and_prediction(self): - annotations = [RegressionAnnotation('identifier', 3)] - predictions = [RegressionPrediction('identifier', 3)] - config = [{'type': 'mse'}] - expected = EvaluationResult( - pytest.approx([0.0, 0.0]), - None, - 'mse', - 'mse', - None, - {'postfix': ' ', 'scale': 1, 'names': ['mean', 'std'], 'calculate_mean': False} - ) - dispatcher = MetricsExecutor(config, None) - - dispatcher.update_metrics_on_batch(annotations, predictions) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_mse_with_negative_diff_between_annotation_and_prediction(self): - annotations = [RegressionAnnotation('identifier', 3), RegressionAnnotation('identifier2', 1)] - predictions = [RegressionPrediction('identifier', 5), RegressionPrediction('identifier2', 5)] - config = [{'type': 'mse'}] - expected = EvaluationResult( - pytest.approx([10.0, 6.0]), - None, - 'mse', - 'mse', - None, - {'postfix': ' ', 'scale': 1, 'names': ['mean', 'std'], 'calculate_mean': False} - ) - dispatcher = MetricsExecutor(config, None) - - dispatcher.update_metrics_on_batch(annotations, predictions) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_mse_with_positive_diff_between_annotation_and_prediction(self): - annotations = [RegressionAnnotation('identifier', 3), RegressionAnnotation('identifier2', 1)] - predictions = [RegressionPrediction('identifier', 1), RegressionPrediction('identifier2', -3)] - config = [{'type': 'mse'}] - expected = EvaluationResult( - pytest.approx([10.0, 6.0]), - None, - 'mse', - 'mse', - None, - {'postfix': ' ', 'scale': 1, 'names': ['mean', 'std'], 'calculate_mean': False} - ) - dispatcher = MetricsExecutor(config, None) - - dispatcher.update_metrics_on_batch(annotations, predictions) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_missed_interval(self): - config = [{'type': 'mae_on_interval'}] - with pytest.raises(ValueError): - MetricsExecutor(config, None) - - def test_mae_on_interval_default_all_missed(self): - annotations = [RegressionAnnotation('identifier', -2)] - predictions = [RegressionPrediction('identifier', 1)] - config = [{'type': 'mae_on_interval', 'end': 1}] - expected = EvaluationResult( - pytest.approx([0.0]), - None, - 'mae_on_interval', - 'mae_on_interval', - None, - {'postfix': ' ', 'scale': 1, 'names': [], 'calculate_mean': False} - ) - dispatcher = MetricsExecutor(config, None) - - dispatcher.update_metrics_on_batch(annotations, predictions) - - with pytest.warns(UserWarning) as warnings: - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert len(warnings) == 1 - assert evaluation_result == expected - - def test_mae_on_interval_default_all_not_in_range_not_ignore_out_of_range(self): - annotations = [RegressionAnnotation('identifier', -1), RegressionAnnotation('identifier', 2)] - predictions = [RegressionPrediction('identifier', 1), RegressionPrediction('identifier', 2)] - expected = EvaluationResult( - pytest.approx([2.0, 0.0, 0.0, 0.0]), - None, - 'mae_on_interval', - 'mae_on_interval', - None, - { - 'postfix': ' ', - 'scale': 1, - 'names': ['mean: < 0.0', 'std: < 0.0', 'mean: > 1.0', 'std: > 1.0'], - 'calculate_mean': False - } - ) - config = [{'type': 'mae_on_interval', 'end': 1, 'ignore_values_not_in_interval': False}] - dispatcher = MetricsExecutor(config, None) - - dispatcher.update_metrics_on_batch(annotations, predictions) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_mae_on_interval_values_in_range(self): - annotations = [RegressionAnnotation('identifier', 0.5), RegressionAnnotation('identifier', 0.5)] - predictions = [RegressionPrediction('identifier', 1), RegressionPrediction('identifier', 0.25)] - config = [{'type': 'mae_on_interval', 'end': 1}] - expected = EvaluationResult( - pytest.approx([0.375, 0.125]), - None, - 'mae_on_interval', - 'mae_on_interval', - None, - {'postfix': ' ', 'scale': 1, 'names': ['mean: <= 0.0 < 1.0', 'std: <= 0.0 < 1.0'], 'calculate_mean': False} - ) - dispatcher = MetricsExecutor(config, None) - - dispatcher.update_metrics_on_batch(annotations, predictions) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_mae_on_interval_default_not_ignore_out_of_range(self): - annotations = [ - RegressionAnnotation('identifier', -1), - RegressionAnnotation('identifier', 2), - RegressionAnnotation('identifier', 0.5) - ] - predictions = [ - RegressionPrediction('identifier', 1), - RegressionPrediction('identifier', 2), - RegressionPrediction('identifier', 1) - ] - config = [{'type': 'mae_on_interval', 'end': 1, 'ignore_values_not_in_interval': False}] - expected = EvaluationResult( - pytest.approx([2.0, 0.0, 0.5, 0.0, 0.0, 0.0]), - None, - 'mae_on_interval', - 'mae_on_interval', - None, - { - 'postfix': ' ', - 'scale': 1, - 'names': [ - 'mean: < 0.0', - 'std: < 0.0', - 'mean: <= 0.0 < 1.0', - 'std: <= 0.0 < 1.0', - 'mean: > 1.0', - 'std: > 1.0' - ], - 'calculate_mean': False - } - ) - dispatcher = MetricsExecutor(config, None) - - dispatcher.update_metrics_on_batch(annotations, predictions) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_mae_on_interval_with_given_interval(self): - annotations = [ - RegressionAnnotation('identifier', -1), - RegressionAnnotation('identifier', 2), - RegressionAnnotation('identifier', 1) - ] - predictions = [ - RegressionPrediction('identifier', 1), - RegressionPrediction('identifier', 3), - RegressionPrediction('identifier', 1) - ] - config = [{'type': 'mae_on_interval', 'intervals': [0.0, 2.0, 4.0]}] - expected = EvaluationResult( - pytest.approx([0.0, 0.0, 1.0, 0.0]), - None, - 'mae_on_interval', - 'mae_on_interval', - None, - { - 'postfix': ' ', - 'scale': 1, - 'names': ['mean: <= 0.0 < 2.0', 'std: <= 0.0 < 2.0', 'mean: <= 2.0 < 4.0', 'std: <= 2.0 < 4.0'], - 'calculate_mean': False - } - ) - dispatcher = MetricsExecutor(config, None) - - dispatcher.update_metrics_on_batch(annotations, predictions) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_mae_on_interval_with_repeated_values(self): - annotations = [ - RegressionAnnotation('identifier', -1), - RegressionAnnotation('identifier', 2), - RegressionAnnotation('identifier', 1) - ] - predictions = [ - RegressionPrediction('identifier', 1), - RegressionPrediction('identifier', 3), - RegressionPrediction('identifier', 1) - ] - config = [{'type': 'mae_on_interval', 'intervals': [0.0, 2.0, 2.0, 4.0]}] - expected = EvaluationResult( - pytest.approx([0.0, 0.0, 1.0, 0.0]), - None, - 'mae_on_interval', - 'mae_on_interval', - None, - { - 'postfix': ' ', - 'scale': 1, - 'names': ['mean: <= 0.0 < 2.0', 'std: <= 0.0 < 2.0', 'mean: <= 2.0 < 4.0', 'std: <= 2.0 < 4.0'], - 'calculate_mean': False - } - ) - dispatcher = MetricsExecutor(config, None) - - dispatcher.update_metrics_on_batch(annotations, predictions) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_mae_on_interval_with_unsorted_values(self): - annotations = [ - RegressionAnnotation('identifier', -1), - RegressionAnnotation('identifier', 2), - RegressionAnnotation('identifier', 1) - ] - predictions = [ - RegressionPrediction('identifier', 1), - RegressionPrediction('identifier', 3), - RegressionPrediction('identifier', 1) - ] - config = [{'type': 'mae_on_interval', 'intervals': [2.0, 0.0, 4.0]}] - expected = EvaluationResult( - pytest.approx([0.0, 0.0, 1.0, 0.0]), - None, - 'mae_on_interval', - 'mae_on_interval', - None, - { - 'postfix': ' ', 'scale': 1, - 'names': ['mean: <= 0.0 < 2.0', 'std: <= 0.0 < 2.0', 'mean: <= 2.0 < 4.0', 'std: <= 2.0 < 4.0'], - 'calculate_mean': False - } - ) - dispatcher = MetricsExecutor(config, None) - - dispatcher.update_metrics_on_batch(annotations, predictions) - - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected diff --git a/tools/accuracy_checker/tests/test_reid_metrics.py b/tools/accuracy_checker/tests/test_reid_metrics.py deleted file mode 100644 index b73008a2e52982..00000000000000 --- a/tools/accuracy_checker/tests/test_reid_metrics.py +++ /dev/null @@ -1,77 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import numpy as np -from accuracy_checker.metrics.reid import eval_cmc - - -class TestCMC: - def test_only_distance_matrix(self): - distance_matrix = np.array([ - [0, 1, 2, 3, 4], - [1, 0, 2, 3, 4], - [0, 1, 2, 3, 4], - [0, 1, 2, 3, 4], - [1, 2, 3, 4, 0] - ]) - m, n = distance_matrix.shape - - result = eval_cmc( - distance_matrix, - query_ids=np.arange(m), - gallery_ids=np.arange(n), - query_cams=np.zeros(m).astype(np.int32), - gallery_cams=np.ones(n).astype(np.int32) - ) - - assert np.all(result[:5] == [0.6, 0.6, 0.8, 1.0, 1.0]) - - def test_duplicate_ids(self): - distance_matrix = np.array([ - [0, 1, 2, 3], - [0, 1, 2, 3], - [0, 1, 2, 3], - [0, 1, 2, 3] - ]) - - result = eval_cmc( - distance_matrix, - query_ids=np.array([0, 0, 1, 1]), - gallery_ids=np.array([0, 0, 1, 1]), - top_k=4, - gallery_cams=np.ones(distance_matrix.shape[1]).astype(np.int32), - query_cams=np.zeros(distance_matrix.shape[0]).astype(np.int32), - separate_camera_set=False, - single_gallery_shot=False - ) - - assert np.all(result == [0.5, 0.5, 1, 1]) - - def test_duplicate_cams(self): - distance_matrix = np.tile(np.arange(5), (5, 1)) - - result = eval_cmc( - distance_matrix, - query_ids=np.array([0, 0, 0, 1, 1]), - gallery_ids=np.array([0, 0, 0, 1, 1]), - query_cams=np.array([0, 0, 0, 0, 0]), - gallery_cams=np.array([0, 1, 1, 1, 1]), - top_k=5, - separate_camera_set=False, - single_gallery_shot=False - ) - - assert np.all(result == [0.6, 0.6, 0.6, 1, 1]) diff --git a/tools/accuracy_checker/tests/test_segmentation_metrics.py b/tools/accuracy_checker/tests/test_segmentation_metrics.py deleted file mode 100644 index 56e13b660af2a5..00000000000000 --- a/tools/accuracy_checker/tests/test_segmentation_metrics.py +++ /dev/null @@ -1,164 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import pytest -import numpy as np -from accuracy_checker.metrics import MetricsExecutor -from accuracy_checker.presenters import EvaluationResult -from .common import single_class_dataset, multi_class_dataset, make_segmentation_representation - - -def create_config(metric_name, use_argmax=False): - return [{'type': metric_name, 'use_argmax': use_argmax}] - - -def generate_expected_result(values, metric_name, labels=None): - meta = {'names': list(labels.values())} if labels else {} - - return EvaluationResult(pytest.approx(values), None, metric_name, metric_name, None, meta) - - -class TestPixelAccuracy: - name = 'segmentation_accuracy' - - def test_one_class(self): - annotations = make_segmentation_representation(np.array([[0, 0], [0, 0]]), True) - predictions = make_segmentation_representation(np.array([[0, 0], [0, 0]]), False) - dispatcher = MetricsExecutor(create_config(self.name), single_class_dataset()) - dispatcher.update_metrics_on_batch(annotations, predictions) - expected = generate_expected_result(1.0, self.name) - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_multi_class_not_matched(self): - annotations = make_segmentation_representation(np.array([[0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]), True) - predictions = make_segmentation_representation(np.array([[1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]), False) - dispatcher = MetricsExecutor(create_config(self.name), multi_class_dataset()) - dispatcher.update_metrics_on_batch(annotations, predictions) - expected = generate_expected_result(0.0, self.name) - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_multi_class(self): - annotations = make_segmentation_representation(np.array([[1, 0, 3, 0, 0], [0, 0, 0, 0, 0]]), True) - predictions = make_segmentation_representation(np.array([[1, 2, 3, 2, 3], [0, 0, 0, 0, 0]]), False) - dispatcher = MetricsExecutor(create_config(self.name), multi_class_dataset()) - dispatcher.update_metrics_on_batch(annotations, predictions) - expected = generate_expected_result((5.0+1.0+1.0)/(8.0+1.0+1.0), self.name) - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - -class TestMeanAccuracy: - name = 'mean_accuracy' - - def test_one_class(self): - annotations = make_segmentation_representation(np.array([[0, 0], [0, 0]]), True) - predictions = make_segmentation_representation(np.array([[0, 0], [0, 0]]), False) - dataset = single_class_dataset() - dispatcher = MetricsExecutor(create_config(self.name), dataset) - dispatcher.update_metrics_on_batch(annotations, predictions) - expected = generate_expected_result([1.0, 0.0], self.name, dataset.labels) - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_multi_class_not_matched(self): - annotations = make_segmentation_representation(np.array([[1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]), True) - predictions = make_segmentation_representation(np.array([[0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]), False) - dataset = multi_class_dataset() - dispatcher = MetricsExecutor(create_config(self.name), dataset) - dispatcher.update_metrics_on_batch(annotations, predictions) - expected = generate_expected_result([0.0, 0.0, 0.0, 0.0], self.name, dataset.labels) - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_multi_class(self): - dataset = multi_class_dataset() - annotations = make_segmentation_representation(np.array([[1, 2, 3, 2, 3], [0, 0, 0, 0, 0]]), True) - predictions = make_segmentation_representation(np.array([[1, 0, 3, 0, 0], [0, 0, 0, 0, 0]]), False) - dispatcher = MetricsExecutor(create_config(self.name), dataset) - dispatcher.update_metrics_on_batch(annotations, predictions) - expected = generate_expected_result([1.0, 1.0, 0.0, 0.5], self.name, dataset.labels) - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - -class TestMeanIOU: - name = 'mean_iou' - - def test_one_class(self): - annotations = make_segmentation_representation(np.array([[0, 0], [0, 0]]), True) - predictions = make_segmentation_representation(np.array([[0, 0], [0, 0]]), False) - dataset = single_class_dataset() - dispatcher = MetricsExecutor(create_config(self.name), dataset) - dispatcher.update_metrics_on_batch(annotations, predictions) - expected = generate_expected_result([1.0, 0.0], self.name, dataset.labels) - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_multi_class_not_matched(self): - annotations = make_segmentation_representation(np.array([[1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]), True) - predictions = make_segmentation_representation(np.array([[0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]), False) - dataset = multi_class_dataset() - dispatcher = MetricsExecutor(create_config(self.name), dataset) - dispatcher.update_metrics_on_batch(annotations, predictions) - expected = generate_expected_result([0.0, 0.0, 0.0, 0.0], self.name, dataset.labels) - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_multi_class(self): - dataset = multi_class_dataset() - annotations = make_segmentation_representation(np.array([[1, 2, 3, 2, 3], [0, 0, 0, 0, 0]]), True) - predictions = make_segmentation_representation(np.array([[1, 0, 3, 0, 0], [0, 0, 0, 0, 0]]), False) - dispatcher = MetricsExecutor(create_config(self.name), dataset) - dispatcher.update_metrics_on_batch(annotations, predictions) - expected = generate_expected_result([0.625, 1.0, 0.0, 0.5], self.name, dataset.labels) - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - -class TestSegmentationFWAcc: - name = 'frequency_weighted_accuracy' - - def test_one_class(self): - annotations = make_segmentation_representation(np.array([[0, 0], [0, 0]]), True) - predictions = make_segmentation_representation(np.array([[0, 0], [0, 0]]), False) - dataset = single_class_dataset() - dispatcher = MetricsExecutor(create_config(self.name), dataset) - dispatcher.update_metrics_on_batch(annotations, predictions) - expected = generate_expected_result(1.0, self.name) - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_multi_class_not_matched(self): - annotations = make_segmentation_representation(np.array([[1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]), True) - predictions = make_segmentation_representation(np.array([[0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]), False) - dataset = multi_class_dataset() - dispatcher = MetricsExecutor(create_config(self.name), dataset) - dispatcher.update_metrics_on_batch(annotations, predictions) - expected = generate_expected_result(0.0, self.name) - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected - - def test_multi_class(self): - dataset = multi_class_dataset() - annotations = make_segmentation_representation(np.array([[1, 2, 3, 2, 3], [0, 0, 0, 0, 0]]), True) - predictions = make_segmentation_representation(np.array([[1, 0, 3, 0, 0], [0, 0, 0, 0, 0]]), False) - dispatcher = MetricsExecutor(create_config(self.name), dataset) - dispatcher.update_metrics_on_batch(annotations, predictions) - expected = generate_expected_result(0.5125, self.name) - for _, evaluation_result in dispatcher.iterate_metrics(annotations, predictions): - assert evaluation_result == expected diff --git a/tools/accuracy_checker/tests/test_utils.py b/tools/accuracy_checker/tests/test_utils.py deleted file mode 100644 index 4ac9cdff25c9b8..00000000000000 --- a/tools/accuracy_checker/tests/test_utils.py +++ /dev/null @@ -1,127 +0,0 @@ -""" -Copyright (c) 2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from accuracy_checker.utils import concat_lists, contains_all, contains_any, overrides, zipped_transform - - -def test_concat_lists(): - assert ['a', 'b'] == concat_lists(['a'], ['b']) - assert ['a', 'b', 'c'] == concat_lists(['a'], ['b'], ['c']) - assert ['a', 'b', 'c'] == concat_lists(['a', 'b'], ['c']) - assert ['a'] == concat_lists(['a'], []) - assert [] == concat_lists([], [], []) - assert [] == concat_lists([]) - - -def test_contains_all(): - assert contains_all([1, 2, 3], [1, 2]) - assert contains_all([1, 2, 3], [1, 2], [3]) - assert not contains_all([1, 2, 3], [1, 5]) - - -def test_contains_any(): - assert contains_any([1, 2, 3], [1]) - assert contains_any([1, 2, 3], [4, 5, 2]) - assert not contains_any([1, 2, 3], [4, 5]) - - -class TestZippedTransform: - def test_two_iterables(self): - as_ = [2, 3, 5] - bs = [2, 3, 6] - - ras, rbs = zipped_transform(lambda a, b: (a + b, a - b), as_, bs) - - assert ras == [4, 6, 11] - assert rbs == [0, 0, -1] - assert as_ == [2, 3, 5] - assert bs == [2, 3, 6] - - def test_inplace(self): - as_ = [2, 3, 5] - bs = [2, 3, 6] - - zipped_transform(lambda a, b: (a + b, a - b), as_, bs, inplace=True) - - assert as_ == [4, 6, 11] - assert bs == [0, 0, -1] - - def test_three_iterables(self): - as_ = [1, 1, 1] - bs = [2, 2, 2] - cs = [3, 3, 3] - - ras, rbs, rcs = zipped_transform(lambda a, b, c: (a + 1, b + 2, c + 3), as_, bs, cs) - - assert ras == [2, 2, 2] - assert rbs == [4, 4, 4] - assert rcs == [6, 6, 6] - - def test_none_function(self): - xs = [1, 1, 1] - ys = [1, 1, 1] - zipped_transform(lambda a, b: None, xs, ys) - - -class TestOverrides: - def test_negative(self): - class A: - def foo(self): - pass - - class B(A): - pass - - assert not overrides(B, 'foo') - assert not overrides(B(), 'foo') - - def test_positive(self): - class A: - def foo(self): - pass - - class B(A): - def foo(self): - pass - - assert overrides(B, 'foo') - assert overrides(B(), 'foo') - - def test_three_class(self): - class A: - def foo(self): pass - - class B(A): - pass - - class C(B): - def foo(self): pass - - assert overrides(C, 'foo') - assert not overrides(B, 'foo') - - def test_custom_base(self): - class A: - def foo(self): pass - - class B: - def foo(self): pass - - class C: - pass - - assert overrides(B, 'foo', A) - assert not overrides(C, 'foo', A) diff --git a/tools/benchmark/README.md b/tools/benchmark/README.md index 16dcdc0a7545a7..fb427423df7def 100644 --- a/tools/benchmark/README.md +++ b/tools/benchmark/README.md @@ -1,31 +1,157 @@ -# OpenVINOâ„¢ Benchmark Python* package -Inference Engine `openvino.tools.benchmark` Python\* package consists types to measure synchronous mode latency. -The package depends on `openvino.tools.accuracy_checker` the package. +# Benchmark Python* Application -Please, refer to https://docs.openvinotoolkit.org for details. +This topic demonstrates how to run the Benchmark Application demo, which performs inference using convolutional networks. -## Usage -You can use the `openvino.tools.calibration` package in a simple way: -```Python -import openvino.tools.benchmark as benchmark +## How It Works + +Upon start-up, the application reads command-line parameters and loads a network and images/binary files to the Inference Engine +plugin, which is chosen depending on a specified device. The number of infer requests and execution approach depend +on the mode defined with the `-api` command-line parameter. + +> **NOTE**: By default, Inference Engine samples and demos expect input with BGR channels order. If you trained your model to work with RGB order, you need to manually rearrange the default channels order in the sample or demo application or reconvert your model using the Model Optimizer tool with `--reverse_input_channels` argument specified. For more information about the argument, refer to **When to Reverse Input Channels** section of [Converting a Model Using General Conversion Parameters](./docs/MO_DG/prepare_model/convert_model/Converting_Model_General.md). + +### Synchronous API + +For synchronous mode, the primary metric is latency. The application creates one infer request and executes the `Infer` method. A number of executions is defined by one of the two values: +* Number of iterations defined with the `-niter` command-line argument +* Time duration specified with the `-t` command-line argument +* Both of them (execution will continue until both conditions are met) +* Predefined duration if `-niter` and `-t` are not specified. Predefined duration value depends on device. + +During the execution, the application collects two types of metrics: +* Latency for each infer request executed with `Infer` method +* Duration of all executions + +Reported latency value is calculated as mean value of all collected latencies. Reported throughput value is a derivative from reported latency and additionally depends on batch size. + +### Asynchronous API +For asynchronous mode, the primary metric is throughput in frames per second (FPS). The application creates a certain number of infer requests and executes the `StartAsync` method. A number of executions is defined by one of the two values: +* Number of iterations defined with the `-niter` command-line argument +* Time duration specified with the `-t` command-line argument +* Both of them (execution will continue until both conditions are met) +* Predefined duration if `-niter` and `-t` are not specified. Predefined duration value depends on device. + +The infer requests are executed asynchronously. Callback is used to wait for previous execution to complete. The application measures all infer requests executions and reports the throughput metric based on batch size and total execution duration. + +## Running +Notice that the benchmark_app usually produces optimal performance for any device out of the box. + +**So in most cases you don't need to play the app options explicitly and the plain device name is enough**, e.g.: +``` +$benchmark_app -m -i -d CPU +``` + +But it is still may be non-optimal for some cases, especially for very small networks. More details can read in [Introduction to Performance Topics](./docs/IE_DG/Intro_to_Performance.md). + +Running the application with the `-h` or `--help`' option yields the following usage message: + +``` +usage: benchmark_app.py [-h] [-i PATH_TO_INPUT] -m PATH_TO_MODEL + [-d TARGET_DEVICE] + [-l PATH_TO_EXTENSION] [-c PATH_TO_CLDNN_CONFIG] + [-api {sync,async}] [-niter NUMBER_ITERATIONS] + [-b BATCH_SIZE] + [-stream_output [STREAM_OUTPUT]] [-t TIME] + [-progress [PROGRESS]] [-nstreams NUMBER_STREAMS] + [-nthreads NUMBER_THREADS] [-pin {YES,NO}] + [--exec_graph_path EXEC_GRAPH_PATH] + [-pc [PERF_COUNTS]] + +Options: + -h, --help Show this help message and exit. + -i PATH_TO_INPUT, --path_to_input PATH_TO_INPUT + Optional. Path to a folder with images and/or binaries + or to specific image or binary file. + -m PATH_TO_MODEL, --path_to_model PATH_TO_MODEL + Required. Path to an .xml file with a trained model. + -d TARGET_DEVICE, --target_device TARGET_DEVICE + Optional. Specify a target device to infer on: CPU, + GPU, FPGA, HDDL or MYRIAD. + Use "-d HETERO:" format to specify HETERO plugin. + Use "-d MULTI:" format to specify MULTI plugin. + The application looks for a suitable plugin for the specified device. + -l PATH_TO_EXTENSION, --path_to_extension PATH_TO_EXTENSION + Optional. Required for CPU custom layers. Absolute + path to a shared library with the kernels + implementations. + -c PATH_TO_CLDNN_CONFIG, --path_to_cldnn_config PATH_TO_CLDNN_CONFIG + Optional. Required for GPU custom kernels. Absolute + path to an .xml file with the kernels description. + -api {sync,async}, --api_type {sync,async} + Optional. Enable using sync/async API. Default value + is async. + -niter NUMBER_ITERATIONS, --number_iterations NUMBER_ITERATIONS + Optional. Number of iterations. If not specified, the + number of iterations is calculated depending on a + device. + -b BATCH_SIZE, --batch_size BATCH_SIZE + Optional. Batch size value. If not specified, the + batch size value is determined from IR + -stream_output [STREAM_OUTPUT] + Optional. Print progress as a plain text. When + specified, an interactive progress bar is replaced + with a multiline output. + -t TIME, --time TIME Optional. Time in seconds to execute topology. + -progress [PROGRESS] Optional. Show progress bar (can affect performance + measurement). Default values is "False". + -nstreams NUMBER_STREAMS, --number_streams NUMBER_STREAMS + Optional. Number of streams to use for inference on the CPU/GPU in throughput mode + (for HETERO and MULTI device cases use format :,: or just ). + Default value is determined automatically for a device. + Please note that although the automatic selection usually provides a reasonable performance, + it still may be non-optimal for some cases, especially for very small networks. + -nthreads NUMBER_THREADS, --number_threads NUMBER_THREADS + Number of threads to use for inference on the CPU + (including HETERO and MULTI cases). + -pin {YES,NO}, --infer_threads_pinning {YES,NO} + Optional. Enable ("YES" is default value) or disable + ("NO")CPU threads pinning for CPU-involved inference. + --exec_graph_path EXEC_GRAPH_PATH + Optional. Path to a file where to store executable + graph information serialized. + -pc [PERF_COUNTS], --perf_counts [PERF_COUNTS] + Optional. Report performance counters. -config = benchmark.CommandLineReader.read() -result = benchmark.Benchmark(config).run() -print("{0}: {1:.4} ms".format(config.model, result.latency * 1000.0)) ``` -### Explanation -1. Import `openvino.tools.benchmark` types: -```Python -import openvino.tools.benchmark as benchmark + +Running the application with the empty list of options yields the usage message given above and an error message. + +Application supports topologies with one or more inputs. If a topology is not data sensitive, you can skip the input parameter. In this case, inputs are filled with random values. +If a model has only image input(s), please a provide folder with images or a path to an image as input. +If a model has some specific input(s) (not images), please prepare a binary file(s), which is filled with data of appropriate precision and provide a path to them as input. +If a model has mixed input types, input folder should contain all required files. Image inputs are filled with image files one by one. Binary inputs are filled with binary inputs one by one. + +To run the demo, you can use public or pre-trained models. To download the pre-trained models, use the OpenVINO [Model Downloader](https://github.com/opencv/open_model_zoo/tree/2018/model_downloader) or go to [https://download.01.org/opencv/](https://download.01.org/opencv/). + +> **NOTE**: Before running the demo with a trained model, make sure the model is converted to the Inference Engine format (\*.xml + \*.bin) using the [Model Optimizer tool](./docs/MO_DG/Deep_Learning_Model_Optimizer_DevGuide.md). + +For example, to do inference of an image using a trained network with multiple outputs on CPU, run the following command: + ``` +python3 benchmark_app.py -i /inputImage.bmp -m /multiple-output.xml -d CPU +``` + +## Demo Output + +The application outputs number of executed iterations, total duration of execution, latency and throughput. +Additionally, if you set the `-pc` parameter, the application outputs performance counters. +If you set `-exec_graph_path`, the application reports executable graph information serialized. + +``` +[Step 8/9] Measuring performance (Start inference asyncronously, 60000 ms duration, 4 inference requests in parallel using 4 streams) +Progress: |................................| 100.00% + +[Step 9/9] Dumping statistics report +Progress: |................................| 100.00% + +Count: 4408 iterations +Duration: 60153.52 ms +Latency: 51.8244 ms +Throughput: 73.28 FPS -2. Read configuration and execute the benchmark: -```Python -config = benchmark.CommandLineReader.read() -result = benchmark.Benchmark(config).run() ``` -3. Print results: -```Python -print("{0}: {1:.4} ms".format(config.model, result.latency * 1000.0)) -``` \ No newline at end of file +## See Also +* [Using Inference Engine Samples](./docs/IE_DG/Samples_Overview.md) +* [Model Optimizer](./docs/MO_DG/Deep_Learning_Model_Optimizer_DevGuide.md) +* [Model Downloader](https://github.com/opencv/open_model_zoo/tree/2018/model_downloader) diff --git a/tools/benchmark/__init__.py b/tools/benchmark/__init__.py index d5f2cf5fe6f779..e69de29bb2d1d6 100644 --- a/tools/benchmark/__init__.py +++ b/tools/benchmark/__init__.py @@ -1,26 +0,0 @@ -""" -Copyright (C) 2018-2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from .benchmark import Benchmark -from .command_line_reader import CommandLineReader -from .configuration import Configuration - -__version__ = "0.0.1" -__all__ = [ - 'Benchmark', - 'CommandLineReader', - 'Configuration' -] diff --git a/tools/benchmark/__main__.py b/tools/benchmark/__main__.py deleted file mode 100644 index 5beda67db3d549..00000000000000 --- a/tools/benchmark/__main__.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -Copyright (C) 2018-2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import openvino.tools.benchmark as benchmark - - -def benchmark(): - - config = benchmark.CommandLineReader.read() - result = benchmark.Benchmark(config).run() - print("{0}: {1:.4} ms".format(config.model, result.latency * 1000.0)) - - -if __name__ == '__main__': - benchmark() diff --git a/tools/benchmark/benchmark.py b/tools/benchmark/benchmark.py index 52a0b39d9a985d..dc6d5f819e644d 100644 --- a/tools/benchmark/benchmark.py +++ b/tools/benchmark/benchmark.py @@ -1,169 +1,189 @@ """ -Copyright (C) 2018-2019 Intel Corporation + Copyright (C) 2018-2019 Intel Corporation -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. """ +from datetime import datetime +from statistics import median +from openvino.inference_engine import IENetwork, IECore, get_version -import numpy -import datetime +from .utils.constants import CPU_DEVICE_NAME, MULTI_DEVICE_NAME, GPU_DEVICE_NAME, MYRIAD_DEVICE_NAME +from .utils.logging import logger +from .utils.utils import get_duration_seconds, parse_value_per_device, parse_devices -import openvino.inference_engine as ie -from ..accuracy_checker.accuracy_checker.config import ConfigReader -from ..accuracy_checker.accuracy_checker.evaluators.model_evaluator import ModelEvaluator -from ..accuracy_checker.accuracy_checker.progress_reporters import PrintProgressReporter, TQDMReporter -from ..network import Network - -from .configuration import Configuration -from .logging import info - - -class BenchmarkCallback: - def __init__(self, configuration: Configuration, network: Network=None, iterations_count:int=1000): - self._latency = None - self._configuration = configuration - self._network = network - self._iterations_count = iterations_count if iterations_count else 1000 - - def output_callback(self, value, latency = None): - pass - - - def benchmark_callback(self, network_inputs_data): - latencies = list() - - if self._network: - ie_network = self._network.ie_network - else: - ie_network = ie.IENetwork(self._configuration.model, self._configuration.weights) - - do_reshape = False - for name in ie_network.inputs.keys(): - if name in network_inputs_data and \ - tuple(ie_network.inputs[name].shape) != network_inputs_data[name].shape: - do_reshape = True - break - - if do_reshape: - new_shapes = {layer_name: data.shape for layer_name, data in network_inputs_data.items()} +class Benchmark: + def __init__(self, device: str, number_infer_requests, number_iterations, duration_seconds, api_type): + self.device = device.upper() + self.ie = IECore() + self.nireq = number_infer_requests + self.niter = number_iterations + self.duration_seconds = get_duration_seconds(duration_seconds, self.niter, self.device) + self.api_type = api_type + self.device_number_streams = {} + + def __del__(self): + del self.ie + + def add_extension(self, path_to_extension: str=None, path_to_cldnn_config: str=None): + if GPU_DEVICE_NAME in self.device: + if path_to_cldnn_config: + self.ie.set_config({'CONFIG_FILE': path_to_cldnn_config}, GPU_DEVICE_NAME) + logger.info('GPU extensions is loaded {}'.format(path_to_cldnn_config)) + if CPU_DEVICE_NAME in self.device or MYRIAD_DEVICE_NAME in self.device: + if path_to_extension: + self.ie.add_extension(extension_path=path_to_extension, device_name=CPU_DEVICE_NAME) + logger.info('CPU extensions is loaded {}'.format(path_to_extension)) + + def get_version_info(self) -> str: + logger.info('InferenceEngine:\n{: <9}{:.<24} {}'.format('', 'API version', get_version())) + version_string = 'Device info\n' + for device, version in self.ie.get_versions(self.device).items(): + version_string += '{: <9}{}\n'.format('', device) + version_string += '{: <9}{:.<24}{} {}.{}\n'.format('', version.description, ' version', version.major, + version.minor) + version_string += '{: <9}{:.<24} {}\n'.format('', 'Build', version.build_number) + return version_string + + @staticmethod + def reshape(ie_network: IENetwork, batch_size: int): + new_shapes = {} + for input_layer_name, input_layer in ie_network.inputs.items(): + shape = input_layer.shape + layout = input_layer.layout + + try: + batch_index = layout.index('N') + except ValueError: + batch_index = 1 if layout == 'C' else -1 + + if batch_index != -1 and shape[batch_index] != batch_size: + shape[batch_index] = batch_size + new_shapes[input_layer_name] = shape + + if new_shapes: + logger.info('Resizing network to batch = {}'.format(batch_size)) ie_network.reshape(new_shapes) - plugin = ie.IEPlugin(self._configuration.device) - if self._configuration.cpu_extension: - plugin.add_cpu_extension(self._configuration.cpu_extension) - exec_network = plugin.load(ie_network) - - # warming up - exec_network.infer(network_inputs_data) - - for i in range(self._iterations_count): - start = datetime.datetime.now() - exec_network.infer(network_inputs_data) - latencies.append((datetime.datetime.now() - start).microseconds) - self._latency = numpy.mean(latencies) / 1000000.0 - - del ie_network - del exec_network - del plugin - - - @property - def latency(self) -> float: - return self._latency - - -class BenchmarkResult: - def __init__(self, latency): - self._latency = latency - - @property - def latency(self) -> float: - return self._latency - - -class InferOptions: - def __init__(self, iterations_count=1000): - self._iterations_count = iterations_count - - @property - def iterations_count(self) -> int: - return self._iterations_count - - -class Benchmark: - def __init__(self, configuration: Configuration): - if configuration is None: - raise ValueError("configuration is None") - - self._configuration = configuration - pass - - def run( - self, - network: Network = None, - statistics=None, - quantization_levels=None, - iterations_count:int = 1000) -> BenchmarkResult: - - model = self._configuration.config['models'][0] - launcher_config = model['launchers'][0] - dataset_config = model['datasets'][0] - - model_evaluator = ModelEvaluator.from_configs(launcher_config, dataset_config) - try: - if network: - del model_evaluator.launcher.network - del model_evaluator.launcher.exec_network - model_evaluator.launcher.network = network.ie_network - model_evaluator.launcher.exec_network = model_evaluator.launcher.plugin.load(network.ie_network) - - ie_network = model_evaluator.launcher.network - - if statistics: - network_stats = {} - for layer_name, node_statistic in statistics.items(): - network_stats[layer_name] = ie.LayerStats( - min=tuple(node_statistic.min_outputs), - max=tuple(node_statistic.max_outputs)) - ie_network.stats.update(network_stats) - - if quantization_levels: - for layer_name, value in quantization_levels.items(): - params = ie_network.layers[layer_name].params - params["quantization_level"] = value - ie_network.layers[layer_name].params = params - - if model_evaluator.dataset.size != 1: - info("only one first image is used from dataset annotation to perform benchmark") - model_evaluator.dataset.size = 1 - - process_dataset_callback = BenchmarkCallback( - configuration=self._configuration, - network=network, - iterations_count=iterations_count) - - model_evaluator.process_dataset( - None, - progress_reporter=None, - output_callback=process_dataset_callback.output_callback, - benchmark=process_dataset_callback.benchmark_callback) - - if len(model_evaluator.launcher.exec_network.requests) != 1: - raise ValueError("unexpected network requests count") - - latency = process_dataset_callback.latency - finally: - model_evaluator.release() - - return BenchmarkResult(latency) + def set_config(self, number_streams: int, api_type: str = 'async', + number_threads: int = None, infer_threads_pinning: int = None): + devices = parse_devices(self.device) + self.device_number_streams = parse_value_per_device(devices, number_streams) + for device in devices: + if device == CPU_DEVICE_NAME: # CPU supports few special performance-oriented keys + # limit threading for CPU portion of inference + if number_threads: + self.ie.set_config({'CPU_THREADS_NUM': str(number_threads)}, device) + + if MULTI_DEVICE_NAME in self.device and GPU_DEVICE_NAME in self.device: + self.ie.set_config({'CPU_BIND_THREAD': 'NO'}, CPU_DEVICE_NAME) + else: + # pin threads for CPU portion of inference + self.ie.set_config({'CPU_BIND_THREAD': infer_threads_pinning}, device) + + # for CPU execution, more throughput-oriented execution via streams + # for pure CPU execution, more throughput-oriented execution via streams + if api_type == 'async': + cpu_throughput = {'CPU_THROUGHPUT_STREAMS': 'CPU_THROUGHPUT_AUTO'} + if device in self.device_number_streams.keys(): + cpu_throughput['CPU_THROUGHPUT_STREAMS'] = str(self.device_number_streams.get(device)) + self.ie.set_config(cpu_throughput, device) + self.device_number_streams[device] = self.ie.get_config(device, 'CPU_THROUGHPUT_STREAMS') + + elif device == GPU_DEVICE_NAME: + if api_type == 'async': + gpu_throughput = {'GPU_THROUGHPUT_STREAMS': 'GPU_THROUGHPUT_AUTO'} + if device in self.device_number_streams.keys(): + gpu_throughput['GPU_THROUGHPUT_STREAMS'] = str(self.device_number_streams.get(device)) + self.ie.set_config(gpu_throughput, device) + self.device_number_streams[device] = self.ie.get_config(device, 'GPU_THROUGHPUT_STREAMS') + + if MULTI_DEVICE_NAME in self.device and CPU_DEVICE_NAME in self.device: + # multi-device execution with the CPU+GPU performs best with GPU trottling hint, + # which releases another CPU thread (that is otherwise used by the GPU driver for active polling) + self.ie.set_config({'CLDNN_PLUGIN_THROTTLE': '1'}, device) + + elif device == MYRIAD_DEVICE_NAME: + self.ie.set_config({'LOG_LEVEL': 'LOG_INFO', + 'VPU_LOG_LEVEL': 'LOG_WARNING'}, MYRIAD_DEVICE_NAME) + + def load_network(self, ie_network: IENetwork, perf_counts: bool, number_infer_requests: int = None): + config = {'PERF_COUNT': ('YES' if perf_counts else 'NO')} + + exe_network = self.ie.load_network(ie_network, + self.device, + config=config, + num_requests=number_infer_requests or 0) + + return exe_network + + def infer(self, request_queue, requests_input_data, batch_size, progress_bar): + progress_count = 0 + # warming up - out of scope + infer_request = request_queue.get_idle_request() + if not infer_request: + raise Exception('No idle Infer Requests!') + + if self.api_type == 'sync': + infer_request.infer(requests_input_data[infer_request.req_id]) + else: + infer_request.start_async(requests_input_data[infer_request.req_id]) + + request_queue.wait_all() + request_queue.reset_times() + + start_time = datetime.now() + exec_time = (datetime.now() - start_time).total_seconds() + iteration = 0 + + # Start inference & calculate performance + # to align number if iterations to guarantee that last infer requests are executed in the same conditions **/ + while (self.niter and iteration < self.niter) or \ + (self.duration_seconds and exec_time < self.duration_seconds) or \ + (self.api_type == 'async' and iteration % self.nireq): + infer_request = request_queue.get_idle_request() + if not infer_request: + raise Exception('No idle Infer Requests!') + + if self.api_type == 'sync': + infer_request.infer(requests_input_data[infer_request.req_id]) + else: + infer_request.start_async(requests_input_data[infer_request.req_id]) + iteration += 1 + + exec_time = (datetime.now() - start_time).total_seconds() + + if self.duration_seconds: + # calculate how many progress intervals are covered by current iteration. + # depends on the current iteration time and time of each progress interval. + # Previously covered progress intervals must be skipped. + progress_interval_time = self.duration_seconds / progress_bar.total_num + new_progress = int(exec_time / progress_interval_time - progress_count) + progress_bar.add_progress(new_progress) + progress_count += new_progress + elif self.niter: + progress_bar.add_progress(1) + + # wait the latest inference executions + request_queue.wait_all() + + total_duration_sec = request_queue.get_duration_in_seconds() + times = request_queue.times + times.sort() + latency_ms = median(times) + fps = batch_size * 1000 / latency_ms + if self.api_type == 'async': + fps = batch_size * iteration / total_duration_sec + progress_bar.finish() + return fps, latency_ms, total_duration_sec, iteration diff --git a/tools/benchmark/command_line_reader.py b/tools/benchmark/command_line_reader.py deleted file mode 100644 index 4599b286793096..00000000000000 --- a/tools/benchmark/command_line_reader.py +++ /dev/null @@ -1,155 +0,0 @@ -""" -Copyright (C) 2018-2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import os -import collections -import errno -import pathlib -from functools import partial -from argparse import ArgumentParser -from typing import Union - -from ..accuracy_checker.accuracy_checker.config import ConfigReader -from ..accuracy_checker.accuracy_checker.utils import get_path -from ..network import Network - -from .configuration import Configuration -from .logging import info - - -class CommandLineReader: - """ - Class for parsing input config - """ - @staticmethod - def read(): - args, unknown_args = CommandLineReader.__build_arguments_parser().parse_known_args() - if unknown_args: - info("unknown command line arguments: {0}".format(unknown_args)) - - args.target_framework = "dlsdk" - args.aocl = None - - merged_config = ConfigReader.merge(args) - launcher = merged_config['models'][0]['launchers'][0] - - batch_size = args.batch_size if args.batch_size else (launcher['batch'] if 'batch' in launcher else None) - if not batch_size: - with Network(str(launcher['model']), str(launcher['weights'])) as network: - batch_size = network.ie_network.batch_size - - return Configuration( - config = merged_config, - model = str(launcher['model']), - weights = str(launcher['weights']), - cpu_extension = (str(launcher['cpu_extensions']) if 'cpu_extensions' in launcher else None), - gpu_extension = (str(launcher['gpu_extensions']) if 'gpu_extensions' in launcher else None), - device = launcher['device'], - benchmark_iterations_count = args.benchmark_iterations_count) - - @staticmethod - def __build_arguments_parser(): - parser = ArgumentParser(description='openvino.tools.benchmark') - - parser.add_argument( - '-d', '--definitions', - help='Optional. Path to the YML file with definitions', - type=str, - required=False) - - parser.add_argument( - '-c', - '--config', - help='Required. Path to the YML file with local configuration', - type=get_path, - required=True) - - parser.add_argument( - '-m', '--models', - help='Optional. Prefix path to the models and weights', - type=partial(get_path, is_directory=True), - default=pathlib.Path.cwd(), - required=False) - - parser.add_argument( - '-s', '--source', - help='Optional. prefix path to the data source', - type=partial(get_path, is_directory=True), - default=pathlib.Path.cwd(), - required=False) - - parser.add_argument( - '-a', '--annotations', - help='Optional. prefix path to the converted annotations and datasets meta data', - type=partial(get_path, is_directory=True), - default=pathlib.Path.cwd(), - required=False) - - parser.add_argument( - '-e', '--extensions', - help='Optional. Prefix path to extensions folder', - type=partial(get_path, is_directory=True), - default=pathlib.Path.cwd(), - required=False) - - parser.add_argument( - '--cpu_extensions_mode', - help='Optional. specified preferable set of processor instruction for automatic searching cpu extension lib', - required=False, - choices=['avx2', 'sse4']) - - parser.add_argument( - '-b', '--bitstreams', - help='Optional. prefix path to bitstreams folder', - type=partial(get_path, is_directory=True), - default=pathlib.Path.cwd(), - required=False) - - parser.add_argument( - '-C', '--converted_models', '--converted-models', - help='Optional. directory to store Model Optimizer converted models. Used for DLSDK launcher only', - type=partial(get_path, is_directory=True), - default=pathlib.Path.cwd(), - required=False) - - parser.add_argument( - '-td', '--target_devices', '--target-devices', - help='Optional. Space-separated list of devices for infer', - required=False, - nargs='+', - default=["CPU"]) - - parser.add_argument( - '-tt', '--target_tags', '--target-tags', - help='Optional. Space-separated list of launcher tags for infer', - required=False, - nargs='+') - - parser.add_argument( - '--batch-size', - help='Optional. Batch size value. If not specified, the batch size value is determined from IR', - type=int, - required=False) - - parser.add_argument( - '-ic', - '--benchmark_iterations_count', - help='Optional. Benchmark itertations count. (1000 is default)', - type=float, - required=False, - default=1000) - - return parser \ No newline at end of file diff --git a/tools/benchmark/configuration.py b/tools/benchmark/configuration.py deleted file mode 100644 index af3d6dc9c7b462..00000000000000 --- a/tools/benchmark/configuration.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -Copyright (C) 2018-2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - - -class Configuration: - def __init__( - self, - config: str, - model: str, - weights: str, - device: str, - cpu_extension: str, - gpu_extension: str, - benchmark_iterations_count: int - ): - - self._config = config - self._model = model - self._weights = weights - self._device = device - self._cpu_extension = cpu_extension - self._gpu_extension = gpu_extension - self._benchmark_iterations_count = benchmark_iterations_count - - @property - def config(self) -> str: - return self._config - - @property - def model(self) -> str: - return self._model - - @property - def weights(self) -> str: - return self._weights - - @property - def device(self) -> str: - return self._device - - @property - def cpu_extension(self) -> str: - return self._cpu_extension - - @property - def gpu_extension(self) -> str: - return self._gpu_extension - - @property - def benchmark_iterations_count(self): - return self._benchmark_iterations_count \ No newline at end of file diff --git a/tools/benchmark/logging.py b/tools/benchmark/logging.py deleted file mode 100644 index f3fec905f16ace..00000000000000 --- a/tools/benchmark/logging.py +++ /dev/null @@ -1,125 +0,0 @@ -""" -Copyright (C) 2018-2019 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import logging -import logging.config -import sys -import warnings - -# TODO: move to utils -_DEFAULT_LOGGER_NAME = 'openvino.tools.benchmark' -_DEFAULT_LOG_FILE = 'openvino.tools.benchmark.log' - -PRINT_INFO = logging.INFO + 5 -logging.addLevelName(PRINT_INFO, "PRINT_INFO") - -_LOG_LEVEL_ENVIRON = "CALIBRATION_TOOL_LOG_LEVEL" -# _LOGGING_LEVEL = logging.getLevelName(os.environ.get(_LOG_LEVEL_ENVIRON, PRINT_INFO)) -# TODO: refactoring: remove, use original line -_LOGGING_LEVEL = "DEBUG" - - -class LoggingFormatter(logging.Formatter): - def format(self, record: logging.LogRecord): - if record.levelno == PRINT_INFO: - return record.msg - return super().format(record) - - -class ConsoleHandler(logging.StreamHandler): - def __init__(self, default_stream=sys.stdout): - super().__init__(default_stream) - self.default_stream = default_stream - self.err_stream = sys.stderr - - def emit(self, record): - if record.levelno >= logging.WARNING: - self.stream = self.err_stream - else: - self.stream = self.default_stream - super().emit(record) - - -_LOGGING_CONFIGURATION = { - 'version': 1, - 'disable_existing_loggers': False, - 'formatters': { - 'default': { - '()': LoggingFormatter, - 'format': '%(asctime)s %(name)s %(levelname)s: %(message)s', - 'datefmt': '%H:%M:%S' - }, - 'detailed': { - 'format': '%(asctime)s %(name)s %(levelname)s: %(message)s' - } - }, - 'handlers': { - 'console': { - 'level': 'DEBUG', - '()': ConsoleHandler, - 'formatter': 'default', - } - }, - - 'loggers': { - _DEFAULT_LOGGER_NAME: { - 'handlers': ['console'], - 'level': _LOGGING_LEVEL, - 'propagate': False - } - } -} - -logging.config.dictConfig(_LOGGING_CONFIGURATION) - -_default_logger = logging.getLogger(_DEFAULT_LOGGER_NAME) - - -def _warning_handler(message, category, filename, lineno): - s = warnings.formatwarning(message, category, filename, lineno) - _default_logger.warning(s) - - -warnings.showwarning = _warning_handler - - -def get_logger(logger_name: str): - if logger_name.startswith(_DEFAULT_LOGGER_NAME): - return _default_logger.getChild(logger_name) - return logging.getLogger(logger_name) - - -def error(msg, *args, **kwargs): - _default_logger.error(msg, *args, **kwargs) - - -def warning(msg, *args, raise_warning=True, **kwargs): - if raise_warning: - warnings.warn(msg) - else: - _default_logger.warning(msg, *args, **kwargs) - - -def info(msg, *args, **kwargs): - _default_logger.info(msg, *args, **kwargs) - - -def debug(msg, *args, **kwargs): - _default_logger.debug(msg, *args, **kwargs) - - -def print_info(msg, *args, **kwargs): - _default_logger.log(PRINT_INFO, msg, *args, **kwargs) diff --git a/tools/benchmark/requirements.txt b/tools/benchmark/requirements.txt index 5e3e8ee14480a9..7042cb2a066171 100644 --- a/tools/benchmark/requirements.txt +++ b/tools/benchmark/requirements.txt @@ -1,8 +1,4 @@ py-cpuinfo numpy progress -pyyaml -opencv-python -shapely -sklearn -xmltodict +opencv-python \ No newline at end of file diff --git a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/__init__.py b/tools/benchmark/utils/__init__.py similarity index 100% rename from inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/__init__.py rename to tools/benchmark/utils/__init__.py diff --git a/tools/benchmark/utils/constants.py b/tools/benchmark/utils/constants.py new file mode 100644 index 00000000000000..8ad915bccd1f55 --- /dev/null +++ b/tools/benchmark/utils/constants.py @@ -0,0 +1,53 @@ +""" + Copyright (C) 2018-2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an 'AS IS' BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +VPU_DEVICE_NAME = 'VPU' +MYRIAD_DEVICE_NAME = 'MYRIAD' +HDDL_DEVICE_NAME = 'HDDL' +FPGA_DEVICE_NAME = 'FPGA' +CPU_DEVICE_NAME = 'CPU' +GPU_DEVICE_NAME = 'GPU' +HETERO_DEVICE_NAME = 'HETERO' +MULTI_DEVICE_NAME = 'MULTI' +UNKNOWN_DEVICE_TYPE = 'UNKNOWN' + +XML_EXTENSION = '.xml' +BIN_EXTENSION = '.bin' + +XML_EXTENSION_PATTERN = '*' + XML_EXTENSION + +IMAGE_EXTENSIONS = ['JPEG', 'JPG', 'PNG', 'BMP'] +BINARY_EXTENSIONS = ['BIN'] + +DEVICE_DURATION_IN_SECS = { + CPU_DEVICE_NAME: 60, + GPU_DEVICE_NAME: 60, + VPU_DEVICE_NAME: 60, + MYRIAD_DEVICE_NAME: 60, + HDDL_DEVICE_NAME: 60, + FPGA_DEVICE_NAME: 120, + UNKNOWN_DEVICE_TYPE: 120 +} + +DEVICE_NIREQ_ASYNC = { + CPU_DEVICE_NAME: 2, + GPU_DEVICE_NAME: 2, + VPU_DEVICE_NAME: 4, + MYRIAD_DEVICE_NAME: 4, + HDDL_DEVICE_NAME: 100, + FPGA_DEVICE_NAME: 3, + UNKNOWN_DEVICE_TYPE: 1 +} diff --git a/tools/benchmark/utils/infer_request_wrap.py b/tools/benchmark/utils/infer_request_wrap.py new file mode 100644 index 00000000000000..37a757def46b33 --- /dev/null +++ b/tools/benchmark/utils/infer_request_wrap.py @@ -0,0 +1,82 @@ +""" + Copyright (C) 2018-2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from datetime import datetime +import threading + + +class InferReqWrap: + def __init__(self, request, req_id, callback_queue): + self.req_id = req_id + self.request = request + self.request.set_completion_callback(self.callback, self.req_id) + self.callbackQueue = callback_queue + + def callback(self, status_code, user_data): + if user_data != self.req_id: + print('Request ID {} does not correspond to user data {}'.format(self.req_id, user_data)) + elif status_code: + print('Request {} failed with status code {}'.format(self.req_id, status_code)) + self.callbackQueue(self.req_id, self.request.latency) + + def start_async(self, input_data): + self.request.async_infer(input_data) + + def infer(self, input_data): + self.request.infer(input_data) + self.callbackQueue(self.req_id, self.request.latency) + + +class InferRequestsQueue: + def __init__(self, requests): + self.idleIds = [] + self.requests = [] + self.times = [] + for req_id in range(len(requests)): + self.requests.append(InferReqWrap(requests[req_id], req_id, self.put_idle_request)) + self.idleIds.append(req_id) + self.startTime = datetime.max + self.endTime = datetime.min + self.cv = threading.Condition() + + def reset_times(self): + self.times.clear() + + def get_duration_in_seconds(self): + return (self.endTime - self.startTime).total_seconds() + + def put_idle_request(self, req_id, latency): + self.cv.acquire() + self.times.append(latency) + self.idleIds.append(req_id) + self.endTime = max(self.endTime, datetime.now()) + self.cv.notify() + self.cv.release() + + def get_idle_request(self): + self.cv.acquire() + while len(self.idleIds) == 0: + self.cv.wait() + req_id = self.idleIds.pop() + self.startTime = min(datetime.now(), self.startTime) + self.cv.release() + return self.requests[req_id] + + def wait_all(self): + self.cv.acquire() + while len(self.idleIds) != len(self.requests): + self.cv.wait() + self.cv.release() diff --git a/tools/benchmark/utils/inputs_filling.py b/tools/benchmark/utils/inputs_filling.py new file mode 100644 index 00000000000000..8dcbee369853fd --- /dev/null +++ b/tools/benchmark/utils/inputs_filling.py @@ -0,0 +1,189 @@ +""" + Copyright (C) 2018-2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import os +import cv2 +import numpy as np + +from glob import glob + +from .constants import IMAGE_EXTENSIONS, BINARY_EXTENSIONS +from .logging import logger + + +def is_image(blob): + if blob.layout != "NCHW": + return False + channels = blob.shape[1] + return channels == 3 + + +def is_image_info(blob): + if blob.layout != "NC": + return False + channels = blob.shape[1] + return channels >= 2 + + +def get_inputs(path_to_input, batch_size, input_info, requests): + input_image_sizes = {} + for key in input_info.keys(): + if is_image(input_info[key]): + input_image_sizes[key] = (input_info[key].shape[2], input_info[key].shape[3]) + logger.info("Network input '{}' precision {}, dimensions ({}): {}".format(key, + input_info[key].precision, + input_info[key].layout, + " ".join(str(x) for x in + input_info[key].shape))) + + images_count = len(input_image_sizes.keys()) + binaries_count = len(input_info) - images_count + + image_files = list() + binary_files = list() + + if path_to_input: + image_files = get_files_by_extensions(path_to_input, IMAGE_EXTENSIONS) + image_files.sort() + binary_files = get_files_by_extensions(path_to_input, BINARY_EXTENSIONS) + binary_files.sort() + + if (len(image_files) == 0) and (len(binary_files) == 0): + logger.warn("No input files were given: all inputs will be filled with random values!") + else: + binary_to_be_used = binaries_count * batch_size * len(requests) + if binary_to_be_used > 0 and len(binary_files) == 0: + logger.warn("No supported binary inputs found! Please check your file extensions: {}".format( + ",".join(BINARY_EXTENSIONS))) + elif binary_to_be_used > len(binary_files): + logger.warn( + "Some binary input files will be duplicated: {} files are required, but only {} were provided".format( + binary_to_be_used, len(binary_files))) + elif binary_to_be_used < len(binary_files): + logger.warn( + "Some binary input files will be ignored: only {} files are required from {}".format(binary_to_be_used, + len(binary_files))) + + images_to_be_used = images_count * batch_size * len(requests) + if images_to_be_used > 0 and len(image_files) == 0: + logger.warn("No supported image inputs found! Please check your file extensions: {}".format( + ",".join(IMAGE_EXTENSIONS))) + elif images_to_be_used > len(image_files): + logger.warn( + "Some image input files will be duplicated: {} files are required, but only {} were provided".format( + images_to_be_used, len(image_files))) + elif images_to_be_used < len(image_files): + logger.warn( + "Some image input files will be ignored: only {} files are required from {}".format(images_to_be_used, + len(image_files))) + + requests_input_data = [] + for request_id in range(0, len(requests)): + logger.info("Infer Request {} filling".format(request_id)) + input_data = {} + keys = list(input_info.keys()) + for key in keys: + if is_image(input_info[key]): + # input is image + if (len(image_files) > 0): + input_data[key] = fill_blob_with_image(image_files, request_id, batch_size, keys.index(key), + len(keys), input_info[key].shape) + continue + + # input is binary + if (len(binary_files) > 0): + input_data[key] = fill_blob_with_binary(binary_files, input_info[key].shape) + continue + + # most likely input is image info + if is_image_info(input_info[key]) and len(input_image_sizes) == 1: + image_size = input_image_sizes[list(input_image_sizes.keys()).pop()] + logger.info("Fill input '" + key + "' with image size " + str(image_size[0]) + "x" + + str(image_size[1])) + input_data[key] = fill_blob_with_image_info(image_size, input_info[key].shape) + continue + + # fill with random data + logger.info("Fill input '{}' with random values ({} is expected)".format(key, "image" if is_image( + input_info[key]) else "some binary data")) + input_data[key] = fill_blob_with_random(input_info[key].precision, input_info[key].shape) + + requests_input_data.append(input_data) + + return requests_input_data + + +def get_files_by_extensions(path_to_input, extensions): + input_files = list() + if os.path.isfile(path_to_input): + input_files.append(path_to_input) + else: + path = os.path.join(path_to_input, '*') + files = glob(path, recursive=True) + for file in files: + file_extension = file.rsplit('.').pop().upper() + if file_extension in extensions: + input_files.append(file) + return input_files + + +def fill_blob_with_image(image_paths, request_id, batch_size, input_id, input_size, shape): + images = np.ndarray(shape) + image_index = request_id * batch_size * input_size + input_id + for b in range(batch_size): + image_index %= len(image_paths) + image_filename = image_paths[image_index] + logger.info('Prepare image {}'.format(image_filename)) + image = cv2.imread(image_filename) + + new_im_size = tuple(shape[2:]) + if image.shape[:-1] != new_im_size: + logger.warn("Image is resized from ({}) to ({})".format(image.shape[:-1], new_im_size)) + image = cv2.resize(image, new_im_size) + + image = image.transpose((2, 1, 0)) + images[b] = image + + image_index += input_size + return images + + +def fill_blob_with_image_info(image_size, shape): + im_info = np.ndarray(shape) + for b in range(shape[0]): + for i in range(shape[1]): + im_info[b][i] = image_size[i] if i in [0, 1] else 1 + + return im_info + + +def fill_blob_with_random(precision, shape): + if precision == "FP32": + return np.random.rand(*shape).astype(np.float32) + elif precision == "FP16": + return np.random.rand(*shape).astype(np.float16) + elif precision == "I32": + return np.random.rand(*shape).astype(np.int32) + elif precision == "U8": + return np.random.rand(*shape).astype(np.uint8) + elif precision == "I8": + return np.random.rand(*shape).astype(np.int8) + elif precision == "U16": + return np.random.rand(*shape).astype(np.uint16) + elif precision == "I16": + return np.random.rand(*shape).astype(np.int16) + else: + raise Exception("Input precision is not supported: " + precision) diff --git a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/logging.py b/tools/benchmark/utils/logging.py similarity index 100% rename from inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/logging.py rename to tools/benchmark/utils/logging.py diff --git a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/progress_bar.py b/tools/benchmark/utils/progress_bar.py similarity index 57% rename from inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/progress_bar.py rename to tools/benchmark/utils/progress_bar.py index f281d1f0e2f226..1f44efc8e7df9e 100644 --- a/inference-engine/ie_bridges/python/sample/benchmark_app/benchmark/utils/progress_bar.py +++ b/tools/benchmark/utils/progress_bar.py @@ -16,23 +16,37 @@ from progress.bar import Bar + class ProgressBar: def __init__(self, total_num, stream_output=False, progress_enabled=False): self.stream_output = stream_output self.is_finished = True self.progress_enabled = progress_enabled + self.percent_to_update = 1 + self.cur_progress = 0 + self.total_num = total_num self.reset(total_num) def add_progress(self, num): self.is_finished = False if self.progress_enabled: - for i in range(num): - self.bar.next() - if self.stream_output: - print() + self.cur_progress += num + total_progress = self.bar.max + if self.cur_progress > total_progress: + self.cur_progress = total_progress + + prev_progress = self.bar.index + prev_percent = 100 * prev_progress / total_progress + cur_percent = 100 * self.cur_progress / total_progress + if prev_progress == 0 or \ + self.cur_progress == total_progress or \ + prev_percent + self.percent_to_update <= cur_percent: + self.bar.next(self.cur_progress - self.bar.index) + if self.stream_output: + print() - def finish(self, num = 0): - if (num > 0): + def finish(self, num=0): + if num: self.add_progress(num) self.is_finished = True @@ -42,10 +56,10 @@ def finish(self, num = 0): def reset(self, total_num): if self.progress_enabled: - self.bar = Bar('Progress:', max = total_num, fill = '.', suffix='%(percent).2f%%') + self.bar = Bar('Progress:', max=total_num, fill='.', suffix='%(percent).d%%') def new_bar(self, total_num): if self.is_finished: self.reset(total_num) else: - raise Exception("Cannot create a new bar. Current bar is still in progress") + raise Exception('Cannot create a new bar. Current bar is still in progress') diff --git a/tools/benchmark/utils/statistics_report.py b/tools/benchmark/utils/statistics_report.py new file mode 100644 index 00000000000000..daa0490ea2a144 --- /dev/null +++ b/tools/benchmark/utils/statistics_report.py @@ -0,0 +1,119 @@ +""" + Copyright (C) 2018-2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import os +import sys +from enum import Enum + +from .logging import logger + +## statistics reports types +noCntReport = 'no_counters' +averageCntReport = 'average_counters' +detailedCntReport = 'detailed_counters' + +## Responsible for collecting of statistics and dumping to .csv file +class StatisticsReport: + class Config(): + def __init__(self, report_type, report_folder): + self.report_type = report_type + self.report_folder = report_folder + + class Category(Enum): + COMMAND_LINE_PARAMETERS = 0, + RUNTIME_CONFIG = 1, + EXECUTION_RESULTS = 2 + + def __init__(self, config): + self.config = config + self.parameters = {} + self.csv_separator = ';' + + def add_parameters(self, category, parameters): + if category not in self.parameters.keys(): + self.parameters[category] = parameters + else: + self.parameters[category].extend(parameters) + + def dump(self): + def dump_parameters(f, parameters): + for k, v in parameters: + f.write('{}{}{}\n'.format(k, self.csv_separator, v)) + + with open(os.path.join(self.config.report_folder, 'benchmark_report.csv'), 'w') as f: + if self.Category.COMMAND_LINE_PARAMETERS in self.parameters.keys(): + f.write('Command line parameters\n') + dump_parameters(f, self.parameters[self.Category.COMMAND_LINE_PARAMETERS]) + f.write('\n') + + if self.Category.RUNTIME_CONFIG in self.parameters.keys(): + f.write('Configuration setup\n') + dump_parameters(f, self.parameters[self.Category.RUNTIME_CONFIG]) + f.write('\n') + + if self.Category.EXECUTION_RESULTS in self.parameters.keys(): + f.write('Execution results\n') + dump_parameters(f, self.parameters[self.Category.EXECUTION_RESULTS]) + f.write('\n') + + logger.info("Statistics report is stored to {}".format(f.name)) + + def dump_performance_counters_request(self, f, perf_counts): + total = 0 + total_cpu = 0 + f.write(self.csv_separator.join(['layerName', 'execStatus', 'layerType', 'execType', 'realTime (ms)', 'cpuTime (ms)\n'])) + for k, v in sorted(perf_counts.items(), key=lambda x: x[1]['execution_index']): + f.write(self.csv_separator.join([k, v['status'], v['layer_type'], v['exec_type'], str(v['real_time']/1000.0), str(v['cpu_time']/1000.0)])) + f.write('\n') + total += v['real_time'] + total_cpu += v['cpu_time'] + f.write(self.csv_separator.join(['Total','','','',str(total/1000.0),str(total_cpu/1000.0)])) + f.write('\n\n') + + def dump_performance_counters(self, perf_counts): + if self.config.report_type == '' or self.config.report_type == noCntReport: + logger.info("Statistics collecting for performance counters was not requested. No reports are dumped.") + return + + if not perf_counts: + logger.info('Peformance counters are empty. No reports are dumped.') + return + + filename = os.path.join(self.config.report_folder, 'benchmark_{}_report.csv'.format(self.config.report_type)) + with open(filename, 'w') as f: + if self.config.report_type == detailedCntReport: + for pc in perf_counts: + self.dump_performance_counters_request(f, pc) + elif self.config.report_type == averageCntReport: + def get_average_performance_counters(perf_counts): + performance_counters_avg = {} + ## iterate over each processed infer request and handle its PM data + for i in range(0, len(perf_counts)): + ## iterate over each layer from sorted vector and add required PM data to the per-layer maps + for k in perf_counts[0].keys(): + if k not in performance_counters_avg.keys(): + performance_counters_avg[k] = perf_counts[i][k] + else: + performance_counters_avg[k]['real_time'] += perf_counts[i][k]['real_time'] + performance_counters_avg[k]['cpu_time'] += perf_counts[i][k]['cpu_time'] + for _, v in performance_counters_avg.items(): + v['real_time'] /= len(perf_counts) + v['cpu_time'] /= len(perf_counts) + return performance_counters_avg + self.dump_performance_counters_request(f, get_average_performance_counters(perf_counts)) + else: + raise Exception('PM data can only be collected for average or detailed report types') + + logger.info('Pefromance counters report is stored to {}'.format(filename)) diff --git a/tools/benchmark/utils/utils.py b/tools/benchmark/utils/utils.py new file mode 100644 index 00000000000000..8fe49b669be778 --- /dev/null +++ b/tools/benchmark/utils/utils.py @@ -0,0 +1,248 @@ +""" + Copyright (C) 2018-2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import os + +from openvino.inference_engine import IENetwork + +from .constants import DEVICE_DURATION_IN_SECS, UNKNOWN_DEVICE_TYPE, DEVICE_NIREQ_ASYNC, BIN_EXTENSION, \ + CPU_DEVICE_NAME, GPU_DEVICE_NAME +from .inputs_filling import is_image +from .logging import logger + + +def static_vars(**kwargs): + def decorate(func): + for k in kwargs: + setattr(func, k, kwargs[k]) + return func + + return decorate + + +@static_vars(step_id=0) +def next_step(additional_info=''): + step_names = { + 1: "Parsing and validating input arguments", + 2: "Loading Inference Engine", + 3: "Reading the Intermediate Representation network", + 4: "Resizing network to match image sizes and given batch", + 5: "Configuring input of the model", + 6: "Setting device configuration", + 7: "Loading the model to the device", + 8: "Setting optimal runtime parameters", + 9: "Creating infer requests and filling input blobs with images", + 10: "Measuring performance", + 11: "Dumping statistics report", + } + + next_step.step_id += 1 + if next_step.step_id not in step_names.keys(): + raise Exception('Step ID {} is out of total steps number '.format(next_step.step_id, str(len(step_names)))) + + step_info_template = '[Step {}/{}] {}' + step_name = step_names[next_step.step_id] + (' ({})'.format(additional_info) if additional_info else '') + step_info_template = step_info_template.format(next_step.step_id, len(step_names), step_name) + print(step_info_template) + + +def read_network(path_to_model: str): + xml_filename = os.path.abspath(path_to_model) + head, tail = os.path.splitext(xml_filename) + bin_filename = os.path.abspath(head + BIN_EXTENSION) + + ie_network = IENetwork(xml_filename, bin_filename) + + input_info = ie_network.inputs + + if not input_info: + raise AttributeError('No inputs info is provided') + + return ie_network + + +def config_network_inputs(ie_network: IENetwork): + input_info = ie_network.inputs + + for key in input_info.keys(): + if is_image(input_info[key]): + # Set the precision of input data provided by the user + # Should be called before load of the network to the plugin + input_info[key].precision = 'U8' + + +def get_number_iterations(number_iterations: int, nireq: int, api_type: str): + niter = number_iterations + + if api_type == 'async' and niter: + niter = int((niter + nireq - 1) / nireq) * nireq + if number_iterations != niter: + logger.warn('Number of iterations was aligned by request number ' + 'from {} to {} using number of requests {}'.format(number_iterations, niter, nireq)) + + return niter + + +def get_duration_seconds(time, number_iterations, device): + if time: + # time limit + return time + + if not number_iterations: + return get_duration_in_secs(device) + return 0 + + +def get_duration_in_milliseconds(duration): + return duration * 1000 + + +def get_duration_in_secs(target_device): + duration = 0 + for device in DEVICE_DURATION_IN_SECS: + if device in target_device: + duration = max(duration, DEVICE_DURATION_IN_SECS[device]) + + if duration == 0: + duration = DEVICE_DURATION_IN_SECS[UNKNOWN_DEVICE_TYPE] + logger.warn('Default duration {} seconds is used for unknown device {}'.format(duration, target_device)) + + return duration + + +def get_nireq(target_device): + nireq = 0 + for device in DEVICE_NIREQ_ASYNC: + if device in target_device: + nireq = max(nireq, DEVICE_NIREQ_ASYNC[device]) + + if nireq == 0: + nireq = DEVICE_NIREQ_ASYNC[UNKNOWN_DEVICE_TYPE] + logger.warn('Default number of requests {} is used for unknown device {}'.format(nireq, target_device)) + + return nireq + + +def parse_devices(device_string): + devices = device_string + if ':' in devices: + devices = devices.partition(':')[2] + return [d[:d.index('(')] if '(' in d else d for d in devices.split(',')] + + +def parse_value_per_device(devices, values_string): + # Format: :,: or just + result = {} + if not values_string: + return result + device_value_strings = values_string.upper().split(',') + for device_value_string in device_value_strings: + device_value_vec = device_value_string.split(':') + if len(device_value_vec) == 2: + for device in devices: + if device == device_value_vec[0]: + value = int(device_value_vec[1]) + result[device_value_vec[0]] = value + break + elif len(device_value_vec) == 1: + value = int(device_value_vec[0]) + for device in devices: + result[device] = value + elif not device_value_vec: + raise Exception('Unknown string format: ' + values_string) + return result + + +def process_help_inference_string(benchmark_app): + output_string = 'Start inference {}ronously'.format(benchmark_app.api_type) + if benchmark_app.api_type == 'async': + output_string += ', {} inference requests'.format(benchmark_app.nireq) + + device_ss = '' + if CPU_DEVICE_NAME in benchmark_app.device: + device_ss += str(benchmark_app.ie.get_config(CPU_DEVICE_NAME, 'CPU_THROUGHPUT_STREAMS')) + device_ss += ' streams for {}'.format(CPU_DEVICE_NAME) + if GPU_DEVICE_NAME in benchmark_app.device: + device_ss += ', ' if device_ss else '' + device_ss += str(benchmark_app.ie.get_config(GPU_DEVICE_NAME, 'GPU_THROUGHPUT_STREAMS')) + device_ss += ' streams for {}'.format(GPU_DEVICE_NAME) + + if device_ss: + output_string += ' using ' + device_ss + + limits = '' + + if benchmark_app.niter and not benchmark_app.duration_seconds: + limits += '{} iterations'.format(benchmark_app.niter) + + if benchmark_app.duration_seconds: + limits += '{} ms duration'.format(get_duration_in_milliseconds(benchmark_app.duration_seconds)) + if limits: + output_string += ', limits: ' + limits + + return output_string + + +def dump_exec_graph(exe_network, exec_graph_path): + try: + exec_graph_info = exe_network.get_exec_graph_info() + exec_graph_info.serialize(exec_graph_path) + logger.info('Executable graph is stored to {}'.format(exec_graph_path)) + del exec_graph_info + except Exception as e: + logger.exception(e) + + +def print_perf_counters(perf_counts_list): + for ni in range(len(perf_counts_list)): + perf_counts = perf_counts_list[ni] + total_time = 0 + total_time_cpu = 0 + logger.info("Performance counts for {}-th infer request".format(ni)) + for layer, stats in sorted(perf_counts.items(), key=lambda x: x[1]['execution_index']): + max_layer_name = 30 + print("{:<30}{:<15}{:<30}{:<20}{:<20}{:<20}".format( + layer[:max_layer_name - 4] + '...' if (len(layer) >= max_layer_name) else layer, + stats['status'], + 'layerType: ' + str(stats['layer_type']), + 'realTime: ' + str(stats['real_time']), + 'cpu: ' + str(stats['cpu_time']), + 'execType: ' + str(stats['exec_type']))) + total_time += stats['real_time'] + total_time_cpu += stats['cpu_time'] + print('Total time: {} microseconds'.format(total_time)) + print('Total CPU time: {} microseconds\n'.format(total_time_cpu)) + +def get_command_line_arguments(argv): + parameters = [] + arg_name = '' + arg_value = '' + for arg in argv[1:]: + if '=' in arg: + arg_name, arg_value = arg.split('=') + parameters.append((arg_name, arg_value)) + arg_name = '' + arg_value = '' + else: + if arg[0] == '-': + if arg_name is not '': + parameters.append((arg_name, arg_value)) + arg_value = '' + arg_name = arg + else: + arg_value = arg + if arg_name is not '': + parameters.append((arg_name, arg_value)) + return parameters diff --git a/tools/calibration/aggregated_statistics.py b/tools/calibration/aggregated_statistics.py index cc381a91786612..027628c12d2517 100644 --- a/tools/calibration/aggregated_statistics.py +++ b/tools/calibration/aggregated_statistics.py @@ -107,7 +107,9 @@ def add_tensor_statistics(self, layer_name: str, data, n: int, sample: int, chan n_index = sample + n * itteration if n_index >= channels.shape[1]: - channels.resize((channels.shape[0], channels.shape[1] + 1, channels.shape[2]), refcheck=False) + channels.resize((channels.shape[0], n_index + 1, channels.shape[2]), refcheck=False) + if channel >= channels.shape[0]: + channels.resize((channel + 1, channels.shape[1], channels.shape[2]), refcheck=False) channels.itemset((channel, n_index, self.INDEX_MIN), data[sample][channel].min()) channels.itemset((channel, n_index, self.INDEX_MAX), data[sample][channel].max()) diff --git a/tools/calibration/base_calibrator.py b/tools/calibration/base_calibrator.py index 3df403e0acc1cf..aea44fa52caf63 100644 --- a/tools/calibration/base_calibrator.py +++ b/tools/calibration/base_calibrator.py @@ -18,14 +18,14 @@ import numpy as np import os import tempfile +from pathlib import Path from typing import Dict import openvino.inference_engine as ie -from ..accuracy_checker.accuracy_checker.progress_reporters import TQDMReporter, ProgressReporter -from ..accuracy_checker.accuracy_checker.config import ConfigReader -from ..accuracy_checker.accuracy_checker.evaluators.model_evaluator import ModelEvaluator -from ..accuracy_checker.accuracy_checker.presenters import get_result_format_parameters +from accuracy_checker.progress_reporters import TQDMReporter, ProgressReporter +from accuracy_checker.evaluators.model_evaluator import ModelEvaluator +from accuracy_checker.presenters import get_result_format_parameters from ..utils.network_info import NetworkInfo from ..utils.building.network_builder import NetworkBuilder @@ -33,14 +33,11 @@ from .logging import info, debug from .calibrator_configuration import CalibratorConfiguration -from .aggregated_statistics import AggregatedStatistics from .nrmsd import compare_nrmsd from .single_layer_network import SingleLayerNetwork from .inference_result import InferenceResult from .calibration_metrics import CalibrationMetrics -from .infer_raw_results import InferRawResults from .accuracy.metric_factory import MetricFactory -from .accuracy.metric_in_percent import MetricInPercent from .process_dataset_callbacks.collect_results_callback import CollectResultsCallback from .process_dataset_callbacks.calculate_accuracy_callback import CalculateAccuracyCallback @@ -82,24 +79,38 @@ def __init__(self, configuration: CalibratorConfiguration): if self._configuration.gpu_extension and self._configuration.device == 'GPU': self.plugin.set_config('CONFIG_FILE', self._configuration.gpu_extension) - def will_be_fused_workaround(self, layer:ie.IENetLayer, network_info:NetworkInfo=None): - if layer.type == "Const" or layer.type == "Tile": - if not network_info: - network_info = NetworkInfo(self._configuration.model) - only_expected = network_info.explore_inputs(network_info.get_layer(layer.name), ['Const', 'Tile']) - return only_expected, network_info - return False, network_info - - def add_outputs(self, network:ie.IENetwork, output_layers: list=None) -> ie.IENetwork: - if output_layers is None: - output_layers = network.layers.values() - - network_info = None - for layer in output_layers: - fused, network_info = self.will_be_fused_workaround(layer, network_info) - if not fused: - network.add_outputs([layer.name]) - return network + def get_allowed_outputs(self, desired_layers: list=None) -> list: + network_tmp = self.create_network() + # During network loading some layers are trancated. Outputs could not be added to these layers + self.plugin.load(network_tmp) + + output_names = list() + excluded_list = ['gather'] + children_require_stat = ['convolution', 'fullyconnected'] + for layer in network_tmp.layers.values(): + add = False + for child_name in layer.children: + if network_tmp.layers[child_name].type.lower() not in excluded_list: + add = True + break + if layer.type.lower() == "gather": + add = False + for child_name in layer.children: + if network_tmp.layers[child_name].type.lower() in children_require_stat: + add = True + break + if add: + output_names.append(layer.name) + + # return just custom layers if they were set + if desired_layers: + custom_layers_list = list() + for name in output_names: + if name in desired_layers: + custom_layers_list.append(name) + return custom_layers_list + + return output_names def create_network(self) -> ie.IENetwork: network = ie.IENetwork(self._configuration.model, self._configuration.weights) @@ -272,39 +283,6 @@ def compare_result(result1, result2, output_name: str): return False return True - # TODO: add_outputs - remove, not neccessary - def infer(self, - add_outputs=False, - statistics=None, - quantization_level: dict = None, - collect_resuls: bool = False, - collect_layers: set = None, - collect_aggregated_statistics: bool = False, - network: ie.IENetwork = None, - collect_performance_counters: bool = False, - ignore_layer_names: list = None) -> InferenceResult: - - if network is None: - network = self.create_network() - - if add_outputs: - self.add_outputs(network) - - if quantization_level: - for layer_name, value in quantization_level.items(): - params = network.layers[layer_name].params - params["quantization_level"] = value - network.layers[layer_name].params = params - - return self._infer( - network=network, - statistics=statistics, - collect_resuls=collect_resuls, - collect_layers=collect_layers, - collect_aggregated_statistics=collect_aggregated_statistics, - collect_performance_counters=collect_performance_counters, - ignore_layer_names=ignore_layer_names) - def infer_single_layer_network(self, single_layer_network: SingleLayerNetwork, full_network_result: InferenceResult): @@ -333,15 +311,16 @@ def infer_single_layer_network(self, accuracy_drop = compare_nrmsd(actual_result_data, expected_result_data) return accuracy_drop - def _infer( + def infer( self, - network=None, - statistics=None, - collect_aggregated_statistics: bool = True, - collect_resuls: bool = True, + model_path=None, + collect_aggregated_statistics: bool = False, + collect_resuls: bool = False, collect_layers: set = None, collect_performance_counters: bool = False, - ignore_layer_names: list = None + ignore_layer_names: list = None, + per_layer_statistics: dict = None, + add_outputs=False ) -> InferenceResult: ''' Accuracy checker infer and compare results @@ -349,29 +328,23 @@ def _infer( accuracy = 0.0 model = self._configuration.config['models'][0] - launcher_config = model['launchers'][0] - dataset_config = model['datasets'][0] + # Need to copy to keep origin configuration here + launcher_config = model['launchers'][0].copy() + dataset_config = model['datasets'][0].copy() + + if model_path: + launcher_config['model'] = Path(model_path) + launcher_config['weights'] = Path(model_path[:len(model_path) - 3] + 'bin') + + if add_outputs: + launcher_config['outputs'] = self.get_allowed_outputs() process_dataset_callback = None model_evaluator = ModelEvaluator.from_configs(launcher_config, dataset_config) try: - if network: - del model_evaluator.launcher.network - del model_evaluator.launcher.exec_network - model_evaluator.launcher.reload_network = False - model_evaluator.launcher.network = network - model_evaluator.launcher.exec_network = model_evaluator.launcher.plugin.load(network) - if collect_performance_counters: model_evaluator.launcher.plugin.set_config({'PERF_COUNT': 'YES'}) - if statistics: - network_stats = {} - for layer_name, node_statistic in statistics.items(): - network_stats[layer_name] = ie.LayerStats(min=tuple(node_statistic.min_outputs), - max=tuple(node_statistic.max_outputs)) - model_evaluator.launcher.network.stats.update(network_stats) - dataset_size = model_evaluator.dataset.size if self._configuration.progress: @@ -389,7 +362,7 @@ def _infer( model_evaluator.launcher.exec_network, collect_layers=collect_layers, configuration=self._configuration, - statistics=statistics, + per_layer_statistics=per_layer_statistics, normalizer=self, ignore_layer_names=ignore_layer_names) else: diff --git a/tools/calibration/benchmark_facade.py b/tools/calibration/benchmark_facade.py new file mode 100644 index 00000000000000..7ce0bd0510aec9 --- /dev/null +++ b/tools/calibration/benchmark_facade.py @@ -0,0 +1,49 @@ +""" +Copyright (C) 2019 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from ..benchmark.benchmark import Benchmark +from ..benchmark.utils.progress_bar import ProgressBar +from ..benchmark.utils.utils import read_network +from ..benchmark.utils.inputs_filling import get_inputs +from ..benchmark.utils.infer_request_wrap import InferRequestsQueue + + +class BenchmarkResult: + def __init__(self, latency): + self._latency = latency + + @property + def latency(self) -> float: + return self._latency + +class BenchmarkFacade: + def __init__(self, device, batch_size, benchmark_iterations_count, cpu_extension): + self._benchmark = Benchmark(device, 1, benchmark_iterations_count, None, "sync") + self._benchmark.add_extension(cpu_extension) + self._progress_bar_total_count = benchmark_iterations_count \ + if benchmark_iterations_count and not self._benchmark.duration_seconds else 10000 + self._progress_bar = ProgressBar(self._progress_bar_total_count) + self._batch_size = batch_size + + def run(self, path_to_model): + ie_network = read_network(path_to_model) + exe_network = self._benchmark.load_network(ie_network, True, 1) + request_queue = InferRequestsQueue(exe_network.requests) + requests_input_data = get_inputs("", self._batch_size, ie_network.inputs, exe_network.requests) + fps, latency, fp32_total_duration, fp32_iter = self._benchmark.infer( + request_queue, requests_input_data, self._batch_size, self._progress_bar) + + return BenchmarkResult(latency) diff --git a/tools/calibration/calibration_configuration.py b/tools/calibration/calibration_configuration.py index d649304e1d7913..17632281cc8888 100644 --- a/tools/calibration/calibration_configuration.py +++ b/tools/calibration/calibration_configuration.py @@ -158,7 +158,7 @@ def read_ignore_layer_names(configuration: CalibrationConfiguration): ignore_layer_types_from_file = [line.strip() for line in ignore_layer_types_file.readlines()] ignore_layer_types.extend(ignore_layer_types_from_file) - ignore_layer_names = NetworkInfo(configuration.model).get_layer_names(layer_types=ignore_layer_types) + ignore_layer_names = NetworkInfo(configuration.model).get_layer_names_by_types(layer_types=ignore_layer_types) if configuration.ignore_layer_names_path: ignore_layer_names_file = open(configuration.ignore_layer_names_path, 'r') diff --git a/tools/calibration/calibrator.py b/tools/calibration/calibrator.py index aaad6ed577162b..b660802f097962 100644 --- a/tools/calibration/calibrator.py +++ b/tools/calibration/calibrator.py @@ -18,9 +18,9 @@ from ..utils.network_info import NetworkInfo -from ..benchmark.benchmark import Benchmark from ..network import Network +from .benchmark_facade import BenchmarkFacade from .logging import info, debug, info_performance_counters, info_layer_accuracy_drop from .calibrator_configuration import CalibratorConfiguration from .calibrator_factory import CalibratorFactory @@ -39,7 +39,8 @@ def __init__(self, configuration: CalibrationConfiguration): if not self._configuration.simplified_mode: self._calibrator = CalibratorFactory.create(self._configuration.precision, CalibratorConfiguration(configuration)) - self._benchmark = Benchmark(configuration) + self._benchmark = BenchmarkFacade(self._configuration.device, self._configuration.batch_size, + self._configuration.benchmark_iterations_count, self._configuration.cpu_extension) self._ignore_layer_names = CalibrationConfigurationHelper.read_ignore_layer_names(self._configuration) self._quantization_levels = self._calibrator.get_quantization_levels(self._ignore_layer_names) @@ -53,11 +54,11 @@ def collect_fp32_results(self) -> RawResults: iterations = self._configuration.benchmark_iterations_count fp32_latency = 0.0 if iterations > 0: - fp32_latency = self._benchmark.run(iterations_count=self._configuration.benchmark_iterations_count).latency + fp32_latency = self._benchmark.run(self._configuration.model).latency accuracy = fp32_stats.metrics.accuracy info("Original network accuracy: {0:.4f}{1}, latency: {2:0.4f} ms".format(accuracy.value, accuracy.symbol, - 1000 * fp32_latency)) + fp32_latency)) info("Original network performance counters:\n") info_performance_counters(fp32_stats.performance_counters) return RawResults(fp32_stats=fp32_stats, fp32_latency=fp32_latency) @@ -79,7 +80,6 @@ def get_statistics(self, raw_results: RawResults) -> LowPrecisionResults: threshold_low_boundary = self._configuration.threshold_boundary threshold_step = self._configuration.threshold_step - best_accuracy_drop = None while threshold >= threshold_low_boundary: info("Validate {} accuracy, threshold for activation statistics: {}%".format( self._configuration.precision, @@ -87,22 +87,19 @@ def get_statistics(self, raw_results: RawResults) -> LowPrecisionResults: lp_latency = best_lp_stats.latency lp_statistics = fp32_aggregated_statistics.get_node_statistics(threshold) - with Network.reload( - model_path=self._configuration.model, - statistics=lp_statistics, - quantization_levels=self._quantization_levels, - batch_size=self._configuration.batch_size - ) as reloaded_network: - - with self._calibrator.infer(network=reloaded_network.ie_network, - collect_performance_counters=True) as lp_result: - lp_accuracy = lp_result.metrics.accuracy - lp_performance_counters = lp_result.performance_counters - iterations = self._configuration.benchmark_iterations_count - if iterations > 0: - lp_latency = self._benchmark.run( - network=reloaded_network, - iterations_count=self._configuration.benchmark_iterations_count).latency + tmp_model_path = Network.serialize_tmp_model( + model_path=self._configuration.model, + statistics=lp_statistics, + quantization_levels=self._quantization_levels) + + with self._calibrator.infer(model_path=tmp_model_path, + collect_performance_counters=True) as lp_result: + lp_accuracy = lp_result.metrics.accuracy + lp_performance_counters = lp_result.performance_counters + iterations = self._configuration.benchmark_iterations_count + if iterations > 0: + lp_latency = self._benchmark.run(tmp_model_path).latency + Network.rm_tmp_location(tmp_model_path) if lp_accuracy.is_better(best_lp_stats.accuracy, fp32_accuracy): @@ -123,14 +120,14 @@ def get_statistics(self, raw_results: RawResults) -> LowPrecisionResults: self._configuration.precision, lp_accuracy.value, lp_accuracy.symbol, - 1000.0 * lp_latency)) + lp_latency)) threshold = threshold - threshold_step info("Best {0} accuracy is {1:.4f}{2}, latency: {3:0.4f} ms for threshold {4}%".format( self._configuration.precision, best_lp_stats.accuracy.value, best_lp_stats.accuracy.symbol, - 1000.0 * best_lp_stats.latency, + best_lp_stats.latency, best_lp_stats.threshold)) info("{} performance counters:\n".format(self._configuration.precision)) @@ -151,11 +148,15 @@ def return_back_to_fp32(self, lp_results: LowPrecisionResults, raw_results: RawR len(quantization_layers), len(NetworkInfo(self._configuration.model).layers))) - with self._calibrator.infer(add_outputs=True, - collect_resuls=True, - collect_layers=quantization_layers, - statistics=lp_results.statistics, - ignore_layer_names=self._ignore_layer_names) as fp32_result_with_raw_data: + # collect raw original precision outputs per image and use each output + # to calculate layer accuracy drop + + with self._calibrator.infer( + add_outputs=True, + collect_resuls=True, + collect_layers=quantization_layers, + per_layer_statistics=lp_results.statistics, + ignore_layer_names=self._ignore_layer_names) as fp32_result_with_raw_data: if fp32_result_with_raw_data.layers_accuracy_drop: layers_accuracy_drop = fp32_result_with_raw_data.layers_accuracy_drop else: @@ -178,33 +179,31 @@ def return_back_to_fp32(self, lp_results: LowPrecisionResults, raw_results: RawR self._quantization_levels[layer_accuracy_drop.layer_name] = layer_accuracy_drop.precision best_lp_latency = 0.0 - with Network.reload( - self._configuration.model, - statistics=lp_results.statistics, - quantization_levels=self._quantization_levels, - batch_size=self._configuration.batch_size - ) as reloaded_network: - - with self._calibrator.infer(network=reloaded_network.ie_network) as layer_int8_result: - lp_results.accuracy = layer_int8_result.metrics.accuracy - iterations = self._configuration.benchmark_iterations_count - if iterations > 0: - best_lp_latency = self._benchmark.run( - network=reloaded_network, - iterations_count=self._configuration.benchmark_iterations_count).latency - - fp32_accuracy = raw_results.fp32_stats.metrics.accuracy - accuracy_drop = lp_results.accuracy.calculate_drop(fp32_accuracy) + tmp_model_path = Network.serialize_tmp_model( + model_path=self._configuration.model, + statistics=lp_results.statistics, + quantization_levels=self._quantization_levels) + + with self._calibrator.infer(model_path=tmp_model_path) as layer_int8_result: + lp_results.accuracy = layer_int8_result.metrics.accuracy + fp32_accuracy = raw_results.fp32_stats.metrics.accuracy + accuracy_drop = lp_results.accuracy.calculate_drop(fp32_accuracy) + iterations = self._configuration.benchmark_iterations_count + if iterations > 0: + best_lp_latency = self._benchmark.run(tmp_model_path).latency + Network.rm_tmp_location(tmp_model_path) + + lp_results.accuracy_drop = accuracy_drop if accuracy_drop < lp_results.accuracy_drop else lp_results.accuracy_drop if not lp_results.accuracy.is_achieved(fp32_accuracy, self._configuration.threshold): info("Was not achieved: original network accuracy: {0:.4f}{1} (latency: {2:.4} ms) VS {3} accuracy: {4:.4f}{5} " "(latency {6:.4f} ms), accuracy drop {7:.4f}%" .format(fp32_accuracy.value, fp32_accuracy.symbol, - 1000.0 * raw_results.fp32_latency, + raw_results.fp32_latency, self._configuration.precision, lp_results.accuracy.value, lp_results.accuracy.symbol, - 1000.0 * best_lp_latency, + best_lp_latency, accuracy_drop)) else: @@ -213,12 +212,12 @@ def return_back_to_fp32(self, lp_results: LowPrecisionResults, raw_results: RawR "(latency: {6:.4} ms), accuracy drop {7:.4}%" .format(fp32_accuracy.value, fp32_accuracy.symbol, - 1000.0 * raw_results.fp32_latency, + raw_results.fp32_latency, self._configuration.precision, lp_results.accuracy.value, lp_results.accuracy.symbol, - 1000.0 * best_lp_latency, - accuracy_drop)) + best_lp_latency, + lp_results.accuracy_drop)) break else: @@ -259,10 +258,10 @@ def run(self) -> Network: "(latency: {5:0.4f} ms), threshold for activation statistics: {6}%") .format(raw_results.fp32_stats.metrics.accuracy.value, raw_results.fp32_stats.metrics.accuracy.symbol, - 1000.0 * raw_results.fp32_latency, + raw_results.fp32_latency, lp_results.accuracy.value, lp_results.accuracy.symbol, - 1000.0 * lp_results.latency, + lp_results.latency, lp_results.threshold)) self.return_back_to_fp32(lp_results, raw_results) @@ -272,10 +271,10 @@ def run(self) -> Network: "{3:.4f}{4} (latency: {5:.4} ms) with threshold for activation statistic: {6}%".format( raw_results.fp32_stats.metrics.accuracy.value, raw_results.fp32_stats.metrics.accuracy.symbol, - 1000.0 * raw_results.fp32_latency, + raw_results.fp32_latency, lp_results.accuracy.value, lp_results.accuracy.symbol, - 1000.0 * lp_results.latency, + lp_results.latency, lp_results.threshold)) quantized_layers_count = 0 diff --git a/tools/calibration/command_line_processor.py b/tools/calibration/command_line_processor.py index 89e117f5a7e01c..78570e4ed89685 100644 --- a/tools/calibration/command_line_processor.py +++ b/tools/calibration/command_line_processor.py @@ -18,9 +18,9 @@ import tempfile import ntpath -from ..accuracy_checker.accuracy_checker.config import ConfigReader -from ..accuracy_checker.accuracy_checker.launcher.dlsdk_launcher import DLSDKLauncher -from ..accuracy_checker.accuracy_checker.launcher.model_conversion import FrameworkParameters +from accuracy_checker.config import ConfigReader +from accuracy_checker.launcher.dlsdk_launcher import DLSDKLauncher +from accuracy_checker.launcher.model_conversion import FrameworkParameters from ..network import Network from ..utils.path import Path diff --git a/tools/calibration/command_line_reader.py b/tools/calibration/command_line_reader.py index 0a20f8391907fd..779a6379dee20e 100644 --- a/tools/calibration/command_line_reader.py +++ b/tools/calibration/command_line_reader.py @@ -18,7 +18,7 @@ from functools import partial from argparse import ArgumentParser -from ..accuracy_checker.accuracy_checker.utils import get_path +from accuracy_checker.utils import get_path from ..utils.path import Path class CommandLineReader: diff --git a/tools/calibration/process_dataset_callbacks/calculate_accuracy_callback.py b/tools/calibration/process_dataset_callbacks/calculate_accuracy_callback.py index eba75f228c20c7..8422b356d12ceb 100644 --- a/tools/calibration/process_dataset_callbacks/calculate_accuracy_callback.py +++ b/tools/calibration/process_dataset_callbacks/calculate_accuracy_callback.py @@ -35,7 +35,7 @@ def __init__( exec_network: ie.ExecutableNetwork, collect_layers: set, configuration: CalibrationConfiguration, - statistics: dict, + per_layer_statistics: dict, normalizer, ignore_layer_names=None): @@ -47,7 +47,7 @@ def __init__( raise ValueError("configuration is not specified") if not collect_layers: raise ValueError("layers to collect is not specified") - if not statistics: + if not per_layer_statistics: raise ValueError("statistics is not specified") if not normalizer: raise ValueError("normalizer is not specified") @@ -56,8 +56,7 @@ def __init__( self._exec_network = exec_network self._collect_layers = collect_layers self._configuration = configuration - self._network_info = NetworkInfo(self._configuration.model) - self._statistics = statistics + self._per_layer_statistics = per_layer_statistics self._normalizer = normalizer self._ignore_layer_names = ignore_layer_names @@ -81,11 +80,11 @@ def get_accuracy_drop(self) -> np.array: accuracy_drop = [] single_layer_network_names = [net.layer_name for net in self._single_layer_networks] for layer_name, accuracy_drop_of_this_layer in self._accuracy_drop_dict.items(): - if layer_name in single_layer_network_names: + if layer_name in single_layer_network_names and accuracy_drop_of_this_layer.size != 0: accuracy_drop.append(LayerAccuracyDropInfo( layer_name=layer_name, value=self.accuracy_drop_for_layer(accuracy_drop_of_this_layer), - precision=self._network_info.get_layer(layer_name).precision)) + precision=self._network.layers[layer_name].precision)) accuracy_drop.sort(key=lambda accuracy_drop: accuracy_drop.value, reverse=True) return accuracy_drop @@ -135,6 +134,8 @@ def calculate_accuracy_drop_for_batch(self, accuracy_drop_list = np.array([]) for raw_data in infer_raw_results: + if single_layer_network.input_layer_name not in raw_data: + continue input_layer_data = raw_data[single_layer_network.input_layer_name] if tuple(single_layer_network._network.inputs[single_layer_network.input_layer_name].shape) != input_layer_data.shape: @@ -161,7 +162,7 @@ def set_accuracy_drop_dict(self): def set_single_layer_networks(self): assert self._configuration is not None, "Configuration should be set" - assert self._statistics is not None, "Statistics should be set" + assert self._per_layer_statistics is not None, "Statistics should be set" network_info = NetworkInfo(self._configuration.model) @@ -195,7 +196,7 @@ def set_single_layer_networks(self): network_stats = {} # TODO: initialize only neccessary statistic - for layer_name, node_statistic in self._statistics.items(): + for layer_name, node_statistic in self._per_layer_statistics.items(): network_stats[layer_name] = ie.LayerStats(min=tuple(node_statistic.min_outputs), max=tuple(node_statistic.max_outputs)) layer_network.stats.update(network_stats) @@ -226,7 +227,7 @@ def set_single_layer_networks(self): self._layers_to_return_to_fp32 = np.append(self._layers_to_return_to_fp32, layer) index += 1 - def callback(self, value, latency=None): + def callback(self, value, latency=None, **kwargs): collect_value = dict() for layer_name in value: diff --git a/tools/calibration/process_dataset_callbacks/collect_results_callback.py b/tools/calibration/process_dataset_callbacks/collect_results_callback.py index 7900dc7a3aa3d8..9ffbc7746a37bd 100644 --- a/tools/calibration/process_dataset_callbacks/collect_results_callback.py +++ b/tools/calibration/process_dataset_callbacks/collect_results_callback.py @@ -46,13 +46,18 @@ def __init__( self._infer_raw_results = InferRawResults() if collect_resuls else None self._latencies = list() - def callback(self, value, latency = None): + def callback(self, value, latency=None, **kwargs): + network = kwargs.get('network') + exec_network = kwargs.get('exec_network') + if not network or not exec_network: + network = self._network + exec_network = self._exec_network if self._collect_aggregated_statistics: if not self._aggregated_statistics: self._aggregated_statistics = AggregatedStatistics( iterations_count = self._iterations_count, dataset_size = self._dataset_size) - self._aggregated_statistics.add(self._network, self._exec_network, value) + self._aggregated_statistics.add(network, exec_network, value) if self._collect_results: if self._collect_layers: @@ -86,4 +91,4 @@ def release(self): self._infer_raw_results.release() def get_accuracy_drop(self): - return None \ No newline at end of file + return None diff --git a/tools/calibration/requirements.txt b/tools/calibration/requirements.txt index 17b7cdbe31d74a..009f55595202b9 100644 --- a/tools/calibration/requirements.txt +++ b/tools/calibration/requirements.txt @@ -5,7 +5,7 @@ pillow progress py-cpuinfo<=4.0 pyyaml -scipy<=0.19 +scipy<1.2 shapely sklearn tqdm diff --git a/tools/network.py b/tools/network.py index c762cb97e06c52..6a49b9d7de1bfc 100644 --- a/tools/network.py +++ b/tools/network.py @@ -18,6 +18,7 @@ import tempfile import shutil import ntpath +from pathlib import Path as std_path import openvino.inference_engine as ie from .utils.path import Path @@ -45,6 +46,29 @@ def reload(model_path: str, statistics = None, quantization_levels: dict = None, if tmp_model_dir: shutil.rmtree(tmp_model_dir) + @staticmethod + def serialize_tmp_model(model_path: str, statistics = None, quantization_levels: dict = None): + try: + with Network(model_path) as network: + if statistics: + network.set_statistics(statistics) + if quantization_levels: + network.set_quantization_levels(quantization_levels) + + tmp_model_dir = tempfile.mkdtemp(".model") + tmp_model_path = os.path.join(tmp_model_dir, ntpath.basename(model_path)) + network.serialize(tmp_model_path) + return tmp_model_path + except: + print('Could not serialize temporary IR') + raise + + @staticmethod + def rm_tmp_location(file_path): + if file_path: + pdir = std_path(file_path).parent + shutil.rmtree(str(pdir)) + def __init__(self, model_path: str, weights_path: str=None): if model_path is None: raise ValueError("model_path is None") diff --git a/tools/utils/layer.py b/tools/utils/layer.py index 707bb0773721fb..dd73ba965b7a05 100644 --- a/tools/utils/layer.py +++ b/tools/utils/layer.py @@ -25,7 +25,6 @@ class Layer: def __init__(self, data: dict): self._id = int(data['id']) self._name = data['name'] - self._precision = data['precision'] self._type = data['type'] self._input_ports = Layer.__init_ports(data, 'input') @@ -66,10 +65,6 @@ def id(self) -> int: def name(self) -> str: return self._name - @property - def precision(self) -> str: - return self._precision - @property def type(self) -> str: return self._type diff --git a/tools/utils/network_info.py b/tools/utils/network_info.py index d318e46a4b3c75..d3c6ec4108ce3c 100644 --- a/tools/utils/network_info.py +++ b/tools/utils/network_info.py @@ -91,13 +91,13 @@ def __init__(self, model_path: str): pass - def get_layer_names(self, layer_types: List[str]) -> List[str]: - skipped = [] + def get_layer_names_by_types(self, layer_types: List[str]) -> List[str]: + layer_names = [] if layer_types: for layer in self._layer_by_name.values(): if layer.type in layer_types: - skipped.append(layer.name) - return skipped + layer_names.append(layer.name) + return layer_names @property def layers(self) -> int: