123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 |
- #!/usr/bin/env python
- import os
- import cv2 as cv
- import numpy as np
- from tests_common import NewOpenCVTests, unittest
- 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])
- y_min, y_max = max(box1[1], box2[1]), min(box1[3], box2[3])
- return (x_max - x_min) * (y_max - y_min)
- def area(box):
- return (box[2] - box[0]) * (box[3] - box[1])
- def box2str(box):
- left, top = box[0], box[1]
- width, height = box[2] - left, box[3] - top
- return '[%f x %f from (%f, %f)]' % (width, height, left, top)
- def normAssertDetections(test, refClassIds, refScores, refBoxes, testClassIds, testScores, testBoxes,
- confThreshold=0.0, scores_diff=1e-5, boxes_iou_diff=1e-4):
- matchedRefBoxes = [False] * len(refBoxes)
- errMsg = ''
- for i in range(len(testBoxes)):
- testScore = testScores[i]
- if testScore < confThreshold:
- continue
- testClassId, testBox = testClassIds[i], testBoxes[i]
- matched = False
- for j in range(len(refBoxes)):
- if (not matchedRefBoxes[j]) and testClassId == refClassIds[j] and \
- abs(testScore - refScores[j]) < scores_diff:
- interArea = inter_area(testBox, refBoxes[j])
- iou = interArea / (area(testBox) + area(refBoxes[j]) - interArea)
- if abs(iou - 1.0) < boxes_iou_diff:
- matched = True
- matchedRefBoxes[j] = True
- if not matched:
- errMsg += '\nUnmatched prediction: class %d score %f box %s' % (testClassId, testScore, box2str(testBox))
- for i in range(len(refBoxes)):
- if (not matchedRefBoxes[i]) and refScores[i] > confThreshold:
- errMsg += '\nUnmatched reference: class %d score %f box %s' % (refClassIds[i], refScores[i], box2str(refBoxes[i]))
- if errMsg:
- test.fail(errMsg)
- def printParams(backend, target):
- backendNames = {
- cv.dnn.DNN_BACKEND_OPENCV: 'OCV',
- cv.dnn.DNN_BACKEND_INFERENCE_ENGINE: 'DLIE'
- }
- targetNames = {
- cv.dnn.DNN_TARGET_CPU: 'CPU',
- cv.dnn.DNN_TARGET_OPENCL: 'OCL',
- cv.dnn.DNN_TARGET_OPENCL_FP16: 'OCL_FP16',
- cv.dnn.DNN_TARGET_MYRIAD: 'MYRIAD'
- }
- print('%s/%s' % (backendNames[backend], targetNames[target]))
- def getDefaultThreshold(target):
- if target == cv.dnn.DNN_TARGET_OPENCL_FP16 or target == cv.dnn.DNN_TARGET_MYRIAD:
- return 4e-3
- else:
- return 1e-5
- testdata_required = bool(os.environ.get('OPENCV_DNN_TEST_REQUIRE_TESTDATA', False))
- g_dnnBackendsAndTargets = None
- class dnn_test(NewOpenCVTests):
- def setUp(self):
- super(dnn_test, self).setUp()
- global g_dnnBackendsAndTargets
- if g_dnnBackendsAndTargets is None:
- g_dnnBackendsAndTargets = self.initBackendsAndTargets()
- self.dnnBackendsAndTargets = g_dnnBackendsAndTargets
- def initBackendsAndTargets(self):
- 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])
- return self.dnnBackendsAndTargets
- def find_dnn_file(self, filename, required=True):
- if not required:
- required = testdata_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')
- model = self.find_dnn_file('dnn/layers/layer_convolution.caffemodel')
- 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_getAvailableTargets(self):
- targets = cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_OPENCV)
- self.assertTrue(cv.dnn.DNN_TARGET_CPU in targets)
- def test_blobFromImage(self):
- np.random.seed(324)
- width = 6
- height = 7
- scale = 1.0/127.5
- mean = (10, 20, 30)
- # Test arguments names.
- img = np.random.randint(0, 255, [4, 5, 3]).astype(np.uint8)
- blob = cv.dnn.blobFromImage(img, scale, (width, height), mean, True, False)
- blob_args = cv.dnn.blobFromImage(img, scalefactor=scale, size=(width, height),
- mean=mean, swapRB=True, crop=False)
- normAssert(self, blob, blob_args)
- # Test values.
- target = cv.resize(img, (width, height), interpolation=cv.INTER_LINEAR)
- target = target.astype(np.float32)
- target = target[:,:,[2, 1, 0]] # BGR2RGB
- target[:,:,0] -= mean[0]
- target[:,:,1] -= mean[1]
- target[:,:,2] -= mean[2]
- target *= scale
- target = target.transpose(2, 0, 1).reshape(1, 3, height, width) # to NCHW
- normAssert(self, blob, target)
- def test_model(self):
- img_path = self.find_dnn_file("dnn/street.png")
- weights = self.find_dnn_file("dnn/MobileNetSSD_deploy.caffemodel", required=False)
- config = self.find_dnn_file("dnn/MobileNetSSD_deploy.prototxt", required=False)
- if weights is None or config is None:
- raise unittest.SkipTest("Missing DNN test files (dnn/MobileNetSSD_deploy.{prototxt/caffemodel}). Verify OPENCV_DNN_TEST_DATA_PATH configuration parameter.")
- frame = cv.imread(img_path)
- model = cv.dnn_DetectionModel(weights, config)
- model.setInputParams(size=(300, 300), mean=(127.5, 127.5, 127.5), scale=1.0/127.5)
- iouDiff = 0.05
- confThreshold = 0.0001
- nmsThreshold = 0
- scoreDiff = 1e-3
- classIds, confidences, boxes = model.detect(frame, confThreshold, nmsThreshold)
- refClassIds = (7, 15)
- refConfidences = (0.9998, 0.8793)
- refBoxes = ((328, 238, 85, 102), (101, 188, 34, 138))
- normAssertDetections(self, refClassIds, refConfidences, refBoxes,
- classIds, confidences, boxes,confThreshold, scoreDiff, iouDiff)
- for box in boxes:
- cv.rectangle(frame, box, (0, 255, 0))
- cv.rectangle(frame, np.array(box), (0, 255, 0))
- cv.rectangle(frame, tuple(box), (0, 255, 0))
- cv.rectangle(frame, list(box), (0, 255, 0))
- def test_classification_model(self):
- img_path = self.find_dnn_file("dnn/googlenet_0.png")
- weights = self.find_dnn_file("dnn/squeezenet_v1.1.caffemodel", required=False)
- config = self.find_dnn_file("dnn/squeezenet_v1.1.prototxt")
- ref = np.load(self.find_dnn_file("dnn/squeezenet_v1.1_prob.npy"))
- if weights is None or config is None:
- raise unittest.SkipTest("Missing DNN test files (dnn/squeezenet_v1.1.{prototxt/caffemodel}). Verify OPENCV_DNN_TEST_DATA_PATH configuration parameter.")
- frame = cv.imread(img_path)
- model = cv.dnn_ClassificationModel(config, weights)
- model.setInputSize(227, 227)
- model.setInputCrop(True)
- out = model.predict(frame)
- normAssert(self, out, ref)
- def test_textdetection_model(self):
- img_path = self.find_dnn_file("dnn/text_det_test1.png")
- weights = self.find_dnn_file("dnn/onnx/models/DB_TD500_resnet50.onnx", required=False)
- if weights is None:
- raise unittest.SkipTest("Missing DNN test files (onnx/models/DB_TD500_resnet50.onnx). Verify OPENCV_DNN_TEST_DATA_PATH configuration parameter.")
- frame = cv.imread(img_path)
- scale = 1.0 / 255.0
- size = (736, 736)
- mean = (122.67891434, 116.66876762, 104.00698793)
- model = cv.dnn_TextDetectionModel_DB(weights)
- model.setInputParams(scale, size, mean)
- out, _ = model.detect(frame)
- self.assertTrue(type(out) == tuple, msg='actual type {}'.format(str(type(out))))
- self.assertTrue(np.array(out).shape == (2, 4, 2))
- def test_face_detection(self):
- proto = self.find_dnn_file('dnn/opencv_face_detector.prototxt')
- model = self.find_dnn_file('dnn/opencv_face_detector.caffemodel', required=False)
- 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.")
- img = self.get_sample('gpu/lbpcascade/er.png')
- blob = cv.dnn.blobFromImage(img, mean=(104, 177, 123), swapRB=False, crop=False)
- ref = [[0, 1, 0.99520785, 0.80997437, 0.16379407, 0.87996572, 0.26685631],
- [0, 1, 0.9934696, 0.2831718, 0.50738752, 0.345781, 0.5985168],
- [0, 1, 0.99096733, 0.13629119, 0.24892329, 0.19756334, 0.3310290],
- [0, 1, 0.98977017, 0.23901358, 0.09084064, 0.29902688, 0.1769477],
- [0, 1, 0.97203469, 0.67965847, 0.06876482, 0.73999709, 0.1513494],
- [0, 1, 0.95097077, 0.51901293, 0.45863652, 0.5777427, 0.5347801]]
- print('\n')
- for backend, target in self.dnnBackendsAndTargets:
- printParams(backend, target)
- net = cv.dnn.readNet(proto, model)
- net.setPreferableBackend(backend)
- net.setPreferableTarget(target)
- net.setInput(blob)
- out = net.forward().reshape(-1, 7)
- scoresDiff = 4e-3 if target in [cv.dnn.DNN_TARGET_OPENCL_FP16, cv.dnn.DNN_TARGET_MYRIAD] else 1e-5
- iouDiff = 2e-2 if target in [cv.dnn.DNN_TARGET_OPENCL_FP16, cv.dnn.DNN_TARGET_MYRIAD] else 1e-4
- ref = np.array(ref, np.float32)
- refClassIds, testClassIds = ref[:, 1], out[:, 1]
- refScores, testScores = ref[:, 2], out[:, 2]
- refBoxes, testBoxes = ref[:, 3:], out[:, 3:]
- normAssertDetections(self, refClassIds, refScores, refBoxes, testClassIds,
- testScores, testBoxes, 0.5, scoresDiff, iouDiff)
- def test_async(self):
- timeout = 10*1000*10**6 # in nanoseconds (10 sec)
- proto = self.find_dnn_file('dnn/layers/layer_convolution.prototxt')
- model = self.find_dnn_file('dnn/layers/layer_convolution.caffemodel')
- 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)):
- ret, result = outs[i].get(timeoutNs=float(timeout))
- self.assertTrue(ret)
- normAssert(self, refs[i], result, 'Index: %d' % i, 1e-10)
- def test_nms(self):
- confs = (1, 1)
- rects = ((0, 0, 0.4, 0.4), (0, 0, 0.2, 0.4)) # 0.5 overlap
- self.assertTrue(all(cv.dnn.NMSBoxes(rects, confs, 0, 0.6).ravel() == (0, 1)))
- def test_custom_layer(self):
- class CropLayer(object):
- def __init__(self, params, blobs):
- self.xstart = 0
- self.xend = 0
- self.ystart = 0
- self.yend = 0
- # Our layer receives two inputs. We need to crop the first input blob
- # to match a shape of the second one (keeping batch size and number of channels)
- def getMemoryShapes(self, inputs):
- inputShape, targetShape = inputs[0], inputs[1]
- batchSize, numChannels = inputShape[0], inputShape[1]
- height, width = targetShape[2], targetShape[3]
- self.ystart = (inputShape[2] - targetShape[2]) // 2
- self.xstart = (inputShape[3] - targetShape[3]) // 2
- self.yend = self.ystart + height
- self.xend = self.xstart + width
- return [[batchSize, numChannels, height, width]]
- def forward(self, inputs):
- return [inputs[0][:,:,self.ystart:self.yend,self.xstart:self.xend]]
- cv.dnn_registerLayer('CropCaffe', CropLayer)
- proto = '''
- name: "TestCrop"
- input: "input"
- input_shape
- {
- dim: 1
- dim: 2
- dim: 5
- dim: 5
- }
- input: "roi"
- input_shape
- {
- dim: 1
- dim: 2
- dim: 3
- dim: 3
- }
- layer {
- name: "Crop"
- type: "CropCaffe"
- bottom: "input"
- bottom: "roi"
- top: "Crop"
- }'''
- net = cv.dnn.readNetFromCaffe(bytearray(proto.encode()))
- for backend, target in self.dnnBackendsAndTargets:
- if backend != cv.dnn.DNN_BACKEND_OPENCV:
- continue
- printParams(backend, target)
- net.setPreferableBackend(backend)
- net.setPreferableTarget(target)
- src_shape = [1, 2, 5, 5]
- dst_shape = [1, 2, 3, 3]
- inp = np.arange(0, np.prod(src_shape), dtype=np.float32).reshape(src_shape)
- roi = np.empty(dst_shape, dtype=np.float32)
- net.setInput(inp, "input")
- net.setInput(roi, "roi")
- out = net.forward()
- ref = inp[:, :, 1:4, 1:4]
- normAssert(self, out, ref)
- cv.dnn_unregisterLayer('CropCaffe')
- # check that dnn module can work with 3D tensor as input for network
- def test_input_3d(self):
- model = self.find_dnn_file('dnn/onnx/models/hidden_lstm.onnx')
- input_file = self.find_dnn_file('dnn/onnx/data/input_hidden_lstm.npy')
- output_file = self.find_dnn_file('dnn/onnx/data/output_hidden_lstm.npy')
- if model is None:
- raise unittest.SkipTest("Missing DNN test files (dnn/onnx/models/hidden_lstm.onnx). "
- "Verify OPENCV_DNN_TEST_DATA_PATH configuration parameter.")
- if input_file is None or output_file is None:
- raise unittest.SkipTest("Missing DNN test files (dnn/onnx/data/{input/output}_hidden_lstm.npy). "
- "Verify OPENCV_DNN_TEST_DATA_PATH configuration parameter.")
- input = np.load(input_file)
- # we have to expand the shape of input tensor because Python bindings cut 3D tensors to 2D
- # it should be fixed in future. see : https://github.com/opencv/opencv/issues/19091
- # please remove `expand_dims` after that
- input = np.expand_dims(input, axis=3)
- gold_output = np.load(output_file)
- for backend, target in self.dnnBackendsAndTargets:
- printParams(backend, target)
- net = cv.dnn.readNet(model)
- net.setPreferableBackend(backend)
- net.setPreferableTarget(target)
- net.setInput(input)
- real_output = net.forward()
- normAssert(self, real_output, gold_output, "", getDefaultThreshold(target))
- if __name__ == '__main__':
- NewOpenCVTests.bootstrap()
|