From c85fb74efcc9e01beaa3accc83b4e3851b85e7ee Mon Sep 17 00:00:00 2001 From: Vladimir Dudnik Date: Fri, 10 Dec 2021 15:58:23 +0300 Subject: [PATCH 1/2] ov2.0 cpp hello reshape ssd (#8874) * OV2.0 API C++ hello_reashe_ssd sample * clean header * fix test for changed sample cmd line * adopt to PR-8898 * sync with PR-9054, simplify code * apply code_style.diff * sync with PR-9051 --- samples/cpp/hello_reshape_ssd/CMakeLists.txt | 1 - samples/cpp/hello_reshape_ssd/README.md | 39 +- samples/cpp/hello_reshape_ssd/main.cpp | 375 ++++++++---------- .../reshape_ssd_extension.hpp | 158 -------- tests/samples_tests/smoke_tests/README.md | 2 +- .../common/specific_samples_parsers.py | 8 +- .../smoke_tests/test_hello_reshape_ssd.py | 4 +- 7 files changed, 191 insertions(+), 396 deletions(-) delete mode 100644 samples/cpp/hello_reshape_ssd/reshape_ssd_extension.hpp diff --git a/samples/cpp/hello_reshape_ssd/CMakeLists.txt b/samples/cpp/hello_reshape_ssd/CMakeLists.txt index ae7283c3634..b2b49866660 100644 --- a/samples/cpp/hello_reshape_ssd/CMakeLists.txt +++ b/samples/cpp/hello_reshape_ssd/CMakeLists.txt @@ -4,5 +4,4 @@ ie_add_sample(NAME hello_reshape_ssd SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp" - HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/reshape_ssd_extension.hpp" DEPENDENCIES format_reader ie_samples_utils) diff --git a/samples/cpp/hello_reshape_ssd/README.md b/samples/cpp/hello_reshape_ssd/README.md index 145cf86d334..d4035999ad2 100644 --- a/samples/cpp/hello_reshape_ssd/README.md +++ b/samples/cpp/hello_reshape_ssd/README.md @@ -1,16 +1,14 @@ # Hello Reshape SSD C++ Sample {#openvino_inference_engine_samples_hello_reshape_ssd_README} -This sample demonstrates how to execute an inference of object detection networks like SSD-VGG using Synchronous Inference Request API, [input reshape feature](../../../docs/IE_DG/ShapeInference.md) and implementation of [custom extension library for CPU device](../../../docs/IE_DG/Extensibility_DG/CPU_Kernel.md) (CustomReLU kernel). +This sample demonstrates how to execute an inference of object detection networks like SSD-VGG using Synchronous Inference Request API, [input reshape feature](../../../docs/IE_DG/ShapeInference.md). Hello Reshape SSD C++ sample application demonstrates how to use the following Inference Engine C++ API in applications: | Feature | API | Description | |:--- |:--- |:--- -| Network Operations | `InferenceEngine::CNNNetwork::getBatchSize`, `InferenceEngine::CNNNetwork::getFunction` | Managing of network, operate with its batch size. -|Input Reshape|`InferenceEngine::CNNNetwork::getInputShapes`, `InferenceEngine::CNNNetwork::reshape`| Resize network to match image sizes and given batch -|nGraph Functions|`ngraph::Function::get_ops`, `ngraph::Node::get_friendly_name`, `ngraph::Node::get_type_info`| Go thru network nGraph -|Custom Extension Kernels|`InferenceEngine::Core::AddExtension`| Load extension library -|CustomReLU kernel| `InferenceEngine::ILayerExecImpl`| Implementation of custom extension library +|Network Operations| `ov::runtime::Core::read_model`, `ov::runtime::Core::compile_model` | Managing of network. +|Input Reshape|`ov::Function::reshape`| Resize network to match image sizes and given batch +|nGraph Functions|`ov::Function::get_ops`, `ov::Node::get_type_info`| Go thru network nGraph Basic Inference Engine API is covered by [Hello Classification C++ sample](../hello_classification/README.md). @@ -80,13 +78,28 @@ of the detected objects along with the respective confidence values and the coor rectangles to the standard output stream. ``` -Resizing network to the image size = [960x1699] with batch = 1 -Resulting input shape = [1,3,960,1699] -Resulting output shape = [1,1,200,7] -[0,1] element, prob = 0.722292, bbox = (852.382,187.756)-(983.352,520.733), batch id = 0 -The resulting image was saved in the file: hello_reshape_ssd_output.jpg - -This sample is an API example, for any performance measurements please use the dedicated benchmark_app tool +[ INFO ] Loading model files: C:\temp\models\public\ssd_mobilenet_v1_fpn_coco\FP16\ssd_mobilenet_v1_fpn_coco.xml +[ INFO ] model name: ssd_mobilenet_v1_fpn_coco +[ INFO ] inputs +[ INFO ] input name: image_tensor +[ INFO ] input type: f32 +[ INFO ] input shape: {1, 3, 640, 640} +[ INFO ] outputs +[ INFO ] output name: DetectionOutput +[ INFO ] output type: f32 +[ INFO ] output shape: {1, 1, 100, 7} +Reshape network to the image size = [512x512] with batch = 1 +[ INFO ] model name: ssd_mobilenet_v1_fpn_coco +[ INFO ] inputs +[ INFO ] input name: image_tensor +[ INFO ] input type: f32 +[ INFO ] input shape: {1, 3, 512, 512} +[ INFO ] outputs +[ INFO ] output name: DetectionOutput +[ INFO ] output type: f32 +[ INFO ] output shape: {1, 1, 100, 7} +[0,18] element, prob = 0.781129 (109,52)-(342,441) batch id = 0 +The resulting image was saved in the file: hello_reshape_ssd_batch_0.bmp ``` ## See Also diff --git a/samples/cpp/hello_reshape_ssd/main.cpp b/samples/cpp/hello_reshape_ssd/main.cpp index 13d30a8c50b..682edc45330 100644 --- a/samples/cpp/hello_reshape_ssd/main.cpp +++ b/samples/cpp/hello_reshape_ssd/main.cpp @@ -2,263 +2,206 @@ // SPDX-License-Identifier: Apache-2.0 // -#include - -#include #include -#include -#include #include #include -#include "reshape_ssd_extension.hpp" +// clang-format off +#include "ngraph/ngraph.hpp" +#include "openvino/openvino.hpp" -using namespace InferenceEngine; +#include "samples/args_helper.hpp" +#include "samples/common.hpp" +#include "samples/slog.hpp" +#include "format_reader_ptr.h" +// clang-format on + +// thickness of a line (in pixels) to be used for bounding boxes +constexpr int BBOX_THICKNESS = 2; + +using namespace ov::preprocess; int main(int argc, char* argv[]) { try { - // ------------------------------ Parsing and validation of input arguments - // --------------------------------- - if (argc != 5) { - std::cout << "Usage : " << argv[0] << " " << std::endl; + // -------- Get OpenVINO runtime version ----------------------------- + slog::info << ov::get_openvino_version() << slog::endl; + + // --------------------------- Parsing and validation of input arguments + if (argc != 4) { + std::cout << "Usage : " << argv[0] << " " << std::endl; return EXIT_FAILURE; } - const std::string input_model{argv[1]}; - const std::string input_image_path{argv[2]}; + const std::string model_path{argv[1]}; + const std::string image_path{argv[2]}; const std::string device_name{argv[3]}; - const size_t batch_size{std::stoul(argv[4])}; - // ----------------------------------------------------------------------------------------------------- + // ------------------------------------------------------------------- - // --------------------------- Step 1. Initialize inference engine core - // ------------------------------------- - Core ie; + // Step 1. Initialize inference engine core + ov::runtime::Core core; + // ------------------------------------------------------------------- - IExtensionPtr inPlaceExtension; - if (device_name.find("CPU") != std::string::npos) { - inPlaceExtension = std::make_shared(); - // register sample's custom kernel (CustomReLU) - ie.AddExtension(inPlaceExtension); + // Step 2. Read a model + slog::info << "Loading model files: " << model_path << slog::endl; + std::shared_ptr model = core.read_model(model_path); + printInputAndOutputsInfo(*model); + + // Step 3. Validate model inputs and outputs + OPENVINO_ASSERT(model->get_parameters().size() == 1, "Sample supports models with 1 input only"); + OPENVINO_ASSERT(model->get_results().size() == 1, "Sample supports models with 1 output only"); + + // SSD has an additional post-processing DetectionOutput layer that simplifies output filtering, + // try to find it. + ov::NodeVector ops = model->get_ops(); + auto it = std::find_if(ops.begin(), ops.end(), [](std::shared_ptr node) { + return node->get_type_info() == ngraph::op::DetectionOutput::get_type_info_static(); + }); + if (it == ops.end()) { + throw std::logic_error("model does not contain DetectionOutput layer"); } - // ----------------------------------------------------------------------------------------------------- + // ------------------------------------------------------------------- - // Step 2. Read a model in OpenVINO Intermediate Representation (.xml and - // .bin files) or ONNX (.onnx file) format - CNNNetwork network = ie.ReadNetwork(input_model); + // Step 4. Read input image - OutputsDataMap outputs_info(network.getOutputsInfo()); - InputsDataMap inputs_info(network.getInputsInfo()); - if (inputs_info.size() != 1 || outputs_info.size() != 1) - throw std::logic_error("Sample supports clean SSD network with one input and one output"); - - // --------------------------- Resize network to match image sizes and given - // batch---------------------- - auto input_shapes = network.getInputShapes(); - std::string input_name; - SizeVector input_shape; - std::tie(input_name, input_shape) = *input_shapes.begin(); - FormatReader::ReaderPtr reader(input_image_path.c_str()); + // Read input image without resize + FormatReader::ReaderPtr reader(image_path.c_str()); if (reader.get() == nullptr) { - std::cout << "Image " + input_image_path + " cannot be read!" << std::endl; + std::cout << "Image " + image_path + " cannot be read!" << std::endl; return 1; } - size_t image_width, image_height; - image_width = reader->width(); - image_height = reader->height(); - input_shape[0] = batch_size; - input_shape[2] = image_height; - input_shape[3] = image_width; - input_shapes[input_name] = input_shape; - std::cout << "Resizing network to the image size = [" << image_height << "x" << image_width << "] " - << "with batch = " << batch_size << std::endl; - network.reshape(input_shapes); - // ----------------------------------------------------------------------------------------------------- - // --------------------------- Step 3. Configure input & output - // --------------------------------------------- - // --------------------------- Prepare input blobs - // ----------------------------------------------------- - InputInfo::Ptr input_info; - std::tie(input_name, input_info) = *inputs_info.begin(); - // Set input layout and precision - input_info->setLayout(Layout::NCHW); - input_info->setPrecision(Precision::U8); - // --------------------------- Prepare output blobs - // ---------------------------------------------------- - DataPtr output_info; - std::string output_name; - std::tie(output_name, output_info) = *outputs_info.begin(); - // SSD has an additional post-processing DetectionOutput layer - // that simplifies output filtering, try to find it. - if (auto ngraphFunction = network.getFunction()) { - for (const auto& op : ngraphFunction->get_ops()) { - if (op->get_type_info() == ngraph::op::DetectionOutput::get_type_info_static()) { - if (output_info->getName() != op->get_friendly_name()) { - throw std::logic_error("Detection output op does not produce a network output"); - } - break; - } - } + std::shared_ptr image_data = reader->getData(); + size_t image_channels = 3; + size_t image_width = reader->width(); + size_t image_height = reader->height(); + // ------------------------------------------------------------------- + + // Step 5. Reshape model to image size and batch size + // assume model layout NCHW + const ov::Layout model_layout{"NCHW"}; + + ov::Shape tensor_shape = model->input().get_shape(); + + size_t batch_size = 1; + + tensor_shape[ov::layout::batch_idx(model_layout)] = batch_size; + tensor_shape[ov::layout::channels_idx(model_layout)] = image_channels; + tensor_shape[ov::layout::height_idx(model_layout)] = image_height; + tensor_shape[ov::layout::width_idx(model_layout)] = image_width; + + std::cout << "Reshape network to the image size = [" << image_height << "x" << image_width << "] " << std::endl; + model->reshape({{model->input().get_any_name(), tensor_shape}}); + printInputAndOutputsInfo(*model); + // ------------------------------------------------------------------- + + // Step 6. Configure model preprocessing + const ov::Layout tensor_layout{"NHWC"}; + + // clang-format off + ov::preprocess::PrePostProcessor ppp = ov::preprocess::PrePostProcessor(model); + + // 1) input() with no args assumes a model has a single input + ov::preprocess::InputInfo& input_info = ppp.input(); + // 2) Set input tensor information: + // - precision of tensor is supposed to be 'u8' + // - layout of data is 'NHWC' + input_info.tensor(). + set_element_type(ov::element::u8). + set_layout(tensor_layout); + // 3) Adding explicit preprocessing steps: + // - convert u8 to f32 + // - convert layout to 'NCHW' (from 'NHWC' specified above at tensor layout) + ppp.input().preprocess(). + convert_element_type(ov::element::f32). + convert_layout("NCHW"); + // 4) Here we suppose model has 'NCHW' layout for input + input_info.model().set_layout("NCHW"); + // 5) output () with no args assumes a model has a single output + ov::preprocess::OutputInfo& output_info = ppp.output(); + // 6) declare output element type as FP32 + output_info.tensor().set_element_type(ov::element::f32); + + // 7) Apply preprocessing modifing the original 'model' + model = ppp.build(); + // clang-format on + // ------------------------------------------------------------------- + + // Step 7. Loading a model to the device + ov::runtime::ExecutableNetwork compiled_model = core.compile_model(model, device_name); + // ------------------------------------------------------------------- + + // Step 8. Create an infer request + ov::runtime::InferRequest infer_request = compiled_model.create_infer_request(); + + // Step 9. Fill model with input data + ov::runtime::Tensor input_tensor = infer_request.get_input_tensor(); + + // copy NHWC data from image to tensor with batch + unsigned char* image_data_ptr = image_data.get(); + unsigned char* tensor_data_ptr = input_tensor.data(); + size_t image_size = image_width * image_height * image_channels; + for (size_t i = 0; i < image_size; i++) { + tensor_data_ptr[i] = image_data_ptr[i]; } + // ------------------------------------------------------------------- - const SizeVector output_shape = output_info->getTensorDesc().getDims(); - const size_t max_proposal_count = output_shape[2]; - const size_t object_size = output_shape[3]; - if (object_size != 7) { - throw std::logic_error("Output item should have 7 as a last dimension"); - } - if (output_shape.size() != 4) { - throw std::logic_error("Incorrect output dimensions for SSD model"); - } - if (output_info == nullptr) { - IE_THROW() << "[SAMPLES] internal error - output information is empty"; - } + // Step 10. Do inference synchronously + infer_request.infer(); - output_info->setPrecision(Precision::FP32); + // Step 11. Get output data from the model + ov::runtime::Tensor output_tensor = infer_request.get_output_tensor(); - auto dumpVec = [](const SizeVector& vec) -> std::string { - if (vec.empty()) - return "[]"; - std::stringstream oss; - oss << "[" << vec[0]; - for (size_t i = 1; i < vec.size(); i++) - oss << "," << vec[i]; - oss << "]"; - return oss.str(); - }; - std::cout << "Resulting input shape = " << dumpVec(input_shape) << std::endl; - std::cout << "Resulting output shape = " << dumpVec(output_shape) << std::endl; - // ----------------------------------------------------------------------------------------------------- + ov::Shape output_shape = model->output().get_shape(); + const size_t ssd_object_count = output_shape[2]; + const size_t ssd_object_size = output_shape[3]; - // --------------------------- Step 4. Loading a model to the device - // ------------------------------------------ - ExecutableNetwork executable_network = ie.LoadNetwork(network, device_name); - // ----------------------------------------------------------------------------------------------------- + const float* detections = output_tensor.data(); + // ------------------------------------------------------------------- - // --------------------------- Step 5. Create an infer request - // ------------------------------------------------- - InferRequest infer_request = executable_network.CreateInferRequest(); - // ----------------------------------------------------------------------------------------------------- + std::vector boxes; + std::vector classes; - // --------------------------- Step 6. Prepare input - // -------------------------------------------------------- - /** Collect images data ptrs **/ - std::shared_ptr image_data, original_image_data; - /** Store image data **/ - std::shared_ptr original_data(reader->getData()); - std::shared_ptr data_reader( - reader->getData(input_info->getTensorDesc().getDims()[3], input_info->getTensorDesc().getDims()[2])); - if (data_reader.get() != nullptr) { - original_image_data = original_data; - image_data = data_reader; - } else { - throw std::logic_error("Valid input images were not found!"); - } - - /** Creating input blob **/ - Blob::Ptr image_input = infer_request.GetBlob(input_name); - - /** Filling input tensor with images. First b channel, then g and r channels **/ - MemoryBlob::Ptr mimage = as(image_input); - if (!mimage) { - std::cout << "We expect image blob to be inherited from MemoryBlob, but by fact we were not able " - "to cast imageInput to MemoryBlob" - << std::endl; - return 1; - } - // locked memory holder should be alive all time while access to its buffer happens - auto minputHolder = mimage->wmap(); - - size_t num_channels = mimage->getTensorDesc().getDims()[1]; - size_t image_size = mimage->getTensorDesc().getDims()[3] * mimage->getTensorDesc().getDims()[2]; - - unsigned char* data = minputHolder.as(); - - /** Iterate over all input images **/ - for (size_t image_id = 0; image_id < batch_size; ++image_id) { - /** Iterate over all pixel in image (b,g,r) **/ - for (size_t pid = 0; pid < image_size; pid++) { - /** Iterate over all channels **/ - for (size_t ch = 0; ch < num_channels; ++ch) { - /** [images stride + channels stride + pixel id ] all in bytes **/ - data[image_id * image_size * num_channels + ch * image_size + pid] = - image_data.get()[pid * num_channels + ch]; - } - } - } - - // ----------------------------------------------------------------------------------------------------- - - // --------------------------- Step 7. Do inference - // -------------------------------------------------------- - infer_request.Infer(); - // ----------------------------------------------------------------------------------------------------- - - // --------------------------- Step 8. Process output - // ------------------------------------------------------ - Blob::Ptr output = infer_request.GetBlob(output_name); - MemoryBlob::CPtr moutput = as(output); - if (!moutput) { - throw std::logic_error("We expect output to be inherited from MemoryBlob, " - "but by fact we were not able to cast output to MemoryBlob"); - } - // locked memory holder should be alive all time while access to its buffer - // happens - auto moutputHolder = moutput->rmap(); - const float* detection = moutputHolder.as(); - - std::vector> boxes(batch_size); - std::vector> classes(batch_size); - - /* Each detection has image_id that denotes processed image */ - for (size_t cur_proposal = 0; cur_proposal < max_proposal_count; cur_proposal++) { - auto image_id = static_cast(detection[cur_proposal * object_size + 0]); + // Step 12. Parse SSD output + for (size_t object = 0; object < ssd_object_count; object++) { + int image_id = static_cast(detections[object * ssd_object_size + 0]); if (image_id < 0) { break; } - float confidence = detection[cur_proposal * object_size + 2]; - auto label = static_cast(detection[cur_proposal * object_size + 1]); - auto xmin = detection[cur_proposal * object_size + 3] * image_width; - auto ymin = detection[cur_proposal * object_size + 4] * image_height; - auto xmax = detection[cur_proposal * object_size + 5] * image_width; - auto ymax = detection[cur_proposal * object_size + 6] * image_height; + // detection, has the format: [image_id, label, conf, x_min, y_min, x_max, y_max] + int label = static_cast(detections[object * ssd_object_size + 1]); + float confidence = detections[object * ssd_object_size + 2]; + int xmin = static_cast(detections[object * ssd_object_size + 3] * image_width); + int ymin = static_cast(detections[object * ssd_object_size + 4] * image_height); + int xmax = static_cast(detections[object * ssd_object_size + 5] * image_width); + int ymax = static_cast(detections[object * ssd_object_size + 6] * image_height); if (confidence > 0.5f) { - /** Drawing only objects with >50% probability **/ - classes[image_id].push_back(label); - boxes[image_id].push_back(static_cast(xmin)); - boxes[image_id].push_back(static_cast(ymin)); - boxes[image_id].push_back(static_cast(xmax - xmin)); - boxes[image_id].push_back(static_cast(ymax - ymin)); + // collect only objects with >50% probability + classes.push_back(label); + boxes.push_back(xmin); + boxes.push_back(ymin); + boxes.push_back(xmax - xmin); + boxes.push_back(ymax - ymin); - std::cout << "[" << cur_proposal << "," << label << "] element, prob = " << confidence << ", bbox = (" - << xmin << "," << ymin << ")-(" << xmax << "," << ymax << ")" - << ", batch id = " << image_id << std::endl; + std::cout << "[" << object << "," << label << "] element, prob = " << confidence << ", (" << xmin + << "," << ymin << ")-(" << xmax << "," << ymax << ")" << std::endl; } } - for (size_t batch_id = 0; batch_id < batch_size; ++batch_id) { - addRectangles(original_image_data.get(), - image_height, - image_width, - boxes[batch_id], - classes[batch_id], - BBOX_THICKNESS); - const std::string image_path = "hello_reshape_ssd_output.bmp"; - if (writeOutputBmp(image_path, original_image_data.get(), image_height, image_width)) { - std::cout << "The resulting image was saved in the file: " + image_path << std::endl; - } else { - throw std::logic_error(std::string("Can't create a file: ") + image_path); - } + // draw bounding boxes on the image + addRectangles(image_data.get(), image_height, image_width, boxes, classes, BBOX_THICKNESS); + + const std::string image_name = "hello_reshape_ssd_output.bmp"; + if (writeOutputBmp(image_name, image_data.get(), image_height, image_width)) { + std::cout << "The resulting image was saved in the file: " + image_name << std::endl; + } else { + throw std::logic_error(std::string("Can't create a file: ") + image_name); } - // ----------------------------------------------------------------------------------------------------- + } catch (const std::exception& ex) { std::cerr << ex.what() << std::endl; return EXIT_FAILURE; } - std::cout << std::endl - << "This sample is an API example, for any performance measurements " - "please use the dedicated benchmark_app tool" - << std::endl; + return EXIT_SUCCESS; } diff --git a/samples/cpp/hello_reshape_ssd/reshape_ssd_extension.hpp b/samples/cpp/hello_reshape_ssd/reshape_ssd_extension.hpp deleted file mode 100644 index fdc4837e31b..00000000000 --- a/samples/cpp/hello_reshape_ssd/reshape_ssd_extension.hpp +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (C) 2018-2021 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include -#include -#include -#include -#include -#include -#include - -#define CUSTOM_RELU_TYPE "CustomReLU" - -/* thickness of a line (in pixels) to be used for bounding boxes */ -#define BBOX_THICKNESS 2 - -class CustomReLUImpl : public InferenceEngine::ILayerExecImpl { -public: - explicit CustomReLUImpl(const std::shared_ptr& node) : _node(node) {} - - InferenceEngine::StatusCode getSupportedConfigurations(std::vector& conf, - InferenceEngine::ResponseDesc* /*resp*/) noexcept override { - InferenceEngine::LayerConfig layerConfig; - layerConfig.dynBatchSupport = true; - - if (_node->outputs().size() != 1 && _node->inputs().size() != 1) - return InferenceEngine::GENERAL_ERROR; - - InferenceEngine::DataConfig cfg; - cfg.constant = false; - cfg.inPlace = 0; - - InferenceEngine::SizeVector order; - auto partialShape = _node->get_output_partial_shape(0); - if (partialShape.is_dynamic()) - return InferenceEngine::GENERAL_ERROR; - - auto shape = _node->get_output_shape(0); - for (size_t i = 0; i < shape.size(); i++) { - order.push_back(i); - } - cfg.desc = InferenceEngine::TensorDesc(InferenceEngine::Precision::FP32, shape, {shape, order}); - layerConfig.outConfs.push_back(cfg); - layerConfig.inConfs.push_back(cfg); - conf.push_back(layerConfig); - return InferenceEngine::OK; - } - - InferenceEngine::StatusCode init(InferenceEngine::LayerConfig& /*config*/, - InferenceEngine::ResponseDesc* /*resp*/) noexcept override { - return InferenceEngine::StatusCode::OK; - } - - InferenceEngine::StatusCode execute(std::vector& inputs, - std::vector& outputs, - InferenceEngine::ResponseDesc* /*resp*/) noexcept override { - static bool wasCalled = false; - if (!wasCalled) { - std::cout << "Running " + std::string(CUSTOM_RELU_TYPE) + - " kernel for the first time (next messages won't be printed)" - << std::endl; - wasCalled = true; - } - for (size_t i = 0; i < inputs.size(); i++) { - InferenceEngine::MemoryBlob::CPtr minput = InferenceEngine::as(inputs[i]); - InferenceEngine::MemoryBlob::Ptr moutput = InferenceEngine::as(outputs[i]); - if (!moutput || !minput) { - return InferenceEngine::StatusCode::PARAMETER_MISMATCH; - } - // locked memory holder should be alive all time while access to its buffer happens - auto minputHolder = minput->rmap(); - auto moutputHolder = moutput->wmap(); - - auto inputData = minputHolder.as(); - auto outputData = moutputHolder.as(); - for (size_t j = 0; j < minput->size(); j++) { - outputData[j] = inputData[j] < 0 ? 0 : inputData[j]; - } - } - return InferenceEngine::StatusCode::OK; - } - -private: - const std::shared_ptr _node; -}; - -class CustomReluOp : public ngraph::op::Op { -public: - OPENVINO_OP("CustomReluOp", "experimental"); - - CustomReluOp() = default; - explicit CustomReluOp(const ngraph::Output& arg) : Op({arg}) { - constructor_validate_and_infer_types(); - } - - void validate_and_infer_types() override { - auto input_shape = get_input_partial_shape(0).to_shape(); - - ngraph::Shape output_shape(input_shape); - for (size_t i = 0; i < input_shape.size(); ++i) { - output_shape[i] = input_shape[i]; - } - - set_output_type(0, get_input_element_type(0), ngraph::PartialShape(output_shape)); - } - - std::shared_ptr clone_with_new_inputs(const ngraph::OutputVector& new_args) const override { - if (new_args.size() != 1) { - throw ngraph::ngraph_error("Incorrect number of new arguments"); - } - - return std::make_shared(new_args.at(0)); - } - - bool visit_attributes(ngraph::AttributeVisitor&) override { - return true; - } -}; - -class InPlaceExtension : public InferenceEngine::IExtension { -public: - InPlaceExtension() { - impls[CUSTOM_RELU_TYPE] = [](const std::shared_ptr& node) -> InferenceEngine::ILayerImpl::Ptr { - return std::make_shared(node); - }; - } - - void GetVersion(const InferenceEngine::Version*& versionInfo) const noexcept override {} - - void Unload() noexcept override {} - - std::vector getImplTypes(const std::shared_ptr& node) override { - if (impls.find(node->description()) == impls.end()) - return {}; - return {"CPU"}; - } - - InferenceEngine::ILayerImpl::Ptr getImplementation(const std::shared_ptr& node, - const std::string& implType) override { - if (impls.find(node->description()) == impls.end() || implType != "CPU") - return nullptr; - return impls[node->description()](node); - } - - std::map getOpSets() override { - static std::map opsets; - if (opsets.empty()) { - ngraph::OpSet opset; - opset.insert(); - opsets["experimental"] = opset; - } - return opsets; - } - -private: - std::map)>> impls; -}; diff --git a/tests/samples_tests/smoke_tests/README.md b/tests/samples_tests/smoke_tests/README.md index 873c65e0c16..88e4c91af8a 100644 --- a/tests/samples_tests/smoke_tests/README.md +++ b/tests/samples_tests/smoke_tests/README.md @@ -5,7 +5,7 @@ These tests execute IE samples on pregenerated IR You can run tests not only from the , but in this case you need to remember to adjust the environment variables like as WORKSPACE and SHARE To install smoke tests: - ``` bash + ``` bash - cd /tests/samples_tests/smoke_tests - mkdir build && cd build - cmake ../.. diff --git a/tests/samples_tests/smoke_tests/common/specific_samples_parsers.py b/tests/samples_tests/smoke_tests/common/specific_samples_parsers.py index 426b1b4b2f6..52aaa55fdc2 100644 --- a/tests/samples_tests/smoke_tests/common/specific_samples_parsers.py +++ b/tests/samples_tests/smoke_tests/common/specific_samples_parsers.py @@ -48,12 +48,10 @@ def parse_hello_reshape_ssd(stdout): log.error('Wrong output line: {}, while the test expects the following format: 4d shape' '(Example: Resulting output shape = [1,1,200,7])'.format(line)) elif 'element, prob' in line: - if re.match("^.*prob\s+=.*\d,\s+bbox\s+=\s+\(.*\d,.*\d\)-\(.*\d,.*\d\)," - "\s+batch id\s+=\s+\d", line) is None: + if re.match("^.*prob\\s+=.*\\d,\\s+\\(.*\\d,.*\\d\)-\\(.*\\d,.*\\d\\)", line) is None: is_ok = False - log.error('Wrong output line: {}, while the test expects the following format: 4d shape' - '(Example: [33,59] element, prob = 0.963015, bbox = (189.776,110.933)-(309.288,306.952), ' - 'batch id = 0)'.format(line)) + log.error('Wrong output line: {}, while the test expects the following format: ' + 'Example: [33,59] element, prob = 0.963015, (189,110)-(309,306)'.format(line)) elif 'was saved' in line: path_result = os.path.join(os.getcwd(), line.split(' ')[-1].strip()) if not os.path.isfile(path_result): diff --git a/tests/samples_tests/smoke_tests/test_hello_reshape_ssd.py b/tests/samples_tests/smoke_tests/test_hello_reshape_ssd.py index ab1d1219b70..cc00199b4e9 100644 --- a/tests/samples_tests/smoke_tests/test_hello_reshape_ssd.py +++ b/tests/samples_tests/smoke_tests/test_hello_reshape_ssd.py @@ -22,8 +22,8 @@ log.basicConfig(format="[ %(levelname)s ] %(message)s", level=log.INFO, stream=s test_data_fp32 = get_tests(cmd_params={'i': [os.path.join('500x500', 'cat.bmp')], 'm': [os.path.join('ssd512', 'FP32', 'ssd512.xml')], - 'd': ['CPU'], - 'batch': [1, 2, 4]}, use_device=['d'], use_batch=True + 'd': ['CPU']}, + use_device=['d'], use_batch=False ) From 562d388ad96c4b04a64edab68a6db44917fdf952 Mon Sep 17 00:00:00 2001 From: Yegor Kruglov Date: Fri, 10 Dec 2021 16:31:49 +0300 Subject: [PATCH 2/2] MaxPool-1 -> MaxPool-8 upgrade transformation (#8784) * upgrade transformation * test fix * comments resolving * added case with dynamic rank --- .../convert_maxpool_upgrade_test.cpp | 71 +++++++++++++++++++ .../convert_maxpool_upgrade.hpp | 27 +++++++ .../common_optimizations.cpp | 2 + .../convert_maxpool_upgrade.cpp | 52 ++++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 inference-engine/tests/functional/inference_engine/transformations/convert_maxpool_upgrade_test.cpp create mode 100644 src/common/transformations/include/transformations/op_conversions/convert_maxpool_upgrade.hpp create mode 100644 src/common/transformations/src/transformations/op_conversions/convert_maxpool_upgrade.cpp diff --git a/inference-engine/tests/functional/inference_engine/transformations/convert_maxpool_upgrade_test.cpp b/inference-engine/tests/functional/inference_engine/transformations/convert_maxpool_upgrade_test.cpp new file mode 100644 index 00000000000..3b61493df6c --- /dev/null +++ b/inference-engine/tests/functional/inference_engine/transformations/convert_maxpool_upgrade_test.cpp @@ -0,0 +1,71 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "common_test_utils/ngraph_test_utils.hpp" + +using namespace testing; + +TEST_F(TransformationTestsF, ConvertMaxPool1ToMaxPool8) { + { + auto data = std::make_shared(ngraph::element::f32, ngraph::Shape{1, 2, 3}); + ngraph::Strides strides{1}; + ngraph::Shape pads_begin{0}, pads_end{0}, kernel{1}; + auto maxpool_1 = std::make_shared(data, strides, pads_begin, pads_end, + kernel); + + function = std::make_shared(ngraph::OutputVector{maxpool_1->output(0)}, + ngraph::ParameterVector{data}); + + manager.register_pass(); + } + + { + auto data = std::make_shared(ngraph::element::f32, ngraph::Shape{1, 2, 3}); + ngraph::Strides strides{1}, dilations{1}; + ngraph::Shape pads_begin{0}, pads_end{0}, kernel{1}; + auto maxpool_8 = std::make_shared(data, strides, dilations, pads_begin, pads_end, + kernel); + + function_ref = std::make_shared(ngraph::OutputVector{maxpool_8->output(0)}, + ngraph::ParameterVector{data}); + } +} + +TEST_F(TransformationTestsF, negative_ConvertMaxPool1ToMaxPool8_dynamic_rank) { + { + auto data = std::make_shared(ngraph::element::f32, ngraph::PartialShape::dynamic()); + ngraph::Strides strides{1}; + ngraph::Shape pads_begin{0}, pads_end{0}, kernel{1}; + auto maxpool_1 = std::make_shared(data, strides, pads_begin, pads_end, + kernel); + + function = std::make_shared(ngraph::OutputVector{maxpool_1->output(0)}, + ngraph::ParameterVector{data}); + + manager.register_pass(); + } + + { + auto data = std::make_shared(ngraph::element::f32, ngraph::PartialShape::dynamic()); + ngraph::Strides strides{1}; + ngraph::Shape pads_begin{0}, pads_end{0}, kernel{1}; + auto maxpool_1 = std::make_shared(data, strides, pads_begin, pads_end, + kernel); + + function_ref = std::make_shared(ngraph::OutputVector{maxpool_1->output(0)}, + ngraph::ParameterVector{data}); + } +} diff --git a/src/common/transformations/include/transformations/op_conversions/convert_maxpool_upgrade.hpp b/src/common/transformations/include/transformations/op_conversions/convert_maxpool_upgrade.hpp new file mode 100644 index 00000000000..3dd72aaf827 --- /dev/null +++ b/src/common/transformations/include/transformations/op_conversions/convert_maxpool_upgrade.hpp @@ -0,0 +1,27 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include + +namespace ngraph { +namespace pass { + +class TRANSFORMATIONS_API ConvertMaxPool1ToMaxPool8; + +} // namespace pass +} // namespace ngraph + +/** + * @ingroup ie_transformation_common_api + * @brief ConvertMaxPool1ToMaxPool8 converts v1::MaxPool into v8::MaxPool. + */ + +class ngraph::pass::ConvertMaxPool1ToMaxPool8 : public ngraph::pass::MatcherPass { +public: + OPENVINO_RTTI("ConvertMaxPool1ToMaxPool8"); + ConvertMaxPool1ToMaxPool8(); +}; diff --git a/src/common/transformations/src/transformations/common_optimizations/common_optimizations.cpp b/src/common/transformations/src/transformations/common_optimizations/common_optimizations.cpp index 5e6c5a06f75..9399107f65f 100644 --- a/src/common/transformations/src/transformations/common_optimizations/common_optimizations.cpp +++ b/src/common/transformations/src/transformations/common_optimizations/common_optimizations.cpp @@ -85,6 +85,7 @@ #include "transformations/op_conversions/gather_normalize_negative_indices.hpp" #include "transformations/op_conversions/convert_deformable_conv_v8_to_v1.hpp" #include "transformations/op_conversions/convert_maxpool_downgrade.hpp" +#include "transformations/op_conversions/convert_maxpool_upgrade.hpp" #include "transformations/disable_decompression_convert_constant_folding.hpp" #include "transformations/op_conversions/convert_prior_box_v8_to_v0.hpp" @@ -183,6 +184,7 @@ bool ngraph::pass::CommonOptimizations::run_on_model(const std::shared_ptr(); manager.register_pass(); manager.register_pass(); + manager.register_pass(); manager.register_pass(); // not plugins implemented priorbox8 manager.register_pass(); manager.register_pass(); diff --git a/src/common/transformations/src/transformations/op_conversions/convert_maxpool_upgrade.cpp b/src/common/transformations/src/transformations/op_conversions/convert_maxpool_upgrade.cpp new file mode 100644 index 00000000000..50cb1c16014 --- /dev/null +++ b/src/common/transformations/src/transformations/op_conversions/convert_maxpool_upgrade.cpp @@ -0,0 +1,52 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "transformations/op_conversions/convert_maxpool_upgrade.hpp" +#include +#include +#include +#include +#include +#include "itt.hpp" + +ngraph::pass::ConvertMaxPool1ToMaxPool8::ConvertMaxPool1ToMaxPool8() { + MATCHER_SCOPE(ConvertMaxPool1ToMaxPool8); + // Replaces v1::MaxPool with v8::MaxPool with default dilations, axis and index_element_type attributes + + auto input = pattern::any_input(pattern::has_static_rank()); + auto maxpool_v1_pattern = ngraph::pattern::wrap_type({input}); + + ngraph::matcher_pass_callback callback = [=](ngraph::pattern::Matcher& m) { + auto maxpool_v1_node = std::dynamic_pointer_cast(m.get_match_root()); + + if (!maxpool_v1_node) + return false; + + auto spatial_dims = maxpool_v1_node->get_input_partial_shape(0).rank().get_length() - 2; + if (spatial_dims <= 0) + return false; + ov::Strides dilations(spatial_dims, 1); + + auto maxpool_v8_node = std::make_shared(maxpool_v1_node->input_value(0), + maxpool_v1_node->get_strides(), + dilations, + maxpool_v1_node->get_pads_begin(), + maxpool_v1_node->get_pads_end(), + maxpool_v1_node->get_kernel(), + maxpool_v1_node->get_rounding_type(), + maxpool_v1_node->get_auto_pad(), + ov::element::i64, + 0); + + maxpool_v8_node->set_friendly_name(maxpool_v1_node->get_friendly_name()); + maxpool_v1_node->output(0).replace(maxpool_v8_node->output(0)); + ngraph::copy_runtime_info(maxpool_v1_node, maxpool_v8_node); + maxpool_v1_node->clear_control_dependencies(); + + return true; + }; + + auto m = std::make_shared(maxpool_v1_pattern, matcher_name); + register_matcher(m, callback); +}