diff --git a/ngraph/python/src/openvino/inference_engine/__init__.py b/ngraph/python/src/openvino/inference_engine/__init__.py index 0387beb17d957d..ca9ba301148da3 100644 --- a/ngraph/python/src/openvino/inference_engine/__init__.py +++ b/ngraph/python/src/openvino/inference_engine/__init__.py @@ -19,6 +19,6 @@ from openvino.pyopenvino import ColorFormat from openvino.pyopenvino import PreProcessChannel -from openvino.inference_engine.ie_api import BlobPatch +from openvino.inference_engine.ie_api import BlobWrapper # Patching for Blob class -Blob = BlobPatch +Blob = BlobWrapper diff --git a/ngraph/python/src/openvino/inference_engine/ie_api.py b/ngraph/python/src/openvino/inference_engine/ie_api.py index 2ed27b467590ad..84264f392bc5d5 100644 --- a/ngraph/python/src/openvino/inference_engine/ie_api.py +++ b/ngraph/python/src/openvino/inference_engine/ie_api.py @@ -2,51 +2,87 @@ # SPDX-License-Identifier: Apache-2.0 from openvino.pyopenvino import TBlobFloat32 +from openvino.pyopenvino import TBlobFloat64 +from openvino.pyopenvino import TBlobFloat16 from openvino.pyopenvino import TBlobInt64 +from openvino.pyopenvino import TBlobUint64 from openvino.pyopenvino import TBlobInt32 +from openvino.pyopenvino import TBlobUint32 from openvino.pyopenvino import TBlobInt16 +from openvino.pyopenvino import TBlobUint16 from openvino.pyopenvino import TBlobInt8 from openvino.pyopenvino import TBlobUint8 +from openvino.pyopenvino import TensorDesc import numpy as np -# Patch for Blobs to dispatch types on Python side -class BlobPatch: - def __new__(cls, tensor_desc, arr : np.ndarray = None): - # TODO: create tensor_desc based on arr itself - # if tenosr_desc is not given - if arr is not None: + +precision_map = {'FP32': np.float32, + 'FP64': np.float64, + 'FP16': np.int16, + 'BF16': np.int16, + 'I16': np.int16, + 'I8': np.int8, + 'BIN': np.int8, + 'I32': np.int32, + 'I64': np.int64, + 'U8': np.uint8, + 'BOOL': np.uint8, + 'U16': np.uint16, + 'U32': np.uint32, + 'U64': np.uint64} + +# Dispatch Blob types on Python side. +class BlobWrapper: + def __new__(cls, tensor_desc : TensorDesc, arr : np.ndarray = None): + arr_size = 0 + precision = "" + if arr is not None and tensor_desc is not None: arr = np.array(arr) # Keeping array as numpy array - size_arr = np.prod(arr.shape) - if arr is not None: - if np.isfortran(arr): - arr = arr.ravel(order="F") - else: - arr = arr.ravel(order="C") - # Return TBlob depends on numpy array dtype - # TODO: add dispatching based on tensor_desc precision value - if tensor_desc is not None and arr is None: + arr_size = np.prod(arr.shape) + tensor_desc_size = np.prod(tensor_desc.dims) precision = tensor_desc.precision - if precision == "FP32": - return TBlobFloat32(tensor_desc) + if np.isfortran(arr): + arr = arr.ravel(order="F") else: - raise ValueError("not supported precision") - elif tensor_desc is not None and arr is not None: - if arr.dtype in [np.float32]: - return TBlobFloat32(tensor_desc, arr, size_arr) - # elif arr.dtype in [np.float64]: - # return TBlobFloat32(tensor_desc, arr.view(dtype=np.float32), size_arr) - # elif arr.dtype in [np.int64]: - # return TBlobInt64(tensor_desc, arr, size) - # elif arr.dtype in [np.int32]: - # return TBlobInt32(tensor_desc, arr, size) - # elif arr.dtype in [np.int16]: - # return TBlobInt16(tensor_desc, arr, size) - # elif arr.dtype in [np.int8]: - # return TBlobInt8(tensor_desc, arr, size) - # elif arr.dtype in [np.uint8]: - # return TBlobUint8(tensor_desc, arr, size) + arr = arr.ravel(order="C") + if arr_size != tensor_desc_size: + raise AttributeError(f'Number of elements in provided numpy array ' + f'{arr_size} and required by TensorDesc ' + f'{tensor_desc_size} are not equal') + if arr.dtype != precision_map[precision]: + raise ValueError(f"Data type {arr.dtype} of provided numpy array " + f"doesn't match to TensorDesc precision {precision}") + if not arr.flags['C_CONTIGUOUS']: + arr = np.ascontiguousarray(arr) + elif arr is None and tensor_desc is not None: + arr = np.empty(0, dtype=precision_map[precision]) + else: + raise AttributeError("TensorDesc can't be None") + + if precision in ["FP32"]: + return TBlobFloat32(tensor_desc, arr, arr_size) + elif precision in ["FP64"]: + return TBlobFloat64(tensor_desc, arr, arr_size) + elif precision in ["FP16", "BF16"]: + return TBlobFloat16(tensor_desc, arr.view(dtype=np.int16), arr_size) + elif precision in ["I64"]: + return TBlobInt64(tensor_desc, arr, arr_size) + elif precision in ["U64"]: + return TBlobUint64(tensor_desc, arr, arr_size) + elif precision in ["I32"]: + return TBlobInt32(tensor_desc, arr, arr_size) + elif precision in ["U32"]: + return TBlobUint32(tensor_desc, arr, arr_size) + elif precision in ["I16"]: + return TBlobInt16(tensor_desc, arr, arr_size) + elif precision in ["U16"]: + return TBlobUint16(tensor_desc, arr, arr_size) + elif precision in ["I8", "BIN"]: + return TBlobInt8(tensor_desc, arr, arr_size) + elif precision in ["U8", "BOOL"]: + return TBlobUint8(tensor_desc, arr, arr_size) else: - # TODO: raise error - return None + raise AttributeError(f'Unsupported precision ' + f'{precision} for Blob') diff --git a/ngraph/python/src/pyopenvino/inference_engine/common.cpp b/ngraph/python/src/pyopenvino/inference_engine/common.cpp index f4a53cc1d3d7b0..8c60193285502d 100644 --- a/ngraph/python/src/pyopenvino/inference_engine/common.cpp +++ b/ngraph/python/src/pyopenvino/inference_engine/common.cpp @@ -77,8 +77,8 @@ namespace Common { return val ? Py_True : Py_False; } // Check for std::vector - else if (param.is < std::vector < std::string >> ()) { - auto val = param.as < std::vector < std::string >> (); + else if (param.is>()) { + auto val = param.as>(); PyObject *list = PyList_New(0); for (const auto &it : val) { PyObject *str_val = PyUnicode_FromString(it.c_str()); @@ -87,8 +87,8 @@ namespace Common { return list; } // Check for std::vector - else if (param.is < std::vector < int >> ()) { - auto val = param.as < std::vector < int >> (); + else if (param.is>()) { + auto val = param.as>(); PyObject *list = PyList_New(0); for (const auto &it : val) { PyList_Append(list, PyLong_FromLong(it)); @@ -105,8 +105,8 @@ namespace Common { return list; } // Check for std::vector - else if (param.is < std::vector < float >> ()) { - auto val = param.as < std::vector < float >> (); + else if (param.is>()) { + auto val = param.as>(); PyObject *list = PyList_New(0); for (const auto &it : val) { PyList_Append(list, PyFloat_FromDouble((double) it)); @@ -114,18 +114,16 @@ namespace Common { return list; } // Check for std::tuple - else if (param.is < std::tuple < unsigned int, unsigned int >> ()) { - auto val = param.as < std::tuple < unsigned int, - unsigned int >> (); + else if (param.is>()) { + auto val = param.as>(); PyObject *tuple = PyTuple_New(2); PyTuple_SetItem(tuple, 0, PyLong_FromUnsignedLong((unsigned long) std::get<0>(val))); PyTuple_SetItem(tuple, 1, PyLong_FromUnsignedLong((unsigned long) std::get<1>(val))); return tuple; } // Check for std::tuple - else if (param.is < std::tuple < unsigned int, unsigned int, unsigned int >> ()) { - auto val = param.as < std::tuple < unsigned int, - unsigned int, unsigned int >> (); + else if (param.is>()) { + auto val = param.as>(); PyObject *tuple = PyTuple_New(3); PyTuple_SetItem(tuple, 0, PyLong_FromUnsignedLong((unsigned long) std::get<0>(val))); PyTuple_SetItem(tuple, 1, PyLong_FromUnsignedLong((unsigned long) std::get<1>(val))); @@ -133,8 +131,8 @@ namespace Common { return tuple; } // Check for std::map - else if (param.is < std::map < std::string, std::string >> ()) { - auto val = param.as < std::map < std::string, std::string>>(); + else if (param.is >()) { + auto val = param.as >(); PyObject *dict = PyDict_New(); for (const auto &it : val) { PyDict_SetItemString(dict, it.first.c_str(), PyUnicode_FromString(it.second.c_str())); @@ -142,9 +140,8 @@ namespace Common { return dict; } // Check for std::map - else if (param.is < std::map < std::string, int >> ()) { - auto val = param.as < std::map < std::string, - int >> (); + else if (param.is>()) { + auto val = param.as>(); PyObject *dict = PyDict_New(); for (const auto &it : val) { PyDict_SetItemString(dict, it.first.c_str(), PyLong_FromLong((long) it.second)); @@ -155,4 +152,31 @@ namespace Common { return (PyObject *) NULL; } } + + const std::shared_ptr convert_to_blob(const py::handle& blob) { + if (py::isinstance>(blob)) { + return blob.cast> &>(); + } else if (py::isinstance>(blob)) { + return blob.cast> &>(); + } else if (py::isinstance>(blob)) { + return blob.cast> &>(); + } else if (py::isinstance>(blob)) { + return blob.cast> &>(); + } else if (py::isinstance>(blob)) { + return blob.cast> &>(); + } else if (py::isinstance>(blob)) { + return blob.cast> &>(); + } else if (py::isinstance>(blob)) { + return blob.cast> &>(); + } else if (py::isinstance>(blob)) { + return blob.cast> &>(); + } else if (py::isinstance>(blob)) { + return blob.cast> &>(); + } else if (py::isinstance>(blob)) { + return blob.cast> &>(); + } else { + // Throw error + } + } + }; diff --git a/ngraph/python/src/pyopenvino/inference_engine/common.hpp b/ngraph/python/src/pyopenvino/inference_engine/common.hpp index ee9c57913d609b..5eda919a1bd4e7 100644 --- a/ngraph/python/src/pyopenvino/inference_engine/common.hpp +++ b/ngraph/python/src/pyopenvino/inference_engine/common.hpp @@ -4,10 +4,14 @@ #pragma once +#include #include #include "Python.h" #include #include "ie_common.h" +#include + +namespace py = pybind11; namespace Common { InferenceEngine::Layout get_layout_from_string(const std::string &layout); @@ -15,4 +19,6 @@ namespace Common { const std::string& get_layout_from_enum(const InferenceEngine::Layout &layout); PyObject *parse_parameter(const InferenceEngine::Parameter ¶m); -}; + + const std::shared_ptr convert_to_blob(const py::handle& blob); +}; // namespace Common diff --git a/ngraph/python/src/pyopenvino/inference_engine/ie_blob.hpp b/ngraph/python/src/pyopenvino/inference_engine/ie_blob.hpp index 411ec4c3a8567c..35584ebf5fc7bb 100644 --- a/ngraph/python/src/pyopenvino/inference_engine/ie_blob.hpp +++ b/ngraph/python/src/pyopenvino/inference_engine/ie_blob.hpp @@ -26,28 +26,13 @@ void regclass_TBlob(py::module m, std::string typestring) { py::class_, std::shared_ptr>> cls( m, pyclass_name); - cls.def(py::init([](const InferenceEngine::TensorDesc& tensorDesc) { - return std::make_shared>(tensorDesc); - })); - - cls.def(py::init([](const InferenceEngine::TensorDesc& tensorDesc, py::array_t arr) { - auto size = arr.size(); // or copy from tensorDesc getDims product? - // py::print(arr.dtype()); // validate tensorDesc with this??? - // assert arr.size() == TensorDesc.getDims().product? ??? - T* ptr = const_cast(arr.data(0)); // Note: obligatory removal of const! - return std::make_shared>(tensorDesc, ptr, size); - })); - cls.def(py::init( [](const InferenceEngine::TensorDesc& tensorDesc, py::array_t& arr, size_t size = 0) { - if (size == 0) { - size = arr.size(); // or copy from tensorDesc getDims product? - } auto blob = InferenceEngine::make_shared_blob(tensorDesc); blob->allocate(); - std::copy(arr.data(0), arr.data(0) + size, blob->rwmap().template as()); - // py::print(arr.dtype()); // validate tensorDesc with this??? - // assert arr.size() == TensorDesc.getDims().product?10 ??? + if (size != 0) { + std::copy(arr.data(0), arr.data(0) + size, blob->rwmap().template as()); + } return blob; })); diff --git a/ngraph/python/src/pyopenvino/inference_engine/ie_infer_request.cpp b/ngraph/python/src/pyopenvino/inference_engine/ie_infer_request.cpp index 88aafcfa6a490e..072666c2ee654d 100644 --- a/ngraph/python/src/pyopenvino/inference_engine/ie_infer_request.cpp +++ b/ngraph/python/src/pyopenvino/inference_engine/ie_infer_request.cpp @@ -12,6 +12,7 @@ #include #include +#include "pyopenvino/inference_engine/common.hpp" #include "pyopenvino/inference_engine/ie_infer_request.hpp" #include "pyopenvino/inference_engine/ie_preprocess_info.hpp" #include "pyopenvino/inference_engine/ie_executable_network.hpp" @@ -28,29 +29,29 @@ void regclass_InferRequest(py::module m) cls.def("set_input", [](InferenceEngine::InferRequest& self, const py::dict& inputs) { for (auto&& input : inputs) { auto name = input.first.cast().c_str(); - const std::shared_ptr>& blob = input.second.cast>&>(); + auto blob = Common::convert_to_blob(input.second); self.SetBlob(name, blob); } }); cls.def("set_output", [](InferenceEngine::InferRequest& self, const py::dict& results) { for (auto&& result : results) { auto name = result.first.cast().c_str(); - const std::shared_ptr>& blob = result.second.cast>&>(); + auto blob = Common::convert_to_blob(result.second); self.SetBlob(name, blob); } }); cls.def("set_blob", [](InferenceEngine::InferRequest& self, const std::string& name, - const InferenceEngine::TBlob::Ptr& blob) { - self.SetBlob(name, blob); + py::handle blob) { + self.SetBlob(name, Common::convert_to_blob(blob)); }); cls.def("set_blob", [](InferenceEngine::InferRequest& self, const std::string& name, - const InferenceEngine::TBlob::Ptr& blob, + py::handle blob, const InferenceEngine::PreProcessInfo& info) { - self.SetBlob(name, blob); + self.SetBlob(name, Common::convert_to_blob(blob)); }); cls.def("set_batch", &InferenceEngine::InferRequest::SetBatch, py::arg("size")); diff --git a/ngraph/python/src/pyopenvino/inference_engine/ie_preprocess_info.cpp b/ngraph/python/src/pyopenvino/inference_engine/ie_preprocess_info.cpp index 0939182b4b1f6b..8671fdce7bd6e7 100644 --- a/ngraph/python/src/pyopenvino/inference_engine/ie_preprocess_info.cpp +++ b/ngraph/python/src/pyopenvino/inference_engine/ie_preprocess_info.cpp @@ -5,6 +5,7 @@ #include #include "pyopenvino/inference_engine/ie_preprocess_info.hpp" +#include "pyopenvino/inference_engine/common.hpp" #include #include @@ -27,13 +28,13 @@ void regclass_PreProcessInfo(py::module m) { cls.def("get_number_of_channels", &InferenceEngine::PreProcessInfo::getNumberOfChannels); cls.def("init", &InferenceEngine::PreProcessInfo::init); cls.def("set_mean_image", [](InferenceEngine::PreProcessInfo& self, - const InferenceEngine::TBlob::Ptr& meanImage) { - self.setMeanImage(meanImage); + py::handle meanImage) { + self.setMeanImage(Common::convert_to_blob(meanImage)); }); cls.def("set_mean_image_for_channel", [](InferenceEngine::PreProcessInfo& self, - const InferenceEngine::TBlob::Ptr& meanImage, + py::handle meanImage, const size_t channel) { - self.setMeanImageForChannel(meanImage, channel); + self.setMeanImageForChannel(Common::convert_to_blob(meanImage), channel); }); cls.def_property("mean_variant", &InferenceEngine::PreProcessInfo::getMeanVariant, &InferenceEngine::PreProcessInfo::setVariant); diff --git a/ngraph/python/src/pyopenvino/pyopenvino.cpp b/ngraph/python/src/pyopenvino/pyopenvino.cpp index 28ffdcc2596aa7..c5e9dea0d74179 100644 --- a/ngraph/python/src/pyopenvino/pyopenvino.cpp +++ b/ngraph/python/src/pyopenvino/pyopenvino.cpp @@ -53,21 +53,20 @@ PYBIND11_MODULE(pyopenvino, m) { regclass_IECore(m); - // GeneralBlob + // Registering template of Blob regclass_Blob(m); - // Specific type Blobs + // Registering specific types of Blobs regclass_TBlob(m, "Float32"); + regclass_TBlob(m, "Float64"); + regclass_TBlob(m, "Float16"); + regclass_TBlob(m, "Int64"); + regclass_TBlob(m, "Uint64"); + regclass_TBlob(m, "Int32"); + regclass_TBlob(m, "Uint32"); + regclass_TBlob(m, "Int16"); + regclass_TBlob(m, "Uint16"); regclass_TBlob(m, "Int8"); regclass_TBlob(m, "Uint8"); - regclass_TBlob(m, "Int16"); - // regclass_TBlob(m, "Uint16"); - regclass_TBlob(m, "Int32"); - // regclass_TBlob(m, "Uint32"); - regclass_TBlob(m, "Int64"); - // regclass_TBlob(m, "UInt64"); - // regclass_TBlob(m); - // regclass_TBlob(m); - // regclass_TBlob(m); regclass_IENetwork(m); regclass_ExecutableNetwork(m);