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 +- inference-engine/cmake/developer_package.cmake | 18 +- inference-engine/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 +- .../share/InferenceEngineConfig-version.cmake.in | 2 +- .../cmake/share/InferenceEngineConfig.cmake.in | 6 +- inference-engine/cmake/vpu_dependencies.cmake | 68 + inference-engine/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 - .../sample/benchmark_app/benchmark/benchmark.py | 343 - .../benchmark/utils/infer_request_wrap.py | 81 - .../benchmark/utils/inputs_filling.py | 194 - .../benchmark_app/benchmark/utils/parameters.py | 92 - .../sample/benchmark_app/benchmark/utils/utils.py | 99 - .../python/sample/benchmark_app/benchmark_app.py | 4 - .../python/sample/classification_sample/README.md | 4 +- .../sample/classification_sample_async/README.md | 6 +- .../hello_query_device/hello_query_device.py | 15 +- .../sample/object_detection_sample_ssd/README.md | 73 + .../object_detection_sample_ssd.py | 189 + .../python/sample/style_transfer_sample/README.md | 2 +- .../src/openvino/inference_engine/ie_api.pyx | 14 +- .../src/openvino/inference_engine/ie_api_impl.cpp | 31 +- .../src/openvino/inference_engine/ie_api_impl.hpp | 4 +- .../openvino/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 +- inference-engine/include/cpp/ie_infer_request.hpp | 22 +- inference-engine/include/cpp/ie_memory_state.hpp | 37 +- inference-engine/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 +- inference-engine/include/ie_icnn_network_stats.hpp | 49 +- .../include/ie_iexecutable_network.hpp | 79 +- inference-engine/include/ie_iextension.h | 8 +- inference-engine/include/ie_ihetero_plugin.hpp | 17 +- inference-engine/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 +- inference-engine/include/ie_plugin_dispatcher.hpp | 3 + inference-engine/include/ie_precision.hpp | 30 +- inference-engine/include/ie_primitive_info.hpp | 35 +- inference-engine/include/ie_tensor_info.hpp | 16 +- .../include/multi-device/multi_device_config.hpp | 36 + .../include/vpu/hddl_plugin_config.hpp | 184 + inference-engine/include/vpu/vpu_plugin_config.hpp | 3 + inference-engine/samples/CMakeLists.txt | 12 +- inference-engine/samples/benchmark_app/README.md | 130 +- .../samples/benchmark_app/benchmark_app.hpp | 11 +- inference-engine/samples/benchmark_app/main.cpp | 178 +- .../samples/benchmark_app/statistics_report.cpp | 288 +- .../samples/benchmark_app/statistics_report.hpp | 62 +- inference-engine/samples/benchmark_app/utils.hpp | 1 - .../samples/common/format_reader/CMakeLists.txt | 17 +- .../samples/common/os/windows/w_dirent.h | 64 +- inference-engine/samples/common/samples/common.hpp | 3 +- .../samples/common/samples/console_progress.hpp | 57 +- .../samples/hello_classification/README.md | 4 +- .../samples/hello_query_device/README.md | 4 +- .../samples/object_detection_sample_ssd/README.md | 2 + inference-engine/samples/speech_sample/README.md | 14 +- inference-engine/samples/speech_sample/main.cpp | 21 +- .../samples/speech_sample/speech_sample.hpp | 2 +- .../samples/thirdparty/gflags/.gitmodules | 4 - inference-engine/src/CMakeLists.txt | 4 +- inference-engine/src/cldnn_engine/CMakeLists.txt | 5 +- inference-engine/src/cldnn_engine/cldnn_config.h | 2 +- .../src/cldnn_engine/cldnn_custom_layer.h | 4 +- inference-engine/src/cldnn_engine/cldnn_engine.cpp | 22 +- inference-engine/src/cldnn_engine/cldnn_engine.h | 2 +- .../src/cldnn_engine/cldnn_executable_network.cpp | 4 +- inference-engine/src/cldnn_engine/cldnn_graph.cpp | 157 +- inference-engine/src/cldnn_engine/cldnn_graph.h | 20 +- .../src/cldnn_engine/cldnn_infer_request.cpp | 3 +- inference-engine/src/cldnn_engine/cldnn_lstm.cpp | 116 +- .../src/cldnn_engine/cldnn_program.cpp | 826 +- inference-engine/src/cldnn_engine/cldnn_program.h | 33 +- .../src/cldnn_engine/debug_options.cpp | 2 +- inference-engine/src/cldnn_engine/debug_options.h | 11 +- inference-engine/src/extension/README.md | 2 + inference-engine/src/extension/ext_broadcast.cpp | 135 +- .../src/extension/ext_detectionoutput_onnx.cpp | 2 +- inference-engine/src/extension/ext_gather.cpp | 99 +- inference-engine/src/extension/ext_list.cpp | 2 +- inference-engine/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 +- inference-engine/src/extension/ext_resample.cpp | 7 +- inference-engine/src/extension/ext_scatter.cpp | 174 + inference-engine/src/extension/ext_simplernms.cpp | 2 +- .../src/extension/ext_sparse_fill_empty_rows.cpp | 232 + .../src/extension/ext_strided_slice.cpp | 3 + inference-engine/src/extension/ext_unique.cpp | 206 + inference-engine/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 + inference-engine/src/gna_plugin/gna_plugin.cpp | 408 +- inference-engine/src/gna_plugin/gna_plugin.hpp | 15 - .../src/gna_plugin/gna_plugin_config.hpp | 3 +- .../src/gna_plugin/gna_plugin_entry_points.cpp | 2 +- .../src/gna_plugin/gna_plugin_internal.hpp | 4 + inference-engine/src/gna_plugin/gna_plugin_log.hpp | 2 +- .../gna_plugin/quantization/layer_quantizer.hpp | 4 +- .../src/gna_plugin/quantization/quantization.h | 1 + .../gna_plugin/quantization/scale_factor_calc.hpp | 25 +- inference-engine/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 => hetero_plugin}/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 +- .../inference_engine/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 +- .../inference_engine/cpp_interfaces/ie_task.hpp | 1 + .../cpp_interfaces/ie_task_synchronizer.hpp | 1 + .../ie_infer_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 + .../src/inference_engine/hetero/hetero_plugin.cpp | 161 - .../src/inference_engine/ie_cnn_layer_builder.h | 2 + .../inference_engine/ie_cnn_net_reader_impl.cpp | 2 +- .../src/inference_engine/ie_cnn_net_reader_impl.h | 1 + inference-engine/src/inference_engine/ie_core.cpp | 92 +- inference-engine/src/inference_engine/ie_data.cpp | 2 +- .../src/inference_engine/ie_device.cpp | 6 + .../src/inference_engine/ie_format_parser.cpp | 9 +- inference-engine/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 - .../src/inference_engine/ie_layer_validators.cpp | 388 +- .../src/inference_engine/ie_layer_validators.hpp | 341 +- .../src/inference_engine/ie_layers_internal.cpp | 9 +- .../src/inference_engine/ie_layouts.cpp | 20 +- .../src/inference_engine/ie_metric_helpers.hpp | 2 +- .../src/inference_engine/ie_plugin_dispatcher.cpp | 10 + .../ie_preprocess_gapi_kernels.cpp | 1 - .../src/inference_engine/ie_util_internal.cpp | 13 +- inference-engine/src/inference_engine/ie_utils.cpp | 43 + .../src/inference_engine/ie_version.cpp | 2 +- .../src/inference_engine/layer_transform.hpp | 63 +- inference-engine/src/inference_engine/net_pass.cpp | 29 +- .../src/inference_engine/network_serializer.cpp | 5 +- .../src/inference_engine/range_iterator.hpp | 83 - .../built-in/ie_broadcast_shape_infer.hpp | 26 +- .../shape_infer/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 + .../shape_infer/built-in/ie_topk_shape_infer.hpp | 2 +- .../shape_infer/built-in/ie_unique_shape_infer.hpp | 44 + .../built-in/ie_unsqueeze_shape_infer.hpp | 4 + .../shape_infer/const_infer/broadcast_offset.hpp | 71 + .../shape_infer/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 +- .../shape_infer/const_infer/ie_mul_const_infer.hpp | 224 +- .../const_infer/ie_onehot_const_infer.hpp | 71 +- .../const_infer/ie_permute_const_infer.hpp | 75 + .../shape_infer/const_infer/ie_pow_const_infer.hpp | 99 + .../const_infer/ie_reduce_const_infer.hpp | 374 + .../const_infer/ie_shape_const_infer.hpp | 12 +- .../const_infer/ie_strided_slice_const_infer.hpp | 87 +- .../shape_infer/ie_reshape_launcher.cpp | 3 +- .../transform/transform_network.cpp | 353 - .../transform/transform_network.hpp | 116 - .../inference_engine/transform/transformation.cpp | 20 - .../inference_engine/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 - inference-engine/src/inference_engine/w_dirent.h | 56 +- .../src/mkldnn_plugin/mkldnn/desc_iterator.hpp | 44 +- inference-engine/src/mkldnn_plugin/mkldnn_edge.cpp | 8 +- .../src/mkldnn_plugin/mkldnn_exec_network.cpp | 244 + .../src/mkldnn_plugin/mkldnn_exec_network.h | 55 + .../src/mkldnn_plugin/mkldnn_extension_utils.cpp | 2 + .../src/mkldnn_plugin/mkldnn_graph.cpp | 251 +- inference-engine/src/mkldnn_plugin/mkldnn_graph.h | 49 +- .../src/mkldnn_plugin/mkldnn_graph_optimizer.cpp | 46 +- .../src/mkldnn_plugin/mkldnn_memory.cpp | 39 +- inference-engine/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 + inference-engine/src/mkldnn_plugin/mkldnn_node.cpp | 217 +- inference-engine/src/mkldnn_plugin/mkldnn_node.h | 55 +- .../src/mkldnn_plugin/mkldnn_plugin.cpp | 2 +- inference-engine/src/mkldnn_plugin/mkldnn_plugin.h | 5 +- .../src/mkldnn_plugin/mkldnn_streams.cpp | 4 +- .../mkldnn_plugin/nodes/mkldnn_batchnorm_node.cpp | 5 +- .../mkldnn_plugin/nodes/mkldnn_bin_conv_node.cpp | 137 +- .../src/mkldnn_plugin/nodes/mkldnn_conv_node.cpp | 190 +- .../mkldnn_plugin/nodes/mkldnn_def_conv_node.cpp | 133 +- .../mkldnn_plugin/nodes/mkldnn_eltwise_node.cpp | 4 +- .../mkldnn_plugin/nodes/mkldnn_generic_node.cpp | 3 +- .../src/mkldnn_plugin/nodes/mkldnn_memory_node.cpp | 12 - .../src/mkldnn_plugin/nodes/mkldnn_memory_node.hpp | 9 +- .../mkldnn_plugin/nodes/mkldnn_permute_node.cpp | 66 +- .../mkldnn_plugin/nodes/mkldnn_quantize_node.cpp | 12 +- .../mkldnn_plugin/nodes/mkldnn_reorder_node.cpp | 5 +- .../src/mkldnn_plugin/nodes/mkldnn_rnn.h | 2 +- .../mkldnn_plugin/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 +- inference-engine/src/vpu/common/CMakeLists.txt | 42 + .../vpu/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 +- .../{graph_transformer => common}/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 .../{vpu_custom_kernels => custom_kernels}/ctc.cl | 0 .../src/vpu/custom_kernels/customLayerBindings.xml | 227 + .../cvtf32f16.cl | 0 .../cvtu8f16.cl | 0 .../{vpu_custom_kernels => custom_kernels}/grn.cl | 0 .../{vpu_custom_kernels => custom_kernels}/mvn.cl | 0 .../src/vpu/custom_kernels/region_chw.cl | 85 + .../vpu/custom_kernels/region_chw_m7_branch0.cl | 68 + .../vpu/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 +- .../vpu/custom_kernels/resample_with_antialias.cl | 75 + .../shuffle_channels.cl | 0 .../src/vpu/graph_transformer/CMakeLists.txt | 5 +- .../include/vpu/backend/backend.hpp | 2 +- .../graph_transformer/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 +- .../graph_transformer/include/vpu/hw/mx_stage.hpp | 9 +- .../graph_transformer/include/vpu/hw/tiling.hpp | 24 +- .../graph_transformer/include/vpu/model/data.hpp | 7 +- .../include/vpu/model/data_desc.hpp | 91 +- .../graph_transformer/include/vpu/model/model.hpp | 6 +- .../graph_transformer/include/vpu/model/stage.hpp | 87 +- .../include/vpu/parsed_config.hpp | 72 +- .../graph_transformer/include/vpu/pass_manager.hpp | 10 + .../passes/hw_conv_tiling/hw_convolution_tiler.hpp | 319 + .../vpu/passes/hw_conv_tiling/hw_stage_tiler.hpp | 126 + .../passes/hw_pooling_tiling/hw_pooling_tiler.hpp | 209 + .../passes/hw_pooling_tiling/hw_stage_tiler.hpp | 83 + .../include/vpu/private_plugin_config.hpp | 13 +- .../include/vpu/special_stage_processor.hpp | 45 + .../graph_transformer/include/vpu/stub_stage.hpp | 11 +- .../include/vpu/sw/post_op_stage.hpp | 8 +- .../src/vpu/graph_transformer/src/allocator.cpp | 2 - .../vpu/graph_transformer/src/backend/backend.cpp | 2 +- .../src/backend/get_meta_data.cpp | 151 +- .../src/vpu/graph_transformer/src/blob_reader.cpp | 64 +- .../src/vpu/graph_transformer/src/custom_layer.cpp | 10 +- .../src/frontend/detect_network_batch.cpp | 24 +- .../graph_transformer/src/frontend/frontend.cpp | 15 +- .../src/frontend/in_out_convert.cpp | 212 +- .../graph_transformer/src/frontend/parse_data.cpp | 93 +- .../src/frontend/parse_network.cpp | 15 - .../graph_transformer/src/frontend/pre_process.cpp | 1 - .../src/vpu/graph_transformer/src/hw/mx_stage.cpp | 75 +- .../src/vpu/graph_transformer/src/hw/tiling.cpp | 187 +- .../src/vpu/graph_transformer/src/model/data.cpp | 125 +- .../vpu/graph_transformer/src/model/data_desc.cpp | 178 +- .../src/vpu/graph_transformer/src/model/model.cpp | 12 +- .../src/vpu/graph_transformer/src/model/stage.cpp | 95 +- .../vpu/graph_transformer/src/parsed_config.cpp | 128 +- .../src/vpu/graph_transformer/src/pass_manager.cpp | 18 +- .../passes/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 + .../graph_transformer/src/passes/final_check.cpp | 2 +- .../src/passes/hw_conv_tiling.cpp | 1313 +- .../passes/hw_conv_tiling/hw_convolution_tiler.cpp | 757 + .../src/passes/hw_conv_tiling/hw_stage_tiler.cpp | 481 + .../graph_transformer/src/passes/hw_fc_tiling.cpp | 51 +- .../graph_transformer/src/passes/hw_padding.cpp | 49 +- .../src/passes/hw_pooling_tiling.cpp | 748 +- .../passes/hw_pooling_tiling/hw_pooling_tiler.cpp | 477 + .../passes/hw_pooling_tiling/hw_stage_tiler.cpp | 234 + .../graph_transformer/src/passes/initial_check.cpp | 28 + .../vpu/graph_transformer/src/passes/inject_sw.cpp | 16 +- .../src/passes/merge_hw_stages.cpp | 4 +- .../src/passes/process_special_stages.cpp | 579 +- .../src/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 +- .../graph_transformer/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 + .../vpu/graph_transformer/src/stages/argmax.cpp | 36 +- .../src/vpu/graph_transformer/src/stages/bias.cpp | 16 +- .../src/vpu/graph_transformer/src/stages/clamp.cpp | 12 +- .../vpu/graph_transformer/src/stages/concat.cpp | 63 +- .../graph_transformer/src/stages/convolution.cpp | 45 +- .../src/vpu/graph_transformer/src/stages/copy.cpp | 42 +- .../src/vpu/graph_transformer/src/stages/crop.cpp | 64 +- .../graph_transformer/src/stages/ctc_decoder.cpp | 42 +- .../vpu/graph_transformer/src/stages/custom.cpp | 98 +- .../src/stages/detection_output.cpp | 42 +- .../vpu/graph_transformer/src/stages/eltwise.cpp | 132 +- .../src/vpu/graph_transformer/src/stages/exp.cpp | 46 + .../src/stages/{broadcast.cpp => expand.cpp} | 53 +- .../src/vpu/graph_transformer/src/stages/floor.cpp | 45 + .../vpu/graph_transformer/src/stages/gather.cpp | 85 +- .../src/vpu/graph_transformer/src/stages/gemm.cpp | 90 +- .../src/vpu/graph_transformer/src/stages/grn.cpp | 31 +- .../vpu/graph_transformer/src/stages/interp.cpp | 31 +- .../src/vpu/graph_transformer/src/stages/log.cpp | 16 +- .../src/vpu/graph_transformer/src/stages/mtcnn.cpp | 46 +- .../src/vpu/graph_transformer/src/stages/mvn.cpp | 44 +- .../src/vpu/graph_transformer/src/stages/none.cpp | 16 +- .../src/vpu/graph_transformer/src/stages/norm.cpp | 40 +- .../vpu/graph_transformer/src/stages/normalize.cpp | 43 +- .../src/vpu/graph_transformer/src/stages/pad.cpp | 47 +- .../vpu/graph_transformer/src/stages/permute.cpp | 124 +- .../src/vpu/graph_transformer/src/stages/power.cpp | 12 +- .../vpu/graph_transformer/src/stages/proposal.cpp | 45 +- .../graph_transformer/src/stages/psroipooling.cpp | 39 +- .../vpu/graph_transformer/src/stages/reduce.cpp | 82 +- .../graph_transformer/src/stages/region_yolo.cpp | 37 +- .../src/vpu/graph_transformer/src/stages/relu.cpp | 14 +- .../graph_transformer/src/stages/reorg_yolo.cpp | 52 +- .../vpu/graph_transformer/src/stages/resample.cpp | 31 +- .../vpu/graph_transformer/src/stages/reshape.cpp | 48 +- .../src/stages/reverse_sequence.cpp | 64 +- .../src/vpu/graph_transformer/src/stages/rnn.cpp | 87 +- .../graph_transformer/src/stages/roipooling.cpp | 39 +- .../src/vpu/graph_transformer/src/stages/scale.cpp | 14 +- .../vpu/graph_transformer/src/stages/shrink.cpp | 31 +- .../vpu/graph_transformer/src/stages/softmax.cpp | 28 +- .../src/vpu/graph_transformer/src/stages/split.cpp | 63 +- .../graph_transformer/src/stages/strided_slice.cpp | 77 + .../src/vpu/graph_transformer/src/stages/tile.cpp | 45 +- .../src/vpu/graph_transformer/src/stages/topk.cpp | 147 + .../src/vpu/graph_transformer/src/stub_stage.cpp | 58 +- .../vpu/graph_transformer/src/sw/post_op_stage.cpp | 41 +- .../src/vpu/myriad_plugin/CMakeLists.txt | 10 +- .../src/vpu/myriad_plugin/api/myriad_api.cpp | 2 +- .../myriad_plugin/myriad_async_infer_request.cpp | 2 + .../src/vpu/myriad_plugin/myriad_config.cpp | 13 + .../src/vpu/myriad_plugin/myriad_config.h | 9 + .../myriad_plugin/myriad_executable_network.cpp | 145 +- .../vpu/myriad_plugin/myriad_executable_network.h | 11 +- .../src/vpu/myriad_plugin/myriad_executor.cpp | 21 +- .../src/vpu/myriad_plugin/myriad_executor.h | 3 +- .../src/vpu/myriad_plugin/myriad_infer_request.cpp | 68 +- .../src/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 +- inference-engine/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 +- .../tests/unit/builders/network_builder_test.cpp | 20 +- .../tests/unit/builders/transform_network_test.cpp | 185 - .../cnn_network/cnn_layer_validation_tests.cpp | 2 +- .../tests/unit/cnn_network/parameters.h | 4 +- inference-engine/tests/unit/cnn_network/shapes.h | 4 +- .../unit/cnn_network/v2_format_parser_test.cpp | 4 +- .../tests/unit/engines/gna/configuration_test.cpp | 39 - .../unit/engines/gna/fp32_non_quantized_tests.cpp | 211 + .../tests/unit/engines/gna/gna_cppwraper_test.cpp | 2 +- .../tests/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 +- .../unit/engines/gna/i16_quantisation_test.cpp | 14 +- .../unit/engines/gna/matchers/diag_matcher.hpp | 24 +- .../tests/unit/engines/gna/test_irs.cpp | 2986 +++- .../tests/unit/engines/gna/test_irs.hpp | 35 + .../graph/layers/extensions/broadcast_tests.cpp | 70 +- .../mkldnn/graph/layers/extensions/fake_layer.cpp | 4 +- .../graph/layers/extensions/gather_tests.cpp | 122 +- .../graph/layers/extensions/gather_tree_tests.cpp | 286 - .../graph/layers/extensions/graph_generic_test.cpp | 2 +- .../mkldnn/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 + .../extensions/sparse_fill_empty_rows_tests.cpp | 553 + .../mkldnn/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 +- .../graph/layers/internal/graph_eltwise_test.cpp | 52 +- .../graph/layers/internal/graph_leaks_test.cpp | 1 + .../graph/layers/internal/graph_permute_test.cpp | 8 +- .../graph/structure/graph_structure_test.cpp | 19 +- .../tests/unit/engines/mkldnn/graph/test_graph.hpp | 6 +- .../mkldnn}/mem_solver_test.cpp | 8 +- .../tests/unit/graph_tools/graph_copy_tests.cpp | 52 +- .../tests/unit/graph_tools/graph_test_base.hpp | 9 +- .../graph_tools/graph_tools_functional_tests.cpp | 39 + .../tests/unit/graph_tools/graph_tools_test.cpp | 132 +- .../inference_engine_tests/blob_proxy_test.cpp | 20 +- .../unit/inference_engine_tests/blob_test.cpp | 5 +- .../inference_engine_tests/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 +- .../unit/inference_engine_tests/device_tests.cpp | 59 - .../unit/inference_engine_tests/local_test.cpp | 143 +- .../inference_engine_tests/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 + ...ck_async_infer_request_thread_safe_internal.hpp | 2 +- .../tests/unit/mocks/mock_icnn_network.hpp | 8 +- .../unit/mocks/mock_not_empty_icnn_network.hpp | 2 +- .../built_in_shape_infer_general_test.cpp | 36 + .../transformations/eltwise_broadcast_test.cpp | 63 - .../tests/unit/transformations/sub_test.cpp | 39 - .../unit/transformations/tranformations_test.hpp | 13 - inference-engine/thirdparty/CMakeLists.txt | 44 +- inference-engine/thirdparty/ade | 2 +- inference-engine/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 - .../thirdparty/clDNN/api/C/average_unpooling.h | 52 - .../thirdparty/clDNN/api/C/batch_norm.h | 64 - .../thirdparty/clDNN/api/C/batch_norm_grad.h | 48 - .../thirdparty/clDNN/api/C/binary_convolution.h | 64 - inference-engine/thirdparty/clDNN/api/C/border.h | 82 - .../thirdparty/clDNN/api/C/broadcast.h | 89 - inference-engine/thirdparty/clDNN/api/C/cldnn.h | 891 - .../thirdparty/clDNN/api/C/concatenation.h | 74 - .../thirdparty/clDNN/api/C/condition.h | 65 - inference-engine/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 - inference-engine/thirdparty/clDNN/api/C/crop.h | 71 - .../thirdparty/clDNN/api/C/custom_gpu_primitive.h | 67 - inference-engine/thirdparty/clDNN/api/C/data.h | 51 - .../thirdparty/clDNN/api/C/deconvolution.h | 69 - .../thirdparty/clDNN/api/C/deformable_conv.h | 55 - .../thirdparty/clDNN/api/C/deformable_interp.h | 66 - .../thirdparty/clDNN/api/C/depth_to_space.h | 46 - .../thirdparty/clDNN/api/C/detection_output.h | 89 - .../thirdparty/clDNN/api/C/detection_output_sort.h | 58 - inference-engine/thirdparty/clDNN/api/C/eltwise.h | 110 - inference-engine/thirdparty/clDNN/api/C/embed.h | 49 - .../thirdparty/clDNN/api/C/fully_connected.h | 62 - .../clDNN/api/C/fully_connected_grad_input.h | 46 - .../clDNN/api/C/fully_connected_grad_weights.h | 55 - inference-engine/thirdparty/clDNN/api/C/gather.h | 54 - inference-engine/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 - inference-engine/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 - inference-engine/thirdparty/clDNN/api/C/one_hot.h | 72 - inference-engine/thirdparty/clDNN/api/C/permute.h | 53 - inference-engine/thirdparty/clDNN/api/C/pooling.h | 77 - .../thirdparty/clDNN/api/C/prior_box.h | 69 - inference-engine/thirdparty/clDNN/api/C/proposal.h | 65 - inference-engine/thirdparty/clDNN/api/C/reduce.h | 87 - .../thirdparty/clDNN/api/C/region_yolo.h | 59 - inference-engine/thirdparty/clDNN/api/C/reorder.h | 55 - .../thirdparty/clDNN/api/C/reorg_yolo.h | 52 - inference-engine/thirdparty/clDNN/api/C/reshape.h | 50 - .../thirdparty/clDNN/api/C/reverse_sequence.h | 48 - .../thirdparty/clDNN/api/C/roi_pooling.h | 68 - inference-engine/thirdparty/clDNN/api/C/scale.h | 58 - .../thirdparty/clDNN/api/C/scale_grad_input.h | 45 - .../thirdparty/clDNN/api/C/scale_grad_weights.h | 54 - inference-engine/thirdparty/clDNN/api/C/select.h | 48 - .../thirdparty/clDNN/api/C/shuffle_channels.h | 48 - inference-engine/thirdparty/clDNN/api/C/softmax.h | 71 - .../thirdparty/clDNN/api/C/softmax_loss_grad.h | 46 - inference-engine/thirdparty/clDNN/api/C/split.h | 68 - .../thirdparty/clDNN/api/C/strided_slice.h | 52 - inference-engine/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 - .../thirdparty/clDNN/api/{CPP => }/activation.hpp | 86 +- .../clDNN/api/{CPP => }/activation_grad.hpp | 33 +- .../thirdparty/clDNN/api/{CPP => }/apply_adam.hpp | 28 +- .../thirdparty/clDNN/api/{CPP => }/arg_max_min.hpp | 25 +- .../clDNN/api/{CPP => }/average_unpooling.hpp | 16 +- .../thirdparty/clDNN/api/{CPP => }/batch_norm.hpp | 22 +- .../clDNN/api/{CPP => }/batch_norm_grad.hpp | 14 +- .../clDNN/api/{CPP => }/binary_convolution.hpp | 38 +- .../thirdparty/clDNN/api/{CPP => }/border.hpp | 30 +- .../thirdparty/clDNN/api/{CPP => }/broadcast.hpp | 17 +- .../clDNN/api/{CPP/cldnn_defs.h => cldnn.hpp} | 203 +- inference-engine/thirdparty/clDNN/api/compounds.h | 101 + .../clDNN/api/{CPP => }/concatenation.hpp | 24 +- .../thirdparty/clDNN/api/{CPP => }/condition.hpp | 21 +- .../thirdparty/clDNN/api/{CPP => }/contract.hpp | 28 +- .../thirdparty/clDNN/api/{CPP => }/convolution.hpp | 413 +- .../clDNN/api/{CPP => }/convolution_grad_input.hpp | 10 +- .../api/{CPP => }/convolution_grad_weights.hpp | 119 +- .../thirdparty/clDNN/api/{CPP => }/crop.hpp | 12 +- .../clDNN/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 +- .../thirdparty/clDNN/api/{CPP => }/eltwise.hpp | 150 +- .../thirdparty/clDNN/api/{CPP => }/embed.hpp | 15 +- .../thirdparty/clDNN/api/{CPP => }/engine.hpp | 141 +- inference-engine/thirdparty/clDNN/api/event.hpp | 94 + .../clDNN/api/{CPP => }/fully_connected.hpp | 50 +- .../api/{CPP => }/fully_connected_grad_input.hpp | 15 +- .../api/{CPP => }/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 +- .../thirdparty/clDNN/api/{CPP => }/meta_utils.hpp | 2 +- .../clDNN/api/{CPP => }/mutable_data.hpp | 15 +- .../thirdparty/clDNN/api/{CPP => }/mvn.hpp | 19 +- inference-engine/thirdparty/clDNN/api/network.hpp | 200 + .../thirdparty/clDNN/api/{CPP => }/normalize.hpp | 18 +- .../thirdparty/clDNN/api/{CPP => }/one_hot.hpp | 16 +- .../thirdparty/clDNN/api/{CPP => }/permute.hpp | 9 +- .../thirdparty/clDNN/api/{CPP => }/pooling.hpp | 40 +- .../thirdparty/clDNN/api/primitive.hpp | 173 + .../thirdparty/clDNN/api/{CPP => }/prior_box.hpp | 47 +- .../thirdparty/clDNN/api/{CPP => }/profiling.hpp | 2 - .../thirdparty/clDNN/api/{CPP => }/program.hpp | 282 +- .../thirdparty/clDNN/api/{CPP => }/proposal.hpp | 55 +- .../clDNN/api/{CPP => }/pyramid_roi_align.hpp | 14 +- .../thirdparty/clDNN/api/{CPP => }/quantize.hpp | 9 +- .../thirdparty/clDNN/api/{CPP => }/reduce.hpp | 49 +- .../thirdparty/clDNN/api/{CPP => }/region_yolo.hpp | 21 +- .../thirdparty/clDNN/api/{CPP => }/reorder.hpp | 37 +- .../thirdparty/clDNN/api/{CPP => }/reorg_yolo.hpp | 9 +- .../thirdparty/clDNN/api/{CPP => }/reshape.hpp | 11 +- .../clDNN/api/{CPP => }/reverse_sequence.hpp | 12 +- .../thirdparty/clDNN/api/{CPP => }/roi_pooling.hpp | 34 +- .../thirdparty/clDNN/api/{CPP => }/scale.hpp | 11 +- .../clDNN/api/{CPP => }/scale_grad_input.hpp | 11 +- .../clDNN/api/{CPP => }/scale_grad_weights.hpp | 20 +- .../thirdparty/clDNN/api/{CPP => }/select.hpp | 9 +- .../clDNN/api/{CPP => }/shuffle_channels.hpp | 12 +- .../thirdparty/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 +- .../thirdparty/clDNN/api/{CPP => }/topology.hpp | 60 +- .../thirdparty/clDNN/api/{CPP => }/upsampling.hpp | 31 +- .../clDNN/api_extension/C/fused_conv_bn_scale.h | 69 - .../clDNN/api_extension/C/fused_conv_eltwise.h | 103 - .../clDNN/api_extension/C/lstm_dynamic_input.h | 58 - .../clDNN/api_extension/C/lstm_dynamic_timeloop.h | 68 - .../{CPP => }/fused_conv_bn_scale.hpp | 69 +- .../api_extension/{CPP => }/fused_conv_eltwise.hpp | 122 +- .../api_extension/{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 +- .../clDNN/kernel_selector/common/common_types.h | 3 +- .../clDNN/kernel_selector/common/tensor_type.cpp | 19 + .../clDNN/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 +- ...binary_convolution_kernel_1x1_b_fs_yx_fsv16.cpp | 190 + .../binary_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/binary_convolution_params.h | 16 +- .../actual_kernels/border/border_kernel_base.cpp | 2 +- .../actual_kernels/border/border_kernel_ref.cpp | 4 + .../concatenation_kernel_simple_ref.cpp | 10 +- .../actual_kernels/contract/contract_kernel_base.h | 2 +- .../convolution/convolution_kernel_base.cpp | 16 +- .../convolution/convolution_kernel_bfyx_1x1.cpp | 2 +- .../convolution_kernel_bfyx_1x1_opt.cpp | 2 +- .../convolution/convolution_kernel_bfyx_f16.cpp | 87 +- .../convolution/convolution_kernel_bfyx_f16.h | 1 - .../convolution_kernel_bfyx_f16_1x1.cpp | 87 +- .../convolution/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/convolution_kernel_bfzyx_f16.cpp | 194 + ...l_tutorial.h => convolution_kernel_bfzyx_f16.h} | 37 +- .../convolution_kernel_bfzyx_f16_fp16.h} | 24 +- .../convolution_kernel_bfzyx_f16_fp32.h} | 36 +- .../convolution/convolution_kernel_bfzyx_ref.cpp | 54 - .../convolution_kernel_fs_byx_fsv32_depthwise.cpp | 192 + .../convolution_kernel_fs_byx_fsv32_depthwise.h | 56 + .../convolution/convolution_kernel_imad_3x3.cpp | 2 +- .../convolution/convolution_kernel_ref.cpp | 9 +- .../convolution/convolution_kernel_ref.h | 2 + .../convolution/convolution_kernel_selector.cpp | 11 +- .../convolution/convolution_kernel_tutorial.cpp | 184 - .../convolution/convolution_params.h | 16 - .../deformable_convolution_kernel_bfyx_conv.cpp | 2 +- .../deconvolution/deconvolution_kernel_base.cpp | 38 +- .../deconvolution/deconvolution_kernel_base.h | 11 + .../deconvolution_kernel_bfzyx_f16.cpp | 152 + .../deconvolution/deconvolution_kernel_bfzyx_f16.h | 42 + .../deconvolution/deconvolution_kernel_ref.cpp | 2 + .../deconvolution_kernel_selector.cpp | 2 + .../depth_to_space/depth_to_space_kernel_ref.h | 2 +- .../actual_kernels/eltwise/eltwise_kernel_base.cpp | 24 +- .../actual_kernels/eltwise/eltwise_kernel_ref.cpp | 1 + .../fully_connected_block_kernel_base.cpp | 14 +- .../fully_connected_block_kernel_base.h | 14 +- .../fully_connected/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 + .../core/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/gather_tree_kernel_selector.cpp | 25 + .../gather_tree/gather_tree_kernel_selector.h | 31 + .../lookup_table/lookup_table_kernel_base.h | 2 +- .../lstm_dynamic/lstm_dynamic_input_bfyx_opt.cpp | 122 + .../lstm_dynamic_input_bfyx_opt.h} | 26 +- .../lstm_dynamic_input_kernel_base.cpp | 46 +- .../lstm_dynamic/lstm_dynamic_input_kernel_base.h | 37 +- .../lstm_dynamic_input_kernel_selector.cpp | 6 +- .../lstm_dynamic/lstm_dynamic_input_ref_kernel.cpp | 5 + .../lstm_dynamic_timeloop_kernel_base.cpp | 39 +- .../lstm_dynamic_timeloop_kernel_base.h | 2 +- .../core/actual_kernels/mvn/mvn_kernel_ref.cpp | 2 + .../actual_kernels/one_hot/one_hot_kernel_base.h | 3 +- .../actual_kernels/pooling/pooling_kernel_base.cpp | 2 +- .../pooling/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 +- .../actual_kernels/quantize/quantize_kernel_ref.h | 32 +- .../quantize/qunatize_kernel_selector.cpp | 4 +- .../core/actual_kernels/reduce/reduce_kernel_ref.h | 2 +- .../region_yolo/region_yolo_kernel_ref.h | 3 +- .../actual_kernels/reorder/reorder_kernel_base.cpp | 6 +- .../actual_kernels/reorder/reorder_kernel_base.h | 3 +- .../reorder/reorder_kernel_binary.cpp | 17 +- .../reorg_yolo/reorg_yolo_kernel_ref.h | 2 +- .../reverse_sequence/reverse_sequence_kernel_ref.h | 3 +- .../roi_pooling/roi_pooling_kernel_base.cpp | 2 +- .../shuffle_channels/shuffle_channels_kernel_ref.h | 2 +- .../softmax/softmax_items_class_kernel_base.cpp | 4 + .../actual_kernels/softmax/softmax_kernel_base.cpp | 2 +- .../core/actual_kernels/tile/tile_kernel_ref.h | 2 +- .../clDNN/kernel_selector/core/auto_tuner.cpp | 1 + .../clDNN/kernel_selector/core/cache/cache.json | 16969 ++++++++++++++++++- .../core/cl_kernels/activation_tutorial.cl | 85 - .../core/cl_kernels/arg_max_min_axis.cl | 10 +- .../core/cl_kernels/binary_convolution_gpu_1x1.cl | 15 +- .../binary_convolution_gpu_1x1_b_fs_yx_fsv16.cl | 176 + .../cl_kernels/binary_convolution_gpu_generic.cl | 42 +- .../core/cl_kernels/binary_convolution_gpu_ref.cl | 9 +- .../core/cl_kernels/border_gpu_ref.cl | 58 +- .../cl_kernels/concatenation_gpu_simple_ref.cl | 4 + .../core/cl_kernels/convolution_gpu_bfyx_f16.cl | 22 +- .../cl_kernels/convolution_gpu_bfyx_f16_1x1.cl | 28 +- .../convolution_gpu_bfyx_f16_depthwise.cl | 25 +- .../cl_kernels/convolution_gpu_bfyx_to_bfyx_f16.cl | 21 +- .../core/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 - .../core/cl_kernels/fused_conv_eltwise_gpu_ref.cl | 67 +- .../core/cl_kernels/gather_tree_gpu_ref.cl | 43 + .../core/cl_kernels/gen9_common_conv_bwd_data.cl | 312 + .../cl_kernels/gen9_common_conv_fwd_data_f16.cl | 1155 ++ .../cl_kernels/gen9_common_conv_fwd_data_f32.cl | 810 + .../core/cl_kernels/generic_eltwise_ref.cl | 20 +- .../core/cl_kernels/include/fetch.cl | 116 +- .../core/cl_kernels/lstm_dynamic_input_bfyx_opt.cl | 121 + .../core/cl_kernels/lstm_dynamic_input_ref.cl | 2 +- .../cl_kernels/mvn_gpu_ref_accross_channels.cl | 42 +- .../core/cl_kernels/mvn_gpu_ref_within_channels.cl | 39 +- .../kernel_selector/core/cl_kernels/ocl_types.h | 444 + .../kernel_selector/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 +- .../cl_kernels/reorder_data_to_yxfb_batched.cl | 2 +- .../core/cl_kernels/reorder_weights.cl | 33 +- .../clDNN/kernel_selector/core/common/jitter.cpp | 208 +- .../clDNN/kernel_selector/core/common/jitter.h | 11 +- .../kernel_selector/core/common/training_params.h | 4 +- .../clDNN/kernel_selector/core/kernel_base.cpp | 70 +- .../clDNN/kernel_selector/core/kernel_base.h | 3 + .../kernel_selector/core/kernel_runner_interface.h | 5 +- .../clDNN/kernel_selector/core/kernel_selector.cpp | 8 +- .../core/kernel_selector_common.cpp | 4 + .../core/kernel_selector_params.cpp | 248 +- .../kernel_selector/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 +- .../thirdparty/clDNN/src/average_unpooling.cpp | 2 +- .../thirdparty/clDNN/src/batch_norm.cpp | 2 +- .../thirdparty/clDNN/src/batch_norm_grad.cpp | 2 +- .../thirdparty/clDNN/src/binary_convolution.cpp | 27 +- inference-engine/thirdparty/clDNN/src/border.cpp | 39 +- .../thirdparty/clDNN/src/broadcast.cpp | 2 +- inference-engine/thirdparty/clDNN/src/cldnn.cpp | 843 +- .../thirdparty/clDNN/src/concatenation.cpp | 2 +- .../thirdparty/clDNN/src/condition.cpp | 2 +- inference-engine/thirdparty/clDNN/src/contract.cpp | 2 +- .../thirdparty/clDNN/src/convolution.cpp | 53 +- .../clDNN/src/convolution_grad_weights.cpp | 6 +- inference-engine/thirdparty/clDNN/src/crop.cpp | 12 +- .../thirdparty/clDNN/src/custom_gpu_primitive.cpp | 4 +- inference-engine/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 +- inference-engine/thirdparty/clDNN/src/eltwise.cpp | 17 +- inference-engine/thirdparty/clDNN/src/embed.cpp | 2 +- inference-engine/thirdparty/clDNN/src/engine.cpp | 87 +- inference-engine/thirdparty/clDNN/src/event.cpp | 43 +- .../thirdparty/clDNN/src/fully_connected.cpp | 4 +- .../clDNN/src/fully_connected_grad_input.cpp | 2 +- .../clDNN/src/fully_connected_grad_weights.cpp | 2 +- .../thirdparty/clDNN/src/fused_conv_bn_scale.cpp | 15 +- .../thirdparty/clDNN/src/fused_conv_eltwise.cpp | 27 +- inference-engine/thirdparty/clDNN/src/gather.cpp | 2 +- .../thirdparty/clDNN/src/gather_tree.cpp | 71 + inference-engine/thirdparty/clDNN/src/gemm.cpp | 2 +- .../thirdparty/clDNN/src/generic_layer.cpp | 2 +- .../thirdparty/clDNN/src/gpu/activation_gpu.cpp | 70 +- .../clDNN/src/gpu/activation_grad_gpu.cpp | 45 +- .../thirdparty/clDNN/src/gpu/apply_adam_gpu.cpp | 30 +- .../thirdparty/clDNN/src/gpu/arg_max_min_gpu.cpp | 12 +- .../clDNN/src/gpu/average_unpooling_gpu.cpp | 50 +- .../thirdparty/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 +- .../thirdparty/clDNN/src/gpu/broadcast_gpu.cpp | 38 +- .../cache/kernels/pooling_gpu_bfyx_average_opt.cl | 1 - .../clDNN/src/gpu/command_queues_builder.cpp | 28 +- .../clDNN/src/gpu/command_queues_builder.h | 8 +- .../thirdparty/clDNN/src/gpu/concatenation_gpu.cpp | 99 +- .../thirdparty/clDNN/src/gpu/condition_gpu.cpp | 23 +- .../thirdparty/clDNN/src/gpu/configuration.cpp | 4 + .../thirdparty/clDNN/src/gpu/confiugration.h | 8 +- .../thirdparty/clDNN/src/gpu/contract_gpu.cpp | 26 +- .../thirdparty/clDNN/src/gpu/convolution_gpu.cpp | 161 +- .../clDNN/src/gpu/convolution_grad_weights_gpu.cpp | 50 +- .../thirdparty/clDNN/src/gpu/crop_gpu.cpp | 80 +- .../clDNN/src/gpu/custom_gpu_primitive_gpu.cpp | 27 +- .../thirdparty/clDNN/src/gpu/deconvolution_gpu.cpp | 53 +- .../clDNN/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 +- .../src/gpu/fully_connected_grad_input_gpu.cpp | 36 +- .../src/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 +- .../thirdparty/clDNN/src/gpu/gather_tree_gpu.cpp | 63 + .../thirdparty/clDNN/src/gpu/gemm_gpu.cpp | 26 +- .../thirdparty/clDNN/src/gpu/generic_layer_gpu.cpp | 15 +- .../thirdparty/clDNN/src/gpu/index_select_gpu.cpp | 46 +- .../thirdparty/clDNN/src/gpu/kernel_runner.cpp | 14 +- .../thirdparty/clDNN/src/gpu/kernel_runner.h | 2 +- .../thirdparty/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 +- .../clDNN/src/gpu/lstm_dynamic_timeloop_gpu.cpp | 26 +- .../thirdparty/clDNN/src/gpu/lstm_elt_gpu.cpp | 32 +- .../thirdparty/clDNN/src/gpu/lstm_gemm_gpu.cpp | 30 +- .../thirdparty/clDNN/src/gpu/max_unpooling_gpu.cpp | 50 +- .../thirdparty/clDNN/src/gpu/memory_gpu.cpp | 4 +- .../thirdparty/clDNN/src/gpu/mutable_data_gpu.cpp | 14 +- .../thirdparty/clDNN/src/gpu/mvn_gpu.cpp | 50 +- .../thirdparty/clDNN/src/gpu/normalize_gpu.cpp | 38 +- .../thirdparty/clDNN/src/gpu/ocl_base_event.cpp | 21 +- .../thirdparty/clDNN/src/gpu/ocl_base_event.h | 7 +- .../thirdparty/clDNN/src/gpu/ocl_queue_wrapper.h | 14 + .../thirdparty/clDNN/src/gpu/ocl_toolkit.cpp | 8 +- .../thirdparty/clDNN/src/gpu/ocl_toolkit.h | 3 +- .../thirdparty/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 +- .../thirdparty/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 +- .../thirdparty/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 +- .../thirdparty/clDNN/src/gpu/reorg_yolo_gpu.cpp | 26 +- .../thirdparty/clDNN/src/gpu/reshape_gpu.cpp | 14 +- .../clDNN/src/gpu/reverse_sequence_gpu.cpp | 24 +- .../thirdparty/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 +- .../thirdparty/clDNN/src/gpu/strided_slice_gpu.cpp | 24 +- .../thirdparty/clDNN/src/gpu/tile_gpu.cpp | 24 +- .../thirdparty/clDNN/src/gpu/upsampling_gpu.cpp | 30 +- .../clDNN/src/gpu/wait_for_events_gpu.cpp | 24 +- .../src/graph_optimizer/add_required_reorders.cpp | 16 +- .../src/graph_optimizer/calculate_prior_boxes.cpp | 8 +- .../src/graph_optimizer/eltwise_remove_stride.cpp | 2 +- .../src/graph_optimizer/eltwise_shrinking.cpp | 3 + .../src/graph_optimizer/graph_initializations.cpp | 48 +- .../clDNN/src/graph_optimizer/handle_reshape.cpp | 2 +- .../src/graph_optimizer/post_input_reorder.cpp | 6 +- .../src/graph_optimizer/post_optimize_weights.cpp | 91 +- .../src/graph_optimizer/pre_optimize_bias.cpp | 39 +- .../prep_opt_depthwise_sep_post.cpp | 4 +- .../src/graph_optimizer/prepare_binarization.cpp | 133 - .../src/graph_optimizer/prepare_buffer_fusing.cpp | 124 +- .../graph_optimizer/prepare_depthwise_sep_opt.cpp | 6 +- .../clDNN/src/graph_optimizer/prepare_padding.cpp | 6 +- .../graph_optimizer/prepare_primitive_fusing.cpp | 640 +- .../src/graph_optimizer/prepare_quantization.cpp | 98 + .../src/graph_optimizer/propagate_constants.cpp | 4 +- .../graph_optimizer/remove_redundant_reorders.cpp | 290 +- .../clDNN/src/graph_optimizer/reorder_inputs.cpp | 685 +- .../reverse_optional_nodes_outputs.cpp | 1 - .../src/graph_optimizer/strided_slice_optimize.cpp | 4 +- inference-engine/thirdparty/clDNN/src/half.cpp | 3 +- .../clDNN/src/include/activation_grad_inst.h | 2 +- .../thirdparty/clDNN/src/include/activation_inst.h | 2 +- .../thirdparty/clDNN/src/include/api_impl.h | 125 - .../thirdparty/clDNN/src/include/apply_adam_inst.h | 2 +- .../clDNN/src/include/arg_max_min_inst.h | 2 +- .../clDNN/src/include/average_unpooling_inst.h | 2 +- .../clDNN/src/include/batch_norm_grad_inst.h | 2 +- .../thirdparty/clDNN/src/include/batch_norm_inst.h | 2 +- .../clDNN/src/include/binary_convolution_inst.h | 46 +- .../thirdparty/clDNN/src/include/border_inst.h | 2 +- .../thirdparty/clDNN/src/include/broadcast_inst.h | 2 +- .../clDNN/src/include/concatenation_inst.h | 2 +- .../thirdparty/clDNN/src/include/condition_inst.h | 8 +- .../thirdparty/clDNN/src/include/contract_inst.h | 2 +- .../src/include/convolution_grad_weights_inst.h | 2 +- .../clDNN/src/include/convolution_inst.h | 70 +- .../thirdparty/clDNN/src/include/crop_inst.h | 2 +- .../clDNN/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 +- .../thirdparty/clDNN/src/include/eltwise_inst.h | 2 +- .../thirdparty/clDNN/src/include/embed_inst.h | 2 +- .../thirdparty/clDNN/src/include/engine_impl.h | 11 +- .../thirdparty/clDNN/src/include/error_handler.h | 2 +- .../thirdparty/clDNN/src/include/event_impl.h | 23 +- .../src/include/fully_connected_grad_input_inst.h | 2 +- .../include/fully_connected_grad_weights_inst.h | 2 +- .../clDNN/src/include/fully_connected_inst.h | 2 +- .../clDNN/src/include/fused_conv_bn_scale_inst.h | 2 +- .../clDNN/src/include/fused_conv_eltwise_inst.h | 6 +- .../thirdparty/clDNN/src/include/gather_inst.h | 2 +- .../clDNN/src/include/gather_tree_inst.h | 49 + .../thirdparty/clDNN/src/include/gemm_inst.h | 2 +- .../thirdparty/clDNN/src/include/generic_layer.h | 35 - .../thirdparty/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 +- .../src/include/internal_primitive_type_base.h | 7 +- .../clDNN/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 +- .../clDNN/src/include/lstm_dynamic_input_inst.h | 2 +- .../clDNN/src/include/lstm_dynamic_inst.h | 2 +- .../clDNN/src/include/lstm_dynamic_timeloop_inst.h | 2 +- .../thirdparty/clDNN/src/include/lstm_elt_inst.h | 6 +- .../thirdparty/clDNN/src/include/lstm_gemm_inst.h | 2 +- .../thirdparty/clDNN/src/include/lstm_inst.h | 10 +- .../clDNN/src/include/max_unpooling_inst.h | 2 +- .../thirdparty/clDNN/src/include/memory_impl.h | 18 +- .../thirdparty/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 +- .../thirdparty/clDNN/src/include/network_impl.h | 5 +- .../thirdparty/clDNN/src/include/normalize_inst.h | 2 +- .../thirdparty/clDNN/src/include/one_hot_inst.h | 2 +- .../thirdparty/clDNN/src/include/pass_manager.h | 54 +- .../thirdparty/clDNN/src/include/permute_inst.h | 2 +- .../thirdparty/clDNN/src/include/pooling_inst.h | 2 +- .../thirdparty/clDNN/src/include/primitive_inst.h | 14 +- .../thirdparty/clDNN/src/include/primitive_type.h | 36 +- .../clDNN/src/include/primitive_type_base.h | 9 +- .../thirdparty/clDNN/src/include/prior_box_inst.h | 4 +- .../thirdparty/clDNN/src/include/program_helpers.h | 14 +- .../thirdparty/clDNN/src/include/program_impl.h | 14 +- .../thirdparty/clDNN/src/include/program_node.h | 91 +- .../thirdparty/clDNN/src/include/proposal_inst.h | 2 +- .../clDNN/src/include/pyramid_roi_align_inst.h | 2 +- .../thirdparty/clDNN/src/include/quantize_inst.h | 10 +- .../thirdparty/clDNN/src/include/reduce_inst.h | 2 +- .../clDNN/src/include/region_yolo_inst.h | 2 +- .../thirdparty/clDNN/src/include/reorder_inst.h | 2 +- .../thirdparty/clDNN/src/include/reorg_yolo_inst.h | 2 +- .../thirdparty/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 +- .../clDNN/src/include/scale_grad_weights_inst.h | 2 +- .../thirdparty/clDNN/src/include/scale_inst.h | 2 +- .../thirdparty/clDNN/src/include/select_inst.h | 2 +- .../clDNN/src/include/shuffle_channels_inst.h | 2 +- .../clDNN/src/include/sliding_window_utils.h | 4 +- .../thirdparty/clDNN/src/include/softmax_inst.h | 2 +- .../clDNN/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 +- .../thirdparty/clDNN/src/include/to_string_utils.h | 13 +- .../thirdparty/clDNN/src/include/topology_impl.h | 9 +- .../thirdparty/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 +- inference-engine/thirdparty/clDNN/src/lstm.cpp | 2 +- .../thirdparty/clDNN/src/lstm_dynamic.cpp | 2 +- .../thirdparty/clDNN/src/lstm_dynamic_input.cpp | 2 +- .../thirdparty/clDNN/src/lstm_dynamic_timeloop.cpp | 3 +- inference-engine/thirdparty/clDNN/src/lstm_elt.cpp | 2 +- .../thirdparty/clDNN/src/lstm_gemm.cpp | 2 +- .../thirdparty/clDNN/src/max_unpooling.cpp | 2 +- inference-engine/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 +- inference-engine/thirdparty/clDNN/src/network.cpp | 118 +- .../thirdparty/clDNN/src/normalize.cpp | 6 +- inference-engine/thirdparty/clDNN/src/one_hot.cpp | 4 +- inference-engine/thirdparty/clDNN/src/permute.cpp | 2 +- inference-engine/thirdparty/clDNN/src/pooling.cpp | 2 +- .../thirdparty/clDNN/src/prior_box.cpp | 2 +- inference-engine/thirdparty/clDNN/src/program.cpp | 215 +- .../thirdparty/clDNN/src/program_helpers.cpp | 10 +- .../thirdparty/clDNN/src/program_node.cpp | 26 +- inference-engine/thirdparty/clDNN/src/proposal.cpp | 2 +- .../thirdparty/clDNN/src/pyramid_roi_align.cpp | 2 +- inference-engine/thirdparty/clDNN/src/quantize.cpp | 18 +- inference-engine/thirdparty/clDNN/src/reduce.cpp | 2 +- .../thirdparty/clDNN/src/region_yolo.cpp | 2 +- inference-engine/thirdparty/clDNN/src/reorder.cpp | 8 +- .../thirdparty/clDNN/src/reorg_yolo.cpp | 2 +- inference-engine/thirdparty/clDNN/src/reshape.cpp | 2 +- .../thirdparty/clDNN/src/reverse_sequence.cpp | 2 +- .../thirdparty/clDNN/src/roi_pooling.cpp | 2 +- inference-engine/thirdparty/clDNN/src/scale.cpp | 2 +- .../thirdparty/clDNN/src/scale_grad_input.cpp | 2 +- .../thirdparty/clDNN/src/scale_grad_weights.cpp | 2 +- inference-engine/thirdparty/clDNN/src/select.cpp | 2 +- .../thirdparty/clDNN/src/shuffle_channels.cpp | 2 +- inference-engine/thirdparty/clDNN/src/softmax.cpp | 2 +- .../thirdparty/clDNN/src/softmax_loss_grad.cpp | 2 +- inference-engine/thirdparty/clDNN/src/split.cpp | 2 +- .../thirdparty/clDNN/src/strided_slice.cpp | 2 +- inference-engine/thirdparty/clDNN/src/tile.cpp | 2 +- inference-engine/thirdparty/clDNN/src/topology.cpp | 56 + .../thirdparty/clDNN/src/upsampling.cpp | 15 +- .../thirdparty/clDNN/tests/CMakeLists.txt | 4 +- .../clDNN/tests/module_tests/events_pool_test.cpp | 20 +- .../clDNN/tests/module_tests/gpu_toolkit_test.cpp | 16 +- .../tests/module_tests/test_uqr_distribution.cpp | 106 +- .../tests/test_cases/activation_grad_gpu_test.cpp | 48 +- .../test_cases/activation_simple_gpu_test.cpp | 206 +- .../tests/test_cases/add_reorders_gpu_test.cpp | 30 +- .../clDNN/tests/test_cases/apply_adam_gpu_test.cpp | 26 +- .../clDNN/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 +- .../clDNN/tests/test_cases/batch_norm_gpu_test.cpp | 61 +- .../tests/test_cases/batch_norm_grad_gpu_test.cpp | 16 +- .../test_cases/binary_convolution_gpu_test.cpp | 21 +- .../clDNN/tests/test_cases/border_gpu_test.cpp | 623 +- .../clDNN/tests/test_cases/broadcast_gpu_test.cpp | 14 +- .../clDNN/tests/test_cases/command_queue_test.cpp | 11 +- .../clDNN/tests/test_cases/condition_gpu_test.cpp | 50 +- .../clDNN/tests/test_cases/contract_gpu_test.cpp | 14 +- .../tests/test_cases/convolution_gpu_test.cpp | 401 +- .../test_cases/convolution_grad_input_gpu_test.cpp | 17 +- .../convolution_grad_weights_gpu_test.cpp | 42 +- .../clDNN/tests/test_cases/crop_gpu_test.cpp | 65 +- .../tests/test_cases/custom_gpu_primitive_test.cpp | 36 +- .../tests/test_cases/deconvolution_gpu_test.cpp | 90 +- .../test_cases/depth_concatenate_gpu_test.cpp | 71 +- .../tests/test_cases/depth_to_space_gpu_test.cpp | 12 +- .../tests/test_cases/detection_output_test.cpp | 13 +- .../clDNN/tests/test_cases/eltwise_gpu_test.cpp | 79 +- .../clDNN/tests/test_cases/embed_gpu_test.cpp | 18 +- .../tests/test_cases/fully_connected_gpu_test.cpp | 66 +- .../fully_connected_grad_input_gpu_test.cpp | 15 +- .../fully_connected_grad_weights_gpu_test.cpp | 22 +- .../test_cases/fused_conv_eltwise_gpu_test.cpp | 23 +- .../clDNN/tests/test_cases/fusings_gpu_test.cpp | 440 + .../clDNN/tests/test_cases/gather_gpu_test.cpp | 12 +- .../clDNN/tests/test_cases/gemm_gpu_test.cpp | 25 +- .../tests/test_cases/index_select_gpu_test.cpp | 27 +- .../clDNN/tests/test_cases/lookup_table_test.cpp | 15 +- .../tests/test_cases/lstm_dynamic_gpu_test.cpp | 222 +- .../clDNN/tests/test_cases/lstm_gpu_test.cpp | 118 +- .../tests/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 +- .../clDNN/tests/test_cases/one_hot_gpu_test.cpp | 12 +- .../clDNN/tests/test_cases/permute_gpu_test.cpp | 26 +- .../clDNN/tests/test_cases/pooling_gpu_test.cpp | 57 +- .../test_cases/propagate_constants_gpu_test.cpp | 18 +- .../clDNN/tests/test_cases/proposal_cpu_test.cpp | 12 +- .../clDNN/tests/test_cases/proposal_test_data.cpp | 3 - .../test_cases/pyramid_roi_align_gpu_test.cpp | 15 +- .../clDNN/tests/test_cases/quantize_gpu_test.cpp | 232 +- .../clDNN/tests/test_cases/reduce_gpu_test.cpp | 12 +- .../tests/test_cases/removing_output_node_test.cpp | 18 +- .../clDNN/tests/test_cases/reorder_gpu_test.cpp | 129 +- .../clDNN/tests/test_cases/reshape_gpu_test.cpp | 18 +- .../tests/test_cases/reverse_sequence_gpu_test.cpp | 12 +- .../clDNN/tests/test_cases/scale_gpu_test.cpp | 33 +- .../tests/test_cases/scale_grad_input_test.cpp | 12 +- .../tests/test_cases/scale_grad_weights_test.cpp | 16 +- .../clDNN/tests/test_cases/select_gpu_test.cpp | 12 +- .../tests/test_cases/shuffle_channels_test.cpp | 13 +- .../clDNN/tests/test_cases/softmax_gpu_test.cpp | 30 +- .../test_cases/softmax_loss_grad_gpu_test.cpp | 14 +- .../test_cases/spatial_concatenate_gpu_test.cpp | 13 +- .../clDNN/tests/test_cases/split_gpu_test.cpp | 19 +- .../clDNN/tests/test_cases/streams_test.cpp | 14 +- .../tests/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 +- .../tests/test_cases/trim_to_outputs_gpu_test.cpp | 15 +- .../clDNN/tests/test_cases/upsampling_gpu_test.cpp | 124 +- .../thirdparty/clDNN/tests/test_utils/float16.h | 2 - .../clDNN/tests/test_utils/instrumentation.cpp | 9 +- .../clDNN/tests/test_utils/instrumentation.h | 2 +- .../thirdparty/clDNN/tests/test_utils/random_gen.h | 2 - .../clDNN/tests/test_utils/test_utils.cpp | 18 +- .../thirdparty/clDNN/tests/test_utils/test_utils.h | 68 +- .../uniform_quantized_real_distribution.hpp | 37 - .../clDNN/tests_core_internal/CMakeLists.txt | 184 +- .../test_cases/graph_manipulation_gpu_test.cpp | 19 +- .../test_cases/prepare_conv_eltw_fusing.cpp | 12 +- inference-engine/thirdparty/fluid/checksum.txt | 2 +- .../thirdparty/fluid/modules/gapi/CMakeLists.txt | 1 + .../fluid/modules/gapi/cmake/standalone.cmake | 1 + .../fluid/modules/gapi/include/opencv2/gapi.hpp | 14 +- .../modules/gapi/include/opencv2/gapi/core.hpp | 55 +- .../modules/gapi/include/opencv2/gapi/cpu/core.hpp | 2 +- .../gapi/include/opencv2/gapi/cpu/gcpukernel.hpp | 15 +- .../include/opencv2/gapi/fluid/gfluidbuffer.hpp | 6 +- .../include/opencv2/gapi/fluid/gfluidkernel.hpp | 25 +- .../modules/gapi/include/opencv2/gapi/garg.hpp | 18 +- .../modules/gapi/include/opencv2/gapi/garray.hpp | 32 +- .../gapi/include/opencv2/gapi/gasync_context.hpp | 38 + .../modules/gapi/include/opencv2/gapi/gcall.hpp | 8 +- .../modules/gapi/include/opencv2/gapi/gcommon.hpp | 12 +- .../gapi/include/opencv2/gapi/gcompiled.hpp | 6 +- .../gapi/include/opencv2/gapi/gcompiled_async.hpp | 14 +- .../gapi/include/opencv2/gapi/gcompoundkernel.hpp | 21 +- .../gapi/include/opencv2/gapi/gcomputation.hpp | 12 +- .../include/opencv2/gapi/gcomputation_async.hpp | 19 +- .../modules/gapi/include/opencv2/gapi/gkernel.hpp | 193 +- .../modules/gapi/include/opencv2/gapi/gmat.hpp | 6 +- .../modules/gapi/include/opencv2/gapi/gmetaarg.hpp | 10 +- .../modules/gapi/include/opencv2/gapi/gproto.hpp | 12 +- .../modules/gapi/include/opencv2/gapi/gpu/core.hpp | 4 +- .../gapi/include/opencv2/gapi/gpu/ggpukernel.hpp | 4 +- .../gapi/include/opencv2/gapi/gpu/imgproc.hpp | 4 +- .../modules/gapi/include/opencv2/gapi/gscalar.hpp | 2 +- .../gapi/include/opencv2/gapi/gtransform.hpp | 103 + .../modules/gapi/include/opencv2/gapi/gtyped.hpp | 8 +- .../modules/gapi/include/opencv2/gapi/imgproc.hpp | 136 +- .../gapi/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 +- .../modules/gapi/include/opencv2/gapi/own/mat.hpp | 12 +- .../modules/gapi/include/opencv2/gapi/render.hpp | 113 + .../modules/gapi/include/opencv2/gapi/util/any.hpp | 2 +- .../gapi/include/opencv2/gapi/util/optional.hpp | 2 +- .../gapi/include/opencv2/gapi/util/util.hpp | 36 +- .../gapi/include/opencv2/gapi/util/variant.hpp | 4 +- .../gapi/perf/common/gapi_core_perf_tests.hpp | 2 +- .../gapi/perf/common/gapi_imgproc_perf_tests.hpp | 5 +- .../perf/common/gapi_imgproc_perf_tests_inl.hpp | 169 +- .../gapi/perf/cpu/gapi_core_perf_tests_cpu.cpp | 2 +- .../gapi/perf/cpu/gapi_imgproc_perf_tests_cpu.cpp | 17 +- .../perf/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 +- .../fluid/modules/gapi/src/api/gbackend_priv.hpp | 2 + .../fluid/modules/gapi/src/api/gcall.cpp | 2 +- .../fluid/modules/gapi/src/api/gcomputation.cpp | 12 +- .../fluid/modules/gapi/src/api/gkernel.cpp | 129 +- .../thirdparty/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 +- .../fluid/modules/gapi/src/api/kernels_core.cpp | 13 +- .../fluid/modules/gapi/src/api/kernels_imgproc.cpp | 30 +- .../fluid/modules/gapi/src/api/operators.cpp | 8 +- .../fluid/modules/gapi/src/api/render.cpp | 91 + .../fluid/modules/gapi/src/api/render_priv.hpp | 30 + .../gapi/src/backends/common/gcompoundbackend.cpp | 2 +- .../gapi/src/backends/common/gcompoundkernel.cpp | 2 +- .../modules/gapi/src/backends/cpu/gcpubackend.cpp | 10 +- .../modules/gapi/src/backends/cpu/gcpubackend.hpp | 6 +- .../modules/gapi/src/backends/cpu/gcpucore.cpp | 23 +- .../modules/gapi/src/backends/cpu/gcpuimgproc.cpp | 81 +- .../modules/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 +- .../modules/gapi/src/backends/fluid/gfluidcore.cpp | 16 +- .../gapi/src/backends/fluid/gfluidimgproc.cpp | 140 +- .../backends/fluid/gfluidimgproc_func.dispatch.cpp | 39 +- .../gapi/src/backends/fluid/gfluidimgproc_func.hpp | 25 +- .../src/backends/fluid/gfluidimgproc_func.simd.hpp | 577 +- .../modules/gapi/src/backends/ocl/goclbackend.cpp | 26 +- .../modules/gapi/src/backends/ocl/goclbackend.hpp | 6 +- .../modules/gapi/src/backends/ocl/goclcore.cpp | 4 +- .../modules/gapi/src/backends/ocl/goclcore.hpp | 2 +- .../modules/gapi/src/backends/ocl/goclimgproc.cpp | 4 +- .../modules/gapi/src/backends/ocl/goclimgproc.hpp | 2 +- .../modules/gapi/src/backends/ocl/goclkernel.cpp | 2 +- .../fluid/modules/gapi/src/compiler/gcompiled.cpp | 4 +- .../fluid/modules/gapi/src/compiler/gcompiler.cpp | 28 +- .../fluid/modules/gapi/src/compiler/gcompiler.hpp | 6 +- .../modules/gapi/src/compiler/gislandmodel.hpp | 4 +- .../fluid/modules/gapi/src/compiler/gmodel.cpp | 6 +- .../fluid/modules/gapi/src/compiler/gmodel.hpp | 8 +- .../modules/gapi/src/compiler/gmodelbuilder.hpp | 4 +- .../modules/gapi/src/compiler/passes/dump_dot.cpp | 2 +- .../modules/gapi/src/compiler/passes/exec.cpp | 2 +- .../modules/gapi/src/compiler/passes/helpers.cpp | 2 +- .../modules/gapi/src/compiler/passes/kernels.cpp | 10 +- .../modules/gapi/src/compiler/passes/meta.cpp | 2 +- .../modules/gapi/src/compiler/passes/passes.hpp | 5 +- .../fluid/modules/gapi/src/executor/gasync.cpp | 129 +- .../fluid/modules/gapi/src/executor/gexecutor.cpp | 28 +- .../thirdparty/fluid/modules/gapi/src/precomp.hpp | 12 +- .../gapi/test/common/gapi_compoundkernel_tests.cpp | 26 +- .../modules/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 +- .../gapi/test/common/gapi_imgproc_tests_inl.hpp | 439 +- .../gapi/test/common/gapi_operators_tests.hpp | 10 +- .../gapi/test/common/gapi_operators_tests_inl.hpp | 30 +- .../modules/gapi/test/common/gapi_render_tests.cpp | 9 + .../modules/gapi/test/common/gapi_render_tests.hpp | 73 + .../gapi/test/common/gapi_render_tests_inl.hpp | 96 + .../modules/gapi/test/common/gapi_tests_common.hpp | 269 +- .../gapi/test/common/gapi_tests_helpers.hpp | 67 + .../modules/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 +- .../gapi/test/cpu/gapi_imgproc_tests_fluid.cpp | 204 +- .../gapi/test/cpu/gapi_operators_tests_cpu.cpp | 62 +- .../gapi/test/cpu/gapi_operators_tests_fluid.cpp | 59 +- .../gapi/test/cpu/gapi_render_tests_cpu.cpp | 66 + .../fluid/modules/gapi/test/gapi_async_test.cpp | 269 +- .../modules/gapi/test/gapi_basic_hetero_tests.cpp | 2 +- .../fluid/modules/gapi/test/gapi_desc_tests.cpp | 2 +- .../gapi/test/gapi_fluid_parallel_rois_test.cpp | 315 + .../modules/gapi/test/gapi_fluid_resize_test.cpp | 7 +- .../fluid/modules/gapi/test/gapi_fluid_test.cpp | 6 +- .../modules/gapi/test/gapi_fluid_test_kernels.cpp | 4 +- .../modules/gapi/test/gapi_fluid_test_kernels.hpp | 2 +- .../modules/gapi/test/gapi_gcomputation_tests.cpp | 71 +- .../fluid/modules/gapi/test/gapi_gpu_test.cpp | 2 +- .../fluid/modules/gapi/test/gapi_kernel_tests.cpp | 389 +- .../fluid/modules/gapi/test/gapi_mock_kernels.hpp | 2 +- .../modules/gapi/test/gapi_sample_pipelines.cpp | 16 + .../modules/gapi/test/gapi_transform_tests.cpp | 189 + .../fluid/modules/gapi/test/gapi_util_tests.cpp | 2 +- .../modules/gapi/test/gpu/gapi_core_tests_gpu.cpp | 285 +- .../gapi/test/gpu/gapi_imgproc_tests_gpu.cpp | 234 +- .../gapi/test/gpu/gapi_operators_tests_gpu.cpp | 60 +- .../test/internal/gapi_int_gmodel_builder_test.cpp | 2 +- .../test/internal/gapi_int_recompilation_test.cpp | 9 +- .../modules/gapi/test/opencl_kernels_test_gapi.hpp | 6 +- .../modules/gapi/test/own/gapi_types_tests.cpp | 2 +- .../fluid/modules/gapi/test/own/mat_tests.cpp | 2 +- .../fluid/modules/gapi/test/own/scalar_tests.cpp | 2 +- .../fluid/modules/gapi/test/test_precomp.hpp | 24 +- .../fluid/modules/gapi/test/util/any_tests.cpp | 2 +- .../modules/gapi/test/util/optional_tests.cpp | 2 +- .../fluid/modules/gapi/test/util/variant_tests.cpp | 2 +- inference-engine/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 +- .../thirdparty/mkl-dnn/src/common/primitive.hpp | 3 +- .../thirdparty/mkl-dnn/src/cpu/cpu_isa_traits.hpp | 4 + .../thirdparty/mkl-dnn/src/cpu/cpu_memory.hpp | 4 +- .../mkl-dnn/src/cpu/jit_uni_bin_conv_kernel.cpp | 56 +- .../thirdparty/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 + .../thirdparty/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 +- .../movidius/XLink/shared/XLinkPlatform_tool.h | 41 + .../movidius/XLink/shared/XLinkPrivateDefines.h | 79 +- .../movidius/XLink/shared/XLinkPublicDefines.h | 24 +- .../movidius/XLink/shared/XLinkVersion.h | 1 - .../thirdparty/movidius/XLink/shared/XLink_tool.h | 100 + .../thirdparty/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 + .../thirdparty/movidius/mvnc/include/mvnc_data.h | 4 + .../thirdparty/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 +- .../thirdparty/movidius/shared/include/mvLog.h | 4 + .../movidius/shared/include/mvStringUtils.h | 20 +- .../thirdparty/movidius/shared/src/mvStringUtils.c | 20 +- .../thirdparty/movidius/watchdog/watchdog.cpp | 23 +- .../thirdparty/movidius/watchdog/watchdog.h | 3 +- inference-engine/thirdparty/ngraph.cmake | 12 +- inference-engine/thirdparty/stb_lib/CMakeLists.txt | 16 +- inference-engine/tools/CMakeLists.txt | 1 - .../tools/accuracy_checker_tool/accuracy_check.py | 2 +- .../accuracy_checker_tool/convert_annotation.py | 2 +- inference-engine/tools/benchmark_tool/README.md | 198 +- inference-engine/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 +- inference-engine/tools/calibration_tool/README.md | 170 +- .../statistics_collector/CMakeLists.txt | 6 +- .../statistics_collector/data_stats.cpp | 2 +- .../statistics_collector/data_stats.hpp | 4 +- .../calibration_tool/statistics_collector/main.cpp | 13 +- .../statistics_collector/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 +- inference-engine/tools/vpu/vpu_compile/main.cpp | 2 +- .../tools/vpu/vpu_perfcheck/CMakeLists.txt | 69 + inference-engine/tools/vpu/vpu_perfcheck/main.cpp | 788 + inference-engine/tools/vpu/vpu_profile/README.md | 11 +- inference-engine/tools/vpu/vpu_profile/main.cpp | 2 +- model-optimizer/README.md | 33 +- model-optimizer/extensions/back/CutMemory.py | 65 + model-optimizer/extensions/back/CutMemory_test.py | 71 + .../extensions/back/FuseReshapesSequence.py | 5 + model-optimizer/extensions/back/Gather0D.py | 61 + model-optimizer/extensions/back/ReduceToPooling.py | 14 +- .../extensions/back/ReduceToPooling_test.py | 52 +- .../extensions/back/ScalarConstNormalize.py | 34 + model-optimizer/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 + .../extensions/front/onnx/constant_of_shape_ext.py | 33 + .../front/onnx/constant_of_shape_to_broadcast.py | 41 + .../extensions/front/onnx/elementwise_ext.py | 62 +- .../extensions/front/onnx/expand_ext.py | 28 + model-optimizer/extensions/front/onnx/floor_ext.py | 28 + .../extensions/front/onnx/not_ext.py | 15 +- .../extensions/front/onnx/reduce_min_ext.py | 33 + model-optimizer/extensions/front/onnx/slice_ext.py | 4 +- model-optimizer/extensions/front/onnx/top_k_ext.py | 30 + .../extensions/front/reduce_axis_normalizer.py | 2 + .../front/tf/BatchToSpaceNDToUpsample.py | 108 + .../extensions/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 + model-optimizer/extensions/front/tf/swish_test.py | 56 + model-optimizer/extensions/front/tf/unique_ext.py | 39 + .../extensions/middle/BiasAddBroadcasting.py | 75 + model-optimizer/extensions/middle/Cast.py | 15 +- .../extensions/middle/EltwiseInputReshape.py | 24 +- model-optimizer/extensions/middle/InsertSelect.py | 146 + .../extensions/middle/InsertSelect_test.py | 271 + .../extensions/middle/RemoveDuplicationMemory.py | 127 +- .../middle/RemoveDuplicationMemory_test.py | 70 +- .../extensions/middle/RemoveUselessCrops.py | 46 +- .../extensions/middle/RemoveUselessCrops_test.py | 87 + .../middle/ReplaceMemoryOffsetWithSplice.py | 60 +- .../middle/ReplaceMemoryOffsetWithSplice_test.py | 24 +- model-optimizer/extensions/middle/ReplacePNorm.py | 62 + .../middle/ReplacePNormNodePattern_test.py | 76 + .../extensions/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 +- model-optimizer/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 +- .../extensions/ops/roifeatureextractor_onnx.py | 8 +- .../extensions/ops/sparse_fill_empty_rows.py | 84 + .../extensions/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 + model-optimizer/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 +- .../common/partial_infer/multi_box_detection.py | 8 +- .../front/common/partial_infer/space_to_batch.py | 10 +- .../mo/front/common/partial_infer/split.py | 7 +- .../front/kaldi/extractors/pnorm_component_ext.py | 58 + .../kaldi/extractors/pnorm_component_ext_test.py | 41 + .../front/kaldi/extractors/splice_component_ext.py | 7 + model-optimizer/mo/front/kaldi/loader/loader.py | 51 +- .../mo/front/kaldi/loader/loader_test.py | 26 +- model-optimizer/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 +- model-optimizer/mo/utils/pipeline_config_test.py | 3 + model-optimizer/mo/utils/unittest/graph.py | 90 +- model-optimizer/mo/utils/unittest/ir_engine.py | 332 + .../mo/utils/unittest/ir_engine_test.py | 136 + ...mxnet_synthetic_gru_bidirectional_FP16_1_v6.bin | Bin 0 -> 222768 bytes ...mxnet_synthetic_gru_bidirectional_FP16_1_v6.xml | 626 + ...thetic_gru_bidirectional_FP16_1_v6_negative.xml | 626 + model-optimizer/mo/utils/versions_checker.py | 55 +- model-optimizer/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/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 - .../accuracy_checker/adapters/classification.py | 45 - .../accuracy_checker/adapters/detection.py | 501 - .../accuracy_checker/adapters/dummy_adapters.py | 64 - .../accuracy_checker/adapters/hit_ratio.py | 47 - .../accuracy_checker/adapters/image_processing.py | 35 - .../accuracy_checker/adapters/pose_estimation.py | 331 - .../accuracy_checker/adapters/reidentification.py | 58 - .../accuracy_checker/adapters/segmentation.py | 74 - .../accuracy_checker/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 - .../accuracy_checker/annotation_converters/lfw.py | 111 - .../annotation_converters/mapillary_20.py | 79 - .../annotation_converters/market1501.py | 41 - .../accuracy_checker/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 - .../annotation_converters/vgg_face_regression.py | 64 - .../annotation_converters/wider.py | 63 - .../accuracy_checker/config/__init__.py | 48 - .../accuracy_checker/config/config_reader.py | 414 - .../accuracy_checker/config/config_validator.py | 341 - .../accuracy_checker/data_readers/__init__.py | 48 - .../accuracy_checker/data_readers/data_reader.py | 267 - tools/accuracy_checker/accuracy_checker/dataset.py | 155 - .../accuracy_checker/dependency.py | 108 - .../accuracy_checker/evaluators/__init__.py | 8 - .../accuracy_checker/evaluators/model_evaluator.py | 162 - .../evaluators/pipeline_evaluator.py | 229 - .../accuracy_checker/launcher/__init__.py | 35 - .../launcher/caffe_installation_readme.md | 56 - .../accuracy_checker/launcher/caffe_launcher.py | 135 - .../launcher/caffe_launcher_readme.md | 24 - .../accuracy_checker/launcher/dlsdk_launcher.py | 474 - .../launcher/dlsdk_launcher_readme.md | 55 - .../accuracy_checker/launcher/dummy_launcher.py | 73 - .../accuracy_checker/launcher/input_feeder.py | 151 - .../accuracy_checker/launcher/launcher.py | 164 - .../accuracy_checker/launcher/loaders/__init__.py | 26 - .../accuracy_checker/launcher/loaders/loader.py | 54 - .../launcher/loaders/pickle_loader.py | 34 - .../launcher/loaders/xml_loader.py | 29 - .../accuracy_checker/launcher/model_conversion.py | 201 - tools/accuracy_checker/accuracy_checker/logging.py | 134 - tools/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 - .../accuracy_checker/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 - .../accuracy_checker/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 - .../accuracy_checker/metrics/text_detection.py | 119 - .../pipeline_connectors/__init__.py | 7 - .../pipeline_connectors/connectors.py | 69 - .../accuracy_checker/postprocessor/README.md | 40 - .../accuracy_checker/postprocessor/__init__.py | 69 - .../accuracy_checker/postprocessor/cast_to_int.py | 67 - .../accuracy_checker/postprocessor/clip_boxes.py | 63 - .../accuracy_checker/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 - .../postprocessor/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 - .../accuracy_checker/preprocessor/preprocessors.py | 642 - .../accuracy_checker/presenters.py | 159 - .../accuracy_checker/progress_reporters.py | 100 - .../accuracy_checker/representation/__init__.py | 103 - .../representation/base_representation.py | 42 - .../character_recognition_representation.py | 31 - .../classification_representation.py | 44 - .../representation/detection_representation.py | 87 - .../representation/hit_ratio_representation.py | 40 - .../representation/multilabel_recognition.py | 32 - .../pose_estimation_representation.py | 63 - .../representation/regression_representation.py | 72 - .../representation/reid_representation.py | 42 - .../representation/representaton_container.py | 78 - .../representation/segmentation_representation.py | 94 - .../super_resolution_representation.py | 67 - .../text_detection_representation.py | 46 - tools/accuracy_checker/accuracy_checker/utils.py | 385 - .../configs/face-detection-adas-0001.yml | 106 - .../configs/face-detection-retail-0004.yml | 113 - .../configs/face-reidentification-retail-0095.yml | 96 - .../configs/human-pose-estimation-0001.yml | 155 - .../configs/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 - tools/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 - .../accuracy_checker/tests/test_caffe_launcher.py | 77 - tools/accuracy_checker/tests/test_config_reader.py | 1295 -- .../tests/test_config_validator.py | 385 - tools/accuracy_checker/tests/test_dataset.py | 220 - tools/accuracy_checker/tests/test_dependency.py | 89 - .../tests/test_detection_metrics.py | 459 - .../accuracy_checker/tests/test_dlsdk_launcher.py | 1121 -- tools/accuracy_checker/tests/test_input_feeder.py | 255 - .../tests/test_metric_evaluator.py | 482 - .../tests/test_model_conversion.py | 80 - .../accuracy_checker/tests/test_model_evaluator.py | 147 - tools/accuracy_checker/tests/test_postprocessor.py | 1070 -- tools/accuracy_checker/tests/test_preprocessor.py | 611 - tools/accuracy_checker/tests/test_presenter.py | 552 - .../tests/test_regression_metrics.py | 342 - tools/accuracy_checker/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 + tools/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 +- 1735 files changed, 72097 insertions(+), 58975 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/depth_to_space.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%) create mode 100644 inference-engine/thirdparty/clDNN/kernel_selector/core/actual_kernels/gather_tree/gather_tree_kernel_selector.cpp 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 8d1dd32..139578a 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 0d449c9..f41d9df 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 4d79782..53b1783 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 71c9007..3e89b16 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 d9a6918..ebe82ee 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 00a5b8e..682f2e5 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 52e0fef..a27143d 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 bd837be..930a640 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 9fa537d..2d7c827 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 8265701..97e8c5e 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 6a5442f..ad15859 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 ee57890..b1a355f 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 c7911a6..bdfa5f3 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 3bbb0cf..0d49227 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 0000000..1550163 --- /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 9fca214..bba853f 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 e874177..9cee626 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 e69de29..0000000 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 7bb4b20..0000000 --- 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 ccee155..0000000 --- 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 cf801fe..0000000 --- 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 00a2945..0000000 --- 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 3e8b59b..0000000 --- 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 c1f0afe..0000000 --- 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 cf1139a..0000000 --- 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 98691c7..7812e07 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 b409f5d..18ada6f 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 9d78438..c0178e9 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 0000000..7b91825 --- /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 0000000..2027469 --- /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 272432b..9671006 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 d79a32a..12fd329 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 371ffcf..c9a1ad3 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 @@ -69,6 +69,11 @@ PyObject* parse_parameter(const InferenceEngine::Parameter & param){ 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()) { auto val = param.as(); @@ -98,6 +103,15 @@ PyObject* parse_parameter(const InferenceEngine::Parameter & param){ } 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>()){ auto val = param.as>(); @@ -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 59e6320..08cb8cb 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 95db6d9..e838a26 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 a649bd2..8e4871d 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 5746467..0df940f 100644 --- a/inference-engine/include/builders/ie_concat_layer.hpp +++ b/inference-engine/include/builders/ie_concat_layer.hpp @@ -72,9 +72,6 @@ public: * @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 9f4f5bb..cef99ef 100644 --- a/inference-engine/include/cpp/ie_cnn_network.h +++ b/inference-engine/include/cpp/ie_cnn_network.h @@ -89,7 +89,7 @@ public: virtual OutputsDataMap getOutputsInfo() const { OutputsDataMap outputs; actual->getOutputsInfo(outputs); - return std::move(outputs); + return outputs; } /** @@ -99,7 +99,7 @@ public: virtual InputsDataMap getInputsInfo() const { InputsDataMap inputs; actual->getInputsInfo(inputs); - return std::move(inputs); + return inputs; } /** @@ -223,7 +223,7 @@ public: } } } - 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 2eb235a..6a3e716 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 @@ public: } /** - * @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 @@ public: } /** - * @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 @@ public: } /** - * @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 @@ public: } /** - * @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 @@ public: } /** - * @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 @@ public: } /** - *@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 @@ public: } /** - * @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 @@ public: } /** - * @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 @@ public: 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 5d1eeb4..9e42226 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 @@ public: } /** - * 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 @@ public: 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 d20fcae..d1867c8 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 8d5744a..093b16c 100644 --- a/inference-engine/include/cpp/ie_plugin_cpp.hpp +++ b/inference-engine/include/cpp/ie_plugin_cpp.hpp @@ -34,6 +34,7 @@ public: /** * @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 @@ public: /** * @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 @@ public: /** * @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 @@ public: /** * @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 @@ public: /** * @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 @@ public: /** * @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 @@ public: /** * @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 @@ public: /** * @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 @@ public: /** * @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 @@ public: * @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 @@ public: /** * @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 @@ public: /** * @brief Converts InferenceEngine to InferenceEnginePluginPtr pointer - * @brief Returns wrapped object + * @return Wrapped object */ operator InferenceEngine::InferenceEnginePluginPtr() { return actual; @@ -162,7 +183,7 @@ public: /** * @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 70b5050..e7be114 100644 --- a/inference-engine/include/details/ie_inetwork_iterator.hpp +++ b/inference-engine/include/details/ie_inetwork_iterator.hpp @@ -71,8 +71,8 @@ public: private: std::vector> 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 d4801ba..850f905 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 0000000..1adca7e --- /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 fb17d07..2eb3621 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 08b6838..af2e0d1 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 @@ public: * @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 2394c0c..e9132f1 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 d2628ad..12d341d 100644 --- a/inference-engine/include/ie_blob.h +++ b/inference-engine/include/ie_blob.h @@ -678,7 +678,7 @@ public: if (_handle != nullptr) { getAllocator()->free(_handle); } - _handle = getAllocator()->alloc(byteSize()); + _handle = getAllocator()->alloc(size() * sizeof(T)); } /** @@ -779,10 +779,7 @@ protected: * @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 9e7241d..581b877 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 b473572..d0d2660 100644 --- a/inference-engine/include/ie_core.hpp +++ b/inference-engine/include/ie_core.hpp @@ -35,7 +35,7 @@ public: /** * @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 @@ public: /** @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 @@ public: * * * - * + * * - * + * ``` + * * - `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 75e906f..d43824d 100644 --- a/inference-engine/include/ie_data.h +++ b/inference-engine/include/ie_data.h @@ -74,6 +74,7 @@ public: * @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 @@ public: * @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 @@ public: /** * @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 c166021..fef50c2 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 2547fb6..fb4847d 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 @@ public: } 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 7dafa40..a644038 100644 --- a/inference-engine/include/ie_iexecutable_network.hpp +++ b/inference-engine/include/ie_iexecutable_network.hpp @@ -38,39 +38,51 @@ public: 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 @@ public: * @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 e1510d9..22c9633 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 c1e3fae..6ef3506 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 @@ public: 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 d922f5b..e8dc9ee 100644 --- a/inference-engine/include/ie_iinfer_request.hpp +++ b/inference-engine/include/ie_iinfer_request.hpp @@ -33,12 +33,18 @@ public: /** 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 @@ public: /** * @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 @@ public: /** * @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 @@ public: /** * @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 @@ public: /** * @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 @@ public: /** * @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 @@ public: /** * @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 @@ public: /** * @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 @@ public: /** * @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 @@ public: /** * @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 @@ public: /** * @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 66f43fa..25941b7 100644 --- a/inference-engine/include/ie_layers.h +++ b/inference-engine/include/ie_layers.h @@ -139,6 +139,16 @@ public: 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 @@ public: * @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 @@ public: 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 @@ public: 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 { @@ -589,10 +621,6 @@ public: return *this; } /** - * @brief move assignment operator - */ - ConvolutionLayer& operator = (ConvolutionLayer &&) = default; - /** * @brief copy constructor */ ConvolutionLayer(const ConvolutionLayer & that) : WeightableLayer(that) { @@ -697,11 +725,6 @@ public: return *this; } /** - * @brief move assignment operator - */ - PoolingLayer& operator = (PoolingLayer &&) = default; - - /** * @brief copy constructor */ PoolingLayer(const PoolingLayer & that) : CNNLayer(that) { @@ -800,10 +823,6 @@ public: return *this; } /** - * @brief move assignment operator - */ - BinaryConvolutionLayer& operator = (BinaryConvolutionLayer &&) = default; - /** * @brief copy constructor */ BinaryConvolutionLayer(const BinaryConvolutionLayer & that) : WeightableLayer(that) { @@ -1020,7 +1039,7 @@ public: 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 @@ public: * - 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 @@ -1604,6 +1636,19 @@ public: /** + * @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 @@ public: }; +/** + * @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 38901c0..45e07ba 100644 --- a/inference-engine/include/ie_layouts.h +++ b/inference-engine/include/ie_layouts.h @@ -226,6 +226,7 @@ public: 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 a214b10..4d9ada8 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 9114118..67a044c 100644 --- a/inference-engine/include/ie_parameter.hpp +++ b/inference-engine/include/ie_parameter.hpp @@ -35,7 +35,9 @@ public: * @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 @@ private: } 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 9229a74..6caf1cf 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 @@ public: * @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 @@ public: * @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 a3764e8..2d14316 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 41b4e41..67730fc 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 @@ public: /** * @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 @@ public: /** * @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 c4d63a5..cbfc19b 100644 --- a/inference-engine/include/ie_precision.hpp +++ b/inference-engine/include/ie_precision.hpp @@ -59,7 +59,8 @@ public: /** * @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 @@ public: } /** - * @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 @@ public: } 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 @@ public: 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 31afb20..cf745f3 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 ccbf3e8..69d092a 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 0000000..a5f037a --- /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 0000000..d2a87c1 --- /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 69d04f1..5462acf 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 d354f64..d3a094c 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 b1bde47..9e4e9d5 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 6b6991f..c9e2da2 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 892bd5d..ca71319 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 821f4fe..2f8005d 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 f7e0bb2..58eae04 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 4c2634d..0dbd8c3 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 c4011c4..a8c9caf 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 e9111d9..4fa5611 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 @@ public: ~DIR() { if (!next) delete next; + next = nullptr; FindClose(hFind); } @@ -96,7 +103,7 @@ public: }; -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 fbcd249..58efdc6 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 5edfea8..c6adc5e 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 @@ public: * @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 @@ public: } 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 @@ public: * @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 @@ public: * @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 0d7db2e..90833a5 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 9d32bd1..0d796de 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 2370670..4ef6a14 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 0046c97..0785ee7 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 efc38ca..be52db4 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 63a18b2..02f8669 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 aa2072c..0000000 --- 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 bd1793f..63fda2a 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 211f660..b618084 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 cf863fb..29b3491 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 e948f29..ee4cd32 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 @@ public: 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 8aba309..a43fc91 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 1fb3190..aa2baa2 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 77fcfe4..7a3ce7a 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 b1f98c9..928491b 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 48c414d..aeda853 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 f2b7e10..8065e71 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 6ca467f..f80c76a 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 cd8cf98..7c19b50 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 25c7310..325670e 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 @@ public: 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 @@ public: Tan, Gemm, OneHot, + Convert, + GatherTree, NO_TYPE }; using GenericBlobMap = std::map; @@ -223,7 +231,8 @@ private: 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 @@ private: 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 c0767c2..ada1385 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 1dad92e..7fab969 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 @@ protected: 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 @@ protected: } }; -}; // 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 91a26ec..e05e80e 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 8de54b3..a76b825 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 @@ public: 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 @@ public: } 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 @@ public: 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 @@ public: } 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 @@ private: 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 6c1f243..fa06d3f 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 898149b..ea9b79f 100644 --- a/inference-engine/src/extension/ext_gather.cpp +++ b/inference-engine/src/extension/ext_gather.cpp @@ -30,44 +30,28 @@ public: 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 @@ public: 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 @@ public: } 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 4bcd9fa..533821c 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 ba53dc8..6effeef 100644 --- a/inference-engine/src/extension/ext_log_softmax.cpp +++ b/inference-engine/src/extension/ext_log_softmax.cpp @@ -31,6 +31,8 @@ public: 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 0000000..e90a084 --- /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 39ff4a4..338e054 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 16d6dec..2334792 100644 --- a/inference-engine/src/extension/ext_reduce.cpp +++ b/inference-engine/src/extension/ext_reduce.cpp @@ -111,6 +111,9 @@ public: } } + 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 @@ public: 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 d4d187d..e3f717d 100644 --- a/inference-engine/src/extension/ext_resample.cpp +++ b/inference-engine/src/extension/ext_resample.cpp @@ -54,6 +54,11 @@ public: 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 @@ public: 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 0000000..0ec01be --- /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 c00e76f..933b26f 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 0000000..e07d54a --- /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 4773770..bb20a60 100644 --- a/inference-engine/src/extension/ext_strided_slice.cpp +++ b/inference-engine/src/extension/ext_strided_slice.cpp @@ -168,6 +168,9 @@ public: 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 0000000..939128c --- /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 f5972af..6db8210 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 a3d0be7..50b8316 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 84f6c1d..e45bc1f 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 e9d149b..aceb42a 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 2ea35ac..77c0c78 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 c15343c..9967b92 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 f0d9fc3..e4ae5cc 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 efe3490..528e789 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 6ea717e..82d2d8c 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 130a094..dab1e15 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 416b590..b68be9e 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 3f3fcf3..8637545 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 8e704fd..6f46c31 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 b6d3b03..aa8e458 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 91eb189..fdb1b0f 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 0bd1e7e..3408a4c 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 a282b01..b4704e7 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 5057ce0..7dcdbb4 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 475cc0b..c922560 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 a402b88..682212b 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 5798173..11ffa39 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 3252632..4a6fdac 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 e76f717..ecd2962 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 f3514ed..f4b34ad 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 08a356d..28f4832 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 { @@ -82,8 +84,39 @@ InferenceEngine::Blob::Ptr make_custom_blob(Args &&... 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 2e4fb74..0873ff1 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 99af91c..d7c0dca 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 9691ee3..d2b179c 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 5633cb2..7561cb8 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 b6138ed..3cc1cc2 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 @@ public: virtual ~IInferencePluginAPI() = default; }; -class DeviceIDParser { +class INFERENCE_ENGINE_API_CLASS(DeviceIDParser) { std::string deviceName; std::string deviceID; @@ -81,6 +82,7 @@ public: 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 aba3c13..eb8fd8d 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 @@ public: 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 1608293..d5f2018 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 @@ public: 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 18ed1c1..414f65d 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 @@ public: 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 7d5a9fd..10f11e7 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 387c19b..cebc688 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 0000000..8be8cae --- /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 2e9200d..6f90705 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 eab19b7..ab7dbc1 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 609a133..3c8e540 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 c8528f8..0000000 --- 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 6283695..9fbc310 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 @@ public: 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 83351a1..8a02da4 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 7c01e37..94c882d 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 3146ec7..06dce03 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 9c4c319..678b0ba 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 fb06cf3..e078e0c 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 62d9924..51db65c 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 a6fcf44..67b6b07 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 3fc177b..0c77a67 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 2867cd1..e4ef8a6 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 @@ public: 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 f5d6e10..85d7454 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 b7f5ff5..f37889f 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 0072f01..2a295a8 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 @@ protected: /** * @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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 fd6b7df..771c52c 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 d61bdd3..0588df9 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 ea85d68..52d371e 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 6dfdfc4..061bc41 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 6588d4c..b8abf79 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 489ab24..1a2f078 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 b5f8b32..bbdce7b 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 8b84be6..3712ba7 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 e386575..b2fb318 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 9721c5e..6a25411 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 db740be..bf15718 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 cf4578f..0000000 --- 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 2e9c33d..663bb80 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 @@ public: } 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 744093a..1b731e3 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 0000000..3b62e2b --- /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 ee40144..a2662cd 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 @@ public: 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 0000000..2d9efcb --- /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 0000000..353529a --- /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 8846bcb..bf562cf 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 @@ public: 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 0000000..06fe26a --- /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 076cb00..a344a71 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 @@ private: 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 0000000..a085c7d --- /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 8435ba6..7116098 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 @@ public: 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 fc4f39d..8951469 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 @@ public: 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 @@ public: 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 @@ public: } 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 904c16b..10945ef 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 @@ public: 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 @@ public: 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 d874ba3..4f5d3c3 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 45883dd..304f0e8 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 0000000..918a5cc --- /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 b8e40c5..865abd3 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 @@ public: _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 @@ public: 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 @@ public: } 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 fc88216..9c43eff 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 @@ public: } }; - 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 @@ public: 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 d9afcce..8cd4dfa 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 ef96d57..de159e8 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 @@ public: _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 @@ public: 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 0000000..bf88a02 --- /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 0000000..a16af70 --- /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 0000000..50b85fd --- /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 c5da316..3360dd3 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 @@ public: 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 d9e7682..45b6d58 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: } } - 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 @@ private: }); } - 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 @@ private: InferenceEngine::SizeVector out_dims; InferenceEngine::SizeVector our_dims; - const float* src_data; }; /** @@ -381,7 +408,7 @@ public: _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 9834ad9..722e65e 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 923fa3f..0000000 --- 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 a712203..0000000 --- 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 7852139..0000000 --- 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 6a9a13d..0000000 --- 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 19e76f9..0000000 --- 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 634a705..0000000 --- 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 ab630a2..0000000 --- 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 d17fede..0000000 --- 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 5a3eeb8..0000000 --- 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 bcefc62..0000000 --- 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 d100d51..4fa5611 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 @@ public: 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 @@ public: 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 271bc56..201545b 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 af464ec..9debb88 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 0000000..b7916b5 --- /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 0000000..2f70ac2 --- /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 e687e43..96bd3c7 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 494accc..6cdf8c0 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 f02d982..057fb82 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 @@ private: }; }; - -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 5a3728e..7ff46e5 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 0ab8ed5..68929cd 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 0a047dd..f11a9a3 100644 --- a/inference-engine/src/mkldnn_plugin/mkldnn_memory.h +++ b/inference-engine/src/mkldnn_plugin/mkldnn_memory.h @@ -101,7 +101,7 @@ public: 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 e70caab..4620b48 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 0881b85..5e09ea9 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 @@ private: 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 0000000..aff1134 --- /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 0000000..a2d94ab --- /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 3739d31..7edda28 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 60206d8..04e61d0 100644 --- a/inference-engine/src/mkldnn_plugin/mkldnn_node.h +++ b/inference-engine/src/mkldnn_plugin/mkldnn_node.h @@ -402,31 +402,28 @@ public: 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 @@ protected: 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 @@ private: 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 6ca5ba6..9fbfcd1 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 4ae7fed..f46c70d 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 68ad8a3..a0c2159 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 5fa64e3..1dad240 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 f17a3ba..0f6320c 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 1184896..5ff69e6 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 01a8172..5d40016 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 ab8e2ac..7609e05 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 326f9e4..030dc7f 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 3c17bfd..7d9e704 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 d389d16..6ea799d 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 8d82728..a8013ad 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 1cacf76..959a4e3 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 1e6d249..14da20c 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 a5323a4..184252e 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 @@ public: 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 f8a4cad..88ef229 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 2f909bd..cffd6a8 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 737d98e..875f260 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 1cd4978..01b93ff 100644 --- a/inference-engine/src/vpu/CMakeLists.txt +++ b/inference-engine/src/vpu/CMakeLists.txt @@ -3,22 +3,10 @@ # # -# 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 0000000..969670c --- /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 0000000..32d6bbe --- /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 98902dc..4fa69d9 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 @@ public: } 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(); @@ -83,6 +91,16 @@ public: } 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); if (it != _tbl.end()) { 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 178eb17..13f1551 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 6db9678..ed3f117 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 58454f9..c7585fa 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 @@ public: "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 574a26c..012fb52 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 @@ public: 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; @@ -75,6 +90,14 @@ public: } 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())) { return Handle(newPtr); 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 0000000..1adb2da --- /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 4b79798..2c48129 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 1b222b4..c583c45 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 f166a5c..c5578dd 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 b4a7af9..05d87b0 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 0000000..4039677 --- /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 df40914..155e5aa 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 5d5d2f5..10f67c7 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 0000000..69ba1ef --- /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 0000000..2aae3a6 --- /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 0000000..4a8b3f0 --- /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 0000000..059e3dd --- /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 0000000..9d0d475 --- /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 3bf8a5f..526bffb 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 0000000..618c84e --- /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 80cbcba..879d583 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 f7b5cab..123ec2b 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 @@ private: 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 7d13751..28a784d 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 d3521e7..6cc4eb5 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 @@ public: 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 @@ public: 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 @@ private: 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 bb0bcc8..bfba1cc 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 @@ public: 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 @@ public: 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 @@ public: 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 e345118..ad71f09 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 f090e33..a2f292f 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 @@ protected: 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 c12613c..42ee16f 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 7253a80..0c26454 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 @@ public: 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 @@ public: 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 19b6175..86b7102 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 @@ public: 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; } @@ -352,6 +357,12 @@ private: }; 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 @@ public: static DimsOrder NCHW; static DimsOrder NHWC; static DimsOrder NHCW; + static DimsOrder NCDHW; + static DimsOrder NDHWC; // // Constructor @@ -386,7 +399,8 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 9ce39fb..3e2d03b 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 @@ public: 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 @@ public: // 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 44b48b7..683ec4d 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 @@ private: 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 @@ public: // 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 @@ public: 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 @@ protected: 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 @@ protected: _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 @@ private: 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 97e6338..e81fa48 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 6384576..9644e5a 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 @@ -140,6 +140,12 @@ public: Pass::Ptr mergeEltwiseAndReLU(); // + // StridedSlice processing + // + + Pass::Ptr stridedSlice(); + + // // Data layout adjustment // @@ -162,6 +168,7 @@ public: // Pass::Ptr eliminateCopyStages(); + Pass::Ptr removeUnusedStagesOutputs(); // // HW/SW injection @@ -199,6 +206,9 @@ public: 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 0000000..a379ca4 --- /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 0000000..3804e8a --- /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 0000000..b1266ce --- /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 0000000..bff3178 --- /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 7cc5dfb..9f2670e 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 0000000..30a445f --- /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 72264c4..383cdb6 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 @@ private: 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 27df041..77ec017 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 8f07987..bc30e8d 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 3cc1f29..307a358 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 8f251b4..2b3dc5e 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 3ed7516..1179077 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 d95263c..bae11a8 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 faea63d..e717144 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 ea38055..819e08a 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 71a73b3..f7065ac 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 @@ protected: 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 @@ protected: 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 @@ protected: } } - 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 @@ protected: } 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 @@ protected: 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 @@ protected: } 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 4aea3d2..67205d7 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 24e3814..00f1d0a 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 42cde67..af5a8a6 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 dd8bb4e..fc9f332 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 f545c35..3aad4d2 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 df4c534..9c2ee8a 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 @@ public: 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 @@ private: 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 @@ public: 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 d97efe2..9001946 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(); @@ -289,6 +312,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 9c65d7b..52541b3 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 98a7059..21d8fff 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 437d013..ca774be 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 84985d3..8368eeb 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"); @@ -148,6 +155,13 @@ PassSet::Ptr PassManager::buildMiddleEnd() { 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 0000000..e28123a --- /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 b704114..b6492f0 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 b280994..d9bfd2e 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 @@ private: 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 @@ private: } 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 @@ private: } 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 @@ private: } 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 d15e6f2..9c242f7 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 5d92644..e9d3613 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 d941109..992632d 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,479 +167,38 @@ 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 0000000..0152984 --- /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 0000000..040c546 --- /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 e0eaae6..351384e 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 @@ private: 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 @@ private: } 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 f2acbdd..183c46d 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 da0011a..2c38e92 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 0000000..4d3a5c9 --- /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 0000000..7a19a96 --- /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 0000000..cafcfc1 --- /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 58525dd..4972a1e 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 1ca839d..a26eb1c 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 128868f..b49b5c1 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 0000000..4793fcd --- /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 2044a18..f8381d2 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 @@ private: 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 @@ private: } void finalCheckImpl() const override { + assertInputsOutputsTypes(this, {{DataType::FP16}}, {{DataType::FP16}}); } void serializeParamsImpl(BlobSerializer& serializer) const override { @@ -94,12 +87,8 @@ private: } 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 @@ private: 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 @@ public: 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 b2e7431..72ffb1e 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 0a1fb47..36976ea 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 eea3075..7f9d647 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 dc38c76..7138d9e 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 d044429..e0870e9 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 0000000..98d5fc7 --- /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 3588814..ba2a0b8 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: } 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 42874b2..3c2dd91 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: } 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 e68ee51..7827bae 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 @@ private: 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 @@ private: 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 25f8417..f1e883b 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: } 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 2b2bdf6..8af1d52 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 0000000..b8b23e6 --- /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 7ab755e..7dfc3eb 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 @@ private: 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 @@ private: } 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 6c29dc6..ff9dc6d 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 @@ protected: 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 efc0143..11cb5c6 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 @@ protected: 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 993ab8b..dab1d97 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 @@ protected: 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 @@ protected: 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 @@ protected: 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 @@ protected: // 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 @@ protected: // 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 1698658..5a6d0b6 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 df9fdc3..e75175a 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 @@ protected: 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 030d1d7..04d8d67 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 @@ private: 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 @@ private: } 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 ca9ccf9..026105e 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 @@ private: 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 89ac460..dd4490f 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 @@ private: 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 @@ private: // 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 @@ private: 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 @@ private: { '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 @@ private: } 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 afbf79d..7e3e021 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 @@ private: 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 @@ private: } 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 c4094fb..71494a3 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: } 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 0000000..9204767 --- /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 d8fdd0f..0fced45 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 @@ protected: 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 @@ protected: 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 @@ protected: // 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 @@ protected: } // 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 0000000..bbc5869 --- /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 d5dea79..29e704e 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 @@ protected: 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 d297d01..fdf3c39 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 @@ private: 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 @@ private: 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 633d457..e608895 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 @@ private: 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 @@ private: } 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 40e6e95..8a56b4b 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 @@ private: 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 @@ private: } 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 a3f886d..7363df5 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 e202b58..d9d69b8 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 @@ private: 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 @@ private: } 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 37736e2..63e3e4c 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 @@ private: 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 7489d98..819fd14 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 @@ private: 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 9ba01e3..465d9b0 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 @@ private: 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 @@ private: 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 @@ private: } 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 68cb5b1..b9309bd 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 @@ private: 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 @@ private: } 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 88d8f1e..3c5f646 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 @@ private: 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 @@ private: } 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 58a79ca..e33d15a 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 @@ private: 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 2f83e63..9504c73 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 @@ private: 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 6f292b9..746ad53 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 @@ private: 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 @@ private: } 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 67d45d7..a77d536 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 @@ private: 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 @@ private: } 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 d889fd9..a00fdfe 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 @@ private: 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 @@ private: 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 @@ private: index = static_cast(perm[ndims - 1 - index]); newIndices[i] = index; } + std::sort(newIndices, newIndices + indicesSize); auto newList = _model->duplicateData( input1, @@ -96,17 +80,18 @@ private: 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 @@ private: } 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 cda718c..9563497 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 @@ private: 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 @@ private: } 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 ff4cd9a..d99c5c5 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 @@ private: 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 b6176e5..03b6763 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 @@ private: 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 @@ private: } 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 2f6ab2d..d3e6bd4 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 @@ private: 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 @@ private: } 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 a9a3c45..7e35592 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 @@ private: 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 66b19a4..886b700 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 @@ private: 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 0a9e10f..78779a8 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 @@ private: 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 @@ private: 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 @@ private: } 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 96cdc1f..0b40214 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 @@ private: 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 @@ private: } 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 eec2694..af07f88 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 @@ private: 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 450842e..ebb33e4 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 @@ protected: 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 @@ protected: // 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 5847be1..01ff1c3 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 @@ private: 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 @@ private: } 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 fdd9706..aff1f7f 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 @@ protected: 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 @@ protected: 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 @@ protected: // 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 @@ protected: // 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 0000000..dcb5d9c --- /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 c026692..64c091b 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 @@ protected: 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 @@ protected: 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 @@ protected: } 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 0000000..463e630 --- /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 70dd080..ef3c8b4 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 1c3c567..cf1aa25 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 b51f1a6..7023513 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 ad09790..1fa7983 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 7a1733e..0c7d472 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 3a9fb04..6e9edf0 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 a4c4437..61b2db4 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 504337e..1b736d6 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 9de33ec..2aba485 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: std::vector _graphBlob; GraphDesc _graphDesc; DevicePtr _device; - std::vector _stagesMetaData; + GraphMetaInfo _graphMetaData; std::shared_ptr _config; std::vector _supportedMetrics; @@ -126,6 +129,8 @@ private: 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 f0d16d4..0f3bfca 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 daec8a5..af40fda 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 @@ private: 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 79530c8..65af2cb 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 b9e56e9..9420c1b 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 9ac80a6..7b86aaa 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 ab92e78..2c6ba6a 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 9452fc3..aaf1aff 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 e7a998c..bb89ae1 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 @@ public: 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 8d97b07..2895e9c 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 @@ private: 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 @@ private: 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 @@ public: } 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 0000000..0fed062 --- /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 0000000..eee97e4 --- /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 d83e088..8180802 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 f707c79..2c694c0 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 @@ public: } } - 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 @@ public: 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 ebfce47..0000000 --- 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 fc9b808..4803449 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 4514225..30f109e 100644 --- a/inference-engine/tests/unit/cnn_network/parameters.h +++ b/inference-engine/tests/unit/cnn_network/parameters.h @@ -214,13 +214,13 @@ public: } 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 a020769..293f0c6 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 @@ public: ~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 d3ce6e9..77e379d 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 95ec605..6f56466 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 bb01872..4a7caab 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 3ff8aa5..a0321fd 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 c64d068..1bb166d 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 e9eeba7..c912935 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 3399937..4f0329a 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 207c782..5708d27 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 b39813d..2c17865 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 @@ public: // if all weights are zero, or zero value doesn't look like padding if (!bWeightsOK && beforePadding == -1) continue; - - return matchInserted; + actualQuantity ++; + } + // means any quantity > 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 4ede25f..18e8554 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 5449607..8383419 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 c1e0985..21cb455 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 @@ protected: // 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 @@ protected: 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 8c2c369..fa0af69 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 d92a4f2..4976106 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 @@ protected: node->getSelectedPrimitiveDescriptor()->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 @@ protected: 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 bd29222..0000000 --- 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 04be8a3..45aab38 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 @@ public: 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 52ce963..564221f 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 08653b5..f4b02fc 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 @@ protected: #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 d845a78..dc8ff4c 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 0000000..0d498a7 --- /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 0000000..b0a1411 --- /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 9f28a81..d1ec622 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 @@ protected: 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 @@ protected: 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 @@ protected: } } 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 0000000..b17369c --- /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 8ba55b3..e5f5afe 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 @@ protected: 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 ba4596d..8018deb 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 @@ protected: 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 ed09cec..d9d98d4 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 @@ protected: 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 @@ protected: 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 @@ protected: 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 @@ protected: } } 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 @@ protected: 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 d8ffa20..e357668 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 3b1b7d2..d91b3ff 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 e8e20c0..85f73ad 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 67188fb..da43e2a 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 @@ public: 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 0f430c0..5c7ffc5 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 fce08b2..0353000 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 @@ protected: 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 @@ protected: }; 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 5e6a683..a4dbb24 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 0000000..a969d71 --- /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 ad6dc17..70c4b82 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 2612f53..16f7564 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 19547a5..f4bbeec 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 d69972d..90cab63 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 @@ public: }; 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 022cc67..9fcb67a 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 88816b7..7e258da 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 @@ protected: 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 @@ protected: 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 9bd254c..08d6c10 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 280cc18..0000000 --- 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 3c4cbf2..6920500 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 @@ protected: 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 7609a80..4b707f7 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 @@ protected: 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 @@ protected: 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 @@ protected: 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 e3855bf..c280ed8 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 367840a..0000000 --- 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 81fad63..5107576 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 b5fe89a..10bdc53 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 @@ protected: 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 6fdc1d0..734b883 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 @@ public: 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 1bdac7d..1a15e52 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 @@ public: 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 1edefb7..563005c 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 @@ public: 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 6e1f7b5..cb9545e 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 23f27c0..0000000 --- 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 85b9b4a..0000000 --- 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 797c298..0000000 --- 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 5a4b259..54de20f 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 562e301..cbe2db6 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 624d95c..c39fe5c 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 0f35d4d..0000000 --- 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 7a1e532..0000000 --- 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 a775ef9..0000000 --- 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 6535909..0000000 --- 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 ea45ff2..0000000 --- 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 58e0f0b..0000000 --- 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 81a6957..0000000 --- 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 a819fc5..0000000 --- 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 29ddc2b..0000000 --- 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 519210f..0000000 --- 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 cbee0ec..0000000 --- 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 9620f83..0000000 --- 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 7c47ed7..0000000 --- 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 da5ad67..0000000 --- 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 1405060..0000000 --- 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 9007307..0000000 --- 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 3fe6f40..0000000 --- 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 f1e1df3..0000000 --- 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 3c3129a..0000000 --- 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 6c89141..0000000 --- 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 0dd6d92..0000000 --- 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 58b2d8a..0000000 --- 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 c71b716..0000000 --- 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/depth_to_space.h b/inference-engine/thirdparty/clDNN/api/C/depth_to_space.h deleted file mode 100644 index c5255f7..0000000 --- a/inference-engine/thirdparty/clDNN/api/C/depth_to_space.h +++ /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. -*/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#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(depth_to_space) -/// @brief Size of spatial block in the output tensor. Should be >= 2. -size_t block_size; -CLDNN_END_PRIMITIVE_DESC(depth_to_space) - -CLDNN_DECLARE_PRIMITIVE_TYPE_ID(depth_to_space); - -#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 ea01203..0000000 --- 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 94d6b3c..0000000 --- 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 64a26ea..0000000 --- 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 b127822..0000000 --- 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 2b46d93..0000000 --- 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 a12ba53..0000000 --- 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 84ae61d..0000000 --- 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 7bf2396..0000000 --- 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 d28a757..0000000 --- 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 3d687ed..0000000 --- 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 e750dc3..0000000 --- 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 45fb500..0000000 --- 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 d57a7f0..0000000 --- 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 4331162..0000000 --- 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 1de13fa..0000000 --- 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 a2ca824..0000000 --- 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 ac6f133..0000000 --- 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 3c7875c..0000000 --- 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 2e4a2a3..0000000 --- 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 479d330..0000000 --- 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 c531b5e..0000000 --- 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 19aaa57..0000000 --- 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 552b0ec..0000000 --- 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 745027f..0000000 --- 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 13d3436..0000000 --- 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 883f5da..0000000 --- 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 63204fe..0000000 --- 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 0ec3f36..0000000 --- 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 5218654..0000000 --- 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 bea2d21..0000000 --- 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 6d6667f..0000000 --- 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 7cc65fd..0000000 --- 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 694d2eb..0000000 --- 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 060f095..0000000 --- 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 f50eacb..0000000 --- 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 bd0887b..0000000 --- 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 042a876..0000000 --- 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 b982c62..0000000 --- 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 144f8fd..0000000 --- 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 33218cf..0000000 --- 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 21d3d0e..0000000 --- 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 87727ef..0000000 --- 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 bc05b8d..0000000 --- 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 81b863c..0000000 --- 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 492a98a..0000000 --- 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 22d2b21..0000000 --- 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 7fe4827..ec03503 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 @@ protected: return {}; return {additional_params_input}; } - - void update_dto(dto& dto) const override { - dto.activation_func = activation_func; - dto.additional_params = additional_params; - dto.additional_params_input = additional_params_input.c_str(); - } }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/activation_grad.hpp b/inference-engine/thirdparty/clDNN/api/activation_grad.hpp similarity index 74% rename from inference-engine/thirdparty/clDNN/api/CPP/activation_grad.hpp rename to inference-engine/thirdparty/clDNN/api/activation_grad.hpp index d318245..d2d4d62 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/activation_grad.hpp +++ b/inference-engine/thirdparty/clDNN/api/activation_grad.hpp @@ -16,8 +16,8 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/activation_grad.h" #include "primitive.hpp" +#include "activation.hpp" #include 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 @@ protected: return {}; return {additional_params_input}; } - - void update_dto(dto& dto) const override { - dto.activation_grad_func = activation_grad_func; - dto.additional_params = additional_params; - dto.additional_params_input = additional_params_input.c_str(); - } }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/apply_adam.hpp b/inference-engine/thirdparty/clDNN/api/apply_adam.hpp similarity index 81% rename from inference-engine/thirdparty/clDNN/api/CPP/apply_adam.hpp rename to inference-engine/thirdparty/clDNN/api/apply_adam.hpp index c2bdb8b..f74523b 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/apply_adam.hpp +++ b/inference-engine/thirdparty/clDNN/api/apply_adam.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/apply_adam.h" #include "primitive.hpp" #include @@ -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 @@ protected: ret.push_back(dependency_id); return ret; } - - void update_dto(dto& dto) const override { - dto.m = m.c_str(); - dto.v = v.c_str(); - dto.beta1_power = beta1_power.c_str(); - dto.beta2_power = beta2_power.c_str(); - dto.lr = lr; - dto.beta1 = beta1; - dto.beta2 = beta2; - dto.epsilon = epsilon; - dto.dependency_id = dependency_id.c_str(); - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/arg_max_min.hpp b/inference-engine/thirdparty/clDNN/api/arg_max_min.hpp similarity index 78% rename from inference-engine/thirdparty/clDNN/api/CPP/arg_max_min.hpp rename to inference-engine/thirdparty/clDNN/api/arg_max_min.hpp index 2b5fb77..c0357fe 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/arg_max_min.hpp +++ b/inference-engine/thirdparty/clDNN/api/arg_max_min.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/arg_max_min.h" #include "primitive.hpp" #include #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 7432840..733324b 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 b6061df..29b8e69 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 @@ protected: return deps; } - - void update_dto(dto& dto) const override { - dto.mean = mean.c_str(); - dto.variance = variance.c_str(); - dto.inv_variance = inv_variance.c_str(); - dto.scale = scale.c_str(); - dto.shift = shift.c_str(); - dto.epsilon = epsilon; - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/batch_norm_grad.hpp b/inference-engine/thirdparty/clDNN/api/batch_norm_grad.hpp similarity index 81% rename from inference-engine/thirdparty/clDNN/api/CPP/batch_norm_grad.hpp rename to inference-engine/thirdparty/clDNN/api/batch_norm_grad.hpp index 8387b6c..cf487ad 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/batch_norm_grad.hpp +++ b/inference-engine/thirdparty/clDNN/api/batch_norm_grad.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/batch_norm_grad.h" #include "primitive.hpp" #include @@ -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 @@ protected: std::vector> 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 3331238..effe04a 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 50f5544..db617f0 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 4aca4c7..7c2ca99 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 9e17acf..e7f7030 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 @@ public: 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 0000000..ba6966f --- /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 0bcd0a3..9fe0250 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 68e4b9c..19501ba 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 3db25c7..9242b4e 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 5143262..1d0b14e 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 @@ private: throw std::runtime_error( "convolution's weights count does not " "match quantization factors count"); - if (with_activation && output_data_type) { + if (output_data_type) { // Use explicit switch to get compiler warning if new data_types would become supported. switch (*output_data_type) { - case data_types::u8: - if (activation_negative_slope != 0.0f) - throw std::runtime_error( - "Negative slope in activation is meaningless for the " - "unsigned type!"); - break; case data_types::bin: throw std::runtime_error("Binary convolution is a separate primitive."); break; + case data_types::u8: case data_types::i8: case data_types::i32: case data_types::i64: @@ -1057,7 +862,7 @@ private: } }; -struct deformable_interp : 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 9b691f6..534aedb 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 30f6d84..fa15fa7 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 3a47f24..ef7aa6e 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 2619237..2a72914 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 d663df9..133c046 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 a68e583..a506a08 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 8719d2f..0c6257a 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 719fa89..6df38c1 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 55e72ab..93cd3fa 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 7082be1..91a66e3 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 @@ protected: 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 fea46a2..0c70122 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 0000000..5db6700 --- /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 103bf18..b91466f 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 @@ protected: return ret; } - - void update_dto(dto& dto) const override { - dto.weights = weights.c_str(); - dto.bias = bias.c_str(); - dto.weights_quantization_factors = weights_quantization_factors.c_str(); - dto.output_calibration_factors = output_calibration_factors.c_str(); - dto.input_quantization_factor = input_quantization_factor; - dto.output_quantization_factor = output_quantization_factor; - dto.with_activation = with_activation; - dto.activation_negative_slope = activation_negative_slope; - } }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/fully_connected_grad_input.hpp b/inference-engine/thirdparty/clDNN/api/fully_connected_grad_input.hpp similarity index 81% rename from inference-engine/thirdparty/clDNN/api/CPP/fully_connected_grad_input.hpp rename to inference-engine/thirdparty/clDNN/api/fully_connected_grad_input.hpp index 8489fc8..23463cd 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/fully_connected_grad_input.hpp +++ b/inference-engine/thirdparty/clDNN/api/fully_connected_grad_input.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/fully_connected_grad_input.h" #include "primitive.hpp" #include @@ -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 @@ protected: std::vector> 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 a72596f..71af7a8 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 @@ protected: 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 de4eb5f..9b22d5c 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 0000000..28980cf --- /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 01ca36b..e99561f 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 1b71d8d..0e6548e 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 7f1fac5..f994ed6 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 a8c8c5a..db17b8e 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 887bd26..65349ed 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 94dce3c..1e48dd0 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 a7a54df..97ae0f3 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 @@ protected: } 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 @@ protected: ret.push_back(hidden); 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.hidden = hidden.c_str(); - dto.direction = direction; - } }; -struct lstm_elt : 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 @@ protected: ret.push_back(cell); return ret; } - - void update_dto(dto& dto) const override { - dto.cell = cell.c_str(); - dto.offset_order = offset_order; - dto.clip = clip; - dto.input_forget = input_forget; - 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.direction = direction; - } }; /// @} /// @} /// @} -} // namespace cldnn \ No newline at end of file +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/api/CPP/lstm_dynamic.hpp b/inference-engine/thirdparty/clDNN/api/lstm_dynamic.hpp similarity index 82% rename from inference-engine/thirdparty/clDNN/api/CPP/lstm_dynamic.hpp rename to inference-engine/thirdparty/clDNN/api/lstm_dynamic.hpp index 591cc27..3dafe26 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/lstm_dynamic.hpp +++ b/inference-engine/thirdparty/clDNN/api/lstm_dynamic.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/lstm_dynamic.h" #include "primitive.hpp" #include @@ -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 @@ protected: } return ret; } - - void update_dto(dto& dto) const override { - dto.dyn_length = dyn_length.c_str(); - dto.weights = weights.c_str(); - dto.recurrent = recurrent.c_str(); - dto.last_hidden_state = last_hidden_state.c_str(); - dto.last_cell_state = last_cell_state.c_str(); - dto.bias = bias.c_str(); - dto.initial_hidden = initial_hidden.c_str(); - dto.initial_cell = initial_cell.c_str(); - dto.clip = clip; - dto.input_forget = input_forget; - } }; /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/max_unpooling.hpp b/inference-engine/thirdparty/clDNN/api/max_unpooling.hpp similarity index 84% rename from inference-engine/thirdparty/clDNN/api/CPP/max_unpooling.hpp rename to inference-engine/thirdparty/clDNN/api/max_unpooling.hpp index 58e8e3c..6fe1dcd 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/max_unpooling.hpp +++ b/inference-engine/thirdparty/clDNN/api/max_unpooling.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/max_unpooling.h" #include "primitive.hpp" #include @@ -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 1ac388d..efc8b8a 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 2f445d7..f34cb6f 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 190eb38..f6345d5 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 d649f7e..f66971f 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 0000000..f593cd4 --- /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 19a4ea6..611cc08 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 f747557..13340d0 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 7028a97..24f6f03 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 02a72b8..8adfd4d 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 @@ protected: 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 0000000..314c71d --- /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 dbdd13c..5908f6a 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 d2563f0..004b9ba 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 @@ private: }; /// @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 ecf00dd..7cbdd54 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 @@ private: /// @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 @@ public: 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 @@ private: 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 @@ private: } _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 @@ public: /// @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 @@ public: 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 4fcc586..19fc536 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 13527e2..6bc3c5f 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 0442826..a1c68d5 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 @@ protected: 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 993e846..e807903 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 05715b2..27444d9 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 28e5419..43dc95c 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 5823558..c44c231 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 7996a84..445b750 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 @@ protected: 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 fb31e13..667cf5b 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 cf773d2..d13b18d 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 @@ protected: return ret; } - - void update_dto(dto& dto) const override { - dto.bias = bias.c_str(); - dto.scale_input = scale_input.c_str(); - dto.prev_scale_grad = prev_scale_grad.c_str(); - dto.prev_bias_grad = prev_bias_grad.c_str(); - dto.scale_grad = scale_grad.c_str(); - } }; /// @} /// @} diff --git a/inference-engine/thirdparty/clDNN/api/CPP/select.hpp b/inference-engine/thirdparty/clDNN/api/select.hpp similarity index 86% rename from inference-engine/thirdparty/clDNN/api/CPP/select.hpp rename to inference-engine/thirdparty/clDNN/api/select.hpp index 8f14fd8..8ed582a 100644 --- a/inference-engine/thirdparty/clDNN/api/CPP/select.hpp +++ b/inference-engine/thirdparty/clDNN/api/select.hpp @@ -16,7 +16,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -#include "../C/select.h" #include "primitive.hpp" namespace cldnn { @@ -32,7 +31,7 @@ namespace cldnn { /// - 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 -struct select : public primitive_base { +struct select : public primitive_base::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}, +namespace detail { - {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_select_gpu::attach_select_gpu() { + implementation_map instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/shuffle_channels.cpp b/inference-engine/thirdparty/clDNN/src/shuffle_channels.cpp index 9d42563..e01b730 100644 --- a/inference-engine/thirdparty/clDNN/src/shuffle_channels.cpp +++ b/inference-engine/thirdparty/clDNN/src/shuffle_channels.cpp @@ -22,7 +22,7 @@ #include namespace cldnn { -primitive_type_id shuffle_channels_type_id() { +primitive_type_id shuffle_channels::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/softmax.cpp b/inference-engine/thirdparty/clDNN/src/softmax.cpp index eccebd2..36e8205 100644 --- a/inference-engine/thirdparty/clDNN/src/softmax.cpp +++ b/inference-engine/thirdparty/clDNN/src/softmax.cpp @@ -20,7 +20,7 @@ #include namespace cldnn { -primitive_type_id softmax_type_id() { +primitive_type_id softmax::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/softmax_loss_grad.cpp b/inference-engine/thirdparty/clDNN/src/softmax_loss_grad.cpp index d19b0cc..13e6b1a 100644 --- a/inference-engine/thirdparty/clDNN/src/softmax_loss_grad.cpp +++ b/inference-engine/thirdparty/clDNN/src/softmax_loss_grad.cpp @@ -20,7 +20,7 @@ #include namespace cldnn { -primitive_type_id softmax_loss_grad_type_id() { +primitive_type_id softmax_loss_grad::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/split.cpp b/inference-engine/thirdparty/clDNN/src/split.cpp index 1562d97..5ffd7bd 100644 --- a/inference-engine/thirdparty/clDNN/src/split.cpp +++ b/inference-engine/thirdparty/clDNN/src/split.cpp @@ -22,7 +22,7 @@ #include namespace cldnn { -primitive_type_id split_type_id() { +primitive_type_id split::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/strided_slice.cpp b/inference-engine/thirdparty/clDNN/src/strided_slice.cpp index d63d794..339e0fa 100644 --- a/inference-engine/thirdparty/clDNN/src/strided_slice.cpp +++ b/inference-engine/thirdparty/clDNN/src/strided_slice.cpp @@ -23,7 +23,7 @@ #include namespace cldnn { -primitive_type_id strided_slice_type_id() { +primitive_type_id strided_slice::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/tile.cpp b/inference-engine/thirdparty/clDNN/src/tile.cpp index 5f9772f..48c15e3 100644 --- a/inference-engine/thirdparty/clDNN/src/tile.cpp +++ b/inference-engine/thirdparty/clDNN/src/tile.cpp @@ -22,7 +22,7 @@ #include namespace cldnn { -primitive_type_id tile_type_id() { +primitive_type_id tile::type_id() { static primitive_type_base instance; return &instance; } diff --git a/inference-engine/thirdparty/clDNN/src/topology.cpp b/inference-engine/thirdparty/clDNN/src/topology.cpp new file mode 100644 index 0000000..3c1d6e3 --- /dev/null +++ b/inference-engine/thirdparty/clDNN/src/topology.cpp @@ -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. +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#include "api/topology.hpp" +#include "topology_impl.h" +#include +#include + +namespace cldnn { + +topology::topology() : _impl(new topology_impl()) {} + +const std::vector topology::get_primitive_ids() const { + return _impl->get_primitives_id(); +} + +void topology::change_input_layout(primitive_id id, const layout& new_layout) { + if (new_layout.format < format::any || new_layout.format >= format::format_num) + throw std::invalid_argument("Unknown format of layout."); + + if (new_layout.data_type != data_types::f16 && new_layout.data_type != data_types::f32 && + new_layout.data_type != data_types::i8 && new_layout.data_type != data_types::bin && + new_layout.data_type != data_types::u8 && new_layout.data_type != data_types::i32 && + new_layout.data_type != data_types::i64) + throw std::invalid_argument("Unknown data_type of layout."); + + _impl->change_input_layout(id, new_layout); +} + +void topology::add_primitive(std::shared_ptr desc) { + _impl->add(desc); +} + +void topology::retain() { + _impl->add_ref(); +} + +void topology::release() { + _impl->release(); +} + +} // namespace cldnn diff --git a/inference-engine/thirdparty/clDNN/src/upsampling.cpp b/inference-engine/thirdparty/clDNN/src/upsampling.cpp index 405b0c6..2521f8e 100644 --- a/inference-engine/thirdparty/clDNN/src/upsampling.cpp +++ b/inference-engine/thirdparty/clDNN/src/upsampling.cpp @@ -21,7 +21,7 @@ #include namespace cldnn { -primitive_type_id upsampling_type_id() { +primitive_type_id upsampling::type_id() { static primitive_type_base instance; return &instance; } @@ -31,14 +31,13 @@ layout upsampling_inst::calc_output_layout(upsampling_node const& node) { "Output data type forcing is not supported for upsampling_node!"); auto desc = node.get_primitive(); auto input_layout = node.input().get_output_layout(); - auto scale = desc->scale; - auto result_sizes = tensor(input_layout.size.batch[0], - input_layout.size.feature[0], - static_cast(input_layout.size.spatial[0] * scale), - static_cast(input_layout.size.spatial[1] * scale)); - auto result = layout({input_layout.data_type, input_layout.format, result_sizes}); + auto result_sizes = desc->output_size; + + CLDNN_ERROR_NOT_EQUAL(node.id(), "Input batch size", input_layout.size.batch[0], "output batch size", result_sizes.batch[0], ""); + CLDNN_ERROR_NOT_EQUAL(node.id(), "Input feature size", input_layout.size.feature[0], "output feature size", result_sizes.feature[0], ""); + auto result = layout({input_layout.data_type, input_layout.format, result_sizes}); return result; } @@ -62,7 +61,7 @@ std::string upsampling_inst::to_string(upsampling_node const& node) { primitive_description << "id: " << desc->id << ", type: upsampling" << "\n\tinput_1: " << input_1.id() << ", count: " << input_1.get_output_layout().count() - << ", size: " << input_1.get_output_layout().size << "\n\tscale: " << desc->scale + << ", size: " << input_1.get_output_layout().size << "\n\tnum_filter: " << desc->num_filter << "\n\tsample_type: " << str_type << "\n\twith activation: " << activation << ", slope: " << desc->activation_negative_slope << "\n\toutput padding lower size: " << desc->output_padding.lower_size() diff --git a/inference-engine/thirdparty/clDNN/tests/CMakeLists.txt b/inference-engine/thirdparty/clDNN/tests/CMakeLists.txt index 7f906cd..63ee610 100644 --- a/inference-engine/thirdparty/clDNN/tests/CMakeLists.txt +++ b/inference-engine/thirdparty/clDNN/tests/CMakeLists.txt @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. - # ========================================= Name / Output settings ===================================== set(CLDNN_BUILD__PROJ "clDNN_unit_tests") @@ -126,7 +125,6 @@ add_executable("${CLDNN_BUILD__PROJ}" set_property(TARGET "${CLDNN_BUILD__PROJ}" PROPERTY PROJECT_LABEL "${CLDNN_BUILD__PROJ_LABEL}") set_property(TARGET "${CLDNN_BUILD__PROJ}" PROPERTY OUTPUT_NAME "${CLDNN_BUILD__PROJ_OUTPUT_NAME}") - # Set library dependencies target_link_libraries("${CLDNN_BUILD__PROJ}" "${CLDNN_BUILD__PROJ__clDNN}" @@ -137,7 +135,7 @@ if(WIN32) elseif((NOT ANDROID) AND (UNIX)) target_link_libraries("${CLDNN_BUILD__PROJ}" pthread) endif() -target_link_libraries("${CLDNN_BUILD__PROJ}" ${CLDNN__SYSTEM_LINK_LIBRARIES} OpenCL) +target_link_libraries("${CLDNN_BUILD__PROJ}" ${CLDNN__SYSTEM_LINK_LIBRARIES}) # =================================== Custom pre- and post-steps ======================================= diff --git a/inference-engine/thirdparty/clDNN/tests/module_tests/events_pool_test.cpp b/inference-engine/thirdparty/clDNN/tests/module_tests/events_pool_test.cpp index c7509ff..06a7ece 100644 --- a/inference-engine/thirdparty/clDNN/tests/module_tests/events_pool_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/module_tests/events_pool_test.cpp @@ -14,13 +14,11 @@ // limitations under the License. */ - - #include -#include "api/CPP/engine.hpp" +#include "api/engine.hpp" #include "test_utils/test_utils.h" -#include "api/CPP/input_layout.hpp" -#include "api/CPP/network.hpp" +#include "api/input_layout.hpp" +#include "api/network.hpp" using namespace tests; using namespace cldnn; @@ -37,12 +35,12 @@ TEST(events_pool, DISABLED_basic_test) topology topology; topology.add(input_layout("input", { data_types::f32, format::bfyx,{ tensor(spatial(x_size, y_size), feature(feature_num), batch(batch_num))}})); - topology.add(activation("relu", "input", activation_relu)); - topology.add(activation("relu1", "relu", activation_relu)); - topology.add(activation("relu2", "relu1", activation_relu)); - topology.add(activation("relu3", "relu2", activation_relu)); - topology.add(activation("relu4", "relu3", activation_relu)); - topology.add(activation("relu5", "relu4", activation_relu)); + topology.add(activation("relu", "input", activation_func::relu)); + topology.add(activation("relu1", "relu", activation_func::relu)); + topology.add(activation("relu2", "relu1", activation_func::relu)); + topology.add(activation("relu3", "relu2", activation_func::relu)); + topology.add(activation("relu4", "relu3", activation_func::relu)); + topology.add(activation("relu5", "relu4", activation_func::relu)); build_options bo; bo.set_option(build_option::optimize_data(true)); diff --git a/inference-engine/thirdparty/clDNN/tests/module_tests/gpu_toolkit_test.cpp b/inference-engine/thirdparty/clDNN/tests/module_tests/gpu_toolkit_test.cpp index 1793d9d..2b6d9d6 100644 --- a/inference-engine/thirdparty/clDNN/tests/module_tests/gpu_toolkit_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/module_tests/gpu_toolkit_test.cpp @@ -15,15 +15,13 @@ */ #include -#include "api/CPP/engine.hpp" +#include "api/engine.hpp" #include "test_utils/test_utils.h" -#include "api/CPP/network.hpp" -#include "api/CPP/topology.hpp" -#include "api/CPP/input_layout.hpp" -#include "api/CPP/activation.hpp" -#include "api/C/input_layout.h" -#include "api/C/activation.h" -#include "api/C/cldnn.h" +#include "api/network.hpp" +#include "api/topology.hpp" +#include "api/input_layout.hpp" +#include "api/activation.hpp" +#include "api/cldnn.hpp" #include "test_utils.h" @@ -123,7 +121,7 @@ TEST(gpu_engine, user_context) auto input_mem = cldnn::memory::allocate(engine, inp_lay); tests::set_values(input_mem, { 1.0f, 2.0f, 3.0f, 4.0f }); auto inp = input_layout("input", inp_lay); - auto activ = activation("this_needs_queue", "input", cldnn_activation_func::activation_abs); + auto activ = activation("this_needs_queue", "input", activation_func::abs); topo.add(inp, activ); network net(engine, topo); diff --git a/inference-engine/thirdparty/clDNN/tests/module_tests/test_uqr_distribution.cpp b/inference-engine/thirdparty/clDNN/tests/module_tests/test_uqr_distribution.cpp index 200936c..0867a79 100644 --- a/inference-engine/thirdparty/clDNN/tests/module_tests/test_uqr_distribution.cpp +++ b/inference-engine/thirdparty/clDNN/tests/module_tests/test_uqr_distribution.cpp @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. - #include #include "test_utils/uniform_quantized_real_distribution.hpp" @@ -28,7 +27,6 @@ #include #include - namespace cldnn { namespace tests { template @@ -47,28 +45,23 @@ protected: /// @brief Expected result_type of uniform_quantized_real_distribution. using expected_uqr_dist_rt = typename std::conditional::value, RealType, float>::type; - void SetUp() override {} void TearDown() override {} }; - using uniform_quantized_real_distribution_test_types = ::testing::Types; TYPED_TEST_CASE(uniform_quantized_real_distribution_test, uniform_quantized_real_distribution_test_types); - TYPED_TEST(uniform_quantized_real_distribution_test, param_construct_default) { using uqr_dist_param = typename TestFixture::uqr_dist_param; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(0); const auto expected_b = static_cast(1); const unsigned expected_srb = std::numeric_limits::digits - 1U; - uqr_dist_param dist_param_instance1; using actual_uqr_dist_rt = typename decltype(dist_param_instance1)::distribution_type::result_type; @@ -83,7 +76,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_construct_a_b_srb) using uqr_dist_param = typename TestFixture::uqr_dist_param; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - // Any auto expected_a = static_cast(-130); auto expected_b = static_cast(244); @@ -95,7 +87,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_construct_a_b_srb) EXPECT_EQ(dist_param_instance1.b(), expected_b); EXPECT_EQ(dist_param_instance1.significand_rand_bits(), expected_srb); - // Zero expected_a = static_cast(57); expected_b = static_cast(73); @@ -107,7 +98,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_construct_a_b_srb) EXPECT_EQ(dist_param_instance2.b(), expected_b); EXPECT_EQ(dist_param_instance2.significand_rand_bits(), expected_srb); - // Almost Maximum expected_a = static_cast(-65); expected_b = static_cast(-45); @@ -119,7 +109,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_construct_a_b_srb) EXPECT_EQ(dist_param_instance3.b(), expected_b); EXPECT_EQ(dist_param_instance3.significand_rand_bits(), expected_srb); - // Maximum expected_a = static_cast(0); expected_b = static_cast(0); @@ -131,7 +120,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_construct_a_b_srb) EXPECT_EQ(dist_param_instance4.b(), expected_b); EXPECT_EQ(dist_param_instance4.significand_rand_bits(), expected_srb); - // Over Maximum expected_a = static_cast(-4); expected_b = static_cast(-1); @@ -145,7 +133,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_construct_a_b_srb) EXPECT_EQ(dist_param_instance5.b(), expected_b); EXPECT_EQ(dist_param_instance5.significand_rand_bits(), expected_srb); - // Throw std::invalid_argument (a > b) expected_a = static_cast(40); expected_b = static_cast(39); @@ -155,7 +142,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_construct_a_b_srb) uqr_dist_param dist_param_instance6(expected_a, expected_b, test_srb); }, std::invalid_argument); - // Throw std::invalid_argument (a is infinite) expected_a = -std::numeric_limits::infinity(); expected_b = static_cast(39); @@ -165,7 +151,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_construct_a_b_srb) uqr_dist_param dist_param_instance7(expected_a, expected_b, test_srb); }, std::invalid_argument); - // Throw std::invalid_argument (b is infinite) expected_a = static_cast(40); expected_b = std::numeric_limits::infinity(); @@ -181,11 +166,9 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_construct_srb) using uqr_dist_param = typename TestFixture::uqr_dist_param; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(0); const auto expected_b = static_cast(1); - // Any unsigned expected_srb = 4U; @@ -195,7 +178,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_construct_srb) EXPECT_EQ(dist_param_instance1.b(), expected_b); EXPECT_EQ(dist_param_instance1.significand_rand_bits(), expected_srb); - // Zero expected_srb = 0U; @@ -205,7 +187,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_construct_srb) EXPECT_EQ(dist_param_instance2.b(), expected_b); EXPECT_EQ(dist_param_instance2.significand_rand_bits(), expected_srb); - // Almost Maximum expected_srb = std::numeric_limits::digits - 4U; @@ -215,7 +196,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_construct_srb) EXPECT_EQ(dist_param_instance3.b(), expected_b); EXPECT_EQ(dist_param_instance3.significand_rand_bits(), expected_srb); - // Maximum expected_srb = std::numeric_limits::digits - 1U; @@ -225,7 +205,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_construct_srb) EXPECT_EQ(dist_param_instance4.b(), expected_b); EXPECT_EQ(dist_param_instance4.significand_rand_bits(), expected_srb); - // Over Maximum expected_srb = std::numeric_limits::digits - 1U; @@ -243,12 +222,10 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_construct_copy) using uqr_dist_param = typename TestFixture::uqr_dist_param; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(-102); const auto expected_b = static_cast(73); const unsigned expected_srb = 3U; - uqr_dist_param dist_param_instance1(expected_a, expected_b, expected_srb); uqr_dist_param dist_param_instance2(dist_param_instance1); @@ -262,12 +239,10 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_construct_move) using uqr_dist_param = typename TestFixture::uqr_dist_param; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(-101); const auto expected_b = static_cast(75); const unsigned expected_srb = 2U; - uqr_dist_param dist_param_instance1(expected_a, expected_b, expected_srb); uqr_dist_param dist_param_instance2(std::move(dist_param_instance1)); @@ -281,12 +256,10 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_assign_copy) using uqr_dist_param = typename TestFixture::uqr_dist_param; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(-112); const auto expected_b = static_cast(70); const unsigned expected_srb = 4U; - uqr_dist_param dist_param_instance1(expected_a, expected_b, expected_srb); uqr_dist_param dist_param_instance2(2U); dist_param_instance2 = dist_param_instance1; @@ -301,12 +274,10 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_assign_move) using uqr_dist_param = typename TestFixture::uqr_dist_param; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(-102); const auto expected_b = static_cast(35); const unsigned expected_srb = 1U; - uqr_dist_param dist_param_instance1(expected_a, expected_b, expected_srb); uqr_dist_param dist_param_instance2(2U); dist_param_instance2 = std::move(dist_param_instance1); @@ -325,7 +296,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_equality_compare) const auto expected_b = static_cast(35); const unsigned expected_srb = 1U; - uqr_dist_param dist_param_instance1(expected_a, expected_b, expected_srb); uqr_dist_param dist_param_instance2(2U); uqr_dist_param dist_param_instance3 = dist_param_instance1; @@ -351,18 +321,15 @@ TYPED_TEST(uniform_quantized_real_distribution_test, param_equality_compare) EXPECT_FALSE(dist_param_instance3 != dist_param_instance3); } - TYPED_TEST(uniform_quantized_real_distribution_test, construct_default) { using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(0); const auto expected_b = static_cast(1); const unsigned expected_srb = std::numeric_limits::digits - 1U; - uqr_dist dist_instance1; using actual_uqr_dist_rt = typename decltype(dist_instance1)::result_type; @@ -377,7 +344,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, construct_a_b_srb) using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - // Any auto expected_a = static_cast(-137); auto expected_b = static_cast(271); @@ -389,7 +355,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, construct_a_b_srb) EXPECT_EQ(dist_instance1.b(), expected_b); EXPECT_EQ(dist_instance1.significand_rand_bits(), expected_srb); - // Zero expected_a = static_cast(47); expected_b = static_cast(63); @@ -401,7 +366,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, construct_a_b_srb) EXPECT_EQ(dist_instance2.b(), expected_b); EXPECT_EQ(dist_instance2.significand_rand_bits(), expected_srb); - // Almost Maximum expected_a = static_cast(-55); expected_b = static_cast(-15); @@ -413,7 +377,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, construct_a_b_srb) EXPECT_EQ(dist_instance3.b(), expected_b); EXPECT_EQ(dist_instance3.significand_rand_bits(), expected_srb); - // Maximum expected_a = static_cast(2); expected_b = static_cast(2); @@ -425,7 +388,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, construct_a_b_srb) EXPECT_EQ(dist_instance4.b(), expected_b); EXPECT_EQ(dist_instance4.significand_rand_bits(), expected_srb); - // Over Maximum expected_a = static_cast(-3); expected_b = static_cast(0); @@ -439,7 +401,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, construct_a_b_srb) EXPECT_EQ(dist_instance5.b(), expected_b); EXPECT_EQ(dist_instance5.significand_rand_bits(), expected_srb); - // Throw std::invalid_argument (a > b) expected_a = static_cast(-40); expected_b = static_cast(-80); @@ -449,7 +410,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, construct_a_b_srb) uqr_dist dist_instance6(expected_a, expected_b, test_srb); }, std::invalid_argument); - // Throw std::invalid_argument (a is infinite) expected_a = -std::numeric_limits::infinity(); expected_b = static_cast(-80); @@ -459,7 +419,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, construct_a_b_srb) uqr_dist dist_instance7(expected_a, expected_b, test_srb); }, std::invalid_argument); - // Throw std::invalid_argument (b is infinite) expected_a = static_cast(-40); expected_b = std::numeric_limits::infinity(); @@ -476,12 +435,10 @@ TYPED_TEST(uniform_quantized_real_distribution_test, construct_param) using uqr_dist_param = typename TestFixture::uqr_dist_param; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(2); const auto expected_b = static_cast(17); const unsigned expected_srb = 3U; - uqr_dist_param dist_param_instance1(expected_a, expected_b, expected_srb); uqr_dist dist_instance1(dist_param_instance1); @@ -499,11 +456,9 @@ TYPED_TEST(uniform_quantized_real_distribution_test, construct_srb) using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(0); const auto expected_b = static_cast(1); - // Any unsigned expected_srb = 3U; @@ -513,7 +468,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, construct_srb) EXPECT_EQ(dist_instance1.b(), expected_b); EXPECT_EQ(dist_instance1.significand_rand_bits(), expected_srb); - // Zero expected_srb = 0U; @@ -523,7 +477,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, construct_srb) EXPECT_EQ(dist_instance2.b(), expected_b); EXPECT_EQ(dist_instance2.significand_rand_bits(), expected_srb); - // Almost Maximum expected_srb = std::numeric_limits::digits - 2U; @@ -533,7 +486,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, construct_srb) EXPECT_EQ(dist_instance3.b(), expected_b); EXPECT_EQ(dist_instance3.significand_rand_bits(), expected_srb); - // Maximum expected_srb = std::numeric_limits::digits - 1U; @@ -543,7 +495,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, construct_srb) EXPECT_EQ(dist_instance4.b(), expected_b); EXPECT_EQ(dist_instance4.significand_rand_bits(), expected_srb); - // Over Maximum expected_srb = std::numeric_limits::digits - 1U; @@ -561,12 +512,10 @@ TYPED_TEST(uniform_quantized_real_distribution_test, construct_copy) using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(-122); const auto expected_b = static_cast(33); const unsigned expected_srb = 5U; - uqr_dist dist_instance1(expected_a, expected_b, expected_srb); uqr_dist dist_instance2(dist_instance1); @@ -580,12 +529,10 @@ TYPED_TEST(uniform_quantized_real_distribution_test, construct_move) using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(0); const auto expected_b = static_cast(15); const unsigned expected_srb = 1U; - uqr_dist dist_instance1(expected_a, expected_b, expected_srb); uqr_dist dist_instance2(std::move(dist_instance1)); @@ -599,12 +546,10 @@ TYPED_TEST(uniform_quantized_real_distribution_test, assign_copy) using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(-1); const auto expected_b = static_cast(1); const unsigned expected_srb = 3U; - uqr_dist dist_instance1(expected_a, expected_b, expected_srb); uqr_dist dist_instance2(2U); dist_instance2 = dist_instance1; @@ -619,12 +564,10 @@ TYPED_TEST(uniform_quantized_real_distribution_test, assign_move) using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(-107); const auto expected_b = static_cast(-36); const unsigned expected_srb = 2U; - uqr_dist dist_instance1(expected_a, expected_b, expected_srb); uqr_dist dist_instance2(3U); dist_instance2 = std::move(dist_instance1); @@ -639,12 +582,10 @@ TYPED_TEST(uniform_quantized_real_distribution_test, get_param) using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(-22); const auto expected_b = static_cast(-17); const unsigned expected_srb = 2U; - uqr_dist dist_instance1(expected_a, expected_b, expected_srb); EXPECT_EQ(dist_instance1.param().a(), expected_a); @@ -658,14 +599,12 @@ TYPED_TEST(uniform_quantized_real_distribution_test, set_param) using uqr_dist_param = typename TestFixture::uqr_dist_param; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(-122); const auto expected_b = static_cast(-67); const unsigned expected_srb = 3U; uqr_dist dist_instance_ref(expected_a, expected_b, expected_srb); - // Custom Parameters uqr_dist dist_instance1(1U); dist_instance1.param(uqr_dist_param(expected_a, expected_b, expected_srb)); @@ -676,7 +615,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, set_param) EXPECT_TRUE(dist_instance1 == dist_instance_ref); - // From Other Distribution uqr_dist dist_instance2(2U); dist_instance2.param(dist_instance1.param()); @@ -694,12 +632,10 @@ TYPED_TEST(uniform_quantized_real_distribution_test, get_member_param_equivalenc using uqr_dist_param = typename TestFixture::uqr_dist_param; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(22); const auto expected_b = static_cast(27); const unsigned expected_srb = 4U; - // Default Constructor uqr_dist_param dist_param_instance1; uqr_dist dist_instance1; @@ -712,7 +648,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, get_member_param_equivalenc EXPECT_EQ(dist_instance1.b(), dist_param_instance1.b()); EXPECT_EQ(dist_instance1.significand_rand_bits(), dist_param_instance1.significand_rand_bits()); - // Constructor (a, b, srb) uqr_dist_param dist_param_instance2(expected_a, expected_b, expected_srb); uqr_dist dist_instance2(expected_a, expected_b, expected_srb); @@ -725,7 +660,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, get_member_param_equivalenc EXPECT_EQ(dist_instance2.b(), dist_param_instance2.b()); EXPECT_EQ(dist_instance2.significand_rand_bits(), dist_param_instance2.significand_rand_bits()); - // Constructor (srb) uqr_dist_param dist_param_instance3(expected_srb); uqr_dist dist_instance3(expected_srb); @@ -744,12 +678,10 @@ TYPED_TEST(uniform_quantized_real_distribution_test, get_min) using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(-99); const auto expected_b = static_cast(-97); const unsigned expected_srb = 3U; - uqr_dist dist_instance1(expected_a, expected_b, expected_srb); EXPECT_EQ(dist_instance1.min(), expected_a); @@ -760,12 +692,10 @@ TYPED_TEST(uniform_quantized_real_distribution_test, get_max) using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(-99); const auto expected_b = static_cast(-97); const unsigned expected_srb = 3U; - uqr_dist dist_instance1(expected_a, expected_b, expected_srb); EXPECT_EQ(dist_instance1.max(), expected_b); @@ -776,12 +706,10 @@ TYPED_TEST(uniform_quantized_real_distribution_test, equality_compare) using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(102); const auto expected_b = static_cast(105); const unsigned expected_srb = 4U; - uqr_dist dist_instance1(expected_a, expected_b, expected_srb); uqr_dist dist_instance2(2U); uqr_dist dist_instance3(dist_instance1); @@ -812,14 +740,12 @@ TYPED_TEST(uniform_quantized_real_distribution_test, serialize) using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(-77); const auto expected_b = static_cast(17); const unsigned expected_srb = 4U; uqr_dist dist_instance1(expected_a, expected_b, expected_srb); - // Preserve Stream Formatting #1 const auto before_flags1 = std::cout.flags(); const auto before_fill1 = std::cout.fill(); @@ -834,7 +760,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, serialize) EXPECT_EQ(before_fill1, after_fill1); EXPECT_EQ(before_prec1, after_prec1); - // Preserve Stream Formatting #2 std::wstringstream ss2; ss2 << std::oct << std::setprecision(5) << std::boolalpha << std::setfill(ss2.widen('#')); @@ -852,7 +777,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, serialize) EXPECT_EQ(before_fill2, after_fill2); EXPECT_EQ(before_prec2, after_prec2); - // Preserve Stream Formatting #3 std::wstringstream ss3; ss3 << std::dec << std::setprecision(5) << std::right << std::setw(400) << std::skipws @@ -871,7 +795,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, serialize) EXPECT_EQ(before_fill3, after_fill3); EXPECT_EQ(before_prec3, after_prec3); - // Serialize Do Not Change Internal State. std::wstringstream ss4; ss4 << std::oct << std::setprecision(5) << std::boolalpha << std::setfill(ss4.widen('#')); @@ -887,7 +810,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, deserialize) using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(82); const auto expected_b = static_cast(99); const unsigned expected_srb = 5U; @@ -896,7 +818,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, deserialize) uqr_dist dist_instance_base(dist_instance_ref); - // Valid Deserialization (Narrow String) std::stringstream ss1, ss1_1, ss1_2; uqr_dist dist_instance1, dist_instance2; @@ -921,7 +842,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, deserialize) EXPECT_EQ(ss1_1.str(), ss1_2.str()); - // Valid Deserialization (Wide String) std::wstringstream ss2, ss2_1, ss2_2; uqr_dist dist_instance3, dist_instance4; @@ -946,7 +866,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, deserialize) EXPECT_EQ(ss2_1.str(), ss2_2.str()); - // Invalid Deserialization std::wstringstream ss3; uqr_dist dist_instance5(dist_instance_ref); @@ -964,13 +883,11 @@ TYPED_TEST(uniform_quantized_real_distribution_test, deserialize) EXPECT_TRUE(dist_instance5 == dist_instance_ref); } - -TYPED_TEST(uniform_quantized_real_distribution_test, generate_random) +TYPED_TEST(uniform_quantized_real_distribution_test, DISABLED_generate_random) { using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - constexpr auto val_zero = static_cast(0); const auto expected_fract = static_cast(0.5); @@ -978,7 +895,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, generate_random) const auto expected_b = static_cast(-92); const unsigned expected_srb = 4U; - std::mt19937_64 g1; uqr_dist dist1(expected_a, expected_b, expected_srb); @@ -1003,7 +919,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_degen_a_b) const auto expected_b = static_cast(110); const unsigned expected_srb = 3U; - std::mt19937_64 g1; uqr_dist dist1(expected_a, expected_b, expected_srb); @@ -1015,7 +930,7 @@ TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_degen_a_b) } } -TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_degen_srb) +TYPED_TEST(uniform_quantized_real_distribution_test, DISABLED_generate_random_degen_srb) { using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; @@ -1024,7 +939,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_degen_srb) const auto expected_b = static_cast(-92); const unsigned expected_srb = 0U; - std::mt19937_64 g1; uqr_dist dist1(expected_a, expected_b, expected_srb); @@ -1041,7 +955,7 @@ TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_degen_srb) std::cout << "a: " << count_a << ", b: " << count_b << std::endl; } -TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_c9) +TYPED_TEST(uniform_quantized_real_distribution_test, DISABLED_generate_random_c9) { using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; @@ -1050,7 +964,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_c9) const auto expected_b = static_cast(2); const unsigned expected_srb = 3U; - std::mt19937_64 g1; uqr_dist dist1(expected_a, expected_b, expected_srb); @@ -1075,7 +988,7 @@ TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_c9) std::cout << std::endl; } -TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_c17) +TYPED_TEST(uniform_quantized_real_distribution_test, DISABLED_generate_random_c17) { using uqr_dist = typename TestFixture::uqr_dist; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; @@ -1084,7 +997,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_c17) const auto expected_b = static_cast(4); const unsigned expected_srb = 4U; - std::mt19937_64 g1; uqr_dist dist1(expected_a, expected_b, expected_srb); @@ -1109,13 +1021,12 @@ TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_c17) std::cout << std::endl; } -TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_param) +TYPED_TEST(uniform_quantized_real_distribution_test, DISABLED_generate_random_param) { using uqr_dist = typename TestFixture::uqr_dist; using uqr_dist_param = typename TestFixture::uqr_dist_param; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - constexpr auto val_zero = static_cast(0); auto expected_fract = static_cast(0.125); @@ -1128,7 +1039,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_param) const auto test_b = static_cast(18); const unsigned test_srb = 5U; - // Temporary Switch Of Param std::mt19937_64 g1; uqr_dist dist1(test_a, test_b, test_srb); @@ -1145,7 +1055,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_param) EXPECT_EQ(std::modf(rnd_val / expected_fract, &actual_ipart), val_zero); } - // Original Param expected_fract = test_fract; expected_a = test_a; @@ -1168,18 +1077,16 @@ TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_param) } } -TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_equivalence) +TYPED_TEST(uniform_quantized_real_distribution_test, DISABLED_generate_random_equivalence) { using uqr_dist = typename TestFixture::uqr_dist; using uqr_dist_param = typename TestFixture::uqr_dist_param; using expected_uqr_dist_rt = typename TestFixture::expected_uqr_dist_rt; - const auto expected_a = static_cast(16); const auto expected_b = static_cast(32); const unsigned expected_srb = 5U; - // Equivalent Initialization. std::mt19937_64 g1, g2, g3, g4, g5, g6, g7, g8, g9; uqr_dist dist1(expected_a, expected_b, expected_srb), dist1_1(expected_a, expected_b, expected_srb); @@ -1209,7 +1116,6 @@ TYPED_TEST(uniform_quantized_real_distribution_test, generate_random_equivalence EXPECT_EQ(rnd_val1, rnd_val5); } - // Equivalent Assignment And Serialization. dist6.reset(); dist6 = dist1; diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/activation_grad_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/activation_grad_gpu_test.cpp index 7cc7865..2637144 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/activation_grad_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/activation_grad_gpu_test.cpp @@ -19,13 +19,13 @@ #include #include #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/activation_grad.hpp" -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/activation_grad.hpp" +#include +#include +#include +#include #include "test_utils/test_utils.h" #include "test_utils/float16.h" @@ -60,13 +60,13 @@ TEST(activation_grad_f16_fw_gpu, basic_bfyx_all_functions) FLOAT16(32.0f), FLOAT16(-32.0f), FLOAT16(32.0f), FLOAT16(52.0f), FLOAT16(12.0f), FLOAT16(12.0f), FLOAT16(12.0f), FLOAT16(12.0f), FLOAT16(-12.0f), FLOAT16(12.0f) }); - std::vector funcs = { - activation_grad_none, - activation_grad_relu, - activation_grad_relu_negative_slope, + std::vector funcs = { + activation_grad_func::none, + activation_grad_func::relu, + activation_grad_func::relu_negative_slope, }; - cldnn_activation_additional_params params = { 0.5f, 2.5f }; + activation_additional_params params = { 0.5f, 2.5f }; set_values(input_params, { FLOAT16(params.a), FLOAT16(params.b) }); for (uint8_t i = 0; i < 2; i++) @@ -114,13 +114,13 @@ TEST(activation_grad_f16_fw_gpu, basic_bfyx_all_functions) { switch (func) { - case activation_grad_none: + case activation_grad_func::none: EXPECT_FLOAT_EQ(float16_to_float32(input_grad_ptr[i]), float16_to_float32(output_ptr[i])); break; - case activation_grad_relu: + case activation_grad_func::relu: EXPECT_FLOAT_EQ(float16_to_float32(input_grad_ptr[i]) * (float16_to_float32(input_ptr[i]) > 0), float16_to_float32(output_ptr[i])); break; - case activation_grad_relu_negative_slope: + case activation_grad_func::relu_negative_slope: EXPECT_FLOAT_EQ(float16_to_float32(input_grad_ptr[i]) * ((float16_to_float32(input_ptr[i]) > 0) + params.a * (float16_to_float32(input_ptr[i]) <= 0)), float16_to_float32(output_ptr[i])); break; default: @@ -159,13 +159,13 @@ TEST(activation_grad_f32_fw_gpu, basic_bfyx_all_functions) 32.0f, -32.0f, 32.0f, 52.0f, 12.0f, 12.0f, 12.0f, 12.0f, -12.0f, 12.0f }); - std::vector funcs = { - activation_grad_none, - activation_grad_relu, - activation_grad_relu_negative_slope, + std::vector funcs = { + activation_grad_func::none, + activation_grad_func::relu, + activation_grad_func::relu_negative_slope, }; - cldnn_activation_additional_params params = { 0.5f, 2.5f }; + activation_additional_params params = { 0.5f, 2.5f }; set_values(input_params, { params.a, params.b }); for (uint8_t i = 0; i < 2; i++) @@ -213,13 +213,13 @@ TEST(activation_grad_f32_fw_gpu, basic_bfyx_all_functions) { switch (func) { - case activation_grad_none: + case activation_grad_func::none: EXPECT_FLOAT_EQ(input_grad_ptr[i], output_ptr[i]); break; - case activation_grad_relu: + case activation_grad_func::relu: EXPECT_FLOAT_EQ(input_grad_ptr[i] * (input_ptr[i] > 0), output_ptr[i]); break; - case activation_grad_relu_negative_slope: + case activation_grad_func::relu_negative_slope: EXPECT_FLOAT_EQ(input_grad_ptr[i] * ((input_ptr[i] > 0) + params.a * (input_ptr[i] <= 0)), output_ptr[i]); break; default: @@ -228,4 +228,4 @@ TEST(activation_grad_f32_fw_gpu, basic_bfyx_all_functions) } } } -} \ No newline at end of file +} diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/activation_simple_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/activation_simple_gpu_test.cpp index 7c36f3a..cd25357 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/activation_simple_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/activation_simple_gpu_test.cpp @@ -19,21 +19,20 @@ #include #include #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/activation.hpp" -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/activation.hpp" +#include +#include +#include +#include #include "test_utils/test_utils.h" #include "test_utils/float16.h" -#include "api/CPP/reorder.hpp" +#include "api/reorder.hpp" using namespace cldnn; using namespace tests; - TEST(activation_f32_fw_gpu, not_basic_yxfb) { // Input: // 1 0 -3 4 5 @@ -63,7 +62,7 @@ TEST(activation_f32_fw_gpu, not_basic_yxfb) { topology topology( input_layout("input", input.get_layout()), - activation("not", "input", activation_not)); + activation("not", "input", activation_func::negation)); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -107,7 +106,7 @@ TEST(activation_f32_fw_gpu, erf_basic_yxfb) { topology topology( input_layout("input", input.get_layout()), - activation("not", "input", activation_erf)); + activation("not", "input", activation_func::erf)); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -144,7 +143,7 @@ TEST(activation_f32_fw_gpu, hard_sigmoid_basic_yxfb) { const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::yxfb, { 1, 1, 5, 4 } }); - cldnn_activation_additional_params params = { 1.0f, 0.5f }; + activation_additional_params params = { 1.0f, 0.5f }; set_values(input, { 1.0f, 0.0f, -3.0f, 4.0f, 5.0f, 0.0f, 2.0f, 3.0f, 4.0f, -6.0f, @@ -153,7 +152,7 @@ TEST(activation_f32_fw_gpu, hard_sigmoid_basic_yxfb) { topology topology( input_layout("input", input.get_layout()), - activation("not", "input", activation_hard_sigmoid, params)); + activation("not", "input", activation_func::hard_sigmoid, params)); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -199,7 +198,7 @@ TEST(activation_f32_fw_gpu, reciprocal_basic_yxfb) { topology topology( input_layout("input", input.get_layout()), - activation("not", "input", activation_reciprocal)); + activation("not", "input", activation_func::reciprocal)); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -237,7 +236,7 @@ TEST(activation_f32_fw_gpu, selu_basic_yxfb) { const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::yxfb, { 1, 1, 5, 4 } }); - cldnn_activation_additional_params params = { 1.0f, 0.5f }; + activation_additional_params params = { 1.0f, 0.5f }; set_values(input, { 1.0f, 0.3f, -3.0f, 4.0f, 5.0f, 21.0f, 2.0f, 3.0f, 4.0f, -6.0f, @@ -246,7 +245,7 @@ TEST(activation_f32_fw_gpu, selu_basic_yxfb) { topology topology( input_layout("input", input.get_layout()), - activation("not", "input", activation_selu, params)); + activation("not", "input", activation_func::selu, params)); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -293,7 +292,7 @@ TEST(activation_f32_fw_gpu, softplus_basic_yxfb) { topology topology( input_layout("input", input.get_layout()), - activation("not", "input", activation_softplus)); + activation("not", "input", activation_func::softplus)); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -339,7 +338,7 @@ TEST(activation_f32_fw_gpu, softsign_basic_yxfb) { topology topology( input_layout("input", input.get_layout()), - activation("not", "input", activation_softsign)); + activation("not", "input", activation_func::softsign)); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -367,7 +366,6 @@ TEST(activation_f32_fw_gpu, softsign_basic_yxfb) { } } - TEST(activation_f32_fw_gpu, sign_basic_yxfb) { // Input: // 1 0 -3 4 5 @@ -386,7 +384,7 @@ TEST(activation_f32_fw_gpu, sign_basic_yxfb) { topology topology( input_layout("input", input.get_layout()), - activation("not", "input", activation_sign)); + activation("not", "input", activation_func::sign)); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -424,7 +422,7 @@ TEST(activation_f32_fw_gpu, pow_basic_yxfb) { topology topology( input_layout("input", input.get_layout()), - activation("pow", "input", activation_pow, { 2.0f })); + activation("pow", "input", activation_func::pow, { 2.0f, 0.0f })); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -460,7 +458,7 @@ TEST(activation_f16_fw_gpu, pow_basic_yxfb) { topology topology( input_layout("input", input.get_layout()), - activation("pow", "input", activation_pow, { FLOAT16(3.0f) })); + activation("pow", "input", activation_func::pow, { FLOAT16(3.0f), FLOAT16(0.0f) })); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -496,7 +494,7 @@ TEST(activation_f16_fw_gpu, linear_basic_yxfb) { topology topology( input_layout("input", input.get_layout()), - activation("linear", "input", activation_linear, {FLOAT16(3.0f), FLOAT16(2.0f)})); + activation("linear", "input", activation_func::linear, {FLOAT16(3.0f), FLOAT16(2.0f)})); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -553,7 +551,7 @@ TEST(activation_f32_fw_gpu, relu_basic_yxfb) { topology topology( input_layout("input", input.get_layout()), - activation("relu", "input", activation_relu_negative_slope, { 0.5f, 0.f }, padding{ { 0, 0, 0, 0 }, 0 })); + activation("relu", "input", activation_func::relu_negative_slope, { 0.5f, 0.f }, padding{ { 0, 0, 0, 0 }, 0 })); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -629,7 +627,7 @@ TEST(activation_f32_fw_gpu, relu_basic_bfzyx) { topology topology( input_layout("input", input.get_layout()), - activation("relu", "input", activation_relu_negative_slope, { 0.5f, 0.f }, padding{ { 0, 0, 0, 0, 0 }, 0 })); + activation("relu", "input", activation_func::relu_negative_slope, { 0.5f, 0.f }, padding{ { 0, 0, 0, 0, 0 }, 0 })); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -657,7 +655,7 @@ TEST(activation_f32_fw_gpu, relu_basic_bfzyx) { } } -TEST(activation_f32_fw_gpu, basic_yxfb_all_functions) +TEST(activation_f32_fw_gpu, basic_yxfb_all_functions) { // Input: // 1 -2 -3 4 5 @@ -671,39 +669,39 @@ TEST(activation_f32_fw_gpu, basic_yxfb_all_functions) const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::yxfb,{ 1, 1, 5, 4 } }); - auto input_params = memory::allocate(engine, { data_types::f32, format::yxfb,{ 1, 1, 2, 1 } }); + auto input_params = memory::allocate(engine, { data_types::f32, format::yxfb,{ 1, 2, 1, 1 } }); set_values(input, { 0.0f, -2.0f, -3.0f, 4.0f, 5.0f, 2.0f, 2.0f, 3.0f, 4.0f, -6.0f, 3.0f, -3.0f, 3.0f, 5.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f }); - std::vector funcs = { - activation_none, - activation_logistic, - activation_hyperbolic_tan, - activation_relu, - activation_relu_negative_slope, - activation_clamp, - activation_softrelu, - activation_abs, - activation_linear, - activation_square, - activation_sqrt, - activation_elu, - activation_sin, - activation_sinh, - activation_cos, - activation_cosh, - activation_exp, - activation_not, - activation_log2, - activation_tan, - activation_negative, - activation_abs + std::vector funcs = { + activation_func::none, + activation_func::logistic, + activation_func::hyperbolic_tan, + activation_func::relu, + activation_func::relu_negative_slope, + activation_func::clamp, + activation_func::softrelu, + activation_func::abs, + activation_func::linear, + activation_func::square, + activation_func::sqrt, + activation_func::elu, + activation_func::sin, + activation_func::sinh, + activation_func::cos, + activation_func::cosh, + activation_func::exp, + activation_func::negation, + activation_func::log2, + activation_func::tan, + activation_func::negative, + activation_func::abs }; - cldnn_activation_additional_params params = { 0.5f, 2.5f }; + activation_additional_params params = { 0.5f, 2.5f }; set_values(input_params, { params.a, params.b }); for (uint8_t i = 0 ; i < 2 ; i++) @@ -747,71 +745,71 @@ TEST(activation_f32_fw_gpu, basic_yxfb_all_functions) { switch (func) { - case activation_none: + case activation_func::none: EXPECT_FLOAT_EQ(input_ptr[i], output_ptr[i]); break; - case activation_logistic: + case activation_func::logistic: EXPECT_FLOAT_EQ(1.f / (1.f + std::exp((float)-input_ptr[i])), output_ptr[i]); break; - case activation_hyperbolic_tan: + case activation_func::hyperbolic_tan: EXPECT_FLOAT_EQ(std::tanh((float)input_ptr[i]), output_ptr[i]); break; - case activation_relu: + case activation_func::relu: EXPECT_FLOAT_EQ(std::fmax((float)input_ptr[i], 0.f), output_ptr[i]); break; - case activation_clamp: + case activation_func::clamp: EXPECT_FLOAT_EQ(std::fmin((float)std::fmax((float)input_ptr[i], params.a), params.b), output_ptr[i]); break; - case activation_softrelu: + case activation_func::softrelu: EXPECT_FLOAT_EQ(std::log(1.f + std::exp((float)input_ptr[i])), output_ptr[i]); break; - case activation_abs: + case activation_func::abs: EXPECT_FLOAT_EQ(std::fabs(input_ptr[i]), output_ptr[i]); break; - case activation_linear: + case activation_func::linear: EXPECT_FLOAT_EQ((params.a*input_ptr[i] + params.b), output_ptr[i]); break; - case activation_square: + case activation_func::square: EXPECT_FLOAT_EQ((input_ptr[i] * input_ptr[i]), output_ptr[i]); break; - case activation_sqrt: + case activation_func::sqrt: if (input_ptr[i] >= 0) { EXPECT_FLOAT_EQ(std::sqrt((float)input_ptr[i]), output_ptr[i]); } break; - case activation_elu: + case activation_func::elu: EXPECT_FLOAT_EQ(std::fmax((float)input_ptr[i], 0.0f) + params.a*(std::exp(std::fmin((float)input_ptr[i], 0.0f)) - 1), output_ptr[i]); break; - case activation_sin: + case activation_func::sin: EXPECT_FLOAT_EQ(std::sin((float)input_ptr[i]), output_ptr[i]); break; - case activation_sinh: + case activation_func::sinh: EXPECT_FLOAT_EQ(std::sinh((float)input_ptr[i]), output_ptr[i]); break; - case activation_cos: + case activation_func::cos: EXPECT_FLOAT_EQ(std::cos((float)input_ptr[i]), output_ptr[i]); break; - case activation_cosh: + case activation_func::cosh: EXPECT_FLOAT_EQ(std::cosh((float)input_ptr[i]), output_ptr[i]); break; - case activation_exp: + case activation_func::exp: EXPECT_FLOAT_EQ(std::exp((float)input_ptr[i]), output_ptr[i]); break; - case activation_not: + case activation_func::negation: EXPECT_FLOAT_EQ((float)(!input_ptr[i]), output_ptr[i]); break; - case activation_log2: + case activation_func::log2: if (input_ptr[i] > 0) //logarithm exist only for positive real values { EXPECT_FLOAT_EQ(std::log2((float)input_ptr[i]), output_ptr[i]); } break; - case activation_tan: + case activation_func::tan: EXPECT_FLOAT_EQ(std::tan((float)input_ptr[i]), output_ptr[i]); break; - case activation_negative: + case activation_func::negative: EXPECT_FLOAT_EQ(-((float)input_ptr[i]), output_ptr[i]); break; default: @@ -829,15 +827,15 @@ TEST(activation_f32_fw_gpu, basic_yxfb_asin_acos_log_atan) auto input = memory::allocate(engine, { data_types::f32, format::yxfb,{ 1, 1, 2, 4 } }); set_values(input, { 0.12f, 0.56f, 0.45f, 0.789f, 0.546f, 0.999f, 0.7899f, 0.6677f}); - std::vector funcs = { - activation_asin, - activation_acos, - activation_log, - activation_log2, - activation_atan, - activation_asin, - activation_asinh, - activation_atanh + std::vector funcs = { + activation_func::asin, + activation_func::acos, + activation_func::log, + activation_func::log2, + activation_func::atan, + activation_func::asin, + activation_func::asinh, + activation_func::atanh }; for (auto func : funcs) @@ -870,28 +868,28 @@ TEST(activation_f32_fw_gpu, basic_yxfb_asin_acos_log_atan) { switch (func) { - case activation_asin: + case activation_func::asin: EXPECT_FLOAT_EQ(std::asin((float)input_ptr[i]), output_ptr[i]); break; - case activation_acos: + case activation_func::acos: EXPECT_FLOAT_EQ(std::acos((float)input_ptr[i]), output_ptr[i]); break; - case activation_log: + case activation_func::log: EXPECT_FLOAT_EQ(std::log((float)input_ptr[i]), output_ptr[i]); break; - case activation_log2: + case activation_func::log2: EXPECT_FLOAT_EQ(std::log2((float)input_ptr[i]), output_ptr[i]); break; - case activation_atan: + case activation_func::atan: EXPECT_FLOAT_EQ(std::atan((float)input_ptr[i]), output_ptr[i]); break; - case activation_asinh: + case activation_func::asinh: EXPECT_FLOAT_EQ(std::asinh((float)input_ptr[i]), output_ptr[i]); break; - case activation_acosh: + case activation_func::acosh: EXPECT_FLOAT_EQ(std::acosh((float)input_ptr[i]), output_ptr[i]); break; - case activation_atanh: + case activation_func::atanh: EXPECT_FLOAT_EQ(std::atanh((float)input_ptr[i]), output_ptr[i]); break; default: @@ -930,7 +928,7 @@ TEST(activation_f32_fw_gpu, relu_basic_acosh_yxfb) { topology topology( input_layout("input", input.get_layout()), reorder("reorder", "input", input.get_layout().with_padding(padding{{0, 0, 2, 1}, 0})), - activation("relu", "reorder", activation_acosh, {0.5f, 0.f}, padding{{0, 0, 0, 0}, 0})); + activation("relu", "reorder", activation_func::acosh, {0.5f, 0.f}, padding{{0, 0, 0, 0}, 0})); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -996,7 +994,7 @@ TEST(activation_f32_fw_gpu, relu_basic_input_padding_yxfb) { topology topology( input_layout("input", input.get_layout()), reorder("reorder", "input", input.get_layout().with_padding(padding{ { 0, 0, 2, 1 }, 0 })), - activation("relu", "reorder", activation_relu_negative_slope, { 0.5f, 0.f }, padding{ { 0, 0, 0, 0 }, 0 })); + activation("relu", "reorder", activation_func::relu_negative_slope, { 0.5f, 0.f }, padding{ { 0, 0, 0, 0 }, 0 })); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -1083,7 +1081,7 @@ TEST(activation_f32_fw_gpu, relu_basic_input_padding_bfzyx) { topology topology( input_layout("input", input.get_layout()), reorder("reorder", "input", input.get_layout().with_padding(padding{ { 0, 0, 2, 1, 0 }, 0 })), - activation("relu", "reorder", activation_relu_negative_slope, { 0.5f, 0.f }, padding{ { 0, 0, 0, 0, 0 }, 0 })); + activation("relu", "reorder", activation_func::relu_negative_slope, { 0.5f, 0.f }, padding{ { 0, 0, 0, 0, 0 }, 0 })); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -1156,7 +1154,7 @@ TEST(activation_f32_fw_gpu, relu_basic_output_padding_yxfb) { topology topology( input_layout("input", input.get_layout()), - activation("relu", "input", activation_relu_negative_slope, { 0.5f, 0.f }, padding{ { 0, 0, 3, 3 }, 0 })); + activation("relu", "input", activation_func::relu_negative_slope, { 0.5f, 0.f }, padding{ { 0, 0, 3, 3 }, 0 })); network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -1190,9 +1188,9 @@ TEST(activation_f32_fw_gpu, basic_yxfb_floor_ceil) auto input = memory::allocate(engine, { data_types::f32, format::yxfb,{ 1, 1, 2, 4 } }); set_values(input, { 0.01f, 0.99f, -0.01f, -0.99f, 1.1f, 1.0f, 0.0f, -1.1f }); - std::vector funcs = { - activation_floor, - activation_ceil + std::vector funcs = { + activation_func::floor, + activation_func::ceil }; for (auto func : funcs) @@ -1225,10 +1223,10 @@ TEST(activation_f32_fw_gpu, basic_yxfb_floor_ceil) { switch (func) { - case activation_floor: + case activation_func::floor: EXPECT_FLOAT_EQ(std::floor((float)input_ptr[i]), output_ptr[i]); break; - case activation_ceil: + case activation_func::ceil: EXPECT_FLOAT_EQ(std::ceil((float)input_ptr[i]), output_ptr[i]); break; default: @@ -1252,10 +1250,10 @@ TEST(activation_i8_fw_gpu, basic_yxfb_all_funcs) set_values(input, input_vec); // functions valid for int8 type input - std::vector funcs = { - activation_none, - activation_negative, - activation_not + std::vector funcs = { + activation_func::none, + activation_func::negative, + activation_func::negation }; for (auto func : funcs) @@ -1280,13 +1278,13 @@ TEST(activation_i8_fw_gpu, basic_yxfb_all_funcs) { switch (func) { - case activation_none: + case activation_func::none: EXPECT_EQ((int8_t)input_ptr[i], output_ptr[i]); break; - case activation_negative: + case activation_func::negative: EXPECT_EQ(-((int8_t)input_ptr[i]), output_ptr[i]); break; - case activation_not: + case activation_func::negation: EXPECT_EQ(!((int8_t)input_ptr[i]), output_ptr[i]); break; default: diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/add_reorders_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/add_reorders_gpu_test.cpp index 8a53e8f..a81d4d0 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/add_reorders_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/add_reorders_gpu_test.cpp @@ -16,22 +16,22 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include +#include +#include #include "test_utils/test_utils.h" -#include -#include -#include -#include -#include -#include -#include - -#include -#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include using namespace cldnn; using namespace tests; diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/apply_adam_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/apply_adam_gpu_test.cpp index 6d2250c..220b176 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/apply_adam_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/apply_adam_gpu_test.cpp @@ -16,17 +16,17 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/apply_adam.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/apply_adam.hpp" +#include +#include +#include #include "test_utils/test_utils.h" -#include -#include -#include -#include +#include +#include +#include +#include using namespace cldnn; using namespace tests; @@ -61,9 +61,9 @@ TEST(apply_adam_gpu, basic_in2x2x3x2_bfyx) { topology.add(data("beta1_power_t1", beta1_power)); topology.add(data("beta2_power_t1", beta2_power)); topology.add(apply_adam("apply_adam", "input", "m", "v", "beta1_power_t1", "beta2_power_t1", lr, beta1, beta2, epsilon)); - topology.add(activation("relu", "input", activation_linear, { 4.f, 0.f })); - topology.add(activation("beta1_power_t2", "beta1_power_t1", activation_linear, { beta1, 0.f })); - topology.add(activation("beta2_power_t2", "beta2_power_t1", activation_linear, { beta2, 0.f })); + topology.add(activation("relu", "input", activation_func::linear, { 4.f, 0.f })); + topology.add(activation("beta1_power_t2", "beta1_power_t1", activation_func::linear, { beta1, 0.f })); + topology.add(activation("beta2_power_t2", "beta2_power_t1", activation_func::linear, { beta2, 0.f })); topology.add(apply_adam("apply_adam2", "relu", "m", "v", "beta1_power_t2", "beta2_power_t2", lr, beta1, beta2, epsilon, "apply_adam")); topology.add(mutable_data("var", { "apply_adam", "apply_adam2" }, var)); diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/arg_max_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/arg_max_gpu_test.cpp index ea41280..d5c77d3 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/arg_max_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/arg_max_gpu_test.cpp @@ -16,23 +16,19 @@ */ #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/arg_max_min.hpp" -#include -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/arg_max_min.hpp" +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" using namespace cldnn; -using namespace std; using namespace tests; - - - template void generic_arg_max_test_xyf(int input_b, int input_f, int input_y, int input_x, arg_max_min::out_type mode, bool expect_throw = false) { @@ -98,7 +94,7 @@ TEST(arg_max_gpu_batch_one, base) { topology.add(input_layout("input", input.get_layout())); topology.add(arg_max_min("arg_max", { "input" }, arg_max_min::max, top_k)); - vector input_vec = { + std::vector input_vec = { //y0x0 y0x1 y1x0 y1x1 /*b0f0*/0.1f, -0.1f, 0.9f, 1.5f, /*b0f1*/0.2f, 0.2f, -10.f, 5.2f, @@ -176,7 +172,7 @@ TEST(arg_max_gpu_top_k, base) { topology.add(input_layout("input", input.get_layout())); topology.add(arg_max_min("arg_max", { "input" }, arg_max_min::max, top_k)); - vector input_vec = { + std::vector input_vec = { //y0x0 y0x1 y1x0 y1x1 /*b0f0*/0.1f, -0.1f, 0.9f, 1.5f, /*b0f1*/0.2f, 0.2f, -10.f, 5.2f, @@ -260,7 +256,7 @@ TEST(arg_max_gpu_min_top_k, base) { topology.add(input_layout("input", input.get_layout())); topology.add(arg_max_min("arg_max", { "input" }, arg_max_min::min, top_k)); - vector input_vec = { + std::vector input_vec = { //f0b0 f0b1 f1b0 f1b1 /*x0y0*/0.1f, -0.1f, 0.9f, 1.5f, /*x0y1*/0.2f, 0.2f, -10.f, 5.2f, @@ -342,7 +338,7 @@ TEST(arg_max_gpu_min_axis_batch, base) { topology.add(input_layout("input", input.get_layout())); topology.add(arg_max_min("arg_max", { "input" }, arg_max_min::min, top_k, arg_max_min::batch)); - vector input_vec = { + std::vector input_vec = { //y0x0 y0x1 y1x0 y1x1 /*b0f0*/0.1f, -0.1f, 0.9f, 1.5f, /*b0f1*/0.2f, 0.2f, -10.f, 5.2f, @@ -431,7 +427,7 @@ TEST(arg_max_gpu_min_axis_batch, i32) { topology.add(input_layout("input", input.get_layout())); topology.add(arg_max_min("arg_max", { "input" }, arg_max_min::min, top_k, arg_max_min::batch, arg_max_min::sort_by_values, false, padding(), data_types::i32)); - vector input_vec = { + std::vector input_vec = { //y0x0 y0x1 y1x0 y1x1 /*b0f0*/0.1f, -0.1f, 0.9f, 1.5f, /*b0f1*/0.2f, 0.2f, -10.f, 5.2f, @@ -476,7 +472,7 @@ TEST(arg_max_gpu_min_axis_batch_bfzyx, i32) { topology.add(input_layout("input", input.get_layout())); topology.add(arg_max_min("arg_max", { "input" }, arg_max_min::min, top_k, arg_max_min::batch, arg_max_min::sort_by_values, false, padding(), data_types::i32)); - vector input_vec = { + std::vector input_vec = { //y0x0 y0x1 y1x0 y1x1 /*b0f0*/0.1f, -0.1f, 0.9f, 1.5f, /*b0f1*/0.2f, 0.2f, -10.f, 5.2f, @@ -520,7 +516,7 @@ TEST(arg_max_gpu_min_axis_y_yxfb, f32) { topology.add(input_layout("input", input.get_layout())); topology.add(arg_max_min("arg_max", { "input" }, arg_max_min::max, top_k, arg_max_min::y, arg_max_min::sort_by_values, false, padding(), data_types::f32)); - vector input_vec = { + std::vector input_vec = { 0.1f, -0.1f, 0.9f, 1.5f, 0.2f, 0.2f, @@ -531,7 +527,6 @@ TEST(arg_max_gpu_min_axis_y_yxfb, f32) { 0.2f, 0.2f, -10.f, 4.2f, - 3.f, 0.5f, 7.f, 10.f, 4.f, 0.5f, @@ -543,7 +538,7 @@ TEST(arg_max_gpu_min_axis_y_yxfb, f32) { 8.f, 8.2f }; - vector ref_vec = { + std::vector ref_vec = { 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, @@ -587,7 +582,7 @@ TEST(arg_max_gpu_min_axis_batch_yxfb, f32) { topology.add(input_layout("input", input.get_layout())); topology.add(arg_max_min("arg_max", { "input" }, arg_max_min::max, top_k, arg_max_min::batch, arg_max_min::sort_by_values, false, padding(), data_types::f32)); - vector input_vec = { + std::vector input_vec = { 0.1f, -0.1f, 0.9f, 1.5f, 0.2f, 0.2f, @@ -598,7 +593,6 @@ TEST(arg_max_gpu_min_axis_batch_yxfb, f32) { 0.2f, 0.2f, -10.f, 4.2f, - 3.f, 0.5f, 7.f, 10.f, 4.f, 0.5f, @@ -610,7 +604,7 @@ TEST(arg_max_gpu_min_axis_batch_yxfb, f32) { 8.f, 8.2f }; - vector ref_vec = { + std::vector ref_vec = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, @@ -654,7 +648,7 @@ TEST(arg_max_gpu_min_axis_y_yxfb_topk_2, f32) { topology.add(input_layout("input", input.get_layout())); topology.add(arg_max_min("arg_max", { "input" }, arg_max_min::max, top_k, arg_max_min::y, arg_max_min::sort_by_values, false, padding(), data_types::f32)); - vector input_vec = { + std::vector input_vec = { 0.1f, -0.1f, 0.9f, 1.5f, 0.2f, 0.2f, @@ -665,7 +659,6 @@ TEST(arg_max_gpu_min_axis_y_yxfb_topk_2, f32) { 0.2f, 0.2f, -10.f, 4.2f, - 3.f, 0.5f, 7.f, 10.f, 4.f, 0.5f, @@ -677,7 +670,7 @@ TEST(arg_max_gpu_min_axis_y_yxfb_topk_2, f32) { 8.f, 8.2f }; - vector ref_vec = { + std::vector ref_vec = { 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, @@ -688,7 +681,6 @@ TEST(arg_max_gpu_min_axis_y_yxfb_topk_2, f32) { 1.f, 1.f, 1.f, 1.f, - 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, @@ -736,7 +728,7 @@ TEST(top_k_layer_tests, second_output) { topology.add(mutable_data("second_output", second_output)); topology.add(arg_max_min("arg_max", { "input", "const", "second_output" }, arg_max_min::min, top_k, arg_max_min::batch)); - vector input_vec = { + std::vector input_vec = { //y0x0 y0x1 y1x0 y1x1 /*b0f0*/0.1f, -0.1f, 0.9f, 1.5f, /*b0f1*/0.2f, 0.2f, -10.f, 5.2f, @@ -788,7 +780,7 @@ TEST(top_k_layer_tests, second_output2) { topology.add(mutable_data("second_output", second_output)); topology.add(arg_max_min("arg_max", { "input", "const", "second_output" }, arg_max_min::max, top_k, arg_max_min::batch, arg_max_min::sort_by_values, false, padding(), data_types::f32)); - vector input_vec = { + std::vector input_vec = { 0.1f, -0.1f, 0.9f, 1.5f, 0.2f, 0.2f, @@ -799,7 +791,6 @@ TEST(top_k_layer_tests, second_output2) { 0.2f, 0.2f, -10.f, 4.2f, - 3.f, 0.5f, 7.f, 10.f, 4.f, 0.5f, @@ -811,7 +802,7 @@ TEST(top_k_layer_tests, second_output2) { 8.f, 8.2f }; - vector ref_vec = { + std::vector ref_vec = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, @@ -823,7 +814,7 @@ TEST(top_k_layer_tests, second_output2) { 0.f, 1.f }; - vector second_ref_vec = { + std::vector second_ref_vec = { 0.1f, 1.5f, 0.2f, @@ -834,7 +825,6 @@ TEST(top_k_layer_tests, second_output2) { 0.2f, 4.2f, - 3.f, 10.f, 4.f, @@ -882,7 +872,7 @@ TEST(arg_max_gpu_min_axis_y_yxfb_topk_2, sort_by_values) { topology.add(input_layout("input", input.get_layout())); topology.add(arg_max_min("arg_max", { "input" }, arg_max_min::max, top_k, arg_max_min::y, arg_max_min::sort_by_values, false, padding(), data_types::f32)); - vector input_vec = { + std::vector input_vec = { 0.1f, -0.1f, 0.9f, 1.5f, 0.2f, 0.2f, @@ -893,7 +883,6 @@ TEST(arg_max_gpu_min_axis_y_yxfb_topk_2, sort_by_values) { 0.2f, 0.2f, -10.f, 4.2f, - 3.f, 0.5f, 7.f, 10.f, 4.f, 0.5f, @@ -905,7 +894,7 @@ TEST(arg_max_gpu_min_axis_y_yxfb_topk_2, sort_by_values) { 8.f, 8.2f }; - vector ref_vec = { + std::vector ref_vec = { 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, @@ -916,7 +905,6 @@ TEST(arg_max_gpu_min_axis_y_yxfb_topk_2, sort_by_values) { 1.f, 1.f, 1.f, 1.f, - 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, @@ -951,7 +939,6 @@ TEST(arg_max_gpu_min_axis_y_yxfb_topk_2, sort_by_values) { } } - TEST(arg_max_gpu_min_axis_y_yxfb_topk_2, sort_by_indices) { static const int32_t x_size = 2, y_size = 2, feature_num = 4, batch_num = 2; const auto& engine = get_test_engine(); @@ -961,7 +948,7 @@ TEST(arg_max_gpu_min_axis_y_yxfb_topk_2, sort_by_indices) { topology.add(input_layout("input", input.get_layout())); topology.add(arg_max_min("arg_max", { "input" }, arg_max_min::max, top_k, arg_max_min::y, arg_max_min::sort_by_indices, false, padding(), data_types::f32)); - vector input_vec = { + std::vector input_vec = { 0.1f, -0.1f, 0.9f, 1.5f, 0.2f, 0.2f, @@ -972,7 +959,6 @@ TEST(arg_max_gpu_min_axis_y_yxfb_topk_2, sort_by_indices) { 0.2f, 0.2f, -10.f, 4.2f, - 3.f, 0.5f, 7.f, 10.f, 4.f, 0.5f, @@ -984,7 +970,7 @@ TEST(arg_max_gpu_min_axis_y_yxfb_topk_2, sort_by_indices) { 8.f, 8.2f }; - vector ref_vec = { + std::vector ref_vec = { 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, @@ -995,7 +981,6 @@ TEST(arg_max_gpu_min_axis_y_yxfb_topk_2, sort_by_indices) { 0.f, 0.f, 0.f, 0.f, - 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/average_unpooling_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/average_unpooling_gpu_test.cpp index d537c89..53414e1 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/average_unpooling_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/average_unpooling_gpu_test.cpp @@ -16,17 +16,17 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/average_unpooling.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/average_unpooling.hpp" +#include +#include +#include #include "test_utils/test_utils.h" -#include -#include -#include -#include +#include +#include +#include +#include #include "test_utils/float16.h" using namespace cldnn; diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/barriers_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/barriers_test.cpp index 4ae9bcf..e4a5209 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/barriers_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/barriers_test.cpp @@ -17,13 +17,13 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/batch_norm_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/batch_norm_gpu_test.cpp index ee56281..d6e306d 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/batch_norm_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/batch_norm_gpu_test.cpp @@ -16,16 +16,16 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/batch_norm.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/batch_norm.hpp" +#include +#include +#include #include "test_utils/test_utils.h" -#include -#include -#include +#include +#include +#include using namespace cldnn; using namespace tests; @@ -49,7 +49,6 @@ TEST(batch_normalization_gpu, basic_in2x3x2x2) { // f0: 44.9305 // f1: 107.0624 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::yxfb, { 2, 2, 3, 2 } }); @@ -131,8 +130,6 @@ TEST(batch_normalization_gpu, basic_in2x3x2x2_scale_shift) { // f0: 0.0 // f1: 5.0 - - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::yxfb,{ 2, 2, 3, 2 } }); @@ -219,7 +216,6 @@ TEST(batch_normalization_gpu, basic_in2x3x2x2_with_var_mean_calc) { // f0: 44.9305 // f1: 107.0624 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::yxfb,{ 2, 2, 3, 2 } }); @@ -288,7 +284,6 @@ TEST(batch_normalization_gpu, basic_in2x3x2x2_with_var_mean_calc_no_inv_var) { // f0: 44.9305 // f1: 107.0624 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::yxfb,{ 2, 2, 3, 2 } }); @@ -363,7 +358,6 @@ TEST(batch_normalization_gpu, basic_in2x3x2x2_with_var_mean_calc_scale_shift) { // f0: 0.0 // f1: 5.0 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::yxfb,{ 2, 2, 3, 2 } }); @@ -455,7 +449,6 @@ TEST(batch_normalization_gpu, basic_in2x3x2x2_with_var_mean_calc_scale_shift_no_ // f0: 0.0 // f1: 5.0 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::yxfb,{ 2, 2, 3, 2 } }); @@ -544,7 +537,6 @@ TEST(batch_normalization_gpu, basic_in2x3x2x2_with_var_mean_outputs) { // f0: 0.0 // f1: 5.0 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::yxfb,{ 2, 2, 3, 2 } }); @@ -650,7 +642,6 @@ TEST(batch_normalization_gpu, basic_in2x3x2x2_with_var_mean_outputs_no_inv_var) // f0: 0.0 // f1: 5.0 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::yxfb,{ 2, 2, 3, 2 } }); @@ -775,7 +766,6 @@ TEST(batch_normalization_gpu, basic_in2x3x2x2_with_var_mean_outputs_error_non_eq EXPECT_ANY_THROW(network(engine, topology)); } - TEST(batch_normalization_gpu, basic_in2x2x3x2_bfyx) { // Mean : 3x2x2 // Input : 2x3x2x2 @@ -795,7 +785,6 @@ TEST(batch_normalization_gpu, basic_in2x2x3x2_bfyx) { // f0: 44.9305 // f1: 107.0624 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 2, 3, 2 } }); @@ -876,7 +865,6 @@ TEST(batch_normalization_gpu, basic_in2x2x3x2_bfyx_padding) { // f0: 44.9305 // f1: 107.0624 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 2, 3, 2 } }); @@ -992,7 +980,6 @@ TEST(batch_normalization_gpu, basic_to_string) { EXPECT_NE(network.get_primitive_info("batch_norm7").length(), zero_length); } - TEST(batch_normalization_gpu, basic_in2x3x2x2_yxfb_scale_shift_different_shapes) { const auto& engine = get_test_engine(); @@ -1369,7 +1356,6 @@ TEST(batch_normalization_gpu, basic_in2x2x3x2_byxf_with_var_mean_outputs_no_inv_ } } - TEST(batch_normalization_gpu, basic_in2x3x5x2_yxfb_scale_shift_different_shapes) { const auto& engine = get_test_engine(); @@ -1876,7 +1862,6 @@ TEST(batch_normalization_gpu, basic_in2x2x3x5_byxf_with_var_mean_outputs_no_inv_ } } - TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_b1c2h2w2) { const auto& engine = get_test_engine(); @@ -1998,7 +1983,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_b2c2h2w1) topology.add(mutable_data("variance", variance)); topology.add(batch_norm("batch_norm", "input", eps, "mean", "variance", "gamma", "beta")); - set_values(input, { 0.54881352f, 0.71518934f, @@ -2006,8 +1990,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_b2c2h2w1) 0.60276335f, 0.54488319f, - - 0.42365479f, 0.64589411f, @@ -2026,7 +2008,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_b2c2h2w1) -0.434702f, - -1.4011f, 0.548275f, @@ -2101,7 +2082,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_inference_b2c2h2w1) topology.add(data("variance", variance)); topology.add(batch_norm("batch_norm", "input", eps, "mean", "variance", "gamma", "beta")); - set_values(input, { 0.54881352f, 0.71518934f, @@ -2109,8 +2089,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_inference_b2c2h2w1) 0.60276335f, 0.54488319f, - - 0.42365479f, 0.64589411f, @@ -2196,7 +2174,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_b2c2h2w1_different_shapes) topology.add(mutable_data("variance", variance)); topology.add(batch_norm("batch_norm", "input", eps, "mean", "variance", "gamma", "beta")); - set_values(input, { 0.54881352f, 0.71518934f, @@ -2204,8 +2181,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_b2c2h2w1_different_shapes) 0.60276335f, 0.54488319f, - - 0.42365479f, 0.64589411f, @@ -2223,8 +2198,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_b2c2h2w1_different_shapes) -0.0963782f, -0.434702f, - - -1.4011f, 0.548275f, @@ -2299,7 +2272,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_inference_b2c2h2w1_differen topology.add(data("variance", variance)); topology.add(batch_norm("batch_norm", "input", eps, "mean", "variance", "gamma", "beta")); - set_values(input, { 0.54881352f, 0.71518934f, @@ -2307,8 +2279,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_inference_b2c2h2w1_differen 0.60276335f, 0.54488319f, - - 0.42365479f, 0.64589411f, @@ -2329,7 +2299,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_inference_b2c2h2w1_differen -0.0963782f, -0.434702f, - -1.4011f, 0.548275f, @@ -2394,7 +2363,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_b2c5h2w1_different_shapes) topology.add(mutable_data("variance", variance)); topology.add(batch_norm("batch_norm", "input", eps, "mean", "variance", "gamma", "beta")); - set_values(input, { 0.54881352f, 0.71518934f, @@ -2411,8 +2379,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_b2c5h2w1_different_shapes) 0.54881352f, 0.71518934f, - - 0.42365479f, 0.64589411f, @@ -2448,9 +2414,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_b2c5h2w1_different_shapes) -0.30327f, 1.1561f, - - - -1.4011f, 0.548275f, @@ -2535,7 +2498,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_inference_b2c5h2w1_differen topology.add(data("variance", variance)); topology.add(batch_norm("batch_norm", "input", eps, "mean", "variance", "gamma", "beta")); - set_values(input, { 0.54881352f, 0.71518934f, @@ -2552,8 +2514,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_inference_b2c5h2w1_differen 0.54881352f, 0.71518934f, - - 0.42365479f, 0.64589411f, @@ -2589,9 +2549,6 @@ TEST(ngraph_batch_normalization_gpu, batchnorm_fprop_inference_b2c5h2w1_differen -0.30327f, 1.1561f, - - - -1.4011f, 0.548275f, diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/batch_norm_grad_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/batch_norm_grad_gpu_test.cpp index f9b820b..d4a625e 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/batch_norm_grad_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/batch_norm_grad_gpu_test.cpp @@ -16,15 +16,15 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/batch_norm_grad.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/batch_norm_grad.hpp" +#include +#include +#include #include "test_utils/test_utils.h" -#include -#include +#include +#include using namespace cldnn; using namespace tests; diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/binary_convolution_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/binary_convolution_gpu_test.cpp index 1162dea..5412f9f 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/binary_convolution_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/binary_convolution_gpu_test.cpp @@ -17,16 +17,16 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include "api/CPP/binary_convolution.hpp" -#include "api/CPP/reorder.hpp" -#include -#include -#include +#include +#include +#include "api/binary_convolution.hpp" +#include "api/reorder.hpp" +#include +#include +#include #include "test_utils/test_utils.h" #include -#include +#include #include #include "float16.h" #include "test_utils.h" @@ -126,7 +126,6 @@ void compute_ref_conv_bin(const cldnn::memory &src, int PH = p.ph; int PW = p.pw; - auto extract_bit = [&](data_t_src val, data_t_src bit) -> data_t_src { return (data_t_src)((val >> bit) & 0x1); }; @@ -257,7 +256,6 @@ TEST_P(binary_convolution_test, conv) std::map outputs = network_bin.execute(); auto outputMemory = outputs.at(output_name).get_memory(); - for (size_t i = 0; i < output_ref.count(); i++) { if (p.dt == data_types::f32) { @@ -414,7 +412,6 @@ TEST(binary_convolution, basic_convolution_1x1_single_packed_channel) auto output_layout = output_memory.get_layout(); auto output_ptr = output_memory.pointer(); - EXPECT_EQ(output_layout.format, format::bfyx); EXPECT_EQ(output_layout.data_type, data_types::f32); EXPECT_EQ(output_layout.size.batch[0], 1); @@ -428,7 +425,6 @@ TEST(binary_convolution, basic_convolution_1x1_single_packed_channel) } } - TEST(binary_convolution, basic_convolution_1x1_single_packed_channel_fp16) { const auto& engine = get_test_engine(); @@ -499,7 +495,6 @@ TEST(binary_convolution, basic_convolution_1x1_single_packed_channel_fp16) { auto output_layout = output_memory.get_layout(); auto output_ptr = output_memory.pointer(); - EXPECT_EQ(output_layout.format, format::bfyx); EXPECT_EQ(output_layout.data_type, data_types::f16); EXPECT_EQ(output_layout.size.batch[0], 1); diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/border_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/border_gpu_test.cpp index f1b3da3..03b6200 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/border_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/border_gpu_test.cpp @@ -15,40 +15,39 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" #include "test_utils/uniform_quantized_real_distribution.hpp" #include - using namespace cldnn; using namespace ::tests; - template static std::vector generate_rnd_real_input( - const std::size_t b, const std::size_t f, const std::size_t y, const std::size_t x, + const std::vector sizes, const T min = static_cast(0), const T max = static_cast(1), const unsigned rnd_bits = 9) { static std::default_random_engine rnd_gen(random_seed); cldnn::tests::distributions::uniform_quantized_real_distribution rnd_dist(min, max, rnd_bits); + auto acum = std::accumulate(sizes.begin(), sizes.end(), 1, std::multiplies()); + std::vector data; - data.reserve(b * f * y * x); - for (size_t i = 0; i < b * f * y * x; ++i) + data.reserve(acum); + for (size_t i = 0; i < static_cast(acum); ++i) data.push_back(rnd_dist(rnd_gen)); return data; } - TEST(border_gpu, basic_yxfb_0x0x1x2_0x0x3x4_border_constant) { // Input (XY) : 4x3 // Output (XY): 10x7 @@ -103,7 +102,7 @@ TEST(border_gpu, basic_yxfb_0x0x1x2_0x0x3x4_border_constant) { }; set_values(input, input_data); - network network(engine, topology); + cldnn::network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); @@ -125,6 +124,221 @@ TEST(border_gpu, basic_yxfb_0x0x1x2_0x0x3x4_border_constant) { } } +TEST(border_gpu, basic_bfzyx_0x0x1x01_0x0x0x0x3_border_constant) { + + constexpr auto in_size_b = 1; + constexpr auto in_size_f = 1; + constexpr auto in_size_y = 2; + constexpr auto in_size_x = 2; + constexpr auto in_size_z = 3; + + constexpr auto blt_size_b = 0; + constexpr auto blt_size_f = 0; + constexpr auto blt_size_y = 1; + constexpr auto blt_size_x = 0; + constexpr auto blt_size_z = 1; + + constexpr auto brb_size_b = 0; + constexpr auto brb_size_f = 0; + constexpr auto brb_size_y = 0; + constexpr auto brb_size_x = 0; + constexpr auto brb_size_z = 3; + + constexpr auto out_size_b = in_size_b + blt_size_b + brb_size_b; + constexpr auto out_size_f = in_size_f + blt_size_f + brb_size_f; + constexpr auto out_size_y = in_size_y + blt_size_y + brb_size_y; + constexpr auto out_size_x = in_size_x + blt_size_x + brb_size_x; + constexpr auto out_size_z = in_size_z + blt_size_z + brb_size_z; + + const auto& engine = get_test_engine(); + auto input = memory::allocate(engine, { data_types::f32, format::bfzyx,{ in_size_b, in_size_f, in_size_x, in_size_y, in_size_z } }); + + topology topology; + topology.add( + input_layout("input", input.get_layout()) + ); + topology.add( + border("output", "input", + { blt_size_b, blt_size_f, blt_size_x, blt_size_y, blt_size_z }, + { brb_size_b, brb_size_f, brb_size_x, brb_size_y, brb_size_z }, + border_type::constant, 0.0f) + ); + + std::vector input_data = { + 1, -2, + 3, -4, + + 5, 6, + 7, 8, + + -10, 12, + 13, -13, + }; + std::vector out_data = { + 0, 0, + 0, 0, + 0, 0, + + 0, 0, + 1, -2, + 3, -4, + + 0, 0, + 5, 6, + 7, 8, + + 0, 0, + -10, 12, + 13, -13, + + 0, 0, + 0, 0, + 0, 0, + + 0, 0, + 0, 0, + 0, 0, + + 0, 0, + 0, 0, + 0, 0, + }; + set_values(input, input_data); + + network network(engine, topology); + network.set_input_data("input", input); + auto outputs = network.execute(); + + auto output = outputs.at("output").get_memory(); + auto output_ptr = output.pointer(); + + ASSERT_EQ(out_data.size(), static_cast(out_size_b * out_size_f * out_size_y * out_size_x * out_size_z)); + + uint32_t idx = 0; + for (auto b = 0; b < out_size_b; ++b) { // B + for (auto f = 0; f < out_size_f; ++f) { // F + for (auto z = 0; z < out_size_z; ++z) { // z + for (auto y = 0; y < out_size_y; ++y) { // Y + for (auto x = 0; x < out_size_x; ++x) { // X + EXPECT_EQ(output_ptr[idx], out_data[idx]); + idx++; + } + } + } + } + } +} + +TEST(border_gpu, basic_bfwzyx_0x0x0x1x0x1_0x0x0x1x0x1_border_constant) { + + constexpr auto in_size_b = 1; + constexpr auto in_size_f = 1; + constexpr auto in_size_y = 2; + constexpr auto in_size_x = 2; + constexpr auto in_size_z = 3; + constexpr auto in_size_w = 1; + + constexpr auto blt_size_b = 0; + constexpr auto blt_size_f = 0; + constexpr auto blt_size_y = 0; + constexpr auto blt_size_x = 1; + constexpr auto blt_size_z = 0; + constexpr auto blt_size_w = 1; + + constexpr auto brb_size_b = 0; + constexpr auto brb_size_f = 0; + constexpr auto brb_size_y = 0; + constexpr auto brb_size_x = 1; + constexpr auto brb_size_z = 0; + constexpr auto brb_size_w = 1; + + constexpr auto out_size_b = in_size_b + blt_size_b + brb_size_b; + constexpr auto out_size_f = in_size_f + blt_size_f + brb_size_f; + constexpr auto out_size_y = in_size_y + blt_size_y + brb_size_y; + constexpr auto out_size_x = in_size_x + blt_size_x + brb_size_x; + constexpr auto out_size_z = in_size_z + blt_size_z + brb_size_z; + constexpr auto out_size_w = in_size_w + blt_size_w + brb_size_w; + + const auto& engine = get_test_engine(); + auto input = memory::allocate(engine, { data_types::f32, format::bfwzyx, tensor{ batch(in_size_b), feature(in_size_f), spatial(in_size_x, in_size_y, in_size_z, in_size_w) } }); + + topology topology; + topology.add( + input_layout("input", input.get_layout()) + ); + topology.add( + border("output", "input", + tensor{ batch(blt_size_b), feature(blt_size_f), spatial(blt_size_x, blt_size_y, blt_size_z, blt_size_w) }, + tensor{ batch(brb_size_b), feature(brb_size_f), spatial(brb_size_x, brb_size_y, brb_size_z, brb_size_w) }, + border_type::constant, 0.0f) + ); + + std::vector input_data = { + 1, -2, + 3, -4, + + 5, 6, + 7, 8, + + -10, 12, + 13, -13, + }; + std::vector out_data = { + 0, 0, 0, 0, + 0, 0, 0, 0, + + 0, 0, 0, 0, + 0, 0, 0, 0, + + 0, 0, 0, 0, + 0, 0, 0, 0, + + 0, 1, -2, 0, + 0, 3, -4, 0, + + 0, 5, 6, 0, + 0, 7, 8, 0, + + 0, -10, 12, 0, + 0, 13, -13, 0, + + 0, 0, 0, 0, + 0, 0, 0, 0, + + 0, 0, 0, 0, + 0, 0, 0, 0, + + 0, 0, 0, 0, + 0, 0, 0, 0, + }; + set_values(input, input_data); + + cldnn::network network(engine, topology); + network.set_input_data("input", input); + auto outputs = network.execute(); + + auto output = outputs.at("output").get_memory(); + auto output_ptr = output.pointer(); + + ASSERT_EQ(out_data.size(), static_cast(out_size_b * out_size_f * out_size_y * out_size_x * out_size_z * out_size_w)); + + uint32_t idx = 0; + for (auto b = 0; b < out_size_b; ++b) { // B + for (auto f = 0; f < out_size_f; ++f) { // F + for (auto w = 0; w < out_size_w; ++w) { // z + for (auto z = 0; z < out_size_z; ++z) { // z + for (auto y = 0; y < out_size_y; ++y) { // Y + for (auto x = 0; x < out_size_x; ++x) { // X + EXPECT_EQ(output_ptr[idx], out_data[idx]); + idx++; + } + } + } + } + } + } +} + TEST(border_gpu, basic_yxfb_0x0x1x2_0x0x3x4_border_constant_non_constant) { // Input (XY) : 4x3 // Output (XY): 10x7 @@ -158,8 +372,8 @@ TEST(border_gpu, basic_yxfb_0x0x1x2_0x0x3x4_border_constant_non_constant) { ); topology.add( border("output", "input", - {blt_size_b, blt_size_f, blt_size_x, blt_size_y}, - {brb_size_b, brb_size_f, brb_size_x, brb_size_y}, + tensor{blt_size_b, blt_size_f, blt_size_x, blt_size_y}, + tensor{brb_size_b, brb_size_f, brb_size_x, brb_size_y}, border_type::constant, 1.0f) ); @@ -277,6 +491,159 @@ TEST(border_gpu, basic_yxfb_0x0x1x2_0x0x3x4_border_mirror) { } } +TEST(border_gpu, basic_bfzyx_0x0x0x0x1_0x0x0x0x1_border_mirror) { + + constexpr auto in_size_b = 1; + constexpr auto in_size_f = 1; + constexpr auto in_size_y = 2; + constexpr auto in_size_x = 4; + constexpr auto in_size_z = 2; + + constexpr auto blt_size_b = 0; + constexpr auto blt_size_f = 0; + constexpr auto blt_size_y = 0; + constexpr auto blt_size_x = 0; + constexpr auto blt_size_z = 1; + + constexpr auto brb_size_b = 0; + constexpr auto brb_size_f = 0; + constexpr auto brb_size_y = 0; + constexpr auto brb_size_x = 0; + constexpr auto brb_size_z = 1; + + constexpr auto out_size_b = in_size_b + blt_size_b + brb_size_b; + constexpr auto out_size_f = in_size_f + blt_size_f + brb_size_f; + constexpr auto out_size_y = in_size_y + blt_size_y + brb_size_y; + constexpr auto out_size_x = in_size_x + blt_size_x + brb_size_x; + constexpr auto out_size_z = in_size_z + blt_size_z + brb_size_z; + + const auto& engine = get_test_engine(); + auto input = memory::allocate(engine, { data_types::f32, format::bfzyx,{ in_size_b, in_size_f, in_size_x, in_size_y, in_size_z } }); + + topology topology; + topology.add( + input_layout("input", input.get_layout()) + ); + topology.add( + border("output", "input", + { blt_size_b, blt_size_f, blt_size_x, blt_size_y, blt_size_z }, + { brb_size_b, brb_size_f, brb_size_x, brb_size_y, brb_size_z }, + border_type::mirror) + ); + + std::vector input_data = generate_rnd_real_input({in_size_b, in_size_f, in_size_y, in_size_x, in_size_z}, -8.0f, 8.0f); + set_values(input, input_data); + + network network(engine, topology); + network.set_input_data("input", input); + auto outputs = network.execute(); + + auto output = outputs.at("output").get_memory(); + auto output_ptr = output.pointer(); + + for (auto b = 0; b < out_size_b; ++b) { // B + for (auto f = 0; f < out_size_f; ++f) { // F + for (auto z = 0; z < out_size_z; ++z) { // F + for (auto y = 0; y < out_size_y; ++y) { // Y + for (auto x = 0; x < out_size_x; ++x) { // X + auto output_off = (((b * out_size_f + f) * out_size_z + z) * out_size_y + y) * out_size_x + x; // BFZYX + + auto in_b = (b >= blt_size_b && b < out_size_b - brb_size_b) ? b - blt_size_b : (b < blt_size_b ? blt_size_b - 1 - b : in_size_b + out_size_b - brb_size_b - 1 - b); + auto in_f = (f >= blt_size_f && f < out_size_f - brb_size_f) ? f - blt_size_f : (f < blt_size_f ? blt_size_f - 1 - f : in_size_f + out_size_f - brb_size_f - 1 - f); + auto in_z = (z >= blt_size_z && z < out_size_z - brb_size_z) ? z - blt_size_z : (z < blt_size_z ? blt_size_z - 1 - z : in_size_z + out_size_z - brb_size_z - 1 - z); + auto in_y = (y >= blt_size_y && y < out_size_y - brb_size_y) ? y - blt_size_y : (y < blt_size_y ? blt_size_y - 1 - y : in_size_y + out_size_y - brb_size_y - 1 - y); + auto in_x = (x >= blt_size_x && x < out_size_x - brb_size_x) ? x - blt_size_x : (x < blt_size_x ? blt_size_x - 1 - x : in_size_x + out_size_x - brb_size_x - 1 - x); + + auto input_off = (((in_b * in_size_f + in_f) * in_size_z + in_z) * in_size_y + in_y) * in_size_x + in_x; // BFZYX + + EXPECT_EQ(output_ptr[output_off], input_data[input_off]); + } + } + } + } + } +} + +TEST(border_gpu, basic_bfzyxw_0x0x0x0x1_0x0x0x0x1_border_mirror) { + + constexpr auto in_size_b = 1; + constexpr auto in_size_f = 1; + constexpr auto in_size_y = 2; + constexpr auto in_size_x = 4; + constexpr auto in_size_z = 2; + constexpr auto in_size_w = 2; + + constexpr auto blt_size_b = 0; + constexpr auto blt_size_f = 0; + constexpr auto blt_size_y = 0; + constexpr auto blt_size_x = 0; + constexpr auto blt_size_z = 1; + constexpr auto blt_size_w = 1; + + constexpr auto brb_size_b = 0; + constexpr auto brb_size_f = 0; + constexpr auto brb_size_y = 0; + constexpr auto brb_size_x = 0; + constexpr auto brb_size_z = 1; + constexpr auto brb_size_w = 1; + + constexpr auto out_size_b = in_size_b + blt_size_b + brb_size_b; + constexpr auto out_size_f = in_size_f + blt_size_f + brb_size_f; + constexpr auto out_size_y = in_size_y + blt_size_y + brb_size_y; + constexpr auto out_size_x = in_size_x + blt_size_x + brb_size_x; + constexpr auto out_size_z = in_size_z + blt_size_z + brb_size_z; + constexpr auto out_size_w = in_size_w + blt_size_w + brb_size_w; + + const auto& engine = get_test_engine(); + auto input = memory::allocate(engine, { data_types::f32, format::bfwzyx, tensor{ batch(in_size_b), feature(in_size_f), spatial(in_size_x, in_size_y, in_size_z, in_size_w) } }); + + topology topology; + topology.add( + input_layout("input", input.get_layout()) + ); + topology.add( + border("output", "input", + tensor{ batch(blt_size_b), feature(blt_size_f), spatial(blt_size_x, blt_size_y, blt_size_z, blt_size_w) }, + tensor{ batch(brb_size_b), feature(brb_size_f), spatial(brb_size_x, brb_size_y, brb_size_z, brb_size_w) }, + border_type::mirror) + ); + + std::vector input_data = generate_rnd_real_input({ in_size_b, in_size_f, in_size_y, in_size_x, in_size_z, in_size_w }, -8.0f, 8.0f); + set_values(input, input_data); + + network network(engine, topology); + network.set_input_data("input", input); + auto outputs = network.execute(); + + auto output = outputs.at("output").get_memory(); + auto output_ptr = output.pointer(); + + for (auto b = 0; b < out_size_b; ++b) { // B + for (auto f = 0; f < out_size_f; ++f) { // F + for (auto w = 0; w < out_size_w; ++w) { // F + for (auto z = 0; z < out_size_z; ++z) { // F + for (auto y = 0; y < out_size_y; ++y) { // Y + for (auto x = 0; x < out_size_x; ++x) { // X + auto output_off = ((((b * out_size_f + f) * out_size_w + w)* out_size_z + z) * out_size_y + y) * out_size_x + x; // BFZYX + + auto in_b = (b >= blt_size_b && b < out_size_b - brb_size_b) ? b - blt_size_b : (b < blt_size_b ? blt_size_b - 1 - b : in_size_b + out_size_b - brb_size_b - 1 - b); + auto in_f = (f >= blt_size_f && f < out_size_f - brb_size_f) ? f - blt_size_f : (f < blt_size_f ? blt_size_f - 1 - f : in_size_f + out_size_f - brb_size_f - 1 - f); + auto in_w = (w >= blt_size_w && w < out_size_w - brb_size_w) ? w - blt_size_w : (w < blt_size_w ? blt_size_w - 1 - w : in_size_w + out_size_w - brb_size_w - 1 - w); + auto in_z = (z >= blt_size_z && z < out_size_z - brb_size_z) ? z - blt_size_z : (z < blt_size_z ? blt_size_z - 1 - z : in_size_z + out_size_z - brb_size_z - 1 - z); + auto in_y = (y >= blt_size_y && y < out_size_y - brb_size_y) ? y - blt_size_y : (y < blt_size_y ? blt_size_y - 1 - y : in_size_y + out_size_y - brb_size_y - 1 - y); + auto in_x = (x >= blt_size_x && x < out_size_x - brb_size_x) ? x - blt_size_x : (x < blt_size_x ? blt_size_x - 1 - x : in_size_x + out_size_x - brb_size_x - 1 - x); + + auto input_off = ((((in_b * in_size_f + in_f) * in_size_w + in_w)* in_size_z + in_z) * in_size_y + in_y) * in_size_x + in_x; // BFZYX + + EXPECT_EQ(output_ptr[output_off], input_data[input_off]); + } + } + } + } + } + } +} + TEST(border_gpu, basic_yxfb_0x0x1x2_0x0x3x4_border_mirror_101) { // Input (XY) : 5x4 // Output (XY): 11x8 @@ -302,7 +669,7 @@ TEST(border_gpu, basic_yxfb_0x0x1x2_0x0x3x4_border_mirror_101) { constexpr auto out_size_x = in_size_x + blt_size_x + brb_size_x; const auto& engine = get_test_engine(); - auto input = memory::allocate(engine, {data_types::f32, format::yxfb, {in_size_b, in_size_f, in_size_x, in_size_y}}); + auto input = memory::allocate(engine, {data_types::f32, format::yxfb, tensor{in_size_b, in_size_f, in_size_x, in_size_y}}); topology topology; topology.add( @@ -310,8 +677,8 @@ TEST(border_gpu, basic_yxfb_0x0x1x2_0x0x3x4_border_mirror_101) { ); topology.add( border("output", "input", - {blt_size_b, blt_size_f, blt_size_x, blt_size_y}, - {brb_size_b, brb_size_f, brb_size_x, brb_size_y}, + tensor{blt_size_b, blt_size_f, blt_size_x, blt_size_y}, + tensor{brb_size_b, brb_size_f, brb_size_x, brb_size_y}, border_type::mirror_101) ); @@ -355,6 +722,185 @@ TEST(border_gpu, basic_yxfb_0x0x1x2_0x0x3x4_border_mirror_101) { } } +TEST(border_gpu, basic_bfzyx_0x0x0x0x1_0x0x0x0x1_border_mirror_101) { + constexpr auto in_size_b = 1; + constexpr auto in_size_f = 1; + constexpr auto in_size_y = 2; + constexpr auto in_size_x = 5; + constexpr auto in_size_z = 2; + + constexpr auto blt_size_b = 0; + constexpr auto blt_size_f = 0; + constexpr auto blt_size_y = 0; + constexpr auto blt_size_x = 0; + constexpr auto blt_size_z = 1; + + constexpr auto brb_size_b = 0; + constexpr auto brb_size_f = 0; + constexpr auto brb_size_y = 0; + constexpr auto brb_size_x = 0; + constexpr auto brb_size_z = 1; + + constexpr auto out_size_b = in_size_b + blt_size_b + brb_size_b; + constexpr auto out_size_f = in_size_f + blt_size_f + brb_size_f; + constexpr auto out_size_y = in_size_y + blt_size_y + brb_size_y; + constexpr auto out_size_x = in_size_x + blt_size_x + brb_size_x; + constexpr auto out_size_z = in_size_z + blt_size_z + brb_size_z; + + const auto& engine = get_test_engine(); + auto input = memory::allocate(engine, { data_types::f32, format::bfzyx, tensor{ in_size_b, in_size_f, in_size_x, in_size_y, in_size_z } }); + + topology topology; + topology.add( + input_layout("input", input.get_layout()) + ); + topology.add( + border("output", "input", + tensor{ blt_size_b, blt_size_f, blt_size_x, blt_size_y, blt_size_z }, + tensor{ brb_size_b, brb_size_f, brb_size_x, brb_size_y, brb_size_z }, + border_type::mirror_101) + ); + + std::vector input_data = { + 1, -2, 3, -4, 4, + 5, 6, 7, 8, -8, + + -10, 12, 13, -13, 10, + -20, 22, 23, -23, 20, + }; + std::vector out_data = { + -10, 12, 13, -13, 10, + -20, 22, 23, -23, 20, + 1, -2, 3, -4, 4, + 5, 6, 7, 8, -8, + -10, 12, 13, -13, 10, + -20, 22, 23, -23, 20, + 1, -2, 3, -4, 4, + 5, 6, 7, 8, -8, + }; + set_values(input, input_data); + + network network(engine, topology); + network.set_input_data("input", input); + auto outputs = network.execute(); + + auto output = outputs.at("output").get_memory(); + auto output_ptr = output.pointer(); + + ASSERT_EQ(out_data.size(), static_cast(out_size_b * out_size_f * out_size_y * out_size_x * out_size_z)); + + uint32_t idx = 0; + for (auto b = 0; b < out_size_b; ++b) { // B + for (auto f = 0; f < out_size_f; ++f) { // F + for (auto z = 0; z < out_size_z; ++z) { // Z + for (auto y = 0; y < out_size_y; ++y) { // Y + for (auto x = 0; x < out_size_x; ++x) { // X + EXPECT_EQ(output_ptr[idx], out_data[idx]); + idx++; + } + } + } + } + } +} + +TEST(border_gpu, basic_bfwzyx_0x0x0x0x1x1_0x0x0x0x1x1_border_mirror_101) { + constexpr auto in_size_b = 1; + constexpr auto in_size_f = 1; + constexpr auto in_size_y = 4; + constexpr auto in_size_x = 2; + constexpr auto in_size_z = 1; + constexpr auto in_size_w = 3; + + constexpr auto blt_size_b = 0; + constexpr auto blt_size_f = 0; + constexpr auto blt_size_y = 0; + constexpr auto blt_size_x = 0; + constexpr auto blt_size_z = 0; + constexpr auto blt_size_w = 1; + + constexpr auto brb_size_b = 0; + constexpr auto brb_size_f = 0; + constexpr auto brb_size_y = 0; + constexpr auto brb_size_x = 0; + constexpr auto brb_size_z = 0; + constexpr auto brb_size_w = 1; + + constexpr auto out_size_b = in_size_b + blt_size_b + brb_size_b; + constexpr auto out_size_f = in_size_f + blt_size_f + brb_size_f; + constexpr auto out_size_y = in_size_y + blt_size_y + brb_size_y; + constexpr auto out_size_x = in_size_x + blt_size_x + brb_size_x; + constexpr auto out_size_z = in_size_z + blt_size_z + brb_size_z; + constexpr auto out_size_w = in_size_w + blt_size_w + brb_size_w; + + const auto& engine = get_test_engine(); + auto input = memory::allocate(engine, { data_types::f32, format::bfwzyx, tensor{ batch(in_size_b), feature(in_size_f), spatial(in_size_x, in_size_y, in_size_z, in_size_w) } }); + + topology topology; + topology.add( + input_layout("input", input.get_layout()) + ); + topology.add( + border("output", "input", + tensor{ batch(blt_size_b), feature(blt_size_f), spatial(blt_size_x, blt_size_y, blt_size_z, blt_size_w) }, + tensor{ batch(brb_size_b), feature(brb_size_f), spatial(brb_size_x, brb_size_y, brb_size_z, brb_size_w) }, + border_type::mirror_101) + ); + + std::vector input_data = { + 1, -2, 3, -4, + 5, 6, 7, 8, + + 2, -3, 4, -5, + 15, 4, 4, 4, + + 2, -6, 13, -14, + 3, 7, 7, 7, + }; + std::vector out_data = { + 2, -3, 4, -5, + 15, 4, 4, 4, + + 1, -2, 3, -4, + 5, 6, 7, 8, + + 2, -3, 4, -5, + 15, 4, 4, 4, + + 2, -6, 13, -14, + 3, 7, 7, 7, + + 2, -3, 4, -5, + 15, 4, 4, 4, + }; + set_values(input, input_data); + + network network(engine, topology); + network.set_input_data("input", input); + auto outputs = network.execute(); + + auto output = outputs.at("output").get_memory(); + auto output_ptr = output.pointer(); + + ASSERT_EQ(out_data.size(), static_cast(out_size_b * out_size_f * out_size_y * out_size_x * out_size_z * out_size_w)); + + uint32_t idx = 0; + for (auto b = 0; b < out_size_b; ++b) { // B + for (auto f = 0; f < out_size_f; ++f) { // F + for (auto w = 0; w < out_size_w; ++w) { // F + for (auto z = 0; z < out_size_z; ++z) { // Z + for (auto y = 0; y < out_size_y; ++y) { // Y + for (auto x = 0; x < out_size_x; ++x) { // X + EXPECT_EQ(output_ptr[idx], out_data[idx]); + idx++; + } + } + } + } + } + } +} + TEST(border_gpu, basic_yxfb_0x0x1x2_0x0x3x4_border_edge) { // Input (XY) : 5x4 // Output (XY): 11x8 @@ -380,7 +926,7 @@ TEST(border_gpu, basic_yxfb_0x0x1x2_0x0x3x4_border_edge) { constexpr auto out_size_x = in_size_x + blt_size_x + brb_size_x; const auto& engine = get_test_engine(); - auto input = memory::allocate(engine, {data_types::f32, format::yxfb, {in_size_b, in_size_f, in_size_x, in_size_y}}); + auto input = memory::allocate(engine, {data_types::f32, format::yxfb, tensor{in_size_b, in_size_f, in_size_x, in_size_y}}); topology topology; topology.add( @@ -388,8 +934,8 @@ TEST(border_gpu, basic_yxfb_0x0x1x2_0x0x3x4_border_edge) { ); topology.add( border("output", "input", - {blt_size_b, blt_size_f, blt_size_x, blt_size_y}, - {brb_size_b, brb_size_f, brb_size_x, brb_size_y}, + tensor{blt_size_b, blt_size_f, blt_size_x, blt_size_y}, + tensor{brb_size_b, brb_size_f, brb_size_x, brb_size_y}, border_type::edge) ); @@ -455,7 +1001,7 @@ TEST(border_gpu, basic_bfyx_2x1x2x3_1x2x3x4_border_constant) { constexpr auto out_size_x = in_size_x + blt_size_x + brb_size_x; const auto& engine = get_test_engine(); - auto input = memory::allocate(engine, {data_types::f32, format::bfyx, {in_size_b, in_size_f, in_size_x, in_size_y}}); + auto input = memory::allocate(engine, {data_types::f32, format::bfyx, tensor{in_size_b, in_size_f, in_size_x, in_size_y}}); topology topology; topology.add( @@ -463,13 +1009,13 @@ TEST(border_gpu, basic_bfyx_2x1x2x3_1x2x3x4_border_constant) { ); topology.add( border("output", "input", - {blt_size_b, blt_size_f, blt_size_x, blt_size_y}, - {brb_size_b, brb_size_f, brb_size_x, brb_size_y}, + tensor{blt_size_b, blt_size_f, blt_size_x, blt_size_y}, + tensor{brb_size_b, brb_size_f, brb_size_x, brb_size_y}, border_type::constant, 0.0f) ); - std::vector input_data = generate_rnd_real_input(in_size_b, in_size_f, in_size_y, in_size_x, -8.0f, 8.0f); + std::vector input_data = generate_rnd_real_input({ in_size_b, in_size_f, in_size_y, in_size_x }, -8.0f, 8.0f); set_values(input, input_data); network network(engine, topology); @@ -525,7 +1071,7 @@ TEST(border_gpu, basic_bfyx_2x1x2x3_1x2x3x4_border_mirror) { constexpr auto out_size_x = in_size_x + blt_size_x + brb_size_x; const auto& engine = get_test_engine(); - auto input = memory::allocate(engine, {data_types::f32, format::bfyx, {in_size_b, in_size_f, in_size_x, in_size_y}}); + auto input = memory::allocate(engine, {data_types::f32, format::bfyx, tensor{in_size_b, in_size_f, in_size_x, in_size_y}}); topology topology; topology.add( @@ -533,12 +1079,12 @@ TEST(border_gpu, basic_bfyx_2x1x2x3_1x2x3x4_border_mirror) { ); topology.add( border("output", "input", - {blt_size_b, blt_size_f, blt_size_x, blt_size_y}, - {brb_size_b, brb_size_f, brb_size_x, brb_size_y}, + tensor{blt_size_b, blt_size_f, blt_size_x, blt_size_y}, + tensor{brb_size_b, brb_size_f, brb_size_x, brb_size_y}, border_type::mirror) ); - std::vector input_data = generate_rnd_real_input(in_size_b, in_size_f, in_size_y, in_size_x, -8.0f, 8.0f); + std::vector input_data = generate_rnd_real_input({ in_size_b, in_size_f, in_size_y, in_size_x }, -8.0f, 8.0f); set_values(input, input_data); network network(engine, topology); @@ -561,7 +1107,6 @@ TEST(border_gpu, basic_bfyx_2x1x2x3_1x2x3x4_border_mirror) { auto input_off = ((in_b * in_size_f + in_f) * in_size_y + in_y) * in_size_x + in_x; // BFYX - EXPECT_EQ(output_ptr[output_off], input_data[input_off]); } } @@ -591,7 +1136,7 @@ TEST(border_gpu, basic_bfyx_2x1x2x3_1x2x3x4_border_mirror_101) { constexpr auto out_size_x = in_size_x + blt_size_x + brb_size_x; const auto& engine = get_test_engine(); - auto input = memory::allocate(engine, {data_types::f32, format::bfyx, {in_size_b, in_size_f, in_size_x, in_size_y}}); + auto input = memory::allocate(engine, {data_types::f32, format::bfyx, tensor{in_size_b, in_size_f, in_size_x, in_size_y}}); topology topology; topology.add( @@ -599,12 +1144,12 @@ TEST(border_gpu, basic_bfyx_2x1x2x3_1x2x3x4_border_mirror_101) { ); topology.add( border("output", "input", - {blt_size_b, blt_size_f, blt_size_x, blt_size_y}, - {brb_size_b, brb_size_f, brb_size_x, brb_size_y}, + tensor{blt_size_b, blt_size_f, blt_size_x, blt_size_y}, + tensor{brb_size_b, brb_size_f, brb_size_x, brb_size_y}, border_type::mirror_101) ); - std::vector input_data = generate_rnd_real_input(in_size_b, in_size_f, in_size_y, in_size_x, -8.0f, 8.0f); + std::vector input_data = generate_rnd_real_input({ in_size_b, in_size_f, in_size_y, in_size_x }, -8.0f, 8.0f); set_values(input, input_data); network network(engine, topology); @@ -627,7 +1172,6 @@ TEST(border_gpu, basic_bfyx_2x1x2x3_1x2x3x4_border_mirror_101) { auto input_off = ((in_b * in_size_f + in_f) * in_size_y + in_y) * in_size_x + in_x; // BFYX - EXPECT_EQ(output_ptr[output_off], input_data[input_off]); } } @@ -657,7 +1201,7 @@ TEST(border_gpu, basic_bfyx_2x1x2x3_1x2x3x4_border_edge) { constexpr auto out_size_x = in_size_x + blt_size_x + brb_size_x; const auto& engine = get_test_engine(); - auto input = memory::allocate(engine, {data_types::f32, format::bfyx, {in_size_b, in_size_f, in_size_x, in_size_y}}); + auto input = memory::allocate(engine, {data_types::f32, format::bfyx, tensor{in_size_b, in_size_f, in_size_x, in_size_y}}); topology topology; topology.add( @@ -665,12 +1209,12 @@ TEST(border_gpu, basic_bfyx_2x1x2x3_1x2x3x4_border_edge) { ); topology.add( border("output", "input", - {blt_size_b, blt_size_f, blt_size_x, blt_size_y}, - {brb_size_b, brb_size_f, brb_size_x, brb_size_y}, + tensor{blt_size_b, blt_size_f, blt_size_x, blt_size_y}, + tensor{brb_size_b, brb_size_f, brb_size_x, brb_size_y}, border_type::edge) ); - std::vector input_data = generate_rnd_real_input(in_size_b, in_size_f, in_size_y, in_size_x, -8.0f, 8.0f); + std::vector input_data = generate_rnd_real_input({ in_size_b, in_size_f, in_size_y, in_size_x }, -8.0f, 8.0f); set_values(input, input_data); network network(engine, topology); @@ -693,7 +1237,6 @@ TEST(border_gpu, basic_bfyx_2x1x2x3_1x2x3x4_border_edge) { auto input_off = ((in_b * in_size_f + in_f) * in_size_y + in_y) * in_size_x + in_x; // BFYX - EXPECT_EQ(output_ptr[output_off], input_data[input_off]); } } diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/broadcast_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/broadcast_gpu_test.cpp index 6fbe040..216366b 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/broadcast_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/broadcast_gpu_test.cpp @@ -15,19 +15,18 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" #include "test_utils/uniform_quantized_real_distribution.hpp" #include - using namespace cldnn; using namespace ::tests; @@ -1015,7 +1014,6 @@ TEST(broadcast_gpu_uint8_t, bfyx_2_to_2x3x4x5_w_b_axes_1_2_3) { start_broadcast_test(data_types::u8, {2, 3, 4, 5}, {2}, {1, 2, 3}, golden_data); } - TEST(broadcast_gpu, basic_error_wrong_b_axes_size) { const auto& engine = get_test_engine(); diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/command_queue_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/command_queue_test.cpp index 175cc0f..778f201 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/command_queue_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/command_queue_test.cpp @@ -15,12 +15,12 @@ */ /////////////////////////////////////////////////////////////////////////////////////////////////// -#include -#include -#include -#include +#include +#include +#include +#include #include "test_utils/test_utils.h" -#include "api/CPP/arg_max_min.hpp" +#include "api/arg_max_min.hpp" using namespace cldnn; using namespace tests; @@ -57,7 +57,6 @@ unsigned long GetMilliseconds(void) } #endif - // Run some topology too see if command queue does work correctly // Coppied from arg_max_gpu.base test. void exexute_network(cldnn::engine engine) diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/condition_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/condition_gpu_test.cpp index 09e299e..f855ba9 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/condition_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/condition_gpu_test.cpp @@ -15,26 +15,24 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" #include - using namespace cldnn; using namespace ::tests; - bool is_output_equal(const cldnn::memory& mem, const std::vector& ref) { auto ptr = mem.pointer(); @@ -63,8 +61,7 @@ topology generate_simple_branch (bool branch_true_false, const primitive_id& inp return branch; } - -TEST(condition_gpu, basic_equal_comp) { +TEST(DISABLED_condition_gpu, basic_equal_comp) { const auto& engine = get_test_engine(); build_options bs; bs.set_option(build_option::optimize_data(true)); @@ -116,7 +113,7 @@ TEST(condition_gpu, basic_equal_comp) { } -TEST(condition_gpu, basic_range_equal_comp) { +TEST(DISABLED_condition_gpu, basic_range_equal_comp) { const auto& engine = get_test_engine(); build_options bs; @@ -335,7 +332,7 @@ TEST(DISABLED_condition_gpu, generic_test_true_false) { } } -TEST(condition_gpu, basic_stacked_ifs) { +TEST(DISABLED_condition_gpu, basic_stacked_ifs) { /* @@ -355,16 +352,15 @@ TEST(condition_gpu, basic_stacked_ifs) { auto compare = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 1, 1 } }); auto compare2 = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); - topology condi_1_true = generate_simple_branch(true, "condi"); topology condi_1_false = generate_simple_branch(false, "condi"); topology condi_2_true; condi_2_true.add( - activation("activ_when_true", "condi2", cldnn_activation_func::activation_log2) + activation("activ_when_true", "condi2", activation_func::log2) ); topology condi_2_false; condi_2_false.add( - activation("activ_when_false", "condi2", cldnn_activation_func::activation_relu) + activation("activ_when_false", "condi2", activation_func::relu) ); topology topology; @@ -407,7 +403,7 @@ TEST(condition_gpu, basic_stacked_ifs) { EXPECT_TRUE(is_output_equal(out_data, {1.0f, 2.0f})); } -TEST(condition_gpu, basic_nested_ifs) { +TEST(DISABLED_condition_gpu, basic_nested_ifs) { /* @@ -431,7 +427,6 @@ TEST(condition_gpu, basic_nested_ifs) { auto scale_10_mem = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 1, 1 } }); set_values(scale_10_mem, { 10.0f }); - topology nested_true; { nested_true.add(scale("scale_5", "condi_nested", "scale_5_data"), @@ -502,8 +497,7 @@ TEST(condition_gpu, basic_nested_ifs) { EXPECT_TRUE(is_output_equal(out_data, { 10.0f, 20.0f })); } - -TEST(condition_gpu, negative_compare_wrong_layout) { +TEST(DISABLED_condition_gpu, negative_compare_wrong_layout) { const auto& engine = get_test_engine(); build_options bs; bs.set_option(build_option::optimize_data(true)); @@ -527,7 +521,7 @@ TEST(condition_gpu, negative_compare_wrong_layout) { EXPECT_ANY_THROW(network net(engine, topology, bs);); } -TEST(condition_gpu, negative_too_big_offset) { +TEST(DISABLED_condition_gpu, negative_too_big_offset) { const auto& engine = get_test_engine(); build_options bs; bs.set_option(build_option::optimize_data(true)); @@ -551,7 +545,7 @@ TEST(condition_gpu, negative_too_big_offset) { EXPECT_ANY_THROW(network net(engine, topology, bs);); } -TEST(condition_gpu, negative_not_same_layouts) { +TEST(DISABLED_condition_gpu, negative_not_same_layouts) { const auto& engine = get_test_engine(); build_options bs; bs.set_option(build_option::optimize_data(true)); @@ -582,7 +576,7 @@ TEST(condition_gpu, negative_not_same_layouts) { EXPECT_ANY_THROW(network net(engine, topology, bs);); } -TEST(condition_gpu, negative_same_names_within_different_networks) { +TEST(DISABLED_condition_gpu, negative_same_names_within_different_networks) { const auto& engine = get_test_engine(); build_options bs; bs.set_option(build_option::optimize_data(true)); @@ -614,4 +608,4 @@ TEST(condition_gpu, negative_same_names_within_different_networks) { ); EXPECT_ANY_THROW(network net(engine, topology, bs);); -} \ No newline at end of file +} diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/contract_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/contract_gpu_test.cpp index 1a2c671..05b4d57 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/contract_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/contract_gpu_test.cpp @@ -15,12 +15,12 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" #include "test_utils/uniform_quantized_real_distribution.hpp" @@ -189,7 +189,6 @@ void generic_contract_test_float(cldnn::format test_input_fmt, int input_b, int EXPECT_EQ(f_size, (int)output_cpu[0].size()); EXPECT_EQ(b_size, (int)output_cpu.size()); - bool test_is_correct = true; VF output_cpu_vec = flatten_4d(test_input_fmt, output_cpu); for (size_t i = 0; i < output_cpu_vec.size(); ++i) { @@ -251,7 +250,6 @@ void generic_contract_test_int(cldnn::format test_input_fmt, int input_b, int in EXPECT_EQ(f_size, (int)output_cpu[0].size()); EXPECT_EQ(b_size, (int)output_cpu.size()); - bool test_is_correct = true; VF output_cpu_vec = flatten_4d(test_input_fmt, output_cpu); diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/convolution_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/convolution_gpu_test.cpp index 7c423c6..5c83855 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/convolution_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/convolution_gpu_test.cpp @@ -18,15 +18,15 @@ #include #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/convolution.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/convolution.hpp" +#include +#include +#include #include "test_utils/test_utils.h" #include "test_utils/float16.h" -#include +#include #include #include #include @@ -34,19 +34,16 @@ #include #include #include -#include +#include using namespace cldnn; using namespace tests; - namespace cldnn { template<> struct type_to_data_type { static const data_types value = data_types::f16; }; } - - template T kahan_summation(std::vector &input) { T sum = 0; @@ -63,14 +60,16 @@ T kahan_summation(std::vector &input) { template VVF reference_convolve(VVVF &input, VVVF &filter, int stride_y, int stride_x, float bias, int dilation_y = 1, int dilation_x = 1, int input_padding_y = 0, int input_padding_x = 0, int output_padding_y = 0, - int output_padding_x = 0, size_t f_begin = 0) + int output_padding_x = 0, size_t f_begin = 0, size_t f_end = 0, bool depthwise = false) { size_t kernel_extent_y = dilation_y * (filter[0].size() - 1) + 1; size_t kernel_extent_x = dilation_x * (filter[0][0].size() - 1) + 1; size_t output_y = 1 + (input[0].size() - kernel_extent_y + 2 * input_padding_y) / stride_y + 2 * output_padding_y; size_t output_x = 1 + (input[0][0].size() - kernel_extent_x + 2 * input_padding_x) / stride_x + 2 * output_padding_x; VVF output(output_y, VF(output_x, 0)); - for (size_t f = 0; f < filter.size(); ++f) { + size_t filter_begin = f_begin ? f_begin : 0; + size_t filter_end = f_end ? f_end : filter.size(); + for (size_t f = filter_begin; f < filter_end; ++f) { for (size_t y = 0; y < (output_y - 2 * output_padding_y); ++y) { for (size_t x = 0; x < (output_x - 2 * output_padding_x); ++x) { VF values; @@ -81,7 +80,10 @@ VVF reference_convolve(VVVF &input, VVVF &filter, int stride_y, int str for (size_t xf = 0; xf < filter[0][0].size(); ++xf) { int xi = -input_padding_x + (int)xf * dilation_x + stride_x * (int)x; if (xi < 0 || (int)input[0][0].size() <= xi) continue; - values.push_back(input[f_begin + f][yi][xi] * filter[f][yf][xf]); + if (!depthwise) + values.push_back(input[f][yi][xi] * filter[f][yf][xf]); + else + values.push_back(input[f][yi][xi] * filter[0][yf][xf]); } } output[y + output_padding_y][x + output_padding_x] += kahan_summation(values); @@ -147,7 +149,7 @@ TEST(deformable_convolution_f32_fw_gpu, basic_deformable_convolution_def_group1_ auto input = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 4, 4, 4 } }); auto trans = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 18, 4, 4 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx, { 4, 4, 3, 3 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 4, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 4, 1, 1 } }); set_values(input, { 0.680375f, -0.211234f, 0.566198f, 0.59688f, 0.823295f, -0.604897f, -0.329554f, 0.536459f, -0.444451f, 0.10794f, -0.0452059f, 0.257742f, -0.270431f, 0.0268018f, 0.904459f, 0.83239f, @@ -279,7 +281,7 @@ TEST(deformable_convolution_f32_fw_gpu, basic_deformable_convolution_def_group1) auto input = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 4, 4, 4 } }); auto trans = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 18, 4, 4 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx, { 4, 4, 3, 3 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 4, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 4, 1, 1 } }); set_values(input, { 0.680375f, -0.211234f, 0.566198f, 0.59688f, 0.823295f, -0.604897f, -0.329554f, 0.536459f, -0.444451f, 0.10794f, -0.0452059f, 0.257742f, -0.270431f, 0.0268018f, 0.904459f, 0.83239f, @@ -411,7 +413,7 @@ TEST(deformable_convolution_f32_fw_gpu, basic_deformable_convolution) { auto input = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 4, 4, 4 } }); auto trans = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 36, 4, 4 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx, { 4, 4, 3, 3 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 4, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 4, 1, 1 } }); set_values(input, { 0.680375f, -0.211234f, 0.566198f, 0.59688f, 0.823295f, -0.604897f, -0.329554f, 0.536459f, -0.444451f, 0.10794f, -0.0452059f, 0.257742f, -0.270431f, 0.0268018f, 0.904459f, 0.83239f, @@ -1020,7 +1022,6 @@ TEST(convolution_f32_fw_gpu, basic_convolution3D_split2) { for (int z = 0; z < z_size; ++z) { for (int y = 0; y < y_size; ++y) { for (int x = 0; x < x_size; ++x) { - int i = f * z_size * y_size * x_size + z * y_size * x_size + y * x_size + x; EXPECT_EQ(output_vec[f][z][y][x], output_ptr[f * z_size * y_size * x_size + z * y_size * x_size + y * x_size + x]); } @@ -1034,7 +1035,7 @@ TEST(convolution_f32_fw_gpu, basic_convolution3D_group2) { const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::bfzyx,{ 1, 2, 4, 4, 4 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfzyx,{ 2, 1, 2, 2, 2 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1, 1 } }); set_values(input, { 1.0f, 0.0f, 1.0f, 0.0f, @@ -1153,7 +1154,6 @@ TEST(convolution_f32_fw_gpu, basic_convolution3D_group2) { for (int z = 0; z < z_size; ++z) { for (int y = 0; y < y_size; ++y) { for (int x = 0; x < x_size; ++x) { - int i = f * z_size * y_size * x_size + z * y_size * x_size + y * x_size + x; EXPECT_EQ(output_vec[f][z][y][x], output_ptr[f * z_size * y_size * x_size + z * y_size * x_size + y * x_size + x]); } @@ -1203,7 +1203,6 @@ TEST(convolution_f32_fw_gpu, three_convolutions_same_weights) { // 8 8 8 8 // 8 8 8 8 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::bfyx, {1,2,2,2} }); @@ -1212,7 +1211,6 @@ TEST(convolution_f32_fw_gpu, three_convolutions_same_weights) { set_values(input, { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }); set_values(weights, { 1.0f, 1.0f, 1.0f, 1.0f }); - topology topology( input_layout("input", input.get_layout()), data("weights", weights), @@ -1250,7 +1248,6 @@ TEST(convolution_f32_fw_gpu, three_convolutions_same_weights) { } } - TEST(convolution_f32_fw_gpu, basic_convolution) { // Filter : 2x3 // Stride : 2x1 @@ -1448,8 +1445,6 @@ TEST(convolution_f32_fw_gpu, basic_convolution_input_padding) { { 1,1,1,1 }, { 0,0,-1,-2 }, { 1, 1, 1, 1 }, - false, - 0, padding{ { 0,0,0,0 }, 0 }) ); @@ -1552,8 +1547,6 @@ TEST(convolution_f32_fw_gpu, basic_convolution_sym_input_padding) { { 1, 1, 1, 1 }, { 0,0,1,2 }, { 0,0,1,2 }, - false, - 0, padding{ { 0,0,0,0 }, 0 }) ); @@ -1651,8 +1644,6 @@ TEST(convolution_f32_fw_gpu, basic_convolution_asym_input_padding) { { 1, 1, 1, 1 }, { 0,0,1,2 }, { 0,0,2,3 }, - false, - 0, padding{ { 0,0,0,0 }, 0 }) ); @@ -1760,8 +1751,6 @@ TEST(convolution_f32_fw_gpu, basic_convolution_sym_input_padding_with_input_offs { 1, 1, 1, 1 }, { 0,0,1,2 }, { 0,0,1,2 }, - false, - 0, padding{ { 0,0,0,0 }, 0 }) ); @@ -1872,8 +1861,6 @@ TEST(convolution_f32_fw_gpu, basic_convolution_asym_input_padding_with_input_off { 1, 1, 1, 1 }, { 0,0,1,2 }, { 0,0,2,3 }, - false, - 0, padding{ { 0,0,0,0 }, 0 }) ); @@ -1972,8 +1959,6 @@ TEST(convolution_f32_fw_gpu, basic_convolution_input_and_output_padding) { { 1,1,1,1 }, { 0,0,-1,-2 }, { 1, 1, 1, 1 }, - false, - 0, padding{ { 0,0,-x_pad,-y_pad }, 0 }) ); @@ -2293,7 +2278,7 @@ TEST(convolution_f32_fw_gpu, basic_ofm_wsiz2x1x2x1_in1x2x1_nopad) { auto input = memory::allocate(engine, { data_types::f32, format::yxfb, { 1, 1, 1, 2 } }); //auto output = memory::allocate({ memory::format::yxfb_f32,{ 1 ,{ 1, 1 }, 2 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx, { 2, 1, 1, 2 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 2, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 2, 1, 1 } }); set_values(input, { 1.0f, 2.0f }); set_values(weights, { 1.0f, 2.0f, -1.0f, -2.0f }); @@ -2352,7 +2337,7 @@ TEST(convolution_f32_fw_gpu, basic_ofm_wsiz3x2x2x1_in2x2x1_nopad) { auto input = memory::allocate(engine, { data_types::f32, format::yxfb, { 1, 2, 1, 2 } }); //auto output = memory::allocate({ memory::format::yxfb_f32,{ 1 ,{ 1, 1 }, 3 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx, { 3, 2, 1, 2 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 3, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 3, 1, 1 } }); set_values(input, { 1.0f, 3.0f, 2.0f, 4.0f }); set_values(weights, { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f }); @@ -2408,7 +2393,7 @@ TEST(convolution_f32_fw_gpu, basic_wsiz2x2x1x3_wstr2x2_in2x2x1x1_nopad) { auto input = memory::allocate(engine, { data_types::f32, format::yxfb, { 1, 1, 2, 2 } }); //auto output = memory::allocate({ memory::format::yxfb_f32,{ 1 ,{ 1, 1 }, 3 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx, { 3, 1, 2, 2 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 3, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 3, 1, 1 } }); set_values(input, { -2.3f, -0.1f, 3.1f, 1.9f }); set_values(weights, { -1.1f, 1.5f, 0.5f, -0.5f, 0.1f, 0.2f, 0.4f, 0.7f, 2.0f, -1.0f, 2.5f, -1.5f }); @@ -2539,8 +2524,6 @@ TEST(convolution_f32_fw_gpu, offsets_wsiz3x3_wstr2x2_in2x2x1x1_zeropad) { { 1,1,2,2 }, { 0,0,-1,-1 }, { 1, 1, 1, 1 }, - false, - 0, padding{ { 0,0,1,1 }, 0 }) ); @@ -2767,7 +2750,7 @@ TEST(convolution_f32_fw_gpu, basic_wsiz2x2_wstr2x2_in4x4x2x1_nopad_group2) { auto input = memory::allocate(engine, { data_types::f32, format::yxfb,{ 1, 2, 4, 4 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 2, 2 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); set_values(input, { -0.5f, 0.5f, 1.0f, 1.5f, 0.5f, 2.3f, 2.0f, -0.4f, @@ -2824,7 +2807,7 @@ TEST(convolution_f32_fw_gpu, basic_wsiz2x2_wstr2x2_in4x4x2x1_nopad_group2_bfyx) auto input = memory::allocate(engine, { data_types::f32, format::yxfb,{ 1, 2, 4, 4 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 2, 2 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); set_values(input, { -0.5f, 0.5f, 1.0f, 1.5f, 0.5f, 2.3f, 2.0f, -0.4f, @@ -2882,7 +2865,7 @@ TEST(convolution_f32_fw_gpu, basic_wsiz2x2_wstr2x2_in4x4x2x2_nopad_group2) { auto input = memory::allocate(engine, { data_types::f32, format::yxfb,{ 2, 2, 4, 4 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 2, 2 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); set_values(input, { -0.5f, -0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.5f, 1.5f, 0.5f, 0.5f, 2.3f, 2.3f, 2.0f, 2.0f, -0.4f, -0.4f, @@ -3169,7 +3152,7 @@ TEST(convolution_f32_fw_gpu, basic_wsiz2x2_wstr2x2_in4x4x2x2_nopad_group16) { topology topology(input_layout("input", input.get_layout())); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 16, 1, 2, 2 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 16, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 16, 1, 1 } }); set_values(weights, { @@ -3263,7 +3246,7 @@ TEST(convolution_f32_fw_gpu, basic_wsiz2x2_wstr2x2_in4x4x2x2_nopad_group16_bfyx) topology topology(input_layout("input", input.get_layout())); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 16, 1, 2, 2 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 16, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 16, 1, 1 } }); set_values(weights, { @@ -3372,9 +3355,9 @@ TEST(convolution_f32_fw_gpu, basic_wsiz1x1_wstr2x2_in1x1x4x1_nopad_split2) { auto input = memory::allocate(engine, { data_types::f32, format::yxfb, { 1, 4, 1, 1 } }); //auto output = memory::allocate({ memory::format::yxfb_f32,{ 1,{ 1, 1 }, 4 } }); auto weights1 = memory::allocate(engine, { data_types::f32, format::bfyx, { 2, 2, 1, 1 } }); - auto biases1 = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 2, 1 } }); + auto biases1 = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 2, 1, 1 } }); auto weights2 = memory::allocate(engine, { data_types::f32, format::bfyx, { 2, 2, 1, 1 } }); - auto biases2 = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 2, 1 } }); + auto biases2 = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 2, 1, 1 } }); set_values(input, { 1.5f, 0.5f, 0.0f, -0.5f @@ -3448,15 +3431,14 @@ TEST(convolution_f32_fw_gpu, basic_wsiz1x1_wstr2x2_in1x1x2x1_nopad_split2) { // 1 // 3.5 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::yxfb, { 1, 2, 1, 1 } }); //auto output = memory::allocate({ memory::format::yxfb_f32,{ 1,{ 1, 1 }, 4 } }); auto weights1 = memory::allocate(engine, { data_types::f32, format::bfyx, { 2, 1, 1, 1 } }); - auto biases1 = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 2, 1 } }); + auto biases1 = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 2, 1, 1 } }); auto weights2 = memory::allocate(engine, { data_types::f32, format::bfyx, { 2, 1, 1, 1 } }); - auto biases2 = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 2, 1 } }); + auto biases2 = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 2, 1, 1 } }); set_values(input, { 1.5f, 0.5f @@ -3536,15 +3518,14 @@ TEST(convolution_f32_fw_gpu, basic_wsiz1x1_wstr2x2_in1x1x4x1_filter_1x3x2x1x1_no // 6 // -2 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::yxfb, { 1, 4, 1, 1 } }); //auto output = memory::allocate({ memory::format::yxfb_f32,{ 1,{ 1, 1 }, 6 } }); auto weights1 = memory::allocate(engine, { data_types::f32, format::bfyx, { 3, 2, 1, 1 } }); - auto biases1 = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 3, 1 } }); + auto biases1 = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 3, 1, 1 } }); auto weights2 = memory::allocate(engine, { data_types::f32, format::bfyx, { 3, 2, 1, 1 } }); - auto biases2 = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 3, 1 } }); + auto biases2 = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 3, 1, 1 } }); set_values(input, { 1.5f, 0.5f, 2.0f, -1.0f @@ -3641,9 +3622,12 @@ TEST(convolution_gpu, trivial_convolution_relu) { { "biases" }, { 1,1,2,2 }, { 0,0,0,0 }, - { 1, 1, 1, 1 }, - true, - 0) + { 1, 1, 1, 1 }), + activation( + "out", + "conv", + activation_func::relu + ) ); network network(engine, topology); @@ -3651,7 +3635,7 @@ TEST(convolution_gpu, trivial_convolution_relu) { auto outputs = network.execute(); EXPECT_EQ(outputs.size(), size_t(1)); - EXPECT_EQ(outputs.begin()->first, "conv"); + EXPECT_EQ(outputs.begin()->first, "out"); auto output_prim = outputs.begin()->second.get_memory(); @@ -3715,9 +3699,13 @@ TEST(convolution_gpu, relu_with_negative_slope) { { "biases" }, { 1,1,2,2 }, { 0,0,0,0 }, - { 1, 1, 1, 1 }, - true, - 0.1f) + { 1, 1, 1, 1 }), + activation( + "out", + "conv", + activation_func::relu_negative_slope, + {0.1f, 0.0f} + ) ); network network(engine, topology); @@ -3725,7 +3713,7 @@ TEST(convolution_gpu, relu_with_negative_slope) { auto outputs = network.execute(); EXPECT_EQ(outputs.size(), size_t(1)); - EXPECT_EQ(outputs.begin()->first, "conv"); + EXPECT_EQ(outputs.begin()->first, "out"); auto output_prim = outputs.begin()->second.get_memory(); @@ -3836,11 +3824,10 @@ TEST(convolution_gpu, basic_yxfb_4_4_yxfb_2_2_b16_if2_of16_st2_2_p0_sp1_fp32) auto input = memory::allocate(engine, { data_types::f32, input_format, input_size }); auto weights_size = tensor( output_feature_count, input_feature_count, weights_x, weights_y ); auto weights = memory::allocate(engine, { data_types::f32, weights_format, weights_size }); - auto biases = memory::allocate(engine, { data_types::f32, biases_format, {1,1,output_feature_count,1}}); + auto biases = memory::allocate(engine, { data_types::f32, biases_format, {1,output_feature_count,1,1}}); //auto output = memory::allocate({output_format, {batch_size, {output_x, output_y}, output_feature_count}}); - // input: std::vector input_vals_template { 0.25f, 0.50f, 0.75f, 1.00f, @@ -3864,7 +3851,6 @@ TEST(convolution_gpu, basic_yxfb_4_4_yxfb_2_2_b16_if2_of16_st2_2_p0_sp1_fp32) } set_values(input, input_vals); - // weights: std::vector weights_vals_template { -4.0f, -2.0f, @@ -3899,7 +3885,6 @@ TEST(convolution_gpu, basic_yxfb_4_4_yxfb_2_2_b16_if2_of16_st2_2_p0_sp1_fp32) #endif set_values(weights, weights_vals); - // biases: std::vector biases_vals; biases_vals.reserve(output_feature_count); @@ -3909,7 +3894,6 @@ TEST(convolution_gpu, basic_yxfb_4_4_yxfb_2_2_b16_if2_of16_st2_2_p0_sp1_fp32) } set_values(biases, biases_vals); - // output: std::vector output_vals_template { 9.0f, 10.0f, @@ -3947,9 +3931,13 @@ TEST(convolution_gpu, basic_yxfb_4_4_yxfb_2_2_b16_if2_of16_st2_2_p0_sp1_fp32) { "biases" }, { 1,1,stride_x,stride_y }, { 0,0,0,0 }, - { 1, 1, 1, 1 }, - true, - 0.1f) + { 1, 1, 1, 1 }), + activation( + "out", + "conv", + activation_func::relu, + { 0.1f, 0.0f } + ) ); network network(engine, topology); @@ -3957,7 +3945,7 @@ TEST(convolution_gpu, basic_yxfb_4_4_yxfb_2_2_b16_if2_of16_st2_2_p0_sp1_fp32) auto outputs = network.execute(); EXPECT_EQ(outputs.size(), size_t(1)); - EXPECT_EQ(outputs.begin()->first, "conv"); + EXPECT_EQ(outputs.begin()->first, "out"); auto output_prim = outputs.begin()->second.get_memory(); @@ -4076,7 +4064,7 @@ void add_primitives(const engine& engine, topology& topology) std::vector weights_values = { 1, 2, 1, 2, 1, 2, 19, 17, -1, -10, 32, 23 }; set_values(weights, weights_values); - cldnn::memory biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + cldnn::memory biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); auto weigths_qfs = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); set_values(biases, { 1.0f, -8.0f }); @@ -4084,7 +4072,9 @@ void add_primitives(const engine& engine, topology& topology) data("weights", weights), data("biases", biases), data("w_qfs", weigths_qfs), - convolution("conv", "input", { "weights" }, { "biases" }, { 0, 0, 1, 2 }, { 0, 0, 0, 0 }, { 1, 1, 1, 1 }, true)); + convolution("conv", "input", { "weights" }, { "biases" }, { 0, 0, 1, 2 }, { 0, 0, 0, 0 }, { 1, 1, 1, 1 }), + activation( "out", "conv", activation_func::relu) + ); } TEST(convolution_f32_fw_gpu, byte_activation) { @@ -4142,9 +4132,9 @@ TEST(convolution_f32_fw_gpu, byte_activation) { network.set_input_data("input", input); auto outputs = network.execute(); - EXPECT_EQ(outputs.begin()->first, "conv"); + EXPECT_EQ(outputs.begin()->first, "out"); - auto output_memory = outputs.at("conv").get_memory(); + auto output_memory = outputs.at("out").get_memory(); auto output_layout = output_memory.get_layout(); auto output_ptr = output_memory.pointer(); @@ -4198,7 +4188,7 @@ TEST(convolution_f32_fw_gpu, quantized_convolution_low_prec_single_ofq) { auto input_f = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 5, 4 } }); auto weights_f = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 3, 2 } }); - cldnn::memory biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + cldnn::memory biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); auto weigths_qfs = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); std::vector weights_values_f = { 1.0, 2.0, 1.0, 2.0, 1.0, 2.0, 19.0, 17.0, -1.0, -10.0, 32.0, 23.0 }; @@ -4280,7 +4270,6 @@ TEST(convolution_f32_fw_gpu, quantized_convolution_low_prec_single_ofq) { } } - TEST(convolution_f32_fw_gpu, quantized_convolution_high_prec_calib_per_ofm) { // Filter : 2x3 // Stride : 2x1 @@ -4313,7 +4302,7 @@ TEST(convolution_f32_fw_gpu, quantized_convolution_high_prec_calib_per_ofm) { auto input_f = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 5, 4 } }); auto weights_f = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 3, 2 } }); - cldnn::memory biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + cldnn::memory biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); auto weigths_qfs = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); auto output_calibrations = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); @@ -4451,7 +4440,7 @@ protected: auto input_shape = tensor(1, n_features, 4, 1); auto weights_shape = tensor(n_features, n_features, 3, 1); - auto biases_shape = tensor(1, 1, n_features, 1); + auto biases_shape = tensor(1, n_features, 1, 1); auto input = memory::allocate( engine, @@ -4500,15 +4489,15 @@ protected: type_to_data_type::value, {1, 1, 1, 1}, {0, 0, 0, 0}, - {1, 1, 1, 1}, - true)); + {1, 1, 1, 1}), + activation("out", "conv", activation_func::relu)); network network(engine, topology, opts); network.set_input_data("input", input); auto outputs = network.execute(); - auto output_memory = outputs.at("conv").get_memory(); + auto output_memory = outputs.at("out").get_memory(); auto output_layout = output_memory.get_layout(); auto output_ptr = output_memory.pointer(); int y_size = output_layout.size.spatial[1]; @@ -4682,10 +4671,10 @@ TEST(convolution_f32_fw_gpu, calibration_advance) { auto input_f = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 5, 4 } }); auto weights_f = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 3, 2 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); auto w_qf = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); auto weights_f_2 = memory::allocate(engine, { data_types::f32, format::bfyx,{ 3, 2, 3, 2 } }); - auto biases_2 = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 3, 1 } }); + auto biases_2 = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 3, 1, 1 } }); auto w_qf_2 = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 3, 1 } }); std::vector weights_values_f = { 1.0f, 2.0f, 1.0f, 2.0f, 1.0f, 2.0f, 1.9f, 1.7f, -1.0f, -1.0f, 3.2f, 2.3f }; @@ -4854,7 +4843,6 @@ TEST(convolution_f32_fw_gpu, local_basic) { EXPECT_FLOAT_EQ(fl, output_vec[cntr++]); } - TEST(convolution_f32_fw_gpu, local_multi_out_features) { // Filter : 3x1x3x3x2x2 - local sizes // Stride : 1x1 @@ -4916,7 +4904,7 @@ TEST(convolution_f32_fw_gpu, local_multi_out_features) { tensor local_size = tensor(3,1,2,2,3,3); auto input_f = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 4, 4 } }); auto weights_f = memory::allocate(engine, { data_types::f32, format::bf_lyx_yx, local_size }); - cldnn::memory biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 3, 1 } }); + cldnn::memory biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 3, 1, 1 } }); std::vector weights_values_f = { 0.0, 0.0, 0.0, 0.0, @@ -5130,7 +5118,6 @@ TEST(convolution_f32_fw_gpu, local_multi_input_features) { EXPECT_FLOAT_EQ(fl, output_vec[cntr++]); } - TEST(convolution_gpu, basic_yxfb_4_4_yxfb_2_2_b16_if2_of16_st2_2_p0_sp1_fp16) { #define USE_OLD_WEIGHTS_FORMAT 0 @@ -5144,7 +5131,6 @@ TEST(convolution_gpu, basic_yxfb_4_4_yxfb_2_2_b16_if2_of16_st2_2_p0_sp1_fp16) return; } - const auto input_format = format::yxfb; #if USE_OLD_WEIGHTS_FORMAT const auto weights_format = format::bfyx; @@ -5168,12 +5154,11 @@ TEST(convolution_gpu, basic_yxfb_4_4_yxfb_2_2_b16_if2_of16_st2_2_p0_sp1_fp16) const int32_t output_x = (input_x - weights_x) / stride_x + 1; const int32_t output_y = (input_y - weights_y) / stride_y + 1; - auto input_size = tensor( batch_size, input_feature_count, input_x, input_y ); auto input = memory::allocate(engine, { data_types::f32, input_format, input_size }); auto weights_size = tensor( output_feature_count, input_feature_count, weights_x, weights_y ); auto weights = memory::allocate(engine, { data_types::f32, weights_format, weights_size }); - auto biases_size = tensor( 1,1,output_feature_count,1 ); + auto biases_size = tensor( 1,output_feature_count,1,1 ); auto biases = memory::allocate(engine, { data_types::f32, biases_format, biases_size }); auto output_size = tensor( batch_size, output_feature_count, output_x, output_y ); //auto output = memory::allocate({output_format, {batch_size, {output_x, output_y}, output_feature_count}}); @@ -5183,7 +5168,6 @@ TEST(convolution_gpu, basic_yxfb_4_4_yxfb_2_2_b16_if2_of16_st2_2_p0_sp1_fp16) //auto biases_cvtd = memory::allocate(engine, { data_types::f16, biases_size }); //auto output_cvtd = memory::allocate({output_cvt_format, {batch_size, {output_x, output_y}, output_feature_count}}); - // input: std::vector input_vals_template { 0.25f, 0.50f, 0.75f, 1.00f, @@ -5207,7 +5191,6 @@ TEST(convolution_gpu, basic_yxfb_4_4_yxfb_2_2_b16_if2_of16_st2_2_p0_sp1_fp16) } set_values(input, input_vals); - // weights: std::vector weights_vals_template { -0.50f, -0.25f, @@ -5242,7 +5225,6 @@ TEST(convolution_gpu, basic_yxfb_4_4_yxfb_2_2_b16_if2_of16_st2_2_p0_sp1_fp16) #endif set_values(weights, weights_vals); - // biases: std::vector biases_vals; biases_vals.reserve(output_feature_count); @@ -5252,7 +5234,6 @@ TEST(convolution_gpu, basic_yxfb_4_4_yxfb_2_2_b16_if2_of16_st2_2_p0_sp1_fp16) } set_values(biases, biases_vals); - // output: std::vector output_vals_template { 1.125f, 1.250f, @@ -5289,7 +5270,6 @@ TEST(convolution_gpu, basic_yxfb_4_4_yxfb_2_2_b16_if2_of16_st2_2_p0_sp1_fp16) // // auto expected_ptr = expected.as().pointer(); - // Computing convolution. topology topology( input_layout("input", input.get_layout()), @@ -5347,6 +5327,15 @@ using TestParamType_convolution_gpu = ::testing::tuple; // 4 - With bias + +using TestParamType_convolution_depthwise_gpu = ::testing::tuple; // 6 - With bias + struct convolution_gpu : public ::testing::TestWithParam { static std::string @@ -5362,6 +5351,23 @@ struct convolution_gpu : public ::testing::TestWithParam +{ + static std::string + PrintToStringParamName(testing::TestParamInfo param_info) + { + // construct a readable name + return "in" + std::to_string(testing::get<0>(param_info.param)) + + "x" + std::to_string(testing::get<0>(param_info.param)) + + "_k" + std::to_string(testing::get<1>(param_info.param)) + + 'x' + std::to_string(testing::get<2>(param_info.param)) + + "_f" + std::to_string(testing::get<3>(param_info.param)) + + "_stride" + std::to_string(testing::get<4>(param_info.param)) + + "_pad" + std::to_string(testing::get<5>(param_info.param)) + + (testing::get<6>(param_info.param) ? "_bias" : ""); + } +}; + TEST_P(convolution_gpu, b_fs_yx_fsv4) { const int in_B = 2; @@ -5442,8 +5448,8 @@ TEST_P(convolution_gpu, b_fs_yx_fsv4) x = 0.3f; return x; }); - auto bias_gold = memory::allocate(engine, {data_types::f32, format::bfyx, {1, 1, _OuD, 1}}); - auto bias_imad = memory::allocate(engine, {data_types::f32, format::bfyx, {1, 1, _OuD, 1}}); + auto bias_gold = memory::allocate(engine, {data_types::f32, format::bfyx, {1, _OuD, 1, 1}}); + auto bias_imad = memory::allocate(engine, {data_types::f32, format::bfyx, {1, _OuD, 1, 1}}); auto callib_gold = memory::allocate(engine, {data_types::f32, format::bfyx, {1, 1, _OuD, 1}}); auto callib_imad = memory::allocate(engine, {data_types::f32, format::bfyx, {1, 1, _OuD, 1}}); auto quant_gold = memory::allocate(engine, {data_types::f32, format::bfyx, {1, 1, _OuD, 1}}); @@ -5529,7 +5535,7 @@ TEST_P(convolution_gpu, b_fs_yx_fsv4) } // Select particular test cases -INSTANTIATE_TEST_CASE_P(convolution_gpu_imad, +INSTANTIATE_TEST_CASE_P(DISABLED_convolution_gpu_imad, convolution_gpu, ::testing::Values( // Filter size, Input features, Stride, Output padding, With bias @@ -5615,7 +5621,7 @@ TEST_P(convolution_gpu, fs_byx_fsv32) if (with_bias) { // Generate bias data - auto biases_size = tensor(1, 1, output_f, 1); + auto biases_size = tensor(1, output_f, 1, 1); auto biases_data = generate_random_1d(output_f, -1, 1); auto biases_mem = memory::allocate(engine, { data_types::f16, format::bfyx, biases_size }); set_values(biases_mem, biases_data); @@ -5699,6 +5705,147 @@ TEST_P(convolution_gpu, fs_byx_fsv32) } } +INSTANTIATE_TEST_CASE_P(convolution_depthwise_gpu, + convolution_depthwise_gpu, + ::testing::Values( + // Input size, Filter size Y, Filter size X, groups, Stride, Output padding, With bias + // Stride testing + TestParamType_convolution_depthwise_gpu(5, 3, 3, 32, 1, 0, false), + TestParamType_convolution_depthwise_gpu(5, 3, 3, 32, 2, 0, false), + TestParamType_convolution_depthwise_gpu(5, 3, 3, 32, 3, 0, false), + // Different Features testing + TestParamType_convolution_depthwise_gpu(5, 3, 3, 16, 1, 0, false), + TestParamType_convolution_depthwise_gpu(5, 3, 3, 20, 1, 0, false), + TestParamType_convolution_depthwise_gpu(5, 3, 3, 25, 1, 0, false), + TestParamType_convolution_depthwise_gpu(5, 3, 3, 33, 1, 0, false), + TestParamType_convolution_depthwise_gpu(5, 3, 3, 35, 1, 0, false), + TestParamType_convolution_depthwise_gpu(5, 3, 3, 45, 1, 0, false), + TestParamType_convolution_depthwise_gpu(5, 3, 3, 65, 1, 0, false), + // Different filter's sizes testing + TestParamType_convolution_depthwise_gpu(5, 3, 2, 16, 1, 0, false), + TestParamType_convolution_depthwise_gpu(5, 3, 1, 16, 1, 0, false), + TestParamType_convolution_depthwise_gpu(5, 2, 3, 16, 1, 0, false), + TestParamType_convolution_depthwise_gpu(5, 1, 3, 16, 1, 0, false), + TestParamType_convolution_depthwise_gpu(5, 3, 2, 16, 2, 0, false), + TestParamType_convolution_depthwise_gpu(5, 3, 1, 16, 2, 0, false), + TestParamType_convolution_depthwise_gpu(5, 2, 3, 16, 2, 0, false), + TestParamType_convolution_depthwise_gpu(5, 1, 3, 16, 2, 0, false), + // Input FeatureMap testing + TestParamType_convolution_depthwise_gpu(20, 3, 3, 50, 1, 0, false), + TestParamType_convolution_depthwise_gpu(30, 3, 3, 50, 1, 0, false), + TestParamType_convolution_depthwise_gpu(55, 3, 3, 50, 1, 0, false), + // Output padding testing + strides + TestParamType_convolution_depthwise_gpu(5, 3, 3, 32, 1, 1, false), + TestParamType_convolution_depthwise_gpu(5, 3, 3, 32, 2, 2, false), + TestParamType_convolution_depthwise_gpu(5, 3, 3, 32, 3, 3, false) + ), + convolution_depthwise_gpu::PrintToStringParamName); + +TEST_P(convolution_depthwise_gpu, depthwise_conv_fs_b_yx_fsv32) +{ + const auto& engine = get_test_engine(); + + if (!engine.get_info().supports_fp16) + { + std::cout << "[ SKIPPED ] The test is skipped (cl_khr_fp16 is not supported)." << std::endl; + EXPECT_EQ(1, 1); + return; + } + + const int batch_num = 2; + const int input_xy = testing::get<0>(GetParam()); + const int groups = testing::get<3>(GetParam()); + const int input_f = groups; + const int output_f = groups; + const int filter_y = testing::get<1>(GetParam()); + const int filter_x = testing::get<2>(GetParam()); + const int stride = testing::get<4>(GetParam()); + const int output_padding = testing::get<5>(GetParam()); + const bool with_bias = testing::get<6>(GetParam()); + const int input_offset_y = -(filter_y / 2); + const int input_offset_x = -(filter_x / 2); + + const int output_y = 1 + (input_xy + 2 * (-input_offset_y) - filter_y) / stride + 2 * output_padding; + const int output_x = 1 + (input_xy + 2 * (-input_offset_x) - filter_x) / stride + 2 * output_padding; + + auto input_size = tensor(batch_num, input_f, input_xy, input_xy); + auto input_data = generate_random_4d(batch_num, input_f, input_xy, input_xy, -1, 1); + auto input_data_bfyx = flatten_4d(format::bfyx, input_data); + auto input_mem = memory::allocate(engine, { data_types::f16, format::bfyx, input_size }); + set_values(input_mem, input_data_bfyx); + + auto weights_size = tensor(output_f, 1, filter_x, filter_y); + auto weights_data = generate_random_4d(output_f, 1, filter_y, filter_x, -1, 1); + auto weights_data_bfyx = flatten_4d(format::bfyx, weights_data); + auto weights_mem = memory::allocate(engine, { data_types::f16, format::bfyx, weights_size }); + set_values(weights_mem, weights_data_bfyx); + + // Will be used to store reference values calculated in branches depending on bias + auto reference_result = VVVVF(batch_num, VVVF(output_f)); + + topology topology( + input_layout("input", input_mem.get_layout()), + data("weights_fsv", weights_mem)); + + // Reorder input to fs_byx_fsv32 + topology.add(reorder("input_fsv", "input", { data_types::f16, format::fs_b_yx_fsv32, input_size })); + + // Calculate reference values without bias + for (auto bi = 0; bi < batch_num; ++bi) + { + for (auto ofi = 0; ofi < output_f; ++ofi) + { + reference_result[bi][ofi] = reference_convolve( + input_data[bi], weights_data[ofi], // input, weights + stride, stride, // strides + 0, // bias + 1, 1, // dilation + -input_offset_y, -input_offset_x, // input padding + output_padding, output_padding, // output_padding + ofi, ofi + 1, // f_begin, f_end + true); // depthwise + } + } + + auto conv_fsv = convolution("conv_fsv", "input_fsv", { "weights_fsv" }, groups, + { 1, 1, stride, stride }, { 0, 0, input_offset_x, input_offset_y }); + conv_fsv.output_padding = padding({ 0, 0, output_padding, output_padding }, 0.f); + + topology.add(conv_fsv); + + build_options options; + options.set_option(build_option::optimize_data(true)); + network network(engine, topology, options); + + network.set_input_data("input", input_mem); + + network.execute(); + + auto out_mem = network.get_output("conv_fsv").get_memory(); + auto out_ptr = out_mem.pointer(); + + ASSERT_EQ(out_mem.get_layout().format, format::fs_b_yx_fsv32); + + for (int bi = 0; bi < batch_num; ++bi) + for (int fi = 0; fi < output_f; ++fi) + for (int yi = 0; yi < output_y; ++yi) + for (int xi = 0; xi < output_x; ++xi) + { + auto val_ref = reference_result[bi][fi][yi][xi]; + auto val = out_ptr[(fi / 32) * batch_num * output_y * output_x * 32 + + bi * output_y * output_x * 32 + + yi * output_x * 32 + + xi * 32 + + fi % 32]; + auto equal = are_equal(val_ref, val, 1e-2f); + EXPECT_TRUE(equal); + if (!equal) + { + std::cout << "At b = " << bi << ", fi = " << fi << ", yi = " << yi << ", xi = " << xi << std::endl; + } + } +} + class convolution_test : public tests::generic_test { @@ -5711,13 +5858,11 @@ public: delete generic_params; } - for (auto layer_params : all_layer_params) - { - delete layer_params; - } + all_layer_params.clear(); + all_test_params.clear(); } - static std::vector generate_specific_test_params() + static std::vector> generate_specific_test_params() { // TODO: check split @@ -5730,31 +5875,28 @@ public: std::vector dilation_sizes = { tensor(1, 1, 1, 1), tensor(1, 1, 5, 4), tensor(1, 1, 1, 3), tensor(1, 1, 7, 2) }; std::vector input_offset_sizes = { tensor(0, 0, 0, 0), tensor(0, 0, 2, 2), tensor(0, 0, -5, -2), tensor(0, 0, 3, -3) }; - std::vector activations = { false, true }; - std::vector activation_slopes = { 0.f, -2.3f }; - // No padding - all_layer_params.push_back(new convolution("convolution", "input0", weights, bias, stride_sizes[0], input_offset_sizes[0], dilation_sizes[0], activations[0], activation_slopes[0])); - all_layer_params.push_back(new convolution("convolution", "input0", weights, bias, stride_sizes[1], input_offset_sizes[1], dilation_sizes[1], activations[0], activation_slopes[0])); - all_layer_params.push_back(new convolution("convolution", "input0", weights, bias, stride_sizes[2], input_offset_sizes[2], dilation_sizes[2], activations[1], activation_slopes[0])); - all_layer_params.push_back(new convolution("convolution", "input0", weights, bias, stride_sizes[3], input_offset_sizes[3], dilation_sizes[3], activations[1], activation_slopes[1])); + all_layer_params.emplace_back(new convolution("convolution_no_relu", "input0", weights, bias, stride_sizes[0], input_offset_sizes[0], dilation_sizes[0])); + all_layer_params.emplace_back(new convolution("convolution_no_relu", "input0", weights, bias, stride_sizes[1], input_offset_sizes[1], dilation_sizes[1])); + all_layer_params.emplace_back(new convolution("convolution_no_relu", "input0", weights, bias, stride_sizes[2], input_offset_sizes[2], dilation_sizes[2])); + all_layer_params.emplace_back(new convolution("convolution_no_relu", "input0", weights, bias, stride_sizes[3], input_offset_sizes[3], dilation_sizes[3])); // Input padding - all_layer_params.push_back(new convolution("convolution", "reorder0", weights, bias, stride_sizes[1], input_offset_sizes[1], dilation_sizes[1], activations[0], activation_slopes[0])); - all_layer_params.push_back(new convolution("convolution", "reorder0", weights, bias, stride_sizes[3], input_offset_sizes[3], dilation_sizes[3], activations[1], activation_slopes[1])); + all_layer_params.emplace_back(new convolution("convolution_no_relu", "reorder0", weights, bias, stride_sizes[1], input_offset_sizes[1], dilation_sizes[1])); + all_layer_params.emplace_back(new convolution("convolution_no_relu", "reorder0", weights, bias, stride_sizes[3], input_offset_sizes[3], dilation_sizes[3])); // Output padding - all_layer_params.push_back(new convolution("convolution", "input0", weights, bias, stride_sizes[1], input_offset_sizes[1], dilation_sizes[1], activations[0], activation_slopes[0], { { 0, 0, 2, 4 },{ 0, 0, 0, 19 } })); - all_layer_params.push_back(new convolution("convolution", "input0", weights, bias, stride_sizes[2], input_offset_sizes[2], dilation_sizes[2], activations[1], activation_slopes[0], { { 0, 0, 1, 0 },{ 0, 0, 13, 9 } })); + all_layer_params.emplace_back(new convolution("convolution_no_relu", "input0", weights, bias, stride_sizes[1], input_offset_sizes[1], dilation_sizes[1], { { 0, 0, 2, 4 },{ 0, 0, 0, 19 } })); + all_layer_params.emplace_back(new convolution("convolution_no_relu", "input0", weights, bias, stride_sizes[2], input_offset_sizes[2], dilation_sizes[2], { { 0, 0, 1, 0 },{ 0, 0, 13, 9 } })); // Input + Output padding - all_layer_params.push_back(new convolution("convolution", "reorder0", weights, bias, stride_sizes[0], input_offset_sizes[0], dilation_sizes[0], activations[0], activation_slopes[0], { { 0, 0, 1, 5 },{ 0, 0, 19, 4 } })); - all_layer_params.push_back(new convolution("convolution", "reorder0", weights, bias, stride_sizes[3], input_offset_sizes[3], dilation_sizes[3], activations[1], activation_slopes[1], { { 0, 0, 1, 2 },{ 0, 0, 3, 4 } })); + all_layer_params.emplace_back(new convolution("convolution_no_relu", "reorder0", weights, bias, stride_sizes[0], input_offset_sizes[0], dilation_sizes[0], { { 0, 0, 1, 5 },{ 0, 0, 19, 4 } })); + all_layer_params.emplace_back(new convolution("convolution_no_relu", "reorder0", weights, bias, stride_sizes[3], input_offset_sizes[3], dilation_sizes[3], { { 0, 0, 1, 2 },{ 0, 0, 3, 4 } })); return all_layer_params; } - static std::vector> generate_all_test_params() + static std::vector>> generate_all_test_params() { generate_specific_test_params(); @@ -5798,7 +5940,7 @@ public: } // Create all the combinations for the test. - for (cldnn::primitive* layer_param : all_layer_params) + for (const auto& layer_param : all_layer_params) { for (tests::test_params* test_param : all_generic_params) { @@ -5816,7 +5958,7 @@ public: virtual cldnn::tensor get_expected_output_tensor() { - const cldnn::convolution* convolution = (cldnn::convolution*)layer_params; + auto convolution = std::static_pointer_cast(layer_params); tensor input_size = generic_params->input_layouts[0].size; tensor dilation = convolution->dilation; tensor stride = convolution->stride; @@ -5877,15 +6019,13 @@ public: { // Output reference is always bfyx. - const cldnn::convolution* convolution = (cldnn::convolution*)layer_params; + auto convolution = std::static_pointer_cast(layer_params); data_types dt = inputs[0].get_layout().data_type; tensor input_size = inputs[0].get_layout().size; tensor dilation = convolution->dilation; tensor stride = convolution->stride; - bool is_relu_fused = convolution->with_activation; - float activation_slope = convolution->activation_negative_slope; tensor input_offset = convolution->input_offset; tensor weights_size = inputs[1].get_layout().size; padding output_padding = convolution->output_padding; @@ -5985,15 +6125,6 @@ public: } } - // Relu activation - if (is_relu_fused) - { - for (int i = 0; i < (int)output_buffer_size.count(); i++) - { - output_mem[i] = (output_mem[i] > 0.f) ? output_mem[i] : (output_mem[i] * (Type)activation_slope); - } - } - return output; } @@ -6012,13 +6143,13 @@ public: private: static std::vector all_generic_params; - static std::vector all_layer_params; - static std::vector> all_test_params; + static std::vector> all_layer_params; + static std::vector>> all_test_params; }; std::vector convolution_test::all_generic_params = {}; -std::vector convolution_test::all_layer_params = {}; -std::vector> convolution_test::all_test_params = {}; +std::vector> convolution_test::all_layer_params = {}; +std::vector>> convolution_test::all_test_params = {}; TEST_P(convolution_test, CONVOLUTION) { diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/convolution_grad_input_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/convolution_grad_input_gpu_test.cpp index 6f8b9d4..e725f71 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/convolution_grad_input_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/convolution_grad_input_gpu_test.cpp @@ -17,15 +17,15 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/convolution_grad_input.hpp" -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/convolution_grad_input.hpp" +#include +#include +#include +#include #include "test_utils/test_utils.h" -#include "api/CPP/eltwise.hpp" +#include "api/eltwise.hpp" using namespace cldnn; using namespace tests; @@ -84,7 +84,6 @@ TEST(convolution_grad_input_f32_fw_gpu, basic_wsiz2x2_in2x2x1x2_bfyx_stride2_pad } } - TEST(convolution_grad_input_f32_fw_gpu, basic_wsiz2x2_in2x2x1x2_bfyx_stride2_pad1_output_size) { // Filter : 2x2 // Input : 2x2x1x2 diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/convolution_grad_weights_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/convolution_grad_weights_gpu_test.cpp index 29c1ea8..a022aa0 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/convolution_grad_weights_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/convolution_grad_weights_gpu_test.cpp @@ -17,17 +17,17 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/convolution_grad_weights.hpp" -#include "api/CPP/convolution.hpp" -#include "api/CPP/convolution_grad_input.hpp" -#include "api/CPP/reorder.hpp" -#include -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/convolution_grad_weights.hpp" +#include "api/convolution.hpp" +#include "api/convolution_grad_input.hpp" +#include "api/reorder.hpp" +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" using namespace cldnn; @@ -68,7 +68,7 @@ TEST(convolution_grad_weights_f32_fw_gpu, basic_wsiz2x2_in2x2x1x2_bfyx_stride2_p auto input_grad = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 2, 2, 2 } }); auto input = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 2, 2 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx, { 2, 1, 3, 3 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); set_values(input, { 8.f, 0.5f, 6.f, 9.f }); set_values(input_grad, { 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.f, 1.7f, 1.8f }); @@ -144,7 +144,7 @@ TEST(convolution_grad_weights_f32_fw_gpu, basic_wsiz2x2_in8x1x2x2_bfyx_stride2_p auto input_grad = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 2, 2, 2 } }); auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 2, 2 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 3, 3 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); set_values(input, { 8.f, 0.5f, 6.f, 9.f, 8.f, 0.5f, 4.f, 7.f }); set_values(input_grad, { 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.f, 1.7f, 1.8f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 1.f, 1.7f, 1.8f }); @@ -447,7 +447,7 @@ TEST(convolution_grad_weights_f32_fw_gpu, basic_wsiz1x1_in1x2x5x5_bfyx_stride2_p auto input_grad = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 5, 5 } }); auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 4, 4 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 1, 1 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); set_values(input, { 8.f, 0.5f, 1.f, 2.f, @@ -620,7 +620,7 @@ TEST(convolution_grad_weights_f32_fw_gpu, basic_wsiz3x3_in2x1x3x3_bfyx_stride1_p auto input_grad = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 2, 3, 3 } }); auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 3, 3 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 3, 3 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); set_values(input, { 0.5f, 0.6f, 0.7f, 0.9f, 1.f, 1.1f, 0.7f, 0.9f, 0.1f, @@ -702,9 +702,9 @@ TEST(convolution_grad_weights_f32_fw_gpu, basic_wsiz3x3_in2x1x3x3_bfyx_stride1_p auto input_grad = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 2, 3, 3 } }); auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 3, 3 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 3, 3 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); auto prev_weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 3, 3} }); - auto prev_biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1} }); + auto prev_biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1} }); set_values(input, { 0.5f, 0.6f, 0.7f, 0.9f, 1.f, 1.1f, 0.7f, 0.9f, 0.1f, @@ -804,7 +804,7 @@ TEST(convolution_grad_weights_f32_fw_gpu, basic_wsiz7x7_in2x1x7x7_bfyx_stride1_p auto input_grad = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 2, 7, 7 } }); auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 7, 7 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 7, 7 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); set_values(input, { 0.5f, 0.6f, 0.7f, 0.9f, 0.2f, 0.1f, 0.7f, @@ -950,9 +950,9 @@ TEST(convolution_grad_weights_f32_fw_gpu, basic_wsiz7x7_in2x1x7x7_bfyx_stride1_p auto input_grad = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 2, 7, 7 } }); auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 7, 7 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 7, 7 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); auto prev_weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 7, 7 } }); - auto prev_biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + auto prev_biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); set_values(input, { 0.5f, 0.6f, 0.7f, 0.9f, 0.2f, 0.1f, 0.7f, @@ -1074,7 +1074,6 @@ TEST(convolution_grad_weights_f32_fw_gpu, ngraph_2d_1item_2iterations) { auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 5, 3 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 2, 2 } }); - topology topology( input_layout("input_grad", input_grad.get_layout()), data("input", input), @@ -1086,7 +1085,6 @@ TEST(convolution_grad_weights_f32_fw_gpu, ngraph_2d_1item_2iterations) { bo.set_option(build_option::optimize_data(true)); network network(engine, topology, bo); - // set values for first iteration set_values(input, { 0.671875f, 0.546875f, -0.5625f, -0.359375f, -0.09375f, 0.546875f, -0.546875f, 0.890625f, 0.828125f, -0.546875f, 1.f, -0.078125f, -0.890625f, 0.40625f, -0.359375f }); diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/crop_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/crop_gpu_test.cpp index 9bea2a3..622065d 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/crop_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/crop_gpu_test.cpp @@ -16,12 +16,12 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/crop.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/crop.hpp" +#include +#include +#include #include "test_utils/test_utils.h" using namespace cldnn; @@ -93,6 +93,53 @@ TEST(crop_gpu, basic_in2x3x2x2_crop_all) { } } +TEST(crop_gpu, basic_in2x2x2x3_crop_all) { + const auto& engine = get_test_engine(); + + auto batch_num = 2; + auto feature_num = 2; + auto x_size = 3; + auto y_size = 2; + + auto crop_batch_num = batch_num - 1; + auto crop_feature_num = feature_num - 1; + auto crop_x_size = x_size - 1; + auto crop_y_size = y_size - 1; + + auto input = memory::allocate(engine, { data_types::f32, format::yxfb,{ batch_num, feature_num, x_size, y_size } }); + + topology topology; + topology.add(input_layout("input", input.get_layout())); + topology.add(crop("crop", "input", { crop_batch_num, crop_feature_num, crop_x_size, crop_y_size }, { 0, 0, 0, 0 })); + + std::vector input_vec; + for (int i = 0; i < batch_num * feature_num * y_size * x_size; i++) + input_vec.push_back(i); + set_values(input, input_vec); + + network network(engine, topology); + + network.set_input_data("input", input); + + auto outputs = network.execute(); + + auto output = outputs.at("crop").get_memory(); + auto output_ptr = output.pointer(); + + printf("Results:\n"); + for (int b = 0; b < crop_batch_num; ++b) { //B + for (int f = 0; f < crop_feature_num; ++f) { //F + for (int y = 0; y < crop_y_size; ++y) { //Y + for (int x = 0; x < crop_x_size; ++x) { //X + int linear_id = b + batch_num * (f + feature_num * (x + x_size * y)); + int output_linear_id = b + crop_batch_num * (f + crop_feature_num * (x + crop_x_size * y)); + EXPECT_EQ(output_ptr[output_linear_id], input_vec[linear_id]); + } + } + } + } +} + TEST(crop_gpu, basic_int_in2x3x2x2_crop_all) { // Reference : 1x2x2x2 // Input : 2x3x4x5 @@ -662,11 +709,11 @@ TEST(crop_gpu, basic_in1x4x1x1_split_w_relu) { topology topology; topology.add(input_layout("input", input.get_layout())); - topology.add(activation("relu", "input", activation_relu)); + topology.add(activation("relu", "input", activation_func::relu)); topology.add(crop("crop1", "relu", tensor(batch(crop_batch_num), spatial(crop_x_size, crop_y_size), feature(crop_feature_num_1)), { tensor(feature(feature_offset_1), spatial(0,0),batch(0)) })); topology.add(crop("crop2", "relu", tensor(batch(crop_batch_num), spatial(crop_x_size, crop_y_size), feature(crop_feature_num_2)), { tensor(feature(feature_offset_2), spatial(0,0),batch(0)) })); - topology.add(activation("relu1", "crop1", activation_relu)); - topology.add(activation("relu2", "crop2", activation_relu)); + topology.add(activation("relu1", "crop1", activation_func::relu)); + topology.add(activation("relu2", "crop2", activation_func::relu)); std::vector input_vec = { -1.f, 2.f, -3.f, 4.f }; std::vector out1 = { 0.f, 2.f,0.f }; diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/custom_gpu_primitive_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/custom_gpu_primitive_test.cpp index f74a5f9..706f870 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/custom_gpu_primitive_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/custom_gpu_primitive_test.cpp @@ -16,18 +16,17 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/eltwise.hpp" -#include "api/CPP/reorder.hpp" -#include "api/CPP/custom_gpu_primitive.hpp" -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/eltwise.hpp" +#include "api/reorder.hpp" +#include "api/custom_gpu_primitive.hpp" +#include +#include +#include +#include #include "test_utils/test_utils.h" - namespace cldnn { template<> struct type_to_data_type { static const data_types value = data_types::f16; }; @@ -74,7 +73,10 @@ TEST(custom_gpu_primitive_f32, add_basic_in2x2x2x2) { } )__krnl"; std::string entry_point = "add_kernel"; - std::vector parameters = { {arg_input, 0}, {arg_input, 1 }, {arg_output, 0 } }; + std::vector parameters = { + {custom_gpu_primitive::arg_input, 0}, + {custom_gpu_primitive::arg_input, 1 }, + {custom_gpu_primitive::arg_output, 0 } }; layout output_layout = { data_types::f32, format::yxfb,{ 2, 2, 2, 2 } }; std::vector gws = { output_layout.count() }; topology topology; @@ -178,7 +180,7 @@ void add_basic_in2x2x2x2_with_reorder() " output[idx] = input0[idx] + input1[idx];\n" + " }\n"; std::string entry_point = "add_kernel"; - std::vector parameters = { { arg_input, 0 },{ arg_input, 1 },{ arg_output, 0 } }; + std::vector parameters = { { custom_gpu_primitive::arg_input, 0 },{ custom_gpu_primitive::arg_input, 1 },{ custom_gpu_primitive::arg_output, 0 } }; layout output_layout = { DType, format::yxfb,{ 2, 2, 2, 2 } }; std::vector gws = { output_layout.count() }; topology topology; @@ -280,7 +282,7 @@ TEST(custom_gpu_primitive_f32, eltwise_add_basic_in2x2x2x2) { } )__krnl"; std::string entry_point = "add_kernel"; - std::vector parameters = { { arg_input, 0 },{ arg_output, 0 } }; + std::vector parameters = { { custom_gpu_primitive::arg_input, 0 },{ custom_gpu_primitive::arg_output, 0 } }; layout output_layout = { data_types::f32, format::yxfb,{ 2, 2, 2, 2 } }; std::vector gws = { output_layout.count() }; topology topology; @@ -373,7 +375,7 @@ TEST(custom_gpu_primitive_f32, add_eltwise_basic_in2x2x2x2) { } )__krnl"; std::string entry_point = "add_kernel"; - std::vector parameters = { { arg_input, 0 },{ arg_output, 0 } }; + std::vector parameters = { { custom_gpu_primitive::arg_input, 0 },{ custom_gpu_primitive::arg_output, 0 } }; layout output_layout = { data_types::f32, format::yxfb,{ 2, 2, 2, 2 } }; std::vector gws = { output_layout.count() }; topology topology; @@ -474,7 +476,7 @@ TEST(custom_gpu_primitive_f32, two_kernels_with_same_entry_point_basic_in2x2x2x2 } )__krnl"; std::string entry_point = "add_kernel"; - std::vector parameters = { { arg_input, 0 },{ arg_output, 0 } }; + std::vector parameters = { { custom_gpu_primitive::arg_input, 0 },{ custom_gpu_primitive::arg_output, 0 } }; layout output_layout = { data_types::f32, format::yxfb,{ 2, 2, 2, 2 } }; std::vector gws = { output_layout.count() }; topology topology; @@ -539,7 +541,7 @@ TEST(custom_gpu_primitive_u8, add_basic_in2x2x2x2) { } )__krnl"; std::string entry_point = "add_kernel"; - std::vector parameters = { { arg_input, 0 },{ arg_input, 1 },{ arg_output, 0 } }; + std::vector parameters = { { custom_gpu_primitive::arg_input, 0 },{ custom_gpu_primitive::arg_input, 1 },{ custom_gpu_primitive::arg_output, 0 } }; layout output_layout = { data_types::u8, format::yxfb,{ 2, 2, 2, 2 } }; std::vector gws = { output_layout.count() }; topology topology; @@ -593,4 +595,4 @@ TEST(custom_gpu_primitive_u8, add_basic_in2x2x2x2) { { EXPECT_TRUE(are_equal(answers[i], output_ptr[i])); } -} \ No newline at end of file +} diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/deconvolution_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/deconvolution_gpu_test.cpp index 4aa2ad8..f0460d7 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/deconvolution_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/deconvolution_gpu_test.cpp @@ -17,17 +17,16 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/deconvolution.hpp" -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/deconvolution.hpp" +#include +#include +#include +#include #include "test_utils/test_utils.h" #include "test_utils/float16.h" -#include "api/CPP/reorder.hpp" - +#include "api/reorder.hpp" using namespace cldnn; using namespace tests; @@ -49,9 +48,9 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in2x2x1x1_nopad) { // 2 // // Output: - // -14 5 2.25 - // 18 0.75 7.25 - // 23 42.5 15.5 + // -14 5 2.25 + // 18 0.75 7.25 + // 23 42.5 15.5 const auto& engine = get_test_engine(); @@ -93,7 +92,6 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in2x2x1x1_nopad) { } } - TEST(deconvolution_f32_fw_gpu, no_bias_basic_wsiz2x2_in2x2x1x1_nopad) { // Filter : 2x2 // Input : 2x2 @@ -108,12 +106,12 @@ TEST(deconvolution_f32_fw_gpu, no_bias_basic_wsiz2x2_in2x2x1x1_nopad) { // 3.5 1.5 // // no bias - // + // // // Output: - // -14 5 2.25 - // 18 0.75 7.25 - // 23 42.5 15.5 + // -14 5 2.25 + // 18 0.75 7.25 + // 23 42.5 15.5 const auto& engine = get_test_engine(); @@ -152,7 +150,6 @@ TEST(deconvolution_f32_fw_gpu, no_bias_basic_wsiz2x2_in2x2x1x1_nopad) { } } - TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in2x2x1x1_nopad_bfyx) { // Filter : 2x2 // Input : 2x2 // Output : 3x3 @@ -169,9 +166,9 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in2x2x1x1_nopad_bfyx) { // Filt // 2 // // Output: - // -14 5 2.25 - // 18 0.75 7.25 - // 23 42.5 15.5 + // -14 5 2.25 + // 18 0.75 7.25 + // 23 42.5 15.5 const auto& engine = get_test_engine(); @@ -231,7 +228,7 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in2x2x1x1_pad1) { // 2 // // Output: - // 0.75 + // 0.75 const auto& engine = get_test_engine(); @@ -282,7 +279,7 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in2x2x1x1_stride2_nopad) { // 1 // // Output: - // 0.75 + // 0.75 const auto& engine = get_test_engine(); @@ -471,8 +468,8 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2x2_in2x2x1x1_stride2_pad1) { // 1 5 // // Output: - // f0: -3 4.5 - // f0: 13 -17 + // f0: -3 4.5 + // f0: 13 -17 // f1: 1 8.5 // f1: 17 - 13 @@ -480,7 +477,7 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2x2_in2x2x1x1_stride2_pad1) { auto input = memory::allocate(engine, { data_types::f32, format::yxfb, { 1, 1, 2, 2 } }); auto weights = memory::allocate(engine, { data_types::f32, format::yxfb, { 2, 1, 2, 2 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 2, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 2, 1, 1 } }); set_values(input, { 8.f, 0.5f, 6.f, 9.f }); set_values(weights, { -2.f, -2.f, 2.f, 2.f, 7.f, 7.f, -0.5f, -0.5f }); @@ -661,8 +658,8 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2x2_in2x2x1x1_stride2_pad1_input_padd // 1 5 // // Output: - // f0: -3 4.5 - // f0: 13 -17 + // f0: -3 4.5 + // f0: 13 -17 // f1: 1 8.5 // f1: 17 - 13 @@ -670,7 +667,7 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2x2_in2x2x1x1_stride2_pad1_input_padd auto input = memory::allocate(engine, { data_types::f32, format::yxfb,{ 1, 1, 2, 2 } }); auto weights = memory::allocate(engine, { data_types::f32, format::yxfb,{ 2, 1, 2, 2 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); set_values(input, { 8.f, 0.5f, 6.f, 9.f }); set_values(weights, { -2.f, -2.f, 2.f, 2.f, 7.f, 7.f, -0.5f, -0.5f }); @@ -913,7 +910,7 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in1x2x2x2_bfyx_stride2_pad1_group2) auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 2, 2 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 2, 2 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); set_values(input, { 8.f, 0.5f, 6.f, 9.f, 1.f, 3.f, 2.f, 4.f }); set_values(weights, { @@ -955,11 +952,11 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in1x2x2x2_bfyx_stride2_pad1_split2_ // Test for depthwise separable optimization, there are 16 weights and biases (split 16) // data is similar as in basic_wsiz2x2_in1x2x2x2_bfyx_stride2_pad1_split2 - const auto& engine = get_test_engine(); + const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 16, 2, 2 } }); - set_values(input, - { 8.f, 0.5f, 6.f, 9.f, 1.f, 3.f, 2.f, 4.f, + set_values(input, + { 8.f, 0.5f, 6.f, 9.f, 1.f, 3.f, 2.f, 4.f, 8.f, 0.5f, 6.f, 9.f, 1.f, 3.f, 2.f, 4.f, 8.f, 0.5f, 6.f, 9.f, 1.f, 3.f, 2.f, 4.f, 8.f, 0.5f, 6.f, 9.f, 1.f, 3.f, 2.f, 4.f, @@ -1056,11 +1053,11 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in1x2x2x2_bfyx_stride2_pad1_group16 std::vector weights_vec; std::vector bias_vec; - + auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 16, 1, 2, 2 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 16, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 16, 1, 1 } }); - set_values(weights, + set_values(weights, { -2.f, 2.f, 7.f, -0.5f, -4.f, 1.f, -9.f, -7.f, @@ -1085,7 +1082,7 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in1x2x2x2_bfyx_stride2_pad1_group16 data("weights", weights), data("bias", biases) ); - + topology.add(deconvolution("deconv", "input", { "weights" }, { "bias" }, 16, { 1, 1, 2, 2 }, { 0, 0, -1, -1 })); network network(engine, topology); @@ -1142,9 +1139,9 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in1x2x2x2_bfyx_stride2_pad1_split2_ for (uint32_t i = 0; i < 8; i++) { auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 2, 2 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); auto weights2 = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 1, 2, 2 } }); - auto biases2 = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 2, 1 } }); + auto biases2 = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 2, 1, 1 } }); set_values(weights, { -2.f, 2.f, 7.f, -0.5f, -2.f, 2.f, 7.f, -0.5f }); set_values(biases, { 1.0f, 1.0f }); @@ -1221,10 +1218,10 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in1x2x2x2_bfyx_stride2_pad1_group16 std::vector weights_vec; std::vector bias_vec; - + auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 32, 1, 2, 2 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 32, 1 } }); - + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 32, 1, 1 } }); + set_values(weights, { -2.f, 2.f, 7.f, -0.5f, -2.f, 2.f, 7.f, -0.5f, @@ -1288,7 +1285,6 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in1x2x2x2_bfyx_stride2_pad1_group16 } } - TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in1x6x1x1_bfyx_stride2_pad1_split2_ofm3) { // Filter : 1x1 // Stride : 1x1 @@ -1318,7 +1314,7 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in1x6x1x1_bfyx_stride2_pad1_split2_ // -1 2.5 2 // // Output: - // -1.5 + // -1.5 // 8 // 7.75 // @@ -1330,9 +1326,9 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in1x6x1x1_bfyx_stride2_pad1_split2_ auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 4, 1, 1 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 3, 2, 1, 1 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 3, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 3, 1, 1 } }); auto weights2 = memory::allocate(engine, { data_types::f32, format::bfyx,{ 3, 2, 1, 1 } }); - auto biases2 = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 3, 1 } }); + auto biases2 = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 3, 1, 1 } }); set_values(input, { 1.5f, 0.5f, 2.0f, -1.0f @@ -1378,7 +1374,7 @@ TEST(deconvolution_f32_fw_gpu, basic_wsiz2x2_in1x6x1x1_bfyx_stride2_pad1_group2_ auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 4, 1, 1 } }); auto weights = memory::allocate(engine, { data_types::f32, format::bfyx,{ 6, 2, 1, 1 } }); - auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 6, 1 } }); + auto biases = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 6, 1, 1 } }); set_values(input, { 1.5f, 0.5f, 2.0f, -1.0f diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/depth_concatenate_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/depth_concatenate_gpu_test.cpp index 3462ca1..6cb0ec0 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/depth_concatenate_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/depth_concatenate_gpu_test.cpp @@ -17,20 +17,20 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/concatenation.hpp" -#include "api/CPP/convolution.hpp" -#include "api/CPP/data.hpp" -#include "api/CPP/eltwise.hpp" -#include "api/CPP/fully_connected.hpp" -#include "api/CPP/pooling.hpp" -#include "api/CPP/crop.hpp" -#include "api/CPP/upsampling.hpp" -#include "api/CPP/reshape.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/concatenation.hpp" +#include "api/convolution.hpp" +#include "api/data.hpp" +#include "api/eltwise.hpp" +#include "api/fully_connected.hpp" +#include "api/pooling.hpp" +#include "api/crop.hpp" +#include "api/upsampling.hpp" +#include "api/reshape.hpp" +#include +#include +#include #include "test_utils/test_utils.h" using namespace cldnn; @@ -266,10 +266,10 @@ TEST(concatenate_f32_gpu, test_concatenation_of_pool_and_unpool) { {1, 1, 2, 1}, /*kernel*/ {1, 1, 1, 1} /*stride*/ )); - topology.add(upsampling("unpool1", "input1", 1, 0, upsampling_sample_type::nearest)); + topology.add(upsampling("unpool1", "input1", tensor(1, 1, 2, 2), 0, upsampling_sample_type::nearest)); topology.add(concatenation("concat1", {"pool1", "unpool1"}, cldnn::concatenation::along_x)); - topology.add(data("weights", weights)), - topology.add(convolution("conv", "concat1", {"weights"})); + topology.add(data("weights", weights)); + topology.add(convolution("conv", "concat1", {"weights"})); cldnn::build_options options; options.set_option(cldnn::build_option::optimize_data(true)); @@ -297,14 +297,14 @@ TEST(depth_concatenate_f32_gpu, test03_cascade_concat_opt) { topology topology; topology.add(input_layout("input1", input1.get_layout())); - topology.add(activation("relu1", "input1", activation_relu)); - topology.add(activation("relu2", "relu1", activation_sqrt)); + topology.add(activation("relu1", "input1", activation_func::relu)); + topology.add(activation("relu2", "relu1", activation_func::sqrt)); topology.add(concatenation("depth1", {"relu2", "relu1"}, concatenation::along_f)); - topology.add(activation("relu3", "depth1", activation_sqrt)); + topology.add(activation("relu3", "depth1", activation_func::sqrt)); topology.add(concatenation("depth2", {"relu3", "depth1"}, concatenation::along_f)); - topology.add(activation("relu4", "depth2", activation_sqrt)); + topology.add(activation("relu4", "depth2", activation_func::sqrt)); topology.add(concatenation("depth3", {"relu4", "depth2"}, concatenation::along_f)); - topology.add(activation("relu5", "depth3", activation_relu)); + topology.add(activation("relu5", "depth3", activation_func::relu)); cldnn::build_options options; options.set_option(cldnn::build_option::optimize_data(true)); @@ -356,7 +356,7 @@ TEST(depth_concatenate_f32_gpu, test04_fused_relu) { topology.add(input_layout("input1", input1.get_layout())); topology.add(input_layout("input2", input2.get_layout())); topology.add(concatenation("depth1", {"input1", "input2"}, concatenation::along_f)); - topology.add(activation("relu1", "depth1", activation_relu)); + topology.add(activation("relu1", "depth1", activation_func::relu)); cldnn::build_options options; options.set_option(cldnn::build_option::optimize_data(true)); @@ -553,7 +553,6 @@ TEST(depth_concatenate_f32_gpu, concat_with_reshape_input) { } } - TEST(depth_concatenate_i32_gpu, optimize_data01) { const auto& engine = get_test_engine(); build_options build_opt; @@ -893,23 +892,21 @@ public: delete generic_params; } - for (auto layer_params : all_layer_params) { - delete layer_params; - } + all_layer_params.clear(); } - static std::vector generate_specific_test_params(int i) { - std::vector all_layer_params; + static std::vector> generate_specific_test_params(int i) { + std::vector> all_layer_params; switch (i) { case 1: - all_layer_params.push_back(new concatenation("depth_concatenate", {"input0"}, concatenation::along_f)); + all_layer_params.emplace_back(new concatenation("depth_concatenate", {"input0"}, concatenation::along_f)); break; case 2: - all_layer_params.push_back(new concatenation("depth_concatenate", {"input0", "input1"}, concatenation::along_f)); + all_layer_params.emplace_back(new concatenation("depth_concatenate", {"input0", "input1"}, concatenation::along_f)); break; case 3: - all_layer_params.push_back(new concatenation("depth_concatenate", {"input0", "input1", "input2"}, concatenation::along_f)); + all_layer_params.emplace_back(new concatenation("depth_concatenate", {"input0", "input1", "input2"}, concatenation::along_f)); break; default: assert(0); @@ -974,8 +971,8 @@ public: return all_generic_params; } - static std::vector> generate_all_test_params() { - std::vector> res; + static std::vector>> generate_all_test_params() { + std::vector>> res; for (int i = 1; i <= 3; ++i) { auto tpv = generate_generic_test_params(i); @@ -1063,7 +1060,7 @@ public: } } - static std::string custom_param_name(const ::testing::TestParamInfo>& info) { + static std::string custom_param_name(const ::testing::TestParamInfo>>& info) { std::stringstream res; const auto& p = std::get<0>(info.param); @@ -1089,10 +1086,10 @@ public: private: static std::vector all_generic_params; - static std::vector all_layer_params; + static std::vector> all_layer_params; }; -std::vector depth_concatenate_test::all_layer_params = {}; +std::vector> depth_concatenate_test::all_layer_params = {}; std::vector depth_concatenate_test::all_generic_params = {}; TEST_P(depth_concatenate_test, DEPTHCONCATENATE) { diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/depth_to_space_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/depth_to_space_gpu_test.cpp index 49e8dcb..cf435ea 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/depth_to_space_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/depth_to_space_gpu_test.cpp @@ -12,16 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. - - /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/detection_output_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/detection_output_test.cpp index 3252224..99f115d 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/detection_output_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/detection_output_test.cpp @@ -16,12 +16,12 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/detection_output.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/detection_output.hpp" +#include +#include +#include #include "test_utils/test_utils.h" namespace cldnn @@ -834,7 +834,6 @@ public: typedef ::testing::Types detection_output_test_types; TYPED_TEST_CASE(detection_output_test, detection_output_test_types); - TYPED_TEST(detection_output_test, test_setup_basic) { this->setup_basic(false); diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/eltwise_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/eltwise_gpu_test.cpp index ce36f07..3493fa4 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/eltwise_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/eltwise_gpu_test.cpp @@ -16,14 +16,14 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/eltwise.hpp" -#include -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/eltwise.hpp" +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" namespace cldnn @@ -109,16 +109,21 @@ void generic_eltwise_test(cldnn::format test_input_fmt, int input_b, int input_f topology.add(input_layout("input1", input1.get_layout())); topology.add(input_layout("input2", input2.get_layout())); topology.add(reorder("reorder1", "input1", input1.get_layout().with_padding(padding{{ 0, 0, input_padding_x, input_padding_y }, 0 }))); - topology.add(eltwise("eltwise", {"reorder1", "input2"}, mode, relu, slope, padding{ { 0, 0, output_padding_x, output_padding_y }, 0 })); - + topology.add(eltwise("eltwise", {"reorder1", "input2"}, mode, padding{ { 0, 0, output_padding_x, output_padding_y }, 0 })); + primitive_id out_id = "eltwise"; + if (relu) + { + topology.add(activation("out", out_id, activation_func::relu, { slope, 0.0f })); + out_id = "out"; + } network network(engine, topology); network.set_input_data("input1", input1); network.set_input_data("input2", input2); auto outputs = network.execute(); EXPECT_EQ(outputs.size(), size_t(1)); - EXPECT_EQ(outputs.begin()->first, "eltwise"); + EXPECT_EQ(outputs.begin()->first, out_id); - auto output_memory = outputs.at("eltwise").get_memory(); + auto output_memory = outputs.at(out_id).get_memory(); auto output_layout = output_memory.get_layout(); auto output_ptr = output_memory.pointer(); @@ -1120,7 +1125,6 @@ TEST(eltwise_gpu_f32, add_in2x2x2x2_broadcast_x) { -2.f, 6.5f, -0.5f, -2.5f }); - set_values(input2, { 1.f, 0.f, @@ -1190,7 +1194,6 @@ TEST(eltwise_gpu_f32, add_in2x2x2x2_broadcast_y) { -2.f, 6.5f, -0.5f, -2.5f }); - set_values(input2, { 1.f, 0.f, 2.f, 0.f, @@ -1254,7 +1257,6 @@ TEST(eltwise_gpu_f32, add_in2x2x2x2_broadcast_batch) { -2.f, 6.5f, -0.5f, -2.5f }); - set_values(input2, { 1.f, 0.f, @@ -1379,7 +1381,6 @@ TEST(eltwise_gpu_f32, pow_in2x2x2x2_broadcast_all) { 13.f, 14.f, 15.f, 16.f }); - set_values(input2, { 2.0f }); network network(engine, topology); @@ -1439,7 +1440,6 @@ TEST(eltwise_gpu_f32, add_basic_in2x2x2x2_broadcast_2_inputs_same_dim) { -2.f, 6.5f, -0.5f, -2.5f }); - set_values(input2, { 1.f, 0.f, @@ -1518,7 +1518,6 @@ TEST(eltwise_gpu_f32, add_basic_in2x2x2x2_broadcast_2_inputs_diff_dim) { -2.f, 6.5f, -0.5f, -2.5f }); - set_values(input2, { 1.f, 0.f, @@ -1779,7 +1778,6 @@ TEST(eltwise_gpu_int, basic_in4x4x4x4) { expected = std::fmod(input_1_vec[i], input_2_vec[i]); } - EXPECT_TRUE(are_equal(std::floor(expected), output_ptr[i])); } } @@ -1923,7 +1921,6 @@ TEST(eltwise_gpu_f32, prod_basic_in4x4x4x4) { // f1: b0: 119 80 b1: 96 -18.75 // - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::yxfb, { 2, 2, 2, 2 } }); auto input2 = memory::allocate(engine, { data_types::f32, format::yxfb, { 2, 2, 2, 2 } }); @@ -2323,7 +2320,6 @@ TEST(eltwise_gpu_f32, max_3inputs_in4x4x4x4_input_padding) { } } - TEST(eltwise_gpu_f32, stride_test_2x2) { // Input : 2x2x2x2 // Input2 : 2x2x4x4 @@ -2496,7 +2492,6 @@ TEST(eltwise_gpu_f16, fs_b_yx_fsv32_basic) //F2 // 1221 1222 2221 2222 - tensor input_tensor(2, 2, 2, 2); auto fp16_bfyx_2x2x2x2_input = { @@ -2721,7 +2716,7 @@ TEST(eltwise_gpu_f16, bfyx_and_fs_b_yx_fsv32_output_padding) topology golden_topology; golden_topology.add(input_layout("input1", input1.get_layout())); golden_topology.add(input_layout("input2", input2.get_layout())); - golden_topology.add(eltwise("eltwise", "input1", "input2", eltwise_mode::sum,false,0.0f, padding{ {0,0,5,10} , 0 })); + golden_topology.add(eltwise("eltwise", "input1", "input2", eltwise_mode::sum, padding{ {0,0,5,10} , 0 })); network golden_network(engine, golden_topology); golden_network.set_input_data("input1", input1); @@ -2737,7 +2732,7 @@ TEST(eltwise_gpu_f16, bfyx_and_fs_b_yx_fsv32_output_padding) FS_B_YX_FSV32_OUTPUT_topology.add(input_layout("input2", input2.get_layout())); FS_B_YX_FSV32_OUTPUT_topology.add(reorder("reorder1", "input1", layout(data_types::f16, format::fs_b_yx_fsv32, input_tensor))); FS_B_YX_FSV32_OUTPUT_topology.add(reorder("reorder2", "input2", layout(data_types::f16, format::byxf, input_tensor))); - FS_B_YX_FSV32_OUTPUT_topology.add(eltwise("eltwise", "reorder1", "reorder2", eltwise_mode::sum,false,0.0f, padding{ {0,0,5,10} , 0 })); + FS_B_YX_FSV32_OUTPUT_topology.add(eltwise("eltwise", "reorder1", "reorder2", eltwise_mode::sum, padding{ {0,0,5,10} , 0 })); FS_B_YX_FSV32_OUTPUT_topology.add(reorder("reorderOutput", "eltwise", layout(data_types::f16, format::bfyx, input_tensor, padding{ {0,0,5,10} , 0 }))); network FS_B_YX_FSV32_OUTPUT_network(engine, FS_B_YX_FSV32_OUTPUT_topology); @@ -2754,7 +2749,7 @@ TEST(eltwise_gpu_f16, bfyx_and_fs_b_yx_fsv32_output_padding) BYXF_OUTPUT_topology.add(input_layout("input2", input2.get_layout())); BYXF_OUTPUT_topology.add(reorder("reorder1", "input1", layout(data_types::f16, format::byxf, input_tensor))); BYXF_OUTPUT_topology.add(reorder("reorder2", "input2", layout(data_types::f16, format::fs_b_yx_fsv32, input_tensor))); - BYXF_OUTPUT_topology.add(eltwise("eltwise", "reorder1", "reorder2", eltwise_mode::sum, false, 0.0f, padding{ {0,0,5,10} , 0 })); + BYXF_OUTPUT_topology.add(eltwise("eltwise", "reorder1", "reorder2", eltwise_mode::sum, padding{ {0,0,5,10} , 0 })); BYXF_OUTPUT_topology.add(reorder("reorderOutput", "eltwise", layout(data_types::f16, format::bfyx, input_tensor, padding{ {0,0,5,10} , 0 }))); network BYXF_OUTPUT_network(engine, BYXF_OUTPUT_topology); @@ -2937,7 +2932,7 @@ void generic_eltwise_bool_test(cldnn::format test_input_fmt, int input_b, int in topology.add(input_layout("input1", input1.get_layout())); topology.add(input_layout("input2", input2.get_layout())); topology.add(reorder("reorder1", "input1", input1.get_layout().with_padding(padding{{ 0, 0, input_padding_x, input_padding_y }, 0 }))); - topology.add(eltwise("eltwise", {"reorder1", "input2"}, mode, false, 0.f, padding{ { 0, 0, output_padding_x, output_padding_y }, 0 })); + topology.add(eltwise("eltwise", {"reorder1", "input2"}, mode, padding{ { 0, 0, output_padding_x, output_padding_y }, 0 })); network network(engine, topology); network.set_input_data("input1", input1); @@ -3025,7 +3020,6 @@ TEST(eltwise_gpu_bool, eltwise_or) { run_eltwise_bool_generic_test(cldnn::eltwise_mode::logic_or); } - void run_eltwise_generic_test(cldnn::eltwise_mode mode) { cldnn::format test_inputs_fmt = cldnn::format::bfyx; @@ -3058,7 +3052,6 @@ TEST(eltwise_gpu, eltwise_mod) { run_eltwise_generic_test(cldnn::eltwise_mode::mod); } - TEST(eltwise_gpu, b_fs_yx_fsv4_w_callib) { int B_array[] = { 1, 4, 16, 32, 0 }; // Batch int F_array[] = { 256, 512, 1024, 2048, 0 }; // Features @@ -3114,15 +3107,15 @@ TEST(eltwise_gpu, b_fs_yx_fsv4_w_callib) { { topology topology; - auto eltw = eltwise("eltw_GOLD", + auto eltw = eltwise("eltw_GOLD_no_relu", "input1", "input2", "callib", - eltwise_mode::sum, true); - + eltwise_mode::sum); + auto actv = activation("eltw_GOLD", eltw, activation_func::relu); // Create a topology topology.add(input_layout("input1", input1.get_layout()), input_layout("input2", input2.get_layout()), - eltw); + eltw, actv); topology.add(data("callib", callib)); @@ -3158,14 +3151,15 @@ TEST(eltwise_gpu, b_fs_yx_fsv4_w_callib) { format::b_fs_yx_fsv4, { in_B, in_F, in_X, in_Y }))); - auto eltw = eltwise("eltw_IMAD", + auto eltw = eltwise("eltw_IMAD_no_relu", "reorder1_Swizzelled", "reorder2_Swizzelled", "callib", - eltwise_mode::sum, true); + eltwise_mode::sum); + auto actv = activation("eltw_IMAD", eltw, activation_func::relu); topology.add(input_layout("input1", input1.get_layout()), input_layout("input2", input2.get_layout()), - eltw); + eltw, actv); topology.add(data("callib", callib)); @@ -3257,15 +3251,16 @@ TEST(eltwise_gpu, b_fs_yx_fsv4_wo_callib) { { topology topology; - auto eltw = eltwise("eltw_GOLD", + auto eltw = eltwise("eltw_GOLD_no_relu", { "input1", "input2", "input3" }, - mode[i], true); + mode[i]); + auto actv = activation("eltw_GOLD", eltw, activation_func::relu); // Create a topology topology.add(input_layout("input1", input1.get_layout()), input_layout("input2", input2.get_layout()), input_layout("input3", input3.get_layout()), - eltw); + eltw, actv); // Network processing network network(engine, topology); @@ -3305,16 +3300,16 @@ TEST(eltwise_gpu, b_fs_yx_fsv4_wo_callib) { format::b_fs_yx_fsv4, { in_B, in_F, in_X, in_Y }))); - auto eltw = eltwise("eltw_IMAD", + auto eltw = eltwise("eltw_IMAD_no_relu", { "reorder1_Swizzelled", "reorder2_Swizzelled", "reorder3_Swizzelled" }, - mode[i], true); - + mode[i]); + auto actv = activation("eltw_IMAD", eltw, activation_func::relu); topology.add(input_layout("input1", input1.get_layout()), input_layout("input2", input2.get_layout()), input_layout("input3", input3.get_layout()), - eltw); + eltw, actv); // Back reordering (a-ka unswizzelling) output from MMAD/IMAD pooling topology.add(reorder("reorder_UnSwizzelled", diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/embed_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/embed_gpu_test.cpp index 1ed4515..fb4e0aa 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/embed_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/embed_gpu_test.cpp @@ -16,23 +16,21 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/embed.hpp" -#include -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/embed.hpp" +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" - #include using namespace cldnn; using namespace tests; - TEST(embed_gpu, seq3num4) { // Input : 1x1x1x3 // Weights: 4x1x3x1 diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/fully_connected_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/fully_connected_gpu_test.cpp index d5df255..5ed3129 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/fully_connected_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/fully_connected_gpu_test.cpp @@ -16,15 +16,15 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/fully_connected.hpp" -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/fully_connected.hpp" +#include +#include +#include +#include #include "test_utils/test_utils.h" -#include +#include #include "instrumentation.h" #include @@ -94,21 +94,26 @@ void generic_fully_connected_test(cldnn::format test_input_fmt, cldnn::format te set_values(weights, weights_rnd_vec); set_values(bias, bias_rnd_vec); + primitive_id out_id = "fully_connected"; topology topology( input_layout("input", input.get_layout()), data("weights", weights), data("bias", bias), - fully_connected("fully_connected", "input", "weights", "bias", relu, slope) + fully_connected(out_id, "input", "weights", "bias") ); - + if (relu) + { + topology.add(activation("out", out_id, activation_func::relu, { slope, 0.0f })); + out_id = "out"; + } network network(engine, topology); network.set_input_data("input", input); auto outputs = network.execute(); EXPECT_EQ(outputs.size(), size_t(1)); - EXPECT_EQ(outputs.begin()->first, "fully_connected"); + EXPECT_EQ(outputs.begin()->first, out_id); - auto output_memory = outputs.at("fully_connected").get_memory(); + auto output_memory = outputs.at(out_id).get_memory(); auto output_layout = output_memory.get_layout(); auto output_ptr = output_memory.pointer(); @@ -236,7 +241,6 @@ TEST(fully_connected_gpu, no_biases) { EXPECT_EQ(3.0f, output_ptr[3]); } - TEST(fully_connected_gpu, no_biases_int8) { // Input : 3x1 // Output : 4x1 @@ -297,7 +301,6 @@ TEST(fully_connected_gpu, no_biases_int8) { EXPECT_EQ(-52.0f, output_ptr[3]); } - TEST(fully_connected_gpu, xb_f32_batch_1) { // Input : 3x1 // Output : 4x1 @@ -479,7 +482,6 @@ TEST(fully_connected_gpu, x_f32) { EXPECT_EQ(7.00f, output_ptr[3]); } - TEST(fully_connected_gpu, yxfn_f32) { // Input : 1x2x1x2 - 1 batch 2 feature maps of size 2x1 // Output : 2x1 - 2 batches 1 neuron each @@ -574,7 +576,8 @@ TEST(fully_connected_gpu, xb_f32_batch_1_relu) { input_layout("input", input_prim.get_layout()), data("weights", weights_prim), data("bias", bias_prim), - fully_connected("full_con_prim", "input", "weights", "bias", true, 0) + fully_connected("full_con_prim", "input", "weights", "bias"), + activation("out", "full_con_prim", activation_func::relu) ); network network(engine, topology); @@ -582,7 +585,7 @@ TEST(fully_connected_gpu, xb_f32_batch_1_relu) { auto outputs = network.execute(); EXPECT_EQ(outputs.size(), size_t(1)); - EXPECT_EQ(outputs.begin()->first, "full_con_prim"); + EXPECT_EQ(outputs.begin()->first, "out"); auto output_prim = outputs.begin()->second.get_memory(); @@ -635,7 +638,8 @@ TEST(fully_connected_gpu, xb_f32_batch_2_relu) { input_layout("input", input_prim.get_layout()), data("weights", weights_prim), data("bias", bias_prim), - fully_connected("full_con_prim", "input", "weights", "bias", true, 0) + fully_connected("full_con_prim", "input", "weights", "bias"), + activation("out", "full_con_prim", activation_func::relu) ); network network(engine, topology); @@ -643,7 +647,7 @@ TEST(fully_connected_gpu, xb_f32_batch_2_relu) { auto outputs = network.execute(); EXPECT_EQ(outputs.size(), size_t(1)); - EXPECT_EQ(outputs.begin()->first, "full_con_prim"); + EXPECT_EQ(outputs.begin()->first, "out"); auto output_prim = outputs.begin()->second.get_memory(); @@ -697,7 +701,8 @@ TEST(fully_connected_gpu, x_f32_relu) { input_layout("input", input_prim.get_layout()), data("weights", weights_prim), data("bias", bias_prim), - fully_connected("full_con_prim", "input", "weights", "bias", true, 0) + fully_connected("full_con_prim", "input", "weights", "bias"), + activation("out", "full_con_prim", activation_func::relu) ); network network(engine, topology); @@ -705,7 +710,7 @@ TEST(fully_connected_gpu, x_f32_relu) { auto outputs = network.execute(); EXPECT_EQ(outputs.size(), size_t(1)); - EXPECT_EQ(outputs.begin()->first, "full_con_prim"); + EXPECT_EQ(outputs.begin()->first, "out"); auto output_prim = outputs.begin()->second.get_memory(); @@ -756,7 +761,8 @@ TEST(fully_connected_gpu, x_f32_relu_with_negative_slope) { input_layout("input", input_prim.get_layout()), data("weights", weights_prim), data("bias", bias_prim), - fully_connected("full_con_prim", "input", "weights", "bias", true, 0.1f) + fully_connected("full_con_prim", "input", "weights", "bias"), + activation("out", "full_con_prim", activation_func::relu_negative_slope, { 0.1f }) ); network network(engine, topology); @@ -764,7 +770,7 @@ TEST(fully_connected_gpu, x_f32_relu_with_negative_slope) { auto outputs = network.execute(); EXPECT_EQ(outputs.size(), size_t(1)); - EXPECT_EQ(outputs.begin()->first, "full_con_prim"); + EXPECT_EQ(outputs.begin()->first, "out"); auto output_prim = outputs.begin()->second.get_memory(); @@ -899,7 +905,7 @@ TEST(fully_connected_gpu, b_fs_yx_fsv4) } } -TEST(fully_connected_gpu, fs_byx_fsv32_b12) +TEST(fully_connected_gpu, DISABLED_fs_byx_fsv32_b12) { const auto& engine = get_test_engine(); @@ -942,7 +948,8 @@ TEST(fully_connected_gpu, fs_byx_fsv32_b12) data("weights", weights_prim), data("bias", bias_prim), reorder("input_fsv", "input", {data_types::f16, format::fs_b_yx_fsv32, { batch_num, input_f, input_y, input_x } }), - fully_connected("fc", "input_fsv", "weights", "bias", true) + fully_connected("fc", "input_fsv", "weights", "bias"), + activation("out", "fc", activation_func::relu) ); // Set data optimization to allow weights reordering to optimal format @@ -954,7 +961,7 @@ TEST(fully_connected_gpu, fs_byx_fsv32_b12) auto outputs = network.execute(); - auto output_prim = outputs.at("fc").get_memory(); + auto output_prim = outputs.at("out").get_memory(); auto output_ptr = output_prim.pointer(); for (size_t bi = 0; bi < batch_num; ++bi) @@ -974,7 +981,7 @@ TEST(fully_connected_gpu, fs_byx_fsv32_b12) } } -TEST(fully_connected_gpu, fs_byx_fsv32_b34) +TEST(fully_connected_gpu, DISABLED_fs_byx_fsv32_b34) { const auto& engine = get_test_engine(); @@ -1017,7 +1024,8 @@ TEST(fully_connected_gpu, fs_byx_fsv32_b34) data("weights", weights_prim), data("bias", bias_prim), reorder("input_fsv", "input", { data_types::f16, format::fs_b_yx_fsv32, { batch_num, input_f, input_y, input_x } }), - fully_connected("fc", "input_fsv", "weights", "bias", true) + fully_connected("fc", "input_fsv", "weights", "bias"), + activation("out", "fc", activation_func::relu) ); // Set data optimization to allow weights reordering to optimal format @@ -1029,7 +1037,7 @@ TEST(fully_connected_gpu, fs_byx_fsv32_b34) auto outputs = network.execute(); - auto output_prim = outputs.at("fc").get_memory(); + auto output_prim = outputs.at("out").get_memory(); auto output_ptr = output_prim.pointer(); for (size_t bi = 0; bi < batch_num; ++bi) diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/fully_connected_grad_input_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/fully_connected_grad_input_gpu_test.cpp index 2737576..89e0f29 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/fully_connected_grad_input_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/fully_connected_grad_input_gpu_test.cpp @@ -17,13 +17,13 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/fully_connected_grad_input.hpp" -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/fully_connected_grad_input.hpp" +#include +#include +#include +#include #include "test_utils/test_utils.h" using namespace cldnn; @@ -50,7 +50,6 @@ TEST(fully_connected_grad_input_gpu, basic_bfyx) { // Output: // -1.125 5.625 10.125 - const auto& engine = get_test_engine(); auto input_grad = memory::allocate(engine, { data_types::f32, format::bfyx,{ 1, 1, 4, 1 } }); diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/fully_connected_grad_weights_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/fully_connected_grad_weights_gpu_test.cpp index b470bda..49c7610 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/fully_connected_grad_weights_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/fully_connected_grad_weights_gpu_test.cpp @@ -17,17 +17,17 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/fully_connected_grad_weights.hpp" -#include "api/CPP/fully_connected.hpp" -#include "api/CPP/fully_connected_grad_input.hpp" -#include "api/CPP/reorder.hpp" -#include -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/fully_connected_grad_weights.hpp" +#include "api/fully_connected.hpp" +#include "api/fully_connected_grad_input.hpp" +#include "api/reorder.hpp" +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" using namespace cldnn; diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/fused_conv_eltwise_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/fused_conv_eltwise_gpu_test.cpp index 7ba200f..a2603af 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/fused_conv_eltwise_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/fused_conv_eltwise_gpu_test.cpp @@ -16,18 +16,18 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/convolution.hpp" -#include "api/CPP/eltwise.hpp" -#include "api/CPP/reorder.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/convolution.hpp" +#include "api/eltwise.hpp" +#include "api/reorder.hpp" +#include +#include +#include #include "test_utils/test_utils.h" -#include +#include -#include +#include #include #include @@ -76,7 +76,6 @@ TEST(fused_conv_eltwise, basic_0) EXPECT_EQ(out_layout.size.spatial[1], 5); } - TEST(fused_conv_eltwise, dont_fuse_if_conv_elt_are_outputs) { const auto& engine = get_test_engine(); @@ -173,7 +172,7 @@ protected: auto input_shape = tensor(1, n_features, 4, 1); auto weights_shape = tensor(n_features, n_features, 3, 1); - auto biases_shape = tensor(1, 1, n_features, 1); + auto biases_shape = tensor(1, n_features, 1, 1); auto sum_input_shape = tensor(1, n_features, 2, 1); auto input = memory::allocate( diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/fusings_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/fusings_gpu_test.cpp new file mode 100644 index 0000000..2c3e76f --- /dev/null +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/fusings_gpu_test.cpp @@ -0,0 +1,440 @@ +/* +// 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 "api/memory.hpp" +#include "api/input_layout.hpp" +#include "api/convolution.hpp" +#include "api/quantize.hpp" +#include "api/topology.hpp" +#include "api/tensor.hpp" +#include "api/network.hpp" +#include "api/eltwise.hpp" +#include "api/fully_connected.hpp" +#include "api/binary_convolution.hpp" +#include "api/engine.hpp" +#include "api/data.hpp" + +#include "test_utils/test_utils.h" + +#include + +using namespace cldnn; +using namespace tests; + +struct bc_test_params { + tensor in_shape; + tensor out_shape; + tensor kernel; + tensor stride; + tensor pad; + tensor dilation; + uint32_t groups; + data_types data_type; + format input_format; + data_types weights_type; + format weights_format; + data_types default_type; + format default_format; + size_t expected_fused_primitives; + size_t expected_not_fused_primitives; +}; + +class BaseFusingTest : public ::testing::TestWithParam { +public: + cldnn::engine engine; + cldnn::topology topology; + cldnn::build_options bo_fused; + cldnn::build_options bo_not_fused; + + float tolerance = 0.0f; + + static const int min_random = -200; + static const int max_random = 200; + + void SetUp() override { + bo_fused.set_option(build_option::optimize_data(true)); + bo_not_fused.set_option(build_option::optimize_data(false)); + } + + void execute(bc_test_params& p) { + auto input_prim = get_mem(get_input_layout(p)); + network network_not_fused(this->engine, this->topology, bo_not_fused); + network network_fused(this->engine, this->topology, bo_fused); + network_fused.set_input_data("input", input_prim); + network_not_fused.set_input_data("input", input_prim); + + compare(network_not_fused, network_fused, p); + } + + void compare(const network& not_fused, const network& fused, bc_test_params& p) { + auto outputs_ref = not_fused.execute(); + auto outputs_fused = fused.execute(); + + ASSERT_EQ(fused.get_executed_primitives().size(), p.expected_fused_primitives); + ASSERT_EQ(not_fused.get_executed_primitives().size(), p.expected_not_fused_primitives); + ASSERT_EQ(outputs_ref.size(), outputs_fused.size()); + ASSERT_EQ(outputs_ref.size(), size_t(1)); + + auto output_not_fused_prim = outputs_ref.begin()->second.get_memory(); + auto output_fused_prim = outputs_fused.begin()->second.get_memory(); + if (output_not_fused_prim.get_layout().data_type == data_types::f32) { + auto ref = output_not_fused_prim.pointer(); + auto output_ptr = output_fused_prim.pointer(); + for (size_t i = 0; i < output_fused_prim.get_layout().count(); i++) { + ASSERT_NEAR(ref[i], output_ptr[i], tolerance) << "i = " << i; + } + } else { + auto ref = output_not_fused_prim.pointer(); + auto output_ptr = output_fused_prim.pointer(); + for (size_t i = 0; i < output_fused_prim.get_layout().count(); i++) { + ASSERT_NEAR(float16_to_float32(ref[i]), float16_to_float32(output_ptr[i]), tolerance) << "i = " << i; + } + } + } + + cldnn::memory get_mem(cldnn::layout l) { + auto prim = memory::allocate(engine, l); + tensor s = l.size; + if (l.data_type == data_types::bin) { + VF rnd_vec = generate_random_1d(s.count()/32, min_random, max_random); + set_values(prim, rnd_vec); + } else { + VVVVF rnd = generate_random_4d(s.batch[0], s.feature[0], s.spatial[1], s.spatial[0], + min_random, max_random); + VF rnd_vec = flatten_4d(format::bfyx, rnd); + set_values(prim, rnd_vec); + } + + return prim; + } + + cldnn::memory get_mem(cldnn::layout l, float fill_value) { + auto prim = memory::allocate(engine, l); + tensor s = l.size; + if (l.data_type == data_types::bin) { + VF rnd_vec(s.count()/32, static_cast(fill_value)); + set_values(prim, rnd_vec); + } else { + VF rnd_vec(s.count(), fill_value); + set_values(prim, rnd_vec); + } + + return prim; + } + + cldnn::memory get_mem(cldnn::layout l, int min, int max) { + auto prim = memory::allocate(engine, l); + tensor s = l.size; + if (l.data_type == data_types::f32) { + VF rnd_vec = generate_random_1d(s.count(), min, max); + set_values(prim, rnd_vec); + } else if (l.data_type == data_types::i8) { + VF rnd_vec = generate_random_1d(s.count(), min, max); + set_values(prim, rnd_vec); + } else if (l.data_type == data_types::bin) { + VF rnd_vec = generate_random_1d(s.count()/32, min, max); + set_values(prim, rnd_vec); + } + + return prim; + } + + layout get_input_layout(bc_test_params& p) { + auto pad = p.pad.negate(); + std::vector pad_ = { 0, 0, pad.spatial[0], pad.spatial[1] }; + return layout{p.data_type, p.input_format, p.in_shape, padding{pad_}}; + } + + layout get_output_layout(bc_test_params& p) { + return layout{p.data_type, p.input_format, p.out_shape}; + } + + layout get_weights_layout(bc_test_params& p) { + return layout{p.weights_type, p.weights_format, tensor{p.out_shape.feature[0], + static_cast(p.in_shape.feature[0] / p.groups), + p.kernel.spatial[0], p.kernel.spatial[1]}}; + } + + layout get_bias_layout(bc_test_params& p) { + return layout{p.data_type, p.default_format, tensor{1, p.out_shape.feature[0], 1, 1}}; + } + + layout get_per_channel_layout(bc_test_params& p) { + return layout{p.default_type, p.default_format, tensor{1, p.out_shape.feature[0], 1, 1}}; + } + layout get_single_element_layout(bc_test_params& p) { + return layout{p.default_type, p.default_format, tensor{1, 1, 1, 1}}; + } +}; + +#define CASE_CONV1 {1, 15, 4, 5}, {1, 30, 2, 3}, {1, 1, 3, 3}, tensor{1}, tensor{0}, tensor{1}, 1, data_types::f32, format::bfyx, data_types::f32, format::bfyx, data_types::f32, format::bfyx +#define CASE_CONV2 {1, 16, 4, 5}, {1, 32, 2, 3}, {1, 1, 3, 3}, tensor{1}, tensor{0}, tensor{1}, 1, data_types::f32, format::bfyx_f16, data_types::f32, format::o_i_yx_i16_o16, data_types::f32, format::bfyx +#define CASE_CONV3 {1, 16, 4, 5}, {1, 32, 4, 5}, {1, 1, 1, 1}, tensor{1}, tensor{0}, tensor{1}, 1, data_types::f32, format::bfyx_f16, data_types::f32, format::o_i_yx_i16_o16, data_types::f32, format::bfyx +#define CASE_CONV4 {1, 32, 4, 5}, {1, 32, 4, 5}, {1, 1, 3, 3}, tensor{1}, tensor{0, 0, -1, -1, 0, 0}, tensor{1}, 32, data_types::f32, format::bfyx_f16, data_types::f32, format::oiyx_o16, data_types::f32, format::bfyx + +#define CASE_BIN_CONV1 {1, 16, 4, 5}, {1, 16, 4, 5}, {1, 1, 3, 3}, tensor{1}, tensor{0, 0, -1, -1, 0, 0}, tensor{1}, 1, data_types::bin, format::b_fs_yx_32fp, data_types::bin, format::os_is_yx_osv32_isv32p, data_types::f32, format::bfyx +#define CASE_BIN_CONV2 {1, 16, 4, 5}, {1, 30, 4, 5}, {1, 1, 1, 1}, tensor{1}, tensor{0}, tensor{1}, 1, data_types::bin, format::b_fs_yx_32fp, data_types::bin, format::os_is_yx_osv32_isv32p, data_types::f32, format::bfyx +#define CASE_BIN_CONV3 {1, 184, 12, 21}, {1, 224, 12, 21}, {1, 1, 1, 1}, tensor{1}, tensor{0}, tensor{1}, 1, data_types::bin, format::b_fs_yx_32fp, data_types::bin, format::os_is_yx_osv32_isv32p, data_types::f32, format::bfyx + +class conv_activation : public BaseFusingTest {}; +TEST_P(conv_activation, basic) { + auto p = GetParam(); + topology.add(input_layout("input", get_input_layout(p)), + data("weights", get_mem(get_weights_layout(p))), + data("bias", get_mem(get_bias_layout(p))), + convolution("conv_prim", "input", {"weights"}, {"bias"}, p.groups, p.stride, p.pad, p.dilation), + activation("activation", "conv_prim", activation_func::abs), + reorder("reorder_bfyx", "activation", p.default_format, data_types::f32) + ); + + execute(p); +} + +INSTANTIATE_TEST_CASE_P(fusings_gpu, conv_activation, ::testing::ValuesIn(std::vector{ + bc_test_params{CASE_CONV1, 3, 4}, + bc_test_params{CASE_CONV2, 3, 4}, + bc_test_params{CASE_CONV3, 3, 4}, + bc_test_params{CASE_CONV4, 3, 4}, +}), ); + + +class conv_fp32_scale : public BaseFusingTest {}; +TEST_P(conv_fp32_scale, basic) { + auto p = GetParam(); + topology.add(input_layout("input", get_input_layout(p)), + data("weights", get_mem(get_weights_layout(p))), + data("bias", get_mem(get_bias_layout(p))), + data("scale_data", get_mem(get_per_channel_layout(p), 1.0f/p.kernel.count())), + convolution("conv_prim", "input", {"weights"}, {"bias"}, p.groups, p.stride, p.pad, p.dilation), + scale("scale", "conv_prim", "scale_data"), + reorder("reorder_bfyx", "scale", p.default_format, data_types::f32) + ); + + tolerance = 1e-5f; + execute(p); +} + +INSTANTIATE_TEST_CASE_P(fusings_gpu, conv_fp32_scale, + ::testing::ValuesIn(std::vector{ + bc_test_params{CASE_CONV1, 4, 4}, // doesn't support this fusing for now + bc_test_params{CASE_CONV2, 3, 4}, + bc_test_params{CASE_CONV3, 3, 4}, + bc_test_params{CASE_CONV4, 3, 4}, + }), ); + +class conv_fp32_prelu_eltwise : public BaseFusingTest {}; +TEST_P(conv_fp32_prelu_eltwise, basic) { + auto p = GetParam(); + topology.add(input_layout("input", get_input_layout(p)), + data("weights", get_mem(get_weights_layout(p))), + data("bias", get_mem(get_bias_layout(p))), + data("slope_data", get_mem(get_per_channel_layout(p))), + data("eltwise_data", get_mem(get_output_layout(p))), + convolution("conv_prim", "input", {"weights"}, {"bias"}, p.groups, p.stride, p.pad, p.dilation), + activation("activation", "conv_prim", "slope_data", activation_func::relu_negative_slope), + eltwise("eltwise", "activation", "eltwise_data", eltwise_mode::sum), + reorder("reorder_bfyx", "eltwise", p.default_format, data_types::f32) + ); + + tolerance = 1e-5f; + execute(p); +} + +INSTANTIATE_TEST_CASE_P(fusings_gpu, conv_fp32_prelu_eltwise, + ::testing::ValuesIn(std::vector{ + bc_test_params{CASE_CONV1, 5, 5}, // doesn't support this fusing for now + bc_test_params{CASE_CONV2, 3, 5}, + bc_test_params{CASE_CONV3, 3, 5}, + bc_test_params{CASE_CONV4, 3, 5}, + }), ); + +class conv_bin_activation : public BaseFusingTest {}; +TEST_P(conv_bin_activation, basic) { + auto p = GetParam(); + topology.add(input_layout("input", get_input_layout(p)), + data("weights", get_mem(get_weights_layout(p), -127, 127)), + binary_convolution("bin_conv_prim", "input", {"weights"}, p.stride, p.pad, p.dilation, p.out_shape, p.groups), + activation("activation", "bin_conv_prim", activation_func::relu), + reorder("reorder_bfyx", "activation", p.default_format, data_types::f32) + ); + + tolerance = 1e-5f; + execute(p); +} + +INSTANTIATE_TEST_CASE_P(fusings_gpu, conv_bin_activation, + ::testing::ValuesIn(std::vector{bc_test_params{CASE_BIN_CONV1, 3, 4}, + }), ); + +class conv_bin_scale_activation : public BaseFusingTest {}; +TEST_P(conv_bin_scale_activation, basic) { + auto p = GetParam(); + topology.add(input_layout("input", get_input_layout(p)), + data("weights", get_mem(get_weights_layout(p), -127, 127)), + data("scale_data", get_mem(get_per_channel_layout(p), 1.0f/p.kernel.count())), + binary_convolution("bin_conv_prim", "input", {"weights"}, p.stride, p.pad, p.dilation, p.out_shape, p.groups), + scale("scale", "bin_conv_prim", "scale_data"), + activation("activation", "scale", activation_func::relu), + reorder("reorder_bfyx", "activation", p.default_format, data_types::f32) + ); + tolerance = 1e-5f; + execute(p); +} + +INSTANTIATE_TEST_CASE_P(fusings_gpu, conv_bin_scale_activation, + ::testing::ValuesIn(std::vector{ + bc_test_params{CASE_BIN_CONV1, 3, 5}, + bc_test_params{CASE_BIN_CONV2, 3, 5}, + }), ); + +class conv_bin_quantize_bin : public BaseFusingTest {}; +TEST_P(conv_bin_quantize_bin, channel_wise_quantize) { + auto p = GetParam(); + auto in_thresh = get_mem(get_per_channel_layout(p), min_random, max_random); + topology.add(input_layout("input", get_input_layout(p)), + data("weights", get_mem(get_weights_layout(p), -127, 127)), + data("in_lo", in_thresh), + data("in_hi", in_thresh), + data("out_lo", get_mem(get_per_channel_layout(p), -1)), + data("out_hi", get_mem(get_per_channel_layout(p), 1)), + binary_convolution("bin_conv_prim", "input", {"weights"}, p.stride, p.pad, p.dilation, p.out_shape, p.groups), + quantize("quantize_data", "bin_conv_prim", "in_lo", "in_hi", "out_lo", "out_hi", 2), + reorder("reorder_bfyx", "quantize_data", p.default_format, data_types::f32) + ); + tolerance = 1e-5f; + execute(p); +} + +TEST_P(conv_bin_quantize_bin, blob_wise_quantize) { + auto p = GetParam(); + auto in_thresh = get_mem(get_single_element_layout(p), min_random, max_random); + topology.add(input_layout("input", get_input_layout(p)), + data("weights", get_mem(get_weights_layout(p), -127, 127)), + data("in_lo", in_thresh), + data("in_hi", in_thresh), + data("out_lo", get_mem(get_single_element_layout(p), -1)), + data("out_hi", get_mem(get_single_element_layout(p), 1)), + binary_convolution("bin_conv_prim", "input", {"weights"}, p.stride, p.pad, p.dilation, p.out_shape, p.groups), + quantize("quantize_data", "bin_conv_prim", "in_lo", "in_hi", "out_lo", "out_hi", 2), + reorder("reorder_bfyx", "quantize_data", p.default_format, data_types::f32) + ); + tolerance = 1e-5f; + execute(p); +} + +INSTANTIATE_TEST_CASE_P(fusings_gpu, conv_bin_quantize_bin, + ::testing::ValuesIn(std::vector{ + bc_test_params{CASE_BIN_CONV1, 3, 4}, + bc_test_params{CASE_BIN_CONV2, 3, 4}, + }), ); + +class conv_bin_scale_conv_dw : public BaseFusingTest {}; +TEST_P(conv_bin_scale_conv_dw, dw_kernel_3x3_stride2) { + auto p = GetParam(); + auto dw_weights_layout = layout{p.default_type, p.default_format, tensor{p.out_shape.feature[0], + 1, 3, 3}}; + + auto dw_stride = tensor{1, 1, 2, 2}; + topology.add(input_layout("input", get_input_layout(p)), + data("weights", get_mem(get_weights_layout(p), -127, 127)), + data("weights_dw", get_mem(dw_weights_layout, -127, 127)), + data("scale_data", get_mem(get_per_channel_layout(p), 1e-1)), + binary_convolution("bin_conv_prim", "input", {"weights"}, p.stride, p.pad, p.dilation, p.out_shape, p.groups), + scale("scale", "bin_conv_prim", "scale_data"), + convolution("conv_dw", "scale", {"weights_dw"}, p.out_shape.feature[0], dw_stride, p.pad, p.dilation), + reorder("reorder_bfyx", "conv_dw", p.default_format, data_types::f32) + ); + tolerance = 1e-5f; + execute(p); +} + +TEST_P(conv_bin_scale_conv_dw, dw_kernel_3x3_stride1) { + auto p = GetParam(); + auto dw_weights_layout = layout{p.default_type, p.default_format, tensor{p.out_shape.feature[0], + 1, 3, 3}}; + + auto dw_stride = tensor{1, 1, 1, 1}; + topology.add(input_layout("input", get_input_layout(p)), + data("weights", get_mem(get_weights_layout(p), -127, 127)), + data("weights_dw", get_mem(dw_weights_layout, -127, 127)), + data("scale_data", get_mem(get_per_channel_layout(p), 1e-1)), + binary_convolution("bin_conv_prim", "input", {"weights"}, p.stride, p.pad, p.dilation, p.out_shape, p.groups), + scale("scale", "bin_conv_prim", "scale_data"), + convolution("conv_dw", "scale", {"weights_dw"}, p.out_shape.feature[0], dw_stride, p.pad, p.dilation), + reorder("reorder_bfyx", "conv_dw", p.default_format, data_types::f32) + ); + tolerance = 1e-5f; + execute(p); +} + +INSTANTIATE_TEST_CASE_P(fusings_gpu, conv_bin_scale_conv_dw, + ::testing::ValuesIn(std::vector{ + bc_test_params{CASE_BIN_CONV2, 4, 5}, + bc_test_params{CASE_BIN_CONV3, 4, 5}, + }), ); + +class conv_bin_scale_conv_dw_prelu : public BaseFusingTest {}; +TEST_P(conv_bin_scale_conv_dw_prelu, dw_kernel_3x3_stride2) { + auto p = GetParam(); + auto dw_weights_layout = layout{p.default_type, p.default_format, tensor{p.out_shape.feature[0], + 1, 3, 3}}; + + auto dw_stride = tensor{1, 1, 2, 2}; + auto in_thresh = get_mem(get_per_channel_layout(p), min_random, max_random); + topology.add(input_layout("input", get_input_layout(p)), + data("weights", get_mem(get_weights_layout(p), -127, 127)), + data("weights_dw", get_mem(dw_weights_layout, -127, 127)), + data("scale_data", get_mem(get_per_channel_layout(p), 1e-1)), + binary_convolution("bin_conv_prim", "input", {"weights"}, p.stride, p.pad, p.dilation, p.out_shape, p.groups), + scale("scale", "bin_conv_prim", "scale_data"), + convolution("conv_dw", "scale", {"weights_dw"}, p.out_shape.feature[0], dw_stride, p.pad, p.dilation), + data("slope_data", get_mem(get_per_channel_layout(p))), + activation("activation", "conv_dw", "slope_data", activation_func::relu_negative_slope), + reorder("reorder_bfyx", "activation", p.default_format, data_types::f32) + ); + tolerance = 1e-5f; + execute(p); +} + +TEST_P(conv_bin_scale_conv_dw_prelu, dw_kernel_3x3_stride1) { + auto p = GetParam(); + auto dw_weights_layout = layout{p.default_type, p.default_format, tensor{p.out_shape.feature[0], + 1, 3, 3}}; + + auto dw_stride = tensor{1, 1, 1, 1}; + auto in_thresh = get_mem(get_per_channel_layout(p), min_random, max_random); + topology.add(input_layout("input", get_input_layout(p)), + data("weights", get_mem(get_weights_layout(p), -127, 127)), + data("weights_dw", get_mem(dw_weights_layout, -127, 127)), + data("scale_data", get_mem(get_per_channel_layout(p), 1e-1)), + binary_convolution("bin_conv_prim", "input", {"weights"}, p.stride, p.pad, p.dilation, p.out_shape, p.groups), + scale("scale", "bin_conv_prim", "scale_data"), + convolution("conv_dw", "scale", {"weights_dw"}, p.out_shape.feature[0], dw_stride, p.pad, p.dilation), + data("slope_data", get_mem(get_per_channel_layout(p))), + activation("activation", "conv_dw", "slope_data", activation_func::relu_negative_slope), + reorder("reorder_bfyx", "activation", p.default_format, data_types::f32) + ); + tolerance = 1e-5f; + execute(p); +} + +INSTANTIATE_TEST_CASE_P(fusings_gpu, conv_bin_scale_conv_dw_prelu, + ::testing::ValuesIn(std::vector{ + bc_test_params{CASE_BIN_CONV2, 4, 6}, + bc_test_params{CASE_BIN_CONV3, 4, 6}, + }), ); diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/gather_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/gather_gpu_test.cpp index 76614e9..8245763 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/gather_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/gather_gpu_test.cpp @@ -12,16 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. - - /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/gemm_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/gemm_gpu_test.cpp index 8c92c97..124cc72 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/gemm_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/gemm_gpu_test.cpp @@ -15,19 +15,18 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" #include "test_utils/uniform_quantized_real_distribution.hpp" #include - using namespace cldnn; using namespace ::tests; @@ -76,7 +75,6 @@ TEST(gemm_gpu, basic_bfyx_t1) { auto output = outputs.at("output").get_memory(); auto output_ptr = output.pointer(); - EXPECT_EQ(output_ptr.size(), (uint32_t)3); for (uint32_t i = 0; i < out_data.size(); ++i) { EXPECT_FLOAT_EQ(output_ptr[i], out_data[i]); @@ -123,7 +121,6 @@ TEST(gemm_gpu, basic_bfyx_t2) { auto output = outputs.at("output").get_memory(); auto output_ptr = output.pointer(); - EXPECT_EQ(output_ptr.size(), (uint32_t)3); for (uint32_t i = 0; i < out_data.size(); ++i) { EXPECT_FLOAT_EQ(output_ptr[i], out_data[i]); @@ -180,7 +177,6 @@ TEST(gemm_gpu, basic_bfyx_t1t2) { auto output = outputs.at("output").get_memory(); auto output_ptr = output.pointer(); - EXPECT_EQ(output_ptr.size(), (uint32_t)6); for (uint32_t i = 0; i < out_data.size(); ++i) { EXPECT_FLOAT_EQ(output_ptr[i], out_data[i]); @@ -195,7 +191,6 @@ TEST(gemm_gpu, basic_input3) { float alpha = 2.f; float beta = 10.f; - std::vector input_data = { 1.0f, 2.0f, 3.0f, 1.0f, 0.0f, 1.0f @@ -259,7 +254,6 @@ TEST(gemm_gpu, basic_input3_t1t2) { float alpha = 2.f; float beta = 3.f; - std::vector input_data = { 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 0.0f, 1.0f, 0.0f, @@ -396,7 +390,6 @@ TEST(gemm_gpu, basic_input3_t2) { float alpha = 2.f; float beta = 3.f; - std::vector input_data = { 1.0f, 1.0f, 0.0f, 2.0f, 0.0f, 0.0f, @@ -404,7 +397,6 @@ TEST(gemm_gpu, basic_input3_t2) { 4.0f, 0.0f, 0.0f }; - std::vector input_data2 = { 3.0f, 3.0f, 1.0f, 2.0f, 1.0f, 2.0f, @@ -466,7 +458,6 @@ TEST(gemm_gpu, basic_input3_t1) { float alpha = 2.f; float beta = 3.f; - std::vector input_data = { 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 0.0f, 1.0f, 0.0f, @@ -3181,14 +3172,11 @@ TEST(gemm_gpu, basic3_bfyx) { auto output = outputs.at("output").get_memory(); auto output_ptr = output.pointer(); - EXPECT_EQ(output_ptr.size(), (uint32_t)45); for (uint32_t i = 0; i < out_data.size(); ++i) { EXPECT_NEAR(output_ptr[i], out_data[i], 0.0001); } - - } TEST(gemm_gpu, basic_smarcink2) { @@ -3243,7 +3231,6 @@ TEST(gemm_gpu, basic_smarcink2) { auto output = outputs.at("output").get_memory(); auto output_ptr = output.pointer(); - EXPECT_EQ(output_ptr.size(), (uint32_t)8); for (uint32_t i = 0; i < out_data.size(); ++i) { EXPECT_FLOAT_EQ(output_ptr[i], out_data[i]); diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/index_select_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/index_select_gpu_test.cpp index 218cac0..33331b1 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/index_select_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/index_select_gpu_test.cpp @@ -18,12 +18,12 @@ #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" @@ -133,7 +133,6 @@ std::vector generate_reference_bfyx(const std::vector& input, cons } } - std::vector generate_reference_yxfb(const std::vector& input, const std::vector& indices, index_select_axis_name axis, const cldnn::layout& input_lay) { auto memory_desc_inp = generic_test::get_linear_memory_desc(input_lay); @@ -215,7 +214,6 @@ std::vector generate_reference_yxfb(const std::vector& input, cons } } - TEST(index_select_gpu, basic_along_b_3_executes_bfyx) { /* @@ -839,8 +837,6 @@ TEST(index_select_gpu, reverse_along_b_bfyx) 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f, 15.f, - - 16.f, 17.f, 18.f, 19.f, 20.f, 21.f, 22.f, 23.f, @@ -1039,7 +1035,6 @@ TEST(index_select_gpu, reverse_along_x_bfyx) } } - TEST(index_select_gpu, reverse_along_y_yxfb) { const auto& engine = get_test_engine(); @@ -1052,8 +1047,6 @@ TEST(index_select_gpu, reverse_along_y_yxfb) 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f, 15.f, - - 16.f, 17.f, 18.f, 19.f, 20.f, 21.f, 22.f, 23.f, @@ -1068,8 +1061,6 @@ TEST(index_select_gpu, reverse_along_y_yxfb) 24.f, 25.f, 26.f, 27.f, 28.f, 29.f, 30.f, 31.f, - - 0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, @@ -1252,7 +1243,6 @@ TEST(index_select_gpu, reverse_along_b_yxfb) } } - TEST(index_select_gpu, reverse_along_yx_bfyx) { const auto& engine = get_test_engine(); @@ -1412,7 +1402,6 @@ TEST(index_select_gpu, reverse_along_bfyx_bfyx) 79.f, 78.f, 77.f, 76.f, 75.f, 74.f, 73.f, 72.f, - 71.f, 70.f, 69.f, 68.f, 67.f, 66.f, 65.f, 64.f, 63.f, 62.f, 61.f, 60.f, @@ -1523,7 +1512,6 @@ TEST(index_select_gpu, reverse_along_bfx_yxfb) 7.f, 6.f, 5.f, 4.f, 3.f, 2.f, 1.f, 0.f, - 71.f, 70.f, 69.f, 68.f, 67.f, 66.f, 65.f, 64.f, 63.f, 62.f, 61.f, 60.f, @@ -1536,7 +1524,6 @@ TEST(index_select_gpu, reverse_along_bfx_yxfb) 43.f, 42.f, 41.f, 40.f, 39.f, 38.f, 37.f, 36.f, - 107.f, 106.f, 105.f, 104.f, 103.f, 102.f, 101.f, 100.f, 99.f, 98.f, 97.f, 96.f, @@ -1634,7 +1621,6 @@ TEST(index_select_gpu, reverse_along_bfyx_yxfb) 79.f, 78.f, 77.f, 76.f, 75.f, 74.f, 73.f, 72.f, - 71.f, 70.f, 69.f, 68.f, 67.f, 66.f, 65.f, 64.f, 63.f, 62.f, 61.f, 60.f, @@ -1647,7 +1633,6 @@ TEST(index_select_gpu, reverse_along_bfyx_yxfb) 43.f, 42.f, 41.f, 40.f, 39.f, 38.f, 37.f, 36.f, - 35.f, 34.f, 33.f, 32.f, 31.f, 30.f, 29.f, 28.f, 27.f, 26.f, 25.f, 24.f, diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/lookup_table_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/lookup_table_test.cpp index 594c803..5deb427 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/lookup_table_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/lookup_table_test.cpp @@ -15,20 +15,19 @@ */ #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/lookup_table.hpp" -#include "api/CPP/arg_max_min.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/lookup_table.hpp" +#include "api/arg_max_min.hpp" +#include +#include +#include #include "test_utils/test_utils.h" using namespace cldnn; using namespace std; using namespace tests; - TEST(lookup_table_base, base) { // Input : 2x3x2x2 static const int32_t x_size = 2, y_size = 2, feature_num = 3, batch_num = 2; diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/lstm_dynamic_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/lstm_dynamic_gpu_test.cpp index 6e93ffb..46cfc1c 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/lstm_dynamic_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/lstm_dynamic_gpu_test.cpp @@ -1,25 +1,28 @@ #include -#include "api/CPP/memory.hpp" -#include "api/CPP/mutable_data.hpp" -#include "api/CPP/input_layout.hpp" -#include "api/CPP/lstm.hpp" -#include "api/CPP/lstm_dynamic.hpp" -#include "api_extension/CPP/lstm_dynamic_input.hpp" -#include "api_extension/CPP/lstm_dynamic_timeloop.hpp" -#include "api/CPP/topology.hpp" -#include "api/CPP/tensor.hpp" -#include "api/CPP/network.hpp" -#include "api/CPP/engine.hpp" +#include "api/memory.hpp" +#include "api/mutable_data.hpp" +#include "api/input_layout.hpp" +#include "api/lstm.hpp" +#include "api/lstm_dynamic.hpp" +#include "api/reorder.hpp" +#include "api_extension/lstm_dynamic_input.hpp" +#include "api_extension/lstm_dynamic_timeloop.hpp" +#include "api/topology.hpp" +#include "api/tensor.hpp" +#include "api/network.hpp" +#include "api/engine.hpp" #include "test_utils/test_utils.h" -#include "api/CPP/data.hpp" +#include "api/data.hpp" #include "instrumentation.h" #include - +#include #include #include #pragma warning( disable : 4503 ) +#define MEASURE_PERF false +#define MEASURE_LOOP 50 using namespace cldnn; using namespace tests; @@ -31,9 +34,9 @@ namespace { struct offset_order_dynamic { size_t it, ot, ft, zt; - offset_order_dynamic(size_t scale, const cldnn_lstm_offset_order& t = cldnn_lstm_offset_order_fizo) { - static const std::map> offset_map{ - { cldnn_lstm_offset_order_fizo, { 1, 3, 0, 2 } }, + offset_order_dynamic(size_t scale, const lstm_weights_order& t = lstm_weights_order::fizo) { + static const std::map> offset_map{ + { lstm_weights_order::fizo, { 1, 3, 0, 2 } }, }; std::vector v = offset_map.at(t); it = v[0] * scale; @@ -42,7 +45,7 @@ struct offset_order_dynamic { zt = v[3] * scale; } }; -cldnn_lstm_offset_order default_offset_type_dynamic = cldnn_lstm_offset_order_fizo; +lstm_weights_order default_offset_type_dynamic = lstm_weights_order::fizo; namespace dynamic_lstm { @@ -243,7 +246,28 @@ struct lstm_dynamic_input_layer_test : public ::testing::Test "weights", bias_id)); - network network(engine, topology); + build_options opts; + opts.set_option(build_option::optimize_data(true)); + network network(engine, topology, opts); + +#if MEASURE_PERF == true + using clock = std::chrono::high_resolution_clock; + std::vector times(MEASURE_LOOP); + for (uint32_t i = 0; i < MEASURE_LOOP; i++) + { + auto t0 = clock::now(); + network.set_input_data("input", input_mem); + network.set_input_data("dynamic_lstm_input", dynamic_length_mem); + auto real_outs = network.execute(); + real_outs.at("dynamic_lstm_input").get_event().wait(); + auto t1 = clock::now(); + auto exec_time = t1 - t0; + times[i] = exec_time; + } + std::sort(times.begin(), times.end()); + std::nth_element(times.begin(), times.begin() + times.size() / 2, times.end()); + std::cout << "Perf: " << std::chrono::duration_cast(times[times.size() / 2]).count() << " micros. " << std::endl; +#else network.set_input_data("input", input_mem); network.set_input_data("dyn_len", dynamic_length_mem); @@ -263,11 +287,17 @@ struct lstm_dynamic_input_layer_test : public ::testing::Test { for (auto x = 0; x < out_tensor.spatial[0]; x++) { - EXPECT_NEAR(output_ref[b][len][dir][x], (float)out_ptr[i++], 1e-3f); + EXPECT_NEAR(output_ref[b][len][dir][x], (float)out_ptr[i++], 1e-3f) + << "b:" << b << ", " + << "len:" << len << ", " + << "dir:" << dir << ", " + << "x:" << x << ", " + << std::endl; } } } } +#endif } }; @@ -297,10 +327,6 @@ struct lstm_dynamic_single_layer_test : public ::testing::Test VF ref_hidden_vec = flatten_4d(cldnn::format::bfyx, ref_hidden); VF ref_cell_vec = flatten_4d(cldnn::format::bfyx, ref_cell); - dynamic_lstm::lstm_dynamic_reference(ref_input, ref_hidden, ref_cell, ref_weights, ref_recurrent, ref_bias, ref_output_hidden, - ref_output_cell, has_bias, has_initial_hidden, has_initial_cell, - clip_threshold, input_forget); - const auto& engine = get_test_engine(); constexpr auto dt = std::is_same::value ? data_types::f32 : data_types::f16; VF ref_dynamic_length; @@ -323,7 +349,6 @@ struct lstm_dynamic_single_layer_test : public ::testing::Test memory initial_cell_mem = memory::allocate(engine, { dt, format::bfyx,{ batch_size, 1, hidden_size, direction } }); set_values(initial_cell_mem, ref_cell_vec); - topology topology; topology.add(input_layout("input", input_mem.get_layout())); topology.add(input_layout("dyn_len", dynamic_length_mem.get_layout())); @@ -378,10 +403,33 @@ struct lstm_dynamic_single_layer_test : public ::testing::Test initial_hidden_id, initial_cell_id)); - network network(engine, topology); + build_options opts; + opts.set_option(build_option::optimize_data(true)); + network network(engine, topology, opts); network.set_input_data("input", input_mem); network.set_input_data("dyn_len", dynamic_length_mem); +#if MEASURE_PERF == true + using clock = std::chrono::high_resolution_clock; + std::vector times(MEASURE_LOOP); + for (uint32_t i = 0; i < MEASURE_LOOP; i++) + { + auto t0 = clock::now(); + network.set_input_data("input", input_mem); + network.set_input_data("dyn_len", dynamic_length_mem); + auto real_outs = network.execute(); + real_outs.at("dynamic_lstm").get_event().wait(); + auto t1 = clock::now(); + auto exec_time = t1 - t0; + times[i] = exec_time; + } + std::sort(times.begin(), times.end()); + std::nth_element(times.begin(), times.begin() + times.size() / 2, times.end()); + std::cout << "Perf: " << std::chrono::duration_cast(times[times.size() / 2]).count() << " micros. " << std::endl; +#else + dynamic_lstm::lstm_dynamic_reference(ref_input, ref_hidden, ref_cell, ref_weights, ref_recurrent, ref_bias, ref_output_hidden, + ref_output_cell, has_bias, has_initial_hidden, has_initial_cell, + clip_threshold, input_forget); auto real_outs = network.execute(); auto out = real_outs.at("dynamic_lstm"); auto out_tensor = out.get_memory().get_layout().size; @@ -400,39 +448,76 @@ struct lstm_dynamic_single_layer_test : public ::testing::Test //check hidden if (len < dynamic_lengths[b]) { - EXPECT_NEAR((float)ref_output_hidden[b][len][dir][x], (float)out_ptr[i++], epsilon); + EXPECT_NEAR((float)ref_output_hidden[b][len][dir][x], (float)out_ptr[i++], epsilon) + << "check hidden, " + << "b:" << b << ", " + << "len:" << len << ", " + << "dir:" << dir << ", " + << "x:" << x << ", " + << std::endl; } else { - EXPECT_NEAR(0.0f, (float)out_ptr[i++], epsilon); + EXPECT_NEAR(0.0f, (float)out_ptr[i++], epsilon) + << "check hidden, " + << "b:" << b << ", " + << "len:" << len << ", " + << "dir:" << dir << ", " + << "x:" << x << ", " + << std::endl; } //check optional last hidden state output if(has_last_hidden_state && len == dynamic_lengths[b] - 1) { auto ratio = (float)ref_output_hidden[b][len][dir][x] / (float)last_hidden_ptr[i_lh++]; - EXPECT_TRUE(std::abs((1.0f - ratio) < 0.01f)); + EXPECT_TRUE(std::abs((1.0f - ratio) < 0.01f)) + << "check has_last_hidden_state with ratio: " << ratio << ", " + << "b:" << b << ", " + << "len:" << len << ", " + << "dir:" << dir << ", " + << "x:" << x << ", " + << std::endl; } else if (has_last_hidden_state && len == 0 && dynamic_lengths[b] == 0) { - EXPECT_NEAR(0.0f, (float)last_hidden_ptr[i_lh++], epsilon); + EXPECT_NEAR(0.0f, (float)last_hidden_ptr[i_lh++], epsilon) + << "check has_last_hidden_state, " + << "b:" << b << ", " + << "len:" << len << ", " + << "dir:" << dir << ", " + << "x:" << x << ", " + << std::endl; } //check optional last cell state output if(has_last_cell_state && len == dynamic_lengths[b] - 1) { auto ratio = (float)ref_output_cell[b][len][dir][x] / (float)last_cell_ptr[i_lc++]; - EXPECT_TRUE(std::abs((1.0f - ratio) < 0.01f)); + EXPECT_TRUE(std::abs((1.0f - ratio) < 0.01f)) + << "check has_last_cell_state with ratio: " << ratio << ", " + << "b:" << b << ", " + << "len:" << len << ", " + << "dir:" << dir << ", " + << "x:" << x << ", " + << std::endl; } else if (has_last_cell_state && len == 0 && dynamic_lengths[b] == 0) { - EXPECT_NEAR(0.0f, (float)last_cell_ptr[i_lc++], epsilon); + EXPECT_NEAR(0.0f, (float)last_cell_ptr[i_lc++], epsilon) + << "check has_last_cell_state, " + << "b:" << b << ", " + << "len:" << len << ", " + << "dir:" << dir << ", " + << "x:" << x << ", " + << std::endl; } } } } } +#endif } }; @@ -445,8 +530,6 @@ TYPED_TEST_CASE(lstm_dynamic_input_layer_test, lstm_dynamic_test_types); DYNAMIC_LSTM INPUT TEST ---------------------------------------------- */ -//VVVVF lstm_dynamic_input_ref(VVVVF& input, VVVVF& weights, VVVVF& bias, -//size_t seq, bool hasBias = true, size_t dir = 0) { TYPED_TEST(lstm_dynamic_input_layer_test, dlstm_input_b1_seq3_is3_hs2) { @@ -462,6 +545,16 @@ TYPED_TEST(lstm_dynamic_input_layer_test, dlstm_input_b3_seq5_is3_hs2) this->input_single_layer_generic_test(dir, batch_size, max_seq_len, input_size, hidden_size, dynamic_lengths, true); } +TYPED_TEST(lstm_dynamic_input_layer_test, b10_seq20_is16_hs64) +{ + auto dir = 1, batch = 10, max_seq_len = 20, input_size = 16, hidden_size = 64; + std::vector dynamic_lengths = + { + 5, 10, 12, 11, 5, 6, 7, 8, 9, 15, + }; + this->input_single_layer_generic_test(dir, batch, max_seq_len, input_size, hidden_size, dynamic_lengths); +} + TYPED_TEST(lstm_dynamic_input_layer_test, dlstm_input_b8_seq10_is4_hs16) { auto batch_size = 8, max_seq_len = 10, input_size = 4, hidden_size = 16; @@ -482,6 +575,28 @@ TYPED_TEST(lstm_dynamic_input_layer_test, dlstm_input_dir2_b8_seq10_is4_hs16_opt } } +TYPED_TEST(lstm_dynamic_input_layer_test, dlstm_input_1b1_seq1_is32_hs_128) +{ + auto dir = 1, batch = 1, max_seq_len = 1, input_size = 32, hidden_size = 128; + std::vector dynamic_lengths = + { + 1 + }; + bool bias = true; + this->input_single_layer_generic_test(dir, batch, max_seq_len, input_size, hidden_size, dynamic_lengths, bias); +} + +TYPED_TEST(lstm_dynamic_input_layer_test, dlstm_input_dir_b8_seq27_is16_hs_56) +{ + auto dir = 1, batch = 8, max_seq_len = 27, input_size = 16, hidden_size = 56; + std::vector dynamic_lengths = + { + 20, 25, 24, 10, 15, 8, 19, 26 + }; + this->input_single_layer_generic_test(dir, batch, max_seq_len, input_size, hidden_size, dynamic_lengths, false); +} + + /* ---------------------------------------------- FULL DYNAMIC_LSTM TESTS @@ -571,12 +686,13 @@ TYPED_TEST(lstm_dynamic_single_layer_test, b10_seq20_is16_hs64) auto dir = 1, batch = 10, max_seq_len = 20, input_size = 16, hidden_size = 64; std::vector dynamic_lengths = { - 5, 10, 12, 11, 5, 6, 7, 8, 9, 15, + 5, 10, 12, 11, 5, 6, 7, 8, 9, 15, }; this->single_layer_generic_test(dir, batch, max_seq_len, input_size, hidden_size, dynamic_lengths); } -TYPED_TEST(lstm_dynamic_single_layer_test, b16_seq20_is32_hs32_options) +// DISABLED beacuse it is veeery long +TYPED_TEST(lstm_dynamic_single_layer_test, DISABLED_b16_seq20_is32_hs32_options) { auto dir = 1, batch = 16, max_seq_len = 20, input_size = 32, hidden_size = 32; std::vector dynamic_lengths = @@ -606,7 +722,6 @@ TYPED_TEST(lstm_dynamic_single_layer_test, b16_seq20_is32_hs32_options) } } - /* ---------------------------------------------- BIDIRECTIONAL TESTS @@ -620,6 +735,26 @@ TYPED_TEST(lstm_dynamic_single_layer_test, bidir_b2_seq7_is3_hs4) this->single_layer_generic_test(dir, batch, max_seq_len, input_size, hidden_size, dynamic_lengths); } +TYPED_TEST(lstm_dynamic_input_layer_test, dlstm_input_dir_b1_seq1_is32_hs_512) +{ + auto dir = 2, batch = 1, max_seq_len = 1, input_size = 8, hidden_size = 128; + std::vector dynamic_lengths = + { + 1 + }; + this->input_single_layer_generic_test(dir, batch, max_seq_len, input_size, hidden_size, dynamic_lengths, true); +} + +TYPED_TEST(lstm_dynamic_input_layer_test, dlstm_input_dir_b8_seq5_is32_hs_512) +{ + auto dir = 2, batch = 8, max_seq_len = 5, input_size = 8, hidden_size = 128; + std::vector dynamic_lengths = + { + 3, 4, 5, 1, 3, 2, 2, 3 + }; + this->input_single_layer_generic_test(dir, batch, max_seq_len, input_size, hidden_size, dynamic_lengths, true); +} + TYPED_TEST(lstm_dynamic_single_layer_test, bidir_b10_seq7_is3_hs4) { auto dir = 2, batch = 10, max_seq_len = 7, input_size = 3, hidden_size = 4; @@ -718,19 +853,6 @@ TYPED_TEST(lstm_dynamic_single_layer_test, b16_seq20_is4_hs8_dirs_optional_outpu 5, 10, 12, 11, 5, 6, 7, 8, 9, 15, 0, 0, 0, 0, 14, 18 }; this->single_layer_generic_test(1, batch, max_seq_len, input_size, hidden_size, dynamic_lengths, false, false, false, true, true, 1e-3f); - //auto dirs = { 1, 2 }; - //auto opitonal_hidden_outputs = { false, true }; - //auto opitonal_cell_outputs = { false, true }; - //for (auto dir : dirs) - //{ - // for (auto o_h_o: opitonal_hidden_outputs) - // { - // for (auto o_c_o : opitonal_cell_outputs) - // { - // this->single_layer_generic_test(dir, batch, max_seq_len, input_size, hidden_size, dynamic_lengths, false, false, false, o_h_o, o_c_o); - // } - // } - //} } /* @@ -839,7 +961,6 @@ TEST(lstm_dynamic_negative, wrong_dynamic_length_size_0) { ASSERT_ANY_THROW(network network(engine, topology)); } - TEST(lstm_dynamic_negative, wrong_dynamic_length_size_1) { auto batch_size = 50, max_sequence_len = 10, input_size = 16, hidden_size = 32, direction = 1; @@ -866,4 +987,3 @@ TEST(lstm_dynamic_negative, wrong_dynamic_length_size_1) { } - diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/lstm_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/lstm_gpu_test.cpp index 41649a2..6ae2ee7 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/lstm_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/lstm_gpu_test.cpp @@ -16,19 +16,19 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/lstm.hpp" -#include -#include -#include -#include -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/lstm.hpp" +#include +#include +#include +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" -#include +#include #include "instrumentation.h" #include @@ -52,10 +52,10 @@ namespace { struct offset_order { size_t it, ot, ft, zt; - offset_order(size_t scale, const cldnn_lstm_offset_order& t = cldnn_lstm_offset_order_iofz) { - static const std::map> offset_map{ - { cldnn_lstm_offset_order_iofz,{ 0, 1, 2, 3 } }, - { cldnn_lstm_offset_order_ifoz,{ 0, 2, 1, 3 } } + offset_order(size_t scale, const lstm_weights_order& t = lstm_weights_order::iofz) { + static const std::map> offset_map{ + { lstm_weights_order::iofz,{ 0, 1, 2, 3 } }, + { lstm_weights_order::ifoz,{ 0, 2, 1, 3 } } }; std::vector v = offset_map.at(t); it = v[0] * scale; @@ -64,7 +64,7 @@ struct offset_order { zt = v[3] * scale; } }; -cldnn_lstm_offset_order default_offset_type = cldnn_lstm_offset_order_iofz; +lstm_weights_order default_offset_type = lstm_weights_order::iofz; template T clip(T val, T threshold) { @@ -75,7 +75,6 @@ T clip(T val, T threshold) { return val; } - template VVVVF lstm_gemm_reference(VVVVF& input, VVVVF& weights, VVVVF& recurrent, VVVVF& bias, VVVVF& hidden, size_t seq, bool hasBias = true, bool hasHidden = true, size_t dir = 0, size_t input_dir = 0) { @@ -213,8 +212,6 @@ void lstm_reference(VVVVF& input, VVVVF& hidden, VVVVF& cell, last_cell = cell; } - - template void generic_lstm_gemm_gpu_test(int sequence_len, int direction, int batch_size, int input_size, int hidden_size, bool hasBias = true, bool hasHidden = true) { @@ -413,7 +410,6 @@ void generate_lstm_topology(topology& t, memory& input, memory& hidden, memory& t.add(concatenation("concatenation", output_ids_offsets, concatenation::along_f)); } - template void generic_lstm_custom_gpu_test(int sequence_len, int direction, int batch_size, int input_size, int hidden_size, bool hasBias = true, bool hasInitialHidden = true, bool hasInitialCell = true) { @@ -609,13 +605,13 @@ void generic_lstm_gpu_test(int layers, int sequence_len, int direction, int batc topology.add(lstm(lstm_id, lstm_inputs, weights_id, recurrent_id, hasBias ? biases_id : "", hasInitialHidden ? hidden_id : "", hasInitialCell ? cell_id : "", "", clip_threshold, input_forget, {}, {}, - cldnn_lstm_output::cldnn_lstm_output_sequence, default_offset_type)); + lstm_output_selection::sequence, default_offset_type)); } else { topology.add(lstm(lstm_id, { prev_lstm_id }, weights_id, recurrent_id, hasBias ? biases_id : "", hasInitialHidden ? hidden_id : "", hasInitialCell ? cell_id : "", "", clip_threshold, input_forget, {}, {}, - cldnn_lstm_output::cldnn_lstm_output_sequence, default_offset_type)); + lstm_output_selection::sequence, default_offset_type)); } prev_lstm_id = lstm_id; } @@ -662,7 +658,7 @@ void generic_lstm_gpu_test(int layers, int sequence_len, int direction, int batc // ------------------------------------------------------- template -void lstm_gpu_output_test(const cldnn_lstm_output& output_selection, int directions) { +void lstm_gpu_output_test(const lstm_output_selection& output_selection, int directions) { int layers = 1; int sequence_len = 4; int batch_size = 3; @@ -671,7 +667,7 @@ void lstm_gpu_output_test(const cldnn_lstm_output& output_selection, int directi std::cout << "Layers = " << layers << " Input Size = " << input_size << " Hidden Size = " << hidden_size << " Sequence Len = " << sequence_len << " Directions = " << directions << " Batch Size = " << batch_size - << " Output selection: " << output_selection << std::endl; + << " Output selection: " << static_cast(output_selection) << std::endl; int min_random = -2, max_random = 2; VVVVF ref_input = generate_random_4d(batch_size, sequence_len, 1, input_size, min_random, max_random); @@ -712,10 +708,10 @@ void lstm_gpu_output_test(const cldnn_lstm_output& output_selection, int directi set_values(hidden, ref_hidden_vec); set_values(cell, ref_cell_vec); - bool emit_last_cell = output_selection == cldnn_lstm_output_hidden_cell || - output_selection == cldnn_lstm_output_sequence_cell; - bool emit_last_hidden = output_selection == cldnn_lstm_output_hidden || - output_selection == cldnn_lstm_output_hidden_cell; + bool emit_last_cell = output_selection == lstm_output_selection::hidden_cell || + output_selection == lstm_output_selection::sequence_cell; + bool emit_last_hidden = output_selection == lstm_output_selection::hidden || + output_selection == lstm_output_selection::hidden_cell; topology topology; std::vector> input_ids_offsets; @@ -820,7 +816,6 @@ void lstm_gpu_output_test(const cldnn_lstm_output& output_selection, int directi } } - // ------------------------------------------------------- template void lstm_gpu_format_test(const cldnn::format& format, int directions) { @@ -830,11 +825,11 @@ void lstm_gpu_format_test(const cldnn::format& format, int directions) { int input_size = 4; int hidden_size = 5; - cldnn_lstm_output output_selection = cldnn_lstm_output::cldnn_lstm_output_sequence; + lstm_output_selection output_selection = lstm_output_selection::sequence; std::cout << "Layers = " << layers << " Input Size = " << input_size << " Hidden Size = " << hidden_size << " Sequence Len = " << sequence_len << " Directions = " << directions << " Batch Size = " << batch_size - << " Output selection: " << output_selection << std::endl; + << " Output selection: " << static_cast(output_selection) << std::endl; int min_random = -2, max_random = 2; VVVVF ref_input = generate_random_4d(batch_size, sequence_len, 1, input_size, min_random, max_random); @@ -875,10 +870,10 @@ void lstm_gpu_format_test(const cldnn::format& format, int directions) { set_values(hidden, ref_hidden_vec); set_values(cell, ref_cell_vec); - bool emit_last_cell = output_selection == cldnn_lstm_output_hidden_cell || - output_selection == cldnn_lstm_output_sequence_cell; - bool emit_last_hidden = output_selection == cldnn_lstm_output_hidden || - output_selection == cldnn_lstm_output_hidden_cell; + bool emit_last_cell = output_selection == lstm_output_selection::hidden_cell || + output_selection == lstm_output_selection::sequence_cell; + bool emit_last_hidden = output_selection == lstm_output_selection::hidden || + output_selection == lstm_output_selection::hidden_cell; topology topology; std::vector> input_ids_offsets; @@ -1071,7 +1066,7 @@ void lstm_gpu_users_test() { topology.add(input_layout("cell", cell.get_layout())); topology.add(lstm("lstm", lstm_inputs, "weights", "recurrent", "biases", "hidden", "cell", "", 0, false, {}, {}, - cldnn_lstm_output::cldnn_lstm_output_hidden, default_offset_type)); + lstm_output_selection::hidden, default_offset_type)); std::vector output_ids_offsets {"lstm", "hidden"}; topology.add(concatenation("concatenation", output_ids_offsets, concatenation::along_f)); @@ -1216,13 +1211,13 @@ void lstm_gpu_concatenated_input_test(int layers, int sequence_len, int directio topology.add(lstm(lstm_id, { "input" }, weights_id, recurrent_id, has_bias ? biases_id : "", has_initial_hidden ? hidden_id : "", has_initial_cell ? cell_id : "", "", clip_threshold, input_forget, {}, {}, - cldnn_lstm_output::cldnn_lstm_output_sequence_cell, default_offset_type)); + lstm_output_selection::sequence_cell, default_offset_type)); } else { topology.add(lstm(lstm_id, { prev_node_id }, weights_id, recurrent_id, has_bias ? biases_id : "", has_initial_hidden ? hidden_id : "", has_initial_cell ? cell_id : "", "", clip_threshold, input_forget, {}, {}, - cldnn_lstm_output::cldnn_lstm_output_sequence_cell, default_offset_type)); + lstm_output_selection::sequence_cell, default_offset_type)); } // Crop out the whole output sequence element @@ -1277,7 +1272,7 @@ void lstm_gpu_concatenated_input_test(int layers, int sequence_len, int directio template void lstm_gpu_chain_test(int batch_size, int input_size, int hidden_size, int directions, size_t layers, size_t chains, int sequence_len, - const cldnn_lstm_output& output_selection) + const lstm_output_selection& output_selection) { int min_random = -2, max_random = 2; bool has_bias = false; @@ -1288,7 +1283,7 @@ void lstm_gpu_chain_test(int batch_size, int input_size, int hidden_size, std::cout << "Layers = " << layers << " Input Size = " << input_size << " Hidden Size = " << hidden_size << " Sequence Len = " << sequence_len << " Directions = " << directions << " Batch Size = " << batch_size - << " Output selection: " << output_selection << std::endl; + << " Output selection: " << static_cast(output_selection) << std::endl; VVVVF ref_input = generate_random_4d(batch_size, sequence_len, 1, input_size, min_random, max_random); std::vector>> ref_weights; @@ -1466,8 +1461,8 @@ void lstm_gpu_chain_test(int batch_size, int input_size, int hidden_size, } topology.add(split("inputSplit", "input", input_ids_offsets)); - bool emit_last_hidden = output_selection == cldnn_lstm_output_hidden - || output_selection == cldnn_lstm_output_hidden_cell; + bool emit_last_hidden = output_selection == lstm_output_selection::hidden + || output_selection == lstm_output_selection::hidden_cell; std::vector output_sequence_ids; std::vector last_hidden_ids; @@ -1500,7 +1495,7 @@ void lstm_gpu_chain_test(int batch_size, int input_size, int hidden_size, primitive_id initial_hidden_id; primitive_id initial_cell_id; - cldnn_lstm_output output_selection_per_layer; + lstm_output_selection output_selection_per_layer; topology.add(data(weights_id, weights[chain][layer])); topology.add(data(recurrent_id, recurrent[chain][layer])); @@ -1528,7 +1523,7 @@ void lstm_gpu_chain_test(int batch_size, int input_size, int hidden_size, // last hidden and last cell if (layer < layers - 1) { - output_selection_per_layer = cldnn_lstm_output::cldnn_lstm_output_sequence_cell; + output_selection_per_layer = lstm_output_selection::sequence_cell; } else { @@ -1651,7 +1646,6 @@ void lstm_gpu_chain_test(int batch_size, int input_size, int hidden_size, } } - TEST(lstm_gemm_gpu, generic_lstm_gemm_test_f32) { generic_lstm_gemm_gpu_test(1, 1, 3, 6, 2, true, true); } @@ -1785,9 +1779,9 @@ TEST(lstm_gpu, generic_lstm_clip_input_forget_f32) { } TEST(lstm_gpu, generic_lstm_offset_order_ifoz_f32) { - default_offset_type = cldnn_lstm_offset_order_ifoz; + default_offset_type = lstm_weights_order::ifoz; generic_lstm_gpu_test(1, 7, 1, 3, 3, 2, true, true, true); - default_offset_type = cldnn_lstm_offset_order_iofz; + default_offset_type = lstm_weights_order::iofz; } TEST(lstm_gpu, generic_lstm_canonical_f32) { @@ -1830,35 +1824,35 @@ TEST(lstm_gpu, generic_lstm_stacked_seq_bi_f32) { // optional outputs support TEST(lstm_gpu, output_test_sequence_f32) { - lstm_gpu_output_test(cldnn_lstm_output::cldnn_lstm_output_sequence, 1); + lstm_gpu_output_test(lstm_output_selection::sequence, 1); } TEST(lstm_gpu, output_test_hidden_f32) { - lstm_gpu_output_test(cldnn_lstm_output::cldnn_lstm_output_hidden, 1); + lstm_gpu_output_test(lstm_output_selection::hidden, 1); } TEST(lstm_gpu, output_test_hidden_cell_f32) { - lstm_gpu_output_test(cldnn_lstm_output::cldnn_lstm_output_hidden_cell, 1); + lstm_gpu_output_test(lstm_output_selection::hidden_cell, 1); } TEST(lstm_gpu, output_test_sequence_cell_f32) { - lstm_gpu_output_test(cldnn_lstm_output::cldnn_lstm_output_sequence_cell, 1); + lstm_gpu_output_test(lstm_output_selection::sequence_cell, 1); } TEST(lstm_gpu, output_test_sequence_bi_f32) { - lstm_gpu_output_test(cldnn_lstm_output::cldnn_lstm_output_sequence, 2); + lstm_gpu_output_test(lstm_output_selection::sequence, 2); } TEST(lstm_gpu, output_test_hidden_bi_f32) { - lstm_gpu_output_test(cldnn_lstm_output::cldnn_lstm_output_hidden, 2); + lstm_gpu_output_test(lstm_output_selection::hidden, 2); } TEST(lstm_gpu, output_test_hidden_cell_bi_f32) { - lstm_gpu_output_test(cldnn_lstm_output::cldnn_lstm_output_hidden_cell, 2); + lstm_gpu_output_test(lstm_output_selection::hidden_cell, 2); } TEST(lstm_gpu, output_test_sequence_cell_bi_f32) { - lstm_gpu_output_test(cldnn_lstm_output::cldnn_lstm_output_sequence_cell, 2); + lstm_gpu_output_test(lstm_output_selection::sequence_cell, 2); } // format tests @@ -1902,7 +1896,7 @@ TEST(lstm_gpu, generic_lstm_chained_unidirectional_f32) { // chains = 1 // sequence length = 1 // output selection = output sequence and cell - lstm_gpu_chain_test(1, 2, 4, 1, 1, 2, 1, cldnn_lstm_output::cldnn_lstm_output_sequence_cell); + lstm_gpu_chain_test(1, 2, 4, 1, 1, 2, 1, lstm_output_selection::sequence_cell); } TEST(lstm_gpu, generic_lstm_chained_bidirectional_f32) { @@ -1914,7 +1908,7 @@ TEST(lstm_gpu, generic_lstm_chained_bidirectional_f32) { // chains = 1 // sequence length = 1 // output selection = output sequence and cell - lstm_gpu_chain_test(1, 2, 4, 2, 1, 1, 1, cldnn_lstm_output::cldnn_lstm_output_sequence_cell); + lstm_gpu_chain_test(1, 2, 4, 2, 1, 1, 1, lstm_output_selection::sequence_cell); } TEST(lstm_gpu, generic_lstm_chained_no_stack_bidirectional_f32) { @@ -1926,7 +1920,7 @@ TEST(lstm_gpu, generic_lstm_chained_no_stack_bidirectional_f32) { // chains = 2 // sequence length = 5 // output selection = output sequence and cell - lstm_gpu_chain_test(2, 2, 4, 2, 1, 2, 5, cldnn_lstm_output::cldnn_lstm_output_sequence_cell); + lstm_gpu_chain_test(2, 2, 4, 2, 1, 2, 5, lstm_output_selection::sequence_cell); } TEST(lstm_gpu, generic_lstm_chained_stacked_bidirectional_f32) { @@ -1938,7 +1932,7 @@ TEST(lstm_gpu, generic_lstm_chained_stacked_bidirectional_f32) { // chains = 2 // sequence length = 5 // output selection = output sequence and cell - lstm_gpu_chain_test(2, 2, 4, 2, 4, 2, 5, cldnn_lstm_output::cldnn_lstm_output_sequence_cell); + lstm_gpu_chain_test(2, 2, 4, 2, 4, 2, 5, lstm_output_selection::sequence_cell); } // FP16 Half precision tests @@ -2023,9 +2017,9 @@ TEST(lstm_gpu, generic_lstm_clip_input_forget_f16) { } TEST(lstm_gpu, generic_lstm_offset_order_ifoz_f16) { - default_offset_type = cldnn_lstm_offset_order_ifoz; + default_offset_type = lstm_weights_order::ifoz; generic_lstm_gpu_test(1, 7, 1, 3, 3, 2, true, true, true); - default_offset_type = cldnn_lstm_offset_order_iofz; + default_offset_type = lstm_weights_order::iofz; } TEST(lstm_gpu, generic_lstm_canonical_f16) { diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/max_unpooling_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/max_unpooling_gpu_test.cpp index afade14..6d9f7d2 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/max_unpooling_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/max_unpooling_gpu_test.cpp @@ -16,17 +16,17 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/max_unpooling.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/max_unpooling.hpp" +#include +#include +#include #include "test_utils/test_utils.h" -#include -#include -#include -#include +#include +#include +#include +#include #include "test_utils/float16.h" using namespace cldnn; @@ -391,7 +391,6 @@ TEST(max_unpooling_gpu, basic_in2x2x3x2_max_with_argmax_pooling_unpooling) { // f1: b0: 0 0 0 b1: 0 0 0 // f1: b0: 0 8 16 b1: 12 0 17 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 2, 3, 2 } }); diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/memory_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/memory_test.cpp index 75821bf..25a1884 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/memory_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/memory_test.cpp @@ -17,18 +17,18 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #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 "test_utils/test_utils.h" @@ -44,7 +44,7 @@ TEST(memory_tests, DISABLED_execution_loop) topology tpl{ input_layout("in", in.get_layout()), - activation("out", "in", activation_linear) + activation("out", "in", activation_func::linear) }; network net(eng, tpl); @@ -64,7 +64,7 @@ TEST(memory_tests, DISABLED_network_creation_loop) topology tpl{ input_layout("in", in.get_layout()), - activation("out", "in", activation_linear) + activation("out", "in", activation_func::linear) }; while (true) @@ -85,12 +85,12 @@ TEST(memory_pool, basic_non_padded_relu_pipe) { topology topology; topology.add(input_layout("input", input.get_layout())); - topology.add(activation("relu", "input", activation_relu)); - topology.add(activation("relu1", "relu", activation_relu)); - topology.add(activation("relu2", "relu1", activation_relu)); - topology.add(activation("relu3", "relu2", activation_relu)); - topology.add(activation("relu4", "relu3", activation_relu)); - topology.add(activation("relu5", "relu4", activation_relu)); + topology.add(activation("relu", "input", activation_func::relu)); + topology.add(activation("relu1", "relu", activation_func::relu)); + topology.add(activation("relu2", "relu1", activation_func::relu)); + topology.add(activation("relu3", "relu2", activation_func::relu)); + topology.add(activation("relu4", "relu3", activation_func::relu)); + topology.add(activation("relu5", "relu4", activation_func::relu)); std::vector input_vec = { -1.f, 2.f, -3.f, 4.f }; set_values(input, input_vec); @@ -101,10 +101,9 @@ TEST(memory_pool, basic_non_padded_relu_pipe) { network.set_input_data("input", input); auto outputs = network.execute(); - EXPECT_EQ(engine.get_max_used_device_memory_size(), (uint64_t) 80); + EXPECT_EQ(engine.get_max_used_device_memory_size(), (uint64_t) 64); } - TEST(memory_pool, basic_non_padded_relu_and_pooling_pipe) { // uncomment this line to disable memory pool /*engine_configuration cfg{ false, false, false, std::string(), std::string(), true, std::string(),std::string(), 0, false }; @@ -119,13 +118,13 @@ TEST(memory_pool, basic_non_padded_relu_and_pooling_pipe) { topology topology; topology.add(input_layout("input", input.get_layout())); - topology.add(activation("relu", "input", activation_relu)); - topology.add(activation("relu1", "relu", activation_relu)); + topology.add(activation("relu", "input", activation_func::relu)); + topology.add(activation("relu1", "relu", activation_func::relu)); topology.add(pooling("pool1", "relu1",pooling_mode::max, { 1,1,3,3 }, { 1,1,2,2 })); - topology.add(activation("relu2", "pool1", activation_relu)); - topology.add(activation("relu3", "relu2", activation_relu)); - topology.add(activation("relu4", "relu3", activation_relu)); - topology.add(activation("relu5", "relu4", activation_relu)); + topology.add(activation("relu2", "pool1", activation_func::relu)); + topology.add(activation("relu3", "relu2", activation_func::relu)); + topology.add(activation("relu4", "relu3", activation_func::relu)); + topology.add(activation("relu5", "relu4", activation_func::relu)); build_options bo; bo.set_option(build_option::optimize_data(true)); @@ -134,10 +133,9 @@ TEST(memory_pool, basic_non_padded_relu_and_pooling_pipe) { network.set_input_data("input", input); auto outputs = network.execute(); - EXPECT_EQ(engine.get_max_used_device_memory_size(), (uint64_t)1088); + EXPECT_EQ(engine.get_max_used_device_memory_size(), (uint64_t)896); } - TEST(memory_pool, multi_outputs_network) { // -- relu -- relu1 -- relu4 // input< @@ -157,14 +155,14 @@ TEST(memory_pool, multi_outputs_network) { topology topology; topology.add(input_layout("input", input.get_layout())); - topology.add(activation("relu", "input", activation_relu)); - topology.add(activation("relu1", "relu", activation_relu)); - topology.add(activation("relu2", "input", activation_relu)); - topology.add(activation("relu3", "relu2", activation_relu)); - topology.add(activation("relu4", "relu1", activation_relu)); - topology.add(activation("relu5", "relu3", activation_relu)); - topology.add(activation("relu6", "relu5", activation_relu)); - topology.add(activation("relu7", "relu6", activation_relu)); + topology.add(activation("relu", "input", activation_func::relu)); + topology.add(activation("relu1", "relu", activation_func::relu)); + topology.add(activation("relu2", "input", activation_func::relu)); + topology.add(activation("relu3", "relu2", activation_func::relu)); + topology.add(activation("relu4", "relu1", activation_func::relu)); + topology.add(activation("relu5", "relu3", activation_func::relu)); + topology.add(activation("relu6", "relu5", activation_func::relu)); + topology.add(activation("relu7", "relu6", activation_func::relu)); build_options bo; bo.set_option(build_option::optimize_data(true)); @@ -173,10 +171,9 @@ TEST(memory_pool, multi_outputs_network) { network.set_input_data("input", input); auto outputs = network.execute(); - EXPECT_EQ(engine.get_max_used_device_memory_size(), (uint64_t)2048); + EXPECT_EQ(engine.get_max_used_device_memory_size(), (uint64_t)1536); } - TEST(memory_pool, oooq) { /* -- relu1 - concat1- relu4 -- input< -- relu2 / >-- concat2 -- relu6 @@ -194,14 +191,14 @@ TEST(memory_pool, oooq) { topology topology; topology.add(input_layout("input", input.get_layout())); - topology.add(activation("relu1", "input", activation_relu)); - topology.add(activation("relu2", "input", activation_relu)); - topology.add(activation("relu3", "input", activation_relu)); + topology.add(activation("relu1", "input", activation_func::relu)); + topology.add(activation("relu2", "input", activation_func::relu)); + topology.add(activation("relu3", "input", activation_func::relu)); topology.add(concatenation("concat1", { "relu1", "relu2"},concatenation::along_f)); - topology.add(activation("relu4", "concat1", activation_relu)); - topology.add(activation("relu5", "relu3", activation_relu)); + topology.add(activation("relu4", "concat1", activation_func::relu)); + topology.add(activation("relu5", "relu3", activation_func::relu)); topology.add(concatenation("concat2", { "relu4", "relu5" }, concatenation::along_f)); - topology.add(activation("relu6", "concat2", activation_relu)); + topology.add(activation("relu6", "concat2", activation_func::relu)); build_options bo; bo.set_option(build_option::optimize_data(true)); @@ -210,7 +207,7 @@ TEST(memory_pool, oooq) { network.set_input_data("input", input); auto outputs = network.execute(); - EXPECT_EQ(engine.get_max_used_device_memory_size(), (uint64_t) 2816); + EXPECT_EQ(engine.get_max_used_device_memory_size(), (uint64_t) 2560); } TEST(memory_pool, shared_mem_pool_same_topology_twice) { @@ -237,14 +234,14 @@ TEST(memory_pool, shared_mem_pool_same_topology_twice) { topology topology; topology.add(input_layout("input", input.get_layout())); - topology.add(activation("relu1", "input", activation_relu)); - topology.add(activation("relu2", "input", activation_sqrt)); - topology.add(activation("relu3", "input", activation_square)); + topology.add(activation("relu1", "input", activation_func::relu)); + topology.add(activation("relu2", "input", activation_func::sqrt)); + topology.add(activation("relu3", "input", activation_func::square)); topology.add(concatenation("concat1", { "relu1", "relu2" }, concatenation::along_f)); - topology.add(activation("relu4", "concat1", activation_relu)); - topology.add(activation("relu5", "relu3", activation_relu)); + topology.add(activation("relu4", "concat1", activation_func::relu)); + topology.add(activation("relu5", "relu3", activation_func::relu)); topology.add(concatenation("concat2", { "relu4", "relu5" }, concatenation::along_f)); - topology.add(activation("relu6", "concat2", activation_linear, {1.0f, 0.5f})); + topology.add(activation("relu6", "concat2", activation_func::linear, {1.0f, 0.5f})); build_options bo; bo.set_option(build_option::optimize_data(true)); @@ -257,7 +254,7 @@ TEST(memory_pool, shared_mem_pool_same_topology_twice) { auto output_layout_first = output_memory_first.get_layout(); auto output_ptr_first = output_memory_first.pointer(); - EXPECT_EQ(engine.get_max_used_device_memory_size(), (uint64_t) 2816); + EXPECT_EQ(engine.get_max_used_device_memory_size(), (uint64_t) 2560); network network_second(engine, topology, bo); network_second.set_input_data("input", input); @@ -267,7 +264,7 @@ TEST(memory_pool, shared_mem_pool_same_topology_twice) { auto output_layout_second = output_memory_second.get_layout(); auto output_ptr_second = output_memory_second.pointer(); - EXPECT_EQ(engine.get_max_used_device_memory_size(), (uint64_t) 3584); + EXPECT_EQ(engine.get_max_used_device_memory_size(), (uint64_t) 3328); EXPECT_EQ(output_layout_first, output_layout_second); int y_size = output_layout_first.size.spatial[1]; @@ -365,7 +362,6 @@ TEST(memory_pool, shared_mem_pool_same_topology_twice_weights) { } } - TEST(memory_pool, shared_mem_pool_diff_batches) { engine_configuration cfg{ false, false, false, std::string(), std::string(), true /*oooq*/, std::string(),std::string(), priority_mode_types::disabled, throttle_mode_types::disabled, true /*mem_pool*/ }; @@ -524,16 +520,15 @@ TEST(memory_pool, add_mem_dep_test) { 5.0f, 6.0f, 7.0f, 8.0f}); set_values(scale_memory, { 1.0f }); - auto input = cldnn::input_layout("input1", input_layout1); - auto actv1 = cldnn::activation("input_activ1", "input1", cldnn_activation_func::activation_abs); - auto actv2 = cldnn::activation("input_activ2", "input1", cldnn_activation_func::activation_abs); + auto actv1 = cldnn::activation("input_activ1", "input1", activation_func::abs); + auto actv2 = cldnn::activation("input_activ2", "input1", activation_func::abs); auto crop1 = cldnn::crop("crop1", "input_activ1", { 1,1,2,2 }, { 0, 0, 0, 0 }); auto crop2 = cldnn::crop("crop2", "input_activ2", { 1,1,2,2 }, { 0, 1, 0, 0 }); auto eltwise1 = cldnn::scale("elt1", "crop1", "scale_mem"); auto eltwise2 = cldnn::scale("elt2", "crop2", "scale_mem"); - auto actv3 = cldnn::activation("out3", "elt1", cldnn_activation_func::activation_abs); - auto actv4 = cldnn::activation("out4", "elt2", cldnn_activation_func::activation_abs); + auto actv3 = cldnn::activation("out3", "elt1", activation_func::abs); + auto actv4 = cldnn::activation("out4", "elt2", activation_func::abs); auto topology = cldnn::topology( input, diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/mvn_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/mvn_gpu_test.cpp index da2cc38..5cf057b 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/mvn_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/mvn_gpu_test.cpp @@ -17,13 +17,13 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include "api/CPP/mvn.hpp" -#include "api/CPP/reorder.hpp" -#include -#include -#include +#include +#include +#include "api/mvn.hpp" +#include "api/reorder.hpp" +#include +#include +#include #include "test_utils/test_utils.h" #include #include "float16.h" diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/one_hot_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/one_hot_gpu_test.cpp index 0e0a089..5bd22ac 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/one_hot_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/one_hot_gpu_test.cpp @@ -15,12 +15,12 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" #include "test_utils/uniform_quantized_real_distribution.hpp" diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/permute_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/permute_gpu_test.cpp index 800469d..31fbc03 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/permute_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/permute_gpu_test.cpp @@ -16,18 +16,18 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/permute.hpp" -#include "api/CPP/reorder.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/permute.hpp" +#include "api/reorder.hpp" +#include +#include +#include #include "test_utils/test_utils.h" -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include @@ -36,12 +36,10 @@ using namespace cldnn; using namespace tests; using namespace testing; - TEST(permute_gpu_f32, output_ordering_test) { const auto& engine = get_test_engine(); - std::vector> input_tensors = { { 10, 5, 15, 2 },{ 2, 4, 6, 8 },{ 2, 2, 3, 2 },{ 9, 8, 7, 4 } @@ -108,7 +106,6 @@ TEST(permute_gpu_f32, basic_bfyx_permute_0_1_2_3) // // Output = input - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 2, 3, 2 } }); @@ -143,7 +140,6 @@ TEST(permute_gpu_f32, basic_bfyx_permute_0_1_2_3) auto output = outputs.begin()->second.get_memory(); - auto output_ptr = output.pointer(); for (int i = 0; i < 24; i++) { diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/pooling_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/pooling_gpu_test.cpp index 8488477..e9b1487 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/pooling_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/pooling_gpu_test.cpp @@ -16,16 +16,16 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/pooling.hpp" -#include "api/CPP/mutable_data.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/pooling.hpp" +#include "api/mutable_data.hpp" +#include +#include +#include #include "test_utils/test_utils.h" -#include "api/CPP/reorder.hpp" -#include +#include "api/reorder.hpp" +#include #include "test_utils/float16.h" using namespace cldnn; @@ -1100,7 +1100,6 @@ TEST(pooling_forward_gpu, basic_in2x2x3x2_max_with_argmax) { // f0: b0: 4 4 b1: 15 13 // f1: b0: 10 11 b1: 21 23 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 2, 3, 2 } }); @@ -1178,7 +1177,6 @@ TEST(pooling_forward_gpu, basic_in2x2x3x2x1_max_with_argmax) { // f0: b0: 4 4 b1: 15 13 // f1: b0: 10 11 b1: 21 23 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::bfzyx,{ 2, 2, 3, 2, 1 } }); @@ -1238,7 +1236,6 @@ TEST(pooling_forward_gpu, basic_in2x2x3x2x1_max_with_argmax) { } } - TEST(pooling_forward_gpu, basic_in2x2x3x2_max_with_argmax_input_padding) { // Input : 2x2x3x2 // Argmax : 2x2x2x1 @@ -1259,7 +1256,6 @@ TEST(pooling_forward_gpu, basic_in2x2x3x2_max_with_argmax_input_padding) { // f0: b0: 4 4 b1: 15 13 // f1: b0: 10 11 b1: 21 23 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 2, 3, 2 } }); @@ -1339,7 +1335,6 @@ TEST(pooling_forward_gpu, basic_in2x2x3x2_max_with_argmax_output_padding) { // f0: b0: 4 4 b1: 15 13 // f1: b0: 10 11 b1: 21 23 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 2, 3, 2 } }); @@ -1429,7 +1424,6 @@ TEST(pooling_forward_gpu, basic_in2x2x3x2_max_with_argmax_with_output_size) { // f0: b0: 4 4 b1: 15 13 // f1: b0: 10 11 b1: 21 23 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::bfyx,{ 2, 2, 3, 2 } }); @@ -2006,8 +2000,6 @@ TEST(pooling_forward_gpu, fs_b_yx_fsv32_max_1x1x3x3_input_2x2_pool_2x2_stride_2x // [0, 1, -0.5, 0, 0] // [0, 0, 0, 0, 0] - - tensor input_tensor(1, 1, 3, 3); auto input_prim = memory::allocate(engine, { data_types::f16, format::bfyx, input_tensor }); @@ -2025,7 +2017,6 @@ TEST(pooling_forward_gpu, fs_b_yx_fsv32_max_1x1x3x3_input_2x2_pool_2x2_stride_2x FLOAT16(-1.00f), FLOAT16(-1.00f), FLOAT16(-0.50f) }); - network.set_input_data("input_prim", input_prim); std::vector expected = { @@ -2214,11 +2205,7 @@ public: { delete generic_params; } - - for (auto layer_params : all_layer_params) - { - delete layer_params; - } + all_layer_params.clear(); } static tensor generate_input_offset(int x, int y, const tensor& window_size) @@ -2226,7 +2213,7 @@ public: return tensor(0, 0, -std::min(x, window_size.spatial[0] - 1), -std::min(y, window_size.spatial[1] - 1)); } - static std::vector generate_specific_test_params() + static std::vector> generate_specific_test_params() { std::vector pooling_modes = { pooling_mode::max, pooling_mode::average, pooling_mode::average_no_padding }; @@ -2241,23 +2228,23 @@ public: for (auto stride : strides) { // No padding - all_layer_params.push_back(new pooling("pooling", "input0", pooling_mode, size, stride)); - all_layer_params.push_back(new pooling("pooling", "input0", pooling_mode, size, stride, generate_input_offset(4, 3, size))); + all_layer_params.emplace_back(new pooling("pooling", "input0", pooling_mode, size, stride)); + all_layer_params.emplace_back(new pooling("pooling", "input0", pooling_mode, size, stride, generate_input_offset(4, 3, size))); // Input padding - all_layer_params.push_back(new pooling("pooling", "reorder0", pooling_mode, size, stride)); + all_layer_params.emplace_back(new pooling("pooling", "reorder0", pooling_mode, size, stride)); // Output padding - all_layer_params.push_back(new pooling("pooling", "input0", pooling_mode, size, stride, generate_input_offset(2, 3, size), { { 0, 0, 1, 5 },{ 0, 0, 19, 4 } })); + all_layer_params.emplace_back(new pooling("pooling", "input0", pooling_mode, size, stride, generate_input_offset(2, 3, size), { { 0, 0, 1, 5 },{ 0, 0, 19, 4 } })); // Input + output padding - all_layer_params.push_back(new pooling("pooling", "reorder0", pooling_mode, size, stride, generate_input_offset(2, 3, size), { { 0, 0, 2, 1 },{ 0, 0, 3, 4 } })); + all_layer_params.emplace_back(new pooling("pooling", "reorder0", pooling_mode, size, stride, generate_input_offset(2, 3, size), { { 0, 0, 2, 1 },{ 0, 0, 3, 4 } })); } } } // This case tests the pooling_gpu_bfyx_average_opt kernel. - all_layer_params.push_back(new pooling("pooling", "input0", pooling_mode::average, tensor(1, 1, 3, 3), tensor(1, 1, 1, 1), generate_input_offset(1, 1, tensor(1, 1, 3, 3)))); + all_layer_params.emplace_back(new pooling("pooling", "input0", pooling_mode::average, tensor(1, 1, 3, 3), tensor(1, 1, 1, 1), generate_input_offset(1, 1, tensor(1, 1, 3, 3)))); return all_layer_params; } @@ -2301,7 +2288,7 @@ public: virtual cldnn::tensor get_expected_output_tensor() { - const cldnn::pooling* pooling = (cldnn::pooling*)layer_params; + auto pooling = std::static_pointer_cast(layer_params); int batch = generic_params->input_layouts[0].size.batch[0]; int feature = generic_params->input_layouts[0].size.feature[0]; @@ -2336,15 +2323,13 @@ public: template memory generate_reference_typed(const std::vector& inputs) { - const cldnn::pooling* pooling = (cldnn::pooling*)layer_params; + auto pooling = std::static_pointer_cast(layer_params); int batch = inputs[0].get_layout().size.batch[0]; int feature = inputs[0].get_layout().size.feature[0]; int height = inputs[0].get_layout().size.spatial[1]; int width = inputs[0].get_layout().size.spatial[0]; - - cldnn::pooling_mode pooling_mode = pooling->mode; int input_offset_width = pooling->input_offset.spatial[0]; @@ -2518,11 +2503,11 @@ public: private: static std::vector all_generic_params; - static std::vector all_layer_params; + static std::vector> all_layer_params; }; -std::vector pooling_test::all_layer_params = {}; +std::vector> pooling_test::all_layer_params = {}; std::vector pooling_test::all_generic_params = {}; TEST_P(pooling_test, POOLING) diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/propagate_constants_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/propagate_constants_gpu_test.cpp index fee9d7f..8d6f1e9 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/propagate_constants_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/propagate_constants_gpu_test.cpp @@ -17,16 +17,16 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include +#include +#include #include "test_utils/test_utils.h" -#include -#include -#include -#include +#include +#include +#include +#include using namespace cldnn; using namespace tests; diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/proposal_cpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/proposal_cpu_test.cpp index c1b818d..2e40ec0 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/proposal_cpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/proposal_cpu_test.cpp @@ -18,12 +18,12 @@ #include #include -#include "api/CPP/memory.hpp" -#include -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" #include "test_utils/float16.h" diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/proposal_test_data.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/proposal_test_data.cpp index 2f2e083..3c138f3 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/proposal_test_data.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/proposal_test_data.cpp @@ -25,7 +25,6 @@ // post nms topn: 150 -> 25 // !!!!!!!! - float cls_scores_data[] = { 0.999760f, 0.997614f, 0.999854f, 0.996280f, 0.994689f, 0.999543f, 0.999865f, // 0 0.999969f, 0.999885f, 0.999879f, 0.999758f, 0.999719f, 0.999626f, 0.999386f, // 7 @@ -859,7 +858,6 @@ float cls_scores_data[] = { size_t cls_scores_data_size = sizeof(cls_scores_data) / sizeof(cls_scores_data[0]); - float bbox_pred_data[] = { 0.006756f, 0.062491f, 0.113831f, 0.063944f, 0.024297f, 0.009997f, -0.043972f, // 0 -0.051204f, -0.036587f, -0.048956f, -0.021944f, -0.011054f, -0.023826f, -0.003094f, // 7 @@ -2519,7 +2517,6 @@ float bbox_pred_data[] = { -0.032304f, -0.061007f, 0.021732f, 0.020398f, -0.115368f, -0.094854f, -0.119841f, // 11585 }; - size_t bbox_pred_data_size = sizeof(bbox_pred_data) / sizeof(bbox_pred_data[0]); float proposal_ref[] = { diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/pyramid_roi_align_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/pyramid_roi_align_gpu_test.cpp index db7a9d2..85cb3af 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/pyramid_roi_align_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/pyramid_roi_align_gpu_test.cpp @@ -15,17 +15,16 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" - using namespace cldnn; using namespace tests; diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/quantize_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/quantize_gpu_test.cpp index 2cce341..b384a32 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/quantize_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/quantize_gpu_test.cpp @@ -15,24 +15,22 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" #include -#include +#include #include - using namespace cldnn; using namespace ::tests; - TEST(quantize_gpu, quantize_levels_2_output_broadcast_inputs_1) { const auto& engine = get_test_engine(); auto input = memory::allocate(engine, {data_types::f32, format::bfyx, {1, 16, 2, 2}}); @@ -69,29 +67,29 @@ TEST(quantize_gpu, quantize_levels_2_output_broadcast_inputs_1) { 4.0f, 5.0f, 6.0f, 7.0f, 7.0f, 6.0f, 5.0f, 4.0f, 3.0f, 2.0f, 1.0f, 0.0f }); - set_values(output_low, { 0.0f }); - set_values(output_high, { 1.0f }); + set_values(output_low, { -1.0f }); + set_values(output_high, { 1.0f }); // 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 // 1 1 1 1 0 1 0 0 0 0 1 1 0 1 1 1 // 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1 // 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1 - std::vector ref_data = { 0, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - 0, 1, 0, 0, - 0, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 1, 0, 0, - 0, 1, 0, 0, - 0, 0, 0, 0, - 1, 1, 1, 1, - 1, 1, 0, 0, - 1, 1, 1, 1 }; + std::vector ref_data = { -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, 1, -1, -1, + -1, 1, -1, -1, + -1, -1, -1, -1, + 1, 1, 1, 1, + 1, 1, -1, -1, + 1, 1, 1, 1 }; topology topology; topology.add( @@ -99,9 +97,7 @@ TEST(quantize_gpu, quantize_levels_2_output_broadcast_inputs_1) { data("input_low", input_low), data("input_high", input_high), data("output_low", output_low), - data("output_high", output_high) - ); - topology.add( + data("output_high", output_high), quantize("quantize", "input", "input_low", "input_high", "output_low", "output_high", 2) ); @@ -123,6 +119,136 @@ TEST(quantize_gpu, quantize_levels_2_output_broadcast_inputs_1) { } } +TEST(quantize_gpu, quantize_levels_2_output_broadcast_inputs_1_ch8) { + const auto& engine = get_test_engine(); + auto input = memory::allocate(engine, {data_types::f32, format::bfyx, {1, 8, 2, 2}}); + auto input_thresh = memory::allocate(engine, { data_types::f32,format::bfyx,{ 1, 8, 1, 1 } }); + auto output_low = memory::allocate(engine, { data_types::f32,format::bfyx,{ 1, 1, 1, 1 } }); + auto output_high = memory::allocate(engine, { data_types::f32,format::bfyx,{ 1, 1, 1, 1 } }); + + set_values(input, { -1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 2.0f, 2.0f, 3.0f, + 4.0f, 6.0f, 3.0f, 3.0f, + 3.0f, 5.0f, 1.0f, 1.0f, + + 1.0f, 1.0f, 1.0f, 1.0f, + 4.0f, 6.0f, 3.0f, 3.0f, + 3.0f, 5.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f }); + + set_values(input_thresh, { 0.0f, 1.0f, 2.0f, 3.0f, + 4.0f, 5.0f, 6.0f, 7.0f }); + + set_values(output_low, { -1.0f }); + set_values(output_high, { 1.0f }); + + // 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 + // 1 1 1 1 0 1 0 0 0 0 1 1 0 1 1 1 + // 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1 + // 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1 + std::vector ref_data = { -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 }; + + topology topology; + topology.add( + input_layout("input", input.get_layout()), + data("input_low", input_thresh), + data("input_high", input_thresh), + data("output_low", output_low), + data("output_high", output_high), + quantize("quantize", "input", "input_low", "input_high", "output_low", "output_high", 2) + ); + + network network(engine, topology); + network.set_input_data("input", input); + auto outputs = network.execute(); + + auto output = outputs.at("quantize").get_memory(); + auto output_ptr = output.pointer(); + + // Check that layout and memory contains logical size of tensor + ASSERT_EQ(output.count(), (size_t)32); + ASSERT_EQ(output.get_layout().count(), (size_t)32); + + ASSERT_EQ(output.size(), ref_data.size() * sizeof(uint32_t)); + + for (size_t i = 0; i < ref_data.size(); ++i) { + EXPECT_EQ(output_ptr[i], ref_data[i]) << " index = " << i; + } +} + +TEST(quantize_gpu, quantize_levels_2_output_broadcast_inputs_1_ch8_binary_pack) { + const auto& engine = get_test_engine(); + auto input = memory::allocate(engine, {data_types::f32, format::bfyx, {1, 8, 2, 2}}); + auto input_thresh = memory::allocate(engine, { data_types::f32,format::bfyx,{ 1, 8, 1, 1 } }); + auto output_low = memory::allocate(engine, { data_types::f32,format::bfyx,{ 1, 1, 1, 1 } }); + auto output_high = memory::allocate(engine, { data_types::f32,format::bfyx,{ 1, 1, 1, 1 } }); + + set_values(input, { -1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 2.0f, 2.0f, 3.0f, + 4.0f, 6.0f, 3.0f, 3.0f, + 3.0f, 5.0f, 1.0f, 1.0f, + + 1.0f, 1.0f, 1.0f, 1.0f, + 4.0f, 6.0f, 3.0f, 3.0f, + 3.0f, 5.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f }); + + set_values(input_thresh, { 0.0f, 1.0f, 2.0f, 3.0f, + 4.0f, 5.0f, 6.0f, 7.0f }); + set_values(output_low, { -1.0f }); + set_values(output_high, { 1.0f }); + + // 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 + // 1 1 1 1 0 1 0 0 0 0 1 1 0 1 1 1 + // 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1 + // 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1 + std::vector ref_data = { -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 }; + + topology topology; + topology.add( + input_layout("input", input.get_layout()), + data("input_low", input_thresh), + data("input_high", input_thresh), + data("output_low", output_low), + data("output_high", output_high), + quantize("quantize", "input", "input_low", "input_high", "output_low", "output_high", 2), + reorder("reorder", "quantize", layout{data_types::f32, format::bfyx, tensor{1,8,2,2}}) + ); + + build_options bo; + bo.set_option(build_option::optimize_data(true)); + network network(engine, topology, bo); + network.set_input_data("input", input); + auto outputs = network.execute(); + + auto output = outputs.at("reorder").get_memory(); + auto output_ptr = output.pointer(); + + // Check that layout and memory contains logical size of tensor + ASSERT_EQ(output.count(), (size_t)32); + ASSERT_EQ(output.get_layout().count(), (size_t)32); + + ASSERT_EQ(output.size(), ref_data.size() * sizeof(uint32_t)); + + for (size_t i = 0; i < ref_data.size(); ++i) { + EXPECT_EQ(output_ptr[i], ref_data[i]) << " index = " << i; + } +} + TEST(quantize_gpu, quantize_levels_2_output_broadcast_inputs_2) { const cldnn::engine& engine = get_test_engine(); auto input = memory::allocate(engine, {data_types::f32, format::bfyx, {1, 16, 2, 2}}); @@ -153,25 +279,25 @@ TEST(quantize_gpu, quantize_levels_2_output_broadcast_inputs_2) { set_values(input_low, { 4.0f }); set_values(input_high, { 4.0f }); - set_values(output_low, { 0.0f }); - set_values(output_high, { 1.0f }); - - std::vector ref_data = { 0, 0, 0, 0, - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 1, 0, 0, - 0, 0, 0, 0, - 0, 1, 0, 0, - 0, 1, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 1, 0, 0, - 0, 0, 0, 0, - 0, 1, 0, 0, - 0, 1, 0, 0, - 0, 0, 0, 0 }; + set_values(output_low, { -1.0f }); + set_values(output_high, { 1.0f }); + + std::vector ref_data = { -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, 1, -1, -1, + -1, 1, -1, -1, + -1, -1, -1, -1, + -1, 1, -1, -1, + -1, 1, -1, -1, + -1, -1, -1, -1 }; topology topology; topology.add( @@ -179,9 +305,7 @@ TEST(quantize_gpu, quantize_levels_2_output_broadcast_inputs_2) { data("input_low", input_low), data("input_high", input_high), data("output_low", output_low), - data("output_high", output_high) - ); - topology.add( + data("output_high", output_high), quantize("quantize", "input", "input_low", "input_high", "output_low", "output_high", 2) ); @@ -270,9 +394,7 @@ TEST(quantize_gpu, quantize_levels_3) { data("input_low", input_low), data("input_high", input_high), data("output_low", output_low), - data("output_high", output_high) - ); - topology.add( + data("output_high", output_high), quantize("quantize", "input", "input_low", "input_high", "output_low", "output_high", 3) ); diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/reduce_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/reduce_gpu_test.cpp index d8e77cd..0b65ef1 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/reduce_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/reduce_gpu_test.cpp @@ -16,13 +16,13 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include "api/CPP/reduce.hpp" -#include -#include -#include +#include +#include "api/reduce.hpp" +#include +#include +#include #include "test_utils/test_utils.h" -#include +#include #include "test_utils/float16.h" using namespace cldnn; diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/removing_output_node_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/removing_output_node_test.cpp index 2cf9b3e..c6c2577 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/removing_output_node_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/removing_output_node_test.cpp @@ -16,15 +16,15 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include +#include +#include +#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" @@ -141,7 +141,7 @@ TEST(removing_output_node, output_node_optimization) { topology.add(input_layout("input", input.get_layout())); topology.add(data("weights", weights)); topology.add(convolution("conv", "input", { "weights" }, { 1,1,1,2 })); - topology.add(activation("relu", "conv", activation_relu)); + topology.add(activation("relu", "conv", activation_func::relu)); network network(engine, topology); network.set_input_data("input", input); diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/reorder_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/reorder_gpu_test.cpp index bd88346..1b07127 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/reorder_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/reorder_gpu_test.cpp @@ -16,16 +16,16 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/reorder.hpp" -#include "api/CPP/crop.hpp" -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/reorder.hpp" +#include "api/crop.hpp" +#include +#include +#include +#include #include "test_utils/test_utils.h" -#include +#include #include #include @@ -560,7 +560,6 @@ TEST(reorder_gpu, basic_convert_f16_f32_f16) { } } - TEST(reorder_gpu, basic_convert_int8) { const auto& engine = get_test_engine(); @@ -593,7 +592,7 @@ TEST(reorder_gpu, basic_convert_int8) { engine, topology, build_options{ - build_option::outputs({ "reorder2"}) + build_option::outputs({ "reorder_input", "reorder2"}) }); network.set_input_data("input", input_memory); @@ -772,7 +771,7 @@ TEST(reorder_gpu_f32, basic_yxfb_to_bfyx_input_padding) topology topology( input_layout("input", input.get_layout()), - reorder("reorder", "input", input.get_layout().format, input.get_layout().data_type, "", cldnn_reorder_mean_mode::mean_subtract, padding{ { 0, 0, 1, 2 }, 0 }), + reorder("reorder", "input", input.get_layout().format, input.get_layout().data_type, "", reorder_mean_mode::subtract, padding{ { 0, 0, 1, 2 }, 0 }), reorder("reorder2", "reorder", output_layout)); network network(engine, topology); @@ -851,7 +850,7 @@ TEST(reorder_gpu_f32, basic_bfyx_to_yxfb_input_padding) topology topology( input_layout("input", input.get_layout()), - reorder("reorder", "input", input.get_layout().format, input.get_layout().data_type, "", cldnn_reorder_mean_mode::mean_subtract, padding{ { 0, 0, 2, 1 }, 0 }), + reorder("reorder", "input", input.get_layout().format, input.get_layout().data_type, "", reorder_mean_mode::subtract, padding{ { 0, 0, 2, 1 }, 0 }), reorder("reorder2", "reorder", output_layout)); network network(engine, topology); @@ -1137,7 +1136,7 @@ TEST(reorder_gpu_opt, remove_redundant_activation_fuse) topology tpl{ input_layout("in", in.get_layout()), reorder("r1", "in", format::bfyx, data_types::f32), - activation("relu", "r1", cldnn_activation_func::activation_relu_negative_slope, {0.01f, 0.0f}), + activation("relu", "r1", activation_func::relu_negative_slope, {0.01f, 0.0f}), data("scale_data", scale_mem), scale("output", "relu", "scale_data") }; @@ -1266,7 +1265,6 @@ TEST(reorder_gpu_opt, non_trivial_remove_redundant) EXPECT_TRUE(outputs.at("r1").get_memory().get_layout().format == format::bfyx); } - TEST(reorder_gpu_opt, mean_mul) { engine eng; @@ -1284,7 +1282,7 @@ TEST(reorder_gpu_opt, mean_mul) topology tpl{ input_layout("in", in.get_layout()), data("mul",mul), - reorder("r1", "in", format::bfyx, data_types::f32,"mul", cldnn_reorder_mean_mode::mean_mul) + reorder("r1", "in", format::bfyx, data_types::f32,"mul", reorder_mean_mode::mul) }; float answers[] = { 0.5f, 5.0f, -15.0f, 17.2f, 6.0f, -21.0f }; @@ -1302,7 +1300,6 @@ TEST(reorder_gpu_opt, mean_mul) } - TEST(reorder_gpu_opt, mean_div) { engine eng; @@ -1320,7 +1317,7 @@ TEST(reorder_gpu_opt, mean_div) topology tpl{ input_layout("in", in.get_layout()), data("mul",mul), - reorder("r1", "in", format::bfyx, data_types::f32,"mul", cldnn_reorder_mean_mode::mean_div) + reorder("r1", "in", format::bfyx, data_types::f32,"mul", reorder_mean_mode::div) }; float answers[] = { 2.0f, 1.0f, -1.0f, 0.5f, 4.0f, -2.0f }; @@ -1338,7 +1335,6 @@ TEST(reorder_gpu_opt, mean_div) } - TEST(reorder_gpu_opt, mean_mul_val) { engine eng; @@ -1352,7 +1348,7 @@ TEST(reorder_gpu_opt, mean_mul_val) std::vector mul_val = { 2.0f, 0.5f, 10.0f }; topology tpl{ input_layout("in", in.get_layout()), - reorder("r1", "in", format::bfyx, data_types::f32, mul_val, cldnn_reorder_mean_mode::mean_mul) + reorder("r1", "in", format::bfyx, data_types::f32, mul_val, reorder_mean_mode::mul) }; float answers[] = { 2.0f, 4.0f, 1.5f, 2.0f, 50.0f, 600.0f }; @@ -1369,7 +1365,6 @@ TEST(reorder_gpu_opt, mean_mul_val) EXPECT_FLOAT_EQ(*(a_ptr++), val);; } - TEST(reorder_gpu_opt, mean_mul_val_float_to_int) { engine eng; @@ -1383,7 +1378,7 @@ TEST(reorder_gpu_opt, mean_mul_val_float_to_int) std::vector mul_val = { 1.4f, 0.5f, 5.0f }; topology tpl{ input_layout("in", in.get_layout()), - reorder("r1", "in", format::bfyx, data_types::i8, mul_val, cldnn_reorder_mean_mode::mean_mul) + reorder("r1", "in", format::bfyx, data_types::i8, mul_val, reorder_mean_mode::mul) }; char answers[] = { 0, 2, 1, 2, 25, 127 }; @@ -1482,7 +1477,7 @@ TEST(reorder_gpu_i64, basic) EXPECT_EQ(*(a_ptr++), val); } -TEST(reorder_gpu_binary, basic) +TEST(reorder_gpu_binary, binary_output) { const auto& engine = get_test_engine(); @@ -1530,6 +1525,53 @@ TEST(reorder_gpu_binary, basic) } } +TEST(reorder_gpu_binary, binary_input) +{ + const auto& engine = get_test_engine(); + + cldnn::build_options options; + options.set_option(cldnn::build_option::optimize_data(true)); + + auto input = memory::allocate(engine, { data_types::bin, format::b_fs_yx_32fp,{ 2, 2, 2, 2 } }); + layout output_layout(data_types::f32, format::bfyx, { 2, 2, 2, 2 }); + + // Data is supposed to be quantized to {0,1} values + std::vector answers = { + 1.f, -1.f, 1.f, 1.f, + -1.f, 1.f, 1.f, -1.f, + + 1.f, 1.f, -1.f, 1.f, + -1.f, -1.f, -1.f, 1.f + }; + + set_values(input, { 1, 2, 3, 1, + 1, 1, 0, 3 }); + + topology topology( + input_layout("input", input.get_layout()), + reorder("reorder", "input", output_layout)); + + network network(engine, topology); + network.set_input_data("input", input); + + auto outputs = network.execute(); + EXPECT_EQ(outputs.size(), size_t(1)); + EXPECT_EQ(outputs.begin()->first, "reorder"); + + auto output = outputs.begin()->second.get_memory(); + auto output_ptr = output.pointer(); + + // Check that layout and memory contains logical size of tensor + ASSERT_EQ(output.count(), input.get_layout().count()); + ASSERT_EQ(output.get_layout().count(), input.get_layout().count()); + + ASSERT_EQ(output.size(), answers.size() * sizeof(float)); + + for (size_t i = 0; i < answers.size(); ++i) { + EXPECT_EQ(answers[i], output_ptr[i]) << "index: " << i; + } +} + TEST(reorder_gpu_f32, bfwzyx_bfyx_chain) { // Topology: @@ -1607,6 +1649,30 @@ TEST(reorder_gpu_f32, bfwzyx_bfyx_chain) } } +TEST(reorder_gpu, any_format) { + auto& engine = get_test_engine(); + + auto input = memory::allocate(engine, layout(data_types::f32, format::yxfb, tensor(5, 7, 13, 9))); + + topology topo; + topo.add(input_layout("in", input.get_layout())); + topo.add(reorder("out", "in", format::any, data_types::f32)); + + network net(engine, topo); + + auto data = generate_random_1d(input.count(), -1, 1); + set_values(input, data); + net.set_input_data("in", input); + + auto outputs = net.execute(); + auto out_mem = outputs.at("out").get_memory(); + auto output = out_mem.pointer(); + + for (size_t i = 0; i < data.size(); ++i) { + EXPECT_EQ(output[i], data[i]) << "i = " << i; + } +} + using namespace cldnn; class reorder_test : public tests::generic_test @@ -1620,15 +1686,10 @@ public: { delete generic_params; } - for (auto test_param : all_test_params) - { - auto primitive = std::get<1>(test_param); - delete primitive; - } + all_test_params.clear(); } - - static std::vector> generate_specific_test_params() + static std::vector>> generate_specific_test_params() { generic_test::generate_generic_test_params(all_generic_params); @@ -1655,7 +1716,7 @@ public: for (const auto& output_layout : output_layouts) { //TODO: check input + output padding. - all_test_params.push_back(std::make_tuple(test_param, new reorder("reorder", "input0", output_layout, subtract))); + all_test_params.emplace_back(std::make_tuple(test_param, std::make_shared("reorder", "input0", output_layout, subtract))); } } @@ -1675,7 +1736,7 @@ public: template memory generate_reference_typed(const std::vector& inputs) { - const cldnn::reorder* reorder = (cldnn::reorder*)layer_params; + auto reorder = std::static_pointer_cast(layer_params); primitive_id mean = reorder->mean; std::vector subtract_per_feature = reorder->subtract_per_feature; assert(mean == ""); @@ -1725,12 +1786,12 @@ public: private: static std::vector all_generic_params; - static std::vector> all_test_params; + static std::vector>> all_test_params; }; std::vector reorder_test::all_generic_params = {}; -std::vector> reorder_test::all_test_params = {}; +std::vector>> reorder_test::all_test_params = {}; TEST_P(reorder_test, REORDER) { diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/reshape_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/reshape_gpu_test.cpp index 1538e53..d39a5b9 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/reshape_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/reshape_gpu_test.cpp @@ -16,13 +16,13 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include +#include +#include +#include -#include -#include -#include +#include +#include +#include #include "test_utils/test_utils.h" @@ -458,11 +458,11 @@ TEST(reshape_gpu_f32, multiple_users_with_reorder) { topology topology; topology.add(input_layout("input", input.get_layout())); - topology.add(activation("relu", "input", activation_relu)); + topology.add(activation("relu", "input", activation_func::relu)); topology.add(reshape("reshape", "relu", tensor(batch(4)))); topology.add(reorder("reorder1", "reshape", format::yxfb, data_types::f32)); - topology.add(activation("relu1", "reorder1", activation_relu)); - topology.add(activation("relu2", "reshape", activation_relu)); + topology.add(activation("relu1", "reorder1", activation_func::relu)); + topology.add(activation("relu2", "reshape", activation_func::relu)); std::vector input_vec = {-1.f, 2.f, -3.f, 4.f}; std::vector out1 = {0.f, 0.f, 2.f, 4.0f}; diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/reverse_sequence_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/reverse_sequence_gpu_test.cpp index 64d8b24..ddb5edf 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/reverse_sequence_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/reverse_sequence_gpu_test.cpp @@ -12,16 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. - - /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/scale_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/scale_gpu_test.cpp index 78908b1..d1cc8ff 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/scale_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/scale_gpu_test.cpp @@ -16,14 +16,14 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/scale.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/scale.hpp" +#include +#include +#include #include "test_utils/test_utils.h" -#include "api/CPP/reorder.hpp" +#include "api/reorder.hpp" #include @@ -1193,7 +1193,6 @@ TEST(scale_gpu, basic_in2x2x2x3x2_scale_same_size_bfzyx) { // Input : 2x2x2x3x2 // Output : 2x2x2x3x2 - const auto& engine = get_test_engine(); auto input = memory::allocate(engine, { data_types::f32, format::bfzyx,{ 2, 2, 2, 3, 2 } }); @@ -1507,14 +1506,14 @@ public: } //TODO: use an enum instead of int i - static std::vector generate_specific_test_params(int variant) + static std::vector> generate_specific_test_params(int variant) { - std::vector all_layer_params; + std::vector> all_layer_params; switch(variant) { - case 0: all_layer_params.push_back(new scale("scale", "input0", "input1")); break; - case 1: all_layer_params.push_back(new scale("scale", "input0", "input1", "input2")); break; + case 0: all_layer_params.emplace_back(new scale("scale", "input0", "input1")); break; + case 1: all_layer_params.emplace_back(new scale("scale", "input0", "input1", "input2")); break; // case 3: all_layer_params.push_back(new scale("scale", "input0", "input1", true)); // This case should be checked by negative_scale_test // case 4: all_layer_params.push_back(new scale("scale", "input0", "input1", false)); // This case should be checked by negative_scale_test default: assert(0); @@ -1570,9 +1569,9 @@ public: return all_generic_params; } - static std::vector> generate_all_test_params() + static std::vector>> generate_all_test_params() { - std::vector> res; + std::vector>> res; for (int variant = 0; variant <= 1; ++variant) { @@ -1702,7 +1701,7 @@ public: } } - static std::string custom_param_name(const ::testing::TestParamInfo>& info) + static std::string custom_param_name(const ::testing::TestParamInfo>>& info) { std::stringstream res; @@ -1732,10 +1731,10 @@ public: private: static std::vector> all_generic_params; - static std::vector> all_layer_params; + static std::vector> all_layer_params; }; -std::vector> scale_test::all_layer_params = {}; +std::vector> scale_test::all_layer_params = {}; std::vector> scale_test::all_generic_params = {}; TEST_P(scale_test, SCALE) diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/scale_grad_input_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/scale_grad_input_test.cpp index c7057d5..c365ef5 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/scale_grad_input_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/scale_grad_input_test.cpp @@ -16,12 +16,12 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/scale_grad_input.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/scale_grad_input.hpp" +#include +#include +#include #include "test_utils/test_utils.h" #include diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/scale_grad_weights_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/scale_grad_weights_test.cpp index 680c68e..0c4521d 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/scale_grad_weights_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/scale_grad_weights_test.cpp @@ -16,14 +16,14 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include -#include -#include "api/CPP/scale_grad_weights.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include +#include +#include "api/scale_grad_weights.hpp" +#include +#include +#include #include "test_utils/test_utils.h" #include diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/select_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/select_gpu_test.cpp index abd2cff..d57a66a 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/select_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/select_gpu_test.cpp @@ -16,12 +16,12 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/select.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/select.hpp" +#include +#include +#include #include "test_utils/test_utils.h" using namespace cldnn; diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/shuffle_channels_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/shuffle_channels_test.cpp index 630b3b8..f78548f 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/shuffle_channels_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/shuffle_channels_test.cpp @@ -12,16 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. - - /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include @@ -71,7 +69,6 @@ TEST(shuffle_channels_fp32_gpu, d1_15_2_2_ax1_g5) { } } - TEST(shuffle_channels_fp32_gpu, d1_15_2_2_axm3_g5) { engine engine; diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/softmax_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/softmax_gpu_test.cpp index 850137c..3e6b58d 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/softmax_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/softmax_gpu_test.cpp @@ -15,19 +15,18 @@ */ #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/softmax.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/softmax.hpp" +#include +#include +#include #include "test_utils/test_utils.h" using namespace cldnn; using namespace std; using namespace tests; - class softmax_gpu_xb_f32_test_fixture: public ::testing::Test { public: static const int32_t @@ -36,7 +35,6 @@ public: in_size = input_x*input_b, out_size = output_x*output_b; - float in_buffer[in_size]; float out_buffer[out_size]; float expected_buffer[out_size]; @@ -294,7 +292,6 @@ TEST(softmax_gpu_bfyx_f32, normalize_y) { 0.999962831f, //b=0, f=2, x=0 0.993307149f, //b=0, f=2, x=1 - 0.98201379f, //b=1, f=0, x=0 0.99998987f, //b=1, f=0, x=1 @@ -878,15 +875,12 @@ public: delete generic_params; } - for (auto layer_params : all_layer_params) - { - delete layer_params; - } + all_layer_params.clear(); } - static std::vector generate_specific_test_params() + static std::vector> generate_specific_test_params() { - all_layer_params.push_back(new softmax("softmax", "input0", softmax::normalize_f)); + all_layer_params.emplace_back(new softmax("softmax", "input0", softmax::normalize_f)); //The test checks only valid combinations. //TODO: add more combinations. @@ -986,7 +980,7 @@ public: } } - static std::string custom_param_name(const ::testing::TestParamInfo>& info) + static std::string custom_param_name(const ::testing::TestParamInfo>>& info) { std::stringstream res; @@ -1015,11 +1009,11 @@ public: private: static std::vector all_generic_params; - static std::vector all_layer_params; + static std::vector> all_layer_params; }; -std::vector softmax_test::all_layer_params = {}; +std::vector> softmax_test::all_layer_params = {}; std::vector softmax_test::all_generic_params = {}; TEST_P(softmax_test, SOFTMAX) diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/softmax_loss_grad_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/softmax_loss_grad_gpu_test.cpp index 302ca0b..9ed37dd 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/softmax_loss_grad_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/softmax_loss_grad_gpu_test.cpp @@ -17,13 +17,13 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/softmax_loss_grad.hpp" -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/softmax_loss_grad.hpp" +#include +#include +#include +#include #include "test_utils/test_utils.h" using namespace cldnn; diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/spatial_concatenate_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/spatial_concatenate_gpu_test.cpp index 3c84efa..861b3d2 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/spatial_concatenate_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/spatial_concatenate_gpu_test.cpp @@ -17,12 +17,12 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/concatenation.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/concatenation.hpp" +#include +#include +#include #include "test_utils/test_utils.h" using namespace cldnn; @@ -281,7 +281,6 @@ TEST(spatial_concatenate_f32_gpu, inputs_3) { tpl.add(input_layout("in3", input3.get_layout())); tpl.add(concatenation("conc", { "in1", "in2", "in3" }, concatenation::along_x)); - network net(eng, tpl); net.set_input_data("in1", input1); net.set_input_data("in2", input2); diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/split_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/split_gpu_test.cpp index 921c382..b4ba3e1 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/split_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/split_gpu_test.cpp @@ -16,14 +16,14 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/split.hpp" -#include "api/CPP/scale.hpp" -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/split.hpp" +#include "api/scale.hpp" +#include +#include +#include +#include #include "test_utils/test_utils.h" #include @@ -113,7 +113,7 @@ void split_test(int batch_num, int feature_num, int x_size, int y_size, std::vec } // 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 < cldnn::tensor_dim_max; dimension++) { size.raw[dimension] = (size.raw[dimension] == 0) ? reference_input_size.raw[dimension] : size.raw[dimension]; @@ -198,7 +198,6 @@ TEST(split_gpu, split_1d_uneven_2_splits) { split_test(batch_num, feature_num, x_size, y_size, split_offsets); } - TEST(split_gpu, basic_split_concat_optimization) { const auto& engine = get_test_engine(); diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/streams_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/streams_test.cpp index b397c09..827978a 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/streams_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/streams_test.cpp @@ -15,11 +15,11 @@ */ #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include "test_utils/test_utils.h" using namespace cldnn; @@ -41,7 +41,6 @@ static engine _engine(engine_configuration(false, TEST(gpu_streams, can_allocate_memory_for_stream) { - ASSERT_NO_THROW(memory::allocate(_engine, layout(data_types::f32, format::bfyx, {1, 2, 3, 4}))); ASSERT_NO_THROW(memory::allocate(_engine, layout(data_types::f32, format::bfyx, {1, 2, 3, 4}), 0)); ASSERT_NO_THROW(memory::allocate(_engine, layout(data_types::f32, format::bfyx, {1, 2, 3, 4}), 1)); @@ -69,7 +68,7 @@ TEST(gpu_streams, can_create_networks_for_stream) topology topology( input_layout("input", input.get_layout()), - activation("relu", "input", activation_relu_negative_slope, cldnn_activation_additional_params{ 0.5f, 0.f }, padding{ { 0, 0, 0, 0 }, 0 })); + activation("relu", "input", activation_func::relu_negative_slope, activation_additional_params{ 0.5f, 0.f }, padding{ { 0, 0, 0, 0 }, 0 })); network network(_engine, topology, build_options(), 1); ASSERT_ANY_THROW(cldnn::network network(_engine, topology, build_options(), 2)); @@ -96,7 +95,6 @@ TEST(gpu_streams, can_create_networks_for_stream) EXPECT_EQ(f_size, 1); EXPECT_EQ(b_size, 1); - for (size_t i = 0; i < output_vec.size(); ++i) { EXPECT_FLOAT_EQ(output_vec[i], output_ptr[i]); } diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/strided_slice_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/strided_slice_gpu_test.cpp index c673071..b5d7e18 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/strided_slice_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/strided_slice_gpu_test.cpp @@ -16,19 +16,17 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include -#include "api/CPP/strided_slice.hpp" -#include -#include -#include +#include +#include "api/strided_slice.hpp" +#include +#include +#include #include "test_utils/test_utils.h" -#include - +#include using namespace cldnn; using namespace tests; - TEST(strided_slice_gpu_f32, test_2x2x2x2) { // Input (BFYX): 2x2x2x2 // Begin (BFYX): 0x0x0x0 diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/tensor_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/tensor_test.cpp index 3c48962..7b3393f 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/tensor_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/tensor_test.cpp @@ -15,7 +15,7 @@ */ #include -#include +#include TEST(tensor_api, order_new_notation) { @@ -24,7 +24,7 @@ TEST(tensor_api, order_new_notation) //sizes EXPECT_EQ(test.batch.size(), size_t(1)); EXPECT_EQ(test.feature.size(), size_t(1)); - EXPECT_EQ(test.spatial.size(), size_t(CLDNN_TENSOR_SPATIAL_DIM_MAX)); + EXPECT_EQ(test.spatial.size(), size_t(cldnn::tensor_spatial_dim_max)); //passed values EXPECT_EQ(test.spatial[0], cldnn::tensor::value_type(2)); @@ -47,7 +47,7 @@ TEST(tensor_api, order_new_notation_feature_default) //sizes EXPECT_EQ(test.batch.size(), size_t(1)); EXPECT_EQ(test.feature.size(), size_t(1)); - EXPECT_EQ(test.spatial.size(), size_t(CLDNN_TENSOR_SPATIAL_DIM_MAX)); + EXPECT_EQ(test.spatial.size(), size_t(cldnn::tensor_spatial_dim_max)); //passed values EXPECT_EQ(test.spatial[0], cldnn::tensor::value_type(2)); @@ -70,7 +70,7 @@ TEST(tensor_api, order) //sizes EXPECT_EQ(test.batch.size(), size_t(1)); EXPECT_EQ(test.feature.size(), size_t(1)); - EXPECT_EQ(test.spatial.size(), size_t(CLDNN_TENSOR_SPATIAL_DIM_MAX)); + EXPECT_EQ(test.spatial.size(), size_t(cldnn::tensor_spatial_dim_max)); //passed values EXPECT_EQ(test.spatial[1], cldnn::tensor::value_type(4)); diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/tile_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/tile_gpu_test.cpp index d3c55d6..897df97 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/tile_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/tile_gpu_test.cpp @@ -16,12 +16,12 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/tile.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/tile.hpp" +#include +#include +#include #include "test_utils/test_utils.h" #include @@ -87,7 +87,6 @@ TEST(tile_gpu, basic_in1x2x2x2_axis_b) { network network(engine, topology); network.set_input_data("input", input); - auto outputs = network.execute(); auto output = outputs.at("tile").get_memory(); @@ -120,7 +119,6 @@ TEST(tile_gpu, basic_in1x2x2x2_axis_f) { network network(engine, topology); network.set_input_data("input", input); - auto outputs = network.execute(); auto output = outputs.at("tile").get_memory(); @@ -153,7 +151,6 @@ TEST(tile_gpu, basic_in1x2x2x2_axis_y) { network network(engine, topology); network.set_input_data("input", input); - auto outputs = network.execute(); auto output = outputs.at("tile").get_memory(); @@ -251,7 +248,6 @@ TEST(tile_gpu, basic_in1x2x2x2_axis_z) { network network(engine, topology); network.set_input_data("input", input); - auto outputs = network.execute(); auto output = outputs.at("tile").get_memory(); diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/topology_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/topology_test.cpp index 3491933..277b93a 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/topology_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/topology_test.cpp @@ -1,22 +1,22 @@ #include -#include -#include -#include +#include +#include +#include #include "test_utils/test_utils.h" #include #include -#include "api/CPP/memory.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -167,7 +167,7 @@ protected: float k = 1.0f; float alpha = 0.0001f; float beta = 0.75f; - cldnn_lrn_norm_region norm_type = cldnn_lrn_norm_region_across_channel; + cldnn::lrn_norm_region norm_type = cldnn::lrn_norm_region_across_channel; topology.add(cldnn::lrn(id, input_id, size, k, alpha, beta, norm_type)); return true; } @@ -240,7 +240,7 @@ protected: // todo: randomize params cldnn::primitive_id input_id = topology_generator::CreateLayerId(); input_layouts.push_back({ input_id, output_layout }); - topology.add(cldnn::activation(id, input_id, activation_relu)); + topology.add(cldnn::activation(id, input_id, cldnn::activation_func::relu)); return true; } }; diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/trim_to_outputs_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/trim_to_outputs_gpu_test.cpp index 428881f..99ca881 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/trim_to_outputs_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/trim_to_outputs_gpu_test.cpp @@ -17,13 +17,13 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/concatenation.hpp" -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/concatenation.hpp" +#include +#include +#include +#include #include "test_utils/test_utils.h" using namespace cldnn; @@ -33,7 +33,6 @@ using namespace tests; This set of tests has been designed to check the correctness of trim_to_outputs optimization pass */ - /* In this test we check if the convolution conv2 will be eliminated from the network. This is expected to be done in trim_to_outputs optimization pass diff --git a/inference-engine/thirdparty/clDNN/tests/test_cases/upsampling_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests/test_cases/upsampling_gpu_test.cpp index 8adf590..c63fa4a 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_cases/upsampling_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_cases/upsampling_gpu_test.cpp @@ -16,15 +16,15 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include -#include "api/CPP/memory.hpp" -#include -#include "api/CPP/upsampling.hpp" -#include -#include -#include +#include "api/memory.hpp" +#include +#include "api/upsampling.hpp" +#include +#include +#include #include "test_utils/test_utils.h" -#include -#include +#include +#include using namespace cldnn; using namespace tests; @@ -45,11 +45,11 @@ TEST(upsampling_gpu, basic_in2x3x2x2_nearest) { auto input = memory::allocate(engine, { data_types::f32, format::bfyx, { 2, 2, 3, 2 } }); - uint32_t scale = 2; + auto output_size = tensor(batch(2), feature(2), spatial(6, 4)); topology topology; topology.add(input_layout("input", input.get_layout())); - topology.add(upsampling("upsampling", "input", scale, 0.0f, upsampling_sample_type::nearest)); + topology.add(upsampling("upsampling", "input", output_size, 0.0f, upsampling_sample_type::nearest)); set_values(input, { 1.f, 2.f, -10.f, @@ -116,11 +116,11 @@ TEST(upsampling_gpu, basic_in2x3x2x2_bilinear) { auto input = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 2, 2 } }); - uint32_t scale = 2; + auto output_size = tensor(batch(1), feature(1), spatial(4, 4)); topology topology; topology.add(input_layout("input", input.get_layout())); - topology.add(upsampling("upsampling", "input", scale, 1.0f, upsampling_sample_type::bilinear)); + topology.add(upsampling("upsampling", "input", output_size, 1.0f, upsampling_sample_type::bilinear)); set_values(input, { 1.f, 2.f, @@ -151,3 +151,103 @@ TEST(upsampling_gpu, basic_in2x3x2x2_bilinear) { } } } + +TEST(upsampling_gpu, nearest_asymmetric) { + // Input : 1x1x2x2 + // Output : 1x1x5x4 + // Sample Type: Nearest + + // Input: + // f0: b0: 1 2 + // f0: b0: 3 4 + // + + const auto& engine = get_test_engine(); + + auto input = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 2, 2 } }); + + auto output_size = tensor(batch(1), feature(1), spatial(5, 4)); + + topology topology; + topology.add(input_layout("input", input.get_layout())); + topology.add(upsampling("upsampling", "input", output_size, 1.0f, upsampling_sample_type::nearest)); + + set_values(input, { + 1.f, 2.f, + 3.f, 4.f, + }); + + network network(engine, topology); + network.set_input_data("input", input); + + auto outputs = network.execute(); + + auto output = outputs.at("upsampling").get_memory(); + auto output_ptr = output.pointer(); + + EXPECT_EQ(output.get_layout().get_linear_size(), (size_t)20); + + float answers[20] = { + 1.f, 1.f, 1.f, 2.f, 2.f, + 1.f, 1.f, 1.f, 2.f, 2.f, + 3.f, 3.f, 3.f, 4.f, 4.f, + 3.f, 3.f, 3.f, 4.f, 4.f, + }; + + for (int k = 0; k < 4; ++k) { //Y + for (int l = 0; l < 5; ++l) { //X + auto linear_id = l + k * 5; + EXPECT_NEAR(answers[linear_id], output_ptr[linear_id], 1e-05F); + } + } +} + +TEST(upsampling_gpu, bilinear_asymmetric) { + // Input : 1x1x2x2 + // Output : 1x1x5x4 + // Sample Type: Nearest + + // Input: + // f0: b0: 1 2 + // f0: b0: 3 4 + // + + const auto& engine = get_test_engine(); + + auto input = memory::allocate(engine, { data_types::f32, format::bfyx, { 1, 1, 2, 2 } }); + + auto output_size = tensor(batch(1), feature(1), spatial(6, 4)); + + topology topology; + topology.add(input_layout("input", input.get_layout())); + topology.add(upsampling("upsampling", "input", output_size, 1.0f, upsampling_sample_type::bilinear)); + + set_values(input, { + 1.f, 2.f, + 3.f, 4.f, + }); + + network network(engine, topology); + network.set_input_data("input", input); + + auto outputs = network.execute(); + + auto output = outputs.at("upsampling").get_memory(); + auto output_ptr = output.pointer(); + + EXPECT_EQ(output.get_layout().get_linear_size(), (size_t)24); + + float answers[24] = { + 0.5f, 0.75f, 1.f, 1.25f, 1.5f, 1.f, + 1.f, 1.5f, 1.83f, 2.17f, 2.5f, 1.67f, + 1.67f, 2.5f, 2.83f, 3.17f, 3.5f, 2.33f, + 1.5f, 2.25f, 2.5f, 2.75f, 3.f, 2.f, + }; + + for (int k = 0; k < 4; ++k) { //Y + for (int l = 0; l < 6; ++l) { //X + auto linear_id = l + k * 6; + EXPECT_NEAR(answers[linear_id], output_ptr[linear_id], 5e-03F) << l << " " << k; + } + } +} diff --git a/inference-engine/thirdparty/clDNN/tests/test_utils/float16.h b/inference-engine/thirdparty/clDNN/tests/test_utils/float16.h index 8288a38..45eb159 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_utils/float16.h +++ b/inference-engine/thirdparty/clDNN/tests/test_utils/float16.h @@ -17,7 +17,6 @@ #pragma once #include "include/math_utils.h" - struct FLOAT16 { struct representation @@ -79,7 +78,6 @@ struct FLOAT16 } }; - inline FLOAT16 operator +(const FLOAT16 &v1, const FLOAT16 &v2) { return (float)v1 + (float)v2; diff --git a/inference-engine/thirdparty/clDNN/tests/test_utils/instrumentation.cpp b/inference-engine/thirdparty/clDNN/tests/test_utils/instrumentation.cpp index 179bdbd..35cbc8c 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_utils/instrumentation.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_utils/instrumentation.cpp @@ -22,12 +22,11 @@ #include #include - namespace instrumentation { // initalize dumping directory for whole run const std::string logger::dump_dir = DUMP_DIRECTORY; - static float convert_half_to_float(half_t val, bool flush_denorm_to_zero = false) + static float convert_half_to_float(cldnn::half_t val, bool flush_denorm_to_zero = false) { #if defined HALF_HALF_HPP return val; @@ -82,7 +81,7 @@ namespace instrumentation { return f; } - float convert_element(half_t h) + float convert_element(cldnn::half_t h) { return convert_half_to_float(h); } @@ -412,7 +411,7 @@ namespace instrumentation { if (mem.get_layout().data_type == cldnn::data_types::f32) dump(mem, dump_strings); else - dump(mem, dump_strings); + dump(mem, dump_strings); for (cldnn::tensor::value_type b = 0; b < batch; b++) for (cldnn::tensor::value_type f = 0; f < feature; f++) @@ -434,7 +433,7 @@ namespace instrumentation { if (mem.get_layout().data_type == cldnn::data_types::f32) dump(mem, stream); else - dump(mem, stream); + dump(mem, stream); std::string filename((dump_dir + "/" + prefix + ".txt")); std::ofstream file_stream(filename); diff --git a/inference-engine/thirdparty/clDNN/tests/test_utils/instrumentation.h b/inference-engine/thirdparty/clDNN/tests/test_utils/instrumentation.h index 85a791d..9c46782 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_utils/instrumentation.h +++ b/inference-engine/thirdparty/clDNN/tests/test_utils/instrumentation.h @@ -18,7 +18,7 @@ #include #include #include -#include "api/CPP/memory.hpp" +#include "api/memory.hpp" #define DUMP_DIRECTORY "./" diff --git a/inference-engine/thirdparty/clDNN/tests/test_utils/random_gen.h b/inference-engine/thirdparty/clDNN/tests/test_utils/random_gen.h index 6e23969..c7209f0 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_utils/random_gen.h +++ b/inference-engine/thirdparty/clDNN/tests/test_utils/random_gen.h @@ -66,7 +66,6 @@ namespace rnd_generators static_assert(number_caps::inv_exp2(7) == 0.0078125f, "1/exp2(7)"); static_assert(number_caps::inv_exp2(8) == 0.00390625f, "1/exp2(8)"); - template <> struct number_caps { @@ -155,7 +154,6 @@ namespace rnd_generators } }; - template auto gen_number(RndEngineTy& rnd_engine, const unsigned significand_rnd_bits = number_caps::significand_bits, diff --git a/inference-engine/thirdparty/clDNN/tests/test_utils/test_utils.cpp b/inference-engine/thirdparty/clDNN/tests/test_utils/test_utils.cpp index 79aa8a0..9437b72 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_utils/test_utils.cpp +++ b/inference-engine/thirdparty/clDNN/tests/test_utils/test_utils.cpp @@ -16,13 +16,13 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// -#include "api/CPP/memory.hpp" -#include -#include -#include -#include -#include -#include +#include "api/memory.hpp" +#include +#include +#include +#include +#include +#include #include "test_utils.h" #include "float16.h" #include "instrumentation.h" @@ -45,7 +45,7 @@ namespace tests generic_params->network_build_options.set_option(cldnn::build_option::graph_dumps_dir(DUMP_DIRECTORY)); } topology topology; - topology.add(*layer_params); + topology.add_primitive(layer_params); std::vector input_mems; std::vector input_layouts_names = {}; @@ -427,4 +427,4 @@ namespace tests std::vector generic_test::test_feature_sizes = { 1, 2 };// , 3, 15}; std::vector generic_test::test_input_sizes = { { 1, 1, 100, 100 } ,{ 1, 1, 277, 277 } ,{ 1, 1, 400, 600 } }; -} \ No newline at end of file +} diff --git a/inference-engine/thirdparty/clDNN/tests/test_utils/test_utils.h b/inference-engine/thirdparty/clDNN/tests/test_utils/test_utils.h index f2c45db..d881cf4 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_utils/test_utils.h +++ b/inference-engine/thirdparty/clDNN/tests/test_utils/test_utils.h @@ -18,28 +18,29 @@ #pragma once -#include "api/CPP/memory.hpp" -#include "api/CPP/tensor.hpp" -#include "api/CPP/program.hpp" -#include "api/CPP/network.hpp" +#include "api/memory.hpp" +#include "api/tensor.hpp" +#include "api/program.hpp" +#include "api/network.hpp" #include #include #include #include +#include #include -#include +#include #include "float16.h" #include "random_gen.h" -#include "api/CPP/concatenation.hpp" -#include "api/CPP/lrn.hpp" -#include "api/CPP/roi_pooling.hpp" -#include "api/CPP/scale.hpp" -#include "api/CPP/softmax.hpp" -#include "api/CPP/reorder.hpp" -#include "api/CPP/normalize.hpp" -#include "api/CPP/convolution.hpp" -#include "api/CPP/activation.hpp" -#include "api/CPP/pooling.hpp" +#include "api/concatenation.hpp" +#include "api/lrn.hpp" +#include "api/roi_pooling.hpp" +#include "api/scale.hpp" +#include "api/softmax.hpp" +#include "api/reorder.hpp" +#include "api/normalize.hpp" +#include "api/convolution.hpp" +#include "api/activation.hpp" +#include "api/pooling.hpp" #include @@ -214,7 +215,6 @@ void set_values_per_batch_and_feature(const cldnn::memory& mem, std::vector a } } - } template @@ -229,7 +229,6 @@ void set_random_values(const cldnn::memory& mem, bool sign = false, unsigned sig } } - // Tries to construct a network, checking if an expected error appears inline void check_exception_massage(const cldnn::engine& engine, cldnn::topology& topology, std::string msg_to_find) { @@ -247,7 +246,6 @@ inline void check_exception_massage(const cldnn::engine& engine, cldnn::topology } } - // Checks equality of floats. // For values less than absoulte_error_limit, absolute error will be counted // for others, the relatve error will be counted. @@ -305,7 +303,6 @@ inline bool floating_point_equal(float x, float y, int max_ulps_diff = 4) { } } - class test_params { public: @@ -358,7 +355,7 @@ private: const std::string name_str = ::testing::UnitTest::GetInstance()->current_test_info()->name(); }; -class generic_test : public ::testing::TestWithParam> +class generic_test : public ::testing::TestWithParam>> { public: @@ -384,7 +381,7 @@ public: virtual cldnn::tensor get_expected_output_tensor(); struct custom_param_name_functor { - std::string operator()(const ::testing::TestParamInfo>& info) { + std::string operator()(const ::testing::TestParamInfo>>& info) { return std::to_string(info.index); } }; @@ -393,7 +390,7 @@ protected: const cldnn::engine& engine = get_test_engine(); test_params* generic_params; test_dump test_info; - cldnn::primitive* layer_params; + std::shared_ptr layer_params; int max_ulps_diff_allowed; //Max number of ulps allowed between 2 values when comparing the output buffer and the reference buffer. bool random_values; // if set memory buffers will be filled with random values bool dump_graphs; // if set tests will dump graphs to file @@ -414,7 +411,7 @@ protected: // When a test assertion such as EXPECT_EQ fails, Google-Test prints the argument values to help with debugging. // It does this using a user - extensible value printer. // This function will be used to print the test params in case of an error. -inline void PrintTupleTo(const std::tuple& t, ::std::ostream* os) +inline void PrintTupleTo(const std::tuple>& t, ::std::ostream* os) { std::stringstream str; @@ -431,13 +428,13 @@ inline void PrintTupleTo(const std::tupletype == cldnn::concatenation::type_id()) { - auto dc = static_cast(primitive); + auto dc = std::static_pointer_cast(primitive); (void)dc; } else if(primitive->type == cldnn::lrn::type_id()) { - auto lrn = static_cast(primitive); - std::string norm_region = (lrn->norm_region == cldnn_lrn_norm_region_across_channel) ? "across channel" : "within channel"; + auto lrn = std::static_pointer_cast(primitive); + std::string norm_region = (lrn->norm_region == cldnn::lrn_norm_region_across_channel) ? "across channel" : "within channel"; str << "Norm region: " << norm_region << " Size: " << lrn->size << " Alpha: " << lrn->alpha @@ -446,7 +443,7 @@ inline void PrintTupleTo(const std::tupletype == cldnn::roi_pooling::type_id()) { - auto p = static_cast(primitive); + auto p = std::static_pointer_cast(primitive); str << "Pooling mode: " << (p->mode == cldnn::pooling_mode::max ? "MAX" : "AVG") << " Pooled width: " << p->pooled_width << " Pooled height: " << p->pooled_height @@ -457,41 +454,40 @@ inline void PrintTupleTo(const std::tupletype == cldnn::scale::type_id()) { - auto s = static_cast(primitive); + auto s = std::static_pointer_cast(primitive); (void)s; } else if(primitive->type == cldnn::softmax::type_id()) { - auto sm = static_cast(primitive); + auto sm = std::static_pointer_cast(primitive); (void)sm; } else if (primitive->type == cldnn::reorder::type_id()) { - auto reorder = static_cast(primitive); + auto reorder = std::static_pointer_cast(primitive); str << "Output data type: " << cldnn::data_type_traits::name(*reorder->output_data_type) << " Mean: " << reorder->mean << "Subtract per feature: " << "TODO" /*std::vector subtract_per_feature*/; } else if (primitive->type == cldnn::normalize::type_id()) { - auto normalize = static_cast(primitive); + auto normalize = std::static_pointer_cast(primitive); std::string norm_region = normalize->across_spatial ? "across_spatial" : "within_spatial"; str << "Norm region: " << norm_region << " Epsilon: " << normalize->epsilon << " Scale input id: " << normalize->scale_input; } else if (primitive->type == cldnn::convolution::type_id()) { - auto convolution = static_cast(primitive); + auto convolution = std::static_pointer_cast(primitive); str << "Stride x: " << convolution->stride.spatial[0] << " Stride y: " << convolution->stride.spatial[1] << " Dilation x: " << convolution->dilation.spatial[0] << " Dilation y: " << convolution->dilation.spatial[1] - << " Input offset x: " << convolution->input_offset.spatial[0] << " Input offset y: " << convolution->input_offset.spatial[1] - << " Activation: " << convolution->with_activation << " Activation slope: " << convolution->activation_negative_slope; + << " Input offset x: " << convolution->input_offset.spatial[0] << " Input offset y: " << convolution->input_offset.spatial[1]; } else if (primitive->type == cldnn::activation::type_id()) { - auto activation = static_cast(primitive); + auto activation = std::static_pointer_cast(primitive); str << "Negative slope: " << activation->additional_params.a << " Negative slope input id: " << activation->additional_params_input; } else if (primitive->type == cldnn::pooling::type_id()) { - auto pooling = static_cast(primitive); + auto pooling = std::static_pointer_cast(primitive); std::string pooling_mode = (pooling->mode == cldnn::pooling_mode::max) ? "max" : "average"; str << "Pooling mode: " << pooling_mode << " Input offset x: " << pooling->input_offset.spatial[0] << " Input offset y: " << pooling->input_offset.spatial[1] diff --git a/inference-engine/thirdparty/clDNN/tests/test_utils/uniform_quantized_real_distribution.hpp b/inference-engine/thirdparty/clDNN/tests/test_utils/uniform_quantized_real_distribution.hpp index 2c90ad2..953ea69 100644 --- a/inference-engine/thirdparty/clDNN/tests/test_utils/uniform_quantized_real_distribution.hpp +++ b/inference-engine/thirdparty/clDNN/tests/test_utils/uniform_quantized_real_distribution.hpp @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. - #pragma once #include @@ -31,7 +30,6 @@ #include #include - namespace cldnn { namespace tests { namespace distributions { /// @cond PRIVATE @@ -115,7 +113,6 @@ private: std::numeric_limits::has_signaling_NaN) + static_cast(std::numeric_limits::has_denorm != std::float_denorm_style::denorm_absent); - public: /// @brief Estimated size of representation in bits. static constexpr std::size_t value = round_up_to_multiply( @@ -133,7 +130,6 @@ struct estimate_repr_size_helper2_ std::numeric_limits::digits, CHAR_BIT); }; - template ::value> struct estimate_repr_size_helper1_ @@ -148,7 +144,6 @@ template struct estimate_repr_size_helper1_ : estimate_repr_size_helper2_ {}; - /// @brief Estimates size in bits (rounded to byte) of representation of specified DataType. /// /// @tparam DataType Data type which size of representation will be estimated. @@ -220,7 +215,6 @@ public: _stream_ios.fill(_old_fill); } - // ---------------------------------------------------------------------------------------------------------------- // Data members. // ---------------------------------------------------------------------------------------------------------------- @@ -232,7 +226,6 @@ private: char_type _old_fill; }; - /// @brief Creates guard that saves / restores upon destruction format state of stream. /// /// @tparam CharType Character type use in stream. @@ -335,7 +328,6 @@ private: /// @brief Number of parts needed to store representation of data_type. static constexpr auto data_repr_parts_count = data_type_mask::data_repr_parts_count; - // ------------------------------------------------------------------------------------------------------------ // data_parts: Constructors, special functions, destructors. // ------------------------------------------------------------------------------------------------------------ @@ -346,7 +338,6 @@ private: constexpr data_parts(data_parts_zero_mark) : parts{} {} - // ------------------------------------------------------------------------------------------------------------ // data_parts: Properties, Accessors. // ------------------------------------------------------------------------------------------------------------ @@ -377,7 +368,6 @@ private: (little_endian ? parts[data_repr_parts_count - 1U - index] : parts[index]) = val; } - /// @brief Gets part by index (ordered from low-to-high mask bits). /// /// @param index Index of the part (@c 0-based). Must be lower than data_repr_parts_count. @@ -405,7 +395,6 @@ private: (little_endian ? parts[index] : parts[data_repr_parts_count - 1U - index]) = val; } - // ------------------------------------------------------------------------------------------------------------ // data_parts: Functions. // ------------------------------------------------------------------------------------------------------------ @@ -442,7 +431,6 @@ private: return *this; } - // ------------------------------------------------------------------------------------------------------------ // data_parts: Operators. // ------------------------------------------------------------------------------------------------------------ @@ -559,7 +547,6 @@ private: return *this; } - // ------------------------------------------------------------------------------------------------------------ // data_parts: EqualityComparable implementation. // ------------------------------------------------------------------------------------------------------------ @@ -587,7 +574,6 @@ private: return !(lhs == rhs); } - // ------------------------------------------------------------------------------------------------------------ // data_parts: Data members. // ------------------------------------------------------------------------------------------------------------ @@ -595,7 +581,6 @@ private: part_type parts[data_repr_parts_count]; }; - public: /// @brief Marker type that allows to select correct candidate constructor (that creates mask by position /// or position and length). @@ -604,7 +589,6 @@ public: /// integral value of data type). static constexpr create_by_pos_mark create_by_pos{}; // "{}": WA for clang 3.6-3.9 - private: // ---------------------------------------------------------------------------------------------------------------- // Constructors, special functions, destructors. @@ -801,7 +785,6 @@ public: return *this; } - /// @brief Serialize to stream. /// /// @tparam CharType Stream character type. @@ -921,7 +904,6 @@ public: return is; } - // ---------------------------------------------------------------------------------------------------------------- // EqualityComparable implementation. // ---------------------------------------------------------------------------------------------------------------- @@ -944,7 +926,6 @@ public: return !(lhs == rhs); } - // ---------------------------------------------------------------------------------------------------------------- // Data members. // ---------------------------------------------------------------------------------------------------------------- @@ -956,11 +937,9 @@ private: #pragma warning(pop) #endif - } // namespace detail /// @endcond - /// @brief Uniform real distribution that generates values in range [a; b]. /// /// @details Uniform distribution with following properties: @@ -1021,7 +1000,6 @@ public: static constexpr unsigned significand_max_bits = ((std::numeric_limits::digits - 1) > 0) ? std::numeric_limits::digits - 1 : 0; - /// @brief Type of parameters used by distribution. /// /// @details The type meets @a CopyConstructible, @a CopyAssignable, @a MoveConstructable, @a MoveAssignable, @@ -1034,7 +1012,6 @@ public: /// @brief Type of distribution for which current parameters are used. using distribution_type = uniform_quantized_real_distribution; - // ------------------------------------------------------------------------------------------------------------ // param_type: Constructors, special functions, destructors. // ------------------------------------------------------------------------------------------------------------ @@ -1089,7 +1066,6 @@ public: : param_type(result_zero, result_one, significand_rand_bits) {} - // ------------------------------------------------------------------------------------------------------------ // param_type: Properties, Accessors. // ------------------------------------------------------------------------------------------------------------ @@ -1140,7 +1116,6 @@ public: return !(lhs == rhs); } - // ------------------------------------------------------------------------------------------------------------ // param_type: Data members. // ------------------------------------------------------------------------------------------------------------ @@ -1169,7 +1144,6 @@ private: /// @brief Type of parameter set swapped and restored by current guard. using param_type = typename distribution_type::param_type; - // ------------------------------------------------------------------------------------------------------------ // param_swap_guard: Constructors, special functions, destructors. // ------------------------------------------------------------------------------------------------------------ @@ -1197,7 +1171,6 @@ private: _distibution.param(_old_param); } - // ------------------------------------------------------------------------------------------------------------ // param_swap_guard: Data members. // ------------------------------------------------------------------------------------------------------------ @@ -1223,7 +1196,6 @@ private: return param_swap_guard(distribution, new_param); } - // ---------------------------------------------------------------------------------------------------------------- // Helper functions. // ---------------------------------------------------------------------------------------------------------------- @@ -1253,7 +1225,6 @@ private: return underlying_dist_type(result_one, upper_bound).param(); } - // ---------------------------------------------------------------------------------------------------------------- // Constructors, special functions, destructors. // ---------------------------------------------------------------------------------------------------------------- @@ -1320,7 +1291,6 @@ public: _base_distribution(create_underlying_dist_param(_mask)) {} - // ---------------------------------------------------------------------------------------------------------------- // State and generators. // ---------------------------------------------------------------------------------------------------------------- @@ -1365,7 +1335,6 @@ public: return (*this)(generator); } - // ---------------------------------------------------------------------------------------------------------------- // Properties, Accessors. // ---------------------------------------------------------------------------------------------------------------- @@ -1429,7 +1398,6 @@ public: return _param.significand_rand_bits(); } - // ---------------------------------------------------------------------------------------------------------------- // Operators. // ---------------------------------------------------------------------------------------------------------------- @@ -1503,7 +1471,6 @@ public: // Although the serialized members are named, the order must be preserved. auto guard = detail::create_format_guard(is); - mask_type param_helper_a, param_helper_b, param_helper_m; unsigned param_helper_srb; underlying_dist_type param_helper_bd; @@ -1535,19 +1502,16 @@ public: rhs.skip_to_char(is, '}'); rhs.skip_to_char(is, '}'); - if (mask != param_helper_m) is.setstate(std::ios::failbit); if (!is) { return is; } - rhs._param = param; rhs._mask = mask; rhs._base_distribution = param_helper_bd; return is; } - // ---------------------------------------------------------------------------------------------------------------- // EqualityComparable implementation. // ---------------------------------------------------------------------------------------------------------------- @@ -1573,7 +1537,6 @@ public: return !(lhs == rhs); } - // ---------------------------------------------------------------------------------------------------------------- // Data members. // ---------------------------------------------------------------------------------------------------------------- diff --git a/inference-engine/thirdparty/clDNN/tests_core_internal/CMakeLists.txt b/inference-engine/thirdparty/clDNN/tests_core_internal/CMakeLists.txt index 04bf5d2..3367fd1 100644 --- a/inference-engine/thirdparty/clDNN/tests_core_internal/CMakeLists.txt +++ b/inference-engine/thirdparty/clDNN/tests_core_internal/CMakeLists.txt @@ -12,15 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# ====================================== Helper constant variables ===================================== - -# Order of scan for special capabilities files (.inc files with capabilities description). -set(CLDNN__CAPS_SCAN_ORDER - "private" - "internal" - "public" - ) - # ========================================= Name / Output settings ===================================== set(CLDNN_BUILD__PROJ "clDNN_tests_core_internal") @@ -52,67 +43,8 @@ endif() # ================================== Compiler preprocessor definitions ================================= -set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS - CLDNN_EXPORTS - EXPORT_NEURAL_SYMBOLS - "CLDNN_VERSION_MAJOR=${CLDNN__VERSION_MAJOR}" - "CLDNN_VERSION_MINOR=${CLDNN__VERSION_MINOR}" - "CLDNN_VERSION_BUILD=${CLDNN__VERSION_BUILD}" - "CLDNN_VERSION_REVISION=${CLDNN__VERSION_REVISION}" - ) - - # ========================================= Source/Header files ======================================== -set(__CLDNN_Directory__clDNN_copy "${CMAKE_CURRENT_SOURCE_DIR}/../src") -set(__CLDNN_Label__clDNN_copy "clDNN") -file(GLOB __CLDNN_Sources__clDNN_copy - "${__CLDNN_Directory__clDNN_copy}/*.h" - "${__CLDNN_Directory__clDNN_copy}/*.hpp" - "${__CLDNN_Directory__clDNN_copy}/*.cpp" - "${__CLDNN_Directory__clDNN_copy}/*.inc" - ) - -set(__CLDNN_Label__api "${__CLDNN_Label__clDNN_copy}\\api") -file(GLOB __CLDNN_Headers__api - "${CLDNN__API_DIR}/*.h" - "${CLDNN__API_DIR}/*.hpp" - ) - -set(__CLDNN_Directory__api__cpp "${CLDNN__API_DIR}/CPP") -set(__CLDNN_Label__api__cpp "${__CLDNN_Label__api}\\CPP") -file(GLOB __CLDNN_Headers__api__cpp - "${__CLDNN_Directory__api__cpp}/*.h" - "${__CLDNN_Directory__api__cpp}/*.hpp" - ) - -set(__CLDNN_Directory__api__c "${CLDNN__API_DIR}/C") -set(__CLDNN_Label__api__c "${__CLDNN_Label__api}\\C") -file(GLOB __CLDNN_Headers__api__c - "${__CLDNN_Directory__api__c}/*.h" - "${__CLDNN_Directory__api__c}/*.hpp" - ) - -set(__CLDNN_Label__api_extension "${__CLDNN_Label__clDNN_copy}\\api_extension") -file(GLOB __CLDNN_Headers__api_extension - "${CLDNN__API_EXTENSION_DIR}/*.h" - "${CLDNN__API_EXTENSION_DIR}/*.hpp" - ) - -set(__CLDNN_Directory__api_extension__cpp "${CLDNN__API_EXTENSION_DIR}/CPP") -set(__CLDNN_Label__api_extension__cpp "${__CLDNN_Label__api_extension}\\CPP") -file(GLOB __CLDNN_Headers__api_extension__cpp - "${__CLDNN_Directory__api_extension__cpp}/*.h" - "${__CLDNN_Directory__api_extension__cpp}/*.hpp" - ) - -set(__CLDNN_Directory__api_extension__c "${CLDNN__API_EXTENSION_DIR}/C") -set(__CLDNN_Label__api_extension__c "${__CLDNN_Label__api_extension}\\C") -file(GLOB __CLDNN_Headers__api_extension__c - "${__CLDNN_Directory__api_extension__c}/*.h" - "${__CLDNN_Directory__api_extension__c}/*.hpp" - ) - set(__CLDNN_Label__main "") file(GLOB __CLDNN_Sources__main "${CMAKE_CURRENT_SOURCE_DIR}/*.h" @@ -120,21 +52,6 @@ file(GLOB __CLDNN_Sources__main "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" ) -set(__CLDNN_Directory__graph_opt "${CMAKE_CURRENT_SOURCE_DIR}/../src/graph_optimizer") -set(__CLDNN_Label__graph_opt "${__CLDNN_Label__clDNN_copy}\\graph_optimizer") -file(GLOB __CLDNN_Sources__graph_opt - "${__CLDNN_Directory__graph_opt}/*.h" - "${__CLDNN_Directory__graph_opt}/*.hpp" - "${__CLDNN_Directory__graph_opt}/*.cpp" - ) - -set(__CLDNN_Directory__include "${CMAKE_CURRENT_SOURCE_DIR}/../src/include") -set(__CLDNN_Label__include "${__CLDNN_Label__clDNN_copy}\\include") -file(GLOB __CLDNN_Headers__include - "${__CLDNN_Directory__include}/*.h" - "${__CLDNN_Directory__include}/*.hpp" - ) - set(__CLDNN_Directory__test_cases "${CMAKE_CURRENT_SOURCE_DIR}/test_cases") set(__CLDNN_Label__test_cases "test cases") file(GLOB __CLDNN_Sources__test_cases @@ -157,76 +74,8 @@ file(GLOB __CLDNN_Sources__gtest "${__CLDNN_Directory__gtest}/*.cc" ) -# Special handling of capabilities files. -set(__CLDNN_Directory__caps "${CMAKE_CURRENT_SOURCE_DIR}/../src/caps") -set(__CLDNN_Label__caps "${__CLDNN_Label__clDNN_copy}\\caps") -foreach(__CLDNN_CapsScanDir ${CLDNN__CAPS_SCAN_ORDER}) - string(REPLACE ";" "\;" __CLDNN_CapsScanDir "${__CLDNN_CapsScanDir}") # [WA#1] Must escape ; again if occurred in item. - file(GLOB __CLDNN_Sources__caps "${__CLDNN_Directory__caps}/${__CLDNN_CapsScanDir}/*.inc") - list(LENGTH __CLDNN_Sources__caps __CLDNN_CapsScanDirFileCount) - if(__CLDNN_CapsScanDirFileCount GREATER 0) - set(__CLDNN_IncDirectory__caps "${__CLDNN_Directory__caps}/${__CLDNN_CapsScanDir}") - message(STATUS "[clDNN] Selected capabilities: ${__CLDNN_CapsScanDir}") - break() - endif() -endforeach() -if(NOT (__CLDNN_CapsScanDirFileCount GREATER 0)) - message(FATAL_ERROR "[clDNN] Cannot locate any capabilities files in \"${__CLDNN_Directory__caps}\" subdirectories.") -endif() -unset(__CLDNN_CapsScanDir) -unset(__CLDNN_CapsScanDirFileCount) - -set(__CLDNN_Directory__gpu "${CMAKE_CURRENT_SOURCE_DIR}/../src/gpu") -set(__CLDNN_Label__gpu "${__CLDNN_Label__clDNN_copy}\\gpu") -file(GLOB __CLDNN_Sources__gpu - "${__CLDNN_Directory__gpu}/*.h" - "${__CLDNN_Directory__gpu}/*.hpp" - "${__CLDNN_Directory__gpu}/*.cpp" - "${__CLDNN_Directory__gpu}/*.inc" - ) - -set(__CLDNN_Directory__cache "${__CLDNN_Directory__gpu}/cache") -set(__CLDNN_Label__cache "${__CLDNN_Label__gpu}\\cache") -file(GLOB __CLDNN_Sources__cache - "${__CLDNN_Directory__cache}/*.h" - "${__CLDNN_Directory__cache}/*.hpp" - "${__CLDNN_Directory__cache}/*.cpp" - ) - -set(__CLDNN_Directory__ch_kernels "${__CLDNN_Directory__cache}/kernels") -set(__CLDNN_Label__ch_kernels "${__CLDNN_Label__cache}\\kernels") -file(GLOB __CLDNN_Sources__ch_kernels - "${__CLDNN_Directory__ch_kernels}/*.cl" - ) - -set(__CLDNN_Directory__cg_cache "${CLDNN__CODEGEN_INCDIR}") -set(__CLDNN_CGDirectory__cg_cache "${CLDNN__CODEGEN_DIR}/cache") -set(__CLDNN_Label__cg_cache "${__CLDNN_Label__cache}\\codegen") - -set(__CLDNN_Directory__ks_main "${CLDNN__KERNEL_SELECTOR_DIR}") -set(__CLDNN_Directory__ks_core "${CLDNN__KERNEL_SELECTOR_DIR}/core") -set(__CLDNN_Directory__ks_common "${CLDNN__KERNEL_SELECTOR_DIR}/common") -set(__CLDNN_Directory__ks_core_common "${__CLDNN_Directory__ks_core}/common") -set(__CLDNN_Directory__ks_actual_kernels "${__CLDNN_Directory__ks_core}/actual_kernels") -set(__CLDNN_Directory__ks_cache "${__CLDNN_Directory__ks_core}/cache") - - set(__CLDNN_AllSources - ${__CLDNN_Sources__clDNN_copy} - ${__CLDNN_Headers__api} - ${__CLDNN_Sources__graph_opt} - ${__CLDNN_Headers__include} - ${__CLDNN_Sources__caps} - ${__CLDNN_Headers__api__cpp} - ${__CLDNN_Headers__api__c} - ${__CLDNN_Headers__api_extension} - ${__CLDNN_Headers__api_extension__c} - ${__CLDNN_Headers__api_extension__cpp} ${__CLDNN_Sources__main} - ${__CLDNN_Sources__gpu} - ${__CLDNN_Sources__cache} - ${__CLDNN_Sources__ch_kernels} - ${__CLDNN_Sources__cg_cache} ${__CLDNN_Sources__test_cases} ${__CLDNN_Sources__test_utils} ${__CLDNN_Sources__gtest} @@ -237,40 +86,22 @@ set_property(SOURCE ${__CLDNN_Sources__cg_cache} PROPERTY GENERATED TRUE) # =============================================== Filters ============================================== -source_group("${__CLDNN_Label__api}" FILES ${__CLDNN_Headers__api}) -source_group("${__CLDNN_Label__api__cpp}" FILES ${__CLDNN_Headers__api__cpp}) -source_group("${__CLDNN_Label__api__c}" FILES ${__CLDNN_Headers__api__c}) -source_group("${__CLDNN_Label__api_extension}" FILES ${__CLDNN_Headers__api_extension}) -source_group("${__CLDNN_Label__api_extension__cpp}" FILES ${__CLDNN_Headers__api_extension__cpp}) -source_group("${__CLDNN_Label__api_extension__c}" FILES ${__CLDNN_Headers__api_extension__c}) -source_group("${__CLDNN_Label__include}" FILES ${__CLDNN_Headers__include}) -source_group("${__CLDNN_Label__graph_opt}" FILES ${__CLDNN_Sources__graph_opt}) -source_group("${__CLDNN_Label__caps}" FILES ${__CLDNN_Sources__caps}) source_group("${__CLDNN_Label__main}" FILES ${__CLDNN_Sources__main}) -source_group("${__CLDNN_Label__gpu}" FILES ${__CLDNN_Sources__gpu}) -source_group("${__CLDNN_Label__cache}" FILES ${__CLDNN_Sources__cache}) -source_group("${__CLDNN_Label__ch_kernels}" FILES ${__CLDNN_Sources__ch_kernels}) -source_group("${__CLDNN_Label__cg_cache}" FILES ${__CLDNN_Sources__cg_cache}) source_group("${__CLDNN_Label__test_cases}" FILES ${__CLDNN_Sources__test_cases}) source_group("${__CLDNN_Label__test_utils}" FILES ${__CLDNN_Sources__test_utils}) -source_group("${__CLDNN_Label__gtest}" FILES ${__CLDNN_Sources__gtest}) - # ===================================== Include/Link directories ======================================= include_directories( "${CLDNN__MAIN_DIR}" "${CLDNN__MAIN_DIR}/src" + "${CLDNN__MAIN_DIR}/src/include" + "${CLDNN__KERNEL_SELECTOR_DIR}/core" + "${CLDNN__KERNEL_SELECTOR_DIR}/core/common" + "${CLDNN__KERNEL_SELECTOR_DIR}/common" "${CLDNN__GTEST_DIR}" "${__CLDNN_Directory__test_utils}" "${CMAKE_CURRENT_SOURCE_DIR}" - "${__CLDNN_Directory__include}" - "${__CLDNN_IncDirectory__caps}" - "${__CLDNN_Directory__ks_core}" - "${__CLDNN_Directory__ks_core}/common" - "${__CLDNN_Directory__ks_actual_kernels}" - "${__CLDNN_Directory__ks_common}" - "${__CLDNN_Directory__gpu}" ) # =================================== Link targets and dependencies ==================================== @@ -283,13 +114,8 @@ add_executable("${CLDNN_BUILD__PROJ}" set_property(TARGET "${CLDNN_BUILD__PROJ}" PROPERTY PROJECT_LABEL "${CLDNN_BUILD__PROJ_LABEL}") set_property(TARGET "${CLDNN_BUILD__PROJ}" PROPERTY OUTPUT_NAME "${CLDNN_BUILD__PROJ_OUTPUT_NAME}") - # Set library dependencies -target_link_libraries("${CLDNN_BUILD__PROJ}" - # "${CLDNN_BUILD__PROJ__clDNN}" - OpenCL - cldnn_kernel_selector - ) +target_link_libraries("${CLDNN_BUILD__PROJ}" "${CLDNN_BUILD__PROJ__clDNN}") if(WIN32) target_link_libraries("${CLDNN_BUILD__PROJ}" setupapi) diff --git a/inference-engine/thirdparty/clDNN/tests_core_internal/test_cases/graph_manipulation_gpu_test.cpp b/inference-engine/thirdparty/clDNN/tests_core_internal/test_cases/graph_manipulation_gpu_test.cpp index c022b79..7b2fe96 100644 --- a/inference-engine/thirdparty/clDNN/tests_core_internal/test_cases/graph_manipulation_gpu_test.cpp +++ b/inference-engine/thirdparty/clDNN/tests_core_internal/test_cases/graph_manipulation_gpu_test.cpp @@ -19,7 +19,6 @@ #include #include "program_impl.h" -#include "api_impl.h" #include "topology_impl.h" #include "engine_impl.h" #include "memory_impl.h" @@ -62,9 +61,9 @@ TEST(basic, test1) { topology.add(concatenation("concat", { "reorder1", "weights2" }, concatenation::along_x)); topology.add(convolution("conv2", { "reorder2" }, { "concat" })); - program_impl::ptr prog = api_cast(engine.get())->build_program(*api_cast(topology.get()), build_opt, false); - cldnn::refcounted_obj_ptr net = api_cast(engine.get())->allocate_network(*prog, 0); - network network = (cldnn::network) api_cast(net.get()); + program_impl::ptr prog = engine.get()->build_program(*topology.get(), build_opt, false); + cldnn::refcounted_obj_ptr net = engine.get()->allocate_network(*prog, 0); + network network = (cldnn::network) net.get(); network.set_input_data("input", input); @@ -109,14 +108,14 @@ TEST(add_intermediate_gpu, test1) topology.add(cldnn::convolution("conv1b", { "input" }, { "weights" })); topology.add(cldnn::convolution("conv2a", { "conv1a" }, { "weights2" })); auto new_reorder = std::make_shared("reorder","nothing", input.get_layout()); - program_impl::ptr prog = api_cast(engine.get())->build_program(*api_cast(topology.get()), build_opt, false, true); + program_impl::ptr prog = engine.get()->build_program(*topology.get(), build_opt, false, true); prog->add_intermediate(new_reorder, prog->get_node("conv1a"), 0); prog->dump_program("custom_dump", true); program_impl_wrapper::run_graph_compilation(*prog); - cldnn::refcounted_obj_ptr net = api_cast(engine.get())->allocate_network(*prog, 0); - network network = (cldnn::network) api_cast(net.get()); + cldnn::refcounted_obj_ptr net = engine.get()->allocate_network(*prog, 0); + network network = (cldnn::network) net.get(); network.set_input_data("input", input); auto outputs = network.execute(); @@ -170,7 +169,7 @@ TEST(add_intermediate_gpu, test2) w_vec.push_back("weights"); auto new_conv = std::make_shared("conv1a", "input", w_vec); auto weights_node = std::make_shared("weights", weights); - program_impl::ptr prog = api_cast(engine.get())->build_program(*api_cast(topology.get()), build_opt, false, true); + program_impl::ptr prog = engine.get()->build_program(*topology.get(), build_opt, false, true); prog->add_intermediate(new_conv, prog->get_node("conv2a"), 0, true, true); program_impl_wrapper::add_connection(*prog, prog->get_or_create(weights_node), prog->get_or_create(new_conv)); @@ -178,8 +177,8 @@ TEST(add_intermediate_gpu, test2) program_impl_wrapper::run_graph_compilation(*prog); - cldnn::refcounted_obj_ptr net = api_cast(engine.get())->allocate_network(*prog, 0); - network network = (cldnn::network) api_cast(net.get()); + cldnn::refcounted_obj_ptr net = engine.get()->allocate_network(*prog, 0); + network network = (cldnn::network) net.get(); network.set_input_data("input", input); auto outputs = network.execute(); diff --git a/inference-engine/thirdparty/clDNN/tests_core_internal/test_cases/prepare_conv_eltw_fusing.cpp b/inference-engine/thirdparty/clDNN/tests_core_internal/test_cases/prepare_conv_eltw_fusing.cpp index 694530e..ff3d074 100644 --- a/inference-engine/thirdparty/clDNN/tests_core_internal/test_cases/prepare_conv_eltw_fusing.cpp +++ b/inference-engine/thirdparty/clDNN/tests_core_internal/test_cases/prepare_conv_eltw_fusing.cpp @@ -55,11 +55,13 @@ std::map test_prepare_conv_eltw_fusing(bool eltw1, topology.add(convolution("conv2", { "input" }, { "weights2" })); if (eltw1) { - topology.add(eltwise("eltw1", "conv1", "conv2", cldnn::eltwise_mode::sum, true)); + topology.add(eltwise("eltw1_no_relu", "conv1", "conv2", cldnn::eltwise_mode::sum)); + topology.add(activation("eltw1", "eltw1_no_relu", activation_func::relu)); } if (eltw2) { - topology.add(eltwise("eltw2", "conv2", "conv1", cldnn::eltwise_mode::sum, true)); + topology.add(eltwise("eltw2_no_relu", "conv2", "conv1", cldnn::eltwise_mode::sum)); + topology.add(activation("eltw2", "eltw2_no_relu", activation_func::relu)); } if (eltw1 && eltw2) { @@ -77,15 +79,15 @@ std::map test_prepare_conv_eltw_fusing(bool eltw1, { topology.add(eltwise("eltw3", "conv1", "conv2", cldnn::eltwise_mode::sum)); } - program_impl::ptr prog = api_cast(engine.get())->build_program(*api_cast(topology.get()), build_opt, false, true); + program_impl::ptr prog = engine.get()->build_program(*topology.get(), build_opt, false, true); layout_optimizer lo; program_impl_wrapper::apply_opt_pass(*prog, lo); program_impl_wrapper::run_graph_compilation(*prog); program_impl_wrapper::prepare_memory_dependencies(*prog); - cldnn::refcounted_obj_ptr net = api_cast(engine.get())->allocate_network(*prog, 0); - network network = (cldnn::network) api_cast(net.get()); + cldnn::refcounted_obj_ptr net = engine.get()->allocate_network(*prog, 0); + network network = (cldnn::network) net.get(); network.set_input_data("input", input); return network.execute(); diff --git a/inference-engine/thirdparty/fluid/checksum.txt b/inference-engine/thirdparty/fluid/checksum.txt index 120cf83..2aecff9 100644 --- a/inference-engine/thirdparty/fluid/checksum.txt +++ b/inference-engine/thirdparty/fluid/checksum.txt @@ -1 +1 @@ -c52fea0b05cc4febb909a59e0a32581a +95ada59df50003a87139bce41608578d diff --git a/inference-engine/thirdparty/fluid/modules/gapi/CMakeLists.txt b/inference-engine/thirdparty/fluid/modules/gapi/CMakeLists.txt index 613c87f..c801bee 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/CMakeLists.txt +++ b/inference-engine/thirdparty/fluid/modules/gapi/CMakeLists.txt @@ -44,6 +44,7 @@ set(gapi_srcs src/api/operators.cpp src/api/kernels_core.cpp src/api/kernels_imgproc.cpp + src/api/render.cpp # Compiler part src/compiler/gmodel.cpp diff --git a/inference-engine/thirdparty/fluid/modules/gapi/cmake/standalone.cmake b/inference-engine/thirdparty/fluid/modules/gapi/cmake/standalone.cmake index 9dd5540..e8dbcaa 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/cmake/standalone.cmake +++ b/inference-engine/thirdparty/fluid/modules/gapi/cmake/standalone.cmake @@ -37,6 +37,7 @@ set_property(TARGET ${FLUID_TARGET} PROPERTY CXX_STANDARD 11) if(MSVC) target_compile_options(${FLUID_TARGET} PUBLIC "/wd4251") + target_compile_options(${FLUID_TARGET} PUBLIC "/wd4275") target_compile_definitions(${FLUID_TARGET} PRIVATE _CRT_SECURE_NO_DEPRECATE) endif() diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi.hpp index a043a83..0600e24 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi.hpp @@ -22,12 +22,12 @@ @} */ -#include "opencv2/gapi/gmat.hpp" -#include "opencv2/gapi/garray.hpp" -#include "opencv2/gapi/gcomputation.hpp" -#include "opencv2/gapi/gcompiled.hpp" -#include "opencv2/gapi/gtyped.hpp" -#include "opencv2/gapi/gkernel.hpp" -#include "opencv2/gapi/operators.hpp" +#include +#include +#include +#include +#include +#include +#include #endif // OPENCV_GAPI_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/core.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/core.hpp index 6455429..a38d747 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/core.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/core.hpp @@ -12,9 +12,9 @@ #include -#include "opencv2/gapi/gmat.hpp" -#include "opencv2/gapi/gscalar.hpp" -#include "opencv2/gapi/gkernel.hpp" +#include +#include +#include /** \defgroup gapi_core G-API core (basic) functionality @{ @@ -398,6 +398,16 @@ namespace core { } }; + G_TYPED_KERNEL(GResizeP, , "org.opencv.core.transform.resizeP") { + static GMatDesc outMeta(GMatDesc in, Size sz, int interp) { + GAPI_Assert(in.depth == CV_8U); + GAPI_Assert(in.chan == 3); + GAPI_Assert(in.planar); + GAPI_Assert(interp == cv::INTER_LINEAR); + return in.withSize(sz); + } + }; + G_TYPED_KERNEL(GMerge3, , "org.opencv.core.transform.merge3") { static GMatDesc outMeta(GMatDesc in, GMatDesc, GMatDesc) { // Preserve depth and add channel component @@ -1342,10 +1352,28 @@ enlarge an image, it will generally look best with cv::INTER_CUBIC (slow) or cv: \f[\texttt{(double)dsize.height/src.rows}\f] @param interpolation interpolation method, see cv::InterpolationFlags -@sa warpAffine, warpPerspective, remap +@sa warpAffine, warpPerspective, remap, resizeP */ GAPI_EXPORTS GMat resize(const GMat& src, const Size& dsize, double fx = 0, double fy = 0, int interpolation = INTER_LINEAR); +/** @brief Resizes a planar image. + +The function resizes the image src down to or up to the specified size. +Planar image memory layout is three planes laying in the memory contiguously, +so the image height should be plane_height*plane_number, image type is @ref CV_8UC1. + +Output image size will have the size dsize, the depth of output is the same as of src. + +@note Function textual ID is "org.opencv.core.transform.resizeP" + +@param src input image, must be of @ref CV_8UC1 type; +@param dsize output image size; +@param interpolation interpolation method, only cv::INTER_LINEAR is supported at the moment + +@sa warpAffine, warpPerspective, remap, resize + */ +GAPI_EXPORTS GMatP resizeP(const GMatP& src, const Size& dsize, int interpolation = cv::INTER_LINEAR); + /** @brief Creates one 3-channel (4-channel) matrix out of 3(4) single-channel ones. The function merges several matrices to make a single multi-channel matrix. That is, each @@ -1564,25 +1592,6 @@ number of channels as in the input matrix. */ GAPI_EXPORTS GMat LUT(const GMat& src, const Mat& lut); -/** @brief Performs a 3D look-up table transform of a multi-channel matrix. - -The function LUT3D fills the output matrix with values from the look-up table. Indices of the entries -are taken from the input matrix. Interpolation is applied for mapping 0-255 range values to 0-16 range of 3DLUT table. -The function processes each element of src as follows: -@code{.cpp} - dst[i][j][k] = lut3D[~src_r][~src_g][~src_b]; -@endcode -where ~ means approximation. -Output is a matrix of of @ref CV_8UC3. - -@note Function textual ID is "org.opencv.core.transform.LUT3D" - -@param src input matrix of @ref CV_8UC3. -@param lut3D look-up table 17x17x17 3-channel elements. -@param interpolation The depth of interpoolation to be used. -*/ -GAPI_EXPORTS GMat LUT3D(const GMat& src, const GMat& lut3D, int interpolation = INTER_NEAREST); - /** @brief Converts a matrix to another data depth with optional scaling. The method converts source pixel values to the target data depth. saturate_cast\<\> is applied at diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/core.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/core.hpp index ec76fe5..ffd3596 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/core.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/core.hpp @@ -9,7 +9,7 @@ #define OPENCV_GAPI_CPU_CORE_API_HPP #include // GKernelPackage -#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS +#include // GAPI_EXPORTS namespace cv { namespace gapi { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp index 80392fe..4205776 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.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_GCPUKERNEL_HPP @@ -19,6 +19,7 @@ #include #include //to_ocv #include //suppress_unused_warning +#include // FIXME: namespace scheme for backends? namespace cv { @@ -43,13 +44,12 @@ namespace cpu * stack. Every backend is hardware-oriented and thus can run its * kernels efficiently on the target platform. * - * Backends are usually "back boxes" for G-API users -- on the API + * Backends are usually "black boxes" for G-API users -- on the API * side, all backends are represented as different objects of the - * same class cv::gapi::GBackend. User can manipulate with backends - * mainly by specifying which kernels to use or where to look up - * for kernels first. + * same class cv::gapi::GBackend. + * User can manipulate with backends by specifying which kernels to use. * - * @sa @ref gapi_hld, cv::gapi::lookup_order() + * @sa @ref gapi_hld */ /** @@ -259,7 +259,8 @@ struct OCVCallHelper, std::tuple > } // namespace detail template -class GCPUKernelImpl: public detail::OCVCallHelper +class GCPUKernelImpl: public cv::detail::OCVCallHelper, + public cv::detail::KernelTag { using P = detail::OCVCallHelper; diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp index 8a72312..a5dd4a6 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp @@ -17,9 +17,9 @@ #include #include -#include "opencv2/gapi/util/optional.hpp" -#include "opencv2/gapi/own/scalar.hpp" -#include "opencv2/gapi/own/mat.hpp" +#include +#include +#include namespace cv { namespace gapi { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp index b6adf9e..79c5c5f 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.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_FLUID_KERNEL_HPP @@ -99,12 +99,33 @@ struct GFluidOutputRois std::vector rois; }; +struct GFluidParallelOutputRois +{ + std::vector parallel_rois; +}; + +struct GFluidParallelFor +{ + std::function)> parallel_for; +}; + namespace detail { template<> struct CompileArgTag { static const char* tag() { return "gapi.fluid.outputRois"; } }; + +template<> struct CompileArgTag +{ + static const char* tag() { return "gapi.fluid.parallelFor"; } +}; + +template<> struct CompileArgTag +{ + static const char* tag() { return "gapi.fluid.parallelOutputRois"; } +}; + } // namespace detail namespace detail @@ -275,7 +296,7 @@ struct FluidCallHelper, std::tuple, UseScratch template -class GFluidKernelImpl +class GFluidKernelImpl : public cv::detail::KernelTag { static const int LPI = 1; static const auto Kind = GFluidKernel::Kind::Filter; diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/garg.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/garg.hpp index f8a3170..8f912aa 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/garg.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/garg.hpp @@ -12,17 +12,17 @@ #include #include -#include "opencv2/gapi/own/mat.hpp" +#include -#include "opencv2/gapi/util/any.hpp" -#include "opencv2/gapi/util/variant.hpp" +#include +#include -#include "opencv2/gapi/gmat.hpp" -#include "opencv2/gapi/gscalar.hpp" -#include "opencv2/gapi/garray.hpp" -#include "opencv2/gapi/gtype_traits.hpp" -#include "opencv2/gapi/gmetaarg.hpp" -#include "opencv2/gapi/own/scalar.hpp" +#include +#include +#include +#include +#include +#include namespace cv { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/garray.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/garray.hpp index 87d0015..b69fb5d 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/garray.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/garray.hpp @@ -18,7 +18,7 @@ #include #include -#include "opencv2/gapi/own/assert.hpp" +#include namespace cv { @@ -55,6 +55,12 @@ namespace detail class VectorRef; using ConstructVec = std::function; + // This is the base struct for GArrayU type holder + struct TypeHintBase{virtual ~TypeHintBase() = default;}; + + // This class holds type of initial GArray to be checked from GArrayU + template + struct TypeHint final : public TypeHintBase{}; // This class strips type information from GArray and makes it usable // in the G-API graph compiler (expression unrolling, graph generation, etc). @@ -64,6 +70,9 @@ namespace detail public: GArrayU(const GNode &n, std::size_t out); // Operation result constructor + template + bool holds() const; // Check if was created from GArray + GOrigin& priv(); // Internal use only const GOrigin& priv() const; // Internal use only @@ -73,7 +82,23 @@ namespace detail void setConstructFcn(ConstructVec &&cv); // Store T-aware constructor + template + void specifyType(); // Store type of initial GArray + std::shared_ptr m_priv; + std::shared_ptr m_hint; + }; + + template + bool GArrayU::holds() const{ + GAPI_Assert(m_hint != nullptr); + using U = typename std::decay::type; + return dynamic_cast*>(m_hint.get()) != nullptr; + }; + + template + void GArrayU::specifyType(){ + m_hint.reset(new TypeHint::type>); }; // This class represents a typed STL vector reference. @@ -239,7 +264,10 @@ public: private: static void VCTor(detail::VectorRef& vref) { vref.reset(); } - void putDetails() {m_ref.setConstructFcn(&VCTor); } + void putDetails() { + m_ref.setConstructFcn(&VCTor); + m_ref.specifyType(); + } detail::GArrayU m_ref; }; diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gasync_context.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gasync_context.hpp new file mode 100644 index 0000000..3e01577 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gasync_context.hpp @@ -0,0 +1,38 @@ +// 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_GASYNC_CONTEXT_HPP +#define OPENCV_GAPI_GASYNC_CONTEXT_HPP + +#if !defined(GAPI_STANDALONE) +# include +#else // Without OpenCV +# include +#endif // !defined(GAPI_STANDALONE) + +#include + +namespace cv { +namespace gapi{ +namespace wip { + +class GAPI_EXPORTS GAsyncContext{ + std::atomic cancelation_requested = {false}; +public: + //returns true if it was a first request to cancel the context + bool cancel(); + bool isCanceled() const; +}; + +class GAPI_EXPORTS GAsyncCanceled : public std::exception { +public: + virtual const char* what() const noexcept CV_OVERRIDE; +}; +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif //OPENCV_GAPI_GASYNC_CONTEXT_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcall.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcall.hpp index 50223ce..87cba52 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcall.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcall.hpp @@ -8,10 +8,10 @@ #ifndef OPENCV_GAPI_GCALL_HPP #define OPENCV_GAPI_GCALL_HPP -#include "opencv2/gapi/garg.hpp" // GArg -#include "opencv2/gapi/gmat.hpp" // GMat -#include "opencv2/gapi/gscalar.hpp" // GScalar -#include "opencv2/gapi/garray.hpp" // GArray +#include // GArg +#include // GMat +#include // GScalar +#include // GArray namespace cv { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcommon.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcommon.hpp index 6a3f51f..dac640a 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcommon.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcommon.hpp @@ -14,9 +14,9 @@ #include -#include "opencv2/gapi/util/any.hpp" -#include "opencv2/gapi/own/exports.hpp" -#include "opencv2/gapi/own/assert.hpp" +#include +#include +#include namespace cv { @@ -29,6 +29,12 @@ namespace detail { static const char* tag() { return ""; }; }; + + // These structures are tags which separate kernels and transformations + struct KernelTag + {}; + struct TransformTag + {}; } // This definition is here because it is reused by both public(?) and internal diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompiled.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompiled.hpp index ad491b7..c825edf 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompiled.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompiled.hpp @@ -10,9 +10,9 @@ #include -#include "opencv2/gapi/opencv_includes.hpp" -#include "opencv2/gapi/own/assert.hpp" -#include "opencv2/gapi/garg.hpp" +#include +#include +#include namespace cv { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompiled_async.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompiled_async.hpp index a9d946b..96da0d7 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompiled_async.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompiled_async.hpp @@ -11,7 +11,8 @@ #include //for std::future #include //for std::exception_ptr #include //for std::function -#include "opencv2/gapi/garg.hpp" +#include +#include namespace cv { //fwd declaration @@ -19,13 +20,22 @@ namespace cv { namespace gapi{ namespace wip { + class GAsyncContext; //These functions asynchronously (i.e. probably on a separate thread of execution) call operator() member function of their first argument with copies of rest of arguments (except callback) passed in. //The difference between the function is the way to get the completion notification (via callback or a waiting on std::future object) //If exception is occurred during execution of apply it is transfered to the callback (via function parameter) or passed to future (and will be thrown on call to std::future::get) + + //N.B. : + //Input arguments are copied on call to async function (actually on call to cv::gin) and thus do not have to outlive the actual completion of asynchronous activity. + //While Output arguments are "captured" by reference(pointer) and therefore _must_ outlive the asynchronous activity + //(i.e. live at least until callback is called or future is unblocked) GAPI_EXPORTS void async(GCompiled& gcmpld, std::function&& callback, GRunArgs &&ins, GRunArgsP &&outs); + GAPI_EXPORTS void async(GCompiled& gcmpld, std::function&& callback, GRunArgs &&ins, GRunArgsP &&outs, GAsyncContext& ctx); + GAPI_EXPORTS std::future async(GCompiled& gcmpld, GRunArgs &&ins, GRunArgsP &&outs); -} // namespace gapi + GAPI_EXPORTS std::future async(GCompiled& gcmpld, GRunArgs &&ins, GRunArgsP &&outs, GAsyncContext& ctx); } // namespace wip +} // namespace gapi } // namespace cv #endif // OPENCV_GAPI_GCOMPILED_ASYNC_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp index f4c0234..7d960cd 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompoundkernel.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcompoundkernel.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_GCOMPOUNDKERNEL_HPP @@ -65,22 +65,6 @@ template struct get_compound_in> } }; -// Kernel may return one object(GMat, GScalar) or a tuple of objects. -// This helper is needed to cast return value to the same form(tuple) -template -struct tuple_wrap_helper; - -template struct tuple_wrap_helper -{ - static std::tuple get(T&& obj) { return std::make_tuple(std::move(obj)); } -}; - -template -struct tuple_wrap_helper> -{ - static std::tuple get(std::tuple&& objs) { return std::forward>(objs); } -}; - template struct GCompoundCallHelper; @@ -104,7 +88,8 @@ struct GCompoundCallHelper, std::tuple > }; template -class GCompoundKernelImpl: public cv::detail::GCompoundCallHelper +class GCompoundKernelImpl: public cv::detail::GCompoundCallHelper, + public cv::detail::KernelTag { using P = cv::detail::GCompoundCallHelper; diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcomputation.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcomputation.hpp index 439b349..fdeef86 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcomputation.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcomputation.hpp @@ -10,11 +10,11 @@ #include -#include "opencv2/gapi/util/util.hpp" -#include "opencv2/gapi/gcommon.hpp" -#include "opencv2/gapi/gproto.hpp" -#include "opencv2/gapi/garg.hpp" -#include "opencv2/gapi/gcompiled.hpp" +#include +#include +#include +#include +#include namespace cv { @@ -315,7 +315,7 @@ public: * inputs/outputs which were used to define this GComputation. */ void apply(const std::vector& ins, // Compatibility overload - const std::vector& outs, + std::vector& outs, GCompileArgs &&args = {}); #endif // !defined(GAPI_STANDALONE) // Various versions of compile(): ////////////////////////////////////////// diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcomputation_async.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcomputation_async.hpp index f2a6d8d..661e097 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcomputation_async.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gcomputation_async.hpp @@ -8,24 +8,35 @@ #define OPENCV_GAPI_GCOMPUTATION_ASYNC_HPP -#include +#include //for std::future #include //for std::exception_ptr #include //for std::function -#include "opencv2/gapi/garg.hpp" //for GRunArgs, GRunArgsP -#include "opencv2/gapi/gcommon.hpp" //for GCompileArgs +#include //for GRunArgs, GRunArgsP +#include //for GCompileArgs +#include + namespace cv { //fwd declaration class GComputation; namespace gapi { namespace wip { + class GAsyncContext; //These functions asynchronously (i.e. probably on a separate thread of execution) call apply member function of their first argument with copies of rest of arguments (except callback) passed in. //The difference between the function is the way to get the completion notification (via callback or a waiting on std::future object) //If exception is occurred during execution of apply it is transfered to the callback (via function parameter) or passed to future (and will be thrown on call to std::future::get) + + //N.B. : + //Input arguments are copied on call to async function (actually on call to cv::gin) and thus do not have to outlive the actual completion of asynchronous activity. + //While Output arguments are "captured" by reference(pointer) and therefore _must_ outlive the asynchronous activity + //(i.e. live at least until callback is called or future is unblocked) GAPI_EXPORTS void async_apply(GComputation& gcomp, std::function&& callback, GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args = {}); + GAPI_EXPORTS void async_apply(GComputation& gcomp, std::function&& callback, GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args, GAsyncContext& ctx); + GAPI_EXPORTS std::future async_apply(GComputation& gcomp, GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args = {}); -} // nmaepspace gapi + GAPI_EXPORTS std::future async_apply(GComputation& gcomp, GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args, GAsyncContext& ctx); } // namespace wip +} // namespace gapi } // namespace cv diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gkernel.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gkernel.hpp index af18424..b8d1dbb 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gkernel.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gkernel.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_GKERNEL_HPP @@ -14,7 +14,6 @@ #include // false_type, true_type #include // map (for GKernelPackage) #include // tuple -#include // lookup order #include // CompileArgTag #include // Seq @@ -23,7 +22,7 @@ #include // GMetaArg #include // GTypeTraits #include //suppress_unused_warning - +#include namespace cv { @@ -57,7 +56,6 @@ namespace detail // namespace { - template struct Yield; template<> struct Yield { @@ -173,12 +171,12 @@ namespace detail // GKernelType and GKernelTypeM are base classes which implement typed ::on() // method based on kernel signature. GKernelTypeM stands for multiple-return-value kernels // -// G_TYPED_KERNEL and G_TYPED_KERNEK_M macros inherit user classes from GKernelType and +// G_TYPED_KERNEL and G_TYPED_KERNEL_M macros inherit user classes from GKernelType and // GKernelTypeM respectively. template class GKernelTypeM(Args...)> >: - public detail::MetaHelper, std::tuple > + public detail::MetaHelper, std::tuple> { template static std::tuple yield(cv::GCall &call, detail::Seq) @@ -202,7 +200,7 @@ template class GKernelType; template class GKernelType >: - public detail::MetaHelper, R > + public detail::MetaHelper, R> { public: using InArgs = std::tuple; @@ -243,18 +241,11 @@ public: #define G_TYPED_KERNEL_M(Class, API, Id) \ G_ID_HELPER_BODY(Class, Id) \ struct Class final: public cv::GKernelTypeM, \ - public detail::G_ID_HELPER_CLASS(Class) \ + public detail::G_ID_HELPER_CLASS(Class) // {body} is to be defined by user namespace cv { -// Declare in cv:: namespace -enum class unite_policy -{ - REPLACE, - KEEP -}; - namespace gapi { // Prework: model "Device" API before it gets to G-API headers. @@ -303,42 +294,16 @@ namespace gapi { * @{ */ - // Lookup order is in fact a vector of Backends to traverse during look-up - /** - * @brief Priority list of backends to use during kernel - * resolution process. - * - * Priority is descending -- the first backend in the list has the - * top priority, and the last one has the lowest priority. - * - * If there's multiple implementations available for a kernel at - * the moment of graph compilation, a kernel (and thus a backend) - * will be selected according to this order (if the parameter is passed). - * - * Default order is not specified (and by default, only - * CPU(OpenCV) backend is involved in graph compilation). - */ - using GLookupOrder = std::vector; - /** - * @brief Create a backend lookup order -- priority list of - * backends to use during graph compilation process. - * - * @sa GLookupOrder, @ref gapi_std_backends - */ - inline GLookupOrder lookup_order(std::initializer_list &&list) - { - return GLookupOrder(std::move(list)); - } - // FIXME: Hide implementation /** * @brief A container class for heterogeneous kernel - * implementation collections. + * implementation collections and graph transformations. * * GKernelPackage is a special container class which stores kernel - * _implementations_. Objects of this class are created and passed - * to cv::GComputation::compile() to specify which kernels to use - * in the compiled graph. GKernelPackage may contain kernels of + * _implementations_ and graph _transformations_. Objects of this class + * are created and passed to cv::GComputation::compile() to specify + * which kernels to use and which transformations to apply in the + * compiled graph. GKernelPackage may contain kernels of * different backends, e.g. be heterogeneous. * * The most easy way to create a kernel package is to use function @@ -350,23 +315,23 @@ namespace gapi { * with an empty package (created with the default constructor) * and then by populating it with kernels via call to * GKernelPackage::include(). Note this method is also a template - * one since G-API kernel implementations are _types_, not objects. + * one since G-API kernel and transformation implementations are _types_, + * not objects. * * Finally, two kernel packages can be combined into a new one - * with function cv::gapi::combine(). There are different rules - * apply to this process, see also cv::gapi::unite_policy for - * details. + * with function cv::gapi::combine(). */ class GAPI_EXPORTS GKernelPackage { + /// @private - using S = std::unordered_map; + using M = std::unordered_map>; /// @private - using M = std::unordered_map; + M m_id_kernels; /// @private - M m_backend_kernels; + std::vector m_transformations; protected: /// @private @@ -378,30 +343,64 @@ namespace gapi { // Remove ALL implementations of the given API (identified by ID) void removeAPI(const std::string &id); + /// @private + // Partial include() specialization for kernels + template + typename std::enable_if<(std::is_base_of::value), void>::type + includeHelper() + { + auto backend = KImpl::backend(); + auto kernel_id = KImpl::API::id(); + auto kernel_impl = GKernelImpl{KImpl::kernel()}; + removeAPI(kernel_id); + + m_id_kernels[kernel_id] = std::make_pair(backend, kernel_impl); + } + + /// @private + // Partial include() specialization for transformations + template + typename std::enable_if<(std::is_base_of::value), void>::type + includeHelper() + { + m_transformations.emplace_back(TImpl::transformation()); + } + public: /** - * @brief Returns total number of kernels in the package - * (across all backends included) + * @brief Returns total number of kernels + * in the package (across all backends included) * * @return a number of kernels in the package */ std::size_t size() const; /** + * @brief Returns vector of transformations included in the package + * + * @return vector of transformations included in the package + */ + const std::vector& get_transformations() const; + + /** * @brief Test if a particular kernel _implementation_ KImpl is * included in this kernel package. * * @sa includesAPI() * + * @note cannot be applied to transformations + * * @return true if there is such kernel, false otherwise. */ template bool includes() const { - const auto set_iter = m_backend_kernels.find(KImpl::backend()); - return (set_iter != m_backend_kernels.end()) - ? (set_iter->second.count(KImpl::API::id()) > 0) - : false; + static_assert(std::is_base_of::value, + "includes() can be applied to kernels only"); + + auto kernel_it = m_id_kernels.find(KImpl::API::id()); + return kernel_it != m_id_kernels.end() && + kernel_it->second.first == KImpl::backend(); } /** @@ -439,47 +438,33 @@ namespace gapi { } /** - * @brief Find a kernel (by its API), given the look-up order. + * @brief Find a kernel (by its API) * - * If order is empty, returns first suitable implementation. + * Returns implementation corresponding id. * Throws if nothing found. * * @return Backend which hosts matching kernel implementation. * - * @sa cv::gapi::lookup_order */ template - GBackend lookup(const GLookupOrder &order = {}) const + GBackend lookup() const { - return lookup(KAPI::id(), order).first; + return lookup(KAPI::id()).first; } /// @private std::pair - lookup(const std::string &id, const GLookupOrder &order = {}) const; + lookup(const std::string &id) const; // FIXME: No overwrites allowed? /** - * @brief Put a new kernel implementation KImpl into package. - * - * @param up unite policy to use. If the package has already - * implementation for this kernel (probably from another - * backend), and cv::unite_policy::KEEP is passed, the - * existing implementation remains in package; on - * cv::unite_policy::REPLACE all other existing - * implementations are first dropped from the package. + * @brief Put a new kernel implementation or a new transformation + * KImpl into the package. */ template - void include(const cv::unite_policy up = cv::unite_policy::KEEP) + void include() { - auto backend = KImpl::backend(); - auto kernel_id = KImpl::API::id(); - auto kernel_impl = GKernelImpl{KImpl::kernel()}; - if (up == cv::unite_policy::REPLACE) removeAPI(kernel_id); - else GAPI_Assert(up == cv::unite_policy::KEEP); - - // Regardless of the policy, store new impl in its storage slot. - m_backend_kernels[backend][kernel_id] = std::move(kernel_impl); + includeHelper(); } /** @@ -492,36 +477,27 @@ namespace gapi { // TODO: Doxygen bug -- it wants me to place this comment // here, not below. /** - * @brief Create a new package based on `lhs` and `rhs`, - * with unity policy defined by `policy`. + * @brief Create a new package based on `lhs` and `rhs`. * * @param lhs "Left-hand-side" package in the process * @param rhs "Right-hand-side" package in the process - * @param policy Unite policy which is used in case of conflicts - * -- when the same kernel API is implemented in both packages by - * different backends; cv::unite_policy::KEEP keeps both - * implementation in the resulting package, while - * cv::unite_policy::REPLACE gives precedence two kernels from - * "Right-hand-side". - * * @return a new kernel package. */ friend GAPI_EXPORTS GKernelPackage combine(const GKernelPackage &lhs, - const GKernelPackage &rhs, - const cv::unite_policy policy); + const GKernelPackage &rhs); }; /** * @brief Create a kernel package object containing kernels - * specified in variadic template argument. + * and transformations specified in variadic template argument. * - * In G-API, kernel implementations are _types_. Every backend has - * its own kernel API (like GAPI_OCV_KERNEL() and + * In G-API, kernel implementations and transformations are _types_. + * Every backend has its own kernel API (like GAPI_OCV_KERNEL() and * GAPI_FLUID_KERNEL()) but all of that APIs define a new type for * each kernel implementation. * * Use this function to pass kernel implementations (defined in - * either way) to the system. Example: + * either way) and transformations to the system. Example: * * @snippet modules/gapi/samples/api_ref_snippets.cpp kernels_snippet * @@ -531,6 +507,10 @@ namespace gapi { */ template GKernelPackage kernels() { + // FIXME: currently there is no check that transformations' signatures are unique + // and won't be any intersection in graph compilation stage + static_assert(detail::all_unique::value, "Kernels API must be unique"); + GKernelPackage pkg; // For those who wonder - below is a trick to call a number of @@ -539,7 +519,6 @@ namespace gapi { // Just note that `f(),a` always equals to `a` (with f() called!) // and parentheses are used to hide function call in the expanded sequence. // Leading 0 helps to handle case when KK is an empty list (kernels<>()). - int unused[] = { 0, (pkg.include(), 0)... }; cv::util::suppress_unused_warning(unused); return pkg; @@ -548,8 +527,17 @@ namespace gapi { /** @} */ GAPI_EXPORTS GKernelPackage combine(const GKernelPackage &lhs, - const GKernelPackage &rhs, - const cv::unite_policy policy); + const GKernelPackage &rhs); + /** + * @brief cv::use_only() is a special combinator which hints G-API to use only + * kernels specified in cv::GComputation::compile() (and not to extend kernels available by + * default with that package). + */ + struct GAPI_EXPORTS use_only + { + GKernelPackage pkg; + }; + } // namespace gapi namespace detail @@ -558,9 +546,10 @@ namespace detail { static const char* tag() { return "gapi.kernel_package"; } }; - template<> struct CompileArgTag + + template<> struct CompileArgTag { - static const char* tag() { return "gapi.lookup_order"; } + static const char* tag() { return "gapi.use_only"; } }; } // namespace detail } // namespace cv diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gmat.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gmat.hpp index f0ce26b..d6b6e72 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gmat.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gmat.hpp @@ -14,9 +14,9 @@ #include #include // GShape -#include "opencv2/gapi/own/types.hpp" // cv::gapi::own::Size -#include "opencv2/gapi/own/convert.hpp" // to_own -#include "opencv2/gapi/own/assert.hpp" +#include // cv::gapi::own::Size +#include // to_own +#include // TODO GAPI_EXPORTS or so namespace cv diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gmetaarg.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gmetaarg.hpp index abdea75..5b6e2ad 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gmetaarg.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gmetaarg.hpp @@ -11,12 +11,12 @@ #include #include -#include "opencv2/gapi/util/util.hpp" -#include "opencv2/gapi/util/variant.hpp" +#include +#include -#include "opencv2/gapi/gmat.hpp" -#include "opencv2/gapi/gscalar.hpp" -#include "opencv2/gapi/garray.hpp" +#include +#include +#include namespace cv { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gproto.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gproto.hpp index a3bce1a..b9e206a 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gproto.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gproto.hpp @@ -12,13 +12,13 @@ #include #include -#include "opencv2/gapi/util/variant.hpp" +#include -#include "opencv2/gapi/gmat.hpp" -#include "opencv2/gapi/gscalar.hpp" -#include "opencv2/gapi/garray.hpp" -#include "opencv2/gapi/garg.hpp" -#include "opencv2/gapi/gmetaarg.hpp" +#include +#include +#include +#include +#include namespace cv { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/core.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/core.hpp index f780545..a7ee595 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/core.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/core.hpp @@ -8,10 +8,10 @@ #ifndef OPENCV_GAPI_GPU_CORE_API_HPP #define OPENCV_GAPI_GPU_CORE_API_HPP /** @file -* @deprecated Use "opencv2/gapi/ocl/core.hpp" instead. +* @deprecated Use instead. */ -#include "opencv2/gapi/ocl/core.hpp" +#include namespace cv { namespace gapi { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/ggpukernel.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/ggpukernel.hpp index f41cf13..b52c21d 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/ggpukernel.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/ggpukernel.hpp @@ -8,10 +8,10 @@ #ifndef OPENCV_GAPI_GGPUKERNEL_HPP #define OPENCV_GAPI_GGPUKERNEL_HPP /** @file -* @deprecated Use "opencv2/gapi/ocl/goclkernel.hpp" instead. +* @deprecated Use instead. */ -#include "opencv2/gapi/ocl/goclkernel.hpp" +#include #define GAPI_GPU_KERNEL GAPI_OCL_KERNEL diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/imgproc.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/imgproc.hpp index 81ae0cb..b0df7ae 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/imgproc.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gpu/imgproc.hpp @@ -8,10 +8,10 @@ #ifndef OPENCV_GAPI_GPU_IMGPROC_API_HPP #define OPENCV_GAPI_GPU_IMGPROC_API_HPP /** @file -* @deprecated Use "opencv2/gapi/ocl/imgproc.hpp" instead. +* @deprecated Use instead. */ -#include "opencv2/gapi/ocl/imgproc.hpp" +#include namespace cv { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gscalar.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gscalar.hpp index dd1205b..f65741e 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gscalar.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gscalar.hpp @@ -14,7 +14,7 @@ #include #include // GShape #include -#include "opencv2/gapi/own/scalar.hpp" +#include namespace cv { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gtransform.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gtransform.hpp new file mode 100644 index 0000000..5d1b91b --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gtransform.hpp @@ -0,0 +1,103 @@ +// 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_GTRANSFORM_HPP +#define OPENCV_GAPI_GTRANSFORM_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace cv +{ + +struct GAPI_EXPORTS GTransform +{ + // FIXME: consider another simplified + // class instead of GComputation + using F = std::function; + + std::string description; + F pattern; + F substitute; + + GTransform(const std::string& d, const F &p, const F &s) : description(d), pattern(p), substitute(s){}; +}; + +namespace detail +{ + +template +struct TransHelper; + +template +struct TransHelper, Out> +{ + template + static GComputation invoke(Callable f, Seq, Seq) + { + const std::tuple ins; + const auto r = tuple_wrap_helper::get(f(std::get(ins)...)); + return GComputation(cv::GIn(std::get(ins)...), + cv::GOut(std::get(r)...)); + } + + static GComputation get_pattern() + { + return invoke(K::pattern, typename MkSeq::type(), + typename MkSeq::type>::value>::type()); + } + static GComputation get_substitute() + { + return invoke(K::substitute, typename MkSeq::type(), + typename MkSeq::type>::value>::type()); + } +}; +} // namespace detail + +template +class GTransformImpl; + +template +class GTransformImpl> : public cv::detail::TransHelper, R>, + public cv::detail::TransformTag +{ +public: + // FIXME: currently there is no check that transformations' signatures are unique + // and won't be any intersection in graph compilation stage + using API = K; + + static GTransform transformation() + { + return GTransform(K::descr(), &K::get_pattern, &K::get_substitute); + } +}; +} // namespace cv + +#define G_DESCR_HELPER_CLASS(Class) Class##DescrHelper + +#define G_DESCR_HELPER_BODY(Class, Descr) \ + namespace detail \ + { \ + struct G_DESCR_HELPER_CLASS(Class) \ + { \ + static constexpr const char *descr() { return Descr; }; \ + }; \ + } + +#define GAPI_TRANSFORM(Class, API, Descr) \ + G_DESCR_HELPER_BODY(Class, Descr) \ + struct Class final : public cv::GTransformImpl, \ + public detail::G_DESCR_HELPER_CLASS(Class) + +#endif // OPENCV_GAPI_GTRANSFORM_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gtyped.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gtyped.hpp index a966f26..8e48e91 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gtyped.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/gtyped.hpp @@ -11,10 +11,10 @@ #include -#include "opencv2/gapi/gcomputation.hpp" -#include "opencv2/gapi/gcompiled.hpp" -#include "opencv2/gapi/gproto.hpp" -#include "opencv2/gapi/gcommon.hpp" +#include +#include +#include +#include namespace cv { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/imgproc.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/imgproc.hpp index db9ac46..ad3f7c1 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/imgproc.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/imgproc.hpp @@ -8,13 +8,13 @@ #ifndef OPENCV_GAPI_IMGPROC_HPP #define OPENCV_GAPI_IMGPROC_HPP -#include "opencv2/imgproc.hpp" +#include #include // std::tuple -#include "opencv2/gapi/gkernel.hpp" -#include "opencv2/gapi/gmat.hpp" -#include "opencv2/gapi/gscalar.hpp" +#include +#include +#include /** \defgroup gapi_imgproc G-API image processing functionality @@ -187,6 +187,55 @@ namespace imgproc { return in.withType(CV_8U, 1); } }; + + G_TYPED_KERNEL(GBayerGR2RGB, , "org.opencv.imgproc.colorconvert.bayergr2rgb") { + static cv::GMatDesc outMeta(cv::GMatDesc in) { + return in.withType(CV_8U, 3); + } + }; + + G_TYPED_KERNEL(GRGB2HSV, , "org.opencv.imgproc.colorconvert.rgb2hsv") { + static cv::GMatDesc outMeta(cv::GMatDesc in) { + return in; + } + }; + + G_TYPED_KERNEL(GRGB2YUV422, , "org.opencv.imgproc.colorconvert.rgb2yuv422") { + static cv::GMatDesc outMeta(cv::GMatDesc in) { + GAPI_Assert(in.depth == CV_8U); + GAPI_Assert(in.chan == 3); + return in.withType(in.depth, 2); + } + }; + + G_TYPED_KERNEL(GNV12toRGBp, , "org.opencv.colorconvert.imgproc.nv12torgbp") { + static GMatDesc outMeta(GMatDesc inY, GMatDesc inUV) { + GAPI_Assert(inY.depth == CV_8U); + GAPI_Assert(inUV.depth == CV_8U); + GAPI_Assert(inY.chan == 1); + GAPI_Assert(inY.planar == false); + GAPI_Assert(inUV.chan == 2); + GAPI_Assert(inUV.planar == false); + GAPI_Assert(inY.size.width == 2 * inUV.size.width); + GAPI_Assert(inY.size.height == 2 * inUV.size.height); + return inY.withType(CV_8U, 3).asPlanar(); + } + }; + + G_TYPED_KERNEL(GNV12toBGRp, , "org.opencv.colorconvert.imgproc.nv12tobgrp") { + static GMatDesc outMeta(GMatDesc inY, GMatDesc inUV) { + GAPI_Assert(inY.depth == CV_8U); + GAPI_Assert(inUV.depth == CV_8U); + GAPI_Assert(inY.chan == 1); + GAPI_Assert(inY.planar == false); + GAPI_Assert(inUV.chan == 2); + GAPI_Assert(inUV.planar == false); + GAPI_Assert(inY.size.width == 2 * inUV.size.width); + GAPI_Assert(inY.size.height == 2 * inUV.size.height); + return inY.withType(CV_8U, 3).asPlanar(); + } + }; + } @@ -784,6 +833,85 @@ Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. @sa YUV2BGR, NV12toRGB */ GAPI_EXPORTS GMat NV12toBGR(const GMat& src_y, const GMat& src_uv); + +/** @brief Converts an image from BayerGR color space to RGB. +The function converts an input image from BayerGR color space to RGB. +The conventional ranges for G, R, and B channel values are 0 to 255. + +Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.bayergr2rgb" + +@param src_gr input image: 8-bit unsigned 1-channel image @ref CV_8UC1. + +@sa YUV2BGR, NV12toRGB +*/ +GAPI_EXPORTS GMat BayerGR2RGB(const GMat& src_gr); + +/** @brief Converts an image from RGB color space to HSV. +The function converts an input image from RGB color space to HSV. +The conventional ranges for R, G, and B channel values are 0 to 255. + +Output image must be 8-bit unsigned 3-channel image @ref CV_8UC3. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.rgb2hsv" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. + +@sa YUV2BGR, NV12toRGB +*/ +GAPI_EXPORTS GMat RGB2HSV(const GMat& src); + +/** @brief Converts an image from RGB color space to YUV422. +The function converts an input image from RGB color space to YUV422. +The conventional ranges for R, G, and B channel values are 0 to 255. + +Output image must be 8-bit unsigned 2-channel image @ref CV_8UC2. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.rgb2yuv422" + +@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3. + +@sa YUV2BGR, NV12toRGB +*/ +GAPI_EXPORTS GMat RGB2YUV422(const GMat& src); + +/** @brief Converts an image from NV12 (YUV420p) color space to RGB. +The function converts an input image from NV12 color space to RGB. +The conventional ranges for Y, U, and V channel values are 0 to 255. + +Output image must be 8-bit unsigned planar 3-channel image @ref CV_8UC1. +Planar image memory layout is three planes laying in the memory contiguously, +so the image height should be plane_height*plane_number, +image type is @ref CV_8UC1. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.nv12torgbp" + +@param src_y input image: 8-bit unsigned 1-channel image @ref CV_8UC1. +@param src_uv input image: 8-bit unsigned 2-channel image @ref CV_8UC2. + +@sa YUV2RGB, NV12toBGRp, NV12toRGB +*/ +GAPI_EXPORTS GMatP NV12toRGBp(const GMat &src_y, const GMat &src_uv); + +/** @brief Converts an image from NV12 (YUV420p) color space to BGR. +The function converts an input image from NV12 color space to BGR. +The conventional ranges for Y, U, and V channel values are 0 to 255. + +Output image must be 8-bit unsigned planar 3-channel image @ref CV_8UC1. +Planar image memory layout is three planes laying in the memory contiguously, +so the image height should be plane_height*plane_number, +image type is @ref CV_8UC1. + +@note Function textual ID is "org.opencv.imgproc.colorconvert.nv12torgbp" + +@param src_y input image: 8-bit unsigned 1-channel image @ref CV_8UC1. +@param src_uv input image: 8-bit unsigned 2-channel image @ref CV_8UC2. + +@sa YUV2RGB, NV12toRGBp, NV12toBGR +*/ +GAPI_EXPORTS GMatP NV12toBGRp(const GMat &src_y, const GMat &src_uv); + //! @} gapi_colorconvert } //namespace gapi } //namespace cv diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/ocl/goclkernel.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/ocl/goclkernel.hpp index ea2cda0..6927492 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/ocl/goclkernel.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/ocl/goclkernel.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_GOCLKERNEL_HPP @@ -226,7 +226,8 @@ struct OCLCallHelper, std::tuple > } // namespace detail template -class GOCLKernelImpl: public detail::OCLCallHelper +class GOCLKernelImpl: public cv::detail::OCLCallHelper, + public cv::detail::KernelTag { using P = detail::OCLCallHelper; diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/operators.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/operators.hpp index 27a1d80..b20062c 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/operators.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/operators.hpp @@ -8,8 +8,8 @@ #ifndef OPENCV_GAPI_OPERATORS_HPP #define OPENCV_GAPI_OPERATORS_HPP -#include "opencv2/gapi/gmat.hpp" -#include "opencv2/gapi/gscalar.hpp" +#include +#include GAPI_EXPORTS cv::GMat operator+(const cv::GMat& lhs, const cv::GMat& rhs); diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/assert.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/assert.hpp index 10ec240..92b246f 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/assert.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/assert.hpp @@ -16,7 +16,7 @@ #else #include #include -#include "opencv2/gapi/util/throw.hpp" +#include namespace detail { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/convert.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/convert.hpp index 8c1feb4..4a7394a 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/convert.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/convert.hpp @@ -13,7 +13,7 @@ #include #include #include -#include "opencv2/gapi/own/scalar.hpp" +#include namespace cv { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/cvdefs.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/cvdefs.hpp index e110536..71c2aa8 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/cvdefs.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/cvdefs.hpp @@ -108,6 +108,10 @@ typedef unsigned short ushort; #define CV_ELEM_SIZE(type) \ (CV_MAT_CN(type) << ((((sizeof(size_t)/4+1)*16384|0x3a50) >> CV_MAT_DEPTH(type)*2) & 3)) +#ifndef CV_OVERRIDE +# define CV_OVERRIDE override +#endif + // base.h: namespace cv { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/exports.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/exports.hpp index 0d955d0..53bff2a 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/exports.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/exports.hpp @@ -8,11 +8,13 @@ #ifndef OPENCV_GAPI_OWN_TYPES_HPP #define OPENCV_GAPI_OWN_TYPES_HPP -# if 0 +# if defined(__OPENCV_BUILD) # include # define GAPI_EXPORTS CV_EXPORTS - # else +# define GAPI_EXPORTS + +#if 0 // Note: the following version currently is not needed for non-OpenCV build # if defined _WIN32 # define GAPI_EXPORTS __declspec(dllexport) # elif defined __GNUC__ && __GNUC__ >= 4 @@ -22,6 +24,7 @@ # ifndef GAPI_EXPORTS # define GAPI_EXPORTS # endif +#endif # endif diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/mat.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/mat.hpp index 73f3afc..20f5b55 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/mat.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/own/mat.hpp @@ -8,15 +8,15 @@ #ifndef OPENCV_GAPI_OWN_MAT_HPP #define OPENCV_GAPI_OWN_MAT_HPP -#include "opencv2/gapi/opencv_includes.hpp" -#include "opencv2/gapi/own/types.hpp" -#include "opencv2/gapi/own/scalar.hpp" -#include "opencv2/gapi/own/saturate.hpp" -#include "opencv2/gapi/own/assert.hpp" +#include +#include +#include +#include +#include #include //std::shared_ptr #include //std::memcpy -#include "opencv2/gapi/util/throw.hpp" +#include namespace cv { namespace gapi { namespace own { namespace detail { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/render.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/render.hpp new file mode 100644 index 0000000..2dd60da --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/render.hpp @@ -0,0 +1,113 @@ +// 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_HPP +#define OPENCV_GAPI_RENDER_HPP + +#include +#include + +#include +#include +#include +#include + +namespace cv +{ +namespace gapi +{ +namespace wip +{ +namespace draw +{ + +/** + * A structure to represent parameters for drawing a text string. + */ +struct Text +{ + /*@{*/ + std::string text; //!< The text string to be drawn + cv::Point org; //!< The bottom-left corner of the text string in the image + int ff; //!< The font type, see #HersheyFonts + double fs; //!< The font scale factor that is multiplied by the font-specific base size + cv::Scalar color; //!< The text color + int thick; //!< The thickness of the lines used to draw a text + int lt; //!< The line type. See #LineTypes + bool bottom_left_origin; //!< When true, the image data origin is at the bottom-left corner. Otherwise, it is at the top-left corner + /*@{*/ +}; + +/** + * A structure to represent parameters for drawing a rectangle + */ +struct Rect +{ + cv::Rect rect; //!< Coordinates of the rectangle + cv::Scalar color; //!< The rectangle color or brightness (grayscale image) + int thick; //!< The thickness of lines that make up the rectangle. Negative values, like #FILLED, mean that the function has to draw a filled rectangle + int lt; //!< The type of the line. See #LineTypes + int shift; //!< The number of fractional bits in the point coordinates +}; + +/** + * A structure to represent parameters for drawing a circle + */ +struct Circle +{ + cv::Point center; //!< The center of the circle + int radius; //!< The radius of the circle + cv::Scalar color; //!< The color of the circle + int thick; //!< The thickness of the circle outline, if positive. Negative values, like #FILLED, mean that a filled circle is to be drawn + int lt; //!< The Type of the circle boundary. See #LineTypes + int shift; //!< The Number of fractional bits in the coordinates of the center and in the radius value +}; + +/** + * A structure to represent parameters for drawing a line + */ +struct Line +{ + cv::Point pt1; //!< The first point of the line segment + cv::Point pt2; //!< The second point of the line segment + cv::Scalar color; //!< The line color + int thick; //!< The thickness of line + int lt; //!< The Type of the line. See #LineTypes + int shift; //!< The number of fractional bits in the point coordinates + +}; + +using Prim = util::variant + < Text + , Rect + , Circle + , Line + >; + +using Prims = std::vector; + +/** @brief The function renders on the input image passed drawing primitivies + +@param bgr input image: 8-bit unsigned 3-channel image @ref CV_8UC3. +@param prims vector of drawing primitivies +*/ +GAPI_EXPORTS void render(cv::Mat& bgr, const Prims& prims); + +/** @brief The function renders on two NV12 planes passed drawing primitivies + +@param y_plane input image: 8-bit unsigned 1-channel image @ref CV_8UC1. +@param uv_plane input image: 8-bit unsigned 2-channel image @ref CV_8UC2. +@param prims vector of drawing primitivies +*/ +GAPI_EXPORTS void render(cv::Mat& y_plane, cv::Mat& uv_plane , const Prims& prims); + +} // namespace draw +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_RENDER_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/any.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/any.hpp index 3146cb6..5f97e95 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/any.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/any.hpp @@ -13,7 +13,7 @@ #include #include -#include "opencv2/gapi/util/throw.hpp" +#include #if defined(_MSC_VER) // disable MSVC warning on "multiple copy constructors specified" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/optional.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/optional.hpp index 54126d6..1aa2b26 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/optional.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/optional.hpp @@ -8,7 +8,7 @@ #ifndef OPENCV_GAPI_UTIL_OPTIONAL_HPP #define OPENCV_GAPI_UTIL_OPTIONAL_HPP -#include "opencv2/gapi/util/variant.hpp" +#include // A poor man's `optional` implementation, incompletely modeled against C++17 spec. namespace cv diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/util.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/util.hpp index d0378e0..afcf559 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/util.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/util.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_UTIL_HPP #define OPENCV_GAPI_UTIL_HPP -#include // std::tuple +#include // \cond HIDDEN_SYMBOLS // This header file contains some generic utility functions which are @@ -84,6 +84,38 @@ namespace detail { static constexpr const std::size_t value = S; }; + + template + struct contains : std::false_type{}; + + template + struct contains : std::integral_constant::value || + contains::value> {}; + template + struct contains> : std::integral_constant::value> {}; + + template + struct all_unique : std::true_type{}; + + template + struct all_unique : std::integral_constant::value && + all_unique::value> {}; + + template + struct tuple_wrap_helper; + + template struct tuple_wrap_helper + { + using type = std::tuple; + static type get(T&& obj) { return std::make_tuple(std::move(obj)); } + }; + + template + struct tuple_wrap_helper> + { + using type = std::tuple; + static type get(std::tuple&& objs) { return std::forward>(objs); } + }; } // namespace detail } // namespace cv diff --git a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/variant.hpp b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/variant.hpp index a7e43c5..134ba66 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/variant.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/include/opencv2/gapi/util/variant.hpp @@ -11,8 +11,8 @@ #include #include -#include "opencv2/gapi/util/throw.hpp" -#include "opencv2/gapi/util/util.hpp" // max_of_t +#include +#include // max_of_t // A poor man's `variant` implementation, incompletely modeled against C++17 spec. namespace cv diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_core_perf_tests.hpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_core_perf_tests.hpp index 8b743f0..25b5615 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_core_perf_tests.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_core_perf_tests.hpp @@ -10,7 +10,7 @@ #include "../../test/common/gapi_tests_common.hpp" -#include "opencv2/gapi/core.hpp" +#include namespace opencv_test { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp index 154f7d3..c4e5bc6 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp @@ -11,7 +11,7 @@ #include "../../test/common/gapi_tests_common.hpp" -#include "opencv2/gapi/imgproc.hpp" +#include namespace opencv_test { @@ -43,5 +43,8 @@ class BGR2LUVPerfTest : public TestPerfParams> {}; class BGR2YUVPerfTest : public TestPerfParams> {}; class YUV2BGRPerfTest : public TestPerfParams> {}; +class RGB2HSVPerfTest : public TestPerfParams> {}; +class BayerGR2RGBPerfTest : public TestPerfParams> {}; +class RGB2YUV422PerfTest : public TestPerfParams> {}; } #endif //OPENCV_GAPI_IMGPROC_PERF_TESTS_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests_inl.hpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests_inl.hpp index e23bbd2..3fea552 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests_inl.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/common/gapi_imgproc_perf_tests_inl.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_PERF_TESTS_INL_HPP @@ -18,6 +18,41 @@ namespace opencv_test using namespace perf; + namespace + { + 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); + } + } + } //------------------------------------------------------------------------------ PERF_TEST_P_(SepFilterPerfTest, TestPerformance) @@ -33,7 +68,7 @@ PERF_TEST_P_(SepFilterPerfTest, TestPerformance) cv::Mat kernelY(kernSize, 1, CV_32F); randu(kernelX, -1, 1); randu(kernelY, -1, 1); - initMatsRandN(type, sz, dtype, false); + initMatrixRandN(type, sz, dtype, false); cv::Point anchor = cv::Point(-1, -1); @@ -75,7 +110,7 @@ PERF_TEST_P_(Filter2DPerfTest, TestPerformance) cv::GCompileArgs compile_args; std::tie(cmpF, type, kernSize, sz, borderType, dtype, compile_args) = GetParam(); - initMatsRandN(type, sz, dtype, false); + initMatrixRandN(type, sz, dtype, false); cv::Point anchor = {-1, -1}; double delta = 0; @@ -125,7 +160,7 @@ PERF_TEST_P_(BoxFilterPerfTest, TestPerformance) cv::GCompileArgs compile_args; std::tie(cmpF, type, filterSize, sz, borderType, dtype, compile_args) = GetParam(); - initMatsRandN(type, sz, dtype, false); + initMatrixRandN(type, sz, dtype, false); cv::Point anchor = {-1, -1}; bool normalize = true; @@ -169,7 +204,7 @@ PERF_TEST_P_(BlurPerfTest, TestPerformance) cv::GCompileArgs compile_args; std::tie(cmpF, type, filterSize, sz, borderType, compile_args) = GetParam(); - initMatsRandN(type, sz, type, false); + initMatrixRandN(type, sz, type, false); cv::Point anchor = {-1, -1}; @@ -215,7 +250,7 @@ PERF_TEST_P_(GaussianBlurPerfTest, TestPerformance) cv::Size kSize = cv::Size(kernSize, kernSize); auto& rng = cv::theRNG(); double sigmaX = rng(); - initMatsRandN(type, sz, type, false); + initMatrixRandN(type, sz, type, false); // OpenCV code /////////////////////////////////////////////////////////// cv::GaussianBlur(in_mat1, out_mat_ocv, kSize, sigmaX); @@ -254,7 +289,7 @@ PERF_TEST_P_(MedianBlurPerfTest, TestPerformance) cv::GCompileArgs compile_args; std::tie(cmpF, type, kernSize, sz, compile_args) = GetParam(); - initMatsRandN(type, sz, type, false); + initMatrixRandN(type, sz, type, false); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -295,7 +330,7 @@ PERF_TEST_P_(ErodePerfTest, TestPerformance) cv::GCompileArgs compile_args; std::tie(cmpF, type, kernSize, sz, kernType, compile_args) = GetParam(); - initMatsRandN(type, sz, type, false); + initMatrixRandN(type, sz, type, false); cv::Mat kernel = cv::getStructuringElement(kernType, cv::Size(kernSize, kernSize)); @@ -338,7 +373,7 @@ PERF_TEST_P_(Erode3x3PerfTest, TestPerformance) cv::GCompileArgs compile_args; std::tie(cmpF, type, sz, numIters, compile_args) = GetParam(); - initMatsRandN(type, sz, type, false); + initMatrixRandN(type, sz, type, false); cv::Mat kernel = cv::getStructuringElement(cv::MorphShapes::MORPH_RECT, cv::Size(3, 3)); @@ -381,7 +416,7 @@ PERF_TEST_P_(DilatePerfTest, TestPerformance) cv::GCompileArgs compile_args; std::tie(cmpF, type, kernSize, sz, kernType, compile_args) = GetParam(); - initMatsRandN(type, sz, type, false); + initMatrixRandN(type, sz, type, false); cv::Mat kernel = cv::getStructuringElement(kernType, cv::Size(kernSize, kernSize)); @@ -424,7 +459,7 @@ PERF_TEST_P_(Dilate3x3PerfTest, TestPerformance) cv::GCompileArgs compile_args; std::tie(cmpF, type, sz, numIters, compile_args) = GetParam(); - initMatsRandN(type, sz, type, false); + initMatrixRandN(type, sz, type, false); cv::Mat kernel = cv::getStructuringElement(cv::MorphShapes::MORPH_RECT, cv::Size(3, 3)); @@ -467,7 +502,7 @@ PERF_TEST_P_(SobelPerfTest, TestPerformance) cv::GCompileArgs compile_args; std::tie(cmpF, type, kernSize, sz, dtype, dx, dy, compile_args) = GetParam(); - initMatsRandN(type, sz, dtype, false); + initMatrixRandN(type, sz, dtype, false); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -510,7 +545,7 @@ PERF_TEST_P_(SobelXYPerfTest, TestPerformance) cv::Mat out_mat_ocv2; cv::Mat out_mat_gapi2; - initMatsRandN(type, sz, dtype, false); + initMatrixRandN(type, sz, dtype, false); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -555,7 +590,7 @@ PERF_TEST_P_(CannyPerfTest, TestPerformance) cv::GCompileArgs compile_args; std::tie(cmpF, type, sz, thrLow, thrUp, apSize, l2gr, compile_args) = GetParam(); - initMatsRandN(type, sz, CV_8UC1, false); + initMatrixRandN(type, sz, CV_8UC1, false); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -593,7 +628,7 @@ PERF_TEST_P_(EqHistPerfTest, TestPerformance) Size sz = get<1>(GetParam()); cv::GCompileArgs compile_args = get<2>(GetParam()); - initMatsRandN(CV_8UC1, sz, CV_8UC1, false); + initMatrixRandN(CV_8UC1, sz, CV_8UC1, false); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -631,7 +666,7 @@ PERF_TEST_P_(RGB2GrayPerfTest, TestPerformance) Size sz = get<1>(GetParam()); cv::GCompileArgs compile_args = get<2>(GetParam()); - initMatsRandN(CV_8UC3, sz, CV_8UC1, false); + initMatrixRandN(CV_8UC3, sz, CV_8UC1, false); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -669,7 +704,7 @@ PERF_TEST_P_(BGR2GrayPerfTest, TestPerformance) Size sz = get<1>(GetParam()); cv::GCompileArgs compile_args = get<2>(GetParam()); - initMatsRandN(CV_8UC3, sz, CV_8UC1, false); + initMatrixRandN(CV_8UC3, sz, CV_8UC1, false); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -707,7 +742,7 @@ PERF_TEST_P_(RGB2YUVPerfTest, TestPerformance) Size sz = get<1>(GetParam()); cv::GCompileArgs compile_args = get<2>(GetParam()); - initMatsRandN(CV_8UC3, sz, CV_8UC3, false); + initMatrixRandN(CV_8UC3, sz, CV_8UC3, false); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -745,7 +780,7 @@ PERF_TEST_P_(YUV2RGBPerfTest, TestPerformance) Size sz = get<1>(GetParam()); cv::GCompileArgs compile_args = get<2>(GetParam()); - initMatsRandN(CV_8UC3, sz, CV_8UC3, false); + initMatrixRandN(CV_8UC3, sz, CV_8UC3, false); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -783,7 +818,7 @@ PERF_TEST_P_(RGB2LabPerfTest, TestPerformance) Size sz = get<1>(GetParam()); cv::GCompileArgs compile_args = get<2>(GetParam()); - initMatsRandN(CV_8UC3, sz, CV_8UC3, false); + initMatrixRandN(CV_8UC3, sz, CV_8UC3, false); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -821,7 +856,7 @@ PERF_TEST_P_(BGR2LUVPerfTest, TestPerformance) Size sz = get<1>(GetParam()); cv::GCompileArgs compile_args = get<2>(GetParam()); - initMatsRandN(CV_8UC3, sz, CV_8UC3, false); + initMatrixRandN(CV_8UC3, sz, CV_8UC3, false); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -859,7 +894,7 @@ PERF_TEST_P_(LUV2BGRPerfTest, TestPerformance) Size sz = get<1>(GetParam()); cv::GCompileArgs compile_args = get<2>(GetParam()); - initMatsRandN(CV_8UC3, sz, CV_8UC3, false); + initMatrixRandN(CV_8UC3, sz, CV_8UC3, false); // OpenCV code ///////////////////////////////////////////////////////////// { @@ -897,7 +932,7 @@ PERF_TEST_P_(BGR2YUVPerfTest, TestPerformance) Size sz = get<1>(GetParam()); cv::GCompileArgs compile_args = get<2>(GetParam()); - initMatsRandN(CV_8UC3, sz, CV_8UC3, false); + initMatrixRandN(CV_8UC3, sz, CV_8UC3, false); cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_BGR2YUV); @@ -927,7 +962,7 @@ PERF_TEST_P_(YUV2BGRPerfTest, TestPerformance) Size sz = get<1>(GetParam()); cv::GCompileArgs compile_args = get<2>(GetParam()); - initMatsRandN(CV_8UC3, sz, CV_8UC3, false); + initMatrixRandN(CV_8UC3, sz, CV_8UC3, false); cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_YUV2BGR); @@ -949,6 +984,92 @@ PERF_TEST_P_(YUV2BGRPerfTest, TestPerformance) SANITY_CHECK_NOTHING(); } +PERF_TEST_P_(BayerGR2RGBPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(GetParam()); + + initMatrixRandN(CV_8UC1, sz, CV_8UC3, false); + + cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_BayerGR2RGB); + + cv::GMat in; + auto out = cv::gapi::BayerGR2RGB(in); + cv::GComputation c(in, out); + + // Warm-up graph engine: + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi); + } + + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P_(RGB2HSVPerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(GetParam()); + + initMatrixRandN(CV_8UC3, sz, CV_8UC3, false); + cv::cvtColor(in_mat1, in_mat1, cv::COLOR_BGR2RGB); + + cv::cvtColor(in_mat1, out_mat_ocv, cv::COLOR_RGB2HSV); + + cv::GMat in; + auto out = cv::gapi::RGB2HSV(in); + cv::GComputation c(in, out); + + // Warm-up graph engine: + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi); + } + + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P_(RGB2YUV422PerfTest, TestPerformance) +{ + compare_f cmpF = get<0>(GetParam()); + Size sz = get<1>(GetParam()); + cv::GCompileArgs compile_args = get<2>(GetParam()); + + initMatrixRandN(CV_8UC3, sz, CV_8UC2, false); + cv::cvtColor(in_mat1, in_mat1, cv::COLOR_BGR2RGB); + + convertRGB2YUV422Ref(in_mat1, out_mat_ocv); + + cv::GMat in; + auto out = cv::gapi::RGB2YUV422(in); + cv::GComputation c(in, out); + + // Warm-up graph engine: + c.apply(in_mat1, out_mat_gapi, std::move(compile_args)); + + TEST_CYCLE() + { + c.apply(in_mat1, out_mat_gapi); + } + + EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv)); + EXPECT_EQ(out_mat_gapi.size(), sz); + + SANITY_CHECK_NOTHING(); +} + //------------------------------------------------------------------------------ } diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_core_perf_tests_cpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_core_perf_tests_cpu.cpp index 82fabfd..4a681f9 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_core_perf_tests_cpu.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_core_perf_tests_cpu.cpp @@ -7,7 +7,7 @@ #include "../perf_precomp.hpp" #include "../common/gapi_core_perf_tests.hpp" -#include "opencv2/gapi/cpu/core.hpp" +#include #define CORE_CPU cv::gapi::core::cpu::kernels() diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_cpu.cpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_cpu.cpp index ea3d753..b2e5b3d 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_cpu.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_cpu.cpp @@ -7,8 +7,7 @@ #include "../perf_precomp.hpp" #include "../common/gapi_imgproc_perf_tests.hpp" -#include "opencv2/gapi/cpu/imgproc.hpp" - +#include #define IMGPROC_CPU cv::gapi::imgproc::cpu::kernels() @@ -185,4 +184,18 @@ INSTANTIATE_TEST_CASE_P(YUV2BGRPerfTestCPU, YUV2BGRPerfTest, Values(szVGA, sz720p, sz1080p), Values(cv::compile_args(IMGPROC_CPU)))); +INSTANTIATE_TEST_CASE_P(RGB2HSVPerfTestCPU, RGB2HSVPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(BayerGR2RGBPerfTestCPU, BayerGR2RGBPerfTest, + Combine(Values(AbsExact().to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_CPU)))); + +INSTANTIATE_TEST_CASE_P(RGB2YUV422PerfTestCPU, RGB2YUV422PerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_CPU)))); } diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_fluid.cpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_fluid.cpp index 635f2d0..6414a81 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_fluid.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_fluid.cpp @@ -173,6 +173,21 @@ INSTANTIATE_TEST_CASE_P(YUV2BGRPerfTestFluid, YUV2BGRPerfTest, Values(szVGA, sz720p, sz1080p), Values(cv::compile_args(IMGPROC_FLUID)))); +INSTANTIATE_TEST_CASE_P(BayerGR2RGBPerfTestFluid, BayerGR2RGBPerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_FLUID)))); + +INSTANTIATE_TEST_CASE_P(RGB2YUV422PerfTestFluid, RGB2YUV422PerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_FLUID)))); + +INSTANTIATE_TEST_CASE_P(RGB2HSVPerfTestFluid, RGB2HSVPerfTest, + Combine(Values(ToleranceColor(1e-3).to_compare_f()), + Values(szVGA, sz720p, sz1080p), + Values(cv::compile_args(IMGPROC_FLUID)))); + INSTANTIATE_TEST_CASE_P(BGR2LUVPerfTestFluid, BGR2LUVPerfTest, Combine(Values(AbsSimilarPoints(1, 0.05).to_compare_f()), Values(szVGA, sz720p, sz1080p), diff --git a/inference-engine/thirdparty/fluid/modules/gapi/perf/perf_precomp.hpp b/inference-engine/thirdparty/fluid/modules/gapi/perf/perf_precomp.hpp index 4f9466e..bf19532 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/perf/perf_precomp.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/perf/perf_precomp.hpp @@ -11,17 +11,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/operators.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include "opencv2/gapi/fluid/core.hpp" -#include "opencv2/gapi/fluid/imgproc.hpp" +#include +#include #endif // __OPENCV_GAPI_PERF_PRECOMP_HPP__ diff --git a/inference-engine/thirdparty/fluid/modules/gapi/samples/api_ref_snippets.cpp b/inference-engine/thirdparty/fluid/modules/gapi/samples/api_ref_snippets.cpp index 5e8859d..2793aee 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/samples/api_ref_snippets.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/samples/api_ref_snippets.cpp @@ -49,8 +49,7 @@ int main(int argc, char *argv[]) //! [apply_with_param] cv::gapi::GKernelPackage kernels = cv::gapi::combine (cv::gapi::core::fluid::kernels(), - cv::gapi::imgproc::fluid::kernels(), - cv::unite_policy::KEEP); + cv::gapi::imgproc::fluid::kernels()); sobelEdge.apply(input, output, cv::compile_args(kernels)); //! [apply_with_param] diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/garray.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/garray.cpp index 793f44a..bdd46b9 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/api/garray.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/garray.cpp @@ -6,7 +6,7 @@ #include "precomp.hpp" -#include "opencv2/gapi/garray.hpp" +#include #include "api/gorigin.hpp" // cv::detail::GArrayU public implementation /////////////////////////////////// diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gbackend.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gbackend.cpp index 3dfd2ef..43227bf 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gbackend.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gbackend.cpp @@ -8,8 +8,8 @@ #include "precomp.hpp" #include // unique_ptr -#include "opencv2/gapi/gkernel.hpp" -#include "opencv2/gapi/own/convert.hpp" +#include +#include #include "api/gbackend_priv.hpp" #include "backends/common/gbackend.hpp" @@ -45,6 +45,11 @@ void cv::gapi::GBackend::Priv::addBackendPasses(ade::ExecutionEngineSetupContext // add custom (backend-specific) graph transformations } +cv::gapi::GKernelPackage cv::gapi::GBackend::Priv::auxiliaryKernels() const +{ + return {}; +} + // GBackend public implementation ////////////////////////////////////////////// cv::gapi::GBackend::GBackend() { @@ -98,7 +103,7 @@ void bindInArg(Mag& mag, const RcDesc &rc, const GRunArg &arg, bool is_umat) auto& mag_umat = mag.template slot()[rc.id]; mag_umat = to_ocv(util::get(arg)).getUMat(ACCESS_READ); #else - util::throw_error(std::logic_error("UMat is not supported in stadnalone build")); + util::throw_error(std::logic_error("UMat is not supported in standalone build")); #endif // !defined(GAPI_STANDALONE) } else diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gbackend_priv.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gbackend_priv.hpp index 1c6e297..afc77b2 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gbackend_priv.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gbackend_priv.hpp @@ -47,6 +47,8 @@ public: virtual void addBackendPasses(ade::ExecutionEngineSetupContext &); + virtual cv::gapi::GKernelPackage auxiliaryKernels() const; + virtual ~Priv() = default; }; diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcall.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcall.cpp index 51de047..4c052ff 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcall.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcall.cpp @@ -7,7 +7,7 @@ #include "precomp.hpp" #include -#include "opencv2/gapi/gcall.hpp" +#include #include "api/gcall_priv.hpp" // GCall private implementation //////////////////////////////////////////////// diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcomputation.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcomputation.cpp index ab761ed..64a4495 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcomputation.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gcomputation.cpp @@ -12,8 +12,8 @@ #include "logger.hpp" // GAPI_LOG -#include "opencv2/gapi/gcomputation.hpp" -#include "opencv2/gapi/gkernel.hpp" +#include +#include #include "api/gcomputation_priv.hpp" #include "api/gcall_priv.hpp" @@ -159,16 +159,14 @@ void cv::GComputation::apply(cv::Mat in1, cv::Mat in2, cv::Scalar &out, GCompile } void cv::GComputation::apply(const std::vector &ins, - const std::vector &outs, + std::vector &outs, GCompileArgs &&args) { GRunArgs call_ins; GRunArgsP call_outs; - // Make a temporary copy of vector outs - cv::Mats are copies anyway - auto tmp = outs; - for (const cv::Mat &m : ins) { call_ins.emplace_back(m); } - for ( cv::Mat &m : tmp) { call_outs.emplace_back(&m); } + for (const cv::Mat &m : ins) { call_ins.emplace_back(m); } + for ( cv::Mat &m : outs) { call_outs.emplace_back(&m); } apply(std::move(call_ins), std::move(call_outs), std::move(args)); } diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gkernel.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gkernel.cpp index f8c851a..6993e95 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gkernel.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gkernel.cpp @@ -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 #include "precomp.hpp" @@ -13,135 +13,88 @@ #include #include "logger.hpp" -#include "opencv2/gapi/gkernel.hpp" +#include #include "api/gbackend_priv.hpp" // GKernelPackage public implementation //////////////////////////////////////// void cv::gapi::GKernelPackage::remove(const cv::gapi::GBackend& backend) { - m_backend_kernels.erase(backend); + std::vector id_deleted_kernels; + for (const auto& p : m_id_kernels) + { + if (p.second.first == backend) + { + id_deleted_kernels.push_back(p.first); + } + } + + for (const auto& kernel_id : id_deleted_kernels) + { + m_id_kernels.erase(kernel_id); + } } bool cv::gapi::GKernelPackage::includesAPI(const std::string &id) const { - // In current form not very efficient (n * log n) - auto it = std::find_if(m_backend_kernels.begin(), - m_backend_kernels.end(), - [&id](const M::value_type &p) { - return ade::util::contains(p.second, id); - }); - return (it != m_backend_kernels.end()); + return ade::util::contains(m_id_kernels, id); } void cv::gapi::GKernelPackage::removeAPI(const std::string &id) { - for (auto &bk : m_backend_kernels) - bk.second.erase(id); + m_id_kernels.erase(id); } std::size_t cv::gapi::GKernelPackage::size() const { - return std::accumulate(m_backend_kernels.begin(), - m_backend_kernels.end(), - static_cast(0u), - [](std::size_t acc, const M::value_type& v) { - return acc + v.second.size(); - }); + return m_id_kernels.size(); +} + +const std::vector &cv::gapi::GKernelPackage::get_transformations() const +{ + return m_transformations; } cv::gapi::GKernelPackage cv::gapi::combine(const GKernelPackage &lhs, - const GKernelPackage &rhs, - const cv::unite_policy policy) + const GKernelPackage &rhs) { - if (policy == cv::unite_policy::REPLACE) - { - // REPLACE policy: if there is a collision, prefer RHS - // to LHS + // If there is a collision, prefer RHS to LHS // since RHS package has a precedense, start with its copy GKernelPackage result(rhs); // now iterate over LHS package and put kernel if and only // if there's no such one - for (const auto &backend : lhs.m_backend_kernels) + for (const auto& kernel : lhs.m_id_kernels) { - for (const auto &kimpl : backend.second) + if (!result.includesAPI(kernel.first)) { - if (!result.includesAPI(kimpl.first)) - result.m_backend_kernels[backend.first].insert(kimpl); + result.m_id_kernels.emplace(kernel.first, kernel.second); } } - return result; - } - else if (policy == cv::unite_policy::KEEP) - { - // KEEP policy: if there is a collision, just keep two versions - // of a kernel - GKernelPackage result(lhs); - for (const auto &p : rhs.m_backend_kernels) - { - result.m_backend_kernels[p.first].insert(p.second.begin(), - p.second.end()); + for (const auto &transforms : lhs.m_transformations){ + result.m_transformations.push_back(transforms); } return result; - } - else GAPI_Assert(false); - return GKernelPackage(); } std::pair -cv::gapi::GKernelPackage::lookup(const std::string &id, - const GLookupOrder &order) const +cv::gapi::GKernelPackage::lookup(const std::string &id) const { - if (order.empty()) + auto kernel_it = m_id_kernels.find(id); + if (kernel_it != m_id_kernels.end()) { - // If order is empty, return what comes first - auto it = std::find_if(m_backend_kernels.begin(), - m_backend_kernels.end(), - [&id](const M::value_type &p) { - return ade::util::contains(p.second, id); - }); - if (it != m_backend_kernels.end()) - { - // FIXME: Two lookups! - return std::make_pair(it->first, it->second.find(id)->second); - } - } - else - { - // There is order, so: - // 1. Limit search scope only to specified backends - // FIXME: Currently it is not configurable if search can fall-back - // to other backends (not listed in order) if kernel hasn't been found - // in the look-up list - // 2. Query backends in the specified order - for (const auto &selected_backend : order) - { - const auto kernels_it = m_backend_kernels.find(selected_backend); - if (kernels_it == m_backend_kernels.end()) - { - GAPI_LOG_WARNING(NULL, - "Backend " - << &selected_backend.priv() // FIXME: name instead - << " was listed in lookup list but was not found " - "in the package"); - continue; - } - if (ade::util::contains(kernels_it->second, id)) - { - // FIXME: two lookups! - return std::make_pair(selected_backend, kernels_it->second.find(id)->second); - } - } + return kernel_it->second; } - - // If reached here, kernel was not found among selected backends. + // If reached here, kernel was not found. util::throw_error(std::logic_error("Kernel " + id + " was not found")); } std::vector cv::gapi::GKernelPackage::backends() const { - std::vector result; - for (const auto &p : m_backend_kernels) result.emplace_back(p.first); - return result; + using kernel_type = std::pair>; + std::unordered_set unique_set; + ade::util::transform(m_id_kernels, std::inserter(unique_set, unique_set.end()), + [](const kernel_type& k) { return k.second.first; }); + + return std::vector(unique_set.begin(), unique_set.end()); } diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gmat.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gmat.cpp index 1466a26..5bc55ef 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gmat.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gmat.cpp @@ -8,8 +8,8 @@ #include "precomp.hpp" #include #include //gapi::own::Mat +#include -#include "opencv2/gapi/gmat.hpp" #include "api/gorigin.hpp" // cv::GMat public implementation ////////////////////////////////////////////// diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gorigin.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gorigin.hpp index ad4ebf5..7129b2f 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gorigin.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gorigin.hpp @@ -11,11 +11,11 @@ #include // set #include // map -#include "opencv2/gapi/util/variant.hpp" // variant -#include "opencv2/gapi/gcommon.hpp" -#include "opencv2/gapi/opencv_includes.hpp" -#include "compiler/gobjref.hpp" +#include // variant +#include +#include +#include "compiler/gobjref.hpp" #include "api/gnode.hpp" namespace cv diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gproto.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gproto.cpp index af5ba56..1106af9 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gproto.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gproto.cpp @@ -8,9 +8,9 @@ #include "precomp.hpp" #include -#include "opencv2/gapi/util/throw.hpp" -#include "opencv2/gapi/garg.hpp" -#include "opencv2/gapi/gproto.hpp" +#include +#include +#include #include "api/gorigin.hpp" #include "api/gproto_priv.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gscalar.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gscalar.cpp index 2e42ae6..fa7c0cd 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/api/gscalar.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/gscalar.cpp @@ -7,8 +7,8 @@ #include "precomp.hpp" -#include "opencv2/gapi/gscalar.hpp" -#include "opencv2/gapi/own/convert.hpp" +#include +#include #include "api/gorigin.hpp" // cv::GScalar public implementation /////////////////////////////////////////// diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/kernels_core.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/kernels_core.cpp index e46968b..b7bfc6b 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/api/kernels_core.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/kernels_core.cpp @@ -7,10 +7,10 @@ #include "precomp.hpp" -#include "opencv2/gapi/gcall.hpp" -#include "opencv2/gapi/gscalar.hpp" -#include "opencv2/gapi/gkernel.hpp" -#include "opencv2/gapi/core.hpp" +#include +#include +#include +#include #include #include @@ -301,6 +301,11 @@ GMat resize(const GMat& src, const Size& dsize, double fx, double fy, int interp return core::GResize::on(src, dsize, fx, fy, interpolation); } +GMatP resizeP(const GMatP& src, const Size& dsize, int interpolation) +{ + return core::GResizeP::on(src, dsize, interpolation); +} + GMat remap(const GMat& src, const Mat& map1, const Mat& map2, int interpolation, int borderMode, const Scalar& borderValue) diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/kernels_imgproc.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/kernels_imgproc.cpp index b24af8c..05d2cc4 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/api/kernels_imgproc.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/kernels_imgproc.cpp @@ -7,10 +7,10 @@ #include "precomp.hpp" -#include "opencv2/gapi/gscalar.hpp" -#include "opencv2/gapi/gcall.hpp" -#include "opencv2/gapi/gkernel.hpp" -#include "opencv2/gapi/imgproc.hpp" +#include +#include +#include +#include namespace cv { namespace gapi { @@ -157,5 +157,27 @@ GMat RGB2Lab(const GMat& src) return imgproc::GRGB2Lab::on(src); } +GMat BayerGR2RGB(const GMat& src_gr) { + return imgproc::GBayerGR2RGB::on(src_gr); +} + +GMat RGB2HSV(const GMat& src) { + return imgproc::GRGB2HSV::on(src); +} + +GMat RGB2YUV422(const GMat& src) { + return imgproc::GRGB2YUV422::on(src); +} + +GMatP NV12toRGBp(const GMat &y, const GMat &uv) +{ + return imgproc::GNV12toRGBp::on(y, uv); +} + +GMatP NV12toBGRp(const GMat &y, const GMat &uv) +{ + return imgproc::GNV12toBGRp::on(y, uv); +} + } //namespace gapi } //namespace cv diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/operators.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/operators.cpp index 44fc4fa..6097c01 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/api/operators.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/operators.cpp @@ -7,10 +7,10 @@ #include "precomp.hpp" -#include "opencv2/gapi/imgproc.hpp" -#include "opencv2/gapi/core.hpp" -#include "opencv2/gapi/gscalar.hpp" -#include "opencv2/gapi/operators.hpp" +#include +#include +#include +#include cv::GMat operator+(const cv::GMat& lhs, const cv::GMat& rhs) { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/render.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/render.cpp new file mode 100644 index 0000000..b087c40 --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/render.cpp @@ -0,0 +1,91 @@ +#include + +#include "opencv2/gapi/render.hpp" +#include "opencv2/gapi/own/assert.hpp" + +#include "api/render_priv.hpp" + +using namespace cv::gapi::wip::draw; +// FXIME util::visitor ? +void cv::gapi::wip::draw::render(cv::Mat& bgr, const Prims& prims) +{ + for (const auto& p : prims) + { + switch (p.index()) + { + case Prim::index_of(): + { + const auto& t_p = cv::util::get(p); + cv::rectangle(bgr, t_p.rect, t_p.color , t_p.thick, t_p.lt, t_p.shift); + break; + } + + case Prim::index_of(): + { + const auto& t_p = cv::util::get(p); + cv::putText(bgr, t_p.text, t_p.org, t_p.ff, t_p.fs, + t_p.color, t_p.thick, t_p.lt, t_p.bottom_left_origin); + break; + } + + case Prim::index_of(): + { + const auto& c_p = cv::util::get(p); + cv::circle(bgr, c_p.center, c_p.radius, c_p.color, c_p.thick, c_p.lt, c_p.shift); + break; + } + + case Prim::index_of(): + { + const auto& l_p = cv::util::get(p); + cv::line(bgr, l_p.pt1, l_p.pt2, l_p.color, l_p.thick, l_p.lt, l_p.shift); + break; + } + + default: util::throw_error(std::logic_error("Unsupported draw operation")); + } + } +} + +void cv::gapi::wip::draw::render(cv::Mat& y_plane, cv::Mat& uv_plane , const Prims& prims) +{ + cv::Mat bgr; + cv::cvtColorTwoPlane(y_plane, uv_plane, bgr, cv::COLOR_YUV2BGR_NV12); + render(bgr, prims); + BGR2NV12(bgr, y_plane, uv_plane); +} + +void cv::gapi::wip::draw::splitNV12TwoPlane(const cv::Mat& yuv, cv::Mat& y_plane, cv::Mat& uv_plane) { + y_plane.create(yuv.size(), CV_8UC1); + uv_plane.create(yuv.size() / 2, CV_8UC2); + + // Fill Y plane + for (int i = 0; i < yuv.rows; ++i) + { + const uchar* in = yuv.ptr(i); + uchar* out = y_plane.ptr(i); + for (int j = 0; j < yuv.cols; j++) { + out[j] = in[3 * j]; + } + } + + // Fill UV plane + for (int i = 0; i < uv_plane.rows; i++) + { + const uchar* in = yuv.ptr(2 * i); + uchar* out = uv_plane.ptr(i); + for (int j = 0; j < uv_plane.cols; j++) { + out[j * 2 ] = in[6 * j + 1]; + out[j * 2 + 1] = in[6 * j + 2]; + } + } +} + +void cv::gapi::wip::draw::BGR2NV12(const cv::Mat& bgr, cv::Mat& y_plane, cv::Mat& uv_plane) +{ + GAPI_Assert(bgr.size().width % 2 == 0); + GAPI_Assert(bgr.size().height % 2 == 0); + + cvtColor(bgr, bgr, cv::COLOR_BGR2YUV); + splitNV12TwoPlane(bgr, y_plane, uv_plane); +} diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/api/render_priv.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/api/render_priv.hpp new file mode 100644 index 0000000..29805ea --- /dev/null +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/api/render_priv.hpp @@ -0,0 +1,30 @@ +// 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_RENDER_PRIV_HPP +#define OPENCV_RENDER_PRIV_HPP + +#include + +namespace cv +{ +namespace gapi +{ +namespace wip +{ +namespace draw +{ +// FIXME only for tests +GAPI_EXPORTS void BGR2NV12(const cv::Mat& bgr, cv::Mat& y_plane, cv::Mat& uv_plane); +void splitNV12TwoPlane(const cv::Mat& yuv, cv::Mat& y_plane, cv::Mat& uv_plane); + +} // namespace draw +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_RENDER_PRIV_HPP diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gcompoundbackend.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gcompoundbackend.cpp index 948898f..894c49e 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gcompoundbackend.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gcompoundbackend.cpp @@ -7,7 +7,7 @@ #include "precomp.hpp" -#include "opencv2/gapi/gcompoundkernel.hpp" // compound::backend() +#include // compound::backend() #include "api/gbackend_priv.hpp" #include "compiler/gislandmodel.hpp" // GIslandExecutable diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gcompoundkernel.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gcompoundkernel.cpp index 89abcef..05ed51d 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gcompoundkernel.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/common/gcompoundkernel.cpp @@ -8,7 +8,7 @@ #include "precomp.hpp" #include // util::indexed -#include "opencv2/gapi/gcompoundkernel.hpp" +#include #include "compiler/gobjref.hpp" // FIXME move to backends diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpubackend.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpubackend.cpp index 8924e3d..cfdb7ae 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpubackend.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpubackend.cpp @@ -18,16 +18,16 @@ #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" #include "backends/cpu/gcpubackend.hpp" -#include "backends/cpu/gcpuimgproc.hpp" -#include "backends/cpu/gcpucore.hpp" +#include +#include #include "api/gbackend_priv.hpp" // FIXME: Make it part of Backend SDK! diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpubackend.hpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpubackend.hpp index 218cac1..923a05e 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpubackend.hpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpubackend.hpp @@ -13,9 +13,9 @@ #include // tuple #include // type_list_index -#include "opencv2/gapi/garg.hpp" -#include "opencv2/gapi/gproto.hpp" -#include "opencv2/gapi/cpu/gcpukernel.hpp" +#include +#include +#include #include "api/gorigin.hpp" #include "backends/common/gbackend.hpp" diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpucore.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpucore.cpp index cec9abc..1c1ac58 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpucore.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpucore.cpp @@ -7,9 +7,9 @@ #include "precomp.hpp" -#include "opencv2/gapi/core.hpp" -#include "opencv2/gapi/cpu/core.hpp" -#include "backends/cpu/gcpucore.hpp" +#include +#include +#include GAPI_OCV_KERNEL(GCPUAdd, cv::gapi::core::GAdd) { @@ -461,6 +461,22 @@ GAPI_OCV_KERNEL(GCPUResize, cv::gapi::core::GResize) } }; +GAPI_OCV_KERNEL(GCPUResizeP, cv::gapi::core::GResizeP) +{ + static void run(const cv::Mat& in, cv::Size out_sz, int interp, cv::Mat& out) + { + int inH = in.rows / 3; + int inW = in.cols; + int outH = out.rows / 3; + int outW = out.cols; + for (int i = 0; i < 3; i++) { + auto in_plane = in(cv::Rect(0, i*inH, inW, inH)); + auto out_plane = out(cv::Rect(0, i*outH, outW, outH)); + cv::resize(in_plane, out_plane, out_sz, 0, 0, interp); + } + } +}; + GAPI_OCV_KERNEL(GCPURemap, cv::gapi::core::GRemap) { static void run(const cv::Mat& in, const cv::Mat& x, const cv::Mat& y, int a, int b, cv::Scalar s, cv::Mat& out) @@ -589,6 +605,7 @@ cv::gapi::GKernelPackage cv::gapi::core::cpu::kernels() , GCPUSplit3 , GCPUSplit4 , GCPUResize + , GCPUResizeP , GCPUMerge3 , GCPUMerge4 , GCPURemap diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpuimgproc.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpuimgproc.cpp index ab5d5d8..cab0520 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpuimgproc.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpuimgproc.cpp @@ -7,9 +7,11 @@ #include "precomp.hpp" -#include "opencv2/gapi/imgproc.hpp" -#include "opencv2/gapi/cpu/imgproc.hpp" -#include "backends/cpu/gcpuimgproc.hpp" +#include +#include +#include + +#include "backends/fluid/gfluidimgproc_func.hpp" namespace { cv::Mat add_border(const cv::Mat& in, const int ksize, const int borderType, const cv::Scalar& bordVal){ @@ -276,6 +278,74 @@ GAPI_OCV_KERNEL(GCPURGB2GrayCustom, cv::gapi::imgproc::GRGB2GrayCustom) } }; +GAPI_OCV_KERNEL(GCPUBayerGR2RGB, cv::gapi::imgproc::GBayerGR2RGB) +{ + static void run(const cv::Mat& in, cv::Mat &out) + { + cv::cvtColor(in, out, cv::COLOR_BayerGR2RGB); + } +}; + +GAPI_OCV_KERNEL(GCPURGB2HSV, cv::gapi::imgproc::GRGB2HSV) +{ + static void run(const cv::Mat& in, cv::Mat &out) + { + cv::cvtColor(in, out, cv::COLOR_RGB2HSV); + } +}; + +GAPI_OCV_KERNEL(GCPURGB2YUV422, cv::gapi::imgproc::GRGB2YUV422) +{ + static void run(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); + cv::gapi::fluid::run_rgb2yuv422_impl(out_line_p, in_line_p, in.cols); + } + } +}; + +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); +} + + +GAPI_OCV_KERNEL(GCPUNV12toRGBp, cv::gapi::imgproc::GNV12toRGBp) +{ + static void run(const cv::Mat& inY, const cv::Mat& inUV, cv::Mat& out) + { + cv::Mat rgb; + cv::cvtColorTwoPlane(inY, inUV, rgb, cv::COLOR_YUV2RGB_NV12); + toPlanar(rgb, out); + } +}; + +GAPI_OCV_KERNEL(GCPUNV12toBGRp, cv::gapi::imgproc::GNV12toBGRp) +{ + static void run(const cv::Mat& inY, const cv::Mat& inUV, cv::Mat& out) + { + cv::Mat rgb; + cv::cvtColorTwoPlane(inY, inUV, rgb, cv::COLOR_YUV2BGR_NV12); + toPlanar(rgb, out); + } +}; + + cv::gapi::GKernelPackage cv::gapi::imgproc::cpu::kernels() { static auto pkg = cv::gapi::kernels @@ -303,6 +373,11 @@ cv::gapi::GKernelPackage cv::gapi::imgproc::cpu::kernels() , GCPUBGR2Gray , GCPURGB2Gray , GCPURGB2GrayCustom + , GCPUBayerGR2RGB + , GCPURGB2HSV + , GCPURGB2YUV422 + , GCPUNV12toRGBp + , GCPUNV12toBGRp >(); return pkg; } diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpukernel.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpukernel.cpp index af13eed..5bc77aa 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpukernel.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/cpu/gcpukernel.cpp @@ -9,7 +9,7 @@ #include -#include "opencv2/gapi/cpu/gcpukernel.hpp" +#include const cv::gapi::own::Mat& cv::GCPUContext::inMat(int input) { diff --git a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbackend.cpp b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbackend.cpp index 6d78edb..1321599 100644 --- a/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbackend.cpp +++ b/inference-engine/thirdparty/fluid/modules/gapi/src/backends/fluid/gfluidbackend.cpp @@ -22,11 +22,11 @@ #include #include -#include "opencv2/gapi/gcommon.hpp" +#include #include "logger.hpp" -#include "opencv2/gapi/own/convert.hpp" -#include "opencv2/gapi/gmat.hpp" //for version of descr_of +#include +#include //for version of descr_of // PRIVATE STUFF! #include "compiler/gobjref.hpp" #include "compiler/gmodel.hpp" @@ -91,7 +91,21 @@ namespace cv::util::throw_error(std::logic_error("GFluidOutputRois feature supports only one-island graphs")); auto rois = out_rois.value_or(cv::GFluidOutputRois()); - return EPtr{new cv::gimpl::GFluidExecutable(graph, nodes, std::move(rois.rois))}; + + auto graph_data = fluidExtractInputDataFromGraph(graph, nodes); + const auto parallel_out_rois = cv::gimpl::getCompileArg(args); + const auto gpfor = cv::gimpl::getCompileArg(args); + + auto serial_for = [](std::size_t count, std::function f){ + for (std::size_t i = 0; i < count; ++i){ + f(i); + } + }; + auto pfor = gpfor.has_value() ? gpfor.value().parallel_for : serial_for; + return parallel_out_rois.has_value() ? + EPtr{new cv::gimpl::GParallelFluidExecutable (graph, graph_data, std::move(parallel_out_rois.value().parallel_rois), pfor)} + : EPtr{new cv::gimpl::GFluidExecutable (graph, graph_data, std::move(rois.rois))} + ; } virtual void addBackendPasses(ade::ExecutionEngineSetupContext &ectx) override; @@ -700,27 +714,31 @@ void cv::gimpl::GFluidExecutable::initBufferRois(std::vector& readStarts, } // while (!nodesToVisit.empty()) } -cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g, - const std::vector &nodes, - const std::vector &outputRois) - : m_g(g), m_gm(m_g) +cv::gimpl::FluidGraphInputData cv::gimpl::fluidExtractInputDataFromGraph(const ade::Graph &g, const std::vector &nodes) { - GConstFluidModel fg(m_g); + decltype(FluidGraphInputData::m_agents_data) agents_data; + decltype(FluidGraphInputData::m_scratch_users) scratch_users; + decltype(FluidGraphInputData::m_id_map) id_map; + decltype(FluidGraphInputData::m_all_gmat_ids) all_gmat_ids; + std::size_t mat_count = 0; + + GConstFluidModel fg(g); + GModel::ConstGraph m_gm(g); // Initialize vector of data buffers, build list of operations // FIXME: There _must_ be a better way to [query] count number of DATA nodes - std::size_t mat_count = 0; - std::size_t last_agent = 0; auto grab_mat_nh = [&](ade::NodeHandle nh) { auto rc = m_gm.metadata(nh).get().rc; - if (m_id_map.count(rc) == 0) + if (id_map.count(rc) == 0) { - m_all_gmat_ids[mat_count] = nh; - m_id_map[rc] = mat_count++; + all_gmat_ids[mat_count] = nh; + id_map[rc] = mat_count++; } }; + std::size_t last_agent = 0; + for (const auto &nh : nodes) { switch (m_gm.metadata(nh).get().t) @@ -733,15 +751,10 @@ cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g, case NodeType::OP: { const auto& fu = fg.metadata(nh).get(); - switch (fu.k.m_kind) - { - case GFluidKernel::Kind::Filter: m_agents.emplace_back(new FluidFilterAgent(m_g, nh)); break; - case GFluidKernel::Kind::Resize: m_agents.emplace_back(new FluidResizeAgent(m_g, nh)); break; - case GFluidKernel::Kind::NV12toRGB: m_agents.emplace_back(new FluidNV12toRGBAgent(m_g, nh)); break; - default: GAPI_Assert(false); - } + + agents_data.push_back({fu.k.m_kind, nh, {}, {}}); // NB.: in_buffer_ids size is equal to Arguments size, not Edges size!!! - m_agents.back()->in_buffer_ids.resize(m_gm.metadata(nh).get().args.size(), -1); + agents_data.back().in_buffer_ids.resize(m_gm.metadata(nh).get().args.size(), -1); for (auto eh : nh->inEdges()) { // FIXME Only GMats are currently supported (which can be represented @@ -751,23 +764,23 @@ cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g, const auto in_port = m_gm.metadata(eh).get().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 @@ -1208,6 +1265,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); for (auto& it : output_objs) bindOutArg(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 d540999..7923f0c 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 @@ private: 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,17 +143,42 @@ 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; }; }} // cv::gimpl 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 0bfdd66..f0dc6ed 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 b58ec07..49d6d2e 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 49f1824..dfbce1e 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 835fb82..3ea4676 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 191ac08..b89ccd8 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 397d3b0..b5c5147 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 @@ -249,6 +270,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 6bfa74e..ab2c420 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 b57c662..52cf6d2 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 f55f7bb..9741100 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 fb6b78a..1ed9c06 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 6e99d00..5795f44 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 7bb18f0..864f5fe 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 d01aae8..11ca51b 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 00de699..b277257 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 83d4da1..9925c19 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 b369c14..3848434 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 03b42ff..d25db58 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 8a3cfde..53464cb 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 2e98fa1..98ab208 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 2d06000..abe1c79 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 89020d1..1eefe72 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 8b20d60..73743a4 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 60bf36a..12267a3 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 0feb7b1..f5f0098 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 528d84c..1577a86 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 14f6acd..4daddab 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 eba0951..b92dbdc 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 @@ public: }}; } } + 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 2594cde..aacc4d1 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 eebe9d8..6106cd9 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 d1d8793..a023759 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 6b5babc..5644c19 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 bf0ac98..5b22ef9 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 14aac47..bb75f82 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 08a317a..396f50c 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 9f53d36..6cf4e5e 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 7ec702a..44bcc9b 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 0000000..8e845e6 --- /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 0000000..84df8c1 --- /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 0000000..aded1ad --- /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 937587c..3d3141f 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: return true; } } -private: + friend std::ostream& operator<<(std::ostream& os, const AbsExact&) + { + return os << "AbsExact()"; + } }; class AbsTolerance : public Wrappable @@ -181,6 +343,10 @@ public: 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 @@ public: } } } + 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 @@ public: 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 @@ public: } 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 @@ public: } 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 @@ public: 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 0000000..db1083d --- /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 5414263..665d525 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 ccf8646..be158d0 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 58b8bab..77622b8 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 f053565..99c36c4 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 435c798..1481755 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 b3e54bb..45c8e18 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 0000000..334a9e5 --- /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 ebf7a7d..9702119 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 62069d8..4f0ac18 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 d0c551a..fa79230 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 0000000..2275dba --- /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 cbe3237..2798b85 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 131f96a..b919d99 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 fcc8d9b..7c4904c 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 567dddd..dfb8822 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 070cea6..0e38e05 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 7cb6f9f..6c4e10a 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 aeb4762..7a33b0d 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 cd876ef..9163281 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 815aa0d..0bfb4f6 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 0000000..c18e930 --- /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 574c0ab..e080f54 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 5ee3f65..0f67688 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 92e23e8..e745bbe 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 73b1c78..a939d32 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 f5de114..e166b87 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 833ea17..c433025 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 1164165..e71985f 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 d843190..b40bb1d 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 a6453c6..42245a5 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 0ee626c..09fec67 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 9fb0e4d..a2f8f3f 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 1c6c9cc..9d3e9c9 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 7b6cdb1..7dde9fc 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 328afe7..bdeea94 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 fc8eec5..3d69bd3 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 03d5840..069981e 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 07a4b04..41dcb27 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 4c6656b..fe6fdc0 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 e91a627..1568c6b 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 4ac2f87..7db5ddc 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 830adcc..02ba03e 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 189bd11..04bca4d 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 4e95474..ec99c62 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 7c15e3b..6821e5a 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 0000000..06d6326 --- /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 0000000..7cde510 --- /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 a28791d..658081f 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 0000000..2cd1380 --- /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 e3f4961..c15145a 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 e2af8dd..e8f8a53 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 9ce2273..4a54b1d 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 7e770c8..95dcdfe 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 91fe89f..7deef99 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 470b11d..5da79ad 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 f996cac..42f99a9 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 252755e..9927ee4 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 df01d11..0b2bf15 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 77acc3d..1de0b16 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 0000000..52fb6e3 --- /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 419136a..635bba3 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 a6abf21..1f0b3e5 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 4832466..1dce173 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 0000000..6752d05 --- /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 49fc39a..0000000 --- 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 aa4622b..0000000 --- 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 8d61aae..9fb2e99 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 5dc90c7..0b0ca75 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 07c8990..7b8838c 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 431eec9..f83a12b 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 e2c8de8..46e46c1 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 a91a0c8..a7c4380 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 af6f9ce..94638c8 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 befa347..56bf3aa 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 4a9ba73..6b3168f 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 @@ protected: _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 @@ protected: * @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 @@ protected: }; 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 @@ protected: /** * @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 176373d..8731186 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 @@ public: * @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 @@ public: /** * @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 @@ public: /** * @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 @@ public: } 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 @@ public: */ 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 b3be2ff..e08953b 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 @@ public: 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 @@ protected: available_myriadX = getAmountOfMyriadXDevices(); available_myriad2 = getAmountOfMyriad2Devices(); - ASSERT_TRUE(available_myriadX > 0); - ASSERT_TRUE(available_myriad2 > 0); devicePlatform = GetParam(); } }; @@ -211,6 +210,9 @@ protected: * @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 4ecb5da..0a85158 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 51b0d0e..d9a632a 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 4cefaf6..8008b02 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 e8d3485..87db12c 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 @@ public: 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 @@ public: 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 @@ public: 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 026f409..f4c4994 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 a930d12..673f806 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 46cb6cb..e476f67 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 fddfa28..3d80df3 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 3d4fc2b..6ce9721 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 @@ See the License for the specific language governing permissions and 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 5313d71..7e024e1 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 @@ See the License for the specific language governing permissions and 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 bf11be2..af4c2e3 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 0e5280f..0000000 --- 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 0000000..8c91b98 --- /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 0000000..cf92b5b --- /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 9b6a061..7042cb2 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 219bc0b..06193db 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 ca082f5..a1c9168 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 0b5ca96..31c7ebc 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 8f770c5..85f91a8 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 @@ public: 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 40c21b4..8a0cbda 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 4f4f48e..7390c21 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 a3b3333..738b5a8 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 7d5fe0c..1ffccb9 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 94830ba..e093029 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 f447330..631c369 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 0000000..34345fd --- /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 0000000..5130c87 --- /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 a7d818f..f5e57fb 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 592a4e9..8403611 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 8bfe217..9a111d9 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 0000000..65488fc --- /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 0000000..911acaa --- /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 2f2459f..27ca948 100644 --- a/model-optimizer/extensions/back/FuseReshapesSequence.py +++ b/model-optimizer/extensions/back/FuseReshapesSequence.py @@ -46,6 +46,11 @@ class FuseReshapesSequence(BackReplacementPattern): 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 0000000..e896bcd --- /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 9646ade..2ed9022 100644 --- a/model-optimizer/extensions/back/ReduceToPooling.py +++ b/model-optimizer/extensions/back/ReduceToPooling.py @@ -78,6 +78,8 @@ class ReduceReplacer(BackReplacementPattern): 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 @@ class ReduceReplacer(BackReplacementPattern): # 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 @@ class ReduceReplacer(BackReplacementPattern): 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 @@ class ReduceReplacer(BackReplacementPattern): ), 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 8861dab..9d7eec9 100644 --- a/model-optimizer/extensions/back/ReduceToPooling_test.py +++ b/model-optimizer/extensions/back/ReduceToPooling_test.py @@ -98,14 +98,14 @@ class ReduceReplacerTest(unittest.TestCase): ('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 @@ class ReduceReplacerTest(unittest.TestCase): ], {'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 @@ class ReduceReplacerTest(unittest.TestCase): ], {'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 @@ class ReduceReplacerTest(unittest.TestCase): ], {'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 @@ class ReduceReplacerTest(unittest.TestCase): ], {'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 @@ class ReduceReplacerTest(unittest.TestCase): ], {'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 e0a0856..add5033 100644 --- a/model-optimizer/extensions/back/ScalarConstNormalize.py +++ b/model-optimizer/extensions/back/ScalarConstNormalize.py @@ -27,6 +27,9 @@ from mo.ops.reshape import Reshape # 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 @@ class ScalarNormalizeForSpecificOps(BackReplacementPattern): '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 @@ class ScalarNormalizeForSpecificOps(BackReplacementPattern): 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 1792ff8..f871d1e 100644 --- a/model-optimizer/extensions/front/mxnet/conv_ext.py +++ b/model-optimizer/extensions/front/mxnet/conv_ext.py @@ -124,6 +124,10 @@ class DeconvFrontExtractor(FrontExtractorOp): '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 2a75fce..35eb0fe 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 @@ class TestDeconvShapesParsing(unittest.TestCase): 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 3a68da7..03475c6 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 @@ class BroadcastAddFrontExtractor(FrontExtractorOp): 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 @@ class ElemwiseSubFrontExtractor(FrontExtractorOp): 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 @@ class GreaterScalarFrontExtractor(FrontExtractorOp): 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 983845c..6f44354 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 @@ class GreaterScalarFrontReplacer(FrontReplacementOp): 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 0000000..ae86c2d --- /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 0000000..62ced0a --- /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 0000000..81e607e --- /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 d33e4a5..09fad5a 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 @@ class NegFrontExtractor(FrontExtractorOp): 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 0000000..e0db7aa --- /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 0000000..5ce07de --- /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 3d35f20..21744b4 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 0000000..bf5bbe9 --- /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 93affa0..8d67c1b 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 @@ class SliceFrontExtractor(FrontExtractorOp): '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 0000000..20688b7 --- /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 739f9e0..0ad8b57 100644 --- a/model-optimizer/extensions/front/reduce_axis_normalizer.py +++ b/model-optimizer/extensions/front/reduce_axis_normalizer.py @@ -45,6 +45,8 @@ class ReduceAxisNormalizer(FrontReplacementSubgraph): 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 0000000..4a96c57 --- /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 0000000..b764eac --- /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 c225868..b8916e9 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 @@ class ObjectDetectionAPISSDPostprocessorReplacement(FrontReplacementFromConfigFi {'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 e9f42ce..04a618d 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 @@ class AddExtractor(FrontExtractorOp): 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 0000000..c0038c2 --- /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 0000000..eb27db6 --- /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 0000000..bd52635 --- /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 0000000..0e56287 --- /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 0000000..8353cd9 --- /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 ba9f2ed..19a115c 100644 --- a/model-optimizer/extensions/middle/Cast.py +++ b/model-optimizer/extensions/middle/Cast.py @@ -32,7 +32,7 @@ class CastToFloatMark(MiddleReplacementPattern): 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 @@ class CastToFloatMark(MiddleReplacementPattern): 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 520621d..23ce86e 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 @@ class Eltwise1DInputReshape(MiddleReplacementPattern): 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 0000000..ece6e98 --- /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 0000000..2b0e972 --- /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 1562360..a182c13 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 @@ class RemoveMemoryDuplicationPattern(MiddleReplacementPattern): @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 bbef79f..db31f6a 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 @@ class RemoveMemoryDuplicationPatternTests(unittest.TestCase): '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 @@ class RemoveMemoryDuplicationPatternTests(unittest.TestCase): '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 @@ class RemoveMemoryDuplicationPatternTests(unittest.TestCase): 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 @@ class RemoveMemoryDuplicationPatternTests(unittest.TestCase): '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 5bee3c3..4333046 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 @@ class RemoveUselessCropsPattern(MiddleReplacementPattern): @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 @@ class RemoveUselessCropsPattern(MiddleReplacementPattern): 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 976ad13..c3f9e0d 100644 --- a/model-optimizer/extensions/middle/RemoveUselessCrops_test.py +++ b/model-optimizer/extensions/middle/RemoveUselessCrops_test.py @@ -54,10 +54,28 @@ class RemoveUselessCropsPatternTests(unittest.TestCase): 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 @@ class RemoveUselessCropsPatternTests(unittest.TestCase): ) (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 043a2e6..dfddc33 100644 --- a/model-optimizer/extensions/middle/ReplaceMemoryOffsetWithSplice.py +++ b/model-optimizer/extensions/middle/ReplaceMemoryOffsetWithSplice.py @@ -46,53 +46,55 @@ class ReplaceMemoryOffsetNodePattern(MiddleReplacementPattern): 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 @@ class ReplaceMemoryOffsetWithMemoryNodePattern(MiddleReplacementPattern): 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 @@ class ReplaceMemoryOffsetWithMemoryNodePattern(MiddleReplacementPattern): 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 57d34b6..9d3c406 100644 --- a/model-optimizer/extensions/middle/ReplaceMemoryOffsetWithSplice_test.py +++ b/model-optimizer/extensions/middle/ReplaceMemoryOffsetWithSplice_test.py @@ -30,8 +30,10 @@ class ReplaceMemoryOffsetNodePatternTests(unittest.TestCase): '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 @@ class ReplaceMemoryOffsetNodePatternTests(unittest.TestCase): 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 @@ class ReplaceMemoryOffsetNodePatternTests(unittest.TestCase): ('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 @@ class ReplaceMemoryOffsetNodePatternTests(unittest.TestCase): 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 @@ class ReplaceMemoryOffsetNodePatternTests(unittest.TestCase): 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 0000000..065f085 --- /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 0000000..c5d1a45 --- /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 935174a..39dbe4a 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 @@ class ReplaceSpliceNodePattern(MiddleReplacementPattern): @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 @@ class ReplaceSpliceNodePattern(MiddleReplacementPattern): '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 ca40336..4689784 100644 --- a/model-optimizer/extensions/middle/ReplaceSpliceNodePattern_test.py +++ b/model-optimizer/extensions/middle/ReplaceSpliceNodePattern_test.py @@ -17,43 +17,129 @@ import unittest 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 7d660f7..112995f 100644 --- a/model-optimizer/extensions/middle/SliceConvert_test.py +++ b/model-optimizer/extensions/middle/SliceConvert_test.py @@ -18,6 +18,7 @@ import unittest 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 @@ class ConvertSliceTests(unittest.TestCase): (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 241afb0..5eba145 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 @@ class ConvertSlice(MiddleReplacementPattern): 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 @@ class ConvertSlice(MiddleReplacementPattern): 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 @@ class ConvertSlice(MiddleReplacementPattern): 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 @@ class ConvertSlice(MiddleReplacementPattern): 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 7722753..98bbe2d 100644 --- a/model-optimizer/extensions/middle/UpsampleToResample.py +++ b/model-optimizer/extensions/middle/UpsampleToResample.py @@ -22,6 +22,7 @@ import numpy as np 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 @@ from mo.ops.strided_slice import StridedSlice 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 @@ class UpsampleToResample(MiddleReplacementPattern): 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 @@ class UpsampleToResample(MiddleReplacementPattern): 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 @@ class UpsampleToResample(MiddleReplacementPattern): 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 5176994..2c31644 100644 --- a/model-optimizer/extensions/ops/Cast.py +++ b/model-optimizer/extensions/ops/Cast.py @@ -18,6 +18,7 @@ import numpy as np 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 5faadf4..457e7b3 100644 --- a/model-optimizer/extensions/ops/ReduceOps.py +++ b/model-optimizer/extensions/ops/ReduceOps.py @@ -25,26 +25,28 @@ reduce_map = { '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 4646b76..6da7eec 100644 --- a/model-optimizer/extensions/ops/activation_ops.py +++ b/model-optimizer/extensions/ops/activation_ops.py @@ -23,7 +23,7 @@ from mo.graph.graph import Node 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 8566e8a..5e5cf2c 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 @@ class ExperimentalDetectronDetectionOutput(Op): 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 @@ class ExperimentalDetectronDetectionOutput(Op): 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 758f1e3..234b218 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 @@ class Elementwise(Op): '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 eddbd9d..2622d6a 100644 --- a/model-optimizer/extensions/ops/gather.py +++ b/model-optimizer/extensions/ops/gather.py @@ -61,7 +61,7 @@ class Gather(Op): # 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 0000000..1ade624 --- /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 0000000..8f1a226 --- /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 2b02ce1..4e23c3b 100644 --- a/model-optimizer/extensions/ops/range.py +++ b/model-optimizer/extensions/ops/range.py @@ -45,7 +45,7 @@ class Range(Op): 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 5477d9b..5a00a05 100644 --- a/model-optimizer/extensions/ops/roifeatureextractor_onnx.py +++ b/model-optimizer/extensions/ops/roifeatureextractor_onnx.py @@ -26,7 +26,9 @@ class ExperimentalDetectronROIFeatureExtractor(Op): 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 @@ class ExperimentalDetectronROIFeatureExtractor(Op): 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 0000000..18d076e --- /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 0000000..e5cecb0 --- /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 330f885..9062feb 100644 --- a/model-optimizer/extensions/ops/splice.py +++ b/model-optimizer/extensions/ops/splice.py @@ -25,6 +25,7 @@ class Splice(Op): 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 @@ class Splice(Op): 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 0000000..23b51f2 --- /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 0000000..9475900 --- /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 2d318ee..0d68c9d 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 372a124..d6ada30 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 12d4b80..7cfdb15 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 755451a..0efc280 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 d157364..00f3300 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 6e08147..0dbb3cd 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 0000000..80b6907 --- /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 0000000..5e725f5 --- /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 da39914..4a0b95f 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 @@ class SpliceFrontExtractor(FrontExtractorOp): 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 288a4dc..232e280 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 d9b7a22..b3f1b26 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 struct 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 @@ class TestKaldiModelsLoading(unittest.TestCase): "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 @@ class TestKaldiModelsLoading(unittest.TestCase): "\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 @@ class TestKaldiModelsLoading(unittest.TestCase): "\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 @@ class TestKaldiModelsLoading(unittest.TestCase): "\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 c7ac3ec..5502368 100644 --- a/model-optimizer/mo/front/kaldi/loader/utils.py +++ b/model-optimizer/mo/front/kaldi/loader/utils.py @@ -28,35 +28,37 @@ end_of_component_tag = '' 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 47e6258..1b221c0 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 @@ class TestKaldiUtilsLoading(unittest.TestCase): 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 86a0c17..12c0888 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 006b07e..ab17ee1 100644 --- a/model-optimizer/mo/front/tf/extractor.py +++ b/model-optimizer/mo/front/tf/extractor.py @@ -73,6 +73,7 @@ tf_op_extractors = { '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 adbd902..6f5d1a1 100644 --- a/model-optimizer/mo/graph/graph.py +++ b/model-optimizer/mo/graph/graph.py @@ -145,6 +145,12 @@ class Node: 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 @@ class Node: 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 e24ad44..8512bd3 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 @@ class Port: 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 3f28bd9..ce785db 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 cc90cff..ab860df 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 b3894a5..d2d8070 100644 --- a/model-optimizer/mo/ops/broadcast.py +++ b/model-optimizer/mo/ops/broadcast.py @@ -49,7 +49,13 @@ class Broadcast(Op): @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 0000000..ef17fa8 --- /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 d314ba7..8539b16 100644 --- a/model-optimizer/mo/ops/crop.py +++ b/model-optimizer/mo/ops/crop.py @@ -90,7 +90,11 @@ class Crop(Op): 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 22215ba..5ad5f46 100644 --- a/model-optimizer/mo/ops/expand_dims.py +++ b/model-optimizer/mo/ops/expand_dims.py @@ -18,7 +18,7 @@ import numpy as np 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 @@ class ExpandDims(Op): # 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 515bb5c..da9f526 100644 --- a/model-optimizer/mo/ops/memoryoffset.py +++ b/model-optimizer/mo/ops/memoryoffset.py @@ -43,33 +43,24 @@ class MemoryOffset(Op): # 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 fda2acd..f146aaa 100644 --- a/model-optimizer/mo/ops/slice.py +++ b/model-optimizer/mo/ops/slice.py @@ -40,8 +40,10 @@ class Slice(Op): @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 @@ class Slice(Op): # 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 @@ class Slice(Op): 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 @@ class Slice(Op): 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 9e513b7..0244472 100644 --- a/model-optimizer/mo/ops/squeeze.py +++ b/model-optimizer/mo/ops/squeeze.py @@ -69,8 +69,8 @@ class Squeeze(Op): 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 3cb314b..ee4a788 100644 --- a/model-optimizer/mo/ops/strided_slice.py +++ b/model-optimizer/mo/ops/strided_slice.py @@ -89,6 +89,7 @@ class StridedSlice(Op): 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 @@ class StridedSlice(Op): 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 dd3c13f..4923ff9 100644 --- a/model-optimizer/mo/ops/unsqueeze.py +++ b/model-optimizer/mo/ops/unsqueeze.py @@ -68,9 +68,9 @@ class Unsqueeze(Op): 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 f98c5ec..98f49b8 100644 --- a/model-optimizer/mo/pipeline/caffe.py +++ b/model-optimizer/mo/pipeline/caffe.py @@ -41,12 +41,14 @@ from mo.utils import class_registration 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 8d833ff..cc91fda 100644 --- a/model-optimizer/mo/pipeline/kaldi.py +++ b/model-optimizer/mo/pipeline/kaldi.py @@ -18,7 +18,9 @@ import logging as log 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.eliminate_redundant_reshape import EliminateRedundan 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 import class_registration 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 e9f6ac8..db0bcbc 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 @@ from extensions.middle.EltwiseInputNormalization import EltwiseInputNormalize 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 9a4af95..4ef9ea6 100644 --- a/model-optimizer/mo/pipeline/onnx.py +++ b/model-optimizer/mo/pipeline/onnx.py @@ -48,10 +48,12 @@ from mo.pipeline.common import prepare_emit_ir 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 e024bf7..65bab2a 100644 --- a/model-optimizer/mo/pipeline/tf.py +++ b/model-optimizer/mo/pipeline/tf.py @@ -51,6 +51,7 @@ from mo.pipeline.common import prepare_emit_ir 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 3e35319..bbe78b3 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 d7d28e7..3ff74fb 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 dfaaf9c..51cfdd3 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 51bc390..ded44a1 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 5352db3..0ebd8a1 100644 --- a/model-optimizer/mo/utils/pipeline_config.py +++ b/model-optimizer/mo/utils/pipeline_config.py @@ -46,10 +46,13 @@ mapping_rules = [ ('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 6c8e19b..747a5ce 100644 --- a/model-optimizer/mo/utils/pipeline_config_test.py +++ b/model-optimizer/mo/utils/pipeline_config_test.py @@ -145,6 +145,9 @@ class TestingSimpleProtoParser(unittest.TestCase): '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 fd5a305..0d2cd53 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 0000000..99347d4 --- /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 0000000..fd87373 --- /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 zZoG0jFowLuUa|;YQ}3g0QT;(C{FY^F=fQo;2;@h5oX5yQ&KU@zM@k|3pEU(9&$hxU z;D1p*`(LS)aXQ!rRLE`(T6=OpV|)qvNWSQ^LLX3A`K4YH+rSXzj2z)ep#AMAs73sx ze6bJ1VA-MQL*`>RLF%P%V~NU*a(kA5sV{Y7Os79655IuZ#vFF$KBY9kRpj?Turge{ zqmMLBVr^=mJnbz^b)RA6l*7T#@+pdFH9B0sPZw$J@it?Fb=N$WoS{0=KC37QQ*%d8 zO{p7qnCa=QNFy>ZZeC8AoIli7w*gHHtUcK42WsT zvU^3W5sc0=0$)Z~&AP@*I|qbAmRDw74|CCJU!FCGxwZhS_kx#b8@w>s1c)d5Qn(;!u70&PxQf#1MyPqfOb}1RrqF5j6d_kITypWv;1rmT zWb)hmH3Bg95*cqcMPrTfq>Me7HF@s?Q=x{&cy}>{dlFbj@}YIGk@=A{it@s*&bi7B z^apn*@Bx1d-i15)+bS{_D;gNfbj(6}F`Ac~g!3B{#3t4O&tupb)ut^KuXaenuBe>o zn7w$3(cXN5U#R!gTE;-QJ+>D&8UAIpF)CB1+zk9DS5eBD1}%mL8P_r^s(Unx+%@ij z>3EtN;*PPCl?X(l@?=F(K&Jq&SXCHI29e9)mXyf-X?k6aEaq@3Uls*W4q%m1(ns-T zFt_SOTkLhxDC51{4KCCZ=yh5O54S^ugOH!*hB8@5CUJ52fyj~e-ttCU5--dJHs~rA zgYV@dEGHEd-h(F0t33_fuq)DlltsS!p#94gfg0E~og0Hy+(X$U(E_$BdjgGQ-lXv` z-1rE16p!&YMD>`f3U>%Ao;Y)*~jdX(q-C?WkEJd%^;&>7!I?b zbf2D(5&>3bnVV@}qrUwC{Ewou@Q>p9qB!oJ0P*Z5vpe?Q3s9tm;_gBEemXyBCK-f#3c80q}vq%-nnKIp6bp>Nxq|z-5%Aea6>mFV=_c zRXgx8>KbDWT$u{u*rc6!fY3iUnf>RUqO}T4g*$3QQbTbfB$3L|O;&A>XwIj_$T(N7 zQXCY}4aDHdSe3^~?Z`8;dC71-Q@n&K;XUXCDn=G7-*I1QrdcqdqNk5mf=|)&yRPwq z>>YB-SpKTWq%RvSIO#psB+_GzB9Ajgy`MIm)QRfM$7jz2#dEb(lzx*=s@Gh$(8w;M zm1J?IYyCphURj6TYClL#?Z@++72$$Dg4+5EHi)hET&9QRzi3Lvh4?i<2+hN{Dlyst z@hB+et)ThnD?LR%D3gNmR)-Zok-k!AOu)@~LfftW2rQTv_AAw9srV2b>A!0Bz`e|5 zXHXkZZV@@Dr{M`nwUh=ShdY`LvKj?nCvFQ*36wG_hmIRZ)JDcA@i|=p%6IO=!u z;X5=0w?=E$1nutDkwB8PWBm`&XQhqK#{7U*MsLY~gl_9OJYrsyHI+`a@$5|dZyL5f z1-oK0h5HGM@xQc^0o@F=9#EWDD`DCywZ3v!N+wfDZG+>*dKE`U+$H=A{@qc9TXqj6 zg$C8PF`E*LS!M7yV;h+Y6XH>R-&#g{IlN*tZ)cQGU#nF?N#<#}CG%wGCpT$D@*XZ% zg<=OQm1187hoTEY8aO2mhQ`VLrSsn1T6<%JT_@>hdrwkssFysG2QyAvU+6ac$~l)* za}~0mli6~tHyjU^cZ(N|V;+~I$#T(Hb=?`YYzxX8590=ZVe6{w$=wO?x%dix)dGISP z>fa~dA*&#JT}EnSEH@7?iis-1K_I6Np2!k4W&8IpnLIxeY_=PG-kLZ77P7gkSR zjvT53SlbWKQZf(pnCJ3m<&jKLOSKb@l?H)px3tpCTt_CO_G$%oiLD3yvAB7jJmBRJ zV?Tip$ZL#M9tRGZgGq|K9(Kb8{#*8cD_3P*;7+NElx61H6<9&qm}u;?|0dj9mm3Gj z6|s&j%Dee4eHi*j+(Pp!0WyTI5S&VZIKnGyvCt>;!R$jP@RHU-{6g&`r3o3*Myka9 zW!JWQL+ivoZ53UNP3H9U(;``}<#O+(r;K#fZ*>57#dF1=dDu+(ymShEwDTF*JZ6x! zYv?>F9i^?Gte2KN>=-=;ytf!RC4LZI6lx!`=+)q`_-brdQXyW z$w_N8NhGEDQkuxalqBVEJ{j_gB}g^tA?Xb>`+V^i^rN;_t*y>OCxpf5Jo^(|(C2v} zJP#(3^QZtbakN;9PhQJWUTruYBz%w_;tOVVx`$4a2iWE8S=vV&EuSE-@u6U2bEm@z zn{gfE1fQ;MwI4w5O0QU)(IkEWKf7*}dWqf&ED`#9ekK_tC-67BwDc)TM1A>fK2Lpa zLQ7`${SC{(r+gUZDGQ~_z2vIqE8Nq3>lh?}t6p3WThTqCDXuLpR!;y$vK-2v(?A$X z+K>{?sn8CRA36e$$!+Oe^SO9d*c_6j(*6=|4KvnD94H3N9k9`E_b+5O)FR*$e5)Ep zce^xtVKi3;q6K7E;3qm89E;cRKWL-(t8YL49gTp5gK92R-byv_hGiw(7EO{DIIBB5 z@-dJnNC}J&7Ru-g8IJqTn|KBPV#g6zd@QIeQ?q=o?ZEikMk2`~t3W`ulhtjgx4S;+ zBFqCe`DyZ)_e5Ql8_FKEiazJpWE))Ke^{eD9Vn!a6qJ zIA)Au-IP!6MDY*wFWo}dC6?l+X>njDRUwN}eya(8M>qJMz&oQ1FKpCK=%@S^H&k25 z|1kQH8jwJ$jq~C}lEgFdTl5!N>1|DB@h$dAn5uqcYTQKWa|EJ`h+Ex)YdJgclJvjw zXVp3Qg{!07$e5bYCQyUr@kQ{lO8#IebD&+8ZMXZNc3GRq2;{WmStIR8#Z(Wt-J~fz z8C8Q!jTUiC7-3FeouPyD0CSOgkS-Y`Wz$re1(&ow;*CTvuzxmrir9ij3^ozB1rF-x zq|>gcJl4#jo}^9g`=o}eva7CtSJG~9JeOrV@HN(k`M}kD5T7*jOUsyHzqf9&y@C1U z1q({&?XC0#*{d`Z_Z#J<`L5!Thf#u(u9f4-ObVQ}kF!(ws2$ITp_5W73qVrtcAz<4 zAXesK)(KLPv;lsAi#F0Br(n)@oksPgr|HYE&yGel?22RvX{0`|=iprMH%DVXZshI- z$%e&jp?ZteQxZrDY@+pWLvTzFqsO!r_%oQQNz&K4 z1Ki8Q);w0)iq*dw-9xLbnPx}nPqBFDA9|jw@}-2fTd(40Ds!OweH&h+G?m*~?}Q`x zB7cJ4YYqnyT4j~-bd-wm2M);`tF`o`aCj2cipEZN0kw$PO1KK0>6v^edur9dHDivO z^Vwsvk{`y&gk*JaH4%P9x5W~^%Ak7Mo{8u`c^@rHvaAB;VNjD)*EoJh-bthBuwY55 z<9z{nYWzcGJs2Ol9mqNV-0 z0nu~N?0~ZANd0#P-7E6;gh9SvJikab9vlk8w{^znhjPr%b_e-i)xV*A=6v2=-Je(k zX1_8K=g8l1!&iC9h~m~$HWu#bot3q|Id*GT7>_3LR=;&cj7F&*&Xi^3yc$v`x>x8m zq+7%p?8K`|D};jR5}rX4)%EyEptzOIJ388-7IGv^!lR7cFd-C{8wgdk96nbWr_P2h zCpT(reZpI%xnd!fC6`4L{1u^t_^I5Amv*1WS9l-&AWcXk))LP@61IOODbV7cM<2@P z;o%ue13t8s4OTk4wgV-0hF;S?09vO5r7%!f{FPC6*EQIXRtoF*1adLqtDR_WmQUFI zrCfcDE7LxRziB>RTE6deNL^TFbTyK06iLFNnY2O3O$VUIuUg+s0l#xA}ZyihB|q4vq}OkWH3&`z*w2rcCU<|euu^yd#{OJtluUC1Wz z!#?7Xai5{GV)8+-{r&yct?ak|iqT~yDqb5>V%w@?-D^*hOP8f$6bguRdc$nc82 zVrN3iVyg86y}*_QB8(rUVYrXsiNB-O58k7lsm%{$egSpsv->g5OFVEt_!oUKpYg&z z%GWFR%6BE-wFlB6T;1wo=CvEyiS}x=6gTGeVp^&tK?5kMb4LQsaL?dzS=VtSb3nrG zH#Esy2aeJ-BR@zfQctL&M!O`54jsT1|3)}Y8R0`zsb3}mRs zAra=FVh8_^v?`gv`U_WrV|yDmN#>xyAbAZuZoU$JBhIqn#4Sg=v#$bZv-=O#+#7Aj_R1ud_g^*txW zVD`7{7luZr@fG9`^}1)1wbiJr2lP>N6BZ*iHk_L7`K)fjLvf_&Bzu>1j*Rx@xciebY@xBy z+DAV525Hby%03&7*+lfG;}V_#?ahV7KZW*YhB!=ZV($}vH=3vR5x>XxVSdG{OkoG< z3O04cuUv%&kD6>2dt--5)!19=qf_GxpmjP2ujdqFR3iM36e9w=$qvs2u^M;*x{$@0 z6G&h86L~6yJh0GDErffUhjY@?_hdnHhi9VfcV9(k)LiwQ=M4DVYm;Wu0_nCC&os|U zT#lS%&)7j8$&R2=_9D7dnyh5;68nBP;RwH44J^+M*VGKa@$I#P4FnI zS)ilN@F_f3?}%0q3@)azNhxw;{DMT$waUn(N#;pjmlxtMvU5>M>mzQDo)|~uIdU>P zhXy5n_m@Wbp!KmnqmCcUnzSEkOF2G*Uh1C%6}hNxF{aUpyq2rB3t2UdGVTd*xmv1U zRL)v`9U8jG*IHv8v&nmDG#g>|!XvZROB=n9*h6iIV-LB*Is9j#yYwJFM`|TZ110W!{Bye_oCy8M&0s zFk4ijFZe1q^d?Am+@I)Zp}A02m_-_b6D=Jzp@X1X#l@%UGwc_nrM?FJBYE*IV{}4} z6u?J-2(%7Y7piD^rM|YE6H8O|iB7I_V^1aIo7DV3M2#Wtp4{HuJ{ zdw`cfZ*URW%o-?9;KhZayrb{ez&z&=e9b->F^zX2A*CTr0Uc+*RoY|;?wEh2cdQ0^ ztX^aH@PBkHdx*k$Zd^RdM*~6~Tu4}?LJuJdQwQ*^Sf(z1UdoXAp+_`VJINF^9^CS^ z$S~u$rC9mM7quwzlV8F=ptRVGKp*!rWliWDz3hTMVD-IyPoG>mLo5?oVPO5TJ=jQf zkCom9W*Pk{z%H;L9!QvH>IsYaWT4rk$QSJmas^dnQTR{$UP4~jua^pMHMa%cJIOYu zaDPG^DwcWQ`!Bmjj%9}QO7_}Se7}54{>DMKeD~?ua#pw zhQDVgrMtpD^h&8lxL#CEhQ>Chu@QV_*Vmtr-^Blp%JWmUWR(?eupFhQ>GQRuuhCQ5 z+L^|Os4a6!K<`5YeSxdfTrQ*St}J1&uZpV|Dw#M9wWC+vw=K6^E#|xt?HDQk6MvL& zX#;Hvo`frD1y~6@SlxlT1UG2Wt{e2G-rO?^KcSzEbW_KbX?-%sGnC9r>Lx~2tgYRE z->`_p#zuW}Sr~HlguXEceQzEkzhjtoxSV)`FHmxv z?Zv+X8Ah{2SMU*i1{%-MV9EGwpd(flN5#f0tbyxWuawq-fPCEChf}y3Bie3{XiP+# z&@h!~@xf|>Y+WD=Ac5aYnGZgej`DQ*CXaEo_hDrq{*o0&SK>2tYhaii#y{Zl)_M1@ z#JheyX=!%H|2m#3E9gJChus+5pv8b~@HOostw4vNGDdI5L{gJ>S5_%onV}vGlmhSL zHR&06a?0>(Sxv}w=T;-0KB3*z=g|N4qqf5}(7(ZS1a?N=VVBA&Kv=8{*Og^~^7i=D z@8%*j(q0pBB(WAS51dvFmMOJX76_-wWjfv6*LDXw3xn+0Y?*8cxHevpShE!0PMfkx*l8Ctm&gjfl@-H&CXLky zxat%tvQ!(w`e-XyhJDjF8fofJCglw^F3Azp2e02kC`gH;fk~$vo_Lq>{a*5hqbLr*FZP8wVf7vqYdD_ z{fCZ8{MG&;zK&fS-7~j|RfShBlgEOKxira1Xq}WLeWbrChlPcK#jGyT&5K-?B{@rP zfx_8*-yzT1U}x(*Y@+q!UEaPiy}gt1♷{*2#3dvT{)=`L3gSU*nddk<;kX3q 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 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 0000000..1c572cb --- /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 0000000..62e8422 --- /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 e8408ec..ff576d9 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 0000000..27a6455 --- /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 5b46a2c..2dba5b9 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 eb74892..a032f83 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 0000000..d325751 --- /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 24caaf4..acd2c87 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 1e2f557..883ec69 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 e196da4..e0ed76e 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 209381c..2accfb7 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 8ff6785..63a5f5d 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 7c903ac..0000000 --- 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 ceee153..0000000 --- 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 c73671b..0000000 --- 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 40cec31..0000000 --- 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 221d8ed..0000000 --- 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 113eb9d..0000000 --- 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 76d5ef6..0000000 --- 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 a166b23..0000000 --- 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 ddcf267..0000000 --- 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 8f36d13..0000000 --- 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 300dec9..0000000 --- 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 f28b84f..0000000 --- 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 21ecec3..0000000 --- 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 25350f5..0000000 --- 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 f2fed25..0000000 --- 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 fcb26c3..0000000 --- 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 d90ebfc..0000000 --- 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 ee13679..0000000 --- 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 d14cb62..0000000 --- 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 8bcce97..0000000 --- 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 45f61e2..0000000 --- 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 e91033d..0000000 --- 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 3bda89a..0000000 --- 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 830f73a..0000000 --- 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 dfe461a..0000000 --- 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 20d3381..0000000 --- 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 184ade3..0000000 --- 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 88df08a..0000000 --- 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 1002daf..0000000 --- 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 a089b5a..0000000 --- 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 3d45cc2..0000000 --- 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 bb8de49..0000000 --- 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 c0ae9f2..0000000 --- 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 f1e41be..0000000 --- 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 86d4cb1..0000000 --- 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 b30a72a..0000000 --- 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 88fb713..0000000 --- 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 4c053f9..0000000 --- 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 53c7c57..0000000 --- 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 672aa3a..0000000 --- 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 a32b29a..0000000 --- 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 a37686f..0000000 --- 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 5853a5f..0000000 --- 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 8906529..0000000 --- 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 66c3c4d..0000000 --- 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 f02add1..0000000 --- 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 947a3ec..0000000 --- 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 278615c..0000000 --- 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 21620b6..0000000 --- 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 e34e701..0000000 --- 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 5ab09b0..0000000 --- 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 8118dcd..0000000 --- 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 b9c28c5..0000000 --- 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 2ff6013..0000000 --- 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 ab7a108..0000000 --- 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 2a060f4..0000000 --- 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 ce004af..0000000 --- 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 5a5d8fc..0000000 --- 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 2ed64db..0000000 --- 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 98217dd..0000000 --- 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 7c07394..0000000 --- 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 ba3578b..0000000 --- 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 13c0de9..0000000 --- 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 fa22360..0000000 --- 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 742097c..0000000 --- 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 c573c7d..0000000 --- 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 c1381b2..0000000 --- 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 f5bc379..0000000 --- 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 3c2e37a..0000000 --- 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 1b7530a..0000000 --- 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 1b8e953..0000000 --- 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 e9c1ea2..0000000 --- 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 7a5c29c..0000000 --- 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 f5ce2c7..0000000 --- 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 cb229dc..0000000 --- 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 ff16cd7..0000000 --- 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 9b24ce1..0000000 --- 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 d9fffc7..0000000 --- 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 c70866f..0000000 --- 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 37920f2..0000000 --- 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 a6138ff..0000000 --- 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 fec5b3c..0000000 --- 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 1e22b65..0000000 --- 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 318a3b8..0000000 --- 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 752276a..0000000 --- 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 c3a93bd..0000000 --- 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 26468d4..0000000 --- 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 0d31750..0000000 --- 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 fdef034..0000000 --- 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 f5e097c..0000000 --- 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 7ed247a..0000000 --- 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 9eb4341..0000000 --- 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 736eb0e..0000000 --- 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 d8dad9d..0000000 --- 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 f0122b2..0000000 --- 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 3edc2e0..0000000 --- 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 323fed3..0000000 --- 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 29bf854..0000000 --- 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 0e9fa73..0000000 --- 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 2ce7b85..0000000 --- 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 0369342..0000000 --- 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 d7d76f4..0000000 --- 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 28fe73f..0000000 --- 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 3999b41..0000000 --- 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 5f5b740..0000000 --- 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 675741e..0000000 --- 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 33c1346..0000000 --- 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 df1e04f..0000000 --- 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 0ceabc3..0000000 --- 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 05d53b5..0000000 --- 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 df6a241..0000000 --- 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 67f72f6..0000000 --- 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 1fc2c8b..0000000 --- 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 f6cb6c7..0000000 --- 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 d5af464..0000000 --- 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 f765dd8..0000000 --- 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 99800d3..0000000 --- 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 d212eb7..0000000 --- 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 add7c69..0000000 --- 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 45f6b01..0000000 --- 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 7d2b660..0000000 --- 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 38e7a9c..0000000 --- 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 ca3b268..0000000 --- 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 952fbb5..0000000 --- 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 affe639..0000000 --- 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 50c0a55..0000000 --- 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 efe6105..0000000 --- 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 533398f..0000000 --- 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 090f69c..0000000 --- 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 9439818..0000000 --- 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 bd07fa0..0000000 --- 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 2291474..0000000 --- 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 d1ebd9b..0000000 --- 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 f304517..0000000 --- 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 zcmb5VRa6|$7cD%vyTf3?HMl#0!CexZ0Kp0F?mq$y?(Pf{T!Rw^3GVLh?r`~kYkd#* z@mAMb{ZQ4Xs=MUu+WYkT!utjQTR~P{761bS1CaZ;0Po8HX#gB7?Emsdg8#?}$Os7V z@Cc|#NQlVjsOacusAy;yn7G&&m^hedXxN0-~eE8VBm0I-unUM000caM{i*O|9?V&g@Z># zLWTjLd=zV717Km`;9%k5KKhIdhX9WT^HGd|1Hh$56i+}Sm0zpz zy=Rh;mfJZvVeU8lbC_cZIt7mc(ACX4tQiFi|yEAQq zbRAuA!TS;b{eRVQ;BWxqfJU#?yJ*XG{rRwc_lulgE`ccXpR5=zT;2g*6AB$)U!hJb zR;FYsfka5eck>$j8<`MXznKQpf4?r72egQw!pFfp&;U zB#*I}?Ir30w0aq(;(dwLyUe*V%Bsd7V=G7$Uw(_kq=*5Zc2dQK=ZPDG{)L(xKTpVf zkH5;p`et+vwr8&Ip^g`QaFm2~NwRxG9LGO9f6tBW=#a`$Y51R1j!g}TBwH(IvZ#6S z5a_5O+L_xb(PucEh1_vzwAAIn+etCo)a5ZAqR)S(Z|B*K9{Vj{ZoDm=Z&ds}pAPhn zjH(o|G#?KVZOfT#i!=kz1a8c#>nC9+yij)j%*e_v{f8**YZ>Fdg?nOsP%?cRHkTBF zF$Q7lh#DIKPaHNUlLT|W1MrO!|Hf7nQnntEMM8<6r{iD7Vy}n6FJHSZzieKPSJH^A zS){%LA|dOWg=;z3?*Lq}C>zhmnV2AMO%#0Rh$|r~ep*Mczb5Vt8eTjtd*8DH=<(q6rxHD0i*qMAHLr-L zf&iVsnSuFRo|yr$kOqAKkC;(v1(@vgFE}5@SbW^z>ra%>-ecz<1yN1#pICeYJ=k`w zJbnsTX`qm~8>E+%mMMu;PDEd1%AOqZ;XH*g@$&-+a5uR}g}olqo)AxXiqIfRK<`36 zioAD#%wE*Cq%JSH4LWO0S3d}n{KDGxiL(R~pniWoq>~`}s8_1&SH=Vhb-jN!{K6Fi z?p&V#C;Qm&ZKW&Tv;YWcV?d97zR)B8-S5M85M=;CJpF;ttX-m1(gwV`uiqGq3G70@xI$r8g<- zuG?b#EA#@^yl8r~&ittredISv<#Cx1C*ob0D9_y~Q;HGC2;KWO@)nEl_)n)iv zA%DOlT|-1O?*KN*1|Iz=_9Q&%WY}Bp@M`VgNO=sP*`_?E2jwZy`>5zQW;g1LdP?@# zu1ejPujN#H!T{c{3p&Ad1$f{vSy6?3`QVaqY7uoXBEG=3fWW+(G*ij~;2of=0iA(+ z(}TzVJj6tp8*B=_m)>{>D7)JxwH#$zZTOl3&GyGKoeVVX@dY+5kcgp3Qj@*0O!>}# zQzTC>-U0Il@(1m+JXU7D_5ze^ND>)nfek%*@bRtOFM``bTQ>2(@rUxXk?sR=#xPq* zH{Dn0l?hQkX~R(zIaN25(5LNxq&Rz8~HfxVDMknnia_ZiG#z;BinCq+7^& z*%ufeH`tgEqqE`iHg$2k6TeLzu6KE8-&bF~s8UzUmq@4ciCr?0DH)}98*i-R94KB9 zV8-qjvK$hL>3p(>LbiG)Rhm{S7>Q98mNL|HCdP)l@&v?7T24h_BdkdfJBNFeXEYU@ zzNvzoD3@}wKnyRJcH-3mq8NqwMf_sdx!TT$;y5;KZV@NP9Rri3*#ogvGQDAMUzc0O z^0{Bn4aKIsi0+u{Gp195r{4knt1sZYW}BSaL$SI#Md>Yqx*`Ai$T>N7M@cpd1dRkl z(;up0C;-}Ibh)J!j20fVEYUqNEPOBVT3x7LX|z0EGtaLY3~$05tVy#sx_7|ltND>P zjNe7|USj3Z6x6y5@=e16SW{XyMn<{l5BfoMxHCO)eHl|aeN}b%teya4xj*i{@l~8p)V^HE2gLAsff|w)IA_Dc_r_syPVca6DK+lF100PASgIU$!c59 z8^)Z)DiEW_`~=`YY!3<*)f$XM=Ryh?mrZpVc2~$(AattrEyc=m0juYx&BHg^CIwug z93V5B)wmVY+V0ijYooV5#0PQiPg4iI19H`DWa?h<+Za4tQd~WD0u=ul@C_euKdw%teZ)Ax3`QHLeYUEWyZV}gB(yuSjdM5uDZxzWSSmH=+q~Y9 zJfmzdOK*|Y(oPmRJkeT&LDX}W_d(vD6^Zj0XC^6Gic(30i!`ty!H|?Yd2L>l?wUUN zJ0PbOK3_v}pGJ0`E@}@h+_bPQuIS2;PQAZU%xnIew3SF0J4JC-2n_R%ah$JJ1T80b zk4i|jL)<=r(#IOL%@+4Q+Ld*^d|;Y*NOYCHly3*8O?!+fxK1X?QWDsvh*pmwwGGN< zc3!GyGm5G!21F+(eR*_IB*R8LErY%TN|o2WXx{-uqjK+n(+~e&oV}gB-kJf6byO){ z0`?}t5d4YSq3;vkrt5jslp?qsjp;@n5|#%Ik6vu@#7A7!vS>dY_r#d%A4pE7Y(4W+2U#7{u(Lyj z)^D>7<7(F$lLdo2+0z)is)N?#<-^%+9&s>?2KUUrvNB68liMv~Rd|)p8Bli|8?)d~ z={Vve&W0!rCA4m;b@X^p{#1q>Ea~DZGm(WlBNv?4xzp5Q#L!LI-fD(13vF|k!_lxJ zPi_~SY0|@^%Xz7I?ZAo~SfN#8|G7#smB>9c0N&A%h#R7bp;h@cF1Rp4J15-cH`+!S z&*j6RIk{JdV7@Q}@`FeXe$8B$hUf!*TL=|?q8v1^mh&E|Gkk*!hw36dB^WedhAs@3 zv$U}(H(8DE(KOfI;iZgwSda0Um7Ub+%6*MXN^ByWYsaPiY6 zGcH_z@PDXxIC=-59Qg|T8NQiyN2E}i^41hiM9;hH<+Muk21L~F*jw*-!u%uox#H>l zo4O*bW^C`zH0_`m(z=+;=Ti(Bm&5j3L-K)`^7Oik8u+5g*s+LOinqerp1BH$Tz~Cu zDMdv01LZjN7aCWUg%`@fOM|~IVm~waKq;Y!_I=UMPfs?DzjUlI6CjM6;~5;F{4rPb z7^@*v2`EJ*xpFTG8V(1|p73G7VC-3MW=lfdR3S-njaEg-LY&~st$?FvQx+p|o@dr_ zQmNPvO6T(uo+gweQ*<3Bh-^oJ@)*Od`k%UBL#c!m*je;rb}-QCy7&?VL*2`oRrfWt zMipr;1{j8i&d4#fdv4K@A8q<1Jcq0ICRaForRIGFj|8o3XmWzB?6qgPLv5_+7FEAd z^=R%UrkM-&@;RzVapkV2L*G6Vic=03(vUv;1)di_I8cT>=$%LA;C=I-?!fsps|k{x zCwxHD)U{vSFUTTDgo{Jz+1_Mug!Z=uZCH;%_Za-Zsedl2tQuA9q11yfZclEPBs&C; z*M`&FH;q@2RekiES*^MTE~(>7n(`hE(H>IhjKr%*ro#(SnO)E+omB@Jb9poiFyu8` z8L!Ix-=uA!FLyboJTR&sv;WwcY@?E7`Nymu4d4PWZtvx^)e(^33A)G%8H*@5K2Z32 zNAl#accf@SaCU+hpLjjl3s^__D?pZojTyX!>WI1U5b<5u_Rl<})`vGMaz-kVP3E}N z!0EWmO^I3JmH#Uo$bjG{jp0m4Ks1n(B1Yv23OIa;DE|*lGwT!v3Z=XT*yixpgb{8* zk#5nhwabGHLU-;d3cr#N8x>*8gpO#O0hz`Jt-dPDmlTWa`uH|T4qz9BC_^{qaoZA< zIZqpoo7pSmcN93gWD?aGf5HtDL3vSDu`1<{b=W_~!1%0;--$dM(aI7{o*kkN_=fWe0qsjO~*bttZ95ic_}w~ds=AziByQze=)HM zbim_*zj&#F*0BFjn|l_+yQF_@^fp!I3&&!%EW|I5rkm^KF4`mdIN2aEj?%B2w43(q zW5ac<+>|s(?G()+bbO%{soC?{QC3Xv*yf1wZ(B;f0atw16Q(N+5?Y|Hu%DP$bJLq;>T$zu#nf;M|R1uNkEYhzVh&3W#*If|vNdK#0C^>J9 zdjxL5wrM{}lHrYsD<4&1nk)Lr$9>+}OPcKhoDgP&#exO1kwq{>V5P znT-+v+TSjJgYL!Enqduk4ry~bP9(W;SM(!y+aj#w=8{(TH3DH$vXdX7?%LkVueAy7 z@@zQD|maBf+>dk#p|G+z74hJp3 zP#z6Sh-zDqwif;!us*FqF7j=MakVsDg`;Y2tTewRcNGOYYm*X5lmF{vM3gq|gq%!0 zb05y|W-|@WMTAD{WAw2K(>EIXVv7;7Z8ve`#zuws2Ze2c*@xl0y{U;Og;4F*C@tLH zZCh#*c#4hZqZi8|OXaZHP9!!%Wj}N`_Nh@2jn}LOFtzk*kad6N#H8r{d*D4=hW8Xn zsYf`=+^ioX`;6@5?K@x#ru~LcrNtQv9bOIyTxys!h4%O$}!(k(i?W z^>06u#8nBh_78ug{*DPNw08w=mv-^yxJIHLhnhITkkyw&1j%S*fYAzk`5XyZE9~z? z2bv1Hhx^hjWH!pWd+w2%{xrhsLg;Aw|G5;j!+7HZ_eC(fKk!C7HEgHc5H04zpb$$N zL$dx%bPQ}@{;w zyBERoQ=a}of+5=HkvXo(GDWRRFSyyV8AUe<@ z-;%t{q&aW70#=3n3Hf|}y$1B?0wEn>7IB2yO-d=r;@O1Yb zFjBJlStJxpw_r9*>+@{2T7a;XfB}qdL3Z?$n~GpBg%5FvOW?qoH!zH5FNkZpXP|Yt zo^0#g5c*9qr{ZIISZM0LEaziAhRNlx#|Qet=qATEy*y7|Tg+Zr0#~4@uL{6ctsy0Y zqBSkZ^M%t02dEdWX^^pT-ECmekUH+P>kiDx3!Oe1HvgqsMidQDcHdpr5Vthn@ei~* zLu;bV^r8juE(eooD8pfLK3XjpHWZ2CF%F#;niLn{8N2V33`C)@VItYPRm*ew#JGK6 z{QAQ}+tdLzw|vE|Xt)0A;!Wv-w$x?h)0YN&RAZA|WrUH9q8zAdhGL+=ajC9-7nMqM89nM{DAFeYgc?P_= z!NzsJooiO`xqx|5xxpL#e0tW?o)(>q`|;oH_s zpQ_+C(sA@bOfgeK|IIc0;I678Vxp}I9@;_Pqf7H9S<*91wiCutWaoeBp|Nj9Cz!jm z8+ds~2psjH%Oo@QTm;e~J%jU_AQ;NX?=#kL{&fBz$U8vEO5?O>hrTarz0W6LaFXO`NE%Ew3-K_>6~X;m=AL)Q4&LpmFzQM7c#0t@bA0E z_Kv?Bu{NkJ+9jcT%%L$JTq(2_&F%&Xk~c%-wP2O;86fI4`$jGC;cvoocVg?qAD@n; zu{b5a{-X_f2ZV44Z@J7iF~*@^F}L8Bl#=K$Y&08Z?^Qixl! z)HsVvZ0!9;~O#ewP2tO=aB3wO@NVN}saMkV&n)Zpcjxe5vd!6YE>!`dTFRQ)-1J4i{}{H@GI?nTZ(u533~Np=Rik%bij~|AXX$ zQ5n@Ae<3&0I`3hw_`G=E|BI(|?7*S4*wKs4s+{;KF73@*dNI}wr*REz`a09Kf7%JE zkb8|cNAw--v%+dRCl!ltgUzW1-pJ< zG+~Bcc=S^gyMArME3;n6!H_|Zhl1~SP(@n>W(T0FSob*-0nhvwDB>0=hr+!+B?7TY zK>r=A;e&Im&VNMDYO%k|ZQ1UKVc-XT;Qdpr(H0JP1W!x9Ea+eKqm9Q=`KkC1H)MDL z`QW5`|r;FP|wu0?s66=s=*H+>AFp}-2h62Qkdb&&Tu!$TX=TFvX z%mu-XWA!Z7ZxU_FCRz6IZ+h1$9drbrguc79XSsw7v@&~~^(@56y3HrgTYra#$BLkt zmIQRjc~Basv~p=^o|q=yj@Pocll^j~$iN$JW}|rIgBtcB9^$)l<&QO&TcvpG&O#peaPJtQnv^N?Tk< z_yF$RM`45zyO0b$+AnNA*lcW&&M zw{|}sGV1Yf=mswtWMTAsOp0R>#wt}r2yaOXmC4tW(AG{IumoAQ8(xOtMTs&lcYH$)L(PC}F0FaKPTUQ$ zJcSy-9iw;!Fr*~GGSKLt9(fnC5`92|U|!e+MH{I16}%c0dpLiz_Q;JlAQn^_vLGIt zXnDLY#`Sdl0=-g3qh8?v+yD=s*M2qW*`IRlql#0xjiw$XHC$S!HjJTk18h_-i$A;X z-Vl;54NVc#q!d^ReBG4#3$B6tGKfTQZP*Z>Oj6~7NZn%M=<4y9-%ymPROC-HUY#b2 z5r*A-gK%{Dv+8$2Zza?XW!Q}#^Yh(WFxKDbF(k)c@*BitncXPx*Ua& zl=+$=zbzg&xneW1?E4v`wZ^#+I^*2tNXD>cq z9e3=?X<;wg>V_hbQsw0;=-dqi+o(cS4|Ck+!k8wyM}~+gMPaS6MYI;oe$Z^918&ZE zh@4AgP&7Cqu1N!EA>*dSWiHu8X49RS$iH+1s%}}kBi$2EoQmlRAxBP|#6=A{f*O5D zUOF&a!1{zlVPmDf2v8JZPCz-^mN|>f4^7;DM`19{7Vxk%De!<`0rc(DfC?#bCt&-jI&+ZplWMt zVtvc-0Be0qjS0!`#~+%26EaQIC07vodGz=1;)`8B!57Wd)9W@E%KnXeyrleJ9E90) z=BmmHoPY@*JcqEE^~tcGVq8b<$EmOMLdLH**x@mNj4|zQg^s7?wlxzt| zT^m6urq_`~2Wq@nT!vU2_O{z{o){VM358JrK{=}pEz0uiw2n|(u=Gs(jE>1Rp%@GuOR0zbzdTyQ9jbggd0FjSHZR%uw1;&qGO=WBfkTlez08Gq3 zL_U}SoLx&Xwi{FAxQIcV3o_&FnrtQRd?LO$79bW8`*Q{ocmt zRwe+0dGjHG2wJ{ja)@qT4j)nwqdQ3uSAA3ZL$G_r&$VNYk$(M~^xmODo(rwrFw!Pm zeEv$DL#ju2tYg^Bx1-SLzo?89-9W2*3dQ!7tHPe?Mqb=&67!pFg$>yFY6iwEL*ZSl z*lanttk|cLyMz-0>uM_srl?;|@F$U<$4Nu$2^IYA!^0>P?Ii%=%O?=tWr@02D!Lv>bsBx0Hlb~-CGY~HWAc(h$( zz@Ht&zRjE8s+#)FgaY`s#X4@VWJU%Li-P99FvenP&zze0gEa6YDV)Lx^*mPOmEaNw z0t;B-TN^ZbCOl#*vQy@NdL;@Q707)4_f&GgaA#HI)%*vI1VMJ0clp9sEon3<)aP?Z-& zKTdq0N-{6KrZkgHp?;Uk{9!=caC0MG05saZ>i2;GrULO4_P zT{&ZZaun^NZgk68W>Os4j*8plI9ZSjGH+Na{^Xc-?557rCyX+$X8} zq`qbMoQ3hSnf$p_d*_6^wdWw33EN*_mJki)F41uA0${t%#boqG^kGmZF5zTBQiIpY zAD{SqD5G;u_$p6TsPf_soX{~ap#=#DuaH|}QzTA&1r7KWBm2XO5I>j*Uk@`$F3{51 zZ-5uA5%B`Q0KlQZgi)P=fHbt?hvrgzZg7SwOuQE5N7@~xdv<^#zBmtiCHS1D2$HdB zLWrVn5*Y9yFMK>Qnbwk5Cn~6A!9{`Tz!VrrYj6zT9!^E~c{F|K^p5{yDy5?CEhP0P z%tQ>N0pL-EhBo$DGazcX*TxHn_xU3CoWS!1>gFhe3rX; zi&A7?F+RdVKDNwIn=*x~xam1Q+D5`|hjCD(jElK!c;E3}{5~7U7aui*C#Yx1ac!}C zS~mf;e#V7=6=0UwCAO5FglYgus@rDJiB*t}tMudCa(gbO3QvhD*9)hoRUgaT5)?d* z(>*G(k(E0*0sFra+AYcL@b}!`>ZPjq<+Hg7#GWgw+uD(jqGqIheKI3t3|hDz@2vG?F^XYAQ+RO@F(YqS`;{=4#JG{r zw_+Q0tn#`Z6bHB`1+ow8X>K^8ncPn($sZ)|2<0s9FK0V;>KZv`eO0K@*{8^Wr^*S> zj)dA#8XG!)a9MhqK_isa?I<-ldf!Dq*p%I2@})pk#e~do)cEsvfMm58TB_)(eT7}y zY)?2Dt-#8kjVs}oMv__y+Mg&5fXjtDA#k#ZTCW!qUUY)~o-2o@DO1(KwiDn`!J+)K zlmOEF^OtNl`VVS>t>+5(@#<=TP4csYd}WJp6$KaYCS0}v47HO;{?*McPbHcZY%B-h zrLm_V3RX%*A)ZKaf`{5SS?5f(eAc4@x0*|OD>Jj;U`Gc=#ia*$UDjk7_Sw@W&iX|t zSSh5%B~~`VT`2TR7-nuC533ZeXyj=bx^$it$ zMXO=Wz)AEAw85$T2g3nkY8$N?j0!Zt3XN&~ZB!^Y{{s-2RD4o#i%+MDXs#qm03nsc$wR6@aGL~P{!ZSWG~yPccH%nY;esj*~Gti z6u+9)BCV}7Aa+`vobVw{*K9kxy zx-p`hPv>z%i^tGbs<*m;=`77V^80^Hv)VP(zC~j@x@1Y^j&RPcLt7E%eR#Vt&}|ZX zXFpYfT?PN?+YM1|j2q7SRlaD4*t>8Rxo3n_TpG0f8Umf>Ip=n2y3G zNAnMHt;MlqnWM(r&tJ1Y#(i&^LKH=xL955Q#g%KeUCZd{yS$m#H*-4cbux{wi&8B^ z?0kbT1_0@hY?#HWr) zHzcQF=bV9AT>HiG@mQYfI36mJAHlq!={TtX@8R{&%G$2yK&E~>62>S^89Xf3Pi1d| z1H3S_Z-vRcvAA>T27$E8hdpDFO-UH|=PUO&a=n`M71o+8t{t+X?_xKIF(D1qlP{(nGWv*7@=k34>pop1y?8!?m=-b zXIO6;zGWj3QQoR-skGRt<1yG%@%@-lY%-){5i~aY#|07&sMPy$LAsi(mIB?tZD7E0 z{%X=`5}pi9d6bq*{g&|ujGC4*RlCeaUtE~59 z%2ovzQ1j^1#Nhhke`69990*{8OS1OtPW^_A5Mc?WIq*3Q6vd5W2BY>_T+%?=oaJbn z+{l-rd#F-43CzRPp+5i#Tmmz5Gj*pxnj->R!T6_Yze%__-=JCVU~hZ zvv{cY!6%KmT*nPu{|u30Y5^u63kmUY>Cm~{sHAQ3>zBsA_zy)xFI|QdIM*7uAq*t* zRLB}B3kd^*vKz9(wiS@ApJ=(hX7#^78gx?7&@6ia^7Meivg1*-g7TY_BA8g8>moK< zCJ+8(dM13#jgWQq7EdNUNfngD_=GMcYm8EO+!Tc@TpufQn$Q}^Fjv(hU&g2@me%rg zL6)~~0}KOlcwl-$80Z$@J`ZO__*-m~8JI^);YAbUz8BYS8>|QV1mNS}aa}lQr%4Kn zgG-H$<<17xI16h0q)9$34+?@m6}=@((_<|sy*Od89{qjivvT~+ybz;FAin7ly5k3W z?s&ZgSANWAFvVHvFa0HDykbvl>CAqVvNiF=b7caiAIl(QSbnO2J$RFKDY+Y=$GzYB zkmPp&-sR?b*vaPPUu*#X{)b2VPh87@K0~fh^$&M*e?7({&F-L32~UY+E0j{Z1Wu2u z%1?04(e(M9P@MN2X9iRm~ZnfI<;?G#6`WRcamI{P=spZ|+Amx3D4& zOXkx)#H`WSqNrw~e=r$qXJ#d`ypm(blpy;Xc2MqrX5*Bie)JehKe)`SOs>XfDlt?t zwdqHv3|%+ALQ6K)z zm7LC;9EfSLxst;L3OO>tOUPOLSy1CjwXXL}QQ2=u3L{F&w|(jqyo;tgvp|#wN1wZW zEjRrlYuf;`8$I;10xLH_l?O#i*M~mm5k9F=&$Yunp#4EUvwFhCKF*PxRLQ$#@mD1? z%}H>#AC%v*m$cnf<2FUL!7>{j>_sMp$ zyS9h~XaiyH=K!~#t>Teld@Fh>qF3{@vflB!UYxVqaDi2f3C90vS?Icp6AEtYW)};E zw@wYuoo!QNJwUS%tbwsvb;iuhZ_v7Ikb=@VoY}L1N`iWAZO(l0C>buTP1>aLD{l>Q2L`pzVnl^&rzu-xu*8){E|avY~A*=z?8}~NTj-9 zt9THF9s5P`BJUv_W?ycuwYu%*x2KNqT&e?bV(31X&iCSI*0bW0G$Uo6G!$AOgH^n^PZ-OZ#7?M`**=@LoqlmJTUD;TRPUlOA{R%k z4v{)##k9MT7YTGaP=Ie(hVc#EX z-)P_BbSe2Wpg#Kb;$VaqR9kIE(LpkFkmGyN+Br`Ez{H!GaZ{^0&iS|c>K z;xC;_eoXG%&c5{u!?K7zL*Lqa}|vpBw3t zI~DG_R{;Zblet}z4K2VIzR#s3q`z9?oKfvh1Q0wYYfWGf>QA2z!ttV5s>4xJ)op$k z#0oN(7nj8TymMB@`_wfN2Q%%VB2KtoVEBrgm+$3>2mf*z@^}y)7k&;en=wnTXb0~T zU%t>QQ}*lW6SjT=A-cAD{Q?I2u)qt=_p(qUT2vw$!CT;Q;6oVxd>EZ}@-O-`;aJek zbYqH3F9;VA2i37UFMQ{JQH!E&R~n=md6-51mkN{yM{0A?W%6Q-Nthr+CEv_wKT+P# zsGq!<Jxp4gqRYdiFxtnNPMXxq==89ehQj{7Av8Cc=ceAGRfqR!6a-o%&%~ ziDP5a!@FtQRBy&3-_GO5gi|t!i6>?h5+-ZwnaKQHvJUwj1a{>%r5TIqis7QsbER7L zbx60hV9Sme-r|RaJo6niG0ueF+LEgnn2Ay<2s(?kAPr^|X&XliwaMyz9?Z(UH;neP z+yvcG49;-55`Mf)?sKUO)4dky@j*i{Zd9QF#I=>{0Z4F#aj=^iQV?{)O7HBy_4b_| zbS#%@1C6P4&mqBi8dKq2Q%~c02`UBS`1k}laW-eBn_@C&XR(gkl}!(nhYyWvf7(#e ziP_wdQ4mgv=v(6_ff`{Cu{L}!1BFC<6YIJ#)`(LC8}XXXJNzFc2}&E5xuH+K19|SK za0Pwk(P;xG`zs+uv>J6V%u+~{y;!YNs3N7M=u|$#2b46LS#UcX_Ne=8Qq&=*>8Ff zBZmyuihp6_`*5SWZ5{*c*+;gnd&j!+JZmOQRvuQ0pYz0@)pO(+7EEHa=9F1J(GxDu z4S*GTsA!`u?|WH{XB+vQ@UL6aQ6s`&gERf`**WmwnmE^EJW*ovf1kTajpv>T7;PaJ zz}X3Cdv}So?TgCeOs8kH#%9CH{<+fz!d#Jh2{&VOvn1E3^F`|yh0mv2^HZE4%8HL` z1vR5H*NJrD$Is}pV5zD9RcE!8G*I&)0J zcD@BqQAGp$M>JVi9jFlu-vEmGZ#LUU?UaqpODTwj6*&!orQCLy)9prea25_ zhvn;8&Z9>UK)X;Fs=nuKUsW$G)lJW`Ul8+kfsi_}#;%+pi9H&{Yzcf4G<4fUm|8Ze zom^(-(>>AAO_598TG_3Udk9Cv8KT*(Ouc({W9Mk?!irE$#qX4a#Eo;tnNSMrX&c(7 zyUS0AWr;zszlI>mEg zY`ZwB`a9*I?CX48ghcjm?uR)gv@r<}n7duQb)n#m&aJV#?k**G3q*642QlxI#WJDy zcto~}6K-Jgn~4Bi2I{F#2Gj0Z1aQSFWR^}V-I_WBrikv$nv8@t+1=wo2-;0c;bn94 z6&FE?y(?r-{)<+dKY|3M@z(5)o_}<8SOaZU}1qeIMTub#PlMai1jipAkk_p+Fej;Q=}3q2Pcf15WMu-vlDwFs@@k!~ zmjcA)H19-kF15Y8N_vgVdP~f9F3pBeq2Ev6+`>O%kS;n^MOD<^*oFk{4jJkCxLRf& z>ZJ1(fU^X~L(}p2JKzvqyg>F$O!u|I@h&aJ==7iGqYuHox|R3u%C^Ej8}{m2<3mlx z^+wi)d>TlTox`_+tt zg5CX7N|~uwvLBGP&k?JdhB_qs8ExsC70iHs=kQRZv^Vl$IR;8h;b(bQ$Ab*L`;MR% zAlVb;^4=d-?xS{D$v1%yZapLn3oc5mseY{ZSrcjqyz1HP`tq#|xF=A}XHk)iWMOG; zHPa*tyKVHzVS28iy^Vi!_{kyjRj|~_Dt;%Y0m0|`nYly!KfbjZ$|j4Zn*gP`zUMN% zNqZYpKFre-v|)*EXm{xzFS@L}<$V&&cga~~#AMRKih-Ize{1Khczg636a6R- zZW8|a#f=xl#)ceARpfC^Gd!?8FOKH>9HUr#iq}!74$W1~mC@8LbTM;D6Gc8g5`8M4 z&#eht%E6dZDQTE1`qjp%jh?hTzrsf%_ZSlEcJf8Y5-X_RIR8cCve1x5S+wv>JUL_i zZP1@_LA`{W<6h5A;M(2=s?k-Dy*{GWF4^)9Xr>b9nsv+0>t!3|y`{8BHxd%MoK8k$ z$u6i{X51(?#~>8ngHtC5UjA}%x_x0$o|R@ebbO&~i`h|XAr^Q#Dg%7gK}(9VT2+N4 zi0V>KYrx3RC5ypZ*qx0xSGNEehd;#!_t7%ll1vhE;R4GKh!*^ikd#mq^aPl8m4X2@ zVxuKp_{ketSMql77+0bBfv#o)cC2k6Iw~;KA7_BqNESR(-S6$mT&RD=!GY<=tCq3Ba=OioKSKv1BfIXI zHqym0$e(rpn}t1*Tg|iFMhjsSRgTW5BZ(Lam(7xsD@a;Uqis}(xTr8|MWUS7@JOC@ z%s|^q4SX;gGF#XL!oUq~3aTw$HM{DNZ zglnrLo!vMSdH|fr-K$$4=8!}tF{&$mryhaE4#hs;XpnfgM5bJQdMKJTG|Mu1CeYzZ zB4JG-S6P(64NJt~DI1G!XeKnvCH{4?B+tb*u@`0; zKKI<(k4W$9ugzQNVGOCH5F`($d#ZYqNm9Lgf4{nMSn4y$zF6yI?=BOfD)B3Kp<+_x z%=YoR5DiPR_+J){l_&oaBDE17WeU#Y5bz!^-iad^ExC*?SWYX4IH+ek*HDM9FU;J| z@e4Ih?XY5EG3_m?1lb_dY?Lf9rlXJfaSX+O-J-bZ=Dz^j$IU_~_$32G|EiklMZ05B zFZ03px6+|&-@n7jZg?jH59=qXmIP5I$(giR_zt-|=&|}v6BiOsuV#F*-Q*AdEZydP zoO04{_Ca0;c!a;?cpipy#{XsQD|R^A4F36X@rK#?N3SZb52*Vc%m`7Ae7mBGBrx!C;pMOxQ2J3wNdlqF20F|txYSHqkI=8>E|Q)gA$gTKJq z^#fyKN=>vGSJ1RBG1;0XIay)6#-wdGDC+^4jT>bvyX!oEVE30;s zLv!pgF-l4-I>*8e&^J1G*D%Yn^rq&tc8Q?}r|=ACj{gQC_kVXmZmVZ% zFG)Zji^rc>$hnD_@i?JU5jl~X;uBdtMm0f;Ajtrhqa4+-yB|EqP!o;WKR`W?ceBcr z5o5^iGwYr)hZ>BwIx`9gK?Ui!0%vgjwP}vvKQdz+Ns*|?{49`0r^eM^O;*8s25_m_ z8ShV%y;&q>*+5Xm(_UM~y;I5I;ZEDq7Kn3hPUCgWJ8jm}GA4WY9ngkC10 z!nlFfpDO(i1R_jvPBk$RJ zY4bK!pbhJ<|Cwq$TFU5a(5Pxm+%Yj^UlH%b-Jr-TjJ0W!4V`fT8}XvYWPdP7fgWjn zomm7l(k6Wd`a90z$u{6}RxPcme^!e-IaJnjai3FZLLUCL2pTQIBO2%A3H>L*YK1fo zF01wnV_C2m$pTgTXJh;5E`55O*8XnO?#t`=#c@Y|SBV(t&(^Mgjq2}xt(SXq%&S3S z;y>^5xA}1J@iRYUVgPfGRBu%E{{WLfY`@yl$9U-jEj8fK*2Va&)seJSd%faqr5ql4 z4l(LArw?apSr>HkN6RDY$oa26bkYd}cKp^0)g5Ql-F?>9+l8|7X<~%8-4=3VN<)HM z&SIfim;;l@D}kM9w@D70PDZ07^VjGXrP1ky_iPzk?T+o#l$|x_Pg`oeQe42Is?5$H zL}fddYcC_>$?kAFb|XJQppJL$jm~KE6<<50dt0Y$G{*`)T~-lQ_IJ9~-b}XJr83*t z?uvP;RyTv>%Oj~MrvrMDKq^Y}je6Lh1CMvV-;cUklE)-}61DhE)7QTay1MHHMF}mS+C(j1d%xv?t2QZJ9L^G^BI=lFF z*L3S;s;RYJ(rKz~*68R=)dDGUy9A7zCqksDU5A0l^cTzj0LNgMyKQy47wi12soxZv zqj&^){gOHZre^8LdWy}|a6@U9=_Ne0=~XB=k~byebynRQZE`^)f*2jS$bvmwCDu;h zd8GZ+k6fhxK-Z4Z5B~r& z;26Al`2f8n#m7(aAJx$_;h)+qFJ3v=Iv(vsRF)g8Mv66-pJiJ+5h5cn?!WB{uqBYO z0k)iL*b?gm57~KV>C9m7Ke+jNpx!1yEIut$J<`c9*BGci@?oZujyI&8lA@%%n3xR4q@p-S zW;q-1FdO1tG_gk*c|YDy%k!;)4fmE^i?y|-l(Z=5Hg4(nw=t%_b>#R zBS5O4k(~3QT4i|>lY#0h_fs=c=wKUeiSbnXg*%Jb#I%(+1l(k584940TLdGi!)1B+ zaC`UE6bLQO-xK^4u5TjhXCRPZ?#Ti$ay>`Wr`Y(bEuR~#kCCD5p?(#3_tZ5VXA05OUZAacViB8s z##Dt6llT}NqYOc1BjGq@d;zTP?rjIgGSTjWvn+aDTMKq%j|%(wd9K-`w_j^6)ty;M zd$@I7(I64prKGq)K{}AF@KjQi5YD-7;{<`*JdaECXKpxG%#Ll6Y~c51@k>u z`hQa#j+u@xHH?OlA_Fe7h!p+n-Z#2+hUC&QoO{<3#lOIxKruQ zt<5YQ<>`e1{{YCF^H~vpksE7J;!l_YzCzI3r-0rbb=4TEtLW%$Q_Y{=P?JY5$*_c> z&wf|eQJEbesVUx~{{X+bxelU64b8Fd=03|V>aVcPMw!KLtJNpDItVmOt@b zLSfUt6kF(T?0=P_-q4Z1t3J5=e+we&&$Cxv^lC{hRgvqc(>bcPpS?yg>UMlX{v{fK z=^eZMq8;`>L;EcT>mqpgvG2)e{{VJqn4{o@;g=o0zN6`?k7J{D*9ty(R+{F#TTO)U zgakW4Bl+k9lpH)0u*5d<_`&RWBU0XQq1phlpGf89RmMJ=ZkY-)4@9D_ImaW@oE zjJ7gx!vuexsyU#X6?LwzLXHYla6Sgd;C?fx3tY@nupS(fB{G)7Ap_IbI*d5*yB$qBi&dJ<*8^DT)L4eYGh6eWP6TvAZ~a^chyc> zFhDrmG3}j7)0`C<&nqzT!l&-`>va&|%Iz})>Hxt0Wa^&2L9jG0eg2Nq-l9o};8)dm z>cVl#{--*>OXfD55qksQ&}fClIyr2asQTGF!XnCTfMlwA#Z^0BZtf3$1{Y#UNC<%9c z2s#dKEiH=+k1iC=OMkml>`Xya>JfOrKM?*$RgV>F+W{i!EfKby!;$hMQIe$%vBGdh z~d5RI}iHk z`jysv6QZGjEb$f|#Ai_uIu#|JKwcT+m4Il9W1NB8K!Oh_TTdZQw^234_Qf2EgNK<@ za6#|f56e2MsGqrEB!zAWOwJoZ#rmVePO_+4gC%W>vNdTKQb|uE`Jk~okHi2cjyczr z{{YlPWS0U@jf{FPbl5Nebq@;u)*sb1x9fCM(^XN>tP>574KuQq0kg*8@bXXL=f9?} z>GiN`jmOCM@1n0ZhDJAa-T5ki-7mLV=%k9D?jmA5wT43!M309(!1Tc%le{w?9eA;YXIZ0>1qQ6%wT4>Aap1|RP5GCjwC zUwu50GgD5wBdG9I1k&lSekPwEcv57Ki27@# z-wU?h;nU4Y36E0J>ZNV)Q>P=l^-PZ}VUieHOtfK<2pkih+?)@dH10cy1o_&oTivzq zZ;<{`TaSa?RpU2{E7QF{YM$kCc*3fzsw0Xq$&yJ)+ZuvCJ93YBbpJDEWQ z9x^qt9+D=5at+=96aN59&CA-**Z>;w;bpd;1G=`?)lX!Kig9wMcm1PhhGMlen;T&h zCdTrMXDY-pvXF9pD9fi|aWes9ulLno{{T2PF!o z66Gq(4~XnB6tuH85s*-u)Bgf`seJcMcbNqEz*tSr~yi@@E($;>s@cU zQ_}SP%9%?{mP(bVq*ca5j}(A99vMhnZDj`?fzoUms7YvZWUhK2rP2pgqt(VHiLPKc zM|?i}J@g+?S)s0`mg_YgT#Av(E&!N`P&S;Y$;l*c80Q0xRMZLEPAzE%z~}i>vS7#4 zF2-`P4;lKVzowdR52)*FOg6iuvQ?{%kjn9bkhqU=%Lv=?dTziZX*8N@E_1j(@)X*g zSe4Ciua>m`fTAxWUFTK=%KeFjApSRh% z_QwQ}LHh%!;ese&twLB-)&K{WIqs;+Sge^iY!1zVVeqne_M8$uDE!cFqHR2cKOhr+ zo`{9l#b5LvtnBN2vDN<46rrQ8rF9!+WD6shNXTX@wV4aEFe4etaXnVd5`Hb**ip<$I-`y0YCK_G&;`tMzpo}i0hK& zZ-fMNGVaM&lLLp1PXlQrZS@0B*)U5C?vHRMkaP44b6=@s&b|wUc%^c=Nej}_E6*rdB0(CemL#NP62LL|ik{iyjbpw>27qzz?6x7(xY%xS z?+BlSFjN?NzR6WNin=RhM3m1?Oa0;ezcXVl?W_*d+!M*|p%bDHsRe_J;)mkdTBd1g=k-42eg-I)n5PO5^rfX=BPt8+ww$RWse0r(% zVuJ6|lhs5~-Uvl~EJ^~@AB?P>$3zPTQ z&{MKV2?dab+D3W(LHXB`U~6#vU-G4Hn1E=lx0LAdY;Xk#z?yh$M(DpaBecMJw4ZyEmKm$L`q3Yyig3{HpU!#V4iS0Wb4s0P~FSO z9Xpsw94y9P22oqAX{o4a`irM-Oe$#Y^;c?`=Z0)HT~K^4Al>{x6M=?0_HlN+Pwvj` zt{6WHXphY=GcRSira%}R!@wl-^{e&ztwW?MdaI^-MhPzUQr1_+Pa6p8?dt%6RdCU~ z@G<1z5X`3@y2sYZ1EeMf(7zkMkyhYtq8P^WXUq(FTTkp!4x{P*i}4SLoO4-S%$RnM$esp?z6?k@-j1KYQuCqfv7BPOHdaRSI zyd>(+4;h;O0Arq#pE&#EU`tNl{*^Mnt`4dl9Gt{MhnGJw&)I2>TyFhX_5MZ*tRm+d zct1`xR*2+a=tVn zc>n|T@2(C34Ys38N*#DyACdlAYbOh~j;aiF&W=a1{w$C1)9nauaN#bZj83~`LC*l= zI*7I13Y0qVRa)=Sk<^xSb4@5vvYtOnn*sXXmCR~Q6aRFD)OP&m_3~}P2_O~5bM1{v(bOa-#W;kpCjgb_f`6W+Z6&iy4Dp(=?mcB4 z5*@}AikTNZ-H-XO)<(DDcw+kh0Qy!jr_efgb+hzeqHB4icSVJoq`rUrGM63jc^^G& zmGJvai`>&m5RQw>QuuK!qk69#g-}U7#tu7o(^rb*MtY-6yPh?HJMOBLV;@{{PwAnK zi;tRzEzzDeXj*Eee&29|BN@jsIl%4hq6doXpMT1WT~g82G)*lEjC>}tStgX_M;=n1 z2q)9O%RnyR^!%q14YtD5v&^z^-fk$;Fb{M5T<7!C6PiWX=kJ95n${$YwH-wq0b5~u zNDQ(A!N-{)@AJo~(zy7p9$$n}EfkM2X>(gL0N?8pnF{+IxxoE2w_GknV@;svIM*%~ zmJc2H*CwR!+P|G4R9(@SY-Gqgpu0rv8!ks_j1nw|C{{RZg z;D7BS{o5awM$J<`3fyEH`l|FY#6~yc*k@5wxur26V5v#w06e)#&!*simXjQnAiwyO z=b2Cd8A3i|zJc4y?}%V8jP6Jc8aLO0q5v7S#GeF~WOjCXa2wf3<3L#$D0DUw)=F@g z#L=ARInX<;Fxy$Mor*Y|s1u%jTmJxELnNKsOUU6(8w8FGY2`=0T^f=DB0rTH;wX;q z#6F(q>93h0lAGHL9@>P*lq}Mo4OD^N_nv<@x;nx`B^*VJ=FLlCMs z!ym4_2XZDf1{8A2AZ?VI8}+sw@>N9gRFoKKMiD=r<|F6^t_?FxMWm_&Ug0P$9}2P3 zUhVga6?!>RbOf&SfPV(v-Ti)A)YEGm z!A{Dpsna%4Knp&r2G_hSUQ+iyVaL}R^bIZ4G&`#EWvKZF=McuCy(4`$s%Hbl& z0VS3w)m39TB;XR~^Uk1>Ugu86MmE{X!|hP{H9H<`0fo3z_@Zf~TuKNW0o#p0WNR7= zdHbwB?Zncn^}kusKTtzmPKilhTM&YxvJe(&Jg`hbk9OJ_g6DyNF|6H2yiX$|cThsT z2Ar{uAp7!IKUQB}*+Wbwky5&nR!HXqB1L1J*&G};4nMp`u%|Oe9C5f?m~ebYksolg z+W?Ml_XomPDAjmX)_r-^bwbx~maa)9JL_6sEfXgNnb?dDIUVpj9eTYS;z<4`kbJ=( zOfPhGnr)2{AB1$J)1Djne?#EChTl_ixIO0i9A=IQiYf(GJG_Vi8;HpKPDvOC;R8|~ zK`#T34yWIe&YpMbFTruQi}oAcH;k9b`qtYep0>JgzZfbQ5U|Pq#e*p!muLeZp2yo- zjAwNi4a%z%nJG4eS!cA--HJ^XVhy#LoUaS=KgG|_PS;&puy+iFp;)?*#ZwwvYbt`* zG^rZy+{}u=ZEy(P{JUff<59fC$GaFA{{W?<5qqI)iTJ(yrEKwI#EW%Bvfm9Xm5F$^ zS4@jTAQDAQPPiypB}0^Nh=H8pxddS8*zqF+nmob$DmsR>uN#e?quqsO=<7vA^P;K5 z^4OuJN(Pu2D~xr8ow-DW?y~bDI54uDhN-fpa>dFUA=i zQbi<(R%RqNejstTInDvo!q>)myFMSVQOzSH!!cO*Yo=~}bJKnu^rZ9jrLM8)>+VfY zaiLpyA9IQ#p_wJ~PU!(`BDour;sr|;U5VzV>|;ecpXEV)RmEj|1w6bf1nf zS~|kt)8Bi7TUB=Ex79^n$tZ=gQD>)=1`Kh4kCTuw+-J_evo?}gaC4A<>3SCG8zg(G zi08xA37suB+w3s4D@2k^%TYobV#WK-ry1pPIpkywcx_Xh+4j6st$H}@N4=+_>1jGu zEcBFgmiZ)}m?rRLIbcs4PtX83*QwIP11+6biPy|sAl=bjx(lQ$sc%$ORn#jb8Af0w z0g$l#Adh_WgRSj6FN}wk=cRCV0@80b7;YATl$2>_yi*ggR0NKpGxFmN-A|#&I?;F! zu;ok|L%L}ynt5rjROH7AR`Mq7=eWr1K;Zjovv)>*CC-Vm3Z-ql^-VQ5kvm8XEW$FN zV`&_W`fAU5*0$6xjJeAqJ`PjO)EB$$s_9~`sksK-Z@%>WQqsDy$yq$6Qd^vPfNmqc zb?D>mcDgo3@Znur<9{o^rwa-Fnp-2~X%>1G0zP$LUVK%r!rE2oNE(uUv8uj_DV8~? zTD>Z1XvppoHy&!okAxN^V*r3gbx&8Sk5KDmVWb{L3H7XfI|JCii98Dfxa)zOAR54H~#?bnmz`H(C~hA zRrYEL{YUBibM&$1vUw<{ibsjdB#7HtS#Ux2Ao35$>Z{{1ov&>lHF2)oEr!!rm^K4v z>T#r|qhu7Ml29{_-hgLHT8MEOUo`n7IubsbVA@T*^$EEu0{W5xWP+HE8 zicI-(6k-lfx6@D-=IL2%o@f-8`q^C&ZU8)8+&pNiyDN;6^j-FVy;DheCIpTPSV1n3eO81$ldqir0JjT| z$dAf$o}ntX?4Fs)^fMqhA5*B~?JWzxOs&6FasL46{?!>UAF^^vib_@=c&eq39l(9Z z`Da}2*3m>WBC#L(TeT+t0QkW5AS^b5neTo%TWP8kWv@tFWD&I+f95cm`f8tgH4-)L zHV;+wDdIhw*pkP=4X>*G1(sDv<)^7M)Q`0JKX|C~rOrS0N-@Sc#=T2u;4jbR!0>-` z{XgDnSq5sQgpG{eNTh}}J=uSWy+2c~un~2ezrXOAwnI-ujTm!lwH9@tlBm@t$%soM-{#{2_h^ z_fl^3WX7x~8J>?LxjAY}It?;)$A{686s?Tmy#&(_-D-?=w zb{ua>fQc&8?49rEoq7wz{09i3waRsz2ff zU3HpAk6N9jm5kw8t4qH1-YeJ zsjZs1;H8>LBp?mOMkgM)9@@Pj4QX%-8WvoQYmP$Fx^mwYw$)Dzbud&%A}sC%Zmk=i z#TyqL%&>DqM?Z*NR{@mme6#~U@(SN3&6*G|JL94;Fl zK0n7A^BS2YV@P@XEcqf3jn5RL-Fk(t*BV-i%bP}!F>y&qq1}(B=?TCX&j25;yyUv* zA#in)N4%`*HBPbJg#FXRJ9U!vM@v&uwu)Kl{J|{GB!Ee~7-j(NKRyT3TRI-lcBtJ_ zpGfD(9M>GHPx^Cjo|6#XR{2o)WP+Y^Dx3}rIrvBOKP`Gk%|n|iaKm}y-->$GYNnp| zY`orSQcAF_AoEE+N}d~F&%#I}C%G8bcA(Fob7U?b81VP%r4tEQ=$p}V$J@rtx8D+a ze(g^1q*2;M6GV(r&dFG*9AkkdF_Haswb_!HTJZD1^H9Xyye?`^k*<;a*qNlI^^z4Y zBA-mIGx}#+7Ty)s?lwkIWw9+a+Qn|AieXtTT0>MPev+fPnexjCJ5Q&NzoM{JRM_qvZCY*>>1LocysU9~ud@Lg;v0Z*jO{08cXBdt?GZKm zPwDdEKs)JJWUFynRkDF7Z1G&{lJfDINZyWunl^}3z{Z~3w26VD|vFDIo?SnmSDi`Q}8l^ z2+j^$+1D_Y@qy68b6h7`y_aSUE%%JulAY>R_YsFEmZV^qGK9Oz6K2&jDl5& zhLgu@vC#VAb z=f!08eF1E^Br^29U0pr)FdjQye9_F6kGPTdd6X)W6pw+1N&F<|0*sRl+gvf?KY!&~ zYz4IVPJBPLTdttFSt_Pmyfe{MfJ4P9l#_5gsZ)Zz^g#uqX9_}J8;NqVczfbQ^nX=Z z?o|-YTqr6x3Ru`UU{R9|yN=Dk&(}ep>^r<*v*v`aI;i`n8Xh4^vsj_I_^(4rYNAVR zHO_e^f{s$tM^3*RDhJNz+Mp>XlZ^M$f0F51`2fhY$4=M_H~Dm1?USdfdX~>qWu58P zxd)giu5qq22{Pf}Ohq?)pl zu~JCuWGvOR+Z9qoVxTh)0bFtcKUedW{OtE+O#6OubS?WnIs7k4}iwMi}e2h(^TE7 zbBDyHY%Q!R_5T2=BYH9^CpK{CCp{OaA~*2^`I#J8Ztx(lV8hkUn~y zt|3+}S6eq<%C5e1Z*T3e_`hEIw#kcCcg4FX+!uMPI%4HX1x)nS%(WkMG%k%Sc_pF| z89p7ufCF*PGwZD_H0&}0`m5{5&a$(g3xixQ$B*0HEKNL7>uWDf$7{LuHTK)qk8o2* zM|H7ATB0)QyB4K}zz%Xl1GMqG1RZ-LqteMCd~vkLSs~nfxn6I-o;GZfM+e>kX#W8F zd!@4V4UeXLNTQ1v^Vrsw@ipe!|rK11-y{{RUG`?`q7 z6Y@R-=aKc~eEzFeElDPhe?LwS%RWmJtF}~7RLf6OOD#Q2ELKRRO|KdDAmESBRU~7h zcGqLy-{h{f!R@h>!KYJ=!H|*Zq`cuZ0vC@$l8g_C50K8KYqF5E_^8ubdmMrD)S%f> zmY!61w@DGokbST_>5=c4R z6Yz*e7aLEw7}SBJ6otTyp`A%^suhq?(iMUwj6Aa{<;gvlp4@wTjd?!V)aoISGB?nr z^Paslo(J4p#xBD@UKcv4^pB^ z#Xxh|_xX)355iQVI8B*X>oOF757Ln5*N z0O{AS>{$cY1!NMuhx)8+?8y&}54!J4e?Fa^5Hu`TbH{6=+5tcN=Hut-uc6n9{NI|d z=q9#CwI(w3Ydf!(ipqzpm4Q9BFTw_K{{UlMctg|qPSjD=igMKtGsU>aP?OL1I0B9` zss}1h11Ftz4|!aC`14KD)65mV;^Aip1NK!3l&*aYvS1!Pf3ATZKa|49ABy~`HBcl} z$|o=wMi~CmnW|kSIpCs_G3+yqcc0-Ys{{K|D&m?pm`6`@YJI^xx@JmvZ~z;F3?06_ zYrRT7sxr$(1#2ZSqYMcGNGW8kl;HL*cn$jNg_Dora5et`U%Hc3YnFF~r&($uV8`y) z8ij!a1H%~5d|U!}=UvAf{a1hX`;+0o_t2%libi~} z4tt+bp-Gp!atFhMpomYxqt{2tau3)2ITup&y!6ckbGydzfX4(9I3wqrX&x+EUJBzl zp;Pd*xOMng6Y4lVp8B9{8d{B_F4TbwKW9Qcj)vd@A-z|^m4G>6-?E)?a9kQn_)y5h zI4lQXan6)J)9gGiMJ&sVCel5IyL(PoYaL2j}aqiK0BUMJwMS+U10Or#?HSy+WytGF5|T zc6;D^YiF};z2qSg+g7t%WuB(<_exqw9;$kWSRsZWOFWFhRRHxQkOrK%HbS)s8*;Or zthCfvy1K5M)5}jiJgkot$%aW3;n1&eLjnl&*5;N$Bv!oUq9;c#=Ji%O8|b*nRZ{Ew zJVODHQ?VpSK>(@@cjTUU03LNhXxjHvM^&pEjBE~+s`byR7Ny<1!XRGVzAX>&U36D} zl+C-32|XuuYKzS~BjF!>b0;7w3=z1GL;SU(AZu7b7P1bjy_Wdsw0jj?bysSs;iQge zVpnGU+RG#=I03SAwU72kar4whQ4~NC^WyDU7GxuKJQXpfJwbGyYQ}uC5f~!{6@Ffy zJtm;KIQzlMpG?-d!GnZG%93=2`kJ~ID8v}fSC&TL{eS7KeM=p!kGPl-YAtjTz9Ioy z~t6dZ` zQkN>N9JrFDN8%_B0|0pW{PlVu9jGR0EDoI&3Fq$Ju>+sPO0KEv54zINS8b=Xz|@jc z%&I?nUpH7itH!!hu(RfwTtJOA6qN<+S zCXSk>lH(rtgL%Tru9yWg!T3S-=U#_Xpo_cT6KM0}j~@~BrK{PUwJg(jGJ$_j>2udp zS60UcB8pW>DWisIkTRAC!LrIw0mjpjocGTsSmZUV4UFUO@9e!Ow?A{nRllqHa*L{Z zA|9r+eb4rVJuGiOP$6i-o5D06_NIl$9p?tnfyqlLez=w<_SPWH%t^xzymfz;1P_ zPTJhCV%2t&s;U~DW2*~QOoU=GJ}8IwkosfWx6@IW#&gfjJ9Pw$BaaL%O5fs2^6Cu_&bF*F?glD3> zB7&{kPCSp?bV0f?1B=PF z^#0M9TGdZ*N4QeM1k}wyu|8cs11&-AmC_m6Y z?QYy2M!gPheNcH0 z%MGn?Siu{U`Du3At$hmk(Mbv9ewym=UF1}{TA8WFRgMR~y31;kP%6`C>e(XM9KcGc z*hm4#(4OCxtw%X;3J1groR;|R_XvEMX{jxW!dIk4W_ctBA+XtvaDA6LwjLgAm6;}- zgU}CuB?HB7z8)P}X|5918v2Kn?4f$PdMPGF<;pWe+sGLB00ie-8a*4eZY>6fjT7~y zPgOY6vdOdHNEh}$MO?r2jialpDS!J!=?JXdEpesutsPHru8tYO+O6f8lPV4vmCKbO zc^=lk#Vm}r2k#*KOsgKWX!_L@&DO}*HS)J>95CYS`dG2)c083MtbWY5ifUK6bal=? zskW68tsx4viieO?DcLB&9}*Feat5;<91l1f4}s(B$n zs-d1psws?8MCBw#8(A10K?jla)px{LcE1H)WSfPIh3eO?aM3XNk4*sHpswHI7t%B* zvt!%SP5~UH-riFMB9INN2h9Hf*wLanM{`a=UYLg?l0E+b7EYlcjH#M!efL<@pF{1p z8X&lGgM)7i!D{=!01=) zS1jArIZsD3%@k1((n#uyxq$eI01wpq>TiGxNlrLW`opKL78|FPaHcfX4+_W>ka)>o zPnwah1^%Ff$2(;_5We63lW})v_ip8`D0fMH(R#jsToNguB(RE6l$Ei<5%cGdm*uZH zrgMAH;#Z>WgEKS_Fmw3xT|=8UTlB~5TctA3X^;kuU<(`4z6cURK*BiWcERK@BVN-_ zsi0$!ZfWEd@pezB(a-)wE~k&bk?~y9;va|C9}e##j;xBBIe~gQ_?K*TC_C}|E;2VK zar06M^L?Y){{Z5i_9Jng4~LQWmG%C~?O3}`E$zdHBg6XhRLRo>sUT;!vDN{%xKi|C z-bsTWz^$Izh?fFItG8m&zK@`SPN=7>k+D5BJmL}PcZChl&}a46r0glGV;Slfi2nds zhr6>tZvn#BT$$=(61?)Pld&n}+^hGp4l)EWFFbpX(_c{-Uz})Ne)sr9u_QwfM^{fw z#Dq*oK@bDD$@tuUTI)oPhmWFeB7#_{;bBQv84x+6f#)iVjiIEFei6yfAm?3@Ehs(< z>!gA@a<@4njZt^SCC+li^5b@6ivw;kjC%fh>&_QfQ}<879L6}Fg{PG~!lElxI?~6H z+?IT?9G{WKyVvjiBiA3oRaCT*0X&OCNj*t@%T+5?JElM(QBDcSz_K zMY=&7R!oZ9Q_k5%6AS_v;|+}Deq4H+#Gzgp%Mv35;3$LF=4F3RK+N9%3>_){Z z((a69Tzx<~Oev_264GMbhJJZJ<)kAjL6qXu$en>F{p|jF1O>p9pxxU}Lrv@_(+rb*P_rzy(tG@v*i__opuvmSb5zog|^o_ClcW2W)Uh zwyUyNFxUyDV`KmvDi4lx{@t?BSDGN7cbj`{%A7DfXP=kLjc)dIjx+IsI8=;#ELzFn zsz;$%i!ls7F^mp74}X1HnSeO$T5KW1JRsLPxadKY#%f$=DyNb?btG+bgbP(8ag&$YGjFLtHKp{-#-fdae_$o)piZV#PQSjrJ(6H?HnGp z`>O|s5Kvxi7wd%CdAz1UDUfc60x^JpwcFb#ldOGf-Xv@+z&#eenV_?OR3^Lc)pWEp zuvODd3`zqmphqktJOF)9`=>uIU1)A#6yZr_)0YbX5%fi_)dr%5mW-w@OcfHGPliB7 zGRHU{ndeq+HxGrsWsu!A$u88(6fkt%>Y9@0AxddVyH-OHXLxq7W8O5;{rg97_tsFrj!k1aPHT z1_!tr^8MVA3y?1rGY;|(D3?;&I&$!)<56sYUQ8wht||?MCs~mzs!31{X9ch`O z*0eq-fnn=aRp_pfB+dT-aNsMohsCSJoffukq?VEhD%;A0)z-6+3}sAsY=#HE!f*)V z+g0KJ08!7`Y?cT?m{v2E{Of?Q9|TOCOAx)jSVCxgB(=eS$9ArIQX{!6AU=>2>^Oy!yuMo zLHHbS6qD*?ikf-r@6B@`LOe8`PSiI;cBZN&qcth;nrZMvivIxFU`r_{BR?%<_O_!{ zqjva1PToP}k1yJlskKrF8t5D=!^h;Jc#V0u^!=8Lr7kp7F;LoWGEGla6*-Ps!^VXj z&UU}RMh-V^;N&RQ;nh1?rH;i11o|HAKm4Yg_$rrD-jlCw^yyP88^t6-7!CnhBV3hD z#Ec9U$G2|BRCMsWPD^=d_rlxMIg_v!ig5ZM-9OO1L)5m%md(@GTfz*I^fWV0ss|*J zibk!vXxQzpcn8A5%iaTMD^G$R9(6BUb)-=A)N>1!B7NkoD~~+HtOK$~kkakQB>*QK z#xiwZSm^d_k-M*_kI#UY{K29EU~|&CttY6wIO<-My3W@dJx#u>&Z|#&rK6rII=9Y2 zZ-@6xayU5WBRb1|mFyPFb36?aKKw1c7jEfwEvJyjAD7q77r%8}olAA4qw09!qUy>j zG^K{7qvC?9HQlwOZsI_=UoZj+s_wxA>M|cJAOLH}jCy!2NW@{*#{v&e;bwjzSnu?8 zsP`rnH42$6O(>OO82lxa01gP?XCRCcHKzmu3k_|0{>q&5cc4-FbXLxc`!(Epl9JzD zP1W{lJ9DugeXBA<4K#%}Aa=n4T!Op1DIlB!ktBNP9t))SKs0W#{3vGZ_ri9!;ymGW z2ZfeCr0{pA*`eyHVUp3LCY7hBS97!mi-96G;uv=$wg?)g?PL>3(-}PY^X8BEuPxf< z^yKtc3wPOC!PeDPmkVE1BsMFZKsP&8M^4766+*7rObjF;SQ4ZaAP_dUE6(h5#9C~d z^&iGPRxYAhp?KWUU}MkM$L6(Ye#sX901E2i>n{yzu4AYo0t%W+b%vQLqG7e0$xq!m z;4xA^gffuB26l*~AME4j&&^Xh<3GWvXg+;M%qs^(*>9JFUsu+&?fW21R*uzK0>tvh zR2dq7ERe%HfKWzzg>*lu^>w0&sDe2?u z3Mw9_niJ)j+SMF50k~++=8_j4g&Yr-q5lBagk-Q@G30$!L)nI{ydM=lYCFxQvWk}5 z;heYIboDI{?MQl!BijchXJmEC<$Vr0<2;RR#!K2B14DlQ07}outhZ^QXg2e>bpG!j zFtn;pkhLuzmT3sxg7DR0&QGB$>N|0(Aco)tM`eW%sCqBFw0ct{GjU!w&og z9kcTUYmGU(NkeHF04hIIbR^w7K?P5n=Z+SkRdMA%p$FAZBR}V=uY@(W)z)}&2TfZg zGh{L2RoePQvekl??@e-vAh=UOTN1E!N|;hmtUC}G935ywseD;!Bhb>Q_<_YBbe@uw5&mgB%Q%{Be%<3_LinV(Y)iR!TFx6Bm79Cdx(#p zLOzF&lC;y^Drj1cj@@Q4wKFU;Ei{P~G;5E9s63S`%78m?a6#2F^5dM9V#ubozkx|T zYtVf$;({>0QvKfRv&)M0Nrjnl_;=%p2m9Ie#x*NmTHTzt!2aDwnX6VdCP=bUgn6eyc(`+Uz?Yg+Md> zBzHLHaK<$TRgw~MpW#;+&j%yJ~m<(#krXC7uRQxO{Ru5%ky286`XtzSkUK%^<6{ zgPto@)6AB;#M`E3+YM8mCHX)1jsVZ`aC?q-;M5D+Prp_34&Uq(2|A59_P1e&&(=>p9;7OwE7QyVK`9^Jr}Br;svTzmb_BdQ_e^pTj1l2j?Mgu)vY9k3EMB9 zyruZRH5Y{) zJ94xNDXY4rgkEK2tQIg0-&rZkWwep!`r+-W7jhc?qG8B56N3q)Hk}HcW zkKLVjFV`AE+5nFqfA+Y^-n(m>vR=39b6e8VP}WsPSv-3;RWS-l-;!`R*4B(XOdbUU zlSbCGk;13hE>K%)A+4UCj^R%WZhA_>{#?T!g@kUekM~$}j&-Fpjon*qN*Z?HszvVW z62ig0A-gJ|Ozk1$lj-TLb~H3lURqfUuP!OIQpYFs zI%ORxiko2o0h6Q|Ia2Tu#F_6kQdCu`c5#+5HaW&UbEE*Zv=xoT=W!M2td^>VuvshU zWksfWof#%^@r-;}KHm8C`D&x$63Qj;T9CcbMX}iM{?O;@S=5!Mc@>?*G>`EN=Yx;) z)sF7#7(>4~0P?jD4E#Il&k|}Rsp;;EyI-n66tdP)!0!^{u`V!*ep&R@h%{5aF+c(4 zLZPYc4JP=8B>q1yBbPK{il%iJmsg zlBGrnAQ6{5PIxSKYwh<}OmNMYOQu!Q&P(+)3j`?B$-15U~!N$&jgQs zc}U$Ods9gQA1HThW+mOf$z0b9S>y7SgnFApZ^3-3l&!rJd#v#4St+XR_Zk(XtUqyBRsqk6dFWFP8 zJbmihnS$jVvRiVRc|U8y$dEAQAsZC7a!+x<=e7o<(M;_-2A@>jrMOqL`hdtlXhZxh z`!?3}w~5tu-oCZTO!lCRv~wTb%*W#0k;IYU4#%AHj@qN!TA8BR1SYrt0L#dDcptxl z*n?0W!r{}R?f#SNsk(ZKC@u571T^4zFjF5YII-Jmt^oYChyEaIcU6@G+)oRKd~}v8 z2iiBUZV<#Rty5neeLQgHFuUVPeAFWt4CUAr>~c89t%pl4mJrUDpF88^6tj57Bi;2z}bKUXfNvs}lSEIeLwM1Lx) zv@B~{Lg?Ci)spEVRK23v_q1FI{^b#n4CjU_Nbl+Iu5W8h@kH0A5(o zAdds!d=J%oZqDrkG)<0TXn0kF<>l6vgVWt(a_Yvbg4JNT-PI|cvasjm+V+7Cd5k zs~z}N$Zm19RfA)Y!y3CZ&C$?)TzYx=tZhUu?FpRbev#M7ZWUcoOJ?cH%GoDaDI$Y( z=4q4!CvaRh2WiI#9r*Uv#*bCFX9Q>DuxHT_;jdgWDM&8PD*HAC|J7 zBbCMMHhc=a!dp#nuQgMG!B1CH6z>zqFl;s$f~P%?{e88F{X9>8`CCMCwM5c2yI5>3 z+oO7to~E7}X(i1?DgZOaRB@lzzP(Rw^%x|iQ=iK z1n!Pl0;mDcDYN2IM<8%@?4{AcBbeCY$5%At(Ee>>*x%ny{4Dpyud_l$OFjPpp#;@bV=pzG4*8+ypDWeW58yu##{~A$0@;l^gT8?O00-^p zwWFH7KI8cdpEtB%hAcLMSh&Uj?n(3?T}9nHacvtN746^vN#%UCEL%AT9;3fHkJUBT z3R>1XC=7s{XM@SohK@l^xSSBZSx~+iSZCWh?2Lpy9tcuZqi_Ir+&kxuDJ~gLO*FoB zIKYsR?ZG-w?+}3=Y9xrkv4BbU8k9H@l!O$)1~dN6Nyj95k5QubHfYOUa8%lTM7mko zJXmvbJ^A%j+x4i-46BkyBaCEweRc9)($XCi&WN0JKlHy$_BU!p+B#-xBJ0TTJwm3< zZu_lBc8D_eWgs>O9f9LNUNw*2J1h>P>+c?met4UyjDdsim9BVi;zeBy6p3_n*)w@` z^$jR3C#K0b066W(%jK4p3&^-G!A3h>tW!MdGH_)p~x%gy^Y$f)D{xp9xy|m@2CJ* z@zrEu{JEGhI3qj{*F*~dk(Km{vqg7J!qp_u6FjvQ@#hj6+RwVj8soSL+w%usaugTi zgXf=An|moqDd}I!mK~4(0Nl|WmBASEABhidr?JxyvhID-_OdEkc^u2+J4Y#vDsFXe zJC+@pVnM+AV+3~42@Q0w$|40*JH@`Tq${hs&{HKWB;+v6jD9blKO>`#Pu!-@pW#Dq zx*66sp3wyLVUkEnwOj9ICxM)8Be*`JM?3;j3gKAcv=Zv`Rn*QC@Wjuu72u2nQONlc zf0ls(HT6;`?ex?Lt4mE3Dm!^d7hTnIlem+UpU?dCE2mdT{#run=#Id8qum?O_a3sjy)_Ji2;xsRrmmylhMGU^NZy|z-2VVQ zOwb%H+$!=sLJ76F^hZ&2kB0YYtyI@rbo3HZtI2vYv&$smxs8YgcH@w#!sC)or4I(c z7#>T{Nv&(1L!2w;{{TuC+KZ*B`p3cQn}w#9p01)QihGEjmK5DQU=YQe46xeSN^8a-9E9eu}2r`xf=a{r=jUuNam=OcR~^{fD+goclP%>`S!;+27$`@ zJ3ucac&ulsdiJzYnkRLoht_5xoQ-Me^sPM!^i3>*zYrNzJF$+Y+R(u#-aO_E<=Yqo zzxZojFj)H{tLmsYhqvO+ReJ4klHUT;$12N}$tZYJ?VVXqZvyak?)|Gkcv^#d4!TMdksYA28&X#Sx0GU$CT=);@gft z#A>*c;^A2gp~{!mG&3iaHsdU;iGT)Kn;p31`(wVI6U%ZbThuh|4@_A)I_GqTp0=`z z;T4HUY3dRZSt1Suj1&TX72JDzYT>x+ddw|*m|Ykv_4<RSZt zt~nJTofbdcBE(2mR>J3u^PGdugLME#A0eVwjSxC{HjI_5_$T7^7mU6WS+DgJpRp?E zghgL#t8f1R15|(sVI=ne;gp_AGLiw#uQE%?Djh^77Z{wsJ^tNvT{+VJEk|2R)3(dK zb?P>nX=IJINhD+sG!w8XcSQ zq4V?fDm~-u({>h{TUAIbl`RQM+m&)+-qP?QQ6yoU79^=9HYwYZGp)!panA^q;INKK z7Cf&w^|HM5^FK=Rz(axH4t&(8{==PFYNUqgSxq&35ul#7ik1Lb5b~_9vNrh0?%P-8 zon&^Fo3u31xI+je4nvxLU~*L#$rhqM^G_fo^rAE2p1w<>`d6lE>AF_X(R9UiT@Aj8 z^93yvsZ>7@kOllo0M15nlb+{ZZ$f65NE@h(mp0CMAm;DgEE#k&+nic*pmg_HRCN8q zj`3;x?ZTXFlVO3I&O&hDGOieWx#R=fXIYJA?K`H4q!4p_Uy<`uY4o!14RZ*;CA-=@ zZ0NqPno4Rep|n)W`_B2@ zbTy*g(Z>5u(^)E4Yk8E&7CB2WQMpLR8P4Uw9_P}OlASmcFnnD5ujylIA#1L%e3og@ zy?1J&yINuC`kAK{bGf?F)ypkQqZL2Bj%UGdhz#dH4;)|}ZR`7AL#<;afK;FI8TZf4 zQ0(y|ZaE1fqS-Ihc3!x*$4_{oim_CF>Q?w=hds(T_?PL}`h%*G>m2WZfJd6w*|#99 zW2*cYy3$wE$J2Ktse)NnH(KMhph9_*a`3@>mB1&^9PzN{>3f~|9#{BJ8{AD5s2>FU zH0o%mDel%PD$9)m1*E>!(yaASIPB~R_}3(#EaVLQI6CvXe$`}hx)O)7TWR25g$-TB#FF558&?C@(CP+u1hgnR&w%~Yh?+9@0JmQZd}_Sj zAW7Da7->>OS>&dYS&R|6!m|*#&f>fhJN{aDG!sPauOwGELRcdh4(E?{uI=`F@b9jC zORno~xVAm_sAOnkdRZqcPneVhc8m;3xpw3+AmDM|Ua0nqba6HiE!D}czrqk{UT;4S zg4#Nkn%yk~R|**nQPP2!>ElRDDcp=k04h&8D}l~?=T*+NvNFfSdt;PG81UvuvyQa7J-c$3VetFzGEBA`ZL-F` zc_}5fwcZ)`{wMO=B@X}-#~^X4?*~)37qoa0&&?AfSm5VbJ#YC&>~qkf)xmQyT;_sF zS*4_7=Bg;&xZ`dZVD={`-&J(ah9c;sJBUY7zDjgrf$>>)iL{rh`j{&EUMky#zOj}^ zs;h~DPOrd5a4@bIcRc(1>W01>YC>8P%ivD1pF zHlS&Qj*E=^efwnnajG##CY9S<{#A!b7NXw9@U;H`4K5X*bE%4@+LWv>Le>!x(h~(Pzu2V~BHwkgB(;>H4l^x6{p8bu_giraPS_5srAs zIBfP=3D4qEtZ~YnR(W&28p5RFWcWcQvvr@~{gu{(tXA1nlT#(cWrE19ZKwcqjIr5; zA3se@9YTm!w*!%2`XAvCuj%N1^;^^(IY$jW+N^G`rV*$$V;DHy!6bJF;cs4f)RM8& zG*hxTKe7FfMQcx~?p#|S!C1#s_)TEzC7P$F%{NZo0}(C0a8im#^-;!Y+dh~n@?BRN ztABT?6z?DB$Jd`#vRFedIwRlFV;9bxx^#Wf?e~jhw%V{a1d#CK(b0xg)cdI;=c_JZ zXx(-^dVBRzG}+@SGQ2Dob27Kj*ZFg(M&W>@ru9-bBQeaI&m8*d9mEAD)}E({V5s)=DkSo(fQo2tQV_l~$Z|WQ`RsNjqcXn2;q(AMxf$}{M%c=`=wksX#Fb}97&tE8#2VT%V zA?5Fd`XV{8VAv>6?3!7KN~l$kx|em0e9*m)#$a~0&)g5LsO^_-2?y=-1CivDQo}P2 z)6>TvG1XrB8^uWM7wD`twR6iG?OICerQ7G9M020V?0DVVxaZOKZl-re8>p?r!#H0n z?M}g-D2}K!0n`qk(%&GeqUsx4(^My!1cMASf~VXcLNmet0MPI5W8*Q(EvJxIh6V9I zb9oDxJWlZ1v%|ZbBXpIvnv?#iTw@9t_c5?jg>XGTh;ltZ#MS5>2r@^Oem(g0Uu*16 z+GlCUPQ;Dme&fsVy3#1eFWwjB@kfugQ8;0( zbyoiXTCa*!b!h73S~NkNZSumBGBR>G<3@txd>^`(L1vaYOR~|^t1AL2U?tlifK04+ zXR+xe6Ykx_J(h80Shnn)%IlezrKWFT@vE(S^T z-Jy0(E;(Nr>MCN)a9k@(41*yQadzx~i$@_65sFG^X?F;z;)I_2SCjQU&*z|gkp`55 z*pA=hq3VsV2^a*N{dByh3+=&wi=6@&Dvw>-((P}nr(y!sM99tQh6PSfuYTG`ix^#j zg~oAEy*rq^)RChsgp=Hc$~cjeqDC3uKqm8kW_(%H}#t2c+VRMyCCl~MVR zP|H2e;<7T3wGBrY3hrBk-pmJmpbc$lw4!Dih(8ZZ2KsJ4b>?+5m&@HcsQS470Q%s5 zQC|vrXQ=5qg1xG49YITAq?LTH+m?EGsxG5$2$|iP@VNLwfB?sC+8U#$V?G8qaps&z zZ>3=!EK)VZmpX@t8TI7hTKq%n3L7n+rkYKmWQn3^)BpmIK^PxXuQ95by9-L*(?sIo zx1!E8*9Aj6*2PJ2@)rP3ETG}|3q{(7NH+LZFQ6qRz78;Y*mk(-xCU#tuZ<;cLXu1{I^G?2hw84BH^zBBtr` zstXVRC%d|KkZzGPVzBr$G@VgP2;uxg1CyB`Q@m9>?v6Yb^siO&voyRMh`lw z?HhmoF<(E~cYPbLc79*FMSqL8?x(AUzDhQlx;5MP_GWjDc;J6!4hJ2(@$E3vkShL( zBXm)kYe2t#?R8JtN5wnWM_Kx|JH!)2F4Ha2jessBm_g;bHZoUtmlg@&9QO65&2(`S z8=sMV6%Nqsm(J$BfONX(nTw;YzB2WK-EZNkRu|sYRYkVbA^s7!2h*_i2N>6$)avJ* z^ygDL`L9Q$)4|!%(=y_^AEu(|j*E(xg56mIZCtfIF(40z0kEma}C^fEbt_bb{2&MR@b6=nzxWu}$#(?e|D?#t@yL3=AINoM(}!T{~w5t-w5vFV$FM zT+fO5r6Nl$qUTa?(AsT+_5g~yrnZb#R=ijWo@rt6?PkW}3FHRPZAwgmjoN6Ap8o)3 zf>$(_xY+XHVBREBRCG^-cX|psdRu+!@pqw(ZS;3pyt4*nlgwIYa~vU=K;0e}B&o&- z(bMQ>*SoryiWY1Rf3<_#4vKfSHdyh%`yb^}yd=ETS=OGmD!G!rNcL0|E3}Q=p;iPC z2*Ct*9mc%8&i?=oXG609;}!M)0HqUjak4yNbnTm~DlS&TQTI1xBWfCX6mBDq&;AEq zlSQR|rJeT;KC2=^7X~iVAG_8l25U_B@nT#oo2+ViXuO(-z7{{H|)Qf*0g1_H47yWp*t$9|5Zu=S+% zkkVUf7NU-}j#OHr3gPluPSEIfo&h8V?gqZEp=&g-_nMZ|s;l|;>a)h8K!4j->lAig zm$A}XthTBvb+U#ff}WZxXz-Ds3ZRmtw*X|El6gFxR)1+qO|Z~Wxt49*s8A!s_xekc7QBp_R=$|(OH}Zm+SMkI$~G#-f!08HA+kUq z@;-X49MQ0j^7F+jZi++t1=9M#;rC2OSyxd_eyyHIm@U|U9 zr}V}8p5aexovYdjQWgyx@=70b+;g2n0gcf{`5i=GnvYc!jdPgRvktA;W~^HD;wrR~vf{ z{{Ww+i%53`MBqjW57k{bx9edv@WN}*hF4+{urran+a8BfxOanTQ2vu)W%YEkP}IB= zCdWgDQ;t7PX1#_tQBn-$J4IO3?g(-Qd-l^c-YH8}rjF)oR}RIW=5w8BM=jv0k6{Z9 z@p$z`tE{b^M|!BArutgjTjWE0>H5Z+XrXXQG}NryTjLmGw1T9mR^wi`M%W~Q(L^`A zi=r#miow+Pt&m9n0EM9P4t|9D^HRJc>dLN)>+0)7v-M^A-+ib`t8LohMJ#mKF>hkg z$%7aqk-aj6DrHx`q1D5_Au|J7TY>=>_UGrHRAG1$7M;!x;E+ey{@C(bhlsr=QF)_& zqv@FQdWm;bP$wUKP&)zt0JR^&e~a*+LNF$Qx|ZjjhxR^Sh3GVzlLNLtg`f1-U)nnE z$S5w*Q(W!R9EwTQk1e|rry-(nK?JT2Mh|Ua#XQk;;+W zeMhn9NaM8Ag%u!ZtQ;p|ca#;*PC3W*(id66VHv7T&Su;bhX;Y>w(O6mzL}+`g~hHN zP?_Rnc@eT5JK;uG=0>e#a;qHW52&qV1TY+q0SEbNP|!`JwhMp_bbhXx&!PHeCw9F%+t(kx|9)dRKm-au! z_2Bv)H0Du5r@wZ-WB#M;ShS=3s4&yg_<%IJ4ne^yxMp|6E2ZS_mCSfAI}gWXsj zLk>aKta6r&@OfSi8D!H+CWO-+^|U;y%$<79Te_a%K7$35WO;;?${m!P%4M|fcmrSiwHqnfY z=d*A?$mEfs$^iH&1*VFEX=-AvmY!4$(_QMsQJ~K3VR;xNX9MM=HRPD%o;=sl)kG#? zY^#c%7F8=1rh|N^kK%GM`5brC5qzKzhuIpmBSB12;i-j#voGHrOpl+4o;z|#$EH2B z4FuI;O#=X+s5)twkAmpt*0ZII9yf%J%+J+Z4oCX!C zWCsX61PsMgh8XNmV0}5&#~R60K=4v4$`p2%L^5aLKyXR==rY0hMi6lX8!Df3s7i{E z7vT%RE${2C7{LQ8Hdk&mzev8s9R|J{cqtE3T`d$aT`x5fRL5HIM%8IZ!F-i6^2n?d zxQyfbUb+%W1~*-FRHPfu)~zqBd{MCd-bAjr$24rGd~Z2$-&DZ|RF1wN>K!~4i2E{U za0=nC6}oGtC_32P?KCYxZ(x!&BEX`Wp|DFztGH|&uH?duf<`l|@$HfOj~@NyS#xEA zDjcPBhe#(`hN%}Q{^d!N5=@N2-_RTq!A3yjl0Y1tcBSoWHtw%AE`^R`#D%xv{Hs4) z*UL|5Xsaq#;Z@{_lu7W!gB&C=ju3a}l68fqb7j=vZmvlmh3ICFyMVY2x6}D66u0zn zy++N^qs0ITfK7ES5-xpLA89oKBqX( z91VGWUun+Sc19_q@V6k+#=tBB?-SF|aJnz1Yv`?X5lv{fJadH#R%Z^;p2vVW{-f7e z^1Qg*V!Hg(5J{;H5v!z~M}CqhYA#fC@$Kb9Fu?WPfMZl7j(JG$B*(ub(U_wa)~GT5 z%jxN9q^PWpR)cfmT2B;TdM5wT4jPNnrfw$>~~sRR8Mc1l3F(01Nmft9x#2Ff@G#@6oeX(#PPB9=?l)YOiKWA$1S-IRD+(wgPf0jdTF84 zYnk3Tq1V-8cP@q+Oo8? zP#oR@hEUks@t>mB`d%6yhPDNWtwr2|veg21gP+2H{t@%<$ELRQu8XtdEYpvRtG|Vk z<{1sUj|CF()iY0)E*Q5s&NHghJ?|Td%9ew0pxHWu?}6Y(Y?4%DVB_iQs!+RgwgY7} zmsXjU?;*NVQtWtzLljm|;=uTc2ha>>>#Z0;bHhtbc#q%1)mAZ?2y1yOCSJ3rz4aAL zb#c9V(MahHGCR7(9&wTs`QR!-95Bve=!CHD=(>>C*TB?~Mqk^I(CWeks zz)D*zh1x;6kk?GDyX>qKH|_bPZLaXzD0xDiV}p86H@|rz1NQ;0$1% z4zRTyl=ex!H&E-)dU-8|_KRN4uNhZrYpgcgdjnCjqC9O(cNoWgPyR!6%q?-EvrJ}Z zSO~43#EW&-ukOzvN`=4=B%h4-$34!UN86FgL~>)!)OwD~d}Dh!R&=eLi}!;3X8Q{*G2$=wohseKnQ(Ty0CyOrowZ$OcG0pDjw)6pm^+Qq@;Wb45v0 z4LwQVrBniVCSO#M4<%2yV=BSrLN^mJ!LnQ_tZ3UQP9#3UO1LT~I_9K>!;c)rM8b;Ux`Md?-4vr~A2L0^yfQW9dn($C`Kf@#$dX#k1p>@7tbTp%pZnz{{VRI$7UM4r`M1>xw1av=kL)$BtPm zcD|#fNh|0Qrbk@)#~Z=--Z=yF)qqHCDs6RevtFh9KBVe@WZmAB5&pvAADFrK-lP%v zgQNkMoGIHP$9m(zuD`HDB-FNpc8W8(YHc$4%n0Xp&L2OK(!L=pOG&%vufZ^5!@;6B zJy4#P>M_+8=M@mO4bGXos>&%;qsGT1FjfHUVaPvG+-v2%r`gWX%|*C8d-7kPI~TOs z+PYx>0QrnKdHDJj7VTMeMp{ZpsNt-UPnR`KN5LT-!YCOn$T&Fs`PEn>gnzjE+t$Bx z(OcBZsTCAKp~r&bkC5^5Dwk8?T`yHE(_bnksjLm<$c%m{*m61S1~5j^^f}e}J3zt% zbl_6Ohp`!|aelqU?&>^0U_BPm;SY$l)+&l_pS@Jn)=IcYE;Rst^k?BoTyeaP0~~J0 z_Pf`IUL5cD2nVKqyyxh!du#nXG)xmf>yOOjeW7rJi+e7BN0Xi#r$dDkgkdDXW3W8x=v4n}z6LhB2*i?F`? z^43hKmkRrvn94LZ*nF4|#oU}65W|cd;Ga!(-}a4Kkxgux3b^N|p`^Cd7}*i)DqeZ8 zd!3-;>&efqHP?MtZLHF=M=!v;uCg0D(|)F+>WRpb^L3Je>qH??pDHnerc9Ly z8)OWYY-(^LfE7Yu1{Biv2XJah+6v2h%f&_N`-HUQ%S$Q} ze(f*;vGO30aq|5%8?SDtkZrCV4hm7Bc9>bBJVy$%ZlQb4NL3%Z9oXtkfZ;iI)%S=! zCvUs-t!2Kfv?|fCN#o$gao^|j9=hqGX>Gfa!ss;)j!SO~f;#%rdUELWPdZLh1!Rxg z(~nJC(Y#6vg3{GEP%5=gcd4gw_jw3nF@U+xet{6oJfm zb!`-#JWM@4;wW%k0 zU`O@-m21Q*zMbmYYFbHc)DcBa%_^i>Bph{T)t&J|byNhdCU$V=SLS>K{993rA z)O3lmnb<%vwNuyr^QY8m<#?hNggRD7kg8rFT%G4shKx*%qb?;NsrjxxFP9@vq0~nc z33uxIBdgNQJvF9>&0G97@ZPVhx@v|Tq;Z##oy<}B%IAUuoG}Cp<0VEjfJx3~u2VF{ z!PrjMH%QRHG8Vn}OK(J3Vzzlvx0e{*B#DM#dxMPUC)n}w)-3vpeGJ-0wbGLNN4hT3 z)zZaRG!fdV+yb!)cA0VNz*q)pIy4C?t-BLEOP4stl-QM5hn43Kf? zuSUQeTso^)L3RC4>^h^eE4@;pt1U+6ilpS>aG(Io__7XoC3()FZMDRKpPqgG5T3u} zaJCDj_NT4-NAI?3Fp|^A($zr-F3~>4BrZl@y8=FX)ob581hu(XU^Y2`dBWijwUg6N z;(Z*H?Mo%W?GU}frWV1PMQ2r#LECqhs0#ox04JVK?`0Rh4X@~FSJl0-%elC<)c*iu z-wJwym&1y4)OO0t1*>d^Ijf^FOFsEa5gaZ7U{sHdhS7jjwySoAmAlKNc07((o0m{G zd^szkE3c4Q?eW`fs3~Y;JVvLv^)2pw&a)pe07lF;P_4&vvQ-ZE)k{enht2ivtmn_& zwAo#@)_r~P327rU%U9(f7#TSJy3dnUBdFO4b`GdNPk6r-N%P<`&hF(Qg)mFEsqet~ zofux`xVhY;@DV|D--SfbTH~+0GFMkt#0$YFSs{TpWGaFVSm0&8SRJ*!q}0DnPAmZyC^`r6lR%6y`VgNPR z^UBV=NAQxzcDK}3R`pfR8X9zIo;rvksMr)1EC?u1ZrIMVyS)R><9}QImc%``sckSc zeinbxJ{yPV>bJGsE9a$_Y+7qzEYT_j1QQD!pkUeBc>@PJpHbQI#Nx+gdKmTn@e&5r z@(+r)TW!|+2kgjtdhaB3cPhhGJyg@Ql5J1Kcdpa9j{!~sj^5g#D1tVPhiLcJOxH;% zJ>zVTB#y1E>I((lnW<}MtD=>>!*O!uD9|Xt+71}8%IznhGVLSbYBVcujeYMhq#coPCSt- zWcAmJT|H~uY0~c?F;~;XP%Oj@d3h~@INR!d#-eC*q1+u(50Y1HYxdW}(G|K@)yG3e zbhk}Zqs%62d5NZF2!!q1!2t1(W09WPvvN6s(UW~gDk#G@IgcMc6)lM<8zG5%tEgCe&(RkoWO?4&eA71*|mDNWkb`2^^**dwo=`NlzrR zRWk;HQUGG4eg;91exp>MEK)yU6i!0n8`|CI+#Va!StJ$GK?}&g5dgjc`eSZ?J!wVR za6&}8K`N}8>7^S9G^~{Mk;NG(g_x7OfJP6?Tg;iEzXMh~<0Sk=ITh7XV$lHDU8O+V zpQd$H+JYTyP2E_hBQ~3qhA`8rp<-|&B!TUpp0u>HIJa}Y%7pQYrmCM<+hL};{^100 z%NUR<@B!x_53fI-tj-1qRNG?;xsNHZd+L?alaxl?T+z50Rj_#gev4c1->qr7_on{yEuyx9ilO9y(p{kg&0J%0`aDjp!*P+2K-OH5yGti_ z=L6mgPl~= zL?3Rf9zyh*T|j#>a;x1j*SDUHV6Qp2(5J-T4kSVD$_FF+%lUz<3A9u{+xPFyYBoSt z`Otna(cWo*ilJF%jKv~08DvAjMhAye>JD{5vOW`Ikfb0yWGmXx+wPQ-3v(sDS38y> z$L{&YJ|9%=+mJq5+=pHtiEw;YY?^Az6oCyzEi`e|M$yawB}vHaGDo5Ht7G(deV)=$`-sSSZkRR;jbck-;7#s9dAgJtXsq?X}eL#aT@-Wm=TpsVbf~ z4^{dRka7s)Uo?AP*}8`{;P@KGJXh#D(BJ)hm z8VnLDqEpOUiRIV2lg=N<6$ zru3asT;enzb)=fzP?AkiG@zuL0{W71l_MK*oafYQ!S=3P<<@sG#a-;IUov)#Y(UrHBLc4E;IRS^qTy;wwJM1L02_Vpt@F=sMrOf zwnnJ9IKV@UI3C=Q&(mGzfX+f~C}pLS?+ayupK;8!w`u(ORYISQmv=bEI46&$h1V(T z$OY_IDC_5^rZ>6kyysY%ZLx@=iAwU@fb0*k$lb^~;14v1CzS0CvT;<>t(JlW< zL1={(9l({wa(0{^R2+KeT~>$XO0ij}~oRrz&z;P z1Zfd_?Fv>!N-H91MrJ>$FSF=WgH*I}Hr~;*$v!$U6Qu7 zU+OJxq-|;1JhAsk@h<%hmW11A5h>#ffRQLg1KR^uWpLqS!qX$SJC)BJM`>HS;$qPj zI9b$?$bG^2d+W|>9RWLDY2StEbgZ7PF=OnhQrLR_k>6KnCH*e< zISnlq`Y8SwRu}S8HS|?n5CGH-h1qdAK40{-B-ECC5w^hdrDX70o|2=jsO79v5<$9D z5;$<|0l*^zIPdMMdihE%y3P@Tb$vG~^< zgteqaAWw%5cIcgLFROF5U#!h{`HI*?X+qhFccEuRlY_0ywM^9w8)6r9i z(PAPm#y})sE02V7PpRj))pt8^uVrH!F5Fc5s43~Ge(hAgTGTL%%`%g*`-6j=43B?b zT~~0%RDr~s3peU-8sw&;mXgyFE2DX4baNz%M|>#&eDj`vL#?QE4UwaJzDg$@ry|xV z)HjNn**xp*Q_UL?>k{x~#_Vq+ka-+;;B%|yMl#u^19#XfG3nhlX2VC_TDnuj*{>D! zR_Y208$}y@Ee$0xjwzdHK)jMv<2Y{o0giQ6X{UxaZ#R64_xmenO9PFwHIRSk1*hCP zTc=l|uANCE)KgzARdjC=r4bh~Nhpctl^u6Ts?3GFwo(Iww2YAbpEi?Mp^_v20Ju2x z$JJ%(4yHX+U}iUS>aMb%HSLREa)yGep{!eU(#G*mJVVNmfZPY!&tTZ&;u_Bk7~JZ2 zt}%|iPt`=?XqJl}K1cGhzOCwZwp>x^AZl8QxS5tZc~~?+oO~dj0Rub_mZ6d$P4!d4 z^Hfd(x7{sOzAX4vTLFTW`*w_G*IQADDpIHqj1F)MV+@3aeCP21bvB$mq_?7x?`vEf z3dOuKy|mTybor{Np}WCTwp)Ene~haT&Y+H2Fn$w;#z4<)U7JAJ8@~XaJpO(upDyvZ z3$K4g(A^DF)EzPRT4^qYsmCO`y;R>F>c;l4zVa zQq-;88PZvrYsXL7?ymJ|Ua(C_uA-@XO5AcsARs$2-A=TbseBH$yK$=Y{g;*2&L(ek z!9%JeH;KRL9bIHw+{*-pHrz)z{vppR?ex}DHC^+Z4=wThsxoOzAUm5?JNH|y5%@z* zDf}_0$vTbmhMH)z;GiwOizV?3#KyLxfliaxFfA1^lL|g=aof|4K-M~U*}?_8cwFIG zed;czx>HYdonEBQ8DK{J!T{OY7uy4pb+z0<6I>%5IXs`)ETA=~!D;lpNl9<3hI(|0 zF2SRx!Cd3`NZ^C%tbI{ap|g&_m8Ju z6;{<1c#^(lAPGxrM8*FAiAY>kf2?|iC^~BCS6J?8Dd}XEZ!Bes1vvxUk=T0bHnGld zU^!E1#_3yLC~ryE)LQyJ-%n(xri!Y*8al+1f82;65hBUCx!a%ahC8vr#>%KTt$<0tae!Lf|DE$}L4I?Yu& zxl?U@E_$}rQ9Y*LdZfEgIeHO!mKk8CU&3N6PbfGgi6`6JR@lPxVVg&d`=wjH?u|Xb zjT81Jfo-z%rW`o84q3W7(9lqcsq4?$yEMzjM0GSInsm?LSZra0ilgQH>0H#1$zaK-x z`&;ePY2f(Y+?9T`Ug|GBF)YaIJ^GF|nwtK9)8(mLqg`fgFvN zGDh9-bFC&%vE;WPtoKK_B2ryqsElkvt~o4m{{UxJ9jIg3wy8oREjMVDrsL)mIT>F0 zJZjdOT+vmgXM!Pgvcr3W&ZpXy*xG=&0zv(gp5DH?-)`AkOBvv_*;L!LvU(XxO&N{x zka>v-9HQ3(hs5L%Jwf->Lcz31RvSIQsRdlA^})d!u@mV)_Ntxxz}p4ms&4&J zQ4QYtS6+nFT2`<}6+zk*Z7a2lDvY3JARYniHR+k!Q!*CDzy)w?KYzN#lSs$NWp$Ez zU%!%*({j=DJRLF9mUg(^EcHHmLp6M_EcClu{g)~VDmFolfGM9&2D2yByHO{3x1MNH zc5Q?EtrBcYjDNnOo$>pr!1!{1vco9fBcDO55x5)`ayNG` zzJ+kjR82)uH8o^&($zsD#Vjw4@w$>3NcRL0kC)R_+Jg;JP|VX(PV`l3 za?P{hZhwhKf2ibYoUpb|LY=Pz;k>PLr2I~~SfQkAye^dTgb7xSx+W|#2JCQg{4LMd zSyO2r9$Ao`lC{fk>8}uJu5{{*=`>CM0G8*Es6D~daB5_T;I5J;$TF`~HB{B>wj&wO zc%?<-=yA@yCZAUvnBc5gwC|52Av;lOfL+-wz{*dj@@g)BM z5@YZ?k0QCJt-5kbxEi*NWEJq?il+s__Q&^^(;fSJYsKhw8wrL_(tN%0zMb0I6Eh6( zezBi3TvwRrp;)?}8=bTEgvgNq!7F3NL~PHl~?L&L|_WjTA&1x>5lw+9Ame(lau1R z)gs?eB9*HtY?aj#D=;;83eAWUpNQ_p1~JCdjN?3Oucr%?$~x++OsV@)>u;usm&*%T1_Pt%Uk`;xcD@a&KNf-r5KM3`}KHl2n$192PPz5!)lHOJerT+kYDJ*z% z<>R*U7>qH;Ao`z7oi;(?07ZDBwt{GqpiM(XXNELZ@{(mPfagA8&OL}Z_3xzJU2QcH zgk>^Rdgq*hqPW_56e-=F-z{}67M{p#XW<%CgyB0*p`^j#SC&57M>>L68d;x;eO+{h zhO*m50Jz+)gwRp+^#h|;Y$GjGP(CI^0C1Qc#^&_W!>9iMBQiT{M^nfBtl4CE6wzej zhd!tF3boT-C3MxVt*c_F>HC$Ts8UE;vg=@CN(dpl*d2~^b1}vQX!q6t`+H0MXTBxY2s8?WR711nYLjRkq$NWmUWRa5AsPJ2Ar*j&O<+x+JJbeAM&y|t3+;E~~5=iZ>1zRYs)izmDx;amr znE`?V>OdT08Nu|_dmE{Wq!=mYbF@=jX2?dmZ?0U zIU2~HvzpJ2F@mqbt`lM7Ds6vV3K*&)-jaDFln$gkpYa2Lz4^g9vksOWn}<|q)jhHp z!q4ZvJonVN%$STEtXsI-ooF?LH)vFQ))$w#Tx=ao*USA4EGgyEG@%%8=N|iy8PYkXRF<1$bkQ_Uk_;$xQIVeeyJtVwUOmSY55HAxz0w`U8K(_S z<))d0nE^80v692N$k<;gq*W|)5}K{1%O#sCxspO8ca0VKi|o5 z7Avpea`#sBb^hJebyYCOG%&+e=F+=1(0(#efCTaeJv}w(;?zUj2A|YLSArW{=k5mx zMc&n4a;bW{nG`W)X9VD|CmU69jVuNzsk|)*Nh6zpi&Qm5mZFl5mMW@fS+_BWD+gTT z03I{J?~m!Ok4yehFOZ7JwH&&(#3*F>D3>m+yVBH2MN3&!w6$ol&F4r#k}_~WQTTbm z_(vGyQnyQ{gzjL-{P~p-nbS6$Kc%2r?h`?9g0k&VB<%yPOBULA3R?t|lYoA@#*ov= zZzt-28=JIngWdXyO5K ziQmhns-<~EF4>JFTx~nMDI<*hhPp-=WTTKmcabb%z+@lFsC73`+xpX}s_Ji2wIyWH zhFg?t<~&j;+NHte(2f~bPmEc zl&N+srwB>L;&2oO_P{yDMvkTvsg8lF3p!>(@Z-g+ZaB-)^zOdWQ2$Y!kI7l?5!^p;X&|NhEjYoc?*#vAwRo z>j3pu=bO9(amZB8ukkWVF{_T*N|lwUo57iimL8#T>IXRW?cY;H-QLTi;<6t&4gdv+ zTspPyr|*@Od!$pGvh8g5+kk)1S`CZ4CWcC!ImZ;G+4`2r1o6*wrk%vc?(w1(*f(&@ z`Ntf9ap+F2MgsB_praSkV({{UXX>K=!yTbEBoSxk|^8489t z+%QKRG6D$)1Y;nc+R)U;8^C;67^*ZqTlt&vmUHo!*$G)IzO#n|S0<*6l z?tgOdqW&WV-lYXRWlk}kOMXFnYJD&cm~{&Fg|{$13rMTBNj0CQ=xL*vH|)sLdD)ag z3Y-s^I3tdGXFBO1?}J(~bR2=WVY8oK zZ)2-5c1r;GpML%ITMq3XirU&ssIpn&v%Mr1ngEDeK=U+>Ab)mz<%h5*1KU;xoW~iA zP#rL#{{Sh^e&-T_+`8?0THOsaMcR=3Wf_!+cJ@-exb8dm&U38YOXSml1(38Lh)(U@ zRp+3*Z;qylXg_MGk*Whk>bZ4Nq=SM65I6vX{j85&Xy|)NIO8mP&QI<9s@k0ki^GF( z27UOW{vlm%Hv1jE^s$;nR}|M8YI%}ab|3a`jqzjS zJZya14o6G;FKu04Th&n0)QX3B%uO9tB8-Wm+y2n0A&&$GUUA>oRW$mkbpToThunWD zwHj#Pbq+czk5bVKJeFzg9XhpFIo^LVT1f#A?LeTFW&sB$ZySzy9rY}&1P2P;^!qN2 z#MqJ46IKqF>N<$3C0cf)c)?M!d@8B_68vpAC%!xSYdC2qE-nM-(Oh)!&=+Y-MOV{7 zY3fi%E;j`-d7ln(=yCZ2#;U#}rvzSoZ7FwraI7|3!&MVL;S(otTa)t|k|{R}DzuEw z6|RanD%Brqhf%={MtJoa+J%wJSjx?Lb5Uz;DUPJMbLEaoao^ilqK9kkRFU{cD!j{a zM)KT4v@vipIrlo-Y))w0I9YCUU5=``)K@B6%H%Le@;1@9wnF368PS=*kUFRXNET2@ z;~?(N2e{Q|3|f9uw6!KTUU8oP06i9zjtJdFs^8S~Yfo{7<&8UebIB1I@Ayd1(C1na zkm0nf`J)cy3BAo==o(4Zj=ow8h^KnrxWQ1h6;-jP;wBki_t+Wa5Wp$JU}IOJ(wPX@ zC|zGc^HmRb76GH-c|Rk{fzVzs*?NceT=HM+;9TTtYI->XQr3ClfH6Ep856?`^E z(%Is7==Cr-?rnzhK*z2B07^D$Baxt&4E&A#NLf$X+ryJ;zS8x5zL$L^(D@e;82FZ@ zSK*K^uK3jF`_Ys9T8aMv6nlpw@%?@l_L5D*Yq~CRsHq-a7ywk{A8lrw6|yn~t&73R z^tsriq>?q3HjKe6MMK8>82%u0pM}B4`D#`Zg_}3R`wHC;L4DOFsj$`>Nuzu<1w$Vt z)b`mYbUv7DulI(amq5wvw+Hg2k62o1=*@?%s_nHRj^z}zl?mM?1b%<@AC|Brid{Rl zxoWk|c(#?PSh~$BQAEw=rU1_yhtT@#Lrt!9eB>%q=^G|S3b8PXu9wY){KOsEd#}`Y z8uU?WBba<8W4;%}>Tp4>R1nre@lP7h6oU?u466@Ne=SZxq*&t9VRJ`|o*0gitctpU zWNSR_wL>vu1fSR^xl%b(`F9%m_igrXc7Mr)VDs<5uf2bzJA3~CP>=jgL%-Z_9%K0n zEuMxKjp^iqCxntrY@=y8$@p=M=bx^;c`s|X3>EYe#<{y3&_VKDRUI9&R;r4U$5USM zNL7VYW;jk9u?X1O*z+3$)O6%d`j%{yr-_BuA``dp6S%yaYm9gghm%(7$6d%M&X|4`VBE2NRfRJ3R)V- zsp=9deIHN7BrG77DF>Srgm6e74+nN|eKV7t0(?sBi|B?_%M{`oqW4RkJz0@_m|S^T z0fQh%21D7fJ^ez6NTOA$7PrP*yf+0AU?kTjeTmn##x$oPzaqohl z0nSha-zkcFT?NYGlfh(`syR!_^%bzPqVC+wv^Qb7yAj6jF^zQ*&KERv0Ux$Oa}&!o zbHJ+6s>e+MW)YA{B)9PskTde&4C}2CVG#U8=hKe*2P>&K9Bm&LA1wsRY)E5OPgC)* z9-hNho^3+oWn@+U4&1GseNlMn`^~?jdU~cGCbmi>Z@*4m_sAYucP{2`&A4qG0i9^K zTF&RYnY)PM_#PZA@uk}#a}L&@4o}Sg04o0gWaxVrOvzUzuGt+dwOI3~cX?%$4#2yy zIQ7SD^Q`Hnl5tWS1JPfKHpD4{uhmfeRIP@RmPle%Mt%;_jbzP$wBQBDE1i1EzG`|? z_jFlVNEm)c=dUrYX)&F@HR|*md(mizMBP69MgV49odPG4IpqPzDh~=U2gqyO@81nHWRAF4-O-9bTyTKXBXm8wp4(!g zH54H1Eg@erOpnLNBRS4-@2rRRbkmaZ`ab>R3uU^f@Ei~bx_0q=zC{hul_;=6q6P>8 zoRt_nXFa~S)RAc!3y3)?+qFC6w&7YJ>5iD`brRFr#Y>|WM0rG|PI&l80|V!isGp|{ ze&&ll{z)6DmQk>{0oS6Uc!4z)J<>X=gd(;@^9#0ih57bA^4 zjPZ~AD-!BSi&pv3$kI$mGhlI^Pai>#&r^P(O1*3#j)}P|zi81%ADfa7An;Gu-2Bd~ zH*{9o!o1+g_P+9wEiCaeNg)dqoGX^ben*{Jjp5iltohln*f>Wlk|M~j2H7Wx!=MN7 zpON*`NE+r4cp*m)@bKo|3hGM@_Mz;f5Q!u^U*QCOa^3OY=dYbV;WgmBVE`LbdS|a9 znn~rZq*5v6A{h69>5bSvxIOsMFWNk99_W!kt7Tou>#2Ou<^te82gE{=$0TFx-%yZU zf_~~D@R~vHR;cUdjifF~9oTSj*khb}^QYk1Hj7BjEU(1MNbR#lEMSu%V$re2+z#U& zowaXDJI9vvR^xHlNLb8uQQU7VQf6gIgry^F@;L3BV~l5Brh_951K+=z#CyS!#GC~x zvaVYGsWVf=DYlY=IL>yrCjjF*jm>Le+U~yn+ITF#94)SZQ>RVSTp zameG>{<`wATPCLDt6-DL$NILZEtF3rRokMTLWTzzbsUgBV0I_uHRv>~4>egGRTkBo?@C z@wL$O{YwN?RV@UNhN(mg66bg!v(82{$>eM2vow~t1Ifzr&VJFissYq@%A(PEbq@>^ zH}5>N$oIkY&ZE+bX=&(-rLknMe)APwyp-CE8A=@IEIR@8(g5h>vZ&EHD=zArY?O6x zIab^pi83+~$tMK-`;X6Fze^~Um{+2GiYRsJ|a664OQg>2s; z`L|$L%Gf zX=4?X_VNR|1Cj$b3NiugK_CN;G#Lw+)7^ufKsAI6t$VH2eJ}c2WE~k5=7Pz6v|Mg9 z=|0%tl`1@mXDW zo`YL#mo!#Gm?>IqQvKxT2P5CqYjPKE%FdKfMDN!Jq_Yq=JWSXnS0s#nALXr`&JCcm zLjitPQ&rWJQ{3W?nslg)h z&w2`2+LlVxTq*N%RyPs0)(sg28w?W}Q>e{eln2G$JO^=kA1xBXm8}r>-Bk`}ID-Azpsx4eF81}4)zYN^=&Ic!DrFHLXTTN@}Mo^gQWa5KuDet*|j zz#!OJj&P+u>C%zcQw5qPn`dFS4H@;p;Er|DcTWYc$W066X|~~0o9|sUR#E)O;m#IG zdmoV_fPTO9*Ob(0_7Y>^dKjeMJ}yTaEi>usKfvM|)uk(9{81|E#0 zcfrBq+geW9GYw=Ex@|<-Ih!ef`0JKxcU_Fk$UD{eB zqMK+R4@LS@vHMOw*3!0S;P6L*_rmCDF3_yCmix`#DwdHN2c(LSg1-ubv^OLY#CBoG z85q~q_OHtJ9gbJgTccE0py{b5SlN8ai`!vX1Rxb>W;p;T+(!$J_~3Dqg!XwMORKq0 zNYtj~dZo8VB#2fDIJc%Z5wytMC^?i~Q?~EMx=ZBI}=%vWF3O~E7)t2iOB1$Ck;VfOy9#G@qImrC+-yDk^lAL(j zk56s2TxY44lA5mYz)4mEf3TFk;ju{}J|bI;eQ|-K(#TPP;4MoM+--{)sgm7lrreOl z97Cu!`kX z7TRVCK$1hqaT)>%d;rjUArvW6 zA+RmllB3RyFknE=shIaPgIQO^g{KQPv5LNrxYt^qS!yZwq>!0^@|TcTf3G+oA8mQP zR7_*TSTMadnpaI8_nfPRCE}){IpUQPC>j#mk7-*6AoJ_)b)Q9~xbUyx!iOs?>N8hw z>DS(4hH9A$C_J(cRb!4b>WI)O89=%n~WPAy16~#|Mv0 z>eL#UB*7l4S+ubMf}XJ5D{k^QmDtEm(m=r0v!f)63n{+?ZN*rs?v{?3iDkJ?vQ#(w zI%vn4EB^q)=ssryQv%qC4(hYqB-Gns7hgr5c!d{ER@6sPBoohas3D{aTNI)(jE%~r ziv2kHYNoqW-L~Veu2rth(7EraF1%>h*9#%)E}K|9+2L|`oRnjo+Q-xDog=oqEv;W? zh}*fUuT5%c?h&dv5tag9c>Txe*!DWsno%5O{{Thhq&o?&RiCUDB&|sWENM?7AVp!7 z;C5m2$LG$l^iFfyNp^GOt3>^x)vu!_x6c$nTceIR*Aj{~F43P%kHydDsI?{K$8)dO zs`OfE0cXOst8F#9;;N5c^g=ZX7Bpcak78Kk^VTcy;$AP`zN<-+IAX0F6c%b&!oUKw zO_4ONOJ|Pw2e|nh>M}`f3@!wdR-UEmL|w5#c!Td|O@b#+`f)AiErd(AS>pY2^f zX`s|Gz{#|M`SV&2hn^h6)RR!lS4hn4fy-14%*2iW1Y`3W^l`-=mLPWgMPuqU(n{h3 zuokW2mw;A37Q8)c6*Ubl4^>+f)L83OEG+VOYFQi)_ETeTe}VS6#v3z5D|eQ>Rjiik z9~1Z8gUbBdq_x)6-S<2%6rOXV!y|>3DOIq`ASWS|f(}Qftj!}@&hPd#y*`}eOn6;C z&>jVK7m0l*1UA~LdTV@5BAKP)Hhk`68El`4y|Kym)^3}#pQt_c%3I7R-1i&43Cp?OSL+|2>$w)^AjD9!oZ%#UXBeb<2fN@&Dv1N-c>ur zf3tU5{hzviD*NM8)LN@CPV>-9=EW68GOERL8C(Y1PSQvqs*p1tr%oK^k(EA^T-S#( z>mOB-^e0S9*4(yujpBZz7TJ)Ws9?@_fnUYEAms4cQy>u0!A zeb$y^MGBY%USw)QJM)5f@S~G~+f;wVd?raA%6Eb1-_>R^0j!nsxX>i7qp)6Gtn}c< z(8r8Mgb}m=037;`0roxhm8%uYX;zv&oN$j?s#<9jk`KshIA(!VDG0yRee1+7pQn*G zQi^H!f(ZfN9$7xw@|Xbt;k`UQE1yMgY!t((T1E6dfPVG`B(5DHgSjN&gW1z%D~ zE4T9pUfO0k@{j=}CeUh`7%h?1vHQI|an0nqG;)FX8E}6?&a>oBn zNFN8bMm~Aen|raxzu`>R#W47WRm-OO^Y3!UOJ4-E6{T`vkx5)}xT9bO9FL#NO&r8> ztH}j$Rnl98MirH^85BCY9gM!%?ar+_P#SS%S7cvWT zfLDgx2|34p`j|3jgSg$OLzu?{A5S%zTsm!fa`#IWf;uphGt>b}S%hlm2@wsJ$O9nm zIR~9$&!u5@=!ZTX7TkK5IfO`6g4ah?Pha74`*<6~CgCIBasszRuKi6?HYY zX{hNcZWJ{?>T|5^=AjXlmNKA#11o|u4sv^GVPi-wDm`1>yam>sA9RLTA~4l6TWT_? z9Onin&NiI(&m$SfVms-Ji$w)c3pYynNf>BAU{${Z1L#L={D{V}C7V1|kCUKx2t`Qty#bMw@ZY8@X4J|?_n4)GVED11k< zGE!})yuzjFmS+4%xr{degY0<+9rb%r-D~18N+|R`e*IRLV|IkSjj0m1F`PJge<4j; zy4hAJV5qm+YFWsridnqr72ky0i~`M!ocj^;*P_Q&;g69)lHo&HJy2%3^$fB=)UD;x zD2?N0_?O`WX$4O$j>P2afbc+5x+&9A)LQOQ3QB7=wwbb#PguaoE>s_claN>9_;Z3W z@1?$l8%k@vGE^_#uk+j{jFuEs5QPWf?eGkoVBqcm?in49o2CNpsZ^9J9aQy8W=dEJ z$vf6vl?;)}&IrK;z5$bfdF{_SUx^R^;C>Vf$a&EqyxndUGC*VV7S~KrsgRJbxZ#dj z2n9o91AtDJvtf2R`}gxi=Z2!?3f5aKh$}qBi_W^!oyIj&i2|`0!Q0LnZR$gJIR-8v16!f6Gd0clK zdX(igc%rt8ELL=r%9f_0Npa>&Q!e&w5=W*6HymTrjc_=^9!QfG!5AcumV)H_RL5_% zPg7GW!w^<^By++>qb$VxU>!tl&H!aWBY?@_a}`%uQgxqS->cH^*3sT>5sGe`p`)jc z5iL8$aFPdNs}Ufbq#TZa7BT4?rZK=6<}i=>5`E=){_sfmIj(l&zyp!zo)*jYezV>z z9RQSed#e9d5@miWd=Eb$muYFb5 zsHgHR(?4!CjP<14E_3P`O=Vn?WRlEz8lv&ywIehUYi+ziZUl}@5Vjp-Hmcq!vek5)&Wq+v2H92j5XX=?2mbif`bU4d z8mEcHt`9v{UbYI)*hmtj(|M!K1CpmF@chU4>uIb6hL6-eRhLTA^Wnm=4PSdwg<5p9$%SAlVR9h(`sH#)@+L>X?frGc7 zQT(-+E~|UDv{_ov>3g>KZaZ7ye*XZKTk5;()<-oxWl=DB04Ls~5)IwSOdKF5fK(1K zk>6CJmVHny-GkHlwWe98(=Sd&uFTd-UGFh05ClBjDzdjSIj;814+nUPgg|Y9Tkz@Z89v19vI`{)ci#E03BlY zZqv#L#rw6AY&aAzr~2BnrD|HHgUe@S3Q}b_IPKVi2+p=7(zbC@aIfk057XUd){D}9 zE8cuX>KkuPQuO7%YmH+Fl38lxi34Dkc3?gfz|T3y&r~Js__XXYI!Xh{TpHps*8K|F zrtnk5IO2(yrl{rHlv!;ZLq{=pI2a2i*TxQc9{lHxXSe)kH+*sjS-|5X#B}vpx}MdX zw`@(T=E299Dv{I{PNMMo?fXvYITaMOLItOpuvxH18a2wJZU7K)NdxPtVlm7_5<6>; zAXSeqd?=vo?(wD1>zlr+M^ay{-D$eD_Q6HPlyzy8aVgN3AF9@h8C1fJjeYs^X7bLu6y%>m#) zb?05)0pPMuy69^a{?B{rdpk}qu=$m)vCq0Wqyv1&$Z`X}Ew~VOVB?dW)@tP&TR#S? zr~UbDn?0SPC?JEwc+g@ zLw*>YLAc|UW_TMT%bqNiroq(}5VDr4GpN8~*Z}kRrcDr#IVg7G1-##q zg|KXpG@nAazRBQitEW1yy76bLYCCJ08kXH$43a^)e+QrZLpi~5oB$Y;$@6pn0H`C? z$F@#D+Ht1;_CJ{L6kpXnj|IBF(?u+UyW_4@P{d2A;+98EKx(G*41lY0<%UkR+TmT;Zwt~gF+hV$ zPOzW539Yp^(F02YOF>q`MUKJ`+z>Rc$r!z%4 z!29|p8>2QbtL@?ChMF0xnvxo6B1V=PqR!IPf*ZR1 zRRf{=sd|pK=~Zv?%@i}3V3sgTv}AlGi3N8YV5sftH5~0N!^v&=SYEDKp@=v@i zUI`NAQ)sE`8hLA@rItYei)uu>le?;&>e&1w{5j8l+IZmD&hY#|cz)vrM9pMcpZ4K! z2g9ml(NAV-y1Gc>yV99_*yMCasEtSga;%^ncHFriA*{VjuXUX}vEjk`;|p>aTIv#t zKV_rz{eAvrMXiz%XCMn`OS8nLC1T_c)Im_Bx?>PDG>?QVGDkWR9) zU+&hr`>#vGP2^2UH|;BGY>3AS#u&6qmC5)?!VHbM^~SwWf$~NSFZimn)nrEa+WoSO zUKKZ?Vv4i(byKjbn``~xu#I6nVMsXq&PKf8le%#ZTQ7{bZfekN7F4aMcxWr(t&FiA zY+!;D969fio-i_XZKbYtH04rrT+wbAQ>%JLFE975DlSzBSUhqthA<8RA5373eDF04 z%xJEzDn=K20Eb#^w-|0looFX809aOSA_+ji$}#WXzspGs4s*T`qLsi5tDUB4rjcTo zDp!I?!5H`T*6a;&A+A|+)e;r1iy{Z7U8l`QUytn_qBkz_E{ZFj#O~ zLVX)%XZbo$(OeuKI{yH*&D1&vcRGV-q30iC_E*oemq8yCKF|G2LkvE3){>d4=#)1m zqMR8Oeqn%gBm3IOh>hD_!N=G8TiR#rcq{WK!0xf1r#wqG*;{2j<~3@4r-4%#EP($2 zzaI7afHg!D8}^m0tB-p_g6m$N>V~t`A1M~1(N;u~K;!WL0BEW17z~VZ4hBcAtjKd= zW;7aGzem->98F1+EOhuWw|&sD$LOT-?UAbtIHePX8&?AtpH2w}c=%h7;vMt|B@k$# zbyRLM@nC%Q3!0~Cv&yJ-FG|66g!!losme&4GM{iqAnKfv2MQrviekg$s*&3&sF=iC zbD`|V0sdh7>p9v$>kYy1=(M}wGI&#IuF^u@RG@}l>f;Q*mZ&+dh|yCDISNx-jV;EW zdsUta&%)y6UsA-j@Yz%(YielB_=tYTmyi-201N~*uwV8jdkTJdakbZG_lmNw%CA>7nZI;E6W(pRGcmW$vEJt z0OayC3D3pCBgICZTSdy_Ql_H5=4xeFN0lKF8|%a?y7GX>fQ_jZw-ekLBAugx}kr|B#eMmoW9x=&mx(5$hhVvTYX z5}cBU;rirqj#hLRO^?IR8DKjLhkQ|s%dB^4~l1AHSNGEX{<6qlT_ z3Yp0`9@^rHAyZdHM^R5rRZ~k2@v#!3R8KVf0LS9`4C=8oZB%gt(dxZx4X|HD-b-~V z-dzL>TJW!)RB^O1=N_0Ee)-|O-t2fYP;PJ9+Js>!`4#!mIV=K!(vSG7i(I zUZi5cYQKM58WKMiajK51%G7C~to4LJYapIN{$ohtf|l~QyT(>G3-w%#p_h44$F{QY zX)v?WaaTE&l5V@~l0NEU? zZ&x%Yr|wP}W-PlMzzVoLg#+h~ewwcl0D?c2VYVDCW;W}By$VECq(nQDapwcL*07Pq z-w4mYbgUN9yFqOx?@!lP+9}M&o~RNUCBn*&jf?CU1AuwexuRsyweIPR`xF&zJKVzN zwYdh?r%Mf&qit1BJfc}p7zA+6P^cj6CD{nz<90jtQ|qriKB^}$K-~IY+xS||idi4M z_*rj>w6Is$z$^d z;#P10Dsp#YKA)zxnW&BUA!bPq(}WhbCZ7;XQtQtvl6GT|aqX+|lI+sP)PYrN+G!`V zMfZ6Ck_Y8Vju`&{fz(=!H+b#f&k+-_1Ak{j0pULUdKYqx1;IDSFmi1}X_!fjMnPaKYljTPUgyin_2 zqUhVrEmK?T=;oHybdj9RadeS(`DQg?B!Grc#AhG|0G)k9LD`xeG>)HBy50IsTZiX& zQFyWDs>}XNF)~;PBpM^2{yi39;zx(HT@TdNcIaA5os#M1i0QAC!Wk;eVL3@+C02OL z0l5HRa9TVwCcQ}z&IpM5UtfRHuN=~r10ds?&0oLmSNuMpkEA-Ll3%<-Uqg3KyVB1b zfkdnuI|(~>$fOlv$3EE_@O_{RVeOHGhUAYU)l08omMzip)pfe`G}l|qGFDzHV7DVP z8ftk2G84wYB}QYnzTtpR7(MmnV(nc9k~7?~&Ucs;2j0rU6Fs;MA3xz1j{g8-@al$0 zq7>9{O}iTxX_igAmSfA3FgH0T9=e0P*7n|^uN^VyKcra{nwQZwQ$7;-lO(i~TBc9C zSJ1ZD;oLDSAHwKI48Fun3 z^;ZV?qiatKpL|xhw}*Xcf3dKr{mkUi$no(kE{f<$JPntQMfdM!3Js~hSIlyLP$Yf=cB zo%1+Bw2jO;Vf8us=UaM9Y)uXs2smyyDZ5mwQ&c2i@P>1+Bx8~7f!p-elS3g`grCw; z%av-x_E>HZyQDG9i5*lU%%>&J(iOO6B<>j<`(uqGK%?MP(uTN@GD4Iu^)Xh8O8HH~ zqL=qFG=<3Hka@!qgWEi0<5i!gZcBJR@zFM&@v)(!1S{`Nb(`8KZf4PPlG`kZ5oCD0 z__X8w?_xApxsWX=U7Ku;93 z^Os74yMhY013wWCpywGG)IF)Iba4j4NV>jw@cxzuYX?K8EO@d{Ki`_`&k?IBAH?pU zdSH-K$yJENY-3P`RE#R%56BI9`LwT|a`q4!Pp9Ev>Du`t+=Nvc>P3U@R%?ynUDaMo zTdbx9&*Nn>Z#eIOr|Y6S6H@;G{j+|C{@>DY-N)i^9;%6c@q)I`dZV|=1=g3#k(|hl z6C~?{g(J)bN3TB;mB$*PE|P69du4V-bU6O{EvS1dy%rqby0>&C6;D*uJxvAXk`Fx@ z(pu60+yOfXRmddd0>tyidCsEIGrf5mfIeW?@T+P&K1SixN6lO9JtNeXo~coIJvCh# z)5kEXp`NBGOipsD$si$4ROAN718z>eF4nQ_2Pc#7l}Pqmi+jkhdK*SAmQJCuS)#71 zq>hG~X-I`x{P`XxkSS(GQdE8-1A2XRMokRy{{XtuDBX2l`VCZ4RAk$zd1uLK-4oY# zyS1vCnrJB_h)9twJV>b#QV7g@NZ3H+5y3niY3Msaj~ivH7~+T1qR!Q6Ue{Q&_fu`K z)LO5VbQHHBwZ2=$9J9uy<91W!vnyp^xH#kcyz%YW- z@*x|V-PSfMq*qF7HOG1*t4b38+7V>2)gDC{=A2I8%RMs|D z?ig5RM=;UKuF=@Kj?vUt395v1TLdw*62U%3L!6DL3>yUJj9?6BQb!bwd2u!pNhEIo z4ps+wjX!VP8aX`F`^>u=<+$Jh{I!Fq1HN$z`+rCU7P3tsng&9s74rMnC6@<*?anmO z_^2K*wK@VNfmrj$uC`l{7Gtnfc~i@kv5x-$T?5KJ5t|%@4ZDvA(~Ve&ol%ZVWtX}V0U(3VI^NUkCeql6)qgdaE}fI%;Z{0tr213C z%FB;f+9s>%x+!Vi-C5JIs)&T~uq8rgcbKIz6cV_0BZkJ3eS7r~hD(yu&tKs~9C5-- zjQB?t@2cB6I<0Eyh>Wa==(4A%U_fE|=Tu3s9IwP|(CWV(zSTY*KZKq%^(~^`DWzL| zde+;eh~UKxn4S*!v$3R#|C{#iyd^{{Rf{(Nsl7DswE5MAUM*8<>DNIsWp0^CqpZ z(yHNDTJ^buj4}K`^PcQD{$mI8)c2JbPD>tm#zFqb@1R0pX$!2U2O|KTKth-mTDx0P zPmHbz_5T1ZS94XU-AE->1XHJ#P=VBb2^8Uz<`4LG*Ey`2yJQN>T98c40YPD>w^2JYywgfP@=AOJ z`DAByc;Au;Jf3v^r4f!2Yc7!^NgczfYU^d(SMIjBObkG5V=7BzV4Uw91{fo-9!On` zl>H?HLN%$Ou%uJTI9VsTAKq-+{?w^CW*OVG;O8fi->mdPtEazyicqy(>eppu>&Qj} zF=U*s3EASd~T9qyRZZBovB*T? zQv_;?cAy(VMgo96fE;LIk;++I+P0nP#WX>A%&XfE`3igPsu zRCSOmw0>+kDYz0C2F4BuAmg#)UCvPRPqq$OiO1JLGID@<^^py{1H1InIM6}dA=fBl z>YkyR#4XhIP)i0b6>~r&F+$l19C-)hW61u_GmPU^U65550Q0bqQn9u12FF8cyw_Ir zO=bT8sBKey$lPnhvrF zv^T_gN?O~VF~&7P>eDLLC2x$$dPNz?IsCo#SZz61fyPn(rIYUVxsPC|dG53g+F8T8h6 zvNn%qarONml$pNceH{8Sj&KJNXYSwUOg?5Dl@Sy%I zX=dE`9TP@*LI7DVP81vWQ`GI2ID$(~z?p!AJY`Qii1g>z)cb49X`*vUEwS@nnoX1m zRGIpOOtTerXruvIvy!;<9>e-;PrG#nkxILq-a$etDk7RO8}2LV#(4h#Em&x9{VJ2% z3Z>ue@2KI8J#P9>+zt1N+%0n^QOM}zhXZkXuHbp!xAtYTRu z36C+`;wL`6$LKZk?%SEu%6^`UUN{2<1+;XHsy?a)(LK6qfmH**BcP3>npeSK%B8T) z#tNxisLlZyHG3xjra)Q0OlG#aGDTp4)OF8aEP5(Bv)s5nrmMwsZQcu)gPlo1GEGra z(zP{9RRpmR)6i9+3?+fVERHdrFk}M_xPW!-bQ;NZ@HRzU4Y_$%!zqJLt(~rR zi%~*$n`s}V{yrS@<9#h)h8-k=W`biPoDJjC)9AApIyR@NI!^g!>NS!Jf?O5o?(&6- zC#xsmOoN4Y!B$)ja7S%b)OLflFN;wVO}5zaRsg>xJkiGsgP3iHHP*h1IhVw{$NJ?g z)aolJK}qJQmurSnLEj!TxE=`3GwaCIBmFxKi5TyN+yFmyK{kBQ13!SAd+Zih=8Mee5hSMATqJdS`i-+;Q8ru;hUivF3XqUx*MaT>TD8ELKA zcL?Bu49o^w2fB;{*qwM0qJ}wp);y36fOz^Ii_J}}X>~?Es|E49z*@fxZ0D@HO6_v1 zTU?Q~eLT`ElcX*1dFEsa&Vh=f1_Y>ZLu0qT?9EhqmYAFepyTL&WoJ#TbN>MRX#md) zIkNbh)_r4o{efw-)padwkpUV*8&6Xa$0y7H5~|1XNO<)+l5WIwtl+lONBh{ts>Q4aWyoOj9?m=nShZ001B2F#s{{mJ~zW|4L<$yrqc-J zb6VK}9c!PVSzRwrbgi!MPja`)mA1yE+c~aOmO3h(=VGfKNh$FKBXaSL!#YhvC#Jca zUQbW2PgMtK2f3RVP2U3qk2D@GwzT;*_S&AAw>2b=W|C7IM>&~+CPiQXUO^#10D1w8 zYQqG8hrj9X_$ydP6JgnZ5FY$^^-$=o)b=`xxhJ$tBdCzaMOP(EQl&fbg_+fYlZG3Q z1P(y-)Y3KKz+ewi`+rKVpHmoVFa1NH1br_l&loJXzLL67QFR4OcMAG)>XTB`EYQ3I zx+X9eYiBErjDw6alRKg?`~=;3rf#-9Snru&-bGRRCww%rS^9>urn>V-4RMUc3dKq|hPy*xb?f2_7aRXarr(mj;I1W)D@ z7?w3vWgV;Bl#BF}2l^i}3t@4isz>HJ3o$@>YH^3TG?)tl@m}; z4ODZ~Qk8_Hk*+tCW@Q`54e+-f-N4p-urpI0)Ajy>X`LjHNfSt-Dznc{S5I|q!mjUg zhV4&onwFmPPSVgNQ^w6BLZMtEt1>bECHRIx#|MphxBmdAX4E{%1g)cZB#-HM;i8&9 zg~Vg3%y{G$evIs6miby*0dWxGcwc=p?eydUX$ z6WB184bq!EMFI3Y{Z-n#?I8zIM2f&z$RLTQ;Ub z8UT2#*vML1Zpn$CqCyJTyfwI6I)|fZspu`#_NcCqGtDhEGe40Te)%nyKMnxgbIHKY zsOjN(wlX>gngvnRf3qutpLOW1y)V+Y5A>p2mCLA>i|=%*8ag_6oHCDrV_^FTP@wkD z;y${z?E}NZi;u{E(vSSe%zHz4^IEJH=&Tm&h0gnS#h#Wgj^iq9iV9>*D?2-I21xYu z#(i{Kp_QV|k@EilwMgos=s;Zg)HW+Lw;FDy>Bpn}jaex)^%m;=yWcySGYfoMf4#RI zuyy9UM?lDGW>-!PAK~p9+p0#ubGe+^EMy=$s5JMGV z^&S0BwyJ-U>3#`L4~1b&bp65aBll}Wx!fx5Ha3FUcw1MHhFa556`e|wNr|JtaCq+7 z9lLqHrIV4glT}gnm91^9uBflVg zvNcPy;*<*n?I)A(DfN$K@Grn7dglEDeb$sIde~|n37cUh6><;9*@o;9&%V7DzJ0NV zfn{cCqC1Hv1y*cP$U1@N66}l&$UVv9>D!-{K6=27gv+~+6>;^lsccqzgkqF_@gJR~ zv)KB5HS1uRp`5H4V>_@E`-+RLma5oE0;L`^H)fA=mB{*MSD4^3X ztF*&aQ$Vm0Sv=B*VIUw03gB&4&!+(UYd=$?8+XJqIa9+m{4Oe?XK<_oV0v?`)=j=}BoLy40Jrb@QovecG`i zs;H4h)RLPlo@<3W8a(CHu09|ER_WZ}T)Yh}jNSO2eHMq%T}y1ZSD0

`wP)%$py>{lK#y-QBl+0B*|-eWfWD=JBT-*FVBpt zZ`w{b7{ji6;0;lX{8&$|Z^9kmyQ8G;zq9V_iy%=mD(*b(aM_Kx`J8a4NUgiTF*X(o!2N()U9 zE)miq@-QF}*v{Tqwnqnma&ktm)#XslQu-d9fXhuieMLpuvYodjML7QM29tay=ml?>< z1Jg=uuB8s+cB8Wgda3pR#Y%GToxAa{Ww>96@xbKZX)JiAu8I}^0IAl;CA}lJ)?J#d z&g8c*nDIgY-7y0@fZV?b#E8*tt? zvKLL5eVI}iW7H*B%I=C)Cq_x;(A3=T6;<^V>kCV}ZpoyEM|hmE8-ZUiu0{)hH*2{# z9{QukN;H3G^!+^3Nbyj?G|y2al@e@Mkj&$>o}KY|)^@-mWpv$g@_&@bk6kHAR$1jkZzoSkLU z(_3P^)!0&cgnF8q4~A(YUh(eWfJdk~<38qu6bvK9b&or%Vdbp3CrO8Cxh#iSP5n|XL+1AY3tvg42&!>DzBf+F`cZXwr_3ER~KjPD$jFPb8mGb%iXio!jI#RRs2c~ z_XZ$fW4?7w{Mxd`ky<1F0PkhN?me{#6s}lAdfuPRu~e>m#7766ME((BAg)^KiY0nE z0`fDC{OX&7iNexpu&5Q38TfYb{haDOgk*|RwfEIYj_X>KFGnnJ*HaYofT;(1FQ8Wk zAP;VJohF%&cIKKsU&7IgSoRIPtjnovGt-xvJ?_c)l;Ds*EnJQ_jPgAd-}9rCZ6b;h zXX=XkRc$PyrHwpp3o9UTu$(Z*sN0@2u4uE2saj~*I9HpD6jaceX@Yq(qf1QjNt7y2 zB~SKGa6Jzl#;_$Xe53>6Ytd=m3@ubCDwxS4ppOLP^NnaVfV3ARF1{1+RZ~V4=4m1k zo=O~(fu1=YzPc=rbGG=ZvOjA{ReSGHwR=A}4VA+TjC{YAwqSt6&sFDV)Cb`P%+fnL zB!CPZ(Sz{(^>(pJfpI3(>VBoGj*e<)nB=L$DvakG43GQHpzc|A0=CUl>ua*RbEl$` z*JYxLbNj`HF!}Ugi=XG6d~00o^1a~Qlht~NhTsa)JU+hE#TVSt;ZqmhV)CA*mPdzf zSP=UV@>dzhemEfN=VtbVq324+7PZyEIP>>QH(RG~{^0auul-IiY zB$`+%gjI9AfgZ9jO3S%dcvfwvg)NBzIOOZoX{U!vJC3w;AUI#@^YI76m7K{W4QKfH z$j6sZvF{5lS1Vnzs?T|nON3C)^Cc}IM-eosxJoiV12SdPBRdHNTPH$C#jn?Cn>=K- zz>(MS$noK5YD*azF_B}`e7}WA@w2Vls(N;76Dg2T)g@W1si9cvgoQSMCqueO%J$w{ z8$nVJe^%Hcf=$ryIvOYc0Nav1Z1lQRVPk;E$?!P&kK&eNQFlwMOLDkW589H(BdH+B z?Nb_%f!OT>aRd06=V{69tBj4IzX-0rzfKemZr5!GuhCsPOYHSo;|1DyI#Q~VijIm8 z^_shrNU@2scAzM(#hGwO0EFP4<2@O3@x>_hhBzy&sNFyIaq7!Yh&0x&s-d`adz?&%Df`voq@03K^>mA)P^04yBF`siLX1)?4ZBR5ejFQ_=w?OqWzn1fK$=~yu{31=05K2%!juv&ux8>6B9ti zpAHq79_fwAlCO0a+3BT`cQx8pEpp;7Y2~m^@V{;Rr&PfET0K)?Z z1bsC-AFzX8kgLpG@I|YIlJj7_Np_m=ZME0bx_Oi=BQ)|$JGaDNV<0E-93O<^9>Z1K z_}=$9-8lEntj+UBBU>3Y)PLH!*sARh(v!{9w(1Fzi6f_q+?1vztf!MWU}D*_c-bBb7e-s{_b8r@>S}FQ zIuTh-Eb=TYTX{{ABJN;Sv&l>m&Hy}Ak;E3789zm{150-_l9;?(s-x+vvrjkO)=0_& zLo#AlS4HyW&pumVusF_eK;Y=gL6y75j&t@Z(6a-BO?~g>{3;a1yk5CW4(} zT5EhCc$#;>4Y3q&AeJ4@0KquMby^wTYB6N29Zg8*NbCciir>;c81;W$b>sErb>42B zy0xrQ%|SgvRQYixc45Mkxa4k8k)Co#X(a6ZRC4Qgl-d`>+V->5qL?cWNZq zStD#7s^Mqwm#4aepy~Q$jXf1aE@h**Q)OhQlNnISfX^OBw%`s&uDi4U0K|5X=%zGD zJm7-1`!87S4LzqG9Tt85irnnix~pjVc+!!8Z_`HIS zM~Aq#yP@W<9X-{}e;~b4c%Y~o)fa052d5pd-G>_6gIT@19B#aMSo1(wYz|fSTSX=6 z%~eko6*Sco4aq4RHa2*Dz-K=$bL+2B5p4060buy7XE?(q;#P4{HD^g(YOPgKq;;y9 zRgHryh93*$<0Bl8&y9GEcYCCtx{&%WQKw{jh(wIaIcI9A&@41_)W+HP;a$Tcxbz3< zooK-F;Y}ClvnFSEW!PFR4aS1eLdZPXWkmmfn!dIF0JrU;-cq`$TPf+%vX)3?4Dm}FM9QpG zkU>+19D{?w9FF?aVJ#uUkf}LxmDj_&6|bv0kE42??bOuWC23Zvp{@IYsg9kd3%*%4 zBVsliekH=QA>0+Y*R^c&IwtBMV@sXLy1M#(*O`1VwpT#+kkOtmp1ujo7qJ-me$q-UU9ODQ7 z09|NLW8nAGK zd~7+&9&$fWe?3|TjSi{<;-S^V>R9qld*emJ3Bg0AB=SJdsXz6`p;F-ws;BPTakrj% z#-kwgQZ)D~-Kxf3b}OTSP>IIp${go!m7+_EmocB%=SZFb->1B0c*z7P{t;YW+6Z5=mprs>*Vo09gd^z{-N`DT5~ zk0IIy*vJ_pQe;MCTsAOwV?xh99{uuw&OAN%SIJncolLZp($d~)=$`54IwiW*7n;$; z7>LK07Vf)736B&#>6T=D_$gPdp(xfinm30eeMAWJCM1_)I`#X>m;Sy+W3v|=Uh z3$`-SA6y&(z|VaF=C}jNQ;Fc5dh?>0Q~X1x-)Uk!2BLbAt01-oOS+CZhb7K>eBQtGJ5a7y+ch-d*>Aux3sO?}I9 zj1QK5zv-kcE*L6M;&6n^Xn3Y=;Pc2Qxf+q67Lrd27aBBx-ug1d=Rma!BsDcs2%(0S zLb6REI0}dX+{B*1YBz}3H$T_q;;hF!=iwHevvjo-c8beuHBed_vm4S^WXlwX1%}bW zgWnt+^Y5=Wte8GlJ|3f*=lNSYU38G*L2W-}o?dAM4OE6j@*K9{jh)1L5$+DIMQ;_# z-!im~%Asx%Jvbqm8YRPir)nTw1%m!W_X1=BdWr%;f$t0RF)^< z0}Qz2oxt&sSjZe*Umf$(y4dUWP->aEq#u6%YiypXlJ(&QT=dS(Y=*uIPgGl?ryJmx zcKR!fIXEuJ!X`WkaHzyG?jHXDSkWhDvR)&72iHo*;pQ=ZBkH_-MbYXA>N4E@kCDxI z6`5SRRxb*=vXbj$x!Y^*u&fsvTlJJ~5ByOE4xok@<~Ii!Am^QUxlFy0s9=I^;ANzF z_>7E>e2UmI{{WWK9xKFu9>V9p6FP1>)vhg36(o~pVpIr&IgqvzHNZTv_&~rMfCjz4 zVrm_#Ys`k-&VPiGN@D?|jultJE{A@wxm4LNG|5wCx4Ko&BoZ`rB@RG^FmbWl1+uvt zcx5M_Q6!~}8*}CFg`Xk3oE6z!9c>1VmY$#PHeQ*53ZxXNI?NH@{Cq2+aLt14Y)ZR5HsIYOwB-w#N8wgE`p`{-6jCzd;VUsCjtJyrewvB#ME;ohshhd9 zFYvS8z3R$(DhX?A>QPSX5D@VWR`A0@yaOV_s0@`SL%_h0@os-L_lFdUw_li1oDxyhW;gVHyfRNh*DuPg- za!J(*bmkaC%kADi8RL#Ugt|>o)lB`OhaXRx4}ZLwZP&h=x*}QV>#AasoWin!5=Iz@fJ= zBmxd_Gwbc>PNgIc6=t-&g7by#9F8Zaisx5T6&WLBu*K%=Uy|fxk8J8T$j4xF;H7*I zW48sRbXQp|Ye#IFNfw9(hN7w}1)7?soMU25O4~%ZU|Zw(mwS~t15oNUXG{a5bsw%h zI)u^b9jG@bzt7i|X1mxeRaTgVswL@bhVeWSTI!`X_RA8>H8U4=J+{b<3W2qwZsR$c z_;q&4*|psq+ZFQuJykt#Wk_3sPrn|ke(-+LYUo>kl@F0scbEfLasU>ck3R_n;y!W} za1V2mbEc0}=;SfH*&cSh`O<7)TFUs7OVc!RRop5drJ__RB(*NFAXDGsYz+SZ-#Xi7 zs1J!zbT@_0*T1wKs^QmRdTOa6wZwru*_^4F_jf>f0DJnLa0a}WZALXGypI+4{?t!y z+6!0fuNf(PJ@A6MvZ|!7Q(WPag56Cy^A>k;hLmj~fQd;hoC0=$a!A%UXGGVzuW&n? z^B?w`X8ySHT%8Y#{Y$-LHFWWa#?@qL*8?1PmDGRBkEXpuvbDmCK4>>g#t=b2TUzIX zemC6im-{_}uGdFA5^s!443hooM^r8j2`YFEpZ9x@Se_y{5&TE3m9?bQ%?{QVlHUp+ zo>yDcB}JYX?hw=YkmMO%oA3|qA7S&=d7vP7@r&_W(MTQeIIEKNZAF^g)eT?eqRObr z6hJXU{9(D>?}3gz^`i!?M8e`#N9p^=P->v{ZO)4Acarf%H{R=6H_Xc+JK0AW&pE*P zcGe$jIxSfeu-k|1wsZyXj_KG4=Rx@M1oV*6&u5g(q$z4jkOO3nSs6Ya{G96xN!m|q zh}v(+`~0c3eUvP`JXe@n?FD=nzq`j=lEEZ|C`Fup?K6y#wEB_9&tAhzt9hXD$?;xx zO*>i%Y2g9!E2Qr?i!C%1lwxIuLo@A|X8}mST>M_A-;aHIX&`&!rmS|otvOj8u~lRQ zV*~;ZZDX~iry)-30}B5D(x-_y?scaDR3)@-y>Ns1omp^D1dCecae?Wg&QiaHWoj?J zTk0vq(o-3tbQ45KWF!z65y-}UHFygOo3bfX>bireES)}<+UjLDvno+iReS~l z9Prps!PT*xf`1Xz1@DMa`5%2%UD7eTNYB`!-6zo29*VE2p37#Yv$bn0Pxny4WZROs z1e^d_um=MtT2ks})CP%NbeWJcGF-;dDIaVLX}tZGdj2(C%$7EsW8No@O#cA;KjrDF z&{um$&+u04BwS^FD)kK`R4qhcu_Q1b&sg_Hmh$Z13Oa#rodaO51P?NvG64r5?HjxM z5ZcgTw&h?sv`AV%NnPZm>L-o>NvR>D+A@69%O^g7jmUk&XtexZsibJzxpb#N-vrc< zv`yxH-)TkNoW#JCAN^>4gI3yYs*{FQ*6nXxB}69?I)+y5!PLwMXg}kCIU)o`2!9H43ZJX&{{WR{yCpYF^yO1@#MYjisr%5{^>K(P!-r8RDBDY&pV{TGFnx4{-38gg6FmgI zQ*U@`p6Ppzs){OFfk+-YX(dVg(K^jh<$zloeiS4Q0XPRoZ_D47R~jci>*@GY=z1Zk zVzx}lZ?!{1=L$-Sxum#D7?MKl1hO=7vE9flBqJXBj=(?0KeZ*EREmnbLeRlI!h6m1 zRn06B(p0lBX=5xH8hG|bIT!_$jz_+T$-qi{(KzS2e$1ze({HD-#?2s_DuoKLpdSOO zAW%U(oZydR&X$Tiga-Ld6jbcJN7e;*M$Pw1Kp>3njfVkF;t5t{$p>)wPZ|Ucg3$Fu zt##CQil`dhV3K&&n8yv$u8<@^1qL+*fo-f?oN^9wG{*CIN0g+ms*UMxcW$GgrMKhZ zh7yJb;eiHHek^WYMn4GPZX-jlit0R+PSJCY^=g8(BX|V6zjY}a(UppjamI2zJvGE0 ztFcJ+o0>|pK?5Ln`D(n183nA%#agN^iRjwv^4pT@b*Qe5B$BuS%NRShp&Z8;+N0p- z-1Dilno+8hM|3)@%}Efma284GsC`w{U31bj)_$pLS?F4O%!p+$){3R6R@(ccrkEB* zDoMd+Z3dy}C3i$M zROS~YP7lIK82N%mu_uB0ca}e&TU#;6*;*dX50xgmvY1s`@7q+|6@kjbGvwVO zQZ|rqH43satk;-6^J#FrmO?o_^p4?h!Cc$aG(KHas(l-|{)bf^gUY_cg;2QAvdZeJ z2tB=VLYZ+rKyBWZX0eKh9B(8jF0-~UWPXh@!Bzde}6@7(FQwnxI;SDnx4#9 zU1~i0)RK9XG)_09O3T1GU9|)xfHltsH!QRXo!fSlJ3klvw8*8 zLC30;wS-t!4-PJqbj2yDT5?iavF1zh0LrLvGCP1+k~8Wx2BH?u+?t`}r_xGccooXp zVz@2dIOR!$WCkA>2aR|c*x1}!3ihouETY{fb)9ZAd9>>dwguhyw|%Tg{6lZB9=gfb z!xLj@WA{i;LwN8$YP`?EJS{`R&lyE8g`Hy7YhA|aTh%ob)NLI+L6#~=nar{s;3IDv zR48rANjOu3uScn!x-QLjiPE$^z+m17Tfy{RRKg_GHSWVj=h?rqm1MY6UhFk<*HK+< zx1TiyJv8ePjgu$LX%6kc^1DL&5Zdr&v4C;_+!!3^9nQX-<~TTqVPtO-FgPC-Z1HogY3=tbH7(L+qqa{%B&2!46wPubn*)9sWI;mWS|{8p;|F!i-9hQmPbR}>FDZphJ}xY~Dj_R5o|nkTq_ zwlA&|vBcd4kaaJMHP#DwiVKG0DcrHTf^c};amXJ-rPFB}BZ)t5pW2Ce+e9oZpWvJ957K@vA9Gjpi(bn@pGsw(?)1(=A)*MckUsXu_jJ_ zAaXtZHDtBNJyaj;3^-c6(EX_k+Ps?EZDm!(UxpH-DX;t(9N%8lth7IiOztOtFpG7dgm>S%Q^>H#r2>2YBkqZKv^>27k+-hj87D1qAJ zVcUSipJT^9=Tir_kft%Y$7$p*s;?AqQ-K9iNXSQ*2~rArlelAce=nY%$-?gKtQO<< zPf=6W^&>?=9F;KJWU50IJg0bSg194iP)0y3RB!<7NY0}nzpA{w^h{%W#@nE;HB>3m z{Y}&LmdYqqYN&R3G*fxd)JRELKn^g$h`~Y#CvfLE)RM3|5aVC<)l|?m`f1Ekn?5{p ztv)F9!I~>g_FH|@hRpCoHOZoeDV~l{N+U;?Ct-bxL+vU-UBvJet+-3$2WaMv(zN-U z$;59W^H{6?RuxfIHAT{@dF1nFmS&bmIr!YMEZ98qNj&@NCu7Z9Xar$gdQmNvi>V zqM{a9S*g;RHE%L(fU4z)3Yf_(GI#(EaxwJ)6Jct?NEB7N(Qc=5zF(Fc5~QI7f;)_l z>8yQ7k&UrrE9|0-U9DAgPfr3Bsh+K3G|L33A#XDQ6X7RoWZM3s3GtD2F9JR8^E1pPgtohE?1RQ#eNdED*V>UcRe+r}K zbN>KI{43u`)$|1oR4gF6%^@QT8OJ`~5E{bPK36w%qQ|Hn{!21uxPo76X6jE2Z9XH@ z+Ap^Ex*9r)XQoPd<=yvoiNd%=e5eGFN~suZllX?4OxHdZ%M)lGeExk_*JZO$JO_2y zKV&A0?9~%oqw{X@!$w#aN1!5bndfis3X_h<82Nf?!JLRX;}BsVi!)l(smYZzs&EqI!`l6(lGL z@d5xRvB)1SX+x)*P)lJJLd~8*6KuM^R7SJWIFTpf?8dwa)o{{W9)=&ZgsP>ZL5U1vsdnYwzpGn{kxX;1Z0 zrhwF=)ojIq8vI4L)Fitl2nOX+2yNUD4?J_qLL`OVRZcsJ$1UGhI?F> z2&Y(KdGGZJPR|ruyrpPq#E#hBz?OAp_1oJ|)7eW|uD<$v`l}T^3sF|B6;=NL?lskH zq|n*oS|ct1P*{!g5C99koqGY1rMvS@;-JyjLAF`?Z>B0`sXO2oS=AYp)a^T^k={(H z+;GGUu0M!ti6a~#Myx3!-_$kbs_XiO8+OI=qFSP08GsVZTTmep&A zr>WQ!Q#RemJ|VqhL6T4I+v8r@1PKq>Gvdw|0hWgr~jV%Hq6X|4@aJlh4Dt)z;bt#MaWR7R-?M<9ZwkHm}{@eX!@ z$ZZqiPz0~4C@bn9lIeKrg{Oei2Y0u*B~d{P;e!xA5X+*E!ZJJP5CY@khtk$rZFQ)& zZl9>FuXz?PzScnTruhqk8TN2V7(Wl;U~)jlm|4O&;+2NnZtFZ1SBk0%XeUD)G<3`v z8TVuqkbqzzTt6>@6(Y-Bs4b4 z)oI^jt_sLo;Q(?*z&XclYRlT2;-)s9c_ZKDRgXor=e)~$d*kS|{-Wx8Cx@0eUaTz* z6l>*EQd33&Y!AYSFgPB(0qS|zm(^;U563CV`I5Z`lSd}0-P|LOpjp(PXzrh_pYHS9 zYia3Z+6_>3kVY8cS(9k^Pr8%&<5(^2%`@f@o3d$o89&pyn4>ZR4JzMNLH8THgMv5i z1bY1Sws2RTY3Nm!jlTD7Qh5b*q*^Np>&scAyv~~r)?!t^&s9zJR$4`6Qfn!ZluC*i z46Z$~q!cU;s$AL`bCs#>F4X8na}^L~2AC6^vXvgCLBTw6ti5#4h&92$4h{bRD&zr# z{C~nf(subbK|Nx+RKDy|MHx{llYl#(Px))bYu)}#&Dt;NpWQrS?Y+Av98p0 zg(#-;3&lvlyofP?0Pd^Dxjgnd&MuZJ+%w<@?~sO{P+H+(QmMKwtE`fW#T^V5y4ojt ziSMf0k)S=3Zw-~ew8Hp zP;zQdU|0-e)O~dDc9U4{<`F}{4nArKhW8Lw8uSr0ER>A1tTQ4yKJ`E3=+b^Oyg?|Hh!AYYua15aGCKQiZv>I>anAmOaj}>x{#5;Wy52ASJEZ(iuZlRW)*DR~4FZuGq;Qa{lg9v$T_&rugBt0z z5)x0${Yt9#24(KD7f|%ygq=s#R!FY)I*KD3qAb+4vK2mPTse))3@JSAAd#NuPZwmt ztYFk>2xvVV?AY_iKC8`1+YW{0k}#h!UjFMf@q58P5a{d`VyC8|YO0k950!C)Xs&eS8s zX;URMzju*w_;(;=V12>Jz{j??qStA)G+~vbdh)bGqL2_>pMPbv+dM_4NGT|1Fw-SW zn^KOx1#QwO?gODi#&FBY`ho?b@gqT_{d@YVhfeTG0{s2*irjk6uCo!-MOApIm2m|v zzB2N+U;r3Y8ORyn4J+FIPaY*8h0f@)Znp4q#a|Pu=e}L6klN{}=KG8kRh3c1l;gWA zK1zq=7TiNNco@dJaDZJC>HVuQ#1Fy_QBV<>s6?1_L-$?{Zgr?T|tO?~o-UX^O;=4IFcl(GSU$ttnQCz3EY$3697 zh4NFgpLtTeM!-c-_vEU^j71zWU@)0?swg{@XN>WUTxsaD-x=ftiR>*BwRow8g2}Y) zkZ(ipsLa8psX>1jjDj!duYFC{X=At9sBDp&d{m0ED+C24C2&gv!w&rV^Q=uKm%cJf zE=k9q^tJI1!tDMKTc20M)DTo9%A%$~#aP2n8pMJ4_?H9WKbJY@TG44^go4KW`u$V` zW3JqVUn*!Fof@WEpTPeBc$Gpv(xc%Za7e%z8lA0cN5C+vgs(NysrrU3xI;trnS?aCz=@_}Mngjmf{J|6XWwyGFpniqq5AG&U>KUhMNAk}0RBG}Tb#a~(uGm?VmQ zQQa5~%Gfv*<$x1_twTKvDZ39;wY44vcc_^QO0dl)*4x4OGw{Cv{5{V-^#1_kvQ~n9 z{K^MXVSO7~k3#iI->7WuPXZg{x2R&Jsv*KhBVa@tH9J`gk@0Zg;BpAYbM%dFV`s!V z@#}p)$u*jf_8d!&(m4J)s`piOy-l;I;H;WTVbm0Lf|6JoP3<8A4jMoK3!JWVxPK5n zx1nicw?E?B%o z14Mz-KK<~kYNmdv!U+2G3%Wkf)g3R?{Uvs>&`OmSm#J%y?n^PDkZg_JQLyp<00AgR zA^lXYHoHBIi+mOEoc{pQf;P*fe#eS%R%pC9YAfuNm3~aq(mO=05Ck%u5Xw&lTj6h* z#&vDlR+&C5j*E0^NNt62w)j)i(b2s~ zV|JrXAIbDuW$uELtU6t(BxU=|Y$m62IZ0G>*zaFDmdU=@(d$$F0RyoIdwe+xhJG`=_>mn#pxb!@Ze_d$8 z;AGs>VNjfWG)7i{x06dm)pJHf2tu@xkU%8hd?bV4+dam$OuJfK?iN^&d7y_~`hn|f znpkS2rjf=A$BdJX8w3%beJGW#B8@2v2+M`2RQ9oPgd2;b^2j$bu-uG6{{W!XrZUC6 z+Nk%Q8W9<;j3IQ1*gK7*9sZh{`UM8ws<~JLGJthe-UtshBHJDgdmTu@N;fEeCD|^Q z?uNNgS?O)n)y=WemgfkPDO4UpJ2?7P@1i^P)H;PGa{)4ww?~`GJ}=KI`s^Zvrgy5F(3;q ztXG%2SOtbCzuk(jsX6`ELwH+n>uYn|X?oVWhUFwx3MgtQ<3%Pgq%ycgP)0(PJ9C0T zCyjZHS<^Ye+G`#+eErteoK7vW)eboQmZ8_-;^;f5Kn$O=sxSx#pSwy&eh=rYxN{)& z{leZNn!tRQ;U!+@iC#oN%z>Zbz$A>G0l?&*JvBmNT_ca0^k7o9_yN{~r94M8Y{g)^ zB8b%F0aF%PKj)0gALhDPi3cj4t$??1U7>GC&0C~xx(b;5$d7!E{{ZK$3=p!84XY=LnsDOQB?qrINhV?blG+# zf}qGFaKX=GqhzPRG^gC-xKTvEv^s)@#cyXkP*OBf9mom&=1$Tao_jFHNIC|R*a&*v zY!w$8s(LyJtLvVLX=);^59Pdj42kAOL1{x{{h$rYs3d1xH*`eRm1vTJ<0S<$P?K({ zM~BabocR$8xbwb9=0uM;!8D`6WyT~~pk&Q~|gZ6a{5-ZkH+Ns)KCwhbfaaHohO392C1+q>KO5k#Z^i0t`s+lWb zwpl7J-+X~#sCZ?C)>wAr11x_F=gn>uo)wfFs2U^@f{7|kJ?`UEW^}vH{*I+ZJaEy{ zQwarJ5^ut7_>MziR4Z&GcsrTu&r(>FAT-=Ytj9Q7y9d zbcoY*>v?r?ML7a$rTj=XcSJlBy9bP8OWB=+Hkwf;zx6cWdHJjTtNx&dSj>7uchza0 zC)wHxm7H=PBLj|pTJ^3JX?P~(Lg^}|l8nSj%KRr9P;4hEo8pa~WCCm*tTwS8p4zP- z>ZD<#g_Ty+P*`H8mVfo~;mnY23-JJP-26uvKHBlUrP^~q{{ZpW!U^kn^;e>6q>wc6 z;;A=p7y4DG;ib3JH0Eg(u>e1pCO9lJg!RWiOljQHLICqIOP!+hCiMM89LX~c37D2E+L+Lp!vJ8Kv_{b!=?I8)+e`F zDsB_7U%OOS#Ztvv;^lWC0Q^87gdAY%#PP=e0QTY*2UFMiNYeKm&s@pX*L&|xbth3- zWSmw=%rI&c6;wDV@#Gw2cOLwMt&JyT!7NPLHj&Sts=K#v54BC{dP7UqR^_aboV(vvGW97H!1twopNU;5J7Lp4=FifF2~Fj&b8<|pxEBe>^`^Q;Fw%mKpx0F|h~ zOK63Y(RAzS}`ghl$Y~hdz90^&nL`;MdvdS_;*2jqoV2i4NP@F!zlZQ-S!E6>&)#z@#)@YNoHpR3 zDJzcH9F1sbbkO#mti!Hm+c#mLKKwFjysPH4+ok^i9hVT`b@%sJe_Zv|!Y-htuIcN| zZSu!F2p%fBW{1m=HeFgh%rn3t$0xTK*RIlObUFt*7+pklAT<6@9#pMDWevBmU#c#FA_X`zI7D>S!e0dV!YBamzlSvTt$Bi{9rmT7#RFG()V&Q zqn4<76vk-H$rai26sa}6dDY!nN#x!-itAai8U>DL44zp4fWsMn{=J87S&vT}MhD#f z3X8vgHV4QkmMaI6R(XhmDFW}E@lV6-TZ!&Z90Ol+A08{H%JY&{7B~+icLwHa;DSc;^_&9sYViTXsTo6O9|R z7JGwI?Tt~go{V@0{6?n)D$adkl!H+#O0%$0xBxJ`_W6A@Z=z7sRus^$SJSrucFDF_ z<|a&KTOm#fJdzKfKc{AC!L(#xu;7VU>=o;3p5T_6m$%cA9MzQ31`)TW;&XwXGsm`d zPkUVLmN$4>MoSEGY%a?B=dz-hiPsP@+&1O59Af~nKu*7tpI&|SD?nhPrh%BldZ<)WzIC{z4d!64+FK`x>;ST3Zjk~5d%*9eS;irVxP z_4fOStbs?~=&DLb5GC6y5d^ZSUB8KQyLWMes%_OYy1}vZS{>MX31Rv2w5x>;H(m6d zp=zlt)(Q$SHnL6z`O%j+p|;h3Vt{=u**_R(8WV)X~lURDq3f!SMKp$W@PJ zc{fWYn^N%OV2&^FPY3K(N2`1buIW0;YdzYsDeA5^hP_eM(bGy(%ko-O10V-hRc*#H z!A~Hz2pgKcGqk%mI33;tkDA%+{Zv}mp7vNRHIhFI3>ECMMC#aR+!b{>E9;F>4BAa~ zQ^_v&Z7SQ)T}36T^-`N>lAg8(rj{6FSc^VHkulr~uvrlpsm4Y?DtXX*Lxw_(7d|IO z4iC@%y;r?^dxNaH&i?>(h7Uce>RxEt1X$Jx$=nlQ3I;hD@5dybPejQG0mp%#f7;4~ z7}y%@c_>{VOWfgsJlC2=2q8t?BOhQrfA!TZLu?aR_wVqbm;fyBwQjKS9=@-`%V&qM zI>ThL)Ja=#l^8NjTI_x@_{yLgQ`nUR`e^CoEQmA=eb3oR*}6uT8Q<5BAh&<9M@#jk zw&!xY3l;JU3RA}{a=iq|v#Bnv|j`UwuL?Vcm z0x3QQ0E6YQJZGQ#&ZE+5Lp>B)m~(4H6H?tN%@t#9iO3gZIC zQM+>NRsDGyw824EamtO&xRZMvFIgrCAuPSJI}JqLL1`{&Djn?FOR^Qqp)2d} zR5aC>3K2m(&dee}*a^GgCwCy@8kLSNZsHWICBRsuSM3RPJv-pPTU-~0q_Nk!o_mUB zLF?|rwuYNB3-KSy-6P{;eAnU~d%OfGjY&lc4i7jQ%y`KwcoI4))qTdQl2~gg6(Xyp zl`3I%Jc$v5FV7hRN(B&CKjUd>9IpBB{{X2W>W+!D-zY+uN~F;!{utZ_3V+=suly%k zQM+);^W~%h(yqxlp}9d;goKlZ0OKmy{{S2x%SA8hZdTXf{m{J9Q?OthdB9_ji-!LI z<^YU)om-6bRUR)ZaG;!A?J!B5&9EaM?+y?6e!7G7xlTSNSAwqH?QI!sXCB0A=sSX| zTTWLv(O#mgxm8qJdTs6`$cVM@aIdu$NqyWaC@j45gxW~R8c}L-`fW+6r@uTYQC)kX zvOpt8e<+67_+7|1k~U`};b2IALI%2rR6Oksv|TOq*2+2;uC3~co_3vTZT@mcB!Tv@ zcLZfoyYJyi!Cp1PHH2&vXw?M0Na&DISZ%gCSY*fDXQ>b7Cp(IW1|#Pf%COS+H)N?wLVwZjo18@= z`G0w;+zT(l!JEu*ahH6cY?fE-NZd`HQ5ec2SvAg@nyIgK?1HCcF-2&mX(gv~AjTb# z@Ny9EW{_yt7OFhA4ny+n2RVk7*G4Ld5J|sBU008;`55uAfaJAUz zrcY?U+~br~-Q%OCkh-Er5lKR{hcUj#*u9ef#zh#;&pS<epYd4WvNW00Dl+s@|r zfMh#!@V6UJ420vckLI3_L}#QZYAly{<*K_>-6oi%ZzSUixLv;p&lx=X?!S9ih@mjvzPw;OTJyIpA3bu5nz8CH&#FuVmJSCf&I91IU$b>%g$fzPNLKytUB5(fVOx@@C8 zWoApYu0$i1tmQ*1Mxb`~$`_>#Fz#O?DAZtG1=e(K7AJI^=QdsNO6h z(z)}fEHKwNSxZGMO_h;UHZXX~upx)Y{<`z}tt5J!yOGzZR=QzOdbgzL`hLw#96=av zDkSpHnYX?bLB|Iq1s#jmECD*$z_HnDI2#KB^Qj0aB;_`lT!qeZQ3mP@IFXH=0*)-`t|y%&s=pi%ATr( znBsFk!5XzvDMRQBIqrS4-&ro~mlJPwl#P}#fpzGveGg-ot2(0B4P?f5p@nMe9x8>A z5?%O6vyMR=p7{Xtq>Di#BoZIeJ=K3JGgqk*vCVH^711$T(kRhUKq@eC$kmBrF5zG| z%~v|ohK8!wS5G-ch19Hw_$uIx71x&5=`3z*sEVStbkp}h61n>KVWp-)Peq8}jBP}W zVL-=V4zr?c<=fnBo~u-Ln+f=pCb|gi;6!9dlntR;JYauKY_ZR~c*?Tb*#7|1pi#+9 zPjHHVJs->LxF8>|^wh6&p2FJYH+<%btSv{Q`p%InBWY!iegcE$dmb+}GjW{Z^-Ux7mDJw8vuVVv<7NyQQwGE?(Iy5CqY({Aw41%DD`njilpNJ1_k~ zs)|nHcYG%Rn>&2@_U%UHP2mZ;7?Wb_2NinmKv0KSqzUSU8x(~Bh-8Z@Zi6 zmKeYpJZb9ioH&K-l(IZ;QQ1M-0X+8k=%LP0xM?=W0Wzwo0I&d%IPN*ubyhz}$-??I zjwYyAn-DSswMok_9AE-7^~Y@kV};MXhL*Qwx^%}!biG^B(@#A`$U`L!W7o^)+hmGQ z-bo~0Sc+wtOD~tW?b@tLl>xl;`<_U1MW3jzI#eNRCu5Aj6UI4JE1dkHT?Vfq;$q9TM-y4nL$%ErsGc}Gqo(L>hVga46W!# zW5M>+(>J_t#lq2`MG=L0f{yLf1Mf8w)La@=#B8z|0Qm4g9rtf4HuvYYu*-JsV;hgD zT?lL~A(;30T@Cg^@gAGQzZN=1mV14+m#F^$ZwqX-H#>eRsM4Y1r*RvzF4+`3sHQ|a zJl9eRoDGNzBYRj`lgX|}j&k*T4YpG}`?fo}d(*03STXnk% zyN#3XmG2cynTDNW_##-)EUF42jhGUl20^vj61lNCrvw~d=lSthyBVGA<8T{Y&!1fS zbnsZei&jYbm!hnlRY?TVR8c~<%Y#Z38Gt#CP(T?fSfLz{zF(l$HiJ}Nl@{la<^C>OA%!Y*ZTGBctyXwN4c@*lyPC;S z=JFH*UBQYu9Yb)Xl@2m8$})A4EG{jnoC5|){#JgWwl^FB`CX^>VEZ9j`U|P4I*Y3X z754JATc~K9G_*8lCu^`|25EZ`%(zeC1pGrO{inJ>)_#H1_4#xQH&Lo{qT1XKe{auK z`U7D90Pgq_rbduR#5i}8cID&~oaFPzJ+-B#V9$2$qs%iX&hR-qfX4nagWu+RYiu#EAghdVV78kIrfl@`M3K#0%m#nis9fi`$a?0&G@_oseO0Kn+L*H& z$n;X|ofFa>IS`K1()Me5=Xd(O66ZeoY@GgjWKM91=c&aZ__<37wo(jqt6`qw?%1vR2O&QazHyQ1m9EhL0E-|Db^ie0%|4$}?L&*Ii1Wb}=i$ehTYrbOkzY)8aYa)E zZ6hniRRM-oV0qk){YF6tzOx+bVWqF9B1g2W-|g$vg3S#oUIc`l$Za}96jI|2R zKmA;9ri0+z?*M-l`>mGS@Eu%Qq7OQ71EIZ0Ib_&r*Ohb zcJ1qqMn7NVMy-*K6*2I-mVx2*(VZxzX)_`@f%wO9{(DYFo->eC5)4x4ZjQPK^C`*V zKqXA_0mmc${k0^`>Xs)_uN7gcgi0_6BoZ^n^3pNXne4U7mq~A7Ae#Gnr?AsaBEcHc z7I$W0*vde_;l3cl9|<_Y&7emKa7|kFq;&L^wpyB)sxLKFraE{k+6QBpw-GbrXiJsnc=7;m%a5+4{1v-Z~088*FbR%4#ZPV&Ha;P`LQFaB;MZ5;Kv?QsA1Xq@}57 z?Dt!|mx(Lu!Xm=afdeA5tTF{9R1mBPDmVWCWOJ?_03-Ua6Fo95YgrfUj-7gHX9hX0 zbHo<`Kml?=a^DU%w%ir<&U6i-b}+trd+l9JG?$C)6b7kXmX_X(vdD@C{77;!x6p&b zpH&A#)f{zEZJkeRnvOekP~30PBU8K{d;!*I_QuG_5Gmj?zr?x9u^GlS*TL$KRCcYp zlJ7@Gv}J4tXe2c$H8`nyNisG7Lj@s;An*v_f)5%MMGkmC>L_j0^W7@zt{01~RlZ0O z%gME1^N>az4nYfpmvF$1r1xD5>Jj}pL~J*Ey=6ob{{W-QYKEbAR#ll485>~Xe*?A- zMp!;J{K3(s*ys_NTPinlGsaGFrAE;DmXSeV+ejdc4EE%IO(DpnO1IAc0C}K-pbUbb z{#ey|RgMyk4ISDj7P6L}v0;BQAyE)<8v(;8IRO0h?0cAg6G>dgy0;6ZP3rFqIw3FB zU1xiunyR!&lRC7{$uxrt8i;|zF(E)+#N#@&HS@(6a>6U_s=G}Pe|qb`vbtJc=QSCU z>a@_+Nack+Hb{jJWM)5&hp7M$Kse5*bjrBh-||wPF(R#WH|pNEw* z99znXKZNjg4T6>`^HFcSG;2LPiBaXNtFCqwIl%+?y*^`D`k0`V;j)XbGOa&MCT9Nt z4}J?Xe+8z3<@+j8J#`B+7B>|U$x7RZb;=%O&HyTQl_7@*S@Snbp&OVtIouBqH3V($ zP7m2=y*c)Eut##WSJhENEj79oijswf2_VIw6bY+(NYj<2-3Aw(;2O6D62 zo;u&ch^-AOJ;4+*kW!y^TRp~okG86RnD)DE*O&6WEIPnNZB$->xrq(l!5tj6YZQV- zk~msWr0^B7$XtR?rnDu6&JCIMT|qT}%I8ehxR7}Y-)Eg@tsYF(vecNj1S9~x&f$-d z)KO%lw^uOfC4KhwvRIMe) zYREd)=|>k)EmaEE$kdVz>spEwC3s6r(yJGfduL3r(W==!^bS+@S( zq_|HUj`B!jmKVm=0e~Q>7yvhH3?6Z+Y=pG;Y;_evcv~R&x-Bvvw5^)(Hq}ctrfg)# zSTPcPSxH>~0KJW6NgR?mj#VKWnm!SJoc+~)@8Yet=K~sB#VtybMq-stKP>U8zv0U4 zxF4cN?&{L2HJyD*>45u>JSopQwB0G&L}4fdoT{z*`xSoDFi7p`q;{ONT`es?!_`)# zsSex`op6YZ6}HJ2u+HjoGI8_A8s{;!l`tUit+w91yHL=_Pv=Eb6ig(C?@=txPUEvi zPi7wcZ7N2?I(F;F|olRzvV+Hc4B(5BxAb@>I&#Cv<4LDBH;P}i>K1$bri-XXFw0QVK>q+yBuuOi>OMz8CihRbfZ^spiv86yqK>Jb z52yENMP&UU)D@7bSSPo}GAabMOI8RynF)zmNcJUx=ds4Fx$bQktREtO3O8x?u+zRS zhmWpMPPXvpr>&N#>!=6{Sw=7moSY8W#a_Vmp zx`VBH-pv*Arfaor1tVKxnt0T+A^3~#2Q48iS={4uWsX?6GEFR#Y2DvASoy0xj~I?k z(T;P~X*P=#-9zCm`i_PwtCUjFR6pF{i+r+66h)iL1F_sPKsh^j-N4t4?EU`$`lxHL ze{P>;?YmM|KTp5Oa-ex2NZ?bJ^Xi7OZS&rvbu%gB6a7gVp_MN7DU4Ed-R+(<{a*VU$D!OofQsytX$f z9IiM7A6;y_RL0RGm8qtT&dUTURpZBbE%gQpUY=^EAH_9p(TsN?V>tbFQclwh!&pzh zn$wT7!@pCh3Y*9Kl~qxOmWtg--~v)_GIs!+XNFPl>8#m%UR~82N6yN=-pq(Jx#XX> z^0bJ0j@ovP${Icb<|WnS7WS7`U=D=>STa}_!~Ttp4q`2m;yQU8eL2}_K6$F^SSMPX#P@{VL11blb!hfi5&~&w_tLKhcBNE3J*su@A z*v~v*44U+ZC1y? zS7}xwp84H}TMU|Z>85D|sRq3d^J_X?G=^^djvj3zgtZ=d~$gA)CI$WqyUva z!XBt@it$p*T{9|ERk63u`M^;jU5;_+vIxofXI7(dyHxdYmooe^zd|8&lH(OX1VvpC zE&l#6f1uWoH_$b3>ZA>WFm9 z-RY8+I=-QvvQqUmHAZ0!0onGN>&Sv@^NOEgrm6!SbkQ zg#wNvA*2zU$=+Z<7#|77b=6#=FND6Sp|P~pwfDQVRZX@z(v;id4Di#-w64Yo8Mn8X zNATxz4&hFP(Xf{tQ#Sjg;vk(f(-T{1W(m5wP-&aXWi93+w$gFC)%PnF-5+p6iK?{V zX9^rvE33sxS9`w1C6Q_2sFsqUqKc#|u>p$!j950@55z!Vz>H{JF7s2VdafyGW%3rL zx`dj_jl?urRmr-=x}NRQlYYavLqhcv%}ormQ>#`?k^bzW zvyKKy1&9RW8U)$3U%cMt{3qX%hb)npvU57#{=eg1HT%~nC~T}zWkRtyQ{@XvDa=asJqdZB#Aip1Y;nNnAAF8 zM@3MY!BZ;br>Fh>M9z^R$x_^FPvBwR_hA6?R!!pn}+6$MGySUDv?MY<+~M^1_F6N&ftJd(8>p%1;g#b=r7K*|FP#jevgH2&?8!JM)M{-z z%$_lq+w$B__T2I<>Qq&*@NGe!)H7mvuR|Go(cVOUP5)R-2+D|x8GbtrbGlPx}ef>2n1E3->Yz0tT1vi8pmUv982t?tAPH~S-SomF+A`Q_> zsH&Q+OVfb6S1gI1KV5KZE{`eN;KM|yvD{?rO9ShHodc(5K(00AsGTKr%^wKvJ@)ge z&xSB)m35ADh0=Mhvn~KhGx});nS|L-xSok}-D+th**tPZCkJGs2LxmuK;VK8&pPk% z7;=XFiC1y-Jc8$swJ{3V&Q_Os*~=sb!Jwa)~Qi z$H27|vcGOiWAz%~*$2b{Ko@mHx^9hXD~JC8cZ!(uM%d)w{KI4V>aLBCxP1oP{jB(6c|nW%6GNG$ty6q-UZ@! znzCn*=Y&}>N9r}A$ekV<7ITRs;Zk9=fk_QjJag1TCP0}}g=}YQ9>Cxp4}5W{2IoB8 z02EDj>uKd!x@=1Y%9`O2F+beLlvW%mlw>mdG4;^YFAstTMI0eENI&End-)q875S z22YwN;HTLp-qduruDX$`<^VXdM%aZ%wDJyQ_t>~m$SmCIZFg*f3pCgsKgWmVeBymW z+;+1cpXqe9P3D)U=_x7HI#Y&PYIy1C{uJ~8DvTob8Q=|haBhpr&Dli^>Q1i~*h<91 z&SBp@w35yu6y8*UwaUwG*Q%?{PnN7@VsU}}PO_wt(qo%abHXWSpnFv?m56EEIAU@J zp^`HbcYnzI=}EK>7LnnXS6r>vWNKkF^7ciHl1qP6^co$WC5KQYX|G&o-$eu1=}2Qm z+O>7JP|ts8Zt|FcNc@LH%7}*S`sdX1pMT3=Sa5fA*AfZx;cIB&5Q5I727tv`7!raQ zc6Iz;udnBz(8r9c*=n!Y>_VHUE>9@mQsq^p=L`wT@#ry)X7@EA-Oq};`OBjKEt9Bg z{^QVnMJ5L1w@LsbBPR+w{LZ}~J0T;}#-CfPEQmv=7Wa65 zR(oR(e(4SeDgK$V$#kB2c_A~>nOn?R3n!XMbw^|q+k=y2W@*^&+OLd%bX5M^SZrR z)DSC=nzb(JIATECwNB&5AY_g+jEzok zEmtqADh=B2VZPhups0?zz8SEl8f$%4{Q6+|U@K?Toj~U_f(nVxV}%_2iVoLmYCH85 zT}wRHp-mb+x|X1jg>X~Mnnd_oL5vVcQJ>FISZN#ZIwG z56?e8eOwvJvK7LSe(69quQC~@&stINkz6Q`QNvG9}JeE$G059O|I3JxK|EA$t^Zm&Ie;T3}F z`TNpBo^ykc80BB|aCM^*v1`t~yKSBd{?FvyY3hLs<^h&G<2-+l^3K z#H;1y67fhhbFG(x{YM!l{rix0l zQxz38REV+2S>;e;_?-?iFg6C-IML&j0iZmT`*r3>5$P-)Cml@%FP8KbAc0O+Tr_}8 zcc@{OCj=?T8P0G;l++3>e5AckSyd$6M@?q30J|hZvY>n?c4Td#bC7TtpMW1yG=Lk* z9S~D;>8c5ue{M#+|4XBw6nYY**8dKBo$UIkBb;a8-lPU$2=V<^YBB>RL!rUtfn~XDwgpx zIz=1~-eQav*s805BLr&8>Z1!mSc9^MnjeH2IS(k9% z3Keg~l_hh-ZUwt(b4b?3iY#U(-+3LXMiArg|!@#%objz^#m{XF0*h!6T61#wF5O8rG{q>|&oi1rKq}3Lkv6vT_|8Xu zef^BR{DG2b>As^sjus?lEv1r@Nboj4JgcwSyQOcJTZI+spQ$S&u-fV*j%!$|nL%PQ zBiWxVl!gq)9sM(^U8O8cVPRn2;m$uit6DZlNOtdpbX`8{k&oxEC&JvdTI!yjON=I1 z#${2Fk6lFI7jUX}nyrPXstHm!9+}m0M+t#dd$r*7{Z+y#Ir7dxVUkpiKV5kJLyQi1 z9S`T<&1h*ycSqUA&ys*5k7L#5ThNBePuN7=c>17B7tOtQcf zD#uTPsz;#582WLk9z~*;s6$rqHMR9CbzR zsieAfC6fx5Y+hR1SvM;{5 zWRZc~STQ&pU}Sgp)G)+i0D+U!!_{c%+awJwc(Qp)zf?tew$sr;Rc`_^P=yC zWsGbgxf{Lv=(IdI^XZ;_4^zx}V77D5LuTDu%Eic_-nkq(x50>m1e9*Xd7*)pZ4hST6 z+}-u2bu0u6!BqyC#1c7F=|fV#mmny9e!7{o@nKbW!fD*5+HJ%8Lr+R0l*!rbGo!*( zg5Yp6bEE3HeHPOo^E0>Q-$Y?@C2gmL)<h6=rc1 zFOsMki6kM)WRh^BB%XD;(&9cbpC!$3aImKIe7AZVG?dn9R#m8A&mQ27>_N{AJN-36 zR&JO`2?PX_X~cv(f)`BqYvJrS#fV%e=~-n#=SKuVRJI0A201?c$@=PM#w9f)Y$tTi zgl=Q97QtcrFu~LHKeg-<)n3U^t7ETqGE-we{uS~!sT`DVh+_b_)Oyav@0s3dsFBd} zeC+-|bw+O0nqT(jG(1NiG(rloBZ$ag%Eb&_nMmM*ybwpR{WbIHU@VZfOc9Ff^i{~l z5gW|Z_RC`zMQ6YvSmy`l`Dx~JVT!3MS$PW?>MP9E)ms(zmM1O;r?A!mCSzrB6lijs zvd2$MJj{*F!9B_R{{Wt@MCLPvFafG>Q}s=sqpe9Jv~z(3dUH=tmsD@im)sQIS07+8oLK(cGqV^-D!1A zc<^vF`~JyG;d$I!UWHeyX0zKQEoQ${)IcQirMJm8YN;HJ$`~*Om6w%p0pkn-!EZ^U zo=3NCrJdY^k;wIF7$fcuE2`q#$jI?hx=94})Z|L_Qq00xWnYa*&LZG&b*39!Dwk-Ts)&!?GLpynYIfjSg~rx1_JQkvCi`xpvGpCs&wsQ`vB?Ah z;E0)mm6Oi=KL{W&U_n#zI`uu7rybLuGXZJq$;X%Qu=Vnmkl5c&2uFyuJ~ecAQ`&A9 zOVp7~S1^K&V6Ry1uO>2Kk~b?DQZi2b;RTLhGD-UcM@<(re>zbMfKf z@6~j)^wif%NQGS`EmAUx7C9qHqXdEYO9CgO2+~_2Z^JBY`RBf@oZ6_|VwK~hYm9Xvqbx+D zw0@-gKx6BXsaRA`3uNg_n1bk}Ir8M}M+0kPKcUC-)Q=VvS425r9ZMau*G(l&{_k6G zbc_|ZtgItx08@l5xTZ=63hptrh|fBd5HOhMD5JH)(^`;O`eK$wiV#wz_ic^Y8MY+M zOKlDSJ4o6HAx?PgTPP!AeIV1USB_OFNqie$&Z9!q2!aC@y3TK zODL%RwU7m`4&Zg5Hba5?)uJc(X91{eNC6!(rIE{)k;jzk=SlO3w z2ZOHaX%Y>ee*I}kwN2DFE2R}hU9t;R5`0q9Ng~No#iQkNs&*BSbF{AYZg55qeAjxR z)IBk7qODrVZZq8KsK}@bPbb43z|oI|N&X*%ra&yYz$EAc)eaO}HST#~cIn$Z?N2nz zSZZz(`CFEt?`a~yD#|x!V2~fgqYN-b8t_eIVMdg>i0B{KJp+lk>TCJAmI)cou<$U=60Cy-K z+8)23eL&$wLg83mQcd?+_L6cxE;N9pAaz14UJ-Pix{~h=4UQ?2>PuCJLnAmRJ4g0c zCpZ`x=U1Jro#nRfI8_^@h%~mY(VBZ@r^D+Af+}cicB*Whf-@Q^W9^KVC+beBOEaEJ zrc zr9CFvdYG%PW>Ez5(UR*50C;i^)02QPlk*tX2Dy!vH$8xEu6>oWqS4FV2RINs@UmJD z6gOS1w#i{xDXXRr=Cj~NIL<*FlE?o2Jl2v2wj(Q!M^3*bxikUKp~wrT`exSLh5!(fRtTUF_O|9B@2` z@~;*;dP*1;8R+7SGbc9(cZ%svXE3io(OXY&o{vvqx?moJ_4s*gSA+FCJKK}q@&CaPo zgKRAsL5FgAX8B+Y50~@P4CE_OI9WWcJEE^K8VKW(EV3xuyVQ2?uPfROb)O%8@Vy?M zoZ4#E`g*c9jdD)|;Ul;@^P%Cssb!DDg<+nKn!X?2g%9Y%jaoA9Bsi*rbY|MCx4w?2 ztA&AR;1Vuj_{yWe7ywQ>gI>@q;f^&n}fM5e)EG9$E= zbky!5X%;yP66`xhHmSoOOyGNw#<=_?IB9nZbtSwr$4-nxx9Z`v#tZu z`6dTp>Jf_&$K*iIzIC4*lM&uSP8~*Xl!<02c`F^^u zDbYsVj4nSfe}xCY(`2YMw`-3`be(m2RS72+d&@)%Mdf@bz?=M zI;Z2|2l(md;-G8T`2${Pd*y45;KxSwCBEHp>h7I_=XXj(g{tY~sD;>Lc^|rU3_)Xp zMsdcyyfL;ITFD2{bN#Ib$lz)dK^5u$00#O>)iJ8Fy=}H!XUmd*y+$$ee~5hW+Kg#5 zlRI;M6YKn`9jMd+wb=-z)1WT3m7aAi=GQRT6f2n`I0NQBdbTuACfM*)Ls9^u(hyj@ z7Q0s7%}q~G*{JE|K(cIxYKai8Kp+C3@Nh@xu05H&hZ!h!#i>TPG*D?jWqz>q*H3g+ zs&=KXudbE9eNkIps{kAf{^(>zK1I9#06Ru4CY6SbpC5m^gh@W3z&KFw3oP+du7A>+ zkwr-?kWwsWCEC9eA>iZKvHt)KVrq2_m*2cCXc^p#>X&A+Cq>6@x1_SmQB_XWv!a4o z4*i7=eH3H2xz~X07^K&2{{S4=CWrHtIDL}m^0z+?x=+rAD!IY(yP`P82dDY|diqyk z_G2_UaT`aOJ}1R_t!|*b*jjY5r5tO8$DqbF>p9q>5k8YRjsj4J;CRrq_+-D`WLTm7mU`+7nT!&pgpWuzy`l_<&p_qdp%E>xY?8QAy*Lps!u9c`ERff=LD~y2`a~5wc z!3r_3Z0+CMTaxy(TeQFh^|4=tmdyj4;@6TJ)2iEiJ?N@#g6VsIPtZ#=^fY_?r<{V= zXJcPJ_rMyc>}5^@8Cg!jN&OCcohB_w z!qGm;olE|V_}fJkjL99&o$0aLE~k2r&_C;|F_B{{H(K9>JHW2|ikziYbO(jb^&c+( z0MP47tSq8wz^R=))5ftdBza6h0G{CP1N!JOE5KV7s-!hMGawDKu`Q6wc^}B*^3~&! zYOu~0VNr0Yj^R}$n#)No&Y8s|t6fhlbIk(AN=PaO6sF>#Mq4>2f9N&snZNBH! z_Mt;rSp~YLNhDd}r^~F=?u?4Af558^AiIpE+aM;wF>({<@9B#>Pi?N?V;&G#t9 zUh>cuC*cve%ws#b4UBgr0fU~MR~(}Bm+FXUtd#vfYKq%WaS}=QYPxwL@(L9vdLd9U z3y|4vK)_&0*In?ssMVxT)7Oiw8w>sJl9G#PX`!cuS|$T$B*>}8^$ZUsckLy((w`N; zbWsGo5o=YFI(fQ=QdN|&X-EJuR*%fs01LfGmz;!UIU@(GrbZUB{{UV|w^(UxVPi^l zsjNgN%iMP?e5eYfZdGLXxFBHg3B1}qY7O_NUK&_G(vG96r=_f#NSX)%NOLqqG8S`% ziE!mePlw?oq$snrSKQu$5Ef!bHvEIlsc9DQc#|d@7Of%TG-r#8A{D z1wLUs>_N1ZJUD3-gWn^a0p_0+r{6Br)|jcyae6^IlBpsA$O>F;&fKoyo_+D16I>;$ zI81bRi8kwZQ$t3RODq*)MKTCq%3z0*uYji<@-jQ~k)$JVrOs`ItX%1V3oJ(?h6Egj z9sBCBJr#ZxTA!sLyu62~Ybq~jU78wWER^)nE=~Z7HUW+?mSRah+7)9eJ)4ijZB=M` zYT4C2M|HPZT6!pLEUy(U!lGK*37;ijF+1f3c=%gA&IfHm+IPe{D#4rFLk}J%!hfoK zaQ^`8-L1C!hepi+0ffy%Qi(>04nJ_)4p{O+IA5vntz8zA{LuZb+FRm3>mFj!mr?#U z(Dw7-Lc=^ozWAkSzc*aXWV;&$M5peaUDi0-DBKYW7{r5M!R@!6dFN5<;nYtL_h83##dL?JI*<3+48q?dh9iutjP4%IuaRo| zAGUPUmbe#JTOZ2$u9LOtSw>K~+U297pqMAFfbGI8+@nci27 zUYeF5UkmyVZ#;sYQAVh;g;*PPVK=$}L{E31oShPtC8uj^J&-f#pQ@trJE zlkpWuG^To|#0r@}m8#w~WZnMIy$8dFDzjTvsZDzL)yh^uX(n&Vs$qYvC zsnrH)PyDi*9Z)K+qNkQOS$33e=&|P`>fQBytZ)?3Ui?*-yztdty7;YTvNEXya&hoP zSdXFA_+XHqfw@*&6W-H*ZV)<;v}LYVYGk%n)WpLK8Z=ZRzkS&n);*sinnZ+A=#c50 z?}f=R_{U4sw%^$lFH>x)kg^4z41jmu%mMZw4}5DXe#??K0_?`#DE*XT6BEHXYN=jn z=&LSbsu<>~6`G3pML)Ug#&cpMnhUFlrC#*Gt|1# z(#us00Fn^=Zh#+)9r2EJaw!WyFCY`|(NU26L4eWwD*aWmc8)hGA|!=PKBeHOXW-B^_nhgZ+HY>jxWR*bOG zYV=KWWgM%7eQ^y9BS||KP;ga1B>b>Q&a1_we1~H~fza8)`lH3WY*7Fbsz=6lq(kf6 zkTs;u1g-d7091?)yb!9LN7k)%X+C1g(6$Qqw)or;(A+4vSl%!RWWH*;C(l@SD9gUU9 z2N@aFsHPH7uut7!4W#@8`V=yv;IAS%5n~{fE=g~Ek&J59k|{~Gw`YX?C1pRF?y|P^ z;Z(jtjOT7WIXZ>yFBDX}SWe(pvEh$Xih8O@M6Mk2Fg_ROky`m zvWn5w^1)%a)QzpiW z{+QqUOHucGZ%$b#NXPFg4E0P0zC6TS{zPk4t#N6mD&iP{>Z(`Y8*bhs#LLvT z_++7hxBcE4rKXN1_wu74ANbV$bxL0GqiAiPOIk5DHk)CuQnz0T`faV0QQhilDyrF# zH8`$0cvrakzd8Qs?a9~A^;2rQLU%!?8(KN(ex6CCdq5Ue?a+M|dV-~H70|L$4~Pf+ zQh)rb&u?Be?E4$B&YgftyzZ}8{{R*(vfEJ*2~SED&j18qK>Yh_+K0sut=U+@8Kn5O z;SpOsMAu1So@#kk5V`xa?+V0ZsbFwOy;$x z8K3a7pe0$3Mhc8DBhZakn@;dcBxwYFl3OQk=DPd{QR(S#kXPKw$wzX%QPNXVM=a%{ zM^-NE?cDA>4oC#AeN+M9)!C)g>+vBG>Dp`m0G%}Vs>n*X3l$|gCdB^pGN{XbWNKy! zF5a2D$+s(&1v;q>jE)He_BxQMhE1UUC3W?`!!C%tbsf>SQCpz%S|C7aobenZBz#WT zQS~R&OvZ0C6zyqygE}wCeyi%cmt1u%)2J?z$5(N<)IC*82H?9K;d9wkVL29j5Y z9AGV$d_N>2FqIj?`FvpZ2m0!%p`KNM3CO}jQIQ$m1E?Q}DJM}E0;g+8d2$H)Td0@DeGg5Vj1ZXo=Hg#%&y?GL)6Er0vMSB7w&%k2Pw?HATvX`mtFk zY0V7Of}`(r^oC9Hvp>S}?p0+!3;zJLLxSA4ai#HH;-QguX{iP8sbIFtP!XQnZ!G1= zU6Kgd0yvDbBV?BEwem1a8YG3>AyQA$)~Zi7)Rg^0S`1En%Cc5f60BxTpzU%UOJ`(& zIWMdV89Ig*NwDb5Ri5r~SAxFlezE1>|EJmx*yN1wlZrpjFv_CBMx$x&^fsaw50 zv`bH0a;8b-1<;tllanr4SQ4ZRt7HS*93xq@s;Dowc`3xTjnlEu8XyIt`|C_&V#~CK z84b0D83&tL*`% zg3S?zl1Ein@!O_4tF7%8+ec2r1eBA=e*V+29|*?5k_ii(lZTqvboI_Wp1$S z0f&@Mrh++R!Hq!m$mdnaQ`+XwAAZRePY^!ycC^H{exPi*tENz^Gk@<;aL4=HH_Yok zeKqBuPZg)8*GUN5$nad_&~(>-3eDrEdX+^S@Jr`IBe>fl5y?a9GCwTq$o7;O1*0m& z@(I9jJRp5-;{-7zwD#$Vm+{ z+uP~#*3bBDBHVHo*oB{{1Z$xB-YPlcj;m~lPTa_Iw^byBgO#1AfFFnjdAjxGx|EPN z)bXDxnLtnYh#$*I6lCJCsK*P*4p^^Pee&mYsHl}vB3zLKc)|~v2N~AX1(EzcR;w6V z5csH!tF|Ci00Cwk{PC&Fz$lSQWhJUQDu`x^Pc8C1$6`nv@^PLGB*QjN$87x zs;8r_PxlGyE1*_omIgvpg~-HgK_p{6kIUO$W3)96Z0rHl54%w6W*aC!m0qdq%3D-L zXjz5BV@T|rp~2&JldsUj0b%z$vf4_}_DO2$0wa9VK}mhNFi zKhl+vqVpBEtAYt^4O1FTgt>;*hUy#$4hZE{=89@_2zP=)L4}m@86<8bbDlInb4}O3 z*g(3+)H_;xql!E6z`xjpcC{D9KE;2S6UQMOh$9H>VB03EbPB{Wi` z;fP}^28^ln+B<>mk)95VNO-N>D$~x!4Z*gfJcj08uC8CaR7uYHK3<j*n|E$S!uC}1`p+D>Kj9F!39=oZCjIK0$m#gxXB)e z=dD=Y_eLI`64(BvKF`us7uo4zsi>7HV_zu=8<;Lh*o)lqN&NK))RJi7Sdooj!r1yZ z! z^I39MQrQHS>sL=(X=tT=%^XokvPenJ8TS%#=s5$74PAS+8i#+p>-8SOvn;YpnVn^5 z-8gOiEo+MPa=%nrAreItbkI*#v89o=q-@3zh%?ShW9O?neG8cV+qRydSU-gNa3zp2 ztDx-3YU>+Vw_WL$vW7=iXj}uefdnwmW*Epmhu6NnV@q`cpOC#1!HQ4oFtEW1S zruEY|X=|;Ok}C=5+{Gjy;C}i7#aMtl4Cl~m2V2>iu{V7Kt*7N)qp#&a*`yAjX;r#A z?FG~pzO1-(pImrH3L_!c=8yX<9QJtCNhzc>_BM9Nda21s$2|VQN27 z7{i^j?9HEAGCg3V5bCL>?A^>r<2W=LX-8@r| zkH5mK=$jzi(SBq12sOUV)iw$TxAi5OdsWxU^4i#g3)wn!|3d zmAP7ZYO1T%PJef2rO~Zuo1-qn|X*iZ=^Z?w#=jeeU04=__PZbTc-= zMM+Gss}E4H#sZO!nrqX*BZr^Wl%#Zb2}afz0G! ztpW&(m1Yd244mVRJ8G@tD@1WHh#&$I{kX=;rC$ z9EZ(#oRzAsF@B7{v2wtB%CXQ z;TcLO3SpafzTz7r=yaZ{N&s@1l%#9%?L2Zvz;X2&9~mjW8^W($xm@6|KgMJyx3KT3 z?gJ*&>?kCZ@yG}RU4(l~dzHp9xaAb%cBQe(Ak$JF<wQVsrgrT%~%pGfs$53Jm@Ukx`a&dqMZVM?G(G-^mea1*_ zsXYf%^&2#mQ~mNKRW#zB6(M$Reag}2@hJ!K94;_N4C0pyHR2O?`r9R%siCphYpd(& z%tNIDO7Bewl-DDuthPIaraJj<({%$_p{O!VM(53xsQuy3c5n~?LxZ_V$T`73`6b6H zJ*%neicKF+bkr)XQp8r$lxSHC49g)5tnO5yIF*ZJoSnGP@+jaMQ|m1?Rkw?kI$dL< zf~hI$R-bv6p;a=j1WrN-oPZk#pNp|M(Dhx)2q-#2#Z5_3PeAuqo=~vOPTqUTG%CX( znnh6{Qp1C}!tFcoG^Ydtj|S4k(&r^=Q`;<%Se8>R+N-4`ixaGSFvV2G<$@i6$j;;= z;s-h&NP+cIM^4XC3)0_iOJ4U1VNA4!P^r9bGBg7Vl0gHA_W+>hzPNyLnFwyNwZ}&l zCAK=dqoOLs7*_?*3@ZsF9}zfUGlIn&cN~oJxSPpMTPM9#%{-Sn3R`SA0vMsG^WB&d z31$EQI0bQ>h6IHmu8%w*mrzoUsI^eFtNu$Wf#A%c9PsED8GSf&?O^vbhJ3p17ic+X_j3>IP>0 z>O2YYoCSx<+@ySo-bwcdQ4cj6YjcWvn)7YB$YP?o(bh~e`>1LW+m8PL2suBNbi#^o z;O+`^#T=pVW0Psv+g)30dmLv6C;Rdmo?u<5t@>k+jf0_)Hx{Z7Q{3>MDeamwIb*kR2aZOh(@Y)etj!n#!usuCdqsi@)khv{U^)ChLGP*e zp7&y;a~+O|9VOnnk#QUD^%>7?JK~M}g$C&wj@d3VU81X!Rz+x&7y$F1Z`0RQWsR}W z2bC0)7Z$XIA}ac3jb(Y&06&28OM87jnsKH)+Pv9-004+fbA-|qa7`9s{86gPf;X7pa`ZBGboio4Tb>nyMQ=7hzGdVH0~}tZ8oGej3%jWlwMt$ zfpbdirwC_c_*CO00(c*$vgR#_(HZ*w>&m)Uyx`#y{i#bOEYecT4DSh7nx39tGHAzb z#Z+e;_ZjW)tZCW^#{~HQ0Mf4F=V9C{ZJ(&Be_=IE9Q5-U25DY7T2Ut**lbdm_9Hxg zS`6&>JK~Iy`1JSkSo6jjGEjP&Ij%KT%U^Q0(?;YUHb!|F&IbXog9PV-qtn}4aj}t| zuWZW(ZRW0E_KC}N4$ zNZtVHgX@~QN(KImrD^3 zAl-DneUahyTABbGZaDib3&MNfRCs-0ppUC;*Lz(EQ5x4*(MuF?e`YxymkaC}xb)7V zE|LeIhvD()y(^s)fNNc4bst0Z!c@l;^tCEcz}qBD8C&Q^LH=V}uiN+Te752@iZm9KiVm5 zjIMF2f8qzqA0VkZ6Or=%l&_E$TzPL_6`OdK(>+_^-CQ)+n|$=IP+UVjG>cIT#9+FK zQ`BcATWKBfj&%;AJw#MUzfXUZ(K(LIs;@`=n);{3o1nP2mu9f2{{UIK)UTN?-1dz- zF+Q0%_6^%r^xcy_u1(K7LNe>w6LzyDbO%THSJQqGQF(T1Qq>ZTvs^02nNv}Z?Xd&l z_Eid_+g^q}92z_`pC1*TWs-Bb>RnCMr>j~jP}MbzKiS4IK=(ZH`ROatgI&CishJpBFt0H(B~gtfj4E%R=5t4)rw(@|MH4IN!H)s)OuT4a=Ys7HT; zhW!RK?F|QLDjx7S)u7%gMfaL2N*N?-Xwr3-tIvs*6$uKiNdPbep2Yn#sPzQx+7(&y zD617GRi%A=QQ7Dxxzle6Jy`wCD#y-ByN}-S#?=@;`mop58ZMJ>2My1^y34v-?9)fn zomEj+ajQ$cm7i|!ynl~XklxP%UC*M`vRf^b zc6bC7bdb^0XCf#NBINft)(nz4=X1@KuE^OKrV0vBoE{aGR~aOoMdqc-=O4At+E$;# zt1R6iVVpl$-06stNYDQO(&?OI%s!rLjD=Imf>`5?Fd{MDJLErqK(FbbFZhVU{Qm%j z0V)F`E&vOFf5%meoa2zS8qgQ6LX5E6gZP5u_0a(IDQpY0gHQshuLl_$O5-~1gmuDU zAOjz=QWk%N&XX4I5OP0FS9otUrfZK8KsJGymD7glEB9D99n{Isn#f|!d>t6BPH-BA@)!l$XlQ>6`R$uf}&7j9o>N(E=y zH^>hv0pnLJa1xSSKtw2Rl=tqPubZXXOI37{yuSq+LphMEZQf-~rz`<1OLpY*uFpiS z1bL|Q^xegl=T>@}uGc{%NY#Jqf~}%q2=hz39f^h+%CH+r7#Rmd3gvayIw$T|OGS>Z zh9{z$wsCK0=-G=&68n%5ngm0Bi< z)x{gZBqSV|*;!O$BPp|Y1Of>MU1*l6QLOeCitE%jxp<6BCMwGz9EiK8w;i6nI{RY@O+GbmxkGoe6y zA$O5d8sXHnrE03;q_JC-#DZGwp~OE66m@cW+GJA8<&|~L3is@AS0T6loax(E>(%0E zt1Zt|WJy_4NT~$Oj0?(1WsiC&47dPEw2h}aUoNYK@S^_Pthv#{M`P)F$~ow=)MBz3 z1JO?!g_0KD9SnnUKsTzCY^(cmKk^8JlwQ8ucD-Aor=INvs%`R`xT)hAk_HT^xeSa^ zfWgR<0fz;jp*_iRX3K56TPTco zi-5Q>2(hvFol36P20*GCBnIa!31C?9t_5)^>49ZD0rJxNBS0j z*V_AiB^`%+RW%cBo}pwBc@YK9?f(G7lgGo)wvqO5)dZW!rY|p@JocIzJB*7_)Tsm} zp6yLk%~K_HI}pW!u_cUbH!AyX9=Zfk&k0=JqRxDOm5=q0j2%B^>sf4e4v@9WEiFn@ z!E&m&)Kh-(BulqROA+CbxqYv zTYtET$%WvWnN?!j&gp?6hI5Um+pyYSJ^yfO-(raVZ9i@eb!21g$SD}+g1KRN6<A^i;Ofpo$-x0S?#5ep5P7l$!`RaMJ;rQsGhgrAvtd6ppaXm!^Oh(gDg%1Sz zSy{edj^9J7B0lDgU3=kcwr%6#DjmkdRdT6^2H~H|$8=AH+gz)+dJ1}ZqpD#AG#+8|Ab~m0ufK9T z=Z$N@E8NYwF7Z}lHfrqortVa6)6vscQ=l}}0Lp-jW99j3cx8EcY?onM)5&DCoP;H- z)(}XEcOFl#=1JC_3t9rsRBFGyR7vH%IgPzll&cS3$DK^p0hBYqD^BP>o?C3yfK|a& z6)bY%wwr&KC?o*Oy;ZPz;gwIg8qLu}Y}@Iaq<5 zB77}^2d1?);T9bbIJbJKDSo)Qwim1_0q}J@yTSi-{{3w;$@__Vyd789FrNA zHUM_WQ;%(VxFHaKzcWpAyaIhy25pbxE%*0PI#0v+dS?3fNT_0|s)<|5sJT+eDYhJM zD09RA00{#)2k{M9n^fsz3~~5RPfryNt5YM!+n}|3brsI}7?zP~toGTQ$Z8~L>4dC% zu|B`1G#eQagTOvuEO!^Z-wN7150j+23ixaKf}@~sk;sB6TJ1Yj$r}3kN8(gtfuDfq z(5TkeXS_ju@nHGpzKUpE*d4vi4!$UA zthK&sihLL8irUXUhL83|RL3nP6n}SVKDz(}{Leb9?JN=-sy8o*R*ubMQJ;hp2Nz&SAN>h>)9u`=GDLM8E03R%!UK~(S zIk~cey_CG@)0oF)3aSA29PzmP&Z3|!l=fYOI{23At8-FRG*boyAUVph{+xlx_RpZz zN%qR!uK6o)IKu72=9%exO*f0Sw2ru~Z4E6^rK+Kn29YXYAUtl2IFU{X$O8ltaKldn zjIms!*3!)pY`bIN_Ub<%sx~;^kLcnrkOXi={l1 z&hRqFwM|-MD$3~IVqlf`bho+=tk z$fb0O6$$tIOfwpdxl<#VQ+phac9qEo7&+CJ_GXGpHGeaYMN!(x#WmIRT`ka-3mx;O zEVqezcADE?TSXkt8^-R`(lUHu1dPRSazO9fUP4_oameNjfJj@BZIz+*R*Q7RJxN7Y z)OW3yIu%Sqa9P!)1a=LRxPm>wAo`K0qY^aH;WInHY1s28+`_0qEGa-LfIjdagbQ&%eu^Ge4o?YA70{vO(_=NTMa<+nh5Argh0 zu2|%DIAh1Rul3L+qai5Va7X9)POMuF<&g4l$9He#r!E5|CE4Ip!l%jrsL1SCFd6yk zGSYIS4%!n9Alw|eMeV>i`tjRD4*-EDk<~!1s4EjL;NWqV4THyQ>NhmIkJ&hO-cRLK zYax#qB_?3D7dZy;l;ahFHC}flfLkwgeVT|e`;aE}z_@bT^U&lgtuhTbBe5g{a zQ%-ZY5z00aPp~CV<*DLu2Gvbud!jxeewR_OYno(jNz>PiZy)~vGN0F4;b3?Wpj4-k zJ1cTyU_ka6{(ibc%5juV$xG5TQ%?<$+v;z0;iHawq8f^XV2U>tl!=M~<>zkEjAPv0 z0>%_X8r+^qI=bT(9IBApWt1|{MO8jh4W$R}j9ZD{wnD5`$_8EsBTSaEhA&Q7jqZAT z_fY=Sxd3O9f~q*9^MgVV$`fYkA7X#Ci)#!Jah)v+C-LtH-U$e;KV2nE5XE7&+^;AS z5|u2nh{SHE%ap#^BxU$WECFRB4i`Ds1DvBq@>Ki$*9hvQny&3nWVbAky4AgCj!EMO z_sGM`-WZ+(B$&Y&8OS|d3Jsuv#Y5A*J#@6sa=1%gmme~Ts%}(XTFWq$ktIt$Herd3 z61z)2a56LoFc*0gcFl61sCqOltjTAq6zv+rZ&HZ_fI6&d(x%b@1CTpowscR52gGGI z_J}MdEA_^%u9^s8S*xjJX)9rRg%}enHs(^B++i@JGLi<+bPWQU`V|u2)Ll7irkv1H zS5&paN2_{DT7SIC?Ck5i%T^$x7@P)-GMoa(oa>K*?gwDr02 z*#pJ_k87+_;Ynf`rtF+>03{Aad0mP@X?jj7nJAj!eXYJ#Eiy;t+d|f64}9Gb}(Ql_T;*~n~wTMI;gb08J$oAn+?z>7J*i>k1;Sf(rPT62sgd?=Q>x zY9}_bpmTPW^-VOk`GY7p&VGMgC8)VYt#`oT13uCZa&i9vq0wBbelU1-Pt{!;cY8&e zj(hyd3QqBlFpa>z7=y_Gj^4V|(Q2I>tq*s?7Ivpj-G$6=#IU-Lvc>AN!)l86X(;-Y znVl$OU`d2y5#;1=BX$7Cpa-2=?LO2!)RF1Mn!)o;+4^86idoG7ayi1uZdMwvy*&}7 zWRf__n5Kc!R8icOATYC zv=mVVV^J(;EtA<7KHTK~n)Fcieyd#_YF)5+4t+d7E6i&D0MvRoUPh2_K0QCR(zG5K zbeBWXEp=2>%Pk-b*9)VD^3T2-;9r&(KHBs;KFiYSNN%1nyTs(L53KDyR;ZIJ@!&8O zvg1iZ)YOqJL@RWFfh=wUu^!Hhcpp%8pyOtoIQuNR>J?J&&^1(mwId4HNXUv@Zv)sK z$MeV6Oj;LiD=zC_3oQL#N@(u11k1N6l46;8{ub^(nD@@2k}&a}RFO{jNG&T2>L0Uh z)2H3#u~gJa7IIZ)LjM4okQKiyeKkzN@xZ^z(Ppqe^H!^ISbab5UU`tUQ~L%Jo~f`hsR+lb1nTsmqQ z2@0w*G4P#SYouuz_wVGLtZ+A3K`yqDL1q-uK`gGaZVZjWgWo>-(Sgn~ZgJ`0rDowF zCl-cTsFHCbNf2ny0I>c;TP?;_vQb$nja^gjc_fv{21!du-GCV+l5hY6K7$&G@@?T- zjuR$Iv_W?Lk6^2UKQK)yfQ*Wy9C|iLz~u5WbB-~r=QQdZ7~zgqgP0jk9TirJ;af;+ z+8T<0l~9=KY2c8ksZhbfI)jH$=!^l#+@SXW>sgEq09Ok*$WCoqJV9a8z7N=}@;)l- zB@@jbsP65b&6H~*Bi?GB{{RH;!);=Wq>ev5Y;dWh7TPxB0P&D?)|GirPSaGP#Ek4u z{!$Xy#!my}3DGEJTe>XyPgPAQ+k?PAKHr!5>Noi%HOiMaN2U7U`>KU7Ku~d?i?}3u z4OQQ^=}AQmgQu3$S5r+bRYKOJo=Ju$3b_ZIx2Pio5yp$&WNm;e%~q6?&{vw)Y(5}Y zf1>L{TX3nQv(m>Qh8h(Rk25(N*?u{P8QtAT>_OL;n@Z;$jL%&A!p!d7yWt*w&qZeI zuMjRa38-kP1t^WEl1WfD;WwBAvnz~eoO>QKsv~$d z>=e_=_0XDiBhFx>%=J|xf<3nnkQ zfat77&GcMNbn+gYEvu<~MzVD6qJ|rPQCQ|%tddC#H5HM;=Xql>XFXOU4%%Or#`Wk&d!uy_~;Ic#t_ zJ-F5@r`5JkjiBF93M`^A!k=X8G;{@|RU41uH~{+U{92&yc~erkQgv$(#r&phWV5pd zVgB-*`u_mwtyWEFP(wP4uZ=Wggr*8GNMcz0{v)9-r(nBI)&f-{r~2voktA3eN}Af) z8<#qYX;}!rP(CLnkXx$`cr73qMA!hL^YK%6$VHG?_;z8Hu?+!g5H2~ zc-4>AelMbx?iyf222WK%uj$mj-B#m#b*`Bkd=x-tnwg-kE+=)6}J0vQsgqmP8OLGeY!lgs zaj!$5*N@RVt+m%HCsU@}-trNf=&7|n1oizyRF}25BVVL#^7++lvq0FuZTKONzBu`J z)$!UoOvO~tsJlt2bx1~6N7x{*rnJ&UbDAoO#D+DNSYrsR;S@2zC%XnYgl~BV!zld`BL?BcbG&Qlfa(;}1@F3()iRCsD{* zH+eL6_*i}7zGAuZqR(%3bHa{KwwbNGlR$EO6!ApqT|s_G_^0CC`^JvHuIh{AS zDoA1xm)|P9om*=!Br48+FjCfeoDvJL&*IuR`g5)=7lfh4jVG`atd8HP8UFyLqw)hO zs*l8)Y1(2{7asiM{4{6*WTg(>`a<;5spKwxWamGYqH~n(1C$owMnkb>D#P3XkLRi{ zAevWO!Vf%5tBfCh4mAM+!BYZARr}Q_dUuU-8I*u>P7k5{wBVbxT+z2H^k>3Ot^WW` z{3+>6{6vZ5rA2%k?2vH4`tc$W{(9MA4sI8eb4x9Up;vptiE1OBFbs2`WEk&}>!;6E zz}T=?u=SN)SD3agmU@W8H}B<^I6-W**d2>3-dlhPLIGr)09PY|a%ff;0s%?t3x`ZYGD~t_+EP?`NgOOp@W@nz zToT?vU?v3aa(1{lItIER0VtJ{=lgb&dRZ+u8znFjA^{uIylM`{AcR2_naKqUf&gIN znqHm=k>F4n}CqVcj4cc8F zR`l0R)Wt<>pmjAS1coYT<=oKA@rPLCULglBh?KA5AOqd3oFmUG>y2Mnbw}Ok<+APM*tZ>6>kISIfIs8rowVqGe=vhA6%? zjS!dE6fwZ$0(m&jiQ#dwyrJqkd9JiHmK#gK0jrTNBlc&OM-Hsp&@$vmk?&(BNOdGO zatN|{L{()&ymXy?+M49IYrt!9O4KWGr;bUWiO0brOA{x;xCdma&I*i3xyGX+*8;tv zw)EAOpg|pFM3wc-k;_K~MBZfk+2mDvedlP;npb%k%MHgk(IpyRFIik~RK|Xv>B(x+ zR1FP@|!3PWhrIcrAR!22x0kY zNWySXktI#Kt~#n&o}Q*7xn48K{{RE@I!`4hB8Xa_N_D*#R>rhdAX8mcd5nxCzcfG8 z9YR`0LagR(QtNGUQc7MRMmaojr3xh?*?TC#B;2HW6VAC6>>Xi0P~57m_F8pl>72z= zbE{I&M-KZ_9myV*hwYs)J0F5n@L|50BNecu6 z1d-3nRb*?Z0z!@Mb456e`6v2*3ztgL)6tt9wdmwKma@@aUwaD|qw67IRHBtGh&~Idn_GJasHwPf(4%+i`LST@w!-cZP zhqO2+s`^@n=on5s)nS$X@6h^vbzUY0U3JQLWmWE;9aK}~(aLHgANf@uiytN*;y*o1 zTWNabXWdKE7OR{6((y!#GV-f3jmz^^AK-M3U^a_hL9nn-5qtrPqM$vlS%_9AWTYSd zVc+Ylm&LG>)ui29tKw9vH-}d)n7K(U#*TSmSpNWbG=t2JG7q@Vah^!jdjaSa=4QB6 zr>f#rM}IOWQIqiHzb!^u7&jFAybxOZT1+xRoDAv1FljhGDy?&~mEX0Bk8pKifL)RA z@}#v~GgMpMA#_KDP6L2PAC|8N;YcW}*H78}`gtniiYl}VhLR~2{GsW&N5rG%Mm3$O zJ=ooU+R=hQ4RC?qsis;9t5Cr7kyQQA;-)V&IZfdCTl~U=1N*?GGw|-+#-!6YI0pgB zo_8InRg><6Qx9yP)-nkB zX*=c&T90#lQq)#jWm!|srgaArz|4#?2smIt91=VBC*MrTTI{r=VDPVfKW13wilU^L zu?mM|8T8;G-PDvJ=U%X7}T6dJi7U0f)2*R8JlbrFuCnL~}BSB}wReAON z@L zh59IOlTTl4nF`4qa5MQVxGLdPhGE)bM{XjoOK*5zQxt|n$?*u zqFbyN7G)FExpJiU9~T8ju*b-1Nuhu=fsMZJ`}?cSoI=vr##PFP$Bwn=`g*HRPW7GU z8W`g+EizR3%T9ygD;ShNmT*oyy`*qP2-Q6_voy8OIO9Lsta2^kXgw45wyEg*MLjQ2 zK|RW7U5wR+d_6>1%7r|F`54IqB$7zfF+n$cTHLh#N9{^5*OE313uLwZt$HH=07V@3 z{-sM~t~jE*K`b&UKf|~wLPxr`HF9X))|$t^ehSXVHv}QQN#mumSWCVBkjJ0J0A?Sr zBl+tg+ppqsaMS3I_!(W-BdBGshQ(6dCs`4YR@@*oMP$z9X#Ca&Wjv3CbMbdL)y*b@ zP%cKak6e8hGI-k|+-x7Rx_F!879&wPndhYj7lK*XG7*o2DL6*K!60V@dTQjl`9KVK zIQO25&}np``3DEzf)n6(j`qHulCnF6lI1NnT0>b)R~(;rQ?y1ly9EOwhI5m+01mF{ z`$9Jxa~wYP^i%6QJh`B68S%ooc$xN}m!~Q%2d5~l*GhTmO-)MHGx>poviVp?1}4Ji zEF19yjCcA6W@w|=!e(=b@Wp<4UpMUuouH6Mw*G5JgT#J{>&lq=XZF1*y4A#w_a$Q> zNlFIU6#IOsBtxDQ^Mk?38mB&jc1B$G9lrBigaq))14$Noe|%c|7rqKc!wWs&Ws)dwlh9DXXGpdgO!>*x>tP)14I5p$h3Ie8=#w z*Ij7Rta#VTta`y3Lg)K^G2%IEMz58{yIDBx~v4M-bfaWn_K zd8uK6(;6Hv$^QTvJaX!P9(vM#qUuVemA)wG=qZr>)`A@8%7ss1z$K3?c|L$tnqB8> zaM9EEK7PXAiXjtdD%Gh=IDN~wALCLns2blDBZ%@=7{fBxGZBR{spp-lHNmzl1;qH2H(j9~((raPGD z=YmJ;s`x5;>V#b2QyJ_Oj(Nb+izEc}7f*eLdbC&c&s21b(t|a=p=+rTk7(t|U=PYv zWB&jiwY27eYnPwZw`|+G71SYCuEI9_CI{dT?4CgWc>J|!fE6~Abe9!ZQD0!1qG)94 zs1_D_R;rWAWSL}TR+D*FF_bF73|n)MPC(VlDlC7;l^X94PSn9`xn8=ChJU+5gs6_H z6^dzMkTc63Sn#@E{z+j66ivPlrS7Diy|ovlAw%h zh4Q-%I7O5EwT(&VlC8 z^HHpQMN(s?rRl2-!r3%1G!=;sAWCz)4$2)^fD~Z~1w#Tx2^t=#_0K70$9t)#Gv4}^ z8X9_dpDBZ(rzG#milX1Ir*{Y^Bh}}g$I$KYm_LQrds9Hcj=`{8+5kIQ!CSLxI$4F0tcFe7-Wc)W&;Z# zR*VdDopH!Ux+w|M)jzPS>3XK`9aSxT7=)-Uu*DEStihisTPHqd&|i#$jA=`3QC(xA z_N3VQcBw7&cByWc5LR@gk8F&FLnEsXHL(zPSq@`ep^qVVXjP-tIP&#f`kf_$2&@Nl z1zPg6OSAZJyiYQB}} zqz-_iDcl`6Dt-RCTLXn6(E2OI2N^tG`7dq7AJ=)6um32h$xZW{RR& z5<9GBd4rYnBz@%e^euso+VlFUj-qbyX~xd!jbgh~Lg^#1AjC-6p)g0kIp-(jG4s`6 z6~b&Mq=<}eQn};;F_Wg@FA5!Xm1cl_;+?#s{{Xa4grDiI!A6x{=i%KPjhJ^)vvLB0 z6#oFic+fe*TBTRG_A6`;UQ-AD(o(L0xM&>W>O7Hx?~hX=+)Uk3KaqF!}CM zGpHX69fICejFrqb)0LR@7unjTQ1V*uvm&0+Rl<`G`P3=>LDWvuP_R|jYRKd)!>D{4 z>YHZm*{EE9wSu3_U(5rMsGi4;LbfN3%7b_4Yn421)6}}Za1A$?pZT=yvT{B9;YuZG z77z+GudvTiI?S};WB6o9+1Q@MahwC{4<7niBDx@7zq*anJ+uKTM^aTmC2**ZY>`b6 zGI?==75#Y}bMoU=w9&XUgM;+`)wp#|k-r$CfmKwQ;7Fs0q$qeof0_B}<09HiiL#}9 z_kqF@T{LW05-Us2;-|3HiX^Ca+D$4wY?(xMNXAaVk~FWTgrwG1jbO3Ga<<1)DohKn z;*oKZ4<{KMYszZo5Xf2xrI9m;c?u=U%STSca&z4Vc^ijpAeG?!X*w&mNkdCYIQ8l2L!C zFDAIuPXtjuC1})x>PCD0b;1UNxM5TfG#r$QdywJCWA^0AzwU6GdEnJ@8KT z;skJ4({%M?3WOjuv6z-ToM(KP9QV($)soqP?&24fD3PobO5eOX`wGKukXGKVa#Dql zm@Vb1CC9D~+-LpNW9_SBM>NzXa0AGHDi>-mh0s13bYF%&D_3}e$7+sS<%VA>zFVa% zvR6RZC0H5Md@KPf(R?S<+gkDHBhoZKZ#{WcT;lf-;;na1q=M@~4P7*%q6&e!IoCfQ z=iear2a${&M)@ta)jVwkSX}+%h?+__u2@UR8Bm2#e82`q?EV}9_`fcA))uN3%F;-q z=(e=_zwpJJgkQry4z8DbK$fnuvd*=X?1GsH$}3|9hDc-Bk8f=1_J>d1Q;tW5a+g!u zCU%n1N5u3&EWJ6={R!2V%RgA9b>@zjY*1BNRel!-EFD;{Bw%g?fJZ+1`lDYw(6P`^ z@;}n~w5*MjIBE$#o<3+Sam;itGqbuA%PSDlJgIc~sw^~vkSN2jNnx^)n$KU#fxrWVwNH%hRcv~; z0mtj9?vyl1$G^Ik9TEprbly1lN$>Ul0Is0|-C;@p02GC3IL|rc62$4wdZC)(OobJP z1RgoVWBTd?UJ6##@}K-CIR#IkeoxC#*Ht@kX+gQEbnmnSpNOCG)Lbc5da)lFTxaXw zP<%I3r58w5X2OAF*xt&cBpm|L$O+owIaiOe?^Oruw;tip`~8>bRjX=LR#Ot8T~5)TLC^GItz+UAI3m*j0MYx+`q6%* zy*(Th6`78i;6Wj$kFhRFpe#-pWsV8Pduq`ehEyV`c&K$$oiEaLaz|4=Wp#VTsL)ka z%QG}jFbWvEDiP*C;*r^aZdk5`C$i-}%YLf7-KZp|wcIGIoIZ@Z5F1OkyhWB%tN?9TD9+r@^#K5TsA%KjM!5kJx!!7~ZNYKDs zP7um|tLaM}zLK(%j;dTV(<)UYf}$B6ce2K=HvB_(`#s}=tCBY8UxI2UlBPpl)cs)u zk}r`3vZhF-h|eR$@G|*qRV2bl_>m++8XTR&1dcREzkDvLG>jSutv01g-PXJBa#o6% z=a#)-s{v%hUlNrEYTz+ZhUXx$*LWex6ogb=tk&z|3r9>(Uv!9yh&+mEeAya6LrBi0 z*n))P%mcaYr2{xbTC-YTsO776x=~O^O(DXpGP1mB@%|upmnYx^Wk)9*3~1ntrQ~pi z+Po~aTj(IAsd=m6jz2HSbFvaP2;1PvIAsgDcWosBz&aV~i6-q^qg17iD5FG;5kzRK zfDw2A4`Ih|mVvTYFL1g3(H_Gi8Vi$dLiSz99)nzpVd*5FoGRzG2l!|sOV8Qrxh1oW zcPOT%r6oxtgWH_vT~v=KT~z|HAwdWGORhv9mA2;RBY;TYa&)H3JQZ56w_u;*$9(gp zDeuKr?iTJ2Q2hwfk(DK(RNg#Yyd}EQQCw-w)23#Pm`9)WifyEBC`sjZw(Y0;JafjN zE@>b;9%z_D!6TK>HXH4e#7hi!YaL`a3K=_118xBO9Ckk=jA%Kv%A(*~f}mV&Gf}a0 z83)jF&X)lWiXAO#LvW5+W8FG>mOTA^4m~u43q(;?J2lEEY0?FZ35j4NBjG*IJmh<8 zfE7!Xf}Wr^4I(yjR|6U#5V#<{ab0GjvosfZ<&GMxtbcZmo?XlF226G;Kp>Ji(mtyy zDGNZSAk~+9wPbX*d!=dVn!12IyP8I(I?<27t-J_j^(aZt@SQD}d?y7VbB#2b{m^@t zR@gigsCasZsV-L=g5fB)5mf$ z=@k1p^>yC&*1Kx@#V4ewNt#PtUBTX3iE;4ljlo^RmiSwC2W)B%(bLArZIS%IR&qODb=nJH0QKaD;X{YEst zNoueae&gX)Ek;ZgQ$|k!HXVPTKhHvw*;OmYgEh{K15#BW^pF((pp632)e!duo%ILV z#u~lSRCLTQ-Mq?Og#B`)AI}<1qzoxL&v{v`=j_8zG!jKfYVE;^Bn8}{`u-q!{Pd1# zaKe~PwDPF6zhippwk0}_We50LnHYcdF1T(A$N59S4R5h0Qpy0Qy-??$_Vlu@N2g+R zY|`)ZKlrH@Z?b1ybmc^jO`78o0}BO2k;$C;Z;v0(Rp$O4`JPq*?BC4)lo{c=1+Cq(Sg`WyD!YL(> z$amvcqU`d<+ntXkY9i7@NvrZy{)y;Yo%m8gRx6~Sh%i=+WtBio=61xYe>%D z_?78MYz^8nR+lGC&`?<}P}`}J5lKxclo+ujOc0zgI6RMiD_qADoc%wAMccP-cU8|Y z(Mb4Lh>`&K3imkV3};$o;Shq=xZt=ic6Gh=CDEK zGO^xI85zz$Ep?I6E1Qc~+Aj$@imWqRpq{w685HnHcVo9@$Me#V59$j&&Sz4OT7J$R zA=XT-A5H95CCgS%6eFK-Sey^0bsL()no*e};??`4zQw&^SYvyg&fRQ6ysR<3OquzU zv3MU{E1AFHuliNpDCiccWcv;D=?>|0{{X~o&HVK+iMMAzbzHg5HcHhkb|_(%GSPwq0!wqA zdHl67XdMt-+t>ahb;MU1-kG?_BS*~wE7NQe{C@>NlZ-xb@q!d%w;9*5(`g_K60}`s z`t}wa`nfljQmIq*t~+X=v_0M(uQX9bKbWZ7i7P6ea(nvcxz4RLX}dwZU3!H8 zcB4#pyBG6U{{R600BCrVZ0ZWX+AVCj(AK1HTR}{T1$xMZ^DQy`*mcKs+mOQ})cGFM z?B;1@1_M{~TG~mBpq>c#!n@xnE*8{`Uh3_ML-(lCIgOp!%df@eZ1Zu2ARaUF8pI*( z!^rw8a!6ZSin`f4I^|N()PJS562jqRLqSUnF}n8$AQC|b-&b14obUPt87_8reU#3i z@DHWBuK6X}p(m%SoOj$JNky$P8fxHA zv{&2a`PUK4Ves{M!KKT;%Z z=il8vjwDhy5<3vU(S;6h`5v09_?HlqyDl6x-R?}SQT*q5OI#obaoUDO|} zmZpM{;y!F9V6i%FAS|k?v9{pE#GuQ9ISawIFjE3*z*A@IJ2Ky=meXYYvvp@=o~D`= z6Bk&Ol~nC6*-~+hzkrTN0QHMX8b>N@H634bxJN&gZlR|QpDs|u&NCp(7E>zZc}=(A zBjN>)m?axRlX8OHXzCh!^}@1YUrk0sQ_{RstQO%?^1@Nvd`=!UQg(o34!fVWiNQjs(deVeO<8;pS$c-+ zZnjiPBY3U!l?at;$h+AZ73|1%km~rtoGR#K{{Sd}2(XRNbPa}%X(VbTt*M(Hbk!qq zn1ZAiPy)B$Q}F}aU0oLvZCL4P;-scpYIu^GnlXfe6%oXv*eC!G(_Mwvl)UUGXd%8s z8tbGN%ek8v-SYMQbPD9}-7$lXPo|Wn9%=DT0+Mol^TvfP!c9uWM@{F{O&u&b{{U-H z;v_#p*auxmge|Ys?^PWwEEP4i6GrmJ-cg1ZRVo;XWeN^A55gP1x~&u+sk+YD()5zl z-@20DVuCpqCyI=D^CPeG`>B|&Z?UIugPqTG-I;0iF7$Z3mgGfK{PG?L8nmp=Jo zf0hT!Tt#6UJyctPmH`T_9c65i%8_|gaS0tm92ab|fQ#LTB!So+bzTssOdbUUTi=KzX2TR<2gzCP?f(fzE*lgF$5eFLeA;^y5?*gpW%q$jAGH z?P2Z6Z(nUeBSmFcjoDnuP2yixco};Y{WEc>q@Iqgsj8tlYD$F90$6eZJYX;ZhIk`Y zn)4sRay%8{#>ze*y7$44w>L-lh9I@|VTYve&Y5SK{{Yk-x#4DG!Nv|44)_I*rW;-Q z4ty4TnpZO|$Xx+hVWzKNG7_#m7a)sdQK7fR8j_ z_8Dwwbw>qAuD9Fzrr~d?t&(H+K-($90TYrz8^%5?5y1U)g`1saMCJ#OPeo&_j*j(7 zEgixLYbjj+nGKQA|K0h*__Qe`jkyT~=Ev61t|`D5*spYO*wh#t9&0 zw-bz?TV!4t3~s(?zLhnZO4RMVlvi5iplwZ~Zsme0ILMwe=lEd$d;gRcUR@8YqpnUb!=*9EA8O6weezp3PfHyn@ug&ru{sG=Wk}EBAZH`CGxOJ- znZ=_D?9ZV?l^3CIwmTn9Q&ZL6?C{pe%{1U#BR>-yjFQ}tFh4CbC;$mYGB%8-{7Jb~ z!PB)cS^9<>CFNBrA*l1C9|$?a2RPcpkDsQr^pS6n7c+Bx_$cR)wZ93<&bmtR;|p}f zW%gS8Me35D%x}BaK@_{yFkcfuP_71eJ+ZAhq0o1NDlI3&`?Xb*YD;(3kH6Uy=pVFw zqpE5nqq|ZIEllJHXNlTMcFr&upN$DVxGUQvYPP4d9}7#^Y!4n(@aq{RHP|I-_R8uj z-6U01lJ1UN?QNw=AdqsW*CSY9gOIL*2(=2n)fH_<#EoRf@iGEFylNKx5L37rkBW?HZ`rWO!|GTQ zp|v4#>P4tAzx&H?FYpR>UulH zqpNMVignGpGO$T5+{CC)#v~czjF8yl0|z?Jn8?S64is-L4!(-jZ@%1bb?}>2tRAuQVbKHMm&1z!(=Wr!H*Q-<<+!dXTCdlBYW`@`A2ZHR76o})|ud_ZK}8@ zCqBnK@Oye|({_HcC?hR;&DFsqjs=S(>kqTPS#-tDl`XI}Sl~vv z`ROkfnyE~;SgLDVbb*+kKr_a>B`GTd@!Rdi(Vh!UQDy1MWa}=ehbbaQAW9lX{{Y^} zgT06L5M+9hraN1i56h~%7M0J-uovgIirzVOpN)5xujH?>YX<3=jMH zX$c(ydnF`=mymJJa$Jw=jZ0;``}I*4gMgY01K=NzBz#HF*SXH6Cl*w7Udr-#*@)fA zBk=G)UIu`9AdrUITQrYE;PZftLh?Vj$aS>?t)Y3T+p<>y>bDte zy)#pOp}C7~HNyFEnc9YWh}Mcx|i7w=0I{YoDwmvAoN{85qx{~P!`L3V;bIij-K63=82@H zj;W;cA}EMq+(biRV&Iqa3vW~0Xw~6&Jra^GmGI(%uANqTk|I=^2MXc*xOXa*AtFL! zOn?9fY4y&zb(f;`k6+atD_>6y3)||XO|S`SO2aCL1xn0_2v$&Ygvy4-52~9e5lRnB z_! zmgl!Q=R_q=tvYVt20pFn`e@ao%6?BOmX)d}c>`uH_UDsk*}7(y=VJ%iy*H8_FTJ(@4asDP|^P8(;trC&?c@ zDWQbG6O~SjuWr}aqgW}5L+b)JGMs_LI0 zJJe=W{mx7zM&AG^S%_`BhEF<8s9E6^ln&>)_4U&(K>C`@mzy1R9JDc9BbGd^Ec3iE z%D{$D&mYF4AY_kEe%e=CM&^r7$89%xPe~KHD@n7=Aa4jsP;tmoK*;*@qI1gbsBLmC zNp48?^!&86yFFEY=T0Q=!w+3@Y6Vidg3h-~bxkCP%ZhYp+tY$Eq;qjefugv}#g3GY zrh3SdRi=9CPc_&MJot%LEHT-Nf_}c5r6YM)<8c}AT_PIT69L+(~3r_KHK6j~L(#sP@%?J>mKMs

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 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ͻ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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 d6b158f..0000000 --- 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 f3d55ee..0000000 --- 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 a42ccd6..0000000 --- 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 3775f8b..0000000 --- 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 60fc9f4..0000000 --- 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 b7b7955..0000000 --- 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 5d5a13c..0000000 --- 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 43d061d..0000000 --- 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 063a6cd..0000000 --- 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 7657240..0000000 --- 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 3e36313..0000000 --- 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 77a5cdf..0000000 --- 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 b03e23a..0000000 --- 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 eae6576..0000000 --- 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 4d8d15f..0000000 --- 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 0f98842..0000000 --- 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 def1354..0000000 --- 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 3e772fe..0000000 --- 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 6a6f882..0000000 --- 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 fc0c4d2..0000000 --- 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 a5a8c77..0000000 --- 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 8540b26..0000000 --- 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 81c14c3..0000000 --- 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 afc8d70..0000000 --- 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 4d2b5d4..0000000 --- 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 5e47804..0000000 --- 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 b73008a..0000000 --- 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 56e13b6..0000000 --- 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 4ac9cdf..0000000 --- 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 16dcdc0..fb42742 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 d5f2cf5..e69de29 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 5beda67..0000000 --- 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 52a0b39..dc6d5f8 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 4599b28..0000000 --- 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 af3d6dc..0000000 --- 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 f3fec90..0000000 --- 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 5e3e8ee..7042cb2 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 0000000..8ad915b --- /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 0000000..37a757d --- /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 0000000..8dcbee3 --- /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 f281d1f..1f44efc 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 @@ class ProgressBar: 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 0000000..daa0490 --- /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 0000000..8fe49b6 --- /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 cc381a9..027628c 100644 --- a/tools/calibration/aggregated_statistics.py +++ b/tools/calibration/aggregated_statistics.py @@ -107,7 +107,9 @@ class AggregatedStatistics: 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 3df403e..aea44fa 100644 --- a/tools/calibration/base_calibrator.py +++ b/tools/calibration/base_calibrator.py @@ -18,14 +18,14 @@ from abc import abstractmethod 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 ..utils.building.layer import Layer 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 @@ class BaseCalibrator: 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 @@ class BaseCalibrator: 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 @@ class BaseCalibrator: 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 @@ class BaseCalibrator: 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 @@ class BaseCalibrator: 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 0000000..7ce0bd0 --- /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 d649304..1763228 100644 --- a/tools/calibration/calibration_configuration.py +++ b/tools/calibration/calibration_configuration.py @@ -158,7 +158,7 @@ class CalibrationConfigurationHelper: 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 aaad6ed..b660802 100644 --- a/tools/calibration/calibrator.py +++ b/tools/calibration/calibrator.py @@ -18,9 +18,9 @@ import platform 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 @@ class Calibrator: 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 @@ class Calibrator: 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 @@ class Calibrator: 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 @@ class Calibrator: 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 @@ class Calibrator: 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 @@ class Calibrator: 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 @@ class Calibrator: 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 @@ class Calibrator: "(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 @@ class Calibrator: "(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 @@ class Calibrator: "{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 89e117f..78570e4 100644 --- a/tools/calibration/command_line_processor.py +++ b/tools/calibration/command_line_processor.py @@ -18,9 +18,9 @@ import os 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 0a20f83..779a637 100644 --- a/tools/calibration/command_line_reader.py +++ b/tools/calibration/command_line_reader.py @@ -18,7 +18,7 @@ import pathlib 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 eba75f2..8422b35 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 @@ class CalculateAccuracyCallback: exec_network: ie.ExecutableNetwork, collect_layers: set, configuration: CalibrationConfiguration, - statistics: dict, + per_layer_statistics: dict, normalizer, ignore_layer_names=None): @@ -47,7 +47,7 @@ class CalculateAccuracyCallback: 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 @@ class CalculateAccuracyCallback: 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 @@ class CalculateAccuracyCallback: 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 @@ class CalculateAccuracyCallback: 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 @@ class CalculateAccuracyCallback: 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 @@ class CalculateAccuracyCallback: 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 @@ class CalculateAccuracyCallback: 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 7900dc7..9ffbc77 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 @@ class CollectResultsCallback: 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 @@ class CollectResultsCallback: 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 17b7cdb..009f555 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 c762cb9..6a49b9d 100644 --- a/tools/network.py +++ b/tools/network.py @@ -18,6 +18,7 @@ import os 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 @@ class Network: 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 707bb07..dd73ba9 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') @@ -67,10 +66,6 @@ class Layer: 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 d318e46..d3c6ec4 100644 --- a/tools/utils/network_info.py +++ b/tools/utils/network_info.py @@ -91,13 +91,13 @@ class NetworkInfo: 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: -- 2.7.4