Merge pull request #13694 from dkurt:dnn_ie_async
Asynchronous API from Intel's Inference Engine (#13694) * Add forwardAsync for asynchronous mode from Intel's Inference Engine * Python test for forwardAsync * Replace Future_Mat to AsyncMat * Shadow AsyncMat * Isolate InferRequest callback * Manage exceptions in Async API of IE
This commit is contained in:
committed by
Alexander Alekhin
parent
3abae3c511
commit
a5c92c2029
@@ -2,7 +2,13 @@
|
||||
typedef dnn::DictValue LayerId;
|
||||
typedef std::vector<dnn::MatShape> vector_MatShape;
|
||||
typedef std::vector<std::vector<dnn::MatShape> > vector_vector_MatShape;
|
||||
|
||||
#ifdef CV_CXX11
|
||||
typedef std::chrono::milliseconds chrono_milliseconds;
|
||||
typedef std::future_status AsyncMatStatus;
|
||||
#else
|
||||
typedef size_t chrono_milliseconds;
|
||||
typedef size_t AsyncMatStatus;
|
||||
#endif
|
||||
|
||||
template<>
|
||||
bool pyopencv_to(PyObject *o, dnn::DictValue &dv, const char *name)
|
||||
@@ -40,6 +46,46 @@ bool pyopencv_to(PyObject *o, std::vector<Mat> &blobs, const char *name) //requi
|
||||
return pyopencvVecConverter<Mat>::to(o, blobs, ArgInfo(name, false));
|
||||
}
|
||||
|
||||
#ifdef CV_CXX11
|
||||
|
||||
template<>
|
||||
PyObject* pyopencv_from(const std::future<Mat>& f_)
|
||||
{
|
||||
std::future<Mat>& f = const_cast<std::future<Mat>&>(f_);
|
||||
Ptr<cv::dnn::AsyncMat> p(new std::future<Mat>(std::move(f)));
|
||||
return pyopencv_from(p);
|
||||
}
|
||||
|
||||
template<>
|
||||
PyObject* pyopencv_from(const std::future_status& status)
|
||||
{
|
||||
return pyopencv_from((int)status);
|
||||
}
|
||||
|
||||
template<>
|
||||
bool pyopencv_to(PyObject* src, std::chrono::milliseconds& dst, const char* name)
|
||||
{
|
||||
size_t millis = 0;
|
||||
if (pyopencv_to(src, millis, name))
|
||||
{
|
||||
dst = std::chrono::milliseconds(millis);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template<>
|
||||
PyObject* pyopencv_from(const cv::dnn::AsyncMat&)
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented, "C++11 is required.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // CV_CXX11
|
||||
|
||||
template<typename T>
|
||||
PyObject* pyopencv_from(const dnn::DictValue &dv)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#error This is a shadow header file, which is not intended for processing by any compiler. \
|
||||
Only bindings parser should handle this file.
|
||||
|
||||
namespace cv { namespace dnn {
|
||||
|
||||
class CV_EXPORTS_W AsyncMat
|
||||
{
|
||||
public:
|
||||
//! Wait for Mat object readiness and return it.
|
||||
CV_WRAP Mat get();
|
||||
|
||||
//! Wait for Mat object readiness.
|
||||
CV_WRAP void wait() const;
|
||||
|
||||
/** @brief Wait for Mat object readiness specific amount of time.
|
||||
* @param timeout Timeout in milliseconds
|
||||
* @returns [std::future_status](https://en.cppreference.com/w/cpp/thread/future_status)
|
||||
*/
|
||||
CV_WRAP AsyncMatStatus wait_for(std::chrono::milliseconds timeout) const;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -5,8 +5,8 @@ import numpy as np
|
||||
|
||||
from tests_common import NewOpenCVTests, unittest
|
||||
|
||||
def normAssert(test, a, b, lInf=1e-5):
|
||||
test.assertLess(np.max(np.abs(a - b)), lInf)
|
||||
def normAssert(test, a, b, msg=None, lInf=1e-5):
|
||||
test.assertLess(np.max(np.abs(a - b)), lInf, msg)
|
||||
|
||||
def inter_area(box1, box2):
|
||||
x_min, x_max = max(box1[0], box2[0]), min(box1[2], box2[2])
|
||||
@@ -53,53 +53,6 @@ def normAssertDetections(test, ref, out, confThreshold=0.0, scores_diff=1e-5, bo
|
||||
if errMsg:
|
||||
test.fail(errMsg)
|
||||
|
||||
|
||||
# Returns a simple one-layer network created from Caffe's format
|
||||
def getSimpleNet():
|
||||
prototxt = """
|
||||
name: "simpleNet"
|
||||
input: "data"
|
||||
layer {
|
||||
type: "Identity"
|
||||
name: "testLayer"
|
||||
top: "testLayer"
|
||||
bottom: "data"
|
||||
}
|
||||
"""
|
||||
return cv.dnn.readNetFromCaffe(bytearray(prototxt, 'utf8'))
|
||||
|
||||
|
||||
def testBackendAndTarget(backend, target):
|
||||
net = getSimpleNet()
|
||||
net.setPreferableBackend(backend)
|
||||
net.setPreferableTarget(target)
|
||||
inp = np.random.standard_normal([1, 2, 3, 4]).astype(np.float32)
|
||||
try:
|
||||
net.setInput(inp)
|
||||
net.forward()
|
||||
except BaseException as e:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
haveInfEngine = testBackendAndTarget(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE, cv.dnn.DNN_TARGET_CPU)
|
||||
dnnBackendsAndTargets = [
|
||||
[cv.dnn.DNN_BACKEND_OPENCV, cv.dnn.DNN_TARGET_CPU],
|
||||
]
|
||||
|
||||
if haveInfEngine:
|
||||
dnnBackendsAndTargets.append([cv.dnn.DNN_BACKEND_INFERENCE_ENGINE, cv.dnn.DNN_TARGET_CPU])
|
||||
if testBackendAndTarget(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE, cv.dnn.DNN_TARGET_MYRIAD):
|
||||
dnnBackendsAndTargets.append([cv.dnn.DNN_BACKEND_INFERENCE_ENGINE, cv.dnn.DNN_TARGET_MYRIAD])
|
||||
|
||||
if cv.ocl.haveOpenCL() and cv.ocl.useOpenCL():
|
||||
dnnBackendsAndTargets.append([cv.dnn.DNN_BACKEND_OPENCV, cv.dnn.DNN_TARGET_OPENCL])
|
||||
dnnBackendsAndTargets.append([cv.dnn.DNN_BACKEND_OPENCV, cv.dnn.DNN_TARGET_OPENCL_FP16])
|
||||
if haveInfEngine and cv.ocl_Device.getDefault().isIntel():
|
||||
dnnBackendsAndTargets.append([cv.dnn.DNN_BACKEND_INFERENCE_ENGINE, cv.dnn.DNN_TARGET_OPENCL])
|
||||
dnnBackendsAndTargets.append([cv.dnn.DNN_BACKEND_INFERENCE_ENGINE, cv.dnn.DNN_TARGET_OPENCL_FP16])
|
||||
|
||||
|
||||
def printParams(backend, target):
|
||||
backendNames = {
|
||||
cv.dnn.DNN_BACKEND_OPENCV: 'OCV',
|
||||
@@ -116,8 +69,44 @@ def printParams(backend, target):
|
||||
|
||||
class dnn_test(NewOpenCVTests):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(dnn_test, self).__init__(*args, **kwargs)
|
||||
self.dnnBackendsAndTargets = [
|
||||
[cv.dnn.DNN_BACKEND_OPENCV, cv.dnn.DNN_TARGET_CPU],
|
||||
]
|
||||
|
||||
if self.checkIETarget(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE, cv.dnn.DNN_TARGET_CPU):
|
||||
self.dnnBackendsAndTargets.append([cv.dnn.DNN_BACKEND_INFERENCE_ENGINE, cv.dnn.DNN_TARGET_CPU])
|
||||
if self.checkIETarget(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE, cv.dnn.DNN_TARGET_MYRIAD):
|
||||
self.dnnBackendsAndTargets.append([cv.dnn.DNN_BACKEND_INFERENCE_ENGINE, cv.dnn.DNN_TARGET_MYRIAD])
|
||||
|
||||
if cv.ocl.haveOpenCL() and cv.ocl.useOpenCL():
|
||||
self.dnnBackendsAndTargets.append([cv.dnn.DNN_BACKEND_OPENCV, cv.dnn.DNN_TARGET_OPENCL])
|
||||
self.dnnBackendsAndTargets.append([cv.dnn.DNN_BACKEND_OPENCV, cv.dnn.DNN_TARGET_OPENCL_FP16])
|
||||
if cv.ocl_Device.getDefault().isIntel():
|
||||
if self.checkIETarget(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE, cv.dnn.DNN_TARGET_OPENCL):
|
||||
self.dnnBackendsAndTargets.append([cv.dnn.DNN_BACKEND_INFERENCE_ENGINE, cv.dnn.DNN_TARGET_OPENCL])
|
||||
if self.checkIETarget(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE, cv.dnn.DNN_TARGET_OPENCL_FP16):
|
||||
self.dnnBackendsAndTargets.append([cv.dnn.DNN_BACKEND_INFERENCE_ENGINE, cv.dnn.DNN_TARGET_OPENCL_FP16])
|
||||
|
||||
def find_dnn_file(self, filename, required=True):
|
||||
return self.find_file(filename, [os.environ.get('OPENCV_DNN_TEST_DATA_PATH', os.getcwd())], required=required)
|
||||
return self.find_file(filename, [os.environ.get('OPENCV_DNN_TEST_DATA_PATH', os.getcwd()),
|
||||
os.environ['OPENCV_TEST_DATA_PATH']],
|
||||
required=required)
|
||||
|
||||
def checkIETarget(self, backend, target):
|
||||
proto = self.find_dnn_file('dnn/layers/layer_convolution.prototxt', required=True)
|
||||
model = self.find_dnn_file('dnn/layers/layer_convolution.caffemodel', required=True)
|
||||
net = cv.dnn.readNet(proto, model)
|
||||
net.setPreferableBackend(backend)
|
||||
net.setPreferableTarget(target)
|
||||
inp = np.random.standard_normal([1, 2, 10, 11]).astype(np.float32)
|
||||
try:
|
||||
net.setInput(inp)
|
||||
net.forward()
|
||||
except BaseException as e:
|
||||
return False
|
||||
return True
|
||||
|
||||
def test_blobFromImage(self):
|
||||
np.random.seed(324)
|
||||
@@ -148,7 +137,7 @@ class dnn_test(NewOpenCVTests):
|
||||
|
||||
def test_face_detection(self):
|
||||
testdata_required = bool(os.environ.get('OPENCV_DNN_TEST_REQUIRE_TESTDATA', False))
|
||||
proto = self.find_dnn_file('dnn/opencv_face_detector.prototxt2', required=testdata_required)
|
||||
proto = self.find_dnn_file('dnn/opencv_face_detector.prototxt', required=testdata_required)
|
||||
model = self.find_dnn_file('dnn/opencv_face_detector.caffemodel', required=testdata_required)
|
||||
if proto is None or model is None:
|
||||
raise unittest.SkipTest("Missing DNN test files (dnn/opencv_face_detector.{prototxt/caffemodel}). Verify OPENCV_DNN_TEST_DATA_PATH configuration parameter.")
|
||||
@@ -164,7 +153,7 @@ class dnn_test(NewOpenCVTests):
|
||||
[0, 1, 0.95097077, 0.51901293, 0.45863652, 0.5777427, 0.5347801]]
|
||||
|
||||
print('\n')
|
||||
for backend, target in dnnBackendsAndTargets:
|
||||
for backend, target in self.dnnBackendsAndTargets:
|
||||
printParams(backend, target)
|
||||
|
||||
net = cv.dnn.readNet(proto, model)
|
||||
@@ -178,5 +167,52 @@ class dnn_test(NewOpenCVTests):
|
||||
|
||||
normAssertDetections(self, ref, out, 0.5, scoresDiff, iouDiff)
|
||||
|
||||
def test_async(self):
|
||||
timeout = 5000 # in milliseconds
|
||||
testdata_required = bool(os.environ.get('OPENCV_DNN_TEST_REQUIRE_TESTDATA', False))
|
||||
proto = self.find_dnn_file('dnn/layers/layer_convolution.prototxt', required=testdata_required)
|
||||
model = self.find_dnn_file('dnn/layers/layer_convolution.caffemodel', required=testdata_required)
|
||||
if proto is None or model is None:
|
||||
raise unittest.SkipTest("Missing DNN test files (dnn/layers/layer_convolution.{prototxt/caffemodel}). Verify OPENCV_DNN_TEST_DATA_PATH configuration parameter.")
|
||||
|
||||
print('\n')
|
||||
for backend, target in self.dnnBackendsAndTargets:
|
||||
if backend != cv.dnn.DNN_BACKEND_INFERENCE_ENGINE:
|
||||
continue
|
||||
|
||||
printParams(backend, target)
|
||||
|
||||
netSync = cv.dnn.readNet(proto, model)
|
||||
netSync.setPreferableBackend(backend)
|
||||
netSync.setPreferableTarget(target)
|
||||
|
||||
netAsync = cv.dnn.readNet(proto, model)
|
||||
netAsync.setPreferableBackend(backend)
|
||||
netAsync.setPreferableTarget(target)
|
||||
|
||||
# Generate inputs
|
||||
numInputs = 10
|
||||
inputs = []
|
||||
for _ in range(numInputs):
|
||||
inputs.append(np.random.standard_normal([2, 6, 75, 113]).astype(np.float32))
|
||||
|
||||
# Run synchronously
|
||||
refs = []
|
||||
for i in range(numInputs):
|
||||
netSync.setInput(inputs[i])
|
||||
refs.append(netSync.forward())
|
||||
|
||||
# Run asynchronously. To make test more robust, process inputs in the reversed order.
|
||||
outs = []
|
||||
for i in reversed(range(numInputs)):
|
||||
netAsync.setInput(inputs[i])
|
||||
outs.insert(0, netAsync.forwardAsync())
|
||||
|
||||
for i in reversed(range(numInputs)):
|
||||
if outs[i].wait_for(timeout) == 1:
|
||||
self.fail("Timeout")
|
||||
normAssert(self, refs[i], outs[i].get(), 'Index: %d' % i, 1e-10)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
NewOpenCVTests.bootstrap()
|
||||
|
||||
Reference in New Issue
Block a user